diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml deleted file mode 100644 index 3fa32fb536..0000000000 --- a/.github/FUNDING.yml +++ /dev/null @@ -1,14 +0,0 @@ -# These are supported funding model platforms - -github: # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2] -patreon: # Replace with a single Patreon username -open_collective: # Replace with a single Open Collective username -ko_fi: # Replace with a single Ko-fi username -tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel -community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry -liberapay: # Replace with a single Liberapay username -issuehunt: # Replace with a single IssueHunt username -lfx_crowdfunding: # Replace with a single LFX Crowdfunding project-name e.g., cloud-foundry -polar: # Replace with a single Polar username -buy_me_a_coffee: philschatzh -custom: ['https://www.paypal.com/paypalme/pschatzmann?country.x=CH&locale.x=en_US'] diff --git a/.github/ISSUE_TEMPLATE/Issue-report.yml b/.github/ISSUE_TEMPLATE/Issue-report.yml deleted file mode 100644 index 6f1ea95fc9..0000000000 --- a/.github/ISSUE_TEMPLATE/Issue-report.yml +++ /dev/null @@ -1,58 +0,0 @@ -name: Bug report -description: Report only a bugs here! -body: - - type: markdown - attributes: - value: | - * Before reporting a new bug please check and search the [list of existing issues](https://github.com/pschatzmann/arduino-audio-tools/issues?q=) - * Please check [the Readme](https://github.com/pschatzmann/arduino-audio-tools) and [Wiki](https://github.com/pschatzmann/arduino-audio-tools/wiki) - * Don't forget to check [the discusions](https://github.com/pschatzmann/arduino-audio-tools/discussions) - * If still experiencing the issue, please provide as many details as possible below about your hardware, computer setup and code. - - type: textarea - id: Description - attributes: - label: Problem Description - description: Please describe your problem here and expected behaviour - placeholder: ex. Can't connect/weird behaviour/wrong function/missing parameter.. - validations: - required: true - - type: textarea - id: Board - attributes: - label: Device Description - description: What development board are you using - placeholder: e.g. ESP32 Wroom, Desktop Build, RP2040 - validations: - required: true - - type: textarea - id: sketch - attributes: - label: Sketch - description: Please provide full minimal sketch/code which can be run to reproduce your issue - placeholder: ex. Related part of the code to replicate the issue - render: cpp - validations: - required: true - - - type: textarea - id: other-remarks - attributes: - label: Other Steps to Reproduce - description: Is there any other information you can think of which will help us reproduce this problem? Any additional info can be added as well. - placeholder: ex. I also tried on other OS, HW...it works correctly on that setup. - - - type: textarea - id: sceanario - attributes: - label: What is your development environment (incl. core version info) - description: Please provide the information about your development/runtime environment - placeholder: Arduino ESP 2.0.14, IDF 5.3.2, STM32-Cube MX, PlatformIO ESP32 6.10.0, JupyterLab, Desktop Build - - - type: checkboxes - id: confirmation - attributes: - label: I have checked existing issues, discussions and online documentation - description: You agree to check all the resources above before opening a new issue. - options: - - label: I confirm I have checked existing issues, discussions and online documentation - required: true diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml deleted file mode 100644 index 3ba13e0cec..0000000000 --- a/.github/ISSUE_TEMPLATE/config.yml +++ /dev/null @@ -1 +0,0 @@ -blank_issues_enabled: false diff --git a/.gitignore b/.gitignore index 76eef990ea..f151f16db9 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,233 @@ -docs/html/ -build/ +################# +## Eclipse +################# + +*.pydevproject +.project +.metadata +bin/ +tmp/ +*.tmp +*.bak +*.swp +*~.nib +local.properties +.classpath +.settings/ +.loadpath +debug.cfg +debug_custom.json +esp32*.svd + +# External tool builders +.externalToolBuilders/ + +# Locally stored "Eclipse launch configurations" +*.launch + +# CDT-specific +.cproject + +# PDT-specific +.buildpath + + +################# +## Visual Studio +################# + +## Ignore Visual Studio temporary files, build results, and +## files generated by popular Visual Studio add-ons. + +# User-specific files +*.suo +*.user +*.sln.docstates .vscode/ -_deps/ + +# Build results + +[Dd]ebug/ +[Rr]elease/ +x64/ +build/ +[Bb]in/ +[Oo]bj/ + +# MSTest test Results +[Tt]est[Rr]esult*/ +[Bb]uild[Ll]og.* + +*_i.c +*_p.c +*.ilk +*.meta +*.obj +*.pch +*.pdb +*.pgc +*.pgd +*.rsp +*.sbr +*.tlb +*.tli +*.tlh +*.tmp +*.tmp_proj +*.log +*.vspscc +*.vssscc +.builds +*.pidb +*.log +*.scc + +# Visual C++ cache files +ipch/ +*.aps +*.ncb +*.opensdf +*.sdf +*.cachefile + +# Visual Studio profiler +*.psess +*.vsp +*.vspx + +# Guidance Automation Toolkit +*.gpState + +# ReSharper is a .NET coding add-in +_ReSharper*/ +*.[Rr]e[Ss]harper + +# TeamCity is a build add-in +_TeamCity* + +# DotCover is a Code Coverage Tool +*.dotCover + +# NCrunch +*.ncrunch* +.*crunch*.local.xml + +# Installshield output folder +[Ee]xpress/ + +# DocProject is a documentation generator add-in +DocProject/buildhelp/ +DocProject/Help/*.HxT +DocProject/Help/*.HxC +DocProject/Help/*.hhc +DocProject/Help/*.hhk +DocProject/Help/*.hhp +DocProject/Help/Html2 +DocProject/Help/html + +# Click-Once directory +publish/ + +# Publish Web Output +*.Publish.xml +*.pubxml + +# NuGet Packages Directory +## TODO: If you have NuGet Package Restore enabled, uncomment the next line +#packages/ + +# Windows Azure Build Output +csx +*.build.csdef + +# Windows Store app package directory +AppPackages/ + +# Others +sql/ +*.Cache +ClientBin/ +[Ss]tyle[Cc]op.* +~$* +*~ +*.dbmdl +*.[Pp]ublish.xml +*.pfx +*.publishsettings + +# RIA/Silverlight projects +Generated_Code/ + +# Backup & report files from converting an old project file to a newer +# Visual Studio version. Backup files are not needed, because we have git ;-) +_UpgradeReport_Files/ +Backup*/ +UpgradeLog*.XML +UpgradeLog*.htm + +# SQL Server files +App_Data/*.mdf +App_Data/*.ldf + +############# +## Windows detritus +############# + +# Windows image file caches +Thumbs.db +ehthumbs.db + +# Folder config file +Desktop.ini + +# Recycle Bin used on file shares +$RECYCLE.BIN/ + +# Mac crap +.DS_Store + + +############# +## Python +############# + +*.py[co] + +# Packages +*.egg +*.egg-info +dist/ +build/ +eggs/ +parts/ +var/ +sdist/ +develop-eggs/ +.installed.cfg + +# Installer logs +pip-log.txt + +# Unit test / coverage reports +.coverage +.tox + +# Translations +*.mo + +# Mr Developer +.mr.developer.cfg + +# Doxygen +.DS_Store +/.vs + +# Jupyter .ipynb_checkpoints/ -.DS_Store \ No newline at end of file +jupyter/*.wav +.DS_Store + +# doxygen +docs/html/ + +debug.svd diff --git a/CMakeLists.txt b/CMakeLists.txt index b3ed3f92ea..94f5c53b57 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,63 +1,49 @@ cmake_minimum_required(VERSION 3.16) -if (DEFINED ESP_PLATFORM) +# set the project name +project(arduino-audio-tools) - # idf component - idf_component_register( - # SRC_DIRS src - INCLUDE_DIRS src - REQUIRES bt esp_common freertos hal log nvs_flash driver esp_adc esp_http_client - ) +set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -O0") +set(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} -O0") +add_compile_options(-Wno-deprecated-declarations) - target_compile_options(${COMPONENT_LIB} INTERFACE -DESP32_CMAKE=1 -Wno-error -Wno-format -fpermissive) +include(FetchContent) -else() +add_library(arduino-audio-tools INTERFACE) - # set the project name - project(arduino-audio-tools) +option(BUILD_SHARED_LIBS "Build using shared libraries" OFF) +option(ADD_PORTAUDIO "Add Portaudio Library" ON) +option(ADD_ARDUINO_EMULATOR "Add Arduino Emulator Library" ON) - set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -O0") - set(FETCHCONTENT_UPDATES_DISCONNECTED ON) - include(FetchContent) +# make include directory available to calling projects +target_include_directories (arduino-audio-tools INTERFACE ${CMAKE_CURRENT_SOURCE_DIR}/src) - add_library(arduino-audio-tools INTERFACE) - - option(BUILD_SHARED_LIBS "Build using shared libraries" OFF) - option(ADD_PORTAUDIO "Add Portaudio Library" OFF) - option(ADD_ARDUINO_EMULATOR "Add Arduino Emulator Library" ON) - - - # make include directory available to calling projects - target_include_directories (arduino-audio-tools INTERFACE ${CMAKE_CURRENT_SOURCE_DIR}/src) - - # installation of all header files - install(DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/src/" # source directory - DESTINATION "include/arduino-audio-tools" # target directory - FILES_MATCHING # install only matched files - PATTERN "*.h" # select header files - ) - - if (ADD_PORTAUDIO) - # Add Portaduio for desktop build - FetchContent_Declare(portaudio GIT_REPOSITORY "https://github.com/PortAudio/portaudio.git" GIT_TAG v19.7.0 ) - FetchContent_GetProperties(portaudio) - if(NOT portaudio_POPULATED) - FetchContent_Populate(portaudio) - add_subdirectory(${portaudio_SOURCE_DIR} ${CMAKE_CURRENT_BINARY_DIR}/portaudio) - endif() +# installation of all header files +install(DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/src/" # source directory + DESTINATION "include/arduino-audio-tools" # target directory + FILES_MATCHING # install only matched files + PATTERN "*.h" # select header files +) +if (ADD_PORTAUDIO) + add_compile_options(-DIS_DESKTOP) + # Add Portaduio for desktop build + FetchContent_Declare(portaudio GIT_REPOSITORY "https://github.com/PortAudio/portaudio.git" GIT_TAG v19.7.0 ) + FetchContent_GetProperties(portaudio) + if(NOT portaudio_POPULATED) + FetchContent_Populate(portaudio) + add_subdirectory(${portaudio_SOURCE_DIR} ${CMAKE_CURRENT_BINARY_DIR}/portaudio) endif() +endif() - if (ADD_ARDUINO_EMULATOR) - # Build with Linux Arduino Emulator - FetchContent_Declare(arduino_emulator GIT_REPOSITORY "https://github.com/pschatzmann/Arduino-Emulator.git" GIT_TAG main ) - FetchContent_GetProperties(arduino_emulator) - if(NOT arduino_emulator_POPULATED) - FetchContent_Populate(arduino_emulator) - add_subdirectory(${arduino_emulator_SOURCE_DIR} ${CMAKE_CURRENT_BINARY_DIR}/emulator) - endif() +if (ADD_ARDUINO_EMULATOR) + # Build with Linux Arduino Emulator + FetchContent_Declare(arduino_emulator GIT_REPOSITORY "https://github.com/pschatzmann/Arduino-Emulator.git" GIT_TAG main ) + FetchContent_GetProperties(arduino_emulator) + if(NOT arduino_emulator_POPULATED) + FetchContent_Populate(arduino_emulator) + add_subdirectory(${arduino_emulator_SOURCE_DIR} ${CMAKE_CURRENT_BINARY_DIR}/emulator) endif() - endif() diff --git a/Doxyfile b/Doxyfile index 5002c3d09b..30120e20d9 100644 --- a/Doxyfile +++ b/Doxyfile @@ -953,7 +953,7 @@ RECURSIVE = YES # Note that relative paths are relative to the directory from which doxygen is # run. -EXCLUDE = examples, tests, docs/html, docs/resources, src/Sandbox +EXCLUDE = examples, tests, docs/html, docs/resources, src/Experiments # The EXCLUDE_SYMLINKS tag can be used to select whether or not files or # directories that are symbolic links (a Unix file system feature) are excluded @@ -980,7 +980,7 @@ EXCLUDE_PATTERNS = # Note that the wildcards are matched against the file with absolute path, so to # exclude all test directories use the pattern */test/* -EXCLUDE_SYMBOLS = audio_tools::IO16Bit, std::initializer_list +EXCLUDE_SYMBOLS = # The EXAMPLE_PATH tag can be used to specify one or more files or directories # that contain example code fragments that are included (see the \include @@ -2217,7 +2217,7 @@ INCLUDE_FILE_PATTERNS = # recursively expanded use the := operator instead of the = operator. # This tag requires that the tag ENABLE_PREPROCESSING is set to YES. -PREDEFINED = DOXYGEN IS_DESKTOP ESP32 ESP8266 ARDUINO_ARCH_MBED_RP2040 ARDUINO_ARCH_RP2040 USE_NANO33BLE ARDUINO_ARCH_MBED ARDUINO_ARCH_RENESAS __AVR__ STM32 ARDUINO_ARCH_SAMD USE_URL_ARDUINO USE_TIMER USE_ANALOG USE_I2S USE_PWM USE_AUDIO_SERVER USE_ANALOG_ARDUINO USE_CONCURRENCY IS_I2S_IMPLEMENTED USE_WIFI +PREDEFINED = DOXYGEN IS_DESKTOP ESP32 ESP8266 ARDUINO_ARCH_MBED_RP2040 ARDUINO_ARCH_RP2040 __AVR__ STM32 ARDUINO_ARCH_SAMD USE_URL_ARDUINO USE_TIMER USE_I2S_ANALOG USE_I2S USE_PWM USE_AUDIO_SERVER # If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then this # tag can be used to specify a list of macro names that should be expanded. The diff --git a/README.md b/README.md index e5bf7efe94..93689907f0 100644 --- a/README.md +++ b/README.md @@ -2,30 +2,72 @@ Some basic __header-only C++ classes__ that can be used for __Audio Processing__ provided as __Arduino and cmake C++ Library__: -- We provide different ["Audio Sources" and "Audio Sinks"](https://github.com/pschatzmann/arduino-audio-tools/wiki/Audio-Sources-and-Sinks) +- We provide different "Audio Sources" and "Audio Sinks" (see next section) - Support for different [Encoders](https://pschatzmann.github.io/arduino-audio-tools/classaudio__tools_1_1_audio_encoder.html) and [Decoders](https://pschatzmann.github.io/arduino-audio-tools/classaudio__tools_1_1_audio_decoder.html) for MP3, AAC, WAV, FLAC, etc for [EncodedAudioStream](https://pschatzmann.github.io/arduino-audio-tools/classaudio__tools_1_1_encoded_audio_stream.html) -- Integrates with different [DSP libraries](https://github.com/pschatzmann/arduino-audio-tools/wiki/DSP-Libraries) -- Helps to [communicate](https://github.com/pschatzmann/arduino-audio-tools/wiki/Communication) audio over the wire or wirelessly - Different [Sound Generators](https://pschatzmann.github.io/arduino-audio-tools/group__generator.html) (e.g. to generate a sine tone) for [GeneratedSoundStream](https://pschatzmann.github.io/arduino-audio-tools/classaudio__tools_1_1_generated_sound_stream.html) - Support for [Sound Effects](https://pschatzmann.github.io/arduino-audio-tools/classaudio__tools_1_1_audio_effect_stream.html) with different [Effect Implementations](https://pschatzmann.github.io/arduino-audio-tools/classaudio__tools_1_1_audio_effect.html) (e.g. Boost, Distortion, Echo, Reverb...) for [AudioEffectStream](https://pschatzmann.github.io/arduino-audio-tools/classaudio__tools_1_1_audio_effect_stream_t.html) -- Provides a [3 Band Equalizer](https://pschatzmann.github.io/arduino-audio-tools/classaudio__tools_1_1_equalizer3_bands.html) +- Different [Buffer Implementations](https://pschatzmann.github.io/arduino-audio-tools/classaudio__tools_1_1_base_buffer.html) for [QueueStream](https://pschatzmann.github.io/arduino-audio-tools/classaudio__tools_1_1_queue_stream.html) - Different [Converters](https://pschatzmann.github.io/arduino-audio-tools/classaudio__tools_1_1_base_converter.html) for [ConverterStream](https://pschatzmann.github.io/arduino-audio-tools/classaudio__tools_1_1_converter_stream.html) - Different [Filters](https://pschatzmann.github.io/arduino-audio-tools/classaudio__tools_1_1_filter.html) for [FilteredStream](https://pschatzmann.github.io/arduino-audio-tools/classaudio__tools_1_1_filtered_stream.html) - [Musical Notes](https://pschatzmann.github.io/arduino-audio-tools/classaudio__tools_1_1_musical_notes.html) (with frequencies of notes) -- Different [Buffer Implementations](https://pschatzmann.github.io/arduino-audio-tools/classaudio__tools_1_1_base_buffer.html) for [QueueStream](https://pschatzmann.github.io/arduino-audio-tools/classaudio__tools_1_1_queue_stream.html) - A [Repeating Timer](https://pschatzmann.github.io/arduino-audio-tools/classaudio__tools_1_1_timer_alarm_repeating.html) (e.g. for sampling audio data using exact times) for [TimerCallbackAudioStream](https://pschatzmann.github.io/arduino-audio-tools/classaudio__tools_1_1_timer_callback_audio_stream.html) - Desktop Integration: Building of Arduino Audio Sketches to be run on [Linux, Windows and OS/X](https://github.com/pschatzmann/arduino-audio-tools/wiki/Running-an-Audio-Sketch-on-the-Desktop) -This functionality provides the glue which makes different audio processing components and [libraries](https://github.com/pschatzmann/arduino-audio-tools/wiki/Optional-Libraries) work together. - -We also provide [plenty of examples](https://github.com/pschatzmann/arduino-audio-tools/wiki/Examples) that demonstrate how to implement the different scenarios. The __design philosophy__ is based on the Arduino conventions: we use the ```begin()``` and ```end()``` methods to start and stop the processing and we propagate the __use of Streams__. - -We all know the Arduino [Print](https://www.arduino.cc/reference/en/language/functions/communication/print/) and [Stream](https://www.arduino.cc/reference/en/language/functions/communication/stream) classes: We usually use them to write out print messages and sometimes we use them to read the output from Serial, Files, Ethernet, etc. The same thing applies to “Audio Streams”: You can read audio data from [“Audio Sources” and you write them to “Audio Sinks”](https://github.com/pschatzmann/arduino-audio-tools/wiki/Audio-Sources-and-Sinks). - - -### Example - -Here is a simple example which streams a file from the Flash Memory and writes it to I2S: +This functionality provides the glue which makes different audio processing components and libraries work together. +We also provide [plenty of examples](https://github.com/pschatzmann/arduino-audio-tools/wiki/Examples) that demonstrate how to implement the different scenarios. The __design philosophy__ is based on the Arduino conventions: we use the ```begin()``` and ```end()``` methods to start and stop the processing and we propagate the __use of Streams__. We all know the [Arduino Streams](https://pschatzmann.github.io/arduino-audio-tools/class_stream.html): We usually use them to write out print messages and sometimes we use them to read the output from Serial devices. The same thing applies to “Audio Streams”: You can read audio data from “Audio Sources” and you write them to “Audio Sinks”. + +As “Audio Sources” we will have e.g.: + +- Digital Microphones – [I2SStream](https://pschatzmann.github.io/arduino-audio-tools/classaudio__tools_1_1_i2_s_stream.html) +- Analog Microphones – [AnalogAudioStream](https://pschatzmann.github.io/arduino-audio-tools/classaudio__tools_1_1_analog_audio_stream.html) +- Files on the Internet – [URLStream](https://pschatzmann.github.io/arduino-audio-tools/classaudio__tools_1_1_u_r_l_stream.html) +- Streaming Internet Radios - [ICYStream](https://pschatzmann.github.io/arduino-audio-tools/classaudio__tools_1_1_i_c_y_stream.html) +- Generated Sound – [GeneratedSoundStream](https://pschatzmann.github.io/arduino-audio-tools/classaudio__tools_1_1_generated_sound_stream.html) +- Encoded Audio - [EncodedAudioStream](https://pschatzmann.github.io/arduino-audio-tools/classaudio__tools_1_1_encoded_audio_stream.html) +- Mobile Phone A2DP Bluetooth – [A2DPStream](https://pschatzmann.github.io/arduino-audio-tools/classaudio__tools_1_1_a2_d_p_stream.html) +- Binary Data in Flash Memory – [MemoryStream](https://pschatzmann.github.io/arduino-audio-tools/classaudio__tools_1_1_memory_stream.html) +- Audio generated by STK Framwork - [STKStream](https://pschatzmann.github.io/arduino-audio-tools/classaudio__tools_1_1_s_t_k_stream.html) +- Desktop Integration - [PortAudioStream](https://pschatzmann.github.io/arduino-audio-tools/classaudio__tools_1_1_port_audio_stream.html) [MiniAudioStream](https://pschatzmann.github.io/arduino-audio-tools/classaudio__tools_1_1_mini_audio_stream.html) [StdioStream](https://pschatzmann.github.io/arduino-audio-tools/classaudio__tools_1_1_stdio_stream.html) +- A Timer based Source - [TimerCallbackAudioStream](https://pschatzmann.github.io/arduino-audio-tools/classaudio__tools_1_1_timer_callback_audio_stream.html) +- ESP32 Lyrat/AudioKit - [AudioKitStream](https://pschatzmann.github.io/arduino-audio-tools/classaudio__tools_1_1_audio_kit_stream.html) +- Input using FIR, IIR Filters - [FilteredStream](https://pschatzmann.github.io/arduino-audio-tools/classaudio__tools_1_1_filtered_stream.html) +- Tensorflow Lite - [TfLiteAudioStream](https://pschatzmann.github.io/arduino-audio-tools/classaudio__tools_1_1_tf_lite_audio_stream.html) +- [Converting Streams](https://pschatzmann.github.io/arduino-audio-tools/group__transform.html) +- Communication - [ESPNowStream](https://pschatzmann.github.io/arduino-audio-tools/classaudio__tools_1_1_e_s_p_now_stream.html), [UDPStream](https://pschatzmann.github.io/arduino-audio-tools/classaudio__tools_1_1_u_d_p_stream.html) +- Any other Arduino Classes implementing Streams: SD, Ethernet etc + +As “Audio Sinks” we will have e.g: + +- external DAC – [I2SStream](https://pschatzmann.github.io/arduino-audio-tools/classaudio__tools_1_1_i2_s_stream.html) +- Analog output e.g. to an Amplifier – [AnalogAudioStream](https://pschatzmann.github.io/arduino-audio-tools/classaudio__tools_1_1_analog_audio_stream.html) +- Output using PWM – [PWMAudioOutput](https://pschatzmann.github.io/arduino-audio-tools/classaudio__tools_1_1_p_w_m_audio_output.html) +- Output to SPDIF/TOSLINK - [SPDIFOutput](https://pschatzmann.github.io/arduino-audio-tools/classaudio__tools_1_1_s_p_d_i_f_output.html) +- Encoded Audio - [EncodedAudioStream](https://pschatzmann.github.io/arduino-audio-tools/classaudio__tools_1_1_encoded_audio_stream.html) +- Bluetooth Speakers – [A2DPStream](https://pschatzmann.github.io/arduino-audio-tools/classaudio__tools_1_1_a2_d_p_stream.html) +- Serial to display the data as CSV – [CsvOutput](https://pschatzmann.github.io/arduino-audio-tools/classaudio__tools_1_1_csv_output.html) +- Serial to display the data as hex dump - [HexDumpOutput](https://pschatzmann.github.io/arduino-audio-tools/classaudio__tools_1_1_hex_dump_output.html) +- Encoding and Decoding of Audio [EncodedAudioStream](https://pschatzmann.github.io/arduino-audio-tools/classaudio__tools_1_1_encoded_audio_stream.html) +- Desktop Integration - [PortAudioStream](https://pschatzmann.github.io/arduino-audio-tools/classaudio__tools_1_1_port_audio_stream.html) [MiniAudioStream](https://pschatzmann.github.io/arduino-audio-tools/classaudio__tools_1_1_mini_audio_stream.html) [StdioStream](https://pschatzmann.github.io/arduino-audio-tools/classaudio__tools_1_1_stdio_stream.html) +- ID3 Metadata for MP3 - [MetaDataID3](https://pschatzmann.github.io/arduino-audio-tools/classaudio__tools_1_1_meta_data_i_d3.html) +- A Timer based Sink - [TimerCallbackAudioStream](https://pschatzmann.github.io/arduino-audio-tools/classaudio__tools_1_1_timer_callback_audio_stream.html) +- ESP32 Lyrat/AudioKit - [AudioKitStream](https://pschatzmann.github.io/arduino-audio-tools/classaudio__tools_1_1_audio_kit_stream.html) +- VS1053 Codec Module - [VS1053Stream](https://pschatzmann.github.io/arduino-audio-tools/classaudio__tools_1_1_v_s1053_stream.html) +- Callback integration e.g. with ESP8266Audio [AudioOutputWithCallback](https://pschatzmann.github.io/arduino-audio-tools/classaudio__tools_1_1_audio_output_with_callback.html) +- Output using FIR, IRR Filters - [FilteredStream](https://pschatzmann.github.io/arduino-audio-tools/classaudio__tools_1_1_filtered_stream.html) +- Determine the Volume - [VolumeOutput](https://pschatzmann.github.io/arduino-audio-tools/classaudio__tools_1_1_volume_output.html) +- Split the Output to different Destinations - [MultiOutput](https://pschatzmann.github.io/arduino-audio-tools/classaudio__tools_1_1_multi_output.html) +- 3 Band Equilizer - [Equilizer3Bands](https://pschatzmann.github.io/arduino-audio-tools/classaudio__tools_1_1_equilizer3_bands.html) +- LED Strip/Matrix - [LEDOutput](https://pschatzmann.github.io/arduino-audio-tools/classaudio__tools_1_1_l_e_d_output.html) +- FFT - [AudioRealFFT](https://pschatzmann.github.io/arduino-audio-tools/classaudio__tools_1_1_audio_real_f_f_t.html) and [AudioKissFFT](https://pschatzmann.github.io/arduino-audio-tools/classaudio__tools_1_1_audio_kiss_f_f_t.html) +- Tensorflow Lite - [TfLiteAudioStream](https://pschatzmann.github.io/arduino-audio-tools/classaudio__tools_1_1_tf_lite_audio_stream.html) +- [Converting Streams](https://pschatzmann.github.io/arduino-audio-tools/group__transform.html) +- Communication - [ESPNowStream](https://pschatzmann.github.io/arduino-audio-tools/classaudio__tools_1_1_e_s_p_now_stream.html), [UDPStream](https://pschatzmann.github.io/arduino-audio-tools/classaudio__tools_1_1_u_d_p_stream.html) +- Multiuser-Webserver for PCM Output - [AudioWAVServerEx](https://pschatzmann.github.io/arduino-audio-tools/classaudio__tools_1_1_audio_w_a_v_server_ex.html) +- Any other Arduino Classes implementing Streams: SD, Ethernet etc + +### Examples + +Here is an simple example which streams a file from the Flash Memory and writes it to I2S: ```C++ #include "AudioTools.h" @@ -33,7 +75,6 @@ Here is a simple example which streams a file from the Flash Memory and writes i uint8_t channels = 2; uint16_t sample_rate = 22050; -uint8_t bits_per_sample = 16; MemoryStream music(StarWars30_raw, StarWars30_raw_len); I2SStream i2s; // Output to I2S @@ -45,10 +86,8 @@ void setup(){ auto config = i2s.defaultConfig(TX_MODE); config.sample_rate = sample_rate; config.channels = channels; - config.bits_per_sample = bits_per_sample; + config.bits_per_sample = 16; i2s.begin(config); - - music.begin(); } void loop(){ @@ -63,7 +102,7 @@ I suggest you continue to read the more [detailed introduction](https://github.c Further examples can be found in the [Wiki](https://github.com/pschatzmann/arduino-audio-tools/wiki/Examples). -Dependent on the example you might need to install some [additional libaries](https://github.com/pschatzmann/arduino-audio-tools/wiki/Optional-Libraries) +Dependent on the example you might need to install some [addional libaries](https://github.com/pschatzmann/arduino-audio-tools/wiki/Optional-Libraries) ### AudioPlayer @@ -72,20 +111,21 @@ The library also provides a versatile [AudioPlayer](https://pschatzmann.github.i ### Logging -The application uses a built in logger: By default we use the log level warning and the logging output is going to Serial. You can change this in your sketch by calling e.g: +The application uses a built in logger (see AudioLogger.h and AudioConfig.h). You can e.g. deactivate the logging by changing USE_AUDIO_LOGGING to false in the AudioConfig.h: ```C++ -AudioToolsLogger.begin(Serial, AudioToolsLogLevel::Debug); +#define USE_AUDIO_LOGGING false +#define LOG_LEVEL AudioLogger::Warning +#define LOG_STREAM Serial ``` -You can log to any object that is a subclass of Print and valid log level values are: Debug, Info, Warning, Error. - -You can also deactivate the logging by changing USE_AUDIO_LOGGING to false in the AudioConfig.h to decrease the memory usage: +Per default we use the log level warning and the logging output is going to Serial. You can also change this in your sketch by calling AudioLogger begin with the output stream and the log level e.g: ```C++ -#define USE_AUDIO_LOGGING false +AudioLogger::instance().begin(Serial, AudioLogger::Debug); ``` + ## Show and Tell Get some inspiration [from projects that were using this library](https://github.com/pschatzmann/arduino-audio-tools/discussions/categories/show-and-tell) or share your projects with the community. @@ -95,29 +135,11 @@ Get some inspiration [from projects that were using this library](https://github Please use this before you raise any issue or start a discussion! -- Read the [Tutorial & Documentation in the Wiki](https://github.com/pschatzmann/arduino-audio-tools/wiki) -- Have a look at the [Examples](https://github.com/pschatzmann/arduino-audio-tools/wiki/Examples) -- Check the [Class Documentation by Topic](https://pschatzmann.github.io/arduino-audio-tools/topics.html). +- Read the [Tutorial in the Wiki](https://github.com/pschatzmann/arduino-audio-tools/wiki) +- Check the [Class Documentation by Topic](https://pschatzmann.github.io/arduino-audio-tools/modules.html). - Find your class in [All Classes Alphabetically](https://pschatzmann.github.io/arduino-audio-tools/namespaceaudio__tools.html) - You also might find further information in [one of my Blogs](https://www.pschatzmann.ch/home/category/machine-sound/) -## Support - -I spent a lot of time to provide a comprehensive and complete documentation. -So please read the documentation first and check the issues and discussions before posting any new ones on Github. - -Open __issues only for bugs__ and if it is not a bug, use a discussion: Provide enough information about -- the selected scenario/sketch -- what exactly you are trying to do -- your hardware -- your software version (from the Boards Manager) and library versions -- what exactly your problem is - -to enable others to understand and reproduce your issue. - -Finally, __don't__ send me any e-mails or post questions on my personal website! - -Please note that discussions and issues which have already been answered before or where the answer can be found in the documentation may get deleted w/o reply. ## Installation in Arduino @@ -129,7 +151,7 @@ git clone https://github.com/pschatzmann/arduino-audio-tools.git ``` I recommend to use git because you can easily update to the latest version just by executing the ```git pull``` command in the project folder. -If you want to use the library on other patforms, you can find [further information in the Wiki](https://github.com/pschatzmann/arduino-audio-tools/wiki). +If you want to use the library in PlatformIO, you can find a [detailed description in the Wiki](https://github.com/pschatzmann/arduino-audio-tools/wiki/Working-with-PlatformIO). ## Sponsor Me diff --git a/component.mk b/component.mk deleted file mode 100644 index a98f634eae..0000000000 --- a/component.mk +++ /dev/null @@ -1,4 +0,0 @@ -# -# "main" pseudo-component makefile. -# -# (Uses default behaviour of compiling all source files in directory, adding 'include' to include path.) diff --git a/docs/index.html b/docs/index.html index fbb86a45e5..1147729dc1 100644 --- a/docs/index.html +++ b/docs/index.html @@ -1,6 +1,6 @@ - + -

If you are not redirected, click here.

+

If you are not redirected, click here.

diff --git a/docs/tensorflow/Dockerfile b/docs/tensorflow/Dockerfile new file mode 100644 index 0000000000..9e8589a8a9 --- /dev/null +++ b/docs/tensorflow/Dockerfile @@ -0,0 +1,16 @@ +FROM snowzach/tensorflow-multiarch +#FROM tensorflow/tensorflow:latest-jupyter +LABEL maintainer="phil schatzmann" +RUN /usr/bin/python3 -m pip install --upgrade pip +RUN pip3 install seaborn matplotlib jupyterlab + +# Working directory +RUN mkdir /content +WORKDIR /content +VOLUME /content +ADD micro_speech_with_lstm_op.ipynb /content +ADD train_micro_speech_model.ipynb /content +ADD train_hello_world_model.ipynb /content +RUN apt-get update && apt-get install git -y && apt-get upgrade -y +EXPOSE 8888 +CMD ["jupyter", "lab", "--no-browser", "--allow-root", "--ip=0.0.0.0" ] diff --git a/docs/tensorflow/docker-compose.yml b/docs/tensorflow/docker-compose.yml new file mode 100644 index 0000000000..ec6b41a930 --- /dev/null +++ b/docs/tensorflow/docker-compose.yml @@ -0,0 +1,9 @@ +version: "3.3" +services: + tensorflow: + container_name: tensorflow + ports: + - "8888:8888" + - "6006:6006" + restart: always + build: . diff --git a/docs/tensorflow/micro_speech_with_lstm_op.ipynb b/docs/tensorflow/micro_speech_with_lstm_op.ipynb new file mode 100644 index 0000000000..5f06b3d4c2 --- /dev/null +++ b/docs/tensorflow/micro_speech_with_lstm_op.ipynb @@ -0,0 +1,2601 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": { + "id": "fluF3_oOgkWF" + }, + "source": [ + "##### Copyright 2021 The TensorFlow Authors." + ] + }, + { + "cell_type": "code", + "execution_count": 112, + "metadata": { + "cellView": "form", + "id": "AJs7HHFmg1M9" + }, + "outputs": [], + "source": [ + "#@title Licensed under the Apache License, Version 2.0 (the \"License\");\n", + "# you may not use this file except in compliance with the License.\n", + "# You may obtain a copy of the License at\n", + "#\n", + "# https://www.apache.org/licenses/LICENSE-2.0\n", + "#\n", + "# Unless required by applicable law or agreed to in writing, software\n", + "# distributed under the License is distributed on an \"AS IS\" BASIS,\n", + "# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n", + "# See the License for the specific language governing permissions and\n", + "# limitations under the License." + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "jYysdyb-CaWM" + }, + "source": [ + "# Simple audio recognition: Recognizing keywords" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "SPfDNFlb66XF" + }, + "source": [ + "This tutorial will show you how to build a basic speech recognition network that recognizes ten different words. It's important to know that real speech and audio recognition systems are much more complex, but like MNIST for images, it should give you a basic understanding of the techniques involved. Once you've completed this tutorial, you'll have a model that tries to classify a one second audio clip as \"down\", \"go\", \"left\", \"no\", \"right\", \"stop\", \"up\" and \"yes\"." + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "Go9C3uLL8Izc" + }, + "source": [ + "## Setup\n", + "\n", + "Import necessary modules and dependencies." + ] + }, + { + "cell_type": "code", + "execution_count": 113, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "dzLKpmZICaWN", + "outputId": "9ba23229-9705-42df-8f7b-c4c321660a9b" + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "2.5.0\n" + ] + } + ], + "source": [ + "import os\n", + "import pathlib\n", + "\n", + "import matplotlib.pyplot as plt\n", + "import numpy as np\n", + "import seaborn as sns\n", + "import tensorflow as tf\n", + "print(tf.version.VERSION)\n", + "from tensorflow.keras.layers.experimental import preprocessing\n", + "from tensorflow.keras import layers\n", + "from tensorflow.keras import models\n", + "from IPython import display\n", + "\n", + "# Set seed for experiment reproducibility\n", + "seed = 42\n", + "tf.random.set_seed(seed)\n", + "np.random.seed(seed)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "yR0EdgrLCaWR" + }, + "source": [ + "## Import the Speech Commands dataset\n", + "\n", + "You'll write a script to download a portion of the [Speech Commands dataset](https://www.tensorflow.org/datasets/catalog/speech_commands). The original dataset consists of over 105,000 WAV audio files of people saying thirty different words. This data was collected by Google and released under a CC BY license.\n", + "\n", + "You'll be using a portion of the dataset to save time with data loading. Extract the `mini_speech_commands.zip` and load it in using the `tf.data` API." + ] + }, + { + "cell_type": "code", + "execution_count": 114, + "metadata": { + "id": "2-rayb7-3Y0I" + }, + "outputs": [], + "source": [ + "data_dir = pathlib.Path('data/mini_speech_commands')\n", + "if not data_dir.exists():\n", + " tf.keras.utils.get_file(\n", + " 'mini_speech_commands.zip',\n", + " origin=\"http://storage.googleapis.com/download.tensorflow.org/data/mini_speech_commands.zip/",\n", + " extract=True,\n", + " cache_dir='.', cache_subdir='data')" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "h_72nZHA9UH9" + }, + "source": [ + "Moving wav files from command directories to unknown sub-directory (Factory Reset to reset data directory)\n" + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "vB4Jq4Qg35iZ", + "outputId": "85d90934-908c-48b2-85b7-8500ad302e04" + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "mkdir: /content: No such file or directory\n", + "mkdir: /content/data/mini_speech_commands: No such file or directory\n", + "mv: rename /content/data/mini_speech_commands/down/* to /content/data/mini_speech_commands/unknown: No such file or directory\n", + "rm: /content/data/mini_speech_commands/down: No such file or directory\n", + "mv: rename /content/data/mini_speech_commands/go/* to /content/data/mini_speech_commands/unknown: No such file or directory\n", + "rm: /content/data/mini_speech_commands/go: No such file or directory\n", + "mv: rename /content/data/mini_speech_commands/left/* to /content/data/mini_speech_commands/unknown: No such file or directory\n", + "rm: /content/data/mini_speech_commands/left: No such file or directory\n", + "mv: rename /content/data/mini_speech_commands/right/* to /content/data/mini_speech_commands/unknown: No such file or directory\n", + "rm: /content/data/mini_speech_commands/right: No such file or directory\n", + "mv: rename /content/data/mini_speech_commands/stop/* to /content/data/mini_speech_commands/unknown: No such file or directory\n", + "rm: /content/data/mini_speech_commands/stop: No such file or directory\n", + "mv: rename /content/data/mini_speech_commands/up/* to /content/data/mini_speech_commands/unknown: No such file or directory\n", + "rm: /content/data/mini_speech_commands/up: No such file or directory\n", + "rm: /content/data/mini_speech_commands/README.md: No such file or directory\n", + "ls: /content/data/mini_speech_commands/unknown: No such file or directory\n", + " 0\n" + ] + } + ], + "source": [ + "!mkdir /content/keras_lstm\n", + "# comment out below line if \"unknown\" directory already exists\n", + "!mkdir /content/data/mini_speech_commands/unknown\n", + "# moves files from their specific commands directory to the \"unknown\" directory (replaces all files with an existing name therefore example set is smaller)\n", + "!mv /content/data/mini_speech_commands/down/* /content/data/mini_speech_commands/unknown\n", + "!rm -d /content/data/mini_speech_commands/down\n", + "!mv /content/data/mini_speech_commands/go/* /content/data/mini_speech_commands/unknown\n", + "!rm -d /content/data/mini_speech_commands/go\n", + "!mv /content/data/mini_speech_commands/left/* /content/data/mini_speech_commands/unknown\n", + "!rm -d /content/data/mini_speech_commands/left\n", + "!mv /content/data/mini_speech_commands/right/* /content/data/mini_speech_commands/unknown\n", + "!rm -d /content/data/mini_speech_commands/right\n", + "!mv /content/data/mini_speech_commands/stop/* /content/data/mini_speech_commands/unknown\n", + "!rm -d /content/data/mini_speech_commands/stop\n", + "!mv /content/data/mini_speech_commands/up/* /content/data/mini_speech_commands/unknown\n", + "!rm -d /content/data/mini_speech_commands/up\n", + "!rm /content/data/mini_speech_commands/README.md\n", + "!ls /content/data/mini_speech_commands/unknown | wc -l" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "BgvFq3uYiS5G" + }, + "source": [ + "Sets wanted commands for training (Available commands: Down, Go, Left, No, Right, Stop, Up, Yes, and Unknown for commands that are not to be tested" + ] + }, + { + "cell_type": "code", + "execution_count": 116, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "70IBxSKxA1N9", + "outputId": "a22025ee-8e85-4377-b223-b1d804fe5a25" + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Commands: ['yes', 'no', 'unknown']\n" + ] + } + ], + "source": [ + "commands = np.array(tf.io.gfile.listdir(str(data_dir)))\n", + "commands = [\"yes\",\"no\", \"unknown\"]\n", + "print('Commands:', commands)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "aMvdU9SY8WXN" + }, + "source": [ + "Extract the audio files into a list and shuffle it." + ] + }, + { + "cell_type": "code", + "execution_count": 117, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "hlX685l1wD9k", + "outputId": "c1847369-0ba1-4ddc-f817-e55e08fdced2" + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Number of total examples: 5311\n", + "Number of examples per label: 1000\n", + "Example file tensor: tf.Tensor(b'data/mini_speech_commands/no/53458368_nohash_0.wav', shape=(), dtype=string)\n" + ] + } + ], + "source": [ + "filenames = tf.io.gfile.glob(str(data_dir) + '/*/*')\n", + "filenames = tf.random.shuffle(filenames)\n", + "num_samples = len(filenames)\n", + "print('Number of total examples:', num_samples)\n", + "print('Number of examples per label:',\n", + " len(tf.io.gfile.listdir(str(data_dir/commands[0]))))\n", + "print('Example file tensor:', filenames[0])" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "9vK3ymy23MCP" + }, + "source": [ + "Split the files into training, validation and test sets using a 80:10:10 ratio, respectively." + ] + }, + { + "cell_type": "code", + "execution_count": 118, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "Cv_wts-l3KgD", + "outputId": "8d1c6417-0157-4945-f0ef-864a87ce6679" + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Training set size 4249\n", + "Validation set size 531\n", + "Test set size 531\n" + ] + } + ], + "source": [ + "# Take 80% of total number examples for training set files\n", + "train_files = filenames[:4249]\n", + "# Take 10% of total number examples adding to 80% of total examples for validation set files\n", + "val_files = filenames[4249: 4249 + 531]\n", + "# Take -10% of total number examples for test set files\n", + "test_files = filenames[-531:]\n", + "\n", + "print('Training set size', len(train_files))\n", + "print('Validation set size', len(val_files))\n", + "print('Test set size', len(test_files))" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "g2Cj9FyvfweD" + }, + "source": [ + "## Reading audio files and their labels" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "j1zjcWteOcBy" + }, + "source": [ + "The audio file will initially be read as a binary file, which you'll want to convert into a numerical tensor.\n", + "\n", + "To load an audio file, you will use [`tf.audio.decode_wav`](https://www.tensorflow.org/api_docs/python/tf/audio/decode_wav), which returns the WAV-encoded audio as a Tensor and the sample rate.\n", + "\n", + "A WAV file contains time series data with a set number of samples per second. \n", + "Each sample represents the amplitude of the audio signal at that specific time. In a 16-bit system, like the files in `mini_speech_commands`, the values range from -32768 to 32767. \n", + "The sample rate for this dataset is 16kHz.\n", + "Note that `tf.audio.decode_wav` will normalize the values to the range [-1.0, 1.0]." + ] + }, + { + "cell_type": "code", + "execution_count": 119, + "metadata": { + "id": "9PjJ2iXYwftD" + }, + "outputs": [], + "source": [ + "def decode_audio(audio_binary):\n", + " audio, _ = tf.audio.decode_wav(audio_binary)\n", + " return tf.squeeze(audio, axis=-1)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "GPQseZElOjVN" + }, + "source": [ + "The label for each WAV file is its parent directory." + ] + }, + { + "cell_type": "code", + "execution_count": 120, + "metadata": { + "id": "8VTtX1nr3YT-" + }, + "outputs": [], + "source": [ + "def get_label(file_path):\n", + " parts = tf.strings.split(file_path, os.path.sep)\n", + "\n", + " # Note: You'll use indexing here instead of tuple unpacking to enable this \n", + " # to work in a TensorFlow graph.\n", + " return parts[-2] " + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "E8Y9w_5MOsr-" + }, + "source": [ + "Let's define a method that will take in the filename of the WAV file and output a tuple containing the audio and labels for supervised training." + ] + }, + { + "cell_type": "code", + "execution_count": 121, + "metadata": { + "id": "WdgUD5T93NyT" + }, + "outputs": [], + "source": [ + "def get_waveform_and_label(file_path):\n", + " label = get_label(file_path)\n", + " audio_binary = tf.io.read_file(file_path)\n", + " waveform = decode_audio(audio_binary)\n", + " return waveform, label" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "nvN8W_dDjYjc" + }, + "source": [ + "You will now apply `process_path` to build your training set to extract the audio-label pairs and check the results. You'll build the validation and test sets using a similar procedure later on." + ] + }, + { + "cell_type": "code", + "execution_count": 122, + "metadata": { + "id": "0SQl8yXl3kNP" + }, + "outputs": [], + "source": [ + "AUTOTUNE = tf.data.AUTOTUNE\n", + "files_ds = tf.data.Dataset.from_tensor_slices(train_files)\n", + "waveform_ds = files_ds.map(get_waveform_and_label, num_parallel_calls=AUTOTUNE)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "voxGEwvuh2L7" + }, + "source": [ + "Let's examine a few audio waveforms with their corresponding labels." + ] + }, + { + "cell_type": "code", + "execution_count": 123, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/", + "height": 716 + }, + "id": "8yuX6Nqzf6wT", + "outputId": "8575133a-16c0-4d08-9857-ee4ecbfbe2fd" + }, + "outputs": [ + { + "data": { + "image/png": "iVBORw0KGgoAAAANSUhEUgAAAlsAAAK7CAYAAADFgUrzAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAALEgAACxIB0t1+/AAAADh0RVh0U29mdHdhcmUAbWF0cGxvdGxpYiB2ZXJzaW9uMy4yLjIsIGh0dHA6Ly9tYXRwbG90bGliLm9yZy+WH4yJAAAgAElEQVR4nOzdeZwcdZ3/8ddn7tzn5E4ggXBEQI4hgCyQ5ZAQlIDuSkBXLo2usqu/xXWDsKh4gLritagbFQOoIKJAkACGW+4MRyB3Qgi5k0lC7mSu/vz+6JpJ92R6ZpKu7urj/Xw88piu6m/X9zOTec98pqq6ytwdEREREcmMkqgLEBERESlkarZEREREMkjNloiIiEgGqdkSERERySA1WyIiIiIZpGZLREREJIPUbImIiBQYM5thZt+Oug6JU7MlIiIikkFqtkREREQySM1WgTCzFWb2FTN7y8y2mdkfzawqeO6zZrbMzLaY2UwzGxZ1vSKZpkxIvjMzN7PDE5ZbDw2a2QQzW21m15nZRjNbZ2ZXpdhOLzN72sx+anEzzOx2M3vEzHaY2StmdljC+A+Z2ZwgN3PM7EPB+n80s7cTxs02szkJy383s4uDxynzV4zUbBWWTwATgdHAccCVZnY2cEvw3FDgPeDeyCoUyS5lQgrZEKAPMBy4BrjdzPolDjCzAcCTwAvu/u++7x59U4BvAv2AZcB3gvH9gUeAnwIDgNuAR4LtvAyMNbOBZlZOPFPDgmauG1AD/D1h+v3yF+6nnz/UbBWWn7r7WnffAjwMHA98ErjD3V9393rgeuA0Mzs0ujJFskaZkELWCNzs7o3uPgvYCRyZ8Pww4FngT+5+Y5vXPuDur7p7E/B74tkAuBBY6u53u3uTu98DLAI+6u57gDnAmcBJwFzgBeB04NTgdZsT5mgvf0VJzVZhWZ/weDfQk3jY3mtZ6e47gc3E/xISKXTKhBSyzUGz1KLle7zFhUA34JftvLa9bECbfATeY18+ngUmEG+4ngWeAc4K/j3bxTmKjpqtwrcWOKRlwcx6EN81vCayikSipUxIvtgNdE9YHnKAr/8V8BgwK/g+74qkfARGsS8fbZutZ0ndbElAzVbhuwe4ysyON7NK4LvAK+6+ItqyRCKjTEi+eBO43MxKzWwi8YbmQF0LLAYeDs6r6sws4Agzu9zMyszsUmAc8Nfg+ReJH6ocD7zq7vOJN2enAM8dRH1FQc1WgXP3J4D/Bv4MrAMOI35ipEhRUiYkj3wJ+Ciwlfi5hg8e6AaCE+KnAquBhzp7R2BwztVHgOuIH17/KvARd98UPL8LeB2Y7+4NwcteAt5z940HWl+xsH1vTBARERGRsGnPloiIiEgGhdJsmdkdwUXV5qV43oKLqS0LLnB2YhjziuQqZUIkmTIhxSysPVsziF+4LJULgLHBv6nAL0KaVyRXzUCZEEk0A2VCilQozZa7Pwds6WDIZOAuj3sZ6GtmQ8OYWyQXKRMiyZQJKWZlWZpnOLAqYXl1sG5d24FmNpX4XzX06NHjpKOOOiorBYp05rXXXtvk7tUhbU6ZkLwXRSaUB8llqTKRrWary9x9OjAdoKamxmtrayOuSCTOzNpeVTkrlAnJVVFkQnmQXJYqE9l6N+IaYGTC8gh0tWYpbsqESDJlQgpWtpqtmcCng3ebnApsc/f9DpeIFBFlQiSZMiEFK5TDiGZ2D/F7JQ00s9XA14FyAHf/JfHL/08ClhG/19NVYcwrkquUCZFkyoQUs1CaLXe/rJPnHfhiGHOJ5ANlQiSZMiHFTFeQFxEREckgNVsiIiIiGaRmS0RERCSD1GyJiIiIZJCaLREREZEMUrMlIiIikkFqtkREREQySM2WiIiISAap2RIRERHJoFCaLTObaGaLzWyZmU1r5/lRZva0mb1hZm+Z2aQw5hXJVcqESDJlQopZ2s2WmZUCtwMXAOOAy8xsXJthNwL3ufsJwBTg5+nOK5KrlAmRZMqEFLsw9myNB5a5+3J3bwDuBSa3GeNA7+BxH2BtCPOK5CplQiSZMiFFLYxmaziwKmF5dbAu0TeATwV3ep8F/FuqjZnZVDOrNbPaurq6EMoTyTplQiRZaJlQHiQfZesE+cuAGe4+ApgE3G1m7c7t7tPdvcbda6qrq7NUnkjWKRMiybqUCeVB8lEYzdYaYGTC8ohgXaJrgPsA3P0loAoYGMLcIrlImchhP3tyKY/NWx91GcVGmZCiFkazNQcYa2ajzayC+ImNM9uMWQmcA2BmRxMPkfb/SqFSJnLYD2cv4fO/ey3qMoqNMiFFLe1my92bgGuBx4GFxN9NMt/Mbjazi4Jh1wGfNbO5wD3Ale7u6c4tkouUicJzynefYMr0l6IuI28pE1LsysLYiLvPIn5CY+K6mxIeLwBOD2MukXygTOS+/31qKdeePbbDMc8uqeNzd9eytzHGhu31WaqsMCkTUsx0BXkRKUq3zV7S6Zgr7niVvY2xLFQjIoVMzZaIFKWYw3ceWUBDU9ebqacWbchgRSJSqNRsiUhR2NvYzL2vrkxa96u/v8sfa1eleMX+rp5RG3ZZIlIEQjlnS0Qk113y8xdZuG77fusbD2DPFsDsBRs4b9zgsMoSkSKgPVsiUhTaa7QOxmfv0t4tETkwarZEpOBt2dWQ8jmzLBYiIkVJzZaIFLxr7pwTdQkiUsTUbIlIwVu1ZXfGtu3u/OGVlWzb3ZixOUQkv4XSbJnZRDNbbGbLzGxaijGfMLMFZjbfzP4QxrwiuUqZyC2ZvA75vDXb+doDb/OV++dmbpICoExIMUv73YhmVgrcDpwHrAbmmNnM4GrALWPGAtcDp7v7+2Y2KN15RXKVMpHf3B0z473Nu1KO2dvYTFV5KQD1Tc1Ax+eFFTtlQopdGHu2xgPL3H25uzcA9wKT24z5LHC7u78P4O4bQ5hXJFcpEzmmox1bTc3Otj3xQ4Brt+5h9PWzuK92FR/+0XMpX3Pdn+YGr41x818XpBwnrZQJKWphNFvDgcSrAq4O1iU6AjjCzF4ws5fNbGKqjZnZVDOrNbPaujrd8F3ykjKRR74zayEf/ObfAHinbicAX73/Leo7uP7WS+9sBuC5pXW8tXpb5ovMf6FlQnmQfJStE+TLgLHABOAy4Fdm1re9ge4+3d1r3L2muro6S+WJZJ0ykUVNzeHe39CDk8Bium1imLqUCeVB8lEYzdYaYGTC8ohgXaLVwEx3b3T3d4ElxEMlUoiUiRyzfW9Tl8YZuuhWhigTUtTCaLbmAGPNbLSZVQBTgJltxjxI/K8VzGwg8d3Fy0OYWyQXKRN56kAucFq3o57PJFxN/rX33mf6c++wftveDFSW95QJKWppN1vu3gRcCzwOLATuc/f5ZnazmV0UDHsc2GxmC4Cngf90983pzi2Si5SJ4vD2mq37rfvurEX8y29eiaCa3KZMSLEL5UbU7j4LmNVm3U0Jjx34j+CfSMFTJorXxh31UZeQk5QJKWa6gryISCCMM7a27WnUNbdEJImaLRGRkHV0jS4RKT5qtkREWoT0ZsRNO3UoUUT2UbMlIgIcOu0Rale8H3UZIlKA1GyJiARum72kS+Pe391IfaOuaCoiXaNmS0TkIFz/wNtRlyAieULNlojIQdi6uzHqEkQkT6jZEhEREckgNVsiIiIiGRRKs2VmE81ssZktM7NpHYz7uJm5mdWEMa9IrlImRJIpE1LM0m62zKwUuB24ABgHXGZm49oZ1wv4EqAbh0lBUyZEkikTUuzC2LM1Hljm7svdvQG4F5jczrhvAd8D9oYwp0guUyZEkikTUtTCaLaGA6sSllcH61qZ2YnASHd/pLONmdlUM6s1s9q6uroQyhPJOmVCuPK3r0ZdQi4JLRPKg+SjjJ8gb2YlwG3AdV0Z7+7T3b3G3Wuqq6szW5xIBJSJ4vDMYjUCXXUgmVAeJB+F0WytAUYmLI8I1rXoBRwDPGNmK4BTgZk6+VEKmDIhkkyZiJC7s6ehmW17Glm/bS9PLNjAik27oi6rqJSFsI05wFgzG008PFOAy1uedPdtwMCWZTN7BviKu9eGMLdILlImRJIpExH6/SsrufHBefutX3HrhRFUU5zS3rPl7k3AtcDjwELgPnefb2Y3m9lF6W5fJN8oEyLJlIloPTZvfdQlFL0w9mzh7rOAWW3W3ZRi7IQw5hTJZcqEAKzYtIuH3lzLv59zOGYWdTmRUiais2j99g6f//ZfF9C/ZwUPvL6Gs48axPWTjs5SZcUjlGZLRET2d8VvX+W9zbu59OSRDOlTFXU5UqQ27Wxod/2dL67glDH9+fXz77auW7pxp5qtDFCzJSKSIe9t3h11CSIpfX3m/KhLKBq6N6KIiIhIBqnZEhHJsPNue1ZvtRcpYmq2RKSg5UKTs6O+iV8/vzzqMkQkImq2RKRgNcecCf/zTNRliEiRU7MlIgXr4blroy6hlVHcl34QKWZqtkSkIG3csZcv//HNqMto5XjUJYhIREJptsxsopktNrNlZjatnef/w8wWmNlbZvakmR0SxrwiuUqZiN747zwZdQmSQJmQYpZ2s2VmpcDtwAXAOOAyMxvXZtgbQI27HwfcD3w/3XlFcpUyIZJMmcgvP3liKcs27oy6jIISxp6t8cAyd1/u7g3AvcDkxAHu/rS7t1zd72Xid3wXKVTKRIQu+fkLHDrtkajLkGTKRB750RNLmDL9pajLKChhNFvDgVUJy6uDdalcAzya6kkzm2pmtWZWW1dXF0J5IlmnTETojZVboy6hXV7cp2yFlgnlITsammJRl1BQsnqCvJl9CqgBfpBqjLtPd/cad6+prq7OXnEiEVAmwrUyh2+P8/tXVnLLrIVRl5HzOsuE8iD5KIxmaw0wMmF5RLAuiZmdC9wAXOTu9SHMK5KrlImInPmDp6MuoUP/91zRXthUmZCiFkazNQcYa2ajzawCmALMTBxgZicA/0c8QBtDmFMklykTEWhszo/DHovX74i6hCgoE3mmuI96hy/tZsvdm4BrgceBhcB97j7fzG42s4uCYT8AegJ/MrM3zWxmis2J5D1lIrvcnYamGBff/kLUpXTJ+T9+LuoSsk6ZyD879jZFXUJBKQtjI+4+C5jVZt1NCY/PDWMekXyhTGRHU3OMr97/Fn95Y78jUpJjlAkpZrqCvIjkpabmGJf96uW8bLSmTH+J11e+H3UZIpIlarayaPveRu56aQVe5O8BFwnDz55axpwV+dmwvLx8C/91/1tRlyHSoVVbcvfdvflGzdYBuOfVlVzy8xfY3dDE7oYmvv7QPJ5bUsfW3Q1dOjn3vx+cx00PzWf09bN46M017G5oYsmGHfxo9hI+8rO/Z+EzECkc+X6Fa/3JJdkw/bl3Dvq1Z3w/t9/dm09COWerWFz/l7cBGHfT463r7nzpPQC6lZfSo7KUTTsbqCov4aPHDeNbFx9DVXkp79Tt5CdPLGXm3LWtr/vSvfvfILflqtcvXX82Q/t0y+SnIpL3Skos6hJEctqu+ia+O2tRWts4dNoj/OaKGs45enBIVRUnNVtd9NSiDR0+v6exmT2NzQDsbYzxp9dW86fXVh/UXKfd8hQfGNabu685hf49Kg5qGyKFLt9brbVb9zBvzTaOGd4n6lKkQIW193TW2+vVbKVJhxG76PuPLc7qfPPXbufEb83m6hlzeH7pJu54/l2WbCjK6/OItGtnfX6/NX13QzMf+dnzUZchBSys84NXbtkVynaKmfZsdcDdeaduJ4N6V7EoogsRPrVoI08tSr6+339NPIrPnTlGh1GkaD3wxur9ciEiyd4L6fZVc1a8z56GZp5ctIEPjxtCRZn20xwoNVvA3sZm5q/dxuPzN/DEwg0sr8vtLv57jy3ie48t4uLjh/Hv54xl9MAemKnxkuLx9KLCuQHxb194l4s+OIwelWVUlZdGXY4UkHteXRnato6+6TEALq0Zyff+6bjQtlssQmm2zGwi8BOgFPi1u9/a5vlK4C7gJGAzcKm7rwhj7q7atruR+19fzcvLN7N9TyM765uYv3Z7NksI3YNvruXBN/eddH/64QO44rRDOWFUP6p7VUZYmeRDJvJZaQHt1f3mwwv45sMLGFPdg6eumxB1ORmjTGTGll0NNMec6l6VbN/byNxVW+ldVc4HR/bl96+E12y1+GPtKv5Yu4qnrjuLQwb04Pllmzhz7MC0/+B/etFGyktLqDm0H0s27OC4EX1Dqjg3pN1smVkpcDtwHrAamGNmM919QcKwa4D33f1wM5sCfA+4NN25u2LB2u1M+mlxXFbhhWWbeWHZ5v3WD+tTxVlHDmJQr0ouPG4oRwzuFUF1xSPXMhGLOWYUzN5Pd+eBPLyQaWeW1+1iwdrtVJSVcPignlGXE6pcy0QhOfFbswG4+5rx/MtvXs3avGf/8Nn91v3yUyfx+d+9xuWnjOIPr6zkJ1OOZ/LxwwFYv20v9U3N7G2M8dMnl7KroYlvTT6GZXU7GT2gB1fNmJO0rbuuHs+ZR1Rn5XPJBkv3BDozOw34hrufHyxfD+DutySMeTwY85KZlQHrgWrvZPKamhqvra3db/3O+iZi7pSYUWpGSQmUmlFaYkm/UBau284FPymORutgjBvam7OOrOasI6opMWNQr0qqyktxPP617OT9Xl353d2VX+9daQK6tp0ujOlkS326l3ewfXvN3Ws6ryOaTDz05hrKS0uo7llJU8z57F37j0vlO5ccQ7/uFexpaGZw7yr6dCunoTnG3sZmulWUEos5g3pVUVICW3c3AlBRVkJpidHYHKM55qzYtJshfapYv20vu+qbKC8zdjc0c8TgXvHtNcXYsquBvt3LKTFr/f9yh/qmGIcP6smWXQ0A9Kwso8SgOeY0u9PU7DTHnJ31TTyxcAM/fmJplz+3fDXhyGo+8w9jOGxQD7btaaS8tIQde5uoKi9h2+5GKstLGdW/+76fgSUW/5lYYtTtqGfhuu10Ky9l4456+nQrp1tFKU3NzpbdDby/q4FeVWX07V7OwJ7xveA79zax6v3drfPMXbWVz5wxhnHDeqesMepMpMoDwPu7GigrNZz49xgOjuNOsM5bn/P4k0nLSeOCCtp7rjnm9OtRgTuUl8a//uWlJeyqb2L99r0M7dON93fHv96G7Xt9innaWx9zIGldfEwsRtHsTEjlyg8dyqad9SzdsJMR/brRvTK+D+kTNSPoUVnGrvomGppilJeWMHpgD/Y2NlNeWkLMnfLSEhqaY/SsLGPx+h307V5Oz8oyyktL6F5RSnPMaYw5Tc0xtu1pZMP2eo4b0YfBvatS1pMqE2EcRhwOrEpYXg2ckmqMuzeZ2TZgALDpYCb84u9f59kl7Z+zYUZrE9bQhQuNFrMF67azYN12fvHMwV/0rpCUlhjvfHdSGJvKaibcnWO+/njnAztwwwPz0nq9hO+ZxXU8szjac9P+8sYaVtx6YRibyvrviSnTX2ax3sFd8Ga8uKL1ceL/98MJ17UM2+JvT6Sy7MDOr8y5E+TNbCowFWDUqFHtjrn8lFGcMXYgMXeaYwQf4/9i7q3rZ729jpW63cBB+fK5YxnQs4PzvrqwR7Qr+0y7smO1K3tfw5grV4+ydZYJM2PqmWOY/tzybJcWqupeldTtqAfglNH9ufC4oZSYUVYS31tQ3xTjxgfVFCa64Jgh1Bzan1iwB7A5Ft8L+KMnlkRdWsZ05XcEwOcnjGHTjobWXJvF92ubEXy01sckPWcJY4K94YmvaVlvsGF7Pbc+uohJxw7h1DEDWvfANsZi/Prv77JlVwPnf2AwL72zmc+cMYZ+3csT5m0zT+u2k+coCT4Ba1NjSfD853/3eka+zsVqeN9unHP0IA6r7hnspTTKSkq46aF57Gpo5hsfHXfAjRaE02ytAUYmLI8I1rU3ZnWwe7gP8RMg9+Pu04HpEN9F3N6Y8z8wpEuFTbvgKL7/2CJ+XgR7bs49ejDLN+3kI8cN44Mj+jCsbzdG9OvG3FXbOO2wAZSWGO5eMOft5LisZ+Jrk47ma5OO3m/93+av57uzFnLv1NMY0if1ru988alTD8HdGX39rKhLyZjyUuOxL5/JYdUHf97Wl84dG2JFoQgtE13JA8AlJ4xIs+Su+fxZh7W7/gsTDs/K/Lnula+dw9xVW5m/djsz567l1DH9mb1gA5t2NnDx8cOS3uSVqLzUeP2/z+O9zbuTLvxb39RMqRkxJ6uXoPj4Sel9P4VxzlYZsAQ4h3hY5gCXu/v8hDFfBI51988HJz5+zN0/0dm2OzoefyBiMWfJxh088tY6Xlm+hbfWbGVvY34eYuxZWcbXJh3NlJNH6jpbWXYA56fkfCby3fNLN/Gp37wSdRmhevorExg9sEfUZRyQqDOhPMBDb66hd1U5Zx1Rzfu7GzjntmeZdOxQ/pCBdyImOmV0f7426Wgm3/4Cd149nrOOqCYWc37y5FI+c8ZoNu1soE+38i7fBaXldnU3Xng0Ty/eyN1Xn5KXv+NSZSLtZivY+CTgx8Tf0nuHu3/HzG4Gat19pplVAXcDJwBbgCnu3ukxj2wEyd3Z09jMkws38vTijTw8dy2NzdHeIvaCY4bQu6qcnlVlfPKUUYxJ4y9cCU9Xf7EEY/M2E/lg3bY9nHbLU1GXEYp3vjspby9lEXUmlIfUmppjnHbrU62H5sP04rSzGdY33Pv3rt+2lz2NzXn3B0dbmTxBHnefBcxqs+6mhMd7gX8OY66wmRndK8r46AeH8dEPDuO2Txzf+tz7uxp48Z3NPPL2Wma9vT7pdT//5ImcdEg/dtU3Maa6J7GYc8OD87jn1ZVcfsoorj59NG+v2cr5HxjC4vU72LyzgZv/uqDDc8i+//Hj+MTJI1M+L/kjnzORD/K1OWmre0VpwXwunVEmsqustIR+3ctDa7auOv1QNu1s4GeXnRDK9toqhNMcOpJzJ8jnkn49KrjwuKGcO24Qw/os5t/OGUvdjr0M6l1F76rkSwSUlBi3fOxYbvnYsa3rWq6Vc8KofgCcO24wLyzbRFmJccqYAbg7L72zmeNH9cUwulXo6tEiXRHSLd8it+DmiVGXIAXsa5OO5srfzul8YCd+dtkJfOS4oTrnNw1qtrqgsqyUGz8yDoA+3VJfh6krTj98YOtjM+NDCcsi0jWDelV2eHKtiMCRQ8K5gPVHPzgslO0UM91NUkTyjpnxb+fk3DvuDshXJx4ZdQlS4Ib26UbtjeemtY3rzjsipGqKm/ZsiUheyucDGjOuOpkJRw6KugwpAgM7ul5iF+T7HzW5Qnu2RCQvjR7Yg/+aeFTUZRyUrr4dXiRKFx+vw4dhUbMlInnJzPjXCYcx9+sfZtzQ1PfvyzW//8wpHDeib9RliHTqkAH5fRmGXKJmS0TyWp9u5cz60hkpr+Sda07Xm2JEio6aLREpCNd9WCfyioSpR6UuRxQWNVsiUhDKS0v4+SdPjLqMDj153VlRlyDSZVd+aHTUJRSMtJotM+tvZrPNbGnwsV87Y443s5fMbL6ZvWVml6Yzp0guUyaiNenYoTl9ODGdm0vnK2UiP038wJCs3ui50KX7lZwGPOnuY4Eng+W2dgOfdvcPABOBH5uZzg6VQqVMRGzaBUfx4rSzoy5jP4cM6B51CVFRJvKQLhYfrnSbrcnAncHjO4GL2w5w9yXuvjR4vBbYCFSnOa9IrlImcsCwvt1486bzoi4jSRH/7lImpOil22wNdvd1weP1wOCOBpvZeKACeKeDMVPNrNbMauvq6tIsTyTrlIkc0bd7BX27p3d7rTAV8X3lQs2E8pB5PSvL+FwOH47PR51eQd7MngCGtPPUDYkL7u5mlvL2sGY2FLgbuMLdY6nGuft0YDpATU1NgdxuVgqJMpE/fnfNKXzkZ89HXQYAw/pWRV1CxmQzE8pD5s375vlRl1BwOm223D3ljZXMbIOZDXX3dUFINqYY1xt4BLjB3V8+6GpFcoAykT+OGd4n6hJa3X55br9TMh3KhEjH0j2MOBO4Inh8BfBQ2wFmVgE8ANzl7venOZ9IrlMmcszZR+XGPQj7di/aW/QoE1L00m22bgXOM7OlwLnBMmZWY2a/DsZ8AjgTuNLM3gz+HZ/mvCK5SpnIMb/6dE3UJfDti4+JuoQoKRNS9Do9jNgRd98MnNPO+lrgM8Hj3wG/S2cekXyhTOSe0pLoT0zvWZnWj9q8pkyI6AryIiIiResfj0y+wsbk44dFVElhU7MlIpJhxXvVB8ll73x3Ep89Y0zSuoE9KyOqprCp2RIRyZAvnzuW40b0yZmT9EUSlZZYUV9tN5vUbImIZMiXzz2Cmdf+A72qcufiqiKJjh7SG4BTx/SPuJLCpmZLRCQD9MtL8kG/HhWsuPVCzjmqwwv7S5rUbImIZMC9U0+LugQRyRFqtkRERIrE2EE9AThkQPeIKykuarZERESKwAvTzubnnyzc20blsrSbLTPrb2azzWxp8LFfB2N7m9lqM/vfdOcVyVXKhMg+ykPuGN63G2Wl8V/7ehNidoWxZ2sa8KS7jwWeDJZT+RbwXAhziuQyZUJkH+Uhh3SvKAXgiMG92n1eTVhmhNFsTQbuDB7fCVzc3iAzOwkYDPwthDlFcpkyIbKP8pBDBveu4t6pp/KjS5NvPfmJk0dy9lGD+NxZh0VUWWEL44Zdg919XfB4PfGwJDGzEuCHwKeI34hUpJApE0WivNRobPaoy8h1ykPEXrr+bOobY63Lp44ZsN+YPt3KuePKk7NZVlHpUrNlZk8AQ9p56obEBXd3M2vvJ88XgFnuvto6uW+FmU0FpgKMGjWqK+WJZJ0yId//+HGc/4EhfPBm7YhRHnLb0D7doi6h6HWp2XL3lH9pmNkGMxvq7uvMbCiwsZ1hpwFnmNkXgJ5AhZntdPf9jt27+3RgOkBNTY3+ZJScpEzIJ04eydbdDe0+9+2Lj8lyNdFSHkQ6FsZhxJnAFcCtwceH2g5w90+2PDazK4Ga9kIkUiCUiSLhKX7Vf+rUQ7JbSG5THqTohXGC/K3AeWa2lPix9lsBzKzGzH4dwvZF8o0ykWOuPn10Rrbbt3s5/6LGqjPKgxS9tPdsuftm4Jx21tcCn2ln/QxgRrrziuQqZSL39Kgszch2zYxvXXwMd7/8Xka2XwiUB7zQvSUAACAASURBVJFwDiOKiAjw1YlHctSQ9q9fJCLFS82WiEhIvjDh8KhLEJEcpHsjioiEoOXK3CIibWnPlohImr738WM5+dD+UZchIjlKzZaIFLxM3+/t0pN1cU0RSU2HEUVEREQySM2WiIiISAap2RIRERHJIDVbIiIHqHeVTncVka5Lq9kys/5mNtvMlgYf+6UYN8rM/mZmC81sgZkdms68IrlKmSgOT31lQtQl5A1lQiT9PVvTgCfdfSzwZLDcnruAH7j70cB42r/ru0ghUCZykYX7fsSBPStD3V6BUyak6KXbbE0G7gwe3wlc3HaAmY0Dytx9NoC773T33WnOK5KrlAmRZMqEFL10m63B7r4ueLweGNzOmCOArWb2FzN7w8x+YGYpL7VsZlPNrNbMauvq6tIsTyTrlIkcpPsVRirUTCgPko86PcvTzJ4AhrTz1A2JC+7uZuYp5jgDOAFYCfwRuBL4TXvzuft0YDpATU1Ne9sTiZQykX8mHTs06hIKWjYzoTxIPuq02XL3c1M9Z2YbzGyou68zs6G0f4x9NfCmuy8PXvMgcCopfrGI5DplorhNOLI66hJyjjIh0rF0DyPOBK4IHl8BPNTOmDlAXzNr+Ql1NrAgzXlFcpUyUeBmXDU+6hLyjTIhRS/dZutW4DwzWwqcGyxjZjVm9msAd28GvgI8aWZvE79N2a/SnFckVykTeeLyU7p+P8PaG8/lrCOqOW9ce6cbSSeUCSl6aV2Zz903A+e0s74W+EzC8mzguHTmEskHykTu+uu//QNNMefi21+gd1UZxw7v0+XXVpSVcOfV2qN1MJQJEV1BXkSKxDHD+zB6QI+Uz3/0g8MAOHFU32yVJCJFQvecEJGiUVYav7jp4YN6ckj/7knPfeeSY/jZZScA8Itn3uF7jy3Ken0iUpjUbIlI0ehRWcbd14zn2OF96Nu9gktrRvLH2lUpxw/sWcGehma6l6e8DJqISKd0GFFEisoZY6vp270CgFs/fmyHYz9+4gjm3zyRslL9qBSRg6efICJStMyMez57KucePZieFe3s6A/3looiUqR0GFFEitpphw3gtMMGRF2GiBQw7dkSERERySA1WyIibVxywnBG9OvGJ8cfEnUpIlIA0m62zKy/mc02s6XBx34pxn3fzOab2UIz+6mZ6WwIKUjKRP4b0qeK5//rbEYN6N75YOmQ8iASzp6tacCT7j4WeDJYTmJmHwJOJ3514GOAk4GzQphbJBcpEyL7KA9S9MJotiYDdwaP7wQubmeMA1VABVAJlAMbQphbJBcpEyL7KA9S9MJotga7+7rg8Xpgvzu1uvtLwNPAuuDf4+6+sL2NmdlUM6s1s9q6uroQyhPJOmVCZB/lQYpely79YGZPAEPaeeqGxAV3dzPzdl5/OHA0MCJYNdvMznD3v7cd6+7TgekANTU1+21LJBcoEyL7KA8iHetSs+Xu56Z6zsw2mNlQd19nZkOBje0MuwR42d13Bq95FDgN2C9IIvlAmRDZR3kQ6VgYhxFnAlcEj68AHmpnzErgLDMrM7Ny4ic+truLWKQAKBMi+ygPUvTCaLZuBc4zs6XAucEyZlZjZr8OxtwPvAO8DcwF5rr7wyHMLZKLlAmRfZQHKXpp367H3TcD57Szvhb4TPC4GfhcunOJ5ANlQmQf5UFEV5AXERERySg1WyIiIiIZpGZLREREJIPUbImIiIhkkJotERERkQxSsyUiIiKSQWq2RERERDJIzZaIiIhIBqnZEhEREcmgtJotM/tnM5tvZjEzq+lg3EQzW2xmy8xsWjpziuQyZUIkmTIhkv6erXnAx4DnUg0ws1LgduACYBxwmZmNS3NekVylTIgkUyak6KV1b0R3XwhgZh0NGw8sc/flwdh7gcnAgnTmFslFyoRIMmVCJDvnbA0HViUsrw7WtcvMpppZrZnV1tXVZbw4kQgoEyLJupwJ5UHyUad7tszsCWBIO0/d4O4PhV2Qu08HpgPU1NR42NsXSZcyIZIsm5lQHiQfddpsufu5ac6xBhiZsDwiWCeSl5QJkWTKhEjHsnEYcQ4w1sxGm1kFMAWYmYV5RXKVMiGSTJmQgpbupR8uMbPVwGnAI2b2eLB+mJnNAnD3JuBa4HFgIXCfu89Pr2yR3KRMiCRTJkTSfzfiA8AD7axfC0xKWJ4FzEpnLpF8oEyIJFMmRHQFeREREZGMUrMlIiIikkFqtkREREQySM2WiIiISAap2RIRERHJIDVbIiIiIhmkZktEREQkg9RsiYiIiGRQuleQ/2czm29mMTOrSTFmpJk9bWYLgrFfSmdOkVymTIgkUyZE0t+zNQ/4GPBcB2OagOvcfRxwKvBFMxuX5rwiuUqZEEmmTEjRS/d2PQsBzKyjMeuAdcHjHWa2EBgOLEhnbpFcpEyIJFMmRNJstg6UmR0KnAC80sGYqcDUYHGnmS1OMXQgsCnM+rIoX2vP17ohnNoPCaOQRMpEq3ytPV/rhjzNxAHkAfT/E4V8rRsymIlOmy0zewIY0s5TN7j7Q12d3cx6An8Gvuzu21ONc/fpwPQubK/W3ds9/p/r8rX2fK0bwq1dmQhfvtaer3VD/maiq3kItqf/nyzL17ohs7V32my5+7npTmJm5cQD9Ht3/0u62xOJkjIhkkyZEOlYxi/9YPED9b8BFrr7bZmeTyTXKRMiyZQJKXTpXvrhEjNbDZwGPGJmjwfrh5nZrGDY6cC/AGeb2ZvBv0lpVR3Xpd3IOSpfa8/XuiFLtSsTBy1fa8/XukGZyHX5Wnu+1g0ZrN3cPVPbFhERESl6uoK8iIiISAap2RIRERHJoLxstsxsopktNrNlZjYt6noAzGyFmb0dnGtQG6zrb2azzWxp8LFfsN7M7KdB/W+Z2YkJ27kiGL/UzK7IUK13mNlGM5uXsC60Ws3spOBrsSx4beqrGaZf9zfMbE1753mY2fVBDYvN7PyE9e1+/5jZaDN7JVj/RzOrCKPuTMvFPIAyoUxER5kIpVZlIsxMuHte/QNKgXeAMUAFMBcYlwN1rQAGtln3fWBa8Hga8L3g8STgUcCI35rilWB9f2B58LFf8LhfBmo9EzgRmJeJWoFXg7EWvPaCDNb9DeAr7YwdF3xvVAKjg++Z0o6+f4D7gCnB418C/xr191UXviY5mYegNmVCmVAmkmtTJoo0E/m4Z2s8sMzdl7t7A3AvMDnimlKZDNwZPL4TuDhh/V0e9zLQ18yGAucDs919i7u/D8wGJoZdlLs/B2zJRK3Bc73d/WWPfzfelbCtTNSdymTgXnevd/d3gWXEv3fa/f4J/qo6G7g/eH3i1yCX5VMeQJlQJjJPmQiBMhFuJvKx2RoOrEpYXh2si5oDfzOz1yx+OwmAwR6/5xfAemBw8DjV5xDl5xZWrcODx23XZ9K1wa7rO1p2a3dSX3vrBwBb3b2pzfpcl6t5AGVCmYiGMpE5ysRBZiIfm61c9Q/ufiJwAfE71p+Z+GTQvefFdTbyqVbgF8BhwPHEb2T7w2jLkQTKRDSUidylTEQj8kzkY7O1BhiZsDwiWBcpd18TfNwIPEB8N+SGYHcpwceNwfBUn0OUn1tYta4JHrddnxHuvsHdm909BvyK+Nf9YOreTHzXd1mb9bkuJ/MAygTKRFSUicxRJg4yE/nYbM0BxgbvCKgApgAzoyzIzHqYWa+Wx8CHgXlBXS3vvrgCaLkh60zg08E7OE4FtgW7Zh8HPmxm/YLdnB8O1mVDKLUGz203s1OD49ufTthW6FqCH7iE+Ne9pe4pZlZpZqOBscRPyGz3+yf4K+1p4J+C1yd+DXJZzuUBlAllIlLKROYoEwebCc+Bd2gc6D/i73xYQvzdAjfkQD1jiL9bYS4wv6Um4sd3nwSWAk8A/YP1Btwe1P82UJOwrauJn6S3DLgqQ/XeQ3xXaiPxY87XhFkrUBN8M78D/C/BnQoyVPfdQV1vBcEZmjD+hqCGxSS80yXV90/w//hq8Pn8CaiM+nsrH/OgTCgTUf9TJpSJXMuEbtcjIiIikkH5eBhRREREJG+o2RIRERHJIDVbIiIiIhmkZktEREQkg9RsiYiIiGSQmi0RERGRDFKzlYfMbIaZfTvqOkRyhTIhkkyZyC1qtkREREQySM2WiIiISAap2YqImbmZHZ6w3LrL18wmmNlqM7vOzDaa2TozuyrFdnqZ2dNm9tPgvlQzzOx2M3vEzHaY2StmdljC+A+Z2Rwz2xZ8/FCw/h/N7O2EcbPNbE7C8t/N7OLg8Qoz+4qZvRVs549mVhX+V0mKiTIhkkyZKBxqtnLXEKAPMJz4vZ1ut/iNPFuZWct9ql5w93/3ffdemgJ8E+hH/P5N3wnG9wceAX5K/B5XtwGPBNt5mfiNNweaWTlwHDAsCGk34vex+nvC9J8AJgKjg7FXhvvpi+xHmRBJpkzkCTVbuasRuNndG919FrATODLh+WHAs8Cf3P3GNq99wN1fdfcm4PfA8cH6C4Gl7n63uze5+z3AIuCj7r6H+J3OzwROIn6z1BeA04FTg9dtTpjjp+6+1t23AA8nzCGSKcqESDJlIk+URV2ApLQ5CEGL3UDPhOULiQfrl+28dn2K1w0D3msz9j3ifxVBPJQTiN8p/VngfeAsoD5Y7miOYak/FZFQKBMiyZSJPKE9W9HZDXRPWB5ygK//FfAYMMvMenTxNWuBQ9qsGwWsCR63hOjM4PGzxEN0FvuHSCRsyoRIMmWiQKjZis6bwOVmVmpmE4l/ox6oa4HFwMPB8fLOzAKOMLPLzazMzC4FxgF/DZ5/kfgu6PHAq+4+n3joTgGeO4j6RA6EMiGSTJkoEGq2ovMl4KPAVuCTwIMHuoHgRMepxHfnPtTZOz2CY+kfAa4DNgNfBT7i7puC53cBrwPz3b0heNlLwHvuvvFA6xM5QMqESDJlokDYvjcmiIiIiEjYtGdLREREJINCabbM7I7gomrzUjxvwcXUlgUXODsxjHlFcpUyIZJMmZBiFtaerRnEL1yWygXA2ODfVOAXIc0rkqtmoEyIJJqBMiFFKpRmy92fA7Z0MGQycJfHvQz0NbOhYcwtkouUCZFkyoQUs2ydszUcWJWwvJp9F0gTKUbKhEgyZUIKVs5dQd7MphLfhUyPHj1OOuqooyKuSCTutdde2+Tu1dmeV5mQXBVFJpQHyWWpMpGtZmsNMDJheQT7rkabxN2nA9MBampqvLa2NvPViXSBmbW9hUU6lAnJe1FkQnmQXJYqE9k6jDgT+HTwbpNTgW3uvi5Lc4vkImVCJJkyIQUrlD1bZnYP8XslDTSz1cDXgXIAd/8l8cv/TwKWEb/X01VhzCuSq5QJkWTKhBSzUJotd7+sk+cd+GIYc4nkA2VCJJkyIcVMV5AXERERySA1WyIiIiIZpGZLREREJIPUbImIiIhkkJotERERkQxSsyUiIiKSQWq2RERERDJIzZaIiIhIBqnZEhEREcmgUJotM5toZovNbJmZTWvn+VFm9rSZvWFmb5nZpDDmFclVyoRIMmVCilnazZaZlQK3AxcA44DLzGxcm2E3Ave5+wnAFODn6c4rkquUCZFkyoQUuzD2bI0Hlrn7cndvAO4FJrcZ40Dv4HEfYG0I84rkKmVCJJkyIUUtjGZrOLAqYXl1sC7RN4BPBXd6nwX8W6qNmdlUM6s1s9q6uroQyhPJOmVCJFlomVAeJB9l6wT5y4AZ7j4CmATcbWbtzu3u0929xt1rqqurs1SeSNYpEyLJupQJ5UHyURjN1hpgZMLyiGBdomuA+wDc/SWgChgYwtwiuUiZEEmmTEhRC6PZmgOMNbPRZlZB/MTGmW3GrATOATCzo4mHSPt/pVApEyLJlAkpamk3W+7eBFwLPA4sJP5ukvlmdrOZXRQMuw74rJnNBe4BrnR3T3dukVykTIgkUyak2JWFsRF3n0X8hMbEdTclPF4AnB7GXCL5QJkQSaZMSDHTFeRFREREMkjNloiIiEgGqdkSERERySA1WyIiIiIZpGZLREREJIPUbImIiIhkkJotERERkQxSsyUiIiKSQaE0W2Y20cwWm9kyM5uWYswnzGyBmc03sz+EMa9IrlImRJIpE1LM0r6CvJmVArcD5wGrgTlmNjO4GnDLmLHA9cDp7v6+mQ1Kd16RXKVMiCRTJqTYhbFnazywzN2Xu3sDcC8wuc2YzwK3u/v7AO6+MYR5RXKVMiGSTJmQohZGszUcWJWwvDpYl+gI4Agze8HMXjaziSHMK5KrlAmRZMqEFLVQbkTdxXnGAhOAEcBzZnasu29tO9DMpgJTAUaNGpWl8kSyTpkQSdalTCgPko/C2LO1BhiZsDwiWJdoNTDT3Rvd/V1gCfFQ7cfdp7t7jbvXVFdXh1CeSNYpEyLJQsuE8iD5KIxmaw4w1sxGm1kFMAWY2WbMg8T/WsHMBhLfXbw8hLlFcpEyIZJMmZCilnaz5e5NwLXA48BC4D53n29mN5vZRcGwx4HNZrYAeBr4T3ffnO7cIrlImRBJpkxIsTN3j7qGlGpqary2tjbqMkQAMLPX3L0myhqUCcklUWdCeZBckyoTuoK8iIiISAap2RIRERHJIDVbIiIiIhmkZktEREQkg9RsiYiIiGSQmi0RERGRDFKzJSIiIpJBarZEREREMkjNloiIiEgGhdJsmdlEM1tsZsvMbFoH4z5uZm5mkV6FWyTTlAmRZMqEFLO0my0zKwVuBy4AxgGXmdm4dsb1Ar4EvJLunCK5TJkQSaZMSLELY8/WeGCZuy939wbgXmByO+O+BXwP2BvCnCK5TJkQSaZMSFELo9kaDqxKWF4drGtlZicCI939kc42ZmZTzazWzGrr6upCKE8k65QJkWShZUJ5kHyU8RPkzawEuA24rivj3X26u9e4e011dXVmixOJgDIhkuxAMqE8SD4Ko9laA4xMWB4RrGvRCzgGeMbMVgCnAjN18qMUMGVCJJkyIUUtjGZrDjDWzEabWQUwBZjZ8qS7b3P3ge5+qLsfCrwMXOTutSHMLZKLlAmRZMqEFLW0my13bwKuBR4HFgL3uft8M7vZzC5Kd/si+UaZEEmmTEixKwtjI+4+C5jVZt1NKcZOCGNOkVymTIgkUyakmOkK8iIiIiIZpGZLREREJIPUbImIiIhkkJotERERkQxSsyUiIiKSQWq2RERERDJIzZaIiIhIBqnZEhEREcmgUJotM5toZovNbJmZTWvn+f8wswVm9paZPWlmh4Qxb7Zt2lnPjQ++TUNTLOpSJMcVSyZEukqZkGKWdrNlZqXA7cAFwDjgMjMb12bYG0CNux8H3A98P915s2l3QxMrNu3i239dwO9eXsmj89ZFXZLksGLIhMiBUCak2IVxu57xwDJ3Xw5gZvcCk4EFLQPc/emE8S8Dnwph3qx4ZvFGrvztnKjLkPxS0JkQOQjKhBS1MA4jDgdWJSyvDtalcg3waKonzWyqmdWaWW1dXV0I5R2855duarfRmrdmWwTVSB4p2EyIHKTQMqE8SD7K6gnyZvYpoAb4Qaox7j7d3Wvcvaa6ujp7xbXjU795pd31v/r7u1muRApVvmVCJNM6y4TyIPkojGZrDTAyYXlEsC6JmZ0L3ABc5O71IcybURt37O3w+Q//6Fm+/tA8djc0ZakiySMFmQmRNCgTUtTCaLbmAGPNbLSZVQBTgJmJA8zsBOD/iAdoYwhzZtT2vY2M/86THY5ZsmEnd770Hl+6980sVSV5pOAyIZImZSJP7G5o4oo7XuW9zbuiLqWgpN1suXsTcC3wOLAQuM/d55vZzWZ2UTDsB0BP4E9m9qaZzUyxuZzwsyeXdnns7AUbMliJ5KNCzIRIOpSJ/PH0ojqeXVLHWT94hh8/sSTqcgpGGO9GxN1nAbParLsp4fG5YcyTSS8v30z3ilI272xg1ZY9B/z6mx6ax6EDenD1P4zOQHWSbwohE8Xg5eWbGTOwB4N6V0VdSsFTJvJDie17/OMnlnLSIf04Y6zOjUtXKM1Wvtu0s54p018+6Nf//pX3uOul9wDo36OCi0/o6E02IpILfvLEUn4U/OW+/LuTKEn8LSNSpP78evKpdCu37I6oksKi2/UANd9+Iq3X3/DAvNbHX/6jzuESyXW/ef7d1kYL4NfPL4+wGpFoLdmwg5pvz2bjjr08sTD51JgbHpjHC8s2RVRZ4Sj6ZuupReGfc6VvTJHc9f3HFvGtvy5IWrdkw86IqhGJ3mfurGXTzgam/fntdp//5K/bvwySdF1RN1svvrOJq2fUhr7dTTv1jmWRqK3ftpdX393C5p31rZdo2dvYzM+feWe/sS+9s5nGZt3zVIpTy6HCpxbpTaCZUnTnbD08dy3NMWfzrob9/roVkcLg7px6S/LlW44c3IvFG3a0O37N1j2MveFRnvvPf2TUgO7ZKFEkJ+yq79q1Iu94/l29ASwNBd9sbdnVwLNLNvLQm2s5pH937gxOZM+kN1Zu5eXlW7jn1ZXMvenD9OlenvE5RWSf//rzW/utS9VoJbpt9mJ+POWETJQkklPcHYDLftW1N4fd/NcFrc3WnBVbOGpIL3pV6XdbVxVss1W3o547X1zB/z69LOtzz3hxRevj9dv3qtkSyaK9jc3cV7v6oF774Jtr+dbFx+iXiBS0uau2Mvn2FzjziGreWt31e/1+6JYnuXfqafzzL19qXfeHz57Chw4bSGNzjPLSoj4zqUMF95Vpao7xP48v5uTvPBFJo9XWR3/2/H7rHnhjtc7rEsmQh+euTev1x37jb62P73xxBcvrdPK8FI69jc1Mvv0FAJ5bcmA38l67bS/rtyffyu7K387hoTfXMPaGR5n44+dCq7PQFEyztbexmYfnruXwGx7NiSarRUObk27Xb9vL//vjXE75bse3AxKRA/eHV1byn/fvfwjxYDz05hq+PnM+Z//w2VC2J5ILfvi3xWm9/po75yQtNzTFeGzeegAWrd/BF3//Ojv2NvLYvPWs39bxPYaLSSiHEc1sIvAToBT4tbvf2ub5SuAu4CRgM3Cpu68IY26A+19bzVf+NDeszYXu6hlzeGrRRt69ZVLrO56aYx5xVZJJUWei2DQ0xTjum4+ztzGcdxQeOu2RULbT1v89+w63PLqIF6adzfC+3TIyR65SJqLT0BTjS/e+wS0fO5aNO9I7qrJj7/4n1D8aNFsAj7y9jo079jJnxfut6/p1L+eGC8dx8fHDqG+KsWVXAyP7F9cbUazlJLmD3oBZKbAEOA9YTfyGo5e5+4KEMV8AjnP3z5vZFOASd7+0s23X1NR4bW3qSzOs3LybGx58m78vzd/rWj3zlQms2LyLP722mlVbdnPu0YMpMfifv8UvuHj5KaMA+McjB1G3o56F67Yzblhv1m/bS0VZCXU76vnGRR/A3TFLfQVsd+epRRs5/fCBPPLWOn7x7Dv8+NLjaWyOUd8U4/iRfakqL+2w1pbvlZZ5Opuz0JjZa+5e04VxkWWiGG3b08gHv/m3zgem6dEvncERg3tRepBXmt+4Y+9+N7i/8cKjuer00Qe9zahFnYmu5CHx51Ys5sTcKS2x1mUz2Lyrgd5V5VSUlez3cy5RfVMzFaUlxDx+W5u2Y5qCP6abYt56/lLLiLZ3KGj787OrP09bxiWOj8WckhJrrX3dtr3071HR+jM9U388pKP2xnP569y1jK7uSXmJ0b9nBYN6VbG7oYkBPSpZu20Ph1X3pG5HPdv2xJuzyrJS3J1NOxsoKzF6VpVRXlpCLOa0dDLNMaeibN9Bu8bmGGXB/3d7Wr5m7vH/t4qyktb/x5iDGZSaYcH/d2f/T6kyEUazdRrwDXc/P1i+PvgEbkkY83gw5iUzKwPWA9XeyeSpgvTV++ce9AmwklmDelVSXlrCmq17GNSrkobmGHsammn53uzfvYL3dzeyp7GZnpVlDO1TxdKNHZ8Tc/ignpSVGN0qSnl79Tb696igsryEitISylv/Ga+v3NqlGqvKSxjYs5KSoKiW2spKjCevm5DydQfwiyXrmfjSvW+wYO321h84rT9AWgYkbDXVmJaZPWFw67o2Ve23/XZev2+57Tba2X4nNXX0Oe3o4lvXM+WoIb1wj3/eMY/XGV+GWPA45s7q9w/snqu9q8oY1Luq9Qd9aYlRYkaJxX9xl5hRasayup1s2dXAkYN7tf5CT/VtlPT1bOf/Ib4+uYa/fOH0lDVGnYmOmq22DUZVeUloez7bGtGvG9v2NLa710cKz6JvTUy5cyJVJsI4jDgcWJWwvBo4JdUYd28ys23AAGC/XVJmNhWYCjBq1Kh2Jxw9sCcfGNab+Wu3p128hOu4EX0AY2d9E/VNMQb2rGDg4Ep6VMa/1QzYuqeRdzft4vDqnq2v61VZlvRL84yxA/n70k1UlJVwWHUPYg57GpoZ1KuS4f260buqnMryEpqanaaY09gcY1ifKta2c45AZVkJTnxXeveKUob0qWJEv+70T3iXqENr8xWCrGdiRL9urYeoreVv6eQPSX+N7VvX/pikr0TrGEvxmuTnk9a1rmr/te29PlVNpHjNHS+8u9/z2VJi8a99SyPU8tevQetySbC8+v01nW0uSe9u5RwxuCexGDS74+40x+INXczje2hiMehVVUZTc2y/64O19zWOr9///ynVmO4VHe/tPgChZaIreYi1c5rG0D7deHfTrg6LHNm/G6u2HFhTDHDyof1xdx58M703ZwD0rCxjZ8R/QBSzrnz9OzsK1J6cu/SDu08HpkP8r5b2xvzrhMP41wmHAfDqu1u46revsquhOXtFhmR4327MuOpkIP5Xa6+qctydFZt3M/255dQc2o/zPzCEQwd0p6E5xq76ZrbvaaRbRSk9Kstojjm9KssO+Aa6723eRXPMGZPQ7Eju6kom/vP8o7JaUy659OSRnJ/hd0GdOqY/9049La1t3Hbp8Ul7W6477wiuOP1QeusyEwekK3koKTFW3HrhgWwzlFMicukabe7xYG4oigAAIABJREFUxry0xHLqMOLHThjOzvom/vfyE9nT0ExZqfFO3U7GDe3deni2LOESEpt21jOwZ2WEFYcjjGZrDTAyYXlEsK69MauD3cN9iJ8Ambbxo/sz/+aJ7G1s5j/ue5NZb6/v/EVZdvc14zljbDUQ/4trzNdm8bmzxnD9BUe3O35Q7yrGj+6ftK6yrJTKslL696hIu55DBvRIexvSoUgzUWyOHNKLMQN7sLyTvRYHamifKtYFe0rTbbRafPzEEfz59dUsvHki3cLba5QPcjoThXjuqZlRGnxaC24+n3E3Pc554wbTq7KMv7xxYHtZD9Ts/3cm5/1o3x9AP5lyPBOOHESfbsl/WLScW3XciL4pt1UIjRaE02zNAcaa2WjiYZkCXN5mzEzgCuAl4J/4/+3deZhcZZn///en0+mEkJ3sgRCQIARkbTYRUAgYQA2gIrgQHDGuM/JzGaNRhnEZM/rVYRi3CYsGVBBRIA6bJKKgEqARkIQACXsgSxPWECDb/fujTifVnaruTtc5tXR9XtfVV5869dQ5d3fX3c9d5zznPPDHrsambK/+ffvw4w8dzIvr1vP4c6+ybPVannp+HXc9/jwPr3qFBolj9xrF1feUf6xXW6EF2/+Jy2pSVeREPbnx3KOY9sO/8tDKru8S3x33nXc8i599OfUJeL9/+v58//T9U91mjXBOVNCApsYt/U5EbCm2puw9ivlLtm8+xPPeNZlv/N+DNCg3gBzgkW+dyN8efY6zf3Y3F53VzKTRg9zPdVBysZWcW/8scDO5S3ovjYjFkr4BtETEPOAS4HJJy4DnySVaJoYOaOLACU0cOGFYwec/d9wkjvrurVnt3qzqcqIe9GvswwcPm8B51y0ueVvfnLYPQwc0ccTuOwFw0Vldjv+2LjgnqofU/gP/9p5iPHjXXN/61ZP25lvXL+HUA8fT1NjA2988ygVWJ0q+GjFLWV3m/tr6Tex93k2pb7eQi85q5vjJo8uyL8tWd6+8ypJv/VBcRPDq+k2sfX3jNpNQbw93GN1X6ZxwPpTursef5/T/vaPo821Hsto8MftkWl95g5GD+vHQypeZuNOOPRow3ltleTVizdmhqQ+/POcwHnjmJSaPHcyNi1ZwxV1Pd/3C7fD4d05i0TMv85adh6S6XTMrTBID+zUysF8jD31zKn0axJzbHuN7N3f/jtlnHrpL143MepFDdxvOP84/gTsfe5516zfyuSvv2/LcHV85lrFDduCOx9Yg4Lvv2w+AkYNy46j2GjO4EiHXpLostgCO3GMER+4xAoCj9xyZerElyYWWWYW0fdL+zDv24OXXNvC/tz3W5WsG9W/kO6ftl3VoZlVncP++W87ATDtg/DbP+1R66XrN3Iiluu1L76h0CGaWgX+duhdT9xnDqQdu24nkO+9dk8sUkZnVm7o9stXRhJ0G8MTsk3n2xdd46+w/VjocM0tJnwbx048cDMA1nVzyftzeHltpZtnwka0Oxg3dgZPeMqZHr/339+zDhWceyEPfnJpyVGaWhnmfLTz1zL7jB6dyDzszs0J8ZKuACz5wIOe/ez2H/kfxK5pu/9d3bLmFRGfzJJlZ9Sh280TR+25qaWbVw0e2CmhqbGDU4P5c95niE7DuMnzrPGQutMxqx11fPY5dhu/Qbt12znhlZrZdXGx1Yv9dhjJmcP+izx+wy1BmnVR4yh0zq06jBvfnpx8+mHFD+jP/80cD8J4CV2CZmaWlpNOIkoYDvwYmAk8Ap0fECx3aHAD8BBgMbAK+HRG/LmW/5TTvn4/k8dZXuW1pKz+69VEAvnnKvgBc28mRL6tP9ZATvcE+44bwt68cB+SmGunbx4e2suKcMCv9yNZMYEFETAIWJI87WgecFRH7AFOBCyQVn3Wyyowa1J/Ddt+JL71zLy7/2KH8beaxfPiwCZUOy6pXr8+J3qapsaFXTkRcRZwTVvdKLbamAXOT5bnAKR0bRMQjEbE0WX4WWA2M7NiuFhw1aSTjhu7gf8zWmbrKCbNucE5Y3Su12BodESuS5ZVApzeqkXQo0AQ82kmbGZJaJLW0traWGJ5Z2TknzNpLNSecD1aLuhyzJWk+UOjGU7PyH0RESCo6q7WkscDlwPSI2FysXUTMAeZAbpLRruIzKzfnhFl75cwJ54PVoi6LrYiYUuw5SaskjY2IFUmSrC7SbjBwPTArIhb2OFqzKuCcMGvPOWHWuVJPI84DpifL04HrOjaQ1ARcA1wWEVeXuD+zauecMGvPOWF1r9RiazZwvKSlwJTkMZKaJV2ctDkdOBo4W9J9ydcBJe7XrFo5J8zac05Y3VNE9Z7ybm5ujpaWlkqHYQaApHsiormSMTgnrJpUOiecD1ZtiuWE7yBvZmZmliEXW2ZmZmYZcrFlZmZmliEXW2ZmZmYZcrFlZmZmliEXW2ZmZmYZcrFlZmZmliEXW2ZmZmYZcrFlZmZmlqGSiy1JwyXdImlp8n1YJ20HS1ou6Yel7tesWjknzLZyPpilc2RrJrAgIiYBC5LHxXwTuC2FfZpVM+eE2VbOB6t7aRRb04C5yfJc4JRCjSQdDIwG/pDCPs2qmXPCbCvng9W9NIqt0RGxIlleSS5Z2pHUAHwf+GJXG5M0Q1KLpJbW1tYUwjMrO+eE2VbOB6t7jd1pJGk+MKbAU7PyH0RESIoC7T4N3BARyyV1uq+ImAPMgdyM7t2Jz6zcnBNmWzkfzDrXrWIrIqYUe07SKkljI2KFpLHA6gLNjgCOkvRpYCDQJGltRHR27t6sajknzLZyPph1rlvFVhfmAdOB2cn36zo2iIgPtS1LOhtodhJZL+acMNvK+WB1L40xW7OB4yUtBaYkj5HULOniFLZvVmucE2ZbOR+s7imiek95Nzc3R0tLS6XDMANA0j0R0VzJGJwTVk0qnRPOB6s2xXLCd5A3MzMzy5CLLTMzM7MMudgyMzMzy5CLLTMzM7MMudgyMzMzy5CLLTMzM7MMudgyMzMzy5CLLTMzM7MMlVRsSRou6RZJS5Pvw4q0myDpD5KWSHpQ0sRS9mtWrZwTZu05J8xKP7I1E1gQEZOABcnjQi4DvhcRewOHUngiUrPewDlh1p5zwupeqcXWNGBusjwXOKVjA0mTgcaIuAUgItZGxLoS92tWrZwTZu05J6zulVpsjY6IFcnySmB0gTZ7Ai9K+p2keyV9T1KfYhuUNENSi6SW1tbWEsMzKzvnhFl7qeaE88FqUWNXDSTNB8YUeGpW/oOICEmFZrVuBI4CDgSeAn4NnA1cUmh/ETEHmAO5SUa7is+s3JwTZu2VMyecD1aLuiy2ImJKseckrZI0NiJWSBpL4XPsy4H7IuKx5DXXAodTpGMxq3bOCbP2nBNmnSv1NOI8YHqyPB24rkCbu4GhkkYmj48FHixxv2bVyjlh1p5zwupeqcXWbOB4SUuBKcljJDVLuhggIjYBXwQWSHoAEHBRifs1q1bOCbP2nBNW97o8jdiZiFgDHFdgfQtwTt7jW4D9StmXWS1wTpi155ww8x3kzczMzDLlYsvMzMwsQy62zMzMzDLkYsvMzMwsQy62zMzMzDLkYsvMzMwsQy62zMzMzDLkYsvMzMwsQyUXW5KGS7pF0tLk+7Ai7b4rabGkJZIulKRS921WjZwTZls5H8zSObI1E1gQEZOABcnjdiS9FTiS3N2B9wUOAY5JYd9m1cg5YbaV88HqXhrF1jRgbrI8FzilQJsA+gNNQD+gL7AqhX2bVSPnhNlWzgere2kUW6MjYkWyvBIY3bFBRNwB3AqsSL5ujoglKezbrBo5J8y2cj5Y3evWRNSS5gNjCjw1K/9BRISkKPD6PYC9gZ2TVbdIOioibi/QdgYwA2DChAndCc+s7JwTZls5H8w6161iKyKmFHtO0ipJYyNihaSxwOoCzU4FFkbE2uQ1NwJHANskUkTMAeYANDc3b5OUZtXAOWG2lfPBrHNpnEacB0xPlqcD1xVo8xRwjKRGSX3JDXz0IWLrrZwTZls5H6zupVFszQaOl7QUmJI8RlKzpIuTNlcDjwIPAPcD90fE71PYt1k1ck6YbeV8sLrXrdOInYmINcBxBda3AOcky5uAT5S6L7Na4Jww28r5YOY7yJuZmZllysWWmZmZWYZcbJmZmZllyMWWmZmZWYZcbJmZmZllyMWWmZmZWYZcbJmZmZllyMWWmZmZWYZcbJmZmZllqKRiS9L7JS2WtFlScyftpkp6WNIySTNL2adZNXNOmLXnnDAr/cjWIuA04LZiDST1AX4EnAhMBs6UNLnE/ZpVK+eEWXvOCat7Jc2NGBFLACR11uxQYFlEPJa0vRKYBjxYyr7NqpFzwqw954RZecZsjQeeznu8PFlXkKQZkloktbS2tmYenFkFOCfM2ut2TjgfrBZ1eWRL0nxgTIGnZkXEdWkHFBFzgDkAzc3Nkfb2zUrlnDBrr5w54XywWtRlsRURU0rcxzPALnmPd07WmdUk54RZe84Js86V4zTi3cAkSbtJagLOAOaVYb9m1co5Ydaec8J6tVJv/XCqpOXAEcD1km5O1o+TdANARGwEPgvcDCwBroqIxaWFbVadnBNm7TknzEq/GvEa4JoC658FTsp7fANwQyn7MqsFzgmz9pwTZr6DvJmZmVmmXGyZmZmZZcjFlpmZmVmGXGyZmZmZZcjFlpmZmVmGXGyZmZmZZcjFlpmZmVmGXGyZmZmZZajUO8i/X9JiSZslNRdps4ukWyU9mLT9XCn7NKtmzgmz9pwTZqUf2VoEnAbc1kmbjcAXImIycDjwGUmTS9yvWbVyTpi155ywulfqdD1LACR11mYFsCJZfkXSEmA88GAp+zarRs4Js/acE2ZlHrMlaSJwIHBnOfdrVq2cE2btOSesN+ryyJak+cCYAk/NiojrursjSQOB3wLnRsTLnbSbAcxIHq6V9HCRpiOA57q7/ypTq7HXatyQTuy7gnMiI7Uae63GDTWaE9uRD+C/TyXUatyQYk50pIgocbsg6U/AFyOipcjzfYH/A26OiB+UvMPcNlsiouBgy2pXq7HXatxQ/tidE9unVmOv1bjBOVHtajX2Wo0bso0989OIyp2ovwRYklYCmdUy54RZe84J6+1KvfXDqZKWA0cA10u6OVk/TtINSbMjgY8Ax0q6L/k6qaSozaqUc8KsPeeEWelXI14DXFNg/bPAScnyX4Dil6H03JwMtlkutRp7rcYNZYrdOdFjtRp7rcYNzolqV6ux12rckGHsqYzZMjMzM7PCPF2PmZmZWYZqstiSNFXSw5KWSZpZ6XgAJD0h6YFkrEFLsm64pFskLU2+D0vWS9KFSfz/kHRQ3namJ+2XSpqeUayXSlotaVHeutRilXRw8rtYlrw2ldMDReI+X9IzhcZ5SPpKEsPDkt6Zt77g+0fSbpLuTNb/WlJTGnFnrRrzAZwTzonKcU6kEqtzIs2ciIia+gL6AI8CuwNNwP3A5CqI6wlgRId13wVmJsszgf9Mlk8CbiQ3RuFw4M5k/XDgseT7sGR5WAaxHg0cBCzKIlbgrqStkteemGHc55O7nLxj28nJe6MfsFvynunT2fsHuAo4I1n+KfCpSr+vuvE7qcp8SGJzTjgnnBPtY3NO1GlO1OKRrUOBZRHxWESsB64EplU4pmKmAXOT5bnAKXnrL4uchcBQSWOBdwK3RMTzEfECcAswNe2gIuI24PksYk2eGxwRCyP3brwsb1tZxF3MNODKiHgjIh4HlpF77xR8/ySfqo4Frk5en/87qGa1lA/gnHBOZM85kQLnRLo5UYvF1njg6bzHy5N1lRbAHyTdo9wdjgFGR27OL4CVwOhkudjPUMmfLa1YxyfLHddn6bPJoetL2w5rdxFfofU7AS9GxMYO66tdteYDOCecE5XhnMiOc6KHOVGLxVa1eltEHAScSG7G+qPzn0yq95q49LOWYgV+ArwJOIDcRLbfr2w4lsc5URnOierlnKiMiudELRZbzwC75D3eOVlXURHxTPJ9Nbl7yhwKrEoOl5J8X500L/YzVPJnSyvWZ5LljuszERGrImJTRGwGLiL3e+9J3GvIHfpu7LC+2lVlPoBzAudEpTgnsuOc6GFO1GKxdTcwKbkioAk4A5hXyYAk7ShpUNsycAKwKImr7eqL6UDbhKzzgLOSKzgOB15KDs3eDJwgaVhymPOEZF05pBJr8tzLkg5Pzm+flbet1LUlfuJUcr/3trjPkNRP0m7AJHIDMgu+f5JPabcC70ten/87qGZVlw/gnHBOVJRzIjvOiZ7mRFTBFRrb+0XuyodHyF0tMKsK4tmd3NUK9wOL22Iid353AbAUmA8MT9YL+FES/wNAc962/oncIL1lwEczivcKcodSN5A75/yxNGMFmpM386PAD0lunptR3Jcncf0jSZyxee1nJTE8TN6VLsXeP8nf8a7k5/kN0K/S761azAfnhHOi0l/OCedEteWE7yBvZmZmlqFaPI1oZmZmVjNcbJmZmZllyMWWmZmZWYZcbJmZmZllyMWWmZmZWYZcbJmZmZllyMWWmZmZWYZcbNUgST+X9K1Kx2FmZtXJ/UR1cbFlZmZmliEXW2ZmZmYZcrFVIZJC0h55j7cc8pX0dknLJX1B0mpJKyR9tMh2Bkm6VdKFySSgP5f0I0nXS3pF0p2S3pTX/q2S7pb0UvL9rcn6d0h6IK/dLZLuznt8u6RTkuUnJH1R0j+S7fxaUv/0f0tm3SfpS5J+22HdhZL+W9IQSZckufSMpG9J6pO02UPSn5P38nOSfl2Zn8CsPfcTvYeLreo1BhgCjCc3keaPklnTt5DUNinoXyPiX2LrRJdnAP8ODCM3Wea3k/bDgeuBC8lNKPoD4PpkOwvJzXI+QlJfYD9gXJKkO5CbNPT2vN2fDkwFdkvanp3uj2+23X4BTJU0FEBSI7lcuAz4ObAR2AM4EDgBOCd53TeBP5DLl52B/ylr1GY9536iRrjYql4bgG9ExIaIuAFYC7w57/lxwJ+B30TE1zq89pqIuCsiNgK/BA5I1p8MLI2IyyNiY0RcATwEvDsiXgPuBo4GDiY3M/1fgSOBw5PXrcnbx4UR8WxEPA/8Pm8fZhURESuA24D3J6umAs8By4GTgHMj4tWIWA38F7nOBnK5tiswLiJej4i/lDdysx5zP1EjGisdgBW1JkmCNuuAgXmPTyaXWD8t8NqVRV43DniyQ9snyX0qglxSvp1c5/Rn4AXgGOCN5HFn+xhX/EcxK5u5wKeAi4APA5eTK6T6AisktbVrAJ5Olv+V3NGtuyS9AHw/Ii4tZ9BmPeR+okb4yFblrAMG5D0es52vvwi4CbhB0o7dfM2z5DqefBOAZ5LltiQ6Oln+M7kkOoZtk8isGl0L7CdpX+Bd5D6xP02uIxgREUOTr8ERsQ9ARKyMiI9HxDjgE8CP88fJmFWQ+4lewsVW5dwHfFBSH0lTyb1Rt9dngYeB3yfny7tyA7CnpA9KapT0AWAy8H/J838jdwj6UOCuiFhMLukOI3d6xqyqRcTrwNXAr8i9h59KTi/+Afi+pMGSGiS9SdIxAJLeL2nnZBMvAAFsrkT8Zh24n+glXGxVzueAdwMvAh8i94l8uyQDHWeQO5x7XVdXeiTn0t8FfAFYQ+70ybsi4rnk+VeBvwOLI2J98rI7gCeTcS5mtWAu8BZypxDbnAU0AQ+SK6iuBsYmzx0C3ClpLTAP+FxEPFa+cM2Kcj/RS2jrhQlmZrVP0gRyA3rHRMTLlY7HzMxHtsys15DUAHweuNKFlplVi1SKLUmXJjdVW1TkeSU3U1uW3ODsoDT2a2bWJhkA/DJwPPBvFQ7HOnA/YfUsrSNbPyd3T5tiTgQmJV8zgJ+ktF8zMyA3liQiBkbEPhHxdNevsDL7Oe4nrE6lUmxFxG3A8500mQZcFjkLgaGSxnbS3szMehH3E1bPynVT0/FsvYEg5K6KGA+s6NhQ0gxyn2rYcccdD95rr73KEqBZV+65557nImJkJWMYMWJETJw4sZIhmG2Rck50q59wH2HVrFhOVN0d5CNiDjAHoLm5OVpaWiockVmOpI53VS67iRMn4pywalGJnHAfYdWsWE6U62rEZ4Bd8h7vzNa70ZqZmbmfsF6rXMXWPOCs5GqTw4GXkrs6m5mZgfsJ68VSOY0o6QpycyWNkLSc3GXXfQEi4qfkbv9/ErCM3FxPH01jv2bVStKl5O7CvDoi9i3wvID/JpcX64CzI+Lv5Y3SrHzcT1g9S6XYiogzu3g+gM+ksS+zGvFz4IfAZUWez7/M/TByl7kfVpbIzCrA/YTVM99B3iwDvsy9PCKC9Rs3s/aNjZUOxcysqKq7GtGsTvTodigTJkwoS3C14hd3PsXXr83dkHzkoH7cPWtKhSMyM9uWj2yZVbmImBMRzRHRPHJkRW/zVXV+9/flW5ZbX3mjgpGYmRXnYsusMnyZu5lZnXCxZVYZvszdzKxOeMyWWQZ8mXt5qNIBmJl1g4stswz4MvfyiEoHYGbWDT6NaGZmZpahVIotSVMlPSxpmaSZBZ6fIOlWSfdK+oekk9LYr5nVN59GrB3uJ6yelVxsSeoD/IjcHbEnA2dKmtyh2deAqyLiQOAM4Mel7tfMzGqD+wmrd2kc2ToUWBYRj0XEeuBKcnfHzhfA4GR5CPBsCvs1Myvq4ZWvMHHm9Vx3n++oUQXcT1hdS6PYKnYn7HznAx9Orsq6AfjnYhuTNENSi6SW1tbWFMIzs3rz/KvreecFtwEw7z732VUgtX7CfYTVonINkD8T+HlE7EzucvfLJRXct++WbWalem6t7yZfg7rVT7iPsFqURrHVnTthfwy4CiAi7gD6AyNS2LeZmVU/9xNW19Iotu4GJknaTVITuYGN8zq0eQo4DkDS3uSSyMd/rdfylVdm7bifsLpWcrEVERuBzwI3A0vIXU2yWNI3JL0nafYF4OOS7geuAM5Obupo1uv4yqvKy//vIt8fouLcT1i9S+UO8hFxA7kBjfnrzstbfhA4Mo19mdWALVdeAUhqu/Lqwbw2vvIqA/c8+QIH7zqs3Tp319XB/YTVM99B3ix9qV6ha9333p/8rdIhmJltw8WWWWV0+wpdX+penIqcI/SpQzOrJi62zNKX6pVXvtR9+3nMlplVExdbZunzlVcZuu2RVh5e+UqlwzCrGS+9toFlq9dWOoy65mLLLGW+8ipbZ11615a7w5tZ1973k78x5Qd/rnQYdS2VqxHNrD1feWVm1WKpj2pVnI9smZmZmWXIxZaZ1aQXXl3PPU++UOkwzGrGxJnX89r6TZUOoy652DKzmjT3jieKPvdYq0+bmBXyyusbKh1CXUql2OpqHrikzemSHpS0WNKv0tivmVlHmzYHn/rl37c8nr9ktT/NVwH3E1bPSi62ujMPnKRJwFeAIyNiH+DcUvdrZvVNFL6B1uYCF3VeMP+RrMOxTrifqJw5tz1a6RCMdI5sbZkHLiLWA23zwOX7OPCjiHgBICJWp7BfM7NuWecjW5XmfqICHlr5Mv9xw0Pt1i1e8XKnr7nnyRfYtNl3oUlbGsVWd+aB2xPYU9JfJS2UNLXYxjw1iZmVwncrq0qp9RPuI7pv6gW3b7Puoz+7m/uffnGb9es3buamRSt570/+xk/+tKwc4dWVcg2QbwQmAW8nNyfcRZKGFmroqUnMrDv+9IgPfPQy3eon3EeUbsVLr2+z7l+uuJdP/uIeAB5e5QtM0pZGsdWdeeCWA/MiYkNEPA48Qi6pzHolDwbO3r1PbfvpHOAjl9xZ5kisG9xPVJFP/uIePnLJnTz9/Lot625avLKCEfV+aRRb3ZkH7lpyn1aQNILc4eLHUti3WdXxYODKuvPx5ysdgm3L/USVuX3pc5w/b3Glw6gbJRdb3ZwH7mZgjaQHgVuBL0XEmlL3bValPBjYLI/7Cat3qcyN2I154AL4fPJl1tsVGgx8WIc2ewJI+ivQBzg/Im4qT3j15/KFT3L5wie56dyj2GvM4EqHU5fcT1g98x3kzSqj2xeN+Oqr9NzlU4xmW/zpkVY2bNpc6TDqgosts/SlOhjYV1+ZWRY2bQ4mzbqx0mHUBRdbZunzYGAzM9vCxZZZyjwYuHoVnuDHzPKF7wyculQGyJtZex4MbGZmbXxky8zMzCxDLrbMzMxsC8kn3NPmYsvMzMwsQ6kUW92ZBy5p915JIak5jf2amW0Xf2KvGPcT5bP42ZdYt35jj1+/0ffeSl3JA+Tz5oE7nty9g+6WNC8iHuzQbhDwOcCzxJpZZfgqq4pwP1E+r76xkZMv/AvH7TWq26+596kX2j2+cZEnpU5bGke2ujMPHMA3gf8EXk9hn2Zm2+3r1y3m6efXVTqMeuR+okzWb8wdlVrwUPenWz31x3/LKhxLpFFsFZoHbnx+A0kHAbtExPVdbcxTk5hZlu57+sVKh1CPUusn3EdYLcp8gLykBuAHwBe6095Tk5iZ1Zft6SfcR1gtSqPY6moeuEHAvsCfJD0BHA7M8+BH6808GNisHfcTVtfSKLY6nQcuIl6KiBERMTEiJgILgfdEREsK+zarOnmDgU8EJgNnSppcoJ0HA1u9cD9hda3kYqub88CZ1RMPBjbL437C6l0qcyN2NQ9ch/VvT2OfZlWs0GDgw/Ib5A8GlvSlzjYmaQYwA2DChAkph1p//vmKe3nHXqMY2M9Tw5aT+wmrZ76DvFmZ+aKRnvvjQ6tS2c7CR9eksh0zs+7wRzuz9G3PYGCAMeQGA3uMShHrN25m+qV3ccdjLpLMrPb4yJZZ+jwYOGVf+d0DLrTMrGa52DJLmQcDp++2pb55pZnVLp9GNMuABwObWSV4rvXq5CNbZlbVlq1eS+srb1Q6DLOa4LnWq5OLLTOrOv923SLO/tldAFzV8nQXrbffDYtW8M9X3MvrGzalvm0zs458GtHMqs7cO57cspzFWZHf/T13cei79xvLCfuMyWAPZpXh04j+GDg8AAAgAElEQVTVKZUjW13NAyfp85IelPQPSQsk7ZrGfs2s95N7j17B/UR5pHUacc1an7pPU8nFVjfngbsXaI6I/YCrge+Wul8zqw+utWqf+4na883/e7DSIfQqaRzZ6nIeuIi4NSLWJQ8XkrvJo5lZl1xr9QruJ8okrQ8n1973bDobMiCdYqvQPHDjO2n/MeDGFPZrZnWgwYe2egP3E2XiqxGrU1mvRpT0YaAZ+F4nbWZIapHU0trqGxmWy8LH1rB+4+YtjzdtDla89FoFIzLLefbF7N6HMy6/x+/zKtNVP+E+wmpRGsVWV/PAASBpCjCL3LQkRUfeedLd8luy4mXOmLOQPb92Izc+sAKA7978EEd854+sfvn1CkdXmzwYOD2/u3ebfyepuu+pFzPdvgEp9hPuIzrnA8HVKY1iq9N54AAkHQj8L7kEWp3CPi1FL7y6fsvyp375d973k79x8e2PA/D8uvXFXmZFeDBwbfnatYt4Y6Pvt5Ux9xNl4tOI1ankYqub88B9DxgI/EbSfZLmFdmclUlEsGz1Wr5zwxJuW/pcu+dannyBTZtzGSsPT+4JDwZOydPPr+u6UYnWvLqemxatzHw/9cz9RG362V8f5/lX/YE7Danc1LSreeAiYkoa+7H0fPZX93J9csqwMz4k3SOFBgMf1kn7TgcDS5oBzACYMGFCGvHVjE/98p5Kh2ApcT9RHmn+z/733z/InNse49/evQ8Dmvpw9J4+bdtTnq6nTnWn0AK2HOGybHTnopF6GaPywPKXWLLiZSLvPMiGjeV5/y187Pmy7Mes1qx46XU++Yt7OOvSuyodSk3zdD3WqRP/+3YAznnbbkx/60R2GT6gwhHVhO0dDHxMZxeN1It3//AvAOwyfIct61a/Up4LNK646ymm7D2K4/YeXZb9mVl9cbFl3XLxXx7n4r88zmX/dCgjBvbjyTWv0tingeMnu3MqYMtgYHJF1hnAB/Mb5A0GnurBwO09/fzWWzG8sG5D2fab5S0mzKy+udiqQw+tfLnHr+14KHlgv0b++uVjGTKgb6lh9RoRsVFS22DgPsClbYOBgZaImEf7wcAAT0XEe4pu1MzMapaLrTo09YLbU9vW2jc2ct/yFzlwwlBue6SVd+03LrVt1zIPBq5BvhrEegHf+qE6udiykk3PO9o1eexgdh85sILRWC2qigsx3EuZdepzV97LBR84APmDyXbz1YiWqtuXPscjq15h4szr+b9/PEvLE77Ky7r2g1sernQIBPDru5/yDU7Nirjuvmd95W4P+ciWperf5i3esvzZX90LwBOzT65UOFYjbnlwVaVD4IYHVrDwsed5cs06/nXqXpUOx6xHsj4+e+ZFC/nB6fuz87ABvLZhE0dPGuEjXd3gYqvOXHz7Y2Xf58SZ1zPzxL345DFvKvu+rfpt3LSZR1atrXQYvPzaRgDWrPUds8068/mr7m/3+I9fOIYJwwfw4msbGDGwX8HXRAQrX36dsUN2KPh8b5fKacRuTLrbT9Kvk+fvlDSxlP29tG4D/+/mh9m4aXMpm6lL37p+SUX2O/vGh5g483oeba18p2rV442Nm9hjVtGb55dV29RAkfmxgfpU7n6iXn392kVl3+f8JavYY9aNNH9rPs8kt1B5bu0bPNq6dstNio+c/UeO+M4fue/pF1m2OjfUZOLM67l84ZPc8egaNvTy/rzkI1t5k+4eT25akrslzYuIB/OafQx4ISL2kHQG8J/AB3q6z/N/v5hr7n2G5S+s44IzDiwl/F7v9Q2bOOnC23ms9dVKhwLAcd//M4P6NXLs3qP4+rsmM6CpDwOafIC1Xv367qe7blQmr7yRO7J1VctyTjlwPKMH92eXYQNoavTQ1lJVop/oTV5bv4m+fcTGzUH/vn06bdvd2UHS9B83PLRl+cjZf9zm+dMOHM+zL+VuUHzKj/7a7rlixeGXp+7Fb+55mh9/6CBGD+rPjv0a6dtHNXvKUlHiFTiSjgDOj4h3Jo+/AhAR38lrc3PS5g5JjcBKYGR0sfPm5uZoaWnZZv3Emddvs+7st06kQaJPAzQ0KLcs0dCQfBcseGg16zdu5sAJQ3v885by2yrtV92zF19xV/V0Zj31ppE7csjE4dus7+z32dnRiWKv69MgZr93v6Kvk3RPRDQX32v2iuVErSqUy9XkvQftzPdP37/SYVSt7uZEVv1EZ/nw/T88zH1Pv8jQAU3s2NSHxj65fiG33/Zt8/9fbPtch8dR/Nn857q7j5Uvv87g/n3ZsV8fhFi7fiP9Ghv43d+3mXSinSl7j2LMkP5E5KL41Z1Pddq+tzv5LWNZ/OxLTNhpR159YyN7jh6UqweU+7tLbcskj7cuN4jksVi3YSP/++f2w22m7D2aR1a9Qr/GBk58y1g+f/yeReMolhNpHFLozqS7W9okN3x8CdgJeK5AoJ1Oulss73739+Vsjtwl5Jsj95Vb3rbtqpdfL/GWOj1/cSn7rc16vnSPtr7K2uSoQ0fq5LfS2e+60FMNDfX6G66MWrhj+2//vpwvn/hmzpyzkE+9fQ9OOWAcr67fxP/36/sYskNf/usDB2Sy35YnnmfWNYvo2yiu/fSRNPap+aNrqfUT3Z2Y/Z4nX+Afy1+iX2MDGzcHHdO74xEStXuu49ZU9LmOTfOf7/j/qf1zORs3B6tfeYMRA/vx4rr1jBzUb0tR2Jn5S1az045NReKtP21H9J558TU2bAqeXPMqmyNXM2wO2BxBJN83J+s6PlfM/CVbL+BpWLSy02KrmKo7fxMRc4A5kPvU0vH5jgmy7NsndvqPKP+Xuakbh2B7m6WrXuH4/7qt0mEUdNJbxvD2N49iUL9GJHj1jU0cuccIXnptA3uOHlizh4shNz4F+G9yd5C/OCJmd3i+H3AZcDCwBvhARDxR7jgr6aQL07u5bpYO/fYCAL74m/v54m/aDwyedfLePLlmHQfvOgzIne7Z+7ybuOLjh/Psi6+xy/AB7LfzECTo11j8f89L6zaw/zf+wKETh3NXh9ul7DHrRi46q9lTYyW66iPa/Orjh5ctpqysWfsGjX0aeGTVK4wfugP9+/ZheFJgFbJs9Vqm/ODPZYywPA7edRhHTxrJnqMHcuzeo2iQaGwQL67bwJAd+iK1rw0iYrv7j8grxjZuDt7YsJmB/Ru3HPVq2+bmHt4TMI1iqzuT7ra1WZ4cHh5CroPpkX8+dg/+54/LWPCFY7r8xCeJPoI+iDqrswCYNHrQllsvbNi0mXPmtvDnR1orEsvFZzVzyG7DGbJD51P7jBnSv0wRZaMS41PaPlT0qZGjcxs3bebFMs57mJXmb80vuP7Mixb2aHsdC602H78sd6psrzGD+OIJb+Yde42qmb91ouz9RG+wU3JlX6FhFIXsMWogP/zggVtuu1MuE4YP4KnkAhOADx02gXOO2p0/Pbya5l2Hs+/4wfz3gqVcMH8pkBvD9bt7t/75j9trFJecfciWx90tloYVKTx78kFdbacaEY192ObATNs2e3oGJI1iq8tJd4F5wHTgDuB9wB+7Gq/Vmc8fvycfPXK3Tit821bfPg1bDjuX04ThA5hx9O5MqZ9P5ocCyyLiMQBJVwLTgPxiaxpwfrJ8NfBDSepJXmzeHBzy7fmseXXrLQv2GTd4y1gOaH/6Pbc+8pa3ttnSqsj6jq/N/95V2/z1z619Y3t/TAMeWvkK51xWfMzePuMGd2s73X2XDd6hkStnHNG9xp0rez9Rr7pzCjJtt/3rOwqu323EbluWz52yJ5885k1bipj/9/79WfNq7rRpR7V8VqOYkoutbk66ewlwuaRlwPPkEq3HJLnQ6qHzp+3T7hNFFp6YfTK/uvMpDt1tOHuMqsupe8o6jrGhQZywzxiuuGvrANmxW44Oast4DkHect565Y0t0daxJJLylguvp9D2OtlP2wvyY7V0NDU25P3du6PrDm1gv3ROB1Sin6hX5Si2bjr3KK6991nWrd+4XeOX8o8WNTSoYKHVW6UyZqsbk+6+Drw/jX1ZaQb378vuI3dM9VYQF3zgABoaxL9ccS+XTM9dhPHBw4oPXLXt050xKt857S1857S3lDWuUvz7e/Zhz69Vx/21asnvPv1W3jJ+CH1rcMC8+4nyKMfZ5b3GDGbmid07imo5VTdA3sogxQPzD5x/AoP658ZgvWf/celtuLZ5fEoXmhob+Ma0fTjvusVdN65i133myGQAfK6Ha33lDT5/1X2c967JPLjiZQbv0Jd3vHlUl9t5+vl1HPXdW5l10t58+4Ztbzx8x1eOrds7b9v2yfrI1slvGZvp9nsrF1t1aIemdE4NvHv/cVsKLWvH41O64awjJtZEsXXnV49jyA59efn1DTRIjBjYj78sfY5nX3qN/Xdpf8++kYP6cfnHcmeMJ40e1O197DJ8wJYLWT5+9O4sW/0K37nhIVqefIE7v3pc3V1FbT3XkPFBzx996KBsd9BLudiqQ//7kYN523/e2qPX7r/zEPYZP4TDd9/JR7KK8PiU7rvzq8dx2H8sqHQYnRo9ODcOKr/gedukEZnuc49Rg9pdnWXWXb1xcHlv4GKrDu08bAAH7zqMe558ocu2P/voIXz0Z3dveXzVJ4/o9H5BluPxKd0zenB/hg3oywtVchuIKz5+OB+8eCERcPzk0fz0wwdXOiSz7ZLlacSWr03JbNu9nYutOrXPuMGdFltfeueb+cw79gDg718/nojYcs8XszTd8ZXj2OvrN1U6DACOeNNO3PqFt+duDlk/tyqxXiSrAfIXfOAARrgP6DEXW3XqaydP5rSDdmZzBKf9+G/bPN9WaAG+zYZlqlrGI32gOXdNw8QROzJxxI4VjsasZ7I4stU2ntB6rvauH7ZUNDU2cMAuQ9l/56FMP2LXds9d/clUbmJo1m2fOHr3iu7/bXuM4Msn7lXRGMzSkHat5SFg6XCxVef6NIh/n7Zvu3XN3Zwawiwt0w4YX9H9//QjB/sIrvUKaR/Z+txxk1LdXr0qqdiSNFzSLZKWJt+HFWhzgKQ7JC2W9A9JPZ7/zbL3s4/6Cigrv42bN1ds3w9/ayoD+3lERVbcT5RXJabrsa6VemRrJrAgIiYBC5LHHa0DzoqIfYCpwAWShhZoZxU077NHcs/XpnTrBoxmadt77GBO3HcMN597NDefe3TZ9vvhwyf46trsuZ8oo7QHyH/k8F27bmRdKrXYmgbMTZbnAqd0bBARj0TE0mT5WWA1MLLE/VrK9tt5qK82tIrp26eBn3z4YN48ZhBDB5TvRrlDd/CpwzJwP1FGad5n66AJ7hfSUmqxNToiViTLK4FOr5WWdCjQBDxa4n7NqpJPmZSunKdBdh/pqw7LwP1EGfVJ8dDWrz5+eGrbqnddDlSQNB8YU+CpWfkPIiIkFZ1uRNJY4HJgekQUHaAhaQYwA2DCBE9mbDWn7ZTJbEkzk8df7tCm7ZTJUknjgHsk3RwRL5Y72Go0clB5PkkfNWkEpx5Y2YH5vUU5+wn3EZ1L6wNE867Dqua2LL1Bl8VWRBS9ZaykVZLGRsSKJElWF2k3GLgemBURC7vY3xxgDkBzc3NdzRVnvcI04O3J8lzgT3QotiLikbzlZyW1nTJxsZU4cMJQ7n0q21/HoROHe2qTlJSzn3Af0bnBKc1Xe+6UPVPZjuWUehqxbTJdku/XdWwgqQm4BrgsIq4ucX9m1c6nTFJQjim5XWeVjfuJGpTVnejrVanF1mzgeElLgSnJYyQ1S7o4aXM6cDRwtqT7kq8DStyvWcVImi9pUYGvafntIiKA7pwy+WhXp9YltUhqaW1tTe3nqGZtv7Tffuqtme3DVyGWjfsJq3sl3VwmItYAxxVY3wKckyz/AvhFKfsxqyY+tZ69SA5tNTaIPg1i0+Z0f+ymPg1Mf+vEVLdphbmfqE118Y+mjHwHebN0+ZRJCjYnxVZWp/r+5bg9aGr0vz+zYobsUL5bsNQD/7cxS5dPmaSgbcyWyKba8sB4s87tO35IpUPoVTxHhVmKfMokHVP3GcPiZ19m9JB+W04pmpnVKhdbZlZ1PvOOPTjriIkMKePd5M3MsuLTiGZWdRoalGmhdfjuwzPbtplZRy62zKyuzDh6dw7e1cWWmZWPiy0zq2pvm5TufMSNvlujmZWZiy0zq2o//fBBfPhwz4Fn1l2PfOvESodgHXiAvJlVtQFNjZz/7n3YZ9wQjtlzJG+d/ceStue7Plhv53vIVZ+S/yKShku6RdLS5PuwTtoOlrRc0g9L3a+Z1Y/GPg2ceeiEVE4BZnXvLivMfYRZOqcRZwILImISsCB5XMw3gdtS2KeZ1SPXSbXIfYTVvTSKrWnA3GR5LnBKoUaSDgZGA39IYZ9mVcuf5LPjo1I1yX2E1b00iq3REbEiWV5JLlnakdQAfB/4YlcbkzRDUoukltbW1hTCMys7f5LPiMdb1ST3EVb3ujVAXtJ8YEyBp2blP4iIkFRobo1PAzdExPKu5iSLiDnAHIDm5mbP02G1aBrw9mR5LvAn4MsdG+V9kr8JaC5TbHXPBVv63EeYda5bxVZETCn2nKRVksZGxApJY4HVBZodARwl6dPAQKBJ0tqI6OwTv1mt2p5P8h8mN2G1dUMaddIHDtklha1YPvcR1ee3nzqC9/7kjm61fdPIHXm09dWMI6pvaZxGnAdMT5anA9d1bBARH4qICRExkdxh4sucRFbLJM2XtKjA17T8dpGbRbnTT/Ld2JdPm6TktIPGs/OwAZUOo964j6iA7Zkl4aZzj+aoSSO2PJ40amAWIdW1NO6zNRu4StLHgCeB0wEkNQOfjIhzUtiHWVUp5yd5nzbZqqtTTFaV3EdUub59GvjZ2YewYVPQ1Njgy1AyUHKxFRFrgOMKrG8BtkmiiPg58PNS92tWxdo+yc+mk0/ybcuSzgaa/Um+a+4Eao/7iNrQ2KeBxj6VjqL38m1mzdI3Gzhe0lJy47FmQ+6TvKSLKxpZjfOBLTOrRZ6uxyxl/iSfHd9ny8xqkYstM+v1Jo0ayMG7DuNL73xzpUMxszrk04hmVjP6NvbsyFa/vg3Mfu9+7DSwX8oRmVWvn330kEqHYAkXW2ZWMwY0+WC8WXe9482jKh2CJVxsmZmZmWXIxZaZmZlZhlxsmVmv853T3tLu8QG7DK1QJGZmJRZbkoZLukXS0uT7sCLtJkj6g6Qlkh6UNLGU/ZpZ/Tpur67HoZx56IR2j8971z5ZhWNdcD9hVvqRrZnAgoiYBCxIHhdyGfC9iNgbOJTC05eYmXXpkrMP4YnZJxd9/uNH7dbu8fAdm2hq9EH8CnI/YXWv1P9A04C5yfJc4JSODSRNBhoj4haAiFgbEetK3K9ZVfKn+MqbdfLkSodg7bmfqGLjhvSvdAh1odRia3RErEiWVwKjC7TZE3hR0u8k3Svpe5KKzsAkaYakFkktra2tJYZnVnb+FF9lfM/5iku1n3AfsX1uPvfoTp+/9jNHlimS+tZlsSVpvqRFBb6m5beLiACiwCYagaOALwKHALsDZxfbX0TMiYjmiGgeOXLk9vwsZtXAn+KrTKF/SpaucvYT7iO2z5vHDGKHvoWPb+w2YkdGDfaRrXLo8g6BETGl2HOSVkkaGxErJI2l8Kfz5cB9EfFY8pprgcOBS3oYs1k1265P8cBuwHxgZkRsKrRBSTOAGQATJkwo1KQuPfKtE9nzazdWOgzD/USt8lHf8in1NOI8YHqyPB24rkCbu4Ghkto+ghwLPFjifs0qxkd7q4MHvdcM9xNW90r9bzUbOF7SUmBK8hhJzZIuBkg+rX8RWCDpAXLF9EUl7tesYiJiSkTsW+DrOmBV8umd7nyKj4iNwLXAQeX7CczKyv2E1b2SJhqLiDXAcQXWtwDn5D2+BdivlH2Z1Yi2T/Gz6can+IhoJfcpvqV8IfYeIwf1o/WVNyodhnXC/UT18tHh8vFv2ixd/hRfRjOO2r3SIZhVvcE7FD6uctFZzWWOpH6VdGTLzNrzp3gzqzZXfeIIjvnen9qt+9rJe7PL8AGVCagOudgyMzPrxXbdaUfm/tOhTL/0Lu4/7wRWvfI6k0YNrHRYdcXFlpn1Gvefd0KlQzCrSsfsOXLLNFdDBvStcDT1x2O2zKzX6N/kf2lmVn38n8nMalZ0uI1Z5D386kl7lTkaM7PCXGyZWa/03oN2rnQIZmaAiy0z66V2aMrNB3f83oVmTDIzKx8PkDezXkN5k70NaGrkrq8ex7AdmyoXkJkZKRzZkjRc0i2SlibfhxVp911JiyUtkXShJM+BaWapig4zUY4a3J++fXwAv5LcR5ilcxpxJrAgIiYBC5LH7Uh6K3AkuZs47ktu8t1jUti3WdVx51I+HYsrq0ruI6zupVFsTQPmJstzgVMKtAmgP9AE9AP6AqtS2LdZNXLnUiEuV6uS+wire2kUW6MjYkWyvBLYZjRqRNwB3AqsSL5ujoglhTYmaYakFkktra2tKYRnVnbuXMy2ch9hda9bA+QlzQfGFHhqVv6DiAhJ2xzYl7QHsDfQdi32LZKOiojbO7aNiDnAHIDm5mafJLBa1K3ORVJb5yLgh511LsAMgAkTJmQTcS8hfGirEtxHmHWuW8VWREwp9pykVZLGRsQKSWOB1QWanQosjIi1yWtuBI4Atkkks1rgzqW6nHrgeE7cdwxNjR4MXwnuI8w6l8Z/pnnA9GR5OnBdgTZPAcdIapTUl9zYlIKf4s1qQURMiYh9C3xdB6xKOhW607kkHUxb52LbYeq+uXr3E8fszgn7FKp9rQq4j7C6l0axNRs4XtJSYEryGEnNki5O2lwNPAo8ANwP3B8Rv09h32bVyJ1Lmey60448Mftk9hozuNKhWHHuI6zulXxT04hYAxxXYH0LcE6yvAn4RKn7MqsRs4GrJH0MeBI4HXKdC/DJiDiHXOdyLLnOJYCb3LlYb+Q+wsx3kDdLnTsXMzPL59GkZmZmZhlysWVmZmaWIRdbZmZmZhlysWVmZmaWIRdbZmZmZhlysWVmZmaWIRdbZmZmZhkqqdiS9H5JiyVtTm7YWKzdVEkPS1omaWYp+zQzs9rhfsKs9CNbi4DTgNuKNZDUB/gRcCIwGThT0uQS92tWldyxmG3D/YTVvZKKrYhYEhEPd9HsUGBZRDwWEeuBK4FppezXrIq5YzHL437CrDxjtsYDT+c9Xp6sK0jSDEktklpaW1szD84sTe5YzHqk2/2E+wirRV0WW5LmS1pU4CuTziEi5kREc0Q0jxw5MotdmFWaP4BYr1LOfsJ9hNWiLieijogpJe7jGWCXvMc7J+vMapKk+cCYAk/Niojr0t5fRMwB5gA0NzdH2ts3K5X7CbPOdVlspeBuYJKk3cglzxnAB8uwX7NMuGMxS537CevVSr31w6mSlgNHANdLujlZP07SDQARsRH4LHAzsAS4KiIWlxa2WU3b0rFIaiLXscyrcExmmXA/YVbika2IuAa4psD6Z4GT8h7fANxQyr7MaoGkU4H/AUaS61jui4h3ShoHXBwRJ0XERkltHUsf4FJ3LNZbuZ8wK89pRLO64Y7FzMw68nQ9ZmZmZhlysWVmZmaWIRdbZmZmZhlysWVmZmaWIRdbZmZmZhlysWVmZmaWIRdbZmZmZhlysWVmZmaWoVKn63m/pMWSNktqLtJmF0m3Snowafu5UvZpVs2cE2btOSfMSj+ytQg4DbitkzYbgS9ExGTgcOAzkiaXuF+zauWcMGvPOWF1r9S5EZcASOqszQpgRbL8iqQlwHjgwVL2bVaNnBNm7TknzMo8N6KkicCBwJ2dtJkBzEgerpX0cJGmI4Dn0oyvzBx/5fQ09l3TDsQ5sUWtxl6rcUM6sZc9J7YjH8B/n0qo1bghw5zostiSNB8YU+CpWRFxXXf3Lmkg8Fvg3Ih4uVi7iJgDzOnG9loiouD5/1rg+Cun1NidE+mr1dhrNW5IN/Zy5kR38yHZnv8+ZVarcUO2sXdZbEXElFJ3IqkvuQT6ZUT8rtTtmVWSc8KsPeeEWecyv/WDcifqLwGWRMQPst6fWbVzTpi155yw3q7UWz+cKmk5cARwvaSbk/XjJN2QNDsS+AhwrKT7kq+TSoo6p1uHkauY46+czGJ3TvRYrcZeq3FDmWJ3TvRYrcZeq3FDln1DRGS1bTMzM7O65zvIm5mZmWXIxZaZmZlZhmqy2JI0VdLDkpZJmlnpeNpIekLSA8l4g5Zk3XBJt0hamnwflqyXpAuTn+Efkg7K2870pP1SSdMzjPdSSaslLcpbl1q8kg5Ofh/LktcWv6thevGfL+mZQuM+JH0lieVhSe/MW1/w/SRpN0l3Jut/LakpzfjT4nxIJdaazAXnQGHOiVRidU6kmRMRUVNfQB/gUWB3oAm4H5hc6biS2J4ARnRY911gZrI8E/jPZPkk4EZA5KanuDNZPxx4LPk+LFkellG8RwMHAYuyiBe4K2mr5LUnliH+84EvFmg7OXmv9AN2S95DfTp7PwFXAWckyz8FPlXp91iBn8v5UMe54BxwTjgnaiMnavHI1qHAsoh4LCLWA1cC0yocU2emAXOT5bnAKXnrL4uchcBQSWOBdwK3RMTzEfECcAswNYvAIuI24Pks4k2eGxwRCyP3rrwsb1tZxl/MNODKiHgjIh4HlpF7LxV8PyWfso4Frk5en/+7qCbOhxTUai44BwpyTqTAOZFuTtRisTUeeDrv8fJkXTUI4A+S7lFuSgmA0ZGb9wtgJTA6WS72c1T650sr3vHJcsf15fDZ5FD2pW2Hudn++HcCXoyIjR3WV5tKv186U+v5UMu5UE850JFzIjvOiR7mRC0WW9XsbRFxEHAiuVnrj85/Mqnga+ZeG7UWb+InwJuAA8hNbPv9yoZT13pNPtRSrDgHqplzojIqnhO1WGw9A+yS93jnZF3FRcQzyffVwDXkDkWuSg6ZknxfnTQv9nNU+udLK95nkuWO6zMVEasiYlNEbAek8gwAAAGCSURBVAYuIvc3oIs4C61fQ+5QeGOH9dWm0u+XonpBPtRkLtRhDnTknMiOc6KHOVGLxdbdwKTkioAm4AxgXoVjQtKOkga1LQMnAIvIxdZ2BcZ0oG1S1nnAWclVHIcDLyWHZ28GTpA0LDnUeUKyrlxSiTd57mVJhyfnuc/K21Zm2v4RJE4l9zdoi/8MSf0k7QZMIjdAs+D7KfnUdivwvuT1+b+LauJ8yE5N5kId5kBHzonsOCd6mhNRBVdobO8XuSsfHiF3tcCsSseTxLQ7uSsW7gcWt8VF7hzvAmApMB8YnqwX8KPkZ3gAaM7b1j+RG6i3DPhohjFfQe6Q6gZy554/lma8QHPypn4U+CHJjAUZx395Et8/kkQam9d+VhLLw+Rd+VLs/ZT8Te9Kfq7fAP0q/T5zPmSTD7WaC84B54RzojZywtP1mJmZmWWoFk8jmpmZmdUMF1tmZmZmGXKxZWZmZpYhF1tmZmZmGXKxZWZmZpYhF1tmZmZmGXKxZWZmZpah/x9RfaxCkytWfwAAAABJRU5ErkJggg==", + "text/plain": [ + "
" + ] + }, + "metadata": { + "needs_background": "light", + "tags": [] + }, + "output_type": "display_data" + } + ], + "source": [ + "rows = 3\n", + "cols = 3\n", + "n = rows*cols\n", + "fig, axes = plt.subplots(rows, cols, figsize=(10, 12))\n", + "for i, (audio, label) in enumerate(waveform_ds.take(n)):\n", + " r = i // cols\n", + " c = i % cols\n", + " ax = axes[r][c]\n", + " ax.plot(audio.numpy())\n", + " ax.set_yticks(np.arange(-1.2, 1.2, 0.2))\n", + " label = label.numpy().decode('utf-8')\n", + " ax.set_title(label)\n", + "\n", + "plt.show()" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "EWXPphxm0B4m" + }, + "source": [ + "## Spectrogram\n", + "\n", + "You'll convert the waveform into a spectrogram, which shows frequency changes over time and can be represented as a 2D image. This can be done by applying the short-time Fourier transform (STFT) to convert the audio into the time-frequency domain.\n", + "\n", + "A Fourier transform ([`tf.signal.fft`](https://www.tensorflow.org/api_docs/python/tf/signal/fft)) converts a signal to its component frequencies, but loses all time information. The STFT ([`tf.signal.stft`](https://www.tensorflow.org/api_docs/python/tf/signal/stft)) splits the signal into windows of time and runs a Fourier transform on each window, preserving some time information, and returning a 2D tensor that you can run standard convolutions on.\n", + "\n", + "STFT produces an array of complex numbers representing magnitude and phase. However, you'll only need the magnitude for this tutorial, which can be derived by applying `tf.abs` on the output of `tf.signal.stft`. \n", + "\n", + "Choose `frame_length` and `frame_step` parameters such that the generated spectrogram \"image\" is almost square. For more information on STFT parameters choice, you can refer to [this video](https://www.coursera.org/lecture/audio-signal-processing/stft-2-tjEQe) on audio signal processing. \n", + "\n", + "You also want the waveforms to have the same length, so that when you convert it to a spectrogram image, the results will have similar dimensions. This can be done by simply zero padding the audio clips that are shorter than one second.\n" + ] + }, + { + "cell_type": "code", + "execution_count": 124, + "metadata": { + "id": "_4CK75DHz_OR" + }, + "outputs": [], + "source": [ + "def get_spectrogram(waveform):\n", + " # Padding for files with less than 16000 samples\n", + " zero_padding = tf.zeros([16000] - tf.shape(waveform), dtype=tf.float32)\n", + "\n", + " # Concatenate audio with padding so that all audio clips will be of the \n", + " # same length\n", + " waveform = tf.cast(waveform, tf.float32)\n", + " equal_length = tf.concat([waveform, zero_padding], 0)\n", + " spectrogram = tf.signal.stft(\n", + " equal_length, frame_length=480, frame_step=320, fft_length=512)\n", + " \n", + " spectrogram = tf.abs(spectrogram)\n", + "\n", + " return spectrogram" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "5rdPiPYJphs2" + }, + "source": [ + "Next, you will explore the data. Compare the waveform, the spectrogram and the actual audio of one example from the dataset." + ] + }, + { + "cell_type": "code", + "execution_count": 125, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/", + "height": 145 + }, + "id": "4Mu6Y7Yz3C-V", + "outputId": "cdb225d1-54f2-4554-e518-a9f32865f728" + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Label: no\n", + "Waveform shape: (16000,)\n", + "Spectrogram shape: (49, 257)\n", + "Audio playback\n" + ] + }, + { + "data": { + "text/html": [ + "\n", + " \n", + " " + ], + "text/plain": [ + "" + ] + }, + "metadata": { + "tags": [] + }, + "output_type": "display_data" + } + ], + "source": [ + "for waveform, label in waveform_ds.take(1):\n", + " label = label.numpy().decode('utf-8')\n", + " spectrogram = get_spectrogram(waveform)\n", + "\n", + "print('Label:', label)\n", + "print('Waveform shape:', waveform.shape)\n", + "print('Spectrogram shape:', spectrogram.shape)\n", + "print('Audio playback')\n", + "display.display(display.Audio(waveform, rate=16000))" + ] + }, + { + "cell_type": "code", + "execution_count": 126, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/", + "height": 499 + }, + "id": "e62jzb36-Jog", + "outputId": "bc3a0ea2-38d7-496a-d1d5-5048f99eeed6" + }, + "outputs": [ + { + "data": { + "image/png": "iVBORw0KGgoAAAANSUhEUgAAAucAAAHiCAYAAABLImLmAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAALEgAACxIB0t1+/AAAADh0RVh0U29mdHdhcmUAbWF0cGxvdGxpYiB2ZXJzaW9uMy4yLjIsIGh0dHA6Ly9tYXRwbG90bGliLm9yZy+WH4yJAAAgAElEQVR4nOydeZxcVZn+n7eqestOFrYESCABQZCoEVAWUVBAweiMMiCjuAzoKC7jb8YJKqAsoo4LyrigiCDDqqIsCXuAEAghYctKyL4vna3T6bWq7vn9cbdzb51TdaurOl3pfr6fT5JbZ6/q6tRz33rOe0QpBUIIIYQQQkjfk+rrBRBCCCGEEEJcKM4JIYQQQgipESjOCSGEEEIIqREozgkhhBBCCKkRKM4JIYQQQgipESjOCSGEEEIIqREozgkhhASIyEEiMktEWkXkZ329HkIIGWhQnBNCSI0gIleKyKOxsuWWsot6aRmXA9gOYJhS6v/10hyEEEIsUJwTQkjtMAvA+0QkDQAicgiAOgDvjJVN9Nr2BkcAWKJ6cEKdiGR6YT2EEDKgoDgnhJDaYR5cMT7Ze3w6gGcALIuVrQRwjogs9ewnq0TkS/4gXvn52uOMiDSLyLu8x6eIyIsisltE3hCRM73y2wFcCuDbIrJXRM4WkQYRuUlENnl/bhKRBq/9mSKyQUT+W0S2APiTiHxfRP4iIv/nrW2hiBztfSuwTUTWi8iHe+8lJISQ/RuKc0IIqRGUUt0A5gI4wys6A8DzAGbHymYB2AbgfADDAHwewC988Q3gHgAXa0OfA2C7UupVERkLYDqA6wGMBPCfAP4mImOUUp8DcBeAnyilhiilngLwXQCnwL05OBHASQC+p419sDfOEXAtMQBwAYA7ARwA4DUAj8P9vBkL4FoAt/TsFSKEkP4PxTkhhNQWzyEU4qfDFefPx8qeU0pNV0qtVC7PAXjCqwOAuwF8TEQGeY8/DVewA8C/ApihlJqhlHKUUk8CmA/gI5b1XALgWqXUNqVUM4AfAPiMVu8AuEYp1aWU6vDKnldKPa6UygH4C4AxAH6klMoCuBfAeBEZUfYrQwghAwCKc0IIqS1mAThNREYCGKOUWg7gRbhe9JEAjgcwS0TOE5GXRGSniOyGK65HA4BSagWApQAu8AT6x+AKdsCNcH/Ks7Ts9vqeBuAQy3oOBbBWe7zWK/NpVkp1xvps1a474Ebt89pjABhS+qUghJCBBzfvEEJIbTEHwHAAlwF4AQCUUntEZJNXtsn7sxjAZwE8qJTKisg/AIg2jm9tScHd4LnCK18P4E6l1GUJ17MJrqBf7D0+3CvzKXvjKCGEEDuMnBNCSA3hWUPmA/gWXDuLz2yvbBaAegANAJoB5ETkPADxTZb3emX/jjBqDgD/Bzeifo6IpEWk0dvYOc6ypHsAfE9ExojIaABXe2MQQgjpBSjOCSGk9ngOwIFwBbnP817ZLKVUK4CvA7gfwC64nvKH9AGUUpvhRuHfB+A+rXw9gKkAvgNX3K8H8F+wfx5cD/dmYQGAhQBe9coIIYT0AtKDVLaEEEIIIYSQXoCRc0IIIYQQQmoEinNCCCGEEEJqBIpzQgghhBBCagSKc0IIIYQQQmoEinNCCCGEEEJqhH51CNHo0aPV+PHj+3oZhBBCCCGkn/PKK69sV0qNqfa4/Uqcjx8/HvPnz+/rZRBCCCGEkH6OiKztjXFpayGEEEIIIaRGoDgnhBBCCCGkRqA4J4QQQgghpEagOCeEEEIIIaRGoDgnhBBCCCGkRqA4J4QQQgghpEagOCeE4M45azDtbwv6ehmEEELIgIfinBCCqx5cjHvnre/rZRBCCCEDHopzQgghhBBCagSKc0IIIYQQQmoEinNCCCGEEEJqBIpzQgghhBBCagSKc0IIIYQQQmqEqohzETlXRJaJyAoRmWaoP0NEXhWRnIh8MlZ3qYgs9/5cqpW/W0QWemP+SkSkGmslhPQdXbk8unNOXy+DEEIIqVkqFucikgbwawDnATgOwMUiclys2ToAnwNwd6zvSADXADgZwEkArhGRA7zq3wK4DMAk78+5la6VENK3HHvVYzj5h0/19TIIIYSQmqUakfOTAKxQSq1SSnUDuBfAVL2BUmqNUmoBgHjI7BwATyqldiqldgF4EsC5InIIgGFKqZeUUgrAnwF8vAprJQOQr93zGsZPm97XyyAAHAXsas/29TIIIYSQmqUa4nwsAP30kg1eWSV9x3rXJccUkctFZL6IzG9ubk68aDJwePiNTRWP8cTiLXhx5fYqrGb/QimF7Xu7+noZhBBCyIBhv98QqpT6vVJqilJqypgxY/p6OaSfcvmdr+DTf5jb18vYJ/z22ZXBNw1/nL0aU65/Cqu3t/XxqgghhJCBQTXE+UYAh2mPx3lllfTd6F33ZExCSAX8+LE3g+vn3nK/jVq7g+KcEEII2RdUQ5zPAzBJRCaISD2AiwA8lLDv4wA+LCIHeBtBPwzgcaXUZgB7ROQUL0vLZwE8WIW1EkIS4m73IIQQQsi+pGJxrpTKAbgCrtBeCuB+pdRiEblWRD4GACLyHhHZAOBTAG4RkcVe350AroMr8OcBuNYrA4CvALgVwAoAKwE8WulaCSGEEEIIqWUy1RhEKTUDwIxY2dXa9TxEbSp6u9sA3GYonw/g+GqsjxCfB1/fiFGDG3DapNEl2y7a2ILm1i584G0H7oOV1R5KAT05XsBxFLrzDhrr0r2wKkIIIaR/s99vCCWkHL5x7+v41z8m29h5/s2z8fnb5yVq6zgKm1s6KllazbOqeS/GT5uOp5ZsLdruhhlL8barHuNhQ4QQQkgPoDgnpArc9NRbeO+NM7Fxd/8R6Cp2/fr63QCARxYUpqac+usXMPnaJwAA9768DoB7GighhBBCyoPinJAq8NxyNwf6tj2dZffd1tqJ9u5ctZdUkhdXbMeKba1F2+imlmIOlzfW78Zu73ChnlhhCCGEEOJCcU5IH3PSDU/jn37z4j6f99O3zsXZP59lrY9na0mavMWX5grAns4sxk+bjjvnrClo9/AbmzDl+qeQyxfaX/KOQt5hthhCCCEDD4pzMmDYF9HpnsrJN7cUj2D3BT2Wxp46VwrY0uJ+k/DnOWsLmv3g4cXYvrcLO9u6C+pO/dFMTP7BEz1dASGEELLfUpVsLYTsD2RzvReJ7fdGDlVoa9nd3o1B9RnUZyz3+CVebt/+Ymq2pQf2IEIIIaQ/wMg5GTj0ooK26dC2rhx2t7uR4Wfe3IYr7n410XjdOQcn//ApPLZoc8Vr293ebbSOlMJmY/GLJ1/7JL505/yCev1lTmKF6fc3NoQQQkgZUJyTAUNf7FM886fPYvK1TwIAPn/7PDyyIJnY3tnWja17uvDl/0sm5m3kHYXJ1z6J//7bworGAQAxyOhnljVb2yvtlqXUa9/SkcXE78zArLfs4xFCCCEDAYpzQqqATXs2t3b1aLxUlW4kco4bMX/ojY1l91VQPbqhCewqZbiIlm7eg5yj8L8zV5Q/ISGEENKPoDgnA5bd7d1BRpIV21oLspP0KVUS5360u9KnpkfBS43lC/pSU+rj0NpCCCGEuFCckwGDLgBXNe/F5GufxJ0vrcWst5px9s9n4a+vbKh4jmrpe5OFpEfjJBTKJpRKnufcOkaSmanMCSGEkACKc9IvcRyF1dvbrPVrdrh1M9/chuXb9gIAFm/ag1zewbod7WXPV20/e7VsLT598a1ARNgXU+BKv6yhby8IIYSQPoDinPRLfv/8Knzgp89i8aaWoEw/uVK3e/jCVQS4YcZSnPE/z2Brman8qq19q3XKpr8uf3n//dcFeHLJ1srGrHI7gKeKEkIIIT4U56Rf8uraXQCA9Ts7grKI/DNoQYHgxRU7AMB4MM6+RF9ed87BJ3/7Iuat2dnj8XyRft/89bjsz4XpD3u6tmL1SqlkNy0lBmzryuHOOWtqa08AIYQQ0ktQnJN+SeC1LiHo9FqRZPaUC383B7c+v8o4X7XQx1u3sw3z1+7CtL8tKNpnw672gnzmldhE9Jcuct3jEcub0+e6R5bgqgcXY9by7b04MyGEEFIbUJyTfklgW7HWh5gEYTFN//Kanbh++tIer60YX77zFdw5Z03Z/Zpbu3Daj5/BDTN6Z109IbGIN5w+quN/i9HRna94TYQQQkitQ3FO+iUp751dKuKrR9Yjgr2PNiY+tngLrnpwsbGu2IpaOlwB+1zsEJ9KnCC21yCpvSSSKtEovstdHG0thBBC+j9VEecicq6ILBORFSIyzVDfICL3efVzRWS8V36JiLyu/XFEZLJX96w3pl93YDXWSgYG/gZDR1OIESGuqUVfhLq2luT+lLyj8LV7XsOijS2lG8d4c8ueotlkyk2l2JODf0qhVGwTbcLXJlgLknnOFTSfunG86OMbH12KmW9WtqmVEEIIqVUqFucikgbwawDnATgOwMUiclys2RcB7FJKTQTwCwA/BgCl1F1KqclKqckAPgNgtVLqda3fJX69UmpbpWslA4eUSZxr9SZbS0SwJxCVG3a14+E3NuErd70aKd+4uwMX/m4OWtqz1r7n3vQ8PvDTZ6315UbuU4E4773ocm+OnXSPAADc8twq/OdfivvvCSGEkP2VakTOTwKwQim1SinVDeBeAFNjbaYCuMO7/iuAs6QwDHex15eQigkzhoRlNt1nE+095TfPrMDLa3bioTc2mucrW+RGV9WdcwrG8POi56sooPWRejRswj6lxjbV93U2HUIIIaS3qIY4HwtgvfZ4g1dmbKOUygFoATAq1uZfANwTK/uTZ2m5yiDmCbESnoxps7X4ZVrkHMCSzXsqnFlVdCpnMEqkc/hgT2cWR3/vUdw8c0WkffBNgeO2GT9tesX5zOOU+ytY6vkbN+KWNQMhhBDS/6iJDaEicjKAdqXUIq34EqXUCQBO9/58xtL3chGZLyLzm5ubTU3IACRl8GBHNigi9EVrhT1CQUUi9Sa/eN6pjuzc5UWM//LK+kh5KhXaeFY3u172m2cur2xDqKVzqSFN31oUnSfiOjeMV+Tn8n8vrcUZP3km2USEEELIfkA1xPlGAIdpj8d5ZcY2IpIBMBzADq3+IsSi5kqpjd6/rQDuhmufKUAp9Xul1BSl1JQxY8ZU8DRIf8IXdLomtmnFYEMoyvOc6+1fXbcbAHDHnLXGMaIbU0uPHZ+pFKng+Spk0u6D7pxTpEf51OJXV9/7xyKs29ne18sghBBCqkY1xPk8AJNEZIKI1MMV2g/F2jwE4FLv+pMAZiovLCciKQAXQvObi0hGREZ713UAzgewCIQkxBfOuih2DKo4YmvR1GdPUyku2LC7KgcSlZpdP/kU0J9v7CajkkOIetwznDvJ/En2BZSqI4QQQvoLmUoHUErlROQKAI8DSAO4TSm1WESuBTBfKfUQgD8CuFNEVgDYCVfA+5wBYL1SSj9ysQHA454wTwN4CsAfKl0rGTikAm9FWGbKu90becDD+SqJlvcMQe/kaI/ccFRpo6c+XLEbmnLTShJCCCH7MxWLcwBQSs0AMCNWdrV23QngU5a+zwI4JVbWBuDd1VgbGZiIZvPw0UWrSe5VSwKWGqcS6Ry13ijcNXcdpk4+tMiBQT2fq1hEu1jGmWpsiI3MxW2ihBBCBhA1sSGUkGoTbAjVyszZQZRRaJbrOQ/L9EN4ek6SdIuvrN2F7/1jEb7z90U1aflIfpKoORd9HOZrIoQQMhCgOCf9EvMJoXoD95/1OzssnvOe88TiLQCAXN4Wza6Oku7Ouxs+t+3prMp4BcSWGXl9ynwKpjSM5eaXr8UbEEIIIaTaUJyTfok5W4tua3EbbNzdEZT21Nsc9bILNrW4YrmlIzwhtCrWjNgQ9Wn31zfnmEfXn1vPplPWVyTJuIk95yXaJfm59ObppYQQQsi+hOKc9EvCDaHJN2VGI8OlxV4pm0Ul6RMjUWXLPBlfnOfNKRNbO3PlTZqQ3tlwWoUduoQQQkg/gOKc9Ev01II+NsH799fctPy5Mg8KMulIfdx8Dw/xsc4Xe5zx7kCyebNvHqgsohzvGt+MaqfwAKhSGJLrFK6nWJ2hcm9XDttae8nyQwghhPQSFOek36CUwpUPLMTr63dHDuXR602s3u6eqNnWFUaabULQdLCPLshT2gOnSqeChl8CqMhcdV7kPJt39knAuaKc6cZNt8nGS7IR1DTSR375PE664elEcxBCCCG1AsU56Te0duVwz8vr8Jlb52o+8pCIN7yHc+zpLPSRlztuEk0a97GbSHt3IMUi/pV5zntWF7ZJmq3FfF2srLCNwu72boyfNh1/emE1APDkUEIIIfslFOek3+BnR0mnQzGrC9skgtfUtqdtbHOXSynLR1bznG9r7er5RCXXUd4tTTmvd6VpEhXcDbAAcN+89eX1VQp3zV0b+eaEEEII6Ssozkm/Iee4IjWTMr+tS0Vys5HUhwmOnTeU9UYubn/MtTvikeDCyH0xyvWf233sxec0HUJUbO5qbDDV12S6EdjS0onx06Zj+oLNBXUvrtyB7/59EX7w8OKK10EIIYRUCsU56Tf44jqTMitkJxLJLay/5+V1xn6RQ3KUubwUCze2BNe+x70Y5QpW21Kq6UWP5oFP6BcvN9puGDdJFp1Sp78u3bIHAHD//MKoent3HgCws627jJUSQgghvQPFOek3+Js10xZxrgu7UpLR5oPWRaAq0Vbnra2twfXq7XtLzB7FJnCt66qSUK9U1yulEol4pZKL+GLPwXSYVDn4/R9ZsAm/fGp5zwYhhBBCKoTinPQb9niH/gxpyJg3FvZwXKeIvaMY0c2oZZ6HaRHepiZFxWgvZHEpZWspd2qFZJH4UsLcHyOJOL/+kSW45NaX3Paxuivufg2/eOqt0oMQQgghvUCmrxdASLXozLr2hMa68J6z3IOFTCjLA388mxiM9OsNM7o+VxXFcjljmjCc/1S1OZPkOk8Shb919urkiyKEEEL2IYyck35DKS0YzR6SfCyrTaTEuHqe80qkeSmbyoZdHfa+leQmN1h4yiWJUFZKJRLyIiU2qWrtesI+SBVPCCGElITinPQbbPaTsD75WKV85vE2xcri9PRQncI2ydflllcmP2054+0U95yXlxunhK2lxIbQYvTylxqEEEJIWVCck36DL95sHuZyosi2DC3ljJc0lWDJtSRpo683webQcifuiX5NnOKxRJ9olhj7XJXefCTpv7mlA8u2tJZsRwghhPQUes5Jv8Eo7Cz15ei4crKf6Kd1VjONYdL5i5ZXMlfkunhEXM9zXm4O9nKsSfH1BVVlhsLLaf7eG2cCANb86KNlzUEIIYQkhZFz0m9wSkS7I/VljGuLRJvm0HOY66LvqDFDgutUAjVYbj71ci0iSYiP01v2D90vXrJtMZtMsCG0h+voYT9CCCGkmlRFnIvIuSKyTERWiMg0Q32DiNzn1c8VkfFe+XgR6RCR170/v9P6vFtEFnp9fiWlzv8mAx5ffEezFvbsbWOzWuji8I0NuwEAza1dJcfLpHv+q2ZdSwJveRIvetnrSZpKsVTmFaNnv5hHvZgwD2Pn5f7IS21afWLxliCHPiGEENLbVCzORSQN4NcAzgNwHICLReS4WLMvAtillJoI4BcAfqzVrVRKTfb+fFkr/y2AywBM8v6cW+laSf/GKN2sp3uWGCsiyM380UvH1503Czd7nvPS5UmwRvR7PGJsfFVZtFwhWRYWJG5XwtZSaeTcMPaLK7fj8jtfwU+fWNbDUQkhhJDyqEbk/CQAK5RSq5RS3QDuBTA11mYqgDu8678COKtYJFxEDgEwTCn1knLVy58BfLwKayX9mNC3HMpWe9Q5fPCuw0cUjhURvmYRXI2vcpJsNrVF7uM9fDLaCam2jaKVIJI8u0oib7oCkoxYLMIdTaVYpJ15YCu7292DrdbtaC+5PkIIIaQaVEOcjwWwXnu8wSsztlFK5QC0ABjl1U0QkddE5DkROV1rv6HEmIREcAwB7CTCNp0qLrNtsrFUVHloY51xDLvtJIG3PEH0v6kuXXKcJFhPJq1CZhR/nGqsx6ssGjlPciNFzzkhhJBaoK83hG4GcLhS6p0AvgXgbhEZVs4AInK5iMwXkfnNzc29skjSdyilSqauO/N/nsEFN882iitb1LusDaGa6I/Y2UtIvuMODd/KNiFaKjd74ayWFpbnVqnnPNqnvO8KEnvTC+aJor/Oxb5pKHViq318QgghpHaohjjfCOAw7fE4r8zYRkQyAIYD2KGU6lJK7QAApdQrAFYCONprP67EmPD6/V4pNUUpNWXMmDFVeDqklvjLKxtwzk2z8Nxb9huvNTvasXBji3FDqK7lbNlcjCLbEnEvJ2KcpK1tTdbrxLNXTqK0hQb817N4wsXoPJVma4nYWqoot/2RqmUJIoQQQkpRDXE+D8AkEZkgIvUALgLwUKzNQwAu9a4/CWCmUkqJyBhvQylE5Ei4Gz9XKaU2A9gjIqd43vTPAniwCmsl+xmLN7YAAFY370VLexYvr94JAGjpyBb4gJUmzoMDiZII2+LavOf50SPXdlFZDkm+CahmfnU9Cl1uRLon9pdK1q5Cdd7D/oWTR33xUXa3d2P8tOm4/YXVPZuQEEIIMVCxOPc85FcAeBzAUgD3K6UWi8i1IvIxr9kfAYwSkRVw7St+usUzACwQkdfhbhT9slJqp1f3FQC3AlgBN6L+aKVrJbVNS3sWndk8snkH/3rrXLy8eif8M31EBF+79zVceMsctHZm8dFfPY8z/ueZSH9jaj5LfcnTPasdrdYj9WK2aVjtKEmGt0W5KxG7Rcaq5g1A8qwupaL5nq2lzPmLZ2m1123a3QkAuHfeemsbQgghpFyqckKoUmoGgBmxsqu1604AnzL0+xuAv1nGnA/g+Gqsj9Quiza2IO8onHjYCJx47RM4Yexw3HTRZMxesR3rd7XjtImjAQApAVY17wUA7NjbjQ27OgrG8oW8dfNlEiUcFJn76TYUm6Y7+qAheGvr3iKRbfN45R82ZInG90KUvuhCio1RjbmCNvbxVIKfSyWY5uXJC4QQQnqDvt4QSgYgP3tiGcZPmw6lFM6/eTam/vqFoG7hxhY4nspOi0Qi5/7JmjlTWhYkiIaXsUZdKzrKXJ5glCq0SBaxLleo94SkYlS3gpR7WFGS1IvGMYK/yqdo3LyIrSWYm3Z0QgghVYTinOxzbp65AgCwqaXTWN/lncZYl04FEdGUSBBltpz5Ewp5razUxsohDdEvj0YPqY/Uu/206LZ56pLYBFzeMY9dSVrFcgV8dEyzpz0+b6KxEr5aekS8lAi2H+ZU+YZQ09BJhLvPTx93bzodh2qdEEJIz6E4J/uMuLDSD8vRRaovzuszqUCQpyQUT3mL+AkPIdL/tllI3H8FpUWkTTSf8/aDAdjziieJCCezspg9OIkkYJ/rRPsCktwIAIgoZKutpYInWqk9xZ/7N8+6N53J0mMSQgghZijOyT7hxhlLMeHKGZizckdQpots3arii5u4yFGG8lICr1REWcEcaU2SFeXIMYMBAG87ZGjhwIiLemOTRJaZRFHxMtvYsPdVieaIj2Vrt7OtO2xX6oYjybxV0MPFBX5hnS1CrwA4jsJ3/74QSzbtqXxhhBBCBhQU56Tq/O65lViyaQ827e7A+GnT8fLqnbhl1ioAwN0vrwva6eLcdO2oqADym0RFvTky7hONOSvj9UkTRgIAPvHOsVq9pV+JG4Ak5TqRG40S8xSOXx1Pe/kUH1UStHrXdU8GWVKS2mRKrqrHnnN76FxfY9J5HaWwfW8X7pq7Dp+97eWeLYoQQsiAheKcVJXunIMfPfom/vm3L+K1dbsBALfNXh0I3/cfHR4UpQvTiMj2rpVS4YY8rX1eFQp5d7zC9ejCKZc321pSnr3m4OGNCKSlNVpb/AbAhjXgW2Y0O4mtw745NMFclnldkVqepUapErHopPkqI5sILGNFrnum0ot5zk0j2uwwSoXvKVpcCCGElAvFOakKv312JR54dQO6cnkAQEc2jxGD6gC4Bwb5OkbXM3mDIAdC8R33iPtN9LZZbXeoKZKsC7WZb24z1GvXFo+47RTP4oXxSHiSNsYm1qmS2GZ6i6SHC1U1fSOKefd7LsqLec5TqdLritcopYl6r9/yra344+zVPVofIYSQgQXFOekxP37sTVz4uznB9bfufyMSnU570cO8UoHA1YWQLXIesbVIOIYvdPIRcW4Wt/51NCWiJfJryJFt81cbo/OFRYVjWNokody85fYsLglsMBabTbH5dP7x2katfxkiPlGrYv2TH2RkHcMYOfcj4KY6M6b32dRfv4DrHlmS+PUghBAycKnKIURkYPLbZ1cWlNk84P5VSvQMLVq/fGE/R7O15POO0dbiR+rj85koJVRtUWxbRN10A5CEEi6ZEn37RtzFLSq2VXzzvtcxdkST1yfp2OW1LVVeTf0rhS4nw9zRWkep8EAs79/2bvd9mncUMmmeXkQIIcQOI+ekJEoptHXlALhfz9/46FJrLmc964of4RZoHm9Nl+gR8O6c3s+fN4xO5hyl2Vpg7Gd2nJgtMEaU2WNcckNoEo+3Rd0lSYltF54JIuHl+sQt89rWk3z2ZK1Ktbd+S5Ggr3XMIh3DDaGlR/fbOipsH79hzFfzzoEQQki/hOKclOT++evx9msex/qd7fjCHfNwy3Or0Ly3y9hWj4D7UXSRUDjpYkW/7s4XRsD1yLkrzk2R88IUjIBZcHXnzUJeGcp0kkTRdXwrRDGLiKm8J6kKy2lfLvHXqCfzJW5X4TcCSrM9lWpnm7vcNdjeN5FvGWI3paYc/X+eswbjp00PboAJIYQMbCjOiZF1O9oxftp0PLNsGx5ZsBkAsGp7G5pbXVGuR6Hzlg2aeYNVRQ9e6/06s5rIDrK1hDaYXN4JhJ4ete/S+xmEs4orTAP+2qK5zW32HIO4K1NXjjugydg32QmelvIklpwyBX+8PElGmCRjGcdO0PZr97yGtTvaSo9nKJMiuz6LzZ0y7EFwH9s97o7S3lPBON7eCUch7yh89a5X8cranQCAW59fDQDB7xYhhJCBDcU5MfLGBjcN4l/mr4+U+1FhfSOmLjzAz50AACAASURBVMh1z3kuqpYBmLOyANGotp6tJRDneuQ8cppoGHE3qSWr7jKIVtsGTttBQeV4zvU2el7tcm0k5dpUKqHSzYuu1SSJ9cb2AFizvS2y8fLxxVuNY7R15Yt881F8DUEE3NAs3BAardQFeHxiRxPu/ty+yHccYMfeLkxfuBlfuetVdw5a0AkhhGhQnBMjjd6R9NHItApERpLIue8/F0gg0kz5zOPz5LXIuS+AcnmzraU7Z46c++iaamhjuP/ZJHIVzNladrWHp1ma0Kf91LvHGee2byo1i+2yLRZJIvBJbiKs4j8WKU56Q5LY1mIe9syfPmsdQn/fnXPTrMh7Ib4OaypLy+vvY3o/ADEBbpjPFjnvyucLNosWm3/WW80475fPl94vQQghpN9AcU7w0Bub8Nq6XcjlHXzkl8/jL/PXoyHjvjU6c3nUp73r7nwgMnQhZEqDqF+LhJs4bQcImbKuuFkvSoh6g+fc5vWeeOCQgueuIlHOSE1wtXFXh9YexuvCXsmi3NHxLAKyEl+6ZRxre63JI29sBgL/vHnMpOxuz1o3ESf1i7tzh+2+eMe8SF3wXigieuP2Fn3zpumJBVYqQ0YWe4rLwveU//vUlQ2zDvlLKRY4v/KBhVi6eQ+2tHQWaUUIIaQ/QXFO8PV7XsMnfvMiWjqyWLJ5D655aHGQo9xxQkGTdcINmnokL3odChZ9c6iPYxHypmwtjlLBdS7vBBYDWypFo1jWynRtuG1P6O/VRZYpr3Upr7U9KmsstmKzuNjtLpaoux71byse9S/Giua9wfWzy5rteeIt6Bsjt+zpxI2PLo3WW/oUHzS8fHZZc6K+RQ8QUsVvWcLNovF+uq0pLty1OvhCPLTH+K3991qxjDB1ad9Gxsg5IYQMFCjO+yn+B/2KbXtx37x1ANwI+fWPLAEAXPnAAvznX96ICIJug3dcDzTm8k4gJHIWYZ2z2F1MtpasLQKu21qcwn55S7/HFm/Rnr8/b4guLqc9sBAA0NadDxrZItR66kaDjT5CIiFta1OB/rIJ0Av+d7Zx3meWFZ6WGm/z8Bub8NRS1+N93/z1+L+X1pa3pth4f/A2PhrbFRlHjywXy2Vv3yxb5JsLwPjzLxgzHo1X9vn0b3z8Nv7vkaO9p01e8+6cgxtnLEVrZxYAkPG+tcpZvnUghBDS/6iKOBeRc0VkmYisEJFphvoGEbnPq58rIuO98g+JyCsistD794Nan2e9MV/3/hxYjbX2NxZtbEFnNh9kV5n1VjPmrdmJCVfOwKrmvbjygQX4778tRGc2j6/f8xpu9Y4Qv+fl9fjrKxsikW6TyBZBJJ1hEDnX2nZZLC5ZJxwjzFGu1Wv9IpHzwNaiec4N+dOBqFf95dU7g+ugSRJRrNWHIipsHE3RWETAFSm3ikNLhN60vsRjJljb5/80T2tT/JsBnw2evadYphIg6tNObKVJqD2LzRv6vKONio1fbI1KF9mxNhEB7pWJVhfX9H5d3lHB70gqps4VgL+/tgG3zFqFnz3xltdGew6EEEIGBBWLcxFJA/g1gPMAHAfgYhE5LtbsiwB2KaUmAvgFgB975dsBXKCUOgHApQDujPW7RCk12ftjDvUNYDq68zj/5tn46l2vYr6Xlu2vr2zA7S+uAQAs3NiCBRtaALieXx9d3OqiVxfIvmjXM4vknTB7ih5l160lto2iQZmmMrJ5s8CP+Nb9TaDajUHecAMQx2RHsEVdzbmv9XpbualfuE7bGOffPDvSvtQ88fHN5aVJdINQZKDOrGYh6pHzPEprp5vX+7YXVgfjrdnRjkcWbLL2KTZrseBysTzz5j0HvnA31+kCPL42fUOoX6hbV+K/FxLp5153dOcjbUzrX7BhN344Y2nFWXUIIYTUFtWInJ8EYIVSapVSqhvAvQCmxtpMBXCHd/1XAGeJiCilXlNK+Z/EiwE0iUhDFdbUr9nS0ond7d1o7XIF9+wV24MP9ZSEETmlgMENboaS3R2h99jmEe82ZVqJ2Vr8SF63Jeodjb6HAt/XDxGfeV7vV7gh1N1IqgrWqYtsk68dQGhVSCA8w+wZ5gh5uSeElqOVkkXXbTcV5bW3ob+exbzNu7QbvCTowrcY0xdsxhdunx88/o5nOTKOmSByPm/NLry5ZU+idRSzp6jgr8KfTSSXuUm4q+iafPJaxD343Qqi4yrcOOr9Pkhsu+hObQ/Bp/8wF7+ftQod2aiQJ4QQsn9TDXE+FoCeDHuDV2Zso5TKAWgBMCrW5p8BvKqU0k/i+JNnablKip0iMsA45cancdbPngssHbrgTYkEm8i6c07w0Z7NWUR4voSw1l72nGMWHXrUu9MSAfflTSRqnzePERXihWPp+tEmJkNbgVnUR9oaBJhtI6neyhhVjdwMhKknbdK7VCTetla3b/GbhsK+pdcw+donSw9UYj7f/tLc2oXP3vZyovFs6ymss9fqlql/vXVu2KeYAFf2+dzNom5twXun1JixyoitJfjditUphYaMl8I0lhZSKWDemp1413VP4rFF7qFgGe/3vK2L4pwQQvoTNbEhVETeDtfq8iWt+BLP7nK69+czlr6Xi8h8EZnf3NxsatIv2dHWHYjs+GaxulThJrKoINcEsmFjp1Kh6NXviPKRg4BQ0A9w0y366J5zU+Q8a7kx0Of2bS05R2kCR+tnEeemqKbN9qCfJiqxMn2s+Hhh/+h1KVtLpK9W8R/3v2Est2h/6yp8K1NSynVFPLOsGTfMWFqy3V1z15U3sIct5SJQfK35Ij8nqwBHmMYx3kZp45ij434//xuisC74Nsaw/iByHouKm2xg+nyLNro/1zkrd0TmyzvuWvx6Qggh+zfVEOcbARymPR7nlRnbiEgGwHAAO7zH4wD8HcBnlVIr/Q5KqY3ev60A7oZrnylAKfV7pdQUpdSUMWPGVOHp1CYz39yKXzz5VqSsy3TkPaLCMtjMaYmWRwRyqWwtTjiyLZ2h/hV7XrsJ0L3jprkjkfOctpHUz9aiZYrp1k8nzZkFjSkaXjpyrgnyJBlaSolapXmNLU30sWe9Zb651Jfd3p0rOf81Dy22LcfI2T9/zlJjZ/qCzcG1biHR8S0a5dIW81sn/dLMJup98WqieORcF+fRVibPeVgXvpfiFpa8oyL5/6Prtwzo1fmZW/LBmGFq0YcXbMb5N8/Gowvdn0tLRxYvrthuH5AQQkjNUg1xPg/AJBGZICL1AC4C8FCszUNwN3wCwCcBzFRKKREZAWA6gGlKqRf8xiKSEZHR3nUdgPMBLKrCWvdbvnD7fPzy6eWRMl8UpyQqrH0dkXOc4Fq3imQt1hJjthZtvlw+HM92uqcu1E3pDyOne+prstwwOCVEfUlbS5lRYT0fdTCWdm0SgKXyaBenvAV++g9zjeVJnqe/ATPOup3tZa0hzrk3PW8sr0tX54u5pOuz3US9/ZrHra/yttYuqwC3HzMUTYlYuJFUBUI7rAvfV8GNr19jeM8VzqeQ9sV47D3oOArrdrQBcDeBA8CX7pyPT986F53ZPHa3d+Pf7pgf8asTQgipXSr+9PQ85FcAeBzAUgD3K6UWi8i1IvIxr9kfAYwSkRUAvgXAT7d4BYCJAK6OpUxsAPC4iCwA8DrcyPsfKl3r/saa7W245sFFEQGaM0Sb0ymJlPuf3VmL+O3K6gK60FqSEi1bS4HnvFAsd9lSIjphW/8mwGpriVyHX/uHBxIhrDdYYOLELQeAPbIatLWIO72f6XCiqH1FFdTHx47ObSkv8sjnD8+vKtEiygd++myCVtWjWllE3li/O9GYxWwhtm4f+OmzwZivrtuNZ7X877oFpdCeUijc9Vzm8Vo9RWcYTfcOIfLXX+y5KS0LkNdMt7XE53nde826cg7+PGctnlq6Fbd5aVQJIYTUNplqDKKUmgFgRqzsau26E8CnDP2uB3C9Zdh3V2Nt+zPXPrIEM9/chgtOPDQo0zd/6eJcF+LBgT95x3iiZ7vuCzdka0mlJMzWAkQEeWBriYjzcLxuQ87zqFddt6TYThnV86P7wrm0qNdRBRfFPOcFTaPRcmUuL5ys0GcunuG+3BSIz7xZOnPoM8tqe49Fb6xvzQ57FF1/bxVGie3CV38PfeWuV6O9/G+KCqLqhfsa9Lbxt0noR9ezGEUx3TwGor7IjUdemy84ddT7V8+wVCwyTwghpHaoiQ2hxMwOT2Do9hDd0+1HwNMixnzfkQOB8pYxDIcJ6WJfJBwnm1eBxonYU2wHEuXDbDK6UA/XpPvMzeW+oHBUuCG0VORcz5YRiYBbxIlp82h0I6lNqBeO9eDrm/DmllbjPCZseun66eGGy0/+bk6Px+lvPGfx5QPFxee2VjcJ1ObdHZH3DxB9P+lj7OnIwn8HmTaE2vS+HnGPo9+o+m9oU/7+Aj+6iuZDj9RpN81+v1RwU64024xbduecNTjxB09AKYXX1u3C2656FM3e63PjjKW47M9hWktCCCH7HorzGsYkRtu0DYG+EE6lJLox0rvMWWwtbV3hGPoGQ79NWkTrK5o9xbFEzoufFprTNsFF85xbhHo+zBrjt9dFcZch5aNO9GAZ3Vpis7WEPX1sgUqT6LJJwngU3YRuo7CRRHhv2t1RulE/58ZH37TW+e+j5dv24oq7o9Fx283XST98uki2luLvkYLIuRYB99cSPyFUt8PE++uHcMW/hdFvlOM+9qz27Znf7+qHFqOlI4v27jxunb0anVkHL650N4/eMmsVnlyy1fzECCGE7BMozmuQO15cg/U724Poly5G27sKLSQpCW0oeUdp6QfNEWb99EH91MduTeyHfVXQV/ecm7zvQNQiYPScJ7Cn+FF73Z9rOyjHZGtxlDkFXklbS+T+Rhds5uuwXynRbxfwxQRlOXzzvterMk5/RZfBT8TEp/4WKtwU6hK/KXOj1dEy3RMeby8Io9dxb7lom0X992g8cm56j+kRd5uNpivnhDcBXhs/i05nNl8g5uNs3N0RzL1xd0ciuxUhhJDKoDivMbbu6cQ1Dy3Gl+58JfjA1D3de7Wot36Kpy9Ss3knEK/RDaHhte457+guFPBpCW0tetQ7lw/Fgy1armdu0a0sQQQ8QdYVX/i7GTHcMl18RG0thaLF0Tzekei19mj0kPqwXBPy8U137vpR0FbHHjkPn/dvn11paUX2BcXSMRbLj27bZKq0yHmSNIuBkFb2tI55R9nTQRZZo+No7+3YyUZ57XyAeH71bD4U9SnDy7OqeS9O/dFM/PY597174e/m4PO3zyvqfyeEEFI5FOc1xo69rs98c0tHGP3SBG/UhuJ/2Ib2jmzeQXeuMLrdrQl8fQzTeHr2l4hfXFMFUXGuRd8NItsdo/DApKwhWq5fOyr8FkC/jkTZc4WRc1046ehll51+JADgyDGDEfiKI9lVbNHyQpFkWkOcpZvNucBJ33PVP8IsrfGotv++LdwQaj+8yCkiwB2lCvKZ68I9fjMQ5DJ3VMGhRfqa4xlcAmub4xTccIZ+dCd4XnGLDQBs2dMJAHjW29i70bNOtXXnkHcUjvrODNyqZQwihBBSHSjOawxd6KaCyLnuOTdbUnTrSdSG4rXVxtA3hJpsLX5fwBX9gT0lH0ak9bbRa9OanGCM6Kmgxa0qrpDRxLlTOLd5Q6iy2FqK20+S2FBMyWE2tXQWHZf0PbaDmeLEf2amPQ2A7w83Y/KjB9FrfUOoX+dv2NQsaQX9lAoexB3mehal+Om0eUcF/4+Elhn/MC9Hi6YX0pBJu+1iN5/ZvEJXLo+8oxKdFEsIIaQ8qpJKkVSPzmxo6fA/aHXB3q7ZWqLiXLeehELYv9bb6laWiFD35lEqFPh5FfWL+9pBX5PN4tJtiL5HbwDMAj+r9Qs8544WqU7gOTdHw+Nt/FSRKGijRzD1KKjt2wgT1Ob7P/774K2te3Htw0uCcj06Hhf0+s2haby4HcYXxjnN1hJEwoMTfot7zhGMFZXZJluORCLnfllsc6qjUJf2RLwhu41/IBJvQAkhpPowcl4j3PPyOkz724JAIANh5Fz/cNwbybTithURzWcees51j3inbo3JhmPoQr3TGy8XOzRI94uH4tzsMzdF1HWB32WJeluztXjz6baWUtla9GwZNq+6frhMeNR6KJh+oAkxfYx/1/Jgf19rY4TKZb9Ht2Hd9sLq4LqYAFcwbAjVotdWy4sm3MN+Xp3hZiAU9U5B5NwncpOJ6M1ANqcKbgJ8so5jTH8KeNa52E3x3XPXYfy06dZDwQghhCSHkfMa4coHFgIAzjxmDAD3AzyMnJsPEOqw2FqCvOSOE3ygRyPnWiQ+q0fiw4h11tHH02wy3gd8RJBbMrf41/m8OXIetbVo17nQytIzW4s5an37i2uC6ycWbwmug0im5aignkrsH5QS76TmsW0I1a0r8XeNyboS9isU9SkvRJJXJluLb10JLVWByC6SrSXIqhT5BsifL7S12KL/2bwK9lLEn0tXzkEmHVXzNz7q2lv2duZwwOB6EEII6TmMnNcYu9qzAKI5vnVh3WaxteS07CrBh6ruOc/qG0I1K0t3oa0lYmXR/OJ5pWdrKTwMCTAfSORmfHEK6m2R8y6DrcV6eJHFc246WVTnjQ0tWnv/IipmfHp6sqJpLLJ/YfsZqmIRcFUodvUIuC3NoilbS2BrcZwiwr3wtlL3nMctK/43ctm8+dwCwLVvxTeC63aYyLkK0KLxjJwTQkjFUJzXGLva3WwtIuEHXZdlM6cvsnPa18w53daiRdFtkfMOQ7kb6Q6tMX5UO69tDtWj5dHIeaFQd1S4ji7DhtGCMbK+9z0UDbpIis5tEtPaJs+C2igC3daiIp5+QvKGk3eBaE7y0Gbi+7DthxDpN5x+k5RWV5CtRZ/Pib6pjSeLFqzf4Dn3/tX3pORizzObVxF7mbvOUNSb9noA4f8nO9u6rd86EEIIKQ7FeY3R4kXO06kw13g09aEesdZEeF4X05o9xbuOiHBbFF2LdGc1z7n/wR0V0Mk9544mspN4zoONpFqUUfeWl7K16HmmEwW9A197YSq9xGOQfoktcu7e7BazvBSLuEfL/Mh2rkgml7xTuBZ9I2lwYxCrM0fjQ5HtV5l85fq+E33M7pxjjZB3Zh105fJ413VP4uoHFxnbEEIIKQ7FeY3hR85dce5+AO7t0sW0tpkzG27g9MV01nHCa83r3WHJla6LZd/i4tpatEwreYNfvES0XG9jy9Zi8pkDodjXo5NZS2aXnFGch5aDWcubC+rjaK4WY350m32B9H9sqRQ/8ZsXsX6nm/c7fP9oeyWKWVcKBLj7r+5Vj2/UzDtOmMklthbd1hIIaU3wF4h6b8zunH7ib9zWEt6UB+kW/X75QnHuz9eZzaO10/3/5YFXN4IQQkj5UJzXGLu9yHlKJBCh7YYMLYAmzrUPSz1arotmm62l05BKURfTOct4kci5LZWi5n3vSeRcqfBE0ajn3HwdEtoK1u5oN9THWqtQVJmCoZTmA5efP/mWtW71jjYAQHNrV3BAD+BnAIqiHzTkKK0hQruIewiRea68U/itjn5Akd8vLvzdPSPRQfVDiILUobF++k1+3LbjRs7t3yj4+2L853z7C6tx7k2zzE+MEEJIAczWUgPo0V9fnDsqFKmm9IlAKKwdpWVr0WwtNhGu21p0kR14zmOpFE1Rb1uec1Mb/eRDmzg3ec6BMGKuR/ZMudR1HFWYQaMYSrsw2REYOCelOPVHM1GfTgFQxhNCfUuIUgrxd2zEghLrFxXuvliORrL1fvEouW6HCfOqh7YW054Ov67gdNRA1CurraUjmw/+v/Kfl59ytCuXDw42IoQQYoeR8z5m0cYWtGti1Le16PnKdUEesbVEouhhnnNfyNpOAm03ZGiJX/vCOuc4xhzlXTahbkirGD0VtPDgpII22nUusOjYxjB7zi37+Iz4usRkRwDCSCMhOrabtgdf22h9/+WdQpuUL7LnrNyBHXu7o3V+v7xTJJOLdopvzB+un87rd08FtpZwQ2g+9nuUy6tIStN4v2Ke872dfuQ8+ovT5tnzbn9hNVY27zX2J4QQwsh5n/Lssm343J/m4crz3haUtXS4kfNc3kHW+zRsK2FrAcJoc85RgbiNinNzrnTbyaFBjnIt53mHLWViiQ2hWVvU2+I/7zKU52y2llyhSFBFNuTF2b63O3h9dY+7zuihDYnGIgOL2SvM+xn+8fomDG+qi5SFhxApiBJj3aOLwtz78Vzm3394CUYPqffqoui2sbh3PG5R0+eL2loK05X6v/e+Dtcj7oWec/ffrlwebd3itY/S2plFXVrw/YeXYPyoQZjxjdNx+4tr8IVTJ6CxjhF1QgjxYeS8D1m93fWsPrFka1C2p9MV59m8CsRrmyVargtu3X8epC3U6m2e8+hJn4WivSsXfoDrY9ii6DrxY7+BaFRc/4CPnGSo6Qv95FPzGIVqWs/WUoqWjixmvrmtYF4d2lqICX9DqIk75qwNrh9ZsCm41r+dUQCeXroVqwxRZEe5YlZ/f2/3our+74pveenI5gtTImpRdScQ7m5duLFT4eU1O70xo4JbPyU4bqPpzjnG323A/T8i2MAeU+etnTl87x9uBpdNLZ342ysb8JPHluHPc9YEbRZtbME3733NOj4hhAwEqiLOReRcEVkmIitEZJqhvkFE7vPq54rIeK3uSq98mYick3TM/sC21i4AsU2Z2TBtYSDOtWwtehS9y2BV6dZ8pLb0iTa7S8Ti4q3D5EkH7LYWHVMaOl3oJjmkxxQR1IW8yXNe7ICYYtiWc81Di8seiww8bLm/r7j7NazxNo9e/eBivLhyBwB3I+kX75gfiG4dx1E44ftP4PX1uwvq4taVax5ajD97NwPx3ylHi5zHRf2OvV1Bu5yjIntbspp1xR8xSKWYdwrOF/DrOrq1DaGxde/pyOLB190ble6cgz2e/WX6wi14dd0uAMDNM5fjH69vwpLNewqeNyGEDBQqFucikgbwawDnATgOwMUiclys2RcB7FJKTQTwCwA/9voeB+AiAG8HcC6A34hIOuGY+y2d2Tx2tXVj/U43k8im3eYInP9h32ZIn2i77rREy3WRraNbXKLWklDsB+NZBHlPo1wmS0pBm+BApeRz2OwppVEYO6KpJx0JKYp+rzh9wWYA0Y3ecZ72vs0xkXcU5q3ZibU7CzMR5WOieXNLZ+RQMiCMgPvf0vn9fK84APxq5nIs29IazAeEor4751h/dztzDrbtcUV/KiVYvrU1qNuwK/r/3Bvejccb63fjn37zIra0dAZZb+6euxbfvPc1rNi2F125PNbvbMd1jywJ/t9pac/inpfXMc0pIaRfUg3P+UkAViilVgGAiNwLYCqAJVqbqQC+713/FcD/imt8nArgXqVUF4DVIrLCGw8JxjRS7LS82P6kgg1LPeWRBZvwnvEjsXp7G449ZBg6s3kMbshgcH0a21q7MGpwPXa0dWPN9jYcc/BQTL72SQDAO8YNBwDsas8WGz5m8wgfRE7NNHxYRjzituwK3eaod6dBzHdbrCw9PQjQtiYdf54kUXafnkbOn1pqF0SE1ApPLNkascLp5GJR9V8+vTyoyzsK//3XBUF60T0doRift3YXBjeEHwcvrdqJl+BaXhyl0NKRRat3M9GZy6MpH3rE27tzwf9hndk8bnrKnXN3exYf+kWYQnH5tlCoA9GbAwA45cang+v7528A4Pr3RwyqC7JY/XH2agxtzAS51O+euw5LN+9BzlEYM7QBF7/nMKzc3oZTjhyF1c1t2LCrPXitfvLP70DOUWiqT6E+nUZ7dw4tHVl84p1jMXf1TkxfsBn/fuZRWLChBZMPG4GNuzuQdxycfexBeGXtLjTWpTF6aAM27upAW3cO7zr8ADTVpdGVy6Mjm8ewxjrsaOtGXVqQFsEBg+rRmcujvTuPxjp3vhFN9ejozmNIYybYbN9Ul4ZINFd8W1cOg+ozaOvOYVhjXWCHyjkK+bxCJi2oS6ewtyuHpro0snkHQxoyVftMI4T0LdUQ52MBrNcebwBwsq2NUionIi0ARnnlL8X6jvWuS41ZwMKNLTjqOzPKWnwx9P/nCoV+mGO4pyzY0NLjvklIok9tolcX9iaq4Qm1ZXwwUY7W7nnknJD9m51tXfjDrFWBZUSnrTuH6Qs3B49bNXE8661mzHrLvME17yhc+cCC4PFPHlsWqT/u6seD62LnCvzh+dWRx0nOIADC9LI+rdpzW7gx/D+0ubULv5q5AkD47YTOt/+2oKAMAK6fvjS41l+ffY0I97cQQlz2+2wtInI5gMsBYOTYCfjWh44GUPifXHyLYGF9DK1BvE7v6yiF3zy7MlL/pTOORFN9Gns6chBxLSCD692X+pZZq0o+p1qgkpuOpPTWFD2NnBOyv/PMsmY8s8wssp9fvj3y2CTgTbR35zFj4ZbSDQH8/bXSp4IOb6pDS0cWm1s6E41ZCxw+chDWxWxEZxw9BieOG44ZCzdj3AGD0JXL46VV7rcNTXVpfOJdY7F5dwdyjsKaHW1Yv7MDRx80BIePHITDRg7Cq+t2Y/yoQThy9JAgN/72tm401aUxtDGDp5duQ1t3DmdMGoMxQxuQSQnSKYGjFBZv2oNjDh7qnuosgkH1aW6iJaQP+MaPe2fcaojzjQAO0x6P88pMbTaISAbAcAA7SvQtNSYAQCn1ewC/B4ApU6aor581qWfPogK+fe7bSjfyuPIjxwIAplz/FLbv7cLoIfXGDWG9RTolyDsK9emU0VZiKy+HurQYs6iUigylpDqCvZxsLYQMFOI33K+s3dVrc932uSn4wu3zAbi/93f/2ym4+A/ul6R3X3YyPvqr2UHbS04+HHfNXQcAOHLMYKxqbgvq7vziSdi+twuTDhyK22avxtEHD8VhBwzCim1uhpt3jBuORk/MHjVmCBQUBtVHP9ZueW4ljhg1GO8/egxSKaC9+1fP1AAAIABJREFUK49BDWk0ZNJVORjp/334mIr62/jm2Uf3yriEkOrxjV4atxrifB6ASSIyAa6AvgjAp2NtHgJwKYA5AD4JYKZSSonIQwDuFpGfAzgUwCQAL8N1kZQac7/moyccjDvmrMUZR4/BA6+69x2ZlJTlrY5Tn0kF0ROb0B1Ul0ZrVw6NdaEI10WzXt7jdaRTyOYLbTH16ZQ17SIA1JWoT0rOUb1uGSKE2Dn6oKHBtVLAKC9HO4CCHPDXTT0eJ4wdjrmrd+LnF56ICVe61sR53z0bY7QzBn7+L5N7tJYvvf+oyGNdjPPEUkJILVJxthalVA7AFQAeB7AUwP1KqcUicq2IfMxr9kcAo7wNn98CMM3ruxjA/XA3ej4G4KtKqbxtzErXWktcdf5xeOpb78cpR44KykYMqi9oV58Jf0QNmcIfV1o7vrJRq9cP9cjoberd8qb6sH6Q1lYv1z33+tx1aTG2Ma1Zb2tav61fEsptTwipHiMHh/9fTRg9OFI3tCEqwEcNjorzey8/BQBw8UmHIZUSXHTS4fjFv0yGiGDsiCZc8YGJEWFOCCEDiap4zpVSMwDMiJVdrV13AviUpe8NAG5IMmZ/IpNOYeKBQyLHWA9pSGP7XmBQfTpIcTikIYOdOdf2Mqg+ja6cg8a6VJBNpakuHaRka6pPBz7SprpwjKb6dLCJqskT4k0RQZ5Bm9fW/UrYTYXWmEkHG0Mb69JBVLsxk0Y2n4u00aP2bjQqG1z7bRvq0oC3DtO3BPXpqKg3WWPi7emzJCQ5QxsyQdaVSjn3+INxt2dHSacEH598KP7h5TEf3BCNSOuBhyENGZxy5Cgsv+E81KULb7BfmPbBqqyPEEL2Vxh67GNGaF/x+l+xDm0M75kG6RFuz0upC2s9Qm4rH6x5MP02kfoG/Wve8C2hz91Yp0XlDeUN2oesHtGOXFvalGobaZMujMqnmD2MkIBLTj7cWvfrS95V9niXvvcIAMB/nXMMFnz/w0H5tPPehuPHDgMArNi2Fz+7MLSdZNIpfOSEgwEAZ73twMg3fH6mK5MwJ4QQ0g+ytezv6BElX+gObazDVu8gjyFa7mH/elB9JsgrHBHNFnuKL7LTKUFdRgrbWvo11acBb2+W7s0snDPrCmvvwMEktpb6TCqI7PubUOti4r3NkIO9Li3wi/329ZmUMS87IQORay54Ozqy+WAvi85JE0bi4pMOx+feNx7n3DQrUnfjP52AZVtacfuLawC4/9+cdeyB+MHU4/G5UyfgiJGDkNJE9pD6DG78xDtwwf+6mzvTsbvk31zybqzY1opDhjcFcx8+clA1nyohhPRLKM77GN236QtgXZDrB4M0GfzijZZouS6gB3mR8bq0IJ0KBa2fuUWPkOtC3RY519v4grvB4o0vFUXPpASpFIC8va1ucanLpOCr8/pg7jTFORlQ/OOrp+Ljv37BWFefSeHnF07GdVOPx9uvcXOQf/vcYzB7+XY01qVx4z+dYOx38UmHQ3kHDl045TC896hwP4zuKR/WmMGezhxSKcEJ44bjL19+L44/dHiwrl1tYfapiQeGG0Pv/9J7e/6ECSFkAEFx3seMHlKPE8YOx3uPGoWlm/cAMNtaRELRaxPTtmvfDlOXSqHOi27Vp3VxXmh7AVwvuk80ch6K/YwnsvUbA5s9xSTa0yn3ND13PJs4D7O/ZFLR8nhbQvoLj3ztNJx/82xj3fhRhRHo+D4O/cb+i6dNwFfOnFhyThHBL0pkRXnyW+/H9r1dweP3jB8ZXE8+bETJOQghhBSH4ryPERE8dMWpEBF84fZ5AIBhjaEP3Y+iC0Ix2qBFvW2WlEZDBLwuk0Im7QthQSYl6EZM7FuyuOjCOhTnqUBY273lxUV7JiVIlRjDfd6+BUaCfn6mmDqazsl+ysQDhwQ5u+McOWawsRyI/n77PPOfZ1rToJr2cIwYVFdw+mYSDhrWiIOGNZbdjxBCSDIozmsAf4OULzH1yLke/fKFdX0mhYwnzvXNnINKiPNMSoLIsy6sS/WLlzfp4twTxomsLIbrTDoVnOZZr/nTdVGvR9TrDBH3DDeWkf2ERT84B8d7VpP/OucYfPSEQ3DmT5+NtPnLl9+LRRtbIjfePjP/3/uxpzOHhkwKo4fU4xtnTcJVD7pZZg8a1ljwLZJ/sqUYcp5O//rp2LS7A21dOeNchBBC+gaK8xoi74nUAzQf+uDA1iKBSK1Lu+K8C1HxHvGq1+tRb/e6Xo+cZ1JIe9dNFltLY705cu5fu7aWUCD70fzIIR8RW0thFD2TkuCwJJutRRftfpu0d5S1PwYhtch3P3IsbpixNHis/45+9QMTsW1PeIT9ieOG440NLXj34QdErCI6R44ZElzP/96HACAQ5/rma58HvvI+bNXm0Bk7ogljRzSV8WwIIYTsCyjOawj/eO2RWgYXXXz7H7716VSQNUFPk6h7x/1+IghsI4116SBy3mCJnFttLd6G0Pq0bo0JI+d1aTeSnYeKHliUsWRr0UQ2vG/iIwcuGaLl/px+Pz8aGM8SQUit8Kkp4zB18qE46YdPG+v19/yDV5xmHee9R47CnFU7jHUPX3EaXli53RgdHz2kAaOH8DAfQgjZn6A4ryFyeUPkXPOcZ7RMK16QPRY5Txf0c60sofUkkwqFteMNMjiJrcWLemfSoUc8k45u5vS1QfRUU4vnPBN+C6A8dV5sQ2hQrnnO/WKKc9LXnHH0GMx6qzl4PLQxExz8VWzDcpLNzIePHIQ/ff49wWFjcU4YNxwnjBte5ooJIYTUKhTnNUTOcUXqyMHhhtDBWraWIDtJOhVE2QcbBDkQCuuUSGBfaYjYWiQYw2ZriWRr8SLnaZGIwE9r136eCFOEPH6tR8BThsi5TZyH/VLGLC+E9AUTRg2CnzV8xtdPx2OLNuNXM1egqT4Ngf3m0XbYls/z3/4AhjXVobEubdwESgghpP9BcV5DHDFqMOat2RUc2gG4BxIBvuc83BDq+9NNVhYgtLukI5HzdMS37nu9o6kZzSeE+hFwEQSWmrpUKPYzKQk2dkZtLVoUXRtb94vnfJGtRcDrDdFyf91uf9DWQmqGyYePwFNLt+HCKYfhuEOH4dhDhuJrZ02K3Dge5WVfufnidwZR9lKbmQ/joT2EEDLgoDivIa6bejzOPvYgHHvIsKDMz9zSoHm96zMpOJ6yjlpZCr3j+sbJhrow0q1H33UxPai+MPru9/XHCyLnmfBQozrNamNNq5gOx/P1dCYtkJz7QD99UBfyusAJbwZSxg2hR44ejFXb20BINbnoPYfhrGMPwmV/ng/AzZrywZ89BwB46lvvx8QDh+AT7xwXtNdvpgFg1n99ACO8b8QuOPFQXHDioUHd1z84EadOHL0vngYhhJD9AIrzGqKpPo1zjz84UhamHJSIrcUxRM71a/2QH91zXqdt5gzEeZ05ut1kiJxHs6Sk4OuPupQEtpYkqRRTQdQ7hbTna8lYIuf6mvSNpH5zPXLOKDrpDW74xAm4ZdbK4LGeNeUIw4FAcQ4v0uZbHz6mssURQgjpV9CsW+P4kfO8o8JDiOpCS4oeLdfTtGW0qLIf3W7IpAMrSF06FXjc9Yh2kzXPuTuGiESi7/7YGe2GIZJK0ZCCEYjaWkKRbUulaPGqGzznzHlOKuHikw43lgtg9Y7zdpAQQkg1oZKpcUYNdtOgZfNK2wSqb9Q0bwj1xW88cu5bT+oyYX5xXQg3Rfzn4Xh+tpa06If/RDeH+iSKnGvrC6/D5x0V+NqBRPohRNoYwRyGXM+EJOWYg4YYy0WA0yeZrScpQwpDQgghpKfQ1lKj3Hv5KXAchYOHN6KpLo1vn3sMNuzqAIBAHAPRaLTuP/fTLqYl6jn3BX69RUzbIud+m5SEEe66dHiQUV1aQs+5JUNLg8HWUqenZrREznVby2DttFO/n+5kYeSc9AYigkOGu0fWjxhUF6vrixURQgjpr1DJ1CinHDkK75s4Go11abxxzYfx+VMnBIK1O+cE7YY1hkJB95wHUeV01CPe5fXV2zZYIue6OPfHSKUksjHVFDmPeMRtthbNL54ORLYY+5luJPSIu96PnnNSCUcdGEbOH/3G6ZE6/33m34T6mA7/IYQQQnoKxfl+gC9IJ3qb0E48bERQp0fx9NNCfdGsR84zKQnEue5V14VwY11hxhcgapPxRUq9dspoJq1bS2ziXMvWot0w+NpGH6PBEjlPaekT/ea6OCqVN5oQG6t++BGMGerayI45aGgkaxIQRsj9lKGXnGz2pxNCCCGVUJGSEZGRIvKkiCz3/j3A0u5Sr81yEbnUKxskItNF5E0RWSwiP9Laf05EmkXkde/Pv1Wyzv7CJ945Fo987TSccfQYXPSewwAABw5tDOp1cetHvfNamC+dliDqrotwmw0lEonXotthtDyMXttOAm2weM51UZ8yRM6jBxnpKRgLbxKGNWo3JfScEwCfOeWIRO2e+tYZwbVIuOnTiYfHEdb5NddNPR7LbzivsoUSQgghMSoNM04D8LRSahKAp73HEURkJIBrAJwM4CQA12gi/qdKqbcBeCeAU0VE/6S7Tyk12ftza4Xr7BekUoLjx7rHdF879Xi88r2zI4JXF6ZDPMHalXUinlg/cm7LohKJgBuyq6T0Q4jSYb71BkuE3BQtd8fRs8kU5iuPRvP1yDmCtsGNgVafoa2FABg5uL5km1GD6zHxwKHBY5Ewc1ChNEeYlsWrTKWEp9MSQgipOpV+skwFcId3fQeAjxvanAPgSaXUTqXULgBPAjhXKdWulHoGAJRS3QBeBTDO0J8YqM+kMGqI+xX8l95/JL542gTUeRsqpxxxQJC5JaN5uh1HoSubD/r7RKLeabPQNWV/yaQFOYM4r8+E/XRhrW9kjWRrkWhZ4XiFWV5SWtYY3WeubyolA5c9nVlr3ZGj3ZM686bouBSJnPO+jxBCyD6g0mwtBymlNnvXWwAcZGgzFsB67fEGryxAREYAuADAL7XifxaRMwC8BeA/lFL6GHrfywFcDgCHHz4wPaBXnndscP3kf5yBsQc0oa3LFeEfOu6gIEqdc1TgI7fZUEzpGPXrlOZhT4kEudJ1m4yerzxa7vYTCdMmZiw3A1avum6H8cfQ5qvLUJwPFMYMbUBza5exzpbecOrkQ3HV+cdhyvVPIZ8vFOBB5NwYOvfqyl4pIYQQkpySSkZEnhKRRYY/U/V2yt0lVfbnlohkANwD4FdKqVVe8cMAxiul3gE30n6Hrb9S6vdKqSlKqSljxowpd/p+x6SDhmJQfQZjhjbgmf88E1edf1ywye3og4biuqnH4wunTsCJ44YHffTNoRGLi8GGoovzdEqQzRdGzvV+kci5Lvb9VIopCSKY0Xzl5jXpBxalDJHzugFoa5l0oDk3d38g/uPUN0O3tNuj47asPb+86J3B+6ncyLn/vh53QFPxRRNCCCEVUDJyrpQ621YnIltF5BCl1GYROQTANkOzjQDO1B6PA/Cs9vj3AJYrpW7S5tyh1d8K4Cel1kkKmeB9fX/BiYdiSEMGZx17IEQEV19wHADg2+ceg588tgxDtXSMeuaTqF3ET6UY3Rzq501vMETIgWjU2w+SizZPJp1CziDwS2Vr0fOcmyL8A4n+HMkVkSCMPXZEU+TGryGTQnfeMfar0/ZO/MuUw7B0yx4s2NACIPymxbdk6cQj5wcMqsMu7yZgUH0Gv/vXd+FdRxj3vRNCCCFVoVJby0MALgXwI+/fBw1tHgfwQ20T6IcBXAkAInI9gOEAItlYfMHvPfwYgKUVrnNAk04Jzj6u0HH0lTMn4itnTgTgHlu+ZJMrXs4+9iAcOWaw0S6iVOi9dW0thcK6zpIS0RfTokXf66y+9eIpGOMR/Pg6BxKqmAejH/HCtA/iwlvmBI+v/8Tx+Ma9rxvbvlsT0D/+5DuwpzOLTbu9Q7xS4R6MOKlY5HzGN07H8q17g/pzjz+kwmdBCCGEFKdScf4jAPeLyBcBrAVwIQCIyBQAX1ZK/ZtSaqeIXAdgntfnWq9sHIDvAngTwKteJPV/vcwsXxeRjwHIAdgJ4HMVrpOU4MZ/OiG4vvXSKQCihx35J3Nm804gXNIpIJcvzP5i85z7Yl8ALUNLKhij3irOC20tKYlG0X0Goq3FoDH7DfEbD/9nfde/nYz3HTXKKs79FKP+BudhjXUYdnBdZIzPvW98QT+JRc4PGd6EQ4bTxkIIIWTfUZE49+wnZxnK50OLhiulbgNwW6zNBoTJyeL9r4QXXSd9hy56/Y2bmVQKvpMgGjlPG/tF0iD6thZNWKdT4RhJxHk6iL7r/vPCdVaLg4Y1YOse86bDWsHkj+4vpEQiz8//WWfzjvVkzge/eiomej5802uTSglW/fAjgRB/28FD8eaWVgDFPeeEEELIvmDgeQBIYlIpwUHDGvDNsydheJMbdTx90ugwuogwcq5bWaInfWqec19YI0yfWJeWIEoZzY+ue84LbS3plAReaz1FYzl5zoc0lL43vfWz70k8XiWcOnFUj/v2NyE5dkQYqU7Ffp6BJaXIc5500JDIe9REKiWBEL/v8vdi+tdPAwAcMqwRnznlCNz2uX3zcyeEEELiVGprIf2cud8J9wM/+o3TMenAIbjthdUAXM+uvyHUlmu8wXCQEUSztaRTgdDSc6zbhLrud/f1WVq7GSjnUJhB9Wns7coBAM55+0F4fPHWgjb7wwZTx7wncr9FF97xV9+/EbPsA/X6iNau9I3L8EF1GD7IzV6USgmu+/jx5S2YEEIIqSKMnJPEHHvIMGTSKQxpcKPogxoygSVFF+SD6kNhrYtlX+jm8k5ga6lLhbYFm5UlMkbEDuN51fVc6enyxLQ/z1FjwnSEUycfGlxva+0sa7xyeIeWznJ/YlhjeE//zbMnVX38Yl8E+O+huOh+/eoPBdci+8dNFSGEEGKC4pyUzbnHH4xLTj4cXzx1QiCkdFHcpG8C1cpDS0K40S+VkmBDo81zbsq3nk5JsGE1ctBRmcc4fu2DE4M1+eg3F/vKMlLuNHp0d1/bWnSb0UdPOARXfGBiVcePRM4FeN9Ro/DvZx4FoFCcX33+cbjnslMwYlB90CetWVa+fe4xVV0bIYQQ0tvQ1kLKZuTgetzwCTe7y88uPBF/nL06chBOkyVyrp/a2JVzgnpliJzXGyLuQGhrSWs51iNty4icOwp4colrZbnjxTXaHOEYR46uzQN+Tho/MrjWb050Jh44BCu27TXWVcI9l52Cs3/+HIDoa/X5U8fjTy+sqXj8uBPl7stOCa59D7p/gNAXTptQ0N9/z6350UcrXgshhBCyr2HknFTEsYcMw08/dSIy6VSQms5qSfGE1RGjBqEzmwfgbsp0/j97bx4n11Xeef+e2rfeW2q1Wi1rt7zgVdiAcVjMZjaTDBAnhCWYOCEkhIQsQDIJE14mJEMg5CUvec0yCQQCBJiEMMkQSMJkNQQYwDY2tmxL1tKSeu+u7trrzB91u1Vqd7dUX6lvt0vn9/noo+qqOnXvPffcc57zPL/n9ywmhJ7+brOBv5z3PdrkcW/+PB459yE9li/pxEyDtlIIzkc6k+e8oz+rX3ruPr3ywLZz/t1zhXOnq002O79/+Nqhs7dtSnV87yuuWvY7a0Xs2LM5p44gmTYaMY3lG2o2o7MXStXm9LX1NnnEpdNRlOU0yj08PDw8PNoB3jj3uGD4rZdcrofefavMTMO9aW3qSC7r9U5EIyoHVUFj0dOc85UqhDZTVRa875EmrnqzfOJSdY+zYTlGyFJmzJtv2atLt3S29LvnAjMtSwnZN9Bx1rbOScO9DcN+Uy61+P6lTW1bZPi0hGalnK/e3ygM/KXvjazcYBV0pM4M4C3Y3S++alCf+emnnvHZwlhYrrrnuWxqPDw8PDw8Njq8ce5xwWBmi57yr/3ys3T328+UwK9UAyM8Hln0gFZrbtELmohGz/itBSyX3HcmreXxUoqXnoOBK10YvvZHX3tAL7hiS8vtmq/KrSD69+ZbWku4bDbIbc185805A623vWX/5jP+/tZvPPeMvxc4/z9+43YN92bO+OwpuxuSk1s6U1qK33/F1Tr47ltbPyEPDw8PD48NBG+ce6wJohFbNKpf89RL9OHXHFikjiRj0UUqSqVWX/ReN3POm9GsBNMs3bjoOY88njoz0PV44205lKqP1+Rb1pu+QvvXPW2HbrlsQH/86uvPeP+Zl2464+9fOIuR3XwNzQb2FVs7deVQw2vfrJJC9hTXDHe33mgFLBw+AtzzXZn44uvLBzvPuO+ffMONZ+icL8WPXDukL/380/X0vf2P+ywSsQtehMrDw8PDwyNs+JXMY83x27ddqedePqCrtnXpyTt69F9eesXiZ80VIFcyzpuTPBe+29yumXN+KuA9/9ODo+d0bssZ58t56pczBiXp5def5qI/u8kjvKMve8b3XhZQLhY45gtYsG23dqcWvf7NRzedNsQv37o8tabZ69583LWktSz0fUMZ5fT7H7j9mrO2TaxiQN+0p3/xapbz/MeiEV059MSUoPTw8PDw8DgXeOPcIzSk4lH9xc88TVcOdS3SEvo7kstKKTZjsbLoElttwYvenHR6/8jMWc/j116wf/H1Ndsa3uTOVEw/emBYks6Q5VvAvoEO/clPnq4a+Z4feZIe/Z0XnmEoNleVfNut+x9H35CWVDBdckE/edOOZc93wTh/262XnfH+csbre1959bK/8bhjr4C+7OOvfTks3LOlnvMrtp7uj/etcC4vvuq0jvxyG4hFyoyXKvfw8PDwuAjhjXOPdcEdT9+p//fHrtVLrhpc9Div5FFdVGhpsuSc3KKB2OzpPhe6R7O3+2M/+WTt2ZzT/3zzzRrsXp0Ks8CDv3lvv26/YfsZvPgFXB0UFkrFo/poYKy/9bn7ms777Fj6swte6njUtH9LR/A7y/9SLhnTzYGXf0GP/L2vuFrv/9GrNbgKXaRlNPW9W6JLvoAfue50P2/uSJ4+x9TqCq5PDmQi+3LJVb/n4eHh4eHRjvDGuce6IBaN6CVXb5WZ6fdfebX++VeftQrnPFBosdPFZZyTvvHohKQzqSkvfNLZEzObjflcMqav/tIzHpd4uBwWWq22AfjkTz1F//tXnrn496H3vEg/f8veM3zcv/L8RmGcpRzys20sTHbGhmDhMpyT/uebn65PveFGSdIfveo6feKOG9QbcLt7s3H98LXbzin5dSUqTO8Sj/rC5iBiZ573QvOlG60Pv+bA4utENLJ4rssd763Pu1R/+ws3a8/mjakx7+Hh4eHhsZbwxrnHuiMVjy4axx+4/ZrFapD7t3To5ddvW5RHNNMZXtoFHJ8qLL7etalh0O3qzz7uewtotYroAhaareS1lhrG/iV9Kx9b0hlUmIXLMZNe89Qd2tGX0cuuGdIvPqfhbR/sSp/m2S95Wj/y2ifr9Tft1CV9GV2xtUtP29PwmHem4rp575kJqdJpbfAP/vi1q57fguHcjKXKOwvnvVS6cmHzsNSYv3q4W7s2ZRevY8F7vhw1JxoxXTZ44aUrPTw8PDw8ngjwFUI9NhRuu2ZItwWv/9dbfkiSNFusSGoYbVsCFZZrt3frT/6t8b1c8vQwXvCyr+YjtnPYkv7K8y9VeknlzQWPe4zoBwZYyXltMm3vy+hrv/IsSdLPPXuPnnP5Zl2xtUs/+uTteteXvq/BzjNpKXs25/SbL7n8nI/94zdu19/ee0LXX9Kj/Vs69MCJWT338gHd/fC4ZkvVxTNZMPKbsTSqsZgQanZGXy+Y2qvtfyJmj+sHX83Tw8PDw8OjAW+ce2x4LBjDV2/r1oueNKjhN2V09XC3rt7Wrd/84n265bLTiZcLyi3LedgXcC6e8zctUxzoxp19+uln7NIdNz2+ZPzZsNIh3QqfRyO2mFx5x9N36vU37TiD0kKkFG/eu2nRCP7QT1yvj/3Lo3rnS6/QM/7bPzYZ5+eGZinF5r6ONNGOlsKW/C+traKMh4eHh4fHExHeOPfY8Egnovr8G5+mfQM5mZmuDvS6d/Rn9fHX33DGdxcM+aUFJK/a1qXvHZ2WtLxUoiS9/uk79dj4vN5w8/LGdzRievsSxZRW4eTOLD7URGtZDQuG+cuu2ar7R2YWIwgUO/uzetfLrjzjHM7lPBZwmtZyZpRitfbNm4vZYmMzsHBPPDw8PDw8PBo4L865mfWa2VfM7KHg/54Vvvfa4DsPmdlrm97/mpn9wMy+E/zbHLyfNLPPmNlBM/u6me04n/P0eOLj+kt61JGKn/V7C6Xgd29amfe9kgHZmYrrfT96jTrP4TitYoFb7dxpikguGVuVv74c7vyhXXrgXS9Q/zkomSx43jd3rG7IN2uvt+rIji6hqKzGy19MqJX08Gi+xSN5eHh4eHhcHDjfhNC3Sfp759xeSX8f/H0GzKxX0m9JulHSDZJ+a4kR/yrn3DXBv1PBe3dImnTO7ZH0fkm/e57n6XGRYKAzpU/ccYP+8MfOTHps9pbThNDzwQLdpisd1407e/WOF+7X7/zIk5q+cW7nZGZKLeHCr4S3PGev/vJNN521aE9Xmm9GohHTmdLtZ78O505LK66WuOvh4eHh4XEx4nyN89sk/Wnw+k8lvWyZ7zxf0leccxPOuUlJX5H0ghZ+93OSbrFzWfU9PNTgVi/1sv/EjZcsvl6J1rKW2Nqd1jtfcrk+8toDMjPd+UO71Z1JIO74uSIWjeiagAK0Gs5FYnEpXv2URn8204z+6w8/6Qx5x9WP2fh//2BHy8f28PDw8PBoZ5yvcT7gnBsJXp+QNLDMd4YkHWn6+2jw3gL+e0Bp+c9NBvhiG+dcVdK0pL7zPFePixjNRuB67fNed9NODXYtXwhoPbeeB4KiP62cx2/fdoUeevetZ7y3pSu5rDTi0t92cqrWG9r0SyuMenh4eHh4XOw4a0KomX1V0nKVXX69+Q9pFFWcAAAgAElEQVTnnDOzVl1wr3LOHTOzDkmfl/RqSR9v5QfM7E5Jd0rS9u3bWzy8x8WC1YzG9cQaOs7PGXfevEvXDHfr9rvuPuc2ZqZ49DSPfuG90wZ4Ay+6alBjs6XG5028+4XoRSZxbhQdDw8PDw+PiwVnNc6dc89Z6TMzO2lmg865ETMblHRqma8dk/TMpr+3Sfpa8NvHgv9nzexTanDSPx60GZZ01Mxikrokja9wfndJukuSDhw4sBFsHY8NiAXO90bDM4JiQa+4fttZvrl2iERMl/Q1ikAt3cT8zDN263lXNAJi127v1nDP4yupLtBiTI9nzv/Rj1+3+NqaKC8vuGKL3vjM3fqZZ+y+MBfh4eHh4eHRJjhfWssXJS2or7xW0l8t850vS3qemfUEiaDPk/RlM4uZWb8kmVlc0osl3bvM775c0j+41YSrPTyWweff+DQ957KGYTnUvTydZL2xvS+jQ+95ka7dvqzQ0brjbbfu13XBuf2Pn73pcYm2zWhFhz0WjejXXrD/vJJRPTw8PDw82hHnq3P+HkmfNbM7JB2W9EpJMrMDkn7GOfcG59yEmb1L0n8EbX47eC+rhpEelxSV9FVJHw6+81FJnzCzg5ImJN1+nufpcRHi+kt69JHXHljv03hC4Hy2vpdv7dQ/PzSmTbnkqqIzC8Z7q/KRHh4eHh4eFxPOyzh3zo1LumWZ978p6Q1Nf39M0seWfGdO0vUr/G5R0ivO59w8PM4Gz3d+PEh+5q8871LdeuWgLt/aqVMzxRW/t28gp/tHZpRN+NpnHh4eHh4eK8Gvkh4XDbZ0pnQiMB4/+YYbtcNrbC9iwSgn6inNko21wAUfXYYw954fuUqvPDDs+93Dw8PDw2MVeOPc46LB3/7CzYuEipv29K/ruWw0bOlM6Y3P3K3/dN35JaYu0GPiy1jn6UTU97uHh4eHh8dZ4I1zj4sGPdnEep/ChoWZ6ddesP+8f2ewK6U7f2iXXnlg+AKclYeHh4eHx8UHb5x7eHg8Dh973QHt6GudfmJmescLL1uDM/Lw8PDw8Lg44I1zDw+Px+HZ+5cr9uvh4eHh4eGx1jhfnXMPDw8PDw8PDw8PjwsE7zn3aAvc/fZbVK7W1/s0PDw8PDw8PDzOC94492gLbOlKrfcpeHh4eHh4eHicNzytxcPDw8PDw8PDw2ODwBvnHh4eHh4eHh4eHhsE3jj38PDw8PDw8PDw2CDwxrmHh4eHh4eHh4fHBoE3zj08PDw8PDw8PDw2CLxx7uHh4eHh4eHh4bFB4I1zDw8PDw8PDw8Pjw0Cc86t9zlcMJjZrKQfrPd5tBH6JY2t90m0CXxfXlj4/ryw8P15YeH788LB9+WFhe/PC4tLnXMdF/pH260I0Q+ccwfW+yTaBWb2Td+fFwa+Ly8sfH9eWPj+vLDw/Xnh4PvywsL354WFmX1zLX7X01o8PDw8PDw8PDw8Ngi8ce7h4eHh4eHh4eGxQdBuxvld630CbQbfnxcOvi8vLHx/Xlj4/ryw8P154eD78sLC9+eFxZr0Z1slhHp4eHh4eHh4eHg8kdFunnMPDw8PDw8PDw+PJyzaxjg3sxeY2Q/M7KCZvW29z2cjwsyGzewfzez7Znafmf1C8H6vmX3FzB4K/u8J3jcz+8OgT79nZtc1/dZrg+8/ZGavXa9rWm+YWdTM/o+ZfSn4e6eZfT3os8+YWSJ4Pxn8fTD4fEfTb7w9eP8HZvb89bmS9YeZdZvZ58zsATO738ye6scmh5n9YvCc32tmf25mKT8+zx1m9jEzO2Vm9za9d8HGo5ldb2b3BG3+0Mws3CsMFyv0538Lnvfvmdn/MLPups+WHXcrrfUrje12xHJ92fTZW83MmVl/8Lcfm2fBSv1pZj8fjM/7zOz3mt5f+7HpnHvC/5MUlfSwpF2SEpK+K+ny9T6vjfZP0qCk64LXHZIelHS5pN+T9Lbg/bdJ+t3g9Qsl/a0kk/QUSV8P3u+V9Ejwf0/wume9r2+d+vSXJH1K0peCvz8r6fbg9R9LemPw+mcl/XHw+nZJnwleXx6M16SkncE4jq73da1TX/6ppDcErxOSuv3YxH05JOlRSeng789Kep0fny314Q9Juk7SvU3vXbDxKOkbwXctaHvrel/zOvTn8yTFgte/29Sfy447rbLWrzS22/Hfcn0ZvD8s6cuSDkvq92PzvMbmsyR9VVIy+HtzmGOzXTznN0g66Jx7xDlXlvRpSbet8zltODjnRpxz3w5ez0q6X41F/DY1DCMF/78seH2bpI+7Bu6W1G1mg5KeL+krzrkJ59ykpK9IekGIl7IhYGbbJL1I0keCv03SsyV9LvjK0r5c6OPPSbol+P5tkj7tnCs55x6VdFCN8XxRwcy61JggPypJzrmyc25KfmyeD2KS0mYWk5SRNCI/Ps8Zzrl/kjSx5O0LMh6Dzzqdc3e7xor98abfakss15/Oub9zzlWDP++WtC14vdK4W3atP8vc23ZYYWxK0vsl/aqk5mRCPzbPghX6842S3uOcKwXfORW8H8rYbBfjfEjSkaa/jwbveayAIGx9raSvSxpwzo0EH52QNBC8XqlffX838AdqTIT14O8+SVNNi01zvyz2WfD5dPB935cN7JQ0Kum/W4Mm9BEzy8qPTQTn3DFJ75X0mBpG+bSkb8mPz/PFhRqPQ8Hrpe9fzHi9Gl5aqfX+XG3uvShgZrdJOuac++6Sj/zYZNgn6eaAjvK/zezJwfuhjM12Mc49WoCZ5SR9XtJbnHMzzZ8FO2Uv4XMWmNmLJZ1yzn1rvc+lTRBTI6z4IefctZLm1KANLMKPzXNHwIW+TY1Nz1ZJWV28EYQ1gR+PFw5m9uuSqpI+ud7n8kSEmWUkvUPSb673ubQRYmpQfp4i6VckfTZM7n27GOfH1OBaLWBb8J7HEphZXA3D/JPOuS8Eb58MQlkK/l8I36zUr76/pZskvdTMDqkRvnq2pA+oETKMBd9p7pfFPgs+75I0Lt+XCzgq6ahz7uvB359Tw1j3Y5PhOZIedc6NOucqkr6gxpj14/P8cKHG4zGdpnA0v3/RwcxeJ+nFkl4VbHik1vtzXCuP7YsBu9XYiH83WJO2Sfq2mW2RH5sURyV9IaADfUONCHm/Qhqb7WKc/4ekvUFGbEKNhKYvrvM5bTgEu76PSrrfOfe+po++KGkhU/u1kv6q6f3XBNneT5E0HYR0vyzpeWbWE3jonhe8d9HAOfd259w259wONcbbPzjnXiXpHyW9PPja0r5c6OOXB993wfu3W0MtY6ekvWok41xUcM6dkHTEzC4N3rpF0vflxybFY5KeYmaZ4Llf6E8/Ps8PF2Q8Bp/NmNlTgvvzmqbfumhgZi9Qgxr4UufcfNNHK427Zdf6YKyuNLbbHs65e5xzm51zO4I16aga4g8n5McmxV+qkRQqM9unRpLnmMIam2fLGH2i/FMjI/lBNbJlf329z2cj/pP0dDXCsN+T9J3g3wvV4ET9vaSH1MhO7g2+b5L+KOjTeyQdaPqt16uRCHFQ0k+u97Wtc78+U6fVWnYFD+pBSX+h05neqeDvg8Hnu5ra/3rQxz9Qm2fFn6Ufr5H0zWB8/qUaCgJ+bPL+/C+SHpB0r6RPqKEu4Mfnufffn6vB16+oYezccSHHo6QDwb15WNIHFRQFbNd/K/TnQTV4ugvr0R+fbdxphbV+pbHdjv+W68slnx/SabUWPzbZ2ExI+rOgH74t6dlhjk1fIdTDw8PDw8PDw8Njg6BdaC0eHh4eHh4eHh4eT3h449zDw8PDw8PDw8Njg8Ab5x4eHh4eHh4eHh4bBN449/Dw8PDw8PDw8Ngg8Ma5h4eHh4eHh4eHxwaBN849PDw8PDw8PDw8Ngi8ce7h4eHh4eHh4eGxQeCNcw8PDw8PDw8PD48NAm+ce3h4eIQIM3u6mf2bmU2b2YSZ/auZPXkNj3fIzJ6zVr/v4eHh4XFhEVvvE/Dw8PC4WGBmnZK+JOmNkj6rRonomyWV1vGcYs656kb9PQ8PD4+LDd5z7uHh4REe9kmSc+7PnXM151zBOfd3zrnvmdnrAi/6BwOv+gNmdstCQzPrMrOPmtmImR0zs//HzKJNn/+Umd1vZrNm9n0zu87MPiFpu6S/NrO8mf2qme0wM2dmd5jZY5L+wcwiZvYbZnbYzE6Z2cfNrKvpt18TfDZuZv+52RtvZu80s8+Z2Z+Z2Yyk15nZDWb272Y2FZzvB80s0fR7zsx+1sweCs73XWa2O4gozJjZZ5u/7+Hh4XExwRvnHh4eHuHhQUk1M/tTM7vVzHqWfH6jpIcl9Uv6LUlfMLPe4LM/kVSVtEfStZKeJ+kNkmRmr5D0TkmvkdQp6aWSxp1zr5b0mKSXOOdyzrnfazrWMyRdJun5kl4X/HuWpF2ScpI+GPz25ZL+P0mvkjQoqUvS0JLzvk3S5yR1S/qkpJqkXwyu46mSbpH0s0vaPF/S9ZKeIulXJd0l6SckDUu6UtKPLd+FHh4eHu0Nb5x7eHh4hATn3Iykp0tykj4sadTMvmhmA8FXTkn6A+dcxTn3GUk/kPSi4PMXSnqLc27OOXdK0vsl3R60e4Ok33PO/Ydr4KBz7vBZTuedwW8V1DC83+ece8Q5l5f0dkm3m1lM0ssl/bVz7l+cc2VJvxmcfzP+3Tn3l865ehAN+JZz7m7nXNU5d0jS/6/GZqAZv+ecm3HO3SfpXkl/Fxx/WtLfqrEB8fDw8Ljo4DnnHh4eHiHCOXe/Gl5qmdl+SX8m6Q8kfVnSMedcs+F7WNJWSZdIiksaMbOFzyKSjgSvh9XwuLeCI02vtwbHaj5uTNJA8Nnid51z82Y2vspvycz2SXqfpAOSMsFvfWtJm5NNrwvL/L3lXC/Ew8PDo53gPeceHh4e6wTn3ANq0FWuDN4asibrWw2++HE1jN+SpH7nXHfwr9M5d0XwvSOSdq90mHN4/7gaG4Dm41bVMJhHJG1b+MDM0pL6znKMD0l6QNJe51ynpHdIMnl4eHh4nBXeOPfw8PAICWa238zeambbgr+H1eBW3x18ZbOkN5tZPOCRXybpb5xzI5L+TtLvm1lnkMC528wWqCIfkfTLZna9NbDHzBaM7ZNq8MhXw59L+kUz22lmOUn/VdJnAtWVz0l6iZk9LUjSfKfObmh3SJqRlA+iA288h+7x8PDw8JA3zj08PDzCxKwaSZ9fN7M5NYzyeyW9Nfj865L2ShqT9G5JL3fOLVBIXqOG9OL3JU2qYTQPSpJz7i+C738qOMZfSlpIJP0dSb8RKKf88grn9TFJn5D0T5IelVSU9PPBb98XvP60Gl70vBrc+NXkH39Z0o8H5/JhSZ9ZvVs8PDw8PBZgZ9IbPTw8PDzWA2b2OklvcM49fb3PZTUEnvUpNSgrj673+Xh4eHi0G7zn3MPDw8NjVZjZS8wsY2ZZSe+VdI+kQ+t7Vh4eHh7tCW+ce3h4eHicDbepkTR6XA3aze3Oh109PDw81gSe1uLh4eHh4eHh4eGxQeA95x4eHh4eHh4eHh4bBN449/Dw8PDw8PDw8NggaKsKobFU1iU7es/+xSWwWuvHqtOegyyiCDhHSarHWTurwnasmRzcJmJSFj1R2I5eX6TC2vEbAZvB67M6a0cR9n0g8wTtk0iV3bx6nA0WF0XN8PXRdmETN2m/RMvsTF2E3b86PE+6FuE5Ah4vUmUDph5nJ0r7M1pq/b67GHxm4bpg1G4JeU4K/WGXNDd5dMw5t+lC/25oxnlQbOPjapSDdpLucs59wMzeKemnJI0GX32Hc+5vgjZvl3SHpJqkNzvnvrzaMZIdvdr/sl9q+dxyJ1q3ROcG2JMYK6JmisCJe3aYTTTdj7AZsdzBjpeaZMeb3BPu/jJaZu3owpSaZPe9kqazMGuGN5108oaLU6UDNVP2BFvoq8nWz7MK7x02YmrsHtTAtUl840+RnGX3rtDHHtrEDOtPavRGKux4xW52fbFiuNdndXi8EDfU0nk8t2B4xubhRg5uIOJz7BmKlOGzt4l5FfF5wjlQkv75i796GDdeBWFaNlVJb3XOfdvMOiR9y8y+Enz2fufce5u/bGaXS7pd0hWStkr6qpntc86t/Ig7KQomqmJP6yMWe7egkU2uS5Lic6gZnhCLPXRHz2YNapBkxtgNpMejxnnucAG1O3Ugi9rhzWPIHhJ6POo6ypxgu7LxK1KtN4KXRo0KK7I+ScNnqNAPjd5Z1jGlznCPR8emi8INZ5a1i0MDj3h6JSkxw3Zl+W3MUMuOsOPVkmy8UOOcbODphoxuWOIzrGE1yyYlul5mjs6jdlaBC/saIjTjPCg/PRK8njWz+yUNrdLkNkmfds6VJD1qZgcl3SDp31c8RkQqd7Q+0InBRb0H9QR8gOGDn4MTFNmwSFJqgvULuW+SlJpix5vfHC6PJj7HGs7szrADQmRPsvFS7mDjJT7PDLxyjo2X6Go1LVdBfiiJ2pFFJgo3SDFoNNGFsAbnMrqJqEGKXgU+QnG2zquSZR3a9SgbnPMDCdSOUhwSs8yQodSIOty0lHrYQKuC/bTEo6rkeUjCSPP8AOuTRAq63CFiBRgx7mbzdDVzHumX3+FNV8O6cM7NbIeka9UoVX2TpJ8zs9dI+qYa3vVJNQz3u5uaHdXqxrzMsZtKvIXzm6FxABdeykfMD7JbnJqg4Sg2yKNws0ONNDoBR+AE3Pkoazi9GxqFcC6duBRO3jPseNzrx45HczDmB9h5Zk+0Pq6p8UrnCOpZrrDgDN6o1lJwAw+pYQlIh6lk2MM3N8iMbDpeuNHLro+OM+rhr7KpM/QIDYlSFzaF65Eud7LjxefYJqKaoRFVNjap83MtEbpxHpR+/ryktzjnZszsQ5LepYZP8l2Sfl/S61v4vTsl3SlJiWwP8oITj0wMGgf04aB0g2I3a0eN7BpbX/BCQXmMmZPQ4w6NtPw21jE0hEzpIvT+ZU6xGzE3CHcR0AuHedmUkgjqSFAqWqEXcoihl6oMxxg1zitwI043qrNDrCGdk+izlx4PN6u6nINGNrzvqSnWoRXoDc0eYVTC6j4WoiGGIaVOdT/Erm1+C/RmQVDaMKdJsuOtJUI1zs0sroZh/knn3BckyTl3sunzD0v6UvDnMUnDTc23Be+dAefcXZLukqRs/7AjN5V4nBJ59nBQviXmqpcgjzGHmuFBTr0/Kch7nd7B7gNd6EuQix+H4wyHZiEnMb+VdUzXYTZgSl2QRpOHm4gtbIAS6gcdY3TBxiFkGPWgXqo6pd/AxNUa9LwadKRQFHugughMqg5b0Sk5zdolptncMj+URu3CzEOjfVncxAY1z/FhzWgknVKn4rMbzzoPU63FJH1U0v3Oufc1vT8Y8NEl6Ycl3Ru8/qKkT5nZ+9RICN0r6RurHqPOuODJqdZvTLGXdR01silHGkvxQVA6DPVy5LfCUCLlP1I5MPjsU440vT4KmoORh0avgzNXrBCuig2iYsBoCQ3/03wPuqGmG3i6caRzYJSmpcCxQqNPs8OQigbpG1QRY34z3FDDZMTpPcxD4SxcigOhtdAoRDUN1dTG2D3AMp+Qfpg9zuijkUrImr7ngDA95zdJerWke8xsgUL/Dkk/ZmbXqLHHOiTppyXJOXefmX1W0vfVUHp506pKLWrsJqtgMawNtD4SqKYpBfWmpU9Aztc8e4hpZIBONvThpwto5yFohG4Ll1sdtuIHee6k86AAwMm71BVuIjBZZCjFi+Zf0Cgg3TiWOsPljlPucRSq2ODoWjdrmDlFlafClTFNTrPznNoHPdnw+mLQgUZUVyS2WaVRgcJmNnHWoYJNYpIZy2UYGS31suuj0TVJ0r/ypqshTLWWf9Hy/qe/WaXNuyW9+1yPYTW2q6+lWh94FbjAdB+kkmyMkFjsZYO83ImaYbUWGlqnXPy5Lez+FTbBRGBG9VP2OPNYYAUHaFjQoCANWcehlBjdlFGQUHdyKtzoE873CFkysNQVLsfdqFwd7Rc4NisweY56UWmEhm6oU5NQuxp6Q7mkJZs8a8BTT51SNOpRS0AaaAzOSZA7jgt5bbx80PaqECpjN5VMGjisBB/g5DRMpoETWzkHd65wAc2AQlCSNHE5m/FxgQpovNJNxOQ+mmHLmjm4gOJcA3i8JJTQpPJ/dPNINjuUk02pPtRoSuRZO8qXpdQiyo2nhV6oZ5kmf1OvH42S0QgNve9UfjgxC41s+vzRoBxoR/skbMWcSgcbZJS/T+d3+uytJdrOOCe7XhJuK/SHKyeVHoOJbFDXlHKW6Q6URC/OBzjhlRr1VKMZytXFoKc+BqkKqXGYIA0jEVRNKD1KN6sw5wOE1innlW4c0zDaVeiFFRFxNVnWDhuhMDpK5wi6AaTXR/NEuAGEmmE6TPY484gU+5lDhNKSkIIUnSOgRzpWYPYH3ahSKloXlCyOTYacxX0OaC/j3LEdEAlDUmOLcnNriXAT57DkIzQKaeEOKj+GpfiggVCltYRCTkqjCz01sino5J0fojr87Hjp460bFjRxjhZBwco+0NjCG2N4fXQuo0Y2poZBjy2u2AkTQumznoCUCuolnhtiA5smSOPoGqCocDsCNcNJuaU+KCEMnz1akbTU08EOKLVXEaK1gosyWkV6vPU21DindIM6fKhoEhUNldIJqgi9cNRooveP3gesqw6LXVVgzkD2cWKl53g8aHBRTW+DBiW979RrS3TcKWWOtsOeZWjE0E1E2FWZKSWQegspH5hKKdJ2mH4D70PXQebxyW9nkwS9f1i9iNRmgX1JUUuHayrGYOVoHLk/n4TQNUJ7GefGOrnc0fokRQeBg8Yd9VLNQeOOhp6pnndihlZlgx4EaNzRyABVa+E8TdauAh0IlANOuYW0mBDW2KYzJbh/NRh6Lg+ydukx6vFDzfBGh9JMaJSs1A2LOtGKljChl/Zn6PcPctXnB6EkIkyapJKWdM2cG2h9A0+TjqkdQcdmtAgrjfezCTdagQvDxqOct5dxLgl1Mgn1UKOCyl7NDbKHg6quUOOHJm052JDSdihov9B2VOWl40FYnGlXuNl6uF+g95WXnGftYsCbXeyDfQnnpFm4caRVkmlxn+6H2WChRjblOhfh8QrwvtNkc0proRvVvvvYiY5dzYxz6vCxWrjJlsT+oPNRCtR0kaQKVfaBikdR2K7cwThlVMd9LdF+xjkAkRKjPE1aTChso7AOF1CKKjSaqPenxqRzQ/dSUXpKvkaLTbDjYbk66LGgBmX2RLi0q3ql9Xa54zCKBJWSyjBagvMo4AZ+djskc8NNC5XGC1uWja5FVAGsBjWvi31QWStkGhSlOGAZP0CjofMmjcpRTz1NQKXPUHKSLdARuBlYS7SVcW51biS0CgdlmijnlXoKaXJSARrLWM2EJnZCYDk3WoyG0pmg57zcw9pR5Y75gXApB9T7Og+T2ehzRGhQVMqNJnvRa6MhcqxcBDcD2ZFw8z3oXE0LctE5l/KW6TgrQ88yjWBQ3nIlzR6IjiPsRlSAt5dQcSVuLJMq6tL51FBAzSRIZSp3bzxTeOOd0XnA6my3TKoGhm1sUS4wNUITU6xd2OoiMRjey++CJai7oN78cVrxDCoxzLAOnd4VMr2Ics6pUQ8jJvFZ1q4Kj0eAZTchPWV+ED4LY9CxAT3EhX5aOZUdr9TN2tExRiMfdHNFaTRUYYTr4rM5F+uqj7IHaXp36wMmPUaTlVEzLHVMixfRsUKpTFW4cVxLtJVxLsdK9rKQRrhGDA3tUXTAh39mGA5y6L0rboaHm4OTRi/b0pf6WX/GZ2BlNtif1ECYH4LcQloiHbXiXlvK5yYGc+YEOxZ1GNANJ90gUaoWnQPphixsKmESGha0omUVVhalXHVqcNE8rQqsTUDrBeR3s10SsT8o9SY1DjnnsCghdbrR+RZXFvVFiNYW9RgrDkS8W1S1o9TF2tHj0fD/9E7IWR5nx6P9Uo9BLlyStUs/xh6ZwnbIhZsIV/+9DukwFNR71/kIu3/jT2LHo1Uma6nWz3N+C5XiQ81wVC4GqX10bBrc9+eOwOQyyOGntBbq1aQb8QhdU6ABlIEOn0Ifu8BEHlp40PFGedkkEkGNXqq6Qo9HEzupE7PcycZK7jH4MKwh2so4l4RcakQGjhZioOL6lI9YD3d+0vQe9jAmJ2kYi7UrXsqswqJByyIJlR8G2I2PT4abPOeiVD6OHY8a2XRcYz19sDmmXqPCFtYwfZwt2NQDXk+wsRKBKhoFyB2nzwIFlYqMQ/oNrTpN1W+o55xU2ZX4eWIZ4S4255LaIPECpGXCyubpcTZWUqeY0Tu1nxXOSMzCdXZTyAoY54C2Ms4jdbZbJiENF4UZ61D1IWwdTurFoRMbLoTSDUPy8ESjA2yycbBfqrOsY2pp6rGgoXXIPx5lz9HcdjYJJyapIRqeFng1BwcL5eFDLjdV9pnfytrR68MbKzoHUqllygiEKiFVKI9HawV0HGUdkweFvCSuN9/zAAtBjV3FDEricU89xHZk+S0sIYJWCHWRcAtdUV31Ujd0Zq0h2so4r0ekMuGZAQ9QYoa6GCE/kOozQw4xLs0NvWlYlg2iVmBDP5tjE3elyh7+apyNs8g8PB70aqoOIxh97HDUUMNFnSANKlpovV8oTYFGBco9cIxVoWeySnmhqFnoeQb0PtDEXEq5wkWIYPSXFNuRuMOHthu9lhnZtHgR4Y8XtsOSzBC0QmikzDheiZlwiwnRsbKWaCvj3BzbhRJPANUrpwsv5WnW+lk7ikg53IWC0loKsPQ4NbLrlLSXgln5BXaekXLIWesRavTC86SGUx5y/4FR3/UQO8mZXaiZ6jDKwqUbITd3HhbpoQpZ0CFCOfzZk6xf8iFXhsX5F5A5gGWEaTIiTeiFGttzW1t/kEqQWx0HFBqJc9WraRaWo1KRmVPsHhBq0VqjrYxzCqJyUIPemBLVnz7F2tEJkXqb6IiqdkKvA1QcSHtAjywAACAASURBVPWyFbs0zzrGoBEqyLOtQ6M+OkuLZNHzhLQkmGhUuYS5XzP3sEVmbrj185y+FB0K33OeZ8DaUYUeTH2jVZKh1nJygrWjRjbNY6JFemgFW7pJwtEumLiaO8Fu/OSlbLHtPNT6DcwPsXWIq8WFWx8Cq2NBVZnkhK8QuuYgN5U8/P33sJt54qnsoaJGNuWT0oeYPowO0jeSo6w/SznWoQY54PVOuNKXYISGcschrSU+AUteZ+CmharRlKAkWMg5GAg0CjHH+oTmNcShyguNelBHQ9+DzGEwtRtG1+AYo57lCnRshCkrKkkpWBiNFlmi6jB07Zsdbn2AYqlBquwDlWgM5kPQ5FoaLaHVa9cS7WWcG3tACJdxal+4N5NqGNNKWxW48GZGYEgehrFKm6CayQnWoZUedjyboZy9cGuB00m/4zCUtIRFKmp0EzEGIx/wOer6QevXN7UfcsDzMHFujlbVgxtquJGjRmgBUvvyWyF1AEpa4mglnCKy0ENscKdKK6723suSH0evZ/wiKhWJE7KBlHPnYbYOUbWW+Bw7Xj1GZWHZ3IKL00Hd+LVEexnnjhmjueOtD7z5zWyQ93+PDbpT18GCCjCRje9AWTuMNKTDUM5yyFKDcch1rmTD5dBN74VeOMrvnII5A9B7V9jKbrw71fr9w2Xc4UaOJlpmjkHFKjhH0POk7WgVxtltrF8SMHmfYmYH2w1QDz+VfDx5AzOyk9NUxYYalDCXgqjFweWLSiLSCqGxPDtesRcKNhxnO/hy98YzhTfeGZ0HnElVsAOKllpvQ2km+SE4cc+w49EFm3LjsdIEze+DHuk61B23TkZnqkN1mAqU1aPJc6RojiRVOll/9n+bnef0LqqLj5qpSPmrR0H1PxhNoBsduuGk0TyqSkI04yUsJKT8VnYf0uPsGaKeZVLERpLisNJnBFYkrUCBEUprobrqVJoyCRXcKiDZEioUKneQTYD5PSxxIwY9DakJFtUp9rMNZ+4xOCmtIdrKOKdqLaT8MVUq6LuXGXcTl4XLVafeJsrNrYENUqMhTC7LQEumHK4eKk20pEY2tWSszo43ux0WJoH5O9RzbrBfCKWCGtk9DzHjgFYDxpUpae4V3ETQ41FK4DwsUBeBtB0qTpCFxjmt1UGNbMqTpmtfFarRECefJJWJchhcLksDTLOYRtJLXcwgIDaZJBktWNUPk/PWEG1lnLuIVAdJIGUwudHBOrUHZlnDnXIKKgcUNrF2tOBHdA5KNeUgPxdKDcan4XnCxEfan+Ue6P2BfOAK5BbSQjalXtau9wHWL1N7wpMIpUZvqYvKnbHjUQ84dWxQ2gedO7Mn2NwycwmMjkLaRxE+C5UcG9OZUch3hlRQamTTzU4VbnYov5o879QInd/CLi4OijtKkotA5xJM5o3DwlNVSNtZS7SVcR6psEIAZCDU4+EuhJ2HYFnaPsgVg7JX1JikBkmlL9wa29TozR6mbkbWzMVggm1vuP05s4e1o4of0zvDTfaihiEBNdKokZ2aZO3KkN4Qtm711G5IL4KPEDVCqReV9svMdlgCHuqq16EXtfMwC5lM7YZRamhQdh5uPURD1UxovkB6lO10Kjl2wGqGtUuNspD/3DbvOV9T1OOs1C8Je1ZgRUsHd+W06BFVAMD66CGXvKa0FheHXG6YSIql+OBCX4VqO73fY9c3fgPjAEQnIBe/g11fAiaSUi8cGddUlYQqysQgha3UxdrR66Med6qegitTUsYcLF6EKQ5dIevNw8JvmVNsEpzbwk40Qbn4VC0XGNrxedYnNDpDjeVakq0nNLnW/uU7qF3spTegdmuJtjLOZcxTRQxRPMjhBo0uFNRzh41lKlsNQ9YFqAMegfeP9me5O1z1lFoHu4FjN7LjGdSpr2UhLSlkZRKs0gPGC332qJFdh2OaGr2Yq06NeuhowEWW6H2gnvOQE3rp5oqCUFUlvmaWuiFFbxJSF4FaSzUNnXVwTMMcdRztohvx+f/0FNQu9whUClhDtJVxHqlK6fHWZ6rCptZHXgmGZjsPh8tjTE6jZjJqvGKvCj0eLFBBiyXBSSoxCWkmgyw0G8uxdjWY8Ooq7PpoElUtEy7NKzUGvYwgckUTGKs0+RseD28iIC90foDyUFEzGdx8YE899LzSQnMUdHziKCDlnMPxSc8zAqsWk0qmVIkmOc0ujlKL4lBK0eBCS+/d7G5YRliSvs2broa2Ms7rEakMitmkgPQVzVifHwhXGSFWgMYrFOUPW1s2BnXAKeIhb7DL8PKokY3VWiiVG15fBEZMqOpKmF5bGh6nUSushgE31BH4rNN8FkpBpMejtA+aj0Q3V/TZo4pHmVE2QPv+7SRqN/K8LagdpbWUc+FRHnFkGyIKNx6iqit0Iwc3LWHmBp0r2so4N8d2r0T6CsuBwTFOF2xqZNMFG/MY4fHK/bTKHTteBVYypR5iBzn1mUeYu6kwDPsTGsuCxZlwvBSCqsMQigPVYqdGE6lQKHEjNDEa7vGqcG5JzkCpwZ5wDRK6piSnaGn1ECUDxY1sCjq1UAO2CgxY6qXHTj5YIZQavfUUe9hpZVFKE1pLtJVx7iJSDeycuh9tfeDNDbBRnt+Gmqn3fjboSt1s0JGqqRLrf0mKQBcxfBalKp2BaZl6djiKwlYa04XNYH/W+9guN3qKZTpjPjCdKUG3FGEBMHrvUuOsHZVbpVGIwmbWjtJFCN1A4kWBKM+WoucBJsk1ei0LRdCoKjXq6WaHyv9Rg5k8t5kxNr9T734VFEqSuBMMR9JhIqkvQrTGsBoLSU3tbN3QLne33EQS58ERXrzEF8LZYbb56P8um/Dnr6LuO/YQ99zHrm/ySuhBgBVJI1AdhhYvqmdhBdQE1MGFm7I6LLJUhu0iRRj5ABETmidSBAWPJO5xT0yxdlVIM6FGNo1y0s1HDHLO6QaQrinjV7IbQamS1CNNlTtoNJbqv+ceYTeiApRQSp3hSilG4TyNaYtwo1PpoFncF7GUopkNS/q4pAE1AnF3Oec+YGa9kj4jaYekQ5Je6ZybNDOT9AFJL5Q0L+l1zrnVqfcRqQpoHORRpBM+TRaiigpdx9iKlt/KhsbY1czIphMwVSqY2QmNNGpM4pg1RAT2JzTqaUQhNsMm02onTDSiXHXYL4SONj+ADoXnJLqAhl19mM6dtGZDfoi1i8Dro15GTC/KswPOwcRcvmay80xPQIpDBs5JsJAN2exgFbYnBosQX1+0HG6+wFoiTM95VdJbnXPfNrMOSd8ys69Iep2kv3fOvcfM3ibpbZJ+TdKtkvYG/26U9KHg/xXhjNEH6ORNQI9Fk4WmdlGBbdaMhmZj8/CAUK2FopaDet5zUAWlj4U+6lA9RbByKr0PVBKRLjKRSnhGtsQSt+rwWY9DhSUKHD2EVYspt5omz9H+pJ7sBIyYlOB9wDUpoJ+BJq5SVDKQGgHVhGh/EopKtMgG2dwgW6CtTrXfYZQlwtYvmmO3ERGace6cG5E0EryeNbP7JQ1Juk3SM4Ov/amkr6lhnN8m6ePOOSfpbjPrNrPB4HdWOAgLfZaBig6VDIxDPW8aoqMLBekT6XwWNPoQs+PVNrOVwqDxSqX/VIQ3nnLHqU79KfZA1BOw2EQxXKmtag4uTmAzkIQc8AosCkSjTxQVqvJCJSYhbYd6wKm0IX3Uqe445fWG7bih953mDFDUY+x4hd7Wb3w9xgYLnd9phVCD6/rcEHuIkpPMi1LzCaENmNkOSddK+rqkgSaD+4QatBepYbgfaWp2NHjvDOPczO6UdKckxTt60MRIigdQzV26k4zQhDvKY4QTYthJTRTRBOSOT7BExHqGHS9SYDcQc6QhPSUJS7kXNrPzpJU+KW+53AslScF8RPsS0xsgdxwrMVDdamgsV2FNCmqE0v6k3H9cDIpSB2DCKwWltRR7oLIWpEZQEPm/OOT9x2Bl0UqWTS6pUUYVSEyziZpuyLL3MLnOtUToxrmZ5SR9XtJbnHMz1iSQ7JxzZq3t7Zxzd0m6S5IyA8OOhD6JNw0nOoQYHpekOky+ogsTVSWhuvFUJSQSpZMUvBGUT5pi52mQ1pKYgd4fqKQRNk+aUvGpUU/a0YRJyuWm0QRK0StC2g6VgaN82TTU5S4AaV6JV3ilDhHqcQ+74iqpXSLxNbOSpUUbaE2R1tuQYosSr7ZagxNucTN72KmRTZV2Zq85D7nOR3nT1RCqcW5mcTUM8086574QvH1yga5iZoOSTgXvH5M03NR8W/DeioiWnLoOtb4aTu5tvRvoBEWN0DIMWcegQhBOOKGeSXh98T7mxjFqpYXrVFGEVtCECZMV6H6tx8PNNKJGPVXSCPM86QaXcpapERM2tS/sypQU9D5Q+k0FUhCTVG0H0nayJ8OlLtKCNHSTSw3DjqOtP4Dzm9hOh2qxYznSHMy1gvfc4aJHIS/s54Aw1VpM0kcl3e+ce1/TR1+U9FpJ7wn+/6um93/OzD6tRiLo9Kp8czUejkq29buaBhVCqdwS9XKkT539O8thDioO0IeRLtg1KHEXhR5wCsvCIj2Q7Gdw4XVz7EbU0tBTj4trhathjLWPodev8+HWDzgzTLWIUTNsTEIVU1z0CC/YlE5Kc9SLUAaOemwhKH0jApWLSt3hVobNjLKHPTXGQgr5bcxjR2qRUFoLpjJRDXeIGvSc8wRU1GxNEabn/CZJr5Z0j5l9J3jvHWoY5Z81szskHZb0yuCzv1FDRvGgGlKKP3m2AzhjSRmkoludUY+x8UrVWmIwQZN6cTCgZ7JSZB3a08NcqPPQCnXw+rJp5sYZm2OhCAcTNB31vtJJkSpFws0xPc+5gdYb0iTuCpwjaAIjTf6mcwvlqmdOsMFCuc6FPsq5Ys1wEj50wFCOO13Dws5jmtrDHoj0BHtwS11gjoBGaPokCz9Rj3SkzPokv53dg+wI21jV4hvPOg9TreVftLIv4pZlvu8kvamVY0TLdeWOtL7NLva0Pmt0HG65iSSugoJLCkNeIeYjwok0NcbaVfbBE4WowxuRg0b29Cxb0WI5NglXZ6HUFuTGV6EXjiauRmAt9xSU/6uAZES68aeqK3RuoUoM2OUO189SJ0ymhwYJTeykcy6t5jxzCSXxs2Y0ukZpJrPbwuWq1yCfO3Oi9V1SsY+ZbtO72Q43c4rt5KCAjaIw+lRNh0ujWUu0VYXQeiyiYj8wLsCNoQlwWFFhBh4PLvTUm0a5+B3H2MPYkQu37O7kFFt5a0ko3QgntyrUZTOoYpOAlUVLlNYSssQkplSA4+F8D9ZMpV7WjtIbKLCDAnLV57bB40EaRgJ6wPND4RokWOcc9kvYOQPJGRg9hJs5gXbxOdYptQQsogc9y2VonWNddUi/KXXD9WQN0VbGuYtI1Uw4WyDKyRYNq8OxQ3moYRsxs8PsgDsSbMafKLKOcSEbJKm72Wagei1MlIUeaepNi6TYZiA2ynad1Uy4iaskIoSTo2GiZQEKFdAoGaVF5I4zg2Ryb7huMWqEYtoHdMDQBFtKu6IeaboWUbpP5iRb3Em+mySVOltvhzXc6bpOlz2YJBstsWedGtnec77GMNdQbGkVUaAJXdjUchNJUgSGLqlsWZgeP4lP+LTKHTWyM3F2IyKwEiZU2ZKeASUVZljog0pMxuNQHabEpqByP0zMhRVJ69DjRIoQUSOGcrmpMUmjZHSOGLsK0hSgI4X2CzVe6Zwbh8FDuhmgc1kMrM0SV11JTrO5rLAJ1paAXtsyMM5jMCGUy61CQYOQRVBwwmu47NhzQlsZ5y4CH2TQhEqyUa8RrRBKaTSYcw69OLRS5I5ORgb+/tjA2b+0DLZvZuUbK/AG9iTZrqzYyW58tc5mt8kCDdEwWJJasLSSKVvoi/2t9ycusATD/+Uu9uxljkFONqwQSiMDhPcvSXGoYoM95+E+Qvg8Y7TdPDQooapMeoztAqf2sF0njkQAo57aAzG4gaAVQmspdqKFfjZPJ/JsEkyOwl3LGqKtjHOJ7ZyIB6HrENyVw0x+zB2noVLYjlbHi89CjybcKu/oZmUYZ8ps4k7H2Mw9XWYe8I44m2xG55glE42w58HV2P2LpWDCK0xGjB+HibJkLaQShdCTnZyE0nj97HhRGAXEkQG47tLETuq4ocYyrtRK1xRIZ6IFcCpp2o6dKDV8sQ4/8DN0P8QeovwwW09ohdDMvcfZ8Q7AhA/oqS9sgUl2a4i2M87DAuUxUnoKlo6D+uE0xkq9KjT8NQWNVwoqiVipsRl/PM8shGQXi+XPF9mCtrWbuRknII2G3ofMQ+z6aOGVSlfr15ceCVdlotgHqVqQ4lWP0uq1qBne7IRenAl6lmlhO8w/htdHjWxOT6FrNNXKhvxqIIs4t5VNSMlJti5Us2z9mruGFVqhuYMZKKU4v4VKZK0dvHEuaPdCbwVVQaELRWKaTRiUj0gxD5PSytDoPTreg9rVquzGXzZ0ArWbjLIbP1VgN7AXqt/U4UofgQmodXgfCvvZ7tEV2FQZm2y9Ha34SOcInKgH7zml9iVZsAsn2NI5MAI99aWucKvldhyFRi+gakncqKeSiKS4oCRV4SaCgtx36gQrdTHnBN0gUXnJeJ5NStTIJrmKaw1vnIstFjXID6QLIU0kzZxig25yP3uo5gZRM8w5z8HEzs4cC2HEIX1jrsImxenDLFOWJqX17GUc/ijkqidT7P5Ricn8KItEGOSck+edzhGUppCEHHdKo6FRQHp9WDeeJsXDBFS6plClQWqE4irX0FgOvVokLgGPmiHalYMTIH1maYXQOuyTWAFGVGH0gh5vLdFWxnlDShFohubBwINh9STkZBf7WLupvXDnCpOvipupph5rl4dGbyoG1T4g/2Y0z7jcl155BLU7mWdZdxPj7Dy3bGa0lgRUeSlXWMQkOgultjbBcHCu9Uk/MRmu0DnlLNPCYdSTXQyZ4w6DVjixkybv041qqTtcI7TQR2ktbM6d38wGNhVDwApnQP6Ges7phiyeZ/MfTQitpmF0Bm6sogWqjb12aCvjXMaSOcqgghydgMshJo1IXB+deuqxGwfObMUqc4tNzbGYdQes9Lmjh3mkhzLQ6IWu8+4Us2RKNTaVxOF55mFibq2DErPZ9UWBLn41F67WPNVMxgmTUPqPFn6jmwhc1QmCUh7pXE03ZclJyI2HxnkFONwkfn3IWSfh8ZKcan1OKvbAInN0fYbtUseZ2PzsHsbtS0wzI7u4iYYB1w5tZZxHqlJ6ovUHq9gTjvyidB6Z9fBO0YmbqrXE87QcOzve5gx7+J/UO4LaReDsNldlD/8/ProXtXvu7gdQu8k423WW4QCl6jf5ImtXhRVQI+MsQkN0zh2MIlHQpHGDyjd1Sos4ydrRuZomAVMjO32KtaM1IiJwk1TJQpoJdfhgo5eNa3p98Tl2vEJf64sf9e7jOiS9zAlW6YCRSmgP1FJQDW8DWsIb8JTOEyBElAAUjvz21ttIUg0WVKByYFSea9N3WcORp7CViaq1jBWY+25XjrnTHphh+ugZKKX4tEseRe0oZqD6DU4IhTc+EWceEpeDERpoIMQOk00E3OBSWgSOdrFmdCGkeuXUU0+VtahxTiMR1OBKQQ845ZxTx1TuKLvAmZ3sRCkvGxcKBFSMWAEWfctAY5k6IyHNhB6P0mFonshaou2Mc7LWlzvASKCRZ7ojhBMipd+cuIGtMOlRdrx5mEhK9bXjkCc0Cz29/UkmUZGNsV0Z9dQnINF2sshCLZQOs72LJW8cz7NwaR16iasdrT+4MRh9ouXKDRp3dC6jKih0bqHGVgpEYaXwDRKa/E2lDWlyYC3Jjjezgy1+XQ8z9/LEZdCoh8okZFNNqWjUCUYrhNI+oeop1NGQGt94JULbyjivR6ViT+s7J1LVknpjckdp6Ctcb1qJKQ3i6n+Y/xhlW94ZGLMezLBM2aE0MybrcMXug3p1IxGWrXfPMba76u9i50kTganaTmWE7XLJ3aOeZfoMlWASd+oUG5uUvlHYxNplIB1mbjDchEnK66VGvYEosyRVoZFNaUI0+ZEa9ZlRKBsIDWYCSr2hRjatEFruYnQYq0F1mASbBIv90Pu5hmgr49zqjPdFKCP5nWzwRMqwMAKcoOb7aew53IWJ8l5p4iPFKKTR9CWZWzMOO7QAQy0FslOVdP3wUdTu0ele1I7ShCqw/J/rZsdz82CKhVZ2FVppBpJWJV6xkwIXv4HnSSUYqY47rrwJz3PrV8dRuxPPZLI5lH6DaSbQgVbOwl0uBLk+WrAqMwrpMDn28CXHWOS3mmODOjnObvrsrpAns3NAWxnnLsrkochaGJ+BCxrkTdIJOArJsuVuSBeZg/JVJXae81Ct5WSR0Rsu6WCVUKqQA9AJd2U0MnB8jvXL0/oZN74KDVGaSJqFuvhRmEhaLbV+3x0szERpJnQuw1EyeH10LsPJ+7A/KTeeF4NimLyG6fOG7QGnRj29fzQCRduhRFnocyt1QQnaIowmJNlgodSpcg87Ho0+rSXayji3uhQDXguyyNTScIGZhwoH0HsAHYVKn4RZz3ATEfbCtAl6si9JM2/Tt6dZBvHDM2wBPTnDLKenbTuE2o1D92QCZuLkYAi5CDnu0RitkNf6wI6fYudY6YCFkqrhrkyRMi0Uwo6XOQml/6AOOI0odD0SbhXG+U3hVniliJYh/xiuKdQrXeiDUXHAr44V6JiG1Vah8wxvkGABqTpUa8kdgTvHNUR7Gec1KTnT+gRXAjz15ES42sBUrcVoPROavUyr8UF0JljHUPrGPIw9X93JaB9/PX0lavfinfehdl3QAvrKif2oHU3ovbKbSWF+Y/QS1A6rymRbd/vF5uBOnLruICiFjXpCKRefGqG0YmcCFpqrQiUvCsoIpMl6VKkMV8yNhVsBNUyZ5Aj0LNONDq22SjeOtWS4c1k9CXcRa4i2Ms5dhE1wxNtbhFzuaAF6zmmSGBxz9PqSk+F6mx6dZJzlpw89gtp9f5YlPr5o0/dYu2FmZJ8oMXrKYIIVPdrZySIKVRjaobSdk+OsX/p7WKSFFLvCHj+o1kLlVimthYbky13h6rFTY4tynamaCeVkUyWv7Al2H+agjClSUxOXiqRVNHlOROvHi8LcNU59Y966OtRHpxskKolY6g7Zq3gOaD/jHFQTI/ZIDNJTaJU76jmnxYTClkmjIfmhLIux9sAVtLODWTJlOHNTycctyRnU7lCR0WiScFb8x3svQ+0u230Mtcvl2P2DwhYqz7U+6RvcqNKk6vhsuFHAJPQsU845LX5DKJIS9zIWe8OlmVCaEDWW6fHomtLxGHvW57ew3U4lxwxmEoGCgV+8MRbeqLL1qx5jBkjuMHsYZnb7hNC1hbEdF/GQlJniXOgZ61hmC4asK12soUuwWSNfYh1zqMCM0HSU3cCpClthhlMsAbUG3X49cbZpoXSf2675P6jd/3qUGfXPuuQh1I4mEI9PtZ4dWBpm1mTyCLsH1FNPvVR07swcZ+2oQyTHmGia3xJuobkyjGBgZS1SUVtSZjRcakSph1mwRI5Z4pvA5DTgnBfDrX5ahH1JEzvp9eW3MyOb5hmsJdrLOIcgnociU5Piet7MRtPcMJuBSdlxSYrAxBEqDUyL0QylWDsqUUiLCRVhhm0UrrzbE4ye8s/Te1G7HKxD/YztD6N2VDWHcs4RYMiaGnc0SlaHG2oq01qEOuf0+ua3sHaU1kLpRYKOG8o5p55sXIWRFmeChmFyGsrXwoRQMsVXIuEWuqKoACaDJKXHII2mE0o+jsPJcw3RVsZ5pCqlx1p/sModrT9UaVjYgnqN6GaA8i2ptUzpPqUsO2AMGqH3TTPu+K2b70Xt/nlyH2q3L8cGWhKGaKhRf6rA3HfxDOO4b4MRhfvzzOIqVtlU6YCGeGKUHYtysmNz0NMLaSaVDupNC9cjTT3LVLGKbiLS4zD/CSZ2zg3CQi/Q404pHHNb4DMLK7zSzQeZqulYIcowEi9exHVM4eEgKlBXfS3RVsa5i0iVTOsTB5ElovxAuoCmxqHRy/IllYCJnYWt4Xrqqc75dT1HULvPHrsetXv2wIOoHeWcHy+yEq+03TXdjAPwzUkoMTnLaEmb0yxr8sh0N2pH1iYq05o9wp6huSFYNRBWCKULduYEO1oRzoF1qtoBDSdKXZwdZv2ZZYJHGNTIhoJOWB2GOPgkXtWysKn1AZOYhbUQoMc9Pcp2uGWYaFnsgx7wSbZDKvVsPFN4453ReYKUJK6ALHLujQnXk01VXqiH38WhF2cqXLrBNyaYpN7TNrFiOw/PsZj8UJrRb2IwZk0lJul92N0xhtpNlpnUxJYUS5R9MMbun0Va7xeqA15i+yrFoeecereoR5oaJHNbYQIjVL+psPQEvBmgHlsq3Rh2YieV9e27B+bPbGFKUNnH2PFqqdbzUqgRSo1eWiEUP+t5SnRtH7SdcU52homZ1if9+QEaCkbNcGVRXNwHGBWSZNCwqCeh7FWdhViv7WGeXurJniqzlakjzgYMVU+hnPqpCjOWaX/ee4rRkjYPz6J2cwXm1qwXW59iYQFNLOVG/QVxOLdQDzGlReBCKCFHujMjMMkPqqdQUGM5zh49TBOauJzNSXS8UJklQhmZ3cY6JQ6LF1XTsHjRKMspym9jk0QCjjFK8VpLtJVx7ow9yGVQrIAuhBHocacVQildhOa/USPbxWBxBHiiJRhSqBvrz/4Uc8NloDpMDWb+DEAJRmqcV2CG9FUDTLrj4CzzgNdDzKSizxD1ZFNjpAyNZapYVaYeaThXY2OS9mcXrBHBHL34PqSmoIFHq1zDasA4QTrc+jdKTLW+yy1n2UlWoZY+zS8p9jNnD6Xf0OJFnd9k0rxribYyzq0uxedanziIbiv1SNMwDz0e3URQGk2kACeNLWyHPV1mK9MsXNG64yymG4WbiHFI36CSj7k4W9EicGCPFZjFdVknIyB/Tfl30wAAIABJREFU89gwaueoexmg/zus3fTucClzlU42pjMj4ep5pxhzCuu448qbNKoK6SLUeKUcd0qHoXrzlBpR6mRrWBkWWSp1te5RTE6xa6Ma9dEyO14V5ABKUnyeHY8W8po5MITaSZIO8aaroa2Mcxdh1b3ieZAQ2h9uQYxyN/Qsh+w5pzJiVUijoR7N0SLjCVHj/CRUMxnOMlUS2i8p6E6bLDNLZrLELIt7preidsk45GrS2Hqp9cXp1JPhs07zYICijCTsqadzIAzO8JoUIapvSOHrlWOOO7x/3KEFi2vNsY6hWuDU20sSSel0lJpkfRKfhcWE4sw4T8CKpDV4vHI35TKtHUIzzs3sY5JeLOmUc+7K4L13SvopSaPB197hnPub4LO3S7pDUk3Sm51zXz6X4xCbhCivRKERSo1eGmqrQ7oInWhcFiaEJtjDX62xh2pXjrnTItDNOJBmMfIyjMlTWstsjUUUpmAEIx1jlszxGeZxT8YgF7/ILJlIZ+uWTB1UFZUkldizkGLS9ip3szGWvwTqT0MFqThN7IR5PskJ1o7K5VKHCO0XXKAO9idVXSl3hmtw0VwKUvcNlodAstES53LTarmVLLt3mDu+8SjnoXrO/0TSByV9fMn773fOvbf5DTO7XNLtkq6QtFXSV81sn3NuVQuO0loIpw3zNCFvMjENQ2b9sGJnLNxsaQcNkp5BtjJVITF0oso8xFW4uyrXwg1uTUEazWbIqaeVRY9WmLRhR4q5l3dsYhbsw8db57hbMVzSKzUKU6Nn/85yyO+AdBgopUgrdlKHCPbUQ4MrwUoF4JLsT5Rq1dTjTnXjyznWoSWQa0DknyVut+AKoZAOQz3ulKOXOww5V2uI0FZ+59w/mdmOc/z6bZI+7ZwrSXrUzA5KukHSv696DGO0FmKn4SppMORJjWyMsHeSkNM7W2YrRTcsU/8gTCicKTHP8v4eVoSIFgUqws0ApdFkYswi2dLFEldHppjl1JNj46U+CzYfUI4U8+Khd4sWa0lMw4UXyORK3ChMsCEmGHzCmwHqkaa0HVJuXuJc9dRkuN7Q+X5mwSZnoCMMRqnRsUKuEEo551hlDqLcAzlea4iNwDn/OTN7jaRvSnqrc25S0pCku5u+czR4b3UY2xmSSYpOwDSpieqj11OwKBAIx58P4km2UuztYu67AiRc7swxD+qXj1yO2m3vYDrnnQmWXTYUY57lR/Os0gtV2+lOsus7WmNi4DMF9sBHOlp/jqKH2c6fGluUvzo/xOaW5DhbsGd2hasOQw0Z2o7SUzofY/chDyt9Ek+vxO8Dr9QK10xop8XzzKJMgSRG4oSUuJRiBBZYqgElPInf83IHrJcCHRRrifU2zj8k6V1q7HHfJen3Jb2+lR8wszsl3SlJiXS3Midbf0DqidYHULmLTWxYAQBKGdW7YSJHBca/4EO1Y+sp1K4Ii+bkq2wGnoN6YP19jLS3Ocna1WAVRlqEKAbL+J2YYx7+kZOM1rJjKyx6VGAGc3d36xZXrZOFWMvfYBukLFOl1MST2IJW3MLmpNh0uJz6EiwK2/EY5NlCtY/5TWwtokpeKUj7iFTZ9XU/wChzJ29kcwvd5FKDkvCksXob3Dimxpizbn4LWy/js+wmxOahHGnmIk4IXQ7OucWYvZl9WNKXgj+PSWrWPNsWvLfcb9wl6S5Jym4adsXe1juZcNMod4smhFZ62GA1yB1PZ5kHtVJhQyoGZ7YT82wCrqbYDexKMMPp6k3MAjpVYtdHK4TS/hybY7vOZIydZ6aDec57Uuz+0XZHZ1qn0RQqbEErbobScVCvPDYHHRR9cC6Dk24VqrzU0myyDrtAXTTkYlDzm6kqCTve+FVUxgY2g+dZ6IdrX6H159Yl2LPX+69sHZq/YgC1i5ahxz0F6TBUZa648SqSrqtxbmaDzrmR4M8flnRv8PqLkj5lZu9TIyF0r6RvnO33GlKKrZ8H4RbS3XWGUYg1s4cZy9kMawdr7ahUYp7X3iSL6ZbrbOKmZdyPFpg7bW+ORQa+O8okA6/ffAS1m4jChNAc825NQY90T5YZy/NVNj73d7IH9/B06zSawjTlzLGVKXWSLYR0M0ClG2spqARVCDchlFIeaeVNXNGSJlqGXMGWRptjcLNDueMVSDUhjiKa7Dr5VFZZmaqg0Aqh8cOMrjr/JLZepg/TrOq1Q5hSin8u6ZmS+s3sqKTfkvRMM7tGDVrLIUk/LUnOufvM7LOSvi+pKulNZ1NqOX2g1s+NcNPoRDp7CWsXgTrgFHHoed2zhT1UlHtcgVKKlPaRgsV9ZmDRo84UW2GmK8zoTcD7noOJncdggmZ3mk2mE/Ns81GHEmRREBFKdrINdfk4s2IKw2wyS55ky0c1Qz2vrF2SpW2oAh22USj8QPORaEIozIkXZL7h4kx0k5Qah4V6OsONDBCOe+YELNIDKLwST1ot9jH6aCXLNhGREuuXud2QwyZJ9/CmqyFMtZYfW+btj67y/XdLendLxzCmvELU3Ki3opqFCZpVqPsJjfoyNHqpB/wEVBfZ3808muMltqI9MsV4vdu7oIUAcXCS6ePt6mYizY/NssmtK8M2H1szLPJxKs/u+1cOXYralY61bjBHB5h156KQCzzPnvXiVrZRtSKcy6DRO7uDtYvNUQ8/O16VJq7StYhWFoUeaVytGvqlqD56oY+1yx1nJ5oYa90mgErAKvSxnU7uONvA0wTN2Dzb6RT71juN8sKhfa5EDaoJkV1KTrf+cIwDbrt0HiHdMnuoaKJeHc74pwrM+KnV2fVV4XnSojl0s3NynvVLucoe0d4MdItB0M1cvsCIryeSkM7UwRJsD45sRu2IoV2vQr4lTLhzsFAZRoYtvJFZ5rKt0U0LNJapB5x6iKnx2ncfu8Cpvew+4AqhkGJJK33C4ChGsbv1Gx+tsJtOk1Zpxc5KFlLmoJFN9d/rUFVmLdFmxrlTZrT1GWBqV+vdQCfuSAka5xU2yOdKLKxULLIJeGaauWN2DjIVjRLU5d6aYbQIGhmgRY+oqszJPDNec3FGT5mdY5udjixzw43ACqGXdE+idhSIjgaTuGs97N7Vy9ANR7nH0EFBi/TQxEcqMUklEWExYMG6aJraDTc7sF9oVcv0GBtoJUhPSU1AiiWkaxH/ElS8xXz6eozZH/E5Sr9hx6OSj3Szs5ZoK+O8HjMV+lof6SS5E4vkUw3caXar5mpw5k6zC4wfZSvhoQijYXQOs1nq8o6Rs39pGRwq9KF2W9OM1nK0wHS5r9t8FLU7OM3uQ3eOcQ76MszN2Bln9/3wDOzP7SzB9t6TW1puU4bRBAc38NEUlC17hG3Ey0PMs1HuZQt9YhL2CzSA8ttZuzjLqcYVQun1VWCV665H2ZpSgZU34/PQqIc67jTyQewPqstNOeelbmZ/0POkCa9UgrEEK6CuJdrKOJcx7wPxkOCqc5B6XIOZ4JR350rMm0YnqGicJiKyFaYrxozJPJS525FmYstbU2zlHSuzTdlQjh2PSmHOVKDHPc487n1pthkYLbD+NJDo3NfPqDfTs8xYrsIoWXk7c4VGJtjxaNSx0kF1x1EzrORVA8VoJO4oCptzXgL0jfNBdgQaal3h0nZIFJ566TseYxtjbJxDT7ZBdRiagIopZWuI9jLOJTlCUAN2RR2GSinni24GItSbNgM3A0nI+YKh9ToMRYzBlXcoAz3gReaxHUiyxMdqnbm3+pLMfZeGRE2aCNwNPUDdSbYpK0L6VC4FKoTCPJF0it2D2XlmjMSz7HiVXjhHFOByBefcSJHNnclxNjbLUDCCFkuiaxEt6kSPR6thzm4P1xsK/T2IdkWPVc3CCprQyK5kYK4c0H6XODWMMhrWEm1nnCOQ8QMpShEYSozCnZ1N00oMrBlVKqjPsB3vQ5ObULtdQ4zjnocE1n1ZKHAP8YNx1i8vuYSdJ+2XB4+y4haFLWzhHcwwrzQtskT036eLzKUZjdIShXDhhcZyNAF5qEXohatRvgFrVmbqoHhNoeXmw5T+kySVqQ44OxzluNNNRAyOT5LxmpxmN6+aZoM6AY9X7mBzRPYYu3luE1uH6tGNZ523lXEeqThlT7Yeypq4tPVuoEk4aWYT0nlNosYyHBnRIg3Nskljdzfr0JMl5lm+vuMQajdWZR7inhijYdwy/CBqR43sOowL3nrZfajdZJnplQ9BffSdWTbORorUUmsdJ2fZGEukmQecbgYScUY3mB6HXPxOdjw3CzcfUF4kTM6yJGVOwAgGNGTM0aQ7drzEDDselaakiceEX13JhUs7FbznySmYZ9DBnC/JCajUsfFs8/Yyzutx09zAxr4kyG5Qgjn8uKoMbEdDs5RviYsXwVmqBt1p/TF2A4+Wma56P8wuO1WGNBNY0eSxArs+WiyJqt/0QX28x/KtPxB0THekWFhubp5ZFbVDzENR2c7GioNJ6nTdjUCPSB1WaqWGE6JySqrkoNE7DY16UOxP4puPUjc7XnwuXOUOkqRpkGaSmGUb6ipcn+N59syWepkdF5+FnPo+uLNaQ2xsS7ZVOMaNSuRbH3g0tEfluTCXCoIa2XFGkVaFFQRTFsYuO2FC4UMFRsMYhJIKFejG6Y+yzUAS8panq8yTPVZkBt4gLEL0rVPDqN2+Xlb59hTwZu/fdAod63gFTkowoSWzj43pWh0ak4LXV6KcQNaMFi/CdBF4nrC0BNaEJsX+JG4s00qfMZjwWu6A3mXg4ad9iZ1g0FlX6oHRJ5gQWtgMI78wh2kt0VbGuTOpBnbnhOGAeX7QC0CNc1KUSeL0m+nd4XLcs1CtZQuUzSnB3dVgnOlr98aYB/ybsCwi9RDTiAI1suegB/x5Qw+gdt+bHkLtnj38UMttHpxl+QIOGtm1SdaXtMxVdxcbYwVYydRBT3ZqjHqkUTOsj0497plR1i/5bSEbMjAyQJMm5zdRCgctgNN6G7xhgRuI9BzzgDtIh6ESjFQ4AwYr1xRtZZybg0kZQJkEShFzbwycaCjoBEWvD+/M4a6FeqQzsBLKdI15lnsh55yqp1Ad90thwuu2FNu0UPWbJIyRp6JQlg2MzxOUOx5lC2hqABbWgpVM54tQ7gwmrsam2LNegXlF1SyjDhjsT1qRtADneFoMiiZodhxhz97Ubui1hedJN0mpidbHC6W1lLpYn9AKoXOD7FnHyjdQOjo1Br2ma4i2Ms4pEqBqVqE/3MIWVAUle5Jq/IZcxQ9e32iRGTIZaLweK7AEvy0p5iHOZFgxoeMFxkuqhiz4GoESFd1xNnt3QJHm/R0nULsH863ToHozzFgemQov+VSSqkW4Mc6zBZt6t2B6AuaORyrhcquxygtN+qeKY5Aukt8KTxQ6iqhXmibKFntan3PpWKEeaVohlFKgklPMkKjk2FiJzXvjfM1BJpz5za0PPDpBJSAnm3LMZoehVi8M0blIuIU0RuahCkqCGUA3dR9E7b4+vQu1+2Z9B2o3nGEe6RqMC47CRNLLssdRu3nIK/va+D7UblOK0YuO5ls3mE9MMCUhB7nctXm2DKSPMIpXYRtbCBMTsBYCnDsTUIYWPgqhRx0ptzpWgJQKmoCah9TMcdZudhtbM7MnoDY3oOL2fI/RMqeuZE4bWoSo62HmRIkUYQ0FaJznt0PvoCT9K2+6GtrKOHfGBjrhZdMKXXQCpl6jKkxApbQWOiHODbLjXdLBjFBKb5ioMkLpga5DqN0IzMwdgJm5DxU2o3a0P8cqzJKJw93cQIolyv778R2oHUl+jERgtGuKce3SI8zoLe6AFULzcDPA8mRVZBR+nB9EQaOOlONO5+qwEzRpEiPdDNDgYTkHvctg8zhxDVsXaKIlxcxOVrPBRWCthwqthYCarSnayjiP1Bh/a34APFQ0JwY+G9RbQU+UemNoAQcaiaBqLYfmmITfcGoCteuIMDfVMejJjsAbMVthHoQopJlQjzuVwqQVZXNQppDgxCikp0CjvrgVWqFlyJGeYe0qLKAgmFMtuA/HjhQo6MQ3H3Cunt/E7h+Wigy5tDpdixy0pohfg0ob0mqrmVNQrxzSWjoPs5tQ7mQ3gdJv1hJtZZzXEpDGASbTsCURYd4c3gzgdhDUSzVaYivoZIntzCmdgrajCa8/mN+C2nVAickaNHpHIIf/1Dy775szzFKLw2TLcrX1++em2eRiNegpTDErLTYBE+5gshedO+swYl1Ls0mQFmLDlUWpQwRWtHRdcIN7jJ0o3QzQ8ULpPnRTlj3R+txCeOqSVIUJk7F5WNUXbiJmh1kUkFZpTY1Bb+Qaoq2Mc6uzLF/yEFMvjqMFFaAHlVZJS06xhzG/lYXI6YRIi9EM5xhn77vT21C7bWl2PGr0/mCa0VMoqFLIyTzznOeS4XmyJakrwQbowZn+1htlYbGdaTadp46wdlDaXlCASGVYwC3DhISw7FzYeuXUA56H3GrquKEiCtQj3XEUGpTQgKU0mtltra+ZnY/BqrdwjDnoWaY5aDRxNTPC5unCwHlwztcIbWWcR6pSeiwcWgsNfVFuE21HufHzm5mRnWa1WlSFsmXU0ztdZp7zhyeY1GBmM6PfHJ9ju8DuJOuX8SKzuLbnWGinM84epJkK86yM5Fl/GnSLlUvACw6PZVVa0RI1w+ob1LFBPe5leDxK7aPGJO1PWjaermG0P+kaRmktiK4q4c0H9ZwTg7ncwdZnyjmvx+CcVIfyp9BT76LhRlnWEhvwlM4DxibGKtg00R0oDbFS0EFHJ5q5raxd52F2QKoucniGueGetvVR1C4XY8Z5DLrFThSYR5rQMCSpWocRExj5oP0yV2JuzdkjzCKJbSIWJSy2AxfQ+CzcwA/ChbfAjtcB5whaNIcahREqlws97tSoj82Hq7oC65ThyADdXFF9+zjMbUiDKDURvpC43CPVHc8dZg3L3exhKGyi1SFZs7VEWxnn9ahU6AtHM5QWKqCZ9dS7RbncCTjRUBmxCMyyniixmbQDepZ3wdKpY/DGUxWUxybY5uPm4UdQu5ECM17L0KindJhskj24M0lmIVRLgHMOPeCpU8z6KbJgEDbq6cY/PwwNCzqXQRoNNZaxWAA9HgRdG6iRnR1hc2B+CNK8YFVtXLwIGMw0ClHsgXPLFDvg1D4WiaXymbC2oDofZCpea4m2Ms6trv/b3pnHWHaeZf757nLO3W/t1VXV1VW92d3tJdh0HGcbmQRCCBFGI4gyAiUBRpaYQZpNGoyQZjTzVxjQaEAgGCuEISMgCQmZWJAEPBDGQBzjLI63tntxL9W1r3dfzr33mz/qOhSh2+3z676nbl+fVyp11a16+yzf9i7P+7xySv4HtTbif8K2GSpC2Sts0hUPw4MQGudugd0n7TpHU5CxCNvxcw4Lb1EWlOU6q/aagVj1A3lIqA8lA+EpZQhPqdZgYa5heqPTjEpja8O/ExGpQD5veDBR464xwtZCvMTWOjUKKWQuc4Xp0cJOGjmvgbIGScrC54tT3nHYhbE+yswUjK+GEX53i3bR9P98qXVmLLcS7F3GYYfQ2ijblCjmnIbAG+OwgKaHMlDGuSQUfSAeL6XZohhwGnWgUaoGrMhPbLLr0YMpATt9ZqFFsgJ5x1/a8t8pUpLccVj4EzAFIzWyaUfSdgvCaBz2PqPwvRjiPEKjgnbZpe3fqVDYBzWy6fuszDA9iuWm0IEm3DubcI+ncBGZYM0NXJiLYUJwr476T4s3c2z/G32eRYi9fLDsKZjycYWlL+ojEJrQQxko49y0JacECkJB8SNlKqBRFRoBpzg4gsOXJBjIxumoJDTOKQZ8s8kG/h2Tl5AeZWsZTrCTnlIbFppswsykWUT6yAFWeXzuCnOS1jdYqixS8z+xOxBCQ9PqFDIXaQQL1KT3SdlhMksQancKNnCDTZYSDGmHC1c9GmSkoWwoQ+fZoblzjL2YygwzYOMV/+s9vcwWe+kw83BxMS+tzYPsMF4W8pxDDH8vZaCMcxuVGvlgOhbEWPd3fIAmtmArYojTpIuxDWcUZRxYr7MURgQCPEcdNvBF2NynAlMKmRhlQWH3SakGOwF3GEnlII/7Zea0NMb9L6RoBWYT4BpKQqrBtgMboUDjjkZsXYYM086xYPdOByLRmjCLC7cIXFeUXmV7bgN2FnU34ANC45xyepMGg4UjbLFTakNKrUwz99krbOwaw8yDj0N2mF7KQBnnxkrRpv/ZQNgDaAqZFv1Qzl1chRwwXo8eaNUWW4y0U2QS9tj2IMVTwWMRW0oxmYM53XILYsfh+FU95rS0O2wcmsOUMsL/go94bG4GTcVHxUbZJhiBLC812kGTUurBLCd1WuhejWsUKE89ZHmh0IjtU+yFUlgSZkKp+99bmplggxqRNqwzWGSbEi0kxU2IFoPtm/FGZKCM806Ud87yKxSGQbHjQdN64RQyZUZgUG6NuAy+UWuzB3ypNIX0Tg9dQnp/vz6H9O4YYrCPqxUWIT6QYljGDchbtl1hTosTY15gPQkbA3X8H9jUOKARYrgUsPFKO2hixiq45zrwfabW2YupTkCcLYxI06xqHGaNKVSSYvFbsL4rsUULvJhaaca/MZFah5Fs+GiUd7xwHDpIuK6BqbVS/WcK998d3YREG1b5V/2P6vYJ/7t+asW3iqSbaOBAWVfgAUOLaSjPOcVbbsCmOYezW0hvKsFyzw1oWbxzkvGqP7U2j/TqkOe8DiPgcdhZtLLBjPrUKLMsDIz2Ghe05p6kkXPIj04DDTDbReEpMGmF97LkJmTWmmfjN/PXEDJ3mDmqFGIJk2QaPscmDOmgKXHnsT7CImF0fhLkIl2z9NmijWBhH5QggjotlBGol9J/d3QT0naNSnPASACLmKZKaUqwTVOeMJJNF4fLbF682SSikOIJhgsXKjDED2UuzTpv1jz2fDsF2CF0gg08Nc6HDjAnaWeNAWYNKOyUmAFrXdgcBhab06LxoPnD6WFF97LiXLA87uv3wU2eIh6hcwV7ZKkMIsQSj5zTbDOlKawcYM9HeNUjAL4r8XdC60tGvsXOheIJtklghAGN1PdQBso4Nx3Y9Qyknhu0QQWcBE0YraiPMD2aWqd0ZzRlnYmzcMWlInsxiViwq/jZDcbnVq6xCXPXwWWkRykRO7CcP5tgE7SUZFFGC7nH22lSEAoj4PjgZXqtoWA7hFJsPC3epw3j6PXoOOC28QF37KTMHTTbTA2u6niwGahYw/8AthNs8DDrCiwk3bkHGtkQvx+vwAZSoDao1zJQxrmN3EThpE+hk5xu+OlFpkcPtDpsbEEPJhzlgJQD8znm0c8nGZH7q1X2QscS7IUux1mEOAJP+jGHpYTOF9h7qTRgM6EhFu6tppgTWLsILAtaiAj3JApviFUg3Rmlk6XPR1lJKAQRjh+F39D3QjMm1ImgARiKcadnilNm3kdjiF2Q9D5JrdEmRDB7AWEtHdjtmNYL1EeYSesWaRqpdxKYcW6M+aSkD0pas9be3f1sRNJnJM1LuiTpQ9babWOMkfTrkj4gqSrpY9bab93oGhFPyiz5f8kE04ajB9C7rrI6RBw9oBh33JWNtt2FzYQaHTb1v7ZxGOkdyjDwf8uyk95rsxd6aYdFOk6NMT6+IcjH3oDYeGrUZxLMgq2M+F+AzlV2j3BKY4e6Psr0krBeh2blPEj9h5muaCQb7oG0iVQE7vG0eB+zCVGnBZ7RmPM6wOArbUJEHXHaIbQyzQYvd5Z5jrVplhltwUxELyXIyPn/kvSbkj6157NHJf2ltfbjxphHuz//oqQfkXS8+/U2Sb/d/fd1xUaZ90oiHbhjJ9wQIbSaRyvghk+x+PSgoDzZJUgd8JMzN/QRrymLEAf1+KV7kN6hIYZVn4c84C9tsOY+Y2k20RyIVd+uwCYcFNcbBTRpU8yjHvoWW+zlg0gNG68NaNS7sPswNXqdgHGo1LmixittJkQj9VSPUj7CLQJ3x8a9QQCem1BGSzfhsDgQtggdncKdDGLglNggBNxu4w1JYMa5tfZJY8z893z8sKSHut//vqS/1q5x/rCkT1lrraSvG2OGjDFT1trXB8Qa9pJJbSA1esFZ3b0gU2szR5IX4VAOY0ivdqnCsOMzKRbJThh2YscjbNN42/RlpNeA3aBox9VjI8xyos2gNqCRTaNblSJz5mzT/4YUAQwvklQ6zMacdvpMQGOZRrIbsH6G9qSIw6ZAOHATMK+6hZDH7CXYTAgavUH33MCZD1qMCMYPG9nQ0fHSsCA+4N4+QV+vl7LfmPPJPQb3iqTXwm8zkhb2/N3V7meva5xbsUYAFNNGpD7B9OhBEWf002pBoz7ahBgzeIBm4gxQ+s7cOaS37LEIuAvxRUmoV4KdPmlzpulEAeltwfAdLeDJ5pnXWS7D99n2/z6txw7CCFx7NGtVmYFWDDWa4MEbL8PmRVPsgukF2OEVRsAjAfOH07qu3AKbaBt3MTMlvQIhgelgI+eRlv/7pHSWtElPJwab9q2x86s2waKfBrxLScqdhwUYPZT9Ns6/K9Zaa4z/2IEx5hFJj0iSkx5WHLC11Ef8TzwYCA08JVgfD5ZRoZWG1yvB68FQQKnDvI8SDMOlYFXaq2WGARiCzZk26iwiPZtkMJqVOqu6o7CWWoNt+sNDDH6zU/D/PmkRlZdla8HdgilrFzZCAQ6LJMUr7D5bqWCZGCiMhkZeaSEpxR9T+E1xlili5rBEsKxA6WW2Htqu//uk3UjjFQr7gA4uNLITm8yRaw5Bpp045aruney3cb76GlzFGDMl6bVWNIuSZvf83cHuZ/9ErLWPSXpMktJjs5bATUjEwoOV/JTn3IGOXfUgjB7AA4amyFOsnlAx6CXRpkDUyKbY+JUKwwBMJFnKhFIbjsEUzYbDsIUbcDNN5plFEouwgzfn+sfwX1ln+A3rUaMeGq8xSktC75ONQbQKaeegsUyzjhgOE3AzqBa0YzArECwkpbR6NDOQWmFSuVZJAAAgAElEQVQvdOukf28gsQOfDRaS0gg4ZbBp5mH2kM5pt/9A5/ttnD8u6aOSPt7994t7Pv8FY8yntVsIWrgh3ly7EYtmxv/Col00iVAMeBPiNPNn2aSjTAwUH0ifr9piYSMPhrfWIL/aiSTjD3/H5CWkd6XK4Dd3D7H7pO9zDHqdpSTLYCxW2Pg1IYa/3fG//lplmNIdYdaPKcICGihRmJWLQOeDsoS0oKPaTjLDyd2k7eaRmpwS8wYoDKo+DOE+MJJN4Sm5K+y9FOfZWUSciIjH5lhyje0RXpbtf06RvctWis2VeBlSTCbfxMa5MeaPtFv8OWaMuSrpP2vXKP+sMebnJF2W9KHun39JuzSK57VLpfgzvbw3EumgqUTcKpviLWH0gHYEo3Af2vr4hUXGMXl3bgnpbTUZ7COaYgN4DKYUajAzMOGw4oZNWOVXhZ1an7l4COndc4iN+7OvsOulRiCwF4j12AETo3ARGDmPwuJvCk+JF6GRPQKfD0MCkZo8yuOeha3cA4bDUGcgswTp/6bgjcIzGmHOk2yOFeeZp+OUYQYe8pVTeyfSZIoOdHZ6KUGytfyL6/zqvdf4WyvpX/u9hmmzSUQ8bDp5ovCspqlEkkm4GaEdSen7nB1jWOezZUb9l4qxk2kVdl6ZirO0Di0krcIw1WqdGedbDTaxD09tIL0WiGRLUm6c4dG8ln9vPJaBudkFlpZLM39FNciln1m48d9cS2gB485xpkcF83nDwk7MHAaNeqoH/XAMVWhDqIIHnSRKP5xa9x/RqhxgphuuhyCd18UcD4k7gFTihYAv+AZkv2Ett1QinlV6yf9KLs36D4NTdhEaWaaReqpHu8fVIBsN3YBnMowl5O4sa7n65eW7kF4ZltffAzGCc0lmvD61fRTp5eLMcip57L2UG0xvKsMyA5kEs0iWzwHi/xxbDBFo/BTuZJtSrMTm5vbdwWLHqbGcP8cCGxXYMC7BliyGSiY2oeEE9+rEFhv3wpFgK2xpUy4aOS/P+DfDopB1pQ0LSWN19nB1WKDpQHgKhd80YGdRSdJTXPX1ZKCM807cqDrp/4QiMOKRsyxltnmCvXJqLOMW1NCJsDDVbWAhKWVroQWhd+TX2fUgZnmlwQbwWIpZJE0YDW1G2fOtFVjEvb7OIu6taXZgD6WY82GG/EdkzDpzPNJX2Roqz1PmB6SmditYfCfNylGsM476BcznTaERlJCd1gxQoVHbDuw4RnnqRc5a+CojsHkR7RBaHWfnSW2EGvX9B0+hMlDGuQxL8eUu+18d628J1simzQNoQwXcMhnSwNHN5u8vzCG90VNsIChcpA2LyzYbjM0kA+E3pSazSFbKzMi+c2Ltxn90DTlrWCvahsfW7dImgyXZqv/rUROmOg0joTAijZuu0M6bcC+jkXN6zHO8LLwe3KvpHk+ZwzD+GAaKYhB+Q5nKqMRq/mdacoMZy/VRWNgOIULUUU2tQnaYPGzEBp2WXspAGedWjCy/OAdWY9AHBaWvghsUjQI4sOkR7XI3Nskw2TTiXoFVTRRbXW1ByscYM+qp0KZAETjRskk2sdc3mRPRqVNuUcLEwC5F+cPpnpRkSSQMfaN7GeYBh0Y29MM5BSPtBQX3XMoU0shDJxAuvWgD0g3COi2aaaGkDUSoQ9ZKsUGIV9gi8jLsetkLDLZYPwALDXooA2WcR9qSW/A/GTxA2xM0zITi4OiGQQ0EF9J64dbcsMDvYpldEPNdw06mYwk20dZqzAhtwXBotcmciFfWWQQ84TDLAvQ529WjzCSb/t8LjUi3E2xuZq6wC1Ij22FlIrjYnPJk0/sMupkQ7Z1BjXrK8kINQ0p1XJ5i95ncZOuoCun4yDhQRhk8N2GHUCzwcqVjzMAy7TBy3lPZ5Tn3P/uIveXBwCTd8HGOFQp2Plj2H0fOEzHmRQw57MQuwgLGV7aYEfqOqYtIbzbNWGzqbXafo2lmIWxWWMSiWmcnfSQGqbYSMI1c8x8BijTZyWSjbJOosSHHcAO8R8A9kO4tlRmmR+Ep6SU4fmMwYwLvkwZ8ME4aRupjMIVBbAjpJposVf3vSZ1osNgbit83HaZHecdpNqgTD9j5eAMyUMa5LJsMUVKMSKkUYWo2sxxsAaoLG1Q0aKMJyH4zlWZprAmX4W+aHdadKZdgzkDJYy/GjcJdCgrNKFAMeH2LYQAi0MiGNWISMJhNEzbggHzllp4CcA/EenQMaKElRTLBrGMjT2+UqVHGDxswdtyDMBPqJFFDlDKh2AigcqaOKowQx6ps8CjG3QbMKhN4ZuANyEAZ56YtJXb8D872McBFDI1sisFaPc2GijZ+2IRtd6nz4eXYplGGkWxqnJ/MrSC91QaDmVB4ihNlmylllVk8w3jjnRkWcTcwAh5dYs5O26GYev86ToE26UFqeI9IslrewOlWKQ84hQBQGA3lAafPR50B+nxUz4PzmhbKxqvsvdB11Hb9Xy+xyYIMTUjN24EFoTgijR1Odi7EII97L2WgjHMZ1tkyARAAFNZSnIfpGngw4QMGYtzjsCCULmLKLkIlBcMxow57oReLLFIfi7CTMOsw7yozxzIY5RIzlqNJdji14OEULcPDyQWRc1jYSdc6rROhHS2p0JQ1bSrjMmQYvh4dP1rQC5NdgTc9ohkMmjGh2Hh6RrcS/m+0BZsQuUU26G2HQn1gUAPqOdtsMbSycNB7KANlnFsD0xNgHtCNlNJsUaHFQjQlSKN3Diz6KTaYcReHJ9N6k0WyZxPMAqJNlr527gjSO33kCtIjnTAlyUJDtA3ZdmIFpkejxJU50JobGncdGN03EJtLY0207oY226G9F6h1R8eP7rlUqBFKBz6zBBvZjEKYF6ybok4gdSII33wMQpIS68xDqk6xc5bg6SUpVmN6tSlmgFCGpV7KQBnnRgyLRQxKmsLCVIpw4QfdSCMKI+eNYaY3mmLex0KVXZAWhI5CcuA706tIb2OWWQgbdba55WDToxZsSBM5z57Py7FNv3QvPLFJQSilUoRGE6Xwo92OcZt6uOfS5jdBZwYyV9kAVqYgPIX11cKGTHEWspkEXN81dI69mJ072EIigT6K368eYIuPjgHFcse32eDVR/qPEpHKYBnnbSu34H/W1sb8vwYaOc8s0hbGwWLAm0NMjzZLoobFdIrBKYbiDGYSMSxy/mJxGukdTLHc+tEs6wV+ZodhxxNxZlHG4xAbn2HriDKaECNbEip+pBHUxDo00mB2rU5ZXqCfQ+E31NmpjzE9mgWMUuhiwLAPSvlIDTwaAadOZ/EIM2ADjb5SLn1YzJu9xIIv5Vn2LitzwRrZlBGolzJQxnk7blSaDuaRaHFLCUYPOEUQ04tAo74xBSc53Lhpc5+jKdZB5XyJWSQTSZZSWKoxL4kWvBbq7EQr11hGYTzHTt6VCTaxbZnpuStsXyFOJ3VUacCANkGhUTGP+bf4vXiwtwTdcyn8pgwj4EHPF1ysB51OOj+9NKRSzAVbxJjcBAxzsPCxPsI8uXaCRt2YGoVcUcINZ4e2Le6dDJRxLsOMURKpwkU/8I0HDaPBzA9xSM8F9Sifdxa+mGNZZtRvNdmEuVpmpNBlj+1u7Q5MQ8ZYBNxrs00/nYY89UVmnLfSsECp6P99OiwZpCYsUq+PBluAiutuAg5u0ToDuncG3aCOOh9UKBSDFExKklNiE6Y+Ap0kOK8JvprQL+4qMjXaIZQKYbCRpOQ6HAT6PnsoA2eckwODGPQ0Ik2F85oyPfp8yRXIagE95UqLRWwv1SFfOUyZJGHO+utX5pFebpLd5x0jDA5DnYjVl1kmghY/RvIsfEedTg9445QJKloPlg8aN12BWGcMtYN7WXWK6VHYDi5EhE4ShX3QgE/QzhU9+9wdyHMOGccaef8DiDHglK6TYsfL7EZboGu7JDXzzKTFxdE9lMEyzi2btKSwiS4OevAmmM3EnQjoSFKMOy0Su1phRmEyxyyLq3VmIWSgJZNwmVF/5jKzLO48xHjccy47sTenWdjPW2bhyXYNwlMo4weBtUAGG0r9R4vGaYQYt5uHxlYcOgNBFyJSY5m+F/p8FOOeu8C8jwqkDaQ87glonNPMlQHZyihsJkTHHHcIpQ6ZhdfD7yWMnPdWYOScBF8pPAUXJ7FAL964aao0aA80FWMvdLPBdtLLRcbyMpdjllMmwU56CjNZKjBnZ36YVeu1vGCxjLFtGFlJ0uYWANYCCwoxRjroqBHNyEM9inFPLzI9GhBx4bjXR5gezWBQg6twhK09mBwVrPnH64GOuwGY+tQqO/fKM+wm4xV2nlQn2fWSa+z5aEfSWC0sCO2pmA7bcMhmQztKdUCTJEmqBcyMAGm5VWVkHzgaQ9vGjydYwWQH9xBnMpKAzYu22Ik9nGYn9lKZAWanx5lFsmxYBqO9yU56d5NNUBKpqsyyvSW1BAvgmD+GI/U0skyN18Ix9j6rB4KlKKSR88Qm08N7LuTYpgaQBxlGKPuNU2SGqFNiUIzyjH+96gFm9FLHqp0IlgaTYupTK3DQaXF0D2WgjHMLC0JJ2+QOrOim0S2K86OFpOVZptdKBptua8KCwhTcuVsdtopTMQZrKcCmR54X7NJuw/dSqjNjOQopGNuQStHL0U53/nXiJWiMQKMX85XT5rw0Ag5hA5kr7ILVA+x6lmL44R5P2WHgksUsKNEG00uvsMOBBsJkIAsRfi/+dWiRLLUHqONBs13VSXajyU22mbWg89FLGSjjnLK1kBo/GuWgxTu0SQ8VL88mebQGFzGciZSiMAmr4JqQyH0bYuOpGBgiicJMRGEbdmajsJYWPAiHmQVLseqdin+9CDRiGhDeEIeNw6hQzDmmr51nejTKSHs9UAr+oHtLUKOe1hXRs8EDnTclqZGD3YdhZoAYsLEGLFqFDgtmZqK0m5TZkELm+tAS7sNbugnpsOhDkAODOYUpXznsjkcFF5zU2H1uNRhF4VyS4XZi8AHnMgwD8LWLR5DezDi7XjrOnBYnxXbTThtSMEJjud2A8JQ4bHrkAJq0GHRwA+4QSikfs4tszLePsbGj0TunwPSo0MwAhRfhuqmAWWWoQYkb9UAnghYVkrPdhdkELw33P1ho6VbZfVYm2X3SPYnW5vVSBso4N5bh4UglsgdwYhLnUE0tIzWcejYQLkI3RGrUTyXZCVqH3g7lVb9YZhW9P3T8DNJbrDJM9qVtFn7Np1hYk0bql2Fzpsgmq/bicDT/C6KdgA48tEI5DpXplWahkU0j0hQugo20YPVokyVKbUgx9bTTZwNCSDFTCDzDKKNJB1Aw1sbYYnAL7B4TGyxoU5pniyi9yqzlZobZZdkLMJ3XQxko49waisUKLrpMowfUOKAbIj3QPIjeoBXyDQiia+Ge10wOJJlLv1xjL3Qmxarnzq2zymMnziboOCx4LQ8xr7MEo1tmgzlzXsb/YUgYXqTbp7kP5p+GkWzaMI5mFOj16F5NMwOUp57ilmmgiJ6ZMQjXovNz7GlWmbvxgP/ATRwSUtBi3nYSUtDCd1mDnUxTa+wcakzCxd5DGSjjnArxsCklIu6SBueOCx1CymFM8asd2GBkp8lu9GSa8XlvGXbyutC7asKTMALDRtkk88ocSN1Y8tiJTSkmS2LzhTb4IQYzTc3SuhSMAYcRVA/uLZRRge6d9PloQ7Wg4SKY+o82WYLOAHV2KAsRPds33sayo4SZhMaWeK0cBZ0ztagXXBZCktpuWBDaU4nVrYbP+jcudo77T73Q6IHghhijjS3oQQgXFaV8bGWZcXcqy/A+ZyHn47vzZ5HeN0qHkV4ahrf+bold79TYKtJ76vnjSE8Qyx1PsxM0O8Ii9dUEm5/miv/TsEahaDRyTo1eGiEO2Pmge5kL6WTpnkuNbGwAUQYwaBjiuinafAoGipoQJkQZVJyyf0OUMMxJnKKQOmS0SNYpsAsaWHjjQThML2WgjPNW0mjrlH9De+i8fwNo6V0sPEKLd2gjBkqzhVEfEZgjh+3RaYTYgyfMSJTlnmchcfwTayeQ3liahUMXIavMW05eRnrPXZ5Beh0ITykV4IIArCuSFAWdRQ0s4qYRPwxrgUYhjSx3aDMaCIehjd9oJoLCdqgBVIE87lTomUIj7m4BFnFHmaFGnY8oYF6h2GoOq2XvkuL+S7PsZbpFCPeBhau9lIEyziU2+Qrz/k+L2yW1R6M/NJrWhMU7tMnBJnwxERhm3GmzsNjdyQWkNzLNnIEnNu9Cems19j4nYVOnOw6ySP3FDWY5tSps0482KHbAv0oL0pjGVoMttKQQPZx1hEIpJikmm+7xDQjtoyQDtNEczY6ml4Ol/6uPBAvFoOuBGNq5y7BvxmHmGdMOoRXYLIlG+GldQ/oyxLD1UAbKODeWRRHarv+JQFNttLFFitkwnBYKPh8uTqqyjfRCaQzpnR6+gvQeX78P6X3kwN8hvbn4BtKjMpJgm1QJpnaOZtnzbVSZE9GCVIoqQerGDLDO25DDHZ4vNJtHG5VheAMMiLTTlD4l2FQ3ZtaCj0eL8Kk0hikJNVOjsJ3sIptohTlYNNnx/4C0TX0EUiJGmmySUTtJTXaf8Qq7z8YYNJR6KANlnFtzE5yoPoVy4FIu29oE08MScKo7y1ARiryd3eizOweRXirGsANf3rkX6RU8BsN4ePzbSO+vdk4iPQoTOrPDsP8jSYYdP5Jn4cLVSbZwtyv+My2VCrPSKhFmbVVYeQKG37jrkIZ2EuL+obODi9uh89GG3ZVpR1kaAadnHw3cJHYgdBF27KRGNoVUuDv+53V5ChrnkO6xPsb2FuoAxgEOX+JORGM4WPa2NyJ9YZwbYy5JKklqS2pZa08bY0YkfUbSvKRLkj5krX3dGI+NSKTbuQOQA7CrulzGtoS5bCn8hmLFaBSOOh+bMII6l2M3ejSzjvQWaqyardlmS5TyuJ9IswLbp7dZs6TVIltIjTrb9e8/xOBFeYeF4eIgdHSxzIxzA7umWhcW5RaChdHgwkfYtfh26TaIsdyUMvB5BqnYOhVsjwHqDFCaQtqopw5oA2kEnMJFLDQIaB0Mb7oI7xPOsV5KXxjnXfkBa+3eHPejkv7SWvtxY8yj3Z9/8Ub/Cdlw0CKGC78JGQcwBSNkDqCTtTRPNzb2QkdTrPrqjgzDCRUhF1U+zjAHmx0WsfXgiR2FKZMmLbDNsAj4OuwhvlBiwF4nxhZEFGAOOk1qbTE1QaPeyzGj3tlhN0qdjw4sNqfsME6B1t2w61E9ShW5fYIZ2TRQ1MzA9wmjtql1iI0H8FhJigIIRycKjWxaXwKgN5KU3GCGS3UC9i+BHVBjtbAg1I88LOmh7ve/L+mvdQPj3FjmqRHMHsX50Ur+6gy0zmmUisI04QEahY1XLqwzzPnhDEth3J1eRHrZCDsJN5IsZXKxwXLWFyvsfb5n7GWkV4De47eTDPD83AJjh3FcFgKqV/1bCCMTDE+xtckcuegqs2KizWANBBrht5D72Drsek16rEZpKp89Xww2ParDAluaVaVR1OwiG7/6EHMeKTMagU91YtBRhQ4L7RBaOcg8R5q9oGw0NPjZS+kX49xK+gtjjJX0P621j0matNa+lmNfkXRjcKplRjMpxBk6x0Zz+wSMaNbZrHM3g43iUKw67Y6XvJcZvbTzZibKCOcfzJxHejl3DemdqUwhPRrhPw954+eSrCD0UIphx6/m2bjTJkuLq/5hV5UkTP9HmTHSHmeOR2SZ3aeXg9hxyJhDjXp8PRipp4ENKi1Y/4RhJtApa8D7TGzDmogimy+VNKU39H+f+YsQWnQSYsdhh1BR3nFYL0CzQUHVKvqRfjHO32WtXTTGTEh6whjzj8Jw1lrbNdz/iRhjHpH0iCQ5mWER24lQX7WSbCFiztYtyu/E1KjQjZt2cystMZhCtck2qe9cYZHXzD3MqL8vdQnpfX+W6ZU6zCv76vqdSK8Gw2Jp6CTdN84yHzXY9WMx5T/MaCFYNpNluPhyiY05b6vOrLROAkbTPEruzK5HmadaGea0RGsQ+w9fC2UFSm6w91mapeB/dr3qOHsx1DAk3TArU2w/oudzKwXnNGzMRCFCyTXYiXsoLAi9plhrF7v/rhljviDpAUmrxpgpa+2yMWZK0jVDiN0o+2OSlJqYtYRDFzGoUB7OgCPSNFpBuYjphk/52N17GE5oLMNC9Ylhtvj/bpMVTH6nyFhl7s8zqsjTqVeR3tkU4whNQjLpHdwDPlg5Ob/kW+fq4/PoWpXTzDrotNgm0R5lEfdYAVLOwdSzW4QwhTvZ3tKmPdKhUEx9ci1YLukWxWTD7ti1sYCbCcHiR0L/3EoGG3WjGPfENstCVCegwwnhPmETomuIMSYtKWKtLXW/f5+k/yrpcUkflfTx7r9fvPF/xjYOshhpUyAaOacbIt3YKKa+mWcHRQ3yv4+57IXGIuwBOzCqWW6yASw2mDd3Tw4aXLCq8N35V5DeVY8BWCtwQSxWYYoGSgmMe+V+aGQXaOtNphahMAx4PQuNpsYYtOoLkDWHXQ1nFCzEqtPADT2LaHaUnkVYjzbXgu+TQDhwEGwH8pVDCsYOrPcgXVMlyYPQoqFng+0n8kZk341z7WLJv2CMkXbv5w+ttV8xxjwj6bPGmJ+TdFnSh97If0YWpLvjX4cuROqVe1nIogHvMwYLNNvJYD3QzSLzko4eYouRUhtSo74EjfOlOoP7vBRlsJ37k5eQHpW/KDI+dspTX/GY4Vvz/C/44WEWsd2CcywCjTvnJRYhbgzTNCBTUyLYPclCeIqFvPEUq44DTBRzTov1aJOlgJs6YWcHGLAJaGRHG0yPRpZr4+y8zF1mUcVmnhlY5ROs47QkiXEh3FD23Ti31r4q6S3X+HxT0nv9/F+xmtXY8/5xl6VZ/6uY0B9J0vYwTLGSToPijUIsTGNFYBFVBxZtpZJsEVNjeS7N8DcnsizC/3KJpRSWa4zlhTY9mnOYszMTZxQOPz79HaT3hcXvQ3rJODPqr1Mq87pCsyVxl1lN8TikOxtn1g+N9EYgdtzSCGoMegMwdN6GToS7SSEASE2C8A1qnFOsemOIcnojNRywiwPOckovmYCOXKRFI+5ITeUZtrdQu6wfZd+N81sprYTR9p3+D7YGsGMMXMEUZhIrUv5Odj0apaIRd9Nmz0cNhGeuHEJ6XyuxCOqP3vcc0nvbMMOAU57zsxXGuvLn2/cgvRjMIU+7rE3hQ5PnkN7lGuSPA/K1HdayM5Nim8vWBqTDyLGT1xThsUPhN1VovMIIuGIwqgkZuYZfhhA9CDmojUHDcIsdKpVJdr30KrseLWKkdVqk+JHCYzGWG9bYUScCliIp/cdfR3qVn3yQXbCHMlDGuayQUZm/5H9z81KwNfBRGsmGmC8YAaetpCl1I22gkkywVVwus+jk2DQzCv92kRWEdqbZ+zwN2VpSMML/fJkVrm43WW49AjvWxaEz0IQnbwRjMfxLqQKrzSvs2ZwJ5vk3mzACDrOAVHDEnV4QXm/7BHufDtvKlF1gN1qehllVWEoRg1zZjVywgTcyYZwy5ODPwLUHJ3UM17zBOop3scyoU+w/ovOBMs4jbckt+J+01Qn/E7YKCxjpWU1xcJQFpcqgx7gRA+3mlocFoW3YYTLnMrq6ZowttS8/yyLSR97OYCYnE4xq0IOd2Z7aPor0KLXht7ZY86IoLCBuAiaUQyMM6rNVYww2BQoB71CKO6aWvsLmWGUWpuQTEDvegh1QYZfkFgykOJAHnDbpCRrL3YIBNBoBd4swUg8i59TITsGOnRSyEzTTjiwLUKQX2bneSxko49walqprgfGkRm9lNtgucOXDEKsOsWmNg7ChSYKlyA9mWPhneGT5xn90DVmoskLLQp15LXccZff5zM480ttIM4jDu7OMreWeqQWkV+yw9zkEmyx9e5NlBhyIAydCHc4YhGEQPL0kOZOs4LWSgJmBGjvmohCLb5cZ1I5CEGl9EA3xU+YOD95nahWpqTYarPPowOZFzWn/li+hX5SkOqx5y19gxmttgq4FWpcCoVMzlOO6dzJYxnlMqo/4X5CZZf8DunWKVtYjNbWGoCJteQ33tajDDjQXwlOGnSrSy8XZZnN6mPGHX0kwzPLVCuMfK3psU3xy5RjSG4MtXimv+tH4OtIrpFh0mXaGPV8Z962zUGYOYLsNG6PRrEATwmEcuJc1WPguOcGcgWaDZWfaKRip99jzuSzRgo3QxjCEfcDgJKWNh74jFornRo0T4RLCbHFpttZpp0/cZwU6gLRYuZcyWMZ5hEXBywTXC1N0w2eY3sZbaQk5DY+wna1dZxtUQwzXUoF4mE2IdT6RWYF6LAI+4jDDgkZ6KYvNNwtzSM+NsEzL/YlLSO+Uy2A7I1HmfBBZqWaR3vwwS+dt1ZnD4iXYWt8qwgZSLnP8Y1FIAwcj7gbuuRT20YKUiDG2tWCKQgpddIpMjxr11PBFzQzFMib0HimHext2RKdwmOQmWwzxInsxjeH+M4X7745uQkybLWTiNVFPqzTPNm5nC1ZZG9gqG3ado/jASIPt3EWYjlossYj0JsT1vnOcRYgn4Mk0A+E+Dtz1v7nCsNzfXGBOxE+fegbpvT3N2FqoRIHFtVFmp7xNM2ur3mLHQNph2a4ohNG0YcAACwy9RmuQrg5SFEYhHIY6A9QwjED8McUtU2cgBiP8TWick3HA5yycY5RYgrKuUKOeFpLGy2FBaE/FRlkXMrJp0MJHmkrMXmR6lWmmFy8H29rZy0GMGTxA3Rg7Ya6sMXjKdo1NmBakmHxg+jLSO5lmmYHDhzeR3oslNkFfLDK9i9UxpFdusYm9AzjL3zN7Fl1rqcbgMBQ7fvHqBLsepFJ0ppnzQWE7pszukxpO8RLT81hLAwka9dTAa8OECUQzYSciAaO2LRhdJrXtlPu9PMXu0YV4ekITKUnNLGSViTCPLAK45nstA2Wcmw7zshMQlf8AABkeSURBVAkUJuhuZ5QdJkJbEdNO4BDTRqkil0rsZKKp7ulx0E5W0vIGM5xoUdpajUEjcjBsdCq1hPSyQ+x6Begdb8Lw1laDWRYEJrRSZ1mdDQhPqTRh0VaCWWnRDAuF5pJwrlTYXLE59nyxy+x9NmiTQnimJKEz0BhmejQwRZsludCApSwvtJ6M0AjHquxcMLRovAKLo6GdRB0dWmdAnYheykAZ59awyUAKVSjOD6eVoNGbgKwy5UNwY4OFq7E0ezHHhhlloAO9lqevMmz17DgbiEqTeUkdSMXwxecZT+yB0wxGM+ewiPtYjMF9qHE+lmCY85mU//VwucKyMwmYDXKibC204EFPI/UTaTYG2yXmtDgptic1YMtyysgVK7JxgMkgLNh4hXZTk/KVQyhGEjZZIrzq5WloEEChHUI7UbYWaPMil0bA+y9wPljGuaxQFCG95n9kDNwxKjPQ6M2zA7QxzxaVk2Y71HiGsafkEyzHulxhkfMfOvAy0vvAvazTZxyeTOst9nxfWmf86D9wkr2XJzeOI72IGDvMVIoZ53elYYQfAntJ06PZBKPfWGyw7MwTF04gvQcPXUJ6T75wB9I7cpI5uGnYObXpwYJQSENL9ag02XTBASZBe5Ia9YltSGoA7zO5Dp25HExTA6FFsvVRdo/YQWpAmtYCzCiExnmPxQiliEoHATc6jJxTDy1aggWhMRZVacIbXWsyOEUzz56PRv1egFhn2mHymMuw3LMwsvzQKMMtl9qswJYWhI7CIsanFueR3vY4pFKEre4mXf9ORBkWblCmnXtnmMNCcfj3n2D1EFsNZlnUYLF5s8KsNGcHQgdYXANjqyk2nhZa0sJVB8JvCK2yxJ+vE2eKiW3/AbRGPuBGULRDKCyupVSKNMsSrYUFoT0V05Hi4Kyv+aciVisFK/kbwXaBi8CW1y3Y5U6QiWFrkeFs77yDUeNdLDCA5yLkHX/3OGxClGRG/ZzDeMDbxLuV9L5D7PleKbKiwqEx9nxFiK9eqUCnM+vfAnpomDV0qltmTE67DJJ0pswKYYpN5gBSRzyfYlbhehlCymDktTkMi9s3IDsMhG+0YGEnZV3xYLSXOh9Uz4PvpRP3v+fiJj00qwNZUKJ1phej2PEEhHil4KD3UAbKOJduwuPyex0HpsxGWeqLtso2tHkcpC2LxqFrDjHnYwkWeV0uMrhIscaMu88XGJbbibOw2PFRhsV/7ygj4n9P7kWk99Y0o5hswhP0bwp3Ij0q203/J/bFBogWSCqSynZJBWj9nN9mzDfTWQZJohj3QpU9X2SHWdn0bMi9CrsyQ3gKzf46sOkRLSSlLDYU1mLhoekx/12JLf9nZtuB9QkwO5NcZ55cZYadl5EmtK+gcU6dj17KQBnnxrLiToJpi8AIeHsIerwlSOs1xIxeF2LOG0W2GJ0su14Ewm/mhmhbPSa00Qttyb5aZYWPT9hTSO+hURbtnY6zcchB4OuDuQtIjxbY1kEYlUbA8/DknXDY9aYSzMiuQlDvhRJzBrDAOEMnwRTLB5nDSWEtFGI5eoatvZUHoLMDMxEUUlFj9dgcMhLxv7cktlkWqTJFO33SImfaLwX2gymy99J2+69F6GAZ5y02aWug7W60DtvE0gYcKTbp4i7buSfzLFwxNcXwq7T5zaEkKxL74OgC0ltpMVjL+eok0jtbYlFUykZDo5NPbrEiv+OZNaQ3GWeGYT7KDFjqBBIe/g4cgw7kLSvAKrFh6Aw02uzYKQLOeEnKppiVVmwwB7edY3t8xKOE3kyNdotc/z5mLUP0FL7P2igsRoRGPRWn7H++1MZgc0FaZwCpDSlfeScWrHHubgU86G9ABso4l2FeKDnT2hADTqMVmTF2EB4cYrzc0ym2k4467D5dw6IxJ5LMGZiNs0LLIxDL/Y7UeaS3Ncwi7i83WMErdSK+scEKQr99iXUInRyDbC0jDMNPnUdSpEkjxNSxujPPHKRGh0HD1uss/59xGGh5DXZcbc3CA7sOI+CQBYViwGGNM74eLSpMsi0Xw2golWJ6hRmG9SH/67YTp9hxpIY7hFIjm0oHwn28TP+Zwv13Rzcjhg2OC+xX2qjAK7OoQx1GRwoJFhUr1CEzAqQfo7Rlp6dZDjIGI8t3ZZgz8NYka/F6HMI+3u4yTuhonrW3Pz/Gdv2zHisI/WrhJNKjjCYEOy5JTVAE854JBhGKQs9/C4KPv3yFjcGpsVWkF4OdPr1WsMVe8S22l9XnmXVuyuz5ok3YhRHaW7hBHbRShi5A6McB9j5ro7Srpf8X6gTcsTPqsb0lvcaCGtVxNgamBbnmR/rPFO6/O7oJ6USlZhZM9JL/Aa3SHQpWS3fWGZZ722GLo1Fm14tCGE0EFqBSI3sHGluffOXtSO+ZycNI74dHX0B6b01eQnrHYuwEPRFnm+lcjEVt74gzvb+qMIOSGrCEFrEMCzs9WCS72mAR8IkMcwC/sXAI6U2NsGxeo85YVywMpGC+cki2bGEBatBCgmASZ2upjbD1QKgNJakNo9npZf+h+tIsnJtwqsTL7Fyvj7D7pMw+jWHYmyAsCO29ELaWCPAKTRtuwBk2ySlbi9e4PYa47bGoQxHu3DGY33voEIOnUKjCr730g0jvQ8e+jfTel30e6Z2Ks6hf0rDNey7GrvfBLHN2qhCsudnx7wRuthkMo9Rma+GQyyBepQxzIhZyLNt1ucz03ATDKdQhBMCzkCi7GmyEv51gBklylTofTK0DXyds2aAaZPyIV9j7JIZ2O8FeZmqN4lqYGo3UE5tM4vUJNDPQS7k9LLc3KBFPSi/7H53iIf+bIm1UcHJ+GeldWGfGXRSmgpWAGPAJFtGkcINfPfQFpPd72w8ivUtVxo/eaLGlds8kw0j/2cJdSO/FPMOqH80wYOhynRXY0kLg+1KXkF4UhpwqHf+R82crLLJMpQH5Zydgd5iFCgMDZ+LMyKaMR9SYVIJZCM4SO1S8LNvj04uQupEtWYzlprzjGEbD1BSBhii5YHIdQrwgHBe3d4UvkxrnUcgOE6uETYh6KjYm1UEqi3jmHZdNgiGHNcT458e/g/SysPTchdVJ2Qi7XhRGsvMRtnP//MjTSG8ddjLd6bCo5hWPRQv/X5y1ZKeFj09vzLHrwcYyKzVWVPhcYQbpJaLsvZD3+cFRttaJIyBJW20G2Xl86V6kN55kvQkuFthaaMOGap06pI8rMj0aybZRpkeZO6iRHaEFqJCPnSJPqdCAXQyYBA1QRCpJTomds/RdQgIpzPKS2oBFuaPQ+eihDJZxbhishRSc0NTeRp3tNA8OsWYt1FiOE/J3SQ7MK0WgcV7osOuNRNjUn4ZUmJOW4XPnYiw6ecJlEfcFj0U1aSObFrQQ6pCOr+zBro/wdCIsB7RLK12zlIJxZZth1dMwAl6G2PFWlbbshONQhD0woHEeq7D7bEA+b9rIJrHDnq8JIaSjLzFvYP1e5uRSIRF+aizT5kXxCjTqIcsL5e6PVWCNXQ56Vj2UwTLOY1IdoD9I2qx2hEWWaQTuK2sMpkDp1XZqzNiiHUmpfOzwU0jvlSprPb7RZHjgB/KMrYVSPs7EWPXVCYfBkv7T9J8hPUBUIEnagUDUKtQrdiCAFQgt7CxZtmYp9/sjd/0t0nu5MoX0NiossNHOQeO1wuYK7VIYK0PDifn9nBIRRtwJWYMkxcvMqN+4GxrZtGgSYs5JXGr4DMs+bd7Nzq/cJeZQp5aYUb9zgk3O6iRbs7FaiDnvrVhWEEA2qRhsN79QZL2WyxW20bR2mN5bTl1GemuwMyXFhVJj66lVxp6ShlzLj1cZBGDEZTCoVIxtpm/LswzNUYfR4w1F2PNRSUA+/Qit3QBSspSthW3nFbE9YjLG2FP+pn4c6ZXr7D4TDhtzCocxgKFH4nCRJjtSUJZZkhzWYoDDaCj/O4TDYIFNlghbXPEIM16pY0V5wKuTlD0FqWHMeWIDYq56KANnnEfABkcmQrvGXt3JOWbEbKTZThOZhJMVRvi3ymzTODXJ3kscwmiGkzA3C4UWhG7U2fuMGBZF3WmyDI0TYRj3uTQr7JyAHULHIEyI1kS0AWTk5RqLLJNupJLUgif2aJy9S8pXnk2yA7QOeyi06pCuLs2eL7XCnAEaAacwmugahO3AQHblAOzGDbNyFFJB+MolyYJ1SzHnNCtAs0GUXpI6EbBGXV42xJz3VGyEbVTojMmyMMf351lE+hnLIr3ZOMOc08LADxx5Cemt1Bl+lUZCc3F20Gfg+6y22MnUhOGm9Rpz5ipNdp/rOyxj8qLLOpJO5Zlxjp9vmxWgHprwD0v6yZlvoWuVIXccdVg+v3I/0qu22EGYddnaK1ZZaDl5jmXlGqOwEQpkQYkxhINakD+cGk60OJA6Hw7kVafUjU22RaiV9P9iaGSZNnSiHULjEC7i7rCgW7TBXkxjqP9M4f67o5sQIzZpPWBXjOcY0O8DGcaz/NM5xj8dhSBwV2wHdg2bUnGoR+UjuStIr2qZM1C1sIochn/WoaG20mIWwnqLOVfLHjOcLtdYNRvp2ClJ4ym23gkbzSLtOw6lBOcKLcqlsrDF3gvuEQHritSAEfAMxMaPwe6NV9j1mtCJyF+AdKRTMPoKpyflR6fUm27R/3tJrcPupxBmQnnAmzn2UmiHULdIu7QitZ7KQBnnFHNOIucliH/chI1ChiIMhkHbWnRg/qslyBcK0230em0bbAFIwkDmB/hi4lGG5R6CfGez8W2kV4KsMqUUDPtBaUMnqQ4a0tBrdSDLSwR2GDk4zSBJdcsi51+JMcjVqxusN4EgRWEEGuc0Ih1pwIY067BpDmyAU5lkepCECL9PiNbi8CLAoEKN1/QKe5mxGgwuDcGiasgZb2Gn8WgjLAjtqZg2LFYB86ACab2erDBs7pLLMNkJyFdOadkIxlbimN4dyNFMn4/CaKgepaakz0clSp0I6FxlYSEpvU8qTesfikGNV2qcU0lDR47ysWdgkXOrCYvSomxP6iThmt2E2PgMu8/aCAwYQHYYGnF3md8vt0A7dgbbATVW93+ftJlQaZbNseGXYW3Xi2yylA/DGjsY4c++wJro9VL62jg3xrxf0q9rNwD8CWvtx2+oA8aGwIHbS8xN/kyC4TQj1COExVdxqOfEgjUK4xG2adCitArkyW7SgtAthuUeHWabYtqBlBFQ6PysQ9zyeoG9z1SSvZcGMAydeLBraCTFsnILGwxmQt9lCrKudNaZM9BJsrmZWGVr3csE6zjWx5kebcAXLzGDkhr1XgZmoCDmHMbB5KX936cDoDCSVBuFXWFH2H5bOMrWXqTFni9zle2dxbdMID1J0jmu+nrSt8a5MSYq6bck/ZCkq5KeMcY8bq29fsWhYXRNqIocMiO40Hjd2GLVJsk0ZDigHcEsW4xplx3YXpSl9zZL0DOH415fhTnPDJsva4vMcMpPspwundfVJmxZ3mLj3oQNaeJx5gTWVv3Ps8gUc6zcgI16KqUCgyRV4sE2h8lcgJ0+4W1GPLbpRiE/OmE2k6QWDBGTZjuSlGZJY7Uodhz6SJQKkxTmWlhLRu+RMt9QakMq8R1m79TG+88U7r87+gd5QNJ5a+2rkmSM+bSkhyVd1zg3LdaFjOCbYmWWEtwqMqMwCo2DahE2E4ItqKMTDG5QLbKd1M3AVHeDGXedMjthrMuicNQZSI8zCofiCqQciEP6uCE2XzzaWn2bOQM1+Hyxkv99opph1l0twp6tFIe86iV2PTfPDtDGDtwjCsFiwKOMVAazrlD6ONpunkaWaWdRuAVyYxnyo8dgywYSUKTc77C8RKYDu9fCMa8Pw74nB1kQjLLf9FL62TifkbSw5+erkt72uhqGeXhRcFbQqIOBRnb7AkvH6wC7UQNbJre22QEaH2EnWqPEDJnkBQhPybFNqp2F7Y9jkF/7WZYLtrNs16eNYRuwdiP3DJtn5YOwyA+Ogzni3+KyHnQcW5B/GjbbiSZZpL5ZZWM+9AKMZENjEmOroX/rBN3pE5YokPNSkhzYpAdHbeF9Uj1qE7hV/3tL/hXmkW3dy1i1YjW2/zXzbC/LwMJVL8UmtVOERBY9lH42zt+QGGMekfRI98fGM5/6D4yrMJQgZUzSxn7fRChvSMKxuj0kHKfbQ8Jxun1k8Mbqm/t9Az2R/R6nuV78p/1snC9Kmt3z88HuZ/9IrLWPSXpMkowx37DWng7m9kKhEo7T7SPhWN0eEo7T7SHhON0+Eo7V7SGDOk59SL3+XXlG0nFjzGFjjCPpw5Ie3+d7CiWUUEIJJZRQQgkllJ5J30bOrbUtY8wvSPpz7VIpftJa++I+31YooYQSSiihhBJKKKH0TPrWOJcka+2XJH3Jh8pjvbqXUG6phON0+0g4VreHhON0e0g4TrePhGN1e8hAjpOxAbcxDyWUUEIJJZRQQgkllFCuLf2MOQ8llFBCCSWUUEIJJZQ3lQyMcW6Meb8x5hVjzHljzKP7fT9vNjHGzBpjvmqMeckY86Ix5t90Px8xxjxhjDnX/Xe4+7kxxvxGd7yeM8bcv+f/+mj3788ZYz66X880yGKMiRpjvm2M+dPuz4eNMU93x+Mz3SJsGWPc7s/nu7+f3/N//FL381eMMT+8P08yuGKMGTLGfM4Y87Ix5owx5u3heupPMcb8u+6+94Ix5o+MMYlwTe2/GGM+aYxZM8a8sOezW7aGjDHfb4x5vqvzG8bA1p2hXG+sfrW7/z1njPmCMWZoz++uuVauZwtebz32rVhrb/sv7RaMXpB0RJIj6TuSTu33fb2ZviRNSbq/+31W0llJpyT9N0mPdj9/VNKvdL//gKQva7d/zYOSnu5+PiLp1e6/w93vh/f7+QbtS9K/l/SHkv60+/NnJX24+/3vSPr57vf/StLvdL//sKTPdL8/1V1nrqTD3fUX3e/nGqQvSb8v6V92v3ckDYXrqf++tNsw76KkZPfnz0r6WLim9v9L0j+TdL+kF/Z8dsvWkKS/7/6t6er+yH4/8+36dZ2xep+kWPf7X9kzVtdcK3odW/B667FfvwYlcv6ApPPW2lettU1Jn5b08D7f05tKrLXL1tpvdb8vSTqj3UPrYe0aGer+++Pd7x+W9Cm7K1+XNGSMmZL0w5KesNZuWWu3JT0h6f0BPsrAizHmoKQflfSJ7s9G0nskfa77J987Tq+N3+ckvbf79w9L+rS1tmGtvSjpvHbXYSi3QIwxee0eVr8rSdbaprV2R+F66leJSUoaY2KSUpKWFa6pfRdr7ZOStr7n41uyhrq/y1lrv253Lb5P7fm/QvEp1xora+1fWGtfaxf6de32u5Guv1auaQve4IzrSxkU43xG0sKen692PwtlH6Sbpr1P0tOSJq21y91frUia7H5/vTELx7L38j8k/UdJr/VkHpW0s2cT3PvOvzse3d8Xun8fjlNv5bCkdUm/14UffcIYk1a4nvpOrLWLkn5N0hXtGuUF7fZiDNdUf8qtWkMz3e+/9/NQeiM/q93shOR/rF7vjOtLGRTjPJQ+EWNMRtLnJf1ba21x7++60YWQHmgfxRjzQUlr1trBbOQ8OBLTbor3t62190mqaDcF/10J11N/SBez/LB2HappSWmF2YnbQsI1dHuIMeaXJbUk/cF+30tQMijG+aKk2T0/H+x+FkqAYoyJa9cw/wNr7Z90P17tpv/U/Xet+/n1xiwcy97KOyX9mDHmknZTfu+R9OvaTeG+1vdg7zv/7nh0f5+XtKlwnHotVyVdtdY+3f35c9o11sP11H/yg5IuWmvXrbWepD/R7joL11R/yq1aQ4v6B5jF3s9DuYVijPmYpA9K+qmuMyX5H6tNXX899qUMinH+jKTj3WpcR7tFNo/v8z29qaSL6fpdSWestf99z68el/RadftHJX1xz+cf6VbIPyip0E01/rmk9xljhrsRqfd1PwvlFoi19pestQettfPaXSd/Za39KUlflfQT3T/73nF6bfx+ovv3tvv5h7vME4clHdducVQot0CstSuSFowxd3Y/eq+klxSup36UK5IeNMakuvvga2MVrqn+lFuyhrq/KxpjHuyO+0f2/F+h3AIxxrxfuxDMH7PWVvf86npr5Zq2YHd9XW899qfsd0XqrfrSbqX1We1W6v7yft/Pm+1L0ru0mx58TtKz3a8PaBfr9ZeSzkn6v5JGun9vJP1Wd7yel3R6z//1s9ot8Dgv6Wf2+9kG9UvSQ/oHtpYj2t3czkv6Y0lu9/NE9+fz3d8f2aP/y93xe0UhS0Evxuf7JH2ju6b+j3aZIsL11Idfkv6LpJclvSDpf2uXRSJcU/s/Ln+k3ToAT7vZqJ+7lWtI0unumF+Q9JvqNnYMv27ZWJ3XLob8NZvid/b8/TXXiq5jC15vPfbrV9ghNJRQQgkllFBCCSWUUPpEBgXWEkoooYQSSiihhBJKKLe9hMZ5KKGEEkoooYQSSiih9ImExnkooYQSSiihhBJKKKH0iYTGeSihhBJKKKGEEkooofSJhMZ5KKGEEkoooYQSSiih9ImExnkooYQSSiihhBJKKKH0iYTGeSihhBJKKKGEEkooofSJhMZ5KKGEEkoooYQSSiih9In8f49CaZDAxKtPAAAAAElFTkSuQmCC", + "text/plain": [ + "
" + ] + }, + "metadata": { + "needs_background": "light", + "tags": [] + }, + "output_type": "display_data" + } + ], + "source": [ + "def plot_spectrogram(spectrogram, ax):\n", + " # Convert to frequencies to log scale and transpose so that the time is\n", + " # represented in the x-axis (columns).\n", + " log_spec = np.log(spectrogram.T)\n", + " height = log_spec.shape[0]\n", + " width = log_spec.shape[1]\n", + " X = np.linspace(0, np.size(spectrogram), num=width, dtype=int)\n", + " Y = range(height)\n", + " ax.pcolormesh(X, Y, log_spec)\n", + "\n", + "\n", + "fig, axes = plt.subplots(2, figsize=(12, 8))\n", + "timescale = np.arange(waveform.shape[0])\n", + "axes[0].plot(timescale, waveform.numpy())\n", + "axes[0].set_title('Waveform')\n", + "axes[0].set_xlim([0, 16000])\n", + "plot_spectrogram(spectrogram.numpy(), axes[1])\n", + "axes[1].set_title('Spectrogram')\n", + "plt.show()" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "GyYXjW07jCHA" + }, + "source": [ + "Now transform the waveform dataset to have spectrogram images and their corresponding labels as integer IDs." + ] + }, + { + "cell_type": "code", + "execution_count": 127, + "metadata": { + "id": "43IS2IouEV40" + }, + "outputs": [], + "source": [ + "def get_spectrogram_and_label_id(audio, label):\n", + " spectrogram = get_spectrogram(audio)\n", + " spectrogram = tf.expand_dims(spectrogram, -1)\n", + " label_id = tf.argmax(label == commands)\n", + " return spectrogram, label_id" + ] + }, + { + "cell_type": "code", + "execution_count": 128, + "metadata": { + "id": "yEVb_oK0oBLQ" + }, + "outputs": [], + "source": [ + "spectrogram_ds = waveform_ds.map(\n", + " get_spectrogram_and_label_id, num_parallel_calls=AUTOTUNE)\n" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "6gQpAAgMnyDi" + }, + "source": [ + "Examine the spectrogram \"images\" for different samples of the dataset." + ] + }, + { + "cell_type": "code", + "execution_count": 129, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/", + "height": 646 + }, + "id": "QUbHfTuon4iF", + "outputId": "f5057001-c343-43c3-ca64-cff3030d3380" + }, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "/usr/local/lib/python3.7/dist-packages/ipykernel_launcher.py:4: RuntimeWarning: divide by zero encountered in log\n", + " after removing the cwd from sys.path.\n" + ] + }, + { + "data": { + "image/png": "iVBORw0KGgoAAAANSUhEUgAAAjwAAAI+CAYAAAC4x9CRAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAALEgAACxIB0t1+/AAAADh0RVh0U29mdHdhcmUAbWF0cGxvdGxpYiB2ZXJzaW9uMy4yLjIsIGh0dHA6Ly9tYXRwbG90bGliLm9yZy+WH4yJAAAgAElEQVR4nOy9Waxl2Xnf9+0zj3eu8dZc1RPJbpLiYEpRJNGTbMh28pIBzkucIH4KEiAKDARIHhIkzpsDCDAQIEBgJAgcwwiSWJGixAkkihYdmRIltZpsNrvZNd+6defhzGMeqrrW7/vvs1dXCa2uo+ZZQAHn1j5nD2uv9Y3/7/8l0+nUFmMxFmMxFmMxFmMxPssj97JvYDEWYzEWYzEWYzEW4097LAyexViMxViMxViMxfjMj4XBsxiLsRiLsRiLsRif+bEweBZjMRZjMRZjMRbjMz8WBs9iLMZiLMZiLMZifObHwuBZjMVYjMVYjMVYjM/8WBg8i7EYi7EYi7EYfwojSZJ/kCTJf/my72MxnoyFwbMYi7EYi7EYi7EYn/mxMHgWYzEWYzEWYzEW4zM/FgbPJziSJLmTJMl/nCTJ20mSHCdJ8o+SJKk8PfbvJUnyQZIkB0mS/JMkSS6+7PtdjMX40x6LPbEYf9ZHkiTTJElu4e9naaokSX4hSZIHSZL8cpIkO0mSPEqS5G9lnKeZJMlvJknyK8mT8Q+SJPn7SZL8WpIkp0mS/G6SJDfx/Z9JkuS7T/fNd5Mk+Zmn///NJEn+GN/7p0mSfBd/fztJkn/16efM/feTOBYGzyc//nUz+ytmdt3M3jKzfztJkj9vZv/102MXzOyumf3PL+0OF2MxPt2x2BOL8Vke581s2cw2zezfNbO/nyTJKr+QJMm6mf2/ZvY70+n0P5iGnk7/ppn952a2amYfmNl/9fT7a2b2a2b2K2a2bmZ/z8x+7el5/j8zeyVJko0kSYr2ZE9dfGpQVc3sq2b2bVw+tf8+2cf/szMWBs8nP35lOp1uTafTAzP7VTP7kpn9W2b230+n0+9Np9O+mf0nZvbTSZJce3m3uRiL8amNxZ5YjM/yGJrZfzGdTofT6fTXzaxlZq/h+EUz+5aZ/ePpdPqfym//1+l0+i+m0+nIzP4ne7I3zMx+yczen06n/+N0Oh1Np9N/aGY/NLO/Pp1Ou2b2XTP7OTP7ipn9kZn9jpn9S2b2jae/28c1Zu2/n8ixMHg++bGNzx0za9iTBX/3o/+cTqctM9u3Jx7BYizGZ30s9sRifJbH/lOD5aPx0Rr/aPySmVXN7L+d8dtZe8NM9sfTcdfC/viWmf2CPTF6vmVmv2VmP//037ee8xo/cWNh8Hw6Y8vMrn70R5IkdXsSpnz40u5oMRbj5Y7FnliMPyujY2Y1/H3+BX//35nZb5jZrz9d588z3P54Oq5Y2B9q8HzLsg2exXg6FgbPpzP+oZn9rSRJvpQkSdnM/q6Z/e50Or3zcm9rMRbjpY3FnliMPyvjD83sbyZJkk+S5K/YE6PiRce/b2bvmdmvPsXZfNz4dTN7NUmSv5kkSSFJkn/DzD5nZv/H0+PfsSdps6+b2b+YTqfftycG0p8zs9/+E9zfT8RYGDyfwphOp/+Pmf1nZva/mNkjM7tpT8Bqi7EYP5FjsScW48/Q+A/N7K+b2ZE9wZ79by96gqcg5b9tZg/M7H//uEqppxicv2Zmv2xPUr1/x8z+2nQ63Xt6vG1m3zOz70+n08HTn/1zM7s7nU53XvT+flJGEsDii7EYi7EYi7EYi7EYn82xiPAsxmIsxmIsxmIsxmd+LAyexViMxViMxViMxfjMj4XBsxiLsRiLsRiLsRif+bEweBZjMRZjMRZjMRbjMz8WBs9iLMZiLMZiLMZifOZHIXbwK3/7v3lWwlU+nrhjvTXYSjhUPvHf65wN36sc+oqw3Cj8PaiH703FDJsUw+dp3h8bgdGg9tifn99NcFvDWuK+V+yE3039IRvhu42H42efO2f9jRTb4RzVvZE71jkXprm/4i9QaOMc3ez74Bg2/MH6o3C9cclPHt9T5SBMgp6D77eyN3DHOufL4Xwr4XzLt/33euvhOScFec5eeLby0dCf/2x4wXxPiV9K9p1/9MuRWfl0xl/4hb/77EGKR71n/z9a9lWm40pYH7n+2B3L4+9k6I9Zksw81rvgyVHLO2HhTEt+G09zPEeYxGTiJ3S4Gu453/X3MS5jfct+LG2f2qwx3PD3mMO1RzV/j8NGOP+w7i9QOQr3UtrvP/s8LfrvTbDWC6d+LY4r4XrFt2+Ha7113Z8Dz5nv+Tko7IU5Hpxv+mO43gTvmtc1Mysehfsf14vu2ATPU8T5polf5lPspZGc47f+z7/z0vfE1/6dv/dsT7g9KwXACf6m7DczK0D2JWN/jL/rbGBfydbJ96FPRL5RtvJ7PJ8OlW9Hr5TCtb0Is2TC+w//r/qqdBq+NxHtu/Kr7zz7fPqXP//s82DJr3uef1TJ1mW8pyf3MnupNO903d+HrwWOxXHZf7d8HM5Zanl50l0P98n5UZugeBL01fENf4HqQXi4/jJsArn1cTn8R6nln/N3/4f/KHNPRA0eLthix9/0oBnOWQhy370MM/9SS6d6jvBAje0wCYe3sm8r79egFds85h98WMOE4ZT5vvmBn+mioLE1xuLipjHz8zGseaGUwwbWjULjblAP5yifZi+m0ok8J5RHbihSBn+Oqrh/mUcuoO5Zvwh5j3mcv33RP2cBmy0n0i7fD88zkTmmgi50w/dyIvjmYtAg6WAhicFDYUPFb2Y2XAqCs7x14o5Ny2FOB2eC4FHhxfuY5r1A5PuaFsLCzw3E8IKRk+v5hUmDtfTetjtm1fCsvevr4Rwj/5y8j0Lbn59KbVyW/TKAkcZzihjjUydiVA7PBU9o+LOvzryumd/vE3EW8u1wX/m+alfMfyH8Ts+fPw3KZLRccseyFJAOGos6x3Mx8MiUwaNykvk91QV9KHU6j2Zejhdo1IihXIahPKp6S8PJHIi3gshxGhDqtFG3FduiyxrhXppbYa3T2TXzcnYixtDpL34B1559XTNvLFL3Pjl/+Nx44B3vcSXcY385XPzoFc+DSFmj5+f8UPc++Zt7GkZ61c/jqBL2lQY3CrAz6Aip88t7VF0cG1GDh8qtt+rfDm8gh0WeF4XLSVGh3VuF0EjC+fUF1/Ymmcc4YY273lLd+Wpg8eaLU+9iUkwyj9G0rG0HK2H/88IbhZ+p5Z70wjmqe2LIbMCQgfXfF6uex/QeKTiHdb+4aPFzYZRO/GZoXQqLkBEjM7NxOdwLF68KNBpbatgVT8J/jOoakQifaw86zz6noh9zMOhtT+phDRSOvWQYrMNYEYMnYWTzrI8clL77XvjeV4Oi1gjGBEJDjRWD0p0WYQz35By1oIBHTXHlMAavXHB/F/fbM7+n9zhq4vx1Lz/6K9lGemk/7OPcaZjXwYUlf71jRH8kskIFx/WV80vbim0YfWrUYB7zhx13aLQRZEu+E+ZfIzw0YHUdGAyl/G4wfDuvnvHfwzZLBvNn8NQeh0ktH4Z3cvSqdFHAc6jsoAwbVb3s43cZ1VEHlzJmXBIlC6NpXKTSzo40DVbymccq+34hUYkPYExo9IHyWZW4y3ggGKBrls5O7bHf+/21sP5GNTH68Nzr3wlOzNHXzrnvFVvZTgbtgLFElzjnFURq1HCkDklE3Q6a4fz1R0GeqGzp0aERwzc2ogZP7OWMGfnAg3JRmHkle3JFwr1YDM7iT/wDnF4Kf1/4jhc8/fUgVPe+7DfYBA7VGKfXkDFdxXFOlXj4vPdmUHBqeHGs/dC/nNMrYbLUkGGozln1Mt+cR712b4kn8cdo8XNxtS94r5qjdTESocLmKx9LxAAC6HTTv+vcKLwMRntSv7sZ3mFsjl/WoAc/WgnrobjXku8holX1c0HDfyJpmukrV8I598NaH656L6zQCmtstOSNlWEjXK90FL6XE+Gef7AX7unMqjs2xjlzXZG4jHJxbbS80ZcrwYkZaXQ3HJuIcupdCGugWOe68Qsi1w7X651dc8fKB5gfGNi17/l+jFM89+CMT8kVjxk+9u+pvxr2SKEbnqV46MPHQ6wRjfQl6DfZvxoiZeUDmcceUtaNbMP0ZY3eBlL2UIiNB6KkNiBXNG2H6VXZx7TQEGulKLqmcBrmvn/LO6Rnfvvxs88nXzz77HN3zb/XymG4OA0jM5/C6ZzPlp+lk7BOuxsqB3G/Xf+gpWMabGHdM3tgJlGWlqR5YVw0Hkuq6o3ghI3OLYf7kOwN4RpqODYeMYqWHQGj0appN6a4ih2/p7kOjm6FtV469dHR5XePw/lveUcoNqIGD1MUqQFN7TwoVcbNbKNpsIzIBNMyIl+re+E+tn625o41tmg06f2G8w8gy1Y/8DfZPp8dXXIDp+fGMPMpouPr/uUQvzKQd8OIT+ccwrriGbj8qIY44SFX97M9QIZddY4re+GkDCGbmdXvh41z/GqYf8Ux0dhd+4E3TDvngReRlCI9DxeefOnohPQoHoS5SI4ClmWy7l8s00wqOAudMPmJGNiM3NAYGqz6NVXcDcp4vOGNoco2BB0jHVUvpIdnQg9Ejb7mW3hJopwmjFrgdxqBYcRk2PTXphIrH/oF7YwoGFuDda/EKp2w6dTQmMLYYvSg+yXfj5EpouKeVxCj8yvhc0PTt8An1Rmt8t8rPQqRm+41b1Tm8Wz0YDUSN1wLe46G7rwM57QhGnP0qjfO8pCDGllxaXOJ3EzhADu5K/Lh+I2gxDXCvPezIYqxdCeslWFd0vdYl2OJQiXIPqsSJz6SRo7iV3qrNAS8nB3AmKXOWP9Dn/be+3J4TsVsDmGEtK6KkwQ7un0x7CXNCnj5LNkQzA/1iZlZHZG+HqJjmv7jHOg6cPNKSE3Ly4jdr4e9ybn/uBE1eGig6CJkqI5pmXzPX7zQg9AWSzWB0iXGprzvr3V6mXlVf4+9NVq7Onm4f2yA1gWvqJfvwtNa9sc40e3z4eJq+XKjpAB5WLy6uGglTxhpks3mIjWSV+XgYjKTcCKxShIFLAeD2UrHArreDBsnK5Wp5+yd8YLEzYnYpRSEjEIVTzWW+/LHcC3MRQEpiXHdGyQ04iZleSdDGveCeyHWAMZQTlIZBCorFiRBu5ikHRTkeMkLQBplxV0PRB4hJTcUZV99EATwcIUL033NRXWmEtZ2OCMFRSMqxbRmedun0vrnghdDcLCZmSnm6emgsWnmjStGlszMaj8+CKcreWOu9MMH4XdfhBEllx2vhHnUCJXDXnE+8l4GjavE8Mwfro2ykPen2ArKjqmm/VlUIikKrmeeXyMMzDqkcECQu61LiF6m0kXh40SCOLyepmF5zK1nad3E6ykQlzrWyfEbPu1dgYOQSufgnKUjv96IJyo5Q0yizFh+apQ13w+Kovd1H1Vlqt6/J38O6jadf9Xvz/5fIuHMZKRwq5ERj/DAY2eu28wLiu5aEPadc36zctGnFhfBbggInF6SlYCHK3lj1y0uzRUy6kKDQSNBQ7yQzhl/jpqhiqOfbbhkAbbMzDq12UaH3nMJOqdypKA+JvL9OaIpxcFsQ0NBgy6MWZYUTEbUSxdadR+h3HW/DpZuo1pFDADmgpmTHstmm4dBrzwZhM95iYLk+uFYriMeO/AfMewPjSYFw/YuBWVfaGcbhr3NoKiLJ/4+qEiG53w6x0VZRMny/qnEtRKLc1WUSFbnTPBGlt/z0cABjMoS0jn9894gqWyFDTPY8Me4xurv7YdzXFlx3yPOQZ21wUV40rJmh69dCve4F+5/tCTVejBWGB008+96iBRiIkqyci8omf6l5w/ff1rDpbwhK1RGVhB97q/4+WTkRh1jVgbRsGAVj5mXdVqFSmeYxpYaHTQSFKfTX4HBIDqE91LfCb9Tg4FyXKNQvOf6NvaVRJMYgaEzbeZ1z7ApEXjoLxdVF2eKTr/u/dNXw/5RwDdB/944dF+zMnSbGsUTzCOzFWp8cr08L/jf7GMMHt60gvEmGRGNobyc2mM+nJZMY1Lo5U8khM7oz5GfoBaMI24MvX8u7FROtEbF4g55rFI52+jg5k55rNhsmt4YIzBQwbM5A8d8JVOq1BHnV4OHiovGlQLOeP68bCJGx0YR6zwHbE5u5G9yuARhcegVb24YjjG1qYbjPIwECni4DqyJAHkniKaksCEnQWJNKtEt+Gwo7qkPRV2SyAHBwiy1zp964yrB4lOAOBW3ruesVFXx2L/X7sWg0EtCReBK5/v+WKENeoPa7PTZk3OEG9MKK0bVOq+ikkzmkeDmiuJvVhEJ0Oou+jCIyGglHOWmCmYaUYUusRF+7xCQncxhhIdywHnocquZURDzclDlCo85B1qx+nT8RIZRPhNkTZCvmQccpyAYMEjo3Jn5CET7LIxckcc0CFWJc06I9cxrAUh7dtbBzDuhWllNqIILWEhpfm03G96QFYHRe445a3w3imnlvPr9km0TTF7AL45XabHEsJKtfFw0QgDHjkNBoic8Vt/CIlzxK4GVXt11sf4Pw2flXuBGmSDCoxgYpohqUkWVFT2ZSirdRZrEqOF9KWqf32XVmm42h6fS+2dYXlJanAMXUlYlxpJL73C7vDCB4Ao2HR9jwStfEj2d1ezyXCf4BvMn3MesPIJiGonnXfvR7rPPk0seu5FrQ7Ee+1TS9FrAGgxWpBIQo3zIqjct6w4vs3gYogpqeJXf2wrHpBJrRODzgTcE+uthM9V//174/89tuu8xx5/v+gVBYd++6aMuXA/lffDTSJRocBbpIgFWD1h1hohJdccbpqN6dsRkACOdoXwzn9pkWXq+LZVwUIS9i37+3X3QyFFqjEq20TcPg8YfZYymlaik0hAJRH8EVsD14OlQxLmLYE6puFlEsvyhvC84heQVM/OGWPWxdx66iDDqOuWgA10WehEGAOiQ6js/wf0rRxYNwrZgLKsOZxSOKR6V3ESNLdlXS+FYTzjlaJjVdkhG5O8xi6/HzKy3hnvG70bCnde8F344kPUSG1GDxwkeIR7khNFKq+55bdxG3lBL0LhRxtVsM20IOZETQ4OCcyhVkAVEkAuQ2RXBCHVhiJEo0cysuotQK0G/KY8PRoHMf/WA6b9soy/TUzKzQSQUSkNMf0ehQONNgcN8v/Utv5l7G0HJs5w4leriHChQFx5sqkwRuolkX4XDCFjpJQ1W2tDgq24L4JVVEMJB03ot5L7z3WV3jGmaCvZfTkjxSHaXaCoXUSgSFhZPhfDxiyEtYzk9B0gPz3rDi8Rh3TfDOdRb5jo9fNUbuVz3Awm9M2I8Afi4LKBi7rncwAtmVmZ16HGf+qIHRlzbV0WAYHSueBwF758RtnHNPyfntfbujj/na6H8vNgKkzUUvp7CBw+ffR7d9EblPIwsbEupk+3cKRcXDdvOGY81Y8RhVGYUwd+Hi4KLOmG0gyXfatS4algxNPi741f8WmFAwMlj0QWEWWgUn85e4wGqxSTYwP2hxyhbOW9mPu1GupLumr9JBjDaF4QwFJEyjTxVcT1eS9NWnCspfnT3TJJbTT2eXg4vX22O2IhHeBz+Rhdv+MwHGCvrK/OlSjIE+UULfCwyg5aeKtkR5ZecP6vkuyuRJnc+qfpsbRKQHf6/uiVWMbBLSurnwNNiaGQZc6kIDBZXmm2ahox4Tsg187lLgpyn8m5vegXnvCpGuURJ+nXg75EcGaQSMJOUGRRVf9UrmXkY9Ngp3PMHPnLQux6MmpJgAZjSG0n10mgVLKeVbM+FgFcNGY+RBmIqTFMqzN1PCn7B5QHu1WPEVlV2QKxXfz4lYJaOYHJw7VfeDVGozpte2U+hQKkwn1wP/DqjsKYGy9kUATo/1V28J0kzMR1Ffp1cV9Jiy8Gg7d3ccMcoU12USEp1aeRo6m4eBmWOw/FJqoTOl5Zkn9yAkSOOMYG5VOi6pvoI1qVoVDLS4/rOGWVRWc11qWz9DnSNiH4qPeeIAf1Nti+GC7AQKMUVhPsaSxCYc6Ls6VPipiIFLJT3Om/Uozr/QwQE9JwchJ6oLqOhxPWi58s7ffgJYXgY7SidZlujroWDVimQekEXIV7WGPIqL479EHovhepm2wmZlBHkSyVQjqRwQLGXn5V3bl0ScDaMt5F3Iq2Ka/clgs70EdNFati56iUJhbq8cGQBceMo/0QdWCtd5Cz7c1w+5/w5+G5UWNDISYFDMzaKVsLNw+gDoE+Db7wuoF8XpfDbjODe8q6PWhC8SsOimCpvJf7Grwem04YXkS5KdA9DacvepGFX/fDAHZtWw7VdakdeF9+fhqQZjS0f+d+VQGnU+VKIIKXalQDLQO4hM7PWJjxArF/FDDC1ommW7pnwnNXHYsighLgPAHbttuKkWLUkPEJIPQ5Q7aaRLKYsY0bwyxpsB+DYtRVQCwM1zbScnaqiDKNcjOFJVHYwIs+1rnjOWGEK4QhaIeYIC1m1JlEQOr/HN/xaZEHOJAs7amY97J3arhhDMCRPrnnHkgY2QeMp5xo6qeez8daAo6/ONaNNlOmqy7jP9NkIVD66DpyidrNBmlqhLLHx3GXpqR9iUsY0zjX6gJejD66CbtZvzLxCL3hZ4I0hiazwehSwhU4kxyoRHhovDh0vOJe+hyG40TmLzSwktbyeyxELlog9sVQgdM7AO9TWGwhdUkCo4dhfjnglpFsnFb+ElGm5K8kaf1eX8uLeWvCCGbKeR+JBGmsT5+WLMhuyZD17m2lbCJapT4bwNo/9wk82guRULJUNZuN7VLgUT8M9K5Mp0zuDVR+ZKLYA3AYHjZbIct2rXODffRGqrmx1kJv5/2YegEymcD2/wx/KVFHGpVoJOAoGJYmbDXytilFZ2kUF16p3x/t8h7xfwWS5KOAcYniccmOqT8qiu0gtds4IToeGkkQOBmRhrvB7guu4H9bw0S2v7F0l6E42cz+/VxDHjA7e8h0Bp2MP9lefb90PxfklTw6dB+1ZRedUCV6JlVRd5oxAzrfoJOoC1Sd8TzGjTwMfWfeh0AdiUHNRzCmMphcgbItjeHCeoTwcFbxOijsHPXafmrURnOI8Fm86ysIT+mM0ciaSRikB0MxIioYI+XJOBYhVfwRAMPE3ch/Nh7Cel/05BjDKUuk6fJXGXPt8NvhbIys0ZFKpJDwrN4PmPVmWruh+LtAsgKKZDy0mKQ4OzKOUF/MYKxA0EjQPY9gMk82U0MFbXnpt/PMQZRmt+eedQqGpcuN6qDwKkqh7xWN9mBZzjT7NrP/aeRzLBsI7Qr6LsjkzrmVmroknq+p6UlBAR2Uk69IpRo8Hds6Jm+PPS0UNDIHmQ2mVguoVClEFijItGQvDn9yqZR6r7gQDcyJEdifAepTFAHDtVtgvS5wFpri0ZH0eBteYq8BZ0/Rh9jkop7QyiJmA5v3slNnxjbDIioIfYgqKSnYszjUBvG2hWGG0Ro175zDiexqFKjJ6eVExQoiQrEBebvhr1beyjRqHp4pkQ3z/SP89wlBI+qvnV2NRddaz70nVNSkC+qmU3OxrDyTa1geeSrG7sRE1ePjyiaw285VTjlhPwkv5SL7RLQymyDQ0XoE1J3K5eJJt3fXQkmaMkJi+ALIwa3Tp9AqsXVjgyphcwnwMRTY6ym05vzsnDKPmPQnrrnDD+nM4o0+OZbE8q2VNrE8qN46Q8uq7YUXuvylN58ZcB369NO6HhaA4Cgc2hJfdPSsPMwejthWe/+QmqoTE6O+Dw0XJBcnMy2iJmdmY5a3Xw4JgVZaZ2bjKyIoIZqw3loMrXsj1NhIl40L7y9LlG4YSWVRLx/4c7YtMacmlSWoqx7iGTy8h/Xfov8dzKO9THVUip0g/K1B04hp/2p9sEKwvbUTI5aK8Ug5jiP3YPSeRIKQflFx1HgZTVS46J6lWOk6pAhZMTZq3JXz59HJ+5v/rtXWeaFAxrak6qYcbcZVGJhxzSiiIaCwddHWu2xcYZVGncLZDreuyGzpjzOhZiN/JHNcfcw7CtbQS6wjNu2uPpWM82j2YiBOH74HeTxkkWPiMVj05x+xonhqwnXN81/bc4wWYlv0xCjCX5ogwaBY1D0cMFSMdEmIbwtqdFmWRHEDwNwWwC2OIC/v0mtwHq+oj8sQtLrGzGL5PAckQXRo2/AXyPSr72ecz8xGqoVS3OoNHmahXZgt0LV93pF0RV5cNG3Mjb/B4j0I8LJT4pvpHUdjhPqbZVbwvbZBB2nEoSWnnACRliidgnl0Bx7XbIc978jlUc/W8UCKLcSLhkwFpHYgzkvsooudPbqApBggU4deh4cQSYhVKS3fC5/233CHLd7JlBp2fLvoaVvb997gPCnv+GI10KkZ1CDhSDOl4T7pmWxdYhUIeHu2XFT4r4+ykiPL++0Ho9aWFBjdI7P5f1nB6guR2EjnwfaTUWAmfU7QeS7MNGcWFkbojRXro2pdkG7mu6kkij5RTrNgyy8a0ardxrtlkIM8JPVc6zs54UPco3tUbW3JtydJ8NE4vZ5sBrU0vW+jMa8QuK4ui5evEb6XA34Sh4FhJMIy1x/YnGlGDx/X9kMgKQ09MbynGxoXcZL4JJO4jYq+hPurO6gO/i7pXwi6qbEk+E/fsBE9NDCMsrqUP/bH9N3k+GjXZhosutPZlpGwE/E38AqNhlQN/fvYd0zl2OB1Rap4ACudXJmdu4Gk2/qZzNexYZRt1Te0kLUZsTl48PyLuWSbauCcPOgeDBhk3pJZWE9ynOXga91pufvp6MHKaHwR3bbTsleBwLWwsVRAVcIT0EC1IgcWXs7VnAWmUsUQtqMj4LH0BwrPiUZ0dCnfunSfXDp9dGa/P6rmIj0Zu/JrKvo+V91EO3hANjcEGxmZ+D7q2MoLl6gOErpVJBI2foGlujPNM8XvzMHwrBf5/NiBY0ygOjJydXfUVnRIZdOlbSeeUT2YbEGp4OSZnYYOmY6mpJOoXBmra52Rtw0DJicGjOvajod0FYsDtPDFpkkamfmH0JNXmA3PXF2OFuk3fIQ1OrtOacu3ArtB7JFExI156LRIO6/zExsfw8OCz7EHnXWHzK3Hf8fVskqH2xfC5EVrT2HBJJxnh3st+Jeda4SYHSxG/yFsAACAASURBVOKh4XejRrag4Ms/ueGvXdvGfbkqaTUsIuh+GFR6j+VDeuDh/0+vibDARlGwdyxv64gO2UpArH0aQKkuuBn9szQ6wVJ39YAo7JR1d1wMD56bEBQ8fxUpBRgo0zxLEP33KIh071Tvhxhs55ovvXepK7wvxekwPZJihAUxIN8Dw+5mZstvh+qr08/5vjg8v5Z850bgMUF3bFUyVay9VoZ3aTajNHVttjGkRQnOW9aqRkZtMaX1beETA5BWmyXzXSRXlI8kfM5DcZGUUUftgfeE2lfgPCDa1r4ojUodr8vzAzQ/rcEqOAJ9j69lp67ZaNLMcyVphaeL/kRoTlxQWaaJaSw29EwRtTq/Ijv6oKz+hHJw7alR5krbJYJdDbA/Z6RrFMfh4mUOEuwRdYydYc79EsH6jNUniswx03WV/WxjJR+hX2E5Ph2fFKXGn5CiLWrwsOdFTUCotH4J5lVALW9sKLQq42o4JydLIzB2LpykWBRvGdGl0a43kUdMiRboXUgaYSu8kfYVOT/o63lf+hIdHqLsz5E7DOeoP/QvjtEsArfLEr7nRleuIC56jQw5Dxybu/nAawg2VFWv58wfBAW99xaEtEQuqMjbX/Vla/WHYfelGmFiLokJ6cwhhoeYjxjIlRu0IqSE3cthDpUro30R5fvrNKiyFZ2mDlSIfzQU19ADEHrp+37Bda6hU7hgf4YZJKGaEqpvB4PBGYdm1sE+G7f8A4zqIMFERFexAN0L4XtFwQ8RQ9AE30lPvHaXwhaxU7wXQtCF1y67Y7X7TAeiVFfWdncjPHfnstdwxLnF8CdM3TQezl+3dOI7WXUY46A5uuFVDw2IgTi8LpuASLdGWRh9SDkBOGdW1NtMjHbZcoxMK+M/IxWM4qgT4C/m/2SEh/J/pKl9LA9V/DQmumf8MZZ2O5iF6GXXg1JEcAmgf8UnHbzBYobw/0v3/Ms4Qjm+pvxobHFOB8qRBfJgjaLFRryXFkKrqS7cGfw3WmIaqxhLxjByIMxKAkTuQuCW1rzymFCjN2V1HYKSHdYPP5uZ9c7A6OgIvsSFq7HZBHC2/KNw7OSGv43+OsjfcqIscBqG6BUzxfkeyByvvgdP+oIsDKLs8fH4mvAIReQojZyVD4ImoXI2Mzv6QlCg2kPGkQ2KYnFgRmK+IlTxL2sQSM21MZB0C8OxJamAYvqL0UszszKwBoyyaOi6hJJyBVBaggolKPjaI9k7wKUcfnndHVv6MEhEZVqmcqbyKAqZZflRkLCdsz6CRNzccCW7fCcWep/CsVDGWToPlF0pAjMYRoqPOfpGIPxT5Xq6Gd7h+W8HsIc2MS0fBplU3vfSfXydIarwsSIA9U4x3Fhvdf6cAFdqTRqSFLdT+JzmWpstA8y88mSqWJ1rrhU9B99z5zz+X+7D4W+EeoT6UI25yiEU8Cb2vuCA+txmsm1peDhCWrkWOd90zTIypOmiLvE9MH60EstVtOk52JXgfHYmgPd/fE1sB5wz1azeYd74I/89ZlG0vD82ogYPlZbiBPqQX5x0DTtTSKm1y78JOFZPrrwaVuVoJJPHv8f+HnkvlR1EoSSKkz9BFKehijrcZO1OeFDNvx6/NtuwMLPM9hFmZtaj8gj/LdmHzPyume9RpgRNrkwU/BC6mZlOS/X7QnTs9ArZEU2+R5yRnAMWeipnjK+ydQXLfedluOga7juFUYEBxP4zZj59wec1MyvjnMTRjKQSi3iTYkuYeV2KE+/umo+vOxp9WbPs9zUp+MXHyqMOyr8Vs7H9896IcgMG4aTsLz5FFLd9Lfx/MtZNgYjoYbZ8ok/EtIGZWQ30DGyZYWZ2+Do6mCu4FeugdSu8bMVJueqri37+s1iee2tei/my7/mjauD8smJG5TiNjlTjYfyt1BqU4zQ6SkIxMI1whLGogCXlsQhPSkbCoFJ6A8pgFwxQ4HaERoXGl+uBqHgkGFEjwbe7amflroE4pYOroGI2sdZUkuO3UvwQeYRwbZUtrsJb5phGE3VIR7FQJPp9ATURj/Cw1byE1c7+XrjK9k8jkiILyIWoRGm7NM0qwcd+tfZPwSp7KnnhJSKs/BserqJ/E0Cz5X3hUEAEJlWlhXl2/X/WpBoDwnhS95OQPwXOaMVfQEHMHw0tv994J5zz6KakAAjOVku4Nvv8WlbPVBiJDM2EdgCWdYroEddWLiJ6Zokw2rLSx1WSrc+fN0tBRyC5MmhzI+s74Tqq7khZOqI6LOvVCqvKAVo/SLsBvnMnQERZVnaChDoWnpmjt4LFUJHWGKQO8NUYki4io63MD7E5ShMxxH7Mr4ZFNlGD54Skiv6QGjbP7kMicex0btJs0fFnyflptI8fYx0o5QJuuSbcV06Z0F8Sqczu3kc3529POLwUI9YS3eAaWP++D60cXw8TrIYz55DVq+l+gNmKmtEg3pcC4Zli0bXiejCKnqChwXRUqgk0FHUiTlJWpEKNAs5jiuiXpdxSPOOMQNxXVcv7Xc8zuRkH+Jbzu+aw2ffoYC6CcfLpu2wHvXmf5KdRM8aN5wYt6+I6enX2xlOrlZb7sOontvYIaSCAK/tnhKBrG1T2q/5YcgLCrsFs5W7mF17zrkQfoDDGJTFI9pgWC/+//J4X7kevo5+OYBIInh5I+L58gA3M/Ksnt7XWRaZB/DG3kGUK6tvhphNIUi17X3snuGa7X/FJXW4AbnQ1ypbuhnfTEVIwEgoqoy1TIRSYWf1vXuZg1KXQyU45uSoeeQwyBCs3S/N374bfrQTJNlzSXHEYIzkHq+cY7VEwenszvMAUTT++qpirNlouuIotOQc9SjWOXUFB3e+5BJ3Vc010S59IujkSOWXlKI1o3Tv0IlNM1OTWUmVEJe9au8g5OtkRGTJdk6SvedtL9/alyHuag+EKErD2WlLZxjWgUazmg7BYjm56JVJDA+cWZLXy8FB2DEU2udY8SJVrFRKVsSr7GGaPcpHyWMHNvQ2se1lT3C8OsCvXdcpfZAsjQymKGOwRRysj8phZgnSvLpxPjL58RhWbFiuxi8Dha9ncVPxd/ZHfR4fgClL5GhvR7UMh0tjKZubdeDvczM5PScoJ4Wq9sV5WxLsqtN1dnDPFFgsQVUs8XRGkH43jVyLo+yPxNhGW7V4EFmdHNjPOoYYXjcXaQ/87h8bvz/5s5heJlsim+oxgnFwLu48bQMmaHn8dJHeySako6dEXBMjOjckmgmZesRckBdODsK9vhV1DLpt5GeOMbs2KBaBwz0k1G9NRWs3W/avXn312VO2pyB06gAt2pkhsHHovpZstZgNMmQpTAki3jxkBleiu73qeDfIcr3uJOEXEZwR+oFxBigEQ/UkeZHNCueiSyCCmeZXss30hW5JW0F6sukPWax8K8n2bBDdHzhos9e1v+FwQo6+aSpmHQdlH2ZSTNct7V2LS4xsgFBSCyZNrNHLC/yu4me985bZfjEc4PykMdN27yj+Zat+k2R9zqTycQ+kSaNyrIcN1f+aPwo3tvSW0EJGehbyPgbQ7KuAYMbPasXxKjJAEMDhdOne8Xhn7Qw0vNuSOFf/QgFKdV0Yp+jgGDJcR1Siuv4tUZjhWYHj2lT0p84PXFKOwdiWzB5LDRtoq0ZdD4R6x6okZGDWkL84BlFiEHyLXA5eMKLgS0mSr72eX5qeYouFFuty1yFo2ctWNwrCgzvHSXVSoRPpx8bk1HcX3RA9OUxjE/ij+ZgDiwbHgwUh62F9FmXB//oQ7BTXDxwr6pqLTeaLhoXxI9Q9DvP3gSyGqw4onM+W/8duYnEou515VBcGKC38fLBNW8Lgz7vEsOUn7+DJVf6x7lsAtv2g3rgSN1+qCnK/iJ3l/O1gyaWNr9ucUmBXC/fSyRGfgSExEsWhK46PRvOsFA5vN1u54wpD2jSBEY+Xm5J5Zfifi3bykQVK/SSG8S42YUa4wamPmC1U0lZSFC1IMD9/t8XWJMGdETDSC4e5JUnI0LlTOunvGY+v5S8CaFSVNw6j+4asgahUtXcUS0FRxVqWXmU/XjZhWikQvdZ27fZxyHsJn7p3WVf+9EozWvKTFHPCZfTJl/3GuPjGmZXplA8lgUeAe3ULliihjekJaJkejId8mcFg8OYS4tfyUpeJqhAxW4RkhdTSUEJ4jQJRikrUfhnMc3YLxpoaXMxjkHrEIo2BhLBI9P/lDTq6qoRE+9+T+yQ9R20XKSfAKLgSprNrEYRWyDVjy8nTO+wVTRAm75uiJT6Fy1VTNPAxuSBqCxUON4iAPLo/B91U6FsPwbJDoDsQeIcXTrsVlSCyuxeUfeAl+eitIupxi7yL92Xg9Rq86su5phKTWCm5ZGWf3t6E9EMFVk6B6P0yQclNRAXHPKS8K09uuV555ZTVRojmsg9Mr4eDq236OK2DE7l3wkRvuJUY/FH9SBl6reyG7p9fLGmweOslIf5t5pyrVaZsg3QhAnI0/6QSa+UopBfry2mvvBu259XPyYh3nmBzSPogYTPXQMBpIyTcxKrqmsioGU4ECTLHqE7dvxdhiVKe+hWibOLiuCEnmke2aGMUx83uClCWFtn9Pjt9P5QILjaCXFZw9xnMqt1ZsRA0e9hJR0CRLWqnQl+74i/fA0aAgJ4fqZmnjevYD0IgxM6vfZd24/y7vsb+Wfc6TW+GzgoiPr2cACyO6WI0OLsqKhGvZx8s15pRNf3Qzm2guxmbN85xcQQWUlCLSw1y665UwgZJcB0t3/W4g+DjlleyGlTxsCCM20jOVXZS9X4q4Xy9pOOUDULUKBhcZlHOMXFm3pLQ2gOW4H94DI2RPrpcdGq/soZP3UpDSh2/6GDeVDhsXmpltvB0W7eFrwm9VpdGLKI601yBQVJ2d5ofhd67C0cxyJ/RuYZBo9eOtIEBW3vfao4VoDY0rbVvjSuwVLLwTfnj4OX/MGe2guT/8og9P1LfARSSOFknWCMJc/rHff/ufJ1uzzd1g5RQNi1SfJ8y9NuZkZZCuRcrI1kVE2ZXIFpU8sbL0x18N87n8Y8HYwOjNS3HFoJrtxDjjGAaDsgAzwq+tkFxfSBII6prFOWLEgGoM0SA8vol9KwB/Oj8p8kLcovLwNO4zlZ4d3SXhsN4j7QfSSVQ62e/p+MbzO8ZRg4dkdLp4XRoFk65Wt2NtVGOaJetU8FKNwf5Zha5YizyHGgJo/7D2dvjd/te9dsofhJOk+nEB00OLXw0SpjTUuyAJmlYFOKAXPAgFVzqPWK7tvAgNMy7PFugqVOg5tc/7ZcE0FhU7DRwzD9AsSZiRRg1xMGZ+4yT/7A+ffS78ja/bvA2mj/julKXX9YSRhrqx9gBFx1Yd5jflzR4wYubPMS6He+SeSO8/VEHIPdEoVUOGgoiM3YmA9OjEKC6DniJJQc3MDEDlPI4lAgoZnoQHIt+NmdmkFASPA1rGIlliX/O9peg28GwOaK/g9U64r5OrPjrjAL6b4YelE/9CqdjnsVs6187IdSXP/k2sLUQseu4wY5oOwfU0XeQ4eggwjgCT1Ylxx1TX4JwMAKgR7chrFXCM/elSPfLKs/p2PbkgPqsuAECbKSd9F9X74SRd6dR+4TtBme295QUKneje6myHw8xnfTRyQ6A720y0L/j74D2r0RQbcR4eKCLNx9IKrG7PXvBm/oXUH4nFDKXrcpEyQTkYOfl+Nqq+dV1WIUCN+2DGTI6l+zMMo6kQCtKDpdGnwjFWCcLwuqbduOF8CsN/j3PCEm8zz3SqOCbeJxekRuwa98JNd8/7k7B7bnU3nKOmJdVYI2oMFYpUrv7+++DFaP9r33j2Wfll5mFwI7sqJGEZpoGiBgMNjfb5bK1QRaQmL5QL+v7c73bD75hu0PsgLk/3twNWL4mB2uE6wm/q2fekgm0QaQicQxqrUAJdRUkijzB4NOXHfRxLw7rfRKpaVn/o7/HkajgneUzU6yWGR8k4OXyPNsFLQhYUO9lz/LKG6zFIBzfCHRYjgIyVO2dhs8y8Ys0rRpHRNYiVVK8oOGok2dOhaV5f+IJ7lEABgb2qy/g8DtupXDtMOcn5He5WfkcHjfCJvoCbCZlQY+LeL4Y9lyrUAC8W95KwTnhmZ9kSDi+KtFhbQmrN+4gILj3/nogaPI7uXNvcX2T4EPgYIZtqPMw2mkgP7RhbT7KlUkEiB7SKp3V135iOwstoSGn7ETxiMbt5/hIxCVoqiE2qnnSsm7nrIBxZ5HkXJcrGCGmYl/dJOvTarn+f+YNw8dOv+sQzq0QYqjy+4ZW1a04qypW07Grxc2Hz/ErKNw9j7QfhRRy9ggaehexN15OmmmwdoISeNJxam9nGEPfZ0n0v+dsXsJ6JsVGqAChgxVQw0qSl8/WHQdI9/npwtbpn/T0S9FmRfH/XlXuIckJlVv8Qfda2pcpplbLFn588PwRr6n1wHkdCm8EeWYrvqW/hM3hyWhclXYu505YwjtgVLUt079O5VEU+D4NRZNcqQInvsirnTBw/cfZ4Tr5nnSc6ghoRdRE6vEqFHzh8jCh0yjSNODhOOdxXSSKDTFspJojyf+09pPYv+C8yHaV6iPeh1bs0Rpmh0XkskqdIKqldJbE8mwNuMyMRIZnUdBfn7vg6UtuydxoP0fT3+Pn1RLwsHUqqI9ausyzxcOn+KbPzu2aSbiFN/4Y/ieNXSDWlxMuXVFjtwzBLbDqa9NW9IDgnW3ER0KzlgMzVqtE3joQ4S7uzj41krghqZJTBTFJ5Ig/JVcH51pTWo7983rIGp4SGyyiva4LAS3+OAnh4Upu0MrvEWisD52GQaZqhfA3fxyqDuCfUU6QhynWkJeVMK6kh45l5KXn8tQgkTxlsSJ2ooj69GhY014amprg3VbGMzwQBkpP8QKUeJGm3FyZBG+9qesqdH1Fbep463z7t5ueAwlcNKpLQlY4g+Tf9Buc+UwZzUu7TUSkLAJ5RiHnkpqJBlkVuZ+YVrirBfIY+0cHUqBpNriWCio4sUjyRx1lYHDOzZBxOWlLqjoxUXopOApCJvID1qSe2vxYWHLFkZn4OVNekAIMYkwysZ6oDAv9Q4DmeLY2Hm33doujKKQs/Urjb2efTqN/xjexWRbERj/DA21ZeA4LOWpfC/6+9K+kKlOQqlw/PnxsgraH4Yva+ksafMeR89yKu516wALDBA5Lf8avcIec5WyJ3eqvZx2jFKniTngc95FSVgUsJ+WMqxDlWfxhWyu6Xg1mvXDuDSC8bAjRp1Ohzkkl2IDwbI4TpY/wWBUQSG/deIDn7KQ0Kd0dzf+onjbgqthAwM2s8DC9Xo2T9pdkSSzEqNHiqB7KvsFb47spjYQd39yXKHq0sCJ428xEflq8XpC8YFdJQjJVcIdtaGQyw0cooeRYFMalhvUmD02kJ4MezlLBKiBk+17b9PdK40IoUroNxlRE1/z0anBrp4zkc1kr6gjHNwk7fczOIX0HkQEuJCZrVPkyNh2EyTq6pAT/7s0apOyD1q+1lyzemcJZui+HCCLPgLalrhuIwMgDgDOVU6iuCVSLdHJ7z9IrAOBx20J+DnDQKaOY64nrLSSSIa1go8ax9CQ6ZpJLG5NxzPclUtuA32n0B91x/ED63fO9eVwKvab3YiBo8BNUdCa8Bc5HcuN0z2aHE08vCt/FHQRl33mIYxE/Q6vfD7w6/IPTsaCCYE0Czb/cQZmVS8kJjCmOLeB4zswE9RYTJp5J/JV15T1iSHXeElM9SQNDI0ZdIQ0kta5fjlgW6/4VwAUYJNJDFlKVG4gjebHwYLkDyOzOzPnpGaSQrjzlO93jBJiI5W37+qrQKGWmg0pGkleAQKMdKVksBM18ZwkieGkYUKBqdYVqM+JucRBiIG9GUDYkutReYa38RIf1yHaRF6PV7iOo1fGy88IOwYUZXSEIjkV/wYqWwDGBMp/EzKUlaDIaM8k/x2cqS0u9CuU4KaG7cyjZIdF+RPfb0cvb+IN+MpkDnYVBPuIi+ePwuSjlRo44M5oLHwlcJRh5IMQCjM0r4x6IMQgK09U5jCy1bil6IUY7HOsEzaqQQjCQS3WDkJlYF7Lh2JBrIxqXqNHMeqXe0BQgNPX2HHBPB3rkG3XCmRhKF4l7VgAUjs8TmaPrMFQ28gA8QNXhoFRcVEc+TsJGXgmYjJdPb3whfdrk8yemfXJ8tvMzM8u3w5NN1PyuTIVZNmyanVGLto0pr3a+SHH7HEL2mtGiBqjfI0ZMID79LC1yBzy4loBEkCm1l1+zNVqAaJaLyi1URMXKj2JF8LwgtzTszRaJ4EaauXAfyZmTXv6ThgOWITA2a/nkpsBT/1ltF9OSBn0NW61Coplq2QGgor1H7/OzqyuUfenfw8PMBLEGOJjOZe1kO1d1w0va5cGP0yMzMuufC5/pDcSTeDIu9PxTcy+fC4k+OggDJVfxcFdbCfh9VPPCjeTG4rSc74dioKfLjLpSfpgcwlJixjVYvLMbQPnQqJ9w5Af4mQSiLEMykknP+Wmk5ueJaM8i9MpK78mPNBLAJrf9dVspeU1quJYIoajri1FfKuzZCtK4ixgSjP1npGzNpXSSyunUFh8Q5Jei/v4bikC1xrsk2LUY6q7u08MWlA6EnphLhYdV1Ck8GguBUf6t73AfA9SoXEQ020ZWFVrbB7O4D+yrV/zIyogZPEgGZOdI1MpJqd1i8VD0HFTwJzFJltmeDgGWfHTOzcY2UtspihI+YlNKOUHXDG6TXaOY3Gzup56SdOVkthxIK1Vw2h2OTZFhUrWK8YF2EfGy1hB27Lj6mOhLT6o54kYwmdNfycgxNQFNKEm0AxuqVhAdi6mY8hxieQX22cZZiVEXKcCQpCkZgxtL4c/VH4UUfvoodr8A/vC9VkI6tlKBcMcqa94CVkfYRZIdOdWRGRCPmXdHI0YqXXic8W6UiLNJwRQsrYT7OrXrJvPUYUlsA09M+S1nCfShxqfPaJUpUOcqWpKwSYRpSsVYkb1Xjh1FAKj91ONj3rr79AtL9UxqusANYRiWrZaHLSPBSnLdUdRHeC3VIGlSM9SYRS+oUGjzNB34Bs+2Bduhe/jDbKHXRJRiAynfm0r7yKmmgsD2RrhsaDApMpkTW+XFzB4da93ANBvzhG/4YCXxjrWSWwaejJLdkSdaMxBhfXf9+OHi6mZ1hUtkYGx+D4Qmf1drlYqCxklN8jGsfIeePYE848iiPmkrbCebxc10hZ2MKCtgf5QTpQqCUpJeWM/pgdauC4zlTwGRY5CmQFhmOsbBHWpaO29K0GIW2RoZ4PS0j5HBdr1fVw5wtZFWRs+qkIE0T2dG2suvDhaVjhpEh3P/4sc3bYJPD4+thLVaFKp9zo8YfIzLKxLr8h2Eh5a+EEIkasvQGU14YQZkU9Kf+JMOVsPj0XcYMGZ6TOIoTwRpU9iEjFJM2QmRFEPrsn1Wvh03RHfq9v/5b4f5Pr/rzDw7CpOS7EcMZt6zClykArVRjdI/7Q98192ZZuJocABkftaDA3cdzysxPc/Ddci2qwlUnjiOWCiOm3TlmImeZkkytN/Zuw6tM0TEQYiB74OgWUqjKwsyUFiGnXUmtPQqftZckAwfEtihPDg0NXQ9L98JBba/BczpdI1kZ4qsmZT8JjCiprmGqkEar3j/Xuhq3NOh3vxTuv7btv0eDLRZF1RHn4UHOX8nT+BCMIqT4Y7inZQFxotl3JxFgMrk4+pLHZ2XWuKlw8zBhlcew3C+oBgofNfzG3ieF08iGgiwmJbaZnyttUHgCJmcCiXOS466j5JK9ucy8MtSUVlb/F83vkmAq1Xna9cCBcJf3SQGkaRbypAwbfpGwwqF8jOf82gWb58GUhHrl3PzqCTkSSVmKj7+JajkqQe13g6Fdo6kw+S67m17yMIqjERiuqYZQt/N33O/a3mEcaanSaAZteLLnNWHj/bCZ1v9iiHnf3/X5h+IlODQalKwhUgg261w/uyhhILKL0QpGWczMlu7A+wSDefHUvwtWV2ovMyqrJkC7k6JXVBBjL9QZ+tMajjGfoknWNhU6iTPNfMpJ94sjDcyqCDO/ZlN9FXE5ykjl4eE6TadzZp/PTFpj4BhTWGaeeFCbTLsKLjjriVaLuXS5P7b/+WwcZVZLh4qkol1TZKlopt7QCjfOeQyC4eZY3pPDEk75/3ItiLLiC7SXi7eWQF7/UDtXE38WqV6Ktbkvsl09oxQCYmSI2ySVxL9yAnabVAC8hAJKgWYZPhSvhGk3V4Eg0Soe06Zz/Lu3LhsMC5Zeewo4jGOqWDh3ushd91xs4PqBl0ack1TkBvfItgjaEb1zBjctyoPGs64RYnqGSBlpddM8DLZ+4LLU9N7yh+j6Lm0hKFAUcDxCOpT8R+qJutYVaudPZh/Tpq3OMJL1zHWpoGtH8MZceoTFePm2v8l9TF51xW+61ivh8zoWSy4vhtfFcM7ytp/jBGksdqGuP/T3yGKAVJk7q49E+TFMz7Wt5IKxiISrAjsMX1SeLYJz5xG07FsRZBdGsNBD+4VxrSuBJQ0UpqMGkWiXOnQuukE+NW0nwojldX8fJLkuiJIlB40rrZYiGMInFBZR3SGGB+eQeaRMV6MsZnCWkIWIRdsY/Un12COGUa7t9hKuXdXoDN5hR3xax4MEGaRl6S7gEnkWHVGDh2y5sRB3zNqi5Zu6MYZqERJ0YGMzW10NT94Rzg4aQ/Wqj23tbYVVOFpmHNDfRl6xPxgUUnzZ2qHVtVxY9cfoKervWIrOTaklsq6ppniKDiAr5+fvSKqoVTkUEI1H2R49S6D70gW8jNLs6mPBZZCxeuBfQOtK2Dn1R8FQGBcjqYiXNLI62qfp8IFXkEoQxzmllUEuWpcNEOR+0SgRgdAESGuUlmH+2mOJEqFCYlDKXiuuv5xIExLBbf2MP3imGoycw5aPPK2eC0KkCyBIveb399FuWDf9M0ImehAeLoHj076sBkl4tqUPxQmIVIJQCZ/5Q4C4L0jTXOAa8OwZSAAAIABJREFUFN/D99s9E+5XebaohNURmofBaA0xMKnWCVCQzfv+mEsBi7Fy6f8OHt7ON8LEKzSBUVB9XzRm6YDqPVIupjBdxGlKZIIpFxrHnUv+e0W0DZmU/XroZlChlQ7VcsTHlPOO34ku7jB4jDlWo4PPohkb7WTP4bCwbK57w3+POKZUliAjNThQ4kFEpYYv0E83avA07geh1Fv1b7h5FzeDhaYLiGFGxSFQeTANNHxVvogxkQs0YOQcn/p7LDTCih2dzhaAZmYjciNIOi0HfAGroXQhxAi3fFNQ0U5MXWHxalklvWytJnHpB+E6OrnKOCbuQ8uhoUtOL0kY0+WMkd7aFm6Y9bCcjm/6Fco2FMpxR/bYURVVd/Nn7zgPm9w7RcEssR/ZRAyGKiKnfN4nf4fPjOSpMcEKudYFf7C3ikqvSDuUZVAMaEUcMV0a9m8+QjrnElpQSDo4B8dF11sHhsxwKKDGo3Cjr94IoIe7+z6uPeU+Vv56sq7j2orFYVWIglQpk9TD5N9kxNZom6u80Xm8l8EMLHNVfwhw+bn5K9NqgXWYqT42Kzbza3gkEUuuHQX6Pvq5YOTEohtRPhbKPjqnUjVLY0gNKtfDSvYjdWDvTHaFFR2VoYS6XT9GLF92NjfzWQJ1rinHO+ezj2Xdk5kHFWuz6yz59OR6dPKyU3eszFIKF5c5Ih5MHHkaOTFqDB1Rg6e3gTcsyoeRCUdnLd1hJ5ESN050ExUdzUY24dzhkdf24zIquETmjRC5SQB8LtX9zuiTPyBS6UUFnArX4pgGnRmezGl8kucg2E07AcNT0LQbG3W2BM3u7gv6Qc8Ri+ARh+BC0RKWdu0IpPqIXcEHYvEwdUVOnnlsLeFwNSjRV2VPgZWuOgnPuP59/yL23gxfJi5IhRJbFqSqJXCPxER1JWLGtIwSJ7Kfk7LKttFU2IW4BWdEfo8Ur1QfDohaQzBeRqDMnSp3y164uDoqEzSoLR0Ac7QskSwUIpT3siNgqdL8/dmKXSOsHLrHHBFhMvOjmZm1LocbUe6ZeRhO/sAATpUVMyUk1BT9HJW4yofw2e0zLQCBuNDoBtepi9xF2LpTZeOIbqiSZfaC9CWKM+qvU6P7Y4w2sjS8dVkMI4gM5WSj/k1RxLAFEc6ha9bJK1n3fBdKoltyPEKAiYihRXtBy9KdjsXnikAwGIlT2o/YiBo8VG6qIPmwuUgUxzU6kwfn5O29Ga51RnjBa0WUNKuhgWetlL1UHfXBoQODpygsr8MKAINiydBaHyCFU9nx33MLT+6RoDNVjFxsNIwUpOUWqCxyLtCUtUtBivkuSsUIQ8p1AakyqsEI1eklv3wcKZ8G6fCiVL/xnD1gYVLnmINBIch3qULDbVapcDy+GuZt7T1pq4Dvlk+QPhTyP5bFVo6EWwbKhOWcGqWIYaTYlVsjEzRe/LNle+2KX7mxEfIDj068pbS2HlzHVj9ozUT4uYYrYbGnuFvgVfbXEYmTTuSDc+FhBiN/k0VEfzSlyEpGruecRHBpoJBA0MwzKnMP63siyeGLADQ/rcEqNUa0UlWh1PXiy6z8OKx1LWOmo6x8cByuMkgZfKlkT2b//5P/CB9VBq98EA6ywaaZr5ylYaHReJL1aeRjjF5u5X0agP4csTXgGIi1sAbPOsaWq0kxLO9f0AeOjqEtQH7+FWtwSkdIOdlo3DGFqBjGFUSnj258Qr20ymCP7a57hFh1hzcTPmvfHdcPQ4QGe2pMYFgslf1O6Y7C7FWqXgtWgXyul7xFVSnOjnF2BO1GQZoUtZSMHgvYOoUmvroN61x5hIiVEAAXSxM9mZz/HoVg44FgYDZne9xmXkFngVnNvNDSY45vhn2aBJNAUGxjR+ZeokEcPURyysfhhvO9SNjpJQ1H/4651tJLB3avqgUcPh5f92uRc0r8DTugPzkGdt8jafuCFJdbNw/9Ozm9Gs6hdvIUnoWWF1Mp9CNlqq7sNhKY6HT9HNAh2XsU3OpSUyxg8N4XSoLhgRUyRspsJFWMlSoY2Hc0TBc+ptLI8MbzESXM+deoBh0J1/FC5ipW1TIPg/velaUrRQaeUVmtx+VspdV4MDv1l2rSTPC0XJsQBPq0Csp1rYQ0e0jMaYQE3gH5U73bCMBR7z38TWLGljQqjUU0uKYGstapC3iPMQyMppyKwN9oBM91iYdAqe7679EY1cgNjcBY9G3vLThykaiqjqjB07oY3ri+YEYB6G3qImFYWFH1tIqLx3jZJS9hK/kwe3mJCxcAYu6P/eOcrYeZeHgaBGel5AV/+zi8gUJVokT4XNxCGF7WXKxSigqjc9Efo7BkXlh5HlzJ36oXDs5QUiInlktGFhAxQn1JTbheV0iz6PnW3g2SuXXZL5gSDJnaO1vu2Pirgux7OljKPi/D4dXwvhKt7gOmRytSmLI5+888h0H71eCisZ2L9ori+uuvSYsPRGSWgak4eEMAtVCkijOKVXuU0T6BwHUtSkiwLtUouFoP8e/3+h5s0C2F+7x0Obh5D+554pJzm0GADMZ+rfRQfj46ZHMjfx8llMdPpLqSESpVjK7/UEYU1cysvhckyNFNqSTDd70368/hmgpH2Gdf1qBuoNxSgCuPpViMIWPSbWmQKo+1Nckg3DTzctD1T5PGnKdozVAUB508PCr7mEqKRVmKx3SghcIAhgEjSGp0EPSrUTRXzBDpUh6jN3AGoVKURHpYsZ8k70vfBc/fPq+hWZwPETaNemY5nh834iktnFTDsSfo/TKOlGQTvMQoiJmkWGCl/WD3nPteH80El+v+yScsWxXJXISUapSDSVsvemtiD7XAYwFQJgV0726Ez4ngCQr4W19AdxnKT0vzQarAfKaC4hxvgqQwuDlSxhami0J7qJVe5NARF7PxaHa4eaJliWfDblOyQhovxz/tO8G5xqgwFOaxM7TzjKAQNRfNZ1IAOrEHOz/npRlbB7iuzmL7UaAoEJ6pGAdal9tw+B6JWFJBKB6C6U8qj66wHTOV21/za/b3d4OR+/mr3gBuD8PF6cSUV3wENw9np9fxizGPEvbSRnYIhrJldN7P49IH2YrFcZpg6pQ0cNAM51cwrlPyJMaLEEmq4J+HMc6IdFaFj4yOlFYuul5Ucv6TKwDhR+ATjFRoJIwGJSvsVMbUUa2o+5bnUGgCHdKio3Tw32ND68KppFdxX0xbKZWCa68hRiWjLCkMEqJc1FG6v4nFW3lfopLQE63NbB1CY0vZwal7VK7RDmjCKFZSU6fnXiDqGW8twXymAMlo5DiAply8jJI6DbHVIedYGne54WNUD/aD6bh/5Muj3tgMOaIHJ/7tn/TAtoq0VVeYnAogKZsoqJgRSFSF5A/91LnojKaE+tkvmAKMG0UXMhUQjU2zOJu1i8SBXFDfPMssq/t+gTp+nQg+jCDMqYCtqFxTRh8aB5KHR/O78zAc8Bv6VwkfCTYri7PA51duHJJPkgxQI35Mjyg3C4VZjCjMMfhGAKbKLUNcikuTphp44nQNL31fXws58YO+j6nTiSG7cj5CSbFU8xbJMQygQj47tDkGXjDR++e+krmjIuNzp6q0IK5UhrJ1Rf12eME7X/Yvg+fMYj1/mYM0Fkw5aaqE5fWa0mITTJ0nriNSHbQkOuBSLJGydH7W6INLP2u0DrJUjYQ20k5U2grsTdiLqiW6hvUgkWhYj5g6OUVKDmE0UBjEpqmpXlQs1JHzk64i1TkBe8Sl9WQe2WPP9dA0j8NitK0iDqXDzX1S3dJphasSHFdn34zSZbtQoiySk5uo/ilkb+SzK+GHWtFx5zDEvS6t+Dq5x61ghu/th913/qwPQzWbweoYSISndxvm7pnwFsfSc6t0iIiX9lU6AXBRw+YwbGj0aQrA5ZZTHWZxTPvXMP8b4W+g8FWOHudJsdJC2IWZuusK4G/5TliV44oQRGb0AqvszR9qWQ3Wj4Z6m5VDRB+kVYcr8xdDg5FDGsDFlhqh2H/CtMywM0PtVcmXe44mfx98J9pRehnK+eC1sBhTApDOj1yA0VhNR9EIOYO09HHee1OXm2G/P+p4l5vGURtMzknZGz9r6+H8+yv+WK8fxKPuW+4DBI+jRHDL9/35c0jVHLyO6KgUd/A9pUC28zCIdYKBp3PGiICWNNO4T1VYYXmcXEFKS9Yb95WudToFVRheqQITViFt+INHr8KokXvkvZCCoy+EvU5RK9v9eVwckf8TSTc7A1vWSgX7vesTJS4yS73TeCBy4VQmBSPWe47OFeXayTWJBOFPDZBwTlhWr86a23+Rnnc6otvHEaRJeI+hywEpt7UfDT0h1V+uC244f2/ktfZRO8xKU8gFr60Ga2uz5ndYCTmclUqwIBTrw9RXSxiOxk08UC/8Li+RIDYW1SgIN5t6FAQkMiWg7SlioM+sHLqZF7jOixLGZyq1dARmtoAoC1iW2KJUmTvxCls+gnd6KxiV7KvVOzN/gAUWEDIikPJiEO1SI9Q1iRUv2KUgh9nvi7QFyoTMd+QqMyQSREZfTVmr0ctxcjUYORRYafZxnFPIRH//UUhpXV712o8GDzF6PemqfjJEJ3UxqLoPggFUvxS000QqQFtdGBpCV1E+zBaPLuUHTNNQXGKW56rhO4VSW38neAsHn/NWE+Xw8geRhngvabgO8RH8B2VdqgUR9s/62x5xvPO1IDR5TjX+uAS0oKID/cV9oIYRW4ho+tAzpPtjxK91ziJNKvs73wGVxco085iLsAqWSHUIh2vCLVF2yijq5VT2hkVI4uywWKnlkQnOEXdFG+IE0FhM4YAQMKEDrU4+i38+sV5aXAw6KVn8NLoQqIA1r0qsAV/A2ZpXiG+uBfKxnGiWNlB8v3n7FXfsL9384bPPh8Ww8gZykycwclo9r2RHqP7I7dObVcGWbWWSXlyxP3yRVZYHynRz0agXyUWoIc0chQxKRlPGpzNkJOeK37FCoLvuPXNGOXQh99dANNcUriDXdTwbjzQPwz0jycGEnboF8kYNC1NwqlAlvwuVpdLt0whVwjxWRTBVqeumsRW0x8lVAUVTOKZYpMNncrBoCoONEydr3jj+S1ffe/Z5q+sXLSM+jNIqnUQBN7nf8lpg9UYARbfQmX3Q9c85pSEme7O9mY0/pJBlmmks3rjHWpkfmEcaOdrTq4n9uPNT81emRSPEyXvxVyoxfYLX8ODP+3fpnQAcEJG7+n64kcNXswHivC/F6Th2X3kPjGDEGqNmcf6YmU2QyRgv+wVRehzuebA2O9pjZpaHTNdCATrXWjWZtaf7AormO1TyQpfSFx1CQ6a7ifTfoUS4cY62FvHQeKFIFRlEWaO0GbHxMRiebI4NVzmASVdkuAMyiSVWh6JmOuew7zf1a81gCXxn97o7dr0ZIjw0cMy8MdQDsu6w58/PiM+tDR9a2aqE6MNpLQil0UPZlK1sz4CRAN0ADC2S2VOFBfOv2ujRAebEG6DAJeFdqlQar7p531srB2/Ao0dYVMOMrExKUiWXvLZfvTkoDCr5+tYLmO6f0nDCDI9xesU/E4WNGgJ8X6keMZhfRgRW3/PSZfdL4Z0o3QOFFNMKBeFeIjO2GjWMBqrxvfKDcNN3/2p4OPKImJlN1sI95yWV1IcA+aNHXurVKuF3m83wALWCX5c97Fs1hg4Pw/6cHpEXQsrX+7OjE2aeJVfTLA5Lg4+VQ2E6B+A2L41Fu2C3pvFc1rQ3CC5ZdTcvg9Eu16xY6TnK2fNJx0+BvqyMZGWvOmaHt8Jcp1pL0Cjj/4vRRCWuJLqxZr6uqWkGvsvMLMEcJAKLGC3NxjkqYN5V5UawZdpJPQedwmdTXcNrD5cUyoKJlb1k7F8HR6Jb17QP5kAyJePBbONlWpIX9SfssRLvpbWUzSTLB2dH1Vj5mIYxsyJDq2V/khbeyDmJ/nTwVi9UvKQow/Vg2urtB17AXlgPv9PQ+BA8PONxNsDRUWmPxDgkDbZYxY5FlI0jBcxFIyfVpBELNifz30D7B7Yg0NwpuS5OL3svuLo3uwxZcSUUzGkeITRylW7QI7D65pxRNn9l6a5NCB5/HNlJWtJskaobpploHA+b/gL0qpWXg0y13uPWEHc29xJBpYoR2n8zLGgaVF1Nz1VYDOAXBA2e9aZ3RYm/WSsHa+5+y0eC2uC5KMokT8CYXj4XzlH4nhdCndfCBJXuew3BiNhUvGVWY03y2ZVwHArCp7Li9JRkXzEaMtG+SnMwXJ8qGHWpPnHY29rXzaWqIk1BuSeUPJVrVo0EFg6cgtwuxgI80UosQgcUKEtsTsQmddGqkd8TwyYNHspBf46SS2f7Y7EIDCNsxNXEOIUSbdZN7ihNatBHys92CMzMGUapNky57Mi4uw/K3hdAPkQNHoJSazv+WDcDYKuCs3OBDyfWHF4kDYaR5NnJfrwvUvX1lRD96apJy/tFhOerV3znutvHAWGr115Ck8OTI1y76d9UrptNiOXaOyg3QgaeYxBhrNZQJc+voPFz3w4u0fG1ELtUw6t9EUar5oxrsxWoluBSANW2s3d9467X8qNGeLjiSZBApzdeoA3upzSW7oXnOnwNJfq69FxPnuxy5OY9LznZl2nocAHZwGf1UisZ9AZlKTygEZqTe2xtIrwuYX+uD+e0rPpnGbcQhar5hX+1Gm7yRDoxVgBQogNya1k4i0bh/L//wAMKamuz67fJnG5mVnyUza219sMwP+RgMfP7eOOPgb953WsPnnMiLVVYpk4FpGlvRkMUUzFvg0Bf3RM0cqrC7sssgVbkkHuIzp32/ONaVIoeZ6BClrLxqZlPHTce+rPsfBkEuLJf6Ey4CI8YVFx/GrUotFHhBv2iZLWM4KpRw/WmETBizdZ+GO6XDV/NfNGD9hPrAq9VeiwOAiNIGczTZnHcn28nET5rlJwtOjRKFBvxCA9yYykgKyqPRhn1/WZm+R6Q4St+cRXxgtl/pCOg5ce9ED+82vRvYASTf0kSqycwybfa4Rw/s3FbzgFEvICWydnD9hRsW2HmGTRT3AKYq6F4Dfwd50o9Refta2454lEcfilYQI6+PhYmlfvP6m+SwgFhf/WXBd8D1uRxWThT4PkNVsOxmIX/sga9N8673mtWw1Uz8djkd3QyesTwyIb3IWk1SLgWsU8r/nvTPMjNtH8R3qWS3bkqC95X3wuJwlJYIJMd/86LmKCUg4PnPhiEC1yo+hzD0SBMpGJzqo2gabp74RxL9/1zsmxY/aUeopl6jCTurUsAT4sAJ32A4rBcxSNToGLns+LPUUvMychiidYoNbMEKawhIzyKe2GbAmLoxKqhwk21VeDywLwr8R1/15amvK59jBjHWQSvKUeI2NdYBAOqUruZU4+W97MpShTbwh54nMfGI4mUrcw2Dp/cmGUeKzrDPHxW1utRjLcPa58dBY5uiT6BvaBBhNiIR3i4CVMpLSjqDiZIWUJxn9XH8nLIFxJR2mfKYaVdrfpd9L3jK88+//jEhzcen4Sb/plLd5593heJUsIqb0glC3ECeZAQjoXKvrgTvjeU6E8yyl7ZpNJmuLP22C9Cci8oUG35Q96XvxbLlzUyxEFMgr4LRm7Iqq1U+a4twop/19UIF9F0hLJbgJYb9+evIoXsx+VIZ2UKd20Ey/nde8tLjaW7EGYwlPvCy7T+TniZ+2/5BUGBRcGpEYwpDGx95wz1xwweGl6Dq/4k9Vo4SX/TH/vO/o1nnx+1PCCCIOM/d/nus88PO34SSFBop16UDbfDsSrmUXlRaKCo8GXFS+XA7ysaoFl4RjMfJVCma/LX5Bypnb8WIxkvwjnyaQ2uMcqt9sVsQHCKgZjcNVJOvYp+c4coDTcJ4nFuNMrO/lncm5rOmWCZahNTRzwoUU/HMcfixKZsuoxinyffZT4KhrLgaKbox6VrlhF+TddlRU+0c71jDleYDgwlTbUxPcWoTgq4jfvX1Ca/u/dF9FVUqAaSNJrViI2owUMl2FVmXnr9ke6zrIzrSxkeSQkpKPbbXmq8gbTV7x1ddcd+6czbzz4/GHjyl+3lIEhfQ4e0t1ub7nus9lgp+XzOtz4IlV8bq2FXCo2EFU/CDisKg6aj2RbD0UVFMD8E75p547D+wJ+CuI8ULT3+rj4EN4mSdiHcruFanp8eS1+MQ0dCJ+y/5YMgjVixZWY2IHgTgrB7LpJcfkmDVUl8Jyl2XBoasicaj8N/HF/30qB9bnYOW0Geu18Oe0Tf+fLt2ecvawkuqrtU+BKLUexk7/0uhU1bWlzUw9/nlv0DsNy8XJB014VgPe70QjhTG/t2B+EFFFrC7YR0Qe8smofq3qRSiGASmvf9PR68EZ7NORlyjt4GgNUKLkd6INYZmg5Tio9kDobn0coudOHza8SMzhMj4mZmh6/MTrfrOWjUpEq3mVrE71Rp08lXHBCNXHUesgyBQmTvjOvS9Hc5nMT1mWx7eUn4RIyLSLmChoCuakk5Bx1vbWtBbFGh659t7V3wSr2RDZGo7iKbI1Vg3I8rON/xNaV04D3Zc4/nJh5US5IvkvwxalXGLD0uDFq3m3UfiljFjC01/SocQAIURbOcxw64AzOwLO7Fb77zxrPPb9z0PN6NRrgeQVoDWYQJNhjL0M08kZNuRBJwMaWVYtbFlORG/vzkTNEoDhcvPcdUdRDeRfOen+PO+aBRhwAmayXEOCNs/OSmcU8dAZgWwsJo3A0PcHJz/jA89ccAgZ9HVYiEd4nP6EhVXetifub3zMyWPgwvZvenIGG1SIEsxrLhCUZ2SkY8uVjvNq4pxQk44jMKXE3Pob2DFiIwjXWl6d0HYnjojLRGXsOxCXDKm+WzXQzXHktukEqzIcy3nK/TS8KszmgFUrKK0+kA06KRvkoGu7Dub3qNWio9D2Mlw8DWNUuuJyX1I05wKBVQxMHwPauy59ykHO+MkuxUNVcEDOtY8aVhpTLjz7qWmaSBVH4OYMgUaPEIpgtprFQhELnz5J4Yicy7lLv/HnVNKvPC6I/eP+VLJEq0+qPwEnvrGj4OH/fems1ebebTfCloRWREDZ5xRKhyMmOhxGlkgobsMQWPrNX3J7kDN7IqUuMIgMfLFZ9XGOOmVyH5O/KG/5Uv/cGzz79x+w137JtX33/2mVgibXHRvxxmvSzVHnzhGsrlPNbQakP7EjUQ1emc9xuAQkCr6Rz/BIQqad7NvJfaX/Xam+XRXFxsg2Dm8SeKV2B3b2J29HetK8HI0SqMeRhsfdFGz9MUHQOUlObqHVWAtAp4/PWgkLmvtD1FlwaJ7CsqZ+bLmw/8F0tQ8CRcM/NA65ShlNFHyqp+Egj4L0hbiKViOLbX94YtCQW3TsKe6/b9vlquwTiUXl10yEolMN+aH2zgqOBHl6pSjiG8w9oegOzSIJSCX6MJjLBtvI0oVNc/y/FNVMsuzd+ecP3aWK0UqUhV55ciWfvSMdrICqtUuwHI2ZUP/VokZ5hyqHHwvY7F8OI+JrDXzKeuykh/Ks6F1c05wbxNgV2dlDFZVW8xDEBC2vxAStuxlfoNv1byGZ3IlfXadTqXYqVxZfY5zLJxUmPZOw9/LuxvlY00bAhsV9uBe0m7scfGcwdINXLAUJdn8/Xfa1/GC5aFlgOug89zZdm/gc1K+FsrseoFWIuyuthZ/UopJGC/fewJChtwYX/+yo/dMYKitWTdDVjnKW8Tm3uS4hMIc9ADvkAFAkN/GiJ0oVcxOAmydeh4BXpleKxm0mQSGB7dzMNctpJ331P2TnSUniyhWeT+/PHwDDIiXFqK6riRxDuhgedYas28V4xpUlwH8R86ny2W3WJtKNMvK1SUhdm1C1BlD0OMz9aX0vNHD0OK+ewrfhKuNdEFve1dUe6zMvh1jnf94u6zPjfv1+wIhsHoBC9jw2vh/E6QJ+qs+W7y/hhghVa7HcLfJ1c8oID9l7R/1AhOAVvArHyQDWh0hQ1zMrIiJqloP0uaNfIBTZRiYSYugpVYsi4p0zQix/XMCIZirlxnAIkgufWgPRFhYPN3vTOC52ST6ZIcWwo/zD8Oi3EihlHxGBw3gnfinBck5Jr1bDrfjGQpSzJVoOJqFNf07Hsd/R6OSdrQQSZwbV1L/Jv66eNG1OChBx+rMLCI5Uu6bLXS2D+LG0CrtBp4ulWxvA6RI9LmgozwHIzD9zqCwszjLa4U/Vv8ncfXn30uIg44EQ6FBAZPf00sa+Q661Il0roC4R7h2HCdbpVQ6mT298w8UdnxtbDi2+ez3+fRTT//5AWht5UCH+Pv5l2vWMigrMSDo1r4oVsHF+YPw1NGp/o2yjnV22FrEMX3UDCoks1lcGxMRLhPYSilBD+EgQMVy9pgOaqS3VE5ayEC8UQUXvUf+XXTvh400F7Xa5YPi8Fj6g79gj4Drq0hJqG87KUjsT9Lm76Ciz33RmBu7tz3IdDKXnYqxXG+KB4Cj/PwL2ajJlsXc5nHeE7u22HdbyxXmTR/7eXcYMHdVNYNo88aVYiVKnNv1R5l6yTX7FV0jdtLEZ6fmKOSFcEwE2qQDaQ4qxJVXQ6bU7E5yT6MHDjG0yXvnY47IIIVtuZ8C463kPhx7XC9aR/L3BGgD+aHa6kie4KygPIkZRNgG6fb0YTPDcGqcrDKz/WL/JgR76UVOcpIDq3MshLmnQ+TmZMXQGbJwlFYkSVxl38HFR1fXvWz8KgXvMPNqt9FJDfbhbR/teFJIFi19bDrvc1vnL3z7PNvP7r57LPLsZpfaKlQbiQvXGwjzbQTvnj4mp+rAin8RTAz2qaKt4VSRHooqaZtuOeNt71J3jkfNHbjDjA2r3oNWj4MG7O96aVKZT8c0+oujnw3PEB/df4QmuShYC+c6pZ/X23g4jVEn1Xarn+7YxFQ8aSULfgdrkGmnd6bYvQowDWi6FhxCQCt+QtQkLLruZnZ5WoQIO/seuTiu4+CQKlVw8K8sOyNmq3DsFcrZS85mf5aqQcnJrnrJ7LVFEOiAAAgAElEQVR3FvNYlvsHCZriIWiscF9papMCXBUosVaMmjGFZaY4PJu7kQW47klZNJ8x1fMP86TevCthZ7RHjBpGeFJAVvzMtfuIBAdUVrPgRBW1i5igjUrxUEhW2ThZo1zL5AZBlLbiF9Xkcjh/4eHzO4V0vLhONWpI/aIZG0Z1xpGSdZ5fdU0dMFnNEhB7R6cihf/FMXX4YiOqUVgppM0FmX/mAhrFOGKk31QyoBcJj0x4OWjkKDCZXBzNoniA2AFMhR1JjJ7nfGfHkx6cvRwkWxuNBic9P3WOWFLpvrEQiqLguKkIXExFT3RxYTivp5kdJeIiVI+VC+/gc35+PM8GOk9P/Ps8vQQCQWnex6hOdddLO/KY0GtQZtp5GLwnVixMIykhTXcRlKppXhorBLXG+lmp4CHIONaZ3YEYBzrXBML7Y+zXQ0NponOAhd+Xxp90RqaS/xyhS3ltORjYEwnRk/m8N/CTUC4iunQYhNL4da+pcp0wkSR+M9P5cYd8tQ2Bz7vSUw2YL90vLAoZE6Mq8sOlYOYv6OmiJ477RcH0mEP1+t331JDhemZVq5yDBqVyv7g5payORKlTeFRyoUmUaFzDTRez5daUuk2oFKyJlFY5rN/xiVwMqbCCBhHqwN5VhByX3DUwHLWApQKiQ80Y8G+NCpMmgNxRJzdVNmbjgJZuI+OBVNXBq1KFiT/1/mMjavBQMBN38GTMVqwpy7cw26gx8x4gQ38q2O53w8p+pe5RVG8shbdzICVQNHjYduJImF3b+PtvXPtjd+wfv//lZ5/HQ3QDFx6eMcLQKTpugCEVfMp5jFJkY+2momioeFBjyzWShIeSEip4b6vvecPx6JUgZcmvo0qSjS+7Z/wCZQkjDRwzzwA6LvP8z5+b/bRGVmde9VipBJUngmkUDa/T8GTqSz0h1yNLSSQzvKsUKR4iE6kGfDTSBTx+AoyQZxL2pyiDm6q/5A+eQnPXyzJ58Oib5fBwXUl1E4zceyCS+WJ4uBKUR2+QTRg6uuQncoLvVu/5a/O9NX+EtPENJdwMn0+vyRxj7uqP+NnPN53N3vrzh+8/rXHm7bCwdr8Y5ildxRM+qwFfRRFFCgtCNm8c0ugDDRLtG+dA+TifRt1KcDJi/QZT9AN4L703kbba1J4t4HVbF2GCzTTG95Yv+MjmKTBpg1WvhwiELp6oxWmzh/w/K6BSEWg8t1YcU54cvhauvf7HfrKOb4XPCoo+hnFU2w4vVAMpzgF5Ab/4Y5qHhs/K2+I8WFw8lh/NCydBAgNidD6cpC3m82YtSPeWACJovIzFU6QX2Yj0kGeU6ELFCyxiAXIohxl3/T2e/b1w7WO1aDGPh74IzGoQdIQnxTADinpnykS9HtdlHQJHrWLfIdc/mycMg5EqHj0jgjkxhnJDGLTFbA4gr9jnT7j7clpG1vz3GLZVsju+SwWPTzMqI1MVjhAA9cf+IIHlXPZqNLkIhoIH8V321HtyM+Gjw2WIAiKWQVHsxOZsVL07Xs6HSWG0d73ic2uMDD3aEKZlGFFtEBkmkopOkGpLsWWjAWKq9J+CH0B2Tf9pk0kO4qtc0DnxN1J/FOaD7NjzMo6vhznM6pxuZlbdh9d/VUloZ4N+zczyMEp6oFrTrAP3phKwbrwTTsr2LcoDU0X7lfYFfw6SKir9ANOaCKSkdTGc4dKKtGJBxHKMBZZkGSpmlhv6eaSBMlj31krtHlrhYLvo3ieLuDpr1EsqT6h76HjrPMb6frrKcKYhRV+pk/e8I2rwsPojVauPRejCjALYdZiEvChIRHiYpxyM/G018XQ5Tdxi5AWkwP5ZQ3C93G75t0hCs//r/uvuGEtrj1ooGS57TfX4X0aK79i/0eoOo1x+DthrjKBlFRZUcGoVs4Q9VcHFRpUkQBSL2fG6yHsiiSC9HvX8Of0pJtU2F4J6TpwDNiqdw+ahbK1xBkzCTTGUMaEpECaW8PoP/IvurYW1fwKyLQV5EtegipqkYlwrqYaKiBLp/o74B76KA4raGThmNm4CyyDp7LVSMHK+9Ud+z33t8x8++8xob0GsvjUYQLULfh7XwPvz7jRskGLTKwHCq5IdH3lkKnK4LIUIcN6y+jSZ+T2nGKr2JjBgkBFqJDFqolireRh0pBipKUhamwSm6suwMlSj4GSaJm8VuaLM/PvSaPnpFRi2GelIM6lCFe1I5ui1H/h19OAXw+dpCwagUDWwxHy55jfk9k4AirHb+FiqHxl5VFLQISM+itmjo4Xtog4ZHRfVJ1zPbTGGyKBcbGWv52UUQqeiRBnpKU2fESP5IiNq8DB3n/LmSXCEByodS5RlA1w7hWxjZQrE+uoFP8sjmIQHUkfIPliDSMvqI/TkOVvJ7rj+YOibvDQrQfJfOxNyST/e8qsk6WV7XgQVV4QzoHUtzDGbxCnXDjepAii5eFMCkZubIWX1ZskMLFgDej0sy9YyWwo4zY07Hh5JVfmITzimTUbnYZBIMw9A1mSU/b40LMz30DnnNSS9VL5X9bQoiE6u+7XHyAFbVWi7D26lGFFbzxOY+47PMOYKbfHkzgdhX6t6C4q4ua98/rY7RoJBdkvfKPl9ywiu0lXk8ABvbASr78Mj/zAJDDE1SFjlMpISYkYznUBPnSN8Tu/b2YZSqs3HYPbneRk+2sV5kSgIqj0VKMvvplJaOD/XZcrLZ3FIpJyazl7JZ4scHi5NJxE+H7zuBdzyZjCdS6jmHUo4g8ce73pLgP3fOE7v+++x6lczAblRuN5QeHjc97jGZLopF1Jpw4hcK/Zmr2dds45jLgKt4DtM0wDgniIRMB1xpmV483rBLGwADZzUiOXakJw9lQaeK4gX/ujUGxonKNZ/fdVXX+10Q7ydPbGUor5WCG/kvFSCPDoKUmq1Ee5jcqrdBKHsJ7qCaAj4YyVwKjDSoS/RNYuUzcxFqJEbRlpIM0ACLzNpJyHvqbOB0uATGLC57JUWW4SjWnYYlmOwGsmPvqRBg7KPlOx0lG3wpsrGuV+Wsg0lRmQ0pVIHD0/rgqYHwmfl3uFgRY3eh4vwqDeONcU+P6rEytXwoKtVb7z+wWFgbbxU9+GrBri1trGHr0kfPfL17PcbmceOBmHDaHDRGTzKE0ZH4sBPgpNzwEpoZ2gqzRSvC/Z0rCkv30WqZcIcDN6fA9DLXA+Ws/UJy8jVWeKob4fFd3xDcFURDh0X3YR805QW4QG65wgPUOOYrOL1UniBd7Z8b4YzG+Glq4HTQ7k5dYhifcZH4XuDSTZmrNjKXs98lpRhxxSiOFqcn+fFaGnUhi2UtD0FAwLUZam0G/ZOW5qrxka8eSg8drXSegCBMWw+Fo6V4VpYoPklje2H819YD5rkrbUt97XfeBCAL1884489SoL1qymtV5ck9/N0/P6+byTSAmZoLKu8DHKzVi+80aWL3jBilUjyAy98HRbqmoDM0Bfl8PPh/xt3/T3TwNQoDkuUNXdNY4LAVPVsqmCLbW0KjT5Bg4j0VY78fJMXQ72vYgtej6R/eM+sWuqvZCvrlzUcDxGJBxXEjvYitUf+nTTAeKwcEqQmYMheU4Q0clK8TNirbIXRESA5z6+CzTXVlHSaM2ZZiXXO3+R5lIOTw8rM7Hw1uKZ7Pb9fVkrhd0vFIOy/tXPLfe9qM7ifb297qbcG56SDvdnt+ckqvhus0d55LacD19HZbNlV/144p6ZZWIqt9ASM/PKdKXMs382LVKR8WoM6N9ZXkUNTFEwfqTHB4N3ul8K71OhDBakwTUexESjPR7C4mW/Sq/xTDh8jjNf0Qe88DC/27FkBHKPSt1jwEzRB6ooprdyOf5gc5njUEH2CCI8afWSmpqGnVAp9rks5R+ci6TD8HJT3sF8QXVJ8Izudj6r+HAwIuKicREd7iBIVRZfFxsektAA0FUXK8DVzgCnPHhTZY6mQYDirXgw7/nDgXeKvnA2tUdtCGrjTDsLyRsN7gD7kHTYKr2VmdtQL1sSjxz6lde1iYJA77Ibv5QWTQJDkQBYJBVhP8qWsTisA+5PiHcCUaP8lhoDVO+TvKkhNaZfayk6QpMfX/QqlsiWfzmBJcCsRAVdsh9+pwVPdDRPGBT/Nzx+Gx4GW8XEi4EHuLAWSs/O2YggICmcX41TPn0hI2oOKwxymMA9kTFaQfKTbu/MOgTceitF03AlfPGz70Mfr14IzctD3+/1aLezjR9BAd479RG61g7OjzUmHIBskW/NJ34u8PMp4re4lfx7MziPRwlRIp1dh6CsjPTJoMdLAKIcMRlVawszDaGyF+e2cy96zxJ0p1ikmO2jkubJxrXAEkFjb3hDgTCbyfmSuU9g70npI+nYMcH2pHn6oLYiYBi+JpcGiGBLbjpb85BROWI4mUVUQnvakPRHnP9aE1aUG5Zhj0u76yXMVwsxyiK4kMar2CcuiK1D55Di/YqhuGVGDp7UJMkABknH/N++Fzyff9Dt+uRaeTu/rtBUEInE1DGObmW0A4NgSg+ebF0Ovq9/c8i0jaChtd4NwVJ6fESzrzfPebSCb6y9sfvDs87dBQmhmdryHhb0puxe7VPv1TItY5Cjb11YC5FeggDXzIciSlkdDsHTRxFKF6vZPhznX6Az5EFoXwZ8iKS1if5TGoHsm7JzatuA57gaLsPNmaOnbfE9cuDkYxGGMgTsrP/ZbaXAFgGYxNNoIXatAYVfgGHCYQz1dYkpaAFoqeNDlwUVP8ZzqxDB03V9FSkhwLiN4m+dWvRtGjM1SyS+4O51gOX1wHNzN4443mlhEcGXDh0/ubYVz5LnHuhK9RNPicl3ZwbNTqg2wSjPCluov5Bok+2Nk43atFSRtRYLLfe+PzcVgnypWm2nbBq4p4nnMvD5Zuu3Xw85XwiS6LtmivQYko5OScsIAHARA3skYEQfy1jy55/BZq4b6g3AzYxgrJSHE5J6YiB4ime0E6ahE8IE0SKrb/hxdFMFo13nXV5FqVE5P40JTtCWC6yNrkRihgtxHFgmkjnGkFRKfLUaQrCOO4SETayRsdPQ6wvyP/Qz1NsMsj4Zeqn7j2p3wPWjmhkRgWtAKJ4Lv+e7+lWefry17yf/ecTBxL9XR72boQTD9UpiGqw05x2E4xw+mIQ5IQ8jMLFcGgLXnn7N7BWWlYu2Wt1GBgdRBz6d+PVmWGCsMDyvinkqNpH5aQdFDQ7ql237+9z8HY8V5mP4c9S2UAl9U1yB8VB6eSi2kIwZNgNC/EGEne0nDsbTCPaSBY2Y2ZefjsteCvXUYCWIAuwqJCCMs96YaMjwHIww1D3HzSlYEPxVLFBd2SG9ZetkBo/Bw27/LZRg5GxXvJL17EJiW2WaiLc1DWVDw4/d9SmvzerAmHj7Ibv3QvBg2xXAkVh8mIVf0L6CPeWXjT6XvcEr+gey5jI70ChJnCiCmIF7WoMEX0xMEq+bFmGDLlv0veB3CqJkqYI6VHyObIBkJttJhdCklS/cRrZMKJRoJCkAftnBwzGv5vT/shouPtv3DkBOKuLlUk1ESIMq+rT6enXkx80UxzK4oxw1ldeO+X7OtTcyPYH+os+pAnsTS5XmNDWRg2XS+aXB+gsSD4XOslxaVOEt1zczqlXDXuggJHmaH5OuSmjqE6X7S88YKG40e9P0CYtf1QTU86k5H33AY2iC0DyF4/yAI7f5D777kz4VZlyIny7XDOcZNbygNVki6x3SOPwdTiKr8yIWS4nVhvhdTpykt3nL3rIABM1ieNYrz/zP3Xj+SpemZ3xcZ3makq8zypqurq810T5NjmkMDcrkj7MoAAqWFdLG60Z2gP0iAIOhKEKS7XQhag6Wo5Qw5ZHMcx7Qr02Wz0puIDO9SF0Pm+3ufk+ebKqDVFd9VVJ3IE+d85rXP+7xsBaERqsKJ7exJSRVLOmB63oarSkJJ6CTjj1IB6cnxsoK0sc7Cyur2ANNF4i2nrUkI6TQF6gmxo/T+N/xFCvflL7xUOkHkd0bMg3R1HkAJZNr+/t/70ErPf7DvsTnfXDUSoyddO3NKUDh1jZv8fjtMoZCY9vzE9cDRkxXlRAdk8oYoLjLrZiJnE17wRGUo74ezqRFWZj5iPGevazj2Z+wbfVZGvxK0GDk4D6LE+XeM6ijYlrg2NbwcP5Brq5D+WwlZhO2mOMoxolwZpEYTBSwUdcrITGfYRbI0JZSO7XNyQZzrU+xTcrlpBJcpp5lSyTjWff93XA8aYgmW+PRj685PbK/z+Ul8+dtG1OBZvmezeXzLf9Xl8tAgdNwVCnkYGoOBFzYft2+cfb550TwyxelUULVxe2nfXWPJekHMRQKQWb5+0Pbao161Xf/zHV/gf71pLtqnm+ZF5qV0foaNnakLoVSRqD53KZwC45Q7sLka19M1v7PUg1dkQmHkDjSVmILQK8D3aMVOFYRb5GZKGDUIU5eOveTP9dKr90Zg4c3je7E+N69rOBZj9oITVlMC+tgnLgTvkZSkmSy9ocYT+7GjN/3CohVVqL3w+23/fdY420dVJEe3mZ701/iex8IenNYzp7AvMuKuRU+uXPXhpRo2pkZ0x3gY5d7hcNWWWt4KYd9s2lk9mvmzT1tbmw8P38dCPRGL84LJpMN3TOMrCzrPn0biCOykd0/wZwgeCrDwkmnOr3VwC2M+NULi0lGaJo10xnbEpxGjiXsgEVXAFqNiVswIFbqC9V1/KK28hFM7g0MzThBcITjQ9Pt+NgTg+Bhtek4kUIDjrTijwRpk8J7/O/a8pAxSkDj7Z2sVG+dOU5Zpe1Ofsf7UfrB9K72SzPXt0sge1nr3w4j3JyNq8LAcWRVkWsOujITfegz1HQpXBngoDhZt9jYqHqE0gkYvCYLyGKmwey/W3bVvXrMeXHvo1nx52e/kEtJTA7EYWPb3zmWD9H/6wu+EPLzDBbGsJ9jkGWn2Nl2z9xkt2Spqo9XYQadQVSFDcsAxwc3K9TEAlkHqkMm344RWegYgjCWHnkPrDQJzQ3AR4JDBtcLx/JGO9LDFPP5K1gu8TC5UHfzaqsClIDp8y/ZigngQf5cRMAMFM39ZBRsNZQXbsoGgGtiMNpWg4Ft3paEusAyDSbpQOuh7YUJD5hhR253PfYx+tgSwe1EMbDg/TD/XF338u31EVL88GKn+l7wrnRqrUYcmRVmH4InaYjeno6Idqudh0EBhNaniXHhN7VhXpSXXGPFiFZXSJVz6kf3g7u94Del+G+tQEPwbjYJE2xfIO9WHQ3ZFZwRXojOlHbtJAgNDigRstxh7t87xCJg6NfqYRnQNX8VIH0r6iIMyvyIVbiQ85Z7Qfa/Myxwu3cUyeu3RSRqDrwrDE2ukRguL4b2sVGLR6Dsti0Ds2c/3UC66IFLjG3ULcf/1oQcLv9ewZOGVO14r7CMVdrVm19RrJKaHbSxCCGGrZ7uL/EBFAaOV8e/WiT9sM2KXpLx14RCsnNiQ2neMZZBqbJL/RDchU5E0jBSEzhJwUtmHEEJ/hUlv/K5Y9PQ86k/9ew6bttZKbMiS9UmFxFmRhP1rGjReaNxrhIR0DBpaznciHkmKDlTj0pXISqSNETn2EEqE71PC/CF4b00xJbVNeGg3cX/h1Bi37bx0Kn6zbOTsnPFshuDxfJeqlpvIv+sl5+NNM4AyMscV0EmQkVkblb795uOzz7/e9U4M+xkFAY4uHNq7ca5I0hmCnx8lHWUax0XMFafDNNH8FS66c0/DZSDGfBpwOITgjLzGEz+H5AwjTqd3wd/k8B2TF5oWdM4YIp1a7syqwwQBJP6tso/A4somyFllDghAn/bEUUHlH+W/crfRcRlcEPA3nqP2VCJDrBwF3kYSKk7WxAIdqodoOPG9tajCVYtJWpKD86/fc30hX4G9JG7wZM737EPw1q8jSNMGgvskZvDXmLaZ7Ntm/aLq6+lYpnq54oVjBStSkdX5y00zjv6L678++9wR6+0H9w1DcHXDV3tcRLTpedtOx+WmN4xaKG2vVf1pOKbgFIWW2bDJm7TB5TNKx30oap8buS+liNw05GE4lZI5eg2TslfIFMZFcO9oJQS5fAYrimlBY9EL/lrjS3NTpiUYEa9Qbvh1DTb+7K7YxGSlwicPw2j8wksGemWnYvuk8fDEmJD12tEd+ztWXOjZPI3gLcjSWt2U0DgA7nkcA41K5iDQy1e9RdUFyUlPwv4kHizjTP/44XX3vQsX7McH0o2dRg6jtDlJe/PahboPnxwgxdXa8flAdx5xplkJGYIYkrKdWaXF7+kZJs+IesvzMOiVE3+uOLNT4smUeJB8YYuqbOxj5/L5RmIIIQwgw/ISseQavSzDeEJW41qMNTvWs3CIKrDSCz9BwzV7GO4vjeg7eXwoTgbebahYfUKEON/SEobdEtSocZFgWSYHksY1tlAJIYQK5EmsL2Rtyx7y4B2/YcifNNHIbGT8luahIE8T76QNZecWVSaB0fxY5VEGG+b9VU8uuDeyFU/w8GRMEL0lsfd31+zfeZyoe8deonz0xqOzz09PvEmew9/dWTYJpeDmHITj/W0fes+jT9ipHKIFTNAUnSMTIV+U/Oa6fvFdvzLla8Hv0TAqHfkvjtDnRjksmJ6ibiJPUwjBNQyditAiD89A0LP9dduxNKxLBy8PRvvaBiNcFaRU1CMga3bDK3uWO2uaiQrTdzP338tHqNW5B+ogORxJE9AYL4p7XvEACQhl9EcNquGqbUxWW4UQwvOR/SENnBBCeIGoagHn7+3rEr7EION6CB4XRGLRC3X/HJ8fmixoFH1YgKBo4hRD8GfOzZ2eb9jBU5ljRiFI/dCTSq96hKhyHoaL1mAvlg8lko6GoWpokMQzZgxRAdclEkQ+Mk31MLrE86HAZ5cOlvVawHrpM9Yen1+urc/Rv2IvM1yUsGoPeFQYHQPpXlA4AmZWcDQkPFXaEJ96BJZIHOjmPfu9o7siM8grpaXi8BeYgtLIL/eIknHS8epctPnQtBs7uituLjbiER5MWEfom8cpXYA1x0zdVnvmr42atIpRTieSoYDn+L3mQ3ctC/OfQjSEEN6rm+G0C8Tmt5aeuu897dvf3V70oGh6mCvo6vyj/ZvuezSA1pf8JGwfYrKeSbrrGsLtxIQIA+XCgc3JVNJdFDhq8fNAM43FnlgheC9tsKTGyvnVA64ZYPBVeIUT6eANHp7yrqS70DCz+tykynBF86jzNbLgzUhUY+CfiyteqnYOzKgu7fi/o4J0DV21WzeUgBKMMSTNcLiWqbrQtYToSfl+4ac+erX//vnlE8p4OobRW8v7H/hu1Titfnp8zV3b7Zi0ZGrqghhNPHP7UlJOR4UFCxdL/iEvX7KI8RcnHgM4xRl5WvaahVHWAjlBFPeRku4JwZPeFXGPxYf+fPfWEPUM8zfS+oC1bvlNS0V6+I6/RjmlRsgsxaHTCHOswofPSAiGYuM4wWOJPtA50b/rXgX2EMaKa9kTQnD440TzNkR3L7FEOl22JMrqIf8TPEh4Zup2navj26iYE0ZpAqjV4CGwmCkotRUY+NApKKO2gZEtfUbXgucVoJ7xsnR0yS5IXw4+QOxQcyhIiwJyDIOqKm/QyJsSvN/3Qukidu9YgA6rWTM8iiVgbCROtz+wnXGx4gXiT3cNiXVn2cJcuyc+xH13zZhjX4x1dZDfveNTYVMoynHA3ykFKKY/0aSxcP73QgiBlbtkItXiARo12ryPnijxItrVmPecKoiX/aOW/CbJDu0+/Qtm5Ci79zwMYjTyoFyoFb1Cb/fNpVRWbgpVdRBYScUoTkIwR0o7aTSRfyRWHqr08vSID96TYoOUBn86ZiX0F8r5Mz1COKgj5+X2srlsSwWbhB8+veW+953L5ri8s+KrwNaLdo4LkMx/te3v8daSndtSVri1uFDiBU9B6Z97gj59Iv8oahJ4Bcw/GbyrL7yzwDWMMRK/rsF3zmNv6L7sXOGm9ddocGs1oWM05/wqP1RaT6/gsxAjVjmN/IO4/pGRsno9j66tDs6trtcCojih6S+SuyvA+VVGYzq8C2IM0VjU52c0loaGAqsnoCzJSpqaRs2pBOC5NgvO6Xr5lBl1COd4Uk7/3quMqMEzRd+koVhpjl2UPBFyR1q4ZR88Ca03WJ5jH6sS4t5AfeBQYu8X84aIWhaJ8pOTG2efV5DUnQq8n0ZOd+Jn8j+5/PnZ51+2rGT9n1y9777Hpqaa3pge2T0FcB+ai/ZcfRwG5WggdkRzuo4xV5QaiQI7VyIGBPAymnfuIc1CDI+utTOaBEhb7trhVhAeq7Y4dWr9z8M4Bj5mEWnMo64/ud1jO6HFupASOs9ehA3K/rnVE0oAQzlHiC9wgEGZT+eARNJiOpjiIjNt40t/k8Ft0OiLOzjDGazkvOTsoaLrGDH0j648cd9bBgP7RHIMTIOvF82qXNvwMuKTlnlajw59hLjXNmuuccVPcufR+aUsagA2vgSAfMPPD88x9333kig4CHveb16Gi8iw+kfSFVRa2ipg9ZdobXMr3Yomf5jKHxpNml7lWSIORZ0qnk3i6ULwke+OD0o6BczfUkxXWLKHzEn/vQnK1Nm6ZCqRrMoL8LpJENxRj2iRAs+0wwCKrAZ8ZSS6hrggTTP1Gbkhd5LCXAbnfy+EEFY+MVmw822byEXd97ipRuJiI948FMRyBWEJZbiSG02VFPEgBJyF4D1FBmee9zyOhk1BN/te0OyXbEW+UXnurpGwcG9oGuODhv/eNZzMthT8r+I0f7Npf3ev4yNNI/TuOZa+QaV1s0gGHW9Q7W/Z+7DSpHDgBfhokah9dymcokpLNzlLAGkYaakjD6nmhdlVm1EiRgBD8FGdRGlpjzgEb7TWn9kGGtdAQbA/f6QjnF82jF2q+gkt5k3BHz71oc0cvKajt+T+LOdEUCTamVhDulgWGj8aXmfuPhYlUhXL/Vd7Yn94+IHSQdt53+z5ObhftvNzo+o14+dty9Ex6pIXxO6Lvt1zIoUdkqwAACAASURBVJY+DaxfHVu7EjWu9kBCym7VIYSQBUN2e1M8PsyB82zF640paI6FSBlyDmvdW5+/qKeLFDLKojwtEkXkYIWVciq5bELu/M8hhLDywH5g95v+Ig1/5zNrpSl+K9GegikbAUWn9aYaLAnOUaO9GJT/p33geQ69wGckK0HgmJK60++S3WXtF16A7HwHXdvFgV79hb3PYMlfZCaAcoxs7CGE0HyIc3Xdv9vRXfIP8dnTC3XyXxXTMsN07JIdgo/W0ApU8GP7WjqqvrINkGvJ7r/T96u4gbz7dxYfu2s/a5up/TTnYemuXw+iRlqldQ2op57EATdHZnyRLK0nvCLbLROI/WPvoeSqzAH4TZIlA+1CevTEW+FSihhjBz1N+Sxys3wAg6QgHe8RdSC5oAqEhTFKtlX3gYcnO/YP2duwOV/8tUmmk7fmr7UEt04VRs1+2+/Zqaxz2lDlxmgN51C5Mfg9rUhxFXfwfjQMz99WwUlB13iaDj5llCh/LD3qlu3MbXW8wcBqyE2p3b3b2A3njZ8felLQ95YMxKwG1QqiP7uQJ2rwDLWdBMYEvdJyS14zsss6hxqOdB4SmAemFTB1ClCnE6lKbB4GlScLXRIYG7x/RZqguu/Gmqfup9//5IoJTXX82EpniKhOgtE+ko5y0RN5rrRoXZB0UR6s38M9v4dySyQcYvNQf/7yJyhf147lz8+PEIcQQolGH17g5KoY+jAg9B7jCtispR9a7+L5DpRiifor6aBrt27MKEtKy3EzpeCJzxu/pUrLPjvgalAlaP+vijoabsKkML/4Tk0BCzZOJGb8LRhAWwISonF0H7WeivU5BKQ8L7v8EFGif//k7tnnqYBU2RQuI52zKRynN7w1zfxp4x6I2rQnFpTaUEDFrimchtRRycA11GjbaZZgH38PbjbmjHWtPQhWoj8wmBN05fjpFvpnzSNegYOEdpO8n8/2ie0bzZGPV01DZKQUhHPowsKiBFxaScLa9K6aDwAUfTs9wqrpB95z93f83zHkTTba1rteo/8Bqh+X896y+9uOUUbMNAUMwcDzSAMnhBCG2IBqyHThZl9CypoVYCGEsFRGKuXIS99S06SqsjCPiM3RPDUGI2WVfb8P9j60z2Pws9QeieGI39KS9XkYjtQP5zzBY+Mq1iT9CZtX9yJ1CA2eYVPkCG0mibTROGK6Sw19prG0nRJTOKqoOaaV9HwwI7+1a56g5qRnwrvyhh2s3qfeIaDhrAZb6y377aVP/T5qvWnvxp5hCboEvGcCO5NS7RaCAI7r6d/jujHoEYLHsnFozzBG7HStYyNq8JRQVthbTwfROjrxvoYYkAKRB3NkWxDSClp+3DVr6KqchjpqBTdFcDLCcwJywazEwPYAkBiLZpnh+Wul9BTL9h5ccAlbOsS98Ovk0ZKAaHYFOLLMT9OG9PrUUOIcu2qPCIBVR1puXD3WCVJa6n1Vdk36jXP+WuOJ/QDbTMxy52/+1zmqxoEZ9m7aouRy6UJuQUCHs6rNRaIiBfYPt2mCeDBCVEdhPACh5IKkvrh+3Uv+GrEibCz7m4e0jzEByGKAujDBXS9aiLgnFttnHSMAbI/sB7TJaCPHiKu/B/+OoGjF1x320HMrsoYTiQSRJZnRb5VxoBtKtGxxQFowRSfAsmldrudkFABboDOWkCmsgFIcIlvgiNOWc4Bm0GcoSzKMwQVJc/BcOQZf+S3OtTrrLDDQa9wPZP1WwC6dpFxWIjc5kwukRNAoTufG+b+lY5b1C+B6mcGASHR+R9xAixk4XyS8DcHrBtcWQuaYZyQv9sIQDjVTiImmtA50HV56RL/augmcjhxkNykRIiTX2kAsZmepYm3+ft+Hruvgx/jhgW80yNy9lpv/4PDO2ecGtMeXHZ/6olHz+MADF+9esPB6s2T32Ol4k5Md0hUERjwOeRJC8ORjLBtOhL8hBJVFlGV+id4qS+cL4xiGR0uU05SaHoYKiAcZAQwhhNI+QSFeok+RQqORQwbmeRkuqvXIFPpIWMQLHfSD0rYNKHcuCZCfkU5HxiYKgsI+ibnC38E5lCBLyCMkzZRyCCF0wG6rQFmeYxrizV95cfKgYRpou+bPy6UbZqX/xc4dd20N9A88mwMpveHZ3+z6yA0Nm+7I9tt6zaNlnWJp+305ZqWdCHfyZzm+JGGlJg4hIfhxJKrPQI4Ya/MhHe/nYdABcyS0ysQLDBnZk0OQ5p4iw2ijUkaqE0ClqJEJV7KObTRcltRaSrVVCGKkxXwxklJK8UkPe3Gj4bV4rXC+Q32/6g94YQXUHUd+U/E9j4WZPI+eftMK9LL0bSRvjuJvmC6fifFNTh3noItNxnns5LxRRszvBMaPOnUs1ta1jo2owbP4KD3C45qPcV41hw3Fp4KZ3aapxJV4sDUCC/OBD2F844KFuf9qz5ecNmEoPWybVfBGw2sZeoc3VnwEidiDDE52q+1Pc4ZlhK30RqvaCI4byFUuyDzGBKcDzMkmpCAhgFWFasxKTkutaEO3cZVAdglVgodHKyMyUDokGyS2Z14GD/IEZaXFHS99aTRqHtw1INWUL6aGgr4qnHucX42mpTGeqtBgJZ16eRRmGmZ2xjiWWRsNThHN1NL8jw9vnH2+s+hZTY+QL2qAv2cklViM3NRFWex2tbPkP/zNyB8QRyAq3d5dUYucueJeSp9Bib42HgMbJ9VXPEuuAbA2pnT9ecLcDVZx1l7AiBZqis7ldJwOlVYp5oxhn0qW0V+TaCYLcLqX4ARqyxAaW5FKxaV7/uLW7+M+gDtkj7xgLW3YD6zKizK1O5ja3+Wafm9TD2nvyoBO7WHozwtT62xjlElkXtKLT9jyIrfv546ONx17dRaov3SduDYuih3ra6atWCIjTjzIQygH3jUqjnFxRH6BYXRiTaYSdn7SNjf1e5ceuWs18Htoj6ztvnmVIzQF1RJWkpTpPbpDm+mTZ2ad5NYUGo6ScgWqwcjpXfTXSPBG5lAtIadw1K60jP4kwHqIsI1q6QKHr52oNIGhyhRMWTqizyI4HRrItSd+7kZNewEaRlEv6jUNJ3wRxZlU0tdcFVgsRcF/UxgoaDlzmi6UCAviVtfvlUAxwDYWIcQN29zh+ddUeIUTkGXK/b+7/Pjs8w/3fX88siuzse9K0VvpTzogcBQOnQJaSPCxDjt+MXpH9gKZgRgkTBfIXswgxeWiryIWBssoL1Y2a8IoCAuIRBYYMZ+XQeOY1TQ6F6esLhJMRp6lypGKRDrGmlZy1cISHKbj7c6ERKkpPxOpRcjFQ2EgPmV6isUnsuZX6hbZzEnVIRvl0hAfS2XvaQUPLSkzt0+VzZo0SPi79Y8lZQbDNKG/cX+NZvo0+8sJb5UtXA+XaZDbuT6fX1VrCaKpNcVCr2YcqdLiCxQEr0BuEVb4HA59KIgprVtC5rOPHy/KQz49NIH4h1e/PPu81ffhb3qOmqqqFu1F2xCAk6GfulN4CqVdfxjYtVYjPBR0TCMohgfFYkmLlsIyYu1y/lWosGFo57J4JQhFc90TTe3I/ivA6tKxXTy+4yUV0f7cyI1789caOu1Qq3CfRM6EE6paSYHt7cjBVH7QABZDg7hcKouhhpYRvlehwcqvBcWTEbCL73Vv+vN36ZqhH1fL3lipQJqxDUQIIZSQP6fgf9j2qejO0F5O264RZLxRtX20Hfz5rl1EBGnqHaH2I8vbFy7755/u2MLNuPaif2gMKfaO54UO5WDFf3Hxvr1c69b8eQGuspABBlGIPNsE04cQwtFbtqGrAmQlNxWNQXXMqE803cUKxVh/uf5GCrA3pMM4QgjhFDibLKqoput+bzMq2Syk11Nz3xfq/h7jA2yWij9z+bL9e6RkmRX06jqxF1fWa+qeoezFUxeplfsz+gNHPgFej/Te5F5ykSCxP1w7pVdoqBs1eAon9gJ98dBomdWf2vfYHTgEfwC0RxNJmZgyy0mssoFw9YLstK0BGnqWfXXXxqIinX4zlOa+M0Yjw763pnsZ+/fKJbPOD/e94FxAfyvts+JK6JbTSwxpkGgpXw3wJG1cR6Hal35ldfwdMRvlfb+D2Owz0RbKta6wv+te9Nunsgc6/5K/lm/b3/VXhFdicv4hGq69AqPU1zQoDOjZJiKgKdVWIfgomRpKXNtYlRorKdRoculP8vVIVC8XARbGwLGO1j1iYA8RVd3v+w1NoPK9fZ+mvrZkACVGbtQwYhTn0b52SsQzkiBSKrHeuGTptJ4wPreX7PcmYwWM2J4lZurkuj88jldJO9LjddhFO1HVAsNBCfvmYVDecf+qg+uBw/4lTxdQobqUPoeOAkX2KCt+FC/KYhqHLxQjlKkejUKl8caFEFxboOkaLVl/6K7VbG9rpmHzxCbvasN0WaXs9VWrCBC+3J9DG1A7DA8IQ/WsM6qTKG5B9mIs5fLs8UUdothBF7GTa3SmHAXIC79QjEiXz2exOHfE8c0Eo4kXyS6nzPHrBNW2oGSlh1J3/XyMhnpabzesCeizgXcbyvAGd4Z+hxay52uMn25edf/+4KJhhpo1r4F2d20TdiPUv34jSHoDbTmKB0oTbp/T+pOFEEKHjywGCau0VKiyTQQPbH/Fa8nFJ2xqpwYJnrecfsCGTfu78qGf++GS/V79id9MM4CWIffCYPkVTPevaTjmAyzzVFJalRc27yNlKSdoMlLBwPurN0vDdl16XR28a+eqCjicgvsoXLQKgkpWgdVp4M2VH3txcvi7dnYWL/kfeIEc3Z1Vj+F52rZJfm/Fzj6rvkLwVVu1i+lRIqaprwrlBZXOjrSLyYF4cLwv3FqOIwlltoK1Iq4pZqwwWliQ6iM6P4m04RwMGsuMDOq+Zyq+fV3ay0Tei6krcslIkN0xWWvhRRruTNsd0ZDRuSYoN9GaZ98O9WTN9l627AXy/sAOj0Z4SKzJCE9/ICktVzgi+mQTk9X0QmMMbA6rg3XuKSeyQzEc4diPFzVkyb+zz5oaTDNqQpAqYDy+Em6SDiNGEaDjtzQPtc8qLB27Jt5bKaCJxu9ckoXDrzvsghgWx2OCGL1L2YdeHUnC8ULJXIydAbqqX/AmIX9vseQ3Yadhzzz9FdyXNa/Q2U1Z+5twcZRHw+VVCVL1uG2Hd4pVJygVOA86U2sN6TTMzrRrP/Xh+52PwCkDi0TxBK56p+Qfsgi8T3/dG1uVLTtxNIwKnQhq8DUNck248lBZEwKHY6XnWlpLr7LBooELQlgJgbL1PX+uGG0iTEAVqSO7E45HV8Iqe4oR3dbtSIplnG4cTzBhBGiGEMLtpoWvnnftzF2SPnc8t5OZ/60C+E7uVG3RtgUM9bRnlt1aTRYK7/2w55nVF44AWo4w31JoT+Sab8mTniqgnFSSyXkYlD+MpCjXDp3kIJFBnokYI7NzMkRZuoilKFkCq7MpTkUIfi31HnxGPe8TpEbZE+tU7k++qKLgzm4vmWdBQzzzmd84mev2W9mqv8c0w4mUdFQJBtVhOv6Q+kuhFd03wCEmXHQjVFzlOjQQ/D1oSE5q/rd9dDP9OeiQ6VrERtTgyaKxWk5avLsQOC1m6aHkXjaSKqGAVRZjlqaWxeweg2ugNfZWWR3GUQMnrCOcHfw9DWuzbHW0dD4wLQS/STTMH00PADy2gDnWzta0ARPcCBQkMseMmhJvMRKqblYxHL3jU0nOWgduJzcQMrZauoJjG4rypp8gYnr4HKXN+Wst4XrIYc3VG3TrKg34XJWWAu6wzlUagk3/RZ6dnggN56V20w0SPocaZY6PRDwoKg8nvLSVAJiW1Uvdg6e7IFqH0Rmms8vKjYGx0/XRmfcXjTCJzYLvnXiP46CX7h7mESE+FTyEq1yEFNUUJb83lKyb893o+Ci4GWshAe65GIzATEBvoCmnaDUpMaGSyS7SoYO9mugUDqyhtiKgjAQ2OGm4QPlXnylGxT5rVWPo2SZwsjrSSkKLc/7mCwPvf/eOFedM7vi8Dx85n/eOd27HXm5wMfixwPnhQ/qvTUCcOLzgZXyGIGmhX3Gs0ixqktL8GSq4MmM9V+dnJGK90RYiqX8dUYNnCNIyxQkQBOb6m0RAmIlKEzwolfFG2cd+y3jztmiIGwAzDCXC80XLhNsUp2O97KX7MUgJtw99HLZeM6k0Qk5fW0S49J9WUZXTrxHE7BrcSW6Tsl6p51n9oflSftcdUt3kKG1Wz8ZREMBlOX7DSwuG7Isn/qAUD2zuDt/1SsYZOQemhXsXXwF+/zUNzieNmnxbQNoAPGq5NnPOeljz6MZ+9Ka9v56dlU9tngarQoOAe1JI99eFbwOGWF5SWjSG1IB3/YawlAQthhDCrGVfHAm4kmlqTUWvoEM6u57/u6dvu+/96dV7Z5//aOOBu0Y5sYv7axVmH/3QFL83PQAouuaf3xky2BMXfua/d/iWrY0axY78jVUtoiPpBedesvrl6xzcK9wbyqJLji2tWKPcUh3icDu4v6ZhSZCp1yYpRs7ogjeiF9DDSsGMNLay6sQi/VlqmJCvlPyit4EXrUu24oM3np177eqqD83SKd996kOz+bumSDN9mUiko1ylaE2MGrRCOh15GV9pmjLoHYll6qAPds8EEgSRocZDmWOoX0ZxtKWK45T7qnppNR7ZXdnUS3/QUYtL9CHHkLqGx5CmoYep9O/Fut20KSzJLVgTLFEPIYQRQuUDACifT732oCc3OfFCb1iw32YH24ywso6X0Bm6LRgYttKK5CxdOFVSiOw9MxLG1o2PTVq0b/o/pCFDQVJ97DXtyZUUXpEQhBuGprv/Go2c4aIA5jr244wchiAGc3Hh3P+fl5HWRVwBxkwR6ZlwRqh2NMZ6NZ7A+Nvwk9HFv/W3mULtXiUYVsCgkWoxxxGj3jiZt3FLJdVkyWyl4V+ULVsUvHkAHh4aP7+78cx9j1WZXYna0vkh63JXIrjkPqlWpJgB83W6nx6mdSWyRb/v07AjIaSD0pXGIIco3TyeCec8uchgOvhYg3UskOluqNdvn13aSpZkMZIC9qnF9PLHfMv+rntN0i2Ob07zNPZxPLJF6sj9//DiQ/stKc4h/xSjP9oYl1Qp2UUvrCcwUBYk+jNDbIjszdmu4kqRmqr6hSKh50LJC7ZTyhc8Y0jInfRMBh1ARxYZKY9/FW6qeFn6GsiPJHfcBRkye+soRoUWuUY3+G+SFikh02dHlj8v5dLD2ieCAGXXcuJ2dANR6F294QGUL/ZMO7GUb1YRDA+8ZcVDULFMyn51cli5GHr96K3zSzNDCGH7u3ZQ1Jp2aUNsIBo4IUiIUKbYgZYxxbVtrbSIeKklYpw0BUMjClGTOeQcodDjwU32i7HPOp/c90ojwD1wchXNELX0HJASPVdsL1IEOZhiI1y/G1G+MUZtV1bqeH4k6nnXNqq2dGDkZk+AZzRkcniwmkwCjaZPjnwYjaXopJ14suXzSvVF06DDsReHM3q3Qu/PiBiVN9npQ/CCurbp70GwM8/fYC2d04n4qXkZ0xQZX2j7ZyWpXP1Z+nuofHNnC9uoeOzv0bpFGePvwXTXyd30HIgzrjp+zxK0rFitYs1evFQAzkXsIkYbLxR9JoPQCnYGIP1CCCFMkF04FWNiRrLBhKGBv0P6qfbYf6+FyMq05z2hMRTMtCeRZXQbCOSwGqjxmR6lpLFfgirWirkYsWtsRA2eBB4HIw3UyF5DIXgBPpCSaW5sKuZKzkvwGw3baTfKB+7alz27qRpKW3mbJQIcVwveenvQsnvQeg4hhJWmhZ56FRO4/UdSgsD0ZaRpXk4wFeOUMmRVYgz1qVHjcCWREmgKpkQpc8pahODBw0xzavsI9stS0HIOJDAzwUOQZ2OwbFuy2J6/1hJpqR7NMbMFgBrALClnOi8E7xUTy6BpRnqUWlJejvw2B50YxTLw77RKiwqeS6kAxPDMDJLpRR+Z/XfPLT31L67/PPUZe9jcf3XomdTvNs2JqUjJehuMysQBNZqChwCuobUrNP0F27PLP/MTxAIAnr/lL4RMDmnfjrRTYEUX94EyFHOtHVvxnAyHGcPeqOwK9cUy0odr/j0Ixk4Av1OaZSbmCdtPK+Kc0QSFm6n69eL9dT8zJadG2fDE9tu165aCag18xP1y2XIziuE5wZ5tFmyuqgVpOI3Hmgm0gjZOgkiTWBp8bL8p3lqPC+ovTRBJzXb9mSi0ILuAoZoVX95IzzN1FdnqdCS08CM2ogZP7YVtBo0IcANxk/QEKMUDoJ4i19sRzonby/D0j/ZvumvXUGY6EUj8GOXtj49Ngr+z6hvSsEcWuUNC8AZQDfnY7rJ3IQrP0TpBZpWHYyDAxbJBGRweQi3aWAiPc6dVHK61RCSV4gjSlE6c1RaRvTtqpKfFyMPTlWq9xj2zyvqXTEAwKjQvY7CO3DSietpzhntAo6NM6SX4QhgNfEkuHBXuPINcy5L3FRxRm64rUwcFuf84raxULPFZg0AXLz/+7Prfn33eFFAanZMaLP/rdS/ZGP3RKi1WwxAUfbwr/FnAXiRo+iPbj/O6dM/ucfCOKAFEgpTfig4glYWeYRoA2kl8HoaT8dwCsqe4V1TGFNsgdRXqizT8TSIdTGUv95847h0YPCc5+V5EwME4LrbkvNfgDCNSU8p7PfGsZ3poSRxvUiaQG27znk+blC+ZvMznvVIdsU+hgIULaIcyWiXQT4oqcA5m0vk9dwhMmkaFSTJ8DCf2QvqcquxiAKD2wP6uJ9aPK/x4hYa6UYOHtOiqxJ1VH8nN8gWKclh7YLXMI/LxZceHgi5XbCP8d1c+dteejsyCeD7w7uztRXNNWeFxMPKgWWJ/XkgYsN8BKeGJeYCnEpYbX7PTt7DrV4AEi6p0XOVGpOSSuIyEQcLIjQjpEdIbjcf2Awyn/+ameAy5pF2e7Tn0/1GmrfdAubkyNLfeAlt2C5Ux82fvuKoCei7afDPN6w0hhN4FSmZ/zUXesB/UoyykVK6E4MGxNFYUPO24LMTAdsBOCWamUb4vCGcH8VhBvLwSpKNiGTgYwV0U/N5fbVrER9NR5aJN+lLZJk9Za9mXSKsMs3tgU9awOV5n/wObrMq2/xpFkkYBHUs18IwqI1ggMlJHaA6Gq1jDFji56ifNFz/4e3TX0w87nSdGM1U+UA5qYUcajYrK0oyTwf5iaRdUClItNzq0B+tXTBeMJ94AvrtoUcm+hG1/sW2b4ELdjJr6VY/AHgyBh5SO60UApkf7Pro0umoTeQojJ9uWrgFId5E1OgQv84o76YBjrhkpW3QMBMPDPXL8JiJq4gRwfV+loe5v4eGBJSzXGEZyXb61EVlK48kQ0umnlQn5Q9AMf973IaRnfZMomqr60fMbZ5+/f/2Ls88l7USG0et7S6NYsRcak2017zfaFB6sCn5ao9pLi/l5dq1lY9UQPClhtNO5zP+MwoIYob5/DpJHaqXXQorA8QzJvru5Ng8d1bP4nvTgQqiY+J58b/54eErAxMzy5xuCIXiDQd+C60UG7RA8wSS9eVV0bGegQttx7wBHEUtRK76HtqwaPDS2nHcvniL7TY0El0LG9C/a3oNldOaX23bev3nR58tzEPYDOXN9KIUmjFEtE6bBE8TbZwpAjXQaL6wgUUXootoy/Y57DPOYqOZiimcOnQAOsuknms5GMtS1TUZ4/N8RxMx+VkqlQPyHkt2lEXo6vpgQwmgRTaDlmiPW0+akYDHurtjCDgUDc69iynKl7AXtat30F9nB23vioK/a33W3ff6PIOZs0z/k9NAmJVNOl62FY4CWRRcMVuHkaa8xrC+DIAuSLo8xVnPv8+wkDFMyxMR4m2REDR4qRd2E/tfto4YZPQBPlTgUPJstStXGFKdc+2W933h+9vmv9m+7ax9CQH7eNpTnh0vP3fcedrAJG95oavVMwzcbtvrHLb8JMxC+SrldBMnTVDYaO4ezzYSGVomqL7T8QSRPiqbMKBSIuUkoSW4ubbwHZVvdAoW65NB7ALlX9vw60TgaiyfNUvThEgyj7vwZPBycQ+XTifEmUYhoZIUeDpWnRvxcE9c9v1eO3rZ1ad+IAARZUq4MAASHSsTBNbKF8Er00cM+Kja9YNhCfrUihQgEc/6zmxbe2B54y+utZfOWd8te8POe2530sAhbRuTFm2W0QolXXVlvBDRJB2Qgj+HSXSztlvuxfeCrADS/rkGlxf22+CgdpK14zhpsWYKbQ/Dr4JiWJYoTa9lCWofaE1tnpWqgrFY5SINKjVIaWIM9++KpOMYDpLt2egJW4v3w0nmJShK0rFVaRHXkpEprWqfnbbJa+zs6Q0YicUzdq8HJv+N50SAII5jKueQKZHC/RLT//48qLQKP1JBhjTzDuDGQpCrx0u75ILBFodzm4i9LbIueojI0f9ky7U/g85WCz611S+lJwFLOtEIZ+VhWgYQQwiePWWMvhHwsUU54wTD0qiRk8s/Bg0grOwQfddB1clTvOF9aHdR4CibkFb+7XIoE0ZmW8L/QGOqveqN18YH94KTs5zvXAxajgqhAhMjwdQ0aKI65WBQijQQFYToqfmUQTanuKviOCFEAOvmWHClepG9NojImwrTsSPLwWdlQT/F3jao/0+TJWSn6P3yB/niPW3Z4vrf+2H3vV0cW/VHCULaV6Qxsv+VzXgnQkx4J5oGClOX9IfhKk8JRuiPhGgfL/IxTMFRq3JJTUflI5mJApLFity19xSi6VU4xpaXpC47yPmTMmjS9RFRBDdR0LKMUUKCKVlOQ1FcaQZqVzielzUrpdg77cqnoz8ThwA4k92+h4O9RgCFz3NW+DSDKFdxc9sAEBfev0kmUt9Mrjp0xJHLBOQhwpppf+Pt3rp2f2QkhuEbFXE8lP6WMY8X4bxtRg4eeS4JBlF1xMSlq+dafwKqX6IMjMMOckIU1hBB6FZMAWwJYuFu2UocbVW/I0ADaKJk7dTLz7mwVM6tGb9UtTgAAIABJREFUE0vY2yh7nwpYxnH0lGQVSTUueeEsSvlibM1UTgkuGFYniLAogBuH3tFgSbgdNtI9m8oe+C1gyGjZKQXf4kOflhw3kHeWdFfnqq1v8ZgYnvkzeJwhEMNTRAKiLqQrr+hC5fTWpGrv1N1DsTP2mUZZ6dDPOxv9RnvaaF+ilBTnWIlF2ThYwJVLsLDudXzbhgK0Ux0VVk97XvouFu2QZOTckndrBamCZ098aOHiFZugw+uS3kDlTW5X2N+hJJjeVzyVK2WO9MqLVVryvKthOg+D+6HYQppxNR0EHmtQnMCBQvadXEXpeYTmRDGnTAmn4eRCCFFMGrnRVM/l0WqEbUKm0sCz17SFXq94C5h7VqsOOaijdN8vAFQ8WUzPIdKoVFDxqMm0vf87yidNs9OgJylrIh0caT5cAvHqcCk9muQwp6/ATRXvlo7u2lpN4gRiigAMwR9QRWRTOPDAt4RP58fHN84+V4RccB+l5x/WnoS0wWqPT08uuWuHQztR2tdnsWAr8hSVXgpGy9VwSp9596IGivK+gPPqj3EPpBAJ2ArBW+EFqQhyHba1OgEVDzRatecWcTulI38TChkaVLVNr8RIhjdY9mtYOkRZuoI38d4L6KJc3Z+/1hIOrwHdqWkfjkSvF8ihvtf1XpBimTVf7hrwaTqNRhmUrAoenscEf1OkGoYGG53IsRhNFSln5/h/tt46+3y57sMWm+ifRVb045F/0b2ubWI9j+yLRQVBAyeEEHpgWh4JM20GZbeTdS93sof23T7A4BrFHiKlX5DGwVTsNJp0rSlr1Wmch8G9Mq6wn5L/Ho12xV3wHccRbMgoUgQTa+Fz5S9ssz/7vukMVl2G4OVn7YlGf/BbcuamqGZyTowwdFdQYq4prQYMeHLKFSQ1RXzaadfv2Skal2b6/kxwnVxlr6RyaQTScPmHu9on9XfxbzYdPbkmt8dvJxsTQ1dCvibSZ646NLz0iBo81Rc2eSdXX65BYSLUxyo5eWiSFFKIXq55Afhe3RK8/3brXXeNfbG+0fALfB3J7785sj4lDan2OIGkVpKnizWLDNVKdmK37gu8vIEyWLGKW2/ZBOVO/DMevQeQHNDsqmQW79uqdqX0n5tGGZppMVPA0gAJIYTWLZ4GCRXTq8SfdS5Lx2NyiYhyZd+tQdPPQQFg53Hd7jlcfgXT/WsaVEYkn1NPiMzYMWdBe/LQ4OH3hgKGjaU2GE52QEIFtJPhWkGeEWIvbg/Ox1RKWEcN++LusXcH/4d3f3j2+Uc4myGE8E7TcuQnEDQ3a17DkePko43H7hqpLNhU+N6hP7eNMnrsdbwWmyE1sSCVLNXn50fHNCrAKKCWlFP+ucaUIsCZJlLcyjwM7h0WsCRaktCIFl+GKYuE04ZrPHOaaqXTpriq7d+zhUjg1TAogxO4rUjVZP86CjbQIb1Y9AbPWtk8xtHUy8FjNPliqrUnfejGfZSGa2UkO20JQH8GzqExm5MKLxqlf2VbrkV653EPk2NKgfzEGWnlKO9ZBdS2e8V/j3x/X1nz0B66WmulxvI9W8iDuwBAiVHjCPMkwsbytwwWTkHLQ8zknUXPhDxERGZ76B/yNsoDublGWf/auy1Y/HseRTW5ZMvfrJh2yggCPrNnUo/CMIQQOgDyacppOjk/baOH3jWui7V+UKIogs1xYmNsl1p9NcukWPUaMWU4WNpHkIent+bXt7+MNFlHPYo5G5xfclEpbQN5cmTNnbOg1VEp2BwFPsc8HApjKh1Nt2RTKiJCEMM5EvZ3aQrxZscDe4FlwfAUYX2tlXzol+muvUgfrO9esIhuQ3LuJCzMYtPuV7yU3kfz0Ky0iyFxW5RDB9GZjCha0m0oDmEhrYJSDAXK0HmkauB+o4wXMe4quLRikHtd+2A5vjacM9Uno0glq5NNrPSSAhBG6IbL6rThObSAC0YDKwGLBe9lUA8RsxOC53y72AATuYBlTgGnSCh7wik0bYj0GnVUImoYwZMl8HwY/C7JTxNVxc5B8NdGS6jyW0/H+jguMDFMYyNq8MQqs/betz8l6lr7wMT69RDxTYH7vOMlM/k3xnKKjkf2ttqDizwHZLHck1Di2+u2OpsV/9t7ByZwW7vpq01+Ka1AKO7bRlOODXp5aZT9IcgBEwXEKVEv3oFKkTrSXjPueQWb00dTPno9BBCGEELp0HZl96LfWuTh0ZQZR/WxKb/BRnon69c1eOCZB1egZedKev6ZXr/ytjiGY9j2enboCdWf+PXqoYzX7Rs5zjxzicgsFZBEkxgZojJaOJaUEGrblS32X29/cPb5UsX/QB+97kgo+JOtq+57//LNvzv7/OsTj1wcwMUnZb8SFO5v2W8lCgoAYi4cSdQT602Bq8LdKQiN3CAy23ojHfNAmfEqrLJf16g/xbnfAJZFdAGrOmMM432Rn47dnJxv6vhBfirmlHqp+sI+K+CVTviCUIO4SkyJnmQR4Wd1VKcrqf1lW8yJsCQzDUsCXHWKC2D8D1XpH9mmZ+zl7EIfeNTbaHa645UNq6cnitNBk+Txor9/HuXsjMgkCi7wiApvWDU+0tACsXrUIXuFETV48l178fKBX+DORZRaY00TncIpSOXAMxXD3kAjyce3xvZ2HUnwrpZM8uz1PJ/Hfta0AkOJL7reMHratjixkqCdouklORuULjsWMXFej0YC+GdslqZlyCmHPgSvQDWvTa+K+XUCkUPw66neF7lxKJimBf+9IVJVU+GoIdeOGnONB3bQT/N2sbQdKdd4TcN16cXjaXjXVSzEQugRmBL3QCy8HuOo4N5IYKegIAby/EwdKKaECpjEYdOGSCWkgWZy+O82zMk4ltpU9r5qomLzmxueh6cFLfbTTW8M1ZGqKqAy67DjfysL7p1TUWKNB2ijEiFm5LxqFMd5y7IPiKNjL76i4Bom+J6WUc/DOLpjk0EjbqJl3Xj/GIeVYkOINVOH2g38nRpNPBN0KkaLwsuUzqLiIk9KNDpCc9nsFRgh4qgcIg27WvXyjdXIOegh7ZdVRyPegx2vy5hO04nMAoQ/rtv9E9WbkPGzguhDFEhodMwZMqBRIU41BE+k2ZcWI61b9m+mxTSlRSjAV8bDM8GiDlb8g9FzcR5gJPytwE6G2Cn41RucRTrH/mLPAMjfu/go9e/ut00yF7J+hk5QttoV5GVuyTZXbg04lF1JfdUIypWycSA7NTXIPTlJiR6EIOXK4tk0wHehZF+upwkxIVKlRS4XGichSJQISrJ05CWaIxeUxp9Mk2Uk3dW+XcP37P/nsTM0jddphOjMGQUaMsb0DiXNRKHKc5Wg0SdfnpT/uoaepHuXPcXIYEEiB/Sg9N2ckcMw9rO8fM8mi7T5IXjHYib5IqauaPwkmJZ3zQWciVKoFU2gPHpuD6xRqCy8+NEFLxemhXQ8h4vuETsiRg3lmnqpzS/RkuJde0/FazlG7/L8GTzczzTikhF9+yzE2G4/J6LUxP5A9in/FPtzKXlqJgVnyojFb+7/cpWy6lgOAX4mgaDu7Rqqr/TaBQD08wDGjta9h7jTNut7Zd3n/9od26jE+oQQwvQqNmPHrnWvCTccuOKygjkdrtsmZmsdHaU9CiG/Tt2LiBKJI8FUGGWQYh1dxbGcl9iIR3iQAlGDh8KSwkAPNf+tndSJbehest+qSknenZrNwoOuv0ltyb67J8xeNGyYCsuKSzxApcb4hTdkCAobEWwqXnU2hTU6BO+pJxqLYg/G8viMICTIHVFGGOOfIE8O24b85t92j6UHUn21jvAq5qC/4g8DwevK5Fw8tHVq35QmjW4uYRjNX+9Qx4ZNDg/N6dMh0AiMK5+NMMLSaJxIo9aeA7z6uWYfG65/IsyPvaKeM8PaZQEuuk7qrDiT8laGynsT/6JFEE3dqHhPiOdzion93z/9lvvenYtGPHiY8y/w6KlNUAa8WDPpen6aoXD34tDzZ7lLToZzDTlvIYSwcIi5k32w+7s2P671geK6YmXUczAc6y0+F7eF4qPycudFG1AzasZ0u0YHKCPVYXTPBWWpHddPwBGjZyINqxRCCIuXzPDoQ59cqPp89ruLFrbYGXp9NcOkdHFepjJZbHh7sCcRHoCkizWvR8dDgJ3bqEBs+JfJsKRcZAsZpac1v6ETzNT/MGjghBDCqGn3LO1pNgHPcXr+5xCkeESqv2PjtzAtp3QIDSEU4X2WkR45viO9N2K/kAJ+VC6cDiyBm1Uf7+Wm2e37DcRoEK3pZ5/6svTCZTsdp3UvbTJ9m9lT9oqStAyNkFjYNVapwcXWSFCMp4IVCQnvCJ+Veyft/ke3/aI5b4mcPxLFIYfOSCrmxov2kLp50+6Rmc2fdCfOg4JecTqxfZ8WMQvBK7dBhIfCtSgRR4uOBMP3Rcmlu2iaeOM09IcrYlABe+f2mwLm0W26JJv2jZIZK+yHF4IHNPdw9sl0HoLnxZpIGrwAYT/esgO5oFtqdv56/uaafWR4PQRfjUSHJtcVx5BnU36bc/fSzk4//Qy/rkEDjS0iEv2xIseZxkTp2C/EuH5+ul3Th5TJGiVjpRtl6yilT2AIyTPMM6jyOZdym0bBLyb1UFYE4Y+eXT/7/NEVA+SXhYm8DW97edVr+04Paauhf4FZG0bUCiI1BbHegC06lZSWq74VzBsjkRk0V01S1SB1F+GVYqZbDcwYDig24kzLwHyoomY66uRaejk1y/g1FUPEfWXTJmHzro/zv9Uw4diV1qhvVw31OZI3/9F9C3l/65Y1Ldq/7MHHgw7MReUdwD+r4NPpXpeNUOIC+3tUtuA1yCFl2oIhPM0t0xLWSBnnVT2b0xR8weJj//xHMFQTJc9cQ2w8NaAIcl98IFUzF23X06gJwRvWZFrWyph5GIzOuFSGYq4gh4aS0nLYHzGO6X3y7xIluB2bHAUF0qBiiFhTa4zwaAM+losuCButY61lWrrvv8cy9YI0Uvp5x4T7e6w/DSH8Tw/+6OzzSsUm61LNh+8/2zLg34Kc2xKqY1zR4Szdo2RkLAQfbtdCBBpHTNVrKiWW4qGX6sC4EonjWmv0Zx4GDb6TK2yP47+XS3GcQvDGhBZUUKzzihrw3N8qw7iWfK7F+5IWQ2Nf7QHlSDYlFVNH1QpZ/fcFHPd2zfSVtkn63lWDZKwUbLIetPzmY1l3R3s/kpVZ5OfCmt1zAiD0TMDT7BSQEV4pNh2dnfiorSs3v2HzoZHT4kG6YOc5IyxAsY5plA6/bUQNHocvkc3LTRirLoqVU2epFOCJUsiFEMIzMKy2BWOzUrCT8lbVS+39q7bZ9lEC2Kh4q4yLv/DAb1D2xTp5n/k5SeekhOJCkL4iaqkSyMeDLSBoNZQ4as/tBzV8yDQGDYj2Vb/JKcB1czXv202O79jLJEoz8W69jfS0lWKE8kf2g4Pl+avM4nBVh2zbIIYmDQE13BznikZFcM4cv5KkvnheErwimGtXii7PwXVW1tRMBCTP56dXpwZDdpAejaCRM5XQ0B9e/PLs8/7I9sOzTjoJjQI72T191gQZW0sqyfCMqkBd92dx5CjXaIxqitI9o8hGpteIHVn7ez/hO9+2H4uCdl/TcNxRmfP/P4R4xaAjs9T2RCmRIcVuuL6Bsp9dM0v8lvbtorxMRNogx9Wg2ju2TZBHW4j3lnxocIxNUJJw/IueHdYGfvz2os9qfHZohv5YWqUUUN3S6/vNuIDqscmAnBda9gujT6I/+Qc2CdO7XuidPrfNOUV0VyOnDJ6oo+hswEz69zhexQmIGjzFlr1sX/omcfMyjNm6JflGClWRV7MUXNOlivfkmnCbFjJ+J3/StvTUlYpHXr5Rt43y2bFtklLeb7Q8NsJQ8pKOaZJGjjbfxOHWvKSj8Ra+Qke/j42gqSnH+yGCmdmCBC8DIQSkr5fnp7BQHFD7lmnUaNSFlUmCOak/BsHbVa+hu9fPN3JYHTYvY7h8fv5ZOW5o2CZwWxCq6gToff5xqKLjeqliGaX0s4sRdOkz0hvXikHuD9fYUaqLjt8FZkyY4H7eMfrVW2X/hx1Yd1Uwq7PNRAgh7IE3Z/TC76EFGBCZdfu705LgDkAZ0ZezufjQPmvVD41MpySVkA7Vp4mWLYj80qDd/shPOFOUsfYUr2twLxZOSH2RbkwkKtZoXIqBTWePAX7l66GhpOvgmvJGIjXcw5oWc88sUbiNZXuY/RN74M2iV3ofrD07+/xw4EP1f7x27+zzLiZEwc1kJj8u+IlkDy49LwQ7s9l1oeyVDSM+Y+n2PrljLz4diE3QNGWWbQEvJOBmRmdUnzjMIfZ9oqs6sY5fFYandZ0PLT+IeWi9gfCYou8jRFTDVZTeARxVlcZBN5S8BuMQHuDf7V1315poznbYM40xEYbLetl+L3/TS6X2lm2SBVDNFwTdT0yTtm1wwlErZWhos5xYBBu5I1T5Hd9Oz4k6QGHEa+c6FQSbQ0Az71d/5ueKJYuDpjd8h8u0tvxvMy9few7G6sn8dUtn/tmxGIuXwUaweqjJXqo4BK4fHYS8eJSnxIaI8GVK1TVHlOkk9X8iMgujRnl+iHsgTUHC6APe5HDorejfXTSMwqbQBzPUTy4tFfwTVBEouDJPQwZ0CQsSdeJ7qxdJwy7RfRsNfLknEhGYiM3O16bDp1E/YqgUdD0Pg3uYVaHKGeRSqhptjGAg2UiZ3D4JQk9eE8wbub/Y42u85GXYeB9g3khKTq8dde0/LjftsDbyPkz0ed9o8msihH+w/+bZ5w+ahtxWSge2P9rveGWjzXE5mO7i95RhPIeoTkaqcybosq6tK3L9873hBOEw8aiavsUc01nIR8goY2SIOuKgZbYKEM4VrlX+yL7Xuu2/51IZWpECy+80co434bJelrhzF1KqP/bW6KWqzdK1Rfu7zRMvmTug7s7ImpGIynXEbfvFPoGtpdEZpwxF6VDgOoUh89FjO4mIHZBoH4DNVdlB1d2Sf9FZLPWIteZmHS4KGVs/fRGZxkqAQzHc/ZdeITn7NQ0+e6yZYECES1sukBhQo8kU/BQUiSoFKmotiUdX53GEfXYSqSQj9kerYbiPemBD1TlgeasS/uXxcmUBNJOXZxeNhLvjdMzAaV5eIIOqzA4+9yKpXH1+pu7EICQ42UXDtNkizk7Zk8SHLmonYq0luH/UYJuHweVz1btiFFA2qZKqbtlijiSd79L+kfQFo+UJ3qQcnHLOteybJWB6dj/06jGttD0E38uNeLX9ofdoPmhYhGcsXsa7i4bvOQb3XF1ya4/bFnrSHnJslaINrl3aDVmNWs3fv9uL5I8wEt0AsE+ZzlZjhXQeiXR5yv7RlHKIGE2x8dI8PInKGuxJCj0lGeLm0gPAkO5o0T5/0fKhvjcbJikORr75Rg1SShf/xw/NCnnrmm2mNSkVfNQz14MVHSGEENB/hJ/HNRGc03TPhp5jArEOL4WHSJVHDGNDTzQRYcPfcZ2W7knpORp/ToRUq4TSzT6iPTNhhKXiTYQggRzNaqqKPEsALauRPW8jJsBZKl449ntl3LRFadxPB2hyXVWAkzdnKluW6QGyNSs7qQNdR7h21EPj/ijv4oIs6/rf2uetK97J2F+1CcuKBf/Dh9Zbq1q1zZ7g64En2vhr/3Lt2/YwJEjT8xHrUs6zqmleJ4CZypWIXRZzrKBxh5OKUDAUD6FMXyF8/3UNYmkYjNeoGPdHspcWdEEkbUe5mOjxxqaUIoPTWnIwah9CCDvfSt8rMU6lS4jqsCfWcsl7GUcIh2k18t7QzsR60ayEf/Xgffe9Jtq0TAVwTKdfAc1snTLYRsNsqUw+BW+cVnAR05Pf8gswWkXPyO75TlcIHroR68buvhexVGL4Vh1Rg4ebRIqjfLd0tlXQQ01uFvEwVcj+46hIGd4BNsKTtgcoXG/YzmZzzxB82O5FywTujSVfcjYZE0zknyV3BA4aeKw5oR0nrkbTG2nvqb/nwnSi4MhNkSD0wm8rkI9Ch+erdcsvPddXK034/C4F1/UHtrJj69a57B8y37W1YI+2EEIo79rfDVbsuWIRo9c1KOi4XsufCZ/H1fRUEvFNiWIANuBD2wk1qFY/Qdflb2l7aXjLcCTUI+PzK2DXVbWIsndVgrjH4kNJfcEpWBAG8787NGfkO8tP3LXfu2XVKvRmO0P/nhT2Jzf9b7NRYoZ4AqV0oFyTtJhrqihygcBLnh2NzhBPpUqe1V1U1orzI7dPvjt/TgD3FfezyntGsBN8YVwXpVmAf8rzQRb5EEIYIZ2m6UkGO1xlrLZCorzUqjqus2jOR0/sUBQbttCEVYTgCQV7ohjK0PY0hspFDR/bKBX9hqbTrwbVsG8vxAhMYctPVv8aStYF5H+KlGqiaAUZG7adiDEyq54jHocySYsqnAH0Cmrit/Dw2GeN8OSA85jBE1fgHxWElraznQQPfE6EI5sLaj6TQ63pR4cmLJdgFb/oeJPz0prN7FbG4wmmB7Y6xQNYvqI8uldtPiov/DPyoGv0h0KQC9y67SecaZAYgZzCndy8IkWpxsQYSlh5fgptO6SFE9u8nct+I/c20rl2piX7bkLxAvtT2caPz1/03s0n1+vw7QhAU4OGoGDQ7tfkrmEaUEPoB+8A26JeEnA7aenIELxyVq+a99T9xjNNoGj7hp8DBywUbMFHK4/PPv+f937HXbu6bIeEXdbXl3xsvI9UtGJbFqr24yPSJ0g1F5l2i1JlltbXLARfts9om87VFI81WhIqCzA5TytcdzG8WDexPn8GD88EjYlET0GcF8WuUSYk9inuz79jGwL97RgFAKM/C6P0CGtGtGN1M530cOVDu2m9aOE/5eEZ4MEqkkP9pGW4hYOceTh1ceQ3t21jZpRGhc9b9X83RJ+LzBXTlf1lMWpwRnLrfkO7KFHWW600eDLjdHlP+ERirTHn1JsxHJDiYmMjntKCoFbPaAh2X0Y01HIP+Hf3un9qkmjxRZsFb7isAJ32qONDSMzrJ0CNsHazMKIOD6T7LCM8E3+P7Kpt2GEVD9n1U8fmghpic96zyCuH08Dn7NB/kfOjESNXjimRAApLgoMzMlf156h4qfpdOLp0ftQlVnZKbyuEEPLddOtluGz3L5wgbVidP4uHHmDnavr3plxXifi5NVIKDChgKkhtXkllXH8qIHPgs2JVPewMzfRyCCHM2EldKxIZ5WKzRaWaxz9bT31K68GqeUb//Nan7tqnLbMmVhdtc++3/eYul+whJ6s+NTE7gRAH9q6w58+tawIq54r0QBp1oZe6fN8mhE2VQwieqFM8Xe4L0lBonyYOguHnZTCtGUuvpzm4Ifi5yOg17D9WZqlTxaoqrRhc/sJ+gNicBPyAz68pMzhm6iBs79r+Ll0yZak8PD9/YU2hPrj4wl1jNOhyyZQGQcohhNBetFzrSHp0ZDApqg9P22iiW0IkqCQtVQBMXhC2TPJbjStCbAh2aBKXai9BxzQvnHUkLHRnU+8BO1KpXmIjavA4fp3IN9lkNMHYGmuqyQoiILwPBeh1vWyhoZxI3+s127E/enTLXbu8ZtfYrqJQ0TI8KFntP8J8JgBuykBJUJwCsB1/gwC46pv220e300n3EmSAGFSgeoBd6pGKShStw8vIb3PDsgN2IrW2bXMyrnqplYGrW+z5ueuiV4wTaHPYWoLGrOOgOfKT5hrjetiZ93Q1VMuUIXBhev4Y+u1clt9GlM/xAUXwXTFsi+4VKjga5gNpLeH6FNW8UK2jElPBm8sQ/M1COgjzBBiFWcdvxkzFfu+0C89WPcUIMDmNBT0En67Z/rZN3uovpcrzmt20f9HPT/ML+8x+aAksURWp9BT6/nkZrtpM5pN8YcpwzHc+VWAyU2ZsuCpAVoJj1SA5vGNrRKdFHXQassmmvEzn+Oe/dskO3SIiPGqs/ME145jqTtKxDgT11/I+UuPSVpINYdsJ5eEJcE7YZHQqUa6FEvh6xn4RuwjP5ATfM+I6rUPfPk9/T23YWzdMt4cF6N+x2OcVqBpeukprYeIfbIx0BsuWlYLdbaCpmu74iMm6WPbanWHAq1Wv0Rnx+f6bn7lrmz3LFzw+MvN/UYgHGf3Z6vscw8IBrFaCDIV9lqXAallTCKilenIVRg6b5CnRmTM6/LVYOTs9TIbbtWxzCAEUA6hzH2jH4z68bM2vl/btAJzc8JNQ3UFLCuA+6g/nr1s656YcYdCmt6nVOY60U1IgNFjJXK2pLxqGrPYJwRtNbDa55AMp4QQpKN03TOGoUuhexvPCuOoL7wr3XuG5xwnsvWGaqyY0FFs9NEdEmjorwv30F/bQ+Xf9Xhm1CEqDoyL0uYML6LNV9vcvPUcJboSniOdv/wMx9FlsIGW7xNvFmJZLB+lM9vMw6PD20XNRI8C9SDouVrnJDgY05hcf+R9ovcEUp78HDX/ew1W/hiTOioOwAj3T20e2F+sX0m/ypGOh2feaXllSb3zZs3zgp/vr7nuM4vS7/lyRU4fOegghhCJ61MHIWTjykzWr2GbPLvp3ySGllVFFwfOD+4/WtDkcUu5SNXkEaABBy4nea6zc+6qYln21TsTaYh8seTcqVj2sY2xChpOH4m5OIoxpG2WT/Ft9Hza/XDFX+v4euqXn/UOuQah2mn4DnbDXzj6ajNb8Yjt+Fj28kSg0BSm5fDTH7Sp25BrnWCNDTBNzWhXU53KiUgnCZ1z92KTF/nd8epG9tWgshxDCtJyuPPrLtr6VXVub4brmR1//cKR7LPkXQ59RnfKBnwvifRTYSdA/FYZGBt2e0qI31y3dfqv9hv8ePedE5AP7SPlOOIjT0O/xmRVUTCNHcXl7SF3ttIDhWfQbc3ALhIItv6EzOYaP8VEb+xKoXNSorX2uSGduVisSo5UggUSLGC10YHSPKZ2JRP0c1COFmPJ1DnYpd/MrssgxMqsuZvdrLXzBZ87ZwXteUfNsqmNJ2ceIgDabOXniAAAgAElEQVQqJT+mOgFco0QrGZR5M6qjzu97ze2QNvZB4DZBuHG54oXE8wMzmmZi1MwQdclJqmqGjgLE6WjHeGbhlur+t/f2bANmJcLjokbA8ISJt1by2zY/ShrIM0dITaJ5L/6dfwUn4KUjPMNFPylUsvRmNQtBrKKG1LnZ6KXWBMw1Rij7TVcH6zl6tBkbq7veXDOT/EhI0GgM5aQ+tLFiUrydsZ1QuR85bBpJjHTZ5QGj4NQDRTyBHmbOf6xbPS1h9SKZFotRu+/8kWm4hJJkmFEwPFlEg1TYpQHg5xHDk9YpXAcP8tFbAualcou0bOFI8FUUzv8cgt9/7IRc2ZSGrhBsiRQqzjc5pkLw3i2VgvZ/I3Hi6Yp/ATIvV3L+vLPs9qhjE0QG2xBCWIBRMxv4dys9tkmYRM4VU9PZY2GOxS11DVmVyTPWv6xGE9KeLf+MvP/iI/vesXKZzR9sxw2WyoNAO0iLNC+nxClk+lD3OtUBZVOC9R3/VvkW4eNzgzoqxuSs3DKdY1MAL2BMXFv2GYlDcEwt5v1DsnXKOzCMfv78svvedAQIQN2fnRl+eyosyQGVWUyXD6/7ezBNlpWU0+KypAYw+qiinMHRym0LKBpiXakyOIjhqT/3z9FBSxDlz4qNePNQKK3lL/xdX/yBvRytc2GQ9xtUFN1gzV5i0rQd2ZS66Kd9Oym3Fz/xz4ib/uLIx/ZpXY9hMa9INdfmjj3kNOeVbA+bi60l2GIghBAywCrpgXXYGQ1W0RAmU6gcKHo9KixYvqzVV90NlQr/8FvKF4Jnruz6Hxgsn0/aVTr032M6SkOQ+a59V9Ndfdw/gz/M9SJx7tc0uC7EE2jax61RhFxQBTG7oJfQZC/B8wNjQvEl3GMLQ1QCRfrWaPSVAFONWrASk6lcBS37fZ+utbXtxGlK/xJlSJ+1TAZpaJxz7kCvgoXLA/szWPXPyLXuiSGzAA+WSjIn4Hz33vJabNh6dBfYOJHKjn17ef7OBPcV51f3LJdVnTY6CIprm9BpjvhAvEeiyCaFKyjWI01LoV3VobauQCrpkmNaTk9vPet6z5JGzv22HbKp4GgaTftxBS33t02w51b8y027JqRIopuR5p61q7bxNY1Mo2asrSVYyIPFjq2ZDtdOArc7vJvuBEy/KoOHD9q64d1IV2GAH9R0C/NwWpFC4BrLlg/kJguQWMdS4/te2VBOy5e89fkfDt49+7zbt3uul3wc7c4VkzyP9n08dQLAYxbKI8GtsohQ4k66J6dRgTSPXqMstGJVARHfQTxVCJ4KgIqKzKYheNAyDZwQgsda4flHQr7YeGIP1rrp9wt5eLobavGfz59RffIKFJpf02DUgntbw9+MsiiQ3LE1K6kpFKQCIzn4233P0+mMV1ZLTLUiAl6eRgZprChegREfF0Es+vsXj+hw+HepIQ79k61r7loZlSCDjk1QoSobvwpgsrCsT8vnz12i0SqGKslYo07XCgHiKsH6ijXU+w1Tym4X1DYkYV+k2vF1Ddf1HfOiBg8xhKLDQw3yqHUzvUKVc0F82m+uIU0jqRKuC7HvmnVwzx8pna9ue0HeAidUE6Dl45HfcCyyUQwkCXdLOdvbZSkvp5GjzkF22b5LvE0IIUyQsnXl7MrXg5Ytg7wYNTzHOv2IZmaAcdVIGc9gjIKAWaSMnk3srQTBZWS8tMGTl/5KBO12roMY60TAvBDoCi6iV1zZtB/7y2e33fd+/7IRkf3F8dvu2rs1K+27XvC1iN9benD2+V/1Pzj7vNnzifCbNTP5C5Lk36+j43rLZnkqecnQSUdOjevk2Ej9mu/J05D86F46BxDXSSNIxJJQ8Q6ltUS+A0WrHjb3OAm8OtIyAYSCCQxPjIeHHjKiOp0b89c5nR47e70oiH2CJrRakk8vWPtPkXmZHqYK9xFpIbSdCMnZaBgJQBOVr1FWWQV2bnxswrh9zf6QXFQheE9dhfbxyLT/RsNLxM1jm5RcCXw6O8L70cCLT/38j+splU3auRne/kC4VVidSPkUgjdyyC6vczVmhaaQO1Jh+Oo8+V4EIzkPw3FORVjlqVf1PZiiSPSlwz251zPTdF2jhgydRK7dWM4ODSVNlVAs7n7oBW32MRYaDsF223tCnZE95J2m9yQaaPa5XLRQx37Ry8EYF90BOOYKBT+RrIjK0Hmv+g03HuJML3iPjJiek5wQFp6eXyjQveUPXREprgRHD86By2pE9r1GBGMjavBwc2kZIUN6I2w8NWrcv8Ui5MamFbix5F1iGijKwvwlwhY/b3tP8T9b+eXZ5zcaJtnGUn9Ky1pJD6/UTDvlge9hs7gQQiBec6DVKthbGSnTL+6h2m0d7LDCB8S1SDQ5rKQbnHxVZg5UqCxEAHmOkbdN1lc/V52L4G+YaATp/LRYCCGUDpAzHqL/UjnS3vs1jSkMmYxL4UjIFZ6QerqO7kGjnmQ4ZuRADSr2mREPx9Hvcy3l/K393DbV7reEJj4Cjm2jqTAjkbpnxw2bq2bJaw/SS5SywgMCF3xyaFoztyohehpAkjJjuTxTQqrEppHye8cgL+kuNkrkXFVf+O8dv2WfJ1U/Pwzfe0Xun8ORh85f4aKTF2z7NJT5pMOlDZZdXzeVTePzr2mW1HEUiZx1ODfSMYgs4lomGjGH9Gun3zYF1pvYTbU0/A+vPDz7nJUDSRbmPoS16qty3iZEndN6Ux4Mw0V1oOcW8n7DzY7tmQejdBncP/QOSBZknzOku/L7fpJdOyXRNWmNthUmQuD5qzgBL008qLlO/mC0pJD9obTlAkLsLJEV2pLwzaah39oClphiG05kY5xg9t5Fu/H/eHDHfY8b6sWxl/QnAFAWEGZcrfn02ckhJkvC977LoVwis6dLMfh7EDNUaKUbVIrNofflGJQ1JYeKPG0jUt9EOWORilz4G1iyntM5sHtUt/wB610ARwawEYWT+cMrlLZtr4yaiIqpXIA7q56oA4/LuVIB8I9DMV29y+DUGKbfnzi54r7/3vZH6fw0PJtaXUQF59qaSHVNaJp1oViAjZK90Me7HhVNDzOATycrIfoAFtjT594BmQKjcJqxPaUEi4zSFYUJnnJtJF2FaRwV0WtO2aZz6RhPD9ikiNCsVUokaF4G5QoNBo1oOaLOCBBbGnQ7xeeAw2L8lbdfzvGORdOoozRywP2h6ckxcnS7XbPeLiz53NoRIpt1oWPYQ6NcAvmXyt6I2WnbgxXEWGGp+EnL68r8NvA3l5D6euS/N7pm17LSXJWA5nzDPz/B1ARIT5a84p9Ab1Sfp6doOccasauBr6frMd3R8dIRnhj3Czdk77LsICoCvQeUehZC9eGe5yRnyuk9qSOsL5jQ2y/78OGjoUV/HnXtnv9k9XP3vRZm9udlT5/7y2c2mwX0LRn0vOW+fME2tjI5Z3cArhRgJxXlKXKsp5LfZTXJSJcNlrsCRylwyfuhuBIKARo4IYQwaAL4irOhefJYKwTy8HSv+Fg306UU9vNIPMg9PGXTPaGHbX6C1JTge4j3YZ8kvf/G39q1/W9IZ3qcl0SXchgkVAKKC2N6Lt+RCBXOpgKyeQQPvwFPcc0LQKYwWG0VQgjFdZu79arfSHQsXuwZaIqdoEPwof3+LS9UeT6LAJR2lv3ey+3axh8K/o1efMyLJGlgTSqTiANRtmbXZR1HeizVbiwbnscIDw2+jhEJh/KOyDD8syCGIHmftCUFDWwCvTWVSyxbUVrsEHvHOcypw8Hy9aZ6p/YC2iapD1qEHoyQU+l1RchELDU1gyDUKM4IGJuSpK2OjwBa1rL0G3AQAMEYbvhnzIBpuXLRn02SHk60CozPGQHrk4xYo9/8LtP46giS80v3QWxEDZ4YuLJ5H+mdu2jhMPCCuYhKkwRgEHNCgFL5fR+jJ79OTWJbH9UMp9Mo+pL1z7qWUGcJ4IOeJ3K6Xjbsz7WK38nPF+23Cyip2dzxoZRuGQSF4qJM12xFFra8JTAGVod51VPhBHHXYk0OZTBUHKsWQwV/KAlrcLGNfCxKxRVUSyKww7el0SN4eJSKmp2SHR5gDruluzQTqhsWJMrCqhDFx9BQTAhVrMvxGzbXChymIp0oOJRd0COElRQuFc9yH47esy+Xd6QjMxQLgYpTDX/38O8LXvg2kIdTJtkJhP1bl03DffbYV2EyFH/tgj+3m2PTcDmex55/RscEryl3CFL1MNOowVRZ04BVsk8CMakr1IBllDxWxvu6Bnl4GLFMcKecpl+LMXtz37LwQttHuL+L9I1zzphWaTFLKkSRvMbzHUIIoQhmZPS+msrL3Du2F/jGsifvYmq3gujPQdfrq7WmbaRWzyvVAtJKWt3FvpAZdgpQpmXok9FEzgsW8VQi1zkUEUxHNskZkY2c12SqCpFxigyFlWL/pEXFzxvx1hKs9pBD2LkMemt8TzEkFAyJKhFMLDfh3q5PK41Qjrp54q/9omwRmD9afeCu/WnTqGUH4JD/+67H+vzlvqW4VPj+yaX7Z587iZIaG4fwYKs1adoGi3xc9VNeAhiZPCYZEcyzFSzAsbes2Rl6QTZ5HukvhoBLR177tW7Zpmcj0RA8BxNTGHkpGx8tppMLjur2XGrIEOBc3rP37K/G2sy/njGtnN8FmDiqEHwFhmJsXFRPGkWymeE0wibqeqtJdVQBTTAp6LUE/jR7fnVcCCE0HtpzaGSIoOsFODjTouYikHYTJyALLXZdnIwLReAXMFmX3/E15b8+MIemO/J7hV2k2bhUnQXKJFVw3MNMDYYQQgnpQYd3EqMpRk/gsnz46dqTdOM50atwDgajJKUjpFSuRxwxSQm5IIb8GWWOOwcRfyjmZAxhlCoZsSNg1UawOI8a8aOhweznzUW/t9m4+uGJzwEPgP25vWjWHKsWQ/D6MCfnqorO6vsHkpODsZJtQx5X/T1mdbSWGPmJzDKFJpGnHKJNU+AvT0XejxEZnx1IdSUCAKVNOskhdcQKgXREv+rCrOJFkmzKdTWVjZDG+hqC0q7b5/qSz1mSaZLleiF4rp2ftbwhQ1bSb1XYw+RN97VbNdtcx2N/EneGtkH5W42idw3a/96QR71v+ZNCz/e04rXOGB6mTxOKd3EJmymI9hufr+BC8F6VV1zSyJB9UJRThviClN5cIfhITYwsrdiS6q4LNj95cDlkRxqSeP2DTTwLrsopXVnqcG0EuroO55fWqoJw3DLS5ZtCmymz0p7/HpW4pkKHS+kYJMdHglTYrCgcNEzRyn6oIRTwecej9sjL87hlm/Z3Lzxz3yujP95+xwuoxYqdwedbZjGUtrzIG4LXJidpPRfFyaqhhHVC5EYNR9coUatUuab4ae29xmuK5ZqHQd2Q1nIjBG8oJ3BKeEfVIaOUKHVCTrH5coRfxxUNqNHESJtEH/KOjFMI+cr25S64agoiJG80zABS/ilGeIr4u7yQ4WaZWhN9eEByTnm3AjBvI9Ct5Ha8s1DYsBelARVCCO0+igiE9HD81BbqtIbMheCAqByUyoLNfQdr9ndaJUn98pWVpfOAKkDMVfywmmHFv4CrMBDw42gJPZQIRhv6jXBp3U75SJqxkedAoz8rBbOgi3jI/3b1b933Nsf24//rs99311gVxs7sJyMv2Qp/akZT51AwPKDgLjzwp5SKhRt0eEUSk11sypocgCNEVgQwnYZmV6+d11Qwu9QmHoPYnhBCKB2DXPDYPyPLzftrfg2X7pkEGi3OISoTg05A9wo8pkHM6Aip1xKDXhgM2YEaPNg2JSHTowfLyKmG4WlUa7qZZ1XxCqwNcI1xj727PKnYi/YK/gd2sTHfqPp83TadDEQXf7rr8XV3ly2FvSAW9vMjS2mxv9CkIsIdUboEYyvpHkR5ULGzoECNQxfN0yasSMmc3KLh5b/IEvvThfnj4Wk8BpXEJaRhPcLAGSQJTiJgbHS/JXAe/zAUJ0hsjuLmGIViGlmrZp2MlPSkw7SKAV+AUULHeLPrdVJ/ZPJto+bxMeyWfoLKEY1eNkrpvRQIWk70uuJAaGuy6o2mJRg5nYHXc0PwYinrcv8KmM+JcVWHCem/mUS4uddZ2r4gNDCEFsRK1nXEMTzYoNouwTUoZOhdLDYyoKp3kkPahmWfJQF6fbJvLs8Hax5s8O3mY7uHaOpftS3d9b88NUPmn9/y2v6PGwZi/u+v/rW79pPOzbPPf/7UakzvrvnT/MtNwxdQwIbgyZqGCYOQOUtESDpCXkgjR5jJ2A+ookDBFPsh0SSPXqSCkbl3WYI69M/RW4PykJrUyh7CmALI7m0A/4Spm8eKFIK9TxG7VtnijAu5Vn+K7wndA42jPgSu3p+HfCwl5EyvlXYRgVEG34hn1L0KJZuVXjiQc7NCekroFCDmNy/78zKFNfFfNn7mrh0DLPHlyABDOw3/on99cOvssxo8pPQ/6Jl0PFwTbBnY08eyFnWk9cpb6UzOfG9dJ5dSFKeR68v0aKLP1CEA8Ir5moPRunm+0aj7jVgzxV1wL06UhZltjLAFVJ/QWBlJs13+m7ALjchR5ig7uP87SefA4GnCSV6RCqta3c5EVaq0ujByrpSlqgRjt28TOZQAwAJ0Q6niLfgJ8DillfTD77oS1P2m7YNeol6Q5++xUziixyXv/M7YZ0v715FNHd9jVWoI3jYpCOY0NuJVWrhpgnXSHXh+lmoSGKMKuGPkgDnxwRWlmrfP/3H7LXet+qFN+i1Bdv5O88nZZzK7nsiJ+rdH3zj7fKno3eUlEA5959KTkDYIVK4K58jhPk56UxDx7ZfLU/ofE9wHvJT6c8nHwrjorwKL01JjBR79WI2y8yuCxsIrQiNKQZ6zPHh4RKBncP/a//E3Z5+7/+KjMG+DgpmcORoBoJBW/EeXJGuRthAxugenFCNVCi7ML/dzzSvlmqa43OB3acxf8ILtwopZztpE8XbJwMgD0YzHM1RNojnT+1ICdaFkk/z5oaebvtKwc/wlaPqVHIZgc025MwqhvCuMkDpeEXEMGRnvitBmFM21XSj477GdxEJ//iI8ruGjIxH133PGoBYnppBl6t/xvCgFhyO5VWwOIglMP2urkf7F8zF6IWibJP8CTPVcXrSb1sSooZFTlHTXEloePe5bdmK14I0Ocli1x16XVZHmbQ/TacWZ+irm00MkZeG92zowi3NQSvdIT4FFnLX992hXzBpCjgjjKEMMoBT7EEtZ3v+KDJ4iUhQz7THFPDPXXlwcgmbVSuOBn2Lzri36UBk309VVb/n+4sCiOP/30/fdtf/x9/7fs89/tvLTs89fDD0d6t8cWRtp7dT+2aG9KLlEFL1Oj/Kw7+O1LA8UOo8whgCuAKSlrLUZpMVOJbzHsOzeh/7+lRfnY0JG9fQKBB2ENZUP7bkmxfSNptTrFEBF8cz6q/Y+nf/GjJzq5iu0wf2aBo26KXSsCneG4dUDGddQ6SAlyI7f5QBefwSbpZGwNMI8TSk7RS33IDZHCxaoaBzouuKF12EbjT+llOzBilWedMXdf7NoPYXyEO5/c+zbvbNFzH7Va79HxxZiu3vJ7vfpU3/2Z2hJUThJj+J0BLPByBnnOyvGJ42myqZENiEPmV5UXqXJEsgutWfYHIzaM3uP3jpkuhh/UYwNdHOCqBP7z5WX65kD0WXh2M8TDW7u50SPNKSmtciGxr2mJ99aMWebHFML4kl8r26FNXnJxWxP7OWGCM13Jn4im6g4Xi16Xfmib/foT/yh7gztnNHIUZxOD2k31WXkFapJhGcXOLo+DMLJc382yzdsfiZTMSq756fCFBNZfWx/p82NYyNq8IzqCFXKBk3DIahwpzWnpF+MGrHCeffIo8svrpjFrKyTzHsWbu64a5tIDLPtxJtF/71ewzbCL9pX3LVyisXcLPmQYA3f0412fGqbZiJlAdlF/B3y30HAzWzMlpEyQkZ8FENAvgtlN3XPgdcZNv09qAyXfmFrsfP7Pm5cQHsKEhn+5pod/LH0OcoiNZYd2efR0vxVaYHpwHmUCQIz9j6TazRChytqydjHWSTaQ0DlRFJaaVULmlKh4aK4iVizWjcY6d0XLywPYOSGPy+HI/vxugiXY3g/bCT85088YSidDG3SOEK1IvEVGfHMHXWI2tcuHSVGSEp6JtFLi9W/kmbxjiL+JCKVFSs2D2NSPl8xxbBqEkgPU8ofwZr5yiybNIUHcKiDQKxIjsaPGJcubSVOoG9s6f9uqWD7tAjwcS5SvTCVwpHH4DSgkXOr7OvvWbnYE6uSht3Nuj/Uo6r93XbPdCyJDEMIoQEANvVfCB4kPZj4jXoBZLxPsIi5K17wkEdo3BEZD12WRbRnUvNr7dbmFWpbXpppubwvEYdTWvKw8G/IAjNHJ5uQYN4i0kB/fOWR+14D6aiyuJvspP5cAGKft82L/Hj3xtnnP7v69+57f1D74uzzf97w1342MPPxCVBsD7qeRexZx6RZq+dPShGYpJFEwNjvpAcun3LTS84RgNzTQ23kagdHCQWdkYPzpcKdS6OCil7r3nftPRM4ILyaprsKSKFNpOydv8dwsxpN8zA4bzRkFCRZRfZFcQKOgE7BsJANzYcIXV/1i0KPmH27QvDpKDbw1Mga02JDMZq4Jv11v2erzxGNwG9p6m4Go/3GmmeCG+MH1Auuwpr7p/VPzj4v3/WC88/37p59ftL2uLw3V01J/PqFWanK1syu0UNpmltGFEfXiTLPVclpNdqm/ceRdHw+xT0a9pqOdT6EEKbAOs4jGadzUgAyT0QNaaSLMeHOiMyhazqKyHReGOdH10w3jKRiMCBdOQZOJIE5HabLHFYmK6v4rw5tj7Ed0fWqNzo2cnZtQTT1f9X8iV2Dnvh86Mv2Dqcm1OnU62gIq+IKvN/DoSn371//wn3vac90qvaWrEMXP+uKw4vvEvujUZxqwdZpK+sFJ7MovS2GydOtGgX5x0YcwwPbYiBePwFjmSoFoHRXpeciHBinWfBtDO0EbK34SfgJqjO0HPz316zc/G7NEzn9+sTSXZcqttH+t4ffdt/7n/sGaP6X7/zYXfu9qvHwLGdtw2RFSP961zb8StULZlZ0rQoIbKdl1nWmDM9AABf9AbgXpJTZYWc0YEBjggJIS0Zdustfy6fgVpQtdZaSaw8hhHEtXWgXEf0pHYA4S4XWHIzBhfPLvMs+aOiMCa3SYui9/qWEdOHdtq+nszXzLC1IpQnTTMVDu9bxwUunxKseHpPa0ysETzw4Xj2ffySEELLAp21Ky5abNVMEu/JyPWi/i6j9/2XHc8gfonRtpeLP1ROUs19ctrPfHnhcwwmrGo+87GLKRDGM7KVFg1AZq9lqQp0Mci4dvod7iGFa2pu/c8DRB/7PtSOKOESJKirIGE1VUeey8qtzQ8hZWyAClTPB36asOxVZmtaCIgRfmaxje8/2N/Exkw1//09Ktoc7Uhp5iIPGfV9f8BvnCN971PGWF7sStAWr2pmyXN4mdV9C/ytoXPqk4x2JHz82TF2j4Z3yAZxyB9cSpUSevWzRK4MhzkS+hUoyceqIr/rKeHg4X9VdLR87H/iVYFPGUFJCspLyoScSYqCFqECsH+5ZXv/DZQE1FpFv5OkTnovtvp2+f/P8XXftaN1OcBmhjq2BF+Dvr1v12JctvwnzsFpp4ITgiZ0WUIY3lfLyTCed1ZcdnzVvTgHE7yUqKPA9NggNwTMqxzzMWYTILteHgpbGorQdh8t2aCrP0hvhva5Bj5DKLcGwi1fUNXEVDIIXpFLkHCq4kvgPTWNy8Lc1pcW11C7fjk9FnpF/l0GVk3LVsDtz98BjAQ4umNDWCA974v2yZQqiLVQQywB5Xq360CbD7ZQZR1te01ZXbVKm0hDYpVY06knunYjMS+vSHUKQykgYzzFWbSnnnofBhqk08BIRnojs8O0j/D46uXZ+pC3bU2MlnVeK54eyL1bSrE4bDS/XqDT49iUkutQ9ex/N27RrAFO7PaDf1TBqITx2p+6rHxkZGi0IHvXIfptn55c7nsH8m+vWO0YdldUl2/gkQAwhhCn0dLVi76YNVBeOsDE2/KZYOEC/rxUwTz/x70KHTCEDsREvS8fn9jW/gZhDo7xKCEfsi0RulkYOLLj9rpcMBFVpE8LNPbNAt4+9MHtzzSTH7bp9/qjxpfter2absiWJ2wNYv5+0TCt0xn4RL5RNcJYE2d7Fpp8K4JjslOwV1Or65zhtwJM+8ofIKSuJ3FQR9IqVSjsiu0S5JxV0ulHTvG/P2LrppV3x2N5zXPcHhXwtxPOc3FYNMQcD88bGk2oIUmklmmqif8TJjfTSVw4FV7JKhEzbIXgjpH+RkQOJ0sKo0fYUrJTRc8sKtELb1k45uDiy6z4VfQAw5AcN76hcL1o66r9etohre+YF/097N88+/1/P3nPX3l81B+ThqVmjJ03vLQ8fmsw4FZwAowRalcNrDsAcIV5tPPb3aL0B4wCyMGZ8zqPB07lq78FeV5q2imHSOAbS04w6ZIC01ak4haXn6A+1IqlLsP06gLhE3WiDaJ8tPrM6nUzhHAPSsFz0UZAb6IfxWLykz0/MEy/Bub5c9l1YWd318b5H7FLXXK57L2kVOmqAcnYFLf/t0xv2N1JARFJFzWSwz1bn1yY0ym/7559eTC+JnwECQzborPi+WajfKMZQRtTgOY1gPuitxLhTCIbsXPMHftJECgfU3LeXPEiL4bePn/sFvrpmP6AETWzO9q9/9c2zzxvf8hvhesFCH6s5r+1p8KyiDPayVKQ86YLNVdgvCZqczKTCCqf5QtXuf3TiJVuhYvMzFOI+ejO5tkTHUrphJDwbnN+R8JGwSoJVWsoh07mUHvNdmKDiLyvWeg04kyn2yPxRjrjqD61e4khj0Q1BCO4UagDPcf1j+7zzHXE4kBZLRHhA3b7QsjOR6IyC0H7FZ4N9ykHzpFiX3qV0aeMCtUfSMBYl7DRURbgAACAASURBVI/63iLkuWUaubngpV4FC/DPLn/mrtFDZmntRFqvVN80WXBy7DX07ATd5OU1GfVkKXoiDYLXHi6lR2bZ2Dcha13X6Pk7FEzB0XBTLCCNwam8hmNwF33IezI6MBMCVjab1GphrldaFFWv6UzH2H1ZSXx7xfTXWsmTmrG/Yy3nBcifrBgf3C7y4I963jDqAdD8RsNj4/5uy1JOx5IN6cNJZwf2seiketUmgQDsEELYQWuMkpSz90BKuHDdJmgsFc3k4TmV80hc2ynsCjqXIXgnoCC8cbERz37hkGtKKwOhRH6JyaLk5JBnLVT9Aq/V7KkXUfW01fWRmu9v2Eb4T9//pbvG0r69if+7f7Nn/Dp/8rbd4wf7vrXEQrh99vlixRs871bNU6yjBCMvLv3VkoXUFUj2Hx4auPKja4/dtR/82ipPbr1txhtDgiGEMBqjSktbdESah7IyxIVolU0Ze5f9cELwuevyHgyvRnoVlXp3gxX7bsKgQlSn0EJFzfzJdid8ncKSKE4PBHEz4VVxRpN4ikyZHb4NyoJt97VwgpTyglC8z45trmmr6C5hI8ZEM0zanaJkBxsQSni3TNVHNrP4u1LZP2MDJa3HI2/cf47Q+88rht9bK3lv8y7Cl0o8yIKF7ROLf19Y9tKRDsegLPxfoNxXNtoxogs0OFWBsrP8/8fem8ZalmV3XvvceXzzi3mOzMihstJZWVWuctldVYaCLmObRlbjbrkFNEJCQmIQuEGAkKARfEBMLUstIbUELdE0ctONwcbGpnFDeSi7qlyuIefMyMyIjPnN787z5UNGvfVb//POqchWknkdddaXOC/Oueeeu8/ea6/hv/6r44mi3ZpjZCHWRoSqZvFw/N4g496pgHw4+gRzhxBC6zKcNjEm2Pl8tIm+gcJJRIOnsuUntCNsJMY8rcWFVkZiemsas4o0Vg2GTF+AP6RHGQgr7O8ePHt0TM6pccX/lu8eGBjvWy9fcee+8qKh399t+9AsK65yWOB96WzwzAlLk7136PeyiyftZSg25+SG7Z1cV9sHPnw872Mvk8BBbssW0AwBkdww2ZlWzGmapBo8BKseXhFEPPhDiGyft/3LiaBJh7Ixb6FE+z4iJE9du+Ou+43bZriUCs+4c39u8+2j42tVvyv8S6eNNZklgN/oPOGue6NlKMzdoU+j/FbHMD09WMgXmh4z8OVVQ7o3xEU79YxNhNekb9BzT9rEZiliSfqnHO7apFHQMkv5lCU5iX1UOw0z3Nw96d8Tva/hsj2jco5EqNbIK3Ed2CPLB1rxZ8fTanI39kUQjmc8VWVCI0cNUt9fzn+OCpjjHuMmQWVCfsdblyzh1BQLhU1HNUrkutZL6oD9uWjkaMn3E6dNcV4UjA0J2J6u+vBS7YSdq2CXmcnu9O3upaPjd7veC/6rZ75+dHz/hOEQ/uTwUkgS4hpCCOGVvuGH5koFsWlrfNax8dcWPExPCW+i60FIo0EBmjSQq/cWb004wwbHq2/630EDnlHdELzeiq0rTiuuJdlw5w1Uw07Uo7NDOi0akWNkMy1NrQ17L68Ir8NDuVj1//8AHBLLkqf58aZVJ58qmnX4loQ3ri3Zump80jsS39s2PM6ZpnfeDxHxYUprreGfg8SGl5b9un11257lwmoyGzRZmMnrE0IIA1RKKulhh5sBoj8KgGeGSfGNafLIEZ7YKSh0pqYiYTJl3yft7cEKIpavN6T2fwfU8FtbHjX+uxNjXv5+zVdx/MyJl4+Or5Ysufy5hu+qzpCeeor3B7YCvn7dJtOJZ7y3SWt9KKvI8SYIidQBqtNYgZYXNmVXZSAveIIqOfU8clDATMHEPHqIYngcgymp1+VVu2awku5hG4rhsnARsUUFDKMPgr7/qIQg4LT+Y9ykNEWRFCUKIYTeaURu2LtH1iJxI1OJCLhyXcc34K9jKjQGRse1GqFix/HlFVOWT697tO0yPKaqTIiNYnLF4xiWXmluD8aS3hBCuADXn01+Qwhhe2Jun36OwvTZiar3Fm6jV5B6s6TRZ+WQGqbcyPueycK9X86DnnSX5vCMpAfeIgjnTv0eImar0kyWgGPRD649kTKxEyMK43sq2LU0orrZSdsxB2WQTe74L3NpN9GlaZ3q18GGfLNrL2ljxc+pP2lfOjpmViCEEL7SsOgM8WoXhHr6BixCFuaE4HvPjWQgV5FF4T53q+WjOMQPKZPzC6csGPHSlgebNSs2xkslU3oNwQhxLdWkE3wOgZRWywZ8eMHfo3rdFPEHaUGUDlpOASMzFD9fIROo5OQwsGVJaQ1bpjRKCMvXZDWcabSOPQ7Bl6Zu97y2+bu3rPx8E4CtL6973oGfWnrTnkNWym7N7vlPftFwAtq3iwbPVGK5VOhPNv2mcLZmlvzbbfNS9zp+F2PuundeGnO2kq0XRgLP/KFNrvs/7p+fFnRJ0pd9MCoTEV/Z89eV2naye1qmFpL2+YECcBnOZuj5z443qx4IDaM0UkLtjJ1P4CrRlBa94PGKRATQ0X2Mvlqxhof7yYYysfsx0j14V0+s2U69LqyvZEFXIH8pb1FVltKG4EGZO0Nbf2SYDSGEr6zYBvFjJ95z5+5PzZNmqvsXN7/prvv9tjlM6uyQSK0j1TZDpJjdRihjnNbk0PEgwZCJNSCF3aDVQYsgNNz6ABxrexni1dR4IEA4BszG9GblXyy137ABLl3xc6UPfFZpFxQZMpx1pNqUD4nG7LTpFzUJMp8/eevo+HzJGyv/4pI523szvybqaAz7qy0D5D9XueWuG2Bg8zLhClDQ2rqC8uvvGMj/qU2/J7FK+mrDpwKerfrsC6Uzxn6O714R4HYRz6hV1zxXr5syLK3437IzBh9cJ8V7F0k1eCr79uX9E+KVDzixUU4tDMHFsj3oyWVvjZ4+bfgYDtAFCQP+3Lq98PtCK3u9ZyG2N9vehSLYmWDh39vzjK1PNsxFOykkGAw7Mu+pynGG+2vonWWEq0IyweZvfPnNmg8LtKD4CVgNIYQcgV8adcHr2H7BjBxlOuV1fWH/jTHQPhSyJ4cQQn8DhF4yB5mqIoN3CNLbpoUFu7d4rSUm9ePp6zUaxemhm9QymJaV38VVROE16AbB96VYBk4/4hqUoIsbSwwvhb81CjUroBoDad6Doe9nRYK3sZSwXl0xRXpKQor//Xe+cHT81WfNqDkY+0H4X3c+fXT8ldVX3LkXKpY/+oOWrfdTS75i5IW6GUoPpAvrRsWcpLYoZq5/12pDezgxA6PdvQmQxavRdDN7nqUB5T8uoXHPlN5EdxdGDQXrxKxHGp4sQrVq6R3/TqrnTakNhQ/ONWPGO1HMFQlTC8IzRqegfN/fn8ZFLeUlDQOajErZeDmyv79UNyf8uwNPoMUimz3hOninY07zU00hB4OQ5+1eRzCzZwzvuqZcFpCmlLGxqWkI9ly3W35d0ZHY3/PP76I/Tbv/waG/roD0ZW7n0VMBj36lQjKYBx3YH8sn/Cy5uGJhu7Wy3+xpzdHgIelSCB6Y/IWaT0f9BYQBS4I9mWKlvA4Ay3f6l9x1RMT/3Ruf8feAJiKW4dkNP5l+fPnG0bHyipyAQv+/7nsMEtN3p+pmEJI4LYQQctfsXO6Of/mjDRvH4r4g4rFIafD3ZaMFNjsm3JQb9+y3DVak4ozNYAWnQ46eGBgQZe+kbO+fTCgx+xhlXkVPM/ReSitbVhI0RLVTN7ASIt7KktyDbaG4EdeIEf24hqe8l8T0c2VLAaB2rJiKCQzu6/fsQV684D3RL23aWj1T8uF7Vl+dLfj1/he/bMDOJmgo8qKEbqEE8W/c+6fcOfI5/dLaHx8d/077k+66T9VuHB1/f+xRxXfA3L5Z83ptG+1vIvRcUv6XfEoa2RmtKZWu3HNKhwsY9YRw3beW/bOyEjAnZVq9U7hWCQuZoSUdwzN+4rPJ9EhArpVz9v76bVOKrnlzCOHwWWQktgWPCr3lKsKC59B5qWcGytWK58lZxTyazqV0HvvVJ4og1cz56OXf2DGi3E/W/Jp7bilZkXO/5b78+bV33XXfR3ulizXv7dwLtiZ+75bvbbdaR48vOAurVR/hoZNfbfiXPYFj1GvZeyo3vKIkPQ2aIfxQSTV4+pv25Y3b/gUfXMMshIfZvuGtuTfP2lcMO34DyyP6w7b2/UuCXh+ZkfDGto/ifPKkxfr//PrL7txnqzeOjn+qYhPyixWf0uqj/u1fWP26O/ePuvbdtKY74hrsIN45Fs1GfEFBeIRev28RKjLCHrZ8XHeK8uKieOrj0zYZxsKZUka1Aj0xJTcjyaSW+VV3EcEDvqBxx4dk2+eR1hNvtvbArh1E/v3ynsT3RLPFC98Xd8nmav8/VYA1KxyTo8AxXEdSqfJUKlcc5kEYzEmI6ADGArzNMzIkjw8qnNAVRlv2JbqwaYqTTRNDCOGllqW0vj274M79M5svHR3fHPlBYEqYeLhzkh54oWzK/t8989v+u4emtP/BgTkxn67fcNd9v2/PNZF1e7Fhv20mA8RWOMMcNlAx5pNSlCGExE722qKDRk5KQebHJgwy7D0DigmJDB4+Zb+rcVMrTe24JrqJmF0S321t+b2mDY6zWJPmN00H56rotH1PoqOYAloAQuN171M+k/Fa13LTbHh7Y+jndm9mTsBM0lHv4OWeR6XX/anfpv9c0/avB5LxIDbuhngqS5HtgZfQ8uL70j/yYGTzuZTz9x8hlP2p016xEZ/6jXcuHR1XBcpyoglC4Io/t4uKribaK3Xe81GoU9dsksykrD5NUg0ex38wFsAx88zInSoZ1Hj4aEGkKWrzWxK6Jmr8yxd8hIe4l//q1a+4c7/4xHeOjv/ppinYZ4t+o65iA74oWIOfa5oR1UOeZncmzLHob9IW8kKCK9sNH4a9tWS72s2OHZdlIhD2MtbYeC85h8mOv9UHyTXK5BJRPEe/ArrvLnLc58VwQe+r2pZofnpH0mWdc8ttAuPFM3g4niTyU6oaYp2GAjSlB68pLQo3y+ZtoXtYJ+26RNMQZeDGoj3MGF2KAf+oQ5TnByDm3Y79gG9NvFFDtmMlQbuHHPzpoo/w1NBL67f3LCIzrXvF9i42k59ECiAEbwxVIlvTX2s95a57CuCoWwP/omjk5GRzGpJGHwbnTJpKkqFCGWH5JyPmipniu1nEXlrOSIDNq8zSebTjGPl91BmDYykzpuExBKdLTqibC/h7cst/+QSsvaVt4K8U8J/C6eLSk7LHdgDuJZbtUlmsN0h77iOuDJiP8WW7U/9bSo6KxQ/WGJt2N0a8ZcL0sIKbu0hTt/J+MyDmtCskbwQ7P3PO1pWmtEYw4JStmfQVxMlFaz4SxNRXWYDPaZJqjdTvAVx0QWiksUE+c8lilW9v+/JQx4xc8Q/2NOr92eX1v7zwa+66/2H/80fHN3reah1CqTLaE0IIv3nLSspfWbYKq6sNPwnZJkLxQwx555G370oS+rtdr+zdM2ISnpDwyS0g+pnemqrVSh1Y8Qu9dBd03E1pxHiH0RP7f62SoNLSzY+qPkdjReyR6jY6otfUFWWS3p+hwcNGhIXu4mn3ItJYNBJjmxnJ6MQeZWpRPzdaBScRmlLe/wk/H9ZewVwUin0aK+0ruN9+8masvcC46bjmmCGEwj37QbVLNpGeXPHr6pNN8wBPF4XGoXbTzuW9Qh8i4voL6I83lhTATYwjI7EqX6hZP7yDut/hdrBhaFfql9CLj80WVRjxmktJOdsfxFjoWRQC1TgWA7YGzFdaB/KPSzifCYOKPStTTkv+N5LGQrG2xLUNUR1XetM7lqUXbY5pP0n3HvDV2rfMsTpLuxgiLRQo+9J9q1g6u2oXVoW743sVS0/lI7/9MjLUxv7y/7b93CYPD1NHIfh5WhHSwHsA77OKakuKfa6AzPBg5Mf45X37nTPx8tZB6/Dym5YePndRQGmQg12xihEw4b42WxWalqoZYv3DlN4uIqkGz2ANIFQJKpAgbaVkoadfePJ77jrmNsvSnIRN0VjJtJzzk+lfW/vG0fG2lDQfzOyFvCf5nK8VjfCvlELHTc6b+31vMbOXDydQSVblz63b71ZjaA8W+q/ffd6dY/XYu4f2/NqCgjipnOSdGXVQLAk3Wxo5uRReA41WuO9ibyZJswzhopTaUomV4h0RxFzbsXcxWP8A9YYfkTAqwkjYtOZ/b+UB22X4e/D3d05KL5lBAhBaXgoxPDGjiSzoqKaMUbAjjTw4oVE3O9aNunPJvvCz62aQfKLh8QMnUQ6+LgjQIiz4zswPUA7nCOSs5vy6+gQ6KOeiV925bwGnl5bS6mGtalsZssxqKnq6C1bZyaMZJLFS7AQHfOmd5CqttLX5cQl/B59VDRfHXSO/Y7Rs40un4v2b2uFk1xbd5KIf0DGMIUXhFw+OfzEaTWIETR0/Ns1VJ6D+lM3hu+g/dbnpHegeovMD2VSJVSV2dFVAenSM1ejYATiTrSRUWFHVGQpVCoycRtGvTfase+3A8wOxgOHZa1Y0cLelHdER0Rf6lUrTvq/Pn9bxe94IoPTKkoK+kiXV4GEZnoYnZ6umEW+0QBrY8K4iAbxavUTvite9LMyrOeRDVqRk6HzBIiZXJB5JL3KAiXF/zd+fk/Ctoa8Tfm9oEaVv75nVuiEkZaweO5Q46V2EVnSCNkDARtBdUcK1U3gUsZA3jJpS3xuEXKTceFffEwWOyM2kkrz5MfSuIEzy6UyqAlh8gMVc9tOOqZZxbQFdWAjH0G1Ys+QxU9AveW3YETiEEJo37LgLWqk0Lh8NyzvSwMrxxyH4bu/6jFFKFCq/ZgqXofzf3/UM5jQSzlV9hOebKONdlUoQdk9/gOOTUs31dNUMrE9L6e5fatrfe3UjJ/2NztPuOlZhnit50AnZ1B+II3TpKYsm33zVvF5taEmjUlO53FA5BJrmpKFUv6dW68cvSU07JwLsdTaI/gw4arE2HpjrxQNgEgWTNoVunYsDEuPseSilLW/VRCn9vvgceo4FJ90S2pqIk8/UaEsWJDMI63m73/2R58nZARXL1aZQOsBIv7EvgE7IcyfMUbm25iMwZ9C767eu+2baL54zQ2al7JXSNoytGqAhyrVz74H9ntyeN7bGJXtPBTRkrbzsr+ugH+N0mAzpUEnvlg6HR63dfMUGtj+2k3/n1c+66yYHtiv82LM33TmG0pjCKV70k/Pv3bTy03pJvEFMEm3URorvzy1bw1CSEIYQwgqat3y26hHr/Pvnl78TkqQ9t8nbLnhPcQOG2E8uveXO/f0H5n12BgjXCgPl+KQ941yaq5YRao3R0mNhciPsnvTKorYDYsDkdRJq91HFcOgVGqM/WnpOiVWhJHRSr2w/uuX+UQltavaRitalFQgaCGprBm4KSiLpGhRio1MguaucE1A0cVa9YXL0wRk54mnNaACLlji76Y2XH4iWwbKC5GrJV6ucQZuWV8fe2rpUOj4ErtxXN0eWc/iLr3zVnft3nv3do+NnyzZAX62/7q57a2wp5W90PQP7K21LgysNxa1tWySMSAjrRJgVjyeSDEEYt5n5F36r/obdo31u8RyCQg+G8xn7kfV3/Ptiewd1lpbeANWB9FycAWTsGMZ73oCMgPnIa8sWkmfCORkrxQeiddr2he9oKI1yiffyBH9CqonGwUqIyXtsIf10Vioch8v2O7eG3hDndz+94tcj4ROM8Ch/3bmafd9PXXrbnWOKbnfoP7eMNNnEVTf7Obu8CrqHgh+fGTIbM3Rf73zCR/Ny+Nys8+iZgFSDhwpXLdop+mE8c9EGdqfuN+PcSWxgklMkud6zJ+0e2qeKjc5UiOGh5RtCCLnINp2DkVmqpZz38i4CsX5CkrobiCAx7TaV3eP1vnl5qhxZ/bEuUSh6wc0q2EDHEgUZoJFh3b+M2n17FjV4mO7Kk51XwuldlIXGALh4bfOI1wmQk6g7welMAXyeFv0XUPlxYx83Fy+l5ajnqUOnfj6wPUDttj/XOwPF35HBhtAYGkg1FyM8eUktDtaPf8+6hmecG73k51AqjtM1e0m/sGkl5JvSeHczbx9ckTARU1qn8v5zbANTCsk4rvNF825zTwm+Z2jGEPl1nq/6El82JC1KDobRWI3Mbq7aGOwzFaZ6kilgGcc5VCXfZ38zuYJJjaZFEKrM4QZ0kXZLR0qrdsuvifYVApr852rvmYIYnIBzJNtCoZ+sw3hT1yA0L4YRnlFLzyvbwAEJkJ/zowWSyoOiV8h5TJDe3Cvht4aWImI6WI0m/l0reEfr+7cv2XUn/fOTb2f3vh2vn/Lrr42S3Y4wLdPgeSCGUh2pNqaxlGm51UNVozhak67pfAZV5gd+rGZgZNbq0zRJ75bOKi1xtlc2LSf/b5z+v4+OD6R6iXiWJWmD2zxjM68O1F5NlCO5d+5Lg9C78ND2BM1+E12Y3+3YsVJps4fVd6a+PQX7Z1FWKv63/OyJl469LgRvwLGhaQh+odDI6fX9987heWjJJT31kbDukoeFxpAqTqYv116THjjPgssFhSzjhp9ojbv2ue5pwSBBsVT3xOsB2Vd5zx6sdyq5OenHJY6sj9iNsSw6vNdYuw+MvUZ4RngPhI1UfYAkdC34EPOWyUTv0mKyhuvv4fnlHjTsFItBhcvj14dn3HXfgCGwK2CJU0CA/trtF/x3oxLnCnoUKSfIaQyekr1dqcmAPZS/v+Mj0Fx/WrBA1tq3tzxeYcIO0I7o0X9fE0FtjWwSJsA5oulL8rs5JvIFkc5lcFOhRdBU09MbNgE70naCbYcKt/2C6V0GfQkqUocntH8EjoWqgVGdCLxY5R3/HOQrnDb9xO+jaW6QFNlezxQDIzUXGj46c3/i9x4KMa2M/rwrhJ40OtS5ZuCgIcYQnev6mlmLQ3Gu2zByOtIKKYeoTl5wbbtIabUObTzyq/4ZT6/Yb7txzyPDa6u2r/b2bTHFTJqePXNBMV8p8shl6QparpdMwazAGroi7It57M7l4LUqAYnFSDQFhFUbVwoCAkPZ30DM+u26vbj7K8dTzYfgS2Rv9n0+h7wDLfITSHPPe6PkicxJ2REDyhk8oBtVo4Z/a4qEURwVhw3Az9ZuxV0YJO3zoozwU100RkLvBdTOFwTLwBLz/noyKHGwsXhGDoUBugkqTUoPpNM23pEal/XbiBzIz53A4Knfxbw5K5VSsLdTOz6nFLoxdambrIvqyf3vdW0i/W7ROjzTqQjBNwx9/dAr7S+dMP1xpu49zB4aP770wDB1fzLwlZCMspyWljNkT78E6uJrdR/m34fV8Wvv+IKCk0t2f22weOd1M4Dyrr9cMqHeynV/boA2DHxPsTYfNLKVImABhM9EWhJt/TClkdgVC5vOgxReOLVO3LMQejIFpUUE/oZ2GOvXR0DtwD8jU3cz+W15GEP73OyFU4kUCUWpBjjAi2b052TRe0W3R1i4KXu9RiVZ0k8ZCSv19QNbx02BkGx1TUFVhd6FRuvmuq0dJe3k+s7Juy6i63zE6kfZ80gKmbb/qaQaPGUap7LOtg7MY/u11qeOjp8oe4XCbsf6gpkWYrroQCI1/BwnjP6tfbD4OVrMyuzKHP+4IS00NDfzUEbiErOX1ixlFrLMPQQfAfv1io3jn77nWV8jgLnmstC9YlG+JDum59+SPjF814rNaZ/HQicniOiU1nnwJsiwFVv2Lqp3vDfeuWzvOwID68r3kjksFkHY0kE7YXMz02o2Th01hpZuANSId6TjyfdABtsQfOUJCSYHgs1y1T8yZYkLU0AzPbsnEEn5iSWf7yeb8l/e8IrzpYHN7y+teYXOyqmvbNoE1h51JPjcF6TvNkJl39o1Q6lS8DqIafbPnvHAZ24Y77T8IMzR4mCMtik54cQqw7jtnJFNkkYmHRrdl3BOyfwWQYix4ZqIGR1t0w+MBL1/E2DGmsnGCtfLZFkKO1hsIdQdVItzwDFG0pTS9e3SSlmkrWeyAW+9ajnn6ZLNqbakhNqgvmdEJ4QQvnl4+ej4/Ia96B3h2rmHIhht78Dq4W/f84SCV9ftngQ0d9sCHAYH3A2hmSF2pl3yYzzqHO+sbuV8gIFs2XOBAnRAJpmDzpjLnJiAl2fa+5AwPK4fkGA+pnfNGv3Vyov2kOLhUDkWJQRWEuVj1wmhFD7XlQjJiBiePZ9TXAcrJyNSKo6meuIHb/vQ7lkjKZJYxQoypqzVzDu8tePJzXhPotln25KzhFKpPPDfrbwdFGI/mJ8utr3yJRHYuKGAPzumwmEqKoQQSi1GccS7ANX/4VX/2whUbty2cWz9mI8KLIKguMg3TRSMDdNHxbbkqVHBNvK6wFXIOSZn8fqJm9AmjWTN5t6pbOn7z+K6gX9fNIC0LP1gcHw0dm/i118bDz0VJ+AWqh93pSqTxKMkM1NOE4oSAy5hI3hhzRwabahIAravvedBy+w3tFT1G8vZ0+Yh3LlhmwIbt4YgmC/Zg2mMjlJwOozMKjfMIggdn3mFf/jr3JwSp81JTVqg4JhYwIJUWE1W7XP5ooJh4Twsmc7VSPp0RG9EgMkEPmsUatkWPDu6v7bjddipihn3T0lH4K+uGSyCgQIl5mRU8u2OnxCslFqWfowvvWx0LHNQOkQyVoRT5Ev+XA5rYiaVqWwAzkifXufS/cpfBiM54ruQWxRrNj75pZRonkh6SgvfF+OMYBd0GC47e94araLj6UB/N8J2dQCbxnnv4uy27SVqznLwAMqy4RfK1h0zLpZPWoitLIZWbwTiPgn7jWA9slS8/0DA2afNuCqnGD8q7UNTuN1iMjNm4217Vfou2KspLyBYQhsm4fhITQghMNI/UR4nrnPeT/Y9AppjxIZ4LJILqhQPwMOw+eit3j4qKXaANcBUaYoxMVwG7kl6CjGCNi8kGxplVM51TwudwT07N2pK5ABjzwbjy297BThYsxc4FsOL70sjebvbG2gp6AAAIABJREFUdvE/rBkp2lDKudgzp1aUKgus4+t3/KZwcsOsuS3ok5l43K5So+UdoaUzdo+zy7bJNAr+OVhNMpcUAEP03VFKqnWSPO+5VvMyjoxmFtiMWZU70qj5xcPxhxyoMNLYx6m6Y4URpHEQQr4c8HEE19eELHPURQRJGnMOzgAAi/tp5JR/5yRtRTqQSVMi6TCiGHFo7XgnoHHR9Ns7Az/vafgz9ap0LvcGtv7ud/1++2DHzl085cOBpU1TBqM9AIf7flItbdhetrfrn3/aAuxCDFOmndg5QUvPZ4juae9H3mNOPj59T0vk8PuwUloutSFe/x5aQfRt8PJ3/W7MHHbsh8MbGK/ZDxhLiKr6tn1OGTpDM9mjqN6xn3eYsxenyPA8wp+NP/a7+PQcNpYqiAc3fZ5iAKu41/ZjcJCHUaZeD0r2Gn8Ej1j0K1qkhJGQZXHhx4wQelX47rKHQjnREmh2ByHIttjzGnz5Dfvg3vN+By2CNXleyMs5hEmvmiFZPlg8pmWWGfO5tW1D4y56h635ZcZ3Eov+4D4MaFTkfQ2Xjsd/hJCMv9l53k+Oyj7pAJKNsoG6V5A33jRUdHndr4mNpgHIrr992p174qrl4Z446wHGN3cs3D4bY64Iu21uAwZc01saA7R+eBMNTqd74i3UMHiCKxkDhzYTnADBrnXakbK+2V5M8SKM9JFKQANZU6jD4criYXgIfmdRg+oYZ1SnUKc0bojTic8x3dfzOHKnI7XTeYTBdyzoSrcyPP44BA8yL+/499BrwBAA9ida8fPyTw8slattFchr8/Vda8y5I52eSQNz+I7Hjs4Apn73tpZ24plZZCFztgMCR90rc2175vINcXA2gH9CNExQKCGHaK/ib2hUOuJzecYR+pZqo9g0iebzR7eOMskkk0wyySSTTP4syuKxWGWSSSaZZJJJJpl8yJIZPJlkkkkmmWSSyWMvmcGTSSaZZJJJJpk89pIZPJlkkkkmmWSSyWMvmcGTSSaZZJJJJpk89pIZPJlkkkkmmWSSyWMvmcGTSSaZZJJJJpk89pIZPJlkkkkmmWSSyWMvmcGTSSaZZJJJJpk89pIZPJlkkkkmmWSSyWMvmcGTSSaZZJJJJpk89pIZPJlkkkkmmWSSyWMvmcGTSSaZZJJJJpk89pIZPJlkkkkmmWSSyWMvmcGTSSaZZJJJJpk89pIZPP+YEkXR346i6D/7uJ8jk0wWRbI1kUkmXrI1sViSGTyZZJJJJplkksljL5nBk0kmmWSSSSaZPPbyI23wRFE0j6LoCfx9FH6MoujLURTdjqLol6Mo2oqi6F4URf9ywn2aURT9P1EU/Ur0vvztKIr+ZhRFvxlFUTuKom9EUXQV138hiqJvRVF0+PDfLzz8/5+OouglXPcPoyj6Fv7+/SiK/rmHxzeiKPprURR9/+F9fjWKosqHP0qZ/ChJtiYyycRLtiYeH/mRNngeQU6FEJZDCGdDCP9KCOFvRlG0yguiKFoPIfxuCOEP5/P5vzmfz+cPT/3lEMJfDyGshhCuhxD+84fXr4UQfjOE8CshhPUQwn8TQvjNh/f54xDCk1EUbURRVAwhPB9COPNwoVRDCJ8JIfw+vv4XQwhfDSFcfnjtX/1wf34mmcQkWxOZZOIlWxN/RiQzeNJlHEL4T+fz+Xg+n/9WCKETQngK58+EEL4WQvhf5vP5fySf/bX5fP7N+Xw+CSH8TyGEFx7+/8+GEN6az+f/43w+n8zn8/85hPB6COHn5/N5P4TwrRDCF0MInw4hfC+E8IchhJ8MIXz+4ed28R2/Mp/P787n870Qwm/gOzLJ5P8vydZEJpl4ydbEnxEpfNwPsOCy+3Ai/kB6IYQG/v7Z8P7k/u+O+ez9hM+dCSHclGtvhve9gxDeXxhfDiHcfni8H0L4Ughh+PDvtO84k/xTMsnkQ5FsTWSSiZdsTfwZkR/1CE8vhFDD36c+4Of/Vgjht0MIvxVFUf0RP3M3hHBR/u9CCOHOw+MfTOQvPjz+Wnh/In8pxCdyJpl82JKtiUwy8ZKticdEftQNnu+GEH4piqJ8FEVfDe9Plg8q/3oI4Y0Qwm88zJ/+MPmtEMK1KIp+KYqiQhRFfymE8GwI4f94eP7r4f1w6I+HEL45n89fCe9P/M+FEH7vH+P5Msnkg0i2JjLJxEu2Jh4T+VE3eP6tEMLPhxAOQgh/JYTwv33QGzwEn/2r4f3Q4v/+wxDwD3OrPxdC+OUQwm4I4d8LIfzcfD7feXi+G0L40xDCK/P5fPTwY38UQrg5n8+3PujzZZLJB5RsTWSSiZdsTTwmEhlYPJNMMskkk0wyyeTxlB/1CE8mmWSSSSaZZPIjIJnBk0kmmWSSSSaZPPaSGTyZZJJJJplkksljL5nBk0kmmWSSSSaZPPaSGTyZZJJJJplkksljL6lMy3/+M//JUQlX/4znS5rDVMqNrNJrWvE2VHVreHTcueAr8Qq92dFxfmj3GDXz7rrK/vjoeFz354rtqZ2Tz80KkR2X7Lh2f+Suy41muM4//zxvn5tW7P5T3C+EECrb9jvHzWLiPYL/WCi2jaBzuGKfi2a+eq50aNcVd7v+/kV7rs6VJXeudrd/dDxp2P1nef8ghR6IQiN/blq2MckPbaxK91vuOn6u/fSaO5Wb2O8p9KfuHJ+F9+9vltx1X/97vyyj99HLs//hf3v0QyZYEsW2v27ctOOld/y7bF+ynzGp+3OVB3ZujFc581Mq5PC6Ch1/boZhK+EVDVf9dXmbsiHyryRMQLOW9tvmeCO5sb9uDu0yLcm5vP3u/FDmWxVjYtMhlPf9dROwmej4TGv2wUKH89dfV8T49M76dzGt2N/Nt71eGG7YcaFnx+MUWrmiX7ZhuGL3r25BV8lv4bvgew8hhNf/43/7Y18Tn/8r//XRDyn07TfV7vbcdePl8tFxvu9/SGHXrp2u1ty50bINSO2lu0fHvec8YTB15mDdb23cX7gnDdfL7rrqPXuO4aany+mvm56t7PsFU+jY38PV5G014tQu+FeXG9tJ6svBmt/Xmu/Z83PvUumf9L+tdGBjPitCB9X8/asPBkfHww1/j2LL7sF9IYQQxs3jf3eh58cqwiMXul5pDDbt+zgepUN/3Qh7pY7jH/yDv5a4JlINnuGmTbxIqtdrN0wLDk4bi7Zu1AF/08AJwRsC/Q0b9MJANvst0xTTs013jgbKrOhfQOO6abPeJfvcpO5/dlSy5yrvDty5XMsMhsEl2zFmBT9JplXcM6XSPz/wYzBctRfHzYOKI4QQhmswhqZ+IdLAmpb9u56V7Dm5OIYn/A5UOjAjMJr5Zxwt2/fNMcaFmr9H74KNsf7OYtcWyqTmx5+/e7Rs54r95MW8CDIr2TsaN/24c1F3zss56Pq5LM0JCOmjR/z5c1nF/NxoGf8vRk1EHSKx3hx8gonQpOVtSQROxblfEs4A0nNhTGfEn5oXYAx17cEGJ/yaoNGUG/mBzA/oqPBz8i5goMzKsnCLNpCjVXGE8OcMv03fmY65uwfe2yzhOIQQpuVk43ARhO+5+a1bR8fdT51z1xU7NvGjqR/rqGuGxnzDW42V+6b/58t2rtjxm+CkbnqwsucHnnpxWrEBpqMaQgjzPPRb29+/gqHXDXhWsM9NarhHL3kRFzvyjAgW0Liay9qkscjnff858DvFKachxv18UvX34B6ujnFunGJgnbBrix27rthONmrUmWq+vHN03PqkeRWRfC/Xme41aZJq8HCSlPf9Qx98wjTp0js2WTvnxTpfEW0Gqd2xz7UvcyL7iTBet3vS8n3/b/xY4RTqXrENmM+vkYNSC9b0Kf/848u2A5UQTaInEEII/dPJz1jes91jWvaany+Ln9Mo1zwH4/Ckj5Qt/ckd++O50/65YNi4cRXdTkNmsObvXzqwseudsvtNL3njs9C1+2ukjH/nB/798rdx550XF0+50yAp9Oz5NMrC69Rj52IttiXSRmMC0zQnBkNp347V4KGBwggPjZ8QvBGikQ9GVsS+dn8zQtI/qWsT+mPP32OE6JVGhnJjbh4wagY6HxAZ9EHbMF62HxBNqIgTbxEzVvIHNrDjhv9tfG802PQeVOglCYhSv3KO6LuYlRdvHVAat6D/P21Gjl/XfqMervnIQf7lQzv3go/ccN+O5va58ZJfWFPoi4I4SzQ8KrftuyYaTcJz5SUyQf2mThsjFaW2fdek4seAkZXhkteRq6+bYVfo2qTa/jG/X+0+Z8qlKobduI61I/Om1EXUEw61BikYOGDEPYQQool937Thx6B8iPFB9Ge45p+f70KDD7OCKQYaZeqE0wgcN9SbSpYfYvAgelLwL6fYw4DB6Cgf+FAlLcJxwX8dQ4b8AfoCCocWdRmcabhzpa+/an985il3bobnd4aGbPa0yHWRcjPhZCp0JcKDVakhtjle+Ew2cSpIH6L319Xv2Bh0z3qDpAcjp9Dx4z9mFAa/TT3u4h3bQaeVDXeOqTyG1NWwo4eiBht/jxrP/ROmuFa+Z01+NS22CEKPnZvUzOtvH92Q/YpjqO+hfGDHXej9acmP9WiF4yn3x5xlpEYjBxWkWPon/Lk8Ap0agclhQ2ZaTw2vGRZaz9vhobKLuSgeLJ+TYzWTMZiVbfHMe2JgIypSRJRIf0vFHMrQPynPge9jNCmEEOZMl/N5xXgrYBwH6/4cDRtGzTT1yHsWvJ+1EMLoNvXntJJsqFVvi2t/wSZITqJidMZyiBRHon9oGTlHWKR/wSz/8gNJu50zJVw89FZ057zpXdXxK2/Yi6ZhVJA0ZucsIvqy9neet72tedteumZXuD+q0VTqAF4ijhaFUcliV40JvkNJWzEtqcYQ/mRkqLLjx3G0aouwLIGD0bqNPw2j0arf8/iMaVFUlVSDhy+kfMtr1dxpmzSMbhRbEmaEJayhp9KerfIIier+hlrP0FJiDA2++Imj48qWH7wZjJwSzk0rPjLB8ORcQnhTRCaY9tH0GQ093eBGS/Z7CjIGNATywELpJJ8Vk1N+umFQHOZm1zRsNParYXjZtLGGcmnQzsmILr+TEZnKnje8XPpSImz8Pa1P2HPogloE4cbkjOGGXIhXpNGNAexJ9eZ7MDy4kItdiQTRWNmUZ8Q9h7AZFf/BDb4qZPRjOL55n+V1URJiVhjxCkGMtLxOaGCVlpPPlYDbGWyKI9S2NaERHob6nTMlypGGnqbF+Hv0GWlIcr3ru+a7iRmOWGZ0dhjlCyGExi377oNrixftoZdOfIx674NN0x3DZZ+2Wn7DJpUaMtTBxR0btNF5H3qsbtlL0dQLjRVu8IPT/jkaN21hjVa8F0M9Vd73E25Wtt9a2ra9ZiIbNXWmztkKDCXugeOmXGc+YVh5w+95Oy/Y72nc9Qu+c4aGqf1/bcuPVQ4ObvM1P6H3XzTlVbsnY8C9DLpb98o+MEnl33nTnZv+zKfCcaK2A/+OZo++JlINHi7I7tPe669smxaMpnYbBVHloPTUKh5u2IRllEUnAqMzirFhlKh7we86eYCeRiegweeqvLChi8FDS3W8lBypSbpfCN7IYXorhBBGCMvSyNHNnimhkiy2/gkAvSqSMgNYj56ShgG50Kc1MYYAXJvA4i8fSjQPiko9A0osuoSo1ACYJs1BL4I4ACnWRyTGBH/jpKbnME4SdiawtWb4zDCQYJczqtXwTIKTacoGG+vIY92dQTVLATRruss9B6ZRrq8AR9xfsDP1m1hzXNLyOwlM1vQncUAlRJMm2sGI6ZKJGGzc78SL4fulgdK87Qd5d8V+t84RCqNEGsVpXbHvbt5IvsfHJdQJtT0YDMsr7jrq/6JiW4iJknVf/o1vHh3PP/e8/f++H9DSXUtVdZ7x4TR+H/VKUXQ18T2almekYrDhrdfyHiIys+T9hOt9JE5Sb9MmFfWA6kuOz8HTXrlMMb8Hq8mpHu41y9954M71r2Lsxn6MOeb5oVcM3bM2JtVtuy4n19Gxn3/mWXeOy8xFceRdcI+tXT8MjyqpBo/LWUp11HDVtMFoCedy3iqOUjZBosEJVuWAhOCNC7W6mafUqEgORsNwhRNZgVjUzOpJ24tjpKbxhrd8u9dsR8rLIuLYMZwXgn/B4xQjoeAsZm+QVHZtsWkol2CvIcBipZYA5mDkjCU3S+OW468TuXcGYLSOKBKMeelQqrQKxxtRml5cBCEA1qfp/HWa3qHQI9HKHW58nQv4jIZtMTS1e/4UozpMu8VD43aswGSmraZqsGGaegXlr8v3kt/fFNicaOyvG0Lfcrw1rVRsY97sulNhuAqsxCqrPcRwocEjYzxeQRWppMxcmgkRrwefU/D08d8Vgh/jtEqvpDmxKFLZBUYRBQ6DNb8Iqjuo8BFdl9+3AZid8ROu+89//uh46TVbaKOzftLmT5oVXRJnjFjGxnv2Xb0zcg/o2fKuD79GUxhNZb8POV0FhzoGzuYck4gfdQHxe1MF02MijWsyn3Hc30hef4wC7/7kKXeuumsP2XnO57q5pyoelV/u92z/Q6uI0qlRycBH/bYtHsWtzpp03iUHnCLpER55WZQpkN2V3WT0PUvFywc+VTKtHW/kqGJgZEJTZjynESQaGow+KPakgjxurusneedp077Me3Jhh+DxPbV3/S42LQE8LTljVo+xKmmqHqvLWcoiQpovli5ChKd2w1CTrKwLwZd+shwwhBC650wBUZGMl73hxWiSPmPjXdsV2k/4cEL9trnIvdM2sTWCtAhS6B+PPRmu6XU4FqCs29zEy6vet2OmnBQMy/RUGjYkLapA5VuQtBVL4hWHwOiMU9KCY6JnqhgkVoiVWhJVLfA6GMqS1uP3aaSJoGVWbI0kNUUDaCLPT0WUk6jzJKE6LSdVVCVgslSvMcVFz1xB7mn0AYsgXOtzUFPofCjv2KLoXBSrrmMTqX7Lv0yXCQDIuHndo8APnzGYRUkcrhGqKMdNG3h1rpkKo4ETgseRaPUVK6CmZVs8WklGY1YjNy79jI8pdm1STcavcE5VJL06RNEC9c7KdcF9Ys9W3F8OVb9DMWgbt22i9k7bg6gOGgeW9/vxKaPcv7htirN7zhs83B8VN5cmjwxaphUfgueMcZw8slky/8oS7BDEyCHquuatvtp1WwyK7iemR8vrVr5lobrRebMCSze8O9h60SzcQt97F7RoC1jYytHgRHhs6Fn3z/sdzoVXkdqZ5RUUjZSW8tjgnG6M1XdM4/auWog55u3j75jByXAzDTGt4k2pAhtt2LhqGidGZXD0vYsX4eHv4rioUcANTCpHfbWO/ETN1x99lyjHCAZKXhY8lSANAcULpWG/KIr9ocHGTU3n3qSa7DCxgivGoYN7eh4hMZp4D1XMIyrE5Goxl26Mzfv58deFEPKj479bx8AZZZpOmycc/xmTfMcmFte5QhOisU1MTfWEZZv4xITqtSzLnzbUQjVRxzja5CaO+0klFvEmxY5fMGlpescpR/yQ7gWkjJA0Miv6XBRVAfM4V9lPNoa6UijgDGd8TPnravdMuTDFF4IvMClLpJ7UKWM8R/2+VyDcp9P44IZnzEKr3fXvgpVfupelSarB4wfFX8ooCXE17St+Q6cB1HzJIyPbz1u4rIpBLlUF7HYCgLO2HzwaXiqtF8xFZo61+5wvx3CkeF1/f1qW9btau2tSOkSqo+qficROiu9heJXEgFrJ1Dtli3taFfJF8luI8RBtmXFHNdJ+1ockGLnJD2SHo42DCVp/2ed+979gZUUMR75/D/sc87sh+CoPVmhouHMRhGuCG7/iLtwilJ/BiIwaHa4KDlNA+SpYHTXWdBQ+l8anEyUYbyF4YHIav44rp5YxYEnuvJgM+lXyxSIiPvRE1dONcM9JinFMD1Mjcc44rPm16Qx/GZ/Rin2wtA/wtKgI/k6NQiXhdlipF4IHVus8WASZVRExQen5pOKjzf3zZtQoPo8R83FDCkLg9TOapOXO5YNp4rmNf2T8QMMnTf8P1pUzwg4VbEs9q/sO05M9GgUH3igYp/BsEeDujMWCGDxYj4dX1UrHsWLemL4lm4us79ZlTFSNSuLamRAirr5qkzOHqgcNRNAx1ihRhFhE5YalL3tX/cJNC5CkSXqVFn5sVVgzO5fszXXPg0NHyQXxLNMNIQ2Epd1HKiMvHAoMXxVnyRatehTOc8RzOMxRCKH2wD5Y6Pib5IfgfQC2RV8ieR+iSfLzF3r++Ycb+N3wNgp7wqZ81p4jRlYHg0rZL0fPXbRzjina36N4AC9NomhUMjxuv+Bzv8QFFQ695p99z+gDJj/74+7cFCFORtR0sS2E4PFK2Hw0NeVAy7HoA24n78FtmClGk6s8UsA0lTbvr1CAlHswopGXqqGkShMlBqQ3rmDhNK+shrTe4VMplYsEDqewNfsIpdwEBjwjOu//R3TcYQjBR6j4zmKRIAeKlnMJ9ASxKN/8+OsWRbQa6wei0X7qe+XJITZQ+WMmp+3+9bskZ/Hf56vlpLDjadNVdNoUz8kKLi1Mof6PcTZBbxWB91I8Jz+nET/nqDjSTp2XCcfBrxFNw1IYecxL1e/SGxZW3f6MB57XdpJzqozMcX+s7HnnfYTgSU5SipXvv3d03Pn8Zfv/Le9AT6uEPjx6njc9pYXJ0HrCa3QaCQSdKnZjis2YrL8h+Bxs53IT1wnwj9VWYlm7aEHMGgVwkaDo4TzxutFaWqoKh4pVglcyvSRcQSAsJHg6BK/AGnugPD/t451LrwF/o20+oEgUjOyYi/Ec9TsSZQGeqnLPg056F48ng9I05wSRp/aTEq+9ZsBD5WpixRjTWPWbkidaAOHvZ+WURkicQpR56VJCis1J4LjRtA9LU5VQ0DH/MmImq52RhFglGY61LH2M76PirOzIRsV7imImkZ9WRx184njSQMXHRDTYVPHjc1xjOgbOmF8WfGCLuDbh+aFRiXvGCBxd9ZF+N44xD3Iy3uRV0udfBKGT5eANsg8xuh1JKoPGSvO21yvtcyA7rSZbfEOw9Y9W/HU0IMp7yaAPtkFQ0DWjNZW7PtTWuWqLYgRuHCW6pJHjGcBFiNkUI53rKsZhRUdLU6i8Pd4NgwYhhNC6BgySMP4zOtM+75XS5v/57tHx/pcvHR1rOyiOMVNkIYRw+MUrR8esCOufEtAy9rwPLcKz9Ipp1d4Vj4Tm5sYNd7isTMKsxvDnumdtYGl0rP3+LXfdwU8Ye2cMIwRLUqNQcxhisxJJo9xlbhGxV0gIyVww6qFEIHyKgb1JyCQtKQj4Zvm3ehCtp22sFGhHenQlJeRzjmBssQIvhBB6J23ire35caS1zhJ7ZTpluXwsBQplp7lwpj4c2HDxMlpOlm7Yezh8QjZjp1CEPwbgW01zJHnwsY2a01SmGwHTLlog1xEfM/CsEz5tpWBkvBdu8DEcTYqXWtkCSaV3Il1FlEszCd8GU1zKQeeq5JiG7Mt7csSGkn7Io7KzIuudnD28pYJIXWm7P8f3xDGdSLSQRvCjthv5KIVtEBp37KUPxejgxjeR6qL6vTHO+c85Y5CVtyll15pSZ88spsr1OtdaQiIfNI53P+33w8o+mJzBftzfkNQXozhiwHPOMqoa5WeJ11EHhSB6KBaVtGMaRuWWv/9gJTkTsH/N/mPlujccWz9h2QRCGGq3veNK7iMtIHJ4OFS7Nd7yed72Uzb+3EN/mKT30gJoSMu1ZwlzLa2CKDaR8Zz1bdssJ2ek7ASimyWV3lSQ//XbwMdg01YQJjWRhjH5d1qlF3PXPbFGSzCiYohyTAx6Ofo7XShUQr5uMcvz5w+Oj38qzQCNqINP+sXs+q6kAPdmeVsM2jCODUPJnB1CCHMAucs7pt3zNz1GaBGEEY0WmoDGqqFSQOBURFpy6sjvGL2XzdIR5skmy4jMcM0+qH2YeshIxrAnmKdn/ouvu3N3/v0vHB0zCqUzw1WQyBgwKjVXjAKzmgAcR1N/ExoCSsxI73aW4knzfTZqfhAO+gi9H3hVqW0ifiDqcfPdx8gRmYIBrYGyXn8QUObHLZV3zUnef8qjZklVoZg3RlNUv7mqJIzFqOkH2zXclKaXJKpjBmFS9Ts6nVWt+qVzHWu2C6etfACnsKa/hfdLTlWxSjKSVCujvQe+uYBzLEr7uk9g/8L67m0kG5ix/ns4d3hZ6FHwuxmYUF43Zo40fUnjiwZy51PJzajZGuqHSarB008BX/VPIK96D6BZBUZigGKke0hPMeWU0814SIPE37+Cmn5Fm7OSiqmeGLkgjIS5ANU46R1gTlJTAc9fu+839MIBOpbXvTtL3oEBGhTqS2QoMcZdQES8sF8St8PJFeM6Yl5badP/FJG+J1YSr2M+VtOSHeCCipKWpFHMsvRic/FIR2gs0/vRDZAVGMpAzDlckI3adRJGOFznPSNBwqPpgLmu35dsMgyvq1HG3/beX/+CP8cqKnB+adVJLiUK5QDIGpaHEcjxqO76h+yfRqpbS7kRyWEPrnhKy47rJb92DuZmiGuEatzAuMKB1Q2CY66pQaYm2E6iKHOJWDG2G1kUWf+m6YfJCZsEscpCDGHtjgeGMWWhBk/58PjocIz3ibxGEoFntKn5tr2w9hPeSc7juxrv+RfRB7Ft466fK72Tx/eM1ApKromROjtM33L+aoRHMT3uJJ73tDidYCZ3RQSxtZ8cJeL9m7e9l9c+d7w5Uez4xTkAGWdRMK0s1iFmqvZAG8Uifbn06Hne1CtpaKQ1s2TPKiWV44MpbwtDoewd0t/01zHsmO8J/gMEiMosSQ4a3oObagg+TVMSsqnhEgk3kCKTSAoNqqlUmfVOmzZTUDeta5/T9xOBbS30+V0+U4xFh7lB7jptksSA56g643tXcLkaORTXCFM8deKw+O6rbyxehMcR+aUQ97m0jzKHl46/LgQfKfLN85KfSTdZGi+uk/V7/iZ7n0DUM8ZubsdaHcUyb9eHO/ATAAAgAElEQVRqY12MaEQvtW2Di9bEADg4nCQrX38Pf47gZBqViqOhcXWq7nEZLfCutASVXtw7nqZfDVNf4Sjn+H5das1fxojPIqa0Rqcsb0qnM432YCgRGEfAKlERz7lCFmN/z0el1vAn/J+MBBFGEILXi1pOTQ4nOsbaz2riaCLUESLQHr9ZIjxzzPu0KJFGRGfV49PDo4aMNwHNohfKqEbunBaME4zF4TJtAm+seM4i/92DdZv84zojcf67lm6A2TqFL1Dlkbulq8V84mtWYr7zk1bmp9USXMgk5wvBW7sErmrfDJYY9p/0O0t9C+XUmi7CODDNpJTkg3UbhuETHr1Jj5ALcfPrO+66wXlbHFop5fqKaFSE5G8AiGkUqnXVnkuVHq3fePoEyh7RK91jyNas6bSDZ+23Lb1jO9xIDFgHXhRlxHy4VgXQkufzt19cPHc2sVRZxpPvVSMfsWogCt7XxPWp8pdRKcUqg+jpIvqw94ykSVmSnVIxotYEo1nEm8TSOcC9xDYgeK3aCyePZp+MbgzXkw0jjZQNT9oPyPdtnmqUhfN0qeijDqt1G/S2lE45Tx1pw0JbNzE7rt/x303dQlzGRJmW8YylR2fR/8iELQWWrtuYHV722wvBpap/WATTOynONYG4iGZv/qnHhhw+CR2pdgD06eFTNmkrkrkgzrHxnp8PpEfRcuq1V+1ZDq7ZC1QOmtYVmxCjpijyGXU15pToizFSwGzDEkII3YtIW4mTwagOo0S6/ogti/XfWwPW9g2/4TLFxc+x9VEIISxdt9C18vywZQeNytoD/55aF+27dIzTJD3CgxSF4gRG5yy1sfp3rNdJ75/9jLuu+SrKSSRq0XreiAdGqHKKs8ra55beUwJENq6TLtybx1dmKX6Fhp3mFFmGx5TTgy95lCfHavVVKeG/iK7wSrhFr4RVJyljMJEuxMRGPSppoE7k+iuGsO9+7rK/B+6ZVIIagu/HpUyqZE3unRKwcxu5fRh9WgW2CBLHf70viuEhGFlxW+U9G9DuBcGCwZhwRqPu9dPkcxQXLUgJT8fu4YC+elM7JHOsKtjqA/u7/aRXIEVw18S6oBOXh6ihtqpw3cbFkBmtEfjMOL+mYe3v1/c9P9d+BxuoeMvEXjFlpuuWTNRqmDqmZUb9xFmgl/2oZJEfpVA/dM8nV7k6p1kI+TowmtJ+I79r95Nex0RuXmorEGQrsNccXvL6bOP7Ntnbl/0LcxWwomj3nrVnKWBecmMOQTinUlJTjDzmcsl9x9pPKZiIwLBkB4HXqU6jka6gZc7N/Wv+t5WBFyWz9VT2qwMYnLqX1bbBtIx0V/ucFPvguz404sEkJuQQfM4y9xfMyNENvY0mbnqOBgRTOM1bfocgoE1TMXwBGtryBGn23ST7CyGE4ZK4VBBGmwoD0l9q/hXfJQ08V75lxCKHL3ruGlYrEI/UOZ9MqjXPKQvz8UDX95+LQGs8o4RaD376qj2HdDr3v9sOGZoMIYThio2jlv7TyCx2/TkqIEarumeSmVQ/LnG9l/AzYpVMTCuJviLGRtNR3LgJ7I31usImqG0hSAA4S0lNcRPX53AEiCml1ozAaCSLlV/aL4u/M8ZATCZZMCN3LqhmA/ZO0xv8PqYeheSwfhuh/algAHvJpKbckApM8SlOhwahGL58Zho1sV5a1HGLtyTC0ruI+qJv0jzymwY7qbcvVuUcqlWl+pOGYRnzVPEfLhUmBhUjzLkxnVj/zknw2nzX7xOk7og5D/itXEu5cbKBHXLJBTguNRWLjuIWWulFVS1GepJloLg21/9N8XW4trYtzhobVRNjKI9Bmpn6fWktcds8vv0XzXZgKi0Ez5eUVkijkmrwdE+wF5U/xx9UwWSayY5LJP3Km0Kml6vh2P7/4Krf7Eso+S5K9Q/L4yfSAXy0BGwLFsNwSfpIsTeQeLN7TyF0tgXPUyzT8pa9gP0nvQYvnzIjZyycB6NlWMIlW2wsbQzBeyxKtESDYSQspaw6YFM4cieF4Mdf+SeoLDrnbDxqD/ykoMGp3dhplMVYfaG4CJT/ILnZj0qYeqjdtuO+Dw44jpjxkv8d1bs0NERh8U/qb3XyyCsijUt7yASyqabybbjnVYMH99cUi+ejsmPteh5pihky3ABRW1sUP0Lv7StIfcnmQcMo1lG6bg9W3rU5pf2duuewPmRDOHXC8kf32j6iy/fG0vn8MDltqAzK7G002IABJVEo/rbRyuKtidYVM15YXq4EigM0L1acIKPxsXSU+4NRBJ03OJadjVW6sbQ/hLiR9iWtwIFhO0w2thh51Pk2BY5mVvOLzusCwEnyWipFT0vmSnl2/HV6LcZRGx3PCPWRc2tvmc7ffdqfLCIC6zBNoruI9Tm8LI1Fm4Z3df3PRAfNYSySCuGHSarBw41IDR4NRf1A1GJ2hsy15EgKhQZOCL6cUUmGaEBMyv7n0DOI4Xt4f1ZZxCIkdtw9Yd+lXa6HMFyUv0GNHHd/Zm2YRpAFxXAqyQRDkMoejayUafQxkpLcG02bvLYvgOWZm4zgkVgJodE8Khm16on9Yc+wheylBWEERin/XSNA8cKUZ4XCiAyNFW2JQKNjLJGVJNp4nbPE9wyl4fAQxYSx/lN4fmKLlE+n2EH0JDlYkprCcF3KVX8Tk6CkfiNycCXfn2m3rijfU0vmbSo5Ij11RyAnyr0CY3QkBkAZeJwJNlrdkJkuVazSIgg3xdzQ9FRaU0dtLcG0dhoPDz8XYztGZELPcY417oAvrK5Osv1dEz11eMUWpzYPLWIdsFp1ItF+zpu5NtkjPQMxNvLKGfGrSOXiAPpjrikt2kKYzzGWdabjxdY6uAqM010ZA0Rd2udBeCsRfRqfjbv+y7lvcP5ozzBmbNoXUpSLSKrBw2jByts+Vntw1aIYTeBqRk0f3RgiytJb8i+HFjm9H9bzhxBC91RyOoQW9PJNqeAiSC7FaqVR09uUPi4oD++v47cInrZ2j8aQkJRBg61+37t5t37GdhoaSmrYzcDeqZ46DQPtEky7g/cnGj4E79nM8/4cx5ypx6G06GD6TMeY3x1rwodXysjWQkZ4mCnBMKk3y02Q3brfvwmv0y+wQ0aNShId8JEVf86xrQKGoOkczqNYJRabNKaQI9JQUj4dDlZROqKPm5gPUmlCJ4DgyvodSZdfwh9qBySsdwWMM+12vuFpcd996ax9ruk1/xTRpvo79gWDTcUjJRsonDPckLUsnYakVs0sglA/HDyZjOFhlFrpMw4vg0tMonWu/9QkeTxJ/qdGNKMKDo4x0jlrhwMhDVx+x0I3CjlwqW7SnGjk0VEuiB6vYI51kV1J6fQ8uCTs4IgS5Tv+y924FpPHiuOtdBvUJ1OhR1l+h9g7fK9UNJMjayjOe3UH0VK8m94Jf4/6few1j27vpBs83KR6p9SFssNDGD/cOEPweX1tV0/LkhOjeyp5Q2SYKwRvWXdO+Z/jSmtxSvOBjP7EexsdH2pWb5bjodEwGgJ3f1pcaQijRARvhSDl4CklxEoLwJLAwycAnpYhXnkDFSkC1qMw2hZLrSHEXN3xg9ADb9NI0l2rb1gekazX02KK6/8xSVKVTJx40N6XRkho5IxWvYHavAHDlopBm16mAI5duTxaOBQ6yZtFXtIonhzRn3JzBwbErCpzFh6s3sNFKtQRxaZAvdC+pBikcOx1KowEKaZiUrfBWi57p+7k09tHx/e3fP+Own17Of0zwPl1kglDCxJh48bisFuiW5iyVCzXIghLkJdugvpCqjgJHC7ueOxAAftLTMfDDuW8LwpDMMurtdyZ86N5yxZr64KmZVA0IbCCDlpcaHELoQM0rjRa5YpUlhUncvxxPhbKwrEYgPxrJvefM6rDirCUAIAKH0XJSjnm3JfVcS3QARQsLDNHfNeaoSEAfjpL1msqqQYPS/YUGERlnBa25QANtecPc64wNArSf8QBoOSJB3iO1BCqC9P5AerTC5a9nukWF7oVo4NNQdXo8wtAHizB22+d9z+0fp/4G3lGjGNaOFhxGhSWXMY5gI7/oBI9MtysTNRMU/bX/FxirpxG1CJWpMTwLA+luu3/HgKTMe0lK1+N/vRATkuOG1dpFDyeJQaGRaSF16kn5EDsKW0hYlEFBwKGglI/hX2V5BkZbVJG2AkwT66XltzDRdF6/ssZGucYqzIfr9t8e7IpLxGyVfSKbbRpNyq0kqsk+ac2mHWGKeeVGICMoqWt4Y9L+Ez9DbTj0N0Fg7H3Kb8ZFF1rHj+I1IvcZIcr/p3TOdUswRDOGA0vTdmU2sAISZsk7ofkkAvBb9TEd+oYOMI/wXvlGqAXaQPTlBK91NDmvAC244IYSgPwqeWPdypCkKizzEUGLYZimNPhZTRJ05euj5dwLlX27Jn768ffLwRvZOs7TJN0pmW81LTmiFSktQd+hA6etuPqPf/jiClwYWd5wSNiBsQYoqGkxpDDjYADoyAN0YYrdmFVuO6YVnD4FXkBvmt7MnBblTbD2iUYjpUDfw+GxlXpcUINBc9Br9Kh+6UnGSdXrEwxoTmgpta40FsXfKiMwLKSel/8E95A/W3J4yyYcJwUAzMroYfZql88pQPmxZKNoTTDiBEBpof0Wq4rxRlx7sXIC5OhM6EAA6h2z44PpSMMjZq00tHhplfMBYCYXU+yJX8dozWaTqOhRMNoJnsAgaJnSx79vVs276RYlJQWUgLkt9L3lEY1Qd1FByoG2j0ey7owsvYaosMXwZgsOM/eRrKOWbphlkzrouZ5ISm/P42qIallgUb7faNZ/2Xdk+Brk82+eev4dhIFqSQjhkdbnky73EiBhUpZPJV7fiPqA4RfuOXHcXyGpYDHR1Lef2Y71qpDBi10r2dqs33B3nXzZX+T3efMwV2+IZx4wABzfSj+kJE4paNJk0dmWo4xmbJhKAwGjfAQlT5a8TeZwGvNp3BZUGnHgJEcCFHa7OpMtlK34QTZPMQLI3aC+f6ZKFimoxp3/T04duotT/BbXYmvRIkYhtVOwO6rZOx6WKRplrALT8q7rmzZALWu2CYQ6zqP99R8z09CRoOU24He3QAe4qSmSN2PX5he4O9gz6oQ/EaqzgLTEnPphFxEtMMTrklYGCkh3WRdqgdKVUHmScZVCLIBa4UYzmmqyj2HS+cIhgdRnNK+HyAaKK5yRYxD1xFdsDkOuI15qdGkUWQ/vCf57J2BKYO80PuHDsgyGVFLoxkQZ4SpTkfgKOvPjUcKhuXjkoNrNqHVSKA4h1QMnr1nGGH255JwS6p/HJ9aLfk5GEXQyAGnkUbquaHPhWCS1/L5B7LnzYjTqfjJUqzYoMzu25hGYpFQZwwuej3Lvlvjk4LvSZo7mhInPlAruLDeu2c19YiUFgITrcvSGQDLbO8pwfBsH7+W1MCMpkyXp1QliKQ3ocDvGQmR09INm4WHV5DbTEnZxIwm5Ood0ZIQmE1WbCKUHyi7L24vi4gLJZeyodOIGq9I/xGEq93nUiJeNbE42+ftmQfiBY9WuPgAEr/uZ2EP+CT1jgjW007t1XswVsDWzHYOIfhu6cqEvP+0fY6kWlqJRT6Yzln/nhj1Ulr2wgBkUzB+NKS8CNI9Cw+qSyUnRnQvWeFyo+v7/oqetyUFoMp1Fkv9sevyINnwqhk9lCvPDiGEPBS6Yu96CWDqaCwhelYyicHmel1p1BOGAfE3ur5dVCcFB1Q8RARGaCeml+2mhwKQqRXQ7uamMC07QHYyaWoMlA4ZISpYMeL6WKSaRpoCyBdBaGi4fnpq/BB/I5EPGi+d034e0VBk6ngoRTAOdybjVLtn92/ctEmw+7zPUfO3nPgTH1YguWBlN7mDefMd+9z9n5AIwFgXK767hZRTMznC4+aY3G+OyHKkUU/cZ47Paaqbzm8ss4Ptsbrlz9GQ4T63+oZSEKAISTI2q6+a19++kkxQOADjc1FY6NPkkSM8Nelhxfp5pkc0tM93pXwkbjCRU5xKxUj5Pui4T/jnqN1kREC+mwRQ+BiR4CFIWbR2Y0/otKxVJ/ydNHBC8M1V++v+HJsQcmPRdBEbtWl32ylAfpG4h8MlDApOMZISgsft6HfTuPW9ctTLSW4tQeXHdiAheBT/FPdovitxzAWQMjYfcqKokc6NWiugRsg/p5UZMzqgfXfSwvdkRqaRoJ7ywIjOQ2Vb5g0iVlpl4dKajIiqp4i/1ZMmhmAmnmcJTNRUxgRgv/8FdqgGJsecUTmtpgvAOQzFnR3hofOnvaU03kahBqpZi+L5MwUvwTznaA2xQUy1nBiixtAiSFLfuDTuKBWS0E5FjzduwQmCrlPOtC6cB03TEPTaRpQ6RhUAA37rs8LkzBT2st8naCuPq7YoYr3PYIRoGjbXNEtj3k4pPeIUU8Au/owRDybkA9U4HCfQToQQQuMODNNzMteZCsO76ZzTxW+HxEyFEMLtf8Ks2xLWjtoVNVRpsd3FD5PU5cMNLYl3JwQfPtSXyHz8RHuHlBjzhoK6Ld1VTyd0VAwh9M6a5mc32BBCyONjLK/rnPWTtXMREZKxKnd49KjAGG5qNRSapEootHMGxGdKLw/Ssj6IHrUaaJ4DiE3C5nX0Gemc8WPAHPXSmzaD9j7pc3eOWVfu3wYex1VJSAl8qW0f7Nb81OKGlxv6z5FZlWX13fOPxtv0UQo9Eoe/ErjR4ERIFGcMpjSDJM+Pdkh2oFyplkjiB0qLDiixl1sHaXqTj6U9eYjRU/0Nw3wqToUr/ScwWSrJfC8q8WbxJ9PZsTYZkCWhrCbz8mQkSpuNGF2DUxksElDK2mdKv3RII8/fI41VexGEJHMN6IAQSS8trJeylDtT/yhhZfuCjcejVqwp9oSVQtzLdDxpUMZA8ohgKAUKK4RdhZiuHczLQs3fY7pvX1A+adaDprRy/WQSxRxK0edr3sOZT/A5rFXt8cYIrlJeMLJSvyt78SbsBeiT1Tf9deSz60hazFWx4ZSSqzom6hR8oEqqwUNPXzvYMp/Zwg+IkaxdMQ2Tu+3ju7lTpqknQJAPzwndNCzf0b6/R9TEtS2vlMbLiBrBg1UcUPWBTQRV/IyYjAGa1KoQt6F/gN4kfHGMCmiUhQCxqShE8iXpIl35voWN9l40TEx1z++0zsOSMCZB0fwtygg7ADhbCRAJ3tPGe4wukXOJUa1Fkf7m8f+vHgg34NJBMiNsWh8sestKVhhr98D7I0JAvhiNNDFVohiknMPHyHOR4I37mxhv9BRJ8BeCT1PESohhTPgqQ39dFcSMvXNifAMXNHMpPv+M86HNt4osnkoBqfSqYNIYoeomOyqcF3rORaQx/LFyX+KwRrqDfvzioifnjm8EHIJP7/U2/MmNl22y58beOu6dxP4ClaARfTojyh/jeilCt8aqbYdpetaO956UTuHga6PDG2vKu2RfOBUjOr9q5wYH9sHcsq82mAHiEQm2jFXe85g1hPQwzvV8tyO3btXQYKpKHfvld9Bt4Clbf92T/jrev7LrToXmHRDsYj/pnJGMB9ZS9wP0mE43eGgJS2UTQ4sObLuarMEnUlo7b4FqfAnhPHlR00ly2Wce1RLjTa+U6u8gFQYFq5sFae5nEnd2nm6Bi0ZaXABhT1KkEEIYrh6/YEPwhhO/K8bZAYNz9U2/SqdgrsyP/O7UvmbWnYvYC/+B64MiqSoaJCOmn2SjYimotrgodjGRhbDQhWHxsdL+ArKs8VkTeiGFEEL+UKwESFpJKDE3VOAjoXTIpaS0GHondkZbM8wc/XtySqh2R+eDHbNoQPFIvK53RtbViLrFf24EvcOojka5qAxUL7ByjWnDmEODMRnLy5jx/v5jLg1H3MRoyb93xxIsm58zEPH8jVv+Ola6akXeIghxlFy/sWgafiPb9IQQQusieXj8x3y/xOP/P4TgoqUKEKdeJIZQWfG1wpZC/ayRD0bS+4h0xN4557DgLae7dtONK+aolvN+06g0bAMb3FerD/eUvWBePb5qRdNW7plS8MC6lw1AN+IclRRGbOW927uGCjpEzWMEiNRdH1ZZOrlUYqXcqEqq30MaQgBn00P7RfXb/tzgBfvlNHKmO36WVE6bVp2LwpoOAObtSSO48zYSlbvHl7uFEMKskhCeDgKapHdVk7eI4Tl8QqJhMAjVW56umZE2Q+VHec9fSMt3sK4Ei0xv+PdExmZGauiJhRBC414yz4/LmyNSo8SDDOvWpc8WcVLaA4fhZk5eNvJbFHH4CmJZxJh3KULxyl0FV0rHZGf8SWWhw6JITJfXpvG7eDbUZINEU2HEVBw8mRwJYkQ01taHxylRzzQuJm46jZvJaWri7fQ5Rudswu1LzokpLXXC5nBwcoPkCCvHXwHTDvTu5r2/roh012ht8QweznUOoaacuGkdXJVoHcDIaaSuFN2opykUJQM6nXhHinmkk58X6g5iSgYyZ/soueeGHotCgd5gtuX3ueIZ+0EThNmv3/b58XkHEbC618EReXjEOJ7SAML9Y7hb3FIjoow+Es8Tgjd86bjpe6JzmFa56KqiZbyX3j3+fj9MfgiGB18uXCJU9q4csKHodaQrnvGhlQg5xRwUf+2sD+EN+vZrC8KHMUJYcL4sEZ5X2IzT/r912V0Wcl11FXjSDskky1B4CH6BKXp97TV7I3e+5N9c7tAmLw0vtZ7ZyFUrxJJo9FX4PpXnh0Do+pYw5oJRroyeXkqOSEUyWPPjU79rJ5ff9T/u4Co6FN+xc53Ti4fQrG7ZXB+sY87e9Yqz6zp7C76E0QGNWGJTTKNMZyg+LwZVksemjScJsNUIjyPEFGVDI8dFmsR4q9+0OdA/IRt1CqfVbMnmQPVNGwQFBHfPhURhQ9LeebCD7yZbUApavtcxbTy96T2tGnoY9S6gokbSFLk0DzkhZamYpgrmXE7blCyAMCJDg1LxMZzPWvnngM8SgWeEgN91+o98ic+dLwMsLF4/ezFxv4o1peTwChCeTn9RWMs5ddKIIonHyZ/0FvBoD6X50C3PXfQ8J6/ehUUsCmQGZ2rSE+gAa+5xrK1MuB4V5M8U11BK7gdkEWHE8p7X97vPJONRWX6exFMVgg+4fGitJZynpVkIItaXqABFsSFlM5McfHnTNDPDx4Oe9HICP8F04hVWnucGApTFbXwUwT8HK2AU58Dy3NEyOzdrlALfKzqVEZlYWm8DqxvI/NpWsien3A4VIN3rt72W2X/a3KyV6zZrClp1h7YTvU3/slfetIU5XLNB1dJSUrFrSSqpC9SYIylYbgTPvLd43qzbE/ETtYEnFYqmCFnarx6mq8zCIh8IdsgpAxlPTX/ZZ/x4uuo42WQLPZ7z9+GGlIYl4nPomiNn1jxWUoOIMbA5ioVyhIJiWJD/Kw+QZ6wrfMte6OHYhyRK6FI9WdaIJbA/IH/TVIfToaJtqUMdBlAM2NQ00YKJM47llevGSknD5jDFx4jA/c97YBv3KOWHIt6ERo4S2jFCpcYQdZquRzq5hRSnhQZPTrijiiu2mOoVW/y9iVT91pDSkq68EXTN/ECc6zX73Az7lxbSMCORF4wN56mC8CmufY5E9In50giPS4Vxfcj6ZnWXgvzTJNXg4U27AlBykwsPU97xP274DN5+K9ndKRfZWMufy+dsFKp14bjp2MgWtgTsdsHuyXLq1Vf9F+x8mpgKCdOhFJZlfqqjXd8uWWwjV67tI2BLy2b0HR6Yebt/zY/32us0BOT+KG0ffsIrbeaWu6cRTSr4d0GcjqYvu+dsjJ1i1ioGpK00b+t6sEienGFkdtKdLF5Gy2HUHO2BjEURm7OWU3MDU0NjDOeBpJRjLcmGKMkaDRISFsaqGfCxGPcVPqcdmQk6ZMqGFZkh+PSfRmeIm4ul9RBtmpeBj9nw1zXfAmmgYISKrJpEdah6rEzlXantuHPX9y2mHnNwSC532iZCeUtauxBPJfO5yDmDzS9WYp+AkVkUOfUN25x3P2F6RSODjlxRCf9o8EiT2/ZFGw9GGDQS5MC2StJOUDib08p4soO9GjxcL2UxBEoALRMCoA7BaGxb7rjr96tCzSZEp2cv/fKKD4e10E+pfej1/dIK9pO6v/+M0UfAP9JY1mNtjGDIxD6HaznvdT9x3diVqoCQWRijmuZkI++0Pnoqj4zhifWgwQ9fAq329o8JUIqeXUl2QYgL9YlR0HtgM3m26Xf7UskebHjWRzfIbzeFl9e6Is+IHiZRW6NEpGxNxl6svmZ/x/gJhskGVZ/J4Ib9ltx9ry3YrE65C/jyY0yv+G7iebQxHim99RknwP4w6hLvv8RFL2PMFIZmT3BuBMO02E/e5D8u4bM3bwITJY0ty+CSUYZjeoB6jhspS93TmGM1z861SgUV4+Vg2kqZnLHpxrhrEsC25R0JcZ9EGkGjREjNsJ9VCCHkEXWZNnBOnp/hb2Ug5ntiekvTfRN4mEXhY9iom8bdKnigA4HKhX1UJskGOkspnfZFBNgkNXwPI7vUSp4HH5fsfBI4TVSTMvUZgn8nigubwzCoyDzi5sn1QTqOEEKYlUAjIPfnmDJ6rl24uZYqe5L2x7VaVs9zvj2Fv44Ziqgo8A8M0FrT5t63r1901507Y9bWXIymOVI9ClqOUAZPLOysINXNm3B2hJ+LS1ANzoCxI69U74S/v2sZIePosYl2GFtXuEcauadKqsFDThf12FlB0j2ZAtob48f2/VOPyrB2cZ2CueoXLMbWPRRAcwNlfj3pzot0l+u7oxZh155Dw+YD9DRhWD6SydS6ApCW8uUxdSoN40Yd4JO27fljuU2A7rSbcFoTSEZ/2PBVWZJJBqiAZheRgPGjljtxXmqUNe6CvLDuX0Bvk64BjKbG4rmzjJJ0ztuxluj3Tyd30Cb2J074Z8fOMBTbr460vr5zpzSIzZEQDyMw2p7CNfdUwDSiTcQutZ8Ubqpde8+jNXUHsZZkDy+fN+XC9LZy7Yyh3MbGshUAACAASURBVBWD5PizugRoSrQNIM+7A+9GTpCbLq96q3Jyy3YMps8KUjjhGpxqny1c6rARAiKlkTNaXjwngP0GXZpUIlrEp8VwOpgemh6OqNbxGvaekmjaIw4NAcyaInQVZ6IjaQ/3N9wp9/4YjYhFwWGgFBr+JB39Bzs2CE9dvOeue+NlUzy5VT+QrX2bl1FdCF77wM6U7Mco6Nel4DXbgvFfes8bBdsvAL5CagxpHup6+snad1gojKMaPC7N+2ExLXMjPbwmv5whQij70QWdyfw2CdXi5dP6LJ31FkOfSk9AgX3w8lTu+Z8zBNfDFFVVsXLqLSDs5fGrt1DaDmXpKruCR5erscI8rirt0GdDI3su9dqZ19YGp75XVzJOg7lT5VAgilQnF40XgsVUIbjeUsvyrkvJ6UyGQqcY1sbtR28K91GJK3EGc7jmmB1mRYaaG5/mzzn/XGNcrYBiWknJLBNwRnHm2+RoAaN8ygnF6I8jWEwh/1Oa4Yibf0eMBJR2zwm8LAuYHmswSik8cNgliV6Sfv9MxedBtoc2yOO+OAH87nHyJk9w60Q5WaBfiWWI6Q+S4S0gD4/jF+JeKY86OIlNduAn9NrLdlwR7hemtGhAxHE6yc/oUuowcpRChER42vaA36eGEteqW8Mx7Cv0pxoTMHiGKIopyMLln5Wat1aGb9tEmp/2mwgjShGKZVS3cF72T/mH5Pw7eEJS2DD8yW2nQqeuftuf42/rIk2tDg0dCZJ2/jBJNXjowTdu+pvSC3HcEAMBvJ61mNXBrgeZFSo2eyd9ewGTkZRMN+zFTar+BfdapkWGazIxOLlQ+ZHb85EghycQQ8bhCThhZIx7IMyre4PcLXxtFkkDaIL+Kc1v+y9onyPXjihtZt20izuULMkilUgytUpievyxgoppAM0VSBszsEyYCnMkZhc+APz+IxLX/ZrjonQG7A0ndhs3MAW4s2G3Azyqh0MMXSVZKfG7Y33uILGOyZ1kJ8BtNPycGDyuTFgMDeI1p0rxACmt2dofSfg+cD3KzsKoTlp5vGu3IgjT1sh+6LyfrCp5z1i7ABhDUmDq3kdah2pGDD4Iq+xHJSSPI6+N9mcjHmsmGxjLupVzhWkP1+hZUq06bpQkkH/3jG6kdhwrpyZ/jExFVp3RUNJI0KN2u18/ZT+aBJgh+Dkwmfh5T2cq/5rP6/WBaY1QLDPu+ev4PnWMOXbKtDwBZIIpRB0rBgfUaHVVWo5l3V/negR+AB8g1eDZeRFfXvWDXt6wJ5gxlSSK4SwoEXtCXsCmaPV1myXKeVGA5avI9jlYmPvawBEgsHGbMz54AcNlJMj2+RLwPfA2dYOjl7P5Hf92tj9lEyrWeoP4ofdYKeUfkWWVTG+F4BtuailoFU3uGHUhPiuEEJavo8modLd1/b5Qzj5NsUfUGKKBVZXGeyRrI/K/cS95I/y4hJtnGlEkiye0hxyjRLFuxAlVLqoYON/yErEk4LjouF4kRE9jSEHR+LMgIFrncPJjGjEis6t8Nw2qwhm/s6wDv3DQtTWRFwyg4xWRx2cbGxqAsWaI0C0z1TsolsgvCdknIs0zhChLAtz2Dof/bgcux7wfrovRxGjY4gV4XCuCKaKBSljJDVGByRwb3i8Ez5h/8huMqiaPterBPDZZzoGOGCR8J2Wh7mCkSSvuaCSQjTjGEYN9dDaWyCbSTEOAmw+GXh9zvQxbXjEUnrBzhbL3tHKsfqbR1JTI6Z491/JbYphiX+J4hBBCBZh/RixVN/LdKNbKFYJg3msHB+fYf1gGD/Pg+WXpywHjguyRc2l5z5K6qbzgiIBj7BClojeuWqjEKpeT2w2QdCkEz+1DICQBpSGEMEQZhEZgggupIxIkpcajFVtFd74k5a3wUGKskwUyEJNcUBcDLHdZRKxyUmuX1VdMb1W3hc9oJTnEyXs6EsKuH6s8ysvZSDQEXy2mqTAHSsRw99cXD8PDzZObeCz8DcUfZwjGoRrwueM3Z+0UntYfilVVVDwzaUrJ9hFpbKUxskzcp3nD7jEUwjx+n46BMxzFSyWzbKNiO9BOVyoLc9z8JOqJd+MYcnV94x41WVjdMSqOxJHLIb02c21E/O1ZmaQVO1xXTvErp6m7/+JZPPnR8ceDTXkneA9swhuCbw9AEsIQfFd56h91uIj90RRzOSFyqpxprgloSmWyGti+VQrup1FVOuWC9ypQ/4N7rrLqF//6klkQ92RN5AvJWQju2YxYqjPiup6vuVMu6jXTrgGkq4CtpZxLjOKUPOVemGD+0FnrS29C7qnqDKZJusGDhTbd93dtnjcXswH+gN4oGasR5fxK3lyyXWI0gUXb8QbDmXX7rqnkAOiVPRj4FVAt2+i1ijbzBkKCRgKoyUBd7gTvSlJfE7bakJ5enAiRgJbHQ+Y38BnxUBhZUTr0zim759J70tTuLCpeXPRAUmbn0RlaFDNbRnBD1i61BO5qSaprRCtWvfOkYMlXdz4AZ/hHJAQZU8HGWie4TVZu4kr0/RgyhOwYYbXvLha8UvFPE7A/Gqnh/XWDcC0dZEmwxJyswKo4i2ivEWtkiGsLJT9nh1P7QrId50viidJi0G7ydMgwdmUpsSfh5rs97+5T18yF/2sG7F15B9VBKUR2MS6lIg1C+38FwJfhOes8WwSJEjAfGgWnjLU3HElL1/05GnnE6ehGyrFXA56cUM7wlEdkP6jWRSkwWbFztTv+HH83udaKbQ0p4rghTiegHFwTmtI67CWXJZHepf+6B+HPT2ENIjBRuu8XOIHx2gOP8692z58jM7IrbRf9x3PKKs7oNO9fve+v617EM95Lnmcq6cSDuE9xXToJI1pz+46t5LOXPZdFC+G4asWH2Pa7ZoSs1Mzo0LL0w77dIy8ecRVkFvW6INYP7P4u8qHVHkh3xRhn4clFLby5Fa/ACwc2HrrZc5HOan6SRyAbzK/b80c3hOYe/Cx9abzHxb0vTe2Y7+V1B1KaX9tG5ZCUrLt74qtX3/LXHVy1MVDiRHpjkaD2Gb1yXBofAIz2UQmxay4gIPsQNzOtzmFEQNO3NDYZCtaN1OE/FPOxiugJwKFTiYIwTBzrWM4NuKvGhB07bISGnRmh0m7v2AwH0hB4XjddcIiqk5I28ARecCZecA4GSa5FPI+7zD1Ha+yfgxtLWTcFlNJzwx9LFRXbQpQlCjiGkzQmqakYCoxwaGRkEcRXJeE3rXr9wC7fanwzQqfA7FzCOtM1wbRS54JEl2Bgr962cwfCdzbFBq+O3/p37fm1iCeJC0YjfmP0myvV/R5yZsUc+9N1YHjyft5fXjNL737ZezvNkj30zZqf7Aw4lN+2ua0REkZ+Y5QUGC49x/XOSk51hPowmuK6BdFpGKkrb/nxHgHW8aFFeErwXMbCQjpCD488cCi3b3nz/LknDIZNqvYQhF0ZEZ7evv+uK7jHewe+ZrNRtheshlIBxorrGi3IP1Zq5A4kdL1s98+9ZyM7FRI08pio1+5TE2Lxo3Rwgh4pGgXh4tYOtg4gFqsWwjOykkCAtNzEDp5IMZpSGpjzu9Qoi5XqQxhBmlbYc2vxlDtTnlOsfgWT8s+Z4rbgwav3kxSdUeU7chgImW8MSvL9C8v3bJg8vjTSNLpEY6sGgP5gw89tlwqT8YmaNgGLUn1Vh9ImkVq3K4YROU2G+t1IA7FkWFioI7xPZbStwMs+WJJBgGJm0UOMDZrrWJb+RLiPjp4pLbC5eEvCg/DLx/+mELyzNwneQlj/vh3vPp+8XlgaPhId6Zn1k42mQ1CIVLx/7krWdd63L9txDDpAIxXG93BdLsQ81V5XBwP7QZ/ZMHrp37n5tLvu+ZPGSXG75Sf0BItu8wnPjri1bfvv+Gl0ORAamKR0cAhxri333Uzf4nOxXl0kJVTsI9rfEPrQPS0GMh3oD4t4kNwZeQlJE3DcRBXVXDwoXnfY8lGLU0hVrVXsBWxc9aNwY98SiYW81wYkCxsJRqhWNcV5WLaXqi+xcMdWyvCE7OhosjYnalx7ejl8j2CVXKdEf/vLZy1h/e6bZv5rk0B6ihpBchNIKgsYrWldYgm8v66HDr8l8URzqApjSb/2V6ORpnn4tHAzc+WuRUdhAbU7xEU6tPyU+fIYf8zxZJYh+PdMIHGsPQU8I43OMIzO5yrtCxEZ5k0sCgVDIFYKjT/JwaJzj5w3CtAk3o5U+SGE0EV1FIk5NfU13bdzpQN//+FJM6hGKyDVVFgYhm6l5ENl726b8zZbFQ8BEaQIx7oRpkX6uJFzeNQIZiRx6d3FA/J7wC7Wsjh35BNypewhhMOrdlwUXAfT+053yFjT01djhTg3GuyaIiTLNbmuQghurtQFkM3qruYNe4G908FfByb5jSWvaFl+Xo5svv0Hz/62u+673QtHx39w/5o7F522yFC1KKBlrLnZtj0wq+dCCCEHmggtuGCqW7GkM3wd07B6HSObWsHK9eMIVCWKw4igpqnTJL0sHaWdEyGBK5PwD0YBu7yGEMKDrsW9psKhs3Vg5xonTOmVhPH0zLKtjlrBjxDvT6BXCI6jyr3USACIbHgaSSUIgV4zVH7MBOvDPL5arcSoCB+fMwiZV80JDoh54bx45kyZxbySCwj785ZK7Y5hbd70Yzxaxj0AfGaaLQSvjEpd/yCMemnTOYKuXdnjgzRX9+MRLjQqx7KEZn0OW94XPKFYGylWMLDTtkR46FXHjZVjHjzE+7jRK9VqMebuNVzPv/nOY2B3GnqSip6BZ0QdlU7XtFsDaeqxgJvHSCuPpJ+Li9pC4SrDL2k0crJ4pvi+SMHlbPQLpuXY+8QGEePXYUseFFJUpG0Bm6TuPbN4QH7OWWLL1GFxOB2pjnJRcNFhnkgT/y84IGLN1PCkQcLeXFqWnoYvocET6/KN1BUrmQriPA7O2CTYrPqNgqmrBvLUY1mArxyaFaVszTRy7t31oZUcMHA5lKUX3vSLwlVsyoQm47tGS9fQbaB1Cca8rAnuj0pB0AOVQQ7pW2VTpiOhzNxpkmrwEDTUrgh3DcF4UF61de/mHX7T3v7Sp3wupogKpbfuGAy7ueTLTi4s22za6vlZXi2aNrt62ocV3rlv353vJEc3ZmgMWJCWDuMNm0AsTZ2NkxVPdUc8epR1F4RkbbuDDr8dNBkVQBsNKn35zRv2fVqy7rhi8FiKsaHR0brs3zUrrMgBpAyaXPR6zpHXyfx0BgDWb0ciZYsgrDSh56LtAKgM1BAgGZ2L9oQQJpgrjh08hVpdK8R4rasTGCZvQBpB6qMtRKwvGvmt8smGuPuufekbhNLagqSiT26YniAGcCBlOaycigQbFzrH4840Gsbq0xNlH1r44tW3jo6/efeiO9e/YTveZAUA0Ft+zrr0Ymwc7dgxKEtBAR2VueCwFkGI4UkivQwhhBl/oowFjZC2H2oXDXJNKVf9TdZeAsbmCUmLwSGhIaMtVeg8xPhjsH0pfoV/k6BWcW117I+ajnp23Rhli1Dcv779/zH3Jj+SZdmZ37V5Hnx2j3nKyImZlTWBVU1KJAtskg1IG6pXDaG3+nME7bTURhAgCeiNwIXQQrOparKbJVYVi5Vzxhzu4bPbPA+9yC4/v/M9f68ihUSF3ZVFmPmzZ/fde+4ZvvN9H7nPnQ0tEtrY9h4DcWelhs9YEoQ/RcCuuDMkl1zjQQiecy8j4Pr+Dv4d7zO59aLEj/w+cu9oQJYDLi8iWJ4wEh0e2pckQr4UavDDno+0Kt+x1apCZ9W6PZCdzXgpXZa0el1/2jdQ44/w/MCQssQ6bwqRk6Oh9989QzaCnyNTZQjekCoQjhul+txfv1VDqNAgjaiAWUfxDs8A9U1tfXWgUtK3C/FgHlw+dH5C8IuNf6eLkIGICpyy3FV96dcSuyFcC3yM6vcbHXT0t8Es+krKe5V4h8E9P3nOBDmyW0WxWTQoU8ECEBDq5ESkJZsGK5L9cZpe4rwST4boSmv1hXOU1t73Xhmzwpq5aRbMLuyfm3WcSlbV0WHIPBb53diqEQkN/N11UZwczi0Im0oWaoG9moYt0NQ7y8+6DpgBI5WA2iDuq1XcE1qC+s3o3fZrioy4GRVAhhlU4tY+ykJlsMyrg02pl+oLOYzR9u4CFbkGy7IRJmc4NbpfsghiyF2jWaISuHHmEsUQ0zrGoiW+NYQQhuDoKQmFyxJl35R4Gr0h6Fd4fvtbdKVWdqaFEEL5JRwNQX8w8KLtimQ9cUYptQkD5QIEWRkIhuD3hGq2JY1Eh4eHVvmZ/+jgoa0aOhYLKVttVW0WXo79Ncix8erQrOXujjc8m7gGs0IhhHB2YBZAs0uzBctMAMOW/ENMtWxxDW+KMwSjSqHSoURaCxjOpfABTUGQpsbM6Q1Rt0tETOkVR5hIsdD0+sRY0BlSZ2UEx067fgjkKx/Ht20yY6CgZRrF0Xo8sJNevWYuVmKQtwWbXzNmFM+LdC+xS0uSWHHZLgWDeqV2yephCUd4pTAcf49kFQioTdKx4f0qhsdFjlLqJl/ISLhEThDB5oCVm4ltyQB7N93w+9aVBoHZmOs84nk25BQeY5OUij6FN+ni8MA1VLbGyQwoUTTa+/metq8753MFu7R4YnKuq8/9vQ72gOkSLi7u9Zk4GnSAfANIPLZsLPwxZO1lp1ukuwpzHRHMps6WBnt4fl2D2ARBZzi19HrJG1p2CeYSkOsN/F1v7CdrOLYbKQjmjfAJUjzMFWNIG5RELSBZLq5TBnWK11rAuT1/R3CFuMYQuFKdDmcbi/E2Tkeiw9N5CyCqm/70uQE21O7IJp3lpxBCWC+YESEKPYQQ3lkzd/1O3VrtNHVNA/jBpnf/X5bs+sdd/wSGMEoBpE6ZgXR0AJBdrfkUyRhOGj3m/KYvu41b9l25U7FsWDPde+IobYDlOcWD0H+O3QS6ERlFqlSBo/HGItRl7DIBsiroRFHQM8IiGqOiHYKPlooXUsYpX10ejMOivMmRIa4NmQ7N6jEFE8nwUHZCnCFyixALoOljOisREDvlPwAU1czgGB0RPHxDCCGLCFCBi/ws16JqGRHjNFFnBUESM706bq1ZevQg44ETnRswiH2/aEfoonS4K5nHChouRku/4BhxayTdP0VJAO3X2h2UxHs/2gKTM1q2FcjJoeWHVRh0LsmSPLjhTym1uxw1tIqffse/l4PjFAGdYzCrWjyWzCmcHDqhisVhZTdSRsERqLgRZjeZhVLCPDIo39sRsBZGGynx476HcbR65lVq1nMJqMXObc+qeDi2H5tFIDGvSZaoDZLeU+EbgtNK0tEQROwTt6Xkgj477d/iWuKZoarq9Wd2kfN3v6UMD/eqpnRJFEiv9XzkO7FGIBHLS4fV6cge5GbRVtP52F/jR1vPLl/vD31Ol05Uo+ydEKbKpwAjz0XpnHIVE9Hxqv8/ZsVbf2zXX54Kpfe2PanUsTecXnPJvRUyAJ0VEEUORDdIAXocZZAyqTNEnSpH376dALLV6IVwCPwWrb92cQBJdSBs/Noc0+5tbYPEazgH2pq/CsMx9SIrUhAStAFS6JpVoNOkBtwztvK79E7iSyCMsrkedO3t/QzqzD+QLio8E+XKiANvaiTH55cRcCW1lLTRYQayQSqWl/PeE2iTM0scC2a2nHCkdJwxyNComozPWh6YoQssRVyeJh3YkJcgrsr2/oi+ELIVqygeymljECTqNY4gLlKWpz2SAKEH0Hbt2dWvQwjh4j0+c4UE4H5xX1oqzsEHmXvePnc4swPx6+tf7ZRpZmKOzrWW9NXX0SX4Rc88x3+2+8R97u+O7to10gITKQmmgd+NfeZA+IIr5W9RAD0Z5Lt3JRBCcEWBaz27OI9arXDEqwyoxU7SyamKAGnSSHR4Ks/tWwZC8z+GM0EpiOncT979dUtNvEj7Ij+dnJyGwRivRmZVJ3Ia09lqfeI5gOZ1glbiQbNUPs8XfbQ5/At7Ikuk3jeF4+DiVxaO6yJnND6RjcKU/RgYpLSoCXOTagRY27f5P39bvRX7sc1H8dIVLF9mRQeLxqgKwTgHUgv+cFVK9Vc/tv9QTSEaCNdaunpNWs4YMKLUjjUOtuOG4I19QXhAWN4j5mG04/dH/RFwAtL6mnblLnutAnzH34sHhbs1Jj8tToR0tBmffYjgBFDSapT9nqsVrs740AEJIXjZF0m9Z9Gan5QNo3Ol3TAnCMiUILJUs3scTGxCUjLH/OHqrDhNJxDl6Xqhg6kBzSoMluAcO7hKFiCGi/CFxSiuh+AVtWkfuvelE5SYtASNJqfoLlvAcfloRy33kmDBXDkNx5yCogvA8JxIA04zb4vnftUacBpSK6Y+ZXcogTe6FHKSYKA6exFn9kwSAK68Kt10PKY1gOK/uZ41E8SMj9oSXoNVjf51/zk6Qywh/raR6PDUn9uvG9z0Ezsq2p3dWLNwfjCVevwYbeNz/3U91Cw7KJBeK/tQMYOVNxPCk42KnZ6Zd/wGICnhSQfGS7x/Oh2VglDgY5EMW7aLtLNkBtruyid+DuZ4qMuG91aISSoigu0Ix810ZtcsHUoqsQ76fSkzsTrYvgu+ENnozBJo23gRoGsanMg16Nip3AGjfblHxy5M7EU8g/obGyTvIzBSNWcYzU8kUmQEOxRQI7MpNLB6WBKEqaU/GmpGtiqp4v5GSzEJpQNiKljCyMkhwFJYXoQMmTE+bftaWB54HPJsTZXsiFFq3VvfadpszaJgP0Z19PogOf1cEKblrC1Ulu1D8OWu3DlToAogt9cRRlsEYe5gVHAzJSiUCX4FBtcfHY2ckDDyoBvLo+S0MTsQgncg2ABSOvDXZzm4c18cjfOr501xgi77IBlLJ3mRkMlj40FaHHGqm+9UfK3nZd+qF+9ULW1/JnWfM1RR1BFvFM0IK4XLDAF7B6KjQtcT+zxD8DZeZSdYviN+S88JbrOCZJCov0aGcS39c/7LAnJPGokOT+86oqT4JqowAkOp6nw8aJibdiC87gcte8BsmVsIO2UdzKuqaEzdHQK2QvAaJDOUqioVH0EOwN/TlLLYaZdkKPYwjv/JF2eXYNcUShDXVpoWQDMxQuUSPIG+ZmrwUluU8bMrojDeuYU0ZgLGZqyHMkYeaPlFPn4h++jOv+eiAfnuzBgOLbrANBpfhUEQnwNhxifWIuykjNLVGeIzYiq4fBBfL9dBzIojdRTjS0dTcTqLhJZ4rkUHpo6oFdpLDRDo8MxG8WAtkhBGNIQoVDyUxcjbYnZXfD7ymDzrea+VB0Y577300dTuuVdOkIXAvGrGwDMD22vNgM7RNRkRP12B4YhPYT6jJIz2Wh0NJ9qpuA5ck9fXUgmzERFy2f7Vn0tqS9fsQ2KmHtlYQgAmsq+WwNysF4RgFyoCx4gYx2JcNkDS++IT76Sv7xlb4qen/ozKkTsPpbWZrNnKMzT7aDcdECWadSHudLCLgEAwPC4IEIeKZwrV0pNKXwryTxqJDg+N6qwiD25gq/fVCwvz8gJuJtV1q+9/XRmYFTo8+y2P03mJnTOXev9kaPfBbqsQQjhas3/nkJHqdkScdNtccnWobq1bSDGs2/XOaj4qnbfNGGvEzUNhIZ1qzaY9yd7PLYSoCQbGEWe9EuzMzfhDjdkZgmDVqBYu4Fk3/DXInuqIAeU+RhATbTz1BxxZmdU5UFXi3wyVv1iFUXKARGxI7UQgD5H8PGdwy36eNCq+/BsRVKRToxgbKiS4tSjkeY4PKEEXRx0eZpfoTCgXEZ9zX+gqXGQqZ/hR226GbbzqNGULCGhk3yrvz2+GYkdoF0h/EYLvMNUWYgZJS5KVyufcwavZmcXVzkv/utZS7OUqqqXTmWA2Wx0SZn3V+XNrQN4bg3W+um//r3is3p14cU92TfLAXUq3WBzxZwghTEE8S86fEPxv4/6OUBHg675s+5N6t2aewQYM9C/aN93nyMic2/MeyauB3cj1hjdKnwMXlEWX1ljIdkk0S1HUEDyrsTrfLMGTKiMrmd8BnEXtOnTXAAGikrc6mSRtnkkYiQ6PS9NJXXmSNgPG2qm2jT9q2dMfnHtHY4l25xpa7TpDAXOBiLDdErcb/DRaLlqSzdUZWEnFIVpjGUwHWwDz0rUxQuZGU9ekIW/JwVVAFqqFRRLpmoFRSQt1JZ2c0pmAKxEdcjPXn/nn1IbshGaJ2ndtc9PTnkkLrpvWSP07Hu9CgHPjic3/+TvfQBXudzT4bBekNxDMFbN6SXgCLSVFDoL/MiKyDeyqUx+JdXbiP8Tw0DCrQrd7luIo8Z7jAOch+Gh5Ll2Hi5ZZrEzT7zkqPpM5diBdVK5lfRCfJfLtslJyImhZbNf5wGzNUDi+qHuXhj5SBNsBJ0dblNlRucAcR/SLUDqYJTQvvKnBqNxpHCl1G3ndJCHnwLEKwn/N30x6gLFK88Cecr9opsnhjORAZwZDsxvV53gP/gn5eULwagMUzw4hhFzNFsiCTQkykdR8WySUOPXvCFSeI8Ma0R0jIalkbZ1avWSu4ziiNFNWf4wsuXSxxQkmaxMMMY31p6/PPp7o8DS/tItOKyrOd/VE9575stUYjKqZnv+64dJOgtw2WjvFwNLJWUqpJ3dm/04LQdN4k3liGDZRXe7DmCkJGokO19bsaZyf+pC4+BSAY/E4ie9ICUNzf2x/R72e1EtvYB2pW8pfg04OU4kheO6dVHwg6g6ucUNq48gSMWrPDuMzBuOmvwYzFDo/KTBlnr0PRzre93xjwzEoY6o1de06QbTUg6HCos7AYB9oJBrHoB1CCMUT++wwoeyWQ2aIon0h+I6LhaSo6Dg78Kn8zMl1lIQEw8OSwFrNe3N5OB7sjmKDQgghjIdEVwrDMY07DG5OAjceGASDhuB5Sz7Z98jwLER/l31Ir6hFxTXSkoljdOv5jKR8icNbSzCrMIi1KILsXg9A5wypnh7MqdomZhVoB7XlO38OmRAlxSMPGGyRziczxWaVFAAAIABJREFUDFqSy0HuR20YyV8pVtt85K9/COmEggTNhHgcN21CHrd95vHDTStbPfr0rntv408N0PKs7ZuE6GyRO6/0wi9aOjU6jyQirAjZqutcw9xlpAeBGeIIbxzsFTPX3Tt+HqtP7LtHQniaNBIdHk8k59+LI9SKgIs+tZ2c0W9DGDyomeVUMUFHXnjhwQtzHKyTYx9Ks/031QOzq2yUrW0zdBWp1TPqq6N7pHzdf+7lwiYof+CjTX3gHGyJT2ERCtzJR6natooSlDItE3je37M56O9KKyLbSbUdHI+UshljydQ0H9mCad33c8DMkBo0ymE45ffE1flmBsHIjlckglfA55Q3CVkGjYK555jGVYwNQcwRB5KOLaLSrPCguK4tWW9cY3mJrhix8SCJ6FQh65mRICZNQk/JrGxCY+gUHFxKGVFFp1RHSt25F/YDlBaAgyXx7tRvrAwmslbz2D7qfw0KEDgVh4oHqHK+ECbg2LEr8ferArCrMOYorzLoIelpCP45RDKK+MnKueKddmZqZE1R5Vv2XJwunTIyEw+iTM4VlNPaD/13VwDSJQPD6QdiGJiFEgeeDvZ/ODBH5sd7T93n/v7o9uVrln1CCOFJy5wjxbQSP0pmcgWJL0ADoI5pHiV37SEgASCzRppFo9Oq5Sj3fQwa5SzoPIScy8XrSxAlHinUgFIyOhrZCviNJjX/gBm5zHWCqDDeNaMxESVygqKngoEhY6sag/ENqtWB2Euu3wa2aL3so805NiZ1r9ISVnPD6iFGvIwkRfyixHtKXc5UefVEsDNwGBSbw5IWwccR4U+e3WpvqWBOXR95nnRyIh0pnPIEwWd2aWVHqxfNciNzg0ccfcyntoOTZVY6R53TQG4fpVbnaDyRjOg9GH52Akk5h/cccbxIMSBYBnbKuMyjPC7iYxQbp9EtxxboKi7GmBC5RvvE9mO+JkzIDeD30KKujiNb2xuilv7FCXQAhRV3ABABnUptt6ZzqDw8zhmFoxAR3cRPiyt5vslBzhsXzChIG/a+8FIycjGCtCH4dbp0GWbJ1rG7SLBaa5/bzXRvo6QiXZLsQCT/Twg+o6T7hVxJ65+hvCXipKlM/AMkjrXXtx9wNvY1PVYh5qIa4M4huf4CTnoG+LfuHbkPnD1lUYWnujyd+RD8+l6U0F1Z9Y4XxUlzz+JL6ey602437W593ZHo8HDjrX0mXRYAmtIx0q4TetNpOYynUI4tguFYl8TBZ7bSlP+mPTCDGDFmMPZOGbrnH0Bq0x7+q44Pw0h2xnNL2wFT2fhTnJHdUgjYHOwFYogaXez8zO5x3PDGgiDmsQCO2WLOQ0wZmenwVPcFxFa92qBp5ioDnI7iENJgIVMAmucYsn+o3tcqDK7nOELGr9+0l5OGHnT2WiNRZoYmcGR1rvkcute1ln41KDBSxsRtaaQ7blz9uRDiQZk6B5MLMLC/5ZkZmantDP3p9CxnXhS7odYbPlQ8wcaaDgXDA/bYOQKmyHPCj7tZ8eLGRaTmvjj3KW4yqzvOLDVBtXi7wCydOpUcLLskUf2/qVF/ZK+p7K7rhpE4AcYhhFAFDqN3VxTAUTphQBDtVo13PNnY4Zjj5byicGlROLIcV5Ic9sxAnP0eg0x/DZa+hiKpwoYWklm2dvz+IMwi3/JnwWDL1qWWikdoHJgD3yoICddxNhQqCwbeEZuEsy0Fbh91DklAqU4l1wyd0aInjQ6lV3b/1DT8bSPR4ZnAsdz8ue8tG+3ZDj3ZA45GUlRlHMYR455BlPQdO0lGAm4uAImu6e8MiJbm2gqNSSfAVI0Gs0ZKgkYMQQHfnUn7Se7MoXqeoPsRKX1QxA3gx0gK7xacIele8voy8h4yLcz65yTrwIyMOhrsOOL100KlSh0hTXfyh2vmJneKUl519VL2HG59I3pXvNEcZYmMApoTOqdYt3ZtywkCpBFJIWSh+LnI/sNtsa4eQgj1J/ZagYXuGnjOeQFWc10qPxf5ZMilFYJvB6/XbWJn4qUTW3RxIV2TaP9dwBDnu35hzjftvTVBhg9z9qAiLM+4ptt/bcEYuuySdJmhjEUnWDNB7MhTrqNVGP0bV/+/Hojs3FHpHOd4yE90mWT8mV7fgW8TMmFJTMgOgC02mE6OduLGBZNKrUHsTLbgv3yCzE1mxwx0pJoALE6SAz+cSGBPUsISAoKsv8kKQcWCCeX8UHrq63uBMwQ4iYoKJ2G53Jxj3Y+2/H38/810JmN4cDNnH/nMB8sjRNjrQuCEaZti6qFFbBtVGJuqNzw9kH61Bt4ZGoA4rHriJ8XVkDFDlacCnoZ2S0e4PojhcWKn+x5IVvnCfngkkqaT0BeDW74aLR8hxGJpUA6uQgwxYAghFFqsryOte0PaZxNYjfNdgCaxN1QokTgv7Sqik6Nt6DT8Ayzs/AqKhxJAyixchH2Yxld9ODo5iozEP0nKpe2hDtOlfEjjmINURUATzs4eulAifEtw+niIqWErrZvRvlb1oe46uERUcubtmoEKXgxZ8PfXJ4hZD48FSuTsopqseSNdadg9jqUL7BfHdpLrPbpDB5dUigCHp5KuH2akcwlgWVdGXUFcG4VmWd4ab2m9FkGPOJ4s4Ss2xxFYYp4inVgE2gtua47vZklFtdXIfJ6VIIbPsvuW/21llOjYwLD+sb/GxduYKz21kdWZsiIhMkCEZ6iaOVdwr+PPsiVKWhNEv2lZU8xcK6dcDrj+8Z723NtLlhsjGR5ka3q3xblH2ZNs6doxx+cbIU1NGInbhwedjvIhqNV3zNorMp8HsCLi50gLXzC1vO5Ty/caZvn3e/4LmCHp35UfDn6BfMUitEXeGzZyEhSlm4QYm+MLtBIIJ8gQD18ZQHmIUbU9hBBCDyW5XfvuedffYw16WWoQ8z0wYosQ55x8MAiCVRGdh5o6JKxOzRO6cuj4quPFji7t7kqBj4QSF5VXq8ejT8ZWGmJtR6YzpKVW12El2Toafpc+VowQfSbl3ItxcnTdOBI3sQS0xZo1dGBbGDbN6pEz66l0jNRAJrpd8mXqPaCkn/TNi1Yc0I2mfa4z9sb9ABmeVB90EoLzK9xFqViY4Pdq5qR98pXvQ87AnlBXbyYgUkeAqDpYeI/7RbEpnG89yFdhUH3cderoQYSsjvJPsQSuB9isAuwPOrG0484BeGWaZlW7Br9rvCHleyzT0qFw7SDmV+mf9U9tUx/90O6xc8ffRzYXH1nmtswoZxFoZ6WaQKqDZcG/R1HeTMWXW4ZwgAqAkMxE7Jrs6epoDJkJVngG/j2+A/WCX4uiO4DhalsCCHxD2/5uIeslqWEkaSRjeACuVE2N3g1zchjNq2Kyq6uue+tewAOho6F6XBQT1VISsy6DrM/+5MowSnhuWvNLka8n47+bYMXzx5gEqc27g0UPDzxDTcNO1rFAL+wPVbSN8xqJzNOgzm/5BZqDLlYPXVqFtnjMjk5colTsUcfDI8+aWRzF6UzqqOnKIp9S7BJLpL+3guEshscJiFODCE3LvMRuRFLScGxYjtLI3rWGJzgrzCap0jYZfCNlhIRWevddXNvaagw212bR11BpxEsJDJMP0BFxMPbBDvlIRkJJwZIZbVBa2/sxWXOZBDpYWgZ3AFA8T5afdOg1mA2izZgnXCPC87MCg9lnj8eTOeM/NGhr0Qnx7zkwLFmMhe2YjOYRIk38HfdfREsL36UNINzvM4lbTz6yC1HMufHEG7tDiF5u1X0Km6z+DBa0lMtsfLYl634TmSy5ySXWH7sT80KDwKztzt/7OT76fWTpyn7f0mTMQb+iPEos/xelKtNdh7ByNn4fsLNMMV9JIznDg8zBcFt0UZjaouaFCopRFVk2wDpKV6WcnQrtkXdcKnivM/KRHBlQleOGwOLFsf2dGo1Z21Zyf+SnJIXN13zfvJDTfUG7geZ+LFT5TgRyW06FCsN9ezkWQBsPFgXCVSEeevauv/88uj/qz+1zvevS7QZnJdLmzFQlPqfnlMP+iEFLT+IXL9fMEKzOq2fafemKnTVRXpH4dgnHvSOGkyn2SPSDwTSxKrUT2JmCMdNOMmYOckqQxoyDlCe5p0dbwLLIQb1EB9TF0Nf8SLhZbvqF9POOIUdvgARKHaNHpxaAUKoihOBY12k4GemH4Dtj/vr5A/feaGB7ML/uI5VJFwBQ4AOzojzNzaOOKbNBXEtL6TBy+mrfgFX2dzWoVt27EV+SJlkcy4wh+IArL7pXUwRLDCwbX/jPOQCslIviHEUtQfI+VA/QiRwrgSU5Y1CmGW0IpguZG9VnI6s4aQ80w+OyY5IN46mRlUaaBXA7BCo7WxWCC5JOvqvPCQG6TOkUZ2cmF++EcI7Vuc2egNAT9k8zggy28zHs9FeN1w6htQ5XPrb/YPSeRCSk46xjd72NWuRI+DYITpxI9mdB77fsT4gFPFxScE+OvPHNglBqcTNePLSLVsHGjgdxd07NjVUGStdWKsyYLhhHujOJW0WxUCSFzCl2ZkhHxl7XXvq56t6wOY8QfyEbNATGpvYiPss1kwwBy6Ojdf8FpTO7l/4uNs0KGne2bGbgTEQAlJgaLUfx4NOMAJ0ch4cTA04guTokHEndP4x0I3IovC8psbhsI1PeyrSM/VgShUJieLSUdL98Eq4aGcmisfT91b5o26F8Untk+7v7wHumjKRvrnnCoTQwIo+OlYjsaqc1HSlRhis/p8OVEMV5dmXOGMLXNzm4dtY/sXs9+rGUreDkaGRP/jDl70nF8OuoSjafuWbZ50Uc1CiHzCv+HilJobxJ5Jxa3vYPeo4OqEUTjkXHbyyuN5VJWkMCgJUMLeXmAOSfS3IgTWCyNPiMwUaezds9Tir+c1mI4UbY02HLFvP4Bb3E3ynFCodmPRk8LFyWXIhs6Tx/g6xnosNDUUeldiZglQBbTSVyoWmkW4OIJ8tYjKxCCOGrqRX98nl/UA/xWRXmXAAHkynHn56zPVtAGSXFAwtzEeKenRd+N8TR7X/9H/ZS03SpDk8/8A0J7oPqszr4feqslE8QUdwkQ6T/XOnkaoLCEDzPT/GcdXLptKjB8er593oASUcNur239iUyZc3X99x/V4ORnGs5lt5Ot5Flv7tyVMTRwOfYAi8HHZ+zZg5YCqPBUhE/HsZ6DTpDGXGo+FlqaS2EE4SM6f2J39Mk8VRxxAY8uP+vfefydWvijTsdJbKghxDCxdK8su4HYG7u+O9ii2910zta5zhMqkKGen5q71FLaykl8SayEOff8Qu/tA+QKoIY3cN8FvoMV2HQMWCWha3DIUQDNQ7HP/WaRJcaZAx3WFNRLNXVIFrFW442kXWT7M9oB+8dSdch7TpBxQKSL8Czvdb0zN6kaqhAZ7IozKXpr2xtzxqS+eA5OvT3SGD/jKzLkpVksKOO6QR2iNcIIYTsK/u+WQPBewL/Xk7a6pkldskBTUIB70Qs8G8byW3pyNxcvC+gIUZ9CW3XoQzw1Zm37q2ireQ8SMoKIv3AVJ+yUy6Av9EW30zDFg29ZE2BTXDgzqV7gGN720paB7sCDv7KPD3lgODGjJCP4d9pzMHyyD/EAZjtdaMvcvHt7K275Pax/4+UYMC1k+vHdwTx+oMdWfA4GIkdCsFna6ZCosd76d60a7ITcGUG/T08u6SOJz2kSFKmmmkE9hOUGWkGcCl1iZZjVOsj+DoAYDXlv8TfTZS6HV/t/k6cvnneFkt1wzsMx307/eo5/97HaBHLArDQzAvbMVDShy0JQEgGSIJFxSqBk+tcomWWElQtfbhjDtbkie19dVackyPR8nAXWQfMY6TMguek/C+rMMabV2dPFN5AqgMVw3W8VZJZcW3kTCgmiDTrHBIXxa7WSOs8CFmVh2eKPaglreIJgj2cISz5huBLqKd9vyGL5HxDs0xbAPnuu9Pq2NnLSsXvKzr3Tler5g+D4itQOshZxmaXmThK0y27/+qXdv/awZpq29/VhTS1jzNlvBm/7kdpQFS0GSBh/BbiQXutrYLU9mFpfbztV3kBkvQzaR1Nv4TC+D0zIDNhU56Rn0C6o4KztwLKZE2RtNri05TX7KQePfcpqtJtO62mMLBrFW98D6sAVouuDyOWCH8AvN1q3VZTd+jnIHuINGMCz48aGccrgcW7/onfDO375mDp4Z1BFxUzexnl00H5zHWHBc/ZE9Eag5I6D2sFT6/CiONUUl4RZtBmlXgHT402Dz5mS7Wrjp084w35bpzqKTz/olSKSBmhpZh5gjhpmWy0BreJsk2DzVWJOs/PzeEZiYbVJyd3Ll9vQlZmXYDPxPqVi35RtQZ2mFDDL4Jj2rW/q+b8NWb44Xr/xAzxLc1y0clRPibXrcc2ZDlkHPfJ/dULAtixxLnQbD9H3j9yhw0U0XrfJu2kJfznMoMEe8G9itdaNmdwEuHhQTfaTLjKWOVgt2KuI8/8OhwvLTnhnGvifJnJD53VkbGUqgalitLp+IyrC1rEccmyYiPOZ/0xoAnCz5XCOujfAK5NnguDOq0U154wMRGPZ6RjrTCapJGc4YFMhKYnaWQn68jidPzn5m0zPAvxJHP3bWbn7Oho+Bz6VgVaWi0BCzPtpWl5kIDNAGIM0to5vYALKvfYP0QketdOncNDfx9L1ogl1efkGHRPkhwxIU3AOijbQPX6ke4rGAh2Q52/J7pB8H9y0jbujBjxG1JmIa5ECbeI94n8HZ5bHqUw1epahTFtXJ2OVYeBBiWig4XPaqRYfYZsI0tTCdpAqWn8PPFQpU5QCD51vdAoCbZSDycyoPL68w2ZBOwDBWi+c9NaWXpCSvivHvzs8vUv2wbUeFj1oj+diV0zlfLXb7FhAXtTHcwl9t/bdU/n+m8+NYKuSsV7nDMyOxPvJI5vnDPw9U3jc8xcSODGTJ8Ga6swGGQRQK/inrQxynBceGavhQ7Jy27gPXWomJFRR4ZUG6RjILNyCH49RzA8cAQ0++N0n9jNJfiV5b5t3KOKz9xkusBR3rMoWYk5XSVAcDSkaem0JbXC5hMkHzItcUjwZw0RP23fB4bzkV+MvY9sjyxI/Kn3CNmJnFR9erdtr9Yeo9NLfgr11pK0KnUkZ3joI0hddUx6eRjcSN3zmhnB9ECcphOooAPVPSv51UpHQDV4+jFeawghzAHGcsA3ibS4gHIFf33+GgKmyw0fbQ7gNEUIFulDadkNyHkuViUoZOSvfCfMGqmTUESbOlvFNTqiIxNlH726/k3gegiezFCJDMmgPJGSlnNyII2RlbLYKoy0axtHNJXgdKTVmcAD04yfkyEh1UHCM9f2Wdd1wb0pXTNJjKfEVChHDw8MBj5LAeRn0bk4E6OXxma9VfW8W9dyZmwWKKuXJTVYB5dPhMk5gdqeg9w4VbGc2+tmVS963uKyxdcF0gt91nhLsj9uLTFTLUBOdmUm/ZY3Ndj2TyFgLeX2kd3QEmrrIe2z/zs6VE7aRogBE3WemOnmuk8yMRoD5K/+WAg+iCEnz8EfJwUjco8o+2a1ksHPITmQUi4cZngk++PYxxdQHtAgAHPM5/L1m/aS8x1CCMvF1feVk9Z5BlrafcVKEkWsNegisP2bNLckOjyMKpVIrgLRrywWb+ee4D+k/ZAjh1ZPp5YrqT6mrntDfyPk2lEQlQupmDJueqcm88o87VHDv1daAw/PmXkyD274aPBL1EdngvznClIMwe0dwwU9O7TC9qIs5b92/KMidqbQFiItOBqNxzZXx9/zq5y8P5qu5QbgYdcXHBPLLhrNjqDpVTkUoB1KV+6RrWJJi44zdn9EWgLLVMm72AIewWvQqCYYYwccVh4eSkvgeklgWM1CrYEhlnpsX3+BvRyBAVqd9BkOf1UbHwFwXBWL9X8efv/y9b/Y/rXdh6Rn9rsGqtDsaIrlc2Q5y1/6U4s4mpcjD1YqACw66nq7k4YztwQdRk6Um5m+Lx7698jI67uKBPB/k18cVm6wZEGQrpZy+TnNtNHh1gOMfi731WhX2K95yApxK0kJ3brfEEAwgvKqCFt23oFC94k8S3BcjddBjigHNdmnK9s+Uum37QxptczBXtuTrgHcY1o15O6ajS9L4qAPu0C1dHWi+e+kDqv6V35+2vCcKvvx15/W6LWKv4DAaHALAdOpAqvtteIbk0aiw8MoTw06Mwc8zFSEcF6AYNzdeJZJPhyVta8W4124cslurD3yIDBn9Nj62hWPcxs/Tnh4hi1bhMV9u6/Whgc45p/ZToxI3mNPTUQThErw6X37rqWUOlxZTIwejcAyo/Nvr4eb9tu2f+k3eg8kf0tZoI4jI0Fx3ZV1ZJ+wRq9dYPxttZdgjd5ZQetOxxyA45lfDmHOsqlGiiSZE2bebEyru5KsOd4WlYzIXB1JJ2UHloJtYZZycN1/NxsW2Gq8ENbX9BB6OkI1cYouqiAG659tmBrlr9D2c7PoM0FbZUtZ/erLm+69NNjTU+DZmjQV72Qvfw4piRBClPofY9myayZxIlEDaLwu0Ti1h9gEIl9LMG7haPWCADrVzPZHlLyH8fU4PV84RlsA8nfj5ymX0E7tZQ/s9TTvP7iA88psVQhSOk4wTa5krQ1EDOgk65nBvWQQ9EdkTbjPpBmAd9w991nJNMtYyP5o5nd4jc6hf4+2S/X3mOWmPYzI7iRkM7kPlqwIyXwziFSql6SR6PAwc1A58gtjsHX1Ex9LbdbzSwhg8MhmpduENo14fWwNz0lbOjMm6VNRn3Utbvb/SsftHoB8d4YZpIpNV1s0vUgilhLKcwqcaApyCDVolxmKr4JEmStxDmi5iN1SpXNb8Odvq8hhiB3zGBZmNSq5Pjbs2M8jwc7qlDFlnR0h1aqS7qswcOtMsyqegJs8KvwZf5A6ziYnQSFZNzpNKjdAWBuCyAgDuOvg8tcYgbE1J+UB13WIZ6nYLAogata2hNK0MhyzdJXD5E2lrudK3Q1v+BfUTsKeWwipn7vfJI+wn1BThBHSjjYe+mqY6YxGMGAY1DNi58qqjBLA8MwGzrW0T5yLdKvSkVEl9TiKh8zQ2wfHA6bwFdiwDByelAS/PBvSwlRMQcyIXiLW23DP1npBMhMsoU6lOcd1TsEZUhbxbAV7R6oabOopNXxtkEB7YmYjdhznV8RhY8lM6UWAlaMzp7qQE2S5CsdKgGuvx+hwy3Y0PW0vI3iqhJHo8PChjhv+C+vPbNLb95hfj79G4VTaVt9nZxZSiVUBCHLyxHDmMOva8k0AaO8tnKridBTB0VOQclcPOjyzTftcOsGrJPNoCF51ftL0Uz6uIyqpA+906Fda7Rkyauty+PGAE8Bxvmu/lS3qivtgNk/bZ10rYgzHy9fXxHfd87+TrKvabj6DxAEJEDVrsgqD2RPyfmgk5Aj5xFmpPYVxF/E8GgeykGZF3d5hGWTT0aDTydH5ZOZOnSHHKqsRGrcxDzHl4cG+VUzCWsmiqZmA0k7hPbamdtOdmfdW2hNkX0XNfIggyXF7qL4TTuFNAYc+PsUD0AMOz5TPetL09omHspJMOsV7OJyq9+WyE/14u/OmBpn2aYvS4pDwMasDzxJF4VzOiRjWbw0y2BSzVG4ZnA3jDTrp/hoEU2nZzR32gkFy4P1y/PUL122NaccxMZzDQ8uA5qV0RycnLdpc1OBS+MQMCgA5UL903/Zn3r3/3e6rfc8nEc5+YNefyL6Na55Q/M0EmbhIez9Ie9kFps4V9wGBzr9tJDMt8/7lmp1b9qfNr2zCzt/xlyTfgjLCTnuYTKTYRlLSmicYzjG83/S2d5R6NZRpaOjEWaETpVpdbPNjylFb4JnOHze0jm+v1eAybe70fyRq7962Oajsu7cCJYZGkoYdoZ5M0kBVOp/BqChbMyMzluuUa2dSR6uuZhMW8ZHp2heQFbkLIsmEqPdNDYfhSbg/RoP6LPs38MxVARwZUu4X7XpL0t2goSaGJNLGi/TxTDI8rrVdSyy4vjuABJydgqOv6Xs2AByN/Ml1MQE/F3A0g5mfhAHIDLsd781xX7GFVbFljPyVDygPu7DY8LZl/tLukfMTOUD5ngRklefAK7A8kOTTrJ6/49cDfMbJpthSSv8o5oplf9F8U0Xzy+tJ52L2xIwOO4e//gLeb3yWgiR82pbOvZqRAMTRaYDXreCrsGGGxZKWjdXHGq7fMKS64tPSXJdjcewKKMkJvofOEfGu2ll98Id0SELs0GYMziUDwOorP8m9W2i//8xfg+zZTtJIGy5wXn0TrOdrl7SUop7P4OJBfO2UkammtuaH9kBI8T0784Zt8x3LWalD0hmSMESRsuDvQWeWAoI76LAq1yW7dAB+nT37MZO+ou5wG1qqZhS85k/JIkp0A1ykrIrr7MQSzAN5kLQWTq4ccuOoYa4exGeQOBxYVp71AHiF4plkcRIW5fH37XmvfWHPZrC1ej24jnPF8VT5zzlupASV7JQwwvI67Jyj4xKCZ0DVSJQATZZRlEuLQ+UvGHnpgcM97brKin5BZL80A57+nj8hjrvm5PzezS/cewtMQg+e3rp44kd586iGZVF8PkNaCp0gkSg0wwPI3z+FgzvHvj6TKiek2zE4j/kzv577t3ANOMi6h1meSSqHvqnBrCdJNYtHEvjhkJquSdYCZH2axYprxddOr/EGshv6nJl5wn6cS0MBtdayPeksxB4Zr/u/Iy8WD/soxAPXEFxbHpx1o4mt53LWnxkLnD2q8cas0ea6xykcH9jNUPZFS+JOrFXmPoPSrmYs05gDOkrnb/uLMGBve/m6MEOFJXcW73gNb9rnMp3XPyeSHR4cliU9wFCGuHiHQDXJfCTQrjsCNjION/2OH6EvVgnAmmUzpNOCkI89og69vZzk/ELmopkrbmTL7mX5xE6g5boPL4pE7YtN2vjEfuhLecDU58pDPX5e9E4fIwotb9BARlgtXb8rPiflDV7TyUcEz2GxdA6P/1zzkf3O9j0Nj/BSpridX88vAAAgAElEQVTxyNZI7xqyRKvn77i0tpOBGMU7DEn1fgXt8czla2XKJ6hYQfJ8Rku0P2rrfDYGixOC4KokUCHjLDluFP82f9vCfdUNIqGglqo+qr24fP1XR793+fpu9cx9boLsrpa6ib1jO66Ke8527HO9qS8j98b2gLNVOXROribqjIhUwuGc7HibkW1dvcCVEZvPbRWznm6tYw0o7xMzbakEdlzd9/76cBg2pZOVWl2iZzi4Zp/d+qWt2eMfSGkNkggZ0W+iDR4LYR7B6r1bV2NHQ/CaW6Wad1ZYxgrgqgmiLZZH5/C4LXxqa3aT3aHfV3Gg5XnRz+MCiQIN1oi1UuxM562r8WpKPDjC3CmHzhxzThuktBnVr2xP929+SyUtbi4VfOQmdwZdGZnZcy8AKIfipzaQKB8zXa1ES0WIEmq5ixuFWlTaxsa6pzo8i3O7af60wpGfOhLSZaQVn3TZy7l0LnC6EnBBeTjrhZa/BtvDNdqn8+JBg/HpyAgTNfS4+FsKF34Vth4QFOKv4ZxbZQ/AVJJ7Rw/hVRguzY3bKx36z/XRNFQ487+DKXstF7HU6IxlRqMw3JOUSWlgZiU6PP67XBeYlBFmrq1e1krMobtU5w3/nM78Qy9CLT0np0IbXvt7DZvYg6EPlzfK5lA9O/doYQZGLD9HyP8wd4pJcB+T0gHtlSv5anYGnEhK0RFbqpH5dWR7Qh+wEoNngTaEYLB9P1L6Y1ymbPHMOCBLrcECPzfc8fOUxmdbD0Bot+WjhTQIarWM7Ph75LudXeBelQWXAc/bVM65VM0efAZdhlrSmmMvpUW9gGdgv+8PXPL38PYjfEBwgDLS3k8ncykiWc4xRzZMeekaX9rrs+/Flx45NMMz2LDfqZivpJHo8DCLQ6XtEELo3kHUjwesnnuK4mayV7fesoitj2iqVpSOiwQGYjo8Jx0/s/NNe68DXZ9QFQHSl9DCkTZF1x0FSu90U1TVT21xRbSTYIuVfLGEe+ShoC2subY91Ol1/4Arh9joG/EbsXxKIkbBVICvRwGaxCRRZ6tzy28GvqddWixpaflkivfIAfRNGDR/V4P3zvS36oPRwVbdIKa/HUlg8Ayi3OTZrjji1IGVA4LPnJw/6siy/KCSC3Hkgl9fKHXVy5CSrpYKBDe7PW+xVEeI48XAHBt2qKi0BBXMWXoOwRvfuMAqBE/B8OTUP6gSM8YyBTxAuQ6WoieWRUQ82VHjfnWnjIJxXbZtBUHLjngWZZr5mmS0sBYjrcSYX3bxhOC5jRx3kTpXuGZBeHJ8YwfuXSlKQFabHYjEUZmOl/CdwXY3Pgc2a9d9LIzRmZXOxh/2ZCdWrA+d70zRzzHPys01n5o9PrJI32FHtfoHx3y6I9433tNKwwzrliVLiuSGEEKPOB1xtnjOTZo2x8Vjv68I+Yi0vSeMZKZleLjDTX9jjBwJytSFXKiZ0ZsI8+PxM7O4JCE8bHvjuL1tp8AsoVVZ9XQG58AQkPPi2B/U1OwYPJTTAw+YLYazhPZTgtZCkPZiecAVGNXDti3IonithC+ouKcClTkGVBAO2CiyjukMaXsxM0jFC4Kz5Xfi8Ubec1GwbGBkNdjBlciC+oaGw1Dw/uQRENsSqZET5JkgHur+Rp4J2XfHwi1Dh4o4PMUTuHuWr80hXa34K94jcQ7TXb+o2hfxlmiBL88LcvRW2ZCepxMoQ0sGhgrm7W0Bb8IhyT81e6JlJepslXZ8pMLAqy2iQsRDMdqPRMvUVTqTw5UJUYqdqrRCn85bWLnBfcq1npLy4QSs+5lzOXri/d8wvWY2MnMK3jIJHpeAH4ylTEPHhuBpbW1np1QUyG//rn8l+CQ8swFKUGNx3lLIwGQlO8NW8TkcIw34GxvmyCjEowi6h7N2/KRmkUEai25jGmfsQnzKFJ1MiaCIXWIFQSs7BJRr2/54F9gc4Lo0+CV1Qeno28rw4EYrr+Q9cHOQgG4oLXTjHn+tX0B7d82qtvq2k/eu+z428mO8PPOeBlWMByOfg6SnyghWhR6dhyh1W5bkxneQedJFgo0TaVHGM1V2yu4dzA+Mrwqi8bAiJUAIIRTwLCJAX9QVeHBlxekYg8JfOxeY2qUjM63oIRzvodDpq+77gzHbRZbue2jH7K6ex0PnOKIiHjM0o7UkQ4LgY8jhxAxMxKFCCUpr5DQOJG3TOjjZUHs3/Vy7bjQNRHEAk38qSEmZmJ6Npv+hGTgMqgZ9nrMNuV0wJ+Rw5GvRZKNdCB8JiQcnALMGlW2ALp0SvDGyTpUkEIKtIc5BW3DJmaXcMxkH8rzaSf36PXtdfRFWbvDMdc0Wsn0HOKSUlNDh2gRrlkI7taMDUGJDSvN0hF/HwS7wUjW3wLycEe4lVi+UK6j0ijVOvCGOF+9x0vfnVX3NjH4bwGTN8DQApj/tilNDvqGvRA7lrqRkfvM5yXIRu6HvEcOjjrnLEqMJKUoCSRsq6wDnL+keRpJw4ZRMvkGZ9/Xb0uWaDphFFveuXyQ33zFP6dmRTxmz4yoPzzSt1Opjcwre2fUCglSSfXHhkWTEELjOEs3O3ISFaQkIrAEgMSIDKqyHEML43CLACJU2WWv3/G/LI3XJVsRO8BFlrm3zevj7fqXx+yr7Eu3DceICVYLC4rldZLgZ3yFGfIFiDSg2mxUldWYoOrf9Rp+V8niNv1lBDA+dV3JxTIUx2QkgavakG+PUBMkG4oxVHR9mFZQCgJ9lJwVb5UOIpts5urfjHSpGvixTzOp+739wzwipstJdeQ65mDs1T088QU3us46Fy7sln4Fx2l3anou30pP4CHB0z/Z3Xgje2Pa+7EiLL+U7cPCOBevosm3q+BKHBeOeFzFHUhW03359gObvahCoPbjNRSvYL2RkNOhMxP6QgXgAhmvFjAF/oyVg1/2IvRqRW8E9srwSgoCkS96R6Rf5BQge8/5z5Hyrlzx0IwuH+w8/enz5+kXfH1gldG1NRejuHCXFmz/2hHBPvrINX71h59d53qcNuV+01J2EIWMDA6U3Ig5PDeKnkqULMU022pgxv4FzeZDsxnC89icjBpdODgzi+IHwVWBF7Wx03HtUUF4rmUeiQoDf3bI+tv2Bj/Ie1kzT6mDbOwntfWjtwF5t/YP7WDj+M7IjCmKdkS5KcrO5f1A0UsrfkHNst34BjZABy5G0rOIjyilbmQUAyhq/tpTzu13Xj6QZk9rGNcK/vIYAyXLIyFAENIQQSgBPp6d+0yyy2GDsAotXInljw8l4ZK92+kMIIQVDkYSx0VRtKsa5TEkmyONv5CZjAhXlgckgU6P4HpapswK6dsBq2gHpZFov2OI7HPq9edi2f3+07omlWLri63rWT1apaj98eCEs67mr77F06H9LD5Idih0kzmEo0tnk3WJ2QjmX5iyRbPhFUpA29ctriK317Nurxz4+2QZhKuQj0ltyFpzCYIjDw3LftCGbiWSnLJ+pGCuyP8rDw0CcQUCES4YizUo8yM6jsl/rsyUfGpi9Bdc2gUOSqfiMSyNv87WGNN9CtA1/eWoSKHlRHmDFIzKQWTk/QoBe979lib2UkpIZS7nVJ/63dd+y1wTrV0Ruiqsi3fD3y0xt4andx0hKg0sEON8E+pDs8HBxySYsoO2PdeWlYlRyYCcWzoPvblpkdwLysWzFO0YluHcfNb3X+nhgWZ2qtKV3sJgJgDr8Y6mr0oiIx5kHJxCV3/Oiqj68Zf/e/WtZ5CgX9VWXCBFAl5iHkb8PZngi5UU6HuK3sCzEElRUER1/05MVhH+Wj2DcJCIeN+ONMQ/5WUn4LTiV+K5VLGn5Vjd7qe3ITp05QdIhmrmx10zVRpwml0HSzAEdMWDXJFpjOUrlI2ZMO0tXI/FDI0gdKHNsATe9XpA6DZK9GbFY10tW6qaAZ2fqPewhulCUW8s5evhpke4dsK7fqnmWuEctsy2akWB2Id8C0FJS9HRoZwJank6uLv+o8xmHkVmVkUG5b8mAUckFcbgp9iR1jgOsIRQAgA/kW/b/k2tyCMIeKZaK3XOUjJjWpCzGcpd2iyGrM7uQaA+3kgfdwET1uDBX60W/J+5Vbd3/qn398vV/s/0r97kvO9bvv1PxWc+PDyyLo0zLXMNFyE4sJRM3DWaUptckmsKziLTmA4/Ds3LS8J5j9ghyStJyz3LaaI996fIxlBsjVBAJI9HhKZ8iyhPJApY9OCUEGIfgy1Pa9vnXz42U5uaarWQlWmLtfi3vwS0EPL469ZY5u2se9OKFORMzYRnOVKCXpXpfFXRSILWqvCIpEBuefujfK2CTzkWNPXVOtmkcHkJ6RTVzjTzKJ/Cm973TN167Wk5iUktI+UpEsf6JbczWQ/NuSWQYgj/Yq69Ee41CoJLiXMJh5t91b6xeNOtS4MuY1yGEOUm5ZD8S2K/kXRmsP9eRo0SRz+z1cNu/x/bwGQy6ZvUyg6uDlhCSQbQ87Oeg88+JwzNbXp2pCcEzLQ/m/gvYlv5l14z7YOY/V2/YuuwEwSugnZapdnLBfP1Be6lMzqSMSFKa53t60Naf2kWOr8W3pdNJ1dZ2h9dawS6t5Rk4iVCumIvD4IDDGlPF7asQnP4gO9gygo8JADTPJUvEDHYRGUvtiGNpbSkNMuWndn3FqrJrb1qP70wmMPnLE09UxLJvEY7+0dSfa2/VTbysPfUb94e3nl++/tWRV/csNMHfQ5UDNVBV+23FR95oOOdbgjU6+ymsZ7U7rov0QHC3mLoJOryzUo6vHNjr9sPXLwUkOjwsj/hun6j3+5vRGXjPtwdwH52aEEK4v2GnOAGD9ysn7nM9zJgi1g8GlppLq8FF2mvJFH1bQijgWVTc0xkzghgTWGsViEoDFpGWIFgvBrAVgrTByuHEElHnruJj7O9KZ2S189fgs6YDFUIInXtXOzmaSowjzQvBk3alhUWPDpZzolbPtosgbXxJiEZVqiEuSh3cEIdnwEPQ/l+7t0Yb8d/t7nEW/zl3sOohS9iAPAfW05P0NikRkVUW47x9YUGMya87ZqirkHtQcDMjWN2P7l/4nO4rvncx9huLnCZK2hjn8CgItv2ATqsCw+01Hc6JHMLZBGqBVRh0ctxv1AwDM4/CSTQHc7XipVhy4eGp9n5KRu2RBHSYN1eWFuxQCrAFhR+4s0CyIvTn3dooq2dnL3cbPjnQwvq+X7ezcSETOYbHcD72jj4zqbt1f/39CwODEZ4RjoWg0FVG4s85LZHn0Vk8zttrPQ+HN8AxlPfPycEnKKZa9s+pjfJZRB8vYSQ6POwMmkg/PlNKXAjvbno2VLKjMhUXQgg7ACEeDW2XKyqd9cxNmT0ay+dCPjYnsRO4d1TFl5HpUgw4eRnKxAwIqRMPFiVacj9Hu8AQSZNAiRxIX3/QXirug6Uq5eEh7IF8N3O5Pg8/vQbfc9ISsheIOWHXVwi+E67oMaqhgO4uOl66UVZhcK0z2tYSIZ1SxSQsgVnSOXQkjMjIKUkdtWQi2Yfx1QekZg4SuXzgGFRe+C8gUyqd9HHX74kXZVDZC2iZvFsbhfis7enIulAKWX+Tg6FdIyOUF3NkY3MnyPYI7mPRs/cyO3KPCGEVuM0M3oyko5KZ5ecKr0QjUA/D33xXAq5r9QpawcEA6EAoL5MDMWuJAmtWn1H6SCW7/8v/f+Y7lPLvmsEYd8Q+OwcIOCNxjBhQp8fxTlOErw37gBlFfWAs5el6vla2Fr/7ZcOmftbbc59jpUQTAH/3xT37hwD5G3vmADF4H6f9XNEuaLnIZaQFND4FbKTyCLpmSofBbjrFULELD0GSfq54bP/u3/mWMjyNxyhHrfvJ4wFP46iEYtfxENWR+fjc6o03qva5lqTpvlu1NN1PW2+593aK5jSpn1erg2YbbM1ZQc6XwN8zSnmjtETGh0BljShJ8BbBbHBdJ2SQMijp6jVcp5Rm13gramPw2GovzCO5eFtBnriERKm1l/aFLeimpcUws+wZYVLF05lJyYxYHd6HypmswuDBRwdPyz4OGDmNTMblUNKvOJmQkk96hilTv2t+ntKBDg/2sMAOXNIlIUjSlLTD8CAjl6v6hXO9Znt6v+vT8nRW5jIJt8sWJM2WFiRpliiOTTkEKbuV4zObDogqTlkJpKZn12VTDJkywDXEMWVHEHGEIQimCi81g0si01XkpmK5YY65KB36dT/cBZhXsgMucyURe/UZAjpgPnQ+F3ByUrLnqKXIPTavSGkKzpuSC04RxNW+8Aa6D1oHR54nmmEMmnWcj+3s7BVss94q+QiRGVCFf2xt2eZs9Xz2p0DRUZTrJgm6Y2XBi46glziOSKXYnAx3bT6aX/jrn+8yGJFSN56plrHcwFuqUZc0Eh0eRvoLbRsEFoVkSozcdPxg7bn7dwO94t0EWdb23B7cd+qeiOLfnz68fH1/04t7fPqLO5ev6ZErfwxTo8qNwN9GWQul0efeuPZTvwgpuaARBf/ttBEj6WB7XdnXVC5xH/7vuLk7d+y3FS+8wWGbOqUkQghhAFK33Z+a9R3c9BuqvwsBQMF88UwTf9aV3ShiGnWa3vxgeWGEOr4SaLENX6ny0/G4TgdAp9Ky8uTQwdbWUWbGJuiWUxoBpqT1GhnHBh3iBysYchqfjWx9nHf8Wrm5aT9uIQ+6ikVLpuWiRMS1CjAJ0p47Ab6DjpHqV8327Lu2Sz6l+J+e3cYFZd8i4nRq7KpAgcMkUgKGDSHDtsp8sLyoz2kVxuKGPYc8BGMHN+Wwf5GJfS+NOUxLS3n79+y5Nz4Gk7NiMaGZlhNCTzqRBOFHOGgw9Xrg0vFUdl86ZZ0HkGYQez/DGXI6iCcGZBYnoiGHwFuddHZF39jwIPwzJCOYGZqLLmT1U5xXkjwZb8dnU2a7tpfS6PRSpvkAzrq8qMlXXtp7rffsYZRe+efZvwuc0atvyeFhrZ703iGEMKVaLEiGFKfzpGv9sxcFv0p6aD/fLpqxaeZ8u15rCsM59ddgWewfTwSkdQvZH1ib/pm/xvS51aCWdf/w8y/tBCL/yGLdnx6UgmjfjVdSj2ir0CF5YNcnIVoI/vAfbMsmwmbO+7KtK68xM6S8BtzAozUB6x3b4uo8tAsW2trtZq8rr/w8zko2J8r46w72PYALz1cvnGVNm06OOmfMrETKIcDjRDBdMUDZvHRRMetS9PbQGXTeB52kEDxeSsuHJCyM1FGIZUgQsxxC8fnaRjv2cyxbheDBw18eW4bn928+i71GX9jZ02gGYKSuWICAA2gy9+aQ0hhtCYToeNCRjEiFkHNJmHvTmEe2r0foGJj9qcr9r8BgwMiyhmKWBsBu6Hu1x/Yj2+9IJgwt5UOgIrTrkBmHRTZe58nxxVyoI2uv2YGo19DnTOeLZLULVf3F2Cz76JTZmpMhSrmS0u/j3NRSMc85Xc90/Nl0k+5qlsVeD25IeRHOykLwcCkosC9YGSnGOyRareghxqDtUqZ54nAj9AEJI9HhoWpv6di/x+6l4gszNsNd/+3kFqiLI3M2tFP2rYp9QUdaRsZ4cGReDSGEr9DFMZY2aYqzscav6c7FGi2WHPYQhSSx3iAvYognWEByCMwTDggHoEsQnXOyDeIw8LCK4IdigMSKEXJGW0pO7bvgjoBBUJFU/hbV2eKIYEnw2BhRKM5oFQY3KOVVSsf+XgfWVRrNVPFwS+hWYakq24t3gIeiSu2eORwqvQ8aLOVhcvp4CgqkDg/IIeeC2RhNbQ1UC/6hnw9s77+999S9x+6uu1vmzWUl3CQDu5a0XDnCdT+KJABeF0VvxYkRR9jZAaiE3IhmYNycSxkni9KHoypIKGd/E4Dm72rMwIScoYCkpLv4TBTwyixiWTBjA6hhO0yJ7B1m75Ziflw2E4e2ahYSR6kBAj+r2BauBxe0yLqZQjJiqF2HAOi/eGZAuT/d/dx9jnunnPdrdoQgo9uPr5osY/TwQpDAXmgQcpyfjv/DERybfOf1HBKlvCBNAOdOM2qsZHxrWloc2o1Rf4TU0wc26QqiYivpl21vmTfAQ/Dvjg2b827TsykP0bbaGXlnaARnqC8PmFLzjh9CFiE5b6pP4kXnCBTNdfznHB185GCx1+XDeINVQFYnkhqfAWTWiL+GOlvEx7BEORe2aQKhc1IWI4ZnsBmf1SCbdb4nHXOIgCJtt5AmiWMaXplB3Ah+ojqa+fP4UhL9+UjLd4xUjXZLOGMsy9kB0Hfp/YiTi5JKQVLLBFZHHCUc8MQqLY/83qy+bZ54J6HDSrF9O6jv5OFhPup64g86Ud2y7H3s98Vzs4hTSd+zpKwO1ekBxBZH8RmDQkKbsxtiQ3mwsDsvqse1vPL1qgy2Frv1IE4oQcApKRcxuFbb57KlsDFubQdxNrWhAiVIchlF+JWwBNQZcrgFZfYm0BrZDRWTbYJKISfr7aBv7Zw/+fDTy9c/O7/tPtcs2ySoKkGlbnvi5YWPjCcDlKroAG55g5xFWXIupVznzIn3kD+J8WxkHZQOgYVV+ixk39gYpf4HETAU9f5t47VLWmr0GM2zxXA49T+axIN5Oen+8YkxRn7/vuF72M4aQgjXS5YO/48v77j3SgW0LI51p+B+4Vkr5Tk1UmZSVuXhxNKRPoDyMTqNhGyq0CIoN762PNyKdwoKqAho+YHPQrEzroU4wVbSuZCqYejvMMODyDavByh+p8pC4J+5gWAUkDmj47WSLbgwFOzS0s3PspXiCdii78RIgzfMPPjmRTHuOHAV5Epj4Esvsu5hJ0Qb0w09uBzQd8S9Ew8q7g898nmjJl41xhH6+BnssOwdQgj/8MrsR0Vo+k/30YLLNn11JtBSPZHUCgn1FvJ3BLdyvpX91+33SKYP95GN35yppHLXCgxG5cx86Fy7jtcnfq5pm0Yb6oRcvdbTkmUpH9i/e3fj9wvLUYqxGa/xPbHVqEFGyjm4fgZwh9mZd8SJ4dHkAJUHOD5oHrh//2PL0sfk6wkhhFc92zubssf2zyxgSDVsv8za/nvnYARXqSh2HUYcHuoMNrjn/OdGW/ZsGl9INi/L68efm3Fktb9tJGd4mBWWA5hppDzwPe1Nb30vPjPE0tvf9zX4P3j46PJ1A+UujeRY0y8IlfZO1Upcw7F3tkb7wOZggWYEFMfOCs0q0GEghf9C1iZJmArt+AN9Kg6VV0y21xHDhktqGtDdh5SBsof2h7UX9kCHG1K7ZseR4M5pZKiCq6nEEvh7+JtD8NpaihFSPNHl9eMzsm9sOD4PzJNmxehoRFKu7LjTDgm8RyHKRS4etxVhhMW+5fPS7jue79oizfvSziOuzRHZg+XQPuvYYt9u+lI0WWa7wqA8gpNTztpNl2ShfH/PWNf/9uld9x5PUB5cM+0OmsYfQAtIuKRFNmOOII9Zo+K+t0ERxe2Y4fiSNKNGJ2oFS1p87vMtLMZefMQy3PMOCdmqU7c8AzGZ8GcwCpqd4aGYlAlj63xWVNuJA0p1NM2Ow1gIQ/lcZpDQUL6e9bL9thsVj2vjv3/StAzPy6kX3CPXTk04DD4+tM7nm+s+bVvbsYChAlWCo6lkL1+B904c+GwHHXkSrI2u29yVn6F0J3QPdHwnQl+SIt0Npk55yBY8H7/Blkh0eJxxl01YQTdN/xqwLUK5Xbxtk6zZn19emKdKVtO3t3wP7gXKWGx1DSGE4z5AtOIMDZkKbcenx4g8X0rbuBJYXf6/GB7WfqMA1hD7nit34HXjqb8Psg5rzZVMzpVjPwete8jO4BB2JIQhhEILkW5doq+FzWP3OsT15JAnLqjQVl6U+CiVjsMCy2cVeXjiuHe0s8YJ34nDwGeuByLX1YgATaEAoDM0EgwP14djLtX2deKx5B5de7xkkKb8rVzPCQKQ1byPmEjyp3IxD4vWC/v/tt++fH2t4Pf+5+Clr1b8BC1K9uO6I6SvZO+wpFXJeoeq0rAgbCqHwgQBFLM94z2//6gRNdcSD5xKOpyKAyKhmz7DlRjE5sBxS8lhGYCJSg29IXRrVp4R9agoaK+NI/0cOvqkc4cZJFYkxrckCsCBq/gVdpkNr0k2E3NQOIb8gmhAkXvn8wtPkf6Ta19cvn41NcxBTyK/tysG+Xg+8s7Q969bF3NNshQnPTsrO8i4LqQyUj4B0F442Sb4PdVH/u/6BZtXNj3oel4gACz4Hif3fcyaKSM97Vrp4Fvq0mKpJFJGgTfNLEXhWL4cxrgmRo/AZ9bx6zlvvH59YMRLP7jpW9ufnNsDrxZl8WKOZqDLjgCCGb11pQ6Jv0u1bbo0guAhpkypcbgM/Tsa457Q0PtWY38NPqf+tn+k7HSqHKL8V/TX7wNsrrgS1s1dh45kFpjFUaXz7BhRoLaTYg44P6q4vgojjntHy1YubS7ZUefg6Q5kbZ1kW3IIjIHBUgeYazND7IJ8leOq0Ww64RBJ9sRx+fjndX3drFle8App2IKaeHPbWcPw7BTsdVHAUJt52xSMnEMI4VULm8QpskqGB2U45QOievVoKHQVMe3mSlexTHAI6XD6oEiArvDXNEu3CiMNXcFFB/OkVW1E9gsp0S6B6ZlNJOOAhhMGbTMlNqTTIV3FA3D3McOTFsLKDCQXKr6SFPpoAlZg9QQ6guxUU7oK7gMN3jlI0zIWBPZe1vbVo4WPdkjx8EnLC1V5okPojglOp3/fPqc0KkwcjAUHyg7nzCF4tqTUTULKSMPF7Op9pQ7PAhmfmeAnk0aiw8OASg9xlj2ISeg99EbpXtMu8sm+Z4ysICq72bCHOBJtHTo5SqVdRFZHuT4CIwxEYdrOWPjYFtfgLVGOZaoZGzYlhq13C/ckJHF0eEbycHjQOHI5jXIw3xFgMsQ+tcOKJa7xGphj4yEUkTHYtmvUn+EQkNIUWZKTiAcbT/3h19tD6YDimVIWW4VBQU+C37W9lc62trC6bIr8RLawM2WcUoE8QkOUJRm3wk4yAqlD8A6bZj0d26qWs0Gm57+aX38AACAASURBVHhmJDvK7sqFMKWuFcxojMTD/ru+NTDkkEbTz70cmsVV4HMOJGuTNQgYvxRMxS5LZiJaiYmcSyCUJo6pe7WRDsHPv4KWNYPwm5GRuXIdXN+gBfd3NeYQlHTEqrp9ByAtFZFm5/BJp2zxkdktpwcoLdP8u96t+MwpH9K1f+MndP9PwMkmeoPE7QyEF4tcTPycBjTE3LSECZRr/U9qn1y+fjzxmaAJwDMzMbSEhtzc8iWt/3hi4OeTE3jR4oi7kris0UU2Ht+TPYYo6Db2kmTzmPFRuzPbsr8rPcb1BArCkmLu9LV7r35LSQv3qVEegWV0ILIVbzR6YzvRiyUfnsxhULxwmr9GBak5JSlji1614i1z6xwLinXm4AfbHoNgKvKo8boOD1kkWnJw14B0Qv+Gf2+Cw2OO1mDlXWEXWNZ393thvAt/X3RKWJqg4xJCCJ1bcDrEoeKBSh0sZUxOArnTAaCDE0IImbjsz+r5O+7wcdkocSDJZbGUc60AfkzF93DumQnSbNqsfrXTEYJkfPAclERsimsoQJPrWY2So39PIIfsAYRJzEAIIew0DNOjmZsC/l0Ej8NIIt0q7MLRwEcSxOMsUI5aCFYpV7SJVYHTeskmoVv0db0UjD0jenV83S0nObd41hFFdPxd+eUKbgp2LiIISHf88UKQcSRDzvXd939H7aX8xwQ6+ttg5qAq89S/juAB3/3qLyVlNqEdFDAj/m7tn/xzbr3DzGz8puhMbU/crnqHpIzIoruw9aadWK9gXA4HPhPxk20ri12IlxCnFLAQe0/nkJidrz8MPOqGCGHTLrCEqNg4PMKxlmiTss78LmSe4iRarhqJDs/6x3ayHv7Yb3giqCdQTF6r+pOfqeY7Df+AH1atFsm03U8P77nP7VTMOF4vS9EPB8u5iP+1a3aaLLEZUrIRnTOnYnIPLG2eGjFiFcZWfHWEZybPA0Iig5guGsW5sKSVxDugmiOMCFneEtvu7lnxPSyv8fDTBZlDpilJqFK72AjsqxzaDydz8yoOhztQsDuyONq9NEXmSgHBjpMowbHgdxdEm4zp+8KpfVBxB3V0SPSvu7cctkgVukugVmB5dSKGfmPLvMCDC1+HPamag/LP137t3tvO2H7/ad+Y1HdyvgTwsm/XVKblHshFne5f1W+sXM4enLalK4jZvUeNrFNgF4SJlq3/ytbMvckgZpbSPQxbK2WEVRguu0HSPS1bocytGfI0Me2SKSy+QtAJvzbV9g5wdscmsXPfnwWLMrLzsMEZlfqB8ruS//M5nP3Qb/gMsT8orWkWpA086nfXPHbtDJwGnwfbxHs5f25+WLaKx88zN917n0B3i9x2IYQwRYLhwzv7l6+/PJUmIcgwzSKAUbBIX/j5Z9DkcNbKPwUnaqbNEpjH8Z55YlEuMHu5+AZUDYkOT/s+WIblkHULCKnE6Sz+Ac8WfsP/h6E5Nmxf36t6uuD7VQuJ/6nly2JsVT3u+ShviUiBQDXeewgh5Gr23TNhVCUQegJxxELdezUTaOvkhGNigIwkW/dCEE4W1illodG5mCvGBreijJQ8KFnuSsmzYCv6tCzki6dI5YKHR5mQ2VYfUdEmh5tEFHQWuzfAiXSwej24LDdkE+QXWI6KaMKQYVfaW+mwOpJDSenm4IQMdwSXguuPwRar2YekshWRo1o/p54RDU+26A+Bw7alzbVLi85EV4hGWboiVf5E+mCvlc1OqKbQ6cC6Q5lCnwu4nFnmF33vTZy2YU+SnB86qXKQO2dAHQBXerTXeYF2DK7hgD6Lv483NaiL5aL8psADUNKK8KogS81DLwTvRBITk5Vu2zmyIoUz6WqEdA4P2elQa04oaXk8cCgBCK28Wwxkl2DDnklZ7KJv9/iLC5/u/8vdX1y+vpc3Z6W78J5Xa27GgB1bIYTwR2tGUngy80bp4sL+jgSFUwkWlgy8SzN5L349OzAysLzq1BCeobaR13ROTlHrwfYy1f+WSlrjZvzmckq47CwRcN92zQxds+CzPyQVY+s5HZwQQjgDL4/yDrB23x/4lANbSRdgRs4d+XucwSPPlP0mHfCaLmUnEQoR5cryyUNBylFsr2PWTEsMLn0rb83IiaRETg4EbK8VH1M9tDno3PSLMN9B1mUnfsmQvI6cPCH48lekPZryF2x5Xl/xDA/LRRpkEPQrzyuPs5+HWQievdT5AQIOIc9RJKtH4VKUTVjCCsE7x4o1SGRaRr6a35VTErcE4qd1KKQPhAfhl70Hl6/vlrwt4ODebyseYtcOgskRM70yV2g//Uoi3XrVNuuF4EpSoMBwh7fMFR3JqbQoUz+LbdppAePyUEjiS3pTI47NO30aXxJSJ5onWOQ9OhOYQi2p0H6OEniN6IRG6BgQuKotJayAXDJff5bBXnxGn2O35IMAh1dDxSMnNzKFUd8s+G4i/ay7Rzybaw3zqp+drcd+LukalWfSpQV9q61f2OujH0rHHM4hJTydNBDgJNhQzmvz84QblpFMPIjNOllLYJ0EOHi94R8AuQVOxz5MpdG+ByenN/MbpQO1SS1p/fzEUnpbTf/dh59DaXmHnoa/fqVuT6B36lNZ2QoQ6yQbE8Zn7dLhYKSuBwuxEhPok2lJiJIUWe/Uu9SrcgAxG0SeCv1cf8cWTVEWYfcmutPwM3N99dwRRQkpIUtoxb4YC1yUpIS5nqaJ3vwg8RbLkerk0qnTaJYaPeQfCcGvh0RGZsoSqZ5VzLSpLXQcUNoyDWujpHjMYhCjspQDvQotKmL5QvAA4c8HPmv7BRjZdyAO18j4hU/A5qtzXzLLUBaijtS43OP0wu7rzkPflvPowO5jIZnrUGYkGo8rIWBdyzjM2iXRWjjw+gqyjzuHDCSECykfco2lxpptvDpLFEKU1d4+GH9PSihIQkja6sWeZKGowSZ7jozwdJr0XhaUZBLF+Omu/ftP1z527y2Q+qVsygd5L5Y3AAmc6mz92/P3Ll+ryvr2OhnMkTXL+WtMMgCJS4ZnCiLF/i3/fCnt8fLPIO556D4WRvfMLizTQnpYvnq/JJWt2t9NEPSTkQxaxrsR4CIyIXNQVuezfhLIhtoo+wzPg4Y5OadjC/PvV6TNCWNDyF/Ou+agUHcnhBCONjCxOFRnm36CyhRgS9hEBBlOb/kawLJnC6H2xP8dW++0NDFZw3whbTe4Lm3p2KRV35nvNmbrgeJj7DUP4ZzgMlgyq730D3u4iYgI36Ukh7x+WpC6S3y2v+t/WxXq79mRve7tvX6q8k0MRidKKOkOLQ1AMG2a7eKec+BVDXqxhDOSNVyiHMXP6TN3ul2SOXD6UJP4v3Oq6kOldAix4x/PDDT053ufuvf+u+uW2n86sqzLVEpaFEfcqHu7cHyGdH4H91URjwF7TstidfDwtM78A3ZEgXQII5EoXkv5kmRqGYoFa7BD87R6TA1hSmp//kZl9i7F3zyBvgvB1TDwduSs4tTw2zRD7qQIENMuBYtZQAAy3vRn2XiXNWa1faBYAeGiOq8lUKdk5GESY7OVseB9KtOWg9dLGZYQQmggGj6WTd0e2CTkQLmgMBQObbhglk7JF2dgLc+cQC1dcG0sfU/XhfMNtiYPrbmF0Kg4XFDh9aEPiScKMRqK6xhk7YTMI3vSLHjr+8G6RU0zSVs8LBtomWntwdxnYBgNDiX9/f6euY9nI5+dIccCcYCpvN9QrGfmqv4Eoujo+DoWq2xK17K45zcDo/ZZTTxHTAmp7POP/eFBwrHuHQGRwj+MtCjT1oN1OdLyTTtVic/ObP7Knu/Jd6VzBd+tshNu48smYnYpg9LBKtLo06r20Zqak646JwshCt25Lo2qZlbsNR1sTek60kPpdHAMpQkAd2pAKXg6i2TpWLAMjt+KgGxZ2hNkPu6tCbIaYyp24Zddy9o+6RgWp7HjbQszwfyuEHxGZlnCZEm5KE35CJnk9oVNnoqTsuRH2Q9GuV9/jjfl3nJgcK4DBYk7hyoBS7QKY+dv7Acf/ZnY0mOofKtsA7pVQ80bsRkyPOyGVAeSzuw0iIj1Z3YN17IumSYyLWt5Mn9ix+XkmneOc2dXY1aUtZ5cOP/H0Q/cez9eexyuGv/z2R+6f3/aMTblf7HtAf/nE4m8MDJYw9SxfP7ck+GkcnCGOj4Dw+xbaV/Eut+1a87JXydzPKPMk/JWoWloyk5XedZZwFUiklIJI1ktHbiL0bYYZnBbTPq2uPaLPrXMqEk5Az5d2oNrgj9A65BvlcwxUuPIUcz4737+EjV5iP9l13ymqQodnnbXH+ITRq1YMNmcv8dUL/5w8iUtwTkQZ3SCDgHpgKo+h3GUTg3ij9c+81Z1sA0vOUuvL8SOtIQU4H4LZx/Y/CQR0mk6mFGa+LPOKLCkxRTyqgxuvGzP5javjKFwEiKq8vz9isXDezxUVZmYmRsFwrtWT15fgiQCLxW0PNyNz/CQm4OlmLGUvankfCEl4D/YMuP+eOCxM6TL/9HWU/suqd3tt22/K34vAIvBrp+IOj1KXBNtXcRQNto0MRt4vrOqn4PiEcrg695mkHiVDrPS6OcQ6b6uVMXvcrDEefQTdL0d+Gcyu2aLrPCpf4+QiYxgLAtI3DsMU8nPJykGpiKw3H7XngOZeYuP/XM9/4GtMS2Lze7C4e76e5w2UMoDAd8y549YVkCUfuXpyJz7fxfevXy9Kay/f7r1Kf7G750X4KbKRjpHbHx6ZJ0HSh8wR3JUubUYuY43JBP3DFg5ULgUpGw/wrovtP17C4gWz3ZtvehJsDyybFWh8/rnxG/B8CDlKhHgtIU6H9gXW2IYPkGk9Uc3v3LvMa39L29YGltBjPs44dekpEV21H/Y9y16xRoE0kqoM/f89S+g8ly741skOof29AmCVrxCbd/mqnNXohekUxUAyizUctPuN7cvbZWY1rQcTrziaF14QGK0nyIlEjhlETIo3AqvoVgicgC1hZSw8di+YLjp1whV3InrWsVglp0hTlPKc4P5lnWZazoX0oDhBRHZPis8TyyNRlrW2e5MKQzJ/vH+tQW3iOyPSkvQcaKjN5LsSQENBsqyTiN+MPaByj0Alav44corQrLST9oeB+TS3LwvaQ3PNe2+hjPvpdebtsA7LZ8q4/7PQUpgojIcyFyozXDixpj/yPNkf4h2q6zASINYjoe9dpul7tl7/fsCzL5AB5dmgG8B0E3SOj2oEYSm0n6elngujhVZSrlkHSbdQAghDPNkf9WsKu6LzNCC9WFXIEWxQ/Bn2SvwPXRFWuKdikmvPOr77MyXEAjdqXlQNBtw8hDdXqx5x6sEosfhLf9e8TC+0845rSjrTZr+g+RBmmx6o1T9yq7fq+H5inPL1v9l+lvK8BDkuvkrzRzAyfkAgmuyWAc9aHbICXanbmnuOY7tqlh3auv8bfct9977ZeMTKLzlJ+//fvmO3ccIYDQBYqVRxtJI0bUbMho89pZtsANAmxxOdNAHUvdkpmjxhTlXeb9Ww5COvNjNDPeNzD8zRSQGzHUFTwCMjcpabP/CftDZexDGk5ITsT55X1oOgy0Cwvx7caW20tnqRbPTOowvW9QH/kfxkNKuE861ziGNCHFbIy19IbWsmmZ85nSU8/I5lwmSZzIm6aE66fjuzjv2A3JCOprBoaP4mH97YnvzX1/7O/deM2M3+jdd+5yyKW8W7XM393zJjCDmKTLQ+WNv8jLX7P4fNjx28Odjwx8uVUwQc0IcXgTvRDZrSd8XoVlEh0cPYWK5kkjt3tRgZyWzBYMbsrjxHDLSej5Hqb/0UjjOwL47z+K9ir9+sRQPXh0NUE5j8K6MEXh+403JYACcrXIJLDumLuy7pte8o98oWpZIpUye9i3Dc6ts6/l+2fPpPFY9BgySZT479bXoKroOiWkdZfxvoSBwSoIYOi/aAMCMN7Nhy7ZPMGQQjCzbfj+6zHLTDiwnWRJCqEHHq3/jW+LhIRAzJwa90EK6HQJ5s5pfhNWaPYBfX/go7LhtB3xvx6z0h4199zlmfH52esu9t3ctXo9kBiM1Qi0yLYRJy2tYhMLLwHT1ksZG29JZrpbDwzkdPb+AxmXIPRAXIBgYJ36nhyT+rV43o3reY14cHlaxlkJ8dva+WeMK2td7e/63rH9mX9C57RfoMmGl0cnZ/V+tc+H0L9+76uNvdjBzIwcYRw4O69BL2rhylJYF+W929BUu/HdNmvbAZsX4Q5YLZyJpfqeLJmuKGIsIloqXoVi6NCyQPf122TcUPOmbBz+XU+cMOfWPKs8uX/9q4Pc+S+QjESYmgJr7fbwtEWVC23AfQVKQA5pYA/KQaUTvMU6SJUdJkZQRuq5Y2iwer17aE4iDMAOpnGK/SGKnpf08MDDDtzz2h9kaF81/A+V4liS5/0bbqm+Ge2wqmyiCX3E8id0iZi8tJSGWmV4OPDaBGpKnE1scisuh8sCNkm+pJeP4ZOD3BDnl5mydF8oFx60lTMtsZoiIIuOx8RxVJvg5504EZhmQLFn9kECC+Mm4rtSrxmu3pY/FWDLNTZG10YY3DN20Ga/Zjp+82xvmxfJhfyVpOkZ27zS9t/vvTy3jU835jVIr2g/opC0lrYRJhS+Qri5rNA68AsistBW4fAJnJSdzhcs3vvDvDVvmDZXw00pn/vAg67CSXnEUL/zf5VHSGGwh9aygYvxTO46g0eiyP5VXfh5bD4DM96Vl5wDoIU9w/OF///7l6/LRCqbvqcOWdl6i+5yCFTniOudCkAMSB3omiRtDpwn7Jc3DWDKPzNwoRw//ndLAmV1awDkMT3zZ5ytw1Tys+ezJAlZVCdI4KC2h2L4WmNWLOWkvZtcPDi520ITggyKl8Ce2b1TwZYVsC6zryODps8ig3DOr+4e9gLo0o/2cZHcpU0K+nlUZ7XdZG8VBKqz1pRdgTBYCyAkwMKWaX6hzPKMJOu6WMhXjF2YYVQMqf27PndnnaU2dS/xDyxX4bcrXNue2pYxRX8CMGMrs3UI9lA04P9p65j5H8t27VR9IvDq1xXL3ht9zRx3bZ3Ws7cGhP1DooMwlQ5U+QuIgoRucJcqI/Ew7fk5m9yz54PiMVAGBoGUlj0wYiZ8kPYaWtEhOxwO4/sQvkosiPPeMN4hfotz15cIAzNev+fT0wZE9xGs7Hh06mtlPmAt78MWRPeAMUq1asyTALSPMj4ysCcRVUG7vmn1u7UtJEeKEUMZqOjmFDrIn10TX57kZ7fY9ScsDa8VSYwh+z9afxeNoGGFqpxeFY1lqU9XzYsvuvy4CoRdvI+KW649AcLn9S7uRzu0EMZU3NFymitkz+U3kX4mSrNnQUliOYFiSUorRaH5mr6niHIJv7aQD5UDrQey5KoXjOWfFUWJ5zZVwJIPxo7vGz/DXLx+49/7Vg59dvtYU/Z83/+ny9S8Gdy5f/9X+++5zN2tmC86lQ7PQtJueoJyRFuHgMTK/+wOPJWqi/NBteoeHxwBtCw/uEEIoH8BObvrvdlT8dQZW8pyQ8clLB9cqjFScwLKA9UfXAeZVbidwnA2PhQIAhxsdyGXTO5CFZ2ZjJuLAE0y+ANB+Xpf0JZoS0i2pBLiWbE2z20se9goxYFCuQtiEfPzRtuFdyxn/O99rWGfyJ+1d997Da5Zu+/RTj2lt3rBqyGBydTNOCCFk0RU3yeoc4HNSIifb/ByVnozIa4QY4d0QQhhXgBHCGlH27TSwd8tvqy2do3tDCNLgM1CeXY3qDO1pGYlwlofm0W6/Y95od+QPuhr4ME7a3htlmq7fkxY6p5CLN4YyedhE6JT/+j38TuoNqX4Rh3YhJYmwkiqB3VDqUI2b8QygdDwi2RPoYvXBa6MNKUnOnHOG6CuK7c137btGmyIc6FTQQ+x7x98FRmj1IDwO00VGXFVEd4KKQmbB36UdORk6QMQByTNpPbTPqbPFkYQz4n0owJSdgOqk154C74X6earmAwkyqX+440n9ngzMybkhTJd9EKt1AW5hcBNCCCPw8CjzOQep8jXyX87iU2dnAzt4I+RsOWACkTGOpO/hJ6nRJqCZYHW1H2RnX0mqBk4pqUCkk5UYyJRE5WwGmF7350TuiU0i98Fcnh3nWm0Hn8vYoDKhcChr6jrAvJJVIEZlOpLAkiz8bKGWslsZ5ai8bNx/OrUMw4c3Xtg9iWjuXx2Y4z8RDp0SOiM3bvl91Rvavrq1bu8pbnVMxn8FhrMD7ViMEn62W+uyNZ18RFvnEc+UGTAhHiRdjMpBJY1Eh6f2zL5EZSZoe9g11PaY4pCGlz8fySG4ZSdpDbITGyXvOhILcND1iD7yb6igJ+uq5EYgJ0MIIXTvxLdCO8VqzL8eQA6YLKU74mhGUtd2eknQk1FtJhIJRRweMi1f+IXBtvQSNLH621KCIY+TMC2zPFN9ZTd88ZbwMNA7l0OYrMwLKflRTZ5zENF3WoHBqLwO2ozebdkfFN8UzR9HnhYpR9lLRlDaKeW674pi3XENtqxHun9wDQX+OXkK4UzpQQKIBws7DkPwDOmqZr4Gvq4HAsokVudjdF9tVXx7LrM6JFULwZdBOB8RyRa0zy7EMq+VrEvr6Stfo83A2BNwq/gb4vdUIZzaZswcakaNGXTldFqF4UogcEIWiifEQapz4TCKesgSVoCM+7wiXGUbUFU/k4gOtmSGxoOJkOLx0I50WCE4qX3uv5sBwhT0A7l1zx2Vw4bfKvr1fOumRdFfjQ34d7fgS1Pktjsb+331s0e3L19/564XJ23nbY9MhbeKg5m5SGcn1reSlWZi2OUjJS1mlGQ75i5wnt+1hzYXzJSji/kGQP5Eh4eq2QXhGZlWAU5DSWjhn6HzplOZ+M361Uvr6/1IHlRrbA9qLFFeDkDJctMvriEBYxAYGwjTLx9OlLXWXpMcq3gaf2iPRQ2c3DIDyYAR9EnCvwi9PJyOoi/bOnBg85Gwgzbtd3duxTsTzpkTh5k4E2aGVBcsi9+Z1kgUWQ4F2ebAYcQ1N6m+/kL+nQ12N3Btq2wD2iaVGp9aMjpPA2A0yDQ635C0M+ZsoZU/rGfNznDQSY/gwmiTxJ+is1+Ecnq/JrwrYAt/r+lTp3/z/P7l64+afr8XkUYkMFm7tKpodZ+W/ByfoIzFFPp0TSYcmQbtJOtP7RoF6QCagHhVpRA4XOlGljPtSeEcGYhmvAMbOTxWYJAjapki6DTewRvflPnkc6n6U3ZeAOD4CNeXLi2Sxs77QiOAOSyxzPgDz60xBSnevCTnBLI1gxsCdkaVgJQISli5gzT+utSE0jiI/sf/9M8vX/9P/9X/5j7XnipPBAYEWlV38hTGm3tnceyDhTLkMBQzRq0xxfCQi4h7QklNJ+D3m0oJmHCTLPa7rqXQMyOkZJ9JI9Hh6QMPoqUSUgMwOtHaaWrHrHup5FOVDzaMb4PpaVVVH8LwkHsjhBAG4M6gGmwIIWSQUl27aaCE9qmAoidcyJLyRiRCVLqS/1GIUSM0er5zSak7/g2nUO2v4Q4nyf7wADqXrAvPiBz3diTle/VrHaM1gKdl37GcqRkJBvFaguGhTL4kVWNfheEECrGMtJTB9mGttpB7ZyLtrdTWGlyPx3Xwmauz5XA1nEK5D+5h1YJjmVTLQOT2AV1IWL/l9+aP1i0F9uvudffe//DuTy9fP5d2nj+pfXL5mqzrytfzsm/YPqXATxNvQSyJsKzXdmxjbUjf/icgZ9tueCTxy23bZ/lHIONUckcadAU0o8Q42op/1klO6yoMaiPlAeYdC0vvFFI/GcnwzAljGMUfYOTkCQkBtOLmisfo6AMGbdbyh30K2Jy06n2hFJORIIYl4SVwL6m6jyz3QFy1m/X7hdIp//Kjn1++/l9e/YH73H+9/uXl6593fOdifsOiUCX6JUbofIAmHnEch4AFqVPJ7Ntkxxty4rA4SOWhYyEMylQwmJ0C3iC2i+Xz3P635PDQkWE0G0IIza9wY4jYGa2HEEKrYAt5KJ07B3nLiWWxeMvSbZWF7oemnZ98Zilvsj+HEMJsZAuvlbIHPNnxJzq9VgUtU8zMrR9N1JAhV41eglI4nYvGI/uu4ZZ8AdZF9cAvoPY9+0J1tuio0tEYSzqykFA+yg2uzrpodkL4sfx9EE6l2RCsMydoKVnFVRguE4ZUe14yfowAJ5LxIxYgJ0yjXDsE9GnnHMHuor3pDlkaer0Gx9pn0mxARgB1lCjwx+7QBBwNda9C8MRq/23zF+697sIcCLasl4SZlhmf/thHCEyBp8jnIdgRdnc1cz7a/4ObBrr+9EK4BTDITULm6RA85iuUZeEjMnUZO7HKTjx09SpaIQMnkq3Q2bJIRPTopfv1xizobEckKYo2b/MJSaYkCGBXj8hTDJF5YkCQFjZlkkhGSsDbWH8b3tAOwctGwdDpLX8Rdh0SkB+Cd/Spq/UXm14+og29mHVx0v/8nnUzPO75Q5t7pFKwOVZs2RKOXf5AJY5YkpD5Z/czhWJljjnnKlo87+L7eFtaecTnBrdeX1E30eGJw6+E4FucaRDLx/G8Bhqvnzy2B3L/PePeeXnRdJ/7cNeIB0diOK+/ZfXNnhi99pnl6VNs1RXQ8pKsjZv+5HcGHSnNUdkvBHYPMD0dQgjrn9jkTZr+u4mX6V2PPzBYPlKOHjpNm78UEcUf2inHklOE64NZOskgFRC9xBEZhiCMzFJmGa2j9i7ZH5bo5gng6ZUYMKoZx53iP+YyPBrNIquTVtAs1xsyKzoXIzrEGv28ZkmLZc3RhqwHppPFKFEqY7oBkGTZr70zpCI3Cr7WTR6eVtXf5N/3rdzVn7GLytsFjmHfLziSmy0SgMmMHFV5muPw3EcIWVx/gudLIHIIwZex+t52Mfjhc0rJ7RJsPiurFX3zYw6gqdMvfCHZEzoyPT8XMzgT+X1Jb79ta4e4oKVkFBbkbVGyTJ7FxE2KFAgbcLLCmcZrzuQcyuCcG+7gPoQLDYH1fAAAIABJREFUhwH7WDxbyiZtox32/zr+0H3udAgwvZStKMidFbbpIoD3xL5GdM2YgRbeKoK8R6InxgzPkrjPp34eu/dt75QO/TMkV9hiBxnBfaWkR5au8S11adHJUeZcHmj8nCphR2pvHHWbsN2yPWDl09kt2pd3JI1AB6g/0faoq792KWltHkiFomSJgIKfgNwss+4do/m5Tcis6B9i6/7V4OkQPKiUG1EdBiqYawRIh+Ho96WlE18XyRphEJujmI3WQ2QkhCPEfRfXi3yO2YWIJAVwO507yFYN4+/3TQ12540Q9E+kxOm4cMSguC4tARw7ZWy2qCtBHqEhGmmx0Qs2ScHoBNBHBEIxSq+ELHODjrO9d6vqv+DVyJyE96qv3HsEW/YFhESR4b89unP5erfiF9Vh1xyqXMEb5nHbrpnB4ZoSDi7y3xxJ2nMM2/LOnscgHfft/o8BBp3LIeOcUbE7ixwAvkjtV4788+QBwcBqVUYK7dtzlEAyd/xGz2MNT4WLZUm+onseHLggrQCxZRK0LcDRk5JSjMv4IPOvSUnev2Z4stD4mokkAp8fMyQajJzDo/qLxq/cey+mlgD4wX9m772DLMuv+75zXw6du2emJ8/ObE5YCIEIFMAsSAQVTIlKLmfZcpUtuaxgl8tVKlly2f8o0SWXVLJsUjQtUqZKsiBAIiWSIgFIRF5wA3Z3dnYnh87dr18O13/MoM/nfF+/hwULhWku76lC4c7e2/fd+7u/3/md8D3fU/N08AdrV8J1r3Y9Pfx/XflwONeo+VzUZtpM+1bQxHRTU06kQdiOe1nnJLoqKEs1KB8GGMdRUaqicX+NOtPIYQsKBeuHCuwpBLAq01tLkE5AN1ngK5jSap6J143moXGlzxap6K/uucY9Xove4NWmn1uXag9ycVxdjyG8knQ+94eXvCRyxgNBrw/6/u/yNVei/PBmFpoJKiCYY6cpP5KKcUNSo2aHaSsFHPNR5NsHwDEn6Bhy3g9nryqhIKMVeN6deB0rwtRADs8lz9idP3pKfJJw7EO6UHBPnGG5aQtS7RiWhCKCpL2uphnHxIbQoNK1SdZeYkjMIhC1J4GVsBEQUiGWMpuAah+sjyy6Eq9Lc7inqx7tXV/29d4aRIeGaXAtS6dCDKy+otv5d1UJWbYRblRvOXRnnxI9Jh+JpqOIy0vRQ26svxN1i1aZHQEpAAbQw0bXkwabBBWPhGGXINeRtCIIWJqExrZEcUgFUY7fcoh7poff7v45wt/EWWcULleRaCCJKLFRJxKFIjB5IRcNu081HY/zp5evHhzfGsaQ+1f3UIl1LNI9sAHu1n40eLogAp2fxW9L6pGkncrOXrnj79k9E5VeGc77AA6g4n/53QYLcS8m9o6R2YIymC9O7mo/TaYaPHPX/WGU0I6VWQTRaogwj75VtSmg5TcAJFYswLQw3U7Pd4JHT8Tyvct3/Z4DlrFpCqDuz9jdkdA4nj80QROPu7w+ebPn5tScTCobjBzd4HhPTS+GEmVNA7HMEsM/e0tYPi8CjFyPAzR74/BQWXdOqhgYdRB9UIByV4Mt4MNwj7GmlUdA+KzscD3e0oOVK3ITzJ0xQDCo3FnaroR50/hYJrE86z3CHFPFjzmlBjyxSwZvVnsDfWD2+sGxhuXPHvOw5GohkgC91fOKTRoh1xoaRnPpC+VFAiXOfP+0CIlGjwmYfmM9FjqQ/ytE4mQeFMAzMlqNA9mfIf2+/3ed96HsWykIjoD0T8MrH0y2JgatyRieAhiwFcNDRuryTZALSlNKgm+V54dzn0ZocSNex/WoBg+jOKXX4kdi3608+kMN56MirMEDXR9G5/375hx/8yKiODmZVI/XPdrYkVw35/DphbiuWij+YVl6bjMqjIDFke80fAJpa9krW2wJwl5zUnWYnPHIXyqNvI0VY6jYGssU0ditf4dSWo1ziG7syEMjbh5wHZKGoAe1txG9vLto/XBmwSM1zy3E8PcrYJOcK0alwVLSG3vRFR0yOoPO6Vo9UEAn1n4rDkmhdHiuPi8WfvcY8pcFUarcxOsyjiQ2RH8QVcyjaRVW+N71e/FkB4BZYrJ2L2iY0Y819cXqyYW3wfh8Po4VI1Sq+NkIVKNXNbwbS/ibJ49e5CcAVNFNWSOg9CLzsh5HAI7khQywj/uf/lUfi3vvn0xap9G64FHxO4jSSLA2U6lq4b7FaM/9eyINi6qNGYmQ3Os7MPlkNXoBX969cHCsEZ5duNIjDKR2XF9r+IaRk4qddBMU+FCO9RuCV3jM77/fjwYPHa/5WgSp7jSx4eGWQ3H4mMbSsnruVTQqtXKFot/pSEiDgGBQKQhwmJH0RM9xA5PpVr6BVBX34uU4H3KYl8rJRlucWBMtYMnjXUZirBBb1D4bz5FNmAZDeW5yNcjX2+fDv8+UPF/+M7c/dHD8x05+KVw3xJrYnFKJ8MhMZMd9Zdv3UYL1R0vSuwzpRq3gCiIOTspviHMl7QOYAlcqgQM6BbkNX8O6rnKI+Iy0GGCKvGOmZSXkI+aDHqWCKzkIC8djDv72dXSHfcZD3Lfasfx0puAfRPuPrDXcyAnN/iwC6MjymWoTv21wagiJ0ZBMllyk6rXjuxWEi4hEmamktI5/1d9n7b1oQKeeHBvvyRhTCXTlO7Fqi+R/zdV4HTuWK1cQh7wVDLv4HDM3mbeN9+gskW5anh/3H9SSidcdBaGhz3YAauiz6kbZlMnEWtyRKBnWy90P+bHSuNNYUSN6Ui8tBR/TGNL0AP+t0boQjUD+XwGU2zBcnp+5Ec6Ra+daN5ZvUokT2KkGQ+gHdE+IhJhKhwJvRrZ9qyDPe7sZc0lnZtxDLhXiu/V7DMdOmbMwxPLSSJJ2njoBlOA8TOH8OQrClNtQo9koJVaSShp8fUlRdC/4ycJd19UjYRlOFD8FYYSgiCjqKG41NgQHUF6YoofsmaZpajjDs5f9uVpLcX2zEfY3mrEtBIVGjvaa+8a+/52uCaZ9a4VoyJSwPve7Po5zixFrtdednIYYgKfIqnF88uDEY3fzzonJ+EMt6EixHgNjtabLr/n47woH1zSZTjwIZl72ijKL+WduwDPX43V7qP4oiBe2sOpeHym31VPsw6J9cSMCEZ5d9mjQnUpUWK/f9dB4/64rXy1fT7HBr5yNwMsd5EGH8BpG29Ku/m2mIsKp8K20JcX6C/A2wBRa3hAsEasHZPNjBdcYMRl+uwV2Zb2OhpLmtUOIHRutOhfsz6VEdlTaClqevemKcOuJo1ia5ULjjBWIQwGjMyqiKWamrSoyH5ge5jdiyNwsRpeK8gOcf5WQ5Y3XtVURQYj96i5J+gE8PMOT/u0WZWK+ifYR9/JxZ/mJBVfoBHKamc1jgrDPlqa6OxVXX1srAnRlY1TS1Qumorntk1vbR/D3uEGYmQ1QcVVkW4Rl8fzZj6kaz7GAgc6DUlewSkudqaMgjDqlF30OlN+MCiL0s9JoI9EC0sOKkaLYliBuXynaGI2laEv87ckVGjNvuF5vPiObMfAmee3tBEOvjWKGUil+85cavn99z/xb4dwuylePFXxvfBHYHjOzp2a8l5ZGeNp5f/7tXsTwvPW2P9jjl3zfvLElqWLqE3GSisxsXIm/3TvmuoAwkZxkVJg21L2meMOfv3cM95DS9r33uyevjsQ0md48FKkN9eYJ4Fq87BPh9sekSgsRk83rMrAo7bw545EaZYgkueBjCxGnw34kJckdMKU1d8FTZnu7cSLk19AXR9I5o+HhaZW8hAF7Cz5ZxyraYBspwJT/Jh26lrcyFM+KLTOzNigCivuySPH4jLooroTfV5+RBhXXl3bfJtW4gnj5HFr2vvOYf19u1kextQSFkZtCU40OP6dOAN9fsTiBKBI8PN2SGlR+3J5MERPICzW0TG98IJUa0/A9gXMKES8FJjNF9GInOio/MusNQr/eiOdWSr6rEzi8JyknEpSqpztECJq4krGWBqjg6ixEJbeO8t9E7p9D4cPMdfB9Ca6ttAlj6ISkAAh3gcorSBSY36I/xUh9WJJAj9NDr0U8rTXRj3E0G+dbD93TNY2STEjxJRKpJ5VCIuS1ORTIjEr4zjIfmhfx2/vRqMnDUa68LT24APpnD66cPAfncEvAdsT3/OLOcwfHbKZrZna1505ARRTtra7vo91CfMaFE55hoTG/PBsdlbtvuQMyFKeWrUPSM7oBYLwG3JPiZe2T2DsF5F++Bjbuk5OxjjkYYvnCd8jgYXWGeuX899aTSHNIWJGTtboUB5b8OmSF3GxHg+STp/yDa2dlcme8dCsysKUwVsoIt2mJL3vhbL0Z63Nzx/FR8XGGyvMAp0S5T6iwtGknPRsSxpUk1cGy530BbSxe9jFonFZ+CHhfTJNrFgQKd4z4jOsSf6el58TfKHiaPDxjrN34d9Xx6dbTdgdHTTCPlFuIOIy9SzrYftiWqHZoUIiGpEXpx9Xx4OV481CkPQa4h85Lbv7ssG4W07Da0Zg6dgTA4yvSufnHTnjZ7d+9/L3h3Kfm3ntw/Fg99tJ6ff9wC04Z2NdQlt6RxsGFO5i0AKlqKjqkOgpxoyVnSkN6daWIGu08iTYiW1LccQxpdW2Yye/GFKU4O4E5+whmtEpouzHAprf3UYm6sfqqHiftEJvz0ukItqUju7aFiJ84nWRozkkj2xFbEWA8R6LHy2sgg5RIerOK9kSPy28j4lNc8T2jtxXnTQ+QgH2pTGGE9I8ufcEmCY2VGfE632i4Yvj+lTfCubcbvrcR+0ows5nZELw28yej997YA6u48FuVZn1M6jX3VrfT6OGSQiKV6EzzvN8zD4NzzEFnpqH9jpE534J4EDrk5OfjB95+wgeJm3ZlPQ7C8JT/Xa8bd0FtKPhN+YkzXwn//se3XTmeqcfFsFrzD/JDl14P5/7lK88eHOdhiBUkN5s75tZbpSw9XhAlamHCF6W3Tn8BH0qo0dmfq3dS+CHguRdmcM/tuIPSGWgfD6dsUGE83EQOj+pU1+QyQmzUIIGVT2CyTkLigsa6vTMtIp4ZN2xGdQZH0OAJGIUpURCCzsdo7tf9HqS5NxMyR6bMBNPFb6SRG26KfQLtpUqLEb/a3XDK9s/53zHVahaNstGKP7D2orqF/it/49l/FM691j11cPzVRgRvPlr3KO4Xt/ycFiwEMlFZ06HsmU0rZX0k8A6pzM3M5qu+mejGyMgQ9V9J2METOF3dikQd+H3hwSp1Bde+ci4dBelt+LgVwUDcF9K9aeXDbBo5FALWdYDT2aMur3xq2DxH/TjWAVTMaIHoanZLT+QeoUml4FdYmt9HU1NGlsyi8TYvjRuvdhzL9h/O+V62M4rG4RcQju+Id/qxFW878UYrOg6nsXfeAV7t7npMNxPrNxIno1Lz9d4Unp8+Kq72MHZa4MPu5kWhjkng2HFdKQ1MAet2GrGoylSDp+50GHbr48KpAO8+MLYKnweR80vzMba13faF8j2rXsK6Ic2iSER4TBhbGRZU5sranE8oGjxapTV/DCF0Mcq49PKlyVGiAEQVz6DL4jHFV+E+Kdev4j4QUVNPPUQ15e84lCGapIRPE6I4en+S1WnzUG4m41iiyXlhcpAQL5SXqOJRkGB4YDzLYnR0wLaq7SOCkSPjxKgOGbsVFxaoCMZ6mvkxv7mmKjln+zOyobPUWjuAYwy4Js7VIiDpHnKcbBdhFtdtXXKXReT5pqWtyBy7Lw0QQ4UQsY8amZ3S6JFEpkyPm0knbe6fknIidYHOe/YY4nONReKm0BgcCcEjBSNHelEZvXnZpPqngOEUDp3dLXg+xIdr4BTjVHstTnY6XEVi0MSoGZyAITNUgAm+l+BRAx0LnoNVvmYxOtOQCM8zNd9wvwDg8GwuRnF+V/XqwfH1fsxI3MS/l2QjagLQfHbGLfO1evQs+zfcAOrMCVM0DHhtGZGgUIARNXL3mJl1gEfs5yUjgfm95J02rHt8srFckuDDNJlq8BAboGHzSc0sFZPAiEm1FB/sFDrHPorukiuFmCupLPnfPVW5Fc59fv/xg+O39yLx4Ik5v8/xqn/8wemovNiry2Tz2NjzyVAAAK0rIfRQKSVQpQrSNDkhNuw8AkAeiLrG8CtMJQljLonKlAmZYdlpTUH5e5qqijggHI8Zb37YFcVfAXtsXaIJ5LYhoeXOY0fPm2WqipEOjYppFCsIxklZmJli4ViP8fDQ7tIGfLgnG5WOtQLBM3elGzvnm/aHCi1nTvnEPy7gtQ/MvH1w/E833xvPzV09OL5AqnCLDREfmXUjarMbrb5GG9WVq9H6TkhyimPF8LCcvSxszb0++irtR8U/WEUvIpDOqaE/xsDN357Q5HUaz5Y+/5EQVDYx6lZUnCOMoVQMjQRUDdrGI0VUi6tgcUE8S8jGJXFcaVDC+EnEKKMBP1SGgUlEtmbWqxyOXxmJHlgo+Tz9p9cjN9V/9/gvHnrvD5XjNr058vfeFJDN+ZKvpc9tPBrPoUz9DiqhH1vZCNd9/ZRvgnUBXbdugN1cWm8w+JgCx9Q5ESd06ZiPgfJnjU76RtS548pLU19FRHQ76++8u+5Ug6eI7JGyfwYkOsK4AwGjMS/eG0gDQdx0d84H+WQx7uik2V7ORbf/Lfz4c4uRv+d6y7XNbNGt5LWtGEE6say0wIc/fw5Ror4AwoY5VGnJGPSxqLQCg2HZ4ch/a/Z6nEy7F7GJjRGT+bF2GGdlFjFZajTxHj1tLIrvG6qtRCH0QiWZbOTQBy0Bb9LY6iHSUIrZyyMnfKexqBsMFEZtzAQTIxskyYQZ8R4uKnJ4MgB9kmE7jEHaEJVSsjsCsnXOElA/BEahK3nM0wWfZM/MRATr53dcGX904c1w7nbLJ+AppKy1j94Q3qamMIgvoHGYSGSBofG5avSk99HBO5mJg8q+SsQ/5SSCRCNzKOsqGEc4rgjYt7vC1ObRcwIKMBRZyVqWzZL4nnQvzhVWUQ1T5d04/HhrM0YmFoERLdQ1jcIOxbjJTLyOUaPSzbhg+o8AjyUGcIp5VNwC8aBEeEgi+AOnL4dzdwduhFwo0giJv7WOWyq7OQ3nFxZuhlN7WOSvrTku4lExeFho1Lwbx7hwHMbKfFyPrODqo5mq9iTr1Ql4FRwQeIs6gOuO0WbwvaVzwjSZHuE5hQqPnfiDVOJdpCQ0t7nb8Bf/PY99I5z7/nn/9xxotk8VogHSQc7yK52Y7//hut+DnWjNYrfYPsIR7zkXo0Q7XVfaGja/u+aT8PSqK3ANu97d9OuGQoFP73kkvXwqUAoMr977SLxu9g1/fq3KYWqpcT5+J0Z4QpsB5SNBt29Nn3Azr8Gm1LRYMIbE+SLPDwHMZmaVbdAfnMRG8s7B9981IZicJISaKmGljYJQLUes02RAMzdBbSkwzUANzKZTOpgT79gVgyo0rBRjiBt17Zh/aBKnmZk10UHwA9W3wzmSCxK7YGb29Lwzye7DSutJdDSP6ExXiOaIzUmaIK7TdgRwYm7fiOmBxVUPdSpAM/RVmpkMik7AtJzIOUbzWHGkfc043uSQOSrCihlWbPWEJye0/1iI+pMkc9VHov4nXcDWNffaclLaznPKvhsIBRfABySpKQJqC89FjyvgsaR6lwScoc9Wc/IWq1WHdxJ//hqwP399+/Fw3ZstN1Z+cOHVcC6PPPv19uQGeY8fW594jj2scjVpEMpzmta764pi7oKP3d62eIP4FqqfemCKzj/iwY2pTDvfhg8wPcIDLhH18tj7ZMbhN9Z9MoaWLy17iO313Yi2ZYUVw9+as1yHa/QhUZwvAfw4FHd5E2WlH1i6dnD8K/fiBGKuPicAlhxCaTdvuQE1K2RNxN/kJExKoJ2mJvhrx1Z8oW/sxPRcR4DK4RzwIskYqJEcHpNBxUwnD7UEGn8XiuRE905rQNo4h7kkEQgaOSyPHx093R6MHAKTdX0Qa6GkhORf0bEYYzU+5G/G7jllwdNRUcOleQbpOXnGLoDKec3V0wtGaFnXDktr/8DiV8O5D89cPvQ6M7MhXmit7dHYhXLUC1d2YflriTINFDoZ8i7chM+ej57u2i5JkcKp0KGZHCH6/Vh5md+erG51jlA4tzRaeBSEaZvQC0mMglFn8rbFirW+4KX6MJxCXzGpqssjVaKppFEOaUfMjcrl6Jw2L/qCnDsWvTZSMFRn41zs7CISgvmgTaY5nx+djUbHrbYbPD+TelPQH53/eriO5esNAfB9APier1QuhHPcb19reEVlSWhgmJtKlF8MY65VjS2MzwDfvqCgZQD+S6txH12a8X9vNdxQGohD0264gVyqT041qkw1eJhS0HA40xBsSlgWYB4jJvTczGLO/4Wyh99uDCJqfDXvD/JKL5a+9pBXUGKy07P+d9t9H7zrt6JHWZ/3hbK/Ga3R4yfB31NEJGhKV2SlVK/ccuuCrKFmZr1bbpQNL/mHGwNXYmOkEjWzoOzZEFL/jozJir8Z8rVlHOeuIgIDAkpNc5YBxVCDKjTek/nJ56ITPw1z9LCEncM7x1k2rsBeHEun32AQjyb/HSNDen8aL4XWZIOKQOWhNGwkhkQNKkYcclKyHjpDE7ArqYirTTfaX62eDudOIW1dlLVU1A6fD4SRWDOzBM4IPWyzGBXJo4JGe/1p01FKD9UkphU7jGpg/qpBQsC69n6i81OFapSajRCxG+spdARkABxGio0pJ0SLgU+nId/rlHC6QGbAqL1Z8TkwEMMopDU1zcvGovgMrbPC+YPxbXcEt4V/K2i5dhtpoMdR6aX9xDAxRwL4qub97/7Rb77v4PgTvzt2VScpoZJ25mGZX6rEUtw7fTeo2LFgrR3vcfycr817t2K7piaYlrlvmsWUVhddD8qVqPBXH/fnuv1K3M/rz/tvt8pgSBfjuYJ7KiXFNJlq8HCTmr0WP9weMCWGed2QnF/lmK/kR2vR4Hmh4qGhdYCvzkozwb9574cOjt83ezWcu9kDKl3yKF+5AvDj874bs3pLRdtfbG77+ywsuPW53xbeD3i6M1+SknJG8ARRXsakWaz6/XdPSnO6qv8eFbhZxMtI540IaCbQfAob9Fg3c7AwE7yuOCCmwjSlRX4dxRmF+2N4tArsKEjrHDx7RHgUtEwMTHkzniQpoRpDAQiNedObU4OEx/GDlVAZRIbmMaK2KWBYzrExckT8HUk870hb9Q8uXj04vix52Bf3fW2eELDzL9168uCYhQ7re1G3MGKQLIiT8Rao7U8BVCutY+gk3XwrcnyVUGI9KMrmStb1Ijx66YHHNLKOcQHX7j6FiJp0hmaUTh2JoyBFGDYjkhDeiTosXcbGNyc8OeDo6YlN11xFRBSbqqZURjSAtEs5frqACEPvVHwOpkIL34hKMnnalZoaMs2nDm+cmZNvvtXy9bK6Eve5t9vuiP/lD/2zg+NXO9FZYHr4lXYk7fwne14c8P0zMd1Fg4fUD7PSzfmlnvPZ5YQYMB35+DS3FUx6+CbStRgtGcCAHc3FdXt1zZ2kMgqeBs048Utv+zim55WIbLJMNXi4+eyfEWXJiHo64YTFdvWfufdsOJdHKublpn/Un1j+Yrjuo/Me/r7dj0r1Xtd3dGWdfOysG1h95EfyEoUqIUesDQrZDoNVGyqBYCpmowLnjZa+9geHh3mHymHBqhxJOfUmVA6ZRU+Xiz5RYDvTFAIWpiHDCMS0jt1qAPRhH3YX4hwha3BgfJ7Q9fuhCh+d+WzBhijmhsJSZUaJzOIGWUJ6pLcohJ49brLx/gEzxrkh5IWB80jmQxXRduV94rsVYMC/2ojEn9+z4IpZsXGsuKrKuj0371HVN7d8Mc1U4trsVHyCaNUPK0M4VmNVcaTNOBMnPnvzpcohg/fhGgt4HovzorQdF0XAgE1pnsmshUbbjoKwFJ3phaHqBwK9JcKTP+GTeCC6j1E44kv60mmb5/KiB8kknifXi/A3BYqB4/otEZ2R6qJ89XAsZkHY/4lDG0qEh2Xkn9112MULxIyI6D0+WneywbuSKSHR4XrXnYeylGBv7kHha2X+gq9BbZvRbqFjARyCWi2u2z0EC+ZWIs0MubC6wAAmEtnsPg3KGXvnMp2iEL+hZdIEOZI8Lz8fw1drIBf87y/9i4k/1UD97N+98/3h3EcWvYpjW0ITu30foK/2Ys+RHfD8vGoeOtNQJY0OxSHUSyBawocaCohx9g2A4rQNB4yEbiNGhvLwiK686kafbqBa9UThfpGXNFAZFR+wKcfaQpBpWTMK7Iu18IaPz94FSXWw5YBmAPqTz7F0l8+lmJOjIExD0JjQZyWWQ/tg1VGBp8Ds2h0YQ+S0GupYAwQ+Bd9FojptHsrUiabMaORoCrK7AiXOklBhjX4LHitpJ8wi1861VvQQlkuu+K8kfm67Ea2ylIDQsgwkFCRJ5zSiNkLJeisfN9DODlLYZd29D/f4chpBAn6vPzc5pThGa8DrsHbGmgofAWGDaDZbHh6TiQPDpXo2RtIH1MEy1gt139z2Ev8mc4txsyTX2mZdeyL4dxmm0MFSLWZMu8kmywiSbtSdnt9niHsWJI3JYpdfXXssnPvxU1/zx8KmQd4dM7NXuq7I86JAtoaurK/1InSDTbnraCzKPdQsFgMsPBYpI8iL1ZJm3UMwHi+jT2ajGVPRy3O+vneFwTxsv2wVItE8Oiqj7xTTcuv05FYHBGUyT91fjopzBfw3P7f+PeHcUhFNAlFR9cx8LC8nEaEqR5ab32vH5Dep4fmh1IMYQOl1huLqAo+TX4cFK9U1TfQsGmNDxaU5KVMMxFcM827EyUS225KkSEKTSeH9INlmSINI9AT0EGPe/vybhxs5yvnTZHWQhHLZ7FI5nWgspNBT0yJID00YriaAUhyCDloKKB4rYJ/Um8f6Z7BUsRtcj9pQsgtiQ7LFqnE1gkKZhtEbA0wj3TWs+00rYm1f3kPz0E4EfL1/wYujGjQWAAAgAElEQVQIHpXWElfbvsbPz3vedLMcJ+ZNsGCmAmosoDqK7OADKShgA9KOODEBIyTzebiH3+O3GSi4HGMlxgrnBb+v9l6jbtE+W0dBSnPoMYU0RFKP77t32+dAryoYHkbghe+CURFiJ7sSHZ9HtPHUYozWXb3uFjyjPzNPxrx8s+XGUF8IaheWfKFpxR1TXKUl35PaYhQs130/ZHrLLKZ5f9+iA5Wv9KJHs4Hu6RrhIaZ1V7wwrs+Foiv8tvQBIt3DfCWGj7fQ9klxrDRy+Hes1DaLEZ6BfEOmRwNZ8N24txdO+zj2vw0fYKrBQ3r20rU4sGkeYDx4fDnxIre7/rIkXTIzu9p0hUXK7RvtuGs/MeOpqb1+nITLYF6eK8WwxY2Rp7/C5FLyMSil2m1RNucO9+hTMXjY60gVG3Pw7d5kV45hefW4e+joXpSUE3E7WtJKsk3m//uijJhm0c2PKSjeT0nVQsm9GjzbCAdPATSTXfko4hVYzdQH31I9Mh1YmqCEVSI8w9Lk0ntG02p3/br983E8GREYCWaPEUYaLv35aEEyxaLcUXlEKpSSgqy13NBLYsmy1YQCjr+668r9TDVuOvE+UI5Sls7qj4GW/0JdtZE2VEZsZtrqi1E/Ne+59T3SssMaxpI4h2mEk3IPGjmM4LajYx5SiEexSotGDqMDu1qOjMhNYazhI1oFCCyCG98QSkFbjdxDVd1iXfK8NO5X/HnL2j+tAtzIZpyzZHyeXYjVRSRVDHw0kvpq9nw+M3VrZvbGrjsIP4Y11pfO3Y+U3UH4V9sRJnK+7GDJk9LnhFXMLF/XrAbB4JutGCmrIeMxECAxo01zgIasLkWMXrs/WbH3CRthbYdEcJdmUc1l3yHiQRoCvZgODKXLpQ2AB49HpfcDJzyn+IWtC+HcatUHYq7gxsr1Vty17wKnc7wS3VmWuutC2dn0j1W+5hOtopsMQaQSYVv+mt9z91E/LkfKkeC112/KBgHY0exL0ZrYP4+JN5ocPaGBOc6TA4Uum1Ptjp9rIDqjKbNQGSI6m+mNkHISo4nVR9V78SZ0NkYaXYIBN3PL79maQif+sIQ9j6r3/H33I3YwGDmKq+J+rv3CmKvuzQMrI0B1ppkU0ByNTXzzrjotfsyu3mYRSzXW1oJs0FBEaxJh/d4VT0WvCVkQK7gYvjcze3nDsUDE0OXFo2SkNtWUFgHlHNMIAQxAYsVbVFZ80+yItx8wKJimWl5Og1Z77HH9DEj0KBHi8IxTUtsPS/b3/PvVZn2j0wo4MhWr2cYUxVC8/nNzvnF/bc0N5YakOeqo3NFUSR7kdCRHbHajMiLWjE1AzcxOLPp+pZs2IxPkDRoqXw/ur8SANXgnLwOM/MnZWJb+YtdJ1M5XY8rpH9z40MHxnz7/6+Eciwqer904OL4na5OpqWOn4n7L51cHhGO+V/b1srYT9UK1QqMp3mMRhszaXTc6CkvxW/C3Rm+LgpoiUw2emauIWghGgV45N//5Oel1hXCBWos0UPZR8q39sij7g6h4KuAQeO12BBFwEnZByFe+Gid5nt1zxfhsogybuAluRvfv4cdjnV0x57XkNLQIAGlj+6SkraZU1JB7R8vNGxcmKEjRONyEe2IwM0rA55jKfqkOHKP+YlQWaESBSVZxRkdBuBkRTqYVZay00fRniMhIRJSTJ2Bn5LLg6U/ZAwMtQV+NUBj6YgjwU+al7D2kYuA93GpEr2h3wSdSQz76hbor6jf2Ysj+eN3X/519V8YFMXgCUHRPrGhcyvWi0zKZ0qqBPCNdiUiMEOEhe7NicTiOBekNN0LUlmtAddC0oNFRkOqMK4gmjB8l3SvO+Us216QCqoZKr24cxODIwmjsdidvX0Mh8coDnDzcRNqqHn+LxkpBoj/cnBWXwlYHLILRtA+N6k3Bo9LgmUeo+41+XB8vNt3oO12O0dFPnnz54HhH4Bk0VojvmS1ERVsA6WFFOHqIyVUiUK6X3Y4bDIp3ZQ+8vc04BpustCPhpoDLu8Dh5i+886aLUw0eRnWmpRdayDG/fz6SKX19xwFWz69EznSG0m60XOPeE16AMgb9ybmY7//yhlu7T5+O2J+r2x4polW5r2FMDHLxjrAkH0OZJUPQLekBwp55klbiZqib+KiAr7o3Oe3DMm822zQTQLAoXP5eSPfqPktIgsyfhSt+8cbz/tuVyNNmgzoML0mzsCR+DEuC32Z7CuX5OQrCtBAjJmlOjIn5w+eNmVlx0+dOUVKcpM5gylAbf86/4b+3/ew7o6ReuCzp5ieBcRN+mgTg7KFgeBi9ayGSdWkxeptf2nTF/HtXXwnnzpd88tQEFc1qL3JpvXztVLhuFpQOexvSPJTgZI6/4KkIuC1JhGdtzb2T/BROmRRmVCIeB9PbvTMR41S454uculbT2SMydU/hDXpY0r4JLw4NJE3aO8yiyq5VE8cVXbjLQjHw0nX/7sUtZBOk/9vxWQ+LX7sXsZ4l9knDxl+R/o502soSQWI0SDdgGkqMSF3Zis+xWPY5u96J3i+rpX7Pkhsu2i/rP1n+3MHxF9qPhHPcU1eLMaW1q1GLB/LyTqyu5Lvda8Zn5Pgs1yL3CA1TprOHYrG3AfDW6schDNoCWJ6VjLJSRTXgt8FQOx3DwyaBCjRd9P9QWfBdlWAoM7Pb5iv5X38l5hufe9qBi7QW97txMfzJS16m/ht7F8O5Z5a8E+V6JxpKHOg5DFBtOU7yFj5ATtD3zEMHfNIz8T2Z98zLQiENufI3EKyXMFctG+GIYW1ZbD1ygmhvkuvwZsj6Klwf3XMgPdyP02L9af++fLf9k9EqyxFoLXOwfQl5fmHe5ALj1tdrHz0QDwG7I5aQr2saYvIipAEsfTNtHy0/QgpeDFRGWDXdReO4C/D01lOTOWJywkAc8GpaEroIDBJaAlx6Ljo7M+iLc0vQ9NcAVFkuxTVHQPOr+66MX3gkpgDYD0hTWkzHMzKr5kJl0ee2AlFnlt3ybzeVIwH3hLHbX4pzOwc+mLQ3efyJAVRi0VAUounLIyBFsOUOr/nmvPR09IgY3Ti2HHP21P9qeC6jGovL5ZwAk8+hA/hqLd7/63fdaGJX71mhOtja94U1W43nuhj64/W42d/cdoed4PqC7AXEtP746RfDuRY23L9y48cOjv/Xc/8kXPeb4MkpSmUHQcyXuzHjcbfrezGNq/cuxnV1bdM9dk3zFuCtLghmdgvvtgLoyUCA1cTy/rvtS+EcFyhTokPBQs3PoqfXBGqXw+Qd13P1jsUXZ+kgP+qOlLi9veEW7iOPxQgMLcJLsz6Vc3OT+3w8Ja22r6LV9ss3owd4bNEn/ckZz7/u9aQ0HIZGUZj7GgV/H+UToJQQNVKjhh9E6bg7XRhbMKi0KiQ8o9yji79Llbm3Bi4DpiI0LcaQ/WL0uEkAxVYCynTKlIly+bSX/f68n5nZHBRLUHzynkdCWI085X1bZ4DNEmOCKcKORANDdRTsdwXC0+DRKjA2e+2gSk9xZ62zSGkpgy/fU0vimaLD4bzk9Uh89lInNm8jYehaL3qR76nfwL9cuQ/Ek6Pe0WeksCO2Gkb0zKkjzMz2Sq4nbg9juq6z7RGlJHRyFkOXKa0dif4AKM4xzQl5IY0cpRY4CsLoeaPoE3OsggjNoyvFqAP2Uc2kOniu4hvrbtn1cVXuUQeB3r5UBdZRDt667QtrsxqjJwRg70uF1XzNn6PVl5JsOJq7bZ8bS7XJ6ZbzpbjP/WbbI6LvmXcj5PPt6OSz+pipLzOz43nf8/6Pex8L58jkfAGe1kY/BgpOL7oCOVWPa4IGz+vbMdX27LLv7+TWurUb1w5L9dX5pWHD49INIS9c9vGuS1/LaTLV4AlVHdKRlEh0/uDbezGE93seee3g+GI1fmB2jiVqXOmy7/V9wN5ux/vTAPrxp74Wzn1ly5XsySqamUmZ3511NP6Umn6W9bZuQsmJYcHUkVZSVNHuoXFqcvohRF1Ef/cQ5i28Jrlx/LN1UgwZYjEAqBzjVdr2CUUwrpnZsOzv3UB6IxUCRGYmtLQ9f8vv0anGyduquNIpraEJnzbdPAJC3BI/kTZ0DZ6KGBP9WZ6Mf8YKq2g/iCFLBl8xsHu4fwnVcc1zk1sb5MZYmBHBkCkbytSBZXlbyot+Lygd/vjs2+Hcv2i5or5diNGft7terTKPiPHdVjSMgrMwF5UeieFyu2SznIwnWCpHr534IXUkEvDrpMCc8L+bxcrFnHCUFW765k1DpiD6e4CIjxpDR0GCQ3emOfE68igpaR2jazNlIX+d4OyVxDD6MgDNSnS59Q3MzaXJPWv2QHynTM732j4fTixHQ4BR6r2rHu2ZfTK+y4maRz6+0ozpqBqAoD804yngzVE0yo7l/R6vdqOTv4l99PevxP3w5+45LczlnO+BShnByIpWcN1u+bVaREBaGBIOK3h9t+d7gUZumOJiUEUDLhXcc68toNApMtXgYXVJ7Uz8wMdmfND3OpN/kBUY9wSUwXPHS26Zak7/vSBeerQS21O80gJZn0xyhjUZ+lupRKv4GqIbIwk7K1X8wW+JUiJGRataiL9Ipc9WbhuEVeA0SbXbL/KZvUUxynBLbQJJYCqjn4oJKW/lJp6jkH9JhaH4VCjDE/Q60ugSMS10kGu3jh5egWmsIb5R4YYayjCMNHiCoVGsFo2jYflwSgSzCOXQdiLETzE6oCDzwGotZG+hIaZiSjAHRriuIOH1GwOP4pwvxPTGD9XcG2ylMaT+1a5bj7+y+9TB8VI5RpCGS+6J3tuLxtAAa7rP91ZHBUp7X7pXE6Og1Tx9AmbZmV02gRTguERUyQBODIlLW6syYYhPf2dwre+qtABUXj7mOndOmr12ev6OjKyZmW1twMmdnZzuYu82pofM4rfUzXjmcQf3srxcq6hGO+BaEz117lLEj1IY1a+e8efXzZgGj4KWWUa+ByLeC4UITP6V5hMHxy0BS9ZgLK4P4n57ouJ7+B2QENbEwmbkrKAZD9BLVIvx71i0wOyNRmAYAVNCwTwchIDvmYlGKo2o9Nvwi6djeE77hN3fjx+O3tUjxzw81pRQH1/8+2djb4/n8IHnc36/ThoH+TPoTnqhGBXnc4tOgHK5F3vhfPbejxwcnz7uv9UTYOGxBUxCZaOlp4jNKCceCknLhsLknJ7096nV4sfv1Q73NhTDM0SvmdyT0YsieaEq1eJlUHWDvLDQiPdnab6mBwgA5aaZrkiYCMqifF1CkLh/YWdyiofGlhIUHgUJhi7mkdIZMAKoVU5dUNYrBT6Zlgl+L+2pIevHCjJnJSCNq2ntLvoFMYZgYGll4QjrIJk9nMzMzOz1jqejXpaeP39ozr3PS4W4cf3rnWcOjlfLHpnd6EmjRGxqCmpkxKAw7xNMyeS6SLOo4icmQTdQgmzbDf/4qaZ54UARaGlm1r4DTxfzRykjEqzVo9g81FDA0ca+oK14mBIqCjZkE6htNV5XZl3fdXH/vDBXs4J3ZyeGmEc0UPlNhE9nOA8DXkhiT1R9n9DCmiaaTpOIsbUW59u9qo/BsXI07Nhe6Ut7Hv35S6c+E677w7OeNfk72x8I52gAva9yNZy72vEo1x887uvvX289Ha6bwTooioX9/LIXHv3SN54K59g2afWYr9u1rWh4FbF3avRnsI8GrVCNCm5uJv6e0xoAq0w1ePLITT97KuJvnpj1SAsjMuuSj+8iRn+rHwELs7nD645P5aNi+MGa9xJ5TbTvjQHCh3K/Dx+/enDch6d1V5DnNViqmyblkgFX4+PRERBjGT1kRloFRmNIcAgDLETeo9+QPjFQgoMp5IUjKT0ewsjJwTDSTuSsMBwpIyz+STp8JdUqIEWizUOn1U735uEdTdjwj4qEtiHwALVKi9GfnHyvudf935oKI7aXRuJIKvNYzj6sirFCox2HvcU4L2u3/Dm0Ik5B7fG3/ZCp7UfKMWXN5sC/0ozK8TP7XsCgwMs/vPSlg+Of3/rgwXFvKJWRUHQj9dQ5N4Hh0QjJ1rrrgrv1yZwjqlRrwK2075AePN6fr7ZQE2LDFbdyegNf7yUhRwzFNVOM1ocl+QVs8DD+FpaiEqBxeedqhCbkYTh3WoKPURLBB8LSZ7PYfFmjS7d33KBKL7ux0j8munQdRuhq/N2Njhs1zZ7oZ1KPgLDw+IUInKuieulZYSvV5tfflH+w88Hw798PZ+FrOxEb9+HT3nfyH++8P5x7adfTXyMsYgUVc95/bT02Lj0z54ZMSRphM61Hg7YkmM1lGLA3tmKQooZCgc4t/061M9E4pOGbameDKTLV4OFmv96W8FvVP+odNClbKEaLmU3KtO/HLzWeOzim0vvcZkRu/9dnf/ng+OliLGv5es93jLrkmbpQkCxt32nGhbKfR48sUZwBKc7uv1q5ArbVnHTqnbvm99h9RjxRGhNvuPJNHo3vkidTsVAhMyJTlWoh2odcTwqyJa5EqB2sg3Yhc1f9gTefj+9CjJDg4GLpvMw6pkyY4pmw/h+qkGgvj3ROSYgiB2CnVjI6GnJDSRGSWC5grkTn95anpKqArSLmIy/g6Q5aUBT3ZN6T5bQ4JapAipQ0RjZnsR770mBuHyH7rvzdNnLCDL0XRH/sIbyu3C1MM5VvQjnKZdVzvhDag/gc1BnauJQKfRMNQ5Pd+J5MU9eKk8GVQ6SeU0khUq1ptO0oCIs59mHwMNpjJkZpVZxCttiRtOPdTRgrmAJKNEvRTbzHbtsL+F7K6oxobF+4nYYrcBgF20LIwezxyZEsBd5T2Bj7x2ZeOjj+RjvidP4ZOqKTz8ps3HmgkCxxGcy2+4P4nrdAEUPAuFkc88GNaBOcet6jP5tNPxccAjPLgasvX4/GEDmMOiie6bTjM87MAEDe1grKyTIdtAwAb7sWJ+8mDJnzNR/0C0LO8meP/ZuD45rUfe4i5NaBJvr9c7Fcr4FyvX+Ij21m9r01Z3L+eic2DyVGaCnnu+fSTDTKOBG0gqgNLg6G8k02gRnm4M9Gxdy46NfW345DTpp+0gDkdqS/CXlSJO1WvYp+QMfiuQoMIIJgVXFSqbaPyTmsoX0QMVYjnCqE5durkwGypd04D2jkMFVTirCxIyEh3YBXHANpI1WlzVLDdeKxEwdFwHFvOY4nO5+P8RpN8Hi0cSYZ0hWbRWDyWCd4RH8IIv1GM/J51HO+Ef7Q7MvhXH6Ma9flsy3vFH1pxvXJPeEtYflyeS4aJMTYdC+A/Ve6qp+q+qIoF2IOlQaWYnjYEylHUruqeMswOBvSXoMbOw1O9mEzM6vdnPytj4KQaTmPNJACvUnkl1Tj9y8jzdHYiQ5pGdG07hUPRXYlwnAHYcrmRtxkq0tgzca+1pNIup3y3yIztJnZ3R2/v1bisv/XDvBIw0WpogI+6blKxK5VEn+fK8C//ZnlL4brcvAy/v7Oc+EcqyE/Mf+b4RxL0d9f9SKCVxvRoKKxqOX9t5s+BotPRmNrBxG39mtuNJUfjfdgC5BE9rIW+mw9edYLkt64HUPhNfAnfed4eBCKr4gyYNNOostnc9EV3cFu9towxs03UY3F3OPFUgSHrRZ8wJ4ox9TaW2ChVNAyo00sl9fJSpDW9rawQVcPNzQKO3HoOujXox4K/6WMvEN4FNM6lvMmbIw4JrLXMRwejP8pxINjShXXVhChbcdit4i/6cUHIVmdtpagBMbnI0g8SKGRUBCcTgoDaCBYA3L5KCajsgbOlSnePMcpJ1CqPlKENC5HJVEMeOTyluTSaWCPpWn8P5C47WwlhgY/vf78wXFvJa6X58peer4qIKTvqV45OP7loeN5usV4j2m5+yKI5rrwYJVZmbrgyblowb+250r25NwU65ugYgF/J00Ylfq8iAQTVqhGMCkONJp3FKSCVDwNF8U9MeKjLMYNYm5E/5B+oIdz7b1oQBZgAOWEKJKMxyyamFuKc69xz/ek7m7E31TBN9feqUw8d+mcz6MrN6KSfP6ER0GO5WMIuwLFuzNyxf1aLxqA78ccawh4kCXmZyUbQlD0zgj8crIfXgUPz+mzMRXQQRRUu6WzEejgBOaEAJNHU6jD2UmdDsfqcnyOfZJAfhv0Je84pXXzRsy57rT8Izx5zAfv5HJkd6S3RgvWzOxKxyfDIrpSvrrzQrjurX0HW/2BEzH6Q/zQ9W58Rvbrud3w3XNzO+ZbCGjLiSFTxYZBrIkKN/HqXdn8oMwaj4iXPcGL1zRC5fZkgCyrwjSiGTpuI5JSvxXv31lCJEhAsPy9xgX8d4kKMHxf2ItGmZbLh2ckyR0Nu9bRU+6hzLg1+ZvQ4NMID/+t7R567DjPjVQ2uhCBKU7BR6FKSxvwsZx6/zFhnGWKVioVGSmqo5ng89Xr4TrSTrzYiNHXv3v5ew+O/+TFL4dzf2jOewc9U3Uv+F4h8nlcrzvgSdlcd5G25ntqdJTKfk4o9tm3b6s7uUHhaEqLgxFI7k7UxdMt+vNzDSjInY1ddb4cBakiKk6jprUdN2q2LNCy9AraU6hD2gEx7AA6RiMwvX1XwpXZ6AW09914Kcwi1SqkdbVjvg8NfzPON/qq9bdiBL73vN+TVcsF4ZkhzQKrrczMfqD++sHxx9Bt/G9uR/zb56CfT0gnaXZcvzuI/WIYcSVfT1Uws4+ueFRVKy9J9Pt5KasnH9MCsii7rTgPzi+713x9K1JS8Nu/BZZqNZ4boA+Ym/8OtZYgGd17LkSAlXY+/6ZoHp9GDvk1zCJwahPu7KLggD6y7B7fF4VpmR/rbkcIjjBIa7d8YHUzNnTPHQm3TPusT1jSyZMW3sxseMyfo1mWYWW+WqvA2EmbpwRL1FtiikRKlIEh0BK9YQVWN5QlU0xmZvvPTyZVNDIvTyobNLME4eyRcI4Ur/gEFTiHJcCt9ACynkQJ8FCFhgdJ9y7Hy7afPdxwMYub21AiDqSCmBaRG5DrSTxilpSTrbmbm9yANJHGoozyiR1gKQwsRi3mpGjg6Yp7sycKMULySM0BziQPNTP754kDmpcKbnTMTmmupiR3ISqF9aJsykwzvbEfw+Zv7binq52ticUoIZ0WsCJmVrztG23nglAXwCguogqvq+lL6KuyMHMfBSE2kxhIMlWbmTV3faxn52N0gylDxVGySKOw78e181HHDPuTo2mM7HFT1WhDBxGeyrPRQC2i+KT60WhodPfcIOYGr4bdrhDzUj7bevTg+N9gb/wD0jz0lZ4zKL/dlTA7RMegiSzK1b4HEVbLcW0SrkI8j5lZ6y95+qv2FyLIn8YjOZe0MnJ/1v/dvRsdiR04xi1UvmlWo3IRGCTpazZNpho8p1c9RE0adzOzRSDn1zbdWMk9Fhfr9X16YXEib7f945Pie14oq49X/eVOSCkfCQvXBFjN/OPyKRAPzkknXU4MKUVkCR3Dor2KgK2gVLXUkaH3xs2Ypyic8HHsrYHV+aQ0YcX+VhbPhh6WKotWC4Bs9CnZl2aRzL2Pta4gkzPuX6zH5+DELquHdR7P1ZCWFEu4FhiLQfMIGjwwVkYYp80X5FnRR2jMMISS1SxHH/cvr/n36i1KWgw4GiWRZLSR3cE1akjcUf1qVChM0Wo39tHM4SHkK0LoebrontwlSVPz3zuVqPQug4cnh/VdEm+TDs35+ZhOexl4ooATkM+kDRApZLTdF7oNGjzRyJFvjUfWDYi92IhdU9uNHF8SxD4SQmOzCx3Ql+KN5Us+HxS8Sw++I7QeZejabgUGpICizx7z+3NvMTPbn2Bo9KQijOtWdekcgLIDOcdWB2wsWpQU5xuoSvrowpvhHA168st9ZhRbMv0u8NK9adFIf7vhRvoLMzHiygjPz93xcvYZoWOogJH5xV9/PJyb/fNucXd7cfzZhqKAYEkqHDrsdF46EQMnTIsxEjcsSOYFe54WFEyTqQbPI7M+gf7U+c+Fc+TDYTk4c49mZrcWYsiKwuoMot6L0riL173UjHwebzZ8Al2YjSWAPYQSHlv0532pF3uMsLXBcjV6HiRTIhHZmPWMdhVajUHFOTgZF8oZ0HiXwCa53oppt7O4Tssx+1Ae2tCthckQvChROPR0qgLcJn8GG79p6JnFSAtSStp7yw29mSfi5tRGLrhcR8XHlE7WD01Gh0d4FLM0RNf3RICy3OiGQkTJEupA5ChRvWng1T5TIHiu+Tfic+w+AdK9xyYTD47hRmDAnYRTcU+Izn51+8mD4wu1GJp4b+3qwfGyYBk+jtD+Fjo+1xIBkYJe4rml2Jj4jZLrhT56shXEUWEK5rg4U4UlH5PPvx4rR2cXML/Jzi7fqYd7aOfp0QreZws8WzKXCGIe1d85XuG7JVsb/h2KcMaeeixiom6DuVpTSdy0tOqGHEvDKe9fh9698WIE4rKvF/VxQ78XWoaU5qPj3QWvkBIn0vhmv0HtC/bsimNQfxjz3MysA8PxNMgGSUJoZvYvdx0b90wtZl4+suJg5I/X3wjn/m3L5/D/cO7TB8c3BtGK/vSm3/+Hf+Qr4RyjP9cb0Wu+i44FbI2kZJzcX1qCVxuQ22wbXDvFeA/ih0rFd07YNtXgIZHTNKGRo+yOu1BYygpJgBUR5Bq6Zlh7vD2FD2xjIACuDloWoNSotR4jQQVUFCVJ9DapEIcVn5B3d+N7kpejI+Wt9ZIv5q54L/R09mE0KZCMnqgyXN55yw242UeitUvji5Em9WzpsWg5KYGIzaY/4+xsNGpoAKkHZGf82pEYW+wh1sb4KIL/KEhouQAvprAVx3NUP9wwMjMbYfEmwuzNVg2h2kqMJmIZKhoJwzOOgPXp1+MzpmiDoCmtEZ4j34hqorQJfA++uVZekaX1s2vRYPhi4fzB8cdWoqf7/tpbB8evdtzBeU/1WrjumUXHE7SFqiFsqDDQtLmu9qyj7Pehr4Y6Z/Ht0YBU+bOMNAu5yVZqTGnFTZI0A0cRtDy36AYrGfjVwI+RoD0AACAASURBVCPe69GFaAB/4WWfHxcuRkOJnDfrLRDU3ou6ugMGeq1IZJqJurp/J96jchrVd6KD50AaqA5vBfw6jGBoCvVRpHK3hNKeJeWPIayXsxjtV9gI5c2mG/p3Z2I2YQtd11/qejVXKZlcndgeSjR+SuXiypI7DIz67e7FMSYhpUZneM9kxfcM4oPM4rienhWOlSkyvbUEXu4X7r4vnLtQ92gK+Sq0pp/5QW0ZcRx1x/zYu1Ljy2oPZUPtDA+PEplFI+d0zSMkr87G8lmGRjVMGoDbX0XHXVE8O+exoW/HiVw5OZlQ5g4IsegZtCXUWgWzq4ZaF0/6OJJgyyxSC5AkThd68ZSn4QbXo0HYOQZAITaIjoQ00zfdgF0/KVxBMA4au0IJz1JWNjjdemcG93dT6qAAaJ/wdwpVTRarrxQzFigGdA9EZGX2Tf+75tk4t2l4tc+I18swOqbK/uMCmAcOTZ+fbLQj6Q/Vn/NvtFpxZXOyGCN3/Zo//5lKxMBcbblX+Yt3Iyhzd8Xnx3M1r+ZSZ6oPpXpFevgFkCP7n21KOvuSK+m+crfA2yyLt0/cAFnRE/FErYnecFo+uwe9c8o3zESNMmDZ0mmcSA9J3nPco2ts4MlmlWZmj9Q9yrLTFxA45ti8kAYuAsC7ueM6ZuG4kF9BzjwWU6h09tic9KkPROPqDsqur9+LzKfPHvfozGIpwhbYx4ugX+08sAhysdd6cR9ixRWb6/7xhS+E654v+XO82I3EgP/eMY/IKKZuhPn9QsWdh1uDmIXhN2xIu5UW9netXDxb9zV+DVCWPenSQGdE2048d8KdmG9seIq8KlGcDxzzdN2WzqUpMtXg2YOH8/7FmA9coQUKw0hLX8lwrCV0NYAP+DGK0lPgw4vu8ZWFIngf91TCwrMwcsj4/PgpKT+97hGSOcHf7KFcMg3NVEWxwchRorneTTcgFLPRKfgEYrsHYQGw5sLh15mZ7dKbmeIAJgjXmmBCBrfwntq3CYrZ5sCIKqWZeTyHtpYICl1KsRm6JDdMb+Xohe9bj2OBouR4LFKD1NdwTptd4SNpiB6nGhfxD01pYRMfq6KCMcRzuWXhqlkg87ZUFsLw1DLpIagauG4fL8V19b7KDZsknXkYc8JTwJLZt4AL0irPq8ArTOXiQONjDZAUsCDXu9EjvoPKTk1hkGNmqCh8CiJluvkRL5Lf9TWmBmY6ATN1VIROJ6vZGv3osLA1w5eunA/nON/e3o6GBiPAJCgkS7aZWf445qUowi30zzp9wg1zGjhmMZo0Oxcj2Fd2HOjLqL1ZNMTOzk1uY9TCXP9oNUY290jNUnaD7We3PxSue7zqRoE22qbTcaU/GdD8U+u/++D4Q3NXwjkGHy7VY0bllT2wNctm9sqW76MkXKzX4lgRr6u9xmhLBHC5/NYa1qqSTE6TqQbPdtsn75ctlpUGWncca86SQoI/s/hCxKVoiKqGcOFyOYb3GEbfbMXIBA2era509IQUwdmgueXA24FNbYwDhGkECUyQOEycHtt7HMr4ONJPAnTNoUx4MB/HODSq1I0LxgVLmZmyMBN2aK0qQjlzDliDRMrSi7sAci4I8SDur/Ozsk42YP/vw9rRAy0TA5IiMsUeMGaRQVS/ZflNsAxrOfjm4SRFSgEwhbdvrBfawT2kFQgJBUezk/ljSDegstHzdfWKeJurRV9/C7noSHDdKgM7PdPVguuCG9KahnqBGDqzyM3R5lhNKUtXNnni2jRF20NqhXw6OUkNst/cjmDvSrf8W/dOw5AeS+XC+Cy/c7zCd0s4btx8bt6J32t00t9jdj4aEyyuaLbit5yp+3xgmfdA6AD6MC6UI6YMjp47G1KxAVlGX8W23KPA55D0JNsndNBCQzu6b6AaeV4ce0JDPgC6+/p8rNJicUBfqMNZtfV2O0Y9n59xvM+jNTeomOoyi/CSm50Y/eG3XmtEY4t74gWUnl+7Gquz91CkkJcCmesNVFMj46EG5i0SIJYPrxg/TKYaPJ884+yo58uRQZlWINHfCjjOj8XsXTopFjw+nGJ91gc+SdTLo6zNR4t/qeThw7f3ffHpJHxkxfPJJF0yi7lCAgZzwq8wRAM9TbG2XwDT60Z8txzKt1mNoKRapVP+Lr3bMkGRd+734idl998Bnr+2GCdJDq0KSCZnFify5h2wjYonGipIphiEapRx824/Pzk1eBRkwB5qucMjKWZmI04Cif6w7HjUiQqrsnu4kSeOYmhtkm/G8RwEvh1OYGUQ9MNUlymDSwKiJeP461tQsLtRwVJJ6QZBfMeJSkxNbMKImkF4Xb3lm3uevtWKEfa6S5Ei0rYv7aLPsTWZsz0YiGNcOzCUGG0r6LdAk8MdMYoNWJ3SbX/e7qoYwcN3GE16SMKoCMv3u0I2SYB7oyNrG+O5NBchADPoAJ7HmpuRiCX7VAUMnZntoGprfdcNnqcuRdDvlTWP4pSlpHwW81nTkwTizqHK+M3tlXDd53pOq3KiGFNC5NS5i3ZNF0sxysLqx+u9uOaInfnhxdis+82OV3RtAzby8k5Mrb130bmvNiVQQOyqQisUZ/NNyUml2qOn3di6uh6ff6nijtGVTT83KwYPjZzWYDKmSWXq6mEjUAKHzaJn1AGbsnprFLVGG6ABHkLD6nUtgruUFQ+yWJqMlTk/4xbzeidapo0uSrclfz6CMqu/7u/ZfFo8RUYwpPImGE3zygCaHnqsmxND6IksdHqiiogvLAFQCWIujVB12pMnDcsN2Qm4P8UgCaXmFp8/lWaaHYxXCVGT3lGs0qIHj42oKIZKjx3F5TX4b43GsPw84MQ0MgGsj9qWCkA++Bsxyhj9078J0R/t0I0f3Nl1xXnmeMTw3N1z5bgq+f4WDMLtXszBczN5c9c3DG0IOQDwfoy9Fc9IEsWxbuMY/54YNaH3k85F0izhniNdRuFjyzku9+Lkb8F5cBS7pTeQlhgB37VxJW5mK8+7fp4VsOouHSnh6GGmITclzcF5o208GC2oosJuoxk39FlEcbTb++1tN0JOL0VMGjMDxL4qPxQpVzrSVJC4nRsdpGvn4j1ouFxrRwedtC3szWUW02l3O2jRIU15f+FVb9/0gQsRyrIYCAUFolI6PBhRrsX/3se61cIUOjU0bvUZ+3AUi7l3nvKdavB86jf8xT+9+Ew4R64WPrQyInKiKciV1zIHr4BaQxOxsYoXAgalkqWwczjVtbUlbUWgrG4sUDZko03a6nK7KH6FUpfGnC2Ae9tIW1R2dCNkyimemwf5NCoK7z8LOi+n4FPpSTiygIaF5Vsx9E7HOkVmU6KRod3BsCH3QAd2nZ+FfTxjHiXqR7BvEDcfRqqG0hsoR9CvvsiU6ivivxgt6M+KQYJ7SPs66y7i79DWIlGjAN9rnA0aa1owaQxejXb9e93OR8A8uyerwrp50zfDxy7cDeeuIcrav+26oPdorOxpI4qj1VfBMFWLE5LbxsusysRE1JY4oPsPc3jZ/kDwNiTKG83KhoBvGAwlMa74bZL9yXrnYUlnw9f61zYuHBwrZuwb1zySoJ22qdLu3Y4bdaHmE5W8aPvt6HBxrqTz8f4Ly25EtdmrS7JbhFl034z4nuJFd/q1Srd9zw2nz+05lvTxsxHXRmzRbUkXHUMH4pttf7D+bPzmv7r22MHxj6x+I5z76dcc7/PcamzDREwuK4J1z/7wRS9t/9KNCGWZtteTj2+jgDTnetwLroKrqSDG0OuYIyE5FO0620OvtMUTk8HrKkmq1LyZZJJJJplkkkkm7zI5eqjQTDLJJJNMMskkk++wZAZPJplkkkkmmWTyrpfM4Mkkk0wyySSTTN71khk8mWSSSSaZZJLJu14ygyeTTDLJJJNMMnnXS2bwZJJJJplkkkkm73rJDJ5MMskkk0wyyeRdL5nBk0kmmWSSSSaZvOslM3gyySSTTDLJJJN3vWQGTyaZZJJJJplk8q6XzODJJJNMMskkk0ze9ZIZPJlkkkkmmWSSybteMoMnk0wyySSTTDJ510tm8GSSSSaZZJJJJu96yQyeTDLJJJNMMsnkXS+ZwfNblCRJfipJkr/6sJ8jk0wyySSToynZPnG0JDN4Mskkk0wyySSTd71kBk8mmWSSSSaZZPKul9/RBk+SJGmSJI/i3wfhxyRJvi9JkptJkvy5JEnWkiS5kyTJfzzhPrNJkvxqkiQ/mdyXn0qS5G8nSfLpJEkaSZJ8IUmSS7j+I0mSfClJkt0H//+RB//9+5MkeQnX/askSb6Ef382SZI/+OD4apIkfz5Jkt98cJ+fT5Kk8p0fpUwyeeeSJMlfSJLkH8t/+8kkSf5WkiTzSZL8/Qdr6VaSJH81SZL8g2seTZLk1x7M5Y0kSX7+4bxBJplEyfaJd4/8jjZ43oGsmtm8mZ02s//UzP52kiSLvCBJkmUz+2Uz+3yapn8mTdP0wak/ZmZ/2cwWzexNM/ufH1y/ZGafNrOfNLNlM/vrZvbpB/f5DTN7LEmSlSRJimb2vJmderBQqmb2fjP7LH7+J8zsE2b2yINr/6Pv7Otnksm3Lf+3mX0iSZIFM7MkSQp2fy38AzP7KTMbmNmjZvZeM/sRM/vPHvzdXzGzX7L76+WMmf1v39WnziST37pk+8RvE8kMnunSN7P/KU3TfpqmnzGzfTN7AudPmdmvmdn/m6bp/yh/+0/SNP1imqYDM/tZM3vhwX//UTO7nKbpz6RpOkjT9B+a2Wtm9mNpmrbN7Etm9jEze5+Zfd3MPm9mHzWzDz34u038xk+maXo7TdMtM/sUfiOTTB6KpGl6x8x+3cz+yIP/9Akz2zCzm2b2+8zsv0nTtJmm6ZqZ/Q27r/DN7q+182Z2Kk3TTpqmn/vuPnkmmfyWJdsnfptI4WE/wBGXzQcT8ZvSMrMZ/PtH7f7k/juH/O3dCX93ysyuybXX7L53YHZ/YXyf3d8gfs3Mts3s42bWffDvab9xavKrZJLJd01+2sz+SzP7e2b275vZz9h9Y6ZoZneSJPnmdTkzu/Hg+C/a/SjPF5Mk2Tazv5am6f/53XzoTDL5LUq2T/w2kd/pEZ6WmdXw79Vv8+//npn9SzP7TJIk9Xf4N7ftvvKnnDOzWw+OvzmRP/bg+Nfs/kT+uI1P5EwyOYryT83s+SRJnjWzT9p9z/WG3VfGK2maLjz431yaps+YmaVpejdN0z+VpukpM/svzOx/J24ik0weomT7xLtEfqcbPC+a2Z9IkiSfJMkn7P5k+XblvzKz183sUw/yp99KPmNmjydJ8ieSJCkkSfJHzexpM/vnD87/W7sfDv2gmX0xTdNX7P7E/x67nyrIJJMjLWmadszsF8zs/7H7c/j6g1TXL5nZX0uSZC5JklySJJeSJPm4mVmSJH8kSZIzD26xbWapmY0exvNnkolItk+8S+R3usHzZ83sx8xsx8z+pN33TL8teQA++8/tfmjx//tWCPgHudVPmtmfM7NNux/K/2SaphsPzjfN7Ktm9kqapr0Hf/bvzOzaA9xDJpn8dpCfNrPn7H4665vyH5hZycxetftGzS+Y2ckH5z5gZl9IkmTfzP6Zmf3ZNE3f+u49biaZTJRsn3iXSOJg8UwyySST74wkSXLO7oMsV9M03XvYz5NJJplk8js9wpNJJpl8hyVJkpyZ/bdm9nOZsZNJJpkcFcmqtDLJJJPvmDwAZd6z+xUln3jIj5NJJplkciBZSiuTTDLJJJNMMnnXS5bSyiSTTDLJJJNM3vWSGTyZZJJJJplkksm7XqZieD78x//aQb4rGcZzychTYYOa203l3Xhhdz5/cFxqRFqNXM//ne/4cWu1GK4bFQ+YWS0VE62yjXtKeq697L9d2fHrBtV4k0IL52qTbcDUH8Pm32yGc/vnnJdqWE7iuVP+74U34/g0V/0Ze3P+30sC9azs4N3kPWdudv1UPv5260Tp4Lg75++28rVGuK57onrodWZm1XUnEe2s+JQZFuNv8fuO5By/m84lSgHzIBnE9/y1z/zFRK//bstHfsLXRHHfX6TYHITrhmV+13w4x/k2Ksex7texlrb8nr35eI/yjv+2rokES4JzvbQbn7Gz4uss34tjPaj4UNfu9cK5ZMC16tW1HI/7z+H31HmZ6/s5HZ9hxZ955kbHf+tEOVxXbOL3JDOf7/ozdhd9zhabUQdxrfK7mJkNMXY613OYm9Rd/C0zs0LHr+N4mJlVf/nl8O9f3P/pxMzsB3/gfwkXcrz3z8Zq5t/42T/30NfED3/0rx48b77h32s4G5+1cOW2nzsXuftybddhfF8zs+6peb+uj3lfiBM/GWK+yajwe3G9jMpx7hW3/fkbF2fjOczvylornBuV/Lv350s2SXqz/ntlWY/tY9j3oOPzcflZru/jU1nrhnMcu7So44N5uuTfprUa52xt3d+ztBnvP6xPNhlKa74npkV/z6QX3zPp+r8HyzPhXOHaPf/HjHM0jmQuDes+xv25aC/82qf+wsQ1MR20jD8r78SH7s37n+axqHPdOFlHeby4LHhOws6yP/Ti1zbDdbvPLvv94rsFRaTKjM8/LPk/hnIPDsLY5oFFVNn1+zceqYXr8l2MQT++5wgff/90XGCdZfwDz9sXo+D4l/cPju98NC7EfM83An1+Gh7FFhbRfidcl666wVPZEqN1YbKRM0k43mbRENZnpFGc7/vfJUcQXpby8+EVB9X4XQc1/3f9elSOg1lfrLmhzJUC5mllsvFdhnLprEbyVm7i3IzTgnyTbV/T/RlR/DTKRHEOZ2BAtCffv7Thmrq9GhVW5Z7P59bqXDhHY25Q89+qbkTN313whVxoxTnbx8aS5jCnRjqpJjtT5XVX9r1F2cTof+D+1ANmZqVtf2ZVzElRFNEDGVTEAKz6daX9o8fFGAyPnB/rvLFZbG5iAKdv3fDjpy6GczlVhg9Ex4n7SRnzy8ysv+z6urDbwXEcz/Zp161pvH1Y72kuvttgxr9RZ9nnrBrRXC80fszM+lX/gcU3YHidj2unOPLrcmpM4N+jXJyzSdfHsdD246HMwwH0Tr4qJgJep3xjO56a9zEelWEftPvxHrvw5leiwZPuu17rP3by4Li4FXWoweBh4ORbyXSDB2tXPdFig96n30Y9OW647aX4gWsbvA7HVY3wHH4/M7MCJlD7WHyd8q6/QO1W2593IU6E0q4rpUE1kmDSuu4s+PMXZSIzKlIVg6E3Ty9PDAb8M9+2iXL7Y5MXIpU9F5uZWWUTC6Dkz9g5Ox+u681ikotH0Z/xh5x7yxdi51j0uBlByHfjfGGEonVM5lLTx6e064sjGR49iydsaFPWByMM7VNxTtGoqWxFZUBjhRGz/J7Mt+Nu5KihQYO7P6ta26V+0yecGjyMEqlipqdLo6y0J04RjITKRvQUW2dd0XGOmsV5ymBmfjuOVQ6GV382znvORY7HqDDZYE9Ebw5mYWhsx0XReMS/KR2J+tsxcto5CS9VdFcyG5W9P0ec94xWaNTzKEgwKO+6Ui/aSriuc9G9u9JG3MCSS+cOjkdiTNCg4uZW3I/zobgejZxwf0Q+ejB+VMd0l3wedefFgIfTNqzJ1olLO4swolfi2hkxuiT2bm3Nn2XvETdyejPxOYp4TUY6zCwYAsW7MU2QVvwHgzEqdml72f9Dvxbvz2h/92xoCG/ly96yK5lndCbuE0ntxMFxfi9uer33P+7Pv+PnBosxwMB3G56PzzFNpho8NCZUUdDToKc4qMcPzFC5hs1pJFC55+5FyzH3qHuAwzh2wUMeiW6vTPAwhxVRPJh5XLz3n9knOSeJWu5cOAPxzNMSDJ6hGGxY90PMrUrUm9aHbiyIYdQ448+vEYM8FAQ3tVxPQ/R+rBtXoenX0shhaNXMbP8srO54ixhiVpsPt+HGNSxNjnA8LOH4Mv2p0cVRafLGWuhyrsRJy82f36suIfTcHgzPszFCQmOLxlBf0rXdFf+Wsy9FctbG88f9fhKxzHewJmDodZaiBuca6azEhctoSkHunwwPvz8jY2ZmQ5zLS2SZY8colwodNI2o8RkHtehlM/rIuU0Dx8ysfdznc2VbIhXlw1MfefFY0xa9ooeewZoqg0dPHxwnA0lx0liTT5KWfe7w+9+/FtsUUz3teN1wFo6FRurx2zQ886IvqYMLLTE8+XetqOBK19zQS586e3Bc3on3aB2jQxOfsbPEEBKe3aaIVlknuLodo/iMwORgAI5lPPBn6gTQoVadl3vEdUbx9ZsHx/kNCYKcPua/PSsBhjZsiXlfcxpIGeJdpg9QlKkGD3+k0JTJBUVNnIAaRvOXXVHvn5/cQoRpjd73XQjncjCUCm2ZhDBQqlvxAzRPu5KlUaYTmR6lplEmbbo5yTMzxFmQxTAq+Lv1YzbKeis+rgmMEI0YcAF0l+JDVtaY1ovjv3fOVzDfLd9TnA48WBmD3gq0ABaYGoccV1XaNBYVnxR+awbe/RFU7pyLjHIOBQswKPM94j2oKFIxjPiNaLB3V+KGOzjta4mOiVn8LiHdIg7HCOPbfPpYOJensyNzMc3DCIGRMBYhgYE19xvXw7ntj3tfROLCzA5JTR/cT3FM7uF35wU70z3cMFWhYVpZj1EoGmmKf0oThOynRNTKu5OdxrQ6Aesh6y88o6T1joLkO4ND/7umfQoNf/a0Er9Xio06J/OZUR2mt3LN+L36x9wrHIneLn/D8UODWTfKVI9TugvxezECo4bn4LRHr2go08Axi+u7uxB/b4glzii7Yh419UopICpi9bjfDuuI8CBqtvxKHMf199A50QCAv4BiVYPxhejl4Hh0yHIIIhR2JN0vkZyD6ySaR+enfKehl0+UqQYPF6hublzYVHQ5Cbn25/zBZq5Hi7OHnDaVkirm/ZP+mPW1+PXpXfVn4kyg4iSYcCRvXUK6oNQQ/AoAlTO3fGK0VqOy4m/pEmIaa1gWb5aRFg7xZKiB5TtiVL7tz7x/Kirc2VsIw0IJlLei4ty74KutLOkTej15rA1V4ASRtk7EQebmOpA53YdTXNn068qNycroYUkA0CdIcQpoOQAjRfnS21STjgpxgJSNgjDpyQ3FIAlAZcznzlL8JkzPqbFSQBRnIL9dvuMW6/5ZT1tUN+PaIZ5v//1n47kBjwXbh/XPzW4Mg3TZAY6dD54J56ggB2VGQMNllkekursco1CMDBVaUeESc8jrdBwZddb3JLCTMoZ17NF4O3pcsUMYL9zMCGA2ExCzgsxbro+SnbiBpZUlP9fx64YzEnVDxLn00tvh3OiSz48+shC6dnL4zEOxR1vHGWmK57heeEqNE0bq1REqIbHBdJc+B/e52m3ZT3Y93zU4E1OKxW03htpn3fO+9z7VC/htsa+p88eKkDD+o4XJTeHpyCq0orjtP17admNosKCYWexrM1PbkgV5xxgeTVUxLz6octOOX7gEL7i7FL9cGQjwUcmVjea6GZ4Mv2VxguokpIKh4hnzRAkW+/JGPPeCe76MUtTuSuUKUx0CYC3sIyc6G3+8gHB1fwm4qFx8meIeI2rxXPPk4canWcRN1db8/lrxwsiZbq71O0gNHptcgcA0glr/ZVTJtVYnKxkqgYF6EEdB8EilHXismvIFFknnPdNiuW7cgUclH4BC0++hlR9MO6ozwogADQY1UGnwlLail9fD72lKqHXJc+bUA8S4mZkVOsBeyGZPp0OBvpx/YUMSr7rzuFf6VATfQ6EzwpC5WcTpJBKVpOHROhm9ZRqS/PaaWqPxmfRVQU1w1VM1jPz+3ZnDjaSHKYzw5N/ySIotxRAGDSMFvKbrXqiSnj8Vz+XpPeBQALVMo6Qnj4dzoVBgcLhBbWZWv+vftXlS8BMT1r4KIzWatqJ+6y0qfghYKKgF1en9OjIv27FaeLTiBoQC7ctMMWJu52TpEDKhEJLFy/6tNRvSOeYvXr3jxkr+5dgDuPdBx+nons3vlN9wxyoZRKMmhZ6ksfyt5FuktPy4M6+GDErjgDsYD2sTNyJl4yeAq2FliVxHpcqKMLMY1t67EL8OS3wJLFSgGn9by+T4QWjBlu7Gmbz9Xg9pFjqq3Cd70qPT7gXloOnzm1LRwer7okTRaliJokOLWA+Ns6z2iPcg3kJTSW2ktFh9VdmJmzUxIjI/rX8SJb6yiApcs/hpHcejICENdBaboO5lmGN5CdFzTQyWFGyLyAoMJTUKwrqSTZaVdPkS14B4ZFD8vSXB2OA79CT9wOhgC6k2/V68h6bEzfD8GrKHARGwMjK3WSyhwE7qrmDM5yTyiH+qAlfnKv7h4ZQXOg8I8O6KQVhJDr+/lkpX7vkO1DxzeMj/YQodwf4LFw6OCxL1LL8OY6gbN6n+e7wyKy9OQOHuzsFxuumGUr5yOlw3uOiGUl7SXaXrWwfH+x/x6p+lfxuxa/d+0O+hEZi5q35PhWcsfMXvk+94dKMZbTcrYtvQ+VwiGBnLsShG0wDn2hcEOHzXlekYwB3zjdHefAzEhcrh8lY8t/24/938W/E7leF0cA8Z/K7HwnVDzJe84ECLqKAbLXsUKteNVlly2wMT3SdkkKfIVIOHVuZ4qfXheJOepJXKAOrpByZ4mIaMgooZ/u6K4VW9B4CjeLCcGAVooiQ3WZGpVcxNgdGNQTV6L/kARBWA1XGEa/ekAq2LT1AAH9CcpAeY+lKcET0K5e/B+DdPTE6R8D0HEmGjAUrvQkvUqSDq9wTQDIMnH3XRxHy1Vu8cBaHXz42UVW73L8TcHgNps2pI/84PuZEoTqK4DydDlIYBX0LDSI0O3qN9XCopMMc0VZwbstoD7ympUK6D/TOCjJxiy3IekRNEq8VYRaPYvv2zfq52GxESwU1M+l2z6J3vn5PUAfJ8vS0fnxNfjrsHcYRjVYeTAjxqCE2BDBwF6aMku7SHKksBLacrPvjqVIUoTipRuONIe5zw48K2cOEAEzWS6qIRAbD4qf3nmuoezwAAIABJREFUToTraGhoccjmM36P41+IVkgPXEEF0W/hOsw/nW80vvtz/M4SPYaOH4tYLjBtKBGkRdCX4Lf7EWIzXo4PyUFXdxfiBK7d9m8/rIEyoiHG7RzK18d4YLCuFuFMCYancNvvqfefJlMNHkY+1PvhJjiNXLC84QqgfTp6JzF0DQNKFkMIvQuxYeukD0ogIbRIYsdwu1aFhCIAMZpoRFG5KxEco1cLL8Vw7c6Pw1C6FxfiYBYg2AoqdBS4SCMqH0/2BljoYzw5/nKlBjdoMQ4ZbpaNq73E1IT/985SnKwrX/Cw9PqHY/6Y8yen8xOPUt2cTDNwFISppM6yj/vc5RhabsETV6JLglw1SqZg72+KkoixsqIoHjHTaX2kbLR0m1EoknSaxSrMjoA3+yAfY6p4+zFNafFvwqlYpSUbC+85qE7GTbAAYPfxeC6HTWf7uclYsPI6ohPzskFgCqcF+XFg8QYoANh+PIY2qSdLGrGc8FhqDPQXfKPSSN9RkNprHt3onQd4V4H8IDetXY2eWWnTjZf+gmBz6GTgurQk0TqskeK+AJrnUCZNw6Ie19UAgZvucjhlVXDibT8TMwHzV3wS02gaSTR+5gacgHPhlHVWfR2X1xEFrsd7lPb8HruPxrGauYlUupBghmIJVqVLSquIT6NFNqVdP67fEQ4gRrWBeWufjPs+U7tqsHWX/X2YHtYIT/PDlw6OmT77VvItDB4/JqeNmVl3DpVBUzzdInJyOrkCEysVoCgGDqTyhdAa1VLoFjgQGOloyUYaIhhS0REUDA00qa5JMVZb710K50pFD8l2BX+TNLB5lLFx7UsVQxnAyJYSbuF+og8r2/4fyKGwfzp63Nz8NPUxRL6U6bP6vTjgTOup4V7d4OY6GYcVlNGU6pqHJQUwseYBKiZTtZmFuaKVNcT0VAQL0Fs4nIxOhQb2ULAMjOpw7Wh0YPcRn7QKjGwswsgVhdiBR1i/zbUpGzX03EAMHhokAx06prF4S7HlyUw+qki6rgviwRIwPPtC/HkJ0dddYZy95WPQejZuoKMOOICI6pboMR25bj7O55mrh1s8iish6H0aGeXDkt4FX/csjCjtxDEjXpEEf2aS2hir+kBEFJwuQ+FrI2DapLS9tIVUyUWf7AWlCsgR7CnQCnznkMYUYYq+0I7zoYutId+VyM2O/3bnOOZsR51YF3KYmZlVbtCQjKGb6iueUtz/0QsHx1oF1kWWTKPxLDjRvbhyC8bWKaSjZD5XkHbrnBTiQabBQb2RSIS7/rJbn60nI15rmrzjKq1hKZ14LqSOxAMp7vmI6f5FZbx/xm+i+A+Gq8f4TRC205AxowXVW54g3T8V49rE92jqjpVHLH0dYzQOFW3x3GA4WUmlACczAplbiBvhaL84+VzXB0yzdUw77V4kdXm8jhNbMSH0sueu+6QmJshMWGAlZ0avXTdXGtM03tSgOgrSPucLNLRwEIA1ozhaWcNz6s2HyqAmgcnxOVh+2l8Wa4Isz296tHHvmeiycq5XN+I3334cBoPMKa5Pkqyp4tRQefhtdoWQ+5OAkwZwQbAG5LAaq4Tjs2CjGksV54DL24n3aK/iO8kaTgr0lifjFCdRBJiZ2QRiTfV6c2Aft/ToRT3JJVVZ94/XOR4tWepnBf2SqTjfF1oPRIry2AQ1csDow0jWRFiDmOpKhkvjXrnKGO1Qxz7f8edvI0s2e01SrWeQApbIJqEJ5GtTgkLq6tKetCp6zPe2yqe+HM6lz3oYlBjOgAE1sxyeY+m1qBc2n/b3bpzR4hO2AJnsaJE0VfdszbB8UwYr8Xumeerhdx71fOcYHtl7uLktfsEZFje/92S4rnERDyYvN3PF86C085gqMIsl62qQcMMoNaIrSg82gYup+X5iDTQlRPzCtDLeGC6M91jf9IWZF+LBpUuOCtvaBI9ENy7EBB7sSJRvOo+NsSAe7MrhxpYC1SiNcwqY9vemkaMTmeOoHhAXVVlwRiu/fufgeOcDXnmj1W5HQZIJPZQG5clLSblZQv58EheLxfLZMYZxsIWPscWC0LN40q0OBeHSOdl6cvIzasoptEDBwi3GrN5EUk2zGD2u34znCJrkPFU6CXrBqUR4RmD6zqNabFAWRYZ36S4LWJbRoJLwkLVBwNYAkF3wk9yEtVBAw/QH/11aKTTASk0+saMirCLrz4H7bF3K0sED0xUiyvpl14M9aTXC+d097efGwPowepU7ioaoGhCUuas+9htzk/VP7W78doHoEutl84X4zWu3EETQwo45RPHp/EokqAdIk0b8QkTxk++PP8BHgXfdjlt2MLZ2L0jGpsHr4t9xDNgDrz+l/5YCHEM1NaPTPUn931j3+1+MOKxpMtXgWX7ZoyI7T0QLK7wsU1NjAE1cJtGZnWf9y5VhqapiprLUQWZ1UW9ucqqKDdLG0y1+07ZQgfP3QqNBMZr4W6qY81W2d5DQ+wRKTW1BkWJ9JbpgcQv1UumVBI9Y7jFNCfB70ANSHFDwjkQvc8NWo3X7Q77iqNCbJ4+gN1vlRorogHha05qshqokwWPVb7v7Rq+0cnk3XNe6uIDrpPUDjO8hzimjKj1FbScS2INX5Bx+boR9S7dvKvT6rXiueebw68zMBmy5RINHjCaG+oeLMuE6uBhLLhFMRQ6RmuGMYBJABVEoaikZDrEgNf3HgIzqDBNQ78F/npnct0sbrR4FoRNAACkNHLNYVaf8N8N5jwbp5sZq3tpNKDFh3maUryhs8aVbDivYvehOlaaEqP81mJZDFdU4xQOrXCfThFDP9gWbU9wlnuydpbTGeO8AGxmrmIZxxL1MQcoprK2BpKlr7puOOTEMCJCIdVidnOHQCtYcNkFiuUpv3g3XDS64kVO8EXtvTpOpO0oTbK5jhgZwNu0nPIdW24gX0ttUbM4QgxkYduX7EhylC6WH+8/ciFo734PCQvRn91LUsB2ENVXxc9KwAaaCPMkz052PC71a95s2d2OYd7eJpp01NBpUBQvJKWi5CWDqpeiO5275uzJ/XNoRgwpRKU0pBpwUxkeVOw02LROeveXfcO+sgg39uHnc/7H0+mRulYclk1oRTNuIygK0J7hSje/GOVQmsNHgUxEXxmq5sjRADAUA3Ixk/bWOwyNWpYd/6zNyDpR3bKJ0+F1jBXFYV+0pKfhgiGvxEqYRAf9mZj1UcDGoWrgTIws5YHiKsuY6J2DcSlSV64pjoOM4dw1Vkqtysnk42FIjmywO0A36KAgbURJgXdqOEZ7CDhpiPhajOCy3H2tZQAwnAdyC66BTpQB9O40KMeopcdpYxTct5aSbOPXnECq+0NDUlx/Pbcbf7sHQR3ZoDLRcBbP+vnQ6Z7cBNcpm3/CJuvZh1yca7SfQuj8rBiEXk9rv2FMCv5xgWQLjv7Z9QfCE3Rz6l1bDZVwjueUpuXORqQbPNsLcFTGi2Pdj4U2f8LqZRVK5OEL0eDgISurXRY8eVb6cvM3T0eScvcrmiH4PJcWro7S9diMqoZ2nfBYG9lwZuWkU/s09gh7iC5w67Z57A13PNxsCj2eRVlEb0QBHIWM8QjXJylf8ur2L4bKQolSyLG6uoe+VRi4YhdKKGkx69ehzSIUUSXp1BAGa/O5kXlUuGe21Rgll6VMoAKpr6NYtDW+rG+y5JQB3cvUFTJ0QCNLgEW+Npa8Doabi96NxPBJ+JY5JTpQqU2GlWNRorTMMC3Ozj9eNlhFNGMPJcVwRGr8YG0z2muA6knRXbg8RCVlX+bPAkqRo8yHPyLD8GFv2BOJBbU1TAEBaCwqOgkxKJbVqEWMz86KDZkt78VwujJOkXlvcxDEfbkq37kc8FJlKNJPFANRhYxEetIIYq47Chq6Vl4x29BZYjCMRGOzNuh+Wtg93mkaCn+0CN1cSh+P/Z+7NmiTLruy84/PsMWdkRuRcM4CuAtAzRZHWalJm4ose9aRfIDP9Kv0F6oWSSDOKbBlaYje6C6gJNWVmZWZMHuHz7K4HAHm+ta7fi6y2YpWfJ890j+vXzz1nn73XXnttqay2VPfFn0Unh+TjmaVyCxDKXTatETY8+tqZBc10JBEAeveCTBQK50Tr19HpGD1SvSGmz2Y7dlZmjNfm8CTK5fHe+JAb0qqQyGN1Mi9SJe2v4P3f1xOxAQfIGd8zoClOJCMUKj2gbF2x4srLf4VvkVE1xAdFByGEEHI4/ddVffjX4839xXYPrMx5jMqCopXmzwAH2wYLw3hfF38KmHRkRgXOp5PY2l/G+b/6CU41M+AVRBdTexaMUr0sXQhucJqynIYfalQ7bOMBh8fI+lmSDvy9rltVYroPhn+6k44gecUgDQoRUE+piHy9OStc98uqBSro10bnx1MAtBFTU5WleOY0ocmymSuX1AdBkFGwtQLnZQk+XLViHdexX9xpKl3gd96zKi18Vpw52xO9B+ll9aG0OY/sJExp3eN6FVswhEd5s1l8LoQQ5vejQ+Ik8zxQIufflHopSO/YyWVxeBqZg/PpDV1HqI5NNpmOrx0FF106UBgqZ7opJnfYU8X2Y0rfRheapVPt+5apdfb6C0H5qERA10XdO9RESp776WRq2jzpqr6nG7fxDQM5vYj08GPLCHucTOV9m56L2SQJzEMiX0exODyPhRnf3d9Ea+BKyGV41xQ+c8M2RN+qBHIAp8m7OuskpVeTkDfSf2iVBavNr52jItwOP9CByCx7OpGHjbh6r8fR7b7pagRUQ7prcKF8qt3bMRyfzHQBTfexATrMq3qJMgiytiqGaENRAgDm5cREDKpXRmjGc/JIl8aCDoX349qGMQSELI0h3ekgomURGo1s44UacxoYbuSECCP5XjahNAbLAp0mh0fjy9qFvkXyrXMIaOhqEKr19aCOrTs1WbwE8L0oueDnHu5//tIqdk7iviLBeNA3jRc4V+szfW/1k3igrqfpa3EJ/R4PJHggtb0MvbjZiV1b+bo4yC5wuQWDZcbTo/gcZjuO9mOepp7KwEtLxZDTwzTH+lirDhckDttZwNYpw9vRfvbv6j0SeXQNnRmkWOpKKVHHAPtx/qY6ZYULtFCyvopsLZSD7lP5mbemwWtDTkm18CCJZzgDy8oL41odQQ/IHDaegY2XhjxBr4td1T2lP4Fyf+OJZlSo+M70paNVDOq+jRhnNsLDw96cBFHEBbwupc8hueg5pPFng46Lfo4HcPOrDCVTz/SQb8EGmJZyGh2z5YKVZOO1CCuZU8PUgSNB1Wr8QUNzeIr5zSjG6lo/N4MXnqsYJwSwf7WskzdFFMHDY2XPkxGLO5yCvCO6SJCzEQRPDtyriS+TEv7xNRGJrRz4WTQ8RRMQJMLl1Tl0jpsfaa64+0Hs3ZZFeC0gIk6UduY3O99OWub9uwIxYfTStT5LwuG8ZnL/4bt8T4f093h60GFINlDF7zaphjVI/2tEpZ7yrQDxmVhn62I5bpiyoaol/LuDPV29sEMG9z/Z07W9s9q8953Dk0X434YxQhWZKIybnc0qH2avRiccD+7FBdf+BJpmx5prpWBt3uA0aXCK9Ta3dC33yzyx3vDSforYQrxXtOq+GasJK/r8c2PCqiAO123NIpPn9Azel4MUacG7p+4CzpeZZpKkpyNL7EPQNDj5ghMv/cfjHd0zMWJQQ4pSBWvfhXPaEe6skenwMHe//6kuwiH4Cyy7bT3TzzG/23qiRmlwD/lzLELP+RH2dwiMzkXZyG40kNwMCQl5KAtnpa3qz6ExcazRICMvT8XUy+g9VFeU65MnkYzV2onXb91VIg31QqYG3/M9R3iYQ57Cc8+PTWEU686jfVHlZK7dIrEi8skz45GV4Sj5/DdesGFfIfVz2zDKvc2HlPMucoBBxqZITWPQ+XNl7PJAI3kz0ZiTaVhbsuR5cF36AURj2Xqq7/WoK2X2hCmuBQMVFykDEJl0VuLLhDOEJSzXdHi9C27fjjn6N1BsHWFv7lnA1IkLP2+CntNitE9TO5w8HfHq+sbhkd/tpmVgH/79NXwtLYHMfgvj/n0NKXggB+MLjd6nR9H21T/SHla9n0c76IgiHaUZ9Fh83UvXens8ozs8axCcpvQzC0GJtyG4QrOjqrxhfO65qQzvxbNgNdP1xlspH8R1uuwron/zNvizn6ZnNRw4oG2RbgYO/DJAsMrFJVTFXTiRqD6de1cHZ9rN+8sxsKs9jWfg6L7ydBgENJ6lpzZ9ZDo8ZG879Cdlmbhn8hpCCKF+AePraA+uUemgdNtyuPTmEkJRrNV3iWyIBs7A2k80J2WEnNGWo3gZH8DCUl+i0WNrcCl5Cn3vnfsRG33Sie705JmGHvmjaPlzhgrNpulNLItQjxXSdc3acFylV6pxo1Mk0D38GtJRjTND0Y743MyQ0IgzOnp9xfDvbZBIvYIhnjfUAaYDWe04ArP5dQjan0bSgP457BHKyYegJfFZ6tqM8iggGILKy7vEPjk2POwSUgfU8rFnSWdofMeaEAIl4UFSMXLz5DjeR72tjsx8joh+P6O1xA5kAKpqWySNtdD5Ke/Fv1udxb06PPXCDESs5hCux5vFsJZ2H9S3ctuyDYOOM/VXvC8hg8nRe+roc18tLEVR6YKg36LzZ8RhzJPLRIggJM8dI/2S21KxQh3pg5VRPENOjPPaCtg7+aGdZbTxv4L9b9qawl5yx0uaLxsqTGeZ1dRXhgTR8V+ZpmmOWQILSKs4w13LTe4DtstT9XxvfLq5YCgE7Qs4NvAha2R3S5eUk77H/B0/R3XjEHQzzI3ZTuEzLvjG14puXP0s1uh5vyzm8rxp5xRM8SZQBP+cVB4ZYXf/w3gvV38Za/+9KkecKyOiTmF819Y8tDuJzsoKE7m2iDIPVMd6woUSCJrTnpXd4lYokDbPGTFQNCb0+lxs4wN47uZYM3+8sKbOTH3sGJeBbUqY3tpG4z4jogHHMFEuvOZvSufYJLqswzCXkAojf8D/bmGoJA0d++n4M2Faumacq+t3050t+Tduyzk8rPZz+8HvLgz1CwjhlwbxCxLl60BZakZGnoyiFed+KRhBc44KqPXCChZYgmvTX6/Fh3/TiNcs9bxcGZcwvytX31yw4MKl3AdOLdiGIY2TWS7sIrF09A0dpoPiHB4q3PPMyJuGS04mW6/BHnW8R59PCuuNXc8OP4cte0JQG5YDZzNnSCBTXOvbakBZjDJGtXPO2qFUO9gTppFFhLGi0l3SE492PNEYHPyhnPWQK0B92u0z+Y1SsGC9+Ihwe7UbaSjC5bPntOQ6S1Es3zSyER5EJB6lMqfGyiwSV0NQlMgNpzDkES333tF8SP2c7R0sv00RNGPms9Ny/+7mqDGEEHJX6RHg9Y8ilMZGjzlbJNP99KmU1hINU1GdRAdlNoZXYMKD8xHKKnv6XQvwLbzUsYLNQYLpyBSfyeFxCX+uAx7sozuG1MBYlK33GvUuxqb+zA3H797/KEMO+gcajNjY1Nk3XfUGzrxFUCRm+4bXPDsOdHNIaLC8fF+4EjjtXVqC3cZ7Dwyevoyvvapu8HAzqlM2A8u9P901w490QSIKHm++fiLifhQfQKejiCgrIxddFES05qmfW8/MQLFazPbLdLZ5v7sqNTk8vg7W+5tbt7MZZwghDE8igzyRGtyCkaZ/UxyprWsjxdV9Sz1gkSgxrg8PO1ZfOTFZAgubKKaHGfC6REYFjkz/DT0XCqABuM7YOgXAdn0ojrxRE+7vRwjzE0iZuIp45wMgm08MlZRehOmBPfsZriwICC0o99uemO3Fh1G+0u8mOsb0lqe6eRa0v9T9KDp7uH2SoENQXyKNZrBpZHN4EBHWP7auprvkWoAL8Cy95n5hJXST3c18jdYTnaHeA+R+rb8SYXrnfEx26cXG/3fHiyjUzhd6yBaGiOTei46Y62Fwk3pJJP9VqusDPmhGl5yO0XSUnv7zaDPUwM2xdBfVekfQbkoQkzOgygYcQkKonkLkNTx/zIPAiYI1RCV0fvr3bMFswSD6lce698a4/JxHrKI+Xkw3nM2nINQeGjcLESzRxRAUumZDTyc40tiXrd3H6AQHkKVz6JCQz+O/ZZah+E5HwHsWCQ+BTpNqLwoHwqkYee4zoqUWlpYhEzG1IEMOTavSmsHhaf8mPntPadFRKvccmt1MUpvd1x9KvoXLfmzb0NSOIZtjGmH9O1Zt5Sylxb5b/UcRFfNKPxbPDE/8aNvMZfM9QR0eX/es2koo4Qu4FD93e0831rN/jKryxXvK4ZqBqxXQGiXM0x3xpVdG4uvcxjM7Qj2j4GcBHLGVzTHnxAMVKQxiRZ4JnpLfUxzpQTGD4jbtiYtR8r1vkwnIdHgksnNeCoyqHG6WbxndQt7wPP3Oyijx7d/Xg47lv2PrdE6Ic53Bv5GSPIfH8N68bSmnx1BU7RIWtcMDG8dJWhOIm7nHfFaICNKcRtXFBT365MATL1hVADfi9ADigteWRqA6qHEpe+inIqqc9jinUAd1tI3OrovcMY978yYq5vyA2IaRckvNp6bTAj2n67etNxwQGd/I5DpdvxMnzZ1LoqNOZOVa5JodnFg0yLVh1RhSWntoUSrKVypXmw/tEEJYgidWHJhDCFRyaZdY7gBJhVBn0VJfwr+x3nNMXS2gdls1cvME+lZN4wExACnv6aZYY356H8R96yXE813M/z29/71fbTa/031vmRBfjy1q34ZBxMSrXDkmKAevXehzkNL2Y2v82UJjUSq9m2NEpLP5ja5ZrnWK4vlhyZTN8KEhTUB4nK/G66xRNXveVbLtez//6tVrr9Bd4O9IWs7/g1WjsXLRjoX6xTr1PVIthLZgk7BEmjfn5xCWn5ft0wkkAl0/d3Agvp5be5DKNSqaUYFdsHVVFIfqO0J4lkB4RiZhTc9vJs/U+vqQG2KGjdEKH4YbcFaazJsehcWXvnh3P40Gcd7A5M3SD1Ln37CkmBwV32xShrdrK22SvsEOW3Gjd3JxwqcX6vRJUzWrl2Tn5qLBk1McOoRkk3Li8bWTlktIz4wQATHtEYLpMRlUzDt22P/67fjl3LDD29tn3NMi7NFt3bhsqeLcDZZbjm5Zfh5RGKHf3c900iaoePF+NOT+MGXmHJ5iRrp50dzsdISgz3l6K36u9Zk3IM1v/FwIQVAX5yjQoaLo2qJpEzlON1/1alzEtduokhzrvlpeQfejrIdkq4ZKGZugg3o0bL2X0QAuTIqfKebWEz9dN69vDxbKfdiWve3bE0xHlVFS7grgY7SN8dRc7UsaHXV4yOFhBa+ncon4eKsX2mvyS5yvKFyzhDAgixTMBnN5M51qvLDRIs5Bb6pG8s3dmLNlu6DwhgZTpW/SNev694iK6Hs8i8t4naubcwiHp9LQw2B+DefTqumkdx44vn1z9EmPSQAYOEep35Pg/8KGsqH1HxqvTVp20mTrKdpJ3Aer2x4ANUL8sGcaSMTT7HPcUO4MsdrDo+DRbTwcoC6ebvEDiYPwmywuC3p5X05gZWTgUt2EP6fk8CSS9UgdOKEZX7cyqLX2Aqz3OyBXPjdiNegEjsCwdHPvMzz3h96ACS/t9hmVeAn04AQpkoxGsdsw0iqnXA6fHl4i0qJ4nKNkcJb5d71Hil3zPo7/zxfyXv+PIruXa9sdTUZeLko4P0iPFPNAWsh9Hx/Z5wT5tfdQPbgyjgJJzDxkXHF2TVTHFlwB0fMSkXOrYR28H8TDpGicihUe4nhqRhXOY6HH+9CP5ZFKGe/rPe6klEQ7iZS80dzO9jk8TG1M0AbI0/5SyWo2t/vTuHhKJksigRQOPk9HcZ3Wz9XIj46jYaGNcVvdAz+tfGlpzN14X7UL/TvRHcNbtareR7MUHYiHrY6891U/pjLLjfh306466XMgoOWOBxnxte93SoUw2+Jk/cZh9JRmxlXjXl0Yr63Ui/9uPov7anBHHTvaBXfuq/g7+g6TPSf4b+b6/KGRndLCoe6EOxpIeo6Jkmymepzoi2s0nsdZ2LnUJ0XuTEJzZECPMAu5wUFl5zT1gVjV4p89+kWsYbz58U5IGwn0B3B+44n+gG8qMZfAqpC8TaTwHKyKYwlCs/fZopNDPZKeEfL2/wGG2bgXVLgdnMYJmRinosL96xk4/Bw3/ESUSKTNep4/1CABTxAT23SVXrozMT7CenhhEgOCXm92rvzfnb/Uxnrcq0QHHIYnedzRn+o30Nna0edAXkoRVVSLhkH0tElmHBkd1l6mp9omcKLKNzoJk5P4fdV9tRm9QTSQcwQStZY6PGwtQQFPH9NJehS5PonXXN2YoUQAMvdKu5ThdpIo4FameTHoiOdsPommsL1ACCGUruPz67+ltpX2lAG1Uwf43UNDXNOEYceH+kxow5y0zKoh5w/p3orvjUbqrCwQ0NDBCUHTpNMbCuraukHhy2Kqc1x/jmuYFpqIKrI/omUFFsgxe0PdBe6las1PCSRcvxWdnN0v7Exipa9lWzrvoZk20O5EEADOV+Hq9T2eTIeH9f5uLAmtSvlpgtuCf2jfPi2thdLmumDqi1RsdVVZREkzV83EA+Cmcf4Nh3uclIRl9Rj1TUJQI+0tEVi+nSgNhsFlyWLumW6U5pP4uvNzEyYrpBvByiUqC8DhcaLoHGnJRKkmoNGs9hpyT3aNNeyPOzLkADAKcQmCbRjuePx+eE+bFQ50jyKJrDBNGoIagBnWCh2XEII4WK6gLPuKGi72TGqXuA+rrpzt45AppyMwbFGS6BkGYTLP99M5WptEAjWicjMeMnr9Uita8ErJ+gbl0GkZlZClPZ2E3TrEROc6kdf9+AAqFqmPF0Rj8YaTp/F1WUiyDKtSIsyf1SPqhxqNZ9HhG57Gg65oZPoVSNrOlWSKtnJjIpIpZenBzoICUqGJLuIgNEslsa9Z2jejDjA96eehoJnsN2XUhOfNaOBuzpXf8+hBrM7jlnAZEioyU+cpBE0lkT8bggZrJBLnLUh4jN6JAAAgAElEQVSmWv/EkM014EYvPqEzyipdrxbb+QJtklwbDEuGtqt+bvsbRSLfWS8tyW/aZqUOAR0I97qpucLS8N++h0V4CEEp4zU0XsZZcHn2EvgLPrHUNCnCErnTcfNWPI0TERTTeuD3OPOcolfObJ8iPdD+3KoCIEu/hKedb1kp4h/Do+1baT5zyzOFDyt0WiHU5XAnScueGqShniH48gNUEEFzAJrPcZC37DmxNwyuMby9fVLLbuh+P3wu6i/jJHYfabTJ1g9ZWiU0UFNruUHnIqGgjPRqUch+Ou+Dk3TDL1ochs7QyE7bqBC0dcnSc99zTIMkHKUJHSo4P14Zg4h4MNB1vwY3p3YaI63ZXE0eSaXegHQJiKpkDtXLTjy4iLCWuqaei0uWLODLpbSWKA71uxZ3segyahd+qMG2DYLOHTrvM76uXhp6MqdTl4G0QZbEyarVTtxzhbmpZqOBJQMpV9YnKrkyCZEdkMyd+0O7xXRt4VQVN6mE3zzQ9756HkV1Ks/i54q2bigEWjVVfDo5jZdG9MWZdf0mzlurQBwjCJ9fqyGnLaiqeoJWI1MLzPq/dR+jFYsFtewMwEos52SJ+n9G4O0jO6XFg8+cKHpfrOZywisjEo9whnBs+OMaL/QXCKvekYMM744EZzpinvtlWsGb86XxSPww5vVZGROCHpKuTkyiZ//N+P95k+0udZBi8N4nGPmRbWCS8HhIZuiF+GamM1RD7tf7ZWU5TXRy/Poku4mq80367/yhxirloHYHj4hl86VJOmA9z0yUiw53GQbd02KFESMtd/TjawYB7qxJ5cqe8y3Aq6rbxiWnBNWDSyMVL3bTnRWpvvKGm1wPMLBrI5GyX1a1poZndRLnnDwEh+hnMPbliiGn+L6qEZqP2xHW/nIYc8CuYTWFyvNk6bZl8/r2Ki06OfmMgosfagzvxIW1/8uY9l/sqBM6uBs3yXRXjUBpAF6KdUdfleN8FNHo2UnLTKPUOnaQMnDFXipagO5ng9z/Pdj4jq1nNrklSnGm3JPicTS8o5FuyHINvB3oziwrFkgQcbflUL1Mz2TQ4aGTsPby+69jdLLe03VfgAozpStCCOHk/96sl5fQ2eJZbJxcAUzwdx7UiZZSxjPzkd2OmgbdOEPSPBRe2tQqlKTs1h/ODVAL/CDXYeCPS8DaSBc4c75xBticRiNDP8arcMr9eI3hMdsv6Of2fxXd8MufKVRJCCOhTnkfqM6EMJ1+bu/j+Lmzv7BDsoMqtl0/nMCJQX56brdYP8N7Fo3zoKTnPrD5ruEa7iDzmn4o5HBWzaRb8fbB9yKihdtLOHEUY7PKvywyL40SnZXahR3ULaJEeg32lCMq6QEH04eO4hC69iaHc6BNdMyXbRP1G2Y0DsY1XXxx3l5t/FzBpPiLcF5G1gWdh8cS5Ob7R0oUnS3jPd6M1MhVod5cK+lvI8euthsPsaV1nuZ+d4QnpCA8mU02t7AsnQjt7CBu9Hlbnz8FN0s966t4H/o6rlvFpciu56eblapD0L6HIYQwPaCzBVK82VkGbYkeckAzV31z2GAXeMC3Hqga53zxeqg1AwQX/pTAxfc0Aku3CxUgYMt3wdNp6hcIGTlD6bJy44jxZgTMswkuRMjR/ijuz5v3I8fJ06N1pFFHd03GPWNkOjxzpC/qL/U9lh3T0XCi1DrPfKNuZDoQREXcY+Mi9JQTjXjdYFJC9kSe3PDTy3QFTUa6dK6cmDy+Ezd61ZWbwWxfDzxSB3GUvAbjPHTeA3FRbXaYnEIZ0yuC5CAD2mY2tfcovvZnzYi7dz89DSK8EkM89j+BKu47xnHCxuRzclL0NgwaG4psOhdngoahjqyQj5NslZLb+DmPZkmK9tJ2kVZg0OJSAYxFMnhgibbR+Ch5DmtrysP3fM/N91G+vGswKow907dM3YYQwvwFvOhdb7keRwUGvV1Wa/ubq+j5D7vWHw+IT6GtP6BajN836sS/y+3rbylCA8gj3dx8M3ycaEFBHtD2+Tth98OI6nR+ulk9OoQQiggCZq10Z6X2VD3Dyd0YBfUeA30wdF+CsQd6fWYQGEwuLKiaMwhwBWKgG4lDO2X7eAqVcgm5qjoa1zdojIr96AEinRwX+iWq4+do/8FmIde8733uWy/ERSCUQJBgX/Y+i3vHbSM7FngX9P47sYhHEHRH81I6IPyh8focHpsTqcwiB8N5HXimrsQqZDw4Ha2nuhBu3oTugB3orEjxhUHniGhBVufmonFzpEU9mvoldEuqGZYIsH9CKOp5fD2AvH+iYzAPGX+++EF5+7tyd/N9OSIhfaEG6e+JomiGgKOLTFKZO3H/Kdc4/NW3SM5+XwPTWb5J39Scp6Khhsp1sucMY6Yl+umop5Oi6bSLY+uOPh0gWyYUppycmpglnHG21/Dy8nWGGmp+zL1kpO5v4ncPH/C012uUkB6oWMqJ1TFr3NblWKNBEjQrJrDIUnSPzCcgLUs0blUzkpp2euD09dY3A0Unom7DGDyGl0A7ZUEh+1mVu/rbKTJ39XOt0mo+j5/d+w9fvXo9+uCeXgMIQ+XG1iIey7KMINzODCotJw4KaWOkb2lDYPAVJ2poy+CCDQyVrACVHEPHY2ZoPFu4dB/ruqyf0VGyAIFcWyrfW7o5v5eu4Fv8AkrIFoDwDKc99LOGZPOE5ls/zs+iDpJ7Mx0Z+zbtVjIdnr2P0xENoic19LhxQbus5nlp5cjUTAhBDXqiIgUjUV1ERAmv+6cmYoSHv7KcIvkW7S/jBzs/0rwP5ydxiGGjF4dGWkZDxHwGb0Jab3xh6Qe44eO3dAWtrzGXTI9a92rCnxNrSCfzimvMrDK//RX4PSaQJuvAe5mlOHPXb2ZnXH+IwR4xZ38SjZJ3hxeyqiGbdPwddibCs8Se2P1MP9h/kC7iRmRI5r2d7lw5wjM5id+Xr5jDg8h3jiDABTHX0JzKdwzVw1pfGCLNKDuPYIE6HyGEUL0LcUEjFdfgyJyfxwu+uNRFe7AbvfurrvV3QtXkbkVxeTb6zUMzJX+TrouSMPwda9X9u+Fk3DkO8m9D0Py+BpEWltT7PicX08n6/M3zmiGW6JA++tcRivZAog3xQmqwhaBOI/ecd1UPuQybA66L2880riedmBBC6HXiGitYn61xHwgMkBTvgccz1tcDqRYehFNEl32vnGdUvxf5adOpw5J46To8CJRpkxwNo+3y6lOm6nkuNJ+oYzE8ic6iE5qzRuaJ0n2DHqH+OHZiLaESZGn9YXKoqfNy5MbTyL4a3I8LwXlAfIh8aCEoz8Grf1S1Np3LoA9R3yJsxzb0Xo0m6JVFcrsfI2I91fcEXr0DEbTnCj+yNNx1crigcl19pJXYj05Sg74IpfTf3qNoIKMLVguEYCXQjibgGq4AOifZGf6aP+ttGCSds/+Urz2ii17Rt4CxcdKeOM54z5vTUjrfU1oUXeO6XFT1EKBjkSinJnxvzsocpd1MyTrRfkVo3IyjfJcXOlTgsMERm+2Z/UAk3aqoQbwaRXtSRl+tqnVVH6BkPdGPCxa3M9AA53gnHgorkJGtwj4U2F/O1c37/bBpzHbcOYT9c3mCLRgM8IhEUislhCDdzJ23xUDHSf5Me1ALJ6F2vEYQYFpl9WfR6MzfiSky74FH+1+4Ktp7vA95K7VX4+pD5XgU3jJCC69Bxfwrohv6OfIvS7aE2NLGgymeo/w77xm2xHpemjO04N63Vi9V+O8iOOwp2l66U5YmQEwHJ4RvR1TmyFZapsiTGSUhGeMHLSw1SwlrX6DzNpqCvozWYLqjxqWGGnzvmspDIcETaGxOJTkhmL/NoyseZNI4M+FZw+mzQ6z7Jg2C/t3iLpQlYWCXJuKWRwO5RJTHSN1gRlYWVC8zkAWmozp6DbZ4kNSE2V7CvIkeNRltDIQX8038Md3H21eDy3mj6OVk3zdufO3pLvYb8ioRlnDSKU3o/+Drape6qEbHENqDXpQjKVxHjs5wLIy/QuXwYi+daF/AAcEmoyF4+b0ZXDg8JYgNzk510e5U4+FRL+p7Xw1RsQOuxNLE8EpFNt61IAZQv8/OHOJsPKg8Fc1CgQTCU9gcmTppmcFacfJPM/T/NQdR9wqQFHcmeJa54zaGLIlXufJwIzKU0JU6B6H5RAPG0b14pnBvJnt/wSnbV0NbeRl/qGcaJP2M609szebQ8DZvFYOUWZA9YJpplHhwJ7qWUaVF5Wu+7rxvRQMo6V9b77mAQoRVxZ4hBAWpB+RODYn3jvQRFWRKzsU4mYnxpuRZI9Ph4YJyY8YUFDdhoqka1pOjIuyjMWvGFePighTyq1257Hh6rpDRsiA85jAU8fBnpnfShgpz7368x0ICwcAcOFcJ8KcTR9dwZKp7cfVOTEdige8rX1l0hN9Tfa5/59/36vqGzhCxc/VR8nH4Ox3WVelyfU9k5RPwL1GIdKdsGwY3skQxhmBoc75g7+E32lpnlSDfcx2eVZF93ewmmXYE3OvGMYfIsdTX689Avi1f6Jqa78BwIqAZneqmaH6Vx3t2i9Tts/sXjR5e0vgxc5TYeCPGO/txQVNQ8E7Tulf3Y4praQ16x924eQ6O9e9G0FOhQvrMSMs0nIk0cnNzdUnlSqPL8X6MIt3+bcMQx5nBr0XhbHg72XfuSXq5OdNRReiuUVsnhBAG9+DkWnNS8kHonLitZi+tgvV4o92iYxFCCL3Hm3mUxWPrRQVOT62u7w2fxw1JnpxTBxi4Vq2ARRSU7YxaVOK8tr6MG7fUUxRqDq27vBG36Yt7UEubR708P5fp7LrDSQ07/hbKFoQQwhrn+bcRqH3tsvTEW1gnXNjeh0k+Z54eYUx6c+408RDwjZJ1jywpZ9rNI26m0Nwr7j7c3KjNH6J0bbdb5Jwkcr+A7Nt1ODxXCpUxXTDbdb4IFknbCKAv8HfSzVzvIzBFaSknIlZiLOxZM31W7ukipI6Mb2Apl8/YsNsw2BajIQRB/Rw3rksdCORtBpd7hErI+boRBNmyZZZ+fRoeJz8SKpcy9KCIjz9nKiFPocXE5rQhhDC+jQjNtHao+1Mwj7DURyoP6uBrU4RtoeLqwsjIg2nct0eNaNy71rBxiGailbJF4zD23kurCdSIvYjWpkod8ptRnBBCCMXN5ndR1/8nas7qv20ZtIVcz7WOHlITFi5YxE4np3amOfXxrfiMaP/9sKSIYLGvz5LXJ6JPwdsQdB+sdtQTXw3jRvCgMC2DMB/quinU4NiZLlMBZOHC5/GhOzrKFLBnVIQ7Y1mZ0TFtAYyBFxR4h3QOVg9bACIcnsLm1yHoXHlqU9BCIH2e2eHv/DbnRLbwIPOZZtDFq89Ic/CwrNghSIlv8nuc65N1CDLKdnE2lsQzMvLUGn9LVkWN6K444gUBuaySxcmRXv/u3chIo+praVcvMq/ER1X93Do+458uiMWGjkRM/B55+LkoIRcsm+aN7hh0IchCOg/Ly95FxwMVRru/2T6GJkvo6ZxM9vRzWcrSnKfGmUUuIIHQqamakNoIkg5+fXKGpPGuBRyNX8f3uo/1vekhSKQHengU0F15AYfdlZbZFmJ6YtdA9Mz2FL+9T/yDis/mlO2W0X9ppnuCiM8CaayVOVdMW91uKSGCnJ69mkYqTKF16sgxfKmO1wK/zUnXaaN6YQTpd9O5KdswaFtJYK5cqJEZ3Ilzc/Ch/sbrd1D9U1CnlCm+ClAiR0fLaEkx3zEvHYO0hVXBzgws4VzPDlmuRVf9xmV2PovvXf+Ffvf6LP62iaWEyFdjDUH1woIiLLGyyvyEKsRaE2lDOMtcR0RsQwihBp7b6ELXM2UiKpd6X+QWMSvgFaZ0xFwGhmuJFVslk3NZAa2ivM0fGtkID+Yhy4viTXtJM/ONFAIMIYQSKpYYJXhFGKXGdz9Xz+vyJyASO/kRztYQaTGH4o7+NrKtRvc1DKYTxcOj/kLv4/qd+EPr1mdLyiXNIRnN4sZ8dBidn08+uiufK6H6gz2xQtASYndkWDrI1KNzQmpIQTl6Jcqh9XTHbu8THALved1mCg8oeEl8fLP3YPuqtCigKE00bS4qPRpmnWxWCTI1FYKuMYoNOnzPCDlL7oENQouG3N28hVSiqXdT8K/cNhVjGPvyN/E5T2+bKiv4EAUTalvh+rVvDBm6A2IkDN2yptd/OkjXfCkVNqd+iPyEEMLRTjRYnZFyB5sVdHy2v7tVi5O5PIcOjx1iHC72ubrpbvwcNUZCCKGy9Q1DqTkFNPiewg9NKOj3HpvmkVTs6nrY/3Wca/bcKl/ouszN4aTf0ufFMngGFY4SkSuZUCCWLuvyVphhKU4R/BTO1Q4uD+J95IzDE0h+Jy/TbLWW2Ot75PMlqpfwd1Og7OuWnVdX4DtZmneJysuRkal3/2Gz8ODaCzPmPJPSi3+ozN14qc+awq4uy5E1Mk8UHmi+6Vh2LMRqvyL+zKMTaiUsAEeOjvRBtZ7Cc2+m33KytxMTjnhpztvNj3Y2fi4EPYC4mfsPdcM2QKzOW3VCVhPWKYSpirixgwfX8jmW6pf+X825MsXF7tUhBGkSWr3Efdg0cgMktBFIUMd7Ts7moewwZlppewiKqkm6J0OC4IcaRKOIrDnfiPC9D8ogJDY85rR/j1oveg2qlM+NCiJOOuZWNEaCzq+TbcMtiO45YQ2Wk5yVnKVzVlPeh16BbSgmx/bdmDrKM+SMVEwnxBGYywGsMbaqc31IYi4X1YBwb95tq3OyV44nHtV0B19qvpak6wTnYby5Ysf3Dm1tlkrtDzWkuGWSHhnzkPKDmmlfFxSkiGDzK6haNzz1B/Vg43yQL0qb7tw47gOnB5D7ufeplUmjaSptWOXaAtxWukNFRXPaS+9ywL1UMZ95iKaprSdqyBcV2BN+t+lakGaR2zGZky75GZaJwZbjfbkIraqP63oZI2VJovLwti4Yon7fXWsJjKEZJUlBwfjevKm7lSmQhGgg8qpMDzTO1ONk/s6Jz3TEPC9M/oK0oHA1ZRzo3oCUOUX2Z/HcbwG/pW7dfsd30o3Az44itHI1jp51wazjDvg9L9/UHcBGc66hc/Kf4lxefID+LEZmrr+Ir90YjcHTEBK6OUbjozgHTOmEEEIPoortr42UDjE1SRVuH3ovFQEkLh78Ukmtg4dx93vftaxWJpxrNv8b3DEUhFC8HaQ0PM1v4pt9S+WyrH5039KHaGo76dmCqG8uS1+a4fRu0xy5CfgcXs6O1xJMjdRc3bkff8Aio/rqqh89wsO2QtAXvThZVePwdK/j37nD818uouhdD58Lia7wQOzO/GBJq9LSf9eg/TQ+2L5NUe5uRhWaTw0Ffzfarf0P9Tn0H8c5dBIqg7MBAk3nhDJ48MIU7jmiCN6Fe3QLFY7n5qzcxn2c6inO7yMStLB0bRFIfeOFvBX6j5Fq47ljDUKnQJO8ZJ3OEKs1Q1DAgan5YNy7gKapa+MglW82o1AhhNB6RgeF9t4qn5vpzpb0KgSSLC2qggIkLjKZNTIdHnpmjp4Q7ZggsveKEZKAy/30H8coYVnWB0CFSOcr1MGBcMIxD2TnL8jnpvT4/XPx38yBZjVTHR3bZoDhdwIayZanjWhUP70+ks9RFM2h0PwsHQphuSc3Ys2qqJh6zFJh5vN1TaoSWyHYGcnIdHBihorBOX6KX2MbBtcpc+SXpg4rpeeG4kjzzcS+iq9ZCZklOOfChlzrbDPhKJSIEhoBkZ2i82W9yVwn5cFYxEehNufpiDCj2QXGLSx9XZk4Yh4T2RlbAziM3QYEQ01Phxo6dHBCCKGI1hKDuf7m01bcq2Psv95MT6DyDfe+3td6uTnt5kKSg1Pa0I1/8sMOrmc8164FZnxvdKLPQVSAbZ0SkSEfxKvA8sITVCgsP45GjIj+upiOxHbfNgoGKhmn1vSXyO+CxFujMNAGU3Q2hHQibqKqVVSd7S3Yiay+a4KeZ0kVm+joDOm68rUV/+xsvk710irV6ul15HWk5OhXeKERQZFEIVPGyHR4WHniHvMiRSzO4SuSl2YJZ2LzyMpZOiudnl6yDxbQHwk2LBXT3owEhaBoECMDRze4QIkshWBl6aag/JO96Ob/p+eP4ndZxFpHN+heX40qSWwL0+8hh2Pn8/jbyN8IQSusvNSR1T1S2WPPieqdvockGkikZ3ioxf+f7r3eevk+B/cEES2Pdqin42R9DpfYLxxGi0jxQl/3XJeUVQghhEJts5PrqS/+lrX1osqh8qjZ1kh9DiE/qSa0fH8ZcL7zVwipT07SvbniNUM+fe9mGr/7R/tn8t7HnXia1Eqzja9DCOGsF2+sbO0piPic9e0HgOC8MI4Wx2wnPsOdz83Rv3PbPx5C2NCeB7fV/nr7ytLnUEJO4zyGYIGl8XR40I3tAGMwyVSup2xm6MBOrk8IIUz2Npf2s6t3CKbCfKP3yGDV0SXaLYLzrn1FkvHEfHSipXOsGw/WeY9eHi8CiBl+gNBLWrr3SxC99fOk/Vk6X5Rnf+NF/Lvrd6yFBuxhQocMZ3HrawqRecDE4Pr1hXiyER6KKdknqxCnm2cQWSvdzR5bCHooNgEtemfUyS5y9Z+pUbp5I97Yyf+u5T/jN6LYDPklS2sYV0Fawb3ina/i93FRj450QlimePBr3Yi9x3EivWz1bBL5OONJ/JyXLJLdn7PDaVkBd6apc7cqxuvwHptP5GNyGHrkQSJ6FTo03s2c6T832kVcY2YNZnv3cXiD1rCNZencE6xcTJThQ+qAGlAhaAq198B1HOLLEvbBrJVeluncOFav7HwSb3Jw1yJuptaeqdEovBMP9P6N/l0L3cGnQIZWNdu3Jyirt07nIkhqmlBSEk/VZevGXofzkrcU8H49RhkXw+ilD0b6O0/24gl0boFEH+KF9w6UUzdGGL/fjN81PtMTjkKg3TflrXC8Y07U70bjhdqP/r14z4OT149mv6/BqldBM81BZVqibMUttK2OZpb7m88Qp25MsOea31h3eyCdOXA7s9KHUxMTZeq1rBnssMDSoY2oWPBIqYZlzb58tTnAcyVypopd8JRz55XVaeKI/qDmd7CvTLl/CCFb79NYwpyQV7Mwx27v4+gkLGvWGQCNQMdH4K2OLc1JiZjy6wfG2WXpVJU10mH9PN4Yex75wymO4ueKJiiVpt/gfVAECjXuDI324MeaBqLSp4pN6T0SSXDYnxEACXnN5/rBm7egwlmx34nIwKXn/+6rWI11citWiz19YmQcODLFSz0kSVRmOXEIIUxuxe9uPMVG0anKRDU5X0JUziDHu+Q5vf+icTWpsUjUwRGJbRjkxDDK83SqpINdWA9rwEtr2b15hdSuOzW8RskChOFJXItjpFcdlRTktKnrZn6JyqO6/gDyY9ZIfSWWEG/L0lHUYvJDh0FBCaKHjvhNl3Fhftnfl/coNlhGxZZ0WA8hzFGl1a5ptHYzjHNQLegcDFEGT8XnYE7fmt2yHbkubkaG1tbjIqvoYRuGpFHY1Nb4aQyQHCGpXaKKykjLDC5JZK1c6vNalqODOr5l1VH4Z/2L6Lyu39Z107sfv8srF0Xd3NXo725OHXuPvTFBPX/8QDHKnXTZBtpdl0ehk+N8V9qQ1rM43zd/ateXwhHjIKFqcmp6cCWcQzzP/R5HUMH2tjuCjsGmpu2VEJLFM1kjm7SM6/jhI0x0gd71y/v30okY2mUdQmQmpU2Slje8Yb+l0sCM0u04sUJotvnZ+QIojqEWvfvxae19grYHbxiZC3PgCFUJeg7znD79v/zRZ69ePx9G6MMrUvZ+Eedx9K/Um5iegexsfIs6nByWTmbBka7tQIMmFRnej4v8G0vBpEmGhxCkfwod09aT7YN4SLDe/Rzd0s0RFyTI5qmAktaZEYk5N0yNumPEOfNOwkJcRNqtaFE1n9HKos1iK3pHnuph48yAderR4AqcnpKlByZ346ngPayI8EiljEXAswWkJrwECuMajoun7l524p472tUJIsH5yoQN39qNTNLfdBGcGBeqekFRU72vdQppedFIJ7k7ZWAbBgVeWeXkqZjyTfwh8xM9F8ZH0XgULZqXAAFzMW+rDab2l6fbj/7t569eX/ybN+L9WoArVA0LVOhs9u/qF9C+1UEyvvxAr8Gq2UpHnzNL3Yd/Fp3o1aU+dJ5RjjQp79EyGTij6NgFQ4ICAg5XkJ+jzQxTcD54TjhYIoGiwXQ7X8SbIf/N02JSaZfCHdo0ssvSMRF+SJGItKzEmylb+bp4ab6/cZ8kJbW/NKXNg3j9qql3zgFVenqAi1CIt15CDJg00e8G99h9DAGwjH5cw1NHYPjdukguJ6hOKOKGx/oF138GFU5rOxEa6RyI/ttxvipn0PKxtBLnxM8O8i+KKR1xQzC1UV+Dae0C7LsXWNevy/nahuFROX+TR+WMNpP93+J1GBC4iFhad+YQjIz8mj5jLkNdtWTl2gWWdoO3U+5YsUGLQYB9HwxpoavrmWkswubTQ70POjKLmW7cKjhvUyjk5o1wvazCuO9pIMGy992qiQGmlBCWz/U+pgdw+lx4MOW88L2zFPmAzX/zQw5JL+BencPDg5oNbkMIYXAa/3DnY3U8F7VorJgWc/Vdanh5qfLNXz1+9ZoI0to033IpwV0Itpfc9pF7CNAoUWzAM++OoYGwz0tWMVr5eoZvr7IZRpJfYglLw2k7P4oImue+RtFUONG6At/Hc9QDaFJg3K7RiaVydlblXlZjYh+ZDg8FlBZWOkplTBIoD/9RdyTzz74IqRHC0rL8VBcCSU5j0+ihAu3gRJ8O87HUsvBUDLktLihFkmD3AXQeLC1Dz9rhWiIrDk++BBmyVY2rcPe+rhKKvXkDxEEf3J+hPtLy1eYoMoHw4P59o9AIcLM5irPzm81liSGoLoML5ZHTQtj1n9oR97/moCPTfRzn+uBDzRcN7saN60aPTo03Ssyz4SagWm8fwS6SqGgAACAASURBVKjaic/aly7+v6e0+nAoiy/NEbgLCYaPVEa69BaT9XhGGYfA8L5VvFwC9bTmpKXe5kh9bet+NsIcm8M2gwO0fxAP0JuiprRYdvviKyUTFcAZKpuQ4d8+u//q9Tu3Ysnjy11DylDZk2grs9x8crmGDPsFZhFRf6iRpv6cEDf9dSzQuPlv7sl73CPD+8qlqnTim+RiehBOwqvvK/LoeNY4N2R4B9IdJiKpYoDptml0Dylf60WVRyBbe6EPc3wbn2VayXifuVW608GiG8+20OFkunE1MaSpgYbWnymyOT2mo2qBCjlCGX04pd+anZUDzD9Tcq2nemDNdugYhdcer53SSvAQ4CUf/02MjHpv62KVDtpOZE2B76e7ZpR4H96bDwvDGetECFpP4x/27xoPCA+gdmUVL9gQ+x/H1zNj99Nr9QNOuFBDcwTehXIlVJcn1rtnsUiHD+nh5iwakI7b7G/i5Yxcx96+A//e+TL+uEsTgRzcTSevU2W1+9A63rMhKaMQL+fessEDrPvIhbHia0cNq1eMUo3vldKDxtuhSI68lK63Qe67p0PKkTImSGAI6rt4a4kFScz4nUR0fBStlxbXn7/HNSBzZ5c/vR25GP2J/rh1Cimt2dSALN+OXzYz5JTI1tikH3bqKaKBDZ3HFQJFP4DCajOqtrT+QnJgbF8MIPfEvexd36//RXQS05y9EJJd1vlv2tnKta5LgHWJSlkGD9ybLPgIQYVzvUqLjrlzVdOqUJ2zycaik1sOdYPrifRwxZBTIim1BD8wXp+9+EIIoQrH8epHqAY1JfVRF/vb2k6wapIq/iGEkAOHh8GVOyS3/i7uwXnbkNnz+N7l+9GXGJnwINFD78yQNTIdHhollg77e1c/jSiFfzkPUm9eyEEuji9W8m92P1ODNTmAtP2uGQqR0k5XPKV+TGGa7vELV8k8U+ar3TkUwq5FeRdXcVL292MkOpvoo2HX2sWV5TP76R6/kFuB2M9MlZ9zkuWRU8nTnSamLbxKa3gL82hrqfUkekeX76e3Ctm2cfCP8Qef/6lpuGAu6hc6Gez9klW+z73j6QGSPMeHBu2jTJ2wsKdyJa1ZtYMaUZ+3hWDPH3Ji/KDOoV9W3vP9+DmuRktkWZwcS/NSmTzNwQkhhCGqHxfm1JC3szKO0HyBNLsRmulgUaOncO1zFV9XbsJrjdwi3db6QbsNg44cHQtPVywzAq40ns5v31xvfG9mauZEBLK0r9hJnQT/EAzpdu2oDBspA+sof2Nq0Lsg+XsqBsUBpadxfTnpl53U88Z3JRLsQrzdh9gHlDKZuoguugbUrdrt4/RKEs4dQSnv8jLdi3NeNeJ573H0jspZLSOwJpwcnzVeu0rLFT6r15u/xBEYLpKaNUCk0yAHpF2aZWed9/Sw5491VjoXXlbLghUWTfeRTgkPXXrWrq2yLDPnagYL62lmBKv2TvSArj+NkPrKRNyKjDbuG3zSg8PjzhbumdHL3ie2GR7BO/fO33DYqOTsbUQEFfB9gTedmzM+jDfJlOI2wvdcb5c/jT9yYegJsy9OQuXZnLaPfDiEPsc1PUXIQ4c6F65TJdc340slZF9TuTtxgSyhPkkHJwQVL1yZw0Oejnt9rI4RFWZz5p+cR7JE0cQ4FzPMD5yc/SPl6Qyn0UrvNTQaefbhnfh3P1bJC2r0PL+JJWfLlt5H+SLOT1bAx5FQsH0a/8ODum0YtPl7H0OyYF+jcqZbHP2hzScvMwRtGDpCT0QPAig3QqcmBBWzFbV4OzNY8u1teshl9L6Q1IPjae9Cs4VherCzhuPIcvbBA/2u+jMG75bWw/niTbhprwtY6p4OXiJVvLaGwPNbaO5pwoMeUP1+OP+GfN1ZS3kRtK/5BZSnzcbRHrqmU9bIdngYTdjDoVcvfT+Md9H8BkrI9XQEJgdv1NGBZWHzd4WgC5YVKSGYJgEQ6KU5PLf/LhrBF/9CrRJTM2xg1r+rm5nKul7pxU2URSItnMRVuDavW7RcBukRd95TB1jz/C3OseF7C5Mr5+bgpslqMuopLVFhtjVCR5JVEq5mug1DolmSSW2ziyaROZDS/M/WCvPgEpVaqrXxFJ7hfcuz72w27isrmHQxQA7uwZV3dabENhWTu8axYQNSkylYIPXqlSbr4us999UgTvLMUhMFqCTnsTA7l/qjW7txcbOUPYQQ8nfie72xBloHjTj/nU7cMMWO7U2RD9D7zy02G4PC2DhZ4IMVv0U0+30NQdLh5DS+1oc+O4zR+6ylz4uBsaeyeW7QxvhBR0FBt890/LOkNYjMjk7MGRKNm3SHTRAeyzmtQE1gm4kQQljk4RwDfV0n2pXE12MrluAaW9TSHQGptjWeUa4Rz7m55cGpyO4iunnsf65Tz4ZQJdnRzMp1PDhu3ok/tPWVGtGbt+NB4Y5v1sh0eKiVUb3S95rPEb0V6ZDol5Mv44cCPVymhKpm3LkbvNkbD08/FLghpHTSZKo7fxQNVpZ+TPdR/Ae70vp3OSS7/xHKAR8aSevTiN/vvxe7e3bz6vnOkeJaW2k7Wfwz00YogjOUS/HAE8OmnyJhjBo676VHQHlzeFrP4pezIiOEECYgjdMx3Ub4noNzkeBn0IC7NhXWR0KHB3uJwcLM1n3nj+Jke+WiOlHx+s1nlrbKx3U0t87KRBiLFuXlIUw27aIowaLZ5m+ofOsHBLgtrkaL1A8rXko9u8dH0SFZTtPhwPVVvMfqiXodLLG//ES1r9YHWMTewBGVagWgS0tTpmXlWgJZ/vrZxvsde2saRC3DO9sXBFQhFsdWJt33tN2Kpq0yHAb7iSQtk7fTe6DzxO92bRY6Q3MUvrhA4XQH6Ukr1CHy6DIUUnFHHuWNRRkgwi+b+t1roKqzx3GPuWzDYojqJWtd0XqK0n+TqyDCRiRodmG9GW8h8G6p8Vrk4yL2LAR/K1F8d/TZHNx9guk+dJBIE7GvIind+19mjWyEB4bZ87H0Hpm7m9czvEq7L+YbeXiMjchJmKv50pSWUSlTVTFUIW1RsNDzx9xgCY2YFSed9+gRClJrlns8/xNocZhBPH07VnicXcdDbDFR68iINddLhxK9LxF5A8PT+Nq5RPJddkAz2iAp2lNfVehPuHE//ykqh2yR0zniszn41et6aN/jwKPlpk6U2lN3yCIQ0Z7w8nJ8lHIJngIoYs6cvEmxTBoNb2LKvjjBdJ9KiD4T3B+msVhFZWeAOivmHENfx/sNMW0q5f1GkmQ6zfWnOHJwXJynUwacv9rXhd9CSw0vIrgqRC/t5CCWIH7d1wNujSavbv/y90/DpuHOAG0QhS+3ZVTPSODG4Wm3yvXsXbLzs/hv59UU2SoFoq7eloB7sNLX67OpKYMvR5rofPt6Y3o10QGc6A8+t7plkV8fv83LzSEuSx22lRes4LZ8bzJ15ylsIvKNF8iMPLS0GNvFlNNTEvkr06JDJS5T9VRPDkHPTkfKWDWXh+TFbNc0l9BTMy2Vtmm8tsPjvAtV342v3fDz3+408RpszJnQb8Dn+qfppED/O3qx3BxzM3orpMw8TbP/6+gZdH4UIVknhHGumk/Vm7j8ICJICY0bMPka9bg5upcKoa8QUZbsAdO5cKNKx4ZwsBPhWBnT/EYf4vU7QK/YGNo8dxGyy7i+zwEP7ymCws672ycr23wBPsEt6Ga4viYdI2t4K40zM4oB2l8CWrZqBqY4O+9Z64en6HWFiMnTmBJxTww5BVrjQqDrHhccrpGo5ExPY3KduvQ8JerncLadoLlmU14TtSk3cKidxfnJ39a9yUqsQoYWEUvgQwih3IrXaaDFhVfl0D6t7JBcl1IkIyZ6H0WgFd8mmv2+Rg4yIt4Hi4MBrvN0FtX0v6PTTj7Pwr6L68+7a9dfxOc1uBcXXOVGF21uSefKHR5+V/pekv8f23mF9FTOeliRxlCpxfdGHd3f6wyzyN6Jbmd5j5RiKVjLFgodFnbUYZv3QKa2isTZzmbwwfuV7XweA4nhXT3nJlgHbEvFjg0hhFCG3MN3hvBIN3PXVGCzN5KQTNCOPbfc8LMySzjL9l3k6fg1iDIkSh1TFHwTCyGlFDiEEC5+GjcHiWp+j6wsu35PMXrqOawNBlziHofo85OzRUi9EHcqBa3JePalXvp7NMYJ+BAoEZ+vRzk1IDxZ4li+Rnh9Pk/XA9qGwRQtIwsnCBI+9qpDKofnLHrb/SIamOt3YFzMyPH6TgrsPgLnAxGUX4NzvbCScjpAiQiK9b+Iwry8nE7vomnkzUl6kDEH8lRDT1DXfVqjnNYrTRbP4h5kqbj3qCsxkDBFaaI6ZatWobLz1x3c8FznQAQXfW+uN28Sl7yYoSzbU6DbMCYnca4lwDUURwJLW4sMVh1lZ1UjNaZqnsqlvIUdkL034j1S5yg/N8cIrSBmxhHifpnbWqSaMGVtCobG5+7FD84HxgM9R0sYICv5kV5D0FKv08FeOvqlSidc/BS8F9rZmd3jLkSFXeSWw1QJ+ez5PAd3/HMQEjb0p3wV7V8fFVsJrqOIWKYL7/rIdHj0B9gfAnqq3vBmXm8SQlBonyVojlLMMrqZ8/pOYuOk8PB0Ay4oUQYpkKk2d3hYsl42OHUJfkdhoH9IdeUb/PC8OUZrwKT1F/JW6L6F77aGbox6xIG1J0/nxcnfacMjIELvCSVnZAt8LbEMmc6bpyi3YRCl5NxOjGDNdZlIR8ExcOf14o829w5IIIPQ5XF1cxKOicz6uudXJ5BBHNSuF5InIRgp2knNTjGUsxfGVkkGwmPlyvYtS4Mrm1+HEMIM6aOc9eoKh9FwVlBmO3upcNJNARU15gzl8d5srL+tWY7XJ6HZm6SS11bXQq9ULRp/FkTAHFXdhjGC5IRoRxm9gYGrIzDN/+fLV69n796V92bNOL/kypV7etCRqFwx4kuqyOO1GqPJz1AxagEi0cbapb7nbUN+P5ZtS7WCL5Nr6v3P9+GIYe2Vrg29ROpobgUm1KK7eUsjRjpsQlo+t317N35w5dWbcOhrZ651FF+TT+V+PhueVk2qYYou93RMnYDNhrKzdoZTZuO1q7T8gfbvA3HgM/Vfl4E4aIt33JQz4FfpBwSHE0flvjL+TsuE9YPsTpyVOqq/BCn3ri0g3LTD2he9uGLvgAvw4lKFcvh9g/vyVijD43duDtEEdj336eC8UqQxhBC6j5BzhX2YqQCvrBfn93D9FL3snY5Yyt9sy2Dbk6H0A9IFQSTSKwuP/j4+JFYbhBBCAQYlq6GrNviz91iZlWELsnLfgnSa0aPmTZ7cH/fKqNfjlYvjdOg9rTrUf2fjC1Rpva+egBM9Xw0LJCYX0ZtYW0orT16QXY9kZ7baWO5YiuR6MwkzhJDaENHtH3WrErZxCwY5lkypsBonBCWrFq0I5vqv33j12kvFqTk1OInzOTp2vhS+61iPtsYLpIiOodL7YyVW0w56FSPtlCM8HOT6rOyZV+/EdVop6wbsXkcUiiktdg0PIYR1EWkfm8c6OK69R+m5L+misKfPqVEFcro0tBGVkTtf6HO6/CMQlaEFtjAhSYIW/RN9TlWoYBNUSeydfLr9yBqv3y3d+RpwHsn58PYOrPDx3PQIUCXJSzPL75LE51yA/GxzxB2CCr6RFOebgVG755alQy68Z1e+ZYd35ztRYMpLd8NJfEnnx2HGKqo9vJKMi9ejYJJDi4iyvWyczotXIJCAxlLxoqlGU3sn0eSQPF3ju7S+Bvx5D4fpFlZpjW/Fm1cYXj+Xpf558TMcshkEeqaD/XNERz1aZtUk0Z6RSxHAeS0O9QvYt8oF0kiolCa3JgxYfxb/PTp1ga54TVebZgQrAmlG+F8+oJaCGVWgOtL760AjelY/enPSNa7Z2NO/ux7HZzgcRmPoSBMR173P9P7Tmoe65hKN/TZ2S+dgwOhrVhSOy/omf3MicKVuDnv+eUNdrNNyLz3C3ftlhI67P9bAkvpZCa4hlkDLeI43b4FTsh/XXm1f1834Oq6ViZHX2IaCrVFcPV/u105wOjmO/HLtTNFqKWepKQY0XsFVQLrr7C+smhDPZnDKVLfeI89OVqKGoBIE7U/iQTfd0efEPVG0syxrZDo8CuFZioUN2KgrYgcdjXH/1Ehm+K2MEgZWetmIhUxharwOOjl5iwzInVjBK/acHx2xWctaOuD7qiseQFZZwl5dXrGDqN2j6ilIbWsiWXm9CKsApqaSTEVQciNCUCOQJb5IRGayr++VKMCFKfYyZEoLJNKGGcKSw5N/GuP+hxiiw4NUqxNv+cwdNRSDnkhV8XU6SsThFYNpIp6JtixMPzj0Tl6b3WO1Gh9SrRJfT8u6+VmK7kZ7RbVYQ13YfoUOT4K0zLVo5NAVVJLzICYXrZpLCNgtQ2dwAE2nen06UTttiId+obAnUbrhbT1YWp9uPpR9b5ILk5A/2LLBNVb7RqPk4XFEU/b+UXsFdj6I77W+tlYHd2KQoYilzQX+6YErbdPgJD4jPwtyx6g+trOMtqncs1YsSIfmKiDCF/QZF1GKnrfKyCUcjzqa3w6CHnqie2dIUxUilV4BRWqIoOfWPJT8tFA3hwQIT84quEq9uEfoLzjvVppFW6pq/z9GqYarfxlTm267SFdZfVetJbi4yOoOIYQGyF3cyM7PoJPjgoI0BjTurj7Liig3es1ncWFcGf+h8Tw+kNpFdAO9Lwe9ShLrQtDogk4OCdch6EP1hcbyxpzlKdZd3AuIyvmX+luog+R52xU4ISw39PtiO4mCc7IoLmh2hNCudKm1Q4wpM7/HrANbULQLwuPbZ9xFLRZGwxW0OZ+sLAkhhDkOLT/A6vj90kPO9jR1RYaGWJYQ3bJVCiu2QrCD1cTNsspRmc4ZoW1DwdpT0IA7esIf5FIKLEvnoeO6KHPo64SaBQjQs6fS8iyrJ51NMits8lWD/SvR7nQGQOza+rn1dbocQ66rXcF/P4oj/S1sb5NWDfRDDrWR8fWyrp4bHf/uu0ryO/ybWPFw/cdH+gXwbMkT9GCBe7No2YRpm9QEHJaGNGUhaLRhfCYh2HPBPQ6faV7s1hvRkPfHauPnF9GxGWCPOfpK1H7nU8s0gH/pv4XIOlNhs3t6jfFT3POOkfWJtpmyOoNy2jWXOWHzVrd/Z/99bCrL9bK0lFb7q3jR3kMvkU0fr53SSrSax2RS78aRD0aRXsrN1NUaEHfRPtf8OkYK0/d1AV2/GxfN7mf6cG7eiDdZgIqqT3IJzosf1JyDrIibTk5iDuBBT2wv04Nmx+rMDrMm1MaF1jdhwxICKTokC3ueTHHNDOEhSVpy3G2LULAoE8q6bHHhvy0FkcjSCvqhxhTpSqZXmX7y0TdO196n0dvsvOulR/Elye5O+Kcz5CR5rtMFNEwS+koAI9auw3OGvjvH+of7zc0KxHNzGMjTWe2a4eyCR2F7jrL6jESdFzbbQyBkirZMRxUu43ct9y3vTfvkFZTU6DGHLY/fKn28XJaD0h6ubL3c7FRWLw3hOGJPgI1/8oMOppZIVJ4e6IHO/eI8nfP/9tar1y6kSUmRwgSOvgeuPGQtzcHGmXRyvKS5gkIJR22Fh2j7UUj5qFxs3VPm89lzRJ1mMnJAGNdAUhxJF8fCW9rg3yXzp7l/SIPIX5vu0SnO255+QR5n2dJS3XlUSso92jxSZuHgb5WEdPEXMdqhOXHfYXJYSn0va2Q7PFh3jtwsuFDIzzCOTeNlvIiX55KgROTAuTiTY+ho2I+79X88f/X64q9O5D2myVjW5n05mI7y30ljLDoPVs1FR6B6pcb9Ug5x05+gVgm1T8YG9UEPaHSsK0jOGe+lhU1KToxXaRWwL50jJPo9LO83hCdTzBAGKAlPxtfUTtrG5qHSpTxjzYaMzTq6HY2IC2lyTEEYTHB4cLA45y2tB5k7udLaZaJ/tGLpudkTOjlDdFZ2w8nnnOvpe2uKrFkEy7+jjINXeoWMewyolhJSZgYfae0K48+gHPtAv4DNQ1nJ4j3JsiQvQjWlIs8cIdEQ20IdHqZKuNa96znTQMWhabi04GBbdVelO8PnwG1xjg0QnmXF7CxMJnmfa0P0lXtnc421kuDNESlCS5Xp3AwtLn98qiVK41ncI8Mi1pdpFJWeIzC2y/P8StAnGOBQH2pPDS2d+4rr8IDzljcJBqqMk5Dv5wKf280HB/Je/SLuVTaqbj0xmRZ2d3DebcbIdHgSYmoYrSebS8Y8UqST47k8euTUMSmZhkJxGCeh+1Bvuf/T269ez01ZUoX2yCDXe+TfJfQV4CWzdYUfQCRpLSp6j8VGXIWrnr0H2X6SNb2b+bO/jju2pOlvQd8q3peIaUmgS+7YceNkNe0kx8Q/J8rWtzKgYuco4J6XGUjQNgyW23MOs0jFXllDBdSl9YAickrEzCvWmLp0R4ZzzX1Vs4qOGXlnxl9ZoaS88YkaguEDoEbU7DCyvli2oXvYmx39ELTqcBz7dyZ6vFWexR861UrmkMd9KZ/H+ApUin6pEzk7TM/D9q7jftzZjxHxjUXEIu7ovLby5vzJ8K4GNKNbJOOm3tIPNoh2MF3UeK6H5TXKpGtXtlaIbNq5U/k4QszDv3rw6rVTBwooTPEggHuwMGaqxOwUUyfu74BOMbDqIsmAoN9UzdS79+/G0//iWiG/JZChAnV4vDFuCyXrA3cqcb/epwqK5swSTJxmgX/PPRAiiuMNrpHiYnrRMypM6ZcMKWPBDBt0e6GRtikJrz1eW2nZ+zBRpp6ROFVeQ1DEZ9Y0DgH+Wb2Mf9e/pyu+gIolL0tfZizQtH5fo+PX9wg56Lz5ISaMeLsPdnIuXOqbsx1CAenk3SJLys0gED1J5KDxdbympwfKGX2hiHKliRCGoE6fw6l0Pj0lN4d9ZypsamXv2zB4hhO99EiRc+iVFERHS9b/jfOUpaHjZZoycI9Mr2bJ4QdTWm48if8e3bVNB2dlMULEbU0ImVbKWSAUmP+3t8YxuyElvo4MTveZXzWjjYN3DSRylaXIvO8km3SonAdSgVVr9ifcq55mCSll6b5euKe3UapBAlkW7R1at3QEmo7+iKq/oVjDn0VvlrxSj4fYiDLZIgj3MUCJ+m2dbJlfTzkRUMxIy1NhvFLURdsdRc/INW4CiM/1eoym+jdWKTVL39O09w4wcB3RnhRrlm5mxmZg6S6gscuqoVzQZaOenfc1k0bb3igYc16GKGF9bO0pUKrvWaWskS08yF5adoCRUMSGnpc/0QkiW9ube/JQoJPTfqJece9BfM9L7Qjx+iHO6hV6iwmPkAvZ5o4boH4eP+iGjaKETrouY9EPbuscVC43k8zIY/hDg6TiynX6e7zn8W29x+lB+qLhOuD8eMopjbwYgm02M/xpSE7uW3ju39egPAP1iXx/cJ5qHX3mYxy6nuZlZESn0VVr6WDlrIyKHB6Sm6kpFYI6rwtrTso+WAnSJAxd6Wqz6FwIQUTKEgU1TAGbiBuNOA2zN8Zlj6y1NWlco2eRfLU5drKEnZOANNzays3zx0BtqUs0To+4q95GpLzZ/LIFQwiK/H4bzZHvazDIZW+qlR10TFff/vf60Cf3YvS0qBo3BygMD7dVya8Ph+FCjcd0F8T1VrrXKOkXm2uS6V2gNge5jhzWWG+kqOFhO27q0o7Cdef9aKwlFWZ7hxWOLhkxxj160MmzofcY1zMi/93j+MErUx8ffxWfU8HQq2UnPrfhnfSAhjpvnr4M+PfkAFVfF3oRonkudpw1XhvhcU9yBIGp41/EsPzyA4Xp6NS4dg3hdv6A67ctMsAh4GkrOkAO7XMaKIiVaIRKMp3zRsi/oVZQSye5doX7MI+zXAIZzapfmFZofQ40TPWwhKDpTlkV8LA7c6KYy81glTF0UDKjFzguRdNm4iHmKblCRvsLwsHScX0LHR46x9TicPSBDvBkNz2aXdnfieYKdufMmjSRqFyxg5SHLJvmOsLAAKT/lh3GeJjOJytdxhub78UfUH1hKsNAL70Ro/QUMnEzUYFF2rB8bca9hgqomd0j0mKLOu7D1MFX1LtyFIoOvBNH4XmwBQUDmBB0r3YfWaXJbzYHGb6/s1Tit2EwIKWNnFqKc++zaFy772tlBPfErX/3RN5b3I2Ga1mJzoqjnHT8vVM4u2tTALH5wsQRsV+m+69vI+mUjt+DAzjRI/ZsGR2GUkm/ewkRIPZ4mxmkT+6Mc3i4ZrNI15zv1q6SbF5ex3tc22JcgZ+0HumX187YoDW9v9oE/3byNx16EXpsuo1I9yuyRqbDw9SDE1nv/E20qrP9uCPZCyiEEAan8WFNrXSXC5Y9Ytwh4UiozzKINOSdqEJBGr+ZwwPv3NvVc5F4ybp8Dk6OtxIYsSfPZfqUT8HfSkCV/G2WtiKvJqEHM9v82g9oPl86RiGEsKIjCRStlJEWcxiTskKeo6fjxN/dyKh8+qGGiGDyN679c/H1witBME+u0UPEMguqdR0QjjGqenh9Ov0hhNC7z7Jx0xWRajG9R2o9kbjulZx8jyTlELQ/UNnk5bn+uJ6d/JifEh21PU2SKnWwjGg/Bw+hdKN7k8/Q9b/YaoJNR9f7eh+tL+P3Ta36MU39PUGkhU0iT25bBg8tOgXuYFPh2Pk3jafRCIzfuyPvjQ/RogQE7uYz/YLeQxB9zcyWr6PxG96KRt3tlBTgJNZzfO1pfwbbFfRdW7nDAERmMjJDiI9Ogbo4EZ6Cr46CM2D39+gcSRBg97gE583Tbuyx5+/xu2kn3Tlk9/TeQz3M2k9AWkZzZkfztHjkO3J46DC0v1KDdfYXgLaw7vygpvNSvzBtBKAkZL1751xWWLlTQ6fJJ5awIx+GC4DVzxGtWU8k3j8XjAgNBiWwutM0eo5u6W39AeRKjI+BEu3YAYS87bqi18gt03U6Cvn0zcEhB0tGF3Qp07cyW6rKJnR3yCvx9ckgm47ptyg3/L5G6Lg0ygAAIABJREFUGtHeDaBo6DiKgz1SMcPPlBmrGF2l/PotlGXa9XkAUcU4IS2B+6g/Te8B5XtuejsugmIXrROa1poB/JuiORMsdR/lzdGgND8QmYXJPbDUfe36OlB9nh3jfq/1uwRpsuU2PUIlmXGE2HZiiSi+fpHOf/NUbtrwSiwe3pP97RPioRPCfVB1/akm0xVeRRUnyve9p/h+P3qPrGSaqXdfDrfiZxkEOPrKUTZ6AAEAv76QaBHdlU14sF2LUeJgqvd//TJuujzECylkGEII0+M4x+VzXc9cw65ATCFh7unR2DIqSGNNriz9x9SubRiu9eu34n0ltY2AiFrTZaJv7E/p6qdsOxFy31EvrSVg6NEtc0LwrLT5pl6DrQiSKsDxNXO/7nXzMHbHi+Rp586kKUD79qGOhIubcRMxKkn09GKw73sIRrVwbV2dSeDlWvK+PvX0iH4JY1//Wq8/PcQ9g43mXB8aVa/gUrQivi7Ys2YVGDunh5Be3h+C7psKnqFHcFsxBFEEOdE3LqrUSJ7/7XvxGY2P9Hlxjc0DnXkr3eZasaXBAIEluBWbTiJI41vpJE8vBy/U4wPko6x+o5atAf2mwT15K6wpSmgesNgJvOcVoFQmL94Y5E3JAJSoewqAhwk1i357I9g7ltYT3gPSaU4qZqDlYp9pwx0v/tuRkW0YbOLZ/lVE/vvvadWByDiYEyMFMhk/sXYRJ7TcM24Z0h6OjoqwLQIJ9mT67X1sDnBDcKVlvUkpzgH/plFRI0kn56aj1RuuXPzqPqzNUCByam8RPa86V5LVsDhj91t6jyRWezPc1lfx73pvGK/wNSunlBjuacn4msUsux9pFD4+SU9tZo1Mh6cseXB9j4QoLhLvI8WF4YJ8ciPw2NbGV9AKK30AAqfa5YkazWHMEocTqrYSPIfn6Gb+ZjTo7hTQoXIiWYACbX6mU84FujyMO6pUV5hoTgjd5PFzkCsfvqmLsIxOuER46kYCY7pxblVU9NDp0CYgX3J4jIPUeAGV1ccmsf8k3gvbTCzq21eSwudMwrETUsk7I5kyhCCOLT8XgpYgc495+aY46bN04yvVYhZpkbQ8eNPKteFMLF0wb7HZWZkeWFECDhk/PKrP00mZ3BNcU967bYHz1Lk5tZcQl6PsxG2HXxGJ7tocDJl2swMUC2F9gu7SJjPANhleXZlbbg5iCPmHEMIY5E1H87ZhkH/58r+L+cgENYFOulfbYm2WMnovrdFmwrmSvKZfn81D+/dRBGOinUSXllfWOBP7oGjCiXTSl8/jYdk9tc8B8Wm0lRNAEIO9tJbXxuEB2u89LmkzvMpV+HDgHHlKa9yFYTcnrP84/QzX1Cts+q1c6uecQ8WydGkGe8+kGg6Z7kq9pcTI5vDgvPFKCg56iyf/Tj/4AhvAFyG9QCp0OjFZEAY7Azmxpb5O3vBOnInGOfKG9y2qhpPTeqbXGJwAfsOh5oaHkVyid8glesGYYaamAmH5akUN8539uELzhlE9v4neRe4L67vCSjt4+Nfv6D3WznC/7p0T1SDh1hYaS+ddL0RysLZnelCH5rw6CX0bBn8zkRVPQ2g1m4fs+JynYWEDRRvJ5oxrrGYVfeSaiTbSkTpeA2jX0DEOQdNAeYPU194m4ncjt6uM/9wzNNW0NTVvxTkpGoJEsUHyXBJEeCBNq6n+ttFdNnXC60k6/F20OSDR2h02iqwF9NnKewNS6tEZqpqbbA6DXTSv8RIpmL3tS2mRO5NZjkznwp1c2IepVQyy8zaRof3PtAyp+070SCodnVvyh1qoAna+VOedrN4S8aWnwmgXmo9jJNE9V/GoOpqJVq1beh9NaIsfxahzcWIq4iD5u0p5In3Bt0jZgw913Tc0g8bGMgsroJnFrrWWEHQmt/H/E/+2dcBzlKR37+XJ6tPJ3neV0qJyokHqhAX58C//XJUTtW1DunfICUpUW1EzwIiy/O7qhRnmQlyF9Ah9ktlZd15Phzh5yHhk3v4y3tjl+6abQE6CiTUR/am04tMumX4DnZzRXD3+2Qh8jpZen5UtRGSc1MeSNicVc3PQ4XQiu2woh4OxZ9tf6j0O0SyWz5qGflsGbYG8tnOIVSFOqiNv1iPM6Q6qqrDWvRKBRiPhGOKjPCy8QzW5Wu5MNA9imD2fm2FDamlRQiQ6Tjcny7rB30jzFi2S5vorok/cvGFIFtJia6sCY3pgDf6NK50vW9RSMpQOTXPnTd9X4Agxkva0G5ssu11O8V0SMvow6NuI8NAWsnfb0DRuGPQ4v4cSIgf/qIaFQozCAzpSO9v6Iq7ZwUN9j3Z8ugfO1QuD9POQInAx0RQuYwj6XMoQt6zu6YG1ACF4MjPnCvt29lZ0jNZW6VXEunQQgUFYVtcAOQs8gEFBQeFA52c1AGevruuUKBdtUvO53iRtmRcCVa+QLq/CtlgQME/p9PCHRqbDU+xDJdlSFHSAiJBkkTdL1nCT5LEBmoy6R8jF5JEiDfrFz3SR0zkiZOr5V1avOGqx/xG6QWOjVLq6YXuPkfc0o8TN4emuJSboZDeiOCt7iiU0Q6yrekhoo6xwYPL+swPwEGY0/Hp9dkh3PSNuKqZ0HKIvAdXxvkHkciVKOiW3HF87uXwbBiMQQu/+e6k+7lIH0jTXdShym1+7OriQ9S1VzAOTGiZ5M45TBHbzQ7XgTTjc3i19ApLjilLz1mBzAQHB4Do2nbhOJ7esCgxOifDwDvT6xepmLlEIIZSREp4NIFJmFWd5CA96b6A1EAqvApui+CAHZJZl+iGEUMD8+LpfVzajCULIDCGsiv80+P77GrT50534e3c/t4rdk/R0FIPOwX3NqZf7EG4FwuOigYPTODn1S30ORMZ4yC4ea7TQehLvo/fAeHN4LI44kBJA292s6RwUcLiVTfW7O4rn1wyK3YWqp5tBU/CG3JSysDNbmjvDXnmgsgYi6u1WlNun88Nzo4YKWyfaV6FLVrSuCqzWJvXEEXSug8J31UsrQczF4ANn1Yl7lWMI2iW6pWMesr5LhO+cV8gyPIfO+H2ieGrRICbTpThYHcPu3bmVHVQZxEJGBu7w1PcgNT6ILvj9PcW/68VomDsTdeyoCeJ6J6wuIdzu0QvRGp/HBZ1F8D7c4eEG89zyOoPERjSIz3PbER6ORKUUnW13DKkr5RoSmHv+nZdlinKsoY1slDhjl2jb7TSAy7rB5lzPtimqNfQ2Aichb07NrJ9+OjuaIu/lN78OZmAraARaq6rDtoBqbQGNRZfW4iIPdeh1yxRnX8CwGb+nAIew0YyH2qCnB+gaFWhTO4BE0pZ/42W2GUjiNgzdv/G5Up8nhGxpECkztlQYkSIJfs08UCMsIYsh/MX4j4F1PR/cjX/nDZAnVLw2omwZwfwYpOWmMYepa+MaN1ItxdRRy4yLIMT6FlGchE6a9ETE/NuaykMFvdAycUG89qrM2tnmQMXlF2jLiqagPN1HMIVH4wADkfDvjLRMyMobhTkRKW3wpn0RLvc2E4nnjhJlCD4xpeWHkWhCkGNjfVZ4YGRVCBCp8TwzD6eyIVnciO4IVFECeK8dvYmFOVQfvow9wxazjMfmiws5WJLdPHctJea+AVhpgjlwh6fxnKxEfY+Or/dE4iFPnsPgZPusuzw//Ma8oThcD81vrBcOUJ2s1J/0lxvoNfp3Acuf60F980b8cjr3iVJdqG271scsw2hf3cQHWKtD36SbTroqdKwsHQ09vWQ9TUk2bxyhIST3T0+UyDRZAHpHGmFhkg5r7DOpHLN7LJlDxb979/D81esPl7flc1M0xSxf2wNYbXbol64gjHSj84C2YZCcTJ6YE/mJsjvvwh16DqKb9RdxDRBxDyGEPJG2errDQ1TV+XU5os2GQlUz5r5/D44SoNTDmhr8pz3LHWM0GzFKWmC9TaxsfHkrXal457N4HxNll4QZBSxBs1iYaGf5TgzCZ339bvHRPcBhB3bk7Z2GQtvYeVeDd6rSMxhclo13S3L5d9VaYgkPrvjcqg+A5LCnUO+Bfi5vP5aDi40okacHmDpwSLf9VVwYrKLyz3KSXZk2q8T37E/ijbGqJUE2ZSNJm/8V+AS+QE9bMY316UWs6/7R8Zl87qd3Ylf4J33dNBc4gJIaPVg04GSVjXDGnG7tXN7S6gQ8C4+whqebuTg+SkZo5twxQtlG+J6/i+k9r+6jA5kw5gyuDI7lfiFnbNrWrcrvIzk/hHTELCE2iSrM9T1FMG5D9v6ba11v75zGtdmDIl/eooXRCLD8gRqCJSB75/cIzJoHQmXluT9+/M2r11djTYOQH7HbjIvWNA5DC4dMZ6KGh2TtSkXn5/F+7MQ6gQ7WuG9CZHDefD3nFpsdHud8MVWwjQiPruf42p30An7XzAJGOua1Cyccx/ntPUSncEvlkje3dvQM/5T2FF7Wzaa/VhnJA7jiQS3upQYF5d9cqooraQtLC2rP+9H4jdCcttw2lIj95UxIU85OmwIGZdMDBMKGnAZs93JTgwz6RuWn6gzx3JBu7LbuKcRatoppAhiN8ziPI5PvoOeVptO0aWSntIDqJJR/8R39u+l9M6iNkyB2shsqflDJ9Srw4HyB9u+BsGuHLJGWIcWarFySDyfhLQpKlF4KzO92US1Vo9W/2y3HHzu6ioucfURCCKEMDs/MpPgXQ9bx6u1TaG0BaNSfJzfD8X9WnPTFP4+7iCXrCeFBODKJJodMkVh6i6kw6veMjsPWDaJaRAa9Mo8Odpb0ebI8lyTd+P/uRLOVSf3MOp0XKEQJw2BOmbQMsfvarcRTtms8hGYxLpYn1zGsu91WT/YJFMYX5qyQG5Br2IIYb7Ynrk21W4l7pzPRSpMxVGwp9la339Ltx4VatYNlfB2duaUFKmfDuPjf2I2VqfdPtSX9k6fxwFt30422/LcptfO5uaTGNowiiMqlMc8Ms4PgZ+wbMbn/OB72031DA3FAkv8xt+Ki+sv4nIenijYS4RmDU+Ll5QF0hET6msWDdogz1TaHI0MHJ4QQLodxU+/V9KBjiqsADpoHEvK9tnWo6+ZczFWN7wFlNhqEnCHOp2XLi13jw8Ebqr/Ed1lQN0WmJ4vKwkyMZ4dYaecd77PGazs8/oDTiKauMtz+3/7m1evz//WfyXuEIKVxpnt9UDWmKnIIShZzrSBG1s0XOJza7hWnc1v4O7MOLqZs3JmovYgXHT3SFfp0EN3p/+lPf/Hq9X948ZZ8juTm+UINZ/swkjF6T5QoIJ17rRqGg/P/1f+ongydEEEPMqoAfLNVSWIzlWqZ44xrbMNQjZh02FY62Hv6EEaJjksIIRTnDBAYEev1SfL0QYJ4E/pHA4uqpTLPKrFugNwUje08WMRJaCHdRZ5ZCMoDypviLJ2cpZWKF8AhYBWV2/2v+9HZuuxqnpSVZC9exs+5hhUvOhmYci+qVcZBD9Dp8/gQj96P+28yN0OJfevKvavaZgiThPcQlACaYJBvwRicRKNAJ5oOTggh5LCeh0ZMzkJWUtPIdtgP78ZnlEB/zqHsfQTk0VNa2Kt7f6/O6/k/j85rogcUuZLwEp52VAynAvRnutTjd7cRDeqgEO+xd2nCaGjTUr5KL49fWCuT5lO8B9rI2goWyAkt/Bc9C+Zofu3+ukg34D0/bwkOJJpMM6AET7g4McFhoNoH//F5eN2R3VoCE7se6EdpfBpnKFE3RvbF/xKdHCdRsY0DGxk6P4bog1eBZYlNpaWZ3FuUhmXmE0j/KRibBE9HVCb1+kJatm7NLDHv4CR80Fbr+HwYT7zOb2wlo2utz0EFZel0VvzwkAaFxkch0sIUSd3W2QRKy8mu8+nkdXGYU3p/beOQ32i/V5xe9zOWm1+HoPw15rNXVlI+BfQ7b1gOngR0EhVtt1dAe5nbghhjXfoZ25/FH9coTzf+fwhqEBPIClRmvS0EBQaXGUGGdJS2USjSMEcLu7ASX1ZzBdPhIW9xbr3AiuiW3gU5oj+2aAdRrwt1pgqxpmdAw7z1+tHs9zZSfO/mU40C5s04v8MTfQ7k6VQvrBS6FOe09Zt4iNy8p04uBT69Inhw1wiHvxuNZ3qP3Tejse783CRWhunOZqLp9O9GsaDrhiioBwhf3UTniE5HrmgTjADBKxwpuFlVfy2M0KJMzomBHnrTaZyD/LFVUCL9tTAiP2UveJ571sQDXrk+ELbBSbwv0mZCUFt2/Zfaey1rZDo8ATnsdc5gRmxeZmmmxsna/RxVTuYMCVkY81qx6JXlyYnqIlSaOEeB/AhqAHm6hR6nowoKESLiNuKtsPSNNS5tLXZ0kZOofD2LEFXddtBlP96IE8jDy/gF5GX4/S8heuhl6RqZ6eVv/X/xP65+HBdh/6F+rv1lfE1RuxCU/+RQNHu+UCiv8972GXdBuIjilNI/55FoCyRm7yTMKGlwSuNlBFD247IKBrZpIXrZfqL76uYt5Mvresj0J/ELhlZ5dP8kWtKrUVxgjjzSUepeW5Q6RSWIcQgIh89gHPNn6kx0kOJajvQB5Aab0UyPSqcgKudu6d5cUlbf+wZNNpvO2VTvo3IOuQ2vKipuXt/Fkdu/dMmObRhMWdBsjW7rumFqyh0EOvrzHZ3DMptNvo2+hDYX0vfQppZd3OlcERUKQblEY6MmtJ+CLGw2nvu9UY4/7p/d/lI+9zdnj169fmmwrSsex5s0/h5kG0p9cybgoExuyVuh/Xn8u/6jdOS/+H5Mww0u1VivdqIxK1zqcyK/k611XAaGNj5RVs9sNhFuc/TlvQx9Px/ZDg9KOx2yZ/qIjHhX2C3DGCeqo0ZchBRIs5QHFrIvNBp+n1gae2mEaoafegvu8Eh0xe7xhlaxOiGhKE3hRNNU+Punp69e/+s3P371+mqqHtXbRzGv1N3R/PfTFxHxmRi5tfKSnYaBHignOoxO4uuaqWpffhBX4dHfx+d5/se6UdhLyztgD+7H177Iy3BaiRD6NbZhMAXF55oojST07iJlOCAo4BmCpmgFAbDLc+84V2J8hIMlhQQdgq5TR0tqUPq+c6wPgv2ASoCo2k3dPE+uQdicmgOCUvFcT9/rvrMZMmDVVAhqvFh6HkIIS6KsIO6vTfizfBENw3zfPFN8Nt+zQ+dORHiuh9EYLq3ihbSjstI5UtNT1Y4uGEa6aWjKDznSuqV7KoNnCNv+hBDCEC2DFsbJmGE9M3ig2m4IIfRP4zPylJNwwZhqM4I4+ZcJh6obn8voSB0lZhNaQD3p4IQQwo8PYoO5yVIdBtIb7jXjnvvF+KF8LocWLrkPNZBYN7Gv+rpm6eQU4ShNTi2lhQCn3FbPdH4Z3/P9mMPvyaOPXu1SP0feLbX4QtD0P9ON/izIvXJNp6yR7fAg7+4S1vTm6PC45siiHheyL8JyLy5YToJ/jovc+TEieuiliNwP3IgZHqFLyO99HB/45fuA+T31RQKoa8kxBW/M/D958OTV619eRefndsMEFjCI9iRGIlUFY4R7TqQGMcdeJcfRv4doM0Ng0QnNmVVs7JbO1hIZP3Pbhj/z1tM4oWxqGIJqfzhpkkjk/kdx7TmJnfC9N2Lks8xqyzLdQ5RnPJ0ZOD2MWENQAj2jUu/+LPo3xtMpQuNmYTojJZB7l5DGyJmzUkXp7sykGhbg8ORQuZgb632wQszL4ynGmEjRwoFjn7u8XUNERzNE3OTaDQtaEMj5ntuGsfNvP3z1uvc//OTVaxKMQ9BgZllKr9LyFAgdf4oyJg46+jGmhVa5wX4EAtr+TO3srBk5kL6np3sskLEUMDTaqqjKuNNUBGCVUWZHR4lE5ZztTTauHVt1JdPDrilHlf/ZPvSzRronqqdQebaFv2jTyBv1BLdS7cSFOrD0JbNAhZlenz3Puo/hTNhvcS291x2ZDg9FubyUmJoE/PKZYcZUuEwsoJ2UumObZOa+vbGc/l36v1ewxU4+zqou4kFz8Kv4MKjqGYI6ae5Q8ec0m8r0ZUnrv7rzyavX//lSI4O7jRgelm+p1fuHeYRnFtZojqkrKdM3PgE1l2oX6YZkjKDdvW46VImcNgmsGVyrEWRMXFRrGwYRGCKKCbFGVPUszElnw7zuQ0MOMBfdR/FZ+uEoHAV/XIikhZRpe1N0mWzP5WEtmd4KIYSjZoTkmNLq3ij8XWtEAz5qqFGdA/ooGHoiJa1sXmjOygRKxQXnOVDgDb+zemZ8hUfxHh2dkS7rfk71EKAdQj/FULRwDvvnndqXmz0e5zCy9crO59sH8Qz/+sevXrc+jca0956lbFBEsve5ooGTAyhvW5qX54u0iLCmvHufxms6P+rmzc1E5fHPtchD7LhXXsLByrJ9HKzCDSGEjzqREDmcqq3+0VGE3YvY8J6GXbHAwNpCVC6Rzj4yRBTOOPk3a3Oix0hjVQ/0/oVbdO2px/iaaVivDqWWlAcSPFcbsJMu58IxtYxQ1sjW4WGPJtdVwQMn+Xj3C53k67fiV7S/1s3Klg5chK5bQlg+ZzCwNCkzW0BeDRerH8aE1YZGDmXukNoRS69aY/54R++ffYqq9oRZ3roCBHy7rqd9Be7zpUEfEmEe6uqa5ONGL3cB17oyMMpJnWPDkaVZVD9Lr8Qi4uOcFiFMM/1nacNtGHx8ex9F7/vsz/WZUP/I0TR2BPa5YKqKBtej5bS9E4IabXILnJAvlRSGkOy34+R3rALqoBHfI2+nvaPRSL8H8qM5JCsgPmtr20AbngfJnynZEELIHcZrzl7qoqVhDvjd02NnzCNdYk1S2VOo1Fe7wBYS1OhxBHdZhmPk4uxpZekZwWv/3vbx2khVKO8i5WFpXnI2ifyHoDY/Ud1FVXG0mfAGnnSaXAOoPIgXIfrQv6uGXDqzu0YPDtZElRYMA9GZvz8/kc+9fxRzPbumv3I5izakjfzfrGvFAAwCLJU7b6cf/mKvueeqOlekXRSsunJFIUIjLc8HCEBwZrtA7f6vKR+gv00KWNDlwFFsQc2/xZbIRnjwwyeHHp7El81vWKWlq4Tl4IO7Jg0PjR5C+e5ckdvVeqY/nJvKPUmOrKZ7vIZr9DCiICTrLTQYlbnjNTuMX/7GjibyP9iN4mnPJpGl/5PWN/K5D/sx3bVf0YNl9yBG3MORLSDA+XPMox/CrIzxPjE3byMy+4TzodcYnKZvtgYquvyAHt3a/Ox9o2zbuPjjaKASneMBqU/N+ePBndDhwW8mv8ejWWn+ZwEC075ZlW7SI804NrcacU3NzVNimTo7Ps+MB8Su6iUzzLMSHB4j4edoXPByflt/TAHwvQtu0mvIMZ1Wts8RNWrre0TAlrX0MmTOwbCjjhcpIq8rGlg1zsO0jR5Urm+1BYNB5/gWlKVdOgEOnjsaTO0e/V/P5L3eH0enoQsVca/Eoh6LE7+prkz0x20MnQI/T6iRleBp4rM7cGT+5zd+IZ/7Zhpt/POxVvg8G0S0aVBFJeShRn4TaEytTE5ivUP+hwWdB3TM4/8XrSM6nfaRSTWUD8lNCDbiM6V9sl7XYYieaq6IzYCSIpONM2s2i4AvDV3bNLJ1eC4IA+p7DTgyhBzd+DK36caXh+7ep9BJOPby0Pi6f6pWg/LliZTZbspBaodTBSVvw2PPN24mgDqniZPu5MTqS+h5mBp0B179v9n/h1ev/333XflcAw/AG4uSU9G1g6t5FDfL8Dpay8KZSYZj8fYfemQWX/Mgnxzo56jX4x28yQuqJMib8aWssy2sSGnBue8+Jvqgn2OpuBPcRUgzo7UEKx28dRvTfQmjzQAQxM7JoRlHeXyWAkBp6tTQn4sQF3+zEn/4TdcIlDDGM/8BRHjMWSEvqPVl/Luepxiuon3KWdk4uTTFAb7b7mN6O054/konkoJsjrqwZQt/t5ObORy+z883R2HO12KByBZuCamqpU2vnevvG8DJce4aHY3FqZaDi+wCZUicVAxO6GxX13p5uDl4cNIszwnuvxBCOPy7uJGv31PHNs2Z/WXvnvz7fi1qQTxqXPrHX43b1c0K/CGEsOqCS2oOPEnG847zFuKzqVwgc3HfrlGLk1AyUcIpsgkrazpaTbHjjZeG4MJH8HkjwMA1wRTZb6+Bf3yLTZGtw3MY73o9VE+P0X0Bh2yCtIw/27E28aNjaIkAoppmwHIuSiiwaUYqr4Jo2csNmbbyPLscTvhtXrXGMvWsxninTT3t98txE/3/zL3Jj21Zdt63b9/f6JvXt/nyZZ/VJKvoZJESSIsyRdGmCdmWYMEQDMuADY888swTj/wXeGDBBkxbkE0IpkxblKgSKZJVpSKrKjMrKzNf38aLeNHfvr/Xg6qM9VvfiXPqJUDku3t0Is65p9nN2qv51rc+6trieLPiPTyPByYEfu/Tr7hzr58zN2lv6BUqbh4EtGm2ShjGT0IKXCqcOcHYMMxHuoAQPAHeQEJ+fJ7jCpo/733oYM46T6R0J93mPVE0+I3VZ5JpAlC46xdZV9xkFPNB5XKEcIMaHC6cJrvHzoFZm1pNObtic/YpMgSzUn4hiwyoaUHWRB3ZJAPBzpA7CHMqpXw9SD0fVyU2CO/SqAYsYkOehZR4LXGRAYYn3Y+3RMeMwVUklELchISKU8PTK+pGNk+CceeQm4phIOIuVA5SBiuOknK3cc27sQhHqD3DvBn58cp22Z9+wZTvGcDk8D0pMoXGMJl6+5vXbADVAUBqjXQCffCNknEu7I19Zse9mSk2AwiJYc/L9Pp5w0m1Gr6vRnsgXxRDIo25OSjZ3C5V/Mf0D2AYS5ZWAcpQX9nTmVKO4e2u+rGm0ahhdl/jcXbqYQjJpYuSWrKH55gxUX+OSOvVj8GTcNF/AeVob82vZM6L7jpATjKZHMhMvArLt22SEy8UQggVbLLU/pUckc9TzwTfkXFbJaGaYXdWsBsF2G7Pu4Z+efH2yfHhxCzFSUJg8u+/+W/d3/986/WT42LOzwTiL7ix6AZEUZER4U4sTRJEb2paAAAgAElEQVQwmSFKzZg7vAlSKjFs4rxv8xjS4vcTs6Rj3jkTj9PhfO5uxFsurv6beMU6Z6w/a0/8JhuXGanZL1S8ckKXsLpgCz4jKPPDtgl+YmUWaj7Oe4QQdlbwMYNjWEKifKeBlyEVBDltQghhtIAYfzbeUh+jhpzgksOsBCZnCQG4bDcRsGNgIIrL9t1D4QNimQ8tqZJqng5Si2Yp8dypP3mpjUYoKRKovIfgwfqTonhgIJ+nksGVb4EtHBmOGsrIt6zvtTzHeNUmErO7NLWd59R4J5meUqyw5bBp/I3lj925//fg7ZPjopQluFkzZWiEjyuWvaBl6Hg28e+RW4vH/sxguKSwHnuHXmnKVO29JkPVSKjpSwYXEl9c2FAiKklZ0owWcQ9RDM/ad20T2flr3gOW1JIxPLD6dfOhFUZlJQK4w9+lPUlThKeFi1qVDldMT/qK/A1qGdGKGCyi19W5kVBawnGVwPOkgEMKIvX+8J4jqYP1YGCDdaNoE/6znmeP5AJ40PUF6Wqoe6RlAIi/oENm0pCQFkDLCkYmRprfIk6oMCnG34N/jzQ9GsTRJJ3rz2FaOseZXCIjyUJyAE0BbzrcWYLXhR4Z3eg4Dlx/IfjsMQpt3SB4j3LRv0izZ4uQhTJD8BldGWRzKfEgrcFBX8LUJDUVDxJrz1FYEoOgrSpW6oCFg5ARpkznAUaAFmIkri2ifOO7xw9gVJzxi5+GhabghtzpWapaioTjltNw8Dw0JJIwXKG4i/SAlr3MlSObK61LQlgIbA7Bx5qWXjwA5lSwpK2Lsqn8rA0W4g30sWT/cK9XhUqzpT5vt/tejq/Co7+S916EBgBaLWyCuja5/lRZGSvfFVoKOLriLbt/76KkthN7J4bQqHf6nA3B950jZZW1Qz1v4Y5X+o9eI8FuPFRm933bN79IQd1kHh56arf8APvCnPFYg8X7yMc/Iy6wmLpMy/f8AB++bj0WYZzFpKxKyCwuV78rIQZqnLopFA5Ot+J1wucgHCOua1yq9VPGeOD/8ey9k+OvLHng3kdHBtw7W/YcAXlon4rvITGcexFRSOjVodIRgmAIML599QxzvkjZiSrmj2J/iHHh4sgkURC8pMYNuHEZXknxAKTwTYr3ojCIzCPIQM43JcQkN4umvbu6RFgDWelPKrJZycYoZE3QVXMeoLRRMtX5btqU78OuFPAk4FHTeGk5ZvxJKgkMvTLEFEII2fPWyVPZcMZnIEPoGa95AZ45jBfg/bMIn4iykjuwdTw6j/5RECmADeVt2RQzp0tqzURlqZpIyH0OGsucUMkhEWAIIWQRPlTPyhAGqXrxmUThSgSJIUG+l+VP/Eba20Ah2LyNkWLoci6by49Df4lRCAnXxRR1vV70DK/LSAb4Xvu6O/dn21dPjv/m+U9PjrcrPr1/rwsFW5R0lkqpLPs+6PZNuPYIGpfQVw6h6WHHaytp1KJLSSkWljEau0Kl7jKnqFLBCcEr+zl6p/O63zLq8+IaT3JIC52pJSOI4aFSc/SKX/DNS4gV7sXjb2jBarXcyg4r5PoBLsDdmRIDsIFnl/HsiXgYuInXngrOCMA1YlQi7m8SD6qyz7CeFBc8hruDSs6e1K5YLdpu1ZYdbowPeNbwvBKs3bL7DIMoE8iB9TbdKSdkuWlGynzAhaTKUIlst/I7ZrxRoe2uaUe+/KZKw+dNQ5zMOlTBSR1U3eZU4Omp0aw6unjT4hFwFlWM1RWCJyI7PPDz7fwZA1ceDrwiUwGQhAr8ctl3TuO5YRQKi17qjeF1mQo2h3JnAjBySuJRtGZTReljcIRMi1jTkoI7RWhqVozH8OSPfR/313AfALDVg1S/a3+rDI1rCqSlBy+u+sDLbEuf2QI+eAOs04o7g1KgnhVfKsWP5cItZKFeRDg1vg5sOH5VAPT0TANMPZJCrSzc2rzoO7sMxUs9Dgy/EH/TEsKzHwOneaflQzHvrJmV+P3DS3bdlq8RUV+w/q6f88Zvt2edfiTlXOi5mdHrKWGZMZT2lKalMzIgmLf8I5BxQmQoFspBSMRLlwYlQeHQNvSuJBMRlqKQgaSWqPDM4LYlT0sIfvG2zgE8KGEIbmZKNc5wET0rE5lMtKq186ih9wRVz98xW0yFBkNQqi262D35ILqna/QhhJDXshOgYuhLrZ1Pj4yI6p0ViRHxHmP73dHAa2xLBevknoYOFrAxIgV3KsLdkS+K95dYFaaQK+6ZnpqhKshLp18XgqcdYMq2EhTOQ3PMxegnTdFnZoKCtN28V0wJ/nZZhrKoqVAlgVz5u4hrGV6LrISECgCc1MXDczCwRV7O2e+afaHbr7HyrrwiPTIaHeDcBJtrRmQQw2JTwTLMVsBae4isFk2dxyQuPRGGYxRXPX5NMEIMuwEMWngqbM30oomyPMudHn4oHPixmOZsvX+RFNwvqx3fOD1XXgssU5kv7fkJ3QE2RzG/B++eTv2uiiGbrgl6PfvM4NJ5id9l1UBIYKrnt9LL/ruPf8Fd99vnPzw5XlrygvCP9145Ob5StcmXPe+/80nDhGtSAV1tXAcTguubgjtjmrp4kNhfWalXRyOX+4ni32oPbb86fENqdZG64FJ89YUs9t9IWZ+ElthbrJXRW/edQvwGNyz1slDhUY2fuFxyKkSsWcTy8g3/gP23kKsv6W+se0TCLgVgcxAX7/rRObph382iZ8O6WqV2zM39p+9sx5rie33BwFeP2gZm0Uq6TD0vCPrx7r6FFfJ5f+7+rp2bITOmctsPhpusghNoXjkdQ6VgNJILFvf8OSqmAyn2zgWxeNekzM5784fQpJJDZaIs3kt6IgtSL4uYNDUCnKBmt4s1S9dvJBsG2CIFU8e10cDPyw4qn2tRw0Vgxuqgw79737sGq6u2w3eee0soXbNBT0tIK91BJ4D/Zlzz/ViEZTqSWDrBnCQ2zHTkOvRj75pXNMZlrBGxgh2TLwwJdd9PIM8j7Lz508WvFrTkPKs8m7/EdGYkMoVcjUd66rVaenmPhar9/ZnlSMVF5z2zFWuP/EC0geFxiQG5+PVR2faLjgkGkaKXMbf5+5d8gsmdHpiWxVPfxpoj0WxaJk6lYPNUvTMtGMOaNp6CIpPCmkstCkYIBIIzSQZIwTM0XhOj+Y79jjCIqVRp2HvXThIIHoKvzFAG5lfHmp64vzoenlH8TVkokkqNEvJxoxuJ94fhI6LeFezGiTwT9z3DA7p5cGNJqinEc81Lfhfnd7NquypN9FwmpXVPJRTGcEE9b4t0XxC73FgmItxrJTu3u+fjvZU6UPtgyeye931c2IufNc5LgPGMeMow9pE6WAlymuOx9za4VeawbhC9JJx7mmbL79VKvxS4ukEOEZF0/alzliFUMXQdyD+h34dL/AD/jscde+Bx8A/Pr9jvnrdNy1054yXb4VP7mOyiN5fHVLAkpAXd3pe/kLXfB74gJYWD0sTm0Grv+ns45mVJjx8t2Ln0QMcQShR4fnpnRfk8iPcsxxUPVRZ3NmUwn4dGjwytbVJRhJCc9NEDyFjDRYUGsrSgwDt+nuBxNaO6VvI+3VhNT+INlQg5K7wMWfHwx2GrRrJx3kUYSw2J9bJtKh8Cs7nX9OHmEUJOWhJm2rcXSStAH4pNBrXsctf9pj15YJvZUIxTNoV1kNiWZSai/WjH6unLwSDh+I4l9Ojk7RewAV44S0sFsy8UFp82TkHN+lt6Dwql1vn4jBdl6ORiUwAaY4UDbCQFeQ9uYlpWof4YpIQgP9Iq1wRua70vbmKzrn9Jemf6WzaxL7/uUb+fPDfLIJ/zk6TZoEbl+78DnoZMK364nQdGwlEcJyp6qvQ5JUcmIedPUhYbPX0Kbp6H5oD2BB9rxYIYxeinN7FDLbLKfiKuIS9WkqtfJ4r+4l2TKO0L4OWQ16DAUhxNEdXStXgolRxNWWdbPm+uwnYv3luXbvn1Pq5BmUAYKy3A4TExPeIlInW+k2NKeYEUeKVqcDxIwkZLBXFGPJxsQMufWv+QmiGEENItWQif/1+85CT0VO/xPLS4UimZgZ8bs7QtHvUO09LXrEPKJuJ7NPQ+SyPjTqYlFZlpNl6ucH4obIGh6Wi5GHgKobH+/vbb7rrfOvNRiGsftAzfs1ywcJcW5R0jG3IsRXkDPDKzuqwJeHzIHF6VqEADdA8RXqknYP2uxK+X9R9ZRx687qMJRSifzUv+HPVDx3otIHHiG3urL75PJAcAXT0g+SGewcGPuG1xrIt1iuIy/Dh+TAgeN6KeJsbFFZRZOqRrH+BjWShUqBQUTWQ+WYbbwvjMRalC1S0+AV4uVmxzyr1mGkRP+LiZRdPu+A8gM+bgwA8UQZS5JjPJBAuFDSIrcVtfzBH/l3nGGHdOZDn7REObcVXcE/i7Xlpj5hiVY+0LrgONP9MoiJRDwX2o/Aw8Ft1h6nSDPAKmggqaYniI0YsoqAmKzKvLtgOXAXR43PYLvDWApbgvAgQMsbNlWTAogDuuAlQsrLKuerpkcFHJoWzpbcg9qPSV5By9OmLNEsDm1n7LX7f71XiiztA5HQGvmXs9yD8NN89D49wvM7wlBR+rOygGWfTnKJ+1EjnDh5zrhWPh0ME9MyPdQ04HudYeeehAb80GU7N5649tnvYlsWbpDrjohjbvv7ny0F33uw8N07NQ9J6VZWAxf/Dk/MmxenEcN46ErVj6YShMy5kmQ3LAyrT82iQmTVWJ4bp1Xn7fP5t7yN7bKDEihrHjtpOq9i6j2Rly/k04l4b1F8cxJV/JOJ90Oqunl4F6P3hDBQ9up4oAs6QZEhINnNZtpGo0NhN197oCiyxmJl/t4sJiyDF2SCT+0i2/UFoXkBWiqY54//66H2DynaxUTavPZ0Xr7tp1LOwYQgh7CGOlRfDnIIDZBxpy4gZR8pmUAWFnt+Grleaa7pcYGs2SK4KI0CkRcwjQpJeyD3fvF7EGHddHQniVWGE1JPg8VdLzrPiMEEME3OywLcJbAqW6tuR32TxecojJzrBrCCHcu2McJOmaAHGBsUmJd4Y4G1f/R5VKKmxSgJS0ALoe2WZ4NlmXQ/D9FanVxWexAKnUBas9tuOep88KoXo60ZRW+madpuHp+N2X2ujtJjB5Ijw2ylXFxlCYQhp4jse6JujpLAl9iduHoAxp9g/lVE7qcdEbESkTwpIwSDD5qHHOXXemimryQ++54e+WaiZo93bU5R5vqAxANpht+v5mQsgYHp5pywvyTMX2tkitLij6w0U/ThmsH+7ZOTH4uN8qCSHB3065lbpspDyIYIMT2s/B8CR4VqCcklW2LJslOUh0EnLylmL4bkLw2J+8WEk+LizPxmbiafT9dfRMaCYFY4VVgNiGmlbJbD0hWDx6lZ4s/3CmjT89som9UPYukgk2iEbX75JkmZ1JtfTx1BbAcAkZKTsS/8Z4anZFbwNCLAa0G4K3PlUYOfez7B0MoZG6QAX/PDRHAIjX65yNx/Cokp6N4R3S+7uFrHUYE/BNjiCNZRq05ha8fyz1EIJXJpQ7qgKvzoO2od1Xi14R37xkmSaqwB91TLPVzEIyI1MhUZwOwcKqzLkSJeyDJf8e5BVR4Z5CXaz8jn/HwaZtCvSUjaQAafOKvdjCXXnH6ulhPnXfpxCmVozkPDRiLVzRTlkSxOIoU/HyZ6igfdb3Cz0+NJpV3hfh0U8PBVDbxiYLbxLfKQRPJqqYsQzuqcYe12pnGB++XS+Zu0P5rfoIhdGjf+WS31QfPoPmXBXBAOVlsiyEgsd0RcYr+gHG9UxKtqhhxJZDH3PfV0dHdQsK1UVlJrdj9fSxOQb5/IvvEz8nLd2OI+y72HNrWyAXPBsff9W6GbwHM6AGdbkQ7zH0mFyHB1GB7opeooN6kkLMTUYVO1+4NH6wOaianTACJmEmlmgRGVeXV2yDaA6EbRRuzGzeD8bUuTi1VAGe7cKQ/v2p6Gn/uM2DfSWzx4FxNaUTv8uI0Gb/E7D4orwlX2aj4sbMvJQaBJh7xaN4RV/HgV44zj31PBLsN81psUl69eIVyMK+zdN+2t+juGqCOZfxDx/EuEyGohidq5kGvNPxrolq0SbIUBiax7AiiW3NSMX1SQqpzO14S901JTkkADQhbKWKjAun4VQSXquvANC0utxOb447dA6B/M7LgAFLCSBYwftsVFC0lmLtKcDj49MVlxD8xpcTcDrDa/TaV59INhcwb9XHXlAdv2pKevWp9/BPAKrtQUHdLHsL/bND49SpiUeU6+z5T3DdKx50ygzEjJRsmQEyMRX8Db0zNCTyh7L+MNfTXdlPnEIiCiGdBTilCR3H1xHuasUrNfSwqRFAOIxiZpNassKDDiL+I4TgPqi7hrRM2eiY7qyamOccsT+o7YcQwvF1YAHEpesUKpV5MWSAkdpG9G7IAHQQP6/scvPw92BmhX4nwZDpBa/u3n5uE/vMcnyAvlIFr4jgK3I5AKv7HtFPErcZiKImAtB0rmjBOGXjwli6PySAeKko6DjRKxjn0pyX5rKjCLYWUDGVOFUgeQ8l4ySfDxVKLfzJsK9uELw/jdSSUAV04G1nTakQQhijBIpmk9xpWKbJlRqU9JGfOI8ahukpiofn8C9s3o+u+PVOo4BkgzPl22CihlZc54eX8ewExchVVQ8e2DmV/iEWjxbyVAg9s8AvqKWrHoSTeyRIZZ0H89AcIzg2Jk0NP3zdFAb1zjSvmOZP4zeEEAbgzWH4trQvpSswzBoqceSywO0cvOmzVBg6oYKjrb/qB2kA5YuIjB/v+tISa1WbENsNsd7RWKKk1/eWfA5sysOmlAgizk2SVFyNRHSIGq5aRNfdH7i20nPfx+Re2/y+jY2m/ruK6IJXIyGlC32JcuvmnOIgE1pylhZifsqdEsfDo64+ZkSRFTmEEIpIXeNC2XtHwFb4IOU7SaqD5QqRAUehG+mA7y9CKAdliMy/GvoiLkUHwIG0cn6R/uLFhyfHOz3T5rTq+QRWw0RcZdyclCZ8WmE+Pq0cf48B8KZKFEXvBfE9ulDKz4m+94NBwOZE5AhJpbubmMhzWFqCzRV1PL1UTwghyhzrF7KA8VAehZ7CpdtS6wp4AsUX06PkyNLkHRk6KtT9pGWpBiU3qxdNGN9vmkXzxpKvSdKHx4eZXSGEUHjbFv9QKj47TA+syKmkzjNuGqnhB0bl7HOAoFfFzY/fpS9JGLmD4sl7EnZDynpYsrGpfuQ7mVl4kXTz1OkKT1JR3gj9wTw0vBLn81iqnpOPSvcJej013ZyhjaXbNkbtcz50RCNDyegogycANys2JKkoKKMESUkKLOD81bUn7rq9gU2Ie80Nd+7VS9snx4OxrZ2HWx78lWpgXkqh5zzKOyhelGzhjIb0L/kJp3hdNhoWmkVLygdWndcSIAwjKrCdsnHhR7YBtK74uURjIYkEWFuiwlPaxSTU9NkYQGWS54M8NiH4dHai6JWKn4u8rVgJWmiaOj/hOYCbxeImLkgFJweHXABZqdM1AGBaq1cfvoN3EhzC8dAGcr1o2n9b4sAd9DeJp0IIYQiFZyRkZjOyaFbjAaBUZFSpPHoNQgzKim7yxUO7Tqt7F5F2evianwdE8fP+8+jhieNI0XnDcJda8gsPTCA2rohiC08b62VNJKulDOt2KBwxDmybAPymJ2XU9/OmvGTCRuts3d8yD88S6vUcDD0Il6SXlbJXqAYfmzmYve6xP6OhSeMMvm2quyTqYqWF9XVKPhUSFgo3CfEx44J0FhQsVZQCvToocTFQ0lEYdYrlUi6ak+uUGwabcFx9wJfZ+E4uLV3CEHHZmJH7ye98dqzJReXQIceZhha5HinHu2timMHDrCFg4nRSQv/hIgiI8fUEUXv30AyETCFewFXAYD4TyogZsgnzR37ddi8g/Cdzvb+GMNZRglJDGFbOT9osnp06lAwxyHwmENW2hFwQZSGWf+Kt2qObtgEcOY9gfH9/kXIriQoPtcCspJYRlFSBZT9Sym0qHYLWphfAhUDkA6jB6USje0zT2eOqsWtLoignpoSuZgVUEbClRVIz2DxqJa+VlVGX6EnbNgFS9ocQQnEBQC8ZYfI0MEX9p79DDa726RTwIfixPn4lHoCrii8b3c0Rtmlkiyl/T9syMD3ZXrxH+aW1OIKxlHjFKPi1eOjxdWzcsn+lY8KwvdV4zUW9RPTQMUSrJT1cCQoRbNWCKSjq4fmFa49Ojre7sFiPfAG1Gxu7J8eNoVApfNXia7u7Puc+g7CTq6UlrK9peEunUrttFhNy0rAVFZLuTEGAHBwZKAwO8TwkKwwhhDwyZZRLKRZnJM05dAtfQLp/SY3GL0G/vRXJ8GG92EG8UavGAz1elDFq4LI/FYzMkBPDIxo+SzLeKf/VaCZmr5QTYYD225eNh6chAL7HXQujfLRlxIOFBW8sVEE0e1z2QnJGrh2hceC6cgqheE5T8OKosuWwpIKHoyeHMojZ0nru8DUpLYElSKN5KnWMak/gaVpOsOqkJZeWwHiIrHHWCumg1cIpb0PgSppfGh3tS1DEb7iR0AFceq0LAnZDp7TPg8l5x79HHhVyG5dfLKef3xyCXygaEmIYKyMenktl1ExBp+4PvLW83bK4W07Ml3Y/PiugcWi7LS0KFSrclJWATTPLPm/qgaGCUjj255qXTnenhuD7y5GMCUHkPDRaim6cRThSMVr/gQc/HrwJnhzpWrLNkyBtIkMcR/wZgmR34f66dojvKhb9pCW7a12U9ObIXob1s5QCnzV/Fsu+D5rMBInwPsFjACtV6Sqo+OeeS7YHBWQCfm+MeT8TAe6yVGWAXS0tADszXfFiH9ixei7iWqYv8qmVYK3NQaNc56anxhGN2jiZEkKI6JZUctiHSV5VLUrp2KBJfSHAcWfURsq52LHCFujxp4fnWtkD57oQfiwcHYIH9tcqtuYOd31H9hDDTsvGzFdOK6AZCS2c21oUMcUYeUbmHg0jydSIIwGO0MDQkSKwBUeAC2yiwgJ6wA1/ESD/izP2SONL8yWVXIuhnlHNP46p6OQtiaQ7I846EvCSiwdK7HewAGGJ/3dF4yRWIonsjpNcN20qbGoZrNXNpeHwNsGzcl4um3Qcy03SyPQaCoaHmAotOzGoWp8fNE2JGl/0G9DswDax4q64UInhMaM9UhHd8dLIPHAlGdQAwuBQyVbleR5aEQUlXXaZpnxjEW5/U0oz0MMlcp8bBu+v85J9qMoQDYYUQCp6HRWeTkO9f8DUNSW8umgfWy2ieGjXa1SXkHV465EHb25umEY8kvpyAemzxT14DVcl1IFyKArm7Z2xDittA4Ct6xYCN7UrHFbkz/KFrR04mdkq1ad+QEkYmRW8RXp4uqRWxY4yNKnsxMtqzUvWbwThq2FJeazEgNVnJvTJaRZCcPJh+QMz+ztXvCKQ64D8Txh8uU+koWwp8R2fpZ4JRih0j6LHh7L7L44uuetYI/H+sQfGlvJ2rt21Nbew6l3ifeA7x3t+3RYPEWrNxU8WGmT5moSbj20d5w4EInHZFkxKsrRovHGvVOWQY5Fv+DXQYpo6jTUtfNw/3aD5eS0ZtEwcgiY3wOgr7yNmmVAOQD/c0e+zsLLMQcZ7k6rgqmVAi5Y4nZWP/QQ6eNPMPA2jMExGt6sOAAdR48JkylQ+EhaJ+5Pd6yfHl6revVFFiGu3570/y2DsLAviURWnz9v2M69NpIllkD7gLTsIP6nmzs1DyQXLhseLKDJUFp3yrCGYOWhU/nis84Hhi4x4XB0vjPJ54G9iqRSsunjXFmDzsldIyBzuiNoihX1hQX3kP6D1DkF6/nfvnTUg5rMuSC9l4ZJaoVD1i7+HMFlaMEIjhLG6eRgtqhwym0RC3cXn1ul9sCvnjv166J7DRjjw96DcUf4bF1rHi7XP+z4o72BdqYdtdLrCo6UPXEh0/iA8TkZy/S7c8gt48pp9yEiyqEYVm/iqvNKDdPwW6/T46xiqYmgtBA9N4LhqkgoJWVV0prBtaOYR9zbO+zeWPZC/hjjcVsdnadFjeWnVjIU7n3ryQmb6piTrdwxv/6Tm5xczHj3QLx6BPT7j121q174tVfQDUHzIfrX/q2HsvGoazGHma/d0h0gIXjdRKEtSSyYedEX85MVI1gfNdyR4AheWkPdahuJx9Lothom4I51HIEJaB3eneA64mbCDnr/nQRX8FvU+vCgAlKh05RtqYRIq4JjF5RiqYlHREHy19NWi9wOuFU2ba0lqcBGdQrbmvDDfjo/AFC2ptRmwMHurNz77SC2gOI9gCD6M4zhH5rC0BC12934yt6f5mOvkWl1X3PvjmMhDCKED8GYkOxFu/8ouPKBVPzFHNXpO/T2WV2xtKoaHSjoV80bea7kPkZb++oYnT3sA61YB0+57EAKeybx0jLOi8PRZxBMp5SOx3GYAPk9ysnCpmBYE3AqajhFqFul4xhEghhCflq6pupRJ8xjmZfaVAwSf9zKM3pSBsOIX4e1XZYgeSyouWkG7jHIDo5pksmJqOsCrrB2On2b/+Pfwv+PYkj1/q+vxaeWs9cmzXU80NkVB3XPnzdu/cMFngHgspsb1+MLxoeICPNXtjv+Y/K79PVryk5bezFxDlVY7XvvQFKWD1+Kz6VS5d+OBT9PQF2XcX1219AQPT/0RtO6rYBO9J9Ya0s6GUg/o+TdNyjotTcaQSk1WvETsZN1YmOLLMFZJMA/0SunGwgXG+ytGhQtK04QHPzTBP3zLx3r+sHvz5PiVVauxQDBzCCFcLdu57x1c9s/GLBlKyIznRti4Jk+9MJqAaTcjXCVUclgRl5WgQ/AemQhpIEPGGrfNnX7ui6Dvv6zGMCwzEdQrSRCjYtd68ACq13P1A5sf279k66O0L5sgFnyp7e8/gVekuIcHXPVjnmvG88eMQW42+Mwv3Ma6TXaCljdK3nOazyJjRDqIoOh2QYojUhSkEG4W0k4nKISTI3QAACAASURBVDqiyJDLh/dTZR44h6l4mkqfmTDQGlxOWUlAulJmqGCeqYL1+XvkJFUXMql7JsxdYzZTad/kSH9Fai3FFN4NIYTiAYg0s/o7KJewVVX5YzJA9ZmUPcDzJoxCiIyhwqPyh0Nb2ZZQzAVk6kFx+ezxpr8Hblpf9IKwHWygD9u2VjVJhfNtOpaNGVQTGcF+0XB1NRG16jmA9zNJZpigtt1EjIDCHfubIUV1RAT8XX3s8YGp81hzGKeIsYA5Nzq9Qsup7YUxPNrnjMcSLc+UsxBkYifgEOhaVO2ZIRV1C/OcVnRvIw2eMolkgiF4TEVf7k8j0r2vvCOtAU25dB4N6cgllJDgxF7K+8VQRSf/e5s/ced+0LA48UrBu5GJEXpets3peFmqzoMyfCbAagrZ8jYtW3dZyKImlobFHChaZh29OsMEPpt5aFSAk2LHxM4oaZYrZSILef9duFpiQIAheNC8lmzhuf6yDYQqZSTWixDBHZoUyV31c+pwYAoPiQefdrxitAxP5GHfe3+GENQzwZ1l9kxYEkA/ywmHDjJIivvCK7WOjsUGlJNNYJSydZCS9P7uOShsUvR36uqQxWMAmZH4ooJZw6OcLxG+oTlozIYtHpiQ1ExWzkv1ABO3pLwt/H7eMcLvAqO5syk1HSG7uSdFaici1V0zschvRaLcEPzewwzqtVVv4A7Af6YJLGTML0EZovERQnB16CYyIejFYcg6hHjSypRmaTG9X9jN6YVKKZs+i0djP1QqmcIxFDahgmDoykWYZL6wPtckgeJAW6LCM1ixm3KjC8Fb8LTs1YvjtHDVpkmdT/ZFLT0AV6KGnNIJeIgiOpbaonLEsOnmwQyBpArYdNdOc/EDsCDZKuer9jJ/feWzk+Oi5Dkz9PVg4BGUFXiDnvd8bOI+eB96XRS/k8yYHIR2lMDx9O9RBXP5Y5sI2+9rdVI7nIlSU97COIHccR7d967cCksKyLzkvCGmJgTvHegJGJbYH+LONPWcrV30QsPhTWIA4SGEUHtg92xd8e+Yr9qcmooFuJgHfmgUr6Fy7n39jCdg+3DP0m4zQsY5WDzd2zgT5lhyhIwWhH38yPpkuGaDM5JUXaasK8MsQZmaFkuFhxuE0mZ0ztnfKx9rNfYXw/CQ32QeuakYCRjAkKp/6hdwf8nmg3rSmR2roSQqjVRClBWfRkHpwPc1DfSN75p7vnVNcDTYQ3QcPKZLMpsYXSVppxC8NrbxPJlTxQWwKw/sRfpPhT0f86265d9xCHykJimwYKjLklPcNslrD/1N+J0R5ZugZeyHSkPSWwUMZeA3bdZDc8W/hcaA8lAhJEkt2cNzOi4vhOA704UoEgpDVp/GxxtJBa6uX05sdW05pmV9RygedKvp/R2hlAj3lU/BeXDVPlrddNzE9B0ZLqArPwSv5NzqmvvznYrfIJ4ODfPwoOOZN+kZUpAymWRLZdvEOqLVJ4WPWIbCKXMynDu/aErOUIDJOQhtVZQYsqQV8kVclV9WYzV3Bz6WlUTFRT1+2ZiaMyH4748L9YUgLmn1eMNK5bM1jNB4FWDepmi5jNLIA560bFG/u/r05HhRSC/7qP683/eD2Ti2jiwLoHkIpWRKBUXSxolRUDDyaAG4GqSNT0teQE2LIFITDw/du4UDyX5cAz8QjIeMZGJxrh++7u+x8MnpIDXNYKJsmUf2cc5Fztnd91eiF/+saYiWc5bGQgje68KwjBoBVLyKR1IqBetg9xs2fyNcO0wS0qK/CX1P3FUGoVHNyr123bI38lKjrjNCyjoKRI82vJE8Qf23tkRUWPA23VfhgmNsDAXxjo4QtpquyEaHtZQ9lgLa0AlYUFyVw/Qwfq8fQ8l0GFytJQj+pNaFF6dtSFR4sp14QHAO4QvKQ037pJBVMBrjoKyhoV4WtrForVSG9P4seaHswe6exXhl5fCmPZAsnMqGyhZhUJ3GaxOVtHXQOQCDbvd97JdKzlLehxiedEy7uL/vhcwILsg8eHjSh36gyCsSKY0Rg+WKxLjJYZFQPDQv+CdadC69M54n8aW1OBD7me94acgaPUm8QypwyQlF9nEVtrT0tZ8IHuc7lp/7Odu6AcUi61+kXLCXVB6eC1UbwDFB97JDbFbMRfW8663UzTXTgBuSzl5bMAHfQh2SdNOLqyzCU1RwQvCufafUiIcngKFZLdZZGWzWS6pV2uF0yfoq/0hqGyUoxbPs6YI6wivCR89hSMt5ESFLldTPhbREqSM1iHqxXEiPBXsF3O3Y9BdlJ8WlrNdXv++Vic45m4utcxrGtGMlq22fxzyCorxZ92yTA5RbUWxODdXThzAe1OBoQaYHqV7OeZ8RBX5IADJuOV4VfBrT9oUNegIunySAvj8WYDLORY1BjM0j6Ad1YY3eiM/eTGrJIa1NbJCSqUGeFYejSSjkpSEnxll5j74IF95TN4gyqkaPqv4dqRUyrZeKSwjJChsHlTFjuuVCSHY1u1CYIJr/8fZ7J8e/tHLP3kNG8WzJNpmdvrhhMXvJwhlCCEdYHHS1qmchjlckBI9jIhZHwzh0vQfpR05y7eNB9nRlax5ByzWE31oIV7Qveq2D8yEd4aay46Io98QGOK4dCQEs3bKJeXRDSYDssLINUs1rgjvAZq+g5XbbHqgKDzP/8vhQzRAkbudcxQt+4oAysiae3rc4H1Np9R0HG/ZshrBCCGG0DA8MlRwxPhiCSi1LCi7lghhTTKebAXuR5DGI0CzEVEvXOlDO6zCH1dKLyNJiLSrWHgzBGzZKs+DoHkQnpQfehWnEsKRRrhupY3mGYqRMv1VkemWlNAMzg5TzjQpVBdxUD7a9N/7V85atWJXEFK4rFtv9we5Fd13hoXXC6Jp4f8C0PBVQPKMXZByfSC2tKbBxk6G/RyYB+uCMAPysty6XUe+SfdOFBuHZVA8P9xOtPZfUkkNa6KAo/40dk+lVGS450apP/Vt3NyAo8DPNq6fhqAuF9OW6AXNi0+pVZYWYh6EAcel69UyS6t6wQ42d1u/hj2/6c+fKpiX8yx3L2Pr6qg9p7aLoHOtvhRDCGKDPg/uezKp2wTaaTpupzL6P6bLXfuT3uEKuO3IdBHOk9hr3TBFoVIC4Zxb3w9w1zlPOh6HMS/IQaYyZi1zxXhQGLLaryQDNyygeqsKAUUc8W8eEocp8Q0JC+zboB0UfjrpUM2zGTs8WzLOmKOKYlxer3q2XT9v773T979I1lFFpY1JJYdwUNqTxou+Ewo79bngOvCXCTOtwOm1vkTm81kjXO4DKFaS9C5ao+DzB3T473V2jbn6Xlt48PQz2MhvLNjD8lBdFn/3ZXxQZDLlS2/K7IEH/3BAHcg8ayiqD6d2kQpkXfF1nHaVAxDB2imgCepxG7RsXtt25dxctBFwW78Afbr9+crxRNuDL+po3Fo5LJlx0G6LSoclRuQNQj0QyHq3NAHdgODgEH75VbI4rRQV5rxne9FarE6RPzCyMESWxZCknLWOU1F4Yw6OeG1dEEspEXjqBL8PUvRDikfOq1Dj3vZLikf1YyRGRuuY6Vsaa5yKU4Ugpbl7OxF7H91KN8/BtsL6O/QivosOWV80E3Or7vG7W2Spl/VS+c9sAoOvXDtw5st/m7tlCGS76CeT6VV37ANdVwCQ7krFw1qzUDeqhMHDJU7I4Qbh4157VvDx/Lh4K1UwC3ijHEJSWheAGJjV/aCEzfV0tIY6XslrzHN38ip3KkNW5mCAAP/AKycOqKdWrJZuzv3P5Q3fdBoBb/9O9b7lzxLL1BbjI0OsAQm/WlQVOBUgU+Nyb9l7DXQyOpNmyQHLnvFdO8sA1jbR4KBpxEylJBnChA1kvqeHp94xisuy4ffbF8QpfVnPKBRIcht65EcrghMqIAkkvTvOihC6BAeEx+X9CCGGIsEeE6ysGcNyTTCyHc2nHr4mIhwq/I7nsGwte4Xk+sLWkxXbfWrJr2+gQBT4TppArxs/Lte/6Tjh4BySbVPSPvYc4Q4VHvScx3v4QfBmVAezuJA4/3UfjuPOU9ZryMFJTLaEl8/AAXKQCt4QSIYzHaqogNXfFGsS5opTu201WcQtz4g28cyPUHtt9GE9WMFr7HLR6Qf5TSYsjRQpBlDfJMnPgbOFN+FdbN8Jp7WzNawysz9KQFN8zl22mbW/5TigA+U/rUwUzw1HqNnf9j42crMsh+LIT6ilz99fUXdyT9dDmEaDpCDex0KaymVGAa5YW+5PYhRDiq9Fr4cmF++A7WZIsrS6UUrje1XNAK28mGSNpxP8HEuPPZ+zZRwhN7cigF7Eofu3cLXfuxw1T0ssShu31kU3YMAmYF/AmCQurNS/1en2kmyO1lpXNQwih85o9WwuojulBEJ4fUu4PN1it1S+eIXg0Ks/cqZAanL5ZRXhLMDTzmKXFeco5puvcKfAiY+gtUKufIGYmoqi8pwxWZmzvgQfOSLK5mBGsYTEaNVq/bvMPzCPf+U2bGyrfN6tmnZTFcKUXP43OW696L8Jy2QTjTsOvOWLqIpGMTfvdZBteolW/dsYN02CnQjxY+wBMziLz4sibk4oWR7K6kVkdlyEdQghZRzz44oZxosLjXLoR4h87dvV/BE7A8IVqetTQSVAYUSZc2Ql/cpJAne9cqNgUcoIa5wJbvOV32ee/YLPcxR4TSA4V7OZQ6fJtr6+Yu4OZLGPhJtnGxL6x5gvS3dq1IGld6q50UJOFs1A3uBedNEmslrT09Drn/pS+cy7Pacz/56Q59mMK5gTXck+wDEkuXadQJWQ/Nq5Y5yzd9RPfVanGhFMldwYwr3oVpmXwqeTjgSMVlDy51/Im/cHATMC360/dOWLUegMB+sJbk14iE5x/9gxKSDfr7zGBYkOMjYa0XNVz9azg2knR/44YoWwZbM3HiqeKD/eH9OlrTgG9Kb7jHGJ4HAYSa7a67b+jswHDSfYCGrljGQhnBHBPUm4WeDp1XRVap49DdyPeY6aeU65pNYyf/ZbhbAojk89ZIbMswlhQnOa9Y0s42ajqw3EPeJBKBa80EeDcueo7KI+5OEH5lmlfOiumbEoIIXTBYF5+Jl46KL40fpWEloZieVeA54iosAq6ZvUNUVg0UpsxoSXX0tK4NRtdtcQraFgJCzQXqYxKlzruJ4KAwMu+WMSOTXndn6PrkoqYpjNWn9o9Dt/wbkZqnC7jZc9LHmYF5LTujrq50ajkrJdskk+1eCjKONzd9xtLDux1/Q99QHNGUBvmTK6hSpkdq9btNPeY4xD82KcivDR2vP5DL+12vgk8Cn5X9HrdXDRXzZx0BpoxjXPK+8RzlR3FEJwO1FM8AQF97TN+0S3cM69Fb92EmSqhmRZCtEJSNjgDAGXJj9d+2xSZi4uG59ksezcUs7Ye9PycvVAxTM9+16+5wwO7fwpZJynFna3a3FYm55A3YV/AcbfvFZICyAzTAp5ubNlCyK/4RZ1G6m6fxVVLXi5MYeyMpB7QtKzW4U+bergdFf8cFg+lXOcc04xabm6Kv6GnfphQZd0V8pZwCPFD07z0NTODWC8rHpoVkYN8j84ZofU43caIMN//ZNdi+72276DLZw20uN+xl2z3pSPRBo99Z00R5i1t+Wd3s6BVKdLrqaFifqi/B/n4ppoxHVNsN2nOaoLSGNEWYjh1r1n/vrmNnv7qi/OXJCo85KzoSny7+pgxUfu/YhnoCckKoVQc1bjGBpm5ogpVD2BO7Vj+jiEtjb9SW9RUx/K+DX7rLGL1YoWN4X5T7T9q/lt7cGh+2eOKaY43FnfddW1wNIxGfhISVZ96xWuVtA6nTNXVCtv0lGmWBMCt9FaosKDiq/fghN17xwt6Tmwi+r9IjZQvrcXg2jTzjE3ZwTlPh5L9w3AGQ2FarDAOSB5CCI1rcEkzJq5FGVEPqr8hNXMAVsxIlfKlis2xAxSyVV6RN+sWw2lKJ3xn54rdX5QVhpYmCFvNxNNEQkT1iGap5PSombrLQrtl76UEiKkKCAt1U8ArM41XQZ7kB4qkvceUllDKC471F7Fmv6xGmUyvpNKEjCETNMxL41e9WFT8HW4uElIhoFb7EGNEp6HIQVcKRJ0/CYkpfGd6dbRI89qGWT9LFa9E1/O2kGlU1CTke9wxQTtVmoUKQt0bqs3ZYQZrbCo1yVLMQJSw3hCGvRqkzD51Gaaqj/CVIyS3eF3HsO3fY/frdlMt85TUkoMGLNwnL9ZhuQHJ1mHjpkjFIgTfKSUoFvS4hBDC/tvxlXQ5QRWUyXOMx2qKL91l6kFqnaPbwg47Z+O7ToFw4017aXVx/rWLd0+OM5iRnzQ23HUsGFo/6xfA845p+fsN8VAVrZPbZXvn4p6UlnDeBHcqZIGIJ0dSRyjDyb2jCg95kNS6Y+hq8c7pytW8NL5rHkSyQjPjFm4ErJrAzcLKwm69SFcQFK6ZOyTi4rPWf+R3y2ffsjmQbfnNdwx24o5YojdWTdJREb936Dmg6LL/jbUfu3P187ZYb7f9XD/AHGatq4ko+pMWvFfi/Znt4p3XMBg7fvG7sF5PWGU3IVB242vOlA5RyuOM7NYcN1V4MqeHU4q7fiPM9G19z6OHh2u9dADuIimpQvzR4sfe7fnkNyzuoaEqlnSgEpWUjqxYUle8GH2oHtZBPV7xYpZjBIMERY80C3/77Y/cdccjU1aUpZzhLvL3HIgHtIQC1GPR+rIlW7dDoWDI7qMoKPB76awaEhi3jBr29vdQ5DixqxFlES2Jh4yKJEOKGhaj15y6yM9riQpPfpkLXiqMx4Q2dKNLx1isIXgtnBibinhPqJAkAckqEjOmJ6dxzY5VM+2g5lYEN0JLDo8uKB4J2TZdQZSzjIO63v/48fWT43c2zSLeLMfHcMl0G0IITQjqsoQfOrcgSEAtrhvtpIbsB1ko3LBb6NOqz5wPnXOxrxxaGDe1Uh0tAMKNETzYHDRWGabrPWJ5U1dRh1/69OtC8OvHYRLUwcB0c/FYVp/ZA9tn7Nzha363ZLX0mQg21lbLPPe/O9iwCVHK2YfnRHAu5m1gf//5O+7cr6zeOTlOi9BYrMODtG+dXP3IKyTtK+Daqfh7UCZlgcUZLclAkaVcUnVT8Jym1701xeyY/iJA1sJgPskjIUKA5xHQ0M9a64rf4HyJkVN/8lJboYGwYwLAmuUBDr7m40WUAYrrYH0lKitJPF1KDNgBVmftR6YAk94hBD9vNFrB71Fy3C5C0QUoLv/m2VV3HQHHysnGgtFVYOPuCzauUrb3Z625EEIYPwHmVHCak1Wb+zPWhjv2wqUAuAMZxUMIYVLH3w3/O9ZWpBNEown0hmv5oDjFVPUKciIlQm+kJSo8Q5SNT9X9E7Od00NJ6tovIytPtTkCxhjuinoHrJM7G/6V2UERpuUYkrzaU78Sm5fjuyGp2jEb8UORel+wUlOy+722bqGrHzy9cHL8jQuP3HWfHVqsZ7XiYe9FoP07Qz8A7XMmqNOwUvUdGddWD9gkBqvSOeuvYwadTnKGKZU1mPOVW6uGNuehUflLAlWTe6ckfELENuiacNkq9CaJ140JUUpURwyIq/elFjF+tnDLT+7mK0hhld/ttmxg+qjPVqr4C5fBCP647ZX07x7ZRnC54qkUfthD+h9S0duvK9EW15KC8OEVoBwT0HIaniAFJpPkZCoEbDOsaSo52YqXLWkoixoGiWvKzTTvxIMMt0YI+dDagEVElBpMj0hafkyYKWk+RxrOHd8AlmWg1og1XXMuAUfCQGSqp2fz4oKPt7xaN3mflkyEf3b/zZPj9y88ODlernt5T6LZCMXKqnVKWub6DEB+ZmCrDBqsoGhuXUgJW0iXX1IHBuYB5WQiD5k/xz4nBjctOFhGCb5IckuyhweZGqOGfzN2Et1Lil+hxa5ZCj51145TIz9QxzftQrX6+buBgMy4eUwgKFrn4z97oi5jPK/+2N6LXqEQ/GY/kfT7PNyME8EanC1a5928YWrx457PuWQ6Y2voJSfpymlBhBDC3r7tjLMqskmEZG3hNkCwl/z7UwsfimvRXYe13RcODsebIIKECpYW05y3Rre2ox/QLCdMMfKPhBDC0Q0Q5okwIN0DYS/q4WHITDE8ZLh12ZTKBwTOlJ6PKoXyU/th+7rfxJeAddnYNGXlsOvd65817abvrTx25z48Nnfg/sBrtjdhBDzMGzBAs7lokIWedBDmekDR0VmQ0B0s1kxXSgkQjyPW8pibB/EQUotvCi+a1uMK09M3Wx3rhQf2jsfX5g/YxnlF4/Tohv+QlY9NDh686eUPPTydTd+HDGkxg0hTz2lsq3zmPsEQcE8IPbl5ajVwGhmRsiCMBEDhOVPybr2tnm1SY2Ek/epZy2T8ztPLJ8fFvF9/h/B6arZtCgaCUlRSyaFXJHXe7xn0iPaOvfWbpFMqZcznTTnZKs/pnfHv37wE2YioQJQqxY4VypLUEhWeEiiyhwUx+/sUqkDYi5eFSoiGnPqoYO5rKHnBVkEmVuOKn2gEI5O0LYQQG1agCzYEv8BmovCQC4b3V+vCuT/lNdbqtsNrSOuzpnlurtXMFbAs9bJ2UAWdYYQQQtg6NC2kV/YfwJpZqU2TECnR7BxYWGY1FTieU8WOyop6+hxDqtQUjEv3j1tAL7M5JR2K/izB8u4v+gnhapPJXCFRJ3EBA2ETJcnXOCFRwLGUJ4RDGJv/2S/tSDbxetEkDIGWdMOH4LMJb9Q8CH+9aGtiKmK0MTBZw6hPVkDRAfrVQDwwAS77FGpi6eRm5pdm9jgyQ2F5zgJAPcUGkRYs0QgWUwTLFce0LHPi4I340hXz0JZRYLl1CfJGhqt9jpgrf46GsoYoWJKClr3KGHoV1MPMuky9VZRUUVoIPDrf8WPepRIlU8XxjEHY/cvPbrrrVlfMcJ1IJ3x93TACXzmzdXL8RLyj/bpt2yMp+TQ6sg8vPfXnaKD1NjHvJ6LowztKyoUQQhhDocoJ7o8KKPfY5iXfWU0oMowAhSAZeY6Oxl/ngOdfYE0kKjz9IWsF+LsWDqDkQPPVopFx+IwQ/Ibm3FLycawBpAUQj1+J5+9xWjcu0xovPKfaaFxVau1khyiXc+2BTcJzNQ/We2/ZQlcjaPx/eeTrp5xFyu+P9z1Ka7FqO6NWYx+BHXo4tE7un/NKUxbx2PyxYHio2PBUQokIdWPWHtpgdDfj70/BsfKT+UtJYRyfFeGVbJLeRfV2sd80vOcUQ9xfNw+H5VBAM57tgPuKp02oUuCeJ5t4s28KSQ8yoiMemG+ef3hy/OGhB3hxU+D6CMED+8kjNeoJ0D4T/wGk0R+DcyTTE+GO+lyzqpqRAMgKo23qU/NKTa/b4E9ks2ZorbIVfIspQKzjQo/PPJZbOb5uY8S9QAkUuc41bO68OkrxUIDVT04ehU8gTKZ7DUNtjjLiWIxwpEk3L8qeBw+2ekv53ZzP37z+wF23XjCFJycYnh8cWCh3+9g8BRXJ0mIF9rSsgRSztF4VEBWM39IOgfb+smEngW8Df6qRNMTcZ9ayyqckBYU4NxrhivUhrk0jO0ktGcPThYCp+9mbBhDBTTz5mDHra8im4EikZrzOd2QGrnetwUK+l0jMFY0a50RCaxmcU+S/Wzh437JUAiZDp4JIz9dtpTxre2bMB0VYwRUjIbxR88jqAaTeV9c9idujtu2MTw69K2CCTqYmr1WjXTadVJ4u7kG5xeSKbMKLAD4f+j44sjIxEaWS/UqB1rw8fykpfPdpQmo4LcrWeckuIjBPVmARC5ueIBWwfN7qx96ne3TDdpMkfN0YuLy0hHPq920s91b8WNLDM4LwVdDyR/sG8vrlM/fcudvwbPZG/sXK8BTVKvashliiTmiIQjLeRBmDvRebR9l9PxjOm5n35ybrwPDA6lVvWBYFWhWTlhqd7hZU+UdshCoK89DGMXiNsmTbMlxRlvIyxcN4PjVXXw5JA6oYUlYruy8jCJw2mgnK9VJ9Gk89omnvTFppgk3/3rG3dh6mDaqwe+j3gptnDdLAQs+VvN+URoiDNw/VvWuHM8G9ZHBPp3x2/frIAMoykVAxw7czKeYb8ZB+/huZ5lRW1PhleIrhffXEOexVDPj/tPbCtbSCsrTSsUIeBsmJ5wapk6QLngC677Uwp3NfKT0+N4/EcwhbRSj28R7PhV4+JrtomsBMrPwQQ7zY26ueX34XoarmyMAz16relPvxkanh1xf8ufvPbVHNNPywYFom+UjGUpeotAOFTcpCMGTi60cJcA+KqU7QbAfWssRcnWeIBGESMpuH5sJH+HyJMobDG8hyEiValRy2OGUownSOObz/tt8FcwgPUBFXi5gxfa1EfvgmNgjJ2iMObRl0CbmM18qOuzaAz3reLU/Onu2nXknfOGdCpIc6WzpnmYUS8fZgHUyYwaVOId5DykcQbN6qSh1AlN5w4ZME13uElHV6uodKgbSO50ZLJsxB2wAJ3MFbtkCUxZgygQpOCJ6dXrO73JrDVKyJQtVJYE2mJ5V7knK4UCnti6Lv1qMYtV0UHQ1YHxq1vFizB15d8GB9R0K7aFrBaBKP29KsQIfTkb2Ac5+e9MFZkeMEO0tIiySk/SMvUMbYDzJQQpS2hnI94uEmQJ1GuOxJ1BG+CF9bosJDIq6xdGxv3T6ujM2yuyn3wILvCz8NKabz8LJo6rkr7pnAw6MYheoWPA7QwDubvof6wAe3hUFz8b71Qfus/U7DYk5hU1crsqiulPwkf3/BeHhqaeuQZyO/CdRRR+hBz4Ng3r1gvvLjgd/8jnqmfWVhgWtyQve89U/+2PcBvQn9dVjOHbmuf/pxCCGMIGS0undcaGUeiQcjdPM/a2p5UznWxcpNS4U7GZWprERIHqmEal9DGJdQ86fto6RunAeb/kUcxEsUnsO2zan+OF6EnFswqfrhtk/pGyPle/2s33WePzPlaAPnCgLebBzZeyhp4JhJFrR6RbHL3BIxZQAAIABJREFUNhy7mWutK7Bmpc7WlIzKTPGVkNYYjMwkegwhhGnhdM/TSLCITAqZRyOA7PSuMKfKSHjgyRUVgrBJa4kgdBNDG6rgUFnR5Jkc1shgKX5dJWEIyZEVMWJiZNWbqx6kspK3jUKztJaxiZCvh/hNbf0lqYO1ZWsite7dzjNwULWvIswr3tHJc0yyZf+hNHaUysKFXp/DgJbX536ihT+ZWc0KC5OiH2vWMmtI1CepJZeW4AcJgygFhwsRybPLACpr2jgbC50xGyoEvziG4hbmRqNu0tZFu2dnEL970npOTDXGOd2Mk/gxCFTOyO7+HJrAAqzl1ayg+wEYOV/0GwQDXHs97+JcKPZwbP9/1PVaWeoI6H6Rw04YZfkt/jrGX3Ui02sUEdoMZ0JxnEf3PZUVZ/FFsGtYH5o2TqtG+rAHq5IWrHoHcigQqgYCc/sp0DXc4jw+Q7GOCfSVshO9FnifkNjAkhAhhLBesR3u0sqhO7fdtAnB8g4heKAkcUFjCWmlDuw9Zht+wlE+ORC3CGl6qpXTJNTsPVID/+wZFJvyExuc3qZXvEoQ/Ophm+VPl0maKcrNNIl75mU1yr4sLG9VGPj9EaOHYFVReFhUnGOpcsqx+0p4pbxrN52lAfqViBBxR3p/Z3gLpoT3ycF7OZSFS5xOf+wfcLZqApTFovPSWQdNe9hi1bv8muchdxSMHMNwrJQLjBApjcpwCzw/EvXh2qKBq0YiefXU2KWxRn1Bw8GLd2w8CYb/eS05SwvsehNhIJ7GcLoov8sQpFHqvmJWDzV31f6dwFLeEiyqvlTPzUMv4IarVbgzbqH4c4MFe6Aj2JIFS9BdJH4MK/jjlrd0/87aX5wcf79z7eS4LGlgFwrmGfp/dt5y55jdlRYz9RnAbwSDVqp+h24uU5vz709Ac+VRvOJIkO1EXO8Rzgw0jj0FuobW5qExVZygZZ3b1GQ0DMEKzZpZSEW6fZa8JV4y5JBB0luTMcH4lfZschciLmj8pO+F13QRGBUB+i4s2wS/umCKTL3glY7bzywt/fKGD8Nu1EwKMiQbQggV4HY6Hevwqgj3AXlzRGHTFPPP20wUU5bPUeUzDa/RTEjWsugvl30l5IV91MDTZIBp7sXSSyjs55GHh94Ol7qd4KGdJQDmK5KYwqwtGrzn/pU3/HbeN8+gKk2Hr56+1WnF7yJIOztnxFBhCSXZgOm9ur5ssnpBCiu+um5W+UCUoWdA3zbh4XnSEC4QvNaBMOs7zI1ox2mGbxH6CkP/Hkx1nyrdAwyhrBAWMszLckQZkS0jGGgkEAzB7xNk8s9LPcLeOgz0L0BQm6jwlPMmLBuPfKcXQafOBa9sonQDarFgZwHgnG4QSTWa6BVU4KjzTGDxpaSDCHbWRcowXOEowV2Lb8lJzbC3Fg230xPzbTNrI/mfL3/n5PjhyEPPf9S7fHL8m5uepv9uV0hU0MhpQiwRi9OFEMJ4CZkmwhU0gnevB0R/vhHvuVC+Hpe2KUJ7AEWVSuoX4Vf4sloPIVtX9VyZq8mhI3OKikwEyOrIzU7/TQh+zqpHgPdoXAULcEJtIMXwuLo74tam4rzft8V/vuKl0vJFm1N3pexECV6dG2d8ynpvZB9UxHXPt/ykyi7AIOv7TqbCw+8eCyNztskUZd8HkyxScFWBYuYa+6ohnnDMi8GKeJeyMe4a+berLzSHaenunYgllXVOy14TR5JqhFGWVKCQPP0bfj64qu0KciWWCvuCbqT0MESKk+IeSgVBhedaxdC25yS96BYwH1ot/XrZfvfDhsWfv7bpKe2//dFrJ8eafLK4Zi8ylFIsYxTDnTCzUI0F3HNyINkS+XgDgcztjohRxoL6Qn81fh4w81VxsQv3TC40rv0VeXhIkR11PZ3OgpjzmFyv8Aj/zcz5znjC34Ph80jBuARAH12t5C1R7Z8tAuzEomWdmP6y7zoqOc0rfhCLGEXV6j/oG1D5jYJhcYpSbnwLVKSvl30nH6JTSEIYQgh1BK/72BmbWR8v6qGIYq7kn02v5pRW9bFkH8Xw9YTglVYV2vTYUgFQnp95aDNXXy7ee8m5qJ5HNs36pJLH71fPZlJBSSpbDgSo5SmYeKAKD9zh6YJ/AAHHW0c2L4cy9whovrzoBf8uio89b/t4cCFrC7QINPjihveNN45Ioy9Zk7AwC0cIK8mc4qY2Kfk+yB2CsFBCYRlYyMNl4CHaCRqJDHZc4oMaTHTtr34w0MtffsNnxFVOD8GHmdQIoDKhtQiZrciQr2YuJoWKOc5cLxrS4rrVNV08RomSrn8AE18K2GDUGN0G8eD7y3fdOcr8jaJ5DlTe//misZQvV/1LVsCLtdP0LvLC22ZN9pEMUFvxG+cxQtOZFT/fCghhdzJ+M07FYNk0tOkKGovsigtZ6p59/IqdjMNVntYSFZ7thnVYJB2figa8Oi0FRkKD7gnqnaEkCl+17OMmawghEMCuyopjrYWgUwIwfpp2bP0hmEOvI7QjblfWLFIuomd92xTutbyl+9XKQzs3tFTd77euuOv+weqfnRw3p15ZuVdE2QnJzT+GMnQ8tN8ReBpCCEWEuNTymNHCh9XQF8dSum+DyFT2EEIYoOK2xn6zMaHHvGRQzEOjYjAdxVuDSRlWnGNj8fC4bybYVgQ457paqXFZQzrvSfo1VIZruKdTT/yucPFXDTV2vmoPv33oQ1M7Bybcr4sXZ7NiUmqn4xUezs08gPYjpaVGp2iWFvu4v2bnNONsuGjnys+8stIBkD/XVJDt6fO5uO/vz5BWEAzVLCakNZHSEpRdO994wfoUX2JzbN6UwQnKfCRcza6IUATjlpgCtcdqQNtx/bH3Su5+xfqN3piol8KONYOru2YvqXUhyQVTw4f+9sIP3HXYQsJUOujHYND/j+u3To7/eeeCu+5//MrvnRx/0vcQiY+ahhHKC1En5ToLlR4NvBWwdtE66OmR96JdWLJOkVKKYYz1OTy0vaa/7tdtDpEB9f44RmzA/tRQYf//lVVLp7DpV3znpXugVqcQ0Uw4KkYJIQpuCpFq5sQ8SAXepCwth/0hm68oNdwgVFkhlTutl86GaPgJcmira6thUXAOI2iO53I2wq+W/e70eGxugvWMV2kvg43sfn/dnRvGpA8MB36mLS4gtfSBzxDLrto7T4ADmUkIwLUEQ1eVyrhaOeo2nodGYCuxYJGyDUlexIS5wt8NMAUi6bM4F6k5RiuJPFhisToLaiDzuQgA4qLfgcYYpCKslqWStxTpwL277eflm+fNaqUnKIQQjqDwtI7teHnVz/t+K74jSYrmmHt1yiaU3nBGnjpjYqb+UEnQcF31iSg8cQBkDf0nzLN5aHHVr5llE4In/1McpQuHy3zmPcnhoiUoKJ+768ItA7Fbf2Qv3F/xW2AD3nlNrlj+xJSoo5veuma4jsVwMzJR2qBkPxDowAW4Qlrouos5D/jvzOzZivUkwJmh4RA8AJmZw4r7ZBp8/6kfjE7N3jErRsZ0enoYOaL44pVVTroACIZXHR30gmp5kKSWTDwIja2wKGyPHRNEdCklFUNULxGFNieyTjQyXuqHJ1nScUUvFVTMsgAqsFx6McZeS1AwAqUgqgY8K1Vh3fufH71/crxYsA3jtzd/5K67ljN31R+233Tn/p2yuUZV4dmEa/RCycIKzxr+Q1fL1il9Qb2TUO6oTOp4ib/Czd9f9YNdfhbviiaGh+5P9UjMRUMMu7xt62MsSjrfXRUN9kwkvo25T1ZdVebpYVU+vvIO6RggGITQjUkDE2UZhhGTP+M/4Pa+/XAAwOMr654s8+aiATT3lvwHPEdIi6HzEDzp2sVz1glHHT8h8lWTnCMBV85QCmIKzUuZllNQ9JRws3rfru1ckHR2hDSoBCsTcg9VtPtSUmXxfji1KfFg+1y8RTwPjYB6YmDa52RiEuskSQyciyrHXZ1FnFu8Kzw8wLmpYcx11bhinVh/LBmC4HrKN4Wb6jXUdJT1Ti9fA4v/v/z0P3XX/cMrf3pyrBm7z0bmTXmzaF7UuwPP9fLHBzdOjj/Z9udWF2zDPe74jZSFfqn8rK+IIYEkm1nd98+zXQDDn/j7j1bs2gwIN7MS/qMs09IS3M+5T+ie0UcSVcHrg4ktUeGZgCtj1PRPzEFQxDFthuA/TjN10jFKgmp95MIhoVsIXvBrWIHvxU7RFHLHRquKDBaYAw8muF3VQhtiAj1qeu/J3z5vAOQl7Pb/bPcdd90E7lQuhhBC+KBvccQfH3ue8CtVgJfQdes1P8mH0OoXy95S5wIogfdBC8vRgzcW9ksqkpF0Uo435FSSR/CltRQVCPt30tzW0hK0NiM0CGSBRchQlSYC+iJgfWAlDl6HolkUSw4CSslKU+Dm0LTxq8hCWUNNrI8P/Nzbatmgv7rslaH3Vh+fHLdEW2R9Ls7Ladm/5JPH6FgJVVHgEoxMAswQQhhcwEAJDqhViGdQHmwwW8We1b4sVP/4nYbT4oqHako15VgugU3+ZTUqOVpLkY17gyo19GBqAgAVD7IdN65qmRA7zqhiiEsJgj6oCBYTypXuQ0nvz3NNzOf/7pX/z123ljEDdCSL/2zWDNLjqQ36946vuuueNO0Dzq94bNyDJ6jNeNHztGzN7HcMb+088iDD/LIJqEzBf+ikZR2bve4nYxpGB4vozoTSpoTotmKoOA8AW43iqQ5OnxM/ryVnaYFVsdnxbgvH78Fy7wkCXAUzeUZYE0sXdXeVM82fIxhZFRl2LImoWlJnhc/Tju0iRFpNSFlkKExDZpME8owHPRPao6J95zeXvfl3p2+a/Cgh3/Pvnf2++/s20oq+s2+4IC1cd6FqM+073/cF75auCenEz1pKKvXSQo6ESIB9UXwPcQ4Espf8HjkXLY2ikXEZVSEIY3RVXL8Z63vNaowLBapFSayPek6dQslojlZm4BoW4r4MvjMtNX+o5Cxjh/jFjQfuuvttm9uf7HnAV/mMLZi6aLajHLIJJ/FxzfqqPbu5L9eBlye9Z8YaFZUQ/HdnRbgPSbbaF88Q+mdSIkuwhgaJA4qnD2DLt31/Fw7gSdTw5Rw0V7Yhhik3BKlDJxiexXsoLbHm+5rz1ie+KHCYP5KXhF6LqkgRA92VXNC0Yj5Zib2ZEYwXviWFqj6eGcbmSGL2v17/6OT49/e/cnKcFR4e0j9oMWrSM2gm7gjeWIKPw4pffzRwFCLv6popt90xi5SBtFPkTlzYKgRPEDysp2OvI/dYUlKItkSFp16yjmi1fQiE9a0YklClxqGu5WnH1wkCs/9rQbcu3MLqCuWi0lAYtUAtJsfGhaIhLccPBCLDksdgug1pJpvf0bFNvDOrHmGahalAJeSRsCm/icqD25LzfaVomsG/bXlr4BW86O+ctTDZnZ7fgG63zDK4+qavcrjXNilbw5zQjbB/BOS8hA1p3SYVXqXl1FMg7Ry0KbyeBLxGqvlifRSfC+A1JosqBO8pmiSkz/IeWXHp0qtKZV4F+JReENEX0kiNHAu1/adHNnfOVkxju1H1FuW3Vu6cHL+iteGQ0bUvoA1an1nMsadPvKuMSkeu6gVDvWrz1IkTif/lSrZYh+LFdgq9KqK4z6yIyuljUYyAH4rgvGKKh46FqNLRNsg8mIfmYIJcywlcOyrHCVvQcyTupHzWjY7Gqj6bf7PI6FQA4i5LVOvXQS8g0+9P38XuM4KAq0oGziLAS1cKfk18CE/96zXDuN3ueFm9BDJZJZrN1JARPPRurikU/3AhnhhtAR7+RvCbagpZqoO2dBA8qYUdW99J/Hu6T/RWTrf4Kjuy1yySH+/Un5zaEhWesXMnC3U7XFbqJnYPSELEY3JR6x6L94STNQIsTGBJpoCnRaEYm6TaRrRK0gl8GKOEsF6lYj9UINkKRvwMzPY92QSooGzKCJOteVNcBmRsZnr806GXFgwdHPf9JGeJAJaqmIkJN12w+w+FHIZzZCLWHftr7YfgeHlnDklHujGTJRKugIKXVCIjoeK806K0r9G9tSce2Hb4up3060OyWiCg1FuXRxmHlaqXSkuF0wXuft9nFn5txUKv5/LeS1jGYvpQslCOhjbHxgBCXrvsi/I8ObDwcFFS59s9ExSsDTQTXpFpwb6zvOQlc+cQNP0lL/9cYUaMjZagSPdtvkRTcE9XeGaSrs4NI4kJ/qW1mCyqjNCE0OuiPF0Ou6fUAQS5JvD1uGfLfk6x6AxouR/T0hVDyL2Hxm8IQl+Cm76S93N2BI2wL7H9r5Yenhz/r7u/dHK8nPfrj2nvzaGfzxngLZckZX1wzTqFRsVSxQ8UMXVLFX+PHpSog4EQD+6Y8KLjQ50gbBrNoU7Aud7ZjC8jkjQntCUuH2I3is/84AyWbWFnQT2vFWZZcyoCFsY31B/FExqxsJxuHvxbNUlHWMiKuxKhiU2rDD7GmFTRnVaIJkZVwFidEa/IP73/9snxKhDwX1vxSX83S4bu+vbha+7cesHwOKoMNUAluo/xfNLzWKJX6mZt/KDvNyDypBzux9fQKD5EGQApLEpwWvOabK6orN64CkV6DgslEpxIBULBsC5zRTlHcI5h0hC8UCXPjy5q4hrISaGNelLhwC+sydl4STHo2T3Tdf+Or9VNiC9g0d3resD8X+7bPPru9LI793VgeN6p+Ln+IGsm4J2p3bMvPD+srdU88rtTZQFYM5SnSK956TsBzcJAsmYct49inHBtCuGu6gN/jy6o/lX+pYenu0A0FETBX5TyOfPQqExw/iYpLhXvRHZwhEgojEz4LO8gGymLvQ7EU0857tiVE2SM7id+bfpzdOTksNn8D/f/lrvunWX78D+484Y79w/f/POT428t3j45fjTwns1vP33l5FixdzmEaHePvKwetwBaxpJ+5Q0Pn2AIrTn0sfT9lnXk6pLXVnbbNjhT1L4qbYlXlYlMgoVilC/O2x2C7//qVoIrUVqiwtNs2ozN6gBDyaGS0LysmTt2nOSdybXtj8ZVL8DLSPmLpCICe6ud5wqMXcP7Sv9QAYrU/QDZYOMyiodKWjoXvTI5U5suSUbKf3TdwkzryODaFaKKf3NsyPz3Fj1WYgs1HTQuTMJCNuXr+aRhWJ9K3ptHLWdFQICX/LeMqjZuBaHRp1s+PVKJhkOmZSfUJ3tpDYKisG/zISlMlxbwNTFejevxqcqcU3r/1iW7UPlIGN/m8A+XhPgOIMOUeCaW1mwhrJS85B/j4w4QP/vWwm133X8G7qiigJzug/jnT5qvunON0emMk+p5XK3aHF4QoP3Tu1C+4J0u1fxgsC7YRKtL80/dhYGVIEdPX8KwuRZD/0JsuHc6AlmLLDOM1Tkf5q4xzOT41NSyj3dYhtUfm8zZe9cLUN6fXhf1ILHKfPHQP4DKF/ekooSD6VhXqhFm4Gk4jQoEcTX/7ZV/4a4j/vJXvvaZO5fB7x4OTcl52vcy/GzdBMOjQ2+4DvrWd1kptpuq24DQg9uVml7pFMDHounXS3aPlZLX4NMX7f13tu2dOxkpy9KJd2BwL+ZYV7b92uEaOb7xV5SWznjdaMELxDxKS1BjS6rmqwoJqABCdxOuX9X6EJqKsvQiHpvRSU5fazj9OPhNlgpOCCEcX0d6HReYLFgutqlkWdANSCbMEELoUlvEvFuX9PU/6Vy39w3eA/NWzayGP9l/xZ2r503Ak6OhLyGnh3uGGSqX/Du60BVCNaOev8fSA4QQRVlxuBXBOJVgtXL+5BTQOw8NXcE1wfUQgg856ffSnR8pn0GyUta60rAY3qN9wc83ZiQqtsg9CplY+bL39iyJksN2q2nKxKNjE7jVK/5jamlbMG9kvGvicg7cUUWvJWzmTRZ0EeNTwcy2f+xDwIV1e396VQfCP0UZl971O9xkieyFQr+P+kDMflPsyJAM48LWHGYxg6O6FdaSzqW5aPxmvDu5aUIQb794yFsXbVw0lMRN0FGNSF/TCFXvML3u9LRFvGkJEQO2yDjDPiV0QBNMbg0MxKwcOu+VzNOyWTItdyChry7wH3lJNuiitAm51UIIoQgG861nprGNl/zcbg8K+I2XC8/37EMXLvhOzoLokAaU7vsu2zkBNM59Wh0dLrT74g6eZIUnA2KhmWQf8KU5gRQfQ2VCzzE5o7RvvdKVYohMO9NK6gS7aXNVfAloU8oR9ELron82gc9UxNpnfX9QyVH+Bm4DzYF3Ef7rZ6agrFYsJWy96K2/91YsBKD4nt+9//WT49+69LE798GxmYS0PDQ8wLFubHl/cGndBphu/tIn/lsa12PI3oJXipPoAyYJGRTz0JQl+vPGbMEQQmhDJ1UBnorZIELw3zxGqQPFbrCOmYZouSlQgGgRv8qCCSzWzQshhBbmaV2U9LcXTMH+9zc+sN8IHer/ffDVk+M/yvp7EIR/veCVobsDw6t9emxATsW/PXxqitLCil8vy2AOp2s/JxtE3xVbDL4xTCkYJ2YdkqskfeTfcYKK65mOn9CDs8pS+NOmSk1SgeR5aC7cindvXYiXAT2poUT5rBQPlM/0eg68c8N555UXy9WNI9dLQh06leNdbLqRNY3vPpMzLGZGLI5fr5p8LspG9JOhedk3M2Awl/IU9PCrEfDNzYcnx9/dvuzOLQKrs80sTHnHHDa6jmCEqnXbzW4/8e/FLMfCXRsAkoCG4OEO5S1xUjBAgZ+pgllCpQOdS0ktGQKH+yQR/rkKueraZ0qhCGZOGucSVE8QWBVb5/0DHPeLepdi7qk4HXofFFNBjoniNm5yTviAaPkLLwCVifWKF8w3apZFdRm7JsHGIYTwpw1z+z9sealHJactcUPSi68A/PanT3w2l3OAiSXKYqIlbCT9NeFmgvs+ic9INxbnbk4gcJyHRsHGMgIRzyOEtqae+/CePMD1DeaRlluBcaVCu4gitzQWlJ7d1awSS24Bqa9rBT9nWRBxA8Vv3yp4fqhfr/7k5Pizocf3/FHD8Av1rM9IXAaC9UwpPgXjtSuWyfKsqbUKrE0AHJ5p2AqptUqQNgFOMQiXCKusO1C0KEYpFjWdqpX6YoKa8moeQcsM/SivChsLf3bFYk8nsO86HiIa19IX/J1ukJQxDI90xHClt0CzGqmUKbEqjQl6eL5R8Om8S2nULEx5rWwxbUDHIbx/71Yfu+voGSKeLoQQ1iFs0mf9Ox4MzFB+7Zz9Ttn4GcZaKHovTg1y4bGUehm0baKOrtg+kd2W7EcIOWWJ5/zmnqoGJcdN9/OklpylBWEwXfRaSOEAQiSGHTEEL9CzfeFtQar48RXEDRMq3apSk5SWvrh1uhaYF5xO9Zl92/EVP4i+YjUwG+J94Lcp+RZrjNRz3tJlSm4Vro83ix7I+V+vf/vkeLLu7/+Pj75xcnxZqF7HmMwEo23UfSeQh+fPetfcuSpCXBMI7a6w8w7yUIwkFZvzIGLBYk31yCU3h8Kdgs0RbirGDcOs85IhJ81SoFeSWDNd8C7rUNYLlXQCObXOHTPzJuJ5fH3JBGJBdqCHYFL80yPzUP699e+569YyBoT/lvA43Mzb39/r+ewuCtztjHlBlgveRb/ft7Vzvu4Vo1s7ZIVEyGlHTH/w5IwEnM3faS2t0Tmb0HkU252IB2m8Z4Ov3FQEpSc1hmfmUeHhXOdc1KKRwwV8r3w6yTnVA0x57appy3Vcgwpd5PRz8AOBQVC3b3vkgPPqqdFGBesYqOhn4kn/0765pZ4OvaL/mzUzXPdgnXyj7EHFP+6b115DZkfQOHXdstEQPpZaWgwBH3a9K4vUKcOuhIebSHTox48114HipJjVTcyhKqbOKfLiDp5khWeKDIa0WDhOI2dRONHYuFiTaNHpZdGJTKuh/tDPtMEiQYH+d63zyHLB2KtGmO3CvSfaLl1s3KiyslENFuJ7Pe1qmPiP+682/vXJ8QVodi1ZUPdBO54TV+h/A3Dot7t+87gK9j6mAmfEQmH79ZufuL+f9213/WzXNpLympdo/ce2W+s8GC6Co0EKLA6w7h3oMZ4q4qW1CrLNWpfsWIW7421R6Ab57KTcAD03BKuOxIXuwJvi/aE7n0R1qav+JVcr9veq1LPqACewXvbK8S9XDWx5btnOdWQ3/gsI5o97Hm17qWCa2IIUVjrESzMk0B55ZYU1vXY7fsKl4aUkPmkoeIVUOyFuinqBo3PxubX9I3sv59EJnq9sKhXp44jt1OtHGpb6/QRQ1ktqrmZhTB23EE7Bq8XcQ+W/rpHPm2L8ONe19hzB5H0QG0aKUWOP0jIhfRjN+mzuX08g0H5//BV3HcNdNUFd/1/Nd0+OF3DuVteXj2BBaPXOPGjYs7VAdK1sA0DaBs0cZgmKpSUvMw52UFBcEh3IwxNQZigvwHAXDpRbuMoJUJrK3pHlwOWDmPlxWku2F2DNR5hzCcrExhQhHsTevPSx1xIO3rENMlJ0D43KlVZcX3xgDxgs+cGvI3uFReHU3dm4bAOsYFt2tJPnGsLAd+s9lkDW91rFj1wR7g66O9dTfmhaU+s7lpL46T1s1b5b8J6hfzowHEUOz0rLTGMoTLNknjZtcBjeymYUDBWfRq00NWwLdxGyBL/FPNLokzSLob+0VsLG2td0ZApw3QRoufCcUh0khf7gmPCbUdpvlqrksL1RtXDRRs57T1bS9rtrOZNeE6m38kdgWl6V9Mcftk1bvNWQwqKLplXuIlXvJ7seM7BeswnS6XlTsQwm2W6foCbhT3HYDuEpwt+a/jsjyyxvIuHgFIgItdiu8u3Yc0/9dwghhOaVL2DOfkmNSkIqgROFWbMT4W6jZzeihLBkBOSsQh+oPyiTszNICD+QEBwjFJFyBlCA1MPP5U9m/YJ8DI1VzYDaQS79QdrmvWJsbh+bECrnvFXY6NoeoopMD4B9FgYfjPyEI3nmRKgaHL/YSIH8rG9FRd/fwnkE5RzlVe0R9m/JZuXYfBH6kuRq6TUD6/moAAAQ6klEQVTUtBkIdgYAvJnD8/jBIc7h6b/rNYGFB2BzBT271lAq76HC7Ot+ph3eZNV2/zumyFPj18XASR+xrs7ZsQPeyju6Oi7imeCkvJT3ZsOTsXlu/vsnv3xy/FtrH7jrqMi8UfAkFqyeuyesfu9XLFU4j8WmGQI/wgbUHftZyHDBMayG7p64cRAeCALQdOSUIrOZTUcX50QE2jw0n3Ybj1mixRcpO0HXuwAvHZsrwlgjMQjoYdRyA3HYuGHHv2R7ZON8qerNMArmjvidKYD/HELugri5fg0YHq0afbNgCtUPi5fduf/tzi+cHL+7aXNdw7B0t59b8iY9wZbu/YdejrH0Q0pkHK3UWVo8Q0PO5wT3PZM2ZI5oOOXksWIwjWrge+rPn8JDBg0nP8UZRWNGQ8DElyl9Qnp4ev+q54cbn1I18L2ObsKoEmOEY6TlXBzZneKMkGBAGMH/8tk33XXXV03+f/LhJXfuxtsm4/9DFI/+s2OfebtZsXWgxIOxir40lmJ587rH3o1XbXAeHviwCffY4paf0Cwt1F8nGErChkcwjBWgjluSSkbnEkOUrcvhhVuiwsOMhmlDQlqYQC5cJNVVhwv2BVF2YiwAgpXkOpZ/j5BBYUyTiAeTlBoqwsKiH8noinvHuEKlIfgNgvwjIYTwd+s2sX9n/Qcnx6ycG0II/6T33snxzdIzd+5izh6om9PB1FZpUrpkGjNZ3aTk4RkdwoLoCk4Hgkld7x1UfFb+jFjv3vx5750FSEtFQ1pcyJoxwg2sIHwhFLKO30T2uSRG2DJ06ja4MWZDMRb69kMW+gwhhKULtphWxdXWx2K9BzDyP9r9lrvuZtU8NWSR/ek9bOHWhKjo/XOWhVJBCPjTA+/hIRO81g2iDuJq/uTVh26HSh45WkZxVRHaOcjDEfGNBX//YUKoO66p94DFT93mPyeNoVfCCr5ICr3bT8T745j2sT4U4zdA2NxRkoT4FHNNpOmgdqKSpzIpQxv3rwFe7DeuenjAAhbuN/76Q3fuAcoJsZL65bIH8P3hlieeZSvlgCcT7wz3oVEtHi9AbJ8WDmYAJ5IkxCQnGEIK5Kf8yzV9n9KL1nwFxojMewdaLrz4RpGo8NxYNfzHjxa9GZlFrJpWZE46MgOuFs0SIXkQC4lq8bik2Cmt26IvTRIyMZa0kgs6a1xCcjlWtqYg0kKMmBddXy8ubCODZO2Mf/gzdMpKxs5pwdFXC7Z5fK993Z3bH9v9J2Ji0rLuTu0lH0vg81nPFKytht/8moe2mRTXTHL0p7LTYoG1z8t7oB87F/0EZWoir1MLax4awxLM+NGK6En8N+Rt0Q3R4XYwt9VrSEyCKt/MYmXm0XjDb8YDMG//g2vfdecYJn1LAPTEmoWiWYfdurcoWfPtf9/7RXfucGBzpypA/odN2zVzCJtWC/66VscmSKfrhYtztxO0LApJaFkfjFYk/LAP5lgRqsM1W0uVh+ANui4WEieCgPxTMbErHU8HnhbZNQ+NYd6Fe3asijjnZRKeJxLmQLe5MhuaEYxwS6RGHYaW+wQVnBBUARJjBO+1eFtS1lEOydfS8nN2pLFptDWQwf7JkWXlcq2EEEIe2ZWK05lMkQ4+9POrWjVli5nDB1KP6/m+7SdpCdGmXdjKvz+NAo6herG5tUXIUEHvkgFBoXriaESqQpXUkqulZ02wpYV4y/Gl4AOW616V3r9gA6LaHH/XB5gwMidohYl3hkpIlOXUjil3IkXtEnhRuJFR+9R3dKEO8WB0e/HW7B80DKh2o2T4nucSw/jzprk1tbo0U9jz4pIiWJQ4HUXwV6HZrVW9RT9EjLffAEBTK0NjUxjIBsHFoG75ziWWWLbDlQ/mz5pNuzAHT/jr3DCo8E1I0XdsvJyzogA7oX3O34SlXviO33jlgbuuiMVUEU2fIc/W1Gued6BMcL69U/Tps29BGRrW/IK5A84RZRXfLNp8JgN4Z+R3wgnfo+I1whFS0XOP7P1H9QSWMglpjVbiPTxpsIwPFvFtPf+dM4Z5hZsqoticPMz/SU6r3rU5RPLjfZnZpIkd9FQpKIqKjOL9nF7IW0TKEgCovi2yiZnEBPLLPeihmgjIvPoECTKL/v79Nbt2t2cxyeOhV0hI8bCYkzpV0KiuVWyBN0c+XYxEsNmMn885YHMKZb9PFOH9ISpPlSb28aTpw1Z5ZCtqktC4QhIjOy6u+A2xd4gKDhI5Gi6B7gGQgb68Is+l1xO0Z2mJCs9uz2bGbEXYd5FbTz4JBUrROplIFWDGWVkhWC17KiiqkVPxmBVlk8XK4YQX+eoUGXXTUWniPaSCg8R+JRsDoYS9sQ/Q/weLPzw5rsCq7hSkvEbaeHNqYh79oyfvnxz/rTOeeJBgUeIyihLXY1ZOUcpfjEanWyUzEQjUKjOS/TKuIxNOis5xfLkJHPlSM3PROB/66ww1+OvSbXhWqvFWUgTcTcWZbL5i9VK5T6viSRApq0RLhuD7C3dPjh+ItfB22bw6zalfFBPgWf6ybfMyJ0o0lXuds20scsWTjdKnz7fhWMCVqFg+kqQKB0y9hGcf+Y5kGIt4nhB8jazJajwad1KG611B0eN4iz6uRcLoBIoqj9AcNM5hYlkmwjhPpVFDtJyzm3/h59Hhq6AswRRIi2E5SdpDsAQJhh0LZxr3F11X7jsTNmAyGv/ysi+3wjWhMpjt28cWtmqLor/XsH1ZiTSZSKJE3mloMlSUNGw1BJVCthk/f3W9sL9mC3bPkXiaUkz2ECNgUoPC9sx+p54gGnyzV1+8emiiwvMYdTpmcVWig2dSVNQ4tfr6Hf87hj2o/KibfyAfy+YsL0XtU4Hmpiqf4mu1aCYFnoXBUdAaZbYSmI0bNmEfDvzGMoFrgGEE3SDO5szXqgvlv7hoaemf9rxGSFbmzYJZzlpjqwkf4Xbba4R0FRdQlJFEUyGEEMBaSwUnBL/xjjcTrFSX7RB/2ctq3Qv2XZk2NsSSvCyVbQGrOtZvJWGEB4ICXL1i0E8jOKA45VvTuikAf6HquT4YUr2W80D7ISo+cy7+pO3nHtPGtQjhvQMLqTLbKoQQKpAhtx6bh+fcpgdcFODVSUqtTWENTySkRfExkyy2zAbW4PAFFRfVR5Yw10XZiniaf9YiSvAiq2fOn8LDOVbcgzfqjGBgmvHvTkXp8d9UvJQdR8pzoNH7qgpJ7ZEdty7HvwdJQjW5pQkmefXa5oA7ouf0h00PTGZdxaykmS2CkOyNiuE087LZkPlcmZb3u2aJ7+36KMEuFJJM2e4ZUaKJxRFR7SIlgquhh39Ksk9JX+fzaCyEEEIaHtIBqFNyDf8sGpuzXjw4W1uiwsMNLb8vpeDREeRYyaqHB02zD9hYZ0XBaBT2kbR3zIWhuBkXHtq79IA8VwI2AkB1Y2GdJ8YldTFwk1n+VNIBUWfpwq96ANr3js1CXsrbTvig7TE2pNW/ueip+C+V7J6qKOVA970MH3NVrnvcNf9kSnbhjRWTAts7EpBFy8LdqXH4ElzMnYtKYgkAKOpTaZrwPDQqvQwdjcTbRVk21c/A36pgpwmcZXRL9ltfy8mfG9XgFsb77otf+C/bl0+O/+6yJw1ksc+mAOEZxuKceqvmsz2msTGbEL61Yt6lrkyWT1sGgvu114zzpySawHfGl0NcY82s6R5JvuIzgHTOMj1XU/ppBLDsxEw1WAh33WZTL6rRp2KO56XRSGHNPDFAR/B0lnb9h3APSSrjkVQT0XmKZZyHdaxVvIfiP1jnLiWxtTwKIrv3DT4yQKVmd+Cxr5cqSDAZ+3X1/T1TjjLY0FtiqBBqQl60EEKYwOu5uOoNidanS7gO3xaZmFBclAQ4oQ4WZdKsxrCMJMjg2cUdwetuksgpASSOU7lbCQU8pSXX0kIcjkI0hBDyxxAGOHXUi48HRmLTJC8k/0gCqZMS2hFkNhWei6NXEeNH1mrZ6wuhC0tEY/Wtq1ikO/Hgab7j3ldU8wUgW6Tqf7L+fbsHRjEjC4q8OZphdTwxrV65HZ4OTZH5J1tfOzm+UvOIMyL488Kvc4QYb/oYU0YmpIvhiiDpkLxZ+pjhGmr8mdb8FdPiBknLNpK1V+E5EY7IUnDWe5CNFR6BbMM/YLiKMdJ9E30/Qerz/Yc+y6l9xgRuWcBxdQDRdE4ReMm6OwdS460EcPOCkGWQbFC5St5dMCwQQfcKyL+8AN4SkTutjC3ICeRYquKt5RlSaXU+F8CgPJVzI4RlpyQ5rEt4DsrWTDJYM6PTFULlFUk1ycY2f0YA5x/ZqpVglDgX9cC4JrKD1n31KTyWF/39iZ1UaAJx9jQC0uKNHy6jAKZyz2EYKk/k28gjBFn6dzZ/4K5bUWATn71q66oPTeN22mfB0Gt/c90zmN/aswhCVzwfUxa5BV5tVvPzsnzHftfb9Ps+GfR1DIl5SyMZQFnKhyvI/k5wzpDSRjN5CafoLWoMOL4lKjyTA1RlbQkPBZ5BRaBx2Wuj+SMbxAgVOOZMXJHOEDxjsjIukoshL24vvmNSlhbdsBGmaCwIDk5WQlpF6g9CGc+B+z+vfdWdO9z/lZNjAi+Vtntx2WDqG1X/AcsF2zzoJQrBc0Kcq5hvWLl2jlFaYCBYiSHck3Rjaoy7fg+eGl0M8O7pPKCzKc1nz6FsLz2Hxe7ozX1fVLbhXZSsQ8e0vOytPFpUZHWOpKV349eVqyGHedrbFEW5ZoP0ex+/686xPtS7Fzzv08c7FmY6s2Ca//Omd+H2WvZtpZp/yQGyN/msEHxmCFPKs3kJk0I5HLSFox5WJXE6uWci8tiv4pwu79qaaF7x50qk/cF4DusC8mQ0StLe08PTsQesHRhCCPU7AIp+AVbZL6sV4a1JImelh0SzDrOguBgsi+IJo5bp5nkps8Zssf+/vbPpTSKKwjDY0QFmpKLWhenClSv//y9w60pN3BjBRNOQIJZCcWgDLs9z3inqsk7eZ9sUZi733vN9TiVDKalTj3CuVGgP4G1WIctoghatjFGd9uFNnI+3H3MPnaKKB9FQ0h6pD+PzOFfrdZap/L+DlJ7X7+MzDpJUzLFbfO/NeT4TNN5H36RvFUeHbPPzH1ZUcvCMslY15MROAgaDOaIE+K5WCgkcLtX0uCdZ+aPCU6AsrJ7lvz3axKFcvkY29TL/OM9Q0Xqyk2RetFYvMN/kVpKbq4sjLs1eW+iSVM6IWVcaO6cAUjfzTUVPFl3X4t3ggDste4ducTnPQmHwBV2eJxAC4hVYNhFDu55kZWXWj52tlugOQoeNcNVd20dMtzUK4Sqeq1pA4RFBy4t6uMgf0pxiI8uu4/rUiIpw5s19gcoFO8fqO7GL7niaT+sWlpxqMqM5cnhwPh6u81pcv7j7Yuj1cuPOYhv/dyMz3jZXsfAnw/yM/a9hIv84y+Zyg0q94fMIp55KVUgzjb2+lZELrOLYa2LvkbDFTpLde0gq1p5QNH7YwFGiCKkHVyMCjgmsKlzJ5FNcNKtX+V24twfi8Xiwuzv8r1ExDoNVIXMfePI53v/XUwgzMR77WAsVYDQQ9oUohrjHOcFcBSnPknrJ6u/xheuXsY/Kn3mx2SpFnTGUJ/orpD487+I+Lls9leKw3p5lhZeybLVAVYx4AssLGKBqeGOvaJFQeYm74DEVR2kqDJu5lH3PO16nHnBVaMRqSTnlrcrK8Sz2ASu3Wy0IjoRR/0b/oKncxhhjjDEd4999QcYYY4wx/ylWeIwxxhjTeazwGGOMMabzWOExxhhjTOexwmOMMcaYzmOFxxhjjDGd5zf/g5drwf0kYAAAAABJRU5ErkJggg==", + "text/plain": [ + "
" + ] + }, + "metadata": { + "needs_background": "light", + "tags": [] + }, + "output_type": "display_data" + } + ], + "source": [ + "rows = 3\n", + "cols = 3\n", + "n = rows*cols\n", + "fig, axes = plt.subplots(rows, cols, figsize=(10, 10))\n", + "for i, (spectrogram, label_id) in enumerate(spectrogram_ds.take(n)):\n", + " r = i // cols\n", + " c = i % cols\n", + " ax = axes[r][c]\n", + " plot_spectrogram(np.squeeze(spectrogram.numpy()), ax)\n", + " ax.set_title(commands[label_id.numpy()])\n", + " ax.axis('off')\n", + " \n", + "plt.show()" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "z5KdY8IF8rkt" + }, + "source": [ + "## Build and train the model\n", + "\n", + "Now you can build and train your model. But before you do that, you'll need to repeat the training set preprocessing on the validation and test sets." + ] + }, + { + "cell_type": "code", + "execution_count": 130, + "metadata": { + "id": "10UI32QH_45b" + }, + "outputs": [], + "source": [ + "def preprocess_dataset(files):\n", + " files_ds = tf.data.Dataset.from_tensor_slices(files)\n", + " output_ds = files_ds.map(get_waveform_and_label, num_parallel_calls=AUTOTUNE)\n", + " output_ds = output_ds.map(\n", + " get_spectrogram_and_label_id, num_parallel_calls=AUTOTUNE)\n", + " return output_ds" + ] + }, + { + "cell_type": "code", + "execution_count": 131, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "HNv4xwYkB2P6", + "outputId": "19e46c86-5f05-4bf2-ac06-1ea1e5f03dfd" + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\n", + "\n" + ] + } + ], + "source": [ + "train_ds = spectrogram_ds\n", + "val_ds = preprocess_dataset(val_files)\n", + "test_ds = preprocess_dataset(test_files)\n", + "print(val_ds)\n", + "print(test_ds)\n" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "assnWo6SB3lR" + }, + "source": [ + "Batch the training and validation sets for model training." + ] + }, + { + "cell_type": "code", + "execution_count": 132, + "metadata": { + "id": "UgY9WYzn61EX" + }, + "outputs": [], + "source": [ + "batch_size = 64\n", + "train_ds = train_ds.batch(batch_size)\n", + "val_ds = val_ds.batch(batch_size)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "GS1uIh6F_TN9" + }, + "source": [ + "Add dataset [`cache()`](https://www.tensorflow.org/api_docs/python/tf/data/Dataset#cache) and [`prefetch()`](https://www.tensorflow.org/api_docs/python/tf/data/Dataset#prefetch) operations to reduce read latency while training the model." + ] + }, + { + "cell_type": "code", + "execution_count": 133, + "metadata": { + "id": "fdZ6M-F5_QzY" + }, + "outputs": [], + "source": [ + "train_ds = train_ds.cache().prefetch(AUTOTUNE)\n", + "val_ds = val_ds.cache().prefetch(AUTOTUNE)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "rwHkKCQQb5oW" + }, + "source": [ + "For the model, you'll use a simple convolutional neural network (CNN), since you have transformed the audio files into spectrogram images.\n", + "The model also has the following additional preprocessing layers:\n", + "- A [`Resizing`](https://www.tensorflow.org/api_docs/python/tf/keras/layers/experimental/preprocessing/Resizing) layer to downsample the input to enable the model to train faster.\n", + "- A [`Normalization`](https://www.tensorflow.org/api_docs/python/tf/keras/layers/experimental/preprocessing/Normalization) layer to normalize each pixel in the image based on its mean and standard deviation.\n", + "\n", + "For the `Normalization` layer, its `adapt` method would first need to be called on the training data in order to compute aggregate statistics (i.e. mean and standard deviation)." + ] + }, + { + "cell_type": "code", + "execution_count": 134, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "ALYz7PFCHblP", + "outputId": "41f2133f-823d-471f-9172-ece3c0b6ea03" + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Input shape: (49, 257, 1)\n", + "num_labels: 3\n", + "WARNING:tensorflow:Please add `keras.layers.InputLayer` instead of `keras.Input` to Sequential model. `keras.Input` is intended to be used by Functional model.\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "WARNING:tensorflow:Please add `keras.layers.InputLayer` instead of `keras.Input` to Sequential model. `keras.Input` is intended to be used by Functional model.\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Model: \"sequential_3\"\n", + "_________________________________________________________________\n", + "Layer (type) Output Shape Param # \n", + "=================================================================\n", + "reshape_3 (Reshape) (None, 49, 257) 0 \n", + "_________________________________________________________________\n", + "lstm_3 (LSTM) (None, 49, 80) 108160 \n", + "_________________________________________________________________\n", + "flatten_3 (Flatten) (None, 3920) 0 \n", + "_________________________________________________________________\n", + "output (Dense) (None, 3) 11763 \n", + "=================================================================\n", + "Total params: 119,923\n", + "Trainable params: 119,923\n", + "Non-trainable params: 0\n", + "_________________________________________________________________\n" + ] + } + ], + "source": [ + "for spectrogram, _ in spectrogram_ds.take(1):\n", + " input_shape = spectrogram.shape\n", + "print('Input shape:', input_shape)\n", + "num_labels = len(commands)\n", + "print('num_labels:', num_labels)\n", + "\n", + "model = models.Sequential([\n", + " layers.Input(shape=(49, 257), name='input'),\n", + " layers.Reshape(target_shape=(49, 257)),\n", + " layers.LSTM(80, time_major=False, return_sequences=True),\n", + " layers.Flatten(),\n", + " layers.Dense(3, activation=tf.nn.softmax, name='output')\n", + "])\n", + "model.summary()" + ] + }, + { + "cell_type": "code", + "execution_count": 135, + "metadata": { + "id": "wFjj7-EmsTD-" + }, + "outputs": [], + "source": [ + "model.compile(optimizer='adam',\n", + " loss='sparse_categorical_crossentropy',\n", + " metrics=['accuracy'])" + ] + }, + { + "cell_type": "code", + "execution_count": 136, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "ttioPJVMcGtq", + "outputId": "2fcf46d7-5704-4547-e8f4-b28e93cf474b" + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Epoch 1/100\n", + "67/67 [==============================] - 10s 132ms/step - loss: 0.5685 - accuracy: 0.7677 - val_loss: 0.4034 - val_accuracy: 0.8249\n", + "Epoch 2/100\n", + "67/67 [==============================] - 0s 7ms/step - loss: 0.3422 - accuracy: 0.8647 - val_loss: 0.3415 - val_accuracy: 0.8663\n", + "Epoch 3/100\n", + "67/67 [==============================] - 0s 6ms/step - loss: 0.2520 - accuracy: 0.9028 - val_loss: 0.3160 - val_accuracy: 0.8908\n", + "Epoch 4/100\n", + "67/67 [==============================] - 0s 6ms/step - loss: 0.1916 - accuracy: 0.9303 - val_loss: 0.3530 - val_accuracy: 0.8814\n", + "Epoch 5/100\n", + "67/67 [==============================] - 0s 6ms/step - loss: 0.1372 - accuracy: 0.9525 - val_loss: 0.3112 - val_accuracy: 0.8964\n", + "Epoch 6/100\n", + "67/67 [==============================] - 0s 6ms/step - loss: 0.1110 - accuracy: 0.9598 - val_loss: 0.3256 - val_accuracy: 0.9021\n", + "Epoch 7/100\n", + "67/67 [==============================] - 0s 6ms/step - loss: 0.0946 - accuracy: 0.9635 - val_loss: 0.2767 - val_accuracy: 0.9153\n", + "Epoch 8/100\n", + "67/67 [==============================] - 0s 6ms/step - loss: 0.0748 - accuracy: 0.9718 - val_loss: 0.2612 - val_accuracy: 0.9115\n", + "Epoch 9/100\n", + "67/67 [==============================] - 0s 6ms/step - loss: 0.0688 - accuracy: 0.9741 - val_loss: 0.2760 - val_accuracy: 0.9209\n", + "Epoch 10/100\n", + "67/67 [==============================] - 0s 6ms/step - loss: 0.0646 - accuracy: 0.9769 - val_loss: 0.2664 - val_accuracy: 0.9228\n", + "Epoch 11/100\n", + "67/67 [==============================] - 0s 6ms/step - loss: 0.0605 - accuracy: 0.9769 - val_loss: 0.2673 - val_accuracy: 0.9303\n", + "Epoch 12/100\n", + "67/67 [==============================] - 0s 6ms/step - loss: 0.0572 - accuracy: 0.9798 - val_loss: 0.3040 - val_accuracy: 0.9058\n", + "Epoch 13/100\n", + "67/67 [==============================] - 0s 6ms/step - loss: 0.0606 - accuracy: 0.9788 - val_loss: 0.2745 - val_accuracy: 0.9153\n", + "Epoch 14/100\n", + "67/67 [==============================] - 0s 6ms/step - loss: 0.0579 - accuracy: 0.9805 - val_loss: 0.2417 - val_accuracy: 0.9266\n", + "Epoch 15/100\n", + "67/67 [==============================] - 0s 6ms/step - loss: 0.0419 - accuracy: 0.9873 - val_loss: 0.2764 - val_accuracy: 0.9190\n", + "Epoch 16/100\n", + "67/67 [==============================] - 0s 6ms/step - loss: 0.0395 - accuracy: 0.9840 - val_loss: 0.2875 - val_accuracy: 0.9266\n", + "Epoch 17/100\n", + "67/67 [==============================] - 0s 6ms/step - loss: 0.0335 - accuracy: 0.9868 - val_loss: 0.2856 - val_accuracy: 0.9303\n", + "Epoch 18/100\n", + "67/67 [==============================] - 0s 6ms/step - loss: 0.0417 - accuracy: 0.9842 - val_loss: 0.3096 - val_accuracy: 0.9171\n", + "Epoch 19/100\n", + "67/67 [==============================] - 0s 6ms/step - loss: 0.0319 - accuracy: 0.9885 - val_loss: 0.3236 - val_accuracy: 0.9209\n", + "Epoch 20/100\n", + "67/67 [==============================] - 0s 6ms/step - loss: 0.0337 - accuracy: 0.9887 - val_loss: 0.3286 - val_accuracy: 0.9303\n", + "Epoch 21/100\n", + "67/67 [==============================] - 0s 6ms/step - loss: 0.0357 - accuracy: 0.9845 - val_loss: 0.3146 - val_accuracy: 0.9077\n", + "Epoch 22/100\n", + "67/67 [==============================] - 0s 6ms/step - loss: 0.0333 - accuracy: 0.9871 - val_loss: 0.3545 - val_accuracy: 0.9228\n", + "Epoch 23/100\n", + "67/67 [==============================] - 0s 6ms/step - loss: 0.0261 - accuracy: 0.9899 - val_loss: 0.3522 - val_accuracy: 0.9247\n", + "Epoch 24/100\n", + "67/67 [==============================] - 0s 6ms/step - loss: 0.0232 - accuracy: 0.9913 - val_loss: 0.3834 - val_accuracy: 0.9171\n", + "Epoch 25/100\n", + "67/67 [==============================] - 0s 6ms/step - loss: 0.0221 - accuracy: 0.9911 - val_loss: 0.4048 - val_accuracy: 0.9228\n", + "Epoch 26/100\n", + "67/67 [==============================] - 0s 6ms/step - loss: 0.0199 - accuracy: 0.9922 - val_loss: 0.3371 - val_accuracy: 0.9209\n", + "Epoch 27/100\n", + "67/67 [==============================] - 0s 6ms/step - loss: 0.0185 - accuracy: 0.9927 - val_loss: 0.4103 - val_accuracy: 0.9115\n", + "Epoch 28/100\n", + "67/67 [==============================] - 0s 6ms/step - loss: 0.0176 - accuracy: 0.9939 - val_loss: 0.3697 - val_accuracy: 0.9284\n", + "Epoch 29/100\n", + "67/67 [==============================] - 0s 6ms/step - loss: 0.0161 - accuracy: 0.9939 - val_loss: 0.3608 - val_accuracy: 0.9341\n", + "Epoch 30/100\n", + "67/67 [==============================] - 0s 6ms/step - loss: 0.0159 - accuracy: 0.9941 - val_loss: 0.3661 - val_accuracy: 0.9397\n", + "Epoch 31/100\n", + "67/67 [==============================] - 0s 6ms/step - loss: 0.0298 - accuracy: 0.9906 - val_loss: 0.3248 - val_accuracy: 0.9266\n", + "Epoch 32/100\n", + "67/67 [==============================] - 0s 6ms/step - loss: 0.0360 - accuracy: 0.9868 - val_loss: 0.3489 - val_accuracy: 0.9303\n", + "Epoch 33/100\n", + "67/67 [==============================] - 0s 6ms/step - loss: 0.0273 - accuracy: 0.9913 - val_loss: 0.3359 - val_accuracy: 0.9171\n", + "Epoch 34/100\n", + "67/67 [==============================] - 0s 6ms/step - loss: 0.0229 - accuracy: 0.9918 - val_loss: 0.2956 - val_accuracy: 0.9360\n", + "Epoch 35/100\n", + "67/67 [==============================] - 0s 6ms/step - loss: 0.0228 - accuracy: 0.9934 - val_loss: 0.2775 - val_accuracy: 0.9341\n", + "Epoch 36/100\n", + "67/67 [==============================] - 0s 6ms/step - loss: 0.0313 - accuracy: 0.9894 - val_loss: 0.4389 - val_accuracy: 0.9096\n", + "Epoch 37/100\n", + "67/67 [==============================] - 0s 6ms/step - loss: 0.0386 - accuracy: 0.9871 - val_loss: 0.3137 - val_accuracy: 0.9247\n", + "Epoch 38/100\n", + "67/67 [==============================] - 0s 6ms/step - loss: 0.0254 - accuracy: 0.9901 - val_loss: 0.3331 - val_accuracy: 0.9228\n", + "Epoch 39/100\n", + "67/67 [==============================] - 0s 6ms/step - loss: 0.0157 - accuracy: 0.9944 - val_loss: 0.3147 - val_accuracy: 0.9303\n", + "Epoch 40/100\n", + "67/67 [==============================] - 0s 6ms/step - loss: 0.0134 - accuracy: 0.9955 - val_loss: 0.3165 - val_accuracy: 0.9228\n", + "Epoch 41/100\n", + "67/67 [==============================] - 0s 6ms/step - loss: 0.0107 - accuracy: 0.9960 - val_loss: 0.3198 - val_accuracy: 0.9247\n", + "Epoch 42/100\n", + "67/67 [==============================] - 0s 6ms/step - loss: 0.0097 - accuracy: 0.9967 - val_loss: 0.3278 - val_accuracy: 0.9303\n", + "Epoch 43/100\n", + "67/67 [==============================] - 0s 6ms/step - loss: 0.0091 - accuracy: 0.9967 - val_loss: 0.3395 - val_accuracy: 0.9266\n", + "Epoch 44/100\n", + "67/67 [==============================] - 0s 6ms/step - loss: 0.0086 - accuracy: 0.9967 - val_loss: 0.3511 - val_accuracy: 0.9266\n", + "Epoch 45/100\n", + "67/67 [==============================] - 0s 6ms/step - loss: 0.0081 - accuracy: 0.9972 - val_loss: 0.3581 - val_accuracy: 0.9266\n", + "Epoch 46/100\n", + "67/67 [==============================] - 0s 6ms/step - loss: 0.0077 - accuracy: 0.9974 - val_loss: 0.3642 - val_accuracy: 0.9284\n", + "Epoch 47/100\n", + "67/67 [==============================] - 0s 6ms/step - loss: 0.0075 - accuracy: 0.9974 - val_loss: 0.3716 - val_accuracy: 0.9303\n", + "Epoch 48/100\n", + "67/67 [==============================] - 0s 6ms/step - loss: 0.0073 - accuracy: 0.9976 - val_loss: 0.3778 - val_accuracy: 0.9303\n", + "Epoch 49/100\n", + "67/67 [==============================] - 0s 6ms/step - loss: 0.0072 - accuracy: 0.9974 - val_loss: 0.3856 - val_accuracy: 0.9341\n", + "Epoch 50/100\n", + "67/67 [==============================] - 0s 6ms/step - loss: 0.0070 - accuracy: 0.9974 - val_loss: 0.3949 - val_accuracy: 0.9303\n", + "Epoch 51/100\n", + "67/67 [==============================] - 0s 6ms/step - loss: 0.0068 - accuracy: 0.9974 - val_loss: 0.4079 - val_accuracy: 0.9322\n", + "Epoch 52/100\n", + "67/67 [==============================] - 0s 6ms/step - loss: 0.0067 - accuracy: 0.9974 - val_loss: 0.4251 - val_accuracy: 0.9303\n", + "Epoch 53/100\n", + "67/67 [==============================] - 0s 6ms/step - loss: 0.0064 - accuracy: 0.9976 - val_loss: 0.4374 - val_accuracy: 0.9303\n", + "Epoch 54/100\n", + "67/67 [==============================] - 0s 6ms/step - loss: 0.0063 - accuracy: 0.9976 - val_loss: 0.4655 - val_accuracy: 0.9228\n", + "Epoch 55/100\n", + "67/67 [==============================] - 0s 6ms/step - loss: 0.0061 - accuracy: 0.9976 - val_loss: 0.4864 - val_accuracy: 0.9266\n", + "Epoch 56/100\n", + "67/67 [==============================] - 0s 6ms/step - loss: 0.0059 - accuracy: 0.9976 - val_loss: 0.4829 - val_accuracy: 0.9266\n", + "Epoch 57/100\n", + "67/67 [==============================] - 0s 6ms/step - loss: 0.0221 - accuracy: 0.9932 - val_loss: 0.4664 - val_accuracy: 0.9134\n", + "Epoch 58/100\n", + "67/67 [==============================] - 0s 6ms/step - loss: 0.1001 - accuracy: 0.9682 - val_loss: 0.5172 - val_accuracy: 0.9040\n", + "Epoch 59/100\n", + "67/67 [==============================] - 0s 6ms/step - loss: 0.0591 - accuracy: 0.9788 - val_loss: 0.3654 - val_accuracy: 0.9190\n", + "Epoch 60/100\n", + "67/67 [==============================] - 0s 6ms/step - loss: 0.0334 - accuracy: 0.9889 - val_loss: 0.3255 - val_accuracy: 0.9284\n", + "Epoch 61/100\n", + "67/67 [==============================] - 0s 6ms/step - loss: 0.0174 - accuracy: 0.9941 - val_loss: 0.3431 - val_accuracy: 0.9228\n", + "Epoch 62/100\n", + "67/67 [==============================] - 0s 6ms/step - loss: 0.0102 - accuracy: 0.9974 - val_loss: 0.3411 - val_accuracy: 0.9303\n", + "Epoch 63/100\n", + "67/67 [==============================] - 0s 6ms/step - loss: 0.0114 - accuracy: 0.9951 - val_loss: 0.3733 - val_accuracy: 0.9397\n", + "Epoch 64/100\n", + "67/67 [==============================] - 0s 6ms/step - loss: 0.0101 - accuracy: 0.9969 - val_loss: 0.3541 - val_accuracy: 0.9360\n", + "Epoch 65/100\n", + "67/67 [==============================] - 0s 6ms/step - loss: 0.0128 - accuracy: 0.9969 - val_loss: 0.4403 - val_accuracy: 0.9228\n", + "Epoch 66/100\n", + "67/67 [==============================] - 0s 6ms/step - loss: 0.0150 - accuracy: 0.9951 - val_loss: 0.3882 - val_accuracy: 0.9190\n", + "Epoch 67/100\n", + "67/67 [==============================] - 0s 6ms/step - loss: 0.0092 - accuracy: 0.9974 - val_loss: 0.3697 - val_accuracy: 0.9266\n", + "Epoch 68/100\n", + "67/67 [==============================] - 0s 6ms/step - loss: 0.0083 - accuracy: 0.9969 - val_loss: 0.4283 - val_accuracy: 0.9209\n", + "Epoch 69/100\n", + "67/67 [==============================] - 0s 6ms/step - loss: 0.0149 - accuracy: 0.9955 - val_loss: 0.3324 - val_accuracy: 0.9303\n", + "Epoch 70/100\n", + "67/67 [==============================] - 0s 6ms/step - loss: 0.0097 - accuracy: 0.9967 - val_loss: 0.3717 - val_accuracy: 0.9247\n", + "Epoch 71/100\n", + "67/67 [==============================] - 0s 6ms/step - loss: 0.0163 - accuracy: 0.9946 - val_loss: 0.3632 - val_accuracy: 0.9341\n", + "Epoch 72/100\n", + "67/67 [==============================] - 0s 6ms/step - loss: 0.0103 - accuracy: 0.9969 - val_loss: 0.4045 - val_accuracy: 0.9228\n", + "Epoch 73/100\n", + "67/67 [==============================] - 0s 6ms/step - loss: 0.0128 - accuracy: 0.9962 - val_loss: 0.3544 - val_accuracy: 0.9284\n", + "Epoch 74/100\n", + "67/67 [==============================] - 0s 6ms/step - loss: 0.0136 - accuracy: 0.9953 - val_loss: 0.3371 - val_accuracy: 0.9341\n", + "Epoch 75/100\n", + "67/67 [==============================] - 0s 6ms/step - loss: 0.0126 - accuracy: 0.9955 - val_loss: 0.3302 - val_accuracy: 0.9303\n", + "Epoch 76/100\n", + "67/67 [==============================] - 0s 6ms/step - loss: 0.0132 - accuracy: 0.9958 - val_loss: 0.3230 - val_accuracy: 0.9435\n", + "Epoch 77/100\n", + "67/67 [==============================] - 0s 6ms/step - loss: 0.0122 - accuracy: 0.9962 - val_loss: 0.3205 - val_accuracy: 0.9397\n", + "Epoch 78/100\n", + "67/67 [==============================] - 0s 6ms/step - loss: 0.0062 - accuracy: 0.9979 - val_loss: 0.3369 - val_accuracy: 0.9379\n", + "Epoch 79/100\n", + "67/67 [==============================] - 0s 6ms/step - loss: 0.0053 - accuracy: 0.9976 - val_loss: 0.3293 - val_accuracy: 0.9416\n", + "Epoch 80/100\n", + "67/67 [==============================] - 0s 6ms/step - loss: 0.0051 - accuracy: 0.9976 - val_loss: 0.3313 - val_accuracy: 0.9416\n", + "Epoch 81/100\n", + "67/67 [==============================] - 0s 6ms/step - loss: 0.0050 - accuracy: 0.9979 - val_loss: 0.3335 - val_accuracy: 0.9416\n", + "Epoch 82/100\n", + "67/67 [==============================] - 0s 6ms/step - loss: 0.0048 - accuracy: 0.9979 - val_loss: 0.3360 - val_accuracy: 0.9416\n", + "Epoch 83/100\n", + "67/67 [==============================] - 0s 6ms/step - loss: 0.0046 - accuracy: 0.9981 - val_loss: 0.3389 - val_accuracy: 0.9435\n", + "Epoch 84/100\n", + "67/67 [==============================] - 0s 6ms/step - loss: 0.0044 - accuracy: 0.9981 - val_loss: 0.3422 - val_accuracy: 0.9454\n", + "Epoch 85/100\n", + "67/67 [==============================] - 0s 6ms/step - loss: 0.0043 - accuracy: 0.9979 - val_loss: 0.3463 - val_accuracy: 0.9454\n", + "Epoch 86/100\n", + "67/67 [==============================] - 0s 6ms/step - loss: 0.0040 - accuracy: 0.9979 - val_loss: 0.3507 - val_accuracy: 0.9435\n", + "Epoch 87/100\n", + "67/67 [==============================] - 0s 6ms/step - loss: 0.0038 - accuracy: 0.9981 - val_loss: 0.3550 - val_accuracy: 0.9454\n", + "Epoch 88/100\n", + "67/67 [==============================] - 0s 6ms/step - loss: 0.0035 - accuracy: 0.9986 - val_loss: 0.3626 - val_accuracy: 0.9435\n", + "Epoch 89/100\n", + "67/67 [==============================] - 0s 6ms/step - loss: 0.0031 - accuracy: 0.9986 - val_loss: 0.3761 - val_accuracy: 0.9435\n", + "Epoch 90/100\n", + "67/67 [==============================] - 0s 6ms/step - loss: 0.0028 - accuracy: 0.9986 - val_loss: 0.3837 - val_accuracy: 0.9416\n", + "Epoch 91/100\n", + "67/67 [==============================] - 0s 6ms/step - loss: 0.0059 - accuracy: 0.9981 - val_loss: 0.3513 - val_accuracy: 0.9416\n", + "Epoch 92/100\n", + "67/67 [==============================] - 0s 6ms/step - loss: 0.0088 - accuracy: 0.9965 - val_loss: 0.3317 - val_accuracy: 0.9360\n", + "Epoch 93/100\n", + "67/67 [==============================] - 0s 6ms/step - loss: 0.0187 - accuracy: 0.9936 - val_loss: 0.3797 - val_accuracy: 0.9209\n", + "Epoch 94/100\n", + "67/67 [==============================] - 0s 6ms/step - loss: 0.0458 - accuracy: 0.9863 - val_loss: 0.4049 - val_accuracy: 0.9284\n", + "Epoch 95/100\n", + "67/67 [==============================] - 0s 6ms/step - loss: 0.0402 - accuracy: 0.9859 - val_loss: 0.4151 - val_accuracy: 0.9322\n", + "Epoch 96/100\n", + "67/67 [==============================] - 0s 6ms/step - loss: 0.0212 - accuracy: 0.9932 - val_loss: 0.3317 - val_accuracy: 0.9322\n", + "Epoch 97/100\n", + "67/67 [==============================] - 0s 6ms/step - loss: 0.0124 - accuracy: 0.9960 - val_loss: 0.3416 - val_accuracy: 0.9416\n", + "Epoch 98/100\n", + "67/67 [==============================] - 0s 6ms/step - loss: 0.0066 - accuracy: 0.9976 - val_loss: 0.3828 - val_accuracy: 0.9379\n", + "Epoch 99/100\n", + "67/67 [==============================] - 0s 6ms/step - loss: 0.0056 - accuracy: 0.9976 - val_loss: 0.3558 - val_accuracy: 0.9379\n", + "Epoch 100/100\n", + "67/67 [==============================] - 0s 6ms/step - loss: 0.0049 - accuracy: 0.9979 - val_loss: 0.3675 - val_accuracy: 0.9360\n" + ] + } + ], + "source": [ + "EPOCHS = 100\n", + "history = model.fit(\n", + " train_ds, \n", + " validation_data=val_ds, \n", + " epochs=EPOCHS,\n", + " # callbacks=tf.keras.callbacks.EarlyStopping(verbose=1, patience=2),\n", + ")" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "gjpCDeQ4mUfS" + }, + "source": [ + "Let's check the training and validation loss curves to see how your model has improved during training." + ] + }, + { + "cell_type": "code", + "execution_count": 137, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/", + "height": 265 + }, + "id": "nzhipg3Gu2AY", + "outputId": "a006e1e9-da10-4617-e699-01c5b04763dc" + }, + "outputs": [ + { + "data": { + "image/png": "iVBORw0KGgoAAAANSUhEUgAAAXQAAAD4CAYAAAD8Zh1EAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAALEgAACxIB0t1+/AAAADh0RVh0U29mdHdhcmUAbWF0cGxvdGxpYiB2ZXJzaW9uMy4yLjIsIGh0dHA6Ly9tYXRwbG90bGliLm9yZy+WH4yJAAAgAElEQVR4nO2dd3hb5fXHP6+GLc8sj3gksbPJHk6YCSTslUAZYbRsaNml/GhDaSmlUFrSQge0QFllE0bbAIGwAiEQsvd2th3HK7HjOJ7S+/vjlWJ5y7ZkWfL5PI8f6V5d3XuuJX3vuec957xKa40gCIIQ+liCbYAgCILgH0TQBUEQwgQRdEEQhDBBBF0QBCFMEEEXBEEIE2zBOnBCQoLOyMgI1uEFQRBCkpUrVxZprRObei1ogp6RkcGKFSuCdXhBEISQRCm1p7nXJOQiCIIQJoigC4IghAki6IIgCGFC0GLogiB0T2pqasjJyaGysjLYpnRpHA4H6enp2O12n98jgi4IQqeSk5NDXFwcGRkZKKWCbU6XRGtNcXExOTk5ZGZm+vw+CbkIgtCpVFZW0qdPHxHzFlBK0adPnzbfxYigC4LQ6YiYt057/kchJ+jLdx/kTwu24nRJ219BEARvQk7Q1+wt4amF2VTUOINtiiAIIUpsbGywTQgIISfojggrABXVIuiCIAjehJygR9mNoFeKhy4IQgfRWnPfffcxatQoRo8ezdtvvw1AXl4eU6dOZdy4cYwaNYpvvvkGp9PJddddd2zbJ598MsjWNybk0hY9gi4hF0EIfX77wUY27T/s132OSI3nNxeO9Gnb999/nzVr1rB27VqKioqYNGkSU6dO5Y033uDss8/mgQcewOl0cvToUdasWUNubi4bNmwAoKSkxK92+4PQ89AjjMkSchEEoaMsXryYK6+8EqvVSnJyMqeeeirLly9n0qRJvPTSSzz00EOsX7+euLg4Bg4cyM6dO7nzzjv55JNPiI+PD7b5jQg5D90hHroghA2+etKdzdSpU1m0aBEfffQR1113HT/72c+45pprWLt2LQsWLOCZZ55h7ty5vPjii8E2tR6h56GLoAuC4CemTJnC22+/jdPppLCwkEWLFjF58mT27NlDcnIyN998MzfddBOrVq2iqKgIl8vFJZdcwiOPPMKqVauCbX4jQs5Dj3JnuVRKyEUQhA5y8cUXs2TJEsaOHYtSiscff5y+ffvy73//mzlz5mC324mNjeWVV14hNzeX66+/HpfLBcBjjz0WZOsbE3qCLh66IAgd5MiRI4CpxpwzZw5z5syp9/q1117Ltdde2+h9XdEr9yZkQy6VNa4gWyIIgtC1CDlBP1ZYJB66IAhCPUJO0KWwSBAEoWlCTtDtVgs2i5I8dEEQhAaEnKCDyUWXkIsgCEJ9RNAFQRDChJAU9KgIi+ShC4IgNCA0BV08dEEQOomWeqfv3r2bUaNGdaI1LSOCLgidhbMGvvs71FQE2xIhTAm5SlFwx9Al5CKEGjsWwqe/gj5DYNg5wbama/DxbDiw3r/77Dsazv1Dsy/Pnj2bfv36cfvttwPw0EMPYbPZWLhwIYcOHaKmpoZHHnmEmTNntumwlZWV3HrrraxYsQKbzcYTTzzBtGnT2LhxI9dffz3V1dW4XC7ee+89UlNTufzyy8nJycHpdPLrX/+aWbNmdei0IUQFPSrCyqHy6mCbIQhto2CTeawsDa4d3ZxZs2bx05/+9Jigz507lwULFnDXXXcRHx9PUVERJ5xwAjNmzGjTRM1PP/00SinWr1/Pli1bOOuss9i2bRvPPPMMd999N1dffTXV1dU4nU7mz59PamoqH330EQClpf75ToSmoNut7JeQixBqFGw2j1X+ndAhpGnBkw4U48ePp6CggP3791NYWEivXr3o27cv99xzD4sWLcJisZCbm0t+fj59+/b1eb+LFy/mzjvvBGD48OEMGDCAbdu2ceKJJ/Loo4+Sk5PDD37wA4YMGcLo0aO59957+cUvfsEFF1zAlClT/HJuPsXQlVLnKKW2KqWylVKzm3j9OqVUoVJqjfvvJr9Y1wwSQxdCkoKN5lE89KBz2WWX8e677/L2228za9YsXn/9dQoLC1m5ciVr1qwhOTmZyspKvxzrqquuYt68eURFRXHeeefx5ZdfMnToUFatWsXo0aP51a9+xcMPP+yXY7XqoSulrMDTwJlADrBcKTVPa72pwaZva63v8ItVreCIsFJRLc25hBDCWQuF28xz8dCDzqxZs7j55pspKiri66+/Zu7cuSQlJWG321m4cCF79uxp8z6nTJnC66+/zvTp09m2bRt79+5l2LBh7Ny5k4EDB3LXXXexd+9e1q1bx/Dhw+nduzc//OEP6dmzJ88//7xfzsuXkMtkIFtrvRNAKfUWMBNoKOidRpTdKr1chNDi0C5wVpnnVWXBtUVg5MiRlJWVkZaWRkpKCldffTUXXngho0ePJisri+HDh7d5n7fddhu33noro0ePxmaz8fLLLxMZGcncuXN59dVXsdvt9O3bl1/+8pcsX76c++67D4vFgt1u55///KdfzssXQU8D9nkt5wDHN7HdJUqpqcA24B6t9b6GGyilbgFuAejfv3/brXXjCblords0aCEIQSPfHW5BQaV46F2B9evrsmsSEhJYsmRJk9t5eqc3RUZGxrFJox0OBy+99FKjbWbPns3s2fUj1WeffTZnn312e8xuEX/loX8AZGitxwCfAf9uaiOt9XNa6yytdVZiYmK7DxYVYcXp0tQ4dbv3IQidSsFmQEHiMAm5CAHDFw89F+jntZzuXncMrXWx1+LzwOMdN615vCeKjrCFZG2U0N0o2AS9B0JsknjoIcj69ev50Y9+VG9dZGQkS5cuDZJFTeOLoC8HhiilMjFCfgVwlfcGSqkUrXWee3EGsNmvVjbAuyd6jyh7IA8lCP6hYBMkjwCtoXxnsK0JOqEWLh09ejRr1qzp1GNq3fYIRKvurda6FrgDWIAR6rla641KqYeVUjPcm92llNqolFoL3AVc12ZL2kBUhDFbqkWFLsPuxfDvC2H5843TEmsq4OBOSBoBkfHd3kN3OBwUFxe3S7C6C1priouLcTgcbXqfT4VFWuv5wPwG6x70en4/cH+bjtwBZKJoocux8DHYuwR2LYJPfw1jr4Rz/gC2CCjaBtoFSccZse/mMfT09HRycnIoLCwMtildGofDQXp6epveE5KVopEi6EJXIn8j7FkMZ/wWMqfAsudhxQuQOByOvwXy3Rm+SSPN86oycLnA0j3Hf+x2O5mZmcE2IywJSUE/FkOXkIvQFVj+PNgcMOEaiO4NF02Akr2w6HEYd5WJn1sjzKCoIx7QUH3E/VwQ/EdIuggSchG6DBUlsPYtGHWpEXMApeCMh6C8EL7/h0lZTBgGVpuJoUO3D7sIgSE0PfQIEXShi7D2Tag5CpNvrr++3yQYfgF8+zcTRx98hlkfGWceKw9Dj841VQh/QttDl5CLEExcLhNuSZ8EqeMav376g1BTDkeLzYAo1IVZxEMXAkBICrqnsKiyVhp0CUFk50IozobJtzT9euIwE0MHk7IIEOl2y7t56qIQGEI65CKDokJQWfc2RPWCES3MbHP6b4yIZ7j7XYuHLgSQkBR0h7vcX2LoQtBw1sC2T2DY+WCLbH672CQ45/d1yzIoKgSQkAy52KwWIqwWEXQheOz51hQJDT+/be/zeOgSchECQEgKOoDDbpFBUSF4bPkIbFEwaHrb3mePBmUVD10ICCEr6FERMsmFECS0NoI+aDpERLftvUqZ1EXx0IUAELqCLvOKCsEibw0czm17uMWDI148dCEghKygO+xWCbkIwWHLR6AsMPSc9r0/sodMQycEhJAV9KgI8dCFILHlI+h/EsT0ad/7HdJCVwgMoSvoMlF0aHD0IFSXB9sK/3Fwp2m21d5wC5jUxarS1rfzlU3zYOXL/tufELKEtKCLhx4CvHYJfPZg69uFCpvmmcfh57V/H/720Jc+C1/90X/7E0KWkBV0R4TE0EOC0n1waHewrfAPh3bDN382VZ+9Mtq/n8g4/w6KluVB2X7T+VHo1oSsoJuQi/Ry6fJUlUHFoWBb0XGctfCeu6PizKc7ti/PNHT+mIJNayg7YJ4Xbun4/oSQJmQF3WGXStEuT2011FaGh6AvmgM5y+CCJ6HXgI7tyxEP2mnmGu0oVWWmoyOYvuvNUZYPG//T8eMJXZqQFfQoSVvs+lQfMY+hLuh7vzezD425AkZf2vH9+bOfi8c7h5Y99GXPwjvXSbpkmBPagl7jlJnDuzIewaooMb3DQxGXCz78GfRIh/Pm+GefDj+20C3LM4/KarJvmqNou3k8UtDxYwpdlpAVdIe7hW6V9ETvulS5PXS0f9P0OpNN/4GCjTD9Qf/NAepXD90t6OmToKAFD7042zyKoIc1ISvoMmtRCOB9ex+KYReXE776AyQOh1E/8N9+j3Vc9MNFziPog6ZBeYHJ+2+IywXFO8zzI/kdP6bQZQl9QZeB0a5LqAv6+negaBucdj9YrP7br2deUX/F0CPiIC3LLDc1MFq6D5xV5nl5YceP2d1Z/Rq8fIG54HcxQlfQZaLo9lFbZTIeOgNvwQo1QXfWGO+872g4boZ/930s5OKHAcqyPIhPgaThZrmpOLon3ALioXeU7Z/BvDth9zf1B6S7CCEr6A4JubSP7/4OT08yKYWBpp6HHmJFL2vfhEO7YNoDYPHzz8Sfk1yUHYC4vhCfZi4UTWW6eATdGimC3hEOrDeZQvYYs+wJd3UhfPqmKqXOUUptVUplK6Vmt7DdJUoprZTK8p+JTeMJuUg/l2YoL4bSnMbrCzab2G3++sDbEKohl6MH4YvfmTBGezsqtkREHKD8Nygal2L6rCcOb3pgtDjbHDNhKBxpQ8ilvKjj9oULh/PgjVnmonnJ8+51+4NrUxO0KuhKKSvwNHAuMAK4Uik1oont4oC7gaX+NrIeq1+DpyYRZTPpihJyaYZ3roU3r2i8vnSfecxZGXgbqsoAZZ6HkqB/dK+x98K/GqH0NxaLfya58FSJxvU1y0nDobCJGHrRdkgYbOY39dVDL9oOfxoK2V90zMZwYeGj5kJ/1duQNtGsC1EPfTKQrbXeqbWuBt4Cmprm/HfAH4FKP9rXGO2Com3EV5kvpoRcmiB/k4nxFW1vXF7u8dpzlgfejqoyI1wRcaEj6Bv/Axvfh9NmQ99RgTtOpB8muag4BM5q46EDJB4HR4sbe+HFO6CPW9B9HRTd862pZt23rGM2hgO1VaYp28iLIGUMRPcBiz00PXQgDdjntZzjXncMpdQEoJ/W+qOWdqSUukUptUIptaKwsJ2j7b0HAhBbvhcQD71JVrxoHmsr63tkzpo6ryJ3ReDt8Ah6VK/QEPQjBaaIKG0inPzTwB4rMq7jaYseQfH20KH+wGhNhbkr6+PloftSjJfj/n4UbOyYjeFA9uemjmKUu0rYYjEX0RD10FtEKWUBngDubW1brfVzWussrXVWYmJi+w7YexAAMeV7AImhN6LqCKx9C+JSzbJ3p8PDueYOp89g09e7qZxlv9py2C3oPbu+oDtr4L+3md7tF/0TrLbAHs8R3/EsF0+WhbeHDvUHRg/uBLT5zGOSjEfvy4Ukd5V5zG+h+rS7sP4diE6AgafWrYtPCVkPPRfo57Wc7l7nIQ4YBXyllNoNnADMC9jAaFxfsEfjOOwRdKkUrcf6d6C6DKb90iwf2lP3Won7RmvkxeYxJ8Beeqh46C6XEfPsz+DcP0DisMAf0x8hF4+H6PHQ4/qatgLeueieDJc+gyE22TxvrVq06oiJxdtjzAWh+mjH7Axlqo7A1k9MuMVqr1sfwh76cmCIUipTKRUBXAHM87yotS7VWidorTO01hnA98AMrXVg1EIp6D0Qe+kuQEIu9dAaVrwASSNh9GVmXYmXoHvi58fNMHNiBjrsEgqCrjXMvxfWz4Xpv4asGzrnuP6Y5MLjoce6BV0p46V7e+ieHi59BkOs+664vBVBz1tj7uRGXQxoKNraMTtDma3zobai7vfkIT7VZL50sV5SrQq61roWuANYAGwG5mqtNyqlHlZK+bniwkd6Z2IpcQu6DIrWkbPC5MpOugHsDuNFeIdcPBkuCUMhaYR46FrDp78yYw4n/xSmtBo19B/+8tCjepvP2kN6lvlcS9030cU7zPcgMtbLQ28l08XzvRh/jXnszmGX9e9Aj36QPrn++rgU07a4i3Wv9CmGrrWer7UeqrUepLV+1L3uQa31vCa2PS1g3rmH3gNRh3YTbZcYej1WvgQRsTBmllnuOaBByGWv+VHbHWbgL3dFYLsgNhT0ruTNeGLmS56CSTfDGQ8FJkWxOfzloXvi5x4m32y86yXuSTiKtxvvHEwMHVrPRc9daWZkSs8CW1TLXRxDkSMF8NqlsPgvLW9XXgw7vjR9fBoWl8W7x6iaC7tUl8MXD3f6ZOChWSnaexA4q8mwHZKQiweX09weDr+grldIrwENQi77jLcBpjtfZSkc3BE4m6rKjCca1QtctXX90YNNVZkpEln7hunTct6czhVzMP8XZ5VJiWsvZXl18XMPvTJMz/aVL5lB7+LsOkGP6gUWW+seeu5Kc8G3WM14Qn4YZboUboXnTzfjJctfaNnJ2PRf871tGG6BugtpcwOjWz820xWufavjNreBEBV0k7o42FYgIRcPuauMFzzkzLp1vTJM3NxT5l+aY/p6g/G+IHBhF5fLDM56PHToGmGXwq3w4rmw8yuY8ZTJN+9sMYe6fi4d8eA8VaINOeUeqDkKC39v/uceQbdYICax5Rj64TyTDeVp9pU8Mnw89F3fwAtnQk0lTLwOSve6s4CaYceX5jeU3EQ9Qrz7/96ch+7JEtrcKIgRUEJa0Ada8sVD95D9OaBg0PS6dT0HANp45lobQe/p9tAThpqCn0AVGHm88a4i6C4XLH0Wnp1qJlS+ai5M+FHw7HG0oyf6od0mDADmjuxIfmMPHSDpOBh2Pix3l6gnDKl7LTap5SyXXHcFsacaMmmEOY7nuKHKoT3mriwuBW76HE66y6zf8WXz78nfACnjmr7gt+ahexIO9nzbqS0UQlPQ41LAFkV/DkgM3UP25+ZHGN27bp1n7suSPaZCsLYSevQ36yxWSJsQuEwXz2BRVxD0owfh9Uvg459D5qlw6xIYckZwbPHQ1kkutIaXL4S57oHK8kITK29K0AGm/AxwhxM8HjqYOLq3oJflm2IqjzDlrjBhmZQxZjnZ3eUjlAuMtIYP7jbCfPW75nfRe6BxeHYsbPo9lYfNBbS5amF7lPleN+WhO2sgby1kTjWf0ZYP/XYqrRGagm6xQO9M+uk88dDBeE+5K+uHW8DcLoLxTjw56D29SgrSJ8GBDbB/jf9t6iqCXpQNz58BuxfD+U+YXhxxyZ1vR0Pa2nGxcKsJEexZbPrwHMtBbyLkAiaklnkqWCPcd2puYpPrC/r6uSbV9aVzzfckd6UJMdijzOtJI81jKGe6rHkddi40A9+e779SZlKQXYuMADfEE2ZKHt38fuPcqYsNyd9onKcJ10KvTNj0v/qvb/04YN1OQ1PQAXoPJNWVJzF0MF9WNAxu4HXGpZieE4d2GzGAuhg6mJzr+DR4ZabxKMB4M5s/hPdu7lh895igxwdP0Hd+bQbAKkvh2g9g0o3BiZc3RVs9dE9owB4D3/21cZVoU8x8Gq58s37Va2yi8e492U37lpkqyIoSeOk8E/v1hFvAhGiieoeuh152ABb8EvqfBFk31n9t4DQzzpPbRKO6A+5upMkjm993fIoJ3zXEc9ebngUjZpiLhue7v/UT0zRvyVNtPxcfCGlBT6rNo6q6iatrdyP7c/OjSx1ff73FajySkj11RUU9vDz0Hmlw3QfGi35lJmz5CF6/DN6+2nhuO5u5HfUFj1B5Sv+h8wTdWQtf/RFevdiEJG7+Avqf0DnH9hVPqKRkr2/b7/jShE6OvwU2fwB7vqu/n6bo2a/xRT42GVw1UFliLt77lplxl+s+NAU01UfqBszBXACTR4amh+5ywgc/NZlEM/7eOPUwc6opsGsqjp6/wVTdejtADYlLadpDz11lLpI9B8BxM02mzNaPzV3yf39iJk054baOnVszhLSg26khrrqbT3rrcpkWp4OmNz1NmicXvWSf21vuWf/1XhnGe7VHw1tXwd7v4axHzK16RzJgvEMu9iiTz9wZgl6UDS+eBV/93qTv3fhpXeipKxGbZC6uvvyPa6vM4Nqg6TD5x6CsZoAXVVcs5Csx7mrRIwVmsPzIAeg32YjMdfNh/A8b94BPGmHaCQSyZsHfVB0x3+dtH8PpvzHtgxsS7XaCmoqjH9hgwi0t3dHFp5qMIWdt/fU5K8xdjlJmnCo+HTa8B+/eYLa97N/1i8H8SOgKeh/TpCuhOreVDcOcA+vMl6qhJ+ahV4Y75LKvvnfuTe9MuO4jOPUXcOcKOOlO8wP3pF61B29Bh86pFl33Djw7xVRHXvoS/OA542V1VTxVna2xb6lJQxw03dzmj5llcthjk9reRMy7WtTTGrefuwoyabgJ03gPrIMZGK0pr1/T0JU5vN+MCWz/FM7/M5zYgjc8aLoJkXjPqOVymhh6a+2T41LMoKd3Xn9lqZmH1nOXo5QJu2R/DjnLYMbfjmlXIAhdQXenLvatbUbQu+B8f35j0RwzCcOmeaZ/N9RPV/Sm1wCoOGi+oC3dPvbONA29PLfwaVmwf3X7J8JtUtADNA2dsxYWPADv32TSzG5bYqr7ujrpk8zYRmvf1R1fmsyTjFPM8kl3mseWwi3NEeuuFi0vNIJuj6kb+GyOYwOjXTyOXltlioWeO83kl181Fybd1PJ7Bk4zorz7m7p1B3eZC2hT+efeNFUtun81oOuPQ4y4yDxOuing38vQFfS4VGpUJCmuJgYlclbCn4fBniWdb1egqT4KCx8zOcZzfwTf/gX6jmk+c8OT4XBod/0Ml9ZIzzJeWVNzVPqCR9AjAizolYfhtR+YQabJP4Zr59X90Lo6nv4grdUC7PgS+h1fd3FMGm4G+Iac3fZjegT9SL7xGNMmtO7l9x1lPr9Vr7T9eJ1BbZX5PfxtAnz0M/Odv2FB46yvpkifZNpleM/M5Jme0RcPHernonvuuNIm1K3rfzzc8Cmc84fW7ekgoSvoFgulUWn00wdwuhqU73oGOfYs7ny7As3+1WYmmVmvwfWfwLRfwTmPNb+9d/y4uZBLU3g8DO+QQNURePdG3wbIqstMXN4jFoHoia61ewb2xTDzH3De4/VbnHZ1Usa4xypaEPTyIpOBNGha/fUXPAHTH2j7MR09zTEP7TGZHP0mt/4ee5S5K9i+IPAN3dpCTYUZS/jrOHPHGp8KP/qPGTfxdbYpW4QJV278j+m/AiZ+rqx1/eWbI66JatHcVWbw2pPZ5aH/8Z3y3QxdQQfKovuToZooLtrrzgDoSAy4q+L58fc/CQacCKfeV3cr3hTegt4WD733QPPj9y482vg+bHgX3rux9R4knsZcHgIRQ1/2nOm3ccZvYPzV/t13Z2CLNHdX+1oQ9J1fmcfmQmptRSlTXLTtE5N90e943943+cdm6rWFv/ePHR2h8rBprPXXsaZYrFdGnZAPmt721NQTbjVZP2veMMv5G0x1bWsDlw2notPa/F7Sslp+XwAJaUE/GtufAaqACu/URWdt3WBPzoqu1eHPH+QsN2Ib08e37aN61YU92uKhK+XuyOh1UVz7tnsChU3wVSu3j40E3c8ees5KEzcfei6ceKf/9tvZ9Jts7ro8xS0uF8y/z3ica982FyxHTzM24C9iE+sGONMn+faeyFjTYnjHFyYTKhiU7DMdDP8yCj7/jWlxcN1HcMPH7RNyD/2ONyK85GkzZnRgQ+vxc2g8Fd3hXBPK8o6fdzIhLeiV8QOJVDVUHvBqwJ+/3uTS9jvBZH8cDqMsGK2NoDfszdwSStW1AGiLoIOJoxdsMqGWQ3tMCOukO2G8O3bfkmfZlIdeW2FukzvK0YPwznXmx3TRPxrnF4cS6Vnm/+IZcNzyobnzWPUq/Medcz7wtKZTUtuLJ9Olz5DGGS0tMekm490vfNR/trRG1RGTvfTKTPjLaPjmCVMBe/OXcM3/Wr479RWl4KQ74NAu46UfzvE9ZOM9Fd2Sf5hHX8JYASLAEycGlprM6bDuIfSmeTDE7cF4vIcTb4d935sqsJayO0KJkr3GA0hv4y1drwxTOt7WnOW0LJMBkLe2Low1+nIjzju/MkUSP/4GIqIbv7cpQQczMOopK28PWps+5mV5cOOCtglSV8TjIecsN+GXr/9oYrC3LjHpb3lr/CNa3nhy0dsqPBHRppPjgvtNocywc317344vzfdhxEzfti8vNvH6zR+aOwJPD6JTfwHjrgxMXcHwC6FnfzPhCbRc8u9NXIoJ0Xz/DHz/tOmtnzLW//b5SAi7NtCjbybLXMPoscOrReWe78wHM/RsE99qqqw3VPHEz9v6Qxx5MUy8tu2erGekPneF6es84GTj7TviTb5ycTasfq3p93p6oXtoWP5fUdK+LnRLnjbFImc9EtRbW7/Ro5+ZQi5nufHO8zcY4bJFGC9x/A/9L2CeTJf2eJJZ10PicHjravju762HNI8ehLnXmaZinz3YdHGSs8aER7+eA8+fCXMGwX9vNRezCdfCtR/C3Wth2v2BKxKz2kz1ZqU7E8tnDz3VpDl+MtvMRXDuH4PaXiKkPfSkuEjecJ7I5MMvm8yLpONg7xITT7NFdrw4pquRs9xkjrSWN9yQ0Zeav7YSk2B+QCtfNnm9J99d99rAU00F3L7vTTl6Q6oON+OhuwX9zStMteLty3wvjtm33MROh18Ax/+47efTFVHK3HHtW2rCLn0Gw6hLAntMT3aGrwOi3tij4MbP4H+3GW82Z7m5uHt/1t4sfsJ8F0ZeDN/+1YTuLnjSXLj2LTWpxfuWutstuysrT5sNQ84yVZydKY7jf2hSgq123+9m41JM1ln6ZLjkef+GxtpBSAt6r+gIPtXH81tewbLxfbBeYQom+p9oNkibCGvfNAMdQf5Ht5mDu2DpM+ZL1td9+5ezHFJ9yBv2J2A1of4AACAASURBVGkTTdmyzdH4ljk9q/mUu2ZDLodg71Jz4QWTLjamiRlhGnL0ILx7vfGIZj7ddZps+YP0SXUtVn/wr8B/V8dcbkJVSa2k5TWHIx4ufxW++xt8/hAUbjNNwHpn1t+uNAeWPgdjrzRjHWkTzUVg03/rtkkcDmOvMGGlAafUTWQdDCLjjIddWeL792vo2ebidPZjHQsl+omQFnSLRWGJS2a7bQLD1r9rQi0AA04yj2kTYfm/TCwy6Thza/fNn80XzDNQ2NWoLIVFfzJi7qw2seoff+OOZa8zYwOdSVqWEfRh5zUuo0+fZH6cZfn1C5u0blnQ175plmMSjQc36pKWw0GH8+C1S8z4wQ2fNO5HE+p4Qh+d4Z2D+Rw7ehylzB1byliYey38azpc/gpkTqnb5qs/ANqESpQyA+pJx5k7rbSJxiHoamMg465s2/aJw0yLiS5CSMfQwYRdFkVMNSPUS581uaEJQ82LnhirJ46+9Fn46rFOn+fPZ0pz4alJJjY5+jIzRVrhFnOrmrfWdMnr7BH0zCmmI92Eaxq/5hnQazhJRm2lyXGOiK1b5xH03BWmq+Okm2DK/5ksmm2fNH/8omx44SyTZnfV3PCImzckdbz5O+uR0LuTHHiayTiJSYBXL4KPZ5tmV3nrTB/ySTfVOVpginim3Q9Dz+p6Yh4GhLSHDpAY52BB8SRuttjNrc/wC+pul/oMNgNzuatMz4av3BWV+RuCZ3BLfPxzUzRx0xeQ7hauHV+Y3i2e9Etf84b9Rd/RcN+Opn98KWPMwHPOchh+ft36hn1cwIi7xWYGUa12mHyLafm78BH45k8mY8L7Nre63GQ5LLgfUKYjpHc5dThhj4Jbvgq2Fe2nzyAzrduH95jJMpb+06yPiDMXbaHTCHlBT4qPZNVeu7nyb/u4ft9riwVSxxkPfcH9xmtMm9g1mwxt/tDEUc/4bZ2YA5zzR8j+0szi3nNAXYZCZ9KcJ2WPMoLfsBzce3ILD0oZL7280Hj7nvM4+aem/8bOr8wFOHel6ZK36X9moCxhKFz5VkA71Al+wNEDLn3RXIh3fWNSFfuf4HsBnOAXQl/Q4yI5WF5NzehZ2Ld9bIoOvEmbaMqE89bA9F+ZtKmvHjNfvIiY4BjdkKoy450njWwcI49LhjMfMt5PZ3vnvpCeBatfrz/w7D25hTceQT/xjrp1466Grx83MXLtbuEQEWeyIsZeYVochHLhUHcjIgaGnWP+hE4nDATd9FsoTD+b1LtWH2ure4y0iYA2VXEn3QXbPzPLBVvqe8LBZOHvTbXZZS833cBnwnUm62XYeZ1tWeukTzKVjQWb63J3mwq5gBlA6zvGDCR5sDtMGtuWj8zdVNpEU3Zti+gc+wUhjAgDQY8EoOBINan9BjbeYMDJpg/GOY+Z3HTPHIH5G7qGoBdlm4yWrBuaH/C0WOCs33WuXb7iqVrNWd66oF/yfNOFKMPPM3+CIHQIn+5llVLnKKW2KqWylVKzm3j9J0qp9UqpNUqpxUqpEf43tWmS4t2Cfriy6Q2ie8OPv65LZew5wNzSdzSOrrV/puRa8zqgTDFFKNIr02QW1Wuz24ygQ3jljwtCF6NVQVdKWYGngXOBEcCVTQj2G1rr0VrrccDjwBN+t7QZPCGXgrJW2rl6sFjMlFodFfRPfwUvNDPtm6+4XLDubTOgG4zBTn+glAm7eBcYNTUoKghCwPHFQ58MZGutd2qtq4G3gHolg1rrw16LMUCn9axNiI1AqTYIOrhnMd/Qsda6Wz4yGRnFO9q/j93fmHTEsVe0fx9dgfQsKNpaNyNRc4OigiAEFF8EPQ3Y57Wc415XD6XU7UqpHRgP/a6mdqSUukUptUIptaKwsLA99jbCZrXQJyaCwrJmQi5NkTzSlPe2t7Xu4TxTyASwbUH79gGmYjKyh+9d67oqxwqM3AVcVWUmP90WGTybBKEb4rd8MK3101rrQcAvgF81s81zWussrXVWYqL/ejYkxjkoONwWD909eNfesIunlWxEbMtVji1RdcRM8jzyoi7RA6JDpE4w1aSeqf88Zf8SLxeETsUXQc8FvGdGSHeva463gIs6YlRbSYqLbFvIxdOUqL0Vo3uWmNnSJ14He741/VfaypYPzSTMY9vYO6Ir4og3eeMrXzZNtBr2cREEoVPwRdCXA0OUUplKqQjgCmCe9wZKqSFei+cD2/1nYusYQW9DyMXRw/SXaLeHvsSkGA6/wFSfejzTlnDWmGrIsgNmee2bJuPGu7I1lJlyr6nsXPpM417ogiB0Cq3moWuta5VSdwALACvwotZ6o1LqYWCF1noecIdS6gygBjgEXBtIoxuSFB9J0ZFqnC6N1eLjbX7yqPYJesUh875pvzSx46heJo4+8uKW3/fRvbDq3+Z5fLqJ35/6i/AJSySPNBe475+B3hnioQtCEPCpsEhrPR+Y32Ddg17P7270pk4kKc6B06U5WF5NYpyPA3HJI40Q11S2Pru3N3uXAtrktVttphH/9k9b7rm+4iUj5lk3mp4kOSvMdHITfuT7cUOBqf9nQkl5a2HI2cG2RhC6HSFfKQpe1aJllW0TdO006XZtmQNw73cmg8PTxnXo2SaXPHdl05We+5aZWdwHnQ7nzQm99qhtIXV83QVOPHRB6HTCouvRsWrRNuWiuzNdDjQxMFqa2/xA554lpo2rJzNl0OmgrE1nuxwpMHMpxqd2iempOoWp95nHyNiWtxMEwe+Eh6B7GnS1JXWx90CISYJVr9QvMCovhn+eBM+dBkca5MpXH4X9q+raCICZPWfASbC1CUFf+bIZBL3i9e7TzL/fZDjjIRh7VbAtEYRuR1gIeqJXyMVnLFaY/oCZ5Nh7jsMvfmuyNA7nwWs/MBNOeMhdYbJa+p9Uf19DzoSCjeY93uxaZBpWeeYE7S6ccg/0b8cExIIgdIiwEHSH3Uq8w9a2kAvA+B+ZHuSf/cYMjuauNB77CbfCrFfN9GhvXWU88yMFsPVjQDWOlQ+cZh53flW3rqbSxM8zpnbk1ARBEHwmLAZFAZLi21gtCsZLP/tRMxfi9/8wGRoxiSad0BEPF/0T3r8Zfp9S957U8Y0nKU4eBdEJRtA9k8zmLAdnVf1JcwVBEAJI2Ah6Ymwbi4s8DJoGQ8+BL38H2gUXP2vEHGDM5WYGltyVENsX4vrW9f/2xmKBgacaQdfa5Jbv/saUww84qfH2giAIASBsBD0pPpKVew61781nPQLZn0P6ZBgzq/5rw8+vPwFycwycBhveMzP3JI8w8yqmjDVVqYIgCJ1A+Ai6u5+L1hrV1urLhCFm1vKeA9pfuTnwNPO4cyH0yjAhlxNubd++BEEQ2kEYCbqD6loXhytq6RHdxLycrZE6vmMG9OxnZq3f+RUkjQBXDWTKgKggCJ1HWGS5QF1xUX574uj+YuA02P0t7PjCFBuFS+MtQRBCgrAR9PRe0QDsO3g0eEYMmmZa4q542Xj8Uv4uCEInEjaCnpkQA8CuovLgGZFxivHMq8skXVEQhE4nbAS9V7SdeIctuILu6FHXtCtDBF0QhM4lbARdKUVmQgy7i4Mo6ADDzzPzhEr8XBCETiZsBB1M2GV3URBj6AAn3gl3rzEFSYIgCJ1IWAl6RkIM+0srqKxxBs8Iq637dFYUBKFLEVaCnpkQg9awpzjIXrogCEIQCCtBz+jTBTJdBEEQgkR4Cbo7dTHoA6OCIAhBIKwEvUeUnT4xEewWD10QhG5IWAk6GC99pwi6IAjdkLATdJO6KIIuCEL3IywFvaCsivKq2mCbIgiC0KmEnaB7Ml1kYFQQhO5G+Al6gum6KKmLgiB0N3wSdKXUOUqprUqpbKXU7CZe/5lSapNSap1S6gul1AD/m+obxzx0EXRBELoZrQq6UsoKPA2cC4wArlRKjWiw2WogS2s9BngXeNzfhvpKTKSN5PhIdgW7p4sgCEIn44uHPhnI1lrv1FpXA28BM7030Fov1Fp7FPR7IN2/ZraNjD5doOuiIAhCJ+OLoKcB+7yWc9zrmuNG4OOmXlBK3aKUWqGUWlFYWOi7lW0kMyFGYuiCIHQ7/DooqpT6IZAFzGnqda31c1rrLK11VmJioj8PXY/MhBgOlldTWlETsGMIgiB0NXwR9Fygn9dyuntdPZRSZwAPADO01lX+Ma99ZHSF6egEQRA6GV8EfTkwRCmVqZSKAK4A5nlvoJQaDzyLEfMC/5vZNgYlGkHfWXgkyJYIgiB0Hq0Kuta6FrgDWABsBuZqrTcqpR5WSs1wbzYHiAXeUUqtUUrNa2Z3ncKAPjHYLIrsAhF0QRC6DzZfNtJazwfmN1j3oNfzM/xsV4ewWy1kJMSwXQRdEIRuRNhVinoYnBjLDhF0QRC6EWEr6EOSY9ldXE5VbRDnFxUEQehEwlbQByfF4tKwWypGBUHoJoS1oAMyMCoIQrchbAV9UGIsSsH2grJgmyIIgtAphK2gO+xW0ntFiYcuCEK3IWwFHWBIUpwIuiAI3YawFvTBSbHsLCrH6dLBNkUQBCHghL2gV9e62HdQMl0EQQh/wl7QAakYFQShW9AtBF3i6IIgdAfCWtDjHXaS4yNF0AVB6BaEtaCD8dKzJRddEIRuQNgL+pCkOHYUlqO1ZLoIghDehL2gD0qK5UhVLQcOVwbbFEEQhIAS9oI+xD0wui1f4uiCIIQ3YS/oI1PjibBaWLStMNimCIIgBJSwF/Q4h52pQxOZvz4Pl1SMCoIQxoS9oANcMCaFvNJKVu87FGxTBEEQAka3EPTTj0siwmbhw3V5wTZFEAQhYHQLQY9z2DlNwi6CIIQ53ULQAc4fk0L+4SpW7pWwiyAI4Um3EfTTj0sm0mbhIwm7CIIQpnQbQY+NtDFtWBLz1+dJf3RBEMKSbiPoYMIuBWVVLNt1MNimCIIg+J1uJeinH5dEjyg7z3y9I9imCIIg+B2fBF0pdY5SaqtSKlspNbuJ16cqpVYppWqVUpf630z/EB1h4/Zpg/h6WyHfZRcF2xxBEAS/0qqgK6WswNPAucAI4Eql1IgGm+0FrgPe8LeB/uaaEzNI7eHgj59skQ6MgiCEFb546JOBbK31Tq11NfAWMNN7A631bq31OsAVABv9isNu5Z4zh7I2p5SPNxwItjlCN6LkaDU3vLyc/SUVwTZFCFN8EfQ0YJ/Xco57XZtRSt2ilFqhlFpRWBi8Zlk/mJDO0ORY5izYSo2zy1+DhDBh9d4SvtxSwGeb8oNtihCmdOqgqNb6Oa11ltY6KzExsTMPXQ+rRfHzs4ezq6icd1bkBM0OoXuxv9R45mv2lQTZEiFc8UXQc4F+Xsvp7nUhzenHJTEmvQfPf7NT2gEInUJeiZlkRQRdCBS+CPpyYIhSKlMpFQFcAcwLrFmBRynFjadksrOonK+2FQTbHKEbkFdqBH1XUTklR6uDbI0QjrQq6FrrWuAOYAGwGZirtd6olHpYKTUDQCk1SSmVA1wGPKuU2hhIo/3FeaNTSOnh4PlvdgXbFKEbkFdaQYTV/OTESxcCgU8xdK31fK31UK31IK31o+51D2qt57mfL9dap2utY7TWfbTWIwNptL+wWy1ce1IG3+0oZtP+w8E2Rwhz8korOWlwH5QSQRcCQ7eqFG2KKyf1J8pu5YXF4qULgUNrzf6SCoYkxTI4MVYEXQgI3V7Qe0TbuTwrnXlrcyk4XBlsc4Qw5dDRGqpqXaT0iGJcv56s3VcihW2C3+n2gg5w/cmZ1Lo0s99fz8FyGawS/I+nmCi1p4Nx/Xty6GgNew8eDbJVQrghgg5kJMTw6/NH8M32Qs7+yyK+3CKFH4J/8WS4eDx0kDi64H9E0N3ccEom/7v9FPrERHDDyyu4/fVVbMgtDbZZQpiQ5y4qSunpYFhyHFF2K6v3iqAL/kUE3YsRqfH8746Tuev0IXy9rZAL/r6Ya15cxpIdxRLvFDrE/pJK7FZFQkwkNquF0Wk9xEMX/I4IegMibVZ+duZQvp09nZ+fM4xN+w9z5b++5+J/fMcnGw5IVanQLg6UVtC3hwOLRQEwrn9PNu0/TFWtM8iWCeGELdgGdFV6RNm57bTB3HByJu+tyuHZr3fyk9dWMqBPNJdOSOeSienERNqYtyaXt1fsI/dQBVOHJnLWiL6cOiyR2Ej51wp17C+tJKVH1LHlcf16Uu10sTmv7FhMXRA6iqhOKzjsVq4+fgCzsvrx8YYDvLF0L3/+bBtPfL4Nu9VCda2L41LiOXVoIl9vK+R/a/aTGBfJgp9OpXdMRLDNF7oIeaUVTOjf69hy1gDz/JtthSLogt8QQfcRm9XChWNTuXBsKvsOHuXdlTmUVdZy8fg0RqXFo5TC6dIs2l7IjS8v5+9fbuc3F4ZEwawQYFwuzYEGHnpSvIOsAb34cF0ed54+JIjWCeGExNDbQb/e0dxz5lAevHAEo9N7oJSJi1otimnDkpg1qT+vLtnD7qLyIFsqdAWKyquocWpSezrqrb9wbCpb88vYll8WJMuEcEMEPQDcc+YQImwWHl+wJdimCF0AT9tcbw8d4NzRfbEo+HDt/mCYJYQhIugBICnOwS1TBzJ//QFW7jkUbHOEIHMsB71HfQ89Kc7BiYP68MG6PEmL7eIs2HiAG15e3uWb+ImgB4hbpg4kKS6S336wkfU5pc2mO9Y4XXyyIY+KaklfC1f2uz301J5RjV67YEwqu4rK2djFhaK788zXO/hySwEznlrM459sobKma/5eRdADRHSEjQfOP471uaVc+NRiJj36OffOXUuu1wTBlTVObn1tJT95bRW3vb4y7Oc33Vt8lAOl3a8BWl5pBZE2C72i7Y1eO2dkX2wWxQfrJOzSVTlQWsnqvSXcPCWTi8en8Y+vdjDjqcVdUtRF0APIzHFpLPvlGTw5ayxThyby8YY8znlyEe+s2EdZZQ3XvriML7YUcMGYFBZuLeSB/6xv8tb7SFUtS3cWs7c4uM2capwu5q/Po6yyps3vnbd2P2c++TWX/PM7jlTVBsC6rkteaSUpPRzHBs+96RUTwZQhCXy4VsIuXZUFGw8AMGtSf+ZcNpanr5rAtvwjvLeq681HLGmLASYxLpKLx6dz8fh09hYf5f/eXct9767j0fmbKaus5S+zxjFzXBoDE7fxty+2kxgXybmjUtiQW8q63FJW7y1h64HDeCI2/XtHc8qQBH54/ABGpMZ32nnsKDzCPW+vYV1OKeP69eSVGycT72jscTbE5dI88dk2nlqYzcjUeDblHeYPH2/mkYtGd4LVXYO8BimLDblwbCo/m7uWVXsPMXFA7060TPCFjzfkMTgplsFJsQCcN7ovY9N78K9FO7liUn+slsYX6mAhHnon0r9PNG/dfAK/vmAE8Q47z/5wIjPHpQFwzxlDuGJSP55euIML/r6Y2e+v54O1+0mIjeCO6UN44dosfjtjJEOT4/jf6lwu/se3zOuE7AitNW8s3csFf1vM3oNHuWPaYDbklnLNC8s43IqnXlXr5PY3VvHUwmxmZfXjP7edzI0nZ/La93v5Lrso4LZ3FfJKKkhpkLLozZkjkomNtPHs1zs70SrBF4qPVLFs10HOHdX32DqlFD85dRC7i48e8967CuKhdzIWi5mc+sZTMuutV0rxyEWjmNC/F44IK2PSetC/d/Sx3h8erj0pg6IjVfzk1ZXc9eZqth44zL1nDmu0nb/4z+pcfvmf9UwZksCfLhtLcryDMek9uP2NVfzohWU8f00WiXGRjd5XWePkJ6+t5KuthTxw3nHcNCUTpRT/d/YwvtxSwH3vrmPBPVPDvkWC06XJL6sitQUPPc5h5yenDuRPn25j+e6DTMoQL72r8OmmfFwazvESdICzRvYlo080z369g3NH9W0ynBYMxEPvQtisFi6f1I8ZY1PJSIhpVqQTYiN54+YTmJVlPPrz/vYNTy/MZmfhEb/ac7C8mt99uIkJ/Xvy7+snkxxvvMyzRvblH1dPZNP+UqY8/iWPfLiJgrK6wc6Kaic3/XsFX28r5PcXj+bmqQOPfeEddiuPXzqG/aUV/O6DTWEfNy4oq8Tp0i166AA3njKQ5PhIfj9/c9j/T0KJjzccoH/vaEak1A9vWi2Km6cOZG1OKUt2FgfJusaEt3sUxkTYLPzhktFkZfTi9aV7mbNgK3MWbCWlh4N+vaJJ7xXF4ORYJmf0ZnR6DyJt1jYf49GPTJz/sR+MaXRxOXNEMgt+OpWnFmbz4re7ePX7PaT3isJmsXC4sob8w5XMuXQsl05Mb7TfrIze3HrqIP7x1Q7694nm9mmD2/1/6OocS1lswUMHiIowXT5/8d56Pt5wgPNGp3SGeUILlB6t4bvsIm48JbNJD/ySCek8+dk2nv16JycNSgiChY0RQQ9hlFJcltWPy7L6sb+kgk82HGDD/lJyDlXw/c5i3l+dC0CkzfTfHpIcx9DkWAYmxtI33kFSXCQ9o+1Nflm/zS7ivVU53DFtMMP6xjV5/IGJsTxx+Tjumj6El7/bTeGRKpxOjUtrHpoxkrNH9m3yfQD/d9Yw8kormbNgKwmxEcya1N8//5Quxgdr92O1KIY28z/05tKJ/Xhh8S4e/2QLZxyXTIRNbqCDyeeb86l16UbhFg8Ou5XrT85kzoKtvPr9Hn50woBOtrAxIuhhQmrPKG5oEJc/WF7N8t0HWbbrIOtySpi/Po83l9UfyHTYLQxOimVIUhwZfWKIibQSabPw/OJdZPSJ5o7prXvPGQkxPDSjbY3ILBbF45eO4WB5Nfe/vx6rxcIFY1Jw2Nt+J9FV2VNczutL93B5Vj/SmigqaojVorj/3OO4/uXl/OHjLfzq/OPaPDbidGkUtPq+WqcLm7X+BaOwrIq5K/Zx6cT0Y+G17krB4Uqe/Hwb/XpHMTa9+W6YN08ZyOq9h/j1fzcABF3UVbDidVlZWXrFihVBOXZ3RWtN4ZEqdhcdpaCskvzDVeQeqmB7QRnZBUeOzXsJJqTz8nWTOGlwYG8lj1bXctW/lrJmXwmRNgtZGb04ZXAiZ41MZlBibECPHWjufHM1n2/K5+v7TiPJR4HUWvPg/zby6vd7OOO4JJ6cNY64FtJDtdZszS9j8fYivt9ZzNJdB4m0WfndzJGc20TYpqLayUPzNvLfNbn8eOpAbj1tMFERVr7LLuLut9dQWFZFjyg7D88cyYyxqSilKKusYcXuQ+SWVFB0pIpD5dXER9lJ7RlFSg8HLq05VF5DSUUNUXYrKT0dpPaIYmBiDHZr6N1lHK6sYdaz37OnuJy3bzmR0ek9Wty+qtbJba+t4ostBfzuolEBF3Wl1EqtdVaTr4mgCx6qap1U1bqornVht1roEdV6nrk/qKxx8m12Ed9mF/PdjiK2HDDdBwclxnDq0CT69TbCkRzvoGd0BD2i7MQ7bI08zK7EupwSZjz1LXdOH8y9Zw1r03u11rz6/R5++8EmBibEcP6YFHYXlbO7+CgRNguDEmMYmBDL/tIKPt+cz76Dpvo4MyGG4zN7s2F/KRtyD3Pe6L48NGMkSXHmYrKj8Ai3v76KLQfKmJzRm2W7D5LWM4rThiXyxrK9DEyI4f5zj+Ppr7JZvbeEacMSOVrtZOWeQ9R6ta6Ic9gor6qltcm7MvpE8+fLxzFxQK+WN+xCVNU6uf6l5SzbdZAXrpvEqUMTfX6fR9RnZfXjl+cfF7Dfjwi6EFLsLzFC9enGfJbvPkhVbdMtERx2C7GRNmIibcQ5bMRG2ohz2ImJsBIVYSM6woSPIjx/Vgs2i8Lm9Wi3KmwWCzarqvf82DqLBavFvBZpsxJhM++xWhQWi8JmUdjd+/OMRWitufr5pWw5UMbX953WoofdEt9lF3H7G6soqaghtUcUA/pEU13rYmdROQfLq4mwWZgyOIEzRiQzbVgSfd3Nv2qdLp77Zid/+Ww71U4XPaLs9I13kHPIXBCenDWO04YlsXRnMb+Zt5EtB8q4eHwaj1w0iphI27H3P/VlNhl9Yjh1WCJThiQwMCGW3jERRNgs1Dpd5JdVsb+kAptF0Ss6gp7Rdo5WO8krrWBX0VGe/GwbeaUV/PjUQfz0jCHtGpjvLPYdPMrHG/L47+r9bMo7zJ8vG8slTQzot0RVrZMnPtvGvxbtJCE2kodnjmTq0ESiI/wb2e6woCulzgH+CliB57XWf2jweiTwCjARKAZmaa13t7RPEXTBF7TWHCyvJq+0kvzDlRyurKH0aA2lFbWUV9dypKqWI5W1lFXWUFZZS1llLUdraqmodnK02kl1rauedxkolAK71UKk1VwIDh2t4TcXjuD6kzNbf3MLVNU60ZpGYwslR6uJtFmJimheJHcUHuGTDQc44P7fRdqt3H/u8HpNwmqdLnYUljM0ObbR4LjWukP51WWVNTzy4WbeXrGPCJuFpLhI90B8BBHWugutzaLcF03z3G6zYPdceBtcVM1F2FxU7e7nnotspM1cwB12y7HnkXYrDru5mDc8l4LDlXy4Lo//rd3PWveE3aPS4rnh5Ex+MKFtYu7NupwSfv7uumN3mj2j7aT1jKJPbCS9o+30jI7gwrEp7a4K7pCgK6WswDbgTCAHWA5cqbXe5LXNbcAYrfVPlFJXABdrrWe1tF8RdKGzcLk01U4XNU4XtU5tHl3aPHc1XOeixqmpdZllp1NT6zKvV9e6qKo1+3G6TDZPrUtTU+ui2v16jXtfMZE2fnbmUMlUAb7ZXsji7UUUlFWRf7iS0oqaY/9Pz//a6dLHLr61TvN5+ROLgii7FYe97q5t78GjuDSMTI3nwrGpnDcqhf59ov1yvBqni8825bOrqJzckgr2l1RwqLyag0erKSmv4dcXjODySf3ate+OCvqJwENa67Pdy/cDaK0f89pmgXubJUopG3AASNQt7FwEXRCE5tBa43TVXUydLn3sYum5ENc4XdTUaqqddWM/1bUuKmtdVNWYdZXux4pqJxU1TjNOVGO2yUyIYcbY1GM99XPzYQAABOxJREFUWjr7/Np799OSoPsS3EkD9nkt5wDHN7eN1rpWKVUK9AHqNexQSt0C3ALQv3945h0LgtBxlFJmLMPaONwUDgSqVUCn3g9qrZ/TWmdprbMSE30bPRYEQRB8wxdBzwW8gz3p7nVNbuMOufTADI4KgiAInYQvgr4cGKKUylRKRQBXAPMabDMPuNb9/FLgy5bi54IgCIL/aTWG7o6J3wEswKQtvqi13qiUehhYobWeB7wAvKqUygYOYkRfEARB6ER8ynjXWs8H5jdY96DX80rgMv+aJgiCILQFSZIVBEEIE0TQBUEQwgQRdEEQhDAhaM25lFKFwJ52vj2BBkVL3YTueN7d8Zyhe553dzxnaPt5D9BaN1nIEzRB7whKqRXNlb6GM93xvLvjOUP3PO/ueM7g3/OWkIsgCEKYIIIuCIIQJoSqoD8XbAOCRHc87+54ztA9z7s7njP48bxDMoYuCIIgNCZUPXRBEAShASLogiAIYULICbpS6hyl1FalVLZSanaw7QkESql+SqmFSqlNSqmNSqm73et7K6U+U0ptdz+GznTqPqKUsiqlViulPnQvZyqllro/77fdHT/DCqVUT6XUu0qpLUqpzUqpE7vJZ32P+/u9QSn1plLKEW6ft1LqRaVUgVJqg9e6Jj9bZfib+9zXKaUmtPV4ISXo7vlNnwbOBUYAVyqlRgTXqoBQC9yrtR4BnADc7j7P2cAXWushwBfu5XDjbmCz1/IfgSe11oOBQ8CNQbEqsPwV+ERrPRwYizn/sP6slVJpwF1AltZ6FKaT6xWE3+f9MnBOg3XNfbbnAkPcf7cA/2zrwUJK0IHJQLbWeqfWuhp4C5gZZJv8jtY6T2u9yv28DPMDT8Oc67/dm/0buCg4FgYGpVQ6cD7wvHtZAdOBd92bhOM59wCmYlpQo7Wu1lqXEOaftRsbEOWeFCcayCPMPm+t9SJMS3FvmvtsZwKvaMP3QE+lVEpbjhdqgt7U/KZpQbKlU1BKZQDjgaVAstY6z/3SASA5SGYFir8APwc8U773AUq01rXu5XD8vDOBQuAld6jpeaVUDGH+WWutc4E/AXsxQl4KrCT8P29o/rPtsL6FmqB3K5RSscB7wE+11oe9X3PPCBU2OadKqQuAAq31ymDb0snYgAnAP7XW44FyGoRXwu2zBnDHjWdiLmipQAyNQxNhj78/21ATdF/mNw0LlFJ2jJi/rrV+370633ML5n4sCJZ9AeBkYIZSajcmlDYdE1vu6b4lh/D8vHOAHK31UvfyuxiBD+fPGuAMYJfWulBrXQO8j/kOhPvnDc1/th3Wt1ATdF/mNw153LHjF4DNWusnvF7ynrv1WuB/nW1boNBa36+1TtdaZ2A+1y+11lcDCzHz1EKYnTOA1voAsE8pNcy96nRgE2H8WbvZC5yglIp2f9895x3Wn7eb5j7becA17myXE4BSr9CMb2itQ+oPOA/YBuwAHgi2PQE6x1Mwt2HrgDXuv/MwMeUvgO3A50DvYNsaoPM/DfjQ/XwgsAzIBt4BIoNtXwDOdxywwv15/xfo1R0+a+C3wBZgA/AqEBlunzfwJmaMoAZzN3Zjc58toDBZfDuA9ZgMoDYdT0r/BUEQwoRQC7kIgiAIzSCCLgiCECaIoAuCIIQJIuiCIAhhggi6IAhCmCCCLgiCECaIoAuCIIQJ/w/jdJK64cfK/gAAAABJRU5ErkJggg==", + "text/plain": [ + "
" + ] + }, + "metadata": { + "needs_background": "light", + "tags": [] + }, + "output_type": "display_data" + } + ], + "source": [ + "metrics = history.history\n", + "plt.plot(history.epoch, metrics['loss'], metrics['val_loss'])\n", + "plt.legend(['loss', 'val_loss'])\n", + "plt.show()" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "5ZTt3kO3mfm4" + }, + "source": [ + "## Evaluate test set performance\n", + "\n", + "Let's run the model on the test set and check performance." + ] + }, + { + "cell_type": "code", + "execution_count": 138, + "metadata": { + "id": "biU2MwzyAo8o" + }, + "outputs": [], + "source": [ + "test_audio = []\n", + "test_labels = []\n", + "\n", + "for audio, label in test_ds:\n", + " test_audio.append(audio.numpy())\n", + " test_labels.append(label.numpy())\n", + "\n", + "test_audio = np.array(test_audio)\n", + "test_labels = np.array(test_labels)" + ] + }, + { + "cell_type": "code", + "execution_count": 139, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "ktUanr9mRZky", + "outputId": "475fcebb-64af-4c8e-e4af-784289106a5b" + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Test set accuracy: 95%\n" + ] + } + ], + "source": [ + "y_pred = np.argmax(model.predict(test_audio), axis=1)\n", + "y_true = test_labels\n", + "\n", + "test_acc = sum(y_pred == y_true) / len(y_true)\n", + "print(f'Test set accuracy: {test_acc:.0%}')" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "en9Znt1NOabH" + }, + "source": [ + "### Display a confusion matrix\n", + "\n", + "A confusion matrix is helpful to see how well the model did on each of the commands in the test set." + ] + }, + { + "cell_type": "code", + "execution_count": 140, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/", + "height": 497 + }, + "id": "LvoSAOiXU3lL", + "outputId": "d3a37e34-55f8-4ca6-d826-ac8255e8f36c" + }, + "outputs": [ + { + "data": { + "image/png": "iVBORw0KGgoAAAANSUhEUgAAAjcAAAHgCAYAAABZ+0ykAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAALEgAACxIB0t1+/AAAADh0RVh0U29mdHdhcmUAbWF0cGxvdGxpYiB2ZXJzaW9uMy4yLjIsIGh0dHA6Ly9tYXRwbG90bGliLm9yZy+WH4yJAAAgAElEQVR4nO3dd7hdVZn48e+bQhJ6CcQIDE1EihCqIIFBkEhTQBThQboTC6IOqGD5DUVwUEEdRkCDVGVEpAgIgyWCRHpA6S3Sk5BCCXUCyX1/f5wd5pBJcm8ud99zzt7fD89+7j7r7H3We/Ncct+8a629IjORJEmqigGtDkCSJKkvmdxIkqRKMbmRJEmVYnIjSZIqxeRGkiRVismNJEmqlEGtDmBhvrPGAa5RV586YeoNrQ5BFTJ00BKtDkEV9Mprj0d/9vfmzMf6/Hft4OFr9+v3sCBWbiRJUqW0beVGkiSVrGtuqyMohZUbSZJUKVZuJEmqq+xqdQSlsHIjSZIqxcqNJEl11VXNyo3JjSRJNZUOS0mSJLU/KzeSJNVVRYelrNxIkqRKsXIjSVJdVXTOjcmNJEl15ROKJUmS2p+VG0mS6qqiw1JWbiRJUqVYuZEkqa4quhTc5EaSpJryCcWSJEkdwMqNJEl1VdFhKSs3kiSpUqzcSJJUV865kSRJan8mN5Ik1VXX3L4/uhERQyPi9oi4OyLuj4gTiva1IuK2iJgUEb+OiCWK9iHF60nF+2t214fJjSRJdZVdfX90bzawY2ZuAowCdomIrYHvAT/KzPcALwCHF9cfDrxQtP+ouG6RTG4kSVK/yYZXipeDiyOBHYFLi/YLgL2K8z2L1xTv7xQRsag+nFAsSVJdtWgpeEQMBO4E3gOcAfwDeDEz5xSXPAOsWpyvCjwNkJlzImIWsBIwc2Gfb+VGkiT1mYgYGxETm46x81+TmXMzcxSwGrAV8L6+jMHKjSRJdVXCUvDMHAeM6+G1L0bE9cA2wPIRMaio3qwGTC4umwysDjwTEYOA5YDnFvW5Vm4kSaqrrq6+P7oREStHxPLF+TBgZ+BB4HrgE8VlBwNXFudXFa8p3v9zZuai+rByI0mS+tNI4IJi3s0A4JLM/F1EPABcHBEnAX8DzimuPwf4RURMAp4H9uuuA5MbSZJqKrP759L0fZ95D7DpAtofozH/Zv72/wE+uTh9OCwlSZIqxcqNJEl1VdG9pUxuJEmqqxY956ZsDktJkqRKsXIjSVJdVXRYysqNJEmqFCs3kiTVVVf/LwXvDyY3kiTVlcNSkiRJ7c/KjSRJdeVScEmSpPZn5UaSpLpyzo0kSVL7s3IjSVJdVXTOjcmNJEl1VdHkxmEpSZJUKVZuJEmqqcxqPqHYyo0kSaoUKzeSJNVVRefcmNxIklRXPudGkiSp/Vm5kSSprio6LGXlRpIkVYqVG0mS6qqic25MbiRJqiuHpSRJktqflRtJkuqqosNSVm4kSVKlWLmRJKmunHMjSZLU/qzcSJJUVxWt3JjcSJJUV04oliRJan9WbiRJqquKDktZuZEkSZVi5UaSpLqq6Jwbk5s299Ef/Avr7rgprz73Ej8bcywAQ5dbin3OOJLlVluZWc/M4LIvnM7/vPQaAGtsvT5j/u1ABg4eyGvPv8yFnzqpleGrg5w97jR23+3DTJ8xk1Gb7tTqcNShzvzp99h1lx2ZMeM5ttpyFwBWWGE5LrjwJ/zTGqvy1JOTOejAI3jxxZdaHKkAh6XUGnf/ZgL/dfD339a27Rc+xuM33c+ZOxzN4zfdz7Zf+BgAQ5Zdkl1POpRff+Y0frrzMVz6hdNbEbI61IUXXsLuexzQ6jDU4S76xWXstdchb2s76ujPc8MNNzFq4x254YabOOroz7cmONWGyU2be+r2h3j9xVfe1rbezptxz2UTALjnsgmsN2ZzADba84M8dN0dvDTlOQBee85/GannJvz1Np5/4cVWh6EOd9NNt/PC82//Odp9j5256KLLALjoosvY46NjWhGaFiS7+v5oA6UlNxHxyYhYpjj/dkRcHhGbldVfnSw1fDlemd74y+OV6S+y1PDlAFhprXcxdLmlOPDib/GZ353Exh8f3cowJQmAVVYZzrRnZwAw7dkZrLLK8BZHpKors3Lz/zLz5YgYDXwYOAc4a1E3RMTYiJgYERMnvjKpxNCqJYuvAwYNZORGa3Hxoady0YGnMPpLe7PiWu9qaWySNL/M7P4i9Y+urr4/2kCZyc3c4uvuwLjMvAZYYlE3ZOa4zNwiM7fYYun3lBhaZ3t15iyWXmV5AJZeZXlemzkLgJemPs9jN97Dm6/P5vUXXuGp2x9ixPr/1MpQJYnp02cy4l0rAzDiXSszY8ZzLY5IbzG5WWyTI+JnwKeAayNiSMn91cbDf7qLjffZDoCN99mOh/94FwCP/PFOVt/yvcTAAQwaugSrjlqHmZOmtDJUSeLaa/7EAQfsA8ABB+zDNb/7Y4sjUtWVuRR8X2AX4NTMfDEiRgJfK7G/Str79CNYY5v1WXKFZfjyrf/JX350KTefeTX7nHkkoz61A7Mmz+SyYlXUzElT+Mdf7uGzvz+F7OribxffwIxHnmnxd6BO8ctfnME/b78Nw4evyBOPTeSEE0/lvPMvbnVY6jDnnf8fbLf91qy00go8/OjNnHzSj/nhaWdx4S9+wkEH78vTT03moAO/2OowNU9FhwijzLHPYr7Nupl5XkSsDCydmY/35N7vrHFANf/E1TInTL2h1SGoQoYOWuQou9Qrr7z2ePRnf6//+oQ+/1077FPH9ev3sCClVW4i4jhgC2A94DxgMPBLYNuy+pQkSYuhTebI9LUy58DsDXwMeBUgM6cAy5TYnyRJUqlzbt7IzIyIBIiIpUrsS5IkLa6KVm7KTG4uKVZLLR8R/wIcBpxdYn+SJGlxtMkThftaqZUb4E/ASzTm3fxbZrr+T5IklarM5GYV4EvAXcC5NBIdSZLULio6LFXahOLM/DawLo1tFw4BHo2I70bEOmX1KUmSVOoTg7PxEJ1ni2MOsAJwaUR8v8x+JUlSD2T2/dEGynzOzZeBg4CZwM+Br2XmmxExAHgU+HpZfUuSpB6o6LBUmXNuVgQ+nplPNjdmZldE7FFiv5IkqcZKS24y87hFvPdgWf1KkqQeqmjlxl26JUlSpZQ5LCVJktqZD/GTJElVkl3tsbqprzksJUmSKsXkRpKkuurq6vujGxGxekRcHxEPRMT9xaNjiIjjI2JyRPy9OHZruucbETEpIh6OiI9014fDUpIkqT/NAY7OzLsiYhngzoiYt/fkjzLz1OaLI2IDYD9gQ+DdwJ8i4r2ZOXdhHZjcSJJUVy2YUJyZU4GpxfnLEfEgsOoibtkTuDgzZwOPR8QkYCvgloXd4LCUJEnqMxExNiImNh1jF3HtmsCmwG1F0xcj4p6IODciVijaVgWebrrtGRadDJncSJJUW13Z50dmjsvMLZqOcQvqOiKWBi4DvpKZLwFnAesAo2hUdk7r7bflsJQkSXXVoicUR8RgGonNRZl5OUBmTmt6/2zgd8XLycDqTbevVrQtlJUbSZLUbyIigHOABzPzh03tI5su2xu4rzi/CtgvIoZExFrAusDti+rDyo0kSXXVmsrNtsCBwL0R8fei7ZvA/hExCkjgCeCzAJl5f0RcAjxAY6XVEYtaKQUmN5IkqR9l5l+BWMBb1y7inpOBk3vah8mNJEl1ldXcfsHkRpKkumrRhOKyOaFYkiRVipUbSZLqyl3BJUmS2p+VG0mS6qoFe0v1B5MbSZLqymEpSZKk9mflRpKkmkqXgkuSJLU/KzeSJNWVc24kSZLan5UbSZLqyqXgkiSpUhyWkiRJan9WbiRJqiuXgkuSJLU/KzeSJNVVRefcmNxIklRXFV0t5bCUJEmqFCs3kiTVVUWHpazcSJKkSrFyI0lSTVV1V3CTG0mS6sphKUmSpPZn5UaSpLqyciNJktT+rNxIklRXPsRPkiSp/Vm5kSSprio658bkRpKkmsqKJjcOS0mSpEqxciNJUl1ZuZEkSWp/Vm4kSaor95aSJEmV4rCUJElS+7NyI0lSXVm5kSRJan9WbiRJqqnMalZuTG4kSaorh6UkSZLan5UbSZLqqqKVm7ZNbk6YekOrQ1DF7D/yA60OQRVy8dTbWh2CpIVo2+RGkiSVy13BJUmSOoCVG0mS6qqilRuTG0mS6qqa+2Y6LCVJkqrFyo0kSTXlhGJJkqQOYOVGkqS6qmjlxuRGkqS6ckKxJElS+7NyI0lSTTmhWJIkqQNYuZEkqa4qOufG5EaSpJpyWEqSJKkDmNxIklRXXSUc3YiI1SPi+oh4ICLuj4gvF+0rRsQfI+LR4usKRXtExOkRMSki7omIzbrrw+RGkiT1pznA0Zm5AbA1cEREbAAcC4zPzHWB8cVrgF2BdYtjLHBWdx2Y3EiSVFPZ1fdHt31mTs3Mu4rzl4EHgVWBPYELissuAPYqzvcELsyGW4HlI2LkovpwQrEkSXXV4tVSEbEmsClwGzAiM6cWbz0LjCjOVwWebrrtmaJtKgth5UaSJPWZiBgbERObjrELuW5p4DLgK5n5UvN7mZlAr5dyWbmRJKmmejKMtNifmTkOGLeoayJiMI3E5qLMvLxonhYRIzNzajHsNL1onwys3nT7akXbQlm5kSRJ/SYiAjgHeDAzf9j01lXAwcX5wcCVTe0HFaumtgZmNQ1fLZCVG0mS6qo1c262BQ4E7o2Ivxdt3wROAS6JiMOBJ4F9i/euBXYDJgGvAYd214HJjSRJ6jeZ+VcgFvL2Tgu4PoEjFqcPkxtJkmqqjDk37cDkRpKkmqpqcuOEYkmSVClWbiRJqikrN5IkSR3Ayo0kSXWVC1u01NlMbiRJqimHpSRJkjqAlRtJkmoqu6o5LGXlRpIkVYqVG0mSaqqqc25MbiRJqqms6Goph6UkSVKlWLmRJKmmqjosZeVGkiRVipUbSZJqyqXgkiRJHcDKjSRJNZXZ6gjKYXIjSVJNOSwlSZLUAazcSJJUU1ZuJEmSOoCVG0mSasoJxZIkqVIclpIkSeoAVm4kSaopdwWXJEnqAFZuJEmqqaruCm5yI0lSTXU5LCVJktT+rNxIklRTTiiWJEnqAFZuJEmqKR/iJ0mS1AGs3EiSVFPuLSVJkirFYSlJkqQOYOVGkqSaqupD/BaZ3ETEy8C8Ebl5fwJZnGdmLltibJIkSYttkclNZi7TX4FIkqT+VfuH+EXE6Ig4tDgfHhFrlReWJEkqW2bfH+2gR8lNRBwHHAN8o2haAvhlWUFJkiT1Vk8nFO8NbArcBZCZUyLCIStJkjpYVScU93RY6o3MTIrJxRGxVHkhSZIk9V5PKzeXRMTPgOUj4l+Aw4CzywtLPXH2uNPYfbcPM33GTEZtulOrw1EHetfa7+aLPzn6rder/NMILvvhxTx4630cevLnGDxkMHPnzuWCb4/jsbsntTBSdaIhQ4Zw/Z8vY8iQIQwcNJDLL7+GE088rdVhqUlVJxRH9nD2T0TsDIwpXv4hM/9YWlTAoCVWbZNpSe1ru9Ef4JVXXuW88/7D5KYH9h/5gVaH0NZiwABOv+1sjt/rWA4/5fNcd87V3HPD39jkQ5ux+2f34rv7/VurQ2wrF0+9rdUhdISlllqSV199jUGDBvGXG67gqKOO47bb72p1WG3rzTcm92u2cdfqe/b579rNnr6y5RnT4jzE715gGI2hqXvLCUeLY8Jfb2ONNVZrdRiqiA23fT/Tn5rGc5NnkJkMW3pJAIYtsyQvTH++xdGpU7366msADB48iMGDB9PTf1BL70RPV0t9Brgd+DjwCeDWiDisB/ctFxE/ioiJxXFaRCz3zkKWVIatPzaaW66aAMBFJ57Lft88iB/fMo79v3Uwl3zvohZHp041YMAAJt7xB6ZMvoc/jb+R2+/4W6tDUpOujD4/2kFPJxR/Ddg0Mw/JzIOBzWksDe/OucBLwL7F8RJw3sIujoix8xKhrq5XexiapHdq4OBBbPbhLbn9mpsB2OnTu3DRd87jK9uM5aITz+Mz3/9CiyNUp+rq6mKLLcew5lpbsOUWm7Lhhuu1OiTVQE+Tm+eAl5tev1y0dWedzDwuMx8rjhOAtRd2cWaOy8wtMnOLAQNckCX1l0122JQn7nuMl2bOAmD0Pjsw8b9vBeD2a25mnU3WbWV4qoBZs17ihr/cxJgxO7Q6FDXJjD4/2sEik5uIOCoijgImAbdFxPHFA/1uBR7pwee/HhGjmz5vW+D1dxKwpL63zce245ar/vrW6xemv8D7tt4QgA22fT/PPjG1VaGpgw0fviLLLdfYgnDo0KF8eKftefjhf7Q4KtVBdxOK5z2o7x/FMc+VPfz8zwMXNM2zeQE4uOfhaVF++Ysz+Oftt2H48BV54rGJnHDiqZx3/sWtDksdZsiwIWy43Sac+82fvtV27jFn8unjD2fgwIG8OfsNzj32rBZGqE41cuQIzj3nxwwcOIAYMIBLL72aa6/9U6vDUpN2mSPT13q8FLxXHx4xhMYE5HWA5YFZNHYTP7G7e10Krr7mUnD1JZeCqwz9vRT81nd/vM9/12495fKWZ0w9WgoeESsDXwc2BIbOa8/MHbu59UrgRRrbNkzuZYySJEk91tPn3FwE/BrYA/gcjaGlGT24b7XM3KWXsUmSpBJVdViqp6ulVsrMc4A3M/MvmXkY0F3VBuDmiHh/78OTJElaPD2t3LxZfJ0aEbsDU4AVe3DfaOCQiHgcmA0EjTk3Gy92pJIkqU+1y9LtvtbT5OakYsXT0cB/AssCX+nBfbv2NjBJklSurlYHUJIeJTeZ+bvidBbwIYCI6Da5ycwnex+aJEnS4uvpnJsFOarPopAkSf0uiT4/uhMR50bE9Ii4r6nt+IiYHBF/L47dmt77RkRMioiHI+IjPfm+3klyU82BOkmSVKbzgQWtpP5RZo4qjmsBImIDYD8aj6LZBTgzIgZ218E7SW58yJ4kSR2sK/v+6E5m3gg838MQ9wQuzszZmfk4je2gturupkXOuYmIl1lwEhPAsB4GJkmS2lBXew3CfDEiDgImAkdn5gvAqjT2s5znmaJtkRZZucnMZTJz2QUcy2RmT1daSZKkmoiIsRExsekY24PbzqKxVdMoYCpw2juJwQRFkqSa6skE4MX+zMxxwLjFvGfavPOIOBuYt0p7MrB606Wr0YPtnN7JnBtJkqR3LCJGNr3cG5i3kuoqYL+IGBIRawHrArd393lWbiRJqqlWPMQvIn4F7AAMj4hngOOAHSJiFI15vk8AnwXIzPsj4hLgAWAOcERmzu2uD5MbSZLUbzJz/wU0n7OI608GTl6cPkxuJEmqqTLm3LQDkxtJkmqqqntLOaFYkiRVipUbSZJqysqNJElSB7ByI0lSTTmhWJIkVUpXNXMbh6UkSVK1WLmRJKmm2mxX8D5j5UaSJFWKlRtJkmoqWx1ASUxuJEmqKZ9zI0mS1AGs3EiSVFNd4YRiSZKktmflRpKkmqrqhGIrN5IkqVKs3EiSVFNVXS1lciNJUk25t5QkSVIHsHIjSVJNubeUJElSB7ByI0lSTVV1KbjJjSRJNeWEYkmSpA5g5UaSpJqq6nNurNxIkqRKsXIjSVJNOaFYkiRVihOKJUmSOoCVG0mSasoJxZIkSR3Ayo0kSTVl5UaSJKkDWLmRJKmmsqKrpUxuJEmqKYelJEmSOoCVG0mSasrKjSRJUgewciNJUk25t5QkSaoU95aSJEnqAFZuJEmqKScUS5IkdQArN5Ik1VRVKzcmN5Ik1VRVV0s5LCVJkirFyo0kSTXlUnBJkqQOYOVGkqSaquqEYis3kiSpUqzcSJJUU1VdLdW2yc2AqOgsJ7XMpdPvbHUIqpDXpkxodQjSO9ZV0fTGYSlJklQpbVu5kSRJ5XJCsSRJUgewciNJUk1Vc8aNyY0kSbXlsJQkSVIHMLmRJKmmuqLvj+5ExLkRMT0i7mtqWzEi/hgRjxZfVyjaIyJOj4hJEXFPRGzWk+/L5EaSJPWn84Fd5ms7FhifmesC44vXALsC6xbHWOCsnnRgciNJUk11kX1+dCczbwSen695T+CC4vwCYK+m9guz4VZg+YgY2V0fJjeSJNVUlnD00ojMnFqcPwuMKM5XBZ5uuu6Zom2RTG4kSVKfiYixETGx6Ri7OPdn5jvMk1wKLklSbZWxFDwzxwHjFvO2aRExMjOnFsNO04v2ycDqTdetVrQtkpUbSZLUalcBBxfnBwNXNrUfVKya2hqY1TR8tVBWbiRJqqlW7AoeEb8CdgCGR8QzwHHAKcAlEXE48CSwb3H5tcBuwCTgNeDQnvRhciNJUk21YvuFzNx/IW/ttIBrEzhicftwWEqSJFWKlRtJkmrKvaUkSZI6gJUbSZJqqhUTivuDlRtJklQpVm4kSaqpatZtTG4kSaotJxRLkiR1ACs3kiTVVFZ0YMrKjSRJqhQrN5Ik1VRV59yY3EiSVFM+50aSJKkDWLmRJKmmqlm3sXIjSZIqxsqNJEk1VdU5NyY3kiTVVFVXSzksJUmSKsXKjSRJNeUTiiVJkjqAlRtJkmrKOTeSJEkdwMqNJEk1VdU5NyY3kiTVlMNSkiRJHcDKjSRJNdWV1RyWsnIjSZIqxcqNJEk1Vc26jcmNJEm1VdWNMx2WkiRJlWLlRpKkmqrqc26s3EiSpEqxciNJUk1V9SF+JjeSJNWUE4olSZI6gJUbSZJqygnFkiRJHcDKjSRJNVXVCcVWbiRJUqVYuZEkqaayoruCm9xIklRTLgWXJEnqAFZuJEmqKScUS5IkdQArN5Ik1VRVH+JnciNJUk05oViSJKkDWLmRJKmmqvqcGys3kiSpUqzcSJJUU1VdCm5yI0lSTVV1tZTDUpIkqVKs3EiSVFNVXQpuctPhBgwYwK23XMvkKc+y996HtDocdbgjjzycQw7Zj8zk/vsfYuzYrzF79uxWh6U2Nnv2Gxx8xNd44803mTtnLjt/aDRf/MyBHHP897j/oUcZNGgQG23wXo77+pcYPGgQmcm///inTLjlDoYOHcLJ3zqaDdZ7T6u/DVWMw1Id7sgjD+ehhya1OgxVwLvfPYIvfOFQtt12D7bYYgwDBw7kk5/8aKvDUptbYonBnHv6KVx+wZlcesEZ3HTbndx934PsPuZDXP2rs7niF2cxe/YbXHb1dQBMuOUOnnpmCtf++hyO//qX+M6pP2nxd1BvmdnnRzswuelgq646kl133Ylzz/uvVoeiihg0aCDDhg1l4MCBDBs2jKlTp7U6JLW5iGDJJYcBMGfOHObMmUNEsP0HtyIiiAjev/56TJs+E4Dr/3orH9tlJyKCTTZan5dffoUZM59v5begCip1WCoihgD7AGs295WZJ5bZb12cdurxfOMbJ7PMMku3OhRVwJQp0/jxj8fxyCO38Prr/8P48RMYP35Cq8NSB5g7dy77HvYlnpo8hf0/vgcbb/i+t957c84crv79eI798ucAmDbjOd61yvC33h+xynCmzZjJysNX7Pe4Vd05N2VXbq4E9gTmAK82HQsUEWMjYmJETOyau9DLBOy2205MnzGTv/3t3laHoopYfvll2WOPMay//mjWXnsrllpqGPvtt3erw1IHGDhwIJddcAbjr/gF9z7wCI8+9sRb75106hlsvslGbD5qo9YFqIXKEv5rB2VPKF4tM3fp6cWZOQ4YB7DEkNXa40+oTX1wmy3ZY/cx7PKRHRk6dAjLLrsM5593Oocc+qVWh6YOteOOo3niiaeZWQwR/Pa317H11ptz8cVXtDgydYpll1marTbbmL/eOpF1116TM8+9iBdenMVx3/32W9eMWHklni2GqACmTZ/JiJWHL+jjpF4ru3Jzc0S8v+Q+aunb/+8U1l5nS9673jZ8+sAjuP6Gm0xs9I48/fQUttpqU4YNGwrAhz60LQ8/7GR1LdrzL7zISy+/AsD/zJ7NLXf8jbXWWJ1Lr7qOm267k++fcAwDBvzvr5odRm/NVdeNJzO5+74HWXrppRySaqGuzD4/eiIinoiIeyPi7xExsWhbMSL+GBGPFl9X6O33VXblZjRwSEQ8DswGAsjM3LjkfiUtpjvu+DtXXHEtt9xyDXPmzOXuu+/nnHOcrK5Fm/HcC3zrpFOZ29VFdiUf2XE7dtj2A2yy/e6MHLEKB4w9CoAP//MH+fxhB7D9Nlsy4ZY72HXfwxg2dCjf+ea/tvg7UAt9KDNnNr0+FhifmadExLHF62N688FR5rKtiFhjQe2Z+WR39zospb42cMDAVoegCnnp6etbHYIqaPDwtaM/+9tu1Z36/HfthMnju/0eIuIJYIvm5CYiHgZ2yMypETESuCEz1+tNDGVXbj4D3AjcnJnOEJYkqY20cLVUAn+IiAR+Vsy5HZGZU4v3nwVG9PbDy05uHgP2B06PiJeBCcCNmXllyf1KkqQWiIixwNimpnFF8tJsdGZOjohVgD9GxEPNb2ZmFolPr5Sa3GTmecB5EfEuYF/gqzS+4WXK7FeSJHWvjMpN88rnRVwzufg6PSKuALYCpkXEyKZhqem9jaHU1VIR8fOIuBk4i0Yi9Qmg17OfJUlSZ4uIpSJimXnnwBjgPuAq4ODisoNpPCuvV8oelloJGAi8CDwPzMzMOSX3KUmSeqBFe0GNAK6ICGjkIf+VmddFxB3AJRFxOPAkjRGfXil7WGpvgIhYH/gIcH1EDMzM1crsV5Ikda8VE4oz8zFgkwW0Pwfs1Bd9lL231B7AdsD2wPLAn2lMKpYkSSpF2cNSu9BIZv4jM6eU3JckSVoM7bIXVF8re1jqixExAtgyIjYDbs/MXs9+liRJ6k7Zq6U+CdwOfJLGxKDbIuITZfYpSZJ6JjP7/GgHZQ9LfRvYcl61JiJWBv4EXFpyv5IkqabKTm4GzDcM9Rzl70QuSZJ6oIXbL5Sq7OTmuoj4PfCr4vWngGtL7lOSJPVAuwwj9bWyJxR/LSL2AbYtmsZl5hVl9ilJkuqt7MoNmXkZcFnZ/UiSpMVT1WGpsldLfTwiHo2IWRHxUkS8HBEvldmnJEmqt7IrN98HPpqZD5bcjyRJWkw+xK93ppnYSJLUnrqcUNwrEyPi18BvgdnzGjPz8pL7lSRJNdLEPwMAAAg/SURBVFV2crMs8BowpqktAZMbSZJazGGp3jk6M59vboiItUruU5Ik1VjZTwu+OiKWnfciItYHri65T0mS1ANdmX1+tIOyk5vv0khwlo6IzWnsKfXpkvuUJEk9kCX81w7KfkLxNRExGPgDsAywd2Y+UmafkiSp3kpJbiLiP+Ft6dtywD+AL0YEmfmlMvqVJEk91y7DSH2trMrNxPle31lSP5IkSW9TSnKTmReU8bmSJKnvtMscmb5W6pybiNgWOB5Yo+grgMzMtcvsV5Ik1VfZz7k5B/hXGsNSc0vuS5IkLQbn3PTOrMz875L7kCRJveCwVO9cHxE/oLHdQvPeUneV3K8kSaqpspObDxRfNy++Bo0l4juW3K8kSepGZlerQyhF2cnNDQtoq2YNTJIktYWyk5tXms6HAnsAD5bcpyRJ6oGuitYbyt5+4bTm1xFxKvD7MvuUJEk9kxVdLVX2xpnzWxJYrZ/7lCRJNVL2Q/zu5X/n2AwEVgZOLLNPSZLUMw5L9c4eTedzgGmZOafkPiVJUo2VPefmyTI/X5Ik9V5V59yUXbmRJEltqqrbL/T3hGJJkqRSWbmRJKmmqrq3lJUbSZJUKVZuJEmqqapOKLZyI0mSKsXKjSRJNeVD/CRJUqU4LCVJktQBrNxIklRTPsRPkiSpA1i5kSSppqo658bkRpKkmqrqaimHpSRJUqVYuZEkqaaqOixl5UaSJFWKlRtJkmqqqkvBTW4kSaqpdEKxJElS+7NyI0lSTVV1WMrKjSRJqhQrN5Ik1ZRLwSVJkjqAlRtJkmqqqqulTG4kSaoph6UkSZI6gJUbSZJqysqNJElSB7ByI0lSTVWzbgNR1ZJUnUTE2Mwc1+o4VA3+PKmv+TOl/uawVDWMbXUAqhR/ntTX/JlSvzK5kSRJlWJyI0mSKsXkphocy1Zf8udJfc2fKfUrJxRLkqRKsXIjSZIqxeRGktQrEXFIRPyk1XFI8zO5kSRJlWJy0+Yi4sSI+ErT65Mj4ssR8bWIuCMi7omIE4r3loqIayLi7oi4LyI+1brI1QkiYs2IeDAizo6I+yPiDxExLCJGRcStxc/XFRGxQqtjVfmKn4f7ml5/NSKOj4gbIuJ7EXF7RDwSEdst4N7dI+KWiBgeEedHxOkRcXNEPBYRnyiuiYj4QfH3073z/o6KiDMi4mPF+RURcW5xfljxd94Cf077509Fncjkpv2dCxwEEBEDgP2AZ4F1ga2AUcDmEbE9sAswJTM3ycyNgOtaE7I6zLrAGZm5IfAisA9wIXBMZm4M3Asc18L41B4GZeZWwFeY7+chIvYGjgV2y8yZRfNIYDSwB3BK0fZxGn9nbQJ8GPhBRIwEJgDzEqZVgQ2K8+2AG4vzBf2cSgtkctPmMvMJ4LmI2BQYA/wN2LLp/C7gfTT+x78X2Ln4F9Z2mTmrNVGrwzyemX8vzu8E1gGWz8y/FG0XANu3JDK1k8uLr3cCaza17wgcA+yemS80tf82M7sy8wFgRNE2GvhVZs7NzGnAX2j8fTYB2C4iNgAeAKYVSc82wM3FvfP/nDbHIL2NG2d2hp8DhwDvolHJ2Qn498z82fwXRsRmwG7ASRExPjNP7M9A1ZFmN53PBZZvVSBquTm8/R+9Q5vO5/2czOXtvzv+AawNvBeYuIDrAWJRnWbm5IhYnkb1+UZgRWBf4JXMfDkiVuL//pw6LKWFsnLTGa6g8T/9lsDvi+OwiFgaICJWjYhVIuLdwGuZ+UvgB8BmrQpYHW0W8ELTvIoDafwLW9U3DVglIlaKiCE0hpS68yTFUGZEbNjNtROAT0XEwIhYmUZF8PbivVtpDHndWFz31eKrtNis3HSAzHwjIq4HXszMucAfImJ94JaIAHgF+DTwHhpj2F3Am8DnWxWzOt7BwE8jYkngMeDQFsejfpCZb0bEiTQSjsnAQz2876GIOAD4TUR8dBGXXkFjqOluIIGvZ+azxXsTgDGZOSkinqRRvTG5Ua/4hOIOUEwkvgv4ZGY+2up4JElqZw5Ltbligt0kYLyJjSRJ3bNyI0mSKsXKjSRJqhSTG0mSVCkmN5IkqVJMbqQOFBFzI+LvxR49vymWbPf2s85v2vvn58Uk9oVdu0NEfLDp9eci4qDe9i1JZTC5kTrT65k5qthD7A3gc81vRkSvnmGVmZ8pHpe/MDsAbyU3mfnTzLywN31JUllMbqTONwF4T1FVmRARVwEPFE+B/UHT7vGfhbd2Zv5JRDwcEX8CVpn3QcXuz1sU57tExF3FLvPjI2JNGknUvxZVo+2KHaO/Wly/wJ3Ee7KjtCT1JZ9QLHWwokKzK/+7A/xmwEaZ+XhEjAVmZeaWxaP0b4qIPwCbAuvR2Hl5BI2NCs+d73NXBs4Gti8+a8XMfD4ifkpjv59Ti+t2arrtQuDIzPxL8ZTb42g8Th+KHaUjYrei/cN9/WchSfOY3EidaVhEzNsheQJwDo3hotsz8/GifQyw8bz5NMByNHaP355iZ2ZgSkT8eQGfvzVw47zPysznFxVMRCzH/91J/DdNlyxsR2lJ6nMmN1Jnej0zRzU3FPuMvdrcRKOS8vv5rtut/PD+j4XtKC1Jfc45N1J1/R74fEQMBoiI90bEUjR2XZ63M/NI4EMLuPdWYPuIWKu4d8Wi/WVgmfkvzkx3EpfUNvwXlFRdP6cxBHRXNMo6M4C9aOzMvCONuTZPAbfMf2Nmzijm7FxebNw6HdgZuBq4NCL2BI6c7zZ3EpfUFtxbSpIkVYrDUpIkqVJMbiRJUqWY3EiSpEoxuZEkSZViciNJkirF5EaSJFWKyY0kSaoUkxtJklQp/x8iVb+UZ6psWgAAAABJRU5ErkJggg==", + "text/plain": [ + "
" + ] + }, + "metadata": { + "needs_background": "light", + "tags": [] + }, + "output_type": "display_data" + } + ], + "source": [ + "confusion_mtx = tf.math.confusion_matrix(y_true, y_pred) \n", + "plt.figure(figsize=(10, 8))\n", + "sns.heatmap(confusion_mtx, xticklabels=commands, yticklabels=commands, \n", + " annot=True, fmt='g')\n", + "plt.xlabel('Prediction')\n", + "plt.ylabel('Label')\n", + "plt.show()" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "mQGi_mzPcLvl" + }, + "source": [ + "## Run inference on an audio file\n", + "\n", + "Finally, verify the model's prediction output using an input audio file of someone saying \"yes.\" How well does your model perform?" + ] + }, + { + "cell_type": "code", + "execution_count": 141, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/", + "height": 333 + }, + "id": "zRxauKMdhofU", + "outputId": "de931674-31e5-42af-c7f2-30420aad5dbe" + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "3\n", + "tf.Tensor([0.57611686 0.21194157 0.21194157], shape=(3,), dtype=float32)\n", + "tf.Tensor([[0.57611686 0.21194157 0.21194157]], shape=(1, 3), dtype=float32)\n" + ] + }, + { + "data": { + "image/png": "iVBORw0KGgoAAAANSUhEUgAAAXQAAAEICAYAAABPgw/pAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAALEgAACxIB0t1+/AAAADh0RVh0U29mdHdhcmUAbWF0cGxvdGxpYiB2ZXJzaW9uMy4yLjIsIGh0dHA6Ly9tYXRwbG90bGliLm9yZy+WH4yJAAAT+klEQVR4nO3dfbRddX3n8feHRNABZxjl2oEkEEZia3Qs2Gu0q6AMxU6QAq0PFZa2plqy6DSrdGzV2AfGQW2xzGpnzZgZxQ5LqqNIHXUyQ5zU+gSoQC4IYsDINYJJFAyPFR+AwHf+ODvO4XJvzklykpv88n6tdVb2w2/v/b3n4XN/97fP3klVIUna/x002wVIkkbDQJekRhjoktQIA12SGmGgS1IjDHRJaoSBrlmV5INJ3tVNn5Rkwy7u531J/my01Q085q8n2ZTkoSQn7M1jS9Mx0DVQkjuS/LgLrru7ED5s1Mepqqur6meHqGdZkmumbHteVb1z1DUN8B+BFVV1WFV9dXd3luQLSU5O8o4k79j98mY8zrLuNVyY5I49dRztfQa6hnVGVR0GvBAYB/50aoMkc/d6VbPrGGD9rmyYZM6Ia5EMdO2cqtoCfBp4PkCSSvJ7SW4Hbu+W/WqSm5I8kOTLSV6wffskJyS5MckPknwMeGrfupOTbO6bX5DkE0m2Jrk3yXuTPBd4H/CL3V8MD3Rtfzp0082fm2QyyX1JVic5qm9dJTkvye1djauSpFt3XJIvJnkwyT1djU+Q5JAkDwFzgJuTfKtb/tyul/1AkvVJzuzb5oNJ/luSNUl+CPzrYZ7vJF9Pckbf/FO6uk7o5l/SPccPJLk5ycl9bZcl2dg9199O8rphjqn9WFX58LHDB3AHcGo3vYBer/Sd3XwBnwGeATwNOAH4PvBieoH3hm77Q4CDgTuBfwc8BXg18Cjwrm5fJwObu+k5wM3AXwOH0gv+E7t1y4BrptT4wb79nALcQ++viUOA/wJc1de2gP8DHA4cDWwFlnbrPgr8Cb3Ozk+POcPzUsBx3fRTgEngj7uf8xTgB8DP9tX3IPBL2/c95HP/VuBjffNnAbd00/OAe4FXdPt8eTc/1j1n/9h3/COB5832e8nHnn3YQ9ewPtX1hq8Bvgj8ed+6v6iq+6rqx8By4P1VdV1VPVZVlwEPAy/pHk8B/lNVPVpVHwfWzXC8JcBRwFuq6odV9ZOqumaGtlO9Dri0qm6sqoeBt9Pr0S/sa3NRVT1QVd8BPg8c3y1/lN5QylE7ecyXAId1+32kqj5H75fGOX1t/ldVfamqHq+qnwy53w8Dr0jyT7v53wQ+1E2/HlhTVWu6fX4GmKAX8ACPA89P8rSq+l5V7dLwkPYfBrqG9WtVdXhVHVNV/7YL7+029U0fA/xhNwTwQPdLYAG9cD4K2FJV/XeEu3OG4y0A7qyqbbtQ61H9+62qh+j1XOf1tbmrb/pH9MIYej3iANd3wyZv3Iljbqqqx/uW3TnlmJvYSVX1XeBLwKuSHA6cBvyPbvUxwGumPNcnAkdW1Q+B1wLnAd9LcmWSn9vZ42v/cqCdxNKe0R/Qm4B3V9W7pzZK8jJgXpL0hfrRwLem2ecm4Ogkc6cJ9UG3CP0uvbDbftxDgWcCWwZsR1XdBZzbbXci8A9JrqqqySGOuSDJQX2hfjTwzZ2oeyaXAb9D7/P6leqdx4Dec/Shqjp3uo2qai2wNsnTgHcBHwBO2sUatB+wh65R+wBwXpIXp+fQJKcneTrwFWAb8Pvdyb1X0htamc71wPeAi7p9PDXJL3Xr7gbmJzl4hm0/Cvx2kuOTHEJveOi6qrpjUPFJXpNkfjd7P70QfnwHm2x3Hb2e/lu7n+1k4Azg8iG2HeRT9M4HnA/8bd/yDwNnJPk3SeZ0z9HJSeYn+ZkkZ3W/zB4GHhry59B+zEDXSFXVBL0e7nvpBeIkvZOYVNUjwCu7+fvoDQl8Yob9PEYvEI8DvgNs7toDfI7eidm7ktwzzbb/APwZ8D/p/VJ4NnD2kD/Ci4Drum+xrAbOr6qNgzbqfrYz6A2J3AP8V+C3quobQx53R/v+Mb2f5Vj6nq+q2kTvJOkf0zuxuwl4C73P9UHAm+n95XAf8DLgd3e3Fu3b8sThTEn7oiQXAM+pqtfPdi3adzmGLu3jkjwDeBO9b7hIM3LIRdqHJTmX3lDKp6vqqtmuR/s2h1wkqRFD9dCTLE2yobuUeuUMbX4jya3dd3c/MtoyJUmDDOyhp3cToW/Su6x4M70r+86pqlv72iwCrgBOqar7kzyrqr6/o/0eccQRtXDhwt0sX5IOLDfccMM9VTU23bphToouASa3f3UryeX0vip1a1+bc4FVVXU/wKAwB1i4cCETExNDHF6StF2Sma6uHmrIZR5PvGR5M0+8nBngOcBzknwpybVJls5QyPIkE0kmtm7dOsShJUnDGtW3XOYCi+jdLe8c4APdfSeeoKouqarxqhofG5v2LwZJ0i4aJtC30LtR0nbzefI9MTYDq7s76H2b3pj7otGUKEkaxjCBvg5YlOTY7t4ZZ9O7JLrfp+j1zklyBL0hmIGXS0uSRmdgoHd3ulsBrAVuA66oqvVJLuz7H1nWAvcmuZXevaXfUlX37qmiJUlPNmsXFo2Pj5ffcpGknZPkhqoan26dl/5LUiMMdElqhIEuSY3YL2+fu3DllbNdQrPuuOj02S5B0i6yhy5JjTDQJakRBrokNcJAl6RGGOiS1AgDXZIaYaBLUiMMdElqhIEuSY0w0CWpEQa6JDXCQJekRhjoktQIA12SGmGgS1IjDHRJaoSBLkmNMNAlqREGuiQ1wkCXpEYY6JLUCANdkhphoEtSI4YK9CRLk2xIMplk5TTrlyXZmuSm7vE7oy9VkrQjcwc1SDIHWAW8HNgMrEuyuqpundL0Y1W1Yg/UKEkawjA99CXAZFVtrKpHgMuBs/ZsWZKknTVMoM8DNvXNb+6WTfWqJF9L8vEkC6bbUZLlSSaSTGzdunUXypUkzWRUJ0X/N7Cwql4AfAa4bLpGVXVJVY1X1fjY2NiIDi1JguECfQvQ3+Oe3y37qaq6t6oe7mb/BviF0ZQnSRrWMIG+DliU5NgkBwNnA6v7GyQ5sm/2TOC20ZUoSRrGwG+5VNW2JCuAtcAc4NKqWp/kQmCiqlYDv5/kTGAbcB+wbA/WLEmaxsBAB6iqNcCaKcsu6Jt+O/D20ZYmSdoZXikqSY0w0CWpEQa6JDXCQJekRhjoktQIA12SGmGgS1IjDHRJaoSBLkmNMNAlqREGuiQ1wkCXpEYY6JLUCANdkhphoEtSIwx0SWqEgS5JjTDQJakRBrokNcJAl6RGGOiS1AgDXZIaYaBLUiMMdElqhIEuSY0w0CWpEQa6JDViqEBPsjTJhiSTSVbuoN2rklSS8dGVKEkaxsBATzIHWAWcBiwGzkmyeJp2TwfOB64bdZGSpMGG6aEvASaramNVPQJcDpw1Tbt3Au8BfjLC+iRJQxom0OcBm/rmN3fLfirJC4EFVXXljnaUZHmSiSQTW7du3eliJUkz2+2TokkOAv4K+MNBbavqkqoar6rxsbGx3T20JKnPMIG+BVjQNz+/W7bd04HnA19IcgfwEmC1J0Ylae8aJtDXAYuSHJvkYOBsYPX2lVX1YFUdUVULq2ohcC1wZlVN7JGKJUnTGhjoVbUNWAGsBW4Drqiq9UkuTHLmni5QkjScucM0qqo1wJopyy6Yoe3Ju1+WJGlneaWoJDXCQJekRhjoktQIA12SGmGgS1IjDHRJaoSBLkmNMNAlqREGuiQ1wkCXpEYY6JLUCANdkhphoEtSIwx0SWqEgS5JjTDQJakRBrokNcJAl6RGGOiS1AgDXZIaYaBLUiMMdElqhIEuSY0w0CWpEQa6JDXCQJekRhjoktSIoQI9ydIkG5JMJlk5zfrzktyS5KYk1yRZPPpSJUk7MjDQk8wBVgGnAYuBc6YJ7I9U1b+qquOBvwT+auSVSpJ2aJge+hJgsqo2VtUjwOXAWf0Nquof+2YPBWp0JUqShjF3iDbzgE1985uBF09tlOT3gDcDBwOnTLejJMuB5QBHH330ztYqSdqBkZ0UrapVVfVs4G3An87Q5pKqGq+q8bGxsVEdWpLEcIG+BVjQNz+/WzaTy4Ff252iJEk7b5hAXwcsSnJskoOBs4HV/Q2SLOqbPR24fXQlSpKGMXAMvaq2JVkBrAXmAJdW1fokFwITVbUaWJHkVOBR4H7gDXuyaEnSkw1zUpSqWgOsmbLsgr7p80dclyRpJ3mlqCQ1wkCXpEYY6JLUCANdkhphoEtSIwx0SWqEgS5JjTDQJakRBrokNcJAl6RGGOiS1AgDXZIaYaBLUiMMdElqhIEuSY0w0CWpEQa6JDXCQJekRhjoktQIA12SGmGgS1IjDHRJaoSBLkmNMNAlqREGuiQ1wkCXpEbMne0CdGBYuPLK2S6hWXdcdPoe2a+v2Z6zp16zoXroSZYm2ZBkMsnKada/OcmtSb6W5LNJjhl9qZKkHRkY6EnmAKuA04DFwDlJFk9p9lVgvKpeAHwc+MtRFypJ2rFheuhLgMmq2lhVjwCXA2f1N6iqz1fVj7rZa4H5oy1TkjTIMIE+D9jUN7+5WzaTNwGfnm5FkuVJJpJMbN26dfgqJUkDjfRbLkleD4wDF0+3vqouqarxqhofGxsb5aEl6YA3zLdctgAL+ubnd8ueIMmpwJ8AL6uqh0dTniRpWMP00NcBi5Icm+Rg4GxgdX+DJCcA7wfOrKrvj75MSdIgAwO9qrYBK4C1wG3AFVW1PsmFSc7sml0MHAb8XZKbkqyeYXeSpD1kqAuLqmoNsGbKsgv6pk8dcV2SpJ3kpf+S1AgDXZIaYaBLUiMMdElqhIEuSY0w0CWpEQa6JDXCQJekRhjoktQIA12SGmGgS1IjDHRJaoSBLkmNMNAlqREGuiQ1wkCXpEYY6JLUCANdkhphoEtSIwx0SWqEgS5JjTDQJakRBrokNcJAl6RGGOiS1AgDXZIaYaBLUiOGCvQkS5NsSDKZZOU061+a5MYk25K8evRlSpIGGRjoSeYAq4DTgMXAOUkWT2n2HWAZ8JFRFyhJGs7cIdosASaraiNAksuBs4Bbtzeoqju6dY/vgRolSUMYZshlHrCpb35zt2ynJVmeZCLJxNatW3dlF5KkGezVk6JVdUlVjVfV+NjY2N48tCQ1b5hA3wIs6Juf3y2TJO1Dhgn0dcCiJMcmORg4G1i9Z8uSJO2sgYFeVduAFcBa4Dbgiqpan+TCJGcCJHlRks3Aa4D3J1m/J4uWJD3ZMN9yoarWAGumLLugb3odvaEYSdIs8UpRSWqEgS5JjTDQJakRBrokNcJAl6RGGOiS1AgDXZIaYaBLUiMMdElqhIEuSY0w0CWpEQa6JDXCQJekRhjoktQIA12SGmGgS1IjDHRJaoSBLkmNMNAlqREGuiQ1wkCXpEYY6JLUCANdkhphoEtSIwx0SWqEgS5JjTDQJakRQwV6kqVJNiSZTLJymvWHJPlYt/66JAtHXagkaccGBnqSOcAq4DRgMXBOksVTmr0JuL+qjgP+GnjPqAuVJO3YMD30JcBkVW2sqkeAy4GzprQ5C7ism/448MtJMroyJUmDzB2izTxgU9/8ZuDFM7Wpqm1JHgSeCdzT3yjJcmB5N/tQkg27UvR+6AimPBf7qvi3FexHrxf4mnUOpNfsmJlWDBPoI1NVlwCX7M1j7guSTFTV+GzXoeH4eu1/fM16hhly2QIs6Juf3y2btk2SucA/A+4dRYGSpOEME+jrgEVJjk1yMHA2sHpKm9XAG7rpVwOfq6oaXZmSpEEGDrl0Y+IrgLXAHODSqlqf5EJgoqpWA/8d+FCSSeA+eqGv/++AG2baz/l67X98zYDYkZakNnilqCQ1wkCXpEYY6JL2WUmWJXnvbNexvzDQJakRBvpuSnJhkj/om393kvOTvCXJuiRfS/IfunWHJrkyyc1Jvp7ktbNXuQCSLExyW5IPJFmf5O+TPC3J8Umu7V6/Tyb557Ndawu65/vrffN/lOQdSb6Q5D1Jrk/yzSQnTbPt6Um+kuSIJB9M8p+TfDnJxiSv7tokycXd5+uW7Z+xJKuSnNlNfzLJpd30G7vP7LTvg73zrIyOgb77LgV+CyDJQfS+snkXsIjefXCOB34hyUuBpcB3q+rnq+r5wP+dnZI1xSJgVVU9D3gAeBXwt8DbquoFwC3Av5/F+g4Uc6tqCfAHTHm+k/w6sBJ4RVVtv8T/SOBE4FeBi7plr6T3mft54FTg4iRHAlcD239JzKN3o0G6ZVd109O9D/YrBvpuqqo7gHuTnAD8CvBV4EV90zcCP0fvzXIL8PKuJ3JSVT04O1Vrim9X1U3d9A3As4HDq+qL3bLLgJfOSmUHlk90/94ALOxbfgrwNuD0qrq/b/mnqurxqroV+Jlu2YnAR6vqsaq6G/givc/j1cBJ3Z1ibwXu7oL+F4Evd9tOfR/017Bf2Kv3cmnY3wDLgH9Br8f+y8BfVNX7pzZM8kLgFcC7kny2qi7cm4VqWg/3TT8GHD5bhRwAtvHEjuRT+6a3vw6P8cRs+hbwL4HnABPTtAfY4d1dq2pLksPp/ZV8FfAM4DeAh6rqB0meyZPfBw65HKA+Se+N8iJ6V9SuBd6Y5DCAJPOSPCvJUcCPqurDwMXAC2erYO3Qg8D9feO4v0mvp6fddzfwrCTPTHIIveGSQe6kGwZL8rwBba8GXptkTpIxen9ZXd+tu5becM5VXbs/6v5thj30EaiqR5J8Hnigqh4D/j7Jc4GvdLeFfwh4PXAcvTG9x4FHgd+drZo10BuA9yX5J8BG4LdnuZ4mVNWj3W1Drqd3U79vDLndN5K8Dvi7JGfsoOkn6Q2j3AwU8NaquqtbdzXwK1U1meROer30pgLdS/9HoDsZeiPwmqq6fbbrkXRgcshlN3UnWSaBzxrmkmaTPXRJaoQ9dElqhIEuSY0w0CWpEQa6JDXCQJekRvw/W6G8dwpGonMAAAAASUVORK5CYII=", + "text/plain": [ + "
" + ] + }, + "metadata": { + "needs_background": "light", + "tags": [] + }, + "output_type": "display_data" + } + ], + "source": [ + "sample_file = '/content/data/mini_speech_commands/yes/0132a06d_nohash_1.wav'\n", + "\n", + "sample_ds = preprocess_dataset([str(sample_file)])\n", + "for spectrogram, label in sample_ds.batch(1):\n", + " prediction = model(spectrogram)\n", + " print(len(commands))\n", + " print(tf.nn.softmax(prediction[0]))\n", + " print(tf.nn.softmax(prediction))\n", + " plt.bar(commands, tf.nn.softmax(prediction[0]))\n", + " plt.title(f'Predictions for \"{commands[label[0]]}\"')\n", + " plt.show()" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "VgWICqdqQNaQ" + }, + "source": [ + "You can see that your model very clearly recognized the audio command as \"yes.\"" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "NNPrzNrcDqq8" + }, + "source": [ + "## Run TF inference on multiple audio files" + ] + }, + { + "cell_type": "code", + "execution_count": 150, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/", + "height": 1000 + }, + "id": "GmyOg4kwDEVH", + "outputId": "b770a9ce-6797-42bc-8b2f-06523463c84a" + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "/content/data/mini_speech_commands/unknown/4c77947d_nohash_0.wav\n" + ] + }, + { + "data": { + "image/png": "iVBORw0KGgoAAAANSUhEUgAAAXQAAAEICAYAAABPgw/pAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAALEgAACxIB0t1+/AAAADh0RVh0U29mdHdhcmUAbWF0cGxvdGxpYiB2ZXJzaW9uMy4yLjIsIGh0dHA6Ly9tYXRwbG90bGliLm9yZy+WH4yJAAAUd0lEQVR4nO3df7RdZX3n8feHxGAFV7FytZIEQyE6RseCvUa7BixF7ApSwapUGDsVrWbsNFNsrRqnlnFSO0WZZWdNTSvYYUGdkUAd0XRIh7Yqv6pALhTUgMg1gkkoGH6O6AgEvvPH2ZeeXO7NPUlOuOTJ+7XWWdk/nr339+xz7ufs8+yzd1JVSJL2fvvNdgGSpOEw0CWpEQa6JDXCQJekRhjoktQIA12SGmGga7ckOT/JR7vhY5Lcuovr+VSSPxhudTNu81eSbEryUJKjnsptDypJJTlituvQ3mHubBegPS/J7cDzgceAHwJ/A6yoqoeGuZ2qugp48QD1nA68q6qO7lv2PcOsZUD/hd5++OIwVpbkcuAjwLEAVfWRYaz36SrJ+cDl3eixVXX6rBUjwCP0fckbqupA4BXAKPDhyQ2S7Gsf8C8ENuzKgknmDLkWabcZ6PuYqtpC7wj9ZfDEV/rfSnIbcFs37ZeT3JjkgSRfTfLyieWTHJXkhiQ/SHIR8My+eccm2dw3vjDJ55NsTXJvkk8meQnwKeDnu66OB7q2T3TddOPvTjKe5L4ka5Mc0jevkrwnyW1djauTpJt3RJIrkjyY5J6uxu0k2T/JQ8Ac4KYk3+mmvyTJ5d06NyQ5qW+Z85P8eZJ1SX4I/OIg+zvJ6UmunjTtiW6Ubr2rk1za7dNrkxw+zbqO7rqIjh1gP+yX5MNJ7kjy/SR/meQnu3kXJHlfNzx/4j3QjR/e7fP9Jl7PJO/r1vFPSd4xyPPWLKkqH40/gNuB47vhhfSOSv+wGy/g74CfAn4COAr4PvAqeoH39m75/YF5wB3A7wDPAN4CPAp8tFvXscDmbngOcBPwJ8AB9IL/6G7e6cDVk2o8v289xwH30Ps2sT/wp8CVfW0L+N/AQcChwFZgWTfvQuD36R2sPLHNafZLAUd0w88AxoH/0D3P44AfAC/uq+9B4F9NrHvAfT/Vc+3f7vnAvcBSel2g/xNYM7ktsAzYBCwdcD+8s3s+PwMcCHwe+EzfvL/uhv818B3gor55X+x7PbcBq7r983rgR8BzZvs97WPqh0fo+44vdEfDVwNXAP+5b94fV9V9VfX/gOXAOVV1bVU9VlUXAA8Dr+4ezwD+a1U9WlWfA9ZPs72lwCHA+6vqh1X146q6epq2k70NOK+qbqiqh4EP0TuiX9TX5qyqeqCqvgd8BTiym/4ova6UQ3Zym6+mF3xnVdUjVfVlemF5Wl+bL1bVP1TV41X14wHXO4hLquq6qtpGL9CPnDT/FOAc4ISqum7SvOn2w9uAT1TVxuqdK/kQcGrXrXYFcHSS/YDXAB+n90EF8Avd/AmPAqu613sd8BADnCfR7DDQ9x1vrKqDquqFVfXvuvCesKlv+IXA+7qv8A90HwIL6YXzIcCWquq/o9sd02xvIXBHF1I765D+9XaBdC8wv6/NXX3DP6IXxgAfAAJc13WbvHMntrmpqh7vm3bHpG1uYs+Y7rlMeC9wcVV9cyeW3W4fdsNzgedX1XfonRw/EjiG3gfXnUlezJMD/d5Jr+FU9elpwkAX9L66T9gE/FEX/hOPZ1XVhcA/AfMn+mk7h06zzk3AodOcaJ3pFp930vtgASDJAcBzgS0zPpGqu6rq3VV1CPBvgT8b8Gd/dwILu6PWCYdO2uau3Jr0h8CzJkaS/PQurOMU4I1JztiJZbbbh/Seyzbg7m78CnpdZvOqd17lCnrda88BbtyFGvU0YKBrsk8D70nyqvQckOTEJM8GvkYvFH47yTOSvIle18pUrqP3AXBWt45nJpn4Wn83sCDJvGmWvRB4R5Ijk+xPr3vo2qq6fabik5ySZEE3ej+9EH58B4tMuJbe0ecHuud2LPAGYM0Ay+7ITcBLu+fyTHo/a9xZdwKvBc5I8psDLnMh8DtJDktyIL19eFHf0fYVwArgym788m786qp6bBdq1NOAga7tVNUY8G7gk/QCcZzeiT2q6hHgTd34fcBb6Z1sm2o9j9ELxCOA7wGbu/YAX6Z3YvauJPdMsezfA38A/C96HwqHA6cO+BReCVzb/YplLXBGVW2caaHuub0BOIHeCdk/A369qr414HanW++36Z1U/Ht6vyIatE9/8nq+Ry/UVyZ51wCLnAd8hl5gfxf4MfDv++ZfATybfw70q+l9k7gS7bWyfXeoJGlv5RG6JDXCQJekRhjoktSIgQI9ybIkt3aXYq+cps2vJrm5++3vZ4dbpiRpJjOeFE3vJkTfBl5H75cK64HTqurmvjaLgYuB46rq/iTPq6rv72i9Bx98cC1atGg3y5ekfcv1119/T1WNTDVvkLvrLQXGJ376lWQNcDJwc1+bdwOrq+p+gJnCHGDRokWMjY0NsHlJ0oQk012dPVCXy3y2v+R5M9tfDg3wIuBFSf4hyTVJlk1TyPIkY0nGtm7dOsCmJUmDGtZJ0bnAYnp3ZzsN+HSSgyY3qqpzq2q0qkZHRqb8xiBJ2kWDBPoWejdamrCAJ99TYzOwtrsj23fp9bkvHk6JkqRBDBLo64HF3T0h5tG7BHvtpDZfoPtvt5IcTK8LZsbLrSVJwzNjoHc381kBXAbcQu82nhuSrOr7H10uA+5NcjO9ezK/v6ru3VNFS5KebNbu5TI6Olr+ykWSdk6S66tqdKp5XikqSY0w0CWpEQa6JDVikCtFJe2DFq28dLZLaNbtZ524R9brEbokNcJAl6RGGOiS1AgDXZIaYaBLUiMMdElqhIEuSY0w0CWpEQa6JDXCQJekRhjoktQIA12SGmGgS1IjDHRJaoSBLkmNMNAlqREGuiQ1wkCXpEYY6JLUCANdkhphoEtSIwx0SWqEgS5JjRgo0JMsS3JrkvEkK6eYf3qSrUlu7B7vGn6pkqQdmTtTgyRzgNXA64DNwPoka6vq5klNL6qqFXugRknSAAY5Ql8KjFfVxqp6BFgDnLxny5Ik7axBAn0+sKlvfHM3bbI3J/l6ks8lWTjVipIsTzKWZGzr1q27UK4kaTrDOin618Ciqno58HfABVM1qqpzq2q0qkZHRkaGtGlJEgwW6FuA/iPuBd20J1TVvVX1cDf6F8DPDac8SdKgBgn09cDiJIclmQecCqztb5DkBX2jJwG3DK9ESdIgZvyVS1VtS7ICuAyYA5xXVRuSrALGqmot8NtJTgK2AfcBp+/BmiVJU5gx0AGqah2wbtK0M/uGPwR8aLilSZJ2hleKSlIjDHRJaoSBLkmNMNAlqREGuiQ1wkCXpEYY6JLUCANdkhphoEtSIwx0SWqEgS5JjTDQJakRBrokNcJAl6RGGOiS1AgDXZIaYaBLUiMMdElqhIEuSY0w0CWpEQa6JDXCQJekRhjoktQIA12SGmGgS1IjDHRJaoSBLkmNGCjQkyxLcmuS8SQrd9DuzUkqyejwSpQkDWLGQE8yB1gNnAAsAU5LsmSKds8GzgCuHXaRkqSZDXKEvhQYr6qNVfUIsAY4eYp2fwh8DPjxEOuTJA1okECfD2zqG9/cTXtCklcAC6vq0h2tKMnyJGNJxrZu3brTxUqSprfbJ0WT7Ad8AnjfTG2r6tyqGq2q0ZGRkd3dtCSpzyCBvgVY2De+oJs24dnAy4DLk9wOvBpY64lRSXpqDRLo64HFSQ5LMg84FVg7MbOqHqyqg6tqUVUtAq4BTqqqsT1SsSRpSjMGelVtA1YAlwG3ABdX1YYkq5KctKcLlCQNZu4gjapqHbBu0rQzp2l77O6XJUnaWV4pKkmNMNAlqREGuiQ1wkCXpEYY6JLUCANdkhphoEtSIwx0SWqEgS5JjTDQJakRBrokNcJAl6RGGOiS1AgDXZIaYaBLUiMMdElqhIEuSY0w0CWpEQa6JDXCQJekRhjoktQIA12SGmGgS1IjDHRJaoSBLkmNMNAlqREGuiQ1YqBAT7Isya1JxpOsnGL+e5J8I8mNSa5OsmT4pUqSdmTGQE8yB1gNnAAsAU6bIrA/W1X/sqqOBD4OfGLolUqSdmiQI/SlwHhVbayqR4A1wMn9Darq//aNHgDU8EqUJA1i7gBt5gOb+sY3A6+a3CjJbwG/C8wDjptqRUmWA8sBDj300J2tVZK0A0M7KVpVq6vqcOCDwIenaXNuVY1W1ejIyMiwNi1JYrBA3wIs7Btf0E2bzhrgjbtTlCRp5w0S6OuBxUkOSzIPOBVY298gyeK+0ROB24ZXoiRpEDP2oVfVtiQrgMuAOcB5VbUhySpgrKrWAiuSHA88CtwPvH1PFi1JerJBTopSVeuAdZOmndk3fMaQ65Ik7SSvFJWkRhjoktQIA12SGmGgS1IjDHRJaoSBLkmNMNAlqREGuiQ1wkCXpEYY6JLUCANdkhphoEtSIwx0SWqEgS5JjTDQJakRBrokNcJAl6RGGOiS1AgDXZIaYaBLUiMMdElqhIEuSY0w0CWpEQa6JDXCQJekRhjoktSIubNdwK5YtPLS2S6hWbefdeIeWa+v2Z6zp14z7X0GOkJPsizJrUnGk6ycYv7vJrk5ydeTfCnJC4dfqiRpR2YM9CRzgNXACcAS4LQkSyY1+0dgtKpeDnwO+PiwC5Uk7dggR+hLgfGq2lhVjwBrgJP7G1TVV6rqR93oNcCC4ZYpSZrJIIE+H9jUN765mzad3wD+ZqoZSZYnGUsytnXr1sGrlCTNaKi/cknya8AocPZU86vq3KoararRkZGRYW5akvZ5g/zKZQuwsG98QTdtO0mOB34f+IWqeng45UmSBjXIEfp6YHGSw5LMA04F1vY3SHIUcA5wUlV9f/hlSpJmMmOgV9U2YAVwGXALcHFVbUiyKslJXbOzgQOBv0pyY5K106xOkrSHDHRhUVWtA9ZNmnZm3/DxQ65LkrSTvPRfkhphoEtSIwx0SWqEgS5JjTDQJakRBrokNcJAl6RGGOiS1AgDXZIaYaBLUiMMdElqhIEuSY0w0CWpEQa6JDXCQJekRhjoktQIA12SGmGgS1IjDHRJaoSBLkmNMNAlqREGuiQ1wkCXpEYY6JLUCANdkhphoEtSIwx0SWrEQIGeZFmSW5OMJ1k5xfzXJLkhybYkbxl+mZKkmcwY6EnmAKuBE4AlwGlJlkxq9j3gdOCzwy5QkjSYuQO0WQqMV9VGgCRrgJOBmycaVNXt3bzH90CNkqQBDNLlMh/Y1De+uZu205IsTzKWZGzr1q27sgpJ0jSe0pOiVXVuVY1W1ejIyMhTuWlJat4ggb4FWNg3vqCbJkl6Ghkk0NcDi5MclmQecCqwds+WJUnaWTMGelVtA1YAlwG3ABdX1YYkq5KcBJDklUk2A6cA5yTZsCeLliQ92SC/cqGq1gHrJk07s294Pb2uGEnSLPFKUUlqhIEuSY0w0CWpEQa6JDXCQJekRhjoktQIA12SGmGgS1IjDHRJaoSBLkmNMNAlqREGuiQ1wkCXpEYY6JLUCANdkhphoEtSIwx0SWqEgS5JjTDQJakRBrokNcJAl6RGGOiS1AgDXZIaYaBLUiMMdElqhIEuSY0w0CWpEQMFepJlSW5NMp5k5RTz909yUTf/2iSLhl2oJGnHZgz0JHOA1cAJwBLgtCRLJjX7DeD+qjoC+BPgY8MuVJK0Y4McoS8FxqtqY1U9AqwBTp7U5mTggm74c8Brk2R4ZUqSZjJ3gDbzgU1945uBV03Xpqq2JXkQeC5wT3+jJMuB5d3oQ0lu3ZWi90IHM2lfPF3F71awF71e4GvW2ZdesxdON2OQQB+aqjoXOPep3ObTQZKxqhqd7To0GF+vvY+vWc8gXS5bgIV94wu6aVO2STIX+Eng3mEUKEkazCCBvh5YnOSwJPOAU4G1k9qsBd7eDb8F+HJV1fDKlCTNZMYul65PfAVwGTAHOK+qNiRZBYxV1VrgvwOfSTIO3Ecv9PXP9rlupr2cr9fex9cMiAfSktQGrxSVpEYY6JLUCANd0tNWktOTfHK269hbGOiS1AgDfTclWZXkvX3jf5TkjCTvT7I+ydeT/Kdu3gFJLk1yU5JvJnnr7FUugCSLktyS5NNJNiT52yQ/keTIJNd0r98lSZ4z27W2oNvf3+wb/70kH0lyeZKPJbkuybeTHDPFsicm+VqSg5Ocn+S/Jflqko1J3tK1SZKzu7+vb0z8jSVZneSkbviSJOd1w+/s/manfB88NXtleAz03Xce8OsASfaj95PNu4DF9O6DcyTwc0leAywD7qyqn62qlwH/Z3ZK1iSLgdVV9VLgAeDNwF8CH6yqlwPfAP7jLNa3r5hbVUuB9zJpfyf5FWAl8PqqmrjE/wXA0cAvA2d1095E72/uZ4HjgbOTvAC4Cpj4kJhP70aDdNOu7Ianeh/sVQz03VRVtwP3JjkK+CXgH4FX9g3fAPwLem+WbwCv645EjqmqB2enak3y3aq6sRu+HjgcOKiqruimXQC8ZlYq27d8vvv3emBR3/TjgA8CJ1bV/X3Tv1BVj1fVzcDzu2lHAxdW1WNVdTdwBb2/x6uAY7o7xd4M3N0F/c8DX+2Wnfw+6K9hr/CU3sulYX8BnA78NL0j9tcCf1xV50xumOQVwOuBjyb5UlWteioL1ZQe7ht+DDhotgrZB2xj+wPJZ/YNT7wOj7F9Nn0H+BngRcDYFO0Bdnh316rakuQget+SrwR+CvhV4KGq+kGS5/Lk94FdLvuoS+i9UV5J74ray4B3JjkQIMn8JM9Lcgjwo6r6H8DZwCtmq2Dt0IPA/X39uP+G3pGedt/dwPOSPDfJ/vS6S2ZyB103WJKXztD2KuCtSeYkGaH3zeq6bt419Lpzruza/V73bzM8Qh+CqnokyVeAB6rqMeBvk7wE+Fp3W/iHgF8DjqDXp/c48Cjwm7NVs2b0duBTSZ4FbATeMcv1NKGqHu1uG3IdvZv6fWvA5b6V5G3AXyV5ww6aXkKvG+UmoIAPVNVd3byrgF+qqvEkd9A7Sm8q0L30fwi6k6E3AKdU1W2zXY+kfZNdLrupO8kyDnzJMJc0mzxCl6RGeIQuSY0w0CWpEQa6JDXCQJekRhjoktSI/w/TIK6YopbnBgAAAABJRU5ErkJggg==", + "text/plain": [ + "
" + ] + }, + "metadata": { + "needs_background": "light", + "tags": [] + }, + "output_type": "display_data" + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "/content/data/mini_speech_commands/unknown/b737ee80_nohash_1.wav\n" + ] + }, + { + "data": { + "image/png": "iVBORw0KGgoAAAANSUhEUgAAAXQAAAEICAYAAABPgw/pAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAALEgAACxIB0t1+/AAAADh0RVh0U29mdHdhcmUAbWF0cGxvdGxpYiB2ZXJzaW9uMy4yLjIsIGh0dHA6Ly9tYXRwbG90bGliLm9yZy+WH4yJAAAUd0lEQVR4nO3df7RdZX3n8feHxGAFV7FytZIEQyE6RseCvUa7BixF7ApSwapUGDsVrWbsNFNsrRqnlnFSO0WZZWdNTSvYYUGdkUAd0XRIh7Yqv6pALhTUgMg1gkkoGH6O6AgEvvPH2ZeeXO7NPUlOuOTJ+7XWWdk/nr339+xz7ufs8+yzd1JVSJL2fvvNdgGSpOEw0CWpEQa6JDXCQJekRhjoktQIA12SGmGga7ckOT/JR7vhY5Lcuovr+VSSPxhudTNu81eSbEryUJKjnsptDypJJTlituvQ3mHubBegPS/J7cDzgceAHwJ/A6yoqoeGuZ2qugp48QD1nA68q6qO7lv2PcOsZUD/hd5++OIwVpbkcuAjwLEAVfWRYaz36SrJ+cDl3eixVXX6rBUjwCP0fckbqupA4BXAKPDhyQ2S7Gsf8C8ENuzKgknmDLkWabcZ6PuYqtpC7wj9ZfDEV/rfSnIbcFs37ZeT3JjkgSRfTfLyieWTHJXkhiQ/SHIR8My+eccm2dw3vjDJ55NsTXJvkk8meQnwKeDnu66OB7q2T3TddOPvTjKe5L4ka5Mc0jevkrwnyW1djauTpJt3RJIrkjyY5J6uxu0k2T/JQ8Ac4KYk3+mmvyTJ5d06NyQ5qW+Z85P8eZJ1SX4I/OIg+zvJ6UmunjTtiW6Ubr2rk1za7dNrkxw+zbqO7rqIjh1gP+yX5MNJ7kjy/SR/meQnu3kXJHlfNzx/4j3QjR/e7fP9Jl7PJO/r1vFPSd4xyPPWLKkqH40/gNuB47vhhfSOSv+wGy/g74CfAn4COAr4PvAqeoH39m75/YF5wB3A7wDPAN4CPAp8tFvXscDmbngOcBPwJ8AB9IL/6G7e6cDVk2o8v289xwH30Ps2sT/wp8CVfW0L+N/AQcChwFZgWTfvQuD36R2sPLHNafZLAUd0w88AxoH/0D3P44AfAC/uq+9B4F9NrHvAfT/Vc+3f7vnAvcBSel2g/xNYM7ktsAzYBCwdcD+8s3s+PwMcCHwe+EzfvL/uhv818B3gor55X+x7PbcBq7r983rgR8BzZvs97WPqh0fo+44vdEfDVwNXAP+5b94fV9V9VfX/gOXAOVV1bVU9VlUXAA8Dr+4ezwD+a1U9WlWfA9ZPs72lwCHA+6vqh1X146q6epq2k70NOK+qbqiqh4EP0TuiX9TX5qyqeqCqvgd8BTiym/4ova6UQ3Zym6+mF3xnVdUjVfVlemF5Wl+bL1bVP1TV41X14wHXO4hLquq6qtpGL9CPnDT/FOAc4ISqum7SvOn2w9uAT1TVxuqdK/kQcGrXrXYFcHSS/YDXAB+n90EF8Avd/AmPAqu613sd8BADnCfR7DDQ9x1vrKqDquqFVfXvuvCesKlv+IXA+7qv8A90HwIL6YXzIcCWquq/o9sd02xvIXBHF1I765D+9XaBdC8wv6/NXX3DP6IXxgAfAAJc13WbvHMntrmpqh7vm3bHpG1uYs+Y7rlMeC9wcVV9cyeW3W4fdsNzgedX1XfonRw/EjiG3gfXnUlezJMD/d5Jr+FU9elpwkAX9L66T9gE/FEX/hOPZ1XVhcA/AfMn+mk7h06zzk3AodOcaJ3pFp930vtgASDJAcBzgS0zPpGqu6rq3VV1CPBvgT8b8Gd/dwILu6PWCYdO2uau3Jr0h8CzJkaS/PQurOMU4I1JztiJZbbbh/Seyzbg7m78CnpdZvOqd17lCnrda88BbtyFGvU0YKBrsk8D70nyqvQckOTEJM8GvkYvFH47yTOSvIle18pUrqP3AXBWt45nJpn4Wn83sCDJvGmWvRB4R5Ijk+xPr3vo2qq6fabik5ySZEE3ej+9EH58B4tMuJbe0ecHuud2LPAGYM0Ay+7ITcBLu+fyTHo/a9xZdwKvBc5I8psDLnMh8DtJDktyIL19eFHf0fYVwArgym788m786qp6bBdq1NOAga7tVNUY8G7gk/QCcZzeiT2q6hHgTd34fcBb6Z1sm2o9j9ELxCOA7wGbu/YAX6Z3YvauJPdMsezfA38A/C96HwqHA6cO+BReCVzb/YplLXBGVW2caaHuub0BOIHeCdk/A369qr414HanW++36Z1U/Ht6vyIatE9/8nq+Ry/UVyZ51wCLnAd8hl5gfxf4MfDv++ZfATybfw70q+l9k7gS7bWyfXeoJGlv5RG6JDXCQJekRhjoktSIgQI9ybIkt3aXYq+cps2vJrm5++3vZ4dbpiRpJjOeFE3vJkTfBl5H75cK64HTqurmvjaLgYuB46rq/iTPq6rv72i9Bx98cC1atGg3y5ekfcv1119/T1WNTDVvkLvrLQXGJ376lWQNcDJwc1+bdwOrq+p+gJnCHGDRokWMjY0NsHlJ0oQk012dPVCXy3y2v+R5M9tfDg3wIuBFSf4hyTVJlk1TyPIkY0nGtm7dOsCmJUmDGtZJ0bnAYnp3ZzsN+HSSgyY3qqpzq2q0qkZHRqb8xiBJ2kWDBPoWejdamrCAJ99TYzOwtrsj23fp9bkvHk6JkqRBDBLo64HF3T0h5tG7BHvtpDZfoPtvt5IcTK8LZsbLrSVJwzNjoHc381kBXAbcQu82nhuSrOr7H10uA+5NcjO9ezK/v6ru3VNFS5KebNbu5TI6Olr+ykWSdk6S66tqdKp5XikqSY0w0CWpEQa6JDVikCtFJe2DFq28dLZLaNbtZ524R9brEbokNcJAl6RGGOiS1AgDXZIaYaBLUiMMdElqhIEuSY0w0CWpEQa6JDXCQJekRhjoktQIA12SGmGgS1IjDHRJaoSBLkmNMNAlqREGuiQ1wkCXpEYY6JLUCANdkhphoEtSIwx0SWqEgS5JjRgo0JMsS3JrkvEkK6eYf3qSrUlu7B7vGn6pkqQdmTtTgyRzgNXA64DNwPoka6vq5klNL6qqFXugRknSAAY5Ql8KjFfVxqp6BFgDnLxny5Ik7axBAn0+sKlvfHM3bbI3J/l6ks8lWTjVipIsTzKWZGzr1q27UK4kaTrDOin618Ciqno58HfABVM1qqpzq2q0qkZHRkaGtGlJEgwW6FuA/iPuBd20J1TVvVX1cDf6F8DPDac8SdKgBgn09cDiJIclmQecCqztb5DkBX2jJwG3DK9ESdIgZvyVS1VtS7ICuAyYA5xXVRuSrALGqmot8NtJTgK2AfcBp+/BmiVJU5gx0AGqah2wbtK0M/uGPwR8aLilSZJ2hleKSlIjDHRJaoSBLkmNMNAlqREGuiQ1wkCXpEYY6JLUCANdkhphoEtSIwx0SWqEgS5JjTDQJakRBrokNcJAl6RGGOiS1AgDXZIaYaBLUiMMdElqhIEuSY0w0CWpEQa6JDXCQJekRhjoktQIA12SGmGgS1IjDHRJaoSBLkmNGCjQkyxLcmuS8SQrd9DuzUkqyejwSpQkDWLGQE8yB1gNnAAsAU5LsmSKds8GzgCuHXaRkqSZDXKEvhQYr6qNVfUIsAY4eYp2fwh8DPjxEOuTJA1okECfD2zqG9/cTXtCklcAC6vq0h2tKMnyJGNJxrZu3brTxUqSprfbJ0WT7Ad8AnjfTG2r6tyqGq2q0ZGRkd3dtCSpzyCBvgVY2De+oJs24dnAy4DLk9wOvBpY64lRSXpqDRLo64HFSQ5LMg84FVg7MbOqHqyqg6tqUVUtAq4BTqqqsT1SsSRpSjMGelVtA1YAlwG3ABdX1YYkq5KctKcLlCQNZu4gjapqHbBu0rQzp2l77O6XJUnaWV4pKkmNMNAlqREGuiQ1wkCXpEYY6JLUCANdkhphoEtSIwx0SWqEgS5JjTDQJakRBrokNcJAl6RGGOiS1AgDXZIaYaBLUiMMdElqhIEuSY0w0CWpEQa6JDXCQJekRhjoktQIA12SGmGgS1IjDHRJaoSBLkmNMNAlqREGuiQ1YqBAT7Isya1JxpOsnGL+e5J8I8mNSa5OsmT4pUqSdmTGQE8yB1gNnAAsAU6bIrA/W1X/sqqOBD4OfGLolUqSdmiQI/SlwHhVbayqR4A1wMn9Darq//aNHgDU8EqUJA1i7gBt5gOb+sY3A6+a3CjJbwG/C8wDjptqRUmWA8sBDj300J2tVZK0A0M7KVpVq6vqcOCDwIenaXNuVY1W1ejIyMiwNi1JYrBA3wIs7Btf0E2bzhrgjbtTlCRp5w0S6OuBxUkOSzIPOBVY298gyeK+0ROB24ZXoiRpEDP2oVfVtiQrgMuAOcB5VbUhySpgrKrWAiuSHA88CtwPvH1PFi1JerJBTopSVeuAdZOmndk3fMaQ65Ik7SSvFJWkRhjoktQIA12SGmGgS1IjDHRJaoSBLkmNMNAlqREGuiQ1wkCXpEYY6JLUCANdkhphoEtSIwx0SWqEgS5JjTDQJakRBrokNcJAl6RGGOiS1AgDXZIaYaBLUiMMdElqhIEuSY0w0CWpEQa6JDXCQJekRhjoktSIubNdwK5YtPLS2S6hWbefdeIeWa+v2Z6zp14z7X0GOkJPsizJrUnGk6ycYv7vJrk5ydeTfCnJC4dfqiRpR2YM9CRzgNXACcAS4LQkSyY1+0dgtKpeDnwO+PiwC5Uk7dggR+hLgfGq2lhVjwBrgJP7G1TVV6rqR93oNcCC4ZYpSZrJIIE+H9jUN765mzad3wD+ZqoZSZYnGUsytnXr1sGrlCTNaKi/cknya8AocPZU86vq3KoararRkZGRYW5akvZ5g/zKZQuwsG98QTdtO0mOB34f+IWqeng45UmSBjXIEfp6YHGSw5LMA04F1vY3SHIUcA5wUlV9f/hlSpJmMmOgV9U2YAVwGXALcHFVbUiyKslJXbOzgQOBv0pyY5K106xOkrSHDHRhUVWtA9ZNmnZm3/DxQ65LkrSTvPRfkhphoEtSIwx0SWqEgS5JjTDQJakRBrokNcJAl6RGGOiS1AgDXZIaYaBLUiMMdElqhIEuSY0w0CWpEQa6JDXCQJekRhjoktQIA12SGmGgS1IjDHRJaoSBLkmNMNAlqREGuiQ1wkCXpEYY6JLUCANdkhphoEtSIwx0SWrEQIGeZFmSW5OMJ1k5xfzXJLkhybYkbxl+mZKkmcwY6EnmAKuBE4AlwGlJlkxq9j3gdOCzwy5QkjSYuQO0WQqMV9VGgCRrgJOBmycaVNXt3bzH90CNkqQBDNLlMh/Y1De+uZu205IsTzKWZGzr1q27sgpJ0jSe0pOiVXVuVY1W1ejIyMhTuWlJat4ggb4FWNg3vqCbJkl6Ghkk0NcDi5MclmQecCqwds+WJUnaWTMGelVtA1YAlwG3ABdX1YYkq5KcBJDklUk2A6cA5yTZsCeLliQ92SC/cqGq1gHrJk07s294Pb2uGEnSLPFKUUlqhIEuSY0w0CWpEQa6JDXCQJekRhjoktQIA12SGmGgS1IjDHRJaoSBLkmNMNAlqREGuiQ1wkCXpEYY6JLUCANdkhphoEtSIwx0SWqEgS5JjTDQJakRBrokNcJAl6RGGOiS1AgDXZIaYaBLUiMMdElqhIEuSY0w0CWpEQMFepJlSW5NMp5k5RTz909yUTf/2iSLhl2oJGnHZgz0JHOA1cAJwBLgtCRLJjX7DeD+qjoC+BPgY8MuVJK0Y4McoS8FxqtqY1U9AqwBTp7U5mTggm74c8Brk2R4ZUqSZjJ3gDbzgU1945uBV03Xpqq2JXkQeC5wT3+jJMuB5d3oQ0lu3ZWi90IHM2lfPF3F71awF71e4GvW2ZdesxdON2OQQB+aqjoXOPep3ObTQZKxqhqd7To0GF+vvY+vWc8gXS5bgIV94wu6aVO2STIX+Eng3mEUKEkazCCBvh5YnOSwJPOAU4G1k9qsBd7eDb8F+HJV1fDKlCTNZMYul65PfAVwGTAHOK+qNiRZBYxV1VrgvwOfSTIO3Ecv9PXP9rlupr2cr9fex9cMiAfSktQGrxSVpEYY6JLUCANd0tNWktOTfHK269hbGOiS1AgDfTclWZXkvX3jf5TkjCTvT7I+ydeT/Kdu3gFJLk1yU5JvJnnr7FUugCSLktyS5NNJNiT52yQ/keTIJNd0r98lSZ4z27W2oNvf3+wb/70kH0lyeZKPJbkuybeTHDPFsicm+VqSg5Ocn+S/Jflqko1J3tK1SZKzu7+vb0z8jSVZneSkbviSJOd1w+/s/manfB88NXtleAz03Xce8OsASfaj95PNu4DF9O6DcyTwc0leAywD7qyqn62qlwH/Z3ZK1iSLgdVV9VLgAeDNwF8CH6yqlwPfAP7jLNa3r5hbVUuB9zJpfyf5FWAl8PqqmrjE/wXA0cAvA2d1095E72/uZ4HjgbOTvAC4Cpj4kJhP70aDdNOu7Ianeh/sVQz03VRVtwP3JjkK+CXgH4FX9g3fAPwLem+WbwCv645EjqmqB2enak3y3aq6sRu+HjgcOKiqruimXQC8ZlYq27d8vvv3emBR3/TjgA8CJ1bV/X3Tv1BVj1fVzcDzu2lHAxdW1WNVdTdwBb2/x6uAY7o7xd4M3N0F/c8DX+2Wnfw+6K9hr/CU3sulYX8BnA78NL0j9tcCf1xV50xumOQVwOuBjyb5UlWteioL1ZQe7ht+DDhotgrZB2xj+wPJZ/YNT7wOj7F9Nn0H+BngRcDYFO0Bdnh316rakuQget+SrwR+CvhV4KGq+kGS5/Lk94FdLvuoS+i9UV5J74ray4B3JjkQIMn8JM9Lcgjwo6r6H8DZwCtmq2Dt0IPA/X39uP+G3pGedt/dwPOSPDfJ/vS6S2ZyB103WJKXztD2KuCtSeYkGaH3zeq6bt419Lpzruza/V73bzM8Qh+CqnokyVeAB6rqMeBvk7wE+Fp3W/iHgF8DjqDXp/c48Cjwm7NVs2b0duBTSZ4FbATeMcv1NKGqHu1uG3IdvZv6fWvA5b6V5G3AXyV5ww6aXkKvG+UmoIAPVNVd3byrgF+qqvEkd9A7Sm8q0L30fwi6k6E3AKdU1W2zXY+kfZNdLrupO8kyDnzJMJc0mzxCl6RGeIQuSY0w0CWpEQa6JDXCQJekRhjoktSI/w/TIK6YopbnBgAAAABJRU5ErkJggg==", + "text/plain": [ + "
" + ] + }, + "metadata": { + "needs_background": "light", + "tags": [] + }, + "output_type": "display_data" + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "/content/data/mini_speech_commands/unknown/feb1d305_nohash_2.wav\n" + ] + }, + { + "data": { + "image/png": "iVBORw0KGgoAAAANSUhEUgAAAXQAAAEICAYAAABPgw/pAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAALEgAACxIB0t1+/AAAADh0RVh0U29mdHdhcmUAbWF0cGxvdGxpYiB2ZXJzaW9uMy4yLjIsIGh0dHA6Ly9tYXRwbG90bGliLm9yZy+WH4yJAAAUd0lEQVR4nO3df7RdZX3n8feHxGAFV7FytZIEQyE6RseCvUa7BixF7ApSwapUGDsVrWbsNFNsrRqnlnFSO0WZZWdNTSvYYUGdkUAd0XRIh7Yqv6pALhTUgMg1gkkoGH6O6AgEvvPH2ZeeXO7NPUlOuOTJ+7XWWdk/nr339+xz7ufs8+yzd1JVSJL2fvvNdgGSpOEw0CWpEQa6JDXCQJekRhjoktQIA12SGmGga7ckOT/JR7vhY5Lcuovr+VSSPxhudTNu81eSbEryUJKjnsptDypJJTlituvQ3mHubBegPS/J7cDzgceAHwJ/A6yoqoeGuZ2qugp48QD1nA68q6qO7lv2PcOsZUD/hd5++OIwVpbkcuAjwLEAVfWRYaz36SrJ+cDl3eixVXX6rBUjwCP0fckbqupA4BXAKPDhyQ2S7Gsf8C8ENuzKgknmDLkWabcZ6PuYqtpC7wj9ZfDEV/rfSnIbcFs37ZeT3JjkgSRfTfLyieWTHJXkhiQ/SHIR8My+eccm2dw3vjDJ55NsTXJvkk8meQnwKeDnu66OB7q2T3TddOPvTjKe5L4ka5Mc0jevkrwnyW1djauTpJt3RJIrkjyY5J6uxu0k2T/JQ8Ac4KYk3+mmvyTJ5d06NyQ5qW+Z85P8eZJ1SX4I/OIg+zvJ6UmunjTtiW6Ubr2rk1za7dNrkxw+zbqO7rqIjh1gP+yX5MNJ7kjy/SR/meQnu3kXJHlfNzx/4j3QjR/e7fP9Jl7PJO/r1vFPSd4xyPPWLKkqH40/gNuB47vhhfSOSv+wGy/g74CfAn4COAr4PvAqeoH39m75/YF5wB3A7wDPAN4CPAp8tFvXscDmbngOcBPwJ8AB9IL/6G7e6cDVk2o8v289xwH30Ps2sT/wp8CVfW0L+N/AQcChwFZgWTfvQuD36R2sPLHNafZLAUd0w88AxoH/0D3P44AfAC/uq+9B4F9NrHvAfT/Vc+3f7vnAvcBSel2g/xNYM7ktsAzYBCwdcD+8s3s+PwMcCHwe+EzfvL/uhv818B3gor55X+x7PbcBq7r983rgR8BzZvs97WPqh0fo+44vdEfDVwNXAP+5b94fV9V9VfX/gOXAOVV1bVU9VlUXAA8Dr+4ezwD+a1U9WlWfA9ZPs72lwCHA+6vqh1X146q6epq2k70NOK+qbqiqh4EP0TuiX9TX5qyqeqCqvgd8BTiym/4ova6UQ3Zym6+mF3xnVdUjVfVlemF5Wl+bL1bVP1TV41X14wHXO4hLquq6qtpGL9CPnDT/FOAc4ISqum7SvOn2w9uAT1TVxuqdK/kQcGrXrXYFcHSS/YDXAB+n90EF8Avd/AmPAqu613sd8BADnCfR7DDQ9x1vrKqDquqFVfXvuvCesKlv+IXA+7qv8A90HwIL6YXzIcCWquq/o9sd02xvIXBHF1I765D+9XaBdC8wv6/NXX3DP6IXxgAfAAJc13WbvHMntrmpqh7vm3bHpG1uYs+Y7rlMeC9wcVV9cyeW3W4fdsNzgedX1XfonRw/EjiG3gfXnUlezJMD/d5Jr+FU9elpwkAX9L66T9gE/FEX/hOPZ1XVhcA/AfMn+mk7h06zzk3AodOcaJ3pFp930vtgASDJAcBzgS0zPpGqu6rq3VV1CPBvgT8b8Gd/dwILu6PWCYdO2uau3Jr0h8CzJkaS/PQurOMU4I1JztiJZbbbh/Seyzbg7m78CnpdZvOqd17lCnrda88BbtyFGvU0YKBrsk8D70nyqvQckOTEJM8GvkYvFH47yTOSvIle18pUrqP3AXBWt45nJpn4Wn83sCDJvGmWvRB4R5Ijk+xPr3vo2qq6fabik5ySZEE3ej+9EH58B4tMuJbe0ecHuud2LPAGYM0Ay+7ITcBLu+fyTHo/a9xZdwKvBc5I8psDLnMh8DtJDktyIL19eFHf0fYVwArgym788m786qp6bBdq1NOAga7tVNUY8G7gk/QCcZzeiT2q6hHgTd34fcBb6Z1sm2o9j9ELxCOA7wGbu/YAX6Z3YvauJPdMsezfA38A/C96HwqHA6cO+BReCVzb/YplLXBGVW2caaHuub0BOIHeCdk/A369qr414HanW++36Z1U/Ht6vyIatE9/8nq+Ry/UVyZ51wCLnAd8hl5gfxf4MfDv++ZfATybfw70q+l9k7gS7bWyfXeoJGlv5RG6JDXCQJekRhjoktSIgQI9ybIkt3aXYq+cps2vJrm5++3vZ4dbpiRpJjOeFE3vJkTfBl5H75cK64HTqurmvjaLgYuB46rq/iTPq6rv72i9Bx98cC1atGg3y5ekfcv1119/T1WNTDVvkLvrLQXGJ376lWQNcDJwc1+bdwOrq+p+gJnCHGDRokWMjY0NsHlJ0oQk012dPVCXy3y2v+R5M9tfDg3wIuBFSf4hyTVJlk1TyPIkY0nGtm7dOsCmJUmDGtZJ0bnAYnp3ZzsN+HSSgyY3qqpzq2q0qkZHRqb8xiBJ2kWDBPoWejdamrCAJ99TYzOwtrsj23fp9bkvHk6JkqRBDBLo64HF3T0h5tG7BHvtpDZfoPtvt5IcTK8LZsbLrSVJwzNjoHc381kBXAbcQu82nhuSrOr7H10uA+5NcjO9ezK/v6ru3VNFS5KebNbu5TI6Olr+ykWSdk6S66tqdKp5XikqSY0w0CWpEQa6JDVikCtFJe2DFq28dLZLaNbtZ524R9brEbokNcJAl6RGGOiS1AgDXZIaYaBLUiMMdElqhIEuSY0w0CWpEQa6JDXCQJekRhjoktQIA12SGmGgS1IjDHRJaoSBLkmNMNAlqREGuiQ1wkCXpEYY6JLUCANdkhphoEtSIwx0SWqEgS5JjRgo0JMsS3JrkvEkK6eYf3qSrUlu7B7vGn6pkqQdmTtTgyRzgNXA64DNwPoka6vq5klNL6qqFXugRknSAAY5Ql8KjFfVxqp6BFgDnLxny5Ik7axBAn0+sKlvfHM3bbI3J/l6ks8lWTjVipIsTzKWZGzr1q27UK4kaTrDOin618Ciqno58HfABVM1qqpzq2q0qkZHRkaGtGlJEgwW6FuA/iPuBd20J1TVvVX1cDf6F8DPDac8SdKgBgn09cDiJIclmQecCqztb5DkBX2jJwG3DK9ESdIgZvyVS1VtS7ICuAyYA5xXVRuSrALGqmot8NtJTgK2AfcBp+/BmiVJU5gx0AGqah2wbtK0M/uGPwR8aLilSZJ2hleKSlIjDHRJaoSBLkmNMNAlqREGuiQ1wkCXpEYY6JLUCANdkhphoEtSIwx0SWqEgS5JjTDQJakRBrokNcJAl6RGGOiS1AgDXZIaYaBLUiMMdElqhIEuSY0w0CWpEQa6JDXCQJekRhjoktQIA12SGmGgS1IjDHRJaoSBLkmNGCjQkyxLcmuS8SQrd9DuzUkqyejwSpQkDWLGQE8yB1gNnAAsAU5LsmSKds8GzgCuHXaRkqSZDXKEvhQYr6qNVfUIsAY4eYp2fwh8DPjxEOuTJA1okECfD2zqG9/cTXtCklcAC6vq0h2tKMnyJGNJxrZu3brTxUqSprfbJ0WT7Ad8AnjfTG2r6tyqGq2q0ZGRkd3dtCSpzyCBvgVY2De+oJs24dnAy4DLk9wOvBpY64lRSXpqDRLo64HFSQ5LMg84FVg7MbOqHqyqg6tqUVUtAq4BTqqqsT1SsSRpSjMGelVtA1YAlwG3ABdX1YYkq5KctKcLlCQNZu4gjapqHbBu0rQzp2l77O6XJUnaWV4pKkmNMNAlqREGuiQ1wkCXpEYY6JLUCANdkhphoEtSIwx0SWqEgS5JjTDQJakRBrokNcJAl6RGGOiS1AgDXZIaYaBLUiMMdElqhIEuSY0w0CWpEQa6JDXCQJekRhjoktQIA12SGmGgS1IjDHRJaoSBLkmNMNAlqREGuiQ1YqBAT7Isya1JxpOsnGL+e5J8I8mNSa5OsmT4pUqSdmTGQE8yB1gNnAAsAU6bIrA/W1X/sqqOBD4OfGLolUqSdmiQI/SlwHhVbayqR4A1wMn9Darq//aNHgDU8EqUJA1i7gBt5gOb+sY3A6+a3CjJbwG/C8wDjptqRUmWA8sBDj300J2tVZK0A0M7KVpVq6vqcOCDwIenaXNuVY1W1ejIyMiwNi1JYrBA3wIs7Btf0E2bzhrgjbtTlCRp5w0S6OuBxUkOSzIPOBVY298gyeK+0ROB24ZXoiRpEDP2oVfVtiQrgMuAOcB5VbUhySpgrKrWAiuSHA88CtwPvH1PFi1JerJBTopSVeuAdZOmndk3fMaQ65Ik7SSvFJWkRhjoktQIA12SGmGgS1IjDHRJaoSBLkmNMNAlqREGuiQ1wkCXpEYY6JLUCANdkhphoEtSIwx0SWqEgS5JjTDQJakRBrokNcJAl6RGGOiS1AgDXZIaYaBLUiMMdElqhIEuSY0w0CWpEQa6JDXCQJekRhjoktSIubNdwK5YtPLS2S6hWbefdeIeWa+v2Z6zp14z7X0GOkJPsizJrUnGk6ycYv7vJrk5ydeTfCnJC4dfqiRpR2YM9CRzgNXACcAS4LQkSyY1+0dgtKpeDnwO+PiwC5Uk7dggR+hLgfGq2lhVjwBrgJP7G1TVV6rqR93oNcCC4ZYpSZrJIIE+H9jUN765mzad3wD+ZqoZSZYnGUsytnXr1sGrlCTNaKi/cknya8AocPZU86vq3KoararRkZGRYW5akvZ5g/zKZQuwsG98QTdtO0mOB34f+IWqeng45UmSBjXIEfp6YHGSw5LMA04F1vY3SHIUcA5wUlV9f/hlSpJmMmOgV9U2YAVwGXALcHFVbUiyKslJXbOzgQOBv0pyY5K106xOkrSHDHRhUVWtA9ZNmnZm3/DxQ65LkrSTvPRfkhphoEtSIwx0SWqEgS5JjTDQJakRBrokNcJAl6RGGOiS1AgDXZIaYaBLUiMMdElqhIEuSY0w0CWpEQa6JDXCQJekRhjoktQIA12SGmGgS1IjDHRJaoSBLkmNMNAlqREGuiQ1wkCXpEYY6JLUCANdkhphoEtSIwx0SWrEQIGeZFmSW5OMJ1k5xfzXJLkhybYkbxl+mZKkmcwY6EnmAKuBE4AlwGlJlkxq9j3gdOCzwy5QkjSYuQO0WQqMV9VGgCRrgJOBmycaVNXt3bzH90CNkqQBDNLlMh/Y1De+uZu205IsTzKWZGzr1q27sgpJ0jSe0pOiVXVuVY1W1ejIyMhTuWlJat4ggb4FWNg3vqCbJkl6Ghkk0NcDi5MclmQecCqwds+WJUnaWTMGelVtA1YAlwG3ABdX1YYkq5KcBJDklUk2A6cA5yTZsCeLliQ92SC/cqGq1gHrJk07s294Pb2uGEnSLPFKUUlqhIEuSY0w0CWpEQa6JDXCQJekRhjoktQIA12SGmGgS1IjDHRJaoSBLkmNMNAlqREGuiQ1wkCXpEYY6JLUCANdkhphoEtSIwx0SWqEgS5JjTDQJakRBrokNcJAl6RGGOiS1AgDXZIaYaBLUiMMdElqhIEuSY0w0CWpEQMFepJlSW5NMp5k5RTz909yUTf/2iSLhl2oJGnHZgz0JHOA1cAJwBLgtCRLJjX7DeD+qjoC+BPgY8MuVJK0Y4McoS8FxqtqY1U9AqwBTp7U5mTggm74c8Brk2R4ZUqSZjJ3gDbzgU1945uBV03Xpqq2JXkQeC5wT3+jJMuB5d3oQ0lu3ZWi90IHM2lfPF3F71awF71e4GvW2ZdesxdON2OQQB+aqjoXOPep3ObTQZKxqhqd7To0GF+vvY+vWc8gXS5bgIV94wu6aVO2STIX+Eng3mEUKEkazCCBvh5YnOSwJPOAU4G1k9qsBd7eDb8F+HJV1fDKlCTNZMYul65PfAVwGTAHOK+qNiRZBYxV1VrgvwOfSTIO3Ecv9PXP9rlupr2cr9fex9cMiAfSktQGrxSVpEYY6JLUCANd0tNWktOTfHK269hbGOiS1AgDfTclWZXkvX3jf5TkjCTvT7I+ydeT/Kdu3gFJLk1yU5JvJnnr7FUugCSLktyS5NNJNiT52yQ/keTIJNd0r98lSZ4z27W2oNvf3+wb/70kH0lyeZKPJbkuybeTHDPFsicm+VqSg5Ocn+S/Jflqko1J3tK1SZKzu7+vb0z8jSVZneSkbviSJOd1w+/s/manfB88NXtleAz03Xce8OsASfaj95PNu4DF9O6DcyTwc0leAywD7qyqn62qlwH/Z3ZK1iSLgdVV9VLgAeDNwF8CH6yqlwPfAP7jLNa3r5hbVUuB9zJpfyf5FWAl8PqqmrjE/wXA0cAvA2d1095E72/uZ4HjgbOTvAC4Cpj4kJhP70aDdNOu7Ianeh/sVQz03VRVtwP3JjkK+CXgH4FX9g3fAPwLem+WbwCv645EjqmqB2enak3y3aq6sRu+HjgcOKiqruimXQC8ZlYq27d8vvv3emBR3/TjgA8CJ1bV/X3Tv1BVj1fVzcDzu2lHAxdW1WNVdTdwBb2/x6uAY7o7xd4M3N0F/c8DX+2Wnfw+6K9hr/CU3sulYX8BnA78NL0j9tcCf1xV50xumOQVwOuBjyb5UlWteioL1ZQe7ht+DDhotgrZB2xj+wPJZ/YNT7wOj7F9Nn0H+BngRcDYFO0Bdnh316rakuQget+SrwR+CvhV4KGq+kGS5/Lk94FdLvuoS+i9UV5J74ray4B3JjkQIMn8JM9Lcgjwo6r6H8DZwCtmq2Dt0IPA/X39uP+G3pGedt/dwPOSPDfJ/vS6S2ZyB103WJKXztD2KuCtSeYkGaH3zeq6bt419Lpzruza/V73bzM8Qh+CqnokyVeAB6rqMeBvk7wE+Fp3W/iHgF8DjqDXp/c48Cjwm7NVs2b0duBTSZ4FbATeMcv1NKGqHu1uG3IdvZv6fWvA5b6V5G3AXyV5ww6aXkKvG+UmoIAPVNVd3byrgF+qqvEkd9A7Sm8q0L30fwi6k6E3AKdU1W2zXY+kfZNdLrupO8kyDnzJMJc0mzxCl6RGeIQuSY0w0CWpEQa6JDXCQJekRhjoktSI/w/TIK6YopbnBgAAAABJRU5ErkJggg==", + "text/plain": [ + "
" + ] + }, + "metadata": { + "needs_background": "light", + "tags": [] + }, + "output_type": "display_data" + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "/content/data/mini_speech_commands/unknown/07ad9b59_nohash_2.wav\n" + ] + }, + { + "data": { + "image/png": "iVBORw0KGgoAAAANSUhEUgAAAXQAAAEICAYAAABPgw/pAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAALEgAACxIB0t1+/AAAADh0RVh0U29mdHdhcmUAbWF0cGxvdGxpYiB2ZXJzaW9uMy4yLjIsIGh0dHA6Ly9tYXRwbG90bGliLm9yZy+WH4yJAAAUd0lEQVR4nO3df7RdZX3n8feHxGAFV7FytZIEQyE6RseCvUa7BixF7ApSwapUGDsVrWbsNFNsrRqnlnFSO0WZZWdNTSvYYUGdkUAd0XRIh7Yqv6pALhTUgMg1gkkoGH6O6AgEvvPH2ZeeXO7NPUlOuOTJ+7XWWdk/nr339+xz7ufs8+yzd1JVSJL2fvvNdgGSpOEw0CWpEQa6JDXCQJekRhjoktQIA12SGmGga7ckOT/JR7vhY5Lcuovr+VSSPxhudTNu81eSbEryUJKjnsptDypJJTlituvQ3mHubBegPS/J7cDzgceAHwJ/A6yoqoeGuZ2qugp48QD1nA68q6qO7lv2PcOsZUD/hd5++OIwVpbkcuAjwLEAVfWRYaz36SrJ+cDl3eixVXX6rBUjwCP0fckbqupA4BXAKPDhyQ2S7Gsf8C8ENuzKgknmDLkWabcZ6PuYqtpC7wj9ZfDEV/rfSnIbcFs37ZeT3JjkgSRfTfLyieWTHJXkhiQ/SHIR8My+eccm2dw3vjDJ55NsTXJvkk8meQnwKeDnu66OB7q2T3TddOPvTjKe5L4ka5Mc0jevkrwnyW1djauTpJt3RJIrkjyY5J6uxu0k2T/JQ8Ac4KYk3+mmvyTJ5d06NyQ5qW+Z85P8eZJ1SX4I/OIg+zvJ6UmunjTtiW6Ubr2rk1za7dNrkxw+zbqO7rqIjh1gP+yX5MNJ7kjy/SR/meQnu3kXJHlfNzx/4j3QjR/e7fP9Jl7PJO/r1vFPSd4xyPPWLKkqH40/gNuB47vhhfSOSv+wGy/g74CfAn4COAr4PvAqeoH39m75/YF5wB3A7wDPAN4CPAp8tFvXscDmbngOcBPwJ8AB9IL/6G7e6cDVk2o8v289xwH30Ps2sT/wp8CVfW0L+N/AQcChwFZgWTfvQuD36R2sPLHNafZLAUd0w88AxoH/0D3P44AfAC/uq+9B4F9NrHvAfT/Vc+3f7vnAvcBSel2g/xNYM7ktsAzYBCwdcD+8s3s+PwMcCHwe+EzfvL/uhv818B3gor55X+x7PbcBq7r983rgR8BzZvs97WPqh0fo+44vdEfDVwNXAP+5b94fV9V9VfX/gOXAOVV1bVU9VlUXAA8Dr+4ezwD+a1U9WlWfA9ZPs72lwCHA+6vqh1X146q6epq2k70NOK+qbqiqh4EP0TuiX9TX5qyqeqCqvgd8BTiym/4ova6UQ3Zym6+mF3xnVdUjVfVlemF5Wl+bL1bVP1TV41X14wHXO4hLquq6qtpGL9CPnDT/FOAc4ISqum7SvOn2w9uAT1TVxuqdK/kQcGrXrXYFcHSS/YDXAB+n90EF8Avd/AmPAqu613sd8BADnCfR7DDQ9x1vrKqDquqFVfXvuvCesKlv+IXA+7qv8A90HwIL6YXzIcCWquq/o9sd02xvIXBHF1I765D+9XaBdC8wv6/NXX3DP6IXxgAfAAJc13WbvHMntrmpqh7vm3bHpG1uYs+Y7rlMeC9wcVV9cyeW3W4fdsNzgedX1XfonRw/EjiG3gfXnUlezJMD/d5Jr+FU9elpwkAX9L66T9gE/FEX/hOPZ1XVhcA/AfMn+mk7h06zzk3AodOcaJ3pFp930vtgASDJAcBzgS0zPpGqu6rq3VV1CPBvgT8b8Gd/dwILu6PWCYdO2uau3Jr0h8CzJkaS/PQurOMU4I1JztiJZbbbh/Seyzbg7m78CnpdZvOqd17lCnrda88BbtyFGvU0YKBrsk8D70nyqvQckOTEJM8GvkYvFH47yTOSvIle18pUrqP3AXBWt45nJpn4Wn83sCDJvGmWvRB4R5Ijk+xPr3vo2qq6fabik5ySZEE3ej+9EH58B4tMuJbe0ecHuud2LPAGYM0Ay+7ITcBLu+fyTHo/a9xZdwKvBc5I8psDLnMh8DtJDktyIL19eFHf0fYVwArgym788m786qp6bBdq1NOAga7tVNUY8G7gk/QCcZzeiT2q6hHgTd34fcBb6Z1sm2o9j9ELxCOA7wGbu/YAX6Z3YvauJPdMsezfA38A/C96HwqHA6cO+BReCVzb/YplLXBGVW2caaHuub0BOIHeCdk/A369qr414HanW++36Z1U/Ht6vyIatE9/8nq+Ry/UVyZ51wCLnAd8hl5gfxf4MfDv++ZfATybfw70q+l9k7gS7bWyfXeoJGlv5RG6JDXCQJekRhjoktSIgQI9ybIkt3aXYq+cps2vJrm5++3vZ4dbpiRpJjOeFE3vJkTfBl5H75cK64HTqurmvjaLgYuB46rq/iTPq6rv72i9Bx98cC1atGg3y5ekfcv1119/T1WNTDVvkLvrLQXGJ376lWQNcDJwc1+bdwOrq+p+gJnCHGDRokWMjY0NsHlJ0oQk012dPVCXy3y2v+R5M9tfDg3wIuBFSf4hyTVJlk1TyPIkY0nGtm7dOsCmJUmDGtZJ0bnAYnp3ZzsN+HSSgyY3qqpzq2q0qkZHRqb8xiBJ2kWDBPoWejdamrCAJ99TYzOwtrsj23fp9bkvHk6JkqRBDBLo64HF3T0h5tG7BHvtpDZfoPtvt5IcTK8LZsbLrSVJwzNjoHc381kBXAbcQu82nhuSrOr7H10uA+5NcjO9ezK/v6ru3VNFS5KebNbu5TI6Olr+ykWSdk6S66tqdKp5XikqSY0w0CWpEQa6JDVikCtFJe2DFq28dLZLaNbtZ524R9brEbokNcJAl6RGGOiS1AgDXZIaYaBLUiMMdElqhIEuSY0w0CWpEQa6JDXCQJekRhjoktQIA12SGmGgS1IjDHRJaoSBLkmNMNAlqREGuiQ1wkCXpEYY6JLUCANdkhphoEtSIwx0SWqEgS5JjRgo0JMsS3JrkvEkK6eYf3qSrUlu7B7vGn6pkqQdmTtTgyRzgNXA64DNwPoka6vq5klNL6qqFXugRknSAAY5Ql8KjFfVxqp6BFgDnLxny5Ik7axBAn0+sKlvfHM3bbI3J/l6ks8lWTjVipIsTzKWZGzr1q27UK4kaTrDOin618Ciqno58HfABVM1qqpzq2q0qkZHRkaGtGlJEgwW6FuA/iPuBd20J1TVvVX1cDf6F8DPDac8SdKgBgn09cDiJIclmQecCqztb5DkBX2jJwG3DK9ESdIgZvyVS1VtS7ICuAyYA5xXVRuSrALGqmot8NtJTgK2AfcBp+/BmiVJU5gx0AGqah2wbtK0M/uGPwR8aLilSZJ2hleKSlIjDHRJaoSBLkmNMNAlqREGuiQ1wkCXpEYY6JLUCANdkhphoEtSIwx0SWqEgS5JjTDQJakRBrokNcJAl6RGGOiS1AgDXZIaYaBLUiMMdElqhIEuSY0w0CWpEQa6JDXCQJekRhjoktQIA12SGmGgS1IjDHRJaoSBLkmNGCjQkyxLcmuS8SQrd9DuzUkqyejwSpQkDWLGQE8yB1gNnAAsAU5LsmSKds8GzgCuHXaRkqSZDXKEvhQYr6qNVfUIsAY4eYp2fwh8DPjxEOuTJA1okECfD2zqG9/cTXtCklcAC6vq0h2tKMnyJGNJxrZu3brTxUqSprfbJ0WT7Ad8AnjfTG2r6tyqGq2q0ZGRkd3dtCSpzyCBvgVY2De+oJs24dnAy4DLk9wOvBpY64lRSXpqDRLo64HFSQ5LMg84FVg7MbOqHqyqg6tqUVUtAq4BTqqqsT1SsSRpSjMGelVtA1YAlwG3ABdX1YYkq5KctKcLlCQNZu4gjapqHbBu0rQzp2l77O6XJUnaWV4pKkmNMNAlqREGuiQ1wkCXpEYY6JLUCANdkhphoEtSIwx0SWqEgS5JjTDQJakRBrokNcJAl6RGGOiS1AgDXZIaYaBLUiMMdElqhIEuSY0w0CWpEQa6JDXCQJekRhjoktQIA12SGmGgS1IjDHRJaoSBLkmNMNAlqREGuiQ1YqBAT7Isya1JxpOsnGL+e5J8I8mNSa5OsmT4pUqSdmTGQE8yB1gNnAAsAU6bIrA/W1X/sqqOBD4OfGLolUqSdmiQI/SlwHhVbayqR4A1wMn9Darq//aNHgDU8EqUJA1i7gBt5gOb+sY3A6+a3CjJbwG/C8wDjptqRUmWA8sBDj300J2tVZK0A0M7KVpVq6vqcOCDwIenaXNuVY1W1ejIyMiwNi1JYrBA3wIs7Btf0E2bzhrgjbtTlCRp5w0S6OuBxUkOSzIPOBVY298gyeK+0ROB24ZXoiRpEDP2oVfVtiQrgMuAOcB5VbUhySpgrKrWAiuSHA88CtwPvH1PFi1JerJBTopSVeuAdZOmndk3fMaQ65Ik7SSvFJWkRhjoktQIA12SGmGgS1IjDHRJaoSBLkmNMNAlqREGuiQ1wkCXpEYY6JLUCANdkhphoEtSIwx0SWqEgS5JjTDQJakRBrokNcJAl6RGGOiS1AgDXZIaYaBLUiMMdElqhIEuSY0w0CWpEQa6JDXCQJekRhjoktSIubNdwK5YtPLS2S6hWbefdeIeWa+v2Z6zp14z7X0GOkJPsizJrUnGk6ycYv7vJrk5ydeTfCnJC4dfqiRpR2YM9CRzgNXACcAS4LQkSyY1+0dgtKpeDnwO+PiwC5Uk7dggR+hLgfGq2lhVjwBrgJP7G1TVV6rqR93oNcCC4ZYpSZrJIIE+H9jUN765mzad3wD+ZqoZSZYnGUsytnXr1sGrlCTNaKi/cknya8AocPZU86vq3KoararRkZGRYW5akvZ5g/zKZQuwsG98QTdtO0mOB34f+IWqeng45UmSBjXIEfp6YHGSw5LMA04F1vY3SHIUcA5wUlV9f/hlSpJmMmOgV9U2YAVwGXALcHFVbUiyKslJXbOzgQOBv0pyY5K106xOkrSHDHRhUVWtA9ZNmnZm3/DxQ65LkrSTvPRfkhphoEtSIwx0SWqEgS5JjTDQJakRBrokNcJAl6RGGOiS1AgDXZIaYaBLUiMMdElqhIEuSY0w0CWpEQa6JDXCQJekRhjoktQIA12SGmGgS1IjDHRJaoSBLkmNMNAlqREGuiQ1wkCXpEYY6JLUCANdkhphoEtSIwx0SWrEQIGeZFmSW5OMJ1k5xfzXJLkhybYkbxl+mZKkmcwY6EnmAKuBE4AlwGlJlkxq9j3gdOCzwy5QkjSYuQO0WQqMV9VGgCRrgJOBmycaVNXt3bzH90CNkqQBDNLlMh/Y1De+uZu205IsTzKWZGzr1q27sgpJ0jSe0pOiVXVuVY1W1ejIyMhTuWlJat4ggb4FWNg3vqCbJkl6Ghkk0NcDi5MclmQecCqwds+WJUnaWTMGelVtA1YAlwG3ABdX1YYkq5KcBJDklUk2A6cA5yTZsCeLliQ92SC/cqGq1gHrJk07s294Pb2uGEnSLPFKUUlqhIEuSY0w0CWpEQa6JDXCQJekRhjoktQIA12SGmGgS1IjDHRJaoSBLkmNMNAlqREGuiQ1wkCXpEYY6JLUCANdkhphoEtSIwx0SWqEgS5JjTDQJakRBrokNcJAl6RGGOiS1AgDXZIaYaBLUiMMdElqhIEuSY0w0CWpEQMFepJlSW5NMp5k5RTz909yUTf/2iSLhl2oJGnHZgz0JHOA1cAJwBLgtCRLJjX7DeD+qjoC+BPgY8MuVJK0Y4McoS8FxqtqY1U9AqwBTp7U5mTggm74c8Brk2R4ZUqSZjJ3gDbzgU1945uBV03Xpqq2JXkQeC5wT3+jJMuB5d3oQ0lu3ZWi90IHM2lfPF3F71awF71e4GvW2ZdesxdON2OQQB+aqjoXOPep3ObTQZKxqhqd7To0GF+vvY+vWc8gXS5bgIV94wu6aVO2STIX+Eng3mEUKEkazCCBvh5YnOSwJPOAU4G1k9qsBd7eDb8F+HJV1fDKlCTNZMYul65PfAVwGTAHOK+qNiRZBYxV1VrgvwOfSTIO3Ecv9PXP9rlupr2cr9fex9cMiAfSktQGrxSVpEYY6JLUCANd0tNWktOTfHK269hbGOiS1AgDfTclWZXkvX3jf5TkjCTvT7I+ydeT/Kdu3gFJLk1yU5JvJnnr7FUugCSLktyS5NNJNiT52yQ/keTIJNd0r98lSZ4z27W2oNvf3+wb/70kH0lyeZKPJbkuybeTHDPFsicm+VqSg5Ocn+S/Jflqko1J3tK1SZKzu7+vb0z8jSVZneSkbviSJOd1w+/s/manfB88NXtleAz03Xce8OsASfaj95PNu4DF9O6DcyTwc0leAywD7qyqn62qlwH/Z3ZK1iSLgdVV9VLgAeDNwF8CH6yqlwPfAP7jLNa3r5hbVUuB9zJpfyf5FWAl8PqqmrjE/wXA0cAvA2d1095E72/uZ4HjgbOTvAC4Cpj4kJhP70aDdNOu7Ianeh/sVQz03VRVtwP3JjkK+CXgH4FX9g3fAPwLem+WbwCv645EjqmqB2enak3y3aq6sRu+HjgcOKiqruimXQC8ZlYq27d8vvv3emBR3/TjgA8CJ1bV/X3Tv1BVj1fVzcDzu2lHAxdW1WNVdTdwBb2/x6uAY7o7xd4M3N0F/c8DX+2Wnfw+6K9hr/CU3sulYX8BnA78NL0j9tcCf1xV50xumOQVwOuBjyb5UlWteioL1ZQe7ht+DDhotgrZB2xj+wPJZ/YNT7wOj7F9Nn0H+BngRcDYFO0Bdnh316rakuQget+SrwR+CvhV4KGq+kGS5/Lk94FdLvuoS+i9UV5J74ray4B3JjkQIMn8JM9Lcgjwo6r6H8DZwCtmq2Dt0IPA/X39uP+G3pGedt/dwPOSPDfJ/vS6S2ZyB103WJKXztD2KuCtSeYkGaH3zeq6bt419Lpzruza/V73bzM8Qh+CqnokyVeAB6rqMeBvk7wE+Fp3W/iHgF8DjqDXp/c48Cjwm7NVs2b0duBTSZ4FbATeMcv1NKGqHu1uG3IdvZv6fWvA5b6V5G3AXyV5ww6aXkKvG+UmoIAPVNVd3byrgF+qqvEkd9A7Sm8q0L30fwi6k6E3AKdU1W2zXY+kfZNdLrupO8kyDnzJMJc0mzxCl6RGeIQuSY0w0CWpEQa6JDXCQJekRhjoktSI/w/TIK6YopbnBgAAAABJRU5ErkJggg==", + "text/plain": [ + "
" + ] + }, + "metadata": { + "needs_background": "light", + "tags": [] + }, + "output_type": "display_data" + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "/content/data/mini_speech_commands/unknown/5c39594f_nohash_4.wav\n" + ] + }, + { + "data": { + "image/png": "iVBORw0KGgoAAAANSUhEUgAAAXQAAAEICAYAAABPgw/pAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAALEgAACxIB0t1+/AAAADh0RVh0U29mdHdhcmUAbWF0cGxvdGxpYiB2ZXJzaW9uMy4yLjIsIGh0dHA6Ly9tYXRwbG90bGliLm9yZy+WH4yJAAAUd0lEQVR4nO3df7RdZX3n8feHxGAFV7FytZIEQyE6RseCvUa7BixF7ApSwapUGDsVrWbsNFNsrRqnlnFSO0WZZWdNTSvYYUGdkUAd0XRIh7Yqv6pALhTUgMg1gkkoGH6O6AgEvvPH2ZeeXO7NPUlOuOTJ+7XWWdk/nr339+xz7ufs8+yzd1JVSJL2fvvNdgGSpOEw0CWpEQa6JDXCQJekRhjoktQIA12SGmGga7ckOT/JR7vhY5Lcuovr+VSSPxhudTNu81eSbEryUJKjnsptDypJJTlituvQ3mHubBegPS/J7cDzgceAHwJ/A6yoqoeGuZ2qugp48QD1nA68q6qO7lv2PcOsZUD/hd5++OIwVpbkcuAjwLEAVfWRYaz36SrJ+cDl3eixVXX6rBUjwCP0fckbqupA4BXAKPDhyQ2S7Gsf8C8ENuzKgknmDLkWabcZ6PuYqtpC7wj9ZfDEV/rfSnIbcFs37ZeT3JjkgSRfTfLyieWTHJXkhiQ/SHIR8My+eccm2dw3vjDJ55NsTXJvkk8meQnwKeDnu66OB7q2T3TddOPvTjKe5L4ka5Mc0jevkrwnyW1djauTpJt3RJIrkjyY5J6uxu0k2T/JQ8Ac4KYk3+mmvyTJ5d06NyQ5qW+Z85P8eZJ1SX4I/OIg+zvJ6UmunjTtiW6Ubr2rk1za7dNrkxw+zbqO7rqIjh1gP+yX5MNJ7kjy/SR/meQnu3kXJHlfNzx/4j3QjR/e7fP9Jl7PJO/r1vFPSd4xyPPWLKkqH40/gNuB47vhhfSOSv+wGy/g74CfAn4COAr4PvAqeoH39m75/YF5wB3A7wDPAN4CPAp8tFvXscDmbngOcBPwJ8AB9IL/6G7e6cDVk2o8v289xwH30Ps2sT/wp8CVfW0L+N/AQcChwFZgWTfvQuD36R2sPLHNafZLAUd0w88AxoH/0D3P44AfAC/uq+9B4F9NrHvAfT/Vc+3f7vnAvcBSel2g/xNYM7ktsAzYBCwdcD+8s3s+PwMcCHwe+EzfvL/uhv818B3gor55X+x7PbcBq7r983rgR8BzZvs97WPqh0fo+44vdEfDVwNXAP+5b94fV9V9VfX/gOXAOVV1bVU9VlUXAA8Dr+4ezwD+a1U9WlWfA9ZPs72lwCHA+6vqh1X146q6epq2k70NOK+qbqiqh4EP0TuiX9TX5qyqeqCqvgd8BTiym/4ova6UQ3Zym6+mF3xnVdUjVfVlemF5Wl+bL1bVP1TV41X14wHXO4hLquq6qtpGL9CPnDT/FOAc4ISqum7SvOn2w9uAT1TVxuqdK/kQcGrXrXYFcHSS/YDXAB+n90EF8Avd/AmPAqu613sd8BADnCfR7DDQ9x1vrKqDquqFVfXvuvCesKlv+IXA+7qv8A90HwIL6YXzIcCWquq/o9sd02xvIXBHF1I765D+9XaBdC8wv6/NXX3DP6IXxgAfAAJc13WbvHMntrmpqh7vm3bHpG1uYs+Y7rlMeC9wcVV9cyeW3W4fdsNzgedX1XfonRw/EjiG3gfXnUlezJMD/d5Jr+FU9elpwkAX9L66T9gE/FEX/hOPZ1XVhcA/AfMn+mk7h06zzk3AodOcaJ3pFp930vtgASDJAcBzgS0zPpGqu6rq3VV1CPBvgT8b8Gd/dwILu6PWCYdO2uau3Jr0h8CzJkaS/PQurOMU4I1JztiJZbbbh/Seyzbg7m78CnpdZvOqd17lCnrda88BbtyFGvU0YKBrsk8D70nyqvQckOTEJM8GvkYvFH47yTOSvIle18pUrqP3AXBWt45nJpn4Wn83sCDJvGmWvRB4R5Ijk+xPr3vo2qq6fabik5ySZEE3ej+9EH58B4tMuJbe0ecHuud2LPAGYM0Ay+7ITcBLu+fyTHo/a9xZdwKvBc5I8psDLnMh8DtJDktyIL19eFHf0fYVwArgym788m786qp6bBdq1NOAga7tVNUY8G7gk/QCcZzeiT2q6hHgTd34fcBb6Z1sm2o9j9ELxCOA7wGbu/YAX6Z3YvauJPdMsezfA38A/C96HwqHA6cO+BReCVzb/YplLXBGVW2caaHuub0BOIHeCdk/A369qr414HanW++36Z1U/Ht6vyIatE9/8nq+Ry/UVyZ51wCLnAd8hl5gfxf4MfDv++ZfATybfw70q+l9k7gS7bWyfXeoJGlv5RG6JDXCQJekRhjoktSIgQI9ybIkt3aXYq+cps2vJrm5++3vZ4dbpiRpJjOeFE3vJkTfBl5H75cK64HTqurmvjaLgYuB46rq/iTPq6rv72i9Bx98cC1atGg3y5ekfcv1119/T1WNTDVvkLvrLQXGJ376lWQNcDJwc1+bdwOrq+p+gJnCHGDRokWMjY0NsHlJ0oQk012dPVCXy3y2v+R5M9tfDg3wIuBFSf4hyTVJlk1TyPIkY0nGtm7dOsCmJUmDGtZJ0bnAYnp3ZzsN+HSSgyY3qqpzq2q0qkZHRqb8xiBJ2kWDBPoWejdamrCAJ99TYzOwtrsj23fp9bkvHk6JkqRBDBLo64HF3T0h5tG7BHvtpDZfoPtvt5IcTK8LZsbLrSVJwzNjoHc381kBXAbcQu82nhuSrOr7H10uA+5NcjO9ezK/v6ru3VNFS5KebNbu5TI6Olr+ykWSdk6S66tqdKp5XikqSY0w0CWpEQa6JDVikCtFJe2DFq28dLZLaNbtZ524R9brEbokNcJAl6RGGOiS1AgDXZIaYaBLUiMMdElqhIEuSY0w0CWpEQa6JDXCQJekRhjoktQIA12SGmGgS1IjDHRJaoSBLkmNMNAlqREGuiQ1wkCXpEYY6JLUCANdkhphoEtSIwx0SWqEgS5JjRgo0JMsS3JrkvEkK6eYf3qSrUlu7B7vGn6pkqQdmTtTgyRzgNXA64DNwPoka6vq5klNL6qqFXugRknSAAY5Ql8KjFfVxqp6BFgDnLxny5Ik7axBAn0+sKlvfHM3bbI3J/l6ks8lWTjVipIsTzKWZGzr1q27UK4kaTrDOin618Ciqno58HfABVM1qqpzq2q0qkZHRkaGtGlJEgwW6FuA/iPuBd20J1TVvVX1cDf6F8DPDac8SdKgBgn09cDiJIclmQecCqztb5DkBX2jJwG3DK9ESdIgZvyVS1VtS7ICuAyYA5xXVRuSrALGqmot8NtJTgK2AfcBp+/BmiVJU5gx0AGqah2wbtK0M/uGPwR8aLilSZJ2hleKSlIjDHRJaoSBLkmNMNAlqREGuiQ1wkCXpEYY6JLUCANdkhphoEtSIwx0SWqEgS5JjTDQJakRBrokNcJAl6RGGOiS1AgDXZIaYaBLUiMMdElqhIEuSY0w0CWpEQa6JDXCQJekRhjoktQIA12SGmGgS1IjDHRJaoSBLkmNGCjQkyxLcmuS8SQrd9DuzUkqyejwSpQkDWLGQE8yB1gNnAAsAU5LsmSKds8GzgCuHXaRkqSZDXKEvhQYr6qNVfUIsAY4eYp2fwh8DPjxEOuTJA1okECfD2zqG9/cTXtCklcAC6vq0h2tKMnyJGNJxrZu3brTxUqSprfbJ0WT7Ad8AnjfTG2r6tyqGq2q0ZGRkd3dtCSpzyCBvgVY2De+oJs24dnAy4DLk9wOvBpY64lRSXpqDRLo64HFSQ5LMg84FVg7MbOqHqyqg6tqUVUtAq4BTqqqsT1SsSRpSjMGelVtA1YAlwG3ABdX1YYkq5KctKcLlCQNZu4gjapqHbBu0rQzp2l77O6XJUnaWV4pKkmNMNAlqREGuiQ1wkCXpEYY6JLUCANdkhphoEtSIwx0SWqEgS5JjTDQJakRBrokNcJAl6RGGOiS1AgDXZIaYaBLUiMMdElqhIEuSY0w0CWpEQa6JDXCQJekRhjoktQIA12SGmGgS1IjDHRJaoSBLkmNMNAlqREGuiQ1YqBAT7Isya1JxpOsnGL+e5J8I8mNSa5OsmT4pUqSdmTGQE8yB1gNnAAsAU6bIrA/W1X/sqqOBD4OfGLolUqSdmiQI/SlwHhVbayqR4A1wMn9Darq//aNHgDU8EqUJA1i7gBt5gOb+sY3A6+a3CjJbwG/C8wDjptqRUmWA8sBDj300J2tVZK0A0M7KVpVq6vqcOCDwIenaXNuVY1W1ejIyMiwNi1JYrBA3wIs7Btf0E2bzhrgjbtTlCRp5w0S6OuBxUkOSzIPOBVY298gyeK+0ROB24ZXoiRpEDP2oVfVtiQrgMuAOcB5VbUhySpgrKrWAiuSHA88CtwPvH1PFi1JerJBTopSVeuAdZOmndk3fMaQ65Ik7SSvFJWkRhjoktQIA12SGmGgS1IjDHRJaoSBLkmNMNAlqREGuiQ1wkCXpEYY6JLUCANdkhphoEtSIwx0SWqEgS5JjTDQJakRBrokNcJAl6RGGOiS1AgDXZIaYaBLUiMMdElqhIEuSY0w0CWpEQa6JDXCQJekRhjoktSIubNdwK5YtPLS2S6hWbefdeIeWa+v2Z6zp14z7X0GOkJPsizJrUnGk6ycYv7vJrk5ydeTfCnJC4dfqiRpR2YM9CRzgNXACcAS4LQkSyY1+0dgtKpeDnwO+PiwC5Uk7dggR+hLgfGq2lhVjwBrgJP7G1TVV6rqR93oNcCC4ZYpSZrJIIE+H9jUN765mzad3wD+ZqoZSZYnGUsytnXr1sGrlCTNaKi/cknya8AocPZU86vq3KoararRkZGRYW5akvZ5g/zKZQuwsG98QTdtO0mOB34f+IWqeng45UmSBjXIEfp6YHGSw5LMA04F1vY3SHIUcA5wUlV9f/hlSpJmMmOgV9U2YAVwGXALcHFVbUiyKslJXbOzgQOBv0pyY5K106xOkrSHDHRhUVWtA9ZNmnZm3/DxQ65LkrSTvPRfkhphoEtSIwx0SWqEgS5JjTDQJakRBrokNcJAl6RGGOiS1AgDXZIaYaBLUiMMdElqhIEuSY0w0CWpEQa6JDXCQJekRhjoktQIA12SGmGgS1IjDHRJaoSBLkmNMNAlqREGuiQ1wkCXpEYY6JLUCANdkhphoEtSIwx0SWrEQIGeZFmSW5OMJ1k5xfzXJLkhybYkbxl+mZKkmcwY6EnmAKuBE4AlwGlJlkxq9j3gdOCzwy5QkjSYuQO0WQqMV9VGgCRrgJOBmycaVNXt3bzH90CNkqQBDNLlMh/Y1De+uZu205IsTzKWZGzr1q27sgpJ0jSe0pOiVXVuVY1W1ejIyMhTuWlJat4ggb4FWNg3vqCbJkl6Ghkk0NcDi5MclmQecCqwds+WJUnaWTMGelVtA1YAlwG3ABdX1YYkq5KcBJDklUk2A6cA5yTZsCeLliQ92SC/cqGq1gHrJk07s294Pb2uGEnSLPFKUUlqhIEuSY0w0CWpEQa6JDXCQJekRhjoktQIA12SGmGgS1IjDHRJaoSBLkmNMNAlqREGuiQ1wkCXpEYY6JLUCANdkhphoEtSIwx0SWqEgS5JjTDQJakRBrokNcJAl6RGGOiS1AgDXZIaYaBLUiMMdElqhIEuSY0w0CWpEQMFepJlSW5NMp5k5RTz909yUTf/2iSLhl2oJGnHZgz0JHOA1cAJwBLgtCRLJjX7DeD+qjoC+BPgY8MuVJK0Y4McoS8FxqtqY1U9AqwBTp7U5mTggm74c8Brk2R4ZUqSZjJ3gDbzgU1945uBV03Xpqq2JXkQeC5wT3+jJMuB5d3oQ0lu3ZWi90IHM2lfPF3F71awF71e4GvW2ZdesxdON2OQQB+aqjoXOPep3ObTQZKxqhqd7To0GF+vvY+vWc8gXS5bgIV94wu6aVO2STIX+Eng3mEUKEkazCCBvh5YnOSwJPOAU4G1k9qsBd7eDb8F+HJV1fDKlCTNZMYul65PfAVwGTAHOK+qNiRZBYxV1VrgvwOfSTIO3Ecv9PXP9rlupr2cr9fex9cMiAfSktQGrxSVpEYY6JLUCANd0tNWktOTfHK269hbGOiS1AgDfTclWZXkvX3jf5TkjCTvT7I+ydeT/Kdu3gFJLk1yU5JvJnnr7FUugCSLktyS5NNJNiT52yQ/keTIJNd0r98lSZ4z27W2oNvf3+wb/70kH0lyeZKPJbkuybeTHDPFsicm+VqSg5Ocn+S/Jflqko1J3tK1SZKzu7+vb0z8jSVZneSkbviSJOd1w+/s/manfB88NXtleAz03Xce8OsASfaj95PNu4DF9O6DcyTwc0leAywD7qyqn62qlwH/Z3ZK1iSLgdVV9VLgAeDNwF8CH6yqlwPfAP7jLNa3r5hbVUuB9zJpfyf5FWAl8PqqmrjE/wXA0cAvA2d1095E72/uZ4HjgbOTvAC4Cpj4kJhP70aDdNOu7Ianeh/sVQz03VRVtwP3JjkK+CXgH4FX9g3fAPwLem+WbwCv645EjqmqB2enak3y3aq6sRu+HjgcOKiqruimXQC8ZlYq27d8vvv3emBR3/TjgA8CJ1bV/X3Tv1BVj1fVzcDzu2lHAxdW1WNVdTdwBb2/x6uAY7o7xd4M3N0F/c8DX+2Wnfw+6K9hr/CU3sulYX8BnA78NL0j9tcCf1xV50xumOQVwOuBjyb5UlWteioL1ZQe7ht+DDhotgrZB2xj+wPJZ/YNT7wOj7F9Nn0H+BngRcDYFO0Bdnh316rakuQget+SrwR+CvhV4KGq+kGS5/Lk94FdLvuoS+i9UV5J74ray4B3JjkQIMn8JM9Lcgjwo6r6H8DZwCtmq2Dt0IPA/X39uP+G3pGedt/dwPOSPDfJ/vS6S2ZyB103WJKXztD2KuCtSeYkGaH3zeq6bt419Lpzruza/V73bzM8Qh+CqnokyVeAB6rqMeBvk7wE+Fp3W/iHgF8DjqDXp/c48Cjwm7NVs2b0duBTSZ4FbATeMcv1NKGqHu1uG3IdvZv6fWvA5b6V5G3AXyV5ww6aXkKvG+UmoIAPVNVd3byrgF+qqvEkd9A7Sm8q0L30fwi6k6E3AKdU1W2zXY+kfZNdLrupO8kyDnzJMJc0mzxCl6RGeIQuSY0w0CWpEQa6JDXCQJekRhjoktSI/w/TIK6YopbnBgAAAABJRU5ErkJggg==", + "text/plain": [ + "
" + ] + }, + "metadata": { + "needs_background": "light", + "tags": [] + }, + "output_type": "display_data" + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "/content/data/mini_speech_commands/unknown/2d92f18b_nohash_0.wav\n" + ] + }, + { + "data": { + "image/png": "iVBORw0KGgoAAAANSUhEUgAAAXQAAAEICAYAAABPgw/pAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAALEgAACxIB0t1+/AAAADh0RVh0U29mdHdhcmUAbWF0cGxvdGxpYiB2ZXJzaW9uMy4yLjIsIGh0dHA6Ly9tYXRwbG90bGliLm9yZy+WH4yJAAAUd0lEQVR4nO3df7RdZX3n8feHxGAFV7FytZIEQyE6RseCvUa7BixF7ApSwapUGDsVrWbsNFNsrRqnlnFSO0WZZWdNTSvYYUGdkUAd0XRIh7Yqv6pALhTUgMg1gkkoGH6O6AgEvvPH2ZeeXO7NPUlOuOTJ+7XWWdk/nr339+xz7ufs8+yzd1JVSJL2fvvNdgGSpOEw0CWpEQa6JDXCQJekRhjoktQIA12SGmGga7ckOT/JR7vhY5Lcuovr+VSSPxhudTNu81eSbEryUJKjnsptDypJJTlituvQ3mHubBegPS/J7cDzgceAHwJ/A6yoqoeGuZ2qugp48QD1nA68q6qO7lv2PcOsZUD/hd5++OIwVpbkcuAjwLEAVfWRYaz36SrJ+cDl3eixVXX6rBUjwCP0fckbqupA4BXAKPDhyQ2S7Gsf8C8ENuzKgknmDLkWabcZ6PuYqtpC7wj9ZfDEV/rfSnIbcFs37ZeT3JjkgSRfTfLyieWTHJXkhiQ/SHIR8My+eccm2dw3vjDJ55NsTXJvkk8meQnwKeDnu66OB7q2T3TddOPvTjKe5L4ka5Mc0jevkrwnyW1djauTpJt3RJIrkjyY5J6uxu0k2T/JQ8Ac4KYk3+mmvyTJ5d06NyQ5qW+Z85P8eZJ1SX4I/OIg+zvJ6UmunjTtiW6Ubr2rk1za7dNrkxw+zbqO7rqIjh1gP+yX5MNJ7kjy/SR/meQnu3kXJHlfNzx/4j3QjR/e7fP9Jl7PJO/r1vFPSd4xyPPWLKkqH40/gNuB47vhhfSOSv+wGy/g74CfAn4COAr4PvAqeoH39m75/YF5wB3A7wDPAN4CPAp8tFvXscDmbngOcBPwJ8AB9IL/6G7e6cDVk2o8v289xwH30Ps2sT/wp8CVfW0L+N/AQcChwFZgWTfvQuD36R2sPLHNafZLAUd0w88AxoH/0D3P44AfAC/uq+9B4F9NrHvAfT/Vc+3f7vnAvcBSel2g/xNYM7ktsAzYBCwdcD+8s3s+PwMcCHwe+EzfvL/uhv818B3gor55X+x7PbcBq7r983rgR8BzZvs97WPqh0fo+44vdEfDVwNXAP+5b94fV9V9VfX/gOXAOVV1bVU9VlUXAA8Dr+4ezwD+a1U9WlWfA9ZPs72lwCHA+6vqh1X146q6epq2k70NOK+qbqiqh4EP0TuiX9TX5qyqeqCqvgd8BTiym/4ova6UQ3Zym6+mF3xnVdUjVfVlemF5Wl+bL1bVP1TV41X14wHXO4hLquq6qtpGL9CPnDT/FOAc4ISqum7SvOn2w9uAT1TVxuqdK/kQcGrXrXYFcHSS/YDXAB+n90EF8Avd/AmPAqu613sd8BADnCfR7DDQ9x1vrKqDquqFVfXvuvCesKlv+IXA+7qv8A90HwIL6YXzIcCWquq/o9sd02xvIXBHF1I765D+9XaBdC8wv6/NXX3DP6IXxgAfAAJc13WbvHMntrmpqh7vm3bHpG1uYs+Y7rlMeC9wcVV9cyeW3W4fdsNzgedX1XfonRw/EjiG3gfXnUlezJMD/d5Jr+FU9elpwkAX9L66T9gE/FEX/hOPZ1XVhcA/AfMn+mk7h06zzk3AodOcaJ3pFp930vtgASDJAcBzgS0zPpGqu6rq3VV1CPBvgT8b8Gd/dwILu6PWCYdO2uau3Jr0h8CzJkaS/PQurOMU4I1JztiJZbbbh/Seyzbg7m78CnpdZvOqd17lCnrda88BbtyFGvU0YKBrsk8D70nyqvQckOTEJM8GvkYvFH47yTOSvIle18pUrqP3AXBWt45nJpn4Wn83sCDJvGmWvRB4R5Ijk+xPr3vo2qq6fabik5ySZEE3ej+9EH58B4tMuJbe0ecHuud2LPAGYM0Ay+7ITcBLu+fyTHo/a9xZdwKvBc5I8psDLnMh8DtJDktyIL19eFHf0fYVwArgym788m786qp6bBdq1NOAga7tVNUY8G7gk/QCcZzeiT2q6hHgTd34fcBb6Z1sm2o9j9ELxCOA7wGbu/YAX6Z3YvauJPdMsezfA38A/C96HwqHA6cO+BReCVzb/YplLXBGVW2caaHuub0BOIHeCdk/A369qr414HanW++36Z1U/Ht6vyIatE9/8nq+Ry/UVyZ51wCLnAd8hl5gfxf4MfDv++ZfATybfw70q+l9k7gS7bWyfXeoJGlv5RG6JDXCQJekRhjoktSIgQI9ybIkt3aXYq+cps2vJrm5++3vZ4dbpiRpJjOeFE3vJkTfBl5H75cK64HTqurmvjaLgYuB46rq/iTPq6rv72i9Bx98cC1atGg3y5ekfcv1119/T1WNTDVvkLvrLQXGJ376lWQNcDJwc1+bdwOrq+p+gJnCHGDRokWMjY0NsHlJ0oQk012dPVCXy3y2v+R5M9tfDg3wIuBFSf4hyTVJlk1TyPIkY0nGtm7dOsCmJUmDGtZJ0bnAYnp3ZzsN+HSSgyY3qqpzq2q0qkZHRqb8xiBJ2kWDBPoWejdamrCAJ99TYzOwtrsj23fp9bkvHk6JkqRBDBLo64HF3T0h5tG7BHvtpDZfoPtvt5IcTK8LZsbLrSVJwzNjoHc381kBXAbcQu82nhuSrOr7H10uA+5NcjO9ezK/v6ru3VNFS5KebNbu5TI6Olr+ykWSdk6S66tqdKp5XikqSY0w0CWpEQa6JDVikCtFJe2DFq28dLZLaNbtZ524R9brEbokNcJAl6RGGOiS1AgDXZIaYaBLUiMMdElqhIEuSY0w0CWpEQa6JDXCQJekRhjoktQIA12SGmGgS1IjDHRJaoSBLkmNMNAlqREGuiQ1wkCXpEYY6JLUCANdkhphoEtSIwx0SWqEgS5JjRgo0JMsS3JrkvEkK6eYf3qSrUlu7B7vGn6pkqQdmTtTgyRzgNXA64DNwPoka6vq5klNL6qqFXugRknSAAY5Ql8KjFfVxqp6BFgDnLxny5Ik7axBAn0+sKlvfHM3bbI3J/l6ks8lWTjVipIsTzKWZGzr1q27UK4kaTrDOin618Ciqno58HfABVM1qqpzq2q0qkZHRkaGtGlJEgwW6FuA/iPuBd20J1TVvVX1cDf6F8DPDac8SdKgBgn09cDiJIclmQecCqztb5DkBX2jJwG3DK9ESdIgZvyVS1VtS7ICuAyYA5xXVRuSrALGqmot8NtJTgK2AfcBp+/BmiVJU5gx0AGqah2wbtK0M/uGPwR8aLilSZJ2hleKSlIjDHRJaoSBLkmNMNAlqREGuiQ1wkCXpEYY6JLUCANdkhphoEtSIwx0SWqEgS5JjTDQJakRBrokNcJAl6RGGOiS1AgDXZIaYaBLUiMMdElqhIEuSY0w0CWpEQa6JDXCQJekRhjoktQIA12SGmGgS1IjDHRJaoSBLkmNGCjQkyxLcmuS8SQrd9DuzUkqyejwSpQkDWLGQE8yB1gNnAAsAU5LsmSKds8GzgCuHXaRkqSZDXKEvhQYr6qNVfUIsAY4eYp2fwh8DPjxEOuTJA1okECfD2zqG9/cTXtCklcAC6vq0h2tKMnyJGNJxrZu3brTxUqSprfbJ0WT7Ad8AnjfTG2r6tyqGq2q0ZGRkd3dtCSpzyCBvgVY2De+oJs24dnAy4DLk9wOvBpY64lRSXpqDRLo64HFSQ5LMg84FVg7MbOqHqyqg6tqUVUtAq4BTqqqsT1SsSRpSjMGelVtA1YAlwG3ABdX1YYkq5KctKcLlCQNZu4gjapqHbBu0rQzp2l77O6XJUnaWV4pKkmNMNAlqREGuiQ1wkCXpEYY6JLUCANdkhphoEtSIwx0SWqEgS5JjTDQJakRBrokNcJAl6RGGOiS1AgDXZIaYaBLUiMMdElqhIEuSY0w0CWpEQa6JDXCQJekRhjoktQIA12SGmGgS1IjDHRJaoSBLkmNMNAlqREGuiQ1YqBAT7Isya1JxpOsnGL+e5J8I8mNSa5OsmT4pUqSdmTGQE8yB1gNnAAsAU6bIrA/W1X/sqqOBD4OfGLolUqSdmiQI/SlwHhVbayqR4A1wMn9Darq//aNHgDU8EqUJA1i7gBt5gOb+sY3A6+a3CjJbwG/C8wDjptqRUmWA8sBDj300J2tVZK0A0M7KVpVq6vqcOCDwIenaXNuVY1W1ejIyMiwNi1JYrBA3wIs7Btf0E2bzhrgjbtTlCRp5w0S6OuBxUkOSzIPOBVY298gyeK+0ROB24ZXoiRpEDP2oVfVtiQrgMuAOcB5VbUhySpgrKrWAiuSHA88CtwPvH1PFi1JerJBTopSVeuAdZOmndk3fMaQ65Ik7SSvFJWkRhjoktQIA12SGmGgS1IjDHRJaoSBLkmNMNAlqREGuiQ1wkCXpEYY6JLUCANdkhphoEtSIwx0SWqEgS5JjTDQJakRBrokNcJAl6RGGOiS1AgDXZIaYaBLUiMMdElqhIEuSY0w0CWpEQa6JDXCQJekRhjoktSIubNdwK5YtPLS2S6hWbefdeIeWa+v2Z6zp14z7X0GOkJPsizJrUnGk6ycYv7vJrk5ydeTfCnJC4dfqiRpR2YM9CRzgNXACcAS4LQkSyY1+0dgtKpeDnwO+PiwC5Uk7dggR+hLgfGq2lhVjwBrgJP7G1TVV6rqR93oNcCC4ZYpSZrJIIE+H9jUN765mzad3wD+ZqoZSZYnGUsytnXr1sGrlCTNaKi/cknya8AocPZU86vq3KoararRkZGRYW5akvZ5g/zKZQuwsG98QTdtO0mOB34f+IWqeng45UmSBjXIEfp6YHGSw5LMA04F1vY3SHIUcA5wUlV9f/hlSpJmMmOgV9U2YAVwGXALcHFVbUiyKslJXbOzgQOBv0pyY5K106xOkrSHDHRhUVWtA9ZNmnZm3/DxQ65LkrSTvPRfkhphoEtSIwx0SWqEgS5JjTDQJakRBrokNcJAl6RGGOiS1AgDXZIaYaBLUiMMdElqhIEuSY0w0CWpEQa6JDXCQJekRhjoktQIA12SGmGgS1IjDHRJaoSBLkmNMNAlqREGuiQ1wkCXpEYY6JLUCANdkhphoEtSIwx0SWrEQIGeZFmSW5OMJ1k5xfzXJLkhybYkbxl+mZKkmcwY6EnmAKuBE4AlwGlJlkxq9j3gdOCzwy5QkjSYuQO0WQqMV9VGgCRrgJOBmycaVNXt3bzH90CNkqQBDNLlMh/Y1De+uZu205IsTzKWZGzr1q27sgpJ0jSe0pOiVXVuVY1W1ejIyMhTuWlJat4ggb4FWNg3vqCbJkl6Ghkk0NcDi5MclmQecCqwds+WJUnaWTMGelVtA1YAlwG3ABdX1YYkq5KcBJDklUk2A6cA5yTZsCeLliQ92SC/cqGq1gHrJk07s294Pb2uGEnSLPFKUUlqhIEuSY0w0CWpEQa6JDXCQJekRhjoktQIA12SGmGgS1IjDHRJaoSBLkmNMNAlqREGuiQ1wkCXpEYY6JLUCANdkhphoEtSIwx0SWqEgS5JjTDQJakRBrokNcJAl6RGGOiS1AgDXZIaYaBLUiMMdElqhIEuSY0w0CWpEQMFepJlSW5NMp5k5RTz909yUTf/2iSLhl2oJGnHZgz0JHOA1cAJwBLgtCRLJjX7DeD+qjoC+BPgY8MuVJK0Y4McoS8FxqtqY1U9AqwBTp7U5mTggm74c8Brk2R4ZUqSZjJ3gDbzgU1945uBV03Xpqq2JXkQeC5wT3+jJMuB5d3oQ0lu3ZWi90IHM2lfPF3F71awF71e4GvW2ZdesxdON2OQQB+aqjoXOPep3ObTQZKxqhqd7To0GF+vvY+vWc8gXS5bgIV94wu6aVO2STIX+Eng3mEUKEkazCCBvh5YnOSwJPOAU4G1k9qsBd7eDb8F+HJV1fDKlCTNZMYul65PfAVwGTAHOK+qNiRZBYxV1VrgvwOfSTIO3Ecv9PXP9rlupr2cr9fex9cMiAfSktQGrxSVpEYY6JLUCANd0tNWktOTfHK269hbGOiS1AgDfTclWZXkvX3jf5TkjCTvT7I+ydeT/Kdu3gFJLk1yU5JvJnnr7FUugCSLktyS5NNJNiT52yQ/keTIJNd0r98lSZ4z27W2oNvf3+wb/70kH0lyeZKPJbkuybeTHDPFsicm+VqSg5Ocn+S/Jflqko1J3tK1SZKzu7+vb0z8jSVZneSkbviSJOd1w+/s/manfB88NXtleAz03Xce8OsASfaj95PNu4DF9O6DcyTwc0leAywD7qyqn62qlwH/Z3ZK1iSLgdVV9VLgAeDNwF8CH6yqlwPfAP7jLNa3r5hbVUuB9zJpfyf5FWAl8PqqmrjE/wXA0cAvA2d1095E72/uZ4HjgbOTvAC4Cpj4kJhP70aDdNOu7Ianeh/sVQz03VRVtwP3JjkK+CXgH4FX9g3fAPwLem+WbwCv645EjqmqB2enak3y3aq6sRu+HjgcOKiqruimXQC8ZlYq27d8vvv3emBR3/TjgA8CJ1bV/X3Tv1BVj1fVzcDzu2lHAxdW1WNVdTdwBb2/x6uAY7o7xd4M3N0F/c8DX+2Wnfw+6K9hr/CU3sulYX8BnA78NL0j9tcCf1xV50xumOQVwOuBjyb5UlWteioL1ZQe7ht+DDhotgrZB2xj+wPJZ/YNT7wOj7F9Nn0H+BngRcDYFO0Bdnh316rakuQget+SrwR+CvhV4KGq+kGS5/Lk94FdLvuoS+i9UV5J74ray4B3JjkQIMn8JM9Lcgjwo6r6H8DZwCtmq2Dt0IPA/X39uP+G3pGedt/dwPOSPDfJ/vS6S2ZyB103WJKXztD2KuCtSeYkGaH3zeq6bt419Lpzruza/V73bzM8Qh+CqnokyVeAB6rqMeBvk7wE+Fp3W/iHgF8DjqDXp/c48Cjwm7NVs2b0duBTSZ4FbATeMcv1NKGqHu1uG3IdvZv6fWvA5b6V5G3AXyV5ww6aXkKvG+UmoIAPVNVd3byrgF+qqvEkd9A7Sm8q0L30fwi6k6E3AKdU1W2zXY+kfZNdLrupO8kyDnzJMJc0mzxCl6RGeIQuSY0w0CWpEQa6JDXCQJekRhjoktSI/w/TIK6YopbnBgAAAABJRU5ErkJggg==", + "text/plain": [ + "
" + ] + }, + "metadata": { + "needs_background": "light", + "tags": [] + }, + "output_type": "display_data" + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "/content/data/mini_speech_commands/unknown/b83c1acf_nohash_2.wav\n" + ] + }, + { + "data": { + "image/png": "iVBORw0KGgoAAAANSUhEUgAAAXQAAAEICAYAAABPgw/pAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAALEgAACxIB0t1+/AAAADh0RVh0U29mdHdhcmUAbWF0cGxvdGxpYiB2ZXJzaW9uMy4yLjIsIGh0dHA6Ly9tYXRwbG90bGliLm9yZy+WH4yJAAAUd0lEQVR4nO3df7RdZX3n8feHxGAFV7FytZIEQyE6RseCvUa7BixF7ApSwapUGDsVrWbsNFNsrRqnlnFSO0WZZWdNTSvYYUGdkUAd0XRIh7Yqv6pALhTUgMg1gkkoGH6O6AgEvvPH2ZeeXO7NPUlOuOTJ+7XWWdk/nr339+xz7ufs8+yzd1JVSJL2fvvNdgGSpOEw0CWpEQa6JDXCQJekRhjoktQIA12SGmGga7ckOT/JR7vhY5Lcuovr+VSSPxhudTNu81eSbEryUJKjnsptDypJJTlituvQ3mHubBegPS/J7cDzgceAHwJ/A6yoqoeGuZ2qugp48QD1nA68q6qO7lv2PcOsZUD/hd5++OIwVpbkcuAjwLEAVfWRYaz36SrJ+cDl3eixVXX6rBUjwCP0fckbqupA4BXAKPDhyQ2S7Gsf8C8ENuzKgknmDLkWabcZ6PuYqtpC7wj9ZfDEV/rfSnIbcFs37ZeT3JjkgSRfTfLyieWTHJXkhiQ/SHIR8My+eccm2dw3vjDJ55NsTXJvkk8meQnwKeDnu66OB7q2T3TddOPvTjKe5L4ka5Mc0jevkrwnyW1djauTpJt3RJIrkjyY5J6uxu0k2T/JQ8Ac4KYk3+mmvyTJ5d06NyQ5qW+Z85P8eZJ1SX4I/OIg+zvJ6UmunjTtiW6Ubr2rk1za7dNrkxw+zbqO7rqIjh1gP+yX5MNJ7kjy/SR/meQnu3kXJHlfNzx/4j3QjR/e7fP9Jl7PJO/r1vFPSd4xyPPWLKkqH40/gNuB47vhhfSOSv+wGy/g74CfAn4COAr4PvAqeoH39m75/YF5wB3A7wDPAN4CPAp8tFvXscDmbngOcBPwJ8AB9IL/6G7e6cDVk2o8v289xwH30Ps2sT/wp8CVfW0L+N/AQcChwFZgWTfvQuD36R2sPLHNafZLAUd0w88AxoH/0D3P44AfAC/uq+9B4F9NrHvAfT/Vc+3f7vnAvcBSel2g/xNYM7ktsAzYBCwdcD+8s3s+PwMcCHwe+EzfvL/uhv818B3gor55X+x7PbcBq7r983rgR8BzZvs97WPqh0fo+44vdEfDVwNXAP+5b94fV9V9VfX/gOXAOVV1bVU9VlUXAA8Dr+4ezwD+a1U9WlWfA9ZPs72lwCHA+6vqh1X146q6epq2k70NOK+qbqiqh4EP0TuiX9TX5qyqeqCqvgd8BTiym/4ova6UQ3Zym6+mF3xnVdUjVfVlemF5Wl+bL1bVP1TV41X14wHXO4hLquq6qtpGL9CPnDT/FOAc4ISqum7SvOn2w9uAT1TVxuqdK/kQcGrXrXYFcHSS/YDXAB+n90EF8Avd/AmPAqu613sd8BADnCfR7DDQ9x1vrKqDquqFVfXvuvCesKlv+IXA+7qv8A90HwIL6YXzIcCWquq/o9sd02xvIXBHF1I765D+9XaBdC8wv6/NXX3DP6IXxgAfAAJc13WbvHMntrmpqh7vm3bHpG1uYs+Y7rlMeC9wcVV9cyeW3W4fdsNzgedX1XfonRw/EjiG3gfXnUlezJMD/d5Jr+FU9elpwkAX9L66T9gE/FEX/hOPZ1XVhcA/AfMn+mk7h06zzk3AodOcaJ3pFp930vtgASDJAcBzgS0zPpGqu6rq3VV1CPBvgT8b8Gd/dwILu6PWCYdO2uau3Jr0h8CzJkaS/PQurOMU4I1JztiJZbbbh/Seyzbg7m78CnpdZvOqd17lCnrda88BbtyFGvU0YKBrsk8D70nyqvQckOTEJM8GvkYvFH47yTOSvIle18pUrqP3AXBWt45nJpn4Wn83sCDJvGmWvRB4R5Ijk+xPr3vo2qq6fabik5ySZEE3ej+9EH58B4tMuJbe0ecHuud2LPAGYM0Ay+7ITcBLu+fyTHo/a9xZdwKvBc5I8psDLnMh8DtJDktyIL19eFHf0fYVwArgym788m786qp6bBdq1NOAga7tVNUY8G7gk/QCcZzeiT2q6hHgTd34fcBb6Z1sm2o9j9ELxCOA7wGbu/YAX6Z3YvauJPdMsezfA38A/C96HwqHA6cO+BReCVzb/YplLXBGVW2caaHuub0BOIHeCdk/A369qr414HanW++36Z1U/Ht6vyIatE9/8nq+Ry/UVyZ51wCLnAd8hl5gfxf4MfDv++ZfATybfw70q+l9k7gS7bWyfXeoJGlv5RG6JDXCQJekRhjoktSIgQI9ybIkt3aXYq+cps2vJrm5++3vZ4dbpiRpJjOeFE3vJkTfBl5H75cK64HTqurmvjaLgYuB46rq/iTPq6rv72i9Bx98cC1atGg3y5ekfcv1119/T1WNTDVvkLvrLQXGJ376lWQNcDJwc1+bdwOrq+p+gJnCHGDRokWMjY0NsHlJ0oQk012dPVCXy3y2v+R5M9tfDg3wIuBFSf4hyTVJlk1TyPIkY0nGtm7dOsCmJUmDGtZJ0bnAYnp3ZzsN+HSSgyY3qqpzq2q0qkZHRqb8xiBJ2kWDBPoWejdamrCAJ99TYzOwtrsj23fp9bkvHk6JkqRBDBLo64HF3T0h5tG7BHvtpDZfoPtvt5IcTK8LZsbLrSVJwzNjoHc381kBXAbcQu82nhuSrOr7H10uA+5NcjO9ezK/v6ru3VNFS5KebNbu5TI6Olr+ykWSdk6S66tqdKp5XikqSY0w0CWpEQa6JDVikCtFJe2DFq28dLZLaNbtZ524R9brEbokNcJAl6RGGOiS1AgDXZIaYaBLUiMMdElqhIEuSY0w0CWpEQa6JDXCQJekRhjoktQIA12SGmGgS1IjDHRJaoSBLkmNMNAlqREGuiQ1wkCXpEYY6JLUCANdkhphoEtSIwx0SWqEgS5JjRgo0JMsS3JrkvEkK6eYf3qSrUlu7B7vGn6pkqQdmTtTgyRzgNXA64DNwPoka6vq5klNL6qqFXugRknSAAY5Ql8KjFfVxqp6BFgDnLxny5Ik7axBAn0+sKlvfHM3bbI3J/l6ks8lWTjVipIsTzKWZGzr1q27UK4kaTrDOin618Ciqno58HfABVM1qqpzq2q0qkZHRkaGtGlJEgwW6FuA/iPuBd20J1TVvVX1cDf6F8DPDac8SdKgBgn09cDiJIclmQecCqztb5DkBX2jJwG3DK9ESdIgZvyVS1VtS7ICuAyYA5xXVRuSrALGqmot8NtJTgK2AfcBp+/BmiVJU5gx0AGqah2wbtK0M/uGPwR8aLilSZJ2hleKSlIjDHRJaoSBLkmNMNAlqREGuiQ1wkCXpEYY6JLUCANdkhphoEtSIwx0SWqEgS5JjTDQJakRBrokNcJAl6RGGOiS1AgDXZIaYaBLUiMMdElqhIEuSY0w0CWpEQa6JDXCQJekRhjoktQIA12SGmGgS1IjDHRJaoSBLkmNGCjQkyxLcmuS8SQrd9DuzUkqyejwSpQkDWLGQE8yB1gNnAAsAU5LsmSKds8GzgCuHXaRkqSZDXKEvhQYr6qNVfUIsAY4eYp2fwh8DPjxEOuTJA1okECfD2zqG9/cTXtCklcAC6vq0h2tKMnyJGNJxrZu3brTxUqSprfbJ0WT7Ad8AnjfTG2r6tyqGq2q0ZGRkd3dtCSpzyCBvgVY2De+oJs24dnAy4DLk9wOvBpY64lRSXpqDRLo64HFSQ5LMg84FVg7MbOqHqyqg6tqUVUtAq4BTqqqsT1SsSRpSjMGelVtA1YAlwG3ABdX1YYkq5KctKcLlCQNZu4gjapqHbBu0rQzp2l77O6XJUnaWV4pKkmNMNAlqREGuiQ1wkCXpEYY6JLUCANdkhphoEtSIwx0SWqEgS5JjTDQJakRBrokNcJAl6RGGOiS1AgDXZIaYaBLUiMMdElqhIEuSY0w0CWpEQa6JDXCQJekRhjoktQIA12SGmGgS1IjDHRJaoSBLkmNMNAlqREGuiQ1YqBAT7Isya1JxpOsnGL+e5J8I8mNSa5OsmT4pUqSdmTGQE8yB1gNnAAsAU6bIrA/W1X/sqqOBD4OfGLolUqSdmiQI/SlwHhVbayqR4A1wMn9Darq//aNHgDU8EqUJA1i7gBt5gOb+sY3A6+a3CjJbwG/C8wDjptqRUmWA8sBDj300J2tVZK0A0M7KVpVq6vqcOCDwIenaXNuVY1W1ejIyMiwNi1JYrBA3wIs7Btf0E2bzhrgjbtTlCRp5w0S6OuBxUkOSzIPOBVY298gyeK+0ROB24ZXoiRpEDP2oVfVtiQrgMuAOcB5VbUhySpgrKrWAiuSHA88CtwPvH1PFi1JerJBTopSVeuAdZOmndk3fMaQ65Ik7SSvFJWkRhjoktQIA12SGmGgS1IjDHRJaoSBLkmNMNAlqREGuiQ1wkCXpEYY6JLUCANdkhphoEtSIwx0SWqEgS5JjTDQJakRBrokNcJAl6RGGOiS1AgDXZIaYaBLUiMMdElqhIEuSY0w0CWpEQa6JDXCQJekRhjoktSIubNdwK5YtPLS2S6hWbefdeIeWa+v2Z6zp14z7X0GOkJPsizJrUnGk6ycYv7vJrk5ydeTfCnJC4dfqiRpR2YM9CRzgNXACcAS4LQkSyY1+0dgtKpeDnwO+PiwC5Uk7dggR+hLgfGq2lhVjwBrgJP7G1TVV6rqR93oNcCC4ZYpSZrJIIE+H9jUN765mzad3wD+ZqoZSZYnGUsytnXr1sGrlCTNaKi/cknya8AocPZU86vq3KoararRkZGRYW5akvZ5g/zKZQuwsG98QTdtO0mOB34f+IWqeng45UmSBjXIEfp6YHGSw5LMA04F1vY3SHIUcA5wUlV9f/hlSpJmMmOgV9U2YAVwGXALcHFVbUiyKslJXbOzgQOBv0pyY5K106xOkrSHDHRhUVWtA9ZNmnZm3/DxQ65LkrSTvPRfkhphoEtSIwx0SWqEgS5JjTDQJakRBrokNcJAl6RGGOiS1AgDXZIaYaBLUiMMdElqhIEuSY0w0CWpEQa6JDXCQJekRhjoktQIA12SGmGgS1IjDHRJaoSBLkmNMNAlqREGuiQ1wkCXpEYY6JLUCANdkhphoEtSIwx0SWrEQIGeZFmSW5OMJ1k5xfzXJLkhybYkbxl+mZKkmcwY6EnmAKuBE4AlwGlJlkxq9j3gdOCzwy5QkjSYuQO0WQqMV9VGgCRrgJOBmycaVNXt3bzH90CNkqQBDNLlMh/Y1De+uZu205IsTzKWZGzr1q27sgpJ0jSe0pOiVXVuVY1W1ejIyMhTuWlJat4ggb4FWNg3vqCbJkl6Ghkk0NcDi5MclmQecCqwds+WJUnaWTMGelVtA1YAlwG3ABdX1YYkq5KcBJDklUk2A6cA5yTZsCeLliQ92SC/cqGq1gHrJk07s294Pb2uGEnSLPFKUUlqhIEuSY0w0CWpEQa6JDXCQJekRhjoktQIA12SGmGgS1IjDHRJaoSBLkmNMNAlqREGuiQ1wkCXpEYY6JLUCANdkhphoEtSIwx0SWqEgS5JjTDQJakRBrokNcJAl6RGGOiS1AgDXZIaYaBLUiMMdElqhIEuSY0w0CWpEQMFepJlSW5NMp5k5RTz909yUTf/2iSLhl2oJGnHZgz0JHOA1cAJwBLgtCRLJjX7DeD+qjoC+BPgY8MuVJK0Y4McoS8FxqtqY1U9AqwBTp7U5mTggm74c8Brk2R4ZUqSZjJ3gDbzgU1945uBV03Xpqq2JXkQeC5wT3+jJMuB5d3oQ0lu3ZWi90IHM2lfPF3F71awF71e4GvW2ZdesxdON2OQQB+aqjoXOPep3ObTQZKxqhqd7To0GF+vvY+vWc8gXS5bgIV94wu6aVO2STIX+Eng3mEUKEkazCCBvh5YnOSwJPOAU4G1k9qsBd7eDb8F+HJV1fDKlCTNZMYul65PfAVwGTAHOK+qNiRZBYxV1VrgvwOfSTIO3Ecv9PXP9rlupr2cr9fex9cMiAfSktQGrxSVpEYY6JLUCANd0tNWktOTfHK269hbGOiS1AgDfTclWZXkvX3jf5TkjCTvT7I+ydeT/Kdu3gFJLk1yU5JvJnnr7FUugCSLktyS5NNJNiT52yQ/keTIJNd0r98lSZ4z27W2oNvf3+wb/70kH0lyeZKPJbkuybeTHDPFsicm+VqSg5Ocn+S/Jflqko1J3tK1SZKzu7+vb0z8jSVZneSkbviSJOd1w+/s/manfB88NXtleAz03Xce8OsASfaj95PNu4DF9O6DcyTwc0leAywD7qyqn62qlwH/Z3ZK1iSLgdVV9VLgAeDNwF8CH6yqlwPfAP7jLNa3r5hbVUuB9zJpfyf5FWAl8PqqmrjE/wXA0cAvA2d1095E72/uZ4HjgbOTvAC4Cpj4kJhP70aDdNOu7Ianeh/sVQz03VRVtwP3JjkK+CXgH4FX9g3fAPwLem+WbwCv645EjqmqB2enak3y3aq6sRu+HjgcOKiqruimXQC8ZlYq27d8vvv3emBR3/TjgA8CJ1bV/X3Tv1BVj1fVzcDzu2lHAxdW1WNVdTdwBb2/x6uAY7o7xd4M3N0F/c8DX+2Wnfw+6K9hr/CU3sulYX8BnA78NL0j9tcCf1xV50xumOQVwOuBjyb5UlWteioL1ZQe7ht+DDhotgrZB2xj+wPJZ/YNT7wOj7F9Nn0H+BngRcDYFO0Bdnh316rakuQget+SrwR+CvhV4KGq+kGS5/Lk94FdLvuoS+i9UV5J74ray4B3JjkQIMn8JM9Lcgjwo6r6H8DZwCtmq2Dt0IPA/X39uP+G3pGedt/dwPOSPDfJ/vS6S2ZyB103WJKXztD2KuCtSeYkGaH3zeq6bt419Lpzruza/V73bzM8Qh+CqnokyVeAB6rqMeBvk7wE+Fp3W/iHgF8DjqDXp/c48Cjwm7NVs2b0duBTSZ4FbATeMcv1NKGqHu1uG3IdvZv6fWvA5b6V5G3AXyV5ww6aXkKvG+UmoIAPVNVd3byrgF+qqvEkd9A7Sm8q0L30fwi6k6E3AKdU1W2zXY+kfZNdLrupO8kyDnzJMJc0mzxCl6RGeIQuSY0w0CWpEQa6JDXCQJekRhjoktSI/w/TIK6YopbnBgAAAABJRU5ErkJggg==", + "text/plain": [ + "
" + ] + }, + "metadata": { + "needs_background": "light", + "tags": [] + }, + "output_type": "display_data" + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "/content/data/mini_speech_commands/unknown/4a1e736b_nohash_4.wav\n" + ] + }, + { + "data": { + "image/png": "iVBORw0KGgoAAAANSUhEUgAAAXQAAAEICAYAAABPgw/pAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAALEgAACxIB0t1+/AAAADh0RVh0U29mdHdhcmUAbWF0cGxvdGxpYiB2ZXJzaW9uMy4yLjIsIGh0dHA6Ly9tYXRwbG90bGliLm9yZy+WH4yJAAAUd0lEQVR4nO3df7RdZX3n8feHxGAFV7FytZIEQyE6RseCvUa7BixF7ApSwapUGDsVrWbsNFNsrRqnlnFSO0WZZWdNTSvYYUGdkUAd0XRIh7Yqv6pALhTUgMg1gkkoGH6O6AgEvvPH2ZeeXO7NPUlOuOTJ+7XWWdk/nr339+xz7ufs8+yzd1JVSJL2fvvNdgGSpOEw0CWpEQa6JDXCQJekRhjoktQIA12SGmGga7ckOT/JR7vhY5Lcuovr+VSSPxhudTNu81eSbEryUJKjnsptDypJJTlituvQ3mHubBegPS/J7cDzgceAHwJ/A6yoqoeGuZ2qugp48QD1nA68q6qO7lv2PcOsZUD/hd5++OIwVpbkcuAjwLEAVfWRYaz36SrJ+cDl3eixVXX6rBUjwCP0fckbqupA4BXAKPDhyQ2S7Gsf8C8ENuzKgknmDLkWabcZ6PuYqtpC7wj9ZfDEV/rfSnIbcFs37ZeT3JjkgSRfTfLyieWTHJXkhiQ/SHIR8My+eccm2dw3vjDJ55NsTXJvkk8meQnwKeDnu66OB7q2T3TddOPvTjKe5L4ka5Mc0jevkrwnyW1djauTpJt3RJIrkjyY5J6uxu0k2T/JQ8Ac4KYk3+mmvyTJ5d06NyQ5qW+Z85P8eZJ1SX4I/OIg+zvJ6UmunjTtiW6Ubr2rk1za7dNrkxw+zbqO7rqIjh1gP+yX5MNJ7kjy/SR/meQnu3kXJHlfNzx/4j3QjR/e7fP9Jl7PJO/r1vFPSd4xyPPWLKkqH40/gNuB47vhhfSOSv+wGy/g74CfAn4COAr4PvAqeoH39m75/YF5wB3A7wDPAN4CPAp8tFvXscDmbngOcBPwJ8AB9IL/6G7e6cDVk2o8v289xwH30Ps2sT/wp8CVfW0L+N/AQcChwFZgWTfvQuD36R2sPLHNafZLAUd0w88AxoH/0D3P44AfAC/uq+9B4F9NrHvAfT/Vc+3f7vnAvcBSel2g/xNYM7ktsAzYBCwdcD+8s3s+PwMcCHwe+EzfvL/uhv818B3gor55X+x7PbcBq7r983rgR8BzZvs97WPqh0fo+44vdEfDVwNXAP+5b94fV9V9VfX/gOXAOVV1bVU9VlUXAA8Dr+4ezwD+a1U9WlWfA9ZPs72lwCHA+6vqh1X146q6epq2k70NOK+qbqiqh4EP0TuiX9TX5qyqeqCqvgd8BTiym/4ova6UQ3Zym6+mF3xnVdUjVfVlemF5Wl+bL1bVP1TV41X14wHXO4hLquq6qtpGL9CPnDT/FOAc4ISqum7SvOn2w9uAT1TVxuqdK/kQcGrXrXYFcHSS/YDXAB+n90EF8Avd/AmPAqu613sd8BADnCfR7DDQ9x1vrKqDquqFVfXvuvCesKlv+IXA+7qv8A90HwIL6YXzIcCWquq/o9sd02xvIXBHF1I765D+9XaBdC8wv6/NXX3DP6IXxgAfAAJc13WbvHMntrmpqh7vm3bHpG1uYs+Y7rlMeC9wcVV9cyeW3W4fdsNzgedX1XfonRw/EjiG3gfXnUlezJMD/d5Jr+FU9elpwkAX9L66T9gE/FEX/hOPZ1XVhcA/AfMn+mk7h06zzk3AodOcaJ3pFp930vtgASDJAcBzgS0zPpGqu6rq3VV1CPBvgT8b8Gd/dwILu6PWCYdO2uau3Jr0h8CzJkaS/PQurOMU4I1JztiJZbbbh/Seyzbg7m78CnpdZvOqd17lCnrda88BbtyFGvU0YKBrsk8D70nyqvQckOTEJM8GvkYvFH47yTOSvIle18pUrqP3AXBWt45nJpn4Wn83sCDJvGmWvRB4R5Ijk+xPr3vo2qq6fabik5ySZEE3ej+9EH58B4tMuJbe0ecHuud2LPAGYM0Ay+7ITcBLu+fyTHo/a9xZdwKvBc5I8psDLnMh8DtJDktyIL19eFHf0fYVwArgym788m786qp6bBdq1NOAga7tVNUY8G7gk/QCcZzeiT2q6hHgTd34fcBb6Z1sm2o9j9ELxCOA7wGbu/YAX6Z3YvauJPdMsezfA38A/C96HwqHA6cO+BReCVzb/YplLXBGVW2caaHuub0BOIHeCdk/A369qr414HanW++36Z1U/Ht6vyIatE9/8nq+Ry/UVyZ51wCLnAd8hl5gfxf4MfDv++ZfATybfw70q+l9k7gS7bWyfXeoJGlv5RG6JDXCQJekRhjoktSIgQI9ybIkt3aXYq+cps2vJrm5++3vZ4dbpiRpJjOeFE3vJkTfBl5H75cK64HTqurmvjaLgYuB46rq/iTPq6rv72i9Bx98cC1atGg3y5ekfcv1119/T1WNTDVvkLvrLQXGJ376lWQNcDJwc1+bdwOrq+p+gJnCHGDRokWMjY0NsHlJ0oQk012dPVCXy3y2v+R5M9tfDg3wIuBFSf4hyTVJlk1TyPIkY0nGtm7dOsCmJUmDGtZJ0bnAYnp3ZzsN+HSSgyY3qqpzq2q0qkZHRqb8xiBJ2kWDBPoWejdamrCAJ99TYzOwtrsj23fp9bkvHk6JkqRBDBLo64HF3T0h5tG7BHvtpDZfoPtvt5IcTK8LZsbLrSVJwzNjoHc381kBXAbcQu82nhuSrOr7H10uA+5NcjO9ezK/v6ru3VNFS5KebNbu5TI6Olr+ykWSdk6S66tqdKp5XikqSY0w0CWpEQa6JDVikCtFJe2DFq28dLZLaNbtZ524R9brEbokNcJAl6RGGOiS1AgDXZIaYaBLUiMMdElqhIEuSY0w0CWpEQa6JDXCQJekRhjoktQIA12SGmGgS1IjDHRJaoSBLkmNMNAlqREGuiQ1wkCXpEYY6JLUCANdkhphoEtSIwx0SWqEgS5JjRgo0JMsS3JrkvEkK6eYf3qSrUlu7B7vGn6pkqQdmTtTgyRzgNXA64DNwPoka6vq5klNL6qqFXugRknSAAY5Ql8KjFfVxqp6BFgDnLxny5Ik7axBAn0+sKlvfHM3bbI3J/l6ks8lWTjVipIsTzKWZGzr1q27UK4kaTrDOin618Ciqno58HfABVM1qqpzq2q0qkZHRkaGtGlJEgwW6FuA/iPuBd20J1TVvVX1cDf6F8DPDac8SdKgBgn09cDiJIclmQecCqztb5DkBX2jJwG3DK9ESdIgZvyVS1VtS7ICuAyYA5xXVRuSrALGqmot8NtJTgK2AfcBp+/BmiVJU5gx0AGqah2wbtK0M/uGPwR8aLilSZJ2hleKSlIjDHRJaoSBLkmNMNAlqREGuiQ1wkCXpEYY6JLUCANdkhphoEtSIwx0SWqEgS5JjTDQJakRBrokNcJAl6RGGOiS1AgDXZIaYaBLUiMMdElqhIEuSY0w0CWpEQa6JDXCQJekRhjoktQIA12SGmGgS1IjDHRJaoSBLkmNGCjQkyxLcmuS8SQrd9DuzUkqyejwSpQkDWLGQE8yB1gNnAAsAU5LsmSKds8GzgCuHXaRkqSZDXKEvhQYr6qNVfUIsAY4eYp2fwh8DPjxEOuTJA1okECfD2zqG9/cTXtCklcAC6vq0h2tKMnyJGNJxrZu3brTxUqSprfbJ0WT7Ad8AnjfTG2r6tyqGq2q0ZGRkd3dtCSpzyCBvgVY2De+oJs24dnAy4DLk9wOvBpY64lRSXpqDRLo64HFSQ5LMg84FVg7MbOqHqyqg6tqUVUtAq4BTqqqsT1SsSRpSjMGelVtA1YAlwG3ABdX1YYkq5KctKcLlCQNZu4gjapqHbBu0rQzp2l77O6XJUnaWV4pKkmNMNAlqREGuiQ1wkCXpEYY6JLUCANdkhphoEtSIwx0SWqEgS5JjTDQJakRBrokNcJAl6RGGOiS1AgDXZIaYaBLUiMMdElqhIEuSY0w0CWpEQa6JDXCQJekRhjoktQIA12SGmGgS1IjDHRJaoSBLkmNMNAlqREGuiQ1YqBAT7Isya1JxpOsnGL+e5J8I8mNSa5OsmT4pUqSdmTGQE8yB1gNnAAsAU6bIrA/W1X/sqqOBD4OfGLolUqSdmiQI/SlwHhVbayqR4A1wMn9Darq//aNHgDU8EqUJA1i7gBt5gOb+sY3A6+a3CjJbwG/C8wDjptqRUmWA8sBDj300J2tVZK0A0M7KVpVq6vqcOCDwIenaXNuVY1W1ejIyMiwNi1JYrBA3wIs7Btf0E2bzhrgjbtTlCRp5w0S6OuBxUkOSzIPOBVY298gyeK+0ROB24ZXoiRpEDP2oVfVtiQrgMuAOcB5VbUhySpgrKrWAiuSHA88CtwPvH1PFi1JerJBTopSVeuAdZOmndk3fMaQ65Ik7SSvFJWkRhjoktQIA12SGmGgS1IjDHRJaoSBLkmNMNAlqREGuiQ1wkCXpEYY6JLUCANdkhphoEtSIwx0SWqEgS5JjTDQJakRBrokNcJAl6RGGOiS1AgDXZIaYaBLUiMMdElqhIEuSY0w0CWpEQa6JDXCQJekRhjoktSIubNdwK5YtPLS2S6hWbefdeIeWa+v2Z6zp14z7X0GOkJPsizJrUnGk6ycYv7vJrk5ydeTfCnJC4dfqiRpR2YM9CRzgNXACcAS4LQkSyY1+0dgtKpeDnwO+PiwC5Uk7dggR+hLgfGq2lhVjwBrgJP7G1TVV6rqR93oNcCC4ZYpSZrJIIE+H9jUN765mzad3wD+ZqoZSZYnGUsytnXr1sGrlCTNaKi/cknya8AocPZU86vq3KoararRkZGRYW5akvZ5g/zKZQuwsG98QTdtO0mOB34f+IWqeng45UmSBjXIEfp6YHGSw5LMA04F1vY3SHIUcA5wUlV9f/hlSpJmMmOgV9U2YAVwGXALcHFVbUiyKslJXbOzgQOBv0pyY5K106xOkrSHDHRhUVWtA9ZNmnZm3/DxQ65LkrSTvPRfkhphoEtSIwx0SWqEgS5JjTDQJakRBrokNcJAl6RGGOiS1AgDXZIaYaBLUiMMdElqhIEuSY0w0CWpEQa6JDXCQJekRhjoktQIA12SGmGgS1IjDHRJaoSBLkmNMNAlqREGuiQ1wkCXpEYY6JLUCANdkhphoEtSIwx0SWrEQIGeZFmSW5OMJ1k5xfzXJLkhybYkbxl+mZKkmcwY6EnmAKuBE4AlwGlJlkxq9j3gdOCzwy5QkjSYuQO0WQqMV9VGgCRrgJOBmycaVNXt3bzH90CNkqQBDNLlMh/Y1De+uZu205IsTzKWZGzr1q27sgpJ0jSe0pOiVXVuVY1W1ejIyMhTuWlJat4ggb4FWNg3vqCbJkl6Ghkk0NcDi5MclmQecCqwds+WJUnaWTMGelVtA1YAlwG3ABdX1YYkq5KcBJDklUk2A6cA5yTZsCeLliQ92SC/cqGq1gHrJk07s294Pb2uGEnSLPFKUUlqhIEuSY0w0CWpEQa6JDXCQJekRhjoktQIA12SGmGgS1IjDHRJaoSBLkmNMNAlqREGuiQ1wkCXpEYY6JLUCANdkhphoEtSIwx0SWqEgS5JjTDQJakRBrokNcJAl6RGGOiS1AgDXZIaYaBLUiMMdElqhIEuSY0w0CWpEQMFepJlSW5NMp5k5RTz909yUTf/2iSLhl2oJGnHZgz0JHOA1cAJwBLgtCRLJjX7DeD+qjoC+BPgY8MuVJK0Y4McoS8FxqtqY1U9AqwBTp7U5mTggm74c8Brk2R4ZUqSZjJ3gDbzgU1945uBV03Xpqq2JXkQeC5wT3+jJMuB5d3oQ0lu3ZWi90IHM2lfPF3F71awF71e4GvW2ZdesxdON2OQQB+aqjoXOPep3ObTQZKxqhqd7To0GF+vvY+vWc8gXS5bgIV94wu6aVO2STIX+Eng3mEUKEkazCCBvh5YnOSwJPOAU4G1k9qsBd7eDb8F+HJV1fDKlCTNZMYul65PfAVwGTAHOK+qNiRZBYxV1VrgvwOfSTIO3Ecv9PXP9rlupr2cr9fex9cMiAfSktQGrxSVpEYY6JLUCANd0tNWktOTfHK269hbGOiS1AgDfTclWZXkvX3jf5TkjCTvT7I+ydeT/Kdu3gFJLk1yU5JvJnnr7FUugCSLktyS5NNJNiT52yQ/keTIJNd0r98lSZ4z27W2oNvf3+wb/70kH0lyeZKPJbkuybeTHDPFsicm+VqSg5Ocn+S/Jflqko1J3tK1SZKzu7+vb0z8jSVZneSkbviSJOd1w+/s/manfB88NXtleAz03Xce8OsASfaj95PNu4DF9O6DcyTwc0leAywD7qyqn62qlwH/Z3ZK1iSLgdVV9VLgAeDNwF8CH6yqlwPfAP7jLNa3r5hbVUuB9zJpfyf5FWAl8PqqmrjE/wXA0cAvA2d1095E72/uZ4HjgbOTvAC4Cpj4kJhP70aDdNOu7Ianeh/sVQz03VRVtwP3JjkK+CXgH4FX9g3fAPwLem+WbwCv645EjqmqB2enak3y3aq6sRu+HjgcOKiqruimXQC8ZlYq27d8vvv3emBR3/TjgA8CJ1bV/X3Tv1BVj1fVzcDzu2lHAxdW1WNVdTdwBb2/x6uAY7o7xd4M3N0F/c8DX+2Wnfw+6K9hr/CU3sulYX8BnA78NL0j9tcCf1xV50xumOQVwOuBjyb5UlWteioL1ZQe7ht+DDhotgrZB2xj+wPJZ/YNT7wOj7F9Nn0H+BngRcDYFO0Bdnh316rakuQget+SrwR+CvhV4KGq+kGS5/Lk94FdLvuoS+i9UV5J74ray4B3JjkQIMn8JM9Lcgjwo6r6H8DZwCtmq2Dt0IPA/X39uP+G3pGedt/dwPOSPDfJ/vS6S2ZyB103WJKXztD2KuCtSeYkGaH3zeq6bt419Lpzruza/V73bzM8Qh+CqnokyVeAB6rqMeBvk7wE+Fp3W/iHgF8DjqDXp/c48Cjwm7NVs2b0duBTSZ4FbATeMcv1NKGqHu1uG3IdvZv6fWvA5b6V5G3AXyV5ww6aXkKvG+UmoIAPVNVd3byrgF+qqvEkd9A7Sm8q0L30fwi6k6E3AKdU1W2zXY+kfZNdLrupO8kyDnzJMJc0mzxCl6RGeIQuSY0w0CWpEQa6JDXCQJekRhjoktSI/w/TIK6YopbnBgAAAABJRU5ErkJggg==", + "text/plain": [ + "
" + ] + }, + "metadata": { + "needs_background": "light", + "tags": [] + }, + "output_type": "display_data" + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "/content/data/mini_speech_commands/unknown/8012c69d_nohash_2.wav\n" + ] + }, + { + "data": { + "image/png": "iVBORw0KGgoAAAANSUhEUgAAAXQAAAEICAYAAABPgw/pAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAALEgAACxIB0t1+/AAAADh0RVh0U29mdHdhcmUAbWF0cGxvdGxpYiB2ZXJzaW9uMy4yLjIsIGh0dHA6Ly9tYXRwbG90bGliLm9yZy+WH4yJAAAUd0lEQVR4nO3df7RdZX3n8feHxGAFV7FytZIEQyE6RseCvUa7BixF7ApSwapUGDsVrWbsNFNsrRqnlnFSO0WZZWdNTSvYYUGdkUAd0XRIh7Yqv6pALhTUgMg1gkkoGH6O6AgEvvPH2ZeeXO7NPUlOuOTJ+7XWWdk/nr339+xz7ufs8+yzd1JVSJL2fvvNdgGSpOEw0CWpEQa6JDXCQJekRhjoktQIA12SGmGga7ckOT/JR7vhY5Lcuovr+VSSPxhudTNu81eSbEryUJKjnsptDypJJTlituvQ3mHubBegPS/J7cDzgceAHwJ/A6yoqoeGuZ2qugp48QD1nA68q6qO7lv2PcOsZUD/hd5++OIwVpbkcuAjwLEAVfWRYaz36SrJ+cDl3eixVXX6rBUjwCP0fckbqupA4BXAKPDhyQ2S7Gsf8C8ENuzKgknmDLkWabcZ6PuYqtpC7wj9ZfDEV/rfSnIbcFs37ZeT3JjkgSRfTfLyieWTHJXkhiQ/SHIR8My+eccm2dw3vjDJ55NsTXJvkk8meQnwKeDnu66OB7q2T3TddOPvTjKe5L4ka5Mc0jevkrwnyW1djauTpJt3RJIrkjyY5J6uxu0k2T/JQ8Ac4KYk3+mmvyTJ5d06NyQ5qW+Z85P8eZJ1SX4I/OIg+zvJ6UmunjTtiW6Ubr2rk1za7dNrkxw+zbqO7rqIjh1gP+yX5MNJ7kjy/SR/meQnu3kXJHlfNzx/4j3QjR/e7fP9Jl7PJO/r1vFPSd4xyPPWLKkqH40/gNuB47vhhfSOSv+wGy/g74CfAn4COAr4PvAqeoH39m75/YF5wB3A7wDPAN4CPAp8tFvXscDmbngOcBPwJ8AB9IL/6G7e6cDVk2o8v289xwH30Ps2sT/wp8CVfW0L+N/AQcChwFZgWTfvQuD36R2sPLHNafZLAUd0w88AxoH/0D3P44AfAC/uq+9B4F9NrHvAfT/Vc+3f7vnAvcBSel2g/xNYM7ktsAzYBCwdcD+8s3s+PwMcCHwe+EzfvL/uhv818B3gor55X+x7PbcBq7r983rgR8BzZvs97WPqh0fo+44vdEfDVwNXAP+5b94fV9V9VfX/gOXAOVV1bVU9VlUXAA8Dr+4ezwD+a1U9WlWfA9ZPs72lwCHA+6vqh1X146q6epq2k70NOK+qbqiqh4EP0TuiX9TX5qyqeqCqvgd8BTiym/4ova6UQ3Zym6+mF3xnVdUjVfVlemF5Wl+bL1bVP1TV41X14wHXO4hLquq6qtpGL9CPnDT/FOAc4ISqum7SvOn2w9uAT1TVxuqdK/kQcGrXrXYFcHSS/YDXAB+n90EF8Avd/AmPAqu613sd8BADnCfR7DDQ9x1vrKqDquqFVfXvuvCesKlv+IXA+7qv8A90HwIL6YXzIcCWquq/o9sd02xvIXBHF1I765D+9XaBdC8wv6/NXX3DP6IXxgAfAAJc13WbvHMntrmpqh7vm3bHpG1uYs+Y7rlMeC9wcVV9cyeW3W4fdsNzgedX1XfonRw/EjiG3gfXnUlezJMD/d5Jr+FU9elpwkAX9L66T9gE/FEX/hOPZ1XVhcA/AfMn+mk7h06zzk3AodOcaJ3pFp930vtgASDJAcBzgS0zPpGqu6rq3VV1CPBvgT8b8Gd/dwILu6PWCYdO2uau3Jr0h8CzJkaS/PQurOMU4I1JztiJZbbbh/Seyzbg7m78CnpdZvOqd17lCnrda88BbtyFGvU0YKBrsk8D70nyqvQckOTEJM8GvkYvFH47yTOSvIle18pUrqP3AXBWt45nJpn4Wn83sCDJvGmWvRB4R5Ijk+xPr3vo2qq6fabik5ySZEE3ej+9EH58B4tMuJbe0ecHuud2LPAGYM0Ay+7ITcBLu+fyTHo/a9xZdwKvBc5I8psDLnMh8DtJDktyIL19eFHf0fYVwArgym788m786qp6bBdq1NOAga7tVNUY8G7gk/QCcZzeiT2q6hHgTd34fcBb6Z1sm2o9j9ELxCOA7wGbu/YAX6Z3YvauJPdMsezfA38A/C96HwqHA6cO+BReCVzb/YplLXBGVW2caaHuub0BOIHeCdk/A369qr414HanW++36Z1U/Ht6vyIatE9/8nq+Ry/UVyZ51wCLnAd8hl5gfxf4MfDv++ZfATybfw70q+l9k7gS7bWyfXeoJGlv5RG6JDXCQJekRhjoktSIgQI9ybIkt3aXYq+cps2vJrm5++3vZ4dbpiRpJjOeFE3vJkTfBl5H75cK64HTqurmvjaLgYuB46rq/iTPq6rv72i9Bx98cC1atGg3y5ekfcv1119/T1WNTDVvkLvrLQXGJ376lWQNcDJwc1+bdwOrq+p+gJnCHGDRokWMjY0NsHlJ0oQk012dPVCXy3y2v+R5M9tfDg3wIuBFSf4hyTVJlk1TyPIkY0nGtm7dOsCmJUmDGtZJ0bnAYnp3ZzsN+HSSgyY3qqpzq2q0qkZHRqb8xiBJ2kWDBPoWejdamrCAJ99TYzOwtrsj23fp9bkvHk6JkqRBDBLo64HF3T0h5tG7BHvtpDZfoPtvt5IcTK8LZsbLrSVJwzNjoHc381kBXAbcQu82nhuSrOr7H10uA+5NcjO9ezK/v6ru3VNFS5KebNbu5TI6Olr+ykWSdk6S66tqdKp5XikqSY0w0CWpEQa6JDVikCtFJe2DFq28dLZLaNbtZ524R9brEbokNcJAl6RGGOiS1AgDXZIaYaBLUiMMdElqhIEuSY0w0CWpEQa6JDXCQJekRhjoktQIA12SGmGgS1IjDHRJaoSBLkmNMNAlqREGuiQ1wkCXpEYY6JLUCANdkhphoEtSIwx0SWqEgS5JjRgo0JMsS3JrkvEkK6eYf3qSrUlu7B7vGn6pkqQdmTtTgyRzgNXA64DNwPoka6vq5klNL6qqFXugRknSAAY5Ql8KjFfVxqp6BFgDnLxny5Ik7axBAn0+sKlvfHM3bbI3J/l6ks8lWTjVipIsTzKWZGzr1q27UK4kaTrDOin618Ciqno58HfABVM1qqpzq2q0qkZHRkaGtGlJEgwW6FuA/iPuBd20J1TVvVX1cDf6F8DPDac8SdKgBgn09cDiJIclmQecCqztb5DkBX2jJwG3DK9ESdIgZvyVS1VtS7ICuAyYA5xXVRuSrALGqmot8NtJTgK2AfcBp+/BmiVJU5gx0AGqah2wbtK0M/uGPwR8aLilSZJ2hleKSlIjDHRJaoSBLkmNMNAlqREGuiQ1wkCXpEYY6JLUCANdkhphoEtSIwx0SWqEgS5JjTDQJakRBrokNcJAl6RGGOiS1AgDXZIaYaBLUiMMdElqhIEuSY0w0CWpEQa6JDXCQJekRhjoktQIA12SGmGgS1IjDHRJaoSBLkmNGCjQkyxLcmuS8SQrd9DuzUkqyejwSpQkDWLGQE8yB1gNnAAsAU5LsmSKds8GzgCuHXaRkqSZDXKEvhQYr6qNVfUIsAY4eYp2fwh8DPjxEOuTJA1okECfD2zqG9/cTXtCklcAC6vq0h2tKMnyJGNJxrZu3brTxUqSprfbJ0WT7Ad8AnjfTG2r6tyqGq2q0ZGRkd3dtCSpzyCBvgVY2De+oJs24dnAy4DLk9wOvBpY64lRSXpqDRLo64HFSQ5LMg84FVg7MbOqHqyqg6tqUVUtAq4BTqqqsT1SsSRpSjMGelVtA1YAlwG3ABdX1YYkq5KctKcLlCQNZu4gjapqHbBu0rQzp2l77O6XJUnaWV4pKkmNMNAlqREGuiQ1wkCXpEYY6JLUCANdkhphoEtSIwx0SWqEgS5JjTDQJakRBrokNcJAl6RGGOiS1AgDXZIaYaBLUiMMdElqhIEuSY0w0CWpEQa6JDXCQJekRhjoktQIA12SGmGgS1IjDHRJaoSBLkmNMNAlqREGuiQ1YqBAT7Isya1JxpOsnGL+e5J8I8mNSa5OsmT4pUqSdmTGQE8yB1gNnAAsAU6bIrA/W1X/sqqOBD4OfGLolUqSdmiQI/SlwHhVbayqR4A1wMn9Darq//aNHgDU8EqUJA1i7gBt5gOb+sY3A6+a3CjJbwG/C8wDjptqRUmWA8sBDj300J2tVZK0A0M7KVpVq6vqcOCDwIenaXNuVY1W1ejIyMiwNi1JYrBA3wIs7Btf0E2bzhrgjbtTlCRp5w0S6OuBxUkOSzIPOBVY298gyeK+0ROB24ZXoiRpEDP2oVfVtiQrgMuAOcB5VbUhySpgrKrWAiuSHA88CtwPvH1PFi1JerJBTopSVeuAdZOmndk3fMaQ65Ik7SSvFJWkRhjoktQIA12SGmGgS1IjDHRJaoSBLkmNMNAlqREGuiQ1wkCXpEYY6JLUCANdkhphoEtSIwx0SWqEgS5JjTDQJakRBrokNcJAl6RGGOiS1AgDXZIaYaBLUiMMdElqhIEuSY0w0CWpEQa6JDXCQJekRhjoktSIubNdwK5YtPLS2S6hWbefdeIeWa+v2Z6zp14z7X0GOkJPsizJrUnGk6ycYv7vJrk5ydeTfCnJC4dfqiRpR2YM9CRzgNXACcAS4LQkSyY1+0dgtKpeDnwO+PiwC5Uk7dggR+hLgfGq2lhVjwBrgJP7G1TVV6rqR93oNcCC4ZYpSZrJIIE+H9jUN765mzad3wD+ZqoZSZYnGUsytnXr1sGrlCTNaKi/cknya8AocPZU86vq3KoararRkZGRYW5akvZ5g/zKZQuwsG98QTdtO0mOB34f+IWqeng45UmSBjXIEfp6YHGSw5LMA04F1vY3SHIUcA5wUlV9f/hlSpJmMmOgV9U2YAVwGXALcHFVbUiyKslJXbOzgQOBv0pyY5K106xOkrSHDHRhUVWtA9ZNmnZm3/DxQ65LkrSTvPRfkhphoEtSIwx0SWqEgS5JjTDQJakRBrokNcJAl6RGGOiS1AgDXZIaYaBLUiMMdElqhIEuSY0w0CWpEQa6JDXCQJekRhjoktQIA12SGmGgS1IjDHRJaoSBLkmNMNAlqREGuiQ1wkCXpEYY6JLUCANdkhphoEtSIwx0SWrEQIGeZFmSW5OMJ1k5xfzXJLkhybYkbxl+mZKkmcwY6EnmAKuBE4AlwGlJlkxq9j3gdOCzwy5QkjSYuQO0WQqMV9VGgCRrgJOBmycaVNXt3bzH90CNkqQBDNLlMh/Y1De+uZu205IsTzKWZGzr1q27sgpJ0jSe0pOiVXVuVY1W1ejIyMhTuWlJat4ggb4FWNg3vqCbJkl6Ghkk0NcDi5MclmQecCqwds+WJUnaWTMGelVtA1YAlwG3ABdX1YYkq5KcBJDklUk2A6cA5yTZsCeLliQ92SC/cqGq1gHrJk07s294Pb2uGEnSLPFKUUlqhIEuSY0w0CWpEQa6JDXCQJekRhjoktQIA12SGmGgS1IjDHRJaoSBLkmNMNAlqREGuiQ1wkCXpEYY6JLUCANdkhphoEtSIwx0SWqEgS5JjTDQJakRBrokNcJAl6RGGOiS1AgDXZIaYaBLUiMMdElqhIEuSY0w0CWpEQMFepJlSW5NMp5k5RTz909yUTf/2iSLhl2oJGnHZgz0JHOA1cAJwBLgtCRLJjX7DeD+qjoC+BPgY8MuVJK0Y4McoS8FxqtqY1U9AqwBTp7U5mTggm74c8Brk2R4ZUqSZjJ3gDbzgU1945uBV03Xpqq2JXkQeC5wT3+jJMuB5d3oQ0lu3ZWi90IHM2lfPF3F71awF71e4GvW2ZdesxdON2OQQB+aqjoXOPep3ObTQZKxqhqd7To0GF+vvY+vWc8gXS5bgIV94wu6aVO2STIX+Eng3mEUKEkazCCBvh5YnOSwJPOAU4G1k9qsBd7eDb8F+HJV1fDKlCTNZMYul65PfAVwGTAHOK+qNiRZBYxV1VrgvwOfSTIO3Ecv9PXP9rlupr2cr9fex9cMiAfSktQGrxSVpEYY6JLUCANd0tNWktOTfHK269hbGOiS1AgDfTclWZXkvX3jf5TkjCTvT7I+ydeT/Kdu3gFJLk1yU5JvJnnr7FUugCSLktyS5NNJNiT52yQ/keTIJNd0r98lSZ4z27W2oNvf3+wb/70kH0lyeZKPJbkuybeTHDPFsicm+VqSg5Ocn+S/Jflqko1J3tK1SZKzu7+vb0z8jSVZneSkbviSJOd1w+/s/manfB88NXtleAz03Xce8OsASfaj95PNu4DF9O6DcyTwc0leAywD7qyqn62qlwH/Z3ZK1iSLgdVV9VLgAeDNwF8CH6yqlwPfAP7jLNa3r5hbVUuB9zJpfyf5FWAl8PqqmrjE/wXA0cAvA2d1095E72/uZ4HjgbOTvAC4Cpj4kJhP70aDdNOu7Ianeh/sVQz03VRVtwP3JjkK+CXgH4FX9g3fAPwLem+WbwCv645EjqmqB2enak3y3aq6sRu+HjgcOKiqruimXQC8ZlYq27d8vvv3emBR3/TjgA8CJ1bV/X3Tv1BVj1fVzcDzu2lHAxdW1WNVdTdwBb2/x6uAY7o7xd4M3N0F/c8DX+2Wnfw+6K9hr/CU3sulYX8BnA78NL0j9tcCf1xV50xumOQVwOuBjyb5UlWteioL1ZQe7ht+DDhotgrZB2xj+wPJZ/YNT7wOj7F9Nn0H+BngRcDYFO0Bdnh316rakuQget+SrwR+CvhV4KGq+kGS5/Lk94FdLvuoS+i9UV5J74ray4B3JjkQIMn8JM9Lcgjwo6r6H8DZwCtmq2Dt0IPA/X39uP+G3pGedt/dwPOSPDfJ/vS6S2ZyB103WJKXztD2KuCtSeYkGaH3zeq6bt419Lpzruza/V73bzM8Qh+CqnokyVeAB6rqMeBvk7wE+Fp3W/iHgF8DjqDXp/c48Cjwm7NVs2b0duBTSZ4FbATeMcv1NKGqHu1uG3IdvZv6fWvA5b6V5G3AXyV5ww6aXkKvG+UmoIAPVNVd3byrgF+qqvEkd9A7Sm8q0L30fwi6k6E3AKdU1W2zXY+kfZNdLrupO8kyDnzJMJc0mzxCl6RGeIQuSY0w0CWpEQa6JDXCQJekRhjoktSI/w/TIK6YopbnBgAAAABJRU5ErkJggg==", + "text/plain": [ + "
" + ] + }, + "metadata": { + "needs_background": "light", + "tags": [] + }, + "output_type": "display_data" + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "/content/data/mini_speech_commands/unknown/71f6fed7_nohash_2.wav\n" + ] + }, + { + "data": { + "image/png": "iVBORw0KGgoAAAANSUhEUgAAAXQAAAEICAYAAABPgw/pAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAALEgAACxIB0t1+/AAAADh0RVh0U29mdHdhcmUAbWF0cGxvdGxpYiB2ZXJzaW9uMy4yLjIsIGh0dHA6Ly9tYXRwbG90bGliLm9yZy+WH4yJAAAUd0lEQVR4nO3df7RdZX3n8feHxGAFV7FytZIEQyE6RseCvUa7BixF7ApSwapUGDsVrWbsNFNsrRqnlnFSO0WZZWdNTSvYYUGdkUAd0XRIh7Yqv6pALhTUgMg1gkkoGH6O6AgEvvPH2ZeeXO7NPUlOuOTJ+7XWWdk/nr339+xz7ufs8+yzd1JVSJL2fvvNdgGSpOEw0CWpEQa6JDXCQJekRhjoktQIA12SGmGga7ckOT/JR7vhY5Lcuovr+VSSPxhudTNu81eSbEryUJKjnsptDypJJTlituvQ3mHubBegPS/J7cDzgceAHwJ/A6yoqoeGuZ2qugp48QD1nA68q6qO7lv2PcOsZUD/hd5++OIwVpbkcuAjwLEAVfWRYaz36SrJ+cDl3eixVXX6rBUjwCP0fckbqupA4BXAKPDhyQ2S7Gsf8C8ENuzKgknmDLkWabcZ6PuYqtpC7wj9ZfDEV/rfSnIbcFs37ZeT3JjkgSRfTfLyieWTHJXkhiQ/SHIR8My+eccm2dw3vjDJ55NsTXJvkk8meQnwKeDnu66OB7q2T3TddOPvTjKe5L4ka5Mc0jevkrwnyW1djauTpJt3RJIrkjyY5J6uxu0k2T/JQ8Ac4KYk3+mmvyTJ5d06NyQ5qW+Z85P8eZJ1SX4I/OIg+zvJ6UmunjTtiW6Ubr2rk1za7dNrkxw+zbqO7rqIjh1gP+yX5MNJ7kjy/SR/meQnu3kXJHlfNzx/4j3QjR/e7fP9Jl7PJO/r1vFPSd4xyPPWLKkqH40/gNuB47vhhfSOSv+wGy/g74CfAn4COAr4PvAqeoH39m75/YF5wB3A7wDPAN4CPAp8tFvXscDmbngOcBPwJ8AB9IL/6G7e6cDVk2o8v289xwH30Ps2sT/wp8CVfW0L+N/AQcChwFZgWTfvQuD36R2sPLHNafZLAUd0w88AxoH/0D3P44AfAC/uq+9B4F9NrHvAfT/Vc+3f7vnAvcBSel2g/xNYM7ktsAzYBCwdcD+8s3s+PwMcCHwe+EzfvL/uhv818B3gor55X+x7PbcBq7r983rgR8BzZvs97WPqh0fo+44vdEfDVwNXAP+5b94fV9V9VfX/gOXAOVV1bVU9VlUXAA8Dr+4ezwD+a1U9WlWfA9ZPs72lwCHA+6vqh1X146q6epq2k70NOK+qbqiqh4EP0TuiX9TX5qyqeqCqvgd8BTiym/4ova6UQ3Zym6+mF3xnVdUjVfVlemF5Wl+bL1bVP1TV41X14wHXO4hLquq6qtpGL9CPnDT/FOAc4ISqum7SvOn2w9uAT1TVxuqdK/kQcGrXrXYFcHSS/YDXAB+n90EF8Avd/AmPAqu613sd8BADnCfR7DDQ9x1vrKqDquqFVfXvuvCesKlv+IXA+7qv8A90HwIL6YXzIcCWquq/o9sd02xvIXBHF1I765D+9XaBdC8wv6/NXX3DP6IXxgAfAAJc13WbvHMntrmpqh7vm3bHpG1uYs+Y7rlMeC9wcVV9cyeW3W4fdsNzgedX1XfonRw/EjiG3gfXnUlezJMD/d5Jr+FU9elpwkAX9L66T9gE/FEX/hOPZ1XVhcA/AfMn+mk7h06zzk3AodOcaJ3pFp930vtgASDJAcBzgS0zPpGqu6rq3VV1CPBvgT8b8Gd/dwILu6PWCYdO2uau3Jr0h8CzJkaS/PQurOMU4I1JztiJZbbbh/Seyzbg7m78CnpdZvOqd17lCnrda88BbtyFGvU0YKBrsk8D70nyqvQckOTEJM8GvkYvFH47yTOSvIle18pUrqP3AXBWt45nJpn4Wn83sCDJvGmWvRB4R5Ijk+xPr3vo2qq6fabik5ySZEE3ej+9EH58B4tMuJbe0ecHuud2LPAGYM0Ay+7ITcBLu+fyTHo/a9xZdwKvBc5I8psDLnMh8DtJDktyIL19eFHf0fYVwArgym788m786qp6bBdq1NOAga7tVNUY8G7gk/QCcZzeiT2q6hHgTd34fcBb6Z1sm2o9j9ELxCOA7wGbu/YAX6Z3YvauJPdMsezfA38A/C96HwqHA6cO+BReCVzb/YplLXBGVW2caaHuub0BOIHeCdk/A369qr414HanW++36Z1U/Ht6vyIatE9/8nq+Ry/UVyZ51wCLnAd8hl5gfxf4MfDv++ZfATybfw70q+l9k7gS7bWyfXeoJGlv5RG6JDXCQJekRhjoktSIgQI9ybIkt3aXYq+cps2vJrm5++3vZ4dbpiRpJjOeFE3vJkTfBl5H75cK64HTqurmvjaLgYuB46rq/iTPq6rv72i9Bx98cC1atGg3y5ekfcv1119/T1WNTDVvkLvrLQXGJ376lWQNcDJwc1+bdwOrq+p+gJnCHGDRokWMjY0NsHlJ0oQk012dPVCXy3y2v+R5M9tfDg3wIuBFSf4hyTVJlk1TyPIkY0nGtm7dOsCmJUmDGtZJ0bnAYnp3ZzsN+HSSgyY3qqpzq2q0qkZHRqb8xiBJ2kWDBPoWejdamrCAJ99TYzOwtrsj23fp9bkvHk6JkqRBDBLo64HF3T0h5tG7BHvtpDZfoPtvt5IcTK8LZsbLrSVJwzNjoHc381kBXAbcQu82nhuSrOr7H10uA+5NcjO9ezK/v6ru3VNFS5KebNbu5TI6Olr+ykWSdk6S66tqdKp5XikqSY0w0CWpEQa6JDVikCtFJe2DFq28dLZLaNbtZ524R9brEbokNcJAl6RGGOiS1AgDXZIaYaBLUiMMdElqhIEuSY0w0CWpEQa6JDXCQJekRhjoktQIA12SGmGgS1IjDHRJaoSBLkmNMNAlqREGuiQ1wkCXpEYY6JLUCANdkhphoEtSIwx0SWqEgS5JjRgo0JMsS3JrkvEkK6eYf3qSrUlu7B7vGn6pkqQdmTtTgyRzgNXA64DNwPoka6vq5klNL6qqFXugRknSAAY5Ql8KjFfVxqp6BFgDnLxny5Ik7axBAn0+sKlvfHM3bbI3J/l6ks8lWTjVipIsTzKWZGzr1q27UK4kaTrDOin618Ciqno58HfABVM1qqpzq2q0qkZHRkaGtGlJEgwW6FuA/iPuBd20J1TVvVX1cDf6F8DPDac8SdKgBgn09cDiJIclmQecCqztb5DkBX2jJwG3DK9ESdIgZvyVS1VtS7ICuAyYA5xXVRuSrALGqmot8NtJTgK2AfcBp+/BmiVJU5gx0AGqah2wbtK0M/uGPwR8aLilSZJ2hleKSlIjDHRJaoSBLkmNMNAlqREGuiQ1wkCXpEYY6JLUCANdkhphoEtSIwx0SWqEgS5JjTDQJakRBrokNcJAl6RGGOiS1AgDXZIaYaBLUiMMdElqhIEuSY0w0CWpEQa6JDXCQJekRhjoktQIA12SGmGgS1IjDHRJaoSBLkmNGCjQkyxLcmuS8SQrd9DuzUkqyejwSpQkDWLGQE8yB1gNnAAsAU5LsmSKds8GzgCuHXaRkqSZDXKEvhQYr6qNVfUIsAY4eYp2fwh8DPjxEOuTJA1okECfD2zqG9/cTXtCklcAC6vq0h2tKMnyJGNJxrZu3brTxUqSprfbJ0WT7Ad8AnjfTG2r6tyqGq2q0ZGRkd3dtCSpzyCBvgVY2De+oJs24dnAy4DLk9wOvBpY64lRSXpqDRLo64HFSQ5LMg84FVg7MbOqHqyqg6tqUVUtAq4BTqqqsT1SsSRpSjMGelVtA1YAlwG3ABdX1YYkq5KctKcLlCQNZu4gjapqHbBu0rQzp2l77O6XJUnaWV4pKkmNMNAlqREGuiQ1wkCXpEYY6JLUCANdkhphoEtSIwx0SWqEgS5JjTDQJakRBrokNcJAl6RGGOiS1AgDXZIaYaBLUiMMdElqhIEuSY0w0CWpEQa6JDXCQJekRhjoktQIA12SGmGgS1IjDHRJaoSBLkmNMNAlqREGuiQ1YqBAT7Isya1JxpOsnGL+e5J8I8mNSa5OsmT4pUqSdmTGQE8yB1gNnAAsAU6bIrA/W1X/sqqOBD4OfGLolUqSdmiQI/SlwHhVbayqR4A1wMn9Darq//aNHgDU8EqUJA1i7gBt5gOb+sY3A6+a3CjJbwG/C8wDjptqRUmWA8sBDj300J2tVZK0A0M7KVpVq6vqcOCDwIenaXNuVY1W1ejIyMiwNi1JYrBA3wIs7Btf0E2bzhrgjbtTlCRp5w0S6OuBxUkOSzIPOBVY298gyeK+0ROB24ZXoiRpEDP2oVfVtiQrgMuAOcB5VbUhySpgrKrWAiuSHA88CtwPvH1PFi1JerJBTopSVeuAdZOmndk3fMaQ65Ik7SSvFJWkRhjoktQIA12SGmGgS1IjDHRJaoSBLkmNMNAlqREGuiQ1wkCXpEYY6JLUCANdkhphoEtSIwx0SWqEgS5JjTDQJakRBrokNcJAl6RGGOiS1AgDXZIaYaBLUiMMdElqhIEuSY0w0CWpEQa6JDXCQJekRhjoktSIubNdwK5YtPLS2S6hWbefdeIeWa+v2Z6zp14z7X0GOkJPsizJrUnGk6ycYv7vJrk5ydeTfCnJC4dfqiRpR2YM9CRzgNXACcAS4LQkSyY1+0dgtKpeDnwO+PiwC5Uk7dggR+hLgfGq2lhVjwBrgJP7G1TVV6rqR93oNcCC4ZYpSZrJIIE+H9jUN765mzad3wD+ZqoZSZYnGUsytnXr1sGrlCTNaKi/cknya8AocPZU86vq3KoararRkZGRYW5akvZ5g/zKZQuwsG98QTdtO0mOB34f+IWqeng45UmSBjXIEfp6YHGSw5LMA04F1vY3SHIUcA5wUlV9f/hlSpJmMmOgV9U2YAVwGXALcHFVbUiyKslJXbOzgQOBv0pyY5K106xOkrSHDHRhUVWtA9ZNmnZm3/DxQ65LkrSTvPRfkhphoEtSIwx0SWqEgS5JjTDQJakRBrokNcJAl6RGGOiS1AgDXZIaYaBLUiMMdElqhIEuSY0w0CWpEQa6JDXCQJekRhjoktQIA12SGmGgS1IjDHRJaoSBLkmNMNAlqREGuiQ1wkCXpEYY6JLUCANdkhphoEtSIwx0SWrEQIGeZFmSW5OMJ1k5xfzXJLkhybYkbxl+mZKkmcwY6EnmAKuBE4AlwGlJlkxq9j3gdOCzwy5QkjSYuQO0WQqMV9VGgCRrgJOBmycaVNXt3bzH90CNkqQBDNLlMh/Y1De+uZu205IsTzKWZGzr1q27sgpJ0jSe0pOiVXVuVY1W1ejIyMhTuWlJat4ggb4FWNg3vqCbJkl6Ghkk0NcDi5MclmQecCqwds+WJUnaWTMGelVtA1YAlwG3ABdX1YYkq5KcBJDklUk2A6cA5yTZsCeLliQ92SC/cqGq1gHrJk07s294Pb2uGEnSLPFKUUlqhIEuSY0w0CWpEQa6JDXCQJekRhjoktQIA12SGmGgS1IjDHRJaoSBLkmNMNAlqREGuiQ1wkCXpEYY6JLUCANdkhphoEtSIwx0SWqEgS5JjTDQJakRBrokNcJAl6RGGOiS1AgDXZIaYaBLUiMMdElqhIEuSY0w0CWpEQMFepJlSW5NMp5k5RTz909yUTf/2iSLhl2oJGnHZgz0JHOA1cAJwBLgtCRLJjX7DeD+qjoC+BPgY8MuVJK0Y4McoS8FxqtqY1U9AqwBTp7U5mTggm74c8Brk2R4ZUqSZjJ3gDbzgU1945uBV03Xpqq2JXkQeC5wT3+jJMuB5d3oQ0lu3ZWi90IHM2lfPF3F71awF71e4GvW2ZdesxdON2OQQB+aqjoXOPep3ObTQZKxqhqd7To0GF+vvY+vWc8gXS5bgIV94wu6aVO2STIX+Eng3mEUKEkazCCBvh5YnOSwJPOAU4G1k9qsBd7eDb8F+HJV1fDKlCTNZMYul65PfAVwGTAHOK+qNiRZBYxV1VrgvwOfSTIO3Ecv9PXP9rlupr2cr9fex9cMiAfSktQGrxSVpEYY6JLUCANd0tNWktOTfHK269hbGOiS1AgDfTclWZXkvX3jf5TkjCTvT7I+ydeT/Kdu3gFJLk1yU5JvJnnr7FUugCSLktyS5NNJNiT52yQ/keTIJNd0r98lSZ4z27W2oNvf3+wb/70kH0lyeZKPJbkuybeTHDPFsicm+VqSg5Ocn+S/Jflqko1J3tK1SZKzu7+vb0z8jSVZneSkbviSJOd1w+/s/manfB88NXtleAz03Xce8OsASfaj95PNu4DF9O6DcyTwc0leAywD7qyqn62qlwH/Z3ZK1iSLgdVV9VLgAeDNwF8CH6yqlwPfAP7jLNa3r5hbVUuB9zJpfyf5FWAl8PqqmrjE/wXA0cAvA2d1095E72/uZ4HjgbOTvAC4Cpj4kJhP70aDdNOu7Ianeh/sVQz03VRVtwP3JjkK+CXgH4FX9g3fAPwLem+WbwCv645EjqmqB2enak3y3aq6sRu+HjgcOKiqruimXQC8ZlYq27d8vvv3emBR3/TjgA8CJ1bV/X3Tv1BVj1fVzcDzu2lHAxdW1WNVdTdwBb2/x6uAY7o7xd4M3N0F/c8DX+2Wnfw+6K9hr/CU3sulYX8BnA78NL0j9tcCf1xV50xumOQVwOuBjyb5UlWteioL1ZQe7ht+DDhotgrZB2xj+wPJZ/YNT7wOj7F9Nn0H+BngRcDYFO0Bdnh316rakuQget+SrwR+CvhV4KGq+kGS5/Lk94FdLvuoS+i9UV5J74ray4B3JjkQIMn8JM9Lcgjwo6r6H8DZwCtmq2Dt0IPA/X39uP+G3pGedt/dwPOSPDfJ/vS6S2ZyB103WJKXztD2KuCtSeYkGaH3zeq6bt419Lpzruza/V73bzM8Qh+CqnokyVeAB6rqMeBvk7wE+Fp3W/iHgF8DjqDXp/c48Cjwm7NVs2b0duBTSZ4FbATeMcv1NKGqHu1uG3IdvZv6fWvA5b6V5G3AXyV5ww6aXkKvG+UmoIAPVNVd3byrgF+qqvEkd9A7Sm8q0L30fwi6k6E3AKdU1W2zXY+kfZNdLrupO8kyDnzJMJc0mzxCl6RGeIQuSY0w0CWpEQa6JDXCQJekRhjoktSI/w/TIK6YopbnBgAAAABJRU5ErkJggg==", + "text/plain": [ + "
" + ] + }, + "metadata": { + "needs_background": "light", + "tags": [] + }, + "output_type": "display_data" + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "/content/data/mini_speech_commands/unknown/ec21c46b_nohash_2.wav\n" + ] + }, + { + "data": { + "image/png": "iVBORw0KGgoAAAANSUhEUgAAAXQAAAEICAYAAABPgw/pAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAALEgAACxIB0t1+/AAAADh0RVh0U29mdHdhcmUAbWF0cGxvdGxpYiB2ZXJzaW9uMy4yLjIsIGh0dHA6Ly9tYXRwbG90bGliLm9yZy+WH4yJAAAUd0lEQVR4nO3df7RdZX3n8feHxGAFV7FytZIEQyE6RseCvUa7BixF7ApSwapUGDsVrWbsNFNsrRqnlnFSO0WZZWdNTSvYYUGdkUAd0XRIh7Yqv6pALhTUgMg1gkkoGH6O6AgEvvPH2ZeeXO7NPUlOuOTJ+7XWWdk/nr339+xz7ufs8+yzd1JVSJL2fvvNdgGSpOEw0CWpEQa6JDXCQJekRhjoktQIA12SGmGga7ckOT/JR7vhY5Lcuovr+VSSPxhudTNu81eSbEryUJKjnsptDypJJTlituvQ3mHubBegPS/J7cDzgceAHwJ/A6yoqoeGuZ2qugp48QD1nA68q6qO7lv2PcOsZUD/hd5++OIwVpbkcuAjwLEAVfWRYaz36SrJ+cDl3eixVXX6rBUjwCP0fckbqupA4BXAKPDhyQ2S7Gsf8C8ENuzKgknmDLkWabcZ6PuYqtpC7wj9ZfDEV/rfSnIbcFs37ZeT3JjkgSRfTfLyieWTHJXkhiQ/SHIR8My+eccm2dw3vjDJ55NsTXJvkk8meQnwKeDnu66OB7q2T3TddOPvTjKe5L4ka5Mc0jevkrwnyW1djauTpJt3RJIrkjyY5J6uxu0k2T/JQ8Ac4KYk3+mmvyTJ5d06NyQ5qW+Z85P8eZJ1SX4I/OIg+zvJ6UmunjTtiW6Ubr2rk1za7dNrkxw+zbqO7rqIjh1gP+yX5MNJ7kjy/SR/meQnu3kXJHlfNzx/4j3QjR/e7fP9Jl7PJO/r1vFPSd4xyPPWLKkqH40/gNuB47vhhfSOSv+wGy/g74CfAn4COAr4PvAqeoH39m75/YF5wB3A7wDPAN4CPAp8tFvXscDmbngOcBPwJ8AB9IL/6G7e6cDVk2o8v289xwH30Ps2sT/wp8CVfW0L+N/AQcChwFZgWTfvQuD36R2sPLHNafZLAUd0w88AxoH/0D3P44AfAC/uq+9B4F9NrHvAfT/Vc+3f7vnAvcBSel2g/xNYM7ktsAzYBCwdcD+8s3s+PwMcCHwe+EzfvL/uhv818B3gor55X+x7PbcBq7r983rgR8BzZvs97WPqh0fo+44vdEfDVwNXAP+5b94fV9V9VfX/gOXAOVV1bVU9VlUXAA8Dr+4ezwD+a1U9WlWfA9ZPs72lwCHA+6vqh1X146q6epq2k70NOK+qbqiqh4EP0TuiX9TX5qyqeqCqvgd8BTiym/4ova6UQ3Zym6+mF3xnVdUjVfVlemF5Wl+bL1bVP1TV41X14wHXO4hLquq6qtpGL9CPnDT/FOAc4ISqum7SvOn2w9uAT1TVxuqdK/kQcGrXrXYFcHSS/YDXAB+n90EF8Avd/AmPAqu613sd8BADnCfR7DDQ9x1vrKqDquqFVfXvuvCesKlv+IXA+7qv8A90HwIL6YXzIcCWquq/o9sd02xvIXBHF1I765D+9XaBdC8wv6/NXX3DP6IXxgAfAAJc13WbvHMntrmpqh7vm3bHpG1uYs+Y7rlMeC9wcVV9cyeW3W4fdsNzgedX1XfonRw/EjiG3gfXnUlezJMD/d5Jr+FU9elpwkAX9L66T9gE/FEX/hOPZ1XVhcA/AfMn+mk7h06zzk3AodOcaJ3pFp930vtgASDJAcBzgS0zPpGqu6rq3VV1CPBvgT8b8Gd/dwILu6PWCYdO2uau3Jr0h8CzJkaS/PQurOMU4I1JztiJZbbbh/Seyzbg7m78CnpdZvOqd17lCnrda88BbtyFGvU0YKBrsk8D70nyqvQckOTEJM8GvkYvFH47yTOSvIle18pUrqP3AXBWt45nJpn4Wn83sCDJvGmWvRB4R5Ijk+xPr3vo2qq6fabik5ySZEE3ej+9EH58B4tMuJbe0ecHuud2LPAGYM0Ay+7ITcBLu+fyTHo/a9xZdwKvBc5I8psDLnMh8DtJDktyIL19eFHf0fYVwArgym788m786qp6bBdq1NOAga7tVNUY8G7gk/QCcZzeiT2q6hHgTd34fcBb6Z1sm2o9j9ELxCOA7wGbu/YAX6Z3YvauJPdMsezfA38A/C96HwqHA6cO+BReCVzb/YplLXBGVW2caaHuub0BOIHeCdk/A369qr414HanW++36Z1U/Ht6vyIatE9/8nq+Ry/UVyZ51wCLnAd8hl5gfxf4MfDv++ZfATybfw70q+l9k7gS7bWyfXeoJGlv5RG6JDXCQJekRhjoktSIgQI9ybIkt3aXYq+cps2vJrm5++3vZ4dbpiRpJjOeFE3vJkTfBl5H75cK64HTqurmvjaLgYuB46rq/iTPq6rv72i9Bx98cC1atGg3y5ekfcv1119/T1WNTDVvkLvrLQXGJ376lWQNcDJwc1+bdwOrq+p+gJnCHGDRokWMjY0NsHlJ0oQk012dPVCXy3y2v+R5M9tfDg3wIuBFSf4hyTVJlk1TyPIkY0nGtm7dOsCmJUmDGtZJ0bnAYnp3ZzsN+HSSgyY3qqpzq2q0qkZHRqb8xiBJ2kWDBPoWejdamrCAJ99TYzOwtrsj23fp9bkvHk6JkqRBDBLo64HF3T0h5tG7BHvtpDZfoPtvt5IcTK8LZsbLrSVJwzNjoHc381kBXAbcQu82nhuSrOr7H10uA+5NcjO9ezK/v6ru3VNFS5KebNbu5TI6Olr+ykWSdk6S66tqdKp5XikqSY0w0CWpEQa6JDVikCtFJe2DFq28dLZLaNbtZ524R9brEbokNcJAl6RGGOiS1AgDXZIaYaBLUiMMdElqhIEuSY0w0CWpEQa6JDXCQJekRhjoktQIA12SGmGgS1IjDHRJaoSBLkmNMNAlqREGuiQ1wkCXpEYY6JLUCANdkhphoEtSIwx0SWqEgS5JjRgo0JMsS3JrkvEkK6eYf3qSrUlu7B7vGn6pkqQdmTtTgyRzgNXA64DNwPoka6vq5klNL6qqFXugRknSAAY5Ql8KjFfVxqp6BFgDnLxny5Ik7axBAn0+sKlvfHM3bbI3J/l6ks8lWTjVipIsTzKWZGzr1q27UK4kaTrDOin618Ciqno58HfABVM1qqpzq2q0qkZHRkaGtGlJEgwW6FuA/iPuBd20J1TVvVX1cDf6F8DPDac8SdKgBgn09cDiJIclmQecCqztb5DkBX2jJwG3DK9ESdIgZvyVS1VtS7ICuAyYA5xXVRuSrALGqmot8NtJTgK2AfcBp+/BmiVJU5gx0AGqah2wbtK0M/uGPwR8aLilSZJ2hleKSlIjDHRJaoSBLkmNMNAlqREGuiQ1wkCXpEYY6JLUCANdkhphoEtSIwx0SWqEgS5JjTDQJakRBrokNcJAl6RGGOiS1AgDXZIaYaBLUiMMdElqhIEuSY0w0CWpEQa6JDXCQJekRhjoktQIA12SGmGgS1IjDHRJaoSBLkmNGCjQkyxLcmuS8SQrd9DuzUkqyejwSpQkDWLGQE8yB1gNnAAsAU5LsmSKds8GzgCuHXaRkqSZDXKEvhQYr6qNVfUIsAY4eYp2fwh8DPjxEOuTJA1okECfD2zqG9/cTXtCklcAC6vq0h2tKMnyJGNJxrZu3brTxUqSprfbJ0WT7Ad8AnjfTG2r6tyqGq2q0ZGRkd3dtCSpzyCBvgVY2De+oJs24dnAy4DLk9wOvBpY64lRSXpqDRLo64HFSQ5LMg84FVg7MbOqHqyqg6tqUVUtAq4BTqqqsT1SsSRpSjMGelVtA1YAlwG3ABdX1YYkq5KctKcLlCQNZu4gjapqHbBu0rQzp2l77O6XJUnaWV4pKkmNMNAlqREGuiQ1wkCXpEYY6JLUCANdkhphoEtSIwx0SWqEgS5JjTDQJakRBrokNcJAl6RGGOiS1AgDXZIaYaBLUiMMdElqhIEuSY0w0CWpEQa6JDXCQJekRhjoktQIA12SGmGgS1IjDHRJaoSBLkmNMNAlqREGuiQ1YqBAT7Isya1JxpOsnGL+e5J8I8mNSa5OsmT4pUqSdmTGQE8yB1gNnAAsAU6bIrA/W1X/sqqOBD4OfGLolUqSdmiQI/SlwHhVbayqR4A1wMn9Darq//aNHgDU8EqUJA1i7gBt5gOb+sY3A6+a3CjJbwG/C8wDjptqRUmWA8sBDj300J2tVZK0A0M7KVpVq6vqcOCDwIenaXNuVY1W1ejIyMiwNi1JYrBA3wIs7Btf0E2bzhrgjbtTlCRp5w0S6OuBxUkOSzIPOBVY298gyeK+0ROB24ZXoiRpEDP2oVfVtiQrgMuAOcB5VbUhySpgrKrWAiuSHA88CtwPvH1PFi1JerJBTopSVeuAdZOmndk3fMaQ65Ik7SSvFJWkRhjoktQIA12SGmGgS1IjDHRJaoSBLkmNMNAlqREGuiQ1wkCXpEYY6JLUCANdkhphoEtSIwx0SWqEgS5JjTDQJakRBrokNcJAl6RGGOiS1AgDXZIaYaBLUiMMdElqhIEuSY0w0CWpEQa6JDXCQJekRhjoktSIubNdwK5YtPLS2S6hWbefdeIeWa+v2Z6zp14z7X0GOkJPsizJrUnGk6ycYv7vJrk5ydeTfCnJC4dfqiRpR2YM9CRzgNXACcAS4LQkSyY1+0dgtKpeDnwO+PiwC5Uk7dggR+hLgfGq2lhVjwBrgJP7G1TVV6rqR93oNcCC4ZYpSZrJIIE+H9jUN765mzad3wD+ZqoZSZYnGUsytnXr1sGrlCTNaKi/cknya8AocPZU86vq3KoararRkZGRYW5akvZ5g/zKZQuwsG98QTdtO0mOB34f+IWqeng45UmSBjXIEfp6YHGSw5LMA04F1vY3SHIUcA5wUlV9f/hlSpJmMmOgV9U2YAVwGXALcHFVbUiyKslJXbOzgQOBv0pyY5K106xOkrSHDHRhUVWtA9ZNmnZm3/DxQ65LkrSTvPRfkhphoEtSIwx0SWqEgS5JjTDQJakRBrokNcJAl6RGGOiS1AgDXZIaYaBLUiMMdElqhIEuSY0w0CWpEQa6JDXCQJekRhjoktQIA12SGmGgS1IjDHRJaoSBLkmNMNAlqREGuiQ1wkCXpEYY6JLUCANdkhphoEtSIwx0SWrEQIGeZFmSW5OMJ1k5xfzXJLkhybYkbxl+mZKkmcwY6EnmAKuBE4AlwGlJlkxq9j3gdOCzwy5QkjSYuQO0WQqMV9VGgCRrgJOBmycaVNXt3bzH90CNkqQBDNLlMh/Y1De+uZu205IsTzKWZGzr1q27sgpJ0jSe0pOiVXVuVY1W1ejIyMhTuWlJat4ggb4FWNg3vqCbJkl6Ghkk0NcDi5MclmQecCqwds+WJUnaWTMGelVtA1YAlwG3ABdX1YYkq5KcBJDklUk2A6cA5yTZsCeLliQ92SC/cqGq1gHrJk07s294Pb2uGEnSLPFKUUlqhIEuSY0w0CWpEQa6JDXCQJekRhjoktQIA12SGmGgS1IjDHRJaoSBLkmNMNAlqREGuiQ1wkCXpEYY6JLUCANdkhphoEtSIwx0SWqEgS5JjTDQJakRBrokNcJAl6RGGOiS1AgDXZIaYaBLUiMMdElqhIEuSY0w0CWpEQMFepJlSW5NMp5k5RTz909yUTf/2iSLhl2oJGnHZgz0JHOA1cAJwBLgtCRLJjX7DeD+qjoC+BPgY8MuVJK0Y4McoS8FxqtqY1U9AqwBTp7U5mTggm74c8Brk2R4ZUqSZjJ3gDbzgU1945uBV03Xpqq2JXkQeC5wT3+jJMuB5d3oQ0lu3ZWi90IHM2lfPF3F71awF71e4GvW2ZdesxdON2OQQB+aqjoXOPep3ObTQZKxqhqd7To0GF+vvY+vWc8gXS5bgIV94wu6aVO2STIX+Eng3mEUKEkazCCBvh5YnOSwJPOAU4G1k9qsBd7eDb8F+HJV1fDKlCTNZMYul65PfAVwGTAHOK+qNiRZBYxV1VrgvwOfSTIO3Ecv9PXP9rlupr2cr9fex9cMiAfSktQGrxSVpEYY6JLUCANd0tNWktOTfHK269hbGOiS1AgDfTclWZXkvX3jf5TkjCTvT7I+ydeT/Kdu3gFJLk1yU5JvJnnr7FUugCSLktyS5NNJNiT52yQ/keTIJNd0r98lSZ4z27W2oNvf3+wb/70kH0lyeZKPJbkuybeTHDPFsicm+VqSg5Ocn+S/Jflqko1J3tK1SZKzu7+vb0z8jSVZneSkbviSJOd1w+/s/manfB88NXtleAz03Xce8OsASfaj95PNu4DF9O6DcyTwc0leAywD7qyqn62qlwH/Z3ZK1iSLgdVV9VLgAeDNwF8CH6yqlwPfAP7jLNa3r5hbVUuB9zJpfyf5FWAl8PqqmrjE/wXA0cAvA2d1095E72/uZ4HjgbOTvAC4Cpj4kJhP70aDdNOu7Ianeh/sVQz03VRVtwP3JjkK+CXgH4FX9g3fAPwLem+WbwCv645EjqmqB2enak3y3aq6sRu+HjgcOKiqruimXQC8ZlYq27d8vvv3emBR3/TjgA8CJ1bV/X3Tv1BVj1fVzcDzu2lHAxdW1WNVdTdwBb2/x6uAY7o7xd4M3N0F/c8DX+2Wnfw+6K9hr/CU3sulYX8BnA78NL0j9tcCf1xV50xumOQVwOuBjyb5UlWteioL1ZQe7ht+DDhotgrZB2xj+wPJZ/YNT7wOj7F9Nn0H+BngRcDYFO0Bdnh316rakuQget+SrwR+CvhV4KGq+kGS5/Lk94FdLvuoS+i9UV5J74ray4B3JjkQIMn8JM9Lcgjwo6r6H8DZwCtmq2Dt0IPA/X39uP+G3pGedt/dwPOSPDfJ/vS6S2ZyB103WJKXztD2KuCtSeYkGaH3zeq6bt419Lpzruza/V73bzM8Qh+CqnokyVeAB6rqMeBvk7wE+Fp3W/iHgF8DjqDXp/c48Cjwm7NVs2b0duBTSZ4FbATeMcv1NKGqHu1uG3IdvZv6fWvA5b6V5G3AXyV5ww6aXkKvG+UmoIAPVNVd3byrgF+qqvEkd9A7Sm8q0L30fwi6k6E3AKdU1W2zXY+kfZNdLrupO8kyDnzJMJc0mzxCl6RGeIQuSY0w0CWpEQa6JDXCQJekRhjoktSI/w/TIK6YopbnBgAAAABJRU5ErkJggg==", + "text/plain": [ + "
" + ] + }, + "metadata": { + "needs_background": "light", + "tags": [] + }, + "output_type": "display_data" + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "/content/data/mini_speech_commands/unknown/2f0a410b_nohash_1.wav\n" + ] + }, + { + "data": { + "image/png": "iVBORw0KGgoAAAANSUhEUgAAAXQAAAEICAYAAABPgw/pAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAALEgAACxIB0t1+/AAAADh0RVh0U29mdHdhcmUAbWF0cGxvdGxpYiB2ZXJzaW9uMy4yLjIsIGh0dHA6Ly9tYXRwbG90bGliLm9yZy+WH4yJAAAUd0lEQVR4nO3df7RdZX3n8feHxGAFV7FytZIEQyE6RseCvUa7BixF7ApSwapUGDsVrWbsNFNsrRqnlnFSO0WZZWdNTSvYYUGdkUAd0XRIh7Yqv6pALhTUgMg1gkkoGH6O6AgEvvPH2ZeeXO7NPUlOuOTJ+7XWWdk/nr339+xz7ufs8+yzd1JVSJL2fvvNdgGSpOEw0CWpEQa6JDXCQJekRhjoktQIA12SGmGga7ckOT/JR7vhY5Lcuovr+VSSPxhudTNu81eSbEryUJKjnsptDypJJTlituvQ3mHubBegPS/J7cDzgceAHwJ/A6yoqoeGuZ2qugp48QD1nA68q6qO7lv2PcOsZUD/hd5++OIwVpbkcuAjwLEAVfWRYaz36SrJ+cDl3eixVXX6rBUjwCP0fckbqupA4BXAKPDhyQ2S7Gsf8C8ENuzKgknmDLkWabcZ6PuYqtpC7wj9ZfDEV/rfSnIbcFs37ZeT3JjkgSRfTfLyieWTHJXkhiQ/SHIR8My+eccm2dw3vjDJ55NsTXJvkk8meQnwKeDnu66OB7q2T3TddOPvTjKe5L4ka5Mc0jevkrwnyW1djauTpJt3RJIrkjyY5J6uxu0k2T/JQ8Ac4KYk3+mmvyTJ5d06NyQ5qW+Z85P8eZJ1SX4I/OIg+zvJ6UmunjTtiW6Ubr2rk1za7dNrkxw+zbqO7rqIjh1gP+yX5MNJ7kjy/SR/meQnu3kXJHlfNzx/4j3QjR/e7fP9Jl7PJO/r1vFPSd4xyPPWLKkqH40/gNuB47vhhfSOSv+wGy/g74CfAn4COAr4PvAqeoH39m75/YF5wB3A7wDPAN4CPAp8tFvXscDmbngOcBPwJ8AB9IL/6G7e6cDVk2o8v289xwH30Ps2sT/wp8CVfW0L+N/AQcChwFZgWTfvQuD36R2sPLHNafZLAUd0w88AxoH/0D3P44AfAC/uq+9B4F9NrHvAfT/Vc+3f7vnAvcBSel2g/xNYM7ktsAzYBCwdcD+8s3s+PwMcCHwe+EzfvL/uhv818B3gor55X+x7PbcBq7r983rgR8BzZvs97WPqh0fo+44vdEfDVwNXAP+5b94fV9V9VfX/gOXAOVV1bVU9VlUXAA8Dr+4ezwD+a1U9WlWfA9ZPs72lwCHA+6vqh1X146q6epq2k70NOK+qbqiqh4EP0TuiX9TX5qyqeqCqvgd8BTiym/4ova6UQ3Zym6+mF3xnVdUjVfVlemF5Wl+bL1bVP1TV41X14wHXO4hLquq6qtpGL9CPnDT/FOAc4ISqum7SvOn2w9uAT1TVxuqdK/kQcGrXrXYFcHSS/YDXAB+n90EF8Avd/AmPAqu613sd8BADnCfR7DDQ9x1vrKqDquqFVfXvuvCesKlv+IXA+7qv8A90HwIL6YXzIcCWquq/o9sd02xvIXBHF1I765D+9XaBdC8wv6/NXX3DP6IXxgAfAAJc13WbvHMntrmpqh7vm3bHpG1uYs+Y7rlMeC9wcVV9cyeW3W4fdsNzgedX1XfonRw/EjiG3gfXnUlezJMD/d5Jr+FU9elpwkAX9L66T9gE/FEX/hOPZ1XVhcA/AfMn+mk7h06zzk3AodOcaJ3pFp930vtgASDJAcBzgS0zPpGqu6rq3VV1CPBvgT8b8Gd/dwILu6PWCYdO2uau3Jr0h8CzJkaS/PQurOMU4I1JztiJZbbbh/Seyzbg7m78CnpdZvOqd17lCnrda88BbtyFGvU0YKBrsk8D70nyqvQckOTEJM8GvkYvFH47yTOSvIle18pUrqP3AXBWt45nJpn4Wn83sCDJvGmWvRB4R5Ijk+xPr3vo2qq6fabik5ySZEE3ej+9EH58B4tMuJbe0ecHuud2LPAGYM0Ay+7ITcBLu+fyTHo/a9xZdwKvBc5I8psDLnMh8DtJDktyIL19eFHf0fYVwArgym788m786qp6bBdq1NOAga7tVNUY8G7gk/QCcZzeiT2q6hHgTd34fcBb6Z1sm2o9j9ELxCOA7wGbu/YAX6Z3YvauJPdMsezfA38A/C96HwqHA6cO+BReCVzb/YplLXBGVW2caaHuub0BOIHeCdk/A369qr414HanW++36Z1U/Ht6vyIatE9/8nq+Ry/UVyZ51wCLnAd8hl5gfxf4MfDv++ZfATybfw70q+l9k7gS7bWyfXeoJGlv5RG6JDXCQJekRhjoktSIgQI9ybIkt3aXYq+cps2vJrm5++3vZ4dbpiRpJjOeFE3vJkTfBl5H75cK64HTqurmvjaLgYuB46rq/iTPq6rv72i9Bx98cC1atGg3y5ekfcv1119/T1WNTDVvkLvrLQXGJ376lWQNcDJwc1+bdwOrq+p+gJnCHGDRokWMjY0NsHlJ0oQk012dPVCXy3y2v+R5M9tfDg3wIuBFSf4hyTVJlk1TyPIkY0nGtm7dOsCmJUmDGtZJ0bnAYnp3ZzsN+HSSgyY3qqpzq2q0qkZHRqb8xiBJ2kWDBPoWejdamrCAJ99TYzOwtrsj23fp9bkvHk6JkqRBDBLo64HF3T0h5tG7BHvtpDZfoPtvt5IcTK8LZsbLrSVJwzNjoHc381kBXAbcQu82nhuSrOr7H10uA+5NcjO9ezK/v6ru3VNFS5KebNbu5TI6Olr+ykWSdk6S66tqdKp5XikqSY0w0CWpEQa6JDVikCtFJe2DFq28dLZLaNbtZ524R9brEbokNcJAl6RGGOiS1AgDXZIaYaBLUiMMdElqhIEuSY0w0CWpEQa6JDXCQJekRhjoktQIA12SGmGgS1IjDHRJaoSBLkmNMNAlqREGuiQ1wkCXpEYY6JLUCANdkhphoEtSIwx0SWqEgS5JjRgo0JMsS3JrkvEkK6eYf3qSrUlu7B7vGn6pkqQdmTtTgyRzgNXA64DNwPoka6vq5klNL6qqFXugRknSAAY5Ql8KjFfVxqp6BFgDnLxny5Ik7axBAn0+sKlvfHM3bbI3J/l6ks8lWTjVipIsTzKWZGzr1q27UK4kaTrDOin618Ciqno58HfABVM1qqpzq2q0qkZHRkaGtGlJEgwW6FuA/iPuBd20J1TVvVX1cDf6F8DPDac8SdKgBgn09cDiJIclmQecCqztb5DkBX2jJwG3DK9ESdIgZvyVS1VtS7ICuAyYA5xXVRuSrALGqmot8NtJTgK2AfcBp+/BmiVJU5gx0AGqah2wbtK0M/uGPwR8aLilSZJ2hleKSlIjDHRJaoSBLkmNMNAlqREGuiQ1wkCXpEYY6JLUCANdkhphoEtSIwx0SWqEgS5JjTDQJakRBrokNcJAl6RGGOiS1AgDXZIaYaBLUiMMdElqhIEuSY0w0CWpEQa6JDXCQJekRhjoktQIA12SGmGgS1IjDHRJaoSBLkmNGCjQkyxLcmuS8SQrd9DuzUkqyejwSpQkDWLGQE8yB1gNnAAsAU5LsmSKds8GzgCuHXaRkqSZDXKEvhQYr6qNVfUIsAY4eYp2fwh8DPjxEOuTJA1okECfD2zqG9/cTXtCklcAC6vq0h2tKMnyJGNJxrZu3brTxUqSprfbJ0WT7Ad8AnjfTG2r6tyqGq2q0ZGRkd3dtCSpzyCBvgVY2De+oJs24dnAy4DLk9wOvBpY64lRSXpqDRLo64HFSQ5LMg84FVg7MbOqHqyqg6tqUVUtAq4BTqqqsT1SsSRpSjMGelVtA1YAlwG3ABdX1YYkq5KctKcLlCQNZu4gjapqHbBu0rQzp2l77O6XJUnaWV4pKkmNMNAlqREGuiQ1wkCXpEYY6JLUCANdkhphoEtSIwx0SWqEgS5JjTDQJakRBrokNcJAl6RGGOiS1AgDXZIaYaBLUiMMdElqhIEuSY0w0CWpEQa6JDXCQJekRhjoktQIA12SGmGgS1IjDHRJaoSBLkmNMNAlqREGuiQ1YqBAT7Isya1JxpOsnGL+e5J8I8mNSa5OsmT4pUqSdmTGQE8yB1gNnAAsAU6bIrA/W1X/sqqOBD4OfGLolUqSdmiQI/SlwHhVbayqR4A1wMn9Darq//aNHgDU8EqUJA1i7gBt5gOb+sY3A6+a3CjJbwG/C8wDjptqRUmWA8sBDj300J2tVZK0A0M7KVpVq6vqcOCDwIenaXNuVY1W1ejIyMiwNi1JYrBA3wIs7Btf0E2bzhrgjbtTlCRp5w0S6OuBxUkOSzIPOBVY298gyeK+0ROB24ZXoiRpEDP2oVfVtiQrgMuAOcB5VbUhySpgrKrWAiuSHA88CtwPvH1PFi1JerJBTopSVeuAdZOmndk3fMaQ65Ik7SSvFJWkRhjoktQIA12SGmGgS1IjDHRJaoSBLkmNMNAlqREGuiQ1wkCXpEYY6JLUCANdkhphoEtSIwx0SWqEgS5JjTDQJakRBrokNcJAl6RGGOiS1AgDXZIaYaBLUiMMdElqhIEuSY0w0CWpEQa6JDXCQJekRhjoktSIubNdwK5YtPLS2S6hWbefdeIeWa+v2Z6zp14z7X0GOkJPsizJrUnGk6ycYv7vJrk5ydeTfCnJC4dfqiRpR2YM9CRzgNXACcAS4LQkSyY1+0dgtKpeDnwO+PiwC5Uk7dggR+hLgfGq2lhVjwBrgJP7G1TVV6rqR93oNcCC4ZYpSZrJIIE+H9jUN765mzad3wD+ZqoZSZYnGUsytnXr1sGrlCTNaKi/cknya8AocPZU86vq3KoararRkZGRYW5akvZ5g/zKZQuwsG98QTdtO0mOB34f+IWqeng45UmSBjXIEfp6YHGSw5LMA04F1vY3SHIUcA5wUlV9f/hlSpJmMmOgV9U2YAVwGXALcHFVbUiyKslJXbOzgQOBv0pyY5K106xOkrSHDHRhUVWtA9ZNmnZm3/DxQ65LkrSTvPRfkhphoEtSIwx0SWqEgS5JjTDQJakRBrokNcJAl6RGGOiS1AgDXZIaYaBLUiMMdElqhIEuSY0w0CWpEQa6JDXCQJekRhjoktQIA12SGmGgS1IjDHRJaoSBLkmNMNAlqREGuiQ1wkCXpEYY6JLUCANdkhphoEtSIwx0SWrEQIGeZFmSW5OMJ1k5xfzXJLkhybYkbxl+mZKkmcwY6EnmAKuBE4AlwGlJlkxq9j3gdOCzwy5QkjSYuQO0WQqMV9VGgCRrgJOBmycaVNXt3bzH90CNkqQBDNLlMh/Y1De+uZu205IsTzKWZGzr1q27sgpJ0jSe0pOiVXVuVY1W1ejIyMhTuWlJat4ggb4FWNg3vqCbJkl6Ghkk0NcDi5MclmQecCqwds+WJUnaWTMGelVtA1YAlwG3ABdX1YYkq5KcBJDklUk2A6cA5yTZsCeLliQ92SC/cqGq1gHrJk07s294Pb2uGEnSLPFKUUlqhIEuSY0w0CWpEQa6JDXCQJekRhjoktQIA12SGmGgS1IjDHRJaoSBLkmNMNAlqREGuiQ1wkCXpEYY6JLUCANdkhphoEtSIwx0SWqEgS5JjTDQJakRBrokNcJAl6RGGOiS1AgDXZIaYaBLUiMMdElqhIEuSY0w0CWpEQMFepJlSW5NMp5k5RTz909yUTf/2iSLhl2oJGnHZgz0JHOA1cAJwBLgtCRLJjX7DeD+qjoC+BPgY8MuVJK0Y4McoS8FxqtqY1U9AqwBTp7U5mTggm74c8Brk2R4ZUqSZjJ3gDbzgU1945uBV03Xpqq2JXkQeC5wT3+jJMuB5d3oQ0lu3ZWi90IHM2lfPF3F71awF71e4GvW2ZdesxdON2OQQB+aqjoXOPep3ObTQZKxqhqd7To0GF+vvY+vWc8gXS5bgIV94wu6aVO2STIX+Eng3mEUKEkazCCBvh5YnOSwJPOAU4G1k9qsBd7eDb8F+HJV1fDKlCTNZMYul65PfAVwGTAHOK+qNiRZBYxV1VrgvwOfSTIO3Ecv9PXP9rlupr2cr9fex9cMiAfSktQGrxSVpEYY6JLUCANd0tNWktOTfHK269hbGOiS1AgDfTclWZXkvX3jf5TkjCTvT7I+ydeT/Kdu3gFJLk1yU5JvJnnr7FUugCSLktyS5NNJNiT52yQ/keTIJNd0r98lSZ4z27W2oNvf3+wb/70kH0lyeZKPJbkuybeTHDPFsicm+VqSg5Ocn+S/Jflqko1J3tK1SZKzu7+vb0z8jSVZneSkbviSJOd1w+/s/manfB88NXtleAz03Xce8OsASfaj95PNu4DF9O6DcyTwc0leAywD7qyqn62qlwH/Z3ZK1iSLgdVV9VLgAeDNwF8CH6yqlwPfAP7jLNa3r5hbVUuB9zJpfyf5FWAl8PqqmrjE/wXA0cAvA2d1095E72/uZ4HjgbOTvAC4Cpj4kJhP70aDdNOu7Ianeh/sVQz03VRVtwP3JjkK+CXgH4FX9g3fAPwLem+WbwCv645EjqmqB2enak3y3aq6sRu+HjgcOKiqruimXQC8ZlYq27d8vvv3emBR3/TjgA8CJ1bV/X3Tv1BVj1fVzcDzu2lHAxdW1WNVdTdwBb2/x6uAY7o7xd4M3N0F/c8DX+2Wnfw+6K9hr/CU3sulYX8BnA78NL0j9tcCf1xV50xumOQVwOuBjyb5UlWteioL1ZQe7ht+DDhotgrZB2xj+wPJZ/YNT7wOj7F9Nn0H+BngRcDYFO0Bdnh316rakuQget+SrwR+CvhV4KGq+kGS5/Lk94FdLvuoS+i9UV5J74ray4B3JjkQIMn8JM9Lcgjwo6r6H8DZwCtmq2Dt0IPA/X39uP+G3pGedt/dwPOSPDfJ/vS6S2ZyB103WJKXztD2KuCtSeYkGaH3zeq6bt419Lpzruza/V73bzM8Qh+CqnokyVeAB6rqMeBvk7wE+Fp3W/iHgF8DjqDXp/c48Cjwm7NVs2b0duBTSZ4FbATeMcv1NKGqHu1uG3IdvZv6fWvA5b6V5G3AXyV5ww6aXkKvG+UmoIAPVNVd3byrgF+qqvEkd9A7Sm8q0L30fwi6k6E3AKdU1W2zXY+kfZNdLrupO8kyDnzJMJc0mzxCl6RGeIQuSY0w0CWpEQa6JDXCQJekRhjoktSI/w/TIK6YopbnBgAAAABJRU5ErkJggg==", + "text/plain": [ + "
" + ] + }, + "metadata": { + "needs_background": "light", + "tags": [] + }, + "output_type": "display_data" + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "/content/data/mini_speech_commands/unknown/f2a90886_nohash_0.wav\n" + ] + }, + { + "data": { + "image/png": "iVBORw0KGgoAAAANSUhEUgAAAXQAAAEICAYAAABPgw/pAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAALEgAACxIB0t1+/AAAADh0RVh0U29mdHdhcmUAbWF0cGxvdGxpYiB2ZXJzaW9uMy4yLjIsIGh0dHA6Ly9tYXRwbG90bGliLm9yZy+WH4yJAAAUd0lEQVR4nO3df7RdZX3n8feHxGAFV7FytZIEQyE6RseCvUa7BixF7ApSwapUGDsVrWbsNFNsrRqnlnFSO0WZZWdNTSvYYUGdkUAd0XRIh7Yqv6pALhTUgMg1gkkoGH6O6AgEvvPH2ZeeXO7NPUlOuOTJ+7XWWdk/nr339+xz7ufs8+yzd1JVSJL2fvvNdgGSpOEw0CWpEQa6JDXCQJekRhjoktQIA12SGmGga7ckOT/JR7vhY5Lcuovr+VSSPxhudTNu81eSbEryUJKjnsptDypJJTlituvQ3mHubBegPS/J7cDzgceAHwJ/A6yoqoeGuZ2qugp48QD1nA68q6qO7lv2PcOsZUD/hd5++OIwVpbkcuAjwLEAVfWRYaz36SrJ+cDl3eixVXX6rBUjwCP0fckbqupA4BXAKPDhyQ2S7Gsf8C8ENuzKgknmDLkWabcZ6PuYqtpC7wj9ZfDEV/rfSnIbcFs37ZeT3JjkgSRfTfLyieWTHJXkhiQ/SHIR8My+eccm2dw3vjDJ55NsTXJvkk8meQnwKeDnu66OB7q2T3TddOPvTjKe5L4ka5Mc0jevkrwnyW1djauTpJt3RJIrkjyY5J6uxu0k2T/JQ8Ac4KYk3+mmvyTJ5d06NyQ5qW+Z85P8eZJ1SX4I/OIg+zvJ6UmunjTtiW6Ubr2rk1za7dNrkxw+zbqO7rqIjh1gP+yX5MNJ7kjy/SR/meQnu3kXJHlfNzx/4j3QjR/e7fP9Jl7PJO/r1vFPSd4xyPPWLKkqH40/gNuB47vhhfSOSv+wGy/g74CfAn4COAr4PvAqeoH39m75/YF5wB3A7wDPAN4CPAp8tFvXscDmbngOcBPwJ8AB9IL/6G7e6cDVk2o8v289xwH30Ps2sT/wp8CVfW0L+N/AQcChwFZgWTfvQuD36R2sPLHNafZLAUd0w88AxoH/0D3P44AfAC/uq+9B4F9NrHvAfT/Vc+3f7vnAvcBSel2g/xNYM7ktsAzYBCwdcD+8s3s+PwMcCHwe+EzfvL/uhv818B3gor55X+x7PbcBq7r983rgR8BzZvs97WPqh0fo+44vdEfDVwNXAP+5b94fV9V9VfX/gOXAOVV1bVU9VlUXAA8Dr+4ezwD+a1U9WlWfA9ZPs72lwCHA+6vqh1X146q6epq2k70NOK+qbqiqh4EP0TuiX9TX5qyqeqCqvgd8BTiym/4ova6UQ3Zym6+mF3xnVdUjVfVlemF5Wl+bL1bVP1TV41X14wHXO4hLquq6qtpGL9CPnDT/FOAc4ISqum7SvOn2w9uAT1TVxuqdK/kQcGrXrXYFcHSS/YDXAB+n90EF8Avd/AmPAqu613sd8BADnCfR7DDQ9x1vrKqDquqFVfXvuvCesKlv+IXA+7qv8A90HwIL6YXzIcCWquq/o9sd02xvIXBHF1I765D+9XaBdC8wv6/NXX3DP6IXxgAfAAJc13WbvHMntrmpqh7vm3bHpG1uYs+Y7rlMeC9wcVV9cyeW3W4fdsNzgedX1XfonRw/EjiG3gfXnUlezJMD/d5Jr+FU9elpwkAX9L66T9gE/FEX/hOPZ1XVhcA/AfMn+mk7h06zzk3AodOcaJ3pFp930vtgASDJAcBzgS0zPpGqu6rq3VV1CPBvgT8b8Gd/dwILu6PWCYdO2uau3Jr0h8CzJkaS/PQurOMU4I1JztiJZbbbh/Seyzbg7m78CnpdZvOqd17lCnrda88BbtyFGvU0YKBrsk8D70nyqvQckOTEJM8GvkYvFH47yTOSvIle18pUrqP3AXBWt45nJpn4Wn83sCDJvGmWvRB4R5Ijk+xPr3vo2qq6fabik5ySZEE3ej+9EH58B4tMuJbe0ecHuud2LPAGYM0Ay+7ITcBLu+fyTHo/a9xZdwKvBc5I8psDLnMh8DtJDktyIL19eFHf0fYVwArgym788m786qp6bBdq1NOAga7tVNUY8G7gk/QCcZzeiT2q6hHgTd34fcBb6Z1sm2o9j9ELxCOA7wGbu/YAX6Z3YvauJPdMsezfA38A/C96HwqHA6cO+BReCVzb/YplLXBGVW2caaHuub0BOIHeCdk/A369qr414HanW++36Z1U/Ht6vyIatE9/8nq+Ry/UVyZ51wCLnAd8hl5gfxf4MfDv++ZfATybfw70q+l9k7gS7bWyfXeoJGlv5RG6JDXCQJekRhjoktSIgQI9ybIkt3aXYq+cps2vJrm5++3vZ4dbpiRpJjOeFE3vJkTfBl5H75cK64HTqurmvjaLgYuB46rq/iTPq6rv72i9Bx98cC1atGg3y5ekfcv1119/T1WNTDVvkLvrLQXGJ376lWQNcDJwc1+bdwOrq+p+gJnCHGDRokWMjY0NsHlJ0oQk012dPVCXy3y2v+R5M9tfDg3wIuBFSf4hyTVJlk1TyPIkY0nGtm7dOsCmJUmDGtZJ0bnAYnp3ZzsN+HSSgyY3qqpzq2q0qkZHRqb8xiBJ2kWDBPoWejdamrCAJ99TYzOwtrsj23fp9bkvHk6JkqRBDBLo64HF3T0h5tG7BHvtpDZfoPtvt5IcTK8LZsbLrSVJwzNjoHc381kBXAbcQu82nhuSrOr7H10uA+5NcjO9ezK/v6ru3VNFS5KebNbu5TI6Olr+ykWSdk6S66tqdKp5XikqSY0w0CWpEQa6JDVikCtFJe2DFq28dLZLaNbtZ524R9brEbokNcJAl6RGGOiS1AgDXZIaYaBLUiMMdElqhIEuSY0w0CWpEQa6JDXCQJekRhjoktQIA12SGmGgS1IjDHRJaoSBLkmNMNAlqREGuiQ1wkCXpEYY6JLUCANdkhphoEtSIwx0SWqEgS5JjRgo0JMsS3JrkvEkK6eYf3qSrUlu7B7vGn6pkqQdmTtTgyRzgNXA64DNwPoka6vq5klNL6qqFXugRknSAAY5Ql8KjFfVxqp6BFgDnLxny5Ik7axBAn0+sKlvfHM3bbI3J/l6ks8lWTjVipIsTzKWZGzr1q27UK4kaTrDOin618Ciqno58HfABVM1qqpzq2q0qkZHRkaGtGlJEgwW6FuA/iPuBd20J1TVvVX1cDf6F8DPDac8SdKgBgn09cDiJIclmQecCqztb5DkBX2jJwG3DK9ESdIgZvyVS1VtS7ICuAyYA5xXVRuSrALGqmot8NtJTgK2AfcBp+/BmiVJU5gx0AGqah2wbtK0M/uGPwR8aLilSZJ2hleKSlIjDHRJaoSBLkmNMNAlqREGuiQ1wkCXpEYY6JLUCANdkhphoEtSIwx0SWqEgS5JjTDQJakRBrokNcJAl6RGGOiS1AgDXZIaYaBLUiMMdElqhIEuSY0w0CWpEQa6JDXCQJekRhjoktQIA12SGmGgS1IjDHRJaoSBLkmNGCjQkyxLcmuS8SQrd9DuzUkqyejwSpQkDWLGQE8yB1gNnAAsAU5LsmSKds8GzgCuHXaRkqSZDXKEvhQYr6qNVfUIsAY4eYp2fwh8DPjxEOuTJA1okECfD2zqG9/cTXtCklcAC6vq0h2tKMnyJGNJxrZu3brTxUqSprfbJ0WT7Ad8AnjfTG2r6tyqGq2q0ZGRkd3dtCSpzyCBvgVY2De+oJs24dnAy4DLk9wOvBpY64lRSXpqDRLo64HFSQ5LMg84FVg7MbOqHqyqg6tqUVUtAq4BTqqqsT1SsSRpSjMGelVtA1YAlwG3ABdX1YYkq5KctKcLlCQNZu4gjapqHbBu0rQzp2l77O6XJUnaWV4pKkmNMNAlqREGuiQ1wkCXpEYY6JLUCANdkhphoEtSIwx0SWqEgS5JjTDQJakRBrokNcJAl6RGGOiS1AgDXZIaYaBLUiMMdElqhIEuSY0w0CWpEQa6JDXCQJekRhjoktQIA12SGmGgS1IjDHRJaoSBLkmNMNAlqREGuiQ1YqBAT7Isya1JxpOsnGL+e5J8I8mNSa5OsmT4pUqSdmTGQE8yB1gNnAAsAU6bIrA/W1X/sqqOBD4OfGLolUqSdmiQI/SlwHhVbayqR4A1wMn9Darq//aNHgDU8EqUJA1i7gBt5gOb+sY3A6+a3CjJbwG/C8wDjptqRUmWA8sBDj300J2tVZK0A0M7KVpVq6vqcOCDwIenaXNuVY1W1ejIyMiwNi1JYrBA3wIs7Btf0E2bzhrgjbtTlCRp5w0S6OuBxUkOSzIPOBVY298gyeK+0ROB24ZXoiRpEDP2oVfVtiQrgMuAOcB5VbUhySpgrKrWAiuSHA88CtwPvH1PFi1JerJBTopSVeuAdZOmndk3fMaQ65Ik7SSvFJWkRhjoktQIA12SGmGgS1IjDHRJaoSBLkmNMNAlqREGuiQ1wkCXpEYY6JLUCANdkhphoEtSIwx0SWqEgS5JjTDQJakRBrokNcJAl6RGGOiS1AgDXZIaYaBLUiMMdElqhIEuSY0w0CWpEQa6JDXCQJekRhjoktSIubNdwK5YtPLS2S6hWbefdeIeWa+v2Z6zp14z7X0GOkJPsizJrUnGk6ycYv7vJrk5ydeTfCnJC4dfqiRpR2YM9CRzgNXACcAS4LQkSyY1+0dgtKpeDnwO+PiwC5Uk7dggR+hLgfGq2lhVjwBrgJP7G1TVV6rqR93oNcCC4ZYpSZrJIIE+H9jUN765mzad3wD+ZqoZSZYnGUsytnXr1sGrlCTNaKi/cknya8AocPZU86vq3KoararRkZGRYW5akvZ5g/zKZQuwsG98QTdtO0mOB34f+IWqeng45UmSBjXIEfp6YHGSw5LMA04F1vY3SHIUcA5wUlV9f/hlSpJmMmOgV9U2YAVwGXALcHFVbUiyKslJXbOzgQOBv0pyY5K106xOkrSHDHRhUVWtA9ZNmnZm3/DxQ65LkrSTvPRfkhphoEtSIwx0SWqEgS5JjTDQJakRBrokNcJAl6RGGOiS1AgDXZIaYaBLUiMMdElqhIEuSY0w0CWpEQa6JDXCQJekRhjoktQIA12SGmGgS1IjDHRJaoSBLkmNMNAlqREGuiQ1wkCXpEYY6JLUCANdkhphoEtSIwx0SWrEQIGeZFmSW5OMJ1k5xfzXJLkhybYkbxl+mZKkmcwY6EnmAKuBE4AlwGlJlkxq9j3gdOCzwy5QkjSYuQO0WQqMV9VGgCRrgJOBmycaVNXt3bzH90CNkqQBDNLlMh/Y1De+uZu205IsTzKWZGzr1q27sgpJ0jSe0pOiVXVuVY1W1ejIyMhTuWlJat4ggb4FWNg3vqCbJkl6Ghkk0NcDi5MclmQecCqwds+WJUnaWTMGelVtA1YAlwG3ABdX1YYkq5KcBJDklUk2A6cA5yTZsCeLliQ92SC/cqGq1gHrJk07s294Pb2uGEnSLPFKUUlqhIEuSY0w0CWpEQa6JDXCQJekRhjoktQIA12SGmGgS1IjDHRJaoSBLkmNMNAlqREGuiQ1wkCXpEYY6JLUCANdkhphoEtSIwx0SWqEgS5JjTDQJakRBrokNcJAl6RGGOiS1AgDXZIaYaBLUiMMdElqhIEuSY0w0CWpEQMFepJlSW5NMp5k5RTz909yUTf/2iSLhl2oJGnHZgz0JHOA1cAJwBLgtCRLJjX7DeD+qjoC+BPgY8MuVJK0Y4McoS8FxqtqY1U9AqwBTp7U5mTggm74c8Brk2R4ZUqSZjJ3gDbzgU1945uBV03Xpqq2JXkQeC5wT3+jJMuB5d3oQ0lu3ZWi90IHM2lfPF3F71awF71e4GvW2ZdesxdON2OQQB+aqjoXOPep3ObTQZKxqhqd7To0GF+vvY+vWc8gXS5bgIV94wu6aVO2STIX+Eng3mEUKEkazCCBvh5YnOSwJPOAU4G1k9qsBd7eDb8F+HJV1fDKlCTNZMYul65PfAVwGTAHOK+qNiRZBYxV1VrgvwOfSTIO3Ecv9PXP9rlupr2cr9fex9cMiAfSktQGrxSVpEYY6JLUCANd0tNWktOTfHK269hbGOiS1AgDfTclWZXkvX3jf5TkjCTvT7I+ydeT/Kdu3gFJLk1yU5JvJnnr7FUugCSLktyS5NNJNiT52yQ/keTIJNd0r98lSZ4z27W2oNvf3+wb/70kH0lyeZKPJbkuybeTHDPFsicm+VqSg5Ocn+S/Jflqko1J3tK1SZKzu7+vb0z8jSVZneSkbviSJOd1w+/s/manfB88NXtleAz03Xce8OsASfaj95PNu4DF9O6DcyTwc0leAywD7qyqn62qlwH/Z3ZK1iSLgdVV9VLgAeDNwF8CH6yqlwPfAP7jLNa3r5hbVUuB9zJpfyf5FWAl8PqqmrjE/wXA0cAvA2d1095E72/uZ4HjgbOTvAC4Cpj4kJhP70aDdNOu7Ianeh/sVQz03VRVtwP3JjkK+CXgH4FX9g3fAPwLem+WbwCv645EjqmqB2enak3y3aq6sRu+HjgcOKiqruimXQC8ZlYq27d8vvv3emBR3/TjgA8CJ1bV/X3Tv1BVj1fVzcDzu2lHAxdW1WNVdTdwBb2/x6uAY7o7xd4M3N0F/c8DX+2Wnfw+6K9hr/CU3sulYX8BnA78NL0j9tcCf1xV50xumOQVwOuBjyb5UlWteioL1ZQe7ht+DDhotgrZB2xj+wPJZ/YNT7wOj7F9Nn0H+BngRcDYFO0Bdnh316rakuQget+SrwR+CvhV4KGq+kGS5/Lk94FdLvuoS+i9UV5J74ray4B3JjkQIMn8JM9Lcgjwo6r6H8DZwCtmq2Dt0IPA/X39uP+G3pGedt/dwPOSPDfJ/vS6S2ZyB103WJKXztD2KuCtSeYkGaH3zeq6bt419Lpzruza/V73bzM8Qh+CqnokyVeAB6rqMeBvk7wE+Fp3W/iHgF8DjqDXp/c48Cjwm7NVs2b0duBTSZ4FbATeMcv1NKGqHu1uG3IdvZv6fWvA5b6V5G3AXyV5ww6aXkKvG+UmoIAPVNVd3byrgF+qqvEkd9A7Sm8q0L30fwi6k6E3AKdU1W2zXY+kfZNdLrupO8kyDnzJMJc0mzxCl6RGeIQuSY0w0CWpEQa6JDXCQJekRhjoktSI/w/TIK6YopbnBgAAAABJRU5ErkJggg==", + "text/plain": [ + "
" + ] + }, + "metadata": { + "needs_background": "light", + "tags": [] + }, + "output_type": "display_data" + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "/content/data/mini_speech_commands/unknown/dbb40d24_nohash_0.wav\n" + ] + }, + { + "data": { + "image/png": "iVBORw0KGgoAAAANSUhEUgAAAXQAAAEICAYAAABPgw/pAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAALEgAACxIB0t1+/AAAADh0RVh0U29mdHdhcmUAbWF0cGxvdGxpYiB2ZXJzaW9uMy4yLjIsIGh0dHA6Ly9tYXRwbG90bGliLm9yZy+WH4yJAAAUbElEQVR4nO3df7RdZX3n8feHxGAFV7ESrYSEUEgdo2PBXqNdA5ZB7ApSwapUGDsVrWbsNFNsrRqnlnFSO0WdZWfNmI5ghwV1RgJ1RNMhLW1VflWFBApqQCRGMAkFw88RHYHAd/7Y+9LD5d7ck+SES568X2udlf3j2Xt/zz7nfs4+zz57J1WFJGnvt99MFyBJGg0DXZIaYaBLUiMMdElqhIEuSY0w0CWpEQa6dkuS85N8uB8+Nsktu7ieTyb5g9FWN+02fyXJ5iQPJjn6qdz2sJJUkiNnug7tHWbPdAHa85LcBjwfeBT4IfBXwPKqenCU26mqq4AXDlHPGcA7quqYgWXfNcpahvSf6fbDF0axsiSXAx8CjgOoqg+NYr1PV0nOBy7vR4+rqjNmrBgBHqHvS15XVQcCLwPGgA9ObJBkX/uAPwzYsCsLJpk14lqk3Wag72OqaivdEfpL4PGv9L+V5Fbg1n7aLye5Icn9Sb6S5KXjyyc5Osn1SX6Q5CLgmQPzjkuyZWB8fpLPJdmW5J4kn0jyIuCTwC/0XR33920f77rpx9+ZZGOSe5OsSXLIwLxK8q4kt/Y1rkqSft6RSa5I8kCSu/sanyDJ/kkeBGYBNyb5Tj/9RUku79e5IcnJA8ucn+S/J1mb5IfAvxxmfyc5I8nVE6Y93o3Sr3dVkkv7fXpNkiOmWNcxfRfRcUPsh/2SfDDJ7Um+n+TPk/xkP++CJO/ph+eNvwf68SP6fb7f+OuZ5D39Ov4xyduGed6aIVXlo/EHcBtwQj88n+6o9A/78QL+Fvgp4CeAo4HvA6+gC7y39svvD8wBbgd+B3gG8CbgEeDD/bqOA7b0w7OAG4E/AQ6gC/5j+nlnAFdPqPH8gfUcD9xN921if+C/AVcOtC3g/wAHAQuAbcDSft6FwO/THaw8vs0p9ksBR/bDzwA2Av++f57HAz8AXjhQ3wPAvxhf95D7frLnOrjd84F7gCV0XaD/C1g9sS2wFNgMLBlyP7y9fz4/AxwIfA749MC8v+yH/xXwHeCigXlfGHg9twMr+/3zWuBHwHNm+j3tY/KHR+j7js/3R8NXA1cA/2lg3h9X1b1V9f+AZcA5VXVNVT1aVRcADwGv7B/PAP5LVT1SVZ8F1k2xvSXAIcB7q+qHVfXjqrp6irYTvQU4r6qur6qHgA/QHdEvHGhzdlXdX1XfA74MHNVPf4SuK+WQndzmK+mC7+yqeriqvkQXlqcPtPlCVf19VT1WVT8ecr3DuKSqrq2q7XSBftSE+acC5wAnVtW1E+ZNtR/eAny8qjZVd67kA8BpfbfaFcAxSfYDXgV8lO6DCuAX+/njHgFW9q/3WuBBhjhPoplhoO87Xl9VB1XVYVX1b/vwHrd5YPgw4D39V/j7+w+B+XThfAiwtaoG7+h2+xTbmw/c3ofUzjpkcL19IN0DzBtoc+fA8I/owhjgfUCAa/tuk7fvxDY3V9VjA9Nun7DNzewZUz2Xce8GLq6qb+7Esk/Yh/3wbOD5VfUdupPjRwHH0n1w3ZHkhTw50O+Z8BpOVp+eJgx0QffVfdxm4I/68B9/PKuqLgT+EZg33k/bWzDFOjcDC6Y40TrdLT7voPtgASDJAcBzga3TPpGqO6vqnVV1CPBvgD8d8md/dwDz+6PWcQsmbHNXbk36Q+BZ4yNJfnoX1nEq8PokZ+7EMk/Yh3TPZTtwVz9+BV2X2ZzqzqtcQde99hzghl2oUU8DBrom+hTwriSvSOeAJCcleTbwVbpQ+O0kz0jyBrqulclcS/cBcHa/jmcmGf9afxdwaJI5Uyx7IfC2JEcl2Z+ue+iaqrptuuKTnJrk0H70ProQfmwHi4y7hu7o8339czsOeB2weohld+RG4MX9c3km3c8ad9YdwKuBM5P85pDLXAj8TpLDkxxItw8vGjjavgJYDlzZj1/ej19dVY/uQo16GjDQ9QRVtR54J/AJukDcSHdij6p6GHhDP34v8Ga6k22TredRukA8EvgesKVvD/AluhOzdya5e5Jl/w74A+B/030oHAGcNuRTeDlwTf8rljXAmVW1abqF+uf2OuBEuhOyfwr8elV9a8jtTrXeb9OdVPw7ul8RDdunP3E936ML9RVJ3jHEIucBn6YL7O8CPwb+3cD8K4Bn80+BfjXdN4kr0V4rT+wOlSTtrTxCl6RGGOiS1IihAj3J0iS39FfurZiiza8muan/qdhnRlumJGk60/ahp7tnxbeB19Cd2FoHnF5VNw20WQRcDBxfVfcleV5VfX/PlS1JmmiYmzEtATaO/1IgyWrgFOCmgTbvBFZV1X0Aw4T5wQcfXAsXLtzpgiVpX3bdddfdXVVzJ5s3TKDP44lXyG2hu8/HoJ8FSPL3dPfw+FBV/fXEFSVZRndpOQsWLGD9+vVDbF6SNC7JVFdnj+yk6GxgEd3NfE4HPpXkoImNqurcqhqrqrG5cyf9gJEk7aJhAn0r3X05xh3Kky/B3gKs6W/g8126PvdFoylRkjSMYQJ9HbCov4R4Dt0Ve2smtPk8/f/SkuRgui6Yaa/OkySNzrSB3t/7YTlwGXAz3V3fNiRZOfAfAFwG3JPkJrpbeL63qu7ZU0VLkp5sxi79HxsbK0+KStLOSXJdVY1NNs8rRSWpEQa6JDXCQJekRhjoktSIYa4UlbQPWrji0pkuoVm3nX3SHlmvR+iS1AgDXZIaYaBLUiMMdElqhIEuSY0w0CWpEQa6JDXCQJekRhjoktQIA12SGmGgS1IjDHRJaoSBLkmNMNAlqREGuiQ1wkCXpEYY6JLUCANdkhphoEtSIwx0SWqEgS5JjTDQJakRQwV6kqVJbkmyMcmKSeafkWRbkhv6xztGX6okaUdmT9cgySxgFfAaYAuwLsmaqrppQtOLqmr5HqhRkjSEYY7QlwAbq2pTVT0MrAZO2bNlSZJ21jCBPg/YPDC+pZ820RuTfD3JZ5PMn2xFSZYlWZ9k/bZt23ahXEnSVEZ1UvQvgYVV9VLgb4ELJmtUVedW1VhVjc2dO3dEm5YkwXCBvhUYPOI+tJ/2uKq6p6oe6kf/DPj50ZQnSRrWMIG+DliU5PAkc4DTgDWDDZK8YGD0ZODm0ZUoSRrGtL9yqartSZYDlwGzgPOqakOSlcD6qloD/HaSk4HtwL3AGXuwZknSJKYNdICqWgusnTDtrIHhDwAfGG1pkqSd4ZWiktQIA12SGmGgS1IjDHRJaoSBLkmNMNAlqREGuiQ1wkCXpEYY6JLUCANdkhphoEtSIwx0SWqEgS5JjTDQJakRBrokNcJAl6RGGOiS1AgDXZIaYaBLUiMMdElqhIEuSY0w0CWpEQa6JDXCQJekRhjoktQIA12SGmGgS1Ijhgr0JEuT3JJkY5IVO2j3xiSVZGx0JUqShjFtoCeZBawCTgQWA6cnWTxJu2cDZwLXjLpISdL0hjlCXwJsrKpNVfUwsBo4ZZJ2fwh8BPjxCOuTJA1pmECfB2weGN/ST3tckpcB86vq0h2tKMmyJOuTrN+2bdtOFytJmtpunxRNsh/wceA907WtqnOraqyqxubOnbu7m5YkDRgm0LcC8wfGD+2njXs28BLg8iS3Aa8E1nhiVJKeWsME+jpgUZLDk8wBTgPWjM+sqgeq6uCqWlhVC4GvASdX1fo9UrEkaVLTBnpVbQeWA5cBNwMXV9WGJCuTnLynC5QkDWf2MI2qai2wdsK0s6Zoe9zulyVJ2lleKSpJjTDQJakRBrokNcJAl6RGGOiS1AgDXZIaYaBLUiMMdElqhIEuSY0w0CWpEQa6JDXCQJekRhjoktQIA12SGmGgS1IjDHRJaoSBLkmNMNAlqREGuiQ1wkCXpEYY6JLUCANdkhphoEtSIwx0SWqEgS5JjTDQJakRBrokNWKoQE+yNMktSTYmWTHJ/Hcl+UaSG5JcnWTx6EuVJO3ItIGeZBawCjgRWAycPklgf6aq/nlVHQV8FPj4yCuVJO3QMEfoS4CNVbWpqh4GVgOnDDaoqv87MHoAUKMrUZI0jNlDtJkHbB4Y3wK8YmKjJL8F/C4wBzh+shUlWQYsA1iwYMHO1ipJ2oGRnRStqlVVdQTwfuCDU7Q5t6rGqmps7ty5o9q0JInhAn0rMH9g/NB+2lRWA6/fnaIkSTtvmEBfByxKcniSOcBpwJrBBkkWDYyeBNw6uhIlScOYtg+9qrYnWQ5cBswCzquqDUlWAuurag2wPMkJwCPAfcBb92TRkqQnG+akKFW1Flg7YdpZA8NnjrguSdJO8kpRSWqEgS5JjTDQJakRBrokNcJAl6RGGOiS1AgDXZIaYaBLUiMMdElqhIEuSY0w0CWpEQa6JDXCQJekRhjoktQIA12SGmGgS1IjDHRJaoSBLkmNMNAlqREGuiQ1wkCXpEYY6JLUCANdkhphoEtSIwx0SWqEgS5JjZg90wXsioUrLp3pEpp129kn7ZH1+prtOXvqNdPeZ6gj9CRLk9ySZGOSFZPM/90kNyX5epIvJjls9KVKknZk2kBPMgtYBZwILAZOT7J4QrN/AMaq6qXAZ4GPjrpQSdKODXOEvgTYWFWbquphYDVwymCDqvpyVf2oH/0acOhoy5QkTWeYQJ8HbB4Y39JPm8pvAH+1O0VJknbeSE+KJvk1YAz4xSnmLwOWASxYsGCUm5akfd4wR+hbgfkD44f2054gyQnA7wMnV9VDk62oqs6tqrGqGps7d+6u1CtJmsIwgb4OWJTk8CRzgNOANYMNkhwNnEMX5t8ffZmSpOlMG+hVtR1YDlwG3AxcXFUbkqxMcnLf7GPAgcBfJLkhyZopVidJ2kOG6kOvqrXA2gnTzhoYPmHEdUmSdpKX/ktSIwx0SWqEgS5JjTDQJakRBrokNcJAl6RGGOiS1AgDXZIaYaBLUiMMdElqhIEuSY0w0CWpEQa6JDXCQJekRhjoktQIA12SGmGgS1IjDHRJaoSBLkmNMNAlqREGuiQ1wkCXpEYY6JLUCANdkhphoEtSIwx0SWqEgS5JjTDQJakRQwV6kqVJbkmyMcmKSea/Ksn1SbYnedPoy5QkTWfaQE8yC1gFnAgsBk5PsnhCs+8BZwCfGXWBkqThzB6izRJgY1VtAkiyGjgFuGm8QVXd1s97bA/UKEkawjBdLvOAzQPjW/ppOy3JsiTrk6zftm3brqxCkjSFp/SkaFWdW1VjVTU2d+7cp3LTktS8YQJ9KzB/YPzQfpok6WlkmEBfByxKcniSOcBpwJo9W5YkaWdNG+hVtR1YDlwG3AxcXFUbkqxMcjJAkpcn2QKcCpyTZMOeLFqS9GTD/MqFqloLrJ0w7ayB4XV0XTGSpBnilaKS1AgDXZIaYaBLUiMMdElqhIEuSY0w0CWpEQa6JDXCQJekRhjoktQIA12SGmGgS1IjDHRJaoSBLkmNMNAlqREGuiQ1wkCXpEYY6JLUCANdkhphoEtSIwx0SWqEgS5JjTDQJakRBrokNcJAl6RGGOiS1AgDXZIaYaBLUiOGCvQkS5PckmRjkhWTzN8/yUX9/GuSLBx1oZKkHZs20JPMAlYBJwKLgdOTLJ7Q7DeA+6rqSOBPgI+MulBJ0o4Nc4S+BNhYVZuq6mFgNXDKhDanABf0w58FXp0koytTkjSd2UO0mQdsHhjfArxiqjZVtT3JA8BzgbsHGyVZBizrRx9McsuuFL0XOpgJ++LpKn63gr3o9QJfs96+9JodNtWMYQJ9ZKrqXODcp3KbTwdJ1lfV2EzXoeH4eu19fM06w3S5bAXmD4wf2k+btE2S2cBPAveMokBJ0nCGCfR1wKIkhyeZA5wGrJnQZg3w1n74TcCXqqpGV6YkaTrTdrn0feLLgcuAWcB5VbUhyUpgfVWtAf4H8OkkG4F76UJf/2Sf62bay/l67X18zYB4IC1JbfBKUUlqhIEuSY0w0CU9bSU5I8knZrqOvYWBLkmNMNB3U5KVSd49MP5HSc5M8t4k65J8Pcl/7OcdkOTSJDcm+WaSN89c5QJIsjDJzUk+lWRDkr9J8hNJjkrytf71uyTJc2a61hb0+/ubA+O/l+RDSS5P8pEk1yb5dpJjJ1n2pCRfTXJwkvOT/NckX0myKcmb+jZJ8rH+7+sb439jSVYlObkfviTJef3w2/u/2UnfB0/NXhkdA333nQf8OkCS/eh+snknsIjuPjhHAT+f5FXAUuCOqvq5qnoJ8NczU7ImWASsqqoXA/cDbwT+HHh/Vb0U+AbwH2awvn3F7KpaArybCfs7ya8AK4DXVtX4Jf4vAI4Bfhk4u5/2Brq/uZ8DTgA+luQFwFXA+IfEPLobDdJPu7Ifnux9sFcx0HdTVd0G3JPkaOCXgH8AXj4wfD3wz+jeLN8AXtMfiRxbVQ/MTNWa4LtVdUM/fB1wBHBQVV3RT7sAeNWMVLZv+Vz/73XAwoHpxwPvB06qqvsGpn++qh6rqpuA5/fTjgEurKpHq+ou4Aq6v8ergGP7O8XeBNzVB/0vAF/pl534PhisYa/wlN7LpWF/BpwB/DTdEfurgT+uqnMmNkzyMuC1wIeTfLGqVj6VhWpSDw0MPwocNFOF7AO288QDyWcODI+/Do/yxGz6DvAzwM8C6ydpD7DDu7tW1dYkB9F9S74S+CngV4EHq+oHSZ7Lk98Hdrnsoy6he6O8nO6K2suAtyc5ECDJvCTPS3II8KOq+p/Ax4CXzVTB2qEHgPsG+nH/Nd2RnnbfXcDzkjw3yf503SXTuZ2+GyzJi6dpexXw5iSzksyl+2Z1bT/va3TdOVf27X6v/7cZHqGPQFU9nOTLwP1V9SjwN0leBHy1vy38g8CvAUfS9ek9BjwC/OZM1axpvRX4ZJJnAZuAt81wPU2oqkf624ZcS3dTv28Nudy3krwF+Iskr9tB00voulFuBAp4X1Xd2c+7CvilqtqY5Ha6o/SmAt1L/0egPxl6PXBqVd060/VI2jfZ5bKb+pMsG4EvGuaSZpJH6JLUCI/QJakRBrokNcJAl6RGGOiS1AgDXZIa8f8BDNWp19GTwoIAAAAASUVORK5CYII=", + "text/plain": [ + "
" + ] + }, + "metadata": { + "needs_background": "light", + "tags": [] + }, + "output_type": "display_data" + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "/content/data/mini_speech_commands/unknown/b9515bf3_nohash_1.wav\n" + ] + }, + { + "data": { + "image/png": "iVBORw0KGgoAAAANSUhEUgAAAXQAAAEICAYAAABPgw/pAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAALEgAACxIB0t1+/AAAADh0RVh0U29mdHdhcmUAbWF0cGxvdGxpYiB2ZXJzaW9uMy4yLjIsIGh0dHA6Ly9tYXRwbG90bGliLm9yZy+WH4yJAAAUd0lEQVR4nO3df7RdZX3n8feHxGAFV7FytZIEQyE6RseCvUa7BixF7ApSwapUGDsVrWbsNFNsrRqnlnFSO0WZZWdNTSvYYUGdkUAd0XRIh7Yqv6pALhTUgMg1gkkoGH6O6AgEvvPH2ZeeXO7NPUlOuOTJ+7XWWdk/nr339+xz7ufs8+yzd1JVSJL2fvvNdgGSpOEw0CWpEQa6JDXCQJekRhjoktQIA12SGmGga7ckOT/JR7vhY5Lcuovr+VSSPxhudTNu81eSbEryUJKjnsptDypJJTlituvQ3mHubBegPS/J7cDzgceAHwJ/A6yoqoeGuZ2qugp48QD1nA68q6qO7lv2PcOsZUD/hd5++OIwVpbkcuAjwLEAVfWRYaz36SrJ+cDl3eixVXX6rBUjwCP0fckbqupA4BXAKPDhyQ2S7Gsf8C8ENuzKgknmDLkWabcZ6PuYqtpC7wj9ZfDEV/rfSnIbcFs37ZeT3JjkgSRfTfLyieWTHJXkhiQ/SHIR8My+eccm2dw3vjDJ55NsTXJvkk8meQnwKeDnu66OB7q2T3TddOPvTjKe5L4ka5Mc0jevkrwnyW1djauTpJt3RJIrkjyY5J6uxu0k2T/JQ8Ac4KYk3+mmvyTJ5d06NyQ5qW+Z85P8eZJ1SX4I/OIg+zvJ6UmunjTtiW6Ubr2rk1za7dNrkxw+zbqO7rqIjh1gP+yX5MNJ7kjy/SR/meQnu3kXJHlfNzx/4j3QjR/e7fP9Jl7PJO/r1vFPSd4xyPPWLKkqH40/gNuB47vhhfSOSv+wGy/g74CfAn4COAr4PvAqeoH39m75/YF5wB3A7wDPAN4CPAp8tFvXscDmbngOcBPwJ8AB9IL/6G7e6cDVk2o8v289xwH30Ps2sT/wp8CVfW0L+N/AQcChwFZgWTfvQuD36R2sPLHNafZLAUd0w88AxoH/0D3P44AfAC/uq+9B4F9NrHvAfT/Vc+3f7vnAvcBSel2g/xNYM7ktsAzYBCwdcD+8s3s+PwMcCHwe+EzfvL/uhv818B3gor55X+x7PbcBq7r983rgR8BzZvs97WPqh0fo+44vdEfDVwNXAP+5b94fV9V9VfX/gOXAOVV1bVU9VlUXAA8Dr+4ezwD+a1U9WlWfA9ZPs72lwCHA+6vqh1X146q6epq2k70NOK+qbqiqh4EP0TuiX9TX5qyqeqCqvgd8BTiym/4ova6UQ3Zym6+mF3xnVdUjVfVlemF5Wl+bL1bVP1TV41X14wHXO4hLquq6qtpGL9CPnDT/FOAc4ISqum7SvOn2w9uAT1TVxuqdK/kQcGrXrXYFcHSS/YDXAB+n90EF8Avd/AmPAqu613sd8BADnCfR7DDQ9x1vrKqDquqFVfXvuvCesKlv+IXA+7qv8A90HwIL6YXzIcCWquq/o9sd02xvIXBHF1I765D+9XaBdC8wv6/NXX3DP6IXxgAfAAJc13WbvHMntrmpqh7vm3bHpG1uYs+Y7rlMeC9wcVV9cyeW3W4fdsNzgedX1XfonRw/EjiG3gfXnUlezJMD/d5Jr+FU9elpwkAX9L66T9gE/FEX/hOPZ1XVhcA/AfMn+mk7h06zzk3AodOcaJ3pFp930vtgASDJAcBzgS0zPpGqu6rq3VV1CPBvgT8b8Gd/dwILu6PWCYdO2uau3Jr0h8CzJkaS/PQurOMU4I1JztiJZbbbh/Seyzbg7m78CnpdZvOqd17lCnrda88BbtyFGvU0YKBrsk8D70nyqvQckOTEJM8GvkYvFH47yTOSvIle18pUrqP3AXBWt45nJpn4Wn83sCDJvGmWvRB4R5Ijk+xPr3vo2qq6fabik5ySZEE3ej+9EH58B4tMuJbe0ecHuud2LPAGYM0Ay+7ITcBLu+fyTHo/a9xZdwKvBc5I8psDLnMh8DtJDktyIL19eFHf0fYVwArgym788m786qp6bBdq1NOAga7tVNUY8G7gk/QCcZzeiT2q6hHgTd34fcBb6Z1sm2o9j9ELxCOA7wGbu/YAX6Z3YvauJPdMsezfA38A/C96HwqHA6cO+BReCVzb/YplLXBGVW2caaHuub0BOIHeCdk/A369qr414HanW++36Z1U/Ht6vyIatE9/8nq+Ry/UVyZ51wCLnAd8hl5gfxf4MfDv++ZfATybfw70q+l9k7gS7bWyfXeoJGlv5RG6JDXCQJekRhjoktSIgQI9ybIkt3aXYq+cps2vJrm5++3vZ4dbpiRpJjOeFE3vJkTfBl5H75cK64HTqurmvjaLgYuB46rq/iTPq6rv72i9Bx98cC1atGg3y5ekfcv1119/T1WNTDVvkLvrLQXGJ376lWQNcDJwc1+bdwOrq+p+gJnCHGDRokWMjY0NsHlJ0oQk012dPVCXy3y2v+R5M9tfDg3wIuBFSf4hyTVJlk1TyPIkY0nGtm7dOsCmJUmDGtZJ0bnAYnp3ZzsN+HSSgyY3qqpzq2q0qkZHRqb8xiBJ2kWDBPoWejdamrCAJ99TYzOwtrsj23fp9bkvHk6JkqRBDBLo64HF3T0h5tG7BHvtpDZfoPtvt5IcTK8LZsbLrSVJwzNjoHc381kBXAbcQu82nhuSrOr7H10uA+5NcjO9ezK/v6ru3VNFS5KebNbu5TI6Olr+ykWSdk6S66tqdKp5XikqSY0w0CWpEQa6JDVikCtFJe2DFq28dLZLaNbtZ524R9brEbokNcJAl6RGGOiS1AgDXZIaYaBLUiMMdElqhIEuSY0w0CWpEQa6JDXCQJekRhjoktQIA12SGmGgS1IjDHRJaoSBLkmNMNAlqREGuiQ1wkCXpEYY6JLUCANdkhphoEtSIwx0SWqEgS5JjRgo0JMsS3JrkvEkK6eYf3qSrUlu7B7vGn6pkqQdmTtTgyRzgNXA64DNwPoka6vq5klNL6qqFXugRknSAAY5Ql8KjFfVxqp6BFgDnLxny5Ik7axBAn0+sKlvfHM3bbI3J/l6ks8lWTjVipIsTzKWZGzr1q27UK4kaTrDOin618Ciqno58HfABVM1qqpzq2q0qkZHRkaGtGlJEgwW6FuA/iPuBd20J1TVvVX1cDf6F8DPDac8SdKgBgn09cDiJIclmQecCqztb5DkBX2jJwG3DK9ESdIgZvyVS1VtS7ICuAyYA5xXVRuSrALGqmot8NtJTgK2AfcBp+/BmiVJU5gx0AGqah2wbtK0M/uGPwR8aLilSZJ2hleKSlIjDHRJaoSBLkmNMNAlqREGuiQ1wkCXpEYY6JLUCANdkhphoEtSIwx0SWqEgS5JjTDQJakRBrokNcJAl6RGGOiS1AgDXZIaYaBLUiMMdElqhIEuSY0w0CWpEQa6JDXCQJekRhjoktQIA12SGmGgS1IjDHRJaoSBLkmNGCjQkyxLcmuS8SQrd9DuzUkqyejwSpQkDWLGQE8yB1gNnAAsAU5LsmSKds8GzgCuHXaRkqSZDXKEvhQYr6qNVfUIsAY4eYp2fwh8DPjxEOuTJA1okECfD2zqG9/cTXtCklcAC6vq0h2tKMnyJGNJxrZu3brTxUqSprfbJ0WT7Ad8AnjfTG2r6tyqGq2q0ZGRkd3dtCSpzyCBvgVY2De+oJs24dnAy4DLk9wOvBpY64lRSXpqDRLo64HFSQ5LMg84FVg7MbOqHqyqg6tqUVUtAq4BTqqqsT1SsSRpSjMGelVtA1YAlwG3ABdX1YYkq5KctKcLlCQNZu4gjapqHbBu0rQzp2l77O6XJUnaWV4pKkmNMNAlqREGuiQ1wkCXpEYY6JLUCANdkhphoEtSIwx0SWqEgS5JjTDQJakRBrokNcJAl6RGGOiS1AgDXZIaYaBLUiMMdElqhIEuSY0w0CWpEQa6JDXCQJekRhjoktQIA12SGmGgS1IjDHRJaoSBLkmNMNAlqREGuiQ1YqBAT7Isya1JxpOsnGL+e5J8I8mNSa5OsmT4pUqSdmTGQE8yB1gNnAAsAU6bIrA/W1X/sqqOBD4OfGLolUqSdmiQI/SlwHhVbayqR4A1wMn9Darq//aNHgDU8EqUJA1i7gBt5gOb+sY3A6+a3CjJbwG/C8wDjptqRUmWA8sBDj300J2tVZK0A0M7KVpVq6vqcOCDwIenaXNuVY1W1ejIyMiwNi1JYrBA3wIs7Btf0E2bzhrgjbtTlCRp5w0S6OuBxUkOSzIPOBVY298gyeK+0ROB24ZXoiRpEDP2oVfVtiQrgMuAOcB5VbUhySpgrKrWAiuSHA88CtwPvH1PFi1JerJBTopSVeuAdZOmndk3fMaQ65Ik7SSvFJWkRhjoktQIA12SGmGgS1IjDHRJaoSBLkmNMNAlqREGuiQ1wkCXpEYY6JLUCANdkhphoEtSIwx0SWqEgS5JjTDQJakRBrokNcJAl6RGGOiS1AgDXZIaYaBLUiMMdElqhIEuSY0w0CWpEQa6JDXCQJekRhjoktSIubNdwK5YtPLS2S6hWbefdeIeWa+v2Z6zp14z7X0GOkJPsizJrUnGk6ycYv7vJrk5ydeTfCnJC4dfqiRpR2YM9CRzgNXACcAS4LQkSyY1+0dgtKpeDnwO+PiwC5Uk7dggR+hLgfGq2lhVjwBrgJP7G1TVV6rqR93oNcCC4ZYpSZrJIIE+H9jUN765mzad3wD+ZqoZSZYnGUsytnXr1sGrlCTNaKi/cknya8AocPZU86vq3KoararRkZGRYW5akvZ5g/zKZQuwsG98QTdtO0mOB34f+IWqeng45UmSBjXIEfp6YHGSw5LMA04F1vY3SHIUcA5wUlV9f/hlSpJmMmOgV9U2YAVwGXALcHFVbUiyKslJXbOzgQOBv0pyY5K106xOkrSHDHRhUVWtA9ZNmnZm3/DxQ65LkrSTvPRfkhphoEtSIwx0SWqEgS5JjTDQJakRBrokNcJAl6RGGOiS1AgDXZIaYaBLUiMMdElqhIEuSY0w0CWpEQa6JDXCQJekRhjoktQIA12SGmGgS1IjDHRJaoSBLkmNMNAlqREGuiQ1wkCXpEYY6JLUCANdkhphoEtSIwx0SWrEQIGeZFmSW5OMJ1k5xfzXJLkhybYkbxl+mZKkmcwY6EnmAKuBE4AlwGlJlkxq9j3gdOCzwy5QkjSYuQO0WQqMV9VGgCRrgJOBmycaVNXt3bzH90CNkqQBDNLlMh/Y1De+uZu205IsTzKWZGzr1q27sgpJ0jSe0pOiVXVuVY1W1ejIyMhTuWlJat4ggb4FWNg3vqCbJkl6Ghkk0NcDi5MclmQecCqwds+WJUnaWTMGelVtA1YAlwG3ABdX1YYkq5KcBJDklUk2A6cA5yTZsCeLliQ92SC/cqGq1gHrJk07s294Pb2uGEnSLPFKUUlqhIEuSY0w0CWpEQa6JDXCQJekRhjoktQIA12SGmGgS1IjDHRJaoSBLkmNMNAlqREGuiQ1wkCXpEYY6JLUCANdkhphoEtSIwx0SWqEgS5JjTDQJakRBrokNcJAl6RGGOiS1AgDXZIaYaBLUiMMdElqhIEuSY0w0CWpEQMFepJlSW5NMp5k5RTz909yUTf/2iSLhl2oJGnHZgz0JHOA1cAJwBLgtCRLJjX7DeD+qjoC+BPgY8MuVJK0Y4McoS8FxqtqY1U9AqwBTp7U5mTggm74c8Brk2R4ZUqSZjJ3gDbzgU1945uBV03Xpqq2JXkQeC5wT3+jJMuB5d3oQ0lu3ZWi90IHM2lfPF3F71awF71e4GvW2ZdesxdON2OQQB+aqjoXOPep3ObTQZKxqhqd7To0GF+vvY+vWc8gXS5bgIV94wu6aVO2STIX+Eng3mEUKEkazCCBvh5YnOSwJPOAU4G1k9qsBd7eDb8F+HJV1fDKlCTNZMYul65PfAVwGTAHOK+qNiRZBYxV1VrgvwOfSTIO3Ecv9PXP9rlupr2cr9fex9cMiAfSktQGrxSVpEYY6JLUCANd0tNWktOTfHK269hbGOiS1AgDfTclWZXkvX3jf5TkjCTvT7I+ydeT/Kdu3gFJLk1yU5JvJnnr7FUugCSLktyS5NNJNiT52yQ/keTIJNd0r98lSZ4z27W2oNvf3+wb/70kH0lyeZKPJbkuybeTHDPFsicm+VqSg5Ocn+S/Jflqko1J3tK1SZKzu7+vb0z8jSVZneSkbviSJOd1w+/s/manfB88NXtleAz03Xce8OsASfaj95PNu4DF9O6DcyTwc0leAywD7qyqn62qlwH/Z3ZK1iSLgdVV9VLgAeDNwF8CH6yqlwPfAP7jLNa3r5hbVUuB9zJpfyf5FWAl8PqqmrjE/wXA0cAvA2d1095E72/uZ4HjgbOTvAC4Cpj4kJhP70aDdNOu7Ianeh/sVQz03VRVtwP3JjkK+CXgH4FX9g3fAPwLem+WbwCv645EjqmqB2enak3y3aq6sRu+HjgcOKiqruimXQC8ZlYq27d8vvv3emBR3/TjgA8CJ1bV/X3Tv1BVj1fVzcDzu2lHAxdW1WNVdTdwBb2/x6uAY7o7xd4M3N0F/c8DX+2Wnfw+6K9hr/CU3sulYX8BnA78NL0j9tcCf1xV50xumOQVwOuBjyb5UlWteioL1ZQe7ht+DDhotgrZB2xj+wPJZ/YNT7wOj7F9Nn0H+BngRcDYFO0Bdnh316rakuQget+SrwR+CvhV4KGq+kGS5/Lk94FdLvuoS+i9UV5J74ray4B3JjkQIMn8JM9Lcgjwo6r6H8DZwCtmq2Dt0IPA/X39uP+G3pGedt/dwPOSPDfJ/vS6S2ZyB103WJKXztD2KuCtSeYkGaH3zeq6bt419Lpzruza/V73bzM8Qh+CqnokyVeAB6rqMeBvk7wE+Fp3W/iHgF8DjqDXp/c48Cjwm7NVs2b0duBTSZ4FbATeMcv1NKGqHu1uG3IdvZv6fWvA5b6V5G3AXyV5ww6aXkKvG+UmoIAPVNVd3byrgF+qqvEkd9A7Sm8q0L30fwi6k6E3AKdU1W2zXY+kfZNdLrupO8kyDnzJMJc0mzxCl6RGeIQuSY0w0CWpEQa6JDXCQJekRhjoktSI/w/TIK6YopbnBgAAAABJRU5ErkJggg==", + "text/plain": [ + "
" + ] + }, + "metadata": { + "needs_background": "light", + "tags": [] + }, + "output_type": "display_data" + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "/content/data/mini_speech_commands/unknown/15c563d7_nohash_3.wav\n" + ] + }, + { + "data": { + "image/png": "iVBORw0KGgoAAAANSUhEUgAAAXoAAAEICAYAAABRSj9aAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAALEgAACxIB0t1+/AAAADh0RVh0U29mdHdhcmUAbWF0cGxvdGxpYiB2ZXJzaW9uMy4yLjIsIGh0dHA6Ly9tYXRwbG90bGliLm9yZy+WH4yJAAAXuklEQVR4nO3df7RddX3m8fdDMFChFYRbW/KDBIgOodrgXEK7BGQUMEgl1IEhVKeg1gwOmWLRahwtOlGnCLNs14xxgE6zoM5AQJF6Z4hDqUCAQSCXX2LQlEsEkiAYCFARBBKe+eN8Q3dOb3J3cu/NTb55Xmudlb2/P/b5nHNunrPz3eeeyDYREVGv3ca6gIiIGF0J+oiIyiXoIyIql6CPiKhcgj4ionIJ+oiIyiXoY1RIukzSl8r20ZJWbONxLpb0ZyNb3ZD3+fuSVkl6XtLh2/O+25JkSYeMdR2xc9h9rAuIsSPpEeBNwAbgF8B3gXm2nx/J+7F9K/CWFvWcBfyR7aMac88eyVpa+i90nofvjMTBJN0MfAE4FsD2F0biuDsqSZcBN5fdY22fNWbFBJAz+oD32d4beDvQC3yue4CkXe2E4EBg+bZMlDRuhGuJGLYEfQBgew2dM/rfgteWBs6R9BDwUGn7PUn3SXpW0u2S3rZxvqTDJd0j6eeSrgL2bPQdK2l1Y3+SpG9LWivpaUlfk3QocDHwu2XJ5Nky9rUloLL/UUkDktZJ6pN0QKPPks6W9FCpcaEklb5DJC2V9Jykp0qNm5C0h6TngXHA/ZIeLu2HSrq5HHO5pJMbcy6T9N8lLZH0C+BftXm+JZ0l6bautteWY8pxF0q6rjynd0o6eDPHOqosNR3b4nnYTdLnJD0q6WeS/kbSG0rf5ZI+UbYnbPwZKPsHl+d8t42vp6RPlGP8VNKH2jzuGCO2c9tFb8AjwHFlexKds9gvln0DNwBvBH4FOBz4GXAknSA8s8zfAxgPPAr8CfA64FTgFeBL5VjHAqvL9jjgfuAvgL3ovCEcVfrOAm7rqvGyxnHeBTxF518fewD/DbilMdbA/wH2ASYDa4FZpe9K4LN0Tm5eu8/NPC8GDinbrwMGgP9YHue7gJ8Db2nU9xzwjo3HbvncD/ZYm/d7GfA0MJPOEuv/AhZ3jwVmAauAmS2fhw+Xx3MQsDfwbeAbjb7/Xbb/AHgYuKrR953G67keWFCen/cCLwD7jvXPdG6D33JGH39bzp5vA5YC/7nR9+e219l+EZgLXGL7TtsbbF8OvAT8Trm9DvhL26/Y/hawbDP3NxM4APhT27+w/Uvbt21mbLcPAIts32P7JeAzdP4FMKUx5gLbz9p+DLgJmFHaX6GzJHPAVt7n79AJxAtsv2z7RjohekZjzHds/z/br9r+ZcvjtnGt7btsr6cT9DO6+k8DLgFOtH1XV9/mnocPAF+1vdKdazGfAeaU5bmlwFGSdgOOAS6k8wYG8M7Sv9ErwILyei8BnqfFdZgYGwn6OMX2PrYPtP3vS6hvtKqxfSDwibIU8Gx5c5hEJ7QPANbYbn5D3qObub9JwKMlvLbWAc3jlqB6GpjQGPNEY/sFOiEN8ClAwF1l+eXDW3Gfq2y/2mh7tOs+VzE6NvdYNvo4cLXtH27F3E2ew7K9O/Am2w/TuSg/Aziazhva45Lewj8P+qe7XsPB6osdRII+tqQZ3KuAL5c3hY2319u+EvgpMGHjOnAxeTPHXAVM3swF3qG+SvVxOm84AEjaC9gPWDPkA7GfsP1R2wcA/w74esuPJz4OTCpnuRtN7rrPbfkK2F8Ar9+4I+k3tuEYpwGnSDp3K+Zs8hzSeSzrgSfL/lI6S2/j3blus5TOMt2+wH3bUGPsABL00dZfAWdLOlIde0k6SdKvAt+nExZ/LOl1kt5PZ4lmMHfReWO4oBxjT0kblweeBCZKGr+ZuVcCH5I0Q9IedJaZ7rT9yFDFSzpN0sSy+wydcH51C1M2upPO2eqnymM7FngfsLjF3C25HzisPJY96Xz8cms9DrwbOFfSx1rOuRL4E0lTJe1N5zm8qnF2vhSYB9xS9m8u+7fZ3rANNcYOIEEfrdjuBz4KfI1OUA7QuaCI7ZeB95f9dcDpdC7yDXacDXSC8hDgMWB1GQ9wI50Lwk9IemqQuX8P/BlwDZ03i4OBOS0fwhHAneVTNX3AubZXDjWpPLb3ASfSuRD8deAPbf+45f1u7rj/QOdi5t/T+VRT22sG3cd5jE7Yz5f0Ry2mLAK+QSfIfwL8EvgPjf6lwK/yT0F/G51/edxC7LS06bJqRETUJmf0ERGVS9BHRFQuQR8RUbkEfURE5Xa4L6vaf//9PWXKlLEuIyJip3L33Xc/ZbtnsL4dLuinTJlCf3//WJcREbFTkbS530bP0k1ERO0S9BERlUvQR0RULkEfEVG5BH1EROUS9BERlUvQR0RULkEfEVG5BH1EROV2uN+MHa4p868b6xKq9cgFJ411CRGxDXJGHxFRuQR9RETlEvQREZVL0EdEVC5BHxFRuQR9RETlEvQREZVL0EdEVC5BHxFRuQR9RETlWgW9pFmSVkgakDR/kP6zJT0g6T5Jt0maXtqnSHqxtN8n6eKRfgAREbFlQ37XjaRxwELgeGA1sExSn+0HG8OusH1xGX8y8FVgVul72PaMkS07IiLaanNGPxMYsL3S9svAYmB2c4Dtf2zs7gV45EqMiIjhaBP0E4BVjf3VpW0Tks6R9DBwIfDHja6pku6VtFTS0YPdgaS5kvol9a9du3Yryo+IiKGM2MVY2wttHwx8Gvhcaf4pMNn24cB5wBWSfm2QuZfa7rXd29PTM1IlRUQE7YJ+DTCpsT+xtG3OYuAUANsv2X66bN8NPAy8edtKjYiIbdEm6JcB0yRNlTQemAP0NQdImtbYPQl4qLT3lIu5SDoImAasHInCIyKinSE/dWN7vaR5wPXAOGCR7eWSFgD9tvuAeZKOA14BngHOLNOPARZIegV4FTjb9rrReCARETG4Vv+VoO0lwJKutvMb2+duZt41wDXDKTAiIoYnvxkbEVG5BH1EROUS9BERlWu1Rh8xWqbMv26sS6jWIxecNNYlxA4iZ/QREZVL0EdEVC5BHxFRuQR9RETlEvQREZVL0EdEVC5BHxFRuQR9RETlEvQREZVL0EdEVC5BHxFRuQR9RETlEvQREZVL0EdEVK5V0EuaJWmFpAFJ8wfpP1vSA5Luk3SbpOmNvs+UeSskvWcki4+IiKENGfSSxgELgROB6cAZzSAvrrD9VtszgAuBr5a504E5wGHALODr5XgREbGdtDmjnwkM2F5p+2VgMTC7OcD2PzZ29wJctmcDi22/ZPsnwEA5XkREbCdt/oepCcCqxv5q4MjuQZLOAc4DxgPvasy9o2vuhEHmzgXmAkyePLlN3RER0dKIXYy1vdD2wcCngc9t5dxLbffa7u3p6RmpkiIignZBvwaY1NifWNo2ZzFwyjbOjYiIEdYm6JcB0yRNlTSezsXVvuYASdMauycBD5XtPmCOpD0kTQWmAXcNv+yIiGhryDV62+slzQOuB8YBi2wvl7QA6LfdB8yTdBzwCvAMcGaZu1zS1cCDwHrgHNsbRumxRETEINpcjMX2EmBJV9v5je1ztzD3y8CXt7XAiIgYnlZBHxGx0ZT51411CdV65IKTRuW4+QqEiIjKJegjIiqXoI+IqFyCPiKicgn6iIjKJegjIiqXoI+IqFyCPiKicgn6iIjKJegjIiqXoI+IqFyCPiKicgn6iIjKJegjIiqXoI+IqFyCPiKicgn6iIjKJegjIirXKuglzZK0QtKApPmD9J8n6UFJP5D0PUkHNvo2SLqv3PpGsviIiBjakP9nrKRxwELgeGA1sExSn+0HG8PuBXptvyDpY8CFwOml70XbM0a47oiIaKnNGf1MYMD2StsvA4uB2c0Btm+y/ULZvQOYOLJlRkTEtmoT9BOAVY391aVtcz4CfLexv6ekfkl3SDplsAmS5pYx/WvXrm1RUkREtDXk0s3WkPRBoBd4Z6P5QNtrJB0E3CjpAdsPN+fZvhS4FKC3t9cjWVNExK6uzRn9GmBSY39iaduEpOOAzwIn235pY7vtNeXPlcDNwOHDqDciIrZSm6BfBkyTNFXSeGAOsMmnZyQdDlxCJ+R/1mjfV9IeZXt/4B1A8yJuRESMsiGXbmyvlzQPuB4YByyyvVzSAqDfdh9wEbA38E1JAI/ZPhk4FLhE0qt03lQu6Pq0TkREjLJWa/S2lwBLutrOb2wft5l5twNvHU6BERExPPnN2IiIyiXoIyIql6CPiKhcgj4ionIJ+oiIyiXoIyIql6CPiKhcgj4ionIJ+oiIyiXoIyIql6CPiKhcgj4ionIJ+oiIyiXoIyIql6CPiKhcgj4ionIJ+oiIyiXoIyIq1yroJc2StELSgKT5g/SfJ+lBST+Q9D1JBzb6zpT0ULmdOZLFR0TE0IYMeknjgIXAicB04AxJ07uG3Qv02n4b8C3gwjL3jcDngSOBmcDnJe07cuVHRMRQ2pzRzwQGbK+0/TKwGJjdHGD7JtsvlN07gIll+z3ADbbX2X4GuAGYNTKlR0REG22CfgKwqrG/urRtzkeA727j3IiIGGG7j+TBJH0Q6AXeuZXz5gJzASZPnjySJUVE7PLanNGvASY19ieWtk1IOg74LHCy7Ze2Zq7tS2332u7t6elpW3tERLTQJuiXAdMkTZU0HpgD9DUHSDocuIROyP+s0XU9cIKkfctF2BNKW0REbCdDLt3YXi9pHp2AHgcssr1c0gKg33YfcBGwN/BNSQCP2T7Z9jpJX6TzZgGwwPa6UXkkERExqFZr9LaXAEu62s5vbB+3hbmLgEXbWmBERAxPfjM2IqJyCfqIiMol6CMiKpegj4ioXII+IqJyCfqIiMol6CMiKpegj4ioXII+IqJyCfqIiMol6CMiKpegj4ioXII+IqJyCfqIiMol6CMiKpegj4ioXII+IqJyCfqIiMol6CMiKtcq6CXNkrRC0oCk+YP0HyPpHknrJZ3a1bdB0n3l1jdShUdERDtD/ufgksYBC4HjgdXAMkl9th9sDHsMOAv45CCHeNH2jBGoNSIitsGQQQ/MBAZsrwSQtBiYDbwW9LYfKX2vjkKNERExDG2WbiYAqxr7q0tbW3tK6pd0h6RTBhsgaW4Z07927dqtOHRERAxle1yMPdB2L/AHwF9KOrh7gO1Lbffa7u3p6dkOJUVE7DraBP0aYFJjf2Jpa8X2mvLnSuBm4PCtqC8iIoapTdAvA6ZJmippPDAHaPXpGUn7StqjbO8PvIPG2n5ERIy+IYPe9npgHnA98CPgatvLJS2QdDKApCMkrQZOAy6RtLxMPxTol3Q/cBNwQdendSIiYpS1+dQNtpcAS7razm9sL6OzpNM973bgrcOsMSIihiG/GRsRUbkEfURE5RL0ERGVS9BHRFQuQR8RUbkEfURE5RL0ERGVS9BHRFQuQR8RUbkEfURE5RL0ERGVS9BHRFQuQR8RUbkEfURE5RL0ERGVS9BHRFQuQR8RUbkEfURE5VoFvaRZklZIGpA0f5D+YyTdI2m9pFO7+s6U9FC5nTlShUdERDtDBr2kccBC4ERgOnCGpOldwx4DzgKu6Jr7RuDzwJHATODzkvYdftkREdFWmzP6mcCA7ZW2XwYWA7ObA2w/YvsHwKtdc98D3GB7ne1ngBuAWSNQd0REtNQm6CcAqxr7q0tbG8OZGxERI2CHuBgraa6kfkn9a9euHetyIiKq0ibo1wCTGvsTS1sbrebavtR2r+3enp6eloeOiIg22gT9MmCapKmSxgNzgL6Wx78eOEHSvuUi7AmlLSIitpMhg972emAenYD+EXC17eWSFkg6GUDSEZJWA6cBl0haXuauA75I581iGbCgtEVExHaye5tBtpcAS7razm9sL6OzLDPY3EXAomHUGBERw7BDXIyNiIjRk6CPiKhcgj4ionIJ+oiIyiXoIyIql6CPiKhcgj4ionIJ+oiIyiXoIyIql6CPiKhcgj4ionIJ+oiIyiXoIyIql6CPiKhcgj4ionIJ+oiIyiXoIyIql6CPiKhcgj4ionKtgl7SLEkrJA1Imj9I/x6Srir9d0qaUtqnSHpR0n3ldvHIlh8REUMZ8j8HlzQOWAgcD6wGlknqs/1gY9hHgGdsHyJpDvAV4PTS97DtGSNcd0REtNTmjH4mMGB7pe2XgcXA7K4xs4HLy/a3gHdL0siVGRER26pN0E8AVjX2V5e2QcfYXg88B+xX+qZKulfSUklHD3YHkuZK6pfUv3bt2q16ABERsWWjfTH2p8Bk24cD5wFXSPq17kG2L7Xda7u3p6dnlEuKiNi1tAn6NcCkxv7E0jboGEm7A28Anrb9ku2nAWzfDTwMvHm4RUdERHttgn4ZME3SVEnjgTlAX9eYPuDMsn0qcKNtS+opF3ORdBAwDVg5MqVHREQbQ37qxvZ6SfOA64FxwCLbyyUtAPpt9wF/DXxD0gCwjs6bAcAxwAJJrwCvAmfbXjcaDyQiIgY3ZNAD2F4CLOlqO7+x/UvgtEHmXQNcM8waIyJiGPKbsRERlUvQR0RULkEfEVG5BH1EROUS9BERlUvQR0RULkEfEVG5BH1EROUS9BERlUvQR0RULkEfEVG5BH1EROUS9BERlUvQR0RULkEfEVG5BH1EROUS9BERlUvQR0RULkEfEVG5VkEvaZakFZIGJM0fpH8PSVeV/jslTWn0faa0r5D0npErPSIi2hgy6CWNAxYCJwLTgTMkTe8a9hHgGduHAH8BfKXMnQ7MAQ4DZgFfL8eLiIjtpM0Z/UxgwPZK2y8Di4HZXWNmA5eX7W8B75ak0r7Y9ku2fwIMlONFRMR2snuLMROAVY391cCRmxtje72k54D9SvsdXXMndN+BpLnA3LL7vKQVrarf+e0PPDXWRbSlr4x1BTuEneY1y+v1ml3lNTtwcx1tgn7U2b4UuHSs69jeJPXb7h3rOqK9vGY7n7xm7ZZu1gCTGvsTS9ugYyTtDrwBeLrl3IiIGEVtgn4ZME3SVEnj6Vxc7esa0wecWbZPBW607dI+p3wqZyowDbhrZEqPiIg2hly6KWvu84DrgXHAItvLJS0A+m33AX8NfEPSALCOzpsBZdzVwIPAeuAc2xtG6bHsjHa55aoK5DXb+ezyr5k6J94REVGr/GZsRETlEvQREZVL0EfETknSWZK+NtZ17AwS9BERlUvQjyJJCyR9vLH/ZUnnSvpTScsk/UDSfyp9e0m6TtL9kn4o6fSxqzwAJE2R9CNJfyVpuaS/k/QrkmZIuqO8ftdK2nesa61Beb5/2Nj/pKQvSLpZ0lck3SXpHyQdPcjckyR9X9L+ki6T9F8l3S5ppaRTyxhJuqj8/Xpg498xSQslnVy2r5W0qGx/uPydHfTnYPs8KyMjQT+6FgF/CCBpNzofO32Czu8TzARmAP9S0jF0vvTtcdu/bfu3gP87NiVHl2nAQtuHAc8C/xr4G+DTtt8GPAB8fgzr21Xsbnsm8HG6nm9Jvw/MB95re+NXHfwmcBTwe8AFpe39dP7O/TZwHHCRpN8EbgU2vnlMoPPljZS2W8r2YD8HO40E/Siy/QjwtKTDgROAe4EjGtv3AP+Czg/RA8Dx5czlaNvPjU3V0eUntu8r23cDBwP72F5a2i4HjhmTynYt3y5/3g1MabS/C/g0cJLtZxrtf2v7VdsPAm8qbUcBV9reYPtJYCmdv4+3AkeXb9t9EHiyvAH8LnB7mdv9c9CsYYe3Q3zXTeX+B3AW8Bt0zvDfDfy57Uu6B0p6O/Be4EuSvmd7wfYsNAb1UmN7A7DPWBWyC1jPpiefeza2N74OG9g0tx4GDgLeDPQPMh5AW7pT22sk7UPnX9W3AG8E/g3wvO2fS9qPf/5zkKWb2MS1dH6AjqDz28XXAx+WtDeApAmSfl3SAcALtv8ncBHw9rEqOLboOeCZxjrxv6VzZhjD9yTw65L2k7QHnWWXoTxKWU6TdNgQY28FTpc0TlIPnX+JbfxKljvoLAvdUsZ9svxZhZzRjzLbL0u6CXi2fP3D30k6FPh+5yv7eR74IHAInTXDV4FXgI+NVc0xpDOBiyW9HlgJfGiM66mC7VfKV6vcRefLD3/cct6PJX0A+Kak921h6LV0lmPuBwx8yvYTpe9W4ATbA5IepXNWX03Q5ysQRlm5CHsPcJrth8a6nojY9WTpZhSVizsDwPcS8hExVnJGHxFRuZzRR0RULkEfEVG5BH1EROUS9BERlUvQR0RU7v8DtUouV5vOw9wAAAAASUVORK5CYII=", + "text/plain": [ + "
" + ] + }, + "metadata": { + "needs_background": "light", + "tags": [] + }, + "output_type": "display_data" + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "/content/data/mini_speech_commands/unknown/f4504600_nohash_1.wav\n" + ] + }, + { + "data": { + "image/png": "iVBORw0KGgoAAAANSUhEUgAAAXQAAAEICAYAAABPgw/pAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAALEgAACxIB0t1+/AAAADh0RVh0U29mdHdhcmUAbWF0cGxvdGxpYiB2ZXJzaW9uMy4yLjIsIGh0dHA6Ly9tYXRwbG90bGliLm9yZy+WH4yJAAAUd0lEQVR4nO3df7RdZX3n8feHxGAFV7FytZIEQyE6RseCvUa7BixF7ApSwapUGDsVrWbsNFNsrRqnlnFSO0WZZWdNTSvYYUGdkUAd0XRIh7Yqv6pALhTUgMg1gkkoGH6O6AgEvvPH2ZeeXO7NPUlOuOTJ+7XWWdk/nr339+xz7ufs8+yzd1JVSJL2fvvNdgGSpOEw0CWpEQa6JDXCQJekRhjoktQIA12SGmGga7ckOT/JR7vhY5Lcuovr+VSSPxhudTNu81eSbEryUJKjnsptDypJJTlituvQ3mHubBegPS/J7cDzgceAHwJ/A6yoqoeGuZ2qugp48QD1nA68q6qO7lv2PcOsZUD/hd5++OIwVpbkcuAjwLEAVfWRYaz36SrJ+cDl3eixVXX6rBUjwCP0fckbqupA4BXAKPDhyQ2S7Gsf8C8ENuzKgknmDLkWabcZ6PuYqtpC7wj9ZfDEV/rfSnIbcFs37ZeT3JjkgSRfTfLyieWTHJXkhiQ/SHIR8My+eccm2dw3vjDJ55NsTXJvkk8meQnwKeDnu66OB7q2T3TddOPvTjKe5L4ka5Mc0jevkrwnyW1djauTpJt3RJIrkjyY5J6uxu0k2T/JQ8Ac4KYk3+mmvyTJ5d06NyQ5qW+Z85P8eZJ1SX4I/OIg+zvJ6UmunjTtiW6Ubr2rk1za7dNrkxw+zbqO7rqIjh1gP+yX5MNJ7kjy/SR/meQnu3kXJHlfNzx/4j3QjR/e7fP9Jl7PJO/r1vFPSd4xyPPWLKkqH40/gNuB47vhhfSOSv+wGy/g74CfAn4COAr4PvAqeoH39m75/YF5wB3A7wDPAN4CPAp8tFvXscDmbngOcBPwJ8AB9IL/6G7e6cDVk2o8v289xwH30Ps2sT/wp8CVfW0L+N/AQcChwFZgWTfvQuD36R2sPLHNafZLAUd0w88AxoH/0D3P44AfAC/uq+9B4F9NrHvAfT/Vc+3f7vnAvcBSel2g/xNYM7ktsAzYBCwdcD+8s3s+PwMcCHwe+EzfvL/uhv818B3gor55X+x7PbcBq7r983rgR8BzZvs97WPqh0fo+44vdEfDVwNXAP+5b94fV9V9VfX/gOXAOVV1bVU9VlUXAA8Dr+4ezwD+a1U9WlWfA9ZPs72lwCHA+6vqh1X146q6epq2k70NOK+qbqiqh4EP0TuiX9TX5qyqeqCqvgd8BTiym/4ova6UQ3Zym6+mF3xnVdUjVfVlemF5Wl+bL1bVP1TV41X14wHXO4hLquq6qtpGL9CPnDT/FOAc4ISqum7SvOn2w9uAT1TVxuqdK/kQcGrXrXYFcHSS/YDXAB+n90EF8Avd/AmPAqu613sd8BADnCfR7DDQ9x1vrKqDquqFVfXvuvCesKlv+IXA+7qv8A90HwIL6YXzIcCWquq/o9sd02xvIXBHF1I765D+9XaBdC8wv6/NXX3DP6IXxgAfAAJc13WbvHMntrmpqh7vm3bHpG1uYs+Y7rlMeC9wcVV9cyeW3W4fdsNzgedX1XfonRw/EjiG3gfXnUlezJMD/d5Jr+FU9elpwkAX9L66T9gE/FEX/hOPZ1XVhcA/AfMn+mk7h06zzk3AodOcaJ3pFp930vtgASDJAcBzgS0zPpGqu6rq3VV1CPBvgT8b8Gd/dwILu6PWCYdO2uau3Jr0h8CzJkaS/PQurOMU4I1JztiJZbbbh/Seyzbg7m78CnpdZvOqd17lCnrda88BbtyFGvU0YKBrsk8D70nyqvQckOTEJM8GvkYvFH47yTOSvIle18pUrqP3AXBWt45nJpn4Wn83sCDJvGmWvRB4R5Ijk+xPr3vo2qq6fabik5ySZEE3ej+9EH58B4tMuJbe0ecHuud2LPAGYM0Ay+7ITcBLu+fyTHo/a9xZdwKvBc5I8psDLnMh8DtJDktyIL19eFHf0fYVwArgym788m786qp6bBdq1NOAga7tVNUY8G7gk/QCcZzeiT2q6hHgTd34fcBb6Z1sm2o9j9ELxCOA7wGbu/YAX6Z3YvauJPdMsezfA38A/C96HwqHA6cO+BReCVzb/YplLXBGVW2caaHuub0BOIHeCdk/A369qr414HanW++36Z1U/Ht6vyIatE9/8nq+Ry/UVyZ51wCLnAd8hl5gfxf4MfDv++ZfATybfw70q+l9k7gS7bWyfXeoJGlv5RG6JDXCQJekRhjoktSIgQI9ybIkt3aXYq+cps2vJrm5++3vZ4dbpiRpJjOeFE3vJkTfBl5H75cK64HTqurmvjaLgYuB46rq/iTPq6rv72i9Bx98cC1atGg3y5ekfcv1119/T1WNTDVvkLvrLQXGJ376lWQNcDJwc1+bdwOrq+p+gJnCHGDRokWMjY0NsHlJ0oQk012dPVCXy3y2v+R5M9tfDg3wIuBFSf4hyTVJlk1TyPIkY0nGtm7dOsCmJUmDGtZJ0bnAYnp3ZzsN+HSSgyY3qqpzq2q0qkZHRqb8xiBJ2kWDBPoWejdamrCAJ99TYzOwtrsj23fp9bkvHk6JkqRBDBLo64HF3T0h5tG7BHvtpDZfoPtvt5IcTK8LZsbLrSVJwzNjoHc381kBXAbcQu82nhuSrOr7H10uA+5NcjO9ezK/v6ru3VNFS5KebNbu5TI6Olr+ykWSdk6S66tqdKp5XikqSY0w0CWpEQa6JDVikCtFJe2DFq28dLZLaNbtZ524R9brEbokNcJAl6RGGOiS1AgDXZIaYaBLUiMMdElqhIEuSY0w0CWpEQa6JDXCQJekRhjoktQIA12SGmGgS1IjDHRJaoSBLkmNMNAlqREGuiQ1wkCXpEYY6JLUCANdkhphoEtSIwx0SWqEgS5JjRgo0JMsS3JrkvEkK6eYf3qSrUlu7B7vGn6pkqQdmTtTgyRzgNXA64DNwPoka6vq5klNL6qqFXugRknSAAY5Ql8KjFfVxqp6BFgDnLxny5Ik7axBAn0+sKlvfHM3bbI3J/l6ks8lWTjVipIsTzKWZGzr1q27UK4kaTrDOin618Ciqno58HfABVM1qqpzq2q0qkZHRkaGtGlJEgwW6FuA/iPuBd20J1TVvVX1cDf6F8DPDac8SdKgBgn09cDiJIclmQecCqztb5DkBX2jJwG3DK9ESdIgZvyVS1VtS7ICuAyYA5xXVRuSrALGqmot8NtJTgK2AfcBp+/BmiVJU5gx0AGqah2wbtK0M/uGPwR8aLilSZJ2hleKSlIjDHRJaoSBLkmNMNAlqREGuiQ1wkCXpEYY6JLUCANdkhphoEtSIwx0SWqEgS5JjTDQJakRBrokNcJAl6RGGOiS1AgDXZIaYaBLUiMMdElqhIEuSY0w0CWpEQa6JDXCQJekRhjoktQIA12SGmGgS1IjDHRJaoSBLkmNGCjQkyxLcmuS8SQrd9DuzUkqyejwSpQkDWLGQE8yB1gNnAAsAU5LsmSKds8GzgCuHXaRkqSZDXKEvhQYr6qNVfUIsAY4eYp2fwh8DPjxEOuTJA1okECfD2zqG9/cTXtCklcAC6vq0h2tKMnyJGNJxrZu3brTxUqSprfbJ0WT7Ad8AnjfTG2r6tyqGq2q0ZGRkd3dtCSpzyCBvgVY2De+oJs24dnAy4DLk9wOvBpY64lRSXpqDRLo64HFSQ5LMg84FVg7MbOqHqyqg6tqUVUtAq4BTqqqsT1SsSRpSjMGelVtA1YAlwG3ABdX1YYkq5KctKcLlCQNZu4gjapqHbBu0rQzp2l77O6XJUnaWV4pKkmNMNAlqREGuiQ1wkCXpEYY6JLUCANdkhphoEtSIwx0SWqEgS5JjTDQJakRBrokNcJAl6RGGOiS1AgDXZIaYaBLUiMMdElqhIEuSY0w0CWpEQa6JDXCQJekRhjoktQIA12SGmGgS1IjDHRJaoSBLkmNMNAlqREGuiQ1YqBAT7Isya1JxpOsnGL+e5J8I8mNSa5OsmT4pUqSdmTGQE8yB1gNnAAsAU6bIrA/W1X/sqqOBD4OfGLolUqSdmiQI/SlwHhVbayqR4A1wMn9Darq//aNHgDU8EqUJA1i7gBt5gOb+sY3A6+a3CjJbwG/C8wDjptqRUmWA8sBDj300J2tVZK0A0M7KVpVq6vqcOCDwIenaXNuVY1W1ejIyMiwNi1JYrBA3wIs7Btf0E2bzhrgjbtTlCRp5w0S6OuBxUkOSzIPOBVY298gyeK+0ROB24ZXoiRpEDP2oVfVtiQrgMuAOcB5VbUhySpgrKrWAiuSHA88CtwPvH1PFi1JerJBTopSVeuAdZOmndk3fMaQ65Ik7SSvFJWkRhjoktQIA12SGmGgS1IjDHRJaoSBLkmNMNAlqREGuiQ1wkCXpEYY6JLUCANdkhphoEtSIwx0SWqEgS5JjTDQJakRBrokNcJAl6RGGOiS1AgDXZIaYaBLUiMMdElqhIEuSY0w0CWpEQa6JDXCQJekRhjoktSIubNdwK5YtPLS2S6hWbefdeIeWa+v2Z6zp14z7X0GOkJPsizJrUnGk6ycYv7vJrk5ydeTfCnJC4dfqiRpR2YM9CRzgNXACcAS4LQkSyY1+0dgtKpeDnwO+PiwC5Uk7dggR+hLgfGq2lhVjwBrgJP7G1TVV6rqR93oNcCC4ZYpSZrJIIE+H9jUN765mzad3wD+ZqoZSZYnGUsytnXr1sGrlCTNaKi/cknya8AocPZU86vq3KoararRkZGRYW5akvZ5g/zKZQuwsG98QTdtO0mOB34f+IWqeng45UmSBjXIEfp6YHGSw5LMA04F1vY3SHIUcA5wUlV9f/hlSpJmMmOgV9U2YAVwGXALcHFVbUiyKslJXbOzgQOBv0pyY5K106xOkrSHDHRhUVWtA9ZNmnZm3/DxQ65LkrSTvPRfkhphoEtSIwx0SWqEgS5JjTDQJakRBrokNcJAl6RGGOiS1AgDXZIaYaBLUiMMdElqhIEuSY0w0CWpEQa6JDXCQJekRhjoktQIA12SGmGgS1IjDHRJaoSBLkmNMNAlqREGuiQ1wkCXpEYY6JLUCANdkhphoEtSIwx0SWrEQIGeZFmSW5OMJ1k5xfzXJLkhybYkbxl+mZKkmcwY6EnmAKuBE4AlwGlJlkxq9j3gdOCzwy5QkjSYuQO0WQqMV9VGgCRrgJOBmycaVNXt3bzH90CNkqQBDNLlMh/Y1De+uZu205IsTzKWZGzr1q27sgpJ0jSe0pOiVXVuVY1W1ejIyMhTuWlJat4ggb4FWNg3vqCbJkl6Ghkk0NcDi5MclmQecCqwds+WJUnaWTMGelVtA1YAlwG3ABdX1YYkq5KcBJDklUk2A6cA5yTZsCeLliQ92SC/cqGq1gHrJk07s294Pb2uGEnSLPFKUUlqhIEuSY0w0CWpEQa6JDXCQJekRhjoktQIA12SGmGgS1IjDHRJaoSBLkmNMNAlqREGuiQ1wkCXpEYY6JLUCANdkhphoEtSIwx0SWqEgS5JjTDQJakRBrokNcJAl6RGGOiS1AgDXZIaYaBLUiMMdElqhIEuSY0w0CWpEQMFepJlSW5NMp5k5RTz909yUTf/2iSLhl2oJGnHZgz0JHOA1cAJwBLgtCRLJjX7DeD+qjoC+BPgY8MuVJK0Y4McoS8FxqtqY1U9AqwBTp7U5mTggm74c8Brk2R4ZUqSZjJ3gDbzgU1945uBV03Xpqq2JXkQeC5wT3+jJMuB5d3oQ0lu3ZWi90IHM2lfPF3F71awF71e4GvW2ZdesxdON2OQQB+aqjoXOPep3ObTQZKxqhqd7To0GF+vvY+vWc8gXS5bgIV94wu6aVO2STIX+Eng3mEUKEkazCCBvh5YnOSwJPOAU4G1k9qsBd7eDb8F+HJV1fDKlCTNZMYul65PfAVwGTAHOK+qNiRZBYxV1VrgvwOfSTIO3Ecv9PXP9rlupr2cr9fex9cMiAfSktQGrxSVpEYY6JLUCANd0tNWktOTfHK269hbGOiS1AgDfTclWZXkvX3jf5TkjCTvT7I+ydeT/Kdu3gFJLk1yU5JvJnnr7FUugCSLktyS5NNJNiT52yQ/keTIJNd0r98lSZ4z27W2oNvf3+wb/70kH0lyeZKPJbkuybeTHDPFsicm+VqSg5Ocn+S/Jflqko1J3tK1SZKzu7+vb0z8jSVZneSkbviSJOd1w+/s/manfB88NXtleAz03Xce8OsASfaj95PNu4DF9O6DcyTwc0leAywD7qyqn62qlwH/Z3ZK1iSLgdVV9VLgAeDNwF8CH6yqlwPfAP7jLNa3r5hbVUuB9zJpfyf5FWAl8PqqmrjE/wXA0cAvA2d1095E72/uZ4HjgbOTvAC4Cpj4kJhP70aDdNOu7Ianeh/sVQz03VRVtwP3JjkK+CXgH4FX9g3fAPwLem+WbwCv645EjqmqB2enak3y3aq6sRu+HjgcOKiqruimXQC8ZlYq27d8vvv3emBR3/TjgA8CJ1bV/X3Tv1BVj1fVzcDzu2lHAxdW1WNVdTdwBb2/x6uAY7o7xd4M3N0F/c8DX+2Wnfw+6K9hr/CU3sulYX8BnA78NL0j9tcCf1xV50xumOQVwOuBjyb5UlWteioL1ZQe7ht+DDhotgrZB2xj+wPJZ/YNT7wOj7F9Nn0H+BngRcDYFO0Bdnh316rakuQget+SrwR+CvhV4KGq+kGS5/Lk94FdLvuoS+i9UV5J74ray4B3JjkQIMn8JM9Lcgjwo6r6H8DZwCtmq2Dt0IPA/X39uP+G3pGedt/dwPOSPDfJ/vS6S2ZyB103WJKXztD2KuCtSeYkGaH3zeq6bt419Lpzruza/V73bzM8Qh+CqnokyVeAB6rqMeBvk7wE+Fp3W/iHgF8DjqDXp/c48Cjwm7NVs2b0duBTSZ4FbATeMcv1NKGqHu1uG3IdvZv6fWvA5b6V5G3AXyV5ww6aXkKvG+UmoIAPVNVd3byrgF+qqvEkd9A7Sm8q0L30fwi6k6E3AKdU1W2zXY+kfZNdLrupO8kyDnzJMJc0mzxCl6RGeIQuSY0w0CWpEQa6JDXCQJekRhjoktSI/w/TIK6YopbnBgAAAABJRU5ErkJggg==", + "text/plain": [ + "
" + ] + }, + "metadata": { + "needs_background": "light", + "tags": [] + }, + "output_type": "display_data" + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "/content/data/mini_speech_commands/unknown/da76aa58_nohash_1.wav\n" + ] + }, + { + "data": { + "image/png": "iVBORw0KGgoAAAANSUhEUgAAAXQAAAEICAYAAABPgw/pAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAALEgAACxIB0t1+/AAAADh0RVh0U29mdHdhcmUAbWF0cGxvdGxpYiB2ZXJzaW9uMy4yLjIsIGh0dHA6Ly9tYXRwbG90bGliLm9yZy+WH4yJAAAUd0lEQVR4nO3df7RdZX3n8feHxGAFV7FytZIEQyE6RseCvUa7BixF7ApSwapUGDsVrWbsNFNsrRqnlnFSO0WZZWdNTSvYYUGdkUAd0XRIh7Yqv6pALhTUgMg1gkkoGH6O6AgEvvPH2ZeeXO7NPUlOuOTJ+7XWWdk/nr339+xz7ufs8+yzd1JVSJL2fvvNdgGSpOEw0CWpEQa6JDXCQJekRhjoktQIA12SGmGga7ckOT/JR7vhY5Lcuovr+VSSPxhudTNu81eSbEryUJKjnsptDypJJTlituvQ3mHubBegPS/J7cDzgceAHwJ/A6yoqoeGuZ2qugp48QD1nA68q6qO7lv2PcOsZUD/hd5++OIwVpbkcuAjwLEAVfWRYaz36SrJ+cDl3eixVXX6rBUjwCP0fckbqupA4BXAKPDhyQ2S7Gsf8C8ENuzKgknmDLkWabcZ6PuYqtpC7wj9ZfDEV/rfSnIbcFs37ZeT3JjkgSRfTfLyieWTHJXkhiQ/SHIR8My+eccm2dw3vjDJ55NsTXJvkk8meQnwKeDnu66OB7q2T3TddOPvTjKe5L4ka5Mc0jevkrwnyW1djauTpJt3RJIrkjyY5J6uxu0k2T/JQ8Ac4KYk3+mmvyTJ5d06NyQ5qW+Z85P8eZJ1SX4I/OIg+zvJ6UmunjTtiW6Ubr2rk1za7dNrkxw+zbqO7rqIjh1gP+yX5MNJ7kjy/SR/meQnu3kXJHlfNzx/4j3QjR/e7fP9Jl7PJO/r1vFPSd4xyPPWLKkqH40/gNuB47vhhfSOSv+wGy/g74CfAn4COAr4PvAqeoH39m75/YF5wB3A7wDPAN4CPAp8tFvXscDmbngOcBPwJ8AB9IL/6G7e6cDVk2o8v289xwH30Ps2sT/wp8CVfW0L+N/AQcChwFZgWTfvQuD36R2sPLHNafZLAUd0w88AxoH/0D3P44AfAC/uq+9B4F9NrHvAfT/Vc+3f7vnAvcBSel2g/xNYM7ktsAzYBCwdcD+8s3s+PwMcCHwe+EzfvL/uhv818B3gor55X+x7PbcBq7r983rgR8BzZvs97WPqh0fo+44vdEfDVwNXAP+5b94fV9V9VfX/gOXAOVV1bVU9VlUXAA8Dr+4ezwD+a1U9WlWfA9ZPs72lwCHA+6vqh1X146q6epq2k70NOK+qbqiqh4EP0TuiX9TX5qyqeqCqvgd8BTiym/4ova6UQ3Zym6+mF3xnVdUjVfVlemF5Wl+bL1bVP1TV41X14wHXO4hLquq6qtpGL9CPnDT/FOAc4ISqum7SvOn2w9uAT1TVxuqdK/kQcGrXrXYFcHSS/YDXAB+n90EF8Avd/AmPAqu613sd8BADnCfR7DDQ9x1vrKqDquqFVfXvuvCesKlv+IXA+7qv8A90HwIL6YXzIcCWquq/o9sd02xvIXBHF1I765D+9XaBdC8wv6/NXX3DP6IXxgAfAAJc13WbvHMntrmpqh7vm3bHpG1uYs+Y7rlMeC9wcVV9cyeW3W4fdsNzgedX1XfonRw/EjiG3gfXnUlezJMD/d5Jr+FU9elpwkAX9L66T9gE/FEX/hOPZ1XVhcA/AfMn+mk7h06zzk3AodOcaJ3pFp930vtgASDJAcBzgS0zPpGqu6rq3VV1CPBvgT8b8Gd/dwILu6PWCYdO2uau3Jr0h8CzJkaS/PQurOMU4I1JztiJZbbbh/Seyzbg7m78CnpdZvOqd17lCnrda88BbtyFGvU0YKBrsk8D70nyqvQckOTEJM8GvkYvFH47yTOSvIle18pUrqP3AXBWt45nJpn4Wn83sCDJvGmWvRB4R5Ijk+xPr3vo2qq6fabik5ySZEE3ej+9EH58B4tMuJbe0ecHuud2LPAGYM0Ay+7ITcBLu+fyTHo/a9xZdwKvBc5I8psDLnMh8DtJDktyIL19eFHf0fYVwArgym788m786qp6bBdq1NOAga7tVNUY8G7gk/QCcZzeiT2q6hHgTd34fcBb6Z1sm2o9j9ELxCOA7wGbu/YAX6Z3YvauJPdMsezfA38A/C96HwqHA6cO+BReCVzb/YplLXBGVW2caaHuub0BOIHeCdk/A369qr414HanW++36Z1U/Ht6vyIatE9/8nq+Ry/UVyZ51wCLnAd8hl5gfxf4MfDv++ZfATybfw70q+l9k7gS7bWyfXeoJGlv5RG6JDXCQJekRhjoktSIgQI9ybIkt3aXYq+cps2vJrm5++3vZ4dbpiRpJjOeFE3vJkTfBl5H75cK64HTqurmvjaLgYuB46rq/iTPq6rv72i9Bx98cC1atGg3y5ekfcv1119/T1WNTDVvkLvrLQXGJ376lWQNcDJwc1+bdwOrq+p+gJnCHGDRokWMjY0NsHlJ0oQk012dPVCXy3y2v+R5M9tfDg3wIuBFSf4hyTVJlk1TyPIkY0nGtm7dOsCmJUmDGtZJ0bnAYnp3ZzsN+HSSgyY3qqpzq2q0qkZHRqb8xiBJ2kWDBPoWejdamrCAJ99TYzOwtrsj23fp9bkvHk6JkqRBDBLo64HF3T0h5tG7BHvtpDZfoPtvt5IcTK8LZsbLrSVJwzNjoHc381kBXAbcQu82nhuSrOr7H10uA+5NcjO9ezK/v6ru3VNFS5KebNbu5TI6Olr+ykWSdk6S66tqdKp5XikqSY0w0CWpEQa6JDVikCtFJe2DFq28dLZLaNbtZ524R9brEbokNcJAl6RGGOiS1AgDXZIaYaBLUiMMdElqhIEuSY0w0CWpEQa6JDXCQJekRhjoktQIA12SGmGgS1IjDHRJaoSBLkmNMNAlqREGuiQ1wkCXpEYY6JLUCANdkhphoEtSIwx0SWqEgS5JjRgo0JMsS3JrkvEkK6eYf3qSrUlu7B7vGn6pkqQdmTtTgyRzgNXA64DNwPoka6vq5klNL6qqFXugRknSAAY5Ql8KjFfVxqp6BFgDnLxny5Ik7axBAn0+sKlvfHM3bbI3J/l6ks8lWTjVipIsTzKWZGzr1q27UK4kaTrDOin618Ciqno58HfABVM1qqpzq2q0qkZHRkaGtGlJEgwW6FuA/iPuBd20J1TVvVX1cDf6F8DPDac8SdKgBgn09cDiJIclmQecCqztb5DkBX2jJwG3DK9ESdIgZvyVS1VtS7ICuAyYA5xXVRuSrALGqmot8NtJTgK2AfcBp+/BmiVJU5gx0AGqah2wbtK0M/uGPwR8aLilSZJ2hleKSlIjDHRJaoSBLkmNMNAlqREGuiQ1wkCXpEYY6JLUCANdkhphoEtSIwx0SWqEgS5JjTDQJakRBrokNcJAl6RGGOiS1AgDXZIaYaBLUiMMdElqhIEuSY0w0CWpEQa6JDXCQJekRhjoktQIA12SGmGgS1IjDHRJaoSBLkmNGCjQkyxLcmuS8SQrd9DuzUkqyejwSpQkDWLGQE8yB1gNnAAsAU5LsmSKds8GzgCuHXaRkqSZDXKEvhQYr6qNVfUIsAY4eYp2fwh8DPjxEOuTJA1okECfD2zqG9/cTXtCklcAC6vq0h2tKMnyJGNJxrZu3brTxUqSprfbJ0WT7Ad8AnjfTG2r6tyqGq2q0ZGRkd3dtCSpzyCBvgVY2De+oJs24dnAy4DLk9wOvBpY64lRSXpqDRLo64HFSQ5LMg84FVg7MbOqHqyqg6tqUVUtAq4BTqqqsT1SsSRpSjMGelVtA1YAlwG3ABdX1YYkq5KctKcLlCQNZu4gjapqHbBu0rQzp2l77O6XJUnaWV4pKkmNMNAlqREGuiQ1wkCXpEYY6JLUCANdkhphoEtSIwx0SWqEgS5JjTDQJakRBrokNcJAl6RGGOiS1AgDXZIaYaBLUiMMdElqhIEuSY0w0CWpEQa6JDXCQJekRhjoktQIA12SGmGgS1IjDHRJaoSBLkmNMNAlqREGuiQ1YqBAT7Isya1JxpOsnGL+e5J8I8mNSa5OsmT4pUqSdmTGQE8yB1gNnAAsAU6bIrA/W1X/sqqOBD4OfGLolUqSdmiQI/SlwHhVbayqR4A1wMn9Darq//aNHgDU8EqUJA1i7gBt5gOb+sY3A6+a3CjJbwG/C8wDjptqRUmWA8sBDj300J2tVZK0A0M7KVpVq6vqcOCDwIenaXNuVY1W1ejIyMiwNi1JYrBA3wIs7Btf0E2bzhrgjbtTlCRp5w0S6OuBxUkOSzIPOBVY298gyeK+0ROB24ZXoiRpEDP2oVfVtiQrgMuAOcB5VbUhySpgrKrWAiuSHA88CtwPvH1PFi1JerJBTopSVeuAdZOmndk3fMaQ65Ik7SSvFJWkRhjoktQIA12SGmGgS1IjDHRJaoSBLkmNMNAlqREGuiQ1wkCXpEYY6JLUCANdkhphoEtSIwx0SWqEgS5JjTDQJakRBrokNcJAl6RGGOiS1AgDXZIaYaBLUiMMdElqhIEuSY0w0CWpEQa6JDXCQJekRhjoktSIubNdwK5YtPLS2S6hWbefdeIeWa+v2Z6zp14z7X0GOkJPsizJrUnGk6ycYv7vJrk5ydeTfCnJC4dfqiRpR2YM9CRzgNXACcAS4LQkSyY1+0dgtKpeDnwO+PiwC5Uk7dggR+hLgfGq2lhVjwBrgJP7G1TVV6rqR93oNcCC4ZYpSZrJIIE+H9jUN765mzad3wD+ZqoZSZYnGUsytnXr1sGrlCTNaKi/cknya8AocPZU86vq3KoararRkZGRYW5akvZ5g/zKZQuwsG98QTdtO0mOB34f+IWqeng45UmSBjXIEfp6YHGSw5LMA04F1vY3SHIUcA5wUlV9f/hlSpJmMmOgV9U2YAVwGXALcHFVbUiyKslJXbOzgQOBv0pyY5K106xOkrSHDHRhUVWtA9ZNmnZm3/DxQ65LkrSTvPRfkhphoEtSIwx0SWqEgS5JjTDQJakRBrokNcJAl6RGGOiS1AgDXZIaYaBLUiMMdElqhIEuSY0w0CWpEQa6JDXCQJekRhjoktQIA12SGmGgS1IjDHRJaoSBLkmNMNAlqREGuiQ1wkCXpEYY6JLUCANdkhphoEtSIwx0SWrEQIGeZFmSW5OMJ1k5xfzXJLkhybYkbxl+mZKkmcwY6EnmAKuBE4AlwGlJlkxq9j3gdOCzwy5QkjSYuQO0WQqMV9VGgCRrgJOBmycaVNXt3bzH90CNkqQBDNLlMh/Y1De+uZu205IsTzKWZGzr1q27sgpJ0jSe0pOiVXVuVY1W1ejIyMhTuWlJat4ggb4FWNg3vqCbJkl6Ghkk0NcDi5MclmQecCqwds+WJUnaWTMGelVtA1YAlwG3ABdX1YYkq5KcBJDklUk2A6cA5yTZsCeLliQ92SC/cqGq1gHrJk07s294Pb2uGEnSLPFKUUlqhIEuSY0w0CWpEQa6JDXCQJekRhjoktQIA12SGmGgS1IjDHRJaoSBLkmNMNAlqREGuiQ1wkCXpEYY6JLUCANdkhphoEtSIwx0SWqEgS5JjTDQJakRBrokNcJAl6RGGOiS1AgDXZIaYaBLUiMMdElqhIEuSY0w0CWpEQMFepJlSW5NMp5k5RTz909yUTf/2iSLhl2oJGnHZgz0JHOA1cAJwBLgtCRLJjX7DeD+qjoC+BPgY8MuVJK0Y4McoS8FxqtqY1U9AqwBTp7U5mTggm74c8Brk2R4ZUqSZjJ3gDbzgU1945uBV03Xpqq2JXkQeC5wT3+jJMuB5d3oQ0lu3ZWi90IHM2lfPF3F71awF71e4GvW2ZdesxdON2OQQB+aqjoXOPep3ObTQZKxqhqd7To0GF+vvY+vWc8gXS5bgIV94wu6aVO2STIX+Eng3mEUKEkazCCBvh5YnOSwJPOAU4G1k9qsBd7eDb8F+HJV1fDKlCTNZMYul65PfAVwGTAHOK+qNiRZBYxV1VrgvwOfSTIO3Ecv9PXP9rlupr2cr9fex9cMiAfSktQGrxSVpEYY6JLUCANd0tNWktOTfHK269hbGOiS1AgDfTclWZXkvX3jf5TkjCTvT7I+ydeT/Kdu3gFJLk1yU5JvJnnr7FUugCSLktyS5NNJNiT52yQ/keTIJNd0r98lSZ4z27W2oNvf3+wb/70kH0lyeZKPJbkuybeTHDPFsicm+VqSg5Ocn+S/Jflqko1J3tK1SZKzu7+vb0z8jSVZneSkbviSJOd1w+/s/manfB88NXtleAz03Xce8OsASfaj95PNu4DF9O6DcyTwc0leAywD7qyqn62qlwH/Z3ZK1iSLgdVV9VLgAeDNwF8CH6yqlwPfAP7jLNa3r5hbVUuB9zJpfyf5FWAl8PqqmrjE/wXA0cAvA2d1095E72/uZ4HjgbOTvAC4Cpj4kJhP70aDdNOu7Ianeh/sVQz03VRVtwP3JjkK+CXgH4FX9g3fAPwLem+WbwCv645EjqmqB2enak3y3aq6sRu+HjgcOKiqruimXQC8ZlYq27d8vvv3emBR3/TjgA8CJ1bV/X3Tv1BVj1fVzcDzu2lHAxdW1WNVdTdwBb2/x6uAY7o7xd4M3N0F/c8DX+2Wnfw+6K9hr/CU3sulYX8BnA78NL0j9tcCf1xV50xumOQVwOuBjyb5UlWteioL1ZQe7ht+DDhotgrZB2xj+wPJZ/YNT7wOj7F9Nn0H+BngRcDYFO0Bdnh316rakuQget+SrwR+CvhV4KGq+kGS5/Lk94FdLvuoS+i9UV5J74ray4B3JjkQIMn8JM9Lcgjwo6r6H8DZwCtmq2Dt0IPA/X39uP+G3pGedt/dwPOSPDfJ/vS6S2ZyB103WJKXztD2KuCtSeYkGaH3zeq6bt419Lpzruza/V73bzM8Qh+CqnokyVeAB6rqMeBvk7wE+Fp3W/iHgF8DjqDXp/c48Cjwm7NVs2b0duBTSZ4FbATeMcv1NKGqHu1uG3IdvZv6fWvA5b6V5G3AXyV5ww6aXkKvG+UmoIAPVNVd3byrgF+qqvEkd9A7Sm8q0L30fwi6k6E3AKdU1W2zXY+kfZNdLrupO8kyDnzJMJc0mzxCl6RGeIQuSY0w0CWpEQa6JDXCQJekRhjoktSI/w/TIK6YopbnBgAAAABJRU5ErkJggg==", + "text/plain": [ + "
" + ] + }, + "metadata": { + "needs_background": "light", + "tags": [] + }, + "output_type": "display_data" + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "/content/data/mini_speech_commands/unknown/dedc7fab_nohash_1.wav\n" + ] + }, + { + "data": { + "image/png": "iVBORw0KGgoAAAANSUhEUgAAAXQAAAEICAYAAABPgw/pAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAALEgAACxIB0t1+/AAAADh0RVh0U29mdHdhcmUAbWF0cGxvdGxpYiB2ZXJzaW9uMy4yLjIsIGh0dHA6Ly9tYXRwbG90bGliLm9yZy+WH4yJAAAUd0lEQVR4nO3df7RdZX3n8feHxGAFV7FytZIEQyE6RseCvUa7BixF7ApSwapUGDsVrWbsNFNsrRqnlnFSO0WZZWdNTSvYYUGdkUAd0XRIh7Yqv6pALhTUgMg1gkkoGH6O6AgEvvPH2ZeeXO7NPUlOuOTJ+7XWWdk/nr339+xz7ufs8+yzd1JVSJL2fvvNdgGSpOEw0CWpEQa6JDXCQJekRhjoktQIA12SGmGga7ckOT/JR7vhY5Lcuovr+VSSPxhudTNu81eSbEryUJKjnsptDypJJTlituvQ3mHubBegPS/J7cDzgceAHwJ/A6yoqoeGuZ2qugp48QD1nA68q6qO7lv2PcOsZUD/hd5++OIwVpbkcuAjwLEAVfWRYaz36SrJ+cDl3eixVXX6rBUjwCP0fckbqupA4BXAKPDhyQ2S7Gsf8C8ENuzKgknmDLkWabcZ6PuYqtpC7wj9ZfDEV/rfSnIbcFs37ZeT3JjkgSRfTfLyieWTHJXkhiQ/SHIR8My+eccm2dw3vjDJ55NsTXJvkk8meQnwKeDnu66OB7q2T3TddOPvTjKe5L4ka5Mc0jevkrwnyW1djauTpJt3RJIrkjyY5J6uxu0k2T/JQ8Ac4KYk3+mmvyTJ5d06NyQ5qW+Z85P8eZJ1SX4I/OIg+zvJ6UmunjTtiW6Ubr2rk1za7dNrkxw+zbqO7rqIjh1gP+yX5MNJ7kjy/SR/meQnu3kXJHlfNzx/4j3QjR/e7fP9Jl7PJO/r1vFPSd4xyPPWLKkqH40/gNuB47vhhfSOSv+wGy/g74CfAn4COAr4PvAqeoH39m75/YF5wB3A7wDPAN4CPAp8tFvXscDmbngOcBPwJ8AB9IL/6G7e6cDVk2o8v289xwH30Ps2sT/wp8CVfW0L+N/AQcChwFZgWTfvQuD36R2sPLHNafZLAUd0w88AxoH/0D3P44AfAC/uq+9B4F9NrHvAfT/Vc+3f7vnAvcBSel2g/xNYM7ktsAzYBCwdcD+8s3s+PwMcCHwe+EzfvL/uhv818B3gor55X+x7PbcBq7r983rgR8BzZvs97WPqh0fo+44vdEfDVwNXAP+5b94fV9V9VfX/gOXAOVV1bVU9VlUXAA8Dr+4ezwD+a1U9WlWfA9ZPs72lwCHA+6vqh1X146q6epq2k70NOK+qbqiqh4EP0TuiX9TX5qyqeqCqvgd8BTiym/4ova6UQ3Zym6+mF3xnVdUjVfVlemF5Wl+bL1bVP1TV41X14wHXO4hLquq6qtpGL9CPnDT/FOAc4ISqum7SvOn2w9uAT1TVxuqdK/kQcGrXrXYFcHSS/YDXAB+n90EF8Avd/AmPAqu613sd8BADnCfR7DDQ9x1vrKqDquqFVfXvuvCesKlv+IXA+7qv8A90HwIL6YXzIcCWquq/o9sd02xvIXBHF1I765D+9XaBdC8wv6/NXX3DP6IXxgAfAAJc13WbvHMntrmpqh7vm3bHpG1uYs+Y7rlMeC9wcVV9cyeW3W4fdsNzgedX1XfonRw/EjiG3gfXnUlezJMD/d5Jr+FU9elpwkAX9L66T9gE/FEX/hOPZ1XVhcA/AfMn+mk7h06zzk3AodOcaJ3pFp930vtgASDJAcBzgS0zPpGqu6rq3VV1CPBvgT8b8Gd/dwILu6PWCYdO2uau3Jr0h8CzJkaS/PQurOMU4I1JztiJZbbbh/Seyzbg7m78CnpdZvOqd17lCnrda88BbtyFGvU0YKBrsk8D70nyqvQckOTEJM8GvkYvFH47yTOSvIle18pUrqP3AXBWt45nJpn4Wn83sCDJvGmWvRB4R5Ijk+xPr3vo2qq6fabik5ySZEE3ej+9EH58B4tMuJbe0ecHuud2LPAGYM0Ay+7ITcBLu+fyTHo/a9xZdwKvBc5I8psDLnMh8DtJDktyIL19eFHf0fYVwArgym788m786qp6bBdq1NOAga7tVNUY8G7gk/QCcZzeiT2q6hHgTd34fcBb6Z1sm2o9j9ELxCOA7wGbu/YAX6Z3YvauJPdMsezfA38A/C96HwqHA6cO+BReCVzb/YplLXBGVW2caaHuub0BOIHeCdk/A369qr414HanW++36Z1U/Ht6vyIatE9/8nq+Ry/UVyZ51wCLnAd8hl5gfxf4MfDv++ZfATybfw70q+l9k7gS7bWyfXeoJGlv5RG6JDXCQJekRhjoktSIgQI9ybIkt3aXYq+cps2vJrm5++3vZ4dbpiRpJjOeFE3vJkTfBl5H75cK64HTqurmvjaLgYuB46rq/iTPq6rv72i9Bx98cC1atGg3y5ekfcv1119/T1WNTDVvkLvrLQXGJ376lWQNcDJwc1+bdwOrq+p+gJnCHGDRokWMjY0NsHlJ0oQk012dPVCXy3y2v+R5M9tfDg3wIuBFSf4hyTVJlk1TyPIkY0nGtm7dOsCmJUmDGtZJ0bnAYnp3ZzsN+HSSgyY3qqpzq2q0qkZHRqb8xiBJ2kWDBPoWejdamrCAJ99TYzOwtrsj23fp9bkvHk6JkqRBDBLo64HF3T0h5tG7BHvtpDZfoPtvt5IcTK8LZsbLrSVJwzNjoHc381kBXAbcQu82nhuSrOr7H10uA+5NcjO9ezK/v6ru3VNFS5KebNbu5TI6Olr+ykWSdk6S66tqdKp5XikqSY0w0CWpEQa6JDVikCtFJe2DFq28dLZLaNbtZ524R9brEbokNcJAl6RGGOiS1AgDXZIaYaBLUiMMdElqhIEuSY0w0CWpEQa6JDXCQJekRhjoktQIA12SGmGgS1IjDHRJaoSBLkmNMNAlqREGuiQ1wkCXpEYY6JLUCANdkhphoEtSIwx0SWqEgS5JjRgo0JMsS3JrkvEkK6eYf3qSrUlu7B7vGn6pkqQdmTtTgyRzgNXA64DNwPoka6vq5klNL6qqFXugRknSAAY5Ql8KjFfVxqp6BFgDnLxny5Ik7axBAn0+sKlvfHM3bbI3J/l6ks8lWTjVipIsTzKWZGzr1q27UK4kaTrDOin618Ciqno58HfABVM1qqpzq2q0qkZHRkaGtGlJEgwW6FuA/iPuBd20J1TVvVX1cDf6F8DPDac8SdKgBgn09cDiJIclmQecCqztb5DkBX2jJwG3DK9ESdIgZvyVS1VtS7ICuAyYA5xXVRuSrALGqmot8NtJTgK2AfcBp+/BmiVJU5gx0AGqah2wbtK0M/uGPwR8aLilSZJ2hleKSlIjDHRJaoSBLkmNMNAlqREGuiQ1wkCXpEYY6JLUCANdkhphoEtSIwx0SWqEgS5JjTDQJakRBrokNcJAl6RGGOiS1AgDXZIaYaBLUiMMdElqhIEuSY0w0CWpEQa6JDXCQJekRhjoktQIA12SGmGgS1IjDHRJaoSBLkmNGCjQkyxLcmuS8SQrd9DuzUkqyejwSpQkDWLGQE8yB1gNnAAsAU5LsmSKds8GzgCuHXaRkqSZDXKEvhQYr6qNVfUIsAY4eYp2fwh8DPjxEOuTJA1okECfD2zqG9/cTXtCklcAC6vq0h2tKMnyJGNJxrZu3brTxUqSprfbJ0WT7Ad8AnjfTG2r6tyqGq2q0ZGRkd3dtCSpzyCBvgVY2De+oJs24dnAy4DLk9wOvBpY64lRSXpqDRLo64HFSQ5LMg84FVg7MbOqHqyqg6tqUVUtAq4BTqqqsT1SsSRpSjMGelVtA1YAlwG3ABdX1YYkq5KctKcLlCQNZu4gjapqHbBu0rQzp2l77O6XJUnaWV4pKkmNMNAlqREGuiQ1wkCXpEYY6JLUCANdkhphoEtSIwx0SWqEgS5JjTDQJakRBrokNcJAl6RGGOiS1AgDXZIaYaBLUiMMdElqhIEuSY0w0CWpEQa6JDXCQJekRhjoktQIA12SGmGgS1IjDHRJaoSBLkmNMNAlqREGuiQ1YqBAT7Isya1JxpOsnGL+e5J8I8mNSa5OsmT4pUqSdmTGQE8yB1gNnAAsAU6bIrA/W1X/sqqOBD4OfGLolUqSdmiQI/SlwHhVbayqR4A1wMn9Darq//aNHgDU8EqUJA1i7gBt5gOb+sY3A6+a3CjJbwG/C8wDjptqRUmWA8sBDj300J2tVZK0A0M7KVpVq6vqcOCDwIenaXNuVY1W1ejIyMiwNi1JYrBA3wIs7Btf0E2bzhrgjbtTlCRp5w0S6OuBxUkOSzIPOBVY298gyeK+0ROB24ZXoiRpEDP2oVfVtiQrgMuAOcB5VbUhySpgrKrWAiuSHA88CtwPvH1PFi1JerJBTopSVeuAdZOmndk3fMaQ65Ik7SSvFJWkRhjoktQIA12SGmGgS1IjDHRJaoSBLkmNMNAlqREGuiQ1wkCXpEYY6JLUCANdkhphoEtSIwx0SWqEgS5JjTDQJakRBrokNcJAl6RGGOiS1AgDXZIaYaBLUiMMdElqhIEuSY0w0CWpEQa6JDXCQJekRhjoktSIubNdwK5YtPLS2S6hWbefdeIeWa+v2Z6zp14z7X0GOkJPsizJrUnGk6ycYv7vJrk5ydeTfCnJC4dfqiRpR2YM9CRzgNXACcAS4LQkSyY1+0dgtKpeDnwO+PiwC5Uk7dggR+hLgfGq2lhVjwBrgJP7G1TVV6rqR93oNcCC4ZYpSZrJIIE+H9jUN765mzad3wD+ZqoZSZYnGUsytnXr1sGrlCTNaKi/cknya8AocPZU86vq3KoararRkZGRYW5akvZ5g/zKZQuwsG98QTdtO0mOB34f+IWqeng45UmSBjXIEfp6YHGSw5LMA04F1vY3SHIUcA5wUlV9f/hlSpJmMmOgV9U2YAVwGXALcHFVbUiyKslJXbOzgQOBv0pyY5K106xOkrSHDHRhUVWtA9ZNmnZm3/DxQ65LkrSTvPRfkhphoEtSIwx0SWqEgS5JjTDQJakRBrokNcJAl6RGGOiS1AgDXZIaYaBLUiMMdElqhIEuSY0w0CWpEQa6JDXCQJekRhjoktQIA12SGmGgS1IjDHRJaoSBLkmNMNAlqREGuiQ1wkCXpEYY6JLUCANdkhphoEtSIwx0SWrEQIGeZFmSW5OMJ1k5xfzXJLkhybYkbxl+mZKkmcwY6EnmAKuBE4AlwGlJlkxq9j3gdOCzwy5QkjSYuQO0WQqMV9VGgCRrgJOBmycaVNXt3bzH90CNkqQBDNLlMh/Y1De+uZu205IsTzKWZGzr1q27sgpJ0jSe0pOiVXVuVY1W1ejIyMhTuWlJat4ggb4FWNg3vqCbJkl6Ghkk0NcDi5MclmQecCqwds+WJUnaWTMGelVtA1YAlwG3ABdX1YYkq5KcBJDklUk2A6cA5yTZsCeLliQ92SC/cqGq1gHrJk07s294Pb2uGEnSLPFKUUlqhIEuSY0w0CWpEQa6JDXCQJekRhjoktQIA12SGmGgS1IjDHRJaoSBLkmNMNAlqREGuiQ1wkCXpEYY6JLUCANdkhphoEtSIwx0SWqEgS5JjTDQJakRBrokNcJAl6RGGOiS1AgDXZIaYaBLUiMMdElqhIEuSY0w0CWpEQMFepJlSW5NMp5k5RTz909yUTf/2iSLhl2oJGnHZgz0JHOA1cAJwBLgtCRLJjX7DeD+qjoC+BPgY8MuVJK0Y4McoS8FxqtqY1U9AqwBTp7U5mTggm74c8Brk2R4ZUqSZjJ3gDbzgU1945uBV03Xpqq2JXkQeC5wT3+jJMuB5d3oQ0lu3ZWi90IHM2lfPF3F71awF71e4GvW2ZdesxdON2OQQB+aqjoXOPep3ObTQZKxqhqd7To0GF+vvY+vWc8gXS5bgIV94wu6aVO2STIX+Eng3mEUKEkazCCBvh5YnOSwJPOAU4G1k9qsBd7eDb8F+HJV1fDKlCTNZMYul65PfAVwGTAHOK+qNiRZBYxV1VrgvwOfSTIO3Ecv9PXP9rlupr2cr9fex9cMiAfSktQGrxSVpEYY6JLUCANd0tNWktOTfHK269hbGOiS1AgDfTclWZXkvX3jf5TkjCTvT7I+ydeT/Kdu3gFJLk1yU5JvJnnr7FUugCSLktyS5NNJNiT52yQ/keTIJNd0r98lSZ4z27W2oNvf3+wb/70kH0lyeZKPJbkuybeTHDPFsicm+VqSg5Ocn+S/Jflqko1J3tK1SZKzu7+vb0z8jSVZneSkbviSJOd1w+/s/manfB88NXtleAz03Xce8OsASfaj95PNu4DF9O6DcyTwc0leAywD7qyqn62qlwH/Z3ZK1iSLgdVV9VLgAeDNwF8CH6yqlwPfAP7jLNa3r5hbVUuB9zJpfyf5FWAl8PqqmrjE/wXA0cAvA2d1095E72/uZ4HjgbOTvAC4Cpj4kJhP70aDdNOu7Ianeh/sVQz03VRVtwP3JjkK+CXgH4FX9g3fAPwLem+WbwCv645EjqmqB2enak3y3aq6sRu+HjgcOKiqruimXQC8ZlYq27d8vvv3emBR3/TjgA8CJ1bV/X3Tv1BVj1fVzcDzu2lHAxdW1WNVdTdwBb2/x6uAY7o7xd4M3N0F/c8DX+2Wnfw+6K9hr/CU3sulYX8BnA78NL0j9tcCf1xV50xumOQVwOuBjyb5UlWteioL1ZQe7ht+DDhotgrZB2xj+wPJZ/YNT7wOj7F9Nn0H+BngRcDYFO0Bdnh316rakuQget+SrwR+CvhV4KGq+kGS5/Lk94FdLvuoS+i9UV5J74ray4B3JjkQIMn8JM9Lcgjwo6r6H8DZwCtmq2Dt0IPA/X39uP+G3pGedt/dwPOSPDfJ/vS6S2ZyB103WJKXztD2KuCtSeYkGaH3zeq6bt419Lpzruza/V73bzM8Qh+CqnokyVeAB6rqMeBvk7wE+Fp3W/iHgF8DjqDXp/c48Cjwm7NVs2b0duBTSZ4FbATeMcv1NKGqHu1uG3IdvZv6fWvA5b6V5G3AXyV5ww6aXkKvG+UmoIAPVNVd3byrgF+qqvEkd9A7Sm8q0L30fwi6k6E3AKdU1W2zXY+kfZNdLrupO8kyDnzJMJc0mzxCl6RGeIQuSY0w0CWpEQa6JDXCQJekRhjoktSI/w/TIK6YopbnBgAAAABJRU5ErkJggg==", + "text/plain": [ + "
" + ] + }, + "metadata": { + "needs_background": "light", + "tags": [] + }, + "output_type": "display_data" + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "/content/data/mini_speech_commands/unknown/a527cb3c_nohash_0.wav\n" + ] + }, + { + "data": { + "image/png": "iVBORw0KGgoAAAANSUhEUgAAAXQAAAEICAYAAABPgw/pAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAALEgAACxIB0t1+/AAAADh0RVh0U29mdHdhcmUAbWF0cGxvdGxpYiB2ZXJzaW9uMy4yLjIsIGh0dHA6Ly9tYXRwbG90bGliLm9yZy+WH4yJAAAUd0lEQVR4nO3df7RdZX3n8feHxGAFV7FytZIEQyE6RseCvUa7BixF7ApSwapUGDsVrWbsNFNsrRqnlnFSO0WZZWdNTSvYYUGdkUAd0XRIh7Yqv6pALhTUgMg1gkkoGH6O6AgEvvPH2ZeeXO7NPUlOuOTJ+7XWWdk/nr339+xz7ufs8+yzd1JVSJL2fvvNdgGSpOEw0CWpEQa6JDXCQJekRhjoktQIA12SGmGga7ckOT/JR7vhY5Lcuovr+VSSPxhudTNu81eSbEryUJKjnsptDypJJTlituvQ3mHubBegPS/J7cDzgceAHwJ/A6yoqoeGuZ2qugp48QD1nA68q6qO7lv2PcOsZUD/hd5++OIwVpbkcuAjwLEAVfWRYaz36SrJ+cDl3eixVXX6rBUjwCP0fckbqupA4BXAKPDhyQ2S7Gsf8C8ENuzKgknmDLkWabcZ6PuYqtpC7wj9ZfDEV/rfSnIbcFs37ZeT3JjkgSRfTfLyieWTHJXkhiQ/SHIR8My+eccm2dw3vjDJ55NsTXJvkk8meQnwKeDnu66OB7q2T3TddOPvTjKe5L4ka5Mc0jevkrwnyW1djauTpJt3RJIrkjyY5J6uxu0k2T/JQ8Ac4KYk3+mmvyTJ5d06NyQ5qW+Z85P8eZJ1SX4I/OIg+zvJ6UmunjTtiW6Ubr2rk1za7dNrkxw+zbqO7rqIjh1gP+yX5MNJ7kjy/SR/meQnu3kXJHlfNzx/4j3QjR/e7fP9Jl7PJO/r1vFPSd4xyPPWLKkqH40/gNuB47vhhfSOSv+wGy/g74CfAn4COAr4PvAqeoH39m75/YF5wB3A7wDPAN4CPAp8tFvXscDmbngOcBPwJ8AB9IL/6G7e6cDVk2o8v289xwH30Ps2sT/wp8CVfW0L+N/AQcChwFZgWTfvQuD36R2sPLHNafZLAUd0w88AxoH/0D3P44AfAC/uq+9B4F9NrHvAfT/Vc+3f7vnAvcBSel2g/xNYM7ktsAzYBCwdcD+8s3s+PwMcCHwe+EzfvL/uhv818B3gor55X+x7PbcBq7r983rgR8BzZvs97WPqh0fo+44vdEfDVwNXAP+5b94fV9V9VfX/gOXAOVV1bVU9VlUXAA8Dr+4ezwD+a1U9WlWfA9ZPs72lwCHA+6vqh1X146q6epq2k70NOK+qbqiqh4EP0TuiX9TX5qyqeqCqvgd8BTiym/4ova6UQ3Zym6+mF3xnVdUjVfVlemF5Wl+bL1bVP1TV41X14wHXO4hLquq6qtpGL9CPnDT/FOAc4ISqum7SvOn2w9uAT1TVxuqdK/kQcGrXrXYFcHSS/YDXAB+n90EF8Avd/AmPAqu613sd8BADnCfR7DDQ9x1vrKqDquqFVfXvuvCesKlv+IXA+7qv8A90HwIL6YXzIcCWquq/o9sd02xvIXBHF1I765D+9XaBdC8wv6/NXX3DP6IXxgAfAAJc13WbvHMntrmpqh7vm3bHpG1uYs+Y7rlMeC9wcVV9cyeW3W4fdsNzgedX1XfonRw/EjiG3gfXnUlezJMD/d5Jr+FU9elpwkAX9L66T9gE/FEX/hOPZ1XVhcA/AfMn+mk7h06zzk3AodOcaJ3pFp930vtgASDJAcBzgS0zPpGqu6rq3VV1CPBvgT8b8Gd/dwILu6PWCYdO2uau3Jr0h8CzJkaS/PQurOMU4I1JztiJZbbbh/Seyzbg7m78CnpdZvOqd17lCnrda88BbtyFGvU0YKBrsk8D70nyqvQckOTEJM8GvkYvFH47yTOSvIle18pUrqP3AXBWt45nJpn4Wn83sCDJvGmWvRB4R5Ijk+xPr3vo2qq6fabik5ySZEE3ej+9EH58B4tMuJbe0ecHuud2LPAGYM0Ay+7ITcBLu+fyTHo/a9xZdwKvBc5I8psDLnMh8DtJDktyIL19eFHf0fYVwArgym788m786qp6bBdq1NOAga7tVNUY8G7gk/QCcZzeiT2q6hHgTd34fcBb6Z1sm2o9j9ELxCOA7wGbu/YAX6Z3YvauJPdMsezfA38A/C96HwqHA6cO+BReCVzb/YplLXBGVW2caaHuub0BOIHeCdk/A369qr414HanW++36Z1U/Ht6vyIatE9/8nq+Ry/UVyZ51wCLnAd8hl5gfxf4MfDv++ZfATybfw70q+l9k7gS7bWyfXeoJGlv5RG6JDXCQJekRhjoktSIgQI9ybIkt3aXYq+cps2vJrm5++3vZ4dbpiRpJjOeFE3vJkTfBl5H75cK64HTqurmvjaLgYuB46rq/iTPq6rv72i9Bx98cC1atGg3y5ekfcv1119/T1WNTDVvkLvrLQXGJ376lWQNcDJwc1+bdwOrq+p+gJnCHGDRokWMjY0NsHlJ0oQk012dPVCXy3y2v+R5M9tfDg3wIuBFSf4hyTVJlk1TyPIkY0nGtm7dOsCmJUmDGtZJ0bnAYnp3ZzsN+HSSgyY3qqpzq2q0qkZHRqb8xiBJ2kWDBPoWejdamrCAJ99TYzOwtrsj23fp9bkvHk6JkqRBDBLo64HF3T0h5tG7BHvtpDZfoPtvt5IcTK8LZsbLrSVJwzNjoHc381kBXAbcQu82nhuSrOr7H10uA+5NcjO9ezK/v6ru3VNFS5KebNbu5TI6Olr+ykWSdk6S66tqdKp5XikqSY0w0CWpEQa6JDVikCtFJe2DFq28dLZLaNbtZ524R9brEbokNcJAl6RGGOiS1AgDXZIaYaBLUiMMdElqhIEuSY0w0CWpEQa6JDXCQJekRhjoktQIA12SGmGgS1IjDHRJaoSBLkmNMNAlqREGuiQ1wkCXpEYY6JLUCANdkhphoEtSIwx0SWqEgS5JjRgo0JMsS3JrkvEkK6eYf3qSrUlu7B7vGn6pkqQdmTtTgyRzgNXA64DNwPoka6vq5klNL6qqFXugRknSAAY5Ql8KjFfVxqp6BFgDnLxny5Ik7axBAn0+sKlvfHM3bbI3J/l6ks8lWTjVipIsTzKWZGzr1q27UK4kaTrDOin618Ciqno58HfABVM1qqpzq2q0qkZHRkaGtGlJEgwW6FuA/iPuBd20J1TVvVX1cDf6F8DPDac8SdKgBgn09cDiJIclmQecCqztb5DkBX2jJwG3DK9ESdIgZvyVS1VtS7ICuAyYA5xXVRuSrALGqmot8NtJTgK2AfcBp+/BmiVJU5gx0AGqah2wbtK0M/uGPwR8aLilSZJ2hleKSlIjDHRJaoSBLkmNMNAlqREGuiQ1wkCXpEYY6JLUCANdkhphoEtSIwx0SWqEgS5JjTDQJakRBrokNcJAl6RGGOiS1AgDXZIaYaBLUiMMdElqhIEuSY0w0CWpEQa6JDXCQJekRhjoktQIA12SGmGgS1IjDHRJaoSBLkmNGCjQkyxLcmuS8SQrd9DuzUkqyejwSpQkDWLGQE8yB1gNnAAsAU5LsmSKds8GzgCuHXaRkqSZDXKEvhQYr6qNVfUIsAY4eYp2fwh8DPjxEOuTJA1okECfD2zqG9/cTXtCklcAC6vq0h2tKMnyJGNJxrZu3brTxUqSprfbJ0WT7Ad8AnjfTG2r6tyqGq2q0ZGRkd3dtCSpzyCBvgVY2De+oJs24dnAy4DLk9wOvBpY64lRSXpqDRLo64HFSQ5LMg84FVg7MbOqHqyqg6tqUVUtAq4BTqqqsT1SsSRpSjMGelVtA1YAlwG3ABdX1YYkq5KctKcLlCQNZu4gjapqHbBu0rQzp2l77O6XJUnaWV4pKkmNMNAlqREGuiQ1wkCXpEYY6JLUCANdkhphoEtSIwx0SWqEgS5JjTDQJakRBrokNcJAl6RGGOiS1AgDXZIaYaBLUiMMdElqhIEuSY0w0CWpEQa6JDXCQJekRhjoktQIA12SGmGgS1IjDHRJaoSBLkmNMNAlqREGuiQ1YqBAT7Isya1JxpOsnGL+e5J8I8mNSa5OsmT4pUqSdmTGQE8yB1gNnAAsAU6bIrA/W1X/sqqOBD4OfGLolUqSdmiQI/SlwHhVbayqR4A1wMn9Darq//aNHgDU8EqUJA1i7gBt5gOb+sY3A6+a3CjJbwG/C8wDjptqRUmWA8sBDj300J2tVZK0A0M7KVpVq6vqcOCDwIenaXNuVY1W1ejIyMiwNi1JYrBA3wIs7Btf0E2bzhrgjbtTlCRp5w0S6OuBxUkOSzIPOBVY298gyeK+0ROB24ZXoiRpEDP2oVfVtiQrgMuAOcB5VbUhySpgrKrWAiuSHA88CtwPvH1PFi1JerJBTopSVeuAdZOmndk3fMaQ65Ik7SSvFJWkRhjoktQIA12SGmGgS1IjDHRJaoSBLkmNMNAlqREGuiQ1wkCXpEYY6JLUCANdkhphoEtSIwx0SWqEgS5JjTDQJakRBrokNcJAl6RGGOiS1AgDXZIaYaBLUiMMdElqhIEuSY0w0CWpEQa6JDXCQJekRhjoktSIubNdwK5YtPLS2S6hWbefdeIeWa+v2Z6zp14z7X0GOkJPsizJrUnGk6ycYv7vJrk5ydeTfCnJC4dfqiRpR2YM9CRzgNXACcAS4LQkSyY1+0dgtKpeDnwO+PiwC5Uk7dggR+hLgfGq2lhVjwBrgJP7G1TVV6rqR93oNcCC4ZYpSZrJIIE+H9jUN765mzad3wD+ZqoZSZYnGUsytnXr1sGrlCTNaKi/cknya8AocPZU86vq3KoararRkZGRYW5akvZ5g/zKZQuwsG98QTdtO0mOB34f+IWqeng45UmSBjXIEfp6YHGSw5LMA04F1vY3SHIUcA5wUlV9f/hlSpJmMmOgV9U2YAVwGXALcHFVbUiyKslJXbOzgQOBv0pyY5K106xOkrSHDHRhUVWtA9ZNmnZm3/DxQ65LkrSTvPRfkhphoEtSIwx0SWqEgS5JjTDQJakRBrokNcJAl6RGGOiS1AgDXZIaYaBLUiMMdElqhIEuSY0w0CWpEQa6JDXCQJekRhjoktQIA12SGmGgS1IjDHRJaoSBLkmNMNAlqREGuiQ1wkCXpEYY6JLUCANdkhphoEtSIwx0SWrEQIGeZFmSW5OMJ1k5xfzXJLkhybYkbxl+mZKkmcwY6EnmAKuBE4AlwGlJlkxq9j3gdOCzwy5QkjSYuQO0WQqMV9VGgCRrgJOBmycaVNXt3bzH90CNkqQBDNLlMh/Y1De+uZu205IsTzKWZGzr1q27sgpJ0jSe0pOiVXVuVY1W1ejIyMhTuWlJat4ggb4FWNg3vqCbJkl6Ghkk0NcDi5MclmQecCqwds+WJUnaWTMGelVtA1YAlwG3ABdX1YYkq5KcBJDklUk2A6cA5yTZsCeLliQ92SC/cqGq1gHrJk07s294Pb2uGEnSLPFKUUlqhIEuSY0w0CWpEQa6JDXCQJekRhjoktQIA12SGmGgS1IjDHRJaoSBLkmNMNAlqREGuiQ1wkCXpEYY6JLUCANdkhphoEtSIwx0SWqEgS5JjTDQJakRBrokNcJAl6RGGOiS1AgDXZIaYaBLUiMMdElqhIEuSY0w0CWpEQMFepJlSW5NMp5k5RTz909yUTf/2iSLhl2oJGnHZgz0JHOA1cAJwBLgtCRLJjX7DeD+qjoC+BPgY8MuVJK0Y4McoS8FxqtqY1U9AqwBTp7U5mTggm74c8Brk2R4ZUqSZjJ3gDbzgU1945uBV03Xpqq2JXkQeC5wT3+jJMuB5d3oQ0lu3ZWi90IHM2lfPF3F71awF71e4GvW2ZdesxdON2OQQB+aqjoXOPep3ObTQZKxqhqd7To0GF+vvY+vWc8gXS5bgIV94wu6aVO2STIX+Eng3mEUKEkazCCBvh5YnOSwJPOAU4G1k9qsBd7eDb8F+HJV1fDKlCTNZMYul65PfAVwGTAHOK+qNiRZBYxV1VrgvwOfSTIO3Ecv9PXP9rlupr2cr9fex9cMiAfSktQGrxSVpEYY6JLUCANd0tNWktOTfHK269hbGOiS1AgDfTclWZXkvX3jf5TkjCTvT7I+ydeT/Kdu3gFJLk1yU5JvJnnr7FUugCSLktyS5NNJNiT52yQ/keTIJNd0r98lSZ4z27W2oNvf3+wb/70kH0lyeZKPJbkuybeTHDPFsicm+VqSg5Ocn+S/Jflqko1J3tK1SZKzu7+vb0z8jSVZneSkbviSJOd1w+/s/manfB88NXtleAz03Xce8OsASfaj95PNu4DF9O6DcyTwc0leAywD7qyqn62qlwH/Z3ZK1iSLgdVV9VLgAeDNwF8CH6yqlwPfAP7jLNa3r5hbVUuB9zJpfyf5FWAl8PqqmrjE/wXA0cAvA2d1095E72/uZ4HjgbOTvAC4Cpj4kJhP70aDdNOu7Ianeh/sVQz03VRVtwP3JjkK+CXgH4FX9g3fAPwLem+WbwCv645EjqmqB2enak3y3aq6sRu+HjgcOKiqruimXQC8ZlYq27d8vvv3emBR3/TjgA8CJ1bV/X3Tv1BVj1fVzcDzu2lHAxdW1WNVdTdwBb2/x6uAY7o7xd4M3N0F/c8DX+2Wnfw+6K9hr/CU3sulYX8BnA78NL0j9tcCf1xV50xumOQVwOuBjyb5UlWteioL1ZQe7ht+DDhotgrZB2xj+wPJZ/YNT7wOj7F9Nn0H+BngRcDYFO0Bdnh316rakuQget+SrwR+CvhV4KGq+kGS5/Lk94FdLvuoS+i9UV5J74ray4B3JjkQIMn8JM9Lcgjwo6r6H8DZwCtmq2Dt0IPA/X39uP+G3pGedt/dwPOSPDfJ/vS6S2ZyB103WJKXztD2KuCtSeYkGaH3zeq6bt419Lpzruza/V73bzM8Qh+CqnokyVeAB6rqMeBvk7wE+Fp3W/iHgF8DjqDXp/c48Cjwm7NVs2b0duBTSZ4FbATeMcv1NKGqHu1uG3IdvZv6fWvA5b6V5G3AXyV5ww6aXkKvG+UmoIAPVNVd3byrgF+qqvEkd9A7Sm8q0L30fwi6k6E3AKdU1W2zXY+kfZNdLrupO8kyDnzJMJc0mzxCl6RGeIQuSY0w0CWpEQa6JDXCQJekRhjoktSI/w/TIK6YopbnBgAAAABJRU5ErkJggg==", + "text/plain": [ + "
" + ] + }, + "metadata": { + "needs_background": "light", + "tags": [] + }, + "output_type": "display_data" + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "/content/data/mini_speech_commands/unknown/26b28ea7_nohash_0.wav\n" + ] + }, + { + "data": { + "image/png": "iVBORw0KGgoAAAANSUhEUgAAAXQAAAEICAYAAABPgw/pAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAALEgAACxIB0t1+/AAAADh0RVh0U29mdHdhcmUAbWF0cGxvdGxpYiB2ZXJzaW9uMy4yLjIsIGh0dHA6Ly9tYXRwbG90bGliLm9yZy+WH4yJAAAUd0lEQVR4nO3df7RdZX3n8feHxGAFV7FytZIEQyE6RseCvUa7BixF7ApSwapUGDsVrWbsNFNsrRqnlnFSO0WZZWdNTSvYYUGdkUAd0XRIh7Yqv6pALhTUgMg1gkkoGH6O6AgEvvPH2ZeeXO7NPUlOuOTJ+7XWWdk/nr339+xz7ufs8+yzd1JVSJL2fvvNdgGSpOEw0CWpEQa6JDXCQJekRhjoktQIA12SGmGga7ckOT/JR7vhY5Lcuovr+VSSPxhudTNu81eSbEryUJKjnsptDypJJTlituvQ3mHubBegPS/J7cDzgceAHwJ/A6yoqoeGuZ2qugp48QD1nA68q6qO7lv2PcOsZUD/hd5++OIwVpbkcuAjwLEAVfWRYaz36SrJ+cDl3eixVXX6rBUjwCP0fckbqupA4BXAKPDhyQ2S7Gsf8C8ENuzKgknmDLkWabcZ6PuYqtpC7wj9ZfDEV/rfSnIbcFs37ZeT3JjkgSRfTfLyieWTHJXkhiQ/SHIR8My+eccm2dw3vjDJ55NsTXJvkk8meQnwKeDnu66OB7q2T3TddOPvTjKe5L4ka5Mc0jevkrwnyW1djauTpJt3RJIrkjyY5J6uxu0k2T/JQ8Ac4KYk3+mmvyTJ5d06NyQ5qW+Z85P8eZJ1SX4I/OIg+zvJ6UmunjTtiW6Ubr2rk1za7dNrkxw+zbqO7rqIjh1gP+yX5MNJ7kjy/SR/meQnu3kXJHlfNzx/4j3QjR/e7fP9Jl7PJO/r1vFPSd4xyPPWLKkqH40/gNuB47vhhfSOSv+wGy/g74CfAn4COAr4PvAqeoH39m75/YF5wB3A7wDPAN4CPAp8tFvXscDmbngOcBPwJ8AB9IL/6G7e6cDVk2o8v289xwH30Ps2sT/wp8CVfW0L+N/AQcChwFZgWTfvQuD36R2sPLHNafZLAUd0w88AxoH/0D3P44AfAC/uq+9B4F9NrHvAfT/Vc+3f7vnAvcBSel2g/xNYM7ktsAzYBCwdcD+8s3s+PwMcCHwe+EzfvL/uhv818B3gor55X+x7PbcBq7r983rgR8BzZvs97WPqh0fo+44vdEfDVwNXAP+5b94fV9V9VfX/gOXAOVV1bVU9VlUXAA8Dr+4ezwD+a1U9WlWfA9ZPs72lwCHA+6vqh1X146q6epq2k70NOK+qbqiqh4EP0TuiX9TX5qyqeqCqvgd8BTiym/4ova6UQ3Zym6+mF3xnVdUjVfVlemF5Wl+bL1bVP1TV41X14wHXO4hLquq6qtpGL9CPnDT/FOAc4ISqum7SvOn2w9uAT1TVxuqdK/kQcGrXrXYFcHSS/YDXAB+n90EF8Avd/AmPAqu613sd8BADnCfR7DDQ9x1vrKqDquqFVfXvuvCesKlv+IXA+7qv8A90HwIL6YXzIcCWquq/o9sd02xvIXBHF1I765D+9XaBdC8wv6/NXX3DP6IXxgAfAAJc13WbvHMntrmpqh7vm3bHpG1uYs+Y7rlMeC9wcVV9cyeW3W4fdsNzgedX1XfonRw/EjiG3gfXnUlezJMD/d5Jr+FU9elpwkAX9L66T9gE/FEX/hOPZ1XVhcA/AfMn+mk7h06zzk3AodOcaJ3pFp930vtgASDJAcBzgS0zPpGqu6rq3VV1CPBvgT8b8Gd/dwILu6PWCYdO2uau3Jr0h8CzJkaS/PQurOMU4I1JztiJZbbbh/Seyzbg7m78CnpdZvOqd17lCnrda88BbtyFGvU0YKBrsk8D70nyqvQckOTEJM8GvkYvFH47yTOSvIle18pUrqP3AXBWt45nJpn4Wn83sCDJvGmWvRB4R5Ijk+xPr3vo2qq6fabik5ySZEE3ej+9EH58B4tMuJbe0ecHuud2LPAGYM0Ay+7ITcBLu+fyTHo/a9xZdwKvBc5I8psDLnMh8DtJDktyIL19eFHf0fYVwArgym788m786qp6bBdq1NOAga7tVNUY8G7gk/QCcZzeiT2q6hHgTd34fcBb6Z1sm2o9j9ELxCOA7wGbu/YAX6Z3YvauJPdMsezfA38A/C96HwqHA6cO+BReCVzb/YplLXBGVW2caaHuub0BOIHeCdk/A369qr414HanW++36Z1U/Ht6vyIatE9/8nq+Ry/UVyZ51wCLnAd8hl5gfxf4MfDv++ZfATybfw70q+l9k7gS7bWyfXeoJGlv5RG6JDXCQJekRhjoktSIgQI9ybIkt3aXYq+cps2vJrm5++3vZ4dbpiRpJjOeFE3vJkTfBl5H75cK64HTqurmvjaLgYuB46rq/iTPq6rv72i9Bx98cC1atGg3y5ekfcv1119/T1WNTDVvkLvrLQXGJ376lWQNcDJwc1+bdwOrq+p+gJnCHGDRokWMjY0NsHlJ0oQk012dPVCXy3y2v+R5M9tfDg3wIuBFSf4hyTVJlk1TyPIkY0nGtm7dOsCmJUmDGtZJ0bnAYnp3ZzsN+HSSgyY3qqpzq2q0qkZHRqb8xiBJ2kWDBPoWejdamrCAJ99TYzOwtrsj23fp9bkvHk6JkqRBDBLo64HF3T0h5tG7BHvtpDZfoPtvt5IcTK8LZsbLrSVJwzNjoHc381kBXAbcQu82nhuSrOr7H10uA+5NcjO9ezK/v6ru3VNFS5KebNbu5TI6Olr+ykWSdk6S66tqdKp5XikqSY0w0CWpEQa6JDVikCtFJe2DFq28dLZLaNbtZ524R9brEbokNcJAl6RGGOiS1AgDXZIaYaBLUiMMdElqhIEuSY0w0CWpEQa6JDXCQJekRhjoktQIA12SGmGgS1IjDHRJaoSBLkmNMNAlqREGuiQ1wkCXpEYY6JLUCANdkhphoEtSIwx0SWqEgS5JjRgo0JMsS3JrkvEkK6eYf3qSrUlu7B7vGn6pkqQdmTtTgyRzgNXA64DNwPoka6vq5klNL6qqFXugRknSAAY5Ql8KjFfVxqp6BFgDnLxny5Ik7axBAn0+sKlvfHM3bbI3J/l6ks8lWTjVipIsTzKWZGzr1q27UK4kaTrDOin618Ciqno58HfABVM1qqpzq2q0qkZHRkaGtGlJEgwW6FuA/iPuBd20J1TVvVX1cDf6F8DPDac8SdKgBgn09cDiJIclmQecCqztb5DkBX2jJwG3DK9ESdIgZvyVS1VtS7ICuAyYA5xXVRuSrALGqmot8NtJTgK2AfcBp+/BmiVJU5gx0AGqah2wbtK0M/uGPwR8aLilSZJ2hleKSlIjDHRJaoSBLkmNMNAlqREGuiQ1wkCXpEYY6JLUCANdkhphoEtSIwx0SWqEgS5JjTDQJakRBrokNcJAl6RGGOiS1AgDXZIaYaBLUiMMdElqhIEuSY0w0CWpEQa6JDXCQJekRhjoktQIA12SGmGgS1IjDHRJaoSBLkmNGCjQkyxLcmuS8SQrd9DuzUkqyejwSpQkDWLGQE8yB1gNnAAsAU5LsmSKds8GzgCuHXaRkqSZDXKEvhQYr6qNVfUIsAY4eYp2fwh8DPjxEOuTJA1okECfD2zqG9/cTXtCklcAC6vq0h2tKMnyJGNJxrZu3brTxUqSprfbJ0WT7Ad8AnjfTG2r6tyqGq2q0ZGRkd3dtCSpzyCBvgVY2De+oJs24dnAy4DLk9wOvBpY64lRSXpqDRLo64HFSQ5LMg84FVg7MbOqHqyqg6tqUVUtAq4BTqqqsT1SsSRpSjMGelVtA1YAlwG3ABdX1YYkq5KctKcLlCQNZu4gjapqHbBu0rQzp2l77O6XJUnaWV4pKkmNMNAlqREGuiQ1wkCXpEYY6JLUCANdkhphoEtSIwx0SWqEgS5JjTDQJakRBrokNcJAl6RGGOiS1AgDXZIaYaBLUiMMdElqhIEuSY0w0CWpEQa6JDXCQJekRhjoktQIA12SGmGgS1IjDHRJaoSBLkmNMNAlqREGuiQ1YqBAT7Isya1JxpOsnGL+e5J8I8mNSa5OsmT4pUqSdmTGQE8yB1gNnAAsAU6bIrA/W1X/sqqOBD4OfGLolUqSdmiQI/SlwHhVbayqR4A1wMn9Darq//aNHgDU8EqUJA1i7gBt5gOb+sY3A6+a3CjJbwG/C8wDjptqRUmWA8sBDj300J2tVZK0A0M7KVpVq6vqcOCDwIenaXNuVY1W1ejIyMiwNi1JYrBA3wIs7Btf0E2bzhrgjbtTlCRp5w0S6OuBxUkOSzIPOBVY298gyeK+0ROB24ZXoiRpEDP2oVfVtiQrgMuAOcB5VbUhySpgrKrWAiuSHA88CtwPvH1PFi1JerJBTopSVeuAdZOmndk3fMaQ65Ik7SSvFJWkRhjoktQIA12SGmGgS1IjDHRJaoSBLkmNMNAlqREGuiQ1wkCXpEYY6JLUCANdkhphoEtSIwx0SWqEgS5JjTDQJakRBrokNcJAl6RGGOiS1AgDXZIaYaBLUiMMdElqhIEuSY0w0CWpEQa6JDXCQJekRhjoktSIubNdwK5YtPLS2S6hWbefdeIeWa+v2Z6zp14z7X0GOkJPsizJrUnGk6ycYv7vJrk5ydeTfCnJC4dfqiRpR2YM9CRzgNXACcAS4LQkSyY1+0dgtKpeDnwO+PiwC5Uk7dggR+hLgfGq2lhVjwBrgJP7G1TVV6rqR93oNcCC4ZYpSZrJIIE+H9jUN765mzad3wD+ZqoZSZYnGUsytnXr1sGrlCTNaKi/cknya8AocPZU86vq3KoararRkZGRYW5akvZ5g/zKZQuwsG98QTdtO0mOB34f+IWqeng45UmSBjXIEfp6YHGSw5LMA04F1vY3SHIUcA5wUlV9f/hlSpJmMmOgV9U2YAVwGXALcHFVbUiyKslJXbOzgQOBv0pyY5K106xOkrSHDHRhUVWtA9ZNmnZm3/DxQ65LkrSTvPRfkhphoEtSIwx0SWqEgS5JjTDQJakRBrokNcJAl6RGGOiS1AgDXZIaYaBLUiMMdElqhIEuSY0w0CWpEQa6JDXCQJekRhjoktQIA12SGmGgS1IjDHRJaoSBLkmNMNAlqREGuiQ1wkCXpEYY6JLUCANdkhphoEtSIwx0SWrEQIGeZFmSW5OMJ1k5xfzXJLkhybYkbxl+mZKkmcwY6EnmAKuBE4AlwGlJlkxq9j3gdOCzwy5QkjSYuQO0WQqMV9VGgCRrgJOBmycaVNXt3bzH90CNkqQBDNLlMh/Y1De+uZu205IsTzKWZGzr1q27sgpJ0jSe0pOiVXVuVY1W1ejIyMhTuWlJat4ggb4FWNg3vqCbJkl6Ghkk0NcDi5MclmQecCqwds+WJUnaWTMGelVtA1YAlwG3ABdX1YYkq5KcBJDklUk2A6cA5yTZsCeLliQ92SC/cqGq1gHrJk07s294Pb2uGEnSLPFKUUlqhIEuSY0w0CWpEQa6JDXCQJekRhjoktQIA12SGmGgS1IjDHRJaoSBLkmNMNAlqREGuiQ1wkCXpEYY6JLUCANdkhphoEtSIwx0SWqEgS5JjTDQJakRBrokNcJAl6RGGOiS1AgDXZIaYaBLUiMMdElqhIEuSY0w0CWpEQMFepJlSW5NMp5k5RTz909yUTf/2iSLhl2oJGnHZgz0JHOA1cAJwBLgtCRLJjX7DeD+qjoC+BPgY8MuVJK0Y4McoS8FxqtqY1U9AqwBTp7U5mTggm74c8Brk2R4ZUqSZjJ3gDbzgU1945uBV03Xpqq2JXkQeC5wT3+jJMuB5d3oQ0lu3ZWi90IHM2lfPF3F71awF71e4GvW2ZdesxdON2OQQB+aqjoXOPep3ObTQZKxqhqd7To0GF+vvY+vWc8gXS5bgIV94wu6aVO2STIX+Eng3mEUKEkazCCBvh5YnOSwJPOAU4G1k9qsBd7eDb8F+HJV1fDKlCTNZMYul65PfAVwGTAHOK+qNiRZBYxV1VrgvwOfSTIO3Ecv9PXP9rlupr2cr9fex9cMiAfSktQGrxSVpEYY6JLUCANd0tNWktOTfHK269hbGOiS1AgDfTclWZXkvX3jf5TkjCTvT7I+ydeT/Kdu3gFJLk1yU5JvJnnr7FUugCSLktyS5NNJNiT52yQ/keTIJNd0r98lSZ4z27W2oNvf3+wb/70kH0lyeZKPJbkuybeTHDPFsicm+VqSg5Ocn+S/Jflqko1J3tK1SZKzu7+vb0z8jSVZneSkbviSJOd1w+/s/manfB88NXtleAz03Xce8OsASfaj95PNu4DF9O6DcyTwc0leAywD7qyqn62qlwH/Z3ZK1iSLgdVV9VLgAeDNwF8CH6yqlwPfAP7jLNa3r5hbVUuB9zJpfyf5FWAl8PqqmrjE/wXA0cAvA2d1095E72/uZ4HjgbOTvAC4Cpj4kJhP70aDdNOu7Ianeh/sVQz03VRVtwP3JjkK+CXgH4FX9g3fAPwLem+WbwCv645EjqmqB2enak3y3aq6sRu+HjgcOKiqruimXQC8ZlYq27d8vvv3emBR3/TjgA8CJ1bV/X3Tv1BVj1fVzcDzu2lHAxdW1WNVdTdwBb2/x6uAY7o7xd4M3N0F/c8DX+2Wnfw+6K9hr/CU3sulYX8BnA78NL0j9tcCf1xV50xumOQVwOuBjyb5UlWteioL1ZQe7ht+DDhotgrZB2xj+wPJZ/YNT7wOj7F9Nn0H+BngRcDYFO0Bdnh316rakuQget+SrwR+CvhV4KGq+kGS5/Lk94FdLvuoS+i9UV5J74ray4B3JjkQIMn8JM9Lcgjwo6r6H8DZwCtmq2Dt0IPA/X39uP+G3pGedt/dwPOSPDfJ/vS6S2ZyB103WJKXztD2KuCtSeYkGaH3zeq6bt419Lpzruza/V73bzM8Qh+CqnokyVeAB6rqMeBvk7wE+Fp3W/iHgF8DjqDXp/c48Cjwm7NVs2b0duBTSZ4FbATeMcv1NKGqHu1uG3IdvZv6fWvA5b6V5G3AXyV5ww6aXkKvG+UmoIAPVNVd3byrgF+qqvEkd9A7Sm8q0L30fwi6k6E3AKdU1W2zXY+kfZNdLrupO8kyDnzJMJc0mzxCl6RGeIQuSY0w0CWpEQa6JDXCQJekRhjoktSI/w/TIK6YopbnBgAAAABJRU5ErkJggg==", + "text/plain": [ + "
" + ] + }, + "metadata": { + "needs_background": "light", + "tags": [] + }, + "output_type": "display_data" + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "/content/data/mini_speech_commands/unknown/d84829e0_nohash_0.wav\n" + ] + }, + { + "data": { + "image/png": "iVBORw0KGgoAAAANSUhEUgAAAXQAAAEICAYAAABPgw/pAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAALEgAACxIB0t1+/AAAADh0RVh0U29mdHdhcmUAbWF0cGxvdGxpYiB2ZXJzaW9uMy4yLjIsIGh0dHA6Ly9tYXRwbG90bGliLm9yZy+WH4yJAAAUd0lEQVR4nO3df7RdZX3n8feHxGAFV7FytZIEQyE6RseCvUa7BixF7ApSwapUGDsVrWbsNFNsrRqnlnFSO0WZZWdNTSvYYUGdkUAd0XRIh7Yqv6pALhTUgMg1gkkoGH6O6AgEvvPH2ZeeXO7NPUlOuOTJ+7XWWdk/nr339+xz7ufs8+yzd1JVSJL2fvvNdgGSpOEw0CWpEQa6JDXCQJekRhjoktQIA12SGmGga7ckOT/JR7vhY5Lcuovr+VSSPxhudTNu81eSbEryUJKjnsptDypJJTlituvQ3mHubBegPS/J7cDzgceAHwJ/A6yoqoeGuZ2qugp48QD1nA68q6qO7lv2PcOsZUD/hd5++OIwVpbkcuAjwLEAVfWRYaz36SrJ+cDl3eixVXX6rBUjwCP0fckbqupA4BXAKPDhyQ2S7Gsf8C8ENuzKgknmDLkWabcZ6PuYqtpC7wj9ZfDEV/rfSnIbcFs37ZeT3JjkgSRfTfLyieWTHJXkhiQ/SHIR8My+eccm2dw3vjDJ55NsTXJvkk8meQnwKeDnu66OB7q2T3TddOPvTjKe5L4ka5Mc0jevkrwnyW1djauTpJt3RJIrkjyY5J6uxu0k2T/JQ8Ac4KYk3+mmvyTJ5d06NyQ5qW+Z85P8eZJ1SX4I/OIg+zvJ6UmunjTtiW6Ubr2rk1za7dNrkxw+zbqO7rqIjh1gP+yX5MNJ7kjy/SR/meQnu3kXJHlfNzx/4j3QjR/e7fP9Jl7PJO/r1vFPSd4xyPPWLKkqH40/gNuB47vhhfSOSv+wGy/g74CfAn4COAr4PvAqeoH39m75/YF5wB3A7wDPAN4CPAp8tFvXscDmbngOcBPwJ8AB9IL/6G7e6cDVk2o8v289xwH30Ps2sT/wp8CVfW0L+N/AQcChwFZgWTfvQuD36R2sPLHNafZLAUd0w88AxoH/0D3P44AfAC/uq+9B4F9NrHvAfT/Vc+3f7vnAvcBSel2g/xNYM7ktsAzYBCwdcD+8s3s+PwMcCHwe+EzfvL/uhv818B3gor55X+x7PbcBq7r983rgR8BzZvs97WPqh0fo+44vdEfDVwNXAP+5b94fV9V9VfX/gOXAOVV1bVU9VlUXAA8Dr+4ezwD+a1U9WlWfA9ZPs72lwCHA+6vqh1X146q6epq2k70NOK+qbqiqh4EP0TuiX9TX5qyqeqCqvgd8BTiym/4ova6UQ3Zym6+mF3xnVdUjVfVlemF5Wl+bL1bVP1TV41X14wHXO4hLquq6qtpGL9CPnDT/FOAc4ISqum7SvOn2w9uAT1TVxuqdK/kQcGrXrXYFcHSS/YDXAB+n90EF8Avd/AmPAqu613sd8BADnCfR7DDQ9x1vrKqDquqFVfXvuvCesKlv+IXA+7qv8A90HwIL6YXzIcCWquq/o9sd02xvIXBHF1I765D+9XaBdC8wv6/NXX3DP6IXxgAfAAJc13WbvHMntrmpqh7vm3bHpG1uYs+Y7rlMeC9wcVV9cyeW3W4fdsNzgedX1XfonRw/EjiG3gfXnUlezJMD/d5Jr+FU9elpwkAX9L66T9gE/FEX/hOPZ1XVhcA/AfMn+mk7h06zzk3AodOcaJ3pFp930vtgASDJAcBzgS0zPpGqu6rq3VV1CPBvgT8b8Gd/dwILu6PWCYdO2uau3Jr0h8CzJkaS/PQurOMU4I1JztiJZbbbh/Seyzbg7m78CnpdZvOqd17lCnrda88BbtyFGvU0YKBrsk8D70nyqvQckOTEJM8GvkYvFH47yTOSvIle18pUrqP3AXBWt45nJpn4Wn83sCDJvGmWvRB4R5Ijk+xPr3vo2qq6fabik5ySZEE3ej+9EH58B4tMuJbe0ecHuud2LPAGYM0Ay+7ITcBLu+fyTHo/a9xZdwKvBc5I8psDLnMh8DtJDktyIL19eFHf0fYVwArgym788m786qp6bBdq1NOAga7tVNUY8G7gk/QCcZzeiT2q6hHgTd34fcBb6Z1sm2o9j9ELxCOA7wGbu/YAX6Z3YvauJPdMsezfA38A/C96HwqHA6cO+BReCVzb/YplLXBGVW2caaHuub0BOIHeCdk/A369qr414HanW++36Z1U/Ht6vyIatE9/8nq+Ry/UVyZ51wCLnAd8hl5gfxf4MfDv++ZfATybfw70q+l9k7gS7bWyfXeoJGlv5RG6JDXCQJekRhjoktSIgQI9ybIkt3aXYq+cps2vJrm5++3vZ4dbpiRpJjOeFE3vJkTfBl5H75cK64HTqurmvjaLgYuB46rq/iTPq6rv72i9Bx98cC1atGg3y5ekfcv1119/T1WNTDVvkLvrLQXGJ376lWQNcDJwc1+bdwOrq+p+gJnCHGDRokWMjY0NsHlJ0oQk012dPVCXy3y2v+R5M9tfDg3wIuBFSf4hyTVJlk1TyPIkY0nGtm7dOsCmJUmDGtZJ0bnAYnp3ZzsN+HSSgyY3qqpzq2q0qkZHRqb8xiBJ2kWDBPoWejdamrCAJ99TYzOwtrsj23fp9bkvHk6JkqRBDBLo64HF3T0h5tG7BHvtpDZfoPtvt5IcTK8LZsbLrSVJwzNjoHc381kBXAbcQu82nhuSrOr7H10uA+5NcjO9ezK/v6ru3VNFS5KebNbu5TI6Olr+ykWSdk6S66tqdKp5XikqSY0w0CWpEQa6JDVikCtFJe2DFq28dLZLaNbtZ524R9brEbokNcJAl6RGGOiS1AgDXZIaYaBLUiMMdElqhIEuSY0w0CWpEQa6JDXCQJekRhjoktQIA12SGmGgS1IjDHRJaoSBLkmNMNAlqREGuiQ1wkCXpEYY6JLUCANdkhphoEtSIwx0SWqEgS5JjRgo0JMsS3JrkvEkK6eYf3qSrUlu7B7vGn6pkqQdmTtTgyRzgNXA64DNwPoka6vq5klNL6qqFXugRknSAAY5Ql8KjFfVxqp6BFgDnLxny5Ik7axBAn0+sKlvfHM3bbI3J/l6ks8lWTjVipIsTzKWZGzr1q27UK4kaTrDOin618Ciqno58HfABVM1qqpzq2q0qkZHRkaGtGlJEgwW6FuA/iPuBd20J1TVvVX1cDf6F8DPDac8SdKgBgn09cDiJIclmQecCqztb5DkBX2jJwG3DK9ESdIgZvyVS1VtS7ICuAyYA5xXVRuSrALGqmot8NtJTgK2AfcBp+/BmiVJU5gx0AGqah2wbtK0M/uGPwR8aLilSZJ2hleKSlIjDHRJaoSBLkmNMNAlqREGuiQ1wkCXpEYY6JLUCANdkhphoEtSIwx0SWqEgS5JjTDQJakRBrokNcJAl6RGGOiS1AgDXZIaYaBLUiMMdElqhIEuSY0w0CWpEQa6JDXCQJekRhjoktQIA12SGmGgS1IjDHRJaoSBLkmNGCjQkyxLcmuS8SQrd9DuzUkqyejwSpQkDWLGQE8yB1gNnAAsAU5LsmSKds8GzgCuHXaRkqSZDXKEvhQYr6qNVfUIsAY4eYp2fwh8DPjxEOuTJA1okECfD2zqG9/cTXtCklcAC6vq0h2tKMnyJGNJxrZu3brTxUqSprfbJ0WT7Ad8AnjfTG2r6tyqGq2q0ZGRkd3dtCSpzyCBvgVY2De+oJs24dnAy4DLk9wOvBpY64lRSXpqDRLo64HFSQ5LMg84FVg7MbOqHqyqg6tqUVUtAq4BTqqqsT1SsSRpSjMGelVtA1YAlwG3ABdX1YYkq5KctKcLlCQNZu4gjapqHbBu0rQzp2l77O6XJUnaWV4pKkmNMNAlqREGuiQ1wkCXpEYY6JLUCANdkhphoEtSIwx0SWqEgS5JjTDQJakRBrokNcJAl6RGGOiS1AgDXZIaYaBLUiMMdElqhIEuSY0w0CWpEQa6JDXCQJekRhjoktQIA12SGmGgS1IjDHRJaoSBLkmNMNAlqREGuiQ1YqBAT7Isya1JxpOsnGL+e5J8I8mNSa5OsmT4pUqSdmTGQE8yB1gNnAAsAU6bIrA/W1X/sqqOBD4OfGLolUqSdmiQI/SlwHhVbayqR4A1wMn9Darq//aNHgDU8EqUJA1i7gBt5gOb+sY3A6+a3CjJbwG/C8wDjptqRUmWA8sBDj300J2tVZK0A0M7KVpVq6vqcOCDwIenaXNuVY1W1ejIyMiwNi1JYrBA3wIs7Btf0E2bzhrgjbtTlCRp5w0S6OuBxUkOSzIPOBVY298gyeK+0ROB24ZXoiRpEDP2oVfVtiQrgMuAOcB5VbUhySpgrKrWAiuSHA88CtwPvH1PFi1JerJBTopSVeuAdZOmndk3fMaQ65Ik7SSvFJWkRhjoktQIA12SGmGgS1IjDHRJaoSBLkmNMNAlqREGuiQ1wkCXpEYY6JLUCANdkhphoEtSIwx0SWqEgS5JjTDQJakRBrokNcJAl6RGGOiS1AgDXZIaYaBLUiMMdElqhIEuSY0w0CWpEQa6JDXCQJekRhjoktSIubNdwK5YtPLS2S6hWbefdeIeWa+v2Z6zp14z7X0GOkJPsizJrUnGk6ycYv7vJrk5ydeTfCnJC4dfqiRpR2YM9CRzgNXACcAS4LQkSyY1+0dgtKpeDnwO+PiwC5Uk7dggR+hLgfGq2lhVjwBrgJP7G1TVV6rqR93oNcCC4ZYpSZrJIIE+H9jUN765mzad3wD+ZqoZSZYnGUsytnXr1sGrlCTNaKi/cknya8AocPZU86vq3KoararRkZGRYW5akvZ5g/zKZQuwsG98QTdtO0mOB34f+IWqeng45UmSBjXIEfp6YHGSw5LMA04F1vY3SHIUcA5wUlV9f/hlSpJmMmOgV9U2YAVwGXALcHFVbUiyKslJXbOzgQOBv0pyY5K106xOkrSHDHRhUVWtA9ZNmnZm3/DxQ65LkrSTvPRfkhphoEtSIwx0SWqEgS5JjTDQJakRBrokNcJAl6RGGOiS1AgDXZIaYaBLUiMMdElqhIEuSY0w0CWpEQa6JDXCQJekRhjoktQIA12SGmGgS1IjDHRJaoSBLkmNMNAlqREGuiQ1wkCXpEYY6JLUCANdkhphoEtSIwx0SWrEQIGeZFmSW5OMJ1k5xfzXJLkhybYkbxl+mZKkmcwY6EnmAKuBE4AlwGlJlkxq9j3gdOCzwy5QkjSYuQO0WQqMV9VGgCRrgJOBmycaVNXt3bzH90CNkqQBDNLlMh/Y1De+uZu205IsTzKWZGzr1q27sgpJ0jSe0pOiVXVuVY1W1ejIyMhTuWlJat4ggb4FWNg3vqCbJkl6Ghkk0NcDi5MclmQecCqwds+WJUnaWTMGelVtA1YAlwG3ABdX1YYkq5KcBJDklUk2A6cA5yTZsCeLliQ92SC/cqGq1gHrJk07s294Pb2uGEnSLPFKUUlqhIEuSY0w0CWpEQa6JDXCQJekRhjoktQIA12SGmGgS1IjDHRJaoSBLkmNMNAlqREGuiQ1wkCXpEYY6JLUCANdkhphoEtSIwx0SWqEgS5JjTDQJakRBrokNcJAl6RGGOiS1AgDXZIaYaBLUiMMdElqhIEuSY0w0CWpEQMFepJlSW5NMp5k5RTz909yUTf/2iSLhl2oJGnHZgz0JHOA1cAJwBLgtCRLJjX7DeD+qjoC+BPgY8MuVJK0Y4McoS8FxqtqY1U9AqwBTp7U5mTggm74c8Brk2R4ZUqSZjJ3gDbzgU1945uBV03Xpqq2JXkQeC5wT3+jJMuB5d3oQ0lu3ZWi90IHM2lfPF3F71awF71e4GvW2ZdesxdON2OQQB+aqjoXOPep3ObTQZKxqhqd7To0GF+vvY+vWc8gXS5bgIV94wu6aVO2STIX+Eng3mEUKEkazCCBvh5YnOSwJPOAU4G1k9qsBd7eDb8F+HJV1fDKlCTNZMYul65PfAVwGTAHOK+qNiRZBYxV1VrgvwOfSTIO3Ecv9PXP9rlupr2cr9fex9cMiAfSktQGrxSVpEYY6JLUCANd0tNWktOTfHK269hbGOiS1AgDfTclWZXkvX3jf5TkjCTvT7I+ydeT/Kdu3gFJLk1yU5JvJnnr7FUugCSLktyS5NNJNiT52yQ/keTIJNd0r98lSZ4z27W2oNvf3+wb/70kH0lyeZKPJbkuybeTHDPFsicm+VqSg5Ocn+S/Jflqko1J3tK1SZKzu7+vb0z8jSVZneSkbviSJOd1w+/s/manfB88NXtleAz03Xce8OsASfaj95PNu4DF9O6DcyTwc0leAywD7qyqn62qlwH/Z3ZK1iSLgdVV9VLgAeDNwF8CH6yqlwPfAP7jLNa3r5hbVUuB9zJpfyf5FWAl8PqqmrjE/wXA0cAvA2d1095E72/uZ4HjgbOTvAC4Cpj4kJhP70aDdNOu7Ianeh/sVQz03VRVtwP3JjkK+CXgH4FX9g3fAPwLem+WbwCv645EjqmqB2enak3y3aq6sRu+HjgcOKiqruimXQC8ZlYq27d8vvv3emBR3/TjgA8CJ1bV/X3Tv1BVj1fVzcDzu2lHAxdW1WNVdTdwBb2/x6uAY7o7xd4M3N0F/c8DX+2Wnfw+6K9hr/CU3sulYX8BnA78NL0j9tcCf1xV50xumOQVwOuBjyb5UlWteioL1ZQe7ht+DDhotgrZB2xj+wPJZ/YNT7wOj7F9Nn0H+BngRcDYFO0Bdnh316rakuQget+SrwR+CvhV4KGq+kGS5/Lk94FdLvuoS+i9UV5J74ray4B3JjkQIMn8JM9Lcgjwo6r6H8DZwCtmq2Dt0IPA/X39uP+G3pGedt/dwPOSPDfJ/vS6S2ZyB103WJKXztD2KuCtSeYkGaH3zeq6bt419Lpzruza/V73bzM8Qh+CqnokyVeAB6rqMeBvk7wE+Fp3W/iHgF8DjqDXp/c48Cjwm7NVs2b0duBTSZ4FbATeMcv1NKGqHu1uG3IdvZv6fWvA5b6V5G3AXyV5ww6aXkKvG+UmoIAPVNVd3byrgF+qqvEkd9A7Sm8q0L30fwi6k6E3AKdU1W2zXY+kfZNdLrupO8kyDnzJMJc0mzxCl6RGeIQuSY0w0CWpEQa6JDXCQJekRhjoktSI/w/TIK6YopbnBgAAAABJRU5ErkJggg==", + "text/plain": [ + "
" + ] + }, + "metadata": { + "needs_background": "light", + "tags": [] + }, + "output_type": "display_data" + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "/content/data/mini_speech_commands/unknown/2313e093_nohash_0.wav\n" + ] + }, + { + "data": { + "image/png": "iVBORw0KGgoAAAANSUhEUgAAAXQAAAEICAYAAABPgw/pAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAALEgAACxIB0t1+/AAAADh0RVh0U29mdHdhcmUAbWF0cGxvdGxpYiB2ZXJzaW9uMy4yLjIsIGh0dHA6Ly9tYXRwbG90bGliLm9yZy+WH4yJAAAUd0lEQVR4nO3df7RdZX3n8feHxGAFV7FytZIEQyE6RseCvUa7BixF7ApSwapUGDsVrWbsNFNsrRqnlnFSO0WZZWdNTSvYYUGdkUAd0XRIh7Yqv6pALhTUgMg1gkkoGH6O6AgEvvPH2ZeeXO7NPUlOuOTJ+7XWWdk/nr339+xz7ufs8+yzd1JVSJL2fvvNdgGSpOEw0CWpEQa6JDXCQJekRhjoktQIA12SGmGga7ckOT/JR7vhY5Lcuovr+VSSPxhudTNu81eSbEryUJKjnsptDypJJTlituvQ3mHubBegPS/J7cDzgceAHwJ/A6yoqoeGuZ2qugp48QD1nA68q6qO7lv2PcOsZUD/hd5++OIwVpbkcuAjwLEAVfWRYaz36SrJ+cDl3eixVXX6rBUjwCP0fckbqupA4BXAKPDhyQ2S7Gsf8C8ENuzKgknmDLkWabcZ6PuYqtpC7wj9ZfDEV/rfSnIbcFs37ZeT3JjkgSRfTfLyieWTHJXkhiQ/SHIR8My+eccm2dw3vjDJ55NsTXJvkk8meQnwKeDnu66OB7q2T3TddOPvTjKe5L4ka5Mc0jevkrwnyW1djauTpJt3RJIrkjyY5J6uxu0k2T/JQ8Ac4KYk3+mmvyTJ5d06NyQ5qW+Z85P8eZJ1SX4I/OIg+zvJ6UmunjTtiW6Ubr2rk1za7dNrkxw+zbqO7rqIjh1gP+yX5MNJ7kjy/SR/meQnu3kXJHlfNzx/4j3QjR/e7fP9Jl7PJO/r1vFPSd4xyPPWLKkqH40/gNuB47vhhfSOSv+wGy/g74CfAn4COAr4PvAqeoH39m75/YF5wB3A7wDPAN4CPAp8tFvXscDmbngOcBPwJ8AB9IL/6G7e6cDVk2o8v289xwH30Ps2sT/wp8CVfW0L+N/AQcChwFZgWTfvQuD36R2sPLHNafZLAUd0w88AxoH/0D3P44AfAC/uq+9B4F9NrHvAfT/Vc+3f7vnAvcBSel2g/xNYM7ktsAzYBCwdcD+8s3s+PwMcCHwe+EzfvL/uhv818B3gor55X+x7PbcBq7r983rgR8BzZvs97WPqh0fo+44vdEfDVwNXAP+5b94fV9V9VfX/gOXAOVV1bVU9VlUXAA8Dr+4ezwD+a1U9WlWfA9ZPs72lwCHA+6vqh1X146q6epq2k70NOK+qbqiqh4EP0TuiX9TX5qyqeqCqvgd8BTiym/4ova6UQ3Zym6+mF3xnVdUjVfVlemF5Wl+bL1bVP1TV41X14wHXO4hLquq6qtpGL9CPnDT/FOAc4ISqum7SvOn2w9uAT1TVxuqdK/kQcGrXrXYFcHSS/YDXAB+n90EF8Avd/AmPAqu613sd8BADnCfR7DDQ9x1vrKqDquqFVfXvuvCesKlv+IXA+7qv8A90HwIL6YXzIcCWquq/o9sd02xvIXBHF1I765D+9XaBdC8wv6/NXX3DP6IXxgAfAAJc13WbvHMntrmpqh7vm3bHpG1uYs+Y7rlMeC9wcVV9cyeW3W4fdsNzgedX1XfonRw/EjiG3gfXnUlezJMD/d5Jr+FU9elpwkAX9L66T9gE/FEX/hOPZ1XVhcA/AfMn+mk7h06zzk3AodOcaJ3pFp930vtgASDJAcBzgS0zPpGqu6rq3VV1CPBvgT8b8Gd/dwILu6PWCYdO2uau3Jr0h8CzJkaS/PQurOMU4I1JztiJZbbbh/Seyzbg7m78CnpdZvOqd17lCnrda88BbtyFGvU0YKBrsk8D70nyqvQckOTEJM8GvkYvFH47yTOSvIle18pUrqP3AXBWt45nJpn4Wn83sCDJvGmWvRB4R5Ijk+xPr3vo2qq6fabik5ySZEE3ej+9EH58B4tMuJbe0ecHuud2LPAGYM0Ay+7ITcBLu+fyTHo/a9xZdwKvBc5I8psDLnMh8DtJDktyIL19eFHf0fYVwArgym788m786qp6bBdq1NOAga7tVNUY8G7gk/QCcZzeiT2q6hHgTd34fcBb6Z1sm2o9j9ELxCOA7wGbu/YAX6Z3YvauJPdMsezfA38A/C96HwqHA6cO+BReCVzb/YplLXBGVW2caaHuub0BOIHeCdk/A369qr414HanW++36Z1U/Ht6vyIatE9/8nq+Ry/UVyZ51wCLnAd8hl5gfxf4MfDv++ZfATybfw70q+l9k7gS7bWyfXeoJGlv5RG6JDXCQJekRhjoktSIgQI9ybIkt3aXYq+cps2vJrm5++3vZ4dbpiRpJjOeFE3vJkTfBl5H75cK64HTqurmvjaLgYuB46rq/iTPq6rv72i9Bx98cC1atGg3y5ekfcv1119/T1WNTDVvkLvrLQXGJ376lWQNcDJwc1+bdwOrq+p+gJnCHGDRokWMjY0NsHlJ0oQk012dPVCXy3y2v+R5M9tfDg3wIuBFSf4hyTVJlk1TyPIkY0nGtm7dOsCmJUmDGtZJ0bnAYnp3ZzsN+HSSgyY3qqpzq2q0qkZHRqb8xiBJ2kWDBPoWejdamrCAJ99TYzOwtrsj23fp9bkvHk6JkqRBDBLo64HF3T0h5tG7BHvtpDZfoPtvt5IcTK8LZsbLrSVJwzNjoHc381kBXAbcQu82nhuSrOr7H10uA+5NcjO9ezK/v6ru3VNFS5KebNbu5TI6Olr+ykWSdk6S66tqdKp5XikqSY0w0CWpEQa6JDVikCtFJe2DFq28dLZLaNbtZ524R9brEbokNcJAl6RGGOiS1AgDXZIaYaBLUiMMdElqhIEuSY0w0CWpEQa6JDXCQJekRhjoktQIA12SGmGgS1IjDHRJaoSBLkmNMNAlqREGuiQ1wkCXpEYY6JLUCANdkhphoEtSIwx0SWqEgS5JjRgo0JMsS3JrkvEkK6eYf3qSrUlu7B7vGn6pkqQdmTtTgyRzgNXA64DNwPoka6vq5klNL6qqFXugRknSAAY5Ql8KjFfVxqp6BFgDnLxny5Ik7axBAn0+sKlvfHM3bbI3J/l6ks8lWTjVipIsTzKWZGzr1q27UK4kaTrDOin618Ciqno58HfABVM1qqpzq2q0qkZHRkaGtGlJEgwW6FuA/iPuBd20J1TVvVX1cDf6F8DPDac8SdKgBgn09cDiJIclmQecCqztb5DkBX2jJwG3DK9ESdIgZvyVS1VtS7ICuAyYA5xXVRuSrALGqmot8NtJTgK2AfcBp+/BmiVJU5gx0AGqah2wbtK0M/uGPwR8aLilSZJ2hleKSlIjDHRJaoSBLkmNMNAlqREGuiQ1wkCXpEYY6JLUCANdkhphoEtSIwx0SWqEgS5JjTDQJakRBrokNcJAl6RGGOiS1AgDXZIaYaBLUiMMdElqhIEuSY0w0CWpEQa6JDXCQJekRhjoktQIA12SGmGgS1IjDHRJaoSBLkmNGCjQkyxLcmuS8SQrd9DuzUkqyejwSpQkDWLGQE8yB1gNnAAsAU5LsmSKds8GzgCuHXaRkqSZDXKEvhQYr6qNVfUIsAY4eYp2fwh8DPjxEOuTJA1okECfD2zqG9/cTXtCklcAC6vq0h2tKMnyJGNJxrZu3brTxUqSprfbJ0WT7Ad8AnjfTG2r6tyqGq2q0ZGRkd3dtCSpzyCBvgVY2De+oJs24dnAy4DLk9wOvBpY64lRSXpqDRLo64HFSQ5LMg84FVg7MbOqHqyqg6tqUVUtAq4BTqqqsT1SsSRpSjMGelVtA1YAlwG3ABdX1YYkq5KctKcLlCQNZu4gjapqHbBu0rQzp2l77O6XJUnaWV4pKkmNMNAlqREGuiQ1wkCXpEYY6JLUCANdkhphoEtSIwx0SWqEgS5JjTDQJakRBrokNcJAl6RGGOiS1AgDXZIaYaBLUiMMdElqhIEuSY0w0CWpEQa6JDXCQJekRhjoktQIA12SGmGgS1IjDHRJaoSBLkmNMNAlqREGuiQ1YqBAT7Isya1JxpOsnGL+e5J8I8mNSa5OsmT4pUqSdmTGQE8yB1gNnAAsAU6bIrA/W1X/sqqOBD4OfGLolUqSdmiQI/SlwHhVbayqR4A1wMn9Darq//aNHgDU8EqUJA1i7gBt5gOb+sY3A6+a3CjJbwG/C8wDjptqRUmWA8sBDj300J2tVZK0A0M7KVpVq6vqcOCDwIenaXNuVY1W1ejIyMiwNi1JYrBA3wIs7Btf0E2bzhrgjbtTlCRp5w0S6OuBxUkOSzIPOBVY298gyeK+0ROB24ZXoiRpEDP2oVfVtiQrgMuAOcB5VbUhySpgrKrWAiuSHA88CtwPvH1PFi1JerJBTopSVeuAdZOmndk3fMaQ65Ik7SSvFJWkRhjoktQIA12SGmGgS1IjDHRJaoSBLkmNMNAlqREGuiQ1wkCXpEYY6JLUCANdkhphoEtSIwx0SWqEgS5JjTDQJakRBrokNcJAl6RGGOiS1AgDXZIaYaBLUiMMdElqhIEuSY0w0CWpEQa6JDXCQJekRhjoktSIubNdwK5YtPLS2S6hWbefdeIeWa+v2Z6zp14z7X0GOkJPsizJrUnGk6ycYv7vJrk5ydeTfCnJC4dfqiRpR2YM9CRzgNXACcAS4LQkSyY1+0dgtKpeDnwO+PiwC5Uk7dggR+hLgfGq2lhVjwBrgJP7G1TVV6rqR93oNcCC4ZYpSZrJIIE+H9jUN765mzad3wD+ZqoZSZYnGUsytnXr1sGrlCTNaKi/cknya8AocPZU86vq3KoararRkZGRYW5akvZ5g/zKZQuwsG98QTdtO0mOB34f+IWqeng45UmSBjXIEfp6YHGSw5LMA04F1vY3SHIUcA5wUlV9f/hlSpJmMmOgV9U2YAVwGXALcHFVbUiyKslJXbOzgQOBv0pyY5K106xOkrSHDHRhUVWtA9ZNmnZm3/DxQ65LkrSTvPRfkhphoEtSIwx0SWqEgS5JjTDQJakRBrokNcJAl6RGGOiS1AgDXZIaYaBLUiMMdElqhIEuSY0w0CWpEQa6JDXCQJekRhjoktQIA12SGmGgS1IjDHRJaoSBLkmNMNAlqREGuiQ1wkCXpEYY6JLUCANdkhphoEtSIwx0SWrEQIGeZFmSW5OMJ1k5xfzXJLkhybYkbxl+mZKkmcwY6EnmAKuBE4AlwGlJlkxq9j3gdOCzwy5QkjSYuQO0WQqMV9VGgCRrgJOBmycaVNXt3bzH90CNkqQBDNLlMh/Y1De+uZu205IsTzKWZGzr1q27sgpJ0jSe0pOiVXVuVY1W1ejIyMhTuWlJat4ggb4FWNg3vqCbJkl6Ghkk0NcDi5MclmQecCqwds+WJUnaWTMGelVtA1YAlwG3ABdX1YYkq5KcBJDklUk2A6cA5yTZsCeLliQ92SC/cqGq1gHrJk07s294Pb2uGEnSLPFKUUlqhIEuSY0w0CWpEQa6JDXCQJekRhjoktQIA12SGmGgS1IjDHRJaoSBLkmNMNAlqREGuiQ1wkCXpEYY6JLUCANdkhphoEtSIwx0SWqEgS5JjTDQJakRBrokNcJAl6RGGOiS1AgDXZIaYaBLUiMMdElqhIEuSY0w0CWpEQMFepJlSW5NMp5k5RTz909yUTf/2iSLhl2oJGnHZgz0JHOA1cAJwBLgtCRLJjX7DeD+qjoC+BPgY8MuVJK0Y4McoS8FxqtqY1U9AqwBTp7U5mTggm74c8Brk2R4ZUqSZjJ3gDbzgU1945uBV03Xpqq2JXkQeC5wT3+jJMuB5d3oQ0lu3ZWi90IHM2lfPF3F71awF71e4GvW2ZdesxdON2OQQB+aqjoXOPep3ObTQZKxqhqd7To0GF+vvY+vWc8gXS5bgIV94wu6aVO2STIX+Eng3mEUKEkazCCBvh5YnOSwJPOAU4G1k9qsBd7eDb8F+HJV1fDKlCTNZMYul65PfAVwGTAHOK+qNiRZBYxV1VrgvwOfSTIO3Ecv9PXP9rlupr2cr9fex9cMiAfSktQGrxSVpEYY6JLUCANd0tNWktOTfHK269hbGOiS1AgDfTclWZXkvX3jf5TkjCTvT7I+ydeT/Kdu3gFJLk1yU5JvJnnr7FUugCSLktyS5NNJNiT52yQ/keTIJNd0r98lSZ4z27W2oNvf3+wb/70kH0lyeZKPJbkuybeTHDPFsicm+VqSg5Ocn+S/Jflqko1J3tK1SZKzu7+vb0z8jSVZneSkbviSJOd1w+/s/manfB88NXtleAz03Xce8OsASfaj95PNu4DF9O6DcyTwc0leAywD7qyqn62qlwH/Z3ZK1iSLgdVV9VLgAeDNwF8CH6yqlwPfAP7jLNa3r5hbVUuB9zJpfyf5FWAl8PqqmrjE/wXA0cAvA2d1095E72/uZ4HjgbOTvAC4Cpj4kJhP70aDdNOu7Ianeh/sVQz03VRVtwP3JjkK+CXgH4FX9g3fAPwLem+WbwCv645EjqmqB2enak3y3aq6sRu+HjgcOKiqruimXQC8ZlYq27d8vvv3emBR3/TjgA8CJ1bV/X3Tv1BVj1fVzcDzu2lHAxdW1WNVdTdwBb2/x6uAY7o7xd4M3N0F/c8DX+2Wnfw+6K9hr/CU3sulYX8BnA78NL0j9tcCf1xV50xumOQVwOuBjyb5UlWteioL1ZQe7ht+DDhotgrZB2xj+wPJZ/YNT7wOj7F9Nn0H+BngRcDYFO0Bdnh316rakuQget+SrwR+CvhV4KGq+kGS5/Lk94FdLvuoS+i9UV5J74ray4B3JjkQIMn8JM9Lcgjwo6r6H8DZwCtmq2Dt0IPA/X39uP+G3pGedt/dwPOSPDfJ/vS6S2ZyB103WJKXztD2KuCtSeYkGaH3zeq6bt419Lpzruza/V73bzM8Qh+CqnokyVeAB6rqMeBvk7wE+Fp3W/iHgF8DjqDXp/c48Cjwm7NVs2b0duBTSZ4FbATeMcv1NKGqHu1uG3IdvZv6fWvA5b6V5G3AXyV5ww6aXkKvG+UmoIAPVNVd3byrgF+qqvEkd9A7Sm8q0L30fwi6k6E3AKdU1W2zXY+kfZNdLrupO8kyDnzJMJc0mzxCl6RGeIQuSY0w0CWpEQa6JDXCQJekRhjoktSI/w/TIK6YopbnBgAAAABJRU5ErkJggg==", + "text/plain": [ + "
" + ] + }, + "metadata": { + "needs_background": "light", + "tags": [] + }, + "output_type": "display_data" + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "/content/data/mini_speech_commands/unknown/763188c4_nohash_1.wav\n" + ] + }, + { + "data": { + "image/png": "iVBORw0KGgoAAAANSUhEUgAAAXQAAAEICAYAAABPgw/pAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAALEgAACxIB0t1+/AAAADh0RVh0U29mdHdhcmUAbWF0cGxvdGxpYiB2ZXJzaW9uMy4yLjIsIGh0dHA6Ly9tYXRwbG90bGliLm9yZy+WH4yJAAAUd0lEQVR4nO3df7RdZX3n8feHxGAFV7FytZIEQyE6RseCvUa7BixF7ApSwapUGDsVrWbsNFNsrRqnlnFSO0WZZWdNTSvYYUGdkUAd0XRIh7Yqv6pALhTUgMg1gkkoGH6O6AgEvvPH2ZeeXO7NPUlOuOTJ+7XWWdk/nr339+xz7ufs8+yzd1JVSJL2fvvNdgGSpOEw0CWpEQa6JDXCQJekRhjoktQIA12SGmGga7ckOT/JR7vhY5Lcuovr+VSSPxhudTNu81eSbEryUJKjnsptDypJJTlituvQ3mHubBegPS/J7cDzgceAHwJ/A6yoqoeGuZ2qugp48QD1nA68q6qO7lv2PcOsZUD/hd5++OIwVpbkcuAjwLEAVfWRYaz36SrJ+cDl3eixVXX6rBUjwCP0fckbqupA4BXAKPDhyQ2S7Gsf8C8ENuzKgknmDLkWabcZ6PuYqtpC7wj9ZfDEV/rfSnIbcFs37ZeT3JjkgSRfTfLyieWTHJXkhiQ/SHIR8My+eccm2dw3vjDJ55NsTXJvkk8meQnwKeDnu66OB7q2T3TddOPvTjKe5L4ka5Mc0jevkrwnyW1djauTpJt3RJIrkjyY5J6uxu0k2T/JQ8Ac4KYk3+mmvyTJ5d06NyQ5qW+Z85P8eZJ1SX4I/OIg+zvJ6UmunjTtiW6Ubr2rk1za7dNrkxw+zbqO7rqIjh1gP+yX5MNJ7kjy/SR/meQnu3kXJHlfNzx/4j3QjR/e7fP9Jl7PJO/r1vFPSd4xyPPWLKkqH40/gNuB47vhhfSOSv+wGy/g74CfAn4COAr4PvAqeoH39m75/YF5wB3A7wDPAN4CPAp8tFvXscDmbngOcBPwJ8AB9IL/6G7e6cDVk2o8v289xwH30Ps2sT/wp8CVfW0L+N/AQcChwFZgWTfvQuD36R2sPLHNafZLAUd0w88AxoH/0D3P44AfAC/uq+9B4F9NrHvAfT/Vc+3f7vnAvcBSel2g/xNYM7ktsAzYBCwdcD+8s3s+PwMcCHwe+EzfvL/uhv818B3gor55X+x7PbcBq7r983rgR8BzZvs97WPqh0fo+44vdEfDVwNXAP+5b94fV9V9VfX/gOXAOVV1bVU9VlUXAA8Dr+4ezwD+a1U9WlWfA9ZPs72lwCHA+6vqh1X146q6epq2k70NOK+qbqiqh4EP0TuiX9TX5qyqeqCqvgd8BTiym/4ova6UQ3Zym6+mF3xnVdUjVfVlemF5Wl+bL1bVP1TV41X14wHXO4hLquq6qtpGL9CPnDT/FOAc4ISqum7SvOn2w9uAT1TVxuqdK/kQcGrXrXYFcHSS/YDXAB+n90EF8Avd/AmPAqu613sd8BADnCfR7DDQ9x1vrKqDquqFVfXvuvCesKlv+IXA+7qv8A90HwIL6YXzIcCWquq/o9sd02xvIXBHF1I765D+9XaBdC8wv6/NXX3DP6IXxgAfAAJc13WbvHMntrmpqh7vm3bHpG1uYs+Y7rlMeC9wcVV9cyeW3W4fdsNzgedX1XfonRw/EjiG3gfXnUlezJMD/d5Jr+FU9elpwkAX9L66T9gE/FEX/hOPZ1XVhcA/AfMn+mk7h06zzk3AodOcaJ3pFp930vtgASDJAcBzgS0zPpGqu6rq3VV1CPBvgT8b8Gd/dwILu6PWCYdO2uau3Jr0h8CzJkaS/PQurOMU4I1JztiJZbbbh/Seyzbg7m78CnpdZvOqd17lCnrda88BbtyFGvU0YKBrsk8D70nyqvQckOTEJM8GvkYvFH47yTOSvIle18pUrqP3AXBWt45nJpn4Wn83sCDJvGmWvRB4R5Ijk+xPr3vo2qq6fabik5ySZEE3ej+9EH58B4tMuJbe0ecHuud2LPAGYM0Ay+7ITcBLu+fyTHo/a9xZdwKvBc5I8psDLnMh8DtJDktyIL19eFHf0fYVwArgym788m786qp6bBdq1NOAga7tVNUY8G7gk/QCcZzeiT2q6hHgTd34fcBb6Z1sm2o9j9ELxCOA7wGbu/YAX6Z3YvauJPdMsezfA38A/C96HwqHA6cO+BReCVzb/YplLXBGVW2caaHuub0BOIHeCdk/A369qr414HanW++36Z1U/Ht6vyIatE9/8nq+Ry/UVyZ51wCLnAd8hl5gfxf4MfDv++ZfATybfw70q+l9k7gS7bWyfXeoJGlv5RG6JDXCQJekRhjoktSIgQI9ybIkt3aXYq+cps2vJrm5++3vZ4dbpiRpJjOeFE3vJkTfBl5H75cK64HTqurmvjaLgYuB46rq/iTPq6rv72i9Bx98cC1atGg3y5ekfcv1119/T1WNTDVvkLvrLQXGJ376lWQNcDJwc1+bdwOrq+p+gJnCHGDRokWMjY0NsHlJ0oQk012dPVCXy3y2v+R5M9tfDg3wIuBFSf4hyTVJlk1TyPIkY0nGtm7dOsCmJUmDGtZJ0bnAYnp3ZzsN+HSSgyY3qqpzq2q0qkZHRqb8xiBJ2kWDBPoWejdamrCAJ99TYzOwtrsj23fp9bkvHk6JkqRBDBLo64HF3T0h5tG7BHvtpDZfoPtvt5IcTK8LZsbLrSVJwzNjoHc381kBXAbcQu82nhuSrOr7H10uA+5NcjO9ezK/v6ru3VNFS5KebNbu5TI6Olr+ykWSdk6S66tqdKp5XikqSY0w0CWpEQa6JDVikCtFJe2DFq28dLZLaNbtZ524R9brEbokNcJAl6RGGOiS1AgDXZIaYaBLUiMMdElqhIEuSY0w0CWpEQa6JDXCQJekRhjoktQIA12SGmGgS1IjDHRJaoSBLkmNMNAlqREGuiQ1wkCXpEYY6JLUCANdkhphoEtSIwx0SWqEgS5JjRgo0JMsS3JrkvEkK6eYf3qSrUlu7B7vGn6pkqQdmTtTgyRzgNXA64DNwPoka6vq5klNL6qqFXugRknSAAY5Ql8KjFfVxqp6BFgDnLxny5Ik7axBAn0+sKlvfHM3bbI3J/l6ks8lWTjVipIsTzKWZGzr1q27UK4kaTrDOin618Ciqno58HfABVM1qqpzq2q0qkZHRkaGtGlJEgwW6FuA/iPuBd20J1TVvVX1cDf6F8DPDac8SdKgBgn09cDiJIclmQecCqztb5DkBX2jJwG3DK9ESdIgZvyVS1VtS7ICuAyYA5xXVRuSrALGqmot8NtJTgK2AfcBp+/BmiVJU5gx0AGqah2wbtK0M/uGPwR8aLilSZJ2hleKSlIjDHRJaoSBLkmNMNAlqREGuiQ1wkCXpEYY6JLUCANdkhphoEtSIwx0SWqEgS5JjTDQJakRBrokNcJAl6RGGOiS1AgDXZIaYaBLUiMMdElqhIEuSY0w0CWpEQa6JDXCQJekRhjoktQIA12SGmGgS1IjDHRJaoSBLkmNGCjQkyxLcmuS8SQrd9DuzUkqyejwSpQkDWLGQE8yB1gNnAAsAU5LsmSKds8GzgCuHXaRkqSZDXKEvhQYr6qNVfUIsAY4eYp2fwh8DPjxEOuTJA1okECfD2zqG9/cTXtCklcAC6vq0h2tKMnyJGNJxrZu3brTxUqSprfbJ0WT7Ad8AnjfTG2r6tyqGq2q0ZGRkd3dtCSpzyCBvgVY2De+oJs24dnAy4DLk9wOvBpY64lRSXpqDRLo64HFSQ5LMg84FVg7MbOqHqyqg6tqUVUtAq4BTqqqsT1SsSRpSjMGelVtA1YAlwG3ABdX1YYkq5KctKcLlCQNZu4gjapqHbBu0rQzp2l77O6XJUnaWV4pKkmNMNAlqREGuiQ1wkCXpEYY6JLUCANdkhphoEtSIwx0SWqEgS5JjTDQJakRBrokNcJAl6RGGOiS1AgDXZIaYaBLUiMMdElqhIEuSY0w0CWpEQa6JDXCQJekRhjoktQIA12SGmGgS1IjDHRJaoSBLkmNMNAlqREGuiQ1YqBAT7Isya1JxpOsnGL+e5J8I8mNSa5OsmT4pUqSdmTGQE8yB1gNnAAsAU6bIrA/W1X/sqqOBD4OfGLolUqSdmiQI/SlwHhVbayqR4A1wMn9Darq//aNHgDU8EqUJA1i7gBt5gOb+sY3A6+a3CjJbwG/C8wDjptqRUmWA8sBDj300J2tVZK0A0M7KVpVq6vqcOCDwIenaXNuVY1W1ejIyMiwNi1JYrBA3wIs7Btf0E2bzhrgjbtTlCRp5w0S6OuBxUkOSzIPOBVY298gyeK+0ROB24ZXoiRpEDP2oVfVtiQrgMuAOcB5VbUhySpgrKrWAiuSHA88CtwPvH1PFi1JerJBTopSVeuAdZOmndk3fMaQ65Ik7SSvFJWkRhjoktQIA12SGmGgS1IjDHRJaoSBLkmNMNAlqREGuiQ1wkCXpEYY6JLUCANdkhphoEtSIwx0SWqEgS5JjTDQJakRBrokNcJAl6RGGOiS1AgDXZIaYaBLUiMMdElqhIEuSY0w0CWpEQa6JDXCQJekRhjoktSIubNdwK5YtPLS2S6hWbefdeIeWa+v2Z6zp14z7X0GOkJPsizJrUnGk6ycYv7vJrk5ydeTfCnJC4dfqiRpR2YM9CRzgNXACcAS4LQkSyY1+0dgtKpeDnwO+PiwC5Uk7dggR+hLgfGq2lhVjwBrgJP7G1TVV6rqR93oNcCC4ZYpSZrJIIE+H9jUN765mzad3wD+ZqoZSZYnGUsytnXr1sGrlCTNaKi/cknya8AocPZU86vq3KoararRkZGRYW5akvZ5g/zKZQuwsG98QTdtO0mOB34f+IWqeng45UmSBjXIEfp6YHGSw5LMA04F1vY3SHIUcA5wUlV9f/hlSpJmMmOgV9U2YAVwGXALcHFVbUiyKslJXbOzgQOBv0pyY5K106xOkrSHDHRhUVWtA9ZNmnZm3/DxQ65LkrSTvPRfkhphoEtSIwx0SWqEgS5JjTDQJakRBrokNcJAl6RGGOiS1AgDXZIaYaBLUiMMdElqhIEuSY0w0CWpEQa6JDXCQJekRhjoktQIA12SGmGgS1IjDHRJaoSBLkmNMNAlqREGuiQ1wkCXpEYY6JLUCANdkhphoEtSIwx0SWrEQIGeZFmSW5OMJ1k5xfzXJLkhybYkbxl+mZKkmcwY6EnmAKuBE4AlwGlJlkxq9j3gdOCzwy5QkjSYuQO0WQqMV9VGgCRrgJOBmycaVNXt3bzH90CNkqQBDNLlMh/Y1De+uZu205IsTzKWZGzr1q27sgpJ0jSe0pOiVXVuVY1W1ejIyMhTuWlJat4ggb4FWNg3vqCbJkl6Ghkk0NcDi5MclmQecCqwds+WJUnaWTMGelVtA1YAlwG3ABdX1YYkq5KcBJDklUk2A6cA5yTZsCeLliQ92SC/cqGq1gHrJk07s294Pb2uGEnSLPFKUUlqhIEuSY0w0CWpEQa6JDXCQJekRhjoktQIA12SGmGgS1IjDHRJaoSBLkmNMNAlqREGuiQ1wkCXpEYY6JLUCANdkhphoEtSIwx0SWqEgS5JjTDQJakRBrokNcJAl6RGGOiS1AgDXZIaYaBLUiMMdElqhIEuSY0w0CWpEQMFepJlSW5NMp5k5RTz909yUTf/2iSLhl2oJGnHZgz0JHOA1cAJwBLgtCRLJjX7DeD+qjoC+BPgY8MuVJK0Y4McoS8FxqtqY1U9AqwBTp7U5mTggm74c8Brk2R4ZUqSZjJ3gDbzgU1945uBV03Xpqq2JXkQeC5wT3+jJMuB5d3oQ0lu3ZWi90IHM2lfPF3F71awF71e4GvW2ZdesxdON2OQQB+aqjoXOPep3ObTQZKxqhqd7To0GF+vvY+vWc8gXS5bgIV94wu6aVO2STIX+Eng3mEUKEkazCCBvh5YnOSwJPOAU4G1k9qsBd7eDb8F+HJV1fDKlCTNZMYul65PfAVwGTAHOK+qNiRZBYxV1VrgvwOfSTIO3Ecv9PXP9rlupr2cr9fex9cMiAfSktQGrxSVpEYY6JLUCANd0tNWktOTfHK269hbGOiS1AgDfTclWZXkvX3jf5TkjCTvT7I+ydeT/Kdu3gFJLk1yU5JvJnnr7FUugCSLktyS5NNJNiT52yQ/keTIJNd0r98lSZ4z27W2oNvf3+wb/70kH0lyeZKPJbkuybeTHDPFsicm+VqSg5Ocn+S/Jflqko1J3tK1SZKzu7+vb0z8jSVZneSkbviSJOd1w+/s/manfB88NXtleAz03Xce8OsASfaj95PNu4DF9O6DcyTwc0leAywD7qyqn62qlwH/Z3ZK1iSLgdVV9VLgAeDNwF8CH6yqlwPfAP7jLNa3r5hbVUuB9zJpfyf5FWAl8PqqmrjE/wXA0cAvA2d1095E72/uZ4HjgbOTvAC4Cpj4kJhP70aDdNOu7Ianeh/sVQz03VRVtwP3JjkK+CXgH4FX9g3fAPwLem+WbwCv645EjqmqB2enak3y3aq6sRu+HjgcOKiqruimXQC8ZlYq27d8vvv3emBR3/TjgA8CJ1bV/X3Tv1BVj1fVzcDzu2lHAxdW1WNVdTdwBb2/x6uAY7o7xd4M3N0F/c8DX+2Wnfw+6K9hr/CU3sulYX8BnA78NL0j9tcCf1xV50xumOQVwOuBjyb5UlWteioL1ZQe7ht+DDhotgrZB2xj+wPJZ/YNT7wOj7F9Nn0H+BngRcDYFO0Bdnh316rakuQget+SrwR+CvhV4KGq+kGS5/Lk94FdLvuoS+i9UV5J74ray4B3JjkQIMn8JM9Lcgjwo6r6H8DZwCtmq2Dt0IPA/X39uP+G3pGedt/dwPOSPDfJ/vS6S2ZyB103WJKXztD2KuCtSeYkGaH3zeq6bt419Lpzruza/V73bzM8Qh+CqnokyVeAB6rqMeBvk7wE+Fp3W/iHgF8DjqDXp/c48Cjwm7NVs2b0duBTSZ4FbATeMcv1NKGqHu1uG3IdvZv6fWvA5b6V5G3AXyV5ww6aXkKvG+UmoIAPVNVd3byrgF+qqvEkd9A7Sm8q0L30fwi6k6E3AKdU1W2zXY+kfZNdLrupO8kyDnzJMJc0mzxCl6RGeIQuSY0w0CWpEQa6JDXCQJekRhjoktSI/w/TIK6YopbnBgAAAABJRU5ErkJggg==", + "text/plain": [ + "
" + ] + }, + "metadata": { + "needs_background": "light", + "tags": [] + }, + "output_type": "display_data" + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "/content/data/mini_speech_commands/unknown/7ea032f3_nohash_3.wav\n" + ] + }, + { + "data": { + "image/png": "iVBORw0KGgoAAAANSUhEUgAAAXQAAAEICAYAAABPgw/pAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAALEgAACxIB0t1+/AAAADh0RVh0U29mdHdhcmUAbWF0cGxvdGxpYiB2ZXJzaW9uMy4yLjIsIGh0dHA6Ly9tYXRwbG90bGliLm9yZy+WH4yJAAAUd0lEQVR4nO3df7RdZX3n8feHxGAFV7FytZIEQyE6RseCvUa7BixF7ApSwapUGDsVrWbsNFNsrRqnlnFSO0WZZWdNTSvYYUGdkUAd0XRIh7Yqv6pALhTUgMg1gkkoGH6O6AgEvvPH2ZeeXO7NPUlOuOTJ+7XWWdk/nr339+xz7ufs8+yzd1JVSJL2fvvNdgGSpOEw0CWpEQa6JDXCQJekRhjoktQIA12SGmGga7ckOT/JR7vhY5Lcuovr+VSSPxhudTNu81eSbEryUJKjnsptDypJJTlituvQ3mHubBegPS/J7cDzgceAHwJ/A6yoqoeGuZ2qugp48QD1nA68q6qO7lv2PcOsZUD/hd5++OIwVpbkcuAjwLEAVfWRYaz36SrJ+cDl3eixVXX6rBUjwCP0fckbqupA4BXAKPDhyQ2S7Gsf8C8ENuzKgknmDLkWabcZ6PuYqtpC7wj9ZfDEV/rfSnIbcFs37ZeT3JjkgSRfTfLyieWTHJXkhiQ/SHIR8My+eccm2dw3vjDJ55NsTXJvkk8meQnwKeDnu66OB7q2T3TddOPvTjKe5L4ka5Mc0jevkrwnyW1djauTpJt3RJIrkjyY5J6uxu0k2T/JQ8Ac4KYk3+mmvyTJ5d06NyQ5qW+Z85P8eZJ1SX4I/OIg+zvJ6UmunjTtiW6Ubr2rk1za7dNrkxw+zbqO7rqIjh1gP+yX5MNJ7kjy/SR/meQnu3kXJHlfNzx/4j3QjR/e7fP9Jl7PJO/r1vFPSd4xyPPWLKkqH40/gNuB47vhhfSOSv+wGy/g74CfAn4COAr4PvAqeoH39m75/YF5wB3A7wDPAN4CPAp8tFvXscDmbngOcBPwJ8AB9IL/6G7e6cDVk2o8v289xwH30Ps2sT/wp8CVfW0L+N/AQcChwFZgWTfvQuD36R2sPLHNafZLAUd0w88AxoH/0D3P44AfAC/uq+9B4F9NrHvAfT/Vc+3f7vnAvcBSel2g/xNYM7ktsAzYBCwdcD+8s3s+PwMcCHwe+EzfvL/uhv818B3gor55X+x7PbcBq7r983rgR8BzZvs97WPqh0fo+44vdEfDVwNXAP+5b94fV9V9VfX/gOXAOVV1bVU9VlUXAA8Dr+4ezwD+a1U9WlWfA9ZPs72lwCHA+6vqh1X146q6epq2k70NOK+qbqiqh4EP0TuiX9TX5qyqeqCqvgd8BTiym/4ova6UQ3Zym6+mF3xnVdUjVfVlemF5Wl+bL1bVP1TV41X14wHXO4hLquq6qtpGL9CPnDT/FOAc4ISqum7SvOn2w9uAT1TVxuqdK/kQcGrXrXYFcHSS/YDXAB+n90EF8Avd/AmPAqu613sd8BADnCfR7DDQ9x1vrKqDquqFVfXvuvCesKlv+IXA+7qv8A90HwIL6YXzIcCWquq/o9sd02xvIXBHF1I765D+9XaBdC8wv6/NXX3DP6IXxgAfAAJc13WbvHMntrmpqh7vm3bHpG1uYs+Y7rlMeC9wcVV9cyeW3W4fdsNzgedX1XfonRw/EjiG3gfXnUlezJMD/d5Jr+FU9elpwkAX9L66T9gE/FEX/hOPZ1XVhcA/AfMn+mk7h06zzk3AodOcaJ3pFp930vtgASDJAcBzgS0zPpGqu6rq3VV1CPBvgT8b8Gd/dwILu6PWCYdO2uau3Jr0h8CzJkaS/PQurOMU4I1JztiJZbbbh/Seyzbg7m78CnpdZvOqd17lCnrda88BbtyFGvU0YKBrsk8D70nyqvQckOTEJM8GvkYvFH47yTOSvIle18pUrqP3AXBWt45nJpn4Wn83sCDJvGmWvRB4R5Ijk+xPr3vo2qq6fabik5ySZEE3ej+9EH58B4tMuJbe0ecHuud2LPAGYM0Ay+7ITcBLu+fyTHo/a9xZdwKvBc5I8psDLnMh8DtJDktyIL19eFHf0fYVwArgym788m786qp6bBdq1NOAga7tVNUY8G7gk/QCcZzeiT2q6hHgTd34fcBb6Z1sm2o9j9ELxCOA7wGbu/YAX6Z3YvauJPdMsezfA38A/C96HwqHA6cO+BReCVzb/YplLXBGVW2caaHuub0BOIHeCdk/A369qr414HanW++36Z1U/Ht6vyIatE9/8nq+Ry/UVyZ51wCLnAd8hl5gfxf4MfDv++ZfATybfw70q+l9k7gS7bWyfXeoJGlv5RG6JDXCQJekRhjoktSIgQI9ybIkt3aXYq+cps2vJrm5++3vZ4dbpiRpJjOeFE3vJkTfBl5H75cK64HTqurmvjaLgYuB46rq/iTPq6rv72i9Bx98cC1atGg3y5ekfcv1119/T1WNTDVvkLvrLQXGJ376lWQNcDJwc1+bdwOrq+p+gJnCHGDRokWMjY0NsHlJ0oQk012dPVCXy3y2v+R5M9tfDg3wIuBFSf4hyTVJlk1TyPIkY0nGtm7dOsCmJUmDGtZJ0bnAYnp3ZzsN+HSSgyY3qqpzq2q0qkZHRqb8xiBJ2kWDBPoWejdamrCAJ99TYzOwtrsj23fp9bkvHk6JkqRBDBLo64HF3T0h5tG7BHvtpDZfoPtvt5IcTK8LZsbLrSVJwzNjoHc381kBXAbcQu82nhuSrOr7H10uA+5NcjO9ezK/v6ru3VNFS5KebNbu5TI6Olr+ykWSdk6S66tqdKp5XikqSY0w0CWpEQa6JDVikCtFJe2DFq28dLZLaNbtZ524R9brEbokNcJAl6RGGOiS1AgDXZIaYaBLUiMMdElqhIEuSY0w0CWpEQa6JDXCQJekRhjoktQIA12SGmGgS1IjDHRJaoSBLkmNMNAlqREGuiQ1wkCXpEYY6JLUCANdkhphoEtSIwx0SWqEgS5JjRgo0JMsS3JrkvEkK6eYf3qSrUlu7B7vGn6pkqQdmTtTgyRzgNXA64DNwPoka6vq5klNL6qqFXugRknSAAY5Ql8KjFfVxqp6BFgDnLxny5Ik7axBAn0+sKlvfHM3bbI3J/l6ks8lWTjVipIsTzKWZGzr1q27UK4kaTrDOin618Ciqno58HfABVM1qqpzq2q0qkZHRkaGtGlJEgwW6FuA/iPuBd20J1TVvVX1cDf6F8DPDac8SdKgBgn09cDiJIclmQecCqztb5DkBX2jJwG3DK9ESdIgZvyVS1VtS7ICuAyYA5xXVRuSrALGqmot8NtJTgK2AfcBp+/BmiVJU5gx0AGqah2wbtK0M/uGPwR8aLilSZJ2hleKSlIjDHRJaoSBLkmNMNAlqREGuiQ1wkCXpEYY6JLUCANdkhphoEtSIwx0SWqEgS5JjTDQJakRBrokNcJAl6RGGOiS1AgDXZIaYaBLUiMMdElqhIEuSY0w0CWpEQa6JDXCQJekRhjoktQIA12SGmGgS1IjDHRJaoSBLkmNGCjQkyxLcmuS8SQrd9DuzUkqyejwSpQkDWLGQE8yB1gNnAAsAU5LsmSKds8GzgCuHXaRkqSZDXKEvhQYr6qNVfUIsAY4eYp2fwh8DPjxEOuTJA1okECfD2zqG9/cTXtCklcAC6vq0h2tKMnyJGNJxrZu3brTxUqSprfbJ0WT7Ad8AnjfTG2r6tyqGq2q0ZGRkd3dtCSpzyCBvgVY2De+oJs24dnAy4DLk9wOvBpY64lRSXpqDRLo64HFSQ5LMg84FVg7MbOqHqyqg6tqUVUtAq4BTqqqsT1SsSRpSjMGelVtA1YAlwG3ABdX1YYkq5KctKcLlCQNZu4gjapqHbBu0rQzp2l77O6XJUnaWV4pKkmNMNAlqREGuiQ1wkCXpEYY6JLUCANdkhphoEtSIwx0SWqEgS5JjTDQJakRBrokNcJAl6RGGOiS1AgDXZIaYaBLUiMMdElqhIEuSY0w0CWpEQa6JDXCQJekRhjoktQIA12SGmGgS1IjDHRJaoSBLkmNMNAlqREGuiQ1YqBAT7Isya1JxpOsnGL+e5J8I8mNSa5OsmT4pUqSdmTGQE8yB1gNnAAsAU6bIrA/W1X/sqqOBD4OfGLolUqSdmiQI/SlwHhVbayqR4A1wMn9Darq//aNHgDU8EqUJA1i7gBt5gOb+sY3A6+a3CjJbwG/C8wDjptqRUmWA8sBDj300J2tVZK0A0M7KVpVq6vqcOCDwIenaXNuVY1W1ejIyMiwNi1JYrBA3wIs7Btf0E2bzhrgjbtTlCRp5w0S6OuBxUkOSzIPOBVY298gyeK+0ROB24ZXoiRpEDP2oVfVtiQrgMuAOcB5VbUhySpgrKrWAiuSHA88CtwPvH1PFi1JerJBTopSVeuAdZOmndk3fMaQ65Ik7SSvFJWkRhjoktQIA12SGmGgS1IjDHRJaoSBLkmNMNAlqREGuiQ1wkCXpEYY6JLUCANdkhphoEtSIwx0SWqEgS5JjTDQJakRBrokNcJAl6RGGOiS1AgDXZIaYaBLUiMMdElqhIEuSY0w0CWpEQa6JDXCQJekRhjoktSIubNdwK5YtPLS2S6hWbefdeIeWa+v2Z6zp14z7X0GOkJPsizJrUnGk6ycYv7vJrk5ydeTfCnJC4dfqiRpR2YM9CRzgNXACcAS4LQkSyY1+0dgtKpeDnwO+PiwC5Uk7dggR+hLgfGq2lhVjwBrgJP7G1TVV6rqR93oNcCC4ZYpSZrJIIE+H9jUN765mzad3wD+ZqoZSZYnGUsytnXr1sGrlCTNaKi/cknya8AocPZU86vq3KoararRkZGRYW5akvZ5g/zKZQuwsG98QTdtO0mOB34f+IWqeng45UmSBjXIEfp6YHGSw5LMA04F1vY3SHIUcA5wUlV9f/hlSpJmMmOgV9U2YAVwGXALcHFVbUiyKslJXbOzgQOBv0pyY5K106xOkrSHDHRhUVWtA9ZNmnZm3/DxQ65LkrSTvPRfkhphoEtSIwx0SWqEgS5JjTDQJakRBrokNcJAl6RGGOiS1AgDXZIaYaBLUiMMdElqhIEuSY0w0CWpEQa6JDXCQJekRhjoktQIA12SGmGgS1IjDHRJaoSBLkmNMNAlqREGuiQ1wkCXpEYY6JLUCANdkhphoEtSIwx0SWrEQIGeZFmSW5OMJ1k5xfzXJLkhybYkbxl+mZKkmcwY6EnmAKuBE4AlwGlJlkxq9j3gdOCzwy5QkjSYuQO0WQqMV9VGgCRrgJOBmycaVNXt3bzH90CNkqQBDNLlMh/Y1De+uZu205IsTzKWZGzr1q27sgpJ0jSe0pOiVXVuVY1W1ejIyMhTuWlJat4ggb4FWNg3vqCbJkl6Ghkk0NcDi5MclmQecCqwds+WJUnaWTMGelVtA1YAlwG3ABdX1YYkq5KcBJDklUk2A6cA5yTZsCeLliQ92SC/cqGq1gHrJk07s294Pb2uGEnSLPFKUUlqhIEuSY0w0CWpEQa6JDXCQJekRhjoktQIA12SGmGgS1IjDHRJaoSBLkmNMNAlqREGuiQ1wkCXpEYY6JLUCANdkhphoEtSIwx0SWqEgS5JjTDQJakRBrokNcJAl6RGGOiS1AgDXZIaYaBLUiMMdElqhIEuSY0w0CWpEQMFepJlSW5NMp5k5RTz909yUTf/2iSLhl2oJGnHZgz0JHOA1cAJwBLgtCRLJjX7DeD+qjoC+BPgY8MuVJK0Y4McoS8FxqtqY1U9AqwBTp7U5mTggm74c8Brk2R4ZUqSZjJ3gDbzgU1945uBV03Xpqq2JXkQeC5wT3+jJMuB5d3oQ0lu3ZWi90IHM2lfPF3F71awF71e4GvW2ZdesxdON2OQQB+aqjoXOPep3ObTQZKxqhqd7To0GF+vvY+vWc8gXS5bgIV94wu6aVO2STIX+Eng3mEUKEkazCCBvh5YnOSwJPOAU4G1k9qsBd7eDb8F+HJV1fDKlCTNZMYul65PfAVwGTAHOK+qNiRZBYxV1VrgvwOfSTIO3Ecv9PXP9rlupr2cr9fex9cMiAfSktQGrxSVpEYY6JLUCANd0tNWktOTfHK269hbGOiS1AgDfTclWZXkvX3jf5TkjCTvT7I+ydeT/Kdu3gFJLk1yU5JvJnnr7FUugCSLktyS5NNJNiT52yQ/keTIJNd0r98lSZ4z27W2oNvf3+wb/70kH0lyeZKPJbkuybeTHDPFsicm+VqSg5Ocn+S/Jflqko1J3tK1SZKzu7+vb0z8jSVZneSkbviSJOd1w+/s/manfB88NXtleAz03Xce8OsASfaj95PNu4DF9O6DcyTwc0leAywD7qyqn62qlwH/Z3ZK1iSLgdVV9VLgAeDNwF8CH6yqlwPfAP7jLNa3r5hbVUuB9zJpfyf5FWAl8PqqmrjE/wXA0cAvA2d1095E72/uZ4HjgbOTvAC4Cpj4kJhP70aDdNOu7Ianeh/sVQz03VRVtwP3JjkK+CXgH4FX9g3fAPwLem+WbwCv645EjqmqB2enak3y3aq6sRu+HjgcOKiqruimXQC8ZlYq27d8vvv3emBR3/TjgA8CJ1bV/X3Tv1BVj1fVzcDzu2lHAxdW1WNVdTdwBb2/x6uAY7o7xd4M3N0F/c8DX+2Wnfw+6K9hr/CU3sulYX8BnA78NL0j9tcCf1xV50xumOQVwOuBjyb5UlWteioL1ZQe7ht+DDhotgrZB2xj+wPJZ/YNT7wOj7F9Nn0H+BngRcDYFO0Bdnh316rakuQget+SrwR+CvhV4KGq+kGS5/Lk94FdLvuoS+i9UV5J74ray4B3JjkQIMn8JM9Lcgjwo6r6H8DZwCtmq2Dt0IPA/X39uP+G3pGedt/dwPOSPDfJ/vS6S2ZyB103WJKXztD2KuCtSeYkGaH3zeq6bt419Lpzruza/V73bzM8Qh+CqnokyVeAB6rqMeBvk7wE+Fp3W/iHgF8DjqDXp/c48Cjwm7NVs2b0duBTSZ4FbATeMcv1NKGqHu1uG3IdvZv6fWvA5b6V5G3AXyV5ww6aXkKvG+UmoIAPVNVd3byrgF+qqvEkd9A7Sm8q0L30fwi6k6E3AKdU1W2zXY+kfZNdLrupO8kyDnzJMJc0mzxCl6RGeIQuSY0w0CWpEQa6JDXCQJekRhjoktSI/w/TIK6YopbnBgAAAABJRU5ErkJggg==", + "text/plain": [ + "
" + ] + }, + "metadata": { + "needs_background": "light", + "tags": [] + }, + "output_type": "display_data" + } + ], + "source": [ + "import glob\n", + "import pandas as pd\n", + "pd.set_option(\"display.precision\", 2)\n", + "\n", + "txtfiles = []\n", + "for file in glob.glob(\"/content/data/mini_speech_commands/unknown/*.wav\"):\n", + " txtfiles.append(file)\n", + "\n", + "for i in range(25):\n", + " print(txtfiles[i])\n", + " sample_ds = preprocess_dataset([str(txtfiles[i])])\n", + " for spectrogram, label in sample_ds.batch(1):\n", + " prediction = model(spectrogram)\n", + " plt.bar(commands, tf.nn.softmax(prediction[0]))\n", + " plt.title(f'Predictions for \"{commands[label[0]]}\"')\n", + " plt.show()" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "E8IcErTDyudr" + }, + "source": [ + "## Generate TF Lite float32 model" + ] + }, + { + "cell_type": "code", + "execution_count": 143, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "nlP5R7Y7ytYU", + "outputId": "7b8fee49-7b77-4bf5-fe17-aa593fafaa52" + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "2.5.0\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "WARNING:absl:Found untraced functions such as lstm_cell_3_layer_call_and_return_conditional_losses, lstm_cell_3_layer_call_fn, lstm_cell_3_layer_call_fn, lstm_cell_3_layer_call_and_return_conditional_losses, lstm_cell_3_layer_call_and_return_conditional_losses while saving (showing 5 of 5). These functions will not be directly callable after loading.\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "INFO:tensorflow:Assets written to: keras_lstm/assets\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "INFO:tensorflow:Assets written to: keras_lstm/assets\n" + ] + }, + { + "data": { + "text/plain": [ + "483000" + ] + }, + "execution_count": 143, + "metadata": { + "tags": [] + }, + "output_type": "execute_result" + } + ], + "source": [ + "import tensorflow\n", + "print(tensorflow.__version__)\n", + "\n", + "run_model = tf.function(lambda x: model(x))\n", + "# This is important, let's fix the input size.\n", + "BATCH_SIZE = 1\n", + "STEPS = 49\n", + "INPUT_SIZE = 257\n", + "\n", + "concrete_func = run_model.get_concrete_function(\n", + " tf.TensorSpec([BATCH_SIZE, STEPS, INPUT_SIZE], model.inputs[0].dtype))\n", + "\n", + "# model directory.\n", + "MODEL_DIR = \"keras_lstm\"\n", + "model.save(MODEL_DIR, save_format=\"tf\", signatures=concrete_func)\n", + "\n", + "#Float LSTM model\n", + "converter = tf.lite.TFLiteConverter.from_saved_model(MODEL_DIR)\n", + "tflite_float_model = converter.convert()\n", + "open('/content/keras_lstm/model_float.tflite', \"wb\").write(tflite_float_model)\n" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "6lo_G30v0raq" + }, + "source": [ + "## Validate audio files using TFLite float32 model" + ] + }, + { + "cell_type": "code", + "execution_count": 144, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "t807WqXQ0qEJ", + "outputId": "16c5640e-e982-47ad-b25a-c414bc2e2780" + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "/content/data/mini_speech_commands/no/5c39594f_nohash_4.wav\n", + " yes no unknown\n", + "0 4.0e-13 1.0 5.8e-09\n", + "/content/data/mini_speech_commands/no/b83c1acf_nohash_2.wav\n", + " yes no unknown\n", + "0 3.1e-08 1.0 7.1e-04\n", + "/content/data/mini_speech_commands/no/8012c69d_nohash_2.wav\n", + " yes no unknown\n", + "0 2.1e-10 1.0 2.4e-10\n", + "/content/data/mini_speech_commands/no/dbb40d24_nohash_0.wav\n", + " yes no unknown\n", + "0 1.1e-06 1.3e-06 1.0\n", + "/content/data/mini_speech_commands/no/f4504600_nohash_1.wav\n", + " yes no unknown\n", + "0 1.9e-16 1.0 2.4e-13\n", + "/content/data/mini_speech_commands/no/c79159aa_nohash_4.wav\n", + " yes no unknown\n", + "0 1.6e-11 1.0 6.5e-11\n", + "/content/data/mini_speech_commands/no/c7aa72e6_nohash_1.wav\n", + " yes no unknown\n", + "0 4.3e-04 0.4 0.6\n", + "/content/data/mini_speech_commands/no/a527cb3c_nohash_0.wav\n", + " yes no unknown\n", + "0 5.2e-12 1.0 1.6e-07\n", + "/content/data/mini_speech_commands/no/26b28ea7_nohash_0.wav\n", + " yes no unknown\n", + "0 5.3e-08 1.0 2.7e-03\n", + "/content/data/mini_speech_commands/no/765ffccb_nohash_3.wav\n", + " yes no unknown\n", + "0 7.8e-10 1.0 2.0e-09\n", + "/content/data/mini_speech_commands/no/735845ab_nohash_2.wav\n", + " yes no unknown\n", + "0 0.2 0.8 1.2e-02\n", + "/content/data/mini_speech_commands/no/38d78313_nohash_3.wav\n", + " yes no unknown\n", + "0 4.6e-13 1.0 7.4e-16\n", + "/content/data/mini_speech_commands/no/89f680f3_nohash_1.wav\n", + " yes no unknown\n", + "0 1.4e-10 0.3 0.7\n", + "/content/data/mini_speech_commands/no/c661be6e_nohash_1.wav\n", + " yes no unknown\n", + "0 2.2e-12 1.0 1.9e-10\n", + "/content/data/mini_speech_commands/no/c33682f0_nohash_0.wav\n", + " yes no unknown\n", + "0 3.7e-08 1.0 5.8e-03\n", + "/content/data/mini_speech_commands/no/2e73212b_nohash_0.wav\n", + " yes no unknown\n", + "0 4.7e-03 1.0 1.6e-05\n", + "/content/data/mini_speech_commands/no/cb802c63_nohash_0.wav\n", + " yes no unknown\n", + "0 1.9e-07 1.0 3.3e-05\n", + "/content/data/mini_speech_commands/no/2903efb3_nohash_0.wav\n", + " yes no unknown\n", + "0 1.3e-04 1.0 9.5e-07\n", + "/content/data/mini_speech_commands/no/1657c9fa_nohash_1.wav\n", + " yes no unknown\n", + "0 3.0e-11 1.0 7.3e-10\n", + "/content/data/mini_speech_commands/no/24befdb3_nohash_4.wav\n", + " yes no unknown\n", + "0 1.2e-06 1.0 3.8e-04\n", + "/content/data/mini_speech_commands/no/bfdb9801_nohash_0.wav\n", + " yes no unknown\n", + "0 1.7e-04 1.0 2.5e-08\n", + "/content/data/mini_speech_commands/no/dc75148d_nohash_0.wav\n", + " yes no unknown\n", + "0 1.6e-07 1.0 1.2e-10\n", + "/content/data/mini_speech_commands/no/07ad9b59_nohash_0.wav\n", + " yes no unknown\n", + "0 2.3e-03 1.0 2.8e-04\n", + "/content/data/mini_speech_commands/no/ac7840d8_nohash_1.wav\n", + " yes no unknown\n", + "0 1.8e-06 1.0 1.4e-05\n", + "/content/data/mini_speech_commands/no/e14a99a5_nohash_0.wav\n", + " yes no unknown\n", + "0 1.3e-06 1.0 2.1e-06\n" + ] + } + ], + "source": [ + "# Load quantized TFLite model\n", + "tflite_interpreter_float = tf.lite.Interpreter(model_path='/content/keras_lstm/model_float.tflite')\n", + "# Learn about its input and output details\n", + "input_details = tflite_interpreter_float.get_input_details()\n", + "output_details = tflite_interpreter_float.get_output_details()\n", + "tflite_interpreter_float.allocate_tensors()\n", + "\n", + "import glob\n", + "import pandas as pd\n", + "pd.set_option(\"display.precision\", 2)\n", + "\n", + "txtfiles = []\n", + "for file in glob.glob(\"/content/data/mini_speech_commands/no/*.wav\"):\n", + " txtfiles.append(file)\n", + "\n", + "for i in range(25):\n", + " sample_ds = preprocess_dataset([str(txtfiles[i])])\n", + " print(txtfiles[i])\n", + " # Run inference\n", + " for spectrogram, label in sample_ds.batch(1):\n", + " tflite_interpreter_float.set_tensor(input_details[0]['index'],np.array(spectrogram, dtype=np.float32).reshape(1,49, 257) )\n", + " tflite_interpreter_float.invoke()\n", + " tflite_float_model_predictions = tflite_interpreter_float.get_tensor(output_details[0]['index'])\n", + " # Convert prediction results to Pandas dataframe, for better visualization\n", + " # Increase precision of presented data for better side-by-side comparison\n", + " tflite_pred_dataframe = pd.DataFrame(tflite_float_model_predictions)\n", + " tflite_pred_dataframe.columns = commands\n", + " pd.set_option(\"display.precision\",1)\n", + " print(tflite_pred_dataframe)\n" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "VJANb9Uk-Xw-" + }, + "source": [ + "##Generate Quantized int8 model\n" + ] + }, + { + "cell_type": "code", + "execution_count": 145, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "nfMYKQgz-W_X", + "outputId": "28dc6ce9-24ab-44b6-bc79-4e65708b1d2c" + }, + "outputs": [ + { + "data": { + "text/plain": [ + "124768" + ] + }, + "execution_count": 145, + "metadata": { + "tags": [] + }, + "output_type": "execute_result" + } + ], + "source": [ + "def representative_dataset_3():\n", + " for spectrogram, _ in spectrogram_ds.take(800):\n", + " # print('test')\n", + " flattened_data = np.array(spectrogram, dtype=np.float32).reshape(1,49, 257)\n", + " yield [flattened_data]\n", + "\n", + "converter.representative_dataset = representative_dataset_3\n", + "converter.optimizations = [tf.lite.Optimize.OPTIMIZE_FOR_SIZE]\n", + "converter.target_spec.supported_ops = [tf.lite.OpsSet.TFLITE_BUILTINS_INT8]\n", + "converter.inference_input_type = tf.int8\n", + "converter.inference_output_type = tf.int8\n", + "quantized_tflite_model = converter.convert()\n", + "open('/content/keras_lstm/model_quantized_minispeech.tflite', \"wb\").write(quantized_tflite_model)\n" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "HX4Ib7o4-kcH" + }, + "source": [ + "## Validate Quantized int8 model" + ] + }, + { + "cell_type": "code", + "execution_count": 152, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "K0fLf2sx-jHx", + "outputId": "710765e4-05e8-4eec-98b2-36a693465370" + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "/content/data/mini_speech_commands/unknown/4c77947d_nohash_0.wav\n", + " yes no unknown\n", + "0 -128 -128 127\n", + "/content/data/mini_speech_commands/unknown/b737ee80_nohash_1.wav\n", + " yes no unknown\n", + "0 -128 -128 127\n", + "/content/data/mini_speech_commands/unknown/feb1d305_nohash_2.wav\n", + " yes no unknown\n", + "0 -128 -128 127\n", + "/content/data/mini_speech_commands/unknown/07ad9b59_nohash_2.wav\n", + " yes no unknown\n", + "0 -128 -128 127\n", + "/content/data/mini_speech_commands/unknown/5c39594f_nohash_4.wav\n", + " yes no unknown\n", + "0 -128 -127 127\n", + "/content/data/mini_speech_commands/unknown/2d92f18b_nohash_0.wav\n", + " yes no unknown\n", + "0 -124 -124 120\n", + "/content/data/mini_speech_commands/unknown/b83c1acf_nohash_2.wav\n", + " yes no unknown\n", + "0 -128 -128 127\n", + "/content/data/mini_speech_commands/unknown/4a1e736b_nohash_4.wav\n", + " yes no unknown\n", + "0 -128 -128 127\n", + "/content/data/mini_speech_commands/unknown/8012c69d_nohash_2.wav\n", + " yes no unknown\n", + "0 -128 127 -128\n", + "/content/data/mini_speech_commands/unknown/71f6fed7_nohash_2.wav\n", + " yes no unknown\n", + "0 -128 -128 127\n", + "/content/data/mini_speech_commands/unknown/ec21c46b_nohash_2.wav\n", + " yes no unknown\n", + "0 -128 -128 127\n", + "/content/data/mini_speech_commands/unknown/2f0a410b_nohash_1.wav\n", + " yes no unknown\n", + "0 -128 -128 127\n", + "/content/data/mini_speech_commands/unknown/f2a90886_nohash_0.wav\n", + " yes no unknown\n", + "0 -128 -128 127\n", + "/content/data/mini_speech_commands/unknown/dbb40d24_nohash_0.wav\n", + " yes no unknown\n", + "0 -126 -1 -1\n", + "/content/data/mini_speech_commands/unknown/b9515bf3_nohash_1.wav\n", + " yes no unknown\n", + "0 -128 -128 127\n", + "/content/data/mini_speech_commands/unknown/15c563d7_nohash_3.wav\n", + " yes no unknown\n", + "0 -128 127 -128\n", + "/content/data/mini_speech_commands/unknown/f4504600_nohash_1.wav\n", + " yes no unknown\n", + "0 -128 -128 127\n", + "/content/data/mini_speech_commands/unknown/da76aa58_nohash_1.wav\n", + " yes no unknown\n", + "0 -128 -128 127\n", + "/content/data/mini_speech_commands/unknown/dedc7fab_nohash_1.wav\n", + " yes no unknown\n", + "0 -128 -128 127\n", + "/content/data/mini_speech_commands/unknown/a527cb3c_nohash_0.wav\n", + " yes no unknown\n", + "0 -128 -128 127\n", + "/content/data/mini_speech_commands/unknown/26b28ea7_nohash_0.wav\n", + " yes no unknown\n", + "0 -128 -128 127\n", + "/content/data/mini_speech_commands/unknown/d84829e0_nohash_0.wav\n", + " yes no unknown\n", + "0 -125 -122 119\n", + "/content/data/mini_speech_commands/unknown/2313e093_nohash_0.wav\n", + " yes no unknown\n", + "0 -128 -128 127\n", + "/content/data/mini_speech_commands/unknown/763188c4_nohash_1.wav\n", + " yes no unknown\n", + "0 -128 -128 127\n", + "/content/data/mini_speech_commands/unknown/7ea032f3_nohash_3.wav\n", + " yes no unknown\n", + "0 -31 -128 30\n" + ] + } + ], + "source": [ + "\n", + "# Load quantized TFLite model\n", + "tflite_interpreter_quant = tf.lite.Interpreter(model_path='/content/keras_lstm/model_quantized_minispeech.tflite')\n", + "# Learn about its input and output details\n", + "input_details = tflite_interpreter_quant.get_input_details()\n", + "\n", + "output_details = tflite_interpreter_quant.get_output_details()\n", + "\n", + "tflite_interpreter_quant.allocate_tensors()\n", + "\n", + "import glob\n", + "import pandas as pd\n", + "pd.set_option(\"display.precision\", 2)\n", + "\n", + "txtfiles = []\n", + "for file in glob.glob(\"/content/data/mini_speech_commands/unknown/*.wav\"):\n", + " txtfiles.append(file)\n", + "\n", + "for i in range(25):\n", + " print(txtfiles[i])\n", + " sample_ds = preprocess_dataset([str(txtfiles[i])])\n", + " # Run inference\n", + " for spectrogram, label in sample_ds.batch(1):\n", + " spectrogram_t = np.array(spectrogram, dtype=np.uint8).reshape(1,49, 257)\n", + " spectrogram_t = np.array(spectrogram_t-128, dtype=np.int8)\n", + " tflite_interpreter_quant.set_tensor(input_details[0]['index'],spectrogram_t )\n", + " tflite_interpreter_quant.invoke()\n", + " tflite_q_model_predictions = tflite_interpreter_quant.get_tensor(output_details[0]['index'])\n", + " tflite_pred_dataframe = pd.DataFrame(tflite_q_model_predictions)\n", + " tflite_pred_dataframe.columns = commands\n", + " print(tflite_pred_dataframe)\n", + "\n" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "qg8U2AZkSfCH" + }, + "source": [ + "## Evaluate int8 model using floor and inputscale and zeropoint" + ] + }, + { + "cell_type": "code", + "execution_count": 147, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "cEwmoArnIcK9", + "outputId": "887b437e-0a79-4ff6-e422-0bf6d8e0d8aa" + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "/content/data/mini_speech_commands/no/5c39594f_nohash_4.wav\n", + "0.7175403237342834\n", + "-128\n", + "[[-128 127 -128]]\n", + " yes no unknown\n", + "0 0.0 1.0 0.0\n", + "/content/data/mini_speech_commands/no/b83c1acf_nohash_2.wav\n", + "0.7175403237342834\n", + "-128\n", + "[[-128 125 -125]]\n", + " yes no unknown\n", + "0 0.0 0.99 0.01\n", + "/content/data/mini_speech_commands/no/8012c69d_nohash_2.wav\n", + "0.7175403237342834\n", + "-128\n", + "[[-128 127 -128]]\n", + " yes no unknown\n", + "0 0.0 1.0 0.0\n", + "/content/data/mini_speech_commands/no/dbb40d24_nohash_0.wav\n", + "0.7175403237342834\n", + "-128\n", + "[[-120 -128 120]]\n", + " yes no unknown\n", + "0 0.03 0.0 0.97\n", + "/content/data/mini_speech_commands/no/f4504600_nohash_1.wav\n", + "0.7175403237342834\n", + "-128\n", + "[[-128 127 -128]]\n", + " yes no unknown\n", + "0 0.0 1.0 0.0\n", + "/content/data/mini_speech_commands/no/c79159aa_nohash_4.wav\n", + "0.7175403237342834\n", + "-128\n", + "[[-128 127 -128]]\n", + " yes no unknown\n", + "0 0.0 1.0 0.0\n", + "/content/data/mini_speech_commands/no/c7aa72e6_nohash_1.wav\n", + "0.7175403237342834\n", + "-128\n", + "[[-128 127 -127]]\n", + " yes no unknown\n", + "0 0.0 1.0 3.91e-03\n", + "/content/data/mini_speech_commands/no/a527cb3c_nohash_0.wav\n", + "0.7175403237342834\n", + "-128\n", + "[[-128 127 -128]]\n", + " yes no unknown\n", + "0 0.0 1.0 0.0\n", + "/content/data/mini_speech_commands/no/26b28ea7_nohash_0.wav\n", + "0.7175403237342834\n", + "-128\n", + "[[-128 118 -118]]\n", + " yes no unknown\n", + "0 0.0 0.96 0.04\n", + "/content/data/mini_speech_commands/no/765ffccb_nohash_3.wav\n", + "0.7175403237342834\n", + "-128\n", + "[[-128 127 -128]]\n", + " yes no unknown\n", + "0 0.0 1.0 0.0\n", + "/content/data/mini_speech_commands/no/735845ab_nohash_2.wav\n", + "0.7175403237342834\n", + "-128\n", + "[[ 126 -128 -126]]\n", + " yes no unknown\n", + "0 0.99 0.0 7.81e-03\n", + "/content/data/mini_speech_commands/no/38d78313_nohash_3.wav\n", + "0.7175403237342834\n", + "-128\n", + "[[-128 127 -128]]\n", + " yes no unknown\n", + "0 0.0 1.0 0.0\n", + "/content/data/mini_speech_commands/no/89f680f3_nohash_1.wav\n", + "0.7175403237342834\n", + "-128\n", + "[[-128 31 -31]]\n", + " yes no unknown\n", + "0 0.0 0.62 0.38\n", + "/content/data/mini_speech_commands/no/c661be6e_nohash_1.wav\n", + "0.7175403237342834\n", + "-128\n", + "[[-128 127 -128]]\n", + " yes no unknown\n", + "0 0.0 1.0 0.0\n", + "/content/data/mini_speech_commands/no/c33682f0_nohash_0.wav\n", + "0.7175403237342834\n", + "-128\n", + "[[-128 -107 107]]\n", + " yes no unknown\n", + "0 0.0 0.08 0.92\n", + "/content/data/mini_speech_commands/no/2e73212b_nohash_0.wav\n", + "0.7175403237342834\n", + "-128\n", + "[[-127 -128 127]]\n", + " yes no unknown\n", + "0 3.91e-03 0.0 1.0\n", + "/content/data/mini_speech_commands/no/cb802c63_nohash_0.wav\n", + "0.7175403237342834\n", + "-128\n", + "[[-128 127 -127]]\n", + " yes no unknown\n", + "0 0.0 1.0 3.91e-03\n", + "/content/data/mini_speech_commands/no/2903efb3_nohash_0.wav\n", + "0.7175403237342834\n", + "-128\n", + "[[-128 127 -128]]\n", + " yes no unknown\n", + "0 0.0 1.0 0.0\n", + "/content/data/mini_speech_commands/no/1657c9fa_nohash_1.wav\n", + "0.7175403237342834\n", + "-128\n", + "[[-128 127 -128]]\n", + " yes no unknown\n", + "0 0.0 1.0 0.0\n", + "/content/data/mini_speech_commands/no/24befdb3_nohash_4.wav\n", + "0.7175403237342834\n", + "-128\n", + "[[-128 127 -128]]\n", + " yes no unknown\n", + "0 0.0 1.0 0.0\n", + "/content/data/mini_speech_commands/no/bfdb9801_nohash_0.wav\n", + "0.7175403237342834\n", + "-128\n", + "[[-128 127 -128]]\n", + " yes no unknown\n", + "0 0.0 1.0 0.0\n", + "/content/data/mini_speech_commands/no/dc75148d_nohash_0.wav\n", + "0.7175403237342834\n", + "-128\n", + "[[-115 115 -128]]\n", + " yes no unknown\n", + "0 0.05 0.95 0.0\n", + "/content/data/mini_speech_commands/no/07ad9b59_nohash_0.wav\n", + "0.7175403237342834\n", + "-128\n", + "[[-128 127 -128]]\n", + " yes no unknown\n", + "0 0.0 1.0 0.0\n", + "/content/data/mini_speech_commands/no/ac7840d8_nohash_1.wav\n", + "0.7175403237342834\n", + "-128\n", + "[[-128 127 -128]]\n", + " yes no unknown\n", + "0 0.0 1.0 0.0\n", + "/content/data/mini_speech_commands/no/e14a99a5_nohash_0.wav\n", + "0.7175403237342834\n", + "-128\n", + "[[ 118 -122 -124]]\n", + " yes no unknown\n", + "0 0.96 0.02 0.02\n" + ] + } + ], + "source": [ + "#test\n", + "# Load quantized TFLite model\n", + "tflite_interpreter_quant_int8 = tf.lite.Interpreter(model_path='/content/keras_lstm/model_quantized_minispeech.tflite')\n", + "# Learn about its input and output details\n", + "input_details = tflite_interpreter_quant_int8.get_input_details()\n", + "\n", + "output_details = tflite_interpreter_quant_int8.get_output_details()\n", + "\n", + "tflite_interpreter_quant_int8.allocate_tensors()\n", + "\n", + "import glob\n", + "import pandas as pd\n", + "pd.set_option(\"display.precision\", 2)\n", + "\n", + "txtfiles = []\n", + "for file in glob.glob(\"/content/data/mini_speech_commands/no/*.wav\"):\n", + " txtfiles.append(file)\n", + "\n", + "\n", + "for i in range(25):\n", + " print(txtfiles[i])\n", + " sample_ds = preprocess_dataset([str(txtfiles[i])])\n", + " # Run inference\n", + " for spectrogram, label in sample_ds.batch(1):\n", + " input_scale, input_zero_point = input_details[0][\"quantization\"]\n", + " print(input_scale)\n", + " print(input_zero_point)\n", + " spectrogram = np.array(spectrogram)\n", + " spectrogram = np.clip(np.floor(spectrogram / input_scale + input_zero_point), -128, 127) # for int8 validation\n", + " q_spectrogram = np.array(spectrogram, dtype=np.int8).reshape(1,49, 257)\n", + " tflite_interpreter_quant_int8.set_tensor(input_details[0]['index'], q_spectrogram )\n", + " tflite_interpreter_quant_int8.invoke()\n", + " tflite_q_model_predictions = tflite_interpreter_quant_int8.get_tensor(output_details[0]['index'])\n", + " print(tflite_q_model_predictions)\n", + " output_scale, output_zero_point = output_details[0][\"quantization\"]\n", + " tflite_model_predictions = (np.array(tflite_q_model_predictions, dtype=np.float32) - output_zero_point) * output_scale\n", + " tflite_pred_dataframe = pd.DataFrame(tflite_model_predictions)\n", + " tflite_pred_dataframe.columns = commands\n", + " print(tflite_pred_dataframe)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "tUToqbr-OqKp" + }, + "source": [ + "## Generate a TensorFlow Lite for MicroControllers Model\n", + "Convert the TensorFlow Lite model into a C source file that can be loaded by TensorFlow Lite for Microcontrollers." + ] + }, + { + "cell_type": "code", + "execution_count": 148, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "_y90gcAtOs0-", + "outputId": "a422f82c-7a13-4d1e-8642-e8f7d63a114d" + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\r0% [Working]\r \rHit:1 https://cloud.r-project.org/bin/linux/ubuntu bionic-cran40/ InRelease\n", + "\r0% [Connecting to archive.ubuntu.com (91.189.88.142)] [Waiting for headers] [Co\r \rIgn:2 https://developer.download.nvidia.com/compute/cuda/repos/ubuntu1804/x86_64 InRelease\n", + "\r0% [Connecting to archive.ubuntu.com (91.189.88.142)] [Waiting for headers] [Co\r0% [1 InRelease gpgv 3,626 B] [Connecting to archive.ubuntu.com (91.189.88.142)\r \rIgn:3 https://developer.download.nvidia.com/compute/machine-learning/repos/ubuntu1804/x86_64 InRelease\n", + "Hit:4 https://developer.download.nvidia.com/compute/cuda/repos/ubuntu1804/x86_64 Release\n", + "Get:5 http://security.ubuntu.com/ubuntu bionic-security InRelease [88.7 kB]\n", + "Hit:6 https://developer.download.nvidia.com/compute/machine-learning/repos/ubuntu1804/x86_64 Release\n", + "Hit:7 http://ppa.launchpad.net/c2d4u.team/c2d4u4.0+/ubuntu bionic InRelease\n", + "Hit:8 http://archive.ubuntu.com/ubuntu bionic InRelease\n", + "Get:10 http://archive.ubuntu.com/ubuntu bionic-updates InRelease [88.7 kB]\n", + "Hit:12 http://ppa.launchpad.net/cran/libgit2/ubuntu bionic InRelease\n", + "Hit:13 http://ppa.launchpad.net/deadsnakes/ppa/ubuntu bionic InRelease\n", + "Get:14 http://archive.ubuntu.com/ubuntu bionic-backports InRelease [74.6 kB]\n", + "Hit:15 http://ppa.launchpad.net/graphics-drivers/ppa/ubuntu bionic InRelease\n", + "Fetched 252 kB in 2s (142 kB/s)\n", + "Reading package lists... Done\n" + ] + } + ], + "source": [ + "# Install xxd if it is not available\n", + "!apt-get update && apt-get -qq install xxd\n", + "# Convert to a C source file\n", + "!xxd -i /content/keras_lstm/model_quantized_minispeech.tflite > /content/keras_lstm/model.cc\n", + "!cat /content/keras_lstm/model.cc" + ] + } + ], + "metadata": { + "accelerator": "GPU", + "colab": { + "collapsed_sections": [], + "name": "micro_speech_with_lstm_op.ipynb", + "provenance": [] + }, + "kernelspec": { + "display_name": "Python 3", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.9.6" + } + }, + "nbformat": 4, + "nbformat_minor": 0 +} diff --git a/docs/tensorflow/train_hello_world_model.ipynb b/docs/tensorflow/train_hello_world_model.ipynb new file mode 100644 index 0000000000..3281ce5e9e --- /dev/null +++ b/docs/tensorflow/train_hello_world_model.ipynb @@ -0,0 +1,3691 @@ +{ + "nbformat": 4, + "nbformat_minor": 0, + "metadata": { + "colab": { + "name": "train_hello_world_model.ipynb", + "provenance": [], + "collapsed_sections": [], + "toc_visible": true + }, + "kernelspec": { + "name": "python3", + "display_name": "Python 3" + } + }, + "cells": [ + { + "cell_type": "markdown", + "metadata": { + "id": "aCZBFzjClURz" + }, + "source": [ + "# Train a Simple TensorFlow Lite for Microcontrollers model\n", + "\n", + "This notebook demonstrates the process of training a 2.5 kB model using TensorFlow and converting it for use with TensorFlow Lite for Microcontrollers. \n", + "\n", + "Deep learning networks learn to model patterns in underlying data. Here, we're going to train a network to model data generated by a [sine](https://en.wikipedia.org/wiki/Sine) function. This will result in a model that can take a value, `x`, and predict its sine, `y`.\n", + "\n", + "The model created in this notebook is used in the [hello_world](https://github.com/tensorflow/tflite-micro/blob/main/tensorflow/lite/micro/examples/hello_world) example for [TensorFlow Lite for MicroControllers](https://www.tensorflow.org/lite/microcontrollers/overview).\n", + "\n", + "\n", + " \n", + " \n", + "
\n", + " Run in Google Colab\n", + " \n", + " View source on GitHub\n", + "
" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "_UQblnrLd_ET" + }, + "source": [ + "## Configure Defaults" + ] + }, + { + "cell_type": "code", + "metadata": { + "id": "5PYwRFppd-WB" + }, + "source": [ + "# Define paths to model files\n", + "import os\n", + "MODELS_DIR = 'models/'\n", + "if not os.path.exists(MODELS_DIR):\n", + " os.mkdir(MODELS_DIR)\n", + "MODEL_TF = MODELS_DIR + 'model'\n", + "MODEL_NO_QUANT_TFLITE = MODELS_DIR + 'model_no_quant.tflite'\n", + "MODEL_TFLITE = MODELS_DIR + 'model.tflite'\n", + "MODEL_TFLITE_MICRO = MODELS_DIR + 'model.cc'" + ], + "execution_count": null, + "outputs": [] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "dh4AXGuHWeu1" + }, + "source": [ + "## Setup Environment\n", + "\n", + "Install Dependencies" + ] + }, + { + "cell_type": "code", + "metadata": { + "id": "cr1VLfotanf6", + "outputId": "510567d6-300e-40e2-f5b8-c3520a3f3a8b", + "colab": { + "base_uri": "https://localhost:8080/" + } + }, + "source": [ + "! pip install tensorflow==2.4.0" + ], + "execution_count": null, + "outputs": [ + { + "output_type": "stream", + "text": [ + "Requirement already satisfied: tensorflow==2.4.0rc0 in /usr/local/lib/python3.6/dist-packages (2.4.0rc0)\n", + "Requirement already satisfied: termcolor~=1.1.0 in /usr/local/lib/python3.6/dist-packages (from tensorflow==2.4.0rc0) (1.1.0)\n", + "Requirement already satisfied: gast==0.3.3 in /usr/local/lib/python3.6/dist-packages (from tensorflow==2.4.0rc0) (0.3.3)\n", + "Requirement already satisfied: astunparse~=1.6.3 in /usr/local/lib/python3.6/dist-packages (from tensorflow==2.4.0rc0) (1.6.3)\n", + "Requirement already satisfied: absl-py~=0.10 in /usr/local/lib/python3.6/dist-packages (from tensorflow==2.4.0rc0) (0.10.0)\n", + "Requirement already satisfied: keras-preprocessing~=1.1.2 in /usr/local/lib/python3.6/dist-packages (from tensorflow==2.4.0rc0) (1.1.2)\n", + "Requirement already satisfied: six~=1.15.0 in /usr/local/lib/python3.6/dist-packages (from tensorflow==2.4.0rc0) (1.15.0)\n", + "Requirement already satisfied: tensorboard~=2.3 in /usr/local/lib/python3.6/dist-packages (from tensorflow==2.4.0rc0) (2.3.0)\n", + "Requirement already satisfied: tensorflow-estimator~=2.3.0 in /usr/local/lib/python3.6/dist-packages (from tensorflow==2.4.0rc0) (2.3.0)\n", + "Requirement already satisfied: flatbuffers~=1.12.0 in /usr/local/lib/python3.6/dist-packages (from tensorflow==2.4.0rc0) (1.12)\n", + "Requirement already satisfied: google-pasta~=0.2 in /usr/local/lib/python3.6/dist-packages (from tensorflow==2.4.0rc0) (0.2.0)\n", + "Requirement already satisfied: protobuf~=3.13.0 in /usr/local/lib/python3.6/dist-packages (from tensorflow==2.4.0rc0) (3.13.0)\n", + "Requirement already satisfied: grpcio~=1.32.0 in /usr/local/lib/python3.6/dist-packages (from tensorflow==2.4.0rc0) (1.32.0)\n", + "Requirement already satisfied: h5py~=2.10.0 in /usr/local/lib/python3.6/dist-packages (from tensorflow==2.4.0rc0) (2.10.0)\n", + "Requirement already satisfied: wrapt~=1.12.1 in /usr/local/lib/python3.6/dist-packages (from tensorflow==2.4.0rc0) (1.12.1)\n", + "Requirement already satisfied: opt-einsum~=3.3.0 in /usr/local/lib/python3.6/dist-packages (from tensorflow==2.4.0rc0) (3.3.0)\n", + "Requirement already satisfied: numpy~=1.19.2 in /usr/local/lib/python3.6/dist-packages (from tensorflow==2.4.0rc0) (1.19.4)\n", + "Requirement already satisfied: typing-extensions~=3.7.4 in /usr/local/lib/python3.6/dist-packages (from tensorflow==2.4.0rc0) (3.7.4.3)\n", + "Requirement already satisfied: wheel~=0.35 in /usr/local/lib/python3.6/dist-packages (from tensorflow==2.4.0rc0) (0.35.1)\n", + "Requirement already satisfied: werkzeug>=0.11.15 in /usr/local/lib/python3.6/dist-packages (from tensorboard~=2.3->tensorflow==2.4.0rc0) (1.0.1)\n", + "Requirement already satisfied: markdown>=2.6.8 in /usr/local/lib/python3.6/dist-packages (from tensorboard~=2.3->tensorflow==2.4.0rc0) (3.3.3)\n", + "Requirement already satisfied: google-auth-oauthlib<0.5,>=0.4.1 in /usr/local/lib/python3.6/dist-packages (from tensorboard~=2.3->tensorflow==2.4.0rc0) (0.4.2)\n", + "Requirement already satisfied: tensorboard-plugin-wit>=1.6.0 in /usr/local/lib/python3.6/dist-packages (from tensorboard~=2.3->tensorflow==2.4.0rc0) (1.7.0)\n", + "Requirement already satisfied: requests<3,>=2.21.0 in /usr/local/lib/python3.6/dist-packages (from tensorboard~=2.3->tensorflow==2.4.0rc0) (2.23.0)\n", + "Requirement already satisfied: setuptools>=41.0.0 in /usr/local/lib/python3.6/dist-packages (from tensorboard~=2.3->tensorflow==2.4.0rc0) (50.3.2)\n", + "Requirement already satisfied: google-auth<2,>=1.6.3 in /usr/local/lib/python3.6/dist-packages (from tensorboard~=2.3->tensorflow==2.4.0rc0) (1.17.2)\n", + "Requirement already satisfied: importlib-metadata; python_version < \"3.8\" in /usr/local/lib/python3.6/dist-packages (from markdown>=2.6.8->tensorboard~=2.3->tensorflow==2.4.0rc0) (2.0.0)\n", + "Requirement already satisfied: requests-oauthlib>=0.7.0 in /usr/local/lib/python3.6/dist-packages (from google-auth-oauthlib<0.5,>=0.4.1->tensorboard~=2.3->tensorflow==2.4.0rc0) (1.3.0)\n", + "Requirement already satisfied: certifi>=2017.4.17 in /usr/local/lib/python3.6/dist-packages (from requests<3,>=2.21.0->tensorboard~=2.3->tensorflow==2.4.0rc0) (2020.6.20)\n", + "Requirement already satisfied: urllib3!=1.25.0,!=1.25.1,<1.26,>=1.21.1 in /usr/local/lib/python3.6/dist-packages (from requests<3,>=2.21.0->tensorboard~=2.3->tensorflow==2.4.0rc0) (1.24.3)\n", + "Requirement already satisfied: chardet<4,>=3.0.2 in /usr/local/lib/python3.6/dist-packages (from requests<3,>=2.21.0->tensorboard~=2.3->tensorflow==2.4.0rc0) (3.0.4)\n", + "Requirement already satisfied: idna<3,>=2.5 in /usr/local/lib/python3.6/dist-packages (from requests<3,>=2.21.0->tensorboard~=2.3->tensorflow==2.4.0rc0) (2.10)\n", + "Requirement already satisfied: cachetools<5.0,>=2.0.0 in /usr/local/lib/python3.6/dist-packages (from google-auth<2,>=1.6.3->tensorboard~=2.3->tensorflow==2.4.0rc0) (4.1.1)\n", + "Requirement already satisfied: rsa<5,>=3.1.4; python_version >= \"3\" in /usr/local/lib/python3.6/dist-packages (from google-auth<2,>=1.6.3->tensorboard~=2.3->tensorflow==2.4.0rc0) (4.6)\n", + "Requirement already satisfied: pyasn1-modules>=0.2.1 in /usr/local/lib/python3.6/dist-packages (from google-auth<2,>=1.6.3->tensorboard~=2.3->tensorflow==2.4.0rc0) (0.2.8)\n", + "Requirement already satisfied: zipp>=0.5 in /usr/local/lib/python3.6/dist-packages (from importlib-metadata; python_version < \"3.8\"->markdown>=2.6.8->tensorboard~=2.3->tensorflow==2.4.0rc0) (3.4.0)\n", + "Requirement already satisfied: oauthlib>=3.0.0 in /usr/local/lib/python3.6/dist-packages (from requests-oauthlib>=0.7.0->google-auth-oauthlib<0.5,>=0.4.1->tensorboard~=2.3->tensorflow==2.4.0rc0) (3.1.0)\n", + "Requirement already satisfied: pyasn1>=0.1.3 in /usr/local/lib/python3.6/dist-packages (from rsa<5,>=3.1.4; python_version >= \"3\"->google-auth<2,>=1.6.3->tensorboard~=2.3->tensorflow==2.4.0rc0) (0.4.8)\n" + ], + "name": "stdout" + } + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "tx9lOPWh9grN" + }, + "source": [ + "Import Dependencies" + ] + }, + { + "cell_type": "code", + "metadata": { + "id": "53PBJBv1jEtJ" + }, + "source": [ + "# TensorFlow is an open source machine learning library\n", + "import tensorflow as tf\n", + "\n", + "# Keras is TensorFlow's high-level API for deep learning\n", + "from tensorflow import keras\n", + "# Numpy is a math library\n", + "import numpy as np\n", + "# Pandas is a data manipulation library \n", + "import pandas as pd\n", + "# Matplotlib is a graphing library\n", + "import matplotlib.pyplot as plt\n", + "# Math is Python's math library\n", + "import math\n", + "\n", + "# Set seed for experiment reproducibility\n", + "seed = 1\n", + "np.random.seed(seed)\n", + "tf.random.set_seed(seed)" + ], + "execution_count": null, + "outputs": [] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "p-PuBEb6CMeo" + }, + "source": [ + "## Dataset" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "7gB0-dlNmLT-" + }, + "source": [ + "### 1. Generate Data\n", + "\n", + "The code in the following cell will generate a set of random `x` values, calculate their sine values, and display them on a graph." + ] + }, + { + "cell_type": "code", + "metadata": { + "id": "uKjg7QeMDsDx", + "outputId": "2ded7790-62a2-40df-a4f9-429f2dd5357f", + "colab": { + "base_uri": "https://localhost:8080/", + "height": 265 + } + }, + "source": [ + "# Number of sample datapoints\n", + "SAMPLES = 1000\n", + "\n", + "# Generate a uniformly distributed set of random numbers in the range from\n", + "# 0 to 2π, which covers a complete sine wave oscillation\n", + "x_values = np.random.uniform(\n", + " low=0, high=2*math.pi, size=SAMPLES).astype(np.float32)\n", + "\n", + "# Shuffle the values to guarantee they're not in order\n", + "np.random.shuffle(x_values)\n", + "\n", + "# Calculate the corresponding sine values\n", + "y_values = np.sin(x_values).astype(np.float32)\n", + "\n", + "# Plot our data. The 'b.' argument tells the library to print blue dots.\n", + "plt.plot(x_values, y_values, 'b.')\n", + "plt.show()" + ], + "execution_count": null, + "outputs": [ + { + "output_type": "display_data", + "data": { + "image/png": "iVBORw0KGgoAAAANSUhEUgAAAYIAAAD4CAYAAADhNOGaAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAALEgAACxIB0t1+/AAAADh0RVh0U29mdHdhcmUAbWF0cGxvdGxpYiB2ZXJzaW9uMy4yLjIsIGh0dHA6Ly9tYXRwbG90bGliLm9yZy+WH4yJAAAgAElEQVR4nO3df5hcdX0v8Pd7syRBuJgQthDZNBtLlER7G9pp0gFNqWAWei2JVbxA9hIVn+GHVq2P7oT2eS5WrWaD1qAlkJGoyd0oBhCIt7QbREJAhoRNCUqyhexNQ9k0gYUENfxIzOZz//ieaWbmnM3u7MycM2fm/XqeeXbPZ87sflbMfOb7m2YGERFpXE1RJyAiItFSIRARaXAqBCIiDU6FQESkwakQiIg0uOaoExiLM844w9ra2qJOQ0QkVrZt2/aymbUUx2NZCNra2tDb2xt1GiIisULy+aC4uoZERBqcCoGISINTIRARaXAqBCIiDU6FQESkwVWkEJD8LsmXSD4zzPMk+S2S/SR/QfIP855bQnKX91hSiXxERGT0KtUi+D6AS07w/KUAZnqPFIDbAIDk6QBuAjAPwFwAN5GcXKGcZAzSaWDaNOAd7wCmTAGamgASaG4GZswAMpmoMxSRSqtIITCzzQAOnOCWhQDWmvMEgEkkpwJoB/CgmR0ws4MAHsSJC4pUWEcHMGECMG6ce9NfvhwYGAB27QIOHAByu5QPDQF79gDXXnu8MMyeDbS3qziIxF1YYwRnA3gh73rAiw0X9yGZItlLsndwcLBqiTaCTMZ9um9qAtatA44cAY4dO/6mPxpDQ0BfH7BxoysO06YB118PZLPVy1tEqiM2g8VmljGzhJklWlp8K6RlFDIZ4K1vdW/ce/aU9sY/koEB4PbbgfPPB047zXUxiUg8hFUI9gKYlnfd6sWGi0sFpdPAqae6AvDrX4/uNZMmAaef7rqBSvWb37guptmzS3+tiIQvrEKwAcDV3uyhPwHwKzPbB6AHwAKSk71B4gVeTCogm3VdNsuXA6+9NvL9J53kCkBnJ3DwIPDKK8e7jMyAxYuB8ePdvePGuUJxIn19bvxBrQOR2lap6aM/BJAF8E6SAySvIXkdyeu8Wx4AsBtAP4DvALgBAMzsAIAvA3jSe3zJi0mZ0mnXTTMwMPw9JDBxonuDN3NjBQcPAl1dwfd3dwOHD7t7jx51haKz07U2hnPkiCtEHR3l/T0iUj2M4+H1iUTCtPvo8ObNA7ZuPfE9CxYAPRVse6XTwMqVwKFDw9+zaJErHMlk5X6viIweyW1mliiOx2awWEanre3ERWD+fODxxytbBADXivjNb9zPbm0Nvue++1wrZd68yv5uESmPCkGdyGaBlhbg+cDdxoGZM92b9COPVPcTeTIJvPCC++Q/nK1bNZAsUktUCOpAJuM+ab/8cvDzCxYAzz0XbpdMV5crPIsWBT/f1+daLyISPRWCmJs3z00LDTJunPtkXuluoNFKJoF773WFKMjzz7tZRVqZLBKtWB5VKc6JBoVnzQJ27gw3n+H09LitKDZu9D935MjxQpZKhZuXiDhqEcRUJjN8EViwoHaKQE5Pj+sqmjQp+PmvfS3cfETkOBWCGMpk3L4+QRYvjq4raCTJpFuncNZZ/uf27FEXkUhUVAhipqPDdaUcO+Z/bvFit+ir1u3b53It9uUva+M6kSioEMRIR4fbLTRIZ2c8ikBOd7d/imn+xnXalkIkPCoEMZHNBheBRYtc3/tw20LUsq4uYNUq4Jxz/M8tX66uIpGwaIuJGMhmgSuv9C8W+4M/ALZvjyanSspmgQsu8G+LPWEC8Oab0eQkUo+0xURM5d4ki4sACdx2WzQ5VVoyCXzhC/744cNu2qmIVJcKQY1buDD4AJnbb6+vzdu6uoKnlm7cqL2JRKpNhaCGTZ0KBJ3K2dlZn4uvHnggOL51q7axFqkmFYIaNW8esH+/P75gQTwHhkcjmXSDx0Gnoq1bp2mlItWiQlCDstngVcOzZtXuYrFKSaWAn//cDRQXW7o0/HxEGkGlTii7hOSzJPtJ+v65kvwmye3e4zmSr+Y9N5T33IZK5BN3CxcGx2tt24hqSSaBb33LH9+9O/xcRBpB2YWA5DgAtwK4FMBsAFeSLNht3sz+2szmmNkcAN8G8OO8p9/IPWdml5WbT9y1tQWPCwy3g2e9SqX8f/Pb367uIZFqqESLYC6AfjPbbWZHANwJYJjPtACAKwH8sAK/t+60twcfLNMIXUJBenrcwHhrq9tS+9FHgfe8R6uORSqtEoXgbAAv5F0PeDEfktMBzADws7zwRJK9JJ8gOcwxJgDJlHdf72DQR+aYy2SCt2mupe2ko9DVBdxwg9tbycx9Xb5c6wtEKinsweIrANxtZkN5seneSrerAKwg+XtBLzSzjJklzCzR0tISRq6hCtqGefr0xi4CORde6J9JtHGjppSKVEolCsFeANPyrlu9WJArUNQtZGZ7va+7AWwCcF4FcoqVdNptw5zv5JP9sUaVTAKf/7w/vm6d9iMSqYRKFIInAcwkOYPkeLg3e9/sH5LnApgMIJsXm0xygvf9GQAuANBQn4HTadfVUeyv/ir8XGpZVxcwc6Y/fu21GkAWKVfZhcDMjgL4FIAeAH0A1pvZDpJfIpk/C+gKAHda4S53swD0knwawMMAlplZQxWClSv9sba2+l00Vo41a4Ljl18ebh4i9aYiZxab2QMAHiiK/e+i6y8GvO5xAL9fiRziKJ0GDh3yx2+8Mfxc4iCZdLOIiltQe/e6/y1VPEXGRiuLI5LNAl//uj++eHF97iNUKV1dwNy5/vjy5eoiEhkrFYKILF3qP24yLkdNRm3LFuC00/zxoLEWERmZCkEE0mlg8+bCWNyOmozazTf7Yxs2qFUgMhYqBCHLZoFbby2Mtbaqf7tUqZT/zOPcYjMRKY0KQYiyWbdFwmuvFcavuiqafOKuq8ud2ZzvJz9Rq0CkVCoEIbr+ev+4wPTpag2Uo7PT7UOUc+wYcPXVWmgmUgoVghD19fljf/M34edRT5JJtxbjpJPcNhRmQH+/W2imYiAyOioEIenoAI4cKYy1tmqqaCWkUsAjjwDFW1AF7d8kIn4qBCHIZNy+OMXWrw8/l3qVTAJNRf9v3rNH4wUio6FCEIJbbvHHOjvdm5dUzuTJ/thHPhJ+HiJxo0JQZZmMfyvpxYs1QFwNn/2sPzYwoO2qRUaiQlBlK1YUXs+apYVj1ZJKuSJb7J57ws9FJE5UCKoonfbPFAr61CqV093t337izTd1vKXIiagQVEnQpnKzZ2uWUBiCtp9YvlzTSUWGo0JQJWvX+hePfeYz0eTSaFIpYP58f3z16vBzEYkDFYIqyGaB7373+DXpZgmpNRCeZcv8sf/8z/DzEImDihQCkpeQfJZkP8mlAc9/lOQgye3e4xN5zy0huct7LKlEPlFbuxb47W/d96Rb5apZQuFKJv37EGkGkUiwsgsByXEAbgVwKYDZAK4kOTvg1h+Z2RzvcYf32tMB3ARgHoC5AG4iGTAbPD4yGeA733FbHQBu64Orr442p0ZVvDspoAPvRYJUokUwF0C/me02syMA7gSwcJSvbQfwoJkdMLODAB4EcEkFcopEJgNcdx0wNHQ89vGPa+FYVJLJ4OmkOvBepFAlCsHZAF7Iux7wYsU+RPIXJO8mOa3E14JkimQvyd7BwcEKpF1Z2awrArmWAOC2PFBrIFrd3cCpp/rjS30dmCKNK6zB4p8AaDOz/w73qX9NqT/AzDJmljCzREvx7mI1YPnywiIAAOeeq9ZALbjhBn9s82a1CkRyKlEI9gKYlnfd6sX+i5m9YmaHvcs7APzRaF8bF0884Y9pumht6OoCZs70x3WamYhTiULwJICZJGeQHA/gCgAb8m8gOTXv8jIAufW2PQAWkJzsDRIv8GKxkskA+/cXxubM0XTRWrJmjZvBle+++9QqEAEqUAjM7CiAT8G9gfcBWG9mO0h+ieRl3m2fJrmD5NMAPg3go95rDwD4MlwxeRLAl7xYrBTve0+6w1KkdiSTwO23++MXXhh6KiI1h1bcsR0DiUTCent7o04DANDeDmzcWBjr7NS6gVo1fvzxNR45c+cCW7ZEk49ImEhuM7NEcVwri8uQyfiLQEuLikAt+7M/88dq5DOFSGRUCMrw13/tj33sY+HnIaPX0+NaBfmOHdNYgTQ2FYIxam8HXn+9MDZliloDcbBpkz+mk8ykkakQjNHDD/tjX/1q+HlI6ZJJt/VHvoEBtQqkcakQjNHJJxden3KKpovGSdBYQVBLQaQRqBCMQSYDHDpUGPuHf4gmFxmbnh43Wyinudl17Yk0IhWCEqXTbtOy/ENnFi1SayCOtmwBVq1y3URDQ8AnP6mdSaUxqRCUIJPxb0vQ1BS83bHEwyuvuCJgBhw9Clx/vYqBNB4VghLccos/9s53amO5OLvwQlfMc44dc8VAA8fSSFQIRimbBXbu9Mc/+9nwc5HKSSaBW28t3IcoVwxEGoUKwSgF7VSpc4jrQyoF/O7vFsaeflpdRNI4VAhG6dlnC69nz9bisXpy3nn+2E03hZ+HSBRUCEYhmwV27SqM6ayB+hI04L9/v8YKpDGoEIzC2rVuRknO/PnqEqo3yaQ7Q6KYxgqkEagQjCCbBf75nwtjs2dHk4tUV9AZErt3h5+HSNgqUghIXkLyWZL9JH3HgpP8HMmd3uH1D5GcnvfcEMnt3mND8WujlM26T//PP388Nm6cDqSvV8kksGBBYYzUoLHUv7ILAclxAG4FcCmA2QCuJFn8mfkpAAnv8Pq7AeTPwXnDzOZ4j8tQQzZtKuwSAoA/+iOtG6hnxVtP/PrXbiW5ioHUs0q0COYC6Dez3WZ2BMCdABbm32BmD5tZbtPmJ+AOqa95r77qP+f2mmuiyUXCM2mSP7ZiRfh5iISlEoXgbAAv5F0PeLHhXAMgv9d9Islekk+QXDTci0imvPt6BwcHy8t4FNJpt3Ygd5JnW5vbl0aDxPXvQx/yx954I/w8RMIS6mAxyQ4ACQA354Wne2doXgVgBcnfC3qtmWXMLGFmiZaWlqrmmc0CX/96Yewd71ARaBSpFLB4cWHsP/5D3UNSvypRCPYCmJZ33erFCpC8GMDfArjMzA7n4ma21/u6G8AmAAFLe8K1aVPh7qJA8KdEqV/d3W5X2RztQST1rBKF4EkAM0nOIDkewBUACmb/kDwPwCq4IvBSXnwyyQne92cAuABAwI4+4dqxo/B68WK1BhpRZ6d/Q7obboguH5FqKbsQmNlRAJ8C0AOgD8B6M9tB8kskc7OAbgZwKoC7iqaJzgLQS/JpAA8DWGZmkRaC9nZg3brC2LveFU0uEq1kEjj33MLY9u3qIpL6Q8uNhsZIIpGw3t7eiv/c3ABxvqYm4LHHNGW0UWUybvpovrPOAvbtiyYfkXKQ3OaNyRbQyuI83/62P/b5z6sINLJUCjj11MKY9iCSeqNC4Ono8E8RbG3VDqMSPC4QtC25SFypEHjuussf+8AHws9Dak9Xl3866X33aaxA6ocKgWdoyB/TnkKS091duPUEANx4YzS5iFSaCgHcJ7viQrBggcYGpFDx9iIHDrhZZiJxp1lDAN72tsJZIFOmAC+/XLEfL3WkubnwQ8OECcCbb0aXj0gpNGtoGOm0fyrge98bTS5S+97+9sLr4rOOReKo4QvBN77hjwUdWygCAGvWFO5Ie9FFmkoq8dfQhWDePP/YwPjxGhuQ4SWTwM9/Dlx3nesmWrUKuPBCFQOJt4YtBJkMsHWrP3755eHnIvGS+6Bw9KjbpvzIEXeutUhcNWwhCJr6N2mSmyYoMpL9+wuvn3gimjxEKqEhC0E266b+FXvggfBzkXg666zC6+3b3cQDkThqyEKwdKk/pnUDUoqrr/YfY7p8ucYKJJ4arhBks8CjjxbGTjvNHVouMlrJZPA04yVLws9FpFwNVwg2bTp+DnHO+94XSSoSc8uW+WO7dmkPIomfhisE991XeN3UpHUDMjbJpH8zOgD42tfCz0WkHBUpBCQvIfksyX6Svh54khNI/sh7fgvJtrznbvTiz5Ks6s4tHR3+KaOplMYGZOy6u4GZMwtje/aoVSDxUnYhIDkOwK0ALgUwG8CVJGcX3XYNgINmdg6AbwLo8l47G+6M43cBuATASu/nVcW99/pj2mFUyrVmjT92yy3h5yEyVpVoEcwF0G9mu83sCIA7ASwsumchgNw/l7sBXESSXvxOMztsZv8OoN/7eRWXyQCvv14Y00whqYRkEpgzpzDW16cZRBIflSgEZwN4Ie96wIsF3uMddv8rAFNG+VoAAMkUyV6SvYODgyUnec89hddTp2qmkFTOn/xJ4bWZVhtLZWUybtvzanQ7xmaw2MwyZpYws0RLS0vJr//Qhwqvv/jFyuQlArguxpNOKox95ztqFUhlZDLAtdcCGze6r5UuBpUoBHsBTMu7bvVigfeQbAbwVgCvjPK1FZFKuQ3CFixwX1OpavwWaVTJJPDII0Bb2/HY0FDw4kWRUhXPRFu9urI/vxKF4EkAM0nOIDkebvB3Q9E9GwDkltp8GMDPzJ2IswHAFd6sohkAZgII2AquMlIp1x2kIiDVEDTetHmzWgVSnkzGzUTL97a3VfZ3lF0IvD7/TwHoAdAHYL2Z7SD5JZKXebetBjCFZD+AzwFY6r12B4D1AHYC+BcAnzSzgNODReLh5JP9MbUKpBxB3diVXvukoypFKijXl5uPdGcYaIaalCqddntY5Zs/33VDjoWOqhQJQSrlX22sGUQyVt/+tj8WtLVJuVQIRCqsu9u/rkDnFUipMhngjTcKY6ecUp2WpQqBSBUUryvYvt1tcSIyWkGr0z/5yer8LhUCkSoIOq9g3TrtQSSjk80CO3cWxmbOBLq6qvP7VAhEqmC48wq0M6mMxg03+GNBe1pVigqBSJUEDert2aN1BXJi2azrSszX0lLdWWcqBCJVkkwCkyb545s2hZ6KxEjxdFEA+NjHqvs7VQhEqihoFfuOHeHnIfGQzfoPz5ozp3pjAzkqBCJV1NXlFgDlW7fOLRQSKbaweAN/ACtXVv/3qhCIVNmyZf4ZRMuXa6xACqXTQPEO+xMnhrMiXYVApMqSSWD6dH98yRJ/TBrXD37gj517bji/W4VAJAQ33uiP7d4dfh5SuyZO9MfC6BYCVAhEQpFKuSmA+Y4dU/eQOOk00N9fGFu1KryNClUIREJy//2F12bBUwWlsWSzwM03F8ZmzQr33BQVApGQJJPuU15T3r+6++7TDKJGt3y5+1CQ753vDDcHFQKREKVSQKJoN/ivf11dRI0qm/W3FMnKHzwzkrIKAcnTST5Icpf3dXLAPXNIZknuIPkLkv8z77nvk/x3ktu9x5zi14vUm2uuKbw+dkznFTSqtWv9rYGFC8M/xKjcFsFSAA+Z2UwAD3nXxV4HcLWZvQvAJQBWkMxfeP8FM5vjPbYHvF6krqRSrg843+23q1XQiPbvL7xuagq/NQCUXwgWAsjtibcGwKLiG8zsOTPb5X3/nwBeAtBSfJ9II3nxRX9MA8eNJZMBfvKT49dNTcBtt0VzpGm5heBMM9vnfb8fwJknupnkXADjAfy/vPDfe11G3yQ54QSvTZHsJdk7WLz8TiRmLr3UH3v00fDzkGhks+6QmaEhd026lmKYM4XyjVgISP6U5DMBj4JdMczMANgwPwYkpwL4PwA+ZmbHvPCNAM4F8McATgcw7PwJM8uYWcLMEi3FE7JFYqa7G2htLYy98grQ3h5NPhKuTZvc2FBOc7M7zCgqIxYCM7vYzN4d8LgfwIveG3zujf6loJ9B8jQA/wTgb83sibyfvc+cwwC+B2BuJf4okThYv94f27hRp5g1ggsvBCZMcN1Bzc3AP/5jNF1COeV2DW0AkNsxZQmA+4tvIDkewL0A1prZ3UXP5YoI4cYXnikzH5HYSCaBxYv98RUrws9FwpNOu0//f/mXwFe+AmzeHF2XUE65hWAZgPeT3AXgYu8aJBMk7/Du+QiA+QA+GjBNdB3JXwL4JYAzAHylzHxEYqW7GzjnnMJYX59mENWrdNpNCujvd9uRv/pqtC2BHFrxJNYYSCQS1tvbG3UaIhWRzQIXXFA4n3zOHOCpp6LLSaqjufn4ADEAnHwy8Prr4f1+ktvMLFEc18pikYglk/51Bdu3a6yg3qTThUUAAI4ciSaXYioEIjXgM5/xx266Kfw8pHqCCvtFF4WfRxAVApEakEoBZ51VGNu/X62CepHNuvGAfBMmAD090eRTTIVApEb83d/5Y0EH2kj8BK0a/9a3ws9jOCoEIjUilQJOP70wduCAtqmOu0zGv8Po/PnRTxnNp0IgUkPmz/fHvve98POQyshmgeuuK5wR1tQELFsWXU5BVAhEakjQzpPNzeHnIZURdOjMZZfVxtqBfCoEIjUkmfQXgxdf1KBxXD37rD8WxTbTI1EhEKkxXV3AorwN3Y8dA264QauN4yaddqvE83V21l5rAFAhEKlJnZ3AuHHHr4eGXDGQeMhk/DOF5s93Rb4WqRCI1KBkEviLvyiMbd+uGURxccst/tjs2eHnMVoqBCI1Kqgv+RvfCD8PKU02C+zcWRhraor2vIGRqBCI1KhkEjj11MLY0JAOr6l1QV14UR1BOVoqBCI1LOhN5aGHws9DRieTcV14+WbPrq3FY0FUCERqWFeX25Mm39AQ0NERTT5yYl/7mj8WtKFgrSmrEJA8neSDJHd5XycPc99Q3qE0G/LiM0huIdlP8kfeaWYikidoT5p16zRwXGvSaWDPnsLYWWfVfmsAKL9FsBTAQ2Y2E8BD3nWQN8xsjve4LC/eBeCbZnYOgIMArikzH5G6k0oFbz2xcmX4uUiwoOmiQPBGgrWo3EKwEMAa7/s1cOcOj4p3TvH7AOTOMS7p9SKNJGhvmkOHtOK4VgSdHVFrG8udSLmF4Ewz2+d9vx/AmcPcN5FkL8knSObe7KcAeNXMjnrXAwDOHu4XkUx5P6N3cHCwzLRF4iVo6wkA+OIXQ09FimQy7uyIYrW2sdyJjFgISP6U5DMBj4X595k7/Hi4A5Cne+dkXgVgBcnfKzVRM8uYWcLMEi0tLaW+XCT2urqAxYsLY/v2aeA4aqtX+2NtbbU9XbTYiPsamtnFwz1H8kWSU81sH8mpAF4a5mfs9b7uJrkJwHkA7gEwiWSz1ypoBbB3DH+DSMPo7gbuvbfwwPMNG4a/X6pv925/LG4HCpXbNbQBwBLv+yUA7i++geRkkhO8788AcAGAnV4L4mEAHz7R60Wk0Ac/WHitsYLotLUBL79cGOvsjM/YQA6teLPsUl5MTgGwHsDvAngewEfM7ADJBIDrzOwTJM8HsArAMbjCs8LMVnuvfzuAOwGcDuApAB1mdnik35tIJKy3t3fMeYvE3Zw5wNNPH79uagIeeyxe3RFxN28esHVrYWzCBODNN6PJZzRIbvO66QuUdeSFmb0C4KKAeC+AT3jfPw7g94d5/W4Ac8vJQaQR3XYb8J73uC2qAff1+uv9q1qlOtJpfxEAgD/90/BzqQStLBaJoWTSf77x00/rzIKwrFjhj02fDvT0hJ9LJagQiMTUxz/ujy0dbkmnVEw2Cxw5Uhgj/auK40SFQCSmurrcWEG+zZs1cFxtQSuI3//+8POoJBUCkRhbudJ9Gs0X1G0hlZFOA/fdVxhrbY1vl1COCoFIjCWTwHvfWxjr69Mis2oI2k+oqQlYvz6afCpJhUAk5pYtc29I+bQ7aeUFtbQuu6w+puyqEIjEXDLpppMW0+6klZPNAi+84I8H7f8URyoEInUglXJ73+c7dEitgkrIZt36gEOHCuPz59dHawBQIRCpG0F73998s9YWlGvpUuC3vy2MNTXFa3fRkagQiNSJVMq/O6kZcPnl0eRTDzIZNyU3H1n7h9GXSoVApI50dwNvfWthbO9ezSIaq6AB4ttvj9+mciNRIRCpM9de64+tW6cuolJls+68h3xtbfVXBAAVApG609UFzA3YynHBgvBziatMBjj/fODVVwvjcTtnYLRUCETq0JYtwbOI2toiSSdWstngVtWiRfXZGgBUCETqVtAsouef15TSkVx9dXC8XtYMBFEhEKlTQWsLALdNgjamC5bNAv39/viCBfU1S6hYWYWA5OkkHyS5y/s6OeCePyO5Pe/xJslF3nPfJ/nvec/N8f8WERmrffuAiRP98Xrt6y7X2rX+WJzPGRitclsESwE8ZGYzATzkXRcws4fNbI6ZzQHwPgCvA9iYd8sXcs+bmc5XEqmwW27xxw4c0JTSYtks8MgjhbFzzon3OQOjVW4hWAhgjff9GgCLRrj/wwD+2cxeL/P3isgopVLB/dt33RV+LrUqnXazhPr6jsdOOim4hVCPyi0EZ5pZbqbtfgBnjnD/FQB+WBT7e5K/IPlNkhOGeyHJFMlekr2Dg4NlpCzSeLq6gClTCmNHjgDt7dHkU0s6OoIPm7nmmvoeF8g3YiEg+VOSzwQ8FubfZ2YGwE7wc6bCHWKf39t2I4BzAfwxgNMBDDufwcwyZpYws0RLS8tIaYtIka9+1R/buLGxu4gyGbfYrti4ccPPHqpHzSPdYGYXD/ccyRdJTjWzfd4b/Usn+FEfAXCvmf3X9k15rYnDJL8H4POjzFtESpRKuX1zit/41q0DBgfrf0A0yKc/HRxfubJxWgNA+V1DGwAs8b5fAuD+E9x7JYq6hbziAZKEG194psx8ROQEuruDVxhv3AjMmxd+PlFqbwcOH/bHFy+u34Vjwym3ECwD8H6SuwBc7F2DZILkHbmbSLYBmAagaEwe60j+EsAvAZwB4Ctl5iMiI+jp8e9SCgBbtzbO+oJMxhW/YrNmuWLZaOi69uMlkUhYb29v1GmIxFp7u//NcPr0+p8umckA113ntujON2kScPBgNDmFheQ2M0sUx7WyWKRB9fQAZ59dGHv++foePM7tI1RcBJqagAceiCanWqBCINLA7rrLHbSSr54Pvv/zP/fHZs0CHnussQaHi6kQiDSwZBL4whf88Ztvrr/xgnTav600AKxe3dhFAFAhEGl4XV3BR1xee239dBO1twcvGps1S6Z0iREAAAeQSURBVEUAUCEQEbiZMp2dwd1Es2dHk1OldHQEzxCaPh3YuTP8fGqRCoGIAHAtg9tv98f7+vzbU8RFRwfwgx/4452d9T87qhQqBCLyX1Kp4DUGBw7Erxi0tbkWTfEMoQULXNGT41QIRKRAd7frOy924EB8uona291U2HykK3KNuJXGSFQIRMRn587gA236+oDzznPz8WtRJgO8613BYwJXXdWYq4ZHQ4VARAL97GfB8e3b3d79tbbWoKPDzXQKGgCePl1F4ERUCEQkUDIJPP44cMYZwc8vX147XUXz5gVvJw24MQENDJ+YCoGIDCuZdFtUBw0gA66r6JRTolt8lk4Dzc1uw7xiTU3AqlUaExgNFQIRGVF3t3tTDfL6665LJuxtrKdMca2SoSH/c2ed5baNaLTtpMdKhUBERmW4s49ztm51A8zVHjvIZl0r4MCB4OdJ4Mc/1orhUqgQiMioBW1Hke/wYfcp/Xd+p/Izizo6gFNPdQPVQa0AwG0l/fOfqwiUSoVAREqS6yY67bTh7xkcdG/Yra3A9dePvSik066onHSSGwx+7bXh7507150noCJQurIKAcnLSe4geYyk77CDvPsuIfksyX6SS/PiM0hu8eI/Ijm+nHxEJBypFPCrX7nWwbhxw9+3d6/btuL8812XzVveMnLXUXu76/ohXeticBA4enT4+0lXmLZsGdvfIuW3CJ4B8JcANg93A8lxAG4FcCmA2QCuJJmbdNYF4Jtmdg6AgwCuKTMfEQlRd7d7kz7R2EG+N95wb+7TprlP+eTxx8SJbgbSxo3Dd/0UmzULOHZMg8LlKqsQmFmfmT07wm1zAfSb2W4zOwLgTgALvQPr3wfgbu++NXAH2ItIzHR1uTUHc+acuIWQMzDg/5R/+LCbgTSSSZPcUZOPP67dQysljDGCswG8kHc94MWmAHjVzI4WxQORTJHsJdk7ODhYtWRFZGySSeCpp9wb/OLFbgzhlFMq9/MnT3Ytj4MHgdtu01hAJY1YCEj+lOQzAY+FYSSYY2YZM0uYWaKlpSXMXy0iJerudmMIhw65opDr8x8/3i30GknuXISmJmDmTPfp/8AB7RpaLSP+JzGzi83s3QGP+0f5O/YCmJZ33erFXgEwiWRzUVxE6kh3N/Db37q+/MOH3UKv+fNdccg3YYLbsmLVKnevmRsreO45ffqvtuaRbynbkwBmkpwB90Z/BYCrzMxIPgzgw3DjBksAjLa4iEhMJZPAI49EnYXkK3f66AdJDgBIAvgnkj1e/G0kHwAAbwzgUwB6APQBWG9mO7wfkQbwOZL9cGMGq8vJR0RESkcrPr4nBhKJhPX29kadhohIrJDcZma+NV9aWSwi0uBUCEREGpwKgYhIg1MhEBFpcLEcLCY5COD5Mb78DAAvVzCdKMT9b4h7/kD8/4a45w/E/2+IIv/pZuZbkRvLQlAOkr1Bo+ZxEve/Ie75A/H/G+KePxD/v6GW8lfXkIhIg1MhEBFpcI1YCDJRJ1ABcf8b4p4/EP+/Ie75A/H/G2om/4YbIxARkUKN2CIQEZE8KgQiIg2uoQoByUtIPkuyn+TSqPMpFcnvknyJ5DNR5zIWJKeRfJjkTpI7SH4m6pxKQXIiya0kn/by/7uocxorkuNIPkXy/0ady1iQ3EPylyS3k4zdDpQkJ5G8m+S/kewjGemJCw0zRkByHIDnALwf7ljMJwFcaWaxOfWU5HwAhwCsNbN3R51PqUhOBTDVzP6V5H8DsA3Aorj8N/DO2T7FzA6RPAnAYwA+Y2ZPRJxayUh+DkACwGlm9oGo8ykVyT0AEmYWywVlJNcAeNTM7iA5HsBbzOzVqPJppBbBXAD9ZrbbzI7AHYYT6nGb5TKzzQAORJ3HWJnZPjP7V+/738CdTzHsOdW1xpxD3uVJ3iN2n6RItgL4HwDuiDqXRkTyrQDmwzt/xcyORFkEgMYqBGcDeCHvegAxehOqNyTbAJwHYEu0mZTG61LZDuAlAA+aWazy96wA0AngWNSJlMEAbCS5jWQq6mRKNAPAIIDved1zd5A8JcqEGqkQSI0geSqAewB81sx+HXU+pTCzITObA3fG9lySseqiI/kBAC+Z2baocynTe8zsDwFcCuCTXrdpXDQD+EMAt5nZeQBeAxDpmGUjFYK9AKblXbd6MQmR17d+D4B1ZvbjqPMZK68p/zCAS6LOpUQXALjM62O/E8D7SHZHm1LpzGyv9/UlAPfCdf3GxQCAgbzW5N1whSEyjVQIngQwk+QMb3DmCgAbIs6poXiDrasB9JnZP0SdT6lItpCc5H1/MtzEg3+LNqvSmNmNZtZqZm1w/wZ+ZmYdEadVEpKneJMN4HWpLAAQm5l0ZrYfwAsk3+mFLgIQ6YSJ5ih/eZjM7CjJTwHoATAOwHfNbEfEaZWE5A8BXAjgDJIDAG4ys9XRZlWSCwD8LwC/9PrZAeBvzOyBCHMqxVQAa7wZaE0A1ptZLKdfxtyZAO51nyvQDOAHZvYv0aZUsr8CsM77ULobwMeiTKZhpo+KiEiwRuoaEhGRACoEIiINToVARKTBqRCIiDQ4FQIRkQanQiAi0uBUCEREGtz/B3TdSrfISH+TAAAAAElFTkSuQmCC\n", + "text/plain": [ + "
" + ] + }, + "metadata": { + "tags": [], + "needs_background": "light" + } + } + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "iWOlC7W_FYvA" + }, + "source": [ + "### 2. Add Noise\n", + "Since it was generated directly by the sine function, our data fits a nice, smooth curve.\n", + "\n", + "However, machine learning models are good at extracting underlying meaning from messy, real world data. To demonstrate this, we can add some noise to our data to approximate something more life-like.\n", + "\n", + "In the following cell, we'll add some random noise to each value, then draw a new graph:" + ] + }, + { + "cell_type": "code", + "metadata": { + "id": "i0FJe3Y-Gkac", + "outputId": "10d4d994-3b78-4512-a029-5ef0e444d75c", + "colab": { + "base_uri": "https://localhost:8080/", + "height": 265 + } + }, + "source": [ + "# Add a small random number to each y value\n", + "y_values += 0.1 * np.random.randn(*y_values.shape)\n", + "\n", + "# Plot our data\n", + "plt.plot(x_values, y_values, 'b.')\n", + "plt.show()" + ], + "execution_count": null, + "outputs": [ + { + "output_type": "display_data", + "data": { + "image/png": "iVBORw0KGgoAAAANSUhEUgAAAXwAAAD4CAYAAADvsV2wAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAALEgAACxIB0t1+/AAAADh0RVh0U29mdHdhcmUAbWF0cGxvdGxpYiB2ZXJzaW9uMy4yLjIsIGh0dHA6Ly9tYXRwbG90bGliLm9yZy+WH4yJAAAgAElEQVR4nO2de5RcdZXvv7se6aAzTO4UrAkPYxxBFCdLGkOckjE2AwoBA9HccSlzpzMhpAMkIOMjmjtyzYhDnOCSCImYJo+bvpfxsYzkMSaCPMoEKEg6dJweCTgJgyHBXGI7GcYx9KPqd//Yvf39zq/Oqa5OV3W99metXt116pyqU1Vd+7fPfnw3GWOgKIqiND6xap+AoiiKMj6owVcURWkS1OAriqI0CWrwFUVRmgQ1+IqiKE1CotonEMUZZ5xhpk6dWu3TUBRFqSv27dv3K2PMmWH31azBnzp1Krq7u6t9GoqiKHUFEf0i6j4N6SiKojQJavAVRVGaBDX4iqIoTYIafEVRlCZBDb6iKEqToAZfURSlSVCDrwAAsllgxQr+rShKY1KzdfjK+JHNApdfDgwMABMmAI89BqTT1T4rRVHKjXr4CjIZNva5HP/OZKp9RoqiVAI1+Ara2tizj8f5d1tbtc9IUZRKoCEdBek0h3EyGTb2Gs5RlMZEDb4CgI28b+izWV0EFKWRUIOvhKKJXEVpPDSGr4SiiVxFaTzU4CuhaCJXURoPDekooWgiV1EaDzX4SiRhiVxFUeoXDek0CCqNoCjKSKiH3wCUu6ImrBxTSzQVpf5Rg98AuBU1b7wBdHWVbpR9Qx62eAClLSi6KChKbaMGvwFoa+NqmlwOMAbYuBFobx/Z6IYZ96hyTH9bWJOW1u0rSm2jMfwGIJ0GbrgBIOLbQ0Ol1c37xr2rCzh8mBcPtxyzlBJNrdtXlNpHPfw6YaRwSXs7sGkTG9t4nA13NlvcyxZDLsds3MiLRSIBLFwYvEoYqUTTfSyt21eU2oSMMdU+h1CmT59uuru7q30aNUFUXD0ssbpyJbB9O4d2Egn2/IuFd2QhOXwYeOAB9tDjceDOO/mxS43JZ7N8hQCUFk5SFKUyENE+Y8z0sPvUw68DwkIv4s378fIf/pD3A/j+tWt5Xz+mLoY+leLbra1BDz2VKj0m7y9I7e2VeicURRkLavDrAD9cAoQnUTMZa+wFYwoTrZ2dwJIlHL4xBojFgGQSmDWL7588GejpiU7U+uGlsPi9eviKUnuowa8DfJkDIOjhy7a2NqClBejv5wRuLAbk8xzakZg+ACxezMZeyOf5mC1b+LYsAPE433afo7OTj8/n+bkee0zj94pSL6jBrxN8mQNZAFIpWxETtjB0dQEbNnB8ftMmYN48NtbFyOd5QVi4EJgyJVijL1cGAC8SmQywbJnq7ihKPaAGv04RoxoVZxfjO2UKh1ok3ALYq4BYDPj0p3nfPXuCj59IFCZfM5nglQGRXVhUd0dRah81+HVMVO27uwisWhUMt7S384/fXfuBDwTj/0NDQG9v0IinUhzzj8JNBPf08Laoih3tylWU8UcNfh0QZRzDYuf+ItDXx56/lEz29vI297F6ewuTvbkccMstwLRpdr++Pvbqxejn8zZBK5U6/f3BkNG6dcCNNwYNv3blKkp1KIvBJ6INAD4C4DVjzJ+E3E8AvgHgagC/BfDXxpjnyvHcjYhr4AFrSGMxYM0aoKODt6fT7MFv3gzMncu3e3ttwtZNoG7aZI2xJGXnz2dDvHlz+HnkckFdHllg+vv5djxuH18WGj8/MDRUWBqqVT2KUh3K5eH/bwCrAXRF3D8LwPnDP+8DcP/wb8XD937nzbOGOp/npCnA3nYqBdx+O++7ezdvv/123i8e58UAAJYvZ1E11zPv77eG+NZbgUceCT+f55+359XVBbzvfcCTT/JjGMOVPRLGkcXAN/p+aai7cBDZXgBFUSpLWQy+MWYXEU0tsst1ALoMt/U+Q0STiOgsY8wvy/H8jYTv/QJWIwfg7YsX2/r5XI4N7MAAsH69NexEHEdfsgQYHAx/LmPY6E6aBMycCezaVbjPk08CH/0oN3T5j5PLcWdvLMaJ4FWreCE6cYK7fU+eBI4e5f1isaBhv/JK3ief50XKDR0pilIZxiuGfw6AV5zbR4a3BQw+EXUA6ACAKVOmjNOpVRc/Pu/H5VtbrWEH2JCLty+GXX56eqwXn0iwdx5l7F22bAEmTgy/L5+39flRyIIjuYG2NrtYJRJ8brkcG3aAf7tXHBrWUZTxoaaStsaYTgCdAGvpVPl0Ko4bvkkkbEzdrWnPZIIhkksvBZ591gqeAbbsUiDirtmnngp/3pijkZrP25LMRMJq6RhTmMiNws0XZDLBRWZoyCZ6BwY4XzAwYI295Bs0rKMolWe8DP5RAG9xbp87vK2pccM3uVwwublsmd1PDDHARlxCPOLpA9aAxmJssLdvDzfYySSwejV741u2BOvvL74YmDOHDXdvL1fpRBn9CRNYmK21NVj109sbvCJJJPjcjOFj5s7lfMPAgO0EFu9fwzqKUlnGSw9/G4B2Yv4UwH9o/J6NZMJZciWmnsnYGbWA1bgB7OIgRtSN78diwDvfWejxA7zfnDnAggVsWJct479dFizg7ek0G3H3WNHIb2kBbrqJz7G9PWjss1lOAEsl0MyZ/NsYm0Tu6OAFbeFCXiyMsSEh1dBXlMpSrrLMbwNoA3AGER0B8CUASQAwxnwLwA5wSeZBcFnm/HI8b73hx+vTaQ7jrF0b9NB9pcorryx8rFiMDfG0aexVG8Pe+4svhksnGANs28Z/y1WElHdKWWdHh63GOXYsqKUjCdlUCti5E7j+etbnAWyJ57FjNnZvDMfpJalMxMe7Es6Sh/BLSBVFqQzlqtL55Aj3GwCLy/Fc9YpfbikG9PTTbWhD6uz7+oKVOpMn2xGGgPXsczlg/34+7tpreb/OzuhzkIXg5Ek2ug89xAtGTw//dHayhy5GO5kMDkLJZtlrd+UVAFviGfOuFydOtK+NiKt33IQuwPdfcQWXjmo4R1EqS00lbRsZN17f328VJ11vXCptXG168bLf8Q7gwAG7r3tcPs8e8+zZ1sACwUXCZ8sW4POfB77xDdtIJeEXYWiItXhcfR7f2AsSYorHrULnM8/YBO7QEHDPPYXHE6mxV5TxQg3+OOGWW7r18y7SlTpxIl8B7NzJhryz0xr+KHI5YOtWG+oRA3zhhXz/iy8WGv9166yxB+xVhhh9P8wiOQdfQC2R4GMnTOArhP37gTe9yYaQ3NfnJnQBfi5fs0dRlMqgBn+ccKWLpUO2WFdqTw/wT/8UNI4zZ9ou1zDEyEtVTz4PvPACh2Z8QwsAv/514WNIaAiwk6tWrLB5h127OBz085/zVcfSpWywN28GLroIuO8+20HrC63FYsA3v8mLmVyt5POFmj2KolQGNfjjiCshPG2alSXeujVYly5TrVzDHo+zt/7kk4WPe/75wEsv8WNIx+vmzcCjj9oKGN/4ikGWKh8pm1y61MbrRUtfavNlPu5DD/Ex/j6PP24riMKYPZtf98GDwe3SsSuPqyhKZVCDXyXE8B8+bMMkiQSXRopnvWkTV7oQAR//OG8LM6Yvv1xY+jhtGte7u962LCB+GEYSyG555eWXB7thpU9g40auyGltBW67LRgScsNJPskkLyZdXeHdv9u38/Oql68olUMN/jjT2VkY/nA9eVdGeNUqO2HqwQfDjalU67ilj4BV0lyyxHrocryrexOmR5/J8Hn5zyV9AlKR44eIWlqAj32Mz9U/xwUL+HlEptnHmKDUsmrlK0r5UYM/jnR2AosW8d+PPFJowAcHrRxxNssLgxsiCTP2118PfO97fNtPsvb12UogosKRhVGkUsFF6KKLOOYuoaEw2QUiXkRk8ImLDF4B+Pf69fxaJREti1Bbm2rlK0olUYM/jkTpzvv4w0TcWna3QeuTnwR+8AOrRrlqVfEBKf4Qkigv2h908rOfsRxDT4+N18diwdCMe3XhE1aKKT/vfz9w/Dhw5pnW+3d7ELq67Pao6VmKopSGGvxxZO7coO6877HH4xwbd9UkpTFp7lw2uA88YEM43/52MC7vG1x/qHmpE6fa2vhcxFDncvzY999vxyMePhzsEHaHocg5CpKUnTGDj5OrlqEhK8l84AD/nUxa8Tb/sTZsUFVNRRkTxpia/Hnve99rGpGlS42JxSQwEvz58IeNiceD2yZMMObpp/nYu+4yhij8WHe/kbjrLvs88Tjf9lm71phkks/1tNMKH/vpp3l7LGZMIsH7u8f65xmP809LC59r1OsgMmbmzPD3iCj8XBVFsQDoNhF2VT38MlMsVJLNclNSVB192NSpG24IjhhMJoPSBMLb3176OYbNwvWRSp+o1xJ19dDZyTH697wH+Od/tlcpUiVkDDB9OjeXhfUUJBKsCBr2HrlXEYqijB4yUUXTVWb69Ommu7u72qcxKoqFSqKGfBejpQV44gn+W+LYra0c2tm1y44fBDikM3Gifc6RKl0qUQnjJqUBNtCiiuk2mrk9ALEYa/wDHMY6+2zu0A17j266icNKiqJEQ0T7jDHTw+5TD7+MFBvO7Q75dpudojjrLOC667iL1RU0k0WgvR247DJbBy8duiIxPFKli9sEVi78pHQux1VBcrWwfLltBpNzBrihbNMm22HsSj67tLaW93wVpdkYLz38pkBCJfF4uA6N1MInk8DnPse/o4zba69xwtKfSesuJE88wV6v/5xhC894MHdu8HYiwSWeouu/fDm/ZhcpzZTzHRy0lUn+exNW8qkoSumoh19GouLaAHvqQ0M2Bfn66zam7SNVKq5evFSquAuJeOlSOeM+50gx+kog+vrr13NoZtYsDuW4Vxrz5wPf+pY9RuQapKvYTdP6bNigpZmKMhbU4JcZGfO3fHlwqMgtt9hQxuAgDwtxRxcKF14IfOpTQUPpNjSFGTw/PFNs4ak0HR3W8K9YUXil4YdlWlv5/Zo2jXMQMq83zODncpzLkNcFaEeuoowGNfhlxu+mPXQImDSpMAn56qvsAW/ZEtw+c+bIFTJRhE3UqiZh1UCZTHDgy86dwfdg6VK++vFr+QHef+NGXjAl4StNYGvW2IVGUZRw1OCXmfXrg7fvvpuTr4lEMBa/Z48dOC6GLZm0EgSjNdi1KEkQdaXR0mLP89VXg8fs3w88/DBPAlu5MnhfPm/DYoB93/J54Oab+W81+ooSjRr8MpLNFiYWjWH542SSh4i/+GJQC16Gi0+ePLb4dLEKoWoyUript5cXP0ESv6+/XvhYUsoZFu7J53mK2KFDvGhIOE1RFIsa/DKSyUQPEM/lWFpg8uTgqEJj2MsVHfpTpZRmqlrBXQTktz9Ifd264DGxGF8ZXHKJlWMAgguASDgAtolNjb6iWLQss4yI0fXLCWWoSSplb7vs2cPhmGz21J9bPOc776yNcM5o6OjgMI4Y566uoOCa5DXmzQP+8i/Z8BPx7899jsNlYWWcpYrVKUqzoB5+GRGj29XFyUUZaiIDQ269leP4rtSA0N8/9jBMLSRqTxU34exz/Lh9PydMAO6912r5Azb848f9/b4ARWl21OCPkpEkCdzaeJFDOP104Mtftt2yuRzw1rcCv/iFPY6otsMw5cY38G7C+dZbg8nsF16wYRvpxgXYuO/YwftJ+eqcORwiW7BAwzmK4qMGfxS4ejillAK6zUQ+b35z8Pbs2fXrnY8Wv6Jo3jybcO7vB77+9aDomrx/MppRBqi49PdzV7KMbZw2bfxfl6LUOhrDHwVdXcDJk7Y88JZbouPuUjUTZuxbWri5SuL9Mjy8WfArigArDxGP2yldABt4kY1YtIjDY/5AFWFoaPzlJBSlnlAPv0SyWY4ju0jnJ1AY5pEEru/hz5ljK3JOpbmqEQibxCXyEKlUYZexP2Dd9/DdKwGi0mWUdXau0myowS+RTCbcszx2zIYn4nGrDQNwqALgGP7+/TwbdtIke2w9J1nHgl+LDwQN70g6/AsW2GlbUpbpzv3N5bh715WU9ge212KjmqJUGjX4JZDN8mi+RMJW14jq5eTJNjyRy7Eh2rDBhn2SSeAnP2HPXg2MRRa7KMNb7L0RsTVZZI0JevxuPb4gdfzy+LXaqKYolURj+CMgBumBB9iwdHSwUf/7v2cj0d4erL0XXXq5GhgcZONTLcniWqeU9yWbZSE2yZek01zJM3ky8K53lfY8+Xzw8YtJWStKo1IWD5+IrgLwDQBxAOuMMV/17v9rAHcDODq8abUxxuulrD2yWVa9dKdUyUAPF7f2XvTcXV59tb46YceTkd4X9wpAQmZuvf3Ro/4jRuM/voTcVHJZaRbGbPCJKA5gDYAPATgCYC8RbTPGPO/t+l1jzJKxPt944Y8kjMX4R7plXdza+9tvD2rDABxzrqZkcS0z0vviXgFIyCxqaAxgBene/W7OmwhEfFUQFkaSnIuiNDrl8PBnADhojHkJAIjoOwCuA+Ab/LoibCRhLscGfdq06LmxrpGJxYDPftZeETRrknYkir0v/gIridowkkleXMWAf+ADtnnLGOCeeziX0tVlq6c0fq80E+WI4Z8D4BXn9pHhbT5zieifiej7RPSWMjxvRXFHEsaG3yU3Dixe4h13WB2cri6bPCQCrr2Wq3LGopHTzGSzwG23BXXxJfm6dCnH713jLzN0Af6MPvGJ4ONJGe2GDbaqJ5HQ8JrSPIxXlc52AN82xvQT0SIAmwD8ub8TEXUA6ACAKfLNrQJivMUoiDCXtPC3tQW9xP5+9vyfey54zI4dwPbtWpVzqshVlkAEXHEF51UAvpp64YXg/amUDdf4uFdqcnv+fP1clOahHAb/KADXYz8XNjkLADDG9Dk31wHwiuZ+t18ngE4AmD59ekiPauURz91tmMrngYUL2XsUDXep2pH79+4NNlgZw5U67lWBGpbRIQnd/n6+nUxaYy/5Ffc9J+IJWlFyFkRck+83fSlKs1AOg78XwPlE9Dawof8EgOvdHYjoLGPML4dvXgvgAGoUXxJBpA+kkqOzk6cruZU4UUM55MpAq3JOjXQaeOIJ20Aln4HMyvWrofJ5vqIK+ywA3t7Xp8lzpXkZs8E3xgwR0RIAD4PLMjcYY35GRF8G0G2M2QbgNiK6FsAQgF8D+OuxPm+lSKVs3D6R4DLA1lY2OitXAtu2FRr7RMJW8uTzbFhaWgplAZTRE5bQlfyKG9uXcE3YABq5Xxbe3l4r46Cfi9JMkIlyh6rM9OnTTXd397g+pxvOIeIKmzlzgMsus2EFH9FuufhirhBpVn2c8ebmm4PyCr7Egs955/GwlEOHgl24S5dyYl0/L6VRIKJ9xpjpofepwbesWAF88YvWS4zHOXYvhqUYRMDEiZqcHS+yWV6IBwaCA2Wiwmvi4fsKpm7YTT87pREoZvBVWsGhra2wzO/YMQ7ZjIRb062MD26eJZnkBVrklP1affl8/MVAxNb0s1OaATX4w0gT1aWXBre//HIwVhyG6Lhrcnb8yGT4c5Ewzvz5fDUmHr7kYFwSicKZw7KvfnZKM9C0apn+iL22Nm6akpi8GHm3cxYALrwQ+Nd/tQ1WLS3BGasaEhgfwjT1ZRGQBPpZZwFHjvD+RLYLd/ly4NFH7X5ubf+KFfo5Ko1LUxp8X0vlyitto44bJgiLBc+cCaxbV1gqqIwvURo8iYQ1+q6wmjEsupZOs3Hfvdt+/m5tv8pXK41MUxp8X5L31VcL9/FH7QFsTMTAqzGoPmGfg9/85iJaOuk0l8xu3gzMnRus7Vd9fKWRaUqD74cDFiwAenpsmGbCBOC++zhMk0rxfYB687WOhHSiyOXYm587l5UzBwf5mGnTbG1/Pl/6iERFqTeaxuD7ypaiYQ/wF371ap6VevbZduas0NnJ9x07VnifUju4C7k0wbm6Ofk8x+4fe8xuHxjgBf+CC4LhPEVpRJqiDj9sjB5gt8noQhFHk/szGeDEiWCjTiIB7NqlRr9WkYX98GFeqKU2/+1vB156KboT1yUeB+68E1i2rOKnqyhlp1gdflN4+FFj9GSbGAGp1e7qsjNTfQMxNKTx3VpGPpeVK4Of68c+xmE6d3pZGKp9pDQyTVGHHza/1N8mjTsTJvAx/f22ztslFlNjUMvI1dzWrXZbLMbyCY89xiWYMt/AnXUA8Od/ySWc0AWCc3QVpRFoCg8/qoTP3QbYv3t7g16g1OXHYsD996t3X8v4aqcAf5YilDZ3LvD447w9mQRmzbL77dwJ7NsH/PSnhSE+/cyVRqApDP5IbNnCDVZSopfJ2KRfLBbUwtcvfm0jV26uJn4sxhVX2SwPqpG4fi5nB9RIL4YYeqG/X0N4SuPQ8AZfpletW8df5ESCv7zHjwMHD3JMXgzDI4/w77Y29v4GBvi3lmPWD24F1oYN/JnH45zE7eoqnFMsE8uidPTl6kBRGoGGNvgSzz150m4bHOQqmyg2b+YyTfny12gRk1IEachqb2cjv3EjTygTcTXAVmYNDfHtqESuXB0oSiPQ0ElbfyZqKcydGxTmyuVURbFeSac5FDc0xJ9jLscia3feyZO07rvPSiuLrj5gE7kyMF1yPNmsJnKV+qahPfxUKjiFqhixGA886ejgL7TbiZtKqahWvRImsiafYSZjjb2IqJ15JvDss8D73ge8+932Mw/r5dD/BaXeaFiDn80Ct91m5RIAvox/29tY7dKHiEv3gKDWykUXcaJPv+j1SViFljRnpVJ2SHosxsb+wQf5uIMHefCNuzio1o5S7zSswe/qKhxLKElb8foFXw9dqjkGBvjSX5qz9Iten7gia66nnkiwJ//UU/wZ/+M/Bo+7+27O56TTwVnH8r/iy3UoSq3TsAY/DGOAAwfsWDv3Un758nBvTvbRDszGwP1sc7niCfxDh3hxWLXKlnPG47YxS0M8Sr3RsEnb9nY73cgXw5KyPEnKucYeCHbhtrQAa9Zwok+/1PWPfLbFBNJOO41/i9TG+vVc1y9y2X190XIdilLLNKyHLw1UK1dyY5WPNFWtWlVoxKM6c5X6x63TX7fOlmW6uGW8sRjLY0vS35VOlmSw1Plns/q/otQ2DevhA/zl++1vo+/P57mdPurYZcv0C9yISI3+xRcHSzHPO6/Q8x8asosCEXDDDTYn8Nhjdo7uAw9wiEdLNpWxUsny34Y2+Nks8KY3Fd8nbNqV0thI4ra72+ZoWlqAz32OvXUXt2wzkQBaW1l2+corWXPJrfPX0I4yVuR/8447KuNANGxIp7MTWLIk/JLdZcGC8TkfpXaQ+Ls/xDyd5kStO//AJZcDFi+2/1OPPMIDcdw6f03qK2Oh0uW/DWnws9ngFzOKOXO40UppLvxmLDdpP2lSYdkuYD19f/v+/ZrvUcqH/79ZbgeiIQ1+JjPyZKNkEpg8WRNtzUhUUj6b5eSrCOe5Iw8TiWATnyAKq9LQpR3ZyliodMFIQ444lDiYNF6FGX/RUEkmtZlKCTZkxePA1VcDO3ZYTfyPfcx24QrnncdJXPHCtC5fqQWabsRhOg3ceivwgx/wUPLduwu1dGQRkJGG+uVsbtzYKQDMmMHx+a4uHl7/7W8XHvNv/wZ88YvsPHzkIyq9oIwet1sbqHxosCENfmenTbwdPFjdc1Hqg6jY6aZNwWEqLrI45POspy8VPpq8VUrBvaqUvJF0c69ZU5n8YlnKMonoKiJ6kYgOEtEXQu5vIaLvDt//LBFNLcfzRrF5c+n7yoATpbmR2KnbUZ3JcFgwzNifc07wdi7HQnsLF2o4RykN96pycNBKuQwNcYVhTdbhE1EcwBoAswBcCOCTRHSht9sCAP9ujDkPwD0A/mGsz1uMiy6Kvs+VWiDiskz9cipAYbNdW1twyLlLmDTDnj08ZUtRSqGtzQ7k8anUHI5yePgzABw0xrxkjBkA8B0A13n7XAdg0/Df3wdwOVExNZOxMWlStFYKEV8yxePAxInq3TczI3U0ptN8aZ1MWsMvInrXXx9+jOSEFGUk0mkeyBNmq1wJj3JSjhj+OQBecW4fAfC+qH2MMUNE9B8AUgB+5e5ERB0AOgBgypQpp3xCbW1szPv7C2WQW1pYP6evT8vnmplSB5p0dLBEsujny/8NADzzDPDkkyOXACvNR6nS2e3tnCdybRVRkyRtjTGdADoBLss81cdxa1nlS+p+WdXIK6PpaHT19IFg2S8RMHMmbxsaslO1lOZlNNPRwsT8jGHZ7ssu43kctdZpexTAW5zb5w5vC9vnCBElAPwBgIqOhva/pIriMpaORknmikeWzQKrVwe9f23Aal6inIkorz+dZoPvKwPUqrTCXgDnE9HbwIb9EwD8COc2APMAZAH8dwCPm1rt+FKagrF0NEoyVwx+LsfGftkynX2rhDsTxf4vsln27n1qUlphOCa/BMDDAOIANhhjfkZEXwbQbYzZBmA9gP9DRAcB/Bq8KIwLOoZOiWI0V4H+/9GaNVw6l8txXki+1MuXW+/fVc/U/8HmIcyZWLEiOoSYydieDoEIuPfe8v+/lCWGb4zZAWCHt+1/OX+/AeAvyvFco0G9LaUchP0fSTJXKnJ6e7m7e2CAb8vs21RK/webEd+Z8L3+Eyd4nvLZZwOzZln9JoGIrxrLTU0lbctNpaVGleag2P/Rpk32i+p6aWecAbz//TwtS4594w2V8Wgm/KtC8fpPnAhKcG/dymW+//Vf3LFtjL1qLDcNafCzWauBIo0N4m1pMk0ZLVEJXl9/x+W113i0Zixm66yNATZu5Coe/f9rbKKiC+k0e/YuxrAw39q1rN9UyfBfwxn8bJbLmUQpM5HgdvfWVuD22/XSWhk9UQleWQj8fg8XGbIiDA3plWYzUKxSp6cn/JhVq4C/+qvKOqQNN+JQ3mghl+MxdH19hR+AopRK2IxjWQiuuCK6s1tIJLh7UoXVmgNxBuJx/uxlyH2xWR0HDrD6aiVnIzecwZc3WkgmeZv7AeiXTikX6TRX5rj/c/5c3GSSq3pcYTalsXGH3Btjh9ynUsH/D1+rya/uKjcNF9JJp7k7Taon3HipjqJTKoH/P/f889wpKcyapaM0mxFRXM3lbGShr4+H5l33Ox0AAB1tSURBVKxda1VYEwkrjSzVXZVySBvO4APR9dXafatUCnfM4Z/9WfC+yZPDj9EekcZFPttUKpjwT6VYVVWMfT4PfPKTwPHjrPI7aZIOQBk1+kVSKon//yVVYQBXhvmCfa2thdVh2iPSuPifrYg1plJcOHLyZHB/mab2+OMc+qvk/0HDGXz9IimVJOzL7Ddcubz97cHqMPnyHz4cLCLo6lInpVHwK3REduPmm7kXw0cchHyeu7enTVMPv2S02UqpJP7/1+bNPK1I8CswDh2y2/v7+Qudz9uqHYB/b9xo1TbVSalfsllezP1xl9ksD8cZSUFMBp+owS+RsaggKspI+P9fc+cWlgK7GMNffpm0lsvZRWHhQi4ZPnyYqzjUSalv3Ku/RAKYPdvmbzKZQjVMosIFoFKDT4SGM/hjUUFUlJEI+/+aNo3DNnv2FO5vDPDpTwOvv87VO089ZSsxpIIsm7USDeqk1C/u1Z8xLJlgDHv2990XVFgF+D7f6Fd6mA7Vqkrx9OnTTXd3d7VPQ1FKorMTWLQo/L6pU4Ff/MJ+seNx4JvftJO0XKkG/291WOoHdzCOb7hvuomT9zffXHifa/RjMeArX+GY/6lCRPuMMdPD7mu4xitFqQZ9fdHdti+/XOjF9fSwcbjjDv4N8Je8txf44Acr33GplJ90mpPyUT50Rwdw//3RM2xlBKuGdBSlxmlrK5S4jUIqecKkPhYvtrHe/n6N59cbPT2FBl8MfDZrG/BuusnuR2TzOZW+qlODryhlQLoqRaUVAH74w2AFj/CZz3C5JlGws9LXWal0Ak8pL9ksV1u5xGL8OT7wAOdpZJYCwIt7Ps9e/XgpqKrBV5Qy4Xdyd3YGPTnhnntsK30iwWEAOa6lhT37WIzn5Kp3Xz90dQWv8GbMAC6+2FZg9fez7tLy5XaAznjnatTgK0qFkLi+b/Bdr39w0E420gqz+sLtuAaCdfYtLbyQA+zZSyL30UeB3buD+vjjiRp8RakQbW3WY5fwTViI50c/sgZe9Z7qA7/jet684CCc97yHf8sivnw5G3tXDbMan7NW6ShKhZAv+1e+wl7d6tXAuecW7rdrV7AiJ5tl7R2t0Kld/I5rgA2/JOS7u+1nKhLaiQQv/NXMzajBV5QKIoNTAG7OOno0fD+pyOnstGWZl13Gddtq+GsPf75Ge7sdhiMNVhKzl89PqnVGGpZTSdTgK8o4kMmwAYiq0c7nebj14sUc9hGDsXat1uPXInL1duedHKuXstrlyzmMJ0b/0Uf58+vq4nJbY+yYy2qgMXxFGQdSqeJt80TAD35QqLdijOrr1AJhkuvyW2L58TgPN1m1Cli/nqU2JGYP1IbGlxp8RSkDI81giKrYEYyxypouRKqvU22iJNezWfbopQInl+MrsmQy+DknEhzyaW+vfgWWGnxFGSOlzGBoa2MPUDz4MOMvYlpurPfSS4ELL4x+3mobkGYgTHIdCNfNMSZYiUUEzJ9feFVQLTSGryhjJMoguKTTPM0ombTdtaKZnkjY2xMn8sg7gB9v1y5O5PoJXFlkRItHY/yVw0/QSlf0wAAbeyJelFtaeJ9kMvh5trdX+QU4qIevKGOklBkM2SyHdVavthOvHniA7zOGY78AyzJ85ztBr1ESuN/6lm3P10E/44eIom3ezPMP0mkWuROMAQ4eZAnkvj77+cvYy1pCDb6ijJGROmTDQj6A1cCPx9nQ79zJhr0YUr6pg37Gj2zWjqn8yU9Y8fKnPw2G5HI5O8pQjpHPVxbpWliQ1eArShko1iHre+Pi+V1yCfCrXwEvvghs2VL6cx0+zL9VhmF8cD+/XA7Yvz94f1hivVavwMZk8InoDwF8F8BUAC8D+Lgx5t9D9ssBkIugw8aYa8fyvIpST7jeeCLBJXthEgtRuAnefJ5DOxs2sBEZy6AMJRo3IZ5KRe9HxINvWltt7iadrt0rsLF6+F8A8Jgx5qtE9IXh258P2e+kMeaiMT6XotQlbsjn8GEu3RsNYaWcAwMcZliwwMaNa8GDbAT82bRSchnGhz7Exn7xYt4nkeA8TUdHbV6BjWnEIRG9CKDNGPNLIjoLQMYYc0HIfr8xxvzeaB5bRxwqjUg2yxU3xWL1orfiN2FFIZOSaiVOXO+sWMHVT7lc8d4JwGrnuEn2RIKrq6r1WVRyxOEfGWN+Ofz3MQB/FLHfRCLqJqJniGhOkRPtGN6v+/jx42M8NUWpDVwxtHQaeOIJ1smfM4d/r10LfPjDwfr7G29kPXWXqVOtgXFxFRiVseOWYYrgWRQy18DfVqufxYghHSJ6FMDkkLv+1r1hjDFEFLUWvtUYc5SI/hjA40TUa4wp6Cs0xnQC6ATYwx/x7BWlxolqyvK9v0OHgEce4b/zeeD003l4Rk8Pe/qJBPDqq3y/zD/N5Xhfd2qWMjqimtfmzePKqR07Rv+YLS0c91+xorbCOUAJBt8Yc0XUfUT0/4joLCek81rEYxwd/v0SEWUAtAIIaSRXlMailGqNbBb4+teD2772Nf6dTHJSEOC6/XyeDf6CBTwDNZXSGP6pIuE1WYyfeIK3ywJNxJ/bSCEdIvu53Hgjx/SljDOq87pajDWksw3AvOG/5wHY6u9ARP+NiFqG/z4DwKUAnh/j8ypKXRDWpemTyRQmBSVUMDjIhr29vVCOt62tNGOv+vrhdHVZBdP+fr7tl2C6xv788wvDO8bw1deiRRy3v/9+vip7443indfVYqxVOl8F8D0iWgDgFwA+DgBENB3ATcaYGwG8C8BaIsqDF5ivGmPU4CtNQSljC9vabIjGR4Zl+N2egNVyicdtZQhQOHpvJJ2fRiebtb0PMiw8mwWeey6437Fj7J2LnpHv2R86FK5/NDTEi7I8rjvqMJGosVCbMaYmf9773vcaRWkWZs40hs2E/YnHjVm7lu9/+mljTjuNt512mjE33WRMLGb3TSR4H9kvFjMmmTRmzhw+Rh7vrruq+zrHm6efNqalxb5PsRi/1xMmBN8/uY/I/u1/HrGYfS/dbaedxs9jDL+/sg8Rf07jDYBuE2FXVTxNUapMNgs8+6y9TcQVPLt3W6+9qysYJgCs+BpgK0Nk0IqEg7Zu5asHCQVJMrFZwjsSohHyeQ69iPCZSz4fbHBzIWJv/TOf4feSiPMr117LCV7BDeHVmnAaoNIKilJ1MpmgbPKiRRwLlth7KhUME0hp5t/8DSd783muDGlrC4p6AXxMPg8sXFjbycRKIQZ4JI2iYrjlsq+/zn8bw4vv9u38/m7cyEnfUkJ41UQNvqJUGb8Nv709WM7px/fzea7YmTCBJZddhcbbby/0To3hGLMkE5tpipb0PXzhC3zFJItmMgmcdx5w4MDIjyHHyKIhnxVgP5f+fmDlSu6daGurXckLNfiKUmXCvMIVK2y1iDG2/M8tFXzjDTbiMklpzx7e5hOP81XC3/2dNV6SDG4WnnkmeIW0ejX/fcstvEAmk8Cf/imHe4px+un2s9qzJyh6t307/9Ty1ZMafEWpAfxmLN/rX7WKPfkTJ9iTBNiArVvHP2EyDCLRIBr8btjohhtq0yCNhc5OFqY7+2xg1ix75ZPJBMXqjOGFctMmu5hefTUfs3cvcPJk9HPs3w/8wz/YipwdO/ixYzEbPqvlqyc1+IpSg0TFglesCJYMRuntzJwJXHWVPTabLQwbNQrZLC+Crre9ZYvVGFq1ij14d5g4EEzcbt0KPPww79vTw4toWNOVlMQC/L5KojyVCuZHavXqSQ2+otQoYRIMbW1B4xUl7vWHfxiMI/sLCGBb/4HChaVe5uVKriPMKxdvu6+PX4tbiw+why85Dclr9PVxwlzCZKkUD6b5+c+BM87gxUA0kYDgZzRtWu2/Z2rwFaWOSKfZk7z77uIt/5ND1K/EOPnyv1JxIqGjnh6uCpJttRqPBgrLLl1cjaHeXuCll+yIQoBfV1cXV9hIWEa0731DftllwPPPc4xfZhH470mxITi1ghp8RakjRHdHjH2U0W9tjX4MVz5AQhoiL7BkCYeJ5HFrOR4NBHMdsRhX3lxwQTCG39tr9YhEoK6jwxpoV8/+9tvZwLuv119UBgdr+z0phhp8RakjwnR3wujr499hoRl/Apd4+L5YGFHQ660mUSGmUurely8P3r777qBR7+srnnD1a/mTydqN0Y+EGnxFqSPa2kobjnLiRHFpZj+e7yceYzE76SnM6x1Pol6HEBZKcReIuXOtZw+wJs7ll9vH8SuifGnjdBq4915bAbR0aX1694AafEWpK9JpbrZasoSNcTIJ/PEfFzYQ3XMPd4VK6Ka/nz3duXNtqMNP6gI28Xj4sJVjrnZYJ0xiWraHefVhC8TSpTwL+PXXwxvPRB5BupH7+3nRW7OG3xNZCHt7+bHqFTX4ilJndHQEK0IA4IMfDNaaS9hHQhH5PPDjH7OnW2wkopvY3bSp/GWGo63+yWZ58Ukk7OtJpYp7/P4C0dXFiVY3Di8qltks/x4Y4Cun2bPt+5XP88K6YMHIMw3qBTX4ilIH+IbSDWNks8A113B1zSuv8LaWFi4tbG9nz/7HPw4Kg7mGK0w+GLBer7ttrK9hNFLN7v7xOOsBSblkmAGW9yiVsgtdLMayx+5iCADz5/MxH/1oUCZh27ag5r27cNZ6jX0pqMFXlBqnmKH0h6InEsBHPsJ/d3WxgTzzzGA1D5E1XK6HC3CcevXqYBNRuZq0Spn+FbU/YDXngUID7L9Ht97K1Uy5HNfRu3kPWQyzWZZCcDGGFTB/+EM+1l04a73GvhTU4CtKjVPMUPolg0ND3DUqBn7t2sLSzQ99yFauLF9eWHK4fn1hSKQcxs5Pjo7kKbe18QImpaNujbwknVMpm3Nwz3n/flt5MzTEVwcAe/uAvaLx35sJEzhGv3Rp4WuuZ0MvqMFXlBqnmKEMk/91jZhv0GIxa+xlYpbP2WdzclLKNsvVhHUq0sGiUZ/Lca28VAvJsW4DmcwHmDCBk9O7dxdepbhXM8kkHzc0xFc9s2cHK3AawcD7qMFXlBqnmKEU+d+uLg5PHD1a/LE++9mgGmc+H4xZJ5NBD1eqdcqVsBxNN6ovejY0xK/Tv7qRkM/ChRz2kQXxyiuBV1/lpCtgw1Tu4y1aZI9pRAPvowZfUeqAYobS7RiVjtIw5sxhpUcgXI2zp6fwMUdbrdPZaefuyrSukYhKGsusX1/fXwibIwDwY8kiBfDrktmzLnLMSInjRojd/46o2YfV/tGZtooyetauNea88+xsVpmtetppfN9dd9n5q08/zbfXrg3O1G1psfvIY374w/xbjnHvd/dz572683ijjnn6aZ4vG/bcTz/Nc3nlvmSy8DHcx5Z5vu5rj/qZMSP8fPzHducIj7R/rYAiM23Vw1eUBkJq9N3Y9vz57P3fdpv1hmUcH8BSyq73299vPe6VK7lU0Rg+RuQXXI1+8X43bw6ey+bNwXOJqpl3wzZu2KirK6jhf801tukqTK1SwlR+3kIGx8iVgkgmj+Sxj7aqqB5Qg68oDUZYzP/mm22CVgy6b1Rdjh0LJjiBoGHu7+ckqjHWkPsSBnPnBoeq9/eH69S4cs+i3ZPNBuf4GsMLz7ZtwaYxt/b+8GFO3Ep1jjxePM5GOxbjeP1FF5X2Po62qqgeUIOvKA1IKcnRbBZ47rnC7fE4yyv7zUo+IrQm3q+UUQ4N8e9p07jaR4xvPl8oxCZDRFau5KSzMZxcnTevcCGSx5GFA7CVRpJ8TiT4Kqe1la8+XIkIAHj5Zf7ZsWNkiYZaH0h+KqjBV5QGICq5KNtbW9lLHRxko3jsWKEcAxHwgQ8AX/0q33YTn2H483G7uuz+xliD6g5p2bkzmMyV8xP9fukCBoonbLds4Zmy7tQqY+zrEekJed0y6EQYHOTzdRPSxaQmGgU1+IpS50R14vrb77vPDjdxm7MA4NxzgTvusMa4szPa2AL2PpmPC/AgEX8R6O0NPs+WLSxnIAJkrnSCq5fT3s7G+pZbgouOxOL37LHP43PsGHcfy+u+915+3evX2wUhmeTfjRajHwk1+IpS54SJhYV1n/b1cQw7bFbrkSMsFHboECtKPvBA8YlagjFsmDOZ8CHp4uW7bNnC82PnzQsOYrnuOmDGDNs929YGfPObbKgnTuSxjU89BRw/bh/rrLP43AVZANx8xc6dwEMP8Xm6EsdAZQTiahk1+IpS57jJxXicPW2Jo7vdp2LQXAVNl8FBjqVHzckV3FBLLGYrdVzBMpm45cb1XcQgS0LVGDbMs2bZBilJwMprCaul/+AHge9+lx8jHmc5Y7efAOCrmc9/nq9wXInjRozRjwSZUpbxKjB9+nTT3d1d7dNQlLpAYuFuZ6woTPqdpG5lS0/PyLH6YkyYYEMhnZ1Wp9+vpFm5kvVtXn7ZHrt2LT+/6P0QAZdcAuzbZydwFTNPM2cCe/fy4uFKIwCci3BfUyzGv/N5fl/uvDM4D6CRIKJ9xpjpYffFxvtkFEUpP+k0G7D2djbC8biNhS9bVijHsGwZJzanTAE+8xn2oEVigYiPf897ij+nG79fsYKNt+jIu4NKenuB3/6WyyHF8MqVQWur3WYMG3u535V88EkmOcTzxht2MtfWrZwTADgUFPOsm9TjixZ+MzKmkA4R/QWA5QDeBWCGMSbUJSeiqwB8A0AcwDpjzFfH8ryKooRTapjCT+h++tPsgZ95JodI8nng5z9nj3n79uBELQm1xGLAf/4ne9r5PBtSN/Ha1sZevyv3EI/bxejECeBLXwp64uLZj+Tdp9NcWukLxclCI967OxnMrSBqVsYaw/8XAB8DsDZqByKKA1gD4EMAjgDYS0TbjDHPj/G5FUUJoZRSQjfR+8YbPNjbmGDj0sAAMGkSx8lfeMEa+dmzWS9+cBB48EH7mENDXOXjhpD8AeLG2Dr5xYvDm75EAKEYx4/bihsZtg4EcxXuZDAJdYnyZjNU5IQxJoNvjDkAAFTs2guYAeCgMeal4X2/A+A6AGrwFaWCFBP+cpOsbvJWYv+uF+5W7BCxAmWYoY7FCsXILroo2H0rz9XXV5g3iPLqYzHgne8EXnzRXkkcPGj3TSY5IevKPAiVHtlYb4xHlc45AF5xbh8B8L6wHYmoA0AHAEyZMqXyZ6YoDcpI4wQl9LN8edAgA+zBS3nk4sWFIZe9e8P1atasKWz6uu++wnPbsIErcfzHiDL2sRgbe0lCA7wIyfNefXW4sXdpxoqcMEY0+ET0KIDJIXf9rTFmazlPxhjTCaAT4Cqdcj62ojQTpQh/ScjlJz+xZZKihy+a+WHNV2GG+brrbNOWWzHkavEIQ0Ph9flhXHAB5xLcMYdtbdZbTyQ4lr99+8gDWvxQV8NJH5fAiAbfGHPFGJ/jKIC3OLfPHd6mKEqFKFX4yx2gAhTq0be0ACdPFh6XTNp4uCwSQOHgcamfdxeOfN5W44yEVAzJIvOjH/FCIkqdUQNaRjLmox2o3iiMR0hnL4DziehtYEP/CQDXj8PzKkrTMpoQhuv5ZrPs2csxq1ZxpYuvuXPNNVb/xl0k/ClUs2cD3d3BbliguGyDywsv2Nh+Pg/s2sU/Uv8PFMbmSzHmjSh9XApjLcv8KID7AJwJ4IdEtN8YcyURnQ0uv7zaGDNEREsAPAwuy9xgjPnZmM9cUZSijFb4Swxlfz971atXc229m6AlYo9+507e7k6aAgq7fnfuDJ+bG4vZRcFN1hJxgvb3f58XiqiFYXDQll/6C5vo4hcz5o0ofVwKY63SeQjAQyHbXwVwtXN7B4AdY3kuRVEqi6tdn8+zcFksFqyGkfmwUXNu3SsLCbeE8Y532FJPCdvI877wAhvhRIINe1jOIJm0Rtpf2Eox5s2axFVpBUVRALCHHyaZLEZ50SLg/vvDQyZAofGU/cJyAHPmsICaPMattwJf+1pQhfOSS1jobOdOPici4NJLgQsvbMJZtKOgmLSCGnxFUX5HZyeXYkq9O5EN3bjaOG6SF2DDOjjInrfr8WezwI03As87XTexGPDkk/y3GOVMBvjiF4MhnFjMjiMUQbSRDL1S3OCrWqaiKL/D7U6VUIjrKfvefXs7G38pvxwYYAO/bp099h3vCBr8a68NhoCElhYb73e7fXt6WAF0YIDljRcsUMN/qqiHryhKyaxYwYNSZEbsFVcAb3oTa9y7JJP26gCwcgktLcEB6u7VgowlTKWsRPKECcCVVwYfn4j18ZullHK0qIevKEpZ8CUZHn3Uhn78EYL+tliM9e6Fzs7gRCtXatm9ypAFQXBF0tTgjw6VR1YUpWSkuuWKK+wglFwOeNe7gvuJJIJLPm8ljF3tfEFKLeV5RNZZJJ/dx26mUspyoh6+oiijQiQZdu+2YZdPfco2aMViXM0DADffHEzEine+eXOheJpbauk/XyZjh7aMpJujRKMGX1GUUSEljyJvIEbaHTAybRpvSybt2ENptkokgLlzecHwp1WVqoOjnBpq8BVFKZko2YIVK+xsWtGbB4JJW3cAybRpzdn4VG3U4CuKUjJRGjRR3a1hmvuilumPXlQqjyZtFUUpGTHsMiDFlTd47DEeDi5ev5vgdWckxeOacK0WWoevKMqoGK1sgSvKFovxoBRfO1/DOuVD6/AVRSkbo02gRgmVNasmfTVRg68oSsUJWySaVZO+mmgMX1GUqhCVD1Aqh3r4iqJUhWbVpK8mavAVRaka2lA1vmhIR1EUpUlQg68oitIkqMFXFEVpEtTgK4qiNAlq8BVFUZoENfiKoihNQs1q6RDRcQC/GMNDnAHgV2U6nWpQ7+cP1P9rqPfzB/Q11ALjff5vNcacGXZHzRr8sUJE3VECQvVAvZ8/UP+vod7PH9DXUAvU0vlrSEdRFKVJUIOvKIrSJDSywe+s9gmMkXo/f6D+X0O9nz+gr6EWqJnzb9gYvqIoihKkkT18RVEUxUENvqIoSpPQcAafiK4ioheJ6CARfaHa5zNaiGgDEb1GRP9S7XM5FYjoLUT0BBE9T0Q/I6JPVfucRgsRTSSiPUT00+HX8HfVPqdTgYjiRNRDRP9U7XM5FYjoZSLqJaL9RFSXA66JaBIRfZ+IXiCiA0RUVTHohorhE1EcwM8BfAjAEQB7AXzSGPN8VU9sFBDRTAC/AdBljPmTap/PaCGiswCcZYx5joh+H8A+AHPq7DMgAG82xvyGiJIAngTwKWPMM1U+tVFBRJ8GMB3A6caYj1T7fEYLEb0MYLoxpm6brohoE4Ddxph1RDQBwJuMMSeqdT6N5uHPAHDQGPOSMWYAwHcAXFflcxoVxphdAH5d7fM4VYwxvzTGPDf8938COADgnOqe1egwzG+GbyaHf+rKMyKicwFcA2Bdtc+lWSGiPwAwE8B6ADDGDFTT2AONZ/DPAfCKc/sI6szYNBJENBVAK4Bnq3smo2c4HLIfwGsAfmyMqbfXsArAUgD5ap/IGDAAHiGifUTUUe2TOQXeBuA4gI3DobV1RPTmap5Qoxl8pUYgot8DsBnA7caY16t9PqPFGJMzxlwE4FwAM4iobsJrRPQRAK8ZY/ZV+1zGyJ8ZYy4GMAvA4uFwZz2RAHAxgPuNMa0A/gtAVfOKjWbwjwJ4i3P73OFtyjgyHPfeDOBBY8wPqn0+Y2H4EvwJAFdV+1xGwaUArh2OgX8HwJ8T0f+t7imNHmPM0eHfrwF4CByyrSeOADjiXB1+H7wAVI1GM/h7AZxPRG8bTpB8AsC2Kp9TUzGc8FwP4IAx5uvVPp9TgYjOJKJJw3+fBi4CeKG6Z1U6xphlxphzjTFTwd+Bx40x/6PKpzUqiOjNw0l/DIdBPgygrirXjDHHALxCRBcMb7ocQFWLFxLVfPJyY4wZIqIlAB4GEAewwRjzsyqf1qggom8DaANwBhEdAfAlY8z66p7VqLgUwF8B6B2OgQPA/zTG7KjiOY2WswBsGq76igH4njGmLksb65g/AvAQ+w9IAPhHY8yPqntKp8StAB4cdkBfAjC/mifTUGWZiqIoSjSNFtJRFEVRIlCDryiK0iSowVcURWkS1OAriqI0CWrwFUVRmgQ1+IqiKE2CGnxFUZQm4f8DVAgRlRU5GYAAAAAASUVORK5CYII=\n", + "text/plain": [ + "
" + ] + }, + "metadata": { + "tags": [], + "needs_background": "light" + } + } + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "Up8Xk_pMH4Rt" + }, + "source": [ + "### 3. Split the Data\n", + "We now have a noisy dataset that approximates real world data. We'll be using this to train our model.\n", + "\n", + "To evaluate the accuracy of the model we train, we'll need to compare its predictions to real data and check how well they match up. This evaluation happens during training (where it is referred to as validation) and after training (referred to as testing) It's important in both cases that we use fresh data that was not already used to train the model.\n", + "\n", + "The data is split as follows:\n", + " 1. Training: 60%\n", + " 2. Validation: 20%\n", + " 3. Testing: 20% \n", + "\n", + "The following code will split our data and then plots each set as a different color:\n" + ] + }, + { + "cell_type": "code", + "metadata": { + "id": "nNYko5L1keqZ", + "outputId": "e1e6915d-5cfe-4086-d20f-8e3aebd80292", + "colab": { + "base_uri": "https://localhost:8080/", + "height": 265 + } + }, + "source": [ + "# We'll use 60% of our data for training and 20% for testing. The remaining 20%\n", + "# will be used for validation. Calculate the indices of each section.\n", + "TRAIN_SPLIT = int(0.6 * SAMPLES)\n", + "TEST_SPLIT = int(0.2 * SAMPLES + TRAIN_SPLIT)\n", + "\n", + "# Use np.split to chop our data into three parts.\n", + "# The second argument to np.split is an array of indices where the data will be\n", + "# split. We provide two indices, so the data will be divided into three chunks.\n", + "x_train, x_test, x_validate = np.split(x_values, [TRAIN_SPLIT, TEST_SPLIT])\n", + "y_train, y_test, y_validate = np.split(y_values, [TRAIN_SPLIT, TEST_SPLIT])\n", + "\n", + "# Double check that our splits add up correctly\n", + "assert (x_train.size + x_validate.size + x_test.size) == SAMPLES\n", + "\n", + "# Plot the data in each partition in different colors:\n", + "plt.plot(x_train, y_train, 'b.', label=\"Train\")\n", + "plt.plot(x_test, y_test, 'r.', label=\"Test\")\n", + "plt.plot(x_validate, y_validate, 'y.', label=\"Validate\")\n", + "plt.legend()\n", + "plt.show()\n" + ], + "execution_count": null, + "outputs": [ + { + "output_type": "display_data", + "data": { + "image/png": "iVBORw0KGgoAAAANSUhEUgAAAXwAAAD4CAYAAADvsV2wAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAALEgAACxIB0t1+/AAAADh0RVh0U29mdHdhcmUAbWF0cGxvdGxpYiB2ZXJzaW9uMy4yLjIsIGh0dHA6Ly9tYXRwbG90bGliLm9yZy+WH4yJAAAgAElEQVR4nOyde3wU1d3/32dmd4MKJhr15wVBioAEc+HiZYrgYKz0Uaq0aEWrQfEBRUVRlBYvT3kerLQoGi9UAYWSp1pqyyNKvdbFUZBR5BISWECgCFJF7WoCVLOzO3N+f5xsbgQBSbgk5/165ZXs7GXObmY/c+Z7vt/PV0gp0Wg0Gk3LxzjYA9BoNBrNgUELvkaj0bQStOBrNBpNK0ELvkaj0bQStOBrNBpNKyF0sAewO4477jh52mmnHexhaDQazWHFsmXL/iWlPL6x+w5ZwT/ttNNYunTpwR6GRqPRHFYIITbv7j4d0tFoNJpWghZ8jUajaSVowddoNJpWwiEbw9doNK2PZDLJ1q1bqaqqOthDOeRp06YN7du3JxwO7/VztOBrNJpDhq1bt9KuXTtOO+00hBAHeziHLFJK4vE4W7dupVOnTnv9PB3S0Wg0hwxVVVVkZ2drsd8DQgiys7P3+UpIC74GANeFSZPUb43mYKLFfu/4Pp+TDulocF0oLATPg0gEolGwrIM9Ko1G09ToGb4Gx1Fi7/vqt+Mc7BFpNAeHeDxOQUEBBQUFnHjiiZxyyik1tz3P+87nLl26lNtuu+0AjfT7oWf4GmxbzezTM3zbPtgj0mgODtnZ2ZSWlgIwYcIE2rZty1133VVzfyqVIhRqXDb79OlDnz59Dsg4vy96hq/BslQYZ+JEHc7RHH409/rTddddx0033cQ555zDuHHjWLJkCZZl0bNnT374wx+ybt06ABzHYdCgQYA6WQwfPhzbtvnBD37A448/3jyD20f0DF8DKJFvKPSVlS4VFQ5ZWTaZmfosoDn0OFDrT1u3bmXx4sWYpsn27dtZuHAhoVCIt956i3vuuYe5c+fu8py1a9fy9ttvs2PHDrp168aoUaP2KWe+OdCCr2mUykqXlSsLCQIPw4iQnx/Voq855Ghs/ak5BP+KK67ANE0AKisrGTZsGOvXr0cIQTKZbPQ5l1xyCRkZGWRkZHDCCSfw+eef0759+6Yf3D6gQzqaRqmocAgCD/AJAo+KCudgD0mj2YX0+pNpNu/601FHHVXz9/3338+AAQNYtWoV8+fP320ufEZGRs3fpmmSSqWaZ3D7gJ7haxolK8vGMCI1M/ysLPtgD0mj2YX0+pPjKLE/EOtPlZWVnHLKKQD84Q9/aP4dNiFa8DWNkplpkZ8f1TF8zSFPY+tPzcm4ceMYNmwYDzzwAJdccsmB23ETIKSUB3sMjdKnTx+pG6BoNK2LNWvW0L1794M9jMOGxj4vIcQyKWWj+aE6ht9C0NYIGo1mT+iQTgugqVPTXHfXmGhj2zQazeGFFvwWQN3UtKoqKCnZe1FuKOSNnTxg704oOm9fozm00YLfArBtlZbm+yAlzJoFRUV7Fv3GxH13vjp7ynXWefsazaGPjuG3ACwLhg+HtFtqKrV3BmgNxb2kBLZsUSePunnNe5PrrPP2NZpDHz3DP0zYU7ikqAhmz1bCbZpKuF33u2f5dU3TTFNdGaRSEArBiBH1rxL2lOus8/Y1mkMfLfiHAY2FS2Ixq54ApwtQXnnF5ZNPHBYtslkzE2YPd+hYZDeq0nWLVrZsgRkz1GwfoEMH9XvSpPr72B2xmMXKlVEKChzy8nQMX3N4Eo/HKSwsBGDbtm2Ypsnxxx8PwJIlS4hEIt/5fMdxiEQi/PCHP2z2sX4ftOAfBjQMl5SVOQwcaO2yiJqT4/LNN4WAxy+uCpE/VnL8NB9m77rSml6szc5Wt3v2rG+RnJ2995k/tWsBFpGIpR03NYcte7JH3hOO49C2bdtDVvB1DP8wIB0uARPDiFBaaje6sFpR4WCaHqbpEwp5fFOQxJC7djWZPh3OPx/uvRduvBHuuw9uuw0GDoRf/crljTcmkUi4u22K0jDnXzdQ0RxUmrkIZdmyZZx//vn07t2bgQMH8tlnnwHw+OOPk5OTQ15eHkOHDuXjjz/m6aef5tFHH6WgoICFCxc2y3j2Bz3DPwxoaHMQDluNNiypG0eHEFkxCaaPH4rw3BabLtXfh1tuUbH6NEEAPRMu535UQq8bZ+H7KXJzI+TlRSkrs+rtY/p09fwggIwMNfPXDVQ0B41m9keWUjJ69Gheeukljj/+eP785z9z7733MnPmTH7729+yadMmMjIyqKioICsri5tuummfrwoOJFrwDxMyM62auHjd2Ht2du2M2rIa+N/8HjaXOAybabNohkVkNgwbpsS6LufiEqWQzwuq2BxOW214PPaYw6JFVr0c/VtvrT1ZJBJq3+PHH3gDK40GaHZ/5EQiwapVq/jRj34EgO/7nHTSSQDk5eXxi1/8gsGDBzN48OAm22dzogX/MCV9TO86ubGIxSyef16Jr9PBYpFf+30ANTNPJMAw4M47wXIcIks8ji2VfJKEADDMEHl5Nv361e7TcaBbN5dL8krIKoVX1hVh27UnIS30mgNOM19eSinp0aMHbiPholdeeYV3332X+fPn85vf/Iby8vIm3XdzoAX/MGZ3sfO6J4Hi4vrfh6Ii9VNvNu7afH2ToPJM6Pwk7OwKmCkIl0PfWhXv39+lT88BhMMJjCSM/OVMjsUB6vsvlGfb/H6F2ra7AjBt1aBpEprZHzkjI4Mvv/wS13WxLItkMslHH31E9+7d+eSTTxgwYADnnXcec+bMYefOnbRr147t27c36RiaEi34hwG7E8fGJjcNTwLxuPo+LF3qUlDgkBNkk/luHKvOi1UG5ZT/LkUQBpECBEjT5/Nvbya/MrcmlNS+vcOmjR4YEEjYkZfk2PQldHUsVSY8OgcRSonyPhbPPAP/+Z/1hf9AtaXTtBKa8fLSMAz++te/ctttt1FZWUkqlWLMmDF07dqVa665hsrKSqSU3HbbbWRlZfGTn/yEyy+/nJdeeoknnniCfnUvkQ8BmkTwhRAzgUHAF1LKMxu5XwCPARcD3wDXSSmXN8W+WyJ1BR6UOPZKuHxrOLSdapM7sjaMUlwMc+fCkCHqdhC4XHONw7JlNhs3qvh7To5LMlmI7ydY+e+A/OcNMv87DNdfD0VFVGyYS9AeMEECCJSop3wqykrI7Kf2V7Mo7CcQQHinWTvI6jONCHzCeNg4vI9FKgXTpqmisLSwH6i2dBrN/jBhwoSav999991d7l+0aNEu27p27UpZWVlzDmu/aKoZ/h+AJ4GS3dz/H0CX6p9zgKeqf2sa0HD2O2yYEvs3g0IigYe8NUI5Uf4Wt8jOhjFj1GMXLoQePVx8v5DrrvO49toIbdpEsYDN8yYQnFoFhiQIQUVewNGrEjBtGmL2bLKeHo2RfJMgvV4bqB8jBf7cbdBPjWt9CfT8wX/wVe+XkSJg7S0mb62HfzowKNsmNxJBJjySQQQHu+Y9SVlf2NNXJomEsoNI1wJoNJrmpUkEX0r5rhDitO94yGVAiVTdVt4XQmQJIU6SUn7WFPtvSTSc/QIM7lHC57lVHFsqabfW4y+3ODwoLQxDPS4I1GOXLXPIzfUQwsc0PdoHJQQDZpHZJYExBYKQEvFMVVeCkBKZ8Mj8ZxZffj6OY3o+DEJiBpITX4X/9yYctfY1Jm9zeeUVeC1ZyOdXVxHvJcGEIEjxxhsOc+ZYTMyw+KA4Sm7c4bOKbO75RwmfdSvhqVeKKCtTY60r7AMHwvz5auxjxkBurp7lazTNzYEqvDoF+KTO7a3V2+ohhBgphFgqhFj65ZdfHqChHVwqK102b55EZaXKAmhoVHbNNS59imexebhk5RT4+kyTBYGNX515I0Ttz6xZNolEhFTKBCIEz29DJBNkxSBvLHSaBWeOFayNnU2CCElMPCKMmWfzr6OzkEJgmJLAEGR8AVkxIEhRMc/hh0mHCCqTx0hCkBKkUqoILH3C+VtcTd+P+/toMm94mq4DnuaRhwdUX3koYZ8+XV3BvPRS/ZOVLtbSaJqfQ2rRVko5HZgOqsXhQR5Os1PXIwcilJdH6dPHqpd0cPLJDps2qcT3ANhy88Usvd1CVBueAZxxhkvPng6lpTZjx0bp1cuhY0ebnnNLOK16X5kxaBeDBG24yygG4HwcFgQ27y+xyNkJjz8eQUoPRIh2qyQpfJJEeLs6POMR4ciYR7exJo8WDGd+aRGxmJq912TEOQ4VPZIEYcAE4Xvk5zuUlysriLlzlcCnO2sKwS6zf41G0zwcKMH/J3Bqndvtq7e1aup65KRSHh984PDLXyrBHz9ePaay0sYgRJDyMVLQ8fevcrbvsgiLIIDu3V0eeqiQcNgjmYxw991RVq2yEcJh/fE9ueTLCGGSpAgxkxuYEy6i6EmLeBzmzbNYskTtJxazeO65KL/+tSra2nILPHqzQ9S3eb867bKQKDYO78Rslm2wGD4cbr9dZQKlk37Ky21OLgtjJD0CCVJEWLnSrjkpDBmi1hs8Twl9EFAz+9dhHY2meTlQgv8ycKsQYg5qsbZSx++pthCOkEp5pFIRVqywa6pXc3Jq7ZD/31vXE9k0jWNLJUfGfM4TDgulhZTQs6dDOKz8cwzD4/bbS+jQYTam6ZG8OsJ/jx3D+bFSXhRD+OKykeScWCus2dnUCD7AuedadOyoFPdvcfgtFj61s/APsVgRsrj+ephSBBb180VdF255Gn6cO5xTf7+N0y45kXunFbF6tYVpqoyikSPV/pcudZHSYcYMm1WrLJ2to9EcAJoqLfNPgA0cJ4TYCvwaCANIKZ8GXkWlZG5ApWVe3xT7PdxomE+fmWlRXh7lgw8cVqywicUsQiFV4FTXDvlTirn0+TZ8m5Ng89WCTWXZnIkK43TokI2UEcBDiAjr1kGnTuoEgExwQ8EjnBaT9JcLuejlXF7GqkmRHDlSjSud1jlypBrj0qUup5+ubI7TXjrFxWomn50NFa+5VFxdQnLLLEKkCMIRnrs+ykfZ8OCDtVcbL7ygvHiCQJ004nH1+q+84mLbhZimx0MPqauSdAqpRnMwGTBgAL/61a8YOHBgzbbi4mLWrVvHU089tcvjbdvm4Ycfpk+fPlx88cU8//zzZGVl1XvM3rhuzps3j65du5KTk9N0b6YRmipL56o93C+BW5piX4crDdMt0wJaUWGxeQ5cFjgca8K1Uy1V4LSp1g75lIFxRswvZvjvboWwz3Wp0ZimQIgUyWSExx8v5tpr42zbZvP663DhhTOBAALBsaUBIQIkHv0Ch/ew+PZbmDwZXnwRrrzSZWB3ZZVQPr2IW56uFe2HH46werVaV0jXVv2yv8vrqUIyqMJAIoAg4bFumsPWa6m52pDS45xzHP74x1rBr6hQJ7vLL3cYMEBlE2VkeNx1l0PXrpae3WsOOldddRVz5sypJ/hz5sxh8uTJe3zuq6+++r33O2/ePAYNGtTsgq/tkQ8QddMtEwnlOHnfffDuZJc3/EL+R95PVBTSZoXL1q21dshCmJxwwhYGjF4B4QDTDAiFkhhGrQ1yu3ZxrrpqPAsWqNCJEEKFYUyDJCGSmCSpnxs/bx6UlLisXDGATd7TrOzyNCc/YfPjnJIa0RbC49JLnRohdhw4L6WydUxVooWPIEmEBdJm+XIbKSNIqWycp02z6drV5aqrJtGtm8ujj0IyCaWlNsmkyiby/QiDB9ta7DXfm4aZbvvD5ZdfziuvvIJXnRP98ccf8+mnn/KnP/2JPn360KNHD3796183+tzTTjuNf/3rXwD85je/oWvXrpx33nmsW7eu5jEzZszgrLPOIj8/nyFDhvDNN9+wePFiXn75Ze6++24KCgrYuHEjGzdu5Mc//jG9e/emX79+rF27dr/fGxxiWTotmbo2CHXz520cqnISfFkQ0LY0wbppDjfNHs/f/x4FSkilZvHZZzM4/fQQiYSp0lt8gS8MAiOoSY30fZXqeO21DqFQCsOQQMCbV4+A5zswfZ3N+36tqubkuKRSE9SisamsErbnJTlmJSSTKlsnFKrfqtC24ZchGy8VQeLhYzJbDOf5UBEfBhaRjXDsp8Vky7m464eQTMIjjwwgFFJrFGPHvs2aNcrcbezYKBddVIJhwBFHQN++B/gfomkRNNYNbn+6rR177LGcffbZvPbaa1x22WXMmTOHn//859xzzz0ce+yx+L5PYWEhZWVl5OXlNfoay5YtY86cOZSWlpJKpejVqxe9e/cG4Gc/+xkjRowA4L777uPZZ59l9OjRXHrppQwaNIjLL78cgMLCQp5++mm6dOnCBx98wM0338yCBQu+9/tKowX/ANHQ0njMGDXT/8cZ2ayaEhCEwUgGbBibjbcO/vhHi2+/dbj22hSg+g4e/e1POPH//kboK5/tXWA+P+F/3xxHLKYOcClh+XKboiIV06+qijB5ThHrN1oEBpzru9g4/CMnm+unjCEjI4FEInxVkHVkaRjZtYj164tqWhU2bKX4u3ctnpwc5aSPHD7ratNvnIUohzZzYVSBS6+bxiATHieJhWy5fSDhcAIhIBxO8OMflzB6tEWxygpl4MDZhMMe3347m8rK/fuialonDbvBVVQ4+30cpcM6acF/9tlneeGFF5g+fTqpVIrPPvuMWCy2W8FfuHAhP/3pTznyyCMBuPTSS2vuW7VqFffddx8VFRXs3LmzXugozc6dO1m8eDFXXHFFzbZEIrFf7ymNFvwDSF2Pp9xcJf6nnx7HjxgIIyApDSK94kQ2qccsW2YzdKiabQsRIeeTE2FFQOkUiQz5nJWaz/++OY6iLi4d/uHwtrRZvtGiTZsoGzY4PPywyoARAs6VLm9RSASPjwsEn0QChAgAgw0r+vDVH3rxXxuKmPSMiqVXVkJZmcPtt0NZmcV5psvs4Q5WkY31ogWooP7mkkmMn2mzyLc4Z4HDpb6HIX0EHifLT+u9/5/8BMJhOH6Dyy+umEA4nMA0A1IpjzffdLjiCi34mn2jbtMfw6h/Rfp9ueyyy7jjjjtYvnw533zzDcceeywPP/wwH374IccccwzXXXcdVVVV3+u1r7vuOubNm0d+fj5/+MMfcBqpOAyCgKysrJpWi02JjuEfJNKeMtu22QgjDAjMUJizz7aJRpW75MaNFrPvLmbz7EK+WVLMS7EiNl8JMgwYYIR9igZO5pmP1RrA22YhHxS79O1r0bXreDZutOhruNxrTGKYUUIEjxA+x5UFmNJEtUzMoLtVjP+Lp5jkpMVeXSanUvfz4IOFDOk2nVe9Qk6ddj/+gEJKRrmUT3fxBxTS/un7edUr5CzfZUFg44kI8RyDzVcLlnxk43kRfF8gRIT8/CLWl7i8lizk8tK3CCcD/JRBKhVh4kS7uTrUaVow6W5wnTpN3O9wTpq2bdsyYMAAhg8fzlVXXcX27ds56qijyMzM5PPPP+e11177zuf379+fefPm8e2337Jjxw7mz59fc9+OHTs46aSTSCaTPPfcczXb27Vrx44dOwA4+uij6dSpE3/5y18A5cm/cuXK/X5foGf4B5z33nPZsMFBCJubbrLo3BkeflgSDoNhSIqKIDNTPfaDYpczbh2DudqjSi7khjOLOaUYzDqvd4r4lJBf7VIpPHLjDqCEWz2/kJDvEZgmUoSQErI2RshvU0xF+7jqjJVp1Yuhq8vkBEIEhEMJLiyYSySmZu7J6oycjwyY4KsTSAZVDKOEOzOe4u/3F3NkT5VNdHHyCZ544gmOOSbO2Wfb2LbF+UwigseRsYAeYw3mFlzIY6UTWLvWqsnD1175mn2hbje4puKqq67ipz/9KXPmzOGMM86gZ8+enHHGGZx66qn03cOCU69evbjyyivJz8/nhBNO4Kyzzqq5b+LEiZxzzjkcf/zxnHPOOTUiP3ToUEaMGMHjjz/OX//6V5577jlGjRrFAw88QDKZZOjQoeTn5+/3+9KCfwB57z2XHTsKad9e5al37hwlL88hFPIxDEkq5VNW5tCvnypiSsx1lJhLZTk8IG+ueiGhfklpknncDaTMckJ4iAYdf3LjDgQeBD6mAEaMgA4dwLbJtCwydzPOrK3ZGFUBQQjCqYDuHI/IiOB7HkmpMnKED/cRwsTHQHK9mEnf4iIqusfxPJVNJKVHZmacv/xlPDffrF67Y5FN6tkIqaRH23UR/nfdBNZKi4wMNXTtla85FBg8eDBS1rq7/OEPf2j0cXVDMh9//HHN3/feey/33nvvLo8fNWoUo0aN2mV73759icVi9ba9/vrr+zbovUAL/gFkwwaH9u1r89Tz81XBVTorJpWKsGaNTSiU9sC3eTOI0MZQlsNvlw2hU/IdTJFAYND2i7EMmjiSXn4uFxgOVxTb5H5Xh5SGXUh2M43OfDdO/vOCijxJVilkrn8BnnySzSviDJtp86Gv/HNmJa9nJNMwkYSFT27c4YlSmy5d1PsJUiFyS7ewJeWS7orlYjFeROkrHBYKG+OHFt2+hOOPh5Jqc+26bqElJbXbd9c9S6PR7B2i7lnsUKJPnz5y6dKlB3sYTUp6hh8KJQCB6/6EOXPGAVBQ4LBqlc0tt1i8/76L56mTwTFr4YELHbKH2KxYAT9Z3J/tZ6bIKoUj12ZwfvA2Lir/fuLEWg+eGhoT9j1No10X+vev7VZuGPDAAzB+fM3LbdkCZdNc/i4LCeNBOEL4nSguFjfe6HJxbgl3lM4kO+bjEeHle4uxRsR5+WWbO+6w8P3GP6NwWBVpnZVyGSAcHNSCMKihavuFls2aNWvo3r37wR7GYUNjn5cQYpmUsk+jT5BSHpI/vXv3li2Rd9+dJqNRUy5YgFywAPn66xF5ySXT5NVXPyifvWOafCj3JvnGaxnyrbdM+dprR8iePRfLxYurn/zgg1IKIaXKwJRf5SCnXX2RzMlZLCMRWfu4PfHgg1Kapnod01S3GzJtmpThsJSGIeURR+zy4osXq819jcXy/tCDsmza4npPvUc8KJOoffwrx5DO38Py7bdN+fbb6j3VeRv1foSQ8pf9F8tvjSNkClP+myPkuSyuua+xoWpaDrFYTAZBcLCHcVgQBIGMxWK7bAeWyt3oqg7pNDHfteDouvCPf8Tp0CFAVMfhQ6EkY8bciiFShJKSEyRsq7YWBo/HHnOw0i9k22oK7HlU5ED5FOgcfospyYU8+2yUmmbie6KxZrgNSbuc7ebN1NYVWNi2RW713ZXvTeecqrlkXVWAPyeCkB47+ghkyEcZPCd49NEJzJkzgenTle1CXUIhCL3nEAo8TOq3SzTNxoeqaTm0adOGeDxOdnY2Iv0l0eyClJJ4PE6bNm326Xla8JuQ74qUpO/r3NlmypQI4bAqpJDSwBAp1XgE+HcXED74UmCGIuTl2TWGZgUFDnkLniDzjyv4Z/vlpMJLaxZHMzIcCgutmn1WVta6be6SwVC3Cuy7UmH20By64d2V701n5Y4bCXrAiV3f5IHycfxn3yyOuSabLf4YgiABBEj5FpddtpBFi6KsWVObIVRVBSefDM7LNh6qmverHBNZsIWcUpf+/bXfTkunffv2bN26ldbSAGl/aNOmDe3bt9+n52jBb0K+qzl3+r5VqyzGjn2biy4q4fhgG23XQ49bXwYkGLCjG4gUbF1+GZVHjOOjjyyeftrlwQcL8TyPFWaEnr+N0p4ivlxRiO/XWiun95mTsxfl5nsQ8+9D3WbogYQOuaU83+ENxveF/MpcPv54Al9//RYQEAp55OU5rFljkZOjmpx7HrRZ4WILhzEU84OcFfSaMosfhWdgJ2fTrt0+XMVoDkvC4TCdOnU62MNosWjBb0K+K1Ji23Ce6dI3cHjvI5t+FxdxyaOFmKkE2zcFbB4GX/cGTPClQXT12Tz3nMqG+fnPnXoulBUVDh07jqdnzyhlZQ53322zbp1Vs8/mKDffG7JOH4KxQzVDN1Lwzuoh9D8fJk0C27bIyZnA118vrPH/Ly21a7p2eR6c5bu85heSITwqzjR5cVgBZjiJaQaYoor2QQla8DWa748W/CbkuyIlOcF0Zl15C1nLAv57fQah7cNUjrwMyIzBabOhMg8CBAEZLF+uDNGkhLIyu1FDs8xMi379LKZOrb/PysqmLzffGzL7jiT/Pfh42VzWfDGE/jePZMyYuiEu5f//8cclNZbJw4erdMvZs+GCKoeI9NjZ3Wf1Qz6nR5aomoMUGClJ1l0zYarOzdRovi9a8JsYy4K25S7xCQ7lQ2xyR1pUVrqUfnsrcliKT66GHmMTeNvghFAE6ScwCTgqZtBtbIg3rx7OD+wiNm60ahqZ33qrxfr10RpDs4az9YbRmXS5+W5j+M1IZt+R5PcdST5qZt8wxNW/P3TpokzTBg5UYZrycovcXPDb2IgPIlT0qiIIqxAXPhyzXJ0QM9f5VC4toeJk9b4aGrtpNJrvRgt+E1M+3aXzjYV0x8N7M8K8jVF63uwQGD7CULHtrwsMntxQxMn/UUTFPIcvyeY44jgxm4KtFtf2bexKodqw7DuonyHU9OXm+0pjIS7VlF2Fm0zT49NPHW68UY1zCRbnjosyoH0JhpxR28d3NrSLCb4uMCnPnUWwKUUqpTplHVUG3xoObaeqk6tGo9k9WvCbmI3POnSvNimTeHz4kMPSsM0FF2QQpBKQMrm39EleiKn4vDBri5DCYXikSP29r2uqh6IlQWMhrobhptdes+s956lSi8G/s8gvOZqKNyaTWQqZMZjHZSzLPZFCOQMhfAyqGJYzmZGlbxAJEohRJvBkbd9GjUazC1rwmxDXhUdX2FxUnVKY7gT1wYMWr74aZfx4hxdesPlrzCInR6VZlpXZnH66xYkn7p91wHdlCB1M9hRuOvdci1mzau8fMkT9rnCzOPV5A4OAFAZLOJu/rbCxr5lFyPAJpSTXls4ngwATiQwCuOUWNm6ET0rjZA/RM36NpiFa8JsQx4H3AotCotgoW4D3sUAqT/kNGyyOO06lTU6ZovrG+skQkReux95Po5i9qaU6VKjrbri7RupT3WwmXG1wTCkcEctgoWGzcaPFUS9ez4nbp3FsqeSomEQIgZSqt670fWQshY4AACAASURBVE6dfAsdkXhvRignqkVfo6mDFvwmJC26H1RZvC9rhUYItT07W3nQ9OpVm2ZpSp/TEtOgcPZ+xWH2tpbqUGTkyPqRmKVLXa757Rg2hwM2J03WvVTMlTkwqWASOUZP2ha2UWe2jAjG7aPhkUcgCAgwMaRf07Q9PtcBLfgaTQ1a8JuQtOiWlMCsWcp7LBSC66+Hnj3h+dEufZMO23OzCYIIQlYRSkmOKZXIhIfYzzhMM9RSHTjqrDgXFDh4nqe6YUlBpPcKTj99DL7vsVJGyI8Wk/luHGwbF4v12wdzPg6po7M5afKYmnBa9hD7YL8rjeaQQgv+PrKn5hxp0S0qqrX1Pfpo+Pv/uLzqqRaDXlmE/y4uRmSt4I7SmRwZ80maESKHchymian3OVJ/xfmop4tJnKwWdlOpCLEYdOqkroiCwKNMxHl+y3i2TYZXXwXft4hEVK/cisG5dP3UofMNOoav0TREC/4+UOuH47JunUMQ2PTtu3tRmT1b+cNICb/CqWkxKPE48+M4RbGneIcibByO+YnNuMN2er5vNMwoWjPMoWP1irNMeMy7Ic6LZ6i6g9JSmyCAiy6ajWGo3r633WbTsN1nIgG33gpBoMQ/mntw3ptGcyijBX8fWLrUZdSoEgYOnIVppvj22wiVlY330UxnzaTbDTgoQzDwMDIiFNxuExkNHyQtloctnHEH9K0cVBpmFL2DTVH1inPKiLDAV83XYzFVfBYEcM89UR57TJ0AVq5s/MSYSqnP+1DKUtJoDiW04O8llZUuubmF9OhRhRASISCV8igrcwiFdq34TC/gpmf476Oydx4b7HD2ONWZytm9+3CLpmFGUZciC4rUivPabJvlYyzM6vuKiyEeV148lmUp++QQJJO1r2cY6jM+R6qmKYtN1T93T+jeuZrWhu54tZds3jyJTZvuB/x0qw48rw2LFkV55BELz4O8PJfHHlP2BwBlZWpGunWrRWkpFBRAVpYWGKgvtlBfePckxKNGwbRpSuTTlunnSJcohSRyElT2MfiqcCrvVeZSUOBgGDbvvmvtU9MvjeZw5bs6XukZ/t7gumQt3YKRGyIAPM/kjTeG4zhFnHOOEvtu3ZSFcSrlUVpq4vvK9atHjwhFRVFiMUsLTB3Si9u7E97v+mzSZmueB6YJ50qXe5IT+OqSb9l4O0gjwE/dTLcTQ3heimQywvPPR5k4sbZfwKFaqKbRNCfGwR7AIU+1ImXeMYP8OyWdzBG0betw/PFPMXWqRVGREqp0br0QPoGfRJDAMFRWyZtvOo0KjKZx4W2I6yojNtdVty0LRo+GwkKXkgdGMa+HzTk5f2fjGJAhwAQR8gmFVGZP2nu/7uunw0ppg7pWlCClacU0yQxfCPFj4DFUY75npJS/bXD/dcBDwD+rNz0ppXymKfbdnLguJCY4nJ/wEIFPZhlkLupAx/G1XZpAzUqXLrUxzQiQwEgGIEAaEKRCvPaazYgRh08l7IFkTxXCda8ATFPZKR99NPztb6paORKuojxX8v/eAClQdsoSAmkS+CEMI1XjvV/39S1c1gxzeAebLkW6k5amdbDfgi+EMIGpwI+ArcCHQoiXpZSxBg/9s5Ty1v3d34EiLTS9EjZvBhHaGB6+EWFttk3DjD8VgrCorIxSMXsMmdOWIICvCgR/Lr2ec2+3DutK2OZkT59L3SsA31exeyFg6FB1RWWYkqB6GcpIQmAaCMPEiT7Jiy+qGH5lZTY9ezr88pfq/5T+53b0PJUdVKQ7aWlaB00xwz8b2CCl/AeAEGIOcBnQUPAPK9JC815g8SOhvHHe8VUGSTS38cXFzBhk3l0KHkjgiLURzririMHVtgGHdSVsM/Jdn8ugbJd/4xCt9iVKL9SWlqqmMMgqQlLSbgOc6ISpePwGsvKKiEQsflt9nZn2LQKVRltR4nBqlYchdQBf07poihj+KcAndW5vrd7WkCFCiDIhxF+FEKc2wX6bFdtWWTfXXDOJnbnwW8bzXmDV6EP6CuD++9Vv14XNJQ5BUnkdCyGIX3o9a7KsmtizZh9xXXJuK2SCfz9RCjkXF8OAjAwYNMhiwYJiTCGQBmy4BUj5dFzUoaYxytChUFBQ61tkGCqNdthMmyoZIYmJH9LxNU3r4UBl6cwH/iSlTAghbgRmAxc0fJAQYiQwEqBDhw4HaGi74rqqyOrhhwsRwkPKCHfdFaWsrLZv7PoSlzuqHBZIm52d4cMPHZa42UzpbvJNQUDmqjBXvVrEovk6K+d74zgIz8Osrk4eIByOvtBiwgR190cfxdWURVQ3lullsCXbron55+S4XHjhFlIps7oALsKqVTaLfFUTcYFw6Ha9TZH+x2haCU0h+P8E6s7Y21O7OAuAlDJe5+YzwOTGXkhKOR2YDioPvwnGts+kZ+5Dhjjk5HgYho8QHo895rBokcrlblvuMnRGIYb0uC3HpOwhgRlJ0e13JmVCqupQXxAfC/4qHTX43tg2MhIhmVBmaIvDNpMmqLuUxYXNQw9lEA6pxjL3rXySf2+3qKqC7t1dJk+utqD2Q7zyygjefruIm29WJ+0PPYuVEYto0UF9hxrNAaUpBP9DoIsQohNK6IcCV9d9gBDiJCnlZ9U3LwXWNMF+m4V07H75cptf/CKCYXiYZoS8PJt+/eC991yWLJzAKd0SHBsL+LYgIBwBDEkoFCAEGIZEGCl693ZYs8bSWTnfF8vCfDvK1hKVTTOpOpsm3Su37SpYOXYYFQXwcmkRa9daiFWqIKtuKEdK+OKLDqxaZRGP68VzTetlvwVfSpkSQtwKvIFKy5wppVwthPgfYKmU8mXgNiHEpUAK+Aq4bn/321z07+9yzTUOy5bZNf4thmHzq19ZZGe7nH9+Iaddl2DVLwJ6jDU4qiyEygVMYZomQZD+O8KIETbdumlh2S8si46WRd2JuG3DeabLq34hkZiHF4vwlejJYOHgBDaLsWoWdaVUjptlZbVpmeXlSvCzs/X/RdO60NYKdaisdFm5shDfVyLx2WdRunSxGDBAuTFeffUkhg+/H9P08VMGG2ddyOw/TeDfeTB8uEPv3jZnnklN+76D3US8JbN51CROnXY/hvSRwiAQJkIGVMkIhUR5n9o2ktu3Z9O7d5wf/cjmy5fhg8m13cheHOcyOMvRZ2VNi0FbK+wlFRUOQaCqZU2zivLyElxXZeYA9WaNQRBizQk/oKI7xFZajBlj0aZNenFWC0dz07HIxp8Vwfc8hCEwpQ8yIEN4DJAO76PcNkGlZWZkqHaSfV+VDMLHI8LtFPPjh8eA0H4XmtaBtlaoQ1aWTRCEqnO9JRdeOJPsbJdQ9WkxFrMYOzbKK6+MQErJoEEzmDKlkJwct54tr6b5cbEolFH+i4ncKqbihzPANJGhCAtNu8ZULR3LF8InkB7/zksSwieMxxDmEpba70LTetCCX43rwu9/b1FZeT1SCoQA0/Tp0qWEoUMnkZOjkuljMYsvvuhAKORjmj7hsEefPo72ZDnAOA4s8i0elOOZLkfy3PVRNo+YyIUiymJpYRjKRjl9VZZKmSAjHLMmjC9MkkT4P4aQkBGkof95mtZBq43hN7TnHW+7/DDp8HGPbIY/Ogbw8H0TIQSmqRwXx45VJfiRiMukSYWEQh6hUIRwOLqL/a6meWnMZdNxVCGc78OZZ7pcfLHDK6/YSAk9ezqcc47N6D7gTHC47y2b9wKLvobLAxc62BNUf1ydvaM53NEx/AY0FIsJA+v0m10VYfiYYkIFcY4/fguDBs2oTu3zKChwOPpoi6Iii6VLVQu+vDy1OFvXTE3T/OzOgycUgjPOcHnooUIiEY8BA9SJ+rnnxnPKKcBoyJhgsXwhmB4sj1hkTLBw0f74mpZPqxT8hpa8XT+t32/2B6vjPLRuPGec4TJw4Oya1L7Vq22eeqrWLE0bbh1cGvPgkRLy86uN1QxljVxQ4BCLWTz6KAwerJ5TXAxz58KQIdTL7df++JqWTKsU/IaWvJ1vsGFFhGSyuqIzYjP1CYjHLdq1ixIEDmvW2Dz1lLbRPZRxHCXY6bi9EF6NNTKo+yZMUCL//GiXvkmH5x2b3FyrxjupRw+H1av3rkWiRnO40Wpi+A2dLV0XSkrUfUVFyi5h47MOH51s029cfWGfPh2efRZOPhnGjdMzv0OVuqG63FyX3FyH5cttVq+2EELN/g0Dfihc3vBVCC9FiLLL/4PM0fBZ8BpSphAiQs+ejTen12gOdVp9DL+xBT6obZM3axZIaeH7FpFyiI6rPUFUVMC7k10uQBXr9P+bxbvvatE/FKkb19+yxaJ8OvwkcMgU8EVni3/8A4IA+lEbwtuZ41N1wzy+SQFGukeuR0WFowVf0+JoFYK/uzZ66W1BoG6nc+lLSmpPBucEqjl2BA+PCIWpKI6jQzuHKpalulltm1xCVjCTED6ejPDmz6Jc/YRFIgFvBzYeEQRVVBZIgjAqQVkCQmAYEbKy7IP7RjSaZqBVCP7u2uilt4VCSux9X20D6NzZJS/PIa90C5FY7YLuBYaj47uHMtWXcydWVSGRCMAwPAZnOUSjylr5rbcsCoMow0QJPy97BiOZUl2zQibt2vXmpJNuqPHU1ymampZEqxD83aXw1d0GtX8Hgctllylr3SAZonKcSWY5BEaEoU/Z5GoBOHRJX85Vr035CBJBhI3ZNpYF//VfLiedpMzxxqx/iu2nF3HZohK4aBvb5Gvs2LGMHTvKufPO3Jr+BzpFU9NSaDWVthYu45mEyriuz/r1Lh99NIn+/V0sC9q3d2jTRlnrhtuk2Dl1OKEHJ5KxKEruSP3NP6SpvpzzhUmCDKZzIxcZUf4Wt6isdPH9Qq677n4efbSQM85wuWe+Rc9HnqLs87MJghTgI2UVV9qTudufRK+Eqx0XNC2Glj/Dr07HqXSfoSLX5+gXwvzuaIeXv7TYsAG6dVNFOuGwx44dEd57L8qZZ9qAMkkTIkJWXhH000J/WFB9Obe1RLUyXORbmCbkboGyMmWOBz6m6ZGb67BypYrrT5xo89BDJpGID0jO/fE8ct94mftjGWzM1k3ONS2Dlj3Dr47nVi58mpW/TbHpOkn5JI+sf5WwZg0kk5CXV9soIxTy2LBBFenceWeUmTMncued0RrXRc1hgmXR8anxTHIsRoxQmTczZsDtt9tABDARQhXSGdXfgFWrLF5/fXiNj5I0YEdBwBGGR27cOXjvRaNpQlq24FfHcyvyUZkYJgQhqCiofUhlZTZSGvi+QSoV4fTTbRwHysos/vjH8ZSVWfqS/jDFsqBDB0il1IJ8WZlFeXmUTp0m0rNnlJtuUiZrUqqft94qwvPagDQwUpBVZiAyalf5XVdV5Oqm9JrDlRYd0inPtjnDiJBZlsBIBgQIvFSE+aWqf1JOjsvo0WOqvXIMjjqqmL59lQjUzeoZlO3CJEenbByGNMzQ6tPHomNH9T+Mx2vF3jDg5JMtkiuKaZOYy3EZBWRenVXzP2+slkMfCprDjRYr+K4LA26z6JWMcv4qh013ZdOmT5yKCrsmRFNQ4BAKeQgRIISgQwfVa92y4O9/d9mwwSFXZJN70xj9TT9MaSxDK11Ul51dba3R2aV3b4ch3bIpvGdMdc3FQsqnRcmt/l83VsuhDwPN4UaLFfySEtWW0MXCxYLVIGJwxhlqNhcEtZ4rpunVK7ZJZ3N07Oix0zeo7OyTuSrQ3/TDlLoma3Vn6qEQXH+9y5AhhZimh0waVOX4HBkLgARHPTQBcieAZTEo2+Vb4bDAsFkeUd47De06NJpDnRYr+I0hJaxZowRfCNXM5O67o9x1l8PgwbU9aNOtDsEnMCUVvQ0y1wjdJKMFUHem7vuwfbuDENUdsQzJVwUGR8fAJKDTxregcCEUF5M7ZgxnBh73mxHWFkfZiaVDPJrDjha7aFtUpL6IQlDT7i5NEKhthgEbN1p07Tq+nm9KVpaNYahsDsPIIGvEVJg4UX+rWwDpmH76mEiVZkPSwE8ZJFMZ3LdyKouPuBCJgZDVV3XPPgtVVYjAJxyorJ3d2XVoNIcyLXaGb1nqSzh5MsybV/++nByXggKHsjKb0aN39cXJzLTIz49SUeGQlVU989cNTloE6Zh+SQmsfsZlVmwMVWN9viowuG9lMX9ePZKjAYsoAhCGAStW1FTuYppg29jULgabJmzZokI8ej6gOZRpsYIP6sv3zTf1t+XkuEyZogqtkskIixY1XlSTmWlpt8QWSlqUP1/ukPGhx5GxgGPWCi76QZxPhMvj8jZC+AD4KR8DEKAuC4YPB8vCovbEMWuWyvOfPVtfBGr2n+ZcG2rRgu+6cOSR6u9zcbFxkAVbagqtpPQ44QQHXUXZukgv3PZK2FwkIxxheIiMCGfdbfPxLQ7fdk3weQFklULbWECSMCFD4JsR1vYswp1e2y2rbp6/XtPX7C/Nnf7bYgV/+nS49Vb1Zfx5znR+U3ALx5YGJEtDrEyGkBJSqQi9e9sHe6iaA0w6/v5eYHGREa1pYp5rWSTblFN+girUM5LQfWyIe2NPcjxx3vFtPrjFIpVSr/Pmm6ohTmNOrBrN96G5039bpOC7LtxyixL7nByXkVNuZWs4xadJ6DE2xfKxIykv6ECnTjYPPKCnY62NusVY6Sbm6Yu87PPjbN9kAAG+FEwp+E9mxEYiqgu0zg3UlaKDzftYlJY27sSq0Xwfdmfl3lS0SMF3nNqmJgUFDoR9Zasg4esCg5eeLyKyHh68wQEX/S1tZezOLtt1YelSm9zcDKT0SFRXZQuhcvb7JF3eqtsMhyhDhlg1ef5p6wUt/Jrvy+6OzaaiRQq+bUNGhiq8KiuzSSYzQCYgZXJv6ZMI4P+62ezcmaTyljCZUx39DW1l1C3GgrqxU4u8vCjjxztMmmSzbp1Fmzbws5/Bqc/VtkaUeIw43eHzuFXjraPz8jVNQcNjsylpkYJvWfD8aJev/s/ho2NtZt9dzIC8uURLh/BCbCSTckax+iGvOk7rkb+0hEz97WzV1I2dlpVZbNhgMXWqysLZtg3+9Cc4G9UaUeKRJMKsTTaL71P1HIMGaesFzb5TWenWpH8fiC5rLVLwy6e7XDRZXXr7GwxMAlgluYaFbCGXyoI67plSuWdmHuxBaw4qu4udzp4NVVUqfv8+FoVEa2P4vvpWBgHMn6/y8UEv3mr2jspKl5UrCwkCjyCIcMcdUYIA1q1zCAKbvn2bXvWbRPCFED8GHgNM4Bkp5W8b3J8BlAC9gThwpZTy46bYd2PE5zp0J0GIALM6n1oVViYYgMNLpUUMSM4iJKs9dPKKmmsomsOExmKnkyapsGC65gqU6L+PxSmnAP+sTfeN+9lc3DvO9l42XYp0k3vNnqln4RJ4XHBBCQMHziYc9qiqilBZGW3yWqD9FnwhhAlMBX4EbAU+FEK8LKWM1XnYDcDXUsrThRBDgd8BV+7vvnfHqQXZmG8GpL+nApBAgMk7wmbNGos7xr5Nr14OZ59tY1+gv52aXWOntl1rtNcQIZTYRykkQoKdOQGVpwuyPgiTWeSgazs0e0KZNUZIpTxSqQhAvRqhigrn0BN84Gxgg5TyHwBCiDnAZUBdwb8MmFD991+BJ4UQQsq6c6emo3NWHCmUF4pENbL2MbmFJ3lfWJgGrFtnsWmTxc03N8cINIcDe6potCyYOlXVc/h+rQdTOAxXXw3GZLWI+++cgLIpEISlXhPS7DWZmaohzwcfOKxYYQMwcODs2taq1e69TUlTCP4pwCd1bm8FztndY6SUKSFEJZAN/Kvug4QQI4GRAB06dPj+I7JtRJsMZMIjEYSYxfWUUMQSwyIjA4qLVfMLnT7XetnbisaRIyE3t9Y/P33cALz0vk1qUYSvC6oIwlKvCWlq2Ft7hD59LH75S9VX+ezAZeXYYbTpuY2z251I5pE0+YXiIbVoK6WcDkwH6NOnz/ef/VcHZIXjsD7bpiJucX02XKpFXlPNvlQ07i6Fs3NnCP1iGFfmbsMQrxDIFEZIrwm1dvbFHiG9drS+xGXoM4UYsQRmLCDAwJ81G/Ptps3vbQrB/ydwap3b7au3NfaYrUKIEGoCFG+Cfe+e6m9pLpDbrDvSHI7sT0Wj46guWQ89pEz4viBC9zOeJJmM11yGb948qdZpVdOq2N1kom4KZt3jwrLg5BIHI+URQoWhTQL8ZsjvbQrB/xDoIoTohBL2ocDVDR7zMjAMVdd6ObCgueL3Gs3esD8VjbatUufSC2zgkUzG6dhxfL1UO8OIkJ/f9JkWmkObxiYT33VcuC788hmb14kACUwCUhiIZsjv3W/Br47J3wq8gUrLnCmlXC2E+B9gqZTyZeBZ4H+FEBuAr1AnhQOCbkOn2R37UtHY8DgKApuqqghQ2x7TdeGjjxw6dqxNtauocA5IQY3m0KGxycTmzfVTMOtm4DgOLPJrazz+RTYniDhDH7dreio3FU0Sw5dSvgq82mDbf9X5uwq4oin2tS80t9WopnXQ2HHUt69FZWWUirISskphy1oYP9rl4i5bOPnhEOE2YBgRtm61+dGP9DHY2mg4mUh30UvP8LdsyWbFS6Po/gUM6lDEhLDF+56q8QAwBLSNN304+pBatG1qmttqVNM62N1xlBmDzIGzwfPozkxe9wWh1Sm+HitYfVFvPj3qBt6JW3genOW7XFDlsL7ExtIHYaug/lVhbRe9LVuyqfr3bWT1SPB5V+h+90weucLhrX9bzJ+vCv0yMpqnWrtFCr7r1nqghKrfYSSi0uq0m6FmX9ntAm+dM8HOHj4V+XBMKRwXg36xD/lXTimreq/gypyezCgfQ0R6iFkRKNLT/JZO49EF1UVvxUujyOyRqEnj3ZmXZOtzDv8xzWLcuOYNQbc4wXddGDBAlcSDEvwRI6BnTxgzRl9aa/ad3S7wVp8JKjsnKHsoIAjDliTkjQWBZN0UjwvC0xDXhPDu9DlydQApfanZGthtdMF1OX/WTMonKbE3UtC2NEQHtvBN8XSsa+NYzTgjbXGCn/6g0/i+akMXj+vwjub70+gCb/WZoOKjCfiRtxCGapoSLzAwCFTlrSnB8KnsY5C1VmhntVZC3avCUKhOk3vH4Zhyn/yx8HUBfF16Gu1inzKC6ZhrArjPUPGcZpqRGk3+igeZ9AedJhxW29LbTVN/5zRNiGWRNXgCwsgglTJJpNrwX6t+z19W3oiXVNsQGWSNmAoTJ+pLy1ZC+qpwxAgVk58xQ4V4yrNtME0yY3Da85C/9hPCpAgRKIPHIKidkTYDLW6Gb1nw9tsqhg9QVFT7/dKt6DTNQWamRc+eUcrKHNassdmaZfH8u5AztoiCAodOnWzsByzoe7BHqjmQWJbSG9+vjSz8LW6RO3w4cto0hJQIJEbIgAAl9obRrDPSFif4sPv86ubsJKNp3WRmWvTrZxEKqbUigFjMIhazuOmm3TxJF4m0WNL/2uzs+gv+g7Jdti0Bs7vJzgKfI0tDxK8Zw1Enl5IlCsj8Z1azHg8tUvB3V8Ks0TQFuxxfrkvl0hIqCuCVN4oIgtpjzjDg5p4uTHJ2baCri0RaJA3/tWmzxkHZLrljCqn4QYKyKQF+WJBKSYzQY5hmEiEW0OXCqZx8cvMdBy1O8HVpu6Y52eX4Moth9GhWPugReDDg/Fm8+OLbABQUOPzg39nk1kkPq/x7MRXt42Qt3UJmnSyCyqUlVJysJyktgYYZOvE4jB8Pm0c5BFUelfkqo0uYEoMUQiiXGSkD1q+/laOOym22Y6DFCX7DLjLN0URA03rZ5fjaNBd6JGtaZhp4XHRRbeciwzeo3OiTuSqgsnOClVW3EmwKMHJD5OeZZJZBZZ7JytxZBJtSepJymFNZ6XLeeQ55eTZlZVZNON51YfxMm9elydGlPkYSktIg5YcwTR8hfIQAKf1m1awWJ/gNS5ibo4mApvWyy/F1+hB4zMFIegQSkjWdixKYZgCGpKK3QeYaQUVvQWD6QEAAVDw2gsxFHag4bwuBPwM9STm8qXv198gjEd56K0o8XuuXk0oBCI6OCU6bavDm+b156Z0bkBLuuONmhAgwjHCzalaLE/zMzNoSZn15rGlqGj2+puYSzChhZWIbmaXQ94fvYxjVfRFFQPiGu9h8RBavkE3X1BhCoTq9lPtZZFW6GCtn60nKYU7dqz9IkJU1gXnzJjBzpsUTT8AFhkMoSLEzR7JptE/n0BJuy1/J448/TiplEg4H+H7zmgiLQ9WluE+fPnLp0qUHexgazV5RPt2ly402VTkeKx8DaaKaKQdQuWAwSx48mwXSZnsO9OnjMHKkjWHUumjm5NQuBAN6wnIYUjvDTyBlgJSQTEa4806H/v0tbu7p0mVUIZtuq+LzSyUIlaO/du3ZdO26rNpq26RTp4l07Dj+e49DCLFMStmnsfta3AxfozkY5MYdpEjyeQFIgRJ7CcKH81+cxyXyZe4lg8JYlP9dM54jj4TZs+sm6VhYlkXle9NVnN/0MYwMHc8/jMjMtDDNKLHYGLp1W1KdUq/WdKqqLHJHWpQTZeW6MZzCElVoBcTjJyNlOXWttpuLFldpq9EcFGwbEQ6TVQpGEvCV2Hd5TLlqhggI42HjYFR/6xpafeC6VMy4hYAkEBAECSoqnIP2ljT7zh//aPHRR73qbUv/v10Xcv9/e+ceJUV17/vPru6uwRc9OjGiRtAgICMDw0O0RLDIKD5jzOGcxGDuuHyhAkbiKCcky4RzzJVEwaAGDRDgMPfqiUlQ8HlFG0p5lA9gZhhtREGQ+CB6RmfQRLq6q/b9Y/drhuHlAD2P/VmL1dPd1VW7uhff2vXbv9/3N97itH+Zhe+b+L7A80wef3wKGzfGOO20uw/5BV7P8DWag0G6rDJaXc2gVTtoHATd73mOY2pTkO5glMTkZWyqqlQ/XCFaFFY6DsXrAoyr0sZaIqTj+R0I14WFC6F370ouvngh4bBHEJgsX15Jfb26F/kfRgAAIABJREFUo8v0Uli92mHePId162y2bLEYNgx69Tr0d3Ja8DWag0W6lDuKatpc/7nLn252+ESW8A0acLB5FYu1v1NV9EGgjLVmzcrUXNlE7y5i0J0JGocaFN/4ex3O6UBUV6u7tXjc4vbbVzB+vIMQNvX1Fr6vHHynTVP/Roywmq3hHK6aOy34Gs0h4pkGi98Ii6BFXsTQpMv3SlVl7jN1ldnUvYzjVtRxiGq7hXZPfsV1PG6xYIFahAXYvNnirLPU72eaSuyDAF56CVauzBVWH+6fWAu+RnOIsG3ldJtI5MI3w5IuT5babJrpEUTgO8mFPLl0Ba5rqf/82vCpQ9Cy4rquLobvq9/tzDNdxo93KC1VWVaxmJrVv/RSczPMQvzMWvA1mkNEy8YpR9e7GHdP45/lucrcsPTYudOhosLKzvq0p1r7p2XFdXm5g2la9O7tct99FXTr5lFXp6qmLcti2jRIOC4jkg6rQza2XZgfVgu+RnMIyU7YXRcmVyB3JWiqlfwtqRZmg1SYstrtvJ9wcRyL+nqYNEll70QicO21zS2+Ne2DlhXXAwfaxGLwzjsO3bplLgQJtm2bxqmnTsMCYqICgYcUJiFiwOH/UXVapkZzOHAcZMJDyICj4wZfVQ2nduGVlFUJ7ojPY1lQQf9Gl4kToU8fl6uumk7v3i5z5ijnRdct9Alo8slUXJ922t2EQjEefliJ95VXqguBktaAzz9/ibq6CprWVhNKeRjSJ5Q6dA1O9oWe4Ws0h4H6EpvegUkEjyQm0+KzsOMOJTxNGB8hPD57wqFfP5gxo4JIxCOZNKmqirFpk6VbchaY1izXo1HV7+DCC1VcPhSC666z+PGPY0TlZD4PXgcRKH+kcojmG+MXqOWeFnyN5iCwrx4MzzRYPCNijJJONj0TwMNE4pGUJvO32Az6kUMk4hEK+UjpMWSIw9atlm7JWUD2aLnuuiSmOQxJ2KwOVOrlnDmwcQE8WVpL03QIwmCEw8o3KVZZ8MUZLfgaTRvZnx4Mtg2/DFmsSanXhYBXpUUFMWwcXsbGlRY7N0AQmBiGB5hYR5fw62um0wub3WK+enX3sNCq5XocqKjg/ITHssDkAmK4WEgJI5IO0TrVqLxxiKB4+LVEbUv9fAX+nbTgazRtZH96MFgWzJ7dfEE2lYJXfYu1YQvDgJAPW7ZYrF0bY+NGB299CQvemswRhoe/0OTRa2P0qbRyi8C6Y9ZhoVXL9ccc8DxE4FMkPG7p77B+i0UqBatDNlKYRDd5RLeaMKGywGeQQwu+RtNG9qcHQ1OTy0UXOaxYYfPKKxbbt8O8eeo9KeG665RjZo8eDnffbeP7NreVT2OXTJAk4LPyXexYWc3Ni9Lpmy3bKukg/yEjY4q2davD6aerkF19CfTDRGTCcZttHnxIdbeybYsQMd6vVndufbAKkI/TOlrwNZo2sq8eDC1DPhMmxIjHraxbZigEJSUuZWVqmxkzwkgpCYdT1PsBSJBhSXlyIb3vrMRxLCzbbt4dWwf5DxmuCxdeaOF5qkn9/f3n0qduMc/KW2miGAebN3yLi9KtDNVnLCoWWernWdR+bsC04Gs0B4Fo1Nqj703LkM+OHdWkUg4zrinhmFcamLvJZutWB99Xi7XhsGqeYhiSwAAhAQMMmaK83GH7dgsXCyu/qqs9qEknJf9m6s5+/85lpfcS9eCC+DLGM4fXhEW3Ftfc9noD1ibBF0IcBzwOnApsA34gpfy8le18oD79dLuU8oq2HFej6Ujkh3yECPPxxwsI/BQDvhcw4BWDsX4R19bOIpk0kdJDSoNwOImUanEX38APIJUyWb/eJh6HBQtQM/2p7UBFOiH5WVclJeo7Li11GTNzBlsjygJ7YBX8YONiwjeNZ/DgXGq9ZSnxb483YG2d4f8MiEkpfyOE+Fn6+b+3st1XUsryNh5Lo+mQ5Id8du3azkcfzcMIBQQSvigPOCbu8e14A1VVMcrLHb75ze1cfvlc1RM3Bce8E9Dt3RD/uWwW8bgSn9NPd3njDYcgUGsCepJ/8MgPwYHJI48on5zycgciEkIQAO9fA6fuOJ4JZ7j8ZaJDzLf5Vdji97+H8eOb22q0l9+mrZW23wMWpf9eBFzZxv1pNJ2SaNSiV6+p9OhRiRAmfsrASMHRtTmf/Hjc4rHHpvLSS5VAEUggBF/0g4aLfIZQA6iZ5syZFQwYcBdffFHBY4+5uhr3IJIfgpPSo7TUASBVW4JICtWy1oDPh8JHl/yJkx6y+WXqLl6UFQxNqmpp11UiP3Vq+xF7aLvgnyCl/Dj99w7ghD1s100IsVYI8aoQYo8XBSHE+PR2az/99NM2Dk2jaR+4Lkyfrh6jUYvBg2OYRb/mo1Vz+HLUr9kyJ8YxYyzOFS4/YzrROMTjMY41hqupZEgV8Bz7feW4WV6uirMMwycc9hg40Ml1zdK0mUwIDkIIGaZ8w3ZuYC6L4j+hvCrg2HXkfhcRsHNgkjB+tqNZELTf32KfIR0hxEtAj1be+kX+EymlFELsqSN6Lynlh0KIbwPLhRD1UsotLTeSUs4F5oJqYr7P0Ws07ZzW0+UtRo60YGRuu1u2uIxZVoGJhxeYvB2dRbceQxAf1yBlCgyTPzyn8rnr622EMAGPVMpkwwa7XcWJOxKt1a7F4xZ1dTHO7lFNv+kLGPnWPHwMQiTpFgexCJoGgi8FyZTJkbWSJD5JTBxsioqgpERd5NtTOAf2Q/CllBfs6T0hxN+FECdKKT8WQpwIfLKHfXyYfnxPCOEAg4HdBF+j6WzsT7aG68L6+x0uxyOMzxelu/iixy3s/BiEiHDiiTexbFklGzZYBIESpLfeinHFFQ4ffGAzbpyO4X8dXBdGj85djFesUK+rC7TFz4XDMN/HkD4SSUAIA5/ucehfFeb+8ht4ZkMlxZtglHBYGbIpv8Hi2sEweXL7rIlr66LtU8A1wG/Sj0tbbiCEOBb4p5QyIYT4BjACuLeNx9VoOgT7k63hOBDzbX6GCSTYWR4gQ+oGV8ok3br1ZNgwq9l+hg2zKC4GcJgwgb22QtQODK1TXa2a04B6rK6Gnj1zF+iYsPmZzBneze4zi+6bawgkVMcreTVuIYT6PQZca/HbtI31LbfArl2qoK49pWRC2wX/N8CfhRDXA+8DPwAQQgwDbpZS3gD0B+YIIQLUmsFvpJTxNh5Xo+kQtGyC0tp/fNuGuwyLCj/Gr5jG8NoXMZKyWSPzXr1U79vFi2HsWLVwm8kk8X2Tbt1ijBihdp4v8KAdGJqaXDZscKittRk2LGdNcdF6h9o8I7sdO2DwYJUKKwSsyfM6crB5fcvu7SqlVBYZPXvmmtfktzoMh9tXqK1Ngi+lbAAqWnl9LXBD+u81QFlbjqPRdGT21bXQsmDECHjlFYv/YBqx+ErOrErwxTCDY8erRuaumwsTrFwJ/fs3zySZN8/BMNRBKirUjDUUgssua58FQIeLpiaXmpoKfN+jb98Qy5dfCjug9O7n+F6dz8WEWci1VFPJp0/B+0sdzpI2rxnKCO1VrOwFwUB9p76f279hNL9zc5zc+0KoBjbt6fvWlbYaTYFxXXjtNfX3q1hcKGL8rq/D8JvtrFpUVzcPE9TW2pSWqkKtVMpk3To7mxmSaZg9PHApXerwWdhmNSok1F4XEw8VjY0OUqoKZsPwOffcJSSA2ulQfjt0j/uMZw7XshACSRifX2BSEcSyQn8OLqOFw5qwzdmTLWbOVN9vOKwuqD3yUlpahvAq249vGqAFX6MpOI6jwgKgZoUDb7IY/oia1TvTlUjnhwlGGC5XxB0aorN4cHkD69bZbNmiFm7r0/XsNzCX2UzCkD5BUMSfboyxa7DVbhcTDxXFxSqjyfd3YRhSVS4DMgyN5dA9DiEk4AHqb4lHJdXYOPwPJTwoJmPigTB5dKdqTSilmsk//bQS/4UL1aLv/oTwCokWfI2mwLQ2K8xP5zSMXJjAwuXFoAJznkevRSZVs2I80y/XIGXyZDg7cJnNRCKkEICUCSp7OlTXwE93OSyXNm94XaOLVqbuYfnyao45Zj6hUFLF6EMR/u5dxgk8RwgfnzAgCfDxCXEtCwmTIsDgy/5JPimHaN0uzkc1K/fU9SH7uyQScO+9MHy4+j0zJmrtDS34Gk2BaW1WOH16LvYupRJ9IeAC4fDPMxJsvSAAsYueiWpsW4n366+rsM/5OBgESuwBoew4ufo/KpDS4xeYXBqKYdudXO3TxOMWV11lcfrplYwZU00oBN//fiW1F1pUPekyMnBYE7E55xwwXnE4he3cyDzC+HxeGvDmTAgiYCQlgz5pzDauev11WLIkd5ynn1b/2vPdkxZ8jaYd0HJht+Wsf9Ys5bV+0ckl1J0QIE0AycdyPpNvqaSuzqK01OVHP3J4r7YEL15ESCQQIQN+/3toaFDNs9P9cxdd59CrPSpSG5g7F+LzXcad5HDEJTbPNKg7H8eBZFIJfzydSvmPf8CiRZCQFhgw81L1mbPfmEr5Vy7XsAiJx+flkiDjnyOhUdZmfyvXheeeU/s2DHVhDoL2vTiuBV+jaYfsKRb8/vsN7NyambuDJMWZZzokkzBzZq75+X8tncWEng25D7tu9goSMk16VdqFObFDgOuqcMqOJS4x0tXKS0yeNWLcXWQxa5bqMJYJw5imevQ8tbD9AhV0W+phvGDy2qwYD9dYXPzHGOf5DrK2kUuS96oU2RQUnz42e1zLUr+P46h1lvz1kfaUipmPFnyNpp3SWjqnWoSMIKUHEgI/TG2tnfXXyTQ/P/47DfBvU5vvLP8KAtl0HRdrtwtLRynWyqx1fPUV/AwHM12tLPEYGTi86lk0NKhzqa5Wn8lkzixaBN/Z5WBKD0OqvNWyBodHHrFwKy0cx6KkBD5a1ZsBR8znqA9OYill9DFy30n+b1RW1v6/My34Gk0HIhq1OOb9hziybgJCBpS8KOgeh1rsrJ9+KmWyY4e9+4fzYxHpFWE/bDJVxljlq7TN12a5dK9xmLrAzr7WXuPRkLOuAHCw8TCR6crYlUbOY6i+Ht57TxWtZc4lFoN3q23EQhOSCRWXKSkBWlxs3TL80fXIxDr+lRe4dEGM6Y6123eyr3qL9kBb3TI1Gs1hxHVhyfUNfPP/Sbr9XSKCFDYO8bhFVVWMhQvvpqoqRlHRXpSnhcHPiKSD78OQhMsZkyo4Zc5dPOdVcJbvtnsXzsxaRygE6yIWt/aPUXPl3WyZE+OyX6v+v/X1cNNNsGyZepw7V33WsqDyEYvQg7NyqVCTJ+/uM+04CM/LOmKOSDrt+jvZG3qGr9F0IBwH3j2jhDfvC9KZIwHvVZVwTtylMl4NcXgPtcALtB6byV8RDpusljYhH74jHMK+h5BK2EYLh3WGlZn0FpQ9hZh2X+uwIF0wlSnvnzYtt/05uITvc6Asb0cNDXtfcbVtpGmSTKg7h9URm+n2oTjLQ48WfI2mA2HbsGlTA8mIQSgUkJQG3y6vYVH8VorwaCqFMeXzaDr5YXDLWjfSyVPJkG0zPR3Dv7zERkxWFwJhmKwM7Oykt6yscOGK1i2mc++3GkrJu0KMHWuxbJkS+xgVdNviQUXejlqkRNWX2DyTX41sWcQfjLFlvsM7J9lMn7J7OKejoAVfo+lAWBYEgc2uXUWARyhscq4HEZLsLIUNMyGI+BhMomnt9UTToRuZ8Hh5msM7Y9Uipm3n+uGmNU39VaYuBI9tt3HnWe0izbA1i+nM660ukLa4QoyPxdgyxaL4Dw7mztwCbeakXCzevSbG+TjsHKzsExLpkP7s2epiVzHZwvMszHqITTmcZ39w0YKv0XQwRoywaGqKZZtsR28Hf+kCGss9ggjpnqs+jeUQNU1kwuOrwOQXL9qsWaaErKhoD4ux6elyHxfMRQc/zTC/OfjeLJ0zuC5s3658a4CsH9BeHUBbXCHer3aYtcBiiGdzW3pR1wibhGwb14WptsulfaqZNwTYnvMiCgKYNAmuv77zGNBpwddoOgAtY9jRqJUVzKZSl02PXof/chwRrEaGJIZRRPHASohV8vI0R4m9VNu3nLU3Nbns2KFyFnv0qMzu95pr1LErKw+OwOU3BzcMk0GDYvv08c8IeygEN96oxrKnpjKZ7+jyEpuy9IUuZZg8vsMmmVTGdBXEGI3DGdfaVFoWK7/v8vjpNptmqIulHyzkqadWUF+vxpWxTthXT4OOghZ8jaads7cYdtb+9ziP5OUmcx5+mKnjaojWwdL/hj6VFq4Np37DobGWbKVpRriamlxqa22k9JASPv54IZHICi680Drojo/5zcGDwKOx0dmr4OcLO+Q852F3Ac7/ju42LR67Ncb6+x2W+zZrn7cIhZRB3atY1BRZrEj7Fe182uGfP0xm74xCwuOuuxyuvtrC99WdUGVl7kLTnnPs9wct+BpNO2dvbRLz7X+l9DjntOcpWfoMx9YE/CC+gD+vupSh9z3P8OEpkkmTO+6IcfLJVjZzZckSh549k9mmH0HgsW6dg+dZ2eNVVx8cscs0B8/M8IuL7b1ub9sqlBME6nkmWyg/M2fUKJeTTnJ46im72ZgfqbWISQs/gFBK3R2AanJy5pkuqZRqiOJIm1trIxhJT1XThk3GjLF5+eXdz7kjC30GLfgaTTtnb20SM/a/qZRHEIQZdtHT/C3s8+GPYWCVx6iBS9iWnr1K6TFkiMMtt+QapfTubTNjRgTTVNVLqZTJJ5/YuazNsLJm9v22m4JFoxaDBsUOKIYfBGStiCdOzGULWVau69fWrR5lZSYDB8bYsEEVi40dqxrFtLxLmTjR5aabKvA8jzPPNPnnoBhj6xyunFLNqLug3xgV0uoIRVRfBy34Gk07Z28e6xn73w0bHN54YzuDBs3NGn29fw0c/woYSUhJQSplcsEFdjM3zjfftLjjDocxY6qREhynktmzLS67TB1v+3aYN+/gLVjmrz3si4zpWYZUSt1t5N/dZEJE4PHAAw6rVqUbuuNy/u3VbPwmHD+0ElC9APr3b25BoT4zlRG2xfBOKPAt0YKv0XQA9jbjjEYtRo60MAyXL75YBHIXRkjy+VBoGgin/D7M3OgNhE6r5Ne/VjvJv2vYssWiXz+Lmho4++zmx3Nd5TmzvwuWc+fm+u6OH79/5+a68G61y/k4ytTNyo3RMHIhnZa0DBENHGgTDqt99Vtj8/ffehRHIJlYyOTbV1BXZ/HllzkLinBYfWbkyL2PrTPE7rNIKdvlv6FDh0qNRnNgrFq1Rj7xxBi5YoUhV6xAxl4UcurVN8sjjpByw5w1Ut5zj5Rr1kgp1cM996jPrP/DlfLFM4fLG5kji4qym2T3+V//dY9ctWpN9jP572eYM0dKFYBR/+bMkc2O09pn1qyR8nxzjfwHR8gkIZkqOqLZ+CKR3P4ikd330di4Rm7bdo9sbFRjO+IIKaeKe+R744Rc8RJyxQrk8peEHDfunux+SkvXyAceUJ/ZG5n9hULqsbXxt0eAtXIPuqpn+BpNJ0Ll6E+jrm6lmvmGTU48u5LXRrmU/qSCwPOQpkloRQzLUh76tetG0dQnReR+uPenryPjUF2tpufPPuty/vkVnHKKRyKhFn0zcfKMR39m9rt4cfOxLF6cLlraS86848CIZM7l0s+LG1VXN2/9mAkzQW4f+SGizOL2CmlzW95CbECYE0/cTmmpSzxusWWLxVlnWUSje/8u97ZY3lHR5mkaTScjszh62ml3M3hwjFtvtehe4yATqso0SKhiJIAdO6qRIgUGyAj8fQyMZTE7digh37rVwTASGIYPMsEt/adxlu+SSKhF1LvuUoLuuiqMk8/YsUokEwklmonE7kZstg2rI8rlMoWBSDtWuq5aLO7f32XcuOlcdtlcjjlmOo895maPB+px+nRYvdrlvPOmM3Cgy+uGxZVxB/eOm3nj+SsJhQ0uv3weD84azZ8uu4X/vKSFOdoeyDdm6+j59xn0DF+j6YS0XBx9GZt/zbMOfhmbPi5s2wYn9mj+2aWhsfTooRZMjzyyEcMI0m0WAy5uepGrWckYYqzxVTPvzOw3k0aZSqnHsjLlVJmJwQcBuxmxWRZMdyyce2dx8dMTle3B5Mm8e00ZffvCffdVEIkkMIyAIDAYN66IO++M4Tj5mUYuQ4bYFBUluf/+CPX1DkVFFg0NFueeNx3ffxrwCQmf4dE5fPexRVz6nLI4hj3H6Nt7Q/KvgxZ8jaYTsKfFxczrJYMtLjVjjEg6rArbHLfD4tnzoW/fSmbOXEBRJAk+bN11J5UrVThn5+q5/OiHMwAVUiEFQVQSweN8HFanK3dDIXXctWtdrrrKYf16m02brOxs/lzhUiXv5WQ+ouH565ut5mbGN65HAwY5x8rzcVg+lHRGjbrgqEePyjH3Muaof7Lx2bF43nguuKA6m1YKHmPGVNOjh8riiURK2LzZJPB3YaQkx9XKrMVxdbXVbEG6tZTTzpaeqQVfo+ng7KkSt+Xrsx6yqKmxeHUBJJeqJcy33rK4/XaHyy93uOIKG/uRtLrNncuJg25hmwhApBupSzi61iCJyYrABtSF4LrrVE58IlFBaanH1Veb/Pznqkn60fUut8tRmKhgfNM7r/P+X56neMwU4nErO74XQjaxsEkINdhelTY3BvDVVya+r2b4qZSBwGD4RUv4PAQn9V3GD8/c/ftIJndQU6Matgth0rfvLJJbauh++wKOjPtZi+P+dL4Y/b7Qgq/RdHBaLi5mKmO3b2/+ekODsifwfSXgGTLNvX/3O/jpT+HMnS4/njeRY/sFbL8aAgBpsOLPP2JT+acsF2Nx31LKKCUMHqxm06Dy2w1D5bdb6aavkhQCaMq4eZpLMOpeoK4uhudZ9OvncsoQh7/0msVVRzUoe2JH5dOfdcQstq1bzOZEOcGpxXTfvITQ8NeztQY/vngxdz47jUsuWUg47AEmn33WA99XYwmCXWzeXMOoUY/ALYP5fP5i3JPGMn2KGv+BpJx2BrTgazQdnPyc+lAIFi7MxdFDIbVNvqCZZs4RMp9kUjUDnyocAhkQjcPAKmgqB5oEyUl/JRRJcVqwkvrJZcTjFoahLiS5nPgEhiHo3bskOziRDuw3lqM8awxl4VBe7jBwINxzT0U6dGOyOhLL+vicF3J5SU6mLOVxZngl35ExTuxbwsTBr2ebiqeKx7Jpk0VV1QqGDnW48Uab9euhX7+FGIaPEJJUah4frezOSZMf4ljP49L6lTBFlex2thj9vtCCr9F0cPIXF/MrY0F5yPTs2VzQMtuWlEBNTfPtQaU1ehRh8BXROBTHYeu4gHDEwwhJpFRiHY9bhMNq39Goxemnz+Lddychpc/mzZM56qgyopYFr7wC996L2bSJVHIzyIBUyuSYY2weeMAhlfIQQlW+5vv4nBs4ID2+KPX5rHwXl9ZWMzX+CFTBBeWLSXYfyx2LxhMEsHGjxemnqwvQsGHwxBPXcsklczAMiRA+7yZncFRviL7Z3Cq0s8Xo94UWfI2mE7CnytjWrI0z2zY1uVx0kYNl2Vx/vZUN9bwmLC4yYtw7oJrBdQsI4XN0rYCkxJeSVMqkttbOxu9BpUaed14DUgZA0MwNc269xeJ/PsmRR8LmO10GDnTYsMFm3DiLUaPA901ANV9fvryEq6+ezvr1NtsoYdMF8D+XgAxJhiXnM70KlsYrmfjueC67DHbtyoWnli6FF15QF7Qrr6wkkfgjpNcOpAGNQwTRtwTZq1QXpE2CL4T4N2Aa0B8YLqVcu4ftLgYeAELAH6WUv2nLcTUaTevsbyphvjf9qaeazJ8f49FHLY4/Hh5/HNYEFhXvWDw2pZJjn67m7PhCyquSfFZu8Kv6Wbz9trIc/uILGDVKhYcGDbK5/34l3hk3zLlzVePw0lKX8nIHw7B5/PGpmCY0NsLo0RZ9+8YYNMihqamESZMmY5oelZVhpJT83QiyTp5hmeSH5XP4SXwR/2nF+N1zVrO1iPwU0alTLT76aHb2jsMgQnGdn9uwi9LWGf6bwL8Ac/a0gRAiBMwGLgQ+AN4QQjwlpYy38dgajaYV9idM0dybPsEnn0zjww+nEYtZzfp5byy2GHe+Q+TtFMfFA459W3DbFQ385W3o29dFSoe+fW3icYu6Oov6+hhXXJFzw1y8WIn9zJkqTu/7Jps2xSgqspg4Ua01ZPS3b98aIhEPw/AJAiX0hiHV+4GK2WfSKi/5tJpQ0mE5Nq8JFcqB5msVJ500nqOOKlPunE9tJ/rmvJz1ZldIyWmFNgm+lHIjgBBib5sNBzZLKd9Lb/sn4HuAFnyN5hCyN+Ov/EVWKQMGD36JAQNWUlUVY9MmK1td2tgIP55n84I0ieDxxYAQH43ezkWpudw68SeEIx6ppMlPq1bwzjsWw4ZZ9OqVO9gt5S6ffGMakUiCUCjAMHYxZkw1TzxhccYZLhdcUM3FFy8gHPaRMoTvh5UoB4JAGgShAAjx1bZLGfHQcxy90YdIiJGbFzJCpvgFJpdGYox7yGpm85AhW4A27BD1bOxgHI4Y/snA3/KefwCc3dqGQojxwHiAnj17HvqRaTSdlL11yYKc/cK2bdNoaHgpW9RUXu7Qt6/F8OFqUXfiREj5qjXg90qrGfLbhUSL5jG5FELChxCYMsGVQ6rpdZvV/MLiulz5UAWN305Q5wcEBggh2bFjAeedN5jBgydjmrsQQiIEyECy89l+lH+yiWNrfVIIHhh6I89sqOTNNy3OC7ksusmhF2pl2sBHCI97L3WINVh7z7TpjGWzX4N9Cr4Q4iWgRytv/UJKufRgDkZKOReYCzBs2LCuG2jTaNrI/hh/RaMWp546jc8/X0kqpRZN43Gbhx8m65mfSd18FYtvlzucFUkhhE/IEIj0Iq+Rgspe0C9dQJutnt3u0MvzKH4r4LjXoOE89b6UPkGwmCIzgTAkSMCHUDJg9LKNFMdBAJIUF0r4zTtqQXkVFo8Vw0ZcAAAKgklEQVT1tJhq561Mh02mPGez6un9aNDSItbV6ayP94N9Cr6U8oI2HuND4JS8599Kv6bRaA4Re+uSlU9+A5WNG20efjg3S7dt1dP1q6/U89raPC95I0yfh32SR/sUvxUhOlu1lMq/s8hUz37ZP8FnwwMkgIRkMszDD4/l1ptXEAoHiABOeB5OWAbHxpX+ZxGqlkDK9MLvcdU0rYVo2qrz0e02q+ZZu1/Y9qHm+7oD6qwcjpDOG0AfIcRpKKG/Chh3GI6r0XRZDiSCkWmgMnKkyt55/3216GpZFrNmwaRJqigrHreoqooxZIiDbZdw2u01FNdCdEIu9zP/zmIVFjO/G2Nk38nI8OvKj8eH9c9fwlNPjaf/u1u4sXwGxbWSI+MRQJAiSYgAiSCByS/ersQXSuxn3DsaM5KgLgmDfm4Sne3QB2v30Px+qHlntD7eH9qalvl94CHgeOBZIUStlPIiIcRJqPTLS6WUKSHEJOAFVFrmAinlW20euUaj2SsHWlSUn6rp+ybdusVIJOCHP1SGaPG4xcaNFkVFcP31FWz1PYxBJoNKK8lYy7es+v3l8xZ/eOckvj2abHVs6bLPGGG43BV/CDMOiBATeIh6yhgtHL55RgmnHtPAjLU2qwN1AgMHOoQjXtZSofHMJFHHwZraSrXsdGefar6/d0CdjbZm6TwJPNnK6x8Bl+Y9fw54ri3H0mg0h5b8VE0pPZYsqeaSSxZlDdGmTo1xzjkW48Y5+H4mpTNXYAWtV/1+Fe+RtWiI1sJR8dVE+1fT7W0PQwZIIegRamB+YPFqYCHeViIswyCSKpxTW2uTSpqYMoGRguK3IjDBzh6zmZ7vh5p31TVcXWmr0WgAlarp+ypGn0qZBAEIofLiDcPjwQcdRo60aGqyqavL9ZItLrZ3C5nnV/3+n68quSE+j2jcR7ksS/r1A2ObEmVhmgy51QblxIyUMNRzufMsh3dOsvnl8xZvv21xx5QV/HZiNed6qDWDPan0fqp5V7NVAC34Go0mTTRq0a1bjHnzHNats4lE4LvfXYRywVQNvzOccMI1APToUUk8rlIik0mIRHIRlIzu3nCDxYT4w8xmEgY+KaOIHlMqYUplVpQ3OjnlPQeXF2UFR6z1EEUmlzwY4+EaC7AoHmgR3R+R7opqvh9owddoNFlGjLAwDCs7OS4tjalK1XTlbH6c3zBMevSopLpaRU8ATj/dZflyh9JSm2gcLMfhmr42/x4fz5uUYeNw7BU2UzJinH60URlBiQR8B4du0kMEKgbfvcZh4ULloBmf71J9vUOvSlsL+tdAC75Go2lG88lx81aJzS0ZPJYscdixQ73/g9K5jJ85CSI+NesiDL5D0r3O56eEiYprWSQr+V3RVFZMyR3LdZV/P+Saol9eYmNMzsXgH99hk0iomf/zyQqK5niwqAvlUh5EtOBrNJr9JmfJ4LFrl8mMGTbvvKPaGP7v8ol8EEmpXDzfo7EUojWSMD43MId/HbCALb+8jn6llYDF3LkwYULOmtk0VYSnzLKgLBeD31qduQtwMPEIyS6WS3kQ0YKv0Wj2m4wlw5IlDjNm2Lz5pvLdubm/w3G1AR8lVdqkkAZH14fwSWIg+bJU8tZ9HkHRHOrqFhEKxZg0yWrmw59M5ml43m1GJbBgAbzs2XiYhAy10NtlcikPIlrwNRrNARGNWvTta7FlC1mTtfLbbLpPKmJAVYKmoQbH3TSb7RPL+Ost1VwTLOCz8iRBRIKQBIHH1q0Ovt98dh6JtK7h6U6JOI7FlpIYZQ1O18qlPIgI2U69oYcNGybXrm3VXl+j0RSQpiaXxkaHDz6weeUVKyvSU22XEUmH1RGb6emsm9GjYUjC5fsDqjnrgflgpBDCJBxewYUXWiQSyuv+u9+FKVO0hh8MhBDrpJTDWntPz/A1Gs1+0zJLZ8KEGNGoxfTpsMq3eFlahNJ286D87l0selHPWUEABoBkwICuWfhUaLTgazSa/aZllk6mynZPxa2mqWb4dw+cyAdCBeylTNHY6GBZlhb6w4wWfI1Gs9/kZ+lkqmxhz8WtsRgkpjl8Y0NuQdcQoeznNIcXHcPXaDQHRCaGnynG2idp98qm3gkahxoU3zib6Ijx2bd0WOfgomP4Go3moJFtG7i/pKf/UcchmqfsXdWTvpBowddoNIeeVrxtuqonfSExCj0AjUbTNcks9GZy+XUd1aFHz/A1Gk1B6Kqe9IVEC75GoykY2sX48KJDOhqNRtNF0IKv0Wg0XQQt+BqNRtNF0IKv0Wg0XQQt+BqNRtNF0IKv0Wg0XYR266UjhPgUeL8Nu/gG8D8HaTiFoKOPHzr+OXT08YM+h/bA4R5/Lynl8a290W4Fv60IIdbuyUCoI9DRxw8d/xw6+vhBn0N7oD2NX4d0NBqNpougBV+j0Wi6CJ1Z8OcWegBtpKOPHzr+OXT08YM+h/ZAuxl/p43hazQajaY5nXmGr9FoNJo8tOBrNBpNF6HTCb4Q4mIhxCYhxGYhxM8KPZ4DRQixQAjxiRDizUKP5esghDhFCLFCCBEXQrwlhLit0GM6UIQQ3YQQrwsh6tLn8B+FHtPXQQgREkLUCCGeKfRYvg5CiG1CiHohRK0QokM2uBZCFAsh/iqEeFsIsVEIUVAz6E4VwxdChIB3gAuBD4A3gB9JKeMFHdgBIIQYBXwJVEspBxR6PAeKEOJE4EQp5XohxDHAOuDKDvYbCOAoKeWXQogIsAq4TUr5aoGHdkAIIW4HhgHdpZSXF3o8B4oQYhswTErZYYuuhBCLgJVSyj8KIUzgSCllY6HG09lm+MOBzVLK96SUHvAn4HsFHtMBIaV8Bfis0OP4ukgpP5ZSrk///QWwETi5sKM6MKTiy/TTSPpfh5oZCSG+BVwG/LHQY+mqCCGiwChgPoCU0iuk2EPnE/yTgb/lPf+ADiY2nQkhxKnAYOC1wo7kwEmHQ2qBT4AXpZQd7RxmAVOAoNADaQMSWCaEWCeEGF/owXwNTgM+BRamQ2t/FEIcVcgBdTbB17QThBBHA4uByVLKnYUez4EipfSllOXAt4DhQogOE14TQlwOfCKlXFfosbSR86SUQ4BLgInpcGdHIgwMAR6RUg4G/gEUdF2xswn+h8Apec+/lX5NcxhJx70XA49KKZ8o9HjaQvoWfAVwcaHHcgCMAK5Ix8D/BHxHCPF/CzukA0dK+WH68RPgSVTItiPxAfBB3t3hX1EXgILR2QT/DaCPEOK09ALJVcBTBR5TlyK94Dkf2CilvL/Q4/k6CCGOF0IUp/8+ApUE8HZhR7X/SCmnSim/JaU8FfV/YLmU8scFHtYBIYQ4Kr3oTzoMMgboUJlrUsodwN+EEP3SL1UABU1eCBfy4AcbKWVKCDEJeAEIAQuklG8VeFgHhBDivwEb+IYQ4gPgV1LK+YUd1QExAvhfQH06Bg7wcynlcwUc04FyIrAonfVlAH+WUnbI1MYOzAnAk2r+QBh4TEr5/wo7pK/FrcCj6Qnoe8C1hRxMp0rL1Gg0Gs2e6WwhHY1Go9HsAS34Go1G00XQgq/RaDRdBC34Go1G00XQgq/RaDRdBC34Go1G00XQgq/RaDRdhP8PTbAQXVY+FCEAAAAASUVORK5CYII=\n", + "text/plain": [ + "
" + ] + }, + "metadata": { + "tags": [], + "needs_background": "light" + } + } + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "Wfdelu1TmgPk" + }, + "source": [ + "## Training" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "t5McVnHmNiDw" + }, + "source": [ + "### 1. Design the Model\n", + "We're going to build a simple neural network model that will take an input value (in this case, `x`) and use it to predict a numeric output value (the sine of `x`). This type of problem is called a _regression_. It will use _layers_ of _neurons_ to attempt to learn any patterns underlying the training data, so it can make predictions.\n", + "\n", + "To begin with, we'll define two layers. The first layer takes a single input (our `x` value) and runs it through 8 neurons. Based on this input, each neuron will become _activated_ to a certain degree based on its internal state (its _weight_ and _bias_ values). A neuron's degree of activation is expressed as a number.\n", + "\n", + "The activation numbers from our first layer will be fed as inputs to our second layer, which is a single neuron. It will apply its own weights and bias to these inputs and calculate its own activation, which will be output as our `y` value.\n", + "\n", + "**Note:** To learn more about how neural networks function, you can explore the [Learn TensorFlow](https://codelabs.developers.google.com/codelabs/tensorflow-lab1-helloworld) codelabs.\n", + "\n", + "The code in the following cell defines our model using [Keras](https://www.tensorflow.org/guide/keras), TensorFlow's high-level API for creating deep learning networks. Once the network is defined, we _compile_ it, specifying parameters that determine how it will be trained:" + ] + }, + { + "cell_type": "code", + "metadata": { + "id": "gD60bE8cXQId" + }, + "source": [ + "# We'll use Keras to create a simple model architecture\n", + "model_1 = tf.keras.Sequential()\n", + "\n", + "# First layer takes a scalar input and feeds it through 8 \"neurons\". The\n", + "# neurons decide whether to activate based on the 'relu' activation function.\n", + "model_1.add(keras.layers.Dense(8, activation='relu', input_shape=(1,)))\n", + "\n", + "# Final layer is a single neuron, since we want to output a single value\n", + "model_1.add(keras.layers.Dense(1))\n", + "\n", + "# Compile the model using the standard 'adam' optimizer and the mean squared error or 'mse' loss function for regression.\n", + "model_1.compile(optimizer='adam', loss='mse', metrics=['mae'])" + ], + "execution_count": null, + "outputs": [] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "O0idLyRLQeGj" + }, + "source": [ + "### 2. Train the Model\n", + "Once we've defined the model, we can use our data to _train_ it. Training involves passing an `x` value into the neural network, checking how far the network's output deviates from the expected `y` value, and adjusting the neurons' weights and biases so that the output is more likely to be correct the next time.\n", + "\n", + "Training runs this process on the full dataset multiple times, and each full run-through is known as an _epoch_. The number of epochs to run during training is a parameter we can set.\n", + "\n", + "During each epoch, data is run through the network in multiple _batches_. Each batch, several pieces of data are passed into the network, producing output values. These outputs' correctness is measured in aggregate and the network's weights and biases are adjusted accordingly, once per batch. The _batch size_ is also a parameter we can set.\n", + "\n", + "The code in the following cell uses the `x` and `y` values from our training data to train the model. It runs for 500 _epochs_, with 64 pieces of data in each _batch_. We also pass in some data for _validation_. As you will see when you run the cell, training can take a while to complete:\n", + "\n" + ] + }, + { + "cell_type": "code", + "metadata": { + "id": "p8hQKr4cVOdE", + "outputId": "e275e119-9fea-451e-89ae-6b3746cbf96d", + "colab": { + "base_uri": "https://localhost:8080/" + } + }, + "source": [ + "# Train the model on our training data while validating on our validation set\n", + "history_1 = model_1.fit(x_train, y_train, epochs=500, batch_size=64,\n", + " validation_data=(x_validate, y_validate))" + ], + "execution_count": null, + "outputs": [ + { + "output_type": "stream", + "text": [ + "Epoch 1/500\n", + "10/10 [==============================] - 1s 47ms/step - loss: 0.7289 - mae: 0.7120 - val_loss: 0.6401 - val_mae: 0.6504\n", + "Epoch 2/500\n", + "10/10 [==============================] - 0s 6ms/step - loss: 0.6329 - mae: 0.6488 - val_loss: 0.5587 - val_mae: 0.6031\n", + "Epoch 3/500\n", + "10/10 [==============================] - 0s 6ms/step - loss: 0.5201 - mae: 0.5735 - val_loss: 0.5014 - val_mae: 0.5763\n", + "Epoch 4/500\n", + "10/10 [==============================] - 0s 6ms/step - loss: 0.5057 - mae: 0.5760 - val_loss: 0.4632 - val_mae: 0.5615\n", + "Epoch 5/500\n", + "10/10 [==============================] - 0s 5ms/step - loss: 0.4502 - mae: 0.5459 - val_loss: 0.4386 - val_mae: 0.5536\n", + "Epoch 6/500\n", + "10/10 [==============================] - 0s 6ms/step - loss: 0.4168 - mae: 0.5332 - val_loss: 0.4227 - val_mae: 0.5490\n", + "Epoch 7/500\n", + "10/10 [==============================] - 0s 6ms/step - loss: 0.4211 - mae: 0.5341 - val_loss: 0.4125 - val_mae: 0.5464\n", + "Epoch 8/500\n", + "10/10 [==============================] - 0s 6ms/step - loss: 0.3988 - mae: 0.5287 - val_loss: 0.4060 - val_mae: 0.5452\n", + "Epoch 9/500\n", + "10/10 [==============================] - 0s 5ms/step - loss: 0.3901 - mae: 0.5230 - val_loss: 0.4014 - val_mae: 0.5440\n", + "Epoch 10/500\n", + "10/10 [==============================] - 0s 5ms/step - loss: 0.3804 - mae: 0.5179 - val_loss: 0.3979 - val_mae: 0.5426\n", + "Epoch 11/500\n", + "10/10 [==============================] - 0s 5ms/step - loss: 0.3695 - mae: 0.5150 - val_loss: 0.3950 - val_mae: 0.5412\n", + "Epoch 12/500\n", + "10/10 [==============================] - 0s 5ms/step - loss: 0.3856 - mae: 0.5245 - val_loss: 0.3921 - val_mae: 0.5399\n", + "Epoch 13/500\n", + "10/10 [==============================] - 0s 6ms/step - loss: 0.3744 - mae: 0.5184 - val_loss: 0.3893 - val_mae: 0.5386\n", + "Epoch 14/500\n", + "10/10 [==============================] - 0s 6ms/step - loss: 0.3749 - mae: 0.5175 - val_loss: 0.3865 - val_mae: 0.5371\n", + "Epoch 15/500\n", + "10/10 [==============================] - 0s 5ms/step - loss: 0.3467 - mae: 0.4993 - val_loss: 0.3837 - val_mae: 0.5354\n", + "Epoch 16/500\n", + "10/10 [==============================] - 0s 6ms/step - loss: 0.3736 - mae: 0.5234 - val_loss: 0.3808 - val_mae: 0.5336\n", + "Epoch 17/500\n", + "10/10 [==============================] - 0s 6ms/step - loss: 0.3655 - mae: 0.5148 - val_loss: 0.3778 - val_mae: 0.5318\n", + "Epoch 18/500\n", + "10/10 [==============================] - 0s 5ms/step - loss: 0.3558 - mae: 0.5067 - val_loss: 0.3747 - val_mae: 0.5297\n", + "Epoch 19/500\n", + "10/10 [==============================] - 0s 5ms/step - loss: 0.3343 - mae: 0.4908 - val_loss: 0.3716 - val_mae: 0.5275\n", + "Epoch 20/500\n", + "10/10 [==============================] - 0s 5ms/step - loss: 0.3742 - mae: 0.5257 - val_loss: 0.3686 - val_mae: 0.5258\n", + "Epoch 21/500\n", + "10/10 [==============================] - 0s 5ms/step - loss: 0.3296 - mae: 0.4831 - val_loss: 0.3654 - val_mae: 0.5235\n", + "Epoch 22/500\n", + "10/10 [==============================] - 0s 5ms/step - loss: 0.3432 - mae: 0.4962 - val_loss: 0.3622 - val_mae: 0.5214\n", + "Epoch 23/500\n", + "10/10 [==============================] - 0s 5ms/step - loss: 0.3397 - mae: 0.4951 - val_loss: 0.3589 - val_mae: 0.5191\n", + "Epoch 24/500\n", + "10/10 [==============================] - 0s 6ms/step - loss: 0.3229 - mae: 0.4803 - val_loss: 0.3558 - val_mae: 0.5172\n", + "Epoch 25/500\n", + "10/10 [==============================] - 0s 6ms/step - loss: 0.3562 - mae: 0.5105 - val_loss: 0.3524 - val_mae: 0.5150\n", + "Epoch 26/500\n", + "10/10 [==============================] - 0s 5ms/step - loss: 0.3458 - mae: 0.5042 - val_loss: 0.3492 - val_mae: 0.5128\n", + "Epoch 27/500\n", + "10/10 [==============================] - 0s 5ms/step - loss: 0.3163 - mae: 0.4764 - val_loss: 0.3459 - val_mae: 0.5106\n", + "Epoch 28/500\n", + "10/10 [==============================] - 0s 5ms/step - loss: 0.3441 - mae: 0.5018 - val_loss: 0.3427 - val_mae: 0.5086\n", + "Epoch 29/500\n", + "10/10 [==============================] - 0s 5ms/step - loss: 0.3062 - mae: 0.4705 - val_loss: 0.3395 - val_mae: 0.5065\n", + "Epoch 30/500\n", + "10/10 [==============================] - 0s 5ms/step - loss: 0.3202 - mae: 0.4808 - val_loss: 0.3362 - val_mae: 0.5043\n", + "Epoch 31/500\n", + "10/10 [==============================] - 0s 7ms/step - loss: 0.3313 - mae: 0.4919 - val_loss: 0.3330 - val_mae: 0.5022\n", + "Epoch 32/500\n", + "10/10 [==============================] - 0s 18ms/step - loss: 0.3028 - mae: 0.4682 - val_loss: 0.3297 - val_mae: 0.4996\n", + "Epoch 33/500\n", + "10/10 [==============================] - 0s 5ms/step - loss: 0.3056 - mae: 0.4670 - val_loss: 0.3264 - val_mae: 0.4972\n", + "Epoch 34/500\n", + "10/10 [==============================] - 0s 5ms/step - loss: 0.3203 - mae: 0.4781 - val_loss: 0.3233 - val_mae: 0.4954\n", + "Epoch 35/500\n", + "10/10 [==============================] - 0s 6ms/step - loss: 0.3256 - mae: 0.4912 - val_loss: 0.3201 - val_mae: 0.4929\n", + "Epoch 36/500\n", + "10/10 [==============================] - 0s 5ms/step - loss: 0.3079 - mae: 0.4728 - val_loss: 0.3170 - val_mae: 0.4905\n", + "Epoch 37/500\n", + "10/10 [==============================] - 0s 6ms/step - loss: 0.2969 - mae: 0.4641 - val_loss: 0.3139 - val_mae: 0.4885\n", + "Epoch 38/500\n", + "10/10 [==============================] - 0s 6ms/step - loss: 0.3043 - mae: 0.4693 - val_loss: 0.3108 - val_mae: 0.4863\n", + "Epoch 39/500\n", + "10/10 [==============================] - 0s 6ms/step - loss: 0.2902 - mae: 0.4549 - val_loss: 0.3078 - val_mae: 0.4843\n", + "Epoch 40/500\n", + "10/10 [==============================] - 0s 6ms/step - loss: 0.3003 - mae: 0.4720 - val_loss: 0.3047 - val_mae: 0.4823\n", + "Epoch 41/500\n", + "10/10 [==============================] - 0s 6ms/step - loss: 0.2970 - mae: 0.4678 - val_loss: 0.3017 - val_mae: 0.4804\n", + "Epoch 42/500\n", + "10/10 [==============================] - 0s 6ms/step - loss: 0.2903 - mae: 0.4582 - val_loss: 0.2988 - val_mae: 0.4787\n", + "Epoch 43/500\n", + "10/10 [==============================] - 0s 6ms/step - loss: 0.2853 - mae: 0.4553 - val_loss: 0.2960 - val_mae: 0.4769\n", + "Epoch 44/500\n", + "10/10 [==============================] - 0s 6ms/step - loss: 0.2910 - mae: 0.4603 - val_loss: 0.2931 - val_mae: 0.4748\n", + "Epoch 45/500\n", + "10/10 [==============================] - 0s 6ms/step - loss: 0.2819 - mae: 0.4533 - val_loss: 0.2902 - val_mae: 0.4727\n", + "Epoch 46/500\n", + "10/10 [==============================] - 0s 6ms/step - loss: 0.2744 - mae: 0.4525 - val_loss: 0.2872 - val_mae: 0.4697\n", + "Epoch 47/500\n", + "10/10 [==============================] - 0s 5ms/step - loss: 0.2707 - mae: 0.4411 - val_loss: 0.2845 - val_mae: 0.4680\n", + "Epoch 48/500\n", + "10/10 [==============================] - 0s 6ms/step - loss: 0.2641 - mae: 0.4414 - val_loss: 0.2818 - val_mae: 0.4661\n", + "Epoch 49/500\n", + "10/10 [==============================] - 0s 6ms/step - loss: 0.2642 - mae: 0.4378 - val_loss: 0.2793 - val_mae: 0.4647\n", + "Epoch 50/500\n", + "10/10 [==============================] - 0s 6ms/step - loss: 0.2603 - mae: 0.4385 - val_loss: 0.2767 - val_mae: 0.4628\n", + "Epoch 51/500\n", + "10/10 [==============================] - 0s 6ms/step - loss: 0.2684 - mae: 0.4473 - val_loss: 0.2740 - val_mae: 0.4604\n", + "Epoch 52/500\n", + "10/10 [==============================] - 0s 6ms/step - loss: 0.2539 - mae: 0.4312 - val_loss: 0.2714 - val_mae: 0.4583\n", + "Epoch 53/500\n", + "10/10 [==============================] - 0s 6ms/step - loss: 0.2621 - mae: 0.4417 - val_loss: 0.2690 - val_mae: 0.4568\n", + "Epoch 54/500\n", + "10/10 [==============================] - 0s 6ms/step - loss: 0.2556 - mae: 0.4366 - val_loss: 0.2664 - val_mae: 0.4545\n", + "Epoch 55/500\n", + "10/10 [==============================] - 0s 6ms/step - loss: 0.2524 - mae: 0.4309 - val_loss: 0.2639 - val_mae: 0.4525\n", + "Epoch 56/500\n", + "10/10 [==============================] - 0s 6ms/step - loss: 0.2555 - mae: 0.4364 - val_loss: 0.2614 - val_mae: 0.4507\n", + "Epoch 57/500\n", + "10/10 [==============================] - 0s 6ms/step - loss: 0.2483 - mae: 0.4264 - val_loss: 0.2589 - val_mae: 0.4485\n", + "Epoch 58/500\n", + "10/10 [==============================] - 0s 7ms/step - loss: 0.2403 - mae: 0.4212 - val_loss: 0.2564 - val_mae: 0.4460\n", + "Epoch 59/500\n", + "10/10 [==============================] - 0s 6ms/step - loss: 0.2462 - mae: 0.4274 - val_loss: 0.2542 - val_mae: 0.4446\n", + "Epoch 60/500\n", + "10/10 [==============================] - 0s 5ms/step - loss: 0.2364 - mae: 0.4178 - val_loss: 0.2522 - val_mae: 0.4437\n", + "Epoch 61/500\n", + "10/10 [==============================] - 0s 6ms/step - loss: 0.2409 - mae: 0.4254 - val_loss: 0.2500 - val_mae: 0.4418\n", + "Epoch 62/500\n", + "10/10 [==============================] - 0s 5ms/step - loss: 0.2338 - mae: 0.4172 - val_loss: 0.2478 - val_mae: 0.4400\n", + "Epoch 63/500\n", + "10/10 [==============================] - 0s 6ms/step - loss: 0.2283 - mae: 0.4132 - val_loss: 0.2456 - val_mae: 0.4381\n", + "Epoch 64/500\n", + "10/10 [==============================] - 0s 6ms/step - loss: 0.2438 - mae: 0.4330 - val_loss: 0.2433 - val_mae: 0.4360\n", + "Epoch 65/500\n", + "10/10 [==============================] - 0s 5ms/step - loss: 0.2169 - mae: 0.4049 - val_loss: 0.2415 - val_mae: 0.4348\n", + "Epoch 66/500\n", + "10/10 [==============================] - 0s 5ms/step - loss: 0.2208 - mae: 0.4087 - val_loss: 0.2393 - val_mae: 0.4329\n", + "Epoch 67/500\n", + "10/10 [==============================] - 0s 5ms/step - loss: 0.2440 - mae: 0.4321 - val_loss: 0.2373 - val_mae: 0.4312\n", + "Epoch 68/500\n", + "10/10 [==============================] - 0s 5ms/step - loss: 0.2250 - mae: 0.4131 - val_loss: 0.2353 - val_mae: 0.4295\n", + "Epoch 69/500\n", + "10/10 [==============================] - 0s 6ms/step - loss: 0.2222 - mae: 0.4081 - val_loss: 0.2334 - val_mae: 0.4277\n", + "Epoch 70/500\n", + "10/10 [==============================] - 0s 5ms/step - loss: 0.2245 - mae: 0.4138 - val_loss: 0.2316 - val_mae: 0.4261\n", + "Epoch 71/500\n", + "10/10 [==============================] - 0s 6ms/step - loss: 0.2132 - mae: 0.3983 - val_loss: 0.2298 - val_mae: 0.4244\n", + "Epoch 72/500\n", + "10/10 [==============================] - 0s 18ms/step - loss: 0.2232 - mae: 0.4144 - val_loss: 0.2280 - val_mae: 0.4227\n", + "Epoch 73/500\n", + "10/10 [==============================] - 0s 7ms/step - loss: 0.2077 - mae: 0.3941 - val_loss: 0.2265 - val_mae: 0.4219\n", + "Epoch 74/500\n", + "10/10 [==============================] - 0s 5ms/step - loss: 0.2116 - mae: 0.3993 - val_loss: 0.2249 - val_mae: 0.4205\n", + "Epoch 75/500\n", + "10/10 [==============================] - 0s 7ms/step - loss: 0.2227 - mae: 0.4148 - val_loss: 0.2235 - val_mae: 0.4198\n", + "Epoch 76/500\n", + "10/10 [==============================] - 0s 5ms/step - loss: 0.2026 - mae: 0.3917 - val_loss: 0.2216 - val_mae: 0.4175\n", + "Epoch 77/500\n", + "10/10 [==============================] - 0s 6ms/step - loss: 0.2083 - mae: 0.3966 - val_loss: 0.2200 - val_mae: 0.4157\n", + "Epoch 78/500\n", + "10/10 [==============================] - 0s 5ms/step - loss: 0.2055 - mae: 0.3947 - val_loss: 0.2186 - val_mae: 0.4144\n", + "Epoch 79/500\n", + "10/10 [==============================] - 0s 5ms/step - loss: 0.2069 - mae: 0.3965 - val_loss: 0.2171 - val_mae: 0.4128\n", + "Epoch 80/500\n", + "10/10 [==============================] - 0s 5ms/step - loss: 0.1983 - mae: 0.3871 - val_loss: 0.2158 - val_mae: 0.4117\n", + "Epoch 81/500\n", + "10/10 [==============================] - 0s 6ms/step - loss: 0.1994 - mae: 0.3876 - val_loss: 0.2146 - val_mae: 0.4109\n", + "Epoch 82/500\n", + "10/10 [==============================] - 0s 6ms/step - loss: 0.1971 - mae: 0.3824 - val_loss: 0.2135 - val_mae: 0.4104\n", + "Epoch 83/500\n", + "10/10 [==============================] - 0s 6ms/step - loss: 0.1965 - mae: 0.3879 - val_loss: 0.2120 - val_mae: 0.4085\n", + "Epoch 84/500\n", + "10/10 [==============================] - 0s 5ms/step - loss: 0.2006 - mae: 0.3906 - val_loss: 0.2109 - val_mae: 0.4074\n", + "Epoch 85/500\n", + "10/10 [==============================] - 0s 6ms/step - loss: 0.2048 - mae: 0.3979 - val_loss: 0.2096 - val_mae: 0.4057\n", + "Epoch 86/500\n", + "10/10 [==============================] - 0s 6ms/step - loss: 0.1932 - mae: 0.3864 - val_loss: 0.2083 - val_mae: 0.4042\n", + "Epoch 87/500\n", + "10/10 [==============================] - 0s 6ms/step - loss: 0.1916 - mae: 0.3830 - val_loss: 0.2073 - val_mae: 0.4036\n", + "Epoch 88/500\n", + "10/10 [==============================] - 0s 5ms/step - loss: 0.1984 - mae: 0.3905 - val_loss: 0.2062 - val_mae: 0.4023\n", + "Epoch 89/500\n", + "10/10 [==============================] - 0s 6ms/step - loss: 0.1939 - mae: 0.3874 - val_loss: 0.2052 - val_mae: 0.4010\n", + "Epoch 90/500\n", + "10/10 [==============================] - 0s 5ms/step - loss: 0.1902 - mae: 0.3827 - val_loss: 0.2042 - val_mae: 0.4001\n", + "Epoch 91/500\n", + "10/10 [==============================] - 0s 7ms/step - loss: 0.1888 - mae: 0.3784 - val_loss: 0.2038 - val_mae: 0.4009\n", + "Epoch 92/500\n", + "10/10 [==============================] - 0s 6ms/step - loss: 0.1927 - mae: 0.3843 - val_loss: 0.2030 - val_mae: 0.4003\n", + "Epoch 93/500\n", + "10/10 [==============================] - 0s 5ms/step - loss: 0.1851 - mae: 0.3730 - val_loss: 0.2018 - val_mae: 0.3981\n", + "Epoch 94/500\n", + "10/10 [==============================] - 0s 6ms/step - loss: 0.1984 - mae: 0.3916 - val_loss: 0.2008 - val_mae: 0.3963\n", + "Epoch 95/500\n", + "10/10 [==============================] - 0s 5ms/step - loss: 0.1851 - mae: 0.3733 - val_loss: 0.2001 - val_mae: 0.3960\n", + "Epoch 96/500\n", + "10/10 [==============================] - 0s 5ms/step - loss: 0.1881 - mae: 0.3796 - val_loss: 0.1994 - val_mae: 0.3953\n", + "Epoch 97/500\n", + "10/10 [==============================] - 0s 6ms/step - loss: 0.1859 - mae: 0.3765 - val_loss: 0.1987 - val_mae: 0.3949\n", + "Epoch 98/500\n", + "10/10 [==============================] - 0s 6ms/step - loss: 0.1822 - mae: 0.3711 - val_loss: 0.1979 - val_mae: 0.3935\n", + "Epoch 99/500\n", + "10/10 [==============================] - 0s 6ms/step - loss: 0.1886 - mae: 0.3764 - val_loss: 0.1970 - val_mae: 0.3915\n", + "Epoch 100/500\n", + "10/10 [==============================] - 0s 5ms/step - loss: 0.1878 - mae: 0.3758 - val_loss: 0.1964 - val_mae: 0.3908\n", + "Epoch 101/500\n", + "10/10 [==============================] - 0s 6ms/step - loss: 0.1769 - mae: 0.3670 - val_loss: 0.1958 - val_mae: 0.3897\n", + "Epoch 102/500\n", + "10/10 [==============================] - 0s 5ms/step - loss: 0.1832 - mae: 0.3731 - val_loss: 0.1953 - val_mae: 0.3897\n", + "Epoch 103/500\n", + "10/10 [==============================] - 0s 6ms/step - loss: 0.1827 - mae: 0.3728 - val_loss: 0.1952 - val_mae: 0.3910\n", + "Epoch 104/500\n", + "10/10 [==============================] - 0s 5ms/step - loss: 0.1867 - mae: 0.3783 - val_loss: 0.1945 - val_mae: 0.3898\n", + "Epoch 105/500\n", + "10/10 [==============================] - 0s 6ms/step - loss: 0.1813 - mae: 0.3691 - val_loss: 0.1937 - val_mae: 0.3869\n", + "Epoch 106/500\n", + "10/10 [==============================] - 0s 6ms/step - loss: 0.1715 - mae: 0.3605 - val_loss: 0.1932 - val_mae: 0.3869\n", + "Epoch 107/500\n", + "10/10 [==============================] - 0s 5ms/step - loss: 0.1800 - mae: 0.3692 - val_loss: 0.1928 - val_mae: 0.3859\n", + "Epoch 108/500\n", + "10/10 [==============================] - 0s 7ms/step - loss: 0.1884 - mae: 0.3725 - val_loss: 0.1925 - val_mae: 0.3863\n", + "Epoch 109/500\n", + "10/10 [==============================] - 0s 6ms/step - loss: 0.1777 - mae: 0.3686 - val_loss: 0.1922 - val_mae: 0.3862\n", + "Epoch 110/500\n", + "10/10 [==============================] - 0s 5ms/step - loss: 0.1801 - mae: 0.3715 - val_loss: 0.1917 - val_mae: 0.3853\n", + "Epoch 111/500\n", + "10/10 [==============================] - 0s 5ms/step - loss: 0.1794 - mae: 0.3665 - val_loss: 0.1913 - val_mae: 0.3846\n", + "Epoch 112/500\n", + "10/10 [==============================] - 0s 6ms/step - loss: 0.1811 - mae: 0.3664 - val_loss: 0.1908 - val_mae: 0.3831\n", + "Epoch 113/500\n", + "10/10 [==============================] - 0s 6ms/step - loss: 0.1821 - mae: 0.3655 - val_loss: 0.1904 - val_mae: 0.3823\n", + "Epoch 114/500\n", + "10/10 [==============================] - 0s 5ms/step - loss: 0.1755 - mae: 0.3621 - val_loss: 0.1901 - val_mae: 0.3818\n", + "Epoch 115/500\n", + "10/10 [==============================] - 0s 5ms/step - loss: 0.1718 - mae: 0.3584 - val_loss: 0.1899 - val_mae: 0.3820\n", + "Epoch 116/500\n", + "10/10 [==============================] - 0s 5ms/step - loss: 0.1684 - mae: 0.3516 - val_loss: 0.1896 - val_mae: 0.3815\n", + "Epoch 117/500\n", + "10/10 [==============================] - 0s 5ms/step - loss: 0.1760 - mae: 0.3618 - val_loss: 0.1894 - val_mae: 0.3816\n", + "Epoch 118/500\n", + "10/10 [==============================] - 0s 5ms/step - loss: 0.1748 - mae: 0.3587 - val_loss: 0.1890 - val_mae: 0.3804\n", + "Epoch 119/500\n", + "10/10 [==============================] - 0s 6ms/step - loss: 0.1749 - mae: 0.3626 - val_loss: 0.1887 - val_mae: 0.3792\n", + "Epoch 120/500\n", + "10/10 [==============================] - 0s 6ms/step - loss: 0.1620 - mae: 0.3493 - val_loss: 0.1884 - val_mae: 0.3779\n", + "Epoch 121/500\n", + "10/10 [==============================] - 0s 5ms/step - loss: 0.1720 - mae: 0.3573 - val_loss: 0.1883 - val_mae: 0.3789\n", + "Epoch 122/500\n", + "10/10 [==============================] - 0s 18ms/step - loss: 0.1767 - mae: 0.3623 - val_loss: 0.1881 - val_mae: 0.3787\n", + "Epoch 123/500\n", + "10/10 [==============================] - 0s 5ms/step - loss: 0.1835 - mae: 0.3729 - val_loss: 0.1881 - val_mae: 0.3794\n", + "Epoch 124/500\n", + "10/10 [==============================] - 0s 7ms/step - loss: 0.1782 - mae: 0.3691 - val_loss: 0.1876 - val_mae: 0.3775\n", + "Epoch 125/500\n", + "10/10 [==============================] - 0s 6ms/step - loss: 0.1669 - mae: 0.3548 - val_loss: 0.1877 - val_mae: 0.3784\n", + "Epoch 126/500\n", + "10/10 [==============================] - 0s 5ms/step - loss: 0.1819 - mae: 0.3693 - val_loss: 0.1878 - val_mae: 0.3791\n", + "Epoch 127/500\n", + "10/10 [==============================] - 0s 6ms/step - loss: 0.1731 - mae: 0.3626 - val_loss: 0.1877 - val_mae: 0.3789\n", + "Epoch 128/500\n", + "10/10 [==============================] - 0s 6ms/step - loss: 0.1696 - mae: 0.3556 - val_loss: 0.1872 - val_mae: 0.3773\n", + "Epoch 129/500\n", + "10/10 [==============================] - 0s 5ms/step - loss: 0.1764 - mae: 0.3649 - val_loss: 0.1869 - val_mae: 0.3758\n", + "Epoch 130/500\n", + "10/10 [==============================] - 0s 6ms/step - loss: 0.1770 - mae: 0.3649 - val_loss: 0.1867 - val_mae: 0.3750\n", + "Epoch 131/500\n", + "10/10 [==============================] - 0s 7ms/step - loss: 0.1857 - mae: 0.3696 - val_loss: 0.1867 - val_mae: 0.3760\n", + "Epoch 132/500\n", + "10/10 [==============================] - 0s 6ms/step - loss: 0.1715 - mae: 0.3566 - val_loss: 0.1865 - val_mae: 0.3754\n", + "Epoch 133/500\n", + "10/10 [==============================] - 0s 6ms/step - loss: 0.1717 - mae: 0.3536 - val_loss: 0.1869 - val_mae: 0.3772\n", + "Epoch 134/500\n", + "10/10 [==============================] - 0s 6ms/step - loss: 0.1692 - mae: 0.3558 - val_loss: 0.1863 - val_mae: 0.3751\n", + "Epoch 135/500\n", + "10/10 [==============================] - 0s 5ms/step - loss: 0.1844 - mae: 0.3690 - val_loss: 0.1862 - val_mae: 0.3744\n", + "Epoch 136/500\n", + "10/10 [==============================] - 0s 7ms/step - loss: 0.1608 - mae: 0.3431 - val_loss: 0.1861 - val_mae: 0.3737\n", + "Epoch 137/500\n", + "10/10 [==============================] - 0s 6ms/step - loss: 0.1626 - mae: 0.3457 - val_loss: 0.1860 - val_mae: 0.3739\n", + "Epoch 138/500\n", + "10/10 [==============================] - 0s 5ms/step - loss: 0.1705 - mae: 0.3598 - val_loss: 0.1861 - val_mae: 0.3748\n", + "Epoch 139/500\n", + "10/10 [==============================] - 0s 6ms/step - loss: 0.1797 - mae: 0.3651 - val_loss: 0.1863 - val_mae: 0.3759\n", + "Epoch 140/500\n", + "10/10 [==============================] - 0s 6ms/step - loss: 0.1692 - mae: 0.3543 - val_loss: 0.1858 - val_mae: 0.3739\n", + "Epoch 141/500\n", + "10/10 [==============================] - 0s 6ms/step - loss: 0.1696 - mae: 0.3572 - val_loss: 0.1859 - val_mae: 0.3743\n", + "Epoch 142/500\n", + "10/10 [==============================] - 0s 6ms/step - loss: 0.1652 - mae: 0.3503 - val_loss: 0.1861 - val_mae: 0.3754\n", + "Epoch 143/500\n", + "10/10 [==============================] - 0s 6ms/step - loss: 0.1644 - mae: 0.3504 - val_loss: 0.1857 - val_mae: 0.3734\n", + "Epoch 144/500\n", + "10/10 [==============================] - 0s 6ms/step - loss: 0.1721 - mae: 0.3567 - val_loss: 0.1855 - val_mae: 0.3728\n", + "Epoch 145/500\n", + "10/10 [==============================] - 0s 6ms/step - loss: 0.1772 - mae: 0.3612 - val_loss: 0.1856 - val_mae: 0.3737\n", + "Epoch 146/500\n", + "10/10 [==============================] - 0s 7ms/step - loss: 0.1654 - mae: 0.3502 - val_loss: 0.1856 - val_mae: 0.3736\n", + "Epoch 147/500\n", + "10/10 [==============================] - 0s 6ms/step - loss: 0.1761 - mae: 0.3575 - val_loss: 0.1856 - val_mae: 0.3738\n", + "Epoch 148/500\n", + "10/10 [==============================] - 0s 5ms/step - loss: 0.1693 - mae: 0.3542 - val_loss: 0.1853 - val_mae: 0.3719\n", + "Epoch 149/500\n", + "10/10 [==============================] - 0s 6ms/step - loss: 0.1634 - mae: 0.3450 - val_loss: 0.1854 - val_mae: 0.3727\n", + "Epoch 150/500\n", + "10/10 [==============================] - 0s 6ms/step - loss: 0.1642 - mae: 0.3457 - val_loss: 0.1853 - val_mae: 0.3723\n", + "Epoch 151/500\n", + "10/10 [==============================] - 0s 7ms/step - loss: 0.1868 - mae: 0.3703 - val_loss: 0.1854 - val_mae: 0.3731\n", + "Epoch 152/500\n", + "10/10 [==============================] - 0s 7ms/step - loss: 0.1797 - mae: 0.3615 - val_loss: 0.1852 - val_mae: 0.3716\n", + "Epoch 153/500\n", + "10/10 [==============================] - 0s 6ms/step - loss: 0.1739 - mae: 0.3548 - val_loss: 0.1851 - val_mae: 0.3716\n", + "Epoch 154/500\n", + "10/10 [==============================] - 0s 6ms/step - loss: 0.1779 - mae: 0.3633 - val_loss: 0.1851 - val_mae: 0.3711\n", + "Epoch 155/500\n", + "10/10 [==============================] - 0s 6ms/step - loss: 0.1606 - mae: 0.3401 - val_loss: 0.1850 - val_mae: 0.3709\n", + "Epoch 156/500\n", + "10/10 [==============================] - 0s 6ms/step - loss: 0.1834 - mae: 0.3646 - val_loss: 0.1853 - val_mae: 0.3728\n", + "Epoch 157/500\n", + "10/10 [==============================] - 0s 5ms/step - loss: 0.1704 - mae: 0.3552 - val_loss: 0.1850 - val_mae: 0.3712\n", + "Epoch 158/500\n", + "10/10 [==============================] - 0s 5ms/step - loss: 0.1741 - mae: 0.3575 - val_loss: 0.1850 - val_mae: 0.3714\n", + "Epoch 159/500\n", + "10/10 [==============================] - 0s 6ms/step - loss: 0.1624 - mae: 0.3450 - val_loss: 0.1849 - val_mae: 0.3705\n", + "Epoch 160/500\n", + "10/10 [==============================] - 0s 7ms/step - loss: 0.1691 - mae: 0.3547 - val_loss: 0.1850 - val_mae: 0.3712\n", + "Epoch 161/500\n", + "10/10 [==============================] - 0s 6ms/step - loss: 0.1604 - mae: 0.3414 - val_loss: 0.1849 - val_mae: 0.3703\n", + "Epoch 162/500\n", + "10/10 [==============================] - 0s 18ms/step - loss: 0.1600 - mae: 0.3412 - val_loss: 0.1848 - val_mae: 0.3700\n", + "Epoch 163/500\n", + "10/10 [==============================] - 0s 6ms/step - loss: 0.1564 - mae: 0.3413 - val_loss: 0.1848 - val_mae: 0.3694\n", + "Epoch 164/500\n", + "10/10 [==============================] - 0s 6ms/step - loss: 0.1664 - mae: 0.3461 - val_loss: 0.1851 - val_mae: 0.3719\n", + "Epoch 165/500\n", + "10/10 [==============================] - 0s 6ms/step - loss: 0.1672 - mae: 0.3500 - val_loss: 0.1848 - val_mae: 0.3698\n", + "Epoch 166/500\n", + "10/10 [==============================] - 0s 5ms/step - loss: 0.1717 - mae: 0.3600 - val_loss: 0.1847 - val_mae: 0.3694\n", + "Epoch 167/500\n", + "10/10 [==============================] - 0s 6ms/step - loss: 0.1645 - mae: 0.3450 - val_loss: 0.1849 - val_mae: 0.3707\n", + "Epoch 168/500\n", + "10/10 [==============================] - 0s 6ms/step - loss: 0.1697 - mae: 0.3467 - val_loss: 0.1853 - val_mae: 0.3724\n", + "Epoch 169/500\n", + "10/10 [==============================] - 0s 6ms/step - loss: 0.1742 - mae: 0.3566 - val_loss: 0.1850 - val_mae: 0.3712\n", + "Epoch 170/500\n", + "10/10 [==============================] - 0s 6ms/step - loss: 0.1650 - mae: 0.3455 - val_loss: 0.1847 - val_mae: 0.3693\n", + "Epoch 171/500\n", + "10/10 [==============================] - 0s 6ms/step - loss: 0.1667 - mae: 0.3511 - val_loss: 0.1847 - val_mae: 0.3693\n", + "Epoch 172/500\n", + "10/10 [==============================] - 0s 6ms/step - loss: 0.1689 - mae: 0.3476 - val_loss: 0.1849 - val_mae: 0.3710\n", + "Epoch 173/500\n", + "10/10 [==============================] - 0s 6ms/step - loss: 0.1709 - mae: 0.3538 - val_loss: 0.1848 - val_mae: 0.3706\n", + "Epoch 174/500\n", + "10/10 [==============================] - 0s 6ms/step - loss: 0.1794 - mae: 0.3588 - val_loss: 0.1847 - val_mae: 0.3696\n", + "Epoch 175/500\n", + "10/10 [==============================] - 0s 6ms/step - loss: 0.1753 - mae: 0.3539 - val_loss: 0.1846 - val_mae: 0.3680\n", + "Epoch 176/500\n", + "10/10 [==============================] - 0s 6ms/step - loss: 0.1704 - mae: 0.3511 - val_loss: 0.1846 - val_mae: 0.3686\n", + "Epoch 177/500\n", + "10/10 [==============================] - 0s 6ms/step - loss: 0.1635 - mae: 0.3465 - val_loss: 0.1846 - val_mae: 0.3691\n", + "Epoch 178/500\n", + "10/10 [==============================] - 0s 6ms/step - loss: 0.1669 - mae: 0.3508 - val_loss: 0.1850 - val_mae: 0.3712\n", + "Epoch 179/500\n", + "10/10 [==============================] - 0s 6ms/step - loss: 0.1661 - mae: 0.3434 - val_loss: 0.1847 - val_mae: 0.3696\n", + "Epoch 180/500\n", + "10/10 [==============================] - 0s 6ms/step - loss: 0.1668 - mae: 0.3500 - val_loss: 0.1847 - val_mae: 0.3696\n", + "Epoch 181/500\n", + "10/10 [==============================] - 0s 6ms/step - loss: 0.1600 - mae: 0.3416 - val_loss: 0.1846 - val_mae: 0.3689\n", + "Epoch 182/500\n", + "10/10 [==============================] - 0s 5ms/step - loss: 0.1672 - mae: 0.3500 - val_loss: 0.1846 - val_mae: 0.3693\n", + "Epoch 183/500\n", + "10/10 [==============================] - 0s 7ms/step - loss: 0.1663 - mae: 0.3461 - val_loss: 0.1847 - val_mae: 0.3698\n", + "Epoch 184/500\n", + "10/10 [==============================] - 0s 8ms/step - loss: 0.1690 - mae: 0.3494 - val_loss: 0.1847 - val_mae: 0.3695\n", + "Epoch 185/500\n", + "10/10 [==============================] - 0s 6ms/step - loss: 0.1716 - mae: 0.3513 - val_loss: 0.1846 - val_mae: 0.3690\n", + "Epoch 186/500\n", + "10/10 [==============================] - 0s 6ms/step - loss: 0.1748 - mae: 0.3588 - val_loss: 0.1847 - val_mae: 0.3696\n", + "Epoch 187/500\n", + "10/10 [==============================] - 0s 6ms/step - loss: 0.1588 - mae: 0.3364 - val_loss: 0.1849 - val_mae: 0.3705\n", + "Epoch 188/500\n", + "10/10 [==============================] - 0s 6ms/step - loss: 0.1739 - mae: 0.3539 - val_loss: 0.1849 - val_mae: 0.3704\n", + "Epoch 189/500\n", + "10/10 [==============================] - 0s 6ms/step - loss: 0.1711 - mae: 0.3497 - val_loss: 0.1846 - val_mae: 0.3690\n", + "Epoch 190/500\n", + "10/10 [==============================] - 0s 5ms/step - loss: 0.1706 - mae: 0.3525 - val_loss: 0.1845 - val_mae: 0.3678\n", + "Epoch 191/500\n", + "10/10 [==============================] - 0s 6ms/step - loss: 0.1621 - mae: 0.3447 - val_loss: 0.1846 - val_mae: 0.3688\n", + "Epoch 192/500\n", + "10/10 [==============================] - 0s 6ms/step - loss: 0.1669 - mae: 0.3485 - val_loss: 0.1847 - val_mae: 0.3699\n", + "Epoch 193/500\n", + "10/10 [==============================] - 0s 5ms/step - loss: 0.1694 - mae: 0.3498 - val_loss: 0.1847 - val_mae: 0.3697\n", + "Epoch 194/500\n", + "10/10 [==============================] - 0s 5ms/step - loss: 0.1708 - mae: 0.3520 - val_loss: 0.1846 - val_mae: 0.3694\n", + "Epoch 195/500\n", + "10/10 [==============================] - 0s 7ms/step - loss: 0.1796 - mae: 0.3623 - val_loss: 0.1849 - val_mae: 0.3708\n", + "Epoch 196/500\n", + "10/10 [==============================] - 0s 7ms/step - loss: 0.1624 - mae: 0.3417 - val_loss: 0.1849 - val_mae: 0.3706\n", + "Epoch 197/500\n", + "10/10 [==============================] - 0s 6ms/step - loss: 0.1671 - mae: 0.3529 - val_loss: 0.1848 - val_mae: 0.3703\n", + "Epoch 198/500\n", + "10/10 [==============================] - 0s 6ms/step - loss: 0.1680 - mae: 0.3479 - val_loss: 0.1845 - val_mae: 0.3690\n", + "Epoch 199/500\n", + "10/10 [==============================] - 0s 7ms/step - loss: 0.1750 - mae: 0.3587 - val_loss: 0.1844 - val_mae: 0.3677\n", + "Epoch 200/500\n", + "10/10 [==============================] - 0s 6ms/step - loss: 0.1613 - mae: 0.3419 - val_loss: 0.1845 - val_mae: 0.3684\n", + "Epoch 201/500\n", + "10/10 [==============================] - 0s 6ms/step - loss: 0.1625 - mae: 0.3434 - val_loss: 0.1845 - val_mae: 0.3684\n", + "Epoch 202/500\n", + "10/10 [==============================] - 0s 6ms/step - loss: 0.1780 - mae: 0.3576 - val_loss: 0.1845 - val_mae: 0.3688\n", + "Epoch 203/500\n", + "10/10 [==============================] - 0s 7ms/step - loss: 0.1567 - mae: 0.3381 - val_loss: 0.1845 - val_mae: 0.3677\n", + "Epoch 204/500\n", + "10/10 [==============================] - 0s 6ms/step - loss: 0.1675 - mae: 0.3489 - val_loss: 0.1846 - val_mae: 0.3692\n", + "Epoch 205/500\n", + "10/10 [==============================] - 0s 5ms/step - loss: 0.1766 - mae: 0.3609 - val_loss: 0.1847 - val_mae: 0.3694\n", + "Epoch 206/500\n", + "10/10 [==============================] - 0s 7ms/step - loss: 0.1649 - mae: 0.3489 - val_loss: 0.1845 - val_mae: 0.3685\n", + "Epoch 207/500\n", + "10/10 [==============================] - 0s 7ms/step - loss: 0.1723 - mae: 0.3526 - val_loss: 0.1845 - val_mae: 0.3679\n", + "Epoch 208/500\n", + "10/10 [==============================] - 0s 6ms/step - loss: 0.1788 - mae: 0.3573 - val_loss: 0.1846 - val_mae: 0.3689\n", + "Epoch 209/500\n", + "10/10 [==============================] - 0s 5ms/step - loss: 0.1659 - mae: 0.3427 - val_loss: 0.1847 - val_mae: 0.3694\n", + "Epoch 210/500\n", + "10/10 [==============================] - 0s 6ms/step - loss: 0.1737 - mae: 0.3549 - val_loss: 0.1845 - val_mae: 0.3684\n", + "Epoch 211/500\n", + "10/10 [==============================] - 0s 6ms/step - loss: 0.1616 - mae: 0.3437 - val_loss: 0.1845 - val_mae: 0.3686\n", + "Epoch 212/500\n", + "10/10 [==============================] - 0s 18ms/step - loss: 0.1665 - mae: 0.3466 - val_loss: 0.1847 - val_mae: 0.3696\n", + "Epoch 213/500\n", + "10/10 [==============================] - 0s 5ms/step - loss: 0.1726 - mae: 0.3560 - val_loss: 0.1846 - val_mae: 0.3692\n", + "Epoch 214/500\n", + "10/10 [==============================] - 0s 6ms/step - loss: 0.1716 - mae: 0.3516 - val_loss: 0.1844 - val_mae: 0.3673\n", + "Epoch 215/500\n", + "10/10 [==============================] - 0s 6ms/step - loss: 0.1662 - mae: 0.3398 - val_loss: 0.1845 - val_mae: 0.3685\n", + "Epoch 216/500\n", + "10/10 [==============================] - 0s 6ms/step - loss: 0.1773 - mae: 0.3588 - val_loss: 0.1845 - val_mae: 0.3686\n", + "Epoch 217/500\n", + "10/10 [==============================] - 0s 6ms/step - loss: 0.1645 - mae: 0.3485 - val_loss: 0.1846 - val_mae: 0.3690\n", + "Epoch 218/500\n", + "10/10 [==============================] - 0s 5ms/step - loss: 0.1664 - mae: 0.3514 - val_loss: 0.1848 - val_mae: 0.3700\n", + "Epoch 219/500\n", + "10/10 [==============================] - 0s 6ms/step - loss: 0.1775 - mae: 0.3572 - val_loss: 0.1848 - val_mae: 0.3700\n", + "Epoch 220/500\n", + "10/10 [==============================] - 0s 6ms/step - loss: 0.1650 - mae: 0.3451 - val_loss: 0.1846 - val_mae: 0.3693\n", + "Epoch 221/500\n", + "10/10 [==============================] - 0s 6ms/step - loss: 0.1607 - mae: 0.3393 - val_loss: 0.1845 - val_mae: 0.3686\n", + "Epoch 222/500\n", + "10/10 [==============================] - 0s 6ms/step - loss: 0.1796 - mae: 0.3623 - val_loss: 0.1845 - val_mae: 0.3685\n", + "Epoch 223/500\n", + "10/10 [==============================] - 0s 5ms/step - loss: 0.1759 - mae: 0.3592 - val_loss: 0.1845 - val_mae: 0.3682\n", + "Epoch 224/500\n", + "10/10 [==============================] - 0s 5ms/step - loss: 0.1702 - mae: 0.3513 - val_loss: 0.1845 - val_mae: 0.3685\n", + "Epoch 225/500\n", + "10/10 [==============================] - 0s 7ms/step - loss: 0.1823 - mae: 0.3648 - val_loss: 0.1852 - val_mae: 0.3715\n", + "Epoch 226/500\n", + "10/10 [==============================] - 0s 7ms/step - loss: 0.1698 - mae: 0.3515 - val_loss: 0.1848 - val_mae: 0.3701\n", + "Epoch 227/500\n", + "10/10 [==============================] - 0s 6ms/step - loss: 0.1658 - mae: 0.3447 - val_loss: 0.1847 - val_mae: 0.3699\n", + "Epoch 228/500\n", + "10/10 [==============================] - 0s 5ms/step - loss: 0.1756 - mae: 0.3553 - val_loss: 0.1846 - val_mae: 0.3694\n", + "Epoch 229/500\n", + "10/10 [==============================] - 0s 6ms/step - loss: 0.1670 - mae: 0.3549 - val_loss: 0.1844 - val_mae: 0.3671\n", + "Epoch 230/500\n", + "10/10 [==============================] - 0s 6ms/step - loss: 0.1685 - mae: 0.3480 - val_loss: 0.1845 - val_mae: 0.3682\n", + "Epoch 231/500\n", + "10/10 [==============================] - 0s 6ms/step - loss: 0.1740 - mae: 0.3578 - val_loss: 0.1846 - val_mae: 0.3691\n", + "Epoch 232/500\n", + "10/10 [==============================] - 0s 5ms/step - loss: 0.1774 - mae: 0.3602 - val_loss: 0.1846 - val_mae: 0.3692\n", + "Epoch 233/500\n", + "10/10 [==============================] - 0s 7ms/step - loss: 0.1566 - mae: 0.3383 - val_loss: 0.1846 - val_mae: 0.3693\n", + "Epoch 234/500\n", + "10/10 [==============================] - 0s 5ms/step - loss: 0.1714 - mae: 0.3518 - val_loss: 0.1847 - val_mae: 0.3696\n", + "Epoch 235/500\n", + "10/10 [==============================] - 0s 5ms/step - loss: 0.1650 - mae: 0.3435 - val_loss: 0.1847 - val_mae: 0.3697\n", + "Epoch 236/500\n", + "10/10 [==============================] - 0s 6ms/step - loss: 0.1721 - mae: 0.3513 - val_loss: 0.1846 - val_mae: 0.3694\n", + "Epoch 237/500\n", + "10/10 [==============================] - 0s 7ms/step - loss: 0.1710 - mae: 0.3535 - val_loss: 0.1845 - val_mae: 0.3683\n", + "Epoch 238/500\n", + "10/10 [==============================] - 0s 6ms/step - loss: 0.1638 - mae: 0.3453 - val_loss: 0.1846 - val_mae: 0.3692\n", + "Epoch 239/500\n", + "10/10 [==============================] - 0s 5ms/step - loss: 0.1687 - mae: 0.3518 - val_loss: 0.1845 - val_mae: 0.3687\n", + "Epoch 240/500\n", + "10/10 [==============================] - 0s 7ms/step - loss: 0.1701 - mae: 0.3519 - val_loss: 0.1847 - val_mae: 0.3697\n", + "Epoch 241/500\n", + "10/10 [==============================] - 0s 6ms/step - loss: 0.1721 - mae: 0.3530 - val_loss: 0.1849 - val_mae: 0.3703\n", + "Epoch 242/500\n", + "10/10 [==============================] - 0s 6ms/step - loss: 0.1610 - mae: 0.3413 - val_loss: 0.1846 - val_mae: 0.3691\n", + "Epoch 243/500\n", + "10/10 [==============================] - 0s 6ms/step - loss: 0.1556 - mae: 0.3387 - val_loss: 0.1845 - val_mae: 0.3685\n", + "Epoch 244/500\n", + "10/10 [==============================] - 0s 5ms/step - loss: 0.1663 - mae: 0.3485 - val_loss: 0.1845 - val_mae: 0.3688\n", + "Epoch 245/500\n", + "10/10 [==============================] - 0s 5ms/step - loss: 0.1761 - mae: 0.3585 - val_loss: 0.1848 - val_mae: 0.3703\n", + "Epoch 246/500\n", + "10/10 [==============================] - 0s 6ms/step - loss: 0.1592 - mae: 0.3394 - val_loss: 0.1849 - val_mae: 0.3706\n", + "Epoch 247/500\n", + "10/10 [==============================] - 0s 7ms/step - loss: 0.1724 - mae: 0.3568 - val_loss: 0.1845 - val_mae: 0.3682\n", + "Epoch 248/500\n", + "10/10 [==============================] - 0s 6ms/step - loss: 0.1668 - mae: 0.3516 - val_loss: 0.1844 - val_mae: 0.3671\n", + "Epoch 249/500\n", + "10/10 [==============================] - 0s 6ms/step - loss: 0.1676 - mae: 0.3474 - val_loss: 0.1845 - val_mae: 0.3688\n", + "Epoch 250/500\n", + "10/10 [==============================] - 0s 7ms/step - loss: 0.1747 - mae: 0.3563 - val_loss: 0.1844 - val_mae: 0.3680\n", + "Epoch 251/500\n", + "10/10 [==============================] - 0s 6ms/step - loss: 0.1766 - mae: 0.3607 - val_loss: 0.1844 - val_mae: 0.3676\n", + "Epoch 252/500\n", + "10/10 [==============================] - 0s 6ms/step - loss: 0.1693 - mae: 0.3522 - val_loss: 0.1847 - val_mae: 0.3696\n", + "Epoch 253/500\n", + "10/10 [==============================] - 0s 19ms/step - loss: 0.1632 - mae: 0.3429 - val_loss: 0.1844 - val_mae: 0.3675\n", + "Epoch 254/500\n", + "10/10 [==============================] - 0s 6ms/step - loss: 0.1747 - mae: 0.3537 - val_loss: 0.1846 - val_mae: 0.3689\n", + "Epoch 255/500\n", + "10/10 [==============================] - 0s 6ms/step - loss: 0.1731 - mae: 0.3574 - val_loss: 0.1847 - val_mae: 0.3695\n", + "Epoch 256/500\n", + "10/10 [==============================] - 0s 6ms/step - loss: 0.1696 - mae: 0.3525 - val_loss: 0.1845 - val_mae: 0.3676\n", + "Epoch 257/500\n", + "10/10 [==============================] - 0s 7ms/step - loss: 0.1572 - mae: 0.3387 - val_loss: 0.1845 - val_mae: 0.3681\n", + "Epoch 258/500\n", + "10/10 [==============================] - 0s 6ms/step - loss: 0.1617 - mae: 0.3409 - val_loss: 0.1849 - val_mae: 0.3702\n", + "Epoch 259/500\n", + "10/10 [==============================] - 0s 5ms/step - loss: 0.1809 - mae: 0.3600 - val_loss: 0.1850 - val_mae: 0.3707\n", + "Epoch 260/500\n", + "10/10 [==============================] - 0s 7ms/step - loss: 0.1633 - mae: 0.3435 - val_loss: 0.1846 - val_mae: 0.3689\n", + "Epoch 261/500\n", + "10/10 [==============================] - 0s 6ms/step - loss: 0.1684 - mae: 0.3506 - val_loss: 0.1846 - val_mae: 0.3689\n", + "Epoch 262/500\n", + "10/10 [==============================] - 0s 6ms/step - loss: 0.1710 - mae: 0.3512 - val_loss: 0.1848 - val_mae: 0.3703\n", + "Epoch 263/500\n", + "10/10 [==============================] - 0s 6ms/step - loss: 0.1657 - mae: 0.3471 - val_loss: 0.1850 - val_mae: 0.3709\n", + "Epoch 264/500\n", + "10/10 [==============================] - 0s 6ms/step - loss: 0.1764 - mae: 0.3611 - val_loss: 0.1849 - val_mae: 0.3704\n", + "Epoch 265/500\n", + "10/10 [==============================] - 0s 6ms/step - loss: 0.1710 - mae: 0.3487 - val_loss: 0.1846 - val_mae: 0.3691\n", + "Epoch 266/500\n", + "10/10 [==============================] - 0s 6ms/step - loss: 0.1759 - mae: 0.3565 - val_loss: 0.1845 - val_mae: 0.3685\n", + "Epoch 267/500\n", + "10/10 [==============================] - 0s 6ms/step - loss: 0.1680 - mae: 0.3505 - val_loss: 0.1844 - val_mae: 0.3669\n", + "Epoch 268/500\n", + "10/10 [==============================] - 0s 7ms/step - loss: 0.1764 - mae: 0.3597 - val_loss: 0.1844 - val_mae: 0.3671\n", + "Epoch 269/500\n", + "10/10 [==============================] - 0s 6ms/step - loss: 0.1676 - mae: 0.3494 - val_loss: 0.1847 - val_mae: 0.3693\n", + "Epoch 270/500\n", + "10/10 [==============================] - 0s 6ms/step - loss: 0.1641 - mae: 0.3478 - val_loss: 0.1846 - val_mae: 0.3687\n", + "Epoch 271/500\n", + "10/10 [==============================] - 0s 5ms/step - loss: 0.1784 - mae: 0.3615 - val_loss: 0.1846 - val_mae: 0.3689\n", + "Epoch 272/500\n", + "10/10 [==============================] - 0s 7ms/step - loss: 0.1767 - mae: 0.3571 - val_loss: 0.1846 - val_mae: 0.3687\n", + "Epoch 273/500\n", + "10/10 [==============================] - 0s 6ms/step - loss: 0.1714 - mae: 0.3521 - val_loss: 0.1845 - val_mae: 0.3676\n", + "Epoch 274/500\n", + "10/10 [==============================] - 0s 6ms/step - loss: 0.1710 - mae: 0.3503 - val_loss: 0.1845 - val_mae: 0.3678\n", + "Epoch 275/500\n", + "10/10 [==============================] - 0s 5ms/step - loss: 0.1729 - mae: 0.3507 - val_loss: 0.1845 - val_mae: 0.3683\n", + "Epoch 276/500\n", + "10/10 [==============================] - 0s 6ms/step - loss: 0.1754 - mae: 0.3579 - val_loss: 0.1845 - val_mae: 0.3677\n", + "Epoch 277/500\n", + "10/10 [==============================] - 0s 7ms/step - loss: 0.1705 - mae: 0.3504 - val_loss: 0.1845 - val_mae: 0.3672\n", + "Epoch 278/500\n", + "10/10 [==============================] - 0s 6ms/step - loss: 0.1721 - mae: 0.3553 - val_loss: 0.1846 - val_mae: 0.3686\n", + "Epoch 279/500\n", + "10/10 [==============================] - 0s 6ms/step - loss: 0.1664 - mae: 0.3476 - val_loss: 0.1847 - val_mae: 0.3692\n", + "Epoch 280/500\n", + "10/10 [==============================] - 0s 6ms/step - loss: 0.1655 - mae: 0.3467 - val_loss: 0.1847 - val_mae: 0.3692\n", + "Epoch 281/500\n", + "10/10 [==============================] - 0s 5ms/step - loss: 0.1693 - mae: 0.3534 - val_loss: 0.1846 - val_mae: 0.3687\n", + "Epoch 282/500\n", + "10/10 [==============================] - 0s 7ms/step - loss: 0.1732 - mae: 0.3580 - val_loss: 0.1847 - val_mae: 0.3692\n", + "Epoch 283/500\n", + "10/10 [==============================] - 0s 6ms/step - loss: 0.1779 - mae: 0.3598 - val_loss: 0.1847 - val_mae: 0.3694\n", + "Epoch 284/500\n", + "10/10 [==============================] - 0s 7ms/step - loss: 0.1763 - mae: 0.3570 - val_loss: 0.1849 - val_mae: 0.3705\n", + "Epoch 285/500\n", + "10/10 [==============================] - 0s 6ms/step - loss: 0.1636 - mae: 0.3474 - val_loss: 0.1845 - val_mae: 0.3674\n", + "Epoch 286/500\n", + "10/10 [==============================] - 0s 7ms/step - loss: 0.1716 - mae: 0.3496 - val_loss: 0.1845 - val_mae: 0.3680\n", + "Epoch 287/500\n", + "10/10 [==============================] - 0s 6ms/step - loss: 0.1769 - mae: 0.3579 - val_loss: 0.1846 - val_mae: 0.3691\n", + "Epoch 288/500\n", + "10/10 [==============================] - 0s 6ms/step - loss: 0.1771 - mae: 0.3565 - val_loss: 0.1856 - val_mae: 0.3726\n", + "Epoch 289/500\n", + "10/10 [==============================] - 0s 6ms/step - loss: 0.1709 - mae: 0.3516 - val_loss: 0.1848 - val_mae: 0.3703\n", + "Epoch 290/500\n", + "10/10 [==============================] - 0s 6ms/step - loss: 0.1719 - mae: 0.3584 - val_loss: 0.1844 - val_mae: 0.3675\n", + "Epoch 291/500\n", + "10/10 [==============================] - 0s 8ms/step - loss: 0.1730 - mae: 0.3544 - val_loss: 0.1846 - val_mae: 0.3693\n", + "Epoch 292/500\n", + "10/10 [==============================] - 0s 8ms/step - loss: 0.1751 - mae: 0.3558 - val_loss: 0.1846 - val_mae: 0.3694\n", + "Epoch 293/500\n", + "10/10 [==============================] - 0s 7ms/step - loss: 0.1658 - mae: 0.3511 - val_loss: 0.1846 - val_mae: 0.3693\n", + "Epoch 294/500\n", + "10/10 [==============================] - 0s 7ms/step - loss: 0.1713 - mae: 0.3536 - val_loss: 0.1846 - val_mae: 0.3693\n", + "Epoch 295/500\n", + "10/10 [==============================] - 0s 7ms/step - loss: 0.1725 - mae: 0.3565 - val_loss: 0.1844 - val_mae: 0.3678\n", + "Epoch 296/500\n", + "10/10 [==============================] - 0s 6ms/step - loss: 0.1724 - mae: 0.3513 - val_loss: 0.1846 - val_mae: 0.3691\n", + "Epoch 297/500\n", + "10/10 [==============================] - 0s 6ms/step - loss: 0.1680 - mae: 0.3520 - val_loss: 0.1845 - val_mae: 0.3683\n", + "Epoch 298/500\n", + "10/10 [==============================] - 0s 6ms/step - loss: 0.1734 - mae: 0.3523 - val_loss: 0.1848 - val_mae: 0.3704\n", + "Epoch 299/500\n", + "10/10 [==============================] - 0s 7ms/step - loss: 0.1756 - mae: 0.3561 - val_loss: 0.1846 - val_mae: 0.3695\n", + "Epoch 300/500\n", + "10/10 [==============================] - 0s 7ms/step - loss: 0.1650 - mae: 0.3467 - val_loss: 0.1844 - val_mae: 0.3675\n", + "Epoch 301/500\n", + "10/10 [==============================] - 0s 6ms/step - loss: 0.1690 - mae: 0.3495 - val_loss: 0.1844 - val_mae: 0.3669\n", + "Epoch 302/500\n", + "10/10 [==============================] - 0s 6ms/step - loss: 0.1642 - mae: 0.3458 - val_loss: 0.1846 - val_mae: 0.3655\n", + "Epoch 303/500\n", + "10/10 [==============================] - 0s 19ms/step - loss: 0.1732 - mae: 0.3490 - val_loss: 0.1846 - val_mae: 0.3690\n", + "Epoch 304/500\n", + "10/10 [==============================] - 0s 6ms/step - loss: 0.1686 - mae: 0.3514 - val_loss: 0.1847 - val_mae: 0.3698\n", + "Epoch 305/500\n", + "10/10 [==============================] - 0s 7ms/step - loss: 0.1757 - mae: 0.3568 - val_loss: 0.1847 - val_mae: 0.3696\n", + "Epoch 306/500\n", + "10/10 [==============================] - 0s 7ms/step - loss: 0.1650 - mae: 0.3475 - val_loss: 0.1846 - val_mae: 0.3689\n", + "Epoch 307/500\n", + "10/10 [==============================] - 0s 6ms/step - loss: 0.1544 - mae: 0.3364 - val_loss: 0.1845 - val_mae: 0.3673\n", + "Epoch 308/500\n", + "10/10 [==============================] - 0s 7ms/step - loss: 0.1714 - mae: 0.3512 - val_loss: 0.1849 - val_mae: 0.3703\n", + "Epoch 309/500\n", + "10/10 [==============================] - 0s 6ms/step - loss: 0.1729 - mae: 0.3549 - val_loss: 0.1853 - val_mae: 0.3718\n", + "Epoch 310/500\n", + "10/10 [==============================] - 0s 6ms/step - loss: 0.1677 - mae: 0.3540 - val_loss: 0.1845 - val_mae: 0.3679\n", + "Epoch 311/500\n", + "10/10 [==============================] - 0s 7ms/step - loss: 0.1731 - mae: 0.3513 - val_loss: 0.1845 - val_mae: 0.3678\n", + "Epoch 312/500\n", + "10/10 [==============================] - 0s 8ms/step - loss: 0.1717 - mae: 0.3521 - val_loss: 0.1845 - val_mae: 0.3687\n", + "Epoch 313/500\n", + "10/10 [==============================] - 0s 6ms/step - loss: 0.1656 - mae: 0.3425 - val_loss: 0.1846 - val_mae: 0.3689\n", + "Epoch 314/500\n", + "10/10 [==============================] - 0s 6ms/step - loss: 0.1632 - mae: 0.3439 - val_loss: 0.1847 - val_mae: 0.3694\n", + "Epoch 315/500\n", + "10/10 [==============================] - 0s 7ms/step - loss: 0.1694 - mae: 0.3512 - val_loss: 0.1846 - val_mae: 0.3690\n", + "Epoch 316/500\n", + "10/10 [==============================] - 0s 6ms/step - loss: 0.1679 - mae: 0.3496 - val_loss: 0.1851 - val_mae: 0.3712\n", + "Epoch 317/500\n", + "10/10 [==============================] - 0s 6ms/step - loss: 0.1754 - mae: 0.3533 - val_loss: 0.1851 - val_mae: 0.3712\n", + "Epoch 318/500\n", + "10/10 [==============================] - 0s 6ms/step - loss: 0.1757 - mae: 0.3582 - val_loss: 0.1847 - val_mae: 0.3694\n", + "Epoch 319/500\n", + "10/10 [==============================] - 0s 7ms/step - loss: 0.1705 - mae: 0.3522 - val_loss: 0.1845 - val_mae: 0.3679\n", + "Epoch 320/500\n", + "10/10 [==============================] - 0s 6ms/step - loss: 0.1539 - mae: 0.3368 - val_loss: 0.1845 - val_mae: 0.3679\n", + "Epoch 321/500\n", + "10/10 [==============================] - 0s 6ms/step - loss: 0.1691 - mae: 0.3523 - val_loss: 0.1849 - val_mae: 0.3704\n", + "Epoch 322/500\n", + "10/10 [==============================] - 0s 7ms/step - loss: 0.1695 - mae: 0.3494 - val_loss: 0.1854 - val_mae: 0.3720\n", + "Epoch 323/500\n", + "10/10 [==============================] - 0s 6ms/step - loss: 0.1648 - mae: 0.3469 - val_loss: 0.1845 - val_mae: 0.3680\n", + "Epoch 324/500\n", + "10/10 [==============================] - 0s 6ms/step - loss: 0.1781 - mae: 0.3610 - val_loss: 0.1845 - val_mae: 0.3684\n", + "Epoch 325/500\n", + "10/10 [==============================] - 0s 7ms/step - loss: 0.1565 - mae: 0.3364 - val_loss: 0.1850 - val_mae: 0.3707\n", + "Epoch 326/500\n", + "10/10 [==============================] - 0s 6ms/step - loss: 0.1680 - mae: 0.3496 - val_loss: 0.1849 - val_mae: 0.3706\n", + "Epoch 327/500\n", + "10/10 [==============================] - 0s 7ms/step - loss: 0.1680 - mae: 0.3463 - val_loss: 0.1849 - val_mae: 0.3704\n", + "Epoch 328/500\n", + "10/10 [==============================] - 0s 6ms/step - loss: 0.1699 - mae: 0.3538 - val_loss: 0.1846 - val_mae: 0.3693\n", + "Epoch 329/500\n", + "10/10 [==============================] - 0s 6ms/step - loss: 0.1782 - mae: 0.3604 - val_loss: 0.1848 - val_mae: 0.3704\n", + "Epoch 330/500\n", + "10/10 [==============================] - 0s 7ms/step - loss: 0.1746 - mae: 0.3527 - val_loss: 0.1848 - val_mae: 0.3704\n", + "Epoch 331/500\n", + "10/10 [==============================] - 0s 6ms/step - loss: 0.1695 - mae: 0.3496 - val_loss: 0.1846 - val_mae: 0.3695\n", + "Epoch 332/500\n", + "10/10 [==============================] - 0s 6ms/step - loss: 0.1635 - mae: 0.3445 - val_loss: 0.1846 - val_mae: 0.3689\n", + "Epoch 333/500\n", + "10/10 [==============================] - 0s 6ms/step - loss: 0.1611 - mae: 0.3453 - val_loss: 0.1845 - val_mae: 0.3678\n", + "Epoch 334/500\n", + "10/10 [==============================] - 0s 5ms/step - loss: 0.1595 - mae: 0.3416 - val_loss: 0.1846 - val_mae: 0.3692\n", + "Epoch 335/500\n", + "10/10 [==============================] - 0s 7ms/step - loss: 0.1733 - mae: 0.3562 - val_loss: 0.1846 - val_mae: 0.3690\n", + "Epoch 336/500\n", + "10/10 [==============================] - 0s 7ms/step - loss: 0.1668 - mae: 0.3458 - val_loss: 0.1845 - val_mae: 0.3676\n", + "Epoch 337/500\n", + "10/10 [==============================] - 0s 6ms/step - loss: 0.1678 - mae: 0.3455 - val_loss: 0.1846 - val_mae: 0.3685\n", + "Epoch 338/500\n", + "10/10 [==============================] - 0s 6ms/step - loss: 0.1768 - mae: 0.3578 - val_loss: 0.1846 - val_mae: 0.3692\n", + "Epoch 339/500\n", + "10/10 [==============================] - 0s 7ms/step - loss: 0.1674 - mae: 0.3485 - val_loss: 0.1846 - val_mae: 0.3690\n", + "Epoch 340/500\n", + "10/10 [==============================] - 0s 6ms/step - loss: 0.1736 - mae: 0.3536 - val_loss: 0.1848 - val_mae: 0.3700\n", + "Epoch 341/500\n", + "10/10 [==============================] - 0s 6ms/step - loss: 0.1655 - mae: 0.3474 - val_loss: 0.1846 - val_mae: 0.3686\n", + "Epoch 342/500\n", + "10/10 [==============================] - 0s 7ms/step - loss: 0.1727 - mae: 0.3539 - val_loss: 0.1846 - val_mae: 0.3686\n", + "Epoch 343/500\n", + "10/10 [==============================] - 0s 19ms/step - loss: 0.1721 - mae: 0.3489 - val_loss: 0.1846 - val_mae: 0.3690\n", + "Epoch 344/500\n", + "10/10 [==============================] - 0s 6ms/step - loss: 0.1620 - mae: 0.3464 - val_loss: 0.1845 - val_mae: 0.3675\n", + "Epoch 345/500\n", + "10/10 [==============================] - 0s 7ms/step - loss: 0.1757 - mae: 0.3548 - val_loss: 0.1845 - val_mae: 0.3681\n", + "Epoch 346/500\n", + "10/10 [==============================] - 0s 7ms/step - loss: 0.1753 - mae: 0.3576 - val_loss: 0.1846 - val_mae: 0.3685\n", + "Epoch 347/500\n", + "10/10 [==============================] - 0s 6ms/step - loss: 0.1698 - mae: 0.3471 - val_loss: 0.1845 - val_mae: 0.3678\n", + "Epoch 348/500\n", + "10/10 [==============================] - 0s 7ms/step - loss: 0.1793 - mae: 0.3578 - val_loss: 0.1845 - val_mae: 0.3676\n", + "Epoch 349/500\n", + "10/10 [==============================] - 0s 7ms/step - loss: 0.1677 - mae: 0.3506 - val_loss: 0.1846 - val_mae: 0.3683\n", + "Epoch 350/500\n", + "10/10 [==============================] - 0s 6ms/step - loss: 0.1683 - mae: 0.3502 - val_loss: 0.1847 - val_mae: 0.3686\n", + "Epoch 351/500\n", + "10/10 [==============================] - 0s 7ms/step - loss: 0.1813 - mae: 0.3624 - val_loss: 0.1846 - val_mae: 0.3678\n", + "Epoch 352/500\n", + "10/10 [==============================] - 0s 6ms/step - loss: 0.1656 - mae: 0.3440 - val_loss: 0.1846 - val_mae: 0.3674\n", + "Epoch 353/500\n", + "10/10 [==============================] - 0s 7ms/step - loss: 0.1705 - mae: 0.3515 - val_loss: 0.1848 - val_mae: 0.3692\n", + "Epoch 354/500\n", + "10/10 [==============================] - 0s 6ms/step - loss: 0.1786 - mae: 0.3562 - val_loss: 0.1850 - val_mae: 0.3703\n", + "Epoch 355/500\n", + "10/10 [==============================] - 0s 7ms/step - loss: 0.1719 - mae: 0.3518 - val_loss: 0.1847 - val_mae: 0.3683\n", + "Epoch 356/500\n", + "10/10 [==============================] - 0s 7ms/step - loss: 0.1698 - mae: 0.3528 - val_loss: 0.1846 - val_mae: 0.3679\n", + "Epoch 357/500\n", + "10/10 [==============================] - 0s 7ms/step - loss: 0.1682 - mae: 0.3499 - val_loss: 0.1846 - val_mae: 0.3678\n", + "Epoch 358/500\n", + "10/10 [==============================] - 0s 7ms/step - loss: 0.1627 - mae: 0.3442 - val_loss: 0.1848 - val_mae: 0.3694\n", + "Epoch 359/500\n", + "10/10 [==============================] - 0s 7ms/step - loss: 0.1634 - mae: 0.3428 - val_loss: 0.1855 - val_mae: 0.3718\n", + "Epoch 360/500\n", + "10/10 [==============================] - 0s 6ms/step - loss: 0.1671 - mae: 0.3486 - val_loss: 0.1848 - val_mae: 0.3694\n", + "Epoch 361/500\n", + "10/10 [==============================] - 0s 7ms/step - loss: 0.1748 - mae: 0.3609 - val_loss: 0.1846 - val_mae: 0.3681\n", + "Epoch 362/500\n", + "10/10 [==============================] - 0s 7ms/step - loss: 0.1655 - mae: 0.3470 - val_loss: 0.1846 - val_mae: 0.3673\n", + "Epoch 363/500\n", + "10/10 [==============================] - 0s 7ms/step - loss: 0.1610 - mae: 0.3395 - val_loss: 0.1848 - val_mae: 0.3693\n", + "Epoch 364/500\n", + "10/10 [==============================] - 0s 7ms/step - loss: 0.1713 - mae: 0.3539 - val_loss: 0.1847 - val_mae: 0.3688\n", + "Epoch 365/500\n", + "10/10 [==============================] - 0s 7ms/step - loss: 0.1664 - mae: 0.3484 - val_loss: 0.1847 - val_mae: 0.3691\n", + "Epoch 366/500\n", + "10/10 [==============================] - 0s 7ms/step - loss: 0.1554 - mae: 0.3350 - val_loss: 0.1847 - val_mae: 0.3691\n", + "Epoch 367/500\n", + "10/10 [==============================] - 0s 6ms/step - loss: 0.1701 - mae: 0.3511 - val_loss: 0.1845 - val_mae: 0.3679\n", + "Epoch 368/500\n", + "10/10 [==============================] - 0s 6ms/step - loss: 0.1720 - mae: 0.3546 - val_loss: 0.1847 - val_mae: 0.3691\n", + "Epoch 369/500\n", + "10/10 [==============================] - 0s 7ms/step - loss: 0.1675 - mae: 0.3495 - val_loss: 0.1847 - val_mae: 0.3695\n", + "Epoch 370/500\n", + "10/10 [==============================] - 0s 6ms/step - loss: 0.1645 - mae: 0.3449 - val_loss: 0.1846 - val_mae: 0.3684\n", + "Epoch 371/500\n", + "10/10 [==============================] - 0s 8ms/step - loss: 0.1790 - mae: 0.3588 - val_loss: 0.1846 - val_mae: 0.3687\n", + "Epoch 372/500\n", + "10/10 [==============================] - 0s 8ms/step - loss: 0.1662 - mae: 0.3466 - val_loss: 0.1847 - val_mae: 0.3689\n", + "Epoch 373/500\n", + "10/10 [==============================] - 0s 7ms/step - loss: 0.1693 - mae: 0.3557 - val_loss: 0.1850 - val_mae: 0.3707\n", + "Epoch 374/500\n", + "10/10 [==============================] - 0s 7ms/step - loss: 0.1682 - mae: 0.3493 - val_loss: 0.1851 - val_mae: 0.3711\n", + "Epoch 375/500\n", + "10/10 [==============================] - 0s 7ms/step - loss: 0.1777 - mae: 0.3612 - val_loss: 0.1846 - val_mae: 0.3690\n", + "Epoch 376/500\n", + "10/10 [==============================] - 0s 7ms/step - loss: 0.1682 - mae: 0.3517 - val_loss: 0.1846 - val_mae: 0.3687\n", + "Epoch 377/500\n", + "10/10 [==============================] - 0s 7ms/step - loss: 0.1623 - mae: 0.3432 - val_loss: 0.1847 - val_mae: 0.3696\n", + "Epoch 378/500\n", + "10/10 [==============================] - 0s 7ms/step - loss: 0.1790 - mae: 0.3576 - val_loss: 0.1850 - val_mae: 0.3709\n", + "Epoch 379/500\n", + "10/10 [==============================] - 0s 6ms/step - loss: 0.1795 - mae: 0.3594 - val_loss: 0.1846 - val_mae: 0.3685\n", + "Epoch 380/500\n", + "10/10 [==============================] - 0s 6ms/step - loss: 0.1635 - mae: 0.3440 - val_loss: 0.1846 - val_mae: 0.3691\n", + "Epoch 381/500\n", + "10/10 [==============================] - 0s 7ms/step - loss: 0.1727 - mae: 0.3509 - val_loss: 0.1847 - val_mae: 0.3697\n", + "Epoch 382/500\n", + "10/10 [==============================] - 0s 6ms/step - loss: 0.1671 - mae: 0.3511 - val_loss: 0.1848 - val_mae: 0.3703\n", + "Epoch 383/500\n", + "10/10 [==============================] - 0s 6ms/step - loss: 0.1748 - mae: 0.3557 - val_loss: 0.1848 - val_mae: 0.3701\n", + "Epoch 384/500\n", + "10/10 [==============================] - 0s 6ms/step - loss: 0.1745 - mae: 0.3581 - val_loss: 0.1848 - val_mae: 0.3703\n", + "Epoch 385/500\n", + "10/10 [==============================] - 0s 7ms/step - loss: 0.1728 - mae: 0.3566 - val_loss: 0.1846 - val_mae: 0.3693\n", + "Epoch 386/500\n", + "10/10 [==============================] - 0s 7ms/step - loss: 0.1679 - mae: 0.3499 - val_loss: 0.1847 - val_mae: 0.3696\n", + "Epoch 387/500\n", + "10/10 [==============================] - 0s 6ms/step - loss: 0.1647 - mae: 0.3420 - val_loss: 0.1849 - val_mae: 0.3704\n", + "Epoch 388/500\n", + "10/10 [==============================] - 0s 6ms/step - loss: 0.1685 - mae: 0.3485 - val_loss: 0.1846 - val_mae: 0.3684\n", + "Epoch 389/500\n", + "10/10 [==============================] - 0s 7ms/step - loss: 0.1622 - mae: 0.3443 - val_loss: 0.1847 - val_mae: 0.3692\n", + "Epoch 390/500\n", + "10/10 [==============================] - 0s 6ms/step - loss: 0.1656 - mae: 0.3495 - val_loss: 0.1847 - val_mae: 0.3692\n", + "Epoch 391/500\n", + "10/10 [==============================] - 0s 6ms/step - loss: 0.1680 - mae: 0.3484 - val_loss: 0.1848 - val_mae: 0.3700\n", + "Epoch 392/500\n", + "10/10 [==============================] - 0s 8ms/step - loss: 0.1779 - mae: 0.3601 - val_loss: 0.1846 - val_mae: 0.3688\n", + "Epoch 393/500\n", + "10/10 [==============================] - 0s 19ms/step - loss: 0.1667 - mae: 0.3450 - val_loss: 0.1847 - val_mae: 0.3695\n", + "Epoch 394/500\n", + "10/10 [==============================] - 0s 6ms/step - loss: 0.1668 - mae: 0.3466 - val_loss: 0.1846 - val_mae: 0.3689\n", + "Epoch 395/500\n", + "10/10 [==============================] - 0s 6ms/step - loss: 0.1751 - mae: 0.3564 - val_loss: 0.1845 - val_mae: 0.3683\n", + "Epoch 396/500\n", + "10/10 [==============================] - 0s 6ms/step - loss: 0.1742 - mae: 0.3558 - val_loss: 0.1845 - val_mae: 0.3686\n", + "Epoch 397/500\n", + "10/10 [==============================] - 0s 7ms/step - loss: 0.1800 - mae: 0.3653 - val_loss: 0.1845 - val_mae: 0.3676\n", + "Epoch 398/500\n", + "10/10 [==============================] - 0s 6ms/step - loss: 0.1663 - mae: 0.3425 - val_loss: 0.1845 - val_mae: 0.3678\n", + "Epoch 399/500\n", + "10/10 [==============================] - 0s 7ms/step - loss: 0.1731 - mae: 0.3566 - val_loss: 0.1845 - val_mae: 0.3683\n", + "Epoch 400/500\n", + "10/10 [==============================] - 0s 5ms/step - loss: 0.1656 - mae: 0.3431 - val_loss: 0.1846 - val_mae: 0.3691\n", + "Epoch 401/500\n", + "10/10 [==============================] - 0s 7ms/step - loss: 0.1603 - mae: 0.3438 - val_loss: 0.1846 - val_mae: 0.3688\n", + "Epoch 402/500\n", + "10/10 [==============================] - 0s 6ms/step - loss: 0.1670 - mae: 0.3487 - val_loss: 0.1848 - val_mae: 0.3701\n", + "Epoch 403/500\n", + "10/10 [==============================] - 0s 6ms/step - loss: 0.1762 - mae: 0.3544 - val_loss: 0.1846 - val_mae: 0.3692\n", + "Epoch 404/500\n", + "10/10 [==============================] - 0s 5ms/step - loss: 0.1714 - mae: 0.3497 - val_loss: 0.1845 - val_mae: 0.3685\n", + "Epoch 405/500\n", + "10/10 [==============================] - 0s 5ms/step - loss: 0.1652 - mae: 0.3454 - val_loss: 0.1846 - val_mae: 0.3689\n", + "Epoch 406/500\n", + "10/10 [==============================] - 0s 7ms/step - loss: 0.1663 - mae: 0.3471 - val_loss: 0.1851 - val_mae: 0.3710\n", + "Epoch 407/500\n", + "10/10 [==============================] - 0s 7ms/step - loss: 0.1604 - mae: 0.3435 - val_loss: 0.1845 - val_mae: 0.3679\n", + "Epoch 408/500\n", + "10/10 [==============================] - 0s 6ms/step - loss: 0.1710 - mae: 0.3495 - val_loss: 0.1845 - val_mae: 0.3671\n", + "Epoch 409/500\n", + "10/10 [==============================] - 0s 6ms/step - loss: 0.1716 - mae: 0.3498 - val_loss: 0.1846 - val_mae: 0.3689\n", + "Epoch 410/500\n", + "10/10 [==============================] - 0s 6ms/step - loss: 0.1906 - mae: 0.3736 - val_loss: 0.1848 - val_mae: 0.3700\n", + "Epoch 411/500\n", + "10/10 [==============================] - 0s 6ms/step - loss: 0.1804 - mae: 0.3610 - val_loss: 0.1848 - val_mae: 0.3703\n", + "Epoch 412/500\n", + "10/10 [==============================] - 0s 6ms/step - loss: 0.1685 - mae: 0.3505 - val_loss: 0.1848 - val_mae: 0.3700\n", + "Epoch 413/500\n", + "10/10 [==============================] - 0s 6ms/step - loss: 0.1598 - mae: 0.3406 - val_loss: 0.1846 - val_mae: 0.3686\n", + "Epoch 414/500\n", + "10/10 [==============================] - 0s 6ms/step - loss: 0.1619 - mae: 0.3453 - val_loss: 0.1846 - val_mae: 0.3686\n", + "Epoch 415/500\n", + "10/10 [==============================] - 0s 7ms/step - loss: 0.1786 - mae: 0.3603 - val_loss: 0.1849 - val_mae: 0.3704\n", + "Epoch 416/500\n", + "10/10 [==============================] - 0s 7ms/step - loss: 0.1803 - mae: 0.3594 - val_loss: 0.1847 - val_mae: 0.3698\n", + "Epoch 417/500\n", + "10/10 [==============================] - 0s 8ms/step - loss: 0.1714 - mae: 0.3564 - val_loss: 0.1845 - val_mae: 0.3681\n", + "Epoch 418/500\n", + "10/10 [==============================] - 0s 6ms/step - loss: 0.1673 - mae: 0.3479 - val_loss: 0.1845 - val_mae: 0.3674\n", + "Epoch 419/500\n", + "10/10 [==============================] - 0s 6ms/step - loss: 0.1648 - mae: 0.3469 - val_loss: 0.1847 - val_mae: 0.3695\n", + "Epoch 420/500\n", + "10/10 [==============================] - 0s 5ms/step - loss: 0.1642 - mae: 0.3439 - val_loss: 0.1847 - val_mae: 0.3698\n", + "Epoch 421/500\n", + "10/10 [==============================] - 0s 7ms/step - loss: 0.1738 - mae: 0.3554 - val_loss: 0.1848 - val_mae: 0.3700\n", + "Epoch 422/500\n", + "10/10 [==============================] - 0s 6ms/step - loss: 0.1662 - mae: 0.3466 - val_loss: 0.1845 - val_mae: 0.3681\n", + "Epoch 423/500\n", + "10/10 [==============================] - 0s 7ms/step - loss: 0.1678 - mae: 0.3476 - val_loss: 0.1845 - val_mae: 0.3686\n", + "Epoch 424/500\n", + "10/10 [==============================] - 0s 5ms/step - loss: 0.1762 - mae: 0.3599 - val_loss: 0.1845 - val_mae: 0.3684\n", + "Epoch 425/500\n", + "10/10 [==============================] - 0s 6ms/step - loss: 0.1688 - mae: 0.3467 - val_loss: 0.1846 - val_mae: 0.3693\n", + "Epoch 426/500\n", + "10/10 [==============================] - 0s 6ms/step - loss: 0.1708 - mae: 0.3483 - val_loss: 0.1846 - val_mae: 0.3687\n", + "Epoch 427/500\n", + "10/10 [==============================] - 0s 6ms/step - loss: 0.1641 - mae: 0.3435 - val_loss: 0.1845 - val_mae: 0.3680\n", + "Epoch 428/500\n", + "10/10 [==============================] - 0s 7ms/step - loss: 0.1683 - mae: 0.3438 - val_loss: 0.1845 - val_mae: 0.3683\n", + "Epoch 429/500\n", + "10/10 [==============================] - 0s 6ms/step - loss: 0.1659 - mae: 0.3468 - val_loss: 0.1845 - val_mae: 0.3667\n", + "Epoch 430/500\n", + "10/10 [==============================] - 0s 7ms/step - loss: 0.1630 - mae: 0.3462 - val_loss: 0.1845 - val_mae: 0.3670\n", + "Epoch 431/500\n", + "10/10 [==============================] - 0s 7ms/step - loss: 0.1713 - mae: 0.3480 - val_loss: 0.1849 - val_mae: 0.3703\n", + "Epoch 432/500\n", + "10/10 [==============================] - 0s 6ms/step - loss: 0.1818 - mae: 0.3676 - val_loss: 0.1851 - val_mae: 0.3712\n", + "Epoch 433/500\n", + "10/10 [==============================] - 0s 19ms/step - loss: 0.1833 - mae: 0.3606 - val_loss: 0.1847 - val_mae: 0.3697\n", + "Epoch 434/500\n", + "10/10 [==============================] - 0s 6ms/step - loss: 0.1676 - mae: 0.3489 - val_loss: 0.1845 - val_mae: 0.3669\n", + "Epoch 435/500\n", + "10/10 [==============================] - 0s 7ms/step - loss: 0.1651 - mae: 0.3451 - val_loss: 0.1845 - val_mae: 0.3679\n", + "Epoch 436/500\n", + "10/10 [==============================] - 0s 7ms/step - loss: 0.1736 - mae: 0.3534 - val_loss: 0.1845 - val_mae: 0.3685\n", + "Epoch 437/500\n", + "10/10 [==============================] - 0s 6ms/step - loss: 0.1700 - mae: 0.3531 - val_loss: 0.1847 - val_mae: 0.3697\n", + "Epoch 438/500\n", + "10/10 [==============================] - 0s 7ms/step - loss: 0.1799 - mae: 0.3615 - val_loss: 0.1845 - val_mae: 0.3685\n", + "Epoch 439/500\n", + "10/10 [==============================] - 0s 6ms/step - loss: 0.1684 - mae: 0.3535 - val_loss: 0.1846 - val_mae: 0.3686\n", + "Epoch 440/500\n", + "10/10 [==============================] - 0s 6ms/step - loss: 0.1644 - mae: 0.3445 - val_loss: 0.1848 - val_mae: 0.3699\n", + "Epoch 441/500\n", + "10/10 [==============================] - 0s 6ms/step - loss: 0.1702 - mae: 0.3541 - val_loss: 0.1845 - val_mae: 0.3682\n", + "Epoch 442/500\n", + "10/10 [==============================] - 0s 7ms/step - loss: 0.1621 - mae: 0.3424 - val_loss: 0.1845 - val_mae: 0.3666\n", + "Epoch 443/500\n", + "10/10 [==============================] - 0s 7ms/step - loss: 0.1757 - mae: 0.3551 - val_loss: 0.1845 - val_mae: 0.3670\n", + "Epoch 444/500\n", + "10/10 [==============================] - 0s 7ms/step - loss: 0.1639 - mae: 0.3403 - val_loss: 0.1845 - val_mae: 0.3682\n", + "Epoch 445/500\n", + "10/10 [==============================] - 0s 6ms/step - loss: 0.1739 - mae: 0.3512 - val_loss: 0.1848 - val_mae: 0.3695\n", + "Epoch 446/500\n", + "10/10 [==============================] - 0s 6ms/step - loss: 0.1712 - mae: 0.3530 - val_loss: 0.1848 - val_mae: 0.3700\n", + "Epoch 447/500\n", + "10/10 [==============================] - 0s 6ms/step - loss: 0.1630 - mae: 0.3460 - val_loss: 0.1848 - val_mae: 0.3698\n", + "Epoch 448/500\n", + "10/10 [==============================] - 0s 7ms/step - loss: 0.1670 - mae: 0.3458 - val_loss: 0.1846 - val_mae: 0.3687\n", + "Epoch 449/500\n", + "10/10 [==============================] - 0s 6ms/step - loss: 0.1576 - mae: 0.3345 - val_loss: 0.1846 - val_mae: 0.3685\n", + "Epoch 450/500\n", + "10/10 [==============================] - 0s 7ms/step - loss: 0.1603 - mae: 0.3429 - val_loss: 0.1847 - val_mae: 0.3694\n", + "Epoch 451/500\n", + "10/10 [==============================] - 0s 7ms/step - loss: 0.1689 - mae: 0.3507 - val_loss: 0.1848 - val_mae: 0.3697\n", + "Epoch 452/500\n", + "10/10 [==============================] - 0s 6ms/step - loss: 0.1692 - mae: 0.3490 - val_loss: 0.1848 - val_mae: 0.3699\n", + "Epoch 453/500\n", + "10/10 [==============================] - 0s 6ms/step - loss: 0.1685 - mae: 0.3514 - val_loss: 0.1845 - val_mae: 0.3679\n", + "Epoch 454/500\n", + "10/10 [==============================] - 0s 7ms/step - loss: 0.1774 - mae: 0.3588 - val_loss: 0.1846 - val_mae: 0.3692\n", + "Epoch 455/500\n", + "10/10 [==============================] - 0s 6ms/step - loss: 0.1672 - mae: 0.3472 - val_loss: 0.1846 - val_mae: 0.3690\n", + "Epoch 456/500\n", + "10/10 [==============================] - 0s 8ms/step - loss: 0.1731 - mae: 0.3566 - val_loss: 0.1846 - val_mae: 0.3688\n", + "Epoch 457/500\n", + "10/10 [==============================] - 0s 7ms/step - loss: 0.1658 - mae: 0.3454 - val_loss: 0.1847 - val_mae: 0.3693\n", + "Epoch 458/500\n", + "10/10 [==============================] - 0s 6ms/step - loss: 0.1702 - mae: 0.3520 - val_loss: 0.1845 - val_mae: 0.3683\n", + "Epoch 459/500\n", + "10/10 [==============================] - 0s 7ms/step - loss: 0.1739 - mae: 0.3532 - val_loss: 0.1846 - val_mae: 0.3684\n", + "Epoch 460/500\n", + "10/10 [==============================] - 0s 6ms/step - loss: 0.1699 - mae: 0.3490 - val_loss: 0.1846 - val_mae: 0.3688\n", + "Epoch 461/500\n", + "10/10 [==============================] - 0s 6ms/step - loss: 0.1703 - mae: 0.3547 - val_loss: 0.1845 - val_mae: 0.3671\n", + "Epoch 462/500\n", + "10/10 [==============================] - 0s 6ms/step - loss: 0.1694 - mae: 0.3505 - val_loss: 0.1846 - val_mae: 0.3682\n", + "Epoch 463/500\n", + "10/10 [==============================] - 0s 6ms/step - loss: 0.1728 - mae: 0.3542 - val_loss: 0.1848 - val_mae: 0.3698\n", + "Epoch 464/500\n", + "10/10 [==============================] - 0s 6ms/step - loss: 0.1638 - mae: 0.3433 - val_loss: 0.1847 - val_mae: 0.3691\n", + "Epoch 465/500\n", + "10/10 [==============================] - 0s 6ms/step - loss: 0.1648 - mae: 0.3382 - val_loss: 0.1845 - val_mae: 0.3676\n", + "Epoch 466/500\n", + "10/10 [==============================] - 0s 7ms/step - loss: 0.1713 - mae: 0.3515 - val_loss: 0.1845 - val_mae: 0.3670\n", + "Epoch 467/500\n", + "10/10 [==============================] - 0s 6ms/step - loss: 0.1660 - mae: 0.3467 - val_loss: 0.1846 - val_mae: 0.3684\n", + "Epoch 468/500\n", + "10/10 [==============================] - 0s 7ms/step - loss: 0.1815 - mae: 0.3630 - val_loss: 0.1852 - val_mae: 0.3714\n", + "Epoch 469/500\n", + "10/10 [==============================] - 0s 7ms/step - loss: 0.1685 - mae: 0.3455 - val_loss: 0.1852 - val_mae: 0.3712\n", + "Epoch 470/500\n", + "10/10 [==============================] - 0s 6ms/step - loss: 0.1791 - mae: 0.3612 - val_loss: 0.1846 - val_mae: 0.3686\n", + "Epoch 471/500\n", + "10/10 [==============================] - 0s 7ms/step - loss: 0.1707 - mae: 0.3523 - val_loss: 0.1846 - val_mae: 0.3685\n", + "Epoch 472/500\n", + "10/10 [==============================] - 0s 6ms/step - loss: 0.1703 - mae: 0.3525 - val_loss: 0.1846 - val_mae: 0.3683\n", + "Epoch 473/500\n", + "10/10 [==============================] - 0s 6ms/step - loss: 0.1608 - mae: 0.3447 - val_loss: 0.1846 - val_mae: 0.3671\n", + "Epoch 474/500\n", + "10/10 [==============================] - 0s 6ms/step - loss: 0.1675 - mae: 0.3465 - val_loss: 0.1848 - val_mae: 0.3693\n", + "Epoch 475/500\n", + "10/10 [==============================] - 0s 8ms/step - loss: 0.1689 - mae: 0.3513 - val_loss: 0.1846 - val_mae: 0.3683\n", + "Epoch 476/500\n", + "10/10 [==============================] - 0s 7ms/step - loss: 0.1632 - mae: 0.3431 - val_loss: 0.1847 - val_mae: 0.3692\n", + "Epoch 477/500\n", + "10/10 [==============================] - 0s 7ms/step - loss: 0.1642 - mae: 0.3464 - val_loss: 0.1846 - val_mae: 0.3674\n", + "Epoch 478/500\n", + "10/10 [==============================] - 0s 6ms/step - loss: 0.1734 - mae: 0.3511 - val_loss: 0.1851 - val_mae: 0.3707\n", + "Epoch 479/500\n", + "10/10 [==============================] - 0s 6ms/step - loss: 0.1803 - mae: 0.3612 - val_loss: 0.1847 - val_mae: 0.3687\n", + "Epoch 480/500\n", + "10/10 [==============================] - 0s 6ms/step - loss: 0.1679 - mae: 0.3531 - val_loss: 0.1846 - val_mae: 0.3677\n", + "Epoch 481/500\n", + "10/10 [==============================] - 0s 7ms/step - loss: 0.1597 - mae: 0.3406 - val_loss: 0.1846 - val_mae: 0.3677\n", + "Epoch 482/500\n", + "10/10 [==============================] - 0s 6ms/step - loss: 0.1761 - mae: 0.3575 - val_loss: 0.1850 - val_mae: 0.3701\n", + "Epoch 483/500\n", + "10/10 [==============================] - 0s 20ms/step - loss: 0.1707 - mae: 0.3541 - val_loss: 0.1847 - val_mae: 0.3692\n", + "Epoch 484/500\n", + "10/10 [==============================] - 0s 6ms/step - loss: 0.1746 - mae: 0.3534 - val_loss: 0.1847 - val_mae: 0.3686\n", + "Epoch 485/500\n", + "10/10 [==============================] - 0s 7ms/step - loss: 0.1644 - mae: 0.3457 - val_loss: 0.1846 - val_mae: 0.3675\n", + "Epoch 486/500\n", + "10/10 [==============================] - 0s 6ms/step - loss: 0.1724 - mae: 0.3497 - val_loss: 0.1849 - val_mae: 0.3699\n", + "Epoch 487/500\n", + "10/10 [==============================] - 0s 6ms/step - loss: 0.1743 - mae: 0.3552 - val_loss: 0.1849 - val_mae: 0.3699\n", + "Epoch 488/500\n", + "10/10 [==============================] - 0s 8ms/step - loss: 0.1662 - mae: 0.3468 - val_loss: 0.1846 - val_mae: 0.3678\n", + "Epoch 489/500\n", + "10/10 [==============================] - 0s 6ms/step - loss: 0.1742 - mae: 0.3513 - val_loss: 0.1847 - val_mae: 0.3686\n", + "Epoch 490/500\n", + "10/10 [==============================] - 0s 7ms/step - loss: 0.1695 - mae: 0.3481 - val_loss: 0.1846 - val_mae: 0.3674\n", + "Epoch 491/500\n", + "10/10 [==============================] - 0s 7ms/step - loss: 0.1736 - mae: 0.3521 - val_loss: 0.1847 - val_mae: 0.3689\n", + "Epoch 492/500\n", + "10/10 [==============================] - 0s 6ms/step - loss: 0.1554 - mae: 0.3364 - val_loss: 0.1846 - val_mae: 0.3664\n", + "Epoch 493/500\n", + "10/10 [==============================] - 0s 7ms/step - loss: 0.1760 - mae: 0.3597 - val_loss: 0.1847 - val_mae: 0.3685\n", + "Epoch 494/500\n", + "10/10 [==============================] - 0s 7ms/step - loss: 0.1666 - mae: 0.3457 - val_loss: 0.1849 - val_mae: 0.3697\n", + "Epoch 495/500\n", + "10/10 [==============================] - 0s 6ms/step - loss: 0.1673 - mae: 0.3484 - val_loss: 0.1848 - val_mae: 0.3695\n", + "Epoch 496/500\n", + "10/10 [==============================] - 0s 7ms/step - loss: 0.1754 - mae: 0.3581 - val_loss: 0.1848 - val_mae: 0.3695\n", + "Epoch 497/500\n", + "10/10 [==============================] - 0s 6ms/step - loss: 0.1729 - mae: 0.3563 - val_loss: 0.1847 - val_mae: 0.3687\n", + "Epoch 498/500\n", + "10/10 [==============================] - 0s 6ms/step - loss: 0.1727 - mae: 0.3584 - val_loss: 0.1847 - val_mae: 0.3688\n", + "Epoch 499/500\n", + "10/10 [==============================] - 0s 6ms/step - loss: 0.1713 - mae: 0.3522 - val_loss: 0.1847 - val_mae: 0.3685\n", + "Epoch 500/500\n", + "10/10 [==============================] - 0s 7ms/step - loss: 0.1634 - mae: 0.3428 - val_loss: 0.1846 - val_mae: 0.3680\n" + ], + "name": "stdout" + } + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "cRE8KpEqVfaS" + }, + "source": [ + "### 3. Plot Metrics" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "SDsjqfjFm7Fz" + }, + "source": [ + "**1. Loss (or Mean Squared Error)**\n", + "\n", + "During training, the model's performance is constantly being measured against both our training data and the validation data that we set aside earlier. Training produces a log of data that tells us how the model's performance changed over the course of the training process.\n", + "\n", + "The following cells will display some of that data in a graphical form:" + ] + }, + { + "cell_type": "code", + "metadata": { + "id": "CmvA-ksoln8r", + "outputId": "220ea767-6ffd-4eab-c327-c82a016c10eb", + "colab": { + "base_uri": "https://localhost:8080/", + "height": 295 + } + }, + "source": [ + "# Draw a graph of the loss, which is the distance between\n", + "# the predicted and actual values during training and validation.\n", + "train_loss = history_1.history['loss']\n", + "val_loss = history_1.history['val_loss']\n", + "\n", + "epochs = range(1, len(train_loss) + 1)\n", + "\n", + "plt.plot(epochs, train_loss, 'g.', label='Training loss')\n", + "plt.plot(epochs, val_loss, 'b', label='Validation loss')\n", + "plt.title('Training and validation loss')\n", + "plt.xlabel('Epochs')\n", + "plt.ylabel('Loss')\n", + "plt.legend()\n", + "plt.show()" + ], + "execution_count": null, + "outputs": [ + { + "output_type": "display_data", + "data": { + "image/png": "iVBORw0KGgoAAAANSUhEUgAAAYIAAAEWCAYAAABrDZDcAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAALEgAACxIB0t1+/AAAADh0RVh0U29mdHdhcmUAbWF0cGxvdGxpYiB2ZXJzaW9uMy4yLjIsIGh0dHA6Ly9tYXRwbG90bGliLm9yZy+WH4yJAAAgAElEQVR4nO3deXxU5b3H8c8vIQsQZHeBRIMWUCwCEsCISxR7q2LRurRSW6AqKK21aiuitUq1Xot6q5dW26JWa8WLbb1ycatWBFGJCioqIAhqKLhC2Ncs/O4f50yYbCRATibJfN+v17zmnDNn+T0zk/nleZ5znmPujoiIJK+URAcgIiKJpUQgIpLklAhERJKcEoGISJJTIhARSXJKBCIiSU6JQBqUmT1nZqMbet1EMrMiMzstgv26mX0tnP6jmf2yPuvuw3EuMrMX9jXOPey3wMxWN/R+pfG1SnQAknhmtiVutg2wEygP5y9z92n13Ze7nxHFui2du1/eEPsxs1zgEyDN3cvCfU8D6v0ZSvJRIhDcPSs2bWZFwKXu/mLV9cysVezHRURaDjUNSa1iVX8zu87MvgAeMrOOZva0ma0xs/XhdHbcNnPM7NJweoyZvWpmd4XrfmJmZ+zjuj3MbK6ZbTazF83sXjN7tJa46xPjrWb2Wri/F8ysS9zrPzCzlWZWbGa/2MP7M8TMvjCz1Lhl3zaz98LpwWZWaGYbzOxzM/u9maXXsq+HzezXcfPXhtt8ZmYXV1l3uJm9Y2abzGyVmU2Ke3lu+LzBzLaYWX7svY3b/ngzm29mG8Pn4+v73uyJmR0Vbr/BzBab2Yi41840syXhPj81s5+Hy7uEn88GM1tnZq+YmX6XGpnecKnLwUAn4DBgHMF35qFw/lBgO/D7PWw/BFgGdAHuAB40M9uHdR8D3gQ6A5OAH+zhmPWJ8XvAD4EDgXQg9sPUB/hDuP9u4fGyqYG7vwFsBU6tst/Hwuly4OqwPPnAMOBHe4ibMIbTw3i+AfQEqvZPbAVGAR2A4cB4MzsnfO2k8LmDu2e5e2GVfXcCngGmhGX7LfCMmXWuUoZq700dMacBTwEvhNv9BJhmZr3DVR4kaGZsB3wdeClc/jNgNdAVOAi4AdC4N41MiUDqsgu42d13uvt2dy929yfcfZu7bwZuA07ew/Yr3f1+dy8H/gIcQvAHX+91zexQYBBwk7uXuPurwMzaDljPGB9y9w/dfTvwN6B/uPx84Gl3n+vuO4Ffhu9Bbf4HGAlgZu2AM8NluPtb7v66u5e5exHwpxriqMl3wvgWuftWgsQXX7457v6+u+9y9/fC49VnvxAkjuXu/tcwrv8BlgLfiluntvdmT44DsoDfhJ/RS8DThO8NUAr0MbMD3H29u78dt/wQ4DB3L3X3V1wDoDU6JQKpyxp33xGbMbM2ZvansOlkE0FTRIf45pEqvohNuPu2cDJrL9ftBqyLWwawqraA6xnjF3HT2+Ji6ha/7/CHuLi2YxH893+umWUA5wJvu/vKMI5eYbPHF2Ec/0lQO6hLpRiAlVXKN8TMZodNXxuBy+u539i+V1ZZthLoHjdf23tTZ8zuHp804/d7HkGSXGlmL5tZfrj8TmAF8IKZfWxmE+tXDGlISgRSl6r/nf0M6A0McfcD2N0UUVtzT0P4HOhkZm3iluXsYf39ifHz+H2Hx+xc28ruvoTgB+8MKjcLQdDEtBToGcZxw77EQNC8Fe8xghpRjru3B/4Yt9+6/pv+jKDJLN6hwKf1iKuu/eZUad+v2K+7z3f3swmajWYQ1DRw983u/jN3PxwYAVxjZsP2MxbZS0oEsrfaEbS5bwjbm2+O+oDhf9gLgElmlh7+N/mtPWyyPzH+AzjLzE4IO3Zvoe6/k8eAnxIknL9XiWMTsMXMjgTG1zOGvwFjzKxPmIiqxt+OoIa0w8wGEySgmDUETVmH17LvZ4FeZvY9M2tlZt8F+hA04+yPNwhqDxPMLM3MCgg+o+nhZ3aRmbV391KC92QXgJmdZWZfC/uCNhL0q+ypKU4ioEQge+seoDWwFngd+GcjHfcigg7XYuDXwOME1zvUZJ9jdPfFwI8Jftw/B9YTdGbuSayN/iV3Xxu3/OcEP9KbgfvDmOsTw3NhGV4iaDZ5qcoqPwJuMbPNwE2E/12H224j6BN5LTwT57gq+y4GziKoNRUDE4CzqsS919y9hOCH/wyC9/0+YJS7Lw1X+QFQFDaRXU7weULQGf4isAUoBO5z99n7E4vsPVO/jDRHZvY4sNTdI6+RiLR0qhFIs2Bmg8zsCDNLCU+vPJugrVlE9pOuLJbm4mDgfwk6blcD4939ncSGJNIyqGlIRCTJqWlIRCTJNbumoS5dunhubm6iwxARaVbeeuutte7etabXml0iyM3NZcGCBYkOQ0SkWTGzqleUV1DTkIhIklMiEBFJcpEmAjM73cyWmdmKmgaTMrO7zWxh+PjQzDZEGY+IiFQXWR9BONLjvQRjqq8G5pvZzHCQLgDc/eq49X8CDIgqHhHZd6WlpaxevZodO3bUvbIkVGZmJtnZ2aSlpdV7myg7iwcDK9z9YwAzm05wNeiSWtYfSSMMYCYie2/16tW0a9eO3Nxcar+vkCSau1NcXMzq1avp0aNHvbeLsmmoO5XHVF9N5THPK5jZYUAPqg+uFXt9nJktMLMFa9asafBARWTPduzYQefOnZUEmjgzo3Pnzntdc2sqncUXAv8I70xVjbtPdfc8d8/r2rXG02DrVLiqkNtfuZ3CVYV1rywi1SgJNA/78jlF2TT0KZVvrpFN7Te/uJBg6N9IFK4qZNgjwygpLyE9NZ1Zo2aRn5Nf94YiIkkgyhrBfKCnmfUIb/BxITXcZza8YUdHgrHIIzGnaA4l5SWUezkl5SXMKZoT1aFEJALFxcX079+f/v37c/DBB9O9e/eK+ZKSkj1uu2DBAq688so6j3H88cc3SKxz5szhrLPOapB9NZbIagTuXmZmVwDPA6nAn919sZndAixw91hSuBCYHuUNqwtyC0hPTa+oERTkFkR1KBGJQOfOnVm4cCEAkyZNIisri5///OcVr5eVldGqVc0/Z3l5eeTl5dV5jHnz5jVMsM1QpH0E7v6su/dy9yPc/bZw2U1xSQB3n+Tukd6wOj8nn1mjZnHrKbeqWUikkUTdLzdmzBguv/xyhgwZwoQJE3jzzTfJz89nwIABHH/88Sxbtgyo/B/6pEmTuPjiiykoKODwww9nypQpFfvLysqqWL+goIDzzz+fI488kosuuojY/6nPPvssRx55JAMHDuTKK6+s8z//devWcc4553DMMcdw3HHH8d577wHw8ssvV9RoBgwYwObNm/n888856aST6N+/P1//+td55ZVXGvw9q02zG2toX+Xn5CsBiDSSxuqXW716NfPmzSM1NZVNmzbxyiuv0KpVK1588UVuuOEGnnjiiWrbLF26lNmzZ7N582Z69+7N+PHjq51z/84777B48WK6devG0KFDee2118jLy+Oyyy5j7ty59OjRg5EjR9YZ380338yAAQOYMWMGL730EqNGjWLhwoXcdddd3HvvvQwdOpQtW7aQmZnJ1KlT+eY3v8kvfvELysvL2bZtW4O9T3VJmkQgIo2npn65KBLBBRdcQGpqKgAbN25k9OjRLF++HDOjtLS0xm2GDx9ORkYGGRkZHHjggXz55ZdkZ2dXWmfw4MEVy/r3709RURFZWVkcfvjhFefnjxw5kqlTp+4xvldffbUiGZ166qkUFxezadMmhg4dyjXXXMNFF13EueeeS3Z2NoMGDeLiiy+mtLSUc845h/79++/Xe7M3msrpoyLSgsT65VItNdJ+ubZt21ZM//KXv+SUU05h0aJFPPXUU7WeS5+RkVExnZqaSllZ2T6tsz8mTpzIAw88wPbt2xk6dChLly7lpJNOYu7cuXTv3p0xY8bwyCOPNOgx90Q1AhFpcLF+uTlFcyjILWiUZtmNGzfSvXtwzerDDz/c4Pvv3bs3H3/8MUVFReTm5vL444/Xuc2JJ57ItGnT+OUvf8mcOXPo0qULBxxwAB999BF9+/alb9++zJ8/n6VLl9K6dWuys7MZO3YsO3fu5O2332bUqFENXo6aKBGISCQau19uwoQJjB49ml//+tcMHz68wfffunVr7rvvPk4//XTatm3LoEGD6twm1jl9zDHH0KZNG/7yl78AcM899zB79mxSUlI4+uijOeOMM5g+fTp33nknaWlpZGVlNWqNoNndszgvL891YxqRxvXBBx9w1FFHJTqMhNuyZQtZWVm4Oz/+8Y/p2bMnV199dd0bNrKaPi8ze8vdazyPVn0EIiL1dP/999O/f3+OPvpoNm7cyGWXXZbokBqEmoZEROrp6quvbpI1gP2lGoGISJJTIhARSXJKBCIiSU6JQEQkySkRiEiTd8opp/D8889XWnbPPfcwfvz4WrcpKCggdqr5mWeeyYYNG6qtM2nSJO666649HnvGjBksWbL7Drs33XQTL7744t6EX6OmNFy1EoGINHkjR45k+vTplZZNnz69XgO/QTBqaIcOHfbp2FUTwS233MJpp522T/tqqpQIRKTJO//883nmmWcqbkJTVFTEZ599xoknnsj48ePJy8vj6KOP5uabb65x+9zcXNauXQvAbbfdRq9evTjhhBMqhqqG4BqBQYMG0a9fP8477zy2bdvGvHnzmDlzJtdeey39+/fno48+YsyYMfzjH/8AYNasWQwYMIC+ffty8cUXs3Pnzorj3XzzzRx77LH07duXpUuX7rF8iR6uWtcRiMheueoqCO8R02D694d77qn99U6dOjF48GCee+45zj77bKZPn853vvMdzIzbbruNTp06UV5ezrBhw3jvvfc45phjatzPW2+9xfTp01m4cCFlZWUce+yxDBw4EIBzzz2XsWPHAnDjjTfy4IMP8pOf/IQRI0Zw1llncf7551fa144dOxgzZgyzZs2iV69ejBo1ij/84Q9cddVVAHTp0oW3336b++67j7vuuosHHnig1vIlerjqpKkR/PnPcPTRUMuAhCLSxMU3D8U3C/3tb3/j2GOPZcCAASxevLhSM05Vr7zyCt/+9rdp06YNBxxwACNGjKh4bdGiRZx44on07duXadOmsXjx4j3Gs2zZMnr06EGvXr0AGD16NHPnzq14/dxzzwVg4MCBFBUV7XFfr776Kj/4wQ+AmoernjJlChs2bKBVq1YMGjSIhx56iEmTJvH+++/Trl27Pe67PpKmRrB+PSxZAiUlkJmZ6GhEmq89/ecepbPPPpurr76at99+m23btjFw4EA++eQT7rrrLubPn0/Hjh0ZM2ZMrcNP12XMmDHMmDGDfv368fDDDzNnzpz9ijc2lPX+DGM9ceJEhg8fzrPPPsvQoUN5/vnnK4arfuaZZxgzZgzXXHPNfo9SmjQ1gtgNiGq5V4WINHFZWVmccsopXHzxxRW1gU2bNtG2bVvat2/Pl19+yXPPPbfHfZx00knMmDGD7du3s3nzZp566qmK1zZv3swhhxxCaWkp06ZNq1jerl07Nm/eXG1fvXv3pqioiBUrVgDw17/+lZNPPnmfyhYbrhqocbjq6667jkGDBrF06VJWrlzJQQcdxNixY7n00kt5++239+mY8ZKmRqBEINL8jRw5km9/+9sVTUT9+vVjwIABHHnkkeTk5DB06NA9bn/sscfy3e9+l379+nHggQdWGkr61ltvZciQIXTt2pUhQ4ZU/PhfeOGFjB07lilTplR0EgNkZmby0EMPccEFF1BWVsagQYO4/PLL96lciR6uOmmGoX7gARg7Fv79b8jJiSAwkRZMw1A3LxqGuhaqEYiI1EyJQEQkySkRiEi9NLdm5GS1L5+TEoGI1CkzM5Pi4mIlgybO3SkuLiZzL8+R11lDIlKn7OxsVq9ezZo1axIditQhMzOT7OzsvdpGiUBE6pSWlkaPHj0SHYZEJOmahsIxq0REJJQ0iSA9PXhWjUBEpLJIE4GZnW5my8xshZlNrGWd75jZEjNbbGaPRRWLmoZERGoWWR+BmaUC9wLfAFYD881sprsviVunJ3A9MNTd15vZgVHFo0QgIlKzKGsEg4EV7v6xu5cA04Gzq6wzFrjX3dcDuPtXUQWjRCAiUrMoE0F3YFXc/OpwWbxeQC8ze83MXjez02vakZmNM7MFZrZgX09fiyWCf7z/fxSuKtynfYiItESJ7ixuBfQECoCRwP1mVu3Gou4+1d3z3D2va9eu+3Sg99cEQ7X+fdEMhj0yTMlARCQUZSL4FIgf5zM7XBZvNTDT3Uvd/RPgQ4LE0ODmfzEPAC9LpaS8hDlFc6I4jIhIsxNlIpgP9DSzHmaWDlwIzKyyzgyC2gBm1oWgqejjKII5scdxANiuDNJT0ynILYjiMCIizU5kicDdy4ArgOeBD4C/uftiM7vFzGI3Cn0eKDazJcBs4Fp3L44invzcYBju4Uecw6xRs8jPyY/iMCIizU6kQ0y4+7PAs1WW3RQ37cA14SNSsc7igkO/Qb5uTCMiUiHRncWNRqePiojUTIlARCTJJU0iSE0NnpUIREQqS5pEYBbUCjT6qIhIZUmTCCAYgVQ1AhGRypIqEaSlKRGIiFSlRCAikuSUCEREkpwSgYhIklMiEBFJckmVCDIyYOfOREchItK0KBGIiCQ5JQIRkSSnRCAikuSSLhHs2JHoKEREmpakSgSZmaoRiIhUlVSJQE1DIiLVKRGIiCQ5JQIRkSSXdIlAncUiIpUlVSJQZ7GISHVJlQiCpiHn9ldup3BVYaLDERFpEpIqEXy1YxXl5caNs25m2CPDlAxEREiyRLBq6woAdpW2oqS8hDlFcxIbkIhIE5BUieDIg3IBSClvS3pqOgW5BQmNR0SkKWiV6AAa05GH9ABgwpBfMiJvEPk5+QmOSEQk8ZIqEWRkBM+XDbiS3JzExiIi0lQkVdNQLBHoFFIRkd2UCEREklykicDMTjezZWa2wswm1vD6GDNbY2YLw8elUcYTSwS6ulhEZLfI+gjMLBW4F/gGsBqYb2Yz3X1JlVUfd/crooojXmZm8KwagYjIblHWCAYDK9z9Y3cvAaYDZ0d4vDrFEoFqBCIiu0WZCLoDq+LmV4fLqjrPzN4zs3+YWY3n8pjZODNbYGYL1qxZs88BtWkTPG/bts+7EBFpcRLdWfwUkOvuxwD/Av5S00ruPtXd89w9r2vXrvt8sLZtg+etW/d5FyIiLU6UieBTIP4//OxwWQV3L3b3WIv9A8DACONRIhARqUGUiWA+0NPMephZOnAhMDN+BTM7JG52BPBBhPEoEYiI1CCys4bcvczMrgCeB1KBP7v7YjO7BVjg7jOBK81sBFAGrAPGRBUPKBGIiNQk0iEm3P1Z4Nkqy26Km74euD7KGOKlpUFqqhKBiEi8RHcWNyqzoFags4ZERHZLqkQAQSJQjUBEZLekSwSpGdt5s2ix7k4mIhJKqkRQuKqQT3cs571VH+lWlSIioaRKBHOK5uBpW6CkjW5VKSISSqpEUJBbQEr6NijTrSpFRGKS6g5l+Tn5DD1iHcs/KuV/R83SrSpFREiyRACQ06UTn30E+TkHJToUEZEmIamahgDat4eNGxMdhYhI05F0iaBjR1i/HtwTHYmISNOQdImgQwcoL4ctWxIdiYhI05B0iaBjx+B5w4bExiEi0lTUKxGYWVszSwmne5nZCDNLiza0aMQSwfr1iY1DRKSpqG+NYC6QaWbdgReAHwAPRxVUlDp0CJ6VCEREAvVNBObu24Bzgfvc/QLg6OjCio5qBCIildU7EZhZPnAR8Ey4LDWakKIVSwSPvvG0xhoSEaH+ieAqghvIPBneZexwYHZ0YUVn+dY3Afjfd2Zr4DkREeqZCNz9ZXcf4e6Tw07jte5+ZcSxRWJ+8SxIKcW3dtHAcyIi1P+socfM7AAzawssApaY2bXRhhaNUw8vwLK+xLZ008BzIiLUv2moj7tvAs4BngN6EJw51Ozk5+Rz1OHtOaLVSczSwHMiIvVOBGnhdQPnADPdvRRotoM09MptR8b2HkoCIiLUPxH8CSgC2gJzzewwYFNUQUWtWzf47LNERyEi0jTUt7N4irt3d/czPbASOCXi2CLTrVtwHcGOHYmOREQk8erbWdzezH5rZgvCx38R1A6apZKs5QA8OW9hgiMREUm8+jYN/RnYDHwnfGwCHooqqCgVripk8rIfAjDmwd/oOgIRSXr1TQRHuPvN7v5x+PgVcHiUgUVlTtEcSju+D0Dpl1/TdQQikvTqmwi2m9kJsRkzGwpsjyakaBXkFpDRphQOWEXK2j66jkBEkl5971l8OfCImbUP59cDo6MJKVr5OfnMGjWLS57bzpbPvk1+TutEhyQiklD1PWvoXXfvBxwDHOPuA4BTI40sQvk5+Qw7JZ1Vn7TmqQULEh2OiEhC7dUdytx9U3iFMcA1da1vZqeb2TIzW2FmE/ew3nlm5maWtzfx7KvCVYXcv+57AJx3+1R1GItIUtufW1XaHl80SwXuBc4A+gAjzaxPDeu1A34KvLEfseyVOUVzKD3wTejwMaXvXqAOYxFJavuTCOoaYmIwsCI8y6gEmA6cXcN6twKTgUa7vKsgt4CMVulY/7/CR98gZ8eZjXVoEZEmZ4+JwMw2m9mmGh6bgW517Ls7sCpufnW4LH7/xwI57v4Me2Bm42IXs61Zs6aOw9Yt1mE8etwm0tts58G7s/d7nyIizdUeE4G7t3P3A2p4tHP3+p5xVKPwvga/BX5W17ruPtXd89w9r2vXrvtz2Eoe//gPlA76L+Y815mHn32vwfYrItKc7E/TUF0+BXLi5rPDZTHtgK8Dc8ysCDgOmNlYHcZziuaws2wnnn8ntFnDLTe2x5vteKoiIvsuykQwH+hpZj3MLB24EJgZe9HdN7p7F3fPdfdc4HVghLs3yvmcndt0Zhe7IHMTFEzik3cO46mnGuPIIiJNS2SJwN3LgCuA54EPgL+F9zu+xcxGRHXc+ireVkyKhcUfeD8dun/BtddCaWli4xIRaWxR1ghw92fdvZe7H+Hut4XLbnL3mTWsW9BYtQEIzhxqlRJ2c6SWsvnk8Xz4IfzpT40VgYhI0xBpImjK8nPyOfNru08bLe85gy593mfSJNiwIXFxiYg0tqRNBAAHZx28e8Zg3Uk/ZN065/bbExeTiEhjS+pEMKrfKFIttWLeD36bnqe8zj33QFFR4uISEWlMSZ0I8nPyuW/4fRXJwHE+HvA9sF386lcJDk5EpJEkdSIAGDdwHN/q9a2K+bJ2ReQMe5pHHoHlyxMYmIhII0n6RABV+gqAj/tcRlp6ObfemqCAREQakRIBQV9BStxb4VlfkHPaU0ybBkuXJjAwEZFGoERA0Fcw4sjK17itOGocaRml3HJLgoISEWkkSgShCcdPqHQGEW3XUDLwbqZPd5YsSVxcIiJRUyIIxc4gsrj77Xj+HaRm7NAZRCLSoikRxBk3cBxnHxl375y2xZQNupu//915//3ExSUiEiUlgiomHD+hUscx+XfRKlO1AhFpuZQIqqjWcdxmPYef8RRPPAHvvpu4uEREoqJEUIMJx08gLSWtYn55rx/ROmunagUi0iIpEdQgPyefSwZcUjG/K7OYHXmTefJJWLgwgYGJiERAiaAW1QakG/JbWrXZyqRJiYtJRCQKSgS1yM/J51u9d49BROuNlA2+g//7P3j77cTFJSLS0JQI9qDaRWbH3QOZ6/npdesSF5SISANTItiDaheZZW6C/P/i1Rc7saDRbqopIhItJYI6VLvIbMgU0rI2q69ARFoMJYJ6qHQ6aeZmyofcwTPPwJtvJjYuEZGGoERQD/k5+QzvObxiftfge1QrEJEWQ4mgnirdvCZjC6VDbue55+D11xMXk4hIQ1AiqKeq1xUw+HfQZi1XTdyQuKBERBqAEkE9VTuDKGMLHH8nb7zcgcLCxMYmIrI/lAj2QrUziAbdS3q7jeorEJFmTYlgL1U6gyhjK2X5t/PCCzBvXmLjEhHZV0oEe6naGUR5vyPjgA3ccAO4JzAwEZF9FGkiMLPTzWyZma0ws4k1vH65mb1vZgvN7FUz6xNlPA2l0hlE6dvYecIvePlleOaZxMUkIrKvIksEZpYK3AucAfQBRtbwQ/+Yu/d19/7AHcBvo4qnIVU7g2jgVOj8IVdes42yssTFJSKyL6KsEQwGVrj7x+5eAkwHzo5fwd03xc22BZpF40q1M4hSy2DYRD5Z3oaHH05oaCIiey3KRNAdWBU3vzpcVomZ/djMPiKoEVwZYTwNqtoZREc9CdnzuP4XJWzdmri4RET2VsI7i939Xnc/ArgOuLGmdcxsnJktMLMFa9asadwA96DSje4N+I9rWftVOnffndCwRET2SpSJ4FMgJ24+O1xWm+nAOTW94O5T3T3P3fO6du3agCHun2o3uj90HocMep3Jk+HLLxMXl4jI3ogyEcwHeppZDzNLBy4EZsavYGY942aHA8sjjCcSVW90/+VxF7Nt+y5uuimBQYmI7IXIEoG7lwFXAM8DHwB/c/fFZnaLmcX+jb7CzBab2ULgGmB0VPFEpdqN7jt/gA/6HQ884Lz7bgIDExGpJ/NmdhVUXl6eL2hitwcrXFXIiQ+dSLmXBwu2dyDt3n9zwqB2zJoFZomNT0TEzN5y97yaXkt4Z3FLUP1G9xsoPel6Zs+GGTMSF5eISH0oETSQaje6H/hHOHAR43+yky1bEheXiEhdlAgaSPWLzMrhrMv48rM0bqzxpFgRkaZBiaABVbvI7NB5kPcHpkxx3clMRJosJYIGVukiM4Bh15PZsZhLL4WSksTFJSJSGyWCBlbtIrPMzWz/5g9ZvBgmT05cXCIitVEiiEC1juPeT2Nfn86tv97FBx8kLi4RkZooEUSgWscx4KdfCelbufRS2LUrgcGJiFShRBCRah3HWWsoO+1K5s2DP/4xcXGJiFSlRBChqh3H3u9huvZdyHXXwapVe9hQRKQRKRFEqFrHscGaU8+lpKyUH/1I9zgWkaZBiSBi1TqOO35C6cnX8/TT8OijiYtLRCRGiSBiNXYcD7mbzkcu5vLLYcmSBAYnIoISQaOo1nGcsot1Z36TjDYlnENQq6QAAA88SURBVHcebN6cuNhERJQIGkm1juMDPqXP5bfy4Ycwdqz6C0QkcZQIGkm1jmPgtVa/5oQfPsfjj8Pvf5+gwEQk6SkRNKJqHcfA3O7DOeK4JVxzDRQWJigwEUlqSgSNqKaOY1Kcj04+gQO77eCCC2DNmsTFJyLJSYmgkY0bOI5rh15beWHr9fS6/AbWroULL4TS0sTEJiLJSYkgASafNpmTDjup0rKXS+5h5PWzeekluOwydR6LSONRIkiQ3wz7TaX+Asf5C8MYPvYtHnoIbr01gcGJSFJRIkiQGi80w3mm2yDyz1rGzTfDf/93AgMUkaShRJBA1S40AzDnjQH9KTijmKuugptuUjORiERLiSDBJhw/gbSUtErLdqXuoPzcC7jkkqCJ6Fe/SlBwIpIUlAgSLD8nn5fHvEyfLn0qLX/l09l0+u51/PCHQSK45RbVDEQkGkoETUB+Tj4PjHig8vUFwJ3z7qDzd69n9Gi4+WYYNw5KShIUpIi0WEoETUR+Tn716wuAu17/DaXf+j433ggPPAAnnAAffZSAAEWkxVIiaEImnzaZCUMnVFv+2KJplJx8HU88AcuXw4ABMG1aAgIUkRZJiaCJqS0Z3PHaHbxxwHW8+y706wff/z6MHq0hrEVk/0WaCMzsdDNbZmYrzGxiDa9fY2ZLzOw9M5tlZodFGU9zMfm0yVzU96Jqy+947Q5uWPB9Zs8O+gwefRQGDoT58xMQpIi0GJElAjNLBe4FzgD6ACPNrE+V1d4B8tz9GOAfwB1RxdPcPHruozXWDKa9P41hj57MNy8pZPZs2L4dhgyB00+H115LQKAi0uxFWSMYDKxw94/dvQSYDlS6esrdZ7v7tnD2dSA7wniandqaieaunMsJD53A0rZTWbQoqB28/z6ceCKMH6/OZBHZO1Emgu7Aqrj51eGy2lwCPFfTC2Y2zswWmNmCNUk2TnNtzUS7fBeXPX0ZI548mf+4uJBly+BHP4IHH4ReveC88+CNNxIQsIg0O02is9jMvg/kAXfW9Lq7T3X3PHfP69q1a+MG1wTU1kwEQe1g6J+H8tiyqfz+91BUBNddBy+9BMcdB/37Bwnin/+E8vLGjVtEmocoE8GnQE7cfHa4rBIzOw34BTDC3XdGGE+zNvm0yfzprD9Vuu9xjONc9vRlnPzwyawsL+Q//xP+/W/43e+gTZugU/mMM+Cww+CKK+Cvfw1OQ9WVyiICYB7Rr4GZtQI+BIYRJID5wPfcfXHcOgMIOolPd/fl9dlvXl6eL1iwIIKIm4fCVYVMfHEic/89t9Z1zjnyHCYcP4H8nHwguBr5qafgoYfg5Zdhy5ZgvQMPhOOPh0GD4Jhjgkd2NqQ0iXqiiDQkM3vL3fNqfC2qRBAe+EzgHiAV+LO732ZmtwAL3H2mmb0I9AU+Dzf5t7uPqGV3gBJBzHUvXsedr92JU/vnd9JhJ/GbYb+pSAgQNA998AHMmxecZTRvHqxYsXsbM2jfHjp2hA4dglrEkCFw8MFQVgbdugXJIj09SCi9ewe1jtTUGgIQkSYjYYkgCkoEu9WndgDQ/+D+HNf9OEb1G1UpKcRs2gSLFsF778Fnn8GGDbB+ffBYurR+ZyGZBckgM3P3IyOj8nz8so0b4YADgmSycSP07Bkkl5SU4FFaGhy/U6dg3ykpwXP89K5dQW1n/fogSaWnQ1ra7kcsOcV/xVNSoF072LYtOHZ5eZD4WreGTz4JjtuhQ/V91ObLL4PE2Lp1cJxNm4Iy7tgRbLt6dZBE164NXuvSBTp3DrYtLd19nF27YOfO4FFSsvv5kEOC9+edd2DYsGDfO3dWfy+qTi9ZEmz/9a8H782OHcE/AB07whFHBJ91q1ZBrXDt2mA6LS14rjodi7Vt25rfgx074NNP4aCDgtgWLQpOWDj44GC7HTt2ly2+jD16BNtv2xbEUVoaLC8pCaa7dAnWTU2FrKzd3zOz4L0uKQk+v7KyoAzduwfvcWlp8B3btSvYpnPnIIYdO4J9WuUhvVi5MvjO9+4dlHfXruC07J07g+9CeXmw//LyIJZWrYLnqtO7dgXrlJcHn9O6dbBsWfAZZGXt/ny2bw++N9267X7v27SB4uLgfYjtJyUlWDc1NdhfejpcdBGcfHLdf481USJo4epTO4ipKynUZNOm4AtbVhYkirVrgz+SVq2Cvojt24PXysqC5bE/utgPQE3zGRnBdq1bBz/MRUXB9u7BH4JZsHzr1mBZbHn8c0pK8IPVvn2wv9LS3T8mpaXBH1Psjz72XFYWlCcrK3ikpgbz27YFialDhyC+2L7i9xEvFlOnTsGPYHl5MN+mTTDdunWw/cEHw5o1wQ9Q+/bBH3txcbBufMypqcEfekbG7kerVsGPVOwHe1V4Dl5GRvBc9f2In87ODt7fjRt3/7B/7WtBWYuKIDc3SKCxdWOfX1lZEE/8tHsQ29atld/LmLS0IL5164If4I4dgzJu3Bi8Fkv+8WVLSQlqomlpwfSWLcExYo/UVPjqq+D9LC0NyhIro3sQQ3r67h/u9PTgM2zfPpiOrZ+aGsTSunVwrPXrq3+OBx0UxByrGaekBPto3TooQ0pK8PmlpQXvSSz5xH70Y9MpKUE8KSnB9zwtDY46KkjA8d/tlJTgeJs3B985s2C6a9fguxLbx65dQQzl5bv/OZg8GUaNqtefbTVKBEmgcFUhd7x2BzOWzaj3Nj079aRVSit6d+ldqU9BRFoeJYIkEksIr69+nS+2frFX2x6cdTCZrTLpkNmB9JR0Ljn2EsYNHBdRpCLSmJQIktTUt6Zyz+v38MHaD/Z5H51ad+KAjAPokNmB9dvXY2bVpneW7SSjVUa119umt+WnQ35K3wP7MqdoDgW5Bap1NCGFqwr1uSQRJYIkF6slvPPFO6zcuDJhcRjGQVkHVdQ6aksge5Ns9nfdZD3GzvKdfLnlSxyv9rm01DI31WPUd91D2x9Kny599qp/r9LfnxKBxMSSwrLiZZTtKmP5unpdviEiTURGagazR8/e62Swp0TQqkEik2YjPyefJy98smK+cFUhj7z7CEvWLGHlxpWYGWkpaUoQIk1USXkJc4rmNGhznhJBksvPya/xCxXfnLQvVdsvNn+x153VIlK39NR0CnILGnSfSgRSo6o1h30R3wzVtW1XcCpqHU2hzTXZj3Fo+0PplNmJddvXVfpcWnKZm+IxGquPYE+UCCQyDZFMRCR6Gl5MRCTJKRGIiCQ5JQIRkSSnRCAikuSUCEREkpwSgYhIkmt2Q0yY2RpgXwfM6QKsbcBwmgOVOTmozMlhf8p8mLt3remFZpcI9oeZLahtrI2WSmVODipzcoiqzGoaEhFJckoEIiJJLtkSwdREB5AAKnNyUJmTQyRlTqo+AhERqS7ZagQiIlKFEoGISJJLikRgZqeb2TIzW2FmExMdT0Mxsz+b2VdmtihuWScz+5eZLQ+fO4bLzcymhO/Be2Z2bOIi33dmlmNms81siZktNrOfhstbbLnNLNPM3jSzd8My/ypc3sPM3gjL9riZpYfLM8L5FeHruYmMf3+YWaqZvWNmT4fzLbrMZlZkZu+b2UIzWxAui/y73eITgZmlAvcCZwB9gJFm1iexUTWYh4HTqyybCMxy957ArHAegvL3DB/jgD80UowNrQz4mbv3AY4Dfhx+ni253DuBU929H9AfON3MjgMmA3e7+9eA9cAl4fqXAOvD5XeH6zVXPwU+iJtPhjKf4u79464XiP677e4t+gHkA8/HzV8PXJ/ouBqwfLnAorj5ZcAh4fQhwLJw+k/AyJrWa84P4P+AbyRLuYE2wNvAEIIrTFuFyyu+58DzQH443SpczxId+z6UNTv84TsVeBqwJChzEdClyrLIv9stvkYAdAdWxc2vDpe1VAe5++fh9BfAQeF0i3sfwur/AOANWni5wyaShcBXwL+Aj4AN7l4WrhJfrooyh69vBDo3bsQN4h5gArArnO9Myy+zAy+Y2VtmNi5cFvl3W7eqbMHc3c2sRZ4fbGZZwBPAVe6+ycwqXmuJ5Xb3cqC/mXUAngSOTHBIkTKzs4Cv3P0tMytIdDyN6AR3/9TMDgT+ZWZL41+M6rudDDWCT4GcuPnscFlL9aWZHQIQPn8VLm8x74OZpREkgWnu/r/h4hZfbgB33wDMJmgW6WBmsX/m4stVUebw9fZAcSOHur+GAiPMrAiYTtA89N+07DLj7p+Gz18RJPzBNMJ3OxkSwXygZ3i2QTpwITAzwTFFaSYwOpweTdCGHls+KjzT4DhgY1x1s9mw4F//B4EP3P23cS+12HKbWdewJoCZtSboE/mAICGcH65Wtcyx9+J84CUPG5GbC3e/3t2z3T2X4G/2JXe/iBZcZjNra2btYtPAfwCLaIzvdqI7RxqpA+ZM4EOCdtVfJDqeBizX/wCfA6UE7YOXELSLzgKWAy8CncJ1jeDsqY+A94G8RMe/j2U+gaAd9T1gYfg4syWXGzgGeCcs8yLgpnD54cCbwArg70BGuDwznF8Rvn54osuwn+UvAJ5u6WUOy/Zu+Fgc+61qjO+2hpgQEUlyydA0JCIie6BEICKS5JQIRESSnBKBiEiSUyIQEUlySgQiITMrD0d9jD0abKRaM8u1uFFiRZoSDTEhstt2d++f6CBEGptqBCJ1CMeIvyMcJ/5NM/tauDzXzF4Kx4KfZWaHhssPMrMnw/sHvGtmx4e7SjWz+8N7CrwQXiWMmV1pwf0V3jOz6QkqpiQxJQKR3VpXaRr6btxrG929L/B7glExAX4H/MXdjwGmAVPC5VOAlz24f8CxBFeJQjBu/L3ufjSwATgvXD4RGBDu5/KoCidSG11ZLBIysy3unlXD8iKCG8N8HA5494W7dzaztQTjv5eGyz939y5mtgbIdvedcfvIBf7lwc1FMLPrgDR3/7WZ/RPYAswAZrj7loiLKlKJagQi9eO1TO+NnXHT5ezuoxtOMGbMscD8uNE1RRqFEoFI/Xw37rkwnJ5HMDImwEXAK+H0LGA8VNxQpn1tOzWzFCDH3WcD1xEMn1ytViISJf3nIbJb6/AuYDH/dPfYKaQdzew9gv/qR4bLfgI8ZGbXAmuAH4bLfwpMNbNLCP7zH08wSmxNUoFHw2RhwBQP7jkg0mjURyBSh7CPIM/d1yY6FpEoqGlIRCTJqUYgIpLkVCMQEUlySgQiIklOiUBEJMkpEYiIJDklAhGRJPf/XLyidzr6ZFEAAAAASUVORK5CYII=\n", + "text/plain": [ + "
" + ] + }, + "metadata": { + "tags": [], + "needs_background": "light" + } + } + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "iOFBSbPcYCN4" + }, + "source": [ + "The graph shows the _loss_ (or the difference between the model's predictions and the actual data) for each epoch. There are several ways to calculate loss, and the method we have used is _mean squared error_. There is a distinct loss value given for the training and the validation data.\n", + "\n", + "As we can see, the amount of loss rapidly decreases over the first 25 epochs, before flattening out. This means that the model is improving and producing more accurate predictions!\n", + "\n", + "Our goal is to stop training when either the model is no longer improving, or when the _training loss_ is less than the _validation loss_, which would mean that the model has learned to predict the training data so well that it can no longer generalize to new data.\n", + "\n", + "To make the flatter part of the graph more readable, let's skip the first 50 epochs:" + ] + }, + { + "cell_type": "code", + "metadata": { + "id": "Zo0RYroFZYIV", + "outputId": "8dc7544d-9504-4ec8-e362-d8dab905a474", + "colab": { + "base_uri": "https://localhost:8080/", + "height": 295 + } + }, + "source": [ + "# Exclude the first few epochs so the graph is easier to read\n", + "SKIP = 50\n", + "\n", + "plt.plot(epochs[SKIP:], train_loss[SKIP:], 'g.', label='Training loss')\n", + "plt.plot(epochs[SKIP:], val_loss[SKIP:], 'b.', label='Validation loss')\n", + "plt.title('Training and validation loss')\n", + "plt.xlabel('Epochs')\n", + "plt.ylabel('Loss')\n", + "plt.legend()\n", + "plt.show()" + ], + "execution_count": null, + "outputs": [ + { + "output_type": "display_data", + "data": { + "image/png": "iVBORw0KGgoAAAANSUhEUgAAAYgAAAEWCAYAAAB8LwAVAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAALEgAACxIB0t1+/AAAADh0RVh0U29mdHdhcmUAbWF0cGxvdGxpYiB2ZXJzaW9uMy4yLjIsIGh0dHA6Ly9tYXRwbG90bGliLm9yZy+WH4yJAAAgAElEQVR4nO3de3xV1Zn/8c+TQECNiAQUBSSoeMEiRIN4QDEWf1O8jPe2UtvAYAWxTofaiv7qy+poL7+iM5NxqlbUWujooNUZa73U1kgENVbuIIgD2iBYUAwiUC4hyfP7Y+8TTg4nyUlyTm7n++bFK+fs69r7JHmy1rPW2ubuiIiIxMtq7wKIiEjHpAAhIiIJKUCIiEhCChAiIpKQAoSIiCSkACEiIgkpQEibMLOXzWxSqrdtT2ZWYWYXpOG4bmYnhq9/aWZ3JLNtC85zrZn9saXlbOS4RWa2KdXHlbbXrb0LIB2Xme2KeXsosA+oCd9Pc/cnkj2Wu1+Yjm27One/IRXHMbN84C9Ad3evDo/9BJD0ZyiZRwFCGuTuudHXZlYBfNvdX43fzsy6RX/piEjXoSYmabZoE4KZ3WpmW4DHzexIM3vBzLaa2efh64Ex+5SZ2bfD15PN7A0zuy/c9i9mdmELtx1iZgvMbKeZvWpmD5jZfzZQ7mTKeI+ZvRke749m1jdm/bfMbIOZVZrZ7Y3cn9FmtsXMsmOWXWFmK8PXZ5lZuZltN7PNZvYLM8tp4Fi/NrMfx7y/Jdznr2Y2JW7bi81smZntMLONZnZXzOoF4dftZrbLzCLRexuz/xgzW2RmX4RfxyR7bxpjZqeG+283s9VmdmnMuovMbE14zI/N7Afh8r7h57PdzLaZ2UIz0++rNqYbLi3VH+gDDAamEnwvPR6+Pw7YA/yikf1HA+8DfYFZwGNmZi3Y9kngHSAPuAv4ViPnTKaM3wD+ATgKyAGiv7CGAQ+Fxz82PN9AEnD3PwN/A74cd9wnw9c1wPfC64kA44EbGyk3YRkmhOX5P8BQID7/8TegGOgNXAxMN7PLw3Xjwq+93T3X3cvjjt0HeBG4P7y2fwVeNLO8uGs46N40UebuwO+BP4b7/SPwhJmdHG7yGEFz5eHAl4DXwuXfBzYB/YCjgR8CmheojSlASEvVAne6+z533+Pule7+rLvvdvedwE+A8xrZf4O7P+LuNcAc4BiCXwRJb2tmxwGjgB+5e5W7vwE839AJkyzj4+7+v+6+B3gaGBkuvxp4wd0XuPs+4I7wHjTkv4CJAGZ2OHBRuAx3X+Lub7t7tbtXAA8nKEciXwvL9667/40gIMZeX5m7r3L3WndfGZ4vmeNCEFDWuftvwnL9F7AW+PuYbRq6N405G8gF/l/4Gb0GvEB4b4D9wDAz6+Xun7v70pjlxwCD3X2/uy90TRzX5hQgpKW2uvve6BszO9TMHg6bYHYQNGn0jm1mibMl+sLdd4cvc5u57bHAtphlABsbKnCSZdwS83p3TJmOjT12+Au6sqFzEdQWrjSzHsCVwFJ33xCW46Sw+WRLWI6fEtQmmlKvDMCGuOsbbWbzwya0L4Abkjxu9Ngb4pZtAAbEvG/o3jRZZnePDaaxx72KIHhuMLPXzSwSLr8XWA/80cw+NLPbkrsMSSUFCGmp+L/mvg+cDIx2914caNJoqNkoFTYDfczs0JhlgxrZvjVl3Bx77PCceQ1t7O5rCH4RXkj95iUImqrWAkPDcvywJWUgaCaL9SRBDWqQux8B/DLmuE399f1Xgqa3WMcBHydRrqaOOyguf1B3XHdf5O6XETQ/PUdQM8Hdd7r79939eOBS4GYzG9/KskgzKUBIqhxO0Ka/PWzPvjPdJwz/Il8M3GVmOeFfn3/fyC6tKeMzwCVmdk6YUL6bpn9+ngT+iSAQ/TauHDuAXWZ2CjA9yTI8DUw2s2FhgIov/+EENaq9ZnYWQWCK2krQJHZ8A8d+CTjJzL5hZt3M7OvAMILmoNb4M0FtY6aZdTezIoLPaF74mV1rZke4+36Ce1ILYGaXmNmJYa7pC4K8TWNNepIGChCSKiXAIcBnwNvAH9rovNcSJHorgR8DTxGM10ikxWV099XAdwh+6W8GPidIojYmmgN4zd0/i1n+A4Jf3juBR8IyJ1OGl8NreI2g+eW1uE1uBO42s53Ajwj/Gg/33U2Qc3kz7Bl0dtyxK4FLCGpZlcBM4JK4cjebu1cRBIQLCe77g0Cxu68NN/kWUBE2td1A8HlCkIR/FdgFlAMPuvv81pRFms+U95GuxMyeAta6e9prMCJdnWoQ0qmZ2SgzO8HMssJuoJcRtGWLSCtpJLV0dv2B/yZIGG8Cprv7svYtkkjXoCYmERFJSE1MIiKSUJdpYurbt6/n5+e3dzFERDqVJUuWfObu/RKt6zIBIj8/n8WLF7d3MUREOhUzix9BX0dNTCIikpAChIiIJKQAISIiCXWZHISItL39+/ezadMm9u7d2/TG0q569uzJwIED6d69e9L7KECISItt2rSJww8/nPz8fBp+3pO0N3ensrKSTZs2MWTIkKT3UxOTiLTY3r17ycvLU3Do4MyMvLy8Ztf0FCCA8nL42c+CryLSPAoOnUNLPqeMb2IqL4fx46GqCnJyoLQUIpGm9xMR6eoyvgZRVhYEh5qa4GtZWXuXSESSVVlZyciRIxk5ciT9+/dnwIABde+rqqoa3Xfx4sV897vfbfIcY8aMSUlZy8rKuOSSS1JyrLaS8TWIoqKg5hCtQRQVtXeJRCRZeXl5LF++HIC77rqL3NxcfvCDH9Str66uplu3xL/mCgsLKSwsbPIcb731VmoK2wllfA0iEgmale65R81LIm2hfGM5P1v4M8o3pifpN3nyZG644QZGjx7NzJkzeeedd4hEIhQUFDBmzBjef/99oP5f9HfddRdTpkyhqKiI448/nvvvv7/ueLm5uXXbFxUVcfXVV3PKKadw7bXXEp0N+6WXXuKUU07hzDPP5Lvf/W6TNYVt27Zx+eWXc/rpp3P22WezcuVKAF5//fW6GlBBQQE7d+5k8+bNjBs3jpEjR/KlL32JhQsXpvyeNSTjaxAi0nbKN5Yzfu54qmqqyMnOobS4lMig1P9VtmnTJt566y2ys7PZsWMHCxcupFu3brz66qv88Ic/5Nlnnz1on7Vr1zJ//nx27tzJySefzPTp0w8aM7Bs2TJWr17Nsccey9ixY3nzzTcpLCxk2rRpLFiwgCFDhjBx4sQmy3fnnXdSUFDAc889x2uvvUZxcTHLly/nvvvu44EHHmDs2LHs2rWLnj17Mnv2bL7yla9w++23U1NTw+7du1N2n5qS8QFCSWqRtlNWUUZVTRU1XkNVTRVlFWVpCRBf/epXyc7OBuCLL75g0qRJrFu3DjNj//79Cfe5+OKL6dGjBz169OCoo47ik08+YeDAgfW2Oeuss+qWjRw5koqKCnJzczn++OPrxhdMnDiR2bNnN1q+N954oy5IffnLX6ayspIdO3YwduxYbr75Zq699lquvPJKBg4cyKhRo5gyZQr79+/n8ssvZ+TIka26N82R8U1MSlKLtJ2i/CJysnPItmxysnMoyi9Ky3kOO+ywutd33HEH559/Pu+++y6///3vGxwL0KNHj7rX2dnZVFdXt2ib1rjtttt49NFH2bNnD2PHjmXt2rWMGzeOBQsWMGDAACZPnszcuXNTes7GZHwNQklqkbYTGRShtLiUsooyivKL0lJ7iPfFF18wYMAAAH7961+n/Pgnn3wyH374IRUVFeTn5/PUU081uc+5557LE088wR133EFZWRl9+/alV69efPDBBwwfPpzhw4ezaNEi1q5dyyGHHMLAgQO5/vrr2bdvH0uXLqW4uDjl15FIxgeIaJK6rCwIDmpeEkmvyKBImwSGqJkzZzJp0iR+/OMfc/HFF6f8+IcccggPPvggEyZM4LDDDmPUqFFN7hNNip9++ukceuihzJkzB4CSkhLmz59PVlYWp512GhdeeCHz5s3j3nvvpXv37uTm5rZpDaLLPJO6sLDQW/PAoPJyBQmR5nrvvfc49dRT27sY7W7Xrl3k5ubi7nznO99h6NChfO9732vvYh0k0edlZkvcPWF/34yvQYAS1SLSOo888ghz5syhqqqKgoICpk2b1t5FSgkFCBInqhUgRCRZ3/ve9zpkjaG1Mr4XExxIVGdnK1EtIhKlGgRKVIuIJKIAEYoGheg4CAUJEcl0ChAhJapFROpTDiKkEdUinc/555/PK6+8Um9ZSUkJ06dPb3CfoqIiol3iL7roIrZv337QNnfddRf33Xdfo+d+7rnnWLNmTd37H/3oR7z66qvNKX5CHWlacAWIkBLVIp3PxIkTmTdvXr1l8+bNS2rCPAhmYe3du3eLzh0fIO6++24uuOCCFh2ro1KACEUiUFISNDOVlKh5SSRdUvmI36uvvpoXX3yx7uFAFRUV/PWvf+Xcc89l+vTpFBYWctppp3HnnXcm3D8/P5/PPvsMgJ/85CecdNJJnHPOOXVTgkMwxmHUqFGMGDGCq666it27d/PWW2/x/PPPc8sttzBy5Eg++OADJk+ezDPPPANAaWkpBQUFDB8+nClTprBv37668915552cccYZDB8+nLVr1zZ6fe09LbgCRKi8HGbMCHIPM2bo+dQi6RDN9d1xR/C1tT9nffr04ayzzuLll18GgtrD1772NcyMn/zkJyxevJiVK1fy+uuv1/1yTWTJkiXMmzeP5cuX89JLL7Fo0aK6dVdeeSWLFi1ixYoVnHrqqTz22GOMGTOGSy+9lHvvvZfly5dzwgkn1G2/d+9eJk+ezFNPPcWqVauorq7moYceqlvft29fli5dyvTp05tsxopOC75y5Up++tOf1s3BFJ0WfPny5SxcuJBDDjmEJ598kq985SssX76cFStWpGTWVwWIkHIQIumXjp+z2Gam2Oalp59+mjPOOIOCggJWr15drzko3sKFC7niiis49NBD6dWrF5deemndunfffZdzzz2X4cOH88QTT7B69epGy/P+++8zZMgQTjrpJAAmTZrEggUL6tZfeeWVAJx55plUVFQ0eqw33niDb33rW0DiacHvv/9+tm/fTrdu3Rg1ahSPP/44d911F6tWreLwww9v9NjJUIAIKQchkn7p+Dm77LLLKC0tZenSpezevZszzzyTv/zlL9x3332UlpaycuVKLr744gan+W7K5MmT+cUvfsGqVau48847W3ycqOiU4a2ZLrytpgVXgAjp0aMi6ZeOn7Pc3FzOP/98pkyZUld72LFjB4cddhhHHHEEn3zySV0TVEPGjRvHc889x549e9i5cye///3v69bt3LmTY445hv379/PEE0/ULT/88MPZuXPnQcc6+eSTqaioYP369QD85je/4bzzzmvRtUWnBQcSTgt+6623MmrUKNauXcuGDRs4+uijuf766/n2t7/N0qVLW3TOWBoHQfAYxOj89EVFEQ2WE0mjSCT1P1sTJ07kiiuuqGtqGjFiBAUFBZxyyikMGjSIsWPHNrr/GWecwde//nVGjBjBUUcdVW/K7nvuuYfRo0fTr18/Ro8eXRcUrrnmGq6//nruv//+uuQ0QM+ePXn88cf56le/SnV1NaNGjeKGG25o0XW197TgaZ3u28wmAP8OZAOPuvv/i1t/M/BtoBrYCkxx9w3huuOAR4FBgAMXuXtFQ+dq6XTfsc/Izf74HGxuKdX7szVYTiQJmu67c2nudN9pa2Iys2zgAeBCYBgw0cyGxW22DCh099OBZ4BZMevmAve6+6nAWcCn6Shn7DNy938wlqoqU6JaRIT05iDOAta7+4fuXgXMAy6L3cDd57v77vDt28BAgDCQdHP3P4Xb7YrZLqVin5Hb/YQ3yclxJapFREhvDmIAsDHm/SZgdCPbXwdEM0knAdvN7L+BIcCrwG3uXpPqQsY/I5fJ2ZrVVaQZ3B0za+9iSBNakk7oEElqM/smUAhEU/3dgHOBAuAj4ClgMvBY3H5TgakAxx13XErKolldRZLXs2dPKisrycvLU5DowNydyspKevbs2az90hkgPiZIMEcNDJfVY2YXALcD57n7vnDxJmC5u38YbvMccDZxAcLdZwOzIUhSt6SQsUnqnOwcSk77MzO+MVyzuookYeDAgWzatImtW7e2d1GkCT179mTgwIHN2iedAWIRMNTMhhAEhmuAb8RuYGYFwMPABHf/NG7f3mbWz923Al8Gmt9FKQmxSeqqmiqefblSjx8VSVL37t0ZMmRIexdD0iRtSWp3rwZuAl4B3gOedvfVZna3mUXHsd8L5AK/NbPlZvZ8uG8N8AOg1MxWAQY8ko5yxiapc7JzuOrCPI2oFhEhzeMg2lJLx0FA/YFykUERZs+GZ5+Fq66CqVNTXFARkQ6ksXEQHSJJ3d4ig4I2pLKKMlYtyWXGjCAHsXAhDB+uJiYRyUwKENRPVNsbe6it+hK1NaYchIhkNE3WR/1Ede3g18juVq0chIhkPNUgOJCorqqpIid/KSXz1lL53nANlhORjKYAwcGjqSODhlN+tAbLiUhmU4AIHZSo1mA5EclwChAhJapFROpTkjqUKFGdlQVmkJfX3qUTEWl7ChCh2BHVPfKX8r27PiI7G2prYcYMKC9v7xKKiLQtBYhQNFF9z/n3UFpcSm8/gdraIEDo4UEikokUIBpQVITmZBKRjKYkdSh+2u/S4lJKSyN6eJCIZCzVIELx036XVZS1d5FERNqVahCheqOps3PIq7yE8d9AYyFEJGMpQISiSeq5K+YCsGxhLz04SEQymgJEnDkr5lBVU0X29vfo1r0UyFaSWkQykgJEjNg8BAPe4Pp/fQJWFrd3sURE2oWS1DHiHz9acEwBc+bAI4/A+PEaLCcimUU1iBjxs7qW/edw5SFEJGMpQMSJndU179RccnIOzOqqPISIZBIFiDj1B8zdQ8mTf2bZK8Pbu1giIm1OOYg48QPmlm1epjyEiGQkBYg48YlqKs47KA8hIpIJFCDiRAZFKJlQwvgh4ymZUELx5YM1aZ+IZCTlIOKUbyxnxh9mUFVTxcKPFlJaPJySkgjPPgtXXaVeTCKSORQg4sTnIOa+sI45349QVQULF8Lw4QoSIpIZ1MQURzkIEZGAahBx4iftK6jeQU4OGgshIhlHAaIB0Un7crLnaCyEiGSktDYxmdkEM3vfzNab2W0J1t9sZmvMbKWZlZrZ4Lj1vcxsk5n9Ip3ljKexECIiaQwQZpYNPABcCAwDJprZsLjNlgGF7n468AwwK279PcCCdJWxIcpDiIikt4npLGC9u38IYGbzgMuANdEN3H1+zPZvA9+MvjGzM4GjgT8AhWks50GUhxARSW+AGABsjHm/CRjdyPbXAS8DmFkW8C8EAeOChnYws6nAVIDjjjuulcU9mPIQIpLJOkQ3VzP7JkEt4d5w0Y3AS+6+qbH93H22uxe6e2G/fv1SWiblIUQk06WzBvExMCjm/cBwWT1mdgFwO3Ceu+8LF0eAc83sRiAXyDGzXe5+UKI7XaJ5iKAGkTgPoQFzItKVpTNALAKGmtkQgsBwDfCN2A3MrAB4GJjg7p9Gl7v7tTHbTCZIZLdZcICDHx7EpsHM+Q/lIUQkc6QtQLh7tZndBLwCZAO/cvfVZnY3sNjdnydoUsoFfmtmAB+5+6XpKlNrRCJQWgpz57Z3SURE2oa5e3uXISUKCwt98eLFKTte/QcH5VBaXAqbIowff6AWUVqqZiYR6dzMbIm7J+wp2iGS1B1RfJK6rKKMsjI0HkJEMoam2mhAfJK6KL8IugXPhaitDb4qDyEiXZkCRAPiB8tFBamSA19FRLoqBYgmRAfLzVkxh0k73qO6ejDuUF2trq4i0rUpB9GI+DwE+a/r8aMikjFUg2hEfB6i+JKhFI9UV1cRyQwKEI1oKA8xZ07Qi2nOHHV1FZGuSwEiCfF5iKqqwZpyQ0S6POUgmtBQHiIrK+jJlJfX3iUUEUkPBYgmxD88qPiSoZSUHBgPMWOGZnYVka5JAaIJkUERSiaUMH7IeEomlBAZFKGyMggOtbUaUS0iXZdyEE0o31jOjD/MoKqmioUfLWT4UcMpKoroCXMi0uWpBtGERHMyRSJQUhI8OKikRElqEemaVINoQqI5mcrLg9xDVRUsXAjDhytIiEjXowDRhERjIRLN6qoAISJdjQJEkmLHQpSc9mdycoazb5+6uopI16UcRBLi8xCVeS+oq6uIdHkKEEmIHwtRlF+krq4i0uWpiSkJifIQRUVBF1c1M4lIV6UaRDPMWTGHR5Y+wvi542FguZqZRKRLU4BIUqLxEGpmEpGuLKkmJjM7DNjj7rVmdhJwCvCyu+9Pa+k6ED2jWkQyTbI5iAXAuWZ2JPBHYBHwdeDadBWso9EzqkUk0yQbIMzdd5vZdcCD7j7LzJans2AdlZ5RLSKZItkchJlZhKDG8GK4LDs9Req49GwIEckkyQaIGcD/Bf7H3Veb2fHA/PQVq2Mqyi8iOysbw8jOytazIUSkS0uqicndXwdeBzCzLOAzd/9uOgvWURlW72uinkxqZhKRriCpGoSZPWlmvcLeTO8Ca8zslvQWreMpqyijurYax6muraasoqxuwJyamUSkq0m2iWmYu+8ALgdeBoYA30pbqTqoRFNuRJ8NoWYmEelqkg0Q3c2sO0GAeD4c/+BN7WRmE8zsfTNbb2a3JVh/s5mtMbOVZlZqZoPD5SPNrNzMVofrvt6ci0qXaFfX68+4nkkjJtUt14A5EemKkg0QDwMVwGHAgvAX+Y7GdjCzbOAB4EJgGDDRzIbFbbYMKHT304FngFnh8t1AsbufBkwASsysd5JlTbvYKTfKN5ZTVBTUIMw0YE5Euo6kAoS73+/uA9z9Ig9sAM5vYrezgPXu/qG7VwHzgMvijjvf3XeHb98GBobL/9fd14Wv/wp8CvRL+qrSKNGUG6ABcyLS9SSbpD7CzP7VzBaH//+FoDbRmAHAxpj3m8JlDbmOIL8Rf+6zgBzggwTrpkbLtHXr1iavIxWieYgssjAz8g7No6wsGCgXO2BORKSzS7aJ6VfATuBr4f8dwOOpKoSZfRMoBO6NW34M8BvgH9y9Nn4/d5/t7oXuXtivX9tUMCKDIpRMKCE7K5tar2XGH2aQd+oq9WQSkS4n2QBxgrvfGTYXfeju/wwc38Q+HwODYt4PDJfVY2YXALcDl7r7vpjlvQhGbd/u7m8nWc42Ubm7klqvpdZr9YQ5Eemykg0Qe8zsnOgbMxsL7Glin0XAUDMbYmY5wDXA87EbmFkBQQL8Unf/NGZ5DvA/wFx3fybJMrYZPWFORDJBspP13QDMNbMjwvefA5Ma2R53rzazm4BXCOZt+lU4TcfdwGJ3f56gSSkX+K0F2d2P3P1SgmascUCemU0ODznZ3TvEBIHRZqZn1zzLVcOuIjIoAkV6wpyIdC3m3uRwhgMbB80+uPsOM5vh7iVpK1kzFRYW+uLFi9vkXOUbyxk/d3zdsyFKi0uJDIowezbcdBPU1ECPHlBaqmk3RKRjM7Ml7l6YaF2znijn7jvCEdUAN7e6ZJ1UQ11dKyuD4FBbG9Qk1MwkIp1Zax45mrE9/hN1dYWgWak27GtVW6tmJhHp3FoTIJJvm+piEnV1Ld9YTmVl0NUVgq+Vle1bThGR1mg0SW1mO0kcCAw4JC0l6iTiu7oGM7tG6NEjaF7KylINQkQ6t0ZrEO5+uLv3SvD/cHdPtgdUl5SomUkzu4pIV9KaJqaM1lgzU3Q8xN69MHdue5dURKRlFCBaIXEzU1CDgGBupscfVy1CRDonBYhWiH9GdfQBQlOmHNhm/351dxWRzkkBopXin1ENUFBwYL26u4pIZ6UA0QqJnlENqLuriHQJChCt0NCAuaKiYKqNrCx1dxWRzksBohUa6smk7q4i0hUoQLRSop5MgLq7ikinpwDRSo01M6m7q4h0ZgoQrdRYM5O6u4pIZ6YAkQINNTOpu6uIdGYKECmQaMAc1O/uagbLlrVfGUVEmksBIkUSDZgrKoJu4ZSGykOISGejAJECDQ2Yi+YhLIwZVVXqzSQinYcCRAo01JMJoLgYuncPXqsWISKdiQJECjTUkwlQbyYR6bQUIFKkoZ5MoN5MItI5KUCkSGPNTOrNJCKdkQJEijTWzBTfm+mRR2D27PYrq4hIMhQgUqihZqb4PERNDdx4o5LVItKxKUCkUEMD5iDozRSdmwmCIKEuryLSkSlApFiiAXMQ1CL+/u/bo0QiIi2jAJFCsQPmqmqqmLuifhVh5szgQUIQ1CZiezeJiHQ0ChApFG1iAnCcx5c/XpeohqAWcf/9wcA5dz1ISEQ6trQGCDObYGbvm9l6M7stwfqbzWyNma00s1IzGxyzbpKZrQv/T0pnOVMlMijClJFT6pqXEtUi9CAhEeks0hYgzCwbeAC4EBgGTDSzYXGbLQMK3f104BlgVrhvH+BOYDRwFnCnmR2ZrrKmUvGIYrpnB3NrJKpFxD9I6LHHVIsQkY4pnTWIs4D17v6hu1cB84DLYjdw9/nuvjt8+zYwMHz9FeBP7r7N3T8H/gRMSGNZUya+FhE7eR8EzUwXXXRg+/37YdasNi6kiEgS0hkgBgAbY95vCpc15Drg5ebsa2ZTzWyxmS3eunVrK4ubOtFaRKLurgD9+9ff/ne/08A5Eel4OkSS2sy+CRQC9zZnP3ef7e6F7l7Yr1+/9BSuhRrq7goHj4lwh5tuUlOTiHQs6QwQHwODYt4PDJfVY2YXALcDl7r7vubs21E11d01EoEHHzwwPxNAdbUS1iLSsaQzQCwChprZEDPLAa4Bno/dwMwKgIcJgsOnMateAf7OzI4Mk9N/Fy7rFJrq7gowdSo89FD9hLWeFSEiHUnaAoS7VwM3Efxifw942t1Xm9ndZnZpuNm9QC7wWzNbbmbPh/tuA+4hCDKLgLvDZZ1CU4nqqKlT4frrD7zXsyJEpCPpls6Du/tLwEtxy34U8/qCRvb9FfCr9JUuvYpHFDNnxRz2Ve87aPrvWPHPiti+vY0KKCLShA6RpO6KGpv+O1Zl5YFnVgPcd596NIlIx6AAkUaVuyupqa2h1mvZV70vYTNT7MA5CGoRN9ygICEi7bPY+HQAABHESURBVE8BIo3yDs2jlloAaqlN2MwUicADD9SvRbgrSIhI+1OASKPK3ZVkWXCLDWPZ5sTPGp06FS67rP4yjY0QkfamAJFGRflFdMsK+gE01N01aubMYJbXWNXV6tUkIu1HASKNkpndtW7bCLz+Oowbd2CZu3o1iUj7UYBIs6Zmd40VicCECfXzEffeC7fe2hYlFRGpTwEizaK1iKj9NfsT9maKiu/V5B7M9qogISJtTQGiDRQcc2A0XEO9maIS9WoCBQkRaXsKEG0g2d5MUVOnwi23HLxcQUJE2pICRBuI7830yNJHmL2k8UEOP/950LMp3r33anyEiLQNBYg2EJ+HqPEabnrppgaT1VGJgoQG0YlIW1GAaCPFI4rrahHQ8Ayv8RoKEtOmqblJRNJLAaKNRAZFuDlyc917xxtNVsf6+c/h8ssPXj5rFpx3nkZbi0h6KEC0od49ejcrWR0r0UhrgAUL4Jxz1OQkIqmnANGGWpKsjko00jqqtjZociooCGoUo0crYIhI6ylAtKGWJqvr9g+DRKLeTQDLlwc1infeUY5CRFpPAaKNtTRZHevnP4eHH4asJj69WbOCWsX06cpTiEjzKUC0sUTJ6u37mj8j39Sp8MYbiZucYi1fDr/8JYwZA0OGBAFj9OigdvGznylwiEjD0vpMakmsd4/eGIbjAPxb+b9x+cmXExkUadZxok1Os2fDY49Bz56wY0cQFBKpqDjw+p13gq9mMHgwHHccDBsGxcXBcaXtlJcH07oXFeneS8di7t7eZUiJwsJCX7x4cXsXIynlG8sZ9+txVNdWA0GPpmlnTuOhSx5KyfFvvTVoXmqp/Hzo3Rv27YN+/YJle/cGv8B694a8vOBZ2kVFwbq54Qzm8cGlvX/xpfP80WPH3ouWnKO8HMaPh6oqyMmB0tLmHSf+GsvL638ekJpyNnbe6DmaOnZs2QoKUleeVH3O6f5+jf9sGvpZgbb9uTGzJe5emHCdAkT7mL1kNje+eCM1XgNAtmXz4MUPMvXMqak5/mwoKYH33kvJ4ZIWDS6ffw4ffRQM6outpQBs3Qo9egQBqLVfowEs/piJzh8Nei09ZqJjRzV0jqaO9fHHwbqofv1gwIDkyrdhQ/1rPPpo+OSTg8vVWDmbKl+ir9XV8MEHQe+52HM09jlXV8P69fXL0th9a87nvHFjUJbWHCv2mszgxBOhW7fWfx829j0zdGhwjvj7mZXV/HKcfHLQeaUlAUUBooOa/sJ0frnkl3Xvu2d15/XJrze7qakx0b9a1qwJvnkb+kEVkc6te/egybm5QaKxAKEcRDsqHlHMo8serWtqivZoSmWAiEQO/oaJBo0tW2Dbtvp/iYpI57R/f9A0lcpmKQWIdhTt0TTrzSBh0NIeTc0+byNBI1rTiK0679gBK1YogIh0ZN27H8hhpIoCRDuL79F031v3ccKRJ6QsF5GsREEjVmytI2rbtgPJ6x07EgeXPn2C7bZubVl7d2vafuPPn6r25Nhjx96LhsqQ7LGSLWfsMRPd42HDoFev4K/Jnj0bL2dLP5OcnKANfd26A+do6nPOyTnwvRL9PmrJNafy/jV0TVVVqfs+jN0m9rOJPUfsuY89Fk466eBt0pWDaIwCRDsryi8iOyu7rpmp1mu58cUbGX7U8JQ2NbVWUwFERLoeDZRrZ5FBER646AGMA88YrfGaumYnEZH2ogDRAUw9cyqXnXJZvWW/e/93SU/kJyKSDmkNEGY2wczeN7P1ZnZbgvXjzGypmVWb2dVx62aZ2Woze8/M7jczi9+/K5k5ZibZll333nFufPHGpCfyExFJtbQFCDPLBh4ALgSGARPNbFjcZh8Bk4En4/YdA4wFTge+BIwCzktXWTuCyKAID178oJqaRKTDSGcN4ixgvbt/6O5VwDygXjuKu1e4+0qgNm5fB3oCOUAPoDvwSRrL2iGoqUlEOpJ0BogBwMaY95vCZU1y93JgPrA5/P+Kux80aYSZTTWzxWa2eGvsfAWdmJqaRKSj6JBJajM7ETgVGEgQVL5sZufGb+fus9290N0L+0U7IndyamoSkY4inQHiY2BQzPuB4bJkXAG87e673H0X8DKQMb3w1dQkIh1BOgPEImComQ0xsxzgGuD5JPf9CDjPzLqZWXeCBHUbz0vavtTUJCLtLW0Bwt2rgZuAVwh+uT/t7qvN7G4zuxTAzEaZ2Sbgq8DDZrY63P0Z4ANgFbACWOHuv09XWTuihpqavv38txUkRKRNaLrvDu6Kp67gubXP1VuWjmnBRSQzNTbdd4dMUssB8U1NAPtr93P5vMuVkxCRtFKA6OASNTUBfLr7U6a9ME1BQkTSRgGiE5h65lR+eckvDwoSALf88RYFCRFJCwWITiIaJLLiPrIdVTuY9sI0hvz7EAUKEUkpBYhOZOqZU3ljyhsM6xs/pRVUbK9g2gvTuPXVW9uhZCLSFSlAdDKRQREevfRRumUlftbTrDdncdqDp6k2ISKtpgDRCUUGRVgweQHjjhuXcP2arWuY9sI0jvmXY7jiqSs0bkJEWkQBopOKDIrw+j+8zltT3mLcceMSJrC37NrCc2ufY8yvxnDer89ToBCRZtFAuS5i9pLZTH9hOrUHzZxe39A+Q+mW1Y2T+57MhSdeSOXuSoryizToTiRDNTZQTgGiCynfWM5tr97Ggo8WNGs/wxjRfwRnDzib4hHFChZdTPnGcsoqyvSHgCSkAJFhyjeWM+vNWby96W22/G1Ls/fP751P75692Ve9j36H9QOHrbu30qNbD3KychiaN5Stf9vKyGNGsmPvDoB2Cyxd4Zdf9BryDs1rtEaX6Fqbuv7yjeWMnzueqpoqcrJzKC0u7bT3KZ3a+/somfOnq4wKEBls9pLZ/HThT9nwxYa0nyvafNWjWw/2Ve876Gt8sEm0TXO+fr7nczbu2Ii7k2VZDD96ODlZORQNKWLH3h2s2bqm3rn6HdaPYX2H0atnL8r+UkbP7j3p07MP2/ZsY+vurfQ7rB99evZJeG3RbWKD5LrKdXXH6J/bn+IRxaz6dBWPLX2Mnt17MqzvMAqOKeDldS/zfuX7nNz3ZGaOmVm3TVVtVd011PqBpsFuWd24OXIz//vZ//J+5ft11/rRFx8BkJ2VzSVDLwHgxXUvsr92P4Zxar9TKehfwLrKdVTVVrGveh97qvewYfsGnODnfPARgzm0+6F11xG9V1t2bWnyGgH65/an4JgClm1eBlD3Orr/3uq9XHfGdQB11xg9T+8evdm+b3vdvY/9LKLlbegziD3vll1b6n0m8Z9r9FiNfR/GnmPbnm28ufFNar0Wwziu93F1fyAls29sGeLvR+z3X7RZN/5+Dc0bytOrn6a6tposy2LscWPrzhHdpnfP3rz6l1ep9VqyLIsLhlzA9r3bD/r+a0ngUIAQZi+ZXfcDu2XnlhbVLESk4+qR3YP5k+Y3O0g0FiASd6aXLmfqmVOZeubUuvezl8zm2TXP0u+wfizdvJS1n62t+ytTRDqfqpoqyirKUtr8pACRoeIDRvnGcuaumJuwWSbaLFRdW80H2z5osqeUiLS9LMuiKL8opcdUgBAgGFeRzF8esQnVaDt0c9p+U5mDiB6zfGM5+2v3JyxvNOH++Z7PD8rDGIbjGMbRuUfz6d8+rWuHBurVqAxjcO/BdM/qXi9IRo/RGMM48pAj2bZnW73lQ/sMpaqm6qAcRKz+uf3pn9u/7ppXfbKKGq+pt00WWZzQ54S6chnGiX1OpKqmio+++Cip62jtNTZk4OED+Xjnx03uH70X8eVtTPRzi+YkYo/VWC6sR7cerNyy8qA/dCz811AOIpoHauh+rtu27qDjDe49mN49ezerWdcwzKzueyJ6/7Msi8JjClm8efFB67ItmwcvfjDlCXYFCGmWZANJW4rt3QEwd8Vc4OCeVdFaUnQdUK9XSPxxGupZlGi7aPL12F7HMnPMzITliOaBotvEHy96ru37trN883KuGnZVvVpe/LbRAB09fmO9nBq7jmSvMXrOLbu20D+3P7169mL55uWMPGYkvXv0bvD+R+97dL/ovU/0OcWXKfZrbGI8trzxn2tz/9CJPU9TPYQau2fRZtvY+xF7rETrY+9D7HVF73n864a+T9PV+0pJahGRDKYnyomISLMpQIiISEIKECIikpAChIiIJKQAISIiCSlAiIhIQl2mm6uZbQXSPyNdevUFPmvvQnQguh8H6F7Up/tRX2vux2B375doRZcJEF2BmS1uqD9yJtL9OED3oj7dj/rSdT/UxCQiIgkpQIiISEIKEB3L7PYuQAej+3GA7kV9uh/1peV+KAchIiIJqQYhIiIJKUCIiEhCChBtyMx+ZWafmtm7Mcv6mNmfzGxd+PXIcLmZ2f1mtt7MVprZGe1X8tQzs0FmNt/M1pjZajP7p3B5pt6Pnmb2jpmtCO/HP4fLh5jZn8PrfsrMcsLlPcL368P1+e1Z/nQws2wzW2ZmL4TvM/leVJjZKjNbbmaLw2Vp/1lRgGhbvwYmxC27DSh196FAafge4EJgaPh/KvBQG5WxrVQD33f3YcDZwHfMbBiZez/2AV929xHASGCCmZ0N/Bz4N3c/EfgcuC7c/jrg83D5v4XbdTX/BLwX8z6T7wXA+e4+Mma8Q/p/Vtxd/9vwP5APvBvz/n3gmPD1McD74euHgYmJtuuK/4HfAf9H98MBDgWWAqMJRsd2C5dHgFfC168AkfB1t3A7a++yp/AeDAx/6X0ZeAGwTL0X4XVVAH3jlqX9Z0U1iPZ3tLtvDl9vAY4OXw8ANsZstylc1uWETQIFwJ/J4PsRNqksBz4F/gR8AGx39+pwk9hrrrsf4fovgLy2LXFalQAzoe7B0Xlk7r0AcOCPZrbEzKLPoU37z4qeSd2BuLubWUb1OzazXOBZYIa77zCzunWZdj/cvQYYaWa9gf8BTmnnIrULM7sE+NTdl5hZUXuXp4M4x90/NrOjgD+Z2drYlen6WVENov19YmbHAIRfPw2XfwwMitluYLisyzCz7gTB4Ql3/+9wccbejyh33w7MJ2hG6W1m0T/kYq+57n6E648AKtu4qOkyFrjUzCqAeQTNTP9OZt4LANz94/DrpwR/PJxFG/ysKEC0v+eBSeHrSQRt8dHlxWGPhLOBL2Kqk52eBVWFx4D33P1fY1Zl6v3oF9YcMLNDCPIx7xEEiqvDzeLvR/Q+XQ285mGDc2fn7v/X3Qe6ez5wDcG1XUsG3gsAMzvMzA6Pvgb+DniXtvhZae/kSyb9B/4L2AzsJ2gXvI6grbQUWAe8CvQJtzXgAYJ26FVAYXuXP8X34hyCdtWVwPLw/0UZfD9OB5aF9+Nd4Efh8uOBd4D1wG+BHuHynuH79eH649v7GtJ0X4qAFzL5XoTXvSL8vxq4PVye9p8VTbUhIiIJqYlJREQSUoAQEZGEFCBERCQhBQgREUlIAUJERBJSgBBpgpnVhLNoRv/f1vReSR8732Jm9xXpSDTVhkjT9rj7yPYuhEhbUw1CpIXCOfpnhfP0v2NmJ4bL883stXAu/lIzOy5cfrSZ/U/4zIcVZjYmPFS2mT0SPgfij+FIaszsuxY8L2Olmc1rp8uUDKYAIdK0Q+KamL4es+4Ldx8O/IJgBlKA/wDmuPvpwBPA/eHy+4HXPXjmwxkEo2IhmLf/AXc/DdgOXBUuvw0oCI9zQ7ouTqQhGkkt0gQz2+XuuQmWVxA85OfDcOLBLe6eZ2afEcy/vz9cvtnd+5rZVmCgu++LOUY+8CcPHvqCmd0KdHf3H5vZH4BdwHPAc+6+K82XKlKPahAireMNvG6OfTGvaziQG7yYYE6dM4BFMTOZirQJBQiR1vl6zNfy8PVbBLOQAlwLLAxflwLToe7hQEc0dFAzywIGuft84FaCKawPqsWIpJP+IhFp2iHhk96i/uDu0a6uR5rZSoJawMRw2T8Cj5vZLcBW4B/C5f8EzDaz6whqCtMJZvdNJBv4zzCIGHC/B8+JEGkzykGItFCYgyh098/auywi6aAmJhERSUg1CBERSUg1CBERSUgBQkREElKAEBGRhBQgREQkIQUIERFJ6P8DuY1bQtxc3zoAAAAASUVORK5CYII=\n", + "text/plain": [ + "
" + ] + }, + "metadata": { + "tags": [], + "needs_background": "light" + } + } + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "W4EQD-Bb8hLM" + }, + "source": [ + "From the plot, we can see that loss continues to reduce until around 200 epochs, at which point it is mostly stable. This means that there's no need to train our network beyond 200 epochs.\n", + "\n", + "However, we can also see that the lowest loss value is still around 0.155. This means that our network's predictions are off by an average of ~15%. In addition, the validation loss values jump around a lot, and is sometimes even higher.\n", + "\n", + "**2. Mean Absolute Error**\n", + "\n", + "To gain more insight into our model's performance we can plot some more data. This time, we'll plot the _mean absolute error_, which is another way of measuring how far the network's predictions are from the actual numbers:" + ] + }, + { + "cell_type": "code", + "metadata": { + "id": "Md9E_azmpkZU", + "outputId": "e47fe879-5e16-4e3c-9e98-279059955384", + "colab": { + "base_uri": "https://localhost:8080/", + "height": 295 + } + }, + "source": [ + "plt.clf()\n", + "\n", + "# Draw a graph of mean absolute error, which is another way of\n", + "# measuring the amount of error in the prediction.\n", + "train_mae = history_1.history['mae']\n", + "val_mae = history_1.history['val_mae']\n", + "\n", + "plt.plot(epochs[SKIP:], train_mae[SKIP:], 'g.', label='Training MAE')\n", + "plt.plot(epochs[SKIP:], val_mae[SKIP:], 'b.', label='Validation MAE')\n", + "plt.title('Training and validation mean absolute error')\n", + "plt.xlabel('Epochs')\n", + "plt.ylabel('MAE')\n", + "plt.legend()\n", + "plt.show()" + ], + "execution_count": null, + "outputs": [ + { + "output_type": "display_data", + "data": { + "image/png": "iVBORw0KGgoAAAANSUhEUgAAAYgAAAEWCAYAAAB8LwAVAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAALEgAACxIB0t1+/AAAADh0RVh0U29mdHdhcmUAbWF0cGxvdGxpYiB2ZXJzaW9uMy4yLjIsIGh0dHA6Ly9tYXRwbG90bGliLm9yZy+WH4yJAAAgAElEQVR4nO2de5wU1ZX4v6ebmQEEJQ6YUUGGKKAoy1O0UXEM0fURFUUTDbvIYuShGDGJaB4a4zOSbGRVVDAGYWNAXX+yqKir6CjKRAVBEJVIdBRUEEcBDTLDzJzfH7eqp7qnu6d76J7n+c6nP1N161bVqVvddeqec+65oqoYhmEYRjyh5hbAMAzDaJmYgjAMwzASYgrCMAzDSIgpCMMwDCMhpiAMwzCMhJiCMAzDMBJiCqKFIyJPichF2a7bnIhIuYh8LwfHVRE5zFu+V0SuTaduI84zTkT+r7FytjVE5AERuSnLx5wgIi9n85hG5nRobgHaIiLydWC1M1AJ1Hjrk1X1wXSPpaqn5aJuW0dVp2TjOCJSDHwA5KlqtXfsB4G076GRW0RkAvBjVT2+uWVpa5iCyAGq2sVfFpFy3Jf3ufh6ItLBf+gYhtHySfSbzfR33Jp+92ZiakJEpERENovI1SKyBZgnIt8SkSdEZJuIfOkt9wzsUyoiP/aWJ4jIyyLyB6/uByJyWiPr9hGRl0TkKxF5TkRmi8hfksidjow3isgr3vH+T0S6B7b/u4h8KCIVIvKrFO1zjIhsEZFwoOwcEVnrLY8QkTIR2S4in4rIXSKSn+RYMWYPEbnK2+cTEZkYV/cMEVktIjtFZJOIXB/Y/JL3f7uIfC0ikXjzh4iMFJHXRWSH939kum0TJ4f//ZghIp958o4RkdNF5O8i8oWI/DJQPyQi14jIP7y2fVhE9g9sf8Rrzx3evT4yrn1mi8iTnlyvisihKe5N0mN5dBeRZ71jvSgivb39RERu965np4isE5GjvG37icgC73v1oYj8WkTqPZNEpFicSbBDoKxURH4sIkcA9wIR7/5s97YXeN/9j0RkqziTY6cU1zdRRN7xvt/P+PJ721RELhOR94D3JPHvuEBEZnnfr0+85YK4+xqtn0yOloYpiKanCNgf6A1Mwt2Ded76IcA3wF0p9j8G2AB0B2YC94uINKLuX4HXgELgeuDfU5wzHRl/BPwHcACQD/wcQEQGAPd4xz/IO19PEqCqrwL/BL4bd9y/ess1wJXe9USA0cClKeTGk+FUT56Tgb5AvP/jn8B4oBtwBjBVRMZ420Z5/7upahdVLYs79v7Ak8Ad3rX9EXhSRArjrqFe2yShCOgIHAxcB9wH/BswDDgBuFZE+nh1LwfGACfi2vZLYHbgWE9513sA8Ab1zWIXAL8FvgVsBG5OIVdDxxoH3Ii7N2sC20/BtWE/YD/gB0CFt+1Or+w73jWMx7VT2qjqO8AUoMy7P928Tb/zzjkYOIy69qyHiJwN/BI4F+gBLAcWxlUbg/s9DfDW43/HvwKO9c43CBgB/Dqwf3z91oGq2ieHH6Ac+J63XAJUAR1T1B8MfBlYL8WZqAAmABsD2zoDChRlUhf3kK8GOge2/wX4S5rXlEjGXwfWLwWe9pavAxYFtu3jtcH3khz7JuDP3nJX3MO7d5K604HHAusKHOYtPwDc5C3/GfhdoF6/YN0Ex50F3O4tF3t1OwS2TwBe9pb/HXgtbv8yYEJDbZPgvCU45RsOXL8CxwTqrALGeMvvAKMD2w4E9gRlDWzr5h1rv0D7/Cmw/XTg3TTvf6JjBe9xF5wy74VT9n/HPTxDgTph73swIFA2GShN0MaJ7kEpsd/1lwPbxPveHBooiwAfJLmep4CLA+shYJf/vfPO/d24+xTzOwb+AZweWP9XoDzd331L/VgPounZpqq7/RUR6Swic7wu9k6cSaObBMwscWzxF1R1l7fYJcO6BwFfBMoANiUTOE0ZtwSWdwVkOih4bFX9J3VvkIn4K3Cu1z0/F3hDVT/05Ognzry1xZPjFtwba0PEyAB8GHd9x4jIC56pYwfujTSd4/rH/jCu7EPcG6tPsrZJRIWq+gEN33j/twa2fxPYvzfwmDiT23acwqgBvi0iYRH5nWd+2ol7UYHY60pLrjSPFbzHXwNfAAep6vO43uZs4DMRmSsi+3r75hHbdvHt1lh64F6IVgXa5mmvPBG9gf8K1P0Cp2SCssT/PmJ+x9T/HnzolSWr3yowBdH0xKfP/RnQH/eWuC91Jo1kZqNs8Cmwv4h0DpT1SlF/b2T8NHhs75yFySqr6tu4H9dpxJqXwJmq3gX6enL8sjEy4HpQQf4KLAF6qep+OJu2f9yG0h1/gnvABDkE+DgNufaWTcBpqtot8Omoqh/j2u5snDltP9xbODTue5XOsYL3uAvOnPIJgKreoarDcOaZfsBVwOe43k6w7ZK12z+9/8Hva1FgOf4efY5TpEcG2mU/DQSPxLEJF10YbMdOqroixTni1+O/B4d4ZcnqtwpMQTQ/XXFf5u2ePfs3uT6h90a+ErheRPJFJAKcmSMZ/wf4vogcL86hfAMNf+/+ClyBU0SPxMmxE/haRA4HpqYpw8PABBEZ4CmoePm74npUu0VkBO6B6LMNqMXZyROxFOgnIj8SkQ4i8kPcg/CJNGXbG+4Fbg44hHt49nRw11SJ6611xvW2Gks6xzo9cI9vBP6mqptE5Givh5aHe9DvBmq9XtLDnvxdvWv4Kc7UGYOqbsMpjn/zejMTgaBDfSvQ0zs3qlqL893cLiIHAIjIwSLyr0mu717gF+I53j3n+fkZtA84n8WvvXvQHWdaTRj00ZowBdH8zAI64d56/obrCjcF43B22Qqc3f8h3EMgEY2WUVXXA5fhHvqf4hypmxvYbSHOafm8qn4eKP857uH9Fe4B8FCaMjzlXcPzOGfs83FVLgVuEJGvcD/shwP77sI5b1/xTBDHxh27Avg+rpdVAcwAvh8nd674L1zP5/882f+Gc6QCLMD1xD4G3va2NZZ0jvVXnOL9AudQ/zevfF/cvfrSO0YF8Htv2+U4pfE+8LJ3jD8nkeESXM+jAjgSCL7dPw+sB7aIiN/uV+Pu9d88s9hzuF5wPVT1MeA2YJFX9y1cDzYTbsK9dK0F1uEc+VkdPNgciOdEMdo5IvIQzkmZ8x6MYRitA+tBtFO8rv+h4mLpT8XZmBc3t1yGYbQcbCR1+6UI+H84h/FmYKqqrm5ekQzDaEmYickwDMNIiJmYDMMwjIS0GRNT9+7dtbi4uLnFMAzDaFWsWrXqc1VNOIiwzSiI4uJiVq5c2dxiGIZhtCpEJD4TQBQzMRmGYRgJMQVhGIZhJMQUhGEYhpGQNuODMAyj6dizZw+bN29m9+5Wl6C03dKxY0d69uxJXl5e2vuYgjAMI2M2b95M165dKS4uJvl8VUZLQVWpqKhg8+bN9OnTp+EdPMzEZBhGxuzevZvCwkJTDq0EEaGwsDDjHl9OFYSInCoiG0Rko4hck6LeWG/e1+GBsn8RN//wenHz2HbMlZxlZXDrre6/YRjpYcqhddGY+5UzE5M329hs3DzAm4HXRWSJNyFMsF5XXO7/VwNlHXC51P9dVd/05vfdkws5y8pg9GioqoL8fFi2DCKRXJzJMAyjdZHLHsQI3JzI76tqFbAIlzE0nhtxudiDfZ9TgLWq+ia4nPuBaRizSmmpUw41Ne5/aWkuzmIYRjapqKhg8ODBDB48mKKiIg4++ODoelVVVcp9V65cyU9+8pMGzzFy5MisyFpaWoqI8Kc//SlatmbNGkSEP/zhD9Gy6upqevTowTXXxBpbSkpK6N+/f/T6zjvvvKzIlQ65VBAHEzuP62bi5psVkaG4aR6fjNu3H6Ai8oyIvCEiMxKdQEQmichKEVm5bdu2RglZUuJ6DuGw+19S0qjDGIbRhBQWFrJmzRrWrFnDlClTuPLKK6Pr+fn5VFdXJ913+PDh3HHHHQ2eY8WKFQ3WSZejjjqKhx+OzkPFwoULGTRoUEydZ599ln79+vHII48Qn0T1wQcfjF7f//zP/2RNroZoNie1iISAP+Jm4oqnA3A8btaz44FzRGR0fCVVnauqw1V1eI8eyeYjT00kArNmOTPTrFlmXjKMXFG2qYxbl99K2abcOPsmTJjAlClTOOaYY5gxYwavvfYakUiEIUOGMHLkSDZs2AC4N/rvf//7AFx//fVMnDiRkpISvvOd78Qoji5dukTrl5SUcN5553H44Yczbty46AN86dKlHH744QwbNoyf/OQn0ePG07t3b3bv3s3WrVtRVZ5++mlOOy120rqFCxdyxRVXcMghh1DWQhyiuQxz/ZjYieJ7EjsheVfgKKDUc54UAUtE5Cxcb+Mlf9pGEVkKDAWWZVvIsjKYPt2Zl5Yvh4EDTUkYRrYp21TG6AWjqaqpIj+cz7Lxy4j0yv4PbfPmzaxYsYJwOMzOnTtZvnw5HTp04LnnnuOXv/wljz76aL193n33XV544QW++uor+vfvz9SpU+uNFVi9ejXr16/noIMO4rjjjuOVV15h+PDhTJ48mZdeeok+ffpw4YUXppTtvPPO45FHHmHIkCEMHTqUgoKC6Lbdu3fz3HPPMWfOHLZv387ChQtjTFzjxo2jU6dOAJx88sn8/ve/r3f8XJDLHsTrQF8R6eNNJn4Bbv5cAFR1h6p2V9ViVS3GzXN7lqquBJ4BBopIZ89hfSJuLtysYz4Iw8g9peWlVNVUUaM1VNVUUVpempPznH/++YTDYQB27NjB+eefz1FHHcWVV17J+vXrE+5zxhlnUFBQQPfu3TnggAPYunVrvTojRoygZ8+ehEIhBg8eTHl5Oe+++y7f+c53ouMKGlIQP/jBD3jkkUdYuHBhvbpPPPEEJ510Ep06dWLs2LEsXryYmpo6t2vQxNRUygFyqCBUtRqYhnvYvwM8rKrrReQGr5eQat8vcean14E1wBsJ/BRZwfdBhEIgAoWFuTiLYbRvSopLyA/nE5Yw+eF8SopLcnKeffbZJ7p87bXXctJJJ/HWW2/x+OOPJx0DEHyTD4fDCf0X6dRpiKKiIvLy8nj22WcZPTrWYr5w4UKee+45iouLGTZsGBUVFTz//PMZnyPb5HQktaouBZbGlV2XpG5J3PpfcKGuOcX3QUyb5noR06ebmckwsk2kV4Rl45dRWl5KSXFJTsxL8ezYsYODD3ZxMQ888EDWj9+/f3/ef/99ysvLKS4u5qGHHmpwnxtuuIHPPvss2ssBoqawTZs2RRXRvHnzWLhwISeffHLW5c4ES7UBVFRAba37+GYmUxCGkV0ivSJNohh8ZsyYwUUXXcRNN93EGWeckfXjd+rUibvvvptTTz2VffbZh6OPPrrBfRKFzj722GN897vfjemlnH322cyYMYPKykog1gfRvXt3nnvuuSxdRWrazJzUw4cP18ZOGGSD5QwjM9555x2OOOKI5haj2fn666/p0qULqspll11G3759ufLKK5tbrKQkum8iskpVhyeqbz0InDJYtgwWLGhuSQzDaE3cd999zJ8/n6qqKoYMGcLkyZObW6SsYgoiwPz5rhcxf771IgzDaJgrr7yyRfcY9hbL5uph4a6GYRixmILwKClx6TZE3H9LuWEYRnvHFEQAPxuuZTE2DMMwBRGltBSqq0HV/TcTk2EY7R1TEB6W1dUwWg8nnXQSzzzzTEzZrFmzmDp1atJ9SkpK8EPhTz/9dLZv316vzvXXXx+TgjsRixcv5u236zL/XHfddVkZl9AS04KbgvDwQ10vuQQuuqi5pTEMIxUXXnghixYtiilbtGhRg/mQfJYuXUq3bt0ade54BXHDDTfwve99r1HHiqelpQU3BRHH/Plw331u4FwLybhrGG2CbE7te9555/Hkk09GJwcqLy/nk08+4YQTTmDq1KkMHz6cI488kt/85jcJ9y8uLubzzz8H4Oabb6Zfv34cf/zx0ZTg4MY4HH300QwaNIixY8eya9cuVqxYwZIlS7jqqqsYPHgw//jHP5gwYUL0Ybxs2TKGDBnCwIEDmThxYnQkdHFxMb/5zW8YOnQoAwcO5N13300oV0tLC24KIoCFuhpGbvCzFVx7bXZevvbff39GjBjBU089Bbjeww9+8ANEhJtvvpmVK1eydu1aXnzxRdauXZv0OKtWrWLRokWsWbOGpUuX8vrrr0e3nXvuubz++uu8+eabHHHEEdx///2MHDmSs846i9///vesWbOGQw89NFp/9+7dTJgwgYceeoh169ZRXV3NPffcE93evXt33njjDaZOnZrSjOWnBV+xYkXStOBnnnkmF154IQsXLozZd9y4cVET01VXXZV+gybBFEQAy+xqGLkhFy9fQTNT0Lz08MMPM3ToUIYMGcL69etjzEHxLF++nHPOOYfOnTuz7777ctZZdYmm33rrLU444QQGDhzIgw8+mDRduM+GDRvo06cP/fr1A+Ciiy7ipZdeim4/99xzARg2bBjl5eVJj9OS0oKbggjgZ3YNh13ivunTzcxkGNkgF0EgZ599NsuWLeONN95g165dDBs2jA8++IA//OEPLFu2jLVr13LGGWckTfPdEBMmTOCuu+5i3bp1/OY3v2n0cXz8nkBD6cJbUlpwUxBxJMrsahjG3uEHgdx4Y/bS2HTp0oWTTjqJiRMnRt+0d+7cyT777MN+++3H1q1boyaoZIwaNYrFixfzzTff8NVXX/H4449Ht3311VcceOCB7NmzhwcffDBa3rVrV7766qt6x+rfvz/l5eVs3LgRgP/+7//mxBNPbNS13XDDDdx2220J04J/9NFHlJeXU15ezuzZs+uZmbKJ5WKKw3/T8TO7WrirYWSHSCT7+c0uvPBCzjnnnKipadCgQQwZMoTDDz+cXr16cdxxx6Xcf+jQofzwhz9k0KBBHHDAATEpu2+88UaOOeYYevTowTHHHBNVChdccAGXXHIJd9xxR0ykUMeOHZk3bx7nn38+1dXVHH300UyZMqVR19VS0oJbuu8ElJW5nkNJiSXsM4xEWLrv1kmm6b7NxGQYhmEkxExMcdjkQYZhGA7rQcQRDMfbvdsmETKMZLQV83R7oTH3yxREHH7ab3CJ++bNs1BXw4inY8eOVFRUmJJoJagqFRUVdOzYMaP9zMQURyQCEyfCnDlOQVRVuV6EmZkMo46ePXuyefNmtm3b1tyiGGnSsWNHevbsmdE+FsWUgLIy15Pw0rxQUAAvvGBKwjCMtodFMWWI34vwJw6y+SEMw2iPmIJIwvjx0LGjzQ9hGEb7xXwQSfDzMj36KIwda+YlwzDaH6YgklBW5pL1VVXB8uUwcKApCcMw2hc5NTGJyKkiskFENorINSnqjRURFZHhceWHiMjXIvLzXMqZCJsbwjCM9k7OFISIhIHZwGnAAOBCERmQoF5X4Arg1QSH+SOQOh1jjrC5IQzDaO/ksgcxAtioqu+rahWwCDg7Qb0bgduAmGTrIjIG+ABIPUtHjrC5IQzDaO/kUkEcDGwKrG/2yqKIyFCgl6o+GVfeBbga+G2qE4jIJBFZKSIr92bATtmmMm5dfitlm2I1gM0NYRhGe6bZnNQiEsKZkCYk2Hw9cLuqfi3+YIQEqOpcYC64gXKNkaNsUxmjF4ymqqaK/HA+y8YvI9LLeaN9M1NlpZmZDMNof+SyB/Ex0Cuw3tMr8+kKHAWUikg5cCywxHNUHwPM9MqnA78UkWm5ELK0vJSqmipqtIaqmipKy0uj28zMZBhGeyaXPYjXgb4i0genGC4AfuRvVNUdQHd/XURKgZ+r6krghED59cDXqnpXLoQsKS4hP5wf7UGUFJfEbE9kZrJwV8Mw2gM5UxCqWu299T8DhIE/q+p6EbkBWKmqS3J17kyI9Iow69RZPPr2o4wdMDZqXvKxKUgNw2ivtPtkfal8ED5z59aNqJ40KVsSG4ZhND+WrC8FqXwQUDeietky80EYhtG+aPcKwvdBhAghIhR2jg1VshnmDMNor7R7BeH7IMKhMLVay/Snp8eMh7AZ5gzDaK+0ewUBULGrglqtpVZrE4a62twQhmG0R0xB0LCZyZ8bwvIyGYbRnjAFQcNmJhswZxhGe8QUhEcqMxNYXibDMNofpiA8fDNTWMIJR1T7zmoR998GzBmG0daxGeU8GhpRDXWO6hT5Aw3DMNoMpiA8yjaVMf3p6VTVVLH8o+UMPGBgjJIoLXURTKp1kUyWk8kwjLaMmZg8GhpRbTPMGYbR3jAF4dFQqKtFMhmG0d4wBeHRUKgrWCSTYRjtC1MQARoKdfXNTOGwpf42DKPtY07qAL6ZqbK6MqmZadkyS9hnGEb7wHoQAdIxMwHMnw/33QejR5sfwjCMtospiDgaMjOVlkJlpUv/XVlpfgjDMNoupiDiKCkuIRwKIwjhULjeiOrCQuekBvffwl0Nw2irmIJIgCAx/4NUVLixEOD+V1Q0pWSGYRhNhymIOErLS6murUZRqmurE0YyFRS4SKaCAotkMgyj7WJRTHH4kUxVNVUJk/ZZJJNhGO0FUxBxRHpFWDZ+GQveTK0B5s93g+Xmz3cKw/IyGYbR1jATUxLmvzmf+964j9ELRtcLdS0tdcqhpsZGVBuG0XYxBZGAYOK+3dW76/UmLHGfYRjtAVMQCfBDXQEUZd6aeQmnIBVxvYjLL7cBc4ZhtD1MQSQg0ivCxMETo2GuiaKZVq92ykHVmZnMaW0YRlsjpwpCRE4VkQ0islFErklRb6yIqIgM99ZPFpFVIrLO+//dXMqZiPGDxpMXzks6YM4wDKOtkzMFISJhYDZwGjAAuFBEBiSo1xW4Ang1UPw5cKaqDgQuAv47V3KmItWAufHj3TgIcGMihgxpSskMwzByTy57ECOAjar6vqpWAYuAsxPUuxG4DdjtF6jqalX9xFtdD3QSkYIcylqPhgbMRSJwxx2Ql+dSblx2Gcyd25QSGoZh5JZcKoiDgU2B9c1eWRQRGQr0UtUnUxxnLPCGqlbGbxCRSSKyUkRWbtu2LRsyR2lohjlwaTZ8P0R1NUybZs5qwzDaDs3mpBaREPBH4Gcp6hyJ611MTrRdVeeq6nBVHd6jR4+sypdO6u+Skrq8TOCUhY2JMAyjrZBLBfEx0Cuw3tMr8+kKHAWUikg5cCywJOCo7gk8BoxX1X/kUM6kNJT6OxKB2bOdmSkUstxMhmG0LXKZauN1oK+I9MEphguAH/kbVXUH0N1fF5FS4OequlJEugFPAteo6is5lDElDc0wBzBpkvv/6KMwdqyl3DAMo+2Qsx6EqlYD04BngHeAh1V1vYjcICJnNbD7NOAw4DoRWeN9DsiVrMlIx8xUVgbTp7t8TNOnmw/CMIy2Q06T9anqUmBpXNl1SeqWBJZvAm7KpWzpksjMFOlV101IlJfJehGGYbQFbCR1AzQ0w5zlZTIMo61iCiINUg2Y8/MyhcNuPISZmQzDaCuYgmiAhgbMgRsPUVvrPrt3W14mwzDaBqYgGiCdAXMlJa4HAW7Q3Lx51oswDKP1YwqiAdKJZIpEYOJE54MAN6raBswZhtHaMQWRBg0NmAOXvK9jR3NWG4bRdjAFkQbpmJnMWW0YRlvDFEQapGNmgrrkfbW1UFlpZibDMFo3piDSJB0zU2GhUw7g/puZyTCM1owpiDRpaMAcuB6En91VxE1LahiG0VoxBZEBqQbMgQt37eAlL1GF++6zSYQMw2i9mIJIk3QGzPnhrj41NTaJkGEYrRdTEGmSTiQTuHDXDoEUiDaJkGEYrZWUCkJE9k2x7ZDsi9NySTeSyZ9EKBx2fogOHWwSIcMwWicN9SBK/QURWRa3bXHWpWnhpBPJBDBwYF0vQhK7KwzDMFo8DSmI4ONt/xTb2gXpmplKS126DVU3R4Ql7zMMozXSkILQJMuJ1ts86ZqZLHmfYRhtgYYUxAEi8lMR+Vlg2V/v0QTytTgqdlVQU1tDrdZSWV2ZMprJkvcZhtGaaUhB3Ad0BboElv31P+VWtJZJYedCanHDpWupTRnNZMn7DMNozaSck1pVf5tsm4gcnX1xWj4VuyoISYharSUkISp2VSSs5yfvmzbNhbpOn+6c1zZftWEYrYWMxkGIyAARuVFENgL35EimFk1JcQkF4QJChAhJKGkPAmJnmquqMjOTYRitiwYVhIgUi8gvRGQt8N/AVOB7qjo859K1QNJ1VEOds1rE/bfxEIZhtCYaGihXBjyJM0WNVdVhwFeqWt4EsrVYguMhdlfvZsGbyeNYRVwkU00NrFvXhEIahmHsJQ31ILbinNLfpi5qqd2Ft8bjZ3YFUJR5a+Yl7EWUlsKePW7Z8jIZhtHaSKkgVHUMMBBYBVwvIh8A3xKREU0hXEsl0ivCxMETo1ldkyXvKympS/8NLtzVBs0ZhtFaaNAHoao7VHWeqp4CHAtcB9wuIptyLl0LZvyg8eSF81LODxHMywQ2aM4wjNZFRlFMqrpVVe9U1eOA43MkU6uhofkhACZNgksuqVvfs8eimQzDaB005KRekuwD3NnQwUXkVBHZICIbReSaFPXGioiKyPBA2S+8/TaIyL9mdFVNQHB+iKqaqpSO6iFD6pZra+G116wXYRhGyyflQDkgAmwCFgKvkkGCPhEJA7OBk4HNwOsiskRV346r1xW4wju+XzYAuAA4EjgIeE5E+qlqTbrnzzW+o7qmpibqqB4/aDyRXvVHwvlTkfrzVS9eDEuXup6EDZwzDKOl0pCJqQj4JXAU8F+4h/3nqvqiqr7YwL4jgI2q+r6qVgGLgLMT1LsRuA3YHSg7G1ikqpWq+gGw0TteiyHeUZ2qFxGcitTHsrwahtHSaSiKqUZVn1bVi3AO6o1AqYhMS+PYB+N6Hz6bvbIoIjIU6KWqT2a6r7f/JBFZKSIrt23bloZI2cV3VEPqcNf4qUgNwzBaA+mMpC4QkXOBvwCXAXcAj+3tiUUkBPwR+Fljj6Gqc1V1uKoO79Gj6ZPL+r0Inz01e5JOIjR+POTn163n5bkywzCMlkpKH4SILMCZl5YCv1XVtzI49sdAr8B6T6/Mp6t37FJxebGLgCUiclYa+7YYhhxY54FOld01EnE+hwULYMsWKCpqIgENwzAaSUNO6n8D/olzIv9E6ubPFEBVNUdT+BAAACAASURBVOmc1cDrQF8R6YN7uF8A/MjfqKo7gO7RA4qUAj9X1ZUi8g3wVxH5I85J3Rd4LYPrajLSze4KdQ7p0aOdD2L+fFi2zBzVhmG0TBpK953ROIm4fas9X8UzQBj4s6quF5EbgJWquiTFvutF5GHgbaAauKwlRTAF8bO7VlZXNpjdFVwvoqrKpd7wM7yagjAMoyXSaAWQDqq6VFX7qeqhqnqzV3ZdIuWgqiWqujKwfrO3X39VfSqXcu4NmWR3BRfRlJ9fl8Rv+/amk9UwDCMTcqog2gvB7K5VNVVJHdXgeguXX+6UQ20tzJwJc+c2nayGYRjpYgoiC5QUl5AfzidECBFp0My0Zk3s+o032shqwzBaHqYgskCmZqaxY2PXN2+Gk04yJWEYRsvCFESWyGQSoUmTYMyY2DKbktQwjJaGKYgske4kQj4zZsQOnOvQwaYkNQyjZWEKIktkMqoanLP6zjvr5qwO2Z0wDKOFYY+lLJLuqGqfCm9Mnaqbbc5MTIZhtCRMQWQRf1Q1uEmEVn+6OmV9f0xEKOR6EYWp9YlhGEaTYgoii5QUl9Ah5Aanp+OHiERg1ixnZqqthenT3ZiIW2+1iCbDMJofUxBZJJM5InwqKpxyqK2Fb76BSy+Fa691+ZpMSRiG0ZyYgsgy6c4R4VNS4noQPjU17lNZaT4JwzCaF1MQWaYx0UyJJhOqrTWfhGEYzYspiByQaTTTkCH1y0KhuignwzCM5sAURA7INJqposJFMQUJh23gnGEYzYspiByQaTRTSYmbgtQnHIa77rJ5IgzDaF5MQeSA+Gim6trqBv0QpaUwZYr7LF/u8jUZhmE0J6YgcsT4QePp2KFj2inAIxG45x73AZg61X0s1NUwjObCFESOyDQFuE9ZmTM53Xuv+4wcCSeeaIrCMIymxxREDqnYVUFNbQ21WktldWVKM5NPaSns2RNb9tJLcPzxNvOcYRhNiymIHFLYuZBaaoH0wl2hvsPap7YWpk2znoRhGE2HKYgckmm4K9Q5rEeNqr/NMr4ahtGUmILIIZmGu/pEIvDiizBnDvTuXVeuaqOrDcNoOkxB5JDGJO8LMmkSTJ5cN5mQCKxuuBNiGIaRFUxB5JhMk/fFU1LipiMF14OYN8/8EIZhNA2mIHJMpsn76u3vJfPzU3FUVrp5I0xJGIaRa0xBNAHxyfu2V27PaP/x42Mjm157DY47Dq6+OlsSGoZh1McURBNQsasi6ocAuL3s9ozMTJEInH56bJkqzJxpYyMMw8gdOVUQInKqiGwQkY0ick2C7VNEZJ2IrBGRl0VkgFeeJyLzvW3viMgvcilnrikpLiEcqpsVqLq2OiNnNUBRUeLy++/fG8kMwzCSkzMFISJhYDZwGjAAuNBXAAH+qqoDVXUwMBP4o1d+PlCgqgOBYcBkESnOlay5JtIrwuzTZxMWpyQU5f7V92fUixg/HgoK6pevXm3+CMMwckMuexAjgI2q+r6qVgGLgLODFVR1Z2B1H0D9TcA+ItIB6ARUAcG6rY5JwyZxZr8zo+t7avdk1IuIROCFF+CWW2IH0VVXO1PTrbc6c9Ott5rCMAwjO3TI4bEPBjYF1jcDx8RXEpHLgJ8C+cB3veL/wSmTT4HOwJWq+kWCfScBkwAOOeSQbMqeE4q6xNqJtny9JaP9IxH3KSlxn6oq54tYvNh9wEU7hcMwe7alDDcMY+9odie1qs5W1UOBq4Ffe8UjgBrgIKAP8DMR+U6Cfeeq6nBVHd6jR48mk7mxjB80nrxQXTjSUxufysjM5JNsHmtwCqO62vI2GYax9+RSQXwM9Aqs9/TKkrEIGOMt/wh4WlX3qOpnwCvA8JxI2YREekW4eMjFjR5ZHWT8eNdTSEZNjeVtMgxj78ilgngd6CsifUQkH7gAWBKsICJ9A6tnAO95yx/hmZtEZB/gWODdHMraZMSPrM7UWR0k1MDdKyx0vQjzSxiG0RhypiBUtRqYBjwDvAM8rKrrReQGETnLqzZNRNaLyBqcH+Iir3w20EVE1uMUzTxVXZsrWZuSSK8Ipx9WN6hhT+0eZr4yM+PjlJa6FODJUIXLL4eTToJrr4XRo01JGIaRGbl0UqOqS4GlcWXXBZavSLLf17hQ1zZJvLP68b8/TtmmMiK9Imkfo6QE8vOdozocds5pf6Kh2lqnIKqq6upXVdWZnEpL3f6R9E9nGEY7JKcKwkjM+EHjue+N+6jRGgBqtZYFby7ISEFEIrBsWd3DHtzy9u0u7DWeDh2cyWn06DqlMnGi82WYojAMIxHNHsXUHon0inD3GXfHDJzLNMsruAf7L35RF/76i19At26JfROHHuoG1VVWOgd2VZWbb8JMT4ZhJMMURDMxadgkLhl6SXQ90yyvyQimBw/y9ttw332xfgtVpzAs2skwjESYgmhG9jbLayJSjZGoqalfVlsLTz8N55wDU6dab8IwjDpMQTQje5vlNRnjx0OnTs7UJNJw/ZdeciOx770XTjzRFIVhGA5TEM1INrK8JsJ3YN90k3vopxpQF8+ePbG+CRtHkXusjY2Wiqhqw7VaAcOHD9eVK1c2txgZM3fVXC598tJoRFNeKI8XJ7yYUURTg+eYC5demtjElIxQyOVymj/fObTz853SiUTcgyw+esrCZhtHWVldZFmwjbN5fLs/RipEZJWqJsxUYWGuzcykYZN46r2nWLzBZdvzs7xmU0FMmgQDB8ICr3MyZIiLaLrvvuRKw/dNVFa65d276/YPhsqKuNxPuXi47Q2t5cFYWura0o8sKy1NX96GrjHXyiddOQxHa2wnUxAtgPiBcy99+FLGA+cawg+FDTJkiEvqV13tIpriKS+vW1Z1CmXLlroHmh8RpeoUyMyZMGJE3Q8gkx9ENn88TfVg9M+1N3IHBzzm59f1ytI5b6JrLCurU+RQd68qK+H6690n2z2UdNs6Vz3PbH13cvkA99upstL1zltLtmVTEC2A8YPGc//q+9lT64ZCv/3525z4wIlZNzXFE+xZzJtXlz48GTU1zpntz48tUjdq2087/r//67bfeSdMn574wTF3Ljz6KIwd62QI/nhE4MwzYcaMxv9IM30rj39w+Q/YZIMI/Yfwli3w5JPObxMOw913p/+jD55z2bLYh3qyekFZSkvrenfBUGU/DTy4+xAK1SnzZ5915zruONh/fzdLYWMHSvpyffRRem1dVubSvlRVuTDsUKiu5zlrluvRgntpqahI/yGdSEH57VNYmP6x5s51L0s1NW5irmy/VATvV22tO9fAgXXnaLG9C1VtE59hw4Zpa2bK41OU64n5THl8SpOdf8UK1VtuUZ0xQ1XEf+Qn/wwerBoOJ98+YIBqKOSWRVTHjKk7frDejBmqI0bU37+gwMmULnPmqJ5yivu/YoVqp07u/OGwO3fwWPF1CwqcjHl57pNKhhUrVPPzE19zOJyezL584bD7P2dO7Lp/jPh6K1bE3qfguefMceXx9y7VPUp0jf7xU11HUK78fHeMeNnjmTIl9ry+nCL1ZQ6F6tolmSy+nFOm1F2jf6+D1yyi2qGDO1bwvscfq0OH2H2mTHHHj5dhxQq3bcoUt5zsmPFyzpkTe45QyJUn+z74+wTPlck9ygRgpSZ5rpqTuoVQtqmME+adEHVWA4zpP4bHLnisyWWZO9eFuqZKBpgtRBL3WkTg5pvdG1XwjR7qUoqsWeN6IQCTJ9ftO2OGGzkedMyHw3DBBfDyy/Dhh3V1DzwQPv00uWy+DP4b6aOPujfxZD+bMWNizWxB/J7HSy+5gYvg3qSHD4fXX687Zu/e8O1vQ8eOsHy5KxeBs8+GZ55xb6JQd39CIRexVlgYe81+iHNDP/FTTnGmp3Xr0nuLnjrVRbqpunY980z45BM46CA47TTXG9gSmAurqMit+5Na+TI39P3yvxsdOsSaZPy3/epqdxz/WOGwK0t03Pjv2YwZLuuAf29/9avY7Xl5dT2vUMi1x6xZ7rx+zrP4axgzxl2/32uBWLPSD38IDz9c176zZrm6r73met6qdaHp8b7BggI3o6R/zN27Xb2f/xxuuy11OzZEKid1s7/5Z+vT2nsQqqpzVs7R0PWhaA8i/NuwzlmZ5NUkx/hvSmPG1H8ra4pPXp57S45/G/R7JcFPt271ywYNyo4MY8bU9TDS3c+Xc8CAujfLOXOSt2G6bZvo2kMh9xY/alTscUKhOplDIff2muoagvX9T8+esW/G/nci2MuK73WlapNUvYZ0PmPG1H8T92UfMcK1QSb3yO9dzJhRvweRqFeTqKeb7Nj5+a5+8Dh5eXW9Av97leieprr+RDKMGrV3vQlS9CASFrbGT1tQEKr1TU15N+Tpio+y1JfcC1ascA+8vX3opvvDTfTAaspPcXH2lGL8wzubnyOOaPgBfcQR7uHS2Ps3apTbP9F59t23ae9LLr4TIqrjxrnr85VponqHHbb35xkxIj2FmuknFEpu5mqIVArCTEwtjLJNZYx6YBTVtdXRsuYyNcXjOxp9E4dIXd4nv9udzGSUDuGw2zfbpq29kamlM2BAnbkqXVpie4jUBT00B36QgW/yCZrDWgvhsDNJZurkTmVispHULYxIrwizT59NKHBrFm9YzNXPXd2MUjkiEWcHveUWZ4O++WZ48UX3mTLFfc4+O3afUaNcdEkQ324cZMwY9wNNlGhwb8jLgxNOiC0bMMCdL5vnyMtruF4Q/4EYpKEZAv39gvXfTTDPYkPpVU4+2dngMxlhnyv8l4x773X+oREj0ksPk4pQKPP7UVPjfCeFhfD445nt2xztmOi7Ulubg8SbyboWre3TVkxMPiPmjogxNcn10mz+iEzwo3x8O2ww8iYYERKMaglGdMRHuyTrqofD9W3uc+Y4e7JvnvLty8GoIz9qJxjplKheOp8jjoiNZknHjBQK1UWmBO3PoVBs1Feq/X1zSKJ6Y8a4qJpkxwhGLU2ZUr+ebwYpLs7czBH8BKPY/E847NrZv/5EUUqJInrGjEl8PUcckdg/FLT1x5vVUtn8479ToNq7d/19CgrcdZxySuKoqWybjxK1rf+dC8qWaeSfD+aDaH3MWTmnXthr+LfhFuGPaIhMQyUThXYGnYh+OGXwoRIMb/TDEhOd/5Zb0qsXXE/2QPIdskHl55NuiKmvDBMpqPjQUT9MN16RnHJK4mOnUn7JwiX9ev6xg+G0/n1Ipaz8T9AJ7ivB4PXEnzvT70+8As7Li335CN6zcDg2hNRXIgUFsaGjc+ak9sv45xgzJrZ8zJjk3+M5c+o7p+OXfWdzqrbNy0vuxA4qgviw28aQSkGYD6IFc/VzV9ebr3rKsCnc8/17mkmi7JJscFA6I27THcHbmFHVwX3CYTj99LpBZankCQ5SKyiAO+5wZou334ZXXnE/72D4aPz1J7tuP/y0utrJ89OfuoGIieTLdOCVXy/RoLLgtuBANn852B6FhfUHRiZrq8YSHCUeP8Av1X1O1RZTpzrzlo/vBwuOdo6vM2UK3BP4CSa6j0FZLr8cbr89NnwY6s/umKpt77+/LhQ6HIYbb3QThGWDVD4IUxAtnHMeOofF79Z5zFqKw7olkOlDMJMHVWP3SfUA25uHZfxIXz+GviWNvG3u0cCNvWclJS7Iws8AkEhRBuukky8rlfJvzOjpXKaPMQXRiinbVMaJD5wYTcMRljB3n3E3k4a1gkQuRta49Va49lqnILL9BtneSedB3dzKL5cymIJo5Ux9Yir3rqrr44YlzPL/WJ7TPE1Gy6IpExAa7QsLc23ljB80nrDUxdLVaA1jFo1h7qq5zSiV0ZT4k0DdeKMpB6PpsGyurYBIrwhn9j8zxhfx2a7PmPyES0Bk5qb2QaKU7YaRS6wH0UqYMXJGTC/C5/437m8GaQzDaA+YgmglRHpFuPuMu2NGWAOs/HSlmZoMw8gJOVUQInKqiGwQkY0ick2C7VNEZJ2IrBGRl0VkQGDbv4hImYis9+p0zKWsrYFJwybx8sSXGdA92kzUai1TnphiSsIwjKyTMwUhImFgNnAaMAC4MKgAPP6qqgNVdTAwE/ijt28H4C/AFFU9EigB9uRK1tZEpFeEP531pxhzk6JMfmJyi8jXZBhG2yGXPYgRwEZVfV9Vq4BFQEwqN1XdGVjdB/Bjbk8B1qrqm169ClWNm0Kj/eI7reOZ+cpMUxKGYWSNXCqIg4FNgfXNXlkMInKZiPwD14P4iVfcD1AReUZE3hCRGTmUs1UyY+QM8kL1U1bOfGUmJz5wImWbyppBKsMw2hLN7qRW1dmqeihwNfBrr7gDcDwwzvt/joiMjt9XRCaJyEoRWblt27Ymk7klEOkV4cUJLzLqkFH1tr304UscP+9480sYhrFX5FJBfAz0Cqz39MqSsQjws/RvBl5S1c9VdRewFBgav4OqzlXV4ao6vEePHlkSu/UQ6RXhxf94kRnH1e9g1Wot05ZOs56EYRiNJpcK4nWgr4j0EZF84AJgSbCCiPQNrJ4BvOctPwMMFJHOnsP6RCDDebPaD7d977aESmJP7R5mvjKTW5ffaorCMIyMydlIalWtFpFpuId9GPizqq4XkRtw+ceXANNE5Hu4CKUvgYu8fb8UkT/ilIwCS1X1yVzJ2ha47Xu3cei3DuWW5bfw4Y4Po+WLNyxm8YbFdAh1YPbps23UtWEYaWPJ+toYty6/lV89/yuU+vfVkvwZhhGPJetrR5QUl5AXTjwhb43WcO5D5zJg9gDOeegcMzsZhpESUxBtjEivCKUXlSaMbgLY8s8tvPP5Oyx+dzEj/zzSQmINw0iKKYg2iB/dNOf7cxhx0AhCkvw2W0isYRjJMAXRhpk0bBKvXvIq95xxT8JMsD61WsvkJyZz5N1HmqIwDCOKOanbCWWbyljw5gLe3vY2f6/4O1v+uSVp3aIuRRzb81hmjHShs6XlpZQUl5hz2zDaIDblqFGPuavm8uvnf822XalHoAuCojYXtmG0UUxBGAkp21TGiQ+cyJ7a9BPljuo9inEDx1Gxq4KS4hIAFry5AHBTo1ovwzBaF6YgjKT4pqe/bf4ba7auyXh/v4cBECJE/+796d+9PzNGzmDdZ+t49O1HGTtgrPU8UuDfA8hMyZZtKmvQ/NfYY7dH0mnP1nCOTDEFYaRF2aYyZr4yk79t/ltKH0VjmHHcDMb0H0NpeSnbK7ez5tM1MYoj6CPZXb2bi4denJFSif/h+cfb8rW7jqIuRWk/INN98JaWl1LYuZDVn64GGvdwL+xcyOVPXU5VTRUABeECXrjohehxEj3g/bJ5a+axp2YPoVAoOko+/rqffO/JaA8xL5THGX3PAOCLb75oVDsnaiNIrxcZvJYhBw6J6YXOfGUmGyo20GOfHgzoPoDxg8YD6fm/4mWZ+cpMPvnqE0r6lLBz986oXKmOV7apjJL5Jeyp2UNeOI/Si0qz/gAv21TG6AWjqaqpIj+cz7Lxy2Luc3MpDlMQRsbMXTWX+9+4n455Hdm5e2ejehfxBHsbPkVdiuia35WNX2yst21w0WCOPfhYhhw4hNWfrmbL11v44psv2LZrGwUdCsgP5VPSp4S/f/53lvx9CbVaiyD07tabTTs2URM3hYj/gPSP0WOfHuzfcf/oev/u/elX2I//XPGf1Gpt9EEBRB/mFbsq2F65nf9c8Z/1ji8IVx13FWP6j4k+pOIfwL4SXvL3JagqIQlRq7Ux1957v950zutMQYcC1m5ZSy21gFMeVxx7RcJzhyXMcb2OY/lHyxOOok9FsJ2feu+peg/qRA/T0QtGU1ldiYgAROXxZSz9oJSOeR3Zv+P+0f0e//vj9eROhf99EYTD9j+Mb3X8FhcPvZiBBwyMKpSCDgWs27qOWq0lJCFUNdpeQUKECIVCVNdWEyLE8b2PZ/+O+0dfHBa8uYB7V90brT/qkFEM6DGg3gsGEKOAg9/HyurKeu0WVIpb/rmF/333f6PXNHnYZO75/j0x7RkKhfjhkT9k2z+3MfjAwUkV3LrP1nH/G/dz0L4HMWPkjL1SKqYgjL1m7qq59fI8tQe6FXRjR+WOjB+6QYq6FNGvsB8ojXqANyeCMKhoEJXVldGH4Pbd2/n060+bW7SsIQhFXYoavKawhOsp81QUdSli69dbU9Yv7lZMZXVlWu3pj2cSpJ6iHdV7FL8b/btGKQpTEEbWmLtqLo++/Sg99unBexXvcdC+B9GvsB+3l92ekbPbMIzskhfK48UJL2asJFIpiJxlczXaJpOGTUpos/b9C4WdC5m2dFpUWYQIJezyJ6Nn155s/mpz1uQ1Wg/7d3LmPqNx7KndQ2l5dn0npiCMrBDpFYl+MQceMDDGaenbS32bdFGXohh7t+9P8O31c1fN5dInL01or/Z9DHmhvBi/hSCc0PsEdu7eyZtb34yW+9OyxvduiroU8dk/P4v6Lb7V6VsZP5wGFw2meL9iiroU8VXVVzy47sGM9k/kk/HLv93l21H5MpXp2IOPZd+O+9bzVQhCOBTmx0N+HL0vido5Ww/qRNcXIkQ4FKamtgYRYeC3B8bc+/hghb6FfXmv4j2+3P0l733xXsyxiroUUdSliHVb19W7hnEDx/Hw+oeprq2OkUFwPpNkZh/x/tJ9qfG/j906duPLb77kox0fNZsJMS+UF3XUZwszMRktEj+qw494GnzgYLoVdIuJ8kgWwhlfDnWOxWA0U3zkiG8+G3zgYO589U52V+8GYFDRoKgi2LfjvvUisHx8xeY7uO887U6eeu8pVm9ZHX1whAhx1uFnRUepB+UKRvYE5Yt3iueF8rjr9Lui0VPJZEoWNRR8w4xvZ/8YwWioeEdsMEBg5+6dUfn37bgvpR+URh2nQMJIL78804gd38Ef7/xPdQ2pzl/YuZCn3nuKJRuWUEttdDBo8AXHf5FZvGFxVI74sUDx7ekruPjAh+D9CwZL+O3Zt7AvD69/mBqtoUOoA6cfdnq0XR/f8Djvfv5uVPn4sgJ77aw2H4RhZEhjww6T7be3YYw2niF3pHNv/JeHxo7pSff+pZIlV98BUxCGYRhGQmzCIMMwDCNjTEEYhmEYCTEFYRiGYSTEFIRhGIaREFMQhmEYRkJMQRiGYRgJaTNhriKyDWjtmeS6A583txAtCGuPOqwtYrH2iGVv2qO3qvZItKHNKIi2gIisTBaP3B6x9qjD2iIWa49YctUeZmIyDMMwEmIKwjAMw0iIKYiWxdzmFqCFYe1Rh7VFLNYeseSkPcwHYRiGYSTEehCGYRhGQkxBGIZhGAkxBdFEiMifReQzEXkrULa/iDwrIu95/7/llYuI3CEiG0VkrYgMbT7Jc4OI9BKRF0TkbRFZLyJXeOXtsk1EpKOIvCYib3rt8VuvvI+IvOpd90Miku+VF3jrG73txc0pfy4QkbCIrBaRJ7z19twW5SKyTkTWiMhKryznvxVTEE3HA8CpcWXXAMtUtS+wzFsHOA3o630mAfc0kYxNSTXwM1UdABwLXCYiA2i/bVIJfFdVBwGDgVNF5FjgNuB2VT0M+BK42Kt/MfClV367V6+tcQXwTmC9PbcFwEmqOjgw3iH3vxVVtU8TfYBi4K3A+gbgQG/5QGCDtzwHuDBRvbb6Af4XONnaRAE6A28Ax+BGx3bwyiPAM97yM0DEW+7g1ZPmlj2LbdDTe+h9F3gCkPbaFt51lQPd48py/luxHkTz8m1V/dRb3gJ821s+GNgUqLfZK2uTeCaBIcCrtOM28Uwqa4DPgGeBfwDbVbXaqxK85mh7eNt3AIVNK3FOmQXMAGq99ULab1sAKPB/IrJKRPw5T3P+W+nQmJ2M7KOqKiLtLuZYRLoAjwLTVXWniES3tbc2UdUaYLCIdAMeAw5vZpGaBRH5PvCZqq4SkZLmlqeFcLyqfiwiBwDPisi7wY25+q1YD6J52SoiBwJ4/z/zyj8GegXq9fTK2hQikodTDg+q6v/zitt1mwCo6nbgBZwZpZuI+C9ywWuOtoe3fT+goolFzRXHAWeJSDmwCGdm+i/aZ1sAoKofe/8/w708jKAJfiumIJqXJcBF3vJFODu8Xz7ei0Y4FtgR6Eq2CcR1Fe4H3lHVPwY2tcs2EZEeXs8BEemE88e8g1MU53nV4tvDb6fzgOfVMzi3dlT1F6raU1WLgQtw1zaOdtgWACKyj4h09ZeBU4C3aIrfSnM7X9rLB1gIfArswdkEL8bZSZcB7wHPAft7dQWYjbNBrwOGN7f8OWiP43F21bXAGu9zenttE+BfgNVee7wFXOeVfwd4DdgIPAIUeOUdvfWN3vbvNPc15KhdSoAn2nNbeNf9pvdZD/zKK8/5b8VSbRiGYRgJMROTYRiGkRBTEIZhGEZCTEEYhmEYCTEFYRiGYSTEFIRhGIaREFMQhtEAIlLjZdH0P9c0vFfaxy6WQIZfw2hJWKoNw2iYb1R1cHMLYRhNjfUgDKOReDn6Z3p5+l8TkcO88mIRed7Lxb9MRA7xyr8tIo95cz68KSIjvUOFReQ+bx6I//NGUiMiPxE3X8ZaEVnUTJdptGNMQRhGw3SKMzH9MLBth6oOBO7CZSAFuBOYr6r/AjwI3OGV3wG8qG7Oh6G4UbHg8vbPVtUjge3AWK/8GmCId5wpubo4w0iGjaQ2jAYQka9VtUuC8nLcJD/ve4kHt6hqoYh8jsu/v8cr/1RVu4vINqCnqlYGjlEMPKtu0hdE5GogT1VvEpGnga+BxcBiVf06x5dqGDFYD8Iw9g5NspwJlYHlGup8g2fgcuoMBV4PZDI1jCbBFIRh7B0/DPwv85ZX4LKQAowDlnvLy4CpEJ0caL9kBxWRENBLVV8ArsalsK7XizGMXGJvJIbRMJ28md58nlZVP9T1WyKyFtcLuNAruxyYJyJXAduA//DKrwDmisjFuJ7CVFyG30SEgb94SkSAO9TNE2EYTYb5IAyjkXg+iOGq+nlzy2IYucBMTIZhGEZCrAdhGIZhJMR6oBY/ZwAAAClJREFUEIZhGEZCTEEYhmEYCTEFYRiGYSTEFIRhGIaREFMQhmEYRkL+P94K4Phwv1s2AAAAAElFTkSuQmCC\n", + "text/plain": [ + "
" + ] + }, + "metadata": { + "tags": [], + "needs_background": "light" + } + } + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "ctawd0CXAVEw" + }, + "source": [ + "This graph of _mean absolute error_ tells another story. We can see that training data shows consistently lower error than validation data, which means that the network may have _overfit_, or learned the training data so rigidly that it can't make effective predictions about new data.\n", + "\n", + "In addition, the mean absolute error values are quite high, ~0.305 at best, which means some of the model's predictions are at least 30% off. A 30% error means we are very far from accurately modelling the sine wave function.\n", + "\n", + "**3. Actual vs Predicted Outputs**\n", + "\n", + "To get more insight into what is happening, let's check its predictions against the test dataset we set aside earlier:" + ] + }, + { + "cell_type": "code", + "metadata": { + "id": "i13eVIT3B9Mj", + "outputId": "6004cf7f-77d3-4cb9-fa0d-49bdc591301e", + "colab": { + "base_uri": "https://localhost:8080/", + "height": 299 + } + }, + "source": [ + "# Calculate and print the loss on our test dataset\n", + "test_loss, test_mae = model_1.evaluate(x_test, y_test)\n", + "\n", + "# Make predictions based on our test dataset\n", + "y_test_pred = model_1.predict(x_test)\n", + "\n", + "# Graph the predictions against the actual values\n", + "plt.clf()\n", + "plt.title('Comparison of predictions and actual values')\n", + "plt.plot(x_test, y_test, 'b.', label='Actual values')\n", + "plt.plot(x_test, y_test_pred, 'r.', label='TF predictions')\n", + "plt.legend()\n", + "plt.show()" + ], + "execution_count": null, + "outputs": [ + { + "output_type": "stream", + "text": [ + "7/7 [==============================] - 0s 2ms/step - loss: 0.1627 - mae: 0.3434\n" + ], + "name": "stdout" + }, + { + "output_type": "display_data", + "data": { + "image/png": "iVBORw0KGgoAAAANSUhEUgAAAXwAAAEICAYAAABcVE8dAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAALEgAACxIB0t1+/AAAADh0RVh0U29mdHdhcmUAbWF0cGxvdGxpYiB2ZXJzaW9uMy4yLjIsIGh0dHA6Ly9tYXRwbG90bGliLm9yZy+WH4yJAAAgAElEQVR4nO2deZgU1dXwf6d7hs0lxnFFJKjBfBrZImL6E3AIBjUuoCQxUYNrBqL4xtcFJdFITCJLFnndmQjqBEQTEdQ3JvJhGAHpaEBBEzCKBmXAhYxLXGCAmfv9cavomp7qvXu6uvv8nqef7lr61qmq7nNPnXPuuWKMQVEURSl/QsUWQFEURekcVOEriqJUCKrwFUVRKgRV+IqiKBWCKnxFUZQKQRW+oihKhaAKv4wRkfNEZHGx5XARke4i8oSIfCQifyjC8aeIyFznc28R+UREwlm08yMRuTf/EnYOInK/iPy82HIkw3uv8txu4M+9kKjCTwMROVdEVjkK4m0R+ZOIDC22XKkwxswzxowqthwevgkcCNQYY75VTEGMMW8ZY/Y0xrQm209EakWkKe67txhjLi2shKWFiFwoIiuKLYeSHFX4KRCRq4CZwC1YZdUbuAsYXUy5UiEiVcWWwYcvAK8aY3bl2lBAz09Rgo0xRl8JXsDngE+AbyXZpyu2Q9jivGYCXZ1ttUATMAl4D3gbGAN8A3gVeB/4kaetKcAjwMPAx8ALwADP9uuB151t64CzPNsuBJ4FbgWagZ8761Y428XZ9h7wH+Bl4BjPeTYAW4E3gRuAkKfdFcCvgA+AfwGnJrkeRwGNwIfAP4AznfU/BXYAO51reonPd1Od/0bgOuAloAWoAr4KrHSOtxao9ex/GPCM09b/A+4A5jrb+gAGqHKW9wXuc+7hB8AiYA9gG9DmyPwJ0NORc67nOGc65/qhc+5Hxcl8jSPzR865dXO27Qf8r/O994Hl7nX3uTb/A2xy7t1qYFjcdfu9cw8/dmQZ7Nk+yLmWHzvHfwj4eYLjHAH8Bfsb+jcwD9jHs/1Q4FHnt9LsXNOjgO1Aq3ONPnT2bQQujfuNrsjgnOYmkHE9cLpnucqR5yvO8h+Ad5zrvQz4smff+91zj5fHWWeAL3r+278C3gLeBe4Bumd674L0Ugs/ORGgG7AwyT4/xiqdgcAAYAhWYboc5LRxCPAT4LfA+cCxwDDgRhE5zLP/aOwPdl/gQWCRiFQ72153vvM5rAKdKyIHe757PPAG9knkF3FyjgKGA0c63/829g8LcLuz7nDgRGAccFFcu//E/shnALNFROIvhCPnE8Bi4ADgCmCeiHzJGHMT9inpYWNdKbPjv5/G+QN8FzgN2Mc5zz9iO7d9sYp1gYjs7+z7IFaR7Af8DLggwTEBfgf0AL7syH6rMeZT4FRgiyPznsaYLXHnfCQwH7gS2B94EnhCRLp4dvs2cAq2A+qPVTQAV2MNgv2dc/kRVuH48Tfsb8y9Ln8QkW6e7WdiFfk+wONYRYwjxyLn/PbFXtuxSa6DAFOxHdtRWAU/xWkrjFVyb2I7zEOAh4wx64EJQNS5RvskaT+Tc0rEfOzvwOVk4N/GmBec5T8BfbH38QVsp5UN07D/l4HAF4n9hyGzexccit3jBPkFnAe8k2Kf14FveJZPBjY6n2uxFmLYWd4L+6M43rP/amCM83kK8FfPthD2qWBYgmOvAUY7ny8E3orbfiExC/9r2KeKr+KxRIAw1vI+2rNuPNDoaWODZ1sP5xwO8pFnGNay8rY/H5jiOT9fqy2d88dayxd7tl8H/C6ujaewir03sAvYw7PtQXwsfOBgrBX/eR+ZaoEmHznddm4Efh8n82acJw1H5vM922cA9zifbwYew7EoM/xtfoDz9OPIs8Sz7Whgm/N5OPapRTzbV5LAwvc5zhjgRedzBGtJV/nst/u35lnXSBILP41zSmThfxH7tNLDWZ4H/CTBvvs49/lzzvL9pGHhYzu+T4EjPNsiwL9yvXfFfKmFn5xmYL8U/uKeWIvH5U1n3e42TCwwuM15f9ezfRuwp2d5k/vBGNOGtSJ6AojIOBFZIyIfisiHwDFY67XDd+MxxvwFa/XdCbwnIvUisrfz/WqfczjEs/yOp53PnI9emV16ApscuRO1lYqE5x+/HRsT+JZ7PZxrMhSrwHsCHxhrpXtl8eNQ4H1jzAcZyOnS7v47Mm8iwfUDPiN27X4JbAAWi8gbInJ9ooOIyDUist7JcPoQ+0Tmvffxx+jm/G57ApuNo6UcEl0HRORAEXlIRDaLyH+AuZ7jHAq8afIQg0nznHwxxmzAunXOEJEe2KebB502wyIyTURed+Tf6HwtZbtx7I81blZ7flt/dtZDBvcuSKjCT04U6ysek2SfLVjF49LbWZcth7ofRCQE9AK2iMgXsO6gidgsl32Av2MtEZekj5TGmNuMMcdiLcAjgWuxftqdPuewOQvZtwCHOnJn25bv+Xu2e89xE9bC38fz2sMYMw37ZPB5EdkjThY/NgH7ioifKyLVY3q7+++4ug4ljXM2xnxsjLnaGHM4VmldJSIj4/cTkWHYONC3sU8h+2D90x3caj68DRwS54JLdB3Aut0M0M8YszfW/eh+dxPQO4EB5HedPsUqTZeD3A85nhPE3DqjgXVOJwBwrrPuJGwH0sc9ZCr5ROQgz7Z/Y42xL3t+W58zxuwJ6d+7oKEKPwnGmI+wPrs7RWSMiPQQkWoROVVEZji7zQduEJH9RWQ/Z/9c8oePFZGznT/VldgO56/YAKLBPlIjIhdhLfy0EJHjROR4xx/+KTbI1uY8ffwe+IWI7OV0LFdleQ7PYa3LSc51qgXOwPqW0yXR+fsxF2vlnexYdt2cNMpexpg3gVXAT0Wki5NGe4ZfI8aYt7F+37tE5POO7MOdze8CNSLyuQQy/B44TURGOtf2akfmlalOVEROF5EvOsr4I2zQs81n172w7qmtQJWI/ATYO1X7DlHnu//lnNfZ2DhTIvbCBl4/EpFDsEaBy/PYDmSaiOzhXO8TnG3vAr3iYhdrgLOd/80XgUvydE5gf1OjgB/gWPeedluwT+c9sB1YItYCXxaRgU7sYIq7wXlS+y1wq4gcACAih4jIyc7ndO9doFCFnwJjzK+xCvAG7I9zE9bKXuTs8nOsYnkJm/nygrMuWx4DzsH6M78HnG2M2WmMWQf8GvsHfhfoh83KSZe9sT/gD7CP9M3Yx1KwwdVPsQHfFdg/0JxMBTfG7MAq1VOxFtJdwDhjzCsZNON7/gmOtwlrzf2I2L25ltjv+lxswPl94CZsFksivod90nkFm8l0pXOMV7Cd+hvOo73XvYQx5p9YK/h255zPAM5wrkUq+gJLsAo2CtxljFnqs99TWHfCq9h7t50k7rs4+XYAZ2P91e9jr+2jSb7yU+ArWCX2R+++jnFwBtbH/RbW3XaOs/kv2Oygd0Tk3866W7HxoXeBB2gfPM36nBxZ3sZes/+LzTxyaXDa24zNZEtkLGCMeRXri18CvIb97Xu5Duu2+avjHloCfMnZlu69CxTS3rWnFBMRmYINAp1fbFmKQaWfv6IUGrXwFUVRKgRV+IqiKBWCunQURVEqBLXwFUVRKoTAFqDab7/9TJ8+fYothqIoSkmxevXqfxtj9vfbFliF36dPH1atWlVsMRRFUUoKEUk4klpdOoqiKBWCKnxFUZQKQRW+oihKhRBYH76iKMVj586dNDU1sX379mKLoiSgW7du9OrVi+rq6tQ7O6jCVxSlA01NTey111706dMHn7lulCJjjKG5uZmmpiYOO+yw1F9wUJeOoigd2L59OzU1NarsA4qIUFNTk/ETmCr8MiUahalT7buiZIMq+2CTzf1Rl04ZEo3CyJGwYwd06QJPPw2RSLGlUhSl2KiFX4Y0Nlpl39pq3xsbiy2RomTHokWLEBFeeSX1lAozZ87ks88+S7lfIu6//34mTpyY9ffz3U4hUIVfhtTWWss+HLbvtbXFlkhRsmP+/PkMHTqU+fPnp9w3V4VfCajCL0MiEevG+dnP0nPnqL9fyQf5/h198sknrFixgtmzZ/PQQ7FZMltbW7nmmms45phj6N+/P7fffju33XYbW7ZsYcSIEYwYMQKAPffcc/d3HnnkES688EIAnnjiCY4//ngGDRrESSedxLvvvptQhra2Nvr06cOHH364e13fvn15991302rnwgsv5JFHHtm97JXpl7/8Jccddxz9+/fnpptuAuDTTz/ltNNOY8CAARxzzDE8/PDDHdrMBfXhlymRSHp++2z8/dGodRPV1mpsQLEUIm702GOPccopp3DkkUdSU1PD6tWrOfbYY6mvr2fjxo2sWbOGqqoq3n//ffbdd19+85vfsHTpUvbbb7+k7Q4dOpS//vWviAj33nsvM2bM4Ne//rXvvqFQiNGjR7Nw4UIuuuginnvuOb7whS9w4IEHZtROPIsXL+a1117j+eefxxjDmWeeybJly9i6dSs9e/bkj3/8IwAfffRRZhctBarwK5x4f39DQ3JlrgFhxQ+/uFGuv4v58+fzwx/+EIDvfOc7zJ8/n2OPPZYlS5YwYcIEqqqs+tp3330zarepqYlzzjmHt99+mx07dqTMYz/nnHO4+eabueiii3jooYc455xzsmrHy+LFi1m8eDGDBg0C7NPMa6+9xrBhw7j66qu57rrrOP300xk2bFhG55YKdelUOLW11tcvAqEQzJkDN95olbrfo3mygLC6hiqXfMeN3n//ff7yl79w6aWX0qdPH375y1/y+9//nkwmbPKmLXrz1a+44gomTpzIyy+/zKxZs1LmskciETZs2MDWrVtZtGgRZ599dtrtVFVV0dbWBlj30I4ddm57YwyTJ09mzZo1rFmzhg0bNnDJJZdw5JFH8sILL9CvXz9uuOEGbr755rTPNx1U4VcQiRSy+79oa4Ndu6wy377dWvvxJPpju5Z/ss5CKV8yjRul4pFHHuF73/seb775Jhs3bmTTpk0cdthhLF++nK9//evMmjWLXbt2AbZzANhrr734+OOPd7dx4IEHsn79etra2li4cOHu9R999BGHHHIIAA888EBKWUSEs846i6uuuoqjjjqKmpqatNvp06cPq1evBuDxxx9n586dAJx88snMmTOHTz75BIDNmzfz3nvvsWXLFnr06MH555/PtddeywsvvJD+RUsDdemUCLn6zb2umKoquOgiGDfOtrlrFxhjX67yNwbuu8/u4z2e+8eOl6UQj/RKaZFu3Cgd5s+fz3XXXddu3dixY5k/fz633347r776Kv3796e6uprvf//7TJw4kbq6Ok455RR69uzJ0qVLmTZtGqeffjr7778/gwcP3q1cp0yZwre+9S0+//nP87WvfY1//etfKeU555xzOO6447j//vt3r0unne9///uMHj2aAQMGcMopp7DHHnsAMGrUKNavX0/EuWB77rknc+fOZcOGDVx77bWEQiGqq6u5++67s72E/hhjcn4Bc4D3gL8n2C7AbcAG4CXgK6naPPbYY41iWbnSmO7djQmH7fvKlZl995ZbjBkzxhgRV63bz927GzNrVvu2x4yJ7RMK2e/Gt+V3/FxkVILHunXrii2CkgZ+9wlYZRLo1XxZ+PcDdwA+TgAATgX6Oq/jgbuddyUNsrWe6+th4sSYBe/FGNtWc3N7i/3ll2HRIrtPWxs4T68pg7WJLH9FUYJDXhS+MWaZiPRJsstooMHpff4qIvuIyMHGmLfzcfxyx/Wbu8o2nYBYNAqXX26VvRc3OAuxtryP4o2Ndntbm31vbo6tT9XpeNvwLiuKEgw6y4d/CLDJs9zkrGun8EWkDqgD6N27dyeJVhpccIF9j/epJ6Kx0SptL6EQdO0KM2daRe5nidfW2n3iO5d0Oh1N2VSUYBOooK0xph6oBxg8eHD6+VdlTLwSHTcuve+5irulxSr6q66CffZJ7W5J5JpJx2WjgVtFCTadpfA3A4d6lns565QUZKtEc/GpJ8q2SJWFkY3rSVGUzqOzFP7jwEQReQgbrP1I/ffpkYsSLaRP3S9NVAO3ihJs8qLwRWQ+UAvsJyJNwE1ANYAx5h7gSeAb2LTMz4CL8nHcSiAXJVoon7qb/dPaat1G3nbzmYutVC7Nzc2MHDkSgHfeeYdwOMz+++8PwNq1axkwYMDufRctWkSfPn0KJsv999/PqlWruOOOO7jnnnvo0aMH4xL4Vjdu3MjKlSs599xzAVi1ahUNDQ3cdtttBZMvE/KVpfPdFNsNcHk+jlVupDOgKlslmo47KNMBXfHZPy0t6qtX8k9NTQ1r1qwB7ACnPffck2uuuQawg5TcbbnQ2tpKOBzO6DsTJkxIun3jxo08+OCDuxX+4MGDGTx4cNYy5hstrVBECl2OIFV9k2yOH5/9Ew6rr15xCEgxpcbGRoYPH85pp53Gl770JSZMmLC7ns2ee+7J1VdfzYABA4hGo8ydO5chQ4YwcOBAxo8fT2trKwD33XcfRx55JEOGDOHZZ5/d3faUKVP41a9+BcCGDRs46aSTGDBgAF/5yld4/fXXuf7661m+fDkDBw7k1ltvpbGxkdNPPx2wJSDGjBlD//79+epXv8pLL720u82LL76Y2tpaDj/88N1PA4UolawKv4hkOjNVpv+nVPVNspkZy83+CYVsiYY77lDrXqFTiylt27aNgQMHMnDgQM466yzffZ5//nluv/121q1bx+uvv86jjz4KWCV6/PHHs3btWmpqanj44Yd59tlnWbNmDeFwmHnz5vH2229z00038eyzz7JixQrWrVvne4zzzjuPyy+/nLVr17Jy5UoOPvhgpk2bxrBhw1izZg3//d//3W7/m266iUGDBvHSSy9xyy23tHMLvfLKKzz11FM8//zz/PSnP2Xnzp38+c9/pmfPnqxdu5a///3vnHLKKTlfu0ClZVYamQRks/XHu+4gt7Pwum6yCQgniylonfwKphNzcrt3757SpTNkyBAOP/xwAL773e+yYsUKvvnNbxIOhxk7diwATz/9NKtXr+a4444DbEdywAEH8Nxzz1FbW7s7ZnDOOefw6quvtmv/448/ZvPmzbs7nG7duqWUe8WKFSxYsACAr33tazQ3N/Of//wHgNNOO42uXbvStWtXDjjgAN5991369euX91LJqvCLSCYB2Vz+T4k6i2wDwn4xBR10VeEELCfXWxrZu9ytW7fdfntjDBdccAFTp05tt+8it7ZIJ9K1a9fdn8PhMLt27dpdKvnJJ5/khhtuYOTIkfzkJz/J6Tjq0ikykQhMnpxaOSbzx6dy9SRz3aRz/ETte9dn4h4KiKtXySf5ro+cI88//zz/+te/aGtr4+GHH2bo0KEd9hk5ciSPPPII7733HmB97G+++SbHH388zzzzDM3NzezcuZM//OEPHb6711570atXr92dQ0tLC5999lmHEs1ehg0bxrx58wAbZ9hvv/3Ye++9E55DIUolq4UfcLxuEj9rPJll7X63piZ74ytR+/HrZ85M7xj6JFDGBCgn97jjjmPixIls2LCBESNG+Pr6jz76aH7+858zatQo2traqK6u5s477+SrX/0qU6ZMIRKJsM8++zBw4EDfY/zud79j/Pjx/OQnP6G6upo//OEP9O/fn3A4zIABA7jwwgt3z2gFseBs//796dGjR8pa/C+//HL+SyUnKqNZ7JeWR06v5PAtt9jtYN/dcsbx3501K3FpY7/juvsmat+7XsSYCROSl09OJa8SLEq5PPLSpUvNaaedVmwxOoVilUdWCkA6fvtErtP47zY3W9dNKlwL3FuDx6/92lqbpdPaakstz5lj6/ykOkbAXL2KUlGowg8w8cqxpgZ+8AO7za2amSjwmkqxJsqoaWy0yr6tzb5uvdWmXsZX14xE7KxZs2ZZhd/a2rFD0vILSjGora2lVi0JX1ThBxivcqypgSuusAoc7PSDS5fGlL5fbfpk6ZOJ/Oi1tbF6+GAVeaKng3Hj4IEH/DuVZMdI5erV9M5gYIzpkO2iBAeTwYTuLpqlExASZa64WTTNzeDMfwykN1AqUQZOqqydO++E6upY/fxExlKyxIxsBnWBToYeFLp160Zzc3NWSkUpPMYYmpub08r/96IWfgCIRmHEiJg17FruXmprrRJ2Lfxc/N+p3D11ddCvX3pWdiJrPVtfvdbUDwa9evWiqamJrVu3FlsUJQHdunWjV69eGX1HFX4AaGiwfnOw7w0N/i6axka7DdKf+cqPdPzo2WTYxbtisvHVa1A3GFRXV3PYYYcVWwwlz6jCLxJe5Zgu+UxzznfKdLLRvJnKpUFdRSkMqvA7iXgF7yrHqio49VTrrtm1y76nO41hkMiHK8Z7jdwgsQZwFSV/qMLvBOKt3wsuiCnH1lZ47DGr6MePz81VU0xydcX4PSGAjspVlHyiCr8TiLd+wSqw7dttDrubx967d+kqtFxdMX5PCG+9FbtGGsBVlNzRtMwCEJ9iGV/4bNw4qxzHj08+QUmpkW4hOD/ir1FNjR1r4GYF6kQripI7auHnmUTBSz/rNxKxyl991B2vUWNjbNyBCFx8sf/1UR+/oqSPKvw8kyh4mShjJUAFBouO91q8/HJstK8x4Ck62K4K6JVX2lTWcNiWgKir63SxFaVkUIWfZzSPPD80N8dKPIRCdhnaP0GJxIq3tbXZydX79dMOVFESoT78PJPJPBA6EUhi3Llzw+H25R28T1BtbVbpu7S2wpQpej0VJRES1FoZgwcPNqtWrSq2GAVDJwJJjZ9/Pv66XXEF/OY3MUvfrf+j11OpVERktTFmsN+28nTp1NfDggUwcCC8+iq8+CLssQf88IfWyZtIk/hF/+rrYfZs6NkTJk2KTfeUqAxlQwO88w4cdFD7pHpXprFjoa6O1xqi/GZ7A//HrKP7tu10v6wvHLB193bfNt9/H/79bzjyyJgsiWSPRmHGjNi5DxoEr71mz+PUU+16sOv/9CfYsgUuucT6RK6/Ht54A849F6ZPz++98SHR5Uy3CuiYMdayX7LEWv2awqkoCUg0M0qxX1nPeDVrlpva7v+aNKnjNFKJppaKb6uqyq7z23flSmO6dGm/f5cudv2sWaYNdr/MpEmmtbpru3Vt3u/NmpW4TfdVXZ1Y9pUr7fZk1yHRS6T98nnnZXcf0iSdWb06sx1FKXWoqBmvFixIvv3RR/3r9vql1sS3tWuXXee3rzeP0GXnTmhs5INFjewDCGCAbQ8+SvddO5KfQ12df5txbfvK7m7PhngX34MPwvDhHWdAyRP5qo6pNXgUJTXlF7QdOzb59rPP7jjaKX7UjxshjG+rqsqu89vXrV/spboaamuJ9rTtuKr0xcMdGVKdg1+bcW37yp7se6nwm/Di8sttgfoRI+yUW3mMiia69NmQy8AvRakEyjNoGzAffjQKvxtez+hdC3isaizfW1ZHBGffdets/YC+fWFrQHz4l10Ga9fa7e7EtW5SvAh065bXqKgOnlKU/JEsaFueCj+AlJxSix/d5Ba1Aav0jzgCrr02MCOdSu76KkqBUIWv5Ib7lDFnjo0NeH8zw4fDtGlF1bKa4qooMZIp/PLz4Sv5JxKBu++2JvQRR7TftmwZDBtmXV9FoqHBPoBkOn+uolQaqvBToKNhPUQi1o0TT2urDewW4SJFo/bBw33oqKrSchaKkghV+ElwXQU33mjf09FnZd9B1NXZgHEo7qfT1mYHbPXtC9dd12niNDba/gZsaOGii9Jz55T9fVIUH/Ki8EXkFBH5p4hsEJHrfbZfKCJbRWSN87o0H8ctNH454smor4cTT4Qbbki/gyhJpk+HFSvsENdw2Cp/Eeve2bDBZgedf36niOJN6+zWLb3pIbPpyBWlHMhZ4YtIGLgTOBU4GviuiBzts+vDxpiBzuveXI/bGWSSIx6NWq/Gzp3W2G1pKXNfciQCCxfC8uXw85/DwQe33/7gg7YHzMGMTscKj0Rg5kyruGfOTM+6z7QjV5RyIR8jbYcAG4wxbwCIyEPAaGBdHtouKpmM3mxsjKWqQwXN0OQWvPnwQ2vZuxhje0BjskqdSTfzJhq1WaM7dti+J53yyFrCWqlU8uHSOQTY5FluctbFM1ZEXhKRR0TkUL+GRKRORFaJyKqtW7fmQbTcSXf0plvONxSygcM77qiw1MDp0+G886xrR8SO9G1ry9qMTtcKz8Zaz6SEtaKUE51VS+cJYL4xpkVExgMPAF+L38kYUw/Ug83D7yTZMsZvkI/WcgHmzrVWvXfAVpZmdLpWeLbWus40plQiOQ+8EpEIMMUYc7KzPBnAGDM1wf5h4H1jzOeStRvUgVc6yCcDkpWgSKNnTHf0rI6yVZQYha6H/zegr4gcBmwGvgOcGyfAwcaYt53FM4H1eThuUchXdceKwM+MzqDHTGWFexX95Ml5k1pRypacFb4xZpeITASeAsLAHGPMP0TkZmxd5seB/xKRM4FdwPvAhbket1howC9H8tRjxvcbM2cWrIKzopQNWksnC9SFkAN+Fj5kfEGnTrV59K2tNlAeDtsYcaZuNr2XSrlReVMcFhgN+OVAfHQbsgqKeJ+0RGIVnFM9NHgVfJaHVpSSRRW+0vl4e8ypU2MunpYWOzntlCkpNa+330g3ISj+4eKCCzQeo1QWqvCV4uKa6i0t1kRfsgSeecYWxfFOAu+Dt9/o1y+1ayY+fAAaj1EqC/XhO6gvt4hEo9aqX7Kk/cxaoZAt2XDuuXZgVx4Ok4fwgaIEGp0AJQWaWx8A3JvgnVnLy3nn2YFdeTiMKnilnNEJUFKgxbQCgOuUHz/ef4L3Bx/MS1lLnehcqWRU4ZNZVUylgHhn1ho+vP02Y7QnVpQcUYWPFtMKHJGIDdy6xdgAuneP9cR5mL1EJ0BRKhH14SvBJt7pnoeAi8ZslHJGffg5oJZgkYl3unsDLi0tNgH/Bz/wvUGJ7l18E1Om6P1VKgO18JOglmAAcW+Km7fvZcAAGwOIRJLeu/gmQiE7l4HeX6UcUAs/SzR7J4C4AZeTTor5913WrsX83xN48qx6GhoS3ztvE6FQ+5IMilLOqMJPgmbvBJRIxPphqqt9NhpGLZqA/LaecDjxvXOb6NpV769SOahLJwU6UCfARK4ZkRYAABxJSURBVKNw2WWwZk271QbYSRUzxyyjdUgk6b3LcY4WRQkcFT/SVv+8Zc7558O8ebi/ZAF2EeLfY+o4aEjvjG+8xm6UUqZiffjRqE3gqK21tdNHjtRsjLJk7lxYuRIZMwYTCtNKiFB1NQc9OSerG6+xG6VcKdtqmX6lWbQEbhkTicDChYTcx7m33oLf/jar2sc6q5lSrpStwnetNFfZi+iftyJwayZHo/DAAx21dhr+vfg5WtRAUMqFslX4Xiutqiqt8uoJ0RhACeKntb3O+VAIBg2CSy6Bujqg433We62UG2Wr8PNlpWkAr4SJ19pe53xrKzz/vH0B0X51ep+VsqdsFT7kx0rzC+CpIihR3Me+bdvar7/ySsxxr7Njx/Td97mhoePDgT7lKaVOWSv8fKABvDLCfeybMQMWLYqt37aNyLIZTA3D5PB0qqpgzhzbyXfpAjNntp8zV61/pVQp67TMfKClk8sMJ5uHWbOge3e8o1D+a995LBk5lZtPje72+uzYAbNn22wvTdNUSp2KGHilKH40nX8dh8ybsXvZhKsIYWgLVXFv60Xc3zaOVdW2h9+50+7TpYu69ZRgU7EDrxQlGb/78nRmMInX+CLLGA5tBlpbkZ0tXNp2Dys4gdk7z6e11e4vAhdfrMpeKV0qQuFrTXvFj9pa+Gn36Rwdfo2bukzDdOmCcSpwhgDBcD7zuMVcRzgM3brZ1F5FKVXKPmiraZVKItqn7kYI8zQvXNnAwOfvAWxNHgNc9rkH+fqR/+HgnnAw4wD9ASmlSdkrfE2rVJLRPnU3QsvMCPOHfsy5bbFibHt9/DZfcToBnpyjPyKlZCl7l47WtFcyIRKBw1fMJTp8Ett7fREZPrzdzFpmxw7eGDeFl+vVP6iUHhWRpeM3D7YOolHSIhqFESOgpWW3xd9KiB105fVZT9OvTn9ASrBIlqVT9i4daP/Yrj59JSMiEVi6FBoa2PK/L3Bg0yqqaMOwg52zG+DFBrtftoWaFKUTKUuXTrKsHK11rmRMJAJ33837N85kB13ZSZhWqhiw+l645x77GjoU6uuLLamiJKXsLPx4C/6KK+wMeGPH2qKIWipByZZ+dRFe5mmaFzTypR5vcfBjs2Ib29pgwgT4059g0iS19pVAkhcfvoicAvwPEAbuNcZMi9veFWgAjgWagXOMMRuTtZmtD3/qVDvJUWurHSjjPb1Zs6zSVx++kjPRKAwbxu5RWdgUTgGbIXDXXbvLLitKZ1LQkbYiEgbuBE4Fjga+KyJHx+12CfCBMeaLwK3A9FyPm4jaWvt/c8bPtGPBAvseicDkyarslRyIRHj96rtoJYTBo+zBdgI/+IG6eJTAkQ8f/hBggzHmDWPMDuAhYHTcPqOBB5zPjwAjRfxUcn5wWw6H268fO7ZQR1QqjWgU+t1exzBWsJAx7RQ/YF08l11mFb8O8VYCQj4U/iHAJs9yk7POdx9jzC7gI6AmD8fuQGMj7NplXTnGwJgxMGpUzJ2jKPnADf5HiTCWhQxnBY+HxuwuzQBYS3/WLBg+HI4/Xi1+pegEKmgrInVAHUDv3r2zaiM+KKvxM6UQxE+hOeCiCAeMW0jo5XqYOLG91bFrV2x2rWXLYO7cYouvBBA3tlhTA83NhYkx5kPhbwYO9Sz3ctb57dMkIlXA57DB23YYY+qBerBB22yE0Qmolc4g4e8sUgf9+tkps+bMsT2Cl3nzrMWvj5uKBze7sKXFegNDIejaNf/jhPKh8P8G9BWRw7CK/TvAuXH7PA5cAESBbwJ/MQUc4qsTUCudQcLfmbth3LiOs2uBnVGlUCacUpK4LkK3ikdbW2Fqf+Xsw3d88hOBp4D1wO+NMf8QkZtF5Exnt9lAjYhsAK4Crs/1uIoSeNzZtc47r/36F1+0ucMjRmhQVwFiLsKQo5FDocKME6qIWjqKUnTq621ecI8e8MQT7fL3qaqyvn219iuafPnwk+Xhq8JXlBzI+E/qOmu3bWu/fswY+zSgKDlS8cXTFKUQJAq0zZzZUfnHRndHiDz9NHz729DUFGtsy5bYU4BbB0Qpa4ox4l8VvqJkiV+graXFZmW2tcWqsUJ8hdYIkRtvhPHjY4317RtbXrxYa/KUOcWq2qsKX1GyxA20eS38UMi6571ZFuAz69pkx4J3LXq37ofLokXw1FNav7sMiUZhypTY78b7Oym0xa8KX1GyxJuL7/rwa2rgyis7VmP1rdBaV9fedbN4cfsDbN9uUzuvvVZdPGWCnxuwSxf48EM48URrFBQi/95FFb6i5IBfLn6/fh0ttZSDAV2FPnu2Tdt0R+pu2BBz9ajSL3ni3YCDB8Mll8Dll9tbDrYzKNS0yZqloyhBIxq1lv2GDbF1o0ZZF49S0kSjttN3B2B37QoXXWTj9W4nUF0NzzyTvcIvaHlkRVHyTCRi3ThetNRrWRCJwMUXxyr6ulZ9167WvVNVBXfcoT58Raks6uKCul53jqZvlizuoOrqauuv79LFPsyNG9c5KZrq0lGUHEgnlzrRPlnlYdfXt0/nHDNG0zdLBG8qZlWVdeWMG5f/W6cDrxSlAKSTS51on6zzsDV9s2RxA7ZuVY3evTv/lqkPX1GyxPsH9uZSp7NPOt/1xc+Xv2OHrcp58sk6yUqAccdthMOFKYyWDmrhK0qWxE+24/cHTrRPOt/1xZu++cILNnVTJFaC2c3lV99+4IhEbNkNN/xSjAcy9eErSg50ug/fr+FFi+xsWi5HHw29emlQN2B0VjkF9eErSoHwDryKRu1EV9A+GJdoopScJ+pxG6ipaa/w162zL63JEyj83HidfVtU4StKHogfUHPffXDbbbFyCwWd4MqbwrlpE6xfH9u2aJGtv3/XXWrtF5ms3Xh5RF06ipIHpk6FH//YutTButXDYTt6Mt05SvNSLjc+bdMlHIbly9XSLzKdURJZXTqKUmBqa+1gGtfCD4Viyh5Sz1GaN/+ua8X/7Gft6+23tdlMns8+U99+ESn2fNuq8BUlD0QiVpm7PvxBg2zVzPiqiIke4/Pq362rsxXcTjwRdu6060IhzeRRVOErSr6It97cqpnp+PDz7t+NRGwFLrcHeuGF9oHd2bMLHFhQgoj68BWlkylYmmYy4n371dXtp+VSpV82qA9fUQJCMl99Qf273kyeHj1s5o7rP2po6PzJVZWioKUVFKUTybqkQj6oq7N1dyZNio3xr6qCOXPghhusz19LM5Q1auErSifgumtqaoqfi91ubsa33orNvtHWBpddZmfcKkQZxwqnM1IyU6E+fEUpMPFunJkzO2lAVrrCebN5wA4i6NKlcPV7K5DOKqsAOuOVohSVeDeOq+SvvBJuvNEqAndijE4nErFTLFV5HvaNsfmk99wDQ4fCddcVSbjyoaiuPA+q8BWlwPiVxQ2KAgCsb3/ZMpgwwQ4Hduffg9iALfXtZ0w0akdgu2U3il0aGdSloyidQrz/tjMf8TPCrQA3a1asTgRoBU4fkvnk/e4vdNI0hpqWqSjFJT7l0hs3TaQAUgX5ChIEdAXde29r2bt4K3BCxSv9VB223xPc5MnF79RV4StKkUiWd59KoRT8CWH6dDjiCP8KnAsWVLzCT1UKIwiVMf1QH76iBJBUPv5OiQG4eftXXtl+vd80ixVGKp+8+wT3s58FyF2HWviKEkhSWYidakF6R+l6ffhBSCwvIhdcYN8TZa4WuzKmHxq0VZSAUhQffroENupceOrr4fLLbQJTqjkOioEGbRUlgOSqsItqQSbyKZW5xR+N2sHIra12uaWlOFMVZktOCl9E9gUeBvoAG4FvG2M+8NmvFXjZWXzLGHNmLsdVlFKn6EHZXIn3KdXUBFzg/NDQEFP2YIcs1NTYfPtS6OdyDdpeDzxtjOkLPO0s+7HNGDPQeamyVyqeQARlcyE+Ktnc3F7ghobYqKMy5oQTAjJiOk1ydemMBmqdzw8AjYCOw1aUFAQqKJst8T4lV+Bw2M7ivmtXWVn70Si8846dPMwYO6XA0UfDs8/maaayTiCnoK2IfGiM2cf5LMAH7nLcfruANcAuYJoxZlGC9uqAOoDevXsf++abb2Ytm6IEHb/Rt8mWA48r8FtvwW9/a7VgKAS9e8Onn8JRR8G0aSVyMjHcwcezZ8dqzIXDcNdddlazoHmykgVtUyp8EVkCHOSz6cfAA14FLyIfGGM+79PGIcaYzSJyOPAXYKQx5vVkx9UsHaWSCLzPPhPck3En9PUiYouylcjALfdUtm9vX2lCBH7xCzt6Nmgdc07VMo0xJxljjvF5PQa8KyIHOwc5GHgvQRubnfc3sG6fQVmei6KUJV6ffUsLTJkSfH9wQlz//kknddxmjC3SViIn596XeLu4ujrmZotEglE2IR1yDdo+DjjDD7gAeCx+BxH5vIh0dT7vB5wArMvxuIpSVrg++1DIGsVLlpRGEDAhkYjttcLhjtuMCWAU2h/viNquXWHMGNtfBd1Xn4hcFf404Osi8hpwkrOMiAwWkXudfY4CVonIWmAp1oevCl9RPHiNYlfpBzI7JxMiEVi+3FbZ9BIOW03qrR8cULzJSEuXwsKFcPfdpansQUfaKkqgKCtfvpfrroMHH4TDD7eBW4j5+UMhuPPOTvXrB83vnk9yCtoWC1X4SqVSzspoN1On2onT3aBuOAxnnAEHHVTwaRW9nWpVVWwmRyiP664KX1EqkEB3HNEoDB9uc/Xjqa6GZ54pmNBTp9qBUu6IWXcKX2Psuvgnq0BfRx+0lo6ilBHpFFVraAj42KdIxLpxJk60QnoNz5074dvftlq5AG4eNxDrploaY619iH1uaLDXuKbGjqQtFxebKnxFKSHSrcHjzRsPbG2zujo7cqmhwZag9ObsNzXB+PHw+ut2MpY84gZiGxpgzhxr1VdVxSx870DhUCjWH5VaoTQ/VOErSgmRaqal+Lxx110R2NpmbnmGQYPal6F0+eUv4T//ybtf3z3suHGxThA6DhR2nwDA9kc1NXkToSjojFeKUkKkmmkpfvv48f61zQKX7llXZ1M4x4xpv94YO6F6moMSMs30jETsNXOvx+TJthNwr2E4bDtNsNZ+c3PaZxRI1MJXlBIi1eTnybaXRDG2hQttCuevfhVz8cQ71hP4pLJJaU30HfcaxvvwA3ndMkAVvqKUGKkmPvHbHt8RQIBruE+fbi19b+S5qso63N3qZcOGdSjElsrd5Uei73ivoRtmKAc0LVNRSoxc0wSTWcKBS0H0VuCcNat9Nk8oZIe9Opk8+bTwM90nSGhapqKUCflQPoms2kAqNtfUjkZtJo9X4be12bTOfv0gEknp7krUvJuxk4hsnhyCigZtFaWESDQTVn09nHyyfffiF8RMFPgN9CxbkQhcc03H9a2t7QTNtnLlAw/YzBy/2HCqQHkpoRa+opQQfjNh1dfbbByAxYvte11dYos9kSUc+Fm2pk+HI46AmTPhn/+067p29RU0mWsqflsqCz6bJ4fAYowJ5OvYY481iqJ0ZOVKY265xb4bY8yoUW62uH2NGmXX33KLMeGwXRcO2+VM2w4siQRdudJsnHCLObHLShMOG9O9e/tdVq6067zb/NaVMsAqk0CvqoWvKCVGfBbO2LExy95dhuws9lQZQIHBT1DnkebQ7Tt40nRhJE/ztx2Rdha7nzU/eXIZWfApUIWvKCWOW25mwQKr7N3lsnJFpIOjzUOmlS5s43FO534uZWhtrDSDXycYuMykAqJpmYqilAdu0GLbNrxaTSZNalePx6vgIYCZSTmS05y2iqKUFiUwkVRhcB9p9t0Xgd0vHn3UbncuTITo7kyeQGcmFQB16ShKGeHNzAmH4eKLCz6fSLCIRODSS2HGjNi6s8+2F+bEE+1IXU+9/cBnJuUZVfiKUkZ4LdbWVjs49YEHysNVkTau++bRR62ynz4dzjorVpZh50645BKYPZtIJFJRcQ5V+IpSRiSa3KOUR4dmxfTp7evob9nSfvv69XbGrTvvJFJXVzHXRn34ilJGuG7s8ePtmKRSGR2a77hDh/YuuaTjTrt22Rr8P/hBxQQ8NEtHUcqUUkk3zHcNn4Tt1dfD7NmwalX72bXAFmK75pq8z65VDDRLR1EqkPi6Moms6GjUGrnFMnTznSmTsL26OnjuOVths7o6NrMJ2A5gxgwb2C1ja199+IpSASSyeqNRGDHCztcKtuR8Z/v7850pk7I971y68SWXly2LTYEV5MeiLFGFrygVQKICYe56l507/XVdId1D6ZQozqa9eHnbn4NTmmHvvduncIK9CClm1ypZEhXZKfZLi6cpSv5IVCBs5UpjunaNFV7r0sW3HlnBi4sV+hhJ2580yRiR2EWoqrIXpUSrqZGkeJr68BWlAnCt3p/9rH1QNBKBpUthwgT78rPuc/WxR6M2Df744zvW68/mGNlk9CRtf/p0ePbZ2EW49FKbwePufP310LevnWu31EnUExT7pRa+ohSPlSuNmTDBvmbNyt76XrnSGsze8s2zZvnvl84xsn0SyOh73p3d+tLua+DAwFv8aHlkRVHSJRq1rmvXt19dDXfcAc3Nmbu0GxutsexlwYJYRU+X9Pzu2U83GInYeVPciqJJv+MV5q67oKkptm3NGjuB+l13dTyJEkAVvqIo7WhsjFUhAPv5xRdtNmOm1NZCVVV7pe/W648nvsS9X2ZRthk90ShceaX93vLlu6fBTYwrzIcfdgzqtrbakW3z5sG0aSUV1FUfvqIo7aitteOQ8kEkYjMdx4yBIUNsFmS6hnEia94vFpFNW2kxfTqcd57/tmXLYOjQxIGJAKIjbRVF6UB9va060NZmXTrFSEvP5whcd7yB29bSpRm2FY1aS3/Roo7bqqqs8g+Ipa8jbRVFyYi6Ouv6+MUvYtZwZ9fYj7fmc5XBtW2zsnEjEVi4ECZN6rittRWmTCmNEbqJornpvIBvAf8A2oDBSfY7BfgnsAG4Pp22NUtHUYJBECb5zlWGbCZ0T8isWcYcfbQxoVAsfz8UCkzOPgXMw/87cDawLNEOIhIG7gROBY4GvisiR+d4XEVROokgzAoVL0NDQ2bWvhvszUv10Lo6+Mc/YMUK+PrXbcCjra0kpszKKUvHGLMeQLxFiDoyBNhgjHnD2fchYDSwLpdjK4rSOSTKjEmn3EI2JRn8vuOVIRyG++6zmT/p+vYLMqF7JGJdOcuX+6cNBbBcaWekZR4CbPIsNwHH++0oInVAHUDv3r0LL5miKCnxU5bpBFTT3Seddr0yvPUW/Pa32eXi513vJhtAEMDZ0VMqfBFZAhzks+nHxpjH8imMMaYeqAebpZPPthVFyZ54ZZnOAKhU+8TrRHdglDtbV0tL+++4MkSjdtrGwMxD69eTZDtCrMCkVPjGmJNyPMZm4FDPci9nnaIoJUo6A6Di3TBvvWWVtav3vDqxpQUmTrRuGjeLpq0Namo6tpuOe6bo3hS/C1R0oTrHpfM3oK+IHIZV9N8Bzu2E4yqKUiDSUbruPjNmwBNP2Nx+74TqXp0oYhW/N2UyFLLlHLx4debkyf6yBcKbEn+BwAq1fbv9PGxYUUbp5qTwReQs4HZgf+CPIrLGGHOyiPQE7jXGfMMYs0tEJgJPAWFgjjHmHzlLrihKUUnXJ/7HP1plDh3dNBdcYN8HDbKlD1parGUfCtk5eeNjoOko8sB4U7wXaOrUmK8K7ECtE06A0aNtbn8nCZhrls5CYKHP+i3ANzzLTwJP5nIsRVFKj8bGmLJ3qanpqLzHjYsZxDU1/oXa0lXk+Z5BKy/U1trHGO8jjDF25O6f/pTF0N/s0OJpiqIUjNpaa6m7xq0x1pK/4IKOyts7/26ittJR5AVJwcyVSMROkh5fiA1iAws6QWBV+IqiFAxX+U6ZAkuWxMYnQXLl7RffzESRFyQFM1emT4cjjoD/+R9Yvz5m7VdV2cmEd+2yvqw77yxY6WUtnqYoSsHx87+Dv/IORNC10ESj7Sfxra+3vSHYlKbly7M+6WTF09TCVxQl78Rb6Ims80yDrgHIbMwP3keQaNSOJHNpbbV+r5kz836SqvAVRckLrjKuqYlNNuIOqHKDsIlSKb0kK+VQlpZ/JAJnnNG+9PLf/mZPNs8nqQpfUZSc8SpjEeudaGuzaZaXX27d1bnWvQlMumUhmDTJZuu0tNhlYwpykloPX1GUnPEq47Y264Z2X21tmVfajEQ6Zu3kteJl0IhEbGrmhAk2ralAJ6kWvqIoORPvhnHdOPHunVz0VyDTLfOJ69cfN65gJ6lZOoqi5IVEAdWyCbSWCMmydFThK4qilBE6p62iKEUlGu38OXGVjqgPX1GUglK26ZQliFr4iqIUlCDMiatYVOErilJQyjqdssRQl46iKAWl7NMpSwhV+IqiFJxAVq+sQNSloyiKUiGowlcURakQVOEriqJUCKrwFUVRKgRV+IqiKBWCKnxFUZQKIbDF00RkK/Bmll/fD/h3HsUpBqV+DqUuP+g5BIFSlx86/xy+YIzZ329DYBV+LojIqkTV4kqFUj+HUpcf9ByCQKnLD8E6B3XpKIqiVAiq8BVFUSqEclX49cUWIA+U+jmUuvyg5xAESl1+CNA5lKUPX1EURelIuVr4iqIoShyq8BVFUSqEslL4InKKiPxTRDaIyPXFlidTRGSOiLwnIn8vtizZIiKHishSEVknIv8QkR8WW6ZMEZFuIvK8iKx1zuGnxZYpG0QkLCIvisj/FluWbBCRjSLysoisEZFVxZYnG0RkHxF5REReEZH1IlLUItFl48MXkTDwKvB1oAn4G/BdY8y6ogqWASIyHPgEaDDGHFNsebJBRA4GDjbGvCAiewGrgTEldh8E2MMY84mIVAMrgB8aY/5aZNEyQkSuAgYDextjTi+2PJkiIhuBwcaYkh14JSIPAMuNMfeKSBeghzHmw2LJU04W/hBggzHmDWPMDuAhYHSRZcoIY8wy4P1iy5ELxpi3jTEvOJ8/BtYDhxRXqswwlk+cxWrnVVKWkYj0Ak4D7i22LJWKiHwOGA7MBjDG7CimsofyUviHAJs8y02UmKIpN0SkDzAIeK64kmSO4w5ZA7wH/D9jTKmdw0xgEtBWbEFywACLRWS1iNQVW5gsOAzYCtznuNbuFZE9iilQOSl8JUCIyJ7AAuBKY8x/ii1PphhjWo0xA4FewBARKRkXm4icDrxnjFldbFlyZKgx5ivAqcDljsuzlKgCvgLcbYwZBHwKFDW2WE4KfzNwqGe5l7NO6WQcv/cCYJ4x5tFiy5MLziP4UuCUYsuSAScAZzo+8IeAr4nI3OKKlDnGmM3O+3vAQqzbtpRoApo8T4ePYDuAolFOCv9vQF8ROcwJjnwHeLzIMlUcTsBzNrDeGPObYsuTDSKyv4js43zujk0EeKW4UqWPMWayMaaXMaYP9n/wF2PM+UUWKyNEZA8n6I/jBhkFlFT2mjHmHWCTiHzJWTUSKGryQlUxD55PjDG7RGQi8BQQBuYYY/5RZLEyQkTmA7XAfiLSBNxkjJldXKky5gTge8DLjg8c4EfGmCeLKFOmHAw84GR+hYDfG2NKMrWxhDkQWGjtB6qAB40xfy6uSFlxBTDPMULfAC4qpjBlk5apKIqiJKecXDqKoihKElThK4qiVAiq8BVFUSoEVfiKoigVgip8RVGUCkEVvqIoSoWgCl9RFKVC+P+IeD9lpzo2HAAAAABJRU5ErkJggg==\n", + "text/plain": [ + "
" + ] + }, + "metadata": { + "tags": [], + "needs_background": "light" + } + } + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "Wokallj1D21L" + }, + "source": [ + "Oh dear! The graph makes it clear that our network has learned to approximate the sine function in a very limited way.\n", + "\n", + "The rigidity of this fit suggests that the model does not have enough capacity to learn the full complexity of the sine wave function, so it's only able to approximate it in an overly simplistic way. By making our model bigger, we should be able to improve its performance." + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "T7sL-hWtoAZC" + }, + "source": [ + "## Training a Larger Model" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "aQd0JSdOoAbw" + }, + "source": [ + "### 1. Design the Model\n", + "To make our model bigger, let's add an additional layer of neurons. The following cell redefines our model in the same way as earlier, but with 16 neurons in the first layer and an additional layer of 16 neurons in the middle:" + ] + }, + { + "cell_type": "code", + "metadata": { + "id": "oW0xus6AF-4o" + }, + "source": [ + "model = tf.keras.Sequential()\n", + "\n", + "# First layer takes a scalar input and feeds it through 16 \"neurons\". The\n", + "# neurons decide whether to activate based on the 'relu' activation function.\n", + "model.add(keras.layers.Dense(16, activation='relu', input_shape=(1,)))\n", + "\n", + "# The new second and third layer will help the network learn more complex representations\n", + "model.add(keras.layers.Dense(16, activation='relu'))\n", + "\n", + "# Final layer is a single neuron, since we want to output a single value\n", + "model.add(keras.layers.Dense(1))\n", + "\n", + "# Compile the model using the standard 'adam' optimizer and the mean squared error or 'mse' loss function for regression.\n", + "model.compile(optimizer='adam', loss=\"mse\", metrics=[\"mae\"])" + ], + "execution_count": null, + "outputs": [] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "Dv2SC409Grap" + }, + "source": [ + "### 2. Train the Model ###\n", + "\n", + "We'll now train and save the new model." + ] + }, + { + "cell_type": "code", + "metadata": { + "id": "DPAUrdkmGq1M", + "outputId": "b0b50b8b-f5fc-4433-db0e-703697443b76", + "colab": { + "base_uri": "https://localhost:8080/" + } + }, + "source": [ + "# Train the model\n", + "history = model.fit(x_train, y_train, epochs=500, batch_size=64,\n", + " validation_data=(x_validate, y_validate))\n", + "\n", + "# Save the model to disk\n", + "model.save(MODEL_TF)" + ], + "execution_count": null, + "outputs": [ + { + "output_type": "stream", + "text": [ + "Epoch 1/500\n", + "10/10 [==============================] - 1s 20ms/step - loss: 0.4355 - mae: 0.5542 - val_loss: 0.4315 - val_mae: 0.5685\n", + "Epoch 2/500\n", + "10/10 [==============================] - 0s 5ms/step - loss: 0.4183 - mae: 0.5548 - val_loss: 0.4157 - val_mae: 0.5581\n", + "Epoch 3/500\n", + "10/10 [==============================] - 0s 6ms/step - loss: 0.3871 - mae: 0.5322 - val_loss: 0.3988 - val_mae: 0.5444\n", + "Epoch 4/500\n", + "10/10 [==============================] - 0s 5ms/step - loss: 0.3954 - mae: 0.5348 - val_loss: 0.3834 - val_mae: 0.5350\n", + "Epoch 5/500\n", + "10/10 [==============================] - 0s 5ms/step - loss: 0.3670 - mae: 0.5163 - val_loss: 0.3684 - val_mae: 0.5257\n", + "Epoch 6/500\n", + "10/10 [==============================] - 0s 5ms/step - loss: 0.3426 - mae: 0.4999 - val_loss: 0.3532 - val_mae: 0.5166\n", + "Epoch 7/500\n", + "10/10 [==============================] - 0s 5ms/step - loss: 0.3453 - mae: 0.5006 - val_loss: 0.3369 - val_mae: 0.5055\n", + "Epoch 8/500\n", + "10/10 [==============================] - 0s 6ms/step - loss: 0.3182 - mae: 0.4830 - val_loss: 0.3203 - val_mae: 0.4940\n", + "Epoch 9/500\n", + "10/10 [==============================] - 0s 5ms/step - loss: 0.3013 - mae: 0.4691 - val_loss: 0.3041 - val_mae: 0.4833\n", + "Epoch 10/500\n", + "10/10 [==============================] - 0s 5ms/step - loss: 0.2795 - mae: 0.4523 - val_loss: 0.2867 - val_mae: 0.4699\n", + "Epoch 11/500\n", + "10/10 [==============================] - 0s 20ms/step - loss: 0.2632 - mae: 0.4395 - val_loss: 0.2698 - val_mae: 0.4558\n", + "Epoch 12/500\n", + "10/10 [==============================] - 0s 6ms/step - loss: 0.2581 - mae: 0.4331 - val_loss: 0.2534 - val_mae: 0.4436\n", + "Epoch 13/500\n", + "10/10 [==============================] - 0s 5ms/step - loss: 0.2363 - mae: 0.4185 - val_loss: 0.2381 - val_mae: 0.4318\n", + "Epoch 14/500\n", + "10/10 [==============================] - 0s 5ms/step - loss: 0.2171 - mae: 0.3972 - val_loss: 0.2220 - val_mae: 0.4151\n", + "Epoch 15/500\n", + "10/10 [==============================] - 0s 6ms/step - loss: 0.1944 - mae: 0.3783 - val_loss: 0.2095 - val_mae: 0.4041\n", + "Epoch 16/500\n", + "10/10 [==============================] - 0s 6ms/step - loss: 0.1948 - mae: 0.3796 - val_loss: 0.1969 - val_mae: 0.3902\n", + "Epoch 17/500\n", + "10/10 [==============================] - 0s 6ms/step - loss: 0.1842 - mae: 0.3642 - val_loss: 0.1868 - val_mae: 0.3790\n", + "Epoch 18/500\n", + "10/10 [==============================] - 0s 6ms/step - loss: 0.1706 - mae: 0.3480 - val_loss: 0.1781 - val_mae: 0.3677\n", + "Epoch 19/500\n", + "10/10 [==============================] - 0s 5ms/step - loss: 0.1599 - mae: 0.3348 - val_loss: 0.1713 - val_mae: 0.3576\n", + "Epoch 20/500\n", + "10/10 [==============================] - 0s 5ms/step - loss: 0.1563 - mae: 0.3340 - val_loss: 0.1653 - val_mae: 0.3468\n", + "Epoch 21/500\n", + "10/10 [==============================] - 0s 6ms/step - loss: 0.1487 - mae: 0.3161 - val_loss: 0.1613 - val_mae: 0.3391\n", + "Epoch 22/500\n", + "10/10 [==============================] - 0s 6ms/step - loss: 0.1425 - mae: 0.3061 - val_loss: 0.1577 - val_mae: 0.3306\n", + "Epoch 23/500\n", + "10/10 [==============================] - 0s 6ms/step - loss: 0.1347 - mae: 0.3005 - val_loss: 0.1552 - val_mae: 0.3235\n", + "Epoch 24/500\n", + "10/10 [==============================] - 0s 5ms/step - loss: 0.1411 - mae: 0.2995 - val_loss: 0.1533 - val_mae: 0.3185\n", + "Epoch 25/500\n", + "10/10 [==============================] - 0s 6ms/step - loss: 0.1414 - mae: 0.2992 - val_loss: 0.1517 - val_mae: 0.3122\n", + "Epoch 26/500\n", + "10/10 [==============================] - 0s 5ms/step - loss: 0.1397 - mae: 0.3011 - val_loss: 0.1517 - val_mae: 0.3129\n", + "Epoch 27/500\n", + "10/10 [==============================] - 0s 5ms/step - loss: 0.1335 - mae: 0.2879 - val_loss: 0.1494 - val_mae: 0.3057\n", + "Epoch 28/500\n", + "10/10 [==============================] - 0s 6ms/step - loss: 0.1335 - mae: 0.2897 - val_loss: 0.1490 - val_mae: 0.3049\n", + "Epoch 29/500\n", + "10/10 [==============================] - 0s 5ms/step - loss: 0.1287 - mae: 0.2792 - val_loss: 0.1478 - val_mae: 0.3010\n", + "Epoch 30/500\n", + "10/10 [==============================] - 0s 5ms/step - loss: 0.1291 - mae: 0.2774 - val_loss: 0.1472 - val_mae: 0.2992\n", + "Epoch 31/500\n", + "10/10 [==============================] - 0s 6ms/step - loss: 0.1245 - mae: 0.2756 - val_loss: 0.1467 - val_mae: 0.2991\n", + "Epoch 32/500\n", + "10/10 [==============================] - 0s 5ms/step - loss: 0.1327 - mae: 0.2775 - val_loss: 0.1460 - val_mae: 0.2977\n", + "Epoch 33/500\n", + "10/10 [==============================] - 0s 6ms/step - loss: 0.1163 - mae: 0.2613 - val_loss: 0.1453 - val_mae: 0.2955\n", + "Epoch 34/500\n", + "10/10 [==============================] - 0s 6ms/step - loss: 0.1251 - mae: 0.2731 - val_loss: 0.1443 - val_mae: 0.2922\n", + "Epoch 35/500\n", + "10/10 [==============================] - 0s 5ms/step - loss: 0.1365 - mae: 0.2829 - val_loss: 0.1441 - val_mae: 0.2951\n", + "Epoch 36/500\n", + "10/10 [==============================] - 0s 6ms/step - loss: 0.1283 - mae: 0.2757 - val_loss: 0.1427 - val_mae: 0.2905\n", + "Epoch 37/500\n", + "10/10 [==============================] - 0s 5ms/step - loss: 0.1146 - mae: 0.2567 - val_loss: 0.1432 - val_mae: 0.2930\n", + "Epoch 38/500\n", + "10/10 [==============================] - 0s 5ms/step - loss: 0.1231 - mae: 0.2655 - val_loss: 0.1412 - val_mae: 0.2869\n", + "Epoch 39/500\n", + "10/10 [==============================] - 0s 6ms/step - loss: 0.1263 - mae: 0.2739 - val_loss: 0.1407 - val_mae: 0.2890\n", + "Epoch 40/500\n", + "10/10 [==============================] - 0s 5ms/step - loss: 0.1396 - mae: 0.2878 - val_loss: 0.1398 - val_mae: 0.2867\n", + "Epoch 41/500\n", + "10/10 [==============================] - 0s 5ms/step - loss: 0.1194 - mae: 0.2651 - val_loss: 0.1390 - val_mae: 0.2835\n", + "Epoch 42/500\n", + "10/10 [==============================] - 0s 5ms/step - loss: 0.1206 - mae: 0.2607 - val_loss: 0.1379 - val_mae: 0.2831\n", + "Epoch 43/500\n", + "10/10 [==============================] - 0s 6ms/step - loss: 0.1296 - mae: 0.2716 - val_loss: 0.1373 - val_mae: 0.2850\n", + "Epoch 44/500\n", + "10/10 [==============================] - 0s 6ms/step - loss: 0.1155 - mae: 0.2545 - val_loss: 0.1362 - val_mae: 0.2814\n", + "Epoch 45/500\n", + "10/10 [==============================] - 0s 5ms/step - loss: 0.1237 - mae: 0.2610 - val_loss: 0.1353 - val_mae: 0.2806\n", + "Epoch 46/500\n", + "10/10 [==============================] - 0s 6ms/step - loss: 0.1163 - mae: 0.2529 - val_loss: 0.1350 - val_mae: 0.2815\n", + "Epoch 47/500\n", + "10/10 [==============================] - 0s 5ms/step - loss: 0.1123 - mae: 0.2527 - val_loss: 0.1339 - val_mae: 0.2814\n", + "Epoch 48/500\n", + "10/10 [==============================] - 0s 6ms/step - loss: 0.1136 - mae: 0.2541 - val_loss: 0.1325 - val_mae: 0.2775\n", + "Epoch 49/500\n", + "10/10 [==============================] - 0s 5ms/step - loss: 0.1154 - mae: 0.2535 - val_loss: 0.1318 - val_mae: 0.2783\n", + "Epoch 50/500\n", + "10/10 [==============================] - 0s 5ms/step - loss: 0.1249 - mae: 0.2632 - val_loss: 0.1312 - val_mae: 0.2715\n", + "Epoch 51/500\n", + "10/10 [==============================] - 0s 17ms/step - loss: 0.1117 - mae: 0.2534 - val_loss: 0.1319 - val_mae: 0.2801\n", + "Epoch 52/500\n", + "10/10 [==============================] - 0s 6ms/step - loss: 0.1088 - mae: 0.2529 - val_loss: 0.1289 - val_mae: 0.2727\n", + "Epoch 53/500\n", + "10/10 [==============================] - 0s 5ms/step - loss: 0.1166 - mae: 0.2611 - val_loss: 0.1290 - val_mae: 0.2760\n", + "Epoch 54/500\n", + "10/10 [==============================] - 0s 6ms/step - loss: 0.1137 - mae: 0.2537 - val_loss: 0.1270 - val_mae: 0.2696\n", + "Epoch 55/500\n", + "10/10 [==============================] - 0s 6ms/step - loss: 0.1063 - mae: 0.2480 - val_loss: 0.1268 - val_mae: 0.2726\n", + "Epoch 56/500\n", + "10/10 [==============================] - 0s 5ms/step - loss: 0.1083 - mae: 0.2461 - val_loss: 0.1253 - val_mae: 0.2655\n", + "Epoch 57/500\n", + "10/10 [==============================] - 0s 6ms/step - loss: 0.0997 - mae: 0.2326 - val_loss: 0.1245 - val_mae: 0.2668\n", + "Epoch 58/500\n", + "10/10 [==============================] - 0s 6ms/step - loss: 0.1066 - mae: 0.2470 - val_loss: 0.1235 - val_mae: 0.2644\n", + "Epoch 59/500\n", + "10/10 [==============================] - 0s 7ms/step - loss: 0.1068 - mae: 0.2399 - val_loss: 0.1231 - val_mae: 0.2662\n", + "Epoch 60/500\n", + "10/10 [==============================] - 0s 6ms/step - loss: 0.1055 - mae: 0.2396 - val_loss: 0.1217 - val_mae: 0.2618\n", + "Epoch 61/500\n", + "10/10 [==============================] - 0s 6ms/step - loss: 0.1034 - mae: 0.2338 - val_loss: 0.1207 - val_mae: 0.2606\n", + "Epoch 62/500\n", + "10/10 [==============================] - 0s 5ms/step - loss: 0.1057 - mae: 0.2456 - val_loss: 0.1217 - val_mae: 0.2662\n", + "Epoch 63/500\n", + "10/10 [==============================] - 0s 6ms/step - loss: 0.1067 - mae: 0.2439 - val_loss: 0.1189 - val_mae: 0.2564\n", + "Epoch 64/500\n", + "10/10 [==============================] - 0s 6ms/step - loss: 0.0984 - mae: 0.2338 - val_loss: 0.1185 - val_mae: 0.2593\n", + "Epoch 65/500\n", + "10/10 [==============================] - 0s 5ms/step - loss: 0.0974 - mae: 0.2356 - val_loss: 0.1175 - val_mae: 0.2598\n", + "Epoch 66/500\n", + "10/10 [==============================] - 0s 5ms/step - loss: 0.0982 - mae: 0.2355 - val_loss: 0.1161 - val_mae: 0.2540\n", + "Epoch 67/500\n", + "10/10 [==============================] - 0s 6ms/step - loss: 0.1027 - mae: 0.2385 - val_loss: 0.1159 - val_mae: 0.2556\n", + "Epoch 68/500\n", + "10/10 [==============================] - 0s 5ms/step - loss: 0.1017 - mae: 0.2356 - val_loss: 0.1144 - val_mae: 0.2511\n", + "Epoch 69/500\n", + "10/10 [==============================] - 0s 6ms/step - loss: 0.0948 - mae: 0.2266 - val_loss: 0.1136 - val_mae: 0.2529\n", + "Epoch 70/500\n", + "10/10 [==============================] - 0s 6ms/step - loss: 0.0969 - mae: 0.2316 - val_loss: 0.1127 - val_mae: 0.2516\n", + "Epoch 71/500\n", + "10/10 [==============================] - 0s 5ms/step - loss: 0.0959 - mae: 0.2247 - val_loss: 0.1116 - val_mae: 0.2473\n", + "Epoch 72/500\n", + "10/10 [==============================] - 0s 6ms/step - loss: 0.0988 - mae: 0.2279 - val_loss: 0.1107 - val_mae: 0.2468\n", + "Epoch 73/500\n", + "10/10 [==============================] - 0s 5ms/step - loss: 0.1069 - mae: 0.2367 - val_loss: 0.1097 - val_mae: 0.2461\n", + "Epoch 74/500\n", + "10/10 [==============================] - 0s 5ms/step - loss: 0.0924 - mae: 0.2292 - val_loss: 0.1090 - val_mae: 0.2463\n", + "Epoch 75/500\n", + "10/10 [==============================] - 0s 6ms/step - loss: 0.0967 - mae: 0.2268 - val_loss: 0.1080 - val_mae: 0.2454\n", + "Epoch 76/500\n", + "10/10 [==============================] - 0s 6ms/step - loss: 0.0892 - mae: 0.2194 - val_loss: 0.1070 - val_mae: 0.2415\n", + "Epoch 77/500\n", + "10/10 [==============================] - 0s 5ms/step - loss: 0.0939 - mae: 0.2265 - val_loss: 0.1064 - val_mae: 0.2431\n", + "Epoch 78/500\n", + "10/10 [==============================] - 0s 5ms/step - loss: 0.0874 - mae: 0.2146 - val_loss: 0.1056 - val_mae: 0.2370\n", + "Epoch 79/500\n", + "10/10 [==============================] - 0s 7ms/step - loss: 0.0895 - mae: 0.2188 - val_loss: 0.1050 - val_mae: 0.2413\n", + "Epoch 80/500\n", + "10/10 [==============================] - 0s 6ms/step - loss: 0.0820 - mae: 0.2127 - val_loss: 0.1036 - val_mae: 0.2366\n", + "Epoch 81/500\n", + "10/10 [==============================] - 0s 6ms/step - loss: 0.0890 - mae: 0.2167 - val_loss: 0.1027 - val_mae: 0.2380\n", + "Epoch 82/500\n", + "10/10 [==============================] - 0s 6ms/step - loss: 0.0905 - mae: 0.2191 - val_loss: 0.1018 - val_mae: 0.2359\n", + "Epoch 83/500\n", + "10/10 [==============================] - 0s 6ms/step - loss: 0.0822 - mae: 0.2072 - val_loss: 0.1012 - val_mae: 0.2346\n", + "Epoch 84/500\n", + "10/10 [==============================] - 0s 6ms/step - loss: 0.0870 - mae: 0.2135 - val_loss: 0.1001 - val_mae: 0.2334\n", + "Epoch 85/500\n", + "10/10 [==============================] - 0s 5ms/step - loss: 0.0936 - mae: 0.2228 - val_loss: 0.0993 - val_mae: 0.2310\n", + "Epoch 86/500\n", + "10/10 [==============================] - 0s 6ms/step - loss: 0.0762 - mae: 0.2036 - val_loss: 0.0990 - val_mae: 0.2330\n", + "Epoch 87/500\n", + "10/10 [==============================] - 0s 6ms/step - loss: 0.0781 - mae: 0.2018 - val_loss: 0.0978 - val_mae: 0.2314\n", + "Epoch 88/500\n", + "10/10 [==============================] - 0s 5ms/step - loss: 0.0835 - mae: 0.2111 - val_loss: 0.0969 - val_mae: 0.2289\n", + "Epoch 89/500\n", + "10/10 [==============================] - 0s 5ms/step - loss: 0.0814 - mae: 0.2084 - val_loss: 0.0961 - val_mae: 0.2279\n", + "Epoch 90/500\n", + "10/10 [==============================] - 0s 6ms/step - loss: 0.0791 - mae: 0.2048 - val_loss: 0.0953 - val_mae: 0.2268\n", + "Epoch 91/500\n", + "10/10 [==============================] - 0s 6ms/step - loss: 0.0816 - mae: 0.2088 - val_loss: 0.0948 - val_mae: 0.2288\n", + "Epoch 92/500\n", + "10/10 [==============================] - 0s 6ms/step - loss: 0.0818 - mae: 0.2083 - val_loss: 0.0939 - val_mae: 0.2217\n", + "Epoch 93/500\n", + "10/10 [==============================] - 0s 6ms/step - loss: 0.0735 - mae: 0.1931 - val_loss: 0.0934 - val_mae: 0.2255\n", + "Epoch 94/500\n", + "10/10 [==============================] - 0s 5ms/step - loss: 0.0812 - mae: 0.2072 - val_loss: 0.0921 - val_mae: 0.2217\n", + "Epoch 95/500\n", + "10/10 [==============================] - 0s 5ms/step - loss: 0.0815 - mae: 0.2055 - val_loss: 0.0913 - val_mae: 0.2205\n", + "Epoch 96/500\n", + "10/10 [==============================] - 0s 6ms/step - loss: 0.0766 - mae: 0.2003 - val_loss: 0.0905 - val_mae: 0.2187\n", + "Epoch 97/500\n", + "10/10 [==============================] - 0s 5ms/step - loss: 0.0803 - mae: 0.2053 - val_loss: 0.0898 - val_mae: 0.2202\n", + "Epoch 98/500\n", + "10/10 [==============================] - 0s 5ms/step - loss: 0.0748 - mae: 0.1963 - val_loss: 0.0890 - val_mae: 0.2184\n", + "Epoch 99/500\n", + "10/10 [==============================] - 0s 5ms/step - loss: 0.0754 - mae: 0.1959 - val_loss: 0.0885 - val_mae: 0.2134\n", + "Epoch 100/500\n", + "10/10 [==============================] - 0s 7ms/step - loss: 0.0785 - mae: 0.2020 - val_loss: 0.0876 - val_mae: 0.2138\n", + "Epoch 101/500\n", + "10/10 [==============================] - 0s 19ms/step - loss: 0.0724 - mae: 0.1939 - val_loss: 0.0868 - val_mae: 0.2148\n", + "Epoch 102/500\n", + "10/10 [==============================] - 0s 5ms/step - loss: 0.0742 - mae: 0.1977 - val_loss: 0.0861 - val_mae: 0.2130\n", + "Epoch 103/500\n", + "10/10 [==============================] - 0s 5ms/step - loss: 0.0714 - mae: 0.1943 - val_loss: 0.0855 - val_mae: 0.2151\n", + "Epoch 104/500\n", + "10/10 [==============================] - 0s 6ms/step - loss: 0.0777 - mae: 0.2017 - val_loss: 0.0845 - val_mae: 0.2110\n", + "Epoch 105/500\n", + "10/10 [==============================] - 0s 6ms/step - loss: 0.0689 - mae: 0.1883 - val_loss: 0.0845 - val_mae: 0.2105\n", + "Epoch 106/500\n", + "10/10 [==============================] - 0s 5ms/step - loss: 0.0660 - mae: 0.1848 - val_loss: 0.0832 - val_mae: 0.2100\n", + "Epoch 107/500\n", + "10/10 [==============================] - 0s 6ms/step - loss: 0.0721 - mae: 0.1913 - val_loss: 0.0825 - val_mae: 0.2089\n", + "Epoch 108/500\n", + "10/10 [==============================] - 0s 6ms/step - loss: 0.0819 - mae: 0.2055 - val_loss: 0.0819 - val_mae: 0.2077\n", + "Epoch 109/500\n", + "10/10 [==============================] - 0s 6ms/step - loss: 0.0731 - mae: 0.1940 - val_loss: 0.0812 - val_mae: 0.2072\n", + "Epoch 110/500\n", + "10/10 [==============================] - 0s 6ms/step - loss: 0.0678 - mae: 0.1863 - val_loss: 0.0805 - val_mae: 0.2051\n", + "Epoch 111/500\n", + "10/10 [==============================] - 0s 6ms/step - loss: 0.0688 - mae: 0.1840 - val_loss: 0.0799 - val_mae: 0.2031\n", + "Epoch 112/500\n", + "10/10 [==============================] - 0s 5ms/step - loss: 0.0739 - mae: 0.1874 - val_loss: 0.0792 - val_mae: 0.2031\n", + "Epoch 113/500\n", + "10/10 [==============================] - 0s 6ms/step - loss: 0.0745 - mae: 0.1944 - val_loss: 0.0788 - val_mae: 0.2023\n", + "Epoch 114/500\n", + "10/10 [==============================] - 0s 5ms/step - loss: 0.0704 - mae: 0.1902 - val_loss: 0.0780 - val_mae: 0.2016\n", + "Epoch 115/500\n", + "10/10 [==============================] - 0s 5ms/step - loss: 0.0664 - mae: 0.1864 - val_loss: 0.0774 - val_mae: 0.2016\n", + "Epoch 116/500\n", + "10/10 [==============================] - 0s 6ms/step - loss: 0.0624 - mae: 0.1774 - val_loss: 0.0769 - val_mae: 0.1986\n", + "Epoch 117/500\n", + "10/10 [==============================] - 0s 8ms/step - loss: 0.0688 - mae: 0.1869 - val_loss: 0.0761 - val_mae: 0.1991\n", + "Epoch 118/500\n", + "10/10 [==============================] - 0s 6ms/step - loss: 0.0671 - mae: 0.1801 - val_loss: 0.0756 - val_mae: 0.1975\n", + "Epoch 119/500\n", + "10/10 [==============================] - 0s 6ms/step - loss: 0.0656 - mae: 0.1839 - val_loss: 0.0750 - val_mae: 0.1986\n", + "Epoch 120/500\n", + "10/10 [==============================] - 0s 6ms/step - loss: 0.0554 - mae: 0.1686 - val_loss: 0.0742 - val_mae: 0.1973\n", + "Epoch 121/500\n", + "10/10 [==============================] - 0s 6ms/step - loss: 0.0627 - mae: 0.1815 - val_loss: 0.0737 - val_mae: 0.1971\n", + "Epoch 122/500\n", + "10/10 [==============================] - 0s 5ms/step - loss: 0.0668 - mae: 0.1842 - val_loss: 0.0733 - val_mae: 0.1955\n", + "Epoch 123/500\n", + "10/10 [==============================] - 0s 6ms/step - loss: 0.0683 - mae: 0.1886 - val_loss: 0.0726 - val_mae: 0.1935\n", + "Epoch 124/500\n", + "10/10 [==============================] - 0s 6ms/step - loss: 0.0601 - mae: 0.1734 - val_loss: 0.0726 - val_mae: 0.1966\n", + "Epoch 125/500\n", + "10/10 [==============================] - 0s 6ms/step - loss: 0.0608 - mae: 0.1845 - val_loss: 0.0715 - val_mae: 0.1950\n", + "Epoch 126/500\n", + "10/10 [==============================] - 0s 6ms/step - loss: 0.0644 - mae: 0.1833 - val_loss: 0.0709 - val_mae: 0.1940\n", + "Epoch 127/500\n", + "10/10 [==============================] - 0s 5ms/step - loss: 0.0572 - mae: 0.1708 - val_loss: 0.0703 - val_mae: 0.1916\n", + "Epoch 128/500\n", + "10/10 [==============================] - 0s 6ms/step - loss: 0.0571 - mae: 0.1712 - val_loss: 0.0698 - val_mae: 0.1901\n", + "Epoch 129/500\n", + "10/10 [==============================] - 0s 5ms/step - loss: 0.0593 - mae: 0.1732 - val_loss: 0.0692 - val_mae: 0.1899\n", + "Epoch 130/500\n", + "10/10 [==============================] - 0s 6ms/step - loss: 0.0583 - mae: 0.1718 - val_loss: 0.0688 - val_mae: 0.1914\n", + "Epoch 131/500\n", + "10/10 [==============================] - 0s 6ms/step - loss: 0.0659 - mae: 0.1828 - val_loss: 0.0681 - val_mae: 0.1880\n", + "Epoch 132/500\n", + "10/10 [==============================] - 0s 6ms/step - loss: 0.0601 - mae: 0.1734 - val_loss: 0.0686 - val_mae: 0.1927\n", + "Epoch 133/500\n", + "10/10 [==============================] - 0s 6ms/step - loss: 0.0614 - mae: 0.1796 - val_loss: 0.0675 - val_mae: 0.1877\n", + "Epoch 134/500\n", + "10/10 [==============================] - 0s 5ms/step - loss: 0.0575 - mae: 0.1725 - val_loss: 0.0670 - val_mae: 0.1899\n", + "Epoch 135/500\n", + "10/10 [==============================] - 0s 5ms/step - loss: 0.0638 - mae: 0.1833 - val_loss: 0.0663 - val_mae: 0.1832\n", + "Epoch 136/500\n", + "10/10 [==============================] - 0s 5ms/step - loss: 0.0519 - mae: 0.1603 - val_loss: 0.0661 - val_mae: 0.1886\n", + "Epoch 137/500\n", + "10/10 [==============================] - 0s 7ms/step - loss: 0.0523 - mae: 0.1655 - val_loss: 0.0650 - val_mae: 0.1851\n", + "Epoch 138/500\n", + "10/10 [==============================] - 0s 6ms/step - loss: 0.0499 - mae: 0.1625 - val_loss: 0.0645 - val_mae: 0.1841\n", + "Epoch 139/500\n", + "10/10 [==============================] - 0s 6ms/step - loss: 0.0582 - mae: 0.1712 - val_loss: 0.0640 - val_mae: 0.1838\n", + "Epoch 140/500\n", + "10/10 [==============================] - 0s 6ms/step - loss: 0.0520 - mae: 0.1618 - val_loss: 0.0640 - val_mae: 0.1853\n", + "Epoch 141/500\n", + "10/10 [==============================] - 0s 6ms/step - loss: 0.0509 - mae: 0.1662 - val_loss: 0.0630 - val_mae: 0.1824\n", + "Epoch 142/500\n", + "10/10 [==============================] - 0s 18ms/step - loss: 0.0508 - mae: 0.1620 - val_loss: 0.0625 - val_mae: 0.1832\n", + "Epoch 143/500\n", + "10/10 [==============================] - 0s 7ms/step - loss: 0.0484 - mae: 0.1564 - val_loss: 0.0624 - val_mae: 0.1823\n", + "Epoch 144/500\n", + "10/10 [==============================] - 0s 7ms/step - loss: 0.0526 - mae: 0.1651 - val_loss: 0.0615 - val_mae: 0.1791\n", + "Epoch 145/500\n", + "10/10 [==============================] - 0s 6ms/step - loss: 0.0536 - mae: 0.1649 - val_loss: 0.0611 - val_mae: 0.1809\n", + "Epoch 146/500\n", + "10/10 [==============================] - 0s 6ms/step - loss: 0.0484 - mae: 0.1590 - val_loss: 0.0606 - val_mae: 0.1786\n", + "Epoch 147/500\n", + "10/10 [==============================] - 0s 6ms/step - loss: 0.0555 - mae: 0.1652 - val_loss: 0.0601 - val_mae: 0.1770\n", + "Epoch 148/500\n", + "10/10 [==============================] - 0s 6ms/step - loss: 0.0493 - mae: 0.1578 - val_loss: 0.0597 - val_mae: 0.1778\n", + "Epoch 149/500\n", + "10/10 [==============================] - 0s 6ms/step - loss: 0.0475 - mae: 0.1538 - val_loss: 0.0591 - val_mae: 0.1779\n", + "Epoch 150/500\n", + "10/10 [==============================] - 0s 6ms/step - loss: 0.0484 - mae: 0.1541 - val_loss: 0.0589 - val_mae: 0.1765\n", + "Epoch 151/500\n", + "10/10 [==============================] - 0s 6ms/step - loss: 0.0580 - mae: 0.1705 - val_loss: 0.0584 - val_mae: 0.1741\n", + "Epoch 152/500\n", + "10/10 [==============================] - 0s 5ms/step - loss: 0.0556 - mae: 0.1653 - val_loss: 0.0579 - val_mae: 0.1759\n", + "Epoch 153/500\n", + "10/10 [==============================] - 0s 5ms/step - loss: 0.0491 - mae: 0.1562 - val_loss: 0.0576 - val_mae: 0.1714\n", + "Epoch 154/500\n", + "10/10 [==============================] - 0s 6ms/step - loss: 0.0526 - mae: 0.1610 - val_loss: 0.0569 - val_mae: 0.1756\n", + "Epoch 155/500\n", + "10/10 [==============================] - 0s 6ms/step - loss: 0.0460 - mae: 0.1538 - val_loss: 0.0563 - val_mae: 0.1722\n", + "Epoch 156/500\n", + "10/10 [==============================] - 0s 6ms/step - loss: 0.0530 - mae: 0.1634 - val_loss: 0.0558 - val_mae: 0.1721\n", + "Epoch 157/500\n", + "10/10 [==============================] - 0s 6ms/step - loss: 0.0464 - mae: 0.1522 - val_loss: 0.0556 - val_mae: 0.1710\n", + "Epoch 158/500\n", + "10/10 [==============================] - 0s 6ms/step - loss: 0.0477 - mae: 0.1548 - val_loss: 0.0550 - val_mae: 0.1701\n", + "Epoch 159/500\n", + "10/10 [==============================] - 0s 5ms/step - loss: 0.0447 - mae: 0.1499 - val_loss: 0.0546 - val_mae: 0.1706\n", + "Epoch 160/500\n", + "10/10 [==============================] - 0s 6ms/step - loss: 0.0428 - mae: 0.1500 - val_loss: 0.0541 - val_mae: 0.1686\n", + "Epoch 161/500\n", + "10/10 [==============================] - 0s 6ms/step - loss: 0.0436 - mae: 0.1471 - val_loss: 0.0540 - val_mae: 0.1717\n", + "Epoch 162/500\n", + "10/10 [==============================] - 0s 6ms/step - loss: 0.0400 - mae: 0.1434 - val_loss: 0.0534 - val_mae: 0.1664\n", + "Epoch 163/500\n", + "10/10 [==============================] - 0s 5ms/step - loss: 0.0361 - mae: 0.1362 - val_loss: 0.0540 - val_mae: 0.1735\n", + "Epoch 164/500\n", + "10/10 [==============================] - 0s 6ms/step - loss: 0.0431 - mae: 0.1522 - val_loss: 0.0536 - val_mae: 0.1694\n", + "Epoch 165/500\n", + "10/10 [==============================] - 0s 7ms/step - loss: 0.0444 - mae: 0.1504 - val_loss: 0.0531 - val_mae: 0.1711\n", + "Epoch 166/500\n", + "10/10 [==============================] - 0s 6ms/step - loss: 0.0448 - mae: 0.1549 - val_loss: 0.0516 - val_mae: 0.1643\n", + "Epoch 167/500\n", + "10/10 [==============================] - 0s 6ms/step - loss: 0.0417 - mae: 0.1455 - val_loss: 0.0511 - val_mae: 0.1664\n", + "Epoch 168/500\n", + "10/10 [==============================] - 0s 6ms/step - loss: 0.0454 - mae: 0.1517 - val_loss: 0.0510 - val_mae: 0.1636\n", + "Epoch 169/500\n", + "10/10 [==============================] - 0s 5ms/step - loss: 0.0439 - mae: 0.1469 - val_loss: 0.0506 - val_mae: 0.1663\n", + "Epoch 170/500\n", + "10/10 [==============================] - 0s 6ms/step - loss: 0.0410 - mae: 0.1447 - val_loss: 0.0501 - val_mae: 0.1610\n", + "Epoch 171/500\n", + "10/10 [==============================] - 0s 6ms/step - loss: 0.0404 - mae: 0.1449 - val_loss: 0.0495 - val_mae: 0.1643\n", + "Epoch 172/500\n", + "10/10 [==============================] - 0s 6ms/step - loss: 0.0427 - mae: 0.1489 - val_loss: 0.0491 - val_mae: 0.1626\n", + "Epoch 173/500\n", + "10/10 [==============================] - 0s 6ms/step - loss: 0.0421 - mae: 0.1473 - val_loss: 0.0490 - val_mae: 0.1632\n", + "Epoch 174/500\n", + "10/10 [==============================] - 0s 6ms/step - loss: 0.0460 - mae: 0.1511 - val_loss: 0.0486 - val_mae: 0.1590\n", + "Epoch 175/500\n", + "10/10 [==============================] - 0s 7ms/step - loss: 0.0431 - mae: 0.1461 - val_loss: 0.0480 - val_mae: 0.1602\n", + "Epoch 176/500\n", + "10/10 [==============================] - 0s 6ms/step - loss: 0.0426 - mae: 0.1484 - val_loss: 0.0473 - val_mae: 0.1597\n", + "Epoch 177/500\n", + "10/10 [==============================] - 0s 6ms/step - loss: 0.0380 - mae: 0.1396 - val_loss: 0.0473 - val_mae: 0.1617\n", + "Epoch 178/500\n", + "10/10 [==============================] - 0s 6ms/step - loss: 0.0367 - mae: 0.1401 - val_loss: 0.0467 - val_mae: 0.1582\n", + "Epoch 179/500\n", + "10/10 [==============================] - 0s 5ms/step - loss: 0.0406 - mae: 0.1438 - val_loss: 0.0462 - val_mae: 0.1578\n", + "Epoch 180/500\n", + "10/10 [==============================] - 0s 6ms/step - loss: 0.0378 - mae: 0.1410 - val_loss: 0.0461 - val_mae: 0.1566\n", + "Epoch 181/500\n", + "10/10 [==============================] - 0s 6ms/step - loss: 0.0353 - mae: 0.1352 - val_loss: 0.0457 - val_mae: 0.1591\n", + "Epoch 182/500\n", + "10/10 [==============================] - 0s 6ms/step - loss: 0.0381 - mae: 0.1427 - val_loss: 0.0451 - val_mae: 0.1558\n", + "Epoch 183/500\n", + "10/10 [==============================] - 0s 6ms/step - loss: 0.0372 - mae: 0.1353 - val_loss: 0.0448 - val_mae: 0.1562\n", + "Epoch 184/500\n", + "10/10 [==============================] - 0s 6ms/step - loss: 0.0400 - mae: 0.1428 - val_loss: 0.0442 - val_mae: 0.1548\n", + "Epoch 185/500\n", + "10/10 [==============================] - 0s 8ms/step - loss: 0.0378 - mae: 0.1393 - val_loss: 0.0438 - val_mae: 0.1541\n", + "Epoch 186/500\n", + "10/10 [==============================] - 0s 6ms/step - loss: 0.0379 - mae: 0.1409 - val_loss: 0.0434 - val_mae: 0.1540\n", + "Epoch 187/500\n", + "10/10 [==============================] - 0s 5ms/step - loss: 0.0357 - mae: 0.1355 - val_loss: 0.0431 - val_mae: 0.1535\n", + "Epoch 188/500\n", + "10/10 [==============================] - 0s 6ms/step - loss: 0.0405 - mae: 0.1478 - val_loss: 0.0427 - val_mae: 0.1514\n", + "Epoch 189/500\n", + "10/10 [==============================] - 0s 7ms/step - loss: 0.0375 - mae: 0.1384 - val_loss: 0.0423 - val_mae: 0.1512\n", + "Epoch 190/500\n", + "10/10 [==============================] - 0s 6ms/step - loss: 0.0387 - mae: 0.1432 - val_loss: 0.0425 - val_mae: 0.1541\n", + "Epoch 191/500\n", + "10/10 [==============================] - 0s 5ms/step - loss: 0.0347 - mae: 0.1374 - val_loss: 0.0418 - val_mae: 0.1500\n", + "Epoch 192/500\n", + "10/10 [==============================] - 0s 5ms/step - loss: 0.0336 - mae: 0.1321 - val_loss: 0.0413 - val_mae: 0.1518\n", + "Epoch 193/500\n", + "10/10 [==============================] - 0s 7ms/step - loss: 0.0369 - mae: 0.1356 - val_loss: 0.0409 - val_mae: 0.1506\n", + "Epoch 194/500\n", + "10/10 [==============================] - 0s 19ms/step - loss: 0.0355 - mae: 0.1353 - val_loss: 0.0405 - val_mae: 0.1480\n", + "Epoch 195/500\n", + "10/10 [==============================] - 0s 7ms/step - loss: 0.0396 - mae: 0.1430 - val_loss: 0.0401 - val_mae: 0.1487\n", + "Epoch 196/500\n", + "10/10 [==============================] - 0s 6ms/step - loss: 0.0348 - mae: 0.1352 - val_loss: 0.0403 - val_mae: 0.1510\n", + "Epoch 197/500\n", + "10/10 [==============================] - 0s 7ms/step - loss: 0.0320 - mae: 0.1299 - val_loss: 0.0396 - val_mae: 0.1464\n", + "Epoch 198/500\n", + "10/10 [==============================] - 0s 6ms/step - loss: 0.0349 - mae: 0.1328 - val_loss: 0.0393 - val_mae: 0.1484\n", + "Epoch 199/500\n", + "10/10 [==============================] - 0s 5ms/step - loss: 0.0362 - mae: 0.1389 - val_loss: 0.0387 - val_mae: 0.1446\n", + "Epoch 200/500\n", + "10/10 [==============================] - 0s 5ms/step - loss: 0.0303 - mae: 0.1235 - val_loss: 0.0384 - val_mae: 0.1446\n", + "Epoch 201/500\n", + "10/10 [==============================] - 0s 7ms/step - loss: 0.0326 - mae: 0.1310 - val_loss: 0.0394 - val_mae: 0.1510\n", + "Epoch 202/500\n", + "10/10 [==============================] - 0s 6ms/step - loss: 0.0345 - mae: 0.1359 - val_loss: 0.0389 - val_mae: 0.1460\n", + "Epoch 203/500\n", + "10/10 [==============================] - 0s 5ms/step - loss: 0.0294 - mae: 0.1263 - val_loss: 0.0388 - val_mae: 0.1494\n", + "Epoch 204/500\n", + "10/10 [==============================] - 0s 6ms/step - loss: 0.0336 - mae: 0.1355 - val_loss: 0.0373 - val_mae: 0.1438\n", + "Epoch 205/500\n", + "10/10 [==============================] - 0s 6ms/step - loss: 0.0323 - mae: 0.1316 - val_loss: 0.0368 - val_mae: 0.1418\n", + "Epoch 206/500\n", + "10/10 [==============================] - 0s 6ms/step - loss: 0.0294 - mae: 0.1234 - val_loss: 0.0366 - val_mae: 0.1427\n", + "Epoch 207/500\n", + "10/10 [==============================] - 0s 6ms/step - loss: 0.0330 - mae: 0.1294 - val_loss: 0.0360 - val_mae: 0.1410\n", + "Epoch 208/500\n", + "10/10 [==============================] - 0s 7ms/step - loss: 0.0333 - mae: 0.1314 - val_loss: 0.0357 - val_mae: 0.1417\n", + "Epoch 209/500\n", + "10/10 [==============================] - 0s 5ms/step - loss: 0.0295 - mae: 0.1240 - val_loss: 0.0360 - val_mae: 0.1401\n", + "Epoch 210/500\n", + "10/10 [==============================] - 0s 6ms/step - loss: 0.0313 - mae: 0.1265 - val_loss: 0.0356 - val_mae: 0.1434\n", + "Epoch 211/500\n", + "10/10 [==============================] - 0s 5ms/step - loss: 0.0301 - mae: 0.1257 - val_loss: 0.0348 - val_mae: 0.1396\n", + "Epoch 212/500\n", + "10/10 [==============================] - 0s 6ms/step - loss: 0.0295 - mae: 0.1253 - val_loss: 0.0349 - val_mae: 0.1390\n", + "Epoch 213/500\n", + "10/10 [==============================] - 0s 7ms/step - loss: 0.0314 - mae: 0.1312 - val_loss: 0.0341 - val_mae: 0.1387\n", + "Epoch 214/500\n", + "10/10 [==============================] - 0s 6ms/step - loss: 0.0332 - mae: 0.1322 - val_loss: 0.0341 - val_mae: 0.1381\n", + "Epoch 215/500\n", + "10/10 [==============================] - 0s 5ms/step - loss: 0.0310 - mae: 0.1268 - val_loss: 0.0344 - val_mae: 0.1396\n", + "Epoch 216/500\n", + "10/10 [==============================] - 0s 6ms/step - loss: 0.0310 - mae: 0.1300 - val_loss: 0.0331 - val_mae: 0.1369\n", + "Epoch 217/500\n", + "10/10 [==============================] - 0s 6ms/step - loss: 0.0273 - mae: 0.1208 - val_loss: 0.0329 - val_mae: 0.1352\n", + "Epoch 218/500\n", + "10/10 [==============================] - 0s 6ms/step - loss: 0.0278 - mae: 0.1210 - val_loss: 0.0326 - val_mae: 0.1368\n", + "Epoch 219/500\n", + "10/10 [==============================] - 0s 6ms/step - loss: 0.0308 - mae: 0.1274 - val_loss: 0.0327 - val_mae: 0.1341\n", + "Epoch 220/500\n", + "10/10 [==============================] - 0s 6ms/step - loss: 0.0267 - mae: 0.1191 - val_loss: 0.0319 - val_mae: 0.1340\n", + "Epoch 221/500\n", + "10/10 [==============================] - 0s 7ms/step - loss: 0.0258 - mae: 0.1150 - val_loss: 0.0318 - val_mae: 0.1359\n", + "Epoch 222/500\n", + "10/10 [==============================] - 0s 6ms/step - loss: 0.0307 - mae: 0.1293 - val_loss: 0.0326 - val_mae: 0.1346\n", + "Epoch 223/500\n", + "10/10 [==============================] - 0s 6ms/step - loss: 0.0305 - mae: 0.1271 - val_loss: 0.0320 - val_mae: 0.1380\n", + "Epoch 224/500\n", + "10/10 [==============================] - 0s 6ms/step - loss: 0.0284 - mae: 0.1225 - val_loss: 0.0306 - val_mae: 0.1320\n", + "Epoch 225/500\n", + "10/10 [==============================] - 0s 7ms/step - loss: 0.0305 - mae: 0.1289 - val_loss: 0.0313 - val_mae: 0.1332\n", + "Epoch 226/500\n", + "10/10 [==============================] - 0s 6ms/step - loss: 0.0281 - mae: 0.1235 - val_loss: 0.0309 - val_mae: 0.1355\n", + "Epoch 227/500\n", + "10/10 [==============================] - 0s 6ms/step - loss: 0.0266 - mae: 0.1183 - val_loss: 0.0320 - val_mae: 0.1343\n", + "Epoch 228/500\n", + "10/10 [==============================] - 0s 7ms/step - loss: 0.0306 - mae: 0.1269 - val_loss: 0.0294 - val_mae: 0.1294\n", + "Epoch 229/500\n", + "10/10 [==============================] - 0s 6ms/step - loss: 0.0257 - mae: 0.1170 - val_loss: 0.0316 - val_mae: 0.1385\n", + "Epoch 230/500\n", + "10/10 [==============================] - 0s 6ms/step - loss: 0.0275 - mae: 0.1260 - val_loss: 0.0293 - val_mae: 0.1300\n", + "Epoch 231/500\n", + "10/10 [==============================] - 0s 7ms/step - loss: 0.0268 - mae: 0.1226 - val_loss: 0.0286 - val_mae: 0.1288\n", + "Epoch 232/500\n", + "10/10 [==============================] - 0s 6ms/step - loss: 0.0270 - mae: 0.1187 - val_loss: 0.0291 - val_mae: 0.1270\n", + "Epoch 233/500\n", + "10/10 [==============================] - 0s 6ms/step - loss: 0.0236 - mae: 0.1130 - val_loss: 0.0281 - val_mae: 0.1288\n", + "Epoch 234/500\n", + "10/10 [==============================] - 0s 6ms/step - loss: 0.0263 - mae: 0.1200 - val_loss: 0.0276 - val_mae: 0.1266\n", + "Epoch 235/500\n", + "10/10 [==============================] - 0s 6ms/step - loss: 0.0249 - mae: 0.1160 - val_loss: 0.0282 - val_mae: 0.1251\n", + "Epoch 236/500\n", + "10/10 [==============================] - 0s 18ms/step - loss: 0.0265 - mae: 0.1175 - val_loss: 0.0272 - val_mae: 0.1249\n", + "Epoch 237/500\n", + "10/10 [==============================] - 0s 6ms/step - loss: 0.0263 - mae: 0.1169 - val_loss: 0.0273 - val_mae: 0.1278\n", + "Epoch 238/500\n", + "10/10 [==============================] - 0s 6ms/step - loss: 0.0232 - mae: 0.1113 - val_loss: 0.0269 - val_mae: 0.1241\n", + "Epoch 239/500\n", + "10/10 [==============================] - 0s 7ms/step - loss: 0.0234 - mae: 0.1135 - val_loss: 0.0263 - val_mae: 0.1240\n", + "Epoch 240/500\n", + "10/10 [==============================] - 0s 6ms/step - loss: 0.0234 - mae: 0.1114 - val_loss: 0.0261 - val_mae: 0.1247\n", + "Epoch 241/500\n", + "10/10 [==============================] - 0s 6ms/step - loss: 0.0241 - mae: 0.1151 - val_loss: 0.0263 - val_mae: 0.1229\n", + "Epoch 242/500\n", + "10/10 [==============================] - 0s 6ms/step - loss: 0.0225 - mae: 0.1123 - val_loss: 0.0257 - val_mae: 0.1235\n", + "Epoch 243/500\n", + "10/10 [==============================] - 0s 7ms/step - loss: 0.0217 - mae: 0.1099 - val_loss: 0.0253 - val_mae: 0.1234\n", + "Epoch 244/500\n", + "10/10 [==============================] - 0s 6ms/step - loss: 0.0247 - mae: 0.1167 - val_loss: 0.0249 - val_mae: 0.1222\n", + "Epoch 245/500\n", + "10/10 [==============================] - 0s 6ms/step - loss: 0.0243 - mae: 0.1174 - val_loss: 0.0247 - val_mae: 0.1216\n", + "Epoch 246/500\n", + "10/10 [==============================] - 0s 6ms/step - loss: 0.0207 - mae: 0.1061 - val_loss: 0.0245 - val_mae: 0.1210\n", + "Epoch 247/500\n", + "10/10 [==============================] - 0s 6ms/step - loss: 0.0224 - mae: 0.1101 - val_loss: 0.0244 - val_mae: 0.1203\n", + "Epoch 248/500\n", + "10/10 [==============================] - 0s 6ms/step - loss: 0.0212 - mae: 0.1060 - val_loss: 0.0240 - val_mae: 0.1203\n", + "Epoch 249/500\n", + "10/10 [==============================] - 0s 6ms/step - loss: 0.0200 - mae: 0.1066 - val_loss: 0.0237 - val_mae: 0.1199\n", + "Epoch 250/500\n", + "10/10 [==============================] - 0s 6ms/step - loss: 0.0221 - mae: 0.1084 - val_loss: 0.0241 - val_mae: 0.1182\n", + "Epoch 251/500\n", + "10/10 [==============================] - 0s 7ms/step - loss: 0.0230 - mae: 0.1124 - val_loss: 0.0235 - val_mae: 0.1195\n", + "Epoch 252/500\n", + "10/10 [==============================] - 0s 6ms/step - loss: 0.0206 - mae: 0.1072 - val_loss: 0.0237 - val_mae: 0.1199\n", + "Epoch 253/500\n", + "10/10 [==============================] - 0s 6ms/step - loss: 0.0205 - mae: 0.1058 - val_loss: 0.0235 - val_mae: 0.1192\n", + "Epoch 254/500\n", + "10/10 [==============================] - 0s 6ms/step - loss: 0.0221 - mae: 0.1101 - val_loss: 0.0230 - val_mae: 0.1177\n", + "Epoch 255/500\n", + "10/10 [==============================] - 0s 6ms/step - loss: 0.0215 - mae: 0.1077 - val_loss: 0.0225 - val_mae: 0.1171\n", + "Epoch 256/500\n", + "10/10 [==============================] - 0s 6ms/step - loss: 0.0202 - mae: 0.1054 - val_loss: 0.0235 - val_mae: 0.1206\n", + "Epoch 257/500\n", + "10/10 [==============================] - 0s 6ms/step - loss: 0.0205 - mae: 0.1071 - val_loss: 0.0220 - val_mae: 0.1163\n", + "Epoch 258/500\n", + "10/10 [==============================] - 0s 6ms/step - loss: 0.0208 - mae: 0.1110 - val_loss: 0.0218 - val_mae: 0.1163\n", + "Epoch 259/500\n", + "10/10 [==============================] - 0s 6ms/step - loss: 0.0224 - mae: 0.1096 - val_loss: 0.0219 - val_mae: 0.1151\n", + "Epoch 260/500\n", + "10/10 [==============================] - 0s 6ms/step - loss: 0.0206 - mae: 0.1071 - val_loss: 0.0222 - val_mae: 0.1178\n", + "Epoch 261/500\n", + "10/10 [==============================] - 0s 7ms/step - loss: 0.0209 - mae: 0.1093 - val_loss: 0.0214 - val_mae: 0.1157\n", + "Epoch 262/500\n", + "10/10 [==============================] - 0s 6ms/step - loss: 0.0211 - mae: 0.1089 - val_loss: 0.0226 - val_mae: 0.1142\n", + "Epoch 263/500\n", + "10/10 [==============================] - 0s 7ms/step - loss: 0.0201 - mae: 0.1063 - val_loss: 0.0208 - val_mae: 0.1141\n", + "Epoch 264/500\n", + "10/10 [==============================] - 0s 7ms/step - loss: 0.0186 - mae: 0.1007 - val_loss: 0.0207 - val_mae: 0.1134\n", + "Epoch 265/500\n", + "10/10 [==============================] - 0s 6ms/step - loss: 0.0195 - mae: 0.1037 - val_loss: 0.0209 - val_mae: 0.1129\n", + "Epoch 266/500\n", + "10/10 [==============================] - 0s 6ms/step - loss: 0.0208 - mae: 0.1072 - val_loss: 0.0207 - val_mae: 0.1124\n", + "Epoch 267/500\n", + "10/10 [==============================] - 0s 6ms/step - loss: 0.0186 - mae: 0.1016 - val_loss: 0.0216 - val_mae: 0.1167\n", + "Epoch 268/500\n", + "10/10 [==============================] - 0s 5ms/step - loss: 0.0193 - mae: 0.1050 - val_loss: 0.0206 - val_mae: 0.1119\n", + "Epoch 269/500\n", + "10/10 [==============================] - 0s 7ms/step - loss: 0.0193 - mae: 0.1063 - val_loss: 0.0198 - val_mae: 0.1116\n", + "Epoch 270/500\n", + "10/10 [==============================] - 0s 6ms/step - loss: 0.0175 - mae: 0.0996 - val_loss: 0.0197 - val_mae: 0.1114\n", + "Epoch 271/500\n", + "10/10 [==============================] - 0s 6ms/step - loss: 0.0194 - mae: 0.1037 - val_loss: 0.0196 - val_mae: 0.1107\n", + "Epoch 272/500\n", + "10/10 [==============================] - 0s 7ms/step - loss: 0.0191 - mae: 0.1032 - val_loss: 0.0194 - val_mae: 0.1106\n", + "Epoch 273/500\n", + "10/10 [==============================] - 0s 6ms/step - loss: 0.0182 - mae: 0.1026 - val_loss: 0.0193 - val_mae: 0.1106\n", + "Epoch 274/500\n", + "10/10 [==============================] - 0s 6ms/step - loss: 0.0189 - mae: 0.1042 - val_loss: 0.0197 - val_mae: 0.1105\n", + "Epoch 275/500\n", + "10/10 [==============================] - 0s 7ms/step - loss: 0.0192 - mae: 0.1077 - val_loss: 0.0189 - val_mae: 0.1098\n", + "Epoch 276/500\n", + "10/10 [==============================] - 0s 7ms/step - loss: 0.0199 - mae: 0.1072 - val_loss: 0.0204 - val_mae: 0.1141\n", + "Epoch 277/500\n", + "10/10 [==============================] - 0s 6ms/step - loss: 0.0197 - mae: 0.1104 - val_loss: 0.0195 - val_mae: 0.1116\n", + "Epoch 278/500\n", + "10/10 [==============================] - 0s 6ms/step - loss: 0.0194 - mae: 0.1074 - val_loss: 0.0192 - val_mae: 0.1091\n", + "Epoch 279/500\n", + "10/10 [==============================] - 0s 7ms/step - loss: 0.0179 - mae: 0.1027 - val_loss: 0.0183 - val_mae: 0.1081\n", + "Epoch 280/500\n", + "10/10 [==============================] - 0s 6ms/step - loss: 0.0177 - mae: 0.1019 - val_loss: 0.0182 - val_mae: 0.1076\n", + "Epoch 281/500\n", + "10/10 [==============================] - 0s 6ms/step - loss: 0.0170 - mae: 0.0987 - val_loss: 0.0181 - val_mae: 0.1079\n", + "Epoch 282/500\n", + "10/10 [==============================] - 0s 6ms/step - loss: 0.0167 - mae: 0.0997 - val_loss: 0.0182 - val_mae: 0.1069\n", + "Epoch 283/500\n", + "10/10 [==============================] - 0s 6ms/step - loss: 0.0182 - mae: 0.1022 - val_loss: 0.0177 - val_mae: 0.1069\n", + "Epoch 284/500\n", + "10/10 [==============================] - 0s 7ms/step - loss: 0.0199 - mae: 0.1073 - val_loss: 0.0192 - val_mae: 0.1088\n", + "Epoch 285/500\n", + "10/10 [==============================] - 0s 6ms/step - loss: 0.0179 - mae: 0.1033 - val_loss: 0.0177 - val_mae: 0.1061\n", + "Epoch 286/500\n", + "10/10 [==============================] - 0s 21ms/step - loss: 0.0171 - mae: 0.1013 - val_loss: 0.0173 - val_mae: 0.1060\n", + "Epoch 287/500\n", + "10/10 [==============================] - 0s 6ms/step - loss: 0.0179 - mae: 0.1022 - val_loss: 0.0174 - val_mae: 0.1053\n", + "Epoch 288/500\n", + "10/10 [==============================] - 0s 6ms/step - loss: 0.0185 - mae: 0.1055 - val_loss: 0.0182 - val_mae: 0.1070\n", + "Epoch 289/500\n", + "10/10 [==============================] - 0s 6ms/step - loss: 0.0182 - mae: 0.1038 - val_loss: 0.0176 - val_mae: 0.1047\n", + "Epoch 290/500\n", + "10/10 [==============================] - 0s 6ms/step - loss: 0.0173 - mae: 0.1032 - val_loss: 0.0177 - val_mae: 0.1069\n", + "Epoch 291/500\n", + "10/10 [==============================] - 0s 6ms/step - loss: 0.0176 - mae: 0.1015 - val_loss: 0.0169 - val_mae: 0.1044\n", + "Epoch 292/500\n", + "10/10 [==============================] - 0s 7ms/step - loss: 0.0173 - mae: 0.1022 - val_loss: 0.0171 - val_mae: 0.1044\n", + "Epoch 293/500\n", + "10/10 [==============================] - 0s 7ms/step - loss: 0.0152 - mae: 0.0967 - val_loss: 0.0166 - val_mae: 0.1037\n", + "Epoch 294/500\n", + "10/10 [==============================] - 0s 6ms/step - loss: 0.0165 - mae: 0.0998 - val_loss: 0.0164 - val_mae: 0.1027\n", + "Epoch 295/500\n", + "10/10 [==============================] - 0s 7ms/step - loss: 0.0165 - mae: 0.0986 - val_loss: 0.0163 - val_mae: 0.1024\n", + "Epoch 296/500\n", + "10/10 [==============================] - 0s 6ms/step - loss: 0.0165 - mae: 0.0979 - val_loss: 0.0163 - val_mae: 0.1026\n", + "Epoch 297/500\n", + "10/10 [==============================] - 0s 8ms/step - loss: 0.0162 - mae: 0.0987 - val_loss: 0.0162 - val_mae: 0.1020\n", + "Epoch 298/500\n", + "10/10 [==============================] - 0s 6ms/step - loss: 0.0173 - mae: 0.1011 - val_loss: 0.0160 - val_mae: 0.1021\n", + "Epoch 299/500\n", + "10/10 [==============================] - 0s 7ms/step - loss: 0.0170 - mae: 0.1019 - val_loss: 0.0165 - val_mae: 0.1029\n", + "Epoch 300/500\n", + "10/10 [==============================] - 0s 6ms/step - loss: 0.0164 - mae: 0.0996 - val_loss: 0.0155 - val_mae: 0.1005\n", + "Epoch 301/500\n", + "10/10 [==============================] - 0s 7ms/step - loss: 0.0161 - mae: 0.0987 - val_loss: 0.0156 - val_mae: 0.1005\n", + "Epoch 302/500\n", + "10/10 [==============================] - 0s 6ms/step - loss: 0.0147 - mae: 0.0952 - val_loss: 0.0162 - val_mae: 0.1029\n", + "Epoch 303/500\n", + "10/10 [==============================] - 0s 6ms/step - loss: 0.0161 - mae: 0.0999 - val_loss: 0.0171 - val_mae: 0.1027\n", + "Epoch 304/500\n", + "10/10 [==============================] - 0s 7ms/step - loss: 0.0164 - mae: 0.0990 - val_loss: 0.0161 - val_mae: 0.1007\n", + "Epoch 305/500\n", + "10/10 [==============================] - 0s 7ms/step - loss: 0.0161 - mae: 0.0975 - val_loss: 0.0156 - val_mae: 0.1001\n", + "Epoch 306/500\n", + "10/10 [==============================] - 0s 7ms/step - loss: 0.0159 - mae: 0.0982 - val_loss: 0.0167 - val_mae: 0.1039\n", + "Epoch 307/500\n", + "10/10 [==============================] - 0s 6ms/step - loss: 0.0150 - mae: 0.0951 - val_loss: 0.0162 - val_mae: 0.1023\n", + "Epoch 308/500\n", + "10/10 [==============================] - 0s 8ms/step - loss: 0.0175 - mae: 0.1035 - val_loss: 0.0148 - val_mae: 0.0985\n", + "Epoch 309/500\n", + "10/10 [==============================] - 0s 6ms/step - loss: 0.0152 - mae: 0.0970 - val_loss: 0.0187 - val_mae: 0.1059\n", + "Epoch 310/500\n", + "10/10 [==============================] - 0s 6ms/step - loss: 0.0172 - mae: 0.1025 - val_loss: 0.0154 - val_mae: 0.1006\n", + "Epoch 311/500\n", + "10/10 [==============================] - 0s 6ms/step - loss: 0.0162 - mae: 0.0999 - val_loss: 0.0180 - val_mae: 0.1055\n", + "Epoch 312/500\n", + "10/10 [==============================] - 0s 7ms/step - loss: 0.0162 - mae: 0.1019 - val_loss: 0.0149 - val_mae: 0.0978\n", + "Epoch 313/500\n", + "10/10 [==============================] - 0s 8ms/step - loss: 0.0151 - mae: 0.0991 - val_loss: 0.0157 - val_mae: 0.0996\n", + "Epoch 314/500\n", + "10/10 [==============================] - 0s 8ms/step - loss: 0.0150 - mae: 0.0951 - val_loss: 0.0148 - val_mae: 0.0983\n", + "Epoch 315/500\n", + "10/10 [==============================] - 0s 7ms/step - loss: 0.0151 - mae: 0.0974 - val_loss: 0.0150 - val_mae: 0.0988\n", + "Epoch 316/500\n", + "10/10 [==============================] - 0s 6ms/step - loss: 0.0156 - mae: 0.0987 - val_loss: 0.0146 - val_mae: 0.0977\n", + "Epoch 317/500\n", + "10/10 [==============================] - 0s 6ms/step - loss: 0.0160 - mae: 0.0987 - val_loss: 0.0151 - val_mae: 0.0971\n", + "Epoch 318/500\n", + "10/10 [==============================] - 0s 6ms/step - loss: 0.0154 - mae: 0.0973 - val_loss: 0.0141 - val_mae: 0.0960\n", + "Epoch 319/500\n", + "10/10 [==============================] - 0s 6ms/step - loss: 0.0143 - mae: 0.0940 - val_loss: 0.0141 - val_mae: 0.0963\n", + "Epoch 320/500\n", + "10/10 [==============================] - 0s 8ms/step - loss: 0.0131 - mae: 0.0904 - val_loss: 0.0143 - val_mae: 0.0965\n", + "Epoch 321/500\n", + "10/10 [==============================] - 0s 6ms/step - loss: 0.0142 - mae: 0.0952 - val_loss: 0.0144 - val_mae: 0.0972\n", + "Epoch 322/500\n", + "10/10 [==============================] - 0s 6ms/step - loss: 0.0146 - mae: 0.0954 - val_loss: 0.0147 - val_mae: 0.0964\n", + "Epoch 323/500\n", + "10/10 [==============================] - 0s 6ms/step - loss: 0.0143 - mae: 0.0941 - val_loss: 0.0147 - val_mae: 0.0973\n", + "Epoch 324/500\n", + "10/10 [==============================] - 0s 7ms/step - loss: 0.0149 - mae: 0.0952 - val_loss: 0.0150 - val_mae: 0.0984\n", + "Epoch 325/500\n", + "10/10 [==============================] - 0s 6ms/step - loss: 0.0154 - mae: 0.0975 - val_loss: 0.0137 - val_mae: 0.0945\n", + "Epoch 326/500\n", + "10/10 [==============================] - 0s 18ms/step - loss: 0.0135 - mae: 0.0925 - val_loss: 0.0137 - val_mae: 0.0944\n", + "Epoch 327/500\n", + "10/10 [==============================] - 0s 6ms/step - loss: 0.0134 - mae: 0.0913 - val_loss: 0.0142 - val_mae: 0.0962\n", + "Epoch 328/500\n", + "10/10 [==============================] - 0s 6ms/step - loss: 0.0143 - mae: 0.0955 - val_loss: 0.0135 - val_mae: 0.0937\n", + "Epoch 329/500\n", + "10/10 [==============================] - 0s 7ms/step - loss: 0.0142 - mae: 0.0938 - val_loss: 0.0145 - val_mae: 0.0957\n", + "Epoch 330/500\n", + "10/10 [==============================] - 0s 7ms/step - loss: 0.0150 - mae: 0.0955 - val_loss: 0.0160 - val_mae: 0.1011\n", + "Epoch 331/500\n", + "10/10 [==============================] - 0s 7ms/step - loss: 0.0162 - mae: 0.0996 - val_loss: 0.0136 - val_mae: 0.0940\n", + "Epoch 332/500\n", + "10/10 [==============================] - 0s 6ms/step - loss: 0.0135 - mae: 0.0913 - val_loss: 0.0134 - val_mae: 0.0933\n", + "Epoch 333/500\n", + "10/10 [==============================] - 0s 6ms/step - loss: 0.0133 - mae: 0.0900 - val_loss: 0.0141 - val_mae: 0.0953\n", + "Epoch 334/500\n", + "10/10 [==============================] - 0s 6ms/step - loss: 0.0144 - mae: 0.0954 - val_loss: 0.0132 - val_mae: 0.0929\n", + "Epoch 335/500\n", + "10/10 [==============================] - 0s 6ms/step - loss: 0.0134 - mae: 0.0895 - val_loss: 0.0144 - val_mae: 0.0956\n", + "Epoch 336/500\n", + "10/10 [==============================] - 0s 5ms/step - loss: 0.0142 - mae: 0.0930 - val_loss: 0.0136 - val_mae: 0.0939\n", + "Epoch 337/500\n", + "10/10 [==============================] - 0s 7ms/step - loss: 0.0135 - mae: 0.0924 - val_loss: 0.0130 - val_mae: 0.0922\n", + "Epoch 338/500\n", + "10/10 [==============================] - 0s 7ms/step - loss: 0.0133 - mae: 0.0918 - val_loss: 0.0130 - val_mae: 0.0920\n", + "Epoch 339/500\n", + "10/10 [==============================] - 0s 6ms/step - loss: 0.0132 - mae: 0.0921 - val_loss: 0.0131 - val_mae: 0.0920\n", + "Epoch 340/500\n", + "10/10 [==============================] - 0s 6ms/step - loss: 0.0125 - mae: 0.0890 - val_loss: 0.0134 - val_mae: 0.0928\n", + "Epoch 341/500\n", + "10/10 [==============================] - 0s 6ms/step - loss: 0.0139 - mae: 0.0937 - val_loss: 0.0135 - val_mae: 0.0929\n", + "Epoch 342/500\n", + "10/10 [==============================] - 0s 6ms/step - loss: 0.0141 - mae: 0.0948 - val_loss: 0.0131 - val_mae: 0.0919\n", + "Epoch 343/500\n", + "10/10 [==============================] - 0s 8ms/step - loss: 0.0154 - mae: 0.0979 - val_loss: 0.0143 - val_mae: 0.0955\n", + "Epoch 344/500\n", + "10/10 [==============================] - 0s 6ms/step - loss: 0.0139 - mae: 0.0927 - val_loss: 0.0136 - val_mae: 0.0932\n", + "Epoch 345/500\n", + "10/10 [==============================] - 0s 7ms/step - loss: 0.0136 - mae: 0.0914 - val_loss: 0.0127 - val_mae: 0.0906\n", + "Epoch 346/500\n", + "10/10 [==============================] - 0s 7ms/step - loss: 0.0139 - mae: 0.0944 - val_loss: 0.0142 - val_mae: 0.0951\n", + "Epoch 347/500\n", + "10/10 [==============================] - 0s 7ms/step - loss: 0.0129 - mae: 0.0892 - val_loss: 0.0129 - val_mae: 0.0910\n", + "Epoch 348/500\n", + "10/10 [==============================] - 0s 7ms/step - loss: 0.0148 - mae: 0.0956 - val_loss: 0.0134 - val_mae: 0.0925\n", + "Epoch 349/500\n", + "10/10 [==============================] - 0s 6ms/step - loss: 0.0127 - mae: 0.0886 - val_loss: 0.0141 - val_mae: 0.0943\n", + "Epoch 350/500\n", + "10/10 [==============================] - 0s 7ms/step - loss: 0.0130 - mae: 0.0918 - val_loss: 0.0130 - val_mae: 0.0915\n", + "Epoch 351/500\n", + "10/10 [==============================] - 0s 6ms/step - loss: 0.0144 - mae: 0.0945 - val_loss: 0.0131 - val_mae: 0.0920\n", + "Epoch 352/500\n", + "10/10 [==============================] - 0s 7ms/step - loss: 0.0128 - mae: 0.0893 - val_loss: 0.0127 - val_mae: 0.0907\n", + "Epoch 353/500\n", + "10/10 [==============================] - 0s 6ms/step - loss: 0.0132 - mae: 0.0926 - val_loss: 0.0135 - val_mae: 0.0927\n", + "Epoch 354/500\n", + "10/10 [==============================] - 0s 6ms/step - loss: 0.0136 - mae: 0.0945 - val_loss: 0.0127 - val_mae: 0.0904\n", + "Epoch 355/500\n", + "10/10 [==============================] - 0s 6ms/step - loss: 0.0127 - mae: 0.0893 - val_loss: 0.0136 - val_mae: 0.0937\n", + "Epoch 356/500\n", + "10/10 [==============================] - 0s 7ms/step - loss: 0.0137 - mae: 0.0936 - val_loss: 0.0135 - val_mae: 0.0937\n", + "Epoch 357/500\n", + "10/10 [==============================] - 0s 7ms/step - loss: 0.0144 - mae: 0.0973 - val_loss: 0.0128 - val_mae: 0.0906\n", + "Epoch 358/500\n", + "10/10 [==============================] - 0s 7ms/step - loss: 0.0130 - mae: 0.0906 - val_loss: 0.0125 - val_mae: 0.0893\n", + "Epoch 359/500\n", + "10/10 [==============================] - 0s 6ms/step - loss: 0.0126 - mae: 0.0896 - val_loss: 0.0125 - val_mae: 0.0899\n", + "Epoch 360/500\n", + "10/10 [==============================] - 0s 6ms/step - loss: 0.0136 - mae: 0.0931 - val_loss: 0.0134 - val_mae: 0.0935\n", + "Epoch 361/500\n", + "10/10 [==============================] - 0s 6ms/step - loss: 0.0139 - mae: 0.0942 - val_loss: 0.0124 - val_mae: 0.0892\n", + "Epoch 362/500\n", + "10/10 [==============================] - 0s 6ms/step - loss: 0.0133 - mae: 0.0922 - val_loss: 0.0125 - val_mae: 0.0894\n", + "Epoch 363/500\n", + "10/10 [==============================] - 0s 7ms/step - loss: 0.0127 - mae: 0.0894 - val_loss: 0.0126 - val_mae: 0.0904\n", + "Epoch 364/500\n", + "10/10 [==============================] - 0s 7ms/step - loss: 0.0126 - mae: 0.0910 - val_loss: 0.0124 - val_mae: 0.0895\n", + "Epoch 365/500\n", + "10/10 [==============================] - 0s 6ms/step - loss: 0.0121 - mae: 0.0883 - val_loss: 0.0123 - val_mae: 0.0892\n", + "Epoch 366/500\n", + "10/10 [==============================] - 0s 7ms/step - loss: 0.0118 - mae: 0.0867 - val_loss: 0.0124 - val_mae: 0.0896\n", + "Epoch 367/500\n", + "10/10 [==============================] - 0s 6ms/step - loss: 0.0122 - mae: 0.0872 - val_loss: 0.0121 - val_mae: 0.0881\n", + "Epoch 368/500\n", + "10/10 [==============================] - 0s 6ms/step - loss: 0.0118 - mae: 0.0865 - val_loss: 0.0125 - val_mae: 0.0903\n", + "Epoch 369/500\n", + "10/10 [==============================] - 0s 6ms/step - loss: 0.0131 - mae: 0.0908 - val_loss: 0.0121 - val_mae: 0.0885\n", + "Epoch 370/500\n", + "10/10 [==============================] - 0s 7ms/step - loss: 0.0131 - mae: 0.0910 - val_loss: 0.0120 - val_mae: 0.0879\n", + "Epoch 371/500\n", + "10/10 [==============================] - 0s 7ms/step - loss: 0.0127 - mae: 0.0897 - val_loss: 0.0129 - val_mae: 0.0906\n", + "Epoch 372/500\n", + "10/10 [==============================] - 0s 6ms/step - loss: 0.0137 - mae: 0.0928 - val_loss: 0.0129 - val_mae: 0.0904\n", + "Epoch 373/500\n", + "10/10 [==============================] - 0s 7ms/step - loss: 0.0128 - mae: 0.0900 - val_loss: 0.0123 - val_mae: 0.0886\n", + "Epoch 374/500\n", + "10/10 [==============================] - 0s 8ms/step - loss: 0.0125 - mae: 0.0898 - val_loss: 0.0125 - val_mae: 0.0901\n", + "Epoch 375/500\n", + "10/10 [==============================] - 0s 7ms/step - loss: 0.0131 - mae: 0.0911 - val_loss: 0.0120 - val_mae: 0.0877\n", + "Epoch 376/500\n", + "10/10 [==============================] - 0s 18ms/step - loss: 0.0121 - mae: 0.0889 - val_loss: 0.0121 - val_mae: 0.0878\n", + "Epoch 377/500\n", + "10/10 [==============================] - 0s 7ms/step - loss: 0.0112 - mae: 0.0845 - val_loss: 0.0125 - val_mae: 0.0889\n", + "Epoch 378/500\n", + "10/10 [==============================] - 0s 7ms/step - loss: 0.0123 - mae: 0.0869 - val_loss: 0.0122 - val_mae: 0.0880\n", + "Epoch 379/500\n", + "10/10 [==============================] - 0s 7ms/step - loss: 0.0123 - mae: 0.0886 - val_loss: 0.0128 - val_mae: 0.0911\n", + "Epoch 380/500\n", + "10/10 [==============================] - 0s 6ms/step - loss: 0.0125 - mae: 0.0904 - val_loss: 0.0119 - val_mae: 0.0878\n", + "Epoch 381/500\n", + "10/10 [==============================] - 0s 7ms/step - loss: 0.0125 - mae: 0.0897 - val_loss: 0.0124 - val_mae: 0.0897\n", + "Epoch 382/500\n", + "10/10 [==============================] - 0s 7ms/step - loss: 0.0127 - mae: 0.0904 - val_loss: 0.0119 - val_mae: 0.0872\n", + "Epoch 383/500\n", + "10/10 [==============================] - 0s 6ms/step - loss: 0.0125 - mae: 0.0896 - val_loss: 0.0118 - val_mae: 0.0872\n", + "Epoch 384/500\n", + "10/10 [==============================] - 0s 7ms/step - loss: 0.0127 - mae: 0.0895 - val_loss: 0.0120 - val_mae: 0.0881\n", + "Epoch 385/500\n", + "10/10 [==============================] - 0s 7ms/step - loss: 0.0127 - mae: 0.0897 - val_loss: 0.0120 - val_mae: 0.0884\n", + "Epoch 386/500\n", + "10/10 [==============================] - 0s 7ms/step - loss: 0.0120 - mae: 0.0875 - val_loss: 0.0120 - val_mae: 0.0882\n", + "Epoch 387/500\n", + "10/10 [==============================] - 0s 7ms/step - loss: 0.0124 - mae: 0.0889 - val_loss: 0.0118 - val_mae: 0.0874\n", + "Epoch 388/500\n", + "10/10 [==============================] - 0s 7ms/step - loss: 0.0135 - mae: 0.0933 - val_loss: 0.0132 - val_mae: 0.0915\n", + "Epoch 389/500\n", + "10/10 [==============================] - 0s 6ms/step - loss: 0.0133 - mae: 0.0926 - val_loss: 0.0125 - val_mae: 0.0892\n", + "Epoch 390/500\n", + "10/10 [==============================] - 0s 7ms/step - loss: 0.0130 - mae: 0.0903 - val_loss: 0.0130 - val_mae: 0.0909\n", + "Epoch 391/500\n", + "10/10 [==============================] - 0s 6ms/step - loss: 0.0126 - mae: 0.0899 - val_loss: 0.0126 - val_mae: 0.0896\n", + "Epoch 392/500\n", + "10/10 [==============================] - 0s 7ms/step - loss: 0.0132 - mae: 0.0906 - val_loss: 0.0121 - val_mae: 0.0878\n", + "Epoch 393/500\n", + "10/10 [==============================] - 0s 7ms/step - loss: 0.0129 - mae: 0.0902 - val_loss: 0.0122 - val_mae: 0.0887\n", + "Epoch 394/500\n", + "10/10 [==============================] - 0s 7ms/step - loss: 0.0123 - mae: 0.0901 - val_loss: 0.0117 - val_mae: 0.0870\n", + "Epoch 395/500\n", + "10/10 [==============================] - 0s 6ms/step - loss: 0.0126 - mae: 0.0909 - val_loss: 0.0120 - val_mae: 0.0882\n", + "Epoch 396/500\n", + "10/10 [==============================] - 0s 7ms/step - loss: 0.0125 - mae: 0.0885 - val_loss: 0.0117 - val_mae: 0.0869\n", + "Epoch 397/500\n", + "10/10 [==============================] - 0s 6ms/step - loss: 0.0123 - mae: 0.0885 - val_loss: 0.0120 - val_mae: 0.0883\n", + "Epoch 398/500\n", + "10/10 [==============================] - 0s 6ms/step - loss: 0.0130 - mae: 0.0907 - val_loss: 0.0120 - val_mae: 0.0882\n", + "Epoch 399/500\n", + "10/10 [==============================] - 0s 8ms/step - loss: 0.0128 - mae: 0.0918 - val_loss: 0.0122 - val_mae: 0.0889\n", + "Epoch 400/500\n", + "10/10 [==============================] - 0s 7ms/step - loss: 0.0124 - mae: 0.0901 - val_loss: 0.0119 - val_mae: 0.0878\n", + "Epoch 401/500\n", + "10/10 [==============================] - 0s 6ms/step - loss: 0.0116 - mae: 0.0862 - val_loss: 0.0117 - val_mae: 0.0868\n", + "Epoch 402/500\n", + "10/10 [==============================] - 0s 6ms/step - loss: 0.0126 - mae: 0.0900 - val_loss: 0.0119 - val_mae: 0.0878\n", + "Epoch 403/500\n", + "10/10 [==============================] - 0s 7ms/step - loss: 0.0118 - mae: 0.0861 - val_loss: 0.0124 - val_mae: 0.0896\n", + "Epoch 404/500\n", + "10/10 [==============================] - 0s 6ms/step - loss: 0.0125 - mae: 0.0893 - val_loss: 0.0118 - val_mae: 0.0875\n", + "Epoch 405/500\n", + "10/10 [==============================] - 0s 7ms/step - loss: 0.0119 - mae: 0.0881 - val_loss: 0.0121 - val_mae: 0.0879\n", + "Epoch 406/500\n", + "10/10 [==============================] - 0s 8ms/step - loss: 0.0122 - mae: 0.0884 - val_loss: 0.0115 - val_mae: 0.0862\n", + "Epoch 407/500\n", + "10/10 [==============================] - 0s 6ms/step - loss: 0.0115 - mae: 0.0864 - val_loss: 0.0121 - val_mae: 0.0880\n", + "Epoch 408/500\n", + "10/10 [==============================] - 0s 6ms/step - loss: 0.0127 - mae: 0.0901 - val_loss: 0.0121 - val_mae: 0.0886\n", + "Epoch 409/500\n", + "10/10 [==============================] - 0s 6ms/step - loss: 0.0123 - mae: 0.0882 - val_loss: 0.0127 - val_mae: 0.0906\n", + "Epoch 410/500\n", + "10/10 [==============================] - 0s 7ms/step - loss: 0.0130 - mae: 0.0898 - val_loss: 0.0119 - val_mae: 0.0875\n", + "Epoch 411/500\n", + "10/10 [==============================] - 0s 8ms/step - loss: 0.0127 - mae: 0.0903 - val_loss: 0.0126 - val_mae: 0.0896\n", + "Epoch 412/500\n", + "10/10 [==============================] - 0s 7ms/step - loss: 0.0119 - mae: 0.0872 - val_loss: 0.0115 - val_mae: 0.0864\n", + "Epoch 413/500\n", + "10/10 [==============================] - 0s 7ms/step - loss: 0.0117 - mae: 0.0867 - val_loss: 0.0127 - val_mae: 0.0896\n", + "Epoch 414/500\n", + "10/10 [==============================] - 0s 6ms/step - loss: 0.0117 - mae: 0.0874 - val_loss: 0.0127 - val_mae: 0.0898\n", + "Epoch 415/500\n", + "10/10 [==============================] - 0s 8ms/step - loss: 0.0134 - mae: 0.0921 - val_loss: 0.0120 - val_mae: 0.0876\n", + "Epoch 416/500\n", + "10/10 [==============================] - 0s 7ms/step - loss: 0.0139 - mae: 0.0941 - val_loss: 0.0117 - val_mae: 0.0869\n", + "Epoch 417/500\n", + "10/10 [==============================] - 0s 18ms/step - loss: 0.0122 - mae: 0.0889 - val_loss: 0.0120 - val_mae: 0.0879\n", + "Epoch 418/500\n", + "10/10 [==============================] - 0s 7ms/step - loss: 0.0120 - mae: 0.0868 - val_loss: 0.0120 - val_mae: 0.0882\n", + "Epoch 419/500\n", + "10/10 [==============================] - 0s 7ms/step - loss: 0.0122 - mae: 0.0884 - val_loss: 0.0119 - val_mae: 0.0877\n", + "Epoch 420/500\n", + "10/10 [==============================] - 0s 6ms/step - loss: 0.0123 - mae: 0.0895 - val_loss: 0.0127 - val_mae: 0.0902\n", + "Epoch 421/500\n", + "10/10 [==============================] - 0s 6ms/step - loss: 0.0126 - mae: 0.0901 - val_loss: 0.0128 - val_mae: 0.0911\n", + "Epoch 422/500\n", + "10/10 [==============================] - 0s 7ms/step - loss: 0.0134 - mae: 0.0921 - val_loss: 0.0117 - val_mae: 0.0868\n", + "Epoch 423/500\n", + "10/10 [==============================] - 0s 7ms/step - loss: 0.0119 - mae: 0.0880 - val_loss: 0.0118 - val_mae: 0.0871\n", + "Epoch 424/500\n", + "10/10 [==============================] - 0s 7ms/step - loss: 0.0127 - mae: 0.0910 - val_loss: 0.0117 - val_mae: 0.0868\n", + "Epoch 425/500\n", + "10/10 [==============================] - 0s 6ms/step - loss: 0.0120 - mae: 0.0881 - val_loss: 0.0117 - val_mae: 0.0869\n", + "Epoch 426/500\n", + "10/10 [==============================] - 0s 7ms/step - loss: 0.0127 - mae: 0.0896 - val_loss: 0.0118 - val_mae: 0.0870\n", + "Epoch 427/500\n", + "10/10 [==============================] - 0s 6ms/step - loss: 0.0117 - mae: 0.0870 - val_loss: 0.0116 - val_mae: 0.0865\n", + "Epoch 428/500\n", + "10/10 [==============================] - 0s 7ms/step - loss: 0.0126 - mae: 0.0909 - val_loss: 0.0119 - val_mae: 0.0874\n", + "Epoch 429/500\n", + "10/10 [==============================] - 0s 7ms/step - loss: 0.0115 - mae: 0.0848 - val_loss: 0.0119 - val_mae: 0.0877\n", + "Epoch 430/500\n", + "10/10 [==============================] - 0s 6ms/step - loss: 0.0118 - mae: 0.0878 - val_loss: 0.0122 - val_mae: 0.0883\n", + "Epoch 431/500\n", + "10/10 [==============================] - 0s 7ms/step - loss: 0.0123 - mae: 0.0889 - val_loss: 0.0118 - val_mae: 0.0871\n", + "Epoch 432/500\n", + "10/10 [==============================] - 0s 7ms/step - loss: 0.0124 - mae: 0.0885 - val_loss: 0.0120 - val_mae: 0.0878\n", + "Epoch 433/500\n", + "10/10 [==============================] - 0s 7ms/step - loss: 0.0124 - mae: 0.0891 - val_loss: 0.0116 - val_mae: 0.0863\n", + "Epoch 434/500\n", + "10/10 [==============================] - 0s 6ms/step - loss: 0.0117 - mae: 0.0879 - val_loss: 0.0119 - val_mae: 0.0877\n", + "Epoch 435/500\n", + "10/10 [==============================] - 0s 7ms/step - loss: 0.0124 - mae: 0.0891 - val_loss: 0.0126 - val_mae: 0.0901\n", + "Epoch 436/500\n", + "10/10 [==============================] - 0s 7ms/step - loss: 0.0125 - mae: 0.0887 - val_loss: 0.0126 - val_mae: 0.0901\n", + "Epoch 437/500\n", + "10/10 [==============================] - 0s 7ms/step - loss: 0.0129 - mae: 0.0913 - val_loss: 0.0123 - val_mae: 0.0886\n", + "Epoch 438/500\n", + "10/10 [==============================] - 0s 6ms/step - loss: 0.0120 - mae: 0.0868 - val_loss: 0.0116 - val_mae: 0.0868\n", + "Epoch 439/500\n", + "10/10 [==============================] - 0s 7ms/step - loss: 0.0128 - mae: 0.0894 - val_loss: 0.0132 - val_mae: 0.0911\n", + "Epoch 440/500\n", + "10/10 [==============================] - 0s 7ms/step - loss: 0.0111 - mae: 0.0849 - val_loss: 0.0115 - val_mae: 0.0865\n", + "Epoch 441/500\n", + "10/10 [==============================] - 0s 6ms/step - loss: 0.0121 - mae: 0.0866 - val_loss: 0.0118 - val_mae: 0.0875\n", + "Epoch 442/500\n", + "10/10 [==============================] - 0s 7ms/step - loss: 0.0116 - mae: 0.0868 - val_loss: 0.0119 - val_mae: 0.0875\n", + "Epoch 443/500\n", + "10/10 [==============================] - 0s 6ms/step - loss: 0.0118 - mae: 0.0861 - val_loss: 0.0124 - val_mae: 0.0891\n", + "Epoch 444/500\n", + "10/10 [==============================] - 0s 6ms/step - loss: 0.0125 - mae: 0.0883 - val_loss: 0.0121 - val_mae: 0.0883\n", + "Epoch 445/500\n", + "10/10 [==============================] - 0s 6ms/step - loss: 0.0114 - mae: 0.0854 - val_loss: 0.0122 - val_mae: 0.0884\n", + "Epoch 446/500\n", + "10/10 [==============================] - 0s 6ms/step - loss: 0.0126 - mae: 0.0893 - val_loss: 0.0116 - val_mae: 0.0866\n", + "Epoch 447/500\n", + "10/10 [==============================] - 0s 6ms/step - loss: 0.0125 - mae: 0.0879 - val_loss: 0.0122 - val_mae: 0.0885\n", + "Epoch 448/500\n", + "10/10 [==============================] - 0s 6ms/step - loss: 0.0121 - mae: 0.0878 - val_loss: 0.0117 - val_mae: 0.0868\n", + "Epoch 449/500\n", + "10/10 [==============================] - 0s 6ms/step - loss: 0.0113 - mae: 0.0865 - val_loss: 0.0117 - val_mae: 0.0866\n", + "Epoch 450/500\n", + "10/10 [==============================] - 0s 6ms/step - loss: 0.0112 - mae: 0.0855 - val_loss: 0.0133 - val_mae: 0.0930\n", + "Epoch 451/500\n", + "10/10 [==============================] - 0s 6ms/step - loss: 0.0137 - mae: 0.0931 - val_loss: 0.0121 - val_mae: 0.0883\n", + "Epoch 452/500\n", + "10/10 [==============================] - 0s 6ms/step - loss: 0.0117 - mae: 0.0861 - val_loss: 0.0114 - val_mae: 0.0862\n", + "Epoch 453/500\n", + "10/10 [==============================] - 0s 6ms/step - loss: 0.0113 - mae: 0.0855 - val_loss: 0.0117 - val_mae: 0.0870\n", + "Epoch 454/500\n", + "10/10 [==============================] - 0s 7ms/step - loss: 0.0135 - mae: 0.0928 - val_loss: 0.0118 - val_mae: 0.0870\n", + "Epoch 455/500\n", + "10/10 [==============================] - 0s 6ms/step - loss: 0.0125 - mae: 0.0884 - val_loss: 0.0114 - val_mae: 0.0860\n", + "Epoch 456/500\n", + "10/10 [==============================] - 0s 6ms/step - loss: 0.0125 - mae: 0.0904 - val_loss: 0.0117 - val_mae: 0.0869\n", + "Epoch 457/500\n", + "10/10 [==============================] - 0s 6ms/step - loss: 0.0124 - mae: 0.0883 - val_loss: 0.0114 - val_mae: 0.0862\n", + "Epoch 458/500\n", + "10/10 [==============================] - 0s 7ms/step - loss: 0.0122 - mae: 0.0873 - val_loss: 0.0117 - val_mae: 0.0869\n", + "Epoch 459/500\n", + "10/10 [==============================] - 0s 6ms/step - loss: 0.0122 - mae: 0.0887 - val_loss: 0.0116 - val_mae: 0.0865\n", + "Epoch 460/500\n", + "10/10 [==============================] - 0s 5ms/step - loss: 0.0125 - mae: 0.0894 - val_loss: 0.0118 - val_mae: 0.0874\n", + "Epoch 461/500\n", + "10/10 [==============================] - 0s 6ms/step - loss: 0.0115 - mae: 0.0857 - val_loss: 0.0115 - val_mae: 0.0863\n", + "Epoch 462/500\n", + "10/10 [==============================] - 0s 6ms/step - loss: 0.0115 - mae: 0.0862 - val_loss: 0.0117 - val_mae: 0.0874\n", + "Epoch 463/500\n", + "10/10 [==============================] - 0s 7ms/step - loss: 0.0118 - mae: 0.0880 - val_loss: 0.0119 - val_mae: 0.0876\n", + "Epoch 464/500\n", + "10/10 [==============================] - 0s 7ms/step - loss: 0.0132 - mae: 0.0928 - val_loss: 0.0116 - val_mae: 0.0865\n", + "Epoch 465/500\n", + "10/10 [==============================] - 0s 6ms/step - loss: 0.0135 - mae: 0.0922 - val_loss: 0.0116 - val_mae: 0.0865\n", + "Epoch 466/500\n", + "10/10 [==============================] - 0s 7ms/step - loss: 0.0121 - mae: 0.0885 - val_loss: 0.0115 - val_mae: 0.0863\n", + "Epoch 467/500\n", + "10/10 [==============================] - 0s 7ms/step - loss: 0.0117 - mae: 0.0868 - val_loss: 0.0116 - val_mae: 0.0871\n", + "Epoch 468/500\n", + "10/10 [==============================] - 0s 7ms/step - loss: 0.0117 - mae: 0.0861 - val_loss: 0.0117 - val_mae: 0.0872\n", + "Epoch 469/500\n", + "10/10 [==============================] - 0s 17ms/step - loss: 0.0121 - mae: 0.0896 - val_loss: 0.0115 - val_mae: 0.0863\n", + "Epoch 470/500\n", + "10/10 [==============================] - 0s 7ms/step - loss: 0.0123 - mae: 0.0896 - val_loss: 0.0125 - val_mae: 0.0895\n", + "Epoch 471/500\n", + "10/10 [==============================] - 0s 6ms/step - loss: 0.0126 - mae: 0.0916 - val_loss: 0.0114 - val_mae: 0.0862\n", + "Epoch 472/500\n", + "10/10 [==============================] - 0s 7ms/step - loss: 0.0113 - mae: 0.0854 - val_loss: 0.0114 - val_mae: 0.0861\n", + "Epoch 473/500\n", + "10/10 [==============================] - 0s 7ms/step - loss: 0.0116 - mae: 0.0862 - val_loss: 0.0129 - val_mae: 0.0904\n", + "Epoch 474/500\n", + "10/10 [==============================] - 0s 7ms/step - loss: 0.0138 - mae: 0.0954 - val_loss: 0.0127 - val_mae: 0.0901\n", + "Epoch 475/500\n", + "10/10 [==============================] - 0s 6ms/step - loss: 0.0134 - mae: 0.0923 - val_loss: 0.0118 - val_mae: 0.0877\n", + "Epoch 476/500\n", + "10/10 [==============================] - 0s 7ms/step - loss: 0.0121 - mae: 0.0877 - val_loss: 0.0115 - val_mae: 0.0862\n", + "Epoch 477/500\n", + "10/10 [==============================] - 0s 7ms/step - loss: 0.0117 - mae: 0.0865 - val_loss: 0.0117 - val_mae: 0.0874\n", + "Epoch 478/500\n", + "10/10 [==============================] - 0s 7ms/step - loss: 0.0130 - mae: 0.0899 - val_loss: 0.0113 - val_mae: 0.0859\n", + "Epoch 479/500\n", + "10/10 [==============================] - 0s 6ms/step - loss: 0.0131 - mae: 0.0907 - val_loss: 0.0115 - val_mae: 0.0864\n", + "Epoch 480/500\n", + "10/10 [==============================] - 0s 7ms/step - loss: 0.0122 - mae: 0.0877 - val_loss: 0.0118 - val_mae: 0.0877\n", + "Epoch 481/500\n", + "10/10 [==============================] - 0s 7ms/step - loss: 0.0119 - mae: 0.0866 - val_loss: 0.0115 - val_mae: 0.0864\n", + "Epoch 482/500\n", + "10/10 [==============================] - 0s 6ms/step - loss: 0.0118 - mae: 0.0874 - val_loss: 0.0115 - val_mae: 0.0866\n", + "Epoch 483/500\n", + "10/10 [==============================] - 0s 6ms/step - loss: 0.0121 - mae: 0.0873 - val_loss: 0.0123 - val_mae: 0.0890\n", + "Epoch 484/500\n", + "10/10 [==============================] - 0s 6ms/step - loss: 0.0127 - mae: 0.0898 - val_loss: 0.0116 - val_mae: 0.0868\n", + "Epoch 485/500\n", + "10/10 [==============================] - 0s 9ms/step - loss: 0.0112 - mae: 0.0845 - val_loss: 0.0123 - val_mae: 0.0890\n", + "Epoch 486/500\n", + "10/10 [==============================] - 0s 7ms/step - loss: 0.0123 - mae: 0.0876 - val_loss: 0.0114 - val_mae: 0.0860\n", + "Epoch 487/500\n", + "10/10 [==============================] - 0s 7ms/step - loss: 0.0116 - mae: 0.0852 - val_loss: 0.0118 - val_mae: 0.0873\n", + "Epoch 488/500\n", + "10/10 [==============================] - 0s 6ms/step - loss: 0.0114 - mae: 0.0859 - val_loss: 0.0114 - val_mae: 0.0859\n", + "Epoch 489/500\n", + "10/10 [==============================] - 0s 6ms/step - loss: 0.0117 - mae: 0.0871 - val_loss: 0.0130 - val_mae: 0.0917\n", + "Epoch 490/500\n", + "10/10 [==============================] - 0s 6ms/step - loss: 0.0135 - mae: 0.0933 - val_loss: 0.0131 - val_mae: 0.0920\n", + "Epoch 491/500\n", + "10/10 [==============================] - 0s 8ms/step - loss: 0.0120 - mae: 0.0879 - val_loss: 0.0119 - val_mae: 0.0881\n", + "Epoch 492/500\n", + "10/10 [==============================] - 0s 6ms/step - loss: 0.0115 - mae: 0.0859 - val_loss: 0.0115 - val_mae: 0.0863\n", + "Epoch 493/500\n", + "10/10 [==============================] - 0s 6ms/step - loss: 0.0113 - mae: 0.0854 - val_loss: 0.0114 - val_mae: 0.0862\n", + "Epoch 494/500\n", + "10/10 [==============================] - 0s 6ms/step - loss: 0.0114 - mae: 0.0866 - val_loss: 0.0116 - val_mae: 0.0868\n", + "Epoch 495/500\n", + "10/10 [==============================] - 0s 6ms/step - loss: 0.0119 - mae: 0.0875 - val_loss: 0.0113 - val_mae: 0.0860\n", + "Epoch 496/500\n", + "10/10 [==============================] - 0s 6ms/step - loss: 0.0118 - mae: 0.0861 - val_loss: 0.0114 - val_mae: 0.0863\n", + "Epoch 497/500\n", + "10/10 [==============================] - 0s 6ms/step - loss: 0.0107 - mae: 0.0818 - val_loss: 0.0115 - val_mae: 0.0864\n", + "Epoch 498/500\n", + "10/10 [==============================] - 0s 7ms/step - loss: 0.0123 - mae: 0.0894 - val_loss: 0.0114 - val_mae: 0.0862\n", + "Epoch 499/500\n", + "10/10 [==============================] - 0s 6ms/step - loss: 0.0126 - mae: 0.0895 - val_loss: 0.0113 - val_mae: 0.0857\n", + "Epoch 500/500\n", + "10/10 [==============================] - 0s 6ms/step - loss: 0.0121 - mae: 0.0882 - val_loss: 0.0115 - val_mae: 0.0865\n" + ], + "name": "stdout" + }, + { + "output_type": "stream", + "text": [ + "/usr/local/lib/python3.6/dist-packages/tensorflow/python/keras/engine/training.py:2325: UserWarning: `Model.state_updates` will be removed in a future version. This property should not be used in TensorFlow 2.0, as `updates` are applied automatically.\n", + " warnings.warn('`Model.state_updates` will be removed in a future version. '\n", + "/usr/local/lib/python3.6/dist-packages/tensorflow/python/keras/engine/base_layer.py:1397: UserWarning: `layer.updates` will be removed in a future version. This property should not be used in TensorFlow 2.0, as `updates` are applied automatically.\n", + " warnings.warn('`layer.updates` will be removed in a future version. '\n" + ], + "name": "stderr" + }, + { + "output_type": "stream", + "text": [ + "WARNING:tensorflow:FOR KERAS USERS: The object that you are saving contains one or more Keras models or layers. If you are loading the SavedModel with `tf.keras.models.load_model`, continue reading (otherwise, you may ignore the following instructions). Please change your code to save with `tf.keras.models.save_model` or `model.save`, and confirm that the file \"keras.metadata\" exists in the export directory. In the future, Keras will only load the SavedModels that have this file. In other words, `tf.saved_model.save` will no longer write SavedModels that can be recovered as Keras models (this will apply in TF 2.5).\n", + "\n", + "FOR DEVS: If you are overwriting _tracking_metadata in your class, this property has been used to save metadata in the SavedModel. The metadta field will be deprecated soon, so please move the metadata to a different file.\n", + "INFO:tensorflow:Assets written to: models/model/assets\n" + ], + "name": "stdout" + } + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "Mc_CQu2_IvOP" + }, + "source": [ + "### 3. Plot Metrics\n", + "Each training epoch, the model prints out its loss and mean absolute error for training and validation. You can read this in the output above (note that your exact numbers may differ): \n", + "\n", + "```\n", + "Epoch 500/500\n", + "10/10 [==============================] - 0s 10ms/step - loss: 0.0121 - mae: 0.0882 - val_loss: 0.0115 - val_mae: 0.0865\n", + "```\n", + "\n", + "You can see that we've already got a huge improvement - validation loss has dropped from 0.15 to 0.01, and validation MAE has dropped from 0.33 to 0.08.\n", + "\n", + "The following cell will print the same graphs we used to evaluate our original model, but showing our new training history:" + ] + }, + { + "cell_type": "code", + "metadata": { + "id": "SYHGswAJJgrC", + "outputId": "0b4baed5-9565-45c7-9fcc-2fd59a86d438", + "colab": { + "base_uri": "https://localhost:8080/", + "height": 297 + } + }, + "source": [ + "# Draw a graph of the loss, which is the distance between\n", + "# the predicted and actual values during training and validation.\n", + "train_loss = history.history['loss']\n", + "val_loss = history.history['val_loss']\n", + "\n", + "epochs = range(1, len(train_loss) + 1)\n", + "\n", + "# Exclude the first few epochs so the graph is easier to read\n", + "SKIP = 100\n", + "\n", + "plt.figure(figsize=(10, 4))\n", + "plt.subplot(1, 2, 1)\n", + "\n", + "plt.plot(epochs[SKIP:], train_loss[SKIP:], 'g.', label='Training loss')\n", + "plt.plot(epochs[SKIP:], val_loss[SKIP:], 'b.', label='Validation loss')\n", + "plt.title('Training and validation loss')\n", + "plt.xlabel('Epochs')\n", + "plt.ylabel('Loss')\n", + "plt.legend()\n", + "\n", + "plt.subplot(1, 2, 2)\n", + "\n", + "# Draw a graph of mean absolute error, which is another way of\n", + "# measuring the amount of error in the prediction.\n", + "train_mae = history.history['mae']\n", + "val_mae = history.history['val_mae']\n", + "\n", + "plt.plot(epochs[SKIP:], train_mae[SKIP:], 'g.', label='Training MAE')\n", + "plt.plot(epochs[SKIP:], val_mae[SKIP:], 'b.', label='Validation MAE')\n", + "plt.title('Training and validation mean absolute error')\n", + "plt.xlabel('Epochs')\n", + "plt.ylabel('MAE')\n", + "plt.legend()\n", + "\n", + "plt.tight_layout()" + ], + "execution_count": null, + "outputs": [ + { + "output_type": "display_data", + "data": { + "image/png": "iVBORw0KGgoAAAANSUhEUgAAAsMAAAEYCAYAAAC5nfszAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAALEgAACxIB0t1+/AAAADh0RVh0U29mdHdhcmUAbWF0cGxvdGxpYiB2ZXJzaW9uMy4yLjIsIGh0dHA6Ly9tYXRwbG90bGliLm9yZy+WH4yJAAAgAElEQVR4nOzdfXxU1bXw8d+aSUJQVFrEWjEYrOIrkvBmj1QYxLbiG1jRq481IuoAiq1ai9pq5Yper8BTuVZUQERpFdT6SFFRK0gANS0CRikKt2ojoGI1FsEKJJlZzx/7TDIzmSRDyOR1ffnkkzn7vMyemWSzs8/aa4uqYowxxhhjTEcUaOkKGGOMMcYY01KsM2yMMcYYYzos6wwbY4wxxpgOyzrDxhhjjDGmw7LOsDHGGGOM6bCsM2yMMcYYYzos6wybeonIiyJyWVMf25JEpExETs/AdVVEjvIfPyQit6VzbCOe5xIR+XNj61nPdUMisrWpr2tMc7N2a6+u26bbrbZKRB4VkTub+JpjROS1prxmR5HV0hUwTU9Evo7b3A/YA0T87XGq+ni611LVEZk4tr1T1fFNcR0RyQf+AWSrapV/7ceBtD9DY9oCa7danrVbHYeIjAGuVNUftHRdWgPrDLdDqtol9lhEynA/8EuTjxORrFhDZYwxLcnaLWPap1S/s3v7e5zp33sLk+hAYrfBReQmEdkGzBORb4nI8yLyuYj8y398eNw5xSJypf94jIi8JiLT/WP/ISIjGnlsLxFZKSI7RWSpiMwUkT/UUe906jhFRF73r/dnETk4bv+lIvKRiJSLyK/reX9OFpFtIhKMKztPRN7xHw8SkRIR2S4in4rI/SKSU8e1Em6Bicgv/XM+EZGxSceeJSJvicgOEdkiIpPjdq/0v28Xka9FxEu+FSYip4jImyLylf/9lHTfm/qIyHH++dtFZIOInBu370wRede/5scicqNffrD/+WwXkS9FZJWIWDtjGs3aLWu36mu34n4+JonIP/36jvLbqP/126FfxR0fEJGbReQD/719SkS+Hbf/af/9/Mr/rE9Ien9misgLfr3+KiLfq+ezqfNavoNF5BX/WitE5Aj/PBGRe/3Xs0NE1ovIif6+g0Rkvv9z9ZGI3Cop2lgRyRcX1pIVV1YsIleKyHHAQ4Dnfz7b/f2d/J/9zSLymbiwmc71vL6xIvKe//P9cqz+/j4VkWtE5O/A3yX173EnEZnh/3x94j/ulPS5Vh9fVz2agv0n1fEcCnwbOAII434G5vnbPYFdwP31nH8ysAk4GJgKzBURacSxTwCrgW7AZODSep4znTr+H+By4BAgB4h1zo4HHvSvf5j/fIeTgqr+Ffg3cFrSdZ/wH0eA6/3X4wHDgavrqTd+Hc7w6/ND4GggOe7v30AR0BU4C5ggIqP8fUP8711VtYuqliRd+9vAC8B9/mv7LfCCiHRLeg213psG6pwNPAf82T/vWuBxETnGP2Qu7tb1AcCJwKt++S+ArUB34DvArwBb893sK2u3rN2qr906FMgFegC/AeYAPwX6A6cCt4lIL//Ya4FRwFDce/svYGbctV70X+8hwDpqh3ZcBPwn8C3gfeCueurV0LUuAabgPpvSuP0/wr2HvYGDgAuBcn/f7/yyI/3XUIR7n9Kmqu8B44ES//Pp6u/6b/85C4CjqHk/axGRkbj2/Se49n4VsCDpsFG436fj/e3k3+NfA9/3n68vMAi4Ne785OMzR1Xtqx1/AWXA6f7jEFAB5NZzfAHwr7jtYtztSoAxwPtx+/bDdXQO3Ztjcf8xVAH7xe3/A/CHNF9TqjreGrd9NfCS//g3wMK4ffv778HpdVz7TuAR//EBuAb/iDqOvQ54Nm5bgaP8x48Cd/qPHwH+O+643vHHprjuDOBe/3G+f2xW3P4xwGv+40uB1UnnlwBjGnpvUjxvCNjqPz4V2AYE4vYvACb7jzcD44ADk65xB/Cnul6bfdlXOl/Wblm7tZft1i4gGPf6FTg57pi1wCj/8XvA8Lh93wUq4+sat6+rf62D4t6fh+P2nwlsTPPzT3Wt+M+4C+4PlzzcHzb/i+soxrfBQf/n4Pi4snFAcYr3ONVnUEziz/prcfvE/7n5XlyZB/yjjtfzInBF3HYA+Cb2c+c/92lJn1PC7zHwAXBm3PaPgbJ0f++b8stGhjuez1V1d2xDRPYTkVn+7ZYduNtbXSXulluSbbEHqvqN/7DLXh57GPBlXBnAlroqnGYdt8U9/iauTofFX1tV/03NX9ipPAH8xL9V8xNgnap+5Nejt7hbndv8evwX7i/6hiTUAfgo6fWdLCLL/dteX+H+Yk8rlMG/9kdJZR/h/qKPqeu9abDOqhqt47rn4/4j+Mi/vef55dNwoyV/FpEPReTm9F6GMfWydsvarfrarXJVjU223OV//yxu/664848AnhUXNrId1zmOAN8RkaCI/Le4EIoduD/KIPF1pVWvNK8V/xl/DXwJHKaqr+LuIswE/ikis0XkQP/cbBLfu+T3rbG64/74Wxv33rzkl6dyBPA/ccd+ietQx9cl+fcj4feY2j8HH/lldR2fMdYZ7niSb1n/AjgG91f0gdTc3qrrFmJT+BT4tojsF1eWV8/x+1LHT+Ov7T9nt7oOVtV3cb+QI0i81QjutuVG4Gi/Hr9qTB1wI0zxngAWA3mqehAulit23YZCDD7BNUrxegIfp1Gvhq6blxSLVn1dVX1TVUfibv8tAp7yy3eq6i9U9UjgXOAGERm+j3Uxxtota7eayhZghKp2jfvKVdWPce/dSFxIyEG40VVo3M9VOteK/4y74EICPgFQ1ftUtT8uxKA38EvgC9wodvx7V9f79m//e/zP66Fxj5M/oy9wfzScEPe+HKRxE1uTbMGFysW/j51V9Y16niN5O/nnoKdfVtfxGWOdYXMA7hdgux/HdXumn9AfsVgDTBaRHH9U8ZwM1fGPwNki8gNxk0buoOGf+yeAn+P+83o6qR47gK9F5FhgQpp1eAoYIyLH+/+pJdf/ANyI024RGYRrRGM+B6K4+LBUlgC9ReT/iEiWiPwHrvF8Ps261eWvuFGPSSKSLSIh3Ge00P/MLhGRg1S1EveeRAFE5GwROcqPsfwKN+ISTf0UxjSatVu1WbuVnoeAu6Rmslp3P/4V3GvagxuF3w83it5Y6VzrzLjPeArwF1XdIiID/ZH3bFyndjcQ9Ue/n/Lrf4D/Gm7AheskUNXPcZ3kn/qj1GOB+Ml+nwGH+8+NfxdwDnCviBwCICI9ROTHdby+h4BbxJ8UKG5i3wV78f6AC7271f8MDsaFB6WckJpp1hk2M4DOuL8K/4K7LdIcLsHFI5Xj4t2exDUcqTS6jqq6AbgG9x/Fp7jJEg0tLLEANzHhVVX9Iq78RlyDvxPXaDyZZh1e9F/Dq7gQgleTDrkauENEduIag6fizv0GN0Hjdf921PeTrl0OnI0bhSoHJgFnJ9V7r6lqBe4/+hG49/0BoEhVN/qHXAqU+bf/xuM+T3CTRZYCX+NiAB9Q1eX7UhdjUrB2q7YO326l6X9wI9p/9uv+F9wkL4D5uBH2j4F3/X2Nlc61nsD9kfElbrLfT/3yA3Gf1b/8a5TjQtDATQD8N/Ah8Jp/jUfqqMNVuBHlcuAEIH7U9lVgA7BNRGLv+024z/ovftu+FHd3oxZVfRa4BzdAsgP4G+7/i71xJ+4PzHeA9bhJhk26EEm6xA9UNqZFiciTuIkIGR/hMcaYpmDtljHtg40Mmxbh3wb6nricj2fgYqsWtXS9jDGmLtZuGdM+2Qp0pqUcCvw/3KSQrcAEVX2rZatkjDH1snbLmHbIwiSMMcYYY0yHldEwCRE5Q0Q2icj7qfKNiluK70l//19FJN8vzxGReeKWIHzbn8lujDHGGGNMk8pYmISfWHwmbinHrcCbIrLYz4cYcwVuRZ6jROQi3MzE/8DNgERV+/gpPl4UkYFJCwAkOPjggzU/Pz9Dr8YYY/bd2rVrv1DVupLYNyk/pvV/cKtWPayq/520/wbgStyqap8DY1X1IxEpwOWmPRCXGu8uVa03A4G1v8aY1q6+9jeTMcODcMtafgggIgtxkw3iO8Mjceu7g8ureL+fn/R4/DQuqvpPf3WTAbg14VPKz89nzZo1Tf0ajDGmyYhI8qpbmXqedAYj3gIGqOo3IjIBmIobjPgGl0bv7yJyGG5FqpdVdXtdz2ftrzGmtauv/c1kmEQPEpfi20rtJQOrj1HVKlyS/m7A28C5fjLuXrj8e7VW+hGRsIisEZE1n3/+eQZegjHGtEnVgxF+zujYYEQ1VV0et7TwX4DD/fL/VdW/+48/Af5J3UuyGmNMm9daU6s9gus8r8El/X4Dd7sugarOVtUBqjqge3drq40xxpfOYES8K4AXkwv9lcVygA9S7LPBCGNMu5DJMImPSRzNPZza62fHjtkqIlm49bvL1aW4uD52kIi8AfxvButqjDEdkoj8FBeGNjSp/LvA74HLUs3XUNXZwGyAAQMGWFoiY0yblcnO8JvA0X6Yw8fARSSuXQ5uScTLcMu2jsYtI6n+Ouiiqv8WkR8CVUmxbsZ0OJWVlWzdupXdu3e3dFVMA3Jzczn88MPJzs5uqSqkMxiBiJwO/BoYqqp74soPBF4Afq2q+7IkrTFtmrW7bU9j2t+MdYZVtUpEJgIv42YzP6KqG0TkDmCNqi4G5gK/F5H3cWtzX+SffgjwsohEcQ34pZmqpzFtxdatWznggAPIz8/HzTM1rZGqUl5eztatW+nVq1dLVaPBwQgRKQRmAWeo6j/jynOAZ4H5qvrH5quyMa2PtbttS2Pb34yuQKeqS4AlSWW/iXu8G7ggxXllwDGZrJsxbc3u3butQW4DRIRu3brRknG0aQ5GTAO6AE/7P1ObVfVc4EJgCNBNRMb4lxyjqqXN/TqMaWnW7rYtjW1/O/RyzCUlUFwMoRB4XkvXxpiGWYPcNrSGzymNwYjT6zjvD8AfMls7x9pg0xa0ht9nk77GfF4dtjNcUgLDh0NFBeTkwLJl1hgbY0xzmT0bJk6ESAQ6dbI22BjTclprarWMKy52HeFIBHbvhvnzW7pGxrRu5eXlFBQUUFBQwKGHHkqPHj2qtysqKuo9d82aNfzsZz9r8DlOOeWUJqlrcXExZ599dpNcyzS9khK45hqorIRoFPbscW2yMSZRW2t3RYSHH364uqy0tBQRYfr06dVlVVVVdO/enZtvvjnh/FAoxDHHHFP9+kaPHt0k9UpHhx0ZDoUgGHSdYVWYNw+Kimxkwpi6dOvWjdJSFzY6efJkunTpwo033li9v6qqiqys1E3KgAEDGDBgQIPP8cYbbzRNZU2rVlzsOsExwaBrk40xidpau3viiSfy1FNPceWVVwKwYMEC+vbtm3DMK6+8Qu/evXn66ae5++67E8IaHn/88bTq3NQ67Miw58HYsTXblZU2MmHan5ItJdy96m5KtpRk5Ppjxoxh/PjxnHzyyUyaNInVq1fjeR6FhYWccsopbNq0CUgcqZ08eTJjx44lFApx5JFHct9991Vfr0uXLtXHh0IhRo8ezbHHHssll1yCSz8OS5Ys4dhjj6V///787Gc/a3AE+Msvv2TUqFGcdNJJfP/73+edd94BYMWKFdUjEIWFhezcuZNPP/2UIUOGUFBQwIknnsiqVaua/D0zruMb+/87GIT777eBCNN+dOR294gjjmD37t189tlnqCovvfQSI0aMSDhmwYIF/PznP6dnz56UlGTmPdpbHXZkGKCwsOZxNArdurVcXYxpaiVbShg+fzgVkQpygjksK1qGl9f0PY6tW7fyxhtvEAwG2bFjB6tWrSIrK4ulS5fyq1/9imeeeabWORs3bmT58uXs3LmTY445hgkTJtTKCfnWW2+xYcMGDjvsMAYPHszrr7/OgAEDGDduHCtXrqRXr15cfPHFDdbv9ttvp7CwkEWLFvHqq69SVFREaWkp06dPZ+bMmQwePJivv/6a3NxcZs+ezY9//GN+/etfE4lE+Oabbxq8vmkcEfeVlQV9+rR0bYxpGtbuwujRo3n66acpLCykX79+dOrUqXrf7t27Wbp0KbNmzWL79u0sWLAgIUzjkksuoXPnzgD88Ic/ZNq0afvyNqWtw44MA5SXQ8B/B0Tgrbdatj7GNKXismIqIhVENEJFpILisuKMPM8FF1xAMBgE4KuvvuKCCy7gxBNP5Prrr2fDhg0pzznrrLPo1KkTBx98MIcccgifffZZrWMGDRrE4YcfTiAQoKCggLKyMjZu3MiRRx5ZnT8ync7wa6+9xqWXulTlp512GuXl5ezYsYPBgwdzww03cN9997F9+3aysrIYOHAg8+bNY/Lkyaxfv54DDjigsW+LqUdxMVRVuRC1qiq7K2faD2t34cILL+Tpp59mwYIFtY59/vnnGTZsGJ07d+b8889n0aJFRCKR6v2PP/44paWllJaWNltHGDp4Zzj+Vp0qzJnjZjgb0x6E8kPkBHMISpCcYA6h/FBGnmf//fevfnzbbbcxbNgw/va3v/Hcc8/VuWpT/EhBMBikqqqqUcfsi5tvvpmHH36YXbt2MXjwYDZu3MiQIUNYuXIlPXr0YMyYMcy3mbUZEQq5LD7BoBuIWLTI2l7TPli7C4ceeijZ2dm88sorDB8+PGHfggULWLp0Kfn5+fTv35/y8nJeffXVvX6OptahO8PJccORiEv100pCWIzZJ16ex7KiZUwZNiVjt+qSffXVV/To0QOARx99tMmvf8wxx/Dhhx9SVlYGwJNPPtngOaeeeiqPP/444GLiDj74YA488EA++OAD+vTpw0033cTAgQPZuHEjH330Ed/5zne46qqruPLKK1m3bl2Tvwbj2t5ly+Ccc9zI8OrVMG4c3HRTS9fMmH1j7a5zxx13cM8991SPXgPV4RybN2+mrKyMsrIyZs6cyYIFC5q8znurQ8cMg8sg8fDDrkEG1yEuLrbJHKZ98PK8ZmmMYyZNmsRll13GnXfeyVlnndXk1+/cuTMPPPAAZ5xxBvvvvz8DBw5s8JzYxJGTTjqJ/fbbj8ceewyAGTNmsHz5cgKBACeccAIjRoxg4cKFTJs2jezsbLp06WIjwxnkeZAckj19OowaZe2vadus3U2dru3ZZ5/ltNNOSxh9HjlyJJMmTWLPnj1AYszwwQcfzNKlS5voVdRPYjMF27oBAwbomjVrGnXu7Nlw9dVuEl12tnWGTev03nvvcdxxx7V0NVrc119/TZcuXVBVrrnmGo4++miuv/76lq5WLak+LxFZq6rNnzcowxrb/s6e7UaEY0Tc9oMPNmHljNkH1u46baXdjdnb9rdDh0nE9OlTEztsqy4a07rNmTOHgoICTjjhBL766ivGxfemTJsSDsOkSTXtbiznu4WqGdO6tPd2t8OHSUDizOaKCrcanY0MG9M6XX/99a16RMLsnXvugR07YNYsa4ONaa3ae7trI8PUrEYHllXCGGOaW1GRC1ED1wbPnWujw8aY5mOdYSyrhDHGtCTPgzPPrNmurHSjw8YY0xysM+wrKqqJGwZLBG+MMc3p0ENbugbGmI7KOsM+z4MbbqjZVrXlmY0xprkUFUEs41IwCIWFLVsfY0zHkdHOsIicISKbROR9Ebk5xf5OIvKkv/+vIpLvl2eLyGMisl5E3hORWzJZz5iuXW15ZmPqMmzYMF5++eWEshkzZjBhwoQ6zwmFQsRSbp155pls37691jGTJ09m+vTp9T73okWLePfdd6u3f/Ob3zRJ/sni4mLOPvvsfb6O2XeeB/fd52KHVeG66yxUzZj22u6KCA8//HB1WWlpKSKSUKeqqiq6d+/OzTcndh9DoRDHHHMMBQUFFBQUMHr06H2uU8Y6wyISBGYCI4DjgYtF5Pikw64A/qWqRwH3Avf45RcAnVS1D9AfGBfrKGdS8vLMluLHmBoXX3wxCxcuTChbuHBhg+vUxyxZsoSuXbs26rmTG+U77riD008/vVHXMq1XebnL9x6NuqwSFqpmOrr22u6eeOKJPPXUU9XbCxYsoG/fvgnHvPLKK/Tu3Zunn36a5DUxHn/8cUpLSyktLeWPf/zjPtcnkyPDg4D3VfVDVa0AFgIjk44ZCTzmP/4jMFxEBFBgfxHJAjoDFcCODNYVqJlIF8t5GUvxY0xbVVICd9/dNH/UjR49mhdeeIGKigoAysrK+OSTTzj11FOZMGECAwYM4IQTTuD2229PeX5+fj5ffPEFAHfddRe9e/fmBz/4AZs2bao+Zs6cOQwcOJC+ffty/vnn88033/DGG2+wePFifvnLX1JQUMAHH3zAmDFjqhvAZcuWUVhYSJ8+fRg7dmz1Skb5+fncfvvt9OvXjz59+rBx48Z6X9+XX37JqFGjOOmkk/j+97/PO++8A8CKFSuqRyAKCwvZuXMnn376KUOGDKGgoIATTzyRVatW7dubawA3IJGT49pgVUgxoGVMq2ftbsPt7hFHHMHu3bv57LPPUFVeeuklRowYkXDMggUL+PnPf07Pnj0pyfDIZCY7wz2ALXHbW/2ylMeoahXwFdAN1zH+N/ApsBmYrqpfJj+BiIRFZI2IrPn888+bpNKW4se0FyUlMHw43Hab+76vP8ff/va3GTRoEC+++CLgRicuvPBCRIS77rqLNWvW8M4777BixYrqjmQqa9euZeHChZSWlrJkyRLefPPN6n0/+clPePPNN3n77bc57rjjmDt3Lqeccgrnnnsu06ZNo7S0lO9973vVx+/evZsxY8bw5JNPsn79eqqqqngwbvmygw8+mHXr1jFhwoQGbwnefvvtFBYW8s477/Bf//VfFBUVATB9+nRmzpxJaWkpq1atonPnzjzxxBP8+Mc/prS0lLfffpuCgoJGvacmkefBtde6tjcahalT4aabWrpWxqTP2t30293Ro0fz9NNP88Ybb9CvX7+EZZp3797N0qVLOeecc7j44otZsGBBwrmXXHJJ9SDFL3/5y/Tf0Dq01gl0g4AIcBjQC/iFiByZfJCqzlbVAao6oHv37k3yxJbix7QXxcXu7kYk0nS3nONv2cXfqnvqqafo168fhYWFbNiwIeHWWrJVq1Zx3nnnsd9++3HggQdy7rnnVu/729/+xqmnnkqfPn14/PHH2bBhQ7312bRpE7169aJ3794AXHbZZaxcubJ6/09+8hMA+vfvT1lZWb3Xeu2117j00ksBOO200ygvL2fHjh0MHjyYG264gfvuu4/t27eTlZXFwIEDmTdvHpMnT2b9+vUccMAB9V7bpK+0NHHbOsSmLbF2N/1298ILL+Tpp59mwYIFtcI+nn/+eYYNG0bnzp05//zzWbRoEZFIpHp/fJjEtGnT6q1vOjLZGf4YyIvbPtwvS3mMHxJxEFAO/B/gJVWtVNV/Aq8DKdeTzoTkFD/btjXXMxvTdGK3nINB9z0U2vdrjhw5kmXLlrFu3Tq++eYb+vfvzz/+8Q+mT5/OsmXLeOeddzjrrLPYvXt3o64/ZswY7r//ftavX8/tt9/e6OvExEYagsEgVVVVjbrGzTffzMMPP8yuXbsYPHgwGzduZMiQIaxcuZIePXowZswY5rfCv5jTmMB8g4i8KyLviMgyETkibt9lIvJ3/+uy5qz3+efXLps61RZCMm2Dtbvpt7uHHnoo2dnZvPLKKwwfPjxh34IFC1i6dCn5+fn079+f8vJyXn311X2qV30y2Rl+EzhaRHqJSA5wEbA46ZjFQKyhHQ28qi5KejNwGoCI7A98H6g/4K8JxYdKALz4ooVKmLbH82DZMpgyxX1viuVtu3TpwrBhwxg7dmz1X/I7duxg//3356CDDuKzzz6rvp1XlyFDhrBo0SJ27drFzp07ee6556r37dy5k+9+97tUVlby+OOPV5cfcMAB7Ny5s9a1jjnmGMrKynj//fcB+P3vf8/QoUMb9dpOPfXU6ucsLi7m4IMP5sADD+SDDz6gT58+3HTTTQwcOJCNGzfy0Ucf8Z3vfIerrrqKK6+8knXr1jXqOTMlzQnMbwEDVPUkXGjaVP/cbwO3Ayfj7tLdLiLfaq66h8MwaVLt8meeaa4aGNN41u7unTvuuIN77rmHYGwZYP+1rVq1is2bN1NWVkZZWRkzZ86sFSrRlLIaPqRxVLVKRCYCLwNB4BFV3SAidwBrVHUxMBf4vYi8D3yJ6zCDa8TnicgGQIB5qlp3MEwT8zy44gqYNcvFrsUm0jXFD7Uxzcnzmv7n9uKLL+a8886rvm3Xt29fCgsLOfbYY8nLy2Pw4MH1nt+vXz/+4z/+g759+3LIIYcwcODA6n1Tpkzh5JNPpnv37px88snVDfFFF13EVVddxX333Zcwczg3N5d58+ZxwQUXUFVVxcCBAxk/fnyjXtfkyZMZO3YsJ510Evvttx+PPebm9s6YMYPly5cTCAQ44YQTGDFiBAsXLmTatGlkZ2fTpUuX1jgyXD2BGUBEYhOYq++jquryuOP/AvzUf/xj4JXYPA0ReQU4A8jc/0RJ7vHzCk2dWlOWasTYmNbI2t30nXLKKbXKnn32WU477bSEGOKRI0cyadKk6ol6l1xyCZ07dwZcjPK+pnyT5HQVbdWAAQM0llevKZSUuNsb/gROsrNhxQrrEJuW895773Hccce1dDVMmlJ9XiKyVlUzHvIlIqOBM1T1Sn/7UuBkVZ1Yx/H3A9tU9U4RuRHIVdU7/X23AbtUtc6ZME3d/sbMnu1GhAsKXB74UMjaYNO8rN1tm/a2/c3YyHBbF5tIt2iR245NpLOG2BjTnojIT3FzMvbqPqeIhIEwQM+ePTNQMxcyATBxIlRVuTjMmTNryo0xpim01mwSrYJNpDPGtFHpTGBGRE4Hfg2cq6p79ubcTGTzSVZSAtdc4wYjVF2HeOJEm8NhjGla1hmuh02kM61Newlrau9awefU4ARmESkEZuE6wv+M2/Uy8CMR+ZY/ce5HflmTK9lSwt2r7qZkS+qGtbjYpaiKV1mZGEtsTKa1gt9nsxca83lZZ7gesYl0tiKdaQ1yc3MpLy+3hrmVU1XKy8vJzc1tyTpUAbEJzO8BT8UmMItILMHoNKAL8LSIlIrIYv/cL4EpuA71m8AdqRY92lclW0oYPn84t+yDkiUAACAASURBVC2/jeHzh6fsEIdCLjQi2aJFcN55NjhhMs/a3balse2vTaBrgE2kM61FZWUlW7du3ecckCbzcnNzOfzww8mOv7VE802ga26NaX8nPD+BWWtnoShBCTJl2BRuOfWWWsfNnu1CIyora1+jc+emS19lTCrW7rY9jWl/bQJdA2winWktsrOz6dWrV0tXw5h9VrKlhEdKH0FxgzFZgSxC+aGUx4bD0KePC42ItcMxu3dbe2wyy9rdjsHCJNJgE+mMMabpFJcVE4m6YGBBuLzgcrw8r84YYs+DZ591i3HEwtbATaqbN8/CJYwx+8Y6w2mwiXTGGNN0QvkhsgJZCEJWIIttX2/jvIXnMeyxYfXGEHftCoGk/7VsLocxZl91+M5wQ7OZwSbSGWNMU1P/X2W0kkWbFrFo0yL2RPYQ0QgVkQqKy4prnRMKQU6OjQ4bY5pWh+4Mz147m6GPDuXW5bfWORIREz86rApz51rja4wxjREfJpFMEHKCOSljiD0PZsyArKTZLlVVLg1bSQncfbe1zcaYvdNhO8MlW0q4Zsk1VEYriWqU3VW7U45ExMQm0sVYrktjjGmcUH6InGAOgaT/grID2YzrP45lRcvw8lLPiisvh2i0ZlvEjRZ36wbDh8Ntt7nv1iE2xqSrw2aTSB6ZUJRu+3Wr95zkiXTPPecaXJvJbIwx6fPyPJYVLaO4rJhu+3XjrU/fAqCob1GdneCYWKhERYXrCPfr58qeeQb27HEd5YoKN1JsbbMxJh0dtjMcyg8RDASpilYB7tZcrEGuS1ERzJlTsyJSJOJGh599NtO1NcaY9sXL8xrs+KY8z3O5hefPd7HCa9bA6tWuY6zqJtjl5LgOsjHGpKPDhkl4eR4zz5xJUNzyRooyZ90cZq+dXfc5HjzwQOJs5kWLXFJ4Y4wxey+dSczJPA969nSxwrGQCVXXIR4wAC67LEOVNca0Sx22MwwQ7h/mqn5XVW9HNMLEJRPrbZTDYdfYxnvmmUzV0Bhj2q/4JZlDj4WY8PyEtDvFqZZqVoW33nJ38Cxu2BiTrg7dGQYXo5YVqIkWqYxWMrl4cr0N8hVXJG4XFGSqdsYY034VlxVTEamoTqc2a+2stDvFngdjx9Yur6pyIWx79sDkydYhNsY0rMN3hr08jxu8GxLKXvnwlXpTrYXDbiWkWLjEb39roRLGGLO3YlklBJc4WNHqTnFD6S7BzePIyanZDgRc2rVAwIVPLF1qI8TGmIZ1+M4wQNdOXQlIzVsRa5DrS7XWtWvN46oqmDjRGlxjjNkbsawS4/qPIztQs8ynouyJ7Km3DQY3OlxcDKNGuZAJVdcJPvbYmg5xLLOEMcbUJaOdYRE5Q0Q2icj7InJziv2dRORJf/9fRSTfL79ERErjvqIikrFghNjSoPGyAlkpk75XnxNKnEhXVWWr0hljzN7y8jwePPtBrihMjD8LSrDeNrj6fA8GDXIdYVUXIrFpk1skKRi0zBLGmIZlrDMsIkFgJjACOB64WESOTzrsCuBfqnoUcC9wD4CqPq6qBapaAFwK/ENVSzNVVy/PY2zB2OpbdYJwecHl9ab98TyYObNmAoetSmeMMY1X1LeIzlmdCRAgKEHO6n1W9b6GMk4kD06owuWXw5QpLg2b5Rs2xtQnkyPDg4D3VfVDVa0AFgIjk44ZCTzmP/4jMFwkftV5AC72z82oor5F5GbluoY4EKTwu4UNnhMOwznn1GxXVtrosDHGNEYsZCLcP0xWIIvFGxcz5NEh3LT0puqME3XFEccGJ7KzXae4UycXT3zLLW6/LdFsjKlPJjvDPYAtcdtb/bKUx6hqFfAVkLwM3H8AC1I9gYiERWSNiKz5/PPP96myXp7HjDNmEAwEiWqU6166Lq0UP8mr0m3btk/VMMaYDsvL8+h5UE8qIhVEiVIVrWLa69PYXbW7OuNEXXHE4TCsWAF33ulGgwEmTIBhw2yJZmNM/Vr1BDoRORn4RlX/lmq/qs5W1QGqOqB79+77/Hzl35QT1ShRjTY4gS6mqMiNRsQsXgznnWeNrjHGNEa3/bqhaPW2+v8CEiAnmFNvHLHn1YwGDx8Os2a5FGuRiE2kM8bULZOd4Y+BvLjtw/2ylMeISBZwEFAet/8i6hgVzoRYmp8AAUSEbvslD1LX5nmJeYejUbcq3bBh1iE2xpi9Vf5NefX8jRhBOL3X6SwrWpbWEs7z58OuXS52OCYQsIl0xpjUMtkZfhM4WkR6iUgOrmO7OOmYxUBs4czRwKuqrvkSkQBwIc0QLxzT2FCJoiKX2zKejUIYY8zeC+WHyM3Kre4QBwiQm5XL5NDk6o5wfRPqSkrgkUdqXzcScZ1kG6QwxiTLaviQxlHVKhGZCLwMBIFHVHWDiNwBrFHVxcBc4Pci8j7wJa7DHDME2KKqH2aqjqnEh0rsrtrN/LfnNzgS4Xlwww0wdWpNmQh0a3hg2RhjTJzYRLrismK279lO8T+KOezAw6r3x5Zw3lO1h0AgwMwzZxLuH67eX1zsOr7JolF46CHXUS4utgwTxpgaGesMA6jqEmBJUtlv4h7vBi6o49xi4PuZrF8qofwQwUCQSCSCosx9ay5FfYsa7BB37VqT5B3c7bnrroM+fazRNcaYvRFrb0OPhaiIVMAn8OLfX2T5ZcspLitmT9UeokSJRqNMXDKRPof0qTkn5HILV1S4QYlotKZdBlc+f761y8aYGq16Al1L8PI8zjzqzOrtymglU1+fWs8ZTijk0vnEEsOpuokbFiphjGkJaSx6NERE1olIlYiMTto3VUQ2iMh7InJfipSXGVdcVkxlpLJ6uyJSwfy357P5q83EVyeikYTJzp7nsklMmQIrV7osE8nWrbNwCWNMjYyODLdVh3ZJzJf2p01/4qalN9G1U1dC+aGUo8SxBnjqVDeBDtxoxPbtzVFjY4ypEbfo0Q9xaS3fFJHFqvpu3GGbgTHAjUnnngIMBk7yi14DhgLFma11olB+iOxgthsZ9s1eNxuAgAQIEkRROgU71cow4XmJI7+PPOJGhGPWrHHZJmxBDmMM2MhwSkV9iwhKsHpbUaa+PpVbl99aZ9J3qFkWNH4MZfp0mD070zU2xpgEDS56pKplqvoOEE06V4FcIAfoBGQDn2W+yom8PI/iy4oZdcwoAgRQtHo+R1SjXNXvKu4cdmeDGSY8z92hGz/etc+xcDab5GyMibHOcApenscDZz1AQBLfnnTyD4dCNUs0g2t0J060W3LGmGaVzqJHKalqCbAc+NT/ellV30s+rikXPaqLl+cxqMeghLzD4FKtFfUt4pZTb0kr1ZrnwYMPujSYwaDrEOfkWKo1Y4xjneE6hPuHufGUhLuHBEgv6fvMma6xjYlEbATCGNM2iMhRwHG43PA9gNNE5NTk45p60aO6xMIl4iUPVKSjpMRNaq6qctvXXmshEsYYxzrD9ejaqWtCozvgsAFpJX0Ph90oRDDoQiaysmwEwhjTrNJZ9Kgu5wF/UdWvVfVr4EWgxbqNsXCJQYcNqs49HNVoyjt09eUfLi52k5pV3R276dPh5JMtjM0YY53heoXyQ2QFauYYrv10Lev/uT6tc/v0cZ1gVTcyvD6904wxpimks+hRXTYDQ0UkS0SycZPnaoVJNKfYgki5Wbl1rhAayz982/LbUs7tCIUS79hFo7B6NYwbZx1iYzo66wzXw8vzGFswtno7ohEmLpmY1qp0xcVQ6WcFikQsbtgY03xUtQqILXr0HvBUbNEjETkXQEQGishWXK73WSKywT/9j8AHwHrgbeBtVX2u2V9EkvgVQiPRCNcsuYbZa2t6scVlxVREKohoJOXcjlgIW/ycjpi5czNceWNMq2ad4QYU9S1KGB2ujFamtUxz8ihEZWXiCnXGGJNJqrpEVXur6vdU9S6/7Df+6p+o6puqeriq7q+q3VT1BL88oqrjVPU4VT1eVW9oydcRr/ybciJRtyBSVbSKCc9P4OQ5JzN77WxC+SFygjkEJVjn3I5wGK66qvZ116yBm26Cu++2QQtjOiLrDDfAy/OYeebMhFRrqz9ZzdBHhzLh+Qn1pllLnki3aJHdjjPGmMYK5YcIxDWqUaKs/mQ1454fx/p/rmdZ0TKmDJtS79yOoiLo3DmxLBp1gxW33uryD1uH2JiOxTrDaQj3D3NVv8ThhMpoJQ+tfYhhjw2rs0McDsOAAYlldjvOGGMaJzY4kR3IrrXvmXefwcvzGky3Flsgafz42iETln/YmI7JOsNpKupbRE4wp1b5nsge5r89v87zrrgicfutt2zUwRhjGivcP8yKMSsYdcyohPKC7xZUZ5KoL6sE1OQdfuCBxA5xqvzDJSUWPmFMe2fLMafJy/P43YjfMf758bUSwNcnHIYXX6xZojkWO/zssxmqqDHGtHNensezFz3L7LWzeebdZ+i+f3fuLbmXiEbICmQhCFXRKnKCOfWGTPTp4zrD0aj7fuWVLowiln+4pMSFTVRUuE6yLd9sTPtkI8N7ofyb8uo8lzEBCVD43cJ6z5s0yaVZi7HYYWOM2Xfh/mHOP/58FvxtAZXRyupVQvdE9tSZVSJecbHL9hPLPfzhh7X3V1S4Yyx8wpj2yzrDeyGUH6JTVicCsX/+ghwNZZfwPOjXL7HMYoeNMWbflGwp4Zol1xDVaMr9WYGselcMDYXciG8g4DrDr7wCQ4a4wYqSEti82Q1kBIO2fLMx7Zl1hveCl+exrGgZd552J+H+YQSpHomob/QBascOr1ljo8PGGLMvisuKiUYTO8Kxu3eCcHnB5WlNpotNdFZ1yzWPHw9Dh8KcOa7snHPgsssy9jKMMS3MOsN7KTZbOTahrr6clvHCYRgVN98jGrWFOIwxZl+E8kNkBWti0GJ37AISIDcrl6K+RWldp7Q0cVvVze+IRFzn+IUXXMfY0q4Z0z5ltDMsImeIyCYReV9Ebk6xv5OIPOnv/6uI5MftO0lESkRkg4isF5HcTNZ1b8VGia/qdxWX9U1vyCA5driqymLQjDGmsWKrhMZGg6NEiWiEgASYccaMekeFY2Jxw3URcfstbtiY9itjnWERCQIzgRHA8cDFInJ80mFXAP9S1aOAe4F7/HOzgD8A4/1VkUJAZabqui8ee/sx5qybw/D5wxtclc7z4Ia4tZxUYfv2DFfQGGPasaK+ReRm5SZMblZV3vr0rXrTq8XE4oaDwdp5h0VciER2tnuclWVxw8a0R5kcGR4EvK+qH6pqBbAQGJl0zEjgMf/xH4HhIiLAj4B3VPVtAFUtV9V6/nZvGcVlxVREKohohF1Vu5j6esPrLXft6hrVmOnTLXbYGGMaK3aXblz/cXQKdiIoQYKBIPNK53Hb8tsaHKiIxQ1PmeKWao5vn0Wgd283cAE1340x7UsmO8M9gC1x21v9spTHqGoV8BXQDegNqIi8LCLrRGRSqicQkbCIrBGRNZ9//nmTv4CGhPJDSFzLuWjTIm5aehMTnp9Q51LNoVDi6IPFDhtjzL7x8jwePPtBll+2nCnDpjC2YCyV0UoiGmF31W4mF09usEN8yy0ux3Bubk2HWBV++1sX0qbqQiUsTMKY9qe1TqDLAn4AXOJ/P09EhicfpKqzVXWAqg7o3r17c9cRL8+j36GJOdOmvT6Nh9Y+xENrH2Loo0NrNcCeBzNnulQ+MZWVML/uReyMMcakITbBufC7hdXp1hTlzx/+mSGPDmH22vpvw8VGiX/4Q9dGx/IPx0IoLL2aMe1TJjvDHwN5cduH+2Upj/HjhA8CynGjyCtV9QtV/QZYAiRl6m0druiXmDMtfnW6ymhlyqWaw2G48cbEstmzLVzCGGOaQvk35QSS/nurilYxccnEtOZ2TJ4MnTq5DnEgANdf78IobAU6Y9qnTHaG3wSOFpFeIpIDXAQsTjpmMRBLxTAaeFVVFXgZ6CMi+/md5KHAuxmsa6OF+4cZdeyohg9Mkhw7bOESxhjTNJJTrsVENNJgTnhwHd4ZM2qWav7d72pGhCdMcF/WVhvTfmSsM+zHAE/EdWzfA55S1Q0icoeInOsfNhfoJiLvAzcAN/vn/gv4La5DXQqsU9UXMlXXfTXplEkEJVirvFOwU515LkMhN0M5nqVaM8aYfZecci0mIAG67dctrWuUl7uOcDTqUqrNn+/a7Ycecl/DhlmH2Jj2IqMxw6q6RFV7q+r3VPUuv+w3qrrYf7xbVS9Q1aNUdZCqfhh37h9U9QRVPVFVU06gay28PI9zjjknoey4g4/j8oLL6z7Hcx3fIUNqyizVmjHGNI2ivkVkBxNHHCLRCNe9dF2DoRKQmHItJ8eVVcYl+LScw8a0H611Al2bM+mUSXQKdkIQsgPZfPivD5m9dna9kzY8D844w1KtGWNMU0s1OqwoFZGKtEMlYinXli2DwsLEic+BAHRLb5DZGNPKWWe4iXh5HssvW85dp93FFYVXUBmpJEq0wUkblmrNGGMyI7YgR2wynSCISNqhErGUawDXXefaZ6jJNHHttRY/bEx7YJ3hJhRL61PUt4hA3BBCfZM2UqVaq6qyVGvGGLOvYgty3HnanVzS5xJEZK9CJWKKi2HPnppFN+JjiWfNguHDrUNsTFtmneEM8PI8Zp45k+xANgEJ0CnYiVB+qM7jw2F48MGaEWJVmDvXGldjjNlXXp5HKD/EkxueJKpRFGV31e6UaS/rEgolDljEU3UdZYsfNqbtss5whoT7h7n/zPs5vdfpzDhjBl5e/ckpw2E4J24OXmUlTG14dWdjjDENKC4rJhqLccDFDs9ZN6fOlUKTxe7gZWe7TnEwKXmQCGzebAMYxrRV1hnOkJItJVz30nUs+8eytG/JHXpo4vaf/mST6YwxjSMiZ4jIJhF5X0RuTrF/iL/cfZWIjE7a11NE/iwi74nIuyKS31z1zoRQfohOWZ0SyiIaYdbaWQyfPzyt9jkchhUr4M474Re/cB1jEdcxDgRgzhwLlzCmrbLOcIYUlxVTEakgohF2Ve1Kq0NcVJQ44qAKV19tjasxZu+ISBCYCYwAjgcuFpHjkw7bDIwBnkhxifnANFU9DhgE/DNztc28WOzwoMMGJZQryp6qPUwunpz2CHEo5BbhiERcez14sJvnEYlYujVj2irrDGdIKD9EMFDTs139yWp+MO8HdaZZA9fQPvBAYqq1SMQm0xlj9tog4H1V/VBVK4CFwMj4A1S1TFXfAaLx5X6nOUtVX/GP+1pVv2mmemeMl+cx44wZZAcScw9HibL0H0vTHiEuLnad3tgkutdfr5lYFwjUrFRnjGk7rDOcIbEcl/GiGuXqF66ut8ENh2HkyMSyl16y9D3GmL3SA9gSt73VL0tHb2C7iPw/EXlLRKb5I80JRCQsImtEZM3nn3/eBFXOPC/P44rCK2qtTBfVaNr5h+MX4xBxAxbV14nC+vVw993WXhvTllhnOIOK+hbVWqY5qtEGG9xJkxKXai4rc8t/nnqqxRAbYzIuCzgVuBEYCByJC6dIoKqzVXWAqg7o3r1789ZwH6RamQ4gK5BVb9afmNhiHFddVTvDRCTiQttuu83ih41pS6wznEFenscDZz1QnfA9Zvue+tdc9jy44ora5ZGILchhjEnLx0Be3Pbhflk6tgKlfohFFbAI6NfE9Wsxqe7aAfT9Tl/W/3M9d6+6u8FwCc+Dnj1rFuGIF4lY/LAxbY11hjMs3D/Ma2NfY0jPIYCbsDH19an1xg6Dm0yXlVW7vKrKGlhjTIPeBI4WkV4ikgNcBCzei3O7ikhsuPc04N0M1LHFFPUtIieYk1C2+pPVjHt+HL9+9ddpxQ/Hh0skp1oD135b/LAxbYN1hpuBl+eRm5WbUDZ33dz6z/HgyisTJ9OBm6jRLb2VRI0xHZQ/ojsReBl4D3hKVTeIyB0ici6AiAwUka3ABcAsEdngnxvBhUgsE5H1gABzWuJ1ZIqX51F8WTGjjhlVK35YUfZE9jQYzhYLl5gyxU18Th686Nu3iSttjMkY6ww3k/OPPz9h+61tb6WVai03N7FDHAhAeXkmamiMaU9UdYmq9lbV76nqXX7Zb1R1sf/4TVU9XFX3V9VuqnpC3LmvqOpJqtpHVcf4GSnaFS/PY1CPQbU6wzHd9qsZdSjZUpIyfMLz4JZb3MTnG25IPH/1ahg2zMLajGkLrDPcTML9w4w6dlT1dlW0qsHlQGMjD+PGQadOriMcCNjIsDHGNIVUi3EAqGp1bviSLSWEHgvx61d/TeixUJ2DGF271p5Qt2ePrSRqTFuQVmdYRPYXkYD/uLeInCsitafjmnpNOmVSdZyaosx9a25aEzUefBDuu8/FpUWjcN11NtpgTHsnIgfWs69nc9alvYotxvGjI3+UMEKsaHWqtflvz6ciUlFdVtcgRijkBi2SxVYSLSmxlGvGtFbpjgyvBHJFpAfwZ+BS4NFMVaq98vI8zjzqzOrtymhlg6PDMeXlNUned+2y0QZjOoDi2AMRWZa0b1HzVqX98vI8Jocmk5uVW535RxACEmDzV5vZ9u9t6V3Hv5M3fnziCLGqu7s3dKilXDOmtUq3Myz+CkQ/AR5Q1QuAExo4BxE5Q0Q2icj7InJziv2dRORJf/9fRSTfL88XkV0iUup/PZT+S2rdDu1yaML2yo9WprXqUSiUGDu8aBHcdFMTV84Y05rEB7N+u559Zh/FRojD/cNkB7JRlMpoJbPXzWbJ35eQHchGEDoFO1HUt6ju6/h38h58sPbk58pKS7lmTGuVdmdYRDzgEuAFvyxFMpmEE4LATGAEcDxwsb/MZ7wrgH+p6lHAvcA9cfs+UNUC/2t8mvVs9Yr6FiUsB/ruF+8y9NGhaYVL9EvK9Dltmi3CYUw7pnU8TrVt9pGX59HzoJ5EtSZ5cFSjRKIRrii8grtOu4vlly3Hy/MavFaqlUTBdZBzcizlmjGtTbqd4euAW4Bn/fQ8RwLLGzhnEPC+n7i9AlgIJDcPI4HH/Md/BIaLJP893b7ElgONVxmt5MrFVzbYIU5eiEPVFuEwph07RERuEJFfxD2ObbedJd/akFB+iGAgcZxHUQq/W0goP0RxWXFad/Kg9kqi2dkuXGLGDDcybO22Ma1HimUdalPVFcAKAH8i3Req+rMGTusBbInb3gqcXNcxqlolIl8BsVwJvUTkLWAHcKuqrkqnrm1BUd8i5qybQ0RrFrWPjRCvGLOizpGHcBg++MCNCKs/LhRbhMNreLDCGNO2zAEOSPEY4OHmr077F1ud7qG1NZF5UY0ycclEAhKgKlpFTjCHZUXLGhwh9jxYsQLmz4dtftjxtm3ws5+5djsYhLFjXQpNz3Od4+JiN2ps7bkxzSutzrCIPAGMByK41YkOFJH/UdVpGarXp0BPVS0Xkf7AIhE5QVV3JNUrDIQBevZsO5OrY8s0j39+PBp3tzM2oa6+RvYeP5AkNoFOFV56yRpQY9obVf3PuvaJyMDmrEtHUtS3iIffepiqaFV1WWW0EkESskykEy4Ra5NDIRcrHC8SgVmz4LHH3Gjxdde5Y3Jy3EQ8a8+NaT7phkkc73dERwEvAr1wGSXq8zGQF7d9uF+W8hgRyQIOAspVdY+qlgOo6lrgA6B38hOo6mxVHaCqA7p3b1t3DcP9wzx09kO1Er5v+7rhmctduyZOzli50s1UtttuxrRfInK8iEwRkfeBB1u6Pu2Vl+cx88yZBKV2uIQgiEjCghwNKS52k+dSUXUd4Geecd9tgp0xLSPdznC2n1d4FLBYVStpeALHm8DRItJLRHKAi4DFSccsBi7zH48GXlVVFZHu/gQ8/Pjko4EP06xrmxHrEAfiPobFmxZz3pPn1RuXFgq5W2zxKivd7ThjTPvhZ9a5RUTeAX4PTABOV9UBLVy1di3cP8yqy1cx6LCaFerE/xeJRvjZiz9LO3Y4FEqMHY4Xm1B3/vnueyDgymxhJWOaV7qd4VlAGbA/sFJEjsDF8tZJVauAicDLwHvAU/7kuztE5Fz/sLlAN3+k4wYgln5tCPCOiJTiJtaNV9Uv039ZbUe4f5hw/3D1dpQoizYuqjfDhOfBzJm1U/dsSy8dpjGmDRCRElz2nizgfFXtD+xU1bIWrVgH4eV5zDhjBrlZuQQlSEACRImiKHsie9LOEe95bqR30KDE8kAABg6Ea691eeSvvdYWVjKmpYhq4zL0iEiW3+FtFQYMGKBr1qxp6Wo0SsmWEoY8OiQhRg1gfP/xPHh23XdDZ8+GCRNc4wlu9aPlyy3WzJjWSkTWpjuqKyKLgH64O2hPqOobIvKhqh6Z0Uo2QltufxtSsqWE4rJiVn+8mkWbatY6GXTYIGacMSOt2GFwndvhw90SzTGq7isQcF+xhZWCQZgyBW65palfjTEdV33tb7rLMR8kIr8VkTX+1//FjRKbJhCLUUuOH25oQY5w2H3FRoj37LGV6YxpL1R1FNAHWAtMFpF/AN8SkUH1n2makpfnccuptzBp8CRygjnV5as/Wc2QR4cwe216yd5jK9TF2uxotCYrUHwnOBi0XMTGNLd0wyQeAXYCF/pfO4B5mapUR5QqfvjdL95l2GPD6u0QFxUlxg/bynTGtB+q+pWqzlPVHwHfB34D3CsiWxo41TQxL8/jdyN+R0Bq2uiqaBUTl0xMO37Y86Bnz5pOcEwg4O7s3X+/GxG2bBLGNK90O8PfU9Xb/QU0PvRT/rS6W3VtXXL8MMCeyJ7qRO93r7q7VqObamW66dMt3syY9kZVP1PV36nqYOAHLV2fjqj8m3KSQwsro5VMfT39W3KhkOv4xs/5CARqYoctTaYxzS/dzvAuEalufEVkMLArM1Xq2Ir6FpEVSEz//NS7TzHssWHctvw2hs8fXqtDnLwyXTRq4RLGtHUisriuL+B3LV2/jijVCnUAizYt2utwiR/+0HWCwbXZ994Lt93m4optMMOY5pVuZ3g8MFNEykSkDLgfGJexWnVgXp7HlYVXJpSVHgM/tAAAIABJREFUbitlT2QPEY1UJ3yPFw67pT/jWbiEMW2eh8vPvgqYDvzfpC/TzGLzO7IDtXOlPfPuM+lfx4PJk90IcSDgwiYqKy3PsDEtJa3OsKq+rap9gZOAk1S1EDgtozXrwFKNDscEJEAoP1Sr/J57aqfusXAJY9q0Q4FfAScC/wP8EPhCVVeo6ooWrVkHFu4fZsWYFYw6ZlRCeff9u6cMZauL57mV50QSY4gDAdi82dpuY5pTuiPDAKjqjrglkW/IQH0MNaMPgRQfT1SjdZ6XKlzC8lUa0zapakRVX1LVy3CT594HikVkYgtXrcPz8jyevehZJg2eVD2h7vH1j/OrV3+1VxkmystrUmPGRKMwZ05NuERJCdx9t7XjxmTSXnWGk0jDh5jGSjWZDlxnuK5k77FwifiJGatXw7Bh1pAa0xaJSCcR+QnwB+Aa4D7g2ZatlYnp2qlrrQl1VdEqrn7haiY8P6HBUeLk1emCQTdKHInA7t1u7sfw4RZLbEym7UtnuHGrdZi0FfUtonNW54T8w4oyr3RenY3sPffAuKRo7j17LAbNmLZGROYDJbiFN/5TVQeq6hRV/biFq2Z8ofwQkrwUKBDRCLPWzko54TlebHW68ePd1wMP1EyqU4XnnnPtt8USG5NZ9XaGRWSniOxI8bUTOKyZ6thheXkey4qWcddpdzHq2FHVneKqaFWtSXTxioogKynkePv2DFbUGJMJPwWOBn4OvBHf/orIjgbONc3Ay/O48ZQbU+5TNOWE51rX8ODBB91Xnz6J8cORiLvTZwtxGJNZ9XaGVfUAVT0wxdcBqpp6hpdpUtWrH50yidysXARBUbbvqbt363lwZWJCCqZPd8s3G2PaBlUN+G1tcjt8gKoe2ND5InKGiGwSkfdF5OYU+4eIyDoRqRKR0Sn2HygiW0Xk/qZ6Te3RPaffw6yzZ3HUt45KKBeEnGBOygnPdSkurr0gB8BVV9lCHMZk0r6ESZhm5OV5XHvytShKVKNMfX0qNy2tO3da8uhwNApXX20xZ8Z0BCISBGYCI4DjgYtF5PikwzYDY4An6rjMFGBlpurYnoT7h5l/3vyE5ZoBrj35Wry89HuwsQU54kUisG2bdYSNySTrDLchpZ+WJmxPe31anbOWPQ9mzkycTBeJuBFj6xAb0+4NAt73VwytABYCI+MPUNUyVX0HqJWiRkT6A98B/twclW0PvDyPsQVjq7cVZerrUznhgROq2+m6VhKtvoa/IEdymsw//cnd2bPMEsZkhnWG25Dzjz8/YVtRrn7h6job1nAYRo5MLHv3XRg61BpTY9q5HsCWuO2tflmDRCSAW9QjdTBszXFhEVkjIms+//zzRle0PUmVI/7dz99l3PPjGPro0HpXEo2J5R8Oxi10p+omRv/gB3DrrQ1nlrBOszF7xzrDbUi4f5hJgxOXmotohKmv17328qRJiY0quJWObLlmY0wdrgaWqOrW+g5S1dmqOkBVB3Tv3r2Zqta6VeeIl9r/ta78aGW9K4kmXMdLzCwRE426r+QMQfGd35ISS8dmzN6yznAbc8/p9zDq2MSVj/606U/1hks88EBiuAS4lD3WSBrTbn0M5MVtH+6XpcMDJopIGW4Z6CIR+e+mrV77Fe4f5sGzHky5aBKkP7EuHHYZJpI7xODa81hmif/f3pmHSVFdDf93umfBJYoMKCoD40JQEgICQVsFh2AUFRWjSUzyZcCtBSQJb76EJS4h0aiM+SJZkMUFmcQk5tUwEsUVGUDSguwIioAZBATRQVQis3TX/f64VT3VywyDTA+znN/z9NNVt25VnS6G06fPPUuy8VtSYsuwaTk2RWk4agy3QMafP56g1Lp7GxIuMWNGokHsOFZpKorSKnkD6C4ip4lIDnA9MK8hJxpjfmCM6WqMKcCGSpQYY1KqUSh1E+4X5rUbX6Nnx+ScRRARpg6dCnDQ9s2eQZzszMjPt/o7ErHGrt/4BVuGTcuxKUrDUWO4BRLKD/HQFQ8lNOM4WLiEZxB7IRPGwKOPqndYUVojxpgoMBZ4EXgL+IcxZoOI/FpErgIQka+LyA7g28BMEdlw5CRufYTyQzxy1SMclXVUwrhjHJ5Y9wQXPX4Rdyy846CNOZJ1N0B5uR0bPBjy8hKN36Iim4R3991ajk1RGkpGjeEG1LnMFZEn3ePLRKQg6XhXEdkvIvUmcrRFwv3CXH1WYnZc6abSesuthcNw5ZW1+zU1Wl1CUVorxpj5xpgvG2POMMb8xh27yxgzz91+wxjTxRhzjDEmzxjzlTTXeNwYM7apZW8teI2TBpySWB5iyXtLqHFqcIxDVawqIX44XcWJcBiWLIFLLkn0EldVwdNP24S7u++2715YxKRJaggrSkPJmDHcwDqXNwEfG2POBB4EpiQd/x3wfKZkbOkkh0sAFC8trjN+GKBz58R9rS6hKIqSOUL5IaYOnUpusLaAsCGxs0be0XmANYSHlAxJW3EiFILJk1MTol96CW67zXYZHTdOE+cU5YuQSc/wQetcuvtz3O2ngCHiNnoXkeHAfwBduquDdOESAD9/6ed1GsRFRVpdQlEUpSkJ5YdYOGJhiocYwBjDuBfGEdkeoay8jOpYdZ0VJ9J1FwWIRm2X0cpKGzucXG1CUZT6yaQx3JA6l/E5bozbJ0CeiBwLTAB+Vd8NtM6lDZf4+QU/Txj7tPpTbn321rQGcV3VJZ55BiZM0NqUiqIomSCUH6LvyX1Txg2GymglJWtLKCwoJCeYQ1CCdVacKCqyscHJOE5tK2fHsbHEiqI0jOaaQDcZeNAYs7++SVrn0pKu3BrA3YvuTpuYka66hDHWO/yLX8DAgbbbkaIoitJ4FPUuSgiX8DAYHl71MCVrS5g6dCp3D76bBUUL0rZyDoXgxhtThhP0eSAAFRWNKbmitG4yaQw3pM5lfI6IZAHHAxXAuUCxW+dyHPALEdEkjnoYf/74lM5HOz7bwcDZA9N6iNMZxB6xGIwZox5iRVGUxsQLlxjVb1SKURwzMWasnMG4F8ZRWFCY1hD2SPYOBwKJ9Yizs7WkmqIcCpk0hhtS53IeMMLdvg541VgGGmMK3DqXU4F7jTF/yqCsLZ5QfojFIxen1LWMmRhj54+t00Oc3K7Zw3E05kxRFKWxCeWHmD5sep0xxF7IRL3XCFn9PGqUfV11ldXZYB0cN9yglSQU5VDImDHckDqXwKPYGOEtwE8BLex+GHh1LXOCiQFlNU5Nnco1Xbtm0GLtiqIomcSrMpFcEcgLmaivKhBYY3f6dOslnj+/Nl7YqzWsKErDyWjMcAPqXFYaY75tjDnTGDPAGPNummtMNsb8NpNytiZC+SHKRpQxqOughPFZq2bVm1CXnW09CsEgDBpkPQuKoihK5vAqAiUbxPWt6CVTVmZD20C9woryRWmuCXTKYRDKDzH0zKEJJdcc4zDq2VFMeGVC2oLuixbBb35jDePXX4eZM61nWOOGFUVRMke4X5hb+t6SMh4zsZTSaukoLKztQNeunfUKRyJaGUhRDoWsg09RWiKFBYVkB7OpjlXHxwyG4qXFCEK7rHYJ2cqhkH2NHl3b37662laYmDv3SHwCRVGUtkFR7yLmrJ1DZbQSg0EQAhIg7+g8Itsj8TC3ot5FKYl1oZBtu1xWZg3j9eth7FhbezgQsF1Hx49Xb7Gi1IcYYw4+qwXQv39/s2LFiiMtRrMisj3CzfNuZuNHG1OOBQhwzzfuYdLASQnjo0fbKhPxeQHrOS4qUmWqKIeLiKw0xvQ/0nI0Nqp/Dx+v6ca+qn08GHmQmIkRkACO4+Bgs+Nyg7ksHLGwzkoTkYgNc4tGE8dzc2HhQtXhStumPv2rYRKtGC+hLjuQnXpQqLOguz+hznGscaztPRVFUTJHKD/EpIGTaJ/bHsc4OMYh6kTjhjCQtiudn7Ky2qoSfqqroaREQycUpS7UGG7lhPJDLBq5KCWh7sRjTqT438UpCRpeQl0g6S+jstIqU0VRFCVzFBYUIukKwANZgSze++S9OhPrCgutFzj59EAAZs+GO+9Ux4aipEON4TZAKD/EohsWMXPYTM7ueDYAu/fvpvTtUi6cfWFKlYlwGPonLSQYY5WpKlFFUZTMEcoP0bdzattmsEl1M1fOZNDjg+qsDrRggU2G9pfNdBzrHY7F7LvWkFeURNQYbkOE+4XJPy4/YcwxDmOeG8PoZ0cneBtuuin1/Koqm1CnKIqiZI6b+qZRwFh9bTBEnWidpddCIZg0Cdq3r609bIx9BQJaQ15R0qHGcBvj2p7Xpox53oYhJUPiyjUctuXVBgxIDJkoLYVrrlEPsaIoSqYI9wszc9hMBpwygOxAdkKZTI+DlV4rLEwNdzvrLOs51kQ6RUlEjeE2hqdkux3fLWHcYKiMVjK5bHKCQbxsWWrIRGkpDB6sBrGiKEqmCPcLs+yWZSwauYire1ydcEwQsgJZLN+5PGVVzyMUgmnTEg3ijRut/tZEOkVJRI3hNki4X5hb+92a4m0wGF5+92UK5xQmKNi6QiY0oU5RFCWzhPJDDDh1AAGxX9eCcFbHs4jGopRuKmXGyhkMnjM4rUGcLv+juBhuv10T6RTFjxrDbRSvKUcyBkN1rDohbCIchuHDU6+xe3cTCKooitLGKSwoJDeYS1CCZAez2VSxKaHkWlWsinEvjEvrJU7nzDAGDhyAm29Wg1hRQI3hNksoP0TZiDKG9xieNh7NM4q9mLTx423ihZ958zR+WFEUJdOE8kMsKFrA3YPv5vLul+OY1GLCy99fzoyVM7jo8Yu45u/XxA1jL/+jQ4fU627cCBddpDpcUdQYbsOE8kPMvX4uM4bNICjBlOMBCcQbc4RCthzPgAG1xx3Hxp9ddJHtXKcKVVEUJTN4TTk6H9O53nk1Tk1K+EQ4bOOE086v0ZA3RVFjWCHcL8wtfW9JGY86UdbvWQ+4rUKj93HTpPUJHerAKtMZM2wb0AkTNDlDURQlUxT1LiInmHPwiSR2rPM8xGefnTpv5kyruxWlraLGsAKkV7AGw+jnRjPhlQkMKRnCnQvvZNyGc7ng4oq014hGbXLGHXfYsj7qLVaUI4eIDBWRTSKyRUQmpjk+SERWiUhURK7zjfcRkYiIbBCRdSLy3aaVXKkPL8Tt3m/cy/gLxscT69JhMOQdnRffD4dtaMTMmdCli2+esbo72SCORNS5obQNso60AErzwFOwxUuLeWbTMxhstXbHOBQvLUaQeBxxz6ufY1lZEVVV6a/ldTuaORPmzNG6lorS1IhIEJgGfBPYAbwhIvOMMRt9094DRgI/Szr9c6DIGLNZRE4BVorIi8aYfU0gutIAQvkhQvlWqZ5xwhmMnT+WGqcmZV6AABWfpzovwmHo1QsuvNDqa4/iYti5E77yFcjLg3HjrC7PyVE9rrRu1DOsxPHHEKcruyYIwUCQomHdWbgwMX44HcZo609FOUIMALYYY941xlQDfwcSitUaY8qNMesAJ2n8HWPMZnf7fWAP0KlpxFYOlXC/MItGLmJUv1FkB2orBAUkQG5WbjzvI5lQCH6W/DMIeOIJ+MUvYNQoqKzUFs5K2yCjxnADlulyReRJ9/gyESlwxweIyBr3tVZErsmknEoi4X5hrj7r6pRxg6EmVkPxv4uhS4SpUyE3F0TsKxlt/akoR4xTge2+/R3u2CEhIgOAHGBrmmNhEVkhIis+/PDDLyyocviE8kNMHzadRSMXce837mXmsJncM/geFhQtiHuQ0zFliq0UlA5t4ay0JTJmDPuW6S4DegLfE5GeSdNuAj42xpwJPAhMccffBPobY/oAQ4GZIqIhHU3I+PPHJ3gZPAyG0rdLuejxi6BLhIUL4dZbISvpX0cELr5Yl9YUpaUiIicDfwZuMCa1lpcxZpYxpr8xpn+nTuo4bg54FSfC/cIUFhRSVl7GrJWzuG/JfWmbckD9BjHU6nHQ+GGl9ZJJAzO+TAcgIt4ynT9m7Wpgsrv9FPAnERFjzOe+Oe3ADWBVmoxQfohFIxdRvLSYeZvmJRR4B1u+p3hpMXOvn0tZWWLcmQi0aweTJ6shrChHiJ1Avm+/izvWIETkOOA54HZjzOuNLJuSYSLbIwwpGUJltDIe4tYuq12dnuIprhvqgQesN9hDBPr0sdtDhmj8sNJ6yWSYREOW6eJzjDFR4BMgD0BEzhWRDcB6YJR7PAFdpsssXgzxaze+xvAeqS3ontn0DLNWzqKw0CrIYNC+X301XHqprV2pXgRFOSK8AXQXkdNEJAe4HpjXkBPd+XOBEmPMUxmUUckQZeVlVEWr4onQBsOB6AGKlxbXeU779jYkwo9XZaK42BrC/vhhrTShtCaabQKdMWaZMeYrwNeBSSLSLs0cXaZrAjyjeOawmQmJdQbDmOfGUFIxmh9NK2XITWWM+9VW5s+3zThmzNCGHIpyJHCdB2OBF4G3gH8YYzaIyK9F5CoAEfm6iOwAvo0NRdvgnv4dYBAw0pe70ecIfAzlC1JYUIikSeQo3VTKrJWz0p9TWOvUSDaKFyywY57DIy/PeorvvNO+q35XWjqZDJNoyDKdN2eHGxN8PJBQB8YY85aI7Ae+CqzInLjKwQj3CwMw+rnR8XagMRNjxsoZwAwCpwZ49dXbidZMxvudVVOjJdYU5UhgjJkPzE8au8u3/QZWLyef9xfgLxkXUMkYofwQV/a4ktK3S1OO3b3obgAqPq+gsKAwHjYRClkdXVYG+/ZZb7DHZ5/Z95494Sc/gYqKVE+x6nalJZNJz3BDlunmASPc7euAV40xxj0nC0BEugFnAeUZlFVpIOF+YX52fpp6PNiaxLGuCyBQjQ3zti9j4MABG0OsHgRFUZTMM/788eQGc1PGd3y2g1ufvZU7Ft7BkJIhCYl1oRBMmmRjiGfOhA4dEs/duNHWHs7LSwyN00oTSksnY8ZwQ5bpgEeBPBHZAvwU8MqvXQisFZE12Ni1McaYjzIlq3JotM9tn1KHOE5+BEYOhlOXYY1hwct/fPllXVJTFEVpCkL5IRaOWMi937iXQd0GpRx3jJPQrjmZcNjGBCdz4AA8+ihMnQp3351+xU/jiZWWRkbLlTVgma4SG6+WfN6fsSV9lGZIYUEh2cFsqmPVKccMBvJfh6H/A7MXgZMNruHsb8KhS2qKoiiZxetUV1hQyMDZA4mZWMLxrEBWnU05wBrEzz9vc0D8LF8Oq1fDokXpDWGtPKG0NJptAp3SfPFaN4/qN4qeHZNLR7vkvw6XjwWJ4q+MZ4xdYlMURVGahlB+iIeueChlRe/cU8+lrLyszhrEYGsQH3VUamOlmprEuGKPsrLUeGJFae6oMax8IbyOR49c9Qg5wZz0k/o/TKD/7IQhx4ExY2BW+oRmRVEUJQOE+4WZMWwGQQnGxxa/t5hfvPoLBj0+qM4qE15i3a23plaZKC2Fa65JDIdILrWp8cRKS0CNYeWw8HuJC44vSJ3Qu4RAMLFhRyxm+94nK1FFURQlc4T7hbml7y0p41Enytj5Y+v0EIdCMH26fSV7iEtL4YILYMKE2rlTp9pQialTNURCaRmoMawcNp6X+K/X/jUhe1kQAl2X8b0JrxEMJp5jjFWigwerQawoitJUFPUuIiuQmi4UM7E6k+k8wmFbP76u5hwTJlh9Pm6c9SaPG6f6XWkZqDGsNBr+7OXxF4wnK5BF1Iny15zB9Jo4hkGXpBYEqaqyneoURVGUzBPKDzHt8mlkB7ITDxhY/v7yeuOHwRrE06eT4uAAaxAPHw6VlRozrLQs1BhWGpVQfohJAyfRPrc9Ucd20DYY1mRPZ/H5ncjruxh/Qh3YMj3qPVAURWkawv3CLBq5iFH9RsVjiB0cSt8u5fzHzue035/GNU9eQ2R7hMj2CPctuS/BSA6HYckSGJRasY09e6ynWERjhpWWQ0ZLqyltF68dqDGJhm/FORNg7UKI5eKVXPOykufOPQKCKoqitEFC+SHKysvi3UT9lO8rp3xfOc+8/QxZgSwc4xAMBLn8zMvpfGxninoXEQqFWLTIhkY88IA1gP0YA5deWrsfiVgvcWGhxhErzQ/1DCsZIZQfSt+pLv91GDmYYwo24PcQl5bCRReph1hRFKWp8GrG14XBUOPUEDMxqmPVlG4qZcbKGQyeMzjuKZ4yJX0cMVi9PmiQNZiHDIE779TGS0rzRI1hJWNMuXgKM4fNpNvx3RIP5L/Of4fcklKDePFim5WsVSYURVEyj1cNaHiP4XV3FU1Dcue6cBhee80avtYoNni6PRq1K38aR6w0Z9QYVjJKuF+Y8nHlzBw2ky5f6lJ7IP91uGIM4JDclKO0FAYO1FrEiqIomSaUH2Lu9XNZeuNSRvUbxaCug2if277ec3KCOSmd60Ih25Fu+tPrCX79ESBGsm734ojz8mD0aPtSx4fSHNCYYaVJCPcL0+vEXoktQfs/Ah+fDksnUqs0rXfCq0W8dSu0b69xZoqiKJnEa90MENkeYdDjg+JJ0H4GnDKAqUOnAnDfkvsoLCiMnwewOushYlfMgM/y4O1rEs41Bnr0gLFjba4IwOzZsHCh6nflyKKeYaXJ8FqCBvx/dt/8BQwLw9EfpMz3alfefrvGmSmKojQVofwQi0cujnuKgxJEELICWRSeVkjJ2hIGzxnMnQvvZEjJkHj8cGR7hMfWPGYvcsEDEKjGHzIBsGZNrSEMhxY2EYmoR1nJDOoZVpoUz0N887yb2fjRRjvY/xE46U14fCHEckj+jWaMrUfsKUzNSFYURcksfk/xrJWzGDt/LFEnSvHS4oR5B6IHGPfCOKYOnUpZeRnRmOtNzn8dbiiEV+6HbWlqsLk0tPxaJGLnVVfbffUoK42JeoaVJieUH+KRqx5JLPruVpmg/0wIeIl1td4EEdi3z1acuOMO9RQriqI0FRWfV+AYB5NUI95j+fvLGTxnMHlH5xHwlZWQ/GWM+tPfmTlT6NIl9bzu3aF3b1i//uAylJV9cY+yohwMNYaVI0IoP8SikYsYcMqA2sH812HYGLhhIHTa4JttCOZU8sADhpoacJxET7GiKIqSOQoLCskJ5tRbcaIqVkXF5xXx7nYBCdAuqx1FvYsIh+Ef/0jtWrd5MyxfDrfeasuv1UUkAss37EICtRWItKGH0phIclOElkr//v3NihUrjrQYyiES2R6hcE4h1bHqxAPbz3PDJrJJ/M1mlXF2ts1cBg2bUFoOIrLSGNP/SMvR2Kj+bf1EtkcoWVvCw6serk2CTqJP5z6cd+p5nHPyOVR8XpGQXBfZHqH4Dx/zzO+GYhzB6nKDp9MDAVueDaxOz8uDigr7/uOfxKiqMhCIEvjyC1zV7zzGj+msOl85JOrTv2oMK0ecyPYIE1+ZyOL3Fice2H4ezJ0De7tD3CNh/17HjxeGD7fhEtXV1kuwYIEaxErzRo1hpaXjGcW79+/mnYp3anM/fGQHslk00norysrL2Fe1jwcjD9rqFCtvwTz3R3ACgOcqtvq9oAB27SK+AihiX45xwASAGPR/mHt/9zGTBk5qks+rtB7UGFZaBBNemcADSx9IjEtbcTM86y84LECME078nOOP/hLl5XY0EIB77oFJqh+VZowaw0pror4SbMN7DGf+lvmpq35gHR3lhbCnJ6z/AQeP2PR9JwSrmPnUZsLDex2O6EobpD79m9GYYREZKiKbRGSLiExMczxXRJ50jy8TkQJ3/JsislJE1rvv38iknErzYMrFU1h641KG9xheW36t/yO29FrHDSAxIAoE+HjPsZSX1ypIx7HLaR6RCNx3nybZKYqiZIpQfohpl08jKMGUY5sqNqU3hMHmhwy8n+xv38SJXy6HOhLzapH4e4Acnn6+Il7OrSHo94FyMDJmDItIEJgGXAb0BL4nIj2Tpt0EfGyMORN4EJjijn8EXGmM6QWMAP6cKTmV5oXXDem1G19jVL9R9OzY0xrEY3vBjQOhw7vuTC/mzMOwerXdikRs+MQddxouGlzDrNIGpCorSiujAc6IQSKySkSiInJd0rERIrLZfY1oOqmVlka4X5glNyyJOzEEISeYQ4+OPQ56bo1Tw54v3+/uJRrExx5b29pZAjGysg2BoMGhmpdWvcWFv/45s1YevE2p931w551ahUipm0zWGR4AbDHGvAsgIn8Hrgb8AUZXA5Pd7aeAP4mIGGNW++ZsAI4SkVxjTFUG5VWaEV6Ny4QEu/zX4fwH3LCJVE/CzJkOn30W4MMP4cABBwjgxITbHvpfevXbn9AlSVFaMz5nxDeBHcAbIjLPGOPXv+8BI4GfJZ3bAfgl0B/7H22le+7HTSG70vLwnBiR7RHKysvirZr/telfdSbbxen/MGA4dvm97N/TyR007N9fO8U4QpQaOvddze7VfWDlLThrRjCaS+h1V696dXtZmc0ricVqy7FpbomSTCbDJE4Ftvv2d7hjaecYY6LAJ0Be0pxrgVXpDGERCYvIChFZ8eGHHzaa4ErzIZQfomxEGcN7DLdLcV7YxKnLoNsitwSbzUg2RnjiCcNLL3kZygYIEv38aMrKy3SpTGlLxJ0RxphqwHNGxDHGlBtj1gFO0rmXAi8bY/a6BvDLwNCmEFpp2YTyQ0waOCnuzHjoiocSQigCUofJ0f8ROk4aQPeiB8jq+C61VSa81b8AODns+/AocLLAZEE0B2fhnZQ8u7lemQoLbYJ1MKjl2JS6adYd6ETkK9jQiUvSHTfGzAJmgU3gaELRlCbE73UoXlpMKY9YoxhsIsZjS8Akl+rxvUd+yr6l2xny68OvPBGJaCk3pUWQzhlx7mGcm+zIQETCQBiga9euX0xKpVXjdRwtWVsCwDknn8O4F8ZRGa1MaeBR/kk5nD4erv6nW1YzN+V6lf8N2qZMMYAgbL2YmWNh97vzGf/jE2rLuCXp6QULVG8r9ZNJY3gnkO/b7+KOpZuzQ0SygOOBCgAR6QLMBYqMMVszKKfSQvCM4gn4BwB4AAAgAElEQVSvTOC3//6tLbfjhU4snYi/ZqXF3XeyKJt7xmEvlXmxZ1rKTVHUGaE0DH9bZyBuHM9eM5vqWHVqVzuvG+naIth/ErwzDJwgEICKs6wx3P492HcaEMQ4htLfXsqzm3/E4j8CO0Jp9bTqaqU+MmkMvwF0F5HTsEbv9cD3k+bMwybIRYDrgFeNMUZE2gPPARONMUszKKPSAply8RSG9xger1/5ALdbdbp0PLWRP5532BrEy5fHkICAGLKyIS8vyH33HZqnwB97VlkJJSWqYJVmS0OcEfWdW5h0blmjSKW0eTzjuKh3ESVrS5i1apZ1bPjJf92+wK7+lf0S3r3YhkfEgH3dfJMFCBCd93uKu7/M+4utfjZGY4SVhpMxY9gYExWRscCL2MrajxljNojIr4EVxph5wKPAn0VkC7AXazADjAXOBO4SkbvcsUuMMXsyJa/SsvB7G8444QxGMxrnrHmw9Oew6UpA3By7IJ6ytPrWoeqk1xg15kJMLEAgCNMfChAOH/yeeWevh8DZEAtijDB7NhQVqaJVmiUNcUbUxYvAvSJygrt/CaAVvJVGxdPh55x8DmOeG0PMxBCEbu270b5de9bsXmMn5r8Ohb+CbYMgZkCM27DDc3hgt00Wpb+9LOEeWVkaI6w0jIzGDBtj5gPzk8bu8m1XAt9Oc949wD2ZlE1pPXhxaRNfmcji/GvjBd2P/XAI+9cNcWd54RNB2DYI4yZnODHDqNExOHFjvUXcI9sjjNswBKf372DFLUCQaFS9DkrzpCHOCBH5OjYU7QTgShH5lTHmK8aYvSJyN9agBvi1MWbvEfkgSqvH099eFYpQfoj7ltzHug/W4RgHQfjmRcfR54KXKCuDdl/az9KHv0OsOorn6EiP4dxzhbIyKF20lbK31nNKr3cY/92BB60s5K+K0ZKqELVUuZsD2oFOaVXMWjmLpzc+zbU9r2XruhMpvvkycHLco+kS7AAc5Kx5LH3ppNoEDFep5FUMo+KtXizfO59nVi/FHLUHXvg94uTSLjeoccPKIaEd6BTl4ES2RxhSMoTqWDU5wRwWFC1IMO4mzC6l+IllcNRHMP9Pdep4CdgSm8bBNm0KRAn0LeFn3xpCe3NGQpicl3S3T7byu1fn4HR7ldyCVSn3bq4c7Jkp9evfZl1NQlEOlXC/MOF+bsxDP9j52TSemH4ybLrK7W1f28nIv8Rm3r6awsJlXD66mM9rPuflBdWYoz6CF8+AqAPmMpBLIFhN4LKfckrW1/j+lacQCg1v+g+pKIrSignlh1hQtKBOL2f7M99CBk6xyXe7+8KKMHYBxNPpDoiD8YdTmCyIBXHeuIniNwIEApCbaxPswCZHV1WB45wOchcEJ1I18hLKystahFFZVl5GdayamIlRHatuMXI3F9QYVlo1f/nxbdx2TYTiJ3/HplfP5e0XL8A4Xt1Lfy1LQ/W751L6868TN5jFcQ3oAHFlGhWcXb3ZMWwMxe8Br4xnysVTUm+chJZkUxRFaTjJVSj8FBYU0i6rnS3R1rsE1oyAaI5V3V/9K+T+F1beRKKBHMPqcjvmONb4LSuzR6uqDY7j6n6TBVEwC39J3hWdE+7dXEMRCgsKyQnmUBWtQkTIOzq5ZcORp7k+O1BjWGkDhPJDzP1ZCH5mjdLiabtYuv3ffBh7J01JNl81CuONGd97AFbfAL1t3cwH7g9yxv719ccba0k2RVGURsPvOd5XtY9iLobyi6CgzCbcPfsQmGziXuFjd8J+z6it1feOA3l5wInrceQMIAdrLMeAIObdwfz4e0KvV+2ZxdN28a931mN6P0dW4EVubD+HouHd0urzpjb8Qvkhpg6dytj5Y4mZGONeGEevE+vvzteUNPcwDjWGlTZFKARzQycT2X4KQ0p+yAHEVqBIKMnmJ02ccSwLXngQdvfBOFmMWeKwemoJRcO6p/3Pre1AFUVRGpfEikKzGPPcGBzjkB3MoWuH7mzxT95/Cv5VQE+XS8Dh108+x4GaA3BqBbx3AZgY/tCK6uoYEyd/xJJXTsA4nYFbYOUIqsUw0+Qw54+pDo7I9giFcwqpidWQHcymbETThCxUfF6BYxwc42QkVOJwVjibexiHGsNKmyTuWRhUxr4t/+KR4jPY+/ZXIbkAfBxfLBpB2Pl1rAEtxKpjzPjNl3lk9XgW3zmFUH6ICbNL+efze/nWZR0YXjicnJxaz7CW+lEURWk8kitSlORtZsuCKojlUFtxotYI9raN47Dz1StIdIL480kMxhgWv9w+MefEybbHCKR1cJSsLaE6Vg1AdayakrUl9Rp+jeVF9kIlPO9rYUHhF75WiowRGPyNGNXVQk6OYeGrwUMyiA9VtqYOLVRjWGmzxD0LA2HKDTDh/q3Mni0c2Pcl9u/pSEodywRD2YtFc0Mndp5L9NFXuDR4Je0+7s+H/5gMJkjxP6vZ+btpTP3rIB6du5VTer0DXQYCWtpHURSlsUiIMR4GD6/6JrE137dhbU7Q2sMxLwfE0+de/og/JM4hwYts/HPc74BADYFgACdmMMEYeWe/A9QdKgd1G3eR7REK75lEzdYLyD5jEn8Mf5+Kzyu+kA49WOKh/57Jcw6mu0tKt1FVdSqYIFVVNZSU7iAU6pYy73BlA5g1C8aOtaupXpJjpg1iNYYVxWXKxDOYMrE2xreyymCIEQiAiQWxVQjTeRhqvQWfPfYPPqs+lrjCjeXyRHE/njjnj3AgD7LLeH7OXfzhq29Q8VavBMU4q3Q9Tz9fQaeOwpNvLCDW7gOClc8zbcyx9cYkNxeae0yYoihtg1B+iIdG/ZCx88cS7fMXgtuG0L3vTt7aEID502rbOycYwe720XugMs+d4w+tcGx5tq5LodNbOJ1XwYGOOAVljF63HPKnE+4XJrI9wu63TyPw2u043RYQ7PoGx314KUO+kz5vpOTZzVQ/Nh+iOVQvdLj1/bFI/0dol9XuoDo0nYHt/1GQ7ng6PQ0wpGQIVeV9CWw7kP47p2ARBK+zjU+CNazKfZDI9u8eko735paVlyXsJ3+m226DaNT+m1RVGcrKRI1hRWlqQiGrrMrKhMJC+1+kpARmz4aaqAFiOE7AdRIkGcTVx7lX8S277RwAO8+N17ms6j6fMVvPBqdWMa7/YD23fucMqDkb6624AAgSlRhjFjn0Kjv0X8ZN7aX1x4RVRisPujSoKIqSKZJDJ9bvWc+tR90KJ70Ja4tstQmT7TvD9Qh/3pHEKhQ+vvo3eOs62HYhBBy4/DZ75uLx3Pre40zvvIZ1L/bBWf1jm1sivyTW41/8vy/twVQbnJhQWRWjpHQHdHmfsvIydm8YYithkAWOgfl/wpy0nqr85fXG1R4sMXvC/Vv57R0FGBOgXa7EjyfH7pY8u5lVL36VA5uegM2X4ThZjF1s6LUw8XpFw7rz2JrLqdl6PqZgISuyljOkZFaDnR5e8vq8d9bhfO1f5BRMThtLXVYGMafW2eRQQ97ZmziY5/1wUWNYUdIQCiUqglDItl72DOT162H0bTGcKNR6i/1xZ35F6ivNFgvC29cQc48cqIxy+c+f4r/vdYeadr5rZcXPidXUUDxtF0yDNVt3wzEf0GfoOi4787IU77LHkUjgKCwoJBgIEovFMBhmr5lNUe+iZmUQaxiHorQd/F5S7/3pjU/T6fKlrFq+go8X3Ey7A6fTtfdWPno3n43LTrF62gt/S1gFBN78vqvaXcP12enunazhtkZwqxC5XmdjrL4PVEOgGiSAkRgzXp3Pw3v/CvkRgjUvQuAVez3EeqTXFmHyl5F3dF6CzmJHKO7p9SdmH6h0+NYtm+madyI3/eAEOHE9xbf3iLetrqxyKCndTln0r+QdnReP3Q3uvJBHf/MDaqoDwDnxzxKLmpQ46FB+iLI77mNy2WRe+c/yBifpRSLWmfToo1BT0xkIw6oRVI8cnNZhUlgIgawaYtWB+A+O1VlZwPR0l2801BhWlAbiN5BDIejVK2g7Fu2DB37r2C5HcTxjOODbTo4/tvFo+5Z+h8TluqR3I5Q+caJ7LVseqHzhYErFgBMlkB1j+pPvJCxrFS8tTkjgKF5azNzr59b52WaVrmfqrL0IMOzbH9P+zLcO2WAM5Ye4sc+NzFw5E4Mh6kQPOWP4YEkTXijJtZflHXLoyKGEcXgKHOyPIK3+oSgtn4SmTN/yHzkpniBWVRUDY5tyIDGcmN+4hVqvsZAYc2ySVgv9IXRZcPIb8KX3YfPlsPIWYmtGwIghxKiyNe3j1w3AypsxnVcxRsZgtp+H859ByNGPIy/2hVgOuTnCj34Ejlc72Qi7N3yZ3cDyxYZO/T6NG8I20S/KzL3fRxa+Tm4wl6lDp7J612pWbb2U5TVC4vePITdHyMuD++5LDcGYXDiZJe8tOWgiXGR7hJJnNzP7pz+gusoLM3TvE8uG8kJW7XqVyPYI7Agl6NsrfzOV0hc+ri2Vx6iG/yN/QbQds6I0ApEIFBfDpk3QowdcdhmMHhPDidURl5YQc0wd48lJe37Ps2d5B0Bq4Bt30f3qp8kKZBF1omxem2eXAgF6lxDIX85VPa5i7zs9+GjjV/hy/11cNrg9FZ9XsG/L2RTfMhRiue5tonDFbRx13l/i8WR1eVP9RuNxx0HZ6x+z6oTbMX1nITvOp/uOu+mR14PxYzqnJI34r+klL1a82xVMkNwcSVn2m1XqhpJEcyBYzfD7/sT47w5ssLE9enoJMx773D7T3iUMv7hz2h8IkYj9Aqiuts8+kB1l+j/ebpS4bW3HrCjNF+/HeF4eVFRYPTDt2UU8cX/IVpMQx40l9hvEyXhJeIGk8Zir0j0jOgod37ahdZ+emnRNY8PqTnsJ/nOpayNbI916rmMgruc5ISHQfT/6A5ujYqQ2nKP/IwAIwlkHbuCdlafgHLUHM//3tbofkIDDz38W5Pd/iFFVaS/Z6dxX6NRtDx07Cj2PGcg5oU9ZnfUQAOecfA6rd62Ob1d8XkHe0XmMe2EclQv/B/Pqr3zedpdgFYwcjOQvI/v9QZjHF7jeacjOMfzpyQ3cNv82ou9eQOC0JUwfVRSPxz6clb369K8aw4qSIWbNglFjYphYslKElFjjtMavfy51HHMVb/ttcPx2OGqvHX7nCnBy3NNjcP4DUNXeZlbHshIV5JKJsOAear0criK+YjQdLvwn+yr34eAgCGd+/kOyt11sjekzL+NH3+0VNxr9dLj4EfYu/GFcyUpWDQPvvIuefT/huHbH8WDkQaJOlGAgyDf2/o2Xpl6b8HkDQYfwLQE4bhu7Oz3J3s/3subJ4Xz6Vn+rWN0fAFkX/ZZpl0+r9fb48BRnXsUwVr/Yi4cfjhKLuZ/RVcbDv9mZ8eePhx0hiqft4v3PdnHKcafwzBMnYeJNV2IELv4lrz12xWGHVqgxrCgtD29Fqs9p+fz+VwXWSEwxdtMhpFSnANIby/6xdI4Qh9rwDZLm1WWYO3BBMXzzF7VDL98L/x5fa2B/5e+wrRA+7YLnXMntvI2qXaeTuLLpyicxsnNARlxMzSmLbUvs7edBeSFUHge7z4Gz/4n0fwSz/VyYs8B6ggMx6D6/VuZjP4DOq+Cta2HrJb7PFePY0zbx3+1n2u/OYDWBkZfwtf7/Zf0H63GMQ0ACXNnjSsafP/6QdLIaw4pyhPB7Ts85B6b/7T+sWZwPjrf0lo50nuE0zT/qrInsvw6+eYntpyEGndfBgePhk9NT7y8x29p075chq9Ieeu+CWm9Dpzfhgz6+z+GTK3s/1ByDX8Ex5A4YeD+suBlW32SXDM98HhbfCZ/mJ34mqQEJ+Bzgxn1mQcCBYA2MHOwuoUGfzn3gvRA713cn97jPqPr0S+w96Z/ETNQq42guidnjrjwFZbB2BKy+0Sps794EaksqSRSuGMMJFzxNtxO6URWtotMxnejZsechx0SrMawoLRu/93j1atj4nwrWbaxi3/bOJOaP1OXY8BuvieEJiJOmlFu6ayXpdonCaS/Du0PTXDdmPdBZ1Um63n9d23EvXjEjIbEw3XdPFDq8C2c/DRU9YNOVPo+3ywX3WwfM/pOs4et2beXxhQme6Np7+/F+GLj3OuMVKPxVXN975AZzWThiYYN1sBrDitKM8CvT55+H99+3y3GffmorVlTXGIwDgYBdsorFcBWN5x3wG3V1UV/4BdStYOs7nm5eQ3GgwxaoOgb+e2o987zP6G3XJVMMOr0NX/6XVbgfnm0zvBMK7DvQ7hOo7JBGZleej0+3S48pZZYcUu7b7TXrefc8Ggc6kn36v1l05/2NooxbMqp/lbZMPN447jG2uuTEE2HPnobqXscNfTjYCiFJx33G6d7TiSdf13kv/3jyu/cK1HNuOq92umv6iVn9GW3nNqzyrUImzE922Hj3crc7r4NgNRQshHafQsEi7h1xJZMGTkojTxoJ1RhWlJZBupg1sAXPdzsbYNc5/OvvJxGL1WUM1xdvnC4sA99+XddIvlc6JZou/jndtUmakyzToXyWdBzMc55Okdfz5ZRikCddK6uKUX94iumji+qQJ+mqagwrSqvE09379sGaNXCtG/l16621c+JhvgkYEEN2tiEgQaqrk+fUpYc8Ped5Vv26tC4PNNSvSw8lVC/dd0pDzktHXXrb7/xJvoaBrEpm/u/WBudz1Kd/tZqEojQjkku61Y53A7oBELnNhl7s3m2P7d0L27bB9u3gOOBXiN27w+bNnlKEtAoF7xyHQBA3exrfeENCNZK9C+mUbF339fY9pZ9srKYz3Osy6v3XTPeDIZDmWF0ekuTnkCxT0LZ7Lb8ozX0URWlL1KW7AZ5+utY4HjPGlkMD213tD38QKiqk1vERL0Nm97Ozrc6qqQEJGDp1rWDPtjy7oiUxOpzyKR/vOgHjBBCxBjdAMMvhlLN2sG1dV1J1sT/+uFb3Gwdf1QcHui0h+P4FxGrqCt2oy6mRbAQfTN8mH4PEHwHprhEg4BxFxVu9YDiHTUaNYREZCvwe+7PlEWPM/UnHc4ESoB9QAXzXGFMuInnAU8DXgceNMWMzKaeitCTqUrrpPBPhMMyaJTz9NPTpY0Mxdu+G557zlK3EFWhuboCpU20c3MMPS1xhe3NqvRWeknJADJ2+uoa9b52D4xiMRBGThXEECRhOOv0Ddm/pTFoj3H9RcejzrZdZP+9iYjV+peeWDkohnTHdEPzGbrrz6zKMSRq328FggKLh3Rp4b0VR2hrhsH159OpVf9lGr6a9fw54JScDQCdfs40g993VgXHjaptvTJ3qrSoGCYW6MWuWNa7f3/M5O7e1A6BduwDfGrGDvz18MphAgu73mksFs2L8aWoHep2UxcTJH/HaKx1wjHHDODy9XJfe9Y2nOHXTGbeOzUMx4oatAYEagoFsYtG6Pc7BYO2PiMMlY2ESIhIE3gG+CewA3gC+Z4zZ6JszBviaMWaUiFwPXGOM+a6IHIOtAP1V4KsNMYZ1mU5RGo6/ni+kadkZSa+MvfANfxhHKFT/9TxlXF0NVVW29Nz48XZe8peCd9+N/6mgMncbN92QTa+TetV6wo/dDZ1XsWFBHzav7oxnmIrAwIHQoYO93t4DFWzbtR850JGuJx8DwNKl1vYOBODUU+G998Aa5THO/toBuhQc4JV/5eE4Nm4vEICsLOG88+C11zyvey3BIDz0UOIX3cHQMAlFUQ6X5HrsB6vPfijn1XWt5BA+L4EQbHK4f/uJpz/i3e1VfL+okuEXnRHX350718599FGIRq0evfnm2u8Zr6pPiu4Hysth3Tqrx4NBmDat8fRvJo3hEDDZGHOpuz8JwBhzn2/Oi+6ciIhkAbuBTsYVSkRGAv3VGFYUJZlDbYxxKF8EdRn7/i+AL9KMQ41hRVGUhhvwjXUeHDlj+DpgqDHmZnf/h8C5fsNWRN505+xw97e6cz5y90dSjzEsImEgDNC1a9d+27Zty8hnURRFaQzUGFYURTky1Kd/G1I1utlijJlljOlvjOnfqVOnIy2OoiiKoiiK0sLIpDG8E8j37Xdxx9LOccMkjscm0imKoiiHgYgMFZFNIrJFRCamOZ4rIk+6x5eJSIE7ni0ic0RkvYi85YW4KYqitFYyaQy/AXQXkdNEJAe4HpiXNGceMMLdvg541WQqbkNRFKWN4CYwTwMuA3oC3xORnknTbgI+NsacCTwITHHHvw3kGmN6YSv93OoZyoqiKK2RjBnDxpgoMBZ4EXgL+IcxZoOI/FpErnKnPQrkicgW4KdA3HshIuXA74CRIrIjjSJXFEVR0jMA2GKMedcYUw38Hbg6ac7VwBx3+ylgiIh4hZCOcVfrjgKqgU+bRmxFUZSmJ6N1ho0x84H5SWN3+bYrsV6IdOcWZFI2RVGUVsypwHbf/g7g3LrmGGOiIvIJ4NV4vxrYBRwN/I8xZm/GJVYURTlCtOgEOkVRFKXRGYCtqn8KcBrwf0Xk9ORJIhIWkRUisuLDDz9sahkVRVEajVbTjnnlypUficih1lbrCHyUCXkOEZUjEZUjEZUjkZYsR1O1qzuUBOYdSQnM3wdeMMbUAHtEZCnQH3jXf7IxZhYwC0BEPlT9e9ioHImoHImoHIk0qv5tNcawMeaQa6uJyIrmUPNT5VA5VA6Vo5GJJzBjjd7rsUauHy+BOYIvgVlE3gO+AfzZ7QZ6HjC1vpup/lU5VA6VoyXLoWESiqIorYzDTGCeBhwrIhuwRvVsY8y6pv0EiqIoTUer8QwriqIotXzRBGZjzP5044qiKK2Vtu4ZnnWkBXBRORJRORJRORJROVoHzeX5qRyJqByJqByJtEo5RHtcKIqiKIqiKG2Vtu4ZVhRFURRFUdowagwriqIoiqIobZZWbQyLyGMiskdE3vSNdRCRl0Vks/t+gjsuIvIHEdkiIutEpG+G5ZgsIjtFZI37utx3bJIrxyYRubSRZMgXkYUislFENojIT9zxJn0e9cjR1M+jnYgsF5G1rhy/csdPE5Fl7v2eFJEcdzzX3d/iHi/IsByPi8h/fM+jjzuesb9T9/pBEVktIs+6+036POqRo8mfh4iUi8h6934r3LEm1x8tlTr0nupf1b+qf+uWR/VvrQxNq3+NMa32BQwC+gJv+saKgYnu9kRgirt9OfA8INi6mssyLMdk4Gdp5vYE1gK52O5PW4FgI8hwMtDX3f4S8I57ryZ9HvXI0dTPQ4Bj3e1sYJn7Of8BXO+OzwBGu9tjgBnu9vXAk430POqS43HgujTzM/Z36l7/p8BfgWfd/SZ9HvXI0eTPAygHOiaNNbn+aKkvVP/6r6v6N/G6qn/Ty6P6t/ba5TSh/m3VnmFjzGJgb9Lw1cAcd3sOMNw3XmIsrwPtReTkDMpRF1cDfzfGVBlj/gNswbZHPVwZdhljVrnbn2Frj55KEz+PeuSoi0w9D2NsCSmwSjAbMNhmA0+548nPw3tOTwFDREQyKEddZOzvVES6AFcAj7j7QhM/j3RyHISMPY967tek+qOlovo3QQbVv4lyqP5NQvVvg8jY/5dWbQzXwUnGmF3u9m7gJHf7VGC7b94O6lcSjcFY16X/mOfubwo53CWVc7C/go/Y80iSA5r4ebhLQWuAPcDLWK/HPmMbFiTfKy6He/wTIC8TchhjvOfxG/d5PCgiuclypJHxcJkKjAccdz+PI/A80sjh0dTPwwAvichKEQm7Y81Jf7REmtPzU/2r+lf178Hl8GjV+rctGsNxjPWvH6nactOBM4A+wC7g/zXFTUXkWOBpYJwx5lP/saZ8HmnkaPLnYYyJGWP6AF2w3o6zMn3PhsghIl8FJrnyfB3oAEzIpAwiMgzYY4xZmcn7HIYcTfo8XC40xvQFLgNuE5FB/oNHWH+0eFT/qv5V/WtR/ZuWJtW/bdEY/sBzn7vve9zxnUC+b14XdywjGGM+cP8TOsDD1C49ZUwOEcnGKsAnjDH/dIeb/Hmkk+NIPA8PY8w+YCEQwi6veJ0Z/feKy+EePx6oyJAcQ93lTGOMqQJmk/nncQFwlYiUA3/HLs/9nqZ/HilyiMhfjsDzwBiz033fA8x179ks9EcLplk8P9W/qn/rkUP1bxvUv23RGJ4HjHC3RwDP+MaL3KzE84BPfO74RicpnuUawMt0ngdcLzZb9DSgO7C8Ee4nwKPAW8aY3/kONenzqEuOI/A8OolIe3f7KOCb2Pi5hcB17rTk5+E9p+uAV91fppmQ423ff3jBxkX5n0ej/7sYYyYZY7oYYwqwCRmvGmN+QBM/jzrk+D9N/TxE5BgR+ZK3DVzi3rNZ6I8WTLN4fqp/Vf/WI4fq37aof00jZiA2txfwN+ySTw02huQmbFzNAmAz8ArQwZ0rwDRs3NJ6oH+G5fize5917j/kyb75t7tybAIuayQZLsQuKawD1rivy5v6edQjR1M/j68Bq937vQnc5Y6fjlX2W4D/BXLd8Xbu/hb3+OkZluNV93m8CfyF2oznjP2d+mQqpDaLuEmfRz1yNOnzcD/3Wve1AbjdHW9y/dFSX6j+9cug+jdRDtW/dctUiOrfJte/2o5ZURRFURRFabO0xTAJRVEURVEURQHUGFYURVEURVHaMGoMK4qiKIqiKG0WNYYVRVEURVGUNosaw4qiKIqiKEqbRY1hpVUiIjERWeN7TWzEaxeIyJsHn6koitL2UP2rtDSyDj5FUVokB4xtsakoiqI0Lap/lRaFeoaVNoWIlItIsYisF5HlInKmO14gIq+KyDoRWSAiXd3xk0RkroisdV/nu5cKisjDIrJBRF5yuxchIj8WkY3udf5+hD6moihKs0P1r9JcUWNYaa0clbRM913fsU+MMb2APwFT3bE/AnOMMV8DngD+4I7/AVhkjOkN9MV2wwHblnSaMeYrwD7gWnd8InCOe51RmfpwiqIozRjVv0qLQjvQKa0SEdlvjDk2zXg58A1jzLsikg3sNsbkichH2BakNe74LmNMRxH5EOhijPObAikAAAE/SURBVKnyXaMAeNkY093dnwBkG2PuEZEXgP1AKVBqjNmf4Y+qKIrSrFD9q7Q01DOstEVMHduHQpVvO0Zt/P0V2B7pfYE3RETj8hVFUWpR/as0O9QYVtoi3/W9R9ztfwPXu9s/AJa42wuA0QAiEhSR4+u6qIgEgHxjzEJgAnA8kOIdURRFacOo/lWaHfqrSWmtHCUia3z7LxhjvPI+J4jIOqx34Xvu2I+A2SLyc+BD4AZ3/CfALBG5CeuBGA3squOeQeAvrsIW4A/GmH2N9okURVFaBqp/lRaFxgwrbQo3Zq2/MeajIy2LoihKW0L1r9Jc0TAJRVEURVEUpc2inmFFURRFURSlzaKeYUVRFEVRFKXNosawoiiKoiiK0mZRY1hRFEVRFEVps6gxrCiKoiiKorRZ1BhWFEVRFEVR2iz/H9GjIHEHN3R4AAAAAElFTkSuQmCC\n", + "text/plain": [ + "
" + ] + }, + "metadata": { + "tags": [], + "needs_background": "light" + } + } + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "f86dWOyZKmN9" + }, + "source": [ + "Great results! From these graphs, we can see several exciting things:\n", + "\n", + "* The overall loss and MAE are much better than our previous network\n", + "* Metrics are better for validation than training, which means the network is not overfitting\n", + "\n", + "The reason the metrics for validation are better than those for training is that validation metrics are calculated at the end of each epoch, while training metrics are calculated throughout the epoch, so validation happens on a model that has been trained slightly longer.\n", + "\n", + "This all means our network seems to be performing well! To confirm, let's check its predictions against the test dataset we set aside earlier:\n" + ] + }, + { + "cell_type": "code", + "metadata": { + "id": "lZfztKKyhLxX", + "outputId": "f48f33ad-aba0-4c62-ba15-cc742bd23805", + "colab": { + "base_uri": "https://localhost:8080/", + "height": 299 + } + }, + "source": [ + "# Calculate and print the loss on our test dataset\n", + "test_loss, test_mae = model.evaluate(x_test, y_test)\n", + "\n", + "# Make predictions based on our test dataset\n", + "y_test_pred = model.predict(x_test)\n", + "\n", + "# Graph the predictions against the actual values\n", + "plt.clf()\n", + "plt.title('Comparison of predictions and actual values')\n", + "plt.plot(x_test, y_test, 'b.', label='Actual values')\n", + "plt.plot(x_test, y_test_pred, 'r.', label='TF predicted')\n", + "plt.legend()\n", + "plt.show()" + ], + "execution_count": null, + "outputs": [ + { + "output_type": "stream", + "text": [ + "7/7 [==============================] - 0s 2ms/step - loss: 0.0102 - mae: 0.0815\n" + ], + "name": "stdout" + }, + { + "output_type": "display_data", + "data": { + "image/png": "iVBORw0KGgoAAAANSUhEUgAAAXwAAAEICAYAAABcVE8dAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAALEgAACxIB0t1+/AAAADh0RVh0U29mdHdhcmUAbWF0cGxvdGxpYiB2ZXJzaW9uMy4yLjIsIGh0dHA6Ly9tYXRwbG90bGliLm9yZy+WH4yJAAAgAElEQVR4nO2de3hU1dX/P2smCVFR0UBVRESp+mqNgCJ6fgKOYkHrDaXWqjVilYAFX2kVkL61ptoKRFtpUZQIKBFErQiK2reUyMhtvABieQutgqKCqDRe6gVymdm/P/aZZDLM5DrJ3NbneeaZmXP22Wefc2a+Z521115bjDEoiqIomY8n2Q1QFEVROgYVfEVRlCxBBV9RFCVLUMFXFEXJElTwFUVRsgQVfEVRlCxBBT+DEZFrRGRZstsRRkT2E5GlIvKliPw5CfsvEZH57ueeIvK1iHhbUc8vRWR24lvYMYjIYyLy22S3ozEir1WC6035Y29PVPCbgYhcLSLrXIHYJSJ/EZGByW5XUxhjFhhjhia7HRH8EDgMKDDGXJHMhhhjPjDGdDbGBBsrJyI+EdkRte09xpgb27eF6YWIjBSR1cluh9I4KvhNICK/AKYD92DFqicwE7g0me1qChHJSXYbYnA08LYxpratFaXo8SlKamOM0VecF3Aw8DVwRSNlOmFvCB+5r+lAJ3edD9gBTAQ+BXYBw4EfAG8DnwG/jKirBHgGeAr4CtgA9IlYfzuwzV23GbgsYt1IYA1wP1AJ/NZdttpdL+66T4H/AJuAkyOOsxzYDbwP/ArwRNS7GrgP+Bx4D7igkfNxIuAHvgD+AVziLv8NUA3UuOf0hhjbNnX824FJwN+BKiAHOBNY6+7vLcAXUf4Y4BW3rr8BDwDz3XW9AAPkuN8PBR51r+HnwBLgAGAPEHLb/DXQ3W3n/Ij9XOIe6xfusZ8Y1ebb3DZ/6R5bvruuK/CCu91nwKrweY9xbv4IfOheu/XAoKjz9rR7Db9y29I/Yn0/91x+5e7/SeC3cfbTG3gZ+xv6N7AA6BKx/ijgWfe3Uume0xOBvUDQPUdfuGX9wI1Rv9HVLTim+XHauAW4KOJ7jtueU93vfwY+ds/3SuB7EWUfCx97dHvcZQb4bsR/+z7gA+AT4GFgv5Zeu1R6qYXfOA6QDyxupMz/YEWnL9AHGIAVzDCHu3UcCfwaeAT4CXAaMAi4Q0SOiSh/KfYHeyjwBLBERHLdddvcbQ7GCuh8ETkiYtszgHexTyK/i2rnUGAwcLy7/Y+wf1iAGe6yY4GzgSLg+qh6/4X9kZcCc0REok+E286lwDLgO8DNwAIROcEYcyf2KekpY10pc6K3b8bxA1wFXAh0cY/zRezN7VCssC4SkW5u2SewQtIVuBu4Ls4+AR4H9ge+57b9fmPMN8AFwEdumzsbYz6KOubjgYXAeKAb8BKwVETyIor9CDgfewM6BSs0ALdiDYJu7rH8Eis4sXgD+xsLn5c/i0h+xPpLsELeBXgeK8S47VjiHt+h2HM7opHzIMAU7I3tRKzAl7h1ebEi9z72hnkk8KQxZgswBgi456hLI/W35JjisRD7OwgzDPi3MWaD+/0vwHHY67gBe9NqDVOx/5e+wHep/w9Dy65d6pDsO04qv4BrgI+bKLMN+EHE92HAdvezD2shet3vB2J/FGdElF8PDHc/lwCvRqzzYJ8KBsXZ90bgUvfzSOCDqPUjqbfwz8U+VZxJhCUCeLGW90kRy0YD/og6tkas2989hsNjtGcQ1rKKrH8hUBJxfDGttuYcP9Za/mnE+knA41F1/BUr7D2BWuCAiHVPEMPCB47AWvGHxGiTD9gRo53heu4Ano5q807cJw23zT+JWF8KPOx+vgt4DteibOFv83Pcpx+3Pcsj1p0E7HE/D8Y+tUjE+rXEsfBj7Gc48Kb72cFa0jkxytX91iKW+WnEwm/GMcWz8L+LfVrZ3/2+APh1nLJd3Ot8sPv9MZph4WNvfN8AvSPWOcB7bb12yXyphd84lUDXJvzF3bEWT5j33WV1dZj6jsE97vsnEev3AJ0jvn8Y/mCMCWGtiO4AIlIkIhtF5AsR+QI4GWu97rNtNMaYl7FW34PApyJSJiIHudvnxjiGIyO+fxxRz7fux8g2h+kOfOi2O15dTRH3+KPXY/sErgifD/ecDMQKeHfgc2Ot9Mi2xOIo4DNjzOctaGeYBtffbfOHxDl/wLfUn7t7ga3AMhF5V0Ruj7cTEblNRLa4EU5fYJ/IIq999D7y3d9td2CncVXKJd55QEQOE5EnRWSniPwHmB+xn6OA900C+mCaeUwxMcZsxbp1LhaR/bFPN0+4dXpFZKqIbHPbv93drMl6o+iGNW7WR/y2/tddDi24dqmECn7jBLC+4uGNlPkIKzxherrLWstR4Q8i4gF6AB+JyNFYd9A4bJRLF+D/sJZImEYfKY0xfzLGnIa1AI8HJmD9tDUxjmFnK9r+EXCU2+7W1hXz+CPWRx7jh1gLv0vE6wBjzFTsk8EhInJAVFti8SFwqIjEckU09Zje4Pq7rq6jaMYxG2O+Msbcaow5FitavxCRIdHlRGQQth/oR9inkC5Y//Q+brUY7AKOjHLBxTsPYN1uBig0xhyEdT+Gt/0Q6BnHAIp1nr7BimaYw8Mf2nhMUO/WuRTY7N4EAK52l52HvYH0Cu+yqfaJyOER6/6NNca+F/HbOtgY0xmaf+1SDRX8RjDGfIn12T0oIsNFZH8RyRWRC0Sk1C22EPiViHQTka5u+bbED58mIpe7f6rx2BvOq9gORIN9pEZErsda+M1CRE4XkTNcf/g32E62kPv08TTwOxE50L2x/KKVx/Aa1rqc6J4nH3Ax1rfcXOIdfyzmY628Ya5ll++GUfYwxrwPrAN+IyJ5bhjtxbEqMcbswvp9Z4rIIW7bB7urPwEKROTgOG14GrhQRIa45/ZWt81rmzpQEblIRL7rivGX2E7PUIyiB2LdU7uBHBH5NXBQU/W7BNxt/9s9rsux/UzxOBDb8fqliByJNQrCvI69gUwVkQPc832Wu+4ToEdU38VG4HL3f/Nd4IYEHRPY39RQ4CZc6z6i3irs0/n+2BtYPN4Cvicifd2+g5LwCvdJ7RHgfhH5DoCIHCkiw9zPzb12KYUKfhMYY36PFcBfYX+cH2Kt7CVukd9iheXv2MiXDe6y1vIccCXWn3ktcLkxpsYYsxn4PfYP/AlQiI3KaS4HYX/An2Mf6Suxj6VgO1e/wXb4rsb+gea2tOHGmGqsqF6AtZBmAkXGmH+2oJqYxx9nfx9irblfUn9tJlD/u74a2+H8GXAnNoolHtdin3T+iY1kGu/u45/Ym/q77qN9pHsJY8y/sFbwDPeYLwYuds9FUxwHLMcKbACYaYxZEaPcX7HuhLex124vjbjvotpXDVyO9Vd/hj23zzayyW+AU7Ei9mJkWdc4uBjr4/4A62670l39MjY66GMR+be77H5s/9AnwDwadp62+pjctuzCnrP/h408ClPu1rcTG8kWz1jAGPM21he/HHgH+9uPZBLWbfOq6x5aDpzgrmvutUsppKFrT0kmIlKC7QT6SbLbkgyy/fgVpb1RC19RFCVLUMFXFEXJEtSloyiKkiWoha8oipIlpGwCqq5du5pevXoluxmKoihpxfr16/9tjOkWa13KCn6vXr1Yt25dspuhKIqSVohI3JHU6tJRFEXJElTwFUVRsgQVfEVRlCwhZX34iqKkBjU1NezYsYO9e/cmuylKBPn5+fTo0YPc3NymC7uo4CuK0ig7duzgwAMPpFevXsSY90ZJAsYYKisr2bFjB8ccc0zTG7ioS0dRlEbZu3cvBQUFKvYphIhQUFDQ4qcuFfwMJRCAKVPsu6K0FRX71KM110RdOhlIIABDhkB1NeTlQUUFOE6yW6UoSrJRCz8D8fut2AeD9t3vT3aLFKXtLFmyBBHhn/9senqF6dOn8+233zZZLh6PPfYY48aNa/X2ia4nUajgZyA+n7XsvV777vMlu0WK0nYWLlzIwIEDWbhwYZNl2yr4mYoKfgbiONaNc/fdzXPnqL9fSTSJ/k19/fXXrF69mjlz5vDkk/UzZgaDQW677TZOPvlkTjnlFGbMmMGf/vQnPvroI8455xzOOeccADp37ly3zTPPPMPIkSMBWLp0KWeccQb9+vXjvPPO45NPPonbhlAoRK9evfjiiy/qlh133HF88sknzapn5MiRPPPMM3XfI9t07733cvrpp3PKKadw5513AvDNN99w4YUX0qdPH04++WSeeuqpfepsKerDz1Acp3l++9b4+wMB6yby+bRvQNmX9uhDeu655zj//PM5/vjjKSgoYP369Zx22mmUlZWxfft2Nm7cSE5ODp999hmHHnoof/jDH1ixYgVdu3ZttN6BAwfy6quvIiLMnj2b0tJSfv/738cs6/F4uPTSS1m8eDHXX389r732GkcffTSHHXZYi+qJZtmyZbzzzju8/vrrGGO45JJLWLlyJbt376Z79+68+OKLAHz55ZctO2kxUMHPcqL9/eXljYu5dggrTRGrD6mtv5GFCxdyyy23APDjH/+YhQsXctppp7F8+XLGjBlDTo6VskMPPbRF9e7YsYMrr7ySXbt2UV1d3WRM+5VXXsldd93F9ddfz5NPPsmVV17ZqnoiWbZsGcuWLaNfv36AfZp55513GDRoELfeeiuTJk3ioosuYtCgQS06tlioSyfL8flgFGX8L8O40ZQxdy7ccYcV9ViP4411CKtrSIHE9yF99tlnvPzyy9x444306tWLe++9l6effpqWTN4UGcIYGbt+8803M27cODZt2sSsWbOajGt3HIetW7eye/dulixZwuWXX97senJycgiFQoB1D1VX23nujTFMnjyZjRs3snHjRrZu3coNN9zA8ccfz4YNGygsLORXv/oVd911V7OPNx4q+FlELEE+6sFJzAyOZijLeCg0mt9UT2JCcAp99wQoL9+3jnh/5rDl39jNQskOWtqH1BTPPPMM1157Le+//z7bt2/nww8/5JhjjmHVqlV8//vfZ9asWdTW1gL25gBw4IEH8tVXX9XVcdhhh7FlyxZCoRCLFy+uW/7ll19y5JFHAjBv3rwm2yIiXHbZZfziF7/gxBNPpKCgoNn19OrVi/Xr1wPw/PPPU1NTA8CwYcOYO3cuX3/9NQA7d+7k008/5aOPPmL//ffnJz/5CRMmTGDDhg3NP2lxUJdOmtBWv3mkK+YsT4C7jivn5K4f033lcwAIYIDbuA8IIQhPzrqaQNH8BvsL/5mj29Iej/FK+tLcPqTmsHDhQiZNmtRg2YgRI1i4cCEzZszg7bff5pRTTiE3N5dRo0Yxbtw4iouLOf/88+nevTsrVqxg6tSpXHTRRXTr1o3+/fvXiWtJSQlXXHEFhxxyCOeeey7vvfdek+258sorOf3003nsscfqljWnnlGjRnHppZfSp08fzj//fA444AAAhg4dypYtW3DcE9a5c2fmz5/P1q1bmTBhAh6Ph9zcXB566KHWnsI6EjKnrYjMBS4CPjXGnBxjvQB/BH4AfAuMNMY0ervq37+/0QlQLG3xm4dvFAc/XcapG+ewl3wcAuRRU1cmLPYAIcAb8X1T32s45c35DeqKddNR337msmXLFk488cRkN0OJQaxrIyLrjTH9Y5VPlIX/GPAAEMMJAMAFwHHu6wzgIfddaQattZ5f+UkZXRdMp4gv6M6uBuvCHk0TfomHf3//KrouewKDqbsJFG58AsoG8/6blUye62N10Ikp6PEsf0VRUoeECL4xZqWI9GqkyKVAubGPE6+KSBcROcIYs6uRbRSXsN88bD03pxNsx08mMXhBaYNl0SIPYLy5eEbdgBQV8R3H4e/9oHDjAgwRlv/YsRwVNPyvyeFRrmdBVRF+v7OPqEe6dyK/K4qSGnSUD/9I4MOI7zvcZQ0EX0SKgWKAnj17dlDT0oPrrrPvRUXNENJAgO5P3AfEEXlPDu+ccBEHnXA4R0xsWOE3M+fzxEC4KvQEIUC8OUgoiMeE6ESQYmbx09BsquefCgU3QHFx5G7VraMoKUxKddoaY8qAMrA+/CQ3JyWIFtGiojiFIn0pfj8S4ZYJ80WXowmd0o+CqRM5IY4SOw6wej7zy8dyNn6O7lcA48fD3r1gDF4MHmrptPl1GP06bNsG06YB2nGrKKlORwn+TuCoiO893GVKEzQporHMap8Pyc/H7K3CGEP1EUeTXzKZQyKs8cawERYO4O6osBDKy5G5c6G6mgZJWe+9F95+GyZOxOdzWux6UhSl4+ioOPzngSKxnAl8qf775tHkIJZ4d4SKCuR3v+Ufs9Zw/83vEShsntjHxHHgoYds3cOHAxHuIWNgyRLw+XAIJDT+WlGUxJIQC19EFgI+oKuI7ADuBHIBjDEPAy9hQzK3YsMyr0/EfrOBfaJfCMAUf737Jl6PruMQwEmsT91xYPFi3hw2iT7L7gXXbQRATQ3cfjvORx/hXH45ONPasCNFqaeyspIhQ4YA8PHHH+P1eunWrRsAb731Fn369Kkru2TJEnr16tVubXnsscdYt24dDzzwAA8//DD7778/RTH9rLB9+3bWrl3L1Vdf3aJ9jBw5kosuuogf/vCHiWhyAxIVpXNVE+sNMDYR+8o0mjOgqm4Qy6RJcN99YAzk59creJx4yOb41Fs6oCsQgMEvT2MkvZnJz8ghCLhD11eutIVKS2HnTpg/vwVnQlFiU1BQwMaNGwE7wKlz587cdtttgB2kFF7XFoLBIF6vt0XbjBkzptH127dv54knnmix4LcnmlohiTQ7HUEgAGefbYU0FLKCv3dvw/jHyZP3Ueym3EGtSYfg99smzKaYwayiTMbw8fAx0L17w4JPPAFlZZpcJ1tJkcRKfr+fwYMHc+GFF3LCCScwZsyYunw2nTt35tZbb6VPnz4EAgHmz5/PgAED6Nu3L6NHjyYYtMbMo48+yvHHH8+AAQNYs2ZNXd0lJSXcd5+Nhtu6dSvnnXceffr04dRTT2Xbtm3cfvvtrFq1ir59+3L//fcTDAaZMGFCXRrkWbNmAdYtOm7cOE444QTOO+88Pv3003Y7Hyr4SaRZM1MFAnDOObByZb3fHECkyV7RpnKatGZmLJ8POnUCjwfW5TjIww9xxOKHINqKMQbGjtXkOtlIByZW2rNnD3379qVv375cdtllMcu8/vrrzJgxg82bN7Nt2zaeffZZwOabP+OMM3jrrbcoKCjgqaeeYs2aNWzcuBGv18uCBQvYtWsXd955J2vWrGH16tVs3rw55j6uueYaxo4dy1tvvcXatWs54ogjmDp1KoMGDWLjxo38/Oc/Z86cORx88MG88cYbvPHGGzzyyCO89957LF68mH/9619s3ryZ8vJy1q5d227nK6XCMrONZg2oKi+HqiqgYQqEHVfdRo9m+GDC7qCwwRXpumnNgK64HqRp0/h04066LnsCASQ3x95JQiGN0cw2OjA+d7/99mvSpTNgwACOPfZYAK666ipWr17ND3/4Q7xeLyNGjACgoqKC9evXc/rppwP2RvKd73yH1157DZ/PV9dncOWVV/L22283qP+rr75i586ddTec/Pz8mO1YtmwZf//73+smQfnyyy955513WLlyJVdddRVer5fu3btz7rnntvJsNI0KfhJpMh1BIABz5wL1Qh9E+D0TCH1vGpObuZ94A6Jamw4hVmKsQACGrJrPqZ6xnOvxc93PC+g9Y7zGaGYjrbEk2pHI1MiR3/Pz8+v89sYYrrvuOqZMmdKg7JIlSxLWDmMMM2bMYNiwYQ2Wv/TSSwnbR1OoSyfJxHG/W/x+ayUBiPCGDMDnWcNv9pvW4D/UlLu0MddNo/tvov7I5eF9rAk53GMm83SX4rj+pE1lAfzDprCpTN08GUmi8yO3kddff5333nuPUCjEU089xcCBA/cpM2TIEJ555pk6//lnn33G+++/zxlnnMErr7xCZWUlNTU1/PnPf95n2wMPPJAePXrU3Ryqqqr49ttv90nRPGzYMB566KG6tMhvv/0233zzDYMHD+app54iGAyya9cuVqxY0R6nAVALP7Xx+Qjm5EHIWkr7/Wk6F1Y63Our/w81ls4gLMQFBa03uOLVH718+vQY+4jxKLCpLEDv0UM4kWqql+WxiQoKi9XVk3EkMj9yGzn99NMZN24cW7du5Zxzzonp6z/ppJP47W9/y9ChQwmFQuTm5vLggw9y5plnUlJSguM4dOnShb59+8bcx+OPP87o0aP59a9/TW5uLn/+85855ZRT8Hq99OnTh5EjR3LLLbewfft2Tj31VIwxdOvWjSVLlnDZZZfx8ssvc9JJJ9GzZ8+6NMntgjEmJV+nnXaaySpmzTJm6FD77rJ2rTFn5601v5R7zNl5a83atftuds89xni9xoB9v+ee+m33288u228/W+0995iYdUSzdm192Xj1Ry4XMWbMmIbbxWPF0HtMDXbDarxmxdB7WnCSlGSwefPmZDeh1axYscJceOGFyW5GuxHr2gDrTBxdVQs/FSgrg9Gj7edly+x7cTF+P6wOOrxiHLzB2H1f8dyl0W6cykrrummKsOVeVWUjcX7xi9j1+3yQ4/bLGmO7GoqKmt5HwQgf1cvyMFRTQx4FI3xNN0pRlISggp9sysr2VclFi6C4eB8xLyiAm26yRcJZM+N1vDbVbxZvwJXfb8U+FLKv+++HBx6wN4zIso4D118Ps2ZZwQ/GuCHF2kdhscMmKqhc5KdghE/dOUq74vP58GnAQD3xTP9kv7LCpTNxovWJRL+i3Dr33GMX5eXVF+nUqWn3TDwXS7S7J3L92rXG5OTU78fjqXfjxKq/sXrirWuqoc1xDSkdx+bNm00oFEp2M5QoQqFQi106SRf2eK+MF/y1a62ausoaAvPNfoearRNnxSx+zz3WVx4WYpH4QtwU8fzyYWbNMiY31zavKbGOJ85N7aNBBRF3hr/PWtv8G4XSIbz77rtm9+7dKvopRCgUMrt37zbvvvvuPusaE3x16SSL8nLrM6E+xn78nimU/7GYFcNj++pzc62LBtoW3tyUu6e42GZEbk58frxgjGaHYkd2NlRVccC9JZxaVcKakKPjtVKEHj16sGPHDnbv3p3spigR5Ofn06NHjxZtk5BJzNuDjJ7EPJwuwR1BG0QoZQK/xGaYHDPGZiOOtVm5O2tws2a+aqIJiZ5/NrrOZu0jspc4FMKIhz2mE0M9FWzo5KRCGLeipBUdMYm50hL8fkxNrU2VIMLqE0fzy81NpxNOZGhzosOkGxvN22RDKiqgpASWL0dCIfbzVFF+bAnfTCihUNVeURKGjrTtICJHpW4q8LEnlEcNXvaYfP7StYjcXJsPLe40hilOaxKx1eE4UFJCMLcTIfFAKMSx7y6ncPwQNpUFUiHpoqJkBGrhdwDR1u911zls8lQwKOTHj4/XVjnk5tpQ/La6apJFW9OnBHCYbCqYbEoYwnJyQiHMnr3kjyliOxMYkl+s7h1FaSMq+B1AtPULsKGTw9q9DsYAbhx7z57pK2itTcQWJjzIrIQSBrEKYS8eDN81W3mY0bAX/P7itD0/ipIKqEunHYhONubzwUBvgF/KFAZ6AxQVWXEcPbqJ+WrTjOYkYotH+AnhDa/DD/Iq+PYQO6FKOM/hT82ctD8/ipJs1MJPMDE7LwnwsjkHMdUYk4eHFeA4OI514SQ6WiYdafiE4PDRjadz/Oc769YfckQ+x8c4P+0RbaQomYoKfoKJOffDB+V4atxJTGqqbGylq04plFQw6USeiyUXTeTYzS+QQy0Ax376qlV3x2mQBXT8eBvR6fXaFBDFxclrv6KkOir4CSZm52V5khuVhmzp4rCLGylmFl4MErLJegI4dU9QIvXJ20IhO6NiYaHeQBUlHurDTzAx534oKrLqHxV3mSLzPKckPh88lVdEFfnUUN/JEfkEFQrZUxomGLTh/Ho+FSU2OtK2o4hyNjc2cYliCQTgnfIAZ+Pn6CJfzPN2883whz/UW/oej51kXc+nkq3oSNtUIMpZ34HzPKc88Tpe7SlzAKfBsujwz+HD6wbq6pzpitIIKvgdRLSopdg8z0mjNU860R3d7kBdVq3S86kojaGC3wHEE7W2DFTKFFr1pBPjkUDPp6I0jQp+exAlSPFETUMyW/Gk08gjgZ5PRWkcFfxEU1Zm4wNDobreQ5/PUfdNHFpsmWvnh6K0GhX8RBIIwLhxUGsHC1FVBX4/zmRH3Q2N0CLLPPKRICcHPvgAAgECOHp+FaUJVPATSXl5vdiDjRF0zXl1NySI8CNBeTnMnQtlZYQemc3j8iBlplhDXBWlEXTgVaIIBKwAhcc1eL3w4IOqPO2B49jUorW1EAohwVqm147j9GCg5bn4FSWLUMFvgmaPhvX7rV8Z7PDPUaM0sUt74vOBx4PBZtTMpYYJlJKTo30kihIPFfxGCAeE3HGHfW9U9F3fsvF4qcnJZ1O/NJy2Kp1wHHjwQYx46iaBv4wlzD6jrFkPVZrWQslGEiL4InK+iPxLRLaKyO0x1o8Ukd0istF93ZiI/bY3LZq2z3FYcnMFd3A3vtoKzhjvqJi0N8XF7Ol5AlCfN/+y96c3uVmLbuSKkkG0WfBFxAs8CFwAnARcJSInxSj6lDGmr/ua3db9dgThgJDmTFASCMAVf3D4XWgya40TDtBR2kBzrPAD+p3Q8PsH/2xSwds0/66ipDGJsPAHAFuNMe8aY6qBJ4FLE1Bv0omZ+TIOfr8NvQ/j9aovuS001wrfdMFEgkidL98YA6Wljdbdkhu5omQSiRD8I4EPI77vcJdFM0JE/i4iz4jIUbEqEpFiEVknIut2796dgKa1neZO2+fz2XFWHo8ND3/gAQ3QaQvNtcJfqHRYGm1fLF3aqJXfkhu5omQSHdVpuxToZYw5BfgbMC9WIWNMmTGmvzGmf7du3TqoaS1nU1kA/7ApbCqrF5WwiPz2t7BypQbotJXmWuE+H/wxbyK1eOusfIJBG6ffCG2Zf1dR0pU258MXEQcoMcYMc79PBjDGTIlT3gt8Zow5uLF6UzUf/qayAL1HDyGPaqrJY9usCgqLVTXag+bOVxsIwOelZZz//M/whNzQ2HZ5oBIAACAASURBVLw8TbugZCWN5cNPhIX/BnCciBwjInnAj4HnoxpwRMTXS4AtCdhvUqhc5CePanIIkks1lYv8yW5SxtKUFR7u1AX4weJiPMWj6qfACga1N1ZRomhzagVjTK2IjAP+CniBucaYf4jIXcA6Y8zzwH+LyCVALfAZMLKt+00WBSN8VC/Lw1BNDXkUjPAlu0lZSXTSzOnTIZ8irsmbh7dWs9QpSix0isOmCATq/cFFReA4bCoLULnIT8EIn7pzksSUKTaCJxi0HeVer42SGugNMO+n7pSI0KRPqLluI0VJF3SKw9YSCMDZZ0NNjf0+dy74/VbkVeiTSmTSTJH6Sc1X4/BET4fJxM6bHynwoPMKK9mFCn5jlJfXiz1o/vUUIjKPfkEBjB8fNd9AjLjOAE4Dgb/uOk2tr2QXKvgtQUdTpRSRKacLC6NdM759ptKKvgeAziusZBfqw3eJ6csNBOCcc6wieDwwc6YG2KcTURc11uyIoD58JbNozIevgk+j06Rqr16GoZdTyXS007YJGp0mVaeqyij0cirZjAo+1tob6A1wVsjPGq8Pn08VQVGUzEMFH3AIUCFDEKoxkoeXCkBFPyNxfTqbCny8UOmoa0fJKlTwAfx+OzrTBKFW4/MyFrezxlRV0zuUx4ueCu7u5Gj8vZI16BSH0GhqRp0KL33Z59q5nTUSsnmQBoX8VFVBSYleXyU7UAsfGo7iiXjGbzR6R0lpYl678LzDVdWYkIdLWcK/QwXMXV7MqlV6fZXMRwU/TIzwjUajd5SUJBx2+cEHMa7dZHtjl9JScpcs4Qxe5wxehxA8Wl2s11fJeFTwGyEyX4uOxEx9Iq36nBzroYOoa+c48O23dZOeG+CHLGJBXrFeXyXjUcFvhDieHiVFiXwiAxg1Cnr2jHHtRoyAZcvqvp76nR1sGllGb6d+FLUO0FIykawQ/Lb8eXWgTvoQ/UTmZrPel3B6jOnTkS1b6PbpZrqVjobedp323SiZSkZH6QQCcNNNVgjuuMP+iTUaI3Np0eTkxcVw4IENl82ZAzR/AnVFSTcy1sIPW2l790I4XVBd5x36vJ6ptOiJrHv3mN+170bJVDJW8MNWWljsReyf96ICfV5XXCZOhBdegNpa28s7cSKgfTdK5pKxgh9ppeXkwPXXW59uYStiLbUDL0NxHFi5su7iBnDwT6m/znqtlUwjYwU/vpXma9HzunbgZTiusgcCMNkX4KwaP5NzfUzxO3qdlYwjYwUf4lhpLXxe18FX2cE75QFeqh5CHtVUV+fxQGkF/gH1ydX0KU/JBDJa8OPSgud17cDLDs7GTx7V5BDEUM1/lvqZutQhLw+mT284Z64+5SnpSkaHZSaCFoX6KWlHOMHaf/r5kE55BMULnhx6BD/g9GCA6mobrbl3r4ZpKumPTnGoZC3R/TOvTQ9Q+GY5wTlzMTU1gPB7uY07cqZRU2O3yctTt56S2jQ2xWF2WPhlZTBsmH1XFJfo/pkXKh3o2ROpqSEHg5cQE00pI2vs70YEfvpTFXslfcl8wS8rw4wejVm2DDN6tIq+UkesaRA2Ffgw2KRq4QRrNzAHrxfy821or6KkKxkv+J/PWQTU/3nD3xUlVv/MC5UOqxnUoFy3vt21D0fJCDI+SmdLfl8clhHuqQh0H8EPktoiJZWIDtjy+WBy3lT+Wn02udQQ8uZy7MyJTFahVzKAzBb8QIAzA38kBIDwB+8EBk4sbmIjJZtxHJjid3iq/BXOxs/RRT67YsqU+tG4fo3HV9KTzBb80lI8NVUAGAzXXvwfDtdBNEoTWKvfAZz6UJ6qKkLi5XF5gDJTrPH4SlqSuYIfCMDSpXVfBTj8cE2VoLQQvx+qqiAUQggxnbG8SSFvVDsanqmkHRnZaRsIgL/EjwlFjDHweqGoSHOdKy3D56ubK1EADyHOFb+OulbSkowT/LAF/6vlPvaYToTEQ63k8sqPZ4LjxAzFU5S4OA488IBNuerxIJ06ccJonz4ZKmlJQlw6InI+8EfAC8w2xkyNWt8JKAdOAyqBK40x2xOx72jCFvyakMN5VHC28ePHx6sLHGYNthMdaa5zpUUUF0NhIfj9eAsKKKr0uyvsj0f7hJR0oc2CLyJe4EHg+8AO4A0Red4Yszmi2A3A58aY74rIj4FpwJVt3XcsfD4YRRnDWcQiRjCVyXXrFi2y/13Nda60mPAPJqIDaNP0Cma+6TB3rnURap+QkuokwsIfAGw1xrwLICJPApcCkYJ/KVDifn4GeEBExLRDIp/vLCljZnA0AEPd+PvZ2FDMESMSvTclq4joADJV1fx5rJ9ZQWffKTRV8JUUJRE+/COBDyO+73CXxSxjjKkFvgQKErDvfZBnG46s/dl3FjF0KMyaZa17RWk1ER1Atd48Xg759plCU/uElFQmpcIyRaQYrDnes2fPVtVhLh8BpfUjaw8aOYK/TktQA5XsJmLynH8W+Ngw3sEbNYWmWvdKawn3BRUUQGVl+/QJJULwdwJHRXzv4S6LVWaHiOQAB2M7bxtgjCkDysCmR25NY3pPK2Yb1tI3l4+g9zQ165UE4nYAFQIVhdpZqySGiPF9hELg8UCnTonvE0qE4L8BHCcix2CF/cfA1VFlngeuAwLAD4GX28N/H6b3tGJQoVfaGYcAzgflNv4MNe+V1hPuHgrZPDCEQu3TJ9RmwTfG1IrIOOCv2LDMucaYf4jIXcA6Y8zzwBzgcRHZCnyGvSkoSvoSCFjTvrrafp8zB155RUVfaRXh7qFIC789+oQS4sM3xrwEvBS17NcRn/cCVyRiX4qSEvj91E2DBfZzebkKvtIqIrqHUt6HryjZh88HHg8mGESwE6a8/crHfBZQzVdaR0eMD8q41AqK0iE4DttunUkQT11E2LFblvL44DLKymw25UCgvnh4svTIZUp2k4zfhFr4itJKnu5SzCG8ySgexgvkEGRG7U2MvQlmS30KZdAMrUpDkpW1Vy18RWklPh88mVdEkJy6OXA9hPhTaCynBwN1URaaoVWJJBCAkhLbQRv5m+gIi18tfEVpJeHZsZaXPsj5z90EJoQAXmq5jnLeynPqoizy8uqtOR2Nm73EirfPy4MvvoCzz7Y3gEE5Aeb91J1tLcFmvwq+orQBxwEWF9vhgj/7GQSDeIBR8gjfv7kfvR07HkQztCqwb7x9//5www0wdizU1sKZBHipegj5s6phXuJ9PerSUZREUFwMo0YB1rXjNUF63z+u7vnccWDyZBX7bCdiPh0A3noL3nzT3gDOJMCdlJBHFR7TPv4/FXxFSRRFRTaxTphgUB32SgMcB376U5tsD6xVD9aNs4Jz+D7L8NJ+I69U8BUlUTgOPPgg5ObWJ0NRh70SQbhDNje3fta9oiJYdGYpnajCi5vpt3//dgndUR++orSBfWa7ipgdi4IC8PvZtAleqHT28d/rTFnZRWQoZk6O9QAWFdmcTKxZ2rDwqae2y49CBV9RWkncWOqI2bFMVTW9Q3m86Kng7k5OXZlkxWErySMyPBegZ0/3mk/xQ2QuSa/X3gnaAXXpKEoraTS+3l0poSC5VDMo5G9QRmPzs4+I+XMauud9Puv+83is6T9zZrvd/dXCV5RWEv4Dx4yvd1eaqmpCIWE4S/hSCvD5ipveVslIHAemT7dza9/UN4BT7qbWLirqsLhdace09G2if//+Zt26dcluhqI0SqN++EAASksxS5bULZKIuTbVh59dhN14p1YFWB7y0Ylq20HbqROsWJGwH4GIrDfG9I+1Ti18RWkDkRkOAwGbIRnC0x068O23dfMrA9bEcwW/I7IjKqlD2I13TaicvLDYQ/vMdBIHFXxFSQDR86E8+ij86U/QY/8RXMCyunKyZQuUldWJvpI9+HwwhUkUM6supbZAh/r0tNNWURJA9Hwo1dV2uPzFzxfzD04CqPuTs2hRzDo0hXJm4yyZxG3BUjwYBFfsBwxIqDunKdTCV5QE4PPZwTRhC9/jscPlQyH4I7dQxui6vPmMGLHP9hqmmeEEAnDffQ3dex6PdfF14IVWwVeUBOA41soP+/D79YPx421WxNmhYgS4wrOIY28bQe/CQmvKR/TWxgrTVMHPIPz+hrH2ALfd1uEXWQVfURJEdCds5IDbyspiOvuK6U1sU17DNDMcnw/y82HvXptI57bbYNq0Dm+GCr6itBMxo3Cm+DFVdkCWqapGXFM+chJrDdPMQFLkAqvgK0oHsqnAR+9QHrlUUxPKY1uBj0J3nYZpZhjRAy1S4AKr4CtKB/JCpcOLngoGhfx04QuuuLcEGKFhmplGivbCq+ArSgcQNvYKCmBDJ4fv7d3EPeaXsBUY7cbpq+hnDjF64QM4yfboqOArSnsTbexNnw4X3jUHdtbH5sucOSr4mURUL/ymAl9KGPw68EpR2ploY6+yEvJ7d29QpjK/e+yNlfQk3El7991QUcELlU5KZEdVwVeUdiZWWtwXT5pIDbmEgBpyefGkiUlupZJoAjhMYTIBnPipkTsYdekoSjsTOyLPYejcVzirxs+aXB9TipLfoae0jHC/zEUFAQor/Q2c87H6bFMgKlMFX1E6guiIPMeBKX4Hv99his9dFxXG11T6ZE2vnDwiUx3fEhqC8VQjneqd87FGTk+enPzrpIKvKEmiwU0gEIBzzqnv5PvTCoaMd+J28qVo1F/WEBb0QSG/TXUcapgTI1VHTqsPX1FSgfJym3jHGKiqomZOeaOdfDpFYnLx+WCgN8DRfEAtORhPQ+d8VJ9tytyM1cJXlBTku19tYKA3wGqcmBZiqlqQ2ULnTQGWBYfglWrI8SI3jArPelNXJgUG1u6DWviKkgoUFVnlFptA96B/raNChjB3VCCmhZiqFmQ2sGRSgG9Gj8cb3IvXBPEEg9CzZ1pcBLXwFSVJNOx0dfMrl5TA8uUQCuGpqabnu34gtpCkogWZ6WybVMZFpTfhJQTYQXNB8ZKTJo9YbRJ8ETkUeAroBWwHfmSM+TxGuSCwyf36gTHmkrbsV1HSndidro4V/FWrMFXV7Anl8avlPjasUis+JQgEOPren+ElVDeRSQj411k/5Xm/g4/Uv0ZtdencDlQYY44DKtzvsdhjjOnrvlTslawnbqer66t55by7mcHN/CpUwrV7y7RTNhUoL8djgnVib4AQXsa+WsQdd9gbeKpPT9lWwb8UmOd+ngcMb2N9ipIVNDry0nE4qm8BEyllGMt42IzmR1+UJamlCgBlZZiyR+pyHxkgiIcnB89kddBJm2iptvrwDzPG7HI/fwwcFqdcvoisA2qBqcaYJbEKiUgxUAzQs2fPNjZNUVKXWKNvG/j0Ny6ySdWw4tJ74yLcv4bSwez4ySS6L7gXcScfDwFvMIBdE6fTe7hD3pD0iZZqUvBFZDlweIxV/xP5xRhjRMTEKAdwtDFmp4gcC7wsIpuMMduiCxljyoAygP79+8erS1EygshO12if/qabR9B7mU2bLBBz4nOl/dk2qYxjF5QC9TffWnL5OdO5uIvD8NSYyKrZNCn4xpjz4q0TkU9E5AhjzC4ROQL4NE4dO933d0XED/QD9hF8RclWIn36VVXws43FzJzoWvYjdIKUZJG/YA5AA1fOWB5gfZ7D7322TDpFS7XVpfM8cB0w1X1/LrqAiBwCfGuMqRKRrsBZQGkb96soGUXYp19VBaGQjcwsXFVMRUVx2ohJRuH61zp3zYed9Ys3Fwwm54pi/EXpI/KRtFXwpwJPi8gNwPvAjwBEpD8wxhhzI3AiMEtEQthO4qnGmM1t3K+iZBRhn35EGH5kahalI4nwrx3s9RLy5kCwFpOTy8lLp/JQGl+PNgm+MaYSGBJj+TrgRvfzWqibp1lRlDhEhOGnTSdgutJoptFI/xrgGTUKevZE0sFJ3wQ60lZRUojYufOj0LzIbSKygzwnB66/3ma2gHB+ex+FkYmKitLUfxMDMSY1g2H69+9v1q1bl+xmKEpqEZVGmRUr4oqR3hdiM2UK3HFHnQGPiD2VA4IBBgXthDQzZlA3qUkqTD7eEkRkvTGmf6x1auErSjpRXo6pqrJRI1VVSHn5PioUCNhsy48+CrW1mi8/mnAH+d69Nhu1MVBUVcYDjMVDiOrqTjzwlwpeGDCZgk0wfnzmzDuggq8oacSujxsOivnPKxs4KBDYZ2q9sJhBwxGg6WSpthdht1l5OcydC6fXBnggNI5cat20CVV8tdTPlKUOHo+9abrTFKR9J7qmR1aUNOJvhxdRRR7hjC6dt6xrkMQl3N8YFvuwu6KgwBZLl5wv7Y3jwEMPwboZARYX3EAuNXWx9oiHFcZHMGjdPuFzGQrZ85jOqOArShpxXJHD+Xl+lvN9gnjwEGpgwkfn6Bk92lqzlZXZMUNWIGB99M26oQUCFI47m667twBW7MXj4f0JD7Khk4PXa8+jO0UBHo89j+mMunQUJY0IT37+TnkJ8ugqqK2OObVeLNdNps+Q1eJ5fv1+TE1Ng+yXX/1Xf3pPK6ZiuD2HBQUNffjpft5U8BUlzbBD+R0oilB2sKatz4fjOHFnyIpRPK190pHESjkd99gCAfjgA4LixWuCdYtXHX8DP6BhuoTCQuvvzwRU8BUlzagPt3RwJrtpNn0+qKmB3Ny4ShcWscYs4XQO5Wz2PL8RJ8CTk8PK2rPIN3uZl3MD106MnbNo3jxb77x56R2po4KvKGlETLEuL7cLwL7HCNWMJJ4l3GKXSIrRrEFrYM+PG8bkAXqNPp+pTI5bb4ueHFIcFXxFSSNiig80yJ0vEeVjWezxLOFMELZGM1eGByjMmVMfepOTw3/6+Zg3Pr4F3+wnhzRABV9R0ohY4rNkSRHn8yi5VFNDHlvePYh+w4axre8Ihswo3sdij2cJZ5Kw7XOjCwRg8GCora2/OYog11/PC5VOoze6Zj85pAGaWkFR0oxoMRs2DP6zLIAPPwfxBbdTWmftb+YkpnMLj3qLuftumBzfcxGz7nQkpmuq9DJYYifas3PRQhX7sW1WBV8XOmntyopGUysoSgYR7bYYMQJGL3N4FYe/MKxB2ZPYTBmjOZ5tDPRNa3Hd6UhM19RHHzUos5MeXO15mgsrHSZnkAXfFDrwSlHSnOJimDULhg6F/a4ZUefDF+r9+beG7sMhO4bXxpwg/oYbAHckLfA7uYMNnRx8vsx4qmku6tJRlEyjrAymT4ctW+qXidhhtz17ZoWyxRTxsjJYtIhtfUfwdJfiuj6KTHLnQOMuHRV8RckwwmJ37T8m0WPhfTYiJS/PJoMJp89sJK1yRjBpEjz7LFx+OUyL78qKTJXs9dKsfo5UR334ipIlRHZYlnincfclw7nycD9Hf/x6XaclVVVNxuqnNZMmQak7bXb4PY7oZ1JkUnNQH76iZBDRHZa3P+dw4rzJ7GqQVBn4+OMWZBlLIwIBmD274bJnn41bPBxyeffdmeHOaQq18BUlg4g1uUd1tU2rXJQ316Zf8HrhL3+BpUszx3EN9Y83e/Y0XH755Y1ulgmRSc1FLXxFySDCFuvo0dCpU32kynFFjjX/f/c7uPFG68tPoVzJLUprHI/w4w02Gufb/Q5lxzUTG/XhZxvaaasoGUrccMPokUnTp8Obb9p1SZiwO2E5fNyKTFU1e0J5DPVUsKGTkzEPMM1FO20VJQuJdlU0yLIZHmlUUEBo3H8jNVUAhB6ZjXfUjR0q/AnL4eM+3rxS4udXy32sCTl40zQnUHuhLh1FyQLCVnTdFIc4MHky779Ziamprhuk5QnWYmbN6tB5EGMOlGotjkOnksl1M1ZlQ+RNS1DBV5QsIJYVDfAKPmrIw1A/ClXCM3aXlNSJfkJ87HEI9zuMGgXXXZe4+qIjb9rzGNIGY0xKvk477TSjKEpiWLvWmP32M8brte9r19YvH5y71sxkjFnEcLOHTibk8YQDfIzxeMyH10yMuW1HtC9d6k8lgHUmjq6qha8oWUA8q9dxYOorDn8f8xB/G7OYd2atQM47r37DUIgjF5Ry7d6yVgf1BAJw2WVwxhk2u0Es4j2BxKuv/KYA79/UfHO9JfVnNPHuBMl+qYWvKEli7VoTFI8JuVZ+CMwyz9BWWcdr1xqTk1P/wADGzJoVu1xzLPC1a425KWeWqSLX1OIxtZ2a1yC18NXCVxQlBgEc7pPbgHq/fs/+3Xj7mGFsurmsRREvfr8N+Y9k0aJ9yzXX725un8QDtWPIpQYvIaS6qlnmuuPY6NMhQ+x7tkbtaFimoigN8Pvhf8w03qE3I1jEp3Tj2tcX2FTLpcugNzYnczPw+SAnp6HojxgRu2ysMNLI+Py3L5+Es9LmxglP8ILH26wwnEAAxrvTGK5aBYWF2Sn6auEritIAnw88HphNMRfwV77D7oYFYpnocXAcWLkShg+HAQNs3v5m3isa+N1PrQrQ/Yn76sJHDYAInpkPNEu51YdvUcFXFKUBjgMzZ9q4eBF4zhtlkscz0Rupb/FieO215os9NIzPP9fjt+GiLgLIhAktftIQse/ZGpuvLh1FUfahuNi6PezI3GLeXQLy7CLM5SPoHRbZdp4qKnLy8IsKfIT+Ox+q9iIieCbc1uIcOeH7RYpmk+kQ2iT4InIFUAKcCAwwxsRMfiMi5wN/BLzAbGPM1LbsV1GU9ifsUw8EoHBGMdXVxeTNgIrh2OkSO2CqKIcADn424WOIqeAs8bMm18eU4Q4t2Zvfb905xtj3bE230FYL//+Ay4FZ8QqIiBd4EPg+sAN4Q0SeN8ZsbuO+FUXpAGLmuiHWwgQraESv7X958qgNVnCPmYw3aOdvacnDRbZNdBKPNgm+MWYLgIg0VmwAsNUY865b9kngUkAFX1HSgNhi6SOYkwehasjJwxtnNvDWeH0CAXinPMDFG0o4pKoKQiFyTDXnevy8KjZHzqOP1s/W2JyHi0j3UBZM6RuXjvDhHwl8GPF9B3BGrIIiUgwUA/Ts2bP9W6YoSpPEEstAwGGyqeAs/KwxPmZsgsLxDV08AZwmvT7RN4RAAB4fXMb02rF4CGIwiMeDdMrjiuk+9quEDz6ARx5p+cNFNk10Eo8mBV9ElkP0/GgA/I8x5rlENsYYUwaUgc2Hn8i6FUVpPdFi6ffD6qDDK8bBG4TKRVP2cfH4cRr1+sRKy795ToA/1v6MHIIIEETwnncelJRQ6DgUutvNm6fumdbQpOAbY85rqkwT7ASOivjew12mKEqaEu3mKRjhg1WRCwq4+s0p/NXrYzXWDfPBB1asw6If2TdQVQWP/yzAvcHxdWJvY+09NmtnxJ2iOe6Zdg4gSls6wqXzBnCciByDFfofA1d3wH4VRWknokW30HGgsH5SFcaP5+jqal725PBatwt465PDeXxWEUPm1c9AFXnTcAjwt6CPPKob7Gfrf13MCRGKHSnkkyfHblvCZtDKQNoalnkZMAPoBrwoIhuNMcNEpDs2/PIHxphaERkH/BUbljnXGPOPNrdcUZSkso9PPLxgSr17R4JBzvx4CWcCxTzM/D3X4PfPr9sunP9+4quldNpYXWfZh4Bq8qgeP7Gu+uYKecJm0MpA2hqlsxhYHGP5R8APIr6/BLzUln0pipImhE33vXvBGCJj+K5lAW8vgYBvfp14D/QGOLp2aYMqdvUYwGd3TKewuF6pmyvkGoIZH02toChKYgn7e0aPbjiTlvt+3OsLWFUaqBPvs2r8EKq/MYjXy5FPNxR7aP5UiPEybyqaWkFRlPbAde94DjoIU1qKgbrEZyHg+I/8DPTCj4PlHCEfY3JyIFhrs7Y9+KAdXzulYadrS2LpNQQzNir4iqK0H9OmWZEvLa1bVEMnCn0FVLzpw0M1hEC8uTaBT1FRo/H7KuRtQ106iqIknAYTl0ybhmftWj4ePoY3B4zhnVkr6N2lEm9tTZ3VT20t9OwJjtNoKmOdiLxtqIWvKEpCCIdMulGZDQZUVVY6+CY69dZ5AMjNtYWggVM+Xqerhlu2HRV8RVHaTKQYi0AoZF9VVTB2rM1S2UCkHcfeHcrLbQVFRXXqHc9Xr+GWbUcFX1GUNhMpxh5P/eQpHo9dFgrFEOlGHPKxVmm4ZdtRwVcUpc1Ei7F14+zr3mmLSGvGy7ajgq8oSptpTIzrZ85qu0hrlE7bEJOi833179/frFsXcwItRVEUJQ4ist4Y0z/WOg3LVBSl3dFwytRAXTqKorQrGk6ZOqiFryhKu9LYQCqlY1HBVxSlXWlu0jOl/VGXjqIo7YqGU6YOKviKorQ7Gk6ZGqhLR1EUJUtQwVcURckSVPAVRVGyBBV8RVGULEEFX1EUJUtQwVcURckSUjZ5mojsBt5v5eZdgX8nsDnJIN2PId3bD3oMqUC6tx86/hiONsZ0i7UiZQW/LYjIunjZ4tKFdD+GdG8/6DGkAunefkitY1CXjqIoSpaggq8oipIlZKrglyW7AQkg3Y8h3dsPegypQLq3H1LoGDLSh68oiqLsS6Za+IqiKEoUKviKoihZQkYJvoicLyL/EpGtInJ7stvTUkRkroh8KiL/l+y2tBYROUpEVojIZhH5h4jckuw2tRQRyReR10XkLfcYfpPsNrUGEfGKyJsi8kKy29IaRGS7iGwSkY0isi7Z7WkNItJFRJ4RkX+KyBYRSWqS6Izx4YuIF3gb+D6wA3gDuMoYszmpDWsBIjIY+BooN8acnOz2tAYROQI4whizQUQOBNYDw9PsOghwgDHmaxHJBVYDtxhjXk1y01qEiPwC6A8cZIy5KNntaSkish3ob4xJ24FXIjIPWGWMmS0iecD+xpgvktWeTLLwBwBbjTHvGmOqgSeBS5PcphZhjFkJfJbsdrQFY8wuY8wG9/NXwBbgyOS2qmUYy9fu11z3lVaWkYj0AC4EZie7LdmKiBwMDAbmABhjqpMp9pBZgn8k8GHE9x2kmdBkGiLSC+gHvJbclrQc1x2yEfgU+JsxJt2OYTowEQgluyFtwADLRGS9d5iqDAAAAbZJREFUiBQnuzGt4BhgN/Co61qbLSIHJLNBmST4SgohIp2BRcB4Y8x/kt2elmKMCRpj+gI9gAEikjYuNhG5CPjUGLM+2W1pIwONMacCFwBjXZdnOpEDnAo8ZIzpB3wDJLVvMZMEfydwVMT3Hu4ypYNx/d6LgAXGmGeT3Z624D6CrwDOT3ZbWsBZwCWuD/xJ4FwRmZ/cJrUcY8xO9/1TYDHWbZtO7AB2RDwdPoO9ASSNTBL8N4DjROQYt3Pkx8DzSW5T1uF2eM4Bthhj/pDs9rQGEekmIl3cz/thAwH+mdxWNR9jzGRjTA9jTC/s/+BlY8xPktysFiEiB7id/rhukKFAWkWvGWM+Bj4UkRPcRUOApAYv5CRz54nEGFMrIuOAvwJeYK4x5h9JblaLEJGFgA/oKiI7gDuNMXOS26oWcxZwLbDJ9YED/NIY81IS29RSjgDmuZFfHuBpY0xahjamMYcBi639QA7whDHmf5PbpFZxM7DANULfBa5PZmMyJixTURRFaZxMcukoiqIojaCCryiKkiWo4CuKomQJKviKoihZggq+oihKlqCCryiKkiWo4CuKomQJ/x8XT+v5zgF9agAAAABJRU5ErkJggg==\n", + "text/plain": [ + "
" + ] + }, + "metadata": { + "tags": [], + "needs_background": "light" + } + } + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "3h7IcvuOOS4J" + }, + "source": [ + "Much better! The evaluation metrics we printed show that the model has a low loss and MAE on the test data, and the predictions line up visually with our data fairly well.\n", + "\n", + "The model isn't perfect; its predictions don't form a smooth sine curve. For instance, the line is almost straight when `x` is between 4.2 and 5.2. If we wanted to go further, we could try further increasing the capacity of the model, perhaps using some techniques to defend from overfitting.\n", + "\n", + "However, an important part of machine learning is *knowing when to stop*. This model is good enough for our use case - which is to make some LEDs blink in a pleasing pattern.\n", + "\n", + "## Generate a TensorFlow Lite Model" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "sHe-Wv47rhm8" + }, + "source": [ + "### 1. Generate Models with or without Quantization\n", + "We now have an acceptably accurate model. We'll use the [TensorFlow Lite Converter](https://www.tensorflow.org/lite/convert) to convert the model into a special, space-efficient format for use on memory-constrained devices.\n", + "\n", + "Since this model is going to be deployed on a microcontroller, we want it to be as tiny as possible! One technique for reducing the size of a model is called [quantization](https://www.tensorflow.org/lite/performance/post_training_quantization). It reduces the precision of the model's weights, and possibly the activations (output of each layer) as well, which saves memory, often without much impact on accuracy. Quantized models also run faster, since the calculations required are simpler.\n", + "\n", + "In the following cell, we'll convert the model twice: once with quantization, once without." + ] + }, + { + "cell_type": "code", + "metadata": { + "id": "1muAoUm8lSXL", + "outputId": "aad8259e-df57-4f03-da77-d490e5609d9f", + "colab": { + "base_uri": "https://localhost:8080/" + } + }, + "source": [ + "# Convert the model to the TensorFlow Lite format without quantization\n", + "converter = tf.lite.TFLiteConverter.from_saved_model(MODEL_TF)\n", + "model_no_quant_tflite = converter.convert()\n", + "\n", + "# Save the model to disk\n", + "open(MODEL_NO_QUANT_TFLITE, \"wb\").write(model_no_quant_tflite)\n", + "\n", + "# Convert the model to the TensorFlow Lite format with quantization\n", + "def representative_dataset():\n", + " for i in range(500):\n", + " yield([x_train[i].reshape(1, 1)])\n", + "# Set the optimization flag.\n", + "converter.optimizations = [tf.lite.Optimize.DEFAULT]\n", + "# Enforce integer only quantization\n", + "converter.target_spec.supported_ops = [tf.lite.OpsSet.TFLITE_BUILTINS_INT8]\n", + "converter.inference_input_type = tf.int8\n", + "converter.inference_output_type = tf.int8\n", + "# Provide a representative dataset to ensure we quantize correctly.\n", + "converter.representative_dataset = representative_dataset\n", + "model_tflite = converter.convert()\n", + "\n", + "# Save the model to disk\n", + "open(MODEL_TFLITE, \"wb\").write(model_tflite)" + ], + "execution_count": null, + "outputs": [ + { + "output_type": "execute_result", + "data": { + "text/plain": [ + "2488" + ] + }, + "metadata": { + "tags": [] + }, + "execution_count": 17 + } + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "L_vE-ZDkHVxe" + }, + "source": [ + "### 2. Compare Model Performance\n", + "\n", + "To prove these models are accurate even after conversion and quantization, we'll compare their predictions and loss on our test dataset.\n", + "\n", + "**Helper functions**\n", + "\n", + "We define the `predict` (for predictions) and `evaluate` (for loss) functions for TFLite models. *Note: These are already included in a TF model, but not in a TFLite model.*" + ] + }, + { + "cell_type": "code", + "metadata": { + "id": "NKtmxEhko1S1", + "cellView": "code" + }, + "source": [ + "def predict_tflite(tflite_model, x_test):\n", + " # Prepare the test data\n", + " x_test_ = x_test.copy()\n", + " x_test_ = x_test_.reshape((x_test.size, 1))\n", + " x_test_ = x_test_.astype(np.float32)\n", + "\n", + " # Initialize the TFLite interpreter\n", + " interpreter = tf.lite.Interpreter(model_content=tflite_model)\n", + " interpreter.allocate_tensors()\n", + "\n", + " input_details = interpreter.get_input_details()[0]\n", + " output_details = interpreter.get_output_details()[0]\n", + "\n", + " # If required, quantize the input layer (from float to integer)\n", + " input_scale, input_zero_point = input_details[\"quantization\"]\n", + " if (input_scale, input_zero_point) != (0.0, 0):\n", + " x_test_ = x_test_ / input_scale + input_zero_point\n", + " x_test_ = x_test_.astype(input_details[\"dtype\"])\n", + " \n", + " # Invoke the interpreter\n", + " y_pred = np.empty(x_test_.size, dtype=output_details[\"dtype\"])\n", + " for i in range(len(x_test_)):\n", + " interpreter.set_tensor(input_details[\"index\"], [x_test_[i]])\n", + " interpreter.invoke()\n", + " y_pred[i] = interpreter.get_tensor(output_details[\"index\"])[0]\n", + " \n", + " # If required, dequantized the output layer (from integer to float)\n", + " output_scale, output_zero_point = output_details[\"quantization\"]\n", + " if (output_scale, output_zero_point) != (0.0, 0):\n", + " y_pred = y_pred.astype(np.float32)\n", + " y_pred = (y_pred - output_zero_point) * output_scale\n", + "\n", + " return y_pred\n", + "\n", + "def evaluate_tflite(tflite_model, x_test, y_true):\n", + " global model\n", + " y_pred = predict_tflite(tflite_model, x_test)\n", + " loss_function = tf.keras.losses.get(model.loss)\n", + " loss = loss_function(y_true, y_pred).numpy()\n", + " return loss" + ], + "execution_count": null, + "outputs": [] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "pLZLY0D4gl6U" + }, + "source": [ + "**1. Predictions**" + ] + }, + { + "cell_type": "code", + "metadata": { + "id": "0RS3zni1gkrt" + }, + "source": [ + "# Calculate predictions\n", + "y_test_pred_tf = model.predict(x_test)\n", + "y_test_pred_no_quant_tflite = predict_tflite(model_no_quant_tflite, x_test)\n", + "y_test_pred_tflite = predict_tflite(model_tflite, x_test)" + ], + "execution_count": null, + "outputs": [] + }, + { + "cell_type": "code", + "metadata": { + "id": "-J7IKlXiYVPz", + "outputId": "24017e5e-7672-460c-8b76-c0ed71f3ec27", + "colab": { + "base_uri": "https://localhost:8080/", + "height": 281 + } + }, + "source": [ + "# Compare predictions\n", + "plt.clf()\n", + "plt.title('Comparison of various models against actual values')\n", + "plt.plot(x_test, y_test, 'bo', label='Actual values')\n", + "plt.plot(x_test, y_test_pred_tf, 'ro', label='TF predictions')\n", + "plt.plot(x_test, y_test_pred_no_quant_tflite, 'bx', label='TFLite predictions')\n", + "plt.plot(x_test, y_test_pred_tflite, 'gx', label='TFLite quantized predictions')\n", + "plt.legend()\n", + "plt.show()" + ], + "execution_count": null, + "outputs": [ + { + "output_type": "display_data", + "data": { + "image/png": "iVBORw0KGgoAAAANSUhEUgAAAXwAAAEICAYAAABcVE8dAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAALEgAACxIB0t1+/AAAADh0RVh0U29mdHdhcmUAbWF0cGxvdGxpYiB2ZXJzaW9uMy4yLjIsIGh0dHA6Ly9tYXRwbG90bGliLm9yZy+WH4yJAAAgAElEQVR4nOydeXxMV/vAv2cmC1GERO3JpC1FIgkSoqQyGqWo0jaoULQob1X7tnZVqtZYWlpVulgqlkRraX9tbRkqNG1CRRWvIhP7vhRBljm/P+5kTJKZLIRs9/v53M/Mvffcc88999znnvs8z3mOkFKioqKiolL60RR1AVRUVFRUHg6qwFdRUVEpI6gCX0VFRaWMoAp8FRUVlTKCKvBVVFRUygiqwFdRUVEpI6gCv4gRQoQLITYVdTkyEUKUF0L8IIS4JoSIfgjn+1sIEfKgz/MwEELohBBSCOGQj7T9hBCxD6Nc+UEI4SGEuCGE0BZ1WR4GQogQIcTJB5Bvsbqv2Sk1Al8I0UsIkWButGeEED8LIVoXdbnyQkoZKaV8tqjLYcXLQHXATUoZ9qBPJqX0llJue9DnUckdKeVxKeUjUsqM+8lHCLFNCDGgsMpllW++X6Yq9ikVAl8I8S7wCTAVRVh5AJ8DLxRlufKimDZeT+CwlDL9QZ6kmF67ikrpRkpZohegMnADCMsljTPKC+G0efkEcDbvCwFOAiOB88AZoCvQETgMXAbGWuU1EVgDrAauA3sAP6v9o4Gj5n0HgG5W+/oBO4GPgUvAZPO2WPN+Yd53HvgX+AvwsbrOZcAFIBl4H9BY5RsLzAKuAEnAc7nUR0NgG3AV+BvoYt7+IZAKpJnr9PVsx9UCbgFVrbY1AS4CjsDjQIz52i4CkYCrVVojMArYB9wBHMzbQvNxnyz1ZJWfBJ4w/+9oru/rwClguJ1rt74HV4FjwFPm7SfMdd83W/uyV+9ac51fNOfzprlMDlbHfo3Spk6Z77c2+/Xkdt9tlL8/cNB8nceAN7LtH2k+32lgQLY66gT8aT7HCWCi1XG6bGXfBnxkrqvrwCbA3byvHLDcfJ+vAvEoHa0pQAZwG6X9fGbnGqKBs8A14FfA22pfeWC2ua6vobTr8sBxc/lumJeWKM/i8lyuwW5dYX7u7ZRvATAr27b1wLv5fMZjbZXHql4HWK2/Zi7jFWAj4FnQNlEgefkghPDDXIAOQLp1pdpIMwmIAx4FqgG7gI+sbnw68AGK0BqI8nCvACoC3ihCzsucfiKKQHzZnH44ioB1NO8PQxGMGqAHcBOoadUY0oG3UIRd+WwNpD2wG3A13/CGVscuMze6iuaGdBizQDbnkWYuuxYYgvLACxt14QgcAcYCTkBbc8N90ur6ludSlzHAQKv1mcAX5v9PAO1QBHc1lIf5E6u0RmAvUBcob7UtNB/3yVJPVvlZC7MzQLD5fxWgqZ3yZ96D/ua6mowiTOaby/2suT4eyUe9DwYOma+nKmAgq8BZCywEKpiv6Q/MQie/991G+TuhvFgF0AZIybxWlGfhLEqbdUERytZ1FAI0RmmbvsA5oKst4YQimI4C9VHa6TZgunnfG8AP5nNogWZAJVsCzc41vGauz8wX/F6rffPNedQ25/2UOV2W8tlqqzauIbe6CsG+wH8a5YUorNrTLaBWPp/xfAl8FA3EEfP9dkDpTOwqaJsokLx8WIL5QS1AOHA2jzRHgY5W6+0Bo9WNv8XdnldF801qYZV+t9WDMRGIs9qnwUrY2Dj3XuAFq8ZwPNt+6wbSFkWgBGHuRZq3a1F63o2str0BbLPK44jVPhfzNdSwUZ5gFKFgnf9KzL098hb4A4AY839hfjCetpO2K/Cn1boReC1bGiN3BX5u98lST1b7rYXZcXOdVMqjLfQD/rFab2zOp7rVtkuAfz7qPQYYbLXvWXNeDig93juYX2zm/a8Ahvze93y2/3XA2+b/3wDTrPY9YV1HNo79BPjY/F9HToH/vlXa/wC/mP+/hvIy9rWR5zbyEPjZ0ruaz1sZ5Vm6hdUXs1W6LOWz1VZtpcmlrkKwL/CFuT09bV4fiLnN20mf/RnPr8D/GauvaPP1p6CoVe+5TeS2lAYd/iXAPQ+dcC2UT8RMks3bLHnIu8aqW+bfc1b7bwGPWK2fyPwjpTShqIRqAQghXhVC7BVCXBVCXAV8AHdbx2ZHShkDfIbSyzkvhFgkhKhkPt7RxjXUtlo/a5VPivmvdZkzqQWcMJfbXl658R3QUghRE6UnZAJ2AAghqgshVgkhTgkh/kXpYbpnO97u9ZP3fcqNl1DUOslCiO1CiJa5pM1+b5FS2rrfedV7LbJej3U6T/OxZ6zawkKUnn4WcrnvORBCPCeEiBNCXDbn2ZG7dZy9PCeyHdtCCGEQQlwQQlxD+ULJfn+sOWv1P4W77elbFPXDKiHEaSFEhBDCMZd8rMugFUJMF0IcNbcRo3mXu3kph/Liv2/yqCu7SEX6rkJ5QQP0QlFPZuab1zOeXzyBuVb5XEZ52dQuSJsoCKVB4P+G0pPqmkua0yiVm4mHedu9UjfzjxBCA9QBTgshPIEvgaEoXi6uwH6Um5iJzC1jKeU8KWUzoBHK5/QIFB1xmo1rOHUPZT8N1DWXu8B5SSmvoOhze6A8CKvMDwgoRnMJNJZSVgJ6k/XaIffrz+0+3UT5cgFACFEjW7nipZQvoAjUdUBUfq4nD/Kq9zNYtQXzvkxOoLRLdymlq3mpJKX0tnUiO/c9C0IIZ5QX7iyULxJX4Cfu1vEZlLaYSd2sObAC2ADUlVJWBr4g5/3JEyllmpTyQyllIxSVS2fg1czdeRzeC0WVEYrSq9eZtwuU+r6NoobJcVob27K0CcDSJvJRV3mxEnjZ/Ey3MOdFPp9x6/Jhr4wobeQNq/bhKqUsL6XcBflrEwWlxAt8KeU1FP37fCFEVyGEixDC0fx2jzAnWwm8L4SoJoRwN6dffh+nbSaEeNH8VfEOyoMdh6KrlSg2AIQQ/VHe/vlCCBFo7oU5ojSW24DJ/PURBUwRQlQ0N7p37/EafkfprY0011MI8DxKjya/rEB5wF82/8+kIopB7ZoQojYFb6C53adEwFsI4S+EKIfyOQ+AEMLJPJ6hspQyDcXIZeI+yUe9RwHDhBB1hBBVUIx5mceeQXkxzhZCVBJCaIQQjwsh2mQ/j737bqNITij67AtAuhDiORQ1UiZRQH8hREMhhAswPtvxFYHLUsrbQojmKMK3wAgh9EKIxmaf/X9RXoqZ5T0HPJbL4RVRnpdLKIJwauYO81fnN8AcIUQt89dAS7PwvmA+h3Xee4GnzWMIKgNjrPblVVe5IqX8E+UF9BWwUUp51bwr38+4lPICSuegt/laXiPry+wLYIwQwtucV2UhRJj5f37bRIEo8QIfQEo5G+VBfB/lRpxAeQOvMyeZDCSgeIf8heJZM/k+TrkepYd7BegDvGju9RxA8TD4DaXhN0bxcsgvlVB6D1dQ1AOXUIyioBh6b6J4G8SiCNpvClpwKWUqioB/DqVBfw68KqU8VIBsNgD1UGwniVbbPwSaonhX/B/wfQGLZ/c+SSkPoxh1twD/oNSBNX0Ao1lNMBjFtlMY5FbvX6KoNhLNZc1+va+iCJ4DKPd0DVDTxjlyu+8WpJTXgWEogv0KisDeYLX/Z2AeivH4CEonBBQBC4oefpIQ4jrKy/Rev4JqmK/lXxQPk+0oah6AuSg94ytCiHk2jl1mvsZTKPUSl23/cJR7H4+i4piBosNOQfEC2mlWgQRJKTejeMvtQ7Gz/ZiZSV51lU9WoHyJWDo19/CMD0Tp+FxCMabvssprrfn6Vpnb7X6U5xLy2SYKSqYVWiWfCCEmohjBehd1WVRUckMI0RBFiDjLBzyuQqVkUCp6+CoqKgpCiG5CCGezimkG8IMq7FUyUQW+ikrp4g2UwTpHUQZBDSna4qgUJ1SVjoqKikoZQe3hq6ioqJQRim0AK3d3d6nT6Yq6GCoqKiolit27d1+UUlazta/YCnydTkdCQkJRF0NFRUWlRCGESLa3T1XpqKioqJQRVIGvoqKiUkZQBb6KiopKGaHY6vBVVB40aWlpnDx5ktu3bxd1UVRUCky5cuWoU6cOjo75ClQKqAJfpQxz8uRJKlasiE6nQ4gCB41UUSkypJRcunSJkydP4uXlle/jVJVOKSQyEnQ60GiU38jIvI4om9y+fRs3NzdV2KuUOIQQuLm5FfjrVO3hlzIiI2HQIEgxT4GSnKysA4QXVvzIUoQq7FVKKvfSdtUefilj3Li7wj6TlBRlu4qKStlGFfiljOPHC7ZdpehZt24dQggOHcp7SoJPPvmElOxv9AKwZMkShg4des/HF3Y+Kg8XVeCXMjw8CrZd1ffnnwdVVytXrqR169asXLkyz7T3K/BVyjaqwC9lTJkCLi5Zt7m4KNuzk6nvT04GKe/q+3MTZGX1BXEvdZUfbty4QWxsLF9//TWrVt2dZTIjI4Phw4fj4+ODr68vn376KfPmzeP06dPo9Xr0ej0Ajzxyd576NWvW0K9fPwB++OEHWrRoQZMmTQgNDeXcuXPYw2QyodPpuHr1qmVbvXr1OHfuXL7y6devH2vWrLGsW5dp5syZBAYG4uvry4QJEwC4efMmnTp1ws/PDx8fH1avXl3AWlO5V1SBX8oID4dFi8DTE4RQfhctsm2wtafv793btjB/UEKvJPCgbCPr16+nQ4cO1K9fHzc3N3bv3g3AokWLMBqN7N27l3379hEeHs6wYcOoVasWBoMBg8GQa76tW7cmLi6OP//8k549exIREWE3rUaj4YUXXmDt2rUA/P7773h6elK9evUC5ZOdTZs28c8///DHH3+wd+9edu/eza+//sovv/xCrVq1SExMZP/+/XTo0CHfearcH6qXTikkPNy2gI/YGcHRy0fp6dOT+NPxJLf7Dt1pV0I1mzlVSdL2mGCWVxPOieokr/iJ1ycb+PFKPCuHjgTyFnrjxim2Ag8P5YuiNHkFPSjbyMqVK3n77bcB6NmzJytXrqRZs2Zs2bKFwYMH4+CgPKJVq1YtUL4nT56kR48enDlzhtTU1Dx9tXv06MGkSZPo378/q1atokePHveUjzWbNm1i06ZNNGnSBFC+Zv755x+Cg4N57733GDVqFJ07dyY4OLhA16Zy76g9/DJCZCTMeS+QRbtW88xXXVkZ6YB49E+MgZtY7Cu4ctWPEe0l5+rvoeXVk6zQ1cD0fDfWzg/EkGQgYmeEXeGW2dMvzT3/gtpG8sPly5eJiYlhwIAB6HQ6Zs6cSVRUFAWZlMjaNc/aJ/utt95i6NCh/PXXXyxcuDBPf+2WLVty5MgRLly4wLp163jxxRfznY+DgwMmkwlQ1EOpqamAMjhozJgx7N27l71793LkyBFef/116tevz549e2jcuDHvv/8+kyZNyvf1qtwfqsAvAdyv3jxTFXPudz21Vn1KeVMKB6uMQGoUYZHhaCKu2d9K4nQHEv3/YmCPi5SPXkxt1yie+fp5AmsF2hVuWm3pdwUtiG0kv6xZs4Y+ffqQnJyM0WjkxIkTeHl5sWPHDtq1a8fChQtJT1emo718+TIAFStW5Pr165Y8qlevzsGDBzGZTBaVDMC1a9eoXbs2AEuXLs2zLEIIunXrxrvvvkvDhg1xc3PLdz46nc6iitqwYQNpaWkAtG/fnm+++YYbN24AcOrUKc6fP8/p06dxcXGhd+/ejBgxgj179uS/0lTuC1WlU8y5l4FUHSM7knQliaYMZNUfW3BJv0HbkKMk6q5w5morMk4+jXwiBkiF5NbgGQvadLjzCDjcIUULpDpRx38ux/y243CzMh9NBvNzq6AzQO14XP4cmUPYZ1KaXEEz67ow1VYrV65k1KhRWba99NJLrFy5kk8//ZTDhw/j6+uLo6MjAwcOZOjQoQwaNIgOHTpYdPnTp0+nc+fOVKtWjYCAAItwnThxImFhYVSpUoW2bduSlJSUZ3l69OhBYGAgS5YssWzLTz4DBw7khRdewM/Pjw4dOlChQgUAnn32WQ4ePEjLli0BxZi7fPlyjhw5wogRI9BoNDg6OrJgwYJ7rUKVAlIoc9oKIb4BOgPnpZQ+NvYLYC7QEUgB+kkpc32tBwQESHUCFKVHn2xjOgNPTzAac26P2BnBZ8uNnKiuPESNDnlxoMHdh9TxeBPSPP5UVkyOoEkz/9eCJuPudqTyEpDgHN+Pct6RLIiuylfG5cTotBDWnUobo/h8pJ5x4wpWxuLCwYMHadiwYVEXQ0XlnrHVhoUQu6WUAbbSF5ZKZwmQm6n9OaCeeRkEqK/0fJKbsTBiZwSGpKzeGju2OXC66jcQPxggi7BHirvCPn4IZCi3X6Q54ry7z910mjSLsCfDiSYHnuCD6EYMCbtMbLeFOPdqR5PoUWiO6wkPt63uEAI6dryfK1dRUSlsCkXgSyl/BS7nkuQFYJlUiANchRA1C+PcpR17zhkeHhBYK5Dua7pbhL4hycCOtEk4x4ylnPdyuFYXBMpypxJoJAjQJPZCe602nPdDxA+i4z53vFy3wcZZaM8/fvckUgNI4npGMIapXEt3J9VvDU8fdOeEsS/RV5/hhVf68k58R/r2VYS85VAJS5eWLsOtikpJ52EZbWsDJ6zWT5q3ZUEIMUgIkSCESLhw4cJDKlrxJTISrOxzFhwdlV51fLSeMddG0X1RKB+0FcrvGm96x1Un/UwzcD2h9NIl4PwvmDQgwdRoPYGnYPZXT1Hppxns+PEAT383kfpny5PhpnxSOCS3AGECqUVo0kh9djxUOgMSNje4zhhdOz4NSmPDk99S/2B1Vq5UhLw1pc1wq6JS0ilWXjpSykVSygApZUC1ajYnXS9TjBsHZg83aBWhGEqBSpUUY6HDv7MYv+1fnvu9Lh+1ged+r8u0v9dxtuMk0h/fblbJWE2OIEy4HmoFjjeJ6zUVqTOwulw/euov8PikvhhDfgZtGi4bP2TT4vLM3gg43Ebeqgq19qBN7AkbZ4PTLd57dR8b2u/gqY1dObwpghbeetyCxijlzERnILlO/gfqqKioPFgelsA/BdS1Wq9j3qaSC1n096cCIaw76Axcvqyob6ZljKJvxgqWB9wgeHsblgfcICyoJT82PQdX68LGWXDWHzIc4NDzcKEBNbSn6LxJT4VrT7J1QC/a31rHwph6jBwJz4Rm4H9+Nu0PPIUXSfjHheAc3w9R8TT19rbAVG8zWvf9cONRRT0kBYln+xOmG8LmJ29wqf10nsg4ppRXZ4Cw7lRPDyySulNRUcnJw3LL3AAMFUKsAloA16SUZx7SuUssHh4oPeSG38H+HjSJHkVyWCgd/zHRfjHUuF6eZcGn6R3dg2+Ni+mT1J/FvZbBlmnUjevOO8ylSVwFuuqiaFh7KcbVMTj71eSHvbbP91P4TxAOfA5gZNo0A+XudGPqmpZM+3sds3TtGBH+LTikKwZfjYmbvcJY4JAKQtIlvha7ghfyfvmFTH3KEdPNGsx+S/8Qa0xFRSU3CqWHL4RYCfwGPCmEOCmEeF0IMVgIMdic5CfgGHAE+BL4T2Gct6ST14CqKVPA+VIgVNsP7d/jfzVSaPZPNZb7Q5oGTl4O4dXojvxsjGA8k/jZGEHoircZ4LKUXdoQ3mEuXiQRYKzI7zvXcY6aWMXHyvP8jz8dz9q+a0l/dSdRYxMx1KyKySGd6id0kPYImBzA8Y7S2wf20ZghCTC5DZgc0qh71J9T6necXS5duoS/vz/+/v7UqFGD2rVrW9aFEJb//v7+GB+wf6t1uOMvvviCZcuW2U1rNBpZsWKFZT0hIYFhw4Y90PKpFBJSymK5NGvWTJZmli+X0sVFSsXUqSwuLsr2TJ6bPEO2bLNMdtb9VzLGRTKBu8vYCvIp3STpznkZQ4iUIGMIUdbHbpZCZM07cxEi/+fPjqbPc7JhUD/pznnZTt9CMhFlGVlFMtZZKdcHQvl930FW0n2vlM3TU84eN1M+N3nGg63UAnLgwIGCHbB8uZSenkolenrmXlkFZMKECXLmzJmW9QoVKhRKvunp6flKt3jxYvnmm2/mK63BYJCdOnW6n2KpFBK22jCQIO3I1WJltC1L5BWILGJnBLrbx4hr/l9i0FP/kPddF8tzjam/YiaJYdMYo2uH3jMJhEDvmUTU2ETiK4fmGfvlXqI/yuU/cTBuMQ1089nc8i+Q4JguKCduotn7qpJII+FmNUhzIbVnT9BtY45wZXjqdEJvleDmVsxChW7bto2nn36aTp068eSTTzJ48GBLPJtHHnmE9957Dz8/P3777TeWL19O8+bN8ff354033iAjQxlgt3jxYurXr0/z5s3ZuXOnJe+JEycya9YsAI4cOUJoaCh+fn40bdqUo0ePMnr0aHbs2IG/vz8ff/wx27Zto3PnzoASAqJr1674+voSFBTEvn37LHm+9tprhISE8NhjjzFv3jxADZX8sCnBT2DJJq/oi0d/DWRF6mIG73AlpWdvDjdOMHvdCKi+n5M1LjApuj7p3snKcFaTCYxG9FNCGTnS9mAoUMIjREbeW/RHDw9AZyC25ycg0um30ZtflkucRSqmZl8piW5VhgoXcDE247Ysx2tPezE87CSzouvw7vLP8lk7xZCHPHfkrVu3LOqcbt262Uzzxx9/8Omnn3LgwAGOHj3K999/DyhCtEWLFiQmJuLm5sbq1avZuXMne/fuRavVEhkZyZkzZ5gwYQI7d+4kNjaWAwcO2DxHeHg4b775JomJiezatYuaNWsyffp0goOD2bt3L//973+zpJ8wYQJNmjRh3759TJ06lVdffdWy79ChQ2zcuJE//viDDz/8kLS0NDVU8kNGFfhFRF498J43MpBRq1gWYgSnf5We8+lmVPh2LaSWJ6X9BMbXeJFAv2ib+WTGxTfHwLJw6ZLSMc1tQJc9pkwBR1087O8Bkb+wJG4/ocmS8skBoJFUOVEf9xn/4B7/IikNDDhdrovxsSRaJ/jwrjGxZAfXechzR5YvX94SZdI6KJo1zZs357HHHkOr1fLKK68QGxsLgFar5aWXXgJg69at7N69m8DAQPz9/dm6dSvHjh3j999/JyQkhGrVquHk5GQJh2zN9evXOXXqlOWFU65cOVxs9SKsiI2NpU8fZdR227ZtuXTpEv/++y8AnTp1wtnZGXd3dx599FHOnTtH48aN2bx5M6NGjWLHjh1Urlz53ipMJV+oAr+IyCv6oj5yAOuM87h12Vu5S5e9wDUZiYYuKwfA4U5UeGw9i46F2jW8hoeD1eRDFjI7qvmJ/mht2B03DgY0HInnXwvBqEerVbQbZ6tep2b882i/iSWK7lz46XuqHGpFao2DNN7+ArEB+5mj86Nj2/a8FT6Lkw46TELDSQcdb4XPouOUEuCr/yDiI98n1qGRrdfLlSuHVqsFFBtd3759LS+P//3vf0ycOPFhFxUAZ2dny3+tVkt6eroaKvkhowr8IiLPmamOHyc66ASmmn9BYh8cnK8wZEdlboX1ZwuhdF45gKvRu9mwwbZaOVNQ2wpqBnD5ct4zY9lSWy9dqsTIcXEBsyoY5h/izE8b8CURL5KYrfPjqschhnzbjd6G+syKrsPwsJPcLv8o82tPJ6quKxokUXVdmV97Oo8fKAHN8EHER75P/vjjD5KSkjCZTKxevZrWrVvnSPPMM8+wZs0azp8/Dyg69uTkZFq0aMH27du5dOkSaWlpREfn/FKsWLEiderUYd26dQDcuXOHlJSUHCGarQkODibS3OvYtm0b7u7uVKpUye41qKGSHy5qeOQiIGJnBN8d+I4ePj242XULz5+rxjt/LGfO/5mI/BkcM15l24AnSal2DO3GafSKq8l6XTdWhPVg0A4PomvvYOOpCCpVUlQ01qSkwNtvw61bOVXO1nh42J4ZKzLybghgjcZKqFvlv2hRzu0AMYTihRFqR9AkWsPYE59RR3wP0gOcRvPjla0W4b8uoQ2xAfuZFV2H7ic+g8jh91SXD40HER/5PgkMDGTo0KEcOXIEvV5vU9ffqFEjJk+ezLPPPovJZMLR0ZH58+cTFBTExIkTadmyJa6urvj7+9s8x7fffssbb7zBBx98gKOjI9HR0fj6+qLVavHz86Nfv36WGa3grnHW19cXFxeXPGPx//XXX2qo5IeJPfedol5Ks1tmzLEYWWFKBSkmCuk9JEAyQUjN+0KKCUjvHl6SCUJWf6XZXbdGs8tlJd338vFWAywegfZcL/Na7Llf2nLVvNfF1jkyUAocrG8jmaj8SlC2FwEFdsssRqiukSpSqm6ZJQK9lx7vfT/gkO7E348mgEli0ko0V+rwdwMjXTa25p2VoQQYK+JFEiaEZQDV0Z1fkpys9OILOM0poKhu+vZVOqrZ9f62HFFsYVYPF/gcp7UezNH5ERuwn+DtbSy6/dPaotODq6iUKey9CYp6Ka09/BmxM+SgqTFySK21Ev37dwcvjXOSTEQ69w+UEqQJ8uxFa7VSOjnl7Fm7udlOn/llYG/AVX6+GFxcpBwyxP5+Ieyfo0e7mVKMcJOzdX5Sguys+68UI9zkjPpPWQYzze2yWbq6PpCxTTkoyT18FRUp1R5+sSewViCr73Rjqcc+HFrOuBu+2CEVzeW63PFI4IWgYKRGa9OP3pqMDKhYMafhde5c+/bF3NzJ83I4ycz/889zuntm4uFh/xzrTSbePDWa7ieuYkIwIPkY5aMX81216iAlhmQvJm3wo+nVLciiH9ukolL6sPcmKOql1PbwZ0g5JMT7boiE9x0tv9Y6fH3nVy0j+XPrcQs76m97UQByC7mQWy/f0zNn/gX9UshRVk9PGUOIdGw1SbbTjbaEiUhDK5voZkpazbB57sJC7eGrlHTUHn4xJ/DaFpamvYjbdSclTMI1T9g4E5b/TIPDOjy0Sfhs7MO2yhcID1cG0Xp62s/PXq8881jzAFyLM0lu7uTh4TB4cNaZq8C292F2t1I3NyhfHvr0UfT2+SlrRJ1k0G0j5FQKm8O+ooluDui20bXzo+wNm06TU0pGJXm8lopKscLem6Col9Law5eennK2zk8ywl3R4Y9wly66DXIIn0lBhuxGtKVHnMny5VI6OubsMTs5FVzHnZ+gadcYUm8AACAASURBVAWNEZYf7x5bXjsxLarLyiMcZSXd97KdbrRkVCWpGessGecshwQ9LtPQyrYoOv2YYzFyRmzhBl9Te/gqJZ2C9vCLXLDbW0qTwLcWoFt0yMojHKXQbZYgZR9dP4vQ68zaLEIyex7Wxlg3t3s3aBZ20Ed7aietNvdzxIzdLCvpvpeVRzjK8XoUYT8R6fTfqrLCWORsnZ9047xsopspxbgK8vFpze+voNkoaoF/8eJF6efnJ/38/GT16tVlrVq1LOuA5b+fn59MSkqy64r5+uuvy7///ltKKeWUKVMe9mXkIDPS56lTp+RLL72Ua9qPP/5Y3rx507L+3HPPyStXrjzQ8pUmVIFfzMje+x3VylV21v1XVuaKHM+H0p3zcrbOT/Zp1SiLsHRzK+qS55986+yzMWOGIvTfbysUT6Wx5aWXvtfdUNBjXWQfvaesMNZs72g5u1C9dgoi8GfMkDImJuu2mBhle2GQn/DI+fG9L6ywytlJS0vLd9qClMHT01NeuHDhXoqkIlUdfrFjgKEjKb5zLOvxO6OJqeHKk70eZxITiKI7U42bObVzriWNo6PiaVNSuNcwMyNHAgO0TG/hAqnlcRJ3GHprBe4rv4FUF3BM4ds2ydx0BDbOwvu30CKbFD0wELp3B4NBWTcYlPXAYjCDY0hICAkJCYwePdoSZTPcbLSxFxrZGp1Ox8iRI2ncuDHNmzfnyJEjAPTr14/BgwfTokULRo4cydGjR+nQoQPNmjUjODiYQ4cOAZCUlETLli0t8XAyMRqN+Pj4AJCRkcHw4cPx8fHB19eXTz/9lHnz5nH69Gn0ej16vd5SlosXLwIwZ84cfHx88PHx4ZNPPrHk2bBhQwYOHIi3tzfPPvsst27dAmDevHk0atQIX19fevbs+SCquuRj701Q1EtJ7eFnV5cQNFsyQchxQVVkBkLqg55TvHCCnpMZCJmEp2zL5iw94wfpe/4guJfJVKRU9PLuEe6SzoNkE91MOTNIGW3sHDRZiqAIZTKViUg+0Mpyuv+Tm9Hn+dVQEAqq0omJkdLdXcrx45Xf7D3++yF7D1+j0VjUOV27dpVS2u/ht2nTRsbHx0sps/auDxw4IDt37ixTU1OllFIOGTJELl26NMfxnp6ecvLkyVJKKZcuXWo5R9++fWWnTp0sk6i0bdtWHj58WEopZVxcnNTr9VJKKZ9//nlLvp999pmlDElJSdLb21tKKeXnn38uX3rpJcuXwqVLlyzntu7hZ64nJCRIHx8feePGDXn9+nXZqFEjuWfPHpmUlCS1Wq38888/pZRShoWFyW+//VZKKWXNmjXl7du3pZSyzKiFCtrDV2PpFCKZwcYyfdCTk+Hx5Oc5xS2mtn+fXxtCrMfPlNs4meNx3dFSL0cenp5FGp7lnrjXMDPxp+OJejmK/vP1/JkMVYz+lGcXac+MR2g0SCHhbGOo/hf0ep7EFY2pnG2KxocZ2kavhyFD4KOPYPx4Zf1BkRke+X6wDo0MSoz9Rx991GbaV155xfJrHeM+LCwMrVbLjRs32LVrF2FhYZZ9d+7cAWDnzp189913APTp04dRo0blyH/Lli0MHjwYBwdF5FTNY5h4bGws3bp1o0KFCgC8+OKL7Nixgy5duuDl5WWJ/dOsWTPL9I++vr6Eh4fTtWtXunbtmnvllFFUlU4hYmvA0UxG4BT3JprjLdnhCZrjLXGKe5NZjMhxfBEHX7wv7LmB5sbIViPRe+ktgShjCCUo7ik8r0lM2gw43RS+2AcbZ3Hb0cSotg74thuITqe4gvbpkzWSZ58+8J8HOFuywQALFijCfsGCu+qd4oqU+Q+NbB1q2fp/psA1mUy4urpa8tq7dy8HDx60ecyDxlaYZYD/+7//480332TPnj0EBgZatqvcRRX4hYgtf/GurCc8KIAMjzhIDibDI47woAC6sp7ly3MPT1xWsPbpj/HScsxVA6eaQq0/cQiahmPcWxA/GFlrL0MSfrCEfJYyaz5SwhdfPJiRuZk6+6gomDRJ+bXW6RcXHB0dSUtLA+yHRrZF5tSCq1evpmXLljn2V6pUCS8vL0sYZSkliYmJALRq1YpVq1YBWEIjZ6ddu3YsXLjQIoQvX74MYDfUcnBwMOvWrSMlJYWbN2+ydu1agoOD7V63yWTixIkT6PV6ZsyYwbVr17hx44bd9GUVVeAXIraMlHOC4Iv2R3HcOAUW/4rjxil80f4os4OKVaTdIic8HBYbDLgP6U6F5Wuo8OV2hmx8jPT2Y0nrOgC811B/+Vy6J53PNR8plcBthS304+MVIZ+pxtHrlfX4+MI9T15s3bqVOnXqWJbffvsty/5BgwZZVBvWoZF9fX1p164dZ86csZnvlStX8PX1Ze7cuXz88cc200RGRvL111/j5+eHt7c369evB2Du3LnMnz+fxo0bc+rUKZvHDhgwAA8PD3x9ffHz82PFihWW8nbo0MFitM2kadOm9OvXj+bNm9OiRQsGDBiQJQxzdjIyMujduzeNGzemSZMmDBs2DFdXV7vpyyz2lPtFvZREo60t42XTXo/KckGTZSWzG2YlrshyQZNl016PFtjIWdqZETtDxhyLka6uUn7PCzKGEOnUNUzxzdePkIP4QibhmesAr4LUZ1H74RcXVNfIkovqllmE2JrFqs6fsTjFvck6ujGJCayjG05xb3JtRWyWYx/gfNjFGuspFD8PH8npXXo++wwWaofSVTeM8vXWMX47lA/4hFW6R3mdr/KVb1mtTxWV3FAFfiGT3XjZql89PuqSQD2tEte+njaJABI4asNDp6zFjLE1heKgQcq+1L5abnfvz4Loqkw0CFZsrMqtPr3Y1mpf1kx0Bmhle07cslaf94rRaMTd3b2oi6HyEFAFfiERsTOCN354A0PSXSueIcnALw5vMPzfPdTNMKLFRN0MIwYRajOPIpwPu0jILVRzh9fi+WXwWl5JOotGmuiYeJb2zlNwaDuO1Y/VIAMNqx+rQflXusFp26Ofylp9qqjkheqHXwhE7IzAQePAsj2ridwdyQ+/PMKfmnOMewbS0yuQbvwhS3opFZWPtZdJSXbJvFfs9cCPH1dcNrPzboovv65YwaCwHuxPkMwLuIzjytVM981gzLmsL4+yWJ8qKnmh9vALgcBagXywaRrPHnqBlDta2oee571nISPDAe2qKNoacw5nl1J1ySxoSAZ95ADWGeeRmvAWH7WB1IS3WGecx7DEATlsJ2WxPlVU8kIV+IWA3kvPpLRR/FD3/9Cdq0iaowQBaXGjmWrczNcMyHGMp2fBByqVNjIHXFmTa8/8+HHQbSM94GvYPh4R8AXotiGTk/nuP1tITlZdXFVUckMV+IVARAQ0+TSW0CMOJHmeggwtpLqgaf4xTXSf4EFW3YWqblCw5dWUW8/c0PxRuoU54hT9LQ6G90lJq0KHcA0f6/zY8a8f3VhDsvd/6PN7gxIxLeKlS5fw9/fH39+fGjVqULt2bcu6EMLy39/fH6PRyLZt2+jcuXOOfAYMGMCBAwcAmDp16sO+jHyxbt06SxkBPvjgA7Zs2XLf+T7yyCP3nUdeWNf7hg0bmD59ut20V69e5fPPP7esnz59mpdffvmBlzHf2PPXLOqlJPnhx4zdLF2CPpRMQGrGOUhGV5YiaIZkdCVZYZRW/p9v9UKNP19WGfT+AFlJ972MIUTO5h1Jx8FKnb8WIGfzjnTu2E8Jo9xxSL6mRSxQeGTzGAFrCnNSluIeHvl+6du3r4yOji70fO/nejODwuVFfuo9E+uAcQ8D1Q+/ELH2Edfpchm9ubU3prYfojncHlPkRhxXrYbgCLps8yP9716s7/J8mVffFAaPV/ySdb0qEt9qG//r/AluB/QQPwRT3QTeG76cO4FLqHuiJvz0eaG7ZAbWCqT7mu4WLyxDkoHua7oTWKvo4yPfb3jkX375hQYNGtC0aVOGDRtm6c1OnDiRWbNmWdL5+PhYApV17dqVZs2a4e3tzaJFiyxpHnnkEcaNG4efnx9BQUGcO3eOXbt2sWHDBkaMGIG/vz9Hjx6lX79+rFmzhoSEBMtXTOPGjS0xeQoaitkao9FIgwYNCA8Pp2HDhrz88sukmC36Op2OUaNG0bRpU6Kjo9m0aRMtW7akadOmhIWFWcIxWNfJ999/b8l7yZIlDB06FIBz587RrVs3/Pz88PPzY9euXYwePZqjR4/i7+/PiBEjsoSIvn37Nv3797eMBjaY43IsWbKEF198kQ4dOlCvXj1GjlQcFjIyMujXrx8+Pj40btzY7gjoAmHvTVDUS1H38AsS8ndGK2V2JkfuSJByPB/K2To/6dJqvJz9VHShTZKhohDTorqsMEorGV1ZotsqGV5NCaM8AYlui/QmMdcefmYI659/PiATE6W8eDGf5zWHcx4fM166R7jn6PHfD0UVHvnWrVuyTp068vDhw9JkMsmwsDDLObKXydvbWyYlJUkp74Y3TklJkd7e3vKiuRIBuWHDBimllCNGjJAfffSRlDJnD99Wj3/48OFy+PDhUsqCh2K2JikpSQIyNjZWSill//79Ldfh6ekpZ5gfyAsXLsjg4GB548YNKaWU06dPlx9++GGudbJ48WL55ptvSiml7N69u/z444+llMrXwtWrV3P08K3XZ82aJfv37y+llPLgwYOybt268tatW3Lx4sXSy8tLXr16Vd66dUt6eHjI48ePy4SEBBkaGmrJy1bIZ7WHX0jk5iOenZEnPfmfcQjlSWE8k1jAEJoYqxC5cx/xHi8zMqeHocr98MxytKujcZR3oE97qHABpNIz1DZaSX8WYyP+F5B1sBdAaqry/9KlvE+r99IzJGAIH/36EUMChqD3enDxkTPDI+/du5e1a9feUx7W4ZH9/f3ZunUrx44dy5Lm0KFDeHl5Ua9ePYQQ9O7dO195z5s3z9KLP3HiBP/88w8ATk5Oli8E69DFebF69Wr27NnD9OnTs4RizvwyyYwBtHPnTkso5z59+tjNr27durRq1QqA3r17Ext7d2R7jx49AIiLi+PAgQO0atUKf39/li5dSnJycr7rJCYmhiFDhgBK1M7KlSvneo2xsbGWvBo0aICnpyeHDx8GlEB3lStXply5cjRq1Ijk5GQee+wxjh07xltvvcUvv/xCpUqV8qzHvCgUgS+E6CCE+J8Q4ogQYrSN/f2EEBeEEHvNS063lWJGbj7i2Znn9xXf8aIlfEIU3Qkjik8ZSrbYVir5JDd1WnzlUCbUysB0+UnQpoMAEntD/GAyAr9mTMd/uXHnWyJ25hyBa+tFbjKBnZhfWTAkGViQsIDxT49nQcKCLIPsiiNS5j88si0cHBwwmUyW9du3bwOKEXPLli389ttvJCYm0qRJE8s+R0dHi1rGOnRxbuzfv5+JEyeyatUqtFptoYRizp7GVshnKSXt2rWznOPAgQN8/fXXeeb9ILAV8rlKlSokJiYSEhLCF198wYAB9y8271vgCyG0wHzgOaAR8IoQopGNpKullP7mJX8BUYoQm77grSJ4tHnWh9yQZGDUzT34kYgXSvgEL5LwI5EYQtXh/feAvZALmUJ/5Ej4X2c3TDUTERkaSHcGv+Vw+QmIH0R6/V8wPPEGR3/NqV+3dz9SU3MvU6bOPurlKCbpJxH1clQWnX5xoaDhkRs0aIDRaOTo0aMArFy50rJPp9OxZ88eAPbs2UNSUhIA165do0qVKri4uHDo0CHi4uLyLJe9MMhXr17llVdeYdmyZVSrVg24/1DMAMePH7dEEl2xYgWtW7fOkSYoKIidO3dapnS8efMmhw8fzrVOrHnmmWdYsGABoOjbr127Zvc6QQn5nFnmw4cPc/z4cZ588km713Dx4kVMJhMvvfQSkydPttyL+6EwevjNgSNSymNSylRgFfBCIeRbpNjyEXe+FEhKp5yGu9vHAokhFC+U8AleGIlBCZ+gDu8vOHmp0wxJBlaJrjinOiPPBCD29IU0F2g/ApxTEOUvcftKQ3reyGmgtHc/nJxyL1Pm7FyZahy9l56ol6OIP/1w4yMXdnjkcuXKsWjRIjp16kTTpk2zzIj10ksvcfnyZby9vfnss8+oX78+AB06dCA9PZ2GDRsyevRogoKC8ix3z549mTlzJk2aNLEIUoD169eTnJzMwIEDLcZbuL9QzABPPvkk8+fPp2HDhly5csWierGmWrVqLFmyhFdeeQVfX19atmzJoUOHcq0Ta+bOnYvBYKBx48Y0a9aMAwcO4ObmRqtWrfDx8WHEiKyTHP3nP//BZDLRuHFjevTowZIlS7L07LNz6tQpQkJC8Pf3p3fv3kybNs1+BecXe8r9/C7Ay8BXVut9gM+ypekHnAH2AWuAunbyGgQkAAkeHh45jBEPm+zz0/bsKeXQXjNl1REa+b4eWXWERg7tNVO6umY17mYuQqgumPeCEPbrU0rFRXLQhkGyou576Rg01eyKOUgyqInFeDsk6HEZU+mFHAZza2P8zz8fkPHxUu7enX/DbWmnIC6IxZWH7RpZlBRXo+0PgE5K6QtsBpbaSiSlXCSlDJBSBmR+3hUl2SNftkzZwsoVfemUUJfJbaBTQl1WrujLQK8tOb4GhIDBg1UXzHshr5ALI1uNZOHzC9Fe7cbEuGs4b5wMgV8qc99KINUFzvrT/d8vCbyWdXCP9WAvUHr2np7g5vbgrkdFpbhQGAL/FFDXar2OeZsFKeUlKeUd8+pXQLNCOO8DxVb0yxr7e/PEKw34tuUFgre3YXnADcbq2jFsX85YLt9+C1YD7lQKQH5CLkRGglYL3/A6Dmd90ZhQDLjHn4K/wlnQcwtjdO3QRyqGLkOSwWLEzXyRe3qCr68q7K0JCQnhxx9/LOpi3Bc6nY79+/cXdTGKJYUh8OOBekIILyGEE9AT2GCdQAhR02q1C3CQYs7Ry0eJ/CuSrqu7YkgyYEgy0PeVc/xe/zLtYoLZYdhG7+jnmRaWyP/qJt/TJN4qtskr5EKmUffSJThKPR5pORGTBhonVwaPXWByACmJ8TkPx4/nOkhK+QJWUSl53Evbve/wyFLKdCHEUGAjoAW+kVL+LYSYhKJL2gAME0J0AdKByyg6/eLN/p6k3VpJhukGnb5qi0nAHQfwiH+OXXFRir+9MYIx0X8SUyeZZ4q6vKWM8HD7L80sRt2gOZyr/yfNDj9Kr99qML7Gi6S0n4g2/jU2NY4ktKokcVEoY7QziI/Wo7caE3HpUjn27LmElG44OQlq11Z7+yolAyklly5doly5cgU6ThTXHk5AQIBMSEgosvO/0fYfliX9w+1eL4PTLQA0ia9gWruc2bzHu3yCgRDCiOKDLokMW297UhOVwkejsZpLoFdHOBZKk7Mm9oZNZ1Z0HY7UuMFCH1dMjx4Ap1v02e7Jz4Z4xndJZE6i4ipbtSo4OKQxduxJnnjiNhqN8jXh5gZmN20VlWJNuXLlqFOnDo6Ojlm2CyF2SykDbB2jToBih567RxBZ9XXQmF37Mhww1f8/uuj+yzDjfMt0hR90SuR2K1XYP0w8PO6OlGXFTwD8CTSJhilho/hPgony7lpuZlRAt/1Flgf8wqykdnT94SpvSyOQObLWkbff9sqSd2bYahWV0ojaw7eDwUvQsZeG245SGcXZYANazS0qZKSyLgr0x4pnvZUFMnX42X31AdB/AG0+onwqvLyiH98aF9NH15+fw5awKhpCjbnfNyEUO4yKSkkltx5+mY+lE7EzgnFfGrIM4x/3pYHRoWASoN04HdYtQ7MqigxTORoer0p8I9eiLnaZJrtrpWXUvM4AAQtodcwZjUnLerownkn8bIxgTLQfm2vnfd/UgXIqpZkyL/CvHQhk6pHuJAuDMoxfGJh6pDtuFzzRRP6AKW44fVhGRWNzyq1ayd7jwwj0iy7qYpd5Mr2ipFRcYKu3MEBYd6r/GkX3qz+iXRWNCOuBXqfENppm3Ez8ztzvmzoxjUppp8zr8COn6EFEQVh3SBgCAQsgKorjZ+rAnTrMsjLQdjOuJdhUh/jKvjy4OIkqBSU8HE7p4gmsFYU+Qk9EBKzz2QJbqxJf+xwjZRLj/RIZ/nMopN09ztERKlWCy5fVqRFVygZlXocvBLRlC976l/m0zTXe2l6Zvw1riCGUmLFblIE7x4+DhweG8K+IrxyqhjsuoURGKi6d5tupCniVUomqw8+FbpW2kKC7zrKAFMZvh2UBKSTortOt0hb0U0KzjKbST1GFfUklIgJqHdiCER0ZUkPsSR2Le2+hSpVcZjJTUSlllHmVzgBdb2LaX0ZGrwbjX8ikxoiwHgzYWBU4W9TFUykkAq9toftUP6LwQk8y/2R4sRc/ml7dwqBBilut2ttXKe2U+R7+/ornWBudxtvGv/iID3jb+Bdro9PYX/Fc/ue0VSl2ZL93sfFhjNG1oztRfMCHdCeKsbp2BLYKIyUF+vZV769KGcBeGM2iXh7anLaenjKGEOnOeTmeD6U752UMIfK6m2e+57RVKV7Ymo94iw7pPgJZS7dMgpR9dP2k+wjkVh2yG9Hq/VUpNZBLeOQiF+z2locl8GPGbrYIeQkW4d+t0mabMdlzmxxbpWjJnL/A1n1LwlPO1vlJRrhJ9O9LRrjJ2To/OZt3pCDDIvTV+6tS0slN4Jd5lU585VCixiai90wCIdB7JhE1NpG1/9oOl6BOWVg8yT45eXZe5yumGjfTJ+ERaDMZEoYwyvgH7zGbWbxHFD0B9f6qlG7KvMAfORKb3jiZozizo47ELJ7YmhbRmhhCaei9lJ8Dkhm/HUTAAtJ1O9Fh5F0+QUsG3ViDs3PuE6irqJRkSr3At354q3RWwihYYz0xhjX5mYRDpfiQV8/cuYGBxB4zeOwKJN4KRqa5QHhHjLokXggKplEvHWs7boWBXrlOoK6iUpIptQI/MhLc3aF377sP79W/lTAKmUI/t4kx8pqEQ6V4kduXl6cndHsznvW9o6hx41U2tI+l+jUtONyGLv3Z0D6WQ841IfALQo+m5jqBuopKSaZUjrTNLZpiE90sksNG8WaCiQUtNIzRziC90nB1QFUJx9Y9d3HJ+ZLu2BEcK/flhyeX4XrdkSsV0+C2K5S7Spf4Wqz76TQacj4TahRNlZJCmRtpa0+f25YtHDcqk5B/1Aae+70u06b0zTHRtUrJI79fZD/9BOtXLqX1cbhSKQ0ynKD8VbhWlzY/dbebv2q7USkNlEqBb0+f+zUDGKtrx/KAG5ZJyK0nuraFasArOeR3XuE5v81hhwfwbw3QpuJ8yxkqn+C9jreZxqi74ZbNqLYbldJCqRT49npjR3XJTAtLpHf081kmITdobPvyWbv6qQa80sGc3+YwfNNwKpwPgIpn8T5eiTvl7lD9XBUI/IJPOh7mmNTRRDcLWkWothuVUkWpFPi2PGwAttZxZUy0Hz8bI7JMjGFvQhNbqiHVgFey2XJsC7OenUX5GtfpYurM/pgqPB9fi3PuN/A+5IX02sb3Olf2hk2nySnl8di5U/3KUykdlEqjLSgP5QBDR24fCMXz9LtMmaJES+wcswufx+by+4rLGAihO1HKwKspOQdaZZks2wrVgFe6MAod3+tcGR52ktYJPsQG7GdWdB1eNF7FC2OO9LaMwSoqxYUyZ7QF5WGc8nooov1whq2cQ3g4zHHZx632E+lxRWYZVRtf2faoWnuqIdWAVzrItM94cJx3jYl4JnRgR5vttE7wUdZJpi05DfrqV55KSaVUh0d+t+W7AAzfNJx1h9YRmx7LrPazeHfiu5Y0evNiiylTbLv6qQa8ko+1G+dxPPhe50pywC+wfRw7AhYyJ8mPJsYq7MOPzqzjR7pmOV4NwaBSEim1PXxQfK4Z7kHrZMmO4ztonSxhuIeyPR+og69KL9b2mRd1QxkedpJOB51w9PsSjrfkvbBTdNa9y1O6yfz42nSc3vTKcrz6ladSEim1PfyOkR1xrFyN9674ggcEJ8MOD9hRfjyz9wK8nK98wsNVAV8ase6h/1nbRJPo0fyXEWz0E9DgBzjUhTSf79jgtxocbvNsfC1+tDpe/cpTKYmUSqNtxM4IjFeNLIhfoGzYOIsaVXdwNnA9ALM2Cd7bpVpdyzI6Xc7Imkkoxtv3wg+Bwx2QAoTMMQLXzQ0uXnz4ZVZRyQ9lzmgbWCuQZQnRVLvmBICL/+ecDVxPlfgwOm8MYatX8XzJqTw8bLnuvs5XTDNuptkuPQhAI+FaXWr/NNaSxsUF5s59uGVVUSksSqXA13vp0SfO4aJjRR65VoWUGsdwPlePKz+t5mLcFDas0BZ1EVWKGFv2mTiXUJrrprL7qW0gAZOAyidY0HE/c3iH4xodetMWevdW/fFVSialUuBHRID+x7/hjB83XK/gfLUad6ofQQTN4neC6M4qdRCNSo5QDO98YuCn8AXgcJsu8bXotKcGmDQQ+AUzOiZzxOTFjhrX0XVuR3KdCHXUtUqJo1QK/MBrWxjb7hzy8Rg48gx3HCXEv4FsP5LOQSGs5WU1VIJKDio3iqc8j6JL7Mzcnxx5d/8ZnDI0YNJwpf4uuuqGkdqzJ0bvP2hySqP646uUOEqd0TYyEhw/qEF4r/OIPf2RPykzGxHWnS5/O5HmepqfV2S9Zk9PpYenopIFjQaDp6R9z/KkaUCDCVOGM7NXe1lG4aqjrlWKG2XGaJs5mCap5jlmLPdF89N80nEi2KjFJfobNl0bxMAVL+Q4Th1Eo2ITDw8whuDw+xBwuoXJ6Q6N/9DzrjERD45bkqiolBRKlcAfYOhIiu8ceuz0xGD8iFScaBbUi7hek/jIGIPTzrcZwcwcx6kPrYotDOFf0VU3DNniM0gtjybVmb+aG/iP7iVMaGiv3ULNmmpgNZWSQ6EIfCFEByHE/4QQR4QQo23sdxZCrDbv/10IoSuM82bn9t+h0H4404McMaDn+aAQdrdfSY1j3kxjLM+whaPUy3KMGipBxR6rHtGSSUqragAAIABJREFUGt6b27IcQfsep1zMWJxEKgt6bGaYriu/1b3OBe1ANXy2SonhvgW+EEILzAeeAxoBrwghGmVL9jpwRUr5BPAxMON+z2uLsD88YONMFrY/ymP967Ch/Q7YOIvBceXxJZG15tG1aqgElfzw+NPx+Dr3ZrbDeKYeOIBj8GQcto3B7ZI7C1veRIT14KNTPyiJdQZoFaEaclWKNfdttBVCtAQmSinbm9fHAEgpp1ml2WhO85sQwgE4C1STuZz8Xoy26cKBebzFe/0TwDMWklsze3EAw/gUR9IB0GohPb2gV6lS5jEbcDuHuZDyz0vg9y2zN8I7cQKtbiuEdYfoKDDqVUOuSpHyoI22tYETVusnzdtsppFSpgPXALdCOHcWtGRA0CfgsROSg5XfoE+U7WYGDSrss6qUCcwGXJkwGPy/xSkxjEnBjryjr5RF2GcmVVEpCJmhuoUABwfl90HYhIpV8DQhxCBgEIDHPTw1s4MEI9pL2DiT4LhAdgTF81774ZgQaOMVYf/554VdapWygCH8K7quuI5TQA+Gb4d5Aeu4deQFPm2zBraPtwh71SakUlCsQ3UDZJj7p5k2ISg8tXNh9PBPAXWt1uuYt9lMY1bpVAYuZc9ISrlIShkgpQyoVq1agQuyoLE/bJzF7LiT/EobZsedhI2zWNDYn/R0Vdir3DurHtEi+vZn7a9VqWAYSb0dL5Hqu4YmiY1wD/iID3WtaKD9hy5dVJuQSsGwNZVqJoVtEyoMgR8P1BNCeAkhnICewIZsaTYAfc3/XwZictPf3ytPntzDqIueDONTJDCMTxl10ZMnT+4p7FOplDEefzqetX3Xoo87i+O4aiSEfgfn61Mr7Qpjov2YGPY//mk/g621/P6/vTuPi7raHz/+OjMMImqu5Q6DWllqZIqiZgrCRcmNEkXR/FpmUd2619QylzZpIfXXdrO6ltcUN0pNja5BjEsoNlRabjdTRhRzyV0UGGbO748PIOiAIuAww3k+HvOAGWY+8x7Q95w55/15H+JS45wdruJCrnUeUGWeJ1ThKR0pZb4Q4hlgPaAHPpdS7hJCvAakSynXAJ8Bi4QQfwCn0N4UKl1iImjvJ9qqrAfwVlU8kVLjTOk1pej7/O2x9JTt2RKwk28a1CZp53x0u1ZiC/iM81YdAS0CnBip4mp8fK5u1X3lzyuL27VWUJQqV7C7favAf5IV9i7Y9aDLxytPx6ylnUgP3M7Spc4OUnEVV87hF+ftXf7S8RrTWkFRbgofH0z05Uzaa5DZC/T5IKB3WgBvWZLIXfZliSqLwgoMdTauUqj4v4lp02DsWO28INBKx6FqzhNSI3xFKSfTtGSGvtGVS4HzsIZNKxrhk+fN4KXj2WKZTm82sopheHpqZ+FarZcffyOjNsV9OBrRV+a/CTXCV5RKZK4fQu3AWVjDpuGVJ+iyKBbWzwHPi6wZ9QmRxhg204dgkslrYcLareQirjobt+aKj9dG81dO31y8CGM+jkO0MZX4FGjKMFVqEYBK+IpSTlOmwMk7d9Pl99vo91tTdnbcSe0mP8PewaDPZ36Pc3Qf6M/ZkaNh1CDIunoRV3VorXkKR/Y2m+Ofy8MBGIZF8KZoxsjROhL9mxGxMIL9myqvCKBanXilKK7iP6GJLBqbzObW5xFRUVwSHiAN8NMErAHz+K8NbHpBx/Vj2FlwUlZx6mzcmqesenuAYIuN9IQFxESOYE+65P2up5ALlxM1qpR3iBugRviKcgOio6FhZAjhWTY8ly3DS+aD/hIEzAO7DpsHdNlxJ0fTZhMikks8Vp2NWzOV9akumGTW05/VlvfJS/87r/eBi+kTWW15n6D48ZUWg0r4inKDli6FzrOG8XILG2LbM2DIAwHo7XDwfn6+/QRTjaGsajRedWhVSv1UV3tUH7YEbmMzvcG4AXvXf8MfoeT3mgPGDZU6/6cSvqJUwJQp8L+BjdEFvo8+H5CATUDzX/C8WI+Zo3ZgvuUgC0wm3tocx7NL5xJPuLPDVpwg3NGfvVccnc6cIydsBmHhbRgQWY/czL7QNgmPnx8hItKAqdttlRaDSviKcoPi46FZoIlPz0VwSXhgk7Xoae4EttpgyCa3YRbZBpgaBGHzIpg+x8Kk7yYR0iZE1ebXMPHxsHDh1bd3ztJh7nCInuaOWAM+Jze3CbRfS09zJ9YnHkAmLGdZ6KBKi0MlfEW5AYUVF8c8zLBzBPZfH+GB+Bf5PfF7YpYMwPB7KDop4WRbtrUG2/kWWO/5GI+U2fyxaCITJqB2yqpBihZse8XBwCe0DXOAlZYPeXJzA7Z0/h1y6kOjDDjWkVmJjQliA6tH1aNtvX9XWhzqxCtFuQFGo+P+JxG3JLMyuz8mW28eDLqPS33mwplW0OAwAQdrYV6Qg17vuDTP1xcslqqOXHGGgm4cWqIfMVRb0Fm2imSCGTJCT7YnoLfheeY28uqfwGv96yTuNhN0dnW5n0udeKUolay0dbTV50O07a6MG8jtugD+CIX6h+l0FNJ9cukYOLbUOmxVm+++ihZsLUG0WP4h3jIb71HBDBoF2QYBOht1/+iO1WCjp7kjOWEzeGnwXZUeh0r4inIDSqu48PEBU7fbiIg0IHY9BG2T0O0dQGY9Az3MndgZtghjeJj20f46j6m4vsIF22CSuWAZhO3Hf3LREy55Ajo7OvPjrFlcm9kJrdja4Qg9D4Vy+o4dlR6HOvFKUW5AbKzjDocHD8LjrQYhE8Lp0nM8vx66hxy/LZzbMIsteklPczxpXZJgu7HE41RtvntbkBcOI/X02XqKB8nm+W4ZYDOAzgp2Pe/s/pEgdhAkfcHzRZLb2EmdNuXaBy4nNYevKDcoPl5bjHM0lx9MMsmE8rTxYeaNSAIhqL9tDLndPyVHehH1wz/ZevgVMjO1kX1srKrNd1dxqXG88IZFOynPWgsvrFh1ApveBtZa6JHUteexajkEHah4PlZz+IpSBaKjtUXWwra2xaUQgg097SytYflK0OVxts+H5Oj0xCwPZWnaLCwWbbq/cKFWlWm6p/2bAvDsEA/mJ8GQS46HXUv2+V7olqzFa8mX9N7ZHPPdDao8FpXwFaWCSltsHc4yJjGHGOahw15wq9C+FFu5LSzxVGWa7inqgg2vhP/g1WExnG2tZV0BjbaMI9nyBnpLbzav202Af0KVx6ISvqJUUGmLrasYxhPGESyIWovdVguvAz1BwidR6zEZAaORudNnM35BnMN2uaqFsnsIih/Pasv75P95HzQ4BHYBEk4FLuQX42lWi4cZEXQCc/2QKo9FJXxFqaDYWDAYHP/svx3PkCO9iFkeSuymbJAe2D1yebqHkbmiAZPy3uKu/TqtPvuKyh1VpukmMjNJCN9AfttNkG+A3Lra9I7nRZ4fuY9fXhrAJym3M6Xy12ivoqp0FKWCChdbn3sOTp4s+TPL6VDuX96L9y2z0GODZf48P2ofe9odZpLvKWYv86OVbjYjHrZBwooSj1Vlmu7B1O02PrnvLzjUlTtSxnKUZuRFPkKO+Ulu8/2e5JZ2Jt6kWNQIX1EqQXQ0/PWXNgcvJSxeXLCYmzqFrYdewUA+EsFEyw56bw0AfT563UXO+O3g6YePUWvtCijWN1+VabquuDhtG8zCVfhldRrD4kQCP5/L/yxPsdryPp4JXxAoL9LF8juJVVB+WRqV8BWlChRW8EgJ+fna1yN6H+Ya/dncdRd+G0eRj4HX+8CT6fCP/JN4eakWyu4g4Gwyw9/wx3TQT/vDpzxLPUs33rglDoQgyDeD1aPqEdF3IYmJNzc2NaWjKFWssF6/Uetn2B75Fu0TXmYvd2PothJrXm3e7iaxZjTkwaa/ss5yj7PDVSrIvCOSqUZfhluSiGEeK3mImca+JLc6SL9UOz5A7N0wxQlv6GqEryhVqHjJ5S8t7dyb8CKETIORg7EuX4ffkvexnrwHRj3I/tD7ix5X2ZtXKzdPwO4zvBm5gwHGKbzOTAYYp/Bm5A6CD59xetmtSviKUoVK7GOaOoVfLJNom30ePC8imv1EhmU8HLkXDLnUt58HtGQ//MvhBLSovM2rlZsnyO7L1AR/FkeupXdQXxZHrmVqgj9tLZfP0HNW2a1K+IpShRyVVq5dCoPX90aGvQjjHoCAf9PT3In9jWGmaSbDvxzOimErCPK7evNzpfozRc/nTUsSo9PrsrnPRkan1+UNSxKPMb/E/ZxRdqsSvqJUIUellW/yAmvSNqDL7AG+m9Fl9mBL4nY6pffk9U2vE9M1RiX7ai4+Hho2hBCRzGEPI1JoPTFM05L55zchGNss5Juuh5i+Eb7peggf40JSKHlilTPKblXCV5QqFBurlVgWN4fnMQS+jfTZQu+DYPfZgi78STb2+JkxO2Ded7H8PXo2DRuCaGOi4cC4q+Z71RaJzjPywzgem2WicYfHMRvPs8/mxwaj5IlWeoYuOc/ZNg+S/vDbnEpIZpZJciohmV8i3y7a5QqcWHYrpayWly5dukhFcQeLF0vp6yulENpXnxFzJC8LOS2wobQh5B3hQZKXkT4j7pFNJiNjAttKMbmxbBz4omRyE4kxRXp7a8cpPJ63d2HFv3Yp/nOlajXtniKZ3EROC2wo6082SO/AV6X3ZG/pHfiqrD/ZIEcOrCsxppT4+2BMkfR6u+jfQFX+rYB0WUpeVe2RFeUmC48Pp+HpEFJnTyQzEzqPbMoOWxeE3srILa1YHLkW8WcnhHEjtsXfF52QVbgFYmnbK6otEqteXGocL4wOoDM/cShyMgP+gEX3AH/eh6H+ftYnnKWPRaAvapZ32c36+6j2yIpSjSRGJxL/zMSi9siztxynrk8y9i2TWWRZgNgXir3dBkbulARbLnfVLFzkK22xT/XeqXoBLQLwHB7BftoyIN2XRf6A3QNa/IwufQJY+nKxsc9V03jV5cxplfAVxcmCLDAz4W7skdEQ8Qh2/+WEbm/K2tsNpBvPE0wycHmRr6ztFZWqExcHr9zfitgVftiiIlnU4zjk1wJdPrdv745X13cZanwW8xPz+fRTbURf3c6cVglfUZyg+KLraobwmmUDHn/0A/9F8Oe9bNi+EGvCV4jIEYw3jkbfzkSPSdqJWI4WgqvLCNJdxcXBt3G/kn6kOa/wGvk6CZ6XwK5DrI/j+O0/M3OzFTF2HMvq6otaaxRucFMdkj2ohK8oN92VG55M4h2sxs143JXAmB2gu/U3rKMepiVHWJVgxdTxGLZhERyz7ge05FFdR5Duav/5x9kU8SjWwA/I6biSXHs9ONIZ9FbouJyZCXdz0qMBT7dcRdsHzM4Ot1QVWrQVQjQClgNGwAIMl1KednA/G/BbwdVMKeXgax1bLdoq7uqqRVejCUNkBIkJ59FbHiAkcAD2sBfA6s2YrbeypvtBsmVtxKpvyPufqs+/2eLiwOOLXkwbkkaOwQ7WOpDyGgTPBEM2XlYdnku+pKulHmneIU5/8y1r0baiCT8OOCWlfEsI8SLQUEr5goP7XZBS1i3PsVXCV9yVTqeN7Iv0ioOsAPpZbOzAn0hWMC8wBxE6Bam3o88zYFvyLRGW06yUw5wWd00UHhvH6Ywkdn//FC/zKpNG/4bU20EKEBKv9bOIPZrA9pZWFqXuApxfLVWVVTpDgIUF3y8EhlbweIri9q5aXE2dApYgUkQI/uzgXzxDzNGtSJsXADYMxDCPFUTd/GBrsDovtOe3vQmkNfmRvKgopvEG8nRbbVtinUQc64Au7Tles2wgK/W9osdV52qpiib8plLKPwu+Pwo0LeV+XkKIdCFEmhCi1DcFIcSEgvulnzhxooKhKUr15GjRFbRR//eEYDLCkqiv0NkEbJyBziZYEvUVm4y2qx+kVJkG++7hcNt0jBYfcqQXOWMGQZN9IEFnA9l0N9bAD2hFZom2CdW5WuqaCV8IkSyE2OngMqT4/QrO8Cptfsi34CPGKOBdIURbR3eSUn4qpewqpex66623lve1KIpLuHLRVa8v+fOlHQW50hP78q/pbQrBvvxrcqUnyzsJ5wRcQy1c9Rd682NY2u/UFmf1dhCg29+Xdxb542UVWMNeYldgctFjqnu11DUTvpQyRErZ0cHla+CYEKI5QMHX46UcI6vg6wFgA9C50l6Borig4mV79itOytx6Opqc5YnMsaxjE32YY1lHzvJEsuqpMpyqVnx7wn6YSErcD9m3aiWYErB54NlqC6/yMncteRtDxkC8OiS7TLVURad01gBjC74fC3x95R2EEA2FELUKvm8C9AJ2V/B5FcVtXDkFsCt1ERGW0zzLB0jgWT5gTovT2E8vckp8Ncn+848zdMl5TAf9EMDIERegzomiuQvj7+3JkV5cjBrNdl0XFvxtLZfmJ1a7evvSVDThvwWECiH2ASEF1xFCdBVCFDZ/vgtIF0LsAEzAW1JKlfAVpYCjOf1VDMNAPjokniKfianDbvr+p+6qrE6jUUlrEZEjGGp8ltbhAznWPh3sOkLXhxVN77S2GJF7RzPin+Zqn+CvpJqnKUo1EB8PY8eCzcG6bGGZX1yctkF2UPx4rRTExwdT9HzM9UOYMuWmh+ySCk96K9qFDKBXHF4nA6hzIojjJ3VsNEoGRNUiV6cHYWdO/J1MtOzARF9Cw9tiuGMjl/7fPqe9hmtRzdMUpZqLjoaFC8tumfDh4T6Ep2zDdNAPpMR00I/wlG18eLjPVcdT/fIdK7HlZKGsAGyDIvigXjMEkl/wJ1enA8+LeG75O50tDZHA7foM5npE8Wrz6pvsr0UlfEWpJq7VMmHgbyfJCZtBeGAIM3mV8MAQcsJmMPC3k0XHiI+HJk1g9OjLrRucuWl2deOoRj7CcpLaCQuIiTzF2CBfnh+RAXYDYzb6UrugIVoISbS2WZiaHELLljc/7kpTWqN8Z1/UBiiKUpIdbXMUXhaScb0lLwsZE9hW2kGmvJQko6Ku3hil+EWvlzdlA47qzNe35O8kmCTZhONyDv+QtYMmSl5B8pK3jDE+LO0glxibSsPk+iU2NPH1dfarKBtlbICiRviK4kIi01ojMnuC72ZEZk8i01qzgb4Mf8Of3MTkq6crirHZ1Ii/+AJ5I/4inG9YwXBeNT5AXtcFcKAfwqYnkq+QCEZZjmJNWAUtLzdEc7T5jKtQCV9RXMRqhhAeGIL0SaXB0VZIny30G1mbgcaJTDWGckenSO2ORpPWn6cMFy9q89nu5HrWLQqnzdr3eZzT4/szaeQ+3g20cSlyHLbN08HqBReaEz5Kx3LjbdqDLEFa+4sCV54o50pUlY6iuIhmvadyrN/bDDY3Z0uHIzTaFczvASmIfE/q5eexcjmEkAKRwyFhRdHWiKUR4uqTvlyVo+obb++SayBxqXHsP7WfqI5RMHIk4f1OaN0v7QKyukGznWDIxpDngcE0g+b6Q+xP/bfD56umaRNQVTqK4hZ0gTu447sJfJV4jKkJ/uzrsIO6+7sjdTZsQs8GP2gUGULnhBdKJHtdKf/LpXSfCh5H1TdXfooJaBHAFz8vY9DCQXDsGLEpBe92Ogmtt2mtjvN01F66grC0u0tN9o0bV9GLuAlUwlcUF3HknUT63fsx/fkvb1iSGJ1elwvttmH4YRLWbROZ1QceTG/Nfst42rEPX19YvBi++MJxszYoOZ/vyqWc17PPb5BfELHWGWTn6BkwSsdLwTqw1dK6XxZceqcF8JXlQ0wNSm9Dff68a/1uilMJX1FcRHy8Vqv/PSH4GBfyTddDjN7oi7XbPPK6f0Ltky1Y1OMENuNmPr1lMhYLHGszl3jCi8o9Hbl4EZ57ruQuXK62sHs9+/zGxUHnD35gzI8NyfW0k+tpB2HX2iYUXJICf2NWm96cPq39HhyN5vPyXHf9Q83hK4qLKNopy2gqmqd/vGMwi+7RkWOrC79FQ8A8DPmCcTskF8LmsOSvSfDdbHyPTCQ2FsaMKd/8s7M387heZc3hZ2VpZyjz4YcMbTQW24hILhr0SH0eAAar4M0UyfRgnTann+fNS37riH086OrNagpU5/UPNYevKG6gaHqipbnEoqzeLvDY+CKcNSLME7B6SL6+E5b+9Twd14+BrROLRuyNGt3gc1ZzxU9aA62SpnAOf3rW7YRuX8YvjSzYRkSSLeogT7YBmwHsevQIWhxtSs6SZPh9IJzoyEdfa2WY1/PJwZWohK8oLqIoyRTskAUwYN0Q5LJV2HrPwc9zB7LDSjjWkWP1wJjZkl1pC5jNRCL4smj066h9Q2kLka6U2KKjL9fZF/YkOngQOv9RF1vAZ0wK9qTFrt6Q0Qdu20urn8OYs6gj8tdoZrQcpP1Ol66F+ds4m6iVYTpqbFfde96XRSV8RXERjpLPZN7BYOnN6PS6ZPRZQqs/G0PTXXDwfjJ8jjAosC8CyWoeYiCrOXnScfuG995zj8TmqFonLXE7Pc2dkK3N7Gu3H9qvpZV5IDmJn9PZ0pCl685dVZFT+EZ3rXYXrkbN4SuKC4mP15JaQbNMMjPhXt/ZHIx8gfv+tJPcFrwOdSLneA+w6yDgE1j/DoOPZrK+42naXazFzu8dlxteeezYWNdLbI7m3O0INtCX4H/shwaH4ExrUt7VNt2LZAX+7CixReGV9fuupqw5fJXwFcWFDXnOxJpa2gJu/57BnDvTiS33ZKLT5WK3eWoLuZ3iwSOPOvlW1iy3ESx9eWpMf1I8M9k7w72a7BctbBeTRD/6h/thC/gMj7PNyK9/FL35Mb5L3E8s00okeyHgySfho49ubtyVSS3aKoqb6jXczODcFegPBVF7SQJbE7fTfWdr7sxopmWve/+jbc/nkYPPCW+CLfBUcw/m6T+l4e/+zg6/0uXlwUBWY0cUXYaEt8AW8Bk9zZ2wvnuUnuZO2AI+Y1j4rSWSPWifDtx5oxmV8BXFhU3pNYWv3wsiPx8O+A/jYVbSeeed7Gl1gTv23qkle70V7Ab2tD6Pzzgf5oUdoNb613ljzR5nh1/phorVrGMIT/MhAniaD7noZ6bpTw+Run43EtiYuBuj+W+c9vvV4TFcpTLpRng4OwBFUSrHmTPwJcPAMoy7No9jT9hC9Daw6UBn02E/0ZFDvjvhYE++TUsmiA1gNLrFrlmFu4GlRETQIeN+5iVu4ise5jhN6ZCxjHy/lZAvEWhJL4OC6R8Hx3KlyqTyUiN8RXETRSNTo4k9fVch8j2w6WH0r+AhrNB0JxztBD5beTdQq1s0HfRj+Bv+2olJLqxw8/E7M1qwK+AHvMLHcdy4B/2jXdkV8APBGVc/xt1KLq+HSviK4iaKRqYtzXCwNzK/NmycQcJdevI87OgOdaXduRz05kdZE/YDrUb6M9T4LFONoZh3aK2VTRkm4lLLbq1c2Sqjh0/h5uMbdn9IE/ND5AQshEf6YWv9Mx3M9/ORg3l5dyu5vB4q4SuKmygasWYFQKs0WL4aTK8hj3UAqzfvpFj5dMs+6nb4AmF+nCzvWlwYMYaZUTsJ2HUGk58g4uP+bJ7TnLiblPMLWyJUtIdP0I/HWZVg5WLko/x1qQNIATpJ3bMN2ZW4iUcarnL4uOhorXWE3a59dedkDyrhK4rbKByxet9esvVC3mc76LLsVf5qdZC+FpiZcDd0+IpmB+7BLjzIlnWY5BfOoMja5K/4kk2rBhEQOxTTtOQqT/zX09b4uvj4gKUv+elPQ5/XQUhuO1OLC/VP4xM+gKXnh1ZazK5MJXxFcSPR0ZD93RQWzwoqmqpo3Bh+PTSJN384TQhJRa2Vj/aZT+i29vDjs/zcJ5Hs9OeRliBWEwHnzt6Uuf3raWt8PUzR8xlqfBZDjzdAag3RclYvpYV5IJkB68kPe6riwboBlfAVxQ0Vn6qoWxesVu32lGKtladvhB+7b8PQbQ5snAFdPybXuIUFjGM4K1jBcILe7s/7Q5Jp2LBq+uSXVhFT3iZvy+rqEWPH0eSijifNsHDJbVyKHMeR3RPBHIOubYrL9vqvTCrhK4qbKzFaNpr4JfJtTiUkc3dGU/KlHqsw4JkRSK2Ehdgio1lkbI0vFoLYgMnWm9fX+HP/mdVV0ic/NhY8Pa++/dy58j1H2wfMrBq7ine65vPFBlli83FD0kd4fLzXZXv9VybVWkFR3FyJdgO94rRFXUsQ7Xo9zp9Zg5FAx57/R5ctQcwjBtHSjEx9gTuMH3O45Z+MTW3Kx8QwlJWsQtsJ6nr75MfHwxNPQHa2dl2n064Xb13QpAmcPHn1Y4s/R3h8ODqhY8+PLWmRfDuLdn3IV4EHWXqvgea3hmKr1YfEaVOKnrN4T6ALF659fHeieukoSg1W2uYg99wD+/RTeDYrlTpkMznyME8m9OXLkJ84Y2+ItfEhvPb0J2fn/zG42avktdnMf5dIbQOWlmbkD2WfqRUfD4884nijkJiYy0n/ejYZmbt1Ls+vfx5dvgFpq82gX+uyJuAIHvk68m11maOfwcRZkxzG4YqbmFSESviKUsOV1glz2r9NvLl/OHL5CjrzE3uiJ5On0xptepnHkrN7LEQPAI9cZq8H/6M6Hoysg/z6a/L+F1TmczpqZFZIr4f8/LLv17ixtv6QmQm1akGbe8exO+w/CLtA6iTYPcBah5hlIXwk00sdrpd2/Jo4wldz+IpSA5RWbx77eBDfP7GCJk8NZ+Dn58gVBux6aHKkNTkBX6Dv/xR45CLyDZytDUMja5GXsIqJdgdzJFcoq9KmcIMScHzGq8GgbRZeOO8ekrOaPWmf0TyzLVIvtU3H9fk03zaMjyxflflk4eHlu92dqYSvKDVckF8QMV1jeH3T60i7gZbb+/BXi8PUyfbC1mwvtY+1QW55gdf7QHb688yxrOOtjKhrHresnjR6/eXvHZ3xesstWudLgGCSSaMXPQKH8afPfq05kARsHhztnsBco3+ZT1Za90t37opZGpXwFaWGM2WYeG/be3gbvBF4kLX9Zeof8SO77iXq5MKlpgegh1bzLyOEAAAM5ElEQVS66dH1Awh8l7hA2zWPGxurzZ87MmFCyetXfgI5deryzwJ6RdIz/F62hK2C/FpgrYd+b3/Q5aPXX2BSVAZzRz9TahyVVevvDlTCV5QazJRhYviXw4nqEMW6keuYalwN0eGcbXEAsrqQVzgSN1zC79JJ8jfP4PkwgcXerkQDHEdn5UZHwxdfQJ06l2/T6Uou2Jam+IA9NOsM3953BO9DHWHHWLw2TMLmk04H8/3Io/50O9GN5Nqlr76WVtNf3lp/d6ASvqLUYOYjZlYMW8Engz4hyC+IkBAQ+nw41B3+nU7L7SGQ541Hvg59wEfU6T0D1r/DfN0jmA76gZRldtyMjtbKIqXULjZb2ck+LjUOU4aJBg/G0bXNbDIwIoB223tx8dZMPBvtIrf3uwxOGMXuxI1M+HYmD92RVFSSqZStQglfCBEphNglhLALIRyuChfcr78Q4n9CiD+EEC9W5DkVRak8U3pNIcjvcrWN+YiZ7//vOxYHp2lVLOuSuH/pZB44ZOePxvCP9Bxi007QOTWU4axgJq9ePis3fnyF4wloEcDwL4fTs9EBfnr4LeICPXgo0sDevwaD3kpem1RGp9dli2U6D7KGBaeG0rJl2ccsPj10Pbe7NSnlDV+Au4A7gQ1A11Luowf2A20AT2AHcPe1jt2lSxepKIpzCSElxhTJ5CaSoBmSyU1kslEbsM/gVQlS9iNJptBXG8QLIaWvr0x5KUlGRUnp61t0k1y8+NrPFxUl5f0d3pFNJiNDI5pKXhay0dBwyYu3yDov6OX0IGSjyTrZ2fiOLPzc4O1d9rF9faW8/Bnj8sXXt3J+R9UNkC5Ly9ml/aA8l2sk/B7A+mLXpwJTr3VMlfAVxfmadi9I9sYULVEaU2Ttl4SMCWwrm3BczuBVWZ/T0tu4Rk7o1UZKkCn0lU04LsP0SSUS7JWJefHikm8IgYFSDiNBNuG4HBPkK3kF2WBcR8krSP1LnjLFiLQhLr8BFcZ0jeS9eLH23GXF4k6cnfCHAfOLXR8DfFjKfScA6UC6j49PFf9aFEW5lqgP3pa12qeUSJbtAp+QvOQt5xj9pQQ5x+gvxeTG0tu4Rs7gVdmE4zKFvjID31JH1Y6ScFt+l/U5LWOMD0sxubFsFtFP8rKQnhPuluLFW+Qco//lYxpTJL3eLnqsEGW/jivfXNw12UtZwYQPJAM7HVyGFLtPpST84hc1wleU6uHKZAlSdja+IxtN1snpQcg6LyFjAtvKfmgj+jEslClG5Fu9kBn4ymBKjvQLjwdSS9rju8l2gU/IgFGNpCH8UckLt0iPmLaS6QbJiIHSY7r2iUJMblxiKqe06ZmalNwdKSvhX3MTcyllyHUvCDiWBbQudr1VwW2KoriA6OiSO0EZjfCLZRKkn2NWn9eJ2F6HJb0zkUfPc1/LESyydWN571v4W8JjGJCkG8/zeMu2DEjtxGTeYdy424vaNZMVgOjzCn+Emem5txHWgM/Brif/tnOQ2QPaf0O4uTlGjwM8nRXHJ0Y7WErGV3wf2iv7BhV2xix8HTXdzSjLNAO3CyH8hBCeQBSw5iY8r6IoVSA2Fmq1N0HXebBxButu98C6eToicgQdblsBYZPIO9ybdYTzvHEQFyPH0T6rHqONj3Gw1wrqWk9cPpgliEeXDAFrbba0P1mwNaENTvuBTxqD19/PlsTteDX6js9XT8K6oWT5ZePGJfehrbQdtNxURcsyI4QQh9EWZr8RQqwvuL2FECIRQEqZDzwDrAf2ACuklLsqFraiKM7SoqcJz1HDabppBWLDa3h+uwrRby6D//RkkT+M+RUMvikwajBEPUR+wkomMZuLkY+SnxXIaZrgxz5Aa5vwteV9Qrd20vrj6CTk3gKNMtBlBrIxbQ0TmcPLm0KuSuSgNVcrPnJXZ9WWrUIJX0q5SkrZSkpZS0rZVEoZVnD7ESlleLH7JUop75BStpVSxlY0aEVRnMd8xMzXo1dwNC0Iux0u/BbEaw/O5Ku7JDO212dNOwNi70DwvAS6PPDbgIwcCQnLwRKMgTxOcSvBJPMZ45lqDCWpx29afxy7Dmqdo/kpL6TPVnIC/0XaXY9x9qzjWK5M5KW11Cmrr09Nos60VRSlXK48WcuUYeLNH95k3ch1BN39JdbN07H6J+CdowNDjrapeHoMWIIh/Cmsk1rhOSqEqbzBAeNBpo36DQyXYO9gEBJsBv5smEv3vY3JDZtBZt+1153IHXXeLD7HX9OphK8oSoUUtmcI8gtiWV09hrC5PLmxA5fOtgOkNnLv+RaMGAIBH0Odvzjhu4fwgT4sb9kG/fH2YH4Cmv+E/vdQYhYPRv97KDv0HWD9bH69kEx4+PUlckedN4vP8dd0agMURVEqTVxqHGd3B/De+5Az4GFs55tD092O72z1xrBkJVYMMCKCOiKb15Z15E1LEr3ZWLSdImiJOzxcS942m9ZeecKEazdhq4nUjleKolSpwh21Dh7URtayp7Z3bh1LV7JfaA21CybhJWCtAymvYOgzE6teAJJadjvfLsulrwVCSCKFq6vBvb2v3qZRjd6vpna8UhSlyhTWvhduIyglkDoFLEFkh78AXme1RA8goHOmJwPT7sT+43PgeVFb3N32LFj6kqX3dZjs9XpVblkZrnnilaIoSlkc1b4DEP4UBMy7fN0uQEh+aXuaX0fMw2ZMpVaeNubM7f4JgzIW88Y9tfFOvnok7/D4qHLL8lIjfEVRKqTUpOuXApcaaN9b68AX30NmTwBs7b/FS5/Nt0vsfLvETh0uYh0dza5AvcNFV19fx0+hyi3LR43wFUWpEB+fy9M5JfxrL4wKB6mHrRPBEgQLUvF6dBD12m8jon0EQbGfALA2w8Syncto28hMdK8gh/PyxVsmgCq3vBFq0VZRlAq5sn8NFCzcystfC1VkobVwYTgzU3uTiY1VC7aOqEVbRVGqjKPa90WLtES/aJHW76ZQ7doVe57iG52rZF9+akpHUZQKu7KjZnGXLl3+/uRJ1b3SmdQIX1GUKqO6V1YvKuErilJlVPfK6kUlfEVRqozqXlm9qISvKEqVUd0rqxeV8BVFqTKqe2X1oqp0FEWpUmVV8Cg3lxrhK4qi1BAq4SuKotQQKuEriqLUECrhK4qi1BAq4SuKotQQ1bZbphDiBOCo6er1aAL8VYnhOIOrvwZXjx/Ua6gOXD1+uPmvwVdKeaujH1TbhF8RQoj00tqDugpXfw2uHj+o11AduHr8UL1eg5rSURRFqSFUwlcURakh3DXhf+rsACqBq78GV48f1GuoDlw9fqhGr8Et5/AVRVGUq7nrCF9RFEW5gkr4iqIoNYRbJXwhRH8hxP+EEH8IIV50djzlJYT4XAhxXAix09mx3CghRGshhEkIsVsIsUsI8ZyzYyovIYSXEOJHIcSOgtfwqrNjuhFCCL0Q4hchxDpnx3IjhBAWIcRvQojtQoh0Z8dzI4QQDYQQXwoh9goh9gghejg1HneZwxdC6IHfgVDgMGAGRkopdzs1sHIQQjwAXAC+kFJ2dHY8N0II0RxoLqX8WQhRD/gJGOpifwcB1JFSXhBCGIAfgOeklGlODq1chBATga7ALVLKgc6Op7yEEBagq5TSZU+8EkIsBDZLKecLITwBbynlGWfF404j/G7AH1LKA1LKPGAZMMTJMZWLlHITcMrZcVSElPJPKeXPBd+fB/YALZ0bVflIzYWCq4aCi0uNjIQQrYAHgfnOjqWmEkLUBx4APgOQUuY5M9mDeyX8lsChYtcP42KJxt0IIYxAZ2CbcyMpv4LpkO3AcSBJSulqr+FdYApgd3YgFSCB74QQPwkhJjg7mBvgB5wAFhRMrc0XQtRxZkDulPCVakQIURf4CviHlPKcs+MpLymlTUp5L9AK6CaEcJkpNiHEQOC4lPInZ8dSQfdLKe8DBgBPF0x5uhIP4D5gnpSyM5ANOHVt0Z0SfhbQutj1VgW3KTdZwbz3V0C8lHKls+OpiIKP4Cagv7NjKYdewOCCOfBlQLAQYrFzQyo/KWVWwdfjwCq0aVtXchg4XOzT4ZdobwBO404J3wzcLoTwK1gciQLWODmmGqdgwfMzYI+Ucq6z47kRQohbhRANCr6vjVYIsNe5UV0/KeVUKWUrKaUR7f9BipRytJPDKhchRJ2CRX8KpkH+BrhU9ZqU8ihwSAhxZ8FN/QCnFi+4zSbmUsp8IcQzwHpAD3wupdzl5LDKRQixFOgLNBFCHAZellJ+5tyoyq0XMAb4rWAOHOAlKWWiE2Mqr+bAwoLKLx2wQkrpkqWNLqwpsEobP+ABLJFS/te5Id2QvwPxBYPQA8A4ZwbjNmWZiqIoStncaUpHURRFKYNK+IqiKDWESviKoig1hEr4iqIoNYRK+IqiKDWESviKoig1hEr4iqIoNcT/B3t7QQYFDknGAAAAAElFTkSuQmCC\n", + "text/plain": [ + "
" + ] + }, + "metadata": { + "tags": [], + "needs_background": "light" + } + } + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "V7vlfJqbiZMU" + }, + "source": [ + "**2. Loss (MSE/Mean Squared Error)**" + ] + }, + { + "cell_type": "code", + "metadata": { + "id": "IpHifyGZRhw8" + }, + "source": [ + "# Calculate loss\n", + "loss_tf, _ = model.evaluate(x_test, y_test, verbose=0)\n", + "loss_no_quant_tflite = evaluate_tflite(model_no_quant_tflite, x_test, y_test)\n", + "loss_tflite = evaluate_tflite(model_tflite, x_test, y_test)" + ], + "execution_count": null, + "outputs": [] + }, + { + "cell_type": "code", + "metadata": { + "id": "g3HLT0UOjTY_", + "outputId": "0c1c279a-96bd-4e8d-8a65-6a071376825b", + "colab": { + "base_uri": "https://localhost:8080/", + "height": 171 + } + }, + "source": [ + "# Compare loss\n", + "df = pd.DataFrame.from_records(\n", + " [[\"TensorFlow\", loss_tf],\n", + " [\"TensorFlow Lite\", loss_no_quant_tflite],\n", + " [\"TensorFlow Lite Quantized\", loss_tflite]],\n", + " columns = [\"Model\", \"Loss/MSE\"], index=\"Model\").round(4)\n", + "df" + ], + "execution_count": null, + "outputs": [ + { + "output_type": "execute_result", + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
Loss/MSE
Model
TensorFlow0.0102
TensorFlow Lite0.0102
TensorFlow Lite Quantized0.0108
\n", + "
" + ], + "text/plain": [ + " Loss/MSE\n", + "Model \n", + "TensorFlow 0.0102\n", + "TensorFlow Lite 0.0102\n", + "TensorFlow Lite Quantized 0.0108" + ] + }, + "metadata": { + "tags": [] + }, + "execution_count": 31 + } + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "E7Vjw7VckLu1" + }, + "source": [ + "**3. Size**" + ] + }, + { + "cell_type": "code", + "metadata": { + "id": "wEXiJ8dFkL2R" + }, + "source": [ + "# Calculate size\n", + "size_tf = os.path.getsize(MODEL_TF)\n", + "size_no_quant_tflite = os.path.getsize(MODEL_NO_QUANT_TFLITE)\n", + "size_tflite = os.path.getsize(MODEL_TFLITE)" + ], + "execution_count": null, + "outputs": [] + }, + { + "cell_type": "code", + "metadata": { + "id": "8DdsCaL7kL4u", + "outputId": "9644f10d-0914-4939-b596-facb90e4b961", + "colab": { + "base_uri": "https://localhost:8080/", + "height": 171 + } + }, + "source": [ + "# Compare size\n", + "pd.DataFrame.from_records(\n", + " [[\"TensorFlow\", f\"{size_tf} bytes\", \"\"],\n", + " [\"TensorFlow Lite\", f\"{size_no_quant_tflite} bytes \", f\"(reduced by {size_tf - size_no_quant_tflite} bytes)\"],\n", + " [\"TensorFlow Lite Quantized\", f\"{size_tflite} bytes\", f\"(reduced by {size_no_quant_tflite - size_tflite} bytes)\"]],\n", + " columns = [\"Model\", \"Size\", \"\"], index=\"Model\")" + ], + "execution_count": null, + "outputs": [ + { + "output_type": "execute_result", + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
Size
Model
TensorFlow4096 bytes
TensorFlow Lite2788 bytes(reduced by 1308 bytes)
TensorFlow Lite Quantized2488 bytes(reduced by 300 bytes)
\n", + "
" + ], + "text/plain": [ + " Size \n", + "Model \n", + "TensorFlow 4096 bytes \n", + "TensorFlow Lite 2788 bytes (reduced by 1308 bytes)\n", + "TensorFlow Lite Quantized 2488 bytes (reduced by 300 bytes)" + ] + }, + "metadata": { + "tags": [] + }, + "execution_count": 33 + } + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "qXdmfo7imGMB" + }, + "source": [ + "**Summary**" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "R1LVMA2nkM_l" + }, + "source": [ + "We can see from the predictions (graph) and loss (table) that the original TF model, the TFLite model, and the quantized TFLite model are all close enough to be indistinguishable - even though they differ in size (table). This implies that the quantized (smallest) model is ready to use!\n", + "\n", + "*Note: The quantized (integer) TFLite model is just 300 bytes smaller than the original (float) TFLite model - a tiny reduction in size! This is because the model is already so small that quantization has little effect. Complex models with more weights, can have upto a 4x reduction in size!*" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "HPSFmDL7pv2L" + }, + "source": [ + "## Generate a TensorFlow Lite for Microcontrollers Model\n", + "Convert the TensorFlow Lite quantized model into a C source file that can be loaded by TensorFlow Lite for Microcontrollers." + ] + }, + { + "cell_type": "code", + "metadata": { + "id": "j1FB4ieeg0lw", + "outputId": "c25b75c6-a28d-47b1-9b3a-b7ba821ee310", + "colab": { + "base_uri": "https://localhost:8080/" + } + }, + "source": [ + "# Install xxd if it is not available\n", + "!apt-get update && apt-get -qq install xxd\n", + "# Convert to a C source file, i.e, a TensorFlow Lite for Microcontrollers model\n", + "!xxd -i {MODEL_TFLITE} > {MODEL_TFLITE_MICRO}\n", + "# Update variable names\n", + "REPLACE_TEXT = MODEL_TFLITE.replace('/', '_').replace('.', '_')\n", + "!sed -i 's/'{REPLACE_TEXT}'/g_model/g' {MODEL_TFLITE_MICRO}" + ], + "execution_count": null, + "outputs": [ + { + "output_type": "stream", + "text": [ + "\r0% [Working]\r \rIgn:1 https://developer.download.nvidia.com/compute/cuda/repos/ubuntu1804/x86_64 InRelease\n", + "\r0% [Waiting for headers] [Waiting for headers] [Waiting for headers] [Waiting f\r \rHit:2 https://cloud.r-project.org/bin/linux/ubuntu bionic-cran40/ InRelease\n", + "\r0% [Waiting for headers] [Waiting for headers] [Waiting for headers] [Waiting f\r0% [2 InRelease gpgv 3,626 B] [Waiting for headers] [Waiting for headers] [Wait\r \rIgn:3 https://developer.download.nvidia.com/compute/machine-learning/repos/ubuntu1804/x86_64 InRelease\n", + "\r0% [2 InRelease gpgv 3,626 B] [Waiting for headers] [Waiting for headers] [Wait\r \rHit:4 http://ppa.launchpad.net/c2d4u.team/c2d4u4.0+/ubuntu bionic InRelease\n", + "\r0% [2 InRelease gpgv 3,626 B] [Waiting for headers] [Waiting for headers] [Conn\r \rHit:5 http://archive.ubuntu.com/ubuntu bionic InRelease\n", + "\r0% [2 InRelease gpgv 3,626 B] [Waiting for headers] [Waiting for headers] [Conn\r \rHit:6 http://security.ubuntu.com/ubuntu bionic-security InRelease\n", + "Hit:7 https://developer.download.nvidia.com/compute/cuda/repos/ubuntu1804/x86_64 Release\n", + "Hit:8 https://developer.download.nvidia.com/compute/machine-learning/repos/ubuntu1804/x86_64 Release\n", + "Hit:9 http://archive.ubuntu.com/ubuntu bionic-updates InRelease\n", + "Hit:10 http://ppa.launchpad.net/graphics-drivers/ppa/ubuntu bionic InRelease\n", + "Hit:11 http://archive.ubuntu.com/ubuntu bionic-backports InRelease\n", + "Reading package lists... Done\n" + ], + "name": "stdout" + } + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "JvRy0ZyMhQOX" + }, + "source": [ + "## Deploy to a Microcontroller\n", + "\n", + "Follow the instructions in the [hello_world](https://github.com/tensorflow/tflite-micro/blob/main/tensorflow/lite/micro/examples/hello_world) README.md for [TensorFlow Lite for MicroControllers](https://www.tensorflow.org/lite/microcontrollers/overview) to deploy this model on a specific microcontroller.\n", + "\n", + "**Reference Model:** If you have not modified this notebook, you can follow the instructions as is, to deploy the model. Refer to the [`hello_world/train/models`](https://github.com/tensorflow/tflite-micro/blob/main/tensorflow/lite/micro/examples/hello_world/train/models) directory to access the models generated in this notebook.\n", + "\n", + "**New Model:** If you have generated a new model, then update the values assigned to the variables defined in [`hello_world/model.cc`](https://github.com/tensorflow/tflite-micro/blob/main/tensorflow/lite/micro/examples/hello_world/model.cc) with values displayed after running the following cell." + ] + }, + { + "cell_type": "code", + "metadata": { + "id": "l4-WhtGpvb-E", + "outputId": "4c7925a5-4cf3-4f7a-fcbc-c8c2857423ea", + "colab": { + "base_uri": "https://localhost:8080/" + } + }, + "source": [ + "# Print the C source file\n", + "!cat {MODEL_TFLITE_MICRO}" + ], + "execution_count": null, + "outputs": [ + { + "output_type": "stream", + "text": [ + "unsigned char g_model[] = {\n", + " 0x1c, 0x00, 0x00, 0x00, 0x54, 0x46, 0x4c, 0x33, 0x14, 0x00, 0x20, 0x00,\n", + " 0x1c, 0x00, 0x18, 0x00, 0x14, 0x00, 0x10, 0x00, 0x0c, 0x00, 0x00, 0x00,\n", + " 0x08, 0x00, 0x04, 0x00, 0x14, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x00,\n", + " 0x98, 0x00, 0x00, 0x00, 0xc8, 0x00, 0x00, 0x00, 0x1c, 0x03, 0x00, 0x00,\n", + " 0x2c, 0x03, 0x00, 0x00, 0x30, 0x09, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00,\n", + " 0x01, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x60, 0xf7, 0xff, 0xff,\n", + " 0x10, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00,\n", + " 0x44, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x73, 0x65, 0x72, 0x76,\n", + " 0x65, 0x00, 0x00, 0x00, 0x0f, 0x00, 0x00, 0x00, 0x73, 0x65, 0x72, 0x76,\n", + " 0x69, 0x6e, 0x67, 0x5f, 0x64, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x00,\n", + " 0x01, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0xbc, 0xff, 0xff, 0xff,\n", + " 0x09, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00,\n", + " 0x64, 0x65, 0x6e, 0x73, 0x65, 0x5f, 0x34, 0x00, 0x01, 0x00, 0x00, 0x00,\n", + " 0x04, 0x00, 0x00, 0x00, 0x76, 0xfd, 0xff, 0xff, 0x04, 0x00, 0x00, 0x00,\n", + " 0x0d, 0x00, 0x00, 0x00, 0x64, 0x65, 0x6e, 0x73, 0x65, 0x5f, 0x32, 0x5f,\n", + " 0x69, 0x6e, 0x70, 0x75, 0x74, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,\n", + " 0x0c, 0x00, 0x00, 0x00, 0x08, 0x00, 0x0c, 0x00, 0x08, 0x00, 0x04, 0x00,\n", + " 0x08, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00,\n", + " 0x13, 0x00, 0x00, 0x00, 0x6d, 0x69, 0x6e, 0x5f, 0x72, 0x75, 0x6e, 0x74,\n", + " 0x69, 0x6d, 0x65, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x00,\n", + " 0x0c, 0x00, 0x00, 0x00, 0x50, 0x02, 0x00, 0x00, 0x48, 0x02, 0x00, 0x00,\n", + " 0x34, 0x02, 0x00, 0x00, 0xdc, 0x01, 0x00, 0x00, 0x8c, 0x01, 0x00, 0x00,\n", + " 0x6c, 0x01, 0x00, 0x00, 0x5c, 0x00, 0x00, 0x00, 0x3c, 0x00, 0x00, 0x00,\n", + " 0x34, 0x00, 0x00, 0x00, 0x2c, 0x00, 0x00, 0x00, 0x24, 0x00, 0x00, 0x00,\n", + " 0x04, 0x00, 0x00, 0x00, 0xfa, 0xfd, 0xff, 0xff, 0x04, 0x00, 0x00, 0x00,\n", + " 0x10, 0x00, 0x00, 0x00, 0x31, 0x2e, 0x35, 0x2e, 0x30, 0x00, 0x00, 0x00,\n", + " 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x84, 0xfd, 0xff, 0xff,\n", + " 0x88, 0xfd, 0xff, 0xff, 0x8c, 0xfd, 0xff, 0xff, 0x22, 0xfe, 0xff, 0xff,\n", + " 0x04, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x21, 0xa5, 0x8b, 0xca,\n", + " 0x5e, 0x1d, 0xce, 0x42, 0x9d, 0xce, 0x1f, 0xb0, 0xdf, 0x54, 0x2f, 0x81,\n", + " 0x3e, 0xfe, 0xff, 0xff, 0x04, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00,\n", + " 0xee, 0xfc, 0x00, 0xec, 0x05, 0x17, 0xef, 0xec, 0xe6, 0xf8, 0x03, 0x01,\n", + " 0x00, 0xfa, 0xf8, 0xf5, 0xdc, 0xeb, 0x27, 0x14, 0xf1, 0xde, 0xe2, 0xdb,\n", + " 0xf0, 0xde, 0x31, 0x06, 0x02, 0xe6, 0xee, 0xf9, 0x00, 0x16, 0x07, 0xe0,\n", + " 0xfe, 0xff, 0xe9, 0x06, 0xe7, 0xef, 0x81, 0x1b, 0x18, 0xea, 0xc9, 0x01,\n", + " 0x0f, 0x00, 0xda, 0xf7, 0x0e, 0xec, 0x13, 0x1f, 0x04, 0x13, 0xb4, 0xe6,\n", + " 0xfd, 0x06, 0xb9, 0xe0, 0x0d, 0xec, 0xf0, 0xde, 0xeb, 0xf7, 0x05, 0x26,\n", + " 0x1a, 0xe4, 0x6f, 0x1a, 0xea, 0x1e, 0x35, 0xdf, 0x1a, 0xf3, 0xf1, 0x19,\n", + " 0x0f, 0x03, 0x1b, 0xe1, 0xde, 0x13, 0xf6, 0x19, 0xff, 0xf6, 0x1b, 0x18,\n", + " 0xf0, 0x1c, 0xda, 0x1b, 0x1b, 0x20, 0xe5, 0x1a, 0xf5, 0xff, 0x96, 0x0b,\n", + " 0x00, 0x01, 0xcd, 0xde, 0x0d, 0xf6, 0x16, 0xe3, 0xed, 0xfc, 0x0e, 0xe9,\n", + " 0xfa, 0xeb, 0x5c, 0xfc, 0x1d, 0x02, 0x5b, 0xe2, 0xe1, 0xf5, 0x15, 0xec,\n", + " 0xf4, 0x00, 0x13, 0x05, 0xec, 0x0c, 0x1d, 0x14, 0x0e, 0xe7, 0x0b, 0xf4,\n", + " 0x19, 0x00, 0xd7, 0x05, 0x27, 0x02, 0x15, 0xea, 0xea, 0x02, 0x9b, 0x00,\n", + " 0x0c, 0xfa, 0xe8, 0xea, 0xfd, 0x00, 0x14, 0xfd, 0x0b, 0x02, 0xef, 0xee,\n", + " 0x06, 0xee, 0x01, 0x0d, 0x06, 0xe6, 0xf7, 0x11, 0xf7, 0x09, 0xf8, 0xf1,\n", + " 0x21, 0xff, 0x0e, 0xf3, 0xec, 0x12, 0x26, 0x1d, 0xf2, 0xe9, 0x28, 0x18,\n", + " 0xe0, 0xfb, 0xf3, 0xf4, 0x05, 0x1d, 0x1d, 0xfb, 0xfd, 0x1e, 0xfc, 0x11,\n", + " 0xe8, 0x07, 0x09, 0x03, 0x12, 0xf2, 0x36, 0xfb, 0xdc, 0x1c, 0xf9, 0xef,\n", + " 0xf3, 0xe7, 0x6f, 0x0c, 0x1d, 0x00, 0x45, 0xfd, 0x0e, 0xf0, 0x0b, 0x19,\n", + " 0x1a, 0xfa, 0xe0, 0x19, 0x1f, 0x13, 0x36, 0x1c, 0x12, 0xeb, 0x3b, 0x0c,\n", + " 0xb4, 0xcb, 0xe6, 0x13, 0xfa, 0xeb, 0xf1, 0x06, 0x1c, 0xfa, 0x18, 0xe5,\n", + " 0xeb, 0xcb, 0x0c, 0xf4, 0x4a, 0xff, 0xff, 0xff, 0x04, 0x00, 0x00, 0x00,\n", + " 0x10, 0x00, 0x00, 0x00, 0x75, 0x1c, 0x11, 0xe1, 0x0c, 0x81, 0xa5, 0x42,\n", + " 0xfe, 0xd5, 0xd4, 0xb2, 0x61, 0x78, 0x19, 0xdf, 0x66, 0xff, 0xff, 0xff,\n", + " 0x04, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00,\n", + " 0x77, 0x0b, 0x00, 0x00, 0x53, 0xf6, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00,\n", + " 0x77, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n", + " 0xd3, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n", + " 0x72, 0x21, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2f, 0x07, 0x00, 0x00,\n", + " 0x67, 0xf5, 0xff, 0xff, 0x34, 0xf0, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00,\n", + " 0xb2, 0xff, 0xff, 0xff, 0x04, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00,\n", + " 0x00, 0x00, 0x00, 0x00, 0xb5, 0x04, 0x00, 0x00, 0x78, 0x0a, 0x00, 0x00,\n", + " 0x2d, 0x06, 0x00, 0x00, 0x71, 0xf8, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00,\n", + " 0x9a, 0x0a, 0x00, 0x00, 0xfe, 0xf7, 0xff, 0xff, 0x0e, 0x05, 0x00, 0x00,\n", + " 0xd4, 0x09, 0x00, 0x00, 0x47, 0xfe, 0xff, 0xff, 0xb6, 0x04, 0x00, 0x00,\n", + " 0x00, 0x00, 0x00, 0x00, 0xac, 0xf7, 0xff, 0xff, 0x4b, 0xf9, 0xff, 0xff,\n", + " 0x4a, 0x05, 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, 0x08, 0x00, 0x04, 0x00,\n", + " 0x06, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00,\n", + " 0x8c, 0xef, 0xff, 0xff, 0x84, 0xff, 0xff, 0xff, 0x88, 0xff, 0xff, 0xff,\n", + " 0x0f, 0x00, 0x00, 0x00, 0x4d, 0x4c, 0x49, 0x52, 0x20, 0x43, 0x6f, 0x6e,\n", + " 0x76, 0x65, 0x72, 0x74, 0x65, 0x64, 0x2e, 0x00, 0x01, 0x00, 0x00, 0x00,\n", + " 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x18, 0x00, 0x14, 0x00,\n", + " 0x10, 0x00, 0x0c, 0x00, 0x08, 0x00, 0x04, 0x00, 0x0e, 0x00, 0x00, 0x00,\n", + " 0x14, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x00, 0xdc, 0x00, 0x00, 0x00,\n", + " 0xe0, 0x00, 0x00, 0x00, 0xe4, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00,\n", + " 0x6d, 0x61, 0x69, 0x6e, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00,\n", + " 0x84, 0x00, 0x00, 0x00, 0x3c, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00,\n", + " 0x96, 0xff, 0xff, 0xff, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08,\n", + " 0x10, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x04, 0x00, 0x04, 0x00,\n", + " 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00,\n", + " 0x03, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00,\n", + " 0x01, 0x00, 0x00, 0x00, 0xca, 0xff, 0xff, 0xff, 0x10, 0x00, 0x00, 0x00,\n", + " 0x00, 0x00, 0x00, 0x08, 0x10, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00,\n", + " 0xba, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00,\n", + " 0x08, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00,\n", + " 0x05, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0e, 0x00,\n", + " 0x16, 0x00, 0x00, 0x00, 0x10, 0x00, 0x0c, 0x00, 0x0b, 0x00, 0x04, 0x00,\n", + " 0x0e, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08,\n", + " 0x18, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x00,\n", + " 0x08, 0x00, 0x07, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,\n", + " 0x01, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00,\n", + " 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00,\n", + " 0x01, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,\n", + " 0x00, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x4c, 0x04, 0x00, 0x00,\n", + " 0xd0, 0x03, 0x00, 0x00, 0x68, 0x03, 0x00, 0x00, 0x0c, 0x03, 0x00, 0x00,\n", + " 0x98, 0x02, 0x00, 0x00, 0x24, 0x02, 0x00, 0x00, 0xb0, 0x01, 0x00, 0x00,\n", + " 0x24, 0x01, 0x00, 0x00, 0x98, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00,\n", + " 0xf0, 0xfb, 0xff, 0xff, 0x18, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00,\n", + " 0x54, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x09,\n", + " 0x6c, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff,\n", + " 0x01, 0x00, 0x00, 0x00, 0xdc, 0xfb, 0xff, 0xff, 0x10, 0x00, 0x00, 0x00,\n", + " 0x18, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00,\n", + " 0x01, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n", + " 0x01, 0x00, 0x00, 0x00, 0x4a, 0xce, 0x0a, 0x3c, 0x01, 0x00, 0x00, 0x00,\n", + " 0x34, 0x84, 0x85, 0x3f, 0x01, 0x00, 0x00, 0x00, 0xc5, 0x02, 0x8f, 0xbf,\n", + " 0x1e, 0x00, 0x00, 0x00, 0x53, 0x74, 0x61, 0x74, 0x65, 0x66, 0x75, 0x6c,\n", + " 0x50, 0x61, 0x72, 0x74, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x65, 0x64, 0x43,\n", + " 0x61, 0x6c, 0x6c, 0x3a, 0x30, 0x5f, 0x69, 0x6e, 0x74, 0x38, 0x00, 0x00,\n", + " 0x02, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,\n", + " 0x80, 0xfc, 0xff, 0xff, 0x18, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00,\n", + " 0x54, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x09,\n", + " 0x64, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff,\n", + " 0x10, 0x00, 0x00, 0x00, 0x6c, 0xfc, 0xff, 0xff, 0x10, 0x00, 0x00, 0x00,\n", + " 0x18, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00,\n", + " 0x01, 0x00, 0x00, 0x00, 0x80, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,\n", + " 0x01, 0x00, 0x00, 0x00, 0x93, 0xd0, 0xc0, 0x3b, 0x01, 0x00, 0x00, 0x00,\n", + " 0xc2, 0x0f, 0xc0, 0x3f, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n", + " 0x14, 0x00, 0x00, 0x00, 0x74, 0x66, 0x6c, 0x2e, 0x66, 0x75, 0x6c, 0x6c,\n", + " 0x79, 0x5f, 0x63, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x65, 0x64, 0x31,\n", + " 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,\n", + " 0x10, 0x00, 0x00, 0x00, 0x08, 0xfd, 0xff, 0xff, 0x18, 0x00, 0x00, 0x00,\n", + " 0x20, 0x00, 0x00, 0x00, 0x58, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00,\n", + " 0x00, 0x00, 0x00, 0x09, 0x64, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00,\n", + " 0xff, 0xff, 0xff, 0xff, 0x10, 0x00, 0x00, 0x00, 0xf4, 0xfc, 0xff, 0xff,\n", + " 0x10, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00,\n", + " 0x24, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x80, 0xff, 0xff, 0xff,\n", + " 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,\n", + " 0xe0, 0xdb, 0x47, 0x3c, 0x01, 0x00, 0x00, 0x00, 0x04, 0x14, 0x47, 0x40,\n", + " 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x13, 0x00, 0x00, 0x00,\n", + " 0x74, 0x66, 0x6c, 0x2e, 0x66, 0x75, 0x6c, 0x6c, 0x79, 0x5f, 0x63, 0x6f,\n", + " 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x65, 0x64, 0x00, 0x02, 0x00, 0x00, 0x00,\n", + " 0x01, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x02, 0xfe, 0xff, 0xff,\n", + " 0x14, 0x00, 0x00, 0x00, 0x48, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00,\n", + " 0x00, 0x00, 0x00, 0x09, 0x50, 0x00, 0x00, 0x00, 0x6c, 0xfd, 0xff, 0xff,\n", + " 0x10, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x00,\n", + " 0x20, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n", + " 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0xfb, 0x4b, 0x0b, 0x3c,\n", + " 0x01, 0x00, 0x00, 0x00, 0x40, 0x84, 0x4b, 0x3f, 0x01, 0x00, 0x00, 0x00,\n", + " 0x63, 0x35, 0x8a, 0xbf, 0x0d, 0x00, 0x00, 0x00, 0x73, 0x74, 0x64, 0x2e,\n", + " 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x74, 0x32, 0x00, 0x00, 0x00,\n", + " 0x02, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00,\n", + " 0x72, 0xfe, 0xff, 0xff, 0x14, 0x00, 0x00, 0x00, 0x48, 0x00, 0x00, 0x00,\n", + " 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x09, 0x50, 0x00, 0x00, 0x00,\n", + " 0xdc, 0xfd, 0xff, 0xff, 0x10, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00,\n", + " 0x1c, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,\n", + " 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,\n", + " 0x60, 0x01, 0x4f, 0x3c, 0x01, 0x00, 0x00, 0x00, 0x47, 0x6d, 0xb3, 0x3f,\n", + " 0x01, 0x00, 0x00, 0x00, 0x5d, 0x63, 0xcd, 0xbf, 0x0d, 0x00, 0x00, 0x00,\n", + " 0x73, 0x74, 0x64, 0x2e, 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x74,\n", + " 0x31, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00,\n", + " 0x10, 0x00, 0x00, 0x00, 0xe2, 0xfe, 0xff, 0xff, 0x14, 0x00, 0x00, 0x00,\n", + " 0x48, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x09,\n", + " 0x50, 0x00, 0x00, 0x00, 0x4c, 0xfe, 0xff, 0xff, 0x10, 0x00, 0x00, 0x00,\n", + " 0x18, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00,\n", + " 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n", + " 0x01, 0x00, 0x00, 0x00, 0xd5, 0x6b, 0x8a, 0x3b, 0x01, 0x00, 0x00, 0x00,\n", + " 0xab, 0x49, 0x01, 0x3f, 0x01, 0x00, 0x00, 0x00, 0xfd, 0x56, 0x09, 0xbf,\n", + " 0x0c, 0x00, 0x00, 0x00, 0x73, 0x74, 0x64, 0x2e, 0x63, 0x6f, 0x6e, 0x73,\n", + " 0x74, 0x61, 0x6e, 0x74, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00,\n", + " 0x10, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x52, 0xff, 0xff, 0xff,\n", + " 0x14, 0x00, 0x00, 0x00, 0x34, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00,\n", + " 0x00, 0x00, 0x00, 0x02, 0x3c, 0x00, 0x00, 0x00, 0x44, 0xff, 0xff, 0xff,\n", + " 0x08, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,\n", + " 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n", + " 0x01, 0x00, 0x00, 0x00, 0x28, 0xb3, 0xd9, 0x38, 0x0c, 0x00, 0x00, 0x00,\n", + " 0x64, 0x65, 0x6e, 0x73, 0x65, 0x5f, 0x32, 0x2f, 0x62, 0x69, 0x61, 0x73,\n", + " 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00,\n", + " 0xaa, 0xff, 0xff, 0xff, 0x14, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00,\n", + " 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x38, 0x00, 0x00, 0x00,\n", + " 0x9c, 0xff, 0xff, 0xff, 0x08, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00,\n", + " 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n", + " 0x01, 0x00, 0x00, 0x00, 0xdd, 0x9b, 0x21, 0x39, 0x0c, 0x00, 0x00, 0x00,\n", + " 0x64, 0x65, 0x6e, 0x73, 0x65, 0x5f, 0x33, 0x2f, 0x62, 0x69, 0x61, 0x73,\n", + " 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00,\n", + " 0x00, 0x00, 0x0e, 0x00, 0x18, 0x00, 0x14, 0x00, 0x13, 0x00, 0x0c, 0x00,\n", + " 0x08, 0x00, 0x04, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00,\n", + " 0x40, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02,\n", + " 0x48, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00,\n", + " 0x08, 0x00, 0x04, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00,\n", + " 0x14, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n", + " 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,\n", + " 0xf4, 0xd4, 0x51, 0x38, 0x0c, 0x00, 0x00, 0x00, 0x64, 0x65, 0x6e, 0x73,\n", + " 0x65, 0x5f, 0x34, 0x2f, 0x62, 0x69, 0x61, 0x73, 0x00, 0x00, 0x00, 0x00,\n", + " 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x14, 0x00, 0x1c, 0x00,\n", + " 0x18, 0x00, 0x17, 0x00, 0x10, 0x00, 0x0c, 0x00, 0x08, 0x00, 0x00, 0x00,\n", + " 0x00, 0x00, 0x04, 0x00, 0x14, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00,\n", + " 0x2c, 0x00, 0x00, 0x00, 0x64, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,\n", + " 0x00, 0x00, 0x00, 0x09, 0x84, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00,\n", + " 0xff, 0xff, 0xff, 0xff, 0x01, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x14, 0x00,\n", + " 0x10, 0x00, 0x0c, 0x00, 0x08, 0x00, 0x04, 0x00, 0x0c, 0x00, 0x00, 0x00,\n", + " 0x10, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00,\n", + " 0x24, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x80, 0xff, 0xff, 0xff,\n", + " 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,\n", + " 0x5d, 0x4f, 0xc9, 0x3c, 0x01, 0x00, 0x00, 0x00, 0x0e, 0x86, 0xc8, 0x40,\n", + " 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x24, 0x00, 0x00, 0x00,\n", + " 0x73, 0x65, 0x72, 0x76, 0x69, 0x6e, 0x67, 0x5f, 0x64, 0x65, 0x66, 0x61,\n", + " 0x75, 0x6c, 0x74, 0x5f, 0x64, 0x65, 0x6e, 0x73, 0x65, 0x5f, 0x32, 0x5f,\n", + " 0x69, 0x6e, 0x70, 0x75, 0x74, 0x3a, 0x30, 0x5f, 0x69, 0x6e, 0x74, 0x38,\n", + " 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,\n", + " 0x01, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00,\n", + " 0x24, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0xd8, 0xff, 0xff, 0xff,\n", + " 0x06, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06,\n", + " 0x0c, 0x00, 0x0c, 0x00, 0x0b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00,\n", + " 0x0c, 0x00, 0x00, 0x00, 0x72, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x72,\n", + " 0x0c, 0x00, 0x10, 0x00, 0x0f, 0x00, 0x00, 0x00, 0x08, 0x00, 0x04, 0x00,\n", + " 0x0c, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00,\n", + " 0x00, 0x00, 0x00, 0x09\n", + "};\n", + "unsigned int g_model_len = 2488;\n" + ], + "name": "stdout" + } + ] + } + ] +} \ No newline at end of file diff --git a/docs/tensorflow/train_micro_speech_model.ipynb b/docs/tensorflow/train_micro_speech_model.ipynb new file mode 100644 index 0000000000..bdaf4539cf --- /dev/null +++ b/docs/tensorflow/train_micro_speech_model.ipynb @@ -0,0 +1,554 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": { + "id": "pO4-CY_TCZZS" + }, + "source": [ + "# Train a Simple Audio Recognition Model" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "BaFfr7DHRmGF" + }, + "source": [ + "*This* notebook demonstrates how to train a 20 kB [Simple Audio Recognition](https://www.tensorflow.org/tutorials/sequences/audio_recognition) model to recognize keywords in speech.\n", + "\n", + "The model created in this notebook is used in the [micro_speech](https://github.com/tensorflow/tflite-micro/blob/main/tensorflow/lite/micro/examples/micro_speech) example for [TensorFlow Lite for MicroControllers](https://www.tensorflow.org/lite/microcontrollers/overview).\n", + "\n", + "\n", + " \n", + " \n", + "
\n", + " Run in Google Colab\n", + " \n", + " View source on GitHub\n", + "
\n" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "XaVtYN4nlCft" + }, + "source": [ + "**Training is much faster using GPU acceleration.** Before you proceed, ensure you are using a GPU runtime by going to **Runtime -> Change runtime type** and set **Hardware accelerator: GPU**. Training 15,000 iterations will take 1.5 - 2 hours on a GPU runtime.\n", + "\n", + "## Configure Defaults\n", + "\n", + "**MODIFY** the following constants for your specific use case." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "ludfxbNIaegy" + }, + "outputs": [], + "source": [ + "# A comma-delimited list of the words you want to train for.\n", + "# The options are: yes,no,up,down,left,right,on,off,stop,go\n", + "# All the other words will be used to train an \"unknown\" label and silent\n", + "# audio data with no spoken words will be used to train a \"silence\" label.\n", + "WANTED_WORDS = \"yes,no\"\n", + "\n", + "# The number of steps and learning rates can be specified as comma-separated\n", + "# lists to define the rate at each stage. For example,\n", + "# TRAINING_STEPS=12000,3000 and LEARNING_RATE=0.001,0.0001\n", + "# will run 12,000 training loops in total, with a rate of 0.001 for the first\n", + "# 8,000, and 0.0001 for the final 3,000.\n", + "TRAINING_STEPS = \"12000,3000\"\n", + "LEARNING_RATE = \"0.001,0.0001\"\n", + "\n", + "# Calculate the total number of steps, which is used to identify the checkpoint\n", + "# file name.\n", + "TOTAL_STEPS = str(sum(map(lambda string: int(string), TRAINING_STEPS.split(\",\"))))\n", + "\n", + "# Print the configuration to confirm it\n", + "print(\"Training these words: %s\" % WANTED_WORDS)\n", + "print(\"Training steps in each stage: %s\" % TRAINING_STEPS)\n", + "print(\"Learning rate in each stage: %s\" % LEARNING_RATE)\n", + "print(\"Total number of training steps: %s\" % TOTAL_STEPS)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "gCgeOpvY9pAi" + }, + "source": [ + "**DO NOT MODIFY** the following constants as they include filepaths used in this notebook and data that is shared during training and inference." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "Nd1iM1o2ymvA" + }, + "outputs": [], + "source": [ + "import os\n", + "\n", + "# Calculate the percentage of 'silence' and 'unknown' training samples required\n", + "# to ensure that we have equal number of samples for each label.\n", + "number_of_labels = WANTED_WORDS.count(',') + 1\n", + "number_of_total_labels = number_of_labels + 2 # for 'silence' and 'unknown' label\n", + "equal_percentage_of_training_samples = int(100.0/(number_of_total_labels))\n", + "SILENT_PERCENTAGE = equal_percentage_of_training_samples\n", + "UNKNOWN_PERCENTAGE = equal_percentage_of_training_samples\n", + "\n", + "# Constants which are shared during training and inference\n", + "PREPROCESS = 'micro'\n", + "WINDOW_STRIDE = 20\n", + "MODEL_ARCHITECTURE = 'tiny_conv' # Other options include: single_fc, conv,\n", + " # low_latency_conv, low_latency_svdf, tiny_embedding_conv\n", + "\n", + "# Constants used during training only\n", + "VERBOSITY = 'WARN'\n", + "EVAL_STEP_INTERVAL = '1000'\n", + "SAVE_STEP_INTERVAL = '1000'\n", + "\n", + "# Constants for training directories and filepaths\n", + "DATASET_DIR = 'dataset/'\n", + "LOGS_DIR = 'logs/'\n", + "TRAIN_DIR = 'train/' # for training checkpoints and other files.\n", + "\n", + "# Constants for inference directories and filepaths\n", + "MODELS_DIR = 'models'\n", + "MODEL_TF = os.path.join(MODELS_DIR, 'model.pb')\n", + "MODEL_TFLITE = os.path.join(MODELS_DIR, 'model.tflite')\n", + "FLOAT_MODEL_TFLITE = os.path.join(MODELS_DIR, 'float_model.tflite')\n", + "MODEL_TFLITE_MICRO = os.path.join(MODELS_DIR, 'model.cc')\n", + "SAVED_MODEL = os.path.join(MODELS_DIR, 'saved_model')\n", + "\n", + "QUANT_INPUT_MIN = 0.0\n", + "QUANT_INPUT_MAX = 26.0\n", + "QUANT_INPUT_RANGE = QUANT_INPUT_MAX - QUANT_INPUT_MIN\n", + "\n", + "if not os.path.exists(MODELS_DIR):\n", + " os.mkdir(MODELS_DIR)\n", + "if not os.path.exists(LOGS_DIR):\n", + " os.mkdir(LOGS_DIR)\n" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "6rLYpvtg9P4o" + }, + "source": [ + "## Setup Environment\n", + "\n", + "Install Dependencies" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "ed_XpUrU5DvY" + }, + "outputs": [], + "source": [ + "# %tensorflow_version 1.x\n", + "import tensorflow as tf" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "T9Ty5mR58E4i" + }, + "source": [ + "**DELETE** any old data from previous runs\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "APGx0fEh7hFF" + }, + "outputs": [], + "source": [ + "!rm -rf {DATASET_DIR} {LOGS_DIR} {TRAIN_DIR} {MODELS_DIR}" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "GfEUlfFBizio" + }, + "source": [ + "Clone the TensorFlow Github Repository, which contains the relevant code required to run this tutorial." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "yZArmzT85SLq" + }, + "outputs": [], + "source": [ + "!git clone -q --depth 1 https://github.com/tensorflow/tensorflow" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "nS9swHLSi7Bi" + }, + "source": [ + "Load TensorBoard to visualize the accuracy and loss as training proceeds.\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "q4qF1VxP3UE4" + }, + "outputs": [], + "source": [ + "%load_ext tensorboard\n", + "%tensorboard --logdir {LOGS_DIR} --bind_all --port=6006\n" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "x1J96Ron-O4R" + }, + "source": [ + "## Training\n", + "\n", + "The following script downloads the dataset and begin training." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "VJsEZx6lynbY" + }, + "outputs": [], + "source": [ + "!python tensorflow/tensorflow/examples/speech_commands/train.py \\\n", + "--data_dir={DATASET_DIR} \\\n", + "--wanted_words={WANTED_WORDS} \\\n", + "--silence_percentage={SILENT_PERCENTAGE} \\\n", + "--unknown_percentage={UNKNOWN_PERCENTAGE} \\\n", + "--preprocess={PREPROCESS} \\\n", + "--window_stride={WINDOW_STRIDE} \\\n", + "--model_architecture={MODEL_ARCHITECTURE} \\\n", + "--how_many_training_steps={TRAINING_STEPS} \\\n", + "--learning_rate={LEARNING_RATE} \\\n", + "--train_dir={TRAIN_DIR} \\\n", + "--summaries_dir={LOGS_DIR} \\\n", + "--verbosity={VERBOSITY} \\\n", + "--eval_step_interval={EVAL_STEP_INTERVAL} \\\n", + "--save_step_interval={SAVE_STEP_INTERVAL}" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "UczQKtqLi7OJ" + }, + "source": [ + "# Skipping the training\n", + "\n", + "If you don't want to spend an hour or two training the model from scratch, you can download pretrained checkpoints by uncommenting the lines below (removing the '#'s at the start of each line) and running them." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "RZw3VNlnla-J" + }, + "outputs": [], + "source": [ + "#!curl -O \"https://storage.googleapis.com/download.tensorflow.org/models/tflite/speech_micro_train_2020_05_10.tgz/"\n", + "#!tar xzf speech_micro_train_2020_05_10.tgz" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "XQUJLrdS-ftl" + }, + "source": [ + "## Generate a TensorFlow Model for Inference\n", + "\n", + "Combine relevant training results (graph, weights, etc) into a single file for inference. This process is known as freezing a model and the resulting model is known as a frozen model/graph, as it cannot be further re-trained after this process." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "xyc3_eLh9sAg" + }, + "outputs": [], + "source": [ + "!rm -rf {SAVED_MODEL}\n", + "!python tensorflow/tensorflow/examples/speech_commands/freeze.py \\\n", + "--wanted_words=$WANTED_WORDS \\\n", + "--window_stride_ms=$WINDOW_STRIDE \\\n", + "--preprocess=$PREPROCESS \\\n", + "--model_architecture=$MODEL_ARCHITECTURE \\\n", + "--start_checkpoint=$TRAIN_DIR$MODEL_ARCHITECTURE'.ckpt-'{TOTAL_STEPS} \\\n", + "--save_format=saved_model \\\n", + "--output_file={SAVED_MODEL}" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "_DBGDxVI-nKG" + }, + "source": [ + "## Generate a TensorFlow Lite Model\n", + "\n", + "Convert the frozen graph into a TensorFlow Lite model, which is fully quantized for use with embedded devices.\n", + "\n", + "The following cell will also print the model size, which will be under 20 kilobytes." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "RIitkqvGWmre" + }, + "outputs": [], + "source": [ + "import sys\n", + "# We add this path so we can import the speech processing modules.\n", + "sys.path.append(\"/content/tensorflow/tensorflow/examples/speech_commands/\")\n", + "import input_data\n", + "import models\n", + "import numpy as np" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "kzqECqMxgBh4" + }, + "outputs": [], + "source": [ + "SAMPLE_RATE = 16000\n", + "CLIP_DURATION_MS = 1000\n", + "WINDOW_SIZE_MS = 30.0\n", + "FEATURE_BIN_COUNT = 40\n", + "BACKGROUND_FREQUENCY = 0.8\n", + "BACKGROUND_VOLUME_RANGE = 0.1\n", + "TIME_SHIFT_MS = 100.0\n", + "\n", + "DATA_URL = 'https://storage.googleapis.com/download.tensorflow.org/data/speech_commands_v0.02.tar.gz'\n", + "VALIDATION_PERCENTAGE = 10\n", + "TESTING_PERCENTAGE = 10" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "rNQdAplJV1fz" + }, + "outputs": [], + "source": [ + "model_settings = models.prepare_model_settings(\n", + " len(input_data.prepare_words_list(WANTED_WORDS.split(','))),\n", + " SAMPLE_RATE, CLIP_DURATION_MS, WINDOW_SIZE_MS,\n", + " WINDOW_STRIDE, FEATURE_BIN_COUNT, PREPROCESS)\n", + "audio_processor = input_data.AudioProcessor(\n", + " DATA_URL, DATASET_DIR,\n", + " SILENT_PERCENTAGE, UNKNOWN_PERCENTAGE,\n", + " WANTED_WORDS.split(','), VALIDATION_PERCENTAGE,\n", + " TESTING_PERCENTAGE, model_settings, LOGS_DIR)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "lBj_AyCh1cC0" + }, + "outputs": [], + "source": [ + "with tf.compat.v1.Session() as sess:\n", + " float_converter = tf.lite.TFLiteConverter.from_saved_model(SAVED_MODEL)\n", + " float_tflite_model = float_converter.convert()\n", + " float_tflite_model_size = open(FLOAT_MODEL_TFLITE, \"wb\").write(float_tflite_model)\n", + " print(\"Float model is %d bytes\" % float_tflite_model_size)\n", + "\n", + " converter = tf.lite.TFLiteConverter.from_saved_model(SAVED_MODEL)\n", + " converter.optimizations = [tf.lite.Optimize.DEFAULT]\n", + " converter.inference_input_type = tf.compat.v1.lite.constants.INT8\n", + " converter.inference_output_type = tf.compat.v1.lite.constants.INT8\n", + " def representative_dataset_gen():\n", + " for i in range(100):\n", + " data, _ = audio_processor.get_data(1, i*1, model_settings,\n", + " BACKGROUND_FREQUENCY, \n", + " BACKGROUND_VOLUME_RANGE,\n", + " TIME_SHIFT_MS,\n", + " 'testing',\n", + " sess)\n", + " flattened_data = np.array(data.flatten(), dtype=np.float32).reshape(1, 1960)\n", + " yield [flattened_data]\n", + " converter.representative_dataset = representative_dataset_gen\n", + " tflite_model = converter.convert()\n", + " tflite_model_size = open(MODEL_TFLITE, \"wb\").write(tflite_model)\n", + " print(\"Quantized model is %d bytes\" % tflite_model_size)\n" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "EeLiDZTbLkzv" + }, + "source": [ + "## Testing the TensorFlow Lite model's accuracy\n", + "\n", + "Verify that the model we've exported is still accurate, using the TF Lite Python API and our test set." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "wQsEteKRLryJ" + }, + "outputs": [], + "source": [ + "# Helper function to run inference\n", + "def run_tflite_inference(tflite_model_path, model_type=\"Float\"):\n", + " # Load test data\n", + " np.random.seed(0) # set random seed for reproducible test results.\n", + " with tf.compat.v1.Session() as sess:\n", + " test_data, test_labels = audio_processor.get_data(\n", + " -1, 0, model_settings, BACKGROUND_FREQUENCY, BACKGROUND_VOLUME_RANGE,\n", + " TIME_SHIFT_MS, 'testing', sess)\n", + " test_data = np.expand_dims(test_data, axis=1).astype(np.float32)\n", + "\n", + " # Initialize the interpreter\n", + " interpreter = tf.lite.Interpreter(tflite_model_path)\n", + " interpreter.allocate_tensors()\n", + "\n", + " input_details = interpreter.get_input_details()[0]\n", + " output_details = interpreter.get_output_details()[0]\n", + "\n", + " # For quantized models, manually quantize the input data from float to integer\n", + " if model_type == \"Quantized\":\n", + " input_scale, input_zero_point = input_details[\"quantization\"]\n", + " test_data = test_data / input_scale + input_zero_point\n", + " test_data = test_data.astype(input_details[\"dtype\"])\n", + "\n", + " correct_predictions = 0\n", + " for i in range(len(test_data)):\n", + " interpreter.set_tensor(input_details[\"index\"], test_data[i])\n", + " interpreter.invoke()\n", + " output = interpreter.get_tensor(output_details[\"index\"])[0]\n", + " top_prediction = output.argmax()\n", + " correct_predictions += (top_prediction == test_labels[i])\n", + "\n", + " print('%s model accuracy is %f%% (Number of test samples=%d)' % (\n", + " model_type, (correct_predictions * 100) / len(test_data), len(test_data)))" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "l-pD52Na6jRa" + }, + "outputs": [], + "source": [ + "# Compute float model accuracy\n", + "run_tflite_inference(FLOAT_MODEL_TFLITE)\n", + "\n", + "# Compute quantized model accuracy\n", + "run_tflite_inference(MODEL_TFLITE, model_type='Quantized')" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "dt6Zqbxu-wIi" + }, + "source": [ + "## Generate a TensorFlow Lite for MicroControllers Model\n", + "Convert the TensorFlow Lite model into a C source file that can be loaded by TensorFlow Lite for Microcontrollers." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "XohZOTjR8ZyE" + }, + "outputs": [], + "source": [ + "# Install xxd if it is not available\n", + "!apt-get update && apt-get -qq install xxd\n", + "# Convert to a C source file\n", + "!xxd -i {MODEL_TFLITE} > {MODEL_TFLITE_MICRO}\n", + "# Update variable names\n", + "REPLACE_TEXT = MODEL_TFLITE.replace('/', '_').replace('.', '_')\n", + "!sed -i 's/'{REPLACE_TEXT}'/g_model/g' {MODEL_TFLITE_MICRO}" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "2pQnN0i_-0L2" + }, + "source": [ + "## Deploy to a Microcontroller\n", + "\n", + "Follow the instructions in the [micro_speech](https://github.com/tensorflow/tflite-micro/blob/main/tensorflow/lite/micro/examples/micro_speech) README.md for [TensorFlow Lite for MicroControllers](https://www.tensorflow.org/lite/microcontrollers/overview) to deploy this model on a specific microcontroller.\n", + "\n", + "**Reference Model:** If you have not modified this notebook, you can follow the instructions as is, to deploy the model. Refer to the [`micro_speech/train/models`](https://github.com/tensorflow/tflite-micro/blob/main/tensorflow/lite/micro/examples/micro_speech/train/models) directory to access the models generated in this notebook.\n", + "\n", + "**New Model:** If you have generated a new model to identify different words: (i) Update `kCategoryCount` and `kCategoryLabels` in [`micro_speech/micro_features/micro_model_settings.h`](https://github.com/tensorflow/tflite-micro/blob/main/tensorflow/lite/micro/examples/micro_speech/micro_features/micro_model_settings.h) and (ii) Update the values assigned to the variables defined in [`micro_speech/micro_features/model.cc`](https://github.com/tensorflow/tflite-micro/blob/main/tensorflow/lite/micro/examples/micro_speech/micro_features/model.cc) with values displayed after running the following cell." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "eoYyh0VU8pca" + }, + "outputs": [], + "source": [ + "# Print the C source file\n", + "!cat {MODEL_TFLITE_MICRO}" + ] + } + ], + "metadata": { + "accelerator": "GPU", + "colab": { + "collapsed_sections": [], + "name": "Copy of train_micro_speech_model.ipynb", + "provenance": [], + "toc_visible": true + }, + "kernelspec": { + "display_name": "Python 3", + "name": "python3" + } + }, + "nbformat": 4, + "nbformat_minor": 0 +} diff --git a/examples/README_ESP32.md b/examples/README_ESP32.md deleted file mode 100644 index 610586343a..0000000000 --- a/examples/README_ESP32.md +++ /dev/null @@ -1,154 +0,0 @@ -# ESP32 Configuration - -If you are using ESP32 by Espressif Systems version 3.0.0 and later, audio tools will use the new adc_continuous API. - -For the adc_continuous API, audio tools provides the following options: - -- **sample_rate** (per channel), the effective ADC sampling rate is the number of channels x sample rate and can be: - - ESP32: 20kHz to 2MHz - - ESP32 S2, S3, H2, C6, C3: 611Hz to 83.333kHz - -for example: - - - 306 - - 4,000 - - 5,000 - - 8,000 - - 10,000 - - 11,025 - - 16,000 - - 20,000 - - 22,050 - - 40,000 - - 44,100 - - 48,000 - - 88,200 - - 96,000 - - 176,400 - - 192,200 - - 352,800 - - 384,000 - - 500,000 - - 1,000,000 - -- **adc_bit_with** - - 9, 10, 11, 12 depending on ESP32 model - - audio stream is int16_t -- **adc_calibration_active**: values measured are in mV -- **is_auto_center_read**: subtraction of current estimated average from samples -- **adc_attenuation**: - - | attenuation | range | accurate range | - | ------------ | --------| -------------- | - | ADC_ATTEN_DB_0 | 0..1.1V | 100-950mV | - | ADC_ATTEN_DB_2_5| 0..1.5V | 100-1250mV | - | ADC_ATTEN_DB_6 | 0..2.2V | 150-1750mV | - | ADC_ATTEN_DB_12 | 0..3.9V | 150-2450mV | - -- **channels**: - - mono = 1 - - stereo = 2 -- **adc_channels**: defining the channels (only channels on ADC unit 1 are supported) e.g.: - - A3 on Sparkfun ESP32 Thing Plus is ADC_CHANNEL_3 - - A4 on Sparkfun ESP32 Thing Plus is ADC_CHANNEL_0 - - D5 on Adafruit ESP32-S3 is ADC_CHANNEL_4 - - D6 on Adafruit ESP32-S3 is ADC_CHANNEL_5 - -- **buffer_size** - - maximum is 2048 - - minimum is number of channels - - number needs to be devisible by number of channels - - care must be taken because some streams in audio tools can not exceed 1024 bytes - -## Example Configuration -``` -auto adcConfig = adc.defaultConfig(RX_MODE); -adcConfig.sample_rate = 44100; -adcConfig.adc_bit_width = 12; -adcConfig.adc_calibration_active = true; -adcConfig.is_auto_center_read = false; -adcConfig.adc_attenuation = ADC_ATTEN_DB_12; -adcConfig.channels = 2; -adcConfig.adc_channels[0] = ADC_CHANNEL_4; -adcConfig.adc_channels[1] = ADC_CHANNEL_5; -``` - -## ADC unit 1 channels on common ESP32 boards -Audio tools continous ADC framewaork supports ADC Unit 1 only. - -### Sparkfun ESP32 Thing Plus (ESP32) -- A2, ADC1_CH6 -- A3, ADC1_CH3 -- A4, ADC1_CH0 -- 32, ADC1_CH4 -- 33, ADC1_CH5 - -### Sparkfun ESP32 Thing Plus C (ESP32) -- A2, ADC1_CH6 -- A3, ADC1_CH3 -- A4, ADC1_CH0 -- A5, ADC1_CH7 -- 32/6, ADC1_CH4 -- 33/10, ADC1_CH5 - -### Sparkfun ESP32 Qwiic Pocket Development (ESP32C6) -- 2, ADC1_CH2 -- 3, ADC1_CH3 -- 4, ADC1_CH4 -- 5, ADC1_CH5 - -### ESP32-C6-DevKit -- 4, ADC1_CH4 -- 5, ADC1_CH5 -- 6, ADC1_CH6 -- 7, ADC1_CH0 -- 0, ADC1_CH1 -- 2, ADC1_CH2 -- 3, ADC1_CH3 - -### ESP32-H2-DevKit -- 1, ADC1_CH0 -- 2, ADC1_CH1 -- 3, ADC1_CH2 -- 4, ADC1_CH3 -- 5, ADC1_CH4 - -### ESP32­-S3­-DevKit -- 1, ADC1_CH0 -- 2, ADC1_CH1 -- 4, ADC1_CH3 -- 5, ADC1_CH4 -- 6, ADC1_CH5 -- 7, ADC1_CH6 -- 8, ADC1_CH7 -- 3, ADC1_CH2 -- 9, ADC1_CH8 -- 10, ADC1_CH9 - -### ESP32-C3-DevKit -- 4, IO2, ADC1_CH2 -- 5, IO3, ADC1_CH3 -- 9, IO0, ADC1_CH0 -- 10, IO1, ADC1_CH1 -- 11, IO4, ADC1_CH4 - -### Adafruit ESP32 Feather V2 -- D32, ADC1_CH4 -- D33, ADC1_CH5 -- A2, ADC1_CH6 -- A3, ADC1_CH3 -- A4, ADC1_CH0 -- D37, ADC1_CH1 - -### Adafruit ESP32-S3 Feather -- D5, ADC1_CH4 -- D6, ADC1_CH5 -- D9, ADC1_CH8 -- D10, ADC1_CH9 -- A5, ADC1_CH7 - -### Adafruit QT Py ESP32-C3 -- A0, ADC1-CH4 -- A1, ADC1-CH3 -- A2, ADC1-CH1 -- A3, ADC1-CH0 diff --git a/examples/build-arch-log.txt b/examples/build-arch-log.txt index 06d6508da4..f44f0fd469 100644 --- a/examples/build-arch-log.txt +++ b/examples/build-arch-log.txt @@ -2,12 +2,8 @@ esp32:esp32:esp32 ./examples-stream/streams-generator-serial -> rc=0 esp32:esp32:esp32c3 ./examples-stream/streams-generator-serial -> rc=0 esp32:esp32:esp32s3 ./examples-stream/streams-generator-serial -> rc=0 esp32:esp32:esp32s2 ./examples-stream/streams-generator-serial -> rc=0 -esp32:esp32:esp32c6 ./examples-stream/streams-generator-serial -> rc=0 -esp32:esp32:esp32h2 ./examples-stream/streams-generator-serial -> rc=0 esp8266:esp8266:generic ./examples-stream/streams-generator-serial -> rc=0 +arduino:mbed_rp2040:pico ./examples-stream/streams-generator-serial -> rc=0 rp2040:rp2040:generic ./examples-stream/streams-generator-serial -> rc=0 arduino:avr:nano ./examples-stream/streams-generator-serial -> rc=0 arduino:samd:arduino_zero_native ./examples-stream/streams-generator-serial -> rc=0 -arduino:renesas_uno:unor4wifi ./examples-stream/streams-generator-serial -> rc=0 -arduino:mbed_nano:nano33ble ./examples-stream/streams-generator-serial -> rc=0 -arduino:mbed_rp2040:pico ./examples-stream/streams-generator-serial -> rc=0 diff --git a/examples/build-arch.sh b/examples/build-arch.sh index f6b8e46b24..6cc242f8db 100755 --- a/examples/build-arch.sh +++ b/examples/build-arch.sh @@ -22,16 +22,11 @@ compile_example "esp32:esp32:esp32" compile_example "esp32:esp32:esp32c3" compile_example "esp32:esp32:esp32s3" compile_example "esp32:esp32:esp32s2" -compile_example "esp32:esp32:esp32c6" -compile_example "esp32:esp32:esp32h2" compile_example "esp8266:esp8266:generic" +compile_example "arduino:mbed_rp2040:pico" compile_example "rp2040:rp2040:generic" compile_example "arduino:avr:nano" compile_example "arduino:samd:arduino_zero_native" -compile_example "arduino:renesas_uno:unor4wifi" -compile_example "arduino:mbed_nano:nano33ble" -compile_example "arduino:mbed_rp2040:pico" -#compile_example "arduino:zephyr:nano33ble" #compile_example "STMicroelectronics:stm32:GenF4" ./cleanup.sh diff --git a/examples/build-examples-log.txt b/examples/build-examples-log.txt index 0afdfbc579..a222c65f29 100644 --- a/examples/build-examples-log.txt +++ b/examples/build-examples-log.txt @@ -1,38 +1,46 @@ -../examples/examples-basic-api/base-adc-average-mono-serial -> rc=0 -../examples/examples-basic-api/base-adc-measure -> rc=0 +../examples/examples-basic-api/base-adc-a2dp -> rc=0 ../examples/examples-basic-api/base-adc-serial -> rc=0 ../examples/examples-basic-api/base-file_raw-serial -> rc=0 +../examples/examples-basic-api/base-generator-a2dp -> rc=0 +../examples/examples-basic-api/base-i2s-a2dp -> rc=0 +../examples/examples-basic-api/base-player-a2dp -> rc=0 ../examples/examples-basic-api/base-SynchronizedBufferRTOS -> rc=0 ../examples/examples-player/player-callback-i2s -> rc=0 ../examples/examples-player/player-littlefs-i2s -> rc=0 -../examples/examples-player/player-sd-audiokit -> rc=0 +../examples/examples-player/player-sdfat-a2dp -> rc=0 ../examples/examples-player/player-sdfat-analog -> rc=0 -../examples/examples-player/player-sdfat-audiokit -> rc=0 ../examples/examples-player/player-sdfat-ffti2s -> rc=0 ../examples/examples-player/player-sdfat-i2s -> rc=0 ../examples/examples-player/player-sd-i2s -> rc=0 -../examples/examples-player/player-sdmmc-audiokit -> rc=0 ../examples/examples-player/player-spiffs-i2s -> rc=0 +../examples/examples-player/player-url-i2s -> rc=0 +../examples/examples-player/player-url_icy-i2s -> rc=0 +../examples/examples-player/player-url_subclass-i2s -> rc=0 +../examples/examples-webserver/streams-effect-webserver_wav -> rc=0 +../examples/examples-webserver/streams-flite-webserver_wav -> rc=0 +../examples/examples-webserver/streams-generator-webserverex_wav -> rc=0 +../examples/examples-webserver/streams-generator-webserverex_wav1 -> rc=0 +../examples/examples-webserver/streams-generator-webserver_wav -> rc=0 +../examples/examples-webserver/streams-i2s-webserver_wav -> rc=0 +../examples/examples-webserver/streams-sam-webserver_wav -> rc=0 +../examples/examples-webserver/streams-tts-webserver_wav -> rc=0 +../examples/examples-stream/streams-a2dp-serial -> rc=0 ../examples/examples-stream/streams-adc-i2s -> rc=0 ../examples/examples-stream/streams-adc-serial -> rc=0 ../examples/examples-stream/streams-adsr-i2s -> rc=0 +../examples/examples-stream/streams-generator-a2dp -> rc=0 ../examples/examples-stream/streams-generator-analog -> rc=0 -../examples/examples-stream/streams-generator-bin-serial -> rc=0 ../examples/examples-stream/streams-generator-formatconverter-i2s -> rc=0 ../examples/examples-stream/streams-generator_fromarray-analog -> rc=0 ../examples/examples-stream/streams-generator-i2s -> rc=0 -../examples/examples-stream/streams-generator-merge-pwm -> rc=0 ../examples/examples-stream/streams-generator-pwm -> rc=0 -../examples/examples-stream/streams-generator-r2r -> rc=0 ../examples/examples-stream/streams-generator-serial -> rc=0 ../examples/examples-stream/streams-generator-spdif -> rc=0 -../examples/examples-stream/streams-generator-timedstream-serial -> rc=0 -../examples/examples-stream/streams-generator-volume -> rc=0 ../examples/examples-stream/streams-generator-wm8960 -> rc=0 +../examples/examples-stream/streams-i2s-a2dp -> rc=0 ../examples/examples-stream/streams-i2s-filter-i2s -> rc=0 ../examples/examples-stream/streams-i2s-i2s -> rc=0 ../examples/examples-stream/streams-i2s-i2s-2 -> rc=0 -../examples/examples-stream/streams-i2s_pdm-serial -> rc=0 ../examples/examples-stream/streams-i2s-serial -> rc=0 ../examples/examples-stream/streams-i2s-serial_16bit -> rc=0 ../examples/examples-stream/streams-i2s-tf -> rc=0 @@ -42,14 +50,34 @@ ../examples/examples-stream/streams-memory_mp3_short-i2s -> rc=0 ../examples/examples-stream/streams-memory_mp3_short-i2s-2 -> rc=0 ../examples/examples-stream/streams-memory_raw-i2s -> rc=0 -../examples/examples-stream/streams-memory_wav-pwm -> rc=0 +../examples/examples-stream/streams-memory_wav-pwm -> rc=1/0 ../examples/examples-stream/streams-memory_wav-serial -> rc=0 ../examples/examples-stream/streams-mp34dt05-serial -> rc=1 ../examples/examples-stream/streams-sdfat_mp3-metadata -> rc=0 ../examples/examples-stream/streams-sd_mp3-i2s -> rc=0 -../examples/examples-stream/streams-sd_wav4-i2s -> rc=0 ../examples/examples-stream/streams-tf-i2s -> rc=0 -../examples/examples-audiokit/README.md -> rc=1 +../examples/examples-stream/streams-url_aac-i2s -> rc=0 +../examples/examples-stream/streams-url-file -> rc=0 +../examples/examples-stream/streams-url_flac-i2s -> rc=0 +../examples/examples-stream/streams-url-measuring -> rc=0 +../examples/examples-stream/streams-url_mp3-analog -> rc=0 +../examples/examples-stream/streams-url_mp3_helix-i2s -> rc=0 +../examples/examples-stream/streams-url_mp3_mad-i2s -> rc=0 +../examples/examples-stream/streams-url_mp3-metadata -> rc=0 +../examples/examples-stream/streams-url_mp3-metadata2 -> rc=0 +../examples/examples-stream/streams-url_raw-i2s -> rc=0 +../examples/examples-stream/streams-url_raw-serial -> rc=0 +../examples/examples-stream/streams-url_vorbis_i2s -> rc=0 +../examples/examples-stream/streams-url_wav-i2s -> rc=1 +../examples/examples-audiokit/basic-a2dp-audiokit -> rc=0 +../examples/examples-audiokit/basic-a2dp-eq-audiokit -> rc=0 +../examples/examples-audiokit/basic-audiokit-a2dp -> rc=0 +../examples/examples-audiokit/player-sd_a2dp-audiokit -> rc=0 +../examples/examples-audiokit/player-sd-audiokit -> rc=0 +../examples/examples-audiokit/player-sdfat-audiokit -> rc=0 +../examples/examples-audiokit/player-sdmmc-audiokit -> rc=0 +../examples/examples-audiokit/player-url_icy-audiokit -> rc=0 +../examples/examples-audiokit/streams-a2dp-audiokit -> rc=0 ../examples/examples-audiokit/streams-audiokit-audiokit -> rc=0 ../examples/examples-audiokit/streams-audiokit-effects-audiokit -> rc=0 ../examples/examples-audiokit/streams-audiokit-fft -> rc=0 @@ -63,6 +91,7 @@ ../examples/examples-audiokit/streams-audiokit-sd_wav -> rc=0 ../examples/examples-audiokit/streams-audiokit-serial -> rc=0 ../examples/examples-audiokit/streams-audiokit-tf -> rc=0 +../examples/examples-audiokit/streams-audiokit-webserver_wav -> rc=0 ../examples/examples-audiokit/streams-file_loop-audiokit -> rc=0 ../examples/examples-audiokit/streams-generator-audiokit -> rc=0 ../examples/examples-audiokit/streams-generator_fromarray-audiokit -> rc=0 @@ -72,97 +101,54 @@ ../examples/examples-audiokit/streams-memory_mp3-audiokit -> rc=0 ../examples/examples-audiokit/streams-memory_pcm-mixer-audiokit -> rc=0 ../examples/examples-audiokit/streams-pins-audiokit -> rc=0 -../examples/examples-audiokit/streams-sd_flac-audiokit -> rc=0 -../examples/examples-audiokit/streams-sdmmc_wav-audiokit -> rc=0 -../examples/examples-audiokit/streams-sd_mp3-audiokit -> rc=0 +../examples/examples-audiokit/streams-sd-audiokit -> rc=0 +../examples/examples-audiokit/streams-synth-a2dp -> rc=0 ../examples/examples-audiokit/streams-synth-audiokit -> rc=0 ../examples/examples-audiokit/streams-synthbasic1-audiokit -> rc=0 ../examples/examples-audiokit/streams-synthbasic2-audiokit -> rc=0 ../examples/examples-audiokit/streams-synthbasic3-audiokit -> rc=0 ../examples/examples-audiokit/streams-synthstk-audiokit -> rc=0 ../examples/examples-audiokit/streams-tf-audiokit -> rc=0 -../examples/examples-tts/streams-azure_tts-i2s -> rc=0 +../examples/examples-audiokit/streams-url_aac-audiokit -> rc=0 +../examples/examples-audiokit/streams-url_mp3-audiokit -> rc=0 +../examples/examples-maximilian/01-TestTone -> rc=0 +../examples/examples-maximilian/02-TwoTones -> rc=0 +../examples/examples-maximilian/03-AM1 -> rc=0 +../examples/examples-maximilian/04-AM2 -> rc=0 +../examples/examples-maximilian/05-FM1 -> rc=0 +../examples/examples-maximilian/06-FM2 -> rc=0 +../examples/examples-maximilian/07-Counting1 -> rc=0 +../examples/examples-maximilian/08-Counting2 -> rc=0 +../examples/examples-maximilian/08-Counting3 -> rc=0 +../examples/examples-maximilian/08-Counting4 -> rc=0 +../examples/examples-maximilian/09-Envelopes -> rc=0 +../examples/examples-maximilian/10-Filters -> rc=0 +../examples/examples-maximilian/11-Mixing -> rc=0 +../examples/examples-maximilian/12-SamplePlayer -> rc=0 +../examples/examples-maximilian/13-AdvancedFilters -> rc=0 +../examples/examples-maximilian/14-MonoSynth -> rc=0 +../examples/examples-maximilian/15-PolySynth -> rc=0 +../examples/examples-maximilian/16-Replicant -> rc=0 +../examples/examples-maximilian/17-Compressor -> rc=0 +../examples/examples-maximilian/18-DrumMachine -> rc=0 +../examples/examples-maximilian/19-Enveloping2 -> rc=0 +../examples/examples-maximilian/20-FFT -> rc=0 +../examples/examples-tts/streams-azure_tts-i2s -> rc=1 ../examples/examples-tts/streams-espeak-audiokit -> rc=0 ../examples/examples-tts/streams-espeak-i2s -> rc=0 ../examples/examples-tts/streams-flite-audiokit -> rc=0 ../examples/examples-tts/streams-flite-i2s -> rc=0 -../examples/examples-tts/streams-google-audiokit -> rc=0 ../examples/examples-tts/streams-sam-audiokit -> rc=0 ../examples/examples-tts/streams-sam-i2s -> rc=0 -../examples/examples-tts/streams-simple_tts-a2dp -> rc=0 ../examples/examples-tts/streams-simple_tts-i2s -> rc=0 -../examples/examples-tts/streams-talkie-a2dp -> rc=0 -../examples/examples-tts/streams-talkie-audiokit -> rc=0 ../examples/examples-tts/streams-tts-i2s -> rc=0 -../examples/examples-tts/streams-url_wav-i2s -> rc=0 -../examples/examples-dsp/examples-maximilian/01-TestTone -> rc=0 -../examples/examples-dsp/examples-maximilian/02-TwoTones -> rc=0 -../examples/examples-dsp/examples-maximilian/03-AM1 -> rc=0 -../examples/examples-dsp/examples-maximilian/04-AM2 -> rc=0 -../examples/examples-dsp/examples-maximilian/05-FM1 -> rc=0 -../examples/examples-dsp/examples-maximilian/06-FM2 -> rc=0 -../examples/examples-dsp/examples-maximilian/07-Counting1 -> rc=0 -../examples/examples-dsp/examples-maximilian/08-Counting2 -> rc=0 -../examples/examples-dsp/examples-maximilian/08-Counting3 -> rc=0 -../examples/examples-dsp/examples-maximilian/08-Counting4 -> rc=0 -../examples/examples-dsp/examples-maximilian/09-Envelopes -> rc=0 -../examples/examples-dsp/examples-maximilian/10-Filters -> rc=0 -../examples/examples-dsp/examples-maximilian/11-Mixing -> rc=0 -../examples/examples-dsp/examples-maximilian/12-SamplePlayer -> rc=0 -../examples/examples-dsp/examples-maximilian/13-AdvancedFilters -> rc=0 -../examples/examples-dsp/examples-maximilian/14-MonoSynth -> rc=0 -../examples/examples-dsp/examples-maximilian/15-PolySynth -> rc=0 -../examples/examples-dsp/examples-maximilian/16-Replicant -> rc=0 -../examples/examples-dsp/examples-maximilian/17-Compressor -> rc=0 -../examples/examples-dsp/examples-maximilian/18-DrumMachine -> rc=0 -../examples/examples-dsp/examples-maximilian/19-Enveloping2 -> rc=0 -../examples/examples-dsp/examples-maximilian/20-FFT -> rc=0 -../examples/examples-dsp/examples-maximilian/README.md -> rc=1 -../examples/examples-dsp/examples-mozzi/audio_input -> rc=0 -../examples/examples-dsp/examples-mozzi/control_gain -> rc=0 -../examples/examples-dsp/examples-mozzi/control_gain-a2dp -> rc=0 -../examples/examples-dsp/examples-pd/README.md -> rc=1 -../examples/examples-dsp/examples-pd/streams-generator-pd-audiokit -> rc=1 -../examples/examples-dsp/examples-pd/streams-pd-audiokit -> rc=1 -../examples/examples-dsp/examples-stk/README.md -> rc=1 -../examples/examples-dsp/examples-stk/streams-stk_allinstruments-audiokit -> rc=0 -../examples/examples-dsp/examples-stk/streams-stk-audiokit -> rc=0 -../examples/examples-dsp/examples-stk/streams-stk-desktop -> rc=1 -../examples/examples-dsp/examples-stk/streams-stk_files-audiokit -> rc=0 -../examples/examples-dsp/examples-stk/streams-stk_generator-audiokit -> rc=0 -../examples/examples-dsp/examples-stk/streams-stk_loop-audiokit -> rc=0 -../examples/examples-dsp/examples-stk/streams-stk_myinstrument-audiokit -> rc=0 -../examples/examples-dsp/examples-stk/streams-stk_sine-audiokit -> rc=0 -../examples/examples-dsp/examples-stk/streams-stk_synth-audiokit -> rc=0 -../examples/examples-dsp/examples-faust/streams-faust_flute-i2s -> rc=0 -../examples/examples-dsp/examples-faust/streams-faust_noise-i2s -> rc=0 -../examples/examples-dsp/examples-faust/streams-generator-faust-i2s -> rc=0 -../examples/examples-dsp/examples-faust/streams-i2s-faust_copy-i2s -> rc=0 -../examples/examples-dsp/examples-faust/streams-i2s-faust_guitarix-i2s -> rc=0 -../examples/examples-dsp/examples-faust/streams-i2s-faust_pitchshift-i2s -> rc=0 -../examples/examples-communication/a2dp/basic-a2dp-audiokit -> rc=0 -../examples/examples-communication/a2dp/basic-a2dp-eq-audiokit -> rc=0 -../examples/examples-communication/a2dp/basic-a2dp-fft -> rc=0 -../examples/examples-communication/a2dp/basic-a2dp-fft-led -> rc=0 -../examples/examples-communication/a2dp/basic-a2dp-i2s -> rc=0 -../examples/examples-communication/a2dp/basic-a2dp-mixer-i2s -> rc=0 -../examples/examples-communication/a2dp/basic-adc-a2dp -> rc=0 -../examples/examples-communication/a2dp/basic-audiokit-a2dp -> rc=0 -../examples/examples-communication/a2dp/basic-file_mp3-a2dp -> rc=0 -../examples/examples-communication/a2dp/basic-generator-a2dp -> rc=0 -../examples/examples-communication/a2dp/basic-i2s-a2dp -> rc=0 -../examples/examples-communication/a2dp/basic-player-a2dp -> rc=0 -../examples/examples-communication/a2dp/player-sd_a2dp-audiokit -> rc=0 -../examples/examples-communication/a2dp/player-sdfat-a2dp -> rc=0 -../examples/examples-communication/a2dp/streams-a2dp-audiokit -> rc=0 -../examples/examples-communication/a2dp/streams-a2dp-serial -> rc=0 -../examples/examples-communication/a2dp/streams-a2dp-spdif -> rc=0 -../examples/examples-communication/a2dp/streams-generator-a2dp -> rc=0 -../examples/examples-communication/a2dp/streams-i2s-a2dp -> rc=0 -../examples/examples-communication/a2dp/streams-synth-a2dp -> rc=0 +../examples/examples-faust/streams-faust_flute-i2s -> rc=0 +../examples/examples-faust/streams-faust_noise-i2s -> rc=0 +../examples/examples-faust/streams-generator-faust-i2s -> rc=0 +../examples/examples-faust/streams-i2s-faust_guitarix-i2s -> rc=0 ../examples/examples-communication/esp-now/codec/communication-codec-espnow-receive -> rc=0 ../examples/examples-communication/esp-now/codec/communication-codec-espnow-receive_measure -> rc=0 -../examples/examples-communication/esp-now/codec/communication-codec-espnow-send -> rc=0 +../examples/examples-communication/esp-now/codec/communication-codec-espnow-send -> rc=1/0 ../examples/examples-communication/esp-now/pcm/communication-espnow-receive -> rc=0 ../examples/examples-communication/esp-now/pcm/communication-espnow-receive_csv -> rc=0 ../examples/examples-communication/esp-now/pcm/communication-espnow-receive_measure -> rc=0 @@ -172,80 +158,20 @@ ../examples/examples-communication/ip/communication-ip-receive -> rc=0 ../examples/examples-communication/ip/communication-ip-receive_measure -> rc=0 ../examples/examples-communication/ip/communication-ip-send -> rc=0 -../examples/examples-communication/udp/communication-udp-receive -> rc=0 -../examples/examples-communication/udp/communication-udp-send -> rc=0 -../examples/examples-communication/vban/player-sdmmc-vban -> rc=0 -../examples/examples-communication/vban/streams-audiokit-vban -> rc=0 -../examples/examples-communication/vban/streams-generator-vban -> rc=0 -../examples/examples-communication/vban/streams-vban-audiokit -> rc=0 ../examples/examples-communication/rtsp/communication-audiokit-rtsp -> rc=0 ../examples/examples-communication/rtsp/communication-codec-rtsp -> rc=0 ../examples/examples-communication/rtsp/communication-generator-rtsp -> rc=0 -../examples/examples-communication/rtsp/communication-rtsp-audiokit -> rc=0 -../examples/examples-communication/rtsp/communication-rtsp-i2s -> rc=0 -../examples/examples-communication/serial/send-8bit-receive -> rc=0 -../examples/examples-communication/serial/send-adpcm_framed-receive -> rc=0 -../examples/examples-communication/serial/send-adpcm-receive -> rc=0 -../examples/examples-communication/serial/send-receive -> rc=0 -../examples/examples-communication/snapcast/snapclient-i2s -> rc=0 -../examples/examples-communication/spi/spi-master -> rc=0 -../examples/examples-communication/spi/spi-slave-esp32 -> rc=0 -../examples/examples-communication/spi/spi-slave-rp2040 -> rc=1 -../examples/examples-communication/http-client/player-url-i2s -> rc=0 -../examples/examples-communication/http-client/player-url_icy-audiokit -> rc=0 -../examples/examples-communication/http-client/player-url_icy-i2s -> rc=0 -../examples/examples-communication/http-client/player-url_subclass-i2s -> rc=0 -../examples/examples-communication/http-client/streams-eth_url_mp3_helix-i2s -> rc=0 -../examples/examples-communication/http-client/streams-http_post -> rc=0 -../examples/examples-communication/http-client/streams-url_aac-audiokit -> rc=0 -../examples/examples-communication/http-client/streams-url_aac-i2s -> rc=0 -../examples/examples-communication/http-client/streams-url-file -> rc=0 -../examples/examples-communication/http-client/streams-url_flac_foxen-i2s -> rc=0 -../examples/examples-communication/http-client/streams-url_flac-i2s -> rc=0 -../examples/examples-communication/http-client/streams-url-measuring -> rc=0 -../examples/examples-communication/http-client/streams-url_mp3-analog -> rc=0 -../examples/examples-communication/http-client/streams-url_mp3-audiokit -> rc=0 -../examples/examples-communication/http-client/streams-url_mp3_helix-i2s -> rc=0 -../examples/examples-communication/http-client/streams-url_mp3_helix-i2s_32bit -> rc=0 -../examples/examples-communication/http-client/streams-url_mp3_mad-i2s -> rc=0 -../examples/examples-communication/http-client/streams-url_mp3-metadata -> rc=0 -../examples/examples-communication/http-client/streams-url_mp3-metadata2 -> rc=0 -../examples/examples-communication/http-client/streams-url_mp3-pwm -> rc=0 -../examples/examples-communication/http-client/streams-url_mts-hex -> rc=0 -../examples/examples-communication/http-client/streams-url_post -> rc=0 -../examples/examples-communication/http-client/streams-url_raw-i2s -> rc=0 -../examples/examples-communication/http-client/streams-url_raw-serial -> rc=0 -../examples/examples-communication/http-client/streams-url_vorbis_i2s -> rc=0 -../examples/examples-communication/http-server/player-sd-webserverex_mp3 -> rc=0 -../examples/examples-communication/http-server/python-post-server -> rc=1 -../examples/examples-communication/http-server/README.md -> rc=1 -../examples/examples-communication/http-server/streams-audiokit-webserver_aac -> rc=0 -../examples/examples-communication/http-server/streams-audiokit-webserver_mp3 -> rc=0 -../examples/examples-communication/http-server/streams-audiokit-webserver_wav -> rc=0 -../examples/examples-communication/http-server/streams-effect-webserver_wav -> rc=0 -../examples/examples-communication/http-server/streams-flite-webserver_wav -> rc=0 -../examples/examples-communication/http-server/streams-generator-webserver_aac -> rc=0 -../examples/examples-communication/http-server/streams-generator-webserverex_wav -> rc=0 -../examples/examples-communication/http-server/streams-generator-webserverex_wav1 -> rc=0 -../examples/examples-communication/http-server/streams-generator-webserver_mp3 -> rc=0 -../examples/examples-communication/http-server/streams-generator-webserver_ogg -> rc=0 -../examples/examples-communication/http-server/streams-generator-webserver_wav -> rc=0 -../examples/examples-communication/http-server/streams-i2s-webserver_wav -> rc=0 -../examples/examples-communication/http-server/streams-sam-webserver_wav -> rc=0 -../examples/examples-communication/http-server/streams-tts-webserver_wav -> rc=0 -../examples/tests/adc/read-csv -> rc=0 -../examples/tests/adc/read-csv_unsigned -> rc=0 -../examples/tests/adc/read-esp32-multi-channel-csv -> rc=0 -../examples/tests/adc/read-speed -> rc=0 -../examples/tests/basic/24bits-write -> rc=0 -../examples/tests/basic/test-allocator -> rc=0 -../examples/tests/basic/test-buffer -> rc=0 -../examples/tests/basic/test-queue -> rc=0 -../examples/tests/basic/test-vector -> rc=0 -../examples/tests/codecs/test-codec-aac-fdk -> rc=0 -../examples/tests/codecs/test-codec-aac-fdk-dec -> rc=0 -../examples/tests/codecs/test-codec-adpcm -> rc=0 -../examples/tests/codecs/test-codec-adpcm-xq -> rc=0 +../examples/tests/test-ads1015 -> rc=0 +../examples/tests/test-bitvector -> rc=0 +../examples/tests/test-filter -> rc=0 +../examples/tests/test-list -> rc=0 +../examples/tests/test-memory-helix -> rc=0 +../examples/tests/test-mulit-compilation-units -> rc=0 +../examples/tests/test-pins -> rc=0 +../examples/tests/test-player -> rc=0 +../examples/tests/test-resample-in -> rc=0 +../examples/tests/test-resample-out -> rc=0 +../examples/tests/24bits/24bits-write -> rc=0 ../examples/tests/codecs/test-codec-aptx -> rc=0 ../examples/tests/codecs/test-codec-base64 -> rc=0 ../examples/tests/codecs/test-codec-codec2 -> rc=0 @@ -260,88 +186,40 @@ ../examples/tests/codecs/test-codec-iLBC -> rc=0 ../examples/tests/codecs/test-codec-l8 -> rc=0 ../examples/tests/codecs/test-codec-lc3 -> rc=0 +../examples/tests/codecs/test-codec-ogg -> rc=0 ../examples/tests/codecs/test-codec-opus -> rc=0 ../examples/tests/codecs/test-codec-opusogg -> rc=0 ../examples/tests/codecs/test-codec-sbc -> rc=0 -../examples/tests/codecs/test-codec-wav-adpcm -> rc=0 -../examples/tests/codecs/test-compile-all -> rc=0 +../examples/tests/codecs/test-container-avi -> rc=1/0 ../examples/tests/codecs/test-container-binary -> rc=0 -../examples/tests/codecs/test-container-binary-meta -> rc=0 -../examples/tests/codecs/test-container-ogg -> rc=0 -../examples/tests/codecs/test-memory-helix -> rc=0 ../examples/tests/codecs/test-mp3-helix -> rc=0 ../examples/tests/codecs/test-mp3-helix-reading -> rc=0 ../examples/tests/codecs/test-mp3-mad -> rc=0 -../examples/tests/codecs/test-mp3_parser -> rc=0 -../examples/tests/codecs/test-streaming-adapter -> rc=0 -../examples/tests/concurrency/audio-test -> rc=0 -../examples/tests/concurrency/BufferRTOS -> rc=0 ../examples/tests/concurrency/NBuffer -> rc=0 +../examples/tests/concurrency/synchBufferRTOS -> rc=0 ../examples/tests/concurrency/synchNBuffer -> rc=0 ../examples/tests/concurrency/synchRingBuffer -> rc=0 -../examples/tests/conversion/channel-converter-avg -> rc=0 -../examples/tests/conversion/channel-converter-bin -> rc=0 -../examples/tests/conversion/channel-converter-bindiff -> rc=0 -../examples/tests/conversion/channel-converter-decimate -> rc=0 -../examples/tests/conversion/channel-converter-diff -> rc=0 ../examples/tests/conversion/channel-converter-increase-in -> rc=0 ../examples/tests/conversion/channel-converter-increase-out -> rc=0 ../examples/tests/conversion/channel-converter-reduce-in -> rc=0 ../examples/tests/conversion/channel-converter-reduce-out -> rc=0 -../examples/tests/conversion/format-converter-in -> rc=0 -../examples/tests/conversion/numberformat-converter -> rc=0 -../examples/tests/conversion/numberformat-converter-typed -> rc=0 -../examples/tests/conversion/pipeline-in -> rc=0 -../examples/tests/conversion/pipeline-out -> rc=0 -../examples/tests/conversion/resample-mixer-in -> rc=0 -../examples/tests/conversion/test-panning -> rc=0 -../examples/tests/conversion/test-resample-in -> rc=0 -../examples/tests/conversion/test-resample-out -> rc=0 -../examples/tests/conversion/test-volumestream -> rc=0 ../examples/tests/effects/delay-in -> rc=0 ../examples/tests/effects/delay-out -> rc=0 -../examples/tests/effects/pitch-shift -> rc=0 -../examples/tests/effects/pitch-shift-180 -> rc=0 -../examples/tests/effects/pitch-shift-simple -> rc=0 -../examples/tests/etc/callback-write -> rc=0 -../examples/tests/etc/test-ads1015 -> rc=0 -../examples/tests/etc/test-audiolibs -> rc=0 -../examples/tests/etc/test-mulit-compilation-units -> rc=0 -../examples/tests/etc/test-pins -> rc=0 -../examples/tests/etc/test-ringbufferfile -> rc=0 -../examples/tests/etc/test-tdm -> rc=0 -../examples/tests/etc/test-write-memory -> rc=0 ../examples/tests/fft/fft-cmsis -> rc=1 ../examples/tests/fft/fft-esp32 -> rc=0 ../examples/tests/fft/fft-espressif -> rc=0 -../examples/tests/fft/fft-ifft -> rc=0 ../examples/tests/fft/fft-kiss -> rc=0 ../examples/tests/fft/fft-real -> rc=0 ../examples/tests/fft/fft-topn -> rc=0 ../examples/tests/fft/fft-window -> rc=0 -../examples/tests/filters/test-90deg -> rc=0 -../examples/tests/filters/test-filter -> rc=0 -../examples/tests/filters/test-lowpass -> rc=0 -../examples/tests/filters/test-median-filter -> rc=0 -../examples/tests/performance/file-speeds-sd -> rc=0 -../examples/tests/performance/file-speeds-sdfat -> rc=0 -../examples/tests/performance/file-speeds-sdmmc -> rc=0 -../examples/tests/performance/file-speeds-vfssd -> rc=0 -../examples/tests/performance/file-speeds-vfssdmmc -> rc=0 ../examples/tests/performance/mp3-Speed -> rc=0 ../examples/tests/performance/mp3-SynchronizedBufferRTOS -> rc=0 ../examples/tests/performance/mp3-SynchronizedNBuffer -> rc=0 ../examples/tests/performance/mp3-SynchronizedRingBuffer -> rc=0 -../examples/tests/performance/sine -> rc=0 -../examples/tests/performance/throttle -> rc=0 -../examples/tests/performance/wifi -> rc=0 -../examples/tests/player/test-index-sd -> rc=0 -../examples/tests/player/test-index-sdfat -> rc=0 -../examples/tests/player/test-index-sdmmc -> rc=0 -../examples/tests/player/test-player -> rc=0 -../examples/tests/player/test-vfs -> rc=0 -../examples/tests/timer/test-timer -> rc=0 -../examples/tests/timer/test-timercb_rx -> rc=0 -../examples/tests/timer/test-timercb_tx -> rc=0 -../examples/tests/timer/test-timercbx_rx -> rc=0 -../examples/tests/timer/test-timercbx_tx -> rc=0 +../examples/tests/performance/sine -> rc=1 +../examples/tests/pitch-shift/pitch-shift -> rc=0 +../examples/tests/pitch-shift/pitch-shift-180 -> rc=0 +../examples/tests/pitch-shift/pitch-shift-simple -> rc=0 +../examples/tests/sd/test-index-sd -> rc=0 +../examples/tests/sd/test-index-sdfat -> rc=0 +../examples/tests/sd/test-index-sdmmc -> rc=0 diff --git a/examples/build-examples.sh b/examples/build-examples.sh index 1feb2da7bb..6fdb8c29c8 100755 --- a/examples/build-examples.sh +++ b/examples/build-examples.sh @@ -10,8 +10,6 @@ #arduino-cli lib linstall git -C .. pull git -C ../../ESP32-A2DP pull -git -C ../../arduino-audio-driver pull -git -C ../../arduino-libhelix pull function compile_example { ARCH=$1 @@ -21,7 +19,7 @@ function compile_example { echo "Processing $f ..." # take action on each file. $f store current file name #arduino-cli compile -b "$ARCH" "$f" - arduino-cli compile -b "$ARCH" --build-property "build.partitions=rainmaker" --build-property "upload.maximum_size=3145728" "$f" + arduino-cli compile -b "$ARCH" --build-property "build.partitions=huge_app" --build-property "upload.maximum_size=3145728" "$f" EC=$? #if [ $EC -ne 0 ]; then #break @@ -33,39 +31,26 @@ function compile_example { rm build-examples-log.txt compile_example "esp32:esp32:esp32" "../examples/examples-basic-api/base*" compile_example "esp32:esp32:esp32" "../examples/examples-player/player*" +compile_example "esp32:esp32:esp32" "../examples/examples-webserver/str*" compile_example "esp32:esp32:esp32" "../examples/examples-stream/streams*" compile_example "esp32:esp32:esp32" "../examples/examples-audiokit/*" +compile_example "esp32:esp32:esp32" "../examples/examples-maximilian/*" compile_example "esp32:esp32:esp32" "../examples/examples-tts/streams*" -compile_example "esp32:esp32:esp32" "../examples/examples-dsp/examples-maximilian/*" -compile_example "esp32:esp32:esp32" "../examples/examples-dsp/examples-mozzi/*" -compile_example "esp32:esp32:esp32" "../examples/examples-dsp/examples-pd/*" -compile_example "esp32:esp32:esp32" "../examples/examples-dsp/examples-stk/*" -compile_example "esp32:esp32:esp32" "../examples/examples-dsp/examples-faust/streams*" -compile_example "esp32:esp32:esp32" "../examples/examples-communication/a2dp/*" +compile_example "esp32:esp32:esp32" "../examples/examples-faust/streams*" compile_example "esp32:esp32:esp32" "../examples/examples-communication/esp-now/codec/*" compile_example "esp32:esp32:esp32" "../examples/examples-communication/esp-now/pcm/*" compile_example "esp32:esp32:esp32" "../examples/examples-communication/esp-now/speed-test/*" compile_example "esp32:esp32:esp32" "../examples/examples-communication/ip/*" -compile_example "esp32:esp32:esp32" "../examples/examples-communication/udp/*" -compile_example "esp32:esp32:esp32" "../examples/examples-communication/vban/*" compile_example "esp32:esp32:esp32" "../examples/examples-communication/rtsp/*" -compile_example "esp32:esp32:esp32" "../examples/examples-communication/serial/*" -compile_example "esp32:esp32:esp32" "../examples/examples-communication/snapcast/*" -compile_example "esp32:esp32:esp32" "../examples/examples-communication/spi/*" -compile_example "esp32:esp32:esp32" "../examples/examples-communication/http-client/*" -compile_example "esp32:esp32:esp32" "../examples/examples-communication/http-server/*" -compile_example "esp32:esp32:esp32" "../examples/tests/adc/*" -compile_example "esp32:esp32:esp32" "../examples/tests/basic/*" +compile_example "esp32:esp32:esp32" "../examples/tests/test*" +compile_example "esp32:esp32:esp32" "../examples/tests/24bits/*" compile_example "esp32:esp32:esp32" "../examples/tests/codecs/*" compile_example "esp32:esp32:esp32" "../examples/tests/concurrency/*" compile_example "esp32:esp32:esp32" "../examples/tests/conversion/*" compile_example "esp32:esp32:esp32" "../examples/tests/effects/*" -compile_example "esp32:esp32:esp32" "../examples/tests/etc/*" compile_example "esp32:esp32:esp32" "../examples/tests/fft/*" -compile_example "esp32:esp32:esp32" "../examples/tests/filters/*" compile_example "esp32:esp32:esp32" "../examples/tests/performance/*" -compile_example "esp32:esp32:esp32" "../examples/tests/player/*" -compile_example "esp32:esp32:esp32" "../examples/tests/timer/*" -rm -rf /tmp/arduino +compile_example "esp32:esp32:esp32" "../examples/tests/pitch-shift/*" +compile_example "esp32:esp32:esp32" "../examples/tests/sd/*" ./cleanup.sh \ No newline at end of file diff --git a/examples/examples-audiokit/README.md b/examples/examples-audiokit/README.md index 03dbc78cef..752187c8a6 100644 --- a/examples/examples-audiokit/README.md +++ b/examples/examples-audiokit/README.md @@ -1,7 +1,3 @@ # AudioKit -Please read the [Audio Boards Wiki](https://github.com/pschatzmann/arduino-audio-tools/wiki/Audio-Boards) first. - -These sketches work on the AI Thinker AudioKit, the LyraT or any other board that is supported by the [arduino-audio-driver](https://github.com/pschatzmann/arduino-audio-driver) library. - -If you want to use them on any other board, you just pass the corresponding board variable in the constructor or you compose your own board. Further information can be found in [this Wiki](https://github.com/pschatzmann/arduino-audio-driver/wiki). +These sketches work on the AI Thinker AudioKit or the LyraT Audio Boards. If you want to use them on any other board you usually just need to replace the AudioKitStream with an I2SStream! \ No newline at end of file diff --git a/examples/examples-communication/a2dp/basic-a2dp-audiokit/README.md b/examples/examples-audiokit/basic-a2dp-audiokit/README.md similarity index 90% rename from examples/examples-communication/a2dp/basic-a2dp-audiokit/README.md rename to examples/examples-audiokit/basic-a2dp-audiokit/README.md index d1551b8033..94f979849e 100644 --- a/examples/examples-communication/a2dp/basic-a2dp-audiokit/README.md +++ b/examples/examples-audiokit/basic-a2dp-audiokit/README.md @@ -4,7 +4,7 @@ I found some cheap [AI Thinker ESP32 Audio Kit V2.2](https://docs.ai-thinker.com Audio Kit -I am using the data callback of the A2DP library to feed the AudioBoardStream +I am using the data callback of the A2DP library to feed the AudioKitStream You dont need to bother about any wires because everything is on one nice board. Just just need to install the dependencies: @@ -14,4 +14,4 @@ You need to install the following libraries: - https://github.com/pschatzmann/arduino-audio-tools - https://github.com/pschatzmann/ESP32-A2DP -- https://github.com/pschatzmann/arduino-audio-driver +- https://github.com/pschatzmann/arduino-audiokit diff --git a/examples/examples-communication/a2dp/basic-a2dp-audiokit/basic-a2dp-audiokit.ino b/examples/examples-audiokit/basic-a2dp-audiokit/basic-a2dp-audiokit.ino similarity index 68% rename from examples/examples-communication/a2dp/basic-a2dp-audiokit/basic-a2dp-audiokit.ino rename to examples/examples-audiokit/basic-a2dp-audiokit/basic-a2dp-audiokit.ino index aa33497b4d..825ea2829f 100644 --- a/examples/examples-communication/a2dp/basic-a2dp-audiokit/basic-a2dp-audiokit.ino +++ b/examples/examples-audiokit/basic-a2dp-audiokit/basic-a2dp-audiokit.ino @@ -7,12 +7,12 @@ */ #include "AudioTools.h" -#include "AudioTools/AudioLibs/A2DPStream.h" // install https://github.com/pschatzmann/ESP32-A2DP -#include "AudioTools/AudioLibs/AudioBoardStream.h" // install https://github.com/pschatzmann/arduino-audio-driver +#include "AudioLibs/AudioKit.h" +#include "AudioLibs/AudioA2DP.h" BluetoothA2DPSink a2dp_sink; -AudioBoardStream kit(AudioKitEs8388V1); +AudioKitStream kit; // Write data to AudioKit in callback void read_data_stream(const uint8_t *data, uint32_t length) { @@ -21,7 +21,7 @@ void read_data_stream(const uint8_t *data, uint32_t length) { void setup() { Serial.begin(115200); - AudioToolsLogger.begin(Serial, AudioToolsLogLevel::Info); + AudioLogger::instance().begin(Serial, AudioLogger::Info); // setup output auto cfg = kit.defaultConfig(TX_MODE); @@ -34,4 +34,5 @@ void setup() { } void loop() { + kit.processActions(); } \ No newline at end of file diff --git a/examples/examples-communication/a2dp/basic-a2dp-eq-audiokit/README.md b/examples/examples-audiokit/basic-a2dp-eq-audiokit/README.md similarity index 84% rename from examples/examples-communication/a2dp/basic-a2dp-eq-audiokit/README.md rename to examples/examples-audiokit/basic-a2dp-eq-audiokit/README.md index 775d5d0a05..a061eddd64 100644 --- a/examples/examples-communication/a2dp/basic-a2dp-eq-audiokit/README.md +++ b/examples/examples-audiokit/basic-a2dp-eq-audiokit/README.md @@ -1,10 +1,10 @@ -## Using the AI Thinker ESP32 Audio Kit as A2DP Receiver with Equalizer +## Using the AI Thinker ESP32 Audio Kit as A2DP Receiver with Equilizer I found some cheap [AI Thinker ESP32 Audio Kit V2.2](https://docs.ai-thinker.com/en/esp32-audio-kit) on AliExpress. Audio Kit -I am using the data callback of the A2DP library to feed the AudioBoardStream that will be controlled by an Equalizer +I am using the data callback of the A2DP library to feed the AudioKitStream that will be controlled by an Equilizer You dont need to bother about any wires because everything is on one nice board. Just just need to install the dependencies: @@ -14,4 +14,4 @@ You need to install the following libraries: - https://github.com/pschatzmann/arduino-audio-tools - https://github.com/pschatzmann/ESP32-A2DP -- https://github.com/pschatzmann/arduino-audio-driver +- https://github.com/pschatzmann/arduino-audiokit diff --git a/examples/examples-communication/a2dp/basic-a2dp-eq-audiokit/basic-a2dp-eq-audiokit.ino b/examples/examples-audiokit/basic-a2dp-eq-audiokit/basic-a2dp-eq-audiokit.ino similarity index 69% rename from examples/examples-communication/a2dp/basic-a2dp-eq-audiokit/basic-a2dp-eq-audiokit.ino rename to examples/examples-audiokit/basic-a2dp-eq-audiokit/basic-a2dp-eq-audiokit.ino index f4dac39893..f1d8fb74b7 100644 --- a/examples/examples-communication/a2dp/basic-a2dp-eq-audiokit/basic-a2dp-eq-audiokit.ino +++ b/examples/examples-audiokit/basic-a2dp-eq-audiokit/basic-a2dp-eq-audiokit.ino @@ -7,14 +7,14 @@ */ #include "AudioTools.h" -#include "AudioTools/AudioLibs/A2DPStream.h" // install https://github.com/pschatzmann/ESP32-A2DP -#include "AudioTools/AudioLibs/AudioBoardStream.h" // install https://github.com/pschatzmann/arduino-audio-driver +#include "AudioLibs/AudioKit.h" +#include "AudioLibs/AudioA2DP.h" BluetoothA2DPSink a2dp_sink; -AudioBoardStream kit(AudioKitEs8388V1); -Equalizer3Bands eq(kit); -ConfigEqualizer3Bands cfg_eq; +AudioKitStream kit; +Equilizer3Bands eq(kit); +ConfigEquilizer3Bands cfg_eq; // Write data to AudioKit in callback @@ -24,16 +24,13 @@ void read_data_stream(const uint8_t *data, uint32_t length) { void setup() { Serial.begin(115200); - AudioToolsLogger.begin(Serial, AudioToolsLogLevel::Info); + AudioLogger::instance().begin(Serial, AudioLogger::Info); // setup output auto cfg = kit.defaultConfig(TX_MODE); cfg.sd_active = false; kit.begin(cfg); - // max volume - kit.setVolume(1.0); - // setup equilizer cfg_eq = eq.defaultConfig(); cfg_eq.setAudioInfo(cfg); // use channels, bits_per_sample and sample_rate from kit @@ -48,4 +45,5 @@ void setup() { } void loop() { + // kit.processActions(); // uncomment for default button commands } \ No newline at end of file diff --git a/examples/examples-communication/a2dp/basic-audiokit-a2dp/basic-audiokit-a2dp.ino b/examples/examples-audiokit/basic-audiokit-a2dp/basic-audiokit-a2dp.ino similarity index 63% rename from examples/examples-communication/a2dp/basic-audiokit-a2dp/basic-audiokit-a2dp.ino rename to examples/examples-audiokit/basic-audiokit-a2dp/basic-audiokit-a2dp.ino index 965c0c0d75..fd0be25ac0 100644 --- a/examples/examples-communication/a2dp/basic-audiokit-a2dp/basic-audiokit-a2dp.ino +++ b/examples/examples-audiokit/basic-audiokit-a2dp/basic-audiokit-a2dp.ino @@ -1,17 +1,17 @@ /** * @file base-audiokit-a2dp.ino * @author Phil Schatzmann - * @brief We play the input from the ADC to an A2DP speaker + * @brief We play mp4 files to an A2DP speaker + * make sure that you compile with partition scheme: huge app * @copyright GPLv3 */ #include "AudioTools.h" -#include "AudioTools/AudioLibs/AudioBoardStream.h" -#include "AudioTools/AudioLibs/A2DPStream.h" +#include "AudioLibs/AudioKit.h" +#include "AudioLibs/AudioA2DP.h" -AudioInfo info(44100, 2, 16); BluetoothA2DPSource a2dp_source; -AudioBoardStream i2s(AudioKitEs8388V1); +AudioKitStream i2s; const int16_t BYTES_PER_FRAME = 4; // callback used by A2DP to provide the sound data - usually len is 128 2 channel int16 frames @@ -22,23 +22,23 @@ int32_t get_sound_data(Frame* data, int32_t frameCount) { // Arduino Setup void setup(void) { Serial.begin(115200); - AudioToolsLogger.begin(Serial, AudioToolsLogLevel::Info); + AudioLogger::instance().begin(Serial, AudioLogger::Info); // start i2s input with default configuration Serial.println("starting I2S..."); auto cfg = i2s.defaultConfig(RX_MODE); cfg.i2s_format = I2S_STD_FORMAT; // or try with I2S_LSB_FORMAT - cfg.copyFrom(info); - cfg.input_device = ADC_INPUT_LINE2; // microphone + cfg.bits_per_sample = 16; + cfg.channels = 2; + cfg.sample_rate = 44100; + cfg.input_device = AUDIO_HAL_ADC_INPUT_LINE2; // microphone i2s.begin(cfg); // start the bluetooth Serial.println("starting A2DP..."); - a2dp_source.set_data_callback_in_frames(get_sound_data); - a2dp_source.start("LEXON MINO L"); + a2dp_source.start("LEXON MINO L", get_sound_data); } // Arduino loop - repeated processing void loop() { - delay(1000); -} +} \ No newline at end of file diff --git a/examples/examples-player/player-sd-audiokit/README.md b/examples/examples-audiokit/player-sd-audiokit/README.md similarity index 94% rename from examples/examples-player/player-sd-audiokit/README.md rename to examples/examples-audiokit/player-sd-audiokit/README.md index 74004fe988..0ad58f2f2f 100644 --- a/examples/examples-player/player-sd-audiokit/README.md +++ b/examples/examples-audiokit/player-sd-audiokit/README.md @@ -18,4 +18,4 @@ You need to install the following libraries: - https://github.com/pschatzmann/arduino-audio-tools - https://github.com/pschatzmann/arduino-libhelix -- https://github.com/pschatzmann/arduino-audio-driver +- https://github.com/pschatzmann/arduino-audiokit diff --git a/examples/examples-player/player-sd-audiokit/player-sd-audiokit.ino b/examples/examples-audiokit/player-sd-audiokit/player-sd-audiokit.ino similarity index 76% rename from examples/examples-player/player-sd-audiokit/player-sd-audiokit.ino rename to examples/examples-audiokit/player-sd-audiokit/player-sd-audiokit.ino index 678f957317..894784fd84 100644 --- a/examples/examples-player/player-sd-audiokit/player-sd-audiokit.ino +++ b/examples/examples-audiokit/player-sd-audiokit/player-sd-audiokit.ino @@ -8,14 +8,14 @@ #include "AudioTools.h" -#include "AudioTools/AudioLibs/AudioBoardStream.h" -#include "AudioTools/Disk/AudioSourceSD.h" // or AudioSourceIdxSD.h -#include "AudioTools/AudioCodecs/CodecMP3Helix.h" +#include "AudioLibs/AudioKit.h" +#include "AudioLibs/AudioSourceSD.h" // or AudioSourceIdxSD.h +#include "AudioCodecs/CodecMP3Helix.h" const char *startFilePath="/"; const char* ext="mp3"; AudioSourceSD source(startFilePath, ext, PIN_AUDIO_KIT_SD_CARD_CS); -AudioBoardStream kit(AudioKitEs8388V1); +AudioKitStream kit; MP3DecoderHelix decoder; // or change to MP3DecoderMAD AudioPlayer player(source, kit, decoder); @@ -33,7 +33,7 @@ void startStop(bool, int, void*) { void setup() { Serial.begin(115200); - AudioToolsLogger.begin(Serial, AudioToolsLogLevel::Info); + AudioLogger::instance().begin(Serial, AudioLogger::Info); // setup output auto cfg = kit.defaultConfig(TX_MODE); @@ -43,10 +43,9 @@ void setup() { kit.begin(cfg); // setup additional buttons - kit.addDefaultActions(); - kit.addAction(kit.getKey(1), startStop); - kit.addAction(kit.getKey(4), next); - kit.addAction(kit.getKey(3), previous); + kit.addAction(PIN_KEY1, startStop); + kit.addAction(PIN_KEY4, next); + kit.addAction(PIN_KEY3, previous); // setup player diff --git a/examples/examples-communication/a2dp/player-sd_a2dp-audiokit/player-sd_a2dp-audiokit.ino b/examples/examples-audiokit/player-sd_a2dp-audiokit/player-sd_a2dp-audiokit.ino similarity index 82% rename from examples/examples-communication/a2dp/player-sd_a2dp-audiokit/player-sd_a2dp-audiokit.ino rename to examples/examples-audiokit/player-sd_a2dp-audiokit/player-sd_a2dp-audiokit.ino index 1de620e9ad..fcf171e9ae 100644 --- a/examples/examples-communication/a2dp/player-sd_a2dp-audiokit/player-sd_a2dp-audiokit.ino +++ b/examples/examples-audiokit/player-sd_a2dp-audiokit/player-sd_a2dp-audiokit.ino @@ -12,15 +12,15 @@ // install https://github.com/greiman/SdFat.git #include "AudioTools.h" -#include "AudioTools/AudioLibs/A2DPStream.h" -#include "AudioTools/AudioLibs/AudioBoardStream.h" -#include "AudioTools/Disk/AudioSourceSDFAT.h" -#include "AudioTools/AudioCodecs/CodecMP3Helix.h" +#include "AudioLibs/AudioA2DP.h" +#include "AudioLibs/AudioKit.h" +#include "AudioLibs/AudioSourceSDFAT.h" +#include "AudioCodecs/CodecMP3Helix.h" const char *startFilePath="/"; const char* ext="mp3"; -AudioBoardStream kit(AudioKitEs8388V1); -SdSpiConfig sdcfg(PIN_AUDIO_KIT_SD_CARD_CS, DEDICATED_SPI, SD_SCK_MHZ(10) , &SPI); +AudioKitStream kit; +SdSpiConfig sdcfg(PIN_AUDIO_KIT_SD_CARD_CS, DEDICATED_SPI, SD_SCK_MHZ(10) , &AUDIOKIT_SD_SPI); AudioSourceSDFAT source(startFilePath, ext, sdcfg); MP3DecoderHelix decoder; AudioPlayer player(source, kit, decoder); @@ -57,7 +57,7 @@ void mode(bool, int, void*) { void setup() { Serial.begin(115200); - AudioToolsLogger.begin(Serial, AudioToolsLogLevel::Info); + AudioLogger::instance().begin(Serial, AudioLogger::Info); // provide a2dp data a2dp_sink.set_stream_reader(read_data_stream, false); @@ -68,8 +68,7 @@ void setup() { kit.begin(cfg); // setup additional buttons - kit.addDefaultActions(); - kit.addAction(kit.getKey(4), mode); + kit.addAction(PIN_KEY4, mode); // setup player player.setVolume(0.7); diff --git a/examples/examples-player/player-sdfat-audiokit/README.md b/examples/examples-audiokit/player-sdfat-audiokit/README.md similarity index 95% rename from examples/examples-player/player-sdfat-audiokit/README.md rename to examples/examples-audiokit/player-sdfat-audiokit/README.md index 64ac89621c..95936b6932 100644 --- a/examples/examples-player/player-sdfat-audiokit/README.md +++ b/examples/examples-audiokit/player-sdfat-audiokit/README.md @@ -18,5 +18,5 @@ You need to install the following libraries: - https://github.com/pschatzmann/arduino-audio-tools - https://github.com/pschatzmann/arduino-libhelix -- https://github.com/pschatzmann/arduino-audio-driver +- https://github.com/pschatzmann/arduino-audiokit - https://github.com/greiman/SdFat diff --git a/examples/examples-player/player-sdfat-audiokit/player-sdfat-audiokit.ino b/examples/examples-audiokit/player-sdfat-audiokit/player-sdfat-audiokit.ino similarity index 72% rename from examples/examples-player/player-sdfat-audiokit/player-sdfat-audiokit.ino rename to examples/examples-audiokit/player-sdfat-audiokit/player-sdfat-audiokit.ino index 2c0942e22f..59880f814d 100644 --- a/examples/examples-player/player-sdfat-audiokit/player-sdfat-audiokit.ino +++ b/examples/examples-audiokit/player-sdfat-audiokit/player-sdfat-audiokit.ino @@ -8,15 +8,15 @@ #include "AudioTools.h" -#include "AudioTools/AudioLibs/AudioBoardStream.h" -#include "AudioTools/Disk/AudioSourceSDFAT.h" // or AudioSourceIdxSDFAT.h -#include "AudioTools/AudioCodecs/CodecMP3Helix.h" +#include "AudioLibs/AudioKit.h" +#include "AudioLibs/AudioSourceSDFAT.h" // or AudioSourceIdxSDFAT.h +#include "AudioCodecs/CodecMP3Helix.h" const char *startFilePath="/"; const char* ext="mp3"; -SdSpiConfig sdcfg(PIN_AUDIO_KIT_SD_CARD_CS, DEDICATED_SPI, SD_SCK_MHZ(10) , &SPI); +SdSpiConfig sdcfg(PIN_AUDIO_KIT_SD_CARD_CS, DEDICATED_SPI, SD_SCK_MHZ(10) , &AUDIOKIT_SD_SPI); AudioSourceSDFAT source(startFilePath, ext, sdcfg); -AudioBoardStream kit(AudioKitEs8388V1); +AudioKitStream kit; MP3DecoderHelix decoder; // or change to MP3DecoderMAD AudioPlayer player(source, kit, decoder); @@ -34,17 +34,16 @@ void startStop(bool, int, void*) { void setup() { Serial.begin(115200); - AudioToolsLogger.begin(Serial, AudioToolsLogLevel::Info); + AudioLogger::instance().begin(Serial, AudioLogger::Info); // setup output auto cfg = kit.defaultConfig(TX_MODE); kit.begin(cfg); // setup additional buttons - kit.addDefaultActions(); - kit.addAction(kit.getKey(1), startStop); - kit.addAction(kit.getKey(4), next); - kit.addAction(kit.getKey(3), previous); + kit.addAction(PIN_KEY1, startStop); + kit.addAction(PIN_KEY4, next); + kit.addAction(PIN_KEY3, previous); // setup player diff --git a/examples/examples-player/player-sdmmc-audiokit/README.md b/examples/examples-audiokit/player-sdmmc-audiokit/README.md similarity index 95% rename from examples/examples-player/player-sdmmc-audiokit/README.md rename to examples/examples-audiokit/player-sdmmc-audiokit/README.md index 85fc694fc1..5baa0397ab 100644 --- a/examples/examples-player/player-sdmmc-audiokit/README.md +++ b/examples/examples-audiokit/player-sdmmc-audiokit/README.md @@ -19,4 +19,4 @@ You need to install the following libraries: - https://github.com/pschatzmann/arduino-audio-tools - https://github.com/pschatzmann/arduino-libhelix -- https://github.com/pschatzmann/arduino-audio-driver +- https://github.com/pschatzmann/arduino-audiokit diff --git a/examples/examples-player/player-sdmmc-audiokit/player-sdmmc-audiokit.ino b/examples/examples-audiokit/player-sdmmc-audiokit/player-sdmmc-audiokit.ino similarity index 76% rename from examples/examples-player/player-sdmmc-audiokit/player-sdmmc-audiokit.ino rename to examples/examples-audiokit/player-sdmmc-audiokit/player-sdmmc-audiokit.ino index af8191a210..1639f2bd05 100644 --- a/examples/examples-player/player-sdmmc-audiokit/player-sdmmc-audiokit.ino +++ b/examples/examples-audiokit/player-sdmmc-audiokit/player-sdmmc-audiokit.ino @@ -7,20 +7,20 @@ */ #include "AudioTools.h" -#include "AudioTools/AudioLibs/AudioBoardStream.h" -#include "AudioTools/Disk/AudioSourceSDMMC.h" // or AudioSourceIdxSDMMC.h -#include "AudioTools/AudioCodecs/CodecMP3Helix.h" +#include "AudioLibs/AudioKit.h" +#include "AudioLibs/AudioSourceSDMMC.h" // or AudioSourceIdxSDMMC.h +#include "AudioCodecs/CodecMP3Helix.h" const char *startFilePath="/"; const char* ext="mp3"; AudioSourceSDMMC source(startFilePath, ext); -AudioBoardStream kit(AudioKitEs8388V1); +AudioKitStream kit; MP3DecoderHelix decoder; // or change to MP3DecoderMAD AudioPlayer player(source, kit, decoder); void setup() { Serial.begin(115200); - AudioToolsLogger.begin(Serial, AudioToolsLogLevel::Info); + AudioLogger::instance().begin(Serial, AudioLogger::Info); // setup output auto cfg = kit.defaultConfig(TX_MODE); diff --git a/examples/examples-communication/http-client/player-url_icy-audiokit/README.md b/examples/examples-audiokit/player-url_icy-audiokit/README.md similarity index 93% rename from examples/examples-communication/http-client/player-url_icy-audiokit/README.md rename to examples/examples-audiokit/player-url_icy-audiokit/README.md index 1e5bb0e59d..bef056ac6b 100644 --- a/examples/examples-communication/http-client/player-url_icy-audiokit/README.md +++ b/examples/examples-audiokit/player-url_icy-audiokit/README.md @@ -18,4 +18,4 @@ I also demonstrate how to assign your own actions to the buttons of the audio ki - https://github.com/pschatzmann/arduino-audio-tools - https://github.com/pschatzmann/arduino-libhelix -- https://github.com/pschatzmann/arduino-audio-driver +- https://github.com/pschatzmann/arduino-audiokit diff --git a/examples/examples-communication/http-client/player-url_icy-audiokit/player-url_icy-audiokit.ino b/examples/examples-audiokit/player-url_icy-audiokit/player-url_icy-audiokit.ino similarity index 79% rename from examples/examples-communication/http-client/player-url_icy-audiokit/player-url_icy-audiokit.ino rename to examples/examples-audiokit/player-url_icy-audiokit/player-url_icy-audiokit.ino index 2f510092bf..579a227824 100644 --- a/examples/examples-communication/http-client/player-url_icy-audiokit/player-url_icy-audiokit.ino +++ b/examples/examples-audiokit/player-url_icy-audiokit/player-url_icy-audiokit.ino @@ -7,9 +7,8 @@ */ #include "AudioTools.h" -#include "AudioTools/AudioCodecs/CodecMP3Helix.h" -#include "AudioTools/AudioLibs/AudioBoardStream.h" -#include "AudioTools/Disk/AudioSourceURL.h" +#include "AudioCodecs/CodecMP3Helix.h" +#include "AudioLibs/AudioKit.h" const char *urls[] = { "http://stream.srg-ssr.ch/m/rsj/mp3_128", @@ -23,7 +22,7 @@ const char *password = "password"; ICYStream urlStream(wifi, password); AudioSourceURL source(urlStream, urls, "audio/mp3"); -AudioBoardStream kit(AudioKitEs8388V1); +AudioKitStream kit; MP3DecoderHelix decoder; AudioPlayer player(source, kit, decoder); @@ -46,7 +45,7 @@ void stopResume(bool, int, void*){ // Arduino setup void setup() { Serial.begin(115200); - AudioToolsLogger.begin(Serial, AudioToolsLogLevel::Info); + AudioLogger::instance().begin(Serial, AudioLogger::Info); // setup output auto cfg = kit.defaultConfig(TX_MODE); @@ -54,8 +53,8 @@ void setup() { kit.begin(cfg); // setup navigation - kit.addAction(kit.getKey(4), next); - kit.addAction(kit.getKey(3), previous); + kit.addAction(PIN_KEY4, next); + kit.addAction(PIN_KEY3, previous); // setup player player.setVolume(0.7); diff --git a/examples/examples-communication/a2dp/streams-a2dp-audiokit/streams-a2dp-audiokit.ino b/examples/examples-audiokit/streams-a2dp-audiokit/streams-a2dp-audiokit.ino similarity index 78% rename from examples/examples-communication/a2dp/streams-a2dp-audiokit/streams-a2dp-audiokit.ino rename to examples/examples-audiokit/streams-a2dp-audiokit/streams-a2dp-audiokit.ino index 15daf91042..f15ce732fc 100644 --- a/examples/examples-communication/a2dp/streams-a2dp-audiokit/streams-a2dp-audiokit.ino +++ b/examples/examples-audiokit/streams-a2dp-audiokit/streams-a2dp-audiokit.ino @@ -9,18 +9,18 @@ */ #include "AudioTools.h" -#include "AudioTools/AudioLibs/A2DPStream.h" -#include "AudioTools/AudioLibs/AudioBoardStream.h" +#include "AudioLibs/AudioA2DP.h" +#include "AudioLibs/AudioKit.h" A2DPStream in; -AudioBoardStream kit(AudioKitEs8388V1); +AudioKitStream kit; StreamCopy copier(kit, in); // copy in to out // Arduino Setup void setup(void) { Serial.begin(115200); - AudioToolsLogger.begin(Serial, AudioToolsLogLevel::Warning); + AudioLogger::instance().begin(Serial, AudioLogger::Warning); // start the bluetooth audio receiver Serial.println("starting A2DP..."); diff --git a/examples/examples-audiokit/streams-audiokit-audiokit/README.md b/examples/examples-audiokit/streams-audiokit-audiokit/README.md index e01ffbb347..3f8e9c24b7 100644 --- a/examples/examples-audiokit/streams-audiokit-audiokit/README.md +++ b/examples/examples-audiokit/streams-audiokit-audiokit/README.md @@ -12,4 +12,4 @@ We implement a AudioKit source and sink: We stream the sound input which we read You need to install the following libraries: - https://github.com/pschatzmann/arduino-audio-tools -- https://github.com/pschatzmann/arduino-audio-driver +- https://github.com/pschatzmann/arduino-audiokit diff --git a/examples/examples-audiokit/streams-audiokit-audiokit/streams-audiokit-audiokit.ino b/examples/examples-audiokit/streams-audiokit-audiokit/streams-audiokit-audiokit.ino index 4a797b1af3..eb14a17777 100644 --- a/examples/examples-audiokit/streams-audiokit-audiokit/streams-audiokit-audiokit.ino +++ b/examples/examples-audiokit/streams-audiokit-audiokit/streams-audiokit-audiokit.ino @@ -9,19 +9,19 @@ #include "AudioTools.h" -#include "AudioTools/AudioLibs/AudioBoardStream.h" +#include "AudioLibs/AudioKit.h" -AudioBoardStream kit(AudioKitEs8388V1); // Access I2S as stream +AudioKitStream kit; // Access I2S as stream StreamCopy copier(kit, kit); // copy kit to kit // Arduino Setup void setup(void) { Serial.begin(115200); - AudioToolsLogger.begin(Serial, AudioToolsLogLevel::Warning); + AudioLogger::instance().begin(Serial, AudioLogger::Warning); auto cfg = kit.defaultConfig(RXTX_MODE); cfg.sd_active = false; - cfg.input_device = ADC_INPUT_LINE2; + cfg.input_device = AUDIO_HAL_ADC_INPUT_LINE2; kit.begin(cfg); } diff --git a/examples/examples-audiokit/streams-audiokit-effects-audiokit/streams-audiokit-effects-audiokit.ino b/examples/examples-audiokit/streams-audiokit-effects-audiokit/streams-audiokit-effects-audiokit.ino index b5d837c7e1..9519adcf69 100644 --- a/examples/examples-audiokit/streams-audiokit-effects-audiokit/streams-audiokit-effects-audiokit.ino +++ b/examples/examples-audiokit/streams-audiokit-effects-audiokit/streams-audiokit-effects-audiokit.ino @@ -11,7 +11,7 @@ #include // https://arduinojson.org/ #include "HttpServer.h" // https://github.com/pschatzmann/TinyHttp #include "AudioTools.h" // https://github.com/pschatzmann/arduino-audio-tools.git -#include "AudioTools/AudioLibs/AudioBoardStream.h" // https://github.com/pschatzmann/arduino-audio-driver.git +#include "AudioLibs/AudioKit.h" // https://github.com/pschatzmann/arduino-audiokit.git // Server WiFiServer wifi; @@ -39,7 +39,7 @@ Fuzz fuzz(fuzzEffectValue); Tremolo tremolo(tremoloDuration, tremoloDepth, sample_rate); // Audio -AudioBoardStream kit(AudioKitEs8388V1); // Access I2S as stream +AudioKitStream kit; // Access I2S as stream AudioEffectStream effects(kit); // input from kit StreamCopy copier(kit, effects); // copy effects to kit @@ -118,7 +118,7 @@ void postJson(HttpServer *server, const char*requestPath, HttpRequestHandlerLine // Arduino Setup void setup(void) { Serial.begin(115200); - AudioToolsLogger.begin(Serial, AudioToolsLogLevel::Warning); + AudioLogger::instance().begin(Serial, AudioLogger::Warning); // Setup Server static HttpTunnel tunnel_url("https://pschatzmann.github.io/TinyHttp/app/guitar-effects.html"); @@ -131,7 +131,7 @@ void setup(void) { // Setup Kit auto cfg = kit.defaultConfig(RXTX_MODE); cfg.sd_active = false; - cfg.input_device = ADC_INPUT_LINE2; + cfg.input_device = AUDIO_HAL_ADC_INPUT_LINE2; cfg.sample_rate = sample_rate; cfg.channels = channels; // minimize lag diff --git a/examples/examples-audiokit/streams-audiokit-fft-led/streams-audiokit-fft-led.ino b/examples/examples-audiokit/streams-audiokit-fft-led/streams-audiokit-fft-led.ino index e27ff93c55..542b5b1caf 100644 --- a/examples/examples-audiokit/streams-audiokit-fft-led/streams-audiokit-fft-led.ino +++ b/examples/examples-audiokit/streams-audiokit-fft-led/streams-audiokit-fft-led.ino @@ -1,62 +1,46 @@ -/** - * @file streams-audiokit-fft-led.ino - * @author Phil Schatzmann - * @brief We peform FFT on the microphone input of the AudioKit and display the - * result on a 32*8 LED matrix - * @version 0.1 - * @date 2022-10-14 - * - * @copyright Copyright (c) 2022 - * - */ -#include // to prevent conflicts introduced with 3.9 #include "AudioTools.h" -#include "AudioTools/AudioLibs/AudioBoardStream.h" -#include "AudioTools/AudioLibs/AudioRealFFT.h" // or AudioKissFFT -#include "AudioTools/AudioLibs/LEDOutput.h" +#include "AudioLibs/AudioKit.h" +#include "AudioLibs/AudioRealFFT.h" // or AudioKissFFT +#include "AudioLibs/LEDOutput.h" #define PIN_LEDS 22 #define LED_X 32 #define LED_Y 8 -AudioBoardStream kit(AudioKitEs8388V1); // Audio source -AudioRealFFT fft; // or AudioKissFFT -FFTDisplay fft_dis(fft); -LEDOutput led(fft_dis); // output to LED matrix +AudioKitStream kit; // Audio source +AudioRealFFT fft; // or AudioKissFFT StreamCopy copier(fft, kit); // copy mic to fft +LEDOutput led(fft); // output to LED matrix void setup() { Serial.begin(115200); - AudioToolsLogger.begin(Serial, AudioToolsLogLevel::Info); + AudioLogger::instance().begin(Serial, AudioLogger::Info); // setup Audiokit as input device auto cfg = kit.defaultConfig(RX_MODE); - cfg.input_device = ADC_INPUT_LINE2; + cfg.input_device = AUDIO_HAL_ADC_INPUT_LINE2; kit.begin(cfg); // Setup FFT output auto tcfg = fft.defaultConfig(); - tcfg.length = 1024; + tcfg.length = 1024; tcfg.copyFrom(cfg); fft.begin(tcfg); - // Setup FFT Display - fft_dis.fft_group_bin = 3; - fft_dis.fft_start_bin = 0; - fft_dis.fft_max_magnitude = 40000; - fft_dis.begin(); - // Setup LED matrix output auto lcfg = led.defaultConfig(); lcfg.x = LED_X; lcfg.y = LED_Y; + lcfg.fft_group_bin = 3; + lcfg.fft_start_bin = 0; + lcfg.fft_max_magnitude = 40000; led.begin(lcfg); // add LEDs FastLED.addLeds(led.ledData(), led.ledCount()); } -void loop() { +void loop() { // update FFT copier.copy(); // update LEDs diff --git a/examples/examples-audiokit/streams-audiokit-fft/README.md b/examples/examples-audiokit/streams-audiokit-fft/README.md index 275bf8b336..d2a0420672 100644 --- a/examples/examples-audiokit/streams-audiokit-fft/README.md +++ b/examples/examples-audiokit/streams-audiokit-fft/README.md @@ -18,4 +18,4 @@ The log level has been set to Info to help you to identify any problems. Please You need to install the following libraries: - https://github.com/pschatzmann/arduino-audio-tools -- https://github.com/pschatzmann/arduino-audio-driver +- https://github.com/pschatzmann/arduino-audiokit diff --git a/examples/examples-audiokit/streams-audiokit-fft/streams-audiokit-fft.ino b/examples/examples-audiokit/streams-audiokit-fft/streams-audiokit-fft.ino index e428ca0371..50bf3e8114 100644 --- a/examples/examples-audiokit/streams-audiokit-fft/streams-audiokit-fft.ino +++ b/examples/examples-audiokit/streams-audiokit-fft/streams-audiokit-fft.ino @@ -1,9 +1,9 @@ #include "AudioTools.h" -#include "AudioTools/AudioLibs/AudioBoardStream.h" -#include "AudioTools/AudioLibs/AudioRealFFT.h" // or AudioKissFFT +#include "AudioLibs/AudioKit.h" +#include "AudioLibs/AudioRealFFT.h" // or AudioKissFFT -AudioBoardStream kit(AudioKitEs8388V1); // Audio source +AudioKitStream kit; // Audio source AudioRealFFT fft; // or AudioKissFFT StreamCopy copier(fft, kit); // copy mic to tfl int channels = 2; @@ -28,11 +28,11 @@ void fftResult(AudioFFTBase &fft){ void setup() { Serial.begin(115200); - AudioToolsLogger.begin(Serial, AudioToolsLogLevel::Warning); + AudioLogger::instance().begin(Serial, AudioLogger::Warning); // setup Audiokit auto cfg = kit.defaultConfig(RX_MODE); - cfg.input_device = ADC_INPUT_LINE2; + cfg.input_device = AUDIO_HAL_ADC_INPUT_LINE2; cfg.channels = channels; cfg.sample_rate = samples_per_second; cfg.bits_per_sample = bits_per_sample; diff --git a/examples/examples-audiokit/streams-audiokit-filter-audiokit/streams-audiokit-filter-audiokit.ino b/examples/examples-audiokit/streams-audiokit-filter-audiokit/streams-audiokit-filter-audiokit.ino index b9343c6d6a..5dfef7d56f 100644 --- a/examples/examples-audiokit/streams-audiokit-filter-audiokit/streams-audiokit-filter-audiokit.ino +++ b/examples/examples-audiokit/streams-audiokit-filter-audiokit/streams-audiokit-filter-audiokit.ino @@ -6,13 +6,13 @@ */ #include "AudioTools.h" -#include "AudioTools/AudioLibs/AudioBoardStream.h" +#include "AudioLibs/AudioKit.h" AudioInfo info(44100, 2, 16); -AudioBoardStream kit(AudioKitEs8388V1); +AudioKitStream kit; // copy filtered values -FilteredStream filtered(kit, info.channels); // Defiles the filter as BaseConverter +FilteredStream filtered(kit, channels); // Defiles the filter as BaseConverter StreamCopy copier(filtered, kit); // copies sound into i2s (both from kit to filtered or filered to kit are supported) // define FIR filter @@ -23,7 +23,7 @@ void setup(void) { // Open Serial Serial.begin(115200); // change to Warning to improve the quality - AudioToolsLogger.begin(Serial, AudioToolsLogLevel::Info); + AudioLogger::instance().begin(Serial, AudioLogger::Info); // setup filters for all available channels filtered.setFilter(0, new FIR(coef)); @@ -34,7 +34,7 @@ void setup(void) { auto config = kit.defaultConfig(RXTX_MODE); config.copyFrom(info); config.sd_active = false; - config.input_device = ADC_INPUT_LINE2; + config.input_device = AUDIO_HAL_ADC_INPUT_LINE2; kit.begin(config); Serial.println("KIT started..."); diff --git a/examples/examples-audiokit/streams-audiokit-multioutput-server/streams-audiokit-multioutput-server.ino b/examples/examples-audiokit/streams-audiokit-multioutput-server/streams-audiokit-multioutput-server.ino index 653992abfc..a2f3e2e5b0 100644 --- a/examples/examples-audiokit/streams-audiokit-multioutput-server/streams-audiokit-multioutput-server.ino +++ b/examples/examples-audiokit/streams-audiokit-multioutput-server/streams-audiokit-multioutput-server.ino @@ -10,11 +10,11 @@ */ #include "AudioTools.h" -#include "AudioTools/AudioLibs/AudioBoardStream.h" +#include "AudioLibs/AudioKit.h" const int buffer_count = 10; const int buffer_size = 1024; -AudioBoardStream kit(AudioKitEs8388V1); // input & output +AudioKitStream kit; // input & output QueueStream queue(buffer_size, buffer_count, true); AudioEncoderServer server(new WAVEncoder(),"WIFI","password"); MultiOutput out(queue, kit); @@ -23,12 +23,12 @@ StreamCopy copier(out, kit); // copy kit to kit // Arduino Setup void setup(void) { Serial.begin(115200); - AudioToolsLogger.begin(Serial, AudioToolsLogLevel::Info); + AudioLogger::instance().begin(Serial, AudioLogger::Info); // setup audiokit auto cfg = kit.defaultConfig(RXTX_MODE); cfg.sd_active = false; - cfg.input_device = ADC_INPUT_LINE2; // input from microphone + cfg.input_device = AUDIO_HAL_ADC_INPUT_LINE2; // input from microphone cfg.sample_rate = 16000; kit.setVolume(0.5); kit.begin(cfg); diff --git a/examples/examples-audiokit/streams-audiokit-multioutput/streams-audiokit-multioutput.ino b/examples/examples-audiokit/streams-audiokit-multioutput/streams-audiokit-multioutput.ino index 118b678d29..47a7b1a670 100644 --- a/examples/examples-audiokit/streams-audiokit-multioutput/streams-audiokit-multioutput.ino +++ b/examples/examples-audiokit/streams-audiokit-multioutput/streams-audiokit-multioutput.ino @@ -10,30 +10,27 @@ */ #include "AudioTools.h" -#include "AudioTools/AudioLibs/AudioBoardStream.h" +#include "AudioLibs/AudioKit.h" -AudioInfo info(8000, 2, 16); -AudioBoardStream kit(AudioKitEs8388V1); // Access I2S as stream -CsvOutput csv(Serial); +AudioKitStream kit; // Access I2S as stream +CsvOutput csv(Serial,2); MultiOutput out; StreamCopy copier(out, kit); // copy kit to kit // Arduino Setup void setup(void) { Serial.begin(230400); - AudioToolsLogger.begin(Serial, AudioToolsLogLevel::Warning); + AudioLogger::instance().begin(Serial, AudioLogger::Warning); out.add(csv); out.add(kit); auto cfg = kit.defaultConfig(RXTX_MODE); - cfg.copyFrom(info); cfg.sd_active = false; - cfg.input_device = ADC_INPUT_LINE2; // input from microphone + cfg.input_device = AUDIO_HAL_ADC_INPUT_LINE2; // input from microphone + cfg.sample_rate = 8000; kit.setVolume(0.5); kit.begin(cfg); - - csv.begin(info); } // Arduino loop - copy data diff --git a/examples/examples-audiokit/streams-audiokit-ram-audiokit/streams-audiokit-ram-audiokit.ino b/examples/examples-audiokit/streams-audiokit-ram-audiokit/streams-audiokit-ram-audiokit.ino index 6be14925f0..68a645159c 100644 --- a/examples/examples-audiokit/streams-audiokit-ram-audiokit/streams-audiokit-ram-audiokit.ino +++ b/examples/examples-audiokit/streams-audiokit-ram-audiokit/streams-audiokit-ram-audiokit.ino @@ -10,11 +10,11 @@ * */ #include "AudioTools.h" -#include "AudioTools/AudioLibs/AudioBoardStream.h" -#include "AudioTools/AudioLibs/MemoryManager.h" +#include "AudioLibs/AudioKit.h" +#include "AudioLibs/MemoryManager.h" AudioInfo info(16000, 1, 16); -AudioBoardStream kit(AudioKitEs8388V1); +AudioKitStream kit; MemoryManager memory(500); // Activate SPI RAM for objects > 500 bytes DynamicMemoryStream recording(true); // Audio stored on heap StreamCopy copier; // copies data @@ -32,7 +32,7 @@ void record_end(bool pinStatus, int pin, void* ref){ // Remove popping noise, from button: we delete 6 segments at the beginning and end // and on the resulting audio we slowly raise the volume on the first segment // end decrease it on the last segment - recording.postProcessSmoothTransition(info.channels, 0.01, 6); + recording.postProcessSmoothTransition(channels, 0.01, 6); copier.begin(kit, recording); // start playback } @@ -40,18 +40,18 @@ void record_end(bool pinStatus, int pin, void* ref){ void setup(){ Serial.begin(115200); while(!Serial); // wait for serial to be ready - AudioToolsLogger.begin(Serial, AudioToolsLogLevel::Info); + AudioLogger::instance().begin(Serial, AudioLogger::Info); // setup input and output auto cfg = kit.defaultConfig(RXTX_MODE); cfg.sd_active = true; cfg.copyFrom(info); - cfg.input_device = ADC_INPUT_LINE2; + cfg.input_device = AUDIO_HAL_ADC_INPUT_LINE2; kit.begin(cfg); kit.setVolume(1.0); // record when key 1 is pressed - kit.audioActions().add(kit.getKey(1), record_start, record_end); + kit.audioActions().add(PIN_KEY1, record_start, record_end); Serial.println("Press Key 1 to record"); } diff --git a/examples/examples-audiokit/streams-audiokit-ram-ptichshift-audiokit/streams-audiokit-ram-ptichshift-audiokit.ino b/examples/examples-audiokit/streams-audiokit-ram-ptichshift-audiokit/streams-audiokit-ram-ptichshift-audiokit.ino index 2f1070e699..a94172b980 100644 --- a/examples/examples-audiokit/streams-audiokit-ram-ptichshift-audiokit/streams-audiokit-ram-ptichshift-audiokit.ino +++ b/examples/examples-audiokit/streams-audiokit-ram-ptichshift-audiokit/streams-audiokit-ram-ptichshift-audiokit.ino @@ -11,12 +11,12 @@ * */ #include "AudioTools.h" -#include "AudioTools/AudioLibs/AudioBoardStream.h" -#include "AudioTools/AudioLibs/MemoryManager.h" +#include "AudioLibs/AudioKit.h" +#include "AudioLibs/MemoryManager.h" AudioInfo info(16000, 1, 16); MemoryManager memory(500); // Activate SPI RAM for objects > 500 bytes -AudioBoardStream kit(AudioKitEs8388V1); +AudioKitStream kit; //use one of VariableSpeedRingBufferSimple, VariableSpeedRingBuffer, VariableSpeedRingBuffer180 PitchShiftOutput> pitch_shift(kit); DynamicMemoryStream recording(false); // Audio stored on heap, non repeating @@ -35,7 +35,7 @@ void record_end(bool pinStatus, int pin, void* ref){ // Remove popping noise, from button: we delete 6 segments at the beginning and end // and on the resulting audio we slowly raise the volume on the first segment // end decrease it on the last segment - recording.postProcessSmoothTransition(info.channels, 0.01, 6); + recording.postProcessSmoothTransition(channels, 0.01, 6); // output with pitch shifting copier.begin(pitch_shift, recording); // start playback @@ -44,13 +44,13 @@ void record_end(bool pinStatus, int pin, void* ref){ void setup(){ Serial.begin(115200); while(!Serial); // wait for serial to be ready - AudioToolsLogger.begin(Serial, AudioToolsLogLevel::Info); + AudioLogger::instance().begin(Serial, AudioLogger::Info); // setup input and output auto cfg = kit.defaultConfig(RXTX_MODE); cfg.sd_active = true; cfg.copyFrom(info); - cfg.input_device = ADC_INPUT_LINE2; + cfg.input_device = AUDIO_HAL_ADC_INPUT_LINE2; kit.begin(cfg); kit.setVolume(1.0); @@ -62,14 +62,17 @@ void setup(){ pitch_shift.begin(cfg_pc); // record when key 1 is pressed - kit.audioActions().add(kit.getKey(1), record_start, record_end); + kit.audioActions().add(PIN_KEY1, record_start, record_end); Serial.println("Press Key 1 to record"); + } void loop(){ + // record or play recording copier.copy(); // Process keys kit.processActions(); + } diff --git a/examples/examples-audiokit/streams-audiokit-sd-audiokit/streams-audiokit-sd-audiokit.ino b/examples/examples-audiokit/streams-audiokit-sd-audiokit/streams-audiokit-sd-audiokit.ino index 65a178757a..e0054c496b 100644 --- a/examples/examples-audiokit/streams-audiokit-sd-audiokit/streams-audiokit-sd-audiokit.ino +++ b/examples/examples-audiokit/streams-audiokit-sd-audiokit/streams-audiokit-sd-audiokit.ino @@ -10,13 +10,13 @@ * */ #include "AudioTools.h" -#include "AudioTools/AudioLibs/AudioBoardStream.h" +#include "AudioLibs/AudioKit.h" #include #include const char *file_name = "/rec.raw"; AudioInfo info(16000, 1, 16); -AudioBoardStream kit(AudioKitEs8388V1); +AudioKitStream kit; File file; // final output stream StreamCopy copier; // copies data bool recording = false; // flag to make sure that close is only executed one @@ -44,15 +44,7 @@ void record_end(bool pinStatus, int pin, void* ref){ void setup(){ Serial.begin(115200); while(!Serial); // wait for serial to be ready - AudioToolsLogger.begin(Serial, AudioToolsLogLevel::Warning); - - // setup input and output: setup audiokit before SD! - auto cfg = kit.defaultConfig(RXTX_MODE); - cfg.sd_active = true; - cfg.copyFrom(info); - cfg.input_device = ADC_INPUT_LINE2; - kit.begin(cfg); - kit.setVolume(1.0); + AudioLogger::instance().begin(Serial, AudioLogger::Warning); // Open SD drive if (!SD.begin(PIN_AUDIO_KIT_SD_CARD_CS)) { @@ -61,9 +53,16 @@ void setup(){ } Serial.println("Initialization done."); + // setup input and output + auto cfg = kit.defaultConfig(RXTX_MODE); + cfg.sd_active = true; + cfg.copyFrom(info); + cfg.input_device = AUDIO_HAL_ADC_INPUT_LINE2; + kit.begin(cfg); + kit.setVolume(1.0); // record when key 1 is pressed - kit.audioActions().add(kit.getKey(1), record_start, record_end); + kit.audioActions().add(PIN_KEY1, record_start, record_end); Serial.println("Press Key 1 to record"); } diff --git a/examples/examples-audiokit/streams-audiokit-sd_wav/streams-audiokit-sd_wav.ino b/examples/examples-audiokit/streams-audiokit-sd_wav/streams-audiokit-sd_wav.ino index 654dc39844..c415ae2dc5 100644 --- a/examples/examples-audiokit/streams-audiokit-sd_wav/streams-audiokit-sd_wav.ino +++ b/examples/examples-audiokit/streams-audiokit-sd_wav/streams-audiokit-sd_wav.ino @@ -13,11 +13,11 @@ #include #include "AudioTools.h" -#include "AudioTools/AudioLibs/AudioBoardStream.h" +#include "AudioLibs/AudioKit.h" const char* file_name = "/rec.wav"; AudioInfo info(16000, 1, 16); -AudioBoardStream in(AudioKitEs8388V1); +AudioKitStream in; File file; // final output stream EncodedAudioStream out(&file, new WAVEncoder()); StreamCopy copier(out, in); // copies data @@ -26,13 +26,13 @@ uint64_t timeout; void setup() { Serial.begin(115200); - AudioToolsLogger.begin(Serial, AudioToolsLogLevel::Info); + AudioLogger::instance().begin(Serial, AudioLogger::Info); // setup input auto cfg = in.defaultConfig(RX_MODE); cfg.sd_active = true; cfg.copyFrom(info); - cfg.input_device = ADC_INPUT_LINE2; // microphone + cfg.input_device = AUDIO_HAL_ADC_INPUT_LINE2; // microphone in.begin(cfg); // Open SD drive diff --git a/examples/examples-audiokit/streams-audiokit-serial/README.md b/examples/examples-audiokit/streams-audiokit-serial/README.md index 10a2da3001..93eff73a8c 100644 --- a/examples/examples-audiokit/streams-audiokit-serial/README.md +++ b/examples/examples-audiokit/streams-audiokit-serial/README.md @@ -12,4 +12,4 @@ We implement a AudioKit source: We stream the sound input which we read in from You need to install the following libraries: - https://github.com/pschatzmann/arduino-audio-tools -- https://github.com/pschatzmann/arduino-audio-driver +- https://github.com/pschatzmann/arduino-audiokit diff --git a/examples/examples-audiokit/streams-audiokit-serial/streams-audiokit-serial.ino b/examples/examples-audiokit/streams-audiokit-serial/streams-audiokit-serial.ino index c0f0987b6a..910450eaaf 100644 --- a/examples/examples-audiokit/streams-audiokit-serial/streams-audiokit-serial.ino +++ b/examples/examples-audiokit/streams-audiokit-serial/streams-audiokit-serial.ino @@ -9,25 +9,23 @@ #include "AudioTools.h" -#include "AudioTools/AudioLibs/AudioBoardStream.h" +#include "AudioLibs/AudioKit.h" -AudioInfo info(44100, 2, 16); -AudioBoardStream kit(AudioKitEs8388V1); // Access I2S as stream -CsvOutput csvOutput(Serial); -StreamCopy copier(csvOutput, kit); // copy kit to csvOutput +AudioKitStream kit; // Access I2S as stream +CsvOutput csvStream(Serial); +StreamCopy copier(csvStream, kit); // copy kit to csvStream // Arduino Setup void setup(void) { Serial.begin(115200); - AudioToolsLogger.begin(Serial, AudioToolsLogLevel::Warning); + AudioLogger::instance().begin(Serial, AudioLogger::Warning); auto cfg = kit.defaultConfig(RX_MODE); - cfg.copyFrom(info); - cfg.input_device = ADC_INPUT_LINE2; + cfg.input_device = AUDIO_HAL_ADC_INPUT_LINE2; kit.begin(cfg); // make sure that we have the correct channels set up - csvOutput.begin(info); + csvStream.begin(); } diff --git a/examples/examples-audiokit/streams-audiokit-tf/README.md b/examples/examples-audiokit/streams-audiokit-tf/README.md index 80157a08f9..3b25ab4bc4 100644 --- a/examples/examples-audiokit/streams-audiokit-tf/README.md +++ b/examples/examples-audiokit/streams-audiokit-tf/README.md @@ -22,5 +22,5 @@ The log level has been set to Info to help you to identify any problems. Please You need to install the following libraries: - https://github.com/pschatzmann/arduino-audio-tools -- https://github.com/pschatzmann/arduino-audio-driver +- https://github.com/pschatzmann/arduino-audiokit - https://github.com/pschatzmann/tflite-micro-arduino-examples \ No newline at end of file diff --git a/examples/examples-audiokit/streams-audiokit-tf/streams-audiokit-tf.ino b/examples/examples-audiokit/streams-audiokit-tf/streams-audiokit-tf.ino index 509011b157..8cc39d8dd1 100644 --- a/examples/examples-audiokit/streams-audiokit-tf/streams-audiokit-tf.ino +++ b/examples/examples-audiokit/streams-audiokit-tf/streams-audiokit-tf.ino @@ -1,10 +1,10 @@ #include "AudioTools.h" -#include "AudioTools/AudioLibs/AudioBoardStream.h" -#include "AudioTools/AudioLibs/TfLiteAudioStream.h" +#include "AudioLibs/AudioKit.h" +#include "AudioLibs/TfLiteAudioStream.h" #include "model.h" // tensorflow model -AudioBoardStream kit(AudioKitEs8388V1); // Audio source +AudioKitStream kit; // Audio source TfLiteAudioStream tfl; // Audio sink const char* kCategoryLabels[4] = { "silence", @@ -28,15 +28,15 @@ void respondToCommand(const char* found_command, uint8_t score, void setup() { Serial.begin(115200); - AudioToolsLogger.begin(Serial, AudioToolsLogLevel::Warning); + AudioLogger::instance().begin(Serial, AudioLogger::Warning); // setup Audiokit auto cfg = kit.defaultConfig(RX_MODE); - cfg.input_device = ADC_INPUT_LINE2; + cfg.input_device = AUDIO_HAL_ADC_INPUT_LINE2; cfg.channels = channels; cfg.sample_rate = samples_per_second; cfg.use_apll = false; - //cfg.auto_clear = true; + cfg.auto_clear = true; cfg.buffer_size = 512; cfg.buffer_count = 16; kit.begin(cfg); diff --git a/examples/examples-communication/http-server/streams-audiokit-webserver_aac/streams-audiokit-webserver_aac.ino b/examples/examples-audiokit/streams-audiokit-webserver_aac/streams-audiokit-webserver_aac.ino similarity index 52% rename from examples/examples-communication/http-server/streams-audiokit-webserver_aac/streams-audiokit-webserver_aac.ino rename to examples/examples-audiokit/streams-audiokit-webserver_aac/streams-audiokit-webserver_aac.ino index f5cf208039..f282174fbc 100644 --- a/examples/examples-communication/http-server/streams-audiokit-webserver_aac/streams-audiokit-webserver_aac.ino +++ b/examples/examples-audiokit/streams-audiokit-webserver_aac/streams-audiokit-webserver_aac.ino @@ -8,42 +8,41 @@ */ #include "AudioTools.h" -#include "AudioTools/AudioLibs/AudioBoardStream.h" -#include "AudioTools/AudioCodecs/CodecAACFDK.h" +#include "AudioLibs/AudioKit.h" +#include "AudioCodecs/CodecAACFDK.h" -// WIFI -const char *ssid = "ssid"; -const char *password = "password"; - -AudioInfo info(16000,1,16); -AACEncoderFDK fdk; -AudioEncoderServer server(&fdk, ssid, password); -AudioBoardStream kit(AudioKitEs8388V1); +AudioKitStream kit; +AACEncoderFDK *fdk=nullptr; +AudioEncoderServer *server=nullptr; // Arduino setup void setup(){ Serial.begin(115200); // Defining Loglevels for the different libraries - //AudioToolsLogger.begin(Serial, AudioToolsLogLevel::Info); + //AudioLogger::instance().begin(Serial, AudioLogger::Info); //LOGLEVEL_FDK = FDKInfo; //LOGLEVEL_AUDIOKIT = AudioKitInfo; - // setup and configure fdk (not necessary if you activate PSRAM) - fdk.setAudioObjectType(2); // AAC low complexity - fdk.setOutputBufferSize(1024); // decrease output buffer size - fdk.setVariableBitrateMode(2); // low variable bitrate + // setup and configure fdk + fdk = new AACEncoderFDK(); + fdk->setAudioObjectType(2); // AAC low complexity + fdk->setOutputBufferSize(1024); // decrease output buffer size + fdk->setVariableBitrateMode(2); // low variable bitrate + server = new AudioEncoderServer(fdk,"WIFI","password"); // start i2s input with default configuration Serial.println("starting AudioKit..."); auto config = kit.defaultConfig(RX_MODE); - config.input_device = ADC_INPUT_LINE2; - config.copyFrom(info); + config.input_device = AUDIO_HAL_ADC_INPUT_LINE2; + config.sample_rate = 44100; + config.default_actions_active = false; + config.channels = 2; config.sd_active = false; kit.begin(config); Serial.println("AudioKit started"); // start data sink - server.begin(kit, info); + server->begin(kit, config); Serial.println("Server started"); } @@ -51,5 +50,5 @@ void setup(){ // Arduino loop void loop() { // Handle new connections - server.doLoop(); + server->doLoop(); } diff --git a/examples/examples-communication/http-server/streams-audiokit-webserver_wav/streams-audiokit-webserver_wav.ino b/examples/examples-audiokit/streams-audiokit-webserver_wav/streams-audiokit-webserver_wav.ino similarity index 79% rename from examples/examples-communication/http-server/streams-audiokit-webserver_wav/streams-audiokit-webserver_wav.ino rename to examples/examples-audiokit/streams-audiokit-webserver_wav/streams-audiokit-webserver_wav.ino index 0bc0a7a35d..3868d22f7b 100644 --- a/examples/examples-communication/http-server/streams-audiokit-webserver_wav/streams-audiokit-webserver_wav.ino +++ b/examples/examples-audiokit/streams-audiokit-webserver_wav/streams-audiokit-webserver_wav.ino @@ -8,20 +8,20 @@ */ #include "AudioTools.h" -#include "AudioTools/AudioLibs/AudioBoardStream.h" +#include "AudioLibs/AudioKit.h" AudioEncoderServer server(new WAVEncoder(),"ssid","password"); -AudioBoardStream kit(AudioKitEs8388V1); +AudioKitStream kit; // Arduino setup void setup(){ Serial.begin(115200); - AudioToolsLogger.begin(Serial, AudioToolsLogLevel::Warning); + AudioLogger::instance().begin(Serial, AudioLogger::Warning); // start i2s input with default configuration Serial.println("starting AudioKit..."); auto config = kit.defaultConfig(RX_MODE); - config.input_device = ADC_INPUT_LINE1; + config.input_device = AUDIO_HAL_ADC_INPUT_LINE1; config.sample_rate = 44100; config.sd_active = false; kit.begin(config); diff --git a/examples/examples-audiokit/streams-file_loop-audiokit/streams-file_loop-audiokit.ino b/examples/examples-audiokit/streams-file_loop-audiokit/streams-file_loop-audiokit.ino index 3e9649bb5b..a0324f7448 100644 --- a/examples/examples-audiokit/streams-file_loop-audiokit/streams-file_loop-audiokit.ino +++ b/examples/examples-audiokit/streams-file_loop-audiokit/streams-file_loop-audiokit.ino @@ -11,20 +11,20 @@ #include #include #include "AudioTools.h" -#include "AudioTools/AudioLibs/AudioBoardStream.h" -#include "AudioTools/Disk/FileLoop.h" -#include "AudioTools/AudioCodecs/CodecMP3Helix.h" +#include "AudioLibs/AudioKit.h" +#include "AudioLibs/FileLoop.h" +#include "AudioCodecs/CodecMP3Helix.h" const int chipSelect=PIN_AUDIO_KIT_SD_CARD_CS; -AudioBoardStream i2s(AudioKitEs8388V1); // final output of decoded stream +AudioKitStream i2s; // final output of decoded stream EncodedAudioStream decoder(&i2s, new MP3DecoderHelix()); // Decoding stream FileLoop loopingFile; StreamCopy copier(decoder, loopingFile); void setup(){ Serial.begin(115200); - AudioToolsLogger.begin(Serial, AudioToolsLogLevel::Info); + AudioLogger::instance().begin(Serial, AudioLogger::Info); // setup audiokit before SD! auto config = i2s.defaultConfig(TX_MODE); @@ -37,7 +37,6 @@ void setup(){ //loopingFile.setLoopCount(-1); // define loop count //audioFile.setStartPos(44); // restart from pos 44 //if ((audioFile.size()-44) % 1024!=0) audioFile.setSize((((audioFile.size()-44)/1024)+1)*1024+44); - loopingFile.begin(); // setup I2S based on sampling rate provided by decoder decoder.begin(); diff --git a/examples/examples-audiokit/streams-generator-audiokit/streams-generator-audiokit.ino b/examples/examples-audiokit/streams-generator-audiokit/streams-generator-audiokit.ino index b26d95ea2f..eeb713e28f 100644 --- a/examples/examples-audiokit/streams-generator-audiokit/streams-generator-audiokit.ino +++ b/examples/examples-audiokit/streams-generator-audiokit/streams-generator-audiokit.ino @@ -6,19 +6,19 @@ */ #include "AudioTools.h" -#include "AudioTools/AudioLibs/AudioBoardStream.h" +#include "AudioLibs/AudioKit.h" AudioInfo info(32000, 2, 16); SineWaveGenerator sineWave(32000); // subclass of SoundGenerator with max amplitude of 32000 GeneratedSoundStream sound(sineWave); // Stream generated from sine wave -AudioBoardStream out(AudioKitEs8388V1); +AudioKitStream out; StreamCopy copier(out, sound); // copies sound into i2s // Arduino Setup void setup(void) { // Open Serial Serial.begin(115200); - AudioToolsLogger.begin(Serial, AudioToolsLogLevel::Info); + AudioLogger::instance().begin(Serial, AudioLogger::Info); // start I2S Serial.println("starting I2S..."); diff --git a/examples/examples-audiokit/streams-generator_fromarray-audiokit/streams-generator_fromarray-audiokit.ino b/examples/examples-audiokit/streams-generator_fromarray-audiokit/streams-generator_fromarray-audiokit.ino index 0916c9feaa..71b0afced3 100644 --- a/examples/examples-audiokit/streams-generator_fromarray-audiokit/streams-generator_fromarray-audiokit.ino +++ b/examples/examples-audiokit/streams-generator_fromarray-audiokit/streams-generator_fromarray-audiokit.ino @@ -6,13 +6,13 @@ */ #include "AudioTools.h" -#include "AudioTools/AudioLibs/AudioBoardStream.h" +#include "AudioLibs/AudioKit.h" AudioInfo info(44100, 2, 16); int16_t sine_array[] = {0, 2285, 4560, 6812, 9031, 11206, 13327, 15383, 17363, 19259, 21062, 22761, 24350, 25820, 27165, 28377, 29450, 30381, 31163, 31793, 32269, 32587, 32747, 32747, 32587, 32269, 31793, 31163, 30381, 29450, 28377, 27165, 25820, 24350, 22761, 21062, 19259, 17363, 15383, 13327, 11206, 9031, 6812, 4560, 2285, 0, -2285, -4560, -6812, -9031, -11206, -13327, -15383, -17363, -19259, -21062, -22761, -24350, -25820, -27165, -28377, -29450, -30381, -31163, -31793, -32269, -32587, -32747, -32747, -32587, -32269, -31793, -31163, -30381, -29450, -28377, -27165, -25820, -24350, -22761, -21062, -19259, -17363, -15383, -13327, -11206, -9031, -6812, -4560, -2285 }; GeneratorFromArray sineWave(sine_array,0,false); GeneratedSoundStream sound(sineWave); // Stream generated from sine wave -AudioBoardStream out(AudioKitEs8388V1); +AudioKitStream out; StreamCopy copier(out, sound); // copies sound into i2s @@ -20,7 +20,7 @@ StreamCopy copier(out, sound); // copies sound into void setup(void) { // Open Serial Serial.begin(115200); - AudioToolsLogger.begin(Serial, AudioToolsLogLevel::Warning); + AudioLogger::instance().begin(Serial, AudioLogger::Warning); // start I2S Serial.println("starting I2S..."); diff --git a/examples/examples-audiokit/streams-generator_inputmixer-audiokit/streams-generator_inputmixer-audiokit.ino b/examples/examples-audiokit/streams-generator_inputmixer-audiokit/streams-generator_inputmixer-audiokit.ino index 59ffc5d4fe..071ed9d483 100644 --- a/examples/examples-audiokit/streams-generator_inputmixer-audiokit/streams-generator_inputmixer-audiokit.ino +++ b/examples/examples-audiokit/streams-generator_inputmixer-audiokit/streams-generator_inputmixer-audiokit.ino @@ -6,7 +6,7 @@ */ #include "AudioTools.h" -#include "AudioTools/AudioLibs/AudioBoardStream.h" +#include "AudioLibs/AudioKit.h" AudioInfo info(32000, 2, 16); SineWaveGenerator sineWave1(32000); // subclass of SoundGenerator with max amplitude of 32000 @@ -14,14 +14,15 @@ SineWaveGenerator sineWave2(32000); // subclass of Sound GeneratedSoundStream sound1(sineWave1); // Stream generated from sine wave GeneratedSoundStream sound2(sineWave2); // Stream generated from sine wave InputMixer mixer; -AudioBoardStream out(AudioKitEs8388V1); +AudioKitStream out; StreamCopy copier(out, mixer); // copies sound into i2s +AudioInfo info; // Arduino Setup void setup(void) { // Open Serial Serial.begin(115200); - AudioToolsLogger.begin(Serial, AudioToolsLogLevel::Info); + AudioLogger::instance().begin(Serial, AudioLogger::Info); // start I2S Serial.println("starting I2S..."); diff --git a/examples/examples-audiokit/streams-generator_outputmixer-audiokit/streams-generator_outputmixer-audiokit.ino b/examples/examples-audiokit/streams-generator_outputmixer-audiokit/streams-generator_outputmixer-audiokit.ino index 1def1bfdeb..6d01af6fd4 100644 --- a/examples/examples-audiokit/streams-generator_outputmixer-audiokit/streams-generator_outputmixer-audiokit.ino +++ b/examples/examples-audiokit/streams-generator_outputmixer-audiokit/streams-generator_outputmixer-audiokit.ino @@ -6,23 +6,24 @@ */ #include "AudioTools.h" -#include "AudioTools/AudioLibs/AudioBoardStream.h" +#include "AudioLibs/AudioKit.h" AudioInfo info(32000, 2, 16); SineWaveGenerator sineWave1(32000); // subclass of SoundGenerator with max amplitude of 32000 SineWaveGenerator sineWave2(32000); // subclass of SoundGenerator with max amplitude of 32000 GeneratedSoundStream sound1(sineWave1); // Stream generated from sine wave GeneratedSoundStream sound2(sineWave2); // Stream generated from sine wave -AudioBoardStream out(AudioKitEs8388V1); -OutputMixer mixer(out, 2); // output mixer with 2 outputs mixing to AudioBoardStream +AudioKitStream out; +OutputMixer mixer(out, 2); // output mixer with 2 outputs mixing to AudioKitStream StreamCopy copier1(mixer, sound1); // copies sound into mixer StreamCopy copier2(mixer, sound2); // copies sound into mixer +AudioInfo info; // Arduino Setup void setup(void) { // Open Serial Serial.begin(115200); - AudioToolsLogger.begin(Serial, AudioToolsLogLevel::Warning); + AudioLogger::instance().begin(Serial, AudioLogger::Warning); // start I2S Serial.println("starting I2S..."); diff --git a/examples/examples-audiokit/streams-generator_sinfromtable-audiokit/streams-generator_sinfromtable-audiokit.ino b/examples/examples-audiokit/streams-generator_sinfromtable-audiokit/streams-generator_sinfromtable-audiokit.ino index 1a5b5c7ea8..e0720fa08e 100644 --- a/examples/examples-audiokit/streams-generator_sinfromtable-audiokit/streams-generator_sinfromtable-audiokit.ino +++ b/examples/examples-audiokit/streams-generator_sinfromtable-audiokit/streams-generator_sinfromtable-audiokit.ino @@ -6,12 +6,12 @@ */ #include "AudioTools.h" -#include "AudioTools/AudioLibs/AudioBoardStream.h" +#include "AudioLibs/AudioKit.h" AudioInfo info(32000, 2, 16); SineFromTable sineWave(32000); // subclass of SoundGenerator with max amplitude of 32000 GeneratedSoundStream sound(sineWave); // Stream generated from sine wave -AudioBoardStream out(AudioKitEs8388V1); +AudioKitStream out; //CsvOutput out(Serial); int sound_len=1024; StreamCopy copier(out, sound, sound_len); // copies sound into i2s @@ -21,7 +21,7 @@ int freq = 122; void setup(void) { // Open Serial Serial.begin(115200); - AudioToolsLogger.begin(Serial, AudioToolsLogLevel::Warning); + AudioLogger::instance().begin(Serial, AudioLogger::Warning); // start I2S Serial.println("starting I2S..."); diff --git a/examples/examples-audiokit/streams-memory_mp3-audiokit/streams-memory_mp3-audiokit.ino b/examples/examples-audiokit/streams-memory_mp3-audiokit/streams-memory_mp3-audiokit.ino index b58e537787..6d97696355 100644 --- a/examples/examples-audiokit/streams-memory_mp3-audiokit/streams-memory_mp3-audiokit.ino +++ b/examples/examples-audiokit/streams-memory_mp3-audiokit/streams-memory_mp3-audiokit.ino @@ -10,19 +10,19 @@ */ #include "AudioTools.h" -#include "AudioTools/AudioCodecs/CodecMP3Helix.h" -#include "AudioTools/AudioLibs/AudioBoardStream.h" +#include "AudioCodecs/CodecMP3Helix.h" +#include "AudioLibs/AudioKit.h" #include "zero.h" MemoryStream mp3(zero_mp3, zero_mp3_len); -AudioBoardStream i2s(AudioKitEs8388V1); +AudioKitStream i2s; MP3DecoderHelix helix; EncodedAudioStream out(&i2s, &helix); // output to decoder StreamCopy copier(out, mp3); // copy in to i2s void setup(){ Serial.begin(115200); - AudioToolsLogger.begin(Serial, AudioToolsLogLevel::Info); + AudioLogger::instance().begin(Serial, AudioLogger::Info); // begin processing auto cfg = i2s.defaultConfig(); diff --git a/examples/examples-audiokit/streams-memory_pcm-mixer-audiokit/streams-memory_pcm-mixer-audiokit.ino b/examples/examples-audiokit/streams-memory_pcm-mixer-audiokit/streams-memory_pcm-mixer-audiokit.ino index 8d8e9c94d2..d492b640ff 100644 --- a/examples/examples-audiokit/streams-memory_pcm-mixer-audiokit/streams-memory_pcm-mixer-audiokit.ino +++ b/examples/examples-audiokit/streams-memory_pcm-mixer-audiokit/streams-memory_pcm-mixer-audiokit.ino @@ -9,19 +9,17 @@ * */ #include "AudioTools.h" -#include "AudioTools/AudioLibs/AudioBoardStream.h" +#include "AudioLibs/AudioKit.h" #include "drums.h" #include "guitar.h" InputMixer mixer; -AudioBoardStream kit(AudioKitEs8388V1); +AudioKitStream kit; MemoryStream drums(drums_raw, drums_raw_len); MemoryStream guitar(guitar_raw, guitar_raw_len); StreamCopy copier(kit, mixer); void setup() { - Serial.begin(115200); - AudioToolsLogger.begin(Serial, AudioToolsLogLevel::Info); // auto restart when MemoryStream has ended drums.setLoop(true); diff --git a/examples/examples-audiokit/streams-pins-audiokit/README.md b/examples/examples-audiokit/streams-pins-audiokit/README.md index d4afb88957..f8097ca8e1 100644 --- a/examples/examples-audiokit/streams-pins-audiokit/README.md +++ b/examples/examples-audiokit/streams-pins-audiokit/README.md @@ -13,6 +13,6 @@ You just need to define you __handler methods__ (button1, button2...) and assign You need to install the following libraries: - [Arduino Audio Tools](https://github.com/pschatzmann/arduino-audio-tools) -- [Audio Driver](https://github.com/pschatzmann/arduino-audio-driver) +- [AudioKit](https://github.com/pschatzmann/arduino-audiokit) - [FLITE](https://github.com/pschatzmann/arduino-flite) diff --git a/examples/examples-audiokit/streams-pins-audiokit/streams-pins-audiokit.ino b/examples/examples-audiokit/streams-pins-audiokit/streams-pins-audiokit.ino index f285a09a1b..7ec8e1ec6f 100644 --- a/examples/examples-audiokit/streams-pins-audiokit/streams-pins-audiokit.ino +++ b/examples/examples-audiokit/streams-pins-audiokit/streams-pins-audiokit.ino @@ -7,10 +7,10 @@ */ #include "AudioTools.h" -#include "AudioTools/AudioLibs/AudioBoardStream.h" +#include "AudioLibs/AudioKit.h" #include "flite_arduino.h" -AudioBoardStream kit(AudioKitEs8388V1); +AudioKitStream kit; Flite flite(kit); void button1(bool, int, void*) { flite.say("Button One"); } @@ -21,7 +21,7 @@ void button4(bool, int, void*) { flite.say("Button Four"); } // Arduino setup void setup() { Serial.begin(115200); - AudioToolsLogger.begin(Serial, AudioToolsLogLevel::Warning); + AudioLogger::instance().begin(Serial, AudioLogger::Warning); //AUDIOKIT_LOG_LEVEL = AudioKitDebug; auto cfg = kit.defaultConfig(TX_MODE); @@ -32,16 +32,16 @@ void setup() { kit.begin(cfg); // Assign pins to methods - kit.addAction(kit.getKey(1), button1); - kit.addAction(kit.getKey(2), button2); - kit.addAction(kit.getKey(3), button3); - kit.addAction(kit.getKey(4), button4); + kit.addAction(PIN_KEY1, button1); + kit.addAction(PIN_KEY2, button2); + kit.addAction(PIN_KEY3, button3); + kit.addAction(PIN_KEY4, button4); // example with actions using lambda expression - auto down = [](bool,int,void*) { AudioBoardStream::actionVolumeDown(true, -1, nullptr); flite.say("Volume down"); }; - kit.addAction(kit.getKey(5), down); - auto up = [](bool,int,void*) { AudioBoardStream::actionVolumeUp(true, -1, nullptr ); flite.say("Volume up"); }; - kit.addAction(kit.getKey(6), up); + auto down = [](bool,int,void*) { AudioKitStream::actionVolumeDown(true, -1, nullptr); flite.say("Volume down"); }; + kit.addAction(PIN_KEY5, down); + auto up = [](bool,int,void*) { AudioKitStream::actionVolumeUp(true, -1, nullptr ); flite.say("Volume up"); }; + kit.addAction(PIN_KEY6, up); flite.say("Please push a button"); } diff --git a/examples/examples-audiokit/streams-sd_mp3-audiokit/streams-sd_mp3-audiokit.ino b/examples/examples-audiokit/streams-sd-audiokit/streams-sd-audiokit.ino similarity index 78% rename from examples/examples-audiokit/streams-sd_mp3-audiokit/streams-sd_mp3-audiokit.ino rename to examples/examples-audiokit/streams-sd-audiokit/streams-sd-audiokit.ino index 65ccb94081..27b39f39ac 100644 --- a/examples/examples-audiokit/streams-sd_mp3-audiokit/streams-sd_mp3-audiokit.ino +++ b/examples/examples-audiokit/streams-sd-audiokit/streams-sd-audiokit.ino @@ -11,19 +11,19 @@ #include #include #include "AudioTools.h" -#include "AudioTools/AudioLibs/AudioBoardStream.h" -#include "AudioTools/AudioCodecs/CodecMP3Helix.h" +#include "AudioLibs/AudioKit.h" +#include "AudioCodecs/CodecMP3Helix.h" const int chipSelect=PIN_AUDIO_KIT_SD_CARD_CS; -AudioBoardStream i2s(AudioKitEs8388V1); // final output of decoded stream +AudioKitStream i2s; // final output of decoded stream EncodedAudioStream decoder(&i2s, new MP3DecoderHelix()); // Decoding stream StreamCopy copier; File audioFile; void setup(){ Serial.begin(115200); - AudioToolsLogger.begin(Serial, AudioToolsLogLevel::Info); + AudioLogger::instance().begin(Serial, AudioLogger::Info); // setup audiokit before SD! auto config = i2s.defaultConfig(TX_MODE); @@ -38,6 +38,7 @@ void setup(){ decoder.begin(); // begin copy + copier.setCheckAvailableForWrite(false); copier.begin(decoder, audioFile); } diff --git a/examples/examples-audiokit/streams-sd_flac-audiokit/streams-sd_flac-audiokit.ino b/examples/examples-audiokit/streams-sd_flac-audiokit/streams-sd_flac-audiokit.ino deleted file mode 100644 index 39009c8835..0000000000 --- a/examples/examples-audiokit/streams-sd_flac-audiokit/streams-sd_flac-audiokit.ino +++ /dev/null @@ -1,45 +0,0 @@ -/** - * @file streams-sd-audiokit.ino - * @author Phil Schatzmann - * @brief Just a small demo, how to use files with the SD library with a streaming decoder - * @version 0.1 - * @date 2022-10-09 - * - * @copyright Copyright (c) 2022 - * - */ -#include -#include -#include "AudioTools.h" -#include "AudioTools/AudioLibs/AudioBoardStream.h" -#include "AudioTools/AudioCodecs/CodecFLAC.h" - - -const int chipSelect=PIN_AUDIO_KIT_SD_CARD_CS; -AudioBoardStream i2s(AudioKitEs8388V1); // final output of decoded stream -FLACDecoder dec; -File audioFile; - -void setup(){ - Serial.begin(115200); - AudioToolsLogger.begin(Serial, AudioToolsLogLevel::Info); - - // setup audiokit before SD! - auto config = i2s.defaultConfig(TX_MODE); - config.sd_active = true; - i2s.begin(config); - - // setup file - SD.begin(chipSelect); - audioFile = SD.open("/flac/test2.flac"); - - // setup decoder - dec.setInput(audioFile); - dec.setOutput(i2s); - dec.begin(); - -} - -void loop(){ - dec.copy(); -} \ No newline at end of file diff --git a/examples/examples-audiokit/streams-sd_m4a-audiokit/streams-sd_m4a-audiokit.ino b/examples/examples-audiokit/streams-sd_m4a-audiokit/streams-sd_m4a-audiokit.ino deleted file mode 100644 index 49269acd09..0000000000 --- a/examples/examples-audiokit/streams-sd_m4a-audiokit/streams-sd_m4a-audiokit.ino +++ /dev/null @@ -1,65 +0,0 @@ -/** -* @file streams-sd_m4a-audiokit.ino -* @author Peter Schatzmann -* @brief Example for decoding M4A files on the AudioKit using the AudioBoardStream -* @version 0.1 -* @date 2023-10-01 -*/ - -#include "AudioTools.h" -#include "AudioTools/AudioCodecs/CodecALAC.h" -#include "AudioTools/AudioCodecs/CodecAACHelix.h" -#include "AudioTools/AudioCodecs/ContainerM4A.h" -#include "AudioTools/AudioCodecs/MultiDecoder.h" -#include "AudioTools/AudioLibs/AudioBoardStream.h" // install https://github.com/pschatzmann/arduino-audio-driver -#include "SD.h" - -MultiDecoder multi_decoder; -ContainerM4A dec_m4a(multi_decoder); -AACDecoderHelix dec_aac; -DecoderALAC dec_alac; -AudioBoardStream out(AudioKitEs8388V1); -EncodedAudioOutput decoder_output(&out, &dec_m4a); -File file; -StreamCopy copier(decoder_output, file); - -void setup() { - Serial.begin(115200); - AudioToolsLogger.begin(Serial, AudioToolsLogLevel::Info); - - // start AudioBoard with setup of CD pins - auto cfg = out.defaultConfig(TX_MODE); - cfg.sd_active = true; - if (!out.begin(cfg)){ - Serial.println("Failed to start CSV output!"); - return; - } - - if (!SD.begin(PIN_AUDIO_KIT_SD_CARD_CS)){ - Serial.println("SD Card initialization failed!"); - return; - } - - file = SD.open("/m4a/aac.m4a"); - if (!file) { - Serial.println("Failed to open file!"); - return; - } - - // mp4 supports alac and aac - multi_decoder.addDecoder(dec_alac,"audio/alac"); - multi_decoder.addDecoder(dec_aac,"audio/aac"); - - // start decoder output - if(!decoder_output.begin()) { - Serial.println("Failed to start decoder output!"); - return; - } - - Serial.println("M4A decoding started..."); -} - - -void loop() { - copier.copy(); -} \ No newline at end of file diff --git a/examples/examples-audiokit/streams-sdmmc_wav-audiokit/streams-sdmmc_wav-audiokit.ino b/examples/examples-audiokit/streams-sdmmc_wav-audiokit/streams-sdmmc_wav-audiokit.ino deleted file mode 100644 index 31a6a2d2d9..0000000000 --- a/examples/examples-audiokit/streams-sdmmc_wav-audiokit/streams-sdmmc_wav-audiokit.ino +++ /dev/null @@ -1,50 +0,0 @@ -/** - * @file streams-sdmmc_wav-audiokit.ino - * @author Phil Schatzmann - * @brief A simple example that shows how to play (eg. a 24bit) wav file - * @date 2021-11-07 - * - * @copyright Copyright (c) 2021 - */ - - -#include "FS.h" -#include "SD_MMC.h" -#include "AudioTools.h" -#include "AudioTools/AudioLibs/AudioBoardStream.h" - -AudioBoardStream i2s(AudioKitEs8388V1); -WAVDecoder wav; -EncodedAudioStream encoded(&i2s, &wav); // Decoding stream -File audioFile; -StreamCopy copier(encoded, audioFile); - -void setup(){ - Serial.begin(115200); - AudioToolsLogger.begin(Serial, AudioToolsLogLevel::Warning); - - // setup audiokit before SD! - auto config = i2s.defaultConfig(TX_MODE); - config.sd_active = false; - i2s.begin(config); - i2s.setVolume(1.0); - - // open sdmmc - if (!SD_MMC.begin("/sdcard", false)){ - Serial.println("SD_MMC Error"); - stop(); - } - // open file - audioFile = SD_MMC.open("/wav24/test.wav"); - if (!audioFile){ - Serial.println("File does not exist"); - stop(); - } - - // start decoder stream - encoded.begin(); -} - -void loop(){ - copier.copy(); -} \ No newline at end of file diff --git a/examples/examples-communication/a2dp/streams-synth-a2dp/README.md b/examples/examples-audiokit/streams-synth-a2dp/README.md similarity index 100% rename from examples/examples-communication/a2dp/streams-synth-a2dp/README.md rename to examples/examples-audiokit/streams-synth-a2dp/README.md diff --git a/examples/examples-communication/a2dp/streams-synth-a2dp/streams-synth-a2dp.ino b/examples/examples-audiokit/streams-synth-a2dp/streams-synth-a2dp.ino similarity index 66% rename from examples/examples-communication/a2dp/streams-synth-a2dp/streams-synth-a2dp.ino rename to examples/examples-audiokit/streams-synth-a2dp/streams-synth-a2dp.ino index 37a24e15a3..6b5be3eb4a 100644 --- a/examples/examples-communication/a2dp/streams-synth-a2dp/streams-synth-a2dp.ino +++ b/examples/examples-audiokit/streams-synth-a2dp/streams-synth-a2dp.ino @@ -6,17 +6,17 @@ */ #define USE_MIDI -#include "AudioTools.h" // must be first -#include "AudioTools/AudioLibs/AudioBoardStream.h" // https://github.com/pschatzmann/arduino-audio-driver -#include "BluetoothA2DPSource.h" // https://github.com/pschatzmann/ESP32-A2DP +#include "BluetoothA2DPSource.h" +#include "AudioTools.h" +#include "AudioLibs/AudioKit.h" BluetoothA2DPSource a2dp_source; int channels = 2; -AudioBoardStream kit(AudioKitEs8388V1); +AudioKitStream kit; Synthesizer synthesizer; GeneratedSoundStream in(synthesizer); -SynthesizerKey keys[] = {{kit.getKey(1), N_C3},{kit.getKey(2), N_D3},{kit.getKey(3), N_E3},{kit.getKey(4), N_F3},{kit.getKey(5), N_G3},{kit.getKey(6), N_A3},{0,0}}; +SynthesizerKey keys[] = {{PIN_KEY1, N_C3},{PIN_KEY2, N_D3},{PIN_KEY3, N_E3},{PIN_KEY4, N_F3},{PIN_KEY5, N_G3},{PIN_KEY6, N_A3},{0,0}}; int32_t get_sound_data(Frame *data, int32_t frameCount) { int frame_size = sizeof(int16_t)*channels; @@ -41,8 +41,7 @@ void setup() { cfg.sample_rate = 44100; in.begin(cfg); - a2dp_source.set_data_callback_in_frames(get_sound_data); - a2dp_source.start("LEXON MINO L"); + a2dp_source.start("LEXON MINO L", get_sound_data); a2dp_source.set_volume(20); } diff --git a/examples/examples-audiokit/streams-synth-audiokit/streams-synth-audiokit.ino b/examples/examples-audiokit/streams-synth-audiokit/streams-synth-audiokit.ino index 5a10942501..70e8a54191 100644 --- a/examples/examples-audiokit/streams-synth-audiokit/streams-synth-audiokit.ino +++ b/examples/examples-audiokit/streams-synth-audiokit/streams-synth-audiokit.ino @@ -6,13 +6,13 @@ */ #include "AudioTools.h" -#include "AudioTools/AudioLibs/AudioBoardStream.h" +#include "AudioLibs/AudioKit.h" -AudioBoardStream kit(AudioKitEs8388V1); +AudioKitStream kit; Synthesizer synthesizer; GeneratedSoundStream in(synthesizer); StreamCopy copier(kit, in); -SynthesizerKey keys[] = {{kit.getKey(1), N_C3},{kit.getKey(2), N_D3},{kit.getKey(3), N_E3},{kit.getKey(4), N_F3},{kit.getKey(5), N_G3},{kit.getKey(6), N_A3},{0,0}}; +SynthesizerKey keys[] = {{PIN_KEY1, N_C3},{PIN_KEY2, N_D3},{PIN_KEY3, N_E3},{PIN_KEY4, N_F3},{PIN_KEY5, N_G3},{PIN_KEY6, N_A3},{0,0}}; void setup() { Serial.begin(115200); diff --git a/examples/examples-audiokit/streams-synthbasic1-audiokit/streams-synthbasic1-audiokit.ino b/examples/examples-audiokit/streams-synthbasic1-audiokit/streams-synthbasic1-audiokit.ino index bb627585d8..fd1cbe3f55 100644 --- a/examples/examples-audiokit/streams-synthbasic1-audiokit/streams-synthbasic1-audiokit.ino +++ b/examples/examples-audiokit/streams-synthbasic1-audiokit/streams-synthbasic1-audiokit.ino @@ -6,9 +6,9 @@ * */ #include "AudioTools.h" -#include "AudioTools/AudioLibs/AudioBoardStream.h" +#include "AudioLibs/AudioKit.h" -AudioBoardStream kit(AudioKitEs8388V1); +AudioKitStream kit; SineWaveGenerator sine; GeneratedSoundStream in(sine); StreamCopy copier(kit, in); @@ -28,12 +28,12 @@ void setupActions(){ // assign buttons to notes auto act_low = AudioActions::ActiveLow; static float note[] = {N_C3, N_D3, N_E3, N_F3, N_G3, N_A3}; // frequencies - kit.audioActions().add(kit.getKey(1), actionKeyOn, actionKeyOff, act_low, &(note[0])); // C3 - kit.audioActions().add(kit.getKey(2), actionKeyOn, actionKeyOff, act_low, &(note[1])); // D3 - kit.audioActions().add(kit.getKey(3), actionKeyOn, actionKeyOff, act_low, &(note[2])); // E3 - kit.audioActions().add(kit.getKey(4), actionKeyOn, actionKeyOff, act_low, &(note[3])); // F3 - kit.audioActions().add(kit.getKey(5), actionKeyOn, actionKeyOff, act_low, &(note[4])); // G3 - kit.audioActions().add(kit.getKey(6), actionKeyOn, actionKeyOff, act_low, &(note[5])); // A3 + kit.audioActions().add(PIN_KEY1, actionKeyOn, actionKeyOff, act_low, &(note[0])); // C3 + kit.audioActions().add(PIN_KEY2, actionKeyOn, actionKeyOff, act_low, &(note[1])); // D3 + kit.audioActions().add(PIN_KEY3, actionKeyOn, actionKeyOff, act_low, &(note[2])); // E3 + kit.audioActions().add(PIN_KEY4, actionKeyOn, actionKeyOff, act_low, &(note[3])); // F3 + kit.audioActions().add(PIN_KEY5, actionKeyOn, actionKeyOff, act_low, &(note[4])); // G3 + kit.audioActions().add(PIN_KEY6, actionKeyOn, actionKeyOff, act_low, &(note[5])); // A3 } void setup() { diff --git a/examples/examples-audiokit/streams-synthbasic2-audiokit/streams-synthbasic2-audiokit.ino b/examples/examples-audiokit/streams-synthbasic2-audiokit/streams-synthbasic2-audiokit.ino index d9a0f66e00..f944c04534 100644 --- a/examples/examples-audiokit/streams-synthbasic2-audiokit/streams-synthbasic2-audiokit.ino +++ b/examples/examples-audiokit/streams-synthbasic2-audiokit/streams-synthbasic2-audiokit.ino @@ -7,9 +7,9 @@ * */ #include "AudioTools.h" -#include "AudioTools/AudioLibs/AudioBoardStream.h" +#include "AudioLibs/AudioKit.h" -AudioBoardStream kit(AudioKitEs8388V1); +AudioKitStream kit; SineWaveGenerator sine; GeneratedSoundStream sine_stream(sine); AudioEffectStream effects(sine_stream); @@ -33,12 +33,12 @@ void setupActions(){ // assign buttons to notes auto act_low = AudioActions::ActiveLow; static float note[] = {N_C3, N_D3, N_E3, N_F3, N_G3, N_A3}; // frequencies - kit.audioActions().add(kit.getKey(1), actionKeyOn, actionKeyOff, act_low, &(note[0])); // C3 - kit.audioActions().add(kit.getKey(2), actionKeyOn, actionKeyOff, act_low, &(note[1])); // D3 - kit.audioActions().add(kit.getKey(3), actionKeyOn, actionKeyOff, act_low, &(note[2])); // E3 - kit.audioActions().add(kit.getKey(4), actionKeyOn, actionKeyOff, act_low, &(note[3])); // F3 - kit.audioActions().add(kit.getKey(5), actionKeyOn, actionKeyOff, act_low, &(note[4])); // G3 - kit.audioActions().add(kit.getKey(6), actionKeyOn, actionKeyOff, act_low, &(note[5])); // A3 + kit.audioActions().add(PIN_KEY1, actionKeyOn, actionKeyOff, act_low, &(note[0])); // C3 + kit.audioActions().add(PIN_KEY2, actionKeyOn, actionKeyOff, act_low, &(note[1])); // D3 + kit.audioActions().add(PIN_KEY3, actionKeyOn, actionKeyOff, act_low, &(note[2])); // E3 + kit.audioActions().add(PIN_KEY4, actionKeyOn, actionKeyOff, act_low, &(note[3])); // F3 + kit.audioActions().add(PIN_KEY5, actionKeyOn, actionKeyOff, act_low, &(note[4])); // G3 + kit.audioActions().add(PIN_KEY6, actionKeyOn, actionKeyOff, act_low, &(note[5])); // A3 } void setup() { diff --git a/examples/examples-audiokit/streams-synthbasic3-audiokit/streams-synthbasic3-audiokit.ino b/examples/examples-audiokit/streams-synthbasic3-audiokit/streams-synthbasic3-audiokit.ino index a6a693cd64..957d8ffb11 100644 --- a/examples/examples-audiokit/streams-synthbasic3-audiokit/streams-synthbasic3-audiokit.ino +++ b/examples/examples-audiokit/streams-synthbasic3-audiokit/streams-synthbasic3-audiokit.ino @@ -8,14 +8,14 @@ */ #include "AudioTools.h" -#include "AudioTools/AudioLibs/AudioBoardStream.h" +#include "AudioLibs/AudioKit.h" #include SineWaveGenerator sine; GeneratedSoundStream sine_stream(sine); ADSRGain adsr(0.0001,0.0001, 0.9 , 0.0002); AudioEffectStream effects(sine_stream); -AudioBoardStream kit(AudioKitEs8388V1); +AudioKitStream kit; StreamCopy copier(kit, effects); class SynthAction : public MidiAction { @@ -52,12 +52,12 @@ void setupActions(){ // assign buttons to notes auto act_low = AudioActions::ActiveLow; static float note[] = {N_C3, N_D3, N_E3, N_F3, N_G3, N_A3}; // frequencies - kit.audioActions().add(kit.getKey(1), actionKeyOn, actionKeyOff, act_low, &(note[0])); // C3 - kit.audioActions().add(kit.getKey(2), actionKeyOn, actionKeyOff, act_low, &(note[1])); // D3 - kit.audioActions().add(kit.getKey(3), actionKeyOn, actionKeyOff, act_low, &(note[2])); // E3 - kit.audioActions().add(kit.getKey(4), actionKeyOn, actionKeyOff, act_low, &(note[3])); // F3 - kit.audioActions().add(kit.getKey(5), actionKeyOn, actionKeyOff, act_low, &(note[4])); // G3 - kit.audioActions().add(kit.getKey(6), actionKeyOn, actionKeyOff, act_low, &(note[5])); // A3 + kit.audioActions().add(PIN_KEY1, actionKeyOn, actionKeyOff, act_low, &(note[0])); // C3 + kit.audioActions().add(PIN_KEY2, actionKeyOn, actionKeyOff, act_low, &(note[1])); // D3 + kit.audioActions().add(PIN_KEY3, actionKeyOn, actionKeyOff, act_low, &(note[2])); // E3 + kit.audioActions().add(PIN_KEY4, actionKeyOn, actionKeyOff, act_low, &(note[3])); // F3 + kit.audioActions().add(PIN_KEY5, actionKeyOn, actionKeyOff, act_low, &(note[4])); // G3 + kit.audioActions().add(PIN_KEY6, actionKeyOn, actionKeyOff, act_low, &(note[5])); // A3 } void setup() { diff --git a/examples/examples-audiokit/streams-synthstk-audiokit/README.md b/examples/examples-audiokit/streams-synthstk-audiokit/README.md index f908a65f20..3bfb6b0f35 100644 --- a/examples/examples-audiokit/streams-synthstk-audiokit/README.md +++ b/examples/examples-audiokit/streams-synthstk-audiokit/README.md @@ -8,6 +8,6 @@ For [further info see my blog](https://www.pschatzmann.ch/home/2021/12/21/ai-thi ### Dependencies - https://github.com/pschatzmann/arduino-audio-tools -- https://github.com/pschatzmann/arduino-audio-driver +- https://github.com/pschatzmann/arduino-audiokit - https://github.com/pschatzmann/arduino-midi - https://github.com/pschatzmann/Arduino-STK diff --git a/examples/examples-audiokit/streams-synthstk-audiokit/streams-synthstk-audiokit.ino b/examples/examples-audiokit/streams-synthstk-audiokit/streams-synthstk-audiokit.ino index fb92df2468..3569e3f800 100644 --- a/examples/examples-audiokit/streams-synthstk-audiokit/streams-synthstk-audiokit.ino +++ b/examples/examples-audiokit/streams-synthstk-audiokit/streams-synthstk-audiokit.ino @@ -5,10 +5,10 @@ * @copyright Copyright (c) 2021 */ #include "AudioTools.h" -#include "AudioTools/AudioLibs/AudioBoardStream.h" +#include "AudioLibs/AudioKit.h" #include "StkAll.h" -AudioBoardStream kit(AudioKitEs8388V1); +AudioKitStream kit; Clarinet clarinet(440); Voicer voicer; ArdStreamOut output(&kit); @@ -16,12 +16,12 @@ float noteAmplitude = 128; int group = 0; void actionKeyOn(bool active, int pin, void* ptr){ - float note = *((float*)ptr); + int note = *((int*)ptr); voicer.noteOn(note, noteAmplitude, group); } void actionKeyOff(bool active, int pin, void* ptr){ - float note = *((float*)ptr); + int note = *((int*)ptr); voicer.noteOff(note, noteAmplitude, group); } @@ -30,12 +30,12 @@ void setupActions(){ // assign buttons to notes auto act_low = AudioActions::ActiveLow; static int note[] = {48,50,52,53,55,57}; // midi keys - kit.audioActions().add(kit.getKey(1), actionKeyOn, actionKeyOff, act_low, &(note[0])); // C3 - kit.audioActions().add(kit.getKey(2), actionKeyOn, actionKeyOff, act_low, &(note[1])); // D3 - kit.audioActions().add(kit.getKey(3), actionKeyOn, actionKeyOff, act_low, &(note[2])); // E3 - kit.audioActions().add(kit.getKey(4), actionKeyOn, actionKeyOff, act_low, &(note[3])); // F3 - kit.audioActions().add(kit.getKey(5), actionKeyOn, actionKeyOff, act_low, &(note[4])); // G3 - kit.audioActions().add(kit.getKey(6), actionKeyOn, actionKeyOff, act_low, &(note[5])); // A3 + kit.audioActions().add(PIN_KEY1, actionKeyOn, actionKeyOff, act_low, &(note[0])); // C3 + kit.audioActions().add(PIN_KEY2, actionKeyOn, actionKeyOff, act_low, &(note[1])); // D3 + kit.audioActions().add(PIN_KEY3, actionKeyOn, actionKeyOff, act_low, &(note[2])); // E3 + kit.audioActions().add(PIN_KEY4, actionKeyOn, actionKeyOff, act_low, &(note[3])); // F3 + kit.audioActions().add(PIN_KEY5, actionKeyOn, actionKeyOff, act_low, &(note[4])); // G3 + kit.audioActions().add(PIN_KEY6, actionKeyOn, actionKeyOff, act_low, &(note[5])); // A3 } void setup() { diff --git a/examples/examples-audiokit/streams-tf-audiokit/README.md b/examples/examples-audiokit/streams-tf-audiokit/README.md index 54f2564410..db28b232a7 100644 --- a/examples/examples-audiokit/streams-tf-audiokit/README.md +++ b/examples/examples-audiokit/streams-tf-audiokit/README.md @@ -18,5 +18,5 @@ The log level has been set to Info to help you to identify any problems. Please You need to install the following libraries: - https://github.com/pschatzmann/arduino-audio-tools -- https://github.com/pschatzmann/arduino-audio-driver +- https://github.com/pschatzmann/arduino-audiokit - https://github.com/pschatzmann/tflite-micro-arduino-examples \ No newline at end of file diff --git a/examples/examples-audiokit/streams-tf-audiokit/streams-tf-audiokit.ino b/examples/examples-audiokit/streams-tf-audiokit/streams-tf-audiokit.ino index d34d5d5ffb..34a5f158b4 100644 --- a/examples/examples-audiokit/streams-tf-audiokit/streams-tf-audiokit.ino +++ b/examples/examples-audiokit/streams-tf-audiokit/streams-tf-audiokit.ino @@ -9,13 +9,13 @@ * */ #include "AudioTools.h" -#include "AudioTools/AudioLibs/TfLiteAudioStream.h" -#include "AudioTools/AudioLibs/AudioBoardStream.h" +#include "AudioLibs/TfLiteAudioStream.h" +#include "AudioLibs/AudioKit.h" #include "model.h" TfLiteSineReader tf_reader(20000,0.3); // Audio generation logic TfLiteAudioStream tf_stream; // Audio source -> no classification so N is 0 -AudioBoardStream i2s(AudioKitEs8388V1); // Audio destination +AudioKitStream i2s; // Audio destination StreamCopy copier(i2s, tf_stream); // copy tf_stream to i2s int channels = 1; int samples_per_second = 16000; @@ -23,7 +23,7 @@ int samples_per_second = 16000; void setup() { Serial.begin(115200); - AudioToolsLogger.begin(Serial, AudioToolsLogLevel::Warning); + AudioLogger::instance().begin(Serial, AudioLogger::Warning); // Setup tensorflow input auto tcfg = tf_stream.defaultConfig(); diff --git a/examples/examples-communication/http-client/streams-url_aac-audiokit/README.md b/examples/examples-audiokit/streams-url_aac-audiokit/README.md similarity index 100% rename from examples/examples-communication/http-client/streams-url_aac-audiokit/README.md rename to examples/examples-audiokit/streams-url_aac-audiokit/README.md diff --git a/examples/examples-communication/http-client/streams-url_aac-audiokit/streams-url_aac-audiokit.ino b/examples/examples-audiokit/streams-url_aac-audiokit/streams-url_aac-audiokit.ino similarity index 78% rename from examples/examples-communication/http-client/streams-url_aac-audiokit/streams-url_aac-audiokit.ino rename to examples/examples-audiokit/streams-url_aac-audiokit/streams-url_aac-audiokit.ino index 3ba8b078e4..874403f32e 100644 --- a/examples/examples-communication/http-client/streams-url_aac-audiokit/streams-url_aac-audiokit.ino +++ b/examples/examples-audiokit/streams-url_aac-audiokit/streams-url_aac-audiokit.ino @@ -11,19 +11,19 @@ // install https://github.com/pschatzmann/arduino-libhelix.git #include "AudioTools.h" -#include "AudioTools/AudioCodecs/CodecAACHelix.h" -#include "AudioTools/AudioLibs/AudioBoardStream.h" +#include "AudioCodecs/CodecAACHelix.h" +#include "AudioLibs/AudioKit.h" URLStream url("http://github.com/ssid%22,%22password"); // or replace with ICYStream to get metadata -AudioBoardStream i2s(AudioKitEs8388V1); // final output of decoded stream +AudioKitStream i2s; // final output of decoded stream EncodedAudioStream dec(&i2s, new AACDecoderHelix()); // Decoding stream StreamCopy copier(dec, url); // copy url to decoder void setup(){ Serial.begin(115200); - AudioToolsLogger.begin(Serial, AudioToolsLogLevel::Info); + AudioLogger::instance().begin(Serial, AudioLogger::Info); // setup i2s auto config = i2s.defaultConfig(TX_MODE); diff --git a/examples/examples-communication/http-client/streams-url_mp3-audiokit/README.md b/examples/examples-audiokit/streams-url_mp3-audiokit/README.md similarity index 100% rename from examples/examples-communication/http-client/streams-url_mp3-audiokit/README.md rename to examples/examples-audiokit/streams-url_mp3-audiokit/README.md diff --git a/examples/examples-communication/http-client/streams-url_mp3-audiokit/streams-url_mp3-audiokit.ino b/examples/examples-audiokit/streams-url_mp3-audiokit/streams-url_mp3-audiokit.ino similarity index 77% rename from examples/examples-communication/http-client/streams-url_mp3-audiokit/streams-url_mp3-audiokit.ino rename to examples/examples-audiokit/streams-url_mp3-audiokit/streams-url_mp3-audiokit.ino index c3a80676f2..f9f2fd062a 100644 --- a/examples/examples-communication/http-client/streams-url_mp3-audiokit/streams-url_mp3-audiokit.ino +++ b/examples/examples-audiokit/streams-url_mp3-audiokit/streams-url_mp3-audiokit.ino @@ -11,19 +11,19 @@ // install https://github.com/pschatzmann/arduino-libhelix.git #include "AudioTools.h" -#include "AudioTools/AudioCodecs/CodecMP3Helix.h" -#include "AudioTools/AudioLibs/AudioBoardStream.h" +#include "AudioCodecs/CodecMP3Helix.h" +#include "AudioLibs/AudioKit.h" URLStream url("http://github.com/ssid%22,%22password"); // or replace with ICYStream to get metadata -AudioBoardStream i2s(AudioKitEs8388V1); // final output of decoded stream +AudioKitStream i2s; // final output of decoded stream EncodedAudioStream dec(&i2s, new MP3DecoderHelix()); // Decoding stream StreamCopy copier(dec, url); // copy url to decoder void setup(){ Serial.begin(115200); - AudioToolsLogger.begin(Serial, AudioToolsLogLevel::Info); + AudioLogger::instance().begin(Serial, AudioLogger::Info); // setup i2s auto config = i2s.defaultConfig(TX_MODE); diff --git a/examples/examples-basic-api/base-SynchronizedBufferRTOS/base-SynchronizedBufferRTOS.ino b/examples/examples-basic-api/base-SynchronizedBufferRTOS/base-SynchronizedBufferRTOS.ino index 74801edf79..b769d3a48e 100644 --- a/examples/examples-basic-api/base-SynchronizedBufferRTOS/base-SynchronizedBufferRTOS.ino +++ b/examples/examples-basic-api/base-SynchronizedBufferRTOS/base-SynchronizedBufferRTOS.ino @@ -1,5 +1,5 @@ /** - * @file base-BufferRTOS.ino + * @file base-SynchronizedBufferRTOS.ino * @author Phil Schatzmann * @brief Data provider on core 0 with data consumer on core 1 * @version 0.1 @@ -9,11 +9,11 @@ * */ #include "AudioTools.h" -#include "AudioTools/AudioLibs/Concurrency.h" // https://github.com/pschatzmann/arduino-freertos-addons +#include "freertos-all.h" // https://github.com/pschatzmann/arduino-freertos-addons -BufferRTOS buffer(1024); +SynchronizedBufferRTOS buffer(1024); void doWrite(); // forward declaration -Task writeTask("write",5000,10, 0); // FreeRTOS task from addons +Task writeTask("write",5000,10, doWrite); // FreeRTOS task from addons // create data and write it to buffer void doWrite() { @@ -27,10 +27,10 @@ void doWrite() { void setup(){ // Setup logging Serial.begin(115200); - AudioToolsLogger.begin(Serial, AudioToolsLogLevel::Info); + AudioLogger::instance().begin(Serial, AudioLogger::Info); // start on core 0 - writeTask.begin(doWrite); + writeTask.Start(0); } // The loop runs on core 1: We read back the data diff --git a/examples/examples-communication/a2dp/basic-adc-a2dp/README.md b/examples/examples-basic-api/base-adc-a2dp/README.md similarity index 100% rename from examples/examples-communication/a2dp/basic-adc-a2dp/README.md rename to examples/examples-basic-api/base-adc-a2dp/README.md diff --git a/examples/examples-communication/a2dp/basic-adc-a2dp/basic-adc-a2dp.ino b/examples/examples-basic-api/base-adc-a2dp/base-adc-a2dp.ino similarity index 75% rename from examples/examples-communication/a2dp/basic-adc-a2dp/basic-adc-a2dp.ino rename to examples/examples-basic-api/base-adc-a2dp/base-adc-a2dp.ino index 63bbe47e52..9ab807ca03 100644 --- a/examples/examples-communication/a2dp/basic-adc-a2dp/basic-adc-a2dp.ino +++ b/examples/examples-basic-api/base-adc-a2dp/base-adc-a2dp.ino @@ -1,15 +1,16 @@ /** - * @file basic-adc-a2dp.ino + * @file adc-a2dp.ino * @author Phil Schatzmann - * @brief see https://github.com/pschatzmann/arduino-audio-tools/blob/main/examples/examples-communication/a2dp/basic-adc-a2dp/README.md + * @brief see https://github.com/pschatzmann/arduino-audio-tools/blob/main/examples/adc-a2dp/README.md * * @author Phil Schatzmann * @copyright GPLv3 * */ -#include "AudioTools.h" +#include "Arduino.h" #include "BluetoothA2DPSource.h" +#include "AudioTools.h" /** * @brief We use a mcp6022 analog microphone as input and send the data to A2DP @@ -17,12 +18,15 @@ AnalogAudioStream adc; BluetoothA2DPSource a2dp_source; +// The data has a center of around 26427, so we we need to shift it down to bring the center to 0 +ConverterScaler scaler(1.0, -26427, 32700 ); // callback used by A2DP to provide the sound data int32_t get_sound_data(Frame* frames, int32_t frameCount) { uint8_t *data = (uint8_t*)frames; int frameSize = 4; size_t resultBytes = adc.readBytes(data, frameCount*frameSize); + scaler.convert(data, resultBytes); return resultBytes/frameSize; } @@ -37,11 +41,9 @@ void setup(void) { // start the bluetooth Serial.println("starting A2DP..."); a2dp_source.set_auto_reconnect(false); - a2dp_source.set_data_callback_in_frames(get_sound_data); - a2dp_source.start("MyMusic"); + a2dp_source.start("MyMusic", get_sound_data); } // Arduino loop - repeated processing void loop() { - delay(1000); } \ No newline at end of file diff --git a/examples/examples-basic-api/base-adc-average-mono-serial/base-adc-average-mono-serial.ino b/examples/examples-basic-api/base-adc-average-mono-serial/base-adc-average-mono-serial.ino deleted file mode 100644 index 12fc9ca771..0000000000 --- a/examples/examples-basic-api/base-adc-average-mono-serial/base-adc-average-mono-serial.ino +++ /dev/null @@ -1,86 +0,0 @@ -/** - * @file base-adc-average-mono-serial.ino - * @brief Attempts down sampling with binning of a mono audio signal by AVG_LEN - * @author Urs Utzinger - * @copyright GPLv3 - **/ - -#include "Arduino.h" -#include "AudioTools.h" - -// On board analog to digital converter -AnalogAudioStream analog_in; - -// Serial terminal output -CsvOutput serial_out(Serial); - -#define BAUD_RATE 500000 -#define SAMPLE_RATE 44100 -#define BUFFER_SIZE 256 -#define AVG_LEN 64 -#define BYTES_PER_SAMPLE sizeof(int16_t) - -// buffer to read samples and store the average samples -int16_t buffer[BUFFER_SIZE]; - -void setup() { - - delay(3000); // wait for serial to become available - - // Serial Interface - Serial.begin(BAUD_RATE); - - // Include logging to serial - AudioToolsLogger.begin(Serial, AudioToolsLogLevel::Error); // Debug, Warning, Info, Error - - // Start ADC input - Serial.println("Starting ADC..."); - auto adcConfig = analog_in.defaultConfig(RX_MODE); - adcConfig.sample_rate = SAMPLE_RATE; - adcConfig.channels = 1; - - // For ESP32 by Espressif Systems version 3.0.0 and later: - // see examples/README_ESP32.md - // adcConfig.sample_rate = 44100; - // adcConfig.adc_bit_width = 12; - // adcConfig.adc_calibration_active = true; - // adcConfig.is_auto_center_read = false; - // adcConfig.adc_attenuation = ADC_ATTEN_DB_12; - // adcConfig.channels = 1; - // adcConfig.adc_channels[0] = ADC_CHANNEL_4; - - analog_in.begin(adcConfig); - - // Start Serial Output CSV - Serial.println("Starting Serial Out..."); - auto csvConfig = serial_out.defaultConfig(); - csvConfig.sample_rate = SAMPLE_RATE/AVG_LEN; - csvConfig.channels = 1; - csvConfig.bits_per_sample = 16; - serial_out.begin(csvConfig); -} - - -void loop() { - - // Read the values from the ADC buffer to local buffer - size_t bytes_read = analog_in.readBytes((uint8_t*) buffer, BUFFER_SIZE * BYTES_PER_SAMPLE); // read byte stream and cast to destination type - size_t samples_read = bytes_read/BYTES_PER_SAMPLE; - size_t avg_samples = samples_read/AVG_LEN; - - // Average the samples over AVG_LEN - int32_t sum; // register to hold summed values - int16_t *sample = (int16_t*) buffer; // sample pointer (input) - int16_t *avg = (int16_t*) buffer; // result pointer (output) - // each time we access a sample we increment sample pointer - for(uint16_t j=0; j scaler(1.0, -26427, 32700 ); // Arduino Setup void setup(void) { - - delay(3000); // wait for serial to become available - Serial.begin(115200); - // Include logging to serial - AudioToolsLogger.begin(Serial, AudioToolsLogLevel::Info); //Warning, Info, Error, Debug - Serial.println("starting ADC..."); - auto adcConfig = adc.defaultConfig(RX_MODE); - // For ESP32 by Espressif Systems version 3.0.0 and later: - // see examples/README_ESP32.md - // adcConfig.sample_rate = 44100; - // adcConfig.adc_bit_width = 12; - // adcConfig.adc_calibration_active = true; - // adcConfig.is_auto_center_read = false; - // adcConfig.adc_attenuation = ADC_ATTEN_DB_12; - // adcConfig.channels = 2; - // adcConfig.adc_channels[0] = ADC_CHANNEL_4; - // adcConfig.adc_channels[1] = ADC_CHANNEL_5; + // start i2s input with default configuration + Serial.println("starting I2S-ADC..."); + adc.begin(adc.defaultConfig(RX_MODE)); - adc.begin(adcConfig); } // Arduino loop - repeated processing diff --git a/examples/examples-basic-api/base-file_raw-serial/README.md b/examples/examples-basic-api/base-file_raw-serial/README.md new file mode 100644 index 0000000000..47ab8cfe13 --- /dev/null +++ b/examples/examples-basic-api/base-file_raw-serial/README.md @@ -0,0 +1,24 @@ +# Stream SD File to A2DP Bluetooth + +We are reading a raw audio file from the SD card and send it to a Bluetooth A2DP device. The audio file must be available using 16 bit integers with 2 channels. + +[Audacity](https://www.audacityteam.org/) might help you out here: export with the file name audio.raw as RAW signed 16 bit PCM and copy it to the SD card. In my example I was using the file [audio.raw](https://pschatzmann.github.io/Resources/img/audio.raw). + +![sd](https://pschatzmann.github.io/Resources/img/sd-module.jpeg) + +The SD module is connected with the help of the SPI bus + +### Pins: + +We connect the SD to the ESP32: + +| SD | ESP32 +|---------|--------------- +| VCC | 5V +| GND | GND +| CS | CS GP5 +| SCK | SCK GP18 +| MOSI | MOSI GP23 +| MISO | MISO GP19 + + diff --git a/examples/examples-communication/a2dp/basic-generator-a2dp/basic-generator-a2dp.ino b/examples/examples-basic-api/base-generator-a2dp/base-generator-a2dp.ino similarity index 73% rename from examples/examples-communication/a2dp/basic-generator-a2dp/basic-generator-a2dp.ino rename to examples/examples-basic-api/base-generator-a2dp/base-generator-a2dp.ino index a8b9e29d48..45a8a603a7 100644 --- a/examples/examples-communication/a2dp/basic-generator-a2dp/basic-generator-a2dp.ino +++ b/examples/examples-basic-api/base-generator-a2dp/base-generator-a2dp.ino @@ -1,12 +1,18 @@ /** - * @file basic-generator-a2dp.ino + * @file base-generator-a2dp.ino * @author Phil Schatzmann * @brief We send a test sine signal to a bluetooth speaker - * @copyright GPLv3 + * @copyright GPLv3 */ +/** + * @file base-generator-a2dp.ino + * @author Phil Schatzmann + * @brief see https://github.com/pschatzmann/arduino-audio-tools/blob/main/examples/base-generator-a2dp/README.md + * @copyright GPLv3 +*/ #include "AudioTools.h" -#include "AudioTools/AudioLibs/A2DPStream.h" +#include "AudioLibs/AudioA2DP.h" const char* name = "LEXON MINO L"; // Replace with your bluetooth speaker name SineWaveGenerator sineWave(15000); // subclass of SoundGenerator, set max amplitude (=volume) @@ -21,7 +27,7 @@ int32_t get_sound_data(uint8_t * data, int32_t len) { // Arduino Setup void setup(void) { Serial.begin(115200); - AudioToolsLogger.begin(Serial, AudioToolsLogLevel::Warning); + AudioLogger::instance().begin(Serial, AudioLogger::Warning); // start input auto cfg = in_stream.defaultConfig(); @@ -35,11 +41,9 @@ void setup(void) { // start the bluetooth Serial.println("starting A2DP..."); a2dp_source.set_auto_reconnect(false); - a2dp_source.set_data_callback(get_sound_data); - a2dp_source.start(name); + a2dp_source.start_raw(name, get_sound_data); } // Arduino loop - repeated processing void loop() { - delay(1000); } \ No newline at end of file diff --git a/examples/examples-communication/a2dp/basic-i2s-a2dp/README.md b/examples/examples-basic-api/base-i2s-a2dp/README.md similarity index 100% rename from examples/examples-communication/a2dp/basic-i2s-a2dp/README.md rename to examples/examples-basic-api/base-i2s-a2dp/README.md diff --git a/examples/examples-basic-api/base-i2s-a2dp/base-i2s-a2dp.ino b/examples/examples-basic-api/base-i2s-a2dp/base-i2s-a2dp.ino new file mode 100644 index 0000000000..11c99740af --- /dev/null +++ b/examples/examples-basic-api/base-i2s-a2dp/base-i2s-a2dp.ino @@ -0,0 +1,58 @@ +/** + * @file base-i2s-a2dp.ino + * @author Phil Schatzmann + * @brief see https://github.com/pschatzmann/arduino-audio-tools/blob/main/examples/base-i2s-a2dp/README.md + * @copyright GPLv3 +*/ + +#include "AudioTools.h" +#include "AudioLibs/AudioA2DP.h" + +/** + * @brief We use a INMP441 I2S microphone as input and send the data to A2DP + * Unfortunatly the data type from the microphone (int32_t) does not match with the required data type by A2DP (int16_t), + * so we need to convert. + */ + +BluetoothA2DPSource a2dp_source; +I2SStream i2s; +ConverterFillLeftAndRight bothChannels(LeftIsEmpty); +const size_t max_buffer_len = 150; +const int channels = 2; +const size_t max_buffer_bytes = max_buffer_len * sizeof(int16_t) * channels; +uint8_t buffer[max_buffer_bytes]={0}; + +// callback used by A2DP to provide the sound data - usually len is 128 2 channel int16 frames +int32_t get_sound_data(Frame* data, int32_t len) { + size_t req_bytes = min(max_buffer_bytes, len*2*sizeof(int16_t)); + // the microphone provides data in int32_t -> we read it into the buffer of int32_t data so *2 + size_t result_bytes = i2s.readBytes((uint8_t*)buffer, req_bytes*2); + // we have data only in 1 channel but we want to fill both + return bothChannels.convert((uint8_t*)buffer, result_bytes); +} + + +// Arduino Setup +void setup(void) { + Serial.begin(115200); + AudioLogger::instance().begin(Serial, AudioLogger::Info); + + // start i2s input with default configuration + Serial.println("starting I2S..."); + auto cfg = i2s.defaultConfig(RX_MODE); + cfg.i2s_format = I2S_STD_FORMAT; // or try with I2S_LSB_FORMAT + cfg.bits_per_sample = 16; + cfg.channels = 2; + cfg.sample_rate = 44100; + cfg.is_master = true; + i2s.begin(cfg); + + // start the bluetooth + Serial.println("starting A2DP..."); + a2dp_source.set_auto_reconnect(false); + a2dp_source.start("LEXON MINO L", get_sound_data); +} + +// Arduino loop - repeated processing +void loop() { +} \ No newline at end of file diff --git a/examples/examples-basic-api/base-player-a2dp/base-player-a2dp.ino b/examples/examples-basic-api/base-player-a2dp/base-player-a2dp.ino new file mode 100644 index 0000000000..d028abe165 --- /dev/null +++ b/examples/examples-basic-api/base-player-a2dp/base-player-a2dp.ino @@ -0,0 +1,66 @@ +/** + * @file base-player-a2dp.ino + * @author Phil Schatzmann + * @brief Sketch which uses the A2DP callback to provide data from the AudioPlayer via a Queue + * + * @version 0.1 + * @date 2022-12-04 + * + * @copyright Copyright (c) 2022 + * + */ + +#include "AudioTools.h" +#include "AudioLibs/AudioA2DP.h" +#include "AudioLibs/AudioSourceSDFAT.h" +#include "AudioCodecs/CodecMP3Helix.h" +#include "AudioLibs/AudioKit.h" + +int buffer_count = 30; +int buffer_size = 512; +const char *startFilePath="/"; +const char* ext="mp3"; +AudioSourceSDFAT source(startFilePath, ext, PIN_AUDIO_KIT_SD_CARD_CS); +MP3DecoderHelix decoder; +//Setup of synchronized buffer +SynchronizedNBuffer buffer(buffer_size,buffer_count, portMAX_DELAY, 10); +QueueStream out(buffer); // convert Buffer to Stream +AudioPlayer player(source, out, decoder); +BluetoothA2DPSource a2dp; + +// Provide data to A2DP +int32_t get_data(uint8_t *data, int32_t bytes) { + size_t result_bytes = buffer.readArray(data, bytes); + //LOGI("get_data_channels %d -> %d of (%d)", bytes, result_bytes , buffer.available()); + return result_bytes; +} + +void setup() { + Serial.begin(115200); + AudioLogger::instance().begin(Serial, AudioLogger::Warning); + + // sd_active is setting up SPI with the right SD pins by calling + SPI.begin(PIN_AUDIO_KIT_SD_CARD_CLK, PIN_AUDIO_KIT_SD_CARD_MISO, PIN_AUDIO_KIT_SD_CARD_MOSI, PIN_AUDIO_KIT_SD_CARD_CS); + + // start QueueStream + out.begin(); + + // setup player + player.setDelayIfOutputFull(0); + player.setVolume(0.1); + player.begin(); + + // fill buffer with some data + player.getStreamCopy().copyN(5); + + // start a2dp source + Serial.println("starting A2DP..."); + a2dp.start_raw("LEXON MINO L", get_data); + Serial.println("Started!"); + +} + +void loop() { + // decode data to buffer + player.copy(); +} \ No newline at end of file diff --git a/examples/examples-communication/a2dp/basic-a2dp-fft-led/basic-a2dp-fft-led.ino b/examples/examples-basic-api/basic-a2dp-fft-led/basic-a2dp-fft-led.ino similarity index 74% rename from examples/examples-communication/a2dp/basic-a2dp-fft-led/basic-a2dp-fft-led.ino rename to examples/examples-basic-api/basic-a2dp-fft-led/basic-a2dp-fft-led.ino index deefeda564..b22b70429d 100644 --- a/examples/examples-communication/a2dp/basic-a2dp-fft-led/basic-a2dp-fft-led.ino +++ b/examples/examples-basic-api/basic-a2dp-fft-led/basic-a2dp-fft-led.ino @@ -6,10 +6,9 @@ * @copyright GPLv3 */ -#include // to prevent conflicts introduced with 3.9 #include "AudioTools.h" -#include "AudioTools/AudioLibs/AudioRealFFT.h" // or any other supported inplementation -#include "AudioTools/AudioLibs/LEDOutput.h" +#include "AudioLibs/AudioRealFFT.h" // or any other supported inplementation +#include "AudioLibs/LEDOutput.h" #include "BluetoothA2DPSink.h" #define PIN_LEDS 22 @@ -18,8 +17,7 @@ BluetoothA2DPSink a2dp_sink; AudioRealFFT fft; // or any other supported inplementation -FFTDisplay fft_dis(fft); -LEDOutput led(fft_dis); // output to LED matrix +LEDOutput led(fft); // output to LED matrix // Provide data to FFT void writeDataStream(const uint8_t *data, uint32_t length) { @@ -28,7 +26,7 @@ void writeDataStream(const uint8_t *data, uint32_t length) { void setup() { Serial.begin(115200); - AudioToolsLogger.begin(Serial, AudioToolsLogLevel::Info); + AudioLogger::instance().begin(Serial, AudioLogger::Info); // Setup FFT auto tcfg = fft.defaultConfig(); @@ -42,13 +40,11 @@ void setup() { auto lcfg = led.defaultConfig(); lcfg.x = LED_X; lcfg.y = LED_Y; + lcfg.fft_group_bin = 3; + lcfg.fft_start_bin = 0; + lcfg.fft_max_magnitude = 40000; led.begin(lcfg); - fft_dis.fft_group_bin = 3; - fft_dis.fft_start_bin = 0; - fft_dis.fft_max_magnitude = 40000; - fft_dis.begin(); - // add LEDs FastLED.addLeds(led.ledData(), led.ledCount()); diff --git a/examples/examples-communication/a2dp/basic-a2dp-fft/basic-a2dp-fft.ino b/examples/examples-basic-api/basic-a2dp-fft/basic-a2dp-fft.ino similarity index 90% rename from examples/examples-communication/a2dp/basic-a2dp-fft/basic-a2dp-fft.ino rename to examples/examples-basic-api/basic-a2dp-fft/basic-a2dp-fft.ino index e822e50848..ae24641eac 100644 --- a/examples/examples-communication/a2dp/basic-a2dp-fft/basic-a2dp-fft.ino +++ b/examples/examples-basic-api/basic-a2dp-fft/basic-a2dp-fft.ino @@ -7,7 +7,7 @@ */ #include "AudioTools.h" -#include "AudioTools/AudioLibs/AudioRealFFT.h" // or any other supported inplementation +#include "AudioLibs/AudioRealFFT.h" // or any other supported inplementation #include "BluetoothA2DPSink.h" BluetoothA2DPSink a2dp_sink; @@ -35,7 +35,7 @@ void fftResult(AudioFFTBase &fft){ void setup() { Serial.begin(115200); - AudioToolsLogger.begin(Serial, AudioToolsLogLevel::Info); + AudioLogger::instance().begin(Serial, AudioLogger::Info); // Setup FFT auto tcfg = fft.defaultConfig(); diff --git a/examples/examples-basic-api/basic-a2dp-i2s/AudioConfigLocal.h b/examples/examples-basic-api/basic-a2dp-i2s/AudioConfigLocal.h new file mode 100644 index 0000000000..8b1ee2548e --- /dev/null +++ b/examples/examples-basic-api/basic-a2dp-i2s/AudioConfigLocal.h @@ -0,0 +1,4 @@ +#pragma once + +#define I2S_BUFFER_COUNT 8 +#define I2S_BUFFER_SIZE 256 diff --git a/examples/examples-communication/a2dp/basic-a2dp-i2s/basic-a2dp-i2s.ino b/examples/examples-basic-api/basic-a2dp-i2s/basic-a2dp-i2s.ino similarity index 86% rename from examples/examples-communication/a2dp/basic-a2dp-i2s/basic-a2dp-i2s.ino rename to examples/examples-basic-api/basic-a2dp-i2s/basic-a2dp-i2s.ino index 8d85a5c490..b2cf2e437d 100644 --- a/examples/examples-communication/a2dp/basic-a2dp-i2s/basic-a2dp-i2s.ino +++ b/examples/examples-basic-api/basic-a2dp-i2s/basic-a2dp-i2s.ino @@ -7,20 +7,21 @@ * @copyright GPLv3 */ -#include "AudioTools.h" #include "BluetoothA2DPSink.h" +#include "AudioConfigLocal.h" +#include "AudioTools.h" BluetoothA2DPSink a2dp_sink; I2SStream i2s; -// Write data to I2S +// Write data to SPDIF in callback void read_data_stream(const uint8_t *data, uint32_t length) { i2s.write(data, length); } void setup() { Serial.begin(115200); - AudioToolsLogger.begin(Serial, AudioToolsLogLevel::Warning); + AudioLogger::instance().begin(Serial, AudioLogger::Warning); // register callback a2dp_sink.set_stream_reader(read_data_stream, false); @@ -35,8 +36,6 @@ void setup() { cfg.sample_rate = a2dp_sink.sample_rate(); cfg.channels = 2; cfg.bits_per_sample = 16; - cfg.buffer_count = 8; - cfg.buffer_size = 256; i2s.begin(cfg); } diff --git a/examples/examples-basic-api/basic-a2dp-spdif/AudioConfigLocal.h b/examples/examples-basic-api/basic-a2dp-spdif/AudioConfigLocal.h new file mode 100644 index 0000000000..c118898465 --- /dev/null +++ b/examples/examples-basic-api/basic-a2dp-spdif/AudioConfigLocal.h @@ -0,0 +1,4 @@ +#pragma once + +#define I2S_BUFFER_COUNT 30 +#define I2S_BUFFER_SIZE 384 \ No newline at end of file diff --git a/examples/examples-basic-api/basic-a2dp-spdif/basic-a2dp-spdif.ino b/examples/examples-basic-api/basic-a2dp-spdif/basic-a2dp-spdif.ino new file mode 100644 index 0000000000..c952cf146d --- /dev/null +++ b/examples/examples-basic-api/basic-a2dp-spdif/basic-a2dp-spdif.ino @@ -0,0 +1,44 @@ +/** + * @file basic-a2dp-audiospdif.ino + * @brief A2DP Sink with output to SPDIFOutput + * + * @author Phil Schatzmann + * @copyright GPLv3 + */ + +#include "AudioConfigLocal.h" +#include "BluetoothA2DPSink.h" +#include "AudioTools.h" + +BluetoothA2DPSink a2dp_sink; +SPDIFOutput spdif; + +// Write data to SPDIF in callback +void read_data_stream(const uint8_t *data, uint32_t length) { + spdif.write(data, length); +} + +void setup() { + Serial.begin(115200); + AudioLogger::instance().begin(Serial, AudioLogger::Warning); + + // register callback + a2dp_sink.set_stream_reader(read_data_stream, false); + + // Start Bluetooth Audio Receiver + a2dp_sink.set_auto_reconnect(false); + a2dp_sink.start("a2dp-spdif"); + + // setup output + auto cfg = spdif.defaultConfig(); + cfg.pin_data = 23; + cfg.sample_rate = a2dp_sink.sample_rate(); + cfg.channels = 2; + cfg.bits_per_sample = 16; + spdif.begin(cfg); + +} + +void loop() { + delay(100); +} \ No newline at end of file diff --git a/examples/examples-communication/a2dp/basic-a2dp-mixer-i2s/basic-a2dp-mixer-i2s.ino b/examples/examples-communication/a2dp/basic-a2dp-mixer-i2s/basic-a2dp-mixer-i2s.ino deleted file mode 100644 index 1f01a3ebc9..0000000000 --- a/examples/examples-communication/a2dp/basic-a2dp-mixer-i2s/basic-a2dp-mixer-i2s.ino +++ /dev/null @@ -1,73 +0,0 @@ -/** - * @file basic-a2dp-mixer-i2s.ino - * @brief A2DP Sink with output to mixer: I think this is the most efficient way - * of mixing a signal that is coming from A2DP which requires only 160 byte of additional RAM. - * - * @author Phil Schatzmann - * @copyright GPLv3 - */ - -#include "AudioTools.h" -#include "BluetoothA2DPSink.h" - -AudioInfo info(44100, 2, 16); -BluetoothA2DPSink a2dp_sink; -I2SStream i2s; -SineWaveGenerator sineWave(10000); // subclass of SoundGenerator with max amplitude of 10000 -GeneratedSoundStream sound(sineWave); // Stream generated from sine wave -OutputMixer mixer(i2s, 2); // output mixer with 2 outputs -const int buffer_size = 80; // split up the output into small chunks -uint8_t sound_buffer[buffer_size]; - -// Write data to mixer -void read_data_stream(const uint8_t *data, uint32_t length) { - // To keep the mixing buffer small we split up the output into small chunks - int count = length / buffer_size + 1; - for (int j = 0; j < count; j++) { - const uint8_t *start = data + (j * buffer_size); - const uint8_t *end = min(data + length, start + buffer_size); - int len = end - start; - if (len > 0) { - // write a2dp - mixer.write(start, len); - - // write sine tone with identical length - sound.readBytes(sound_buffer, len); - mixer.write(sound_buffer, len); - - // We could flush to force the output but this is not necessary because we - // were already writing all 2 streams mixer.flushMixer(); - } - } -} - -void setup() { - Serial.begin(115200); - AudioToolsLogger.begin(Serial, AudioToolsLogLevel::Warning); - - // setup Output mixer with min necessary memory - mixer.begin(buffer_size); - - // Register data callback - a2dp_sink.set_stream_reader(read_data_stream, false); - - // Start Bluetooth Audio Receiver - a2dp_sink.set_auto_reconnect(false); - a2dp_sink.start("a2dp-i2s"); - - // Update sample rate - info.sample_rate = a2dp_sink.sample_rate(); - - // start sine wave - sineWave.begin(info, N_B4); - - // setup output - auto cfg = i2s.defaultConfig(); - cfg.copyFrom(info); - // cfg.pin_data = 23; - cfg.buffer_count = 8; - cfg.buffer_size = 256; - i2s.begin(cfg); -} - -void loop() { delay(100); } \ No newline at end of file diff --git a/examples/examples-communication/a2dp/basic-file_mp3-a2dp/basic-file_mp3-a2dp.ino b/examples/examples-communication/a2dp/basic-file_mp3-a2dp/basic-file_mp3-a2dp.ino deleted file mode 100644 index 15408212f1..0000000000 --- a/examples/examples-communication/a2dp/basic-file_mp3-a2dp/basic-file_mp3-a2dp.ino +++ /dev/null @@ -1,53 +0,0 @@ -// We use the decoding on the input side to provid pcm data - -#include "SPI.h" -#include "SD.h" -#include "AudioTools.h" -#include "AudioTools/AudioLibs/A2DPStream.h" -#include "AudioTools/AudioCodecs/CodecMP3Helix.h" -//#include "AudioTools/AudioLibs/AudioBoardStream.h" // for SPI pins - -File file; -MP3DecoderHelix mp3; // or change to MP3DecoderMAD -EncodedAudioStream decoder(&file, &mp3); -BluetoothA2DPSource a2dp_source; -const int chipSelect = SS; //PIN_AUDIO_KIT_SD_CARD_CS; - -// callback used by A2DP to provide the sound data - usually len is 128 2 channel int16 frames -int32_t get_sound_data(uint8_t* data, int32_t size) { - int32_t result = decoder.readBytes((uint8_t*)data, size); - delay(1); // feed the dog - return result; -} - -// Arduino Setup -void setup(void) { - Serial.begin(115200); - AudioToolsLogger.begin(Serial, AudioToolsLogLevel::Info); - - // open file - //SPI.begin(PIN_AUDIO_KIT_SD_CARD_CLK, PIN_AUDIO_KIT_SD_CARD_MISO, PIN_AUDIO_KIT_SD_CARD_MOSI, PIN_AUDIO_KIT_SD_CARD_CS); - SD.begin(chipSelect); - file = SD.open("/test.mp3", FILE_READ); - if (!file) { - Serial.println("file failed"); - stop(); - } - - // make sure we have enough space for the pcm data - decoder.transformationReader().resizeResultQueue(1024 * 8); - if (!decoder.begin()) { - Serial.println("decoder failed"); - stop(); - } - - // start the bluetooth - Serial.println("starting A2DP..."); - a2dp_source.set_data_callback(get_sound_data); - a2dp_source.start("LEXON MINO L"); -} - -// Arduino loop - repeated processing -void loop() { - delay(1000); -} diff --git a/examples/examples-communication/a2dp/basic-i2s-a2dp/basic-i2s-a2dp.ino b/examples/examples-communication/a2dp/basic-i2s-a2dp/basic-i2s-a2dp.ino deleted file mode 100644 index dba0f2b9c5..0000000000 --- a/examples/examples-communication/a2dp/basic-i2s-a2dp/basic-i2s-a2dp.ino +++ /dev/null @@ -1,51 +0,0 @@ -/** - * @file basic-i2s-a2dp.ino - * @author Phil Schatzmann - * @brief We use a INMP441 I2S microphone as input and send the data to A2DP - * Unfortunatly the data type from the microphone (int32_t) does not match with - * the required data type by A2DP (int16_t), so we need to convert. - * @copyright GPLv3 - */ - -#include "AudioTools.h" -#include "AudioTools/AudioLibs/A2DPStream.h" - -AudioInfo info32(44100, 2, 32); -AudioInfo info16(44100, 2, 16); -BluetoothA2DPSource a2dp_source; -I2SStream i2s; -FormatConverterStream conv(i2s); -const int BYTES_PER_FRAME = 4; - - -int32_t get_sound_data(Frame* data, int32_t frameCount) { - return conv.readBytes((uint8_t*)data, frameCount*BYTES_PER_FRAME)/BYTES_PER_FRAME; -} - -// Arduino Setup -void setup(void) { - Serial.begin(115200); - AudioToolsLogger.begin(Serial, AudioToolsLogLevel::Info); - - // setup conversion - conv.begin(info32, info16); - - // start i2s input with default configuration - Serial.println("starting I2S..."); - auto cfg = i2s.defaultConfig(RX_MODE); - cfg.i2s_format = I2S_STD_FORMAT; // or try with I2S_LSB_FORMAT - cfg.copyFrom(info32); - cfg.is_master = true; - i2s.begin(cfg); - - // start the bluetooth - Serial.println("starting A2DP..."); - // a2dp_source.set_auto_reconnect(false); - a2dp_source.set_data_callback_in_frames(get_sound_data); - a2dp_source.start("LEXON MINO L"); - - Serial.println("A2DP started"); -} - -// Arduino loop - repeated processing -void loop() { delay(1000); } \ No newline at end of file diff --git a/examples/examples-communication/a2dp/basic-player-a2dp/basic-player-a2dp.ino b/examples/examples-communication/a2dp/basic-player-a2dp/basic-player-a2dp.ino deleted file mode 100644 index 06638dacc3..0000000000 --- a/examples/examples-communication/a2dp/basic-player-a2dp/basic-player-a2dp.ino +++ /dev/null @@ -1,66 +0,0 @@ -/** - * @file basic-player-a2dp.ino - * @author Phil Schatzmann - * @brief Sketch which uses the A2DP callback to provide data from the AudioPlayer via a Queue - * The queue is filled by the Arduino loop. - * @version 0.1 - * @date 2022-12-04 - * - * @copyright Copyright (c) 2022 - * - */ - - #include "AudioTools.h" - #include "AudioTools/AudioLibs/A2DPStream.h" - #include "AudioTools/Disk/AudioSourceSDFAT.h" - #include "AudioTools/AudioCodecs/CodecMP3Helix.h" - //#include "AudioTools/AudioLibs/AudioBoardStream.h" // for SD Pins - - const int cs = 33; //PIN_AUDIO_KIT_SD_CARD_CS; - const int buffer_size = 15*1024; - const char *startFilePath = "/"; - const char *ext = "mp3"; - AudioSourceSDFAT source(startFilePath, ext, cs); - MP3DecoderHelix decoder; - //Setup of synchronized buffer - BufferRTOS buffer(0); - QueueStream out(buffer); // convert Buffer to Stream - AudioPlayer player(source, out, decoder); - BluetoothA2DPSource a2dp; - - // Provide data to A2DP - int32_t get_data(uint8_t *data, int32_t bytes) { - size_t result_bytes = buffer.readArray(data, bytes); - //LOGI("get_data_channels %d -> %d of (%d)", bytes, result_bytes , buffer.available()); - return result_bytes; - } - - void setup() { - Serial.begin(115200); - AudioToolsLogger.begin(Serial, AudioToolsLogLevel::Info); - - // allocate in PSRAM only possible in setup or loop - buffer.resize(buffer_size); - - // sd_active is setting up SPI with the right SD pins by calling - // SPI.begin(PIN_AUDIO_KIT_SD_CARD_CLK, PIN_AUDIO_KIT_SD_CARD_MISO, PIN_AUDIO_KIT_SD_CARD_MOSI, PIN_AUDIO_KIT_SD_CARD_CS); - - // start QueueStream when 95% full - out.begin(95); - - // setup player - player.setDelayIfOutputFull(0); - player.setVolume(0.1); - player.begin(); - - // start a2dp source - Serial.println("starting A2DP..."); - a2dp.set_data_callback(get_data); - a2dp.start("LEXON MINO L"); - Serial.println("Started!"); - } - - void loop() { - // decode data to buffer - player.copy(); - } \ No newline at end of file diff --git a/examples/examples-communication/a2dp/streams-a2dp-spdif/streams-a2dp-spdif.ino b/examples/examples-communication/a2dp/streams-a2dp-spdif/streams-a2dp-spdif.ino deleted file mode 100644 index c94faf49e5..0000000000 --- a/examples/examples-communication/a2dp/streams-a2dp-spdif/streams-a2dp-spdif.ino +++ /dev/null @@ -1,35 +0,0 @@ -/** - * @file basic-a2dp-audiospdif.ino - * @brief A2DP Sink with output to SPDIFOutput - * - * @author Phil Schatzmann - * @copyright GPLv3 - */ -#include "AudioTools.h" -#include "AudioTools/AudioLibs/SPDIFOutput.h" -#include "BluetoothA2DPSink.h" - -AudioInfo info(44100, 2, 16); -SPDIFOutput spdif; -BluetoothA2DPSink a2dp_sink(spdif); - -void setup() { - Serial.begin(115200); - AudioToolsLogger.begin(Serial, AudioToolsLogLevel::Warning); - - // setup output - auto cfg = spdif.defaultConfig(); - cfg.copyFrom(info); - cfg.buffer_size = 384; - cfg.buffer_count = 30; - cfg.pin_data = 23; - spdif.begin(cfg); - - // Start Bluetooth Audio Receiver - a2dp_sink.start("a2dp-spdif"); - -} - -void loop() { - delay(100); -} diff --git a/examples/examples-communication/esp-now/codec/communication-codec-espnow-receive/communication-codec-espnow-receive.ino b/examples/examples-communication/esp-now/codec/communication-codec-espnow-receive/communication-codec-espnow-receive.ino index a8bb0c99a5..010fb4e0a9 100644 --- a/examples/examples-communication/esp-now/codec/communication-codec-espnow-receive/communication-codec-espnow-receive.ino +++ b/examples/examples-communication/esp-now/codec/communication-codec-espnow-receive/communication-codec-espnow-receive.ino @@ -9,20 +9,20 @@ */ #include "AudioTools.h" -#include "AudioTools/Communication/ESPNowStream.h" -#include "AudioTools/AudioCodecs/CodecSBC.h" -// #include "AudioTools/AudioLibs/AudioBoardStream.h" +#include "AudioLibs/Communication.h" +#include "AudioCodecs/CodecSBC.h" +// #include "AudioLibs/AudioKit.h" ESPNowStream now; -I2SStream out; // or AudioBoardStream +I2SStream out; // or AudioKitStream EncodedAudioStream decoder(&out, new SBCDecoder(256)); // decode and write to I2S - ESP Now is limited to 256 bytes StreamCopy copier(decoder, now); const char *peers[] = {"A8:48:FA:0B:93:02"}; void setup() { Serial.begin(115200); - AudioToolsLogger.begin(Serial, AudioToolsLogLevel::Warning); + AudioLogger::instance().begin(Serial, AudioLogger::Warning); // setup esp-now auto cfg = now.defaultConfig(); diff --git a/examples/examples-communication/esp-now/codec/communication-codec-espnow-receive_measure/communication-codec-espnow-receive_measure.ino b/examples/examples-communication/esp-now/codec/communication-codec-espnow-receive_measure/communication-codec-espnow-receive_measure.ino index 0111cd0de7..35c9ac393f 100644 --- a/examples/examples-communication/esp-now/codec/communication-codec-espnow-receive_measure/communication-codec-espnow-receive_measure.ino +++ b/examples/examples-communication/esp-now/codec/communication-codec-espnow-receive_measure/communication-codec-espnow-receive_measure.ino @@ -10,8 +10,8 @@ */ #include "AudioTools.h" -#include "AudioTools/Communication/ESPNowStream.h" -#include "AudioTools/AudioCodecs/CodecSBC.h" +#include "AudioLibs/Communication.h" +#include "AudioCodecs/CodecSBC.h" ESPNowStream now; MeasuringStream out(1000, &Serial); @@ -21,7 +21,7 @@ const char *peers[] = {"A8:48:FA:0B:93:02"}; void setup() { Serial.begin(115200); - AudioToolsLogger.begin(Serial, AudioToolsLogLevel::Warning); + AudioLogger::instance().begin(Serial, AudioLogger::Warning); // start esp-now auto cfg = now.defaultConfig(); diff --git a/examples/examples-communication/esp-now/codec/communication-codec-espnow-send/communication-codec-espnow-send.ino b/examples/examples-communication/esp-now/codec/communication-codec-espnow-send/communication-codec-espnow-send.ino index c97e93bf0e..06196262c5 100644 --- a/examples/examples-communication/esp-now/codec/communication-codec-espnow-send/communication-codec-espnow-send.ino +++ b/examples/examples-communication/esp-now/codec/communication-codec-espnow-send/communication-codec-espnow-send.ino @@ -9,8 +9,8 @@ */ #include "AudioTools.h" -#include "AudioTools/Communication/ESPNowStream.h" -#include "AudioTools/AudioCodecs/CodecSBC.h" +#include "AudioLibs/Communication.h" +#include "AudioCodecs/CodecSBC.h" AudioInfo info(32000,1,16); SineWaveGenerator sineWave( 32000); // subclass of SoundGenerator with max amplitude of 32000 @@ -23,7 +23,7 @@ const char *peers[] = {"A8:48:FA:0B:93:01"}; void setup() { Serial.begin(115200); - AudioToolsLogger.begin(Serial, AudioToolsLogLevel::Warning); + AudioLogger::instance().begin(Serial, AudioLogger::Warning); auto cfg = now.defaultConfig(); cfg.mac_address = "A8:48:FA:0B:93:02"; diff --git a/examples/examples-communication/esp-now/pcm/communication-espnow-receive/communication-espnow-receive.ino b/examples/examples-communication/esp-now/pcm/communication-espnow-receive/communication-espnow-receive.ino index e111495917..6f5ebc0c82 100644 --- a/examples/examples-communication/esp-now/pcm/communication-espnow-receive/communication-espnow-receive.ino +++ b/examples/examples-communication/esp-now/pcm/communication-espnow-receive/communication-espnow-receive.ino @@ -1,5 +1,5 @@ /** - * @file example-espnow-receive.ino + * @file example-serial-receive.ino * @author Phil Schatzmann * @brief Receiving audio via ESPNow * @version 0.1 @@ -9,7 +9,7 @@ */ #include "AudioTools.h" -#include "AudioTools/Communication/ESPNowStream.h" +#include "AudioLibs/Communication.h" AudioInfo info(8000, 1, 16); ESPNowStream now; @@ -21,7 +21,7 @@ const char *peers[] = {"A8:48:FA:0B:93:02"}; void setup() { Serial.begin(115200); - AudioToolsLogger.begin(Serial, AudioToolsLogLevel::Info); + AudioLogger::instance().begin(Serial, AudioLogger::Info); auto cfg = now.defaultConfig(); cfg.mac_address = "A8:48:FA:0B:93:01"; diff --git a/examples/examples-communication/esp-now/pcm/communication-espnow-receive_csv/communication-espnow-receive_csv.ino b/examples/examples-communication/esp-now/pcm/communication-espnow-receive_csv/communication-espnow-receive_csv.ino index 9da044bcdd..79e8cdca60 100644 --- a/examples/examples-communication/esp-now/pcm/communication-espnow-receive_csv/communication-espnow-receive_csv.ino +++ b/examples/examples-communication/esp-now/pcm/communication-espnow-receive_csv/communication-espnow-receive_csv.ino @@ -1,5 +1,5 @@ /** - * @file example-espnow-receive_csv.ino + * @file example-serial-receive_csv.ino * @author Phil Schatzmann * @brief Receiving audio via ESPNow * @version 0.1 @@ -9,14 +9,14 @@ */ #include "AudioTools.h" -#include "AudioTools/Communication/ESPNowStream.h" +#include "AudioLibs/Communication.h" ESPNowStream now; const char *peers[] = {"A8:48:FA:0B:93:02"}; void setup() { Serial.begin(115200); - AudioToolsLogger.begin(Serial, AudioToolsLogLevel::Warning); + AudioLogger::instance().begin(Serial, AudioLogger::Warning); auto cfg = now.defaultConfig(); cfg.mac_address = "A8:48:FA:0B:93:01"; diff --git a/examples/examples-communication/esp-now/pcm/communication-espnow-receive_measure/communication-espnow-receive_measure.ino b/examples/examples-communication/esp-now/pcm/communication-espnow-receive_measure/communication-espnow-receive_measure.ino index a31d5cf19c..522a1c8e9b 100644 --- a/examples/examples-communication/esp-now/pcm/communication-espnow-receive_measure/communication-espnow-receive_measure.ino +++ b/examples/examples-communication/esp-now/pcm/communication-espnow-receive_measure/communication-espnow-receive_measure.ino @@ -1,5 +1,5 @@ /** - * @file example-espnow-receive_measure.ino + * @file example-serial-receive_measure.ino * @author Phil Schatzmann * @brief Receiving audio via ESPNow with max speed to measure thruput * @version 0.1 @@ -10,7 +10,7 @@ */ #include "AudioTools.h" -#include "AudioTools/Communication/ESPNowStream.h" +#include "AudioLibs/Communication.h" ESPNowStream now; MeasuringStream out; @@ -19,7 +19,7 @@ const char *peers[] = {"A8:48:FA:0B:93:02"}; void setup() { Serial.begin(115200); - AudioToolsLogger.begin(Serial, AudioToolsLogLevel::Info); + AudioLogger::instance().begin(Serial, AudioLogger::Info); auto cfg = now.defaultConfig(); cfg.mac_address = "A8:48:FA:0B:93:01"; diff --git a/examples/examples-communication/esp-now/pcm/communication-espnow-send/communication-espnow-send.ino b/examples/examples-communication/esp-now/pcm/communication-espnow-send/communication-espnow-send.ino index 4180a82f06..9058540083 100644 --- a/examples/examples-communication/esp-now/pcm/communication-espnow-send/communication-espnow-send.ino +++ b/examples/examples-communication/esp-now/pcm/communication-espnow-send/communication-espnow-send.ino @@ -1,5 +1,5 @@ /** - * @file example-espnow-send.ino + * @file example-serial-send.ino * @author Phil Schatzmann * @brief Sending audio over ESPNow * @version 0.1 @@ -9,7 +9,7 @@ */ #include "AudioTools.h" -#include "AudioTools/Communication/ESPNowStream.h" +#include "AudioLibs/Communication.h" AudioInfo info(8000, 1, 16); SineWaveGenerator sineWave( 32000); // subclass of SoundGenerator with max amplitude of 32000 @@ -20,7 +20,7 @@ const char *peers[] = {"A8:48:FA:0B:93:01"}; void setup() { Serial.begin(115200); - AudioToolsLogger.begin(Serial, AudioToolsLogLevel::Warning); + AudioLogger::instance().begin(Serial, AudioLogger::Warning); auto cfg = now.defaultConfig(); cfg.mac_address = "A8:48:FA:0B:93:02"; diff --git a/examples/examples-communication/esp-now/speed-test/communication-espnow-receive_measure/communication-espnow-receive_measure.ino b/examples/examples-communication/esp-now/speed-test/communication-espnow-receive_measure/communication-espnow-receive_measure.ino index c801ea1adb..d3a58cc793 100644 --- a/examples/examples-communication/esp-now/speed-test/communication-espnow-receive_measure/communication-espnow-receive_measure.ino +++ b/examples/examples-communication/esp-now/speed-test/communication-espnow-receive_measure/communication-espnow-receive_measure.ino @@ -10,7 +10,7 @@ */ #include "AudioTools.h" -#include "AudioTools/Communication/ESPNowStream.h" +#include "AudioLibs/Communication.h" ESPNowStream now; MeasuringStream out; @@ -19,7 +19,7 @@ const char *peers[] = {"A8:48:FA:0B:93:02"}; void setup() { Serial.begin(115200); - AudioToolsLogger.begin(Serial, AudioToolsLogLevel::Info); + AudioLogger::instance().begin(Serial, AudioLogger::Info); auto cfg = now.defaultConfig(); cfg.mac_address = "A8:48:FA:0B:93:01"; diff --git a/examples/examples-communication/esp-now/speed-test/communication-espnow-send/communication-espnow-send.ino b/examples/examples-communication/esp-now/speed-test/communication-espnow-send/communication-espnow-send.ino index 51948991c9..293cd9c421 100644 --- a/examples/examples-communication/esp-now/speed-test/communication-espnow-send/communication-espnow-send.ino +++ b/examples/examples-communication/esp-now/speed-test/communication-espnow-send/communication-espnow-send.ino @@ -9,7 +9,7 @@ */ #include "AudioTools.h" -#include "AudioTools/Communication/ESPNowStream.h" +#include "AudioLibs/Communication.h" ESPNowStream now; const char *peers[] = {"A8:48:FA:0B:93:01"}; @@ -17,7 +17,7 @@ uint8_t buffer[1024] = {0}; void setup() { Serial.begin(115200); - AudioToolsLogger.begin(Serial, AudioToolsLogLevel::Warning); + AudioLogger::instance().begin(Serial, AudioLogger::Warning); auto cfg = now.defaultConfig(); cfg.mac_address = "A8:48:FA:0B:93:02"; diff --git a/examples/examples-communication/ftp/ftp-client/ftp-client.ino b/examples/examples-communication/ftp/ftp-client/ftp-client.ino deleted file mode 100644 index a74ccb98a7..0000000000 --- a/examples/examples-communication/ftp/ftp-client/ftp-client.ino +++ /dev/null @@ -1,52 +0,0 @@ -/** - * @file ftp-client.ino - * @author Phil Schatzmann - * @brief Receiving audio via FTP and writing to I2S of the AudioKit. - * Replace the userids, passwords and ip adresses with your own! - * And don't forget to read the Wiki of the imported projects - * @version 0.1 - * @date 2023-11-09 - * - * @copyright Copyright (c) 2023 - */ - -#include "WiFi.h" -#include "FTPClient.h" // install https://github.com/pschatzmann/TinyFTPClient -#include "AudioTools.h" // https://github.com/pschatzmann/arduino-audio-tools -#include "AudioTools/AudioCodecs/CodecMP3Helix.h" // https://github.com/pschatzmann/arduino-libhelix -#include "AudioTools/AudioLibs/AudioBoardStream.h" // https://github.com/pschatzmann/arduino-audio-driver - -FTPClient client; -FTPFile file; -AudioBoardStream kit(AudioKitEs8388V1); // or replace with e.g. I2SStream -MP3DecoderHelix mp3; -EncodedAudioStream decoder(&kit, &mp3); -StreamCopy copier(decoder, file); - -void setup() { - Serial.begin(115200); - - // connect to WIFI - WiFi.begin("network name", "password"); - while (WiFi.status() != WL_CONNECTED) { - delay(500); - Serial.print("."); - } - - // optional logging - FTPLogger::setOutput(Serial); - // FTPLogger::setLogLevel(LOG_DEBUG); - - // open connection - client.begin(IPAddress(192, 168, 1, 10), "user", "password"); - - // copy data from file - file = client.open("/data/music/Neil Young/After the Gold Rush/08 Birds.mp3"); - - // open output device - kit.begin(); - decoder.begin(); - -} - -void loop() { copier.copy(); } \ No newline at end of file diff --git a/examples/examples-communication/hls/hls-buffer-i2s/hls-buffer-i2s.ino b/examples/examples-communication/hls/hls-buffer-i2s/hls-buffer-i2s.ino deleted file mode 100644 index 992cf35e60..0000000000 --- a/examples/examples-communication/hls/hls-buffer-i2s/hls-buffer-i2s.ino +++ /dev/null @@ -1,64 +0,0 @@ -/** - * @file hls-buffer-i2s.ino - * @brief Buffered playback of HLSStream: activate PSRAM! - * We use a MultiDecoder to handle different formats. - * For MPEG-TS (MTS) you need to set the log level to Warning or higher. - * - * @author Phil Schatzmann - * @copyright GPLv3 - */ - -#include "AudioTools.h" -#include "AudioTools/AudioCodecs/CodecHelix.h" -#include "AudioTools/AudioCodecs/CodecMTS.h" -#include "AudioTools/AudioLibs/HLSStream.h" -#include "AudioTools/Concurrency/RTOS.h" -// #include "AudioTools/AudioLibs/AudioBoardStream.h" - -// AudioBoardStream out(AudioKitEs8388V1); // final output of decoded stream -I2SStream out; // audio output -BufferRTOS buffer(0); -QueueStream queue(buffer); -HLSStream hls_stream("ssid", "password"); // audio data source -// decoder -MP3DecoderHelix mp3; -AACDecoderHelix aac; -MTSDecoder mts(aac); // MPEG-TS (MTS) decoder -MultiDecoder multi(hls_stream); // MultiDecoder using mime from hls_stream -EncodedAudioOutput dec(&out, &multi); -// 2 separate copy processes -StreamCopy copier_play(dec, queue); -StreamCopy copier_write_queue(queue, hls_stream); -Task writeTask("write", 1024 * 8, 10, 0); - -// Arduino Setup -void setup(void) { - Serial.begin(115200); - AudioToolsLogger.begin(Serial, AudioToolsLogLevel::Info); - - // https://streams.radiomast.io/ref-128k-mp3-stereo/hls.m3u8 - // https://streams.radiomast.io/ref-128k-aaclc-stereo/hls.m3u8 - // https://streams.radiomast.io/ref-64k-heaacv1-stereo/hls.m3u8 - if (!hls_stream.begin( - "https://streams.radiomast.io/ref-128k-mp3-stereo/hls.m3u8")) - stop(); - - // register decoders with mime types - multi.addDecoder(mp3, "audio/mpeg"); - multi.addDecoder(aac, "audio/aac"); - multi.addDecoder(mts, "video/MP2T"); // MPEG-TS - - // start output - auto cfg = out.defaultConfig(TX_MODE); - // add optional configuration here: e.g. pins - out.begin(cfg); - - buffer.resize(10 * 1024); // increase to 50k psram - dec.begin(); // start decoder - queue.begin(100); // activate read when 100% full - - writeTask.begin([]() { copier_write_queue.copy(); }); -} - -// Arduino loop -void loop() { copier_play.copy(); } diff --git a/examples/examples-communication/hls/hls-i2s/hls-i2s.ino b/examples/examples-communication/hls/hls-i2s/hls-i2s.ino deleted file mode 100644 index ffdfd338dd..0000000000 --- a/examples/examples-communication/hls/hls-i2s/hls-i2s.ino +++ /dev/null @@ -1,48 +0,0 @@ -/** - * @file hls-i2s.ino - * @brief Copy hls stream to decoder: the re-loading of data is causig pauses - * We use a MultiDecoder to handle different formats. - * For MPEG-TS (MTS) you need to set the log level to Warning or higher. - * - * @author Phil Schatzmann - * @copyright GPLv3 - */ - -#include "AudioTools.h" -#include "AudioTools/AudioLibs/HLSStream.h" -#include "AudioTools/AudioCodecs/CodecHelix.h" -//#include "AudioTools/AudioLibs/AudioBoardStream.h" - -//AudioBoardStream out(AudioKitEs8388V1); -I2SStream out; -HLSStream hls_stream("ssid", "password"); -MP3DecoderHelix mp3; -AACDecoderHelix aac; -MultiDecoder multi(hls_stream); // MultiDecoder using mime from hls_stream -EncodedAudioOutput dec(&out, &multi); -StreamCopy copier(dec, hls_stream); - -// Arduino Setup -void setup(void) { - Serial.begin(115200); - AudioToolsLogger.begin(Serial, AudioToolsLogLevel::Info); - - // https://streams.radiomast.io/ref-128k-mp3-stereo/hls.m3u8 - // https://streams.radiomast.io/ref-128k-aaclc-stereo/hls.m3u8 - // https://streams.radiomast.io/ref-64k-heaacv1-stereo/hls.m3u8 - if (!hls_stream.begin("https://streams.radiomast.io/ref-128k-mp3-stereo/hls.m3u8")) - stop(); - - multi.addDecoder(mp3, "audio/mpeg"); - multi.addDecoder(aac, "audio/aac"); - - auto cfg = out.defaultConfig(TX_MODE); - out.begin(cfg); - - dec.begin(); // start decoder -} - -// Arduino loop -void loop() { - copier.copy(); -} \ No newline at end of file diff --git a/examples/examples-communication/http-client/streams-eth_url_mp3_helix-i2s/streams-eth_url_mp3_helix-i2s.ino b/examples/examples-communication/http-client/streams-eth_url_mp3_helix-i2s/streams-eth_url_mp3_helix-i2s.ino deleted file mode 100644 index 68857587c0..0000000000 --- a/examples/examples-communication/http-client/streams-eth_url_mp3_helix-i2s/streams-eth_url_mp3_helix-i2s.ino +++ /dev/null @@ -1,68 +0,0 @@ -/** - * @file streams-url_mp3-i2s.ino - * @author Phil Schatzmann - * @brief decode MP3 stream from url and output it on I2S - * @version 0.1 - * @date 2021-96-25 - * - * @copyright Copyright (c) 2021 - */ - -// install https://github.com/pschatzmann/arduino-libhelix.git - -#include "AudioTools.h" -#include "AudioTools/AudioCodecs/CodecMP3Helix.h" -#include -#include - -// Enter a MAC address for your controller below. -// Newer Ethernet shields have a MAC address printed on a sticker on the shield -byte mac[] = { 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED }; -const int CS = SS; -EthernetClient eth; -URLStream url(http://github.com/eth); -I2SStream i2s; // final output of decoded stream -EncodedAudioStream dec(&i2s, new MP3DecoderHelix()); // Decoding stream -StreamCopy copier(dec, url); // copy url to decoder - -void setupEthernet() { - // You can use Ethernet.init(pin) to configure the CS pin - Ethernet.init(CS); - - // start the Ethernet connection: - Serial.println("Initialize Ethernet with DHCP:"); - if (Ethernet.begin(mac)) { - Serial.print(" DHCP assigned IP "); - Serial.println(Ethernet.localIP()); - } else { - Serial.println("Failed to configure Ethernet using DHCP"); - stop(); - } -} - -void setup(){ - Serial.begin(115200); - AudioToolsLogger.begin(Serial, AudioToolsLogLevel::Info); - - setupEthernet(); - - // setup i2s - auto config = i2s.defaultConfig(TX_MODE); - // you could define e.g your pins and change other settings - //config.pin_ws = 10; - //config.pin_bck = 11; - //config.pin_data = 12; - //config.mode = I2S_STD_FORMAT; - i2s.begin(config); - - // setup I2S based on sampling rate provided by decoder - dec.begin(); - -// mp3 radio - url.begin("http://stream.srg-ssr.ch/m/rsj/mp3_128","audio/mp3"); - -} - -void loop(){ - copier.copy(); -} diff --git a/examples/examples-communication/http-client/streams-http_post/streams-http_post.ino b/examples/examples-communication/http-client/streams-http_post/streams-http_post.ino deleted file mode 100644 index 54186a978b..0000000000 --- a/examples/examples-communication/http-client/streams-http_post/streams-http_post.ino +++ /dev/null @@ -1,66 +0,0 @@ - -/** - * @file streams-url-post.ino - * @author Phil Schatzmann - * @brief example how to http post data from an input stream using the - * HttpRequest class. - * @copyright GPLv3 - */ - -#include "AudioTools.h" - -const char *ssid = "your SSID"; -const char *password = "your PASSWORD"; -const char *url_str = "http://192.168.1.44:9988/"; -AudioInfo info(44100, 2, 16); -SineWaveGenerator sineWave(32000); -GeneratedSoundStream sound(sineWave); -TimedStream timed(sound); -WiFiClient client; -HttpRequest http(client); -StreamCopy copier(http, timed); - -void startWiFi() { - WiFi.mode(WIFI_STA); - WiFi.begin(ssid, password); - Serial.print("Connecting to WiFi .."); - while (WiFi.status() != WL_CONNECTED) { - Serial.print('.'); - delay(1000); - } - Serial.println(); - Serial.println(WiFi.localIP()); -} - -// Arduino Setup -void setup(void) { - // Open Serial - Serial.begin(115200); - AudioToolsLogger.begin(Serial, AudioToolsLogLevel::Info); - - startWiFi(); - - // Setup sine wave - sineWave.begin(info, N_B4); - - // limit the size of the input stream to 60 seconds - timed.setEndSec(60); - timed.begin(); - - // start post - Url url(http://github.com/url_str); - http.header().put(TRANSFER_ENCODING, CHUNKED); // uncomment if chunked - if (!http.processBegin(POST, url, "audio/pcm")){ - Serial.println("post failed"); - stop(); - } -} - -// Arduino loop - copy sound to out -void loop() { - // posting the data - if (copier.copy() == 0) { - // closing the post - http.processEnd(); - } -} \ No newline at end of file diff --git a/examples/examples-communication/http-client/streams-url_flac_foxen-i2s/streams-url_flac_foxen-i2s.ino b/examples/examples-communication/http-client/streams-url_flac_foxen-i2s/streams-url_flac_foxen-i2s.ino deleted file mode 100644 index 0bb8b9faa7..0000000000 --- a/examples/examples-communication/http-client/streams-url_flac_foxen-i2s/streams-url_flac_foxen-i2s.ino +++ /dev/null @@ -1,53 +0,0 @@ -/** - * @file streams-url_flac_foxen-i2s.ino - * @author Phil Schatzmann - * @brief Demo using the Foxen FLAC decoder - * @version 0.1 - * @date 2025-01-09 - * - * @copyright Copyright (c) 2022 - * - * Warning: The WIFI speed is quite limited and the FLAC files are quite big. So there - * is a big chance that the WIFI is just not fast enough. For my test I was downsampling - * the file to 8000 samples per second! - */ -#include "AudioTools.h" -#include "AudioTools/AudioCodecs/CodecFLACFoxen.h" -#include "AudioTools/AudioLibs/AudioBoardStream.h" - -const char* ssid = "ssid"; -const char* pwd = "password"; -URLStream url(http://github.com/ssid,%20pwd); -AudioBoardStream i2s(AudioKitEs8388V1); // or replace with e.g. I2SStream i2s; - -FLACDecoderFoxen flac(5*1024, 2); -EncodedAudioStream dec(&i2s, &flac); // Decoding to i2s -StreamCopy copier(dec, url, 1024); // copy url to decoder - -void setup() { - Serial.begin(115200); - AudioToolsLogger.begin(Serial, AudioToolsLogLevel::Warning); - while(!Serial); - Serial.println("starting..."); - - // Open I2S: if the buffer is too slow audio is breaking up - auto cfg =i2s.defaultConfig(TX_MODE); - cfg.buffer_size= 1024; - cfg.buffer_count = 20; - if (!i2s.begin(cfg)){ - Serial.println("i2s error"); - stop(); - } - - // Open url - url.begin("https://pschatzmann.github.io/Resources/audio/audio.flac", "audio/flac"); - - // Start decoder - if (!dec.begin()){ - Serial.println("Decoder failed"); - } -} - -void loop() { - copier.copy(); -} \ No newline at end of file diff --git a/examples/examples-communication/http-client/streams-url_mp3-pwm/streams-url_mp3-pwm.ino b/examples/examples-communication/http-client/streams-url_mp3-pwm/streams-url_mp3-pwm.ino deleted file mode 100644 index 15b96d25b5..0000000000 --- a/examples/examples-communication/http-client/streams-url_mp3-pwm/streams-url_mp3-pwm.ino +++ /dev/null @@ -1,49 +0,0 @@ -/** - * @file streams-url_mp3-out.ino - * @author Phil Schatzmann - * @brief decode MP3 stream from url and output it with the help of PWM - * @version 0.1 - * @date 2021-96-25 - * - * @copyright Copyright (c) 2021 - */ - -// install https://github.com/pschatzmann/arduino-libhelix.git - -#include "AudioTools.h" -#include "AudioTools/AudioCodecs/CodecMP3Helix.h" - - -URLStream url("http://github.com/ssid%22,%22password"); -PWMAudioOutput out; // final output of decoded stream -EncodedAudioStream dec(&out, new MP3DecoderHelix()); // Decoding stream -StreamCopy copier(dec, url); // copy url to decoder - - -void setup(){ - Serial.begin(115200); - AudioToolsLogger.begin(Serial, AudioToolsLogLevel::Info); - - // setup out - auto config = out.defaultConfig(TX_MODE); - //config.resolution = 8; // must be between 8 and 11 -> drives pwm frequency (8 is default) - // alternative 1 - //config.start_pin = 3; - // alternative 2 - int pins[] = {22, 23}; - // alternative 3 - //Pins pins = {3}; - //config.setPins(pins); - out.begin(config); - - // setup I2S based on sampling rate provided by decoder - dec.begin(); - -// mp3 radio - url.begin("http://stream.srg-ssr.ch/m/rsj/mp3_128","audio/mp3"); - -} - -void loop(){ - copier.copy(); -} diff --git a/examples/examples-communication/http-client/streams-url_mp3_helix-i2s_32bit/streams-url_mp3_helix-i2s_32bit.ino b/examples/examples-communication/http-client/streams-url_mp3_helix-i2s_32bit/streams-url_mp3_helix-i2s_32bit.ino deleted file mode 100644 index 705d8bb202..0000000000 --- a/examples/examples-communication/http-client/streams-url_mp3_helix-i2s_32bit/streams-url_mp3_helix-i2s_32bit.ino +++ /dev/null @@ -1,52 +0,0 @@ -/** - * @file streams-url_mp3-i2s_24bit.ino - * @author Phil Schatzmann - * @brief decode MP3 stream from url and output it on I2S. The data is converted from - * 16 to 32 bit - * @version 0.1 - * @date 2021-96-25 - * - * @copyright Copyright (c) 2021 - */ - -// install https://github.com/pschatzmann/arduino-libhelix.git - -#include "AudioTools.h" -#include "AudioTools/AudioCodecs/CodecMP3Helix.h" - - -URLStream url("http://github.com/ssid%22,%22password"); -I2SStream i2s; // final output of decoded stream -NumberFormatConverterStream nfc(i2s); -EncodedAudioStream dec(&nfc, new MP3DecoderHelix()); // Decoding stream -StreamCopy copier(dec, url); // copy url to decoder - - -void setup(){ - Serial.begin(115200); - AudioToolsLogger.begin(Serial, AudioToolsLogLevel::Info); - - // convert 16 bits to 32, you could also change the gain - nfc.begin(16, 32); - - // setup i2s - auto config = i2s.defaultConfig(TX_MODE); - // you could define e.g your pins and change other settings - //config.pin_ws = 10; - //config.pin_bck = 11; - //config.pin_data = 12; - //config.mode = I2S_STD_FORMAT; - //config.bits_per_sample = 32; // we coult do this explicitly - i2s.begin(config); - - // setup I2S based on sampling rate provided by decoder - dec.begin(); - -// mp3 radio - url.begin("http://stream.srg-ssr.ch/m/rsj/mp3_128","audio/mp3"); - -} - -void loop(){ - copier.copy(); -} diff --git a/examples/examples-communication/http-client/streams-url_mts-hex/streams-url_mts-hex.ino b/examples/examples-communication/http-client/streams-url_mts-hex/streams-url_mts-hex.ino deleted file mode 100644 index f180512d93..0000000000 --- a/examples/examples-communication/http-client/streams-url_mts-hex/streams-url_mts-hex.ino +++ /dev/null @@ -1,32 +0,0 @@ -/** - * @file url_mts-hex.ino - * @author Phil Schatzmann - - * @copyright GPLv3 - */ - -#include "AudioTools.h" -#include "AudioTools/AudioCodecs/CodecMTS.h" -#include "AudioTools/AudioLibs/HLSStream.h" - -HexDumpOutput out(Serial); -HLSStream hls_stream("SSID", "password"); -MTSDecoder mts; -EncodedAudioStream mts_stream(&out, &mts); -StreamCopy copier(mts_stream, hls_stream); - -// Arduino Setup -void setup(void) { - Serial.begin(115200); - AudioToolsLogger.begin(Serial, AudioToolsLogLevel::Info); - - mts_stream.begin(); - - hls_stream.begin("http://a.files.bbci.co.uk/media/live/manifesto/audio/simulcast/hls/nonuk/sbr_vlow/ak/bbc_world_service.m3u8"); - -} - -// Arduino loop -void loop() { - copier.copy(); -} \ No newline at end of file diff --git a/examples/examples-communication/http-client/streams-url_post/streams-url_post.ino b/examples/examples-communication/http-client/streams-url_post/streams-url_post.ino deleted file mode 100644 index 721f6a506e..0000000000 --- a/examples/examples-communication/http-client/streams-url_post/streams-url_post.ino +++ /dev/null @@ -1,35 +0,0 @@ - -/** - * @file streams-url-post.ino - * @author Phil Schatzmann - * @brief example how to http post data from an input stream - * @copyright GPLv3 - */ - -#include "AudioTools.h" - -AudioInfo info(44100, 2, 16); -SineWaveGenerator sineWave(32000); -GeneratedSoundStream sound(sineWave); -TimedStream timed(sound); -URLStream url("http://github.com/ssid%22,%20%22password"); - -// Arduino Setup -void setup(void) { - // Open Serial - Serial.begin(115200); - AudioToolsLogger.begin(Serial, AudioToolsLogLevel::Info); - - // Setup sine wave - sineWave.begin(info, N_B4); - - // limit the size of the input stream - timed.setEndSec(60); - timed.begin(); - - // post the data - url.begin("http://192.168.1.35:8000/", "audio/bin", POST, "text/html", timed); -} - -// Arduino loop - copy sound to out -void loop() {} diff --git a/examples/examples-communication/http-server/python-post-server/README.md b/examples/examples-communication/http-server/python-post-server/README.md deleted file mode 100644 index ca24566e93..0000000000 --- a/examples/examples-communication/http-server/python-post-server/README.md +++ /dev/null @@ -1,5 +0,0 @@ - -This directory contains a server in Python that was used to test the [Arduino -post sketch](https://github.com/pschatzmann/arduino-audio-tools/blob/main/examples/examples-communication/http-client/streams-http_post/streams-http_post.ino) using chunged writes. - -The server logs each written line and writes the data to a file. diff --git a/examples/examples-communication/http-server/python-post-server/server.py b/examples/examples-communication/http-server/python-post-server/server.py deleted file mode 100755 index a63e4d1d32..0000000000 --- a/examples/examples-communication/http-server/python-post-server/server.py +++ /dev/null @@ -1,48 +0,0 @@ - -#!/usr/bin/env python3 - -from http.server import HTTPServer, SimpleHTTPRequestHandler - -HOST = "" -PORT = 9988 -path = "./audio.pcm" - -class TestHTTPRequestHandler(SimpleHTTPRequestHandler): - def do_POST(self): - self.send_response(200) - self.end_headers() - - if "Content-Length" in self.headers: - content_length = int(self.headers["Content-Length"]) - body = self.rfile.read(content_length) - with open(path, "wb") as out_file: - print("writing:", content_length) - out_file.write(body) - elif "chunked" in self.headers.get("Transfer-Encoding", ""): - with open(path, "wb") as out_file: - while True: - line = self.rfile.readline().strip() - print(line) - chunk_length = int(line, 16) - - if chunk_length != 0: - print("writing chunk:", chunk_length) - chunk = self.rfile.read(chunk_length) - out_file.write(chunk) - - # Each chunk is followed by an additional empty newline - # that we have to consume. - self.rfile.readline() - - # Finally, a chunk size of 0 is an end indication - if chunk_length == 0: - break - -def main(): - httpd = HTTPServer((HOST, PORT), TestHTTPRequestHandler) - print("Serving at port:", httpd.server_port) - httpd.serve_forever() - - -if __name__ == "__main__": - main() diff --git a/examples/examples-communication/http-server/streams-audiokit-webserver_mp3/streams-audiokit-webserver_mp3.ino b/examples/examples-communication/http-server/streams-audiokit-webserver_mp3/streams-audiokit-webserver_mp3.ino deleted file mode 100644 index 24025fb94a..0000000000 --- a/examples/examples-communication/http-server/streams-audiokit-webserver_mp3/streams-audiokit-webserver_mp3.ino +++ /dev/null @@ -1,62 +0,0 @@ -/** - * @file streams-audiokit-webserver_mp3.ino - * - * This sketch reads sound data from the AudioKit. The result is provided as MP3 stream which can be listened to in a Web Browser - * - * @author Phil Schatzmann, Thorsten Godau (changed AAC example to MP3, added optional static IP) - * @copyright GPLv3 - */ - -#include "AudioTools.h" -#include "AudioTools/AudioLibs/AudioBoardStream.h" -#include "AudioTools/AudioCodecs/CodecMP3LAME.h" - -// Set static IP address and stuff (optional) -IPAddress IPA_address(192, 168, 0, 222); -IPAddress IPA_gateway(192, 168, 0, 1); -IPAddress IPA_subnet(255, 255, 0, 0); -IPAddress IPA_primaryDNS(192, 168, 0, 1); //optional -IPAddress IPA_secondaryDNS(8, 8, 8, 8); //optional - -// WIFI -const char *ssid = "ssid"; -const char *password = "password"; - -AudioInfo info(16000,1,16); -MP3EncoderLAME mp3; -AudioEncoderServer server(&mp3, ssid, password); -AudioBoardStream kit(AudioKitEs8388V1); - -// Arduino setup -void setup(){ - Serial.begin(115200); - // Defining Loglevels for the different libraries - //AudioToolsLogger.begin(Serial, AudioToolsLogLevel::Info); - //LOGLEVEL_AUDIOKIT = AudioKitInfo; - - // Configures static IP address (optional) - if (!WiFi.config(IPA_address, IPA_gateway, IPA_subnet, IPA_primaryDNS, IPA_secondaryDNS)) - { - Serial.println("WiFi.config: Failed to configure static IPv4..."); - } - - // start i2s input with default configuration - Serial.println("starting AudioKit..."); - auto config = kit.defaultConfig(RX_MODE); - config.input_device = ADC_INPUT_LINE2; - config.copyFrom(info); - config.sd_active = false; - kit.begin(config); - Serial.println("AudioKit started"); - - // start data sink - server.begin(kit, config); - Serial.println("Server started"); - -} - -// Arduino loop -void loop() { - // Handle new connections - server.doLoop(); -} diff --git a/examples/examples-communication/ip/communication-ip-receive/communication-ip-receive.ino b/examples/examples-communication/ip/communication-ip-receive/communication-ip-receive.ino index 64eb6d00c0..2e17dae7c6 100644 --- a/examples/examples-communication/ip/communication-ip-receive/communication-ip-receive.ino +++ b/examples/examples-communication/ip/communication-ip-receive/communication-ip-receive.ino @@ -11,9 +11,6 @@ #include "AudioTools.h" #include -const char *ssid = "ssid"; -const char *password = "password"; - AudioInfo info(16000, 1, 16); uint16_t port = 8000; WiFiServer server(port); @@ -21,6 +18,8 @@ WiFiClient client; I2SStream out; MeasuringStream outTimed(out); StreamCopy copier(outTimed, client); +const char *ssid = "ssid"; +const char *password = "password"; void connectWifi(){ // connect to WIFI @@ -33,12 +32,12 @@ void connectWifi(){ Serial.println(WiFi. localIP()); // Performance Hack - WiFi.setSleep(false); + esp_wifi_set_ps(WIFI_PS_NONE); } void setup() { Serial.begin(115200); - AudioToolsLogger.begin(Serial, AudioToolsLogLevel::Info); + AudioLogger::instance().begin(Serial, AudioLogger::Info); connectWifi(); @@ -61,12 +60,8 @@ void loop() { if (!client){ client = server.available(); } - // copy data if we are connected if (client.connected()){ copier.copy(); - } else { - // feed the dog - delay(100); } } diff --git a/examples/examples-communication/ip/communication-ip-receive_measure/communication-ip-receive_measure.ino b/examples/examples-communication/ip/communication-ip-receive_measure/communication-ip-receive_measure.ino index 5a1852a4f5..fd1281d3dd 100644 --- a/examples/examples-communication/ip/communication-ip-receive_measure/communication-ip-receive_measure.ino +++ b/examples/examples-communication/ip/communication-ip-receive_measure/communication-ip-receive_measure.ino @@ -11,15 +11,14 @@ #include "AudioTools.h" #include -const char *ssid = "ssid"; -const char *password = "password"; - AudioInfo info(16000, 1, 16); uint16_t port = 8000; WiFiServer server(port); WiFiClient client; MeasuringStream out; StreamCopy copier(out, client); +const char *ssid = "ssid"; +const char *password = "password"; void connectWifi() { // connect to WIFI @@ -32,12 +31,12 @@ void connectWifi() { Serial.println(WiFi. localIP()); // Performance Hack - WiFi.setSleep(false); + esp_wifi_set_ps(WIFI_PS_NONE); } void setup() { Serial.begin(115200); - AudioToolsLogger.begin(Serial, AudioToolsLogLevel::Info); + AudioLogger::instance().begin(Serial, AudioLogger::Info); connectWifi(); @@ -58,8 +57,5 @@ void loop() { // copy data if we are connected if (client.connected()){ copier.copy(); - } else { - // feed the dog - delay(100); } } \ No newline at end of file diff --git a/examples/examples-communication/ip/communication-ip-send/communication-ip-send.ino b/examples/examples-communication/ip/communication-ip-send/communication-ip-send.ino index 43d1c4c28e..304d4ac3f2 100644 --- a/examples/examples-communication/ip/communication-ip-send/communication-ip-send.ino +++ b/examples/examples-communication/ip/communication-ip-send/communication-ip-send.ino @@ -48,7 +48,7 @@ void connectIP() { void setup() { Serial.begin(115200); - AudioToolsLogger.begin(Serial, AudioToolsLogLevel::Info); + AudioLogger::instance().begin(Serial, AudioLogger::Info); connectWifi(); // Setup sine wave diff --git a/examples/examples-communication/lora/communication-lora-receive/communication-lora-receive.ino b/examples/examples-communication/lora/communication-lora-receive/communication-lora-receive.ino deleted file mode 100644 index 0af5c92d45..0000000000 --- a/examples/examples-communication/lora/communication-lora-receive/communication-lora-receive.ino +++ /dev/null @@ -1,36 +0,0 @@ -/** - * @file example-lora-receive_measure.ino - * @author Phil Schatzmann - * @brief Receiving audio via LoRa with max speed to measure thruput. - * @version 0.1 - * @date 2023-11-09 - * - * @copyright Copyright (c) 2022 - * - */ - -#include "AudioTools.h" -#include "AudioTools/Communication/LoRaStream.h" - -LoRaStream lora; -MeasuringStream out; // or CsvOutput -StreamCopy copier(out, lora); - -void setup() { - Serial.begin(115200); - AudioToolsLogger.begin(Serial, AudioToolsLogLevel::Info); - while(!Serial); - - auto cfg = lora.defaultConfig(); - lora.begin(cfg); - - // start output - Serial.println("starting Out..."); - out.begin(); - - Serial.println("Receiver started..."); -} - -void loop() { - copier.copy(); -} \ No newline at end of file diff --git a/examples/examples-communication/lora/communication-lora-send/communication-lora-send.ino b/examples/examples-communication/lora/communication-lora-send/communication-lora-send.ino deleted file mode 100644 index d7658b3cde..0000000000 --- a/examples/examples-communication/lora/communication-lora-send/communication-lora-send.ino +++ /dev/null @@ -1,45 +0,0 @@ -/** - * @file example-lora-send.ino - * @author Phil Schatzmann - * @brief Sending data over LoRa. We use a Heltec WiFi LoRa V3 board for testing. - * V3 boards use the SX1262. - * @version 0.1 - * @date 2023-11-09 - * - * @copyright Copyright (c) 2022 - */ - -#include "AudioTools.h" -#include "AudioTools/Communication/LoRaStream.h" - -AudioInfo info(8000, 1, 16); -SineWaveGenerator sineWave( 32000); // subclass of SoundGenerator with max amplitude of 32000 -GeneratedSoundStream sound( sineWave); // Stream generated from sine wave -LoRaStream lora; -StreamCopy copier(lora, sound); // copies sound into i2s - -void setup() { - Serial.begin(115200); - AudioToolsLogger.begin(Serial, AudioToolsLogLevel::Info); - while(!Serial); - Serial.println("starting..."); - - // Setup LoRa - Serial.printf("SCK: %d, MISO: %d, MOSI: %d, SS=%d", SCK,MISO,MOSI,SS); - SPI.begin(SCK, MISO, MOSI, SS); - auto cfg = lora.defaultConfig(); - cfg.copyFrom(info); - if (!lora.begin(cfg)){ - Serial.println("LoRa failed"); - stop(); - } - - // Setup sine wave - sineWave.begin(info, N_B4); - - Serial.println("Sender started..."); -} - -void loop() { - copier.copy(); -} \ No newline at end of file diff --git a/examples/examples-communication/mqtt/communication-mqtt-send/communication-mqtt-send.ino b/examples/examples-communication/mqtt/communication-mqtt-send/communication-mqtt-send.ino deleted file mode 100644 index 5f601955b6..0000000000 --- a/examples/examples-communication/mqtt/communication-mqtt-send/communication-mqtt-send.ino +++ /dev/null @@ -1,113 +0,0 @@ -/** - * @file communication-mqtt-send.ino - * @author Phil Schatzmann - * @brief Send Audio File to MQTT. - * I am using the ArduinoMQTTClient library from Arduino provided by the library manager. - * We can just copy the audio data to the MQTT client! - * @version 0.1 - * @date 2023-09-07 - * - * @copyright Copyright (c) 2023 - */ - -#include "WiFi.h" -#include "ArduinoMqttClient.h" -#include "AudioTools.h" - -#define SIZE 1024 -#define N 100 - -// Communication -const char* ssid = "SSID"; // your network SSID (name) -const char* password = "PASSWORD"; // your network password (use for WPA, or use as key for WEP) -const char* broker = "test.mosquitto.org"; -const char* topic = "audio.wav"; -int port = 1883; -WiFiClient wifiClient; -MqttClient mqttClient(wifiClient); - -// Audio -AudioInfo info(8000, 1, 16); -WhiteNoiseGenerator noise(32000); // subclass of SoundGenerator with max amplitude of 32000 -GeneratedSoundStream in_stream(noise); // Stream generated from noise -EncodedAudioStream out_stream(&mqttClient, new WAVEncoder()); // encode as wav file -StreamCopy copier(out_stream, in_stream, SIZE); // copies sound to MQTT client - -// Connect to Wifi -void connectWIFI() { - // attempt to connect to WiFi network: - Serial.print("Attempting to connect to WPA SSID: "); - Serial.println(ssid); - WiFi.begin(ssid, password); - - Serial.print("Connecting to WiFi .."); - while (WiFi.status() != WL_CONNECTED) { - Serial.print('.'); - delay(1000); - } - - Serial.println("You're connected to the network"); - Serial.println(); -} - -// Connect to MQTT Server -void connectMQTT() { - // You can provide a unique client ID, if not set the library uses Arduino-millis() - // Each client must have a unique client ID - mqttClient.setId("AudioTools"); - - // You can provide a username and password for authentication - // mqttClient.setUsernamePassword("username", "password"); - - Serial.print("Attempting to connect to the MQTT broker: "); - Serial.println(broker); - - if (!mqttClient.connect(broker, port)) { - Serial.print("MQTT connection failed! Error code = "); - Serial.println(mqttClient.connectError()); - - stop(); - } - - Serial.println("You're connected to the MQTT broker!"); - Serial.println(); -} - -// Send audio to MQTT Server -void sendMQTT() { - // make sure that we write wav header - out_stream.begin(info); - - // send message, the Print interface can be used to set the message contents - mqttClient.beginMessage(topic, SIZE * N, true); - - // copy audio data to mqtt: 100 * 1024 bytes - copier.copyN(N); - - mqttClient.endMessage(); -} - - -void setup() { - // Initialize logger - Serial.begin(115200); - AudioToolsLogger.begin(Serial, AudioToolsLogLevel::Info); - - // connect - connectWIFI(); - connectMQTT(); - - // setup audio - noise.begin(info); - in_stream.begin(info); - - // send Audio - sendMQTT(); -} - -void loop() { - // call poll() regularly to allow the library to send MQTT keep alives which - // avoids being disconnected by the broker - mqttClient.poll(); - delay(1000); -} \ No newline at end of file diff --git a/examples/examples-communication/rtsp/communication-audiokit-rtsp/communication-audiokit-rtsp.ino b/examples/examples-communication/rtsp/communication-audiokit-rtsp/communication-audiokit-rtsp.ino index 26ff8315ea..fc346013cb 100644 --- a/examples/examples-communication/rtsp/communication-audiokit-rtsp/communication-audiokit-rtsp.ino +++ b/examples/examples-communication/rtsp/communication-audiokit-rtsp/communication-audiokit-rtsp.ino @@ -10,13 +10,13 @@ */ #include "AudioTools.h" -#include "AudioTools/AudioLibs/AudioBoardStream.h" -#include "AudioTools/AudioLibs/RTSP.h" +#include "AudioLibs/AudioKit.h" +#include "AudioLibs/RTSP.h" #include "AudioStreamer.h" #include "RTSPServer.h" int port = 554; -AudioBoardStream kit(AudioKitEs8388V1); // Audio source +AudioKitStream kit; // Audio source RTSPSourceFromAudioStream source(kit); // IAudioSource for RTSP AudioStreamer streamer = AudioStreamer(&source); // Stream audio via RTSP RTSPServer rtsp = RTSPServer(&streamer, port); @@ -26,11 +26,11 @@ const char* password = "password"; void setup() { Serial.begin(115200); - AudioToolsLogger.begin(Serial, AudioToolsLogLevel::Info); + AudioLogger::instance().begin(Serial, AudioLogger::Info); // setup Audiokit as source auto cfg = kit.defaultConfig(RX_MODE); - cfg.input_device = ADC_INPUT_LINE2; + cfg.input_device = AUDIO_HAL_ADC_INPUT_LINE2; cfg.channels = 1; cfg.sample_rate = 8000; cfg.bits_per_sample = 16; diff --git a/examples/examples-communication/rtsp/communication-codec-rtsp/communication-codec-rtsp.ino b/examples/examples-communication/rtsp/communication-codec-rtsp/communication-codec-rtsp.ino index 52c9370248..823de66ff3 100644 --- a/examples/examples-communication/rtsp/communication-codec-rtsp/communication-codec-rtsp.ino +++ b/examples/examples-communication/rtsp/communication-codec-rtsp/communication-codec-rtsp.ino @@ -1,16 +1,6 @@ -/** - * @file communication-codec-rtsp.ino - * @author Phil Schatzmann - * @brief Provide Audio via RTSP using a codec. Depends on https://github.com/pschatzmann/Micro-RTSP-Audio - * @version 0.1 - * @date 2022-05-02 - * - * @copyright Copyright (c) 2022 - * - */ #include "AudioTools.h" -#include "AudioTools/AudioLibs/RTSP.h" // https://github.com/pschatzmann/Micro-RTSP-Audio -#include "AudioTools/AudioCodecs/CodecG7xx.h" // https://github.com/pschatzmann/arduino-libg7xx.git +#include "AudioLibs/RTSP.h" +#include "AudioCodecs/CodecG7xx.h" #include "RTSPServer.h" int port = 554; @@ -32,7 +22,10 @@ RTSPServer rtsp(rtsp_stream.streamer(), port); void setup() { Serial.begin(115200); - AudioToolsLogger.begin(Serial, AudioToolsLogLevel::Warning); + AudioLogger::instance().begin(Serial, AudioLogger::Warning); + + // Start Wifi & rtsp server + rtsp.begin(wifi, password); // Setup sine wave sineWave.begin(info, N_B4); @@ -40,9 +33,6 @@ void setup() { // Start Output Stream rtsp_stream.begin(info); - // Start Wifi & rtsp server - rtsp.begin(wifi, password); - } void loop() { diff --git a/examples/examples-communication/rtsp/communication-generator-rtsp/communication-generator-rtsp.ino b/examples/examples-communication/rtsp/communication-generator-rtsp/communication-generator-rtsp.ino index b473adea39..1dd232802e 100644 --- a/examples/examples-communication/rtsp/communication-generator-rtsp/communication-generator-rtsp.ino +++ b/examples/examples-communication/rtsp/communication-generator-rtsp/communication-generator-rtsp.ino @@ -10,7 +10,7 @@ */ #include "AudioTools.h" -#include "AudioTools/AudioLibs/RTSP.h" +#include "AudioLibs/RTSP.h" #include "AudioStreamer.h" #include "RTSPServer.h" @@ -30,7 +30,7 @@ RTSPServer rtsp = RTSPServer(&streamer, port); void setup() { Serial.begin(115200); - AudioToolsLogger.begin(Serial, AudioToolsLogLevel::Info); + AudioLogger::instance().begin(Serial, AudioLogger::Info); // Setup sine wave auto cfgS = sineWave.defaultConfig(); diff --git a/examples/examples-communication/rtsp/communication-rtsp-audiokit/communication-rtsp-audiokit.ino b/examples/examples-communication/rtsp/communication-rtsp-audiokit/communication-rtsp-audiokit.ino deleted file mode 100644 index 3243ecd693..0000000000 --- a/examples/examples-communication/rtsp/communication-rtsp-audiokit/communication-rtsp-audiokit.ino +++ /dev/null @@ -1,28 +0,0 @@ - -/** - * @file communication-rtsp-i2s.ino - * @author Phil Schatzmann - * @brief Demo for RTSP Client that is playing mp3. I tested with the live555 server with linux - * @version 0.1 - * @date 2022-05-02 - * - * @copyright Copyright (c) 2022 - * - */ -#include "AudioTools.h" // https://github.com/pschatzmann/arduino-audio-tools -#include "AudioTools/AudioCodecs/CodecMP3Helix.h" // https://github.com/pschatzmann/arduino-libhelix -#include "AudioTools/AudioLibs/AudioBoardStream.h" // https://github.com/pschatzmann/arduino-audio-driver -#include "AudioTools/AudioLibs/AudioClientRTSP.h" // install https://github.com/pschatzmann/arduino-live555 - -AudioBoardStream i2s(AudioKitEs8388V1); // final output of decoded stream -EncodedAudioStream out_mp3(&i2s, new MP3DecoderHelix()); // Decoding stream -AudioClientRTSP rtsp(1024); - -void setup(){ - rtsp.setLogin("ssid", "password"); - rtsp.begin("https://samples.mplayerhq.hu/A-codecs/MP3/01%20-%20Charity%20Case.mp3", out_mp3); -} - -void loop() { - rtsp.loop(); -} \ No newline at end of file diff --git a/examples/examples-communication/rtsp/communication-rtsp-i2s/communication-rtsp-i2s.ino b/examples/examples-communication/rtsp/communication-rtsp-i2s/communication-rtsp-i2s.ino deleted file mode 100644 index 00bb1f1605..0000000000 --- a/examples/examples-communication/rtsp/communication-rtsp-i2s/communication-rtsp-i2s.ino +++ /dev/null @@ -1,46 +0,0 @@ -/** - * @file communication-rtsp-i2s.ino - * @author Phil Schatzmann - * @brief Demo for RTSP Client that is playing mp3: tested with the live555 server with linux - * @version 0.1 - * @date 2022-05-02 - * - * @copyright Copyright (c) 2022 - * - */ - -#include "AudioTools.h" // https://github.com/pschatzmann/arduino-audio-tools -#include "AudioTools/AudioCodecs/CodecMP3Helix.h" // https://github.com/pschatzmann/arduino-libhelix -#include "RTSPSimpleClient.hh" // https://github.com/pschatzmann/arduino-live555.git - -I2SStream i2s; -EncodedAudioStream out_mp3(&i2s, new MP3DecoderHelix()); // Decoding stream -RTSPSimpleClient rtsp; - -void setup() { - Serial.begin(115200); - AudioToolsLogger.begin(Serial, AudioToolsLogLevel::Info); - - // setup output: make sure we can buffer 1 decoded frame - auto cfg_i2s = i2s.defaultConfig(TX_MODE); - cfg_i2s.buffer_size = 1024; - cfg_i2s.buffer_count = 10; - i2s.begin(cfg_i2s); - - out_mp3.begin(); - - // setup rtsp data source - auto cfg = rtsp.defaultConfig(); - cfg.ssid = "ssid"; - cfg.password = "password"; - cfg.url = "rtsp://192.168.1.38:8554/test.mp3"; - cfg.output = &out_mp3; - cfg.buffer_size = 1024*2; // space for 1 encoded frame - //cfg.is_tcp = false; // use udp when false (default false) - //cfg.is_blocking = false; // call singleStep in loop if false (default false) - rtsp.begin(cfg); -} - -void loop() { - rtsp.singleStep(); -} \ No newline at end of file diff --git a/examples/examples-communication/serial/mp3-custom/receive-mp3/receive-mp3.ino b/examples/examples-communication/serial/mp3-custom/receive-mp3/receive-mp3.ino deleted file mode 100644 index f866a45b00..0000000000 --- a/examples/examples-communication/serial/mp3-custom/receive-mp3/receive-mp3.ino +++ /dev/null @@ -1,61 +0,0 @@ - -/** - * @file receive-mp3.ino - * @brief Example of receiving an mp3 stream over serial and playing it over I2S - * using the AudioTools library. - * The processing implements a flow control using a custom digital pin. We process - * the data receiving in a separate task and the playback in the main loop. - */ - -#include "AudioTools.h" -#include "AudioTools/AudioCodecs/CodecMP3Helix.h" -#include "AudioTools/AudioLibs/AudioBoardStream.h" -#include "AudioTools/Concurrency/RTOS.h" - -const int flowControlPin = 17; // flow control pin acitive low -const int min_percent = 10; -const int max_percent = 90; - -AudioBoardStream i2s(AudioKitEs8388V1); // final output of decoded stream -MP3DecoderHelix helix; -EncodedAudioStream dec(&i2s, &helix); // Decoding stream -// queue -BufferRTOS buffer(0); -QueueStream queue(buffer); -// copy -StreamCopy copierFill(queue, Serial1); -StreamCopy copierPlay(dec, queue); - -Task task("mp3-copy", 10000, 1, 0); - -void setup() { - Serial.begin(115200); - AudioLogger::instance().begin(Serial, AudioLogger::Info); - - Serial1.begin(115200); - pinMode(flowControlPin, OUTPUT); // flow control pin - - // set up buffer here to allow PSRAM usage - buffer.resize(1024 * 10); // 10kB buffer - queue.begin(50); // start when half full - - // setup i2s - auto config = i2s.defaultConfig(TX_MODE); - i2s.begin(config); - - // setup decoder - dec.begin(); - - // start fill buffer copy task - task.begin([]() { - copierFill.copy(); - // data synchronization to prevent buffer overflow - if (buffer.levelPercent() >= max_percent) { - digitalWrite(flowControlPin, HIGH); // stop receiving - } else if (buffer.levelPercent() <= min_percent) { - digitalWrite(flowControlPin, LOW); // start receiving - } - }); -} - -void loop() { copierPlay.copy(); } diff --git a/examples/examples-communication/serial/mp3-custom/send-mp3/send-mp3.ino b/examples/examples-communication/serial/mp3-custom/send-mp3/send-mp3.ino deleted file mode 100644 index 66a096ce00..0000000000 --- a/examples/examples-communication/serial/mp3-custom/send-mp3/send-mp3.ino +++ /dev/null @@ -1,35 +0,0 @@ - -/** - * @file send-mp3.ino - * @brief Example of sending an mp3 stream over Serial the AudioTools library. - * We use a custom pin to control the flow of the data. If we are ready to - * receive data, we set the pin to LOW. - */ - -#include "AudioTools.h" -#include "AudioTools/AudioCodecs/CodecMP3Helix.h" - -URLStream url("http://github.com/ssid%22,%20%22password"); // or replace with ICYStream to get metadata -StreamCopy copier(Serial1, url); // copy url to decoder -// pins -const int flowControlPin = 17; - -void setup() { - Serial.begin(115200); - AudioToolsLogger.begin(Serial, AudioToolsLogLevel::Error); - - // setup flow control pin - pinMode(flowControlPin, INPUT_PULLUP); // flow control pin - - // setup serial data sink - Serial1.begin(115200); - - // mp3 radio - url.begin("http://stream.srg-ssr.ch/m/rsj/mp3_128", "audio/mp3"); -} - -void loop() { - if (digitalRead(flowControlPin) == LOW) { - copier.copy(); - } -} \ No newline at end of file diff --git a/examples/examples-communication/serial/mp3-xon-xoff/receive-mp3/receive-mp3.ino b/examples/examples-communication/serial/mp3-xon-xoff/receive-mp3/receive-mp3.ino deleted file mode 100644 index 7641a3d03b..0000000000 --- a/examples/examples-communication/serial/mp3-xon-xoff/receive-mp3/receive-mp3.ino +++ /dev/null @@ -1,65 +0,0 @@ - -/** - * @file receive-mp3.ino - * @brief Example of receiving an mp3 stream over serial and playing it over I2S - * using the AudioTools library. - * The processing implements a xon-xoff flow control over Serial. We - * process the data receiving in a separate task and the playback in the main - * loop. - */ - -#include "AudioTools.h" -#include "AudioTools/AudioCodecs/CodecMP3Helix.h" -#include "AudioTools/AudioLibs/AudioBoardStream.h" -#include "AudioTools/Concurrency/RTOS.h" - -// xon/xoff flow control -const char xon = 17; -const char xoff = 19; -const int min_percent = 10; -const int max_percent = 90; - -AudioBoardStream i2s(AudioKitEs8388V1); // final output of decoded stream -MP3DecoderHelix helix; -EncodedAudioStream dec(&i2s, &helix); // Decoding stream -// queue -BufferRTOS buffer(0); -QueueStream queue(buffer); -// copy -StreamCopy copierFill(queue, Serial1); -StreamCopy copierPlay(dec, queue); - -Task task("mp3-copy", 10000, 1, 0); - -void setup() { - Serial.begin(115200); - AudioLogger::instance().begin(Serial, AudioLogger::Info); - - Serial0.begin(115200); - - // set up buffer here to allow PSRAM usage - buffer.resize(1024 * 10); // 10kB buffer - queue.begin(50); // start when half full - - // setup i2s - auto config = i2s.defaultConfig(TX_MODE); - i2s.begin(config); - - // setup decoder - dec.begin(); - - // start fill buffer copy task - task.begin([]() { - copierFill.copy(); - // data synchronization to prevent buffer overflow - if (buffer.levelPercent() >= max_percent) { - Serial1.write(xoff); // stop receiving - Serial1.flush(); - } else if (buffer.levelPercent() <= min_percent) { - Serial1.write(xon); // start receiving - Serial1.flush(); - } - }); -} - -void loop() { copierPlay.copy(); } diff --git a/examples/examples-communication/serial/mp3-xon-xoff/send-mp3/send-mp3.ino b/examples/examples-communication/serial/mp3-xon-xoff/send-mp3/send-mp3.ino deleted file mode 100644 index 709249ae1c..0000000000 --- a/examples/examples-communication/serial/mp3-xon-xoff/send-mp3/send-mp3.ino +++ /dev/null @@ -1,47 +0,0 @@ - -/** - * @file send-mp3.ino - * @brief Example of sending an mp3 stream over Serial the AudioTools library. - * We use xon/xoff to control the flow of the data. - * I am using an ESP32 Dev Module for the test with the pins TX=17 and RX=16. - */ - -#include "AudioTools.h" - -URLStream url("http://github.com/ssid%22,%20%22password"); // or replace with ICYStream to get metadata -StreamCopy copier(Serial1, url); // copy url to decoder -// xon/xoff flow control -const char xon = 17; -const char xoff = 19; -bool is_active = false; - -void setup() { - Serial.begin(115200); - AudioToolsLogger.begin(Serial, AudioToolsLogLevel::Error); - - // setup serial data sink - Serial2.begin(115200); - - // mp3 radio - url.begin("http://stream.srg-ssr.ch/m/rsj/mp3_128", "audio/mp3"); -} - -// Determine if we can send data from the flow control sent by the receiver -bool isActive() { - char c = Serial2.read(); - switch (c) { - case xon: - is_active = true; - break; - case xoff: - is_active = false; - break; - } - return is_active; -} - -void loop() { - if (isActive()) { - copier.copy(); - } -} \ No newline at end of file diff --git a/examples/examples-communication/serial/mp3/receive-mp3/receive-mp3.ino b/examples/examples-communication/serial/mp3/receive-mp3/receive-mp3.ino deleted file mode 100644 index db847a61ed..0000000000 --- a/examples/examples-communication/serial/mp3/receive-mp3/receive-mp3.ino +++ /dev/null @@ -1,42 +0,0 @@ - -/** - * @file receive-mp3.ino - * @brief Example of receiving an mp3 stream over serial and playing it over I2S - * using the AudioTools library. - * The processing must be synchronized with RTS/CTS flow control to prevent a - * buffer overflow and lost data. - */ -#include "AudioTools.h" -#include "AudioTools/AudioCodecs/CodecMP3Helix.h" -#include "AudioTools/AudioLibs/AudioBoardStream.h" - - -AudioBoardStream i2s(AudioKitEs8388V1); // final output of decoded stream -EncodedAudioStream dec(&i2s, new MP3DecoderHelix()); // Decoding stream -HardwareSerial MP3Serial(1); // define a Serial for UART1 -StreamCopy copier(dec, MP3Serial); // copy url to decoder -// pins -const int MySerialTX = -1; -const int MySerialRX = 18; -const int MySerialRTS = -1; -const int MySerialCTS = 20; - -void setup() { - Serial.begin(115200); - AudioToolsLogger.begin(Serial, AudioToolsLogLevel::Info); - - // setup serial data source with flow control - MP3Serial.begin(115200, SERIAL_8N1); - MP3Serial.setPins(MySerialRX, MySerialTX, MySerialCTS, MySerialRTS); - MP3Serial.setHwFlowCtrlMode(UART_HW_FLOWCTRL_CTS_RTS); - - // setup i2s - auto config = i2s.defaultConfig(TX_MODE); - i2s.begin(config); - - // setup I2S based on sampling rate provided by decoder - dec.begin(); - -} - -void loop() { copier.copy(); } \ No newline at end of file diff --git a/examples/examples-communication/serial/mp3/send-mp3/send-mp3.ino b/examples/examples-communication/serial/mp3/send-mp3/send-mp3.ino deleted file mode 100644 index ae0223f8fd..0000000000 --- a/examples/examples-communication/serial/mp3/send-mp3/send-mp3.ino +++ /dev/null @@ -1,39 +0,0 @@ - -/** - * @file send-mp3.ino - * @brief ESP32 Example of sending an mp3 stream over Serial the AudioTools library. - * The processing must be synchronized with RTS/CTS flow control to prevent a - * buffer overflow and lost data. We get the mp3 from an URLStream. - * We used a ESP32 Dev Module for the test. - */ -#include - -#include "AudioTools.h" -#include "AudioTools/AudioCodecs/CodecMP3Helix.h" - -URLStream url("http://github.com/ssid%22,%20%22password"); // or replace with ICYStream to get metadata -HardwareSerial MP3Serial(1); // define a Serial for UART1 -StreamCopy copier(MP3Serial, url); // copy url to decoder -// pins -const int MySerialTX = 17; // TX2 -const int MySerialRX = -1; // not used -const int MySerialRTS = 4; // GPIO 4 -const int MySerialCTS = -1; // not useed - -void setup() { - Serial.begin(115200); - AudioToolsLogger.begin(Serial, AudioToolsLogLevel::Info); - - // improve performance - MP3Serial.setTxBufferSize(1024); // must be called before begin - - // setup serial data sink with flow control - MP3Serial.begin(115200, SERIAL_8N1); - MP3Serial.setPins(MySerialRX, MySerialTX, MySerialCTS, MySerialRTS); - MP3Serial.setHwFlowCtrlMode(UART_HW_FLOWCTRL_CTS_RTS); - - // mp3 radio - url.begin("http://stream.srg-ssr.ch/m/rsj/mp3_128", "audio/mp3"); -} - -void loop() { copier.copy(); } \ No newline at end of file diff --git a/examples/examples-communication/serial/send-8bit-receive/send-8bit-receive.ino b/examples/examples-communication/serial/send-8bit-receive/send-8bit-receive.ino deleted file mode 100644 index 9e852559a2..0000000000 --- a/examples/examples-communication/serial/send-8bit-receive/send-8bit-receive.ino +++ /dev/null @@ -1,59 +0,0 @@ -/** - * @file send-8bit-receive.ino - * @author Phil Schatzmann - * @brief Sending and receiving audio via Serial. You need to connect the RX pin - * with the TX pin! - * - * We send 8bit data over the wire, so any (small) data loss will not be audible - * - * @version 0.1 - * @date 2023-11-25 - * - * @copyright Copyright (c) 2022 - */ - -#include "AudioTools.h" -// #include "AudioTools/AudioLibs/AudioBoardStream.h" - -AudioInfo info(44100, 2, 16); -I2SStream out; // or AnalogAudioStream, AudioBoardStream etc -SineWaveGenerator sineWave(32000); -GeneratedSoundStream sound(sineWave); -auto &serial = Serial2; -EncoderL8 enc; -DecoderL8 dec; -EncodedAudioStream enc_stream(&serial, &enc); -EncodedAudioStream dec_stream(&out, &dec); -Throttle throttle(enc_stream); -StreamCopy copierOut(throttle, sound, 256); // copies sound into Serial -StreamCopy copierIn(dec_stream, serial, 256); // copies sound from Serial - -void setup() { - Serial.begin(115200); - AudioToolsLogger.begin(Serial, AudioToolsLogLevel::Warning); - - // Note the format for setting a serial port is as follows: - // Serial.begin(baud-rate, protocol, RX pin, TX pin); - Serial2.begin(3000000, SERIAL_8N1); - - sineWave.begin(info, N_B4); - throttle.begin(info); - enc_stream.begin(info); - dec_stream.begin(info); - - // start I2S - auto config = out.defaultConfig(TX_MODE); - config.copyFrom(info); - out.begin(config); - - // better visibility in logging - copierOut.setLogName("out"); - copierIn.setLogName("in"); -} - -void loop() { - // copy to serial - copierOut.copy(); - // copy from serial - copierIn.copy(); -} \ No newline at end of file diff --git a/examples/examples-communication/serial/send-adpcm-receive/send-adpcm-receive.ino b/examples/examples-communication/serial/send-adpcm-receive/send-adpcm-receive.ino deleted file mode 100644 index bcf104e3d0..0000000000 --- a/examples/examples-communication/serial/send-adpcm-receive/send-adpcm-receive.ino +++ /dev/null @@ -1,64 +0,0 @@ -/** - * @file send-adpcm-receive.ino - * @author Phil Schatzmann - * @brief Sending and receiving audio via Serial. You need to connect the RX pin - * with the TX pin! - * - * We send encoded ADPCM audio over the serial wire: The higher the transmission rate - * the higher the risk of data loss! - * - * @version 0.1 - * @date 2023-11-25 - * - * @copyright Copyright (c) 2022 - */ - -#include "AudioTools.h" -#include "AudioTools/AudioCodecs/CodecADPCM.h" // https://github.com/pschatzmann/adpcm -//#include "AudioTools/AudioLibs/AudioBoardStream.h" - -AudioInfo info(22000, 1, 16); -I2SStream out; // or AnalogAudioStream, AudioBoardStream etc -SineWaveGenerator sineWave(32000); -GeneratedSoundStream sound(sineWave); - -auto &serial = Serial2; -ADPCMEncoder enc(AV_CODEC_ID_ADPCM_IMA_WAV); -ADPCMDecoder dec(AV_CODEC_ID_ADPCM_IMA_WAV); -EncodedAudioStream enc_stream(&serial, &enc); -EncodedAudioStream dec_stream(&out, &dec); -Throttle throttle(enc_stream); -static int frame_size = 498; -StreamCopy copierOut(throttle, sound, frame_size); // copies sound into Serial -StreamCopy copierIn(dec_stream, serial, frame_size); // copies sound from Serial - -void setup() { - Serial.begin(115200); - AudioToolsLogger.begin(Serial, AudioToolsLogLevel::Warning); - - // Note the format for setting a serial port is as follows: - // Serial.begin(baud-rate, protocol, RX pin, TX pin); - Serial2.begin(115200, SERIAL_8N1); - - sineWave.begin(info, N_B4); - throttle.begin(info); - enc_stream.begin(info); - dec_stream.begin(info); - - // start I2S - auto config = out.defaultConfig(TX_MODE); - config.copyFrom(info); - out.begin(config); - - // better visibility in logging - copierOut.setLogName("out"); - copierIn.setLogName("in"); - -} - -void loop() { - // copy to serial - copierOut.copy(); - // copy from serial - copierIn.copy(); -} \ No newline at end of file diff --git a/examples/examples-communication/serial/send-adpcm_framed-receive/send-adpcm_framed-receive.ino b/examples/examples-communication/serial/send-adpcm_framed-receive/send-adpcm_framed-receive.ino deleted file mode 100644 index a25b96545d..0000000000 --- a/examples/examples-communication/serial/send-adpcm_framed-receive/send-adpcm_framed-receive.ino +++ /dev/null @@ -1,73 +0,0 @@ -/** - * @file send-adpcm-receive.ino - * @author Phil Schatzmann - * @brief Sending and receiving audio via Serial. You need to connect the RX pin - * with the TX pin! - * - * We send encoded ADPCM audio over the serial wire. In order to be able to recover - * from data loss in the encoded frames we use a Container - * - * @version 0.1 - * @date 2023-11-25 - * - * @copyright Copyright (c) 2022 - */ - -#include "AudioTools.h" -#include "AudioTools/AudioCodecs/CodecADPCM.h" // https://github.com/pschatzmann/adpcm -#include "AudioTools/AudioCodecs/ContainerBinary.h" -// #include "AudioTools/AudioLibs/AudioBoardStream.h" - -AudioInfo info(44100, 2, 16); -I2SStream out; // or AnalogAudioStream, AudioBoardStream etc -SineWaveGenerator sineWave(32000); -GeneratedSoundStream sound(sineWave); - -auto &serial = Serial2; -ADPCMEncoder enc(AV_CODEC_ID_ADPCM_IMA_WAV); -ADPCMDecoder dec(AV_CODEC_ID_ADPCM_IMA_WAV); -BinaryContainerEncoder bin_enc(&enc); -BinaryContainerDecoder bin_dec(&dec); -EncodedAudioOutput enc_stream(&serial, &bin_enc); -EncodedAudioOutput dec_stream(&out, &bin_dec); -Throttle throttle(enc_stream); -static int frame_size = 498; -StreamCopy copierOut(throttle, sound, frame_size); // copies sound into Serial -StreamCopy copierIn(dec_stream, serial, frame_size); // copies sound from Serial - -void setup() { - Serial.begin(115200); - AudioToolsLogger.begin(Serial, AudioToolsLogLevel::Warning); - - // Note the format for setting a serial port is as follows: - // Serial.begin(baud-rate, protocol, RX pin, TX pin); - Serial2.begin(1100000, SERIAL_8N1); - - sineWave.begin(info, N_B4); - throttle.begin(info); - -// increase the reliability - dec_stream.setFrameSize(frame_size); - enc_stream.setFrameSize(frame_size); - - enc_stream.begin(info); - dec_stream.begin(info); - - - // start I2S - auto config = out.defaultConfig(TX_MODE); - config.copyFrom(info); - out.begin(config); - - // better visibility in logging - copierOut.setLogName("out"); - copierIn.setLogName("in"); - -} - -void loop() { - // copy to serial - copierOut.copy(); - // copy from serial - copierIn.copy(); -} \ No newline at end of file diff --git a/examples/examples-communication/serial/send-receive/send-receive.ino b/examples/examples-communication/serial/send-receive/send-receive.ino index 0227ca1fb7..4286c8969e 100644 --- a/examples/examples-communication/serial/send-receive/send-receive.ino +++ b/examples/examples-communication/serial/send-receive/send-receive.ino @@ -3,9 +3,6 @@ * @author Phil Schatzmann * @brief Sending and receiving audio via Serial. You need to connect the RX pin * with the TX pin! - * The sine wave generator is providing the data as fast as possible, therefore we - * throttle on the sending side to prevent that the receiver is getting the data - * too fast. * * @version 0.1 * @date 2022-03-09 @@ -14,38 +11,33 @@ */ #include "AudioTools.h" -// #include "AudioTools/AudioLibs/AudioBoardStream.h" +// #include "AudioLibs/AudioKit.h" AudioInfo info(22000, 1, 16); SineWaveGenerator sineWave(32000); GeneratedSoundStream sound(sineWave); -I2SStream out; // or AnalogAudioStream, AudioBoardStream etc -auto &serial = Serial2; -Throttle throttle(serial); -StreamCopy copierOut(throttle, sound, 256); // copies sound into Serial -StreamCopy copierIn(out, serial, 256); // copies sound from Serial +I2SStream out; // or AudioKitStream +StreamCopy copierOut(Serial, sound, 256); // copies sound into Serial +StreamCopy copierIn(out, Serial, 256); // copies sound from Serial void setup() { - Serial.begin(115200); - AudioToolsLogger.begin(Serial, AudioToolsLogLevel::Warning); + Serial2.begin(115200); + AudioLogger::instance().begin(Serial2, AudioLogger::Warning); // Note the format for setting a serial port is as follows: // Serial.begin(baud-rate, protocol, RX pin, TX pin); - Serial2.begin(3000000, SERIAL_8N1 ); + Serial.begin(1000000, SERIAL_8N1); // Setup sine wave sineWave.begin(info, N_B4); - throttle.begin(info); // start I2S + Serial.println("starting I2S..."); auto config = out.defaultConfig(TX_MODE); config.copyFrom(info); out.begin(config); - // better visibility in logging - copierOut.setLogName("out"); - copierIn.setLogName("in"); - + Serial.println("started..."); } void loop() { diff --git a/examples/examples-communication/snapcast/snapclient-i2s/snapclient-i2s.ino b/examples/examples-communication/snapcast/snapclient-i2s/snapclient-i2s.ino deleted file mode 100644 index fe5ef1ab43..0000000000 --- a/examples/examples-communication/snapcast/snapclient-i2s/snapclient-i2s.ino +++ /dev/null @@ -1,64 +0,0 @@ -/** - * @file send-receive.ino - * @author Phil Schatzmann - * @brief Receive audio sent by snapcast on an ESP32 and output to i2s. - * I was testing with ffmpeg -i http://stream.srg-ssr.ch/m/rsj/mp3_128 -f s16le -ar 48000 /tmp/snapfifo - * More examples can be found at https://github.com/pschatzmann/arduino-snapclient/tree/main/examples - * @version 0.1 - * @date 2023-09-25 - * - * @copyright Copyright (c) 2022 - */ - -/** - * @brief SnapClient with Opus decoder: I2S OUtput - * @author Phil Schatzmann - * @copyright GPLv3 - */ - -#include "AudioTools.h" -#include "SnapClient.h" -#include "AudioTools/AudioCodecs/CodecOpus.h" - -#define ARDUINO_LOOP_STACK_SIZE (10 * 1024) - -OpusAudioDecoder opus; -I2SStream out; -WiFiClient wifi; -SnapTimeSyncDynamic synch(172, 10); // optional configuratioin -SnapClient client(wifi, out, opus); - -void setup() { - Serial.begin(115200); - - // login to wifi -> Define values in SnapConfig.h or replace them here - WiFi.begin(CONFIG_WIFI_SSID, CONFIG_WIFI_PASSWORD); - Serial.print("Connecting to WiFi .."); - while (WiFi.status() != WL_CONNECTED) { - Serial.print('.'); - delay(1000); - } - - // print ip address - Serial.println(); - Serial.println(WiFi.localIP()); - - // setup I2S to define custom pins - auto cfg = out.defaultConfig(); - cfg.pin_bck = 14; - cfg.pin_ws = 15; - cfg.pin_data = 22; - //cfg.buffer_size = 512; - //cfg.buffer_count = 6; - out.begin(cfg); - - // Define CONFIG_SNAPCAST_SERVER_HOST in SnapConfig.h or here - // client.setServerIP(IPAddress(192,168,1,38)); - - // start snap client - client.begin(synch); -} - -void loop() { - client.doLoop(); -} \ No newline at end of file diff --git a/examples/examples-communication/udp/communication-udp-receive/communication-udp-receive.ino b/examples/examples-communication/udp/communication-udp-receive/communication-udp-receive.ino index 1333980e10..a2649e083f 100644 --- a/examples/examples-communication/udp/communication-udp-receive/communication-udp-receive.ino +++ b/examples/examples-communication/udp/communication-udp-receive/communication-udp-receive.ino @@ -11,20 +11,20 @@ * */ #include "AudioTools.h" -#include "AudioTools/Communication/UDPStream.h" -// #include "AudioTools/AudioLibs/AudioBoardStream.h" +#include "AudioLibs/Communication.h" +// #include "AudioLibs/AudioKit.h" const char* ssid="SSID"; const char* password="password"; AudioInfo info(22000, 1, 16); UDPStream udp(ssid, password); const int udpPort = 7000; -I2SStream out; // or ony other e.g. AudioBoardStream +I2SStream out; // or ony other e.g. AudioKitStream StreamCopy copier(out, udp); void setup() { Serial.begin(115200); - AudioToolsLogger.begin(Serial, AudioToolsLogLevel::Info); + AudioLogger::instance().begin(Serial, AudioLogger::Info); // start UDP receive udp.begin(udpPort); diff --git a/examples/examples-communication/udp/communication-udp-send/communication-udp-send.ino b/examples/examples-communication/udp/communication-udp-send/communication-udp-send.ino index b46fb3bbd2..4b075066d4 100644 --- a/examples/examples-communication/udp/communication-udp-send/communication-udp-send.ino +++ b/examples/examples-communication/udp/communication-udp-send/communication-udp-send.ino @@ -10,7 +10,7 @@ */ #include "AudioTools.h" -#include "AudioTools/Communication/UDPStream.h" +#include "AudioLibs/Communication.h" const char *ssid = "ssid"; const char *password = "password"; @@ -18,18 +18,15 @@ AudioInfo info(22000, 1, 16); SineWaveGenerator sineWave( 32000); // subclass of SoundGenerator with max amplitude of 32000 GeneratedSoundStream sound( sineWave); // Stream generated from sine wave UDPStream udp(ssid, password); -Throttle throttle(udp); +Throttle throttle; IPAddress udpAddress(192, 168, 1, 255); const int udpPort = 7000; -StreamCopy copier(throttle, sound); // copies sound into i2s +StreamCopy copier(udp, sound); // copies sound into i2s void setup() { Serial.begin(115200); - AudioToolsLogger.begin(Serial, AudioToolsLogLevel::Info); - - // Setup sine wave - sineWave.begin(info, N_B4); + AudioLogger::instance().begin(Serial, AudioLogger::Info); // Define udp address and port udp.begin(udpAddress, udpPort); @@ -40,9 +37,13 @@ void setup() { //cfg.correction_ms = 0; throttle.begin(cfg); + // Setup sine wave + sineWave.begin(info, N_B4); Serial.println("started..."); } void loop() { - copier.copy(); + throttle.startDelay(); + int bytes = copier.copy(); + throttle.delayBytes(bytes); } \ No newline at end of file diff --git a/examples/examples-communication/usb/adafruit/adafruit.ino b/examples/examples-communication/usb/adafruit/adafruit.ino deleted file mode 100644 index d1a0ed26f6..0000000000 --- a/examples/examples-communication/usb/adafruit/adafruit.ino +++ /dev/null @@ -1,69 +0,0 @@ -#include "AudioTools.h" -#include "AudioTools/Sandbox/USB/USBDeviceAudioAdafruit.h" - -size_t sample_count_mic = 0; -size_t sample_count_spk = 0; - -// Microphone: generate data for USB -size_t readCB(uint8_t* data, size_t len, USBDeviceAudio& ref) { - int16_t* data16 = (int16_t*)data; - size_t samples = len / sizeof(int16_t); - size_t result = 0; - // generate random stereo data - for (int j = 0; j < samples; j+=2) { - data16[j] = random(-32000, 32000); - data16[j+1] = random(-32000, 32000);; - result += sizeof(int16_t)*2; - sample_count_mic += 2; - } - return result; -} - -// Speaker: receive data from USB and write them to the final destination -size_t writeCB(const uint8_t* data, size_t len, USBDeviceAudio & ref) { - int16_t* data16 = (int16_t*)data; - size_t samples = len / sizeof(int16_t); - sample_count_spk += samples; - return len; -} - -auto config() { - USBAudioConfig result; - result.p_read_callback = readCB; - result.p_write_callback = writeCB; - return result; -} - -USBDeviceAudioAdafruit usb{config()}; - -void setup() { - // Manual begin() is required on core without built-in support e.g. mbed rp2040 - if (!TinyUSBDevice.isInitialized()) { - TinyUSBDevice.begin(0); - } - - Serial.begin(115200); - - // If already enumerated, additional class driver begin() e.g msc, hid, midi won't take effect until re-enumeration - if (TinyUSBDevice.mounted()) { - TinyUSBDevice.detach(); - delay(10); - TinyUSBDevice.attach(); - } -} - -void loop() { - #ifdef TINYUSB_NEED_POLLING_TASK - // Manual call tud_task since it isn't called by Core's background - TinyUSBDevice.task(); - #endif - // use LED do display status - if (usb.updateLED(LED_BUILTIN)){ - Serial.print("Total Microphone samples: "); - Serial.print(sample_count_mic); - Serial.print(" / Speaker samples: "); - Serial.print(sample_count_spk); - Serial.print(" / Sample rate: "); - Serial.println(usb.rate()); - } -} \ No newline at end of file diff --git a/examples/examples-communication/usb/microphone/microphone.ino b/examples/examples-communication/usb/microphone/microphone.ino deleted file mode 100644 index ab84042728..0000000000 --- a/examples/examples-communication/usb/microphone/microphone.ino +++ /dev/null @@ -1,51 +0,0 @@ -/********************************************************************* - This example generates a sawtooth that you can output on your PC - We use the AudioTools to generate the data input. - - We could use the callback function here as well, but we demo how - to integrate with a (fast) Arduino Stream. - - Please read the Wiki of the project for the supported platforms! - -*********************************************************************/ - -#include "Adafruit_TinyUSB.h" // https://github.com/pschatzmann/Adafruit_TinyUSB_Arduino -#include "AudioTools.h" // https://github.com/pschatzmann/arduino-audio-tools - -Adafruit_USBD_Audio usb; -AudioInfo info(44100, 2, 16); -SawToothGenerator sawtooth; -GeneratedSoundStream sawthooth_stream(sawtooth); - -void setup() { - // Manual begin() is required on core without built-in support e.g. mbed rp2040 - if (!TinyUSBDevice.isInitialized()) { - TinyUSBDevice.begin(0); - } - - Serial.begin(115200); - //while(!Serial); // wait for serial - - // generate 493 hz (note B4) - sawtooth.begin(info, 493.0f); - - // Start USB device as Audio Source - usb.setInput(sawthooth_stream); - usb.begin(info.sample_rate, info.channels, info.bits_per_sample); - - // If already enumerated, additional class driverr begin() e.g msc, hid, midi won't take effect until re-enumeration - if (TinyUSBDevice.mounted()) { - TinyUSBDevice.detach(); - delay(10); - TinyUSBDevice.attach(); - } -} - -void loop() { - #ifdef TINYUSB_NEED_POLLING_TASK - // Manual call tud_task since it isn't called by Core's background - TinyUSBDevice.task(); - #endif - // optional: use LED do display status - usb.updateLED(); -} \ No newline at end of file diff --git a/examples/examples-communication/vban/player-sdmmc-vban/player-sdmmc-vban.ino b/examples/examples-communication/vban/player-sdmmc-vban/player-sdmmc-vban.ino deleted file mode 100644 index 3dd3f51443..0000000000 --- a/examples/examples-communication/vban/player-sdmmc-vban/player-sdmmc-vban.ino +++ /dev/null @@ -1,73 +0,0 @@ -/** - * @file player-sdmmc-vban.ino - * @author Phil Schatzmann - * @brief decodes mp3 to vban: We need to use a queue to prevent delays to get too big. - * We try to keep the queue filled, so that we can always provide pcm data to vban - */ - -#include "AudioTools.h" -#include "AudioTools/AudioCodecs/CodecMP3Helix.h" -#include "AudioTools/AudioLibs/VBANStream.h" -#include "AudioTools/Disk/AudioSourceSDMMC.h" // or AudioSourceIdxSDMMC.h - -const char *startFilePath="/"; -const char* ext="mp3"; -const int mp3_pcm_size = 14000; -AudioInfo info(44100,2,16); -AudioSourceSDMMC source(startFilePath, ext); -RingBuffer ring_buffer(mp3_pcm_size*3); -QueueStream queue(ring_buffer); -MP3DecoderHelix decoder; // or change to MP3DecoderMAD -AudioPlayer player(source, queue, decoder); - -VBANStream out; -StreamCopy copier(out, queue, 2048); // 44100 needs 2048 - - -void fillQueue(){ - // fill queue when smaller then limit - if(ring_buffer.availableForWrite() >= mp3_pcm_size){ - player.copy(); - // activate copy when limit is reached - if (!copier.isActive() && ring_buffer.available()>=23000){ - copier.setActive(true); - LOGI("copier active"); - } - } - LOGI("available: %d - avail to write: %d", ring_buffer.available(), ring_buffer.availableForWrite()); -} - - -void setup() { - Serial.begin(115200); - AudioToolsLogger.begin(Serial, AudioToolsLogLevel::Warning); - - // setup output - auto cfg = out.defaultConfig(TX_MODE); - cfg.copyFrom(info); - cfg.ssid = "ssid"; - cfg.password = "password"; - cfg.stream_name = "Stream1"; - cfg.target_ip = IPAddress{192,168,1,37}; - cfg.throttle_active = true; - //cfg.throttle_correction_us = 0; - if (!out.begin(cfg)) stop(); - - // setup player - player.setVolume(0.7); - player.begin(); - - // select file with setPath() or setIndex() - //player.setPath("/ZZ Top/Unknown Album/Lowrider.mp3"); - //player.setIndex(1); // 2nd file - - queue.begin(); // start queue - copier.setActive(false); // deactivate copy - -} - -void loop() { - fillQueue(); - // output from queue - copier.copy(); -} \ No newline at end of file diff --git a/examples/examples-communication/vban/streams-audiokit-vban/streams-audiokit-vban.ino b/examples/examples-communication/vban/streams-audiokit-vban/streams-audiokit-vban.ino deleted file mode 100644 index c356859ded..0000000000 --- a/examples/examples-communication/vban/streams-audiokit-vban/streams-audiokit-vban.ino +++ /dev/null @@ -1,47 +0,0 @@ -/** - * @file streams-i2s-vban.ino - * @author Phil Schatzmann - * @brief sends signal from i2s (using an AudioKit) to VBAN Receptor App - */ - -#include "AudioTools.h" -#include "AudioTools/AudioLibs/VBANStream.h" -#include "AudioTools/AudioLibs/AudioBoardStream.h" // comment out when not using AudioKit - -AudioInfo info(44100, 2, 16); -AudioBoardStream in(AudioKitEs8388V1); // Audio source e.g. replace with I2SStream -VBANStream out; -StreamCopy copier(out, in, 2048); // copies sound into i2s - -// Arduino Setup -void setup(void) { - // Open Serial - Serial.begin(115200); - while(!Serial); - AudioToolsLogger.begin(Serial, AudioToolsLogLevel::Info); - - // setup output - auto cfg = out.defaultConfig(TX_MODE); - cfg.copyFrom(info); - cfg.ssid = "ssid"; - cfg.password = "password"; - cfg.stream_name = "Stream1"; - cfg.target_ip = IPAddress{192,168,1,37}; // comment out to broadcast - cfg.throttle_active = false; // generator is much too fast, we need to stall - if (!out.begin(cfg)) stop(); - - // Setup input from mic - // setup input - auto cfg_in = in.defaultConfig(RX_MODE); - cfg_in.sd_active = false; - cfg_in.buffer_size = 256; - cfg_in.buffer_count = 4; - cfg_in.copyFrom(info); - cfg_in.input_device = ADC_INPUT_LINE2; // microphone - in.begin(cfg_in); -} - -// Arduino loop - copy sound to out -void loop() { - copier.copy(); -} \ No newline at end of file diff --git a/examples/examples-communication/vban/streams-generator-vban/streams-generator-vban.ino b/examples/examples-communication/vban/streams-generator-vban/streams-generator-vban.ino deleted file mode 100644 index 2a149c8342..0000000000 --- a/examples/examples-communication/vban/streams-generator-vban/streams-generator-vban.ino +++ /dev/null @@ -1,42 +0,0 @@ -/** - * @file streams-generator-vban.ino - * @author Phil Schatzmann - * @brief sends sine test signal to VBAN Receptor App - */ - -#include "AudioTools.h" -#include "AudioTools/AudioLibs/VBANStream.h" - -AudioInfo info(44100, 2, 16); -SineWaveGenerator sineWave(32000); // subclass of SoundGenerator with max amplitude of 32000 -GeneratedSoundStream sound(sineWave); // Stream generated from sine wave -VBANStream out; -StreamCopy copier(out, sound, 2048); // 44100 needs 2048 - -// Arduino Setup -void setup(void) { - // Open Serial - Serial.begin(115200); - while(!Serial); - AudioToolsLogger.begin(Serial, AudioToolsLogLevel::Info); - - // setup output - auto cfg = out.defaultConfig(TX_MODE); - cfg.copyFrom(info); - cfg.ssid = "ssid"; - cfg.password = "password"; - cfg.stream_name = "Stream1"; - cfg.target_ip = IPAddress{192,168,1,37}; - cfg.throttle_active = true; - //cfg.throttle_correction_us = 0; // optimize overload and underrun - if (!out.begin(cfg)) stop(); - - // Setup sine wave - sineWave.begin(info, N_B4); - Serial.println("started..."); -} - -// Arduino loop - copy sound to out -void loop() { - copier.copy(); -} \ No newline at end of file diff --git a/examples/examples-communication/vban/streams-vban-audiokit/streams-vban-audiokit.ino b/examples/examples-communication/vban/streams-vban-audiokit/streams-vban-audiokit.ino deleted file mode 100644 index a82dbfcbc4..0000000000 --- a/examples/examples-communication/vban/streams-vban-audiokit/streams-vban-audiokit.ino +++ /dev/null @@ -1,40 +0,0 @@ -/** - * @file streams-i2s-vban.ino - * @author Phil Schatzmann - * @brief sends signal from i2s (using an AudioKit) to VBAN Receptor App - */ - -#include "AudioTools.h" -#include "AudioTools/AudioLibs/VBANStream.h" -#include "AudioTools/AudioLibs/AudioBoardStream.h" // comment out when not using AudioKit - -AudioBoardStream out(AudioKitEs8388V1); // Audio source e.g. replace with I2SStream -VBANStream in; -StreamCopy copier(out, in); // copies sound into i2s - -// Arduino Setup -void setup(void) { - // Open Serial - Serial.begin(115200); - while(!Serial); - AudioToolsLogger.begin(Serial, AudioToolsLogLevel::Info); - - // setup output - auto cfg_out = out.defaultConfig(TX_MODE); - if (!out.begin(cfg_out)) stop(); - - // format changes in vban must change the output as well - in.addNotifyAudioChange(out); - - // setup input from vban - auto cfg_in = in.defaultConfig(RX_MODE); - cfg_in.ssid = "ssid"; - cfg_in.password = "password"; - cfg_in.stream_name = "Talkie"; - in.begin(cfg_in); -} - -// Arduino loop - copy sound to out -void loop() { - copier.copy(); -} \ No newline at end of file diff --git a/examples/examples-custom-boards/cross_band_handy_walkie_talkie/cross_band_handy_walkie_talkie.ino b/examples/examples-custom-boards/cross_band_handy_walkie_talkie/cross_band_handy_walkie_talkie.ino index b16295bf75..1c9475d3ca 100644 --- a/examples/examples-custom-boards/cross_band_handy_walkie_talkie/cross_band_handy_walkie_talkie.ino +++ b/examples/examples-custom-boards/cross_band_handy_walkie_talkie/cross_band_handy_walkie_talkie.ino @@ -8,42 +8,48 @@ */ #include "AudioTools.h" -#include "AudioTools/AudioLibs/AudioBoardStream.h" +#include "AudioLibs/AudioKit.h" #include "SD.h" AudioInfo info(32000, 2, 16); -SineWaveGenerator sineWave( - 32000); // subclass of SoundGenerator with max amplitude of 32000 -GeneratedSoundStream sound( - sineWave); // Stream generated from sine wave -DriverPins my_pins; -AudioBoard board(AudioDriverES8388, my_pins); -AudioBoardStream out(board); -StreamCopy copier(out, sound); // copies sound into i2s +SineWaveGenerator sineWave(32000); // subclass of SoundGenerator with max amplitude of 32000 +GeneratedSoundStream sound(sineWave); // Stream generated from sine wave +AudioKitStream out; +StreamCopy copier(out, sound); // copies sound into i2s // Arduino Setup void setup(void) { // Open Serial Serial.begin(115200); - while (!Serial); - AudioToolsLogger.begin(Serial, AudioToolsLogLevel::Info); - - // sd pins: clk, miso, mosi,cs, - my_pins.addSPI(ESP32PinsSD{PinFunction::SD, 44, 42, 43, 2, SPI}); - // add i2c codec pins: scl, sda, port, frequency - my_pins.addI2C(PinFunction::CODEC, 35, 36); - // add i2s pins: mclk, bck, ws,data_out, data_in ,(port) - my_pins.addI2S(PinFunction::CODEC, 47, 21, 12, 14, 11); + while(!Serial); + AudioLogger::instance().begin(Serial, AudioLogger::Info); + //LOGLEVEL_AUDIOKIT = AudioKitDebug; // start I2S Serial.println("starting I2S..."); auto config = out.defaultConfig(TX_MODE); config.copyFrom(info); - config.sd_active = true; + config.sd_active = false; + config.default_actions_active = flase; + // i2c + config.pins.i2c_sda = 36; + config.pins.i2c_scl = 35; + // i2s + config.pin_mclk = 47 + config.pin_bck = 21; + config.pin_ws = 12; + config.pin_data = 14; + config.pin_data_in = 11; + + //config.sd_active = false; + config.pins.sd_cs = 2; + config.pins.sd_miso = 42; + config.pins.sd_mosi = 43; + config.pins.sd_clk = 44; out.begin(config); // check SD drive - if (!SD.begin(2)) { + if(!SD.begin(config.pins.sd_cs)){ Serial.println("Card Mount Failed"); stop(); } @@ -54,4 +60,6 @@ void setup(void) { } // Arduino loop - copy sound to out -void loop() { copier.copy(); } \ No newline at end of file +void loop() { + copier.copy(); +} \ No newline at end of file diff --git a/examples/examples-custom-boards/esp32-audio-dev-mini/esp32-audio-dev-mini.ino b/examples/examples-custom-boards/esp32-audio-dev-mini/esp32-audio-dev-mini.ino index ca08ee228d..7bbde9a3f2 100644 --- a/examples/examples-custom-boards/esp32-audio-dev-mini/esp32-audio-dev-mini.ino +++ b/examples/examples-custom-boards/esp32-audio-dev-mini/esp32-audio-dev-mini.ino @@ -18,7 +18,7 @@ void setup(void) { // Open Serial Serial.begin(115200); while(!Serial); - AudioToolsLogger.begin(Serial, AudioToolsLogLevel::Info); + AudioLogger::instance().begin(Serial, AudioLogger::Info); // start I2S Serial.println("starting I2S..."); diff --git a/examples/examples-custom-boards/esp32s3-mic-cam/Overview.jpg b/examples/examples-custom-boards/esp32s3-mic-cam/Overview.jpg deleted file mode 100644 index 668e75eb30..0000000000 Binary files a/examples/examples-custom-boards/esp32s3-mic-cam/Overview.jpg and /dev/null differ diff --git a/examples/examples-custom-boards/esp32s3-mic-cam/README.md b/examples/examples-custom-boards/esp32s3-mic-cam/README.md deleted file mode 100644 index 3ab9848c5d..0000000000 --- a/examples/examples-custom-boards/esp32s3-mic-cam/README.md +++ /dev/null @@ -1,16 +0,0 @@ - -## ESP32S3 Camera Baord - -Here are the sketches which test the functionality of this ESP32 S3 camera board - -![ESP32S3 Camera Board](Overview.jpg) - - -Here is a summary of the limitations/issues that I have found: - -- I could not make the 4 pin SDMMC to work. -- The microphone just provides noise -- The board has no Boot button, so you can not set the board easily into upload mode. Here is the work around: - - connect GND with Pin 0 - - press and release the SWT2 (Reset/EN button) - - disconnect Pin 0 \ No newline at end of file diff --git a/examples/examples-custom-boards/esp32s3-mic-cam/Schematic Diagram.pdf b/examples/examples-custom-boards/esp32s3-mic-cam/Schematic Diagram.pdf deleted file mode 100644 index c2bc7369e2..0000000000 Binary files a/examples/examples-custom-boards/esp32s3-mic-cam/Schematic Diagram.pdf and /dev/null differ diff --git a/examples/examples-custom-boards/esp32s3-mic-cam/button/button.ino b/examples/examples-custom-boards/esp32s3-mic-cam/button/button.ino deleted file mode 100644 index c89172ad8e..0000000000 --- a/examples/examples-custom-boards/esp32s3-mic-cam/button/button.ino +++ /dev/null @@ -1,13 +0,0 @@ - -/// If button is pressed it is pulled low - -const int BUTTON = 38; - -void setup() { - Serial.begin(115200); - pinMode(BUTTON, INPUT); -} - -void loop() { - Serial.println(digitalRead(BUTTON)); -} \ No newline at end of file diff --git a/examples/examples-custom-boards/esp32s3-mic-cam/camera/Camera.h b/examples/examples-custom-boards/esp32s3-mic-cam/camera/Camera.h deleted file mode 100644 index ff53fd02a8..0000000000 --- a/examples/examples-custom-boards/esp32s3-mic-cam/camera/Camera.h +++ /dev/null @@ -1,89 +0,0 @@ -#pragma once - -#include -#include "esp_camera.h" - -class Camera; - -/*** - * @brief A simple Arduino C++ API over the ESP32 camera functionality. - * @author Phil Schatzmann - */ - -class Camera { - // esp_camera_fb_return - struct Deleter { - void operator()(camera_fb_t *ptr) const { - if (ptr) { - esp_camera_fb_return(ptr); - } - } - }; - - public: - Camera() = default; - /** - * @brief Initialize the camera driver - * - * @note call camera_probe before calling this function - * - * This function detects and configures camera over I2C interface, - * allocates framebuffer and DMA buffers, - * initializes parallel I2S input, and sets up DMA descriptors. - * - * Currently this function can only be called once and there is - * no way to de-initialize this module. - * - * @param config Camera configuration parameters - * - * @return RESULT_OK on success - */ - bool begin(const camera_config_t &config) { - return esp_camera_init(&config) == ESP_OK; - } - - /** - * @brief Deinitialize the camera driver - * - * @return - * - RESULT_OK on success - * - ERR_INVALID_STATE if the driver hasn't been initialized yet - */ - bool end(void) { return esp_camera_deinit() == ESP_OK; } - - /** - * @brief Obtain unique_ptr to a frame buffer. - * - * @return pointer to the frame buffer - */ - auto frameBuffer(void) { - return std::unique_ptr(esp_camera_fb_get()); - } - - /** - * @brief Save camera settings to non-volatile-storage (NVS) - * - * @param key A unique nvs key name for the camera settings - */ - bool settingsSave(const char *key) { - return esp_camera_save_to_nvs(key) == ESP_OK; - } - - /** - * @brief Load camera settings from non-volatile-storage (NVS) - * - * @param key A unique nvs key name for the camera settings - */ - bool settingsLoad(const char *key) { - return esp_camera_load_from_nvs(key) == ESP_OK; - } - - /** - * @brief Return the frame buffer to be reused again. - * - * @param fb Pointer to the frame buffer - */ - void returnFrameBuffer(camera_fb_t &fb) { return esp_camera_fb_return(&fb); } -}; - - diff --git a/examples/examples-custom-boards/esp32s3-mic-cam/camera/camera.ino b/examples/examples-custom-boards/esp32s3-mic-cam/camera/camera.ino deleted file mode 100644 index 8592486b60..0000000000 --- a/examples/examples-custom-boards/esp32s3-mic-cam/camera/camera.ino +++ /dev/null @@ -1,68 +0,0 @@ - -// important settings: -// - USB CDC on Boot: Enabled (to see the Serial.print) -// - PSRAM: QSPI PSRAM (to have enough memory for the framebuffer) - -#include "Camera.h" - -#define XCLK_GPIO_NUM 10 -#define SIOD_GPIO_NUM 21 -#define SIOC_GPIO_NUM 14 -#define Y9_GPIO_NUM 11 -#define Y8_GPIO_NUM 9 -#define Y7_GPIO_NUM 8 -#define Y6_GPIO_NUM 6 -#define Y5_GPIO_NUM 4 -#define Y4_GPIO_NUM 2 -#define Y3_GPIO_NUM 3 -#define Y2_GPIO_NUM 5 -#define VSYNC_GPIO_NUM 13 -#define HREF_GPIO_NUM 12 -#define PCLK_GPIO_NUM 7 -#define LED_GPIO_NUM 34 - -Camera camera; -camera_config_t config; - -void setup() { - Serial.begin(115200); - while(!Serial); - - config.ledc_timer = LEDC_TIMER_0; - config.pin_d0 = Y2_GPIO_NUM; - config.pin_d1 = Y3_GPIO_NUM; - config.pin_d2 = Y4_GPIO_NUM; - config.pin_d3 = Y5_GPIO_NUM; - config.pin_d4 = Y6_GPIO_NUM; - config.pin_d5 = Y7_GPIO_NUM; - config.pin_d6 = Y8_GPIO_NUM; - config.pin_d7 = Y9_GPIO_NUM; - config.pin_xclk = XCLK_GPIO_NUM; - config.pin_pclk = PCLK_GPIO_NUM; - config.pin_vsync = VSYNC_GPIO_NUM; - config.pin_href = HREF_GPIO_NUM; - config.pin_sccb_sda = SIOD_GPIO_NUM; - config.pin_sccb_scl = SIOC_GPIO_NUM; - config.xclk_freq_hz = 20000000; - config.pixel_format = PIXFORMAT_JPEG; // YUV422,GRAYSCALE,RGB565,JPEG - config.frame_size = FRAMESIZE_HD; // - config.jpeg_quality = 16; - config.fb_count = 2; // 8 - config.fb_location = CAMERA_FB_IN_PSRAM; /*!< The location where the frame - buffer will be allocated */ - config.grab_mode = CAMERA_GRAB_LATEST; /*!< When buffers should be filled */ - - - if (!camera.begin(config)){ - Serial.println("Camera failed"); - while(true); - } - Serial.print("Recording..."); -} - -void loop() { - - auto fb = camera.frameBuffer(); - Serial.println(fb->len); - -} \ No newline at end of file diff --git a/examples/examples-custom-boards/esp32s3-mic-cam/color-led/color-led.ino b/examples/examples-custom-boards/esp32s3-mic-cam/color-led/color-led.ino deleted file mode 100644 index 3c90c9115b..0000000000 --- a/examples/examples-custom-boards/esp32s3-mic-cam/color-led/color-led.ino +++ /dev/null @@ -1,19 +0,0 @@ -// Demo for the color LED (SK6812) - -#include - -const int NUM_LEDS = 1; -const int DATA_PIN = 33; -CRGB led; - -void setup() { - FastLED.addLeds(&led, NUM_LEDS); -} -void loop() { - led = CRGB::Red; - FastLED.show(); - delay(1000); - led = CRGB::Black; - FastLED.show(); - delay(1000); -} \ No newline at end of file diff --git a/examples/examples-custom-boards/esp32s3-mic-cam/led/led.ino b/examples/examples-custom-boards/esp32s3-mic-cam/led/led.ino deleted file mode 100644 index 3bc620056f..0000000000 --- a/examples/examples-custom-boards/esp32s3-mic-cam/led/led.ino +++ /dev/null @@ -1,17 +0,0 @@ -// Demo for the LED - -const int LED=34; - -// the setup function runs once when you press reset or power the board -void setup() { - // initialize digital pin as an output. - pinMode(LED, OUTPUT); -} - -// the loop function runs over and over again forever -void loop() { - digitalWrite(LED, HIGH); // turn the LED on (HIGH is the voltage level) - delay(1000); // wait for a second - digitalWrite(LED, LOW); // turn the LED off by making the voltage LOW - delay(1000); // wait for a second -} \ No newline at end of file diff --git a/examples/examples-custom-boards/esp32s3-mic-cam/mic/mic.ino b/examples/examples-custom-boards/esp32s3-mic-cam/mic/mic.ino deleted file mode 100644 index 815e08e67b..0000000000 --- a/examples/examples-custom-boards/esp32s3-mic-cam/mic/mic.ino +++ /dev/null @@ -1,37 +0,0 @@ -// The module comes with a MSM261 I2S Microphone -// Make sure that USB CDC on Boot is enabled - - -const int I2S_WS = 37; -const int I2S_SCK = 36; -const int I2S_SD = 35; -const AudioInfo info(8000, 1, 16); -I2SStream i2sStream; // Access I2S as stream -CsvOutput csvOutput(Serial); -StreamCopy copier(csvOutput, i2sStream); // copy i2sStream to csvOutput - -// Arduino Setup -void setup(void) { - Serial.begin(115200); - AudioLogger::instance().begin(Serial, AudioLogger::Info); - - auto cfg = i2sStream.defaultConfig(RX_MODE); - cfg.copyFrom(info); - cfg.i2s_format = I2S_STD_FORMAT; // or try with I2S_LSB_FORMAT - cfg.use_apll = false; - cfg.pin_data = I2S_SD; - cfg.pin_mck = I2S_SCK; - cfg.pin_ws = I2S_WS; - i2sStream.begin(cfg); - - // make sure that we have the correct channels set up - csvOutput.begin(info); - - Serial.println("starting..."); - -} - -// Arduino loop - copy data -void loop() { - copier.copy(); -} \ No newline at end of file diff --git a/examples/examples-custom-boards/esp32s3-mic-cam/psram/psram.ino b/examples/examples-custom-boards/esp32s3-mic-cam/psram/psram.ino deleted file mode 100644 index 2d24c17b8e..0000000000 --- a/examples/examples-custom-boards/esp32s3-mic-cam/psram/psram.ino +++ /dev/null @@ -1,20 +0,0 @@ - - -/// set -/// - USB CDC on Boot: enabled -/// - PSRAM: QSPI RAM - -#include - -void setup() { - Serial.begin(115200); - - Serial.printf("Total heap: %d\n", ESP.getHeapSize()); - Serial.printf("Free heap: %d\n", ESP.getFreeHeap()); - Serial.printf("Total PSRAM: %d\n", ESP.getPsramSize()); - Serial.printf("Free PSRAM: %d\n", ESP.getFreePsram()); -} - -void loop() { - -} \ No newline at end of file diff --git a/examples/examples-custom-boards/esp32s3-mic-cam/sd/sd.ino b/examples/examples-custom-boards/esp32s3-mic-cam/sd/sd.ino deleted file mode 100644 index d046e50635..0000000000 --- a/examples/examples-custom-boards/esp32s3-mic-cam/sd/sd.ino +++ /dev/null @@ -1,259 +0,0 @@ -/* - * pin 1 - not used | Micro SD card | - * pin 2 - CS (SS) | / - * pin 3 - DI (MOSI) | |__ - * pin 4 - VDD (3.3V) | | - * pin 5 - SCK (SCLK) | 8 7 6 5 4 3 2 1 / - * pin 6 - VSS (GND) | ▄ ▄ ▄ ▄ ▄ ▄ ▄ ▄ / - * pin 7 - DO (MISO) | ▀ ▀ █ ▀ █ ▀ ▀ ▀ | - * pin 8 - not used |_________________| - * ║ ║ ║ ║ ║ ║ ║ ║ - * ╔═══════╝ ║ ║ ║ ║ ║ ║ ╚═════════╗ - * ║ ║ ║ ║ ║ ║ ╚══════╗ ║ - * ║ ╔═════╝ ║ ║ ║ ╚═════╗ ║ ║ - * Connections for ║ ║ ╔═══╩═║═║═══╗ ║ ║ ║ - * full-sized ║ ║ ║ ╔═╝ ║ ║ ║ ║ ║ - * SD card ║ ║ ║ ║ ║ ║ ║ ║ ║ - * Pin name | - DO VSS SCK VDD VSS DI CS - | - * SD pin number | 8 7 6 5 4 3 2 1 9 / - * | █/ - * |__▍___▊___█___█___█___█___█___█___/ - * - * Note: The SPI pins can be manually configured by using `SPI.begin(sck, miso, mosi, cs).` - * Alternatively, you can change the CS pin and use the other default settings by using `SD.begin(cs)`. - * - * +--------------+---------+-------+----------+----------+----------+----------+----------+ - * | SPI Pin Name | ESP8266 | ESP32 | ESP32‑S2 | ESP32‑S3 | ESP32‑C3 | ESP32‑C6 | ESP32‑H2 | - * +==============+=========+=======+==========+==========+==========+==========+==========+ - * | CS (SS) | GPIO15 | GPIO5 | GPIO34 | GPIO10 | GPIO7 | GPIO18 | GPIO0 | - * +--------------+---------+-------+----------+----------+----------+----------+----------+ - * | DI (MOSI) | GPIO13 | GPIO23| GPIO35 | GPIO11 | GPIO6 | GPIO19 | GPIO25 | - * +--------------+---------+-------+----------+----------+----------+----------+----------+ - * | DO (MISO) | GPIO12 | GPIO19| GPIO37 | GPIO13 | GPIO5 | GPIO20 | GPIO11 | - * +--------------+---------+-------+----------+----------+----------+----------+----------+ - * | SCK (SCLK) | GPIO14 | GPIO18| GPIO36 | GPIO12 | GPIO4 | GPIO21 | GPIO10 | - * +--------------+---------+-------+----------+----------+----------+----------+----------+ - * - * For more info see file README.md in this library or on URL: - * https://github.com/espressif/arduino-esp32/tree/master/libraries/SD - */ - -#include "FS.h" -#include "SD.h" -#include "SPI.h" - -#define REASSIGN_PINS -int sck = 42; -int miso = 41; -int mosi = 39; -int cs = 38; - -void listDir(fs::FS &fs, const char *dirname, uint8_t levels) { - Serial.printf("Listing directory: %s\n", dirname); - - File root = fs.open(dirname); - if (!root) { - Serial.println("Failed to open directory"); - return; - } - if (!root.isDirectory()) { - Serial.println("Not a directory"); - return; - } - - File file = root.openNextFile(); - while (file) { - if (file.isDirectory()) { - Serial.print(" DIR : "); - Serial.println(file.name()); - if (levels) { - listDir(fs, file.path(), levels - 1); - } - } else { - Serial.print(" FILE: "); - Serial.print(file.name()); - Serial.print(" SIZE: "); - Serial.println(file.size()); - } - file = root.openNextFile(); - } -} - -void createDir(fs::FS &fs, const char *path) { - Serial.printf("Creating Dir: %s\n", path); - if (fs.mkdir(path)) { - Serial.println("Dir created"); - } else { - Serial.println("mkdir failed"); - } -} - -void removeDir(fs::FS &fs, const char *path) { - Serial.printf("Removing Dir: %s\n", path); - if (fs.rmdir(path)) { - Serial.println("Dir removed"); - } else { - Serial.println("rmdir failed"); - } -} - -void readFile(fs::FS &fs, const char *path) { - Serial.printf("Reading file: %s\n", path); - - File file = fs.open(path); - if (!file) { - Serial.println("Failed to open file for reading"); - return; - } - - Serial.print("Read from file: "); - while (file.available()) { - Serial.write(file.read()); - } - file.close(); -} - -void writeFile(fs::FS &fs, const char *path, const char *message) { - Serial.printf("Writing file: %s\n", path); - - File file = fs.open(path, FILE_WRITE); - if (!file) { - Serial.println("Failed to open file for writing"); - return; - } - if (file.print(message)) { - Serial.println("File written"); - } else { - Serial.println("Write failed"); - } - file.close(); -} - -void appendFile(fs::FS &fs, const char *path, const char *message) { - Serial.printf("Appending to file: %s\n", path); - - File file = fs.open(path, FILE_APPEND); - if (!file) { - Serial.println("Failed to open file for appending"); - return; - } - if (file.print(message)) { - Serial.println("Message appended"); - } else { - Serial.println("Append failed"); - } - file.close(); -} - -void renameFile(fs::FS &fs, const char *path1, const char *path2) { - Serial.printf("Renaming file %s to %s\n", path1, path2); - if (fs.rename(path1, path2)) { - Serial.println("File renamed"); - } else { - Serial.println("Rename failed"); - } -} - -void deleteFile(fs::FS &fs, const char *path) { - Serial.printf("Deleting file: %s\n", path); - if (fs.remove(path)) { - Serial.println("File deleted"); - } else { - Serial.println("Delete failed"); - } -} - -void testFileIO(fs::FS &fs, const char *path) { - File file = fs.open(path); - static uint8_t buf[512]; - size_t len = 0; - uint32_t start = millis(); - uint32_t end = start; - if (file) { - len = file.size(); - size_t flen = len; - start = millis(); - while (len) { - size_t toRead = len; - if (toRead > 512) { - toRead = 512; - } - file.read(buf, toRead); - len -= toRead; - } - end = millis() - start; - Serial.printf("%u bytes read for %lu ms\n", flen, end); - file.close(); - } else { - Serial.println("Failed to open file for reading"); - } - - file = fs.open(path, FILE_WRITE); - if (!file) { - Serial.println("Failed to open file for writing"); - return; - } - - size_t i; - start = millis(); - for (i = 0; i < 2048; i++) { - file.write(buf, 512); - } - end = millis() - start; - Serial.printf("%u bytes written for %lu ms\n", 2048 * 512, end); - file.close(); -} - -void setup() { - Serial.begin(115200); - while (!Serial) { - delay(10); - } - -#ifdef REASSIGN_PINS - SPI.begin(sck, miso, mosi, cs); - if (!SD.begin(cs)) { -#else - if (!SD.begin()) { -#endif - Serial.println("Card Mount Failed"); - return; - } - uint8_t cardType = SD.cardType(); - - if (cardType == CARD_NONE) { - Serial.println("No SD card attached"); - return; - } - - Serial.print("SD Card Type: "); - if (cardType == CARD_MMC) { - Serial.println("MMC"); - } else if (cardType == CARD_SD) { - Serial.println("SDSC"); - } else if (cardType == CARD_SDHC) { - Serial.println("SDHC"); - } else { - Serial.println("UNKNOWN"); - } - - uint64_t cardSize = SD.cardSize() / (1024 * 1024); - Serial.printf("SD Card Size: %lluMB\n", cardSize); - - listDir(SD, "/", 0); - createDir(SD, "/mydir"); - listDir(SD, "/", 0); - removeDir(SD, "/mydir"); - listDir(SD, "/", 2); - writeFile(SD, "/hello.txt", "Hello "); - appendFile(SD, "/hello.txt", "World!\n"); - readFile(SD, "/hello.txt"); - deleteFile(SD, "/foo.txt"); - renameFile(SD, "/hello.txt", "/foo.txt"); - readFile(SD, "/foo.txt"); - testFileIO(SD, "/test.txt"); - Serial.printf("Total space: %lluMB\n", SD.totalBytes() / (1024 * 1024)); - Serial.printf("Used space: %lluMB\n", SD.usedBytes() / (1024 * 1024)); -} - -void loop() {} \ No newline at end of file diff --git a/examples/examples-custom-boards/esp32s3-mic-cam/sdmmc/sdmmc.ino b/examples/examples-custom-boards/esp32s3-mic-cam/sdmmc/sdmmc.ino deleted file mode 100644 index 909c951ceb..0000000000 --- a/examples/examples-custom-boards/esp32s3-mic-cam/sdmmc/sdmmc.ino +++ /dev/null @@ -1,262 +0,0 @@ -/* - * pin 1 - D2 | Micro SD card | - * pin 2 - D3 | / - * pin 3 - CMD | |__ - * pin 4 - VDD (3.3V) | | - * pin 5 - CLK | 8 7 6 5 4 3 2 1 / - * pin 6 - VSS (GND) | ▄ ▄ ▄ ▄ ▄ ▄ ▄ ▄ / - * pin 7 - D0 | ▀ ▀ █ ▀ █ ▀ ▀ ▀ | - * pin 8 - D1 |_________________| - * ║ ║ ║ ║ ║ ║ ║ ║ - * ╔═══════╝ ║ ║ ║ ║ ║ ║ ╚═════════╗ - * ║ ║ ║ ║ ║ ║ ╚══════╗ ║ - * ║ ╔═════╝ ║ ║ ║ ╚═════╗ ║ ║ - * Connections for ║ ║ ╔═══╩═║═║═══╗ ║ ║ ║ - * full-sized ║ ║ ║ ╔═╝ ║ ║ ║ ║ ║ - * SD card ║ ║ ║ ║ ║ ║ ║ ║ ║ - * ESP32-S3 DevKit | 21 47 GND 39 3V3 GND 40 41 42 | - * ESP32-S3-USB-OTG | 38 37 GND 36 3V3 GND 35 34 33 | - * ESP32 | 4 2 GND 14 3V3 GND 15 13 12 | - * ESP32-S3-Camera 40 41 42 39 38 37 - * Pin name | D1 D0 VSS CLK VDD VSS CMD D3 D2 | - * SD pin number | 8 7 6 5 4 3 2 1 9 / - * | █/ - * |__▍___▊___█___█___█___█___█___█___/ - * WARNING: ALL data pins must be pulled up to 3.3V with an external 10k Ohm resistor! - * Note to ESP32 pin 2 (D0): Add a 1K Ohm pull-up resistor to 3.3V after flashing - * - * - * For more info see file README.md in this library or on URL: - * https://github.com/espressif/arduino-esp32/tree/master/libraries/SD_MMC - */ - -#include "FS.h" -#include "SD_MMC.h" - -// Default pins for ESP-S3 -// Warning: ESP32-S3-WROOM-2 is using most of the default GPIOs (33-37) to interface with on-board OPI flash. -// If the SD_MMC is initialized with default pins it will result in rebooting loop - please -// reassign the pins elsewhere using the mentioned command `setPins`. -// Note: ESP32-S3-WROOM-1 does not have GPIO 33 and 34 broken out. -// Note: if it's ok to use default pins, you do not need to call the setPins -int clk = 42; -int cmd = 39; -int d0 = 41; -int d1 = 40; -int d2 = 37; -int d3 = 38; - -void listDir(fs::FS &fs, const char *dirname, uint8_t levels) { - Serial.printf("Listing directory: %s\n", dirname); - - File root = fs.open(dirname); - if (!root) { - Serial.println("Failed to open directory"); - return; - } - if (!root.isDirectory()) { - Serial.println("Not a directory"); - return; - } - - File file = root.openNextFile(); - while (file) { - if (file.isDirectory()) { - Serial.print(" DIR : "); - Serial.println(file.name()); - if (levels) { - listDir(fs, file.path(), levels - 1); - } - } else { - Serial.print(" FILE: "); - Serial.print(file.name()); - Serial.print(" SIZE: "); - Serial.println(file.size()); - } - file = root.openNextFile(); - } -} - -void createDir(fs::FS &fs, const char *path) { - Serial.printf("Creating Dir: %s\n", path); - if (fs.mkdir(path)) { - Serial.println("Dir created"); - } else { - Serial.println("mkdir failed"); - } -} - -void removeDir(fs::FS &fs, const char *path) { - Serial.printf("Removing Dir: %s\n", path); - if (fs.rmdir(path)) { - Serial.println("Dir removed"); - } else { - Serial.println("rmdir failed"); - } -} - -void readFile(fs::FS &fs, const char *path) { - Serial.printf("Reading file: %s\n", path); - - File file = fs.open(path); - if (!file) { - Serial.println("Failed to open file for reading"); - return; - } - - Serial.print("Read from file: "); - while (file.available()) { - Serial.write(file.read()); - } -} - -void writeFile(fs::FS &fs, const char *path, const char *message) { - Serial.printf("Writing file: %s\n", path); - - File file = fs.open(path, FILE_WRITE); - if (!file) { - Serial.println("Failed to open file for writing"); - return; - } - if (file.print(message)) { - Serial.println("File written"); - } else { - Serial.println("Write failed"); - } -} - -void appendFile(fs::FS &fs, const char *path, const char *message) { - Serial.printf("Appending to file: %s\n", path); - - File file = fs.open(path, FILE_APPEND); - if (!file) { - Serial.println("Failed to open file for appending"); - return; - } - if (file.print(message)) { - Serial.println("Message appended"); - } else { - Serial.println("Append failed"); - } -} - -void renameFile(fs::FS &fs, const char *path1, const char *path2) { - Serial.printf("Renaming file %s to %s\n", path1, path2); - if (fs.rename(path1, path2)) { - Serial.println("File renamed"); - } else { - Serial.println("Rename failed"); - } -} - -void deleteFile(fs::FS &fs, const char *path) { - Serial.printf("Deleting file: %s\n", path); - if (fs.remove(path)) { - Serial.println("File deleted"); - } else { - Serial.println("Delete failed"); - } -} - -void testFileIO(fs::FS &fs, const char *path) { - File file = fs.open(path); - static uint8_t buf[512]; - size_t len = 0; - uint32_t start = millis(); - uint32_t end = start; - if (file) { - len = file.size(); - size_t flen = len; - start = millis(); - while (len) { - size_t toRead = len; - if (toRead > 512) { - toRead = 512; - } - file.read(buf, toRead); - len -= toRead; - } - end = millis() - start; - Serial.printf("%u bytes read for %lu ms\n", flen, end); - file.close(); - } else { - Serial.println("Failed to open file for reading"); - } - - file = fs.open(path, FILE_WRITE); - if (!file) { - Serial.println("Failed to open file for writing"); - return; - } - - size_t i; - start = millis(); - for (i = 0; i < 2048; i++) { - file.write(buf, 512); - } - end = millis() - start; - Serial.printf("%u bytes written for %lu ms\n", 2048 * 512, end); - file.close(); -} - -void setup() { - Serial.begin(115200); - while (!Serial) - ; - Serial.println("starting..."); - /* - // If you want to change the pin assignment on ESP32-S3 uncomment this block and the appropriate - // line depending if you want to use 1-bit or 4-bit line. - // Please note that ESP32 does not allow pin change and will always fail. - //if(! SD_MMC.setPins(clk, cmd, d0)){ - if(! SD_MMC.setPins(clk, cmd, d0, d1, d2, d3)){ - Serial.println("Pin change failed!"); - return; - } - */ - if (!SD_MMC.setPins(clk, cmd, d0)) { - Serial.println("Pin change failed!"); - return; - } - if (!SD_MMC.begin("/sdcard", true)) { - Serial.println("Card Mount Failed"); - return; - } - uint8_t cardType = SD_MMC.cardType(); - - if (cardType == CARD_NONE) { - Serial.println("No SD_MMC card attached"); - return; - } - - Serial.print("SD_MMC Card Type: "); - if (cardType == CARD_MMC) { - Serial.println("MMC"); - } else if (cardType == CARD_SD) { - Serial.println("SDSC"); - } else if (cardType == CARD_SDHC) { - Serial.println("SDHC"); - } else { - Serial.println("UNKNOWN"); - } - - uint64_t cardSize = SD_MMC.cardSize() / (1024 * 1024); - Serial.printf("SD_MMC Card Size: %lluMB\n", cardSize); - - listDir(SD_MMC, "/", 0); - createDir(SD_MMC, "/mydir"); - listDir(SD_MMC, "/", 0); - removeDir(SD_MMC, "/mydir"); - listDir(SD_MMC, "/", 2); - writeFile(SD_MMC, "/hello.txt", "Hello "); - appendFile(SD_MMC, "/hello.txt", "World!\n"); - readFile(SD_MMC, "/hello.txt"); - deleteFile(SD_MMC, "/foo.txt"); - renameFile(SD_MMC, "/hello.txt", "/foo.txt"); - readFile(SD_MMC, "/foo.txt"); - testFileIO(SD_MMC, "/test.txt"); - Serial.printf("Total space: %lluMB\n", SD_MMC.totalBytes() / (1024 * 1024)); - Serial.printf("Used space: %lluMB\n", SD_MMC.usedBytes() / (1024 * 1024)); -} - -void loop() {} \ No newline at end of file diff --git a/examples/examples-custom-boards/lyrat-mini/README.md b/examples/examples-custom-boards/lyrat-mini/README.md deleted file mode 100644 index 2df1341074..0000000000 --- a/examples/examples-custom-boards/lyrat-mini/README.md +++ /dev/null @@ -1,10 +0,0 @@ -The LyraT Mini is a bit tricky to use because it has a ES8311 which is handling the output and a -ES7243 which is handling the microphone input: both need to be on separate I2S ports. - -You should be able to use the examples that can be found in the [audiokit directory](https://github.com/pschatzmann/arduino-audio-tools/tree/main/examples/examples-audiokit): just replace the -driver with LyratMini! - -For the examples install: - -- [Arduino AudioTools](https://github.com/pschatzmann/arduino-audio-tools) -- [Arduino Audio Driver](https://github.com/pschatzmann/arduino-audio-driver) \ No newline at end of file diff --git a/examples/examples-custom-boards/lyrat-mini/buttons/buttons.ino b/examples/examples-custom-boards/lyrat-mini/buttons/buttons.ino deleted file mode 100644 index 51962e008c..0000000000 --- a/examples/examples-custom-boards/lyrat-mini/buttons/buttons.ino +++ /dev/null @@ -1,64 +0,0 @@ -/** - * @file buttons.ino - * @author Phil Schatzmann - * @brief Demo how to use the buttons. - * @version 0.1 - * @date 2024-11-03 - * - * The button values are determined with an analogRead(39) via the driver library. - * This demo shows how to use the integrated AudioActions class via the AudioBoardStream - * - * @copyright Copyright (c) 2022 - */ - - -#include "AudioTools.h" -#include "AudioTools/AudioLibs/AudioBoardStream.h" - -AudioBoardStream lyrat(LyratMini); - -void rec(bool, int, void*) { - Serial.println("rec"); -} - -void mode(bool, int, void*) { - Serial.println("mode"); -} - -void play(bool, int, void*) { - Serial.println("play"); -} - -void set(bool, int, void*) { - Serial.println("set"); -} - -void volUp(bool, int, void*) { - Serial.println("vol+"); -} - -void volDown(bool, int, void*) { - Serial.println("vol-"); -} - - -// Arduino Setup -void setup(void) { - Serial.begin(115200); - AudioToolsLogger.begin(Serial, AudioToolsLogLevel::Warning); - - // start board - lyrat.begin(lyrat.defaultConfig(TX_MODE)); - - lyrat.addAction(KEY_REC, rec); - lyrat.addAction(KEY_MODE, mode); - lyrat.addAction(KEY_PLAY, play); - lyrat.addAction(KEY_SET, set); - lyrat.addAction(KEY_VOLUME_UP, volUp); - lyrat.addAction(KEY_VOLUME_DOWN, volDown); - -} - -void loop() { - lyrat.processActions(); -} diff --git a/examples/examples-custom-boards/lyrat-mini/mic/mic.ino b/examples/examples-custom-boards/lyrat-mini/mic/mic.ino deleted file mode 100644 index 317fc756fd..0000000000 --- a/examples/examples-custom-boards/lyrat-mini/mic/mic.ino +++ /dev/null @@ -1,49 +0,0 @@ - -/** - * @file output.ino - * @author Phil Schatzmann - * @brief Demo how to use the microphone. - * @version 0.1 - * @date 2024-11-03 - * - * The microphone seems to be attached to 2 different i2s ports. In addition to the - * ES8311, the ES7243 is also started. - * The I2S pins can be selected via cfg.i2s_function: CODEC uses the ES8311 I2S pins - * and CODEC_ADC uses the ES7243 I2S pins; By default the CODEC value is used. - * - * Only CODEC_ADC will give a proper microphone input! - * - * @copyright Copyright (c) 2022 - */ - - -#include "AudioTools.h" -#include "AudioTools/AudioLibs/AudioBoardStream.h" - -AudioInfo info(44100, 2, 16); -AudioBoardStream i2s(LyratMini); // Access I2S as stream -CsvOutput csvOutput(Serial); -StreamCopy copier(csvOutput, i2s); // copy i2s to csvOutput - -// Arduino Setup -void setup(void) { - Serial.begin(115200); - // Display details what is going on - AudioToolsLogger.begin(Serial, AudioToolsLogLevel::Info); - AudioDriverLogger.begin(Serial, AudioDriverLogLevel::Info); - - auto cfg = i2s.defaultConfig(RX_MODE); - cfg.copyFrom(info); - // cfg.i2s_function = PinFunction::CODEC_ADC; // determined automatically - // cfg.i2s_port_no = 0; // or 1 if 0 is already in use - i2s.begin(cfg); - - // make sure that we have the correct number of channels set up - csvOutput.begin(info); - -} - -// Arduino loop - copy data -void loop() { - copier.copy(); -} \ No newline at end of file diff --git a/examples/examples-custom-boards/lyrat-mini/output/output.ino b/examples/examples-custom-boards/lyrat-mini/output/output.ino deleted file mode 100644 index c9707ba6e6..0000000000 --- a/examples/examples-custom-boards/lyrat-mini/output/output.ino +++ /dev/null @@ -1,48 +0,0 @@ -/** - * @file output.ino - * @author Phil Schatzmann - * @brief Demo to output audio on a lyrat-mini board: with headphone detection - * active to switch the power amplifier on and off. - * I2S is used and the ES8311 is initialized via the driver library. - * @version 0.1 - * @date 2024-11-03 - * - * @copyright Copyright (c) 2022 - */ - -#include "AudioTools.h" -#include "AudioTools/AudioLibs/AudioBoardStream.h" - -AudioInfo info(44100, 2, 16); -SineWaveGenerator sineWave(32000); // subclass of SoundGenerator with max amplitude of 32000 -GeneratedSoundStream sound(sineWave); // Stream generated from sine wave -AudioBoardStream out(LyratMini); -StreamCopy copier(out, sound); // copies sound into i2s - -// Arduino Setup -void setup(void) { - // Open Serial - Serial.begin(115200); - AudioToolsLogger.begin(Serial, AudioToolsLogLevel::Warning); - - // start I2S - Serial.println("starting I2S..."); - auto config = out.defaultConfig(TX_MODE); - config.copyFrom(info); - // cfg.i2s_function = PinFunction::CODEC; // determined automatically - // cfg.i2s_port_no = 0; // or 1 if 0 is already in use - out.begin(config); - - // additinal settings - out.setVolume(0.5); - out.addHeadphoneDetectionAction(); - - // start sine wave - sineWave.begin(info, N_B4); - Serial.println("started..."); -} - -void loop() { - copier.copy(); - out.processActions(); -} \ No newline at end of file diff --git a/examples/examples-custom-boards/lyrat-mini/sd/sd.ino b/examples/examples-custom-boards/lyrat-mini/sd/sd.ino deleted file mode 100644 index c847c29b09..0000000000 --- a/examples/examples-custom-boards/lyrat-mini/sd/sd.ino +++ /dev/null @@ -1,55 +0,0 @@ - -/** - * @file sd.ino - * @author Phil Schatzmann - * @brief Test/Demo that SD is not working! - * @version 0.1 - * @date 2024-11-03 - * - * @copyright Copyright (c) 2022 - */ - -#include -#include - -// These pins are defined in the HAL -#define PIN_SD_CARD_CS 13 -#define PIN_SD_CARD_MISO 2 -#define PIN_SD_CARD_MOSI 15 -#define PIN_SD_CARD_CLK 14 -#define PIN_SD_CARD_DET 34 - - -// Arduino Setup -void setup(void) { - Serial.begin(115200); - - // setup SPI - SPI.begin(PIN_SD_CARD_CLK, PIN_SD_CARD_MISO, PIN_SD_CARD_MOSI, PIN_SD_CARD_CS); - - // Optionally determine if there is an SD card - pinMode(PIN_SD_CARD_DET, INPUT); - if (digitalRead(PIN_SD_CARD_DET)!=0){ - Serial.println("No SD Card detected"); - } - - // Open SD library - if (!SD.begin(PIN_SD_CARD_CS)){ - Serial.println("SD.begin failed"); - while(true); - } - - // Open an existing file - auto file = SD.open("/audio8000.raw", FILE_READ); - if (!file){ - Serial.println("file open failed"); - while(true); - } - - file.close(); - - Serial.println("Success"); -} - -// Arduino loop - repeated processing -void loop() {} \ No newline at end of file diff --git a/examples/examples-custom-boards/lyrat-mini/sdmmc/sdmmc.ino b/examples/examples-custom-boards/lyrat-mini/sdmmc/sdmmc.ino deleted file mode 100644 index 4b19010191..0000000000 --- a/examples/examples-custom-boards/lyrat-mini/sdmmc/sdmmc.ino +++ /dev/null @@ -1,52 +0,0 @@ -/** - * @file sdmmc.ino - * @author Phil Schatzmann - * @brief Test/Demo how to use the SD_MMC API in Arduino with the LyraT Mini - * @version 0.1 - * @date 2024-11-03 - * - * @copyright Copyright (c) 2022 - */ - -#include "FS.h" -#include "SD_MMC.h" - -// These pins are defined in the HAL -const int PIN_SD_CARD_POWER = 13; -const int PIN_SD_CARD_DET = 34; - - -// Arduino Setup -void setup(void) { - Serial.begin(115200); - - // Mandatory: set power pin to low - pinMode(PIN_SD_CARD_POWER, OUTPUT); - digitalWrite(PIN_SD_CARD_POWER, LOW); - - // Optionally: Determine if there is an SD card - pinMode(PIN_SD_CARD_DET, INPUT); - if (digitalRead(PIN_SD_CARD_DET)!=0){ - Serial.println("No SD Card detected"); - } - - // open SDMMC in 1 bit mode - if (!SD_MMC.begin("/sdcard", true)) { - Serial.println("Card Mount Failed"); - while(true); - } - - // open an existing file - auto file = SD_MMC.open("/test.mp3", FILE_READ); - if (!file){ - Serial.println("file open failed"); - while(true); - } - - file.close(); - - Serial.println("Success"); -} - -// Arduino loop - repeated processing -void loop() {} \ No newline at end of file diff --git a/examples/examples-custom/README.md b/examples/examples-custom/README.md new file mode 100644 index 0000000000..2352fb9e5f --- /dev/null +++ b/examples/examples-custom/README.md @@ -0,0 +1,2 @@ + +Some Examples that show how to use some custom boards with their corresponding I2S pins \ No newline at end of file diff --git a/examples/examples-custom/cross_band_handy_walkie_talkie/cross_band_handy_walkie_talkie.ino b/examples/examples-custom/cross_band_handy_walkie_talkie/cross_band_handy_walkie_talkie.ino new file mode 100644 index 0000000000..577d824927 --- /dev/null +++ b/examples/examples-custom/cross_band_handy_walkie_talkie/cross_band_handy_walkie_talkie.ino @@ -0,0 +1,65 @@ +/** + @file streams-generator-audiokit.ino + @brief Tesing I2S output on the cross band handy walkie talkie + https://github.com/immortal-sniper1/cross_band_handy_walkie_talkie + + @author Phil Schatzmann + @copyright GPLv3 +*/ + +#include "AudioTools.h" +#include "AudioLibs/AudioKit.h" +#include "SD.h" + +AudioInfo info(32000, 2, 16); +SineWaveGenerator sineWave(32000); // subclass of SoundGenerator with max amplitude of 32000 +GeneratedSoundStream sound(sineWave); // Stream generated from sine wave +AudioKitStream out; +StreamCopy copier(out, sound); // copies sound into i2s + +// Arduino Setup +void setup(void) { + // Open Serial + Serial.begin(115200); + while(!Serial); + AudioLogger::instance().begin(Serial, AudioLogger::Info); + //LOGLEVEL_AUDIOKIT = AudioKitDebug; + + // start I2S + Serial.println("starting I2S..."); + auto config = out.defaultConfig(TX_MODE); + config.copyFrom(info); + config.sd_active = false; + config.default_actions_active = flase; + // i2c + config.pins.i2c_sda = 36; + config.pins.i2c_scl = 35; + // i2s + config.pin_mclk = 47 + config.pin_bck = 21; + config.pin_ws = 12; + config.pin_data = 11; + config.pin_data_in = 14; + + //config.sd_active = false; + config.pins.sd_cs = 2; + config.pins.sd_miso = 42; + config.pins.sd_mosi = 43; + config.pins.sd_clk = 44; + out.begin(config); + + // check SD drive + if(!SD.begin(config.pins.sd_cs)){ + Serial.println("Card Mount Failed"); + stop(); + } + + // Setup sine wave + sineWave.begin(info, N_B4); + Serial.println("started..."); +} + +// Arduino loop - copy sound to out +void loop() { + copier.copy(); +} \ No newline at end of file diff --git a/examples/tests/conversion/numberformat-converter/numberformat-converter.ino b/examples/examples-custom/esp32-audio-dev-mini/esp32-audio-dev-mini.ino similarity index 57% rename from examples/tests/conversion/numberformat-converter/numberformat-converter.ino rename to examples/examples-custom/esp32-audio-dev-mini/esp32-audio-dev-mini.ino index 8361180a4d..7bbde9a3f2 100644 --- a/examples/tests/conversion/numberformat-converter/numberformat-converter.ino +++ b/examples/examples-custom/esp32-audio-dev-mini/esp32-audio-dev-mini.ino @@ -1,26 +1,33 @@ +/** + * @file AudioMini.ino + * See https://github.com/sonocotta/esp32-audio-development-kit + * @author Phil Schatzmann + * @copyright GPLv3 + */ + #include "AudioTools.h" -#include "AudioTools/AudioLibs/AudioBoardStream.h" -AudioInfo info(48000, 2, 16); -AudioInfo info_to(48000, 2, 32); +AudioInfo info(44100, 2, 16); SineWaveGenerator sineWave(32000); // subclass of SoundGenerator with max amplitude of 32000 GeneratedSoundStream sound(sineWave); // Stream generated from sine wave -AudioBoardStream out(AudioKitEs8388V1); -NumberFormatConverterStream nfc(out); -StreamCopy copier(nfc, sound); // copies sound into i2s +I2SStream out; +StreamCopy copier(out, sound); // copies sound into i2s // Arduino Setup void setup(void) { // Open Serial Serial.begin(115200); - AudioToolsLogger.begin(Serial, AudioToolsLogLevel::Info); - - nfc.begin(info.bits_per_sample, info_to.bits_per_sample); + while(!Serial); + AudioLogger::instance().begin(Serial, AudioLogger::Info); // start I2S Serial.println("starting I2S..."); auto config = out.defaultConfig(TX_MODE); - config.copyFrom(info_to); + config.copyFrom(info); + // Custom I2S output pins + config.pin_bck = 26; + config.pin_ws = 25; + config.pin_data = 22; out.begin(config); // Setup sine wave @@ -31,4 +38,4 @@ void setup(void) { // Arduino loop - copy sound to out void loop() { copier.copy(); -} +} \ No newline at end of file diff --git a/examples/examples-desktop/generator/CMakeLists.txt b/examples/examples-desktop/generator/CMakeLists.txt index 8d299050de..ed596bbb21 100644 --- a/examples/examples-desktop/generator/CMakeLists.txt +++ b/examples/examples-desktop/generator/CMakeLists.txt @@ -12,23 +12,13 @@ if(CMAKE_SOURCE_DIR STREQUAL CMAKE_CURRENT_SOURCE_DIR) add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/../../.. ${CMAKE_CURRENT_BINARY_DIR}/arduino-audio-tools ) endif() -# Add Portaduio for desktop build -add_compile_options(-DIS_DESKTOP) -FetchContent_Declare(portaudio GIT_REPOSITORY "https://github.com/PortAudio/portaudio.git" GIT_TAG v19.7.0 ) -FetchContent_GetProperties(portaudio) -if(NOT portaudio_POPULATED) - FetchContent_Populate(portaudio) - add_subdirectory(${portaudio_SOURCE_DIR} ${CMAKE_CURRENT_BINARY_DIR}/portaudio) -endif() - - # build sketch as executable set_source_files_properties(generator.ino PROPERTIES LANGUAGE CXX) add_executable (generator generator.ino) # set preprocessor defines target_compile_definitions(arduino_emulator PUBLIC -DDEFINE_MAIN) -target_compile_definitions(generator PUBLIC -DARDUINO -DIS_DESKTOP) +target_compile_definitions(generator PUBLIC -DARDUINO -DIS_DESKTOP -DEXIT_ON_STOP) # specify libraries target_link_libraries(generator portaudio arduino_emulator arduino-audio-tools) diff --git a/examples/examples-desktop/generator/generator.ino b/examples/examples-desktop/generator/generator.ino index eb9cb40148..8adc17bdc5 100644 --- a/examples/examples-desktop/generator/generator.ino +++ b/examples/examples-desktop/generator/generator.ino @@ -1,5 +1,5 @@ #include "AudioTools.h" -#include "AudioTools/AudioLibs/PortAudioStream.h" +#include "AudioLibs/PortAudioStream.h" AudioInfo info(44100, 1, 16); SineWaveGenerator sineWave(32000); // subclass of SoundGenerator with max amplitude of 32000 @@ -10,7 +10,7 @@ StreamCopy copier(out, in); // copy in to out // Arduino Setup void setup(void) { Serial.begin(115200); - AudioToolsLogger.begin(Serial, AudioToolsLogLevel::Info); + AudioLogger::instance().begin(Serial, AudioLogger::Info); // open output auto config = out.defaultConfig(); diff --git a/examples/examples-desktop/min-generator/CMakeLists.txt b/examples/examples-desktop/min-generator/CMakeLists.txt index 14cee42ead..0c171c2fa6 100644 --- a/examples/examples-desktop/min-generator/CMakeLists.txt +++ b/examples/examples-desktop/min-generator/CMakeLists.txt @@ -21,7 +21,7 @@ set_source_files_properties(min-generator.ino PROPERTIES LANGUAGE CXX) add_executable (min-generator min-generator.ino) # set preprocessor defines -target_compile_definitions(min-generator PUBLIC -DIS_MIN_DESKTOP) +target_compile_definitions(min-generator PUBLIC -DIS_MIN_DESKTOP -DEXIT_ON_STOP) # specify libraries target_link_libraries(min-generator arduino-audio-tools) diff --git a/examples/examples-desktop/min-generator/min-generator.ino b/examples/examples-desktop/min-generator/min-generator.ino index 89ef874a3f..74eb007b41 100644 --- a/examples/examples-desktop/min-generator/min-generator.ino +++ b/examples/examples-desktop/min-generator/min-generator.ino @@ -8,7 +8,7 @@ StreamCopy copier(out, in); // copy in to out // Arduino Setup void setup(void) { - AudioToolsLogger.begin(Serial, AudioToolsLogLevel::Info); + AudioLogger::instance().begin(Serial, AudioLogger::Info); // open output auto config = out.defaultConfig(); diff --git a/examples/examples-desktop/mp3/CMakeLists.txt b/examples/examples-desktop/mp3/CMakeLists.txt index 461f47f231..12f602a479 100644 --- a/examples/examples-desktop/mp3/CMakeLists.txt +++ b/examples/examples-desktop/mp3/CMakeLists.txt @@ -7,7 +7,6 @@ set (DCMAKE_CXX_FLAGS "-Werror") include(FetchContent) option(BUILD_SHARED_LIBS "Build using shared libraries" OFF) -option(ADD_PORTAUDIO "Add Portaudio Library" ON) if(CMAKE_SOURCE_DIR STREQUAL CMAKE_CURRENT_SOURCE_DIR) add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/../../.. ${CMAKE_CURRENT_BINARY_DIR}/arduino-audio-tools ) @@ -27,8 +26,8 @@ add_executable (mp3_dt mp3.ino) # set preprocessor defines target_compile_definitions(arduino_emulator PUBLIC -DDEFINE_MAIN) -target_compile_definitions(arduino_helix PUBLIC -DARDUINO -DHELIX_LOGGING_ACTIVE=0) -target_compile_definitions(mp3_dt PUBLIC -DIS_DESKTOP) +target_compile_definitions(arduino_helix PUBLIC -DARDUINO) +target_compile_definitions(mp3_dt PUBLIC -DARDUINO -DIS_DESKTOP -DEXIT_ON_STOP) # OS/X might need this setting for core audio target_compile_options(portaudio PRIVATE -Wno-deprecated) diff --git a/examples/examples-desktop/mp3/mp3.ino b/examples/examples-desktop/mp3/mp3.ino index 61b9f393ee..815667e743 100644 --- a/examples/examples-desktop/mp3/mp3.ino +++ b/examples/examples-desktop/mp3/mp3.ino @@ -2,8 +2,8 @@ #include "Arduino.h" #include "AudioTools.h" #include "BabyElephantWalk60_mp3.h" -#include "AudioTools/AudioLibs/PortAudioStream.h" -#include "AudioTools/AudioCodecs/CodecMP3Helix.h" +#include "AudioLibs/PortAudioStream.h" +#include "AudioCodecs/CodecMP3Helix.h" MemoryStream mp3(BabyElephantWalk60_mp3, BabyElephantWalk60_mp3_len); @@ -13,7 +13,7 @@ StreamCopy copier(dec, mp3); // copy in to out void setup(){ Serial.begin(115200); - AudioToolsLogger.begin(Serial, AudioToolsLogLevel::Info); + AudioLogger::instance().begin(Serial, AudioLogger::Info); out.begin(); mp3.begin(); diff --git a/examples/examples-dsp/examples-faust/streams-i2s-faust_copy-i2s/Copy.dsp b/examples/examples-dsp/examples-faust/streams-i2s-faust_copy-i2s/Copy.dsp deleted file mode 100644 index 0d393a2447..0000000000 --- a/examples/examples-dsp/examples-faust/streams-i2s-faust_copy-i2s/Copy.dsp +++ /dev/null @@ -1,3 +0,0 @@ -declare filename "copy.dsp"; -declare name "copy"; -process = _,_; diff --git a/examples/examples-dsp/examples-faust/streams-i2s-faust_copy-i2s/Copy.h b/examples/examples-dsp/examples-faust/streams-i2s-faust_copy-i2s/Copy.h deleted file mode 100644 index bc14205f09..0000000000 --- a/examples/examples-dsp/examples-faust/streams-i2s-faust_copy-i2s/Copy.h +++ /dev/null @@ -1,106 +0,0 @@ -/* ------------------------------------------------------------ -name: "Copy" -Code generated with Faust 2.68.1 (https://faust.grame.fr) -Compilation options: -lang cpp -ct 1 -es 1 -mcd 16 -single -ftz 0 ------------------------------------------------------------- */ - -#ifndef __mydsp_H__ -#define __mydsp_H__ - -#ifndef FAUSTFLOAT -#define FAUSTFLOAT float -#endif - -#include -#include -#include - -#ifndef FAUSTCLASS -#define FAUSTCLASS mydsp -#endif - -#ifdef __APPLE__ -#define exp10f __exp10f -#define exp10 __exp10 -#endif - -#if defined(_WIN32) -#define RESTRICT __restrict -#else -#define RESTRICT __restrict__ -#endif - - -class mydsp : public dsp { - - private: - - int fSampleRate; - - public: - mydsp() {} - - void metadata(Meta* m) { - m->declare("compile_options", "-lang cpp -ct 1 -es 1 -mcd 16 -single -ftz 0"); - m->declare("filename", "Copy.dsp"); - m->declare("name", "Copy"); - } - - virtual int getNumInputs() { - return 2; - } - virtual int getNumOutputs() { - return 2; - } - - static void classInit(int sample_rate) { - } - - virtual void instanceConstants(int sample_rate) { - fSampleRate = sample_rate; - } - - virtual void instanceResetUserInterface() { - } - - virtual void instanceClear() { - } - - virtual void init(int sample_rate) { - classInit(sample_rate); - instanceInit(sample_rate); - } - - virtual void instanceInit(int sample_rate) { - instanceConstants(sample_rate); - instanceResetUserInterface(); - instanceClear(); - } - - virtual mydsp* clone() { - return new mydsp(); - } - - virtual int getSampleRate() { - return fSampleRate; - } - - virtual void buildUserInterface(UI* ui_interface) { - ui_interface->openVerticalBox("Copy"); - ui_interface->closeBox(); - } - - virtual void compute(int count, FAUSTFLOAT** RESTRICT inputs, FAUSTFLOAT** RESTRICT outputs) { - FAUSTFLOAT* input0 = inputs[0]; - FAUSTFLOAT* input1 = inputs[1]; - FAUSTFLOAT* output0 = outputs[0]; - FAUSTFLOAT* output1 = outputs[1]; - for (int i0 = 0; i0 < count; i0 = i0 + 1) { - output0[i0] = FAUSTFLOAT(float(input0[i0])); - output1[i0] = FAUSTFLOAT(float(input1[i0])); - } - } - -}; - -#endif diff --git a/examples/examples-dsp/examples-faust/streams-i2s-faust_copy-i2s/streams-i2s-faust_copy-i2s.ino b/examples/examples-dsp/examples-faust/streams-i2s-faust_copy-i2s/streams-i2s-faust_copy-i2s.ino deleted file mode 100644 index e4e6ce8c72..0000000000 --- a/examples/examples-dsp/examples-faust/streams-i2s-faust_copy-i2s/streams-i2s-faust_copy-i2s.ino +++ /dev/null @@ -1,43 +0,0 @@ -/** - * @file streams-i2s-faust_copy-i2s.ino - * @author Phil Schatzmann - * @brief Example how to use Faust to write and process a stereo signal. The data - * is just copied, keeping the left and right signal separate. - * @version 0.1 - * @date 2022-04-22 - * - * @copyright Copyright (c) 2022 - * - */ - -#include "AudioTools.h" -#include "AudioTools/AudioLibs/AudioBoardStream.h" -#include "AudioTools/AudioLibs/AudioFaust.h" -#include "Copy.h" - -AudioBoardStream io(AudioKitEs8388V1); -FaustStream faust(io); // final output to io -StreamCopy copier(faust, io); // copy mic to faust - -// Arduino Setup -void setup(void) { - // Open Serial - Serial.begin(115200); - AudioToolsLogger.begin(Serial, AudioToolsLogLevel::Warning); - - // Setup Faust - auto cfg = faust.defaultConfig(); - faust.begin(cfg); - - - // start I2S - auto cfg_i2s = io.defaultConfig(RXTX_MODE); - cfg_i2s.copyFrom(cfg); - io.begin(cfg_i2s); - -} - -// Arduino loop - copy sound to out -void loop() { - copier.copy(); -} \ No newline at end of file diff --git a/examples/examples-dsp/examples-faust/streams-i2s-faust_pitchshift-i2s/pitchShifter.dsp b/examples/examples-dsp/examples-faust/streams-i2s-faust_pitchshift-i2s/pitchShifter.dsp deleted file mode 100644 index da9ec9e101..0000000000 --- a/examples/examples-dsp/examples-faust/streams-i2s-faust_pitchshift-i2s/pitchShifter.dsp +++ /dev/null @@ -1,22 +0,0 @@ -declare filename "pitchShifter.dsp"; -declare name "pitchShifter"; -declare name "pitchShifter"; -declare version "1.0"; -declare author "Grame"; -declare license "BSD"; -declare copyright "(c)GRAME 2006"; - - //-------------------------------------- - // very simple real time pitch shifter - //-------------------------------------- - -import("stdfaust.lib"); - -pitchshifter = vgroup("Pitch Shifter", ef.transpose( - hslider("window (samples)", 1000, 50, 10000, 1), - hslider("xfade (samples)", 10, 1, 10000, 1), - hslider("shift (semitones) ", 0, -12, +12, 0.1) - ) - ); - -process = (pitchshifter, pitchshifter); diff --git a/examples/examples-dsp/examples-faust/streams-i2s-faust_pitchshift-i2s/pitchShifter.h b/examples/examples-dsp/examples-faust/streams-i2s-faust_pitchshift-i2s/pitchShifter.h deleted file mode 100644 index 39550954de..0000000000 --- a/examples/examples-dsp/examples-faust/streams-i2s-faust_pitchshift-i2s/pitchShifter.h +++ /dev/null @@ -1,172 +0,0 @@ -/* ------------------------------------------------------------ -author: "Grame" -copyright: "(c)GRAME 2006" -license: "BSD" -name: "pitchShifter" -version: "1.0" -Code generated with Faust 2.68.1 (https://faust.grame.fr) -Compilation options: -lang cpp -ct 1 -es 1 -mcd 16 -single -ftz 0 ------------------------------------------------------------- */ - -#ifndef __mydsp_H__ -#define __mydsp_H__ - -#ifndef FAUSTFLOAT -#define FAUSTFLOAT float -#endif - -#include -#include -#include - -#ifndef FAUSTCLASS -#define FAUSTCLASS mydsp -#endif - -#ifdef __APPLE__ -#define exp10f __exp10f -#define exp10 __exp10 -#endif - -#if defined(_WIN32) -#define RESTRICT __restrict -#else -#define RESTRICT __restrict__ -#endif - - -class mydsp : public dsp { - - private: - - FAUSTFLOAT fHslider0; - FAUSTFLOAT fHslider1; - float fRec0[2]; - FAUSTFLOAT fHslider2; - int IOTA0; - float fVec0[131072]; - float fVec1[131072]; - int fSampleRate; - - public: - mydsp() {} - - void metadata(Meta* m) { - m->declare("author", "Grame"); - m->declare("compile_options", "-lang cpp -ct 1 -es 1 -mcd 16 -single -ftz 0"); - m->declare("copyright", "(c)GRAME 2006"); - m->declare("delays.lib/name", "Faust Delay Library"); - m->declare("delays.lib/version", "1.1.0"); - m->declare("filename", "pitchShifter.dsp"); - m->declare("license", "BSD"); - m->declare("maths.lib/author", "GRAME"); - m->declare("maths.lib/copyright", "GRAME"); - m->declare("maths.lib/license", "LGPL with exception"); - m->declare("maths.lib/name", "Faust Math Library"); - m->declare("maths.lib/version", "2.6.0"); - m->declare("misceffects.lib/name", "Misc Effects Library"); - m->declare("misceffects.lib/version", "2.1.0"); - m->declare("name", "pitchShifter"); - m->declare("version", "1.0"); - } - - virtual int getNumInputs() { - return 2; - } - virtual int getNumOutputs() { - return 2; - } - - static void classInit(int sample_rate) { - } - - virtual void instanceConstants(int sample_rate) { - fSampleRate = sample_rate; - } - - virtual void instanceResetUserInterface() { - fHslider0 = FAUSTFLOAT(0.0f); - fHslider1 = FAUSTFLOAT(1e+03f); - fHslider2 = FAUSTFLOAT(1e+01f); - } - - virtual void instanceClear() { - for (int l0 = 0; l0 < 2; l0 = l0 + 1) { - fRec0[l0] = 0.0f; - } - IOTA0 = 0; - for (int l1 = 0; l1 < 131072; l1 = l1 + 1) { - fVec0[l1] = 0.0f; - } - for (int l2 = 0; l2 < 131072; l2 = l2 + 1) { - fVec1[l2] = 0.0f; - } - } - - virtual void init(int sample_rate) { - classInit(sample_rate); - instanceInit(sample_rate); - } - - virtual void instanceInit(int sample_rate) { - instanceConstants(sample_rate); - instanceResetUserInterface(); - instanceClear(); - } - - virtual mydsp* clone() { - return new mydsp(); - } - - virtual int getSampleRate() { - return fSampleRate; - } - - virtual void buildUserInterface(UI* ui_interface) { - ui_interface->openVerticalBox("Pitch Shifter"); - ui_interface->addHorizontalSlider("shift (semitones)", &fHslider0, FAUSTFLOAT(0.0f), FAUSTFLOAT(-12.0f), FAUSTFLOAT(12.0f), FAUSTFLOAT(0.1f)); - ui_interface->addHorizontalSlider("window (samples)", &fHslider1, FAUSTFLOAT(1e+03f), FAUSTFLOAT(5e+01f), FAUSTFLOAT(1e+04f), FAUSTFLOAT(1.0f)); - ui_interface->addHorizontalSlider("xfade (samples)", &fHslider2, FAUSTFLOAT(1e+01f), FAUSTFLOAT(1.0f), FAUSTFLOAT(1e+04f), FAUSTFLOAT(1.0f)); - ui_interface->closeBox(); - } - - virtual void compute(int count, FAUSTFLOAT** RESTRICT inputs, FAUSTFLOAT** RESTRICT outputs) { - FAUSTFLOAT* input0 = inputs[0]; - FAUSTFLOAT* input1 = inputs[1]; - FAUSTFLOAT* output0 = outputs[0]; - FAUSTFLOAT* output1 = outputs[1]; - float fSlow0 = std::pow(2.0f, 0.083333336f * float(fHslider0)); - float fSlow1 = float(fHslider1); - float fSlow2 = 1.0f / float(fHslider2); - for (int i0 = 0; i0 < count; i0 = i0 + 1) { - fRec0[0] = std::fmod(fSlow1 + (fRec0[1] + 1.0f - fSlow0), fSlow1); - float fTemp0 = std::min(fSlow2 * fRec0[0], 1.0f); - float fTemp1 = 1.0f - fTemp0; - float fTemp2 = float(input0[i0]); - fVec0[IOTA0 & 131071] = fTemp2; - float fTemp3 = fSlow1 + fRec0[0]; - int iTemp4 = int(fTemp3); - int iTemp5 = std::min(65537, std::max(0, iTemp4 + 1)); - float fTemp6 = std::floor(fTemp3); - float fTemp7 = fSlow1 + (fRec0[0] - fTemp6); - float fTemp8 = 1.0f - fRec0[0]; - float fTemp9 = fTemp6 + fTemp8 - fSlow1; - int iTemp10 = std::min(65537, std::max(0, iTemp4)); - int iTemp11 = int(fRec0[0]); - int iTemp12 = std::min(65537, std::max(0, iTemp11 + 1)); - float fTemp13 = std::floor(fRec0[0]); - float fTemp14 = fRec0[0] - fTemp13; - float fTemp15 = fTemp13 + fTemp8; - int iTemp16 = std::min(65537, std::max(0, iTemp11)); - output0[i0] = FAUSTFLOAT((fVec0[(IOTA0 - iTemp16) & 131071] * fTemp15 + fTemp14 * fVec0[(IOTA0 - iTemp12) & 131071]) * fTemp0 + (fVec0[(IOTA0 - iTemp10) & 131071] * fTemp9 + fTemp7 * fVec0[(IOTA0 - iTemp5) & 131071]) * fTemp1); - float fTemp17 = float(input1[i0]); - fVec1[IOTA0 & 131071] = fTemp17; - output1[i0] = FAUSTFLOAT(fTemp0 * (fVec1[(IOTA0 - iTemp16) & 131071] * fTemp15 + fTemp14 * fVec1[(IOTA0 - iTemp12) & 131071]) + fTemp1 * (fTemp9 * fVec1[(IOTA0 - iTemp10) & 131071] + fTemp7 * fVec1[(IOTA0 - iTemp5) & 131071])); - fRec0[1] = fRec0[0]; - IOTA0 = IOTA0 + 1; - } - } - -}; - -#endif diff --git a/examples/examples-dsp/examples-faust/streams-i2s-faust_pitchshift-i2s/streams-i2s-faust_pitchshift-i2s.ino b/examples/examples-dsp/examples-faust/streams-i2s-faust_pitchshift-i2s/streams-i2s-faust_pitchshift-i2s.ino deleted file mode 100644 index 51ae33d776..0000000000 --- a/examples/examples-dsp/examples-faust/streams-i2s-faust_pitchshift-i2s/streams-i2s-faust_pitchshift-i2s.ino +++ /dev/null @@ -1,42 +0,0 @@ -/** - * @file streams-i2s-faust_pitchshift-i2s.ino - * @author Phil Schatzmann - * @brief Example how to use Faust for pitch shifting in stereo - * @version 0.1 - * @date 2022-04-22 - * - * @copyright Copyright (c) 2022 - * - */ - -#include "AudioTools.h" -#include "AudioTools/AudioLibs/AudioBoardStream.h" -#include "AudioTools/AudioLibs/AudioFaust.h" -#include "pitchShifter.h" - -AudioBoardStream io(AudioKitEs8388V1); -FaustStream faust(io); // final output to io -StreamCopy copier(faust, io); // copy mic to faust - -// Arduino Setup -void setup(void) { - // Open Serial - Serial.begin(115200); - AudioToolsLogger.begin(Serial, AudioToolsLogLevel::Warning); - - // Setup Faust - auto cfg = faust.defaultConfig(); - faust.begin(cfg); - - - // start I2S - auto cfg_i2s = io.defaultConfig(RXTX_MODE); - cfg_i2s.copyFrom(cfg); - io.begin(cfg_i2s); - -} - -// Arduino loop - copy sound to out -void loop() { - copier.copy(); -} \ No newline at end of file diff --git a/examples/examples-dsp/examples-mozzi/audio_input/audio_input.ino b/examples/examples-dsp/examples-mozzi/audio_input/audio_input.ino deleted file mode 100644 index 3277897e0d..0000000000 --- a/examples/examples-dsp/examples-mozzi/audio_input/audio_input.ino +++ /dev/null @@ -1,54 +0,0 @@ -/** - * Mozzi Input example: we get the input from a generator and - * output the audio via i2s again. In this example we use the - * default value range of 0-1023 for the getAudioInput() method. - * However usually it is easier to define the desired range in - * the Mozzi configuration object: e.g. cfg.input_range_from = -244; - * cfg.input_range_to = 243; - */ -#include "AudioTools.h" -#include "AudioTools/AudioLibs/AudioBoardStream.h" -#include "AudioTools/AudioLibs/MozziStream.h" - -const int sample_rate = 16000; -AudioInfo info(sample_rate, 1, 16); -AudioBoardStream i2s(AudioKitEs8388V1); // final output of decoded stream -MozziStream mozzi; -SineWaveGenerator sineWave; // subclass of SoundGenerator with max amplitude of 32000 -GeneratedSoundStream sound(sineWave); // Stream generated from sine wave - -StreamCopy copier(i2s, mozzi); -// use: Oscil oscilName (wavetable), look in .h file - -void setup() { - Serial.begin(115200); - AudioToolsLogger.begin(Serial, AudioToolsLogLevel::Warning); - - // setup mozzi - auto cfg = mozzi.defaultConfig(); - cfg.control_rate = CONTROL_RATE; - cfg.copyFrom(info); - mozzi.begin(cfg); - - // setup data source for mozzi - mozzi.setInput(sound); - sineWave.begin(info, N_B4); - - // setup output - auto out_cfg = i2s.defaultConfig(RXTX_MODE); - out_cfg.copyFrom(info); - i2s.begin(out_cfg); - i2s.setVolume(1.0); -} - -void updateControl() {} - -AudioOutputMozzi updateAudio() { - int asig = mozzi.getAudioInput(); // range 0-1023 - asig = asig - 512; // now range is -512 to 511 - // output range in STANDARD mode is -244 to 243, - // so you might need to adjust your signal to suit - return asig; -} - -void loop() { copier.copy(); } \ No newline at end of file diff --git a/examples/examples-dsp/examples-mozzi/control_gain-a2dp/control_gain-a2dp.ino b/examples/examples-dsp/examples-mozzi/control_gain-a2dp/control_gain-a2dp.ino deleted file mode 100644 index 3356bde6f0..0000000000 --- a/examples/examples-dsp/examples-mozzi/control_gain-a2dp/control_gain-a2dp.ino +++ /dev/null @@ -1,65 +0,0 @@ -/** - * Demo how to use the Mozzi API to provide a stream of int16_t data. - * Inspired by https://sensorium.github.io/Mozzi/examples/#01.Basics - * The result is published to a bluetooth speaker. We use the more efficient - * A2DP base API with a callback - */ -#include "AudioTools.h" -#include "AudioTools/AudioLibs/A2DPStream.h" -#include "AudioTools/AudioLibs/MozziStream.h" -#include // oscillator template -#include // sine table for oscillator - -const int sample_rate = 44100; -AudioInfo info(sample_rate, 2, 16); // bluetooth requires 44100, stereo, 16 bits -BluetoothA2DPSource a2dp_source; -MozziStream mozzi; // audio source -const int16_t BYTES_PER_FRAME = 4; -// use: Oscil oscilName (wavetable), look in .h file -// of table #included above -Oscil aSin(SIN2048_DATA); -// control variable, use the smallest data size you can for anything used in -// audio -byte gain = 255; - -// callback used by A2DP to provide the sound data - usually len is 128 2 channel int16 frames -int32_t get_sound_data(uint8_t* data, int32_t size) { - int32_t result = mozzi.readBytes(data, size); - //LOGI("get_sound_data %d->%d",size, result); - return result; -} - -// Arduino Setup -void setup(void) { - Serial.begin(115200); - AudioToolsLogger.begin(Serial, AudioToolsLogLevel::Info); - - // setup mozzi - auto cfg = mozzi.defaultConfig(); - cfg.control_rate = CONTROL_RATE; - cfg.copyFrom(info); - mozzi.begin(cfg); - - aSin.setFreq(3320); // set the frequency - - // start the bluetooth - Serial.println("starting A2DP..."); - a2dp_source.set_data_callback(get_sound_data); - a2dp_source.start("LEXON MINO L"); - //a2dp_source.set_volume(100); -} - -void updateControl() { - // as byte, this will automatically roll around to 255 when it passes 0 - gain = gain - 3; -} - -AudioOutputMozzi updateAudio() { - // shift back to STANDARD audio range, like /256 but faster - return (aSin.next() * gain) >> 8; -} - -// Arduino loop - repeated processing -void loop() { - delay(1000); -} \ No newline at end of file diff --git a/examples/examples-dsp/examples-mozzi/control_gain/control_gain.ino b/examples/examples-dsp/examples-mozzi/control_gain/control_gain.ino deleted file mode 100644 index 0b5770e961..0000000000 --- a/examples/examples-dsp/examples-mozzi/control_gain/control_gain.ino +++ /dev/null @@ -1,52 +0,0 @@ -/** - * Demo how to use the Mozzi API to provide a stream of int16_t data. - * Inspired by https://sensorium.github.io/Mozzi/examples/#01.Basics - */ -#include "AudioTools.h" -#include "AudioTools/AudioLibs/AudioBoardStream.h" -#include "AudioTools/AudioLibs/MozziStream.h" -#include // oscillator template -#include // sine table for oscillator - -const int sample_rate = 16000; -AudioInfo info(sample_rate, 1, 16); -AudioBoardStream i2s(AudioKitEs8388V1); // audio sink -MozziStream mozzi; // audio source -StreamCopy copier(i2s, mozzi); // copy source to sink -// use: Oscil oscilName (wavetable), look in .h file -// of table #included above -Oscil aSin(SIN2048_DATA); -// control variable, use the smallest data size you can for anything used in -// audio -byte gain = 255; - -void setup() { - Serial.begin(115200); - AudioToolsLogger.begin(Serial, AudioToolsLogLevel::Info); - - // setup mozzi - auto cfg = mozzi.defaultConfig(); - cfg.control_rate = CONTROL_RATE; - cfg.copyFrom(info); - mozzi.begin(cfg); - - // setup output - auto out_cfg = i2s.defaultConfig(); - out_cfg.copyFrom(info); - i2s.begin(out_cfg); - - // setup mozzi sine - aSin.setFreq(3320); // set the frequency -} - -void loop() { copier.copy(); } - -void updateControl() { - // as byte, this will automatically roll around to 255 when it passes 0 - gain = gain - 3; -} - -AudioOutputMozzi updateAudio() { - return (aSin.next() * gain) >> - 8; // shift back to STANDARD audio range, like /256 but faster -} \ No newline at end of file diff --git a/examples/examples-dsp/examples-pd/README.md b/examples/examples-dsp/examples-pd/README.md deleted file mode 100644 index d5ed42f4c6..0000000000 --- a/examples/examples-dsp/examples-pd/README.md +++ /dev/null @@ -1 +0,0 @@ -Pure Data (or just "Pd") is an open source visual programming language for multimedia. Further information can be found in the [Wiki](https://github.com/pschatzmann/arduino-audio-tools/wiki/Pure-Data) \ No newline at end of file diff --git a/examples/examples-dsp/examples-pd/streams-generator-pd-audiokit/HeavyContext.cpp b/examples/examples-dsp/examples-pd/streams-generator-pd-audiokit/HeavyContext.cpp deleted file mode 100644 index 88fa134215..0000000000 --- a/examples/examples-dsp/examples-pd/streams-generator-pd-audiokit/HeavyContext.cpp +++ /dev/null @@ -1,264 +0,0 @@ -/** - * Copyright (c) 2014-2018 Enzien Audio Ltd. - * - * Permission to use, copy, modify, and/or distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH - * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY - * AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, - * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM - * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR - * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR - * PERFORMANCE OF THIS SOFTWARE. - */ - -#include "HeavyContext.hpp" -#include "HvTable.h" - -void defaultSendHook(HeavyContextInterface *context, - const char *sendName, hv_uint32_t sendHash, const HvMessage *msg) { - HeavyContext *thisContext = reinterpret_cast(context); - const hv_uint32_t numBytes = sizeof(ReceiverMessagePair) + msg_getSize(msg) - sizeof(HvMessage); - ReceiverMessagePair *p = reinterpret_cast(hLp_getWriteBuffer(&thisContext->outQueue, numBytes)); - if (p != nullptr) { - p->receiverHash = sendHash; - msg_copyToBuffer(msg, (char *) &p->msg, msg_getSize(msg)); - hLp_produce(&thisContext->outQueue, numBytes); - } else { - hv_assert(false && - "::defaultSendHook - The out message queue is full and cannot accept more messages until they " - "have been processed. Try increasing the outQueueKb size in the new_with_options() constructor."); - } -} - -HeavyContext::HeavyContext(double sampleRate, int poolKb, int inQueueKb, int outQueueKb) : - sampleRate(sampleRate) { - - hv_assert(sampleRate > 0.0); // sample rate must be positive - hv_assert(poolKb > 0); - hv_assert(inQueueKb > 0); - hv_assert(outQueueKb >= 0); - - blockStartTimestamp = 0; - printHook = nullptr; - userData = nullptr; - - // if outQueueKb is positive, then the outQueue is allocated and the default sendhook is set. - // Otherwise outQueue and the sendhook are set to NULL. - sendHook = (outQueueKb > 0) ? &defaultSendHook : nullptr; - - HV_SPINLOCK_RELEASE(inQueueLock); - HV_SPINLOCK_RELEASE(outQueueLock); - - numBytes = sizeof(HeavyContext); - - numBytes += mq_initWithPoolSize(&mq, poolKb); - numBytes += hLp_init(&inQueue, inQueueKb * 1024); - numBytes += hLp_init(&outQueue, outQueueKb * 1024); // outQueueKb value of 0 sets everything to NULL -} - -HeavyContext::~HeavyContext() { - mq_free(&mq); - hLp_free(&inQueue); - hLp_free(&outQueue); -} - -bool HeavyContext::sendBangToReceiver(hv_uint32_t receiverHash) { - HvMessage *m = HV_MESSAGE_ON_STACK(1); - msg_initWithBang(m, 0); - bool success = sendMessageToReceiver(receiverHash, 0.0, m); - return success; -} - -bool HeavyContext::sendFloatToReceiver(hv_uint32_t receiverHash, float f) { - HvMessage *m = HV_MESSAGE_ON_STACK(1); - msg_initWithFloat(m, 0, f); - bool success = sendMessageToReceiver(receiverHash, 0.0, m); - return success; -} - -bool HeavyContext::sendSymbolToReceiver(hv_uint32_t receiverHash, const char *s) { - hv_assert(s != nullptr); - HvMessage *m = HV_MESSAGE_ON_STACK(1); - msg_initWithSymbol(m, 0, (char *) s); - bool success = sendMessageToReceiver(receiverHash, 0.0, m); - return success; -} - -bool HeavyContext::sendMessageToReceiverV(hv_uint32_t receiverHash, double delayMs, const char *format, ...) { - hv_assert(delayMs >= 0.0); - hv_assert(format != nullptr); - - va_list ap; - va_start(ap, format); - const int numElem = (int) hv_strlen(format); - HvMessage *m = HV_MESSAGE_ON_STACK(numElem); - msg_init(m, numElem, blockStartTimestamp + (hv_uint32_t) (hv_max_d(0.0, delayMs)*getSampleRate()/1000.0)); - for (int i = 0; i < numElem; i++) { - switch (format[i]) { - case 'b': msg_setBang(m, i); break; - case 'f': msg_setFloat(m, i, (float) va_arg(ap, double)); break; - case 'h': msg_setHash(m, i, (int) va_arg(ap, int)); break; - case 's': msg_setSymbol(m, i, (char *) va_arg(ap, char *)); break; - default: break; - } - } - va_end(ap); - - bool success = sendMessageToReceiver(receiverHash, delayMs, m); - return success; -} - -bool HeavyContext::sendMessageToReceiver(hv_uint32_t receiverHash, double delayMs, HvMessage *m) { - hv_assert(delayMs >= 0.0); - hv_assert(m != nullptr); - - const hv_uint32_t timestamp = blockStartTimestamp + - (hv_uint32_t) (hv_max_d(0.0, delayMs)*(getSampleRate()/1000.0)); - - ReceiverMessagePair *p = nullptr; - HV_SPINLOCK_ACQUIRE(inQueueLock); - const hv_uint32_t numBytes = sizeof(ReceiverMessagePair) + msg_getSize(m) - sizeof(HvMessage); - p = (ReceiverMessagePair *) hLp_getWriteBuffer(&inQueue, numBytes); - if (p != nullptr) { - p->receiverHash = receiverHash; - msg_copyToBuffer(m, (char *) &p->msg, msg_getSize(m)); - msg_setTimestamp(&p->msg, timestamp); - hLp_produce(&inQueue, numBytes); - } else { - hv_assert(false && - "::sendMessageToReceiver - The input message queue is full and cannot accept more messages until they " - "have been processed. Try increasing the inQueueKb size in the new_with_options() constructor."); - } - HV_SPINLOCK_RELEASE(inQueueLock); - return (p != nullptr); -} - -bool HeavyContext::cancelMessage(HvMessage *m, void (*sendMessage)(HeavyContextInterface *, int, const HvMessage *)) { - return mq_removeMessage(&mq, m, sendMessage); -} - -HvMessage *HeavyContext::scheduleMessageForObject(const HvMessage *m, - void (*sendMessage)(HeavyContextInterface *, int, const HvMessage *), - int letIndex) { - HvMessage *n = mq_addMessageByTimestamp(&mq, m, letIndex, sendMessage); - return n; -} - -float *HeavyContext::getBufferForTable(hv_uint32_t tableHash) { - HvTable *t = getTableForHash(tableHash); - if (t != nullptr) { - return hTable_getBuffer(t); - } else return nullptr; -} - -int HeavyContext::getLengthForTable(hv_uint32_t tableHash) { - HvTable *t = getTableForHash(tableHash); - if (t != nullptr) { - return hTable_getLength(t); - } else return 0; -} - -bool HeavyContext::setLengthForTable(hv_uint32_t tableHash, hv_uint32_t newSampleLength) { - HvTable *t = getTableForHash(tableHash); - if (t != nullptr) { - hTable_resize(t, newSampleLength); - return true; - } else return false; -} - -void HeavyContext::lockAcquire() { - HV_SPINLOCK_ACQUIRE(inQueueLock); -} - -bool HeavyContext::lockTry() { - HV_SPINLOCK_TRY(inQueueLock); -} - -void HeavyContext::lockRelease() { - HV_SPINLOCK_RELEASE(inQueueLock); -} - -void HeavyContext::setInputMessageQueueSize(int inQueueKb) { - hv_assert(inQueueKb > 0); - hLp_free(&inQueue); - hLp_init(&inQueue, inQueueKb*1024); -} - -void HeavyContext::setOutputMessageQueueSize(int outQueueKb) { - hv_assert(outQueueKb > 0); - hLp_free(&outQueue); - hLp_init(&outQueue, outQueueKb*1024); -} - -bool HeavyContext::getNextSentMessage(hv_uint32_t *destinationHash, HvMessage *outMsg, hv_size_t msgLengthBytes) { - *destinationHash = 0; - ReceiverMessagePair *p = nullptr; - hv_assert((sendHook == &defaultSendHook) && - "::getNextSentMessage - this function won't do anything if the msg outQueue " - "size is 0, or you've overriden the default sendhook."); - if (sendHook == &defaultSendHook) { - HV_SPINLOCK_ACQUIRE(outQueueLock); - if (hLp_hasData(&outQueue)) { - hv_uint32_t numBytes = 0; - p = reinterpret_cast(hLp_getReadBuffer(&outQueue, &numBytes)); - hv_assert((p != nullptr) && "::getNextSentMessage - something bad happened."); - hv_assert(numBytes >= sizeof(ReceiverMessagePair)); - hv_assert((numBytes <= msgLengthBytes) && - "::getNextSentMessage - the sent message is bigger than the message " - "passed to handle it."); - *destinationHash = p->receiverHash; - hv_memcpy(outMsg, &p->msg, numBytes); - hLp_consume(&outQueue); - } - HV_SPINLOCK_RELEASE(outQueueLock); - } - return (p != nullptr); -} - -hv_uint32_t HeavyContext::getHashForString(const char *str) { - return hv_string_to_hash(str); -} - -HvTable *_hv_table_get(HeavyContextInterface *c, hv_uint32_t tableHash) { - hv_assert(c != nullptr); - return reinterpret_cast(c)->getTableForHash(tableHash); -} - -void _hv_scheduleMessageForReceiver(HeavyContextInterface *c, hv_uint32_t receiverHash, HvMessage *m) { - hv_assert(c != nullptr); - reinterpret_cast(c)->scheduleMessageForReceiver(receiverHash, m); -} - -HvMessage *_hv_scheduleMessageForObject(HeavyContextInterface *c, const HvMessage *m, - void (*sendMessage)(HeavyContextInterface *, int, const HvMessage *), - int letIndex) { - hv_assert(c != nullptr); - HvMessage *n = reinterpret_cast(c)->scheduleMessageForObject( - m, sendMessage, letIndex); - return n; -} - -#ifdef __cplusplus -extern "C" { -#endif - -HvTable *hv_table_get(HeavyContextInterface *c, hv_uint32_t tableHash) { - return _hv_table_get(c, tableHash); -} - -void hv_scheduleMessageForReceiver(HeavyContextInterface *c, hv_uint32_t receiverHash, HvMessage *m) { - _hv_scheduleMessageForReceiver(c, receiverHash, m); -} - -HvMessage *hv_scheduleMessageForObject(HeavyContextInterface *c, const HvMessage *m, - void (*sendMessage)(HeavyContextInterface *, int, const HvMessage *), - int letIndex) { - return _hv_scheduleMessageForObject(c, m, sendMessage, letIndex); -} - -#ifdef __cplusplus -} -#endif diff --git a/examples/examples-dsp/examples-pd/streams-generator-pd-audiokit/HeavyContext.hpp b/examples/examples-dsp/examples-pd/streams-generator-pd-audiokit/HeavyContext.hpp deleted file mode 100644 index 87b95d74c2..0000000000 --- a/examples/examples-dsp/examples-pd/streams-generator-pd-audiokit/HeavyContext.hpp +++ /dev/null @@ -1,107 +0,0 @@ -/** - * Copyright (c) 2014-2018 Enzien Audio Ltd. - * - * Permission to use, copy, modify, and/or distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH - * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY - * AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, - * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM - * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR - * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR - * PERFORMANCE OF THIS SOFTWARE. - */ - -#ifndef _HEAVY_CONTEXT_H_ -#define _HEAVY_CONTEXT_H_ - -#include "HeavyContextInterface.hpp" -#include "HvLightPipe.h" -#include "HvMessageQueue.h" -#include "HvMath.h" - -struct HvTable; - -class HeavyContext : public HeavyContextInterface { - - public: - HeavyContext(double sampleRate, int poolKb=10, int inQueueKb=2, int outQueueKb=0); - virtual ~HeavyContext(); - - int getSize() override { return (int) numBytes; } - - double getSampleRate() override { return sampleRate; } - - hv_uint32_t getCurrentSample() override { return blockStartTimestamp; } - float samplesToMilliseconds(hv_uint32_t numSamples) override { return (float) (1000.0*numSamples/sampleRate); } - hv_uint32_t millisecondsToSamples(float ms) override { return (hv_uint32_t) (hv_max_f(0.0f,ms)*sampleRate/1000.0); } - - void setUserData(void *x) override { userData = x; } - void *getUserData() override { return userData; } - - // hook management - void setSendHook(HvSendHook_t *f) override { sendHook = f; } - HvSendHook_t *getSendHook() override { return sendHook; } - - void setPrintHook(HvPrintHook_t *f) override { printHook = f; } - HvPrintHook_t *getPrintHook() override { return printHook; } - - // message scheduling - bool sendMessageToReceiver(hv_uint32_t receiverHash, double delayMs, HvMessage *m) override; - bool sendMessageToReceiverV(hv_uint32_t receiverHash, double delayMs, const char *fmt, ...) override; - bool sendFloatToReceiver(hv_uint32_t receiverHash, float f) override; - bool sendBangToReceiver(hv_uint32_t receiverHash) override; - bool sendSymbolToReceiver(hv_uint32_t receiverHash, const char *symbol) override; - bool cancelMessage(HvMessage *m, void (*sendMessage)(HeavyContextInterface *, int, const HvMessage *)) override; - - // table manipulation - float *getBufferForTable(hv_uint32_t tableHash) override; - int getLengthForTable(hv_uint32_t tableHash) override; - bool setLengthForTable(hv_uint32_t tableHash, hv_uint32_t newSampleLength) override; - - // lock control - void lockAcquire() override; - bool lockTry() override; - void lockRelease() override; - - // message queue management - void setInputMessageQueueSize(int inQueueKb) override; - void setOutputMessageQueueSize(int outQueueKb) override; - bool getNextSentMessage(hv_uint32_t *destinationHash, HvMessage *outMsg, hv_size_t msgLength) override; - - // utility functions - static hv_uint32_t getHashForString(const char *str); - - protected: - virtual HvTable *getTableForHash(hv_uint32_t tableHash) = 0; - friend HvTable *_hv_table_get(HeavyContextInterface *, hv_uint32_t); - - virtual void scheduleMessageForReceiver(hv_uint32_t receiverHash, HvMessage *m) = 0; - friend void _hv_scheduleMessageForReceiver(HeavyContextInterface *, hv_uint32_t, HvMessage *); - - HvMessage *scheduleMessageForObject(const HvMessage *, - void (*sendMessage)(HeavyContextInterface *, int, const HvMessage *), - int); - friend HvMessage *_hv_scheduleMessageForObject(HeavyContextInterface *, const HvMessage *, - void (*sendMessage)(HeavyContextInterface *, int, const HvMessage *), - int); - - friend void defaultSendHook(HeavyContextInterface *, const char *, hv_uint32_t, const HvMessage *); - - // object state - double sampleRate; - hv_uint32_t blockStartTimestamp; - hv_size_t numBytes; - HvMessageQueue mq; - HvSendHook_t *sendHook; - HvPrintHook_t *printHook; - void *userData; - HvLightPipe inQueue; - HvLightPipe outQueue; - hv_atomic_bool inQueueLock; - hv_atomic_bool outQueueLock; -}; - -#endif // _HEAVY_CONTEXT_H_ diff --git a/examples/examples-dsp/examples-pd/streams-generator-pd-audiokit/HeavyContextInterface.hpp b/examples/examples-dsp/examples-pd/streams-generator-pd-audiokit/HeavyContextInterface.hpp deleted file mode 100644 index a78fcbf396..0000000000 --- a/examples/examples-dsp/examples-pd/streams-generator-pd-audiokit/HeavyContextInterface.hpp +++ /dev/null @@ -1,291 +0,0 @@ -/** - * Copyright (c) 2014-2018 Enzien Audio Ltd. - * - * Permission to use, copy, modify, and/or distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH - * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY - * AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, - * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM - * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR - * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR - * PERFORMANCE OF THIS SOFTWARE. - */ - -#ifndef _HEAVY_CONTEXT_INTERFACE_H_ -#define _HEAVY_CONTEXT_INTERFACE_H_ - -#include "HvUtils.h" - -#ifndef _HEAVY_DECLARATIONS_ -#define _HEAVY_DECLARATIONS_ - -class HeavyContextInterface; -struct HvMessage; - -typedef enum { - HV_PARAM_TYPE_PARAMETER_IN, - HV_PARAM_TYPE_PARAMETER_OUT, - HV_PARAM_TYPE_EVENT_IN, - HV_PARAM_TYPE_EVENT_OUT -} HvParameterType; - -typedef struct HvParameterInfo { - const char *name; // the human readable parameter name - hv_uint32_t hash; // an integer identified used by heavy for this parameter - HvParameterType type; // type of this parameter - float minVal; // the minimum value of this parameter - float maxVal; // the maximum value of this parameter - float defaultVal; // the default value of this parameter -} HvParameterInfo; - -typedef void (HvSendHook_t) (HeavyContextInterface *context, const char *sendName, hv_uint32_t sendHash, const HvMessage *msg); -typedef void (HvPrintHook_t) (HeavyContextInterface *context, const char *printName, const char *str, const HvMessage *msg); - -#endif // _HEAVY_DECLARATIONS_ - - - -class HeavyContextInterface { - - public: - HeavyContextInterface() {} - virtual ~HeavyContextInterface() {}; - - /** Returns the read-only user-assigned name of this patch. */ - virtual const char *getName() = 0; - - /** Returns the number of input channels with which this context has been configured. */ - virtual int getNumInputChannels() = 0; - - /** Returns the number of output channels with which this context has been configured. */ - virtual int getNumOutputChannels() = 0; - - /** - * Returns the total size in bytes of the context. - * This value may change if tables are resized. - */ - virtual int getSize() = 0; - - /** Returns the sample rate with which this context has been configured. */ - virtual double getSampleRate() = 0; - - /** Returns the current patch time in samples. This value is always exact. */ - virtual hv_uint32_t getCurrentSample() = 0; - virtual float samplesToMilliseconds(hv_uint32_t numSamples) = 0; - - /** Converts milliseconds to samples. Input is limited to non-negative range. */ - virtual hv_uint32_t millisecondsToSamples(float ms) = 0; - - /** Sets a user-definable value. This value is never manipulated by Heavy. */ - virtual void setUserData(void *x) = 0; - - /** Returns the user-defined data. */ - virtual void *getUserData() = 0; - - /** - * Set the send hook. The function is called whenever a message is sent to any send object. - * Messages returned by this function should NEVER be freed. If the message must persist, call - * hv_msg_copy() first. - */ - virtual void setSendHook(HvSendHook_t *f) = 0; - - /** Returns the send hook, or NULL if unset. */ - virtual HvSendHook_t *getSendHook() = 0; - - /** Set the print hook. The function is called whenever a message is sent to a print object. */ - virtual void setPrintHook(HvPrintHook_t *f) = 0; - - /** Returns the print hook, or NULL if unset. */ - virtual HvPrintHook_t *getPrintHook() = 0; - - /** - * Processes one block of samples for a patch instance. The buffer format is an array of float channel arrays. - * If the context has not input or output channels, the respective argument may be NULL. - * The number of samples to to tbe processed should be a multiple of 1, 4, or 8, depending on if - * no, SSE or NEON, or AVX optimisation is being used, respectively. - * e.g. [[LLLL][RRRR]] - * - * @return The number of samples processed. - * - * This function is NOT thread-safe. It is assumed that only the audio thread will execute this function. - */ - virtual int process(float **inputBuffers, float **outputBuffer, int n) = 0; - - /** - * Processes one block of samples for a patch instance. The buffer format is an uninterleaved float array of channels. - * If the context has not input or output channels, the respective argument may be NULL. - * The number of samples to to tbe processed should be a multiple of 1, 4, or 8, depending on if - * no, SSE or NEON, or AVX optimisation is being used, respectively. - * e.g. [LLLLRRRR] - * - * @return The number of samples processed. - * - * This function is NOT thread-safe. It is assumed that only the audio thread will execute this function. - */ - virtual int processInline(float *inputBuffers, float *outputBuffer, int n) = 0; - - /** - * Processes one block of samples for a patch instance. The buffer format is an interleaved float array of channels. - * If the context has not input or output channels, the respective argument may be NULL. - * The number of samples to to tbe processed should be a multiple of 1, 4, or 8, depending on if - * no, SSE or NEON, or AVX optimisation is being used, respectively. - * e.g. [LRLRLRLR] - * - * @return The number of samples processed. - * - * This function is NOT thread-safe. It is assumed that only the audio thread will execute this function. - */ - virtual int processInlineInterleaved(float *inputBuffers, float *outputBuffer, int n) = 0; - - /** - * Sends a formatted message to a receiver that can be scheduled for the future. - * The receiver is addressed with its hash, which can also be determined using hv_stringToHash(). - * This function is thread-safe. - * - * @return True if the message was accepted. False if the message could not fit onto - * the message queue to be processed this block. - */ - virtual bool sendMessageToReceiver(hv_uint32_t receiverHash, double delayMs, HvMessage *m) = 0; - - /** - * Sends a formatted message to a receiver that can be scheduled for the future. - * The receiver is addressed with its hash, which can also be determined using hv_stringToHash(). - * This function is thread-safe. - * - * @return True if the message was accepted. False if the message could not fit onto - * the message queue to be processed this block. - */ - virtual bool sendMessageToReceiverV(hv_uint32_t receiverHash, double delayMs, const char *fmt, ...) = 0; - - /** - * A convenience function to send a float to a receiver to be processed immediately. - * The receiver is addressed with its hash, which can also be determined using hv_stringToHash(). - * This function is thread-safe. - * - * @return True if the message was accepted. False if the message could not fit onto - * the message queue to be processed this block. - */ - virtual bool sendFloatToReceiver(hv_uint32_t receiverHash, float f) = 0; - - /** - * A convenience function to send a bang to a receiver to be processed immediately. - * The receiver is addressed with its hash, which can also be determined using hv_stringToHash(). - * This function is thread-safe. - * - * @return True if the message was accepted. False if the message could not fit onto - * the message queue to be processed this block. - */ - virtual bool sendBangToReceiver(hv_uint32_t receiverHash) = 0; - - /** - * A convenience function to send a symbol to a receiver to be processed immediately. - * The receiver is addressed with its hash, which can also be determined using hv_stringToHash(). - * This function is thread-safe. - * - * @return True if the message was accepted. False if the message could not fit onto - * the message queue to be processed this block. - */ - virtual bool sendSymbolToReceiver(hv_uint32_t receiverHash, const char *symbol) = 0; - - /** - * Cancels a previously scheduled message. - * - * @param sendMessage May be NULL. - */ - virtual bool cancelMessage(HvMessage *m, void (*sendMessage)(HeavyContextInterface *, int, const HvMessage *)=nullptr) = 0; - - /** - * Returns information about each parameter such as name, hash, and range. - * The total number of parameters is always returned. - * - * @param index The parameter index. - * @param info A pointer to a HvParameterInfo struct. May be null. - * - * @return The total number of parameters. - */ - virtual int getParameterInfo(int index, HvParameterInfo *info) = 0; - - /** Returns a pointer to the raw buffer backing this table. DO NOT free it. */ - virtual float *getBufferForTable(hv_uint32_t tableHash) = 0; - - /** Returns the length of this table in samples. */ - virtual int getLengthForTable(hv_uint32_t tableHash) = 0; - - /** - * Resizes the table to the given length. - * - * Existing contents are copied to the new table. Remaining space is cleared - * if the table is longer than the original, truncated otherwise. - * - * @param tableHash The table identifier. - * @param newSampleLength The new length of the table, in samples. - * - * @return False if the table could not be found. True otherwise. - */ - virtual bool setLengthForTable(hv_uint32_t tableHash, hv_uint32_t newSampleLength) = 0; - - /** - * Acquire the input message queue lock. - * - * This function will block until the message lock as been acquired. - * Typical applications will not require the use of this function. - */ - virtual void lockAcquire() = 0; - - /** - * Try to acquire the input message queue lock. - * - * If the lock has been acquired, hv_lock_release() must be called to release it. - * Typical applications will not require the use of this function. - * - * @return Returns true if the lock has been acquired, false otherwise. - */ - virtual bool lockTry() = 0; - - /** - * Release the input message queue lock. - * - * Typical applications will not require the use of this function. - */ - virtual void lockRelease() = 0; - - /** - * Set the size of the input message queue in kilobytes. - * - * The buffer is reset and all existing contents are lost on resize. - * - * @param inQueueKb Must be positive i.e. at least one. - */ - virtual void setInputMessageQueueSize(int inQueueKb) = 0; - - /** - * Set the size of the output message queue in kilobytes. - * - * The buffer is reset and all existing contents are lost on resize. - * Only the default sendhook uses the outgoing message queue. If the default - * sendhook is not being used, then this function is not useful. - * - * @param outQueueKb Must be postive i.e. at least one. - */ - virtual void setOutputMessageQueueSize(int outQueueKb) = 0; - - /** - * Get the next message in the outgoing queue, will also consume the message. - * Returns false if there are no messages. - * - * @param destinationHash a hash of the name of the receiver the message was sent to. - * @param outMsg message pointer that is filled by the next message contents. - * @param msgLengthBytes max length of outMsg in bytes. - * - * @return True if there is a message in the outgoing queue. - */ - virtual bool getNextSentMessage(hv_uint32_t *destinationHash, HvMessage *outMsg, hv_size_t msgLengthBytes) = 0; - - /** Returns a 32-bit hash of any string. Returns 0 if string is NULL. */ - static hv_uint32_t getHashForString(const char *str); -}; - -#endif // _HEAVY_CONTEXT_INTERFACE_H_ diff --git a/examples/examples-dsp/examples-pd/streams-generator-pd-audiokit/Heavy_Adc2Dac.cpp b/examples/examples-dsp/examples-pd/streams-generator-pd-audiokit/Heavy_Adc2Dac.cpp deleted file mode 100644 index 5c88136494..0000000000 --- a/examples/examples-dsp/examples-pd/streams-generator-pd-audiokit/Heavy_Adc2Dac.cpp +++ /dev/null @@ -1,274 +0,0 @@ -/** - * Copyright (c) 2024 Enzien Audio, Ltd. - * - * Redistribution and use in source and binary forms, with or without modification, - * are permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright notice, - * this list of conditions, and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the phrase "powered by heavy", - * the heavy logo, and a hyperlink to https://enzienaudio.com, all in a visible - * form. - * - * 2.1 If the Application is distributed in a store system (for example, - * the Apple "App Store" or "Google Play"), the phrase "powered by heavy" - * shall be included in the app description or the copyright text as well as - * the in the app itself. The heavy logo will shall be visible in the app - * itself as well. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, - * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR - * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER - * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, - * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF - * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - */ - -#include "Heavy_Adc2Dac.hpp" - -#include - -#define Context(_c) static_cast(_c) - - -/* - * C Functions - */ - -extern "C" { - HV_EXPORT HeavyContextInterface *hv_Adc2Dac_new(double sampleRate) { - // allocate aligned memory - void *ptr = hv_malloc(sizeof(Heavy_Adc2Dac)); - // ensure non-null - if (!ptr) return nullptr; - // call constructor - new(ptr) Heavy_Adc2Dac(sampleRate); - return Context(ptr); - } - - HV_EXPORT HeavyContextInterface *hv_Adc2Dac_new_with_options(double sampleRate, - int poolKb, int inQueueKb, int outQueueKb) { - // allocate aligned memory - void *ptr = hv_malloc(sizeof(Heavy_Adc2Dac)); - // ensure non-null - if (!ptr) return nullptr; - // call constructor - new(ptr) Heavy_Adc2Dac(sampleRate, poolKb, inQueueKb, outQueueKb); - return Context(ptr); - } - - HV_EXPORT void hv_Adc2Dac_free(HeavyContextInterface *instance) { - // call destructor - Context(instance)->~Heavy_Adc2Dac(); - // free memory - hv_free(instance); - } -} // extern "C" - - - - - - - -/* - * Class Functions - */ - -Heavy_Adc2Dac::Heavy_Adc2Dac(double sampleRate, int poolKb, int inQueueKb, int outQueueKb) - : HeavyContext(sampleRate, poolKb, inQueueKb, outQueueKb) { - -} - -Heavy_Adc2Dac::~Heavy_Adc2Dac() { - // nothing to free -} - -HvTable *Heavy_Adc2Dac::getTableForHash(hv_uint32_t tableHash) { - return nullptr; -} - -void Heavy_Adc2Dac::scheduleMessageForReceiver(hv_uint32_t receiverHash, HvMessage *m) { - switch (receiverHash) { - default: return; - } -} - -int Heavy_Adc2Dac::getParameterInfo(int index, HvParameterInfo *info) { - if (info != nullptr) { - switch (index) { - default: { - info->name = "invalid parameter index"; - info->hash = 0; - info->type = HvParameterType::HV_PARAM_TYPE_PARAMETER_IN; - info->minVal = 0.0f; - info->maxVal = 0.0f; - info->defaultVal = 0.0f; - break; - } - } - } - return 0; -} - - - -/* - * Send Function Implementations - */ - - - - - -/* - * Context Process Implementation - */ - -int Heavy_Adc2Dac::process(float **inputBuffers, float **outputBuffers, int n) { - while (hLp_hasData(&inQueue)) { - hv_uint32_t numBytes = 0; - ReceiverMessagePair *p = reinterpret_cast(hLp_getReadBuffer(&inQueue, &numBytes)); - hv_assert(numBytes >= sizeof(ReceiverMessagePair)); - scheduleMessageForReceiver(p->receiverHash, &p->msg); - hLp_consume(&inQueue); - } - - sendBangToReceiver(0xDD21C0EB); // send to __hv_bang~ on next cycle - const int n4 = n & ~HV_N_SIMD_MASK; // ensure that the block size is a multiple of HV_N_SIMD - - // temporary signal vars - - // input and output vars - hv_bufferf_t O0, O1; - hv_bufferf_t I0, I1; - - // declare and init the zero buffer - hv_bufferf_t ZERO; __hv_zero_f(VOf(ZERO)); - - hv_uint32_t nextBlock = blockStartTimestamp; - for (int n = 0; n < n4; n += HV_N_SIMD) { - - // process all of the messages for this block - nextBlock += HV_N_SIMD; - while (mq_hasMessageBefore(&mq, nextBlock)) { - MessageNode *const node = mq_peek(&mq); - node->sendMessage(this, node->let, node->m); - mq_pop(&mq); - } - - // load input buffers - __hv_load_f(inputBuffers[0]+n, VOf(I0)); - __hv_load_f(inputBuffers[1]+n, VOf(I1)); - - // zero output buffers - __hv_zero_f(VOf(O0)); - __hv_zero_f(VOf(O1)); - - // process all signal functions - __hv_add_f(VIf(I0), VIf(O0), VOf(O0)); - __hv_add_f(VIf(I1), VIf(O1), VOf(O1)); - - // save output vars to output buffer - __hv_store_f(outputBuffers[0]+n, VIf(O0)); - __hv_store_f(outputBuffers[1]+n, VIf(O1)); - } - - blockStartTimestamp = nextBlock; - - return n4; // return the number of frames processed - -} - -int Heavy_Adc2Dac::processInline(float *inputBuffers, float *outputBuffers, int n4) { - hv_assert(!(n4 & HV_N_SIMD_MASK)); // ensure that n4 is a multiple of HV_N_SIMD - - // define the heavy input buffer for 2 channel(s) - float **const bIn = reinterpret_cast(hv_alloca(2*sizeof(float *))); - bIn[0] = inputBuffers+(0*n4); - bIn[1] = inputBuffers+(1*n4); - - // define the heavy output buffer for 2 channel(s) - float **const bOut = reinterpret_cast(hv_alloca(2*sizeof(float *))); - bOut[0] = outputBuffers+(0*n4); - bOut[1] = outputBuffers+(1*n4); - - int n = process(bIn, bOut, n4); - return n; -} - -int Heavy_Adc2Dac::processInlineInterleaved(float *inputBuffers, float *outputBuffers, int n4) { - hv_assert(n4 & ~HV_N_SIMD_MASK); // ensure that n4 is a multiple of HV_N_SIMD - - // define the heavy input buffer for 2 channel(s), uninterleave - float *const bIn = reinterpret_cast(hv_alloca(2*n4*sizeof(float))); - #if HV_SIMD_SSE || HV_SIMD_AVX - for (int i = 0, j = 0; j < n4; j += 4, i += 8) { - __m128 a = _mm_load_ps(inputBuffers+i); // LRLR - __m128 b = _mm_load_ps(inputBuffers+4+i); // LRLR - __m128 x = _mm_shuffle_ps(a, b, _MM_SHUFFLE(2,0,2,0)); // LLLL - __m128 y = _mm_shuffle_ps(a, b, _MM_SHUFFLE(3,1,3,1)); // RRRR - _mm_store_ps(bIn+j, x); - _mm_store_ps(bIn+n4+j, y); - } - #elif HV_SIMD_NEON - for (int i = 0, j = 0; j < n4; j += 4, i += 8) { - float32x4x2_t a = vld2q_f32(inputBuffers+i); // load and uninterleave - vst1q_f32(bIn+j, a.val[0]); - vst1q_f32(bIn+n4+j, a.val[1]); - } - #else // HV_SIMD_NONE - for (int j = 0; j < n4; ++j) { - bIn[0*n4+j] = inputBuffers[0+2*j]; - bIn[1*n4+j] = inputBuffers[1+2*j]; - } - #endif - - // define the heavy output buffer for 2 channel(s) - float *const bOut = reinterpret_cast(hv_alloca(2*n4*sizeof(float))); - - int n = processInline(bIn, bOut, n4); - - // interleave the heavy output into the output buffer - #if HV_SIMD_AVX - for (int i = 0, j = 0; j < n4; j += 8, i += 16) { - __m256 x = _mm256_load_ps(bOut+j); // LLLLLLLL - __m256 y = _mm256_load_ps(bOut+n4+j); // RRRRRRRR - __m256 a = _mm256_unpacklo_ps(x, y); // LRLRLRLR - __m256 b = _mm256_unpackhi_ps(x, y); // LRLRLRLR - _mm256_store_ps(outputBuffers+i, a); - _mm256_store_ps(outputBuffers+8+i, b); - } - #elif HV_SIMD_SSE - for (int i = 0, j = 0; j < n4; j += 4, i += 8) { - __m128 x = _mm_load_ps(bOut+j); // LLLL - __m128 y = _mm_load_ps(bOut+n4+j); // RRRR - __m128 a = _mm_unpacklo_ps(x, y); // LRLR - __m128 b = _mm_unpackhi_ps(x, y); // LRLR - _mm_store_ps(outputBuffers+i, a); - _mm_store_ps(outputBuffers+4+i, b); - } - #elif HV_SIMD_NEON - // https://community.arm.com/groups/processors/blog/2012/03/13/coding-for-neon--part-5-rearranging-vectors - for (int i = 0, j = 0; j < n4; j += 4, i += 8) { - float32x4_t x = vld1q_f32(bOut+j); - float32x4_t y = vld1q_f32(bOut+n4+j); - float32x4x2_t z = {x, y}; - vst2q_f32(outputBuffers+i, z); // interleave and store - } - #else // HV_SIMD_NONE - for (int i = 0; i < 2; ++i) { - for (int j = 0; j < n4; ++j) { - outputBuffers[i+2*j] = bOut[i*n4+j]; - } - } - #endif - - return n; -} diff --git a/examples/examples-dsp/examples-pd/streams-generator-pd-audiokit/Heavy_Adc2Dac.h b/examples/examples-dsp/examples-pd/streams-generator-pd-audiokit/Heavy_Adc2Dac.h deleted file mode 100644 index ad55758b73..0000000000 --- a/examples/examples-dsp/examples-pd/streams-generator-pd-audiokit/Heavy_Adc2Dac.h +++ /dev/null @@ -1,78 +0,0 @@ -/** - * Copyright (c) 2024 Enzien Audio, Ltd. - * - * Redistribution and use in source and binary forms, with or without modification, - * are permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright notice, - * this list of conditions, and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the phrase "powered by heavy", - * the heavy logo, and a hyperlink to https://enzienaudio.com, all in a visible - * form. - * - * 2.1 If the Application is distributed in a store system (for example, - * the Apple "App Store" or "Google Play"), the phrase "powered by heavy" - * shall be included in the app description or the copyright text as well as - * the in the app itself. The heavy logo will shall be visible in the app - * itself as well. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, - * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR - * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER - * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, - * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF - * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - */ - -#ifndef _HEAVY_ADC2DAC_H_ -#define _HEAVY_ADC2DAC_H_ - -#include "HvHeavy.h" - -#ifdef __cplusplus -extern "C" { -#endif - -#if HV_APPLE -#pragma mark - Heavy Context -#endif - - - -/** - * Creates a new patch instance. - * Sample rate should be positive and in Hertz, e.g. 44100.0. - */ -HeavyContextInterface *hv_Adc2Dac_new(double sampleRate); - -/** - * Creates a new patch instance. - * @param sampleRate Sample rate should be positive (> 0) and in Hertz, e.g. 48000.0. - * @param poolKb Pool size is in kilobytes, and determines the maximum amount of memory - * allocated to messages at any time. By default this is 10 KB. - * @param inQueueKb The size of the input message queue in kilobytes. It determines the - * amount of memory dedicated to holding scheduled messages between calls to - * process(). Default is 2 KB. - * @param outQueueKb The size of the output message queue in kilobytes. It determines the - * amount of memory dedicated to holding scheduled messages to the default sendHook. - * See getNextSentMessage() for info on accessing these messages. Default is 0 KB. - */ -HeavyContextInterface *hv_Adc2Dac_new_with_options(double sampleRate, int poolKb, int inQueueKb, int outQueueKb); - -/** - * Free the patch instance. - */ -void hv_Adc2Dac_free(HeavyContextInterface *instance); - - -#ifdef __cplusplus -} // extern "C" -#endif - -#endif // _HEAVY_ADC2DAC_H_ diff --git a/examples/examples-dsp/examples-pd/streams-generator-pd-audiokit/Heavy_Adc2Dac.hpp b/examples/examples-dsp/examples-pd/streams-generator-pd-audiokit/Heavy_Adc2Dac.hpp deleted file mode 100644 index 37a5f0c563..0000000000 --- a/examples/examples-dsp/examples-pd/streams-generator-pd-audiokit/Heavy_Adc2Dac.hpp +++ /dev/null @@ -1,65 +0,0 @@ -/** - * Copyright (c) 2024 Enzien Audio, Ltd. - * - * Redistribution and use in source and binary forms, with or without modification, - * are permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright notice, - * this list of conditions, and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the phrase "powered by heavy", - * the heavy logo, and a hyperlink to https://enzienaudio.com, all in a visible - * form. - * - * 2.1 If the Application is distributed in a store system (for example, - * the Apple "App Store" or "Google Play"), the phrase "powered by heavy" - * shall be included in the app description or the copyright text as well as - * the in the app itself. The heavy logo will shall be visible in the app - * itself as well. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, - * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR - * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER - * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, - * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF - * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - */ - -#ifndef _HEAVY_CONTEXT_ADC2DAC_HPP_ -#define _HEAVY_CONTEXT_ADC2DAC_HPP_ - -// object includes -#include "HeavyContext.hpp" -#include "HvMath.h" - -class Heavy_Adc2Dac : public HeavyContext { - - public: - Heavy_Adc2Dac(double sampleRate, int poolKb=10, int inQueueKb=2, int outQueueKb=0); - ~Heavy_Adc2Dac(); - - const char *getName() override { return "Adc2Dac"; } - int getNumInputChannels() override { return 2; } - int getNumOutputChannels() override { return 2; } - - int process(float **inputBuffers, float **outputBuffer, int n) override; - int processInline(float *inputBuffers, float *outputBuffer, int n) override; - int processInlineInterleaved(float *inputBuffers, float *outputBuffer, int n) override; - - int getParameterInfo(int index, HvParameterInfo *info) override; - - private: - HvTable *getTableForHash(hv_uint32_t tableHash) override; - void scheduleMessageForReceiver(hv_uint32_t receiverHash, HvMessage *m) override; - - // static sendMessage functions - - // objects -}; - -#endif // _HEAVY_CONTEXT_ADC2DAC_HPP_ diff --git a/examples/examples-dsp/examples-pd/streams-generator-pd-audiokit/HvHeavy.cpp b/examples/examples-dsp/examples-pd/streams-generator-pd-audiokit/HvHeavy.cpp deleted file mode 100644 index 19ca4128d8..0000000000 --- a/examples/examples-dsp/examples-pd/streams-generator-pd-audiokit/HvHeavy.cpp +++ /dev/null @@ -1,318 +0,0 @@ -/** - * Copyright (c) 2014-2018 Enzien Audio Ltd. - * - * Permission to use, copy, modify, and/or distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH - * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY - * AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, - * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM - * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR - * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR - * PERFORMANCE OF THIS SOFTWARE. - */ - -#include "HeavyContext.hpp" - -#ifdef __cplusplus -extern "C" { -#endif - -#if HV_APPLE -#pragma mark - Heavy Table -#endif - -HV_EXPORT bool hv_table_setLength(HeavyContextInterface *c, hv_uint32_t tableHash, hv_uint32_t newSampleLength) { - hv_assert(c != nullptr); - return c->setLengthForTable(tableHash, newSampleLength); -} - -HV_EXPORT float *hv_table_getBuffer(HeavyContextInterface *c, hv_uint32_t tableHash) { - hv_assert(c != nullptr); - return c->getBufferForTable(tableHash); -} - -HV_EXPORT hv_uint32_t hv_table_getLength(HeavyContextInterface *c, hv_uint32_t tableHash) { - hv_assert(c != nullptr); - return c->getLengthForTable(tableHash); -} - - - -#if HV_APPLE -#pragma mark - Heavy Message -#endif - -HV_EXPORT hv_size_t hv_msg_getByteSize(hv_uint32_t numElements) { - return msg_getCoreSize(numElements); -} - -HV_EXPORT void hv_msg_init(HvMessage *m, int numElements, hv_uint32_t timestamp) { - msg_init(m, numElements, timestamp); -} - -HV_EXPORT hv_size_t hv_msg_getNumElements(const HvMessage *m) { - return msg_getNumElements(m); -} - -HV_EXPORT hv_uint32_t hv_msg_getTimestamp(const HvMessage *m) { - return msg_getTimestamp(m); -} - -HV_EXPORT void hv_msg_setTimestamp(HvMessage *m, hv_uint32_t timestamp) { - msg_setTimestamp(m, timestamp); -} - -HV_EXPORT bool hv_msg_isBang(const HvMessage *const m, int i) { - return msg_isBang(m,i); -} - -HV_EXPORT void hv_msg_setBang(HvMessage *m, int i) { - msg_setBang(m,i); -} - -HV_EXPORT bool hv_msg_isFloat(const HvMessage *const m, int i) { - return msg_isFloat(m, i); -} - -HV_EXPORT float hv_msg_getFloat(const HvMessage *const m, int i) { - return msg_getFloat(m,i); -} - -HV_EXPORT void hv_msg_setFloat(HvMessage *m, int i, float f) { - msg_setFloat(m,i,f); -} - -HV_EXPORT bool hv_msg_isSymbol(const HvMessage *const m, int i) { - return msg_isSymbol(m,i); -} - -HV_EXPORT const char *hv_msg_getSymbol(const HvMessage *const m, int i) { - return msg_getSymbol(m,i); -} - -HV_EXPORT void hv_msg_setSymbol(HvMessage *m, int i, const char *s) { - msg_setSymbol(m,i,s); -} - -HV_EXPORT bool hv_msg_isHash(const HvMessage *const m, int i) { - return msg_isHash(m, i); -} - -HV_EXPORT hv_uint32_t hv_msg_getHash(const HvMessage *const m, int i) { - return msg_getHash(m, i); -} - -HV_EXPORT bool hv_msg_hasFormat(const HvMessage *const m, const char *fmt) { - return msg_hasFormat(m, fmt); -} - -HV_EXPORT char *hv_msg_toString(const HvMessage *const m) { - return msg_toString(m); -} - -HV_EXPORT HvMessage *hv_msg_copy(const HvMessage *const m) { - return msg_copy(m); -} - -HV_EXPORT void hv_msg_free(HvMessage *m) { - msg_free(m); -} - - - -#if HV_APPLE -#pragma mark - Heavy Common -#endif - -HV_EXPORT int hv_getSize(HeavyContextInterface *c) { - hv_assert(c != nullptr); - return (int) c->getSize(); -} - -HV_EXPORT double hv_getSampleRate(HeavyContextInterface *c) { - hv_assert(c != nullptr); - return c->getSampleRate(); -} - -HV_EXPORT int hv_getNumInputChannels(HeavyContextInterface *c) { - hv_assert(c != nullptr); - return c->getNumInputChannels(); -} - -HV_EXPORT int hv_getNumOutputChannels(HeavyContextInterface *c) { - hv_assert(c != nullptr); - return c->getNumOutputChannels(); -} - -HV_EXPORT void hv_setPrintHook(HeavyContextInterface *c, HvPrintHook_t *f) { - hv_assert(c != nullptr); - c->setPrintHook(f); -} - -HV_EXPORT HvPrintHook_t *hv_getPrintHook(HeavyContextInterface *c) { - hv_assert(c != nullptr); - return c->getPrintHook(); -} - -HV_EXPORT void hv_setSendHook(HeavyContextInterface *c, HvSendHook_t *f) { - hv_assert(c != nullptr); - c->setSendHook(f); -} - -HV_EXPORT hv_uint32_t hv_stringToHash(const char *s) { - return hv_string_to_hash(s); -} - -HV_EXPORT bool hv_sendBangToReceiver(HeavyContextInterface *c, hv_uint32_t receiverHash) { - hv_assert(c != nullptr); - return c->sendBangToReceiver(receiverHash); -} - -HV_EXPORT bool hv_sendFloatToReceiver(HeavyContextInterface *c, hv_uint32_t receiverHash, float x) { - hv_assert(c != nullptr); - return c->sendFloatToReceiver(receiverHash, x); -} - -HV_EXPORT bool hv_sendSymbolToReceiver(HeavyContextInterface *c, hv_uint32_t receiverHash, char *s) { - hv_assert(c != nullptr); - return c->sendSymbolToReceiver(receiverHash, s); -} - -HV_EXPORT bool hv_sendMessageToReceiverV( - HeavyContextInterface *c, hv_uint32_t receiverHash, double delayMs, const char *format, ...) { - hv_assert(c != nullptr); - hv_assert(delayMs >= 0.0); - hv_assert(format != nullptr); - - va_list ap; - va_start(ap, format); - const int numElem = (int) hv_strlen(format); - HvMessage *m = HV_MESSAGE_ON_STACK(numElem); - msg_init(m, numElem, c->getCurrentSample() + (hv_uint32_t) (hv_max_d(0.0, delayMs)*c->getSampleRate()/1000.0)); - for (int i = 0; i < numElem; i++) { - switch (format[i]) { - case 'b': msg_setBang(m, i); break; - case 'f': msg_setFloat(m, i, (float) va_arg(ap, double)); break; - case 'h': msg_setHash(m, i, (int) va_arg(ap, int)); break; - case 's': msg_setSymbol(m, i, (char *) va_arg(ap, char *)); break; - default: break; - } - } - va_end(ap); - - return c->sendMessageToReceiver(receiverHash, delayMs, m); -} - -HV_EXPORT bool hv_sendMessageToReceiver( - HeavyContextInterface *c, hv_uint32_t receiverHash, double delayMs, HvMessage *m) { - hv_assert(c != nullptr); - return c->sendMessageToReceiver(receiverHash, delayMs, m); -} - -HV_EXPORT void hv_cancelMessage(HeavyContextInterface *c, HvMessage *m, void (*sendMessage)(HeavyContextInterface *, int, const HvMessage *)) { - hv_assert(c != nullptr); - c->cancelMessage(m, sendMessage); -} - -HV_EXPORT const char *hv_getName(HeavyContextInterface *c) { - hv_assert(c != nullptr); - return c->getName(); -} - -HV_EXPORT void hv_setUserData(HeavyContextInterface *c, void *userData) { - hv_assert(c != nullptr); - c->setUserData(userData); -} - -HV_EXPORT void *hv_getUserData(HeavyContextInterface *c) { - hv_assert(c != nullptr); - return c->getUserData(); -} - -HV_EXPORT double hv_getCurrentTime(HeavyContextInterface *c) { - hv_assert(c != nullptr); - return (double) c->samplesToMilliseconds(c->getCurrentSample()); -} - -HV_EXPORT hv_uint32_t hv_getCurrentSample(HeavyContextInterface *c) { - hv_assert(c != nullptr); - return c->getCurrentSample(); -} - -HV_EXPORT float hv_samplesToMilliseconds(HeavyContextInterface *c, hv_uint32_t numSamples) { - hv_assert(c != nullptr); - return c->samplesToMilliseconds(numSamples); -} - -HV_EXPORT hv_uint32_t hv_millisecondsToSamples(HeavyContextInterface *c, float ms) { - hv_assert(c != nullptr); - return c->millisecondsToSamples(ms); -} - -HV_EXPORT int hv_getParameterInfo(HeavyContextInterface *c, int index, HvParameterInfo *info) { - hv_assert(c != nullptr); - return c->getParameterInfo(index, info); -} - -HV_EXPORT void hv_lock_acquire(HeavyContextInterface *c) { - hv_assert(c != nullptr); - c->lockAcquire(); -} - -HV_EXPORT bool hv_lock_try(HeavyContextInterface *c) { - hv_assert(c != nullptr); - return c->lockTry(); -} - -HV_EXPORT void hv_lock_release(HeavyContextInterface *c) { - hv_assert(c != nullptr); - c->lockRelease(); -} - -HV_EXPORT void hv_setInputMessageQueueSize(HeavyContextInterface *c, hv_uint32_t inQueueKb) { - hv_assert(c != nullptr); - c->setInputMessageQueueSize(inQueueKb); -} - -HV_EXPORT void hv_setOutputMessageQueueSize(HeavyContextInterface *c, hv_uint32_t outQueueKb) { - hv_assert(c != nullptr); - c->setOutputMessageQueueSize(outQueueKb); -} - -HV_EXPORT bool hv_getNextSentMessage(HeavyContextInterface *c, hv_uint32_t *destinationHash, HvMessage *outMsg, hv_uint32_t msgLength) { - hv_assert(c != nullptr); - hv_assert(destinationHash != nullptr); - hv_assert(outMsg != nullptr); - return c->getNextSentMessage(destinationHash, outMsg, msgLength); -} - - -#if HV_APPLE -#pragma mark - Heavy Common -#endif - -HV_EXPORT int hv_process(HeavyContextInterface *c, float **inputBuffers, float **outputBuffers, int n) { - hv_assert(c != nullptr); - return c->process(inputBuffers, outputBuffers, n); -} - -HV_EXPORT int hv_processInline(HeavyContextInterface *c, float *inputBuffers, float *outputBuffers, int n) { - hv_assert(c != nullptr); - return c->processInline(inputBuffers, outputBuffers, n); -} - -HV_EXPORT int hv_processInlineInterleaved(HeavyContextInterface *c, float *inputBuffers, float *outputBuffers, int n) { - hv_assert(c != nullptr); - return c->processInlineInterleaved(inputBuffers, outputBuffers, n); -} - -HV_EXPORT void hv_delete(HeavyContextInterface *c) { - delete c; -} - -#ifdef __cplusplus -} -#endif diff --git a/examples/examples-dsp/examples-pd/streams-generator-pd-audiokit/HvHeavy.h b/examples/examples-dsp/examples-pd/streams-generator-pd-audiokit/HvHeavy.h deleted file mode 100644 index cb1aecfa98..0000000000 --- a/examples/examples-dsp/examples-pd/streams-generator-pd-audiokit/HvHeavy.h +++ /dev/null @@ -1,413 +0,0 @@ -/** - * Copyright (c) 2014-2018 Enzien Audio Ltd. - * - * Permission to use, copy, modify, and/or distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH - * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY - * AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, - * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM - * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR - * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR - * PERFORMANCE OF THIS SOFTWARE. - */ - -#ifndef _HEAVY_H_ -#define _HEAVY_H_ - -#include "HvUtils.h" - -#ifdef __cplusplus -extern "C" { -#endif - -#ifndef _HEAVY_DECLARATIONS_ -#define _HEAVY_DECLARATIONS_ - -#ifdef __cplusplus -class HeavyContextInterface; -#else -typedef struct HeavyContextInterface HeavyContextInterface; -#endif - -typedef struct HvMessage HvMessage; - -typedef enum { - HV_PARAM_TYPE_PARAMETER_IN, - HV_PARAM_TYPE_PARAMETER_OUT, - HV_PARAM_TYPE_EVENT_IN, - HV_PARAM_TYPE_EVENT_OUT -} HvParameterType; - -typedef struct HvParameterInfo { - const char *name; // the human readable parameter name - hv_uint32_t hash; // an integer identified used by heavy for this parameter - HvParameterType type; // type of this parameter - float minVal; // the minimum value of this parameter - float maxVal; // the maximum value of this parameter - float defaultVal; // the default value of this parameter -} HvParameterInfo; - -typedef void (HvSendHook_t) (HeavyContextInterface *context, const char *sendName, hv_uint32_t sendHash, const HvMessage *msg); -typedef void (HvPrintHook_t) (HeavyContextInterface *context, const char *printName, const char *str, const HvMessage *msg); - -#endif // _HEAVY_DECLARATIONS_ - - - -#if HV_APPLE -#pragma mark - Heavy Context -#endif - -/** Deletes a patch instance. */ -void hv_delete(HeavyContextInterface *c); - - - -#if HV_APPLE -#pragma mark - Heavy Process -#endif - -/** - * Processes one block of samples for a patch instance. The buffer format is an array of float channel arrays. - * If the context has not input or output channels, the respective argument may be NULL. - * The number of samples to to tbe processed should be a multiple of 1, 4, or 8, depending on if - * no, SSE or NEON, or AVX optimisation is being used, respectively. - * e.g. [[LLLL][RRRR]] - * This function support in-place processing. - * - * @return The number of samples processed. - * - * This function is NOT thread-safe. It is assumed that only the audio thread will execute this function. - */ -int hv_process(HeavyContextInterface *c, float **inputBuffers, float **outputBuffers, int n); - -/** - * Processes one block of samples for a patch instance. The buffer format is an uninterleaved float array of channels. - * If the context has not input or output channels, the respective argument may be NULL. - * The number of samples to to tbe processed should be a multiple of 1, 4, or 8, depending on if - * no, SSE or NEON, or AVX optimisation is being used, respectively. - * e.g. [LLLLRRRR] - * This function support in-place processing. - * - * @return The number of samples processed. - * - * This function is NOT thread-safe. It is assumed that only the audio thread will execute this function. - */ -int hv_processInline(HeavyContextInterface *c, float *inputBuffers, float *outputBuffers, int n); - -/** - * Processes one block of samples for a patch instance. The buffer format is an interleaved float array of channels. - * If the context has not input or output channels, the respective argument may be NULL. - * The number of samples to to tbe processed should be a multiple of 1, 4, or 8, depending on if - * no, SSE or NEON, or AVX optimisation is being used, respectively. - * e.g. [LRLRLRLR] - * This function support in-place processing. - * - * @return The number of samples processed. - * - * This function is NOT thread-safe. It is assumed that only the audio thread will execute this function. - */ -int hv_processInlineInterleaved(HeavyContextInterface *c, float *inputBuffers, float *outputBuffers, int n); - - - -#if HV_APPLE -#pragma mark - Heavy Common -#endif - -/** - * Returns the total size in bytes of the context. - * This value may change if tables are resized. - */ -int hv_getSize(HeavyContextInterface *c); - -/** Returns the sample rate with which this context has been configured. */ -double hv_getSampleRate(HeavyContextInterface *c); - -/** Returns the number of input channels with which this context has been configured. */ -int hv_getNumInputChannels(HeavyContextInterface *c); - -/** Returns the number of output channels with which this context has been configured. */ -int hv_getNumOutputChannels(HeavyContextInterface *c); - -/** Set the print hook. The function is called whenever a message is sent to a print object. */ -void hv_setPrintHook(HeavyContextInterface *c, HvPrintHook_t *f); - -/** Returns the print hook, or NULL. */ -HvPrintHook_t *hv_getPrintHook(HeavyContextInterface *c); - -/** - * Set the send hook. The function is called whenever a message is sent to any send object. - * Messages returned by this function should NEVER be freed. If the message must persist, call - * hv_msg_copy() first. - */ -void hv_setSendHook(HeavyContextInterface *c, HvSendHook_t *f); - -/** Returns a 32-bit hash of any string. Returns 0 if string is NULL. */ -hv_uint32_t hv_stringToHash(const char *s); - -/** - * A convenience function to send a bang to a receiver to be processed immediately. - * The receiver is addressed with its hash, which can also be determined using hv_stringToHash(). - * This function is thread-safe. - * - * @return True if the message was accepted. False if the message could not fit onto - * the message queue to be processed this block. - */ -bool hv_sendBangToReceiver(HeavyContextInterface *c, hv_uint32_t receiverHash); - -/** - * A convenience function to send a float to a receiver to be processed immediately. - * The receiver is addressed with its hash, which can also be determined using hv_stringToHash(). - * This function is thread-safe. - * - * @return True if the message was accepted. False if the message could not fit onto - * the message queue to be processed this block. - */ -bool hv_sendFloatToReceiver(HeavyContextInterface *c, hv_uint32_t receiverHash, const float x); - -/** - * A convenience function to send a symbol to a receiver to be processed immediately. - * The receiver is addressed with its hash, which can also be determined using hv_stringToHash(). - * This function is thread-safe. - * - * @return True if the message was accepted. False if the message could not fit onto - * the message queue to be processed this block. - */ -bool hv_sendSymbolToReceiver(HeavyContextInterface *c, hv_uint32_t receiverHash, char *s); - -/** - * Sends a formatted message to a receiver that can be scheduled for the future. - * The receiver is addressed with its hash, which can also be determined using hv_stringToHash(). - * This function is thread-safe. - * - * @return True if the message was accepted. False if the message could not fit onto - * the message queue to be processed this block. - */ -bool hv_sendMessageToReceiverV(HeavyContextInterface *c, hv_uint32_t receiverHash, double delayMs, const char *format, ...); - -/** - * Sends a message to a receiver that can be scheduled for the future. - * The receiver is addressed with its hash, which can also be determined using hv_stringToHash(). - * This function is thread-safe. - * - * @return True if the message was accepted. False if the message could not fit onto - * the message queue to be processed this block. - */ -bool hv_sendMessageToReceiver(HeavyContextInterface *c, hv_uint32_t receiverHash, double delayMs, HvMessage *m); - -/** - * Cancels a previously scheduled message. - * - * @param sendMessage May be NULL. - */ -void hv_cancelMessage(HeavyContextInterface *c, HvMessage *m, void (*sendMessage)(HeavyContextInterface *, int, const HvMessage *)); - -/** Returns the read-only user-assigned name of this patch. */ -const char *hv_getName(HeavyContextInterface *c); - -/** Sets a user-definable value. This value is never manipulated by Heavy. */ -void hv_setUserData(HeavyContextInterface *c, void *userData); - -/** Returns the user-defined data. */ -void *hv_getUserData(HeavyContextInterface *c); - -/** Returns the current patch time in milliseconds. This value may have rounding errors. */ -double hv_getCurrentTime(HeavyContextInterface *c); - -/** Returns the current patch time in samples. This value is always exact. */ -hv_uint32_t hv_getCurrentSample(HeavyContextInterface *c); - -/** - * Returns information about each parameter such as name, hash, and range. - * The total number of parameters is always returned. - * - * @param index The parameter index. - * @param info A pointer to a HvParameterInfo struct. May be null. - * - * @return The total number of parameters. - */ -int hv_getParameterInfo(HeavyContextInterface *c, int index, HvParameterInfo *info); - -/** */ -float hv_samplesToMilliseconds(HeavyContextInterface *c, hv_uint32_t numSamples); - -/** Converts milliseconds to samples. Input is limited to non-negative range. */ -hv_uint32_t hv_millisecondsToSamples(HeavyContextInterface *c, float ms); - -/** - * Acquire the input message queue lock. - * - * This function will block until the message lock as been acquired. - * Typical applications will not require the use of this function. - * - * @param c A Heavy context. - */ -void hv_lock_acquire(HeavyContextInterface *c); - -/** - * Try to acquire the input message queue lock. - * - * If the lock has been acquired, hv_lock_release() must be called to release it. - * Typical applications will not require the use of this function. - * - * @param c A Heavy context. - * - * @return Returns true if the lock has been acquired, false otherwise. - */ -bool hv_lock_try(HeavyContextInterface *c); - -/** - * Release the input message queue lock. - * - * Typical applications will not require the use of this function. - * - * @param c A Heavy context. - */ -void hv_lock_release(HeavyContextInterface *c); - -/** - * Set the size of the input message queue in kilobytes. - * - * The buffer is reset and all existing contents are lost on resize. - * - * @param c A Heavy context. - * @param inQueueKb Must be positive i.e. at least one. - */ -void hv_setInputMessageQueueSize(HeavyContextInterface *c, hv_uint32_t inQueueKb); - -/** - * Set the size of the output message queue in kilobytes. - * - * The buffer is reset and all existing contents are lost on resize. - * Only the default sendhook uses the outgoing message queue. If the default - * sendhook is not being used, then this function is not useful. - * - * @param c A Heavy context. - * @param outQueueKb Must be postive i.e. at least one. - */ -void hv_setOutputMessageQueueSize(HeavyContextInterface *c, hv_uint32_t outQueueKb); - -/** - * Get the next message in the outgoing queue, will also consume the message. - * Returns false if there are no messages. - * - * @param c A Heavy context. - * @param destinationHash a hash of the name of the receiver the message was sent to. - * @param outMsg message pointer that is filled by the next message contents. - * @param msgLength length of outMsg in bytes. - * - * @return True if there is a message in the outgoing queue. -*/ -bool hv_getNextSentMessage(HeavyContextInterface *c, hv_uint32_t *destinationHash, HvMessage *outMsg, hv_uint32_t msgLength); - - - -#if HV_APPLE -#pragma mark - Heavy Message -#endif - -typedef struct HvMessage HvMessage; - -/** Returns the total size in bytes of a HvMessage with a number of elements on the heap. */ -unsigned long hv_msg_getByteSize(hv_uint32_t numElements); - -/** Initialise a HvMessage structure with the number of elements and a timestamp (in samples). */ -void hv_msg_init(HvMessage *m, int numElements, hv_uint32_t timestamp); - -/** Returns the number of elements in this message. */ -unsigned long hv_msg_getNumElements(const HvMessage *m); - -/** Returns the time at which this message exists (in samples). */ -hv_uint32_t hv_msg_getTimestamp(const HvMessage *m); - -/** Set the time at which this message should be executed (in samples). */ -void hv_msg_setTimestamp(HvMessage *m, hv_uint32_t timestamp); - -/** Returns true of the indexed element is a bang. False otherwise. Index is not bounds checked. */ -bool hv_msg_isBang(const HvMessage *const m, int i); - -/** Sets the indexed element to a bang. Index is not bounds checked. */ -void hv_msg_setBang(HvMessage *m, int i); - -/** Returns true of the indexed element is a float. False otherwise. Index is not bounds checked. */ -bool hv_msg_isFloat(const HvMessage *const m, int i); - -/** Returns the indexed element as a float value. Index is not bounds checked. */ -float hv_msg_getFloat(const HvMessage *const m, int i); - -/** Sets the indexed element to float value. Index is not bounds checked. */ -void hv_msg_setFloat(HvMessage *m, int i, float f); - -/** Returns true of the indexed element is a symbol. False otherwise. Index is not bounds checked. */ -bool hv_msg_isSymbol(const HvMessage *const m, int i); - -/** Returns the indexed element as a symbol value. Index is not bounds checked. */ -const char *hv_msg_getSymbol(const HvMessage *const m, int i); - -/** Returns true of the indexed element is a hash. False otherwise. Index is not bounds checked. */ -bool hv_msg_isHash(const HvMessage *const m, int i); - -/** Returns the indexed element as a hash value. Index is not bounds checked. */ -hv_uint32_t hv_msg_getHash(const HvMessage *const m, int i); - -/** Sets the indexed element to symbol value. Index is not bounds checked. */ -void hv_msg_setSymbol(HvMessage *m, int i, const char *s); - -/** - * Returns true if the message has the given format, in number of elements and type. False otherwise. - * Valid element types are: - * 'b': bang - * 'f': float - * 's': symbol - * - * For example, a message with three floats would have a format of "fff". A single bang is "b". - * A message with two symbols is "ss". These types can be mixed and matched in any way. - */ -bool hv_msg_hasFormat(const HvMessage *const m, const char *fmt); - -/** - * Returns a basic string representation of the message. - * The character array MUST be deallocated by the caller. - */ -char *hv_msg_toString(const HvMessage *const m); - -/** Copy a message onto the stack. The message persists. */ -HvMessage *hv_msg_copy(const HvMessage *const m); - -/** Free a copied message. */ -void hv_msg_free(HvMessage *m); - - - -#if HV_APPLE -#pragma mark - Heavy Table -#endif - -/** - * Resizes the table to the given length. - * - * Existing contents are copied to the new table. Remaining space is cleared - * if the table is longer than the original, truncated otherwise. - * - * @param tableHash The table identifier. - * @param newSampleLength The new length of the table, in samples. Must be positive. - * - * @return False if the table could not be found. True otherwise. - */ -bool hv_table_setLength(HeavyContextInterface *c, hv_uint32_t tableHash, hv_uint32_t newSampleLength); - -/** Returns a pointer to the raw buffer backing this table. DO NOT free it. */ -float *hv_table_getBuffer(HeavyContextInterface *c, hv_uint32_t tableHash); - -/** Returns the length of this table in samples. */ -hv_uint32_t hv_table_getLength(HeavyContextInterface *c, hv_uint32_t tableHash); - -#ifdef __cplusplus -} // extern "C" -#endif - -#endif // _HEAVY_H_ diff --git a/examples/examples-dsp/examples-pd/streams-generator-pd-audiokit/HvHeavyInternal.h b/examples/examples-dsp/examples-pd/streams-generator-pd-audiokit/HvHeavyInternal.h deleted file mode 100644 index e10b944410..0000000000 --- a/examples/examples-dsp/examples-pd/streams-generator-pd-audiokit/HvHeavyInternal.h +++ /dev/null @@ -1,51 +0,0 @@ -/** - * Copyright (c) 2014-2018 Enzien Audio Ltd. - * - * Permission to use, copy, modify, and/or distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH - * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY - * AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, - * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM - * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR - * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR - * PERFORMANCE OF THIS SOFTWARE. - */ - -#ifndef _HEAVY_INTERNAL_H_ -#define _HEAVY_INTERNAL_H_ - -#include "HvHeavy.h" -#include "HvUtils.h" -#include "HvTable.h" -#include "HvMessage.h" -#include "HvMath.h" - -#ifdef __cplusplus -extern "C" { -#endif - -/** - * - */ -HvTable *hv_table_get(HeavyContextInterface *c, hv_uint32_t tableHash); - -/** - * - */ -void hv_scheduleMessageForReceiver(HeavyContextInterface *c, hv_uint32_t receiverHash, HvMessage *m); - -/** - * - */ -HvMessage *hv_scheduleMessageForObject(HeavyContextInterface *c, const HvMessage *m, - void (*sendMessage)(HeavyContextInterface *, int, const HvMessage *), - int letIndex); - -#ifdef __cplusplus -} -#endif - -#endif diff --git a/examples/examples-dsp/examples-pd/streams-generator-pd-audiokit/HvLightPipe.c b/examples/examples-dsp/examples-pd/streams-generator-pd-audiokit/HvLightPipe.c deleted file mode 100644 index 98de428312..0000000000 --- a/examples/examples-dsp/examples-pd/streams-generator-pd-audiokit/HvLightPipe.c +++ /dev/null @@ -1,136 +0,0 @@ -/** - * Copyright (c) 2014-2018 Enzien Audio Ltd. - * - * Permission to use, copy, modify, and/or distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH - * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY - * AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, - * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM - * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR - * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR - * PERFORMANCE OF THIS SOFTWARE. - */ - -#include "HvLightPipe.h" - -#if __SSE__ || HV_SIMD_SSE -#include -#define hv_sfence() _mm_sfence() -#elif __arm__ || HV_SIMD_NEON - #if __ARM_ACLE - #include - // https://msdn.microsoft.com/en-us/library/hh875058.aspx#BarrierRestrictions - // http://doxygen.reactos.org/d8/d47/armintr_8h_a02be7ec76ca51842bc90d9b466b54752.html - #define hv_sfence() __dmb(0xE) /* _ARM_BARRIER_ST */ - #elif defined(__GNUC__) - #define hv_sfence() __asm__ volatile ("dmb 0xE":::"memory") - #else - // http://stackoverflow.com/questions/19965076/gcc-memory-barrier-sync-synchronize-vs-asm-volatile-memory - #define hv_sfence() __sync_synchronize() - #endif -#elif HV_WIN -// https://msdn.microsoft.com/en-us/library/windows/desktop/ms684208(v=vs.85).aspx -#define hv_sfence() _WriteBarrier() -#else -#define hv_sfence() __asm__ volatile("" : : : "memory") -#endif - -#define HLP_STOP 0 -#define HLP_LOOP 0xFFFFFFFF -#define HLP_SET_UINT32_AT_BUFFER(a, b) (*((hv_uint32_t *) (a)) = (b)) -#define HLP_GET_UINT32_AT_BUFFER(a) (*((hv_uint32_t *) (a))) - -hv_uint32_t hLp_init(HvLightPipe *q, hv_uint32_t numBytes) { - if (numBytes > 0) { - q->buffer = (char *) hv_malloc(numBytes); - hv_assert(q->buffer != NULL); - HLP_SET_UINT32_AT_BUFFER(q->buffer, HLP_STOP); - } else { - q->buffer = NULL; - } - q->writeHead = q->buffer; - q->readHead = q->buffer; - q->len = numBytes; - q->remainingBytes = numBytes; - return numBytes; -} - -void hLp_free(HvLightPipe *q) { - hv_free(q->buffer); -} - -hv_uint32_t hLp_hasData(HvLightPipe *q) { - hv_uint32_t x = HLP_GET_UINT32_AT_BUFFER(q->readHead); - if (x == HLP_LOOP) { - q->readHead = q->buffer; - x = HLP_GET_UINT32_AT_BUFFER(q->readHead); - } - return x; -} - -char *hLp_getWriteBuffer(HvLightPipe *q, hv_uint32_t bytesToWrite) { - char *const readHead = q->readHead; - char *const oldWriteHead = q->writeHead; - const hv_uint32_t totalByteRequirement = bytesToWrite + 2*sizeof(hv_uint32_t); - - // check if there is enough space to write the data in the remaining - // length of the buffer - if (totalByteRequirement <= q->remainingBytes) { - char *const newWriteHead = oldWriteHead + sizeof(hv_uint32_t) + bytesToWrite; - - // check if writing would overwrite existing data in the pipe (return NULL if so) - if ((oldWriteHead < readHead) && (newWriteHead >= readHead)) return NULL; - else return (oldWriteHead + sizeof(hv_uint32_t)); - } else { - // there isn't enough space, try looping around to the start - if (totalByteRequirement <= q->len) { - if ((oldWriteHead < readHead) || ((q->buffer + totalByteRequirement) > readHead)) { - return NULL; // overwrite condition - } else { - q->writeHead = q->buffer; - q->remainingBytes = q->len; - HLP_SET_UINT32_AT_BUFFER(q->buffer, HLP_STOP); - hv_sfence(); - HLP_SET_UINT32_AT_BUFFER(oldWriteHead, HLP_LOOP); - return q->buffer + sizeof(hv_uint32_t); - } - } else { - return NULL; // there isn't enough space to write the data - } - } -} - -void hLp_produce(HvLightPipe *q, hv_uint32_t numBytes) { - hv_assert(q->remainingBytes >= (numBytes + 2*sizeof(hv_uint32_t))); - q->remainingBytes -= (sizeof(hv_uint32_t) + numBytes); - char *const oldWriteHead = q->writeHead; - q->writeHead += (sizeof(hv_uint32_t) + numBytes); - HLP_SET_UINT32_AT_BUFFER(q->writeHead, HLP_STOP); - - // save everything before this point to memory - hv_sfence(); - - // then save this - HLP_SET_UINT32_AT_BUFFER(oldWriteHead, numBytes); -} - -char *hLp_getReadBuffer(HvLightPipe *q, hv_uint32_t *numBytes) { - *numBytes = HLP_GET_UINT32_AT_BUFFER(q->readHead); - char *const readBuffer = q->readHead + sizeof(hv_uint32_t); - return readBuffer; -} - -void hLp_consume(HvLightPipe *q) { - hv_assert(HLP_GET_UINT32_AT_BUFFER(q->readHead) != HLP_STOP); - q->readHead += sizeof(hv_uint32_t) + HLP_GET_UINT32_AT_BUFFER(q->readHead); -} - -void hLp_reset(HvLightPipe *q) { - q->writeHead = q->buffer; - q->readHead = q->buffer; - q->remainingBytes = q->len; - memset(q->buffer, 0, q->len); -} diff --git a/examples/examples-dsp/examples-pd/streams-generator-pd-audiokit/HvLightPipe.h b/examples/examples-dsp/examples-pd/streams-generator-pd-audiokit/HvLightPipe.h deleted file mode 100644 index 438f7261dc..0000000000 --- a/examples/examples-dsp/examples-pd/streams-generator-pd-audiokit/HvLightPipe.h +++ /dev/null @@ -1,104 +0,0 @@ -/** - * Copyright (c) 2014-2018 Enzien Audio Ltd. - * - * Permission to use, copy, modify, and/or distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH - * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY - * AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, - * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM - * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR - * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR - * PERFORMANCE OF THIS SOFTWARE. - */ - -#ifndef _HEAVY_LIGHTPIPE_H_ -#define _HEAVY_LIGHTPIPE_H_ - -#include "HvUtils.h" - -#ifdef __cplusplus -extern "C" { -#endif - -/* - * This pipe assumes that there is only one producer thread and one consumer - * thread. This data structure does not support any other configuration. - */ -typedef struct HvLightPipe { - char *buffer; - char *writeHead; - char *readHead; - hv_uint32_t len; - hv_uint32_t remainingBytes; // total bytes from write head to end -} HvLightPipe; - -/** - * Initialise the pipe with a given length, in bytes. - * @return Returns the size of the pipe in bytes. - */ -hv_uint32_t hLp_init(HvLightPipe *q, hv_uint32_t numBytes); - -/** - * Frees the internal buffer. - * @param q The light pipe. - */ -void hLp_free(HvLightPipe *q); - -/** - * Indicates if data is available for reading. - * @param q The light pipe. - * - * @return Returns the number of bytes available for reading. Zero if no bytes - * are available. - */ -hv_uint32_t hLp_hasData(HvLightPipe *q); - -/** - * Returns a pointer to a location in the pipe where numBytes can be written. - * - * @param numBytes The number of bytes to be written. - * @return A pointer to a location where those bytes can be written. Returns - * NULL if no more space is available. Successive calls to this - * function may eventually return a valid pointer because the readhead - * has been advanced on another thread. - */ -char *hLp_getWriteBuffer(HvLightPipe *q, hv_uint32_t numBytes); - -/** - * Indicates to the pipe how many bytes have been written. - * - * @param numBytes The number of bytes written. In general this should be the - * same value as was passed to the preceeding call to - * hLp_getWriteBuffer(). - */ -void hLp_produce(HvLightPipe *q, hv_uint32_t numBytes); - -/** - * Returns the current read buffer, indicating the number of bytes available - * for reading. - * @param q The light pipe. - * @param numBytes This value will be filled with the number of bytes available - * for reading. - * - * @return A pointer to the read buffer. - */ -char *hLp_getReadBuffer(HvLightPipe *q, hv_uint32_t *numBytes); - -/** - * Indicates that the next set of bytes have been read and are no longer needed. - * @param q The light pipe. - */ -void hLp_consume(HvLightPipe *q); - -// resets the queue to it's initialised state -// This should be done when only one thread is accessing the pipe. -void hLp_reset(HvLightPipe *q); - -#ifdef __cplusplus -} -#endif - -#endif // _HEAVY_LIGHTPIPE_H_ diff --git a/examples/examples-dsp/examples-pd/streams-generator-pd-audiokit/HvMath.h b/examples/examples-dsp/examples-pd/streams-generator-pd-audiokit/HvMath.h deleted file mode 100644 index d25de2df98..0000000000 --- a/examples/examples-dsp/examples-pd/streams-generator-pd-audiokit/HvMath.h +++ /dev/null @@ -1,736 +0,0 @@ -/** - * Copyright (c) 2014-2018 Enzien Audio Ltd. - * - * Permission to use, copy, modify, and/or distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH - * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY - * AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, - * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM - * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR - * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR - * PERFORMANCE OF THIS SOFTWARE. - */ - -#ifndef _HEAVY_MATH_H_ -#define _HEAVY_MATH_H_ - -#include "HvUtils.h" - -// https://software.intel.com/sites/landingpage/IntrinsicsGuide/ -// https://gcc.gnu.org/onlinedocs/gcc-4.8.1/gcc/ARM-NEON-Intrinsics.html -// http://codesuppository.blogspot.co.uk/2015/02/sse2neonh-porting-guide-and-header-file.html - -static inline void __hv_zero_f(hv_bOutf_t bOut) { -#if HV_SIMD_AVX - *bOut = _mm256_setzero_ps(); -#elif HV_SIMD_SSE - *bOut = _mm_setzero_ps(); -#elif HV_SIMD_NEON - *bOut = vdupq_n_f32(0.0f); -#else // HV_SIMD_NONE - *bOut = 0.0f; -#endif -} - -static inline void __hv_zero_i(hv_bOuti_t bOut) { -#if HV_SIMD_AVX - *bOut = _mm256_setzero_si256(); -#elif HV_SIMD_SSE - *bOut = _mm_setzero_si128(); -#elif HV_SIMD_NEON - *bOut = vdupq_n_s32(0); -#else // HV_SIMD_NONE - *bOut = 0; -#endif -} - -static inline void __hv_load_f(float *bIn, hv_bOutf_t bOut) { -#if HV_SIMD_AVX - *bOut = _mm256_load_ps(bIn); -#elif HV_SIMD_SSE - *bOut = _mm_load_ps(bIn); -#elif HV_SIMD_NEON - *bOut = vld1q_f32(bIn); -#else // HV_SIMD_NONE - *bOut = *bIn; -#endif -} - -static inline void __hv_store_f(float *bOut, hv_bInf_t bIn) { -#if HV_SIMD_AVX - _mm256_store_ps(bOut, bIn); -#elif HV_SIMD_SSE - _mm_store_ps(bOut, bIn); -#elif HV_SIMD_NEON - vst1q_f32(bOut, bIn); -#else // HV_SIMD_NONE - *bOut = bIn; -#endif -} - -static inline void __hv_log2_f(hv_bInf_t bIn, hv_bOutf_t bOut) { -#if HV_SIMD_AVX - hv_assert(0); // __hv_log2_f() not implemented -#elif HV_SIMD_SSE - // https://en.wikipedia.org/wiki/Fast_inverse_square_root - __m128i a = _mm_castps_si128(bIn); - __m128i b = _mm_srli_epi32(a, 23); - __m128i c = _mm_sub_epi32(b, _mm_set1_epi32(127)); // exponent (int) - __m128 d = _mm_cvtepi32_ps(c); // exponent (float) - __m128i e = _mm_or_si128(_mm_andnot_si128(_mm_set1_epi32(0xFF800000), a), _mm_set1_epi32(0x3F800000)); - __m128 f = _mm_castsi128_ps(e); // 1+m (float) - __m128 g = _mm_add_ps(d, f); // e + 1 + m - __m128 h = _mm_add_ps(g, _mm_set1_ps(-0.9569643f)); // e + 1 + m + (sigma-1) - *bOut = h; -#elif HV_SIMD_NEON - int32x4_t a = vreinterpretq_s32_f32(bIn); - int32x4_t b = vshrq_n_s32(a, 23); - int32x4_t c = vsubq_s32(b, vdupq_n_s32(127)); - float32x4_t d = vcvtq_f32_s32(c); - int32x4_t e = vorrq_s32(vbicq_s32(a, vdupq_n_s32(0xFF800000)), vdupq_n_s32(0x3F800000)); - float32x4_t f = vreinterpretq_f32_s32(e); - float32x4_t g = vaddq_f32(d, f); - float32x4_t h = vaddq_f32(g, vdupq_n_f32(-0.9569643f)); - *bOut = h; -#else // HV_SIMD_NONE - *bOut = 1.442695040888963f * hv_log_f(bIn); -#endif -} - -// NOTE(mhroth): this is a pretty ghetto implementation -static inline void __hv_cos_f(hv_bInf_t bIn, hv_bOutf_t bOut) { -#if HV_SIMD_AVX - *bOut = _mm256_set_ps( - hv_cos_f(bIn[7]), hv_cos_f(bIn[6]), hv_cos_f(bIn[5]), hv_cos_f(bIn[4]), - hv_cos_f(bIn[3]), hv_cos_f(bIn[2]), hv_cos_f(bIn[1]), hv_cos_f(bIn[0])); -#elif HV_SIMD_SSE - const float *const b = (float *) &bIn; - *bOut = _mm_set_ps(hv_cos_f(b[3]), hv_cos_f(b[2]), hv_cos_f(b[1]), hv_cos_f(b[0])); -#elif HV_SIMD_NEON - *bOut = (float32x4_t) {hv_cos_f(bIn[0]), hv_cos_f(bIn[1]), hv_cos_f(bIn[2]), hv_cos_f(bIn[3])}; -#else // HV_SIMD_NONE - *bOut = hv_cos_f(bIn); -#endif -} - -static inline void __hv_acos_f(hv_bInf_t bIn, hv_bOutf_t bOut) { -#if HV_SIMD_AVX - hv_assert(0); // __hv_acos_f() not implemented -#elif HV_SIMD_SSE - hv_assert(0); // __hv_acos_f() not implemented -#elif HV_SIMD_NEON - hv_assert(0); // __hv_acos_f() not implemented -#else // HV_SIMD_NONE - *bOut = hv_acos_f(bIn); -#endif -} - -static inline void __hv_cosh_f(hv_bInf_t bIn, hv_bOutf_t bOut) { -#if HV_SIMD_AVX - hv_assert(0); // __hv_cosh_f() not implemented -#elif HV_SIMD_SSE - hv_assert(0); // __hv_cosh_f() not implemented -#elif HV_SIMD_NEON - hv_assert(0); // __hv_cosh_f() not implemented -#else // HV_SIMD_NONE - *bOut = hv_cosh_f(bIn); -#endif -} - -static inline void __hv_acosh_f(hv_bInf_t bIn, hv_bOutf_t bOut) { -#if HV_SIMD_AVX - hv_assert(0); // __hv_acosh_f() not implemented -#elif HV_SIMD_SSE - hv_assert(0); // __hv_acosh_f() not implemented -#elif HV_SIMD_NEON - hv_assert(0); // __hv_acosh_f() not implemented -#else // HV_SIMD_NONE - *bOut = hv_acosh_f(bIn); -#endif -} - -static inline void __hv_sin_f(hv_bInf_t bIn, hv_bOutf_t bOut) { -#if HV_SIMD_AVX - hv_assert(0); // __hv_sin_f() not implemented -#elif HV_SIMD_SSE - hv_assert(0); // __hv_sin_f() not implemented -#elif HV_SIMD_NEON - hv_assert(0); // __hv_sin_f() not implemented -#else // HV_SIMD_NONE - *bOut = hv_sin_f(bIn); -#endif -} - -static inline void __hv_asin_f(hv_bInf_t bIn, hv_bOutf_t bOut) { -#if HV_SIMD_AVX - hv_assert(0); // __hv_asin_f() not implemented -#elif HV_SIMD_SSE - hv_assert(0); // __hv_asin_f() not implemented -#elif HV_SIMD_NEON - hv_assert(0); // __hv_asin_f() not implemented -#else // HV_SIMD_NONE - *bOut = hv_asin_f(bIn); -#endif -} - -static inline void __hv_sinh_f(hv_bInf_t bIn, hv_bOutf_t bOut) { -#if HV_SIMD_AVX - hv_assert(0); // __hv_sinh_f() not implemented -#elif HV_SIMD_SSE - hv_assert(0); // __hv_sinh_f() not implemented -#elif HV_SIMD_NEON - hv_assert(0); // __hv_sinh_f() not implemented -#else // HV_SIMD_NONE - *bOut = hv_sinh_f(bIn); -#endif -} - -static inline void __hv_asinh_f(hv_bInf_t bIn, hv_bOutf_t bOut) { -#if HV_SIMD_AVX - hv_assert(0); // __hv_asinh_f() not implemented -#elif HV_SIMD_SSE - hv_assert(0); // __hv_asinh_f() not implemented -#elif HV_SIMD_NEON - hv_assert(0); // __hv_asinh_f() not implemented -#else // HV_SIMD_NONE - *bOut = hv_asinh_f(bIn); -#endif -} - -static inline void __hv_tan_f(hv_bInf_t bIn, hv_bOutf_t bOut) { -#if HV_SIMD_AVX - hv_assert(0); // __hv_tan_f() not implemented -#elif HV_SIMD_SSE - hv_assert(0); // __hv_tan_f() not implemented -#elif HV_SIMD_NEON - hv_assert(0); // __hv_tan_f() not implemented -#else // HV_SIMD_NONE - *bOut = hv_tan_f(bIn); -#endif -} - -static inline void __hv_atan_f(hv_bInf_t bIn, hv_bOutf_t bOut) { -#if HV_SIMD_AVX - hv_assert(0); // __hv_atan_f() not implemented -#elif HV_SIMD_SSE - hv_assert(0); // __hv_atan_f() not implemented -#elif HV_SIMD_NEON - hv_assert(0); // __hv_atan_f() not implemented -#else // HV_SIMD_NONE - *bOut = hv_atan_f(bIn); -#endif -} - -static inline void __hv_atan2_f(hv_bInf_t bIn0, hv_bInf_t bIn1, hv_bOutf_t bOut) { -#if HV_SIMD_AVX - hv_assert(0); // __hv_atan2_f() not implemented -#elif HV_SIMD_SSE - hv_assert(0); // __hv_atan2_f() not implemented -#elif HV_SIMD_NEON - hv_assert(0); // __hv_atan2_f() not implemented -#else // HV_SIMD_NONE - *bOut = hv_atan2_f(bIn0, bIn1); -#endif -} - -static inline void __hv_tanh_f(hv_bInf_t bIn, hv_bOutf_t bOut) { -#if HV_SIMD_AVX - hv_assert(0); // __hv_tanh_f() not implemented -#elif HV_SIMD_SSE - hv_assert(0); // __hv_tanh_f() not implemented -#elif HV_SIMD_NEON - hv_assert(0); // __hv_tanh_f() not implemented -#else // HV_SIMD_NONE - *bOut = hv_tanh_f(bIn); -#endif -} - -static inline void __hv_atanh_f(hv_bInf_t bIn, hv_bOutf_t bOut) { -#if HV_SIMD_AVX - hv_assert(0); // __hv_atanh_f() not implemented -#elif HV_SIMD_SSE - hv_assert(0); // __hv_atanh_f() not implemented -#elif HV_SIMD_NEON - hv_assert(0); // __hv_atanh_f() not implemented -#else // HV_SIMD_NONE - *bOut = hv_atanh_f(bIn); -#endif -} - -static inline void __hv_sqrt_f(hv_bInf_t bIn, hv_bOutf_t bOut) { -#if HV_SIMD_AVX - *bOut = _mm256_sqrt_ps(bIn); -#elif HV_SIMD_SSE - *bOut = _mm_sqrt_ps(bIn); -#elif HV_SIMD_NEON - const float32x4_t y = vrsqrteq_f32(bIn); - *bOut = vmulq_f32(bIn, vmulq_f32(vrsqrtsq_f32(vmulq_f32(bIn, y), y), y)); // numerical results may be inexact -#else // HV_SIMD_NONE - *bOut = hv_sqrt_f(bIn); -#endif -} - -static inline void __hv_rsqrt_f(hv_bInf_t bIn, hv_bOutf_t bOut) { -#if HV_SIMD_AVX - *bOut = _mm256_rsqrt_ps(bIn); -#elif HV_SIMD_SSE - *bOut = _mm_rsqrt_ps(bIn); -#elif HV_SIMD_NEON - const float32x4_t y = vrsqrteq_f32(bIn); - *bOut = vmulq_f32(vrsqrtsq_f32(vmulq_f32(bIn, y), y), y); // numerical results may be inexact -#else // HV_SIMD_NONE - *bOut = 1.0f/hv_sqrt_f(bIn); -#endif -} - -static inline void __hv_abs_f(hv_bInf_t bIn, hv_bOutf_t bOut) { -#if HV_SIMD_AVX - *bOut = _mm256_andnot_ps(_mm256_set1_ps(-0.0f), bIn); -#elif HV_SIMD_SSE - *bOut = _mm_andnot_ps(_mm_set1_ps(-0.0f), bIn); // == 1 << 31 -#elif HV_SIMD_NEON - *bOut = vabsq_f32(bIn); -#else // HV_SIMD_NONE - *bOut = hv_abs_f(bIn); -#endif -} - -static inline void __hv_neg_f(hv_bInf_t bIn, hv_bOutf_t bOut) { -#if HV_SIMD_AVX - *bOut = _mm256_xor_ps(bIn, _mm256_set1_ps(-0.0f)); -#elif HV_SIMD_SSE - *bOut = _mm_xor_ps(bIn, _mm_set1_ps(-0.0f)); -#elif HV_SIMD_NEON - *bOut = vnegq_f32(bIn); -#else // HV_SIMD_NONE - *bOut = bIn * -1.0f; -#endif -} - -static inline void __hv_exp_f(hv_bInf_t bIn, hv_bOutf_t bOut) { -#if HV_SIMD_AVX - float *const b = (float *) hv_alloca(HV_N_SIMD*sizeof(float)); - _mm256_store_ps(b, bIn); - *bOut = _mm256_set_ps( - hv_exp_f(b[7]), hv_exp_f(b[6]), hv_exp_f(b[5]), hv_exp_f(b[4]), - hv_exp_f(b[3]), hv_exp_f(b[2]), hv_exp_f(b[1]), hv_exp_f(b[0])); -#elif HV_SIMD_SSE - float *const b = (float *) hv_alloca(HV_N_SIMD*sizeof(float)); - _mm_store_ps(b, bIn); - *bOut = _mm_set_ps(hv_exp_f(b[3]), hv_exp_f(b[2]), hv_exp_f(b[1]), hv_exp_f(b[0])); -#elif HV_SIMD_NEON - *bOut = (float32x4_t) { - hv_exp_f(bIn[0]), - hv_exp_f(bIn[1]), - hv_exp_f(bIn[2]), - hv_exp_f(bIn[3])}; -#else // HV_SIMD_NONE - *bOut = hv_exp_f(bIn); -#endif -} - -static inline void __hv_ceil_f(hv_bInf_t bIn, hv_bOutf_t bOut) { -#if HV_SIMD_AVX - *bOut = _mm256_ceil_ps(bIn); -#elif HV_SIMD_SSE - *bOut = _mm_ceil_ps(bIn); -#elif HV_SIMD_NEON -#if __ARM_ARCH >= 8 - *bOut = vrndpq_f32(bIn); -#else - // A slow NEON implementation of __hv_ceil_f() is being used because - // the necessary intrinsic cannot be found. It is only available in ARMv8. - *bOut = (float32x4_t) {hv_ceil_f(bIn[0]), hv_ceil_f(bIn[1]), hv_ceil_f(bIn[2]), hv_ceil_f(bIn[3])}; -#endif // vrndpq_f32 -#else // HV_SIMD_NONE - *bOut = hv_ceil_f(bIn); -#endif -} - -static inline void __hv_floor_f(hv_bInf_t bIn, hv_bOutf_t bOut) { -#if HV_SIMD_AVX - *bOut = _mm256_floor_ps(bIn); -#elif HV_SIMD_SSE - *bOut = _mm_floor_ps(bIn); -#elif HV_SIMD_NEON -#if __ARM_ARCH >= 8 - *bOut = vrndmq_f32(bIn); -#else - // A slow implementation of __hv_floor_f() is being used because - // the necessary intrinsic cannot be found. It is only available from ARMv8. - *bOut = (float32x4_t) {hv_floor_f(bIn[0]), hv_floor_f(bIn[1]), hv_floor_f(bIn[2]), hv_floor_f(bIn[3])}; -#endif // vrndmq_f32 -#else // HV_SIMD_NONE - *bOut = hv_floor_f(bIn); -#endif -} - -// __add~f -static inline void __hv_add_f(hv_bInf_t bIn0, hv_bInf_t bIn1, hv_bOutf_t bOut) { -#if HV_SIMD_AVX - *bOut = _mm256_add_ps(bIn0, bIn1); -#elif HV_SIMD_SSE - *bOut = _mm_add_ps(bIn0, bIn1); -#elif HV_SIMD_NEON - *bOut = vaddq_f32(bIn0, bIn1); -#else // HV_SIMD_NONE - *bOut = bIn0 + bIn1; -#endif -} - -// __add~i -static inline void __hv_add_i(hv_bIni_t bIn0, hv_bIni_t bIn1, hv_bOuti_t bOut) { -#if HV_SIMD_AVX - __m128i x = _mm_add_epi32(_mm256_castsi256_si128(bIn0), _mm256_castsi256_si128(bIn1)); - __m128i y = _mm_add_epi32(_mm256_extractf128_si256(bIn0, 1), _mm256_extractf128_si256(bIn1, 1)); - *bOut = _mm256_insertf128_si256(_mm256_castsi128_si256(x), y, 1); -#elif HV_SIMD_SSE - *bOut = _mm_add_epi32(bIn0, bIn1); -#elif HV_SIMD_NEON - *bOut = vaddq_s32(bIn0, bIn1); -#else // HV_SIMD_NONE - *bOut = bIn0 + bIn1; -#endif -} - -// __sub~f -static inline void __hv_sub_f(hv_bInf_t bIn0, hv_bInf_t bIn1, hv_bOutf_t bOut) { -#if HV_SIMD_AVX - *bOut = _mm256_sub_ps(bIn0, bIn1); -#elif HV_SIMD_SSE - *bOut = _mm_sub_ps(bIn0, bIn1); -#elif HV_SIMD_NEON - *bOut = vsubq_f32(bIn0, bIn1); -#else // HV_SIMD_NONE - *bOut = bIn0 - bIn1; -#endif -} - -// __mul~f -static inline void __hv_mul_f(hv_bInf_t bIn0, hv_bInf_t bIn1, hv_bOutf_t bOut) { -#if HV_SIMD_AVX - *bOut = _mm256_mul_ps(bIn0, bIn1); -#elif HV_SIMD_SSE - *bOut = _mm_mul_ps(bIn0, bIn1); -#elif HV_SIMD_NEON - *bOut = vmulq_f32(bIn0, bIn1); -#else // HV_SIMD_NONE - *bOut = bIn0 * bIn1; -#endif -} - -// __*~i -static inline void __hv_mul_i(hv_bIni_t bIn0, hv_bIni_t bIn1, hv_bOuti_t bOut) { -#if HV_SIMD_AVX - __m128i x = _mm_mullo_epi32(_mm256_castsi256_si128(bIn0), _mm256_castsi256_si128(bIn1)); - __m128i y = _mm_mullo_epi32(_mm256_extractf128_si256(bIn0, 1), _mm256_extractf128_si256(bIn1, 1)); - *bOut = _mm256_insertf128_si256(_mm256_castsi128_si256(x), y, 1); -#elif HV_SIMD_SSE - *bOut = _mm_mullo_epi32(bIn0, bIn1); -#elif HV_SIMD_NEON - *bOut = vmulq_s32(bIn0, bIn1); -#else // HV_SIMD_NONE - *bOut = bIn0 * bIn1; -#endif -} - -// __cast~if -static inline void __hv_cast_if(hv_bIni_t bIn, hv_bOutf_t bOut) { -#if HV_SIMD_AVX - *bOut = _mm256_cvtepi32_ps(bIn); -#elif HV_SIMD_SSE - *bOut = _mm_cvtepi32_ps(bIn); -#elif HV_SIMD_NEON - *bOut = vcvtq_f32_s32(bIn); -#else // HV_SIMD_NONE - *bOut = (float) bIn; -#endif -} - -// __cast~fi -static inline void __hv_cast_fi(hv_bInf_t bIn, hv_bOuti_t bOut) { -#if HV_SIMD_AVX - *bOut = _mm256_cvtps_epi32(bIn); -#elif HV_SIMD_SSE - *bOut = _mm_cvtps_epi32(bIn); -#elif HV_SIMD_NEON - *bOut = vcvtq_s32_f32(bIn); -#else // HV_SIMD_NONE - *bOut = (int) bIn; -#endif -} - -static inline void __hv_div_f(hv_bInf_t bIn0, hv_bInf_t bIn1, hv_bOutf_t bOut) { -#if HV_SIMD_AVX - __m256 a = _mm256_cmp_ps(bIn1, _mm256_setzero_ps(), _CMP_EQ_OQ); - __m256 b = _mm256_div_ps(bIn0, bIn1); - *bOut = _mm256_andnot_ps(a, b); -#elif HV_SIMD_SSE - __m128 a = _mm_cmpeq_ps(bIn1, _mm_setzero_ps()); - __m128 b = _mm_div_ps(bIn0, bIn1); - *bOut = _mm_andnot_ps(a, b); -#elif HV_SIMD_NEON - uint32x4_t a = vceqq_f32(bIn1, vdupq_n_f32(0.0f)); - float32x4_t b = vmulq_f32(bIn0, vrecpeq_f32(bIn1)); // NOTE(mhroth): numerical results may be inexact - *bOut = vreinterpretq_f32_u32(vbicq_u32(vreinterpretq_u32_f32(b), a)); -#else // HV_SIMD_NONE - *bOut = (bIn1 != 0.0f) ? (bIn0 / bIn1) : 0.0f; -#endif -} - -static inline void __hv_min_f(hv_bInf_t bIn0, hv_bInf_t bIn1, hv_bOutf_t bOut) { -#if HV_SIMD_AVX - *bOut = _mm256_min_ps(bIn0, bIn1); -#elif HV_SIMD_SSE - *bOut = _mm_min_ps(bIn0, bIn1); -#elif HV_SIMD_NEON - *bOut = vminq_f32(bIn0, bIn1); -#else // HV_SIMD_NONE - *bOut = hv_min_f(bIn0, bIn1); -#endif -} - -static inline void __hv_min_i(hv_bIni_t bIn0, hv_bIni_t bIn1, hv_bOuti_t bOut) { -#if HV_SIMD_AVX - __m128i x = _mm_min_epi32(_mm256_castsi256_si128(bIn0), _mm256_castsi256_si128(bIn1)); - __m128i y = _mm_min_epi32(_mm256_extractf128_si256(bIn0, 1), _mm256_extractf128_si256(bIn1, 1)); - *bOut = _mm256_insertf128_si256(_mm256_castsi128_si256(x), y, 1); -#elif HV_SIMD_SSE - *bOut = _mm_min_epi32(bIn0, bIn1); -#elif HV_SIMD_NEON - *bOut = vminq_s32(bIn0, bIn1); -#else // HV_SIMD_NONE - *bOut = hv_min_i(bIn0, bIn1); -#endif -} - -static inline void __hv_max_f(hv_bInf_t bIn0, hv_bInf_t bIn1, hv_bOutf_t bOut) { -#if HV_SIMD_AVX - *bOut = _mm256_max_ps(bIn0, bIn1); -#elif HV_SIMD_SSE - *bOut = _mm_max_ps(bIn0, bIn1); -#elif HV_SIMD_NEON - *bOut = vmaxq_f32(bIn0, bIn1); -#else // HV_SIMD_NONE - *bOut = hv_max_f(bIn0, bIn1); -#endif -} - -static inline void __hv_max_i(hv_bIni_t bIn0, hv_bIni_t bIn1, hv_bOuti_t bOut) { -#if HV_SIMD_AVX - __m128i x = _mm_max_epi32(_mm256_castsi256_si128(bIn0), _mm256_castsi256_si128(bIn1)); - __m128i y = _mm_max_epi32(_mm256_extractf128_si256(bIn0, 1), _mm256_extractf128_si256(bIn1, 1)); - *bOut = _mm256_insertf128_si256(_mm256_castsi128_si256(x), y, 1); -#elif HV_SIMD_SSE - *bOut = _mm_max_epi32(bIn0, bIn1); -#elif HV_SIMD_NEON - *bOut = vmaxq_s32(bIn0, bIn1); -#else // HV_SIMD_NONE - *bOut = hv_max_i(bIn0, bIn1); -#endif -} - -static inline void __hv_pow_f(hv_bInf_t bIn0, hv_bInf_t bIn1, hv_bOutf_t bOut) { -#if HV_SIMD_AVX - float *b = (float *) hv_alloca(16*sizeof(float)); - _mm256_store_ps(b, bIn0); - _mm256_store_ps(b+8, bIn1); - *bOut = _mm256_set_ps( - hv_pow_f(b[7], b[15]), - hv_pow_f(b[6], b[14]), - hv_pow_f(b[5], b[13]), - hv_pow_f(b[4], b[12]), - hv_pow_f(b[3], b[11]), - hv_pow_f(b[2], b[10]), - hv_pow_f(b[1], b[9]), - hv_pow_f(b[0], b[8])); -#elif HV_SIMD_SSE - float *b = (float *) hv_alloca(8*sizeof(float)); - _mm_store_ps(b, bIn0); - _mm_store_ps(b+4, bIn1); - *bOut = _mm_set_ps( - hv_pow_f(b[3], b[7]), - hv_pow_f(b[2], b[6]), - hv_pow_f(b[1], b[5]), - hv_pow_f(b[0], b[4])); -#elif HV_SIMD_NEON - *bOut = (float32x4_t) { - hv_pow_f(bIn0[0], bIn1[0]), - hv_pow_f(bIn0[1], bIn1[1]), - hv_pow_f(bIn0[2], bIn1[2]), - hv_pow_f(bIn0[3], bIn1[3])}; -#else // HV_SIMD_NONE - *bOut = hv_pow_f(bIn0, bIn1); -#endif -} - -static inline void __hv_gt_f(hv_bInf_t bIn0, hv_bInf_t bIn1, hv_bOutf_t bOut) { -#if HV_SIMD_AVX - *bOut = _mm256_cmp_ps(bIn0, bIn1, _CMP_GT_OQ); -#elif HV_SIMD_SSE - *bOut = _mm_cmpgt_ps(bIn0, bIn1); -#elif HV_SIMD_NEON - *bOut = vreinterpretq_f32_u32(vcgtq_f32(bIn0, bIn1)); -#else // HV_SIMD_NONE - *bOut = (bIn0 > bIn1) ? 1.0f : 0.0f; -#endif -} - -static inline void __hv_gte_f(hv_bInf_t bIn0, hv_bInf_t bIn1, hv_bOutf_t bOut) { -#if HV_SIMD_AVX - *bOut = _mm256_cmp_ps(bIn0, bIn1, _CMP_GE_OQ); -#elif HV_SIMD_SSE - *bOut = _mm_cmpge_ps(bIn0, bIn1); -#elif HV_SIMD_NEON - *bOut = vreinterpretq_f32_u32(vcgeq_f32(bIn0, bIn1)); -#else // HV_SIMD_NONE - *bOut = (bIn0 >= bIn1) ? 1.0f : 0.0f; -#endif -} - -static inline void __hv_lt_f(hv_bInf_t bIn0, hv_bInf_t bIn1, hv_bOutf_t bOut) { -#if HV_SIMD_AVX - *bOut = _mm256_cmp_ps(bIn0, bIn1, _CMP_LT_OQ); -#elif HV_SIMD_SSE - *bOut = _mm_cmplt_ps(bIn0, bIn1); -#elif HV_SIMD_NEON - *bOut = vreinterpretq_f32_u32(vcltq_f32(bIn0, bIn1)); -#else // HV_SIMD_NONE - *bOut = (bIn0 < bIn1) ? 1.0f : 0.0f; -#endif -} - -static inline void __hv_lte_f(hv_bInf_t bIn0, hv_bInf_t bIn1, hv_bOutf_t bOut) { -#if HV_SIMD_AVX - *bOut = _mm256_cmp_ps(bIn0, bIn1, _CMP_LE_OQ); -#elif HV_SIMD_SSE - *bOut = _mm_cmple_ps(bIn0, bIn1); -#elif HV_SIMD_NEON - *bOut = vreinterpretq_f32_u32(vcleq_f32(bIn0, bIn1)); -#else // HV_SIMD_NONE - *bOut = (bIn0 <= bIn1) ? 1.0f : 0.0f; -#endif -} - -static inline void __hv_eq_f(hv_bInf_t bIn0, hv_bInf_t bIn1, hv_bOutf_t bOut) { -#if HV_SIMD_AVX - *bOut = _mm256_cmp_ps(bIn0, bIn1, _CMP_EQ_OQ); -#elif HV_SIMD_SSE - *bOut = _mm_cmpeq_ps(bIn0, bIn1); -#elif HV_SIMD_NEON - *bOut = vreinterpretq_f32_u32(vceqq_f32(bIn0, bIn1)); -#else // HV_SIMD_NONE - *bOut = (bIn0 == bIn1) ? 1.0f : 0.0f; -#endif -} - -static inline void __hv_neq_f(hv_bInf_t bIn0, hv_bInf_t bIn1, hv_bOutf_t bOut) { -#if HV_SIMD_AVX - *bOut = _mm256_cmp_ps(bIn0, bIn1, _CMP_NEQ_OQ); -#elif HV_SIMD_SSE - *bOut = _mm_cmpneq_ps(bIn0, bIn1); -#elif HV_SIMD_NEON - *bOut = vreinterpretq_f32_u32(vmvnq_u32(vceqq_f32(bIn0, bIn1))); -#else // HV_SIMD_NONE - *bOut = (bIn0 != bIn1) ? 1.0f : 0.0f; -#endif -} - -static inline void __hv_or_f(hv_bInf_t bIn0, hv_bInf_t bIn1, hv_bOutf_t bOut) { -#if HV_SIMD_AVX - *bOut = _mm256_or_ps(bIn1, bIn0); -#elif HV_SIMD_SSE - *bOut = _mm_or_ps(bIn1, bIn0); -#elif HV_SIMD_NEON - *bOut = vreinterpretq_f32_u32(vorrq_u32(vreinterpretq_u32_f32(bIn1), vreinterpretq_u32_f32(bIn0))); -#else // HV_SIMD_NONE - if (bIn0 == 0.0f && bIn1 == 0.0f) *bOut = 0.0f; - else if (bIn0 == 0.0f) *bOut = bIn1; - else if (bIn1 == 0.0f) *bOut = bIn0; - else hv_assert(0); -#endif -} - -static inline void __hv_and_f(hv_bInf_t bIn0, hv_bInf_t bIn1, hv_bOutf_t bOut) { -#if HV_SIMD_AVX - *bOut = _mm256_and_ps(bIn1, bIn0); -#elif HV_SIMD_SSE - *bOut = _mm_and_ps(bIn1, bIn0); -#elif HV_SIMD_NEON - *bOut = vreinterpretq_f32_u32(vandq_u32(vreinterpretq_u32_f32(bIn1), vreinterpretq_u32_f32(bIn0))); -#else // HV_SIMD_NONE - if (bIn0 == 0.0f || bIn1 == 0.0f) *bOut = 0.0f; - else if (bIn0 == 1.0f) *bOut = bIn1; - else if (bIn1 == 1.0f) *bOut = bIn0; - else hv_assert(0); -#endif -} - -static inline void __hv_andnot_f(hv_bInf_t bIn0_mask, hv_bInf_t bIn1, hv_bOutf_t bOut) { -#if HV_SIMD_AVX - *bOut = _mm256_andnot_ps(bIn0_mask, bIn1); -#elif HV_SIMD_SSE - *bOut = _mm_andnot_ps(bIn0_mask, bIn1); -#elif HV_SIMD_NEON - *bOut = vreinterpretq_f32_s32(vbicq_s32(vreinterpretq_s32_f32(bIn1), vreinterpretq_s32_f32(bIn0_mask))); -#else // HV_SIMD_NONE - *bOut = (bIn0_mask == 0.0f) ? bIn1 : 0.0f; -#endif -} - -// bOut = (bIn0 * bIn1) + bIn2 -static inline void __hv_fma_f(hv_bInf_t bIn0, hv_bInf_t bIn1, hv_bInf_t bIn2, hv_bOutf_t bOut) { -#if HV_SIMD_AVX -#if HV_SIMD_FMA - *bOut = _mm256_fmadd_ps(bIn0, bIn1, bIn2); -#else - *bOut = _mm256_add_ps(_mm256_mul_ps(bIn0, bIn1), bIn2); -#endif // HV_SIMD_FMA -#elif HV_SIMD_SSE -#if HV_SIMD_FMA - *bOut = _mm_fmadd_ps(bIn0, bIn1, bIn2); -#else - *bOut = _mm_add_ps(_mm_mul_ps(bIn0, bIn1), bIn2); -#endif // HV_SIMD_FMA -#elif HV_SIMD_NEON -#if __ARM_ARCH >= 8 - *bOut = vfmaq_f32(bIn2, bIn0, bIn1); -#else - // NOTE(mhroth): it turns out, fma SUUUUCKS on lesser ARM architectures - *bOut = vaddq_f32(vmulq_f32(bIn0, bIn1), bIn2); -#endif -#else // HV_SIMD_NONE - *bOut = hv_fma_f(bIn0, bIn1, bIn2); -#endif -} - -// bOut = (bIn0 * bIn1) - bIn2 -static inline void __hv_fms_f(hv_bInf_t bIn0, hv_bInf_t bIn1, hv_bInf_t bIn2, hv_bOutf_t bOut) { -#if HV_SIMD_AVX -#if HV_SIMD_FMA - *bOut = _mm256_fmsub_ps(bIn0, bIn1, bIn2); -#else - *bOut = _mm256_sub_ps(_mm256_mul_ps(bIn0, bIn1), bIn2); -#endif // HV_SIMD_FMA -#elif HV_SIMD_SSE -#if HV_SIMD_FMA - *bOut = _mm_fmsub_ps(bIn0, bIn1, bIn2); -#else - *bOut = _mm_sub_ps(_mm_mul_ps(bIn0, bIn1), bIn2); -#endif // HV_SIMD_FMA -#elif HV_SIMD_NEON -#if __ARM_ARCH >= 8 - *bOut = vfmsq_f32(bIn2, bIn0, bIn1); -#else - // NOTE(mhroth): it turns out, fma SUUUUCKS on lesser ARM architectures - *bOut = vsubq_f32(vmulq_f32(bIn0, bIn1), bIn2); -#endif -#else // HV_SIMD_NONE - *bOut = (bIn0 * bIn1) - bIn2; -#endif -} - -#endif // _HEAVY_MATH_H_ diff --git a/examples/examples-dsp/examples-pd/streams-generator-pd-audiokit/HvMessage.c b/examples/examples-dsp/examples-pd/streams-generator-pd-audiokit/HvMessage.c deleted file mode 100644 index 0f1ac5d7c2..0000000000 --- a/examples/examples-dsp/examples-pd/streams-generator-pd-audiokit/HvMessage.c +++ /dev/null @@ -1,199 +0,0 @@ -/** - * Copyright (c) 2014-2018 Enzien Audio Ltd. - * - * Permission to use, copy, modify, and/or distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH - * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY - * AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, - * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM - * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR - * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR - * PERFORMANCE OF THIS SOFTWARE. - */ - -#include "HvMessage.h" - -HvMessage *msg_init(HvMessage *m, hv_size_t numElements, hv_uint32_t timestamp) { - m->timestamp = timestamp; - m->numElements = (hv_uint16_t) numElements; - m->numBytes = (hv_uint16_t) msg_getCoreSize(numElements); - return m; -} - -HvMessage *msg_initWithFloat(HvMessage *m, hv_uint32_t timestamp, float f) { - m->timestamp = timestamp; - m->numElements = 1; - m->numBytes = sizeof(HvMessage); - msg_setFloat(m, 0, f); - return m; -} - -HvMessage *msg_initWithBang(HvMessage *m, hv_uint32_t timestamp) { - m->timestamp = timestamp; - m->numElements = 1; - m->numBytes = sizeof(HvMessage); - msg_setBang(m, 0); - return m; -} - -HvMessage *msg_initWithSymbol(HvMessage *m, hv_uint32_t timestamp, const char *s) { - m->timestamp = timestamp; - m->numElements = 1; - m->numBytes = sizeof(HvMessage) + (hv_uint16_t) hv_strlen(s); - msg_setSymbol(m, 0, s); - return m; -} - -HvMessage *msg_initWithHash(HvMessage *m, hv_uint32_t timestamp, hv_uint32_t h) { - m->timestamp = timestamp; - m->numElements = 1; - m->numBytes = sizeof(HvMessage); - msg_setHash(m, 0, h); - return m; -} - -void msg_copyToBuffer(const HvMessage *m, char *buffer, hv_size_t len) { - HvMessage *r = (HvMessage *) buffer; - - hv_size_t len_r = msg_getCoreSize(msg_getNumElements(m)); - - // assert that the message is not already larger than the length of the buffer - hv_assert(len_r <= len); - - // copy the basic message to the buffer - hv_memcpy(r, m, len_r); - - char *p = buffer + len_r; // points to the end of the base message - for (int i = 0; i < msg_getNumElements(m); ++i) { - if (msg_isSymbol(m,i)) { - const hv_size_t symLen = (hv_size_t) hv_strlen(msg_getSymbol(m,i)) + 1; // include the trailing null char - hv_assert(len_r + symLen <= len); // stay safe! - hv_strncpy(p, msg_getSymbol(m,i), symLen); - msg_setSymbol(r, i, p); - p += symLen; - len_r += symLen; - } - } - - r->numBytes = (hv_uint16_t) len_r; // update the message size in memory -} - -// the message is serialised such that all symbol elements are placed in order at the end of the buffer -HvMessage *msg_copy(const HvMessage *m) { - const hv_uint32_t heapSize = msg_getSize(m); - char *r = (char *) hv_malloc(heapSize); - hv_assert(r != NULL); - msg_copyToBuffer(m, r, heapSize); - return (HvMessage *) r; -} - -void msg_free(HvMessage *m) { - hv_free(m); // because heap messages are serialised in memory, a simple call to free releases the message -} - -bool msg_hasFormat(const HvMessage *m, const char *fmt) { - hv_assert(fmt != NULL); - const int n = msg_getNumElements(m); - for (int i = 0; i < n; ++i) { - switch (fmt[i]) { - case 'b': if (!msg_isBang(m, i)) return false; break; - case 'f': if (!msg_isFloat(m, i)) return false; break; - case 'h': if (!msg_isHash(m, i)) return false; break; - case 's': if (!msg_isSymbol(m, i)) return false; break; - default: return false; - } - } - return (fmt[n] == '\0'); -} - -bool msg_compareSymbol(const HvMessage *m, int i, const char *s) { - switch (msg_getType(m,i)) { - case HV_MSG_SYMBOL: return !hv_strcmp(msg_getSymbol(m, i), s); - case HV_MSG_HASH: return (msg_getHash(m,i) == hv_string_to_hash(s)); - default: return false; - } -} - -bool msg_equalsElement(const HvMessage *m, int i_m, const HvMessage *n, int i_n) { - if (i_m < msg_getNumElements(m) && i_n < msg_getNumElements(n)) { - if (msg_getType(m, i_m) == msg_getType(n, i_n)) { - switch (msg_getType(m, i_m)) { - case HV_MSG_BANG: return true; - case HV_MSG_FLOAT: return (msg_getFloat(m, i_m) == msg_getFloat(n, i_n)); - case HV_MSG_SYMBOL: return msg_compareSymbol(m, i_m, msg_getSymbol(n, i_n)); - case HV_MSG_HASH: return msg_getHash(m,i_m) == msg_getHash(n,i_n); - default: break; - } - } - } - return false; -} - -void msg_setElementToFrom(HvMessage *n, int i_n, const HvMessage *const m, int i_m) { - switch (msg_getType(m, i_m)) { - case HV_MSG_BANG: msg_setBang(n, i_n); break; - case HV_MSG_FLOAT: msg_setFloat(n, i_n, msg_getFloat(m, i_m)); break; - case HV_MSG_SYMBOL: msg_setSymbol(n, i_n, msg_getSymbol(m, i_m)); break; - case HV_MSG_HASH: msg_setHash(n, i_n, msg_getHash(m, i_m)); - default: break; - } -} - -hv_uint32_t msg_getHash(const HvMessage *const m, int i) { - hv_assert(i < msg_getNumElements(m)); // invalid index - switch (msg_getType(m,i)) { - case HV_MSG_BANG: return 0xFFFFFFFF; - case HV_MSG_FLOAT: { - float f = msg_getFloat(m,i); - return *((hv_uint32_t *) &f); - } - case HV_MSG_SYMBOL: return hv_string_to_hash(msg_getSymbol(m,i)); - case HV_MSG_HASH: return (&(m->elem)+i)->data.h; - default: return 0; - } -} - -char *msg_toString(const HvMessage *m) { - hv_assert(msg_getNumElements(m) > 0); - int *len = (int *) hv_alloca(msg_getNumElements(m)*sizeof(int)); - int size = 0; // the total length of our final buffer - - // loop through every element in our list of atoms - // first loop figures out how long our buffer should be - for (int i = 0; i < msg_getNumElements(m); i++) { - // length of our string is each atom plus a space, or \0 on the end - switch (msg_getType(m, i)) { - case HV_MSG_BANG: len[i] = hv_snprintf(NULL, 0, "%s", "bang") + 1; break; - case HV_MSG_FLOAT: len[i] = hv_snprintf(NULL, 0, "%g", msg_getFloat(m, i)) + 1; break; - case HV_MSG_SYMBOL: len[i] = hv_snprintf(NULL, 0, "%s", msg_getSymbol(m, i)) + 1; break; - case HV_MSG_HASH: len[i] = hv_snprintf(NULL, 0, "0x%X", msg_getHash(m, i)) + 1; break; - default: break; - } - size += len[i]; - } - - hv_assert(size > 0); - - // now we do the piecewise concatenation into our final string - // the final buffer we will pass back after concatenating all strings - user should free it - char *finalString = (char *) hv_malloc(size*sizeof(char)); - hv_assert(finalString != NULL); - int pos = 0; - for (int i = 0; i < msg_getNumElements(m); i++) { - // put a string representation of each atom into the final string - switch (msg_getType(m, i)) { - case HV_MSG_BANG: hv_snprintf(finalString+pos, len[i], "%s", "bang"); break; - case HV_MSG_FLOAT: hv_snprintf(finalString+pos, len[i], "%g", msg_getFloat(m, i)); break; - case HV_MSG_SYMBOL: hv_snprintf(finalString+pos, len[i], "%s", msg_getSymbol(m, i)); break; - case HV_MSG_HASH: hv_snprintf(finalString+pos, len[i], "0x%X", msg_getHash(m, i)); break; - default: break; - } - pos += len[i]; - finalString[pos-1] = 32; // ASCII space - } - finalString[size-1] = '\0'; // ensure that the string is null terminated - return finalString; -} diff --git a/examples/examples-dsp/examples-pd/streams-generator-pd-audiokit/HvMessage.h b/examples/examples-dsp/examples-pd/streams-generator-pd-audiokit/HvMessage.h deleted file mode 100644 index a3fdd1c9e6..0000000000 --- a/examples/examples-dsp/examples-pd/streams-generator-pd-audiokit/HvMessage.h +++ /dev/null @@ -1,183 +0,0 @@ -/** - * Copyright (c) 2014-2018 Enzien Audio Ltd. - * - * Permission to use, copy, modify, and/or distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH - * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY - * AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, - * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM - * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR - * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR - * PERFORMANCE OF THIS SOFTWARE. - */ - -#ifndef _HEAVY_MESSAGE_H_ -#define _HEAVY_MESSAGE_H_ - -#include "HvUtils.h" - -#ifdef __cplusplus -extern "C" { -#endif - -typedef enum ElementType { - HV_MSG_BANG = 0, - HV_MSG_FLOAT = 1, - HV_MSG_SYMBOL = 2, - HV_MSG_HASH = 3 -} ElementType; - -typedef struct Element { - ElementType type; - union { - float f; // float - const char *s; // symbol - hv_uint32_t h; // hash - } data; -} Element; - -typedef struct HvMessage { - hv_uint32_t timestamp; // the sample at which this message should be processed - hv_uint16_t numElements; - hv_uint16_t numBytes; // the total number of bytes that this message occupies in memory, including strings - Element elem; -} HvMessage; - -typedef struct ReceiverMessagePair { - hv_uint32_t receiverHash; - HvMessage msg; -} ReceiverMessagePair; - -#define HV_MESSAGE_ON_STACK(_x) (HvMessage *) hv_alloca(msg_getCoreSize(_x)) - -/** Returns the number of bytes that this message consumes in memory, not including strings. */ -static inline hv_size_t msg_getCoreSize(hv_size_t numElements) { - hv_assert(numElements > 0); - return sizeof(HvMessage) + ((numElements-1) * sizeof(Element)); -} - -HvMessage *msg_copy(const HvMessage *m); - -/** Copies the message into the given buffer. The buffer must be at least as large as msg_getNumHeapBytes(). */ -void msg_copyToBuffer(const HvMessage *m, char *buffer, hv_size_t len); - -void msg_setElementToFrom(HvMessage *n, int indexN, const HvMessage *const m, int indexM); - -/** Frees a message on the heap. Does nothing if argument is NULL. */ -void msg_free(HvMessage *m); - -HvMessage *msg_init(HvMessage *m, hv_size_t numElements, hv_uint32_t timestamp); - -HvMessage *msg_initWithFloat(HvMessage *m, hv_uint32_t timestamp, float f); - -HvMessage *msg_initWithBang(HvMessage *m, hv_uint32_t timestamp); - -HvMessage *msg_initWithSymbol(HvMessage *m, hv_uint32_t timestamp, const char *s); - -HvMessage *msg_initWithHash(HvMessage *m, hv_uint32_t timestamp, hv_uint32_t h); - -static inline hv_uint32_t msg_getTimestamp(const HvMessage *m) { - return m->timestamp; -} - -static inline void msg_setTimestamp(HvMessage *m, hv_uint32_t timestamp) { - m->timestamp = timestamp; -} - -static inline int msg_getNumElements(const HvMessage *m) { - return (int) m->numElements; -} - -/** Returns the total number of bytes this message consumes in memory. */ -static inline hv_uint32_t msg_getSize(const HvMessage *m) { - return m->numBytes; -} - -static inline ElementType msg_getType(const HvMessage *m, int index) { - hv_assert(index < msg_getNumElements(m)); // invalid index - return (&(m->elem)+index)->type; -} - -static inline void msg_setBang(HvMessage *m, int index) { - hv_assert(index < msg_getNumElements(m)); // invalid index - (&(m->elem)+index)->type = HV_MSG_BANG; - (&(m->elem)+index)->data.s = NULL; -} - -static inline bool msg_isBang(const HvMessage *m, int index) { - return (index < msg_getNumElements(m)) ? (msg_getType(m,index) == HV_MSG_BANG) : false; -} - -static inline void msg_setFloat(HvMessage *m, int index, float f) { - hv_assert(index < msg_getNumElements(m)); // invalid index - (&(m->elem)+index)->type = HV_MSG_FLOAT; - (&(m->elem)+index)->data.f = f; -} - -static inline float msg_getFloat(const HvMessage *const m, int index) { - hv_assert(index < msg_getNumElements(m)); // invalid index - return (&(m->elem)+index)->data.f; -} - -static inline bool msg_isFloat(const HvMessage *const m, int index) { - return (index < msg_getNumElements(m)) ? (msg_getType(m,index) == HV_MSG_FLOAT) : false; -} - -static inline void msg_setHash(HvMessage *m, int index, hv_uint32_t h) { - hv_assert(index < msg_getNumElements(m)); // invalid index - (&(m->elem)+index)->type = HV_MSG_HASH; - (&(m->elem)+index)->data.h = h; -} - -static inline bool msg_isHash(const HvMessage *m, int index) { - return (index < msg_getNumElements(m)) ? (msg_getType(m, index) == HV_MSG_HASH) : false; -} - -/** Returns true if the element is a hash or symbol. False otherwise. */ -static inline bool msg_isHashLike(const HvMessage *m, int index) { - return (index < msg_getNumElements(m)) ? ((msg_getType(m, index) == HV_MSG_HASH) || (msg_getType(m, index) == HV_MSG_SYMBOL)) : false; -} - -/** Returns a 32-bit hash of the given element. */ -hv_uint32_t msg_getHash(const HvMessage *const m, int i); - -static inline void msg_setSymbol(HvMessage *m, int index, const char *s) { - hv_assert(index < msg_getNumElements(m)); // invalid index - hv_assert(s != NULL); - (&(m->elem)+index)->type = HV_MSG_SYMBOL; - (&(m->elem)+index)->data.s = s; - // NOTE(mhroth): if the same message container is reused and string reset, - // then the message size will be overcounted - m->numBytes += (hv_uint16_t) (hv_strlen(s) + 1); // also count '\0' -} - -static inline const char *msg_getSymbol(const HvMessage *m, int index) { - hv_assert(index < msg_getNumElements(m)); // invalid index - return (&(m->elem)+index)->data.s; -} - -static inline bool msg_isSymbol(const HvMessage *m, int index) { - return (index < msg_getNumElements(m)) ? (msg_getType(m, index) == HV_MSG_SYMBOL) : false; -} - -bool msg_compareSymbol(const HvMessage *m, int i, const char *s); - -/** Returns 1 if the element i_m of message m is equal to element i_n of message n. */ -bool msg_equalsElement(const HvMessage *m, int i_m, const HvMessage *n, int i_n); - -bool msg_hasFormat(const HvMessage *m, const char *fmt); - -/** - * Create a string representation of the message. Suitable for use by the print object. - * The resulting string must be freed by the caller. - */ -char *msg_toString(const HvMessage *msg); - -#ifdef __cplusplus -} -#endif - -#endif // _HEAVY_MESSAGE_H_ diff --git a/examples/examples-dsp/examples-pd/streams-generator-pd-audiokit/HvMessagePool.c b/examples/examples-dsp/examples-pd/streams-generator-pd-audiokit/HvMessagePool.c deleted file mode 100644 index dbddb1d1dc..0000000000 --- a/examples/examples-dsp/examples-pd/streams-generator-pd-audiokit/HvMessagePool.c +++ /dev/null @@ -1,144 +0,0 @@ -/** - * Copyright (c) 2014-2018 Enzien Audio Ltd. - * - * Permission to use, copy, modify, and/or distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH - * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY - * AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, - * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM - * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR - * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR - * PERFORMANCE OF THIS SOFTWARE. - */ - -#include "HvMessagePool.h" -#include "HvMessage.h" - -// the number of bytes reserved at a time from the pool -#define MP_BLOCK_SIZE_BYTES 512 - -#if HV_APPLE -#pragma mark - MessageList -#endif - -typedef struct MessageListNode { - char *p; - struct MessageListNode *next; -} MessageListNode; - -static inline bool ml_hasAvailable(HvMessagePoolList *ml) { - return (ml->head != NULL); -} - -static char *ml_pop(HvMessagePoolList *ml) { - MessageListNode *n = ml->head; - ml->head = n->next; - n->next = ml->pool; - ml->pool = n; - char *const p = n->p; - n->p = NULL; // set to NULL to make it clear that this node does not have a valid buffer - return p; -} - -/** Push a MessageListNode with the given pointer onto the head of the queue. */ -static void ml_push(HvMessagePoolList *ml, void *p) { - MessageListNode *n = NULL; - if (ml->pool != NULL) { - // take an empty MessageListNode from the pool - n = ml->pool; - ml->pool = n->next; - } else { - // a MessageListNode is not available, allocate one - n = (MessageListNode *) hv_malloc(sizeof(MessageListNode)); - hv_assert(n != NULL); - } - n->p = (char *) p; - n->next = ml->head; - ml->head = n; // push to the front of the queue -} - -static void ml_free(HvMessagePoolList *ml) { - if (ml != NULL) { - while (ml_hasAvailable(ml)) { - ml_pop(ml); - } - while (ml->pool != NULL) { - MessageListNode *n = ml->pool; - ml->pool = n->next; - hv_free(n); - } - } -} - -#if HV_APPLE -#pragma mark - HvMessagePool -#endif - -static hv_size_t mp_messagelistIndexForSize(hv_size_t byteSize) { - return (hv_size_t) hv_max_i((hv_min_max_log2((hv_uint32_t) byteSize) - 5), 0); -} - -hv_size_t mp_init(HvMessagePool *mp, hv_size_t numKB) { - mp->bufferSize = numKB * 1024; - mp->buffer = (char *) hv_malloc(mp->bufferSize); - hv_assert(mp->buffer != NULL); - mp->bufferIndex = 0; - - // initialise all message lists - for (int i = 0; i < MP_NUM_MESSAGE_LISTS; i++) { - mp->lists[i].head = NULL; - mp->lists[i].pool = NULL; - } - - return mp->bufferSize; -} - -void mp_free(HvMessagePool *mp) { - hv_free(mp->buffer); - for (int i = 0; i < MP_NUM_MESSAGE_LISTS; i++) { - ml_free(&mp->lists[i]); - } -} - -void mp_freeMessage(HvMessagePool *mp, HvMessage *m) { - const hv_size_t b = msg_getSize(m); // the number of bytes that a message occupies in memory - const hv_size_t i = mp_messagelistIndexForSize(b); // the HvMessagePoolList index in the pool - HvMessagePoolList *ml = &mp->lists[i]; - const hv_size_t chunkSize = 32 << i; - hv_memclear(m, chunkSize); // clear the chunk, just in case - ml_push(ml, m); -} - -HvMessage *mp_addMessage(HvMessagePool *mp, const HvMessage *m) { - const hv_size_t b = msg_getSize(m); - // determine the message list index to allocate data from based on the msg size - // smallest chunk size is 32 bytes - const hv_size_t i = mp_messagelistIndexForSize(b); - - hv_assert(i < MP_NUM_MESSAGE_LISTS); // how many chunk sizes do we want to support? 32, 64, 128, 256 at the moment - HvMessagePoolList *ml = &mp->lists[i]; - const hv_size_t chunkSize = 32 << i; - - if (ml_hasAvailable(ml)) { - char *buf = ml_pop(ml); - msg_copyToBuffer(m, buf, chunkSize); - return (HvMessage *) buf; - } else { - // if no appropriately sized buffer is immediately available, increase the size of the used buffer - const hv_size_t newIndex = mp->bufferIndex + MP_BLOCK_SIZE_BYTES; - hv_assert((newIndex <= mp->bufferSize) && - "The message pool buffer size has been exceeded. The context cannot store more messages. " - "Try using the new_with_options() initialiser with a larger pool size (default is 10KB)."); - - for (hv_size_t j = mp->bufferIndex; j < newIndex; j += chunkSize) { - ml_push(ml, mp->buffer + j); // push new nodes onto the list with chunk pointers - } - mp->bufferIndex = newIndex; - char *buf = ml_pop(ml); - msg_copyToBuffer(m, buf, chunkSize); - return (HvMessage *) buf; - } -} diff --git a/examples/examples-dsp/examples-pd/streams-generator-pd-audiokit/HvMessagePool.h b/examples/examples-dsp/examples-pd/streams-generator-pd-audiokit/HvMessagePool.h deleted file mode 100644 index 693e470e12..0000000000 --- a/examples/examples-dsp/examples-pd/streams-generator-pd-audiokit/HvMessagePool.h +++ /dev/null @@ -1,71 +0,0 @@ -/** - * Copyright (c) 2014-2018 Enzien Audio Ltd. - * - * Permission to use, copy, modify, and/or distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH - * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY - * AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, - * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM - * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR - * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR - * PERFORMANCE OF THIS SOFTWARE. - */ - -#ifndef _MESSAGE_POOL_H_ -#define _MESSAGE_POOL_H_ - -#include "HvUtils.h" - -#ifdef HV_MP_NUM_MESSAGE_LISTS -#define MP_NUM_MESSAGE_LISTS HV_MP_NUM_MESSAGE_LISTS -#else // HV_MP_NUM_MESSAGE_LISTS -#define MP_NUM_MESSAGE_LISTS 4 -#endif // HV_MP_NUM_MESSAGE_LISTS - -#ifdef __cplusplus -extern "C" { -#endif - -typedef struct HvMessagePoolList { - struct MessageListNode *head; // list of currently available blocks - struct MessageListNode *pool; // list of currently used blocks -} HvMessagePoolList; - -typedef struct HvMessagePool { - char *buffer; // the buffer of all messages - hv_size_t bufferSize; // in bytes - hv_size_t bufferIndex; // the number of total reserved bytes - - HvMessagePoolList lists[MP_NUM_MESSAGE_LISTS]; -} HvMessagePool; - -/** - * The HvMessagePool is a basic memory management system. It reserves a large block of memory at initialisation - * and proceeds to divide this block into smaller chunks (usually 512 bytes) as they are needed. These chunks are - * further divided into 32, 64, 128, or 256 sections. Each of these sections is managed by a HvMessagePoolList (MPL). - * An MPL is a linked-list data structure which is initialised such that its own pool of listnodes is filled with nodes - * that point at each subblock (e.g. each 32-byte block of a 512-block chunk). - * - * HvMessagePool is loosely inspired by TCMalloc. http://goog-perftools.sourceforge.net/doc/tcmalloc.html - */ - -hv_size_t mp_init(struct HvMessagePool *mp, hv_size_t numKB); - -void mp_free(struct HvMessagePool *mp); - -/** - * Adds a message to the pool and returns a pointer to the copy. Returns NULL - * if no space was available in the pool. - */ -struct HvMessage *mp_addMessage(struct HvMessagePool *mp, const struct HvMessage *m); - -void mp_freeMessage(struct HvMessagePool *mp, struct HvMessage *m); - -#ifdef __cplusplus -} -#endif - -#endif // _MESSAGE_POOL_H_ diff --git a/examples/examples-dsp/examples-pd/streams-generator-pd-audiokit/HvMessageQueue.c b/examples/examples-dsp/examples-pd/streams-generator-pd-audiokit/HvMessageQueue.c deleted file mode 100644 index 2eeab8ff8a..0000000000 --- a/examples/examples-dsp/examples-pd/streams-generator-pd-audiokit/HvMessageQueue.c +++ /dev/null @@ -1,215 +0,0 @@ -/** - * Copyright (c) 2014-2018 Enzien Audio Ltd. - * - * Permission to use, copy, modify, and/or distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH - * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY - * AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, - * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM - * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR - * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR - * PERFORMANCE OF THIS SOFTWARE. - */ - -#include "HvMessageQueue.h" - -hv_size_t mq_initWithPoolSize(HvMessageQueue *q, hv_size_t poolSizeKB) { - hv_assert(poolSizeKB > 0); - q->head = NULL; - q->tail = NULL; - q->pool = NULL; - return mp_init(&q->mp, poolSizeKB); -} - -void mq_free(HvMessageQueue *q) { - mq_clear(q); - while (q->pool != NULL) { - MessageNode *n = q->pool; - q->pool = q->pool->next; - hv_free(n); - } - mp_free(&q->mp); -} - -static MessageNode *mq_getOrCreateNodeFromPool(HvMessageQueue *q) { - if (q->pool == NULL) { - // if necessary, create a new empty node - q->pool = (MessageNode *) hv_malloc(sizeof(MessageNode)); - hv_assert(q->pool != NULL); - q->pool->next = NULL; - } - MessageNode *node = q->pool; - q->pool = q->pool->next; - return node; -} - -int mq_size(HvMessageQueue *q) { - int size = 0; - MessageNode *n = q->head; - while (n != NULL) { - ++size; - n = n->next; - } - return size; -} - -HvMessage *mq_addMessage(HvMessageQueue *q, const HvMessage *m, int let, - void (*sendMessage)(HeavyContextInterface *, int, const HvMessage *)) { - MessageNode *node = mq_getOrCreateNodeFromPool(q); - node->m = mp_addMessage(&q->mp, m); - node->let = let; - node->sendMessage = sendMessage; - node->prev = NULL; - node->next = NULL; - - if (q->tail != NULL) { - // the list already contains elements - q->tail->next = node; - node->prev = q->tail; - q->tail = node; - } else { - // the list is empty - node->prev = NULL; - q->head = node; - q->tail = node; - } - return mq_node_getMessage(node); -} - -HvMessage *mq_addMessageByTimestamp(HvMessageQueue *q, const HvMessage *m, int let, - void (*sendMessage)(HeavyContextInterface *, int, const HvMessage *)) { - if (mq_hasMessage(q)) { - MessageNode *n = mq_getOrCreateNodeFromPool(q); - n->m = mp_addMessage(&q->mp, m); - n->let = let; - n->sendMessage = sendMessage; - - if (msg_getTimestamp(m) < msg_getTimestamp(q->head->m)) { - // the message occurs before the current head - n->next = q->head; - q->head->prev = n; - n->prev = NULL; - q->head = n; - } else if (msg_getTimestamp(m) >= msg_getTimestamp(q->tail->m)) { - // the message occurs after the current tail - n->next = NULL; - n->prev = q->tail; - q->tail->next = n; - q->tail = n; - } else { - // the message occurs somewhere between the head and tail - MessageNode *node = q->head; - while (node != NULL) { - if (msg_getTimestamp(m) < msg_getTimestamp(node->next->m)) { - MessageNode *r = node->next; - node->next = n; - n->next = r; - n->prev = node; - r->prev = n; - break; - } - node = node->next; - } - } - return n->m; - } else { - // add a message to the head - return mq_addMessage(q, m, let, sendMessage); - } -} - -void mq_pop(HvMessageQueue *q) { - if (mq_hasMessage(q)) { - MessageNode *n = q->head; - - mp_freeMessage(&q->mp, n->m); - n->m = NULL; - - n->let = 0; - n->sendMessage = NULL; - - q->head = n->next; - if (q->head == NULL) { - q->tail = NULL; - } else { - q->head->prev = NULL; - } - n->next = q->pool; - n->prev = NULL; - q->pool = n; - } -} - -bool mq_removeMessage(HvMessageQueue *q, HvMessage *m, void (*sendMessage)(HeavyContextInterface *, int, const HvMessage *)) { - if (mq_hasMessage(q)) { - if (mq_node_getMessage(q->head) == m) { // msg in head node - // only remove the message if sendMessage is the same as the stored one, - // if the sendMessage argument is NULL, it is not checked and will remove any matching message pointer - if (sendMessage == NULL || q->head->sendMessage == sendMessage) { - mq_pop(q); - return true; - } - } else { - MessageNode *prevNode = q->head; - MessageNode *currNode = q->head->next; - while ((currNode != NULL) && (currNode->m != m)) { - prevNode = currNode; - currNode = currNode->next; - } - if (currNode != NULL) { - if (sendMessage == NULL || currNode->sendMessage == sendMessage) { - mp_freeMessage(&q->mp, m); - currNode->m = NULL; - currNode->let = 0; - currNode->sendMessage = NULL; - if (currNode == q->tail) { // msg in tail node - prevNode->next = NULL; - q->tail = prevNode; - } else { // msg in middle node - prevNode->next = currNode->next; - currNode->next->prev = prevNode; - } - currNode->next = (q->pool == NULL) ? NULL : q->pool; - currNode->prev = NULL; - q->pool = currNode; - return true; - } - } - } - } - return false; -} - -void mq_clear(HvMessageQueue *q) { - while (mq_hasMessage(q)) { - mq_pop(q); - } -} - -void mq_clearAfter(HvMessageQueue *q, const hv_uint32_t timestamp) { - MessageNode *n = q->tail; - while (n != NULL && timestamp <= msg_getTimestamp(n->m)) { - // free the node's message - mp_freeMessage(&q->mp, n->m); - n->m = NULL; - n->let = 0; - n->sendMessage = NULL; - - // the tail points at the previous node - q->tail = n->prev; - - // put the node back in the pool - n->next = q->pool; - n->prev = NULL; - if (q->pool != NULL) q->pool->prev = n; - q->pool = n; - - // update the tail node - n = q->tail; - } - - if (q->tail == NULL) q->head = NULL; -} diff --git a/examples/examples-dsp/examples-pd/streams-generator-pd-audiokit/HvMessageQueue.h b/examples/examples-dsp/examples-pd/streams-generator-pd-audiokit/HvMessageQueue.h deleted file mode 100644 index a35e4aa579..0000000000 --- a/examples/examples-dsp/examples-pd/streams-generator-pd-audiokit/HvMessageQueue.h +++ /dev/null @@ -1,101 +0,0 @@ -/** - * Copyright (c) 2014-2018 Enzien Audio Ltd. - * - * Permission to use, copy, modify, and/or distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH - * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY - * AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, - * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM - * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR - * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR - * PERFORMANCE OF THIS SOFTWARE. - */ - -#ifndef _MESSAGE_QUEUE_H_ -#define _MESSAGE_QUEUE_H_ - -#include "HvMessage.h" -#include "HvMessagePool.h" - -#ifdef __cplusplus -extern "C" { -#endif - -#ifdef __cplusplus -class HeavyContextInterface; -#else -typedef struct HeavyContextInterface HeavyContextInterface; -#endif - -typedef struct MessageNode { - struct MessageNode *prev; // doubly linked list - struct MessageNode *next; - HvMessage *m; - void (*sendMessage)(HeavyContextInterface *, int, const HvMessage *); - int let; -} MessageNode; - -/** A doubly linked list containing scheduled messages. */ -typedef struct HvMessageQueue { - MessageNode *head; // the head of the queue - MessageNode *tail; // the tail of the queue - MessageNode *pool; // the head of the reserve pool - HvMessagePool mp; -} HvMessageQueue; - -hv_size_t mq_initWithPoolSize(HvMessageQueue *q, hv_size_t poolSizeKB); - -void mq_free(HvMessageQueue *q); - -int mq_size(HvMessageQueue *q); - -static inline HvMessage *mq_node_getMessage(MessageNode *n) { - return n->m; -} - -static inline int mq_node_getLet(MessageNode *n) { - return n->let; -} - -static inline bool mq_hasMessage(HvMessageQueue *q) { - return (q->head != NULL); -} - -// true if there is a message and it occurs before (<) timestamp -static inline bool mq_hasMessageBefore(HvMessageQueue *const q, const hv_uint32_t timestamp) { - return mq_hasMessage(q) && (msg_getTimestamp(mq_node_getMessage(q->head)) < timestamp); -} - -static inline MessageNode *mq_peek(HvMessageQueue *q) { - return q->head; -} - -/** Appends the message to the end of the queue. */ -HvMessage *mq_addMessage(HvMessageQueue *q, const HvMessage *m, int let, - void (*sendMessage)(HeavyContextInterface *, int, const HvMessage *)); - -/** Insert in ascending order the message acccording to its timestamp. */ -HvMessage *mq_addMessageByTimestamp(HvMessageQueue *q, const HvMessage *m, int let, - void (*sendMessage)(HeavyContextInterface *, int, const HvMessage *)); - -/** Pop the message at the head of the queue (and free its memory). */ -void mq_pop(HvMessageQueue *q); - -/** Remove a message from the queue (and free its memory) */ -bool mq_removeMessage(HvMessageQueue *q, HvMessage *m, - void (*sendMessage)(HeavyContextInterface *, int, const HvMessage *)); - -/** Clears (and frees) all messages in the queue. */ -void mq_clear(HvMessageQueue *q); - -/** Removes all messages occuring at or after the given timestamp. */ -void mq_clearAfter(HvMessageQueue *q, const hv_uint32_t timestamp); - -#ifdef __cplusplus -} -#endif - -#endif // _MESSAGE_QUEUE_H_ diff --git a/examples/examples-dsp/examples-pd/streams-generator-pd-audiokit/HvTable.c b/examples/examples-dsp/examples-pd/streams-generator-pd-audiokit/HvTable.c deleted file mode 100644 index 1b2cd38315..0000000000 --- a/examples/examples-dsp/examples-pd/streams-generator-pd-audiokit/HvTable.c +++ /dev/null @@ -1,110 +0,0 @@ -/** - * Copyright (c) 2014-2018 Enzien Audio Ltd. - * - * Permission to use, copy, modify, and/or distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH - * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY - * AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, - * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM - * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR - * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR - * PERFORMANCE OF THIS SOFTWARE. - */ - -#include "HvTable.h" -#include "HvMessage.h" - -hv_size_t hTable_init(HvTable *o, int length) { - o->length = length; - // true size of the table is always an integer multple of HV_N_SIMD - o->size = (length + HV_N_SIMD_MASK) & ~HV_N_SIMD_MASK; - // add an extra length for mirroring - o->allocated = o->size + HV_N_SIMD; - o->head = 0; - hv_size_t numBytes = o->allocated * sizeof(float); - o->buffer = (float *) hv_malloc(numBytes); - hv_assert(o->buffer != NULL); - hv_memclear(o->buffer, numBytes); - return numBytes; -} - -hv_size_t hTable_initWithData(HvTable *o, int length, const float *data) { - o->length = length; - o->size = (length + HV_N_SIMD_MASK) & ~HV_N_SIMD_MASK; - o->allocated = o->size + HV_N_SIMD; - o->head = 0; - hv_size_t numBytes = o->size * sizeof(float); - o->buffer = (float *) hv_malloc(numBytes); - hv_assert(o->buffer != NULL); - hv_memclear(o->buffer, numBytes); - hv_memcpy(o->buffer, data, length*sizeof(float)); - return numBytes; -} - -hv_size_t hTable_initWithFinalData(HvTable *o, int length, float *data) { - o->length = length; - o->size = length; - o->allocated = length; - o->buffer = data; - o->head = 0; - return 0; -} - -void hTable_free(HvTable *o) { - hv_free(o->buffer); -} - -int hTable_resize(HvTable *o, hv_uint32_t newLength) { - // TODO(mhroth): update context with memory allocated by table - // NOTE(mhroth): mirrored bytes are not necessarily carried over - const hv_uint32_t newSize = (newLength + HV_N_SIMD_MASK) & ~HV_N_SIMD_MASK; - if (newSize == o->size) return 0; // early exit if no change in size - const hv_uint32_t oldSizeBytes = (hv_uint32_t) (o->size * sizeof(float)); - const hv_uint32_t newAllocated = newSize + HV_N_SIMD; - const hv_uint32_t newAllocatedBytes = (hv_uint32_t) (newAllocated * sizeof(float)); - - float *b = (float *) hv_realloc(o->buffer, newAllocatedBytes); - hv_assert(b != NULL); // error while reallocing! - // ensure that hv_realloc has given us a correctly aligned buffer - if ((((hv_uintptr_t) (const void *) b) & ((0x1< o->size) { - hv_memclear(b + o->size, (newAllocated - o->size) * sizeof(float)); // clear new parts of the buffer - } - o->buffer = b; - } else { - // if not, we have to re-malloc ourselves - char *c = (char *) hv_malloc(newAllocatedBytes); - hv_assert(c != NULL); // error while allocating new buffer! - if (newAllocatedBytes > oldSizeBytes) { - hv_memcpy(c, b, oldSizeBytes); - hv_memclear(c + oldSizeBytes, newAllocatedBytes - oldSizeBytes); - } else { - hv_memcpy(c, b, newAllocatedBytes); - } - hv_free(b); - o->buffer = (float *) c; - } - o->length = newLength; - o->size = newSize; - o->allocated = newAllocated; - return (int) (newAllocated - oldSizeBytes - (HV_N_SIMD*sizeof(float))); -} - -void hTable_onMessage(HeavyContextInterface *_c, HvTable *o, int letIn, const HvMessage *m, - void (*sendMessage)(HeavyContextInterface *, int, const HvMessage *)) { - if (msg_compareSymbol(m,0,"resize") && msg_isFloat(m,1) && msg_getFloat(m,1) >= 0.0f) { - hTable_resize(o, (int) hv_ceil_f(msg_getFloat(m,1))); // apply ceil to ensure that tables always have enough space - - // send out the new size of the table - HvMessage *n = HV_MESSAGE_ON_STACK(1); - msg_initWithFloat(n, msg_getTimestamp(m), (float) hTable_getSize(o)); - sendMessage(_c, 0, n); - } - - else if (msg_compareSymbol(m,0,"mirror")) { - hv_memcpy(o->buffer+o->size, o->buffer, HV_N_SIMD*sizeof(float)); - } -} diff --git a/examples/examples-dsp/examples-pd/streams-generator-pd-audiokit/HvTable.h b/examples/examples-dsp/examples-pd/streams-generator-pd-audiokit/HvTable.h deleted file mode 100644 index 1d748874ea..0000000000 --- a/examples/examples-dsp/examples-pd/streams-generator-pd-audiokit/HvTable.h +++ /dev/null @@ -1,88 +0,0 @@ -/** - * Copyright (c) 2014-2018 Enzien Audio Ltd. - * - * Permission to use, copy, modify, and/or distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH - * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY - * AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, - * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM - * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR - * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR - * PERFORMANCE OF THIS SOFTWARE. - */ - -#ifndef _HEAVY_TABLE_H_ -#define _HEAVY_TABLE_H_ - -#include "HvHeavy.h" -#include "HvUtils.h" - -#ifdef __cplusplus -extern "C" { -#endif - -typedef struct HvTable { - float *buffer; - // the number of values that the table is requested to have - hv_uint32_t length; - - // the number of usable values that the table actually has - // this is always an even multiple of HV_N_SIMD - hv_uint32_t size; - - // Note that the true size of the table is (size + HV_N_SIMD), - // with the trailing values used by the system, e.g. to create a circular - // buffer - hv_uint32_t allocated; - - hv_uint32_t head; // the most recently written point -} HvTable; - -hv_size_t hTable_init(HvTable *o, int length); - -hv_size_t hTable_initWithData(HvTable *o, int length, const float *data); - -hv_size_t hTable_initWithFinalData(HvTable *o, int length, float *data); - -void hTable_free(HvTable *o); - -int hTable_resize(HvTable *o, hv_uint32_t newLength); - -void hTable_onMessage(HeavyContextInterface *_c, HvTable *o, int letIn, const HvMessage *m, - void (*sendMessage)(HeavyContextInterface *, int, const HvMessage *)); - -static inline float *hTable_getBuffer(HvTable *o) { - return o->buffer; -} - -// the user-requested length of the table (number of floats) -static inline hv_uint32_t hTable_getLength(HvTable *o) { - return o->length; -} - -// the usable length of the table (an even multiple of HV_N_SIMD) -static inline hv_uint32_t hTable_getSize(HvTable *o) { - return o->size; -} - -// the number of floats allocated to this table (usually size + HV_N_SIMD) -static inline hv_uint32_t hTable_getAllocated(HvTable *o) { - return o->allocated; -} - -static inline hv_uint32_t hTable_getHead(HvTable *o) { - return o->head; -} - -static inline void hTable_setHead(HvTable *o, hv_uint32_t head) { - o->head = head; -} - -#ifdef __cplusplus -} // extern "C" -#endif - -#endif // _HEAVY_TABLE_H_ diff --git a/examples/examples-dsp/examples-pd/streams-generator-pd-audiokit/HvUtils.c b/examples/examples-dsp/examples-pd/streams-generator-pd-audiokit/HvUtils.c deleted file mode 100644 index 2fdf682c4c..0000000000 --- a/examples/examples-dsp/examples-pd/streams-generator-pd-audiokit/HvUtils.c +++ /dev/null @@ -1,54 +0,0 @@ -/** - * Copyright (c) 2014-2018 Enzien Audio Ltd. - * - * Permission to use, copy, modify, and/or distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH - * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY - * AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, - * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM - * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR - * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR - * PERFORMANCE OF THIS SOFTWARE. - */ - -#include "HvUtils.h" - -hv_uint32_t hv_string_to_hash(const char *str) { - // this hash is based MurmurHash2 - // http://en.wikipedia.org/wiki/MurmurHash - // https://sites.google.com/site/murmurhash/ - static const hv_uint32_t n = 0x5bd1e995; - static const hv_int32_t r = 24; - - if (str == NULL) return 0; - - hv_uint32_t len = (hv_uint32_t) hv_strlen(str); - hv_uint32_t x = len; // seed (0) ^ len - - while (len >= 4) { -#if HV_EMSCRIPTEN - hv_uint32_t k = str[0] | (str[1] << 8) | (str[2] << 16) | (str[3] << 24); -#else - hv_uint32_t k = *((hv_uint32_t *) str); -#endif - k *= n; - k ^= (k >> r); - k *= n; - x *= n; - x ^= k; - str += 4; len -= 4; - } - switch (len) { - case 3: x ^= (str[2] << 16); - case 2: x ^= (str[1] << 8); - case 1: x ^= str[0]; x *= n; - default: break; - } - x ^= (x >> 13); - x *= n; - x ^= (x >> 15); - return x; -} diff --git a/examples/examples-dsp/examples-pd/streams-generator-pd-audiokit/HvUtils.h b/examples/examples-dsp/examples-pd/streams-generator-pd-audiokit/HvUtils.h deleted file mode 100644 index 2186f547c0..0000000000 --- a/examples/examples-dsp/examples-pd/streams-generator-pd-audiokit/HvUtils.h +++ /dev/null @@ -1,319 +0,0 @@ -/** - * Copyright (c) 2014-2018 Enzien Audio Ltd. - * - * Permission to use, copy, modify, and/or distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH - * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY - * AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, - * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM - * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR - * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR - * PERFORMANCE OF THIS SOFTWARE. - */ - -#ifndef _HEAVY_UTILS_H_ -#define _HEAVY_UTILS_H_ - -// platform definitions -#if _WIN32 || _WIN64 || _MSC_VER - #define HV_WIN 1 -#elif __APPLE__ - #define HV_APPLE 1 -#elif __ANDROID__ - #define HV_ANDROID 1 -#elif __unix__ || __unix - #define HV_UNIX 1 -#else - #warning Could not detect platform. Assuming Unix-like. -#endif - -#ifdef EMSCRIPTEN -#define HV_EMSCRIPTEN 1 -#endif - -// basic includes -#include -#include -#include - -// type definitions -#include -#include -#define hv_uint8_t uint8_t -#define hv_int16_t int16_t -#define hv_uint16_t uint16_t -#define hv_int32_t int32_t -#define hv_uint32_t uint32_t -#define hv_uint64_t uint64_t -#define hv_size_t size_t -#define hv_uintptr_t uintptr_t - -// SIMD-specific includes -#if !(HV_SIMD_NONE || HV_SIMD_NEON || HV_SIMD_SSE || HV_SIMD_AVX) - #define HV_SIMD_NEON __ARM_NEON__ - #define HV_SIMD_SSE (__SSE__ && __SSE2__ && __SSE3__ && __SSSE3__ && __SSE4_1__) - #define HV_SIMD_AVX (__AVX__ && HV_SIMD_SSE) -#endif -#ifndef HV_SIMD_FMA - #define HV_SIMD_FMA __FMA__ -#endif - -#if HV_SIMD_AVX || HV_SIMD_SSE - #include -#elif HV_SIMD_NEON - #include -#endif - -#if HV_SIMD_NEON // NEON - #define HV_N_SIMD 4 - #define hv_bufferf_t float32x4_t - #define hv_bufferi_t int32x4_t - #define hv_bInf_t float32x4_t - #define hv_bOutf_t float32x4_t* - #define hv_bIni_t int32x4_t - #define hv_bOuti_t int32x4_t* - #define VIf(_x) (_x) - #define VOf(_x) (&_x) - #define VIi(_x) (_x) - #define VOi(_x) (&_x) -#elif HV_SIMD_AVX // AVX - #define HV_N_SIMD 8 - #define hv_bufferf_t __m256 - #define hv_bufferi_t __m256i - #define hv_bInf_t __m256 - #define hv_bOutf_t __m256* - #define hv_bIni_t __m256i - #define hv_bOuti_t __m256i* - #define VIf(_x) (_x) - #define VOf(_x) (&_x) - #define VIi(_x) (_x) - #define VOi(_x) (&_x) -#elif HV_SIMD_SSE // SSE - #define HV_N_SIMD 4 - #define hv_bufferf_t __m128 - #define hv_bufferi_t __m128i - #define hv_bInf_t __m128 - #define hv_bOutf_t __m128* - #define hv_bIni_t __m128i - #define hv_bOuti_t __m128i* - #define VIf(_x) (_x) - #define VOf(_x) (&_x) - #define VIi(_x) (_x) - #define VOi(_x) (&_x) -#else // DEFAULT - #define HV_N_SIMD 1 - #undef HV_SIMD_NONE - #define HV_SIMD_NONE 1 - #define hv_bufferf_t float - #define hv_bufferi_t int - #define hv_bInf_t float - #define hv_bOutf_t float* - #define hv_bIni_t int - #define hv_bOuti_t int* - #define VIf(_x) (_x) - #define VOf(_x) (&_x) - #define VIi(_x) (_x) - #define VOi(_x) (&_x) -#endif - -#define HV_N_SIMD_MASK (HV_N_SIMD-1) - -// Strings -#include -#define hv_strlen(a) strlen(a) -#define hv_strcmp(a, b) strcmp(a, b) -#define hv_snprintf(a, b, c, ...) snprintf(a, b, c, __VA_ARGS__) -#if HV_WIN -#define hv_strncpy(_dst, _src, _len) strncpy_s(_dst, _len, _src, _TRUNCATE) -#else -#define hv_strncpy(_dst, _src, _len) strncpy(_dst, _src, _len) -#endif - -// Memory management -#define hv_memcpy(a, b, c) memcpy(a, b, c) -#define hv_memclear(a, b) memset(a, 0, b) -#if HV_WIN - #include - #define hv_alloca(_n) _alloca(_n) - #if HV_SIMD_AVX - #define hv_malloc(_n) _aligned_malloc(_n, 32) - #define hv_realloc(a, b) _aligned_realloc(a, b, 32) - #define hv_free(x) _aligned_free(x) - #elif HV_SIMD_SSE || HV_SIMD_NEON - #define hv_malloc(_n) _aligned_malloc(_n, 16) - #define hv_realloc(a, b) _aligned_realloc(a, b, 16) - #define hv_free(x) _aligned_free(x) - #else // HV_SIMD_NONE - #define hv_malloc(_n) malloc(_n) - #define hv_realloc(a, b) realloc(a, b) - #define hv_free(_n) free(_n) - #endif -#elif HV_APPLE - #define hv_alloca(_n) alloca(_n) - #define hv_realloc(a, b) realloc(a, b) - #if HV_SIMD_AVX - #include - #define hv_malloc(_n) _mm_malloc(_n, 32) - #define hv_free(x) _mm_free(x) - #elif HV_SIMD_SSE - #include - #define hv_malloc(_n) _mm_malloc(_n, 16) - #define hv_free(x) _mm_free(x) - #elif HV_SIMD_NEON - // malloc on ios always has 16-byte alignment - #define hv_malloc(_n) malloc(_n) - #define hv_free(x) free(x) - #else // HV_SIMD_NONE - #define hv_malloc(_n) malloc(_n) - #define hv_free(x) free(x) - #endif -#else - #include - #define hv_alloca(_n) alloca(_n) - #define hv_realloc(a, b) realloc(a, b) - #if HV_SIMD_AVX - #define hv_malloc(_n) aligned_alloc(32, _n) - #define hv_free(x) free(x) - #elif HV_SIMD_SSE - #define hv_malloc(_n) aligned_alloc(16, _n) - #define hv_free(x) free(x) - #elif HV_SIMD_NEON - #if HV_ANDROID - #define hv_malloc(_n) memalign(16, _n) - #define hv_free(x) free(x) - #else - #define hv_malloc(_n) aligned_alloc(16, _n) - #define hv_free(x) free(x) - #endif - #else // HV_SIMD_NONE - #define hv_malloc(_n) malloc(_n) - #define hv_free(_n) free(_n) - #endif -#endif - -// Assert -#include -#define hv_assert(e) assert(e) - -// Export and Inline -#if HV_WIN -#define HV_EXPORT __declspec(dllexport) -#ifndef __cplusplus // MSVC doesn't like redefining "inline" keyword -#define inline __inline -#endif -#define HV_FORCE_INLINE __forceinline -#else -#define HV_EXPORT -#define HV_FORCE_INLINE inline __attribute__((always_inline)) -#endif - -#ifdef __cplusplus -extern "C" { -#endif - // Returns a 32-bit hash of any string. Returns 0 if string is NULL. - hv_uint32_t hv_string_to_hash(const char *str); -#ifdef __cplusplus -} -#endif - -// Math -#include -static inline hv_size_t __hv_utils_max_ui(hv_size_t x, hv_size_t y) { return (x > y) ? x : y; } -static inline hv_size_t __hv_utils_min_ui(hv_size_t x, hv_size_t y) { return (x < y) ? x : y; } -static inline hv_int32_t __hv_utils_max_i(hv_int32_t x, hv_int32_t y) { return (x > y) ? x : y; } -static inline hv_int32_t __hv_utils_min_i(hv_int32_t x, hv_int32_t y) { return (x < y) ? x : y; } -#define hv_max_ui(a, b) __hv_utils_max_ui(a, b) -#define hv_min_ui(a, b) __hv_utils_min_ui(a, b) -#define hv_max_i(a, b) __hv_utils_max_i(a, b) -#define hv_min_i(a, b) __hv_utils_min_i(a, b) -#define hv_max_f(a, b) fmaxf(a, b) -#define hv_min_f(a, b) fminf(a, b) -#define hv_max_d(a, b) fmax(a, b) -#define hv_min_d(a, b) fmin(a, b) -#define hv_sin_f(a) sinf(a) -#define hv_sinh_f(a) sinhf(a) -#define hv_cos_f(a) cosf(a) -#define hv_cosh_f(a) coshf(a) -#define hv_tan_f(a) tanf(a) -#define hv_tanh_f(a) tanhf(a) -#define hv_asin_f(a) asinf(a) -#define hv_asinh_f(a) asinhf(a) -#define hv_acos_f(a) acosf(a) -#define hv_acosh_f(a) acoshf(a) -#define hv_atan_f(a) atanf(a) -#define hv_atanh_f(a) atanhf(a) -#define hv_atan2_f(a, b) atan2f(a, b) -#define hv_exp_f(a) expf(a) -#define hv_abs_f(a) fabsf(a) -#define hv_sqrt_f(a) sqrtf(a) -#define hv_log_f(a) logf(a) -#define hv_ceil_f(a) ceilf(a) -#define hv_floor_f(a) floorf(a) -#define hv_round_f(a) roundf(a) -#define hv_pow_f(a, b) powf(a, b) -#if HV_EMSCRIPTEN -#define hv_fma_f(a, b, c) ((a*b)+c) // emscripten does not support fmaf (yet?) -#else -#define hv_fma_f(a, b, c) fmaf(a, b, c) -#endif -#if HV_WIN - // finds ceil(log2(x)) - #include - static inline hv_uint32_t __hv_utils_min_max_log2(hv_uint32_t x) { - unsigned long z = 0; - _BitScanReverse(&z, x); - return (hv_uint32_t) (z+1); - } -#else - static inline hv_uint32_t __hv_utils_min_max_log2(hv_uint32_t x) { - return (hv_uint32_t) (32 - __builtin_clz(x-1)); - } -#endif -#define hv_min_max_log2(a) __hv_utils_min_max_log2(a) - -// Atomics -#if HV_WIN - #include - #define hv_atomic_bool volatile LONG - #define HV_SPINLOCK_ACQUIRE(_x) while (InterlockedCompareExchange(&_x, true, false)) { } - #define HV_SPINLOCK_TRY(_x) return !InterlockedCompareExchange(&_x, true, false) - #define HV_SPINLOCK_RELEASE(_x) (_x = false) -#elif HV_ANDROID - // Android support for atomics isn't that great, we'll do it manually - // https://gcc.gnu.org/onlinedocs/gcc-4.1.2/gcc/Atomic-Builtins.html - #define hv_atomic_bool hv_uint8_t - #define HV_SPINLOCK_ACQUIRE(_x) while (__sync_lock_test_and_set(&_x, 1)) - #define HV_SPINLOCK_TRY(_x) return !__sync_lock_test_and_set(&_x, 1) - #define HV_SPINLOCK_RELEASE(_x) __sync_lock_release(&_x) -#elif __cplusplus - #include - #define hv_atomic_bool std::atomic_flag - #define HV_SPINLOCK_ACQUIRE(_x) while (_x.test_and_set(std::memory_order_acquire)) - #define HV_SPINLOCK_TRY(_x) return !_x.test_and_set(std::memory_order_acquire) - #define HV_SPINLOCK_RELEASE(_x) _x.clear(std::memory_order_release) -#elif defined(__has_include) - #if __has_include() - #include - #define hv_atomic_bool atomic_flag - #define HV_SPINLOCK_ACQUIRE(_x) while (atomic_flag_test_and_set_explicit(&_x, memory_order_acquire)) - #define HV_SPINLOCK_TRY(_x) return !atomic_flag_test_and_set_explicit(&_x, memory_order_acquire) - #define HV_SPINLOCK_RELEASE(_x) atomic_flag_clear_explicit(memory_order_release) - #endif -#endif -#ifndef hv_atomic_bool - #define hv_atomic_bool volatile bool - #define HV_SPINLOCK_ACQUIRE(_x) \ - while (_x) {} \ - _x = true; - #define HV_SPINLOCK_TRY(_x) \ - if (!_x) { \ - _x = true; \ - return true; \ - } else return false; - #define HV_SPINLOCK_RELEASE(_x) (_x = false) -#endif - -#endif // _HEAVY_UTILS_H_ diff --git a/examples/examples-dsp/examples-pd/streams-generator-pd-audiokit/adc2dac.pd b/examples/examples-dsp/examples-pd/streams-generator-pd-audiokit/adc2dac.pd deleted file mode 100644 index 710804d5bc..0000000000 --- a/examples/examples-dsp/examples-pd/streams-generator-pd-audiokit/adc2dac.pd +++ /dev/null @@ -1,5 +0,0 @@ -#N canvas 0 0 450 300 12; -#X obj 93 88 adc~; -#X obj 93 173 dac~; -#X connect 0 0 1 0; -#X connect 0 1 1 1; diff --git a/examples/examples-dsp/examples-pd/streams-generator-pd-audiokit/streams-generator-pd-audiokit.ino b/examples/examples-dsp/examples-pd/streams-generator-pd-audiokit/streams-generator-pd-audiokit.ino deleted file mode 100644 index fff1ae6d9f..0000000000 --- a/examples/examples-dsp/examples-pd/streams-generator-pd-audiokit/streams-generator-pd-audiokit.ino +++ /dev/null @@ -1,46 +0,0 @@ -/** - * Test sketch for adc2dac.pd that was compiled with hvcc -n Adc2Dac adc2dac.pd - * The ADC receives data from the SineWaveGenerator and the output from the - * DAC is copied to I2S (AudioBoardStream) - */ - -#include "AudioTools.h" -#include "AudioTools/AudioLibs/AudioBoardStream.h" -#include "Heavy_Adc2Dac.hpp" -#include "AudioTools/AudioLibs/PureDataStream.h" - -AudioInfo info(44100, 2, 16); -Heavy_Adc2Dac pd_test(info.sample_rate); -PureDataStream pd(pd_test); -AudioBoardStream i2s(AudioKitEs8388V1); // final output of decoded stream -SineWaveGenerator sineWave; // subclass of SoundGenerator with max amplitude of 32000 -GeneratedSoundStream sound(sineWave); // Stream generated from sine wave - -StreamCopy copierToPd(pd, sound, 1024); -StreamCopy copierFromPd(i2s, pd, 1024); - -void setup() { - Serial.begin(115200); - AudioToolsLogger.begin(Serial, AudioToolsLogLevel::Warning); - - // setup output - auto config = i2s.defaultConfig(TX_MODE); - config.sd_active = false; - config.copyFrom(info); - i2s.begin(config); - - // setup input - sineWave.begin(info, N_B4); - - // setup pd - pd.begin(); - -} - -void loop() { - // provide data to adc - copierToPd.copy(); - // get data from dac - copierFromPd.copy(); -} - diff --git a/examples/examples-dsp/examples-pd/streams-pd-audiokit/HeavyContext.cpp b/examples/examples-dsp/examples-pd/streams-pd-audiokit/HeavyContext.cpp deleted file mode 100644 index 88fa134215..0000000000 --- a/examples/examples-dsp/examples-pd/streams-pd-audiokit/HeavyContext.cpp +++ /dev/null @@ -1,264 +0,0 @@ -/** - * Copyright (c) 2014-2018 Enzien Audio Ltd. - * - * Permission to use, copy, modify, and/or distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH - * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY - * AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, - * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM - * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR - * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR - * PERFORMANCE OF THIS SOFTWARE. - */ - -#include "HeavyContext.hpp" -#include "HvTable.h" - -void defaultSendHook(HeavyContextInterface *context, - const char *sendName, hv_uint32_t sendHash, const HvMessage *msg) { - HeavyContext *thisContext = reinterpret_cast(context); - const hv_uint32_t numBytes = sizeof(ReceiverMessagePair) + msg_getSize(msg) - sizeof(HvMessage); - ReceiverMessagePair *p = reinterpret_cast(hLp_getWriteBuffer(&thisContext->outQueue, numBytes)); - if (p != nullptr) { - p->receiverHash = sendHash; - msg_copyToBuffer(msg, (char *) &p->msg, msg_getSize(msg)); - hLp_produce(&thisContext->outQueue, numBytes); - } else { - hv_assert(false && - "::defaultSendHook - The out message queue is full and cannot accept more messages until they " - "have been processed. Try increasing the outQueueKb size in the new_with_options() constructor."); - } -} - -HeavyContext::HeavyContext(double sampleRate, int poolKb, int inQueueKb, int outQueueKb) : - sampleRate(sampleRate) { - - hv_assert(sampleRate > 0.0); // sample rate must be positive - hv_assert(poolKb > 0); - hv_assert(inQueueKb > 0); - hv_assert(outQueueKb >= 0); - - blockStartTimestamp = 0; - printHook = nullptr; - userData = nullptr; - - // if outQueueKb is positive, then the outQueue is allocated and the default sendhook is set. - // Otherwise outQueue and the sendhook are set to NULL. - sendHook = (outQueueKb > 0) ? &defaultSendHook : nullptr; - - HV_SPINLOCK_RELEASE(inQueueLock); - HV_SPINLOCK_RELEASE(outQueueLock); - - numBytes = sizeof(HeavyContext); - - numBytes += mq_initWithPoolSize(&mq, poolKb); - numBytes += hLp_init(&inQueue, inQueueKb * 1024); - numBytes += hLp_init(&outQueue, outQueueKb * 1024); // outQueueKb value of 0 sets everything to NULL -} - -HeavyContext::~HeavyContext() { - mq_free(&mq); - hLp_free(&inQueue); - hLp_free(&outQueue); -} - -bool HeavyContext::sendBangToReceiver(hv_uint32_t receiverHash) { - HvMessage *m = HV_MESSAGE_ON_STACK(1); - msg_initWithBang(m, 0); - bool success = sendMessageToReceiver(receiverHash, 0.0, m); - return success; -} - -bool HeavyContext::sendFloatToReceiver(hv_uint32_t receiverHash, float f) { - HvMessage *m = HV_MESSAGE_ON_STACK(1); - msg_initWithFloat(m, 0, f); - bool success = sendMessageToReceiver(receiverHash, 0.0, m); - return success; -} - -bool HeavyContext::sendSymbolToReceiver(hv_uint32_t receiverHash, const char *s) { - hv_assert(s != nullptr); - HvMessage *m = HV_MESSAGE_ON_STACK(1); - msg_initWithSymbol(m, 0, (char *) s); - bool success = sendMessageToReceiver(receiverHash, 0.0, m); - return success; -} - -bool HeavyContext::sendMessageToReceiverV(hv_uint32_t receiverHash, double delayMs, const char *format, ...) { - hv_assert(delayMs >= 0.0); - hv_assert(format != nullptr); - - va_list ap; - va_start(ap, format); - const int numElem = (int) hv_strlen(format); - HvMessage *m = HV_MESSAGE_ON_STACK(numElem); - msg_init(m, numElem, blockStartTimestamp + (hv_uint32_t) (hv_max_d(0.0, delayMs)*getSampleRate()/1000.0)); - for (int i = 0; i < numElem; i++) { - switch (format[i]) { - case 'b': msg_setBang(m, i); break; - case 'f': msg_setFloat(m, i, (float) va_arg(ap, double)); break; - case 'h': msg_setHash(m, i, (int) va_arg(ap, int)); break; - case 's': msg_setSymbol(m, i, (char *) va_arg(ap, char *)); break; - default: break; - } - } - va_end(ap); - - bool success = sendMessageToReceiver(receiverHash, delayMs, m); - return success; -} - -bool HeavyContext::sendMessageToReceiver(hv_uint32_t receiverHash, double delayMs, HvMessage *m) { - hv_assert(delayMs >= 0.0); - hv_assert(m != nullptr); - - const hv_uint32_t timestamp = blockStartTimestamp + - (hv_uint32_t) (hv_max_d(0.0, delayMs)*(getSampleRate()/1000.0)); - - ReceiverMessagePair *p = nullptr; - HV_SPINLOCK_ACQUIRE(inQueueLock); - const hv_uint32_t numBytes = sizeof(ReceiverMessagePair) + msg_getSize(m) - sizeof(HvMessage); - p = (ReceiverMessagePair *) hLp_getWriteBuffer(&inQueue, numBytes); - if (p != nullptr) { - p->receiverHash = receiverHash; - msg_copyToBuffer(m, (char *) &p->msg, msg_getSize(m)); - msg_setTimestamp(&p->msg, timestamp); - hLp_produce(&inQueue, numBytes); - } else { - hv_assert(false && - "::sendMessageToReceiver - The input message queue is full and cannot accept more messages until they " - "have been processed. Try increasing the inQueueKb size in the new_with_options() constructor."); - } - HV_SPINLOCK_RELEASE(inQueueLock); - return (p != nullptr); -} - -bool HeavyContext::cancelMessage(HvMessage *m, void (*sendMessage)(HeavyContextInterface *, int, const HvMessage *)) { - return mq_removeMessage(&mq, m, sendMessage); -} - -HvMessage *HeavyContext::scheduleMessageForObject(const HvMessage *m, - void (*sendMessage)(HeavyContextInterface *, int, const HvMessage *), - int letIndex) { - HvMessage *n = mq_addMessageByTimestamp(&mq, m, letIndex, sendMessage); - return n; -} - -float *HeavyContext::getBufferForTable(hv_uint32_t tableHash) { - HvTable *t = getTableForHash(tableHash); - if (t != nullptr) { - return hTable_getBuffer(t); - } else return nullptr; -} - -int HeavyContext::getLengthForTable(hv_uint32_t tableHash) { - HvTable *t = getTableForHash(tableHash); - if (t != nullptr) { - return hTable_getLength(t); - } else return 0; -} - -bool HeavyContext::setLengthForTable(hv_uint32_t tableHash, hv_uint32_t newSampleLength) { - HvTable *t = getTableForHash(tableHash); - if (t != nullptr) { - hTable_resize(t, newSampleLength); - return true; - } else return false; -} - -void HeavyContext::lockAcquire() { - HV_SPINLOCK_ACQUIRE(inQueueLock); -} - -bool HeavyContext::lockTry() { - HV_SPINLOCK_TRY(inQueueLock); -} - -void HeavyContext::lockRelease() { - HV_SPINLOCK_RELEASE(inQueueLock); -} - -void HeavyContext::setInputMessageQueueSize(int inQueueKb) { - hv_assert(inQueueKb > 0); - hLp_free(&inQueue); - hLp_init(&inQueue, inQueueKb*1024); -} - -void HeavyContext::setOutputMessageQueueSize(int outQueueKb) { - hv_assert(outQueueKb > 0); - hLp_free(&outQueue); - hLp_init(&outQueue, outQueueKb*1024); -} - -bool HeavyContext::getNextSentMessage(hv_uint32_t *destinationHash, HvMessage *outMsg, hv_size_t msgLengthBytes) { - *destinationHash = 0; - ReceiverMessagePair *p = nullptr; - hv_assert((sendHook == &defaultSendHook) && - "::getNextSentMessage - this function won't do anything if the msg outQueue " - "size is 0, or you've overriden the default sendhook."); - if (sendHook == &defaultSendHook) { - HV_SPINLOCK_ACQUIRE(outQueueLock); - if (hLp_hasData(&outQueue)) { - hv_uint32_t numBytes = 0; - p = reinterpret_cast(hLp_getReadBuffer(&outQueue, &numBytes)); - hv_assert((p != nullptr) && "::getNextSentMessage - something bad happened."); - hv_assert(numBytes >= sizeof(ReceiverMessagePair)); - hv_assert((numBytes <= msgLengthBytes) && - "::getNextSentMessage - the sent message is bigger than the message " - "passed to handle it."); - *destinationHash = p->receiverHash; - hv_memcpy(outMsg, &p->msg, numBytes); - hLp_consume(&outQueue); - } - HV_SPINLOCK_RELEASE(outQueueLock); - } - return (p != nullptr); -} - -hv_uint32_t HeavyContext::getHashForString(const char *str) { - return hv_string_to_hash(str); -} - -HvTable *_hv_table_get(HeavyContextInterface *c, hv_uint32_t tableHash) { - hv_assert(c != nullptr); - return reinterpret_cast(c)->getTableForHash(tableHash); -} - -void _hv_scheduleMessageForReceiver(HeavyContextInterface *c, hv_uint32_t receiverHash, HvMessage *m) { - hv_assert(c != nullptr); - reinterpret_cast(c)->scheduleMessageForReceiver(receiverHash, m); -} - -HvMessage *_hv_scheduleMessageForObject(HeavyContextInterface *c, const HvMessage *m, - void (*sendMessage)(HeavyContextInterface *, int, const HvMessage *), - int letIndex) { - hv_assert(c != nullptr); - HvMessage *n = reinterpret_cast(c)->scheduleMessageForObject( - m, sendMessage, letIndex); - return n; -} - -#ifdef __cplusplus -extern "C" { -#endif - -HvTable *hv_table_get(HeavyContextInterface *c, hv_uint32_t tableHash) { - return _hv_table_get(c, tableHash); -} - -void hv_scheduleMessageForReceiver(HeavyContextInterface *c, hv_uint32_t receiverHash, HvMessage *m) { - _hv_scheduleMessageForReceiver(c, receiverHash, m); -} - -HvMessage *hv_scheduleMessageForObject(HeavyContextInterface *c, const HvMessage *m, - void (*sendMessage)(HeavyContextInterface *, int, const HvMessage *), - int letIndex) { - return _hv_scheduleMessageForObject(c, m, sendMessage, letIndex); -} - -#ifdef __cplusplus -} -#endif diff --git a/examples/examples-dsp/examples-pd/streams-pd-audiokit/HeavyContext.hpp b/examples/examples-dsp/examples-pd/streams-pd-audiokit/HeavyContext.hpp deleted file mode 100644 index 87b95d74c2..0000000000 --- a/examples/examples-dsp/examples-pd/streams-pd-audiokit/HeavyContext.hpp +++ /dev/null @@ -1,107 +0,0 @@ -/** - * Copyright (c) 2014-2018 Enzien Audio Ltd. - * - * Permission to use, copy, modify, and/or distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH - * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY - * AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, - * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM - * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR - * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR - * PERFORMANCE OF THIS SOFTWARE. - */ - -#ifndef _HEAVY_CONTEXT_H_ -#define _HEAVY_CONTEXT_H_ - -#include "HeavyContextInterface.hpp" -#include "HvLightPipe.h" -#include "HvMessageQueue.h" -#include "HvMath.h" - -struct HvTable; - -class HeavyContext : public HeavyContextInterface { - - public: - HeavyContext(double sampleRate, int poolKb=10, int inQueueKb=2, int outQueueKb=0); - virtual ~HeavyContext(); - - int getSize() override { return (int) numBytes; } - - double getSampleRate() override { return sampleRate; } - - hv_uint32_t getCurrentSample() override { return blockStartTimestamp; } - float samplesToMilliseconds(hv_uint32_t numSamples) override { return (float) (1000.0*numSamples/sampleRate); } - hv_uint32_t millisecondsToSamples(float ms) override { return (hv_uint32_t) (hv_max_f(0.0f,ms)*sampleRate/1000.0); } - - void setUserData(void *x) override { userData = x; } - void *getUserData() override { return userData; } - - // hook management - void setSendHook(HvSendHook_t *f) override { sendHook = f; } - HvSendHook_t *getSendHook() override { return sendHook; } - - void setPrintHook(HvPrintHook_t *f) override { printHook = f; } - HvPrintHook_t *getPrintHook() override { return printHook; } - - // message scheduling - bool sendMessageToReceiver(hv_uint32_t receiverHash, double delayMs, HvMessage *m) override; - bool sendMessageToReceiverV(hv_uint32_t receiverHash, double delayMs, const char *fmt, ...) override; - bool sendFloatToReceiver(hv_uint32_t receiverHash, float f) override; - bool sendBangToReceiver(hv_uint32_t receiverHash) override; - bool sendSymbolToReceiver(hv_uint32_t receiverHash, const char *symbol) override; - bool cancelMessage(HvMessage *m, void (*sendMessage)(HeavyContextInterface *, int, const HvMessage *)) override; - - // table manipulation - float *getBufferForTable(hv_uint32_t tableHash) override; - int getLengthForTable(hv_uint32_t tableHash) override; - bool setLengthForTable(hv_uint32_t tableHash, hv_uint32_t newSampleLength) override; - - // lock control - void lockAcquire() override; - bool lockTry() override; - void lockRelease() override; - - // message queue management - void setInputMessageQueueSize(int inQueueKb) override; - void setOutputMessageQueueSize(int outQueueKb) override; - bool getNextSentMessage(hv_uint32_t *destinationHash, HvMessage *outMsg, hv_size_t msgLength) override; - - // utility functions - static hv_uint32_t getHashForString(const char *str); - - protected: - virtual HvTable *getTableForHash(hv_uint32_t tableHash) = 0; - friend HvTable *_hv_table_get(HeavyContextInterface *, hv_uint32_t); - - virtual void scheduleMessageForReceiver(hv_uint32_t receiverHash, HvMessage *m) = 0; - friend void _hv_scheduleMessageForReceiver(HeavyContextInterface *, hv_uint32_t, HvMessage *); - - HvMessage *scheduleMessageForObject(const HvMessage *, - void (*sendMessage)(HeavyContextInterface *, int, const HvMessage *), - int); - friend HvMessage *_hv_scheduleMessageForObject(HeavyContextInterface *, const HvMessage *, - void (*sendMessage)(HeavyContextInterface *, int, const HvMessage *), - int); - - friend void defaultSendHook(HeavyContextInterface *, const char *, hv_uint32_t, const HvMessage *); - - // object state - double sampleRate; - hv_uint32_t blockStartTimestamp; - hv_size_t numBytes; - HvMessageQueue mq; - HvSendHook_t *sendHook; - HvPrintHook_t *printHook; - void *userData; - HvLightPipe inQueue; - HvLightPipe outQueue; - hv_atomic_bool inQueueLock; - hv_atomic_bool outQueueLock; -}; - -#endif // _HEAVY_CONTEXT_H_ diff --git a/examples/examples-dsp/examples-pd/streams-pd-audiokit/HeavyContextInterface.hpp b/examples/examples-dsp/examples-pd/streams-pd-audiokit/HeavyContextInterface.hpp deleted file mode 100644 index a78fcbf396..0000000000 --- a/examples/examples-dsp/examples-pd/streams-pd-audiokit/HeavyContextInterface.hpp +++ /dev/null @@ -1,291 +0,0 @@ -/** - * Copyright (c) 2014-2018 Enzien Audio Ltd. - * - * Permission to use, copy, modify, and/or distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH - * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY - * AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, - * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM - * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR - * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR - * PERFORMANCE OF THIS SOFTWARE. - */ - -#ifndef _HEAVY_CONTEXT_INTERFACE_H_ -#define _HEAVY_CONTEXT_INTERFACE_H_ - -#include "HvUtils.h" - -#ifndef _HEAVY_DECLARATIONS_ -#define _HEAVY_DECLARATIONS_ - -class HeavyContextInterface; -struct HvMessage; - -typedef enum { - HV_PARAM_TYPE_PARAMETER_IN, - HV_PARAM_TYPE_PARAMETER_OUT, - HV_PARAM_TYPE_EVENT_IN, - HV_PARAM_TYPE_EVENT_OUT -} HvParameterType; - -typedef struct HvParameterInfo { - const char *name; // the human readable parameter name - hv_uint32_t hash; // an integer identified used by heavy for this parameter - HvParameterType type; // type of this parameter - float minVal; // the minimum value of this parameter - float maxVal; // the maximum value of this parameter - float defaultVal; // the default value of this parameter -} HvParameterInfo; - -typedef void (HvSendHook_t) (HeavyContextInterface *context, const char *sendName, hv_uint32_t sendHash, const HvMessage *msg); -typedef void (HvPrintHook_t) (HeavyContextInterface *context, const char *printName, const char *str, const HvMessage *msg); - -#endif // _HEAVY_DECLARATIONS_ - - - -class HeavyContextInterface { - - public: - HeavyContextInterface() {} - virtual ~HeavyContextInterface() {}; - - /** Returns the read-only user-assigned name of this patch. */ - virtual const char *getName() = 0; - - /** Returns the number of input channels with which this context has been configured. */ - virtual int getNumInputChannels() = 0; - - /** Returns the number of output channels with which this context has been configured. */ - virtual int getNumOutputChannels() = 0; - - /** - * Returns the total size in bytes of the context. - * This value may change if tables are resized. - */ - virtual int getSize() = 0; - - /** Returns the sample rate with which this context has been configured. */ - virtual double getSampleRate() = 0; - - /** Returns the current patch time in samples. This value is always exact. */ - virtual hv_uint32_t getCurrentSample() = 0; - virtual float samplesToMilliseconds(hv_uint32_t numSamples) = 0; - - /** Converts milliseconds to samples. Input is limited to non-negative range. */ - virtual hv_uint32_t millisecondsToSamples(float ms) = 0; - - /** Sets a user-definable value. This value is never manipulated by Heavy. */ - virtual void setUserData(void *x) = 0; - - /** Returns the user-defined data. */ - virtual void *getUserData() = 0; - - /** - * Set the send hook. The function is called whenever a message is sent to any send object. - * Messages returned by this function should NEVER be freed. If the message must persist, call - * hv_msg_copy() first. - */ - virtual void setSendHook(HvSendHook_t *f) = 0; - - /** Returns the send hook, or NULL if unset. */ - virtual HvSendHook_t *getSendHook() = 0; - - /** Set the print hook. The function is called whenever a message is sent to a print object. */ - virtual void setPrintHook(HvPrintHook_t *f) = 0; - - /** Returns the print hook, or NULL if unset. */ - virtual HvPrintHook_t *getPrintHook() = 0; - - /** - * Processes one block of samples for a patch instance. The buffer format is an array of float channel arrays. - * If the context has not input or output channels, the respective argument may be NULL. - * The number of samples to to tbe processed should be a multiple of 1, 4, or 8, depending on if - * no, SSE or NEON, or AVX optimisation is being used, respectively. - * e.g. [[LLLL][RRRR]] - * - * @return The number of samples processed. - * - * This function is NOT thread-safe. It is assumed that only the audio thread will execute this function. - */ - virtual int process(float **inputBuffers, float **outputBuffer, int n) = 0; - - /** - * Processes one block of samples for a patch instance. The buffer format is an uninterleaved float array of channels. - * If the context has not input or output channels, the respective argument may be NULL. - * The number of samples to to tbe processed should be a multiple of 1, 4, or 8, depending on if - * no, SSE or NEON, or AVX optimisation is being used, respectively. - * e.g. [LLLLRRRR] - * - * @return The number of samples processed. - * - * This function is NOT thread-safe. It is assumed that only the audio thread will execute this function. - */ - virtual int processInline(float *inputBuffers, float *outputBuffer, int n) = 0; - - /** - * Processes one block of samples for a patch instance. The buffer format is an interleaved float array of channels. - * If the context has not input or output channels, the respective argument may be NULL. - * The number of samples to to tbe processed should be a multiple of 1, 4, or 8, depending on if - * no, SSE or NEON, or AVX optimisation is being used, respectively. - * e.g. [LRLRLRLR] - * - * @return The number of samples processed. - * - * This function is NOT thread-safe. It is assumed that only the audio thread will execute this function. - */ - virtual int processInlineInterleaved(float *inputBuffers, float *outputBuffer, int n) = 0; - - /** - * Sends a formatted message to a receiver that can be scheduled for the future. - * The receiver is addressed with its hash, which can also be determined using hv_stringToHash(). - * This function is thread-safe. - * - * @return True if the message was accepted. False if the message could not fit onto - * the message queue to be processed this block. - */ - virtual bool sendMessageToReceiver(hv_uint32_t receiverHash, double delayMs, HvMessage *m) = 0; - - /** - * Sends a formatted message to a receiver that can be scheduled for the future. - * The receiver is addressed with its hash, which can also be determined using hv_stringToHash(). - * This function is thread-safe. - * - * @return True if the message was accepted. False if the message could not fit onto - * the message queue to be processed this block. - */ - virtual bool sendMessageToReceiverV(hv_uint32_t receiverHash, double delayMs, const char *fmt, ...) = 0; - - /** - * A convenience function to send a float to a receiver to be processed immediately. - * The receiver is addressed with its hash, which can also be determined using hv_stringToHash(). - * This function is thread-safe. - * - * @return True if the message was accepted. False if the message could not fit onto - * the message queue to be processed this block. - */ - virtual bool sendFloatToReceiver(hv_uint32_t receiverHash, float f) = 0; - - /** - * A convenience function to send a bang to a receiver to be processed immediately. - * The receiver is addressed with its hash, which can also be determined using hv_stringToHash(). - * This function is thread-safe. - * - * @return True if the message was accepted. False if the message could not fit onto - * the message queue to be processed this block. - */ - virtual bool sendBangToReceiver(hv_uint32_t receiverHash) = 0; - - /** - * A convenience function to send a symbol to a receiver to be processed immediately. - * The receiver is addressed with its hash, which can also be determined using hv_stringToHash(). - * This function is thread-safe. - * - * @return True if the message was accepted. False if the message could not fit onto - * the message queue to be processed this block. - */ - virtual bool sendSymbolToReceiver(hv_uint32_t receiverHash, const char *symbol) = 0; - - /** - * Cancels a previously scheduled message. - * - * @param sendMessage May be NULL. - */ - virtual bool cancelMessage(HvMessage *m, void (*sendMessage)(HeavyContextInterface *, int, const HvMessage *)=nullptr) = 0; - - /** - * Returns information about each parameter such as name, hash, and range. - * The total number of parameters is always returned. - * - * @param index The parameter index. - * @param info A pointer to a HvParameterInfo struct. May be null. - * - * @return The total number of parameters. - */ - virtual int getParameterInfo(int index, HvParameterInfo *info) = 0; - - /** Returns a pointer to the raw buffer backing this table. DO NOT free it. */ - virtual float *getBufferForTable(hv_uint32_t tableHash) = 0; - - /** Returns the length of this table in samples. */ - virtual int getLengthForTable(hv_uint32_t tableHash) = 0; - - /** - * Resizes the table to the given length. - * - * Existing contents are copied to the new table. Remaining space is cleared - * if the table is longer than the original, truncated otherwise. - * - * @param tableHash The table identifier. - * @param newSampleLength The new length of the table, in samples. - * - * @return False if the table could not be found. True otherwise. - */ - virtual bool setLengthForTable(hv_uint32_t tableHash, hv_uint32_t newSampleLength) = 0; - - /** - * Acquire the input message queue lock. - * - * This function will block until the message lock as been acquired. - * Typical applications will not require the use of this function. - */ - virtual void lockAcquire() = 0; - - /** - * Try to acquire the input message queue lock. - * - * If the lock has been acquired, hv_lock_release() must be called to release it. - * Typical applications will not require the use of this function. - * - * @return Returns true if the lock has been acquired, false otherwise. - */ - virtual bool lockTry() = 0; - - /** - * Release the input message queue lock. - * - * Typical applications will not require the use of this function. - */ - virtual void lockRelease() = 0; - - /** - * Set the size of the input message queue in kilobytes. - * - * The buffer is reset and all existing contents are lost on resize. - * - * @param inQueueKb Must be positive i.e. at least one. - */ - virtual void setInputMessageQueueSize(int inQueueKb) = 0; - - /** - * Set the size of the output message queue in kilobytes. - * - * The buffer is reset and all existing contents are lost on resize. - * Only the default sendhook uses the outgoing message queue. If the default - * sendhook is not being used, then this function is not useful. - * - * @param outQueueKb Must be postive i.e. at least one. - */ - virtual void setOutputMessageQueueSize(int outQueueKb) = 0; - - /** - * Get the next message in the outgoing queue, will also consume the message. - * Returns false if there are no messages. - * - * @param destinationHash a hash of the name of the receiver the message was sent to. - * @param outMsg message pointer that is filled by the next message contents. - * @param msgLengthBytes max length of outMsg in bytes. - * - * @return True if there is a message in the outgoing queue. - */ - virtual bool getNextSentMessage(hv_uint32_t *destinationHash, HvMessage *outMsg, hv_size_t msgLengthBytes) = 0; - - /** Returns a 32-bit hash of any string. Returns 0 if string is NULL. */ - static hv_uint32_t getHashForString(const char *str); -}; - -#endif // _HEAVY_CONTEXT_INTERFACE_H_ diff --git a/examples/examples-dsp/examples-pd/streams-pd-audiokit/Heavy_test.cpp b/examples/examples-dsp/examples-pd/streams-pd-audiokit/Heavy_test.cpp deleted file mode 100644 index 65cd042e34..0000000000 --- a/examples/examples-dsp/examples-pd/streams-pd-audiokit/Heavy_test.cpp +++ /dev/null @@ -1,264 +0,0 @@ -/** - * Copyright (c) 2024 Enzien Audio, Ltd. - * - * Redistribution and use in source and binary forms, with or without modification, - * are permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright notice, - * this list of conditions, and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the phrase "powered by heavy", - * the heavy logo, and a hyperlink to https://enzienaudio.com, all in a visible - * form. - * - * 2.1 If the Application is distributed in a store system (for example, - * the Apple "App Store" or "Google Play"), the phrase "powered by heavy" - * shall be included in the app description or the copyright text as well as - * the in the app itself. The heavy logo will shall be visible in the app - * itself as well. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, - * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR - * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER - * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, - * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF - * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - */ - -#include "Heavy_test.hpp" - -#include - -#define Context(_c) static_cast(_c) - - -/* - * C Functions - */ - -extern "C" { - HV_EXPORT HeavyContextInterface *hv_test_new(double sampleRate) { - // allocate aligned memory - void *ptr = hv_malloc(sizeof(Heavy_test)); - // ensure non-null - if (!ptr) return nullptr; - // call constructor - new(ptr) Heavy_test(sampleRate); - return Context(ptr); - } - - HV_EXPORT HeavyContextInterface *hv_test_new_with_options(double sampleRate, - int poolKb, int inQueueKb, int outQueueKb) { - // allocate aligned memory - void *ptr = hv_malloc(sizeof(Heavy_test)); - // ensure non-null - if (!ptr) return nullptr; - // call constructor - new(ptr) Heavy_test(sampleRate, poolKb, inQueueKb, outQueueKb); - return Context(ptr); - } - - HV_EXPORT void hv_test_free(HeavyContextInterface *instance) { - // call destructor - Context(instance)->~Heavy_test(); - // free memory - hv_free(instance); - } -} // extern "C" - - - - - - - -/* - * Class Functions - */ - -Heavy_test::Heavy_test(double sampleRate, int poolKb, int inQueueKb, int outQueueKb) - : HeavyContext(sampleRate, poolKb, inQueueKb, outQueueKb) { - numBytes += sPhasor_k_init(&sPhasor_GM4o5ge7, 220.0f, sampleRate); - -} - -Heavy_test::~Heavy_test() { - // nothing to free -} - -HvTable *Heavy_test::getTableForHash(hv_uint32_t tableHash) { - return nullptr; -} - -void Heavy_test::scheduleMessageForReceiver(hv_uint32_t receiverHash, HvMessage *m) { - switch (receiverHash) { - default: return; - } -} - -int Heavy_test::getParameterInfo(int index, HvParameterInfo *info) { - if (info != nullptr) { - switch (index) { - default: { - info->name = "invalid parameter index"; - info->hash = 0; - info->type = HvParameterType::HV_PARAM_TYPE_PARAMETER_IN; - info->minVal = 0.0f; - info->maxVal = 0.0f; - info->defaultVal = 0.0f; - break; - } - } - } - return 0; -} - - - -/* - * Send Function Implementations - */ - - - - - -/* - * Context Process Implementation - */ - -int Heavy_test::process(float **inputBuffers, float **outputBuffers, int n) { - while (hLp_hasData(&inQueue)) { - hv_uint32_t numBytes = 0; - ReceiverMessagePair *p = reinterpret_cast(hLp_getReadBuffer(&inQueue, &numBytes)); - hv_assert(numBytes >= sizeof(ReceiverMessagePair)); - scheduleMessageForReceiver(p->receiverHash, &p->msg); - hLp_consume(&inQueue); - } - - sendBangToReceiver(0xDD21C0EB); // send to __hv_bang~ on next cycle - const int n4 = n & ~HV_N_SIMD_MASK; // ensure that the block size is a multiple of HV_N_SIMD - - // temporary signal vars - hv_bufferf_t Bf0, Bf1, Bf2, Bf3, Bf4; - - // input and output vars - hv_bufferf_t O0, O1; - - // declare and init the zero buffer - hv_bufferf_t ZERO; __hv_zero_f(VOf(ZERO)); - - hv_uint32_t nextBlock = blockStartTimestamp; - for (int n = 0; n < n4; n += HV_N_SIMD) { - - // process all of the messages for this block - nextBlock += HV_N_SIMD; - while (mq_hasMessageBefore(&mq, nextBlock)) { - MessageNode *const node = mq_peek(&mq); - node->sendMessage(this, node->let, node->m); - mq_pop(&mq); - } - - - - // zero output buffers - __hv_zero_f(VOf(O0)); - __hv_zero_f(VOf(O1)); - - // process all signal functions - __hv_phasor_k_f(&sPhasor_GM4o5ge7, VOf(Bf0)); - __hv_var_k_f(VOf(Bf1), 0.5f, 0.5f, 0.5f, 0.5f, 0.5f, 0.5f, 0.5f, 0.5f); - __hv_sub_f(VIf(Bf0), VIf(Bf1), VOf(Bf1)); - __hv_abs_f(VIf(Bf1), VOf(Bf1)); - __hv_var_k_f(VOf(Bf0), 0.25f, 0.25f, 0.25f, 0.25f, 0.25f, 0.25f, 0.25f, 0.25f); - __hv_sub_f(VIf(Bf1), VIf(Bf0), VOf(Bf0)); - __hv_var_k_f(VOf(Bf1), 6.283185307179586f, 6.283185307179586f, 6.283185307179586f, 6.283185307179586f, 6.283185307179586f, 6.283185307179586f, 6.283185307179586f, 6.283185307179586f); - __hv_mul_f(VIf(Bf0), VIf(Bf1), VOf(Bf1)); - __hv_mul_f(VIf(Bf1), VIf(Bf1), VOf(Bf0)); - __hv_mul_f(VIf(Bf1), VIf(Bf0), VOf(Bf2)); - __hv_mul_f(VIf(Bf2), VIf(Bf0), VOf(Bf0)); - __hv_var_k_f(VOf(Bf3), 0.007833333333333f, 0.007833333333333f, 0.007833333333333f, 0.007833333333333f, 0.007833333333333f, 0.007833333333333f, 0.007833333333333f, 0.007833333333333f); - __hv_var_k_f(VOf(Bf4), -0.166666666666667f, -0.166666666666667f, -0.166666666666667f, -0.166666666666667f, -0.166666666666667f, -0.166666666666667f, -0.166666666666667f, -0.166666666666667f); - __hv_fma_f(VIf(Bf2), VIf(Bf4), VIf(Bf1), VOf(Bf1)); - __hv_fma_f(VIf(Bf0), VIf(Bf3), VIf(Bf1), VOf(Bf1)); - __hv_add_f(VIf(Bf1), VIf(O0), VOf(O0)); - - // save output vars to output buffer - __hv_store_f(outputBuffers[0]+n, VIf(O0)); - __hv_store_f(outputBuffers[1]+n, VIf(O1)); - } - - blockStartTimestamp = nextBlock; - - return n4; // return the number of frames processed - -} - -int Heavy_test::processInline(float *inputBuffers, float *outputBuffers, int n4) { - hv_assert(!(n4 & HV_N_SIMD_MASK)); // ensure that n4 is a multiple of HV_N_SIMD - - // define the heavy input buffer for 0 channel(s) - float **const bIn = NULL; - - // define the heavy output buffer for 2 channel(s) - float **const bOut = reinterpret_cast(hv_alloca(2*sizeof(float *))); - bOut[0] = outputBuffers+(0*n4); - bOut[1] = outputBuffers+(1*n4); - - int n = process(bIn, bOut, n4); - return n; -} - -int Heavy_test::processInlineInterleaved(float *inputBuffers, float *outputBuffers, int n4) { - // ps hv_assert(n4 & ~HV_N_SIMD_MASK); // ensure that n4 is a multiple of HV_N_SIMD - - // define the heavy input buffer for 0 channel(s), uninterleave - float *const bIn = NULL; - - // define the heavy output buffer for 2 channel(s) - float *const bOut = reinterpret_cast(hv_alloca(2*n4*sizeof(float))); - - int n = processInline(bIn, bOut, n4); - - // interleave the heavy output into the output buffer - #if HV_SIMD_AVX - for (int i = 0, j = 0; j < n4; j += 8, i += 16) { - __m256 x = _mm256_load_ps(bOut+j); // LLLLLLLL - __m256 y = _mm256_load_ps(bOut+n4+j); // RRRRRRRR - __m256 a = _mm256_unpacklo_ps(x, y); // LRLRLRLR - __m256 b = _mm256_unpackhi_ps(x, y); // LRLRLRLR - _mm256_store_ps(outputBuffers+i, a); - _mm256_store_ps(outputBuffers+8+i, b); - } - #elif HV_SIMD_SSE - for (int i = 0, j = 0; j < n4; j += 4, i += 8) { - __m128 x = _mm_load_ps(bOut+j); // LLLL - __m128 y = _mm_load_ps(bOut+n4+j); // RRRR - __m128 a = _mm_unpacklo_ps(x, y); // LRLR - __m128 b = _mm_unpackhi_ps(x, y); // LRLR - _mm_store_ps(outputBuffers+i, a); - _mm_store_ps(outputBuffers+4+i, b); - } - #elif HV_SIMD_NEON - // https://community.arm.com/groups/processors/blog/2012/03/13/coding-for-neon--part-5-rearranging-vectors - for (int i = 0, j = 0; j < n4; j += 4, i += 8) { - float32x4_t x = vld1q_f32(bOut+j); - float32x4_t y = vld1q_f32(bOut+n4+j); - float32x4x2_t z = {x, y}; - vst2q_f32(outputBuffers+i, z); // interleave and store - } - #else // HV_SIMD_NONE - for (int i = 0; i < 2; ++i) { - for (int j = 0; j < n4; ++j) { - outputBuffers[i+2*j] = bOut[i*n4+j]; - } - } - #endif - - return n; -} diff --git a/examples/examples-dsp/examples-pd/streams-pd-audiokit/Heavy_test.h b/examples/examples-dsp/examples-pd/streams-pd-audiokit/Heavy_test.h deleted file mode 100644 index 18213d89b5..0000000000 --- a/examples/examples-dsp/examples-pd/streams-pd-audiokit/Heavy_test.h +++ /dev/null @@ -1,78 +0,0 @@ -/** - * Copyright (c) 2024 Enzien Audio, Ltd. - * - * Redistribution and use in source and binary forms, with or without modification, - * are permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright notice, - * this list of conditions, and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the phrase "powered by heavy", - * the heavy logo, and a hyperlink to https://enzienaudio.com, all in a visible - * form. - * - * 2.1 If the Application is distributed in a store system (for example, - * the Apple "App Store" or "Google Play"), the phrase "powered by heavy" - * shall be included in the app description or the copyright text as well as - * the in the app itself. The heavy logo will shall be visible in the app - * itself as well. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, - * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR - * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER - * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, - * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF - * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - */ - -#ifndef _HEAVY_TEST_H_ -#define _HEAVY_TEST_H_ - -#include "HvHeavy.h" - -#ifdef __cplusplus -extern "C" { -#endif - -#if HV_APPLE -#pragma mark - Heavy Context -#endif - - - -/** - * Creates a new patch instance. - * Sample rate should be positive and in Hertz, e.g. 44100.0. - */ -HeavyContextInterface *hv_test_new(double sampleRate); - -/** - * Creates a new patch instance. - * @param sampleRate Sample rate should be positive (> 0) and in Hertz, e.g. 48000.0. - * @param poolKb Pool size is in kilobytes, and determines the maximum amount of memory - * allocated to messages at any time. By default this is 10 KB. - * @param inQueueKb The size of the input message queue in kilobytes. It determines the - * amount of memory dedicated to holding scheduled messages between calls to - * process(). Default is 2 KB. - * @param outQueueKb The size of the output message queue in kilobytes. It determines the - * amount of memory dedicated to holding scheduled messages to the default sendHook. - * See getNextSentMessage() for info on accessing these messages. Default is 0 KB. - */ -HeavyContextInterface *hv_test_new_with_options(double sampleRate, int poolKb, int inQueueKb, int outQueueKb); - -/** - * Free the patch instance. - */ -void hv_test_free(HeavyContextInterface *instance); - - -#ifdef __cplusplus -} // extern "C" -#endif - -#endif // _HEAVY_TEST_H_ diff --git a/examples/examples-dsp/examples-pd/streams-pd-audiokit/Heavy_test.hpp b/examples/examples-dsp/examples-pd/streams-pd-audiokit/Heavy_test.hpp deleted file mode 100644 index a9f015a541..0000000000 --- a/examples/examples-dsp/examples-pd/streams-pd-audiokit/Heavy_test.hpp +++ /dev/null @@ -1,68 +0,0 @@ -/** - * Copyright (c) 2024 Enzien Audio, Ltd. - * - * Redistribution and use in source and binary forms, with or without modification, - * are permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright notice, - * this list of conditions, and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the phrase "powered by heavy", - * the heavy logo, and a hyperlink to https://enzienaudio.com, all in a visible - * form. - * - * 2.1 If the Application is distributed in a store system (for example, - * the Apple "App Store" or "Google Play"), the phrase "powered by heavy" - * shall be included in the app description or the copyright text as well as - * the in the app itself. The heavy logo will shall be visible in the app - * itself as well. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, - * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR - * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER - * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, - * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF - * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - */ - -#ifndef _HEAVY_CONTEXT_TEST_HPP_ -#define _HEAVY_CONTEXT_TEST_HPP_ - -// object includes -#include "HeavyContext.hpp" -#include "HvSignalVar.h" -#include "HvSignalPhasor.h" -#include "HvMath.h" - -class Heavy_test : public HeavyContext { - - public: - Heavy_test(double sampleRate, int poolKb=10, int inQueueKb=2, int outQueueKb=0); - ~Heavy_test(); - - const char *getName() override { return "test"; } - int getNumInputChannels() override { return 0; } - int getNumOutputChannels() override { return 2; } - - int process(float **inputBuffers, float **outputBuffer, int n) override; - int processInline(float *inputBuffers, float *outputBuffer, int n) override; - int processInlineInterleaved(float *inputBuffers, float *outputBuffer, int n) override; - - int getParameterInfo(int index, HvParameterInfo *info) override; - - private: - HvTable *getTableForHash(hv_uint32_t tableHash) override; - void scheduleMessageForReceiver(hv_uint32_t receiverHash, HvMessage *m) override; - - // static sendMessage functions - - // objects - SignalPhasor sPhasor_GM4o5ge7; -}; - -#endif // _HEAVY_CONTEXT_TEST_HPP_ diff --git a/examples/examples-dsp/examples-pd/streams-pd-audiokit/HvHeavy.cpp b/examples/examples-dsp/examples-pd/streams-pd-audiokit/HvHeavy.cpp deleted file mode 100644 index 19ca4128d8..0000000000 --- a/examples/examples-dsp/examples-pd/streams-pd-audiokit/HvHeavy.cpp +++ /dev/null @@ -1,318 +0,0 @@ -/** - * Copyright (c) 2014-2018 Enzien Audio Ltd. - * - * Permission to use, copy, modify, and/or distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH - * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY - * AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, - * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM - * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR - * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR - * PERFORMANCE OF THIS SOFTWARE. - */ - -#include "HeavyContext.hpp" - -#ifdef __cplusplus -extern "C" { -#endif - -#if HV_APPLE -#pragma mark - Heavy Table -#endif - -HV_EXPORT bool hv_table_setLength(HeavyContextInterface *c, hv_uint32_t tableHash, hv_uint32_t newSampleLength) { - hv_assert(c != nullptr); - return c->setLengthForTable(tableHash, newSampleLength); -} - -HV_EXPORT float *hv_table_getBuffer(HeavyContextInterface *c, hv_uint32_t tableHash) { - hv_assert(c != nullptr); - return c->getBufferForTable(tableHash); -} - -HV_EXPORT hv_uint32_t hv_table_getLength(HeavyContextInterface *c, hv_uint32_t tableHash) { - hv_assert(c != nullptr); - return c->getLengthForTable(tableHash); -} - - - -#if HV_APPLE -#pragma mark - Heavy Message -#endif - -HV_EXPORT hv_size_t hv_msg_getByteSize(hv_uint32_t numElements) { - return msg_getCoreSize(numElements); -} - -HV_EXPORT void hv_msg_init(HvMessage *m, int numElements, hv_uint32_t timestamp) { - msg_init(m, numElements, timestamp); -} - -HV_EXPORT hv_size_t hv_msg_getNumElements(const HvMessage *m) { - return msg_getNumElements(m); -} - -HV_EXPORT hv_uint32_t hv_msg_getTimestamp(const HvMessage *m) { - return msg_getTimestamp(m); -} - -HV_EXPORT void hv_msg_setTimestamp(HvMessage *m, hv_uint32_t timestamp) { - msg_setTimestamp(m, timestamp); -} - -HV_EXPORT bool hv_msg_isBang(const HvMessage *const m, int i) { - return msg_isBang(m,i); -} - -HV_EXPORT void hv_msg_setBang(HvMessage *m, int i) { - msg_setBang(m,i); -} - -HV_EXPORT bool hv_msg_isFloat(const HvMessage *const m, int i) { - return msg_isFloat(m, i); -} - -HV_EXPORT float hv_msg_getFloat(const HvMessage *const m, int i) { - return msg_getFloat(m,i); -} - -HV_EXPORT void hv_msg_setFloat(HvMessage *m, int i, float f) { - msg_setFloat(m,i,f); -} - -HV_EXPORT bool hv_msg_isSymbol(const HvMessage *const m, int i) { - return msg_isSymbol(m,i); -} - -HV_EXPORT const char *hv_msg_getSymbol(const HvMessage *const m, int i) { - return msg_getSymbol(m,i); -} - -HV_EXPORT void hv_msg_setSymbol(HvMessage *m, int i, const char *s) { - msg_setSymbol(m,i,s); -} - -HV_EXPORT bool hv_msg_isHash(const HvMessage *const m, int i) { - return msg_isHash(m, i); -} - -HV_EXPORT hv_uint32_t hv_msg_getHash(const HvMessage *const m, int i) { - return msg_getHash(m, i); -} - -HV_EXPORT bool hv_msg_hasFormat(const HvMessage *const m, const char *fmt) { - return msg_hasFormat(m, fmt); -} - -HV_EXPORT char *hv_msg_toString(const HvMessage *const m) { - return msg_toString(m); -} - -HV_EXPORT HvMessage *hv_msg_copy(const HvMessage *const m) { - return msg_copy(m); -} - -HV_EXPORT void hv_msg_free(HvMessage *m) { - msg_free(m); -} - - - -#if HV_APPLE -#pragma mark - Heavy Common -#endif - -HV_EXPORT int hv_getSize(HeavyContextInterface *c) { - hv_assert(c != nullptr); - return (int) c->getSize(); -} - -HV_EXPORT double hv_getSampleRate(HeavyContextInterface *c) { - hv_assert(c != nullptr); - return c->getSampleRate(); -} - -HV_EXPORT int hv_getNumInputChannels(HeavyContextInterface *c) { - hv_assert(c != nullptr); - return c->getNumInputChannels(); -} - -HV_EXPORT int hv_getNumOutputChannels(HeavyContextInterface *c) { - hv_assert(c != nullptr); - return c->getNumOutputChannels(); -} - -HV_EXPORT void hv_setPrintHook(HeavyContextInterface *c, HvPrintHook_t *f) { - hv_assert(c != nullptr); - c->setPrintHook(f); -} - -HV_EXPORT HvPrintHook_t *hv_getPrintHook(HeavyContextInterface *c) { - hv_assert(c != nullptr); - return c->getPrintHook(); -} - -HV_EXPORT void hv_setSendHook(HeavyContextInterface *c, HvSendHook_t *f) { - hv_assert(c != nullptr); - c->setSendHook(f); -} - -HV_EXPORT hv_uint32_t hv_stringToHash(const char *s) { - return hv_string_to_hash(s); -} - -HV_EXPORT bool hv_sendBangToReceiver(HeavyContextInterface *c, hv_uint32_t receiverHash) { - hv_assert(c != nullptr); - return c->sendBangToReceiver(receiverHash); -} - -HV_EXPORT bool hv_sendFloatToReceiver(HeavyContextInterface *c, hv_uint32_t receiverHash, float x) { - hv_assert(c != nullptr); - return c->sendFloatToReceiver(receiverHash, x); -} - -HV_EXPORT bool hv_sendSymbolToReceiver(HeavyContextInterface *c, hv_uint32_t receiverHash, char *s) { - hv_assert(c != nullptr); - return c->sendSymbolToReceiver(receiverHash, s); -} - -HV_EXPORT bool hv_sendMessageToReceiverV( - HeavyContextInterface *c, hv_uint32_t receiverHash, double delayMs, const char *format, ...) { - hv_assert(c != nullptr); - hv_assert(delayMs >= 0.0); - hv_assert(format != nullptr); - - va_list ap; - va_start(ap, format); - const int numElem = (int) hv_strlen(format); - HvMessage *m = HV_MESSAGE_ON_STACK(numElem); - msg_init(m, numElem, c->getCurrentSample() + (hv_uint32_t) (hv_max_d(0.0, delayMs)*c->getSampleRate()/1000.0)); - for (int i = 0; i < numElem; i++) { - switch (format[i]) { - case 'b': msg_setBang(m, i); break; - case 'f': msg_setFloat(m, i, (float) va_arg(ap, double)); break; - case 'h': msg_setHash(m, i, (int) va_arg(ap, int)); break; - case 's': msg_setSymbol(m, i, (char *) va_arg(ap, char *)); break; - default: break; - } - } - va_end(ap); - - return c->sendMessageToReceiver(receiverHash, delayMs, m); -} - -HV_EXPORT bool hv_sendMessageToReceiver( - HeavyContextInterface *c, hv_uint32_t receiverHash, double delayMs, HvMessage *m) { - hv_assert(c != nullptr); - return c->sendMessageToReceiver(receiverHash, delayMs, m); -} - -HV_EXPORT void hv_cancelMessage(HeavyContextInterface *c, HvMessage *m, void (*sendMessage)(HeavyContextInterface *, int, const HvMessage *)) { - hv_assert(c != nullptr); - c->cancelMessage(m, sendMessage); -} - -HV_EXPORT const char *hv_getName(HeavyContextInterface *c) { - hv_assert(c != nullptr); - return c->getName(); -} - -HV_EXPORT void hv_setUserData(HeavyContextInterface *c, void *userData) { - hv_assert(c != nullptr); - c->setUserData(userData); -} - -HV_EXPORT void *hv_getUserData(HeavyContextInterface *c) { - hv_assert(c != nullptr); - return c->getUserData(); -} - -HV_EXPORT double hv_getCurrentTime(HeavyContextInterface *c) { - hv_assert(c != nullptr); - return (double) c->samplesToMilliseconds(c->getCurrentSample()); -} - -HV_EXPORT hv_uint32_t hv_getCurrentSample(HeavyContextInterface *c) { - hv_assert(c != nullptr); - return c->getCurrentSample(); -} - -HV_EXPORT float hv_samplesToMilliseconds(HeavyContextInterface *c, hv_uint32_t numSamples) { - hv_assert(c != nullptr); - return c->samplesToMilliseconds(numSamples); -} - -HV_EXPORT hv_uint32_t hv_millisecondsToSamples(HeavyContextInterface *c, float ms) { - hv_assert(c != nullptr); - return c->millisecondsToSamples(ms); -} - -HV_EXPORT int hv_getParameterInfo(HeavyContextInterface *c, int index, HvParameterInfo *info) { - hv_assert(c != nullptr); - return c->getParameterInfo(index, info); -} - -HV_EXPORT void hv_lock_acquire(HeavyContextInterface *c) { - hv_assert(c != nullptr); - c->lockAcquire(); -} - -HV_EXPORT bool hv_lock_try(HeavyContextInterface *c) { - hv_assert(c != nullptr); - return c->lockTry(); -} - -HV_EXPORT void hv_lock_release(HeavyContextInterface *c) { - hv_assert(c != nullptr); - c->lockRelease(); -} - -HV_EXPORT void hv_setInputMessageQueueSize(HeavyContextInterface *c, hv_uint32_t inQueueKb) { - hv_assert(c != nullptr); - c->setInputMessageQueueSize(inQueueKb); -} - -HV_EXPORT void hv_setOutputMessageQueueSize(HeavyContextInterface *c, hv_uint32_t outQueueKb) { - hv_assert(c != nullptr); - c->setOutputMessageQueueSize(outQueueKb); -} - -HV_EXPORT bool hv_getNextSentMessage(HeavyContextInterface *c, hv_uint32_t *destinationHash, HvMessage *outMsg, hv_uint32_t msgLength) { - hv_assert(c != nullptr); - hv_assert(destinationHash != nullptr); - hv_assert(outMsg != nullptr); - return c->getNextSentMessage(destinationHash, outMsg, msgLength); -} - - -#if HV_APPLE -#pragma mark - Heavy Common -#endif - -HV_EXPORT int hv_process(HeavyContextInterface *c, float **inputBuffers, float **outputBuffers, int n) { - hv_assert(c != nullptr); - return c->process(inputBuffers, outputBuffers, n); -} - -HV_EXPORT int hv_processInline(HeavyContextInterface *c, float *inputBuffers, float *outputBuffers, int n) { - hv_assert(c != nullptr); - return c->processInline(inputBuffers, outputBuffers, n); -} - -HV_EXPORT int hv_processInlineInterleaved(HeavyContextInterface *c, float *inputBuffers, float *outputBuffers, int n) { - hv_assert(c != nullptr); - return c->processInlineInterleaved(inputBuffers, outputBuffers, n); -} - -HV_EXPORT void hv_delete(HeavyContextInterface *c) { - delete c; -} - -#ifdef __cplusplus -} -#endif diff --git a/examples/examples-dsp/examples-pd/streams-pd-audiokit/HvHeavy.h b/examples/examples-dsp/examples-pd/streams-pd-audiokit/HvHeavy.h deleted file mode 100644 index cb1aecfa98..0000000000 --- a/examples/examples-dsp/examples-pd/streams-pd-audiokit/HvHeavy.h +++ /dev/null @@ -1,413 +0,0 @@ -/** - * Copyright (c) 2014-2018 Enzien Audio Ltd. - * - * Permission to use, copy, modify, and/or distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH - * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY - * AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, - * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM - * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR - * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR - * PERFORMANCE OF THIS SOFTWARE. - */ - -#ifndef _HEAVY_H_ -#define _HEAVY_H_ - -#include "HvUtils.h" - -#ifdef __cplusplus -extern "C" { -#endif - -#ifndef _HEAVY_DECLARATIONS_ -#define _HEAVY_DECLARATIONS_ - -#ifdef __cplusplus -class HeavyContextInterface; -#else -typedef struct HeavyContextInterface HeavyContextInterface; -#endif - -typedef struct HvMessage HvMessage; - -typedef enum { - HV_PARAM_TYPE_PARAMETER_IN, - HV_PARAM_TYPE_PARAMETER_OUT, - HV_PARAM_TYPE_EVENT_IN, - HV_PARAM_TYPE_EVENT_OUT -} HvParameterType; - -typedef struct HvParameterInfo { - const char *name; // the human readable parameter name - hv_uint32_t hash; // an integer identified used by heavy for this parameter - HvParameterType type; // type of this parameter - float minVal; // the minimum value of this parameter - float maxVal; // the maximum value of this parameter - float defaultVal; // the default value of this parameter -} HvParameterInfo; - -typedef void (HvSendHook_t) (HeavyContextInterface *context, const char *sendName, hv_uint32_t sendHash, const HvMessage *msg); -typedef void (HvPrintHook_t) (HeavyContextInterface *context, const char *printName, const char *str, const HvMessage *msg); - -#endif // _HEAVY_DECLARATIONS_ - - - -#if HV_APPLE -#pragma mark - Heavy Context -#endif - -/** Deletes a patch instance. */ -void hv_delete(HeavyContextInterface *c); - - - -#if HV_APPLE -#pragma mark - Heavy Process -#endif - -/** - * Processes one block of samples for a patch instance. The buffer format is an array of float channel arrays. - * If the context has not input or output channels, the respective argument may be NULL. - * The number of samples to to tbe processed should be a multiple of 1, 4, or 8, depending on if - * no, SSE or NEON, or AVX optimisation is being used, respectively. - * e.g. [[LLLL][RRRR]] - * This function support in-place processing. - * - * @return The number of samples processed. - * - * This function is NOT thread-safe. It is assumed that only the audio thread will execute this function. - */ -int hv_process(HeavyContextInterface *c, float **inputBuffers, float **outputBuffers, int n); - -/** - * Processes one block of samples for a patch instance. The buffer format is an uninterleaved float array of channels. - * If the context has not input or output channels, the respective argument may be NULL. - * The number of samples to to tbe processed should be a multiple of 1, 4, or 8, depending on if - * no, SSE or NEON, or AVX optimisation is being used, respectively. - * e.g. [LLLLRRRR] - * This function support in-place processing. - * - * @return The number of samples processed. - * - * This function is NOT thread-safe. It is assumed that only the audio thread will execute this function. - */ -int hv_processInline(HeavyContextInterface *c, float *inputBuffers, float *outputBuffers, int n); - -/** - * Processes one block of samples for a patch instance. The buffer format is an interleaved float array of channels. - * If the context has not input or output channels, the respective argument may be NULL. - * The number of samples to to tbe processed should be a multiple of 1, 4, or 8, depending on if - * no, SSE or NEON, or AVX optimisation is being used, respectively. - * e.g. [LRLRLRLR] - * This function support in-place processing. - * - * @return The number of samples processed. - * - * This function is NOT thread-safe. It is assumed that only the audio thread will execute this function. - */ -int hv_processInlineInterleaved(HeavyContextInterface *c, float *inputBuffers, float *outputBuffers, int n); - - - -#if HV_APPLE -#pragma mark - Heavy Common -#endif - -/** - * Returns the total size in bytes of the context. - * This value may change if tables are resized. - */ -int hv_getSize(HeavyContextInterface *c); - -/** Returns the sample rate with which this context has been configured. */ -double hv_getSampleRate(HeavyContextInterface *c); - -/** Returns the number of input channels with which this context has been configured. */ -int hv_getNumInputChannels(HeavyContextInterface *c); - -/** Returns the number of output channels with which this context has been configured. */ -int hv_getNumOutputChannels(HeavyContextInterface *c); - -/** Set the print hook. The function is called whenever a message is sent to a print object. */ -void hv_setPrintHook(HeavyContextInterface *c, HvPrintHook_t *f); - -/** Returns the print hook, or NULL. */ -HvPrintHook_t *hv_getPrintHook(HeavyContextInterface *c); - -/** - * Set the send hook. The function is called whenever a message is sent to any send object. - * Messages returned by this function should NEVER be freed. If the message must persist, call - * hv_msg_copy() first. - */ -void hv_setSendHook(HeavyContextInterface *c, HvSendHook_t *f); - -/** Returns a 32-bit hash of any string. Returns 0 if string is NULL. */ -hv_uint32_t hv_stringToHash(const char *s); - -/** - * A convenience function to send a bang to a receiver to be processed immediately. - * The receiver is addressed with its hash, which can also be determined using hv_stringToHash(). - * This function is thread-safe. - * - * @return True if the message was accepted. False if the message could not fit onto - * the message queue to be processed this block. - */ -bool hv_sendBangToReceiver(HeavyContextInterface *c, hv_uint32_t receiverHash); - -/** - * A convenience function to send a float to a receiver to be processed immediately. - * The receiver is addressed with its hash, which can also be determined using hv_stringToHash(). - * This function is thread-safe. - * - * @return True if the message was accepted. False if the message could not fit onto - * the message queue to be processed this block. - */ -bool hv_sendFloatToReceiver(HeavyContextInterface *c, hv_uint32_t receiverHash, const float x); - -/** - * A convenience function to send a symbol to a receiver to be processed immediately. - * The receiver is addressed with its hash, which can also be determined using hv_stringToHash(). - * This function is thread-safe. - * - * @return True if the message was accepted. False if the message could not fit onto - * the message queue to be processed this block. - */ -bool hv_sendSymbolToReceiver(HeavyContextInterface *c, hv_uint32_t receiverHash, char *s); - -/** - * Sends a formatted message to a receiver that can be scheduled for the future. - * The receiver is addressed with its hash, which can also be determined using hv_stringToHash(). - * This function is thread-safe. - * - * @return True if the message was accepted. False if the message could not fit onto - * the message queue to be processed this block. - */ -bool hv_sendMessageToReceiverV(HeavyContextInterface *c, hv_uint32_t receiverHash, double delayMs, const char *format, ...); - -/** - * Sends a message to a receiver that can be scheduled for the future. - * The receiver is addressed with its hash, which can also be determined using hv_stringToHash(). - * This function is thread-safe. - * - * @return True if the message was accepted. False if the message could not fit onto - * the message queue to be processed this block. - */ -bool hv_sendMessageToReceiver(HeavyContextInterface *c, hv_uint32_t receiverHash, double delayMs, HvMessage *m); - -/** - * Cancels a previously scheduled message. - * - * @param sendMessage May be NULL. - */ -void hv_cancelMessage(HeavyContextInterface *c, HvMessage *m, void (*sendMessage)(HeavyContextInterface *, int, const HvMessage *)); - -/** Returns the read-only user-assigned name of this patch. */ -const char *hv_getName(HeavyContextInterface *c); - -/** Sets a user-definable value. This value is never manipulated by Heavy. */ -void hv_setUserData(HeavyContextInterface *c, void *userData); - -/** Returns the user-defined data. */ -void *hv_getUserData(HeavyContextInterface *c); - -/** Returns the current patch time in milliseconds. This value may have rounding errors. */ -double hv_getCurrentTime(HeavyContextInterface *c); - -/** Returns the current patch time in samples. This value is always exact. */ -hv_uint32_t hv_getCurrentSample(HeavyContextInterface *c); - -/** - * Returns information about each parameter such as name, hash, and range. - * The total number of parameters is always returned. - * - * @param index The parameter index. - * @param info A pointer to a HvParameterInfo struct. May be null. - * - * @return The total number of parameters. - */ -int hv_getParameterInfo(HeavyContextInterface *c, int index, HvParameterInfo *info); - -/** */ -float hv_samplesToMilliseconds(HeavyContextInterface *c, hv_uint32_t numSamples); - -/** Converts milliseconds to samples. Input is limited to non-negative range. */ -hv_uint32_t hv_millisecondsToSamples(HeavyContextInterface *c, float ms); - -/** - * Acquire the input message queue lock. - * - * This function will block until the message lock as been acquired. - * Typical applications will not require the use of this function. - * - * @param c A Heavy context. - */ -void hv_lock_acquire(HeavyContextInterface *c); - -/** - * Try to acquire the input message queue lock. - * - * If the lock has been acquired, hv_lock_release() must be called to release it. - * Typical applications will not require the use of this function. - * - * @param c A Heavy context. - * - * @return Returns true if the lock has been acquired, false otherwise. - */ -bool hv_lock_try(HeavyContextInterface *c); - -/** - * Release the input message queue lock. - * - * Typical applications will not require the use of this function. - * - * @param c A Heavy context. - */ -void hv_lock_release(HeavyContextInterface *c); - -/** - * Set the size of the input message queue in kilobytes. - * - * The buffer is reset and all existing contents are lost on resize. - * - * @param c A Heavy context. - * @param inQueueKb Must be positive i.e. at least one. - */ -void hv_setInputMessageQueueSize(HeavyContextInterface *c, hv_uint32_t inQueueKb); - -/** - * Set the size of the output message queue in kilobytes. - * - * The buffer is reset and all existing contents are lost on resize. - * Only the default sendhook uses the outgoing message queue. If the default - * sendhook is not being used, then this function is not useful. - * - * @param c A Heavy context. - * @param outQueueKb Must be postive i.e. at least one. - */ -void hv_setOutputMessageQueueSize(HeavyContextInterface *c, hv_uint32_t outQueueKb); - -/** - * Get the next message in the outgoing queue, will also consume the message. - * Returns false if there are no messages. - * - * @param c A Heavy context. - * @param destinationHash a hash of the name of the receiver the message was sent to. - * @param outMsg message pointer that is filled by the next message contents. - * @param msgLength length of outMsg in bytes. - * - * @return True if there is a message in the outgoing queue. -*/ -bool hv_getNextSentMessage(HeavyContextInterface *c, hv_uint32_t *destinationHash, HvMessage *outMsg, hv_uint32_t msgLength); - - - -#if HV_APPLE -#pragma mark - Heavy Message -#endif - -typedef struct HvMessage HvMessage; - -/** Returns the total size in bytes of a HvMessage with a number of elements on the heap. */ -unsigned long hv_msg_getByteSize(hv_uint32_t numElements); - -/** Initialise a HvMessage structure with the number of elements and a timestamp (in samples). */ -void hv_msg_init(HvMessage *m, int numElements, hv_uint32_t timestamp); - -/** Returns the number of elements in this message. */ -unsigned long hv_msg_getNumElements(const HvMessage *m); - -/** Returns the time at which this message exists (in samples). */ -hv_uint32_t hv_msg_getTimestamp(const HvMessage *m); - -/** Set the time at which this message should be executed (in samples). */ -void hv_msg_setTimestamp(HvMessage *m, hv_uint32_t timestamp); - -/** Returns true of the indexed element is a bang. False otherwise. Index is not bounds checked. */ -bool hv_msg_isBang(const HvMessage *const m, int i); - -/** Sets the indexed element to a bang. Index is not bounds checked. */ -void hv_msg_setBang(HvMessage *m, int i); - -/** Returns true of the indexed element is a float. False otherwise. Index is not bounds checked. */ -bool hv_msg_isFloat(const HvMessage *const m, int i); - -/** Returns the indexed element as a float value. Index is not bounds checked. */ -float hv_msg_getFloat(const HvMessage *const m, int i); - -/** Sets the indexed element to float value. Index is not bounds checked. */ -void hv_msg_setFloat(HvMessage *m, int i, float f); - -/** Returns true of the indexed element is a symbol. False otherwise. Index is not bounds checked. */ -bool hv_msg_isSymbol(const HvMessage *const m, int i); - -/** Returns the indexed element as a symbol value. Index is not bounds checked. */ -const char *hv_msg_getSymbol(const HvMessage *const m, int i); - -/** Returns true of the indexed element is a hash. False otherwise. Index is not bounds checked. */ -bool hv_msg_isHash(const HvMessage *const m, int i); - -/** Returns the indexed element as a hash value. Index is not bounds checked. */ -hv_uint32_t hv_msg_getHash(const HvMessage *const m, int i); - -/** Sets the indexed element to symbol value. Index is not bounds checked. */ -void hv_msg_setSymbol(HvMessage *m, int i, const char *s); - -/** - * Returns true if the message has the given format, in number of elements and type. False otherwise. - * Valid element types are: - * 'b': bang - * 'f': float - * 's': symbol - * - * For example, a message with three floats would have a format of "fff". A single bang is "b". - * A message with two symbols is "ss". These types can be mixed and matched in any way. - */ -bool hv_msg_hasFormat(const HvMessage *const m, const char *fmt); - -/** - * Returns a basic string representation of the message. - * The character array MUST be deallocated by the caller. - */ -char *hv_msg_toString(const HvMessage *const m); - -/** Copy a message onto the stack. The message persists. */ -HvMessage *hv_msg_copy(const HvMessage *const m); - -/** Free a copied message. */ -void hv_msg_free(HvMessage *m); - - - -#if HV_APPLE -#pragma mark - Heavy Table -#endif - -/** - * Resizes the table to the given length. - * - * Existing contents are copied to the new table. Remaining space is cleared - * if the table is longer than the original, truncated otherwise. - * - * @param tableHash The table identifier. - * @param newSampleLength The new length of the table, in samples. Must be positive. - * - * @return False if the table could not be found. True otherwise. - */ -bool hv_table_setLength(HeavyContextInterface *c, hv_uint32_t tableHash, hv_uint32_t newSampleLength); - -/** Returns a pointer to the raw buffer backing this table. DO NOT free it. */ -float *hv_table_getBuffer(HeavyContextInterface *c, hv_uint32_t tableHash); - -/** Returns the length of this table in samples. */ -hv_uint32_t hv_table_getLength(HeavyContextInterface *c, hv_uint32_t tableHash); - -#ifdef __cplusplus -} // extern "C" -#endif - -#endif // _HEAVY_H_ diff --git a/examples/examples-dsp/examples-pd/streams-pd-audiokit/HvHeavyInternal.h b/examples/examples-dsp/examples-pd/streams-pd-audiokit/HvHeavyInternal.h deleted file mode 100644 index e10b944410..0000000000 --- a/examples/examples-dsp/examples-pd/streams-pd-audiokit/HvHeavyInternal.h +++ /dev/null @@ -1,51 +0,0 @@ -/** - * Copyright (c) 2014-2018 Enzien Audio Ltd. - * - * Permission to use, copy, modify, and/or distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH - * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY - * AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, - * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM - * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR - * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR - * PERFORMANCE OF THIS SOFTWARE. - */ - -#ifndef _HEAVY_INTERNAL_H_ -#define _HEAVY_INTERNAL_H_ - -#include "HvHeavy.h" -#include "HvUtils.h" -#include "HvTable.h" -#include "HvMessage.h" -#include "HvMath.h" - -#ifdef __cplusplus -extern "C" { -#endif - -/** - * - */ -HvTable *hv_table_get(HeavyContextInterface *c, hv_uint32_t tableHash); - -/** - * - */ -void hv_scheduleMessageForReceiver(HeavyContextInterface *c, hv_uint32_t receiverHash, HvMessage *m); - -/** - * - */ -HvMessage *hv_scheduleMessageForObject(HeavyContextInterface *c, const HvMessage *m, - void (*sendMessage)(HeavyContextInterface *, int, const HvMessage *), - int letIndex); - -#ifdef __cplusplus -} -#endif - -#endif diff --git a/examples/examples-dsp/examples-pd/streams-pd-audiokit/HvLightPipe.c b/examples/examples-dsp/examples-pd/streams-pd-audiokit/HvLightPipe.c deleted file mode 100644 index 98de428312..0000000000 --- a/examples/examples-dsp/examples-pd/streams-pd-audiokit/HvLightPipe.c +++ /dev/null @@ -1,136 +0,0 @@ -/** - * Copyright (c) 2014-2018 Enzien Audio Ltd. - * - * Permission to use, copy, modify, and/or distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH - * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY - * AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, - * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM - * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR - * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR - * PERFORMANCE OF THIS SOFTWARE. - */ - -#include "HvLightPipe.h" - -#if __SSE__ || HV_SIMD_SSE -#include -#define hv_sfence() _mm_sfence() -#elif __arm__ || HV_SIMD_NEON - #if __ARM_ACLE - #include - // https://msdn.microsoft.com/en-us/library/hh875058.aspx#BarrierRestrictions - // http://doxygen.reactos.org/d8/d47/armintr_8h_a02be7ec76ca51842bc90d9b466b54752.html - #define hv_sfence() __dmb(0xE) /* _ARM_BARRIER_ST */ - #elif defined(__GNUC__) - #define hv_sfence() __asm__ volatile ("dmb 0xE":::"memory") - #else - // http://stackoverflow.com/questions/19965076/gcc-memory-barrier-sync-synchronize-vs-asm-volatile-memory - #define hv_sfence() __sync_synchronize() - #endif -#elif HV_WIN -// https://msdn.microsoft.com/en-us/library/windows/desktop/ms684208(v=vs.85).aspx -#define hv_sfence() _WriteBarrier() -#else -#define hv_sfence() __asm__ volatile("" : : : "memory") -#endif - -#define HLP_STOP 0 -#define HLP_LOOP 0xFFFFFFFF -#define HLP_SET_UINT32_AT_BUFFER(a, b) (*((hv_uint32_t *) (a)) = (b)) -#define HLP_GET_UINT32_AT_BUFFER(a) (*((hv_uint32_t *) (a))) - -hv_uint32_t hLp_init(HvLightPipe *q, hv_uint32_t numBytes) { - if (numBytes > 0) { - q->buffer = (char *) hv_malloc(numBytes); - hv_assert(q->buffer != NULL); - HLP_SET_UINT32_AT_BUFFER(q->buffer, HLP_STOP); - } else { - q->buffer = NULL; - } - q->writeHead = q->buffer; - q->readHead = q->buffer; - q->len = numBytes; - q->remainingBytes = numBytes; - return numBytes; -} - -void hLp_free(HvLightPipe *q) { - hv_free(q->buffer); -} - -hv_uint32_t hLp_hasData(HvLightPipe *q) { - hv_uint32_t x = HLP_GET_UINT32_AT_BUFFER(q->readHead); - if (x == HLP_LOOP) { - q->readHead = q->buffer; - x = HLP_GET_UINT32_AT_BUFFER(q->readHead); - } - return x; -} - -char *hLp_getWriteBuffer(HvLightPipe *q, hv_uint32_t bytesToWrite) { - char *const readHead = q->readHead; - char *const oldWriteHead = q->writeHead; - const hv_uint32_t totalByteRequirement = bytesToWrite + 2*sizeof(hv_uint32_t); - - // check if there is enough space to write the data in the remaining - // length of the buffer - if (totalByteRequirement <= q->remainingBytes) { - char *const newWriteHead = oldWriteHead + sizeof(hv_uint32_t) + bytesToWrite; - - // check if writing would overwrite existing data in the pipe (return NULL if so) - if ((oldWriteHead < readHead) && (newWriteHead >= readHead)) return NULL; - else return (oldWriteHead + sizeof(hv_uint32_t)); - } else { - // there isn't enough space, try looping around to the start - if (totalByteRequirement <= q->len) { - if ((oldWriteHead < readHead) || ((q->buffer + totalByteRequirement) > readHead)) { - return NULL; // overwrite condition - } else { - q->writeHead = q->buffer; - q->remainingBytes = q->len; - HLP_SET_UINT32_AT_BUFFER(q->buffer, HLP_STOP); - hv_sfence(); - HLP_SET_UINT32_AT_BUFFER(oldWriteHead, HLP_LOOP); - return q->buffer + sizeof(hv_uint32_t); - } - } else { - return NULL; // there isn't enough space to write the data - } - } -} - -void hLp_produce(HvLightPipe *q, hv_uint32_t numBytes) { - hv_assert(q->remainingBytes >= (numBytes + 2*sizeof(hv_uint32_t))); - q->remainingBytes -= (sizeof(hv_uint32_t) + numBytes); - char *const oldWriteHead = q->writeHead; - q->writeHead += (sizeof(hv_uint32_t) + numBytes); - HLP_SET_UINT32_AT_BUFFER(q->writeHead, HLP_STOP); - - // save everything before this point to memory - hv_sfence(); - - // then save this - HLP_SET_UINT32_AT_BUFFER(oldWriteHead, numBytes); -} - -char *hLp_getReadBuffer(HvLightPipe *q, hv_uint32_t *numBytes) { - *numBytes = HLP_GET_UINT32_AT_BUFFER(q->readHead); - char *const readBuffer = q->readHead + sizeof(hv_uint32_t); - return readBuffer; -} - -void hLp_consume(HvLightPipe *q) { - hv_assert(HLP_GET_UINT32_AT_BUFFER(q->readHead) != HLP_STOP); - q->readHead += sizeof(hv_uint32_t) + HLP_GET_UINT32_AT_BUFFER(q->readHead); -} - -void hLp_reset(HvLightPipe *q) { - q->writeHead = q->buffer; - q->readHead = q->buffer; - q->remainingBytes = q->len; - memset(q->buffer, 0, q->len); -} diff --git a/examples/examples-dsp/examples-pd/streams-pd-audiokit/HvLightPipe.h b/examples/examples-dsp/examples-pd/streams-pd-audiokit/HvLightPipe.h deleted file mode 100644 index 438f7261dc..0000000000 --- a/examples/examples-dsp/examples-pd/streams-pd-audiokit/HvLightPipe.h +++ /dev/null @@ -1,104 +0,0 @@ -/** - * Copyright (c) 2014-2018 Enzien Audio Ltd. - * - * Permission to use, copy, modify, and/or distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH - * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY - * AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, - * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM - * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR - * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR - * PERFORMANCE OF THIS SOFTWARE. - */ - -#ifndef _HEAVY_LIGHTPIPE_H_ -#define _HEAVY_LIGHTPIPE_H_ - -#include "HvUtils.h" - -#ifdef __cplusplus -extern "C" { -#endif - -/* - * This pipe assumes that there is only one producer thread and one consumer - * thread. This data structure does not support any other configuration. - */ -typedef struct HvLightPipe { - char *buffer; - char *writeHead; - char *readHead; - hv_uint32_t len; - hv_uint32_t remainingBytes; // total bytes from write head to end -} HvLightPipe; - -/** - * Initialise the pipe with a given length, in bytes. - * @return Returns the size of the pipe in bytes. - */ -hv_uint32_t hLp_init(HvLightPipe *q, hv_uint32_t numBytes); - -/** - * Frees the internal buffer. - * @param q The light pipe. - */ -void hLp_free(HvLightPipe *q); - -/** - * Indicates if data is available for reading. - * @param q The light pipe. - * - * @return Returns the number of bytes available for reading. Zero if no bytes - * are available. - */ -hv_uint32_t hLp_hasData(HvLightPipe *q); - -/** - * Returns a pointer to a location in the pipe where numBytes can be written. - * - * @param numBytes The number of bytes to be written. - * @return A pointer to a location where those bytes can be written. Returns - * NULL if no more space is available. Successive calls to this - * function may eventually return a valid pointer because the readhead - * has been advanced on another thread. - */ -char *hLp_getWriteBuffer(HvLightPipe *q, hv_uint32_t numBytes); - -/** - * Indicates to the pipe how many bytes have been written. - * - * @param numBytes The number of bytes written. In general this should be the - * same value as was passed to the preceeding call to - * hLp_getWriteBuffer(). - */ -void hLp_produce(HvLightPipe *q, hv_uint32_t numBytes); - -/** - * Returns the current read buffer, indicating the number of bytes available - * for reading. - * @param q The light pipe. - * @param numBytes This value will be filled with the number of bytes available - * for reading. - * - * @return A pointer to the read buffer. - */ -char *hLp_getReadBuffer(HvLightPipe *q, hv_uint32_t *numBytes); - -/** - * Indicates that the next set of bytes have been read and are no longer needed. - * @param q The light pipe. - */ -void hLp_consume(HvLightPipe *q); - -// resets the queue to it's initialised state -// This should be done when only one thread is accessing the pipe. -void hLp_reset(HvLightPipe *q); - -#ifdef __cplusplus -} -#endif - -#endif // _HEAVY_LIGHTPIPE_H_ diff --git a/examples/examples-dsp/examples-pd/streams-pd-audiokit/HvMath.h b/examples/examples-dsp/examples-pd/streams-pd-audiokit/HvMath.h deleted file mode 100644 index d25de2df98..0000000000 --- a/examples/examples-dsp/examples-pd/streams-pd-audiokit/HvMath.h +++ /dev/null @@ -1,736 +0,0 @@ -/** - * Copyright (c) 2014-2018 Enzien Audio Ltd. - * - * Permission to use, copy, modify, and/or distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH - * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY - * AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, - * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM - * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR - * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR - * PERFORMANCE OF THIS SOFTWARE. - */ - -#ifndef _HEAVY_MATH_H_ -#define _HEAVY_MATH_H_ - -#include "HvUtils.h" - -// https://software.intel.com/sites/landingpage/IntrinsicsGuide/ -// https://gcc.gnu.org/onlinedocs/gcc-4.8.1/gcc/ARM-NEON-Intrinsics.html -// http://codesuppository.blogspot.co.uk/2015/02/sse2neonh-porting-guide-and-header-file.html - -static inline void __hv_zero_f(hv_bOutf_t bOut) { -#if HV_SIMD_AVX - *bOut = _mm256_setzero_ps(); -#elif HV_SIMD_SSE - *bOut = _mm_setzero_ps(); -#elif HV_SIMD_NEON - *bOut = vdupq_n_f32(0.0f); -#else // HV_SIMD_NONE - *bOut = 0.0f; -#endif -} - -static inline void __hv_zero_i(hv_bOuti_t bOut) { -#if HV_SIMD_AVX - *bOut = _mm256_setzero_si256(); -#elif HV_SIMD_SSE - *bOut = _mm_setzero_si128(); -#elif HV_SIMD_NEON - *bOut = vdupq_n_s32(0); -#else // HV_SIMD_NONE - *bOut = 0; -#endif -} - -static inline void __hv_load_f(float *bIn, hv_bOutf_t bOut) { -#if HV_SIMD_AVX - *bOut = _mm256_load_ps(bIn); -#elif HV_SIMD_SSE - *bOut = _mm_load_ps(bIn); -#elif HV_SIMD_NEON - *bOut = vld1q_f32(bIn); -#else // HV_SIMD_NONE - *bOut = *bIn; -#endif -} - -static inline void __hv_store_f(float *bOut, hv_bInf_t bIn) { -#if HV_SIMD_AVX - _mm256_store_ps(bOut, bIn); -#elif HV_SIMD_SSE - _mm_store_ps(bOut, bIn); -#elif HV_SIMD_NEON - vst1q_f32(bOut, bIn); -#else // HV_SIMD_NONE - *bOut = bIn; -#endif -} - -static inline void __hv_log2_f(hv_bInf_t bIn, hv_bOutf_t bOut) { -#if HV_SIMD_AVX - hv_assert(0); // __hv_log2_f() not implemented -#elif HV_SIMD_SSE - // https://en.wikipedia.org/wiki/Fast_inverse_square_root - __m128i a = _mm_castps_si128(bIn); - __m128i b = _mm_srli_epi32(a, 23); - __m128i c = _mm_sub_epi32(b, _mm_set1_epi32(127)); // exponent (int) - __m128 d = _mm_cvtepi32_ps(c); // exponent (float) - __m128i e = _mm_or_si128(_mm_andnot_si128(_mm_set1_epi32(0xFF800000), a), _mm_set1_epi32(0x3F800000)); - __m128 f = _mm_castsi128_ps(e); // 1+m (float) - __m128 g = _mm_add_ps(d, f); // e + 1 + m - __m128 h = _mm_add_ps(g, _mm_set1_ps(-0.9569643f)); // e + 1 + m + (sigma-1) - *bOut = h; -#elif HV_SIMD_NEON - int32x4_t a = vreinterpretq_s32_f32(bIn); - int32x4_t b = vshrq_n_s32(a, 23); - int32x4_t c = vsubq_s32(b, vdupq_n_s32(127)); - float32x4_t d = vcvtq_f32_s32(c); - int32x4_t e = vorrq_s32(vbicq_s32(a, vdupq_n_s32(0xFF800000)), vdupq_n_s32(0x3F800000)); - float32x4_t f = vreinterpretq_f32_s32(e); - float32x4_t g = vaddq_f32(d, f); - float32x4_t h = vaddq_f32(g, vdupq_n_f32(-0.9569643f)); - *bOut = h; -#else // HV_SIMD_NONE - *bOut = 1.442695040888963f * hv_log_f(bIn); -#endif -} - -// NOTE(mhroth): this is a pretty ghetto implementation -static inline void __hv_cos_f(hv_bInf_t bIn, hv_bOutf_t bOut) { -#if HV_SIMD_AVX - *bOut = _mm256_set_ps( - hv_cos_f(bIn[7]), hv_cos_f(bIn[6]), hv_cos_f(bIn[5]), hv_cos_f(bIn[4]), - hv_cos_f(bIn[3]), hv_cos_f(bIn[2]), hv_cos_f(bIn[1]), hv_cos_f(bIn[0])); -#elif HV_SIMD_SSE - const float *const b = (float *) &bIn; - *bOut = _mm_set_ps(hv_cos_f(b[3]), hv_cos_f(b[2]), hv_cos_f(b[1]), hv_cos_f(b[0])); -#elif HV_SIMD_NEON - *bOut = (float32x4_t) {hv_cos_f(bIn[0]), hv_cos_f(bIn[1]), hv_cos_f(bIn[2]), hv_cos_f(bIn[3])}; -#else // HV_SIMD_NONE - *bOut = hv_cos_f(bIn); -#endif -} - -static inline void __hv_acos_f(hv_bInf_t bIn, hv_bOutf_t bOut) { -#if HV_SIMD_AVX - hv_assert(0); // __hv_acos_f() not implemented -#elif HV_SIMD_SSE - hv_assert(0); // __hv_acos_f() not implemented -#elif HV_SIMD_NEON - hv_assert(0); // __hv_acos_f() not implemented -#else // HV_SIMD_NONE - *bOut = hv_acos_f(bIn); -#endif -} - -static inline void __hv_cosh_f(hv_bInf_t bIn, hv_bOutf_t bOut) { -#if HV_SIMD_AVX - hv_assert(0); // __hv_cosh_f() not implemented -#elif HV_SIMD_SSE - hv_assert(0); // __hv_cosh_f() not implemented -#elif HV_SIMD_NEON - hv_assert(0); // __hv_cosh_f() not implemented -#else // HV_SIMD_NONE - *bOut = hv_cosh_f(bIn); -#endif -} - -static inline void __hv_acosh_f(hv_bInf_t bIn, hv_bOutf_t bOut) { -#if HV_SIMD_AVX - hv_assert(0); // __hv_acosh_f() not implemented -#elif HV_SIMD_SSE - hv_assert(0); // __hv_acosh_f() not implemented -#elif HV_SIMD_NEON - hv_assert(0); // __hv_acosh_f() not implemented -#else // HV_SIMD_NONE - *bOut = hv_acosh_f(bIn); -#endif -} - -static inline void __hv_sin_f(hv_bInf_t bIn, hv_bOutf_t bOut) { -#if HV_SIMD_AVX - hv_assert(0); // __hv_sin_f() not implemented -#elif HV_SIMD_SSE - hv_assert(0); // __hv_sin_f() not implemented -#elif HV_SIMD_NEON - hv_assert(0); // __hv_sin_f() not implemented -#else // HV_SIMD_NONE - *bOut = hv_sin_f(bIn); -#endif -} - -static inline void __hv_asin_f(hv_bInf_t bIn, hv_bOutf_t bOut) { -#if HV_SIMD_AVX - hv_assert(0); // __hv_asin_f() not implemented -#elif HV_SIMD_SSE - hv_assert(0); // __hv_asin_f() not implemented -#elif HV_SIMD_NEON - hv_assert(0); // __hv_asin_f() not implemented -#else // HV_SIMD_NONE - *bOut = hv_asin_f(bIn); -#endif -} - -static inline void __hv_sinh_f(hv_bInf_t bIn, hv_bOutf_t bOut) { -#if HV_SIMD_AVX - hv_assert(0); // __hv_sinh_f() not implemented -#elif HV_SIMD_SSE - hv_assert(0); // __hv_sinh_f() not implemented -#elif HV_SIMD_NEON - hv_assert(0); // __hv_sinh_f() not implemented -#else // HV_SIMD_NONE - *bOut = hv_sinh_f(bIn); -#endif -} - -static inline void __hv_asinh_f(hv_bInf_t bIn, hv_bOutf_t bOut) { -#if HV_SIMD_AVX - hv_assert(0); // __hv_asinh_f() not implemented -#elif HV_SIMD_SSE - hv_assert(0); // __hv_asinh_f() not implemented -#elif HV_SIMD_NEON - hv_assert(0); // __hv_asinh_f() not implemented -#else // HV_SIMD_NONE - *bOut = hv_asinh_f(bIn); -#endif -} - -static inline void __hv_tan_f(hv_bInf_t bIn, hv_bOutf_t bOut) { -#if HV_SIMD_AVX - hv_assert(0); // __hv_tan_f() not implemented -#elif HV_SIMD_SSE - hv_assert(0); // __hv_tan_f() not implemented -#elif HV_SIMD_NEON - hv_assert(0); // __hv_tan_f() not implemented -#else // HV_SIMD_NONE - *bOut = hv_tan_f(bIn); -#endif -} - -static inline void __hv_atan_f(hv_bInf_t bIn, hv_bOutf_t bOut) { -#if HV_SIMD_AVX - hv_assert(0); // __hv_atan_f() not implemented -#elif HV_SIMD_SSE - hv_assert(0); // __hv_atan_f() not implemented -#elif HV_SIMD_NEON - hv_assert(0); // __hv_atan_f() not implemented -#else // HV_SIMD_NONE - *bOut = hv_atan_f(bIn); -#endif -} - -static inline void __hv_atan2_f(hv_bInf_t bIn0, hv_bInf_t bIn1, hv_bOutf_t bOut) { -#if HV_SIMD_AVX - hv_assert(0); // __hv_atan2_f() not implemented -#elif HV_SIMD_SSE - hv_assert(0); // __hv_atan2_f() not implemented -#elif HV_SIMD_NEON - hv_assert(0); // __hv_atan2_f() not implemented -#else // HV_SIMD_NONE - *bOut = hv_atan2_f(bIn0, bIn1); -#endif -} - -static inline void __hv_tanh_f(hv_bInf_t bIn, hv_bOutf_t bOut) { -#if HV_SIMD_AVX - hv_assert(0); // __hv_tanh_f() not implemented -#elif HV_SIMD_SSE - hv_assert(0); // __hv_tanh_f() not implemented -#elif HV_SIMD_NEON - hv_assert(0); // __hv_tanh_f() not implemented -#else // HV_SIMD_NONE - *bOut = hv_tanh_f(bIn); -#endif -} - -static inline void __hv_atanh_f(hv_bInf_t bIn, hv_bOutf_t bOut) { -#if HV_SIMD_AVX - hv_assert(0); // __hv_atanh_f() not implemented -#elif HV_SIMD_SSE - hv_assert(0); // __hv_atanh_f() not implemented -#elif HV_SIMD_NEON - hv_assert(0); // __hv_atanh_f() not implemented -#else // HV_SIMD_NONE - *bOut = hv_atanh_f(bIn); -#endif -} - -static inline void __hv_sqrt_f(hv_bInf_t bIn, hv_bOutf_t bOut) { -#if HV_SIMD_AVX - *bOut = _mm256_sqrt_ps(bIn); -#elif HV_SIMD_SSE - *bOut = _mm_sqrt_ps(bIn); -#elif HV_SIMD_NEON - const float32x4_t y = vrsqrteq_f32(bIn); - *bOut = vmulq_f32(bIn, vmulq_f32(vrsqrtsq_f32(vmulq_f32(bIn, y), y), y)); // numerical results may be inexact -#else // HV_SIMD_NONE - *bOut = hv_sqrt_f(bIn); -#endif -} - -static inline void __hv_rsqrt_f(hv_bInf_t bIn, hv_bOutf_t bOut) { -#if HV_SIMD_AVX - *bOut = _mm256_rsqrt_ps(bIn); -#elif HV_SIMD_SSE - *bOut = _mm_rsqrt_ps(bIn); -#elif HV_SIMD_NEON - const float32x4_t y = vrsqrteq_f32(bIn); - *bOut = vmulq_f32(vrsqrtsq_f32(vmulq_f32(bIn, y), y), y); // numerical results may be inexact -#else // HV_SIMD_NONE - *bOut = 1.0f/hv_sqrt_f(bIn); -#endif -} - -static inline void __hv_abs_f(hv_bInf_t bIn, hv_bOutf_t bOut) { -#if HV_SIMD_AVX - *bOut = _mm256_andnot_ps(_mm256_set1_ps(-0.0f), bIn); -#elif HV_SIMD_SSE - *bOut = _mm_andnot_ps(_mm_set1_ps(-0.0f), bIn); // == 1 << 31 -#elif HV_SIMD_NEON - *bOut = vabsq_f32(bIn); -#else // HV_SIMD_NONE - *bOut = hv_abs_f(bIn); -#endif -} - -static inline void __hv_neg_f(hv_bInf_t bIn, hv_bOutf_t bOut) { -#if HV_SIMD_AVX - *bOut = _mm256_xor_ps(bIn, _mm256_set1_ps(-0.0f)); -#elif HV_SIMD_SSE - *bOut = _mm_xor_ps(bIn, _mm_set1_ps(-0.0f)); -#elif HV_SIMD_NEON - *bOut = vnegq_f32(bIn); -#else // HV_SIMD_NONE - *bOut = bIn * -1.0f; -#endif -} - -static inline void __hv_exp_f(hv_bInf_t bIn, hv_bOutf_t bOut) { -#if HV_SIMD_AVX - float *const b = (float *) hv_alloca(HV_N_SIMD*sizeof(float)); - _mm256_store_ps(b, bIn); - *bOut = _mm256_set_ps( - hv_exp_f(b[7]), hv_exp_f(b[6]), hv_exp_f(b[5]), hv_exp_f(b[4]), - hv_exp_f(b[3]), hv_exp_f(b[2]), hv_exp_f(b[1]), hv_exp_f(b[0])); -#elif HV_SIMD_SSE - float *const b = (float *) hv_alloca(HV_N_SIMD*sizeof(float)); - _mm_store_ps(b, bIn); - *bOut = _mm_set_ps(hv_exp_f(b[3]), hv_exp_f(b[2]), hv_exp_f(b[1]), hv_exp_f(b[0])); -#elif HV_SIMD_NEON - *bOut = (float32x4_t) { - hv_exp_f(bIn[0]), - hv_exp_f(bIn[1]), - hv_exp_f(bIn[2]), - hv_exp_f(bIn[3])}; -#else // HV_SIMD_NONE - *bOut = hv_exp_f(bIn); -#endif -} - -static inline void __hv_ceil_f(hv_bInf_t bIn, hv_bOutf_t bOut) { -#if HV_SIMD_AVX - *bOut = _mm256_ceil_ps(bIn); -#elif HV_SIMD_SSE - *bOut = _mm_ceil_ps(bIn); -#elif HV_SIMD_NEON -#if __ARM_ARCH >= 8 - *bOut = vrndpq_f32(bIn); -#else - // A slow NEON implementation of __hv_ceil_f() is being used because - // the necessary intrinsic cannot be found. It is only available in ARMv8. - *bOut = (float32x4_t) {hv_ceil_f(bIn[0]), hv_ceil_f(bIn[1]), hv_ceil_f(bIn[2]), hv_ceil_f(bIn[3])}; -#endif // vrndpq_f32 -#else // HV_SIMD_NONE - *bOut = hv_ceil_f(bIn); -#endif -} - -static inline void __hv_floor_f(hv_bInf_t bIn, hv_bOutf_t bOut) { -#if HV_SIMD_AVX - *bOut = _mm256_floor_ps(bIn); -#elif HV_SIMD_SSE - *bOut = _mm_floor_ps(bIn); -#elif HV_SIMD_NEON -#if __ARM_ARCH >= 8 - *bOut = vrndmq_f32(bIn); -#else - // A slow implementation of __hv_floor_f() is being used because - // the necessary intrinsic cannot be found. It is only available from ARMv8. - *bOut = (float32x4_t) {hv_floor_f(bIn[0]), hv_floor_f(bIn[1]), hv_floor_f(bIn[2]), hv_floor_f(bIn[3])}; -#endif // vrndmq_f32 -#else // HV_SIMD_NONE - *bOut = hv_floor_f(bIn); -#endif -} - -// __add~f -static inline void __hv_add_f(hv_bInf_t bIn0, hv_bInf_t bIn1, hv_bOutf_t bOut) { -#if HV_SIMD_AVX - *bOut = _mm256_add_ps(bIn0, bIn1); -#elif HV_SIMD_SSE - *bOut = _mm_add_ps(bIn0, bIn1); -#elif HV_SIMD_NEON - *bOut = vaddq_f32(bIn0, bIn1); -#else // HV_SIMD_NONE - *bOut = bIn0 + bIn1; -#endif -} - -// __add~i -static inline void __hv_add_i(hv_bIni_t bIn0, hv_bIni_t bIn1, hv_bOuti_t bOut) { -#if HV_SIMD_AVX - __m128i x = _mm_add_epi32(_mm256_castsi256_si128(bIn0), _mm256_castsi256_si128(bIn1)); - __m128i y = _mm_add_epi32(_mm256_extractf128_si256(bIn0, 1), _mm256_extractf128_si256(bIn1, 1)); - *bOut = _mm256_insertf128_si256(_mm256_castsi128_si256(x), y, 1); -#elif HV_SIMD_SSE - *bOut = _mm_add_epi32(bIn0, bIn1); -#elif HV_SIMD_NEON - *bOut = vaddq_s32(bIn0, bIn1); -#else // HV_SIMD_NONE - *bOut = bIn0 + bIn1; -#endif -} - -// __sub~f -static inline void __hv_sub_f(hv_bInf_t bIn0, hv_bInf_t bIn1, hv_bOutf_t bOut) { -#if HV_SIMD_AVX - *bOut = _mm256_sub_ps(bIn0, bIn1); -#elif HV_SIMD_SSE - *bOut = _mm_sub_ps(bIn0, bIn1); -#elif HV_SIMD_NEON - *bOut = vsubq_f32(bIn0, bIn1); -#else // HV_SIMD_NONE - *bOut = bIn0 - bIn1; -#endif -} - -// __mul~f -static inline void __hv_mul_f(hv_bInf_t bIn0, hv_bInf_t bIn1, hv_bOutf_t bOut) { -#if HV_SIMD_AVX - *bOut = _mm256_mul_ps(bIn0, bIn1); -#elif HV_SIMD_SSE - *bOut = _mm_mul_ps(bIn0, bIn1); -#elif HV_SIMD_NEON - *bOut = vmulq_f32(bIn0, bIn1); -#else // HV_SIMD_NONE - *bOut = bIn0 * bIn1; -#endif -} - -// __*~i -static inline void __hv_mul_i(hv_bIni_t bIn0, hv_bIni_t bIn1, hv_bOuti_t bOut) { -#if HV_SIMD_AVX - __m128i x = _mm_mullo_epi32(_mm256_castsi256_si128(bIn0), _mm256_castsi256_si128(bIn1)); - __m128i y = _mm_mullo_epi32(_mm256_extractf128_si256(bIn0, 1), _mm256_extractf128_si256(bIn1, 1)); - *bOut = _mm256_insertf128_si256(_mm256_castsi128_si256(x), y, 1); -#elif HV_SIMD_SSE - *bOut = _mm_mullo_epi32(bIn0, bIn1); -#elif HV_SIMD_NEON - *bOut = vmulq_s32(bIn0, bIn1); -#else // HV_SIMD_NONE - *bOut = bIn0 * bIn1; -#endif -} - -// __cast~if -static inline void __hv_cast_if(hv_bIni_t bIn, hv_bOutf_t bOut) { -#if HV_SIMD_AVX - *bOut = _mm256_cvtepi32_ps(bIn); -#elif HV_SIMD_SSE - *bOut = _mm_cvtepi32_ps(bIn); -#elif HV_SIMD_NEON - *bOut = vcvtq_f32_s32(bIn); -#else // HV_SIMD_NONE - *bOut = (float) bIn; -#endif -} - -// __cast~fi -static inline void __hv_cast_fi(hv_bInf_t bIn, hv_bOuti_t bOut) { -#if HV_SIMD_AVX - *bOut = _mm256_cvtps_epi32(bIn); -#elif HV_SIMD_SSE - *bOut = _mm_cvtps_epi32(bIn); -#elif HV_SIMD_NEON - *bOut = vcvtq_s32_f32(bIn); -#else // HV_SIMD_NONE - *bOut = (int) bIn; -#endif -} - -static inline void __hv_div_f(hv_bInf_t bIn0, hv_bInf_t bIn1, hv_bOutf_t bOut) { -#if HV_SIMD_AVX - __m256 a = _mm256_cmp_ps(bIn1, _mm256_setzero_ps(), _CMP_EQ_OQ); - __m256 b = _mm256_div_ps(bIn0, bIn1); - *bOut = _mm256_andnot_ps(a, b); -#elif HV_SIMD_SSE - __m128 a = _mm_cmpeq_ps(bIn1, _mm_setzero_ps()); - __m128 b = _mm_div_ps(bIn0, bIn1); - *bOut = _mm_andnot_ps(a, b); -#elif HV_SIMD_NEON - uint32x4_t a = vceqq_f32(bIn1, vdupq_n_f32(0.0f)); - float32x4_t b = vmulq_f32(bIn0, vrecpeq_f32(bIn1)); // NOTE(mhroth): numerical results may be inexact - *bOut = vreinterpretq_f32_u32(vbicq_u32(vreinterpretq_u32_f32(b), a)); -#else // HV_SIMD_NONE - *bOut = (bIn1 != 0.0f) ? (bIn0 / bIn1) : 0.0f; -#endif -} - -static inline void __hv_min_f(hv_bInf_t bIn0, hv_bInf_t bIn1, hv_bOutf_t bOut) { -#if HV_SIMD_AVX - *bOut = _mm256_min_ps(bIn0, bIn1); -#elif HV_SIMD_SSE - *bOut = _mm_min_ps(bIn0, bIn1); -#elif HV_SIMD_NEON - *bOut = vminq_f32(bIn0, bIn1); -#else // HV_SIMD_NONE - *bOut = hv_min_f(bIn0, bIn1); -#endif -} - -static inline void __hv_min_i(hv_bIni_t bIn0, hv_bIni_t bIn1, hv_bOuti_t bOut) { -#if HV_SIMD_AVX - __m128i x = _mm_min_epi32(_mm256_castsi256_si128(bIn0), _mm256_castsi256_si128(bIn1)); - __m128i y = _mm_min_epi32(_mm256_extractf128_si256(bIn0, 1), _mm256_extractf128_si256(bIn1, 1)); - *bOut = _mm256_insertf128_si256(_mm256_castsi128_si256(x), y, 1); -#elif HV_SIMD_SSE - *bOut = _mm_min_epi32(bIn0, bIn1); -#elif HV_SIMD_NEON - *bOut = vminq_s32(bIn0, bIn1); -#else // HV_SIMD_NONE - *bOut = hv_min_i(bIn0, bIn1); -#endif -} - -static inline void __hv_max_f(hv_bInf_t bIn0, hv_bInf_t bIn1, hv_bOutf_t bOut) { -#if HV_SIMD_AVX - *bOut = _mm256_max_ps(bIn0, bIn1); -#elif HV_SIMD_SSE - *bOut = _mm_max_ps(bIn0, bIn1); -#elif HV_SIMD_NEON - *bOut = vmaxq_f32(bIn0, bIn1); -#else // HV_SIMD_NONE - *bOut = hv_max_f(bIn0, bIn1); -#endif -} - -static inline void __hv_max_i(hv_bIni_t bIn0, hv_bIni_t bIn1, hv_bOuti_t bOut) { -#if HV_SIMD_AVX - __m128i x = _mm_max_epi32(_mm256_castsi256_si128(bIn0), _mm256_castsi256_si128(bIn1)); - __m128i y = _mm_max_epi32(_mm256_extractf128_si256(bIn0, 1), _mm256_extractf128_si256(bIn1, 1)); - *bOut = _mm256_insertf128_si256(_mm256_castsi128_si256(x), y, 1); -#elif HV_SIMD_SSE - *bOut = _mm_max_epi32(bIn0, bIn1); -#elif HV_SIMD_NEON - *bOut = vmaxq_s32(bIn0, bIn1); -#else // HV_SIMD_NONE - *bOut = hv_max_i(bIn0, bIn1); -#endif -} - -static inline void __hv_pow_f(hv_bInf_t bIn0, hv_bInf_t bIn1, hv_bOutf_t bOut) { -#if HV_SIMD_AVX - float *b = (float *) hv_alloca(16*sizeof(float)); - _mm256_store_ps(b, bIn0); - _mm256_store_ps(b+8, bIn1); - *bOut = _mm256_set_ps( - hv_pow_f(b[7], b[15]), - hv_pow_f(b[6], b[14]), - hv_pow_f(b[5], b[13]), - hv_pow_f(b[4], b[12]), - hv_pow_f(b[3], b[11]), - hv_pow_f(b[2], b[10]), - hv_pow_f(b[1], b[9]), - hv_pow_f(b[0], b[8])); -#elif HV_SIMD_SSE - float *b = (float *) hv_alloca(8*sizeof(float)); - _mm_store_ps(b, bIn0); - _mm_store_ps(b+4, bIn1); - *bOut = _mm_set_ps( - hv_pow_f(b[3], b[7]), - hv_pow_f(b[2], b[6]), - hv_pow_f(b[1], b[5]), - hv_pow_f(b[0], b[4])); -#elif HV_SIMD_NEON - *bOut = (float32x4_t) { - hv_pow_f(bIn0[0], bIn1[0]), - hv_pow_f(bIn0[1], bIn1[1]), - hv_pow_f(bIn0[2], bIn1[2]), - hv_pow_f(bIn0[3], bIn1[3])}; -#else // HV_SIMD_NONE - *bOut = hv_pow_f(bIn0, bIn1); -#endif -} - -static inline void __hv_gt_f(hv_bInf_t bIn0, hv_bInf_t bIn1, hv_bOutf_t bOut) { -#if HV_SIMD_AVX - *bOut = _mm256_cmp_ps(bIn0, bIn1, _CMP_GT_OQ); -#elif HV_SIMD_SSE - *bOut = _mm_cmpgt_ps(bIn0, bIn1); -#elif HV_SIMD_NEON - *bOut = vreinterpretq_f32_u32(vcgtq_f32(bIn0, bIn1)); -#else // HV_SIMD_NONE - *bOut = (bIn0 > bIn1) ? 1.0f : 0.0f; -#endif -} - -static inline void __hv_gte_f(hv_bInf_t bIn0, hv_bInf_t bIn1, hv_bOutf_t bOut) { -#if HV_SIMD_AVX - *bOut = _mm256_cmp_ps(bIn0, bIn1, _CMP_GE_OQ); -#elif HV_SIMD_SSE - *bOut = _mm_cmpge_ps(bIn0, bIn1); -#elif HV_SIMD_NEON - *bOut = vreinterpretq_f32_u32(vcgeq_f32(bIn0, bIn1)); -#else // HV_SIMD_NONE - *bOut = (bIn0 >= bIn1) ? 1.0f : 0.0f; -#endif -} - -static inline void __hv_lt_f(hv_bInf_t bIn0, hv_bInf_t bIn1, hv_bOutf_t bOut) { -#if HV_SIMD_AVX - *bOut = _mm256_cmp_ps(bIn0, bIn1, _CMP_LT_OQ); -#elif HV_SIMD_SSE - *bOut = _mm_cmplt_ps(bIn0, bIn1); -#elif HV_SIMD_NEON - *bOut = vreinterpretq_f32_u32(vcltq_f32(bIn0, bIn1)); -#else // HV_SIMD_NONE - *bOut = (bIn0 < bIn1) ? 1.0f : 0.0f; -#endif -} - -static inline void __hv_lte_f(hv_bInf_t bIn0, hv_bInf_t bIn1, hv_bOutf_t bOut) { -#if HV_SIMD_AVX - *bOut = _mm256_cmp_ps(bIn0, bIn1, _CMP_LE_OQ); -#elif HV_SIMD_SSE - *bOut = _mm_cmple_ps(bIn0, bIn1); -#elif HV_SIMD_NEON - *bOut = vreinterpretq_f32_u32(vcleq_f32(bIn0, bIn1)); -#else // HV_SIMD_NONE - *bOut = (bIn0 <= bIn1) ? 1.0f : 0.0f; -#endif -} - -static inline void __hv_eq_f(hv_bInf_t bIn0, hv_bInf_t bIn1, hv_bOutf_t bOut) { -#if HV_SIMD_AVX - *bOut = _mm256_cmp_ps(bIn0, bIn1, _CMP_EQ_OQ); -#elif HV_SIMD_SSE - *bOut = _mm_cmpeq_ps(bIn0, bIn1); -#elif HV_SIMD_NEON - *bOut = vreinterpretq_f32_u32(vceqq_f32(bIn0, bIn1)); -#else // HV_SIMD_NONE - *bOut = (bIn0 == bIn1) ? 1.0f : 0.0f; -#endif -} - -static inline void __hv_neq_f(hv_bInf_t bIn0, hv_bInf_t bIn1, hv_bOutf_t bOut) { -#if HV_SIMD_AVX - *bOut = _mm256_cmp_ps(bIn0, bIn1, _CMP_NEQ_OQ); -#elif HV_SIMD_SSE - *bOut = _mm_cmpneq_ps(bIn0, bIn1); -#elif HV_SIMD_NEON - *bOut = vreinterpretq_f32_u32(vmvnq_u32(vceqq_f32(bIn0, bIn1))); -#else // HV_SIMD_NONE - *bOut = (bIn0 != bIn1) ? 1.0f : 0.0f; -#endif -} - -static inline void __hv_or_f(hv_bInf_t bIn0, hv_bInf_t bIn1, hv_bOutf_t bOut) { -#if HV_SIMD_AVX - *bOut = _mm256_or_ps(bIn1, bIn0); -#elif HV_SIMD_SSE - *bOut = _mm_or_ps(bIn1, bIn0); -#elif HV_SIMD_NEON - *bOut = vreinterpretq_f32_u32(vorrq_u32(vreinterpretq_u32_f32(bIn1), vreinterpretq_u32_f32(bIn0))); -#else // HV_SIMD_NONE - if (bIn0 == 0.0f && bIn1 == 0.0f) *bOut = 0.0f; - else if (bIn0 == 0.0f) *bOut = bIn1; - else if (bIn1 == 0.0f) *bOut = bIn0; - else hv_assert(0); -#endif -} - -static inline void __hv_and_f(hv_bInf_t bIn0, hv_bInf_t bIn1, hv_bOutf_t bOut) { -#if HV_SIMD_AVX - *bOut = _mm256_and_ps(bIn1, bIn0); -#elif HV_SIMD_SSE - *bOut = _mm_and_ps(bIn1, bIn0); -#elif HV_SIMD_NEON - *bOut = vreinterpretq_f32_u32(vandq_u32(vreinterpretq_u32_f32(bIn1), vreinterpretq_u32_f32(bIn0))); -#else // HV_SIMD_NONE - if (bIn0 == 0.0f || bIn1 == 0.0f) *bOut = 0.0f; - else if (bIn0 == 1.0f) *bOut = bIn1; - else if (bIn1 == 1.0f) *bOut = bIn0; - else hv_assert(0); -#endif -} - -static inline void __hv_andnot_f(hv_bInf_t bIn0_mask, hv_bInf_t bIn1, hv_bOutf_t bOut) { -#if HV_SIMD_AVX - *bOut = _mm256_andnot_ps(bIn0_mask, bIn1); -#elif HV_SIMD_SSE - *bOut = _mm_andnot_ps(bIn0_mask, bIn1); -#elif HV_SIMD_NEON - *bOut = vreinterpretq_f32_s32(vbicq_s32(vreinterpretq_s32_f32(bIn1), vreinterpretq_s32_f32(bIn0_mask))); -#else // HV_SIMD_NONE - *bOut = (bIn0_mask == 0.0f) ? bIn1 : 0.0f; -#endif -} - -// bOut = (bIn0 * bIn1) + bIn2 -static inline void __hv_fma_f(hv_bInf_t bIn0, hv_bInf_t bIn1, hv_bInf_t bIn2, hv_bOutf_t bOut) { -#if HV_SIMD_AVX -#if HV_SIMD_FMA - *bOut = _mm256_fmadd_ps(bIn0, bIn1, bIn2); -#else - *bOut = _mm256_add_ps(_mm256_mul_ps(bIn0, bIn1), bIn2); -#endif // HV_SIMD_FMA -#elif HV_SIMD_SSE -#if HV_SIMD_FMA - *bOut = _mm_fmadd_ps(bIn0, bIn1, bIn2); -#else - *bOut = _mm_add_ps(_mm_mul_ps(bIn0, bIn1), bIn2); -#endif // HV_SIMD_FMA -#elif HV_SIMD_NEON -#if __ARM_ARCH >= 8 - *bOut = vfmaq_f32(bIn2, bIn0, bIn1); -#else - // NOTE(mhroth): it turns out, fma SUUUUCKS on lesser ARM architectures - *bOut = vaddq_f32(vmulq_f32(bIn0, bIn1), bIn2); -#endif -#else // HV_SIMD_NONE - *bOut = hv_fma_f(bIn0, bIn1, bIn2); -#endif -} - -// bOut = (bIn0 * bIn1) - bIn2 -static inline void __hv_fms_f(hv_bInf_t bIn0, hv_bInf_t bIn1, hv_bInf_t bIn2, hv_bOutf_t bOut) { -#if HV_SIMD_AVX -#if HV_SIMD_FMA - *bOut = _mm256_fmsub_ps(bIn0, bIn1, bIn2); -#else - *bOut = _mm256_sub_ps(_mm256_mul_ps(bIn0, bIn1), bIn2); -#endif // HV_SIMD_FMA -#elif HV_SIMD_SSE -#if HV_SIMD_FMA - *bOut = _mm_fmsub_ps(bIn0, bIn1, bIn2); -#else - *bOut = _mm_sub_ps(_mm_mul_ps(bIn0, bIn1), bIn2); -#endif // HV_SIMD_FMA -#elif HV_SIMD_NEON -#if __ARM_ARCH >= 8 - *bOut = vfmsq_f32(bIn2, bIn0, bIn1); -#else - // NOTE(mhroth): it turns out, fma SUUUUCKS on lesser ARM architectures - *bOut = vsubq_f32(vmulq_f32(bIn0, bIn1), bIn2); -#endif -#else // HV_SIMD_NONE - *bOut = (bIn0 * bIn1) - bIn2; -#endif -} - -#endif // _HEAVY_MATH_H_ diff --git a/examples/examples-dsp/examples-pd/streams-pd-audiokit/HvMessage.c b/examples/examples-dsp/examples-pd/streams-pd-audiokit/HvMessage.c deleted file mode 100644 index 0f1ac5d7c2..0000000000 --- a/examples/examples-dsp/examples-pd/streams-pd-audiokit/HvMessage.c +++ /dev/null @@ -1,199 +0,0 @@ -/** - * Copyright (c) 2014-2018 Enzien Audio Ltd. - * - * Permission to use, copy, modify, and/or distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH - * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY - * AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, - * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM - * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR - * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR - * PERFORMANCE OF THIS SOFTWARE. - */ - -#include "HvMessage.h" - -HvMessage *msg_init(HvMessage *m, hv_size_t numElements, hv_uint32_t timestamp) { - m->timestamp = timestamp; - m->numElements = (hv_uint16_t) numElements; - m->numBytes = (hv_uint16_t) msg_getCoreSize(numElements); - return m; -} - -HvMessage *msg_initWithFloat(HvMessage *m, hv_uint32_t timestamp, float f) { - m->timestamp = timestamp; - m->numElements = 1; - m->numBytes = sizeof(HvMessage); - msg_setFloat(m, 0, f); - return m; -} - -HvMessage *msg_initWithBang(HvMessage *m, hv_uint32_t timestamp) { - m->timestamp = timestamp; - m->numElements = 1; - m->numBytes = sizeof(HvMessage); - msg_setBang(m, 0); - return m; -} - -HvMessage *msg_initWithSymbol(HvMessage *m, hv_uint32_t timestamp, const char *s) { - m->timestamp = timestamp; - m->numElements = 1; - m->numBytes = sizeof(HvMessage) + (hv_uint16_t) hv_strlen(s); - msg_setSymbol(m, 0, s); - return m; -} - -HvMessage *msg_initWithHash(HvMessage *m, hv_uint32_t timestamp, hv_uint32_t h) { - m->timestamp = timestamp; - m->numElements = 1; - m->numBytes = sizeof(HvMessage); - msg_setHash(m, 0, h); - return m; -} - -void msg_copyToBuffer(const HvMessage *m, char *buffer, hv_size_t len) { - HvMessage *r = (HvMessage *) buffer; - - hv_size_t len_r = msg_getCoreSize(msg_getNumElements(m)); - - // assert that the message is not already larger than the length of the buffer - hv_assert(len_r <= len); - - // copy the basic message to the buffer - hv_memcpy(r, m, len_r); - - char *p = buffer + len_r; // points to the end of the base message - for (int i = 0; i < msg_getNumElements(m); ++i) { - if (msg_isSymbol(m,i)) { - const hv_size_t symLen = (hv_size_t) hv_strlen(msg_getSymbol(m,i)) + 1; // include the trailing null char - hv_assert(len_r + symLen <= len); // stay safe! - hv_strncpy(p, msg_getSymbol(m,i), symLen); - msg_setSymbol(r, i, p); - p += symLen; - len_r += symLen; - } - } - - r->numBytes = (hv_uint16_t) len_r; // update the message size in memory -} - -// the message is serialised such that all symbol elements are placed in order at the end of the buffer -HvMessage *msg_copy(const HvMessage *m) { - const hv_uint32_t heapSize = msg_getSize(m); - char *r = (char *) hv_malloc(heapSize); - hv_assert(r != NULL); - msg_copyToBuffer(m, r, heapSize); - return (HvMessage *) r; -} - -void msg_free(HvMessage *m) { - hv_free(m); // because heap messages are serialised in memory, a simple call to free releases the message -} - -bool msg_hasFormat(const HvMessage *m, const char *fmt) { - hv_assert(fmt != NULL); - const int n = msg_getNumElements(m); - for (int i = 0; i < n; ++i) { - switch (fmt[i]) { - case 'b': if (!msg_isBang(m, i)) return false; break; - case 'f': if (!msg_isFloat(m, i)) return false; break; - case 'h': if (!msg_isHash(m, i)) return false; break; - case 's': if (!msg_isSymbol(m, i)) return false; break; - default: return false; - } - } - return (fmt[n] == '\0'); -} - -bool msg_compareSymbol(const HvMessage *m, int i, const char *s) { - switch (msg_getType(m,i)) { - case HV_MSG_SYMBOL: return !hv_strcmp(msg_getSymbol(m, i), s); - case HV_MSG_HASH: return (msg_getHash(m,i) == hv_string_to_hash(s)); - default: return false; - } -} - -bool msg_equalsElement(const HvMessage *m, int i_m, const HvMessage *n, int i_n) { - if (i_m < msg_getNumElements(m) && i_n < msg_getNumElements(n)) { - if (msg_getType(m, i_m) == msg_getType(n, i_n)) { - switch (msg_getType(m, i_m)) { - case HV_MSG_BANG: return true; - case HV_MSG_FLOAT: return (msg_getFloat(m, i_m) == msg_getFloat(n, i_n)); - case HV_MSG_SYMBOL: return msg_compareSymbol(m, i_m, msg_getSymbol(n, i_n)); - case HV_MSG_HASH: return msg_getHash(m,i_m) == msg_getHash(n,i_n); - default: break; - } - } - } - return false; -} - -void msg_setElementToFrom(HvMessage *n, int i_n, const HvMessage *const m, int i_m) { - switch (msg_getType(m, i_m)) { - case HV_MSG_BANG: msg_setBang(n, i_n); break; - case HV_MSG_FLOAT: msg_setFloat(n, i_n, msg_getFloat(m, i_m)); break; - case HV_MSG_SYMBOL: msg_setSymbol(n, i_n, msg_getSymbol(m, i_m)); break; - case HV_MSG_HASH: msg_setHash(n, i_n, msg_getHash(m, i_m)); - default: break; - } -} - -hv_uint32_t msg_getHash(const HvMessage *const m, int i) { - hv_assert(i < msg_getNumElements(m)); // invalid index - switch (msg_getType(m,i)) { - case HV_MSG_BANG: return 0xFFFFFFFF; - case HV_MSG_FLOAT: { - float f = msg_getFloat(m,i); - return *((hv_uint32_t *) &f); - } - case HV_MSG_SYMBOL: return hv_string_to_hash(msg_getSymbol(m,i)); - case HV_MSG_HASH: return (&(m->elem)+i)->data.h; - default: return 0; - } -} - -char *msg_toString(const HvMessage *m) { - hv_assert(msg_getNumElements(m) > 0); - int *len = (int *) hv_alloca(msg_getNumElements(m)*sizeof(int)); - int size = 0; // the total length of our final buffer - - // loop through every element in our list of atoms - // first loop figures out how long our buffer should be - for (int i = 0; i < msg_getNumElements(m); i++) { - // length of our string is each atom plus a space, or \0 on the end - switch (msg_getType(m, i)) { - case HV_MSG_BANG: len[i] = hv_snprintf(NULL, 0, "%s", "bang") + 1; break; - case HV_MSG_FLOAT: len[i] = hv_snprintf(NULL, 0, "%g", msg_getFloat(m, i)) + 1; break; - case HV_MSG_SYMBOL: len[i] = hv_snprintf(NULL, 0, "%s", msg_getSymbol(m, i)) + 1; break; - case HV_MSG_HASH: len[i] = hv_snprintf(NULL, 0, "0x%X", msg_getHash(m, i)) + 1; break; - default: break; - } - size += len[i]; - } - - hv_assert(size > 0); - - // now we do the piecewise concatenation into our final string - // the final buffer we will pass back after concatenating all strings - user should free it - char *finalString = (char *) hv_malloc(size*sizeof(char)); - hv_assert(finalString != NULL); - int pos = 0; - for (int i = 0; i < msg_getNumElements(m); i++) { - // put a string representation of each atom into the final string - switch (msg_getType(m, i)) { - case HV_MSG_BANG: hv_snprintf(finalString+pos, len[i], "%s", "bang"); break; - case HV_MSG_FLOAT: hv_snprintf(finalString+pos, len[i], "%g", msg_getFloat(m, i)); break; - case HV_MSG_SYMBOL: hv_snprintf(finalString+pos, len[i], "%s", msg_getSymbol(m, i)); break; - case HV_MSG_HASH: hv_snprintf(finalString+pos, len[i], "0x%X", msg_getHash(m, i)); break; - default: break; - } - pos += len[i]; - finalString[pos-1] = 32; // ASCII space - } - finalString[size-1] = '\0'; // ensure that the string is null terminated - return finalString; -} diff --git a/examples/examples-dsp/examples-pd/streams-pd-audiokit/HvMessage.h b/examples/examples-dsp/examples-pd/streams-pd-audiokit/HvMessage.h deleted file mode 100644 index a3fdd1c9e6..0000000000 --- a/examples/examples-dsp/examples-pd/streams-pd-audiokit/HvMessage.h +++ /dev/null @@ -1,183 +0,0 @@ -/** - * Copyright (c) 2014-2018 Enzien Audio Ltd. - * - * Permission to use, copy, modify, and/or distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH - * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY - * AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, - * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM - * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR - * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR - * PERFORMANCE OF THIS SOFTWARE. - */ - -#ifndef _HEAVY_MESSAGE_H_ -#define _HEAVY_MESSAGE_H_ - -#include "HvUtils.h" - -#ifdef __cplusplus -extern "C" { -#endif - -typedef enum ElementType { - HV_MSG_BANG = 0, - HV_MSG_FLOAT = 1, - HV_MSG_SYMBOL = 2, - HV_MSG_HASH = 3 -} ElementType; - -typedef struct Element { - ElementType type; - union { - float f; // float - const char *s; // symbol - hv_uint32_t h; // hash - } data; -} Element; - -typedef struct HvMessage { - hv_uint32_t timestamp; // the sample at which this message should be processed - hv_uint16_t numElements; - hv_uint16_t numBytes; // the total number of bytes that this message occupies in memory, including strings - Element elem; -} HvMessage; - -typedef struct ReceiverMessagePair { - hv_uint32_t receiverHash; - HvMessage msg; -} ReceiverMessagePair; - -#define HV_MESSAGE_ON_STACK(_x) (HvMessage *) hv_alloca(msg_getCoreSize(_x)) - -/** Returns the number of bytes that this message consumes in memory, not including strings. */ -static inline hv_size_t msg_getCoreSize(hv_size_t numElements) { - hv_assert(numElements > 0); - return sizeof(HvMessage) + ((numElements-1) * sizeof(Element)); -} - -HvMessage *msg_copy(const HvMessage *m); - -/** Copies the message into the given buffer. The buffer must be at least as large as msg_getNumHeapBytes(). */ -void msg_copyToBuffer(const HvMessage *m, char *buffer, hv_size_t len); - -void msg_setElementToFrom(HvMessage *n, int indexN, const HvMessage *const m, int indexM); - -/** Frees a message on the heap. Does nothing if argument is NULL. */ -void msg_free(HvMessage *m); - -HvMessage *msg_init(HvMessage *m, hv_size_t numElements, hv_uint32_t timestamp); - -HvMessage *msg_initWithFloat(HvMessage *m, hv_uint32_t timestamp, float f); - -HvMessage *msg_initWithBang(HvMessage *m, hv_uint32_t timestamp); - -HvMessage *msg_initWithSymbol(HvMessage *m, hv_uint32_t timestamp, const char *s); - -HvMessage *msg_initWithHash(HvMessage *m, hv_uint32_t timestamp, hv_uint32_t h); - -static inline hv_uint32_t msg_getTimestamp(const HvMessage *m) { - return m->timestamp; -} - -static inline void msg_setTimestamp(HvMessage *m, hv_uint32_t timestamp) { - m->timestamp = timestamp; -} - -static inline int msg_getNumElements(const HvMessage *m) { - return (int) m->numElements; -} - -/** Returns the total number of bytes this message consumes in memory. */ -static inline hv_uint32_t msg_getSize(const HvMessage *m) { - return m->numBytes; -} - -static inline ElementType msg_getType(const HvMessage *m, int index) { - hv_assert(index < msg_getNumElements(m)); // invalid index - return (&(m->elem)+index)->type; -} - -static inline void msg_setBang(HvMessage *m, int index) { - hv_assert(index < msg_getNumElements(m)); // invalid index - (&(m->elem)+index)->type = HV_MSG_BANG; - (&(m->elem)+index)->data.s = NULL; -} - -static inline bool msg_isBang(const HvMessage *m, int index) { - return (index < msg_getNumElements(m)) ? (msg_getType(m,index) == HV_MSG_BANG) : false; -} - -static inline void msg_setFloat(HvMessage *m, int index, float f) { - hv_assert(index < msg_getNumElements(m)); // invalid index - (&(m->elem)+index)->type = HV_MSG_FLOAT; - (&(m->elem)+index)->data.f = f; -} - -static inline float msg_getFloat(const HvMessage *const m, int index) { - hv_assert(index < msg_getNumElements(m)); // invalid index - return (&(m->elem)+index)->data.f; -} - -static inline bool msg_isFloat(const HvMessage *const m, int index) { - return (index < msg_getNumElements(m)) ? (msg_getType(m,index) == HV_MSG_FLOAT) : false; -} - -static inline void msg_setHash(HvMessage *m, int index, hv_uint32_t h) { - hv_assert(index < msg_getNumElements(m)); // invalid index - (&(m->elem)+index)->type = HV_MSG_HASH; - (&(m->elem)+index)->data.h = h; -} - -static inline bool msg_isHash(const HvMessage *m, int index) { - return (index < msg_getNumElements(m)) ? (msg_getType(m, index) == HV_MSG_HASH) : false; -} - -/** Returns true if the element is a hash or symbol. False otherwise. */ -static inline bool msg_isHashLike(const HvMessage *m, int index) { - return (index < msg_getNumElements(m)) ? ((msg_getType(m, index) == HV_MSG_HASH) || (msg_getType(m, index) == HV_MSG_SYMBOL)) : false; -} - -/** Returns a 32-bit hash of the given element. */ -hv_uint32_t msg_getHash(const HvMessage *const m, int i); - -static inline void msg_setSymbol(HvMessage *m, int index, const char *s) { - hv_assert(index < msg_getNumElements(m)); // invalid index - hv_assert(s != NULL); - (&(m->elem)+index)->type = HV_MSG_SYMBOL; - (&(m->elem)+index)->data.s = s; - // NOTE(mhroth): if the same message container is reused and string reset, - // then the message size will be overcounted - m->numBytes += (hv_uint16_t) (hv_strlen(s) + 1); // also count '\0' -} - -static inline const char *msg_getSymbol(const HvMessage *m, int index) { - hv_assert(index < msg_getNumElements(m)); // invalid index - return (&(m->elem)+index)->data.s; -} - -static inline bool msg_isSymbol(const HvMessage *m, int index) { - return (index < msg_getNumElements(m)) ? (msg_getType(m, index) == HV_MSG_SYMBOL) : false; -} - -bool msg_compareSymbol(const HvMessage *m, int i, const char *s); - -/** Returns 1 if the element i_m of message m is equal to element i_n of message n. */ -bool msg_equalsElement(const HvMessage *m, int i_m, const HvMessage *n, int i_n); - -bool msg_hasFormat(const HvMessage *m, const char *fmt); - -/** - * Create a string representation of the message. Suitable for use by the print object. - * The resulting string must be freed by the caller. - */ -char *msg_toString(const HvMessage *msg); - -#ifdef __cplusplus -} -#endif - -#endif // _HEAVY_MESSAGE_H_ diff --git a/examples/examples-dsp/examples-pd/streams-pd-audiokit/HvMessagePool.c b/examples/examples-dsp/examples-pd/streams-pd-audiokit/HvMessagePool.c deleted file mode 100644 index dbddb1d1dc..0000000000 --- a/examples/examples-dsp/examples-pd/streams-pd-audiokit/HvMessagePool.c +++ /dev/null @@ -1,144 +0,0 @@ -/** - * Copyright (c) 2014-2018 Enzien Audio Ltd. - * - * Permission to use, copy, modify, and/or distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH - * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY - * AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, - * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM - * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR - * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR - * PERFORMANCE OF THIS SOFTWARE. - */ - -#include "HvMessagePool.h" -#include "HvMessage.h" - -// the number of bytes reserved at a time from the pool -#define MP_BLOCK_SIZE_BYTES 512 - -#if HV_APPLE -#pragma mark - MessageList -#endif - -typedef struct MessageListNode { - char *p; - struct MessageListNode *next; -} MessageListNode; - -static inline bool ml_hasAvailable(HvMessagePoolList *ml) { - return (ml->head != NULL); -} - -static char *ml_pop(HvMessagePoolList *ml) { - MessageListNode *n = ml->head; - ml->head = n->next; - n->next = ml->pool; - ml->pool = n; - char *const p = n->p; - n->p = NULL; // set to NULL to make it clear that this node does not have a valid buffer - return p; -} - -/** Push a MessageListNode with the given pointer onto the head of the queue. */ -static void ml_push(HvMessagePoolList *ml, void *p) { - MessageListNode *n = NULL; - if (ml->pool != NULL) { - // take an empty MessageListNode from the pool - n = ml->pool; - ml->pool = n->next; - } else { - // a MessageListNode is not available, allocate one - n = (MessageListNode *) hv_malloc(sizeof(MessageListNode)); - hv_assert(n != NULL); - } - n->p = (char *) p; - n->next = ml->head; - ml->head = n; // push to the front of the queue -} - -static void ml_free(HvMessagePoolList *ml) { - if (ml != NULL) { - while (ml_hasAvailable(ml)) { - ml_pop(ml); - } - while (ml->pool != NULL) { - MessageListNode *n = ml->pool; - ml->pool = n->next; - hv_free(n); - } - } -} - -#if HV_APPLE -#pragma mark - HvMessagePool -#endif - -static hv_size_t mp_messagelistIndexForSize(hv_size_t byteSize) { - return (hv_size_t) hv_max_i((hv_min_max_log2((hv_uint32_t) byteSize) - 5), 0); -} - -hv_size_t mp_init(HvMessagePool *mp, hv_size_t numKB) { - mp->bufferSize = numKB * 1024; - mp->buffer = (char *) hv_malloc(mp->bufferSize); - hv_assert(mp->buffer != NULL); - mp->bufferIndex = 0; - - // initialise all message lists - for (int i = 0; i < MP_NUM_MESSAGE_LISTS; i++) { - mp->lists[i].head = NULL; - mp->lists[i].pool = NULL; - } - - return mp->bufferSize; -} - -void mp_free(HvMessagePool *mp) { - hv_free(mp->buffer); - for (int i = 0; i < MP_NUM_MESSAGE_LISTS; i++) { - ml_free(&mp->lists[i]); - } -} - -void mp_freeMessage(HvMessagePool *mp, HvMessage *m) { - const hv_size_t b = msg_getSize(m); // the number of bytes that a message occupies in memory - const hv_size_t i = mp_messagelistIndexForSize(b); // the HvMessagePoolList index in the pool - HvMessagePoolList *ml = &mp->lists[i]; - const hv_size_t chunkSize = 32 << i; - hv_memclear(m, chunkSize); // clear the chunk, just in case - ml_push(ml, m); -} - -HvMessage *mp_addMessage(HvMessagePool *mp, const HvMessage *m) { - const hv_size_t b = msg_getSize(m); - // determine the message list index to allocate data from based on the msg size - // smallest chunk size is 32 bytes - const hv_size_t i = mp_messagelistIndexForSize(b); - - hv_assert(i < MP_NUM_MESSAGE_LISTS); // how many chunk sizes do we want to support? 32, 64, 128, 256 at the moment - HvMessagePoolList *ml = &mp->lists[i]; - const hv_size_t chunkSize = 32 << i; - - if (ml_hasAvailable(ml)) { - char *buf = ml_pop(ml); - msg_copyToBuffer(m, buf, chunkSize); - return (HvMessage *) buf; - } else { - // if no appropriately sized buffer is immediately available, increase the size of the used buffer - const hv_size_t newIndex = mp->bufferIndex + MP_BLOCK_SIZE_BYTES; - hv_assert((newIndex <= mp->bufferSize) && - "The message pool buffer size has been exceeded. The context cannot store more messages. " - "Try using the new_with_options() initialiser with a larger pool size (default is 10KB)."); - - for (hv_size_t j = mp->bufferIndex; j < newIndex; j += chunkSize) { - ml_push(ml, mp->buffer + j); // push new nodes onto the list with chunk pointers - } - mp->bufferIndex = newIndex; - char *buf = ml_pop(ml); - msg_copyToBuffer(m, buf, chunkSize); - return (HvMessage *) buf; - } -} diff --git a/examples/examples-dsp/examples-pd/streams-pd-audiokit/HvMessagePool.h b/examples/examples-dsp/examples-pd/streams-pd-audiokit/HvMessagePool.h deleted file mode 100644 index 693e470e12..0000000000 --- a/examples/examples-dsp/examples-pd/streams-pd-audiokit/HvMessagePool.h +++ /dev/null @@ -1,71 +0,0 @@ -/** - * Copyright (c) 2014-2018 Enzien Audio Ltd. - * - * Permission to use, copy, modify, and/or distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH - * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY - * AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, - * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM - * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR - * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR - * PERFORMANCE OF THIS SOFTWARE. - */ - -#ifndef _MESSAGE_POOL_H_ -#define _MESSAGE_POOL_H_ - -#include "HvUtils.h" - -#ifdef HV_MP_NUM_MESSAGE_LISTS -#define MP_NUM_MESSAGE_LISTS HV_MP_NUM_MESSAGE_LISTS -#else // HV_MP_NUM_MESSAGE_LISTS -#define MP_NUM_MESSAGE_LISTS 4 -#endif // HV_MP_NUM_MESSAGE_LISTS - -#ifdef __cplusplus -extern "C" { -#endif - -typedef struct HvMessagePoolList { - struct MessageListNode *head; // list of currently available blocks - struct MessageListNode *pool; // list of currently used blocks -} HvMessagePoolList; - -typedef struct HvMessagePool { - char *buffer; // the buffer of all messages - hv_size_t bufferSize; // in bytes - hv_size_t bufferIndex; // the number of total reserved bytes - - HvMessagePoolList lists[MP_NUM_MESSAGE_LISTS]; -} HvMessagePool; - -/** - * The HvMessagePool is a basic memory management system. It reserves a large block of memory at initialisation - * and proceeds to divide this block into smaller chunks (usually 512 bytes) as they are needed. These chunks are - * further divided into 32, 64, 128, or 256 sections. Each of these sections is managed by a HvMessagePoolList (MPL). - * An MPL is a linked-list data structure which is initialised such that its own pool of listnodes is filled with nodes - * that point at each subblock (e.g. each 32-byte block of a 512-block chunk). - * - * HvMessagePool is loosely inspired by TCMalloc. http://goog-perftools.sourceforge.net/doc/tcmalloc.html - */ - -hv_size_t mp_init(struct HvMessagePool *mp, hv_size_t numKB); - -void mp_free(struct HvMessagePool *mp); - -/** - * Adds a message to the pool and returns a pointer to the copy. Returns NULL - * if no space was available in the pool. - */ -struct HvMessage *mp_addMessage(struct HvMessagePool *mp, const struct HvMessage *m); - -void mp_freeMessage(struct HvMessagePool *mp, struct HvMessage *m); - -#ifdef __cplusplus -} -#endif - -#endif // _MESSAGE_POOL_H_ diff --git a/examples/examples-dsp/examples-pd/streams-pd-audiokit/HvMessageQueue.c b/examples/examples-dsp/examples-pd/streams-pd-audiokit/HvMessageQueue.c deleted file mode 100644 index 2eeab8ff8a..0000000000 --- a/examples/examples-dsp/examples-pd/streams-pd-audiokit/HvMessageQueue.c +++ /dev/null @@ -1,215 +0,0 @@ -/** - * Copyright (c) 2014-2018 Enzien Audio Ltd. - * - * Permission to use, copy, modify, and/or distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH - * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY - * AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, - * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM - * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR - * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR - * PERFORMANCE OF THIS SOFTWARE. - */ - -#include "HvMessageQueue.h" - -hv_size_t mq_initWithPoolSize(HvMessageQueue *q, hv_size_t poolSizeKB) { - hv_assert(poolSizeKB > 0); - q->head = NULL; - q->tail = NULL; - q->pool = NULL; - return mp_init(&q->mp, poolSizeKB); -} - -void mq_free(HvMessageQueue *q) { - mq_clear(q); - while (q->pool != NULL) { - MessageNode *n = q->pool; - q->pool = q->pool->next; - hv_free(n); - } - mp_free(&q->mp); -} - -static MessageNode *mq_getOrCreateNodeFromPool(HvMessageQueue *q) { - if (q->pool == NULL) { - // if necessary, create a new empty node - q->pool = (MessageNode *) hv_malloc(sizeof(MessageNode)); - hv_assert(q->pool != NULL); - q->pool->next = NULL; - } - MessageNode *node = q->pool; - q->pool = q->pool->next; - return node; -} - -int mq_size(HvMessageQueue *q) { - int size = 0; - MessageNode *n = q->head; - while (n != NULL) { - ++size; - n = n->next; - } - return size; -} - -HvMessage *mq_addMessage(HvMessageQueue *q, const HvMessage *m, int let, - void (*sendMessage)(HeavyContextInterface *, int, const HvMessage *)) { - MessageNode *node = mq_getOrCreateNodeFromPool(q); - node->m = mp_addMessage(&q->mp, m); - node->let = let; - node->sendMessage = sendMessage; - node->prev = NULL; - node->next = NULL; - - if (q->tail != NULL) { - // the list already contains elements - q->tail->next = node; - node->prev = q->tail; - q->tail = node; - } else { - // the list is empty - node->prev = NULL; - q->head = node; - q->tail = node; - } - return mq_node_getMessage(node); -} - -HvMessage *mq_addMessageByTimestamp(HvMessageQueue *q, const HvMessage *m, int let, - void (*sendMessage)(HeavyContextInterface *, int, const HvMessage *)) { - if (mq_hasMessage(q)) { - MessageNode *n = mq_getOrCreateNodeFromPool(q); - n->m = mp_addMessage(&q->mp, m); - n->let = let; - n->sendMessage = sendMessage; - - if (msg_getTimestamp(m) < msg_getTimestamp(q->head->m)) { - // the message occurs before the current head - n->next = q->head; - q->head->prev = n; - n->prev = NULL; - q->head = n; - } else if (msg_getTimestamp(m) >= msg_getTimestamp(q->tail->m)) { - // the message occurs after the current tail - n->next = NULL; - n->prev = q->tail; - q->tail->next = n; - q->tail = n; - } else { - // the message occurs somewhere between the head and tail - MessageNode *node = q->head; - while (node != NULL) { - if (msg_getTimestamp(m) < msg_getTimestamp(node->next->m)) { - MessageNode *r = node->next; - node->next = n; - n->next = r; - n->prev = node; - r->prev = n; - break; - } - node = node->next; - } - } - return n->m; - } else { - // add a message to the head - return mq_addMessage(q, m, let, sendMessage); - } -} - -void mq_pop(HvMessageQueue *q) { - if (mq_hasMessage(q)) { - MessageNode *n = q->head; - - mp_freeMessage(&q->mp, n->m); - n->m = NULL; - - n->let = 0; - n->sendMessage = NULL; - - q->head = n->next; - if (q->head == NULL) { - q->tail = NULL; - } else { - q->head->prev = NULL; - } - n->next = q->pool; - n->prev = NULL; - q->pool = n; - } -} - -bool mq_removeMessage(HvMessageQueue *q, HvMessage *m, void (*sendMessage)(HeavyContextInterface *, int, const HvMessage *)) { - if (mq_hasMessage(q)) { - if (mq_node_getMessage(q->head) == m) { // msg in head node - // only remove the message if sendMessage is the same as the stored one, - // if the sendMessage argument is NULL, it is not checked and will remove any matching message pointer - if (sendMessage == NULL || q->head->sendMessage == sendMessage) { - mq_pop(q); - return true; - } - } else { - MessageNode *prevNode = q->head; - MessageNode *currNode = q->head->next; - while ((currNode != NULL) && (currNode->m != m)) { - prevNode = currNode; - currNode = currNode->next; - } - if (currNode != NULL) { - if (sendMessage == NULL || currNode->sendMessage == sendMessage) { - mp_freeMessage(&q->mp, m); - currNode->m = NULL; - currNode->let = 0; - currNode->sendMessage = NULL; - if (currNode == q->tail) { // msg in tail node - prevNode->next = NULL; - q->tail = prevNode; - } else { // msg in middle node - prevNode->next = currNode->next; - currNode->next->prev = prevNode; - } - currNode->next = (q->pool == NULL) ? NULL : q->pool; - currNode->prev = NULL; - q->pool = currNode; - return true; - } - } - } - } - return false; -} - -void mq_clear(HvMessageQueue *q) { - while (mq_hasMessage(q)) { - mq_pop(q); - } -} - -void mq_clearAfter(HvMessageQueue *q, const hv_uint32_t timestamp) { - MessageNode *n = q->tail; - while (n != NULL && timestamp <= msg_getTimestamp(n->m)) { - // free the node's message - mp_freeMessage(&q->mp, n->m); - n->m = NULL; - n->let = 0; - n->sendMessage = NULL; - - // the tail points at the previous node - q->tail = n->prev; - - // put the node back in the pool - n->next = q->pool; - n->prev = NULL; - if (q->pool != NULL) q->pool->prev = n; - q->pool = n; - - // update the tail node - n = q->tail; - } - - if (q->tail == NULL) q->head = NULL; -} diff --git a/examples/examples-dsp/examples-pd/streams-pd-audiokit/HvMessageQueue.h b/examples/examples-dsp/examples-pd/streams-pd-audiokit/HvMessageQueue.h deleted file mode 100644 index a35e4aa579..0000000000 --- a/examples/examples-dsp/examples-pd/streams-pd-audiokit/HvMessageQueue.h +++ /dev/null @@ -1,101 +0,0 @@ -/** - * Copyright (c) 2014-2018 Enzien Audio Ltd. - * - * Permission to use, copy, modify, and/or distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH - * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY - * AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, - * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM - * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR - * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR - * PERFORMANCE OF THIS SOFTWARE. - */ - -#ifndef _MESSAGE_QUEUE_H_ -#define _MESSAGE_QUEUE_H_ - -#include "HvMessage.h" -#include "HvMessagePool.h" - -#ifdef __cplusplus -extern "C" { -#endif - -#ifdef __cplusplus -class HeavyContextInterface; -#else -typedef struct HeavyContextInterface HeavyContextInterface; -#endif - -typedef struct MessageNode { - struct MessageNode *prev; // doubly linked list - struct MessageNode *next; - HvMessage *m; - void (*sendMessage)(HeavyContextInterface *, int, const HvMessage *); - int let; -} MessageNode; - -/** A doubly linked list containing scheduled messages. */ -typedef struct HvMessageQueue { - MessageNode *head; // the head of the queue - MessageNode *tail; // the tail of the queue - MessageNode *pool; // the head of the reserve pool - HvMessagePool mp; -} HvMessageQueue; - -hv_size_t mq_initWithPoolSize(HvMessageQueue *q, hv_size_t poolSizeKB); - -void mq_free(HvMessageQueue *q); - -int mq_size(HvMessageQueue *q); - -static inline HvMessage *mq_node_getMessage(MessageNode *n) { - return n->m; -} - -static inline int mq_node_getLet(MessageNode *n) { - return n->let; -} - -static inline bool mq_hasMessage(HvMessageQueue *q) { - return (q->head != NULL); -} - -// true if there is a message and it occurs before (<) timestamp -static inline bool mq_hasMessageBefore(HvMessageQueue *const q, const hv_uint32_t timestamp) { - return mq_hasMessage(q) && (msg_getTimestamp(mq_node_getMessage(q->head)) < timestamp); -} - -static inline MessageNode *mq_peek(HvMessageQueue *q) { - return q->head; -} - -/** Appends the message to the end of the queue. */ -HvMessage *mq_addMessage(HvMessageQueue *q, const HvMessage *m, int let, - void (*sendMessage)(HeavyContextInterface *, int, const HvMessage *)); - -/** Insert in ascending order the message acccording to its timestamp. */ -HvMessage *mq_addMessageByTimestamp(HvMessageQueue *q, const HvMessage *m, int let, - void (*sendMessage)(HeavyContextInterface *, int, const HvMessage *)); - -/** Pop the message at the head of the queue (and free its memory). */ -void mq_pop(HvMessageQueue *q); - -/** Remove a message from the queue (and free its memory) */ -bool mq_removeMessage(HvMessageQueue *q, HvMessage *m, - void (*sendMessage)(HeavyContextInterface *, int, const HvMessage *)); - -/** Clears (and frees) all messages in the queue. */ -void mq_clear(HvMessageQueue *q); - -/** Removes all messages occuring at or after the given timestamp. */ -void mq_clearAfter(HvMessageQueue *q, const hv_uint32_t timestamp); - -#ifdef __cplusplus -} -#endif - -#endif // _MESSAGE_QUEUE_H_ diff --git a/examples/examples-dsp/examples-pd/streams-pd-audiokit/HvSignalPhasor.c b/examples/examples-dsp/examples-pd/streams-pd-audiokit/HvSignalPhasor.c deleted file mode 100644 index b763b95a16..0000000000 --- a/examples/examples-dsp/examples-pd/streams-pd-audiokit/HvSignalPhasor.c +++ /dev/null @@ -1,141 +0,0 @@ -/** - * Copyright (c) 2014-2018 Enzien Audio Ltd. - * - * Permission to use, copy, modify, and/or distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH - * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY - * AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, - * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM - * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR - * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR - * PERFORMANCE OF THIS SOFTWARE. - */ - -#include "HvSignalPhasor.h" - -#define HV_PHASOR_2_32 4294967296.0 - -#if HV_SIMD_AVX -static void sPhasor_updatePhase(SignalPhasor *o, float p) { - o->phase = _mm256_set1_ps(p+1.0f); // o->phase is in range [1,2] -#elif HV_SIMD_SSE - static void sPhasor_updatePhase(SignalPhasor *o, hv_uint32_t p) { - o->phase = _mm_set1_epi32(p); -#elif HV_SIMD_NEON - static void sPhasor_updatePhase(SignalPhasor *o, hv_uint32_t p) { - o->phase = vdupq_n_u32(p); -#else // HV_SIMD_NONE - static void sPhasor_updatePhase(SignalPhasor *o, hv_uint32_t p) { - o->phase = p; -#endif -} - -// input phase is in the range of [0,1]. It is independent of o->phase. -#if HV_SIMD_AVX -static void sPhasor_k_updatePhase(SignalPhasor *o, float p) { - o->phase = _mm256_set_ps( - p+1.0f+7.0f*o->step.f2sc, p+1.0f+6.0f*o->step.f2sc, - p+1.0f+5.0f*o->step.f2sc, p+1.0f+4.0f*o->step.f2sc, - p+1.0f+3.0f*o->step.f2sc, p+1.0f+2.0f*o->step.f2sc, - p+1.0f+o->step.f2sc, p+1.0f); - - // ensure that o->phase is still in range [1,2] - o->phase = _mm256_or_ps(_mm256_andnot_ps( - _mm256_set1_ps(-INFINITY), o->phase), _mm256_set1_ps(1.0f)); -#elif HV_SIMD_SSE -static void sPhasor_k_updatePhase(SignalPhasor *o, hv_uint32_t p) { - o->phase = _mm_set_epi32(3*o->step.s+p, 2*o->step.s+p, o->step.s+p, p); -#elif HV_SIMD_NEON -static void sPhasor_k_updatePhase(SignalPhasor *o, hv_uint32_t p) { - o->phase = (uint32x4_t) {p, o->step.s+p, 2*o->step.s+p, 3*o->step.s+p}; -#else // HV_SIMD_NONE -static void sPhasor_k_updatePhase(SignalPhasor *o, hv_uint32_t p) { - o->phase = p; -#endif -} - -static void sPhasor_k_updateFrequency(SignalPhasor *o, float f, double r) { -#if HV_SIMD_AVX - o->step.f2sc = (float) (f/r); - o->inc = _mm256_set1_ps((float) (8.0f*f/r)); - sPhasor_k_updatePhase(o, o->phase[0]); -#elif HV_SIMD_SSE - o->step.s = (hv_int32_t) (f*(HV_PHASOR_2_32/r)); - o->inc = _mm_set1_epi32(4*o->step.s); - const hv_uint32_t *const p = (hv_uint32_t *) &o->phase; - sPhasor_k_updatePhase(o, p[0]); -#elif HV_SIMD_NEON - o->step.s = (hv_int32_t) (f*(HV_PHASOR_2_32/r)); - o->inc = vdupq_n_s32(4*o->step.s); - sPhasor_k_updatePhase(o, vgetq_lane_u32(o->phase, 0)); -#else // HV_SIMD_NONE - o->step.s = (hv_int32_t) (f*(HV_PHASOR_2_32/r)); - o->inc = o->step.s; - // no need to update phase -#endif -} - -hv_size_t sPhasor_init(SignalPhasor *o, double samplerate) { -#if HV_SIMD_AVX - o->phase = _mm256_set1_ps(1.0f); - o->inc = _mm256_setzero_ps(); - o->step.f2sc = (float) (1.0/samplerate); -#elif HV_SIMD_SSE - o->phase = _mm_setzero_si128(); - o->inc = _mm_setzero_si128(); - o->step.f2sc = (float) (HV_PHASOR_2_32/samplerate); -#elif HV_SIMD_NEON - o->phase = vdupq_n_u32(0); - o->inc = vdupq_n_s32(0); - o->step.f2sc = (float) (HV_PHASOR_2_32/samplerate); -#else // HV_SIMD_NONE - o->phase = 0; - o->inc = 0; - o->step.f2sc = (float) (HV_PHASOR_2_32/samplerate); -#endif - return 0; -} - -void sPhasor_onMessage(HeavyContextInterface *_c, SignalPhasor *o, int letIn, const HvMessage *m) { - if (letIn == 1) { - if (msg_isFloat(m,0)) { - float p = msg_getFloat(m,0); - while (p < 0.0f) p += 1.0f; // wrap phase to [0,1] - while (p > 1.0f) p -= 1.0f; -#if HV_SIMD_AVX - sPhasor_updatePhase(o, p); -#else // HV_SIMD_SSE || HV_SIMD_NEON || HV_SIMD_NONE - sPhasor_updatePhase(o, (hv_uint32_t) (p * HV_PHASOR_2_32)); -#endif - } - } -} - -hv_size_t sPhasor_k_init(SignalPhasor *o, float frequency, double samplerate) { - __hv_zero_i((hv_bOuti_t) &o->phase); - sPhasor_k_updateFrequency(o, frequency, samplerate); - return 0; -} - -void sPhasor_k_onMessage(HeavyContextInterface *_c, SignalPhasor *o, int letIn, const HvMessage *m) { - if (msg_isFloat(m,0)) { - switch (letIn) { - case 0: sPhasor_k_updateFrequency(o, msg_getFloat(m,0), hv_getSampleRate(_c)); break; - case 1: { - float p = msg_getFloat(m,0); - while (p < 0.0f) p += 1.0f; // wrap phase to [0,1] - while (p > 1.0f) p -= 1.0f; -#if HV_SIMD_AVX - sPhasor_k_updatePhase(o, p); -#else // HV_SIMD_SSE || HV_SIMD_NEON || HV_SIMD_NONE - sPhasor_k_updatePhase(o, (hv_uint32_t) (p * HV_PHASOR_2_32)); -#endif - break; - } - default: break; - } - } -} diff --git a/examples/examples-dsp/examples-pd/streams-pd-audiokit/HvSignalPhasor.h b/examples/examples-dsp/examples-pd/streams-pd-audiokit/HvSignalPhasor.h deleted file mode 100644 index 08c6464978..0000000000 --- a/examples/examples-dsp/examples-pd/streams-pd-audiokit/HvSignalPhasor.h +++ /dev/null @@ -1,139 +0,0 @@ -/** - * Copyright (c) 2014-2018 Enzien Audio Ltd. - * - * Permission to use, copy, modify, and/or distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH - * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY - * AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, - * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM - * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR - * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR - * PERFORMANCE OF THIS SOFTWARE. - */ - -#ifndef _HEAVY_SIGNAL_PHASOR_H_ -#define _HEAVY_SIGNAL_PHASOR_H_ - -#include "HvHeavyInternal.h" - -#ifdef __cplusplus -extern "C" { -#endif - -typedef struct SignalPhasor { -#if HV_SIMD_AVX - __m256 phase; // current phase - __m256 inc; // phase increment -#elif HV_SIMD_SSE - __m128i phase; - __m128i inc; -#elif HV_SIMD_NEON - uint32x4_t phase; - int32x4_t inc; -#else // HV_SIMD_NONE - hv_uint32_t phase; - hv_int32_t inc; -#endif - union { - float f2sc; // float to step conversion (used for __phasor~f) - hv_int32_t s; // step value (used for __phasor_k~f) - } step; -} SignalPhasor; - -hv_size_t sPhasor_init(SignalPhasor *o, double samplerate); - -hv_size_t sPhasor_k_init(SignalPhasor *o, float frequency, double samplerate); - -void sPhasor_k_onMessage(HeavyContextInterface *_c, SignalPhasor *o, int letIn, const HvMessage *m); - -void sPhasor_onMessage(HeavyContextInterface *_c, SignalPhasor *o, int letIn, const HvMessage *m); - -static inline void __hv_phasor_f(SignalPhasor *o, hv_bInf_t bIn, hv_bOutf_t bOut) { -#if HV_SIMD_AVX - __m256 p = _mm256_mul_ps(bIn, _mm256_set1_ps(o->step.f2sc)); // a b c d e f g h - - __m256 z = _mm256_setzero_ps(); - - // http://stackoverflow.com/questions/11906814/how-to-rotate-an-sse-avx-vector - __m256 a = _mm256_permute_ps(p, _MM_SHUFFLE(2,1,0,3)); // d a b c h e f g - __m256 b = _mm256_permute2f128_ps(a, a, 0x01); // h e f g d a b c - __m256 c = _mm256_blend_ps(a, b, 0x10); // d a b c d e f g - __m256 d = _mm256_blend_ps(c, z, 0x01); // 0 a b c d e f g - __m256 e = _mm256_add_ps(p, d); // a (a+b) (b+c) (c+d) (d+e) (e+f) (f+g) (g+h) - - __m256 f = _mm256_permute_ps(e, _MM_SHUFFLE(1,0,3,2)); // (b+c) (c+d) a (a+b) (f+g) (g+h) (d+e) (e+f) - __m256 g = _mm256_permute2f128_ps(f, f, 0x01); // (f+g) (g+h) (d+e) (e+f) (b+c) (c+d) a (a+b) - __m256 h = _mm256_blend_ps(f, g, 0x33); // (b+c) (c+d) a (a+b) (b+c) (c+d) (d+e) (e+f) - __m256 i = _mm256_blend_ps(h, z, 0x03); // 0 0 a (a+b) (b+c) (c+d) (d+e) (e+f) - __m256 j = _mm256_add_ps(e, i); // a (a+b) (a+b+c) (a+b+c+d) (b+c+d+e) (c+d+e+f) (d+e+f+g) (e+f+g+h) - - __m256 k = _mm256_permute2f128_ps(j, z, 0x02); // 0 0 0 0 a (a+b) (a+b+c) (a+b+c+d) (b+c+d+e) - __m256 m = _mm256_add_ps(j, k); // a (a+b) (a+b+c) (a+b+c+d) (a+b+c+d+e) (a+b+c+d+e+f) (a+b+c+d+e+f+g) (a+b+c+d+e+f+g+h) - - __m256 n = _mm256_or_ps(_mm256_andnot_ps( - _mm256_set1_ps(-INFINITY), - _mm256_add_ps(o->phase, m)), - _mm256_set1_ps(1.0f)); - - *bOut = _mm256_sub_ps(n, _mm256_set1_ps(1.0f)); - - __m256 x = _mm256_permute_ps(n, _MM_SHUFFLE(3,3,3,3)); - o->phase = _mm256_permute2f128_ps(x, x, 0x11); -#elif HV_SIMD_SSE - __m128i p = _mm_cvtps_epi32(_mm_mul_ps(bIn, _mm_set1_ps(o->step.f2sc))); // convert frequency to step - p = _mm_add_epi32(p, _mm_slli_si128(p, 4)); // add incremental steps to phase (prefix sum) - p = _mm_add_epi32(p, _mm_slli_si128(p, 8)); // http://stackoverflow.com/questions/10587598/simd-prefix-sum-on-intel-cpu?rq=1 - p = _mm_add_epi32(o->phase, p); - *bOut = _mm_sub_ps(_mm_castsi128_ps( - _mm_or_si128(_mm_srli_epi32(p, 9), - _mm_set_epi32(0x3F800000, 0x3F800000, 0x3F800000, 0x3F800000))), - _mm_set1_ps(1.0f)); - o->phase = _mm_shuffle_epi32(p, _MM_SHUFFLE(3,3,3,3)); -#elif HV_SIMD_NEON - int32x4_t p = vcvtq_s32_f32(vmulq_n_f32(bIn, o->step.f2sc)); - p = vaddq_s32(p, vextq_s32(vdupq_n_s32(0), p, 3)); // http://stackoverflow.com/questions/11259596/arm-neon-intrinsics-rotation - p = vaddq_s32(p, vextq_s32(vdupq_n_s32(0), p, 2)); - uint32x4_t pp = vaddq_u32(o->phase, vreinterpretq_u32_s32(p)); - *bOut = vsubq_f32(vreinterpretq_f32_u32(vorrq_u32(vshrq_n_u32(pp, 9), vdupq_n_u32(0x3F800000))), vdupq_n_f32(1.0f)); - o->phase = vdupq_n_u32(pp[3]); -#else // HV_SIMD_NONE - const hv_uint32_t p = (o->phase >> 9) | 0x3F800000; - *bOut = *((float *) (&p)) - 1.0f; - o->phase += ((int) (bIn * o->step.f2sc)); -#endif -} - -static inline void __hv_phasor_k_f(SignalPhasor *o, hv_bOutf_t bOut) { -#if HV_SIMD_AVX - *bOut = _mm256_sub_ps(o->phase, _mm256_set1_ps(1.0f)); - o->phase = _mm256_or_ps(_mm256_andnot_ps( - _mm256_set1_ps(-INFINITY), - _mm256_add_ps(o->phase, o->inc)), - _mm256_set1_ps(1.0f)); -#elif HV_SIMD_SSE - *bOut = _mm_sub_ps(_mm_castsi128_ps( - _mm_or_si128(_mm_srli_epi32(o->phase, 9), - _mm_set_epi32(0x3F800000, 0x3F800000, 0x3F800000, 0x3F800000))), - _mm_set1_ps(1.0f)); - o->phase = _mm_add_epi32(o->phase, o->inc); -#elif HV_SIMD_NEON - *bOut = vsubq_f32(vreinterpretq_f32_u32( - vorrq_u32(vshrq_n_u32(o->phase, 9), - vdupq_n_u32(0x3F800000))), - vdupq_n_f32(1.0f)); - o->phase = vaddq_u32(o->phase, vreinterpretq_u32_s32(o->inc)); -#else // HV_SIMD_NONE - const hv_uint32_t p = (o->phase >> 9) | 0x3F800000; - *bOut = *((float *) (&p)) - 1.0f; - o->phase += o->inc; -#endif -} - -#ifdef __cplusplus -} // extern "C" -#endif - -#endif // _HEAVY_SIGNAL_PHASOR_H_ diff --git a/examples/examples-dsp/examples-pd/streams-pd-audiokit/HvSignalVar.c b/examples/examples-dsp/examples-pd/streams-pd-audiokit/HvSignalVar.c deleted file mode 100644 index 393cd23424..0000000000 --- a/examples/examples-dsp/examples-pd/streams-pd-audiokit/HvSignalVar.c +++ /dev/null @@ -1,75 +0,0 @@ -/** - * Copyright (c) 2014-2018 Enzien Audio Ltd. - * - * Permission to use, copy, modify, and/or distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH - * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY - * AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, - * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM - * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR - * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR - * PERFORMANCE OF THIS SOFTWARE. - */ - -#include "HvSignalVar.h" - -// __var~f - -static void sVarf_update(SignalVarf *o, float k, float step, bool reverse) { -#if HV_SIMD_AVX - if (reverse) o->v = _mm256_setr_ps(k+7.0f*step, k+6.0f*step, k+5.0f*step, k+4.0f*step, k+3.0f*step, k+2.0f*step, k+step, k); - else o->v = _mm256_set_ps(k+7.0f*step, k+6.0f*step, k+5.0f*step, k+4.0f*step, k+3.0f*step, k+2.0f*step, k+step, k); -#elif HV_SIMD_SSE - if (reverse) o->v = _mm_setr_ps(k+3.0f*step, k+2.0f*step, k+step, k); - else o->v = _mm_set_ps(k+3.0f*step, k+2.0f*step, k+step, k); -#elif HV_SIMD_NEON - if (reverse) o->v = (float32x4_t) {3.0f*step+k, 2.0f*step+k, step+k, k}; - else o->v = (float32x4_t) {k, step+k, 2.0f*step+k, 3.0f*step+k}; -#else // HV_SIMD_NONE - o->v = k; -#endif -} - -hv_size_t sVarf_init(SignalVarf *o, float k, float step, bool reverse) { - sVarf_update(o, k, step, reverse); - return 0; -} - -void sVarf_onMessage(HeavyContextInterface *_c, SignalVarf *o, const HvMessage *m) { - if (msg_isFloat(m,0)) { - sVarf_update(o, msg_getFloat(m,0), msg_isFloat(m,1) ? msg_getFloat(m,1) : 0.0f, msg_getNumElements(m) == 3); - } -} - - - -// __var~i - -static void sVari_update(SignalVari *o, int k, int step, bool reverse) { -#if HV_SIMD_AVX - if (reverse) o->v = _mm256_setr_epi32(k+7*step, k+6*step, k+5*step, k+4*step, k+3*step, k+2*step, k+step, k); - else o->v = _mm256_set_epi32(k+7*step, k+6*step, k+5*step, k+4*step, k+3*step, k+2*step, k+step, k); -#elif HV_SIMD_SSE - if (reverse) o->v = _mm_setr_epi32(k+3*step, k+2*step, k+step, k); - else o->v = _mm_set_epi32(k+3*step, k+2*step, k+step, k); -#elif HV_SIMD_NEON - if (reverse) o->v = (int32x4_t) {3*step+k, 2*step+k, step+k, k}; - else o->v = (int32x4_t) {k, step+k, 2*step+k, 3*step+k}; -#else // HV_SIMD_NEON - o->v = k; -#endif -} - -hv_size_t sVari_init(SignalVari *o, int k, int step, bool reverse) { - sVari_update(o, k, step, reverse); - return 0; -} - -void sVari_onMessage(HeavyContextInterface *_c, SignalVari *o, const HvMessage *m) { - if (msg_isFloat(m,0)) { - sVari_update(o, (int) msg_getFloat(m,0), msg_isFloat(m,1) ? (int) msg_getFloat(m,1) : 0, msg_getNumElements(m) == 3); - } -} diff --git a/examples/examples-dsp/examples-pd/streams-pd-audiokit/HvSignalVar.h b/examples/examples-dsp/examples-pd/streams-pd-audiokit/HvSignalVar.h deleted file mode 100644 index 6502f6b5cc..0000000000 --- a/examples/examples-dsp/examples-pd/streams-pd-audiokit/HvSignalVar.h +++ /dev/null @@ -1,94 +0,0 @@ -/** - * Copyright (c) 2014-2018 Enzien Audio Ltd. - * - * Permission to use, copy, modify, and/or distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH - * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY - * AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, - * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM - * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR - * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR - * PERFORMANCE OF THIS SOFTWARE. - */ - -#ifndef _HEAVY_SIGNAL_VAR_H_ -#define _HEAVY_SIGNAL_VAR_H_ - -#include "HvHeavyInternal.h" - -#ifdef __cplusplus -extern "C" { -#endif - -// __var~f, __varread~f, __varwrite~f - -typedef struct SignalVarf { - hv_bufferf_t v; -} SignalVarf; - -hv_size_t sVarf_init(SignalVarf *o, float k, float step, bool reverse); - -static inline void __hv_varread_f(SignalVarf *o, hv_bOutf_t bOut) { - *bOut = o->v; -} - -static inline void __hv_varwrite_f(SignalVarf *o, hv_bInf_t bIn) { - o->v = bIn; -} - -void sVarf_onMessage(HeavyContextInterface *_c, SignalVarf *o, const HvMessage *m); - - - -// __var~i, __varread~i, __varwrite~i - -typedef struct SignalVari { - hv_bufferi_t v; -} SignalVari; - -hv_size_t sVari_init(SignalVari *o, int k, int step, bool reverse); - -static inline void __hv_varread_i(SignalVari *o, hv_bOuti_t bOut) { - *bOut = o->v; -} - -static inline void __hv_varwrite_i(SignalVari *o, hv_bIni_t bIn) { - o->v = bIn; -} - -void sVari_onMessage(HeavyContextInterface *_c, SignalVari *o, const HvMessage *m); - - - -// __var_k~f, __var_k~i - -#if HV_SIMD_AVX -#define __hv_var_k_i(_z,_a,_b,_c,_d,_e,_f,_g,_h) *_z=_mm256_set_epi32(_h,_g,_f,_e,_d,_c,_b,_a) -#define __hv_var_k_i_r(_z,_a,_b,_c,_d,_e,_f,_g,_h) *_z=_mm256_set_epi32(_a,_b,_c,_d,_e,_f,_g,_h) -#define __hv_var_k_f(_z,_a,_b,_c,_d,_e,_f,_g,_h) *_z=_mm256_set_ps(_h,_g,_f,_e,_d,_c,_b,_a) -#define __hv_var_k_f_r(_z,_a,_b,_c,_d,_e,_f,_g,_h) *_z=_mm256_set_ps(_a,_b,_c,_d,_e,_f,_g,_h) -#elif HV_SIMD_SSE -#define __hv_var_k_i(_z,_a,_b,_c,_d,_e,_f,_g,_h) *_z=_mm_set_epi32(_d,_c,_b,_a) -#define __hv_var_k_i_r(_z,_a,_b,_c,_d,_e,_f,_g,_h) *_z=_mm_set_epi32(_a,_b,_c,_d) -#define __hv_var_k_f(_z,_a,_b,_c,_d,_e,_f,_g,_h) *_z=_mm_set_ps(_d,_c,_b,_a) -#define __hv_var_k_f_r(_z,_a,_b,_c,_d,_e,_f,_g,_h) *_z=_mm_set_ps(_a,_b,_c,_d) -#elif HV_SIMD_NEON -#define __hv_var_k_i(_z,_a,_b,_c,_d,_e,_f,_g,_h) *_z=((int32x4_t) {_a,_b,_c,_d}) -#define __hv_var_k_i_r(_z,_a,_b,_c,_d,_e,_f,_g,_h) *_z=((int32x4_t) {_d,_c,_b,_a}) -#define __hv_var_k_f(_z,_a,_b,_c,_d,_e,_f,_g,_h) *_z=((float32x4_t) {_a,_b,_c,_d}) -#define __hv_var_k_f_r(_z,_a,_b,_c,_d,_e,_f,_g,_h) *_z=((float32x4_t) {_d,_c,_b,_a}) -#else // HV_SIMD_NONE -#define __hv_var_k_i(_z,_a,_b,_c,_d,_e,_f,_g,_h) *_z=_a -#define __hv_var_k_i_r(_z,_a,_b,_c,_d,_e,_f,_g,_h) *_z=_a -#define __hv_var_k_f(_z,_a,_b,_c,_d,_e,_f,_g,_h) *_z=_a -#define __hv_var_k_f_r(_z,_a,_b,_c,_d,_e,_f,_g,_h) *_z=_a -#endif - -#ifdef __cplusplus -} // extern "C" -#endif - -#endif // _HEAVY_SIGNAL_VAR_H_ diff --git a/examples/examples-dsp/examples-pd/streams-pd-audiokit/HvTable.c b/examples/examples-dsp/examples-pd/streams-pd-audiokit/HvTable.c deleted file mode 100644 index 1b2cd38315..0000000000 --- a/examples/examples-dsp/examples-pd/streams-pd-audiokit/HvTable.c +++ /dev/null @@ -1,110 +0,0 @@ -/** - * Copyright (c) 2014-2018 Enzien Audio Ltd. - * - * Permission to use, copy, modify, and/or distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH - * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY - * AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, - * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM - * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR - * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR - * PERFORMANCE OF THIS SOFTWARE. - */ - -#include "HvTable.h" -#include "HvMessage.h" - -hv_size_t hTable_init(HvTable *o, int length) { - o->length = length; - // true size of the table is always an integer multple of HV_N_SIMD - o->size = (length + HV_N_SIMD_MASK) & ~HV_N_SIMD_MASK; - // add an extra length for mirroring - o->allocated = o->size + HV_N_SIMD; - o->head = 0; - hv_size_t numBytes = o->allocated * sizeof(float); - o->buffer = (float *) hv_malloc(numBytes); - hv_assert(o->buffer != NULL); - hv_memclear(o->buffer, numBytes); - return numBytes; -} - -hv_size_t hTable_initWithData(HvTable *o, int length, const float *data) { - o->length = length; - o->size = (length + HV_N_SIMD_MASK) & ~HV_N_SIMD_MASK; - o->allocated = o->size + HV_N_SIMD; - o->head = 0; - hv_size_t numBytes = o->size * sizeof(float); - o->buffer = (float *) hv_malloc(numBytes); - hv_assert(o->buffer != NULL); - hv_memclear(o->buffer, numBytes); - hv_memcpy(o->buffer, data, length*sizeof(float)); - return numBytes; -} - -hv_size_t hTable_initWithFinalData(HvTable *o, int length, float *data) { - o->length = length; - o->size = length; - o->allocated = length; - o->buffer = data; - o->head = 0; - return 0; -} - -void hTable_free(HvTable *o) { - hv_free(o->buffer); -} - -int hTable_resize(HvTable *o, hv_uint32_t newLength) { - // TODO(mhroth): update context with memory allocated by table - // NOTE(mhroth): mirrored bytes are not necessarily carried over - const hv_uint32_t newSize = (newLength + HV_N_SIMD_MASK) & ~HV_N_SIMD_MASK; - if (newSize == o->size) return 0; // early exit if no change in size - const hv_uint32_t oldSizeBytes = (hv_uint32_t) (o->size * sizeof(float)); - const hv_uint32_t newAllocated = newSize + HV_N_SIMD; - const hv_uint32_t newAllocatedBytes = (hv_uint32_t) (newAllocated * sizeof(float)); - - float *b = (float *) hv_realloc(o->buffer, newAllocatedBytes); - hv_assert(b != NULL); // error while reallocing! - // ensure that hv_realloc has given us a correctly aligned buffer - if ((((hv_uintptr_t) (const void *) b) & ((0x1< o->size) { - hv_memclear(b + o->size, (newAllocated - o->size) * sizeof(float)); // clear new parts of the buffer - } - o->buffer = b; - } else { - // if not, we have to re-malloc ourselves - char *c = (char *) hv_malloc(newAllocatedBytes); - hv_assert(c != NULL); // error while allocating new buffer! - if (newAllocatedBytes > oldSizeBytes) { - hv_memcpy(c, b, oldSizeBytes); - hv_memclear(c + oldSizeBytes, newAllocatedBytes - oldSizeBytes); - } else { - hv_memcpy(c, b, newAllocatedBytes); - } - hv_free(b); - o->buffer = (float *) c; - } - o->length = newLength; - o->size = newSize; - o->allocated = newAllocated; - return (int) (newAllocated - oldSizeBytes - (HV_N_SIMD*sizeof(float))); -} - -void hTable_onMessage(HeavyContextInterface *_c, HvTable *o, int letIn, const HvMessage *m, - void (*sendMessage)(HeavyContextInterface *, int, const HvMessage *)) { - if (msg_compareSymbol(m,0,"resize") && msg_isFloat(m,1) && msg_getFloat(m,1) >= 0.0f) { - hTable_resize(o, (int) hv_ceil_f(msg_getFloat(m,1))); // apply ceil to ensure that tables always have enough space - - // send out the new size of the table - HvMessage *n = HV_MESSAGE_ON_STACK(1); - msg_initWithFloat(n, msg_getTimestamp(m), (float) hTable_getSize(o)); - sendMessage(_c, 0, n); - } - - else if (msg_compareSymbol(m,0,"mirror")) { - hv_memcpy(o->buffer+o->size, o->buffer, HV_N_SIMD*sizeof(float)); - } -} diff --git a/examples/examples-dsp/examples-pd/streams-pd-audiokit/HvTable.h b/examples/examples-dsp/examples-pd/streams-pd-audiokit/HvTable.h deleted file mode 100644 index 1d748874ea..0000000000 --- a/examples/examples-dsp/examples-pd/streams-pd-audiokit/HvTable.h +++ /dev/null @@ -1,88 +0,0 @@ -/** - * Copyright (c) 2014-2018 Enzien Audio Ltd. - * - * Permission to use, copy, modify, and/or distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH - * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY - * AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, - * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM - * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR - * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR - * PERFORMANCE OF THIS SOFTWARE. - */ - -#ifndef _HEAVY_TABLE_H_ -#define _HEAVY_TABLE_H_ - -#include "HvHeavy.h" -#include "HvUtils.h" - -#ifdef __cplusplus -extern "C" { -#endif - -typedef struct HvTable { - float *buffer; - // the number of values that the table is requested to have - hv_uint32_t length; - - // the number of usable values that the table actually has - // this is always an even multiple of HV_N_SIMD - hv_uint32_t size; - - // Note that the true size of the table is (size + HV_N_SIMD), - // with the trailing values used by the system, e.g. to create a circular - // buffer - hv_uint32_t allocated; - - hv_uint32_t head; // the most recently written point -} HvTable; - -hv_size_t hTable_init(HvTable *o, int length); - -hv_size_t hTable_initWithData(HvTable *o, int length, const float *data); - -hv_size_t hTable_initWithFinalData(HvTable *o, int length, float *data); - -void hTable_free(HvTable *o); - -int hTable_resize(HvTable *o, hv_uint32_t newLength); - -void hTable_onMessage(HeavyContextInterface *_c, HvTable *o, int letIn, const HvMessage *m, - void (*sendMessage)(HeavyContextInterface *, int, const HvMessage *)); - -static inline float *hTable_getBuffer(HvTable *o) { - return o->buffer; -} - -// the user-requested length of the table (number of floats) -static inline hv_uint32_t hTable_getLength(HvTable *o) { - return o->length; -} - -// the usable length of the table (an even multiple of HV_N_SIMD) -static inline hv_uint32_t hTable_getSize(HvTable *o) { - return o->size; -} - -// the number of floats allocated to this table (usually size + HV_N_SIMD) -static inline hv_uint32_t hTable_getAllocated(HvTable *o) { - return o->allocated; -} - -static inline hv_uint32_t hTable_getHead(HvTable *o) { - return o->head; -} - -static inline void hTable_setHead(HvTable *o, hv_uint32_t head) { - o->head = head; -} - -#ifdef __cplusplus -} // extern "C" -#endif - -#endif // _HEAVY_TABLE_H_ diff --git a/examples/examples-dsp/examples-pd/streams-pd-audiokit/HvUtils.c b/examples/examples-dsp/examples-pd/streams-pd-audiokit/HvUtils.c deleted file mode 100644 index 2fdf682c4c..0000000000 --- a/examples/examples-dsp/examples-pd/streams-pd-audiokit/HvUtils.c +++ /dev/null @@ -1,54 +0,0 @@ -/** - * Copyright (c) 2014-2018 Enzien Audio Ltd. - * - * Permission to use, copy, modify, and/or distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH - * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY - * AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, - * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM - * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR - * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR - * PERFORMANCE OF THIS SOFTWARE. - */ - -#include "HvUtils.h" - -hv_uint32_t hv_string_to_hash(const char *str) { - // this hash is based MurmurHash2 - // http://en.wikipedia.org/wiki/MurmurHash - // https://sites.google.com/site/murmurhash/ - static const hv_uint32_t n = 0x5bd1e995; - static const hv_int32_t r = 24; - - if (str == NULL) return 0; - - hv_uint32_t len = (hv_uint32_t) hv_strlen(str); - hv_uint32_t x = len; // seed (0) ^ len - - while (len >= 4) { -#if HV_EMSCRIPTEN - hv_uint32_t k = str[0] | (str[1] << 8) | (str[2] << 16) | (str[3] << 24); -#else - hv_uint32_t k = *((hv_uint32_t *) str); -#endif - k *= n; - k ^= (k >> r); - k *= n; - x *= n; - x ^= k; - str += 4; len -= 4; - } - switch (len) { - case 3: x ^= (str[2] << 16); - case 2: x ^= (str[1] << 8); - case 1: x ^= str[0]; x *= n; - default: break; - } - x ^= (x >> 13); - x *= n; - x ^= (x >> 15); - return x; -} diff --git a/examples/examples-dsp/examples-pd/streams-pd-audiokit/HvUtils.h b/examples/examples-dsp/examples-pd/streams-pd-audiokit/HvUtils.h deleted file mode 100644 index 2186f547c0..0000000000 --- a/examples/examples-dsp/examples-pd/streams-pd-audiokit/HvUtils.h +++ /dev/null @@ -1,319 +0,0 @@ -/** - * Copyright (c) 2014-2018 Enzien Audio Ltd. - * - * Permission to use, copy, modify, and/or distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH - * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY - * AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, - * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM - * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR - * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR - * PERFORMANCE OF THIS SOFTWARE. - */ - -#ifndef _HEAVY_UTILS_H_ -#define _HEAVY_UTILS_H_ - -// platform definitions -#if _WIN32 || _WIN64 || _MSC_VER - #define HV_WIN 1 -#elif __APPLE__ - #define HV_APPLE 1 -#elif __ANDROID__ - #define HV_ANDROID 1 -#elif __unix__ || __unix - #define HV_UNIX 1 -#else - #warning Could not detect platform. Assuming Unix-like. -#endif - -#ifdef EMSCRIPTEN -#define HV_EMSCRIPTEN 1 -#endif - -// basic includes -#include -#include -#include - -// type definitions -#include -#include -#define hv_uint8_t uint8_t -#define hv_int16_t int16_t -#define hv_uint16_t uint16_t -#define hv_int32_t int32_t -#define hv_uint32_t uint32_t -#define hv_uint64_t uint64_t -#define hv_size_t size_t -#define hv_uintptr_t uintptr_t - -// SIMD-specific includes -#if !(HV_SIMD_NONE || HV_SIMD_NEON || HV_SIMD_SSE || HV_SIMD_AVX) - #define HV_SIMD_NEON __ARM_NEON__ - #define HV_SIMD_SSE (__SSE__ && __SSE2__ && __SSE3__ && __SSSE3__ && __SSE4_1__) - #define HV_SIMD_AVX (__AVX__ && HV_SIMD_SSE) -#endif -#ifndef HV_SIMD_FMA - #define HV_SIMD_FMA __FMA__ -#endif - -#if HV_SIMD_AVX || HV_SIMD_SSE - #include -#elif HV_SIMD_NEON - #include -#endif - -#if HV_SIMD_NEON // NEON - #define HV_N_SIMD 4 - #define hv_bufferf_t float32x4_t - #define hv_bufferi_t int32x4_t - #define hv_bInf_t float32x4_t - #define hv_bOutf_t float32x4_t* - #define hv_bIni_t int32x4_t - #define hv_bOuti_t int32x4_t* - #define VIf(_x) (_x) - #define VOf(_x) (&_x) - #define VIi(_x) (_x) - #define VOi(_x) (&_x) -#elif HV_SIMD_AVX // AVX - #define HV_N_SIMD 8 - #define hv_bufferf_t __m256 - #define hv_bufferi_t __m256i - #define hv_bInf_t __m256 - #define hv_bOutf_t __m256* - #define hv_bIni_t __m256i - #define hv_bOuti_t __m256i* - #define VIf(_x) (_x) - #define VOf(_x) (&_x) - #define VIi(_x) (_x) - #define VOi(_x) (&_x) -#elif HV_SIMD_SSE // SSE - #define HV_N_SIMD 4 - #define hv_bufferf_t __m128 - #define hv_bufferi_t __m128i - #define hv_bInf_t __m128 - #define hv_bOutf_t __m128* - #define hv_bIni_t __m128i - #define hv_bOuti_t __m128i* - #define VIf(_x) (_x) - #define VOf(_x) (&_x) - #define VIi(_x) (_x) - #define VOi(_x) (&_x) -#else // DEFAULT - #define HV_N_SIMD 1 - #undef HV_SIMD_NONE - #define HV_SIMD_NONE 1 - #define hv_bufferf_t float - #define hv_bufferi_t int - #define hv_bInf_t float - #define hv_bOutf_t float* - #define hv_bIni_t int - #define hv_bOuti_t int* - #define VIf(_x) (_x) - #define VOf(_x) (&_x) - #define VIi(_x) (_x) - #define VOi(_x) (&_x) -#endif - -#define HV_N_SIMD_MASK (HV_N_SIMD-1) - -// Strings -#include -#define hv_strlen(a) strlen(a) -#define hv_strcmp(a, b) strcmp(a, b) -#define hv_snprintf(a, b, c, ...) snprintf(a, b, c, __VA_ARGS__) -#if HV_WIN -#define hv_strncpy(_dst, _src, _len) strncpy_s(_dst, _len, _src, _TRUNCATE) -#else -#define hv_strncpy(_dst, _src, _len) strncpy(_dst, _src, _len) -#endif - -// Memory management -#define hv_memcpy(a, b, c) memcpy(a, b, c) -#define hv_memclear(a, b) memset(a, 0, b) -#if HV_WIN - #include - #define hv_alloca(_n) _alloca(_n) - #if HV_SIMD_AVX - #define hv_malloc(_n) _aligned_malloc(_n, 32) - #define hv_realloc(a, b) _aligned_realloc(a, b, 32) - #define hv_free(x) _aligned_free(x) - #elif HV_SIMD_SSE || HV_SIMD_NEON - #define hv_malloc(_n) _aligned_malloc(_n, 16) - #define hv_realloc(a, b) _aligned_realloc(a, b, 16) - #define hv_free(x) _aligned_free(x) - #else // HV_SIMD_NONE - #define hv_malloc(_n) malloc(_n) - #define hv_realloc(a, b) realloc(a, b) - #define hv_free(_n) free(_n) - #endif -#elif HV_APPLE - #define hv_alloca(_n) alloca(_n) - #define hv_realloc(a, b) realloc(a, b) - #if HV_SIMD_AVX - #include - #define hv_malloc(_n) _mm_malloc(_n, 32) - #define hv_free(x) _mm_free(x) - #elif HV_SIMD_SSE - #include - #define hv_malloc(_n) _mm_malloc(_n, 16) - #define hv_free(x) _mm_free(x) - #elif HV_SIMD_NEON - // malloc on ios always has 16-byte alignment - #define hv_malloc(_n) malloc(_n) - #define hv_free(x) free(x) - #else // HV_SIMD_NONE - #define hv_malloc(_n) malloc(_n) - #define hv_free(x) free(x) - #endif -#else - #include - #define hv_alloca(_n) alloca(_n) - #define hv_realloc(a, b) realloc(a, b) - #if HV_SIMD_AVX - #define hv_malloc(_n) aligned_alloc(32, _n) - #define hv_free(x) free(x) - #elif HV_SIMD_SSE - #define hv_malloc(_n) aligned_alloc(16, _n) - #define hv_free(x) free(x) - #elif HV_SIMD_NEON - #if HV_ANDROID - #define hv_malloc(_n) memalign(16, _n) - #define hv_free(x) free(x) - #else - #define hv_malloc(_n) aligned_alloc(16, _n) - #define hv_free(x) free(x) - #endif - #else // HV_SIMD_NONE - #define hv_malloc(_n) malloc(_n) - #define hv_free(_n) free(_n) - #endif -#endif - -// Assert -#include -#define hv_assert(e) assert(e) - -// Export and Inline -#if HV_WIN -#define HV_EXPORT __declspec(dllexport) -#ifndef __cplusplus // MSVC doesn't like redefining "inline" keyword -#define inline __inline -#endif -#define HV_FORCE_INLINE __forceinline -#else -#define HV_EXPORT -#define HV_FORCE_INLINE inline __attribute__((always_inline)) -#endif - -#ifdef __cplusplus -extern "C" { -#endif - // Returns a 32-bit hash of any string. Returns 0 if string is NULL. - hv_uint32_t hv_string_to_hash(const char *str); -#ifdef __cplusplus -} -#endif - -// Math -#include -static inline hv_size_t __hv_utils_max_ui(hv_size_t x, hv_size_t y) { return (x > y) ? x : y; } -static inline hv_size_t __hv_utils_min_ui(hv_size_t x, hv_size_t y) { return (x < y) ? x : y; } -static inline hv_int32_t __hv_utils_max_i(hv_int32_t x, hv_int32_t y) { return (x > y) ? x : y; } -static inline hv_int32_t __hv_utils_min_i(hv_int32_t x, hv_int32_t y) { return (x < y) ? x : y; } -#define hv_max_ui(a, b) __hv_utils_max_ui(a, b) -#define hv_min_ui(a, b) __hv_utils_min_ui(a, b) -#define hv_max_i(a, b) __hv_utils_max_i(a, b) -#define hv_min_i(a, b) __hv_utils_min_i(a, b) -#define hv_max_f(a, b) fmaxf(a, b) -#define hv_min_f(a, b) fminf(a, b) -#define hv_max_d(a, b) fmax(a, b) -#define hv_min_d(a, b) fmin(a, b) -#define hv_sin_f(a) sinf(a) -#define hv_sinh_f(a) sinhf(a) -#define hv_cos_f(a) cosf(a) -#define hv_cosh_f(a) coshf(a) -#define hv_tan_f(a) tanf(a) -#define hv_tanh_f(a) tanhf(a) -#define hv_asin_f(a) asinf(a) -#define hv_asinh_f(a) asinhf(a) -#define hv_acos_f(a) acosf(a) -#define hv_acosh_f(a) acoshf(a) -#define hv_atan_f(a) atanf(a) -#define hv_atanh_f(a) atanhf(a) -#define hv_atan2_f(a, b) atan2f(a, b) -#define hv_exp_f(a) expf(a) -#define hv_abs_f(a) fabsf(a) -#define hv_sqrt_f(a) sqrtf(a) -#define hv_log_f(a) logf(a) -#define hv_ceil_f(a) ceilf(a) -#define hv_floor_f(a) floorf(a) -#define hv_round_f(a) roundf(a) -#define hv_pow_f(a, b) powf(a, b) -#if HV_EMSCRIPTEN -#define hv_fma_f(a, b, c) ((a*b)+c) // emscripten does not support fmaf (yet?) -#else -#define hv_fma_f(a, b, c) fmaf(a, b, c) -#endif -#if HV_WIN - // finds ceil(log2(x)) - #include - static inline hv_uint32_t __hv_utils_min_max_log2(hv_uint32_t x) { - unsigned long z = 0; - _BitScanReverse(&z, x); - return (hv_uint32_t) (z+1); - } -#else - static inline hv_uint32_t __hv_utils_min_max_log2(hv_uint32_t x) { - return (hv_uint32_t) (32 - __builtin_clz(x-1)); - } -#endif -#define hv_min_max_log2(a) __hv_utils_min_max_log2(a) - -// Atomics -#if HV_WIN - #include - #define hv_atomic_bool volatile LONG - #define HV_SPINLOCK_ACQUIRE(_x) while (InterlockedCompareExchange(&_x, true, false)) { } - #define HV_SPINLOCK_TRY(_x) return !InterlockedCompareExchange(&_x, true, false) - #define HV_SPINLOCK_RELEASE(_x) (_x = false) -#elif HV_ANDROID - // Android support for atomics isn't that great, we'll do it manually - // https://gcc.gnu.org/onlinedocs/gcc-4.1.2/gcc/Atomic-Builtins.html - #define hv_atomic_bool hv_uint8_t - #define HV_SPINLOCK_ACQUIRE(_x) while (__sync_lock_test_and_set(&_x, 1)) - #define HV_SPINLOCK_TRY(_x) return !__sync_lock_test_and_set(&_x, 1) - #define HV_SPINLOCK_RELEASE(_x) __sync_lock_release(&_x) -#elif __cplusplus - #include - #define hv_atomic_bool std::atomic_flag - #define HV_SPINLOCK_ACQUIRE(_x) while (_x.test_and_set(std::memory_order_acquire)) - #define HV_SPINLOCK_TRY(_x) return !_x.test_and_set(std::memory_order_acquire) - #define HV_SPINLOCK_RELEASE(_x) _x.clear(std::memory_order_release) -#elif defined(__has_include) - #if __has_include() - #include - #define hv_atomic_bool atomic_flag - #define HV_SPINLOCK_ACQUIRE(_x) while (atomic_flag_test_and_set_explicit(&_x, memory_order_acquire)) - #define HV_SPINLOCK_TRY(_x) return !atomic_flag_test_and_set_explicit(&_x, memory_order_acquire) - #define HV_SPINLOCK_RELEASE(_x) atomic_flag_clear_explicit(memory_order_release) - #endif -#endif -#ifndef hv_atomic_bool - #define hv_atomic_bool volatile bool - #define HV_SPINLOCK_ACQUIRE(_x) \ - while (_x) {} \ - _x = true; - #define HV_SPINLOCK_TRY(_x) \ - if (!_x) { \ - _x = true; \ - return true; \ - } else return false; - #define HV_SPINLOCK_RELEASE(_x) (_x = false) -#endif - -#endif // _HEAVY_UTILS_H_ diff --git a/examples/examples-dsp/examples-pd/streams-pd-audiokit/streams-pd-audiokit.ino b/examples/examples-dsp/examples-pd/streams-pd-audiokit/streams-pd-audiokit.ino deleted file mode 100644 index 707da3dbd8..0000000000 --- a/examples/examples-dsp/examples-pd/streams-pd-audiokit/streams-pd-audiokit.ino +++ /dev/null @@ -1,29 +0,0 @@ -/** - * Test sketch for test.pd that was compiled with hvcc -n test test.pd - */ - -#include "AudioTools.h" -#include "AudioTools/AudioLibs/AudioBoardStream.h" // install https://github.com/pschatzmann/arduino-audio-driver -#include "Heavy_test.hpp" // import before PureDataStream! -#include "AudioTools/AudioLibs/PureDataStream.h" - -Heavy_test pd_test(44100); -PureDataStream pd(pd_test); -AudioBoardStream out(AudioKitEs8388V1); // or replace with other output -StreamCopy copier(out, pd); // copy kit to kit - -void setup() { - Serial.begin(115200); - AudioToolsLogger.begin(Serial, AudioToolsLogLevel::Warning); - - // setup pd - pd.begin(); - - // setup output - auto cfg = out.defaultConfig(TX_MODE); - cfg.sd_active = false; - cfg.copyFrom(pd.audioInfo()); - out.begin(cfg); -} - -void loop() { copier.copy(); } \ No newline at end of file diff --git a/examples/examples-dsp/examples-pd/streams-pd-audiokit/test.pd b/examples/examples-dsp/examples-pd/streams-pd-audiokit/test.pd deleted file mode 100644 index e27e764539..0000000000 --- a/examples/examples-dsp/examples-pd/streams-pd-audiokit/test.pd +++ /dev/null @@ -1,4 +0,0 @@ -#N canvas 349 94 450 300 12; -#X obj 134 65 osc~ 220; -#X obj 132 178 dac~; -#X connect 0 0 1 0; diff --git a/examples/examples-dsp/examples-faust/README.md b/examples/examples-faust/README.md similarity index 100% rename from examples/examples-dsp/examples-faust/README.md rename to examples/examples-faust/README.md diff --git a/examples/examples-dsp/examples-faust/streams-faust_flute-i2s/fluteMIDI.dsp b/examples/examples-faust/streams-faust_flute-i2s/fluteMIDI.dsp similarity index 100% rename from examples/examples-dsp/examples-faust/streams-faust_flute-i2s/fluteMIDI.dsp rename to examples/examples-faust/streams-faust_flute-i2s/fluteMIDI.dsp diff --git a/examples/examples-dsp/examples-faust/streams-faust_flute-i2s/fluteMIDI.h b/examples/examples-faust/streams-faust_flute-i2s/fluteMIDI.h similarity index 100% rename from examples/examples-dsp/examples-faust/streams-faust_flute-i2s/fluteMIDI.h rename to examples/examples-faust/streams-faust_flute-i2s/fluteMIDI.h diff --git a/examples/examples-dsp/examples-faust/streams-faust_flute-i2s/streams-faust_flute-i2s.ino b/examples/examples-faust/streams-faust_flute-i2s/streams-faust_flute-i2s.ino similarity index 81% rename from examples/examples-dsp/examples-faust/streams-faust_flute-i2s/streams-faust_flute-i2s.ino rename to examples/examples-faust/streams-faust_flute-i2s/streams-faust_flute-i2s.ino index 78cbbbe44a..6513651e24 100644 --- a/examples/examples-dsp/examples-faust/streams-faust_flute-i2s/streams-faust_flute-i2s.ino +++ b/examples/examples-faust/streams-faust_flute-i2s/streams-faust_flute-i2s.ino @@ -13,19 +13,19 @@ #define USE_MEMORY_MANAGER #include "AudioTools.h" -#include "AudioTools/AudioLibs/AudioBoardStream.h" -#include "AudioTools/AudioLibs/AudioFaust.h" +#include "AudioLibs/AudioKit.h" +#include "AudioLibs/AudioFaust.h" #include "fluteMIDI.h" FaustStream faust; -AudioBoardStream out(AudioKitEs8388V1); +AudioKitStream out; StreamCopy copier(out, faust); // copy mic to tfl // Arduino Setup void setup(void) { // Open Serial Serial.begin(115200); - AudioToolsLogger.begin(Serial, AudioToolsLogLevel::Warning); + AudioLogger::instance().begin(Serial, AudioLogger::Warning); // Setup Faust auto cfg = faust.defaultConfig(); diff --git a/examples/examples-dsp/examples-faust/streams-faust_noise-i2s/Noise.dsp b/examples/examples-faust/streams-faust_noise-i2s/Noise.dsp similarity index 100% rename from examples/examples-dsp/examples-faust/streams-faust_noise-i2s/Noise.dsp rename to examples/examples-faust/streams-faust_noise-i2s/Noise.dsp diff --git a/examples/examples-dsp/examples-faust/streams-faust_noise-i2s/Noise.h b/examples/examples-faust/streams-faust_noise-i2s/Noise.h similarity index 97% rename from examples/examples-dsp/examples-faust/streams-faust_noise-i2s/Noise.h rename to examples/examples-faust/streams-faust_noise-i2s/Noise.h index 7ef9ccd589..6448cd4aad 100644 --- a/examples/examples-dsp/examples-faust/streams-faust_noise-i2s/Noise.h +++ b/examples/examples-faust/streams-faust_noise-i2s/Noise.h @@ -18,7 +18,7 @@ Compilation options: -lang cpp -es 1 -mcd 16 -single -ftz 0 #include #include #include -#include "AudioTools/AudioLibs/AudioFaustDSP.h" // used to define dsp class +#include "AudioLibs/AudioFaustDSP.h" // used to define dsp class #ifndef FAUSTCLASS #define FAUSTCLASS mydsp diff --git a/examples/examples-dsp/examples-faust/streams-faust_noise-i2s/streams-faust_noise-i2s.ino b/examples/examples-faust/streams-faust_noise-i2s/streams-faust_noise-i2s.ino similarity index 80% rename from examples/examples-dsp/examples-faust/streams-faust_noise-i2s/streams-faust_noise-i2s.ino rename to examples/examples-faust/streams-faust_noise-i2s/streams-faust_noise-i2s.ino index 581bf35e9f..c0d71a003b 100644 --- a/examples/examples-dsp/examples-faust/streams-faust_noise-i2s/streams-faust_noise-i2s.ino +++ b/examples/examples-faust/streams-faust_noise-i2s/streams-faust_noise-i2s.ino @@ -10,19 +10,19 @@ */ #include "AudioTools.h" -#include "AudioTools/AudioLibs/AudioBoardStream.h" -#include "AudioTools/AudioLibs/AudioFaust.h" +#include "AudioLibs/AudioKit.h" +#include "AudioLibs/AudioFaust.h" #include "Noise.h" FaustStream faust; -AudioBoardStream out(AudioKitEs8388V1); +AudioKitStream out; StreamCopy copier(out, faust); // copy mic to tfl // Arduino Setup void setup(void) { // Open Serial Serial.begin(115200); - AudioToolsLogger.begin(Serial, AudioToolsLogLevel::Warning); + AudioLogger::instance().begin(Serial, AudioLogger::Warning); // Setup Faust auto cfg = faust.defaultConfig(); diff --git a/examples/examples-dsp/examples-faust/streams-generator-faust-i2s/streams-generator-faust-i2s.ino b/examples/examples-faust/streams-generator-faust-i2s/streams-generator-faust-i2s.ino similarity index 85% rename from examples/examples-dsp/examples-faust/streams-generator-faust-i2s/streams-generator-faust-i2s.ino rename to examples/examples-faust/streams-generator-faust-i2s/streams-generator-faust-i2s.ino index 92cae8cd9c..37569e85b4 100644 --- a/examples/examples-dsp/examples-faust/streams-generator-faust-i2s/streams-generator-faust-i2s.ino +++ b/examples/examples-faust/streams-generator-faust-i2s/streams-generator-faust-i2s.ino @@ -10,14 +10,14 @@ */ #include "AudioTools.h" -#include "AudioTools/AudioLibs/AudioBoardStream.h" -#include "AudioTools/AudioLibs/AudioFaust.h" +#include "AudioLibs/AudioKit.h" +#include "AudioLibs/AudioFaust.h" #include "volume.h" SineWaveGenerator sineWave(32000); // subclass of SoundGenerator with max amplitude of 32000 GeneratedSoundStream sound(sineWave); // Stream generated from sine wave mydsp dsp; -AudioBoardStream out(AudioKitEs8388V1); +AudioKitStream out; FaustStream faust(out); StreamCopy copier(faust, sound); // copy mic to tfl @@ -25,7 +25,7 @@ StreamCopy copier(faust, sound); // copy mic to tfl void setup(void) { // Open Serial Serial.begin(115200); - AudioToolsLogger.begin(Serial, AudioToolsLogLevel::Warning); + AudioLogger::instance().begin(Serial, AudioLogger::Warning); // Setup Faust volume control auto cfg = faust.defaultConfig(); diff --git a/examples/examples-dsp/examples-faust/streams-generator-faust-i2s/volume.dsp b/examples/examples-faust/streams-generator-faust-i2s/volume.dsp similarity index 100% rename from examples/examples-dsp/examples-faust/streams-generator-faust-i2s/volume.dsp rename to examples/examples-faust/streams-generator-faust-i2s/volume.dsp diff --git a/examples/examples-dsp/examples-faust/streams-generator-faust-i2s/volume.h b/examples/examples-faust/streams-generator-faust-i2s/volume.h similarity index 100% rename from examples/examples-dsp/examples-faust/streams-generator-faust-i2s/volume.h rename to examples/examples-faust/streams-generator-faust-i2s/volume.h diff --git a/examples/examples-dsp/examples-faust/streams-i2s-faust_guitarix-i2s/guitarix.dsp b/examples/examples-faust/streams-i2s-faust_guitarix-i2s/guitarix.dsp similarity index 100% rename from examples/examples-dsp/examples-faust/streams-i2s-faust_guitarix-i2s/guitarix.dsp rename to examples/examples-faust/streams-i2s-faust_guitarix-i2s/guitarix.dsp diff --git a/examples/examples-dsp/examples-faust/streams-i2s-faust_guitarix-i2s/guitarix.h b/examples/examples-faust/streams-i2s-faust_guitarix-i2s/guitarix.h similarity index 99% rename from examples/examples-dsp/examples-faust/streams-i2s-faust_guitarix-i2s/guitarix.h rename to examples/examples-faust/streams-i2s-faust_guitarix-i2s/guitarix.h index 58f65e9143..71ace1d022 100644 --- a/examples/examples-dsp/examples-faust/streams-i2s-faust_guitarix-i2s/guitarix.h +++ b/examples/examples-faust/streams-i2s-faust_guitarix-i2s/guitarix.h @@ -35,7 +35,7 @@ Compilation options: -lang cpp -es 1 -mcd 16 -single -ftz 0 #define RESTRICT __restrict__ #endif -static const float fmydspSIG0Wave0[2001] = {249.987076f,249.986847f,249.986633f,249.986404f,249.986176f,249.985947f,249.985703f,249.985458f,249.985214f,249.98497f,249.984711f,249.984451f,249.984192f,249.983932f,249.983658f,249.983383f,249.983109f,249.982819f,249.982529f,249.982239f,249.981934f,249.981628f,249.981323f,249.981003f,249.980682f,249.980362f,249.980026f,249.979691f,249.979355f,249.979004f,249.978653f,249.978287f,249.977921f,249.977554f,249.977173f,249.976791f,249.976395f,249.975998f,249.975601f,249.975189f,249.974762f,249.97435f,249.973907f,249.97348f,249.973022f,249.972565f,249.972107f,249.971634f,249.971161f,249.970673f,249.970184f,249.969681f,249.969177f,249.968658f,249.968124f,249.96759f,249.967041f,249.966492f,249.965927f,249.965347f,249.964767f,249.964172f,249.963577f,249.962967f,249.962341f,249.961716f,249.96106f,249.960403f,249.959747f,249.959061f,249.958374f,249.957687f,249.95697f,249.956253f,249.955505f,249.954758f,249.95401f,249.953232f,249.952438f,249.951645f,249.950836f,249.950012f,249.949173f,249.948318f,249.947449f,249.946564f,249.945663f,249.944763f,249.943832f,249.942886f,249.941925f,249.940964f,249.939972f,249.938965f,249.937943f,249.93689f,249.935837f,249.934753f,249.93367f,249.932556f,249.931427f,249.930283f,249.929108f,249.927917f,249.926712f,249.925491f,249.92424f,249.922974f,249.921677f,249.920364f,249.919037f,249.917679f,249.916306f,249.914902f,249.913483f,249.912033f,249.910553f,249.909058f,249.907547f,249.905991f,249.904419f,249.902832f,249.901199f,249.899551f,249.897873f,249.896164f,249.89444f,249.89267f,249.890884f,249.889053f,249.887207f,249.88533f,249.883408f,249.88147f,249.879486f,249.877487f,249.875443f,249.873367f,249.871262f,249.86911f,249.866928f,249.864716f,249.862457f,249.860168f,249.857849f,249.855484f,249.853073f,249.850632f,249.848145f,249.845627f,249.843048f,249.840454f,249.837799f,249.835098f,249.832367f,249.829575f,249.826752f,249.823883f,249.820953f,249.817993f,249.814972f,249.811905f,249.808777f,249.805618f,249.802399f,249.799118f,249.795792f,249.792419f,249.788986f,249.785492f,249.781952f,249.778336f,249.774673f,249.77095f,249.767166f,249.763321f,249.759415f,249.755447f,249.751419f,249.747314f,249.743149f,249.738922f,249.734619f,249.730255f,249.725815f,249.721298f,249.716721f,249.712051f,249.707321f,249.702515f,249.697632f,249.692673f,249.687622f,249.682495f,249.677292f,249.671997f,249.666626f,249.661163f,249.655624f,249.649994f,249.644257f,249.638443f,249.632538f,249.626541f,249.620438f,249.614243f,249.607956f,249.601562f,249.595062f,249.58847f,249.581772f,249.574966f,249.568054f,249.561035f,249.553909f,249.546661f,249.539307f,249.53183f,249.524246f,249.516541f,249.508698f,249.500748f,249.492676f,249.484482f,249.476151f,249.467697f,249.459106f,249.450378f,249.441528f,249.432526f,249.423401f,249.414124f,249.404709f,249.395142f,249.385437f,249.375565f,249.365555f,249.355392f,249.345078f,249.334595f,249.323959f,249.313171f,249.3022f,249.291077f,249.27977f,249.268311f,249.256668f,249.244843f,249.232849f,249.220673f,249.208298f,249.195755f,249.183014f,249.17009f,249.156967f,249.143661f,249.130142f,249.116425f,249.102509f,249.088379f,249.074051f,249.059494f,249.044739f,249.029755f,249.014557f,248.99913f,248.98349f,248.967606f,248.951508f,248.935165f,248.918579f,248.901749f,248.884689f,248.867371f,248.849808f,248.832001f,248.813919f,248.795593f,248.776993f,248.758133f,248.739014f,248.719604f,248.699936f,248.679977f,248.659744f,248.639221f,248.618408f,248.597305f,248.575912f,248.554214f,248.532211f,248.509903f,248.487289f,248.464355f,248.441116f,248.417557f,248.393661f,248.369446f,248.344894f,248.320023f,248.2948f,248.269226f,248.243317f,248.217072f,248.19046f,248.163483f,248.136154f,248.108459f,248.080399f,248.051956f,248.023148f,247.993958f,247.964386f,247.934418f,247.904068f,247.873306f,247.842163f,247.810608f,247.778641f,247.746262f,247.71347f,247.680267f,247.646622f,247.612564f,247.578064f,247.543137f,247.507767f,247.471954f,247.435699f,247.398987f,247.361816f,247.324173f,247.286087f,247.247513f,247.208481f,247.168976f,247.128983f,247.088501f,247.047531f,247.006073f,246.964111f,246.921661f,246.878693f,246.83522f,246.791229f,246.746719f,246.701706f,246.656143f,246.610062f,246.563446f,246.516296f,246.468613f,246.420364f,246.371582f,246.322235f,246.272324f,246.221863f,246.170837f,246.119247f,246.067078f,246.014328f,245.960999f,245.907074f,245.85257f,245.79747f,245.741776f,245.685486f,245.628586f,245.571091f,245.51297f,245.454239f,245.394882f,245.334915f,245.274307f,245.213089f,245.151215f,245.088715f,245.025574f,244.961792f,244.897369f,244.832291f,244.766556f,244.70015f,244.633102f,244.565384f,244.496994f,244.427948f,244.358215f,244.287811f,244.216721f,244.144958f,244.07251f,243.999359f,243.925522f,243.850998f,243.775772f,243.699844f,243.623199f,243.545868f,243.467819f,243.389053f,243.309586f,243.229401f,243.148483f,243.066849f,242.984497f,242.901413f,242.817596f,242.733047f,242.647781f,242.561752f,242.475006f,242.387512f,242.299271f,242.210281f,242.12056f,242.030075f,241.938843f,241.846863f,241.75412f,241.660629f,241.566376f,241.471359f,241.37558f,241.279037f,241.181732f,241.083664f,240.984818f,240.885208f,240.784821f,240.68367f,240.581741f,240.479034f,240.375549f,240.271286f,240.166245f,240.060425f,239.953827f,239.846451f,239.738281f,239.629333f,239.519592f,239.409073f,239.29776f,239.185654f,239.072769f,238.959091f,238.84462f,238.72937f,238.613327f,238.496475f,238.378845f,238.260422f,238.141205f,238.021194f,237.900391f,237.778778f,237.656387f,237.533203f,237.40921f,237.284439f,237.158859f,237.032486f,236.905334f,236.777374f,236.648605f,236.519058f,236.388718f,236.257568f,236.125641f,235.992905f,235.859375f,235.725067f,235.589951f,235.454041f,235.317337f,235.179855f,235.041565f,234.902481f,234.762619f,234.621964f,234.480515f,234.338272f,234.195251f,234.051422f,233.90683f,233.761429f,233.61525f,233.468292f,233.320541f,233.172012f,233.022705f,232.872604f,232.721725f,232.570068f,232.417618f,232.264404f,232.110413f,231.955643f,231.800095f,231.643768f,231.486679f,231.328812f,231.170166f,231.010757f,230.850571f,230.689621f,230.527908f,230.365433f,230.202194f,230.038177f,229.873413f,229.707886f,229.541595f,229.374542f,229.206741f,229.038177f,228.868866f,228.698807f,228.527985f,228.35643f,228.184113f,228.011047f,227.837234f,227.662689f,227.487396f,227.311356f,227.134583f,226.957077f,226.778824f,226.599838f,226.42012f,226.23967f,226.058502f,225.876587f,225.693954f,225.51059f,225.326508f,225.141708f,224.956177f,224.769928f,224.582962f,224.395279f,224.206894f,224.017776f,223.827972f,223.637436f,223.446198f,223.254272f,223.061615f,222.868271f,222.674225f,222.479477f,222.284042f,222.087906f,221.891068f,221.693542f,221.495331f,221.296432f,221.096848f,220.896576f,220.695618f,220.493973f,220.291656f,220.088654f,219.884979f,219.680634f,219.475616f,219.269928f,219.063568f,218.856537f,218.648849f,218.440491f,218.231476f,218.021805f,217.811462f,217.600479f,217.388824f,217.176529f,216.963577f,216.749969f,216.535721f,216.320831f,216.105301f,215.889114f,215.672302f,215.454834f,215.23674f,215.018021f,214.798645f,214.578659f,214.358032f,214.13678f,213.914902f,213.692398f,213.469284f,213.245529f,213.021164f,212.796188f,212.570602f,212.344391f,212.117569f,211.890137f,211.662094f,211.433456f,211.204208f,210.97435f,210.743912f,210.512848f,210.281204f,210.048965f,209.816132f,209.582687f,209.348679f,209.114059f,208.878876f,208.643097f,208.406723f,208.169785f,207.932266f,207.694153f,207.455475f,207.216232f,206.976395f,206.736008f,206.495041f,206.25351f,206.011414f,205.768738f,205.525513f,205.281738f,205.037384f,204.79248f,204.547028f,204.301025f,204.054459f,203.807343f,203.559677f,203.311478f,203.062714f,202.813416f,202.563583f,202.313202f,202.062286f,201.810837f,201.558838f,201.30632f,201.053268f,200.799683f,200.545578f,200.290939f,200.035767f,199.780075f,199.52388f,199.267151f,199.009903f,198.752136f,198.493851f,198.235062f,197.975769f,197.715958f,197.455627f,197.194809f,196.933472f,196.671631f,196.409302f,196.146454f,195.883118f,195.619293f,195.354965f,195.090134f,194.824829f,194.559021f,194.292725f,194.025955f,193.758682f,193.490936f,193.222702f,192.953995f,192.684814f,192.415146f,192.145004f,191.87439f,191.603302f,191.331741f,191.059723f,190.787231f,190.514267f,190.240845f,189.966965f,189.692627f,189.417816f,189.142563f,188.866837f,188.590668f,188.314056f,188.036987f,187.75946f,187.481491f,187.203079f,186.924225f,186.644928f,186.365204f,186.085022f,185.804413f,185.523361f,185.241882f,184.959976f,184.677643f,184.394867f,184.111679f,183.828064f,183.544022f,183.259567f,182.974701f,182.689407f,182.403702f,182.117569f,181.831039f,181.544113f,181.25676f,180.969009f,180.680862f,180.392303f,180.103348f,179.814011f,179.524261f,179.234131f,178.943604f,178.652695f,178.361389f,178.069702f,177.777649f,177.485199f,177.192383f,176.8992f,176.605637f,176.311691f,176.017395f,175.722733f,175.427704f,175.132324f,174.836594f,174.540497f,174.244049f,173.947266f,173.650131f,173.352646f,173.054825f,172.756683f,172.458191f,172.159378f,171.860245f,171.560776f,171.261002f,170.960907f,170.660492f,170.359772f,170.058746f,169.757431f,169.455811f,169.1539f,168.8517f,168.549225f,168.24646f,167.94342f,167.640121f,167.336548f,167.032715f,166.728622f,166.424286f,166.11969f,165.814865f,165.509811f,165.204529f,164.899017f,164.593292f,164.287369f,163.981232f,163.674896f,163.368378f,163.061676f,162.754791f,162.447739f,162.140533f,161.833176f,161.525681f,161.218033f,160.910278f,160.602386f,160.294403f,159.986313f,159.678131f,159.369873f,159.061554f,158.753174f,158.444733f,158.136276f,157.827789f,157.519287f,157.210785f,156.902283f,156.593826f,156.2854f,155.977036f,155.668732f,155.360504f,155.052383f,154.74437f,154.436478f,154.128738f,153.821167f,153.513763f,153.206558f,152.899551f,152.592789f,152.286285f,151.980042f,151.674088f,151.368439f,151.063126f,150.758163f,150.453568f,150.149384f,149.845596f,149.542267f,149.23938f,148.936996f,148.635117f,148.333786f,148.033005f,147.732803f,147.433228f,147.134277f,146.835999f,146.538422f,146.241547f,145.945435f,145.650085f,145.355545f,145.061829f,144.768967f,144.477005f,144.185959f,143.895859f,143.606735f,143.318604f,143.031525f,142.745499f,142.460556f,142.176743f,141.894089f,141.61261f,141.332336f,141.053299f,140.775528f,140.499039f,140.223892f,139.950073f,139.677643f,139.406601f,139.136993f,138.868835f,138.602158f,138.33699f,138.073334f,137.811234f,137.55069f,137.291748f,137.034424f,136.778717f,136.524673f,136.272293f,136.021606f,135.772614f,135.525345f,135.279816f,135.036026f,134.794006f,134.553741f,134.315277f,134.078598f,133.843735f,133.610672f,133.379425f,133.150009f,132.922424f,132.696671f,132.472748f,132.250671f,132.030441f,131.812057f,131.595505f,131.380798f,131.167938f,130.956909f,130.747726f,130.540359f,130.334824f,130.131104f,129.929214f,129.729111f,129.530823f,129.33432f,129.139603f,128.946671f,128.755493f,128.566071f,128.378387f,128.192429f,128.008209f,127.825691f,127.644867f,127.465729f,127.288269f,127.112457f,126.938293f,126.765755f,126.594841f,126.425522f,126.25779f,126.091629f,125.927032f,125.763977f,125.602448f,125.442429f,125.283905f,125.126862f,124.971291f,124.817169f,124.664482f,124.513214f,124.36335f,124.214874f,124.067772f,123.922028f,123.777626f,123.634544f,123.492783f,123.35231f,123.213127f,123.075203f,122.938538f,122.803101f,122.668884f,122.535881f,122.404068f,122.27343f,122.143951f,122.015633f,121.888435f,121.762367f,121.637405f,121.513535f,121.390747f,121.26902f,121.148354f,121.028725f,120.910118f,120.792526f,120.675941f,120.560341f,120.445717f,120.332054f,120.219353f,120.107582f,119.996742f,119.886818f,119.777802f,119.669678f,119.562439f,119.45607f,119.350563f,119.245903f,119.142082f,119.039093f,118.93692f,118.835556f,118.734993f,118.635216f,118.536217f,118.437988f,118.340515f,118.243797f,118.14782f,118.052574f,117.958046f,117.864235f,117.771133f,117.678719f,117.586998f,117.495956f,117.405594f,117.315887f,117.226837f,117.138435f,117.050667f,116.963539f,116.877037f,116.791145f,116.705864f,116.621193f,116.537109f,116.453621f,116.370712f,116.288376f,116.206612f,116.125404f,116.044762f,115.964661f,115.885101f,115.806076f,115.727585f,115.64962f,115.572174f,115.495239f,115.418808f,115.34288f,115.267441f,115.192497f,115.118042f,115.04406f,114.970551f,114.897514f,114.824936f,114.752823f,114.681152f,114.60994f,114.539169f,114.468834f,114.398933f,114.32946f,114.260414f,114.191788f,114.123573f,114.055779f,113.98838f,113.921394f,113.854805f,113.788605f,113.722794f,113.657372f,113.592339f,113.527672f,113.463387f,113.399467f,113.335922f,113.272736f,113.209908f,113.147438f,113.085312f,113.023544f,112.96212f,112.901031f,112.840286f,112.779877f,112.719795f,112.660042f,112.600616f,112.541519f,112.482735f,112.424263f,112.366104f,112.308258f,112.250717f,112.193481f,112.136551f,112.07991f,112.023575f,111.967522f,111.911766f,111.856293f,111.801109f,111.746201f,111.691574f,111.63723f,111.583153f,111.52935f,111.475815f,111.422546f,111.369545f,111.316803f,111.26432f,111.212097f,111.160133f,111.108414f,111.056946f,111.00573f,110.954758f,110.90403f,110.853546f,110.803299f,110.753296f,110.703522f,110.653984f,110.604675f,110.555595f,110.506744f,110.458122f,110.409721f,110.361542f,110.313583f,110.265839f,110.218315f,110.171005f,110.123909f,110.077026f,110.03035f,109.983879f,109.937614f,109.891556f,109.845703f,109.800041f,109.754585f,109.709328f,109.664268f,109.6194f,109.574722f,109.530243f,109.485947f,109.441841f,109.397919f,109.354187f,109.310638f,109.267273f,109.224091f,109.181084f,109.138252f,109.095604f,109.053131f,109.010826f,108.968704f,108.926743f,108.884956f,108.843346f,108.801895f,108.760612f,108.719498f,108.678543f,108.637756f,108.597122f,108.556656f,108.51635f,108.476196f,108.436203f,108.39637f,108.356682f,108.317154f,108.277779f,108.238548f,108.199478f,108.160553f,108.121773f,108.083138f,108.044655f,108.006317f,107.968117f,107.930061f,107.892151f,107.854385f,107.81675f,107.779259f,107.741905f,107.704689f,107.66761f,107.630669f,107.593857f,107.557175f,107.52063f,107.484215f,107.447937f,107.411781f,107.375755f,107.339859f,107.304092f,107.268448f,107.232933f,107.19754f,107.16227f,107.127129f,107.092102f,107.057198f,107.022423f,106.987755f,106.953217f,106.918793f,106.884483f,106.850296f,106.816223f,106.782265f,106.748421f,106.714684f,106.681068f,106.647568f,106.614174f,106.580887f,106.547714f,106.514656f,106.481705f,106.448853f,106.416115f,106.383484f,106.35096f,106.318542f,106.286232f,106.254021f,106.221916f,106.189911f,106.158005f,106.126205f,106.094513f,106.062912f,106.031418f,106.000015f,105.968719f,105.937515f,105.90641f,105.875404f,105.84449f,105.813675f,105.782959f,105.752335f,105.721802f,105.691368f,105.661018f,105.630768f,105.600609f,105.570541f,105.540565f,105.510681f,105.480881f,105.451172f,105.421555f,105.392021f,105.362579f,105.333229f,105.303955f,105.274773f,105.245682f,105.216667f,105.187744f,105.158905f,105.130142f,105.101471f,105.072876f,105.044373f,105.015945f,104.987602f,104.959335f,104.931152f,104.903053f,104.875031f,104.847084f,104.819221f,104.791435f,104.763733f,104.736099f,104.708549f,104.681076f,104.653679f,104.626358f,104.599106f,104.571938f,104.544838f,104.517822f,104.490868f,104.463997f,104.437195f,104.410469f,104.383812f,104.357231f,104.330719f,104.304283f,104.277916f,104.251617f,104.225388f,104.199234f,104.173141f,104.147125f,104.121178f,104.095299f,104.069481f,104.043739f,104.018059f,103.992455f,103.966911f,103.941437f,103.916023f,103.890678f,103.865402f,103.840187f,103.815041f,103.789955f,103.764938f,103.739983f,103.715088f,103.690262f,103.665497f,103.640793f,103.61615f,103.591568f,103.567055f,103.542603f,103.518204f,103.493874f,103.469597f,103.445389f,103.421234f,103.397141f,103.373108f,103.349136f,103.325226f,103.301369f,103.277573f,103.25383f,103.230148f,103.20652f,103.182953f,103.159447f,103.135994f,103.112595f,103.089256f,103.065971f,103.04274f,103.019569f,102.996452f,102.973389f,102.950378f,102.927422f,102.904526f,102.881676f,102.858887f,102.836143f,102.813461f,102.790825f,102.76825f,102.74572f,102.723244f,102.700821f,102.678452f,102.656128f,102.633858f,102.611641f,102.589478f,102.56736f,102.545296f,102.523277f,102.501312f,102.479401f,102.457535f,102.435715f,102.413948f,102.392227f,102.37056f,102.348938f,102.327362f,102.30584f,102.284363f,102.262932f,102.241547f,102.220215f,102.198929f,102.177689f,102.156494f,102.135345f,102.114243f,102.093185f,102.072174f,102.051208f,102.030296f,102.009422f,101.988594f,101.967804f,101.947067f,101.926376f,101.905724f,101.885117f,101.864555f,101.84404f,101.823563f,101.803131f,101.782745f,101.762398f,101.742096f,101.72184f,101.701622f,101.68145f,101.661316f,101.641228f,101.621178f,101.601173f,101.581207f,101.561287f,101.541405f,101.521561f,101.501762f,101.482002f,101.462288f,101.442604f,101.422966f,101.403374f,101.383812f,101.364296f,101.344818f,101.325378f,101.305977f,101.286613f,101.267288f,101.248009f,101.228767f,101.209557f,101.190392f,101.171257f,101.152168f,101.133118f,101.114098f,101.095123f,101.07618f,101.057281f,101.038414f,101.019585f,101.000793f,100.982033f,100.963318f,100.944633f,100.925987f,100.907379f,100.888809f,100.87027f,100.851768f,100.833305f,100.814873f,100.796478f,100.778114f,100.759796f,100.741501f,100.723251f,100.705032f,100.686844f,100.668694f,100.650574f,100.632492f,100.614449f,100.596436f,100.578453f,100.560509f,100.542595f,100.524719f,100.506866f,100.489059f,100.471275f,100.453529f,100.435822f,100.418137f,100.40049f,100.382874f,100.365295f,100.34774f,100.330223f,100.312737f,100.29528f,100.277863f,100.260468f,100.243111f,100.225784f,100.208488f,100.191223f,100.173988f,100.156784f,100.139618f,100.122475f,100.10537f,100.088287f,100.071236f,100.054222f,100.037231f,100.020271f,100.003349f,99.9864502f,99.9695816f,99.9527435f,99.935936f,99.9191589f,99.9024124f,99.8856888f,99.8689957f,99.8523407f,99.8357086f,99.8191071f,99.8025284f,99.7859879f,99.7694702f,99.7529831f,99.7365189f,99.7200928f,99.7036896f,99.6873169f,99.6709671f,99.6546478f,99.6383591f,99.6220932f,99.6058655f,99.589653f,99.5734787f,99.5573273f,99.5411987f,99.5251007f,99.5090332f,99.4929886f,99.4769745f,99.4609833f,99.4450226f,99.4290848f,99.4131775f,99.3973007f,99.3814392f,99.3656158f,99.3498077f,99.3340378f,99.3182831f,99.3025589f,99.2868652f,99.2711868f,99.2555466f,99.2399216f,99.2243271f,99.2087631f,99.1932144f,99.1776962f,99.1622009f,99.1467361f,99.1312943f,99.1158752f,99.1004868f,99.0851135f,99.0697708f,99.0544586f,99.0391617f,99.0238953f,99.0086517f,98.9934311f,98.9782333f,98.9630661f,98.9479218f,98.9327927f,98.9176941f,98.902626f,98.8875732f,98.8725433f,98.8575439f,98.8425674f,98.8276062f,98.8126755f,98.7977676f,98.7828827f,98.7680206f,98.7531891f,98.7383728f,98.7235794f,98.7088089f,98.6940613f,98.6793442f,98.6646423f,98.6499634f,98.6353073f,98.6206818f,98.6060715f,98.5914841f,98.5769196f,98.5623779f,98.5478592f,98.5333633f,98.5188828f,98.5044327f,98.4900055f,98.4755936f,98.4612045f,98.4468384f,98.4324951f,98.4181747f,98.4038773f,98.389595f,98.3753433f,98.3611069f,98.3468933f,98.3327026f,98.3185272f,98.3043823f,98.2902527f,98.2761459f,98.2620544f,98.2479935f,98.2339478f,98.2199249f,98.2059174f,98.1919327f,98.1779709f,98.164032f,98.150116f,98.1362152f,98.1223297f,98.1084747f,98.094635f,98.0808182f,98.0670166f,98.0532379f,98.0394821f,98.0257416f,98.0120239f,97.9983292f,97.9846497f,97.9709854f,97.9573517f,97.9437332f,97.93013f,97.9165497f,97.9029922f,97.8894501f,97.8759308f,97.8624268f,97.8489456f,97.8354797f,97.8220367f,97.808609f,97.7952042f,97.7818146f,97.7684479f,97.7550964f,97.7417679f,97.7284546f,97.7151642f,97.701889f,97.6886368f,97.6753998f,97.662178f,97.6489792f,97.6357956f,97.6226349f,97.6094894f,97.5963669f,97.583252f,97.5701675f,97.5570908f,97.5440445f,97.5310059f,97.5179901f,97.5049896f,97.4920044f,97.4790421f,97.466095f,97.4531708f,97.4402618f,97.4273682f,97.4144897f,97.4016342f,97.3887939f,97.3759766f,97.3631668f,97.3503799f,97.3376083f,97.3248596f,97.3121262f,97.299408f,97.286705f,97.2740173f,97.2613525f,97.248703f,97.2360687f,97.2234573f,97.2108536f,97.1982727f,97.1857071f,97.1731567f,97.1606293f,97.1481094f,97.1356125f,97.1231308f,97.1106644f,97.0982208f,97.0857849f,97.0733719f,97.0609665f,97.048584f,97.0362167f,97.0238647f,97.0115356f,96.9992142f,96.9869156f,96.9746246f,96.9623566f,96.9501038f,96.9378662f,96.9256439f,96.9134369f,96.9012451f,96.8890686f,96.876915f,96.864769f,96.8526382f,96.8405304f,96.8284302f,96.8163528f,96.8042908f,96.7922363f,96.7802048f,96.7681808f,96.7561798f,96.744194f,96.7322235f,96.7202606f,96.7083206f,96.6963959f,96.6844788f,96.6725845f,96.6607056f,96.6488342f,96.6369858f,96.625145f,96.613327f,96.6015167f,96.5897217f,96.5779495f,96.566185f,96.5544357f,96.5427017f,96.530983f,96.5192795f,96.5075912f,96.4959183f,96.4842529f,96.4726105f,96.4609756f,96.4493637f,96.4377594f,96.4261703f,96.4145966f,96.403038f,96.3914948f,96.3799591f,96.3684464f,96.3569412f,96.3454514f,96.3339767f,96.3225174f,96.3110733f,96.2996368f,96.2882156f,96.2768097f,96.265419f,96.2540436f,96.2426834f,96.2313309f,96.2199936f,96.2086716f,96.1973648f,96.1860733f,96.1747894f,96.1635208f,96.1522675f,96.1410294f,96.1297989f,96.1185837f,96.1073837f,96.096199f,96.085022f,96.0738602f,96.0627136f,96.0515823f,96.0404587f,96.0293579f,96.0182571f,96.0071793f,95.996109f,95.985054f,95.9740143f,95.9629822f,95.951973f,95.9409637f,95.9299774f,95.9189987f,95.9080353f,95.8970871f,95.8861465f,95.8752213f,95.8643036f,95.8534088f,95.8425217f,95.8316422f,95.8207779f,95.8099289f,95.7990952f,95.788269f,95.7774582f,95.766655f,95.755867f,95.7450943f,95.7343369f,95.7235794f,95.7128448f,95.7021179f,95.6914062f,95.6807098f,95.6700211f,95.6593399f,95.648674f,95.6380234f,95.627388f,95.6167603f,95.6061401f,95.5955353f,95.5849457f,95.5743637f,95.563797f,95.5532455f,95.5427017f,95.5321655f,95.5216446f,95.5111389f,95.5006409f,95.4901581f,95.4796829f,95.469223f,95.4587708f,95.4483337f,95.437912f,95.4274979f,95.4170914f,95.4067001f,95.3963242f,95.3859558f,95.3755951f,95.3652496f,95.3549194f,95.3445969f,95.3342819f,95.3239822f,95.3136978f,95.303421f,95.2931519f,95.2828979f,95.2726517f,95.2624207f,95.2522049f,95.2419891f,95.2317963f,95.2216034f,95.2114258f,95.2012634f,95.1911087f,95.1809692f,95.1708374f,95.1607132f,95.1506042f,95.1405029f,95.1304169f,95.1203384f,95.1102753f,95.1002197f,95.0901718f,95.0801392f,95.0701141f,95.0601044f,95.0501022f,95.0401077f,95.0301285f,95.0201569f,95.0102005f,95.0002518f,94.9903183f,94.9803848f,94.9704742f,94.9605637f,94.9506683f,94.9407806f,94.9309082f,94.9210434f,94.9111938f,94.9013519f,94.8915176f,94.881691f,94.8718796f,94.8620758f,94.8522873f,94.8425064f,94.8327332f,94.8229752f,94.8132248f,94.8034821f,94.7937546f,94.7840347f,94.7743225f,94.7646179f,94.7549286f,94.7452545f,94.7355804f,94.7259216f,94.7162704f,94.7066345f,94.6970062f,94.6873856f,94.6777725f,94.6681747f,94.6585846f,94.6490021f,94.6394348f,94.6298752f,94.6203232f,94.6107788f,94.6012497f,94.5917282f,94.5822144f,94.5727158f,94.5632172f,94.5537415f,94.5442657f,94.5347977f,94.5253448f,94.5158997f,94.5064697f,94.4970398f,94.4876251f,94.4782181f,94.4688263f,94.4594345f,94.450058f,94.4406891f,94.4313354f,94.4219818f,94.4126434f,94.4033127f,94.3939896f,94.3846817f,94.3753738f,94.3660812f,94.3567963f,94.3475266f,94.3382568f,94.3290024f,94.3197556f,94.3105164f,94.3012848f,94.2920685f,94.2828598f,94.2736588f,94.2644653f,94.2552795f,94.2461014f,94.2369385f,94.2277832f,94.2186356f,94.2094955f,94.2003708f,94.191246f,94.1821365f,94.1730347f,94.1639404f,94.1548538f,94.1457825f,94.1367111f,94.127655f,94.1186066f,94.1095657f,94.1005325f,94.091507f,94.0824966f,94.0734863f,94.0644913f,94.0555038f,94.046524f,94.0375519f,94.028595f,94.0196381f,94.0106964f,94.0017548f,93.9928284f,93.9839096f,93.9749985f,93.966095f,93.9572067f,93.9483185f,93.9394455f,93.9305725f,93.9217148f,93.9128647f,93.9040222f,93.8951874f,93.8863602f,93.8775406f,93.8687363f,93.8599319f,93.8511429f,93.8423538f,93.83358f,93.8248138f,93.8160553f,93.8073044f,93.7985611f,93.7898254f,93.7810974f,93.772377f,93.7636642f}; +const static float fmydspSIG0Wave0[2001] = {249.987076f,249.986847f,249.986633f,249.986404f,249.986176f,249.985947f,249.985703f,249.985458f,249.985214f,249.98497f,249.984711f,249.984451f,249.984192f,249.983932f,249.983658f,249.983383f,249.983109f,249.982819f,249.982529f,249.982239f,249.981934f,249.981628f,249.981323f,249.981003f,249.980682f,249.980362f,249.980026f,249.979691f,249.979355f,249.979004f,249.978653f,249.978287f,249.977921f,249.977554f,249.977173f,249.976791f,249.976395f,249.975998f,249.975601f,249.975189f,249.974762f,249.97435f,249.973907f,249.97348f,249.973022f,249.972565f,249.972107f,249.971634f,249.971161f,249.970673f,249.970184f,249.969681f,249.969177f,249.968658f,249.968124f,249.96759f,249.967041f,249.966492f,249.965927f,249.965347f,249.964767f,249.964172f,249.963577f,249.962967f,249.962341f,249.961716f,249.96106f,249.960403f,249.959747f,249.959061f,249.958374f,249.957687f,249.95697f,249.956253f,249.955505f,249.954758f,249.95401f,249.953232f,249.952438f,249.951645f,249.950836f,249.950012f,249.949173f,249.948318f,249.947449f,249.946564f,249.945663f,249.944763f,249.943832f,249.942886f,249.941925f,249.940964f,249.939972f,249.938965f,249.937943f,249.93689f,249.935837f,249.934753f,249.93367f,249.932556f,249.931427f,249.930283f,249.929108f,249.927917f,249.926712f,249.925491f,249.92424f,249.922974f,249.921677f,249.920364f,249.919037f,249.917679f,249.916306f,249.914902f,249.913483f,249.912033f,249.910553f,249.909058f,249.907547f,249.905991f,249.904419f,249.902832f,249.901199f,249.899551f,249.897873f,249.896164f,249.89444f,249.89267f,249.890884f,249.889053f,249.887207f,249.88533f,249.883408f,249.88147f,249.879486f,249.877487f,249.875443f,249.873367f,249.871262f,249.86911f,249.866928f,249.864716f,249.862457f,249.860168f,249.857849f,249.855484f,249.853073f,249.850632f,249.848145f,249.845627f,249.843048f,249.840454f,249.837799f,249.835098f,249.832367f,249.829575f,249.826752f,249.823883f,249.820953f,249.817993f,249.814972f,249.811905f,249.808777f,249.805618f,249.802399f,249.799118f,249.795792f,249.792419f,249.788986f,249.785492f,249.781952f,249.778336f,249.774673f,249.77095f,249.767166f,249.763321f,249.759415f,249.755447f,249.751419f,249.747314f,249.743149f,249.738922f,249.734619f,249.730255f,249.725815f,249.721298f,249.716721f,249.712051f,249.707321f,249.702515f,249.697632f,249.692673f,249.687622f,249.682495f,249.677292f,249.671997f,249.666626f,249.661163f,249.655624f,249.649994f,249.644257f,249.638443f,249.632538f,249.626541f,249.620438f,249.614243f,249.607956f,249.601562f,249.595062f,249.58847f,249.581772f,249.574966f,249.568054f,249.561035f,249.553909f,249.546661f,249.539307f,249.53183f,249.524246f,249.516541f,249.508698f,249.500748f,249.492676f,249.484482f,249.476151f,249.467697f,249.459106f,249.450378f,249.441528f,249.432526f,249.423401f,249.414124f,249.404709f,249.395142f,249.385437f,249.375565f,249.365555f,249.355392f,249.345078f,249.334595f,249.323959f,249.313171f,249.3022f,249.291077f,249.27977f,249.268311f,249.256668f,249.244843f,249.232849f,249.220673f,249.208298f,249.195755f,249.183014f,249.17009f,249.156967f,249.143661f,249.130142f,249.116425f,249.102509f,249.088379f,249.074051f,249.059494f,249.044739f,249.029755f,249.014557f,248.99913f,248.98349f,248.967606f,248.951508f,248.935165f,248.918579f,248.901749f,248.884689f,248.867371f,248.849808f,248.832001f,248.813919f,248.795593f,248.776993f,248.758133f,248.739014f,248.719604f,248.699936f,248.679977f,248.659744f,248.639221f,248.618408f,248.597305f,248.575912f,248.554214f,248.532211f,248.509903f,248.487289f,248.464355f,248.441116f,248.417557f,248.393661f,248.369446f,248.344894f,248.320023f,248.2948f,248.269226f,248.243317f,248.217072f,248.19046f,248.163483f,248.136154f,248.108459f,248.080399f,248.051956f,248.023148f,247.993958f,247.964386f,247.934418f,247.904068f,247.873306f,247.842163f,247.810608f,247.778641f,247.746262f,247.71347f,247.680267f,247.646622f,247.612564f,247.578064f,247.543137f,247.507767f,247.471954f,247.435699f,247.398987f,247.361816f,247.324173f,247.286087f,247.247513f,247.208481f,247.168976f,247.128983f,247.088501f,247.047531f,247.006073f,246.964111f,246.921661f,246.878693f,246.83522f,246.791229f,246.746719f,246.701706f,246.656143f,246.610062f,246.563446f,246.516296f,246.468613f,246.420364f,246.371582f,246.322235f,246.272324f,246.221863f,246.170837f,246.119247f,246.067078f,246.014328f,245.960999f,245.907074f,245.85257f,245.79747f,245.741776f,245.685486f,245.628586f,245.571091f,245.51297f,245.454239f,245.394882f,245.334915f,245.274307f,245.213089f,245.151215f,245.088715f,245.025574f,244.961792f,244.897369f,244.832291f,244.766556f,244.70015f,244.633102f,244.565384f,244.496994f,244.427948f,244.358215f,244.287811f,244.216721f,244.144958f,244.07251f,243.999359f,243.925522f,243.850998f,243.775772f,243.699844f,243.623199f,243.545868f,243.467819f,243.389053f,243.309586f,243.229401f,243.148483f,243.066849f,242.984497f,242.901413f,242.817596f,242.733047f,242.647781f,242.561752f,242.475006f,242.387512f,242.299271f,242.210281f,242.12056f,242.030075f,241.938843f,241.846863f,241.75412f,241.660629f,241.566376f,241.471359f,241.37558f,241.279037f,241.181732f,241.083664f,240.984818f,240.885208f,240.784821f,240.68367f,240.581741f,240.479034f,240.375549f,240.271286f,240.166245f,240.060425f,239.953827f,239.846451f,239.738281f,239.629333f,239.519592f,239.409073f,239.29776f,239.185654f,239.072769f,238.959091f,238.84462f,238.72937f,238.613327f,238.496475f,238.378845f,238.260422f,238.141205f,238.021194f,237.900391f,237.778778f,237.656387f,237.533203f,237.40921f,237.284439f,237.158859f,237.032486f,236.905334f,236.777374f,236.648605f,236.519058f,236.388718f,236.257568f,236.125641f,235.992905f,235.859375f,235.725067f,235.589951f,235.454041f,235.317337f,235.179855f,235.041565f,234.902481f,234.762619f,234.621964f,234.480515f,234.338272f,234.195251f,234.051422f,233.90683f,233.761429f,233.61525f,233.468292f,233.320541f,233.172012f,233.022705f,232.872604f,232.721725f,232.570068f,232.417618f,232.264404f,232.110413f,231.955643f,231.800095f,231.643768f,231.486679f,231.328812f,231.170166f,231.010757f,230.850571f,230.689621f,230.527908f,230.365433f,230.202194f,230.038177f,229.873413f,229.707886f,229.541595f,229.374542f,229.206741f,229.038177f,228.868866f,228.698807f,228.527985f,228.35643f,228.184113f,228.011047f,227.837234f,227.662689f,227.487396f,227.311356f,227.134583f,226.957077f,226.778824f,226.599838f,226.42012f,226.23967f,226.058502f,225.876587f,225.693954f,225.51059f,225.326508f,225.141708f,224.956177f,224.769928f,224.582962f,224.395279f,224.206894f,224.017776f,223.827972f,223.637436f,223.446198f,223.254272f,223.061615f,222.868271f,222.674225f,222.479477f,222.284042f,222.087906f,221.891068f,221.693542f,221.495331f,221.296432f,221.096848f,220.896576f,220.695618f,220.493973f,220.291656f,220.088654f,219.884979f,219.680634f,219.475616f,219.269928f,219.063568f,218.856537f,218.648849f,218.440491f,218.231476f,218.021805f,217.811462f,217.600479f,217.388824f,217.176529f,216.963577f,216.749969f,216.535721f,216.320831f,216.105301f,215.889114f,215.672302f,215.454834f,215.23674f,215.018021f,214.798645f,214.578659f,214.358032f,214.13678f,213.914902f,213.692398f,213.469284f,213.245529f,213.021164f,212.796188f,212.570602f,212.344391f,212.117569f,211.890137f,211.662094f,211.433456f,211.204208f,210.97435f,210.743912f,210.512848f,210.281204f,210.048965f,209.816132f,209.582687f,209.348679f,209.114059f,208.878876f,208.643097f,208.406723f,208.169785f,207.932266f,207.694153f,207.455475f,207.216232f,206.976395f,206.736008f,206.495041f,206.25351f,206.011414f,205.768738f,205.525513f,205.281738f,205.037384f,204.79248f,204.547028f,204.301025f,204.054459f,203.807343f,203.559677f,203.311478f,203.062714f,202.813416f,202.563583f,202.313202f,202.062286f,201.810837f,201.558838f,201.30632f,201.053268f,200.799683f,200.545578f,200.290939f,200.035767f,199.780075f,199.52388f,199.267151f,199.009903f,198.752136f,198.493851f,198.235062f,197.975769f,197.715958f,197.455627f,197.194809f,196.933472f,196.671631f,196.409302f,196.146454f,195.883118f,195.619293f,195.354965f,195.090134f,194.824829f,194.559021f,194.292725f,194.025955f,193.758682f,193.490936f,193.222702f,192.953995f,192.684814f,192.415146f,192.145004f,191.87439f,191.603302f,191.331741f,191.059723f,190.787231f,190.514267f,190.240845f,189.966965f,189.692627f,189.417816f,189.142563f,188.866837f,188.590668f,188.314056f,188.036987f,187.75946f,187.481491f,187.203079f,186.924225f,186.644928f,186.365204f,186.085022f,185.804413f,185.523361f,185.241882f,184.959976f,184.677643f,184.394867f,184.111679f,183.828064f,183.544022f,183.259567f,182.974701f,182.689407f,182.403702f,182.117569f,181.831039f,181.544113f,181.25676f,180.969009f,180.680862f,180.392303f,180.103348f,179.814011f,179.524261f,179.234131f,178.943604f,178.652695f,178.361389f,178.069702f,177.777649f,177.485199f,177.192383f,176.8992f,176.605637f,176.311691f,176.017395f,175.722733f,175.427704f,175.132324f,174.836594f,174.540497f,174.244049f,173.947266f,173.650131f,173.352646f,173.054825f,172.756683f,172.458191f,172.159378f,171.860245f,171.560776f,171.261002f,170.960907f,170.660492f,170.359772f,170.058746f,169.757431f,169.455811f,169.1539f,168.8517f,168.549225f,168.24646f,167.94342f,167.640121f,167.336548f,167.032715f,166.728622f,166.424286f,166.11969f,165.814865f,165.509811f,165.204529f,164.899017f,164.593292f,164.287369f,163.981232f,163.674896f,163.368378f,163.061676f,162.754791f,162.447739f,162.140533f,161.833176f,161.525681f,161.218033f,160.910278f,160.602386f,160.294403f,159.986313f,159.678131f,159.369873f,159.061554f,158.753174f,158.444733f,158.136276f,157.827789f,157.519287f,157.210785f,156.902283f,156.593826f,156.2854f,155.977036f,155.668732f,155.360504f,155.052383f,154.74437f,154.436478f,154.128738f,153.821167f,153.513763f,153.206558f,152.899551f,152.592789f,152.286285f,151.980042f,151.674088f,151.368439f,151.063126f,150.758163f,150.453568f,150.149384f,149.845596f,149.542267f,149.23938f,148.936996f,148.635117f,148.333786f,148.033005f,147.732803f,147.433228f,147.134277f,146.835999f,146.538422f,146.241547f,145.945435f,145.650085f,145.355545f,145.061829f,144.768967f,144.477005f,144.185959f,143.895859f,143.606735f,143.318604f,143.031525f,142.745499f,142.460556f,142.176743f,141.894089f,141.61261f,141.332336f,141.053299f,140.775528f,140.499039f,140.223892f,139.950073f,139.677643f,139.406601f,139.136993f,138.868835f,138.602158f,138.33699f,138.073334f,137.811234f,137.55069f,137.291748f,137.034424f,136.778717f,136.524673f,136.272293f,136.021606f,135.772614f,135.525345f,135.279816f,135.036026f,134.794006f,134.553741f,134.315277f,134.078598f,133.843735f,133.610672f,133.379425f,133.150009f,132.922424f,132.696671f,132.472748f,132.250671f,132.030441f,131.812057f,131.595505f,131.380798f,131.167938f,130.956909f,130.747726f,130.540359f,130.334824f,130.131104f,129.929214f,129.729111f,129.530823f,129.33432f,129.139603f,128.946671f,128.755493f,128.566071f,128.378387f,128.192429f,128.008209f,127.825691f,127.644867f,127.465729f,127.288269f,127.112457f,126.938293f,126.765755f,126.594841f,126.425522f,126.25779f,126.091629f,125.927032f,125.763977f,125.602448f,125.442429f,125.283905f,125.126862f,124.971291f,124.817169f,124.664482f,124.513214f,124.36335f,124.214874f,124.067772f,123.922028f,123.777626f,123.634544f,123.492783f,123.35231f,123.213127f,123.075203f,122.938538f,122.803101f,122.668884f,122.535881f,122.404068f,122.27343f,122.143951f,122.015633f,121.888435f,121.762367f,121.637405f,121.513535f,121.390747f,121.26902f,121.148354f,121.028725f,120.910118f,120.792526f,120.675941f,120.560341f,120.445717f,120.332054f,120.219353f,120.107582f,119.996742f,119.886818f,119.777802f,119.669678f,119.562439f,119.45607f,119.350563f,119.245903f,119.142082f,119.039093f,118.93692f,118.835556f,118.734993f,118.635216f,118.536217f,118.437988f,118.340515f,118.243797f,118.14782f,118.052574f,117.958046f,117.864235f,117.771133f,117.678719f,117.586998f,117.495956f,117.405594f,117.315887f,117.226837f,117.138435f,117.050667f,116.963539f,116.877037f,116.791145f,116.705864f,116.621193f,116.537109f,116.453621f,116.370712f,116.288376f,116.206612f,116.125404f,116.044762f,115.964661f,115.885101f,115.806076f,115.727585f,115.64962f,115.572174f,115.495239f,115.418808f,115.34288f,115.267441f,115.192497f,115.118042f,115.04406f,114.970551f,114.897514f,114.824936f,114.752823f,114.681152f,114.60994f,114.539169f,114.468834f,114.398933f,114.32946f,114.260414f,114.191788f,114.123573f,114.055779f,113.98838f,113.921394f,113.854805f,113.788605f,113.722794f,113.657372f,113.592339f,113.527672f,113.463387f,113.399467f,113.335922f,113.272736f,113.209908f,113.147438f,113.085312f,113.023544f,112.96212f,112.901031f,112.840286f,112.779877f,112.719795f,112.660042f,112.600616f,112.541519f,112.482735f,112.424263f,112.366104f,112.308258f,112.250717f,112.193481f,112.136551f,112.07991f,112.023575f,111.967522f,111.911766f,111.856293f,111.801109f,111.746201f,111.691574f,111.63723f,111.583153f,111.52935f,111.475815f,111.422546f,111.369545f,111.316803f,111.26432f,111.212097f,111.160133f,111.108414f,111.056946f,111.00573f,110.954758f,110.90403f,110.853546f,110.803299f,110.753296f,110.703522f,110.653984f,110.604675f,110.555595f,110.506744f,110.458122f,110.409721f,110.361542f,110.313583f,110.265839f,110.218315f,110.171005f,110.123909f,110.077026f,110.03035f,109.983879f,109.937614f,109.891556f,109.845703f,109.800041f,109.754585f,109.709328f,109.664268f,109.6194f,109.574722f,109.530243f,109.485947f,109.441841f,109.397919f,109.354187f,109.310638f,109.267273f,109.224091f,109.181084f,109.138252f,109.095604f,109.053131f,109.010826f,108.968704f,108.926743f,108.884956f,108.843346f,108.801895f,108.760612f,108.719498f,108.678543f,108.637756f,108.597122f,108.556656f,108.51635f,108.476196f,108.436203f,108.39637f,108.356682f,108.317154f,108.277779f,108.238548f,108.199478f,108.160553f,108.121773f,108.083138f,108.044655f,108.006317f,107.968117f,107.930061f,107.892151f,107.854385f,107.81675f,107.779259f,107.741905f,107.704689f,107.66761f,107.630669f,107.593857f,107.557175f,107.52063f,107.484215f,107.447937f,107.411781f,107.375755f,107.339859f,107.304092f,107.268448f,107.232933f,107.19754f,107.16227f,107.127129f,107.092102f,107.057198f,107.022423f,106.987755f,106.953217f,106.918793f,106.884483f,106.850296f,106.816223f,106.782265f,106.748421f,106.714684f,106.681068f,106.647568f,106.614174f,106.580887f,106.547714f,106.514656f,106.481705f,106.448853f,106.416115f,106.383484f,106.35096f,106.318542f,106.286232f,106.254021f,106.221916f,106.189911f,106.158005f,106.126205f,106.094513f,106.062912f,106.031418f,106.000015f,105.968719f,105.937515f,105.90641f,105.875404f,105.84449f,105.813675f,105.782959f,105.752335f,105.721802f,105.691368f,105.661018f,105.630768f,105.600609f,105.570541f,105.540565f,105.510681f,105.480881f,105.451172f,105.421555f,105.392021f,105.362579f,105.333229f,105.303955f,105.274773f,105.245682f,105.216667f,105.187744f,105.158905f,105.130142f,105.101471f,105.072876f,105.044373f,105.015945f,104.987602f,104.959335f,104.931152f,104.903053f,104.875031f,104.847084f,104.819221f,104.791435f,104.763733f,104.736099f,104.708549f,104.681076f,104.653679f,104.626358f,104.599106f,104.571938f,104.544838f,104.517822f,104.490868f,104.463997f,104.437195f,104.410469f,104.383812f,104.357231f,104.330719f,104.304283f,104.277916f,104.251617f,104.225388f,104.199234f,104.173141f,104.147125f,104.121178f,104.095299f,104.069481f,104.043739f,104.018059f,103.992455f,103.966911f,103.941437f,103.916023f,103.890678f,103.865402f,103.840187f,103.815041f,103.789955f,103.764938f,103.739983f,103.715088f,103.690262f,103.665497f,103.640793f,103.61615f,103.591568f,103.567055f,103.542603f,103.518204f,103.493874f,103.469597f,103.445389f,103.421234f,103.397141f,103.373108f,103.349136f,103.325226f,103.301369f,103.277573f,103.25383f,103.230148f,103.20652f,103.182953f,103.159447f,103.135994f,103.112595f,103.089256f,103.065971f,103.04274f,103.019569f,102.996452f,102.973389f,102.950378f,102.927422f,102.904526f,102.881676f,102.858887f,102.836143f,102.813461f,102.790825f,102.76825f,102.74572f,102.723244f,102.700821f,102.678452f,102.656128f,102.633858f,102.611641f,102.589478f,102.56736f,102.545296f,102.523277f,102.501312f,102.479401f,102.457535f,102.435715f,102.413948f,102.392227f,102.37056f,102.348938f,102.327362f,102.30584f,102.284363f,102.262932f,102.241547f,102.220215f,102.198929f,102.177689f,102.156494f,102.135345f,102.114243f,102.093185f,102.072174f,102.051208f,102.030296f,102.009422f,101.988594f,101.967804f,101.947067f,101.926376f,101.905724f,101.885117f,101.864555f,101.84404f,101.823563f,101.803131f,101.782745f,101.762398f,101.742096f,101.72184f,101.701622f,101.68145f,101.661316f,101.641228f,101.621178f,101.601173f,101.581207f,101.561287f,101.541405f,101.521561f,101.501762f,101.482002f,101.462288f,101.442604f,101.422966f,101.403374f,101.383812f,101.364296f,101.344818f,101.325378f,101.305977f,101.286613f,101.267288f,101.248009f,101.228767f,101.209557f,101.190392f,101.171257f,101.152168f,101.133118f,101.114098f,101.095123f,101.07618f,101.057281f,101.038414f,101.019585f,101.000793f,100.982033f,100.963318f,100.944633f,100.925987f,100.907379f,100.888809f,100.87027f,100.851768f,100.833305f,100.814873f,100.796478f,100.778114f,100.759796f,100.741501f,100.723251f,100.705032f,100.686844f,100.668694f,100.650574f,100.632492f,100.614449f,100.596436f,100.578453f,100.560509f,100.542595f,100.524719f,100.506866f,100.489059f,100.471275f,100.453529f,100.435822f,100.418137f,100.40049f,100.382874f,100.365295f,100.34774f,100.330223f,100.312737f,100.29528f,100.277863f,100.260468f,100.243111f,100.225784f,100.208488f,100.191223f,100.173988f,100.156784f,100.139618f,100.122475f,100.10537f,100.088287f,100.071236f,100.054222f,100.037231f,100.020271f,100.003349f,99.9864502f,99.9695816f,99.9527435f,99.935936f,99.9191589f,99.9024124f,99.8856888f,99.8689957f,99.8523407f,99.8357086f,99.8191071f,99.8025284f,99.7859879f,99.7694702f,99.7529831f,99.7365189f,99.7200928f,99.7036896f,99.6873169f,99.6709671f,99.6546478f,99.6383591f,99.6220932f,99.6058655f,99.589653f,99.5734787f,99.5573273f,99.5411987f,99.5251007f,99.5090332f,99.4929886f,99.4769745f,99.4609833f,99.4450226f,99.4290848f,99.4131775f,99.3973007f,99.3814392f,99.3656158f,99.3498077f,99.3340378f,99.3182831f,99.3025589f,99.2868652f,99.2711868f,99.2555466f,99.2399216f,99.2243271f,99.2087631f,99.1932144f,99.1776962f,99.1622009f,99.1467361f,99.1312943f,99.1158752f,99.1004868f,99.0851135f,99.0697708f,99.0544586f,99.0391617f,99.0238953f,99.0086517f,98.9934311f,98.9782333f,98.9630661f,98.9479218f,98.9327927f,98.9176941f,98.902626f,98.8875732f,98.8725433f,98.8575439f,98.8425674f,98.8276062f,98.8126755f,98.7977676f,98.7828827f,98.7680206f,98.7531891f,98.7383728f,98.7235794f,98.7088089f,98.6940613f,98.6793442f,98.6646423f,98.6499634f,98.6353073f,98.6206818f,98.6060715f,98.5914841f,98.5769196f,98.5623779f,98.5478592f,98.5333633f,98.5188828f,98.5044327f,98.4900055f,98.4755936f,98.4612045f,98.4468384f,98.4324951f,98.4181747f,98.4038773f,98.389595f,98.3753433f,98.3611069f,98.3468933f,98.3327026f,98.3185272f,98.3043823f,98.2902527f,98.2761459f,98.2620544f,98.2479935f,98.2339478f,98.2199249f,98.2059174f,98.1919327f,98.1779709f,98.164032f,98.150116f,98.1362152f,98.1223297f,98.1084747f,98.094635f,98.0808182f,98.0670166f,98.0532379f,98.0394821f,98.0257416f,98.0120239f,97.9983292f,97.9846497f,97.9709854f,97.9573517f,97.9437332f,97.93013f,97.9165497f,97.9029922f,97.8894501f,97.8759308f,97.8624268f,97.8489456f,97.8354797f,97.8220367f,97.808609f,97.7952042f,97.7818146f,97.7684479f,97.7550964f,97.7417679f,97.7284546f,97.7151642f,97.701889f,97.6886368f,97.6753998f,97.662178f,97.6489792f,97.6357956f,97.6226349f,97.6094894f,97.5963669f,97.583252f,97.5701675f,97.5570908f,97.5440445f,97.5310059f,97.5179901f,97.5049896f,97.4920044f,97.4790421f,97.466095f,97.4531708f,97.4402618f,97.4273682f,97.4144897f,97.4016342f,97.3887939f,97.3759766f,97.3631668f,97.3503799f,97.3376083f,97.3248596f,97.3121262f,97.299408f,97.286705f,97.2740173f,97.2613525f,97.248703f,97.2360687f,97.2234573f,97.2108536f,97.1982727f,97.1857071f,97.1731567f,97.1606293f,97.1481094f,97.1356125f,97.1231308f,97.1106644f,97.0982208f,97.0857849f,97.0733719f,97.0609665f,97.048584f,97.0362167f,97.0238647f,97.0115356f,96.9992142f,96.9869156f,96.9746246f,96.9623566f,96.9501038f,96.9378662f,96.9256439f,96.9134369f,96.9012451f,96.8890686f,96.876915f,96.864769f,96.8526382f,96.8405304f,96.8284302f,96.8163528f,96.8042908f,96.7922363f,96.7802048f,96.7681808f,96.7561798f,96.744194f,96.7322235f,96.7202606f,96.7083206f,96.6963959f,96.6844788f,96.6725845f,96.6607056f,96.6488342f,96.6369858f,96.625145f,96.613327f,96.6015167f,96.5897217f,96.5779495f,96.566185f,96.5544357f,96.5427017f,96.530983f,96.5192795f,96.5075912f,96.4959183f,96.4842529f,96.4726105f,96.4609756f,96.4493637f,96.4377594f,96.4261703f,96.4145966f,96.403038f,96.3914948f,96.3799591f,96.3684464f,96.3569412f,96.3454514f,96.3339767f,96.3225174f,96.3110733f,96.2996368f,96.2882156f,96.2768097f,96.265419f,96.2540436f,96.2426834f,96.2313309f,96.2199936f,96.2086716f,96.1973648f,96.1860733f,96.1747894f,96.1635208f,96.1522675f,96.1410294f,96.1297989f,96.1185837f,96.1073837f,96.096199f,96.085022f,96.0738602f,96.0627136f,96.0515823f,96.0404587f,96.0293579f,96.0182571f,96.0071793f,95.996109f,95.985054f,95.9740143f,95.9629822f,95.951973f,95.9409637f,95.9299774f,95.9189987f,95.9080353f,95.8970871f,95.8861465f,95.8752213f,95.8643036f,95.8534088f,95.8425217f,95.8316422f,95.8207779f,95.8099289f,95.7990952f,95.788269f,95.7774582f,95.766655f,95.755867f,95.7450943f,95.7343369f,95.7235794f,95.7128448f,95.7021179f,95.6914062f,95.6807098f,95.6700211f,95.6593399f,95.648674f,95.6380234f,95.627388f,95.6167603f,95.6061401f,95.5955353f,95.5849457f,95.5743637f,95.563797f,95.5532455f,95.5427017f,95.5321655f,95.5216446f,95.5111389f,95.5006409f,95.4901581f,95.4796829f,95.469223f,95.4587708f,95.4483337f,95.437912f,95.4274979f,95.4170914f,95.4067001f,95.3963242f,95.3859558f,95.3755951f,95.3652496f,95.3549194f,95.3445969f,95.3342819f,95.3239822f,95.3136978f,95.303421f,95.2931519f,95.2828979f,95.2726517f,95.2624207f,95.2522049f,95.2419891f,95.2317963f,95.2216034f,95.2114258f,95.2012634f,95.1911087f,95.1809692f,95.1708374f,95.1607132f,95.1506042f,95.1405029f,95.1304169f,95.1203384f,95.1102753f,95.1002197f,95.0901718f,95.0801392f,95.0701141f,95.0601044f,95.0501022f,95.0401077f,95.0301285f,95.0201569f,95.0102005f,95.0002518f,94.9903183f,94.9803848f,94.9704742f,94.9605637f,94.9506683f,94.9407806f,94.9309082f,94.9210434f,94.9111938f,94.9013519f,94.8915176f,94.881691f,94.8718796f,94.8620758f,94.8522873f,94.8425064f,94.8327332f,94.8229752f,94.8132248f,94.8034821f,94.7937546f,94.7840347f,94.7743225f,94.7646179f,94.7549286f,94.7452545f,94.7355804f,94.7259216f,94.7162704f,94.7066345f,94.6970062f,94.6873856f,94.6777725f,94.6681747f,94.6585846f,94.6490021f,94.6394348f,94.6298752f,94.6203232f,94.6107788f,94.6012497f,94.5917282f,94.5822144f,94.5727158f,94.5632172f,94.5537415f,94.5442657f,94.5347977f,94.5253448f,94.5158997f,94.5064697f,94.4970398f,94.4876251f,94.4782181f,94.4688263f,94.4594345f,94.450058f,94.4406891f,94.4313354f,94.4219818f,94.4126434f,94.4033127f,94.3939896f,94.3846817f,94.3753738f,94.3660812f,94.3567963f,94.3475266f,94.3382568f,94.3290024f,94.3197556f,94.3105164f,94.3012848f,94.2920685f,94.2828598f,94.2736588f,94.2644653f,94.2552795f,94.2461014f,94.2369385f,94.2277832f,94.2186356f,94.2094955f,94.2003708f,94.191246f,94.1821365f,94.1730347f,94.1639404f,94.1548538f,94.1457825f,94.1367111f,94.127655f,94.1186066f,94.1095657f,94.1005325f,94.091507f,94.0824966f,94.0734863f,94.0644913f,94.0555038f,94.046524f,94.0375519f,94.028595f,94.0196381f,94.0106964f,94.0017548f,93.9928284f,93.9839096f,93.9749985f,93.966095f,93.9572067f,93.9483185f,93.9394455f,93.9305725f,93.9217148f,93.9128647f,93.9040222f,93.8951874f,93.8863602f,93.8775406f,93.8687363f,93.8599319f,93.8511429f,93.8423538f,93.83358f,93.8248138f,93.8160553f,93.8073044f,93.7985611f,93.7898254f,93.7810974f,93.772377f,93.7636642f}; class mydspSIG0 { private: @@ -67,7 +67,7 @@ class mydspSIG0 { static mydspSIG0* newmydspSIG0() { return (mydspSIG0*)new mydspSIG0(); } static void deletemydspSIG0(mydspSIG0* dsp) { delete dsp; } -static const float fmydspSIG1Wave0[2001] = {249.987076f,249.986847f,249.986633f,249.986404f,249.986176f,249.985947f,249.985703f,249.985458f,249.985214f,249.98497f,249.984711f,249.984451f,249.984192f,249.983932f,249.983658f,249.983383f,249.983109f,249.982819f,249.982529f,249.982239f,249.981934f,249.981628f,249.981323f,249.981003f,249.980682f,249.980362f,249.980026f,249.979691f,249.979355f,249.979004f,249.978653f,249.978287f,249.977921f,249.977554f,249.977173f,249.976791f,249.976395f,249.975998f,249.975601f,249.975189f,249.974762f,249.97435f,249.973907f,249.97348f,249.973022f,249.972565f,249.972107f,249.971634f,249.971161f,249.970673f,249.970184f,249.969681f,249.969177f,249.968658f,249.968124f,249.96759f,249.967041f,249.966492f,249.965927f,249.965347f,249.964767f,249.964172f,249.963577f,249.962967f,249.962341f,249.961716f,249.96106f,249.960403f,249.959747f,249.959061f,249.958374f,249.957687f,249.95697f,249.956253f,249.955505f,249.954758f,249.95401f,249.953232f,249.952438f,249.951645f,249.950836f,249.950012f,249.949173f,249.948318f,249.947449f,249.946564f,249.945663f,249.944763f,249.943832f,249.942886f,249.941925f,249.940964f,249.939972f,249.938965f,249.937943f,249.93689f,249.935837f,249.934753f,249.93367f,249.932556f,249.931427f,249.930283f,249.929108f,249.927917f,249.926712f,249.925491f,249.92424f,249.922974f,249.921677f,249.920364f,249.919037f,249.917679f,249.916306f,249.914902f,249.913483f,249.912033f,249.910553f,249.909058f,249.907547f,249.905991f,249.904419f,249.902832f,249.901199f,249.899551f,249.897873f,249.896164f,249.89444f,249.89267f,249.890884f,249.889053f,249.887207f,249.88533f,249.883408f,249.88147f,249.879486f,249.877487f,249.875443f,249.873367f,249.871262f,249.86911f,249.866928f,249.864716f,249.862457f,249.860168f,249.857849f,249.855484f,249.853073f,249.850632f,249.848145f,249.845627f,249.843048f,249.840454f,249.837799f,249.835098f,249.832367f,249.829575f,249.826752f,249.823883f,249.820953f,249.817993f,249.814972f,249.811905f,249.808777f,249.805618f,249.802399f,249.799118f,249.795792f,249.792419f,249.788986f,249.785492f,249.781952f,249.778336f,249.774673f,249.77095f,249.767166f,249.763321f,249.759415f,249.755447f,249.751419f,249.747314f,249.743149f,249.738922f,249.734619f,249.730255f,249.725815f,249.721298f,249.716721f,249.712051f,249.707321f,249.702515f,249.697632f,249.692673f,249.687622f,249.682495f,249.677292f,249.671997f,249.666626f,249.661163f,249.655624f,249.649994f,249.644257f,249.638443f,249.632538f,249.626541f,249.620438f,249.614243f,249.607956f,249.601562f,249.595062f,249.58847f,249.581772f,249.574966f,249.568054f,249.561035f,249.553909f,249.546661f,249.539307f,249.53183f,249.524246f,249.516541f,249.508698f,249.500748f,249.492676f,249.484482f,249.476151f,249.467697f,249.459106f,249.450378f,249.441528f,249.432526f,249.423401f,249.414124f,249.404709f,249.395142f,249.385437f,249.375565f,249.365555f,249.355392f,249.345078f,249.334595f,249.323959f,249.313171f,249.3022f,249.291077f,249.27977f,249.268311f,249.256668f,249.244843f,249.232849f,249.220673f,249.208298f,249.195755f,249.183014f,249.17009f,249.156967f,249.143661f,249.130142f,249.116425f,249.102509f,249.088379f,249.074051f,249.059494f,249.044739f,249.029755f,249.014557f,248.99913f,248.98349f,248.967606f,248.951508f,248.935165f,248.918579f,248.901749f,248.884689f,248.867371f,248.849808f,248.832001f,248.813919f,248.795593f,248.776993f,248.758133f,248.739014f,248.719604f,248.699936f,248.679977f,248.659744f,248.639221f,248.618408f,248.597305f,248.575912f,248.554214f,248.532211f,248.509903f,248.487289f,248.464355f,248.441116f,248.417557f,248.393661f,248.369446f,248.344894f,248.320023f,248.2948f,248.269226f,248.243317f,248.217072f,248.19046f,248.163483f,248.136154f,248.108459f,248.080399f,248.051956f,248.023148f,247.993958f,247.964386f,247.934418f,247.904068f,247.873306f,247.842163f,247.810608f,247.778641f,247.746262f,247.71347f,247.680267f,247.646622f,247.612564f,247.578064f,247.543137f,247.507767f,247.471954f,247.435699f,247.398987f,247.361816f,247.324173f,247.286087f,247.247513f,247.208481f,247.168976f,247.128983f,247.088501f,247.047531f,247.006073f,246.964111f,246.921661f,246.878693f,246.83522f,246.791229f,246.746719f,246.701706f,246.656143f,246.610062f,246.563446f,246.516296f,246.468613f,246.420364f,246.371582f,246.322235f,246.272324f,246.221863f,246.170837f,246.119247f,246.067078f,246.014328f,245.960999f,245.907074f,245.85257f,245.79747f,245.741776f,245.685486f,245.628586f,245.571091f,245.51297f,245.454239f,245.394882f,245.334915f,245.274307f,245.213089f,245.151215f,245.088715f,245.025574f,244.961792f,244.897369f,244.832291f,244.766556f,244.70015f,244.633102f,244.565384f,244.496994f,244.427948f,244.358215f,244.287811f,244.216721f,244.144958f,244.07251f,243.999359f,243.925522f,243.850998f,243.775772f,243.699844f,243.623199f,243.545868f,243.467819f,243.389053f,243.309586f,243.229401f,243.148483f,243.066849f,242.984497f,242.901413f,242.817596f,242.733047f,242.647781f,242.561752f,242.475006f,242.387512f,242.299271f,242.210281f,242.12056f,242.030075f,241.938843f,241.846863f,241.75412f,241.660629f,241.566376f,241.471359f,241.37558f,241.279037f,241.181732f,241.083664f,240.984818f,240.885208f,240.784821f,240.68367f,240.581741f,240.479034f,240.375549f,240.271286f,240.166245f,240.060425f,239.953827f,239.846451f,239.738281f,239.629333f,239.519592f,239.409073f,239.29776f,239.185654f,239.072769f,238.959091f,238.84462f,238.72937f,238.613327f,238.496475f,238.378845f,238.260422f,238.141205f,238.021194f,237.900391f,237.778778f,237.656387f,237.533203f,237.40921f,237.284439f,237.158859f,237.032486f,236.905334f,236.777374f,236.648605f,236.519058f,236.388718f,236.257568f,236.125641f,235.992905f,235.859375f,235.725067f,235.589951f,235.454041f,235.317337f,235.179855f,235.041565f,234.902481f,234.762619f,234.621964f,234.480515f,234.338272f,234.195251f,234.051422f,233.90683f,233.761429f,233.61525f,233.468292f,233.320541f,233.172012f,233.022705f,232.872604f,232.721725f,232.570068f,232.417618f,232.264404f,232.110413f,231.955643f,231.800095f,231.643768f,231.486679f,231.328796f,231.170166f,231.010757f,230.850571f,230.689621f,230.527908f,230.365433f,230.202194f,230.038177f,229.873413f,229.707886f,229.541595f,229.374542f,229.206741f,229.038177f,228.868866f,228.698807f,228.527985f,228.356415f,228.184113f,228.011047f,227.837234f,227.662689f,227.487396f,227.311356f,227.134583f,226.957077f,226.778824f,226.599838f,226.42012f,226.23967f,226.058487f,225.876587f,225.693954f,225.51059f,225.326508f,225.141693f,224.956177f,224.769928f,224.582962f,224.395279f,224.206879f,224.017776f,223.827957f,223.637436f,223.446198f,223.254257f,223.061615f,222.868271f,222.674225f,222.479477f,222.284027f,222.087891f,221.891068f,221.693542f,221.495331f,221.296417f,221.096832f,220.896561f,220.695602f,220.493958f,220.291641f,220.088654f,219.884979f,219.680634f,219.475601f,219.269913f,219.063553f,218.856522f,218.648834f,218.440475f,218.231461f,218.02179f,217.811447f,217.600449f,217.388809f,217.176498f,216.963547f,216.749954f,216.535706f,216.320816f,216.10527f,215.889084f,215.672272f,215.454803f,215.23671f,215.017975f,214.798615f,214.578629f,214.358002f,214.136749f,213.914871f,213.692368f,213.469238f,213.245483f,213.021118f,212.796143f,212.570541f,212.34433f,212.117508f,211.890076f,211.662048f,211.433395f,211.204147f,210.974289f,210.743835f,210.512787f,210.281128f,210.048889f,209.81604f,209.582611f,209.348587f,209.113968f,208.878769f,208.64299f,208.406631f,208.169678f,207.932144f,207.694046f,207.455353f,207.216095f,206.976273f,206.73587f,206.494888f,206.253357f,206.011246f,205.768585f,205.525345f,205.281555f,205.037201f,204.792297f,204.546829f,204.300812f,204.054245f,203.807114f,203.559448f,203.311234f,203.062469f,202.813156f,202.563309f,202.312912f,202.061981f,201.810516f,201.558517f,201.305984f,201.052917f,200.799316f,200.545181f,200.290527f,200.035355f,199.779648f,199.523422f,199.266678f,199.009399f,198.751617f,198.493317f,198.234512f,197.975189f,197.715347f,197.455002f,197.194153f,196.932785f,196.670929f,196.408554f,196.145691f,195.882324f,195.618454f,195.354095f,195.089233f,194.823883f,194.558044f,194.291702f,194.024887f,193.757568f,193.489777f,193.221497f,192.952744f,192.683502f,192.413788f,192.143585f,191.87291f,191.601761f,191.330154f,191.05806f,190.785492f,190.512466f,190.238968f,189.965012f,189.690582f,189.415695f,189.14035f,188.864548f,188.588272f,188.311554f,188.034378f,187.75676f,187.478683f,187.20015f,186.921173f,186.641754f,186.361893f,186.081573f,185.800827f,185.519638f,185.238007f,184.955933f,184.673431f,184.390488f,184.107117f,183.823303f,183.539078f,183.25441f,182.96933f,182.683823f,182.397873f,182.111526f,181.824738f,181.537552f,181.249939f,180.961899f,180.673462f,180.384598f,180.095337f,179.805664f,179.515579f,179.225082f,178.934189f,178.642899f,178.351196f,178.059097f,177.766602f,177.473709f,177.18042f,176.886734f,176.592667f,176.298203f,176.003357f,175.708115f,175.412506f,175.116501f,174.820114f,174.523346f,174.226212f,173.928696f,173.630798f,173.332535f,173.033905f,172.734894f,172.435532f,172.135788f,171.835693f,171.535233f,171.234421f,170.933243f,170.631714f,170.329834f,170.027603f,169.725021f,169.422089f,169.11882f,168.815201f,168.511246f,168.20694f,167.902313f,167.597351f,167.292053f,166.98642f,166.680466f,166.374191f,166.067596f,165.760666f,165.45343f,165.145889f,164.838028f,164.529846f,164.221375f,163.912598f,163.603516f,163.294144f,162.984467f,162.6745f,162.364258f,162.053711f,161.742905f,161.431808f,161.120438f,160.808792f,160.496887f,160.184708f,159.872269f,159.559586f,159.246643f,158.933441f,158.62001f,158.306335f,157.992432f,157.678284f,157.363922f,157.049332f,156.734528f,156.41951f,156.104279f,155.788849f,155.473236f,155.157425f,154.841431f,154.525253f,154.208908f,153.89241f,153.575729f,153.258911f,152.941956f,152.624847f,152.307602f,151.99025f,151.672775f,151.355194f,151.037506f,150.719727f,150.401871f,150.083939f,149.765945f,149.447891f,149.129807f,148.811676f,148.493515f,148.175354f,147.857178f,147.539017f,147.220871f,146.902756f,146.584686f,146.266678f,145.94873f,145.630875f,145.313126f,144.995468f,144.677948f,144.360565f,144.043335f,143.726288f,143.409424f,143.092758f,142.776321f,142.460129f,142.14418f,141.828522f,141.513153f,141.19809f,140.883362f,140.569f,140.255005f,139.941391f,139.62822f,139.315475f,139.003189f,138.691391f,138.380096f,138.069336f,137.75914f,137.449524f,137.140503f,136.832123f,136.524384f,136.217346f,135.911011f,135.605408f,135.300583f,134.996536f,134.693329f,134.390945f,134.089462f,133.788864f,133.489212f,133.190521f,132.892822f,132.596146f,132.300522f,132.005981f,131.71254f,131.420258f,131.129135f,130.839203f,130.550507f,130.263062f,129.976898f,129.692062f,129.408569f,129.126434f,128.845703f,128.566391f,128.288528f,128.012161f,127.737282f,127.463936f,127.192139f,126.921921f,126.653305f,126.386307f,126.120956f,125.857269f,125.595276f,125.334976f,125.076408f,124.819572f,124.564499f,124.311203f,124.059692f,123.80999f,123.562096f,123.316032f,123.071815f,122.829437f,122.588921f,122.350273f,122.113503f,121.878609f,121.645607f,121.41449f,121.185272f,120.957947f,120.732521f,120.509003f,120.287376f,120.067642f,119.849815f,119.633881f,119.41983f,119.207672f,118.997398f,118.788994f,118.582458f,118.377792f,118.174973f,117.974007f,117.774872f,117.577568f,117.382088f,117.188408f,116.996521f,116.806427f,116.618103f,116.431541f,116.246719f,116.063644f,115.882278f,115.702629f,115.524666f,115.348389f,115.173775f,115.000801f,114.829475f,114.65976f,114.491646f,114.325127f,114.160179f,113.996788f,113.834938f,113.674614f,113.5158f,113.358482f,113.202637f,113.048248f,112.895317f,112.743805f,112.593712f,112.445007f,112.297691f,112.151741f,112.007141f,111.863869f,111.721916f,111.581268f,111.44191f,111.303818f,111.166985f,111.031387f,110.897018f,110.763863f,110.631905f,110.501122f,110.371506f,110.24305f,110.115723f,109.989525f,109.864433f,109.74044f,109.617531f,109.495689f,109.374901f,109.25515f,109.136436f,109.01873f,108.902031f,108.786324f,108.671593f,108.557831f,108.445015f,108.333145f,108.222206f,108.112183f,108.003059f,107.894836f,107.787498f,107.68103f,107.575424f,107.470673f,107.366753f,107.263672f,107.1614f,107.059944f,106.959282f,106.859413f,106.760315f,106.661995f,106.56443f,106.467621f,106.371544f,106.276207f,106.181587f,106.087685f,105.994492f,105.901985f,105.810173f,105.719048f,105.628586f,105.538788f,105.449646f,105.361153f,105.2733f,105.186073f,105.09948f,105.013504f,104.928131f,104.843369f,104.759193f,104.675613f,104.592613f,104.510193f,104.428337f,104.347038f,104.266304f,104.186111f,104.106461f,104.027351f,103.948769f,103.870712f,103.793175f,103.716148f,103.639633f,103.563614f,103.488091f,103.413055f,103.338509f,103.264442f,103.190842f,103.117714f,103.045052f,102.972847f,102.901093f,102.829788f,102.758926f,102.688507f,102.618515f,102.548958f,102.47982f,102.41111f,102.342812f,102.274925f,102.207443f,102.140366f,102.073692f,102.007408f,101.941513f,101.875999f,101.810875f,101.746132f,101.681755f,101.61776f,101.554123f,101.490852f,101.42794f,101.365379f,101.303177f,101.241325f,101.17981f,101.118645f,101.057816f,100.997322f,100.937157f,100.877327f,100.817818f,100.758629f,100.69976f,100.641212f,100.582977f,100.525047f,100.46743f,100.41011f,100.353096f,100.296379f,100.23996f,100.18383f,100.127991f,100.072441f,100.017174f,99.9621964f,99.9074936f,99.8530655f,99.798912f,99.7450333f,99.6914215f,99.6380768f,99.5849991f,99.5321808f,99.4796219f,99.4273224f,99.3752747f,99.3234863f,99.2719498f,99.2206573f,99.1696091f,99.1188049f,99.0682449f,99.0179291f,98.9678497f,98.9180069f,98.8683929f,98.8190155f,98.7698593f,98.7209396f,98.6722488f,98.6237717f,98.5755234f,98.5274963f,98.4796829f,98.4320908f,98.3847122f,98.3375473f,98.290596f,98.2438507f,98.1973114f,98.1509781f,98.1048508f,98.0589294f,98.0132065f,97.9676819f,97.9223633f,97.8772278f,97.8322983f,97.7875595f,97.7430115f,97.6986542f,97.65448f,97.6104965f,97.5667038f,97.5230865f,97.47966f,97.436409f,97.3933411f,97.3504562f,97.3077469f,97.2652054f,97.222847f,97.1806564f,97.1386414f,97.0967941f,97.0551224f,97.0136108f,96.9722748f,96.9310989f,96.8900833f,96.8492355f,96.808548f,96.7680283f,96.7276611f,96.6874542f,96.6473999f,96.6075058f,96.5677719f,96.528183f,96.4887543f,96.4494705f,96.4103394f,96.3713608f,96.3325272f,96.2938385f,96.2553024f,96.2169113f,96.1786575f,96.1405563f,96.1025925f,96.0647659f,96.0270844f,95.9895401f,95.9521408f,95.9148712f,95.877739f,95.840744f,95.8038864f,95.7671585f,95.7305603f,95.6940994f,95.6577682f,95.6215668f,95.585495f,95.5495529f,95.5137329f,95.4780426f,95.442482f,95.4070435f,95.371727f,95.3365326f,95.3014603f,95.2665176f,95.2316895f,95.1969833f,95.1623993f,95.1279297f,95.0935745f,95.0593414f,95.0252228f,94.9912262f,94.9573364f,94.9235611f,94.8899002f,94.8563538f,94.8229218f,94.7895966f,94.7563858f,94.7232819f,94.6902847f,94.657402f,94.6246185f,94.5919495f,94.5593872f,94.5269241f,94.4945755f,94.462326f,94.4301758f,94.39814f,94.3661957f,94.3343582f,94.3026276f,94.2709885f,94.2394562f,94.2080154f,94.1766815f,94.1454468f,94.1143036f,94.0832596f,94.0523148f,94.0214615f,93.9907074f,93.9600525f,93.9294815f,93.8990097f,93.8686371f,93.8383484f,93.8081589f,93.7780533f,93.7480469f,93.7181244f,93.6882935f,93.6585541f,93.6289062f,93.5993423f,93.5698624f,93.5404816f,93.5111771f,93.4819641f,93.4528427f,93.4237976f,93.3948441f,93.3659668f,93.3371811f,93.3084793f,93.2798615f,93.2513199f,93.2228622f,93.1944885f,93.1661987f,93.1379929f,93.1098557f,93.08181f,93.0538406f,93.0259476f,92.9981384f,92.9703979f,92.9427414f,92.9151688f,92.8876648f,92.8602448f,92.8328934f,92.8056183f,92.7784271f,92.7513046f,92.7242584f,92.6972809f,92.6703873f,92.6435623f,92.616806f,92.590126f,92.5635223f,92.5369873f,92.5105286f,92.4841309f,92.4578171f,92.4315643f,92.4053879f,92.3792725f,92.3532333f,92.3272629f,92.3013611f,92.275528f,92.2497635f,92.2240601f,92.1984329f,92.1728668f,92.1473694f,92.1219406f,92.0965729f,92.0712738f,92.0460434f,92.020874f,91.9957733f,91.9707336f,91.9457626f,91.920845f,91.8960037f,91.8712158f,91.8464966f,91.8218384f,91.7972412f,91.7727051f,91.74823f,91.7238235f,91.6994705f,91.6751862f,91.6509552f,91.6267853f,91.6026764f,91.5786285f,91.5546341f,91.5307083f,91.5068359f,91.4830246f,91.4592667f,91.4355698f,91.4119263f,91.3883514f,91.3648224f,91.3413544f,91.3179474f,91.2945862f,91.2712936f,91.2480469f,91.2248611f,91.2017288f,91.1786499f,91.155632f,91.1326599f,91.1097488f,91.0868912f,91.0640869f,91.0413361f,91.0186386f,90.9959946f,90.9733963f,90.9508591f,90.9283752f,90.9059372f,90.8835526f,90.8612213f,90.8389435f,90.8167191f,90.7945404f,90.7724152f,90.7503357f,90.7283096f,90.706337f,90.6844101f,90.6625366f,90.6407089f,90.618927f,90.5971985f,90.5755234f,90.553894f,90.5323105f,90.5107727f,90.4892883f,90.4678497f,90.4464645f,90.4251175f,90.4038239f,90.382576f,90.3613739f,90.3402176f,90.3191071f,90.2980423f,90.2770233f,90.2560577f,90.2351303f,90.2142487f,90.1934128f,90.1726227f,90.1518784f,90.1311798f,90.1105194f,90.0899124f,90.0693436f,90.0488205f,90.0283432f,90.0079041f,89.9875107f,89.9671631f,89.9468536f,89.92659f,89.9063721f,89.8861923f,89.8660583f,89.8459625f,89.8259125f,89.8059006f,89.7859344f,89.7660065f,89.7461166f,89.7262726f,89.7064667f,89.6867065f,89.6669846f,89.6473007f,89.6276627f,89.6080627f,89.588501f,89.5689774f,89.5494995f,89.5300598f,89.5106583f,89.4912949f,89.4719696f,89.4526825f,89.4334335f,89.4142303f,89.3950577f,89.3759308f,89.3568344f,89.3377838f,89.3187637f,89.2997894f,89.2808456f,89.26194f,89.2430725f,89.2242432f,89.205452f,89.1866989f,89.1679764f,89.1492996f,89.1306534f,89.1120453f,89.0934677f,89.0749359f,89.0564346f,89.0379639f,89.0195389f,89.0011444f,88.9827881f,88.9644623f,88.9461746f,88.9279251f,88.9097061f,88.8915176f,88.8733673f,88.8552551f,88.8371735f,88.8191299f,88.8011169f,88.7831421f,88.7651978f,88.7472916f,88.7294083f,88.7115707f,88.6937561f,88.6759796f,88.6582413f,88.6405258f,88.6228485f,88.6052094f,88.5875931f,88.570015f,88.5524673f,88.5349579f,88.5174713f,88.5000229f,88.482605f,88.4652176f,88.4478607f,88.430542f,88.4132462f,88.3959885f,88.3787613f,88.3615646f,88.3443985f,88.3272629f,88.3101578f,88.2930832f,88.2760391f,88.2590256f,88.2420425f,88.22509f,88.208168f,88.1912766f,88.1744156f,88.1575851f,88.1407776f,88.1240082f,88.1072693f,88.0905533f,88.0738678f,88.0572128f,88.0405884f,88.0239944f,88.0074234f,87.9908829f,87.9743805f,87.9578934f,87.9414444f,87.9250183f,87.9086227f,87.8922577f,87.8759155f,87.8596115f,87.8433228f,87.8270721f,87.8108444f,87.7946472f,87.7784729f,87.7623291f,87.7462158f,87.7301254f,87.7140656f,87.6980286f,87.6820221f,87.6660385f,87.6500854f,87.6341629f,87.6182632f,87.6023865f,87.5865402f,87.5707245f,87.5549316f,87.5391617f,87.5234222f,87.5077057f,87.4920197f,87.4763565f,87.4607162f,87.4451065f,87.4295197f,87.4139633f,87.3984299f,87.3829193f,87.3674393f,87.3519821f,87.3365479f,87.3211365f,87.3057556f,87.2903976f,87.2750702f,87.259758f,87.2444763f,87.2292175f,87.2139893f,87.1987762f,87.1835938f,87.1684341f,87.1532974f,87.1381912f,87.1231003f,87.1080399f,87.0930023f,87.0779877f,87.0629959f,87.048027f,87.0330811f,87.0181656f,87.0032654f,86.9883957f,86.9735413f,86.9587173f,86.9439163f,86.9291382f,86.9143753f,86.8996429f,86.8849335f,86.8702469f,86.8555832f,86.8409424f,86.8263245f,86.8117218f,86.7971497f,86.7826004f,86.768074f,86.7535629f,86.7390823f,86.724617f,86.7101822f,86.6957626f,86.681366f,86.6669922f,86.6526413f,86.6383133f,86.6240005f,86.6097183f,86.5954514f,86.5812073f,86.5669861f,86.5527878f,86.5386124f,86.5244522f,86.5103149f,86.4962006f,86.4821091f,86.4680328f,86.4539871f,86.4399567f,86.4259415f,86.4119568f,86.3979874f,86.3840408f,86.3701172f,86.3562088f,86.3423233f,86.3284607f,86.3146133f,86.3007889f,86.2869873f,86.273201f,86.2594376f,86.245697f,86.2319717f,86.2182693f,86.2045898f,86.1909256f,86.1772842f,86.1636581f,86.1500549f,86.1364746f,86.1229095f,86.1093597f,86.0958405f,86.0823288f,86.0688477f,86.0553818f,86.0419312f,86.0285034f,86.0150909f,86.0017014f,85.9883347f,85.9749832f,85.961647f,85.9483337f,85.9350433f,85.9217606f,85.9085083f,85.8952637f,85.8820496f,85.8688431f,85.8556595f,85.8424988f,85.8293533f,85.8162231f,85.8031158f,85.7900238f,85.7769547f,85.7639008f,85.7508621f,85.7378464f,85.7248459f,85.7118607f,85.6988983f,85.6859512f,85.673027f,85.6601181f,85.6472244f,85.634346f,85.6214905f,85.6086502f,85.5958328f,85.5830231f,85.5702362f,85.5574722f,85.5447159f,85.5319824f,85.5192642f,85.5065689f,85.4938812f,85.4812164f,85.4685669f,85.4559402f,85.4433212f,85.4307251f,85.4181442f,85.4055786f,85.3930359f,85.3805084f,85.3679886f,85.3554993f,85.3430176f,85.3305511f,85.3181076f,85.3056793f,85.2932663f,85.2808685f,85.268486f,85.2561188f,85.2437744f,85.2314453f,85.2191315f,85.2068329f,85.1945496f,85.1822815f,85.1700287f,85.1577911f,85.1455765f,85.1333771f,85.1211853f,85.1090164f,85.0968628f,85.0847244f,85.0726013f,85.0604935f,85.0484009f,85.0363235f,85.0242615f,85.0122147f,85.0001907f,84.9881744f,84.9761734f,84.9641953f,84.9522247f,84.9402695f,84.9283371f,84.9164124f,84.9045105f,84.8926163f,84.8807373f,84.8688812f,84.8570328f,84.8451996f,84.8333817f,84.8215866f,84.8097992f,84.798027f,84.7862701f,84.7745285f,84.7628021f,84.751091f,84.7393875f,84.7277069f,84.7160416f,84.7043839f,84.692749f,84.6811218f,84.6695099f,84.6579132f,84.6463394f,84.6347656f,84.6232147f,84.6116791f,84.6001511f,84.5886459f,84.5771484f,84.5656662f,84.5541992f,84.5427475f,84.531311f,84.5198822f,84.5084763f,84.4970779f,84.4856949f,84.4743271f,84.4629669f,84.4516296f,84.4403f,84.4289856f,84.4176865f,84.4064026f,84.3951263f,84.3838654f,84.3726196f,84.3613892f,84.350174f,84.3389664f,84.327774f,84.316597f,84.3054352f,84.294281f,84.2831421f,84.2720184f,84.26091f,84.2498093f,84.2387238f,84.2276535f,84.2165985f,84.2055511f,84.194519f,84.1835022f,84.172493f,84.161499f,84.1505203f,84.1395569f,84.1286011f,84.1176605f,84.1067352f,84.0958176f,84.0849152f,84.074028f,84.0631485f,84.0522842f,84.0414276f,84.0305939f,84.0197678f,84.0089493f,83.9981537f,83.9873657f,83.9765854f,83.9658203f,83.9550705f,83.9443359f,83.933609f,83.9228897f,83.9121933f,83.9015045f,83.8908234f,83.8801575f,83.8695068f,83.8588638f,83.8482361f,83.8376236f,83.8270187f,83.8164291f,83.8058472f,83.7952805f,83.7847214f,83.7741776f,83.763649f,83.7531281f,83.7426224f,83.7321243f,83.7216415f,83.711174f,83.7007065f,83.6902618f,83.6798248f,83.6694031f,83.658989f,83.6485825f,83.6381912f,83.6278152f,83.6174469f,83.6070938f,83.5967484f,83.5864182f,83.5760956f,83.5657883f,83.5554886f,83.5452042f,83.5349274f,83.5246658f,83.5144119f,83.5041733f,83.4939423f,83.4837265f,83.4735184f,83.4633255f,83.4531403f,83.4429626f,83.4328003f,83.4226532f,83.4125137f,83.4023819f,83.3922653f,83.3821564f,83.3720627f,83.3619766f,83.3519058f,83.3418427f,83.3317871f,83.3217468f,83.3117142f,83.3016968f,83.291687f,83.2816925f,83.2717056f,83.2617264f,83.2517624f,83.241806f,83.2318649f,83.2219315f,83.2120056f,83.202095f,83.1921921f,83.1823044f,83.1724243f,83.1625519f,83.1526947f,83.1428452f,83.1330109f,83.1231842f,83.1133652f,83.1035538f,83.0937576f,83.0839767f,83.0741959f,83.0644302f,83.0546799f,83.0449295f,83.0351944f,83.0254745f,83.0157547f,83.0060577f,82.9963608f,82.9866791f,82.977005f,82.9673386f,82.9576874f,82.9480438f,82.9384079f,82.9287872f,82.9191742f,82.9095688f,82.8999786f,82.8903961f,82.8808212f,82.871254f,82.861702f,82.8521576f,82.8426208f,82.8330994f,82.8235855f,82.8140793f,82.8045883f,82.795105f,82.7856293f,82.7761612f,82.7667007f,82.7572556f,82.747818f,82.7383957f,82.728981f,82.7195663f,82.7101746f,82.7007828f,82.6914062f,82.6820374f,82.6726761f,82.6633224f,82.6539841f,82.6446533f,82.6353302f,82.6260147f,82.6167145f,82.6074142f,82.5981293f,82.5888596f,82.5795898f,82.5703354f,82.5610886f,82.5518494f,82.5426178f,82.5334015f,82.5241852f,82.5149841f,82.5057983f,82.4966125f,82.4874344f,82.4782715f,82.4691162f,82.4599686f,82.4508286f,82.4417038f,82.4325867f,82.4234695f,82.4143677f,82.4052811f,82.3961945f,82.3871231f,82.3780518f,82.3689957f,82.3599472f,82.3509064f,82.3418808f,82.3328552f,82.3238449f,82.3148422f,82.3058472f,82.2968597f,82.2878799f}; +const static float fmydspSIG1Wave0[2001] = {249.987076f,249.986847f,249.986633f,249.986404f,249.986176f,249.985947f,249.985703f,249.985458f,249.985214f,249.98497f,249.984711f,249.984451f,249.984192f,249.983932f,249.983658f,249.983383f,249.983109f,249.982819f,249.982529f,249.982239f,249.981934f,249.981628f,249.981323f,249.981003f,249.980682f,249.980362f,249.980026f,249.979691f,249.979355f,249.979004f,249.978653f,249.978287f,249.977921f,249.977554f,249.977173f,249.976791f,249.976395f,249.975998f,249.975601f,249.975189f,249.974762f,249.97435f,249.973907f,249.97348f,249.973022f,249.972565f,249.972107f,249.971634f,249.971161f,249.970673f,249.970184f,249.969681f,249.969177f,249.968658f,249.968124f,249.96759f,249.967041f,249.966492f,249.965927f,249.965347f,249.964767f,249.964172f,249.963577f,249.962967f,249.962341f,249.961716f,249.96106f,249.960403f,249.959747f,249.959061f,249.958374f,249.957687f,249.95697f,249.956253f,249.955505f,249.954758f,249.95401f,249.953232f,249.952438f,249.951645f,249.950836f,249.950012f,249.949173f,249.948318f,249.947449f,249.946564f,249.945663f,249.944763f,249.943832f,249.942886f,249.941925f,249.940964f,249.939972f,249.938965f,249.937943f,249.93689f,249.935837f,249.934753f,249.93367f,249.932556f,249.931427f,249.930283f,249.929108f,249.927917f,249.926712f,249.925491f,249.92424f,249.922974f,249.921677f,249.920364f,249.919037f,249.917679f,249.916306f,249.914902f,249.913483f,249.912033f,249.910553f,249.909058f,249.907547f,249.905991f,249.904419f,249.902832f,249.901199f,249.899551f,249.897873f,249.896164f,249.89444f,249.89267f,249.890884f,249.889053f,249.887207f,249.88533f,249.883408f,249.88147f,249.879486f,249.877487f,249.875443f,249.873367f,249.871262f,249.86911f,249.866928f,249.864716f,249.862457f,249.860168f,249.857849f,249.855484f,249.853073f,249.850632f,249.848145f,249.845627f,249.843048f,249.840454f,249.837799f,249.835098f,249.832367f,249.829575f,249.826752f,249.823883f,249.820953f,249.817993f,249.814972f,249.811905f,249.808777f,249.805618f,249.802399f,249.799118f,249.795792f,249.792419f,249.788986f,249.785492f,249.781952f,249.778336f,249.774673f,249.77095f,249.767166f,249.763321f,249.759415f,249.755447f,249.751419f,249.747314f,249.743149f,249.738922f,249.734619f,249.730255f,249.725815f,249.721298f,249.716721f,249.712051f,249.707321f,249.702515f,249.697632f,249.692673f,249.687622f,249.682495f,249.677292f,249.671997f,249.666626f,249.661163f,249.655624f,249.649994f,249.644257f,249.638443f,249.632538f,249.626541f,249.620438f,249.614243f,249.607956f,249.601562f,249.595062f,249.58847f,249.581772f,249.574966f,249.568054f,249.561035f,249.553909f,249.546661f,249.539307f,249.53183f,249.524246f,249.516541f,249.508698f,249.500748f,249.492676f,249.484482f,249.476151f,249.467697f,249.459106f,249.450378f,249.441528f,249.432526f,249.423401f,249.414124f,249.404709f,249.395142f,249.385437f,249.375565f,249.365555f,249.355392f,249.345078f,249.334595f,249.323959f,249.313171f,249.3022f,249.291077f,249.27977f,249.268311f,249.256668f,249.244843f,249.232849f,249.220673f,249.208298f,249.195755f,249.183014f,249.17009f,249.156967f,249.143661f,249.130142f,249.116425f,249.102509f,249.088379f,249.074051f,249.059494f,249.044739f,249.029755f,249.014557f,248.99913f,248.98349f,248.967606f,248.951508f,248.935165f,248.918579f,248.901749f,248.884689f,248.867371f,248.849808f,248.832001f,248.813919f,248.795593f,248.776993f,248.758133f,248.739014f,248.719604f,248.699936f,248.679977f,248.659744f,248.639221f,248.618408f,248.597305f,248.575912f,248.554214f,248.532211f,248.509903f,248.487289f,248.464355f,248.441116f,248.417557f,248.393661f,248.369446f,248.344894f,248.320023f,248.2948f,248.269226f,248.243317f,248.217072f,248.19046f,248.163483f,248.136154f,248.108459f,248.080399f,248.051956f,248.023148f,247.993958f,247.964386f,247.934418f,247.904068f,247.873306f,247.842163f,247.810608f,247.778641f,247.746262f,247.71347f,247.680267f,247.646622f,247.612564f,247.578064f,247.543137f,247.507767f,247.471954f,247.435699f,247.398987f,247.361816f,247.324173f,247.286087f,247.247513f,247.208481f,247.168976f,247.128983f,247.088501f,247.047531f,247.006073f,246.964111f,246.921661f,246.878693f,246.83522f,246.791229f,246.746719f,246.701706f,246.656143f,246.610062f,246.563446f,246.516296f,246.468613f,246.420364f,246.371582f,246.322235f,246.272324f,246.221863f,246.170837f,246.119247f,246.067078f,246.014328f,245.960999f,245.907074f,245.85257f,245.79747f,245.741776f,245.685486f,245.628586f,245.571091f,245.51297f,245.454239f,245.394882f,245.334915f,245.274307f,245.213089f,245.151215f,245.088715f,245.025574f,244.961792f,244.897369f,244.832291f,244.766556f,244.70015f,244.633102f,244.565384f,244.496994f,244.427948f,244.358215f,244.287811f,244.216721f,244.144958f,244.07251f,243.999359f,243.925522f,243.850998f,243.775772f,243.699844f,243.623199f,243.545868f,243.467819f,243.389053f,243.309586f,243.229401f,243.148483f,243.066849f,242.984497f,242.901413f,242.817596f,242.733047f,242.647781f,242.561752f,242.475006f,242.387512f,242.299271f,242.210281f,242.12056f,242.030075f,241.938843f,241.846863f,241.75412f,241.660629f,241.566376f,241.471359f,241.37558f,241.279037f,241.181732f,241.083664f,240.984818f,240.885208f,240.784821f,240.68367f,240.581741f,240.479034f,240.375549f,240.271286f,240.166245f,240.060425f,239.953827f,239.846451f,239.738281f,239.629333f,239.519592f,239.409073f,239.29776f,239.185654f,239.072769f,238.959091f,238.84462f,238.72937f,238.613327f,238.496475f,238.378845f,238.260422f,238.141205f,238.021194f,237.900391f,237.778778f,237.656387f,237.533203f,237.40921f,237.284439f,237.158859f,237.032486f,236.905334f,236.777374f,236.648605f,236.519058f,236.388718f,236.257568f,236.125641f,235.992905f,235.859375f,235.725067f,235.589951f,235.454041f,235.317337f,235.179855f,235.041565f,234.902481f,234.762619f,234.621964f,234.480515f,234.338272f,234.195251f,234.051422f,233.90683f,233.761429f,233.61525f,233.468292f,233.320541f,233.172012f,233.022705f,232.872604f,232.721725f,232.570068f,232.417618f,232.264404f,232.110413f,231.955643f,231.800095f,231.643768f,231.486679f,231.328796f,231.170166f,231.010757f,230.850571f,230.689621f,230.527908f,230.365433f,230.202194f,230.038177f,229.873413f,229.707886f,229.541595f,229.374542f,229.206741f,229.038177f,228.868866f,228.698807f,228.527985f,228.356415f,228.184113f,228.011047f,227.837234f,227.662689f,227.487396f,227.311356f,227.134583f,226.957077f,226.778824f,226.599838f,226.42012f,226.23967f,226.058487f,225.876587f,225.693954f,225.51059f,225.326508f,225.141693f,224.956177f,224.769928f,224.582962f,224.395279f,224.206879f,224.017776f,223.827957f,223.637436f,223.446198f,223.254257f,223.061615f,222.868271f,222.674225f,222.479477f,222.284027f,222.087891f,221.891068f,221.693542f,221.495331f,221.296417f,221.096832f,220.896561f,220.695602f,220.493958f,220.291641f,220.088654f,219.884979f,219.680634f,219.475601f,219.269913f,219.063553f,218.856522f,218.648834f,218.440475f,218.231461f,218.02179f,217.811447f,217.600449f,217.388809f,217.176498f,216.963547f,216.749954f,216.535706f,216.320816f,216.10527f,215.889084f,215.672272f,215.454803f,215.23671f,215.017975f,214.798615f,214.578629f,214.358002f,214.136749f,213.914871f,213.692368f,213.469238f,213.245483f,213.021118f,212.796143f,212.570541f,212.34433f,212.117508f,211.890076f,211.662048f,211.433395f,211.204147f,210.974289f,210.743835f,210.512787f,210.281128f,210.048889f,209.81604f,209.582611f,209.348587f,209.113968f,208.878769f,208.64299f,208.406631f,208.169678f,207.932144f,207.694046f,207.455353f,207.216095f,206.976273f,206.73587f,206.494888f,206.253357f,206.011246f,205.768585f,205.525345f,205.281555f,205.037201f,204.792297f,204.546829f,204.300812f,204.054245f,203.807114f,203.559448f,203.311234f,203.062469f,202.813156f,202.563309f,202.312912f,202.061981f,201.810516f,201.558517f,201.305984f,201.052917f,200.799316f,200.545181f,200.290527f,200.035355f,199.779648f,199.523422f,199.266678f,199.009399f,198.751617f,198.493317f,198.234512f,197.975189f,197.715347f,197.455002f,197.194153f,196.932785f,196.670929f,196.408554f,196.145691f,195.882324f,195.618454f,195.354095f,195.089233f,194.823883f,194.558044f,194.291702f,194.024887f,193.757568f,193.489777f,193.221497f,192.952744f,192.683502f,192.413788f,192.143585f,191.87291f,191.601761f,191.330154f,191.05806f,190.785492f,190.512466f,190.238968f,189.965012f,189.690582f,189.415695f,189.14035f,188.864548f,188.588272f,188.311554f,188.034378f,187.75676f,187.478683f,187.20015f,186.921173f,186.641754f,186.361893f,186.081573f,185.800827f,185.519638f,185.238007f,184.955933f,184.673431f,184.390488f,184.107117f,183.823303f,183.539078f,183.25441f,182.96933f,182.683823f,182.397873f,182.111526f,181.824738f,181.537552f,181.249939f,180.961899f,180.673462f,180.384598f,180.095337f,179.805664f,179.515579f,179.225082f,178.934189f,178.642899f,178.351196f,178.059097f,177.766602f,177.473709f,177.18042f,176.886734f,176.592667f,176.298203f,176.003357f,175.708115f,175.412506f,175.116501f,174.820114f,174.523346f,174.226212f,173.928696f,173.630798f,173.332535f,173.033905f,172.734894f,172.435532f,172.135788f,171.835693f,171.535233f,171.234421f,170.933243f,170.631714f,170.329834f,170.027603f,169.725021f,169.422089f,169.11882f,168.815201f,168.511246f,168.20694f,167.902313f,167.597351f,167.292053f,166.98642f,166.680466f,166.374191f,166.067596f,165.760666f,165.45343f,165.145889f,164.838028f,164.529846f,164.221375f,163.912598f,163.603516f,163.294144f,162.984467f,162.6745f,162.364258f,162.053711f,161.742905f,161.431808f,161.120438f,160.808792f,160.496887f,160.184708f,159.872269f,159.559586f,159.246643f,158.933441f,158.62001f,158.306335f,157.992432f,157.678284f,157.363922f,157.049332f,156.734528f,156.41951f,156.104279f,155.788849f,155.473236f,155.157425f,154.841431f,154.525253f,154.208908f,153.89241f,153.575729f,153.258911f,152.941956f,152.624847f,152.307602f,151.99025f,151.672775f,151.355194f,151.037506f,150.719727f,150.401871f,150.083939f,149.765945f,149.447891f,149.129807f,148.811676f,148.493515f,148.175354f,147.857178f,147.539017f,147.220871f,146.902756f,146.584686f,146.266678f,145.94873f,145.630875f,145.313126f,144.995468f,144.677948f,144.360565f,144.043335f,143.726288f,143.409424f,143.092758f,142.776321f,142.460129f,142.14418f,141.828522f,141.513153f,141.19809f,140.883362f,140.569f,140.255005f,139.941391f,139.62822f,139.315475f,139.003189f,138.691391f,138.380096f,138.069336f,137.75914f,137.449524f,137.140503f,136.832123f,136.524384f,136.217346f,135.911011f,135.605408f,135.300583f,134.996536f,134.693329f,134.390945f,134.089462f,133.788864f,133.489212f,133.190521f,132.892822f,132.596146f,132.300522f,132.005981f,131.71254f,131.420258f,131.129135f,130.839203f,130.550507f,130.263062f,129.976898f,129.692062f,129.408569f,129.126434f,128.845703f,128.566391f,128.288528f,128.012161f,127.737282f,127.463936f,127.192139f,126.921921f,126.653305f,126.386307f,126.120956f,125.857269f,125.595276f,125.334976f,125.076408f,124.819572f,124.564499f,124.311203f,124.059692f,123.80999f,123.562096f,123.316032f,123.071815f,122.829437f,122.588921f,122.350273f,122.113503f,121.878609f,121.645607f,121.41449f,121.185272f,120.957947f,120.732521f,120.509003f,120.287376f,120.067642f,119.849815f,119.633881f,119.41983f,119.207672f,118.997398f,118.788994f,118.582458f,118.377792f,118.174973f,117.974007f,117.774872f,117.577568f,117.382088f,117.188408f,116.996521f,116.806427f,116.618103f,116.431541f,116.246719f,116.063644f,115.882278f,115.702629f,115.524666f,115.348389f,115.173775f,115.000801f,114.829475f,114.65976f,114.491646f,114.325127f,114.160179f,113.996788f,113.834938f,113.674614f,113.5158f,113.358482f,113.202637f,113.048248f,112.895317f,112.743805f,112.593712f,112.445007f,112.297691f,112.151741f,112.007141f,111.863869f,111.721916f,111.581268f,111.44191f,111.303818f,111.166985f,111.031387f,110.897018f,110.763863f,110.631905f,110.501122f,110.371506f,110.24305f,110.115723f,109.989525f,109.864433f,109.74044f,109.617531f,109.495689f,109.374901f,109.25515f,109.136436f,109.01873f,108.902031f,108.786324f,108.671593f,108.557831f,108.445015f,108.333145f,108.222206f,108.112183f,108.003059f,107.894836f,107.787498f,107.68103f,107.575424f,107.470673f,107.366753f,107.263672f,107.1614f,107.059944f,106.959282f,106.859413f,106.760315f,106.661995f,106.56443f,106.467621f,106.371544f,106.276207f,106.181587f,106.087685f,105.994492f,105.901985f,105.810173f,105.719048f,105.628586f,105.538788f,105.449646f,105.361153f,105.2733f,105.186073f,105.09948f,105.013504f,104.928131f,104.843369f,104.759193f,104.675613f,104.592613f,104.510193f,104.428337f,104.347038f,104.266304f,104.186111f,104.106461f,104.027351f,103.948769f,103.870712f,103.793175f,103.716148f,103.639633f,103.563614f,103.488091f,103.413055f,103.338509f,103.264442f,103.190842f,103.117714f,103.045052f,102.972847f,102.901093f,102.829788f,102.758926f,102.688507f,102.618515f,102.548958f,102.47982f,102.41111f,102.342812f,102.274925f,102.207443f,102.140366f,102.073692f,102.007408f,101.941513f,101.875999f,101.810875f,101.746132f,101.681755f,101.61776f,101.554123f,101.490852f,101.42794f,101.365379f,101.303177f,101.241325f,101.17981f,101.118645f,101.057816f,100.997322f,100.937157f,100.877327f,100.817818f,100.758629f,100.69976f,100.641212f,100.582977f,100.525047f,100.46743f,100.41011f,100.353096f,100.296379f,100.23996f,100.18383f,100.127991f,100.072441f,100.017174f,99.9621964f,99.9074936f,99.8530655f,99.798912f,99.7450333f,99.6914215f,99.6380768f,99.5849991f,99.5321808f,99.4796219f,99.4273224f,99.3752747f,99.3234863f,99.2719498f,99.2206573f,99.1696091f,99.1188049f,99.0682449f,99.0179291f,98.9678497f,98.9180069f,98.8683929f,98.8190155f,98.7698593f,98.7209396f,98.6722488f,98.6237717f,98.5755234f,98.5274963f,98.4796829f,98.4320908f,98.3847122f,98.3375473f,98.290596f,98.2438507f,98.1973114f,98.1509781f,98.1048508f,98.0589294f,98.0132065f,97.9676819f,97.9223633f,97.8772278f,97.8322983f,97.7875595f,97.7430115f,97.6986542f,97.65448f,97.6104965f,97.5667038f,97.5230865f,97.47966f,97.436409f,97.3933411f,97.3504562f,97.3077469f,97.2652054f,97.222847f,97.1806564f,97.1386414f,97.0967941f,97.0551224f,97.0136108f,96.9722748f,96.9310989f,96.8900833f,96.8492355f,96.808548f,96.7680283f,96.7276611f,96.6874542f,96.6473999f,96.6075058f,96.5677719f,96.528183f,96.4887543f,96.4494705f,96.4103394f,96.3713608f,96.3325272f,96.2938385f,96.2553024f,96.2169113f,96.1786575f,96.1405563f,96.1025925f,96.0647659f,96.0270844f,95.9895401f,95.9521408f,95.9148712f,95.877739f,95.840744f,95.8038864f,95.7671585f,95.7305603f,95.6940994f,95.6577682f,95.6215668f,95.585495f,95.5495529f,95.5137329f,95.4780426f,95.442482f,95.4070435f,95.371727f,95.3365326f,95.3014603f,95.2665176f,95.2316895f,95.1969833f,95.1623993f,95.1279297f,95.0935745f,95.0593414f,95.0252228f,94.9912262f,94.9573364f,94.9235611f,94.8899002f,94.8563538f,94.8229218f,94.7895966f,94.7563858f,94.7232819f,94.6902847f,94.657402f,94.6246185f,94.5919495f,94.5593872f,94.5269241f,94.4945755f,94.462326f,94.4301758f,94.39814f,94.3661957f,94.3343582f,94.3026276f,94.2709885f,94.2394562f,94.2080154f,94.1766815f,94.1454468f,94.1143036f,94.0832596f,94.0523148f,94.0214615f,93.9907074f,93.9600525f,93.9294815f,93.8990097f,93.8686371f,93.8383484f,93.8081589f,93.7780533f,93.7480469f,93.7181244f,93.6882935f,93.6585541f,93.6289062f,93.5993423f,93.5698624f,93.5404816f,93.5111771f,93.4819641f,93.4528427f,93.4237976f,93.3948441f,93.3659668f,93.3371811f,93.3084793f,93.2798615f,93.2513199f,93.2228622f,93.1944885f,93.1661987f,93.1379929f,93.1098557f,93.08181f,93.0538406f,93.0259476f,92.9981384f,92.9703979f,92.9427414f,92.9151688f,92.8876648f,92.8602448f,92.8328934f,92.8056183f,92.7784271f,92.7513046f,92.7242584f,92.6972809f,92.6703873f,92.6435623f,92.616806f,92.590126f,92.5635223f,92.5369873f,92.5105286f,92.4841309f,92.4578171f,92.4315643f,92.4053879f,92.3792725f,92.3532333f,92.3272629f,92.3013611f,92.275528f,92.2497635f,92.2240601f,92.1984329f,92.1728668f,92.1473694f,92.1219406f,92.0965729f,92.0712738f,92.0460434f,92.020874f,91.9957733f,91.9707336f,91.9457626f,91.920845f,91.8960037f,91.8712158f,91.8464966f,91.8218384f,91.7972412f,91.7727051f,91.74823f,91.7238235f,91.6994705f,91.6751862f,91.6509552f,91.6267853f,91.6026764f,91.5786285f,91.5546341f,91.5307083f,91.5068359f,91.4830246f,91.4592667f,91.4355698f,91.4119263f,91.3883514f,91.3648224f,91.3413544f,91.3179474f,91.2945862f,91.2712936f,91.2480469f,91.2248611f,91.2017288f,91.1786499f,91.155632f,91.1326599f,91.1097488f,91.0868912f,91.0640869f,91.0413361f,91.0186386f,90.9959946f,90.9733963f,90.9508591f,90.9283752f,90.9059372f,90.8835526f,90.8612213f,90.8389435f,90.8167191f,90.7945404f,90.7724152f,90.7503357f,90.7283096f,90.706337f,90.6844101f,90.6625366f,90.6407089f,90.618927f,90.5971985f,90.5755234f,90.553894f,90.5323105f,90.5107727f,90.4892883f,90.4678497f,90.4464645f,90.4251175f,90.4038239f,90.382576f,90.3613739f,90.3402176f,90.3191071f,90.2980423f,90.2770233f,90.2560577f,90.2351303f,90.2142487f,90.1934128f,90.1726227f,90.1518784f,90.1311798f,90.1105194f,90.0899124f,90.0693436f,90.0488205f,90.0283432f,90.0079041f,89.9875107f,89.9671631f,89.9468536f,89.92659f,89.9063721f,89.8861923f,89.8660583f,89.8459625f,89.8259125f,89.8059006f,89.7859344f,89.7660065f,89.7461166f,89.7262726f,89.7064667f,89.6867065f,89.6669846f,89.6473007f,89.6276627f,89.6080627f,89.588501f,89.5689774f,89.5494995f,89.5300598f,89.5106583f,89.4912949f,89.4719696f,89.4526825f,89.4334335f,89.4142303f,89.3950577f,89.3759308f,89.3568344f,89.3377838f,89.3187637f,89.2997894f,89.2808456f,89.26194f,89.2430725f,89.2242432f,89.205452f,89.1866989f,89.1679764f,89.1492996f,89.1306534f,89.1120453f,89.0934677f,89.0749359f,89.0564346f,89.0379639f,89.0195389f,89.0011444f,88.9827881f,88.9644623f,88.9461746f,88.9279251f,88.9097061f,88.8915176f,88.8733673f,88.8552551f,88.8371735f,88.8191299f,88.8011169f,88.7831421f,88.7651978f,88.7472916f,88.7294083f,88.7115707f,88.6937561f,88.6759796f,88.6582413f,88.6405258f,88.6228485f,88.6052094f,88.5875931f,88.570015f,88.5524673f,88.5349579f,88.5174713f,88.5000229f,88.482605f,88.4652176f,88.4478607f,88.430542f,88.4132462f,88.3959885f,88.3787613f,88.3615646f,88.3443985f,88.3272629f,88.3101578f,88.2930832f,88.2760391f,88.2590256f,88.2420425f,88.22509f,88.208168f,88.1912766f,88.1744156f,88.1575851f,88.1407776f,88.1240082f,88.1072693f,88.0905533f,88.0738678f,88.0572128f,88.0405884f,88.0239944f,88.0074234f,87.9908829f,87.9743805f,87.9578934f,87.9414444f,87.9250183f,87.9086227f,87.8922577f,87.8759155f,87.8596115f,87.8433228f,87.8270721f,87.8108444f,87.7946472f,87.7784729f,87.7623291f,87.7462158f,87.7301254f,87.7140656f,87.6980286f,87.6820221f,87.6660385f,87.6500854f,87.6341629f,87.6182632f,87.6023865f,87.5865402f,87.5707245f,87.5549316f,87.5391617f,87.5234222f,87.5077057f,87.4920197f,87.4763565f,87.4607162f,87.4451065f,87.4295197f,87.4139633f,87.3984299f,87.3829193f,87.3674393f,87.3519821f,87.3365479f,87.3211365f,87.3057556f,87.2903976f,87.2750702f,87.259758f,87.2444763f,87.2292175f,87.2139893f,87.1987762f,87.1835938f,87.1684341f,87.1532974f,87.1381912f,87.1231003f,87.1080399f,87.0930023f,87.0779877f,87.0629959f,87.048027f,87.0330811f,87.0181656f,87.0032654f,86.9883957f,86.9735413f,86.9587173f,86.9439163f,86.9291382f,86.9143753f,86.8996429f,86.8849335f,86.8702469f,86.8555832f,86.8409424f,86.8263245f,86.8117218f,86.7971497f,86.7826004f,86.768074f,86.7535629f,86.7390823f,86.724617f,86.7101822f,86.6957626f,86.681366f,86.6669922f,86.6526413f,86.6383133f,86.6240005f,86.6097183f,86.5954514f,86.5812073f,86.5669861f,86.5527878f,86.5386124f,86.5244522f,86.5103149f,86.4962006f,86.4821091f,86.4680328f,86.4539871f,86.4399567f,86.4259415f,86.4119568f,86.3979874f,86.3840408f,86.3701172f,86.3562088f,86.3423233f,86.3284607f,86.3146133f,86.3007889f,86.2869873f,86.273201f,86.2594376f,86.245697f,86.2319717f,86.2182693f,86.2045898f,86.1909256f,86.1772842f,86.1636581f,86.1500549f,86.1364746f,86.1229095f,86.1093597f,86.0958405f,86.0823288f,86.0688477f,86.0553818f,86.0419312f,86.0285034f,86.0150909f,86.0017014f,85.9883347f,85.9749832f,85.961647f,85.9483337f,85.9350433f,85.9217606f,85.9085083f,85.8952637f,85.8820496f,85.8688431f,85.8556595f,85.8424988f,85.8293533f,85.8162231f,85.8031158f,85.7900238f,85.7769547f,85.7639008f,85.7508621f,85.7378464f,85.7248459f,85.7118607f,85.6988983f,85.6859512f,85.673027f,85.6601181f,85.6472244f,85.634346f,85.6214905f,85.6086502f,85.5958328f,85.5830231f,85.5702362f,85.5574722f,85.5447159f,85.5319824f,85.5192642f,85.5065689f,85.4938812f,85.4812164f,85.4685669f,85.4559402f,85.4433212f,85.4307251f,85.4181442f,85.4055786f,85.3930359f,85.3805084f,85.3679886f,85.3554993f,85.3430176f,85.3305511f,85.3181076f,85.3056793f,85.2932663f,85.2808685f,85.268486f,85.2561188f,85.2437744f,85.2314453f,85.2191315f,85.2068329f,85.1945496f,85.1822815f,85.1700287f,85.1577911f,85.1455765f,85.1333771f,85.1211853f,85.1090164f,85.0968628f,85.0847244f,85.0726013f,85.0604935f,85.0484009f,85.0363235f,85.0242615f,85.0122147f,85.0001907f,84.9881744f,84.9761734f,84.9641953f,84.9522247f,84.9402695f,84.9283371f,84.9164124f,84.9045105f,84.8926163f,84.8807373f,84.8688812f,84.8570328f,84.8451996f,84.8333817f,84.8215866f,84.8097992f,84.798027f,84.7862701f,84.7745285f,84.7628021f,84.751091f,84.7393875f,84.7277069f,84.7160416f,84.7043839f,84.692749f,84.6811218f,84.6695099f,84.6579132f,84.6463394f,84.6347656f,84.6232147f,84.6116791f,84.6001511f,84.5886459f,84.5771484f,84.5656662f,84.5541992f,84.5427475f,84.531311f,84.5198822f,84.5084763f,84.4970779f,84.4856949f,84.4743271f,84.4629669f,84.4516296f,84.4403f,84.4289856f,84.4176865f,84.4064026f,84.3951263f,84.3838654f,84.3726196f,84.3613892f,84.350174f,84.3389664f,84.327774f,84.316597f,84.3054352f,84.294281f,84.2831421f,84.2720184f,84.26091f,84.2498093f,84.2387238f,84.2276535f,84.2165985f,84.2055511f,84.194519f,84.1835022f,84.172493f,84.161499f,84.1505203f,84.1395569f,84.1286011f,84.1176605f,84.1067352f,84.0958176f,84.0849152f,84.074028f,84.0631485f,84.0522842f,84.0414276f,84.0305939f,84.0197678f,84.0089493f,83.9981537f,83.9873657f,83.9765854f,83.9658203f,83.9550705f,83.9443359f,83.933609f,83.9228897f,83.9121933f,83.9015045f,83.8908234f,83.8801575f,83.8695068f,83.8588638f,83.8482361f,83.8376236f,83.8270187f,83.8164291f,83.8058472f,83.7952805f,83.7847214f,83.7741776f,83.763649f,83.7531281f,83.7426224f,83.7321243f,83.7216415f,83.711174f,83.7007065f,83.6902618f,83.6798248f,83.6694031f,83.658989f,83.6485825f,83.6381912f,83.6278152f,83.6174469f,83.6070938f,83.5967484f,83.5864182f,83.5760956f,83.5657883f,83.5554886f,83.5452042f,83.5349274f,83.5246658f,83.5144119f,83.5041733f,83.4939423f,83.4837265f,83.4735184f,83.4633255f,83.4531403f,83.4429626f,83.4328003f,83.4226532f,83.4125137f,83.4023819f,83.3922653f,83.3821564f,83.3720627f,83.3619766f,83.3519058f,83.3418427f,83.3317871f,83.3217468f,83.3117142f,83.3016968f,83.291687f,83.2816925f,83.2717056f,83.2617264f,83.2517624f,83.241806f,83.2318649f,83.2219315f,83.2120056f,83.202095f,83.1921921f,83.1823044f,83.1724243f,83.1625519f,83.1526947f,83.1428452f,83.1330109f,83.1231842f,83.1133652f,83.1035538f,83.0937576f,83.0839767f,83.0741959f,83.0644302f,83.0546799f,83.0449295f,83.0351944f,83.0254745f,83.0157547f,83.0060577f,82.9963608f,82.9866791f,82.977005f,82.9673386f,82.9576874f,82.9480438f,82.9384079f,82.9287872f,82.9191742f,82.9095688f,82.8999786f,82.8903961f,82.8808212f,82.871254f,82.861702f,82.8521576f,82.8426208f,82.8330994f,82.8235855f,82.8140793f,82.8045883f,82.795105f,82.7856293f,82.7761612f,82.7667007f,82.7572556f,82.747818f,82.7383957f,82.728981f,82.7195663f,82.7101746f,82.7007828f,82.6914062f,82.6820374f,82.6726761f,82.6633224f,82.6539841f,82.6446533f,82.6353302f,82.6260147f,82.6167145f,82.6074142f,82.5981293f,82.5888596f,82.5795898f,82.5703354f,82.5610886f,82.5518494f,82.5426178f,82.5334015f,82.5241852f,82.5149841f,82.5057983f,82.4966125f,82.4874344f,82.4782715f,82.4691162f,82.4599686f,82.4508286f,82.4417038f,82.4325867f,82.4234695f,82.4143677f,82.4052811f,82.3961945f,82.3871231f,82.3780518f,82.3689957f,82.3599472f,82.3509064f,82.3418808f,82.3328552f,82.3238449f,82.3148422f,82.3058472f,82.2968597f,82.2878799f}; class mydspSIG1 { private: @@ -99,7 +99,7 @@ class mydspSIG1 { static mydspSIG1* newmydspSIG1() { return (mydspSIG1*)new mydspSIG1(); } static void deletemydspSIG1(mydspSIG1* dsp) { delete dsp; } -static const float fmydspSIG2Wave0[100] = {0.0f,-0.0296990145f,-0.0599780679f,-0.0908231661f,-0.122163236f,-0.15376009f,-0.184938014f,-0.214177266f,-0.239335433f,-0.259232581f,-0.274433911f,-0.286183298f,-0.295538545f,-0.303222328f,-0.309706241f,-0.315301329f,-0.320218444f,-0.324604988f,-0.328567117f,-0.332183361f,-0.335513115f,-0.338602364f,-0.341487259f,-0.344196707f,-0.346754223f,-0.349179149f,-0.351487488f,-0.35369277f,-0.35580641f,-0.357838273f,-0.359796762f,-0.36168924f,-0.363522142f,-0.365301102f,-0.367031157f,-0.368716747f,-0.370361924f,-0.371970236f,-0.373544991f,-0.375089139f,-0.376605392f,-0.378096253f,-0.379564017f,-0.38101083f,-0.38243863f,-0.383849323f,-0.385244638f,-0.386626214f,-0.38799566f,-0.389354438f,-0.390703976f,-0.392045677f,-0.393380851f,-0.394710839f,-0.396036893f,-0.397360265f,-0.398682207f,-0.40000397f,-0.401326776f,-0.402651846f,-0.403980494f,-0.405313969f,-0.406653613f,-0.408000737f,-0.409356743f,-0.41072312f,-0.412101358f,-0.413493067f,-0.414899886f,-0.416323662f,-0.417766303f,-0.419229805f,-0.420716405f,-0.422228485f,-0.42376864f,-0.425339758f,-0.426944911f,-0.428587586f,-0.430271626f,-0.432001382f,-0.433781654f,-0.435617924f,-0.437516481f,-0.439484537f,-0.441530377f,-0.443663776f,-0.445896149f,-0.448241174f,-0.450715303f,-0.453338623f,-0.456136048f,-0.45913893f,-0.462387681f,-0.465935349f,-0.469853997f,-0.474244624f,-0.479255259f,-0.485115886f,-0.492212713f,-0.501272738f}; +const static float fmydspSIG2Wave0[100] = {0.0f,-0.0296990145f,-0.0599780679f,-0.0908231661f,-0.122163236f,-0.15376009f,-0.184938014f,-0.214177266f,-0.239335433f,-0.259232581f,-0.274433911f,-0.286183298f,-0.295538545f,-0.303222328f,-0.309706241f,-0.315301329f,-0.320218444f,-0.324604988f,-0.328567117f,-0.332183361f,-0.335513115f,-0.338602364f,-0.341487259f,-0.344196707f,-0.346754223f,-0.349179149f,-0.351487488f,-0.35369277f,-0.35580641f,-0.357838273f,-0.359796762f,-0.36168924f,-0.363522142f,-0.365301102f,-0.367031157f,-0.368716747f,-0.370361924f,-0.371970236f,-0.373544991f,-0.375089139f,-0.376605392f,-0.378096253f,-0.379564017f,-0.38101083f,-0.38243863f,-0.383849323f,-0.385244638f,-0.386626214f,-0.38799566f,-0.389354438f,-0.390703976f,-0.392045677f,-0.393380851f,-0.394710839f,-0.396036893f,-0.397360265f,-0.398682207f,-0.40000397f,-0.401326776f,-0.402651846f,-0.403980494f,-0.405313969f,-0.406653613f,-0.408000737f,-0.409356743f,-0.41072312f,-0.412101358f,-0.413493067f,-0.414899886f,-0.416323662f,-0.417766303f,-0.419229805f,-0.420716405f,-0.422228485f,-0.42376864f,-0.425339758f,-0.426944911f,-0.428587586f,-0.430271626f,-0.432001382f,-0.433781654f,-0.435617924f,-0.437516481f,-0.439484537f,-0.441530377f,-0.443663776f,-0.445896149f,-0.448241174f,-0.450715303f,-0.453338623f,-0.456136048f,-0.45913893f,-0.462387681f,-0.465935349f,-0.469853997f,-0.474244624f,-0.479255259f,-0.485115886f,-0.492212713f,-0.501272738f}; class mydspSIG2 { private: diff --git a/examples/examples-dsp/examples-faust/streams-i2s-faust_guitarix-i2s/streams-i2s-faust_guitarix-i2s.ino b/examples/examples-faust/streams-i2s-faust_guitarix-i2s/streams-i2s-faust_guitarix-i2s.ino similarity index 83% rename from examples/examples-dsp/examples-faust/streams-i2s-faust_guitarix-i2s/streams-i2s-faust_guitarix-i2s.ino rename to examples/examples-faust/streams-i2s-faust_guitarix-i2s/streams-i2s-faust_guitarix-i2s.ino index 25693c4cd7..746a22579a 100644 --- a/examples/examples-dsp/examples-faust/streams-i2s-faust_guitarix-i2s/streams-i2s-faust_guitarix-i2s.ino +++ b/examples/examples-faust/streams-i2s-faust_guitarix-i2s/streams-i2s-faust_guitarix-i2s.ino @@ -10,11 +10,11 @@ */ #include "AudioTools.h" -#include "AudioTools/AudioLibs/AudioBoardStream.h" -#include "AudioTools/AudioLibs/AudioFaust.h" +#include "AudioLibs/AudioKit.h" +#include "AudioLibs/AudioFaust.h" #include "guitarix.h" -AudioBoardStream kit(AudioKitEs8388V1); +AudioKitStream kit; FaustStream faust(kit); // final output of Faust is kit StreamCopy copier(faust, kit); // copy data from kit to faust @@ -22,7 +22,7 @@ StreamCopy copier(faust, kit); // copy data from kit to faust void setup(void) { // Open Serial Serial.begin(115200); - AudioToolsLogger.begin(Serial, AudioToolsLogLevel::Warning); + AudioLogger::instance().begin(Serial, AudioLogger::Warning); // Setup Faust auto cfg = faust.defaultConfig(); @@ -48,7 +48,7 @@ void setup(void) { cfg_i2s.sample_rate = cfg.sample_rate; cfg_i2s.channels = cfg.channels; cfg_i2s.bits_per_sample = cfg.bits_per_sample; - cfg_i2s.input_device = ADC_INPUT_LINE1; + cfg_i2s.input_device = AUDIO_HAL_ADC_INPUT_LINE1; kit.begin(cfg_i2s); } diff --git a/examples/examples-dsp/examples-maximilian/01-TestTone/01-TestTone.ino b/examples/examples-maximilian/01-TestTone/01-TestTone.ino similarity index 84% rename from examples/examples-dsp/examples-maximilian/01-TestTone/01-TestTone.ino rename to examples/examples-maximilian/01-TestTone/01-TestTone.ino index 6de7092468..63a1686207 100644 --- a/examples/examples-dsp/examples-maximilian/01-TestTone/01-TestTone.ino +++ b/examples/examples-maximilian/01-TestTone/01-TestTone.ino @@ -1,5 +1,5 @@ #include "AudioTools.h" -#include "AudioTools/AudioLibs/MaximilianDSP.h" +#include "AudioLibs/MaximilianDSP.h" // Define output I2SStream out; @@ -11,7 +11,7 @@ maxiOsc mySine;//One oscillator - can be called anything. Can be any of the avai void setup() {//some inits // setup logging Serial.begin(115200); - AudioToolsLogger.begin(Serial, AudioToolsLogLevel::Info); + AudioLogger::instance().begin(Serial, AudioLogger::Info); // setup Aduio output auto cfg = out.defaultConfig(TX_MODE); out.begin(cfg); diff --git a/examples/examples-dsp/examples-maximilian/02-TwoTones/02-TwoTones.ino b/examples/examples-maximilian/02-TwoTones/02-TwoTones.ino similarity index 93% rename from examples/examples-dsp/examples-maximilian/02-TwoTones/02-TwoTones.ino rename to examples/examples-maximilian/02-TwoTones/02-TwoTones.ino index 89163f4cc8..d7d5864d8e 100755 --- a/examples/examples-dsp/examples-maximilian/02-TwoTones/02-TwoTones.ino +++ b/examples/examples-maximilian/02-TwoTones/02-TwoTones.ino @@ -1,7 +1,7 @@ //This examples shows another fundamental building block of digital audio - adding two sine waves together. When you add waves together they create a new wave whose amplitude at any time is computed by adding the current amplitudes of each wave together. So, if one wave has an amplitude of 1, and the other has an amplitude of 1, the new wave will be equal to 2 at that point in time. Whereas, later, if one wave has an amplitude of -1, and the other has an amplitude of 1, the new wave - the one you hear - will equal 0. This can create some interesting effects, including 'beating', when the waves interact to create a single wave that fades up and down based on the frequencies of the two interacting waves. The frequency of the 'beating' i.e. the fading in and out, is equal to the difference in frequency between the two waves. #include "AudioTools.h" -#include "AudioTools/AudioLibs/MaximilianDSP.h" +#include "AudioLibs/MaximilianDSP.h" // Define output I2SStream out; @@ -12,7 +12,7 @@ maxiOsc mySine,myOtherSine;//Two oscillators with names. void setup() {//some inits // setup logging Serial.begin(115200); - AudioToolsLogger.begin(Serial, AudioToolsLogLevel::Info); + AudioLogger::instance().begin(Serial, AudioLogger::Info); // setup Aduio output auto cfg = out.defaultConfig(TX_MODE); diff --git a/examples/examples-dsp/examples-maximilian/03-AM1/03-AM1.ino b/examples/examples-maximilian/03-AM1/03-AM1.ino similarity index 92% rename from examples/examples-dsp/examples-maximilian/03-AM1/03-AM1.ino rename to examples/examples-maximilian/03-AM1/03-AM1.ino index f23e520e11..2793a03573 100755 --- a/examples/examples-dsp/examples-maximilian/03-AM1/03-AM1.ino +++ b/examples/examples-maximilian/03-AM1/03-AM1.ino @@ -1,5 +1,5 @@ #include "AudioTools.h" -#include "AudioTools/AudioLibs/MaximilianDSP.h" +#include "AudioLibs/MaximilianDSP.h" // Define Arduino output I2SStream out; @@ -12,7 +12,7 @@ maxiOsc mySine,myOtherSine;//Two oscillators. They can be called anything. They void setup() {//some inits // setup logging Serial.begin(115200); - AudioToolsLogger.begin(Serial, AudioToolsLogLevel::Info); + AudioLogger::instance().begin(Serial, AudioLogger::Info); // setup Aduio output auto cfg = out.defaultConfig(TX_MODE); diff --git a/examples/examples-dsp/examples-maximilian/04-AM2/04-AM2.ino b/examples/examples-maximilian/04-AM2/04-AM2.ino similarity index 92% rename from examples/examples-dsp/examples-maximilian/04-AM2/04-AM2.ino rename to examples/examples-maximilian/04-AM2/04-AM2.ino index 8347317dc8..bf421321f7 100755 --- a/examples/examples-dsp/examples-maximilian/04-AM2/04-AM2.ino +++ b/examples/examples-maximilian/04-AM2/04-AM2.ino @@ -1,5 +1,5 @@ #include "AudioTools.h" -#include "AudioTools/AudioLibs/MaximilianDSP.h" +#include "AudioLibs/MaximilianDSP.h" // Define Arduino output I2SStream out; @@ -15,7 +15,7 @@ maxiOsc mySine,myOtherSine,myPhasor;//Three oscillators. They can be called anyt void setup() {//some inits // setup logging Serial.begin(115200); - AudioToolsLogger.begin(Serial, AudioToolsLogLevel::Info); + AudioLogger::instance().begin(Serial, AudioLogger::Info); // setup Aduio output auto cfg = out.defaultConfig(TX_MODE); diff --git a/examples/examples-dsp/examples-maximilian/05-FM1/05-FM1.ino b/examples/examples-maximilian/05-FM1/05-FM1.ino similarity index 95% rename from examples/examples-dsp/examples-maximilian/05-FM1/05-FM1.ino rename to examples/examples-maximilian/05-FM1/05-FM1.ino index 5348920f8d..afef75cb10 100755 --- a/examples/examples-dsp/examples-maximilian/05-FM1/05-FM1.ino +++ b/examples/examples-maximilian/05-FM1/05-FM1.ino @@ -7,7 +7,7 @@ // This has some interesting effects. #include "AudioTools.h" -#include "AudioTools/AudioLibs/MaximilianDSP.h" +#include "AudioLibs/MaximilianDSP.h" // Define Arduino output I2SStream out; @@ -19,7 +19,7 @@ maxiOsc mySine,myOtherSine;//Two oscillators void setup() {//some inits // setup logging Serial.begin(115200); - AudioToolsLogger.begin(Serial, AudioToolsLogLevel::Info); + AudioLogger::instance().begin(Serial, AudioLogger::Info); // setup Aduio output auto cfg = out.defaultConfig(TX_MODE); diff --git a/examples/examples-dsp/examples-maximilian/06-FM2/06-FM2.ino b/examples/examples-maximilian/06-FM2/06-FM2.ino similarity index 85% rename from examples/examples-dsp/examples-maximilian/06-FM2/06-FM2.ino rename to examples/examples-maximilian/06-FM2/06-FM2.ino index 1ddfaa3032..339fbd22b3 100755 --- a/examples/examples-dsp/examples-maximilian/06-FM2/06-FM2.ino +++ b/examples/examples-maximilian/06-FM2/06-FM2.ino @@ -1,7 +1,7 @@ // Nothing much to say about this other than I like it. #include "AudioTools.h" -#include "AudioTools/AudioLibs/MaximilianDSP.h" +#include "AudioLibs/MaximilianDSP.h" // Define Arduino output I2SStream out; @@ -13,7 +13,7 @@ maxiOsc mySine,myOtherSine,myLastSine,myPhasor;//Three oscillators void setup() {//some inits // setup logging Serial.begin(115200); - AudioToolsLogger.begin(Serial, AudioToolsLogLevel::Info); + AudioLogger::instance().begin(Serial, AudioLogger::Info); // setup Aduio output auto cfg = out.defaultConfig(TX_MODE); diff --git a/examples/examples-dsp/examples-maximilian/07-Counting1/07-Counting1.ino b/examples/examples-maximilian/07-Counting1/07-Counting1.ino similarity index 92% rename from examples/examples-dsp/examples-maximilian/07-Counting1/07-Counting1.ino rename to examples/examples-maximilian/07-Counting1/07-Counting1.ino index 9a3b234236..c4dbf212c5 100755 --- a/examples/examples-dsp/examples-maximilian/07-Counting1/07-Counting1.ino +++ b/examples/examples-maximilian/07-Counting1/07-Counting1.ino @@ -1,6 +1,6 @@ #include "AudioTools.h" -#include "AudioTools/AudioLibs/MaximilianDSP.h" +#include "AudioLibs/MaximilianDSP.h" // Define Arduino output I2SStream out; @@ -14,7 +14,7 @@ double freq; // This is a variable that we will use to hold and set the current void setup() { // setup logging Serial.begin(115200); - AudioToolsLogger.begin(Serial, AudioToolsLogLevel::Info); + AudioLogger::instance().begin(Serial, AudioLogger::Info); // setup audio output auto cfg = out.defaultConfig(TX_MODE); diff --git a/examples/examples-dsp/examples-maximilian/08-Counting2/08-Counting2.ino b/examples/examples-maximilian/08-Counting2/08-Counting2.ino similarity index 94% rename from examples/examples-dsp/examples-maximilian/08-Counting2/08-Counting2.ino rename to examples/examples-maximilian/08-Counting2/08-Counting2.ino index 6fe43d1e33..84fbd8e795 100755 --- a/examples/examples-dsp/examples-maximilian/08-Counting2/08-Counting2.ino +++ b/examples/examples-maximilian/08-Counting2/08-Counting2.ino @@ -6,7 +6,7 @@ // This creates a bunch of steps. #include "AudioTools.h" -#include "AudioTools/AudioLibs/MaximilianDSP.h" +#include "AudioLibs/MaximilianDSP.h" // Define Arduino output I2SStream out; @@ -20,7 +20,7 @@ int CurrentCount;//we're going to put the current count in this variable so that void setup() {//some inits // setup logging Serial.begin(115200); - AudioToolsLogger.begin(Serial, AudioToolsLogLevel::Info); + AudioLogger::instance().begin(Serial, AudioLogger::Info); // setup audio output auto cfg = out.defaultConfig(TX_MODE); diff --git a/examples/examples-dsp/examples-maximilian/08-Counting3/08-Counting3.ino b/examples/examples-maximilian/08-Counting3/08-Counting3.ino similarity index 91% rename from examples/examples-dsp/examples-maximilian/08-Counting3/08-Counting3.ino rename to examples/examples-maximilian/08-Counting3/08-Counting3.ino index aae0943cda..79c2067903 100755 --- a/examples/examples-dsp/examples-maximilian/08-Counting3/08-Counting3.ino +++ b/examples/examples-maximilian/08-Counting3/08-Counting3.ino @@ -1,5 +1,5 @@ #include "AudioTools.h" -#include "AudioTools/AudioLibs/MaximilianDSP.h" +#include "AudioLibs/MaximilianDSP.h" // Define Arduino output I2SStream out; @@ -13,7 +13,7 @@ double myOscOutput;//we're going to stick the output here to make it easier to m void setup() {//some inits // setup logging Serial.begin(115200); - AudioToolsLogger.begin(Serial, AudioToolsLogLevel::Info); + AudioLogger::instance().begin(Serial, AudioLogger::Info); // setup audio output auto cfg = out.defaultConfig(TX_MODE); diff --git a/examples/examples-dsp/examples-maximilian/08-Counting4/08-Counting4.ino b/examples/examples-maximilian/08-Counting4/08-Counting4.ino similarity index 92% rename from examples/examples-dsp/examples-maximilian/08-Counting4/08-Counting4.ino rename to examples/examples-maximilian/08-Counting4/08-Counting4.ino index 2cbc7c05fb..a0261ad975 100755 --- a/examples/examples-dsp/examples-maximilian/08-Counting4/08-Counting4.ino +++ b/examples/examples-maximilian/08-Counting4/08-Counting4.ino @@ -1,5 +1,5 @@ #include "AudioTools.h" -#include "AudioTools/AudioLibs/MaximilianDSP.h" +#include "AudioLibs/MaximilianDSP.h" // Define Arduino output I2SStream out; @@ -14,7 +14,7 @@ int myArray[10]={100,200,300,400,300,200,100,240,640,360}; void setup() {//some inits // setup logging Serial.begin(115200); - AudioToolsLogger.begin(Serial, AudioToolsLogLevel::Info); + AudioLogger::instance().begin(Serial, AudioLogger::Info); // setup audio output auto cfg = out.defaultConfig(TX_MODE); diff --git a/examples/examples-dsp/examples-maximilian/09-Envelopes/09-Envelopes.ino b/examples/examples-maximilian/09-Envelopes/09-Envelopes.ino similarity index 94% rename from examples/examples-dsp/examples-maximilian/09-Envelopes/09-Envelopes.ino rename to examples/examples-maximilian/09-Envelopes/09-Envelopes.ino index 6760bc16c2..ab82da9f2d 100755 --- a/examples/examples-dsp/examples-maximilian/09-Envelopes/09-Envelopes.ino +++ b/examples/examples-maximilian/09-Envelopes/09-Envelopes.ino @@ -5,7 +5,7 @@ // Release: This is how long it takes to fade out. #include "AudioTools.h" -#include "AudioTools/AudioLibs/MaximilianDSP.h" +#include "AudioLibs/MaximilianDSP.h" // Define Arduino output I2SStream out; @@ -21,7 +21,7 @@ maxiEnv myEnvelope; void setup() {//some inits // setup logging Serial.begin(115200); - AudioToolsLogger.begin(Serial, AudioToolsLogLevel::Info); + AudioLogger::instance().begin(Serial, AudioLogger::Info); // setup audio output auto cfg = out.defaultConfig(TX_MODE); diff --git a/examples/examples-dsp/examples-maximilian/10-Filters/10-Filters.ino b/examples/examples-maximilian/10-Filters/10-Filters.ino similarity index 94% rename from examples/examples-dsp/examples-maximilian/10-Filters/10-Filters.ino rename to examples/examples-maximilian/10-Filters/10-Filters.ino index 5bb8abe5c8..9176e26d58 100755 --- a/examples/examples-dsp/examples-maximilian/10-Filters/10-Filters.ino +++ b/examples/examples-maximilian/10-Filters/10-Filters.ino @@ -4,7 +4,7 @@ #include "AudioTools.h" -#include "AudioTools/AudioLibs/MaximilianDSP.h" +#include "AudioLibs/MaximilianDSP.h" // Define Arduino output I2SStream out; @@ -20,7 +20,7 @@ maxiFilter myFilter; void setup() {//some inits // setup logging Serial.begin(115200); - AudioToolsLogger.begin(Serial, AudioToolsLogLevel::Info); + AudioLogger::instance().begin(Serial, AudioLogger::Info); // setup audio output auto cfg = out.defaultConfig(TX_MODE); diff --git a/examples/examples-dsp/examples-maximilian/11-Mixing/11-Mixing.ino b/examples/examples-maximilian/11-Mixing/11-Mixing.ino similarity index 88% rename from examples/examples-dsp/examples-maximilian/11-Mixing/11-Mixing.ino rename to examples/examples-maximilian/11-Mixing/11-Mixing.ino index 24b9e9e8a0..d5e56d278c 100755 --- a/examples/examples-dsp/examples-maximilian/11-Mixing/11-Mixing.ino +++ b/examples/examples-maximilian/11-Mixing/11-Mixing.ino @@ -1,5 +1,5 @@ #include "AudioTools.h" -#include "AudioTools/AudioLibs/MaximilianDSP.h" +#include "AudioLibs/MaximilianDSP.h" // Define Arduino output I2SStream out; @@ -13,7 +13,7 @@ maxiMix myOutputs;//this is the stereo mixer channel. void setup() {//some inits // setup logging Serial.begin(115200); - AudioToolsLogger.begin(Serial, AudioToolsLogLevel::Info); + AudioLogger::instance().begin(Serial, AudioLogger::Info); // setup audio output auto cfg = out.defaultConfig(TX_MODE); diff --git a/examples/examples-dsp/examples-maximilian/12-SamplePlayer/12-SamplePlayer.ino b/examples/examples-maximilian/12-SamplePlayer/12-SamplePlayer.ino similarity index 88% rename from examples/examples-dsp/examples-maximilian/12-SamplePlayer/12-SamplePlayer.ino rename to examples/examples-maximilian/12-SamplePlayer/12-SamplePlayer.ino index 66ba351712..5d0227a8fd 100755 --- a/examples/examples-dsp/examples-maximilian/12-SamplePlayer/12-SamplePlayer.ino +++ b/examples/examples-maximilian/12-SamplePlayer/12-SamplePlayer.ino @@ -1,13 +1,13 @@ #include "AudioTools.h" -#include "AudioTools/AudioLibs/MaximilianDSP.h" -#include "AudioTools/AudioLibs/AudioBoardStream.h" +#include "AudioLibs/MaximilianDSP.h" +#include "AudioLibs/AudioKit.h" #include "maximilian.h" #include #include // Define Arduino output -AudioBoardStream out(AudioKitEs8388V1); +AudioKitStream out; Maximilian maximilian(out); // MAXIMILIAN @@ -16,7 +16,7 @@ maxiSample beats; //We give our sample a name. It's called beats this time. We c void setup() {//some inits // setup logging Serial.begin(115200); - AudioToolsLogger.begin(Serial, AudioToolsLogLevel::Info); + AudioLogger::instance().begin(Serial, AudioLogger::Info); // setup audio output auto cfg = out.defaultConfig(TX_MODE); diff --git a/examples/examples-dsp/examples-maximilian/13-AdvancedFilters/13-AdvancedFilters.ino b/examples/examples-maximilian/13-AdvancedFilters/13-AdvancedFilters.ino similarity index 93% rename from examples/examples-dsp/examples-maximilian/13-AdvancedFilters/13-AdvancedFilters.ino rename to examples/examples-maximilian/13-AdvancedFilters/13-AdvancedFilters.ino index e2b52a0226..615bccb8d1 100644 --- a/examples/examples-dsp/examples-maximilian/13-AdvancedFilters/13-AdvancedFilters.ino +++ b/examples/examples-maximilian/13-AdvancedFilters/13-AdvancedFilters.ino @@ -2,7 +2,7 @@ //Example contributed by Rebecca Fiebrink #include "AudioTools.h" -#include "AudioTools/AudioLibs/MaximilianDSP.h" +#include "AudioLibs/MaximilianDSP.h" // Define Arduino output I2SStream out; @@ -19,7 +19,7 @@ maxiOsc mySwitchableOsc; void setup() {//some inits // setup logging Serial.begin(115200); - AudioToolsLogger.begin(Serial, AudioToolsLogLevel::Info); + AudioLogger::instance().begin(Serial, AudioLogger::Info); // setup audio output auto cfg = out.defaultConfig(TX_MODE); diff --git a/examples/examples-dsp/examples-maximilian/14-MonoSynth/14-MonoSynth.ino b/examples/examples-maximilian/14-MonoSynth/14-MonoSynth.ino similarity index 95% rename from examples/examples-dsp/examples-maximilian/14-MonoSynth/14-MonoSynth.ino rename to examples/examples-maximilian/14-MonoSynth/14-MonoSynth.ino index c1e4265a00..af38a20d24 100755 --- a/examples/examples-dsp/examples-maximilian/14-MonoSynth/14-MonoSynth.ino +++ b/examples/examples-maximilian/14-MonoSynth/14-MonoSynth.ino @@ -1,7 +1,7 @@ //This shows how to use maximilian to build a monophonic synth #include "AudioTools.h" -#include "AudioTools/AudioLibs/MaximilianDSP.h" +#include "AudioLibs/MaximilianDSP.h" // Define Arduino output I2SStream out; @@ -24,7 +24,7 @@ double VCO1out,VCO2out,LFO1out,LFO2out,VCFout,ADSRout; void setup() {//some inits // setup logging Serial.begin(115200); - AudioToolsLogger.begin(Serial, AudioToolsLogLevel::Info); + AudioLogger::instance().begin(Serial, AudioLogger::Info); // setup audio output auto cfg = out.defaultConfig(TX_MODE); diff --git a/examples/examples-dsp/examples-maximilian/15-PolySynth/15-PolySynth.ino b/examples/examples-maximilian/15-PolySynth/15-PolySynth.ino similarity index 96% rename from examples/examples-dsp/examples-maximilian/15-PolySynth/15-PolySynth.ino rename to examples/examples-maximilian/15-PolySynth/15-PolySynth.ino index 530aaa6f02..70f4b8437c 100644 --- a/examples/examples-dsp/examples-maximilian/15-PolySynth/15-PolySynth.ino +++ b/examples/examples-maximilian/15-PolySynth/15-PolySynth.ino @@ -2,7 +2,7 @@ //This shows how to use maximilian to build a polyphonic synth. #include "AudioTools.h" -#include "AudioTools/AudioLibs/MaximilianDSP.h" +#include "AudioLibs/MaximilianDSP.h" // Define Arduino output I2SStream out; @@ -24,7 +24,7 @@ double VCO1out[6],VCO2out[6],LFO1out[6],LFO2out[6],VCFout[6],ADSRout[6],mix,pitc void setup() {//some inits // setup logging Serial.begin(115200); - AudioToolsLogger.begin(Serial, AudioToolsLogLevel::Info); + AudioLogger::instance().begin(Serial, AudioLogger::Info); // setup audio output auto cfg = out.defaultConfig(TX_MODE); diff --git a/examples/examples-dsp/examples-maximilian/16-Replicant/16-Replicant.ino b/examples/examples-maximilian/16-Replicant/16-Replicant.ino similarity index 97% rename from examples/examples-dsp/examples-maximilian/16-Replicant/16-Replicant.ino rename to examples/examples-maximilian/16-Replicant/16-Replicant.ino index c390a6f4c8..ce20ff772f 100644 --- a/examples/examples-dsp/examples-maximilian/16-Replicant/16-Replicant.ino +++ b/examples/examples-maximilian/16-Replicant/16-Replicant.ino @@ -4,7 +4,7 @@ //Bizarelly, this sounds a little bit like Kraftwerk's 'Metropolis', although it isn't. Funny that. #include "AudioTools.h" -#include "AudioTools/AudioLibs/MaximilianDSP.h" +#include "AudioLibs/MaximilianDSP.h" // Define Arduino output I2SStream out; @@ -32,7 +32,7 @@ int leadLinePitch[15]={69,67,65,64,67,66,64,62,65,64,62,57,55,60,57}; void setup() {//some inits // setup logging Serial.begin(115200); - AudioToolsLogger.begin(Serial, AudioToolsLogLevel::Info); + AudioLogger::instance().begin(Serial, AudioLogger::Info); // setup audio output auto cfg = out.defaultConfig(TX_MODE); diff --git a/examples/examples-dsp/examples-maximilian/17-Compressor/17-Compressor.ino b/examples/examples-maximilian/17-Compressor/17-Compressor.ino similarity index 92% rename from examples/examples-dsp/examples-maximilian/17-Compressor/17-Compressor.ino rename to examples/examples-maximilian/17-Compressor/17-Compressor.ino index 4aa74e8755..2f652f2279 100644 --- a/examples/examples-dsp/examples-maximilian/17-Compressor/17-Compressor.ino +++ b/examples/examples-maximilian/17-Compressor/17-Compressor.ino @@ -1,6 +1,6 @@ #include "AudioTools.h" -#include "AudioTools/AudioLibs/MaximilianDSP.h" +#include "AudioLibs/MaximilianDSP.h" #include #include @@ -17,7 +17,7 @@ float fout; void setup() {//some inits // setup logging Serial.begin(115200); - AudioToolsLogger.begin(Serial, AudioToolsLogLevel::Info); + AudioLogger::instance().begin(Serial, AudioLogger::Info); // setup audio output auto cfg = out.defaultConfig(TX_MODE); diff --git a/examples/examples-dsp/examples-maximilian/18-DrumMachine/18-DrumMachine.ino b/examples/examples-maximilian/18-DrumMachine/18-DrumMachine.ino similarity index 94% rename from examples/examples-dsp/examples-maximilian/18-DrumMachine/18-DrumMachine.ino rename to examples/examples-maximilian/18-DrumMachine/18-DrumMachine.ino index b60ff53d2c..85fe287ff4 100644 --- a/examples/examples-dsp/examples-maximilian/18-DrumMachine/18-DrumMachine.ino +++ b/examples/examples-maximilian/18-DrumMachine/18-DrumMachine.ino @@ -1,5 +1,5 @@ #include "AudioTools.h" -#include "AudioTools/AudioLibs/MaximilianDSP.h" +#include "AudioLibs/MaximilianDSP.h" #include "audio/kick.h" #include "audio/snare.h" @@ -21,7 +21,7 @@ double sampleOut; void setup() {//some inits // setup logging Serial.begin(115200); - AudioToolsLogger.begin(Serial, AudioToolsLogLevel::Info); + AudioLogger::instance().begin(Serial, AudioLogger::Info); // setup audio output auto cfg = out.defaultConfig(TX_MODE); diff --git a/examples/examples-dsp/examples-maximilian/19-Enveloping2/19-Enveloping2.ino b/examples/examples-maximilian/19-Enveloping2/19-Enveloping2.ino similarity index 94% rename from examples/examples-dsp/examples-maximilian/19-Enveloping2/19-Enveloping2.ino rename to examples/examples-maximilian/19-Enveloping2/19-Enveloping2.ino index cd9d735875..6f3c94d75a 100644 --- a/examples/examples-dsp/examples-maximilian/19-Enveloping2/19-Enveloping2.ino +++ b/examples/examples-maximilian/19-Enveloping2/19-Enveloping2.ino @@ -2,7 +2,7 @@ //this tutorial explains how to use the maxiEnv #include "AudioTools.h" -#include "AudioTools/AudioLibs/MaximilianDSP.h" +#include "AudioLibs/MaximilianDSP.h" #include "audio/cello_open_string_bowed.h" // Arduino output @@ -21,7 +21,7 @@ float sampleOut; void setup() {//some inits // setup logging Serial.begin(115200); - AudioToolsLogger.begin(Serial, AudioToolsLogLevel::Info); + AudioLogger::instance().begin(Serial, AudioLogger::Info); // setup audio output auto cfg = out.defaultConfig(TX_MODE); diff --git a/examples/examples-dsp/examples-maximilian/20-FFT/20-FFT.ino b/examples/examples-maximilian/20-FFT/20-FFT.ino similarity index 89% rename from examples/examples-dsp/examples-maximilian/20-FFT/20-FFT.ino rename to examples/examples-maximilian/20-FFT/20-FFT.ino index 22560c3163..8c56da1249 100644 --- a/examples/examples-dsp/examples-maximilian/20-FFT/20-FFT.ino +++ b/examples/examples-maximilian/20-FFT/20-FFT.ino @@ -3,7 +3,7 @@ #include "AudioTools.h" -#include "AudioTools/AudioLibs/MaximilianDSP.h" +#include "AudioLibs/MaximilianDSP.h" #include "libs/maxim.h" // Define Arduino output @@ -17,7 +17,7 @@ maxiFFT myFFT; void setup() { // setup logging Serial.begin(115200); - AudioToolsLogger.begin(Serial, AudioToolsLogLevel::Info); + AudioLogger::instance().begin(Serial, AudioLogger::Info); // setup audio output auto cfg = out.defaultConfig(TX_MODE); diff --git a/examples/examples-dsp/examples-maximilian/README.md b/examples/examples-maximilian/README.md similarity index 100% rename from examples/examples-dsp/examples-maximilian/README.md rename to examples/examples-maximilian/README.md diff --git a/examples/examples-player/player-callback-i2s/player-callback-i2s.ino b/examples/examples-player/player-callback-i2s/player-callback-i2s.ino index 9f3da1d630..7979f8b30c 100644 --- a/examples/examples-player/player-callback-i2s/player-callback-i2s.ino +++ b/examples/examples-player/player-callback-i2s/player-callback-i2s.ino @@ -11,7 +11,7 @@ #include "Arduino.h" #include "AudioTools.h" -#include "AudioTools/AudioCodecs/CodecMP3Helix.h" +#include "AudioCodecs/CodecMP3Helix.h" #include #include @@ -52,7 +52,7 @@ void callbackPrintMetaData(MetaDataType type, const char* str, int len){ void setup() { Serial.begin(115200); - AudioToolsLogger.begin(Serial, AudioToolsLogLevel::Info); + AudioLogger::instance().begin(Serial, AudioLogger::Info); // setup SD SD.begin(chipSelect); diff --git a/examples/examples-player/player-ftp-audiokit/player-ftp-audiokit.ino b/examples/examples-player/player-ftp-audiokit/player-ftp-audiokit.ino deleted file mode 100644 index 377f14b28f..0000000000 --- a/examples/examples-player/player-ftp-audiokit/player-ftp-audiokit.ino +++ /dev/null @@ -1,79 +0,0 @@ -/** - * @file player-ftp-audiokit.ino - * @brief AudioPlayer example that is using FTP as audio source - * @author Phil Schatzmann - * @copyright GPLv3 - */ - -#include "WiFi.h" -#include "AudioTools.h" -#include "AudioTools/AudioCodecs/CodecMP3Helix.h" -#include "AudioTools/AudioLibs/AudioBoardStream.h" -#include "AudioTools/Disk/AudioSourceFTP.h" - -const char* path = "Music/Tracy Chapman/Matters of the Heart"; -const char* ext = "mp3"; -const char* ftp_user = "user"; -const char* ftp_pwd = "password"; -const char* ssid = "ssid"; -const char* ssid_pwd = "ssid-password"; - -FTPClient ftp; -AudioSourceFTP source(ftp, path, ext); -AudioBoardStream i2s(AudioKitEs8388V1); -MP3DecoderHelix decoder; -AudioPlayer player(source, i2s, decoder); - -void next(bool, int, void*) { player.next(); } - -void previous(bool, int, void*) { player.previous(); } - -void startStop(bool, int, void*) { player.setActive(!player.isActive()); } - -void setup() { - Serial.begin(115200); - AudioToolsLogger.begin(Serial, AudioToolsLogLevel::Info); - FTPLogger::setOutput(Serial); - FTPLogger::setLogLevel(LOG_DEBUG); - - // connect to WIFI - WiFi.begin(ssid, ssid_pwd); - while (WiFi.status() != WL_CONNECTED) { - delay(500); - Serial.print("."); - } - Serial.println(); - WiFi.setSleep(false); - - // connect to ftp - if (!ftp.begin(IPAddress(192,168,1,39), ftp_user, ftp_pwd)){ - Serial.println("ftp failed"); - stop(); - } - - // setup output - auto cfg = i2s.defaultConfig(TX_MODE); - if (!i2s.begin(cfg)){ - Serial.println("i2s failed"); - stop(); - } - - // setup additional buttons - i2s.addDefaultActions(); - i2s.addAction(i2s.getKey(1), startStop); - i2s.addAction(i2s.getKey(4), next); - i2s.addAction(i2s.getKey(3), previous); - - // setup player - player.setVolume(0.7); - // load the directory - if (!player.begin()){ - Serial.println("player failed"); - stop(); - } -} - -void loop() { - player.copy(); - i2s.processActions(); -} diff --git a/examples/examples-player/player-littlefs-i2s/player-littlefs-i2s.ino b/examples/examples-player/player-littlefs-i2s/player-littlefs-i2s.ino index 7a7745b4e0..8f97cc4e69 100644 --- a/examples/examples-player/player-littlefs-i2s/player-littlefs-i2s.ino +++ b/examples/examples-player/player-littlefs-i2s/player-littlefs-i2s.ino @@ -7,8 +7,8 @@ */ #include "AudioTools.h" -#include "AudioTools/Disk/AudioSourceLittleFS.h" -#include "AudioTools/AudioCodecs/CodecMP3Helix.h" +#include "AudioLibs/AudioSourceLittleFS.h" +#include "AudioCodecs/CodecMP3Helix.h" const char *startFilePath="/"; const char* ext="mp3"; @@ -26,7 +26,7 @@ void printMetaData(MetaDataType type, const char* str, int len){ void setup() { Serial.begin(115200); - AudioToolsLogger.begin(Serial, AudioToolsLogLevel::Info); + AudioLogger::instance().begin(Serial, AudioLogger::Info); // setup output auto cfg = i2s.defaultConfig(TX_MODE); diff --git a/examples/examples-player/player-sd-i2s/player-sd-i2s.ino b/examples/examples-player/player-sd-i2s/player-sd-i2s.ino index e77481ee52..acee47dad2 100644 --- a/examples/examples-player/player-sd-i2s/player-sd-i2s.ino +++ b/examples/examples-player/player-sd-i2s/player-sd-i2s.ino @@ -7,8 +7,8 @@ */ #include "AudioTools.h" -#include "AudioTools/Disk/AudioSourceSD.h" -#include "AudioTools/AudioCodecs/CodecMP3Helix.h" +#include "AudioLibs/AudioSourceSD.h" +#include "AudioCodecs/CodecMP3Helix.h" const char *startFilePath="/"; const char* ext="mp3"; @@ -26,7 +26,7 @@ void printMetaData(MetaDataType type, const char* str, int len){ void setup() { Serial.begin(115200); - AudioToolsLogger.begin(Serial, AudioToolsLogLevel::Info); + AudioLogger::instance().begin(Serial, AudioLogger::Info); // setup output auto cfg = i2s.defaultConfig(TX_MODE); diff --git a/examples/examples-player/player-sd_m4a-audiokit/player-sd_m4a-audiokit.ino b/examples/examples-player/player-sd_m4a-audiokit/player-sd_m4a-audiokit.ino deleted file mode 100644 index 3d5b55358d..0000000000 --- a/examples/examples-player/player-sd_m4a-audiokit/player-sd_m4a-audiokit.ino +++ /dev/null @@ -1,56 +0,0 @@ -/** - * @file player-sd-i2s.ino - * @brief example using the SD library that shows how to support m4a - * aac, mp3 and alac files. We provid a MultiDecoder to the ContainerM4A - * so that the container can decode aac and alac. We also provide it to - * the AudioPlayer so that we can play aac, alac, mp4 and m4a files. If - * you only want to play m4a files, you can provide the ContainerM4A directly. - * - * @author Phil Schatzmann - * @copyright GPLv3 - */ - -#include "AudioTools.h" -#include "AudioTools/Disk/AudioSourceSD.h" -#include "AudioTools/AudioCodecs/CodecALAC.h" -#include "AudioTools/AudioCodecs/CodecAACHelix.h" -#include "AudioTools/AudioCodecs/CodecMP3Helix.h" -#include "AudioTools/AudioCodecs/ContainerM4A.h" -#include "AudioTools/AudioCodecs/MultiDecoder.h" -#include "AudioTools/AudioLibs/AudioBoardStream.h" // install https://github.com/pschatzmann/arduino-audio-driver - - -const char *startFilePath="/m4a"; -const char* ext="m4a"; -AudioSourceSD source(startFilePath, ext); -AudioBoardStream i2s(AudioKitEs8388V1); // or e.g. I2SStream i2s; -MultiDecoder multi_decoder; -ContainerM4A dec_m4a(multi_decoder); -AACDecoderHelix dec_aac; -MP3DecoderHelix dec_mp3; -DecoderALAC dec_alac; -AudioPlayer player(source, i2s, multi_decoder); - - -void setup() { - Serial.begin(115200); - AudioToolsLogger.begin(Serial, AudioToolsLogLevel::Info); - - // setup multi decoder - multi_decoder.addDecoder(dec_m4a, "audio/m4a"); - multi_decoder.addDecoder(dec_alac,"audio/alac"); - multi_decoder.addDecoder(dec_aac,"audio/aac"); - multi_decoder.addDecoder(dec_mp3,"audio/mp3"); - - // setup output - auto cfg = i2s.defaultConfig(TX_MODE); - i2s.begin(cfg); - - // setup player - //source.setFileFilter("*Bob Dylan*"); - player.begin(); -} - -void loop() { - player.copy(); -} \ No newline at end of file diff --git a/examples/examples-communication/a2dp/player-sdfat-a2dp/README.md b/examples/examples-player/player-sdfat-a2dp/README.md similarity index 100% rename from examples/examples-communication/a2dp/player-sdfat-a2dp/README.md rename to examples/examples-player/player-sdfat-a2dp/README.md diff --git a/examples/examples-communication/a2dp/player-sdfat-a2dp/player-sdfat-a2dp.ino b/examples/examples-player/player-sdfat-a2dp/player-sdfat-a2dp.ino similarity index 74% rename from examples/examples-communication/a2dp/player-sdfat-a2dp/player-sdfat-a2dp.ino rename to examples/examples-player/player-sdfat-a2dp/player-sdfat-a2dp.ino index 596410438a..0fe38cd3b9 100644 --- a/examples/examples-communication/a2dp/player-sdfat-a2dp/player-sdfat-a2dp.ino +++ b/examples/examples-player/player-sdfat-a2dp/player-sdfat-a2dp.ino @@ -10,9 +10,10 @@ #define HELIX_LOGGING_ACTIVE false #include "AudioTools.h" -#include "AudioTools/AudioLibs/A2DPStream.h" -#include "AudioTools/Disk/AudioSourceSDFAT.h" -#include "AudioTools/AudioCodecs/CodecMP3Helix.h" +#include "AudioLibs/AudioA2DP.h" +#include "AudioLibs/AudioSourceSDFAT.h" +#include "AudioCodecs/CodecMP3Helix.h" +//#include "AudioLibs/AudioKit.h" const char *startFilePath="/"; const char* ext="mp3"; @@ -21,23 +22,23 @@ A2DPStream out; MP3DecoderHelix decoder; AudioPlayer player(source, out, decoder); + void setup() { Serial.begin(115200); - AudioToolsLogger.begin(Serial, AudioToolsLogLevel::Warning); + AudioLogger::instance().begin(Serial, AudioLogger::Warning); - // setup player // Setting up SPI if necessary with the right SD pins by calling // SPI.begin(PIN_AUDIO_KIT_SD_CARD_CLK, PIN_AUDIO_KIT_SD_CARD_MISO, PIN_AUDIO_KIT_SD_CARD_MOSI, PIN_AUDIO_KIT_SD_CARD_CS); - player.setVolume(0.1); - player.begin(); // setup output - We send the test signal via A2DP - so we conect to a Bluetooth Speaker auto cfg = out.defaultConfig(TX_MODE); - cfg.silence_on_nodata = true; // prevent disconnect when there is no audio data - cfg.name = "LEXON MINO L"; // set the device here. Otherwise the first available device is used for output + //cfg.name = "LEXON MINO L"; // set the device here. Otherwise the next available device is used for output //cfg.auto_reconnect = true; // if this is use we just quickly connect to the last device ignoring cfg.name out.begin(cfg); + // setup player + player.setVolume(0.1); + player.begin(); } diff --git a/examples/examples-player/player-sdfat-analog/player-sdfat-analog.ino b/examples/examples-player/player-sdfat-analog/player-sdfat-analog.ino index d9d1f1df1d..72ea3492ba 100644 --- a/examples/examples-player/player-sdfat-analog/player-sdfat-analog.ino +++ b/examples/examples-player/player-sdfat-analog/player-sdfat-analog.ino @@ -8,8 +8,8 @@ #include "AudioTools.h" -#include "AudioTools/Disk/AudioSourceSDFAT.h" -#include "AudioTools/AudioCodecs/CodecMP3Helix.h" +#include "AudioLibs/AudioSourceSDFAT.h" +#include "AudioCodecs/CodecMP3Helix.h" const char *startFilePath="/"; @@ -29,7 +29,7 @@ void printMetaData(MetaDataType type, const char* str, int len){ void setup() { Serial.begin(115200); - AudioToolsLogger.begin(Serial, AudioToolsLogLevel::Info); + AudioLogger::instance().begin(Serial, AudioLogger::Info); // setup output auto cfg = out.defaultConfig(); diff --git a/examples/examples-player/player-sdfat-ffti2s/player-sdfat-ffti2s.ino b/examples/examples-player/player-sdfat-ffti2s/player-sdfat-ffti2s.ino index a893396194..ea00060e84 100644 --- a/examples/examples-player/player-sdfat-ffti2s/player-sdfat-ffti2s.ino +++ b/examples/examples-player/player-sdfat-ffti2s/player-sdfat-ffti2s.ino @@ -8,9 +8,9 @@ #include "AudioTools.h" -#include "AudioTools/Disk/AudioSourceSD.h" -#include "AudioTools/AudioCodecs/CodecMP3Helix.h" -#include "AudioTools/AudioLibs/AudioRealFFT.h" // or AudioKissFFT or others +#include "AudioLibs/AudioSourceSD.h" +#include "AudioCodecs/CodecMP3Helix.h" +#include "AudioLibs/AudioRealFFT.h" // or AudioKissFFT or others const char *startFilePath="/"; const char* ext="mp3"; @@ -38,7 +38,7 @@ void fftResult(AudioFFTBase &fft){ void setup() { Serial.begin(115200); - AudioToolsLogger.begin(Serial, AudioToolsLogLevel::Info); + AudioLogger::instance().begin(Serial, AudioLogger::Info); // setup I2S auto cfg = i2s.defaultConfig(TX_MODE); diff --git a/examples/examples-player/player-sdfat-i2s/player-sdfat-i2s.ino b/examples/examples-player/player-sdfat-i2s/player-sdfat-i2s.ino index 57de59c3e4..52821ad1e7 100644 --- a/examples/examples-player/player-sdfat-i2s/player-sdfat-i2s.ino +++ b/examples/examples-player/player-sdfat-i2s/player-sdfat-i2s.ino @@ -8,8 +8,8 @@ #include "AudioTools.h" -#include "AudioTools/Disk/AudioSourceSDFAT.h" -#include "AudioTools/AudioCodecs/CodecMP3Helix.h" +#include "AudioLibs/AudioSourceSDFAT.h" +#include "AudioCodecs/CodecMP3Helix.h" const char *startFilePath="/"; @@ -29,7 +29,7 @@ void printMetaData(MetaDataType type, const char* str, int len){ void setup() { Serial.begin(115200); - AudioToolsLogger.begin(Serial, AudioToolsLogLevel::Info); + AudioLogger::instance().begin(Serial, AudioLogger::Info); // setup output auto cfg = i2s.defaultConfig(TX_MODE); diff --git a/examples/examples-player/player-spiffs-i2s/player-spiffs-i2s.ino b/examples/examples-player/player-spiffs-i2s/player-spiffs-i2s.ino index 774cbad05b..48390a0d52 100644 --- a/examples/examples-player/player-spiffs-i2s/player-spiffs-i2s.ino +++ b/examples/examples-player/player-spiffs-i2s/player-spiffs-i2s.ino @@ -7,8 +7,8 @@ */ #include "AudioTools.h" -#include "AudioTools/Disk/AudioSourceSPIFFS.h" -#include "AudioTools/AudioCodecs/CodecMP3Helix.h" +#include "AudioLibs/AudioSourceSPIFFS.h" +#include "AudioCodecs/CodecMP3Helix.h" const char *startFilePath="/"; const char* ext="mp3"; @@ -26,7 +26,7 @@ void printMetaData(MetaDataType type, const char* str, int len){ void setup() { Serial.begin(115200); - AudioToolsLogger.begin(Serial, AudioToolsLogLevel::Info); + AudioLogger::instance().begin(Serial, AudioLogger::Info); // setup output auto cfg = i2s.defaultConfig(TX_MODE); diff --git a/examples/examples-communication/http-client/player-url-i2s/README.md b/examples/examples-player/player-url-i2s/README.md similarity index 100% rename from examples/examples-communication/http-client/player-url-i2s/README.md rename to examples/examples-player/player-url-i2s/README.md diff --git a/examples/examples-communication/http-client/player-url-i2s/player-url-i2s.ino b/examples/examples-player/player-url-i2s/player-url-i2s.ino similarity index 91% rename from examples/examples-communication/http-client/player-url-i2s/player-url-i2s.ino rename to examples/examples-player/player-url-i2s/player-url-i2s.ino index ee1f014ef9..3fb7f0ec6b 100644 --- a/examples/examples-communication/http-client/player-url-i2s/player-url-i2s.ino +++ b/examples/examples-player/player-url-i2s/player-url-i2s.ino @@ -8,8 +8,7 @@ #include "AudioTools.h" -#include "AudioTools/AudioCodecs/CodecMP3Helix.h" -#include "AudioTools/Disk/AudioSourceURL.h" +#include "AudioCodecs/CodecMP3Helix.h" const char *urls[] = { @@ -35,7 +34,7 @@ const int nextButtonPin = 13; void setup() { Serial.begin(115200); - AudioToolsLogger.begin(Serial, AudioToolsLogLevel::Info); + AudioLogger::instance().begin(Serial, AudioLogger::Info); // setup output auto cfg = i2s.defaultConfig(TX_MODE); diff --git a/examples/examples-communication/http-client/player-url_icy-i2s/README.md b/examples/examples-player/player-url_icy-i2s/README.md similarity index 100% rename from examples/examples-communication/http-client/player-url_icy-i2s/README.md rename to examples/examples-player/player-url_icy-i2s/README.md diff --git a/examples/examples-communication/http-client/player-url_icy-i2s/player-url_icy-i2s.ino b/examples/examples-player/player-url_icy-i2s/player-url_icy-i2s.ino similarity index 92% rename from examples/examples-communication/http-client/player-url_icy-i2s/player-url_icy-i2s.ino rename to examples/examples-player/player-url_icy-i2s/player-url_icy-i2s.ino index 53b193d7e8..2923e3009d 100644 --- a/examples/examples-communication/http-client/player-url_icy-i2s/player-url_icy-i2s.ino +++ b/examples/examples-player/player-url_icy-i2s/player-url_icy-i2s.ino @@ -8,8 +8,7 @@ #include "AudioTools.h" -#include "AudioTools/AudioCodecs/CodecMP3Helix.h" -#include "AudioTools/Disk/AudioSourceURL.h" +#include "AudioCodecs/CodecMP3Helix.h" const char *urls[] = { @@ -45,7 +44,7 @@ void printMetaData(MetaDataType type, const char* str, int len){ // Arduino setup void setup() { Serial.begin(115200); - AudioToolsLogger.begin(Serial, AudioToolsLogLevel::Info); + AudioLogger::instance().begin(Serial, AudioLogger::Info); // setup output auto cfg = i2s.defaultConfig(TX_MODE); diff --git a/examples/examples-communication/http-client/player-url_subclass-i2s/AudioSourceIcyUrl.h b/examples/examples-player/player-url_subclass-i2s/AudioSourceIcyUrl.h similarity index 88% rename from examples/examples-communication/http-client/player-url_subclass-i2s/AudioSourceIcyUrl.h rename to examples/examples-player/player-url_subclass-i2s/AudioSourceIcyUrl.h index acb5fffaf5..8e37c75cb9 100644 --- a/examples/examples-communication/http-client/player-url_subclass-i2s/AudioSourceIcyUrl.h +++ b/examples/examples-player/player-url_subclass-i2s/AudioSourceIcyUrl.h @@ -1,7 +1,5 @@ #pragma once #include "AudioTools.h" -#include "AudioTools/CoreAudio/AudioHttp/URLStream.h" -#include "AudioTools/Disk/AudioSourceURL.h" namespace audio_tools { @@ -32,7 +30,7 @@ class AudioSourceIcyUrl : public AudioSourceURL { /// Returns the last section of a url: https://22323.live.streamtheworld.com/TOPRETRO.mp3 gives TOPRETRO.mp3 const char *urlName(){ const char* result = ""; - StrView tmpStr(toStr()); + Str tmpStr(toStr()); int pos = tmpStr.lastIndexOf("/"); if (pos>0){ result = toStr()+pos+1; @@ -42,7 +40,7 @@ class AudioSourceIcyUrl : public AudioSourceURL { /// Returns the icy name if available otherwise we use our custom logic const char* name() { - StrView result(icyName()); + Str result(icyName()); if (result.isEmpty()){ result.set(urlName()); } diff --git a/examples/examples-communication/http-client/player-url_subclass-i2s/README.md b/examples/examples-player/player-url_subclass-i2s/README.md similarity index 100% rename from examples/examples-communication/http-client/player-url_subclass-i2s/README.md rename to examples/examples-player/player-url_subclass-i2s/README.md diff --git a/examples/examples-communication/http-client/player-url_subclass-i2s/player-url_subclass-i2s.ino b/examples/examples-player/player-url_subclass-i2s/player-url_subclass-i2s.ino similarity index 94% rename from examples/examples-communication/http-client/player-url_subclass-i2s/player-url_subclass-i2s.ino rename to examples/examples-player/player-url_subclass-i2s/player-url_subclass-i2s.ino index 1c354c6074..aad3558366 100644 --- a/examples/examples-communication/http-client/player-url_subclass-i2s/player-url_subclass-i2s.ino +++ b/examples/examples-player/player-url_subclass-i2s/player-url_subclass-i2s.ino @@ -8,7 +8,7 @@ #include "AudioTools.h" -#include "AudioTools/AudioCodecs/CodecMP3Helix.h" +#include "AudioCodecs/CodecMP3Helix.h" #include "AudioSourceIcyUrl.h" @@ -43,7 +43,7 @@ void printName() { void setup() { Serial.begin(115200); - AudioToolsLogger.begin(Serial, AudioToolsLogLevel::Info); + AudioLogger::instance().begin(Serial, AudioLogger::Info); // setup output auto cfg = i2s.defaultConfig(TX_MODE); diff --git a/examples/examples-dsp/examples-stk/README.md b/examples/examples-stk/README.md similarity index 100% rename from examples/examples-dsp/examples-stk/README.md rename to examples/examples-stk/README.md diff --git a/examples/examples-dsp/examples-stk/streams-stk-audiokit/streams-stk-audiokit.ino b/examples/examples-stk/streams-stk-audiokit/streams-stk-audiokit.ino similarity index 80% rename from examples/examples-dsp/examples-stk/streams-stk-audiokit/streams-stk-audiokit.ino rename to examples/examples-stk/streams-stk-audiokit/streams-stk-audiokit.ino index 28e5035b06..2698cd1058 100644 --- a/examples/examples-dsp/examples-stk/streams-stk-audiokit/streams-stk-audiokit.ino +++ b/examples/examples-stk/streams-stk-audiokit/streams-stk-audiokit.ino @@ -2,17 +2,17 @@ * @file streams-stk-audioout.ino * @brief Plays random notes on instrument. For available instruments * see https://pschatzmann.github.io/Arduino-STK/html/classstk_1_1Instrmnt.html - * I used an AudioBoardStream to test the output, but you can replace it with any other output class (e.g. I2SStream) + * I used an AudioKitStream to test the output, but you can replace it with any other output class (e.g. I2SStream) * @author Phil Schatzmann * @copyright Copyright (c) 2021 */ #include "AudioTools.h" -#include "AudioTools/AudioLibs/AudioSTK.h" // install https://github.com/pschatzmann/Arduino-STK -#include "AudioTools/AudioLibs/AudioBoardStream.h" // install https://github.com/pschatzmann/arduino-audio-driver +#include "AudioLibs/AudioSTK.h" +#include "AudioLibs/AudioKit.h" Sitar instrument(440); STKStream in(instrument); -AudioBoardStream out(AudioKitEs8388V1); +AudioKitStream out; StreamCopy copier(out, in); MusicalNotes notes; diff --git a/examples/examples-dsp/examples-stk/streams-stk-desktop/CMakeLists.txt b/examples/examples-stk/streams-stk-desktop/CMakeLists.txt similarity index 90% rename from examples/examples-dsp/examples-stk/streams-stk-desktop/CMakeLists.txt rename to examples/examples-stk/streams-stk-desktop/CMakeLists.txt index 8464414a69..59e2325050 100644 --- a/examples/examples-dsp/examples-stk/streams-stk-desktop/CMakeLists.txt +++ b/examples/examples-stk/streams-stk-desktop/CMakeLists.txt @@ -17,7 +17,7 @@ endif() # Build with arduino-audio-tools if(CMAKE_SOURCE_DIR STREQUAL CMAKE_CURRENT_SOURCE_DIR) - add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/../../../.. ${CMAKE_CURRENT_BINARY_DIR}/arduino-audio-tools ) + add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/../../.. ${CMAKE_CURRENT_BINARY_DIR}/arduino-audio-tools ) endif() # build sketch as executable @@ -26,7 +26,7 @@ add_executable (stk_example streams-stk-desktop.ino) # set preprocessor defines target_compile_definitions(arduino_emulator PUBLIC -DDEFINE_MAIN) -target_compile_definitions(stk_example PUBLIC -DARDUINO -DIS_DESKTOP) +target_compile_definitions(stk_example PUBLIC -DARDUINO -DIS_DESKTOP -DEXIT_ON_STOP) target_compile_definitions(arduino-stk PUBLIC -DIS_DESKTOP) # OS/X might need this setting for core audio diff --git a/examples/examples-dsp/examples-stk/streams-stk-desktop/streams-stk-desktop.ino b/examples/examples-stk/streams-stk-desktop/streams-stk-desktop.ino similarity index 91% rename from examples/examples-dsp/examples-stk/streams-stk-desktop/streams-stk-desktop.ino rename to examples/examples-stk/streams-stk-desktop/streams-stk-desktop.ino index d486d1256d..88957c873b 100644 --- a/examples/examples-dsp/examples-stk/streams-stk-desktop/streams-stk-desktop.ino +++ b/examples/examples-stk/streams-stk-desktop/streams-stk-desktop.ino @@ -12,8 +12,8 @@ #include "Arduino.h" #include "AudioTools.h" -#include "AudioTools/AudioLibs/AudioSTK.h" // install https://github.com/pschatzmann/Arduino-STK3 -#include "AudioTools/AudioLibs/PortAudioStream.h" +#include "AudioLibs/AudioSTK.h" +#include "AudioLibs/PortAudioStream.h" Flute insturment(50); STKStream in(insturment); diff --git a/examples/examples-dsp/examples-stk/streams-stk_allinstruments-audiokit/CMakeLists.txt b/examples/examples-stk/streams-stk_allinstruments-audiokit/CMakeLists.txt similarity index 90% rename from examples/examples-dsp/examples-stk/streams-stk_allinstruments-audiokit/CMakeLists.txt rename to examples/examples-stk/streams-stk_allinstruments-audiokit/CMakeLists.txt index f4d09007da..d04f6c63ba 100644 --- a/examples/examples-dsp/examples-stk/streams-stk_allinstruments-audiokit/CMakeLists.txt +++ b/examples/examples-stk/streams-stk_allinstruments-audiokit/CMakeLists.txt @@ -18,7 +18,7 @@ endif() # Build with arduino-audio-tools if(CMAKE_SOURCE_DIR STREQUAL CMAKE_CURRENT_SOURCE_DIR) - add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/../../../.. ${CMAKE_CURRENT_BINARY_DIR}/arduino-audio-tools ) + add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/../../.. ${CMAKE_CURRENT_BINARY_DIR}/arduino-audio-tools ) endif() # build sketch as executable @@ -27,7 +27,7 @@ add_executable (arduino_sketch ${SKETCH}) # set preprocessor defines target_compile_definitions(arduino_emulator PUBLIC -DDEFINE_MAIN) -target_compile_definitions(arduino_sketch PUBLIC -DARDUINO -DIS_DESKTOP) +target_compile_definitions(arduino_sketch PUBLIC -DARDUINO -DIS_DESKTOP -DEXIT_ON_STOP) target_compile_definitions(arduino-stk PUBLIC -DIS_DESKTOP) # OS/X might need this setting for core audio diff --git a/examples/examples-dsp/examples-stk/streams-stk_allinstruments-audiokit/streams-stk_allinstruments-audiokit.ino b/examples/examples-stk/streams-stk_allinstruments-audiokit/streams-stk_allinstruments-audiokit.ino similarity index 90% rename from examples/examples-dsp/examples-stk/streams-stk_allinstruments-audiokit/streams-stk_allinstruments-audiokit.ino rename to examples/examples-stk/streams-stk_allinstruments-audiokit/streams-stk_allinstruments-audiokit.ino index aec57914e3..d6dc1a84cd 100644 --- a/examples/examples-dsp/examples-stk/streams-stk_allinstruments-audiokit/streams-stk_allinstruments-audiokit.ino +++ b/examples/examples-stk/streams-stk_allinstruments-audiokit/streams-stk_allinstruments-audiokit.ino @@ -5,19 +5,19 @@ * @copyright Copyright (c) 2021 */ #include "AudioTools.h" -#include "AudioTools/AudioLibs/AudioSTK.h" // install https://github.com/pschatzmann/Arduino-STK -#include +#include "AudioLibs/AudioSTK.h" #ifdef IS_DESKTOP -#include "AudioTools/AudioLibs/PortAudioStream.h" -PortAudioStream out; +#include "AudioLibs/PortAudioStream.h" +typedef PortAudioStream MyStdOut; #else -#include "AudioTools/AudioLibs/AudioBoardStream.h" // install https://github.com/pschatzmann/arduino-audio-driver -AudioBoardStream out(AudioKitEs8388V1); -#endif +#include "AudioLibs/AudioKit.h" +typedef AudioKitStream MyStdOut; +#endif Instrmnt *p_Instrmnt=nullptr; // will be allocated dynamically STKStream in; +MyStdOut out; // or replace with I2SStream or any other output class StreamCopy copier(out, in); MusicalNotes notes; @@ -128,7 +128,7 @@ void play() { void setup() { Serial.begin(115200); - AudioToolsLogger.begin(Serial, AudioToolsLogLevel::Warning); + AudioLogger::instance().begin(Serial, AudioLogger::Warning); StkLogLevel = StkWarning; // setup input diff --git a/examples/examples-dsp/examples-stk/streams-stk_files-audiokit/streams-stk_files-audiokit.ino b/examples/examples-stk/streams-stk_files-audiokit/streams-stk_files-audiokit.ino similarity index 88% rename from examples/examples-dsp/examples-stk/streams-stk_files-audiokit/streams-stk_files-audiokit.ino rename to examples/examples-stk/streams-stk_files-audiokit/streams-stk_files-audiokit.ino index 20ef106dc3..14525bd383 100644 --- a/examples/examples-dsp/examples-stk/streams-stk_files-audiokit/streams-stk_files-audiokit.ino +++ b/examples/examples-stk/streams-stk_files-audiokit/streams-stk_files-audiokit.ino @@ -10,12 +10,12 @@ #include "SD_MMC.h" #include "AudioTools.h" -#include "AudioTools/AudioLibs/AudioSTK.h" // install https://github.com/pschatzmann/Arduino-STK -#include "AudioTools/AudioLibs/AudioBoardStream.h" // install https://github.com/pschatzmann/arduino-audio-driver +#include "AudioLibs/AudioSTK.h" +#include "AudioLibs/AudioKit.h" STKStream in; -AudioBoardStream out(AudioKitEs8388V1); +AudioKitStream out; StreamCopy copier(out, in); MusicalNotes notes; Instrmnt* p_instrument=nullptr; // instrument depends on file system diff --git a/examples/examples-dsp/examples-stk/streams-stk_generator-audiokit/streams-stk_generator-audiokit.ino b/examples/examples-stk/streams-stk_generator-audiokit/streams-stk_generator-audiokit.ino similarity index 83% rename from examples/examples-dsp/examples-stk/streams-stk_generator-audiokit/streams-stk_generator-audiokit.ino rename to examples/examples-stk/streams-stk_generator-audiokit/streams-stk_generator-audiokit.ino index 3b0d319505..c8df667e93 100644 --- a/examples/examples-dsp/examples-stk/streams-stk_generator-audiokit/streams-stk_generator-audiokit.ino +++ b/examples/examples-stk/streams-stk_generator-audiokit/streams-stk_generator-audiokit.ino @@ -7,20 +7,20 @@ */ #include "AudioTools.h" -#include "AudioTools/AudioLibs/AudioSTK.h" // install https://github.com/pschatzmann/Arduino-STK -#include "AudioTools/AudioLibs/AudioBoardStream.h" // install https://github.com/pschatzmann/arduino-audio-driver +#include "AudioLibs/AudioKit.h" +#include "AudioLibs/AudioSTK.h" Clarinet clarinet(440); // the stk clarinet instrument STKGenerator generator(clarinet); // subclass of SoundGenerator GeneratedSoundStream in(generator); // Stream generated from sine wave -AudioBoardStream out(AudioKitEs8388V1); +AudioKitStream out; StreamCopy copier(out, in); // copy stkStream to a2dpStream MusicalNotes notes; // notes with frequencies // Arduino Setup void setup(void) { Serial.begin(115200); - AudioToolsLogger.begin(Serial, AudioToolsLogLevel::Warning); + AudioLogger::instance().begin(Serial, AudioLogger::Warning); // start Output Serial.println("starting Analog Output..."); @@ -30,7 +30,7 @@ void setup(void) { Serial.println("starting STK..."); auto cfgSTK = in.defaultConfig(); cfgSTK.channels = 2; - in.addNotifyAudioChange(out); + in.setNotifyAudioChange(out); in.begin(cfgSTK); } diff --git a/examples/examples-dsp/examples-stk/streams-stk_loop-audiokit/streams-stk_loop-audiokit.ino b/examples/examples-stk/streams-stk_loop-audiokit/streams-stk_loop-audiokit.ino similarity index 73% rename from examples/examples-dsp/examples-stk/streams-stk_loop-audiokit/streams-stk_loop-audiokit.ino rename to examples/examples-stk/streams-stk_loop-audiokit/streams-stk_loop-audiokit.ino index 8da2a906bf..f8abb1b858 100644 --- a/examples/examples-dsp/examples-stk/streams-stk_loop-audiokit/streams-stk_loop-audiokit.ino +++ b/examples/examples-stk/streams-stk_loop-audiokit/streams-stk_loop-audiokit.ino @@ -9,12 +9,12 @@ * */ #include "AudioTools.h" -#include "AudioTools/AudioLibs/AudioSTK.h" // install https://github.com/pschatzmann/Arduino-STK -#include "AudioTools/AudioLibs/AudioBoardStream.h" // install https://github.com/pschatzmann/arduino-audio-driver +#include "AudioLibs/AudioKit.h" +#include "AudioLibs/AudioSTK.h" MemoryLoop mloop("crashcym.raw", crashcym_raw, crashcym_raw_len); STKStream in(mloop); -AudioBoardStream out(AudioKitEs8388V1); +AudioKitStream out; StreamCopy copier(out, in); void setup() { diff --git a/examples/examples-dsp/examples-stk/streams-stk_myinstrument-audiokit/MyFirstInstrument.h b/examples/examples-stk/streams-stk_myinstrument-audiokit/MyFirstInstrument.h similarity index 90% rename from examples/examples-dsp/examples-stk/streams-stk_myinstrument-audiokit/MyFirstInstrument.h rename to examples/examples-stk/streams-stk_myinstrument-audiokit/MyFirstInstrument.h index 6279b9a01c..61c45ec13f 100644 --- a/examples/examples-dsp/examples-stk/streams-stk_myinstrument-audiokit/MyFirstInstrument.h +++ b/examples/examples-stk/streams-stk_myinstrument-audiokit/MyFirstInstrument.h @@ -1,5 +1,5 @@ #pragma once -#include "StkAll.h" +#include "StkAllh.h" /** * @brief Demo how you can compose your own instrument @@ -23,9 +23,9 @@ class MyFirstInstrument : public Instrmnt { adsr.keyOff(); } - float tick( unsigned int channel = 0) override { + float tick() { return echo.tick(wave.tick()) * adsr.tick(); - }; + } protected: stk::SineWave wave; diff --git a/examples/examples-dsp/examples-stk/streams-stk_myinstrument-audiokit/streams-stk_myinstrument-audiokit.ino b/examples/examples-stk/streams-stk_myinstrument-audiokit/streams-stk_myinstrument-audiokit.ino similarity index 85% rename from examples/examples-dsp/examples-stk/streams-stk_myinstrument-audiokit/streams-stk_myinstrument-audiokit.ino rename to examples/examples-stk/streams-stk_myinstrument-audiokit/streams-stk_myinstrument-audiokit.ino index f824b3e992..d71d60109e 100644 --- a/examples/examples-dsp/examples-stk/streams-stk_myinstrument-audiokit/streams-stk_myinstrument-audiokit.ino +++ b/examples/examples-stk/streams-stk_myinstrument-audiokit/streams-stk_myinstrument-audiokit.ino @@ -5,13 +5,13 @@ * @copyright Copyright (c) 2021 */ #include "AudioTools.h" -#include "AudioTools/AudioLibs/AudioSTK.h" // install https://github.com/pschatzmann/Arduino-STK -#include "AudioTools/AudioLibs/AudioBoardStream.h" // install https://github.com/pschatzmann/arduino-audio-driver +#include "AudioLibs/AudioSTK.h" +#include "AudioLibs/AudioKit.h" #include "MyFirstInstrument.h" MyFirstInstrument instrument; STKStream in(instrument); -AudioBoardStream out(AudioKitEs8388V1); +AudioKitStream out; StreamCopy copier(out, in); MusicalNotes notes; diff --git a/examples/examples-dsp/examples-stk/streams-stk_sine-audiokit/streams-stk_sine-audiokit.ino b/examples/examples-stk/streams-stk_sine-audiokit/streams-stk_sine-audiokit.ino similarity index 84% rename from examples/examples-dsp/examples-stk/streams-stk_sine-audiokit/streams-stk_sine-audiokit.ino rename to examples/examples-stk/streams-stk_sine-audiokit/streams-stk_sine-audiokit.ino index fb3737f0ef..00353bef4e 100644 --- a/examples/examples-dsp/examples-stk/streams-stk_sine-audiokit/streams-stk_sine-audiokit.ino +++ b/examples/examples-stk/streams-stk_sine-audiokit/streams-stk_sine-audiokit.ino @@ -7,12 +7,12 @@ #include "SD_MMC.h" #include "AudioTools.h" -#include "AudioTools/AudioLibs/AudioSTK.h" // install https://github.com/pschatzmann/Arduino-STK -#include "AudioTools/AudioLibs/AudioBoardStream.h" // install https://github.com/pschatzmann/arduino-audio-driver +#include "AudioLibs/AudioSTK.h" +#include "AudioLibs/AudioKit.h" SineWave sine; STKStream in(sine); -AudioBoardStream out(AudioKitEs8388V1); +AudioKitStream out; StreamCopy copier(out, in); MusicalNotes notes; diff --git a/examples/examples-dsp/examples-stk/streams-stk_synth-audiokit/README.md b/examples/examples-stk/streams-stk_synth-audiokit/README.md similarity index 91% rename from examples/examples-dsp/examples-stk/streams-stk_synth-audiokit/README.md rename to examples/examples-stk/streams-stk_synth-audiokit/README.md index 8e73083302..af21087241 100644 --- a/examples/examples-dsp/examples-stk/streams-stk_synth-audiokit/README.md +++ b/examples/examples-stk/streams-stk_synth-audiokit/README.md @@ -10,6 +10,6 @@ For [further info see my blog](https://www.pschatzmann.ch/home/2021/12/21/ai-thi ### Dependencies - https://github.com/pschatzmann/arduino-audio-tools -- https://github.com/pschatzmann/arduino-audio-driver +- https://github.com/pschatzmann/arduino-audiokit - https://github.com/pschatzmann/arduino-midi - https://github.com/pschatzmann/Arduino-STK diff --git a/examples/examples-dsp/examples-stk/streams-stk_synth-audiokit/streams-stk_synth-audiokit.ino b/examples/examples-stk/streams-stk_synth-audiokit/streams-stk_synth-audiokit.ino similarity index 59% rename from examples/examples-dsp/examples-stk/streams-stk_synth-audiokit/streams-stk_synth-audiokit.ino rename to examples/examples-stk/streams-stk_synth-audiokit/streams-stk_synth-audiokit.ino index ba02e7cfea..2474be2caf 100644 --- a/examples/examples-dsp/examples-stk/streams-stk_synth-audiokit/streams-stk_synth-audiokit.ino +++ b/examples/examples-stk/streams-stk_synth-audiokit/streams-stk_synth-audiokit.ino @@ -5,10 +5,10 @@ * @copyright Copyright (c) 2021 */ #include "AudioTools.h" -#include "AudioTools/AudioLibs/AudioBoardStream.h" // install https://github.com/pschatzmann/arduino-audio-driver -#include "StkAll.h" // install https://github.com/pschatzmann/Arduino-STK +#include "AudioLibs/AudioKit.h" +#include "StkAll.h" -AudioBoardStream kit(AudioKitEs8388V1); +AudioKitStream kit; Clarinet clarinet(440); Voicer voicer; ArdStreamOut output(&kit); @@ -16,12 +16,12 @@ float noteAmplitude = 128; int group = 0; void actionKeyOn(bool active, int pin, void* ptr){ - float note = *((float*)ptr); + int note = *((int*)ptr); voicer.noteOn(note, noteAmplitude, group); } void actionKeyOff(bool active, int pin, void* ptr){ - float note = *((float*)ptr); + int note = *((int*)ptr); voicer.noteOff(note, noteAmplitude, group); } @@ -30,12 +30,12 @@ void setupActions(){ // assign buttons to notes auto act_low = AudioActions::ActiveLow; static int note[] = {48,50,52,53,55,57}; // midi keys - kit.audioActions().add(kit.getKey(1), actionKeyOn, actionKeyOff, act_low, &(note[0])); // C3 - kit.audioActions().add(kit.getKey(2), actionKeyOn, actionKeyOff, act_low, &(note[1])); // D3 - kit.audioActions().add(kit.getKey(3), actionKeyOn, actionKeyOff, act_low, &(note[2])); // E3 - kit.audioActions().add(kit.getKey(4), actionKeyOn, actionKeyOff, act_low, &(note[3])); // F3 - kit.audioActions().add(kit.getKey(5), actionKeyOn, actionKeyOff, act_low, &(note[4])); // G3 - kit.audioActions().add(kit.getKey(6), actionKeyOn, actionKeyOff, act_low, &(note[5])); // A3 + kit.audioActions().add(PIN_KEY1, actionKeyOn, actionKeyOff, act_low, &(note[0])); // C3 + kit.audioActions().add(PIN_KEY2, actionKeyOn, actionKeyOff, act_low, &(note[1])); // D3 + kit.audioActions().add(PIN_KEY3, actionKeyOn, actionKeyOff, act_low, &(note[2])); // E3 + kit.audioActions().add(PIN_KEY4, actionKeyOn, actionKeyOff, act_low, &(note[3])); // F3 + kit.audioActions().add(PIN_KEY5, actionKeyOn, actionKeyOff, act_low, &(note[4])); // G3 + kit.audioActions().add(PIN_KEY6, actionKeyOn, actionKeyOff, act_low, &(note[5])); // A3 } void setup() { diff --git a/examples/examples-communication/a2dp/streams-a2dp-serial/README.md b/examples/examples-stream/streams-a2dp-serial/README.md similarity index 100% rename from examples/examples-communication/a2dp/streams-a2dp-serial/README.md rename to examples/examples-stream/streams-a2dp-serial/README.md diff --git a/examples/examples-communication/a2dp/streams-a2dp-serial/streams-a2dp-serial.ino b/examples/examples-stream/streams-a2dp-serial/streams-a2dp-serial.ino similarity index 93% rename from examples/examples-communication/a2dp/streams-a2dp-serial/streams-a2dp-serial.ino rename to examples/examples-stream/streams-a2dp-serial/streams-a2dp-serial.ino index fd040ad589..10e8dab2d9 100644 --- a/examples/examples-communication/a2dp/streams-a2dp-serial/streams-a2dp-serial.ino +++ b/examples/examples-stream/streams-a2dp-serial/streams-a2dp-serial.ino @@ -9,7 +9,7 @@ */ #include "AudioTools.h" -#include "AudioTools/AudioLibs/A2DPStream.h" +#include "AudioLibs/AudioA2DP.h" A2DPStream in; diff --git a/examples/examples-stream/streams-adc-i2s/streams-adc-i2s.ino b/examples/examples-stream/streams-adc-i2s/streams-adc-i2s.ino index 2f42242d4f..1bed01f024 100644 --- a/examples/examples-stream/streams-adc-i2s/streams-adc-i2s.ino +++ b/examples/examples-stream/streams-adc-i2s/streams-adc-i2s.ino @@ -13,11 +13,12 @@ AudioInfo info(44100, 2, 16); AnalogAudioStream in; I2SStream out; StreamCopy copier(out, in); // copy in to out +ConverterAutoCenter converter; // Arduino Setup void setup(void) { Serial.begin(115200); - AudioToolsLogger.begin(Serial, AudioToolsLogLevel::Info); + AudioLogger::instance().begin(Serial, AudioLogger::Info); // RX automatically uses port 0 with pin GPIO34 auto cfgRx = in.defaultConfig(RX_MODE); @@ -33,5 +34,5 @@ void setup(void) { // Arduino loop - copy data void loop() { - copier.copy(); + copier.copy(converter); } \ No newline at end of file diff --git a/examples/examples-stream/streams-adc-serial/README.md b/examples/examples-stream/streams-adc-serial/README.md index 9ae4dc1a64..cbcc7c0297 100644 --- a/examples/examples-stream/streams-adc-serial/README.md +++ b/examples/examples-stream/streams-adc-serial/README.md @@ -12,13 +12,13 @@ Here is the result on the Arduino Serial Plotter: ### Analog Input: -| ADC | ESP32 | UNO R4 -| --------| --------|------- -| VCC | 3.3V | 3.3V -| GND | GND | GND -| OUT | GPIO34 | A0 +| ADC | ESP32 +| --------| --------------- +| VCC | 3.3V +| GND | GND +| OUT | GPIO34 For the input I was using a MCP6022 Microphone Sensor. -Plaese note that the signal that we receive from the ADC might need to be adjusted so that it is oscillating around 0. +Plaese note that the signal that we receive from the ADC needs to be adjusted so that it is oscillating around 0. ![MCP6022](https://pschatzmann.github.io/Resources/img/mcp6022.jpeg) diff --git a/examples/examples-stream/streams-adc-serial/streams-adc-serial.ino b/examples/examples-stream/streams-adc-serial/streams-adc-serial.ino index 73ed2e1c60..48004608bd 100644 --- a/examples/examples-stream/streams-adc-serial/streams-adc-serial.ino +++ b/examples/examples-stream/streams-adc-serial/streams-adc-serial.ino @@ -12,16 +12,16 @@ AnalogAudioStream in; AudioInfo info(8000, 1, 16); CsvOutput out(Serial); // ASCII output stream -StreamCopy copier(out, in); +StreamCopy copier(out, in); // copy i2sStream to CsvOutput +ConverterAutoCenter center(2); // set avg to 0 // Arduino Setup void setup(void) { Serial.begin(115200); - AudioToolsLogger.begin(Serial, AudioToolsLogLevel::Warning); + AudioLogger::instance().begin(Serial, AudioLogger::Info); + // RX automatically uses port 0 with pins GPIO34,GPIO35 auto cfgRx = in.defaultConfig(RX_MODE); - // cfgRx.start_pin = A1; // optinally define pin - // cfgRx.is_auto_center_read = true; cfgRx.copyFrom(info); in.begin(cfgRx); @@ -32,5 +32,5 @@ void setup(void) { // Arduino loop - copy data void loop() { - copier.copy(); // -} + copier.copy(center); +} \ No newline at end of file diff --git a/examples/examples-stream/streams-adsr-i2s/streams-adsr-i2s.ino b/examples/examples-stream/streams-adsr-i2s/streams-adsr-i2s.ino index b9bdf2b777..f7eb548935 100644 --- a/examples/examples-stream/streams-adsr-i2s/streams-adsr-i2s.ino +++ b/examples/examples-stream/streams-adsr-i2s/streams-adsr-i2s.ino @@ -6,9 +6,9 @@ * */ #include "AudioTools.h" -//#include "AudioTools/AudioLibs/AudioBoardStream.h" +//#include "AudioLibs/AudioKit.h" -I2SStream i2s; //AudioBoardStream +I2SStream i2s; //AudioKitStream SineWaveGenerator sine; GeneratedSoundStream stream(sine); AudioEffectStream effects(stream); @@ -18,7 +18,7 @@ uint64_t time_on; uint64_t time_off; -void actionKeyOn(float note){ +void actionKeyOn(int note){ Serial.println("KeyOn"); sine.setFrequency(note); adsr.keyOn(); diff --git a/examples/examples-communication/a2dp/streams-generator-a2dp/README.md b/examples/examples-stream/streams-generator-a2dp/README.md similarity index 83% rename from examples/examples-communication/a2dp/streams-generator-a2dp/README.md rename to examples/examples-stream/streams-generator-a2dp/README.md index e92da8503e..8186e2ecca 100644 --- a/examples/examples-communication/a2dp/streams-generator-a2dp/README.md +++ b/examples/examples-stream/streams-generator-a2dp/README.md @@ -5,6 +5,3 @@ We can use the GeneratedSoundStream class together with a SoundGenerator class. To test the output I'm using this generated signal and write it to A2DP (e.g. a Bluetooth Speaker). -### Dependencies - -- https://github.com/pschatzmann/ESP32-A2DP.git diff --git a/examples/examples-communication/a2dp/streams-generator-a2dp/streams-generator-a2dp.ino b/examples/examples-stream/streams-generator-a2dp/streams-generator-a2dp.ino similarity index 90% rename from examples/examples-communication/a2dp/streams-generator-a2dp/streams-generator-a2dp.ino rename to examples/examples-stream/streams-generator-a2dp/streams-generator-a2dp.ino index fc14b57d8c..c53b717543 100644 --- a/examples/examples-communication/a2dp/streams-generator-a2dp/streams-generator-a2dp.ino +++ b/examples/examples-stream/streams-generator-a2dp/streams-generator-a2dp.ino @@ -9,7 +9,7 @@ */ #include "AudioTools.h" -#include "AudioTools/AudioLibs/A2DPStream.h" +#include "AudioLibs/AudioA2DP.h" const char* name = "LEXON MINO L"; // Replace with your device name AudioInfo info(44100, 2, 16); @@ -21,7 +21,7 @@ StreamCopy copier(out, in); // copy in to out // Arduino Setup void setup(void) { Serial.begin(115200); - AudioToolsLogger.begin(Serial, AudioToolsLogLevel::Warning); + AudioLogger::instance().begin(Serial, AudioLogger::Warning); // set the frequency sineWave.setFrequency(N_B4); @@ -29,7 +29,7 @@ void setup(void) { // Setup sine wave auto cfg = in.defaultConfig(); cfg.copyFrom(info); - in.addNotifyAudioChange(out); + in.setNotifyAudioChange(out); in.begin(cfg); // We send the test signal via A2DP - so we conect to the MyMusic Bluetooth Speaker diff --git a/examples/examples-stream/streams-generator-analog/README.md b/examples/examples-stream/streams-generator-analog/README.md index 8da1940dec..9e35030498 100644 --- a/examples/examples-stream/streams-generator-analog/README.md +++ b/examples/examples-stream/streams-generator-analog/README.md @@ -3,37 +3,23 @@ This is a simple basic test for the ESP32 __analog output__ using I2S. We just send a generated sine wave and expect to hear a clean signal. -Please note the log level should be set to Warning so that there is no disturbing output! - -Make sure that you do not use any noisy power supply! - -### Output Device: Earphones - -You can also use some simple earphones - -![DAC](https://pschatzmann.github.io/Resources/img/earphones.jpg) - -On the ESP32 the stereo output is on the Pins GPIO25 and GPIO26 - -| Earphone| ESP32 | UNO R4 | -| --------| -----------------|---------| -| Left | GPIO25 | A0 | -| Righ | GPIO25 | GND | -| GND | GND | GND | - +Please note the log level should be set so that there is no disturbing output! + ### Output Device: Piezo Electric Element To test the output I am using a piezo electric element ![DAC](https://pschatzmann.github.io/Resources/img/piezo.jpeg) +It should also be possible to connect a headphone to the output pins... + -On the ESP32 the stereo output is on the Pins GPIO25 and GPIO26 +On the ESP32 the output is on the Pins GPIO26 and GPIO27 -| PIEZO | ESP32 | UNO R4 | -| --------| -----------------|---------| -| + | GPIO25 | A0 | -| - | GND | GND | +| PIEZO | ESP32 +| --------| --------------- +| + | GPIO25 / GPIO26 +| - | GND diff --git a/examples/examples-stream/streams-generator-analog/streams-generator-analog.ino b/examples/examples-stream/streams-generator-analog/streams-generator-analog.ino index 4a5df458cc..332c95951c 100644 --- a/examples/examples-stream/streams-generator-analog/streams-generator-analog.ino +++ b/examples/examples-stream/streams-generator-analog/streams-generator-analog.ino @@ -8,9 +8,10 @@ #include "AudioTools.h" -AudioInfo info(8000, 1, 16); -SineWaveGenerator sineWave(32000); // subclass of SoundGenerator with max amplitude of 32000 -GeneratedSoundStream sound(sineWave); // Stream generated from sine wave +typedef int16_t sound_t; // sound will be represented as int16_t (with 2 bytes) +AudioInfo info(44100, 2, 16); +SineWaveGenerator sineWave(32000); // subclass of SoundGenerator with max amplitude of 32000 +GeneratedSoundStream sound(sineWave); // Stream generated from sine wave AnalogAudioStream out; StreamCopy copier(out, sound); // copies sound into i2s @@ -18,7 +19,7 @@ StreamCopy copier(out, sound); // copies sound into void setup(void) { // Open Serial Serial.begin(115200); - AudioToolsLogger.begin(Serial, AudioToolsLogLevel::Info); + AudioLogger::instance().begin(Serial, AudioLogger::Info); // start the analog output auto config = out.defaultConfig(TX_MODE); diff --git a/examples/examples-stream/streams-generator-bin-serial/streams-generator-bin-serial.ino b/examples/examples-stream/streams-generator-bin-serial/streams-generator-bin-serial.ino deleted file mode 100644 index 9136d64403..0000000000 --- a/examples/examples-stream/streams-generator-bin-serial/streams-generator-bin-serial.ino +++ /dev/null @@ -1,34 +0,0 @@ -/** - * @file streams-generator-bin-serial.ino - * @author Urs Utzinger - * @brief Reduce samples by binning; which is summing consecutive samples and optionally dividing by the number of samples summed. - * @copyright GPLv3 - **/ - -#include "AudioTools.h" - -AudioInfo info(44100, 2, 16); -SineWaveGenerator sineWave(16000); // subclass of SoundGenerator with max amplitude of 32000 -GeneratedSoundStream sound(sineWave); // stream generated from sine wave -Bin binner(64, 2, true, 16); // configure binning with binsize, channels, enable averaging, bits per channel -ConverterStream binning(sound, binner); // pipe the sound to the binner -CsvOutput out(Serial); // serial output -StreamCopy copier(out, binning); // stream the binner output to serial port - -// Arduino Setup -void setup(void) { - // Open Serial - Serial.begin(115200); - AudioToolsLogger.begin(Serial, AudioToolsLogLevel::Warning); - - // Define CSV Output - out.begin(info); - - // Setup sine wave - sineWave.begin(info, N_B4); -} - -// Arduino loop - copy sound to out with conversion -void loop() { - copier.copy(); -} \ No newline at end of file diff --git a/examples/examples-stream/streams-generator-formatconverter-i2s/streams-generator-formatconverter-i2s.ino b/examples/examples-stream/streams-generator-formatconverter-i2s/streams-generator-formatconverter-i2s.ino index 7d3348a44d..4aa8bf069a 100644 --- a/examples/examples-stream/streams-generator-formatconverter-i2s/streams-generator-formatconverter-i2s.ino +++ b/examples/examples-stream/streams-generator-formatconverter-i2s/streams-generator-formatconverter-i2s.ino @@ -8,13 +8,13 @@ */ #include "AudioTools.h" -#include "AudioTools/AudioLibs/AudioBoardStream.h" +#include "AudioLibs/AudioKit.h" AudioInfo from(32000,2,32); AudioInfo to(16000,1,16); SineWaveGenerator sineWave; GeneratedSoundStream sound(sineWave); // Stream generated from sine wave -I2SStream out; // or any other e.g. AudioBoardStream, CsvOutput out(Serial); +I2SStream out; // or any other e.g. AudioKitStream, CsvOutput out(Serial); FormatConverterStream converter(sound); // or use converter(out) StreamCopy copier(out, converter); // copier(converter, sound); @@ -22,7 +22,7 @@ StreamCopy copier(out, converter); // copier(converter, sound); void setup(void) { // Open Serial Serial.begin(115200); - AudioToolsLogger.begin(Serial, AudioToolsLogLevel::Warning); + AudioLogger::instance().begin(Serial, AudioLogger::Warning); // Setup Input sineWave.begin(from, N_B4); diff --git a/examples/examples-stream/streams-generator-i2s/streams-generator-i2s.ino b/examples/examples-stream/streams-generator-i2s/streams-generator-i2s.ino index 51516e9122..f735f4f9d9 100644 --- a/examples/examples-stream/streams-generator-i2s/streams-generator-i2s.ino +++ b/examples/examples-stream/streams-generator-i2s/streams-generator-i2s.ino @@ -18,7 +18,7 @@ void setup(void) { // Open Serial Serial.begin(115200); while(!Serial); - AudioToolsLogger.begin(Serial, AudioToolsLogLevel::Info); + AudioLogger::instance().begin(Serial, AudioLogger::Info); // start I2S Serial.println("starting I2S..."); diff --git a/examples/examples-stream/streams-generator-merge-pwm/streams-generator-merge-pwm.ino b/examples/examples-stream/streams-generator-merge-pwm/streams-generator-merge-pwm.ino deleted file mode 100644 index d39708df13..0000000000 --- a/examples/examples-stream/streams-generator-merge-pwm/streams-generator-merge-pwm.ino +++ /dev/null @@ -1,50 +0,0 @@ -/** - * @file streams-generator-merge-pwm.ino - * @brief merge 2 mono signals into 1 stereo signal - * @author Phil Schatzmann - * @copyright GPLv3 - */ - -#include "AudioTools.h" - -AudioInfo info_in(8000, 1, 16); -AudioInfo info_out(8000, 2, 16); -SineWaveGenerator sineWave1(32000); // subclass of SoundGenerator with max amplitude of 32000 -GeneratedSoundStream sound1(sineWave1); // Stream generated from sine wave -SineWaveGenerator sineWave2(32000); // subclass of SoundGenerator with max amplitude of 32000 -GeneratedSoundStream sound2(sineWave2); // Stream generated from sine wave -InputMerge imerge; -PWMAudioOutput pwm; -StreamCopy copier(pwm, imerge); // copy in to out - - -void setup() { - Serial.begin(115200); - AudioToolsLogger.begin(Serial, AudioToolsLogLevel::Warning); - - // setup sine 2 mono sine waves - sineWave1.begin(info_in, N_B4); - sineWave2.begin(info_in, N_B5); - - // merge input to stereo - imerge.add(sound1, 1); - imerge.add(sound2, 1); - imerge.begin(info_out); - - // setup PWM output - auto config = pwm.defaultConfig(); - config.copyFrom(info_out); - //config.resolution = 8; // must be between 8 and 11 -> drives pwm frequency (8 is default) - // alternative 1 - //config.start_pin = 3; - // alternative 2 - //int pins[] = {3}; - // alternative 3 - Pins pins = {3, 4}; - config.setPins(pins); - pwm.begin(config); -} - -void loop(){ - copier.copy(); -} \ No newline at end of file diff --git a/examples/examples-stream/streams-generator-pwm/README.md b/examples/examples-stream/streams-generator-pwm/README.md index 93230c718b..2ec81c74ba 100644 --- a/examples/examples-stream/streams-generator-pwm/README.md +++ b/examples/examples-stream/streams-generator-pwm/README.md @@ -11,18 +11,13 @@ To test the output I am using a piezo electric element ![DAC](https://pschatzmann.github.io/Resources/img/piezo.jpeg) -You can also use some simple earphones - -![DAC](https://pschatzmann.github.io/Resources/img/earphones.jpg) - - It should also be possible to connect a headphone to the output pins... The pins depend on the Processor: -| PIEZO | ESP32 | RPI Pico | MBED | UNO R4 | -| --------| -------------|---------------|--------------|-------------| -| + | GPIO4/GPIO5 | GPIO2/GPIO3 | GPIO2/GPIO3 | GPIO2/GPIO4 | -| - | GND | GND | GND | GND | +| PIEZO | ESP32 | RPI Pico | MBED | +| --------| -------------|---------------|--------------| +| + | GPI4/GPIO5 | GPI2/GPIO3 | GPI2/GPI3 | +| - | GND | | | diff --git a/examples/examples-stream/streams-generator-pwm/streams-generator-pwm.ino b/examples/examples-stream/streams-generator-pwm/streams-generator-pwm.ino index f841830e30..8e57d66541 100644 --- a/examples/examples-stream/streams-generator-pwm/streams-generator-pwm.ino +++ b/examples/examples-stream/streams-generator-pwm/streams-generator-pwm.ino @@ -8,6 +8,7 @@ #include "AudioTools.h" +//Pins pins = {22, 23}; AudioInfo info(8000, 1, 16); SineWaveGenerator sineWave(32000); // subclass of SoundGenerator with max amplitude of 32000 GeneratedSoundStream sound(sineWave); // Stream generated from sine wave @@ -17,7 +18,7 @@ StreamCopy copier(pwm, sound); // copy in to out void setup() { Serial.begin(115200); - AudioToolsLogger.begin(Serial, AudioToolsLogLevel::Warning); + AudioLogger::instance().begin(Serial, AudioLogger::Warning); // setup sine wave sineWave.begin(info, N_B4); @@ -27,11 +28,9 @@ void setup() { config.copyFrom(info); //config.resolution = 8; // must be between 8 and 11 -> drives pwm frequency (8 is default) // alternative 1 + //config.channels = 2; // not necesarry because 2 is default //config.start_pin = 3; - // alternative 2 - //int pins[] = {3}; - // alternative 3 - //Pins pins = {3}; + // alternative 2: defines pins and channels //config.setPins(pins); pwm.begin(config); } diff --git a/examples/examples-stream/streams-generator-r2r/streams-generator-r2r.ino b/examples/examples-stream/streams-generator-r2r/streams-generator-r2r.ino deleted file mode 100644 index cc03429336..0000000000 --- a/examples/examples-stream/streams-generator-r2r/streams-generator-r2r.ino +++ /dev/null @@ -1,42 +0,0 @@ -/** - * @file streams-generator-r2r.ino - * @author Phil Schatzmann - * @brief Example for a self built resistor ladder DAC - * @copyright GPLv3 - */ - -#include "AudioTools.h" -#include "AudioTools/AudioLibs/R2ROutput.h" - -AudioInfo info(8000, 1, 16); -SineWaveGenerator sineWave; // subclass of SoundGenerator with max amplitude of 32000 -GeneratedSoundStream sound(sineWave); // Stream generated from sine wave -R2ROutput out; -StreamCopy copier(out, sound); // copies sound into i2s -const int pins1[] = {12,14,27,26,25,33,32, 35}; // ESP32 pins - -// Arduino Setup -void setup(void) { - // Open Serial - Serial.begin(115200); - while(!Serial); - AudioToolsLogger.begin(Serial, AudioToolsLogLevel::Info); - - // start I2S - Serial.println("starting R2R..."); - auto config = out.defaultConfig(); - config.copyFrom(info); - // 8 pins for 8 bit DAC for channel 1 - config.channel1_pins = pins1; - // channel 2 would be config.channel2_pins - out.begin(config); - - // Setup sine wave - sineWave.begin(info, N_B4); - Serial.println("started..."); -} - -// Arduino loop - copy sound to out -void loop() { - copier.copy(); -} diff --git a/examples/examples-stream/streams-generator-serial/streams-generator-serial.ino b/examples/examples-stream/streams-generator-serial/streams-generator-serial.ino index 4933fb7b3e..8f91ab3374 100644 --- a/examples/examples-stream/streams-generator-serial/streams-generator-serial.ino +++ b/examples/examples-stream/streams-generator-serial/streams-generator-serial.ino @@ -8,7 +8,7 @@ #include "AudioTools.h" -AudioInfo audio_info(44100, 2, 16); +AudioInfo info(44100, 2, 16); SineWaveGenerator sineWave(32000); // subclass of SoundGenerator with max amplitude of 32000 GeneratedSoundStream sound(sineWave); // Stream generated from sine wave CsvOutput out(Serial); @@ -18,13 +18,13 @@ StreamCopy copier(out, sound); // copies sound to ou void setup(void) { // Open Serial Serial.begin(115200); - //AudioToolsLogger.begin(Serial, AudioToolsLogLevel::Warning); + AudioLogger::instance().begin(Serial, AudioLogger::Warning); // Define CSV Output - out.begin(audio_info); + out.begin(info); // Setup sine wave - sineWave.begin(audio_info, N_B4); + sineWave.begin(info, N_B4); Serial.println("started..."); } diff --git a/examples/examples-stream/streams-generator-spdif/AudioConfigLocal.h b/examples/examples-stream/streams-generator-spdif/AudioConfigLocal.h new file mode 100644 index 0000000000..bce5136f8e --- /dev/null +++ b/examples/examples-stream/streams-generator-spdif/AudioConfigLocal.h @@ -0,0 +1,5 @@ +#pragma once + +#define I2S_BUFFER_COUNT 8 +#define I2S_BUFFER_SIZE 256 +#define DEFAULT_BUFFER_SIZE 2048 \ No newline at end of file diff --git a/examples/examples-stream/streams-generator-spdif/streams-generator-spdif.ino b/examples/examples-stream/streams-generator-spdif/streams-generator-spdif.ino index b3dede3b3a..342176852f 100644 --- a/examples/examples-stream/streams-generator-spdif/streams-generator-spdif.ino +++ b/examples/examples-stream/streams-generator-spdif/streams-generator-spdif.ino @@ -6,29 +6,28 @@ * @copyright GPLv3 */ +#include "AudioConfigLocal.h" #include "AudioTools.h" -#include "AudioTools/AudioLibs/SPDIFOutput.h" + +typedef int16_t sound_t; // sound will be represented as int16_t (with 2 bytes) AudioInfo info(44100, 2, 16); -SineWaveGenerator sineWave(32000); // subclass of SoundGenerator with max amplitude of 32000 -GeneratedSoundStream sound(sineWave); // Stream generated from sine wave +SineWaveGenerator sineWave(32000); // subclass of SoundGenerator with max amplitude of 32000 +GeneratedSoundStream sound(sineWave); // Stream generated from sine wave SPDIFOutput out; -StreamCopy copier(out, sound, 2048); // copies sound into i2s +StreamCopy copier(out, sound); // copies sound into i2s // Arduino Setup void setup(void) { // Open Serial Serial.begin(115200); - AudioToolsLogger.begin(Serial, AudioToolsLogLevel::Info); + AudioLogger::instance().begin(Serial, AudioLogger::Info); // start I2S Serial.println("starting SPDIF..."); auto config = out.defaultConfig(); config.copyFrom(info); config.pin_data = 23; - config.buffer_size = 384; - config.buffer_count = 8; - out.begin(config); // Setup sine wave diff --git a/examples/examples-stream/streams-generator-timedstream-serial/streams-generator-timedstream-serial.ino b/examples/examples-stream/streams-generator-timedstream-serial/streams-generator-timedstream-serial.ino deleted file mode 100644 index e6e9b50e59..0000000000 --- a/examples/examples-stream/streams-generator-timedstream-serial/streams-generator-timedstream-serial.ino +++ /dev/null @@ -1,34 +0,0 @@ - -/** - * @file streams-generator-serial.ino - * @author Phil Schatzmann - * @brief Example how to limit the output with a timed stream: generate audio for 1 second - * @copyright GPLv3 - **/ - -#include "AudioTools.h" - -AudioInfo info(8000, 1, 16); -SineWaveGenerator sineWave(32000); -GeneratedSoundStream sound(sineWave); -TimedStream timed(sound, 0, 1); -CsvOutput out(Serial); -StreamCopy copier(out, timed); // copies sound to out - -// Arduino Setup -void setup(void) { - // Open Serial - Serial.begin(115200); - AudioToolsLogger.begin(Serial, AudioToolsLogLevel::Info); - - sineWave.begin(info, N_B4); - - timed.begin(info); - - out.begin(info); -} - -// Arduino loop - copy sound to out -void loop() { - copier.copy(); -} \ No newline at end of file diff --git a/examples/examples-stream/streams-generator-volume/streams-generator-volume.ino b/examples/examples-stream/streams-generator-volume/streams-generator-volume.ino index 457509f049..f94ea3946b 100644 --- a/examples/examples-stream/streams-generator-volume/streams-generator-volume.ino +++ b/examples/examples-stream/streams-generator-volume/streams-generator-volume.ino @@ -10,17 +10,17 @@ AudioInfo info(44100, 2, 16); SineWaveGenerator sineWave(32000); // subclass of SoundGenerator with max amplitude of 32000 GeneratedSoundStream sound(sineWave); // Stream generated from sine wave -VolumeMeter out; -StreamCopy copier(out, sound, 1024*2); // copies sound into VolumeMeter +VolumeOutput out; +StreamCopy copier(out, sound, 1024*2); // copies sound into VolumeOutput // Arduino Setup void setup(void) { // Open Serial Serial.begin(115200); while(!Serial); - AudioToolsLogger.begin(Serial, AudioToolsLogLevel::Warning); + AudioLogger::instance().begin(Serial, AudioLogger::Warning); - // start Volume Meter + // start Volume Output out.begin(info); // Setup sine wave diff --git a/examples/examples-stream/streams-generator-wm8960/streams-generator-wm8960.ino b/examples/examples-stream/streams-generator-wm8960/streams-generator-wm8960.ino index 5e54936b48..a13541a0f0 100644 --- a/examples/examples-stream/streams-generator-wm8960/streams-generator-wm8960.ino +++ b/examples/examples-stream/streams-generator-wm8960/streams-generator-wm8960.ino @@ -6,7 +6,7 @@ */ #include "AudioTools.h" -#include "AudioTools/AudioLibs/WM8960Stream.h" +#include "AudioLibs/WM8960Stream.h" AudioInfo info(44100, 2, 16); SineWaveGenerator sineWave(32000); // subclass of SoundGenerator with max amplitude of 32000 @@ -19,7 +19,7 @@ void setup(void) { // Open Serial Serial.begin(115200); while(!Serial); - AudioToolsLogger.begin(Serial, AudioToolsLogLevel::Warning); + AudioLogger::instance().begin(Serial, AudioLogger::Warning); // setup wire on pins 19 and 21 Wire.begin(19, 21); diff --git a/examples/examples-stream/streams-generator_fromarray-analog/streams-generator_fromarray-analog.ino b/examples/examples-stream/streams-generator_fromarray-analog/streams-generator_fromarray-analog.ino index 1a3842138d..94856bdd82 100644 --- a/examples/examples-stream/streams-generator_fromarray-analog/streams-generator_fromarray-analog.ino +++ b/examples/examples-stream/streams-generator_fromarray-analog/streams-generator_fromarray-analog.ino @@ -9,7 +9,7 @@ * */ #include "AudioTools.h" -#include "AudioTools/AudioLibs/AudioBoardStream.h" +#include "AudioLibs/AudioKit.h" AudioInfo info(44100, 1, 16); int16_t sine_array[] = {0, 4560, 9031, 13327, 17363, 21062, 24350, 27165, 29450, 31163, 32269, 32747, 32587, 31793, 30381, 28377, 25820, 22761, 19259, 15383, 11206, 6812, 2285, -2285, -6812, -11206, -15383, -19259, -22761, -25820, -28377, -30381, -31793, -32587, -32747, -32269, -31163, -29450, -27165, -24350, -21062, -17363, -13327, -9031, -4560 }; @@ -22,7 +22,7 @@ StreamCopy copier(out, sound); // copies sound into void setup(void) { // Open Serial Serial.begin(115200); - AudioToolsLogger.begin(Serial, AudioToolsLogLevel::Warning); + AudioLogger::instance().begin(Serial, AudioLogger::Warning); // start I2S Serial.println("starting I2S..."); diff --git a/examples/examples-communication/a2dp/streams-i2s-a2dp/README.md b/examples/examples-stream/streams-i2s-a2dp/README.md similarity index 100% rename from examples/examples-communication/a2dp/streams-i2s-a2dp/README.md rename to examples/examples-stream/streams-i2s-a2dp/README.md diff --git a/examples/examples-communication/a2dp/streams-i2s-a2dp/streams-i2s-a2dp.ino b/examples/examples-stream/streams-i2s-a2dp/streams-i2s-a2dp.ino similarity index 77% rename from examples/examples-communication/a2dp/streams-i2s-a2dp/streams-i2s-a2dp.ino rename to examples/examples-stream/streams-i2s-a2dp/streams-i2s-a2dp.ino index fe90354e9b..eeb00ef098 100644 --- a/examples/examples-communication/a2dp/streams-i2s-a2dp/streams-i2s-a2dp.ino +++ b/examples/examples-stream/streams-i2s-a2dp/streams-i2s-a2dp.ino @@ -8,17 +8,21 @@ */ #include "AudioTools.h" -#include "AudioTools/AudioLibs/A2DPStream.h" +#include "AudioLibs/AudioA2DP.h" I2SStream i2sStream; // Access I2S as stream A2DPStream a2dpStream; // access A2DP as stream -StreamCopy copier(a2dpStream, i2sStream); // copy i2sStream to a2dpStream +VolumeStream volume(a2dpStream); +StreamCopy copier(volume, i2sStream); // copy i2sStream to a2dpStream ConverterFillLeftAndRight filler(LeftIsEmpty); // fill both channels // Arduino Setup void setup(void) { Serial.begin(115200); - AudioToolsLogger.begin(Serial, AudioToolsLogLevel::Info); + AudioLogger::instance().begin(Serial, AudioLogger::Info); + + // set intial volume + volume.setVolume(0.3); // start bluetooth Serial.println("starting A2DP..."); @@ -26,12 +30,9 @@ void setup(void) { cfgA2DP.name = "LEXON MINO L"; a2dpStream.begin(cfgA2DP); - // set intial volume - a2dpStream.setVolume(0.3); - // start i2s input with default configuration Serial.println("starting I2S..."); - a2dpStream.addNotifyAudioChange(i2sStream); // i2s is using the info from a2dp + a2dpStream.setNotifyAudioChange(i2sStream); // i2s is using the info from a2dp i2sStream.begin(i2sStream.defaultConfig(RX_MODE)); } @@ -40,4 +41,4 @@ void setup(void) { void loop() { // copier.copy(filler); copier.copy(); -} +} \ No newline at end of file diff --git a/examples/examples-stream/streams-i2s-filter-i2s/streams-i2s-filter-i2s.ino b/examples/examples-stream/streams-i2s-filter-i2s/streams-i2s-filter-i2s.ino index e8aeb47c35..33614b2386 100644 --- a/examples/examples-stream/streams-i2s-filter-i2s/streams-i2s-filter-i2s.ino +++ b/examples/examples-stream/streams-i2s-filter-i2s/streams-i2s-filter-i2s.ino @@ -12,8 +12,8 @@ I2SStream in; I2SStream out; // copy filtered values -FilteredStream filtered(in, info.channels); // Defiles the filter as BaseConverter -StreamCopy copier(out, filtered); // copies sound into i2s +FilteredStream filtered(in, channels); // Defiles the filter as BaseConverter +StreamCopy copier(out, filtered); // copies sound into i2s // define FIR filter parameters float coef[] = { 0.021, 0.096, 0.146, 0.096, 0.021}; @@ -24,7 +24,7 @@ void setup(void) { // Open Serial Serial.begin(115200); // change to Warning to improve the quality - AudioToolsLogger.begin(Serial, AudioToolsLogLevel::Info); + AudioLogger::instance().begin(Serial, AudioLogger::Info); // setup filters for all available channels filtered.setFilter(0, new FIR(coef)); diff --git a/examples/examples-stream/streams-i2s-i2s-2/streams-i2s-i2s-2.ino b/examples/examples-stream/streams-i2s-i2s-2/streams-i2s-i2s-2.ino index 53fd5c9779..05bcbb1f5a 100644 --- a/examples/examples-stream/streams-i2s-i2s-2/streams-i2s-i2s-2.ino +++ b/examples/examples-stream/streams-i2s-i2s-2/streams-i2s-i2s-2.ino @@ -18,7 +18,7 @@ void setup(void) { // Open Serial Serial.begin(115200); // change to Warning to improve the quality - AudioToolsLogger.begin(Serial, AudioToolsLogLevel::Info); + AudioLogger::instance().begin(Serial, AudioLogger::Info); // start I2S in Serial.println("starting I2S..."); diff --git a/examples/examples-stream/streams-i2s-i2s/streams-i2s-i2s.ino b/examples/examples-stream/streams-i2s-i2s/streams-i2s-i2s.ino index 321d0b4495..f5c25d6a80 100644 --- a/examples/examples-stream/streams-i2s-i2s/streams-i2s-i2s.ino +++ b/examples/examples-stream/streams-i2s-i2s/streams-i2s-i2s.ino @@ -17,7 +17,7 @@ void setup(void) { // Open Serial Serial.begin(115200); // change to Warning to improve the quality - AudioToolsLogger.begin(Serial, AudioToolsLogLevel::Info); + AudioLogger::instance().begin(Serial, AudioLogger::Info); // start I2S in Serial.println("starting I2S..."); diff --git a/examples/examples-stream/streams-i2s-serial/README.md b/examples/examples-stream/streams-i2s-serial/README.md index 7d5506fa27..f47e44f831 100644 --- a/examples/examples-stream/streams-i2s-serial/README.md +++ b/examples/examples-stream/streams-i2s-serial/README.md @@ -30,7 +30,7 @@ the ESP32 is acting as master. I recommend this sketch as a starting point for all I2S input. Just leave the masterclock out, because this is not needed for most input devices. If it is working with 32 bit you can try to change it to 16bits which most of the time works as well: -- CsvOutput csvOutput(Serial); +- CsvOutput csvStream(Serial); - cfg.bits_per_sample = 16; see streams-12s-serial_16_bit \ No newline at end of file diff --git a/examples/examples-stream/streams-i2s-serial/streams-i2s-serial.ino b/examples/examples-stream/streams-i2s-serial/streams-i2s-serial.ino index 3bf62a6fb5..3644eb2138 100644 --- a/examples/examples-stream/streams-i2s-serial/streams-i2s-serial.ino +++ b/examples/examples-stream/streams-i2s-serial/streams-i2s-serial.ino @@ -10,19 +10,21 @@ #include "AudioTools.h" -AudioInfo info(44100, 2, 32); +int channels = 2; I2SStream i2sStream; // Access I2S as stream -CsvOutput csvOutput(Serial); -StreamCopy copier(csvOutput, i2sStream); // copy i2sStream to csvOutput +CsvOutput csvStream(Serial, channels); +StreamCopy copier(csvStream, i2sStream); // copy i2sStream to csvStream // Arduino Setup void setup(void) { Serial.begin(115200); - AudioToolsLogger.begin(Serial, AudioToolsLogLevel::Info); + AudioLogger::instance().begin(Serial, AudioLogger::Info); auto cfg = i2sStream.defaultConfig(RX_MODE); - cfg.copyFrom(info); cfg.i2s_format = I2S_STD_FORMAT; // or try with I2S_LSB_FORMAT + cfg.bits_per_sample = 32; + cfg.channels = channels; + cfg.sample_rate = 44100; cfg.is_master = true; // this module nees a master clock if the ESP32 is master cfg.use_apll = false; // try with yes @@ -30,7 +32,7 @@ void setup(void) { i2sStream.begin(cfg); // make sure that we have the correct channels set up - csvOutput.begin(info); + csvStream.begin(cfg); } diff --git a/examples/examples-stream/streams-i2s-serial_16bit/streams-i2s-serial_16bit.ino b/examples/examples-stream/streams-i2s-serial_16bit/streams-i2s-serial_16bit.ino index 511f42c5b2..cd7a68c017 100644 --- a/examples/examples-stream/streams-i2s-serial_16bit/streams-i2s-serial_16bit.ino +++ b/examples/examples-stream/streams-i2s-serial_16bit/streams-i2s-serial_16bit.ino @@ -10,19 +10,21 @@ #include "AudioTools.h" -AudioInfo info(44100, 2, 16); + I2SStream i2sStream; // Access I2S as stream -CsvOutput csvOutput(Serial); -StreamCopy copier(csvOutput, i2sStream); // copy i2sStream to csvOutput +CsvOutput csvStream(Serial); +StreamCopy copier(csvStream, i2sStream); // copy i2sStream to csvStream // Arduino Setup void setup(void) { Serial.begin(115200); - AudioToolsLogger.begin(Serial, AudioToolsLogLevel::Info); + AudioLogger::instance().begin(Serial, AudioLogger::Info); auto cfg = i2sStream.defaultConfig(RX_MODE); - cfg.copyFrom(info); cfg.i2s_format = I2S_STD_FORMAT; // or try with I2S_LSB_FORMAT + cfg.bits_per_sample = 16; + cfg.channels = 2; + cfg.sample_rate = 44100; cfg.is_master = true; // this module nees a master clock if the ESP32 is master cfg.use_apll = false; // try with yes @@ -30,7 +32,7 @@ void setup(void) { i2sStream.begin(cfg); // make sure that we have the correct channels set up - csvOutput.begin(info); + csvStream.begin(cfg); } diff --git a/examples/examples-stream/streams-i2s-tf/streams-i2s-tf.ino b/examples/examples-stream/streams-i2s-tf/streams-i2s-tf.ino index ab7ff6a09a..218122a262 100644 --- a/examples/examples-stream/streams-i2s-tf/streams-i2s-tf.ino +++ b/examples/examples-stream/streams-i2s-tf/streams-i2s-tf.ino @@ -10,7 +10,7 @@ */ #include "AudioTools.h" -#include "AudioTools/AudioLibs/TfLiteAudioStream.h" +#include "AudioLibs/TfLiteAudioStream.h" #include "model.h" // tensorflow model I2SStream i2s; // Audio source @@ -37,14 +37,14 @@ void respondToCommand(const char* found_command, uint8_t score, void setup() { Serial.begin(115200); - AudioToolsLogger.begin(Serial, AudioToolsLogLevel::Warning); + AudioLogger::instance().begin(Serial, AudioLogger::Warning); // setup Audioi2s input auto cfg = i2s.defaultConfig(RX_MODE); cfg.channels = channels; cfg.sample_rate = samples_per_second; cfg.use_apll = false; - //cfg.auto_clear = true; + cfg.auto_clear = true; cfg.buffer_size = 512; cfg.buffer_count = 16; i2s.begin(cfg); diff --git a/examples/examples-stream/streams-i2s_pdm-serial/README.md b/examples/examples-stream/streams-i2s_pdm-serial/README.md deleted file mode 100644 index 3da9b183af..0000000000 --- a/examples/examples-stream/streams-i2s_pdm-serial/README.md +++ /dev/null @@ -1,29 +0,0 @@ - -# Stream PDM Input to CSV Output - -## General Description: - -To use a PDM microphone you need to have a microcontroller that supports PDM input or a dedicated library. - -The ESP32 supports PDM via the I2S API: We stream the sound input from a PDM microphone (PDM Digital MEMS MP34DT01) which we read in from the I2S interface and displays it on the Arduino Serial Plotter. - - -## Pins - -![i2s-adc](https://pschatzmann.github.io/Resources/img/pdm-mic.jpg) - -| i2s-mic | ESP32 -| ---------| --------------- -| 3V | 3V -| GND | GND -| SEL | GND (GND or 3.3V) -| - | WS (GPIO15) -| DAT | IN (GPIO32) -| CLK | BCK (GPIO14) - - -## Additional Comments - -You can select if you receive only data on the left or right biy setting SEL to high or low. -Please note that in the 2.x realease of the Arduino ESP core, the WS pin was used as CLK - diff --git a/examples/examples-stream/streams-i2s_pdm-serial/streams-i2s_pdm-serial.ino b/examples/examples-stream/streams-i2s_pdm-serial/streams-i2s_pdm-serial.ino deleted file mode 100644 index b6b60ec6c3..0000000000 --- a/examples/examples-stream/streams-i2s_pdm-serial/streams-i2s_pdm-serial.ino +++ /dev/null @@ -1,39 +0,0 @@ -/** - * @file streams-i2s_pdm-serial.ino - * @author Phil Schatzmann - * @brief see https://github.com/pschatzmann/arduino-audio-tools/blob/main/examples/examples-stream/streams-i2s_pdm-serial/README.md - * - * @author Phil Schatzmann - * @copyright GPLv3 - */ - - -#include "AudioTools.h" - -AudioInfo info(44100, 1, 16); -I2SStream i2sStream; // Access I2S as stream -CsvOutput csvOutput(Serial); -StreamCopy copier(csvOutput, i2sStream); // copy i2sStream to csvOutput - -// Arduino Setup -void setup(void) { - Serial.begin(115200); - AudioToolsLogger.begin(Serial, AudioToolsLogLevel::Info); - - auto cfg = i2sStream.defaultConfig(RX_MODE); - cfg.copyFrom(info); - cfg.signal_type = PDM; - //cfg.use_apll = false; - //cfg.auto_clear = false; - cfg.pin_ws = -1; // not used - i2sStream.begin(cfg); - - // make sure that we have the correct channels set up - csvOutput.begin(info); - -} - -// Arduino loop - copy data -void loop() { - copier.copy(); -} diff --git a/examples/examples-stream/streams-memory_mp3-analog/streams-memory_mp3-analog.ino b/examples/examples-stream/streams-memory_mp3-analog/streams-memory_mp3-analog.ino index a7c836f935..c2f5214ffc 100644 --- a/examples/examples-stream/streams-memory_mp3-analog/streams-memory_mp3-analog.ino +++ b/examples/examples-stream/streams-memory_mp3-analog/streams-memory_mp3-analog.ino @@ -1,7 +1,7 @@ /** * @file stream-memory_mp3-analog.ino * @author Phil Schatzmann - * @brief decode MP3 stream and output as out signal + * @brief decode MP3 stream and output as analog signal * @version 0.1 * @date 2021-01-24 * @@ -12,22 +12,26 @@ // install https://github.com/pschatzmann/arduino-libhelix.git #include "AudioTools.h" -#include "AudioTools/AudioCodecs/CodecMP3Helix.h" +#include "AudioCodecs/CodecMP3Helix.h" #include "BabyElephantWalk60_mp3.h" MemoryStream mp3(BabyElephantWalk60_mp3, BabyElephantWalk60_mp3_len); AnalogAudioStream analog; // Analog output EncodedAudioStream out(&analog, new MP3DecoderHelix()); // output to decoder -StreamCopy copier(out, mp3); // copy in to out +StreamCopy copier(out, mp3); // copy in to analog void setup(){ Serial.begin(115200); - AudioToolsLogger.begin(Serial, AudioToolsLogLevel::Info); + AudioLogger::instance().begin(Serial, AudioLogger::Info); - analog.begin(analog.defaultConfig(TX_MODE)); + // update audio info with info from decoder + out.setNotifyAudioChange(analog); // begin processing + auto cfg = analog.defaultConfig(); + analog.begin(cfg); + out.begin(); } @@ -38,7 +42,7 @@ void loop(){ auto info = out.decoder().audioInfo(); LOGI("The audio rate from the mp3 file is %d", info.sample_rate); LOGI("The channels from the mp3 file is %d", info.channels); - out.end(); + analog.end(); stop(); } } \ No newline at end of file diff --git a/examples/examples-stream/streams-memory_mp3-metadata/streams-memory_mp3-metadata.ino b/examples/examples-stream/streams-memory_mp3-metadata/streams-memory_mp3-metadata.ino index 207648ea9e..ce96a3679b 100644 --- a/examples/examples-stream/streams-memory_mp3-metadata/streams-memory_mp3-metadata.ino +++ b/examples/examples-stream/streams-memory_mp3-metadata/streams-memory_mp3-metadata.ino @@ -24,7 +24,7 @@ void printMetaData(MetaDataType type, const char* str, int len){ void setup(){ Serial.begin(115200); - AudioToolsLogger.begin(Serial, AudioToolsLogLevel::Info); + AudioLogger::instance().begin(Serial, AudioLogger::Info); mp3.begin(); diff --git a/examples/examples-stream/streams-memory_mp3-pwm/streams-memory_mp3-pwm.ino b/examples/examples-stream/streams-memory_mp3-pwm/streams-memory_mp3-pwm.ino index 2ffeddccc0..9e16d247bc 100644 --- a/examples/examples-stream/streams-memory_mp3-pwm/streams-memory_mp3-pwm.ino +++ b/examples/examples-stream/streams-memory_mp3-pwm/streams-memory_mp3-pwm.ino @@ -11,7 +11,7 @@ #include "AudioTools.h" -#include "AudioTools/AudioCodecs/CodecMP3Helix.h" +#include "AudioCodecs/CodecMP3Helix.h" #include "BabyElephantWalk60_mp3.h" @@ -22,7 +22,10 @@ StreamCopy copier(decoded, mp3); // copy in to out void setup(){ Serial.begin(115200); - AudioToolsLogger.begin(Serial, AudioToolsLogLevel::Info); + AudioLogger::instance().begin(Serial, AudioLogger::Info); + + // update audio info with info from decoder + decoded.setNotifyAudioChange(out); // begin processing auto cfg = out.defaultConfig(); diff --git a/examples/examples-stream/streams-memory_mp3_short-i2s-2/streams-memory_mp3_short-i2s-2.ino b/examples/examples-stream/streams-memory_mp3_short-i2s-2/streams-memory_mp3_short-i2s-2.ino index 749627708f..c36672c591 100644 --- a/examples/examples-stream/streams-memory_mp3_short-i2s-2/streams-memory_mp3_short-i2s-2.ino +++ b/examples/examples-stream/streams-memory_mp3_short-i2s-2/streams-memory_mp3_short-i2s-2.ino @@ -10,7 +10,7 @@ */ #include "AudioTools.h" -#include "AudioTools/AudioCodecs/CodecMP3Helix.h" +#include "AudioCodecs/CodecMP3Helix.h" #include "zero.h" @@ -22,7 +22,7 @@ StreamCopy copier(out, mp3); // copy in to out void setup(){ Serial.begin(115200); - AudioToolsLogger.begin(Serial, AudioToolsLogLevel::Info); + AudioLogger::instance().begin(Serial, AudioLogger::Info); // begin processing auto cfg = i2s.defaultConfig(); diff --git a/examples/examples-stream/streams-memory_mp3_short-i2s/streams-memory_mp3_short-i2s.ino b/examples/examples-stream/streams-memory_mp3_short-i2s/streams-memory_mp3_short-i2s.ino index b71c4fcfbb..e1f00827fc 100644 --- a/examples/examples-stream/streams-memory_mp3_short-i2s/streams-memory_mp3_short-i2s.ino +++ b/examples/examples-stream/streams-memory_mp3_short-i2s/streams-memory_mp3_short-i2s.ino @@ -10,7 +10,7 @@ */ #include "AudioTools.h" -#include "AudioTools/AudioCodecs/CodecMP3Helix.h" +#include "AudioCodecs/CodecMP3Helix.h" #include "zero.h" MemoryStream mp3(zero_mp3, zero_mp3_len); @@ -21,7 +21,7 @@ StreamCopy copier(out, mp3); // copy in to i2s void setup(){ Serial.begin(115200); - AudioToolsLogger.begin(Serial, AudioToolsLogLevel::Info); + AudioLogger::instance().begin(Serial, AudioLogger::Info); // begin processing auto cfg = i2s.defaultConfig(); diff --git a/examples/examples-stream/streams-memory_raw-i2s/README.md b/examples/examples-stream/streams-memory_raw-i2s/README.md index 63a71185c3..5332cf39ba 100644 --- a/examples/examples-stream/streams-memory_raw-i2s/README.md +++ b/examples/examples-stream/streams-memory_raw-i2s/README.md @@ -4,6 +4,8 @@ Somtimes we want to store the sound file in memory. [Audacity](https://www.audac Then you can convert the file with xxd into a C file that contains the data in an array. In the Sketch I am using the __MemoryStream class__ which turns the array into a Stream. +Unlike in the other examples I am using the typed __StreamCopyT__ class together with the __copy2()__ method. This reads the one channel input and copies it as 2 channels to the destination I2S stream. + Please note that you must compile this sketch with the __Partition Scheme: Huge App__! diff --git a/examples/examples-stream/streams-memory_raw-i2s/streams-memory_raw-i2s.ino b/examples/examples-stream/streams-memory_raw-i2s/streams-memory_raw-i2s.ino index 0aed963c7d..9fc29aa40d 100644 --- a/examples/examples-stream/streams-memory_raw-i2s/streams-memory_raw-i2s.ino +++ b/examples/examples-stream/streams-memory_raw-i2s/streams-memory_raw-i2s.ino @@ -1,5 +1,5 @@ /** - * @file streams-memory_raw-i2s.ino + * @file streams-memory_raw-i2s_external_dac.ino * @author Phil Schatzmann * @brief Compile with Partition Scheme Hughe APP! * @version 0.1 @@ -18,11 +18,12 @@ StreamCopy copier(i2s, music); // copies sound into i2s void setup(){ Serial.begin(115200); - AudioToolsLogger.begin(Serial, AudioToolsLogLevel::Info); + AudioLogger::instance().begin(Serial, AudioLogger::Info); auto config = i2s.defaultConfig(TX_MODE); config.copyFrom(info); i2s.begin(config); + } void loop(){ diff --git a/examples/examples-stream/streams-memory_wav-pwm/streams-memory_wav-pwm.ino b/examples/examples-stream/streams-memory_wav-pwm/streams-memory_wav-pwm.ino index da4d33c80f..3bcdd280c2 100644 --- a/examples/examples-stream/streams-memory_wav-pwm/streams-memory_wav-pwm.ino +++ b/examples/examples-stream/streams-memory_wav-pwm/streams-memory_wav-pwm.ino @@ -12,24 +12,27 @@ //#include "knghtsng.h" #include "alice.h" + + //Data Flow: MemoryStream -> EncodedAudioStream -> PWMAudioOutput -//Use 8000 for alice_wav and 11025 for knghtsng_wav -AudioInfo info(8000, 1, 16); + //MemoryStream wav(knghtsng_wav, knghtsng_wav_len); MemoryStream wav(alice_wav, alice_wav_len); PWMAudioOutput pwm; // PWM output -EncodedAudioStream out(&pwm, new WAVDecoder()); // Decoder stream +WAVDecoder decoder(pwm); // decode wav to pcm and send it to printer +EncodedAudioStream out(&pwm, &decoder); // Decoder stream StreamCopy copier(out, wav); // copy in to out void setup(){ Serial.begin(115200); - AudioToolsLogger.begin(Serial, AudioToolsLogLevel::Info); - - wav.begin(); - out.begin(); + AudioLogger::instance().begin(Serial, AudioLogger::Info); auto config = pwm.defaultConfig(); - config.copyFrom(info); + + // setup pwm output + config.channels = 1; + //config.sample_rate = 11025; // for knghtsng_wav + config.sample_rate = 8000; // for alice_wav pwm.begin(config); } @@ -38,14 +41,14 @@ void loop(){ copier.copy(); } else { // after we are done we just print some info form the wav file - auto info = out.audioInfo(); + auto info = decoder.audioInfo(); LOGI("The audio rate from the wav file is %d", info.sample_rate); LOGI("The channels from the wav file is %d", info.channels); // restart from the beginning Serial.println("Restarting..."); delay(5000); - out.begin(); // indicate that we process the WAV header + decoder.begin(); // indicate that we process the WAV header wav.begin(); // reset actual position to 0 pwm.begin(); // reset counters } diff --git a/examples/examples-stream/streams-memory_wav-serial/streams-memory_wav-serial.ino b/examples/examples-stream/streams-memory_wav-serial/streams-memory_wav-serial.ino index a4aedc2add..0d39196304 100644 --- a/examples/examples-stream/streams-memory_wav-serial/streams-memory_wav-serial.ino +++ b/examples/examples-stream/streams-memory_wav-serial/streams-memory_wav-serial.ino @@ -11,18 +11,21 @@ #include "AudioTools.h" #include "knghtsng.h" + + // MemoryStream -> EncodedAudioStream -> CsvOutput MemoryStream wav(knghtsng_wav, knghtsng_wav_len); -CsvOutput out(Serial, 1); // ASCII stream +CsvOutput out(Serial); // ASCII stream EncodedAudioStream enc(&out, new WAVDecoder()); StreamCopy copier(enc, wav); // copy in to out void setup(){ Serial.begin(115200); - AudioToolsLogger.begin(Serial, AudioToolsLogLevel::Info); + AudioLogger::instance().begin(Serial, AudioLogger::Info); // update number of channels from wav file - enc.begin(); + enc.setNotifyAudioChange(out); + out.begin(); wav.begin(); } diff --git a/examples/examples-stream/streams-mp34dt05-serial/streams-mp34dt05-serial.ino b/examples/examples-stream/streams-mp34dt05-serial/streams-mp34dt05-serial.ino index 67685f527f..e0ad00591f 100644 --- a/examples/examples-stream/streams-mp34dt05-serial/streams-mp34dt05-serial.ino +++ b/examples/examples-stream/streams-mp34dt05-serial/streams-mp34dt05-serial.ino @@ -9,17 +9,17 @@ #include "AudioTools.h" -#include "AudioTools/AudioLibs/AudioMP34DT05.h" +#include "AudioLibs/AudioMP34DT05.h" AudioMP34DT05 mic; // Access I2S as stream -CsvOutput csvOutput(Serial); -StreamCopy copier(csvOutput, mic); // copy mic to csvOutput +CsvOutput csvStream(Serial); +StreamCopy copier(csvStream, mic); // copy mic to csvStream // Arduino Setup void setup(void) { Serial.begin(115200); - AudioToolsLogger.begin(Serial, AudioToolsLogLevel::Debug); + AudioLogger::instance().begin(Serial, AudioLogger::Debug); while(!Serial); Serial.println("starting..."); @@ -31,7 +31,7 @@ void setup(void) { mic.begin(cfg); // make sure that we have the correct channels set up - csvOutput.begin(cfg); + csvStream.begin(cfg); Serial.println("started"); diff --git a/examples/examples-stream/streams-sd_mp3-i2s/streams-sd_mp3-i2s.ino b/examples/examples-stream/streams-sd_mp3-i2s/streams-sd_mp3-i2s.ino index c27146e1a6..8f04bfacd4 100644 --- a/examples/examples-stream/streams-sd_mp3-i2s/streams-sd_mp3-i2s.ino +++ b/examples/examples-stream/streams-sd_mp3-i2s/streams-sd_mp3-i2s.ino @@ -12,7 +12,7 @@ #include #include #include "AudioTools.h" -#include "AudioTools/AudioCodecs/CodecMP3Helix.h" +#include "AudioCodecs/CodecMP3Helix.h" const int chipSelect=10; @@ -23,7 +23,7 @@ File audioFile; void setup(){ Serial.begin(115200); - AudioToolsLogger.begin(Serial, AudioToolsLogLevel::Info); + AudioLogger::instance().begin(Serial, AudioLogger::Info); // setup file SD.begin(chipSelect); @@ -34,6 +34,7 @@ void setup(){ i2s.begin(config); // setup I2S based on sampling rate provided by decoder + decoder.setNotifyAudioChange(i2s); decoder.begin(); // begin copy diff --git a/examples/examples-stream/streams-sd_wav4-i2s/streams-sd_wav4-i2s.ino b/examples/examples-stream/streams-sd_wav4-i2s/streams-sd_wav4-i2s.ino deleted file mode 100644 index bb6c774301..0000000000 --- a/examples/examples-stream/streams-sd_wav4-i2s/streams-sd_wav4-i2s.ino +++ /dev/null @@ -1,69 +0,0 @@ -/** - * @file streams-sd_wav4-i2s.ino - * @author Phil Schatzmann - * @brief decode WAV file with 4 channels and output it on 2 I2S ports - * @version 0.1 - * @date 2021-96-25 - * - * @copyright Copyright (c) 2021 - */ - -#include -#include -#include "AudioTools.h" -#include "AudioTools/AudioCodecs/CodecMP3Helix.h" - -const int chipSelect = 10; -AudioInfo info(44100, 2, 16); -AudioInfo info4(44100, 4, 16); -I2SStream i2s_1; // final output port 0 -I2SStream i2s_2; // final output port 1 -ChannelsSelectOutput out; -WAVDecoder wav; -EncodedAudioOutput decoder(&out, &wav); // Decoding stream -StreamCopy copier; -File audioFile; - -void setup() { - Serial.begin(115200); - AudioToolsLogger.begin(Serial, AudioToolsLogLevel::Info); - - // setup file - SD.begin(chipSelect); - audioFile = SD.open("/Music/ch4.wav"); - - // setup i2s - auto config1 = i2s_1.defaultConfig(TX_MODE); - config1.copyFrom(info); - // config1.port_no = 0; // 0 is default port - i2s_1.begin(config1); - - auto config2 = i2s_2.defaultConfig(TX_MODE); - config2.copyFrom(info); - // use separate pins - config2.pin_ws = 4; - config2.pin_bck = 5; - config2.pin_data = 6; - // use port 1 - config2.port_no = 1; - i2s_2.begin(config2); - - // split channels to different i2s ports - out.addOutput(i2s_1, 0, 1); - out.addOutput(i2s_2, 2, 3); - - // 4 channels - out.begin(info4); - - // setup decoder - decoder.begin(); - - // begin copy - copier.begin(decoder, audioFile); -} - -void loop() { - if (!copier.copy()) { - stop(); - } -} diff --git a/examples/examples-stream/streams-sdfat_mp3-metadata/streams-sdfat_mp3-metadata.ino b/examples/examples-stream/streams-sdfat_mp3-metadata/streams-sdfat_mp3-metadata.ino index 953146039b..3f10a5c3d0 100644 --- a/examples/examples-stream/streams-sdfat_mp3-metadata/streams-sdfat_mp3-metadata.ino +++ b/examples/examples-stream/streams-sdfat_mp3-metadata/streams-sdfat_mp3-metadata.ino @@ -14,7 +14,7 @@ #include #include #include "AudioTools.h" -#include "AudioTools/AudioCodecs/CodecMP3Helix.h" +#include "AudioCodecs/CodecMP3Helix.h" // -> EncodedAudioStream -> I2SStream @@ -39,7 +39,7 @@ void printMetaData(MetaDataType type, const char* str, int len){ void setup(){ Serial.begin(115200); - AudioToolsLogger.begin(Serial, AudioToolsLogLevel::Info); + AudioLogger::instance().begin(Serial, AudioLogger::Info); // setup multi output out.add(outMeta); @@ -59,6 +59,7 @@ void setup(){ i2s.begin(config); // setup I2S based on sampling rate provided by decoder + out2dec.setNotifyAudioChange(i2s); out2dec.begin(); } diff --git a/examples/examples-stream/streams-tf-i2s/streams-tf-i2s.ino b/examples/examples-stream/streams-tf-i2s/streams-tf-i2s.ino index 78a36c65e0..26240d59ab 100644 --- a/examples/examples-stream/streams-tf-i2s/streams-tf-i2s.ino +++ b/examples/examples-stream/streams-tf-i2s/streams-tf-i2s.ino @@ -9,7 +9,7 @@ * */ #include "AudioTools.h" -#include "AudioTools/AudioLibs/TfLiteAudioStream.h" +#include "AudioLibs/TfLiteAudioStream.h" #include "model.h" TfLiteSineReader tf_reader(20000,0.3); // Audio generation logic @@ -22,7 +22,7 @@ int samples_per_second = 16000; void setup() { Serial.begin(115200); - AudioToolsLogger.begin(Serial, AudioToolsLogLevel::Warning); + AudioLogger::instance().begin(Serial, AudioLogger::Warning); // Setup tensorflow input auto tcfg = tf_stream.defaultConfig(); diff --git a/examples/examples-communication/http-client/streams-url-file/streams-url-file.ino b/examples/examples-stream/streams-url-file/streams-url-file.ino similarity index 88% rename from examples/examples-communication/http-client/streams-url-file/streams-url-file.ino rename to examples/examples-stream/streams-url-file/streams-url-file.ino index b4b8a1d2d8..6632553158 100644 --- a/examples/examples-communication/http-client/streams-url-file/streams-url-file.ino +++ b/examples/examples-stream/streams-url-file/streams-url-file.ino @@ -28,7 +28,7 @@ int retryCount = 5; // Arduino Setup void setup(void) { Serial.begin(115200); - AudioToolsLogger.begin(Serial, AudioToolsLogLevel::Info); + AudioLogger::instance().begin(Serial, AudioLogger::Info); // define custom SPI pins SPI.begin(PIN_AUDIO_KIT_SD_CARD_CLK, PIN_AUDIO_KIT_SD_CARD_MISO, PIN_AUDIO_KIT_SD_CARD_MOSI, PIN_AUDIO_KIT_SD_CARD_CS); @@ -46,6 +46,8 @@ void setup(void) { file = SD.open("/audio-8000.raw", FILE_WRITE); // overwirte from beginning file.seek(0); + // File returns avaiableForWrite() = 0, we we need to deactivate this check + copier.setCheckAvailableForWrite(false); copier.begin(file, url); copier.copyAll(); file.close(); diff --git a/examples/examples-communication/http-client/streams-url-measuring/streams-url-measuring.ino b/examples/examples-stream/streams-url-measuring/streams-url-measuring.ino similarity index 87% rename from examples/examples-communication/http-client/streams-url-measuring/streams-url-measuring.ino rename to examples/examples-stream/streams-url-measuring/streams-url-measuring.ino index 0e72aa05f3..81ee41a24b 100644 --- a/examples/examples-communication/http-client/streams-url-measuring/streams-url-measuring.ino +++ b/examples/examples-stream/streams-url-measuring/streams-url-measuring.ino @@ -11,7 +11,7 @@ // install https://github.com/pschatzmann/arduino-libhelix.git #include "AudioTools.h" -#include "AudioTools/AudioCodecs/CodecMP3Helix.h" +#include "AudioCodecs/CodecMP3Helix.h" URLStream url("http://github.com/ssid%22,%22password"); // or replace with ICYStream to get metadata MeasuringStream out(50, &Serial); // final output of decoded stream @@ -20,7 +20,7 @@ StreamCopy copier(dec, url); // copy url to decoder void setup(){ Serial.begin(115200); - AudioToolsLogger.begin(Serial, AudioToolsLogLevel::Warning); + AudioLogger::instance().begin(Serial, AudioLogger::Warning); dec.begin(); diff --git a/examples/examples-communication/http-client/streams-url_aac-i2s/README.md b/examples/examples-stream/streams-url_aac-i2s/README.md similarity index 100% rename from examples/examples-communication/http-client/streams-url_aac-i2s/README.md rename to examples/examples-stream/streams-url_aac-i2s/README.md diff --git a/examples/examples-communication/http-client/streams-url_aac-i2s/streams-url_aac-i2s.ino b/examples/examples-stream/streams-url_aac-i2s/streams-url_aac-i2s.ino similarity index 87% rename from examples/examples-communication/http-client/streams-url_aac-i2s/streams-url_aac-i2s.ino rename to examples/examples-stream/streams-url_aac-i2s/streams-url_aac-i2s.ino index 2d409b9ca7..67717335ed 100644 --- a/examples/examples-communication/http-client/streams-url_aac-i2s/streams-url_aac-i2s.ino +++ b/examples/examples-stream/streams-url_aac-i2s/streams-url_aac-i2s.ino @@ -11,7 +11,7 @@ // install https://github.com/pschatzmann/arduino-libhelix.git #include "AudioTools.h" -#include "AudioTools/AudioCodecs/CodecAACHelix.h" +#include "AudioCodecs/CodecAACHelix.h" URLStream url("http://github.com/ssid%22,%22password"); @@ -22,7 +22,7 @@ StreamCopy copier(dec, url); // copy url to decoder void setup(){ Serial.begin(115200); - AudioToolsLogger.begin(Serial, AudioToolsLogLevel::Info); + AudioLogger::instance().begin(Serial, AudioLogger::Info); // setup i2s auto config = i2s.defaultConfig(TX_MODE); @@ -34,6 +34,7 @@ void setup(){ i2s.begin(config); // aac radio + dec.setNotifyAudioChange(i2s); url.begin("http://peacefulpiano.stream.publicradio.org/peacefulpiano.aac","audio/aac"); // initialize decoder diff --git a/examples/examples-communication/http-client/streams-url_flac-i2s/streams-url_flac-i2s.ino b/examples/examples-stream/streams-url_flac-i2s/streams-url_flac-i2s.ino similarity index 82% rename from examples/examples-communication/http-client/streams-url_flac-i2s/streams-url_flac-i2s.ino rename to examples/examples-stream/streams-url_flac-i2s/streams-url_flac-i2s.ino index cd153ab24e..ef343515ab 100644 --- a/examples/examples-communication/http-client/streams-url_flac-i2s/streams-url_flac-i2s.ino +++ b/examples/examples-stream/streams-url_flac-i2s/streams-url_flac-i2s.ino @@ -9,7 +9,7 @@ * */ #include "AudioTools.h" -#include "AudioTools/AudioCodecs/CodecFLAC.h" +#include "AudioCodecs/CodecFLAC.h" const char* ssid = "ssid"; const char* pwd = "password"; @@ -19,12 +19,12 @@ I2SStream i2s; void setup() { Serial.begin(115200); - AudioToolsLogger.begin(Serial, AudioToolsLogLevel::Info); + AudioLogger::instance().begin(Serial, AudioLogger::Info); i2s.begin(i2s.defaultConfig(TX_MODE)); url.begin("http://www.lindberg.no/hires/test/2L-145_01_stereo_01.cd.flac"); - dec.setInput(url); + dec.setInputStream(url); dec.setOutput(i2s); dec.begin(); } diff --git a/examples/examples-communication/http-client/streams-url_mp3-analog/README.md b/examples/examples-stream/streams-url_mp3-analog/README.md similarity index 100% rename from examples/examples-communication/http-client/streams-url_mp3-analog/README.md rename to examples/examples-stream/streams-url_mp3-analog/README.md diff --git a/examples/examples-communication/http-client/streams-url_mp3-analog/streams-url_mp3-analog.ino b/examples/examples-stream/streams-url_mp3-analog/streams-url_mp3-analog.ino similarity index 59% rename from examples/examples-communication/http-client/streams-url_mp3-analog/streams-url_mp3-analog.ino rename to examples/examples-stream/streams-url_mp3-analog/streams-url_mp3-analog.ino index bad4e6ffef..c482c6c465 100644 --- a/examples/examples-communication/http-client/streams-url_mp3-analog/streams-url_mp3-analog.ino +++ b/examples/examples-stream/streams-url_mp3-analog/streams-url_mp3-analog.ino @@ -1,5 +1,5 @@ /** - * @file streams-url_mp3-out.ino + * @file streams-url_mp3-analog.ino * @author Phil Schatzmann * @brief decode MP3 stream from url and output it on I2S * @version 0.1 @@ -11,24 +11,25 @@ // install https://github.com/pschatzmann/arduino-libhelix.git #include "AudioTools.h" -#include "AudioTools/AudioCodecs/CodecMP3Helix.h" +#include "AudioCodecs/CodecMP3Helix.h" URLStream url("http://github.com/ssid%22,%22password"); -AnalogAudioStream out; // final output of decoded stream -EncodedAudioStream dec(&out, new MP3DecoderHelix()); // Decoding stream +AnalogAudioStream analog; // final output of decoded stream +EncodedAudioStream dec(&analog, new MP3DecoderHelix()); // Decoding stream StreamCopy copier(dec, url); // copy url to decoder void setup(){ Serial.begin(115200); - AudioToolsLogger.begin(Serial, AudioToolsLogLevel::Info); + AudioLogger::instance().begin(Serial, AudioLogger::Info); - // setup out - auto config = out.defaultConfig(TX_MODE); - out.begin(config); + // setup analog + auto config = analog.defaultConfig(TX_MODE); + analog.begin(config); // setup I2S based on sampling rate provided by decoder + dec.setNotifyAudioChange(analog); dec.begin(); // mp3 radio diff --git a/examples/examples-communication/http-client/streams-url_mp3-metadata/streams-url_mp3-metadata.ino b/examples/examples-stream/streams-url_mp3-metadata/streams-url_mp3-metadata.ino similarity index 89% rename from examples/examples-communication/http-client/streams-url_mp3-metadata/streams-url_mp3-metadata.ino rename to examples/examples-stream/streams-url_mp3-metadata/streams-url_mp3-metadata.ino index 7d6e67c08f..69a65ea876 100644 --- a/examples/examples-communication/http-client/streams-url_mp3-metadata/streams-url_mp3-metadata.ino +++ b/examples/examples-stream/streams-url_mp3-metadata/streams-url_mp3-metadata.ino @@ -10,7 +10,7 @@ // install https://github.com/pschatzmann/arduino-libhelix.git #include "AudioTools.h" -#include "AudioTools/AudioCodecs/CodecMP3Helix.h" +#include "AudioCodecs/CodecMP3Helix.h" ICYStream url("http://github.com/ssid%22,%22password"); @@ -27,7 +27,7 @@ void printMetaData(MetaDataType type, const char* str, int len){ void setup(){ Serial.begin(115200); - AudioToolsLogger.begin(Serial, AudioToolsLogLevel::Info); + AudioLogger::instance().begin(Serial, AudioLogger::Info); // mp3 radio url.httpRequest().header().put("Icy-MetaData","1"); diff --git a/examples/examples-communication/http-client/streams-url_mp3-metadata2/streams-url_mp3-metadata2.ino b/examples/examples-stream/streams-url_mp3-metadata2/streams-url_mp3-metadata2.ino similarity index 91% rename from examples/examples-communication/http-client/streams-url_mp3-metadata2/streams-url_mp3-metadata2.ino rename to examples/examples-stream/streams-url_mp3-metadata2/streams-url_mp3-metadata2.ino index 399da89a92..e134db708b 100644 --- a/examples/examples-communication/http-client/streams-url_mp3-metadata2/streams-url_mp3-metadata2.ino +++ b/examples/examples-stream/streams-url_mp3-metadata2/streams-url_mp3-metadata2.ino @@ -11,7 +11,7 @@ // install https://github.com/pschatzmann/arduino-libhelix.git #include "AudioTools.h" -#include "AudioTools/AudioCodecs/CodecMP3Helix.h" +#include "AudioCodecs/CodecMP3Helix.h" // -> EncodedAudioStream -> I2SStream // URLStream -> MultiOutput -| @@ -34,7 +34,7 @@ void printMetaData(MetaDataType type, const char* str, int len){ void setup(){ Serial.begin(115200); - AudioToolsLogger.begin(Serial, AudioToolsLogLevel::Info); + AudioLogger::instance().begin(Serial, AudioLogger::Info); // setup multi output out.add(out1); @@ -52,6 +52,7 @@ void setup(){ i2s.begin(config); // setup I2S based on sampling rate provided by decoder + out2dec.setNotifyAudioChange(i2s); out2dec.begin(); } diff --git a/examples/examples-communication/http-client/streams-url_mp3_helix-i2s/README.md b/examples/examples-stream/streams-url_mp3_helix-i2s/README.md similarity index 100% rename from examples/examples-communication/http-client/streams-url_mp3_helix-i2s/README.md rename to examples/examples-stream/streams-url_mp3_helix-i2s/README.md diff --git a/examples/examples-communication/http-client/streams-url_mp3_helix-i2s/streams-url_mp3_helix-i2s.ino b/examples/examples-stream/streams-url_mp3_helix-i2s/streams-url_mp3_helix-i2s.ino similarity index 87% rename from examples/examples-communication/http-client/streams-url_mp3_helix-i2s/streams-url_mp3_helix-i2s.ino rename to examples/examples-stream/streams-url_mp3_helix-i2s/streams-url_mp3_helix-i2s.ino index 18cfef0a60..d2a837fa77 100644 --- a/examples/examples-communication/http-client/streams-url_mp3_helix-i2s/streams-url_mp3_helix-i2s.ino +++ b/examples/examples-stream/streams-url_mp3_helix-i2s/streams-url_mp3_helix-i2s.ino @@ -11,7 +11,7 @@ // install https://github.com/pschatzmann/arduino-libhelix.git #include "AudioTools.h" -#include "AudioTools/AudioCodecs/CodecMP3Helix.h" +#include "AudioCodecs/CodecMP3Helix.h" URLStream url("http://github.com/ssid%22,%22password"); @@ -22,7 +22,7 @@ StreamCopy copier(dec, url); // copy url to decoder void setup(){ Serial.begin(115200); - AudioToolsLogger.begin(Serial, AudioToolsLogLevel::Info); + AudioLogger::instance().begin(Serial, AudioLogger::Info); // setup i2s auto config = i2s.defaultConfig(TX_MODE); @@ -34,6 +34,7 @@ void setup(){ i2s.begin(config); // setup I2S based on sampling rate provided by decoder + dec.setNotifyAudioChange(i2s); dec.begin(); // mp3 radio diff --git a/examples/examples-communication/http-client/streams-url_mp3_mad-i2s/README.md b/examples/examples-stream/streams-url_mp3_mad-i2s/README.md similarity index 100% rename from examples/examples-communication/http-client/streams-url_mp3_mad-i2s/README.md rename to examples/examples-stream/streams-url_mp3_mad-i2s/README.md diff --git a/examples/examples-communication/http-client/streams-url_mp3_mad-i2s/streams-url_mp3_mad-i2s.ino b/examples/examples-stream/streams-url_mp3_mad-i2s/streams-url_mp3_mad-i2s.ino similarity index 88% rename from examples/examples-communication/http-client/streams-url_mp3_mad-i2s/streams-url_mp3_mad-i2s.ino rename to examples/examples-stream/streams-url_mp3_mad-i2s/streams-url_mp3_mad-i2s.ino index bf3d81445d..29967379d2 100644 --- a/examples/examples-communication/http-client/streams-url_mp3_mad-i2s/streams-url_mp3_mad-i2s.ino +++ b/examples/examples-stream/streams-url_mp3_mad-i2s/streams-url_mp3_mad-i2s.ino @@ -11,7 +11,7 @@ // install https://github.com/pschatzmann/arduino-libmad.git #include "AudioTools.h" -#include "AudioTools/AudioCodecs/CodecMP3MAD.h" +#include "AudioCodecs/CodecMP3MAD.h" URLStream url("http://github.com/ssid%22,%22password"); @@ -22,7 +22,7 @@ StreamCopy copier(dec, url); // copy url to decoder void setup(){ Serial.begin(115200); - AudioToolsLogger.begin(Serial, AudioToolsLogLevel::Info); + AudioLogger::instance().begin(Serial, AudioLogger::Info); // setup i2s auto config = i2s.defaultConfig(TX_MODE); @@ -34,6 +34,7 @@ void setup(){ i2s.begin(config); // setup I2S based on sampling rate provided by decoder + dec.setNotifyAudioChange(i2s); dec.begin(); // mp3 radio diff --git a/examples/examples-communication/http-client/streams-url_raw-i2s/README.md b/examples/examples-stream/streams-url_raw-i2s/README.md similarity index 100% rename from examples/examples-communication/http-client/streams-url_raw-i2s/README.md rename to examples/examples-stream/streams-url_raw-i2s/README.md diff --git a/examples/examples-communication/http-client/streams-url_raw-i2s/streams-url_raw-i2s.ino b/examples/examples-stream/streams-url_raw-i2s/streams-url_raw-i2s.ino similarity index 94% rename from examples/examples-communication/http-client/streams-url_raw-i2s/streams-url_raw-i2s.ino rename to examples/examples-stream/streams-url_raw-i2s/streams-url_raw-i2s.ino index b000e4ea17..a508a3984a 100644 --- a/examples/examples-communication/http-client/streams-url_raw-i2s/streams-url_raw-i2s.ino +++ b/examples/examples-stream/streams-url_raw-i2s/streams-url_raw-i2s.ino @@ -17,7 +17,7 @@ StreamCopy copier(i2s, music, 1024); // copy music to i2s // Arduino Setup void setup(void) { Serial.begin(115200); - AudioToolsLogger.begin(Serial, AudioToolsLogLevel::Info); + AudioLogger::instance().begin(Serial, AudioLogger::Info); // connect to WIFI WiFi.begin("network", "pwd"); diff --git a/examples/examples-communication/http-client/streams-url_raw-serial/README.md b/examples/examples-stream/streams-url_raw-serial/README.md similarity index 100% rename from examples/examples-communication/http-client/streams-url_raw-serial/README.md rename to examples/examples-stream/streams-url_raw-serial/README.md diff --git a/examples/examples-communication/http-client/streams-url_raw-serial/streams-url_raw-serial.ino b/examples/examples-stream/streams-url_raw-serial/streams-url_raw-serial.ino similarity index 94% rename from examples/examples-communication/http-client/streams-url_raw-serial/streams-url_raw-serial.ino rename to examples/examples-stream/streams-url_raw-serial/streams-url_raw-serial.ino index bab43889fe..bdcb2f2412 100644 --- a/examples/examples-communication/http-client/streams-url_raw-serial/streams-url_raw-serial.ino +++ b/examples/examples-stream/streams-url_raw-serial/streams-url_raw-serial.ino @@ -20,7 +20,7 @@ StreamCopy copier(printer, music); // copies music into printer void setup(void) { // Open Serial Serial.begin(115200); - AudioToolsLogger.begin(Serial, AudioToolsLogLevel::Info); + AudioLogger::instance().begin(Serial, AudioLogger::Info); // connect to WIFI WiFi.begin("network-name", "password"); diff --git a/examples/examples-communication/http-client/streams-url_vorbis_i2s/streams-url_vorbis_i2s.ino b/examples/examples-stream/streams-url_vorbis_i2s/streams-url_vorbis_i2s.ino similarity index 82% rename from examples/examples-communication/http-client/streams-url_vorbis_i2s/streams-url_vorbis_i2s.ino rename to examples/examples-stream/streams-url_vorbis_i2s/streams-url_vorbis_i2s.ino index 5e25d3d127..6b842e3ab7 100644 --- a/examples/examples-communication/http-client/streams-url_vorbis_i2s/streams-url_vorbis_i2s.ino +++ b/examples/examples-stream/streams-url_vorbis_i2s/streams-url_vorbis_i2s.ino @@ -9,7 +9,7 @@ * */ #include "AudioTools.h" -#include "AudioTools/AudioCodecs/CodecVorbis.h" +#include "AudioCodecs/CodecVorbis.h" const char* ssid = "ssid"; const char* pwd = "password"; @@ -19,12 +19,12 @@ I2SStream i2s; void setup() { Serial.begin(115200); - AudioToolsLogger.begin(Serial, AudioToolsLogLevel::Info); + AudioLogger::instance().begin(Serial, AudioLogger::Info); i2s.begin(i2s.defaultConfig(TX_MODE)); url.begin("http://marmalade.scenesat.com:8086/bitjam.ogg","application/ogg"); - dec.setInput(url); + dec.setInputStream(url); dec.setOutput(i2s); dec.begin(); } diff --git a/examples/examples-tts/streams-url_wav-i2s/README.md b/examples/examples-stream/streams-url_wav-i2s/README.md similarity index 100% rename from examples/examples-tts/streams-url_wav-i2s/README.md rename to examples/examples-stream/streams-url_wav-i2s/README.md diff --git a/examples/examples-tts/streams-url_wav-i2s/streams-url_wav-i2s.ino b/examples/examples-stream/streams-url_wav-i2s/streams-url_wav-i2s.ino similarity index 75% rename from examples/examples-tts/streams-url_wav-i2s/streams-url_wav-i2s.ino rename to examples/examples-stream/streams-url_wav-i2s/streams-url_wav-i2s.ino index 62e439e0ec..23aa17ff7c 100644 --- a/examples/examples-tts/streams-url_wav-i2s/streams-url_wav-i2s.ino +++ b/examples/examples-stream/streams-url_wav-i2s/streams-url_wav-i2s.ino @@ -1,7 +1,7 @@ /** * @file streams-url_wav-serial.ino * @author Phil Schatzmann - * @brief decode WAV stream from rhasspy url and output it on I2S + * @brief decode WAV stream from url and output it on I2S * @version 0.1 * @date 2021-96-25 * @@ -10,18 +10,20 @@ */ #include "AudioTools.h" + + // UrlStream -copy-> EncodedAudioStream -> I2S URLStream url("http://github.com/ssid%22,%22password"); I2SStream i2s; // I2S stream -WAVDecoder decoder; // decode wav to pcm and send it to I2S -EncodedAudioStream out(&i2s, &decoder); // Decoder stream +WAVDecoder decoder(i2s); // decode wav to pcm and send it to I2S +EncodedAudioStream out(i2s, decoder); // Decoder stream StreamCopy copier(out, url); // copy in to out void setup(){ Serial.begin(115200); - AudioToolsLogger.begin(Serial, AudioToolsLogLevel::Info); + AudioLogger::instance().begin(Serial, AudioLogger::Info); // setup i2s auto config = i2s.defaultConfig(TX_MODE); diff --git a/examples/examples-tts/streams-azure_tts-i2s/streams-azure_tts-i2s.ino b/examples/examples-tts/streams-azure_tts-i2s/streams-azure_tts-i2s.ino index e265410f69..cdbdf8dd92 100644 --- a/examples/examples-tts/streams-azure_tts-i2s/streams-azure_tts-i2s.ino +++ b/examples/examples-tts/streams-azure_tts-i2s/streams-azure_tts-i2s.ino @@ -17,15 +17,17 @@ String msg = "This is a demonstration of Phil Schatzmann's AudioTools integratin URLStream AzureURLStream("ssid", "pwd"); I2SStream i2s; // or I2SStream -StreamCopy copier(i2s, AzureURLStream); // copy in to out +WAVDecoder decoder(i2s); // decode wav to pcm and send it to I2S +EncodedAudioStream out(i2s, decoder); // Decoder stream +StreamCopy copier(out, AzureURLStream); // copy in to out void setup(){ Serial.begin(115200); - AudioToolsLogger.begin(Serial, AudioToolsLogLevel::Info); + AudioLogger::instance().begin(Serial, AudioLogger::Info); // setup i2s auto config = i2s.defaultConfig(TX_MODE); - config.sample_rate = 8000; + config.sample_rate = 16000; config.bits_per_sample = 16; config.channels = 1; config.pin_ws = GPIO_NUM_12; //LCK @@ -36,7 +38,7 @@ void setup(){ // Source: https://learn.microsoft.com/en-us/azure/cognitive-services/speech-service/get-started-text-to-speech?tabs=windows%2Cterminal&pivots=programming-language-rest String ssml = "" + msg + ""; AzureURLStream.addRequestHeader("Ocp-Apim-Subscription-Key", speechKey.c_str()); - AzureURLStream.addRequestHeader("X-Microsoft-OutputFormat", "raw-8khz-16bit-mono-pcm"); // if you change this, change the settings for i2s and the decoder + AzureURLStream.addRequestHeader("X-Microsoft-OutputFormat", "riff-16khz-16bit-mono-pcm"); // if you change this, change the settings for i2s and the decoder AzureURLStream.addRequestHeader(USER_AGENT, String("Arduino with Audiotools version:" + String(AUDIOTOOLS_VERSION)).c_str()); AzureURLStream.begin(url_str.c_str(), "audio/wav", POST, "application/ssml+xml", ssml.c_str()); diff --git a/examples/examples-tts/streams-espeak-audiokit/partitions.csv b/examples/examples-tts/streams-espeak-audiokit/partitions.csv deleted file mode 100644 index 3a1aa5869e..0000000000 --- a/examples/examples-tts/streams-espeak-audiokit/partitions.csv +++ /dev/null @@ -1,3 +0,0 @@ -# Name, Type, SubType, Offset, Size, Flags -nvs, data, nvs, 36K, 20K, -factory, app, factory, 64K, 4000K, diff --git a/examples/examples-tts/streams-espeak-audiokit/streams-espeak-audiokit.ino b/examples/examples-tts/streams-espeak-audiokit/streams-espeak-audiokit.ino index 12bc735b69..9fce585ec5 100644 --- a/examples/examples-tts/streams-espeak-audiokit/streams-espeak-audiokit.ino +++ b/examples/examples-tts/streams-espeak-audiokit/streams-espeak-audiokit.ino @@ -11,11 +11,11 @@ */ #include "AudioTools.h" // https://github.com/pschatzmann/arduino-audio-tools -#include "AudioTools/AudioLibs/AudioBoardStream.h" // https://github.com/pschatzmann/arduino-audio-driver +#include "AudioLibs/AudioKit.h" // https://github.com/pschatzmann/arduino-audiokit #include "FileSystems.h" // https://github.com/pschatzmann/arduino-posix-fs -#include "espeak.h" // https://github.com/pschatzmann/arduino-espeak-ng +#include "espeak.h" -AudioBoardStream i2s(AudioKitEs8388V1); +AudioKitStream i2s; ESpeak espeak(i2s); void setup() { diff --git a/examples/examples-tts/streams-espeak-i2s/partitions.csv b/examples/examples-tts/streams-espeak-i2s/partitions.csv deleted file mode 100644 index 3a1aa5869e..0000000000 --- a/examples/examples-tts/streams-espeak-i2s/partitions.csv +++ /dev/null @@ -1,3 +0,0 @@ -# Name, Type, SubType, Offset, Size, Flags -nvs, data, nvs, 36K, 20K, -factory, app, factory, 64K, 4000K, diff --git a/examples/examples-tts/streams-espeak-i2s/streams-espeak-i2s.ino b/examples/examples-tts/streams-espeak-i2s/streams-espeak-i2s.ino index 2b761a90c4..ab7d300fe5 100644 --- a/examples/examples-tts/streams-espeak-i2s/streams-espeak-i2s.ino +++ b/examples/examples-tts/streams-espeak-i2s/streams-espeak-i2s.ino @@ -12,7 +12,7 @@ #include "AudioTools.h" // https://github.com/pschatzmann/arduino-audio-tools #include "FileSystems.h" // https://github.com/pschatzmann/arduino-posix-fs -#include "espeak.h" // https://github.com/pschatzmann/arduino-espeak-ng +#include "espeak.h" I2SStream i2s; // or replace with any other audio sink ESpeak espeak(i2s); diff --git a/examples/examples-tts/streams-flite-audiokit/README.md b/examples/examples-tts/streams-flite-audiokit/README.md index ba71663768..a57cb18d81 100644 --- a/examples/examples-tts/streams-flite-audiokit/README.md +++ b/examples/examples-tts/streams-flite-audiokit/README.md @@ -12,8 +12,8 @@ The output goes to a ‘AI Thinker Audio Kit’. You need to install the following libraries: - [Arduino Audio Tools](https://github.com/pschatzmann/arduino-audio-tools) -- [Audio Driver](https://github.com/pschatzmann/arduino-audio-driver) +- [AudioKit](https://github.com/pschatzmann/arduino-audiokit) - [FLITE](https://github.com/pschatzmann/arduino-flite) -FLITE is quite big: you need to need to use the custom Partition Schema or RainMaker 4MB no OTA +FLITE is quite big: you need to set the Partition Schema to Huge APP diff --git a/examples/examples-tts/streams-flite-audiokit/partitions.csv b/examples/examples-tts/streams-flite-audiokit/partitions.csv deleted file mode 100644 index 3a1aa5869e..0000000000 --- a/examples/examples-tts/streams-flite-audiokit/partitions.csv +++ /dev/null @@ -1,3 +0,0 @@ -# Name, Type, SubType, Offset, Size, Flags -nvs, data, nvs, 36K, 20K, -factory, app, factory, 64K, 4000K, diff --git a/examples/examples-tts/streams-flite-audiokit/streams-flite-audiokit.ino b/examples/examples-tts/streams-flite-audiokit/streams-flite-audiokit.ino index b9b2e9d66e..eebdce7bac 100644 --- a/examples/examples-tts/streams-flite-audiokit/streams-flite-audiokit.ino +++ b/examples/examples-tts/streams-flite-audiokit/streams-flite-audiokit.ino @@ -1,7 +1,6 @@ /** @file streams-flite-audiokit.ino - * You need to install https://github.com/pschatzmann/arduino-flite - Read the README.md! + @author Phil Schatzmann @copyright GPLv3 @@ -9,9 +8,9 @@ #include "flite_arduino.h" #include "AudioTools.h" -#include "AudioTools/AudioLibs/AudioBoardStream.h" +#include "AudioLibs/AudioKit.h" -AudioBoardStream kit(AudioKitEs8388V1); +AudioKitStream kit; Flite flite(kit); const char* alice = "Hallo my name is FLITE"; diff --git a/examples/examples-tts/streams-flite-i2s/README.md b/examples/examples-tts/streams-flite-i2s/README.md index 4ddadcbcbf..bc268b4e4a 100644 --- a/examples/examples-tts/streams-flite-i2s/README.md +++ b/examples/examples-tts/streams-flite-i2s/README.md @@ -5,8 +5,6 @@ You need to install https://github.com/pschatzmann/arduino-flite In this demo we provide the result as I2SStream but you can easly replace with any other output stream. -FLITE is quite big: you need to need to use the custom Partition Schema or RainMaker 4MB no OTA - ## External DAC for defails see the [Wiki](https://github.com/pschatzmann/arduino-audio-tools/wiki/External-DAC) \ No newline at end of file diff --git a/examples/examples-tts/streams-flite-i2s/partitions.csv b/examples/examples-tts/streams-flite-i2s/partitions.csv deleted file mode 100644 index 3a1aa5869e..0000000000 --- a/examples/examples-tts/streams-flite-i2s/partitions.csv +++ /dev/null @@ -1,3 +0,0 @@ -# Name, Type, SubType, Offset, Size, Flags -nvs, data, nvs, 36K, 20K, -factory, app, factory, 64K, 4000K, diff --git a/examples/examples-tts/streams-flite-i2s/streams-flite-i2s.ino b/examples/examples-tts/streams-flite-i2s/streams-flite-i2s.ino index caabef7fa0..c9eca9668c 100644 --- a/examples/examples-tts/streams-flite-i2s/streams-flite-i2s.ino +++ b/examples/examples-tts/streams-flite-i2s/streams-flite-i2s.ino @@ -1,7 +1,6 @@ /** * @file streams-flite-i2s.ino * You need to install https://github.com/pschatzmann/arduino-flite - * * @author Phil Schatzmann * @copyright GPLv3 * @@ -9,15 +8,15 @@ #include "flite_arduino.h" #include "AudioTools.h" -//#include "AudioTools/AudioLibs/AudioBoardStream.h" +//#include "AudioLibs/AudioKit.h" -I2SStream out; // Replace with desired class e.g. AudioBoardStream, AnalogAudioStream etc. +I2SStream out; // Replace with desired class e.g. AudioKitStream, AnalogAudioStream etc. Flite flite(out); void setup(){ Serial.begin(115200); - AudioToolsLogger.begin(Serial, AudioToolsLogLevel::Info); + AudioLogger::instance().begin(Serial, AudioLogger::Info); // start data sink auto cfg = out.defaultConfig(); diff --git a/examples/examples-tts/streams-google-audiokit/streams-google-audiokit.ino b/examples/examples-tts/streams-google-audiokit/streams-google-audiokit.ino deleted file mode 100644 index 591c3003a7..0000000000 --- a/examples/examples-tts/streams-google-audiokit/streams-google-audiokit.ino +++ /dev/null @@ -1,57 +0,0 @@ -/** - * @file streams-google-audiokit.ino - * @author Phil Schatzmann - * @brief decode MP3 stream from url and output it on I2S on audiokit. - * We are using the free google translate service to generate the mp3 - * @version 0.1 - * @date 2021-96-25 - * - * @copyright Copyright (c) 2021 - */ - -// install https://github.com/pschatzmann/arduino-libhelix.git - -#include "AudioTools.h" -#include "AudioTools/AudioCodecs/CodecMP3Helix.h" -#include "AudioTools/AudioLibs/AudioBoardStream.h" - - -URLStream url("http://github.com/ssid%22,%22password"); -AudioBoardStream i2s(AudioKitEs8388V1); // final output of decoded stream -EncodedAudioStream dec(&i2s, new MP3DecoderHelix()); // Decoding stream -StreamCopy copier(dec, url); // copy url to decoder -Str query("http://translate.google.com/translate_tts?ie=UTF-8&tl=%1&client=tw-ob&ttsspeed=%2&q=%3"); - -const char* tts(const char* text, const char* lang="en", const char* speed="1"){ - query.replace("%1",lang); - query.replace("%2",speed); - - Str encoded(text); - encoded.urlEncode(); - query.replace("%3", encoded.c_str()); - return query.c_str(); -} - -void setup(){ - Serial.begin(115200); - AudioToolsLogger.begin(Serial, AudioToolsLogLevel::Info); - - // setup i2s - auto config = i2s.defaultConfig(TX_MODE); - i2s.begin(config); - - // setup decoder - dec.begin(); - - // display url - const char* url_str = tts("this is an english text"); - Serial.println(url_str); - - // generate mp3 with the help of google translate - url.begin(url_str ,"audio/mp3"); - -} - -void loop(){ - copier.copy(); -} diff --git a/examples/examples-tts/streams-sam-audiokit/README.md b/examples/examples-tts/streams-sam-audiokit/README.md index 777d8cc4cf..7e3e5120d3 100644 --- a/examples/examples-tts/streams-sam-audiokit/README.md +++ b/examples/examples-tts/streams-sam-audiokit/README.md @@ -12,7 +12,7 @@ The output goes to a ‘AI Thinker Audio Kit’. You need to install the following libraries: - [Arduino Audio Tools](https://github.com/pschatzmann/arduino-audio-tools) -- [Audio Driver](https://github.com/pschatzmann/arduino-audio-driver) +- [AudioKit](https://github.com/pschatzmann/arduino-audiokit) - [SAM](https://github.com/pschatzmann/arduino-SAM) diff --git a/examples/examples-tts/streams-sam-audiokit/streams-sam-audiokit.ino b/examples/examples-tts/streams-sam-audiokit/streams-sam-audiokit.ino index b0cab9377d..7c32d20fe4 100644 --- a/examples/examples-tts/streams-sam-audiokit/streams-sam-audiokit.ino +++ b/examples/examples-tts/streams-sam-audiokit/streams-sam-audiokit.ino @@ -7,10 +7,10 @@ */ #include "AudioTools.h" -#include "AudioTools/AudioLibs/AudioBoardStream.h" +#include "AudioLibs/AudioKit.h" #include "sam_arduino.h" -AudioBoardStream kit(AudioKitEs8388V1); +AudioKitStream kit; SAM sam(kit); const char* text = "Hallo my name is SAM"; diff --git a/examples/examples-tts/streams-sam-i2s/README.md b/examples/examples-tts/streams-sam-i2s/README.md index 2fa0789c8f..0b2c58458c 100644 --- a/examples/examples-tts/streams-sam-i2s/README.md +++ b/examples/examples-tts/streams-sam-i2s/README.md @@ -1,10 +1,10 @@ # Using SAM Speach to Text I am providing a simple sketch which generates sound data with the SAM text to speach engine. -You need to install https://github.com/pschatzmann/arduino-SAM +You need to install https://github.com/pschatzmann/SAM In this demo we provide the result as I2SStream but you can easly replace with any other output stream. ## External DAC -for defails see the [Wiki](https://github.com/pschatzmann/arduino-audio-tools/wiki/External-DAC) +for defails see the [Wiki](https://github.com/pschatzmann/arduino-audio-tools/wiki/External-DAC) \ No newline at end of file diff --git a/examples/examples-tts/streams-sam-i2s/streams-sam-i2s.ino b/examples/examples-tts/streams-sam-i2s/streams-sam-i2s.ino index 79927cd16a..a95691c52c 100644 --- a/examples/examples-tts/streams-sam-i2s/streams-sam-i2s.ino +++ b/examples/examples-tts/streams-sam-i2s/streams-sam-i2s.ino @@ -7,10 +7,10 @@ */ #include "AudioTools.h" #include "sam_arduino.h" -//#include "AudioTools/AudioLibs/AudioBoardStream.h" +//#include "AudioLibs/AudioKit.h" -I2SStream out; // Replace with desired class e.g. AudioBoardStream, AnalogAudioStream etc. +I2SStream out; // Replace with desired class e.g. AudioKitStream, AnalogAudioStream etc. SAM sam(out, false); // Callback which provides the audio data @@ -19,7 +19,7 @@ void outputData(Print *out){ void setup(){ Serial.begin(115200); - AudioToolsLogger.begin(Serial, AudioToolsLogLevel::Info); + AudioLogger::instance().begin(Serial, AudioLogger::Info); // start data sink auto cfg = out.defaultConfig(); diff --git a/examples/examples-tts/streams-simple_tts-a2dp/README.md b/examples/examples-tts/streams-simple_tts-a2dp/README.md deleted file mode 100644 index d8d05fd7c6..0000000000 --- a/examples/examples-tts/streams-simple_tts-a2dp/README.md +++ /dev/null @@ -1,17 +0,0 @@ -# Simple TTS to A2DP - -This is just an adaptation of the [streams-simple_tts-i2s example](https://github.com/pschatzmann/arduino-audio-tools/tree/main/examples/examples-tts/streams-simple_tts-i2s) where the output stream has been replaced with a A2DPStream. - -So the output goes to a Bluetooth Speaker! - -More examples can be found at https://github.com/pschatzmann/arduino-simple-tts/tree/main/examples - -Because we need a lot of progmem, do not forget to set the partition scheme to Huge APP! - -### Dependencies - -- https://github.com/pschatzmann/ESP32-A2DP.git -- https://github.com/pschatzmann/arduino-simple-tts - - -A word of warning: The A2DP output has it's challenges, so I do not recommend this as your first sketch. \ No newline at end of file diff --git a/examples/examples-tts/streams-simple_tts-a2dp/streams-simple_tts-a2dp.ino b/examples/examples-tts/streams-simple_tts-a2dp/streams-simple_tts-a2dp.ino deleted file mode 100644 index 9869597cf9..0000000000 --- a/examples/examples-tts/streams-simple_tts-a2dp/streams-simple_tts-a2dp.ino +++ /dev/null @@ -1,57 +0,0 @@ -/** - * @file streams-tts-a2dp.ino - * @author Phil Schatzmann - * @copyright GPLv3 - * - */ - - -#include "AudioTools.h" -#include "AudioTools/AudioCodecs/CodecMP3Helix.h" -#include "AudioTools/AudioLibs/A2DPStream.h" -#include "SimpleTTS.h" - -const char* name = "LEXON MINO L"; // Replace with your device name - -AudioInfo from(24000, 1, 16); // TTS -AudioInfo to(44100, 2, 16); // A2DP - -NumberToText ntt; -A2DPStream a2dp; -FormatConverterStream out(a2dp); -MP3DecoderHelix mp3; -AudioDictionary dictionary(ExampleAudioDictionaryValues); -TextToSpeech tts(ntt, out, mp3, dictionary); - -int64_t number = 1; - -void setup(){ - Serial.begin(115200); - AudioToolsLogger.begin(Serial, AudioToolsLogLevel::Info); - Serial.println("Starting..."); - - // setup conversion to provide stereo at 44100hz - out.begin(from, to); - - // setup a2dp - auto cfg = a2dp.defaultConfig(TX_MODE); - cfg.name = name; - cfg.silence_on_nodata = true; // allow delays with silence - a2dp.begin(cfg); - a2dp.setVolume(0.3); - - Serial.println("A2DP Started"); - -} - - -void loop() { - // speach output - Serial.print("Providing data: "); - Serial.println(number); - - ntt.say(number); - - number +=1; - delay(1000); -} diff --git a/examples/examples-tts/streams-simple_tts-i2s/README.md b/examples/examples-tts/streams-simple_tts-i2s/README.md index 146998cabc..3760f69b23 100644 --- a/examples/examples-tts/streams-simple_tts-i2s/README.md +++ b/examples/examples-tts/streams-simple_tts-i2s/README.md @@ -1,8 +1,6 @@ -# Simple TTS - -I am providing a simple sketch which generates sound data with my Simple TTS text to speach engine that -uses a configurable library of prerecorded words. +# Using SAM Speach to Text +I am providing a simple sketch which generates sound data with the TTS text to speach engine. You need to install https://github.com/pschatzmann/arduino-simple-tts In this demo we provide the result as I2SStream but you can easly replace with any other output stream. diff --git a/examples/examples-tts/streams-simple_tts-i2s/streams-simple_tts-i2s.ino b/examples/examples-tts/streams-simple_tts-i2s/streams-simple_tts-i2s.ino index 9e88bad6bd..b250ade9cd 100644 --- a/examples/examples-tts/streams-simple_tts-i2s/streams-simple_tts-i2s.ino +++ b/examples/examples-tts/streams-simple_tts-i2s/streams-simple_tts-i2s.ino @@ -7,12 +7,12 @@ */ #include "SimpleTTS.h" -#include "AudioTools/AudioCodecs/CodecMP3Helix.h" -//#include "AudioTools/AudioLibs/AudioBoardStream.h" +#include "AudioCodecs/CodecMP3Helix.h" +//#include "AudioLibs/AudioKit.h" NumberToText ntt; -I2SStream out; // Replace with desired class e.g. AudioBoardStream, AnalogAudioStream etc. +I2SStream out; // Replace with desired class e.g. AudioKitStream, AnalogAudioStream etc. MP3DecoderHelix mp3; AudioDictionary dictionary(ExampleAudioDictionaryValues); TextToSpeech tts(ntt, out, mp3, dictionary); @@ -21,7 +21,7 @@ int64_t number = 1; void setup(){ Serial.begin(115200); - AudioToolsLogger.begin(Serial, AudioToolsLogLevel::Warning); + AudioLogger::instance().begin(Serial, AudioLogger::Warning); // setup out auto cfg = out.defaultConfig(); diff --git a/examples/examples-tts/streams-talkie-a2dp/streams-talkie-a2dp.ino b/examples/examples-tts/streams-talkie-a2dp/streams-talkie-a2dp.ino deleted file mode 100644 index be4bcaf8d9..0000000000 --- a/examples/examples-tts/streams-talkie-a2dp/streams-talkie-a2dp.ino +++ /dev/null @@ -1,54 +0,0 @@ -/** - * @file streams-talkie-a2dp.ino - * @author Phil Schatzmann - * @copyright GPLv3 - * Using TalkiePCM to generate audio to be sent to a Bluetooth Speaker - */ - -#include "AudioTools.h" -#include "AudioTools/AudioCodecs/CodecMP3Helix.h" -#include "AudioTools/AudioLibs/A2DPStream.h" -#include "TalkiePCM.h" // https://github.com/pschatzmann/TalkiePCM -#include "Vocab_US_Large.h" - -const char* name = "LEXON MINO L"; // Replace with your device name - -AudioInfo from(8000, 2, 16); // TTS -AudioInfo to(44100, 2, 16); // A2DP - -A2DPStream a2dp; -FormatConverterStream out(a2dp); -// talkie is sumbmitting too many individual samples, so we buffer them -BufferedStream bs(1024, out); -TalkiePCM voice(bs, from.channels); - -void setup() { - Serial.begin(115200); - AudioToolsLogger.begin(Serial, AudioToolsLogLevel::Info); - Serial.println("Starting..."); - - // setup conversion to provide stereo at 44100hz - out.begin(from, to); - - // setup a2dp - auto cfg = a2dp.defaultConfig(TX_MODE); - cfg.name = name; - cfg.silence_on_nodata = true; // allow delays with silence - a2dp.begin(cfg); - a2dp.setVolume(0.3); - - Serial.println("A2DP Started"); -} - -void loop() { - voice.say(sp2_DANGER); - voice.say(sp2_DANGER); - voice.say(sp2_RED); - voice.say(sp2_ALERT); - voice.say(sp2_MOTOR); - voice.say(sp2_IS); - voice.say(sp2_ON); - voice.say(sp2_FIRE); - bs.flush(); - voice.silence(1000); -} diff --git a/examples/examples-tts/streams-talkie-audiokit/streams-talkie-audiokit.ino b/examples/examples-tts/streams-talkie-audiokit/streams-talkie-audiokit.ino deleted file mode 100644 index 17db4db36c..0000000000 --- a/examples/examples-tts/streams-talkie-audiokit/streams-talkie-audiokit.ino +++ /dev/null @@ -1,40 +0,0 @@ -/** - * @file streams-talkie-audiokit.ino - * We use the TalkiePCM TTS library to generate the audio - * You need to install https://github.com/pschatzmann/TalkiePCM - * @author Phil Schatzmann - * @copyright GPLv3 - */ - -#include "AudioTools.h" -#include "AudioTools/AudioLibs/AudioBoardStream.h" //https://github.com/pschatzmann/arduino-audio-driver -#include "TalkiePCM.h" // https://github.com/pschatzmann/TalkiePCM -#include "Vocab_US_Large.h" - -const AudioInfo info(8000, 2, 16); -AudioBoardStream out(AudioKitEs8388V1); // Audio sink -//CsvOutput out(Serial); -TalkiePCM voice(out, info.channels); - -void setup() { - Serial.begin(115200); - AudioToolsLogger.begin(Serial, AudioToolsLogLevel::Info); - // setup AudioKit - auto cfg = out.defaultConfig(); - cfg.copyFrom(info); - out.begin(cfg); - - Serial.println("Talking..."); -} - -void loop() { - voice.say(sp2_DANGER); - voice.say(sp2_DANGER); - voice.say(sp2_RED); - voice.say(sp2_ALERT); - voice.say(sp2_MOTOR); - voice.say(sp2_IS); - voice.say(sp2_ON); - voice.say(sp2_FIRE); - voice.silence(1000); -} \ No newline at end of file diff --git a/examples/examples-tts/streams-tts-i2s/streams-tts-i2s.ino b/examples/examples-tts/streams-tts-i2s/streams-tts-i2s.ino index 50b4092720..4ab83e32fb 100644 --- a/examples/examples-tts/streams-tts-i2s/streams-tts-i2s.ino +++ b/examples/examples-tts/streams-tts-i2s/streams-tts-i2s.ino @@ -8,15 +8,15 @@ #include "AudioTools.h" #include "TTS.h" -//#include "AudioTools/AudioLibs/AudioBoardStream.h" +//#include "AudioLibs/AudioKit.h" -I2SStream out; // Replace with desired class e.g. AudioBoardStream, AnalogAudioStream etc. +I2SStream out; // Replace with desired class e.g. AudioKitStream, AnalogAudioStream etc. TTS tts = TTS(out); void setup(){ Serial.begin(115200); - AudioToolsLogger.begin(Serial, AudioToolsLogLevel::Info); + AudioLogger::instance().begin(Serial, AudioLogger::Info); // start data sink TTSInfo info = TTS::getInfo(); diff --git a/examples/examples-vs1053/player-sd-vs1053/player-sd-vs1053.ino b/examples/examples-vs1053/player-sd-vs1053/player-sd-vs1053.ino index 226d108dbf..8bd61031b1 100644 --- a/examples/examples-vs1053/player-sd-vs1053/player-sd-vs1053.ino +++ b/examples/examples-vs1053/player-sd-vs1053/player-sd-vs1053.ino @@ -9,20 +9,23 @@ // install https://github.com/pschatzmann/arduino-vs1053.git #include "AudioTools.h" -#include "AudioTools/AudioLibs/VS1053Stream.h" -#include "AudioTools/Disk/AudioSourceSD.h" -#include "AudioTools/AudioCodecs/CodecCopy.h" +#include "AudioLibs/VS1053Stream.h" +#include "AudioLibs/AudioSourceSDFAT.h" +#include "AudioCodecs/CodecCopy.h" + +#define SD_CARD_CS 22 const char *startFilePath="/"; const char* ext="mp3"; -AudioSourceSD source(startFilePath, ext); +SdSpiConfig sdcfg(SD_CARD_CS, DEDICATED_SPI, SD_SCK_MHZ(10) , &SPI); +AudioSourceSDFAT source(startFilePath, ext, sdcfg); VS1053Stream vs1053; // final output AudioPlayer player(source, vs1053, *new CopyDecoder()); void setup() { Serial.begin(115200); - AudioToolsLogger.begin(Serial, AudioToolsLogLevel::Info); + AudioLogger::instance().begin(Serial, AudioLogger::Info); // setup output auto cfg = vs1053.defaultConfig(); diff --git a/examples/examples-vs1053/player-sdfat-vs1053/player-sdfat-vs1053.ino b/examples/examples-vs1053/player-sdfat-vs1053/player-sdfat-vs1053.ino deleted file mode 100644 index deadd6023c..0000000000 --- a/examples/examples-vs1053/player-sdfat-vs1053/player-sdfat-vs1053.ino +++ /dev/null @@ -1,51 +0,0 @@ -/** - * @file player-sdfat-vs1053.ino - * @brief Audio player which sends the output to a VS1053 module. Using a module with built in SD card - * - * @author Phil Schatzmann - * @copyright GPLv3 - */ - -// install https://github.com/pschatzmann/arduino-vs1053.git -// install https://github.com/greiman/SdFat - -#include "AudioTools.h" -#include "AudioTools/AudioLibs/VS1053Stream.h" -#include "AudioTools/Disk/AudioSourceSDFAT.h" -#include "AudioTools/AudioCodecs/CodecCopy.h" - -const char *startFilePath="/"; -const char* ext="mp3"; -SdSpiConfig sd_cfg(PIN_CS, SHARED_SPI, SPI_CLOCK); -AudioSourceSDFAT source(startFilePath, ext, sd_cfg); -VS1053Stream vs1053; // final output -AudioPlayer player(source, vs1053, *new CopyDecoder()); - - -void setup() { - Serial.begin(115200); - AudioToolsLogger.begin(Serial, AudioToolsLogLevel::Info); - - // setup output - auto cfg = vs1053.defaultConfig(); - cfg.is_encoded_data = true; // vs1053 is accepting encoded data - // Use your custom pins or define in AudioCodnfig.h - //cfg.cs_pin = VS1053_CS; - //cfg.dcs_pin = VS1053_DCS; - //cfg.dreq_pin = VS1053_DREQ; - //cfg.reset_pin = VS1053_RESET; - vs1053.begin(cfg); - vs1053.setVolume(1.0); // full volume - - // setup player - player.begin(); - - // select file with setPath() or setIndex() - //player.setPath("/ZZ Top/Unknown Album/Lowrider.mp3"); - //player.setIndex(1); // 2nd file - -} - -void loop() { - player.copy(); -} diff --git a/examples/examples-vs1053/streams-generator-vs1053/streams-generator-vs1053.ino b/examples/examples-vs1053/streams-generator-vs1053/streams-generator-vs1053.ino index ab7b4c179f..0bbdd6c3ff 100644 --- a/examples/examples-vs1053/streams-generator-vs1053/streams-generator-vs1053.ino +++ b/examples/examples-vs1053/streams-generator-vs1053/streams-generator-vs1053.ino @@ -12,7 +12,7 @@ // install https://github.com/pschatzmann/arduino-vs1053.git #include "AudioTools.h" -#include "AudioTools/AudioLibs/VS1053Stream.h" +#include "AudioLibs/VS1053Stream.h" AudioInfo info(44100, 2, 16); SineWaveGenerator sineWave(32000); // subclass of SoundGenerator with max amplitude of 32000 @@ -23,10 +23,10 @@ StreamCopy copier(out, sound); // copy sound to decoder void setup(){ Serial.begin(115200); - AudioToolsLogger.begin(Serial, AudioToolsLogLevel::Warning); // Info is causing a lot of noise + AudioLogger::instance().begin(Serial, AudioLogger::Warning); // Info is causing a lot of noise // Setup sine wave - sineWave.begin(info, N_A4); + sineWave.begin(channels, sample_rate, N_A4); // setup output auto cfg = out.defaultConfig(); diff --git a/examples/examples-vs1053/streams-midi-vs1053/streams-midi-vs1053.ino b/examples/examples-vs1053/streams-midi-vs1053/streams-midi-vs1053.ino index 1d96c4f4d8..201385aed9 100644 --- a/examples/examples-vs1053/streams-midi-vs1053/streams-midi-vs1053.ino +++ b/examples/examples-vs1053/streams-midi-vs1053/streams-midi-vs1053.ino @@ -9,7 +9,7 @@ * */ #include "AudioTools.h" -#include "AudioTools/AudioLibs/VS1053Stream.h" +#include "AudioLibs/VS1053Stream.h" VS1053Stream vs1053; // final output @@ -40,7 +40,7 @@ void selectInstrument(uint8_t channel, uint8_t instrument ){ void setup() { Serial.begin(115200); - AudioToolsLogger.begin(Serial, AudioToolsLogLevel::Info); + AudioLogger::instance().begin(Serial, AudioLogger::Info); // setup vs1053 auto cfg = vs1053.defaultConfig(); diff --git a/examples/examples-vs1053/streams-midi_lib-vs1053/streams-midi_lib-vs1053.ino b/examples/examples-vs1053/streams-midi_lib-vs1053/streams-midi_lib-vs1053.ino index 9d0eb1213c..4cd0c1859f 100644 --- a/examples/examples-vs1053/streams-midi_lib-vs1053/streams-midi_lib-vs1053.ino +++ b/examples/examples-vs1053/streams-midi_lib-vs1053/streams-midi_lib-vs1053.ino @@ -11,7 +11,7 @@ */ #include "AudioTools.h" -#include "AudioTools/AudioLibs/VS1053Stream.h" +#include "AudioLibs/VS1053Stream.h" #include "Midi.h" @@ -34,7 +34,7 @@ void selectInstrument(uint8_t instrument, uint8_t bank=0x00){ void setup() { Serial.begin(115200); - AudioToolsLogger.begin(Serial, AudioToolsLogLevel::Info); + AudioLogger::instance().begin(Serial, AudioLogger::Info); // setup vs1053 auto cfg = vs1053.defaultConfig(); diff --git a/examples/examples-vs1053/streams-midi_memory-vs1053/streams-midi_memory-vs1053.ino b/examples/examples-vs1053/streams-midi_memory-vs1053/streams-midi_memory-vs1053.ino index 8d8258bb93..c25ff88e59 100644 --- a/examples/examples-vs1053/streams-midi_memory-vs1053/streams-midi_memory-vs1053.ino +++ b/examples/examples-vs1053/streams-midi_memory-vs1053/streams-midi_memory-vs1053.ino @@ -10,7 +10,7 @@ * */ #include "AudioTools.h" -#include "AudioTools/AudioLibs/VS1053Stream.h" +#include "AudioLibs/VS1053Stream.h" #include "bora0.h" @@ -20,7 +20,7 @@ StreamCopyT copier(out, music); // copies sound into i2s void setup(){ Serial.begin(115200); - AudioToolsLogger.begin(Serial, AudioToolsLogLevel::Info); + AudioLogger::instance().begin(Serial, AudioLogger::Info); auto config = out.defaultConfig(TX_MODE); config.is_encoded_data = true; // vs1053 is accepting encoded midi data diff --git a/examples/examples-vs1053/streams-url_mp3-vs1053/streams-url_mp3-vs1053.ino b/examples/examples-vs1053/streams-url_mp3-vs1053/streams-url_mp3-vs1053.ino index 6a113c9da6..583f06d9be 100644 --- a/examples/examples-vs1053/streams-url_mp3-vs1053/streams-url_mp3-vs1053.ino +++ b/examples/examples-vs1053/streams-url_mp3-vs1053/streams-url_mp3-vs1053.ino @@ -11,7 +11,7 @@ // install https://github.com/pschatzmann/arduino-vs1053.git #include "AudioTools.h" -#include "AudioTools/AudioLibs/VS1053Stream.h" +#include "AudioLibs/VS1053Stream.h" URLStream url("http://github.com/ssid%22,%22password"); // or replace with ICYStream to get metadata VS1053Stream vs1053; // final output @@ -19,7 +19,7 @@ StreamCopy copier(vs1053, url); // copy url to decoder void setup(){ Serial.begin(115200); - AudioToolsLogger.begin(Serial, AudioToolsLogLevel::Info); + AudioLogger::instance().begin(Serial, AudioLogger::Info); // setup vs1053 auto cfg = vs1053.defaultConfig(); diff --git a/examples/examples-vs1053/streams-vs1053-serial/streams-vs1053-serial.ino b/examples/examples-vs1053/streams-vs1053-serial/streams-vs1053-serial.ino index fef07e606b..551d9a1542 100644 --- a/examples/examples-vs1053/streams-vs1053-serial/streams-vs1053-serial.ino +++ b/examples/examples-vs1053/streams-vs1053-serial/streams-vs1053-serial.ino @@ -9,25 +9,26 @@ #include "AudioTools.h" -#include "AudioTools/AudioLibs/VS1053Stream.h" +#include "AudioLibs/VS1053Stream.h" -AudioInfo info(1600, 1, 16); +int channels = 1; VS1053Stream in; // Access VS1053/VS1003 as stream -CsvOutput csvOutput(Serial); -StreamCopy copier(csvOutput, in); // copy in to csvOutput +CsvOutput csvStream(Serial, channels); +StreamCopy copier(csvStream, in); // copy in to csvStream // Arduino Setup void setup(void) { Serial.begin(115200); - AudioToolsLogger.begin(Serial, AudioToolsLogLevel::Warning); + AudioLogger::instance().begin(Serial, AudioLogger::Warning); auto cfg = in.defaultConfig(RX_MODE); - cfg.copyFrom(info); + cfg.sample_rate = 16000; + cfg.channels = channels; cfg.input_device = VS1053_MIC; // or VS1053_AUX in.begin(cfg); // make sure that we have the correct channels set up - csvOutput.begin(info); + csvStream.begin(); } diff --git a/examples/examples-communication/http-server/README.md b/examples/examples-webserver/README.md similarity index 100% rename from examples/examples-communication/http-server/README.md rename to examples/examples-webserver/README.md diff --git a/examples/examples-communication/http-server/player-sd-webserverex_mp3/README.md b/examples/examples-webserver/player-sd-webserverex_mp3/README.md similarity index 100% rename from examples/examples-communication/http-server/player-sd-webserverex_mp3/README.md rename to examples/examples-webserver/player-sd-webserverex_mp3/README.md diff --git a/examples/examples-communication/http-server/player-sd-webserverex_mp3/player-sd-webserverex_mp3.ino b/examples/examples-webserver/player-sd-webserverex_mp3/player-sd-webserverex_mp3.ino similarity index 85% rename from examples/examples-communication/http-server/player-sd-webserverex_mp3/player-sd-webserverex_mp3.ino rename to examples/examples-webserver/player-sd-webserverex_mp3/player-sd-webserverex_mp3.ino index eebf181aca..4ecd1d4c6d 100644 --- a/examples/examples-communication/http-server/player-sd-webserverex_mp3/player-sd-webserverex_mp3.ino +++ b/examples/examples-webserver/player-sd-webserverex_mp3/player-sd-webserverex_mp3.ino @@ -8,9 +8,9 @@ #include "AudioTools.h" -#include "AudioTools/Disk/AudioSourceSD.h" -#include "AudioTools/AudioLibs/AudioServerEx.h" -#include "AudioTools/AudioCodecs/CodecCopy.h" +#include "AudioLibs/AudioSourceSD.h" +#include "AudioLibs/AudioServerEx.h" +#include "AudioCodecs/CodecCopy.h" #define PIN_AUDIO_KIT_SD_CARD_CS 13 #define PIN_AUDIO_KIT_SD_CARD_MISO 2 @@ -28,7 +28,7 @@ AudioPlayer player(source, out, *new CopyDecoder()); void setup() { Serial.begin(115200); - AudioToolsLogger.begin(Serial, AudioToolsLogLevel::Warning); + AudioLogger::instance().begin(Serial, AudioLogger::Warning); HttpLogger.setLevel(tinyhttp::Warning); // setup SPI for SD card diff --git a/examples/examples-communication/http-server/streams-effect-webserver_wav/streams-effect-webserver_wav.ino b/examples/examples-webserver/streams-effect-webserver_wav/streams-effect-webserver_wav.ino similarity index 100% rename from examples/examples-communication/http-server/streams-effect-webserver_wav/streams-effect-webserver_wav.ino rename to examples/examples-webserver/streams-effect-webserver_wav/streams-effect-webserver_wav.ino diff --git a/examples/examples-communication/http-server/streams-flite-webserver_wav/README.md b/examples/examples-webserver/streams-flite-webserver_wav/README.md similarity index 100% rename from examples/examples-communication/http-server/streams-flite-webserver_wav/README.md rename to examples/examples-webserver/streams-flite-webserver_wav/README.md diff --git a/examples/examples-communication/http-server/streams-flite-webserver_wav/streams-flite-webserver_wav.ino b/examples/examples-webserver/streams-flite-webserver_wav/streams-flite-webserver_wav.ino similarity index 100% rename from examples/examples-communication/http-server/streams-flite-webserver_wav/streams-flite-webserver_wav.ino rename to examples/examples-webserver/streams-flite-webserver_wav/streams-flite-webserver_wav.ino diff --git a/examples/examples-communication/http-server/streams-generator-webserver_aac/streams-generator-webserver_aac.ino b/examples/examples-webserver/streams-generator-webserver_aac/streams-generator-webserver_aac.ino similarity index 74% rename from examples/examples-communication/http-server/streams-generator-webserver_aac/streams-generator-webserver_aac.ino rename to examples/examples-webserver/streams-generator-webserver_aac/streams-generator-webserver_aac.ino index 730acf2324..14b9ebfaf9 100644 --- a/examples/examples-communication/http-server/streams-generator-webserver_aac/streams-generator-webserver_aac.ino +++ b/examples/examples-webserver/streams-generator-webserver_aac/streams-generator-webserver_aac.ino @@ -2,14 +2,14 @@ * @file streams-generator-server_aac.ino * * This sketch generates a test sine wave. The result is provided as AAC stream which can be listened to in a Web Browser + * * @author Phil Schatzmann * @copyright GPLv3 * */ #include "AudioTools.h" -#include "AudioTools/AudioCodecs/CodecAACFDK.h" - +#include "AudioCodecs/CodecAACFDK.h" // WIFI const char *ssid = "ssid"; @@ -25,11 +25,6 @@ void setup() { Serial.begin(115200); AudioLogger::instance().begin(Serial,AudioLogger::Info); - // configure FDK to use less RAM (not necessary if you activate PSRAM) - fdk.setAudioObjectType(2); // AAC low complexity - fdk.setOutputBufferSize(1024); // decrease output buffer size - fdk.setVariableBitrateMode(2); // low variable bitrate - // start server server.begin(in, info); diff --git a/examples/examples-communication/http-server/streams-generator-webserver_mp3/streams-generator-webserver_mp3.ino b/examples/examples-webserver/streams-generator-webserver_mp3/streams-generator-webserver_mp3.ino similarity index 87% rename from examples/examples-communication/http-server/streams-generator-webserver_mp3/streams-generator-webserver_mp3.ino rename to examples/examples-webserver/streams-generator-webserver_mp3/streams-generator-webserver_mp3.ino index 6ec100a8d6..27a5536bc3 100644 --- a/examples/examples-communication/http-server/streams-generator-webserver_mp3/streams-generator-webserver_mp3.ino +++ b/examples/examples-webserver/streams-generator-webserver_mp3/streams-generator-webserver_mp3.ino @@ -1,22 +1,21 @@ /** - * @file streams-generator-webserver_mp3.ino + * @file streams-generator-server_ogg.ino * * This sketch generates a test sine wave. The result is provided as mp3 stream which can be listened to in a Web Browser * Please note that MP3EncoderLAME needs a processor with PSRAM ! - * * @author Phil Schatzmann * @copyright GPLv3 * */ #include "AudioTools.h" -#include "AudioTools/AudioCodecs/CodecMP3LAME.h" +#include "AudioCodecs/CodecMP3LAME.h" // WIFI const char *ssid = "ssid"; const char *password = "password"; -AudioInfo info(24000, 1, 16); +AudioInfo info(16000,1,16); MP3EncoderLAME mp3; AudioEncoderServer server(&mp3, ssid, password); SineWaveGenerator sineWave; // Subclass of SoundGenerator with max amplitude of 32000 diff --git a/examples/examples-communication/http-server/streams-generator-webserver_ogg/streams-generator-webserver_ogg.ino b/examples/examples-webserver/streams-generator-webserver_ogg/streams-generator-webserver_ogg.ino similarity index 89% rename from examples/examples-communication/http-server/streams-generator-webserver_ogg/streams-generator-webserver_ogg.ino rename to examples/examples-webserver/streams-generator-webserver_ogg/streams-generator-webserver_ogg.ino index 1ef880b9bf..ddbeb362e2 100644 --- a/examples/examples-communication/http-server/streams-generator-webserver_ogg/streams-generator-webserver_ogg.ino +++ b/examples/examples-webserver/streams-generator-webserver_ogg/streams-generator-webserver_ogg.ino @@ -15,7 +15,7 @@ */ #include "AudioTools.h" -#include "AudioTools/AudioCodecs/CodecOpusOgg.h" +#include "AudioCodecs/CodecOpusOgg.h" // WIFI const char *ssid = "ssid"; @@ -29,7 +29,7 @@ GeneratedSoundStream in(sineWave); // Stream generated from sine wave void setup() { Serial.begin(115200); - AudioToolsLogger.begin(Serial, AudioToolsLogLevel::Info); + AudioLogger::instance().begin(Serial, AudioLogger::Info); // start server server.begin(in, info); diff --git a/examples/examples-communication/http-server/streams-generator-webserver_wav/README.md b/examples/examples-webserver/streams-generator-webserver_wav/README.md similarity index 100% rename from examples/examples-communication/http-server/streams-generator-webserver_wav/README.md rename to examples/examples-webserver/streams-generator-webserver_wav/README.md diff --git a/examples/examples-communication/http-server/streams-generator-webserver_wav/streams-generator-webserver_wav.ino b/examples/examples-webserver/streams-generator-webserver_wav/streams-generator-webserver_wav.ino similarity index 100% rename from examples/examples-communication/http-server/streams-generator-webserver_wav/streams-generator-webserver_wav.ino rename to examples/examples-webserver/streams-generator-webserver_wav/streams-generator-webserver_wav.ino diff --git a/examples/examples-communication/http-server/streams-generator-webserverex_wav/README.md b/examples/examples-webserver/streams-generator-webserverex_wav/README.md similarity index 100% rename from examples/examples-communication/http-server/streams-generator-webserverex_wav/README.md rename to examples/examples-webserver/streams-generator-webserverex_wav/README.md diff --git a/examples/examples-communication/http-server/streams-generator-webserverex_wav/streams-generator-webserverex_wav.ino b/examples/examples-webserver/streams-generator-webserverex_wav/streams-generator-webserverex_wav.ino similarity index 84% rename from examples/examples-communication/http-server/streams-generator-webserverex_wav/streams-generator-webserverex_wav.ino rename to examples/examples-webserver/streams-generator-webserverex_wav/streams-generator-webserverex_wav.ino index a2dafe1c4f..0aab023268 100644 --- a/examples/examples-communication/http-server/streams-generator-webserverex_wav/streams-generator-webserverex_wav.ino +++ b/examples/examples-webserver/streams-generator-webserverex_wav/streams-generator-webserverex_wav.ino @@ -9,13 +9,14 @@ */ #include "AudioTools.h" -#include "AudioTools/AudioLibs/AudioServerEx.h" +#include "AudioLibs/AudioServerEx.h" // WIFI const char *ssid = "SSID"; const char *password = "password"; +const int sample_rate = 10000; +const int channels = 1; -AudioInfo info(10000, 1, 16); SineWaveGenerator sineWave; // Subclass of SoundGenerator with max amplitude of 32000 GeneratedSoundStream in(sineWave); // Stream generated from sine wave AudioWAVServerEx server; @@ -32,13 +33,14 @@ void setup() { // start server auto cfg = server.defaultConfig(); - cfg.copyFrom(info); + cfg.sample_rate = sample_rate; + cfg.channels = channels; cfg.ssid = ssid; cfg.password = password; server.begin(cfg); // start generation of sound - sineWave.begin(info, N_B4); + sineWave.begin(channels, sample_rate, N_B4); in.begin(); } diff --git a/examples/examples-communication/http-server/streams-generator-webserverex_wav1/README.md b/examples/examples-webserver/streams-generator-webserverex_wav1/README.md similarity index 100% rename from examples/examples-communication/http-server/streams-generator-webserverex_wav1/README.md rename to examples/examples-webserver/streams-generator-webserverex_wav1/README.md diff --git a/examples/examples-communication/http-server/streams-generator-webserverex_wav1/streams-generator-webserverex_wav1.ino b/examples/examples-webserver/streams-generator-webserverex_wav1/streams-generator-webserverex_wav1.ino similarity index 82% rename from examples/examples-communication/http-server/streams-generator-webserverex_wav1/streams-generator-webserverex_wav1.ino rename to examples/examples-webserver/streams-generator-webserverex_wav1/streams-generator-webserverex_wav1.ino index a9c374db5c..d0c1d471df 100644 --- a/examples/examples-communication/http-server/streams-generator-webserverex_wav1/streams-generator-webserverex_wav1.ino +++ b/examples/examples-webserver/streams-generator-webserverex_wav1/streams-generator-webserverex_wav1.ino @@ -9,13 +9,14 @@ */ #include "AudioTools.h" -#include "AudioTools/AudioLibs/AudioServerEx.h" +#include "AudioLibs/AudioServerEx.h" // WIFI const char *ssid = "SSID"; const char *password = "password"; +const int sample_rate = 10000; +const int channels = 1; -AudioInfo info(10000, 1, 16); SineWaveGenerator sineWave; // Subclass of SoundGenerator with max amplitude of 32000 GeneratedSoundStream in(sineWave); // Stream generated from sine wave AudioWAVServerEx server; @@ -28,14 +29,15 @@ void setup() { // start server auto cfg = server.defaultConfig(); - cfg.copyFrom(info); + cfg.sample_rate = sample_rate; + cfg.channels = channels; cfg.ssid = ssid; cfg.password = password; cfg.input = ∈ // Define input server.begin(cfg); // start generation of sound - sineWave.begin(info, N_B4); + sineWave.begin(channels, sample_rate, N_B4); in.begin(); } diff --git a/examples/examples-communication/http-server/streams-i2s-webserver_wav/README.md b/examples/examples-webserver/streams-i2s-webserver_wav/README.md similarity index 100% rename from examples/examples-communication/http-server/streams-i2s-webserver_wav/README.md rename to examples/examples-webserver/streams-i2s-webserver_wav/README.md diff --git a/examples/examples-communication/http-server/streams-i2s-webserver_wav/streams-i2s-webserver_wav.ino b/examples/examples-webserver/streams-i2s-webserver_wav/streams-i2s-webserver_wav.ino similarity index 95% rename from examples/examples-communication/http-server/streams-i2s-webserver_wav/streams-i2s-webserver_wav.ino rename to examples/examples-webserver/streams-i2s-webserver_wav/streams-i2s-webserver_wav.ino index 7e96b79117..4428d8343a 100644 --- a/examples/examples-communication/http-server/streams-i2s-webserver_wav/streams-i2s-webserver_wav.ino +++ b/examples/examples-webserver/streams-i2s-webserver_wav/streams-i2s-webserver_wav.ino @@ -17,7 +17,7 @@ ConverterFillLeftAndRight filler(LeftIsEmpty); // fill both channels - void setup(){ Serial.begin(115200); - AudioToolsLogger.begin(Serial, AudioToolsLogLevel::Info); + AudioLogger::instance().begin(Serial, AudioLogger::Info); // start i2s input with default configuration Serial.println("starting I2S..."); diff --git a/examples/examples-communication/http-server/streams-sam-webserver_wav/README.md b/examples/examples-webserver/streams-sam-webserver_wav/README.md similarity index 100% rename from examples/examples-communication/http-server/streams-sam-webserver_wav/README.md rename to examples/examples-webserver/streams-sam-webserver_wav/README.md diff --git a/examples/examples-communication/http-server/streams-sam-webserver_wav/streams-sam-webserver_wav.ino b/examples/examples-webserver/streams-sam-webserver_wav/streams-sam-webserver_wav.ino similarity index 100% rename from examples/examples-communication/http-server/streams-sam-webserver_wav/streams-sam-webserver_wav.ino rename to examples/examples-webserver/streams-sam-webserver_wav/streams-sam-webserver_wav.ino diff --git a/examples/examples-communication/http-server/streams-tts-webserver_wav/README.md b/examples/examples-webserver/streams-tts-webserver_wav/README.md similarity index 100% rename from examples/examples-communication/http-server/streams-tts-webserver_wav/README.md rename to examples/examples-webserver/streams-tts-webserver_wav/README.md diff --git a/examples/examples-communication/http-server/streams-tts-webserver_wav/streams-tts-webserver_wav.ino b/examples/examples-webserver/streams-tts-webserver_wav/streams-tts-webserver_wav.ino similarity index 91% rename from examples/examples-communication/http-server/streams-tts-webserver_wav/streams-tts-webserver_wav.ino rename to examples/examples-webserver/streams-tts-webserver_wav/streams-tts-webserver_wav.ino index 24ce8af2d0..3a8163cf4a 100644 --- a/examples/examples-communication/http-server/streams-tts-webserver_wav/streams-tts-webserver_wav.ino +++ b/examples/examples-webserver/streams-tts-webserver_wav/streams-tts-webserver_wav.ino @@ -20,7 +20,7 @@ void outputData(Print *out){ void setup(){ Serial.begin(115200); - AudioToolsLogger.begin(Serial, AudioToolsLogLevel::Info); + AudioLogger::instance().begin(Serial, AudioLogger::Info); // start data sink TTSInfo info = TTS::getInfo(); server.begin(outputData, info.sample_rate, info.channels, info.bits_per_sample); diff --git a/examples/sandbox/basic-a2dp-vs1053/basic-a2dp-vs1053.ino b/examples/sandbox/basic-a2dp-vs1053/basic-a2dp-vs1053.ino index 189baa84b2..dca7d747e2 100644 --- a/examples/sandbox/basic-a2dp-vs1053/basic-a2dp-vs1053.ino +++ b/examples/sandbox/basic-a2dp-vs1053/basic-a2dp-vs1053.ino @@ -10,37 +10,42 @@ // install https://github.com/pschatzmann/arduino-vs1053.git #include "AudioTools.h" -#include "AudioTools/AudioLibs/VS1053Stream.h" +#include "AudioLibs/VS1053Stream.h" #include "BluetoothA2DPSink.h" BluetoothA2DPSink a2dp_sink; -VS1053Stream out; // output +VS1053Stream out; // final output VS1053Config cfg; +QueueStream buffer(1024,10); +StreamCopy copier(out, buffer); // copies sound into i2s -// Write data to VS1053 -void read_data_stream(const uint8_t *data, uint32_t bytes) { - int samples = bytes / sizeof(int16_t); - // split up writes to max 512 samples - writeData(&out, (int16_t*) data, samples, 512); +// Write data from A2DP to buffer +void read_data_stream(const uint8_t *data, uint32_t frames) { + int frameSize = 4; + buffer.write(data, frames*frameSize); } // for esp_a2d_audio_state_t see https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-reference/bluetooth/esp_a2dp.html#_CPPv421esp_a2d_audio_state_t void audio_state_changed(esp_a2d_audio_state_t state, void *ptr){ - Serial.println(a2dp_sink.to_str(state)); + Serial.println(a2dp_sink.to_str(state)); switch(state){ + case ESP_A2D_AUDIO_STATE_STARTED: + buffer.begin(); + out.begin(cfg); + break; case ESP_A2D_AUDIO_STATE_STOPPED: case ESP_A2D_AUDIO_STATE_REMOTE_SUSPEND: - // add new wav header out.end(); - out.begin(cfg); + buffer.end(); + buffer.clear(); break; } } void setup() { Serial.begin(115200); - AudioToolsLogger.begin(Serial, AudioToolsLogLevel::Info); + AudioLogger::instance().begin(Serial, AudioLogger::Info); // setup VS1053 cfg = out.defaultConfig(); @@ -51,9 +56,8 @@ void setup() { //cfg.cs_pin = VS1053_CS; //cfg.dcs_pin = VS1053_DCS; //cfg.dreq_pin = VS1053_DREQ; - // cfg.reset_pin = //VS1053_RESET; - out.begin(cfg); - + //cfg.reset_pin = VS1053_RESET; + // register callbacks a2dp_sink.set_stream_reader(read_data_stream, false); a2dp_sink.set_on_audio_state_changed(audio_state_changed); @@ -61,7 +65,9 @@ void setup() { a2dp_sink.set_auto_reconnect(false); a2dp_sink.start("a2dp-vs1053"); + } void loop() { + if (buffer) copier.copy(); } \ No newline at end of file diff --git a/examples/sandbox/ble/client-as-source/communication-ble-client-send/communication-ble-client-send.ino b/examples/sandbox/ble/client-as-source/communication-ble-client-send/communication-ble-client-send.ino deleted file mode 100644 index cc8bfee995..0000000000 --- a/examples/sandbox/ble/client-as-source/communication-ble-client-send/communication-ble-client-send.ino +++ /dev/null @@ -1,38 +0,0 @@ -/** - * @file communications-ble-client-send.ino - * @author Phil Schatzmann - * @brief Sending audio via BLE: the client acts as audio source - * Please not that the thruput in this scenario is very limited! - - * @version 0.1 - * @date 2022-11-04 - * - * @copyright Copyright (c) 2022 - */ - -#include "AudioTools.h" -#include "AudioTools/AudioCodecs/CodecADPCM.h" // https://github.com/pschatzmann/adpcm -#include "AudioTools/Sandbox/BLE/AudioBLE.h" - -AudioInfo info(16000, 1, 16); -SineWaveGenerator sineWave(32000); -GeneratedSoundStream sound(sineWave); -Throttle throttle(sound); -AudioBLEClient ble; -ADPCMEncoder adpcm(AV_CODEC_ID_ADPCM_IMA_WAV); -EncodedAudioStream encoder(&ble, &adpcm); -StreamCopy copier(encoder, throttle); - -void setup() { - Serial.begin(115200); - AudioToolsLogger.begin(Serial, AudioToolsLogLevel::Info); - - sineWave.begin(info, N_B4); - throttle.begin(info); - encoder.begin(info); - ble.begin("ble-send", 60 * 10); - - Serial.println("started..."); -} - -void loop() { copier.copy(); } \ No newline at end of file diff --git a/examples/sandbox/ble/client-as-source/communication-ble-server-receive/communication-ble-server-receive.ino b/examples/sandbox/ble/client-as-source/communication-ble-server-receive/communication-ble-server-receive.ino deleted file mode 100644 index 6149ff4612..0000000000 --- a/examples/sandbox/ble/client-as-source/communication-ble-server-receive/communication-ble-server-receive.ino +++ /dev/null @@ -1,39 +0,0 @@ -/** - * @file communications-ble-server-receive.ino - * @author Phil Schatzmann - * @brief Receiving audio via BLE: the server acts as audio sink - * Please not that the thruput in this scenario is very limited! - * @version 0.1 - * @date 2022-11-04 - * - * @copyright Copyright (c) 2022 - */ - -#include "AudioTools.h" -#include "AudioTools/AudioCodecs/CodecADPCM.h" // https://github.com/pschatzmann/adpcm -#include "AudioTools/Sandbox/BLE/AudioBLE.h" -//#include "AudioTools/AudioLibs/AudioBoardStream.h" - -AudioInfo info(16000, 1, 16); -ADPCMDecoder adpcm(AV_CODEC_ID_ADPCM_IMA_WAV); -I2SStream i2s; // or AudioBoardStream ... -EncodedAudioStream decoder(&i2s, &adpcm); -AudioBLEServer ble; -StreamCopy copier(decoder, ble); - -void setup() { - Serial.begin(115200); - AudioToolsLogger.begin(Serial, AudioToolsLogLevel::Warning); - - decoder.begin(info); - - auto cfg = i2s.defaultConfig(); - cfg.copyFrom(info); - i2s.begin(cfg); - - ble.begin("ble-receive"); -} - -void loop() { - copier.copy(); -} \ No newline at end of file diff --git a/examples/sandbox/ble/server-as-source/communication-ble-client-receive/communication-ble-client-receive.ino b/examples/sandbox/ble/server-as-source/communication-ble-client-receive/communication-ble-client-receive.ino deleted file mode 100644 index 8152fc3b88..0000000000 --- a/examples/sandbox/ble/server-as-source/communication-ble-client-receive/communication-ble-client-receive.ino +++ /dev/null @@ -1,46 +0,0 @@ -/** - * @file communications-ble-client-receive.ino - * @author Phil Schatzmann - * @brief Receiving audio via BLE: The client acts as audio sink and is writing to I2S. - * This scenario works amazingly well! - * @version 0.1 - * @date 2022-11-04 - * - * @copyright Copyright (c) 2023 - */ - - -#include "AudioTools.h" -//#include "AudioTools/AudioLibs/AudioBoardStream.h" -#include "AudioTools/AudioCodecs/CodecADPCM.h" // https://github.com/pschatzmann/adpcm -#include "AudioTools/Sandbox/BLE/AudioBLE.h" - -AudioInfo info(44100, 2, 16); -AudioBLEClient ble; -I2SStream i2s; -ADPCMDecoder adpcm(AV_CODEC_ID_ADPCM_IMA_WAV); -EncodedAudioStream decoder(&i2s, &adpcm); -StreamCopy copier(decoder, ble); - -void setup() { - Serial.begin(115200); - AudioToolsLogger.begin(Serial, AudioToolsLogLevel::Warning); - - // start BLE client - wait at most 10 minutes - ble.begin("ble-receive", 60*10); - - // start decoder - decoder.begin(info); - - // start I2S - auto config = i2s.defaultConfig(TX_MODE); - config.copyFrom(info); - i2s.begin(config); - - Serial.println("started..."); -} - -void loop() { - if (ble) - copier.copy(); -} diff --git a/examples/sandbox/ble/server-as-source/communication-ble-server-send/communication-ble-server-send.ino b/examples/sandbox/ble/server-as-source/communication-ble-server-send/communication-ble-server-send.ino deleted file mode 100644 index 7f9fa4be30..0000000000 --- a/examples/sandbox/ble/server-as-source/communication-ble-server-send/communication-ble-server-send.ino +++ /dev/null @@ -1,41 +0,0 @@ -/** - * @file communications-ble-server-send.ino - * @author Phil Schatzmann - * @brief Sending audio via BLE: The server acts as audio source. - * This scenario works amazingly well! - * @version 0.1 - * @date 2022-11-04 - * - * @copyright Copyright (c) 2023 - */ - -#include "AudioTools.h" -#include "AudioTools/AudioCodecs/CodecADPCM.h" // https://github.com/pschatzmann/adpcm -#include "AudioTools/Sandbox/BLE/AudioBLE.h" - -AudioInfo info(44100, 2, 16); -SineWaveGenerator - sineWave(32000); // subclass of SoundGenerator with max amplitude of 32000 -GeneratedSoundStream - sound(sineWave); // Stream generated from sine wave -AudioBLEServer ble; -ADPCMEncoder adpcm(AV_CODEC_ID_ADPCM_IMA_WAV); -EncodedAudioStream encoder(&ble, &adpcm); -StreamCopy copier(encoder, sound); - -void setup() { - Serial.begin(115200); - AudioToolsLogger.begin(Serial, AudioToolsLogLevel::Info); - - sineWave.begin(info, N_B4); - - encoder.begin(info); - - // ble.setAudioInfoActive(true); - ble.begin("ble-send"); -} - -void loop() { - if (ble.availableForWrite() > 0) - copier.copy(); -} \ No newline at end of file diff --git a/examples/sandbox/ggwave/receive/receive.ino b/examples/sandbox/ggwave/receive/receive.ino index b3b419f4e6..94313600f8 100644 --- a/examples/sandbox/ggwave/receive/receive.ino +++ b/examples/sandbox/ggwave/receive/receive.ino @@ -12,11 +12,11 @@ #include "AudioTools.h" #include "Experiments/CodecGGWave.h" // https://github.com/ggerganov/ggwave-arduinop -#include "AudioTools/AudioLibs/AudioBoardStream.h" // https://github.com/pschatzmann/arduino-audio-driver.git +#include "AudioLibs/AudioKit.h" // https://github.com/pschatzmann/arduino-audiokit.git int sample_rate = GGWAVE_DEFAULT_SAMPLE_RATE; int channels = 1; -AudioBoardStream in(AudioKitEs8388V1); // or e.g. I2SStream +AudioKitStream in; // or AudioKitStream GGWaveDecoder dec; EncodedAudioStream encoder_stream(Serial, dec); // decode and write to I2S - ESP Now is limited to 256 bytes StreamCopy copier(encoder_stream, in, GGWAVE_DEFAULT_BYTES_PER_FRAME); // copy mic to tfl @@ -24,13 +24,13 @@ StreamCopy copier(encoder_stream, in, GGWAVE_DEFAULT_BYTES_PER_FRAME); // copy void setup() { Serial.begin(115200); - AudioToolsLogger.begin(Serial, AudioToolsLogLevel::Info); + AudioLogger::instance().begin(Serial, AudioLogger::Info); // audio input auto config = in.defaultConfig(RX_MODE); config.sample_rate = sample_rate; config.channels = channels; - config.input_device = ADC_INPUT_LINE2; // microphone + config.input_device = AUDIO_HAL_ADC_INPUT_LINE2; // microphone in.begin(config); // setup codec diff --git a/examples/sandbox/ggwave/send/send.ino b/examples/sandbox/ggwave/send/send.ino index f4a63ee378..087923fcfd 100644 --- a/examples/sandbox/ggwave/send/send.ino +++ b/examples/sandbox/ggwave/send/send.ino @@ -12,18 +12,18 @@ #include "AudioTools.h" #include "Experiments/CodecGGWave.h" // https://github.com/ggerganov/ggwave-arduinop -#include "AudioTools/AudioLibs/AudioBoardStream.h" // https://github.com/pschatzmann/arduino-audio-driver.git +#include "AudioLibs/AudioKit.h" // https://github.com/pschatzmann/arduino-audiokit.git int sample_rate = GGWAVE_DEFAULT_SAMPLE_RATE; int channels = 1; -AudioBoardStream out(AudioKitEs8388V1); // or AudioBoardStream +AudioKitStream out; // or AudioKitStream GGWaveEncoder enc; EncodedAudioStream encoder_stream(&out, &enc); // decode and write to I2S - ESP Now is limited to 256 bytes void setup() { Serial.begin(115200); - AudioToolsLogger.begin(Serial, AudioToolsLogLevel::Info); + AudioLogger::instance().begin(Serial, AudioLogger::Info); // audio output auto config = out.defaultConfig(TX_MODE); diff --git a/examples/sandbox/send-hdlc-receive/send-hdlc-receive.ino b/examples/sandbox/send-hdlc-receive/send-hdlc-receive.ino deleted file mode 100644 index d0ab8aa285..0000000000 --- a/examples/sandbox/send-hdlc-receive/send-hdlc-receive.ino +++ /dev/null @@ -1,60 +0,0 @@ -/** - * @file send-hdlc-receive.ino - * @author Phil Schatzmann - * @brief Sending and receiving audio via Serial. You need to connect the RX pin - * with the TX pin! - * The sine wave generator is providing the data as fast as possible, therefore we - * throttle on the sending side to prevent that the receiver is getting the data - * too fast. - * - * To make sure that we transmit only valid data we use HDLC. - * - * @version 0.1 - * @date 2022-03-09 - * - * @copyright Copyright (c) 2022 - */ - -#include "AudioTools.h" -#include "AudioTools/Communication/HDLCStream.h" -// #include "AudioTools/AudioLibs/AudioBoardStream.h" - -AudioInfo info(8000, 1, 16); -SineWaveGenerator sineWave(32000); -GeneratedSoundStream sound(sineWave); -Throttle throttle(sound); -I2SStream out; -HDLCStream hdlc_enc(Serial, 256); -HDLCStream hdlc_dec(Serial, 256); -StreamCopy copierOut(hdlc_enc, throttle, 256); -StreamCopy copierIn(out, hdlc_dec, 256); - -void setup() { - Serial2.begin(115200); - AudioLogger::instance().begin(Serial2, AudioLogger::Warning); - hdlc_enc.begin(); - hdlc_dec.begin(); - - // Note the format for setting a serial port is as follows: - // Serial.begin(baud-rate, protocol, RX pin, TX pin); - Serial.begin(1000000, SERIAL_8N1); - - // Setup sine wave - sineWave.begin(info, N_B4); - throttle.begin(info); - - // start I2S - Serial.println("starting I2S..."); - auto config = out.defaultConfig(TX_MODE); - config.copyFrom(info); - out.begin(config); - - Serial.println("started..."); -} - -void loop() { - // copy to serial - copierOut.copy(); - // copy from serial - copierIn.copy(); -} \ No newline at end of file diff --git a/examples/sandbox/spi/spi-master/spi-master.ino b/examples/sandbox/spi/spi-master/spi-master.ino deleted file mode 100644 index 5800f5e28e..0000000000 --- a/examples/sandbox/spi/spi-master/spi-master.ino +++ /dev/null @@ -1,34 +0,0 @@ - -/** - * We use the default Arduino SPI API to send/write the data as SPI Master. - * For a sample rate of 44100 with 2 channels and 16 bit data you need to be - * able to transmit faster then 44100 * 2 channels * 2 bytes = 176400 bytes per - * second. Using a SPI communication this gives 176400 * 8 = - * 1'411'200 bps! - * - * Untested DRAFT implementation! - */ -#include - -#include "AudioTools.h" - -const size_t BUFFER_SIZE = 1024; -const int SPI_CLOCK = 2000000; // 2 MHz -AudioInfo info(44100, 2, 16); // -Vector buffer(BUFFER_SIZE); -SineWaveGenerator sineWave(32000); -GeneratedSoundStream sound(sineWave); - -void setup() { - SPI.begin(); - sineWave.begin(info, N_B4); - SPI.beginTransaction(SPISettings(SPI_CLOCK, MSBFIRST, SPI_MODE0)); -} - -void loop() { - // get data - size_t result = sound.readBytes(&buffer[0], buffer.size()); - // transmit data - SPI.transfer(&buffer[0], result); - //SPI.endTransaction(); -} diff --git a/examples/sandbox/spi/spi-slave-esp32/spi-slave-esp32.ino b/examples/sandbox/spi/spi-slave-esp32/spi-slave-esp32.ino deleted file mode 100644 index 914f5961d3..0000000000 --- a/examples/sandbox/spi/spi-slave-esp32/spi-slave-esp32.ino +++ /dev/null @@ -1,39 +0,0 @@ -/*** - * We use the ESP32DMASPISlave library to implement the SPI slave on the ESP32 - * which receives the data - * - * Untested DRAFT implementation! - */ -#include -#include "AudioTools.h" - -const size_t BUFFER_SIZE = 1024; -uint8_t *dma_rx_buf; -ESP32DMASPI::Slave spi_slave; -AudioInfo info(44100, 2, 16); // -I2SStream out; // or replace with another output - -void setup() { - Serial.begin(115200); - // setup I2S - auto cfg = out.defaultConfig(TX_MODE); - cfg.copyFrom(info); - //cfg.pin_bck = 14; - //cfg.pin_ws = 15; - //cfg.pin_data = 22; - // to use DMA buffer, use these methods to allocate buffer - dma_rx_buf = spi_slave.allocDMABuffer(BUFFER_SIZE); - - spi_slave.setDataMode(SPI_MODE0); // default: SPI_MODE0 - spi_slave.setMaxTransferSize(BUFFER_SIZE); // default: 4092 bytes - - // begin() after setting - spi_slave.begin(); // default: HSPI (please refer README for pin assignments) - -} - -void loop() { - // start and wait to complete one BIG transaction - size_t received_bytes = spi_slave.transfer(nullptr, dma_rx_buf, BUFFER_SIZE); - out.write(dma_rx_buf, received_bytes); -} diff --git a/examples/sandbox/spi/spi-slave-rp2040/spi-slave-rp2040 b/examples/sandbox/spi/spi-slave-rp2040/spi-slave-rp2040 deleted file mode 100644 index 6aaf1ad8c9..0000000000 --- a/examples/sandbox/spi/spi-slave-rp2040/spi-slave-rp2040 +++ /dev/null @@ -1,48 +0,0 @@ - -/*** - * We use the RP2040 SPISlave to implement the SPI slave - * which receives the data - * - * Untested DRAFT implementation! - */ -#include - -AudioInfo info(44100, 2, 16); // -I2SStream out; // or replace with another output - -// Wiring: -// Master RX GP0 <-> GP11 Slave TX -// Master CS GP1 <-> GP9 Slave CS -// Master CK GP2 <-> GP10 Slave CK -// Master TX GP3 <-> GP8 Slave RX - -SPISettings spisettings(2000000, MSBFIRST, SPI_MODE0); - -// Core 1 will be SPI slave -void recvCallback(uint8_t *data, size_t len) { out.write(data, len); } - -void setup() { - Serial.begin(115200); - - // setup I2S - auto cfg = out.defaultConfig(TX_MODE); - cfg.copyFrom(info); - // cfg.pin_bck = 14; - // cfg.pin_ws = 15; - // cfg.pin_data = 22; - out.begin(cfg); - - // setup SPI - SPISlave.setRX(8); - SPISlave.setCS(9); - SPISlave.setSCK(10); - SPISlave.setTX(11); - // Hook our callbacks into the slave - SPISlave.onDataRecv(recvCallback); - SPISlave.begin(spisettings); - - delay(1000); - Serial.println("SPISlave started"); -} - -void loop() {} \ No newline at end of file diff --git a/examples/sandbox/streams-generator-freq/streams-generator-freq.ino b/examples/sandbox/streams-generator-freq/streams-generator-freq.ino deleted file mode 100644 index bf076f6c4a..0000000000 --- a/examples/sandbox/streams-generator-freq/streams-generator-freq.ino +++ /dev/null @@ -1,48 +0,0 @@ -/** - * @file streams-generator-freq.ino - * @brief Test frequency detection - * @author Phil Schatzmann - * @copyright GPLv3 - **/ - -#include "AudioTools.h" -#include "Experiments/FrequencyDetection.h" - -AudioInfo info(16000, 1, 16); -SineWaveGenerator sineWave(32000); // subclass of SoundGenerator with max amplitude of 32000 -GeneratedSoundStream sound(sineWave); // Stream generated from sine wave -FrequncyZeroCrossingStream out; // or use FrequncyAutoCorrelationStream -StreamCopy copier(out, sound); -MusicalNotes notes; // copies sound into i2s -int idx = 0; - -// Arduino Setup -void setup(void) { - // Open Serial - Serial.begin(115200); - AudioToolsLogger.begin(Serial, AudioToolsLogLevel::Warning); - - // start the analog output - out.begin(info); - - // Setup sine wave - Serial.println("started..."); -} - -// Arduino loop - copy sound to out -void loop() { - float freq = notes.frequency(idx); - sineWave.begin(info, freq); - copier.copy(); - - // print result - Serial.print(notes.note(freq)); - Serial.print(" "); - Serial.print(notes.frequency(idx)); - Serial.print("->"); - Serial.print(out.frequency(0)); - Serial.print(" "); - Serial.println(notes.note(out.frequency(0))); - - if (++idx >= notes.frequencyCount()) stop(); -} \ No newline at end of file diff --git a/examples/sandbox/streams-memory_wav-resample-audiokit/g8-saw.h b/examples/sandbox/streams-memory_wav-resample-audiokit/g8-saw.h deleted file mode 100644 index 291b55daac..0000000000 --- a/examples/sandbox/streams-memory_wav-resample-audiokit/g8-saw.h +++ /dev/null @@ -1,14 +0,0 @@ -const unsigned char g8_saw_raw[] = { - 0x00, 0x00, 0xb5, 0x0b, 0x69, 0x17, 0x22, 0x23, 0xd3, 0x2e, 0x8b, 0x3a, - 0x3e, 0x46, 0xf2, 0x51, 0x79, 0xaa, 0x29, 0xb6, 0xe3, 0xc1, 0x93, 0xcd, - 0x4a, 0xd9, 0x02, 0xe5, 0xb3, 0xf0, 0x6b, 0xfc, 0x1f, 0x08, 0xd3, 0x13, - 0x8c, 0x1f, 0x3b, 0x2b, 0xf8, 0x36, 0xa5, 0x42, 0x61, 0x4e, 0xdd, 0xa6, - 0x98, 0xb2, 0x48, 0xbe, 0x00, 0xca, 0xb5, 0xd5, 0x69, 0xe1, 0x1e, 0xed, - 0xd8, 0xf8, 0x84, 0x04, 0x42, 0x10, 0xf1, 0x1b, 0xa8, 0x27, 0x62, 0x33, - 0x0e, 0x3f, 0xcd, 0x4a, 0x78, 0x56, 0x03, 0xaf, 0xb1, 0xba, 0x6c, 0xc6, - 0x1e, 0xd2, 0xd3, 0xdd, 0x89, 0xe9, 0x3e, 0xf5, 0xf4, 0x00, 0xa6, 0x0c, - 0x61, 0x18, 0x0e, 0x24, 0xcd, 0x2f, 0x78, 0x3b, 0x37, 0x47, 0xe2, 0x52, - 0x6e, 0xab, 0x1a, 0xb7, 0xd6, 0xc2, 0x89, 0xce, 0x3c, 0xda, 0xf4, 0xe5, - 0xa7, 0xf1, 0x5d, 0xfd -}; -unsigned int g8_saw_raw_len = 124; diff --git a/examples/sandbox/streams-memory_wav-resample-audiokit/g8-sine-long.h b/examples/sandbox/streams-memory_wav-resample-audiokit/g8-sine-long.h deleted file mode 100644 index faf8809b74..0000000000 --- a/examples/sandbox/streams-memory_wav-resample-audiokit/g8-sine-long.h +++ /dev/null @@ -1,3993 +0,0 @@ -const unsigned char g8_sine_long_raw[] = { - 0x01, 0x00, 0xbf, 0x23, 0x96, 0x41, 0x79, 0x54, 0x65, 0x59, 0x63, 0x4f, - 0x42, 0x38, 0xb9, 0x17, 0x4d, 0xf3, 0xee, 0xd0, 0x72, 0xb6, 0x1e, 0xa8, - 0x72, 0xa8, 0x4a, 0xb7, 0x38, 0xd2, 0xc4, 0xf4, 0x2c, 0x19, 0x66, 0x39, - 0x16, 0x50, 0x76, 0x59, 0xfd, 0x53, 0x8b, 0x40, 0x62, 0x22, 0x84, 0xfe, - 0xde, 0xda, 0x70, 0xbd, 0x03, 0xab, 0xc0, 0xa6, 0x4c, 0xb1, 0xec, 0xc8, - 0xb7, 0xe9, 0x2e, 0x0e, 0x53, 0x30, 0x67, 0x4a, 0x28, 0x58, 0x3c, 0x57, - 0xd4, 0x47, 0x7d, 0x2c, 0xc0, 0x09, 0x68, 0xe5, 0x76, 0xc5, 0x43, 0xaf, - 0x77, 0xa6, 0x8b, 0xac, 0x81, 0xc0, 0xfe, 0xde, 0xfd, 0x02, 0x79, 0x26, - 0x91, 0x43, 0x70, 0x55, 0x1c, 0x59, 0xfb, 0x4d, 0xe4, 0x35, 0xd9, 0x14, - 0x56, 0xf0, 0x71, 0xce, 0xc2, 0xb4, 0x9b, 0xa7, 0x1c, 0xa9, 0x13, 0xb9, - 0xd1, 0xd4, 0xba, 0xf7, 0x07, 0x1c, 0xa5, 0x3b, 0x63, 0x51, 0x94, 0x59, - 0xe5, 0x52, 0x71, 0x3e, 0x9e, 0x1f, 0x83, 0xfb, 0x34, 0xd8, 0x73, 0xbb, - 0x21, 0xaa, 0x0f, 0xa7, 0xc4, 0xb2, 0x4e, 0xcb, 0x9f, 0xec, 0x1b, 0x11, - 0xd3, 0x32, 0x03, 0x4c, 0xa7, 0x58, 0x7f, 0x56, 0x03, 0x46, 0xdf, 0x29, - 0xc9, 0x06, 0x90, 0xe2, 0x3e, 0xc3, 0x03, 0xae, 0x66, 0xa6, 0xae, 0xad, - 0xa4, 0xc2, 0xc9, 0xe1, 0xf8, 0x05, 0x23, 0x29, 0x7f, 0x45, 0x49, 0x56, - 0xc1, 0x58, 0x76, 0x4c, 0x7b, 0x33, 0xee, 0x11, 0x6a, 0xed, 0xfa, 0xcb, - 0x30, 0xb3, 0x29, 0xa7, 0xe5, 0xa9, 0xee, 0xba, 0x75, 0xd7, 0xb4, 0xfa, - 0xd7, 0x1e, 0xd9, 0x3d, 0x94, 0x52, 0x99, 0x59, 0xb7, 0x51, 0x46, 0x3c, - 0xcc, 0x1c, 0x8c, 0xf8, 0x8b, 0xd5, 0x92, 0xb9, 0x54, 0xa9, 0x76, 0xa7, - 0x53, 0xb4, 0xc1, 0xcd, 0x86, 0xef, 0x0e, 0x14, 0x39, 0x35, 0x92, 0x4d, - 0x07, 0x59, 0xad, 0x55, 0x1c, 0x44, 0x36, 0x27, 0xcf, 0x03, 0xc2, 0xdf, - 0x18, 0xc1, 0xd6, 0xac, 0x74, 0xa6, 0xe3, 0xae, 0xdd, 0xc4, 0x9a, 0xe4, - 0xf2, 0x08, 0xc4, 0x2b, 0x56, 0x47, 0x0c, 0x57, 0x4a, 0x58, 0xdf, 0x4a, - 0x01, 0x31, 0x00, 0x0f, 0x82, 0xea, 0x92, 0xc9, 0xb4, 0xb1, 0xd0, 0xa6, - 0xc6, 0xaa, 0xde, 0xbc, 0x23, 0xda, 0xb0, 0xfd, 0x9e, 0x21, 0xfa, 0x3f, - 0xb0, 0x53, 0x84, 0x59, 0x72, 0x50, 0x07, 0x3a, 0xf6, 0x19, 0x94, 0xf5, - 0xf0, 0xd2, 0xc4, 0xb7, 0x9f, 0xa8, 0xf8, 0xa7, 0xf8, 0xb5, 0x3e, 0xd0, - 0x79, 0xf2, 0xf2, 0x16, 0x99, 0x37, 0x05, 0x4f, 0x51, 0x59, 0xc0, 0x54, - 0x26, 0x42, 0x7d, 0x24, 0xd7, 0x00, 0xfd, 0xdc, 0xff, 0xbe, 0xca, 0xab, - 0x91, 0xa6, 0x39, 0xb0, 0x1d, 0xc7, 0x7a, 0xe7, 0xe5, 0x0b, 0x5a, 0x2e, - 0x1a, 0x49, 0xb3, 0x57, 0xbe, 0x57, 0x2e, 0x49, 0x7d, 0x2e, 0x0d, 0x0c, - 0x9d, 0xe7, 0x40, 0xc7, 0x47, 0xb0, 0x95, 0xa6, 0xbe, 0xab, 0xe0, 0xbe, - 0xde, 0xdc, 0xab, 0x00, 0x5d, 0x24, 0x07, 0x42, 0xb8, 0x54, 0x4f, 0x59, - 0x1c, 0x4f, 0xb5, 0x37, 0x19, 0x17, 0x9f, 0xf2, 0x62, 0xd0, 0x0b, 0xb6, - 0x02, 0xa8, 0x93, 0xa8, 0xb0, 0xb7, 0xcd, 0xd2, 0x6c, 0xf5, 0xd2, 0x19, - 0xe6, 0x39, 0x65, 0x50, 0x7e, 0x59, 0xc0, 0x53, 0x15, 0x40, 0xc3, 0x21, - 0xd8, 0xfd, 0x45, 0xda, 0xf9, 0xbc, 0xd1, 0xaa, 0xcf, 0xa6, 0x9e, 0xb1, - 0x75, 0xc9, 0x5a, 0xea, 0xd9, 0x0e, 0xe2, 0x30, 0xc7, 0x4a, 0x46, 0x58, - 0x13, 0x57, 0x6f, 0x47, 0xe6, 0x2b, 0x19, 0x09, 0xc1, 0xe4, 0xf8, 0xc4, - 0xf7, 0xae, 0x71, 0xa6, 0xcc, 0xac, 0xf8, 0xc0, 0x9f, 0xdf, 0xa8, 0x03, - 0x12, 0x27, 0x02, 0x44, 0xa2, 0x55, 0x0b, 0x59, 0xa5, 0x4d, 0x5c, 0x35, - 0x2f, 0x14, 0xb2, 0xef, 0xde, 0xcd, 0x69, 0xb4, 0x7e, 0xa7, 0x46, 0xa9, - 0x7f, 0xb9, 0x63, 0xd5, 0x68, 0xf8, 0xa5, 0x1c, 0x2b, 0x3c, 0xa4, 0x51, - 0x9b, 0x59, 0xa1, 0x52, 0xf8, 0x3d, 0xfa, 0x1e, 0xdd, 0xfa, 0x96, 0xd7, - 0x09, 0xbb, 0xef, 0xa9, 0x25, 0xa7, 0x1a, 0xb3, 0xdc, 0xcb, 0x40, 0xed, - 0xcc, 0x11, 0x56, 0x33, 0x66, 0x4c, 0xb9, 0x58, 0x55, 0x56, 0x97, 0x45, - 0x46, 0x29, 0x20, 0x06, 0xed, 0xe1, 0xc3, 0xc2, 0xbb, 0xad, 0x69, 0xa6, - 0xf0, 0xad, 0x22, 0xc3, 0x6b, 0xe2, 0xa1, 0x06, 0xbe, 0x29, 0xe9, 0x45, - 0x76, 0x56, 0xab, 0x58, 0x19, 0x4c, 0xf2, 0x32, 0x44, 0x11, 0xc4, 0xec, - 0x6e, 0xcb, 0xd9, 0xb2, 0x12, 0xa7, 0x16, 0xaa, 0x5c, 0xbb, 0x0d, 0xd8, - 0x5f, 0xfb, 0x77, 0x1f, 0x55, 0x3e, 0xd7, 0x52, 0x94, 0x59, 0x73, 0x51, - 0xc3, 0x3b, 0x2e, 0x1c, 0xdf, 0xf7, 0xf4, 0xd4, 0x2b, 0xb9, 0x27, 0xa9, - 0x93, 0xa7, 0xaf, 0xb4, 0x4c, 0xce, 0x34, 0xf0, 0xaf, 0x14, 0xc6, 0x35, - 0xe7, 0x4d, 0x17, 0x59, 0x7e, 0x55, 0xaa, 0x43, 0x9c, 0x26, 0x23, 0x03, - 0x25, 0xdf, 0x9a, 0xc0, 0x9d, 0xac, 0x73, 0xa6, 0x33, 0xaf, 0x56, 0xc5, - 0x45, 0xe5, 0x96, 0x09, 0x5e, 0x2c, 0xbb, 0x47, 0x34, 0x57, 0x2c, 0x58, - 0x82, 0x4a, 0x6e, 0x30, 0x5d, 0x0e, 0xd5, 0xe9, 0x11, 0xc9, 0x5b, 0xb1, - 0xc5, 0xa6, 0xf8, 0xaa, 0x53, 0xbd, 0xbc, 0xda, 0x5c, 0xfe, 0x3c, 0x22, - 0x72, 0x40, 0xed, 0x53, 0x7b, 0x59, 0x25, 0x50, 0x87, 0x39, 0x4e, 0x19, - 0xee, 0xf4, 0x59, 0xd2, 0x62, 0xb7, 0x78, 0xa8, 0x19, 0xa8, 0x58, 0xb6, - 0xd1, 0xd0, 0x22, 0xf3, 0x96, 0x17, 0x1f, 0x38, 0x57, 0x4f, 0x5b, 0x59, - 0x8d, 0x54, 0xab, 0x41, 0xe6, 0x23, 0x28, 0x00, 0x63, 0xdc, 0x87, 0xbe, - 0x92, 0xab, 0x9a, 0xa6, 0x89, 0xb0, 0xa1, 0xc7, 0x20, 0xe8, 0x8d, 0x0c, - 0xef, 0x2e, 0x79, 0x49, 0xd8, 0x57, 0x9a, 0x57, 0xca, 0x48, 0xec, 0x2d, - 0x61, 0x0b, 0xfb, 0xe6, 0xb9, 0xc6, 0xfb, 0xaf, 0x8b, 0xa6, 0xf7, 0xab, - 0x58, 0xbf, 0x7b, 0xdd, 0x55, 0x01, 0xfb, 0x24, 0x7a, 0x42, 0xed, 0x54, - 0x46, 0x59, 0xc5, 0x4e, 0x32, 0x37, 0x73, 0x16, 0xf5, 0xf1, 0xd3, 0xcf, - 0xa9, 0xb5, 0xe2, 0xa7, 0xbb, 0xa8, 0x14, 0xb8, 0x62, 0xd3, 0x16, 0xf6, - 0x75, 0x1a, 0x69, 0x3a, 0xb0, 0x50, 0x85, 0x59, 0x83, 0x53, 0x9c, 0x3f, - 0x25, 0x21, 0x2c, 0xfd, 0xab, 0xd9, 0x87, 0xbc, 0x9d, 0xaa, 0xe0, 0xa6, - 0xf1, 0xb1, 0xfd, 0xc9, 0x01, 0xeb, 0x81, 0x0f, 0x72, 0x31, 0x25, 0x4b, - 0x61, 0x58, 0xed, 0x56, 0x03, 0x47, 0x55, 0x2b, 0x6a, 0x08, 0x22, 0xe4, - 0x75, 0xc4, 0xb0, 0xae, 0x6b, 0xa6, 0x0e, 0xad, 0x70, 0xc1, 0x42, 0xe0, - 0x4f, 0x04, 0xaf, 0x27, 0x6f, 0x44, 0xd7, 0x55, 0xf4, 0x58, 0x51, 0x4d, - 0xd0, 0x34, 0x8b, 0x13, 0x07, 0xef, 0x52, 0xcd, 0x0f, 0xb4, 0x5f, 0xa7, - 0x79, 0xa9, 0xe2, 0xb9, 0xff, 0xd5, 0x11, 0xf9, 0x47, 0x1d, 0xa8, 0x3c, - 0xee, 0x51, 0x97, 0x59, 0x64, 0x52, 0x77, 0x3d, 0x5c, 0x1e, 0x30, 0xfa, - 0xff, 0xd6, 0x9a, 0xba, 0xc3, 0xa9, 0x38, 0xa7, 0x76, 0xb3, 0x64, 0xcc, - 0xec, 0xed, 0x6f, 0x12, 0xe6, 0x33, 0xbc, 0x4c, 0xd2, 0x58, 0x26, 0x56, - 0x2a, 0x45, 0xaf, 0x28, 0x73, 0x05, 0x50, 0xe1, 0x41, 0xc2, 0x7c, 0xad, - 0x68, 0xa6, 0x36, 0xae, 0xa1, 0xc3, 0x0b, 0xe3, 0x4e, 0x07, 0x53, 0x2a, - 0x54, 0x46, 0xa4, 0x56, 0x8e, 0x58, 0xc3, 0x4b, 0x61, 0x32, 0x9d, 0x10, - 0x1d, 0xec, 0xe2, 0xca, 0x85, 0xb2, 0xfc, 0xa6, 0x49, 0xaa, 0xc8, 0xbb, - 0xa8, 0xd8, 0x0a, 0xfc, 0x17, 0x20, 0xcf, 0x3e, 0x18, 0x53, 0x91, 0x59, - 0x2a, 0x51, 0x44, 0x3b, 0x89, 0x1b, 0x37, 0xf7, 0x5d, 0xd4, 0xc2, 0xb8, - 0xfe, 0xa8, 0xb0, 0xa7, 0x0b, 0xb5, 0xdd, 0xce, 0xd9, 0xf0, 0x59, 0x15, - 0x4d, 0x36, 0x3b, 0x4e, 0x2a, 0x59, 0x48, 0x55, 0x3a, 0x43, 0x02, 0x26, - 0x76, 0x02, 0x89, 0xde, 0x1f, 0xc0, 0x5f, 0xac, 0x7b, 0xa6, 0x7b, 0xaf, - 0xdd, 0xc5, 0xe3, 0xe5, 0x46, 0x0a, 0xec, 0x2c, 0x26, 0x48, 0x58, 0x57, - 0x0f, 0x58, 0x20, 0x4a, 0xe1, 0x2f, 0xaf, 0x0d, 0x34, 0xe9, 0x86, 0xc8, - 0x0d, 0xb1, 0xb4, 0xa6, 0x31, 0xab, 0xc3, 0xbd, 0x5a, 0xdb, 0x06, 0xff, - 0xdc, 0x22, 0xe7, 0x40, 0x2a, 0x54, 0x6e, 0x59, 0xdc, 0x4f, 0xff, 0x38, - 0xae, 0x18, 0x41, 0xf4, 0xc7, 0xd1, 0xfe, 0xb6, 0x55, 0xa8, 0x39, 0xa8, - 0xbd, 0xb6, 0x5f, 0xd1, 0xce, 0xf3, 0x3b, 0x18, 0xa4, 0x38, 0xa5, 0x4f, - 0x69, 0x59, 0x50, 0x54, 0x3a, 0x41, 0x46, 0x23, 0x7f, 0xff, 0xc3, 0xdb, - 0x17, 0xbe, 0x54, 0xab, 0xac, 0xa6, 0xd5, 0xb0, 0x29, 0xc8, 0xc2, 0xe8, - 0x3a, 0x0d, 0x7d, 0x2f, 0xde, 0x49, 0xf8, 0x57, 0x74, 0x57, 0x67, 0x48, - 0x58, 0x2d, 0xb7, 0x0a, 0x58, 0xe6, 0x33, 0xc6, 0xb2, 0xaf, 0x7f, 0xa6, - 0x36, 0xac, 0xcd, 0xbf, 0x19, 0xde, 0x01, 0x02, 0x96, 0x25, 0xee, 0x42, - 0x21, 0x55, 0x37, 0x59, 0x74, 0x4e, 0xa9, 0x36, 0xce, 0x15, 0x4c, 0xf1, - 0x41, 0xcf, 0x4d, 0xb5, 0xc2, 0xa7, 0xe2, 0xa8, 0x7c, 0xb8, 0xf5, 0xd3, - 0xc2, 0xf6, 0x17, 0x1b, 0xec, 0x3a, 0xf8, 0x50, 0x8d, 0x59, 0x43, 0x53, - 0x24, 0x3f, 0x84, 0x20, 0x83, 0xfc, 0x0e, 0xd9, 0x1a, 0xbc, 0x67, 0xaa, - 0xf4, 0xa6, 0x43, 0xb2, 0x8b, 0xca, 0xa2, 0xeb, 0x2f, 0x10, 0xfd, 0x31, - 0x82, 0x4b, 0x7f, 0x58, 0xbf, 0x56, 0x9e, 0x46, 0xbd, 0x2a, 0xbf, 0x07, - 0x81, 0xe3, 0xf3, 0xc3, 0x6b, 0xae, 0x67, 0xa6, 0x4f, 0xad, 0xea, 0xc1, - 0xe5, 0xe0, 0xf7, 0x04, 0x4c, 0x28, 0xda, 0x44, 0x07, 0x56, 0xe1, 0x58, - 0xf8, 0x4c, 0x48, 0x34, 0xe2, 0x12, 0x5e, 0xee, 0xc8, 0xcc, 0xb0, 0xb3, - 0x4c, 0xa7, 0xa1, 0xa9, 0x50, 0xba, 0x96, 0xd6, 0xba, 0xf9, 0xec, 0x1d, - 0x23, 0x3d, 0x33, 0x52, 0x99, 0x59, 0x1d, 0x52, 0xfe, 0x3c, 0xb8, 0x1d, - 0x86, 0xf9, 0x67, 0xd6, 0x2f, 0xba, 0x94, 0xa9, 0x51, 0xa7, 0xce, 0xb3, - 0xf2, 0xcc, 0x93, 0xee, 0x17, 0x13, 0x72, 0x34, 0x11, 0x4d, 0xeb, 0x58, - 0xf4, 0x55, 0xbe, 0x44, 0x16, 0x28, 0xc9, 0x04, 0xac, 0xe0, 0xca, 0xc1, - 0x36, 0xad, 0x6c, 0xa6, 0x7e, 0xae, 0x1c, 0xc4, 0xb2, 0xe3, 0xf4, 0x07, - 0xee, 0x2a, 0xbb, 0x46, 0xcf, 0x56, 0x74, 0x58, 0x67, 0x4b, 0xd2, 0x31, - 0xf7, 0x0f, 0x73, 0xeb, 0x5d, 0xca, 0x2b, 0xb2, 0xed, 0xa6, 0x77, 0xaa, - 0x3c, 0xbc, 0x40, 0xd9, 0xb6, 0xfc, 0xb6, 0x20, 0x4a, 0x3f, 0x55, 0x53, - 0x8e, 0x59, 0xdf, 0x50, 0xc4, 0x3a, 0xe7, 0x1a, 0x8a, 0xf6, 0xca, 0xd3, - 0x5a, 0xb8, 0xd5, 0xa8, 0xce, 0xa7, 0x69, 0xb5, 0x6d, 0xcf, 0x82, 0xf1, - 0xff, 0x15, 0xd4, 0x36, 0x90, 0x4e, 0x37, 0x59, 0x17, 0x55, 0xc5, 0x42, - 0x69, 0x25, 0xcb, 0x01, 0xe7, 0xdd, 0xab, 0xbf, 0x21, 0xac, 0x83, 0xa6, - 0xc8, 0xaf, 0x5c, 0xc6, 0x8b, 0xe6, 0xec, 0x0a, 0x85, 0x2d, 0x86, 0x48, - 0x80, 0x57, 0xef, 0x57, 0xbd, 0x49, 0x53, 0x2f, 0x02, 0x0d, 0x92, 0xe8, - 0xfe, 0xc7, 0xbe, 0xb0, 0xa5, 0xa6, 0x6a, 0xab, 0x35, 0xbe, 0xfa, 0xdb, - 0xad, 0xff, 0x7e, 0x23, 0x59, 0x41, 0x65, 0x54, 0x65, 0x59, 0x8b, 0x4f, - 0x7d, 0x38, 0x06, 0x18, 0x9a, 0xf3, 0x33, 0xd1, 0x9d, 0xb6, 0x2f, 0xa8, - 0x60, 0xa8, 0x1c, 0xb7, 0xf5, 0xd1, 0x77, 0xf4, 0xde, 0x18, 0x2a, 0x39, - 0xf2, 0x4f, 0x73, 0x59, 0x17, 0x54, 0xc4, 0x40, 0xa8, 0x22, 0xd4, 0xfe, - 0x26, 0xdb, 0xa3, 0xbd, 0x1e, 0xab, 0xb9, 0xa6, 0x26, 0xb1, 0xaf, 0xc8, - 0x69, 0xe9, 0xe1, 0x0d, 0x10, 0x30, 0x3d, 0x4a, 0x18, 0x58, 0x4e, 0x57, - 0x03, 0x48, 0xc1, 0x2c, 0x11, 0x0a, 0xb0, 0xe5, 0xb4, 0xc5, 0x63, 0xaf, - 0x7c, 0xa6, 0x6d, 0xac, 0x4b, 0xc0, 0xb4, 0xde, 0xae, 0x02, 0x32, 0x26, - 0x5c, 0x43, 0x5a, 0x55, 0x23, 0x59, 0x22, 0x4e, 0x23, 0x36, 0x24, 0x15, - 0xa6, 0xf0, 0xb0, 0xce, 0xef, 0xb4, 0xa7, 0xa7, 0x0a, 0xa9, 0xe1, 0xb8, - 0x8e, 0xd4, 0x68, 0xf7, 0xc1, 0x1b, 0x66, 0x3b, 0x46, 0x51, 0x8c, 0x59, - 0x09, 0x53, 0xa6, 0x3e, 0xe8, 0x1f, 0xd4, 0xfb, 0x77, 0xd8, 0xa8, 0xbb, - 0x38, 0xaa, 0x04, 0xa7, 0x9d, 0xb2, 0x10, 0xcb, 0x4d, 0xec, 0xd4, 0x10, - 0x8d, 0x32, 0xdd, 0x4b, 0x98, 0x58, 0x96, 0x56, 0x32, 0x46, 0x26, 0x2a, - 0x17, 0x07, 0xdb, 0xe2, 0x78, 0xc3, 0x22, 0xae, 0x68, 0xa6, 0x8d, 0xad, - 0x6d, 0xc2, 0x7c, 0xe1, 0xac, 0x05, 0xdc, 0x28, 0x4e, 0x45, 0x32, 0x56, - 0xcc, 0x58, 0xa0, 0x4c, 0xbb, 0x33, 0x3c, 0x12, 0xb6, 0xed, 0x3a, 0xcc, - 0x5a, 0xb3, 0x31, 0xa7, 0xd3, 0xa9, 0xb8, 0xba, 0x32, 0xd7, 0x62, 0xfa, - 0x90, 0x1e, 0x9e, 0x3d, 0x77, 0x52, 0x98, 0x59, 0xd9, 0x51, 0x7e, 0x3c, - 0x19, 0x1d, 0xda, 0xf8, 0xcf, 0xd5, 0xc5, 0xb9, 0x65, 0xa9, 0x6e, 0xa7, - 0x25, 0xb4, 0x84, 0xcd, 0x34, 0xef, 0xc5, 0x13, 0xf6, 0x34, 0x6f, 0x4d, - 0xfa, 0x58, 0xc6, 0x55, 0x4e, 0x44, 0x7e, 0x27, 0x1b, 0x04, 0x11, 0xe0, - 0x49, 0xc1, 0xfb, 0xac, 0x6b, 0xa6, 0xc7, 0xae, 0x9d, 0xc4, 0x53, 0xe4, - 0xa1, 0x08, 0x81, 0x2b, 0x25, 0x47, 0xf9, 0x56, 0x58, 0x58, 0x09, 0x4b, - 0x46, 0x31, 0x4a, 0x0f, 0xd2, 0xea, 0xce, 0xc9, 0xdb, 0xb1, 0xd9, 0xa6, - 0xac, 0xaa, 0xac, 0xbc, 0xda, 0xd9, 0x61, 0xfd, 0x57, 0x21, 0xbf, 0x3f, - 0x98, 0x53, 0x83, 0x59, 0x97, 0x50, 0x44, 0x3a, 0x3f, 0x1a, 0xe4, 0xf5, - 0x33, 0xd3, 0xf3, 0xb7, 0xb1, 0xa8, 0xea, 0xa7, 0xca, 0xb5, 0xfd, 0xcf, - 0x2b, 0xf2, 0xa4, 0x16, 0x5e, 0x37, 0xde, 0x4e, 0x4a, 0x59, 0xdc, 0x54, - 0x57, 0x42, 0xc9, 0x24, 0x23, 0x01, 0x47, 0xdd, 0x36, 0xbf, 0xe3, 0xab, - 0x8e, 0xa6, 0x14, 0xb0, 0xe1, 0xc6, 0x2e, 0xe7, 0x97, 0x0b, 0x17, 0x2e, - 0xeb, 0x48, 0xa5, 0x57, 0xcb, 0x57, 0x5f, 0x49, 0xbc, 0x2e, 0x5f, 0x0c, - 0xe7, 0xe7, 0x7d, 0xc7, 0x6c, 0xb0, 0x9a, 0xa6, 0xa1, 0xab, 0xad, 0xbe, - 0x94, 0xdc, 0x5c, 0x00, 0x16, 0x24, 0xd1, 0x41, 0x9c, 0x54, 0x5a, 0x59, - 0x3b, 0x4f, 0xf8, 0x37, 0x62, 0x17, 0xed, 0xf2, 0xa6, 0xd0, 0x37, 0xb6, - 0x11, 0xa8, 0x83, 0xa8, 0x80, 0xb7, 0x89, 0xd2, 0x1f, 0xf5, 0x86, 0x19, - 0xa9, 0x39, 0x44, 0x50, 0x78, 0x59, 0xdf, 0x53, 0x48, 0x40, 0x0f, 0x22, - 0x24, 0xfe, 0x90, 0xda, 0x2b, 0xbd, 0xeb, 0xaa, 0xc6, 0xa6, 0x78, 0xb1, - 0x39, 0xc9, 0x0b, 0xea, 0x8d, 0x0e, 0x9f, 0x30, 0x9b, 0x4a, 0x3a, 0x58, - 0x24, 0x57, 0x9e, 0x47, 0x2d, 0x2c, 0x64, 0x09, 0x10, 0xe5, 0x2f, 0xc5, - 0x1c, 0xaf, 0x72, 0xa6, 0xb0, 0xac, 0xbf, 0xc0, 0x57, 0xdf, 0x57, 0x03, - 0xcc, 0x26, 0xd0, 0x43, 0x89, 0x55, 0x15, 0x59, 0xcb, 0x4d, 0x9b, 0x35, - 0x7f, 0x14, 0xfb, 0xef, 0x23, 0xce, 0x92, 0xb4, 0x8a, 0xa7, 0x35, 0xa9, - 0x4a, 0xb9, 0x22, 0xd5, 0x17, 0xf8, 0x5e, 0x1c, 0xeb, 0x3b, 0x88, 0x51, - 0x97, 0x59, 0xc0, 0x52, 0x33, 0x3e, 0x41, 0x1f, 0x2d, 0xfb, 0xdc, 0xd7, - 0x3a, 0xbb, 0x09, 0xaa, 0x16, 0xa7, 0xf7, 0xb2, 0x96, 0xcb, 0xf9, 0xec, - 0x79, 0x11, 0x1b, 0x33, 0x38, 0x4c, 0xb0, 0x58, 0x6a, 0x56, 0xc6, 0x45, - 0x90, 0x29, 0x6a, 0x06, 0x3c, 0xe2, 0xf8, 0xc2, 0xde, 0xad, 0x66, 0xa6, - 0xd3, 0xad, 0xe6, 0xc2, 0x22, 0xe2, 0x53, 0x06, 0x77, 0x29, 0xb8, 0x45, - 0x61, 0x56, 0xb4, 0x58, 0x48, 0x4c, 0x2d, 0x33, 0x96, 0x11, 0x0d, 0xed, - 0xb0, 0xcb, 0x01, 0xb3, 0x1c, 0xa7, 0xff, 0xa9, 0x2b, 0xbb, 0xc5, 0xd7, - 0x11, 0xfb, 0x2e, 0x1f, 0x1b, 0x3e, 0xb9, 0x52, 0x98, 0x59, 0x8e, 0x51, - 0x06, 0x3c, 0x6f, 0x1c, 0x35, 0xf8, 0x37, 0xd5, 0x59, 0xb9, 0x3f, 0xa9, - 0x81, 0xa7, 0x88, 0xb4, 0x0b, 0xce, 0xe3, 0xef, 0x66, 0x14, 0x84, 0x35, - 0xc2, 0x4d, 0x0e, 0x59, 0x95, 0x55, 0xdd, 0x43, 0xe5, 0x26, 0x71, 0x03, - 0x6e, 0xdf, 0xd3, 0xc0, 0xb8, 0xac, 0x71, 0xa6, 0x12, 0xaf, 0x1b, 0xc5, - 0xf7, 0xe4, 0x4c, 0x09, 0x14, 0x2c, 0x8f, 0x47, 0x21, 0x57, 0x3a, 0x58, - 0xad, 0x4a, 0xb2, 0x30, 0xa8, 0x0e, 0x23, 0xea, 0x50, 0xc9, 0x80, 0xb1, - 0xcd, 0xa6, 0xdf, 0xaa, 0x1d, 0xbd, 0x77, 0xda, 0x0b, 0xfe, 0xf6, 0x21, - 0x38, 0x40, 0xd4, 0x53, 0x7c, 0x59, 0x4c, 0x50, 0xbf, 0x39, 0x9e, 0x19, - 0x38, 0xf5, 0xa1, 0xd2, 0x8c, 0xb7, 0x8d, 0xa8, 0x07, 0xa8, 0x2d, 0xb6, - 0x8c, 0xd0, 0xd5, 0xf2, 0x4b, 0x17, 0xe1, 0x37, 0x32, 0x4f, 0x55, 0x59, - 0xa6, 0x54, 0xe3, 0x41, 0x2d, 0x24, 0x77, 0x00, 0xaa, 0xdc, 0xbf, 0xbe, - 0xaa, 0xab, 0x98, 0xa6, 0x62, 0xb0, 0x66, 0xc7, 0xd1, 0xe7, 0x43, 0x0c, - 0xa7, 0x2e, 0x51, 0x49, 0xc4, 0x57, 0xac, 0x57, 0xf9, 0x48, 0x2e, 0x2e, - 0xb0, 0x0b, 0x47, 0xe7, 0xf4, 0xc6, 0x21, 0xb0, 0x8e, 0xa6, 0xdd, 0xab, - 0x21, 0xbf, 0x32, 0xdd, 0x07, 0x01, 0xb2, 0x24, 0x47, 0x42, 0xd2, 0x54, - 0x4d, 0x59, 0xeb, 0x4e, 0x70, 0x37, 0xbe, 0x16, 0x45, 0xf2, 0x12, 0xd0, - 0xd9, 0xb5, 0xef, 0xa7, 0xa9, 0xa8, 0xe6, 0xb7, 0x1c, 0xd3, 0xca, 0xf5, - 0x28, 0x1a, 0x2f, 0x3a, 0x8b, 0x50, 0x85, 0x59, 0x9d, 0x53, 0xd5, 0x3f, - 0x6d, 0x21, 0x7c, 0xfd, 0xf1, 0xd9, 0xbd, 0xbc, 0xb3, 0xaa, 0xd8, 0xa6, - 0xcc, 0xb1, 0xbd, 0xc9, 0xb6, 0xea, 0x32, 0x0f, 0x31, 0x31, 0xf8, 0x4a, - 0x57, 0x58, 0xfc, 0x56, 0x38, 0x47, 0x95, 0x2b, 0xbd, 0x08, 0x69, 0xe4, - 0xb3, 0xc4, 0xce, 0xae, 0x72, 0xa6, 0xea, 0xac, 0x3e, 0xc1, 0xf3, 0xdf, - 0x04, 0x04, 0x67, 0x27, 0x3c, 0x44, 0xbf, 0x55, 0xff, 0x58, 0x78, 0x4d, - 0x0f, 0x35, 0xda, 0x13, 0x50, 0xef, 0x99, 0xcd, 0x32, 0xb4, 0x72, 0xa7, - 0x5e, 0xa9, 0xb6, 0xb9, 0xb6, 0xd5, 0xc4, 0xf8, 0xfd, 0x1c, 0x6e, 0x3c, - 0xcb, 0x51, 0x9a, 0x59, 0x7f, 0x52, 0xb5, 0x3d, 0xa3, 0x1e, 0x80, 0xfa, - 0x44, 0xd7, 0xcd, 0xba, 0xd7, 0xa9, 0x30, 0xa7, 0x4a, 0xb3, 0x27, 0xcc, - 0x9c, 0xed, 0x23, 0x12, 0xa7, 0x33, 0x91, 0x4c, 0xc9, 0x58, 0x3a, 0x56, - 0x5d, 0x45, 0xf6, 0x28, 0xc1, 0x05, 0x98, 0xe1, 0x7e, 0xc2, 0x97, 0xad, - 0x6b, 0xa6, 0x13, 0xae, 0x67, 0xc3, 0xc3, 0xe2, 0xfc, 0x06, 0x12, 0x2a, - 0x1f, 0x46, 0x91, 0x56, 0x9a, 0x58, 0xee, 0x4b, 0x9f, 0x32, 0xef, 0x10, - 0x64, 0xec, 0x29, 0xcb, 0xa5, 0xb2, 0x0b, 0xa7, 0x2d, 0xaa, 0x99, 0xbb, - 0x60, 0xd8, 0xbb, 0xfb, 0xce, 0x1f, 0x96, 0x3e, 0xfb, 0x52, 0x92, 0x59, - 0x4c, 0x51, 0x7f, 0x3b, 0xd3, 0x1b, 0x87, 0xf7, 0xa0, 0xd4, 0xf5, 0xb8, - 0x0e, 0xa9, 0xa4, 0xa7, 0xdf, 0xb4, 0x9d, 0xce, 0x8a, 0xf0, 0x0d, 0x15, - 0x0f, 0x36, 0x13, 0x4e, 0x24, 0x59, 0x5d, 0x55, 0x72, 0x43, 0x46, 0x26, - 0xc9, 0x02, 0xcc, 0xde, 0x5b, 0xc0, 0x7a, 0xac, 0x78, 0xa6, 0x5a, 0xaf, - 0x9e, 0xc5, 0x9a, 0xe5, 0xf4, 0x09, 0xae, 0x2c, 0xf1, 0x47, 0x4c, 0x57, - 0x19, 0x58, 0x4e, 0x4a, 0x23, 0x30, 0xfd, 0x0d, 0x80, 0xe9, 0xc6, 0xc8, - 0x2f, 0xb1, 0xbe, 0xa6, 0x14, 0xab, 0x91, 0xbd, 0x11, 0xdb, 0xb7, 0xfe, - 0x94, 0x22, 0xaf, 0x40, 0x10, 0x54, 0x73, 0x59, 0xfe, 0x4f, 0x3e, 0x39, - 0xf7, 0x18, 0x91, 0xf4, 0x0a, 0xd2, 0x2d, 0xb7, 0x62, 0xa8, 0x2f, 0xa8, - 0x89, 0xb6, 0x21, 0xd1, 0x7e, 0xf3, 0xee, 0x17, 0x6b, 0x38, 0x7c, 0x4f, - 0x66, 0x59, 0x6a, 0x54, 0x70, 0x41, 0x90, 0x23, 0xcc, 0xff, 0x0b, 0xdc, - 0x4d, 0xbe, 0x6e, 0xab, 0xa6, 0xa6, 0xb0, 0xb0, 0xeb, 0xc7, 0x77, 0xe8, - 0xec, 0x0c, 0x39, 0x2f, 0xb3, 0x49, 0xe7, 0x57, 0x85, 0x57, 0x99, 0x48, - 0x96, 0x2d, 0x0c, 0x0b, 0x9d, 0xe6, 0x75, 0xc6, 0xd0, 0xaf, 0x88, 0xa6, - 0x15, 0xac, 0x9b, 0xbf, 0xcc, 0xdd, 0xb6, 0x01, 0x4c, 0x25, 0xbb, 0x42, - 0x07, 0x55, 0x40, 0x59, 0x97, 0x4e, 0xec, 0x36, 0x17, 0x16, 0x9a, 0xf1, - 0x85, 0xcf, 0x76, 0xb5, 0xd3, 0xa7, 0xce, 0xa8, 0x4d, 0xb8, 0xb0, 0xd3, - 0x74, 0xf6, 0xcd, 0x1a, 0xaf, 0x3a, 0xd8, 0x50, 0x88, 0x59, 0x62, 0x53, - 0x5a, 0x3f, 0xd1, 0x20, 0xcd, 0xfc, 0x59, 0xd9, 0x4a, 0xbc, 0x82, 0xaa, - 0xe7, 0xa6, 0x22, 0xb2, 0x44, 0xca, 0x5d, 0xeb, 0xdc, 0x0f, 0xbd, 0x31, - 0x59, 0x4b, 0x6f, 0x58, 0xd6, 0x56, 0xcc, 0x46, 0x03, 0x2b, 0x0e, 0x08, - 0xcb, 0xe3, 0x2f, 0xc4, 0x8a, 0xae, 0x6a, 0xa6, 0x2e, 0xad, 0xb6, 0xc1, - 0x96, 0xe0, 0xad, 0x04, 0x01, 0x28, 0xaa, 0x44, 0xf1, 0x55, 0xea, 0x58, - 0x21, 0x4d, 0x86, 0x34, 0x31, 0x13, 0xab, 0xee, 0x08, 0xcd, 0xda, 0xb3, - 0x57, 0xa7, 0x8c, 0xa9, 0x20, 0xba, 0x4e, 0xd6, 0x6e, 0xf9, 0x9e, 0x1d, - 0xed, 0x3c, 0x10, 0x52, 0x9c, 0x59, 0x3c, 0x52, 0x35, 0x3d, 0x06, 0x1e, - 0xd1, 0xf9, 0xb1, 0xd6, 0x5c, 0xba, 0xac, 0xa9, 0x44, 0xa7, 0xa7, 0xb3, - 0xaf, 0xcc, 0x48, 0xee, 0xc7, 0x12, 0x33, 0x34, 0xeb, 0x4c, 0xdd, 0x58, - 0x0f, 0x56, 0xec, 0x44, 0x60, 0x28, 0x15, 0x05, 0xf7, 0xe0, 0x03, 0xc2, - 0x54, 0xad, 0x6b, 0xa6, 0x5d, 0xae, 0xe3, 0xc3, 0x65, 0xe3, 0xa8, 0x07, - 0xa6, 0x2a, 0x8c, 0x46, 0xbc, 0x56, 0x80, 0x58, 0x91, 0x4b, 0x14, 0x32, - 0x43, 0x10, 0xc2, 0xeb, 0x9a, 0xca, 0x54, 0xb2, 0xf3, 0xa6, 0x63, 0xaa, - 0x06, 0xbc, 0xfa, 0xd8, 0x66, 0xfc, 0x6e, 0x20, 0x11, 0x3f, 0x3b, 0x53, - 0x8c, 0x59, 0x05, 0x51, 0xfd, 0x3a, 0x32, 0x1b, 0xda, 0xf6, 0x0e, 0xd4, - 0x89, 0xb8, 0xea, 0xa8, 0xbc, 0xa7, 0x40, 0xb5, 0x2a, 0xcf, 0x36, 0xf1, - 0xb0, 0x15, 0x98, 0x36, 0x67, 0x4e, 0x33, 0x59, 0x2c, 0x55, 0xfd, 0x42, - 0xae, 0x25, 0x1a, 0x02, 0x32, 0xde, 0xe0, 0xbf, 0x3d, 0xac, 0x81, 0xa6, - 0xa2, 0xaf, 0x24, 0xc6, 0x3b, 0xe6, 0xa2, 0x0a, 0x3d, 0x2d, 0x5b, 0x48, - 0x6e, 0x57, 0xfd, 0x57, 0xeb, 0x49, 0x95, 0x2f, 0x50, 0x0d, 0xdf, 0xe8, - 0x3b, 0xc8, 0xe2, 0xb0, 0xac, 0xa6, 0x4f, 0xab, 0x02, 0xbe, 0xae, 0xdb, - 0x63, 0xff, 0x30, 0x23, 0x27, 0x41, 0x49, 0x54, 0x69, 0x59, 0xb1, 0x4f, - 0xb9, 0x38, 0x53, 0x18, 0xe8, 0xf3, 0x75, 0xd1, 0xcb, 0xb6, 0x3f, 0xa8, - 0x4f, 0xa8, 0xf0, 0xb6, 0xb0, 0xd1, 0x29, 0xf4, 0x93, 0x18, 0xed, 0x38, - 0xcd, 0x4f, 0x71, 0x59, 0x30, 0x54, 0xfa, 0x40, 0xf2, 0x22, 0x21, 0xff, - 0x70, 0xdb, 0xd8, 0xbd, 0x35, 0xab, 0xb5, 0xa6, 0xfe, 0xb0, 0x74, 0xc8, - 0x1b, 0xe9, 0x94, 0x0d, 0xcc, 0x2f, 0x13, 0x4a, 0x07, 0x58, 0x63, 0x57, - 0x2e, 0x48, 0x09, 0x2d, 0x5b, 0x0a, 0x00, 0xe6, 0xed, 0xc5, 0x87, 0xaf, - 0x7e, 0xa6, 0x53, 0xac, 0x10, 0xc0, 0x6f, 0xde, 0x5d, 0x02, 0xea, 0x25, - 0x2a, 0x43, 0x40, 0x55, 0x2c, 0x59, 0x49, 0x4e, 0x5f, 0x36, 0x74, 0x15, - 0xf1, 0xf0, 0xf4, 0xce, 0x1a, 0xb5, 0xb2, 0xa7, 0xf9, 0xa8, 0xb1, 0xb8, - 0x48, 0xd4, 0x1d, 0xf7, 0x70, 0x1b, 0x31, 0x3b, 0x1f, 0x51, 0x90, 0x59, - 0x21, 0x53, 0xe2, 0x3e, 0x2f, 0x20, 0x25, 0xfc, 0xbd, 0xd8, 0xdb, 0xbb, - 0x4f, 0xaa, 0xfb, 0xa6, 0x76, 0xb2, 0xcf, 0xca, 0x02, 0xec, 0x86, 0x10, - 0x4b, 0x32, 0xb4, 0x4b, 0x8b, 0x58, 0xab, 0x56, 0x62, 0x46, 0x6d, 0x2a, - 0x64, 0x07, 0x26, 0xe3, 0xb3, 0xc3, 0x41, 0xae, 0x69, 0xa6, 0x70, 0xad, - 0x30, 0xc2, 0x37, 0xe1, 0x59, 0x05, 0x98, 0x28, 0x1b, 0x45, 0x1e, 0x56, - 0xd5, 0x58, 0xca, 0x4c, 0xf9, 0x33, 0x8c, 0x12, 0x01, 0xee, 0x7d, 0xcc, - 0x80, 0xb3, 0x3f, 0xa7, 0xb9, 0xa9, 0x8c, 0xba, 0xe6, 0xd6, 0x19, 0xfa, - 0x40, 0x1e, 0x69, 0x3d, 0x55, 0x52, 0x9b, 0x59, 0xf7, 0x51, 0xba, 0x3c, - 0x61, 0x1d, 0x2a, 0xf9, 0x14, 0xd6, 0xf7, 0xb9, 0x79, 0xa9, 0x62, 0xa7, - 0xfc, 0xb3, 0x40, 0xcd, 0xec, 0xee, 0x72, 0x13, 0xbc, 0x34, 0x42, 0x4d, - 0xf5, 0x58, 0xda, 0x55, 0x83, 0x44, 0xc2, 0x27, 0x6d, 0x04, 0x57, 0xe0, - 0x86, 0xc1, 0x14, 0xad, 0x6d, 0xa6, 0xa3, 0xae, 0x65, 0xc4, 0x06, 0xe4, - 0x53, 0x08, 0x3c, 0x2b, 0xf5, 0x46, 0xe6, 0x56, 0x65, 0x58, 0x35, 0x4b, - 0x84, 0x31, 0x9d, 0x0f, 0x19, 0xeb, 0x12, 0xca, 0xff, 0xb1, 0xe1, 0xa6, - 0x95, 0xaa, 0x77, 0xbc, 0x95, 0xd9, 0x10, 0xfd, 0x0f, 0x21, 0x88, 0x3f, - 0x7a, 0x53, 0x89, 0x59, 0xb7, 0x50, 0x80, 0x3a, 0x8c, 0x1a, 0x30, 0xf6, - 0x79, 0xd3, 0x23, 0xb8, 0xc0, 0xa8, 0xdf, 0xa7, 0x9a, 0xb5, 0xbe, 0xcf, - 0xdb, 0xf1, 0x59, 0x16, 0x1f, 0x37, 0xb9, 0x4e, 0x43, 0x59, 0xf5, 0x54, - 0x8c, 0x42, 0x11, 0x25, 0x73, 0x01, 0x8d, 0xdd, 0x6f, 0xbf, 0xfc, 0xab, - 0x8c, 0xa6, 0xee, 0xaf, 0xa6, 0xc6, 0xe2, 0xe6, 0x48, 0x0b, 0xd5, 0x2d, - 0xba, 0x48, 0x97, 0x57, 0xda, 0x57, 0x8b, 0x49, 0x03, 0x2f, 0xa7, 0x0c, - 0x38, 0xe8, 0xb8, 0xc7, 0x90, 0xb0, 0xa2, 0xa6, 0x84, 0xab, 0x79, 0xbe, - 0x4a, 0xdc, 0x0e, 0x00, 0xce, 0x23, 0x9b, 0x41, 0x84, 0x54, 0x5c, 0x59, - 0x64, 0x4f, 0x32, 0x38, 0xaf, 0x17, 0x3e, 0xf3, 0xe3, 0xd0, 0x6b, 0xb6, - 0x1a, 0xa8, 0x76, 0xa8, 0x50, 0xb7, 0x45, 0xd2, 0xd2, 0xf4, 0x39, 0x19, - 0x6e, 0x39, 0x20, 0x50, 0x73, 0x59, 0xfb, 0x53, 0x81, 0x40, 0x55, 0x22, - 0x76, 0xfe, 0xd3, 0xda, 0x64, 0xbd, 0x02, 0xab, 0xbf, 0xa6, 0x55, 0xb1, - 0xf4, 0xc8, 0xc6, 0xe9, 0x3a, 0x0e, 0x5f, 0x30, 0x71, 0x4a, 0x28, 0x58, - 0x39, 0x57, 0xcd, 0x47, 0x70, 0x2c, 0xb5, 0x09, 0x58, 0xe5, 0x6e, 0xc5, - 0x3b, 0xaf, 0x78, 0xa6, 0x8f, 0xac, 0x8b, 0xc0, 0x0a, 0xdf, 0x0d, 0x03, - 0x82, 0x26, 0x9c, 0x43, 0x74, 0x55, 0x19, 0x59, 0xf7, 0x4d, 0xd7, 0x35, - 0xcc, 0x14, 0x4b, 0xf0, 0x61, 0xce, 0xbf, 0xb4, 0x96, 0xa7, 0x20, 0xa9, - 0x1d, 0xb9, 0xdb, 0xd4, 0xc8, 0xf7, 0x13, 0x1c, 0xb1, 0x3b, 0x68, 0x51, - 0x94, 0x59, 0xe1, 0x52, 0x66, 0x3e, 0x90, 0x1f, 0x79, 0xfb, 0x24, 0xd8, - 0x6d, 0xbb, 0x1c, 0xaa, 0x10, 0xa7, 0xcc, 0xb2, 0x5a, 0xcb, 0xa8, 0xec, - 0x2f, 0x11, 0xd6, 0x32, 0x13, 0x4c, 0xa3, 0x58, 0x7e, 0x56, 0xfa, 0x45, - 0xd2, 0x29, 0xbd, 0x06, 0x83, 0xe2, 0x34, 0xc3, 0xfc, 0xad, 0x67, 0xa6, - 0xb4, 0xad, 0xad, 0xc2, 0xd7, 0xe1, 0x03, 0x06, 0x33, 0x29, 0x85, 0x45, - 0x4e, 0x56, 0xbe, 0x58, 0x6f, 0x4c, 0x72, 0x33, 0xde, 0x11, 0x5f, 0xed, - 0xed, 0xcb, 0x2a, 0xb3, 0x27, 0xa7, 0xe8, 0xa9, 0xf8, 0xba, 0x80, 0xd7, - 0xc2, 0xfa, 0xe3, 0x1e, 0xe3, 0x3d, 0x9b, 0x52, 0x96, 0x59, 0xb4, 0x51, - 0x39, 0x3c, 0xc2, 0x1c, 0x7d, 0xf8, 0x7f, 0xd5, 0x8a, 0xb9, 0x50, 0xa9, - 0x79, 0xa7, 0x5a, 0xb4, 0xcb, 0xcd, 0x96, 0xef, 0x19, 0x14, 0x45, 0x35, - 0x9a, 0x4d, 0x05, 0x59, 0xad, 0x55, 0x10, 0x44, 0x2c, 0x27, 0xc0, 0x03, - 0xb6, 0xdf, 0x0e, 0xc1, 0xd1, 0xac, 0x74, 0xa6, 0xea, 0xae, 0xe5, 0xc4, - 0xa9, 0xe4, 0xff, 0x08, 0xcf, 0x2b, 0x60, 0x47, 0x0d, 0x57, 0x49, 0x58, - 0xd7, 0x4a, 0xf6, 0x30, 0xf3, 0x0e, 0x73, 0xea, 0x8a, 0xc9, 0xaa, 0xb1, - 0xd2, 0xa6, 0xc7, 0xaa, 0xea, 0xbc, 0x2d, 0xda, 0xbf, 0xfd, 0xaa, 0x21, - 0x04, 0x40, 0xb6, 0x53, 0x80, 0x59, 0x6f, 0x50, 0xfb, 0x39, 0xea, 0x19, - 0x87, 0xf5, 0xe2, 0xd2, 0xbe, 0xb7, 0x9b, 0xa8, 0xfb, 0xa7, 0xff, 0xb5, - 0x49, 0xd0, 0x89, 0xf2, 0xfc, 0x16, 0xa6, 0x37, 0x0b, 0x4f, 0x4f, 0x59, - 0xc2, 0x54, 0x15, 0x42, 0x78, 0x24, 0xc4, 0x00, 0xf4, 0xdc, 0xf4, 0xbe, - 0xc5, 0xab, 0x93, 0xa6, 0x3e, 0xb0, 0x2a, 0xc7, 0x84, 0xe7, 0xf4, 0x0b, - 0x66, 0x2e, 0x21, 0x49, 0xb7, 0x57, 0xba, 0x57, 0x27, 0x49, 0x72, 0x2e, - 0xfd, 0x0b, 0x94, 0xe7, 0x30, 0xc7, 0x46, 0xb0, 0x92, 0xa6, 0xc2, 0xab, - 0xea, 0xbe, 0xeb, 0xdc, 0xb6, 0x00, 0x6e, 0x24, 0x0c, 0x42, 0xbe, 0x54, - 0x4f, 0x59, 0x13, 0x4f, 0xad, 0x37, 0x0b, 0x17, 0x90, 0xf2, 0x58, 0xd0, - 0x03, 0xb6, 0xff, 0xa7, 0x98, 0xa8, 0xb5, 0xb7, 0xda, 0xd2, 0x7a, 0xf5, - 0xdd, 0x19, 0xf5, 0x39, 0x65, 0x50, 0x84, 0x59, 0xb7, 0x53, 0x0e, 0x40, - 0xb5, 0x21, 0xcb, 0xfd, 0x38, 0xda, 0xf1, 0xbc, 0xcd, 0xaa, 0xcf, 0xa6, - 0xa5, 0xb1, 0x80, 0xc9, 0x67, 0xea, 0xe8, 0x0e, 0xeb, 0x30, 0xd2, 0x4a, - 0x44, 0x58, 0x13, 0x57, 0x65, 0x47, 0xdb, 0x2b, 0x0b, 0x09, 0xb4, 0xe4, - 0xee, 0xc4, 0xf0, 0xae, 0x73, 0xa6, 0xce, 0xac, 0x04, 0xc1, 0xac, 0xdf, - 0xb2, 0x03, 0x23, 0x27, 0x06, 0x44, 0xab, 0x55, 0x06, 0x59, 0xa0, 0x4d, - 0x4f, 0x35, 0x25, 0x14, 0xa1, 0xef, 0xd6, 0xcd, 0x5f, 0xb4, 0x7d, 0xa7, - 0x4a, 0xa9, 0x86, 0xb9, 0x72, 0xd5, 0x72, 0xf8, 0xb6, 0x1c, 0x2f, 0x3c, - 0xaf, 0x51, 0x99, 0x59, 0x9c, 0x52, 0xee, 0x3d, 0xee, 0x1e, 0xcd, 0xfa, - 0x8d, 0xd7, 0xfe, 0xba, 0xec, 0xa9, 0x26, 0xa7, 0x22, 0xb3, 0xe6, 0xcb, - 0x50, 0xed, 0xd5, 0x11, 0x66, 0x33, 0x69, 0x4c, 0xbe, 0x58, 0x50, 0x56, - 0x8d, 0x45, 0x3d, 0x29, 0x0f, 0x06, 0xe4, 0xe1, 0xb6, 0xc2, 0xb8, 0xad, - 0x66, 0xa6, 0xf9, 0xad, 0x28, 0xc3, 0x7d, 0xe2, 0xaa, 0x06, 0xcd, 0x29, - 0xef, 0x45, 0x7c, 0x56, 0xa6, 0x58, 0x16, 0x4c, 0xe3, 0x32, 0x39, 0x11, - 0xb6, 0xec, 0x62, 0xcb, 0xd4, 0xb2, 0x0f, 0xa7, 0x1b, 0xaa, 0x63, 0xbb, - 0x1b, 0xd8, 0x6b, 0xfb, 0x86, 0x1f, 0x5c, 0x3e, 0xdd, 0x52, 0x95, 0x59, - 0x6b, 0x51, 0xbc, 0x3b, 0x1d, 0x1c, 0xd4, 0xf7, 0xe8, 0xd4, 0x21, 0xb9, - 0x25, 0xa9, 0x94, 0xa7, 0xb7, 0xb4, 0x58, 0xce, 0x40, 0xf0, 0xbd, 0x14, - 0xd1, 0x35, 0xed, 0x4d, 0x1a, 0x59, 0x78, 0x55, 0xa3, 0x43, 0x8e, 0x26, - 0x18, 0x03, 0x15, 0xdf, 0x94, 0xc0, 0x95, 0xac, 0x75, 0xa6, 0x38, 0xaf, - 0x63, 0xc5, 0x4f, 0xe5, 0xa6, 0x09, 0x67, 0x2c, 0xc5, 0x47, 0x36, 0x57, - 0x2c, 0x58, 0x76, 0x4a, 0x68, 0x30, 0x4a, 0x0e, 0xcc, 0xe9, 0x03, 0xc9, - 0x57, 0xb1, 0xc2, 0xa6, 0xff, 0xaa, 0x58, 0xbd, 0xcd, 0xda, 0x65, 0xfe, - 0x4d, 0x22, 0x78, 0x40, 0xf4, 0x53, 0x79, 0x59, 0x20, 0x50, 0x7b, 0x39, - 0x43, 0x19, 0xde, 0xf4, 0x50, 0xd2, 0x58, 0xb7, 0x76, 0xa8, 0x1c, 0xa8, - 0x5f, 0xb6, 0xdc, 0xd0, 0x31, 0xf3, 0xa2, 0x17, 0x2b, 0x38, 0x5c, 0x4f, - 0x5c, 0x59, 0x89, 0x54, 0xa1, 0x41, 0xdb, 0x23, 0x1a, 0x00, 0x54, 0xdc, - 0x83, 0xbe, 0x87, 0xab, 0xa1, 0xa6, 0x8b, 0xb0, 0xaf, 0xc7, 0x29, 0xe8, - 0xa0, 0x0c, 0xf4, 0x2e, 0x87, 0x49, 0xd8, 0x57, 0x95, 0x57, 0xc7, 0x48, - 0xda, 0x2d, 0x59, 0x0b, 0xeb, 0xe6, 0xaf, 0xc6, 0xf6, 0xaf, 0x89, 0xa6, - 0xfc, 0xab, 0x62, 0xbf, 0x86, 0xdd, 0x64, 0x01, 0x08, 0x25, 0x82, 0x42, - 0xf3, 0x54, 0x41, 0x59, 0xc2, 0x4e, 0x27, 0x37, 0x64, 0x16, 0xea, 0xf1, - 0xc4, 0xcf, 0xa5, 0xb5, 0xde, 0xa7, 0xbf, 0xa8, 0x1a, 0xb8, 0x6f, 0xd3, - 0x24, 0xf6, 0x81, 0x1a, 0x77, 0x3a, 0xb0, 0x50, 0x8a, 0x59, 0x7b, 0x53, - 0x95, 0x3f, 0x18, 0x21, 0x1d, 0xfd, 0xa0, 0xd9, 0x7c, 0xbc, 0x9d, 0xaa, - 0xdc, 0xa6, 0xfd, 0xb1, 0x05, 0xca, 0x0e, 0xeb, 0x92, 0x0f, 0x78, 0x31, - 0x31, 0x4b, 0x61, 0x58, 0xe9, 0x56, 0xfe, 0x46, 0x45, 0x2b, 0x60, 0x08, - 0x12, 0xe4, 0x6c, 0xc4, 0xaa, 0xae, 0x6c, 0xa6, 0x11, 0xad, 0x7c, 0xc1, - 0x4c, 0xe0, 0x5f, 0x04, 0xb9, 0x27, 0x7b, 0x44, 0xd6, 0x55, 0xf7, 0x58, - 0x47, 0x4d, 0xc6, 0x34, 0x7f, 0x13, 0xf7, 0xee, 0x4a, 0xcd, 0x03, 0xb4, - 0x64, 0xa7, 0x75, 0xa9, 0xf0, 0xb9, 0x09, 0xd6, 0x1d, 0xf9, 0x58, 0x1d, - 0xaf, 0x3c, 0xf3, 0x51, 0x9a, 0x59, 0x5b, 0x52, 0x70, 0x3d, 0x4e, 0x1e, - 0x24, 0xfa, 0xf0, 0xd6, 0x95, 0xba, 0xbb, 0xa9, 0x3e, 0xa7, 0x7b, 0xb3, - 0x70, 0xcc, 0xf8, 0xed, 0x7d, 0x12, 0xf1, 0x33, 0xc4, 0x4c, 0xd2, 0x58, - 0x24, 0x56, 0x1f, 0x45, 0xa5, 0x28, 0x65, 0x05, 0x41, 0xe1, 0x3b, 0xc2, - 0x73, 0xad, 0x6a, 0xa6, 0x3b, 0xae, 0xab, 0xc3, 0x19, 0xe3, 0x5a, 0x07, - 0x60, 0x2a, 0x5c, 0x46, 0xa7, 0x56, 0x8d, 0x58, 0xbb, 0x4b, 0x56, 0x32, - 0x90, 0x10, 0x0f, 0xec, 0xd8, 0xca, 0x7d, 0xb2, 0xfb, 0xa6, 0x4d, 0xaa, - 0xd1, 0xbb, 0xb5, 0xd8, 0x16, 0xfc, 0x25, 0x20, 0xd9, 0x3e, 0x1c, 0x53, - 0x91, 0x59, 0x24, 0x51, 0x3b, 0x3b, 0x7a, 0x1b, 0x2b, 0xf7, 0x51, 0xd4, - 0xb9, 0xb8, 0xfc, 0xa8, 0xb0, 0xa7, 0x14, 0xb5, 0xe9, 0xce, 0xe5, 0xf0, - 0x66, 0x15, 0x5a, 0x36, 0x3f, 0x4e, 0x2d, 0x59, 0x43, 0x55, 0x32, 0x43, - 0xf4, 0x25, 0x6d, 0x02, 0x75, 0xde, 0x1d, 0xc0, 0x54, 0xac, 0x80, 0xa6, - 0x7e, 0xaf, 0xea, 0xc5, 0xed, 0xe5, 0x55, 0x0a, 0xf8, 0x2c, 0x2d, 0x48, - 0x5d, 0x57, 0x0a, 0x58, 0x1a, 0x4a, 0xd5, 0x2f, 0xa2, 0x0d, 0x27, 0xe9, - 0x7b, 0xc8, 0x07, 0xb1, 0xb2, 0xa6, 0x36, 0xab, 0xcc, 0xbd, 0x67, 0xdb, - 0x14, 0xff, 0xe7, 0x22, 0xf2, 0x40, 0x2c, 0x54, 0x71, 0x59, 0xd3, 0x4f, - 0xf7, 0x38, 0x9f, 0x18, 0x34, 0xf4, 0xbb, 0xd1, 0xf7, 0xb6, 0x50, 0xa8, - 0x40, 0xa8, 0xbf, 0xb6, 0x70, 0xd1, 0xd8, 0xf3, 0x4a, 0x18, 0xae, 0x38, - 0xab, 0x4f, 0x69, 0x59, 0x4d, 0x54, 0x30, 0x41, 0x3a, 0x23, 0x71, 0xff, - 0xb6, 0xdb, 0x0e, 0xbe, 0x50, 0xab, 0xad, 0xa6, 0xda, 0xb0, 0x36, 0xc8, - 0xcd, 0xe8, 0x49, 0x0d, 0x88, 0x2f, 0xe5, 0x49, 0xfc, 0x57, 0x6f, 0x57, - 0x63, 0x48, 0x46, 0x2d, 0xb0, 0x0a, 0x45, 0xe6, 0x2f, 0xc6, 0xa6, 0xaf, - 0x83, 0xa6, 0x36, 0xac, 0xdb, 0xbf, 0x23, 0xde, 0x11, 0x02, 0xa1, 0x25, - 0xf6, 0x42, 0x28, 0x55, 0x32, 0x59, 0x71, 0x4e, 0x9e, 0x36, 0xbf, 0x15, - 0x3f, 0xf1, 0x37, 0xcf, 0x43, 0xb5, 0xc3, 0xa7, 0xe4, 0xa8, 0x82, 0xb8, - 0x04, 0xd4, 0xce, 0xf6, 0x24, 0x1b, 0xf8, 0x3a, 0xfb, 0x50, 0x8f, 0x59, - 0x3f, 0x53, 0x18, 0x3f, 0x7a, 0x20, 0x72, 0xfc, 0x05, 0xd9, 0x0e, 0xbc, - 0x67, 0xaa, 0xf1, 0xa6, 0x4e, 0xb2, 0x91, 0xca, 0xb4, 0xeb, 0x39, 0x10, - 0x09, 0x32, 0x8b, 0x4b, 0x7e, 0x58, 0xbe, 0x56, 0x96, 0x46, 0xad, 0x2a, - 0xb8, 0x07, 0x6d, 0xe3, 0xef, 0xc3, 0x62, 0xae, 0x68, 0xa6, 0x54, 0xad, - 0xf4, 0xc1, 0xf1, 0xe0, 0x06, 0x05, 0x56, 0x28, 0xe5, 0x44, 0x0a, 0x56, - 0xdf, 0x58, 0xf1, 0x4c, 0x3c, 0x34, 0xd6, 0x12, 0x52, 0xee, 0xb9, 0xcc, - 0xad, 0xb3, 0x46, 0xa7, 0xa9, 0xa9, 0x55, 0xba, 0xa5, 0xd6, 0xc5, 0xf9, - 0xfb, 0x1d, 0x2b, 0x3d, 0x3a, 0x52, 0x98, 0x59, 0x19, 0x52, 0xf2, 0x3c, - 0xad, 0x1d, 0x78, 0xf9, 0x5b, 0xd6, 0x25, 0xba, 0x92, 0xa9, 0x52, 0xa7, - 0xd7, 0xb3, 0xfb, 0xcc, 0xa1, 0xee, 0x25, 0x13, 0x7b, 0x34, 0x1c, 0x4d, - 0xe8, 0x58, 0xf4, 0x55, 0xb2, 0x44, 0x0e, 0x28, 0xb6, 0x04, 0xa5, 0xe0, - 0xbb, 0xc1, 0x34, 0xad, 0x6c, 0xa6, 0x81, 0xae, 0x2b, 0xc4, 0xba, 0xe3, - 0x06, 0x08, 0xf6, 0x2a, 0xc6, 0x46, 0xd2, 0x56, 0x71, 0x58, 0x61, 0x4b, - 0xc5, 0x31, 0xec, 0x0f, 0x63, 0xeb, 0x53, 0xca, 0x25, 0xb2, 0xea, 0xa6, - 0x7d, 0xaa, 0x43, 0xbc, 0x4e, 0xd9, 0xc2, 0xfc, 0xc5, 0x20, 0x51, 0x3f, - 0x5c, 0x53, 0x8d, 0x59, 0xd9, 0x50, 0xbb, 0x3a, 0xd7, 0x1a, 0x7f, 0xf6, - 0xbd, 0xd3, 0x52, 0xb8, 0xd3, 0xa8, 0xce, 0xa7, 0x72, 0xb5, 0x78, 0xcf, - 0x90, 0xf1, 0x0b, 0x16, 0xe1, 0x36, 0x93, 0x4e, 0x3c, 0x59, 0x0f, 0x55, - 0xbe, 0x42, 0x5d, 0x25, 0xbc, 0x01, 0xdc, 0xdd, 0xa0, 0xbf, 0x1c, 0xac, - 0x85, 0xa6, 0xce, 0xaf, 0x68, 0xc6, 0x95, 0xe6, 0xfc, 0x0a, 0x8f, 0x2d, - 0x8f, 0x48, 0x85, 0x57, 0xe8, 0x57, 0xb9, 0x49, 0x46, 0x2f, 0xf4, 0x0c, - 0x87, 0xe8, 0xef, 0xc7, 0xbc, 0xb0, 0xa2, 0xa6, 0x6e, 0xab, 0x41, 0xbe, - 0x02, 0xdc, 0xc0, 0xff, 0x84, 0x23, 0x68, 0x41, 0x66, 0x54, 0x65, 0x59, - 0x86, 0x4f, 0x70, 0x38, 0xfc, 0x17, 0x8a, 0xf3, 0x28, 0xd1, 0x95, 0xb6, - 0x2e, 0xa8, 0x61, 0xa8, 0x26, 0xb7, 0xff, 0xd1, 0x84, 0xf4, 0xee, 0x18, - 0x31, 0x39, 0xfb, 0x4f, 0x72, 0x59, 0x14, 0x54, 0xb7, 0x40, 0xa0, 0x22, - 0xc2, 0xfe, 0x1d, 0xdb, 0x98, 0xbd, 0x1a, 0xab, 0xba, 0xa6, 0x2d, 0xb1, - 0xb9, 0xc8, 0x76, 0xe9, 0xf0, 0x0d, 0x19, 0x30, 0x47, 0x4a, 0x18, 0x58, - 0x4e, 0x57, 0xf8, 0x47, 0xb8, 0x2c, 0x00, 0x0a, 0xa6, 0xe5, 0xa8, 0xc5, - 0x5f, 0xaf, 0x78, 0xa6, 0x77, 0xac, 0x4e, 0xc0, 0xc7, 0xde, 0xb7, 0x02, - 0x41, 0x26, 0x65, 0x43, 0x5c, 0x55, 0x23, 0x59, 0x1c, 0x4e, 0x16, 0x36, - 0x1a, 0x15, 0x95, 0xf0, 0xa7, 0xce, 0xe7, 0xb4, 0xa4, 0xa7, 0x0e, 0xa9, - 0xeb, 0xb8, 0x96, 0xd4, 0x7b, 0xf7, 0xc7, 0x1b, 0x77, 0x3b, 0x47, 0x51, - 0x90, 0x59, 0x01, 0x53, 0x9e, 0x3e, 0xda, 0x1f, 0xc8, 0xfb, 0x6a, 0xd8, - 0xa0, 0xbb, 0x32, 0xaa, 0x08, 0xa7, 0xa3, 0xb2, 0x1a, 0xcb, 0x5e, 0xec, - 0xdd, 0x10, 0x9b, 0x32, 0xe2, 0x4b, 0x9d, 0x58, 0x8f, 0x56, 0x2d, 0x46, - 0x18, 0x2a, 0x09, 0x07, 0xd0, 0xe2, 0x6c, 0xc3, 0x1e, 0xae, 0x66, 0xa6, - 0x95, 0xad, 0x74, 0xc2, 0x8c, 0xe1, 0xb7, 0x05, 0xea, 0x28, 0x54, 0x45, - 0x3a, 0x56, 0xc7, 0x58, 0x9b, 0x4c, 0xae, 0x33, 0x2f, 0x12, 0xaa, 0xed, - 0x2e, 0xcc, 0x54, 0xb3, 0x2e, 0xa7, 0xd6, 0xa9, 0xc3, 0xba, 0x3b, 0xd7, - 0x73, 0xfa, 0x99, 0x1e, 0xaa, 0x3d, 0x7c, 0x52, 0x98, 0x59, 0xd3, 0x51, - 0x75, 0x3c, 0x0a, 0x1d, 0xcf, 0xf8, 0xc1, 0xd5, 0xbe, 0xb9, 0x62, 0xa9, - 0x6e, 0xa7, 0x2f, 0xb4, 0x8c, 0xcd, 0x45, 0xef, 0xcf, 0x13, 0x04, 0x35, - 0x73, 0x4d, 0xfc, 0x58, 0xc4, 0x55, 0x43, 0x44, 0x74, 0x27, 0x0c, 0x04, - 0x04, 0xe0, 0x3f, 0xc1, 0xf8, 0xac, 0x68, 0xa6, 0xd1, 0xae, 0xa3, 0xc4, - 0x64, 0xe4, 0xab, 0x08, 0x8f, 0x2b, 0x2c, 0x47, 0xfe, 0x56, 0x54, 0x58, - 0x04, 0x4b, 0x37, 0x31, 0x41, 0x0f, 0xc0, 0xea, 0xc8, 0xc9, 0xd1, 0xb1, - 0xd9, 0xa6, 0xb0, 0xaa, 0xb4, 0xbc, 0xe9, 0xd9, 0x6b, 0xfd, 0x67, 0x21, - 0xc6, 0x3f, 0xa0, 0x53, 0x7f, 0x59, 0x95, 0x50, 0x35, 0x3a, 0x35, 0x1a, - 0xd7, 0xf5, 0x24, 0xd3, 0xf0, 0xb7, 0xa9, 0xa8, 0xef, 0xa7, 0xd1, 0xb5, - 0x08, 0xd0, 0x39, 0xf2, 0xb1, 0x16, 0x68, 0x37, 0xe5, 0x4e, 0x4c, 0x59, - 0xd6, 0x54, 0x4e, 0x42, 0xbe, 0x24, 0x13, 0x01, 0x3e, 0xdd, 0x28, 0xbf, - 0xe2, 0xab, 0x8d, 0xa6, 0x1b, 0xb0, 0xeb, 0xc6, 0x3b, 0xe7, 0xa5, 0x0b, - 0x22, 0x2e, 0xf4, 0x48, 0xa6, 0x57, 0xca, 0x57, 0x56, 0x49, 0xb2, 0x2e, - 0x4f, 0x0c, 0xdd, 0xe7, 0x6f, 0xc7, 0x69, 0xb0, 0x96, 0xa6, 0xa9, 0xab, - 0xb4, 0xbe, 0xa1, 0xdc, 0x69, 0x00, 0x24, 0x24, 0xd9, 0x41, 0xa1, 0x54, - 0x59, 0x59, 0x35, 0x4f, 0xec, 0x37, 0x56, 0x17, 0xe0, 0xf2, 0x98, 0xd0, - 0x33, 0xb6, 0x0b, 0xa8, 0x88, 0xa8, 0x86, 0xb7, 0x97, 0xd2, 0x2a, 0xf5, - 0x95, 0x19, 0xb4, 0x39, 0x47, 0x50, 0x7b, 0x59, 0xd8, 0x53, 0x41, 0x40, - 0x01, 0x22, 0x18, 0xfe, 0x81, 0xda, 0x24, 0xbd, 0xe6, 0xaa, 0xc7, 0xa6, - 0x81, 0xb1, 0x40, 0xc9, 0x1a, 0xea, 0x9c, 0x0e, 0xa6, 0x30, 0xaa, 0x4a, - 0x33, 0x58, 0x2a, 0x57, 0x8f, 0x47, 0x26, 0x2c, 0x52, 0x09, 0x07, 0xe5, - 0x23, 0xc5, 0x16, 0xaf, 0x74, 0xa6, 0xb1, 0xac, 0xcd, 0xc0, 0x60, 0xdf, - 0x67, 0x03, 0xd9, 0x26, 0xd6, 0x43, 0x91, 0x55, 0x10, 0x59, 0xc6, 0x4d, - 0x90, 0x35, 0x70, 0x14, 0xf1, 0xef, 0x15, 0xce, 0x8c, 0xb4, 0x87, 0xa7, - 0x37, 0xa9, 0x56, 0xb9, 0x2b, 0xd5, 0x27, 0xf8, 0x68, 0x1c, 0xf7, 0x3b, - 0x8d, 0x51, 0x97, 0x59, 0xbc, 0x52, 0x27, 0x3e, 0x36, 0x1f, 0x1f, 0xfb, - 0xcf, 0xd7, 0x33, 0xbb, 0x02, 0xaa, 0x1b, 0xa7, 0xfc, 0xb2, 0xa3, 0xcb, - 0x04, 0xed, 0x88, 0x11, 0x25, 0x33, 0x41, 0x4c, 0xb0, 0x58, 0x67, 0x56, - 0xbe, 0x45, 0x83, 0x29, 0x5f, 0x06, 0x2c, 0xe2, 0xf0, 0xc2, 0xd7, 0xad, - 0x67, 0xa6, 0xd9, 0xad, 0xf0, 0xc2, 0x2e, 0xe2, 0x61, 0x06, 0x82, 0x29, - 0xc1, 0x45, 0x67, 0x56, 0xb0, 0x58, 0x41, 0x4c, 0x22, 0x33, 0x88, 0x11, - 0x02, 0xed, 0xa3, 0xcb, 0xfa, 0xb2, 0x1b, 0xa7, 0x02, 0xaa, 0x34, 0xbb, - 0xd2, 0xd7, 0x1d, 0xfb, 0x3c, 0x1f, 0x24, 0x3e, 0xbf, 0x52, 0x95, 0x59, - 0x8e, 0x51, 0xf4, 0x3b, 0x6a, 0x1c, 0x23, 0xf8, 0x2b, 0xd5, 0x53, 0xb9, - 0x38, 0xa9, 0x87, 0xa7, 0x8d, 0xb4, 0x17, 0xce, 0xf1, 0xef, 0x72, 0x14, - 0x91, 0x35, 0xc6, 0x4d, 0x12, 0x59, 0x8f, 0x55, 0xd6, 0x43, 0xd7, 0x26, - 0x64, 0x03, 0x61, 0xdf, 0xcb, 0xc0, 0xb0, 0xac, 0x76, 0xa6, 0x12, 0xaf, - 0x2a, 0xc5, 0x03, 0xe5, 0x58, 0x09, 0x22, 0x2c, 0x96, 0x47, 0x24, 0x57, - 0x38, 0x58, 0xa6, 0x4a, 0xa6, 0x30, 0x9a, 0x0e, 0x18, 0xea, 0x40, 0xc9, - 0x7f, 0xb1, 0xc9, 0xa6, 0xe4, 0xaa, 0x27, 0xbd, 0x81, 0xda, 0x1a, 0xfe, - 0x03, 0x22, 0x41, 0x40, 0xd9, 0x53, 0x7b, 0x59, 0x46, 0x50, 0xb5, 0x39, - 0x91, 0x19, 0x2a, 0xf5, 0x95, 0xd2, 0x85, 0xb7, 0x89, 0xa8, 0x0b, 0xa8, - 0x34, 0xb6, 0x97, 0xd0, 0xe5, 0xf2, 0x54, 0x17, 0xf0, 0x37, 0x34, 0x4f, - 0x5a, 0x59, 0x9f, 0x54, 0xdb, 0x41, 0x20, 0x24, 0x6a, 0x00, 0x9c, 0xdc, - 0xb8, 0xbe, 0xa2, 0xab, 0x9e, 0xa6, 0x63, 0xb0, 0x75, 0xc7, 0xda, 0xe7, - 0x54, 0x0c, 0xb0, 0x2e, 0x5b, 0x49, 0xc5, 0x57, 0xaa, 0x57, 0xf0, 0x48, - 0x23, 0x2e, 0xa3, 0x0b, 0x39, 0xe7, 0xea, 0xc6, 0x1a, 0xb0, 0x8e, 0xa6, - 0xe0, 0xab, 0x2d, 0xbf, 0x3c, 0xdd, 0x16, 0x01, 0xbe, 0x24, 0x50, 0x42, - 0xd6, 0x54, 0x4e, 0x59, 0xe1, 0x4e, 0x6a, 0x37, 0xac, 0x16, 0x3b, 0xf2, - 0x05, 0xd0, 0xd1, 0xb5, 0xef, 0xa7, 0xa8, 0xa8, 0xf2, 0xb7, 0x24, 0xd3, - 0xdb, 0xf5, 0x32, 0x1a, 0x3b, 0x3a, 0x91, 0x50, 0x83, 0x59, 0x9c, 0x53, - 0xc9, 0x3f, 0x60, 0x21, 0x70, 0xfd, 0xe2, 0xd9, 0xb8, 0xbc, 0xab, 0xaa, - 0xdd, 0xa6, 0xcf, 0xb1, 0xcb, 0xc9, 0xc1, 0xea, 0x41, 0x0f, 0x3b, 0x31, - 0x02, 0x4b, 0x56, 0x58, 0xfc, 0x56, 0x2d, 0x47, 0x8b, 0x2b, 0xae, 0x08, - 0x5d, 0xe4, 0xa8, 0xc4, 0xca, 0xae, 0x6f, 0xa6, 0xf1, 0xac, 0x46, 0xc1, - 0x02, 0xe0, 0x10, 0x04, 0x74, 0x27, 0x43, 0x44, 0xc5, 0x55, 0xfd, 0x58, - 0x70, 0x4d, 0x06, 0x35, 0xca, 0x13, 0x46, 0xef, 0x8a, 0xcd, 0x2e, 0xb4, - 0x6e, 0xa7, 0x63, 0xa9, 0xbd, 0xb9, 0xc5, 0xd5, 0xcd, 0xf8, 0x0e, 0x1d, - 0x75, 0x3c, 0xd2, 0x51, 0x9d, 0x59, 0x75, 0x52, 0xaf, 0x3d, 0x91, 0x1e, - 0x79, 0xfa, 0x33, 0xd7, 0xc8, 0xba, 0xd1, 0xa9, 0x31, 0xa7, 0x54, 0xb3, - 0x2f, 0xcc, 0xac, 0xed, 0x2f, 0x12, 0xb1, 0x33, 0x99, 0x4c, 0xcc, 0x58, - 0x34, 0x56, 0x57, 0x45, 0xe7, 0x28, 0xb5, 0x05, 0x8d, 0xe1, 0x71, 0xc2, - 0x94, 0xad, 0x68, 0xa6, 0x1c, 0xae, 0x71, 0xc3, 0xcd, 0xe2, 0x0d, 0x07, - 0x19, 0x2a, 0x2d, 0x46, 0x91, 0x56, 0x9b, 0x58, 0xe2, 0x4b, 0x9a, 0x32, - 0xdb, 0x10, 0x5e, 0xec, 0x16, 0xcb, 0xa6, 0xb2, 0x04, 0xa7, 0x35, 0xaa, - 0x9f, 0xbb, 0x6d, 0xd8, 0xc9, 0xfb, 0xdb, 0x1f, 0x9f, 0x3e, 0x01, 0x53, - 0x91, 0x59, 0x46, 0x51, 0x77, 0x3b, 0xc2, 0x1b, 0x7e, 0xf7, 0x91, 0xd4, - 0xed, 0xb8, 0x0d, 0xa9, 0xa2, 0xa7, 0xeb, 0xb4, 0xa5, 0xce, 0x9a, 0xf0, - 0x19, 0x15, 0x18, 0x36, 0x1c, 0x4e, 0x25, 0x59, 0x59, 0x55, 0x69, 0x43, - 0x38, 0x26, 0xbe, 0x02, 0xbe, 0xde, 0x54, 0xc0, 0x70, 0xac, 0x7d, 0xa6, - 0x5d, 0xaf, 0xab, 0xc5, 0xa6, 0xe5, 0x02, 0x0a, 0xb9, 0x2c, 0xfa, 0x47, - 0x4d, 0x57, 0x19, 0x58, 0x45, 0x4a, 0x19, 0x30, 0xed, 0x0d, 0x77, 0xe9, - 0xb5, 0xc8, 0x30, 0xb1, 0xb5, 0xa6, 0x1f, 0xab, 0x96, 0xbd, 0x1f, 0xdb, - 0xc5, 0xfe, 0x9f, 0x22, 0xbb, 0x40, 0x12, 0x54, 0x73, 0x59, 0xfa, 0x4f, - 0x2f, 0x39, 0xef, 0x18, 0x7f, 0xf4, 0x02, 0xd2, 0x22, 0xb7, 0x62, 0xa8, - 0x2e, 0xa8, 0x94, 0xb6, 0x2c, 0xd1, 0x8b, 0xf3, 0xfc, 0x17, 0x73, 0x38, - 0x85, 0x4f, 0x66, 0x59, 0x66, 0x54, 0x66, 0x41, 0x84, 0x23, 0xbd, 0xff, - 0x02, 0xdc, 0x3f, 0xbe, 0x6d, 0xab, 0xa6, 0xa6, 0xb5, 0xb0, 0xf9, 0xc7, - 0x80, 0xe8, 0xfd, 0x0c, 0x42, 0x2f, 0xbb, 0x49, 0xea, 0x57, 0x83, 0x57, - 0x90, 0x48, 0x8b, 0x2d, 0xfc, 0x0a, 0x93, 0xe6, 0x68, 0xc6, 0xcd, 0xaf, - 0x84, 0xa6, 0x1c, 0xac, 0xa3, 0xbf, 0xdb, 0xdd, 0xc1, 0x01, 0x5b, 0x25, - 0xc0, 0x42, 0x11, 0x55, 0x39, 0x59, 0x96, 0x4e, 0xdc, 0x36, 0x0c, 0x16, - 0x8e, 0xf1, 0x77, 0xcf, 0x71, 0xb5, 0xcd, 0xa7, 0xd4, 0xa8, 0x53, 0xb8, - 0xbf, 0xd3, 0x7f, 0xf6, 0xda, 0x1a, 0xbb, 0x3a, 0xda, 0x50, 0x8e, 0x59, - 0x58, 0x53, 0x55, 0x3f, 0xbf, 0x20, 0xc4, 0xfc, 0x4a, 0xd9, 0x43, 0xbc, - 0x7d, 0xaa, 0xe9, 0xa6, 0x27, 0xb2, 0x52, 0xca, 0x67, 0xeb, 0xed, 0x0f, - 0xc6, 0x31, 0x60, 0x4b, 0x73, 0x58, 0xd0, 0x56, 0xc8, 0x46, 0xf2, 0x2a, - 0x06, 0x08, 0xb8, 0xe3, 0x2a, 0xc4, 0x80, 0xae, 0x6d, 0xa6, 0x32, 0xad, - 0xc0, 0xc1, 0xa3, 0xe0, 0xba, 0x04, 0x0d, 0x28, 0xb4, 0x44, 0xf3, 0x55, - 0xeb, 0x58, 0x18, 0x4d, 0x7c, 0x34, 0x22, 0x13, 0x9f, 0xee, 0xfc, 0xcc, - 0xd4, 0xb3, 0x55, 0xa7, 0x8d, 0xa9, 0x2b, 0xba, 0x59, 0xd6, 0x7c, 0xf9, - 0xac, 0x1d, 0xf4, 0x3c, 0x19, 0x52, 0x99, 0x59, 0x38, 0x52, 0x2c, 0x3d, - 0xf7, 0x1d, 0xc6, 0xf9, 0xa1, 0xd6, 0x58, 0xba, 0xa5, 0xa9, 0x49, 0xa7, - 0xaa, 0xb3, 0xbe, 0xcc, 0x52, 0xee, 0xd8, 0x12, 0x3c, 0x34, 0xf2, 0x4c, - 0xdf, 0x58, 0x0b, 0x56, 0xe3, 0x44, 0x55, 0x28, 0x06, 0x05, 0xec, 0xe0, - 0xf6, 0xc1, 0x52, 0xad, 0x68, 0xa6, 0x65, 0xae, 0xeb, 0xc3, 0x73, 0xe3, - 0xb6, 0x07, 0xb1, 0x2a, 0x95, 0x46, 0xbf, 0x56, 0x7d, 0x58, 0x8c, 0x4b, - 0x06, 0x32, 0x39, 0x10, 0xb1, 0xeb, 0x92, 0xca, 0x4b, 0xb2, 0xf5, 0xa6, - 0x62, 0xaa, 0x13, 0xbc, 0x03, 0xd9, 0x77, 0xfc, 0x79, 0x20, 0x1a, 0x3f, - 0x40, 0x53, 0x8d, 0x59, 0xfd, 0x50, 0xf5, 0x3a, 0x23, 0x1b, 0xce, 0xf6, - 0x01, 0xd4, 0x82, 0xb8, 0xe4, 0xa8, 0xc2, 0xa7, 0x45, 0xb5, 0x37, 0xcf, - 0x41, 0xf1, 0xc0, 0x15, 0xa1, 0x36, 0x6f, 0x4e, 0x33, 0x59, 0x28, 0x55, - 0xf3, 0x42, 0xa4, 0x25, 0x0b, 0x02, 0x26, 0xde, 0xd5, 0xbf, 0x3a, 0xac, - 0x7f, 0xa6, 0xac, 0xaf, 0x2a, 0xc6, 0x4c, 0xe6, 0xac, 0x0a, 0x4c, 0x2d, - 0x60, 0x48, 0x73, 0x57, 0xf8, 0x57, 0xe7, 0x49, 0x86, 0x2f, 0x46, 0x0d, - 0xce, 0xe8, 0x32, 0xc8, 0xdc, 0xb0, 0xab, 0xa6, 0x53, 0xab, 0x0c, 0xbe, - 0xba, 0xdb, 0x71, 0xff, 0x3b, 0x23, 0x33, 0x41, 0x4b, 0x54, 0x6c, 0x59, - 0xa7, 0x4f, 0xaf, 0x38, 0x47, 0x18, 0xd8, 0xf3, 0x6e, 0xd1, 0xbf, 0xb6, - 0x3e, 0xa8, 0x53, 0xa8, 0xf4, 0xb6, 0xc0, 0xd1, 0x33, 0xf4, 0xa2, 0x18, - 0xf8, 0x38, 0xd2, 0x4f, 0x73, 0x59, 0x29, 0x54, 0xf3, 0x40, 0xe4, 0x22, - 0x14, 0xff, 0x64, 0xdb, 0xcd, 0xbd, 0x33, 0xab, 0xb4, 0xa6, 0x07, 0xb1, - 0x7c, 0xc8, 0x29, 0xe9, 0xa2, 0x0d, 0xd8, 0x2f, 0x19, 0x4a, 0x0b, 0x58, - 0x5e, 0x57, 0x28, 0x48, 0xfc, 0x2c, 0x4f, 0x0a, 0xf0, 0xe5, 0xe6, 0xc5, - 0x7d, 0xaf, 0x81, 0xa6, 0x55, 0xac, 0x1d, 0xc0, 0x79, 0xde, 0x6a, 0x02, - 0xf9, 0x25, 0x30, 0x43, 0x47, 0x55, 0x29, 0x59, 0x42, 0x4e, 0x56, 0x36, - 0x66, 0x15, 0xe3, 0xf0, 0xe9, 0xce, 0x12, 0xb5, 0xb0, 0xa7, 0xfd, 0xa8, - 0xb9, 0xb8, 0x53, 0xd4, 0x2b, 0xf7, 0x7d, 0x1b, 0x3b, 0x3b, 0x26, 0x51, - 0x8f, 0x59, 0x1e, 0x53, 0xd5, 0x3e, 0x26, 0x20, 0x14, 0xfc, 0xb3, 0xd8, - 0xd2, 0xbb, 0x4a, 0xaa, 0xfd, 0xa6, 0x7d, 0xb2, 0xd9, 0xca, 0x11, 0xec, - 0x91, 0x10, 0x58, 0x32, 0xba, 0x4b, 0x8f, 0x58, 0xa4, 0x56, 0x5f, 0x46, - 0x5b, 0x2a, 0x5a, 0x07, 0x19, 0xe3, 0xa7, 0xc3, 0x3d, 0xae, 0x69, 0xa6, - 0x73, 0xad, 0x3d, 0xc2, 0x42, 0xe1, 0x66, 0x05, 0xa6, 0x28, 0x21, 0x45, - 0x24, 0x56, 0xd3, 0x58, 0xc2, 0x4c, 0xef, 0x33, 0x7d, 0x12, 0xf5, 0xed, - 0x73, 0xcc, 0x75, 0xb3, 0x42, 0xa7, 0xb8, 0xa9, 0x98, 0xba, 0xf1, 0xd6, - 0x25, 0xfa, 0x4f, 0x1e, 0x72, 0x3d, 0x5b, 0x52, 0x9a, 0x59, 0xf3, 0x51, - 0xad, 0x3c, 0x58, 0x1d, 0x19, 0xf9, 0x0c, 0xd6, 0xea, 0xb9, 0x7a, 0xa9, - 0x5f, 0xa7, 0x08, 0xb4, 0x48, 0xcd, 0xfb, 0xee, 0x7f, 0x13, 0xc6, 0x34, - 0x4b, 0x4d, 0xf4, 0x58, 0xd8, 0x55, 0x78, 0x44, 0xb9, 0x27, 0x5d, 0x04, - 0x4c, 0xe0, 0x7a, 0xc1, 0x11, 0xad, 0x6c, 0xa6, 0xaa, 0xae, 0x6e, 0xc4, - 0x12, 0xe4, 0x64, 0x08, 0x44, 0x2b, 0x01, 0x47, 0xe6, 0x56, 0x65, 0x58, - 0x2c, 0x4b, 0x7b, 0x31, 0x8d, 0x0f, 0x0e, 0xeb, 0x06, 0xca, 0xf8, 0xb1, - 0xe1, 0xa6, 0x98, 0xaa, 0x81, 0xbc, 0x9f, 0xd9, 0x21, 0xfd, 0x18, 0x21, - 0x95, 0x3f, 0x7d, 0x53, 0x88, 0x59, 0xb2, 0x50, 0x75, 0x3a, 0x7e, 0x1a, - 0x25, 0xf6, 0x6b, 0xd3, 0x1c, 0xb8, 0xbc, 0xa8, 0xe1, 0xa7, 0xa2, 0xb5, - 0xca, 0xcf, 0xe8, 0xf1, 0x67, 0x16, 0x27, 0x37, 0xc2, 0x4e, 0x43, 0x59, - 0xf2, 0x54, 0x82, 0x42, 0x04, 0x25, 0x65, 0x01, 0x83, 0xdd, 0x63, 0xbf, - 0xfa, 0xab, 0x8a, 0xa6, 0xf7, 0xaf, 0xaf, 0xc6, 0xee, 0xe6, 0x58, 0x0b, - 0xde, 0x2d, 0xc5, 0x48, 0x98, 0x57, 0xd6, 0x57, 0x87, 0x49, 0xf2, 0x2e, - 0x9f, 0x0c, 0x28, 0xe8, 0xad, 0xc7, 0x8c, 0xb0, 0x9e, 0xa6, 0x8c, 0xab, - 0x7f, 0xbe, 0x59, 0xdc, 0x1b, 0x00, 0xda, 0x23, 0xa6, 0x41, 0x84, 0x54, - 0x61, 0x59, 0x59, 0x4f, 0x2a, 0x38, 0xa2, 0x17, 0x2d, 0xf3, 0xdd, 0xd0, - 0x5d, 0xb6, 0x1d, 0xa8, 0x75, 0xa8, 0x5a, 0xb7, 0x51, 0xd2, 0xde, 0xf4, - 0x46, 0x19, 0x7c, 0x39, 0x20, 0x50, 0x7b, 0x59, 0xf0, 0x53, 0x7b, 0x40, - 0x47, 0x22, 0x69, 0xfe, 0xc6, 0xda, 0x5c, 0xbd, 0xfc, 0xaa, 0xc3, 0xa6, - 0x56, 0xb1, 0x05, 0xc9, 0xce, 0xe9, 0x4c, 0x0e, 0x68, 0x30, 0x77, 0x4a, - 0x2d, 0x58, 0x34, 0x57, 0xc7, 0x47, 0x62, 0x2c, 0xa9, 0x09, 0x4b, 0xe5, - 0x62, 0xc5, 0x38, 0xaf, 0x74, 0xa6, 0x97, 0xac, 0x93, 0xc0, 0x19, 0xdf, - 0x16, 0x03, 0x92, 0x26, 0xa3, 0x43, 0x78, 0x55, 0x1b, 0x59, 0xeb, 0x4d, - 0xd0, 0x35, 0xbd, 0x14, 0x3d, 0xf0, 0x58, 0xce, 0xb6, 0xb4, 0x94, 0xa7, - 0x25, 0xa9, 0x23, 0xb9, 0xe8, 0xd4, 0xd6, 0xf7, 0x1f, 0x1c, 0xbc, 0x3b, - 0x6d, 0x51, 0x94, 0x59, 0xdc, 0x52, 0x5c, 0x3e, 0x84, 0x1f, 0x6b, 0xfb, - 0x18, 0xd8, 0x64, 0xbb, 0x18, 0xaa, 0x12, 0xa7, 0xd3, 0xb2, 0x64, 0xcb, - 0xb7, 0xec, 0x3b, 0x11, 0xe3, 0x32, 0x19, 0x4c, 0xa4, 0x58, 0x7c, 0x56, - 0xef, 0x45, 0xcb, 0x29, 0xaa, 0x06, 0x7a, 0xe2, 0x27, 0xc3, 0xf9, 0xad, - 0x65, 0xa6, 0xbc, 0xad, 0xb3, 0xc2, 0xe7, 0xe1, 0x10, 0x06, 0x3f, 0x29, - 0x8d, 0x45, 0x52, 0x56, 0xbc, 0x58, 0x69, 0x4c, 0x65, 0x33, 0xd3, 0x11, - 0x50, 0xed, 0xe3, 0xcb, 0x22, 0xb3, 0x25, 0xa7, 0xed, 0xa9, 0x00, 0xbb, - 0x8d, 0xd7, 0xce, 0xfa, 0xf1, 0x1e, 0xeb, 0x3d, 0xa3, 0x52, 0x93, 0x59, - 0xb2, 0x51, 0x2c, 0x3c, 0xb5, 0x1c, 0x72, 0xf8, 0x70, 0xd5, 0x84, 0xb9, - 0x4c, 0xa9, 0x7a, 0xa7, 0x63, 0xb4, 0xd5, 0xcd, 0xa3, 0xef, 0x27, 0x14, - 0x50, 0x35, 0xa0, 0x4d, 0x08, 0x59, 0xa7, 0x55, 0x09, 0x44, 0x1f, 0x27, - 0xb1, 0x03, 0xad, 0xdf, 0xff, 0xc0, 0xd3, 0xac, 0x6d, 0xa6, 0xf6, 0xae, - 0xea, 0xc4, 0xbb, 0xe4, 0x07, 0x09, 0xe1, 0x2b, 0x62, 0x47, 0x16, 0x57, - 0x42, 0x58, 0xd3, 0x4a, 0xe8, 0x30, 0xe7, 0x0e, 0x67, 0xea, 0x7d, 0xc9, - 0xa5, 0xb1, 0xd0, 0xa6, 0xcc, 0xaa, 0xf3, 0xbc, 0x39, 0xda, 0xcc, 0xfd, - 0xb9, 0x21, 0x0a, 0x40, 0xbd, 0x53, 0x7f, 0x59, 0x69, 0x50, 0xf1, 0x39, - 0xdc, 0x19, 0x79, 0xf5, 0xd8, 0xd2, 0xb5, 0xb7, 0x99, 0xa8, 0xfb, 0xa7, - 0x0a, 0xb6, 0x52, 0xd0, 0x97, 0xf2, 0x0a, 0x17, 0xaf, 0x37, 0x13, 0x4f, - 0x50, 0x59, 0xbb, 0x54, 0x0f, 0x42, 0x69, 0x24, 0xb8, 0x00, 0xe6, 0xdc, - 0xec, 0xbe, 0xc0, 0xab, 0x94, 0xa6, 0x43, 0xb0, 0x35, 0xc7, 0x92, 0xe7, - 0x02, 0x0c, 0x71, 0x2e, 0x28, 0x49, 0xba, 0x57, 0xb8, 0x57, 0x1f, 0x49, - 0x67, 0x2e, 0xee, 0x0b, 0x88, 0xe7, 0x25, 0xc7, 0x40, 0xb0, 0x92, 0xa6, - 0xc5, 0xab, 0xf5, 0xbe, 0xf6, 0xdc, 0xc5, 0x00, 0x79, 0x24, 0x18, 0x42, - 0xbf, 0x54, 0x51, 0x59, 0x0a, 0x4f, 0xa4, 0x37, 0xfd, 0x16, 0x84, 0xf2, - 0x4b, 0xd0, 0xfb, 0xb5, 0xfe, 0xa7, 0x98, 0xa8, 0xc1, 0xb7, 0xe3, 0xd2, - 0x88, 0xf5, 0xec, 0x19, 0xfa, 0x39, 0x71, 0x50, 0x80, 0x59, 0xb6, 0x53, - 0x03, 0x40, 0xa8, 0x21, 0xbd, 0xfd, 0x2d, 0xda, 0xe8, 0xbc, 0xc8, 0xaa, - 0xd1, 0xa6, 0xab, 0xb1, 0x8c, 0xc9, 0x74, 0xea, 0xf4, 0x0e, 0xf8, 0x30, - 0xd7, 0x4a, 0x4a, 0x58, 0x0d, 0x57, 0x5e, 0x47, 0xcf, 0x2b, 0xfd, 0x08, - 0xa9, 0xe4, 0xe1, 0xc4, 0xed, 0xae, 0x70, 0xa6, 0xd5, 0xac, 0x0e, 0xc1, - 0xb5, 0xdf, 0xc7, 0x03, 0x26, 0x27, 0x18, 0x44, 0xa8, 0x55, 0x08, 0x59, - 0x98, 0x4d, 0x45, 0x35, 0x16, 0x14, 0x96, 0xef, 0xc8, 0xcd, 0x5b, 0xb4, - 0x78, 0xa7, 0x4f, 0xa9, 0x8d, 0xb9, 0x7e, 0xd5, 0x82, 0xf8, 0xbf, 0x1c, - 0x3d, 0x3c, 0xb3, 0x51, 0x97, 0x59, 0x9b, 0x52, 0xe0, 0x3d, 0xe4, 0x1e, - 0xbf, 0xfa, 0x80, 0xd7, 0xf5, 0xba, 0xe9, 0xa9, 0x27, 0xa7, 0x2a, 0xb3, - 0xf1, 0xcb, 0x5c, 0xed, 0xe4, 0x11, 0x6f, 0x33, 0x72, 0x4c, 0xbe, 0x58, - 0x4e, 0x56, 0x84, 0x45, 0x31, 0x29, 0x01, 0x06, 0xd8, 0xe1, 0xaa, 0xc2, - 0xb6, 0xad, 0x64, 0xa6, 0xfe, 0xad, 0x35, 0xc3, 0x85, 0xe2, 0xbd, 0x06, - 0xd5, 0x29, 0xf9, 0x45, 0x80, 0x56, 0xa3, 0x58, 0x0f, 0x4c, 0xd8, 0x32, - 0x2b, 0x11, 0xaa, 0xec, 0x56, 0xcb, 0xcd, 0xb2, 0x0e, 0xa7, 0x1e, 0xaa, - 0x6d, 0xbb, 0x26, 0xd8, 0x7a, 0xfb, 0x91, 0x1f, 0x68, 0x3e, 0xe1, 0x52, - 0x94, 0x59, 0x68, 0x51, 0xaf, 0x3b, 0x12, 0x1c, 0xc6, 0xf7, 0xdb, 0xd4, - 0x1a, 0xb9, 0x22, 0xa9, 0x96, 0xa7, 0xbd, 0xb4, 0x65, 0xce, 0x4b, 0xf0, - 0xce, 0x14, 0xd9, 0x35, 0xf6, 0x4d, 0x19, 0x59, 0x75, 0x55, 0x9a, 0x43, - 0x81, 0x26, 0x0c, 0x03, 0x07, 0xdf, 0x8c, 0xc0, 0x8d, 0xac, 0x79, 0xa6, - 0x3a, 0xaf, 0x71, 0xc5, 0x59, 0xe5, 0xb6, 0x09, 0x72, 0x2c, 0xcd, 0x47, - 0x39, 0x57, 0x2a, 0x58, 0x6e, 0x4a, 0x5e, 0x30, 0x3a, 0x0e, 0xc2, 0xe9, - 0xf6, 0xc8, 0x53, 0xb1, 0xbd, 0xa6, 0x06, 0xab, 0x60, 0xbd, 0xda, 0xda, - 0x74, 0xfe, 0x56, 0x22, 0x86, 0x40, 0xf3, 0x53, 0x7e, 0x59, 0x17, 0x50, - 0x70, 0x39, 0x37, 0x19, 0xd1, 0xf4, 0x42, 0xd2, 0x53, 0xb7, 0x71, 0xa8, - 0x1e, 0xa8, 0x69, 0xb6, 0xe6, 0xd0, 0x3f, 0xf3, 0xaf, 0x17, 0x36, 0x38, - 0x61, 0x4f, 0x5f, 0x59, 0x83, 0x54, 0x98, 0x41, 0xcf, 0x23, 0x0b, 0x00, - 0x4a, 0xdc, 0x76, 0xbe, 0x86, 0xab, 0xa0, 0xa6, 0x92, 0xb0, 0xb9, 0xc7, - 0x37, 0xe8, 0xac, 0x0c, 0x02, 0x2f, 0x8c, 0x49, 0xdc, 0x57, 0x93, 0x57, - 0xbd, 0x48, 0xd1, 0x2d, 0x49, 0x0b, 0xe0, 0xe6, 0xa3, 0xc6, 0xf1, 0xaf, - 0x88, 0xa6, 0x01, 0xac, 0x6b, 0xbf, 0x94, 0xdd, 0x6f, 0x01, 0x17, 0x25, - 0x89, 0x42, 0xf8, 0x54, 0x41, 0x59, 0xbb, 0x4e, 0x1b, 0x37, 0x59, 0x16, - 0xd9, 0xf1, 0xbd, 0xcf, 0x99, 0xb5, 0xe0, 0xa7, 0xbd, 0xa8, 0x28, 0xb8, - 0x76, 0xd3, 0x35, 0xf6, 0x8c, 0x1a, 0x80, 0x3a, 0xba, 0x50, 0x87, 0x59, - 0x7b, 0x53, 0x85, 0x3f, 0x10, 0x21, 0x0d, 0xfd, 0x94, 0xd9, 0x77, 0xbc, - 0x91, 0xaa, 0xe6, 0xa6, 0xfd, 0xb1, 0x13, 0xca, 0x1c, 0xeb, 0x9b, 0x0f, - 0x89, 0x31, 0x35, 0x4b, 0x64, 0x58, 0xe6, 0x56, 0xf5, 0x46, 0x39, 0x2b, - 0x54, 0x08, 0x03, 0xe4, 0x65, 0xc4, 0xa1, 0xae, 0x6e, 0xa6, 0x14, 0xad, - 0x88, 0xc1, 0x58, 0xe0, 0x6d, 0x04, 0xc5, 0x27, 0x82, 0x44, 0xdd, 0x55, - 0xf2, 0x58, 0x43, 0x4d, 0xba, 0x34, 0x70, 0x13, 0xed, 0xee, 0x3b, 0xcd, - 0xff, 0xb3, 0x5f, 0xa7, 0x7b, 0xa9, 0xf7, 0xb9, 0x16, 0xd6, 0x2a, 0xf9, - 0x65, 0x1d, 0xb8, 0x3c, 0xfb, 0x51, 0x97, 0x59, 0x59, 0x52, 0x63, 0x3d, - 0x44, 0x1e, 0x14, 0xfa, 0xe6, 0xd6, 0x8b, 0xba, 0xb8, 0xa9, 0x40, 0xa7, - 0x81, 0xb3, 0x7c, 0xcc, 0x06, 0xee, 0x89, 0x12, 0xfe, 0x33, 0xc9, 0x4c, - 0xd5, 0x58, 0x20, 0x56, 0x16, 0x45, 0x9b, 0x28, 0x55, 0x05, 0x36, 0xe1, - 0x30, 0xc2, 0x6f, 0xad, 0x68, 0xa6, 0x44, 0xae, 0xb1, 0xc3, 0x2a, 0xe3, - 0x65, 0x07, 0x6d, 0x2a, 0x64, 0x46, 0xab, 0x56, 0x8b, 0x58, 0xb4, 0x4b, - 0x49, 0x32, 0x85, 0x10, 0xff, 0xeb, 0xd1, 0xca, 0x72, 0xb2, 0xfe, 0xa6, - 0x4b, 0xaa, 0xe0, 0xbb, 0xbb, 0xd8, 0x2a, 0xfc, 0x2c, 0x20, 0xe7, 0x3e, - 0x1d, 0x53, 0x95, 0x59, 0x1a, 0x51, 0x33, 0x3b, 0x6e, 0x1b, 0x1a, 0xf7, - 0x49, 0xd4, 0xae, 0xb8, 0xfa, 0xa8, 0xb2, 0xa7, 0x1c, 0xb5, 0xf3, 0xce, - 0xf4, 0xf0, 0x74, 0x15, 0x62, 0x36, 0x49, 0x4e, 0x2c, 0x59, 0x40, 0x55, - 0x27, 0x43, 0xec, 0x25, 0x59, 0x02, 0x6f, 0xde, 0x0e, 0xc0, 0x53, 0xac, - 0x7f, 0xa6, 0x85, 0xaf, 0xf1, 0xc5, 0xff, 0xe5, 0x5e, 0x0a, 0x09, 0x2d, - 0x30, 0x48, 0x63, 0x57, 0x06, 0x58, 0x13, 0x4a, 0xca, 0x2f, 0x93, 0x0d, - 0x1b, 0xe9, 0x70, 0xc8, 0x00, 0xb1, 0xb3, 0xa6, 0x37, 0xab, 0xd8, 0xbd, - 0x72, 0xdb, 0x21, 0xff, 0xf5, 0x22, 0xf9, 0x40, 0x34, 0x54, 0x6d, 0x59, - 0xcf, 0x4f, 0xea, 0x38, 0x93, 0x18, 0x27, 0xf4, 0xaf, 0xd1, 0xf0, 0xb6, - 0x4c, 0xa8, 0x43, 0xa8, 0xc8, 0xb6, 0x7a, 0xd1, 0xe7, 0xf3, 0x56, 0x18, - 0xb9, 0x38, 0xb1, 0x4f, 0x6b, 0x59, 0x47, 0x54, 0x28, 0x41, 0x2c, 0x23, - 0x64, 0xff, 0xaa, 0xdb, 0x05, 0xbe, 0x4a, 0xab, 0xb0, 0xa6, 0xdf, 0xb0, - 0x42, 0xc8, 0xd9, 0xe8, 0x57, 0x0d, 0x93, 0x2f, 0xed, 0x49, 0xff, 0x57, - 0x6c, 0x57, 0x59, 0x48, 0x3e, 0x2d, 0x9e, 0x0a, 0x3d, 0xe6, 0x20, 0xc6, - 0xa3, 0xaf, 0x80, 0xa6, 0x3d, 0xac, 0xe3, 0xbf, 0x31, 0xde, 0x1d, 0x02, - 0xae, 0x25, 0x00, 0x43, 0x2a, 0x55, 0x34, 0x59, 0x66, 0x4e, 0x97, 0x36, - 0xaf, 0x15, 0x35, 0xf1, 0x27, 0xcf, 0x40, 0xb5, 0xbc, 0xa7, 0xeb, 0xa8, - 0x89, 0xb8, 0x0f, 0xd4, 0xdc, 0xf6, 0x32, 0x1b, 0x01, 0x3b, 0x02, 0x51, - 0x8f, 0x59, 0x39, 0x53, 0x10, 0x3f, 0x6c, 0x20, 0x67, 0xfc, 0xf5, 0xd8, - 0x0a, 0xbc, 0x5d, 0xaa, 0xf8, 0xa6, 0x52, 0xb2, 0x9d, 0xca, 0xc1, 0xeb, - 0x46, 0x10, 0x16, 0x32, 0x90, 0x4b, 0x83, 0x58, 0xb8, 0x56, 0x8e, 0x46, - 0xa2, 0x2a, 0xa8, 0x07, 0x64, 0xe3, 0xe1, 0xc3, 0x5e, 0xae, 0x68, 0xa6, - 0x57, 0xad, 0x03, 0xc2, 0xf7, 0xe0, 0x19, 0x05, 0x5f, 0x28, 0xef, 0x44, - 0x0e, 0x56, 0xdc, 0x58, 0xeb, 0x4c, 0x30, 0x34, 0xc9, 0x12, 0x45, 0xee, - 0xad, 0xcc, 0xa6, 0xb3, 0x45, 0xa7, 0xaa, 0xa9, 0x61, 0xba, 0xaf, 0xd6, - 0xd4, 0xf9, 0x06, 0x1e, 0x37, 0x3d, 0x3c, 0x52, 0x9d, 0x59, 0x10, 0x52, - 0xea, 0x3c, 0x9e, 0x1d, 0x6b, 0xf9, 0x50, 0xd6, 0x1c, 0xba, 0x8e, 0xa9, - 0x55, 0xa7, 0xdc, 0xb3, 0x09, 0xcd, 0xad, 0xee, 0x31, 0x13, 0x88, 0x34, - 0x21, 0x4d, 0xeb, 0x58, 0xef, 0x55, 0xac, 0x44, 0xfc, 0x27, 0xaf, 0x04, - 0x92, 0xe0, 0xb5, 0xc1, 0x2f, 0xad, 0x69, 0xa6, 0x8b, 0xae, 0x30, 0xc4, - 0xcb, 0xe3, 0x12, 0x08, 0x01, 0x2b, 0xd0, 0x46, 0xd4, 0x56, 0x70, 0x58, - 0x59, 0x4b, 0xb9, 0x31, 0xdf, 0x0f, 0x57, 0xeb, 0x48, 0xca, 0x1c, 0xb2, - 0xec, 0xa6, 0x7d, 0xaa, 0x51, 0xbc, 0x55, 0xd9, 0xd3, 0xfc, 0xcf, 0x20, - 0x5d, 0x3f, 0x61, 0x53, 0x8a, 0x59, 0xd5, 0x50, 0xaf, 0x3a, 0xcc, 0x1a, - 0x71, 0xf6, 0xb1, 0xd3, 0x49, 0xb8, 0xd1, 0xa8, 0xd0, 0xa7, 0x79, 0xb5, - 0x85, 0xcf, 0x9b, 0xf1, 0x1c, 0x16, 0xe8, 0x36, 0x9d, 0x4e, 0x3a, 0x59, - 0x0d, 0x55, 0xb4, 0x42, 0x51, 0x25, 0xae, 0x01, 0xd0, 0xdd, 0x95, 0xbf, - 0x1a, 0xac, 0x83, 0xa6, 0xd5, 0xaf, 0x71, 0xc6, 0xa5, 0xe6, 0x07, 0x0b, - 0x9c, 0x2d, 0x97, 0x48, 0x86, 0x57, 0xe8, 0x57, 0xb1, 0x49, 0x38, 0x2f, - 0xeb, 0x0c, 0x75, 0xe8, 0xe9, 0xc7, 0xb3, 0xb0, 0xa1, 0xa6, 0x73, 0xab, - 0x4b, 0xbe, 0x0d, 0xdc, 0xcf, 0xff, 0x90, 0x23, 0x70, 0x41, 0x6d, 0x54, - 0x63, 0x59, 0x7e, 0x4f, 0x68, 0x38, 0xee, 0x17, 0x7b, 0xf3, 0x21, 0xd1, - 0x87, 0xb6, 0x2f, 0xa8, 0x64, 0xa8, 0x2b, 0xb7, 0x0f, 0xd2, 0x8d, 0xf4, - 0xfd, 0x18, 0x3d, 0x39, 0xff, 0x4f, 0x74, 0x59, 0x0e, 0x54, 0xaf, 0x40, - 0x92, 0x22, 0xb6, 0xfe, 0x11, 0xdb, 0x8c, 0xbd, 0x19, 0xab, 0xb9, 0xa6, - 0x34, 0xb1, 0xc5, 0xc8, 0x81, 0xe9, 0xfe, 0x0d, 0x27, 0x30, 0x4b, 0x4a, - 0x1f, 0x58, 0x45, 0x57, 0xf5, 0x47, 0xa9, 0x2c, 0xf6, 0x09, 0x96, 0xe5, - 0x9f, 0xc5, 0x58, 0xaf, 0x79, 0xa6, 0x7a, 0xac, 0x5a, 0xc0, 0xd0, 0xde, - 0xc9, 0x02, 0x4a, 0x26, 0x6f, 0x43, 0x60, 0x55, 0x22, 0x59, 0x14, 0x4e, - 0x0e, 0x36, 0x0a, 0x15, 0x8a, 0xf0, 0x9a, 0xce, 0xe0, 0xb4, 0xa2, 0xa7, - 0x11, 0xa9, 0xf3, 0xb8, 0xa3, 0xd4, 0x88, 0xf7, 0xd3, 0x1b, 0x83, 0x3b, - 0x49, 0x51, 0x95, 0x59, 0xf8, 0x52, 0x97, 0x3e, 0xcc, 0x1f, 0xba, 0xfb, - 0x5e, 0xd8, 0x97, 0xbb, 0x2f, 0xaa, 0x09, 0xa7, 0xaa, 0xb2, 0x25, 0xcb, - 0x6a, 0xec, 0xed, 0x10, 0xa2, 0x32, 0xf0, 0x4b, 0x97, 0x58, 0x94, 0x56, - 0x1c, 0x46, 0x12, 0x2a, 0xf9, 0x06, 0xc3, 0xe2, 0x64, 0xc3, 0x15, 0xae, - 0x69, 0xa6, 0x99, 0xad, 0x7d, 0xc2, 0x9b, 0xe1, 0xc2, 0x05, 0xf8, 0x28, - 0x5c, 0x45, 0x3d, 0x56, 0xc6, 0x58, 0x93, 0x4c, 0xa3, 0x33, 0x24, 0x12, - 0x99, 0xed, 0x27, 0xcc, 0x49, 0xb3, 0x2e, 0xa7, 0xda, 0xa9, 0xcb, 0xba, - 0x48, 0xd7, 0x80, 0xfa, 0xa6, 0x1e, 0xb4, 0x3d, 0x80, 0x52, 0x9a, 0x59, - 0xcb, 0x51, 0x6e, 0x3c, 0xf9, 0x1c, 0xc5, 0xf8, 0xb3, 0xd5, 0xb6, 0xb9, - 0x5f, 0xa9, 0x6f, 0xa7, 0x37, 0xb4, 0x96, 0xcd, 0x54, 0xef, 0xdb, 0x13, - 0x10, 0x35, 0x78, 0x4d, 0x01, 0x59, 0xbc, 0x55, 0x3e, 0x44, 0x64, 0x27, - 0x02, 0x04, 0xf4, 0xdf, 0x3a, 0xc1, 0xed, 0xac, 0x6d, 0xa6, 0xd4, 0xae, - 0xb0, 0xc4, 0x6e, 0xe4, 0xbb, 0x08, 0x98, 0x2b, 0x38, 0x47, 0xfe, 0x56, - 0x55, 0x58, 0xf9, 0x4a, 0x2d, 0x31, 0x34, 0x0f, 0xb2, 0xea, 0xbf, 0xc9, - 0xc8, 0xb1, 0xda, 0xa6, 0xb1, 0xaa, 0xc1, 0xbc, 0xf1, 0xd9, 0x7e, 0xfd, - 0x6d, 0x21, 0xd7, 0x3f, 0x9d, 0x53, 0x86, 0x59, 0x89, 0x50, 0x2f, 0x3a, - 0x26, 0x1a, 0xc9, 0xf5, 0x1a, 0xd3, 0xe6, 0xb7, 0xa8, 0xa8, 0xf0, 0xa7, - 0xd9, 0xb5, 0x14, 0xd0, 0x46, 0xf2, 0xbf, 0x16, 0x72, 0x37, 0xeb, 0x4e, - 0x4e, 0x59, 0xd0, 0x54, 0x47, 0x42, 0xb0, 0x24, 0x07, 0x01, 0x2f, 0xdd, - 0x21, 0xbf, 0xdc, 0xab, 0x8f, 0xa6, 0x1f, 0xb0, 0xf9, 0xc6, 0x44, 0xe7, - 0xb7, 0x0b, 0x2a, 0x2e, 0xfe, 0x48, 0xa8, 0x57, 0xc8, 0x57, 0x4c, 0x49, - 0xab, 0x2e, 0x3c, 0x0c, 0xd4, 0xe7, 0x62, 0xc7, 0x63, 0xb0, 0x98, 0xa6, - 0xaa, 0xab, 0xbf, 0xbe, 0xad, 0xdc, 0x77, 0x00, 0x30, 0x24, 0xe3, 0x41, - 0xa5, 0x54, 0x58, 0x59, 0x2e, 0x4f, 0xe3, 0x37, 0x48, 0x17, 0xd2, 0xf2, - 0x8f, 0xd0, 0x27, 0xb6, 0x0c, 0xa8, 0x89, 0xa8, 0x91, 0xb7, 0x9e, 0xd2, - 0x3d, 0xf5, 0x9c, 0x19, 0xc4, 0x39, 0x4a, 0x50, 0x7e, 0x59, 0xd0, 0x53, - 0x3b, 0x40, 0xf1, 0x21, 0x0e, 0xfe, 0x71, 0xda, 0x1f, 0xbd, 0xde, 0xaa, - 0xcb, 0xa6, 0x85, 0xb1, 0x4d, 0xc9, 0x26, 0xea, 0xaa, 0x0e, 0xb1, 0x30, - 0xb1, 0x4a, 0x38, 0x58, 0x22, 0x57, 0x8c, 0x47, 0x14, 0x2c, 0x4d, 0x09, - 0xf1, 0xe4, 0x20, 0xc5, 0x0c, 0xaf, 0x74, 0xa6, 0xb8, 0xac, 0xd4, 0xc0, - 0x6f, 0xdf, 0x73, 0x03, 0xe6, 0x26, 0xde, 0x43, 0x95, 0x55, 0x0f, 0x59, - 0xbf, 0x4d, 0x85, 0x35, 0x64, 0x14, 0xe0, 0xef, 0x0d, 0xce, 0x82, 0xb4, - 0x87, 0xa7, 0x3b, 0xa9, 0x5b, 0xb9, 0x3a, 0xd5, 0x31, 0xf8, 0x78, 0x1c, - 0x01, 0x3c, 0x92, 0x51, 0x96, 0x59, 0xb9, 0x52, 0x1a, 0x3e, 0x2e, 0x1f, - 0x0d, 0xfb, 0xc6, 0xd7, 0x28, 0xbb, 0x00, 0xaa, 0x1c, 0xa7, 0x02, 0xb3, - 0xaf, 0xcb, 0x12, 0xed, 0x94, 0x11, 0x31, 0x33, 0x47, 0x4c, 0xb4, 0x58, - 0x62, 0x56, 0xb7, 0x45, 0x75, 0x29, 0x52, 0x06, 0x20, 0xe2, 0xe5, 0xc2, - 0xd3, 0xad, 0x66, 0xa6, 0xde, 0xad, 0xfa, 0xc2, 0x3b, 0xe2, 0x6f, 0x06, - 0x8e, 0x29, 0xca, 0x45, 0x68, 0x56, 0xb0, 0x58, 0x3a, 0x4c, 0x17, 0x33, - 0x7b, 0x11, 0xf2, 0xec, 0x9b, 0xcb, 0xf1, 0xb2, 0x1c, 0xa7, 0x03, 0xaa, - 0x3f, 0xbb, 0xdc, 0xd7, 0x2d, 0xfb, 0x47, 0x1f, 0x2f, 0x3e, 0xc3, 0x52, - 0x97, 0x59, 0x85, 0x51, 0xee, 0x3b, 0x59, 0x1c, 0x17, 0xf8, 0x20, 0xd5, - 0x49, 0xb9, 0x36, 0xa9, 0x89, 0xa7, 0x93, 0xb4, 0x24, 0xce, 0xfc, 0xef, - 0x82, 0x14, 0x9a, 0x35, 0xce, 0x4d, 0x14, 0x59, 0x88, 0x55, 0xd1, 0x43, - 0xc8, 0x26, 0x57, 0x03, 0x56, 0xdf, 0xbe, 0xc0, 0xae, 0xac, 0x76, 0xa6, - 0x16, 0xaf, 0x37, 0xc5, 0x0c, 0xe5, 0x69, 0x09, 0x2d, 0x2c, 0x9d, 0x47, - 0x28, 0x57, 0x37, 0x58, 0x9b, 0x4a, 0x9e, 0x30, 0x8a, 0x0e, 0x0d, 0xea, - 0x35, 0xc9, 0x78, 0xb1, 0xc7, 0xa6, 0xe8, 0xaa, 0x32, 0xbd, 0x8c, 0xda, - 0x29, 0xfe, 0x0c, 0x22, 0x4e, 0x40, 0xdb, 0x53, 0x7e, 0x59, 0x3c, 0x50, - 0xad, 0x39, 0x82, 0x19, 0x1e, 0xf5, 0x89, 0xd2, 0x7e, 0xb7, 0x83, 0xa8, - 0x11, 0xa8, 0x39, 0xb6, 0xa5, 0xd0, 0xf0, 0xf2, 0x64, 0x17, 0xf7, 0x37, - 0x3f, 0x4f, 0x56, 0x59, 0x9f, 0x54, 0xcd, 0x41, 0x18, 0x24, 0x59, 0x00, - 0x93, 0xdc, 0xab, 0xbe, 0xa1, 0xab, 0x9a, 0xa6, 0x6e, 0xb0, 0x7c, 0xc7, - 0xec, 0xe7, 0x5c, 0x0c, 0xc0, 0x2e, 0x5e, 0x49, 0xce, 0x57, 0xa2, 0x57, - 0xec, 0x48, 0x14, 0x2e, 0x98, 0x0b, 0x2a, 0xe7, 0xe3, 0xc6, 0x10, 0xb0, - 0x90, 0xa6, 0xe4, 0xab, 0x35, 0xbf, 0x4b, 0xdd, 0x22, 0x01, 0xcc, 0x24, - 0x56, 0x42, 0xde, 0x54, 0x49, 0x59, 0xde, 0x4e, 0x5d, 0x37, 0xa1, 0x16, - 0x2b, 0xf2, 0xfb, 0xcf, 0xc9, 0xb5, 0xeb, 0xa7, 0xaf, 0xa8, 0xf6, 0xb7, - 0x32, 0xd3, 0xe8, 0xf5, 0x3e, 0x1a, 0x48, 0x3a, 0x94, 0x50, 0x86, 0x59, - 0x96, 0x53, 0xbe, 0x3f, 0x58, 0x21, 0x5c, 0xfd, 0xdd, 0xd9, 0xa7, 0xbc, - 0xaf, 0xaa, 0xd8, 0xa6, 0xda, 0xb1, 0xd3, 0xc9, 0xce, 0xea, 0x51, 0x0f, - 0x44, 0x31, 0x0a, 0x4b, 0x5a, 0x58, 0xf5, 0x56, 0x29, 0x47, 0x7c, 0x2b, - 0xa1, 0x08, 0x51, 0xe4, 0x9c, 0xc4, 0xc6, 0xae, 0x6d, 0xa6, 0xf8, 0xac, - 0x4f, 0xc1, 0x0d, 0xe0, 0x20, 0x04, 0x7d, 0x27, 0x50, 0x44, 0xc6, 0x55, - 0xfc, 0x58, 0x69, 0x4d, 0xfc, 0x34, 0xbc, 0x13, 0x38, 0xef, 0x81, 0xcd, - 0x23, 0xb4, 0x70, 0xa7, 0x63, 0xa9, 0xc8, 0xb9, 0xcf, 0xd5, 0xdd, 0xf8, - 0x19, 0x1d, 0x7f, 0x3c, 0xda, 0x51, 0x98, 0x59, 0x76, 0x52, 0x9f, 0x3d, - 0x8b, 0x1e, 0x64, 0xfa, 0x2d, 0xd7, 0xba, 0xba, 0xd1, 0xa9, 0x32, 0xa7, - 0x5a, 0xb3, 0x3c, 0xcc, 0xb7, 0xed, 0x3e, 0x12, 0xbc, 0x33, 0xa1, 0x4c, - 0xcb, 0x58, 0x34, 0x56, 0x4b, 0x45, 0xdd, 0x28, 0xa8, 0x05, 0x7d, 0xe1, - 0x6a, 0xc2, 0x8e, 0xad, 0x67, 0xa6, 0x24, 0xae, 0x76, 0xc3, 0xe0, 0xe2, - 0x16, 0x07, 0x28, 0x2a, 0x33, 0x46, 0x96, 0x56, 0x98, 0x58, 0xdc, 0x4b, - 0x8e, 0x32, 0xcd, 0x10, 0x52, 0xec, 0x0b, 0xcb, 0x9d, 0xb2, 0x05, 0xa7, - 0x36, 0xaa, 0xab, 0xbb, 0x77, 0xd8, 0xd8, 0xfb, 0xe5, 0x1f, 0xad, 0x3e, - 0x02, 0x53, 0x93, 0x59, 0x40, 0x51, 0x6b, 0x3b, 0xba, 0x1b, 0x6a, 0xf7, - 0x8b, 0xd4, 0xdf, 0xb8, 0x0f, 0xa9, 0xa1, 0xa7, 0xf4, 0xb4, 0xaf, 0xce, - 0xa7, 0xf0, 0x28, 0x15, 0x22, 0x36, 0x23, 0x4e, 0x25, 0x59, 0x56, 0x55, - 0x5f, 0x43, 0x2e, 0x26, 0xad, 0x02, 0xb4, 0xde, 0x48, 0xc0, 0x6e, 0xac, - 0x7b, 0xa6, 0x65, 0xaf, 0xb3, 0xc5, 0xb5, 0xe5, 0x0f, 0x0a, 0xc3, 0x2c, - 0x05, 0x48, 0x4e, 0x57, 0x17, 0x58, 0x3e, 0x4a, 0x0c, 0x30, 0xe2, 0x0d, - 0x67, 0xe9, 0xad, 0xc8, 0x27, 0xb1, 0xb7, 0xa6, 0x20, 0xab, 0xa2, 0xbd, - 0x2a, 0xdb, 0xd3, 0xfe, 0xac, 0x22, 0xc3, 0x40, 0x19, 0x54, 0x70, 0x59, - 0xf6, 0x4f, 0x22, 0x39, 0xe5, 0x18, 0x6f, 0xf4, 0xf8, 0xd1, 0x18, 0xb7, - 0x60, 0xa8, 0x32, 0xa8, 0x9a, 0xb6, 0x39, 0xd1, 0x96, 0xf3, 0x0c, 0x18, - 0x7b, 0x38, 0x8e, 0x4f, 0x64, 0x59, 0x64, 0x54, 0x5b, 0x41, 0x78, 0x23, - 0xaf, 0xff, 0xf6, 0xdb, 0x36, 0xbe, 0x69, 0xab, 0xa6, 0xa6, 0xbd, 0xb0, - 0x01, 0xc8, 0x90, 0xe8, 0x08, 0x0d, 0x50, 0x2f, 0xc2, 0x49, 0xed, 0x57, - 0x7f, 0x57, 0x88, 0x48, 0x80, 0x2d, 0xef, 0x0a, 0x85, 0xe6, 0x5f, 0xc6, - 0xc4, 0xaf, 0x87, 0xa6, 0x1e, 0xac, 0xae, 0xbf, 0xe7, 0xdd, 0xce, 0x01, - 0x68, 0x25, 0xca, 0x42, 0x12, 0x55, 0x3d, 0x59, 0x89, 0x4e, 0xd8, 0x36, - 0xfb, 0x15, 0x81, 0xf1, 0x6c, 0xcf, 0x69, 0xb5, 0xcc, 0xa7, 0xd6, 0xa8, - 0x5d, 0xb8, 0xc6, 0xd3, 0x92, 0xf6, 0xe3, 0x1a, 0xc8, 0x3a, 0xe0, 0x50, - 0x8b, 0x59, 0x58, 0x53, 0x45, 0x3f, 0xb9, 0x20, 0xb2, 0xfc, 0x41, 0xd9, - 0x37, 0xbc, 0x7a, 0xaa, 0xea, 0xa6, 0x30, 0xb2, 0x5b, 0xca, 0x75, 0xeb, - 0xf9, 0x0f, 0xd1, 0x31, 0x6b, 0x4b, 0x71, 0x58, 0xd1, 0x56, 0xba, 0x46, - 0xeb, 0x2a, 0xf4, 0x07, 0xaf, 0xe3, 0x1d, 0xc4, 0x7c, 0xae, 0x6d, 0xa6, - 0x35, 0xad, 0xcd, 0xc1, 0xab, 0xe0, 0xcd, 0x04, 0x16, 0x28, 0xbe, 0x44, - 0xf7, 0x55, 0xe8, 0x58, 0x12, 0x4d, 0x71, 0x34, 0x16, 0x13, 0x90, 0xee, - 0xf2, 0xcc, 0xcc, 0xb3, 0x53, 0xa7, 0x93, 0xa9, 0x31, 0xba, 0x66, 0xd6, - 0x89, 0xf9, 0xba, 0x1d, 0xfe, 0x3c, 0x1e, 0x52, 0x98, 0x59, 0x34, 0x52, - 0x21, 0x3d, 0xeb, 0x1d, 0xb9, 0xf9, 0x94, 0xd6, 0x4f, 0xba, 0xa3, 0xa9, - 0x48, 0xa7, 0xb6, 0xb3, 0xc5, 0xcc, 0x61, 0xee, 0xe5, 0x12, 0x47, 0x34, - 0xfa, 0x4c, 0xdf, 0x58, 0x08, 0x56, 0xdb, 0x44, 0x47, 0x28, 0xfa, 0x04, - 0xde, 0xe0, 0xee, 0xc1, 0x4b, 0xad, 0x6b, 0xa6, 0x66, 0xae, 0xfb, 0xc3, - 0x7b, 0xe3, 0xc7, 0x07, 0xbb, 0x2a, 0x9e, 0x46, 0xc2, 0x56, 0x7c, 0x58, - 0x83, 0x4b, 0xfd, 0x31, 0x29, 0x10, 0xa6, 0xeb, 0x85, 0xca, 0x46, 0xb2, - 0xf2, 0xa6, 0x68, 0xaa, 0x1a, 0xbc, 0x11, 0xd9, 0x83, 0xfc, 0x85, 0x20, - 0x26, 0x3f, 0x43, 0x53, 0x8f, 0x59, 0xf5, 0x50, 0xeb, 0x3a, 0x17, 0x1b, - 0xbf, 0xf6, 0xf7, 0xd3, 0x78, 0xb8, 0xe2, 0xa8, 0xc4, 0xa7, 0x4d, 0xb5, - 0x41, 0xcf, 0x51, 0xf1, 0xca, 0x15, 0xae, 0x36, 0x74, 0x4e, 0x36, 0x59, - 0x22, 0x55, 0xed, 0x42, 0x92, 0x25, 0x04, 0x02, 0x12, 0xde, 0xd4, 0xbf, - 0x2d, 0xac, 0x86, 0xa6, 0xae, 0xaf, 0x36, 0xc6, 0x59, 0xe6, 0xb9, 0x0a, - 0x58, 0x2d, 0x69, 0x48, 0x74, 0x57, 0xf7, 0x57, 0xde, 0x49, 0x7b, 0x2f, - 0x39, 0x0d, 0xc1, 0xe8, 0x27, 0xc8, 0xd5, 0xb0, 0xac, 0xa6, 0x55, 0xab, - 0x16, 0xbe, 0xc8, 0xdb, 0x7a, 0xff, 0x4f, 0x23, 0x35, 0x41, 0x55, 0x54, - 0x67, 0x59, 0xa3, 0x4f, 0xa5, 0x38, 0x38, 0x18, 0xcd, 0xf3, 0x60, 0xd1, - 0xb9, 0xb6, 0x3a, 0xa8, 0x57, 0xa8, 0xfb, 0xb6, 0xcc, 0xd1, 0x40, 0xf4, - 0xb0, 0x18, 0x01, 0x39, 0xdc, 0x4f, 0x6e, 0x59, 0x2b, 0x54, 0xe4, 0x40, - 0xdb, 0x22, 0x06, 0xff, 0x56, 0xdb, 0xc6, 0xbd, 0x2d, 0xab, 0xb6, 0xa6, - 0x0d, 0xb1, 0x87, 0xc8, 0x36, 0xe9, 0xb0, 0x0d, 0xe2, 0x2f, 0x22, 0x4a, - 0x0d, 0x58, 0x5b, 0x57, 0x21, 0x48, 0xef, 0x2c, 0x41, 0x0a, 0xe5, 0xe5, - 0xd8, 0xc5, 0x7d, 0xaf, 0x7b, 0xa6, 0x5d, 0xac, 0x24, 0xc0, 0x87, 0xde, - 0x79, 0x02, 0x03, 0x26, 0x3b, 0x43, 0x48, 0x55, 0x2b, 0x59, 0x39, 0x4e, - 0x4d, 0x36, 0x57, 0x15, 0xd7, 0xf0, 0xdc, 0xce, 0x0c, 0xb5, 0xad, 0xa7, - 0x00, 0xa9, 0xc1, 0xb8, 0x61, 0xd4, 0x36, 0xf7, 0x8c, 0x1b, 0x44, 0x3b, - 0x2a, 0x51, 0x93, 0x59, 0x15, 0x53, 0xcf, 0x3e, 0x17, 0x20, 0x06, 0xfc, - 0xa9, 0xd8, 0xc6, 0xbb, 0x48, 0xaa, 0xff, 0xa6, 0x82, 0xb2, 0xe7, 0xca, - 0x1b, 0xec, 0xa1, 0x10, 0x61, 0x32, 0xc4, 0x4b, 0x8f, 0x58, 0xa2, 0x56, - 0x55, 0x46, 0x50, 0x2a, 0x4d, 0x07, 0x0b, 0xe3, 0x9e, 0xc3, 0x36, 0xae, - 0x6a, 0xa6, 0x78, 0xad, 0x47, 0xc2, 0x4e, 0xe1, 0x75, 0x05, 0xb1, 0x28, - 0x2a, 0x45, 0x27, 0x56, 0xd2, 0x58, 0xbb, 0x4c, 0xe3, 0x33, 0x71, 0x12, - 0xe6, 0xed, 0x68, 0xcc, 0x71, 0xb3, 0x3b, 0xa7, 0xc2, 0xa9, 0x9a, 0xba, - 0x03, 0xd7, 0x2e, 0xfa, 0x60, 0x1e, 0x77, 0x3d, 0x64, 0x52, 0x99, 0x59, - 0xed, 0x51, 0xa3, 0x3c, 0x4b, 0x1d, 0x0b, 0xf9, 0x00, 0xd6, 0xe3, 0xb9, - 0x74, 0xa9, 0x64, 0xa7, 0x0c, 0xb4, 0x55, 0xcd, 0x08, 0xef, 0x8c, 0x13, - 0xd2, 0x34, 0x51, 0x4d, 0xf5, 0x58, 0xd6, 0x55, 0x6e, 0x44, 0xad, 0x27, - 0x50, 0x04, 0x3e, 0xe0, 0x72, 0xc1, 0x0a, 0xad, 0x6d, 0xa6, 0xb0, 0xae, - 0x78, 0xc4, 0x20, 0xe4, 0x6e, 0x08, 0x54, 0x2b, 0x06, 0x47, 0xed, 0x56, - 0x5f, 0x58, 0x27, 0x4b, 0x6d, 0x31, 0x84, 0x0f, 0xfc, 0xea, 0xfe, 0xc9, - 0xf0, 0xb1, 0xe0, 0xa6, 0x9c, 0xaa, 0x8a, 0xbc, 0xab, 0xd9, 0x2f, 0xfd, - 0x24, 0x21, 0xa0, 0x3f, 0x7f, 0x53, 0x8b, 0x59, 0xaa, 0x50, 0x6b, 0x3a, - 0x72, 0x1a, 0x16, 0xf6, 0x61, 0xd3, 0x11, 0xb8, 0xbd, 0xa8, 0xdf, 0xa7, - 0xae, 0xb5, 0xd3, 0xcf, 0xf5, 0xf1, 0x76, 0x16, 0x31, 0x37, 0xc8, 0x4e, - 0x46, 0x59, 0xeb, 0x54, 0x7a, 0x42, 0xfa, 0x24, 0x53, 0x01, 0x7a, 0xdd, - 0x57, 0xbf, 0xf6, 0xab, 0x8c, 0xa6, 0xfb, 0xaf, 0xbc, 0xc6, 0xf8, 0xe6, - 0x69, 0x0b, 0xe6, 0x2d, 0xd0, 0x48, 0x99, 0x57, 0xd5, 0x57, 0x7c, 0x49, - 0xeb, 0x2e, 0x8d, 0x0c, 0x1d, 0xe8, 0xa3, 0xc7, 0x83, 0xb0, 0xa1, 0xa6, - 0x8d, 0xab, 0x8b, 0xbe, 0x64, 0xdc, 0x28, 0x00, 0xe9, 0x23, 0xac, 0x41, - 0x8c, 0x54, 0x5e, 0x59, 0x52, 0x4f, 0x21, 0x38, 0x93, 0x17, 0x22, 0xf3, - 0xcf, 0xd0, 0x57, 0xb6, 0x18, 0xa8, 0x7a, 0xa8, 0x61, 0xb7, 0x5c, 0xd2, - 0xee, 0xf4, 0x50, 0x19, 0x88, 0x39, 0x26, 0x50, 0x7a, 0x59, 0xef, 0x53, - 0x6d, 0x40, 0x3f, 0x22, 0x57, 0xfe, 0xbd, 0xda, 0x52, 0xbd, 0xf6, 0xaa, - 0xc6, 0xa6, 0x5d, 0xb1, 0x0e, 0xc9, 0xdd, 0xe9, 0x57, 0x0e, 0x75, 0x30, - 0x7f, 0x4a, 0x2f, 0x58, 0x31, 0x57, 0xbe, 0x47, 0x57, 0x2c, 0x9a, 0x09, - 0x40, 0xe5, 0x57, 0xc5, 0x32, 0xaf, 0x73, 0xa6, 0x9d, 0xac, 0x9c, 0xc0, - 0x26, 0xdf, 0x24, 0x03, 0x9e, 0x26, 0xac, 0x43, 0x7c, 0x55, 0x1a, 0x59, - 0xe3, 0x4d, 0xc8, 0x35, 0xac, 0x14, 0x32, 0xf0, 0x4c, 0xce, 0xad, 0xb4, - 0x95, 0xa7, 0x24, 0xa9, 0x2f, 0xb9, 0xf0, 0xd4, 0xe7, 0xf7, 0x2b, 0x1c, - 0xc5, 0x3b, 0x75, 0x51, 0x90, 0x59, 0xdc, 0x52, 0x4f, 0x3e, 0x79, 0x1f, - 0x5c, 0xfb, 0x0c, 0xd8, 0x5c, 0xbb, 0x14, 0xaa, 0x13, 0xa7, 0xda, 0xb2, - 0x70, 0xcb, 0xc3, 0xec, 0x49, 0x11, 0xee, 0x32, 0x20, 0x4c, 0xa5, 0x58, - 0x7b, 0x56, 0xe5, 0x45, 0xbe, 0x29, 0x9f, 0x06, 0x6a, 0xe2, 0x20, 0xc3, - 0xf2, 0xad, 0x65, 0xa6, 0xc0, 0xad, 0xc1, 0xc2, 0xf1, 0xe1, 0x1e, 0x06, - 0x4b, 0x29, 0x95, 0x45, 0x57, 0x56, 0xba, 0x58, 0x61, 0x4c, 0x5a, 0x33, - 0xc6, 0x11, 0x42, 0xed, 0xd9, 0xcb, 0x1a, 0xb3, 0x25, 0xa7, 0xef, 0xa9, - 0x09, 0xbb, 0x9a, 0xd7, 0xda, 0xfa, 0x00, 0x1f, 0xf5, 0x3d, 0xa4, 0x52, - 0x9a, 0x59, 0xa3, 0x51, 0x2b, 0x3c, 0xa3, 0x1c, 0x65, 0xf8, 0x66, 0xd5, - 0x79, 0xb9, 0x4a, 0xa9, 0x7c, 0xa7, 0x69, 0xb4, 0xe2, 0xcd, 0xb1, 0xef, - 0x32, 0x14, 0x5c, 0x35, 0xa7, 0x4d, 0x08, 0x59, 0xa6, 0x55, 0xfe, 0x43, - 0x13, 0x27, 0xa5, 0x03, 0x9d, 0xdf, 0xf9, 0xc0, 0xca, 0xac, 0x72, 0xa6, - 0xf8, 0xae, 0xf6, 0xc4, 0xc7, 0xe4, 0x16, 0x09, 0xeb, 0x2b, 0x6d, 0x47, - 0x15, 0x57, 0x44, 0x58, 0xc9, 0x4a, 0xdd, 0x30, 0xdb, 0x0e, 0x56, 0xea, - 0x76, 0xc9, 0x9c, 0xb1, 0xcf, 0xa6, 0xd1, 0xaa, 0xfb, 0xbc, 0x46, 0xda, - 0xd9, 0xfd, 0xc5, 0x21, 0x15, 0x40, 0xc1, 0x53, 0x7f, 0x59, 0x62, 0x50, - 0xe8, 0x39, 0xce, 0x19, 0x6c, 0xf5, 0xcc, 0xd2, 0xac, 0xb7, 0x98, 0xa8, - 0xfd, 0xa7, 0x10, 0xb6, 0x60, 0xd0, 0xa3, 0xf2, 0x17, 0x17, 0xbb, 0x37, - 0x17, 0x4f, 0x54, 0x59, 0xb5, 0x54, 0x06, 0x42, 0x5c, 0x24, 0xab, 0x00, - 0xda, 0xdc, 0xe1, 0xbe, 0xbd, 0xab, 0x93, 0xa6, 0x4c, 0xb0, 0x3d, 0xc7, - 0xa0, 0xe7, 0x10, 0x0c, 0x7b, 0x2e, 0x32, 0x49, 0xbc, 0x57, 0xb4, 0x57, - 0x1a, 0x49, 0x56, 0x2e, 0xe7, 0x0b, 0x75, 0xe7, 0x20, 0xc7, 0x34, 0xb0, - 0x95, 0xa6, 0xc8, 0xab, 0xff, 0xbe, 0x03, 0xdd, 0xd2, 0x00, 0x85, 0x24, - 0x22, 0x42, 0xc2, 0x54, 0x52, 0x59, 0x03, 0x4f, 0x99, 0x37, 0xf0, 0x16, - 0x76, 0xf2, 0x41, 0xd0, 0xf3, 0xb5, 0xfb, 0xa7, 0x9b, 0xa8, 0xc9, 0xb7, - 0xef, 0xd2, 0x96, 0xf5, 0xf8, 0x19, 0x07, 0x3a, 0x74, 0x50, 0x83, 0x59, - 0xaf, 0x53, 0xfa, 0x3f, 0x9d, 0x21, 0xae, 0xfd, 0x22, 0xda, 0xdd, 0xbc, - 0xc6, 0xaa, 0xcf, 0xa6, 0xb7, 0xb1, 0x90, 0xc9, 0x87, 0xea, 0xfe, 0x0e, - 0x05, 0x31, 0xdf, 0x4a, 0x4a, 0x58, 0x0c, 0x57, 0x54, 0x47, 0xc5, 0x2b, - 0xee, 0x08, 0x9b, 0xe4, 0xd9, 0xc4, 0xe6, 0xae, 0x71, 0xa6, 0xd8, 0xac, - 0x19, 0xc1, 0xc0, 0xdf, 0xd7, 0x03, 0x31, 0x27, 0x21, 0x44, 0xac, 0x55, - 0x06, 0x59, 0x92, 0x4d, 0x39, 0x35, 0x0b, 0x14, 0x85, 0xef, 0xc1, 0xcd, - 0x50, 0xb4, 0x79, 0xa7, 0x51, 0xa9, 0x96, 0xb9, 0x8a, 0xd5, 0x8e, 0xf8, - 0xcf, 0x1c, 0x45, 0x3c, 0xb9, 0x51, 0x98, 0x59, 0x93, 0x52, 0xdb, 0x3d, - 0xd2, 0x1e, 0xb5, 0xfa, 0x72, 0xd7, 0xec, 0xba, 0xe8, 0xa9, 0x26, 0xa7, - 0x33, 0xb3, 0xfa, 0xcb, 0x6b, 0xed, 0xf1, 0x11, 0x7a, 0x33, 0x79, 0x4c, - 0xc1, 0x58, 0x48, 0x56, 0x7f, 0x45, 0x20, 0x29, 0xf8, 0x05, 0xc8, 0xe1, - 0xa2, 0xc2, 0xaf, 0xad, 0x64, 0xa6, 0x06, 0xae, 0x3b, 0xc3, 0x96, 0xe2, - 0xc8, 0x06, 0xe1, 0x29, 0x04, 0x46, 0x80, 0x56, 0xa4, 0x58, 0x07, 0x4c, - 0xcc, 0x32, 0x20, 0x11, 0x98, 0xec, 0x50, 0xcb, 0xc2, 0xb2, 0x0f, 0xa7, - 0x21, 0xaa, 0x75, 0xbb, 0x33, 0xd8, 0x87, 0xfb, 0x9f, 0x1f, 0x70, 0x3e, - 0xe8, 0x52, 0x92, 0x59, 0x63, 0x51, 0xa4, 0x3b, 0x07, 0x1c, 0xb6, 0xf7, - 0xd3, 0xd4, 0x0d, 0xb9, 0x22, 0xa9, 0x94, 0xa7, 0xca, 0xb4, 0x6c, 0xce, - 0x5c, 0xf0, 0xd7, 0x14, 0xe7, 0x35, 0xfa, 0x4d, 0x1e, 0x59, 0x6e, 0x55, - 0x91, 0x43, 0x78, 0x26, 0xfa, 0x02, 0xfe, 0xde, 0x80, 0xc0, 0x88, 0xac, - 0x7c, 0xa6, 0x3e, 0xaf, 0x7c, 0xc5, 0x65, 0xe5, 0xc5, 0x09, 0x7c, 0x2c, - 0xd6, 0x47, 0x3d, 0x57, 0x25, 0x58, 0x6a, 0x4a, 0x4f, 0x30, 0x2f, 0x0e, - 0xb3, 0xe9, 0xed, 0xc8, 0x4a, 0xb1, 0xc0, 0xa6, 0x05, 0xab, 0x6e, 0xbd, - 0xe2, 0xda, 0x85, 0xfe, 0x62, 0x22, 0x8d, 0x40, 0xfd, 0x53, 0x76, 0x59, - 0x17, 0x50, 0x63, 0x39, 0x2a, 0x19, 0xc4, 0xf4, 0x37, 0xd2, 0x48, 0xb7, - 0x71, 0xa8, 0x20, 0xa8, 0x70, 0xb6, 0xf2, 0xd0, 0x4c, 0xf3, 0xbc, 0x17, - 0x42, 0x38, 0x66, 0x4f, 0x61, 0x59, 0x7c, 0x54, 0x92, 0x41, 0xc0, 0x23, - 0x00, 0x00, 0x3b, 0xdc, 0x6e, 0xbe, 0x81, 0xab, 0xa1, 0xa6, 0x99, 0xb0, - 0xc3, 0xc7, 0x45, 0xe8, 0xb8, 0x0c, 0x0f, 0x2f, 0x93, 0x49, 0xde, 0x57, - 0x92, 0x57, 0xb4, 0x48, 0xc5, 0x2d, 0x3c, 0x0b, 0xd2, 0xe6, 0x99, 0xc6, - 0xeb, 0xaf, 0x86, 0xa6, 0x08, 0xac, 0x72, 0xbf, 0xa2, 0xdd, 0x7c, 0x01, - 0x24, 0x25, 0x92, 0x42, 0xfc, 0x54, 0x40, 0x59, 0xb3, 0x4e, 0x13, 0x37, - 0x49, 0x16, 0xcf, 0xf1, 0xac, 0xcf, 0x98, 0xb5, 0xd7, 0xa7, 0xc7, 0xa8, - 0x29, 0xb8, 0x87, 0xd3, 0x3f, 0xf6, 0x9c, 0x1a, 0x89, 0x3a, 0xbf, 0x50, - 0x89, 0x59, 0x73, 0x53, 0x80, 0x3f, 0xff, 0x20, 0x02, 0xfd, 0x87, 0xd9, - 0x6d, 0xbc, 0x8f, 0xaa, 0xe6, 0xa6, 0x03, 0xb2, 0x20, 0xca, 0x27, 0xeb, - 0xab, 0x0f, 0x92, 0x31, 0x3d, 0x4b, 0x66, 0x58, 0xe4, 0x56, 0xea, 0x46, - 0x32, 0x2b, 0x3f, 0x08, 0xfe, 0xe3, 0x53, 0xc4, 0xa3, 0xae, 0x68, 0xa6, - 0x1c, 0xad, 0x91, 0xc1, 0x64, 0xe0, 0x7c, 0x04, 0xd0, 0x27, 0x8c, 0x44, - 0xe0, 0x55, 0xf2, 0x58, 0x3a, 0x4d, 0xaf, 0x34, 0x65, 0x13, 0xdc, 0xee, - 0x35, 0xcd, 0xf3, 0xb3, 0x60, 0xa7, 0x7d, 0xa9, 0x00, 0xba, 0x22, 0xd6, - 0x38, 0xf9, 0x71, 0x1d, 0xc4, 0x3c, 0xfd, 0x51, 0x9b, 0x59, 0x50, 0x52, - 0x5c, 0x3d, 0x35, 0x1e, 0x07, 0xfa, 0xdb, 0xd6, 0x80, 0xba, 0xb7, 0xa9, - 0x3f, 0xa7, 0x8b, 0xb3, 0x85, 0xcc, 0x14, 0xee, 0x97, 0x12, 0x07, 0x34, - 0xd3, 0x4c, 0xd4, 0x58, 0x1e, 0x56, 0x0d, 0x45, 0x8d, 0x28, 0x4a, 0x05, - 0x28, 0xe1, 0x27, 0xc2, 0x67, 0xad, 0x6c, 0xa6, 0x46, 0xae, 0xbf, 0xc3, - 0x32, 0xe3, 0x76, 0x07, 0x77, 0x2a, 0x6e, 0x46, 0xae, 0x56, 0x88, 0x58, - 0xad, 0x4b, 0x3e, 0x32, 0x77, 0x10, 0xf3, 0xeb, 0xc5, 0xca, 0x6b, 0xb2, - 0xfd, 0xa6, 0x4e, 0xaa, 0xea, 0xbb, 0xc8, 0xd8, 0x34, 0xfc, 0x3f, 0x20, - 0xe8, 0x3e, 0x2b, 0x53, 0x8d, 0x59, 0x1a, 0x51, 0x25, 0x3b, 0x62, 0x1b, - 0x0e, 0xf7, 0x3b, 0xd4, 0xa6, 0xb8, 0xf9, 0xa8, 0xb1, 0xa7, 0x26, 0xb5, - 0xfe, 0xce, 0x00, 0xf1, 0x84, 0x15, 0x6a, 0x36, 0x51, 0x4e, 0x2d, 0x59, - 0x3c, 0x55, 0x20, 0x43, 0xdc, 0x25, 0x4f, 0x02, 0x5f, 0xde, 0x07, 0xc0, - 0x4e, 0xac, 0x7e, 0xa6, 0x8c, 0xaf, 0xfc, 0xc5, 0x0a, 0xe6, 0x6e, 0x0a, - 0x11, 0x2d, 0x3c, 0x48, 0x63, 0x57, 0x06, 0x58, 0x09, 0x4a, 0xbe, 0x2f, - 0x88, 0x0d, 0x0d, 0xe9, 0x64, 0xc8, 0xfb, 0xb0, 0xb0, 0xa6, 0x3d, 0xab, - 0xe2, 0xbd, 0x7c, 0xdb, 0x32, 0xff, 0xff, 0x22, 0x04, 0x41, 0x37, 0x54, - 0x6e, 0x59, 0xc8, 0x4f, 0xe0, 0x38, 0x87, 0x18, 0x17, 0xf4, 0xa6, 0xd1, - 0xe5, 0xb6, 0x4c, 0xa8, 0x44, 0xa8, 0xd1, 0xb6, 0x85, 0xd1, 0xf4, 0xf3, - 0x64, 0x18, 0xc2, 0x38, 0xba, 0x4f, 0x68, 0x59, 0x46, 0x54, 0x1a, 0x41, - 0x24, 0x23, 0x54, 0xff, 0xa0, 0xdb, 0xf8, 0xbd, 0x4a, 0xab, 0xac, 0xa6, - 0xea, 0xb0, 0x4a, 0xc8, 0xe8, 0xe8, 0x63, 0x0d, 0xa0, 0x2f, 0xf4, 0x49, - 0x01, 0x58, 0x6a, 0x57, 0x50, 0x48, 0x33, 0x2d, 0x91, 0x0a, 0x2e, 0xe6, - 0x18, 0xc6, 0x9a, 0xaf, 0x83, 0xa6, 0x3f, 0xac, 0xee, 0xbf, 0x3d, 0xde, - 0x2a, 0x02, 0xbb, 0x25, 0x09, 0x43, 0x2e, 0x55, 0x33, 0x59, 0x60, 0x4e, - 0x8a, 0x36, 0xa5, 0x15, 0x23, 0xf1, 0x21, 0xcf, 0x34, 0xb5, 0xbd, 0xa7, - 0xeb, 0xa8, 0x94, 0xb8, 0x19, 0xd4, 0xeb, 0xf6, 0x3d, 0x1b, 0x0c, 0x3b, - 0x08, 0x51, 0x90, 0x59, 0x33, 0x53, 0x06, 0x3f, 0x61, 0x20, 0x56, 0xfc, - 0xed, 0xd8, 0xfd, 0xbb, 0x5c, 0xaa, 0xf7, 0xa6, 0x5b, 0xb2, 0xa7, 0xca, - 0xce, 0xeb, 0x54, 0x10, 0x20, 0x32, 0x99, 0x4b, 0x84, 0x58, 0xb6, 0x56, - 0x83, 0x46, 0x9a, 0x2a, 0x97, 0x07, 0x58, 0xe3, 0xd8, 0xc3, 0x57, 0xae, - 0x69, 0xa6, 0x5c, 0xad, 0x0b, 0xc2, 0x07, 0xe1, 0x25, 0x05, 0x6b, 0x28, - 0xf8, 0x44, 0x11, 0x56, 0xdc, 0x58, 0xe4, 0x4c, 0x24, 0x34, 0xbc, 0x12, - 0x36, 0xee, 0xa5, 0xcc, 0x9d, 0xb3, 0x44, 0xa7, 0xad, 0xa9, 0x69, 0xba, - 0xbb, 0xd6, 0xe2, 0xf9, 0x14, 0x1e, 0x3f, 0x3d, 0x44, 0x52, 0x99, 0x59, - 0x0d, 0x52, 0xdf, 0x3c, 0x93, 0x1d, 0x5c, 0xf9, 0x44, 0xd6, 0x14, 0xba, - 0x89, 0xa9, 0x58, 0xa7, 0xe3, 0xb3, 0x15, 0xcd, 0xb9, 0xee, 0x3f, 0x13, - 0x93, 0x34, 0x27, 0x4d, 0xef, 0x58, 0xea, 0x55, 0xa1, 0x44, 0xf4, 0x27, - 0x9e, 0x04, 0x87, 0xe0, 0xad, 0xc1, 0x26, 0xad, 0x6c, 0xa6, 0x8f, 0xae, - 0x3c, 0xc4, 0xd7, 0xe3, 0x20, 0x08, 0x0d, 0x2b, 0xd7, 0x46, 0xd9, 0x56, - 0x6d, 0x58, 0x51, 0x4b, 0xb0, 0x31, 0xce, 0x0f, 0x4d, 0xeb, 0x3a, 0xca, - 0x18, 0xb2, 0xe9, 0xa6, 0x82, 0xaa, 0x58, 0xbc, 0x64, 0xd9, 0xde, 0xfc, - 0xde, 0x20, 0x65, 0x3f, 0x66, 0x53, 0x8a, 0x59, 0xd0, 0x50, 0xa4, 0x3a, - 0xbf, 0x1a, 0x63, 0xf6, 0xa6, 0xd3, 0x41, 0xb8, 0xce, 0xa8, 0xd1, 0xa7, - 0x83, 0xb5, 0x8e, 0xcf, 0xab, 0xf1, 0x26, 0x16, 0xf5, 0x36, 0xa2, 0x4e, - 0x3e, 0x59, 0x06, 0x55, 0xad, 0x42, 0x41, 0x25, 0xa5, 0x01, 0xc0, 0xdd, - 0x8f, 0xbf, 0x12, 0xac, 0x86, 0xa6, 0xd9, 0xaf, 0x7f, 0xc6, 0xad, 0xe6, - 0x19, 0x0b, 0xa6, 0x2d, 0x9e, 0x48, 0x8c, 0x57, 0xe1, 0x57, 0xac, 0x49, - 0x2b, 0x2f, 0xdf, 0x0c, 0x66, 0xe8, 0xe1, 0xc7, 0xa9, 0xb0, 0xa3, 0xa6, - 0x77, 0xab, 0x52, 0xbe, 0x1d, 0xdc, 0xda, 0xff, 0x9e, 0x23, 0x7a, 0x41, - 0x6f, 0x54, 0x64, 0x59, 0x78, 0x4f, 0x5d, 0x38, 0xdf, 0x17, 0x71, 0xf3, - 0x11, 0xd1, 0x85, 0xb6, 0x28, 0xa8, 0x67, 0xa8, 0x35, 0xb7, 0x18, 0xd2, - 0x9e, 0xf4, 0x07, 0x19, 0x49, 0x39, 0x04, 0x50, 0x77, 0x59, 0x07, 0x54, - 0xa8, 0x40, 0x83, 0x22, 0xa9, 0xfe, 0x04, 0xdb, 0x86, 0xbd, 0x11, 0xab, - 0xbd, 0xa6, 0x38, 0xb1, 0xd0, 0xc8, 0x90, 0xe9, 0x0b, 0x0e, 0x30, 0x30, - 0x57, 0x4a, 0x1c, 0x58, 0x48, 0x57, 0xe9, 0x47, 0x9f, 0x2c, 0xe6, 0x09, - 0x8b, 0xe5, 0x94, 0xc5, 0x53, 0xaf, 0x78, 0xa6, 0x7e, 0xac, 0x65, 0xc0, - 0xdc, 0xde, 0xd7, 0x02, 0x54, 0x26, 0x7b, 0x43, 0x63, 0x55, 0x22, 0x59, - 0x0b, 0x4e, 0x04, 0x36, 0xfc, 0x14, 0x7e, 0xf0, 0x8e, 0xce, 0xd9, 0xb4, - 0x9f, 0xa7, 0x15, 0xa9, 0xfb, 0xb8, 0xaf, 0xd4, 0x95, 0xf7, 0xe1, 0x1b, - 0x8d, 0x3b, 0x4e, 0x51, 0x97, 0x59, 0xf0, 0x52, 0x90, 0x3e, 0xbd, 0x1f, - 0xae, 0xfb, 0x51, 0xd8, 0x8f, 0xbb, 0x2a, 0xaa, 0x0c, 0xa7, 0xae, 0xb2, - 0x34, 0xcb, 0x73, 0xec, 0xfe, 0x10, 0xac, 0x32, 0xf5, 0x4b, 0x9e, 0x58, - 0x89, 0x56, 0x1c, 0x46, 0xff, 0x29, 0xf1, 0x06, 0xb3, 0xe2, 0x5a, 0xc3, - 0x11, 0xae, 0x67, 0xa6, 0xa0, 0xad, 0x88, 0xc2, 0xa5, 0xe1, 0xd1, 0x05, - 0x05, 0x29, 0x62, 0x45, 0x44, 0x56, 0xc2, 0x58, 0x8d, 0x4c, 0x98, 0x33, - 0x15, 0x12, 0x8e, 0xed, 0x19, 0xcc, 0x45, 0xb3, 0x2b, 0xa7, 0xdd, 0xa9, - 0xd5, 0xba, 0x53, 0xd7, 0x8f, 0xfa, 0xb1, 0x1e, 0xc0, 0x3d, 0x83, 0x52, - 0x9c, 0x59, 0xc5, 0x51, 0x62, 0x3c, 0xf0, 0x1c, 0xb3, 0xf8, 0xab, 0xd5, - 0xab, 0xb9, 0x5b, 0xa9, 0x73, 0xa7, 0x3d, 0xb4, 0xa2, 0xcd, 0x62, 0xef, - 0xe6, 0x13, 0x1e, 0x35, 0x7c, 0x4d, 0x05, 0x59, 0xb6, 0x55, 0x36, 0x44, - 0x58, 0x27, 0xf4, 0x03, 0xe8, 0xdf, 0x2f, 0xc1, 0xe8, 0xac, 0x6f, 0xa6, - 0xd8, 0xae, 0xba, 0xc4, 0x7d, 0xe4, 0xc6, 0x08, 0xa8, 0x2b, 0x3c, 0x47, - 0x04, 0x57, 0x50, 0x58, 0xf5, 0x4a, 0x1f, 0x31, 0x28, 0x0f, 0xa4, 0xea, - 0xb4, 0xc9, 0xc1, 0xb1, 0xda, 0xa6, 0xb4, 0xaa, 0xcb, 0xbc, 0xfb, 0xd9, - 0x8e, 0xfd, 0x78, 0x21, 0xe3, 0x3f, 0xa0, 0x53, 0x86, 0x59, 0x83, 0x50, - 0x23, 0x3a, 0x1c, 0x1a, 0xb9, 0xf5, 0x11, 0xd3, 0xdb, 0xb7, 0xa6, 0xa8, - 0xf3, 0xa7, 0xe0, 0xb5, 0x21, 0xd0, 0x51, 0xf2, 0xcf, 0x16, 0x79, 0x37, - 0xf6, 0x4e, 0x4a, 0x59, 0xd2, 0x54, 0x38, 0x42, 0xa8, 0x24, 0xf4, 0x00, - 0x28, 0xdd, 0x14, 0xbf, 0xd9, 0xab, 0x90, 0xa6, 0x24, 0xb0, 0x04, 0xc7, - 0x51, 0xe7, 0xc3, 0x0b, 0x39, 0x2e, 0x03, 0x49, 0xad, 0x57, 0xc2, 0x57, - 0x48, 0x49, 0x9b, 0x2e, 0x33, 0x0c, 0xc4, 0xe7, 0x59, 0xc7, 0x5c, 0xb0, - 0x97, 0xa6, 0xaf, 0xab, 0xc8, 0xbe, 0xba, 0xdc, 0x85, 0x00, 0x3b, 0x24, - 0xee, 0x41, 0xa8, 0x54, 0x58, 0x59, 0x27, 0x4f, 0xd9, 0x37, 0x39, 0x17, - 0xc8, 0xf2, 0x7f, 0xd0, 0x23, 0xb6, 0x07, 0xa8, 0x8e, 0xa8, 0x97, 0xb7, - 0xac, 0xd2, 0x48, 0xf5, 0xac, 0x19, 0xca, 0x39, 0x54, 0x50, 0x7c, 0x59, - 0xce, 0x53, 0x2f, 0x40, 0xe7, 0x21, 0xfc, 0xfd, 0x6a, 0xda, 0x11, 0xbd, - 0xdd, 0xaa, 0xcc, 0xa6, 0x8a, 0xb1, 0x58, 0xc9, 0x35, 0xea, 0xb4, 0x0e, - 0xc1, 0x30, 0xb4, 0x4a, 0x3c, 0x58, 0x20, 0x57, 0x82, 0x47, 0x0b, 0x2c, - 0x3b, 0x09, 0xe9, 0xe4, 0x10, 0xc5, 0x0d, 0xaf, 0x6d, 0xa6, 0xc2, 0xac, - 0xda, 0xc0, 0x7f, 0xdf, 0x7e, 0x03, 0xf4, 0x26, 0xe5, 0x43, 0x9c, 0x55, - 0x0a, 0x59, 0xbc, 0x4d, 0x77, 0x35, 0x57, 0x14, 0xd5, 0xef, 0xff, 0xcd, - 0x7e, 0xb4, 0x82, 0xa7, 0x3f, 0xa9, 0x64, 0xb9, 0x45, 0xd5, 0x41, 0xf8, - 0x82, 0x1c, 0x0d, 0x3c, 0x96, 0x51, 0x98, 0x59, 0xb3, 0x52, 0x10, 0x3e, - 0x20, 0x1f, 0x02, 0xfb, 0xb7, 0xd7, 0x22, 0xbb, 0xfa, 0xa9, 0x1e, 0xa7, - 0x0a, 0xb3, 0xb9, 0xcb, 0x20, 0xed, 0xa1, 0x11, 0x3c, 0x33, 0x4f, 0x4c, - 0xb4, 0x58, 0x60, 0x56, 0xae, 0x45, 0x69, 0x29, 0x45, 0x06, 0x12, 0xe2, - 0xdc, 0xc2, 0xcd, 0xad, 0x67, 0xa6, 0xe2, 0xad, 0x06, 0xc3, 0x47, 0xe2, - 0x7b, 0x06, 0x9d, 0x29, 0xcf, 0x45, 0x6f, 0x56, 0xad, 0x58, 0x31, 0x4c, - 0x0d, 0x33, 0x6d, 0x11, 0xe7, 0xec, 0x8d, 0xcb, 0xec, 0xb2, 0x18, 0xa7, - 0x0a, 0xaa, 0x45, 0xbb, 0xe9, 0xd7, 0x3b, 0xfb, 0x52, 0x1f, 0x3b, 0x3e, - 0xc7, 0x52, 0x96, 0x59, 0x81, 0x51, 0xe2, 0x3b, 0x4e, 0x1c, 0x08, 0xf8, - 0x14, 0xd5, 0x42, 0xb9, 0x31, 0xa9, 0x8d, 0xa7, 0x98, 0xb4, 0x31, 0xce, - 0x0a, 0xf0, 0x8e, 0x14, 0xa5, 0x35, 0xd6, 0x4d, 0x12, 0x59, 0x8a, 0x55, - 0xc2, 0x43, 0xbf, 0x26, 0x4a, 0x03, 0x47, 0xdf, 0xb6, 0xc0, 0xa9, 0xac, - 0x73, 0xa6, 0x22, 0xaf, 0x3c, 0xc5, 0x1c, 0xe5, 0x75, 0x09, 0x39, 0x2c, - 0xa7, 0x47, 0x2a, 0x57, 0x33, 0x58, 0x95, 0x4a, 0x92, 0x30, 0x7e, 0x0e, - 0xfe, 0xe9, 0x2b, 0xc9, 0x71, 0xb1, 0xc4, 0xa6, 0xf1, 0xaa, 0x34, 0xbd, - 0x9f, 0xda, 0x33, 0xfe, 0x1a, 0x22, 0x57, 0x40, 0xe0, 0x53, 0x7b, 0x59, - 0x3b, 0x50, 0x9d, 0x39, 0x7a, 0x19, 0x0d, 0xf5, 0x7e, 0xd2, 0x77, 0xb7, - 0x7f, 0xa8, 0x14, 0xa8, 0x40, 0xb6, 0xb2, 0xd0, 0xfc, 0xf2, 0x72, 0x17, - 0x02, 0x38, 0x43, 0x4f, 0x5b, 0x59, 0x96, 0x54, 0xc9, 0x41, 0x06, 0x24, - 0x51, 0x00, 0x81, 0xdc, 0xa7, 0xbe, 0x99, 0xab, 0x9c, 0xa6, 0x76, 0xb0, - 0x82, 0xc7, 0xfe, 0xe7, 0x65, 0x0c, 0xd0, 0x2e, 0x63, 0x49, 0xd0, 0x57, - 0xa2, 0x57, 0xe0, 0x48, 0x0e, 0x2e, 0x84, 0x0b, 0x22, 0xe7, 0xd5, 0xc6, - 0x0c, 0xb0, 0x8f, 0xa6, 0xe7, 0xab, 0x41, 0xbf, 0x55, 0xdd, 0x31, 0x01, - 0xd8, 0x24, 0x60, 0x42, 0xe2, 0x54, 0x48, 0x59, 0xd8, 0x4e, 0x50, 0x37, - 0x97, 0x16, 0x1b, 0xf2, 0xf1, 0xcf, 0xc2, 0xb5, 0xe6, 0xa7, 0xb5, 0xa8, - 0xfb, 0xb7, 0x41, 0xd3, 0xf2, 0xf5, 0x4f, 0x1a, 0x4f, 0x3a, 0x9c, 0x50, - 0x86, 0x59, 0x8f, 0x53, 0xb8, 0x3f, 0x48, 0x21, 0x52, 0xfd, 0xcc, 0xd9, - 0xa3, 0xbc, 0xa6, 0xaa, 0xdd, 0xa6, 0xdf, 0xb1, 0xdd, 0xc9, 0xdf, 0xea, - 0x5b, 0x0f, 0x50, 0x31, 0x14, 0x4b, 0x56, 0x58, 0xfa, 0x56, 0x19, 0x47, - 0x76, 0x2b, 0x91, 0x08, 0x44, 0xe4, 0x93, 0xc4, 0xbf, 0xae, 0x6e, 0xa6, - 0xfd, 0xac, 0x57, 0xc1, 0x1c, 0xe0, 0x2c, 0x04, 0x8b, 0x27, 0x58, 0x44, - 0xca, 0x55, 0xf9, 0x58, 0x66, 0x4d, 0xec, 0x34, 0xb3, 0x13, 0x2a, 0xef, - 0x73, 0xcd, 0x20, 0xb4, 0x6a, 0xa7, 0x69, 0xa9, 0xd0, 0xb9, 0xdb, 0xd5, - 0xe9, 0xf8, 0x28, 0x1d, 0x88, 0x3c, 0xe0, 0x51, 0x98, 0x59, 0x71, 0x52, - 0x93, 0x3d, 0x82, 0x1e, 0x53, 0xfa, 0x23, 0xd7, 0xb2, 0xba, 0xca, 0xa9, - 0x38, 0xa7, 0x5e, 0xb3, 0x48, 0xcc, 0xc4, 0xed, 0x4c, 0x12, 0xc6, 0x33, - 0xa9, 0x4c, 0xcc, 0x58, 0x31, 0x56, 0x41, 0x45, 0xd3, 0x28, 0x97, 0x05, - 0x74, 0xe1, 0x5d, 0xc2, 0x8b, 0xad, 0x66, 0xa6, 0x29, 0xae, 0x82, 0xc3, - 0xeb, 0xe2, 0x24, 0x07, 0x35, 0x2a, 0x3b, 0x46, 0x99, 0x56, 0x96, 0x58, - 0xd5, 0x4b, 0x82, 0x32, 0xc2, 0x10, 0x42, 0xec, 0x01, 0xcb, 0x97, 0xb2, - 0x02, 0xa7, 0x3c, 0xaa, 0xb1, 0xbb, 0x86, 0xd8, 0xe3, 0xfb, 0xf5, 0x1f, - 0xb3, 0x3e, 0x0a, 0x53, 0x91, 0x59, 0x3b, 0x51, 0x61, 0x3b, 0xab, 0x1b, - 0x5f, 0xf7, 0x7d, 0xd4, 0xda, 0xb8, 0x06, 0xa9, 0xaa, 0xa7, 0xf4, 0xb4, - 0xc2, 0xce, 0xb0, 0xf0, 0x36, 0x15, 0x2d, 0x36, 0x2a, 0x4e, 0x25, 0x59, - 0x55, 0x55, 0x53, 0x43, 0x23, 0x26, 0x9f, 0x02, 0xa9, 0xde, 0x3c, 0xc0, - 0x6c, 0xac, 0x78, 0xa6, 0x6d, 0xaf, 0xbe, 0xc5, 0xc1, 0xe5, 0x1d, 0x0a, - 0xd0, 0x2c, 0x09, 0x48, 0x56, 0x57, 0x11, 0x58, 0x38, 0x4a, 0x01, 0x30, - 0xd3, 0x0d, 0x5b, 0xe9, 0xa2, 0xc8, 0x1f, 0xb1, 0xb8, 0xa6, 0x23, 0xab, - 0xac, 0xbd, 0x36, 0xdb, 0xe0, 0xfe, 0xb9, 0x22, 0xcc, 0x40, 0x1e, 0x54, - 0x70, 0x59, 0xed, 0x4f, 0x1d, 0x39, 0xd1, 0x18, 0x68, 0xf4, 0xe6, 0xd1, - 0x16, 0xb7, 0x5a, 0xa8, 0x35, 0xa8, 0xa4, 0xb6, 0x40, 0xd1, 0xa9, 0xf3, - 0x15, 0x18, 0x88, 0x38, 0x93, 0x4f, 0x66, 0x59, 0x5d, 0x54, 0x55, 0x41, - 0x68, 0x23, 0xa5, 0xff, 0xe6, 0xdb, 0x30, 0xbe, 0x62, 0xab, 0xa8, 0xa6, - 0xc2, 0xb0, 0x0e, 0xc8, 0x9a, 0xe8, 0x19, 0x0d, 0x59, 0x2f, 0xca, 0x49, - 0xf1, 0x57, 0x7a, 0x57, 0x81, 0x48, 0x75, 0x2d, 0xe0, 0x0a, 0x7a, 0xe6, - 0x52, 0xc6, 0xc1, 0xaf, 0x82, 0xa6, 0x28, 0xac, 0xb3, 0xbf, 0xf7, 0xdd, - 0xd9, 0x01, 0x76, 0x25, 0xd1, 0x42, 0x1b, 0x55, 0x34, 0x59, 0x8b, 0x4e, - 0xc6, 0x36, 0xf2, 0x15, 0x72, 0xf1, 0x61, 0xcf, 0x61, 0xb5, 0xcc, 0xa7, - 0xd6, 0xa8, 0x66, 0xb8, 0xd3, 0xd3, 0x9e, 0xf6, 0xf3, 0x1a, 0xcf, 0x3a, - 0xe7, 0x50, 0x8c, 0x59, 0x52, 0x53, 0x3e, 0x3f, 0xa9, 0x20, 0xa7, 0xfc, - 0x31, 0xd9, 0x34, 0xbc, 0x71, 0xaa, 0xf0, 0xa6, 0x31, 0xb2, 0x6b, 0xca, - 0x7f, 0xeb, 0x08, 0x10, 0xde, 0x31, 0x6d, 0x4b, 0x7a, 0x58, 0xc7, 0x56, - 0xb8, 0x46, 0xda, 0x2a, 0xea, 0x07, 0xa0, 0xe3, 0x13, 0xc4, 0x78, 0xae, - 0x6a, 0xa6, 0x3f, 0xad, 0xd1, 0xc1, 0xbd, 0xe0, 0xd5, 0x04, 0x27, 0x28, - 0xc5, 0x44, 0xfa, 0x55, 0xe7, 0x58, 0x0b, 0x4d, 0x65, 0x34, 0x0a, 0x13, - 0x81, 0xee, 0xe8, 0xcc, 0xc4, 0xb3, 0x52, 0xa7, 0x95, 0xa9, 0x3b, 0xba, - 0x71, 0xd6, 0x97, 0xf9, 0xc8, 0x1d, 0x05, 0x3d, 0x27, 0x52, 0x96, 0x59, - 0x2f, 0x52, 0x17, 0x3d, 0xde, 0x1d, 0xab, 0xf9, 0x89, 0xd6, 0x46, 0xba, - 0x9e, 0xa9, 0x4c, 0xa7, 0xbb, 0xb3, 0xd1, 0xcc, 0x70, 0xee, 0xef, 0x12, - 0x55, 0x34, 0xfe, 0x4c, 0xe5, 0x58, 0x00, 0x56, 0xd5, 0x44, 0x39, 0x28, - 0xed, 0x04, 0xd3, 0xe0, 0xe1, 0xc1, 0x49, 0xad, 0x67, 0xa6, 0x71, 0xae, - 0x00, 0xc4, 0x8c, 0xe3, 0xd1, 0x07, 0xc9, 0x2a, 0xa7, 0x46, 0xc3, 0x56, - 0x7e, 0x58, 0x75, 0x4b, 0xf9, 0x31, 0x15, 0x10, 0x9f, 0xeb, 0x75, 0xca, - 0x42, 0xb2, 0xef, 0xa6, 0x6d, 0xaa, 0x22, 0xbc, 0x1f, 0xd9, 0x8e, 0xfc, - 0x95, 0x20, 0x2e, 0x3f, 0x46, 0x53, 0x93, 0x59, 0xeb, 0x50, 0xe5, 0x3a, - 0x06, 0x1b, 0xb5, 0xf6, 0xe7, 0xd3, 0x75, 0xb8, 0xda, 0xa8, 0xca, 0xa7, - 0x51, 0xb5, 0x51, 0xcf, 0x58, 0xf1, 0xdf, 0x15, 0xb2, 0x36, 0x7f, 0x4e, - 0x36, 0x59, 0x1d, 0x55, 0xe5, 0x42, 0x85, 0x25, 0xf6, 0x01, 0x08, 0xde, - 0xc6, 0xbf, 0x2e, 0xac, 0x81, 0xa6, 0xb7, 0xaf, 0x41, 0xc6, 0x63, 0xe6, - 0xcb, 0x0a, 0x5f, 0x2d, 0x74, 0x48, 0x77, 0x57, 0xf3, 0x57, 0xd8, 0x49, - 0x6d, 0x2f, 0x2e, 0x0d, 0xb2, 0xe8, 0x1e, 0xc8, 0xcf, 0xb0, 0xa7, 0xa6, - 0x5e, 0xab, 0x1d, 0xbe, 0xd4, 0xdb, 0x8d, 0xff, 0x53, 0x23, 0x45, 0x41, - 0x55, 0x54, 0x69, 0x59, 0x9d, 0x4f, 0x99, 0x38, 0x2d, 0x18, 0xbc, 0xf3, - 0x58, 0xd1, 0xad, 0xb6, 0x3d, 0xa8, 0x52, 0xa8, 0x0b, 0xb7, 0xd1, 0xd1, - 0x52, 0xf4, 0xbb, 0x18, 0x0b, 0x39, 0xe1, 0x4f, 0x72, 0x59, 0x22, 0x54, - 0xe0, 0x40, 0xca, 0x22, 0xfa, 0xfe, 0x49, 0xdb, 0xbd, 0xbd, 0x29, 0xab, - 0xb7, 0xa6, 0x13, 0xb1, 0x93, 0xc8, 0x41, 0xe9, 0xc0, 0x0d, 0xea, 0x2f, - 0x2d, 0x4a, 0x0e, 0x58, 0x58, 0x57, 0x1a, 0x48, 0xe1, 0x2c, 0x35, 0x0a, - 0xd7, 0xe5, 0xcf, 0xc5, 0x76, 0xaf, 0x7b, 0xa6, 0x62, 0xac, 0x2d, 0xc0, - 0x93, 0xde, 0x88, 0x02, 0x0f, 0x26, 0x43, 0x43, 0x4f, 0x55, 0x25, 0x59, - 0x37, 0x4e, 0x3f, 0x36, 0x4b, 0x15, 0xc9, 0xf0, 0xd3, 0xce, 0x01, 0xb5, - 0xaf, 0xa7, 0xff, 0xa8, 0xcc, 0xb8, 0x6b, 0xd4, 0x46, 0xf7, 0x97, 0x1b, - 0x4f, 0x3b, 0x30, 0x51, 0x93, 0x59, 0x0f, 0x53, 0xc8, 0x3e, 0x05, 0x20, - 0x00, 0xfc, 0x94, 0xd8, 0xc6, 0xbb, 0x3e, 0xaa, 0x03, 0xa7, 0x89, 0xb2, - 0xef, 0xca, 0x2c, 0xec, 0xac, 0x10, 0x6e, 0x32, 0xcb, 0x4b, 0x8e, 0x58, - 0xa4, 0x56, 0x46, 0x46, 0x4a, 0x2a, 0x3b, 0x07, 0x01, 0xe3, 0x92, 0xc3, - 0x33, 0xae, 0x66, 0xa6, 0x81, 0xad, 0x4f, 0xc2, 0x5b, 0xe1, 0x84, 0x05, - 0xbb, 0x28, 0x34, 0x45, 0x2b, 0x56, 0xcf, 0x58, 0xb5, 0x4c, 0xd9, 0x33, - 0x60, 0x12, 0xdd, 0xed, 0x5a, 0xcc, 0x6a, 0xb3, 0x3b, 0xa7, 0xc3, 0xa9, - 0xa6, 0xba, 0x0b, 0xd7, 0x3f, 0xfa, 0x6a, 0x1e, 0x84, 0x3d, 0x68, 0x52, - 0x99, 0x59, 0xe6, 0x51, 0x9d, 0x3c, 0x39, 0x1d, 0x03, 0xf9, 0xef, 0xd5, - 0xde, 0xb9, 0x6e, 0xa9, 0x67, 0xa7, 0x14, 0xb4, 0x5f, 0xcd, 0x17, 0xef, - 0x97, 0x13, 0xdf, 0x34, 0x56, 0x4d, 0xf9, 0x58, 0xd0, 0x55, 0x66, 0x44, - 0xa0, 0x27, 0x43, 0x04, 0x30, 0xe0, 0x6a, 0xc1, 0x03, 0xad, 0x6f, 0xa6, - 0xb5, 0xae, 0x80, 0xc4, 0x31, 0xe4, 0x78, 0x08, 0x64, 0x2b, 0x09, 0x47, - 0xf5, 0x56, 0x59, 0x58, 0x22, 0x4b, 0x62, 0x31, 0x73, 0x0f, 0xf4, 0xea, - 0xee, 0xc9, 0xed, 0xb1, 0xdd, 0xa6, 0xa1, 0xaa, 0x92, 0xbc, 0xb9, 0xd9, - 0x3a, 0xfd, 0x34, 0x21, 0xa6, 0x3f, 0x88, 0x53, 0x87, 0x59, 0xa6, 0x50, - 0x60, 0x3a, 0x65, 0x1a, 0x08, 0xf6, 0x56, 0xd3, 0x08, 0xb8, 0xba, 0xa8, - 0xe3, 0xa7, 0xb3, 0xb5, 0xe0, 0xcf, 0x02, 0xf2, 0x83, 0x16, 0x3d, 0x37, - 0xcd, 0x4e, 0x48, 0x59, 0xe6, 0x54, 0x72, 0x42, 0xec, 0x24, 0x48, 0x01, - 0x6b, 0xdd, 0x4f, 0xbf, 0xf1, 0xab, 0x8c, 0xa6, 0x03, 0xb0, 0xc4, 0xc6, - 0x08, 0xe7, 0x74, 0x0b, 0xf3, 0x2d, 0xd9, 0x48, 0x99, 0x57, 0xd5, 0x57, - 0x73, 0x49, 0xe0, 0x2e, 0x7f, 0x0c, 0x11, 0xe8, 0x96, 0xc7, 0x80, 0xb0, - 0x9d, 0xa6, 0x94, 0xab, 0x92, 0xbe, 0x72, 0xdc, 0x36, 0x00, 0xf2, 0x23, - 0xba, 0x41, 0x8d, 0x54, 0x5e, 0x59, 0x4c, 0x4f, 0x16, 0x38, 0x86, 0x17, - 0x15, 0xf3, 0xc2, 0xd0, 0x51, 0xb6, 0x14, 0xa8, 0x7f, 0xa8, 0x66, 0xb7, - 0x6a, 0xd2, 0xfa, 0xf4, 0x5f, 0x19, 0x90, 0x39, 0x2e, 0x50, 0x79, 0x59, - 0xeb, 0x53, 0x64, 0x40, 0x31, 0x22, 0x4b, 0xfe, 0xaf, 0xda, 0x49, 0xbd, - 0xf4, 0xaa, 0xc3, 0xa6, 0x69, 0xb1, 0x14, 0xc9, 0xec, 0xe9, 0x65, 0x0e, - 0x7f, 0x30, 0x89, 0x4a, 0x2f, 0x58, 0x2f, 0x57, 0xb4, 0x47, 0x4f, 0x2c, - 0x89, 0x09, 0x35, 0xe5, 0x4b, 0xc5, 0x2d, 0xaf, 0x74, 0xa6, 0xa0, 0xac, - 0xa7, 0xc0, 0x31, 0xdf, 0x33, 0x03, 0xab, 0x26, 0xb3, 0x43, 0x82, 0x55, - 0x16, 0x59, 0xe0, 0x4d, 0xb9, 0x35, 0xa2, 0x14, 0x22, 0xf0, 0x43, 0xce, - 0xa6, 0xb4, 0x91, 0xa7, 0x28, 0xa9, 0x37, 0xb9, 0xfe, 0xd4, 0xf4, 0xf7, - 0x36, 0x1c, 0xd2, 0x3b, 0x76, 0x51, 0x98, 0x59, 0xcf, 0x52, 0x4b, 0x3e, - 0x68, 0x1f, 0x50, 0xfb, 0x01, 0xd8, 0x51, 0xbb, 0x12, 0xaa, 0x14, 0xa7, - 0xe0, 0xb2, 0x7c, 0xcb, 0xd0, 0xec, 0x57, 0x11, 0xf8, 0x32, 0x27, 0x4c, - 0xaa, 0x58, 0x72, 0x56, 0xe2, 0x45, 0xad, 0x29, 0x94, 0x06, 0x5c, 0xe2, - 0x17, 0xc3, 0xeb, 0xad, 0x68, 0xa6, 0xc2, 0xad, 0xcc, 0xc2, 0xfd, 0xe1, - 0x2e, 0x06, 0x54, 0x29, 0xa2, 0x45, 0x56, 0x56, 0xba, 0x58, 0x5b, 0x4c, - 0x4d, 0x33, 0xba, 0x11, 0x34, 0xed, 0xce, 0xcb, 0x13, 0xb3, 0x24, 0xa7, - 0xf1, 0xa9, 0x14, 0xbb, 0xa4, 0xd7, 0xea, 0xfa, 0x0a, 0x1f, 0x00, 0x3e, - 0xab, 0x52, 0x96, 0x59, 0xa3, 0x51, 0x1a, 0x3c, 0x9b, 0x1c, 0x56, 0xf8, - 0x59, 0xd5, 0x72, 0xb9, 0x45, 0xa9, 0x80, 0xa7, 0x6f, 0xb4, 0xee, 0xcd, - 0xbd, 0xef, 0x40, 0x14, 0x69, 0x35, 0xaa, 0x4d, 0x0f, 0x59, 0x9b, 0x55, - 0xfb, 0x43, 0x03, 0x27, 0x9a, 0x03, 0x8f, 0xdf, 0xef, 0xc0, 0xc6, 0xac, - 0x71, 0xa6, 0xff, 0xae, 0x02, 0xc5, 0xd0, 0xe4, 0x28, 0x09, 0xf2, 0x2b, - 0x79, 0x47, 0x17, 0x57, 0x42, 0x58, 0xc0, 0x4a, 0xd5, 0x30, 0xc9, 0x0e, - 0x4d, 0xea, 0x68, 0xc9, 0x97, 0xb1, 0xcf, 0xa6, 0xd2, 0xaa, 0x07, 0xbd, - 0x51, 0xda, 0xe7, 0xfd, 0xd1, 0x21, 0x20, 0x40, 0xc4, 0x53, 0x80, 0x59, - 0x5c, 0x50, 0xdb, 0x39, 0xc4, 0x19, 0x5c, 0xf5, 0xc2, 0xd2, 0xa5, 0xb7, - 0x92, 0xa8, 0x02, 0xa8, 0x16, 0xb6, 0x6d, 0xd0, 0xaf, 0xf2, 0x27, 0x17, - 0xc2, 0x37, 0x20, 0x4f, 0x54, 0x59, 0xb0, 0x54, 0xff, 0x41, 0x4e, 0x24, - 0x9d, 0x00, 0xce, 0xdc, 0xd8, 0xbe, 0xb8, 0xab, 0x95, 0xa6, 0x51, 0xb0, - 0x47, 0xc7, 0xb0, 0xe7, 0x18, 0x0c, 0x8d, 0x2e, 0x35, 0x49, 0xc2, 0x57, - 0xaf, 0x57, 0x13, 0x49, 0x4a, 0x2e, 0xdb, 0x0b, 0x66, 0xe7, 0x17, 0xc7, - 0x2d, 0xb0, 0x95, 0xa6, 0xcc, 0xab, 0x09, 0xbf, 0x0e, 0xdd, 0xe2, 0x00, - 0x90, 0x24, 0x2b, 0x42, 0xc8, 0x54, 0x4e, 0x59, 0x00, 0x4f, 0x8c, 0x37, - 0xe3, 0x16, 0x6a, 0xf2, 0x33, 0xd0, 0xee, 0xb5, 0xf6, 0xa7, 0xa1, 0xa8, - 0xce, 0xb7, 0xfd, 0xd2, 0xa2, 0xf5, 0x06, 0x1a, 0x11, 0x3a, 0x7a, 0x50, - 0x83, 0x59, 0xab, 0x53, 0xf0, 0x3f, 0x91, 0x21, 0x9f, 0xfd, 0x17, 0xda, - 0xd3, 0xbc, 0xc1, 0xaa, 0xd4, 0xa6, 0xb8, 0xb1, 0xa1, 0xc9, 0x8e, 0xea, - 0x11, 0x0f, 0x0c, 0x31, 0xe8, 0x4a, 0x4d, 0x58, 0x07, 0x57, 0x4e, 0x47, - 0xb7, 0x2b, 0xe1, 0x08, 0x8f, 0xe4, 0xce, 0xc4, 0xdf, 0xae, 0x72, 0xa6, - 0xde, 0xac, 0x1f, 0xc1, 0xd4, 0xdf, 0xda, 0x03, 0x48, 0x27, 0x22, 0x44, - 0xb5, 0x55, 0x02, 0x59, 0x8b, 0x4d, 0x2f, 0x35, 0xfd, 0x13, 0x79, 0xef, - 0xb5, 0xcd, 0x47, 0xb4, 0x79, 0xa7, 0x53, 0xa9, 0xa0, 0xb9, 0x94, 0xd5, - 0x9d, 0xf8, 0xda, 0x1c, 0x52, 0x3c, 0xbb, 0x51, 0x9b, 0x59, 0x8c, 0x52, - 0xd1, 0x3d, 0xc6, 0x1e, 0xa8, 0xfa, 0x63, 0xd7, 0xe9, 0xba, 0xde, 0xa9, - 0x2c, 0xa7, 0x37, 0xb3, 0x07, 0xcc, 0x78, 0xed, 0xfe, 0x11, 0x86, 0x33, - 0x7e, 0x4c, 0xc5, 0x58, 0x43, 0x56, 0x76, 0x45, 0x16, 0x29, 0xe8, 0x05, - 0xbd, 0xe1, 0x97, 0xc2, 0xaa, 0xad, 0x65, 0xa6, 0x09, 0xae, 0x48, 0xc3, - 0xa0, 0xe2, 0xd8, 0x06, 0xec, 0x29, 0x0c, 0x46, 0x84, 0x56, 0xa1, 0x58, - 0x02, 0x4c, 0xbe, 0x32, 0x15, 0x11, 0x89, 0xec, 0x46, 0xcb, 0xbb, 0xb2, - 0x0e, 0xa7, 0x23, 0xaa, 0x81, 0xbb, 0x3c, 0xd8, 0x97, 0xfb, 0xaa, 0x1f, - 0x7b, 0x3e, 0xec, 0x52, 0x94, 0x59, 0x5b, 0x51, 0x9c, 0x3b, 0xf8, 0x1b, - 0xaa, 0xf7, 0xc5, 0xd4, 0x08, 0xb9, 0x1b, 0xa9, 0x9a, 0xa7, 0xce, 0xb4, - 0x7a, 0xce, 0x68, 0xf0, 0xe5, 0x14, 0xf2, 0x35, 0x00, 0x4e, 0x1f, 0x59, - 0x6b, 0x55, 0x89, 0x43, 0x69, 0x26, 0xef, 0x02, 0xef, 0xde, 0x76, 0xc0, - 0x87, 0xac, 0x77, 0xa6, 0x49, 0xaf, 0x82, 0xc5, 0x75, 0xe5, 0xd0, 0x09, - 0x8a, 0x2c, 0xde, 0x47, 0x3e, 0x57, 0x26, 0x58, 0x5e, 0x4a, 0x48, 0x30, - 0x1e, 0x0e, 0xa8, 0xe9, 0xe0, 0xc8, 0x46, 0xb1, 0xbd, 0xa6, 0x0b, 0xab, - 0x75, 0xbd, 0xf0, 0xda, 0x91, 0xfe, 0x71, 0x22, 0x94, 0x40, 0x04, 0x54, - 0x73, 0x59, 0x13, 0x50, 0x56, 0x39, 0x21, 0x19, 0xb1, 0xf4, 0x30, 0xd2, - 0x3e, 0xb7, 0x6e, 0xa8, 0x24, 0xa8, 0x76, 0xb6, 0x00, 0xd1, 0x57, 0xf3, - 0xcc, 0x17, 0x48, 0x38, 0x72, 0x4f, 0x5d, 0x59, 0x7c, 0x54, 0x85, 0x41, - 0xb6, 0x23, 0xef, 0xff, 0x33, 0xdc, 0x61, 0xbe, 0x7f, 0xab, 0xa0, 0xa6, - 0xa0, 0xb0, 0xce, 0xc7, 0x52, 0xe8, 0xc5, 0x0c, 0x1b, 0x2f, 0x9a, 0x49, - 0xe3, 0x57, 0x8c, 0x57, 0xae, 0x48, 0xb9, 0x2d, 0x2f, 0x0b, 0xc3, 0xe6, - 0x92, 0xc6, 0xe1, 0xaf, 0x89, 0xa6, 0x0a, 0xac, 0x7d, 0xbf, 0xad, 0xdd, - 0x8d, 0x01, 0x2b, 0x25, 0xa1, 0x42, 0xfb, 0x54, 0x44, 0x59, 0xa8, 0x4e, - 0x0b, 0x37, 0x3b, 0x16, 0xc0, 0xf1, 0xa5, 0xcf, 0x8c, 0xb5, 0xd7, 0xa7, - 0xc8, 0xa8, 0x34, 0xb8, 0x91, 0xd3, 0x4d, 0xf6, 0xa9, 0x1a, 0x92, 0x3a, - 0xc8, 0x50, 0x86, 0x59, 0x72, 0x53, 0x72, 0x3f, 0xf6, 0x20, 0xf2, 0xfc, - 0x7c, 0xd9, 0x64, 0xbc, 0x8b, 0xaa, 0xe6, 0xa6, 0x0c, 0xb2, 0x28, 0xca, - 0x37, 0xeb, 0xb7, 0x0f, 0x9e, 0x31, 0x44, 0x4b, 0x69, 0x58, 0xde, 0x56, - 0xe6, 0x46, 0x20, 0x2b, 0x39, 0x08, 0xeb, 0xe3, 0x4d, 0xc4, 0x99, 0xae, - 0x6b, 0xa6, 0x20, 0xad, 0x9b, 0xc1, 0x70, 0xe0, 0x8b, 0x04, 0xdb, 0x27, - 0x96, 0x44, 0xe2, 0x55, 0xf1, 0x58, 0x34, 0x4d, 0xa4, 0x34, 0x56, 0x13, - 0xd1, 0xee, 0x25, 0xcd, 0xf3, 0xb3, 0x57, 0xa7, 0x86, 0xa9, 0x04, 0xba, - 0x31, 0xd6, 0x44, 0xf9, 0x7f, 0x1d, 0xcd, 0x3c, 0x03, 0x52, 0x9b, 0x59, - 0x4a, 0x52, 0x53, 0x3d, 0x27, 0x1e, 0xfb, 0xf9, 0xcd, 0xd6, 0x7a, 0xba, - 0xb0, 0xa9, 0x44, 0xa7, 0x8f, 0xb3, 0x93, 0xcc, 0x20, 0xee, 0xa4, 0x12, - 0x14, 0x34, 0xd6, 0x4c, 0xda, 0x58, 0x16, 0x56, 0x09, 0x45, 0x7d, 0x28, - 0x3f, 0x05, 0x19, 0xe1, 0x1d, 0xc2, 0x65, 0xad, 0x69, 0xa6, 0x4d, 0xae, - 0xc8, 0xc3, 0x40, 0xe3, 0x84, 0x07, 0x82, 0x2a, 0x77, 0x46, 0xb0, 0x56, - 0x87, 0x58, 0xa6, 0x4b, 0x32, 0x32, 0x6b, 0x10, 0xe4, 0xeb, 0xb9, 0xca, - 0x68, 0xb2, 0xf7, 0xa6, 0x57, 0xaa, 0xef, 0xbb, 0xd5, 0xd8, 0x43, 0xfc, - 0x49, 0x20, 0xf6, 0x3e, 0x2d, 0x53, 0x8d, 0x59, 0x16, 0x51, 0x18, 0x3b, - 0x58, 0x1b, 0xff, 0xf6, 0x2e, 0xd4, 0xa2, 0xb8, 0xf0, 0xa8, 0xb8, 0xa7, - 0x2b, 0xb5, 0x09, 0xcf, 0x10, 0xf1, 0x8d, 0x15, 0x78, 0x36, 0x56, 0x4e, - 0x2f, 0x59, 0x37, 0x55, 0x16, 0x43, 0xd1, 0x25, 0x41, 0x02, 0x53, 0xde, - 0xfd, 0xbf, 0x47, 0xac, 0x82, 0xa6, 0x91, 0xaf, 0x06, 0xc6, 0x18, 0xe6, - 0x7a, 0x0a, 0x20, 0x2d, 0x40, 0x48, 0x69, 0x57, 0x02, 0x58, 0x03, 0x4a, - 0xb3, 0x2f, 0x78, 0x0d, 0x00, 0xe9, 0x5b, 0xc8, 0xf4, 0xb0, 0xaf, 0xa6, - 0x43, 0xab, 0xe6, 0xbd, 0x8f, 0xdb, 0x39, 0xff, 0x11, 0x23, 0x0a, 0x41, - 0x3d, 0x54, 0x6c, 0x59, 0xc2, 0x4f, 0xd6, 0x38, 0x79, 0x18, 0x0a, 0xf4, - 0x9a, 0xd1, 0xde, 0xb6, 0x48, 0xa8, 0x48, 0xa8, 0xd7, 0xb6, 0x93, 0xd1, - 0x00, 0xf4, 0x72, 0x18, 0xcd, 0x38, 0xbe, 0x4f, 0x6c, 0x59, 0x3e, 0x54, - 0x14, 0x41, 0x16, 0x23, 0x45, 0xff, 0x96, 0xdb, 0xec, 0xbd, 0x48, 0xab, - 0xac, 0xa6, 0xf0, 0xb0, 0x55, 0xc8, 0xf5, 0xe8, 0x72, 0x0d, 0xa8, 0x2f, - 0x00, 0x4a, 0xff, 0x57, 0x6b, 0x57, 0x47, 0x48, 0x26, 0x2d, 0x85, 0x0a, - 0x20, 0xe6, 0x0d, 0xc6, 0x97, 0xaf, 0x7f, 0xa6, 0x46, 0xac, 0xf7, 0xbf, - 0x48, 0xde, 0x3a, 0x02, 0xc7, 0x25, 0x10, 0x43, 0x35, 0x55, 0x30, 0x59, - 0x58, 0x4e, 0x83, 0x36, 0x94, 0x15, 0x19, 0xf1, 0x12, 0xcf, 0x2f, 0xb5, - 0xba, 0xa7, 0xee, 0xa8, 0x9c, 0xb8, 0x26, 0xd4, 0xf7, 0xf6, 0x4c, 0x1b, - 0x14, 0x3b, 0x10, 0x51, 0x8e, 0x59, 0x31, 0x53, 0xfa, 0x3e, 0x55, 0x20, - 0x49, 0xfc, 0xe0, 0xd8, 0xf4, 0xbb, 0x5a, 0xaa, 0xf6, 0xa6, 0x64, 0xb2, - 0xaf, 0xca, 0xdf, 0xeb, 0x5e, 0x10, 0x2d, 0x32, 0xa0, 0x4b, 0x86, 0x58, - 0xb2, 0x56, 0x7d, 0x46, 0x89, 0x2a, 0x8f, 0x07, 0x48, 0xe3, 0xcf, 0xc3, - 0x52, 0xae, 0x66, 0xa6, 0x64, 0xad, 0x14, 0xc2, 0x14, 0xe1, 0x32, 0x05, - 0x77, 0x28, 0x01, 0x45, 0x15, 0x56, 0xda, 0x58, 0xdc, 0x4c, 0x1b, 0x34, - 0xad, 0x12, 0x2a, 0xee, 0x9a, 0xcc, 0x94, 0xb3, 0x45, 0xa7, 0xae, 0xa9, - 0x74, 0xba, 0xc5, 0xd6, 0xf2, 0xf9, 0x1e, 0x1e, 0x4b, 0x3d, 0x48, 0x52, - 0x9a, 0x59, 0x07, 0x52, 0xd6, 0x3c, 0x83, 0x1d, 0x53, 0xf9, 0x33, 0xd6, - 0x10, 0xba, 0x84, 0xa9, 0x59, 0xa7, 0xed, 0xb3, 0x1c, 0xcd, 0xc9, 0xee, - 0x4d, 0x13, 0x9d, 0x34, 0x2f, 0x4d, 0xef, 0x58, 0xe6, 0x55, 0x9a, 0x44, - 0xe7, 0x27, 0x90, 0x04, 0x7c, 0xe0, 0x9f, 0xc1, 0x26, 0xad, 0x68, 0xa6, - 0x99, 0xae, 0x43, 0xc4, 0xe5, 0xe3, 0x2c, 0x08, 0x1b, 0x2b, 0xdf, 0x46, - 0xdc, 0x56, 0x6a, 0x58, 0x4b, 0x4b, 0xa3, 0x31, 0xc4, 0x0f, 0x3b, 0xeb, - 0x34, 0xca, 0x0d, 0xb2, 0xea, 0xa6, 0x86, 0xaa, 0x60, 0xbc, 0x71, 0xd9, - 0xeb, 0xfc, 0xeb, 0x20, 0x6e, 0x3f, 0x6c, 0x53, 0x89, 0x59, 0xc9, 0x50, - 0x9b, 0x3a, 0xb0, 0x1a, 0x59, 0xf6, 0x96, 0xd3, 0x3d, 0xb8, 0xc6, 0xa8, - 0xd8, 0xa7, 0x88, 0xb5, 0x9b, 0xcf, 0xb7, 0xf1, 0x35, 0x16, 0xfd, 0x36, - 0xac, 0x4e, 0x3b, 0x59, 0x05, 0x55, 0xa2, 0x42, 0x36, 0x25, 0x96, 0x01, - 0xb4, 0xdd, 0x86, 0xbf, 0x0b, 0xac, 0x89, 0xa6, 0xdf, 0xaf, 0x87, 0xc6, - 0xbf, 0xe6, 0x21, 0x0b, 0xb5, 0x2d, 0xa6, 0x48, 0x8c, 0x57, 0xe4, 0x57, - 0x9e, 0x49, 0x25, 0x2f, 0xcc, 0x0c, 0x5e, 0xe8, 0xd3, 0xc7, 0xa5, 0xb0, - 0xa0, 0xa6, 0x7c, 0xab, 0x5b, 0xbe, 0x2b, 0xdc, 0xe4, 0xff, 0xaf, 0x23, - 0x7f, 0x41, 0x77, 0x54, 0x61, 0x59, 0x72, 0x4f, 0x52, 0x38, 0xd4, 0x17, - 0x61, 0xf3, 0x07, 0xd1, 0x7b, 0xb6, 0x28, 0xa8, 0x68, 0xa8, 0x3e, 0xb7, - 0x22, 0xd2, 0xad, 0xf4, 0x15, 0x19, 0x52, 0x39, 0x0b, 0x50, 0x76, 0x59, - 0x03, 0x54, 0xa0, 0x40, 0x75, 0x22, 0x9e, 0xfe, 0xf5, 0xda, 0x7d, 0xbd, - 0x0d, 0xab, 0xbe, 0xa6, 0x3f, 0xb1, 0xdc, 0xc8, 0x9b, 0xe9, 0x1a, 0x0e, - 0x3a, 0x30, 0x60, 0x4a, 0x1d, 0x58, 0x46, 0x57, 0xe0, 0x47, 0x93, 0x2c, - 0xda, 0x09, 0x7d, 0xe5, 0x89, 0xc5, 0x4e, 0xaf, 0x76, 0xa6, 0x86, 0xac, - 0x6c, 0xc0, 0xea, 0xde, 0xe4, 0x02, 0x61, 0x26, 0x83, 0x43, 0x68, 0x55, - 0x1f, 0x59, 0x07, 0x4e, 0xf6, 0x35, 0xf1, 0x14, 0x70, 0xf0, 0x82, 0xce, - 0xd3, 0xb4, 0x9b, 0xa7, 0x19, 0xa9, 0x03, 0xb9, 0xbc, 0xd4, 0xa1, 0xf7, - 0xf0, 0x1b, 0x94, 0x3b, 0x57, 0x51, 0x95, 0x59, 0xed, 0x52, 0x85, 0x3e, - 0xb0, 0x1f, 0xa2, 0xfb, 0x42, 0xd8, 0x89, 0xbb, 0x26, 0xaa, 0x0b, 0xa7, - 0xb9, 0xb2, 0x3a, 0xcb, 0x85, 0xec, 0x08, 0x11, 0xb9, 0x32, 0xfb, 0x4b, - 0xa0, 0x58, 0x87, 0x56, 0x13, 0x46, 0xf3, 0x29, 0xe2, 0x06, 0xa7, 0xe2, - 0x51, 0xc3, 0x0a, 0xae, 0x6a, 0xa6, 0xa1, 0xad, 0x94, 0xc2, 0xb2, 0xe1, - 0xdf, 0x05, 0x0f, 0x29, 0x6e, 0x45, 0x43, 0x56, 0xc4, 0x58, 0x84, 0x4c, - 0x8d, 0x33, 0x09, 0x12, 0x7e, 0xed, 0x11, 0xcc, 0x3b, 0xb3, 0x2b, 0xa7, - 0xe1, 0xa9, 0xdc, 0xba, 0x62, 0xd7, 0x99, 0xfa, 0xc0, 0x1e, 0xc9, 0x3d, - 0x89, 0x52, 0x9c, 0x59, 0xbe, 0x51, 0x5a, 0x3c, 0xe0, 0x1c, 0xaa, 0xf8, - 0x9a, 0xd5, 0xa6, 0xb9, 0x57, 0xa9, 0x74, 0xa7, 0x46, 0xb4, 0xab, 0xcd, - 0x71, 0xef, 0xf2, 0x13, 0x2a, 0x35, 0x84, 0x4d, 0x03, 0x59, 0xb6, 0x55, - 0x2a, 0x44, 0x4e, 0x27, 0xe5, 0x03, 0xdc, 0xdf, 0x25, 0xc1, 0xe3, 0xac, - 0x70, 0xa6, 0xdc, 0xae, 0xc7, 0xc4, 0x86, 0xe4, 0xd8, 0x08, 0xaf, 0x2b, - 0x48, 0x47, 0x06, 0x57, 0x4c, 0x58, 0xf0, 0x4a, 0x13, 0x31, 0x1a, 0x0f, - 0x98, 0xea, 0xa6, 0xc9, 0xbf, 0xb1, 0xd4, 0xa6, 0xbd, 0xaa, 0xcf, 0xbc, - 0x0d, 0xda, 0x96, 0xfd, 0x89, 0x21, 0xe9, 0x3f, 0xa7, 0x53, 0x85, 0x59, - 0x7c, 0x50, 0x1a, 0x3a, 0x0e, 0x1a, 0xac, 0xf5, 0x04, 0xd3, 0xd4, 0xb7, - 0xa3, 0xa8, 0xf4, 0xa7, 0xeb, 0xb5, 0x27, 0xd0, 0x65, 0xf2, 0xd6, 0x16, - 0x89, 0x37, 0xf9, 0x4e, 0x4d, 0x59, 0xcc, 0x54, 0x30, 0x42, 0x9b, 0x24, - 0xe8, 0x00, 0x19, 0xdd, 0x0d, 0xbf, 0xd2, 0xab, 0x91, 0xa6, 0x2c, 0xb0, - 0x0d, 0xc7, 0x5f, 0xe7, 0xd1, 0x0b, 0x41, 0x2e, 0x0f, 0x49, 0xad, 0x57, - 0xc2, 0x57, 0x3e, 0x49, 0x91, 0x2e, 0x23, 0x0c, 0xba, 0xe7, 0x4b, 0xc7, - 0x59, 0xb0, 0x94, 0xa6, 0xb4, 0xab, 0xd3, 0xbe, 0xc4, 0xdc, 0x94, 0x00, - 0x48, 0x24, 0xf5, 0x41, 0xb0, 0x54, 0x53, 0x59, 0x24, 0x4f, 0xcb, 0x37, - 0x2f, 0x17, 0xb8, 0xf2, 0x76, 0xd0, 0x19, 0xb6, 0x06, 0xa8, 0x8e, 0xa8, - 0xa2, 0xb7, 0xb7, 0xd2, 0x54, 0xf5, 0xbc, 0x19, 0xd2, 0x39, 0x5b, 0x50, - 0x7d, 0x59, 0xc8, 0x53, 0x27, 0x40, 0xd8, 0x21, 0xf2, 0xfd, 0x5a, 0xda, - 0x0b, 0xbd, 0xd7, 0xaa, 0xcd, 0xa6, 0x92, 0xb1, 0x62, 0xc9, 0x42, 0xea, - 0xc2, 0x0e, 0xcc, 0x30, 0xbb, 0x4a, 0x41, 0x58, 0x19, 0x57, 0x7d, 0x47, - 0xfc, 0x2b, 0x30, 0x09, 0xda, 0xe4, 0x09, 0xc5, 0x01, 0xaf, 0x73, 0xa6, - 0xc2, 0xac, 0xe8, 0xc0, 0x88, 0xdf, 0x8e, 0x03, 0xfd, 0x26, 0xf3, 0x43, - 0x9a, 0x55, 0x0f, 0x59, 0xb0, 0x4d, 0x6e, 0x35, 0x4b, 0x14, 0xc5, 0xef, - 0xf7, 0xcd, 0x73, 0xb4, 0x83, 0xa7, 0x40, 0xa9, 0x6f, 0xb9, 0x50, 0xd5, - 0x4d, 0xf8, 0x91, 0x1c, 0x15, 0x3c, 0x9e, 0x51, 0x97, 0x59, 0xad, 0x52, - 0x08, 0x3e, 0x12, 0x1f, 0xf4, 0xfa, 0xac, 0xd7, 0x19, 0xbb, 0xf5, 0xa9, - 0x23, 0xa7, 0x0d, 0xb3, 0xc7, 0xcb, 0x2c, 0xed, 0xaf, 0x11, 0x48, 0x33, - 0x54, 0x4c, 0xb9, 0x58, 0x59, 0x56, 0xa8, 0x45, 0x5c, 0x29, 0x37, 0x06, - 0x06, 0xe2, 0xd2, 0xc2, 0xc6, 0xad, 0x69, 0xa6, 0xe5, 0xad, 0x13, 0xc3, - 0x51, 0xe2, 0x8b, 0x06, 0xa7, 0x29, 0xd9, 0x45, 0x72, 0x56, 0xab, 0x58, - 0x2a, 0x4c, 0x03, 0x33, 0x5e, 0x11, 0xd9, 0xec, 0x85, 0xcb, 0xe3, 0xb2, - 0x18, 0xa7, 0x0c, 0xaa, 0x4e, 0xbb, 0xf7, 0xd7, 0x47, 0xfb, 0x61, 0x1f, - 0x42, 0x3e, 0xcf, 0x52, 0x93, 0x59, 0x7e, 0x51, 0xd6, 0x3b, 0x42, 0x1c, - 0xfb, 0xf7, 0x06, 0xd5, 0x3c, 0xb9, 0x2b, 0xa9, 0x91, 0xa7, 0x9f, 0xb4, - 0x3c, 0xce, 0x18, 0xf0, 0x99, 0x14, 0xb3, 0x35, 0xd9, 0x4d, 0x17, 0x59, - 0x84, 0x55, 0xb8, 0x43, 0xb6, 0x26, 0x38, 0x03, 0x3d, 0xdf, 0xad, 0xc0, - 0xa1, 0xac, 0x77, 0xa6, 0x25, 0xaf, 0x47, 0xc5, 0x2b, 0xe5, 0x80, 0x09, - 0x46, 0x2c, 0xaf, 0x47, 0x2c, 0x57, 0x33, 0x58, 0x8c, 0x4a, 0x87, 0x30, - 0x6f, 0x0e, 0xf3, 0xe9, 0x1f, 0xc9, 0x6b, 0xb1, 0xc5, 0xa6, 0xf0, 0xaa, - 0x44, 0xbd, 0xa4, 0xda, 0x46, 0xfe, 0x25, 0x22, 0x60, 0x40, 0xe5, 0x53, - 0x7b, 0x59, 0x33, 0x50, 0x95, 0x39, 0x6b, 0x19, 0x00, 0xf5, 0x73, 0xd2, - 0x6d, 0xb7, 0x7f, 0xa8, 0x13, 0xa8, 0x4d, 0xb6, 0xb7, 0xd0, 0x10, 0xf3, - 0x79, 0x17, 0x11, 0x38, 0x47, 0x4f, 0x5d, 0x59, 0x91, 0x54, 0xbf, 0x41, - 0xfc, 0x23, 0x3e, 0x00, 0x7c, 0xdc, 0x96, 0xbe, 0x99, 0xab, 0x9c, 0xa6, - 0x7a, 0xb0, 0x92, 0xc7, 0x05, 0xe8, 0x79, 0x0c, 0xd5, 0x2e, 0x6f, 0x49, - 0xd2, 0x57, 0x9e, 0x57, 0xdc, 0x48, 0xfc, 0x2d, 0x7c, 0x0b, 0x12, 0xe7, - 0xcb, 0xc6, 0x07, 0xb0, 0x8c, 0xa6, 0xee, 0xab, 0x48, 0xbf, 0x64, 0xdd, - 0x3d, 0x01, 0xe6, 0x24, 0x67, 0x42, 0xe8, 0x54, 0x45, 0x59, 0xd4, 0x4e, - 0x44, 0x37, 0x89, 0x16, 0x0f, 0xf2, 0xe5, 0xcf, 0xb9, 0xb5, 0xe7, 0xa7, - 0xb4, 0xa8, 0x06, 0xb8, 0x4c, 0xd3, 0xff, 0xf5, 0x5d, 0x1a, 0x59, 0x3a, - 0xa2, 0x50, 0x86, 0x59, 0x8c, 0x53, 0xac, 0x3f, 0x3e, 0x21, 0x41, 0xfd, - 0xc3, 0xd9, 0x98, 0xbc, 0xa3, 0xaa, 0xde, 0xa6, 0xe4, 0xb1, 0xeb, 0xc9, - 0xe9, 0xea, 0x6a, 0x0f, 0x5b, 0x31, 0x1a, 0x4b, 0x5c, 0x58, 0xf3, 0x56, - 0x13, 0x47, 0x69, 0x2b, 0x83, 0x08, 0x38, 0xe4, 0x88, 0xc4, 0xb9, 0xae, - 0x6f, 0xa6, 0x01, 0xad, 0x61, 0xc1, 0x2a, 0xe0, 0x37, 0x04, 0x9a, 0x27, - 0x5f, 0x44, 0xce, 0x55, 0xfa, 0x58, 0x5b, 0x4d, 0xe5, 0x34, 0xa3, 0x13, - 0x1d, 0xef, 0x68, 0xcd, 0x1a, 0xb4, 0x65, 0xa7, 0x71, 0xa9, 0xd3, 0xb9, - 0xeb, 0xd5, 0xf5, 0xf8, 0x36, 0x1d, 0x90, 0x3c, 0xe8, 0x51, 0x95, 0x59, - 0x6e, 0x52, 0x89, 0x3d, 0x72, 0x1e, 0x4a, 0xfa, 0x12, 0xd7, 0xad, 0xba, - 0xc6, 0xa9, 0x37, 0xa7, 0x67, 0xb3, 0x53, 0xcc, 0xd2, 0xed, 0x58, 0x12, - 0xd3, 0x33, 0xad, 0x4c, 0xd1, 0x58, 0x2b, 0x56, 0x3b, 0x45, 0xc4, 0x28, - 0x8c, 0x05, 0x65, 0xe1, 0x55, 0xc2, 0x84, 0xad, 0x68, 0xa6, 0x2d, 0xae, - 0x8d, 0xc3, 0xf6, 0xe2, 0x36, 0x07, 0x3b, 0x2a, 0x48, 0x46, 0x9b, 0x56, - 0x93, 0x58, 0xd0, 0x4b, 0x75, 0x32, 0xb4, 0x10, 0x36, 0xec, 0xf6, 0xca, - 0x8f, 0xb2, 0x03, 0xa7, 0x3c, 0xaa, 0xbe, 0xbb, 0x90, 0xd8, 0xf1, 0xfb, - 0x02, 0x20, 0xbc, 0x3e, 0x10, 0x53, 0x92, 0x59, 0x32, 0x51, 0x59, 0x3b, - 0x9e, 0x1b, 0x50, 0xf7, 0x74, 0xd4, 0xcd, 0xb8, 0x08, 0xa9, 0xa8, 0xa7, - 0xff, 0xb4, 0xca, 0xce, 0xbf, 0xf0, 0x43, 0x15, 0x38, 0x36, 0x30, 0x4e, - 0x28, 0x59, 0x4e, 0x55, 0x4c, 0x43, 0x16, 0x26, 0x91, 0x02, 0x9c, 0xde, - 0x34, 0xc0, 0x65, 0xac, 0x7b, 0xa6, 0x71, 0xaf, 0xc9, 0xc5, 0xcc, 0xe5, - 0x2d, 0x0a, 0xdb, 0x2c, 0x12, 0x48, 0x58, 0x57, 0x0f, 0x58, 0x30, 0x4a, - 0xf5, 0x2f, 0xc7, 0x0d, 0x4c, 0xe9, 0x99, 0xc8, 0x19, 0xb1, 0xb5, 0xa6, - 0x29, 0xab, 0xb4, 0xbd, 0x42, 0xdb, 0xf0, 0xfe, 0xc3, 0x22, 0xd8, 0x40, - 0x20, 0x54, 0x71, 0x59, 0xe6, 0x4f, 0x12, 0x39, 0xc6, 0x18, 0x58, 0xf4, - 0xdc, 0xd1, 0x0e, 0xb7, 0x56, 0xa8, 0x39, 0xa8, 0xab, 0xb6, 0x4c, 0xd1, - 0xb7, 0xf3, 0x21, 0x18, 0x93, 0x38, 0x99, 0x4f, 0x67, 0x59, 0x5a, 0x54, - 0x49, 0x41, 0x5e, 0x23, 0x96, 0xff, 0xda, 0xdb, 0x27, 0xbe, 0x5c, 0xab, - 0xab, 0xa6, 0xc8, 0xb0, 0x18, 0xc8, 0xa9, 0xe8, 0x22, 0x0d, 0x69, 0x2f, - 0xcf, 0x49, 0xf5, 0x57, 0x77, 0x57, 0x79, 0x48, 0x69, 0x2d, 0xd3, 0x0a, - 0x6c, 0xe6, 0x48, 0xc6, 0xbb, 0xaf, 0x82, 0xa6, 0x2c, 0xac, 0xbc, 0xbf, - 0x04, 0xde, 0xe8, 0x01, 0x80, 0x25, 0xdd, 0x42, 0x1a, 0x55, 0x39, 0x59, - 0x80, 0x4e, 0xbf, 0x36, 0xe1, 0x15, 0x68, 0xf1, 0x52, 0xcf, 0x5c, 0xb5, - 0xc7, 0xa7, 0xdc, 0xa8, 0x6d, 0xb8, 0xdf, 0xd3, 0xab, 0xf6, 0xff, 0x1a, - 0xda, 0x3a, 0xee, 0x50, 0x8b, 0x59, 0x4e, 0x53, 0x33, 0x3f, 0x9d, 0x20, - 0x99, 0xfc, 0x26, 0xd9, 0x28, 0xbc, 0x71, 0xaa, 0xed, 0xa6, 0x3c, 0xb2, - 0x72, 0xca, 0x8f, 0xeb, 0x14, 0x10, 0xe9, 0x31, 0x76, 0x4b, 0x79, 0x58, - 0xc9, 0x56, 0xa9, 0x46, 0xd4, 0x2a, 0xd8, 0x07, 0x95, 0xe3, 0x0a, 0xc4, - 0x70, 0xae, 0x6d, 0xa6, 0x40, 0xad, 0xde, 0xc1, 0xc8, 0xe0, 0xe5, 0x04, - 0x30, 0x28, 0xd0, 0x44, 0xfd, 0x55, 0xe5, 0x58, 0x05, 0x4d, 0x5a, 0x34, - 0xfa, 0x12, 0x79, 0xee, 0xd7, 0xcc, 0xc1, 0xb3, 0x4e, 0xa7, 0x99, 0xa9, - 0x43, 0xba, 0x80, 0xd6, 0xa0, 0xf9, 0xd8, 0x1d, 0x0e, 0x3d, 0x2b, 0x52, - 0x9a, 0x59, 0x25, 0x52, 0x10, 0x3d, 0xcf, 0x1d, 0x9f, 0xf9, 0x7c, 0xd6, - 0x3f, 0xba, 0x98, 0xa9, 0x50, 0xa7, 0xc0, 0xb3, 0xde, 0xcc, 0x7d, 0xee, - 0xfc, 0x12, 0x60, 0x34, 0x05, 0x4d, 0xe6, 0x58, 0xfd, 0x55, 0xcd, 0x44, - 0x2a, 0x28, 0xe3, 0x04, 0xc3, 0xe0, 0xda, 0xc1, 0x41, 0xad, 0x6a, 0xa6, - 0x73, 0xae, 0x0e, 0xc4, 0x97, 0xe3, 0xde, 0x07, 0xd7, 0x2a, 0xac, 0x46, - 0xcb, 0x56, 0x77, 0x58, 0x73, 0x4b, 0xe8, 0x31, 0x0e, 0x10, 0x8b, 0xeb, - 0x70, 0xca, 0x37, 0xb2, 0xf0, 0xa6, 0x71, 0xaa, 0x2a, 0xbc, 0x2b, 0xd9, - 0x9d, 0xfc, 0xa0, 0x20, 0x39, 0x3f, 0x4c, 0x53, 0x8e, 0x59, 0xea, 0x50, - 0xd7, 0x3a, 0xfc, 0x1a, 0xa6, 0xf6, 0xdb, 0xd3, 0x6c, 0xb8, 0xda, 0xa8, - 0xc7, 0xa7, 0x5f, 0xb5, 0x55, 0xcf, 0x6e, 0xf1, 0xe4, 0x15, 0xc3, 0x36, - 0x82, 0x4e, 0x38, 0x59, 0x1a, 0x55, 0xd9, 0x42, 0x7c, 0x25, 0xe5, 0x01, - 0xff, 0xdd, 0xb9, 0xbf, 0x2b, 0xac, 0x81, 0xa6, 0xbe, 0xaf, 0x4a, 0xc6, - 0x72, 0xe6, 0xd5, 0x0a, 0x6f, 0x2d, 0x7a, 0x48, 0x79, 0x57, 0xf3, 0x57, - 0xcd, 0x49, 0x65, 0x2f, 0x1e, 0x0d, 0xa6, 0xe8, 0x12, 0xc8, 0xc9, 0xb0, - 0xa8, 0xa6, 0x60, 0xab, 0x28, 0xbe, 0xdf, 0xdb, 0x99, 0xff, 0x63, 0x23, - 0x4d, 0x41, 0x59, 0x54, 0x6a, 0x59, 0x93, 0x4f, 0x92, 0x38, 0x1d, 0x18, - 0xb2, 0xf3, 0x49, 0xd1, 0xaa, 0xb6, 0x33, 0xa8, 0x5d, 0xa8, 0x0b, 0xb7, - 0xe4, 0xd1, 0x5b, 0xf4, 0xca, 0x18, 0x15, 0x39, 0xe8, 0x4f, 0x71, 0x59, - 0x20, 0x54, 0xd4, 0x40, 0xbe, 0x22, 0xee, 0xfe, 0x3b, 0xdb, 0xb5, 0xbd, - 0x24, 0xab, 0xb7, 0xa6, 0x1b, 0xb1, 0x9c, 0xc8, 0x51, 0xe9, 0xca, 0x0d, - 0xfa, 0x2f, 0x30, 0x4a, 0x14, 0x58, 0x52, 0x57, 0x14, 0x48, 0xd5, 0x2c, - 0x28, 0x0a, 0xc9, 0xe5, 0xc6, 0xc5, 0x6d, 0xaf, 0x7e, 0xa6, 0x64, 0xac, - 0x39, 0xc0, 0x9f, 0xde, 0x94, 0x02, 0x1d, 0x26, 0x4b, 0x43, 0x53, 0x55, - 0x25, 0x59, 0x2f, 0x4e, 0x35, 0x36, 0x3d, 0x15, 0xbd, 0xf0, 0xc5, 0xce, - 0xfd, 0xb4, 0xa9, 0xa7, 0x04, 0xa9, 0xd6, 0xb8, 0x74, 0xd4, 0x56, 0xf7, - 0xa1, 0x1b, 0x5c, 0x3b, 0x34, 0x51, 0x95, 0x59, 0x09, 0x53, 0xbe, 0x3e, - 0xf9, 0x1f, 0xf2, 0xfb, 0x88, 0xd8, 0xbd, 0xbb, 0x39, 0xaa, 0x06, 0xa7, - 0x8e, 0xb2, 0xfe, 0xca, 0x35, 0xec, 0xbd, 0x10, 0x75, 0x32, 0xd5, 0x4b, - 0x91, 0x58, 0x9e, 0x56, 0x40, 0x46, 0x3d, 0x2a, 0x2d, 0x07, 0xf5, 0xe2, - 0x87, 0xc3, 0x2e, 0xae, 0x65, 0xa6, 0x89, 0xad, 0x55, 0xc2, 0x6c, 0xe1, - 0x8d, 0x05, 0xcb, 0x28, 0x3b, 0x45, 0x2f, 0x56, 0xcd, 0x58, 0xae, 0x4c, - 0xce, 0x33, 0x54, 0x12, 0xce, 0xed, 0x4e, 0xcc, 0x66, 0xb3, 0x36, 0xa7, - 0xc9, 0xa9, 0xad, 0xba, 0x18, 0xd7, 0x4e, 0xfa, 0x74, 0x1e, 0x90, 0x3d, - 0x6c, 0x52, 0x99, 0x59, 0xe2, 0x51, 0x91, 0x3c, 0x2e, 0x1d, 0xf4, 0xf8, - 0xe4, 0xd5, 0xd4, 0xb9, 0x6c, 0xa9, 0x6a, 0xa7, 0x17, 0xb4, 0x70, 0xcd, - 0x1e, 0xef, 0xab, 0x13, 0xe4, 0x34, 0x61, 0x4d, 0xf7, 0x58, 0xd0, 0x55, - 0x5a, 0x44, 0x97, 0x27, 0x31, 0x04, 0x28, 0xe0, 0x5c, 0xc1, 0x02, 0xad, - 0x6d, 0xa6, 0xbb, 0xae, 0x8c, 0xc4, 0x3b, 0xe4, 0x88, 0x08, 0x6d, 0x2b, - 0x16, 0x47, 0xf3, 0x56, 0x5d, 0x58, 0x15, 0x4b, 0x59, 0x31, 0x66, 0x0f, - 0xe5, 0xea, 0xe6, 0xc9, 0xe3, 0xb1, 0xde, 0xa6, 0xa3, 0xaa, 0x9e, 0xbc, - 0xc2, 0xd9, 0x4a, 0xfd, 0x3f, 0x21, 0xb0, 0x3f, 0x8e, 0x53, 0x85, 0x59, - 0xa1, 0x50, 0x56, 0x3a, 0x57, 0x1a, 0xfd, 0xf5, 0x45, 0xd3, 0x06, 0xb8, - 0xb3, 0xa8, 0xe6, 0xa7, 0xbd, 0xb5, 0xe8, 0xcf, 0x12, 0xf2, 0x90, 0x16, - 0x44, 0x37, 0xd9, 0x4e, 0x44, 0x59, 0xe6, 0x54, 0x66, 0x42, 0xe0, 0x24, - 0x3b, 0x01, 0x5e, 0xdd, 0x46, 0xbf, 0xec, 0xab, 0x8d, 0xa6, 0x08, 0xb0, - 0xcf, 0xc6, 0x17, 0xe7, 0x7e, 0x0b, 0x03, 0x2e, 0xdc, 0x48, 0x9f, 0x57, - 0xd1, 0x57, 0x6c, 0x49, 0xd4, 0x2e, 0x72, 0x0c, 0x04, 0xe8, 0x8a, 0xc7, - 0x7b, 0xb0, 0x9b, 0xa6, 0x99, 0xab, 0x9d, 0xbe, 0x7c, 0xdc, 0x45, 0x00, - 0xff, 0x23, 0xc1, 0x41, 0x94, 0x54, 0x5b, 0x59, 0x48, 0x4f, 0x09, 0x38, - 0x7b, 0x17, 0x04, 0xf3, 0xbb, 0xd0, 0x45, 0xb6, 0x16, 0xa8, 0x7d, 0xa8, - 0x71, 0xb7, 0x76, 0xd2, 0x04, 0xf5, 0x72, 0x19, 0x94, 0x39, 0x39, 0x50, - 0x78, 0x59, 0xe5, 0x53, 0x5e, 0x40, 0x1f, 0x22, 0x43, 0xfe, 0x9f, 0xda, - 0x42, 0xbd, 0xee, 0xaa, 0xc7, 0xa6, 0x6b, 0xb1, 0x24, 0xc9, 0xf5, 0xe9, - 0x75, 0x0e, 0x8a, 0x30, 0x8f, 0x4a, 0x33, 0x58, 0x2b, 0x57, 0xae, 0x47, - 0x40, 0x2c, 0x7e, 0x09, 0x26, 0xe5, 0x42, 0xc5, 0x27, 0xaf, 0x72, 0xa6, - 0xa7, 0xac, 0xaf, 0xc0, 0x3f, 0xdf, 0x40, 0x03, 0xb5, 0x26, 0xc0, 0x43, - 0x82, 0x55, 0x19, 0x59, 0xd5, 0x4d, 0xb0, 0x35, 0x95, 0x14, 0x15, 0xf0, - 0x36, 0xce, 0xa0, 0xb4, 0x8c, 0xa7, 0x31, 0xa9, 0x3a, 0xb9, 0x0d, 0xd5, - 0xfe, 0xf7, 0x46, 0x1c, 0xdc, 0x3b, 0x7b, 0x51, 0x97, 0x59, 0xcb, 0x52, - 0x40, 0x3e, 0x5e, 0x1f, 0x40, 0xfb, 0xf6, 0xd7, 0x47, 0xbb, 0x10, 0xaa, - 0x15, 0xa7, 0xe7, 0xb2, 0x87, 0xcb, 0xdd, 0xec, 0x65, 0x11, 0x03, 0x33, - 0x2f, 0x4c, 0xaa, 0x58, 0x71, 0x56, 0xd7, 0x45, 0xa3, 0x29, 0x86, 0x06, - 0x4f, 0xe2, 0x0c, 0xc3, 0xe7, 0xad, 0x67, 0xa6, 0xc8, 0xad, 0xd6, 0xc2, - 0x0a, 0xe2, 0x3a, 0x06, 0x63, 0x29, 0xa6, 0x45, 0x5f, 0x56, 0xb3, 0x58, - 0x58, 0x4c, 0x3e, 0x33, 0xb1, 0x11, 0x22, 0xed, 0xc7, 0xcb, 0x09, 0xb3, - 0x24, 0xa7, 0xf4, 0xa9, 0x1e, 0xbb, 0xae, 0xd7, 0xfa, 0xfa, 0x15, 0x1f, - 0x0b, 0x3e, 0xaf, 0x52, 0x97, 0x59, 0x9d, 0x51, 0x10, 0x3c, 0x8e, 0x1c, - 0x48, 0xf8, 0x4d, 0xd5, 0x6b, 0xb9, 0x40, 0xa9, 0x83, 0xa7, 0x76, 0xb4, - 0xfa, 0xcd, 0xc8, 0xef, 0x52, 0x14, 0x6e, 0x35, 0xb7, 0x4d, 0x0b, 0x59, - 0x9b, 0x55, 0xf0, 0x43, 0xf7, 0x26, 0x8e, 0x03, 0x80, 0xdf, 0xe8, 0xc0, - 0xbe, 0xac, 0x73, 0xa6, 0x04, 0xaf, 0x0d, 0xc5, 0xdc, 0xe4, 0x37, 0x09, - 0xfc, 0x2b, 0x84, 0x47, 0x17, 0x57, 0x42, 0x58, 0xb8, 0x4a, 0xc8, 0x30, - 0xbf, 0x0e, 0x3d, 0xea, 0x5f, 0xc9, 0x90, 0xb1, 0xcc, 0xa6, 0xda, 0xaa, - 0x0c, 0xbd, 0x61, 0xda, 0xf2, 0xfd, 0xdf, 0x21, 0x29, 0x40, 0xca, 0x53, - 0x7d, 0x59, 0x58, 0x50, 0xcf, 0x39, 0xb8, 0x19, 0x4f, 0xf5, 0xb6, 0xd2, - 0x9b, 0xb7, 0x91, 0xa8, 0x03, 0xa8, 0x1f, 0xb6, 0x79, 0xd0, 0xbb, 0xf2, - 0x35, 0x17, 0xcc, 0x37, 0x28, 0x4f, 0x53, 0x59, 0xae, 0x54, 0xf3, 0x41, - 0x43, 0x24, 0x91, 0x00, 0xbe, 0xdc, 0xd2, 0xbe, 0xb1, 0xab, 0x96, 0xa6, - 0x59, 0xb0, 0x50, 0xc7, 0xbd, 0xe7, 0x27, 0x0c, 0x96, 0x2e, 0x40, 0x49, - 0xc2, 0x57, 0xaf, 0x57, 0x07, 0x49, 0x43, 0x2e, 0xc8, 0x0b, 0x60, 0xe7, - 0x05, 0xc7, 0x2d, 0xb0, 0x8e, 0xa6, 0xd6, 0xab, 0x10, 0xbf, 0x1b, 0xdd, - 0xef, 0x00, 0x9c, 0x24, 0x36, 0x42, 0xcb, 0x54, 0x4e, 0x59, 0xf7, 0x4e, - 0x84, 0x37, 0xd5, 0x16, 0x5c, 0xf2, 0x28, 0xd0, 0xe6, 0xb5, 0xf4, 0xa7, - 0xa3, 0xa8, 0xd8, 0xb7, 0x06, 0xd3, 0xb4, 0xf5, 0x0e, 0x1a, 0x1f, 0x3a, - 0x7e, 0x50, 0x85, 0x59, 0xa7, 0x53, 0xe4, 0x3f, 0x86, 0x21, 0x90, 0xfd, - 0x0b, 0xda, 0xcb, 0xbc, 0xbc, 0xaa, 0xd5, 0xa6, 0xbf, 0xb1, 0xab, 0xc9, - 0x9d, 0xea, 0x1b, 0x0f, 0x1c, 0x31, 0xeb, 0x4a, 0x54, 0x58, 0x00, 0x57, - 0x47, 0x47, 0xac, 0x2b, 0xd2, 0x08, 0x84, 0xe4, 0xc2, 0xc4, 0xda, 0xae, - 0x72, 0xa6, 0xe2, 0xac, 0x2b, 0xc1, 0xdd, 0xdf, 0xec, 0x03, 0x4f, 0x27, - 0x30, 0x44, 0xb5, 0x55, 0x03, 0x59, 0x83, 0x4d, 0x24, 0x35, 0xf0, 0x13, - 0x6b, 0xef, 0xaa, 0xcd, 0x41, 0xb4, 0x75, 0xa7, 0x57, 0xa9, 0xa8, 0xb9, - 0xa1, 0xd5, 0xab, 0xf8, 0xe6, 0x1c, 0x5c, 0x3c, 0xc1, 0x51, 0x9b, 0x59, - 0x88, 0x52, 0xc4, 0x3d, 0xbe, 0x1e, 0x95, 0xfa, 0x5d, 0xd7, 0xda, 0xba, - 0xde, 0xa9, 0x2c, 0xa7, 0x40, 0xb3, 0x11, 0xcc, 0x85, 0xed, 0x0c, 0x12, - 0x90, 0x33, 0x88, 0x4c, 0xc3, 0x58, 0x43, 0x56, 0x6b, 0x45, 0x0c, 0x29, - 0xd9, 0x05, 0xb0, 0xe1, 0x8f, 0xc2, 0xa1, 0xad, 0x69, 0xa6, 0x0b, 0xae, - 0x55, 0xc3, 0xab, 0xe2, 0xe6, 0x06, 0xf7, 0x29, 0x16, 0x46, 0x86, 0x56, - 0xa2, 0x58, 0xf6, 0x4b, 0xb8, 0x32, 0x03, 0x11, 0x80, 0xec, 0x37, 0xcb, - 0xb7, 0xb2, 0x0a, 0xa7, 0x29, 0xaa, 0x88, 0xbb, 0x4a, 0xd8, 0xa2, 0xfb, - 0xb9, 0x1f, 0x83, 0x3e, 0xf3, 0x52, 0x92, 0x59, 0x56, 0x51, 0x92, 0x3b, - 0xe9, 0x1b, 0xa0, 0xf7, 0xb6, 0xd4, 0x02, 0xb9, 0x16, 0xa9, 0x9e, 0xa7, - 0xd3, 0xb4, 0x88, 0xce, 0x72, 0xf0, 0xf6, 0x14, 0xfa, 0x35, 0x09, 0x4e, - 0x20, 0x59, 0x65, 0x55, 0x81, 0x43, 0x5e, 0x26, 0xdf, 0x02, 0xe6, 0xde, - 0x69, 0xc0, 0x84, 0xac, 0x77, 0xa6, 0x4e, 0xaf, 0x8f, 0xc5, 0x7f, 0xe5, - 0xe0, 0x09, 0x95, 0x2c, 0xe4, 0x47, 0x45, 0x57, 0x1f, 0x58, 0x5b, 0x4a, - 0x38, 0x30, 0x16, 0x0e, 0x96, 0xe9, 0xd9, 0xc8, 0x3d, 0xb1, 0xbc, 0xa6, - 0x11, 0xab, 0x7d, 0xbd, 0xfd, 0xda, 0x9f, 0xfe, 0x7c, 0x22, 0x9f, 0x40, - 0x07, 0x54, 0x75, 0x59, 0x09, 0x50, 0x50, 0x39, 0x10, 0x19, 0xa7, 0xf4, - 0x22, 0xd2, 0x37, 0xb7, 0x6b, 0xa8, 0x27, 0xa8, 0x7d, 0xb6, 0x0c, 0xd1, - 0x66, 0xf3, 0xd6, 0x17, 0x57, 0x38, 0x72, 0x4f, 0x64, 0x59, 0x73, 0x54, - 0x7f, 0x41, 0xa8, 0x23, 0xe1, 0xff, 0x27, 0xdc, 0x58, 0xbe, 0x7a, 0xab, - 0xa2, 0xa6, 0xa5, 0xb0, 0xd9, 0xc7, 0x5f, 0xe8, 0xd4, 0x0c, 0x24, 0x2f, - 0xa5, 0x49, 0xe1, 0x57, 0x8d, 0x57, 0xa4, 0x48, 0xaf, 0x2d, 0x1f, 0x0b, - 0xb9, 0xe6, 0x84, 0xc6, 0xde, 0xaf, 0x87, 0xa6, 0x0e, 0xac, 0x89, 0xbf, - 0xb7, 0xdd, 0x9c, 0x01, 0x37, 0x25, 0xa9, 0x42, 0x02, 0x55, 0x3f, 0x59, - 0xa6, 0x4e, 0xfb, 0x36, 0x33, 0x16, 0xaf, 0xf1, 0x9c, 0xcf, 0x80, 0xb5, - 0xdb, 0xa7, 0xc6, 0xa8, 0x3f, 0xb8, 0x9c, 0xd3, 0x59, 0xf6, 0xb9, 0x1a, - 0x9a, 0x3a, 0xce, 0x50, 0x88, 0x59, 0x6a, 0x53, 0x6d, 0x3f, 0xe5, 0x20, - 0xe7, 0xfc, 0x6e, 0xd9, 0x5b, 0xbc, 0x89, 0xaa, 0xe6, 0xa6, 0x12, 0xb2, - 0x36, 0xca, 0x40, 0xeb, 0xc8, 0x0f, 0xa7, 0x31, 0x4b, 0x4b, 0x6e, 0x58, - 0xda, 0x56, 0xdc, 0x46, 0x17, 0x2b, 0x28, 0x08, 0xdf, 0xe3, 0x46, 0xc4, - 0x8f, 0xae, 0x6f, 0xa6, 0x23, 0xad, 0xa4, 0xc1, 0x80, 0xe0, 0x94, 0x04, - 0xeb, 0x27, 0x9d, 0x44, 0xe7, 0x55, 0xee, 0x58, 0x2e, 0x4d, 0x99, 0x34, - 0x49, 0x13, 0xc3, 0xee, 0x1b, 0xcd, 0xe8, 0xb3, 0x5c, 0xa7, 0x83, 0xa9, - 0x12, 0xba, 0x38, 0xd6, 0x56, 0xf9, 0x89, 0x1d, 0xd8, 0x3c, 0x09, 0x52, - 0x99, 0x59, 0x48, 0x52, 0x47, 0x3d, 0x19, 0x1e, 0xef, 0xf9, 0xc0, 0xd6, - 0x72, 0xba, 0xae, 0xa9, 0x42, 0xa7, 0x9a, 0xb3, 0x9c, 0xcc, 0x2d, 0xee, - 0xb4, 0x12, 0x1b, 0x34, 0xe2, 0x4c, 0xd8, 0x58, 0x14, 0x56, 0xff, 0x44, - 0x72, 0x28, 0x31, 0x05, 0x0d, 0xe1, 0x12, 0xc2, 0x60, 0xad, 0x68, 0xa6, - 0x55, 0xae, 0xd0, 0xc3, 0x4f, 0xe3, 0x8e, 0x07, 0x91, 0x2a, 0x7f, 0x46, - 0xb2, 0x56, 0x88, 0x58, 0x9a, 0x4b, 0x2b, 0x32, 0x5c, 0x10, 0xd7, 0xeb, - 0xb0, 0xca, 0x5e, 0xb2, 0xf7, 0xa6, 0x5b, 0xaa, 0xf8, 0xbb, 0xe2, 0xd8, - 0x50, 0xfc, 0x54, 0x20, 0x02, 0x3f, 0x2f, 0x53, 0x92, 0x59, 0x0a, 0x51, - 0x12, 0x3b, 0x48, 0x1b, 0xf2, 0xf6, 0x24, 0xd4, 0x98, 0xb8, 0xed, 0xa8, - 0xbb, 0xa7, 0x31, 0xb5, 0x16, 0xcf, 0x1c, 0xf1, 0x9c, 0x15, 0x81, 0x36, - 0x5f, 0x4e, 0x2e, 0x59, 0x33, 0x55, 0x10, 0x43, 0xc1, 0x25, 0x36, 0x02, - 0x45, 0xde, 0xf3, 0xbf, 0x45, 0xac, 0x7f, 0xa6, 0x9a, 0xaf, 0x0f, 0xc6, - 0x26, 0xe6, 0x86, 0x0a, 0x2c, 0x2d, 0x4a, 0x48, 0x6a, 0x57, 0x01, 0x58, - 0xf8, 0x49, 0xaa, 0x2f, 0x6b, 0x0d, 0xf1, 0xe8, 0x53, 0xc8, 0xe9, 0xb0, - 0xb3, 0xa6, 0x43, 0xab, 0xf4, 0xbd, 0x96, 0xdb, 0x4c, 0xff, 0x19, 0x23, - 0x17, 0x41, 0x3f, 0x54, 0x6d, 0x59, 0xbb, 0x4f, 0xcc, 0x38, 0x6a, 0x18, - 0xff, 0xf3, 0x8c, 0xd1, 0xd8, 0xb6, 0x45, 0xa8, 0x49, 0xa8, 0xe2, 0xb6, - 0x9a, 0xd1, 0x13, 0xf4, 0x79, 0x18, 0xdd, 0x38, 0xc0, 0x4f, 0x70, 0x59, - 0x38, 0x54, 0x0b, 0x41, 0x09, 0x23, 0x38, 0xff, 0x88, 0xdb, 0xe6, 0xbd, - 0x41, 0xab, 0xae, 0xa6, 0xf8, 0xb0, 0x5b, 0xc8, 0x08, 0xe9, 0x7b, 0x0d, - 0xb7, 0x2f, 0x04, 0x4a, 0x05, 0x58, 0x65, 0x57, 0x41, 0x48, 0x1b, 0x2d, - 0x73, 0x0a, 0x18, 0xe6, 0xff, 0xc5, 0x93, 0xaf, 0x7e, 0xa6, 0x4a, 0xac, - 0x01, 0xc0, 0x56, 0xde, 0x46, 0x02, 0xd4, 0x25, 0x19, 0x43, 0x39, 0x55, - 0x2f, 0x59, 0x54, 0x4e, 0x73, 0x36, 0x8c, 0x15, 0x07, 0xf1, 0x0a, 0xcf, - 0x27, 0xb5, 0xb6, 0xa7, 0xf3, 0xa8, 0xa4, 0xb8, 0x2f, 0xd4, 0x09, 0xf7, - 0x55, 0x1b, 0x22, 0x3b, 0x12, 0x51, 0x92, 0x59, 0x28, 0x53, 0xf4, 0x3e, - 0x46, 0x20, 0x3c, 0xfc, 0xd4, 0xd8, 0xeb, 0xbb, 0x56, 0xaa, 0xf8, 0xa6, - 0x69, 0xb2, 0xbc, 0xca, 0xeb, 0xeb, 0x6d, 0x10, 0x38, 0x32, 0xa5, 0x4b, - 0x8a, 0x58, 0xae, 0x56, 0x75, 0x46, 0x7f, 0x2a, 0x7d, 0x07, 0x3f, 0xe3, - 0xc2, 0xc3, 0x4d, 0xae, 0x68, 0xa6, 0x67, 0xad, 0x1f, 0xc2, 0x20, 0xe1, - 0x40, 0x05, 0x83, 0x28, 0x0b, 0x45, 0x17, 0x56, 0xd9, 0x58, 0xd6, 0x4c, - 0x0e, 0x34, 0xa2, 0x12, 0x1a, 0xee, 0x90, 0xcc, 0x8d, 0xb3, 0x43, 0xa7, - 0xb1, 0xa9, 0x7e, 0xba, 0xcf, 0xd6, 0x01, 0xfa, 0x2a, 0x1e, 0x56, 0x3d, - 0x4d, 0x52, 0x9a, 0x59, 0x01, 0x52, 0xcc, 0x3c, 0x78, 0x1d, 0x43, 0xf9, - 0x29, 0xd6, 0x06, 0xba, 0x80, 0xa9, 0x5e, 0xa7, 0xef, 0xb3, 0x2d, 0xcd, - 0xd2, 0xee, 0x5e, 0x13, 0xa4, 0x34, 0x39, 0x4d, 0xef, 0x58, 0xe3, 0x55, - 0x93, 0x44, 0xd7, 0x27, 0x85, 0x04, 0x6e, 0xe0, 0x96, 0xc1, 0x21, 0xad, - 0x68, 0xa6, 0x9d, 0xae, 0x4e, 0xc4, 0xf3, 0xe3, 0x39, 0x08, 0x27, 0x2b, - 0xe7, 0x46, 0xde, 0x56, 0x6c, 0x58, 0x3f, 0x4b, 0x9c, 0x31, 0xb2, 0x0f, - 0x32, 0xeb, 0x25, 0xca, 0x0b, 0xb2, 0xe4, 0xa6, 0x8e, 0xaa, 0x66, 0xbc, - 0x7f, 0xd9, 0xf8, 0xfc, 0xf8, 0x20, 0x78, 0x3f, 0x6f, 0x53, 0x8c, 0x59, - 0xc0, 0x50, 0x94, 0x3a, 0xa0, 0x1a, 0x4c, 0xf6, 0x8b, 0xd3, 0x34, 0xb8, - 0xc4, 0xa8, 0xda, 0xa7, 0x8e, 0xb5, 0xa8, 0xcf, 0xc4, 0xf1, 0x42, 0x16, - 0x09, 0x37, 0xb0, 0x4e, 0x3f, 0x59, 0xff, 0x54, 0x9a, 0x42, 0x2a, 0x25, - 0x86, 0x01, 0xaa, 0xdd, 0x7b, 0xbf, 0x08, 0xac, 0x88, 0xa6, 0xe6, 0xaf, - 0x91, 0xc6, 0xcb, 0xe6, 0x30, 0x0b, 0xbf, 0x2d, 0xb0, 0x48, 0x8d, 0x57, - 0xe1, 0x57, 0x98, 0x49, 0x17, 0x2f, 0xc2, 0x0c, 0x4e, 0xe8, 0xc9, 0xc7, - 0x9f, 0xb0, 0x9f, 0xa6, 0x81, 0xab, 0x65, 0xbe, 0x35, 0xdc, 0xf5, 0xff, - 0xb7, 0x23, 0x8d, 0x41, 0x77, 0x54, 0x64, 0x59, 0x69, 0x4f, 0x49, 0x38, - 0xc6, 0x17, 0x53, 0xf3, 0xfd, 0xd0, 0x73, 0xb6, 0x24, 0xa8, 0x6d, 0xa8, - 0x43, 0xb7, 0x32, 0xd2, 0xb6, 0xf4, 0x26, 0x19, 0x58, 0x39, 0x15, 0x50, - 0x74, 0x59, 0x02, 0x54, 0x91, 0x40, 0x6e, 0x22, 0x8b, 0xfe, 0xed, 0xda, - 0x72, 0xbd, 0x0a, 0xab, 0xbe, 0xa6, 0x47, 0xb1, 0xe4, 0xc8, 0xab, 0xe9, - 0x26, 0x0e, 0x47, 0x30, 0x64, 0x4a, 0x25, 0x58, 0x3d, 0x57, 0xdd, 0x47, - 0x83, 0x2c, 0xce, 0x09, 0x70, 0xe5, 0x81, 0xc5, 0x44, 0xaf, 0x79, 0xa6, - 0x87, 0xac, 0x7a, 0xc0, 0xf4, 0xde, 0xf2, 0x02, 0x6e, 0x26, 0x8c, 0x43, - 0x6b, 0x55, 0x20, 0x59, 0xfd, 0x4d, 0xef, 0x35, 0xe1, 0x14, 0x63, 0xf0, - 0x77, 0xce, 0xcc, 0xb4, 0x99, 0xa7, 0x1b, 0xa9, 0x0e, 0xb9, 0xc3, 0xd4, - 0xb5, 0xf7, 0xf7, 0x1b, 0xa3, 0x3b, 0x5a, 0x51, 0x95, 0x59, 0xea, 0x52, - 0x78, 0x3e, 0xa8, 0x1f, 0x8e, 0xfb, 0x3c, 0xd8, 0x7b, 0xbb, 0x25, 0xaa, - 0x0c, 0xa7, 0xc0, 0xb2, 0x44, 0xcb, 0x94, 0xec, 0x13, 0x11, 0xc7, 0x32, - 0x01, 0x4c, 0xa2, 0x58, 0x84, 0x56, 0x08, 0x46, 0xea, 0x29, 0xd2, 0x06, - 0x9d, 0xe2, 0x44, 0xc3, 0x07, 0xae, 0x67, 0xa6, 0xa8, 0xad, 0x9f, 0xc2, - 0xbd, 0xe1, 0xee, 0x05, 0x1a, 0x29, 0x77, 0x45, 0x47, 0x56, 0xc2, 0x58, - 0x7d, 0x4c, 0x82, 0x33, 0xfb, 0x11, 0x72, 0xed, 0x05, 0xcc, 0x34, 0xb3, - 0x2b, 0xa7, 0xe2, 0xa9, 0xe8, 0xba, 0x6a, 0xd7, 0xab, 0xfa, 0xca, 0x1e, - 0xd4, 0x3d, 0x8f, 0x52, 0x99, 0x59, 0xbc, 0x51, 0x4c, 0x3c, 0xd8, 0x1c, - 0x97, 0xf8, 0x92, 0xd5, 0x9b, 0xb9, 0x55, 0xa9, 0x77, 0xa7, 0x4b, 0xb4, - 0xb7, 0xcd, 0x7f, 0xef, 0xff, 0x13, 0x36, 0x35, 0x88, 0x4d, 0x07, 0x59, - 0xb1, 0x55, 0x22, 0x44, 0x41, 0x27, 0xd7, 0x03, 0xd0, 0xdf, 0x1b, 0xc1, - 0xde, 0xac, 0x71, 0xa6, 0xe0, 0xae, 0xd4, 0xc4, 0x90, 0xe4, 0xe7, 0x08, - 0xbc, 0x2b, 0x4e, 0x47, 0x0b, 0x57, 0x4a, 0x58, 0xe6, 0x4a, 0x0a, 0x31, - 0x0c, 0x0f, 0x8a, 0xea, 0x9e, 0xc9, 0xb4, 0xb1, 0xd7, 0xa6, 0xbe, 0xaa, - 0xda, 0xbc, 0x18, 0xda, 0xa4, 0xfd, 0x96, 0x21, 0xf2, 0x3f, 0xac, 0x53, - 0x84, 0x59, 0x76, 0x50, 0x10, 0x3a, 0x01, 0x1a, 0x9e, 0xf5, 0xfa, 0xd2, - 0xc8, 0xb7, 0xa4, 0xa8, 0xf5, 0xa7, 0xf1, 0xb5, 0x36, 0xd0, 0x6f, 0xf2, - 0xe5, 0x16, 0x94, 0x37, 0xfd, 0x4e, 0x51, 0x59, 0xc5, 0x54, 0x29, 0x42, - 0x8d, 0x24, 0xdc, 0x00, 0x0b, 0xdd, 0x04, 0xbf, 0xcf, 0xab, 0x90, 0xa6, - 0x34, 0xb0, 0x15, 0xc7, 0x6f, 0xe7, 0xdc, 0x0b, 0x50, 0x2e, 0x13, 0x49, - 0xb2, 0x57, 0xbf, 0x57, 0x36, 0x49, 0x84, 0x2e, 0x19, 0x0c, 0xa7, 0xe7, - 0x48, 0xc7, 0x4c, 0xb0, 0x97, 0xa6, 0xb7, 0xab, 0xdd, 0xbe, 0xd1, 0xdc, - 0xa0, 0x00, 0x55, 0x24, 0x00, 0x42, 0xb2, 0x54, 0x54, 0x59, 0x1c, 0x4f, - 0xc0, 0x37, 0x25, 0x17, 0xa7, 0xf2, 0x6c, 0xd0, 0x12, 0xb6, 0x01, 0xa8, - 0x96, 0xa8, 0xa4, 0xb7, 0xc7, 0xd2, 0x61, 0xf5, 0xc6, 0x19, 0xe0, 0x39, - 0x5f, 0x50, 0x7e, 0x59, 0xc4, 0x53, 0x1b, 0x40, 0xcf, 0x21, 0xe0, 0xfd, - 0x53, 0xda, 0xfc, 0xbc, 0xd8, 0xaa, 0xca, 0xa6, 0x9c, 0xb1, 0x6a, 0xc9, - 0x52, 0xea, 0xcd, 0x0e, 0xda, 0x30, 0xc0, 0x4a, 0x45, 0x58, 0x14, 0x57, - 0x77, 0x47, 0xef, 0x2b, 0x23, 0x09, 0xcd, 0xe4, 0xfc, 0xc4, 0x01, 0xaf, - 0x6d, 0xa6, 0xcb, 0xac, 0xee, 0xc0, 0x97, 0xdf, 0x9b, 0x03, 0x0b, 0x27, - 0xf9, 0x43, 0xa0, 0x55, 0x0b, 0x59, 0xab, 0x4d, 0x65, 0x35, 0x3a, 0x14, - 0xbb, 0xef, 0xe8, 0xcd, 0x6e, 0xb4, 0x80, 0xa7, 0x45, 0xa9, 0x76, 0xb9, - 0x5c, 0xd5, 0x5b, 0xf8, 0x9e, 0x1c, 0x1d, 0x3c, 0xa7, 0x51, 0x94, 0x59, - 0xab, 0x52, 0xfb, 0x3d, 0x07, 0x1f, 0xe5, 0xfa, 0xa2, 0xd7, 0x0e, 0xbb, - 0xf3, 0xa9, 0x22, 0xa7, 0x17, 0xb3, 0xd1, 0xcb, 0x39, 0xed, 0xbd, 0x11, - 0x51, 0x33, 0x5d, 0x4c, 0xba, 0x58, 0x57, 0x56, 0x9e, 0x45, 0x50, 0x29, - 0x2a, 0x06, 0xf8, 0xe1, 0xc9, 0xc2, 0xc2, 0xad, 0x66, 0xa6, 0xf0, 0xad, - 0x16, 0xc3, 0x63, 0xe2, 0x96, 0x06, 0xb4, 0x29, 0xe2, 0x45, 0x75, 0x56, - 0xa8, 0x58, 0x26, 0x4c, 0xf2, 0x32, 0x57, 0x11, 0xc7, 0xec, 0x7c, 0xcb, - 0xdb, 0xb2, 0x16, 0xa7, 0x11, 0xaa, 0x57, 0xbb, 0x02, 0xd8, 0x54, 0xfb, - 0x6e, 0x1f, 0x4c, 0x3e, 0xd4, 0x52, 0x95, 0x59, 0x75, 0x51, 0xce, 0x3b, - 0x33, 0x1c, 0xef, 0xf7, 0xfa, 0xd4, 0x33, 0xb9, 0x29, 0xa9, 0x91, 0xa7, - 0xa9, 0xb4, 0x45, 0xce, 0x26, 0xf0, 0xa8, 0x14, 0xbc, 0x35, 0xe1, 0x4d, - 0x18, 0x59, 0x7f, 0x55, 0xb2, 0x43, 0xa7, 0x26, 0x2c, 0x03, 0x30, 0xdf, - 0xa2, 0xc0, 0x9f, 0xac, 0x74, 0xa6, 0x2f, 0xaf, 0x4d, 0xc5, 0x3b, 0xe5, - 0x8c, 0x09, 0x53, 0x2c, 0xb6, 0x47, 0x31, 0x57, 0x2e, 0x58, 0x87, 0x4a, - 0x7b, 0x30, 0x60, 0x0e, 0xe8, 0xe9, 0x13, 0xc9, 0x64, 0xb1, 0xc5, 0xa6, - 0xf3, 0xaa, 0x4e, 0xbd, 0xb1, 0xda, 0x53, 0xfe, 0x31, 0x22, 0x6b, 0x40, - 0xe9, 0x53, 0x7a, 0x59, 0x2e, 0x50, 0x8a, 0x39, 0x5d, 0x19, 0xf5, 0xf4, - 0x64, 0xd2, 0x68, 0xb7, 0x7a, 0xa8, 0x17, 0xa8, 0x52, 0xb6, 0xc7, 0xd0, - 0x18, 0xf3, 0x8b, 0x17, 0x19, 0x38, 0x4e, 0x4f, 0x5f, 0x59, 0x8b, 0x54, - 0xb7, 0x41, 0xee, 0x23, 0x33, 0x00, 0x6c, 0xdc, 0x90, 0xbe, 0x93, 0xab, - 0x9c, 0xa6, 0x83, 0xb0, 0x99, 0xc7, 0x15, 0xe8, 0x83, 0x0c, 0xe5, 0x2e, - 0x73, 0x49, 0xd8, 0x57, 0x97, 0x57, 0xd7, 0x48, 0xf0, 0x2d, 0x6e, 0x0b, - 0x05, 0xe7, 0xc0, 0xc6, 0x01, 0xb0, 0x8d, 0xa6, 0xf0, 0xab, 0x54, 0xbf, - 0x6e, 0xdd, 0x4d, 0x01, 0xf0, 0x24, 0x73, 0x42, 0xe9, 0x54, 0x46, 0x59, - 0xcd, 0x4e, 0x39, 0x37, 0x7c, 0x16, 0x02, 0xf2, 0xd7, 0xcf, 0xb6, 0xb5, - 0xdf, 0xa7, 0xbb, 0xa8, 0x0d, 0xb8, 0x56, 0xd3, 0x11, 0xf6, 0x64, 0x1a, - 0x69, 0x3a, 0xa3, 0x50, 0x8b, 0x59, 0x84, 0x53, 0xa4, 0x3f, 0x30, 0x21, - 0x35, 0xfd, 0xb6, 0xd9, 0x8e, 0xbc, 0xa0, 0xaa, 0xde, 0xa6, 0xee, 0xb1, - 0xf3, 0xc9, 0xf6, 0xea, 0x79, 0x0f, 0x65, 0x31, 0x23, 0x4b, 0x5d, 0x58, - 0xf0, 0x56, 0x0a, 0x47, 0x5f, 0x2b, 0x73, 0x08, 0x2e, 0xe4, 0x7b, 0xc4, - 0xb6, 0xae, 0x6c, 0xa6, 0x08, 0xad, 0x6a, 0xc1, 0x36, 0xe0, 0x47, 0x04, - 0xa3, 0x27, 0x6a, 0x44, 0xd1, 0x55, 0xf8, 0x58, 0x55, 0x4d, 0xd9, 0x34, - 0x96, 0x13, 0x10, 0xef, 0x5e, 0xcd, 0x10, 0xb4, 0x65, 0xa7, 0x73, 0xa9, - 0xdd, 0xb9, 0xf7, 0xd5, 0x02, 0xf9, 0x42, 0x1d, 0x9c, 0x3c, 0xeb, 0x51, - 0x99, 0x59, 0x65, 0x52, 0x81, 0x3d, 0x66, 0x1e, 0x3a, 0xfa, 0x09, 0xd7, - 0xa1, 0xba, 0xc5, 0xa9, 0x37, 0xa7, 0x71, 0xb3, 0x5b, 0xcc, 0xe1, 0xed, - 0x64, 0x12, 0xdf, 0x33, 0xb4, 0x4c, 0xd2, 0x58, 0x2a, 0x56, 0x2e, 0x45, - 0xbc, 0x28, 0x7c, 0x05, 0x58, 0xe1, 0x4d, 0xc2, 0x7d, 0xad, 0x69, 0xa6, - 0x33, 0xae, 0x96, 0xc3, 0x05, 0xe3, 0x3e, 0x07, 0x4e, 0x2a, 0x4c, 0x46, - 0xa0, 0x56, 0x93, 0x58, 0xc5, 0x4b, 0x6b, 0x32, 0xa9, 0x10, 0x25, 0xec, - 0xef, 0xca, 0x86, 0xb2, 0x00, 0xa7, 0x43, 0xaa, 0xc4, 0xbb, 0x9d, 0xd8, - 0x00, 0xfc, 0x0c, 0x20, 0xc9, 0x3e, 0x12, 0x53, 0x92, 0x59, 0x2e, 0x51, - 0x4d, 0x3b, 0x92, 0x1b, 0x43, 0xf7, 0x65, 0xd4, 0xca, 0xb8, 0x00, 0xa9, - 0xad, 0xa7, 0x06, 0xb5, 0xd4, 0xce, 0xcf, 0xf0, 0x4e, 0x15, 0x45, 0x36, - 0x35, 0x4e, 0x29, 0x59, 0x4c, 0x55, 0x40, 0x43, 0x0e, 0x26, 0x7f, 0x02, - 0x92, 0xde, 0x28, 0xc0, 0x63, 0xac, 0x79, 0xa6, 0x7a, 0xaf, 0xcf, 0xc5, - 0xde, 0xe5, 0x37, 0x0a, 0xe7, 0x2c, 0x1c, 0x48, 0x58, 0x57, 0x0f, 0x58, - 0x28, 0x4a, 0xe9, 0x2f, 0xba, 0x0d, 0x3f, 0xe9, 0x8c, 0xc8, 0x16, 0xb1, - 0xb1, 0xa6, 0x30, 0xab, 0xbb, 0xbd, 0x51, 0xdb, 0xfa, 0xfe, 0xd3, 0x22, - 0xdf, 0x40, 0x26, 0x54, 0x71, 0x59, 0xde, 0x4f, 0x09, 0x39, 0xb7, 0x18, - 0x4c, 0xf4, 0xd2, 0xd1, 0x02, 0xb7, 0x58, 0xa8, 0x38, 0xa8, 0xb3, 0xb6, - 0x5b, 0xd1, 0xbf, 0xf3, 0x34, 0x18, 0x9a, 0x38, 0xa1, 0x4f, 0x66, 0x59, - 0x57, 0x54, 0x3e, 0x41, 0x53, 0x23, 0x87, 0xff, 0xcf, 0xdb, 0x1d, 0xbe, - 0x59, 0xab, 0xa9, 0xa6, 0xd2, 0xb0, 0x1e, 0xc8, 0xbc, 0xe8, 0x2b, 0x0d, - 0x77, 0x2f, 0xd6, 0x49, 0xf6, 0x57, 0x77, 0x57, 0x6e, 0x48, 0x5f, 0x2d, - 0xc4, 0x0a, 0x61, 0xe6, 0x3c, 0xc6, 0xb6, 0xaf, 0x81, 0xa6, 0x30, 0xac, - 0xc8, 0xbf, 0x0d, 0xde, 0xf9, 0x01, 0x8a, 0x25, 0xe8, 0x42, 0x1d, 0x55, - 0x38, 0x59, 0x7a, 0x4e, 0xb2, 0x36, 0xd7, 0x15, 0x57, 0xf1, 0x4a, 0xcf, - 0x53, 0xb5, 0xc5, 0xa7, 0xdf, 0xa8, 0x74, 0xb8, 0xed, 0xd3, 0xb7, 0xf6, - 0x0e, 0x1b, 0xe3, 0x3a, 0xf3, 0x50, 0x8d, 0x59, 0x47, 0x53, 0x2c, 0x3f, - 0x8e, 0x20, 0x8d, 0xfc, 0x19, 0xd9, 0x1f, 0xbc, 0x6d, 0xaa, 0xef, 0xa6, - 0x43, 0xb2, 0x7b, 0xca, 0x9f, 0xeb, 0x20, 0x10, 0xf4, 0x31, 0x7f, 0x4b, - 0x7a, 0x58, 0xc4, 0x56, 0xa5, 0x46, 0xc3, 0x2a, 0xcd, 0x07, 0x89, 0xe3, - 0xfd, 0xc3, 0x6e, 0xae, 0x69, 0xa6, 0x48, 0xad, 0xe7, 0xc1, 0xd5, 0xe0, - 0xf2, 0x04, 0x3d, 0x28, 0xd8, 0x44, 0x02, 0x56, 0xe2, 0x58, 0xff, 0x4c, - 0x4e, 0x34, 0xee, 0x12, 0x69, 0xee, 0xd0, 0xcc, 0xb6, 0xb3, 0x4f, 0xa7, - 0x9b, 0xa9, 0x4c, 0xba, 0x8b, 0xd6, 0xb0, 0xf9, 0xe2, 0x1d, 0x1b, 0x3d, - 0x2e, 0x52, 0x99, 0x59, 0x23, 0x52, 0x02, 0x3d, 0xc7, 0x1d, 0x8c, 0xf9, - 0x74, 0xd6, 0x32, 0xba, 0x99, 0xa9, 0x50, 0xa7, 0xc7, 0xb3, 0xeb, 0xcc, - 0x86, 0xee, 0x0e, 0x13, 0x68, 0x34, 0x0f, 0x4d, 0xe5, 0x58, 0xfc, 0x55, - 0xc0, 0x44, 0x24, 0x28, 0xcf, 0x04, 0xbc, 0xe0, 0xcb, 0xc1, 0x40, 0xad, - 0x67, 0xa6, 0x7c, 0xae, 0x15, 0xc4, 0xa5, 0xe3, 0xed, 0x07, 0xe1, 0x2a, - 0xb6, 0x46, 0xcd, 0x56, 0x75, 0x58, 0x6d, 0x4b, 0xdc, 0x31, 0x00, 0x10, - 0x7f, 0xeb, 0x63, 0xca, 0x33, 0xb2, 0xec, 0xa6, 0x76, 0xaa, 0x33, 0xbc, - 0x36, 0xd9, 0xad, 0xfc, 0xab, 0x20, 0x42, 0x3f, 0x53, 0x53, 0x8b, 0x59, - 0xe6, 0x50, 0xce, 0x3a, 0xeb, 0x1a, 0x9d, 0xf6, 0xcc, 0xd3, 0x65, 0xb8, - 0xd6, 0xa8, 0xcb, 0xa7, 0x65, 0xb5, 0x62, 0xcf, 0x7a, 0xf1, 0xf1, 0x15, - 0xcf, 0x36, 0x88, 0x4e, 0x39, 0x59, 0x16, 0x55, 0xd1, 0x42, 0x6d, 0x25, - 0xdc, 0x01, 0xeb, 0xdd, 0xb9, 0xbf, 0x1e, 0xac, 0x87, 0xa6, 0xc1, 0xaf, - 0x55, 0xc6, 0x81, 0xe6, 0xe0, 0x0a, 0x7d, 0x2d, 0x7f, 0x48, 0x7e, 0x57, - 0xf1, 0x57, 0xc3, 0x49, 0x5c, 0x2f, 0x0e, 0x0d, 0x9a, 0xe8, 0x08, 0xc8, - 0xc2, 0xb0, 0xa7, 0xa6, 0x64, 0xab, 0x32, 0xbe, 0xea, 0xdb, 0xa9, 0xff, - 0x6e, 0x23, 0x55, 0x41, 0x61, 0x54, 0x64, 0x59, 0x93, 0x4f, 0x82, 0x38, - 0x14, 0x18, 0xa2, 0xf3, 0x3d, 0xd1, 0xa4, 0xb6, 0x2f, 0xa8, 0x61, 0xa8, - 0x14, 0xb7, 0xed, 0xd1, 0x69, 0xf4, 0xd8, 0x18, 0x1f, 0x39, 0xf0, 0x4f, - 0x70, 0x59, 0x1c, 0x54, 0xc8, 0x40, 0xb8, 0x22, 0xd8, 0xfe, 0x37, 0xdb, - 0xa4, 0xbd, 0x25, 0xab, 0xb6, 0xa6, 0x22, 0xb1, 0xa7, 0xc8, 0x5c, 0xe9, - 0xdb, 0x0d, 0x01, 0x30, 0x3c, 0x4a, 0x13, 0x58, 0x52, 0x57, 0x0a, 0x48, - 0xc8, 0x2c, 0x1d, 0x0a, 0xba, 0xe5, 0xbd, 0xc5, 0x67, 0xaf, 0x7c, 0xa6, - 0x6b, 0xac, 0x41, 0xc0, 0xac, 0xde, 0xa2, 0x02, 0x29, 0x26, 0x55, 0x43, - 0x56, 0x55, 0x24, 0x59, 0x29, 0x4e, 0x29, 0x36, 0x32, 0x15, 0xad, 0xf0, - 0xbb, 0xce, 0xf5, 0xb4, 0xa7, 0xa7, 0x08, 0xa9, 0xdd, 0xb8, 0x80, 0xd4, - 0x64, 0xf7, 0xae, 0x1b, 0x66, 0x3b, 0x3a, 0x51, 0x93, 0x59, 0x07, 0x53, - 0xb2, 0x3e, 0xef, 0x1f, 0xe1, 0xfb, 0x7f, 0xd8, 0xb1, 0xbb, 0x39, 0xaa, - 0x04, 0xa7, 0x98, 0xb2, 0x05, 0xcb, 0x47, 0xec, 0xc5, 0x10, 0x87, 0x32, - 0xd6, 0x4b, 0x98, 0x58, 0x95, 0x56, 0x3d, 0x46, 0x2c, 0x2a, 0x24, 0x07, - 0xe6, 0xe2, 0x7c, 0xc3, 0x2b, 0xae, 0x62, 0xa6, 0x90, 0xad, 0x5f, 0xc2, - 0x78, 0xe1, 0x9c, 0x05, 0xd6, 0x28, 0x44, 0x45, 0x33, 0x56, 0xca, 0x58, - 0xa9, 0x4c, 0xc1, 0x33, 0x48, 0x12, 0xc0, 0xed, 0x44, 0xcc, 0x5d, 0xb3, - 0x36, 0xa7, 0xcc, 0xa9, 0xb5, 0xba, 0x26, 0xd7, 0x58, 0xfa, 0x86, 0x1e, - 0x95, 0x3d, 0x74, 0x52, 0x98, 0x59, 0xdd, 0x51, 0x86, 0x3c, 0x23, 0x1d, - 0xe4, 0xf8, 0xda, 0xd5, 0xcb, 0xb9, 0x69, 0xa9, 0x69, 0xa7, 0x24, 0xb4, - 0x75, 0xcd, 0x31, 0xef, 0xb4, 0x13, 0xf2, 0x34, 0x66, 0x4d, 0xfa, 0x58, - 0xca, 0x55, 0x54, 0x44, 0x88, 0x27, 0x27, 0x04, 0x18, 0xe0, 0x54, 0xc1, - 0xfd, 0xac, 0x6b, 0xa6, 0xc4, 0xae, 0x94, 0xc4, 0x48, 0xe4, 0x99, 0x08, - 0x73, 0x2b, 0x24, 0x47, 0xf2, 0x56, 0x5d, 0x58, 0x0d, 0x4b, 0x4e, 0x31, - 0x58, 0x0f, 0xd7, 0xea, 0xdd, 0xc9, 0xdc, 0xb1, 0xdb, 0xa6, 0xaa, 0xaa, - 0xa2, 0xbc, 0xd3, 0xd9, 0x56, 0xfd, 0x4a, 0x21, 0xbd, 0x3f, 0x8e, 0x53, - 0x89, 0x59, 0x99, 0x50, 0x4a, 0x3a, 0x4d, 0x1a, 0xed, 0xf5, 0x3b, 0xd3, - 0xfe, 0xb7, 0xad, 0xa8, 0xec, 0xa7, 0xc3, 0xb5, 0xf3, 0xcf, 0x22, 0xf2, - 0x98, 0x16, 0x56, 0x37, 0xd8, 0x4e, 0x4b, 0x59, 0xde, 0x54, 0x5f, 0x42, - 0xd2, 0x24, 0x2f, 0x01, 0x4f, 0xdd, 0x40, 0xbf, 0xe4, 0xab, 0x90, 0xa6, - 0x0d, 0xb0, 0xdb, 0xc6, 0x21, 0xe7, 0x8e, 0x0b, 0x0d, 0x2e, 0xe6, 0x48, - 0xa1, 0x57, 0xcf, 0x57, 0x62, 0x49, 0xca, 0x2e, 0x64, 0x0c, 0xf7, 0xe7, - 0x80, 0xc7, 0x75, 0xb0, 0x98, 0xa6, 0xa0, 0xab, 0xa4, 0xbe, 0x8a, 0xdc, - 0x52, 0x00, 0x0b, 0x24, 0xcc, 0x41, 0x97, 0x54, 0x5b, 0x59, 0x41, 0x4f, - 0xff, 0x37, 0x6d, 0x17, 0xf9, 0xf2, 0xad, 0xd0, 0x3f, 0xb6, 0x12, 0xa8, - 0x80, 0xa8, 0x7c, 0xb7, 0x7d, 0xd2, 0x17, 0xf5, 0x78, 0x19, 0xa6, 0x39, - 0x3a, 0x50, 0x7b, 0x59, 0xe0, 0x53, 0x52, 0x40, 0x18, 0x22, 0x2f, 0xfe, - 0x99, 0xda, 0x33, 0xbd, 0xef, 0xaa, 0xc3, 0xa6, 0x76, 0xb1, 0x2b, 0xc9, - 0x07, 0xea, 0x7e, 0x0e, 0x97, 0x30, 0x97, 0x4a, 0x34, 0x58, 0x2b, 0x57, - 0xa1, 0x47, 0x38, 0x2c, 0x6f, 0x09, 0x19, 0xe5, 0x3a, 0xc5, 0x1d, 0xaf, - 0x75, 0xa6, 0xaa, 0xac, 0xb9, 0xc0, 0x4d, 0xdf, 0x4b, 0x03, 0xc5, 0x26, - 0xc4, 0x43, 0x8c, 0x55, 0x10, 0x59, 0xd6, 0x4d, 0x9e, 0x35, 0x8e, 0x14, - 0x03, 0xf0, 0x2e, 0xce, 0x96, 0xb4, 0x8c, 0xa7, 0x32, 0xa9, 0x45, 0xb9, - 0x17, 0xd5, 0x0d, 0xf8, 0x52, 0x1c, 0xe5, 0x3b, 0x83, 0x51, 0x96, 0x59, - 0xc6, 0x52, 0x37, 0x3e, 0x4e, 0x1f, 0x38, 0xfb, 0xe3, 0xd7, 0x46, 0xbb, - 0x05, 0xaa, 0x1b, 0xa7, 0xec, 0xb2, 0x93, 0xcb, 0xeb, 0xec, 0x6f, 0x11, - 0x14, 0x33, 0x2e, 0x4c, 0xb5, 0x58, 0x66, 0x56, 0xd3, 0x45, 0x95, 0x29, - 0x78, 0x06, 0x44, 0xe2, 0x01, 0xc3, 0xe1, 0xad, 0x68, 0xa6, 0xcc, 0xad, - 0xe2, 0xc2, 0x14, 0xe2, 0x4a, 0x06, 0x6d, 0x29, 0xb1, 0x45, 0x60, 0x56, - 0xb4, 0x58, 0x4d, 0x4c, 0x37, 0x33, 0xa0, 0x11, 0x19, 0xed, 0xb7, 0xcb, - 0x06, 0xb3, 0x1f, 0xa7, 0xfc, 0xa9, 0x22, 0xbb, 0xbe, 0xd7, 0x04, 0xfb, - 0x25, 0x1f, 0x13, 0x3e, 0xb4, 0x52, 0x98, 0x59, 0x96, 0x51, 0x08, 0x3c, - 0x7f, 0x1c, 0x3b, 0xf8, 0x42, 0xd5, 0x62, 0xb9, 0x3d, 0xa9, 0x85, 0xa7, - 0x7d, 0xb4, 0x06, 0xce, 0xd6, 0xef, 0x5d, 0x14, 0x7b, 0x35, 0xbc, 0x4d, - 0x0f, 0x59, 0x95, 0x55, 0xe8, 0x43, 0xe9, 0x26, 0x82, 0x03, 0x72, 0xdf, - 0xe1, 0xc0, 0xb6, 0xac, 0x76, 0xa6, 0x09, 0xaf, 0x15, 0xc5, 0xed, 0xe4, - 0x41, 0x09, 0x0b, 0x2c, 0x8a, 0x47, 0x1b, 0x57, 0x3f, 0x58, 0xb2, 0x4a, - 0xbb, 0x30, 0xb2, 0x0e, 0x30, 0xea, 0x53, 0xc9, 0x8c, 0xb1, 0xc7, 0xa6, - 0xe1, 0xaa, 0x13, 0xbd, 0x6f, 0xda, 0xff, 0xfd, 0xec, 0x21, 0x32, 0x40, - 0xcd, 0x53, 0x81, 0x59, 0x4c, 0x50, 0xcc, 0x39, 0xa4, 0x19, 0x46, 0xf5, - 0xa7, 0xd2, 0x96, 0xb7, 0x8c, 0xa8, 0x08, 0xa8, 0x24, 0xb6, 0x85, 0xd0, - 0xca, 0xf2, 0x40, 0x17, 0xd9, 0x37, 0x2d, 0x4f, 0x54, 0x59, 0xaa, 0x54, - 0xe9, 0x41, 0x38, 0x24, 0x82, 0x00, 0xb3, 0xdc, 0xc8, 0xbe, 0xab, 0xab, - 0x99, 0xa6, 0x5d, 0xb0, 0x5d, 0xc7, 0xc9, 0xe7, 0x35, 0x0c, 0xa1, 0x2e, - 0x47, 0x49, 0xc7, 0x57, 0xaa, 0x57, 0x02, 0x49, 0x35, 0x2e, 0xbc, 0x0b, - 0x51, 0xe7, 0xfd, 0xc6, 0x24, 0xb0, 0x91, 0xa6, 0xd7, 0xab, 0x1b, 0xbf, - 0x28, 0xdd, 0xfa, 0x00, 0xad, 0x24, 0x3a, 0x42, 0xd4, 0x54, 0x49, 0x59, - 0xf3, 0x4e, 0x78, 0x37, 0xc8, 0x16, 0x4f, 0xf2, 0x1c, 0xd0, 0xde, 0xb5, - 0xf2, 0xa7, 0xa6, 0xa8, 0xdf, 0xb7, 0x14, 0xd3, 0xbe, 0xf5, 0x1f, 0x1a, - 0x26, 0x3a, 0x86, 0x50, 0x85, 0x59, 0xa1, 0x53, 0xdd, 0x3f, 0x75, 0x21, - 0x89, 0xfd, 0xf8, 0xd9, 0xc7, 0xbc, 0xb5, 0xaa, 0xd8, 0xa6, 0xc5, 0xb1, - 0xb6, 0xc9, 0xaa, 0xea, 0x2a, 0x0f, 0x25, 0x31, 0xf6, 0x4a, 0x51, 0x58, - 0x02, 0x57, 0x3c, 0x47, 0xa0, 0x2b, 0xc6, 0x08, 0x76, 0xe4, 0xb7, 0xc4, - 0xd7, 0xae, 0x6e, 0xa6, 0xe9, 0xac, 0x34, 0xc1, 0xeb, 0xdf, 0xf8, 0x03, - 0x5e, 0x27, 0x34, 0x44, 0xbd, 0x55, 0x00, 0x59, 0x7c, 0x4d, 0x1b, 0x35, - 0xdf, 0x13, 0x61, 0xef, 0x9d, 0xcd, 0x39, 0xb4, 0x75, 0xa7, 0x59, 0xa9, - 0xb1, 0xb9, 0xad, 0xd5, 0xb8, 0xf8, 0xf4, 0x1c, 0x64, 0x3c, 0xca, 0x51, - 0x97, 0x59, 0x87, 0x52, 0xb8, 0x3d, 0xb0, 0x1e, 0x8a, 0xfa, 0x4e, 0xd7, - 0xd4, 0xba, 0xda, 0xa9, 0x2c, 0xa7, 0x49, 0xb3, 0x19, 0xcc, 0x96, 0xed, - 0x17, 0x12, 0x9d, 0x33, 0x8d, 0x4c, 0xc6, 0x58, 0x3e, 0x56, 0x64, 0x45, - 0xfe, 0x28, 0xcd, 0x05, 0xa2, 0xe1, 0x85, 0xc2, 0x9d, 0xad, 0x67, 0xa6, - 0x12, 0xae, 0x5f, 0xc3, 0xb7, 0xe2, 0xf4, 0x06, 0x04, 0x2a, 0x1c, 0x46, - 0x8d, 0x56, 0x9c, 0x58, 0xf2, 0x4b, 0xab, 0x32, 0xf6, 0x10, 0x73, 0xec, - 0x2c, 0xcb, 0xb0, 0xb2, 0x09, 0xa7, 0x2c, 0xaa, 0x92, 0xbb, 0x55, 0xd8, - 0xb2, 0xfb, 0xc2, 0x1f, 0x91, 0x3e, 0xf4, 0x52, 0x96, 0x59, 0x4c, 0x51, - 0x8b, 0x3b, 0xdb, 0x1b, 0x92, 0xf7, 0xab, 0xd4, 0xf9, 0xb8, 0x13, 0xa9, - 0xa0, 0xa7, 0xdb, 0xb4, 0x92, 0xce, 0x82, 0xf0, 0x01, 0x15, 0x05, 0x36, - 0x10, 0x4e, 0x21, 0x59, 0x63, 0x55, 0x76, 0x43, 0x52, 0x26, 0xd1, 0x02, - 0xdb, 0xde, 0x5e, 0xc0, 0x81, 0xac, 0x74, 0xa6, 0x59, 0xaf, 0x94, 0xc5, - 0x90, 0xe5, 0xeb, 0x09, 0xa1, 0x2c, 0xf0, 0x47, 0x43, 0x57, 0x20, 0x58, - 0x51, 0x4a, 0x2e, 0x30, 0x07, 0x0e, 0x8b, 0xe9, 0xcc, 0xc8, 0x38, 0xb1, - 0xbb, 0xa6, 0x13, 0xab, 0x89, 0xbd, 0x06, 0xdb, 0xb0, 0xfe, 0x85, 0x22, - 0xad, 0x40, 0x07, 0x54, 0x77, 0x59, 0x02, 0x50, 0x45, 0x39, 0x03, 0x19, - 0x9a, 0xf4, 0x15, 0xd2, 0x30, 0xb7, 0x69, 0xa8, 0x26, 0xa8, 0x8a, 0xb6, - 0x14, 0xd1, 0x73, 0xf3, 0xe6, 0x17, 0x5f, 0x38, 0x7b, 0x4f, 0x64, 0x59, - 0x6e, 0x54, 0x76, 0x41, 0x9b, 0x23, 0xd6, 0xff, 0x17, 0xdc, 0x52, 0xbe, - 0x73, 0xab, 0xa4, 0xa6, 0xac, 0xb0, 0xe2, 0xc7, 0x6d, 0xe8, 0xe1, 0x0c, - 0x31, 0x2f, 0xab, 0x49, 0xe6, 0x57, 0x88, 0x57, 0x9d, 0x48, 0xa3, 0x2d, - 0x12, 0x0b, 0xac, 0xe6, 0x7a, 0xc6, 0xd6, 0xaf, 0x89, 0xa6, 0x0f, 0xac, - 0x96, 0xbf, 0xc2, 0xdd, 0xa9, 0x01, 0x45, 0x25, 0xb0, 0x42, 0x08, 0x55, - 0x3d, 0x59, 0xa0, 0x4e, 0xf0, 0x36, 0x25, 0x16, 0xa4, 0xf1, 0x8d, 0xcf, - 0x7e, 0xb5, 0xd1, 0xa7, 0xd0, 0xa8, 0x43, 0xb8, 0xa9, 0xd3, 0x68, 0xf6, - 0xc3, 0x1a, 0xa7, 0x3a, 0xd3, 0x50, 0x88, 0x59, 0x65, 0x53, 0x64, 0x3f, - 0xd7, 0x20, 0xdc, 0xfc, 0x60, 0xd9, 0x53, 0xbc, 0x83, 0xaa, 0xea, 0xa6, - 0x17, 0xb2, 0x41, 0xca, 0x4f, 0xeb, 0xd2, 0x0f, 0xb6, 0x31, 0x51, 0x4b, - 0x6f, 0x58, 0xd7, 0x56, 0xd4, 0x46, 0x0b, 0x2b, 0x1a, 0x08, 0xd4, 0xe3, - 0x37, 0xc4, 0x90, 0xae, 0x68, 0xa6, 0x2d, 0xad, 0xaa, 0xc1, 0x90, 0xe0, - 0xa0, 0x04, 0xf8, 0x27, 0xa5, 0x44, 0xeb, 0x55, 0xed, 0x58, 0x26, 0x4d, - 0x8e, 0x34, 0x3d, 0x13, 0xb5, 0xee, 0x0f, 0xcd, 0xe3, 0xb3, 0x55, 0xa7, - 0x8c, 0xa9, 0x16, 0xba, 0x49, 0xd6, 0x5e, 0xf9, 0x9a, 0x1d, 0xdf, 0x3c, - 0x10, 0x52, 0x9b, 0x59, 0x3d, 0x52, 0x43, 0x3d, 0x09, 0x1e, 0xe3, 0xf9, - 0xb3, 0xd6, 0x6a, 0xba, 0xa9, 0xa9, 0x47, 0xa7, 0x9f, 0xb3, 0xa6, 0xcc, - 0x3e, 0xee, 0xbd, 0x12, 0x2a, 0x34, 0xe6, 0x4c, 0xdb, 0x58, 0x11, 0x56, - 0xf4, 0x44, 0x68, 0x28, 0x22, 0x05, 0x00, 0xe1, 0x0a, 0xc2, 0x5a, 0xad, - 0x68, 0xa6, 0x5b, 0xae, 0xd9, 0xc3, 0x5d, 0xe3, 0x9c, 0x07, 0x9d, 0x2a, - 0x85, 0x46, 0xb9, 0x56, 0x83, 0x58, 0x94, 0x4b, 0x21, 0x32, 0x4a, 0x10, - 0xce, 0xeb, 0xa3, 0xca, 0x57, 0xb2, 0xf9, 0xa6, 0x5a, 0xaa, 0x04, 0xbc, - 0xec, 0xd8, 0x5f, 0xfc, 0x62, 0x20, 0x0a, 0x3f, 0x36, 0x53, 0x8d, 0x59, - 0x09, 0x51, 0x06, 0x3b, 0x3c, 0x1b, 0xe4, 0xf6, 0x17, 0xd4, 0x90, 0xb8, - 0xeb, 0xa8, 0xbd, 0xa7, 0x37, 0xb5, 0x23, 0xcf, 0x29, 0xf1, 0xa8, 0x15, - 0x8e, 0x36, 0x63, 0x4e, 0x31, 0x59, 0x2f, 0x55, 0x05, 0x43, 0xb6, 0x25, - 0x28, 0x02, 0x38, 0xde, 0xea, 0xbf, 0x41, 0xac, 0x7d, 0xa6, 0xa3, 0xaf, - 0x15, 0xc6, 0x37, 0xe6, 0x92, 0x0a, 0x38, 0x2d, 0x53, 0x48, 0x6b, 0x57, - 0xff, 0x57, 0xf3, 0x49, 0x9b, 0x2f, 0x5f, 0x0d, 0xe5, 0xe8, 0x45, 0xc8, - 0xe9, 0xb0, 0xab, 0xa6, 0x4c, 0xab, 0xfb, 0xbd, 0xa4, 0xdb, 0x58, 0xff, - 0x28, 0x23, 0x1c, 0x41, 0x4a, 0x54, 0x65, 0x59, 0xbb, 0x4f, 0xbc, 0x38, - 0x61, 0x18, 0xf0, 0xf3, 0x80, 0xd1, 0xd1, 0xb6, 0x42, 0xa8, 0x4b, 0xa8, - 0xeb, 0xb6, 0xa6, 0xd1, 0x1f, 0xf4, 0x89, 0x18, 0xe4, 0x38, 0xca, 0x4f, - 0x6d, 0x59, 0x37, 0x54, 0xff, 0x40, 0xfe, 0x22, 0x2a, 0xff, 0x7a, 0xdb, - 0xdf, 0xbd, 0x39, 0xab, 0xb4, 0xa6, 0xfa, 0xb0, 0x69, 0xc8, 0x12, 0xe9, - 0x89, 0x0d, 0xc4, 0x2f, 0x0b, 0x4a, 0x08, 0x58, 0x61, 0x57, 0x39, 0x48, - 0x0e, 0x2d, 0x6a, 0x0a, 0x06, 0xe6, 0xf9, 0xc5, 0x89, 0xaf, 0x80, 0xa6, - 0x4e, 0xac, 0x0a, 0xc0, 0x64, 0xde, 0x52, 0x02, 0xe2, 0x25, 0x21, 0x43, - 0x3d, 0x55, 0x2d, 0x59, 0x4f, 0x4e, 0x67, 0x36, 0x7f, 0x15, 0xfa, 0xf0, - 0xff, 0xce, 0x1d, 0xb5, 0xb8, 0xa7, 0xf2, 0xa8, 0xaf, 0xb8, 0x3b, 0xd4, - 0x14, 0xf7, 0x65, 0x1b, 0x2a, 0x3b, 0x1a, 0x51, 0x90, 0x59, 0x25, 0x53, - 0xe9, 0x3e, 0x39, 0x20, 0x30, 0xfc, 0xc6, 0xd8, 0xe4, 0xbb, 0x50, 0xaa, - 0xfa, 0xa6, 0x71, 0xb2, 0xc5, 0xca, 0xfa, 0xeb, 0x7a, 0x10, 0x42, 0x32, - 0xaf, 0x4b, 0x89, 0x58, 0xad, 0x56, 0x6b, 0x46, 0x73, 0x2a, 0x71, 0x07, - 0x31, 0xe3, 0xb9, 0xc3, 0x46, 0xae, 0x69, 0xa6, 0x6b, 0xad, 0x2b, 0xc2, - 0x2b, 0xe1, 0x4e, 0x05, 0x90, 0x28, 0x11, 0x45, 0x1e, 0x56, 0xd6, 0x58, - 0xce, 0x4c, 0x05, 0x34, 0x91, 0x12, 0x11, 0xee, 0x83, 0xcc, 0x86, 0xb3, - 0x42, 0xa7, 0xb3, 0xa9, 0x88, 0xba, 0xdb, 0xd6, 0x0f, 0xfa, 0x35, 0x1e, - 0x63, 0x3d, 0x4e, 0x52, 0x9e, 0x59, 0xf9, 0x51, 0xc3, 0x3c, 0x6b, 0x1d, - 0x35, 0xf9, 0x1d, 0xd6, 0xfe, 0xb9, 0x7c, 0xa9, 0x60, 0xa7, 0xf7, 0xb3, - 0x36, 0xcd, 0xe2, 0xee, 0x69, 0x13, 0xb0, 0x34, 0x41, 0x4d, 0xee, 0x58, - 0xe3, 0x55, 0x84, 0x44, 0xd1, 0x27, 0x74, 0x04, 0x63, 0xe0, 0x8c, 0xc1, - 0x19, 0xad, 0x6c, 0xa6, 0xa1, 0xae, 0x59, 0xc4, 0x00, 0xe4, 0x44, 0x08, - 0x37, 0x2b, 0xeb, 0x46, 0xe6, 0x56, 0x65, 0x58, 0x3c, 0x4b, 0x8c, 0x31, - 0xa9, 0x0f, 0x22, 0xeb, 0x1b, 0xca, 0x05, 0xb2, 0xe1, 0xa6, 0x93, 0xaa, - 0x6f, 0xbc, 0x8a, 0xd9, 0x09, 0xfd, 0x01, 0x21, 0x84, 0x3f, 0x73, 0x53, - 0x8a, 0x59, 0xbe, 0x50, 0x85, 0x3a, 0x98, 0x1a, 0x3b, 0xf6, 0x80, 0xd3, - 0x2d, 0xb8, 0xc0, 0xa8, 0xdc, 0xa7, 0x98, 0xb5, 0xb0, 0xcf, 0xd5, 0xf1, - 0x4c, 0x16, 0x16, 0x37, 0xb6, 0x4e, 0x40, 0x59, 0xfb, 0x54, 0x90, 0x42, - 0x1e, 0x25, 0x7a, 0x01, 0x9b, 0xdd, 0x73, 0xbf, 0x03, 0xac, 0x88, 0xa6, - 0xee, 0xaf, 0x99, 0xc6, 0xdb, 0xe6, 0x3b, 0x0b, 0xcd, 0x2d, 0xb4, 0x48, - 0x94, 0x57, 0xdc, 0x57, 0x92, 0x49, 0x0a, 0x2f, 0xb4, 0x0c, 0x41, 0xe8, - 0xc0, 0xc7, 0x97, 0xb0, 0xa0, 0xa6, 0x83, 0xab, 0x70, 0xbe, 0x42, 0xdc, - 0x01, 0x00, 0xc7, 0x23, 0x92, 0x41, 0x80, 0x54, 0x5e, 0x59, 0x69, 0x4f, - 0x38, 0x38, 0xbf, 0x17, 0x40, 0xf3, 0xf5, 0xd0, 0x69, 0xb6, 0x23, 0xa8, - 0x6e, 0xa8, 0x4e, 0xb7, 0x39, 0xd2, 0xca, 0xf4, 0x2b, 0x19, 0x6a, 0x39, - 0x16, 0x50, 0x79, 0x59, 0xf9, 0x53, 0x8b, 0x40, 0x5f, 0x22, 0x7f, 0xfe, - 0xe0, 0xda, 0x68, 0xbd, 0x06, 0xab, 0xc0, 0xa6, 0x4c, 0xb1, 0xf0, 0xc8, - 0xb8, 0xe9, 0x31, 0x0e, 0x57, 0x30, 0x67, 0x4a, 0x2b, 0x58, 0x38, 0x57, - 0xd6, 0x47, 0x77, 0x2c, 0xc0, 0x09, 0x64, 0xe5, 0x73, 0xc5, 0x45, 0xaf, - 0x71, 0xa6, 0x93, 0xac, 0x7c, 0xc0, 0x07, 0xdf, 0xfb, 0x02, 0x7f, 0x26, - 0x90, 0x43, 0x73, 0x55, 0x1a, 0x59, 0xfb, 0x4d, 0xe1, 0x35, 0xd6, 0x14, - 0x54, 0xf0, 0x6c, 0xce, 0xc4, 0xb4, 0x97, 0xa7, 0x1f, 0xa9, 0x15, 0xb9, - 0xd2, 0xd4, 0xbd, 0xf7, 0x0a, 0x1c, 0xa7, 0x3b, 0x64, 0x51, 0x95, 0x59, - 0xe2, 0x52, 0x72, 0x3e, 0x96, 0x1f, 0x86, 0xfb, 0x2d, 0xd8, 0x73, 0xbb, - 0x22, 0xaa, 0x0c, 0xa7, 0xc8, 0xb2, 0x4f, 0xcb, 0xa1, 0xec, 0x21, 0x11, - 0xd2, 0x32, 0x08, 0x4c, 0xa4, 0x58, 0x80, 0x56, 0x01, 0x46, 0xdc, 0x29, - 0xc6, 0x06, 0x8e, 0xe2, 0x3b, 0xc3, 0x03, 0xae, 0x65, 0xa6, 0xaf, 0xad, - 0xa6, 0xc2, 0xcc, 0xe1, 0xfc, 0x05, 0x25, 0x29, 0x81, 0x45, 0x49, 0x56, - 0xc1, 0x58, 0x75, 0x4c, 0x79, 0x33, 0xeb, 0x11, 0x68, 0xed, 0xf5, 0xcb, - 0x31, 0xb3, 0x26, 0xa7, 0xe9, 0xa9, 0xee, 0xba, 0x78, 0xd7, 0xb5, 0xfa, - 0xdc, 0x1e, 0xd9, 0x3d, 0x98, 0x52, 0x96, 0x59, 0xb9, 0x51, 0x41, 0x3c, - 0xca, 0x1c, 0x8a, 0xf8, 0x87, 0xd5, 0x92, 0xb9, 0x53, 0xa9, 0x75, 0xa7, - 0x56, 0xb4, 0xc2, 0xcd, 0x8a, 0xef, 0x10, 0x14, 0x3c, 0x35, 0x94, 0x4d, - 0x06, 0x59, 0xac, 0x55, 0x1c, 0x44, 0x31, 0x27, 0xce, 0x03, 0xc0, 0xdf, - 0x12, 0xc1, 0xda, 0xac, 0x70, 0xa6, 0xe8, 0xae, 0xdb, 0xc4, 0xa0, 0xe4, - 0xf2, 0x08, 0xc9, 0x2b, 0x58, 0x47, 0x0a, 0x57, 0x4c, 0x58, 0xdb, 0x4a, - 0x01, 0x31, 0xfd, 0x0e, 0x7d, 0xea, 0x93, 0xc9, 0xae, 0xb1, 0xd5, 0xa6, - 0xc3, 0xaa, 0xe2, 0xbc, 0x24, 0xda, 0xb3, 0xfd, 0xa2, 0x21, 0xfb, 0x3f, - 0xb2, 0x53, 0x82, 0x59, 0x72, 0x50, 0x04, 0x3a, 0xf4, 0x19, 0x90, 0xf5, - 0xf0, 0xd2, 0xbf, 0xb7, 0xa2, 0xa8, 0xf5, 0xa7, 0xfb, 0xb5, 0x40, 0xd0, - 0x7e, 0xf2, 0xf1, 0x16, 0xa0, 0x37, 0x01, 0x4f, 0x55, 0x59, 0xbe, 0x54, - 0x22, 0x42, 0x7e, 0x24, 0xd1, 0x00, 0xfc, 0xdc, 0xfd, 0xbe, 0xc7, 0xab, - 0x94, 0xa6, 0x38, 0xb0, 0x22, 0xc7, 0x7a, 0xe7, 0xe9, 0x0b, 0x5e, 0x2e, - 0x19, 0x49, 0xb7, 0x57, 0xba, 0x57, 0x2e, 0x49, 0x7c, 0x2e, 0x06, 0x0c, - 0xa0, 0xe7, 0x38, 0xc7, 0x49, 0xb0, 0x95, 0xa6, 0xbc, 0xab, 0xe6, 0xbe, - 0xdd, 0xdc, 0xb0, 0x00, 0x5f, 0x24, 0x0a, 0x42, 0xb7, 0x54, 0x51, 0x59, - 0x18, 0x4f, 0xb5, 0x37, 0x15, 0x17, 0x9e, 0xf2, 0x5c, 0xd0, 0x0d, 0xb6, - 0xff, 0xa7, 0x95, 0xa8, 0xb1, 0xb7, 0xce, 0xd2, 0x72, 0xf5, 0xd2, 0x19, - 0xec, 0x39, 0x61, 0x50, 0x82, 0x59, 0xbe, 0x53, 0x12, 0x40, 0xc3, 0x21, - 0xd1, 0xfd, 0x47, 0xda, 0xf5, 0xbc, 0xd1, 0xaa, 0xce, 0xa6, 0xa0, 0xb1, - 0x77, 0xc9, 0x5d, 0xea, 0xde, 0x0e, 0xe1, 0x30, 0xcc, 0x4a, 0x43, 0x58, - 0x15, 0x57, 0x6b, 0x47, 0xe7, 0x2b, 0x11, 0x09, 0xc4, 0xe4, 0xf1, 0xc4, - 0xf8, 0xae, 0x72, 0xa6, 0xca, 0xac, 0xfe, 0xc0, 0x9f, 0xdf, 0xab, 0x03, - 0x16, 0x27, 0x02, 0x44, 0xa6, 0x55, 0x07, 0x59, 0xa7, 0x4d, 0x56, 0x35, - 0x31, 0x14, 0xaa, 0xef, 0xe1, 0xcd, 0x63, 0xb4, 0x80, 0xa7, 0x46, 0xa9, - 0x80, 0xb9, 0x68, 0xd5, 0x69, 0xf8, 0xa9, 0x1c, 0x2b, 0x3c, 0xa7, 0x51, - 0x9a, 0x59, 0xa0, 0x52, 0xf7, 0x3d, 0xf6, 0x1e, 0xda, 0xfa, 0x94, 0xd7, - 0x06, 0xbb, 0xf0, 0xa9, 0x24, 0xa7, 0x1c, 0xb3, 0xde, 0xcb, 0x44, 0xed, - 0xce, 0x11, 0x59, 0x33, 0x67, 0x4c, 0xba, 0x58, 0x53, 0x56, 0x96, 0x45, - 0x45, 0x29, 0x1a, 0x06, 0xef, 0xe1, 0xbc, 0xc2, 0xbd, 0xad, 0x67, 0xa6, - 0xf3, 0xad, 0x23, 0xc3, 0x6f, 0xe2, 0xa3, 0x06, 0xc1, 0x29, 0xe8, 0x45, - 0x7c, 0x56, 0xa4, 0x58, 0x1f, 0x4c, 0xe9, 0x32, 0x45, 0x11, 0xbf, 0xec, - 0x6d, 0xcb, 0xd7, 0xb2, 0x13, 0xa7, 0x15, 0xaa, 0x5e, 0xbb, 0x11, 0xd8, - 0x61, 0xfb, 0x7a, 0x1f, 0x57, 0x3e, 0xd7, 0x52, 0x96, 0x59, 0x70, 0x51, - 0xc2, 0x3b, 0x29, 0x1c, 0xdf, 0xf7, 0xef, 0xd4, 0x2c, 0xb9, 0x22, 0xa9, - 0x98, 0xa7, 0xac, 0xb4, 0x53, 0xce, 0x33, 0xf0, 0xb5, 0x14, 0xc6, 0x35, - 0xea, 0x4d, 0x17, 0x59, 0x7c, 0x55, 0xa9, 0x43, 0x9a, 0x26, 0x20, 0x03, - 0x22, 0xdf, 0x99, 0xc0, 0x9a, 0xac, 0x74, 0xa6, 0x34, 0xaf, 0x5b, 0xc5, - 0x43, 0xe5, 0x9e, 0x09, 0x5c, 0x2c, 0xbf, 0x47, 0x34, 0x57, 0x2d, 0x58, - 0x7d, 0x4a, 0x71, 0x30, 0x53, 0x0e, 0xd9, 0xe9, 0x09, 0xc9, 0x5f, 0xb1, - 0xc1, 0xa6, 0xfa, 0xaa, 0x55, 0xbd, 0xbe, 0xda, 0x5f, 0xfe, 0x42, 0x22, - 0x6f, 0x40, 0xf2, 0x53, 0x77, 0x59, 0x27, 0x50, 0x83, 0x39, 0x4c, 0x19, - 0xeb, 0xf4, 0x56, 0xd2, 0x61, 0xb7, 0x77, 0xa8, 0x1a, 0xa8, 0x58, 0xb6, - 0xd6, 0xd0, 0x22, 0xf3, 0x9c, 0x17, 0x20, 0x38, 0x57, 0x4f, 0x5d, 0x59, - 0x8a, 0x54, 0xab, 0x41, 0xe3, 0x23, 0x24, 0x00, 0x61, 0xdc, 0x85, 0xbe, - 0x90, 0xab, 0x9d, 0xa6, 0x88, 0xb0, 0xa4, 0xc7, 0x23, 0xe8, 0x8f, 0x0c, - 0xf3, 0x2e, 0x79, 0x49, 0xdb, 0x57, 0x96, 0x57, 0xcb, 0x48, 0xe8, 0x2d, - 0x5f, 0x0b, 0xf8, 0xe6, 0xb7, 0xc6, 0xf9, 0xaf, 0x8c, 0xa6, 0xf7, 0xab, - 0x5a, 0xbf, 0x7f, 0xdd, 0x56, 0x01, 0x00, 0x25, 0x7a, 0x42, 0xee, 0x54, - 0x46, 0x59, 0xc4, 0x4e, 0x30, 0x37, 0x6f, 0x16, 0xf4, 0xf1, 0xcd, 0xcf, - 0xac, 0xb5, 0xde, 0xa7, 0xbe, 0xa8, 0x14, 0xb8, 0x65, 0xd3, 0x1a, 0xf6, - 0x77, 0x1a, 0x6c, 0x3a, 0xaf, 0x50, 0x88, 0x59, 0x81, 0x53, 0x9a, 0x3f, - 0x23, 0x21, 0x28, 0xfd, 0xa8, 0xd9, 0x89, 0xbc, 0x97, 0xaa, 0xe4, 0xa6, - 0xf0, 0xb1, 0x01, 0xca, 0x04, 0xeb, 0x83, 0x0f, 0x75, 0x31, 0x25, 0x4b, - 0x65, 0x58, 0xe8, 0x56, 0x06, 0x47, 0x4e, 0x2b, 0x6a, 0x08, 0x1e, 0xe4, - 0x73, 0xc4, 0xaf, 0xae, 0x6c, 0xa6, 0x0d, 0xad, 0x73, 0xc1, 0x45, 0xe0, - 0x51, 0x04, 0xb3, 0x27, 0x70, 0x44, 0xd7, 0x55, 0xf5, 0x58, 0x4f, 0x4d, - 0xcd, 0x34, 0x8a, 0x13, 0x01, 0xef, 0x54, 0xcd, 0x08, 0xb4, 0x65, 0xa7, - 0x73, 0xa9, 0xea, 0xb9, 0xfd, 0xd5, 0x17, 0xf9, 0x48, 0x1d, 0xac, 0x3c, - 0xec, 0x51, 0x9a, 0x59, 0x60, 0x52, 0x78, 0x3d, 0x57, 0x1e, 0x2f, 0xfa, - 0xfa, 0xd6, 0x9b, 0xba, 0xc0, 0xa9, 0x39, 0xa7, 0x77, 0xb3, 0x68, 0xcc, - 0xed, 0xed, 0x74, 0x12, 0xe6, 0x33, 0xbe, 0x4c, 0xd3, 0x58, 0x25, 0x56, - 0x28, 0x45, 0xad, 0x28, 0x70, 0x05, 0x4b, 0xe1, 0x43, 0xc2, 0x78, 0xad, - 0x69, 0xa6, 0x37, 0xae, 0xa2, 0xc3, 0x10, 0xe3, 0x4f, 0x07, 0x57, 0x2a, - 0x54, 0x46, 0xa6, 0x56, 0x8d, 0x58, 0xc2, 0x4b, 0x5d, 0x32, 0x9c, 0x10, - 0x19, 0xec, 0xe1, 0xca, 0x82, 0xb2, 0xfd, 0xa6, 0x47, 0xaa, 0xce, 0xbb, - 0xa7, 0xd8, 0x10, 0xfc, 0x17, 0x20, 0xd4, 0x3e, 0x16, 0x53, 0x93, 0x59, - 0x27, 0x51, 0x43, 0x3b, 0x86, 0x1b, 0x35, 0xf7, 0x59, 0xd4, 0xc2, 0xb8, - 0xfc, 0xa8, 0xaf, 0xa7, 0x10, 0xb5, 0xdc, 0xce, 0xdf, 0xf0, 0x5a, 0x15, - 0x4f, 0x36, 0x3e, 0x4e, 0x28, 0x59, 0x49, 0x55, 0x38, 0x43, 0xfe, 0x25, - 0x76, 0x02, 0x81, 0xde, 0x23, 0xc0, 0x59, 0xac, 0x7f, 0xa6, 0x7b, 0xaf, - 0xdd, 0xc5, 0xea, 0xe5, 0x42, 0x0a, 0xf8, 0x2c, 0x1f, 0x48, 0x5f, 0x57, - 0x0a, 0x58, 0x20, 0x4a, 0xe0, 0x2f, 0xa9, 0x0d, 0x35, 0xe9, 0x80, 0xc8, - 0x0f, 0xb1, 0xb2, 0xa6, 0x31, 0xab, 0xc7, 0xbd, 0x5b, 0xdb, 0x0a, 0xff, - 0xdf, 0x22, 0xe8, 0x40, 0x2b, 0x54, 0x6f, 0x59, 0xd9, 0x4f, 0xfe, 0x38, - 0xab, 0x18, 0x3d, 0xf4, 0xc7, 0xd1, 0xfa, 0xb6, 0x54, 0xa8, 0x3d, 0xa8, - 0xba, 0xb6, 0x66, 0xd1, 0xce, 0xf3, 0x3f, 0x18, 0xa6, 0x38, 0xa7, 0x4f, - 0x67, 0x59, 0x52, 0x54, 0x36, 0x41, 0x46, 0x23, 0x78, 0xff, 0xc5, 0xdb, - 0x0f, 0xbe, 0x5a, 0xab, 0xa7, 0xa6, 0xd9, 0xb0, 0x2a, 0xc8, 0xc5, 0xe8, - 0x3e, 0x0d, 0x7e, 0x2f, 0xe0, 0x49, 0xf8, 0x57, 0x73, 0x57, 0x69, 0x48, - 0x51, 0x2d, 0xb7, 0x0a, 0x54, 0xe6, 0x31, 0xc6, 0xb1, 0xaf, 0x80, 0xa6, - 0x34, 0xac, 0xd2, 0xbf, 0x1b, 0xde, 0x03, 0x02, 0x9b, 0x25, 0xed, 0x42, - 0x24, 0x55, 0x36, 0x59, 0x71, 0x4e, 0xab, 0x36, 0xc7, 0x15, 0x4c, 0xf1, - 0x3c, 0xcf, 0x4d, 0xb5, 0xc2, 0xa7, 0xe1, 0xa8, 0x80, 0xb8, 0xf4, 0xd3, - 0xc9, 0xf6, 0x17, 0x1b, 0xf0, 0x3a, 0xf7, 0x50, 0x90, 0x59, 0x40, 0x53, - 0x23, 0x3f, 0x82, 0x20, 0x7e, 0xfc, 0x0e, 0xd9, 0x15, 0xbc, 0x69, 0xaa, - 0xf2, 0xa6, 0x48, 0xb2, 0x88, 0xca, 0xa9, 0xeb, 0x2f, 0x10, 0x00, 0x32, - 0x86, 0x4b, 0x7c, 0x58, 0xc1, 0x56, 0x9a, 0x46, 0xba, 0x2a, 0xc0, 0x07, - 0x7a, 0xe3, 0xf5, 0xc3, 0x66, 0xae, 0x69, 0xa6, 0x4f, 0xad, 0xef, 0xc1, - 0xe2, 0xe0, 0x02, 0x05, 0x46, 0x28, 0xe3, 0x44, 0x04, 0x56, 0xe1, 0x58, - 0xf9, 0x4c, 0x42, 0x34, 0xe2, 0x12, 0x5b, 0xee, 0xc3, 0xcc, 0xb2, 0xb3, - 0x49, 0xa7, 0xa3, 0xa9, 0x51, 0xba, 0x9a, 0xd6, 0xbb, 0xf9, 0xf0, 0x1d, - 0x24, 0x3d, 0x35, 0x52, 0x99, 0x59, 0x1c, 0x52, 0xfa, 0x3c, 0xb8, 0x1d, - 0x81, 0xf9, 0x67, 0xd6, 0x2a, 0xba, 0x95, 0xa9, 0x50, 0xa7, 0xd2, 0xb3, - 0xf2, 0xcc, 0x97, 0xee, 0x1a, 0x13, 0x71, 0x34, 0x1a, 0x4d, 0xe2, 0x58, - 0xfb, 0x55, 0xb6, 0x44, 0x18, 0x28, 0xc2, 0x04, 0xaf, 0xe0, 0xc2, 0xc1, - 0x39, 0xad, 0x69, 0xa6, 0x81, 0xae, 0x1e, 0xc4, 0xb5, 0xe3, 0xf6, 0x07, - 0xf2, 0x2a, 0xba, 0x46, 0xd4, 0x56, 0x6f, 0x58, 0x68, 0x4b, 0xd0, 0x31, - 0xf2, 0x0f, 0x74, 0xeb, 0x56, 0xca, 0x2d, 0xb2, 0xea, 0xa6, 0x7b, 0xaa, - 0x3b, 0xbc, 0x45, 0xd9, 0xb7, 0xfc, 0xba, 0x20, 0x4b, 0x3f, 0x57, 0x53, - 0x8d, 0x59, 0xde, 0x50, 0xc3, 0x3a, 0xe2, 0x1a, 0x8a, 0xf6, 0xc4, 0xd3, - 0x5c, 0xb8, 0xd1, 0xa8, 0xd1, 0xa7, 0x68, 0xb5, 0x72, 0xcf, 0x83, 0xf1, - 0x03, 0x16, 0xd6, 0x36, 0x8f, 0x4e, 0x3c, 0x59, 0x11, 0x55, 0xc7, 0x42, - 0x63, 0x25, 0xca, 0x01, 0xe4, 0xdd, 0xaa, 0xbf, 0x1d, 0xac, 0x86, 0xa6, - 0xc7, 0xaf, 0x62, 0xc6, 0x89, 0xe6, 0xf3, 0x0a, 0x83, 0x2d, 0x8d, 0x48, - 0x7d, 0x57, 0xf0, 0x57, 0xbb, 0x49, 0x50, 0x2f, 0x01, 0x0d, 0x8d, 0xe8, - 0xfe, 0xc7, 0xba, 0xb0, 0xa7, 0xa6, 0x68, 0xab, 0x3b, 0xbe, 0xf9, 0xdb, - 0xb4, 0xff, 0x7b, 0x23, 0x60, 0x41, 0x62, 0x54, 0x68, 0x59, 0x88, 0x4f, - 0x7b, 0x38, 0x05, 0x18, 0x94, 0xf3, 0x34, 0xd1, 0x98, 0xb6, 0x31, 0xa8, - 0x5f, 0xa8, 0x20, 0xb7, 0xf4, 0xd1, 0x7d, 0xf4, 0xdf, 0x18, 0x2d, 0x39, - 0xf4, 0x4f, 0x71, 0x59, 0x19, 0x54, 0xbe, 0x40, 0xaa, 0x22, 0xcc, 0xfe, - 0x28, 0xdb, 0x9f, 0xbd, 0x1c, 0xab, 0xbc, 0xa6, 0x23, 0xb1, 0xb6, 0xc8, - 0x67, 0xe9, 0xea, 0x0d, 0x0c, 0x30, 0x43, 0x4a, 0x16, 0x58, 0x4e, 0x57, - 0x03, 0x48, 0xbd, 0x2c, 0x0e, 0x0a, 0xae, 0xe5, 0xb1, 0xc5, 0x64, 0xaf, - 0x79, 0xa6, 0x71, 0xac, 0x49, 0xc0, 0xbb, 0xde, 0xae, 0x02, 0x36, 0x26, - 0x5e, 0x43, 0x59, 0x55, 0x26, 0x59, 0x1d, 0x4e, 0x23, 0x36, 0x21, 0x15, - 0xa3, 0xf0, 0xae, 0xce, 0xee, 0xb4, 0xa4, 0xa7, 0x0d, 0xa9, 0xe3, 0xb8, - 0x8f, 0xd4, 0x6d, 0xf7, 0xc0, 0x1b, 0x6c, 0x3b, 0x44, 0x51, 0x91, 0x59, - 0x01, 0x53, 0xab, 0x3e, 0xde, 0x1f, 0xd7, 0xfb, 0x71, 0xd8, 0xa8, 0xbb, - 0x37, 0xaa, 0x03, 0xa7, 0xa1, 0xb2, 0x0e, 0xcb, 0x55, 0xec, 0xd3, 0x10, - 0x90, 0x32, 0xe1, 0x4b, 0x96, 0x58, 0x96, 0x56, 0x31, 0x46, 0x21, 0x2a, - 0x17, 0x07, 0xd7, 0xe2, 0x76, 0xc3, 0x21, 0xae, 0x66, 0xa6, 0x92, 0xad, - 0x6a, 0xc2, 0x85, 0xe1, 0xa9, 0x05, 0xe3, 0x28, 0x4c, 0x45, 0x36, 0x56, - 0xcb, 0x58, 0x9e, 0x4c, 0xb8, 0x33, 0x3a, 0x12, 0xb4, 0xed, 0x37, 0xcc, - 0x59, 0xb3, 0x30, 0xa7, 0xd2, 0xa9, 0xbf, 0xba, 0x2f, 0xd7, 0x69, 0xfa, - 0x8f, 0x1e, 0xa2, 0x3d, 0x77, 0x52, 0x99, 0x59, 0xd7, 0x51, 0x7d, 0x3c, - 0x14, 0x1d, 0xd9, 0xf8, 0xca, 0xd5, 0xc7, 0xb9, 0x62, 0xa9, 0x6e, 0xa7, - 0x29, 0xb4, 0x81, 0xcd, 0x3f, 0xef, 0xc0, 0x13, 0xfe, 0x34, 0x6c, 0x4d, - 0xfc, 0x58, 0xc7, 0x55, 0x4a, 0x44, 0x7d, 0x27, 0x17, 0x04, 0x0e, 0xe0, - 0x49, 0xc1, 0xf8, 0xac, 0x6c, 0xa6, 0xc9, 0xae, 0x9e, 0xc4, 0x57, 0xe4, - 0xa2, 0x08, 0x85, 0x2b, 0x26, 0x47, 0xfa, 0x56, 0x58, 0x58, 0x07, 0x4b, - 0x42, 0x31, 0x4b, 0x0f, 0xca, 0xea, 0xd1, 0xc9, 0xd7, 0xb1, 0xd9, 0xa6, - 0xad, 0xaa, 0xad, 0xbc, 0xde, 0xd9, 0x64, 0xfd, 0x59, 0x21, 0xc2, 0x3f, - 0x98, 0x53, 0x83, 0x59, 0x98, 0x50, 0x3d, 0x3a, 0x42, 0x1a, 0xdd, 0xf5, - 0x32, 0xd3, 0xf3, 0xb7, 0xad, 0xa8, 0xed, 0xa7, 0xca, 0xb5, 0x01, 0xd0, - 0x2b, 0xf2, 0xab, 0x16, 0x5a, 0x37, 0xe6, 0x4e, 0x46, 0x59, 0xdd, 0x54, - 0x53, 0x42, 0xc8, 0x24, 0x1f, 0x01, 0x46, 0xdd, 0x32, 0xbf, 0xe3, 0xab, - 0x8f, 0xa6, 0x14, 0xb0, 0xe5, 0xc6, 0x2f, 0xe7, 0x9a, 0x0b, 0x1b, 0x2e, - 0xec, 0x48, 0xa5, 0x57, 0xcb, 0x57, 0x5c, 0x49, 0xbc, 0x2e, 0x5a, 0x0c, - 0xe6, 0xe7, 0x79, 0xc7, 0x6b, 0xb0, 0x9a, 0xa6, 0xa3, 0xab, 0xae, 0xbe, - 0x97, 0xdc, 0x5e, 0x00, 0x1a, 0x24, 0xd1, 0x41, 0xa1, 0x54, 0x54, 0x59, - 0x40, 0x4f, 0xf0, 0x37, 0x63, 0x17, 0xe9, 0xf2, 0xa4, 0xd0, 0x34, 0xb6, - 0x12, 0xa8, 0x82, 0xa8, 0x83, 0xb7, 0x8b, 0xd2, 0x22, 0xf5, 0x87, 0x19, - 0xaf, 0x39, 0x40, 0x50, 0x7d, 0x59, 0xda, 0x53, 0x49, 0x40, 0x0b, 0x22, - 0x21, 0xfe, 0x8e, 0xda, 0x29, 0xbd, 0xea, 0xaa, 0xc7, 0xa6, 0x79, 0xb1, - 0x3a, 0xc9, 0x0f, 0xea, 0x90, 0x0e, 0xa0, 0x30, 0xa0, 0x4a, 0x36, 0x58, - 0x27, 0x57, 0x9a, 0x47, 0x2b, 0x2c, 0x62, 0x09, 0x0c, 0xe5, 0x2f, 0xc5, - 0x18, 0xaf, 0x74, 0xa6, 0xaf, 0xac, 0xc3, 0xc0, 0x58, 0xdf, 0x5c, 0x03, - 0xcd, 0x26, 0xd2, 0x43, 0x8a, 0x55, 0x14, 0x59, 0xcb, 0x4d, 0x98, 0x35, - 0x7c, 0x14, 0xf9, 0xef, 0x20, 0xce, 0x90, 0xb4, 0x8a, 0xa7, 0x35, 0xa9, - 0x4d, 0xb9, 0x25, 0xd5, 0x17, 0xf8, 0x63, 0x1c, 0xec, 0x3b, 0x8a, 0x51, - 0x96, 0x59, 0xc1, 0x52, 0x2d, 0x3e, 0x43, 0x1f, 0x26, 0xfb, 0xdc, 0xd7, - 0x38, 0xbb, 0x06, 0xaa, 0x1a, 0xa7, 0xf4, 0xb2, 0x9d, 0xcb, 0xf9, 0xec, - 0x7d, 0x11, 0x1d, 0x33, 0x39, 0x4c, 0xb2, 0x58, 0x68, 0x56, 0xc5, 0x45, - 0x8d, 0x29, 0x68, 0x06, 0x39, 0xe2, 0xf6, 0xc2, 0xdc, 0xad, 0x67, 0xa6, - 0xd3, 0xad, 0xea, 0xc2, 0x24, 0xe2, 0x55, 0x06, 0x7b, 0x29, 0xb7, 0x45, - 0x66, 0x56, 0xb1, 0x58, 0x47, 0x4c, 0x2c, 0x33, 0x90, 0x11, 0x0d, 0xed, - 0xad, 0xcb, 0xfe, 0xb2, 0x1e, 0xa7, 0xff, 0xa9, 0x2b, 0xbb, 0xc9, 0xd7, - 0x15, 0xfb, 0x2e, 0x1f, 0x1f, 0x3e, 0xba, 0x52, 0x95, 0x59, 0x93, 0x51, - 0xfc, 0x3b, 0x73, 0x1c, 0x2e, 0xf8, 0x35, 0xd5, 0x5a, 0xb9, 0x39, 0xa9, - 0x88, 0xa7, 0x83, 0xb4, 0x13, 0xce, 0xe2, 0xef, 0x6b, 0x14, 0x86, 0x35, - 0xc2, 0x4d, 0x10, 0x59, 0x94, 0x55, 0xda, 0x43, 0xe4, 0x26, 0x6c, 0x03, - 0x6d, 0xdf, 0xd1, 0xc0, 0xb5, 0xac, 0x75, 0xa6, 0x0e, 0xaf, 0x22, 0xc5, - 0xf7, 0xe4, 0x4f, 0x09, 0x19, 0x2c, 0x8e, 0x47, 0x23, 0x57, 0x39, 0x58, - 0xab, 0x4a, 0xb1, 0x30, 0xa2, 0x0e, 0x25, 0xea, 0x48, 0xc9, 0x84, 0xb1, - 0xc8, 0xa6, 0xe3, 0xaa, 0x1e, 0xbd, 0x7a, 0xda, 0x0d, 0xfe, 0xf9, 0x21, - 0x3b, 0x40, 0xd3, 0x53, 0x7f, 0x59, 0x47, 0x50, 0xc0, 0x39, 0x98, 0x19, - 0x38, 0xf5, 0x9c, 0xd2, 0x8d, 0xb7, 0x89, 0xa8, 0x0b, 0xa8, 0x2b, 0xb6, - 0x93, 0xd0, 0xd3, 0xf2, 0x52, 0x17, 0xe0, 0x37, 0x35, 0x4f, 0x55, 0x59, - 0xa5, 0x54, 0xe0, 0x41, 0x2c, 0x24, 0x73, 0x00, 0xa8, 0xdc, 0xbd, 0xbe, - 0xa8, 0xab, 0x99, 0xa6, 0x63, 0xb0, 0x6a, 0xc7, 0xd1, 0xe7, 0x48, 0x0c, - 0xa8, 0x2e, 0x53, 0x49, 0xc6, 0x57, 0xaa, 0x57, 0xf8, 0x48, 0x2a, 0x2e, - 0xb0, 0x0b, 0x40, 0xe7, 0xf7, 0xc6, 0x1b, 0xb0, 0x91, 0xa6, 0xdb, 0xab, - 0x26, 0xbf, 0x32, 0xdd, 0x0c, 0x01, 0xb3, 0x24, 0x49, 0x42, 0xd4, 0x54, - 0x4b, 0x59, 0xec, 0x4e, 0x6c, 0x37, 0xbc, 0x16, 0x41, 0xf2, 0x11, 0xd0, - 0xd6, 0xb5, 0xf1, 0xa7, 0xa6, 0xa8, 0xeb, 0xb7, 0x1b, 0xd3, 0xcf, 0xf5, - 0x2b, 0x1a, 0x2f, 0x3a, 0x90, 0x50, 0x80, 0x59, 0xa1, 0x53, 0xcf, 0x3f, - 0x6d, 0x21, 0x77, 0xfd, 0xf0, 0xd9, 0xbb, 0xbc, 0xb2, 0xaa, 0xd8, 0xa6, - 0xcd, 0xb1, 0xc1, 0xc9, 0xb6, 0xea, 0x39, 0x0f, 0x2e, 0x31, 0xff, 0x4a, - 0x53, 0x58, 0xff, 0x56, 0x34, 0x47, 0x93, 0x2b, 0xb9, 0x08, 0x69, 0xe4, - 0xad, 0xc4, 0xd1, 0xae, 0x6d, 0xa6, 0xf0, 0xac, 0x3b, 0xc1, 0xfa, 0xdf, - 0x03, 0x04, 0x6d, 0x27, 0x3b, 0x44, 0xc3, 0x55, 0xfb, 0x58, 0x79, 0x4d, - 0x0c, 0x35, 0xd7, 0x13, 0x4f, 0xef, 0x93, 0xcd, 0x35, 0xb4, 0x6d, 0xa7, - 0x64, 0xa9, 0xb2, 0xb9, 0xbf, 0xd5, 0xc0, 0xf8, 0x06, 0x1d, 0x6b, 0x3c, - 0xd0, 0x51, 0x98, 0x59, 0x80, 0x52, 0xaf, 0x3d, 0xa4, 0x1e, 0x7a, 0xfa, - 0x44, 0xd7, 0xcb, 0xba, 0xd5, 0xa9, 0x30, 0xa7, 0x4d, 0xb3, 0x27, 0xcc, - 0xa3, 0xed, 0x21, 0x12, 0xac, 0x33, 0x91, 0x4c, 0xcb, 0x58, 0x39, 0x56, - 0x5b, 0x45, 0xf2, 0x28, 0xc0, 0x05, 0x94, 0xe1, 0x7d, 0xc2, 0x96, 0xad, - 0x69, 0xa6, 0x17, 0xae, 0x67, 0xc3, 0xc7, 0xe2, 0xfe, 0x06, 0x14, 0x2a, - 0x21, 0x46, 0x94, 0x56, 0x96, 0x58, 0xef, 0x4b, 0x9c, 0x32, 0xeb, 0x10, - 0x65, 0xec, 0x21, 0xcb, 0xa9, 0xb2, 0x08, 0xa7, 0x2f, 0xaa, 0x9c, 0xbb, - 0x60, 0xd8, 0xc0, 0xfb, 0xcf, 0x1f, 0x9a, 0x3e, 0xfb, 0x52, 0x92, 0x59, - 0x4b, 0x51, 0x7c, 0x3b, 0xd1, 0x1b, 0x84, 0xf7, 0x9e, 0xd4, 0xf2, 0xb8, - 0x0e, 0xa9, 0xa4, 0xa7, 0xe1, 0xb4, 0x9f, 0xce, 0x8d, 0xf0, 0x10, 0x15, - 0x10, 0x36, 0x17, 0x4e, 0x20, 0x59, 0x62, 0x55, 0x6a, 0x43, 0x48, 0x26, - 0xc4, 0x02, 0xca, 0xde, 0x5b, 0xc0, 0x74, 0xac, 0x7e, 0xa6, 0x55, 0xaf, - 0xa5, 0xc5, 0x9b, 0xe5, 0xf8, 0x09, 0xaf, 0x2c, 0xf4, 0x47, 0x4a, 0x57, - 0x1b, 0x58, 0x4c, 0x4a, 0x20, 0x30, 0xfc, 0x0d, 0x7c, 0xe9, 0xc2, 0xc8, - 0x31, 0xb1, 0xba, 0xa6, 0x18, 0xab, 0x93, 0xbd, 0x10, 0xdb, 0xc0, 0xfe, - 0x90, 0x22, 0xb7, 0x40, 0x0c, 0x54, 0x76, 0x59, 0xfd, 0x4f, 0x39, 0x39, - 0xf8, 0x18, 0x8a, 0xf4, 0x0b, 0xd2, 0x29, 0xb7, 0x62, 0xa8, 0x30, 0xa8, - 0x8a, 0xb6, 0x24, 0xd1, 0x80, 0xf3, 0xf1, 0x17, 0x6d, 0x38, 0x7e, 0x4f, - 0x65, 0x59, 0x6b, 0x54, 0x6c, 0x41, 0x8e, 0x23, 0xca, 0xff, 0x07, 0xdc, - 0x4d, 0xbe, 0x6b, 0xab, 0xa8, 0xa6, 0xb0, 0xb0, 0xee, 0xc7, 0x7b, 0xe8, - 0xec, 0x0c, 0x3e, 0x2f, 0xb2, 0x49, 0xe9, 0x57, 0x85, 0x57, 0x96, 0x48, - 0x95, 0x2d, 0x06, 0x0b, 0x9e, 0xe6, 0x70, 0xc6, 0xd1, 0xaf, 0x85, 0xa6, - 0x19, 0xac, 0x9a, 0xbf, 0xd3, 0xdd, 0xb4, 0x01, 0x53, 0x25, 0xb8, 0x42, - 0x0d, 0x55, 0x3c, 0x59, 0x98, 0x4e, 0xe9, 0x36, 0x12, 0x16, 0x9b, 0xf1, - 0x7f, 0xcf, 0x78, 0xb5, 0xcf, 0xa7, 0xd1, 0xa8, 0x4d, 0xb8, 0xb4, 0xd3, - 0x76, 0xf6, 0xcf, 0x1a, 0xb3, 0x3a, 0xd6, 0x50, 0x8c, 0x59, 0x5e, 0x53, - 0x5b, 0x3f, 0xca, 0x20, 0xce, 0xfc, 0x53, 0xd9, 0x4c, 0xbc, 0x7f, 0xaa, - 0xe9, 0xa6, 0x21, 0xb2, 0x48, 0xca, 0x60, 0xeb, 0xde, 0x0f, 0xc1, 0x31, - 0x59, 0x4b, 0x70, 0x58, 0xd6, 0x56, 0xc9, 0x46, 0x01, 0x2b, 0x0c, 0x08, - 0xc7, 0xe3, 0x2d, 0xc4, 0x89, 0xae, 0x6a, 0xa6, 0x2f, 0xad, 0xb9, 0xc1, - 0x96, 0xe0, 0xb3, 0x04, 0x02, 0x28, 0xac, 0x44, 0xf3, 0x55, 0xe7, 0x58, - 0x22, 0x4d, 0x83, 0x34, 0x2d, 0x13, 0xaa, 0xee, 0x04, 0xcd, 0xda, 0xb3, - 0x56, 0xa7, 0x8d, 0xa9, 0x20, 0xba, 0x53, 0xd6, 0x6e, 0xf9, 0xa5, 0x1d, - 0xea, 0x3c, 0x17, 0x52, 0x96, 0x59, 0x3f, 0x52, 0x30, 0x3d, 0x05, 0x1e, - 0xcf, 0xf9, 0xab, 0xd6, 0x5e, 0xba, 0xa8, 0xa9, 0x47, 0xa7, 0xa7, 0xb3, - 0xb1, 0xcc, 0x4b, 0xee, 0xca, 0x12, 0x37, 0x34, 0xea, 0x4c, 0xdf, 0x58, - 0x0c, 0x56, 0xed, 0x44, 0x5a, 0x28, 0x15, 0x05, 0xf4, 0xe0, 0xfe, 0xc1, - 0x57, 0xad, 0x67, 0xa6, 0x60, 0xae, 0xe5, 0xc3, 0x67, 0xe3, 0xad, 0x07, - 0xa6, 0x2a, 0x90, 0x46, 0xba, 0x56, 0x81, 0x58, 0x90, 0x4b, 0x10, 0x32, - 0x44, 0x10, 0xba, 0xeb, 0x9b, 0xca, 0x51, 0xb2, 0xf4, 0xa6, 0x63, 0xaa, - 0x08, 0xbc, 0xfc, 0xd8, 0x6b, 0xfc, 0x6e, 0x20, 0x15, 0x3f, 0x3a, 0x53, - 0x8e, 0x59, 0x02, 0x51, 0xfc, 0x3a, 0x2f, 0x1b, 0xd7, 0xf6, 0x0b, 0xd4, - 0x88, 0xb8, 0xe7, 0xa8, 0xc0, 0xa7, 0x3f, 0xb5, 0x2f, 0xcf, 0x35, 0xf1, - 0xb6, 0x15, 0x9a, 0x36, 0x67, 0x4e, 0x36, 0x59, 0x28, 0x55, 0xfc, 0x42, - 0xac, 0x25, 0x18, 0x02, 0x2e, 0xde, 0xde, 0xbf, 0x3c, 0xac, 0x7f, 0xa6, - 0xa8, 0xaf, 0x22, 0xc6, 0x41, 0xe6, 0xa2, 0x0a, 0x42, 0x2d, 0x5a, 0x48, - 0x71, 0x57, 0xfa, 0x57, 0xec, 0x49, 0x91, 0x2f, 0x4e, 0x0d, 0xdc, 0xe8, - 0x37, 0xc8, 0xe3, 0xb0, 0xab, 0xa6, 0x4f, 0xab, 0x05, 0xbe, 0xb1, 0xdb, - 0x65, 0xff, 0x33, 0x23, 0x2a, 0x41, 0x48, 0x54, 0x6b, 0x59, 0xaf, 0x4f, - 0xb6, 0x38, 0x52, 0x18, 0xe3, 0xf3, 0x75, 0xd1, 0xc7, 0xb6, 0x40, 0xa8, - 0x50, 0xa8, 0xef, 0xb6, 0xb7, 0xd1, 0x26, 0xf4, 0x9b, 0x18, 0xec, 0x38, - 0xd0, 0x4f, 0x71, 0x59, 0x2e, 0x54, 0xf9, 0x40, 0xf0, 0x22, 0x1c, 0xff, - 0x70, 0xdb, 0xd3, 0xbd, 0x38, 0xab, 0xb2, 0xa6, 0x01, 0xb1, 0x76, 0xc8, - 0x1b, 0xe9, 0x9d, 0x0d, 0xc8, 0x2f, 0x19, 0x4a, 0x04, 0x58, 0x65, 0x57, - 0x2b, 0x48, 0x07, 0x2d, 0x58, 0x0a, 0xfd, 0xe5, 0xeb, 0xc5, 0x86, 0xaf, - 0x7d, 0xa6, 0x54, 0xac, 0x15, 0xc0, 0x6d, 0xde, 0x64, 0x02, 0xea, 0x25, - 0x2d, 0x43, 0x40, 0x55, 0x2c, 0x59, 0x47, 0x4e, 0x5f, 0x36, 0x6f, 0x15, - 0xef, 0xf0, 0xf0, 0xce, 0x1a, 0xb5, 0xb0, 0xa7, 0xfc, 0xa8, 0xb1, 0xb8, - 0x4c, 0xd4, 0x1e, 0xf7, 0x74, 0x1b, 0x33, 0x3b, 0x21, 0x51, 0x8e, 0x59, - 0x24, 0x53, 0xda, 0x3e, 0x32, 0x20, 0x1e, 0xfc, 0xbb, 0xd8, 0xdd, 0xbb, - 0x49, 0xaa, 0xfe, 0xa6, 0x76, 0xb2, 0xd2, 0xca, 0x05, 0xec, 0x89, 0x10, - 0x4c, 0x32, 0xb8, 0x4b, 0x8a, 0x58, 0xaa, 0x56, 0x61, 0x46, 0x6a, 0x2a, - 0x62, 0x07, 0x24, 0xe3, 0xae, 0xc3, 0x43, 0xae, 0x66, 0xa6, 0x74, 0xad, - 0x30, 0xc2, 0x3b, 0xe1, 0x5a, 0x05, 0x9e, 0x28, 0x18, 0x45, 0x23, 0x56, - 0xd2, 0x58, 0xca, 0x4c, 0xf7, 0x33, 0x87, 0x12, 0x01, 0xee, 0x78, 0xcc, - 0x80, 0xb3, 0x3e, 0xa7, 0xba, 0xa9, 0x8e, 0xba, 0xe8, 0xd6, 0x1b, 0xfa, - 0x45, 0x1e, 0x69, 0x3d, 0x58, 0x52, 0x99, 0x59, 0xf7, 0x51, 0xb7, 0x3c, - 0x60, 0x1d, 0x26, 0xf9, 0x12, 0xd6, 0xf4, 0xb9, 0x7a, 0xa9, 0x61, 0xa7, - 0xfe, 0xb3, 0x43, 0xcd, 0xee, 0xee, 0x76, 0x13, 0xbd, 0x34, 0x45, 0x4d, - 0xf2, 0x58, 0xdd, 0x55, 0x7e, 0x44, 0xc2, 0x27, 0x6a, 0x04, 0x51, 0xe0, - 0x87, 0xc1, 0x11, 0xad, 0x6e, 0xa6, 0xa5, 0xae, 0x65, 0xc4, 0x0a, 0xe4, - 0x56, 0x08, 0x3e, 0x2b, 0xf7, 0x46, 0xe7, 0x56, 0x64, 0x58, 0x34, 0x4b, - 0x82, 0x31, 0x99, 0x0f, 0x17, 0xeb, 0x0f, 0xca, 0xfe, 0xb1, 0xe2, 0xa6, - 0x93, 0xaa, 0x7d, 0xbc, 0x92, 0xd9, 0x19, 0xfd, 0x0c, 0x21, 0x8e, 0x3f, - 0x7a, 0x53, 0x87, 0x59, 0xb9, 0x50, 0x7a, 0x3a, 0x8b, 0x1a, 0x2f, 0xf6, - 0x72, 0xd3, 0x26, 0xb8, 0xbb, 0xa8, 0xe1, 0xa7, 0x9d, 0xb5, 0xbe, 0xcf, - 0xe0, 0xf1, 0x5b, 0x16, 0x1f, 0x37, 0xbd, 0x4e, 0x42, 0x59, 0xf5, 0x54, - 0x8a, 0x42, 0x0d, 0x25, 0x70, 0x01, 0x8c, 0xdd, 0x6b, 0xbf, 0xfe, 0xab, - 0x88, 0xa6, 0xf4, 0xaf, 0xa5, 0xc6, 0xe5, 0xe6, 0x4d, 0x0b, 0xd3, 0x2d, - 0xc3, 0x48, 0x91, 0x57, 0xde, 0x57, 0x86, 0x49, 0x02, 0x2f, 0xa5, 0x0c, - 0x35, 0xe8, 0xb4, 0xc7, 0x91, 0xb0, 0xa0, 0xa6, 0x87, 0xab, 0x79, 0xbe, - 0x4d, 0xdc, 0x11, 0x00, 0xd1, 0x23, 0x9d, 0x41, 0x84, 0x54, 0x5d, 0x59, - 0x61, 0x4f, 0x30, 0x38, 0xae, 0x17, 0x38, 0xf3, 0xe5, 0xd0, 0x64, 0xb6, - 0x1d, 0xa8, 0x75, 0xa8, 0x51, 0xb7, 0x4a, 0xd2, 0xd2, 0xf4, 0x3d, 0x19, - 0x71, 0x39, 0x1e, 0x50, 0x78, 0x59, 0xf6, 0x53, 0x80, 0x40, 0x54, 0x22, - 0x70, 0xfe, 0xd4, 0xda, 0x60, 0xbd, 0x01, 0xab, 0xc0, 0xa6, 0x55, 0xb1, - 0xf8, 0xc8, 0xc7, 0xe9, 0x40, 0x0e, 0x5e, 0x30, 0x75, 0x4a, 0x26, 0x58, - 0x3b, 0x57, 0xc9, 0x47, 0x6f, 0x2c, 0xb1, 0x09, 0x56, 0xe5, 0x6c, 0xc5, - 0x39, 0xaf, 0x78, 0xa6, 0x90, 0xac, 0x8e, 0xc0, 0x0c, 0xdf, 0x10, 0x03, - 0x84, 0x26, 0x9e, 0x43, 0x75, 0x55, 0x1a, 0x59, 0xf4, 0x4d, 0xd5, 0x35, - 0xc9, 0x14, 0x47, 0xf0, 0x61, 0xce, 0xbd, 0xb4, 0x94, 0xa7, 0x22, 0xa9, - 0x1f, 0xb9, 0xdb, 0xd4, 0xcf, 0xf7, 0x12, 0x1c, 0xb6, 0x3b, 0x66, 0x51, - 0x97, 0x59, 0xdd, 0x52, 0x66, 0x3e, 0x8d, 0x1f, 0x74, 0xfb, 0x24, 0xd8, - 0x69, 0xbb, 0x1d, 0xaa, 0x10, 0xa7, 0xcc, 0xb2, 0x5d, 0xcb, 0xac, 0xec, - 0x30, 0x11, 0xdd, 0x32, 0x0d, 0x4c, 0xaa, 0x58, 0x79, 0x56, 0xfa, 0x45, - 0xd0, 0x29, 0xb8, 0x06, 0x82, 0xe2, 0x31, 0xc3, 0xfb, 0xad, 0x67, 0xa6, - 0xb6, 0xad, 0xac, 0xc2, 0xde, 0xe1, 0x03, 0x06, 0x37, 0x29, 0x86, 0x45, - 0x4f, 0x56, 0xbd, 0x58, 0x71, 0x4c, 0x6a, 0x33, 0xe0, 0x11, 0x5a, 0xed, - 0xea, 0xcb, 0x2b, 0xb3, 0x24, 0xa7, 0xea, 0xa9, 0xfa, 0xba, 0x82, 0xd7, - 0xc5, 0xfa, 0xe7, 0x1e, 0xe3, 0x3d, 0x9d, 0x52, 0x97, 0x59, 0xb2, 0x51, - 0x38, 0x3c, 0xbd, 0x1c, 0x7c, 0xf8, 0x7b, 0xd5, 0x8a, 0xb9, 0x4e, 0xa9, - 0x7a, 0xa7, 0x5b, 0xb4, 0xce, 0xcd, 0x98, 0xef, 0x1c, 0x14, 0x47, 0x35, - 0x9d, 0x4d, 0x04, 0x59, 0xad, 0x55, 0x0e, 0x44, 0x28, 0x27, 0xbe, 0x03, - 0xb5, 0xdf, 0x08, 0xc1, 0xd5, 0xac, 0x70, 0xa6, 0xed, 0xae, 0xe7, 0xc4, - 0xab, 0xe4, 0x02, 0x09, 0xd3, 0x2b, 0x60, 0x47, 0x0e, 0x57, 0x4a, 0x58, - 0xd4, 0x4a, 0xf4, 0x30, 0xf1, 0x0e, 0x6f, 0xea, 0x89, 0xc9, 0xa7, 0xb1, - 0xd4, 0xa6, 0xc6, 0xaa, 0xed, 0xbc, 0x2f, 0xda, 0xc1, 0xfd, 0xaf, 0x21, - 0x04, 0x40, 0xb8, 0x53, 0x80, 0x59, 0x6d, 0x50, 0xf9, 0x39, 0xe8, 0x19, - 0x83, 0xf5, 0xe1, 0xd2, 0xbc, 0xb7, 0x99, 0xa8, 0xfd, 0xa7, 0x00, 0xb6, - 0x4d, 0xd0, 0x89, 0xf2, 0x01, 0x17, 0xa7, 0x37, 0x0c, 0x4f, 0x53, 0x59, - 0xbc, 0x54, 0x15, 0x42, 0x77, 0x24, 0xbe, 0x00, 0xf3, 0xdc, 0xf3, 0xbe, - 0xc2, 0xab, 0x95, 0xa6, 0x3e, 0xb0, 0x2c, 0xc7, 0x88, 0xe7, 0xf7, 0x0b, - 0x68, 0x2e, 0x22, 0x49, 0xb8, 0x57, 0xb9, 0x57, 0x26, 0x49, 0x6f, 0x2e, - 0xfb, 0x0b, 0x90, 0xe7, 0x2e, 0xc7, 0x45, 0xb0, 0x92, 0xa6, 0xc2, 0xab, - 0xee, 0xbe, 0xeb, 0xdc, 0xbb, 0x00, 0x6f, 0x24, 0x10, 0x42, 0xbd, 0x54, - 0x50, 0x59, 0x11, 0x4f, 0xab, 0x37, 0x07, 0x17, 0x90, 0xf2, 0x53, 0xd0, - 0x02, 0xb6, 0x00, 0xa8, 0x95, 0xa8, 0xbb, 0xb7, 0xda, 0xd2, 0x7d, 0xf5, - 0xe2, 0x19, 0xf3, 0x39, 0x6b, 0x50, 0x80, 0x59, 0xb9, 0x53, 0x0a, 0x40, - 0xb5, 0x21, 0xc5, 0xfd, 0x39, 0xda, 0xeb, 0xbc, 0xcf, 0xaa, 0xcf, 0xa6, - 0xa6, 0xb1, 0x83, 0xc9, 0x68, 0xea, 0xed, 0x0e, 0xec, 0x30, 0xd4, 0x4a, - 0x45, 0x58, 0x12, 0x57, 0x63, 0x47, 0xda, 0x2b, 0x06, 0x09, 0xb4, 0xe4, - 0xe8, 0xc4, 0xf4, 0xae, 0x6d, 0xa6, 0xd5, 0xac, 0x02, 0xc1, 0xb0, 0xdf, - 0xb7, 0x03, 0x21, 0x27, 0x0d, 0x44, 0xa8, 0x55, 0x08, 0x59, 0x9e, 0x4d, - 0x4c, 0x35, 0x24, 0x14, 0x9c, 0xef, 0xd6, 0xcd, 0x5b, 0xb4, 0x7e, 0xa7, - 0x4a, 0xa9, 0x89, 0xb9, 0x73, 0xd5, 0x76, 0xf8, 0xb7, 0x1c, 0x34, 0x3c, - 0xaf, 0x51, 0x97, 0x59, 0x9f, 0x52, 0xe7, 0x3d, 0xef, 0x1e, 0xca, 0xfa, - 0x87, 0xd7, 0x00, 0xbb, 0xe9, 0xa9, 0x26, 0xa7, 0x26, 0xb3, 0xe5, 0xcb, - 0x56, 0xed, 0xd5, 0x11, 0x6b, 0x33, 0x67, 0x4c, 0xc2, 0x58, 0x4c, 0x56, - 0x8f, 0x45, 0x37, 0x29, 0x0f, 0x06, 0xdf, 0xe1, 0xb4, 0xc2, 0xb9, 0xad, - 0x63, 0xa6, 0xfe, 0xad, 0x28, 0xc3, 0x7e, 0xe2, 0xb1, 0x06, 0xcb, 0x29, - 0xf4, 0x45, 0x7c, 0x56, 0xa5, 0x58, 0x15, 0x4c, 0xe1, 0x32, 0x35, 0x11, - 0xb4, 0xec, 0x5f, 0xcb, 0xd4, 0xb2, 0x0d, 0xa7, 0x1e, 0xaa, 0x63, 0xbb, - 0x1f, 0xd8, 0x6c, 0xfb, 0x8b, 0x1f, 0x5c, 0x3e, 0xe1, 0x52, 0x91, 0x59, - 0x6e, 0x51, 0xb6, 0x3b, 0x1d, 0x1c, 0xd1, 0xf7, 0xe3, 0xd4, 0x22, 0xb9, - 0x23, 0xa9, 0x95, 0xa7, 0xb7, 0xb4, 0x5e, 0xce, 0x3d, 0xf0, 0xc7, 0x14, - 0xcd, 0x35, 0xf2, 0x4d, 0x1a, 0x59, 0x75, 0x55, 0xa3, 0x43, 0x8b, 0x26, - 0x14, 0x03, 0x15, 0xdf, 0x8e, 0xc0, 0x98, 0xac, 0x72, 0xa6, 0x3c, 0xaf, - 0x62, 0xc5, 0x53, 0xe5, 0xaa, 0x09, 0x68, 0x2c, 0xc8, 0x47, 0x36, 0x57, - 0x2b, 0x58, 0x76, 0x4a, 0x65, 0x30, 0x46, 0x0e, 0xcb, 0xe9, 0x00, 0xc9, - 0x55, 0xb1, 0xc4, 0xa6, 0xfd, 0xaa, 0x5d, 0xbd, 0xcc, 0xda, 0x6b, 0xfe, - 0x4d, 0x22, 0x7d, 0x40, 0xf2, 0x53, 0x7b, 0x59, 0x1d, 0x50, 0x7a, 0x39, - 0x40, 0x19, 0xdb, 0xf4, 0x4c, 0xd2, 0x59, 0xb7, 0x73, 0xa8, 0x1e, 0xa8, - 0x60, 0xb6, 0xdf, 0xd0, 0x33, 0xf3, 0xa6, 0x17, 0x2c, 0x38, 0x5d, 0x4f, - 0x5f, 0x59, 0x84, 0x54, 0xa4, 0x41, 0xd4, 0x23, 0x19, 0x00, 0x52, 0xdc, - 0x7e, 0xbe, 0x89, 0xab, 0xa0, 0xa6, 0x8d, 0xb0, 0xb0, 0xc7, 0x2d, 0xe8, - 0xa1, 0x0c, 0xf8, 0x2e, 0x89, 0x49, 0xd6, 0x57, 0x97, 0x57, 0xc5, 0x48, - 0xd7, 0x2d, 0x57, 0x0b, 0xe7, 0xe6, 0xad, 0xc6, 0xf6, 0xaf, 0x88, 0xa6, - 0xfe, 0xab, 0x62, 0xbf, 0x8b, 0xdd, 0x65, 0x01, 0x0b, 0x25, 0x85, 0x42, - 0xf1, 0x54, 0x45, 0x59, 0xbe, 0x4e, 0x25, 0x37, 0x63, 0x16, 0xe3, 0xf1, - 0xc6, 0xcf, 0xa0, 0xb5, 0xe1, 0xa7, 0xbc, 0xa8, 0x20, 0xb8, 0x6d, 0xd3, - 0x2a, 0xf6, 0x83, 0x1a, 0x77, 0x3a, 0xb5, 0x50, 0x87, 0x59, 0x7e, 0x53, - 0x8f, 0x3f, 0x17, 0x21, 0x1a, 0xfd, 0x9d, 0xd9, 0x7d, 0xbc, 0x98, 0xaa, - 0xdf, 0xa6, 0xfc, 0xb1, 0x09, 0xca, 0x12, 0xeb, 0x91, 0x0f, 0x7f, 0x31, - 0x2e, 0x4b, 0x65, 0x58, 0xe7, 0x56, 0xfc, 0x46, 0x43, 0x2b, 0x5c, 0x08, - 0x11, 0xe4, 0x69, 0xc4, 0xaa, 0xae, 0x6b, 0xa6, 0x12, 0xad, 0x7e, 0xc1, - 0x4f, 0xe0, 0x62, 0x04, 0xbd, 0x27, 0x79, 0x44, 0xdc, 0x55, 0xf1, 0x58, - 0x4a, 0x4d, 0xc3, 0x34, 0x7a, 0x13, 0xf6, 0xee, 0x47, 0xcd, 0x01, 0xb4, - 0x65, 0xa7, 0x75, 0xa9, 0xf2, 0xb9, 0x0b, 0xd6, 0x21, 0xf9, 0x59, 0x1d, - 0xb2, 0x3c, 0xf5, 0x51, 0x98, 0x59, 0x5d, 0x52, 0x6a, 0x3d, 0x4f, 0x1e, - 0x1d, 0xfa, 0xf3, 0xd6, 0x8e, 0xba, 0xbe, 0xa9, 0x3b, 0xa7, 0x7e, 0xb3, - 0x73, 0xcc, 0xfb, 0xed, 0x80, 0x12, 0xf3, 0x33, 0xc4, 0x4c, 0xd3, 0x58, - 0x25, 0x56, 0x1c, 0x45, 0xa4, 0x28, 0x60, 0x05, 0x3f, 0xe1, 0x39, 0xc2, - 0x73, 0xad, 0x69, 0xa6, 0x3e, 0xae, 0xa9, 0xc3, 0x20, 0xe3, 0x5b, 0x07, - 0x62, 0x2a, 0x61, 0x46, 0xa3, 0x56, 0x90, 0x58, 0xb8, 0x4b, 0x54, 0x32, - 0x8e, 0x10, 0x0b, 0xec, 0xd7, 0xca, 0x7a, 0xb2, 0xfc, 0xa6, 0x4e, 0xaa, - 0xd2, 0xbb, 0xb8, 0xd8, 0x19, 0xfc, 0x27, 0x20, 0xdc, 0x3e, 0x1d, 0x53, - 0x90, 0x59, 0x24, 0x51, 0x37, 0x3b, 0x7a, 0x1b, 0x25, 0xf7, 0x51, 0xd4, - 0xb7, 0xb8, 0xfb, 0xa8, 0xaf, 0xa7, 0x18, 0xb5, 0xe8, 0xce, 0xed, 0xf0, - 0x66, 0x15, 0x5b, 0x36, 0x43, 0x4e, 0x2c, 0x59, 0x41, 0x55, 0x33, 0x43, - 0xef, 0x25, 0x6a, 0x02, 0x74, 0xde, 0x18, 0xc0, 0x57, 0xac, 0x7c, 0xa6, - 0x83, 0xaf, 0xe8, 0xc5, 0xf5, 0xe5, 0x53, 0x0a, 0xff, 0x2c, 0x2b, 0x48, - 0x5e, 0x57, 0x0d, 0x58, 0x13, 0x4a, 0xd8, 0x2f, 0x9b, 0x0d, 0x25, 0xe9, - 0x7a, 0xc8, 0x04, 0xb1, 0xb3, 0xa6, 0x36, 0xab, 0xce, 0xbd, 0x6a, 0xdb, - 0x17, 0xff, 0xe9, 0x22, 0xf4, 0x40, 0x2e, 0x54, 0x6f, 0x59, 0xd5, 0x4f, - 0xef, 0x38, 0xa1, 0x18, 0x2f, 0xf4, 0xba, 0xd1, 0xf5, 0xb6, 0x4e, 0xa8, - 0x41, 0xa8, 0xc2, 0xb6, 0x71, 0xd1, 0xdc, 0xf3, 0x4b, 0x18, 0xb1, 0x38, - 0xad, 0x4f, 0x68, 0x59, 0x4e, 0x54, 0x2a, 0x41, 0x3c, 0x23, 0x6a, 0xff, - 0xb7, 0xdb, 0x0a, 0xbe, 0x4f, 0xab, 0xae, 0xa6, 0xdb, 0xb0, 0x39, 0xc8, - 0xcf, 0xe8, 0x4d, 0x0d, 0x89, 0x2f, 0xe7, 0x49, 0xfd, 0x57, 0x6e, 0x57, - 0x61, 0x48, 0x46, 0x2d, 0xa9, 0x0a, 0x46, 0xe6, 0x29, 0xc6, 0xa8, 0xaf, - 0x82, 0xa6, 0x37, 0xac, 0xdd, 0xbf, 0x26, 0xde, 0x12, 0x02, 0xa7, 0x25, - 0xf5, 0x42, 0x2a, 0x55, 0x34, 0x59, 0x6b, 0x4e, 0x9f, 0x36, 0xbc, 0x15, - 0x3b, 0xf1, 0x36, 0xcf, 0x41, 0xb5, 0xc1, 0xa7, 0xe6, 0xa8, 0x84, 0xb8, - 0x05, 0xd4, 0xd2, 0xf6, 0x27, 0x1b, 0xf9, 0x3a, 0xfd, 0x50, 0x90, 0x59, - 0x3b, 0x53, 0x1a, 0x3f, 0x74, 0x20, 0x72, 0xfc, 0x00, 0xd9, 0x0f, 0xbc, - 0x63, 0xaa, 0xf3, 0xa6, 0x50, 0xb2, 0x92, 0xca, 0xb9, 0xeb, 0x39, 0x10, - 0x0f, 0x32, 0x88, 0x4b, 0x83, 0x58, 0xbb, 0x56, 0x94, 0x46, 0xad, 0x2a, - 0xb1, 0x07, 0x6e, 0xe3, 0xea, 0xc3, 0x62, 0xae, 0x68, 0xa6, 0x54, 0xad, - 0xf9, 0xc1, 0xef, 0xe0, 0x0e, 0x05, 0x53, 0x28, 0xec, 0x44, 0x07, 0x56, - 0xe1, 0x58, 0xef, 0x4c, 0x39, 0x34, 0xd3, 0x12, 0x4f, 0xee, 0xb7, 0xcc, - 0xab, 0xb3, 0x47, 0xa7, 0xa7, 0xa9, 0x59, 0xba, 0xa6, 0xd6, 0xc8, 0xf9, - 0xff, 0x1d, 0x2c, 0x3d, 0x3c, 0x52, 0x96, 0x59, 0x1b, 0x52, 0xec, 0x3c, - 0xae, 0x1d, 0x72, 0xf9, 0x5a, 0xd6, 0x24, 0xba, 0x8f, 0xa9, 0x55, 0xa7, - 0xd6, 0xb3, 0x00, 0xcd, 0xa1, 0xee, 0x2a, 0x13, 0x7c, 0x34, 0x1f, 0x4d, - 0xe6, 0x58, 0xf5, 0x55, 0xaf, 0x44, 0x0b, 0x28, 0xb5, 0x04, 0xa0, 0xe0, - 0xba, 0xc1, 0x33, 0xad, 0x6b, 0xa6, 0x85, 0xae, 0x29, 0xc4, 0xc0, 0xe3, - 0x07, 0x08, 0xf9, 0x2a, 0xc9, 0x46, 0xd0, 0x56, 0x74, 0x58, 0x5c, 0x4b, - 0xc6, 0x31, 0xe5, 0x0f, 0x65, 0xeb, 0x4d, 0xca, 0x25, 0xb2, 0xea, 0xa6, - 0x7d, 0xaa, 0x46, 0xbc, 0x4f, 0xd9, 0xc6, 0xfc, 0xc6, 0x20, 0x55, 0x3f, - 0x5b, 0x53, 0x8f, 0x59, 0xd6, 0x50, 0xba, 0x3a, 0xd3, 0x1a, 0x7e, 0xf6, - 0xb8, 0xd3, 0x54, 0xb8, 0xce, 0xa8, 0xd2, 0xa7, 0x71, 0xb5, 0x7d, 0xcf, - 0x90, 0xf1, 0x11, 0x16, 0xe0, 0x36, 0x97, 0x4e, 0x3c, 0x59, 0x0c, 0x55, - 0xc0, 0x42, 0x54, 0x25, 0xc0, 0x01, 0xd5, 0xdd, 0xa0, 0xbf, 0x1b, 0xac, - 0x83, 0xa6, 0xd2, 0xaf, 0x67, 0xc6, 0x9b, 0xe6, 0xfc, 0x0a, 0x95, 0x2d, - 0x8e, 0x48, 0x86, 0x57, 0xe8, 0x57, 0xb7, 0x49, 0x43, 0x2f, 0xf4, 0x0c, - 0x80, 0xe8, 0xf3, 0xc7, 0xb4, 0xb0, 0xa6, 0xa6, 0x6d, 0xab, 0x43, 0xbe, - 0x07, 0xdc, 0xc0, 0xff, 0x89, 0x23, 0x69, 0x41, 0x66, 0x54, 0x68, 0x59, - 0x81, 0x4f, 0x70, 0x38, 0xf9, 0x17, 0x86, 0xf3, 0x28, 0xd1, 0x91, 0xb6, - 0x2e, 0xa8, 0x63, 0xa8, 0x25, 0xb7, 0x03, 0xd2, 0x87, 0xf4, 0xef, 0x18, - 0x37, 0x39, 0xf9, 0x4f, 0x73, 0x59, 0x14, 0x54, 0xb4, 0x40, 0x9d, 0x22, - 0xc1, 0xfe, 0x18, 0xdb, 0x99, 0xbd, 0x17, 0xab, 0xbb, 0xa6, 0x2d, 0xb1, - 0xbd, 0xc8, 0x78, 0xe9, 0xf3, 0x0d, 0x1d, 0x30, 0x44, 0x4a, 0x1f, 0x58, - 0x47, 0x57, 0xfb, 0x47, 0xb3, 0x2c, 0xfe, 0x09, 0xa3, 0xe5, 0xa6, 0xc5, - 0x5d, 0xaf, 0x79, 0xa6, 0x76, 0xac, 0x54, 0xc0, 0xc5, 0xde, 0xbe, 0x02, - 0x40, 0x26, 0x68, 0x43, 0x5e, 0x55, 0x23, 0x59, 0x18, 0x4e, 0x18, 0x36, - 0x11, 0x15, 0x99, 0xf0, 0x9f, 0xce, 0xea, 0xb4, 0xa0, 0xa7, 0x10, 0xa9, - 0xec, 0xb8, 0x9a, 0xd4, 0x7c, 0xf7, 0xcc, 0x1b, 0x77, 0x3b, 0x48, 0x51, - 0x93, 0x59, 0xfb, 0x52, 0xa2, 0x3e, 0xd1, 0x1f, 0xcb, 0xfb, 0x61, 0xd8, - 0xa5, 0xbb, 0x2c, 0xaa, 0x0c, 0xa7, 0xa1, 0xb2, 0x1f, 0xcb, 0x5d, 0xec, - 0xe6, 0x10, 0x96, 0x32, 0xec, 0x4b, 0x95, 0x58, 0x94, 0x56, 0x29, 0x46, - 0x14, 0x2a, 0x0b, 0x07, 0xc8, 0xe2, 0x6e, 0xc3, 0x1a, 0xae, 0x68, 0xa6, - 0x95, 0xad, 0x76, 0xc2, 0x90, 0xe1, 0xb9, 0x05, 0xeb, 0x28, 0x59, 0x45, - 0x37, 0x56, 0xc9, 0x58, 0x99, 0x4c, 0xab, 0x33, 0x2e, 0x12, 0xa5, 0xed, - 0x2d, 0xcc, 0x51, 0xb3, 0x31, 0xa7, 0xd4, 0xa9, 0xc7, 0xba, 0x3b, 0xd7, - 0x78, 0xfa, 0x9b, 0x1e, 0xad, 0x3d, 0x7b, 0x52, 0x9a, 0x59, 0xd0, 0x51, - 0x74, 0x3c, 0x06, 0x1d, 0xce, 0xf8, 0xbc, 0xd5, 0xbe, 0xb9, 0x5f, 0xa9, - 0x71, 0xa7, 0x30, 0xb4, 0x8c, 0xcd, 0x4b, 0xef, 0xce, 0x13, 0x0b, 0x35, - 0x71, 0x4d, 0xff, 0x58, 0xc0, 0x55, 0x44, 0x44, 0x6f, 0x27, 0x0c, 0x04, - 0xfe, 0xdf, 0x41, 0xc1, 0xf2, 0xac, 0x6d, 0xa6, 0xcf, 0xae, 0xa6, 0xc4, - 0x67, 0xe4, 0xad, 0x08, 0x93, 0x2b, 0x2d, 0x47, 0xfe, 0x56, 0x54, 0x58, - 0x02, 0x4b, 0x35, 0x31, 0x3e, 0x0f, 0xbe, 0xea, 0xc4, 0xc9, 0xd1, 0xb1, - 0xd7, 0xa6, 0xb4, 0xaa, 0xb4, 0xbc, 0xeb, 0xd9, 0x71, 0xfd, 0x64, 0x21, - 0xd0, 0x3f, 0x98, 0x53, 0x88, 0x59, 0x8b, 0x50, 0x3a, 0x3a, 0x2e, 0x1a, - 0xd5, 0xf5, 0x23, 0xd3, 0xec, 0xb7, 0xaa, 0xa8, 0xee, 0xa7, 0xd4, 0xb5, - 0x09, 0xd0, 0x3f, 0xf2, 0xaf, 0x16, 0x6f, 0x37, 0xe2, 0x4e, 0x4f, 0x59, - 0xd5, 0x54, 0x4b, 0x42, 0xbc, 0x24, 0x11, 0x01, 0x39, 0xdd, 0x29, 0xbf, - 0xde, 0xab, 0x8f, 0xa6, 0x1c, 0xb0, 0xee, 0xc6, 0x3d, 0xe7, 0xa7, 0x0b, - 0x26, 0x2e, 0xf4, 0x48, 0xa8, 0x57, 0xc9, 0x57, 0x54, 0x49, 0xb0, 0x2e, - 0x4c, 0x0c, 0xda, 0xe7, 0x6d, 0xc7, 0x67, 0xb0, 0x98, 0xa6, 0xa7, 0xab, - 0xb8, 0xbe, 0xa3, 0xdc, 0x6d, 0x00, 0x24, 0x24, 0xde, 0x41, 0xa0, 0x54, - 0x59, 0x59, 0x36, 0x4f, 0xe7, 0x37, 0x54, 0x17, 0xde, 0xf2, 0x95, 0xd0, - 0x31, 0xb6, 0x0c, 0xa8, 0x87, 0xa8, 0x89, 0xb7, 0x98, 0xd2, 0x2f, 0xf5, - 0x95, 0x19, 0xb8, 0x39, 0x48, 0x50, 0x7b, 0x59, 0xd8, 0x53, 0x3e, 0x40, - 0xfe, 0x21, 0x15, 0xfe, 0x7f, 0xda, 0x23, 0xbd, 0xe4, 0xaa, 0xc8, 0xa6, - 0x80, 0xb1, 0x45, 0xc9, 0x1c, 0xea, 0x9e, 0x0e, 0xab, 0x30, 0xa6, 0x4a, - 0x3b, 0x58, 0x21, 0x57, 0x95, 0x47, 0x1d, 0x2c, 0x55, 0x09, 0xff, 0xe4, - 0x24, 0xc5, 0x14, 0xaf, 0x72, 0xa6, 0xb6, 0xac, 0xca, 0xc0, 0x68, 0xdf, - 0x66, 0x03, 0xdc, 0x26, 0xd9, 0x43, 0x90, 0x55, 0x12, 0x59, 0xc4, 0x4d, - 0x8c, 0x35, 0x70, 0x14, 0xea, 0xef, 0x16, 0xce, 0x89, 0xb4, 0x87, 0xa7, - 0x39, 0xa9, 0x55, 0xb9, 0x30, 0xd5, 0x27, 0xf8, 0x6d, 0x1c, 0xf9, 0x3b, - 0x8d, 0x51, 0x99, 0x59, 0xb9, 0x52, 0x25, 0x3e, 0x35, 0x1f, 0x1a, 0xfb, - 0xcf, 0xd7, 0x2f, 0xbb, 0x02, 0xaa, 0x1b, 0xa7, 0xfd, 0xb2, 0xa6, 0xcb, - 0x08, 0xed, 0x89, 0x11, 0x28, 0x33, 0x41, 0x4c, 0xb3, 0x58, 0x64, 0x56, - 0xbf, 0x45, 0x7e, 0x29, 0x5c, 0x06, 0x2a, 0xe2, 0xee, 0xc2, 0xd6, 0xad, - 0x67, 0xa6, 0xd9, 0xad, 0xf2, 0xc2, 0x33, 0xe2, 0x61, 0x06, 0x88, 0x29, - 0xbf, 0x45, 0x6a, 0x56, 0xaf, 0x58, 0x3f, 0x4c, 0x21, 0x33, 0x84, 0x11, - 0xfe, 0xec, 0xa4, 0xcb, 0xf5, 0xb2, 0x1e, 0xa7, 0x01, 0xaa, 0x35, 0xbb, - 0xd6, 0xd7, 0x20, 0xfb, 0x3e, 0x1f, 0x27, 0x3e, 0xbf, 0x52, 0x96, 0x59, - 0x8d, 0x51, 0xf1, 0x3b, 0x68, 0x1c, 0x1e, 0xf8, 0x2c, 0xd5, 0x4f, 0xb9, - 0x39, 0xa9, 0x85, 0xa7, 0x91, 0xb4, 0x17, 0xce, 0xf6, 0xef, 0x73, 0x14, - 0x94, 0x35, 0xc8, 0x4d, 0x11, 0x59, 0x90, 0x55, 0xd2, 0x43, 0xd7, 0x26, - 0x5f, 0x03, 0x5f, 0xdf, 0xc8, 0xc0, 0xb1, 0xac, 0x74, 0xa6, 0x16, 0xaf, - 0x29, 0xc5, 0x06, 0xe5, 0x5d, 0x09, 0x23, 0x2c, 0x98, 0x47, 0x25, 0x57, - 0x37, 0x58, 0xa5, 0x4a, 0xa3, 0x30, 0x98, 0x0e, 0x14, 0xea, 0x40, 0xc9, - 0x7b, 0xb1, 0xca, 0xa6, 0xe5, 0xaa, 0x28, 0xbd, 0x85, 0xda, 0x1b, 0xfe, - 0x06, 0x22, 0x44, 0x40, 0xd9, 0x53, 0x7c, 0x59, 0x43, 0x50, 0xb4, 0x39, - 0x8c, 0x19, 0x2b, 0xf5, 0x8e, 0xd2, 0x88, 0xb7, 0x83, 0xa8, 0x0f, 0xa8, - 0x34, 0xb6, 0x9b, 0xd0, 0xe5, 0xf2, 0x5a, 0x17, 0xee, 0x37, 0x3b, 0x4f, - 0x54, 0x59, 0xa4, 0x54, 0xd4, 0x41, 0x20, 0x24, 0x66, 0x00, 0x9c, 0xdc, - 0xb2, 0xbe, 0xa7, 0xab, 0x95, 0xa6, 0x6d, 0xb0, 0x71, 0xc7, 0xe2, 0xe7, - 0x53, 0x0c, 0xb5, 0x2e, 0x5a, 0x49, 0xc8, 0x57, 0xa8, 0x57, 0xef, 0x48, - 0x21, 0x2e, 0x9f, 0x0b, 0x37, 0xe7, 0xe9, 0xc6, 0x16, 0xb0, 0x91, 0xa6, - 0xdf, 0xab, 0x2f, 0xbf, 0x40, 0xdd, 0x17, 0x01, 0xc3, 0x24, 0x50, 0x42, - 0xd8, 0x54, 0x4d, 0x59, 0xe0, 0x4e, 0x67, 0x37, 0xab, 0x16, 0x37, 0xf2, - 0x02, 0xd0, 0xd2, 0xb5, 0xe9, 0xa7, 0xb0, 0xa8, 0xed, 0xb7, 0x2b, 0xd3, - 0xdb, 0xf5, 0x36, 0x1a, 0x3f, 0x3a, 0x8e, 0x50, 0x88, 0x59, 0x97, 0x53, - 0xc9, 0x3f, 0x5e, 0x21, 0x6b, 0xfd, 0xe2, 0xd9, 0xb3, 0xbc, 0xad, 0xaa, - 0xdb, 0xa6, 0xd2, 0xb1, 0xcc, 0xc9, 0xc5, 0xea, 0x42, 0x0f, 0x3f, 0x31, - 0x02, 0x4b, 0x59, 0x58, 0xf8, 0x56, 0x2e, 0x47, 0x86, 0x2b, 0xae, 0x08, - 0x58, 0xe4, 0xa7, 0xc4, 0xc7, 0xae, 0x70, 0xa6, 0xf4, 0xac, 0x45, 0xc1, - 0x06, 0xe0, 0x13, 0x04, 0x74, 0x27, 0x4a, 0x44, 0xc2, 0x55, 0xfd, 0x58, - 0x70, 0x4d, 0x02, 0x35, 0xc8, 0x13, 0x44, 0xef, 0x86, 0xcd, 0x2e, 0xb4, - 0x6c, 0xa7, 0x65, 0xa9, 0xbf, 0xb9, 0xc5, 0xd5, 0xd4, 0xf8, 0x0c, 0x1d, - 0x7b, 0x3c, 0xd3, 0x51, 0x98, 0x59, 0x7c, 0x52, 0xa3, 0x3d, 0x9a, 0x1e, - 0x6a, 0xfa, 0x3a, 0xd7, 0xc0, 0xba, 0xd4, 0xa9, 0x2f, 0xa7, 0x58, 0xb3, - 0x2d, 0xcc, 0xb3, 0xed, 0x2f, 0x12, 0xb6, 0x33, 0x99, 0x4c, 0xcb, 0x58, - 0x36, 0x56, 0x53, 0x45, 0xe6, 0x28, 0xb2, 0x05, 0x87, 0xe1, 0x74, 0xc2, - 0x90, 0xad, 0x69, 0xa6, 0x1c, 0xae, 0x73, 0xc3, 0xd1, 0xe2, 0x10, 0x07, - 0x1b, 0x2a, 0x2e, 0x46, 0x94, 0x56, 0x96, 0x58, 0xe7, 0x4b, 0x91, 0x32, - 0xde, 0x10, 0x58, 0xec, 0x15, 0xcb, 0xa2, 0xb2, 0x07, 0xa7, 0x33, 0xaa, - 0xa3, 0xbb, 0x70, 0xd8, 0xc8, 0xfb, 0xe2, 0x1f, 0x9f, 0x3e, 0x02, 0x53, - 0x91, 0x59, 0x45, 0x51, 0x74, 0x3b, 0xc2, 0x1b, 0x77, 0xf7, 0x92, 0xd4, - 0xe9, 0xb8, 0x0d, 0xa9, 0xa4, 0xa7, 0xe9, 0xb4, 0xab, 0xce, 0x9a, 0xf0, - 0x1d, 0x15, 0x1b, 0x36, 0x1d, 0x4e, 0x23, 0x59, 0x5c, 0x55, 0x63, 0x43, - 0x3a, 0x26, 0xb6, 0x02, 0xc0, 0xde, 0x4e, 0xc0, 0x73, 0xac, 0x7a, 0xa6, - 0x5f, 0xaf, 0xae, 0xc5, 0xa7, 0xe5, 0x08, 0x0a, 0xb7, 0x2c, 0xff, 0x47, - 0x4c, 0x57, 0x19, 0x58, 0x43, 0x4a, 0x17, 0x30, 0xea, 0x0d, 0x73, 0xe9, - 0xb4, 0xc8, 0x2e, 0xb1, 0xb6, 0xa6, 0x1e, 0xab, 0x99, 0xbd, 0x22, 0xdb, - 0xc7, 0xfe, 0xa4, 0x22, 0xb9, 0x40, 0x16, 0x54, 0x72, 0x59, 0xf8, 0x4f, - 0x2f, 0x39, 0xeb, 0x18, 0x7c, 0xf4, 0xff, 0xd1, 0x21, 0xb7, 0x61, 0xa8, - 0x30, 0xa8, 0x95, 0xb6, 0x2d, 0xd1, 0x8e, 0xf3, 0x00, 0x18, 0x74, 0x38, - 0x88, 0x4f, 0x65, 0x59, 0x64, 0x54, 0x66, 0x41, 0x7f, 0x23, 0xbd, 0xff, - 0xfd, 0xdb, 0x3f, 0xbe, 0x6b, 0xab, 0xa6, 0xa6, 0xb8, 0xb0, 0xf8, 0xc7, - 0x87, 0xe8, 0xfb, 0x0c, 0x49, 0x2f, 0xbb, 0x49, 0xe9, 0x57, 0x85, 0x57, - 0x8b, 0x48, 0x8c, 0x2d, 0xf7, 0x0a, 0x92, 0xe6, 0x64, 0xc6, 0xcd, 0xaf, - 0x84, 0xa6, 0x1b, 0xac, 0xa7, 0xbf, 0xdd, 0xdd, 0xc3, 0x01, 0x5f, 0x25, - 0xc1, 0x42, 0x11, 0x55, 0x3b, 0x59, 0x91, 0x4e, 0xde, 0x36, 0x06, 0x16, - 0x8e, 0xf1, 0x71, 0xcf, 0x73, 0xb5, 0xca, 0xa7, 0xd7, 0xa8, 0x53, 0xb8, - 0xc0, 0xd3, 0x85, 0xf6, 0xdb, 0x1a, 0xbe, 0x3a, 0xdc, 0x50, 0x8a, 0x59, - 0x5d, 0x53, 0x4d, 0x3f, 0xc2, 0x20, 0xbd, 0xfc, 0x49, 0xd9, 0x41, 0xbc, - 0x7c, 0xaa, 0xe9, 0xa6, 0x2a, 0xb2, 0x51, 0xca, 0x6e, 0xeb, 0xeb, 0x0f, - 0xcd, 0x31, 0x5e, 0x4b, 0x77, 0x58, 0xcc, 0x56, 0xc8, 0x46, 0xef, 0x2a, - 0x02, 0x08, 0xb8, 0xe3, 0x24, 0xc4, 0x83, 0xae, 0x6a, 0xa6, 0x34, 0xad, - 0xc2, 0xc1, 0xa4, 0xe0, 0xc0, 0x04, 0x0e, 0x28, 0xb6, 0x44, 0xf4, 0x55, - 0xe9, 0x58, 0x19, 0x4d, 0x78, 0x34, 0x21, 0x13, 0x9a, 0xee, 0xfb, 0xcc, - 0xd3, 0xb3, 0x53, 0xa7, 0x90, 0xa9, 0x2a, 0xba, 0x5e, 0xd6, 0x7d, 0xf9, - 0xb1, 0x1d, 0xf5, 0x3c, 0x1a, 0x52, 0x99, 0x59, 0x37, 0x52, 0x29, 0x3d, - 0xf6, 0x1d, 0xc3, 0xf9, 0x9d, 0xd6, 0x57, 0xba, 0xa4, 0xa9, 0x48, 0xa7, - 0xb0, 0xb3, 0xbb, 0xcc, 0x58, 0xee, 0xd9, 0x12, 0x3f, 0x34, 0xf4, 0x4c, - 0xdf, 0x58, 0x0a, 0x56, 0xe1, 0x44, 0x52, 0x28, 0x04, 0x05, 0xe9, 0xe0, - 0xf4, 0xc1, 0x51, 0xad, 0x69, 0xa6, 0x65, 0xae, 0xee, 0xc3, 0x75, 0xe3, - 0xba, 0x07, 0xb2, 0x2a, 0x9a, 0x46, 0xbb, 0x56, 0x82, 0x58, 0x85, 0x4b, - 0x08, 0x32, 0x32, 0x10, 0xb2, 0xeb, 0x8d, 0xca, 0x4b, 0xb2, 0xf3, 0xa6, - 0x65, 0xaa, 0x13, 0xbc, 0x07, 0xd9, 0x79, 0xfc, 0x7b, 0x20, 0x1e, 0x3f, - 0x40, 0x53, 0x8c, 0x59, 0xfd, 0x50, 0xf2, 0x3a, 0x21, 0x1b, 0xcb, 0xf6, - 0xfe, 0xd3, 0x7f, 0xb8, 0xe5, 0xa8, 0xc2, 0xa7, 0x47, 0xb5, 0x3a, 0xcf, - 0x42, 0xf1, 0xc4, 0x15, 0xa3, 0x36, 0x70, 0x4e, 0x35, 0x59, 0x26, 0x55, - 0xf3, 0x42, 0x9d, 0x25, 0x0c, 0x02, 0x20, 0xde, 0xd7, 0xbf, 0x36, 0xac, - 0x80, 0xa6, 0xad, 0xaf, 0x2d, 0xc6, 0x4e, 0xe6, 0xaf, 0x0a, 0x4d, 0x2d, - 0x65, 0x48, 0x71, 0x57, 0xfa, 0x57, 0xe2, 0x49, 0x85, 0x2f, 0x44, 0x0d, - 0xca, 0xe8, 0x33, 0xc8, 0xd6, 0xb0, 0xaf, 0xa6, 0x4f, 0xab, 0x13, 0xbe, - 0xb9, 0xdb, 0x76, 0xff, 0x3e, 0x23, 0x32, 0x41, 0x4f, 0x54, 0x6a, 0x59, - 0xa6, 0x4f, 0xaf, 0x38, 0x41, 0x18, 0xd8, 0xf3, 0x68, 0xd1, 0xc2, 0xb6, - 0x39, 0xa8, 0x57, 0xa8, 0xf3, 0xb6, 0xc4, 0xd1, 0x36, 0xf4, 0xa4, 0x18, - 0xfa, 0x38, 0xd5, 0x4f, 0x70, 0x59, 0x2d, 0x54, 0xec, 0x40, 0xe6, 0x22, - 0x0e, 0xff, 0x63, 0xdb, 0xca, 0xbd, 0x34, 0xab, 0xb2, 0xa6, 0x0b, 0xb1, - 0x7b, 0xc8, 0x2f, 0xe9, 0xa2, 0x0d, 0xdc, 0x2f, 0x1b, 0x4a, 0x0a, 0x58, - 0x5e, 0x57, 0x28, 0x48, 0xf6, 0x2c, 0x50, 0x0a, 0xe9, 0xe5, 0xe7, 0xc5, - 0x7c, 0xaf, 0x80, 0xa6, 0x55, 0xac, 0x21, 0xc0, 0x79, 0xde, 0x70, 0x02, - 0xfa, 0x25, 0x32, 0x43, 0x47, 0x55, 0x2a, 0x59, 0x40, 0x4e, 0x54, 0x36, - 0x62, 0x15, 0xe2, 0xf0, 0xe5, 0xce, 0x11, 0xb5, 0xb1, 0xa7, 0xfb, 0xa8, - 0xbd, 0xb8, 0x55, 0xd4, 0x2e, 0xf7, 0x7f, 0x1b, 0x3f, 0x3b, 0x23, 0x51, - 0x94, 0x59, 0x18, 0x53, 0xd9, 0x3e, 0x1c, 0x20, 0x17, 0xfc, 0xac, 0xd8, - 0xd3, 0xbb, 0x48, 0xaa, 0xff, 0xa6, 0x7c, 0xb2, 0xdd, 0xca, 0x13, 0xec, - 0x95, 0x10, 0x59, 0x32, 0xbe, 0x4b, 0x8c, 0x58, 0xa7, 0x56, 0x5a, 0x46, - 0x5a, 0x2a, 0x58, 0x07, 0x14, 0xe3, 0xa7, 0xc3, 0x3c, 0xae, 0x66, 0xa6, - 0x78, 0xad, 0x3d, 0xc2, 0x44, 0xe1, 0x6c, 0x05, 0xa6, 0x28, 0x23, 0x45, - 0x26, 0x56, 0xd1, 0x58, 0xc1, 0x4c, 0xee, 0x33, 0x78, 0x12, 0xf4, 0xed, - 0x6f, 0xcc, 0x76, 0xb3, 0x3e, 0xa7, 0xbd, 0xa9, 0x96, 0xba, 0xf7, 0xd6, - 0x25, 0xfa, 0x54, 0x1e, 0x72, 0x3d, 0x5e, 0x52, 0x99, 0x59, 0xf1, 0x51, - 0xad, 0x3c, 0x52, 0x1d, 0x1b, 0xf9, 0x03, 0xd6, 0xef, 0xb9, 0x72, 0xa9, - 0x67, 0xa7, 0x03, 0xb4, 0x4f, 0xcd, 0xfb, 0xee, 0x83, 0x13, 0xc9, 0x34, - 0x4a, 0x4d, 0xf7, 0x58, 0xd5, 0x55, 0x79, 0x44, 0xb3, 0x27, 0x5d, 0x04, - 0x46, 0xe0, 0x7c, 0xc1, 0x0b, 0xad, 0x71, 0xa6, 0xa8, 0xae, 0x71, 0xc4, - 0x16, 0xe4, 0x64, 0x08, 0x49, 0x2b, 0x02, 0x47, 0xe7, 0x56, 0x64, 0x58, - 0x2b, 0x4b, 0x77, 0x31, 0x8d, 0x0f, 0x08, 0xeb, 0x06, 0xca, 0xf6, 0xb1, - 0xdf, 0xa6, 0x9b, 0xaa, 0x81, 0xbc, 0xa4, 0xd9, 0x21, 0xfd, 0x1e, 0x21, - 0x93, 0x3f, 0x81, 0x53, 0x87, 0x59, 0xb1, 0x50, 0x73, 0x3a, 0x7b, 0x1a, - 0x23, 0xf6, 0x67, 0xd3, 0x1b, 0xb8, 0xbc, 0xa8, 0xe0, 0xa7, 0xa8, 0xb5, - 0xc6, 0xcf, 0xf0, 0xf1, 0x66, 0x16, 0x2d, 0x37, 0xc2, 0x4e, 0x42, 0x59, - 0xf2, 0x54, 0x7f, 0x42, 0x03, 0x25, 0x61, 0x01, 0x81, 0xdd, 0x60, 0xbf, - 0xf9, 0xab, 0x8a, 0xa6, 0xf9, 0xaf, 0xb0, 0xc6, 0xf3, 0xe6, 0x59, 0x0b, - 0xe1, 0x2d, 0xc8, 0x48, 0x96, 0x57, 0xd9, 0x57, 0x81, 0x49, 0xf5, 0x2e, - 0x97, 0x0c, 0x29, 0xe8, 0xa7, 0xc7, 0x8e, 0xb0, 0x9c, 0xa6, 0x8d, 0xab, - 0x83, 0xbe, 0x58, 0xdc, 0x21, 0x00, 0xdb, 0x23, 0xa8, 0x41, 0x87, 0x54, - 0x5f, 0x59, 0x57, 0x4f, 0x2b, 0x38, 0x9a, 0x17, 0x30, 0xf3, 0xd6, 0xd0, - 0x5e, 0xb6, 0x1c, 0xa8, 0x73, 0xa8, 0x60, 0xb7, 0x4f, 0xd2, 0xe6, 0xf4, - 0x45, 0x19, 0x7e, 0x39, 0x24, 0x50, 0x78, 0x59, 0xf2, 0x53, 0x76, 0x40, - 0x47, 0x22, 0x63, 0xfe, 0xc8, 0xda, 0x55, 0xbd, 0xff, 0xaa, 0xc0, 0xa6, - 0x5a, 0xb1, 0x06, 0xc9, 0xd1, 0xe9, 0x4f, 0x0e, 0x69, 0x30, 0x7c, 0x4a, - 0x2a, 0x58, 0x36, 0x57, 0xc3, 0x47, 0x61, 0x2c, 0xa6, 0x09, 0x46, 0xe5, - 0x64, 0xc5, 0x33, 0xaf, 0x76, 0xa6, 0x98, 0xac, 0x93, 0xc0, 0x1e, 0xdf, - 0x18, 0x03, 0x95, 0x26, 0xa3, 0x43, 0x7b, 0x55, 0x19, 0x59, 0xec, 0x4d, - 0xcb, 0x35, 0xbc, 0x14, 0x38, 0xf0, 0x58, 0xce, 0xb3, 0xb4, 0x93, 0xa7, - 0x28, 0xa9, 0x21, 0xb9, 0xee, 0xd4, 0xd7, 0xf7, 0x22, 0x1c, 0xc0, 0x3b, - 0x6a, 0x51, 0x99, 0x59, 0xd6, 0x52, 0x5f, 0x3e, 0x7d, 0x1f, 0x6b, 0xfb, - 0x13, 0xd8, 0x65, 0xbb, 0x14, 0xaa, 0x16, 0xa7, 0xd0, 0xb2, 0x6a, 0xcb, - 0xb9, 0xec, 0x3c, 0x11, 0xe8, 0x32, 0x18, 0x4c, 0xa5, 0x58, 0x7d, 0x56, - 0xec, 0x45, 0xc6, 0x29, 0xab, 0x06, 0x73, 0xe2, 0x29, 0xc3, 0xf6, 0xad, - 0x65, 0xa6, 0xbc, 0xad, 0xb7, 0xc2, 0xe9, 0xe1, 0x13, 0x06, 0x41, 0x29, - 0x8f, 0x45, 0x53, 0x56, 0xbc, 0x58, 0x66, 0x4c, 0x64, 0x33, 0xcf, 0x11, - 0x4e, 0xed, 0xe0, 0xcb, 0x22, 0xb3, 0x23, 0xa7, 0xef, 0xa9, 0x01, 0xbb, - 0x8f, 0xd7, 0xd3, 0xfa, 0xf2, 0x1e, 0xef, 0x3d, 0xa1, 0x52, 0x96, 0x59, - 0xae, 0x51, 0x2c, 0x3c, 0xb3, 0x1c, 0x6c, 0xf8, 0x70, 0xd5, 0x81, 0xb9, - 0x4b, 0xa9, 0x7c, 0xa7, 0x63, 0xb4, 0xd9, 0xcd, 0xa5, 0xef, 0x2a, 0x14, - 0x52, 0x35, 0xa2, 0x4d, 0x08, 0x59, 0xa7, 0x55, 0x06, 0x44, 0x1d, 0x27, - 0xaf, 0x03, 0xa8, 0xdf, 0xff, 0xc0, 0xd0, 0xac, 0x6f, 0xa6, 0xf7, 0xae, - 0xeb, 0xc4, 0xbf, 0xe4, 0x09, 0x09, 0xe5, 0x2b, 0x63, 0x47, 0x16, 0x57, - 0x44, 0x58, 0xce, 0x4a, 0xe8, 0x30, 0xe4, 0x0e, 0x61, 0xea, 0x80, 0xc9, - 0x9f, 0xb1, 0xd2, 0xa6, 0xcc, 0xaa, 0xf3, 0xbc, 0x40, 0xda, 0xcb, 0xfd, - 0xbd, 0x21, 0x0d, 0x40, 0xbc, 0x53, 0x81, 0x59, 0x66, 0x50, 0xf0, 0x39, - 0xd9, 0x19, 0x76, 0xf5, 0xd6, 0xd2, 0xb2, 0xb7, 0x99, 0xa8, 0xfe, 0xa7, - 0x08, 0xb6, 0x57, 0xd0, 0x99, 0xf2, 0x0c, 0x17, 0xb3, 0x37, 0x13, 0x4f, - 0x51, 0x59, 0xbb, 0x54, 0x0b, 0x42, 0x68, 0x24, 0xb3, 0x00, 0xe7, 0xdc, - 0xe6, 0xbe, 0xc3, 0xab, 0x90, 0xa6, 0x49, 0xb0, 0x34, 0xc7, 0x95, 0xe7, - 0x07, 0x0c, 0x6f, 0x2e, 0x30, 0x49, 0xb6, 0x57, 0xb9, 0x57, 0x1d, 0x49, - 0x63, 0x2e, 0xee, 0x0b, 0x83, 0xe7, 0x25, 0xc7, 0x3b, 0xb0, 0x95, 0xa6, - 0xc4, 0xab, 0xf9, 0xbe, 0xf6, 0xdc, 0xcb, 0x00, 0x78, 0x24, 0x1d, 0x42, - 0xbe, 0x54, 0x52, 0x59, 0x09, 0x4f, 0xa0, 0x37, 0xfc, 0x16, 0x7f, 0xf2, - 0x4b, 0xd0, 0xf9, 0xb5, 0xfd, 0xa7, 0x9a, 0xa8, 0xbf, 0xb7, 0xe9, 0xd2, - 0x8a, 0xf5, 0xee, 0x19, 0x00, 0x3a, 0x6e, 0x50, 0x81, 0x59, 0xb8, 0x53, - 0xfb, 0x3f, 0xad, 0x21, 0xb4, 0xfd, 0x2d, 0xda, 0xe5, 0xbc, 0xc7, 0xaa, - 0xd3, 0xa6, 0xab, 0xb1, 0x8e, 0xc9, 0x75, 0xea, 0xfb, 0x0e, 0xf6, 0x30, - 0xde, 0x4a, 0x45, 0x58, 0x10, 0x57, 0x59, 0x47, 0xd0, 0x2b, 0xf7, 0x08, - 0xa8, 0xe4, 0xdf, 0xc4, 0xea, 0xae, 0x72, 0xa6, 0xd5, 0xac, 0x0f, 0xc1, - 0xbb, 0xdf, 0xc5, 0x03, 0x2e, 0x27, 0x15, 0x44, 0xac, 0x55, 0x06, 0x59, - 0x98, 0x4d, 0x40, 0x35, 0x17, 0x14, 0x8f, 0xef, 0xc9, 0xcd, 0x57, 0xb4, - 0x78, 0xa7, 0x52, 0xa9, 0x8b, 0xb9, 0x85, 0xd5, 0x7e, 0xf8, 0xcb, 0x1c, - 0x37, 0x3c, 0xba, 0x51, 0x93, 0x59, 0x9c, 0x52, 0xde, 0x3d, 0xe1, 0x1e, - 0xbc, 0xfa, 0x7e, 0xd7, 0xf3, 0xba, 0xe8, 0xa9, 0x29, 0xa7, 0x29, 0xb3, - 0xf5, 0xcb, 0x5f, 0xed, 0xe5, 0x11, 0x74, 0x33, 0x71, 0x4c, 0xc0, 0x58, - 0x4d, 0x56, 0x81, 0x45, 0x30, 0x29, 0xfc, 0x05, 0xd8, 0xe1, 0xa5, 0xc2, - 0xb6, 0xad, 0x64, 0xa6, 0x00, 0xae, 0x36, 0xc3, 0x88, 0xe2, 0xc0, 0x06, - 0xd7, 0x29, 0xfc, 0x45, 0x80, 0x56, 0xa3, 0x58, 0x0e, 0x4c, 0xd4, 0x32, - 0x2b, 0x11, 0xa3, 0xec, 0x58, 0xcb, 0xc8, 0xb2, 0x10, 0xa7, 0x1e, 0xaa, - 0x70, 0xbb, 0x26, 0xd8, 0x7f, 0xfb, 0x92, 0x1f, 0x6c, 0x3e, 0xe1, 0x52, - 0x94, 0x59, 0x66, 0x51, 0xae, 0x3b, 0x0f, 0x1c, 0xc3, 0xf7, 0xd9, 0xd4, - 0x17, 0xb9, 0x22, 0xa9, 0x96, 0xa7, 0xbf, 0xb4, 0x68, 0xce, 0x4d, 0xf0, - 0xd1, 0x14, 0xdb, 0x35, 0xf7, 0x4d, 0x1b, 0x59, 0x72, 0x55, 0x99, 0x43, - 0x80, 0x26, 0x07, 0x03, 0x06, 0xdf, 0x88, 0xc0, 0x8d, 0xac, 0x79, 0xa6, - 0x3d, 0xaf, 0x70, 0xc5, 0x5e, 0xe5, 0xb7, 0x09, 0x76, 0x2c, 0xcd, 0x47, - 0x3c, 0x57, 0x26, 0x58, 0x70, 0x4a, 0x59, 0x30, 0x38, 0x0e, 0xbf, 0xe9, - 0xf3, 0xc8, 0x52, 0xb1, 0xbf, 0xa6, 0x03, 0xab, 0x66, 0xbd, 0xd8, 0xda, - 0x7a, 0xfe, 0x59, 0x22, 0x85, 0x40, 0xf9, 0x53, 0x77, 0x59, 0x1b, 0x50, - 0x6c, 0x39, 0x36, 0x19, 0xca, 0xf4, 0x44, 0xd2, 0x4d, 0xb7, 0x74, 0xa8, - 0x1e, 0xa8, 0x69, 0xb6, 0xea, 0xd0, 0x40, 0xf3, 0xb5, 0x17, 0x34, 0x38, - 0x67, 0x4f, 0x5b, 0x59, 0x85, 0x54, 0x95, 0x41, 0xcc, 0x23, 0x09, 0x00, - 0x46, 0xdc, 0x77, 0xbe, 0x82, 0xab, 0xa2, 0xa6, 0x93, 0xb0, 0xbb, 0xc7, - 0x3a, 0xe8, 0xb0, 0x0c, 0x02, 0x2f, 0x90, 0x49, 0xdc, 0x57, 0x91, 0x57, - 0xbe, 0x48, 0xcd, 0x2d, 0x45, 0x0b, 0xdf, 0xe6, 0xa0, 0xc6, 0xef, 0xaf, - 0x8a, 0xa6, 0xff, 0xab, 0x70, 0xbf, 0x93, 0xdd, 0x76, 0x01, 0x16, 0x25, - 0x8d, 0x42, 0xf9, 0x54, 0x40, 0x59, 0xb9, 0x4e, 0x1b, 0x37, 0x53, 0x16, - 0xda, 0xf1, 0xb6, 0xcf, 0x9c, 0xb5, 0xdb, 0xa7, 0xc2, 0xa8, 0x26, 0xb8, - 0x7b, 0xd3, 0x36, 0xf6, 0x90, 0x1a, 0x83, 0x3a, 0xb8, 0x50, 0x8b, 0x59, - 0x76, 0x53, 0x87, 0x3f, 0x0a, 0x21, 0x0c, 0xfd, 0x91, 0xd9, 0x74, 0xbc, - 0x93, 0xaa, 0xe3, 0xa6, 0xff, 0xb1, 0x18, 0xca, 0x1a, 0xeb, 0xa4, 0x0f, - 0x86, 0x31, 0x38, 0x4b, 0x66, 0x58, 0xe4, 0x56, 0xf3, 0x46, 0x39, 0x2b, - 0x4c, 0x08, 0x06, 0xe4, 0x5e, 0xc4, 0xa4, 0xae, 0x6a, 0xa6, 0x19, 0xad, - 0x86, 0xc1, 0x5e, 0xe0, 0x6d, 0x04, 0xcb, 0x27, 0x81, 0x44, 0xe0, 0x55, - 0xf0, 0x58, 0x43, 0x4d, 0xb7, 0x34, 0x6e, 0x13, 0xe8, 0xee, 0x3a, 0xcd, - 0xff, 0xb3, 0x5b, 0xa7, 0x81, 0xa9, 0xf3, 0xb9, 0x1c, 0xd6, 0x2c, 0xf9, - 0x66, 0x1d, 0xbe, 0x3c, 0xf7, 0x51, 0x9d, 0x59, 0x52, 0x52, 0x65, 0x3d, - 0x3e, 0x1e, 0x13, 0xfa, 0xe4, 0xd6, 0x88, 0xba, 0xb9, 0xa9, 0x3d, 0xa7, - 0x86, 0xb3, 0x7c, 0xcc, 0x0b, 0xee, 0x8b, 0x12, 0x00, 0x34, 0xca, 0x4c, - 0xd6, 0x58, 0x1f, 0x56, 0x15, 0x45, 0x97, 0x28, 0x53, 0x05, 0x33, 0xe1, - 0x2d, 0xc2, 0x6f, 0xad, 0x69, 0xa6, 0x43, 0xae, 0xb6, 0xc3, 0x28, 0xe3, - 0x6c, 0x07, 0x6e, 0x2a, 0x66, 0x46, 0xac, 0x56, 0x89, 0x58, 0xb3, 0x4b, - 0x48, 0x32, 0x80, 0x10, 0xff, 0xeb, 0xcb, 0xca, 0x74, 0xb2, 0xfb, 0xa6, - 0x4f, 0xaa, 0xde, 0xbb, 0xc3, 0xd8, 0x26, 0xfc, 0x36, 0x20, 0xe3, 0x3e, - 0x23, 0x53, 0x91, 0x59, 0x1b, 0x51, 0x2f, 0x3b, 0x6c, 0x1b, 0x18, 0xf7, - 0x46, 0xd4, 0xac, 0xb8, 0xf9, 0xa8, 0xb3, 0xa7, 0x1b, 0xb5, 0xfb, 0xce, - 0xf1, 0xf0, 0x7b, 0x15, 0x61, 0x36, 0x4c, 0x4e, 0x2c, 0x59, 0x3f, 0x55, - 0x26, 0x43, 0xe7, 0x25, 0x59, 0x02, 0x6a, 0xde, 0x0e, 0xc0, 0x51, 0xac, - 0x7e, 0xa6, 0x88, 0xaf, 0xf4, 0xc5, 0xff, 0xe5, 0x65, 0x0a, 0x06, 0x2d, - 0x38, 0x48, 0x5f, 0x57, 0x08, 0x58, 0x11, 0x4a, 0xc5, 0x2f, 0x94, 0x0d, - 0x16, 0xe9, 0x6d, 0xc8, 0x01, 0xb1, 0xb0, 0xa6, 0x3a, 0xab, 0xd9, 0xbd, - 0x74, 0xdb, 0x25, 0xff, 0xf8, 0x22, 0xfb, 0x40, 0x34, 0x54, 0x6d, 0x59, - 0xcf, 0x4f, 0xe5, 0x38, 0x95, 0x18, 0x1f, 0xf4, 0xb1, 0xd1, 0xeb, 0xb6, - 0x4d, 0xa8, 0x43, 0xa8, 0xca, 0xb6, 0x7c, 0xd1, 0xec, 0xf3, 0x55, 0x18, - 0xbf, 0x38, 0xb0, 0x4f, 0x6b, 0x59, 0x49, 0x54, 0x20, 0x41, 0x30, 0x23, - 0x5c, 0xff, 0xac, 0xdb, 0xfe, 0xbd, 0x4f, 0xab, 0xa9, 0xa6, 0xe7, 0xb0, - 0x3f, 0xc8, 0xe0, 0xe8, 0x58, 0x0d, 0x95, 0x2f, 0xf0, 0x49, 0xfd, 0x57, - 0x6d, 0x57, 0x58, 0x48, 0x3a, 0x2d, 0x9d, 0x0a, 0x38, 0xe6, 0x1e, 0xc6, - 0xa4, 0xaf, 0x7e, 0xa6, 0x3f, 0xac, 0xe5, 0xbf, 0x32, 0xde, 0x23, 0x02, - 0xae, 0x25, 0x03, 0x43, 0x2c, 0x55, 0x31, 0x59, 0x68, 0x4e, 0x92, 0x36, - 0xad, 0x15, 0x33, 0xf1, 0x23, 0xcf, 0x40, 0xb5, 0xba, 0xa7, 0xed, 0xa8, - 0x89, 0xb8, 0x14, 0xd4, 0xdc, 0xf6, 0x37, 0x1b, 0x01, 0x3b, 0x05, 0x51, - 0x8e, 0x59, 0x38, 0x53, 0x0f, 0x3f, 0x68, 0x20, 0x64, 0xfc, 0xf3, 0xd8, - 0x07, 0xbc, 0x5e, 0xaa, 0xf6, 0xa6, 0x55, 0xb2, 0x9f, 0xca, 0xc4, 0xeb, - 0x49, 0x10, 0x17, 0x32, 0x94, 0x4b, 0x80, 0x58, 0xbc, 0x56, 0x87, 0x46, - 0xa6, 0x2a, 0xa0, 0x07, 0x63, 0xe3, 0xdf, 0xc3, 0x5b, 0xae, 0x6c, 0xa6, - 0x54, 0xad, 0x08, 0xc2, 0xf8, 0xe0, 0x1d, 0x05, 0x61, 0x28, 0xf0, 0x44, - 0x11, 0x56, 0xda, 0x58, 0xec, 0x4c, 0x2c, 0x34, 0xc6, 0x12, 0x42, 0xee, - 0xac, 0xcc, 0xa4, 0xb3, 0x46, 0xa7, 0xa9, 0xa9, 0x64, 0xba, 0xaf, 0xd6, - 0xd9, 0xf9, 0x09, 0x1e, 0x38, 0x3d, 0x40, 0x52, 0x98, 0x59, 0x11, 0x52, - 0xe8, 0x3c, 0x9b, 0x1d, 0x6a, 0xf9, 0x4a, 0xd6, 0x1e, 0xba, 0x89, 0xa9, - 0x59, 0xa7, 0xdc, 0xb3, 0x0c, 0xcd, 0xaf, 0xee, 0x35, 0x13, 0x89, 0x34, - 0x24, 0x4d, 0xea, 0x58, 0xf0, 0x55, 0xa7, 0x44, 0xfd, 0x27, 0xaa, 0x04, - 0x91, 0xe0, 0xb3, 0xc1, 0x2c, 0xad, 0x6b, 0xa6, 0x8b, 0xae, 0x34, 0xc4, - 0xcc, 0xe3, 0x15, 0x08, 0x05, 0x2b, 0xcf, 0x46, 0xd7, 0x56, 0x6f, 0x58, - 0x55, 0x4b, 0xbc, 0x31, 0xd5, 0x0f, 0x5b, 0xeb, 0x40, 0xca, 0x1f, 0xb2, - 0xe8, 0xa6, 0x82, 0xaa, 0x4d, 0xbc, 0x5e, 0xd9, 0xd1, 0xfc, 0xd4, 0x20, - 0x5e, 0x3f, 0x62, 0x53, 0x8a, 0x59, 0xd6, 0x50, 0xa9, 0x3a, 0xcd, 0x1a, - 0x6a, 0xf6, 0xb2, 0xd3, 0x46, 0xb8, 0xd1, 0xa8, 0xd0, 0xa7, 0x7b, 0xb5, - 0x87, 0xcf, 0x9f, 0xf1, 0x1c, 0x16, 0xed, 0x36, 0x9c, 0x4e, 0x3e, 0x59, - 0x09, 0x55, 0xb3, 0x42, 0x4d, 0x25, 0xae, 0x01, 0xca, 0xdd, 0x98, 0xbf, - 0x12, 0xac, 0x8a, 0xa6, 0xd1, 0xaf, 0x78, 0xc6, 0xa3, 0xe6, 0x0d, 0x0b, - 0x9d, 0x2d, 0x99, 0x48, 0x87, 0x57, 0xe6, 0x57, 0xb1, 0x49, 0x33, 0x2f, - 0xeb, 0x0c, 0x70, 0xe8, 0xe8, 0xc7, 0xb1, 0xb0, 0xa1, 0xa6, 0x75, 0xab, - 0x4a, 0xbe, 0x13, 0xdc, 0xcf, 0xff, 0x95, 0x23, 0x71, 0x41, 0x6e, 0x54, - 0x62, 0x59, 0x7f, 0x4f, 0x63, 0x38, 0xec, 0x17, 0x7a, 0xf3, 0x1a, 0xd1, - 0x8c, 0xb6, 0x29, 0xa8, 0x66, 0xa8, 0x2e, 0xb7, 0x0f, 0xd2, 0x93, 0xf4, - 0xfe, 0x18, 0x3f, 0x39, 0x00, 0x50, 0x75, 0x59, 0x0d, 0x54, 0xad, 0x40, - 0x8f, 0x22, 0xb3, 0xfe, 0x0d, 0xdb, 0x8e, 0xbd, 0x14, 0xab, 0xbb, 0xa6, - 0x35, 0xb1, 0xc7, 0xc8, 0x85, 0xe9, 0x00, 0x0e, 0x29, 0x30, 0x4c, 0x4a, - 0x21, 0x58, 0x43, 0x57, 0xf6, 0x47, 0xa3, 0x2c, 0xf5, 0x09, 0x92, 0xe5, - 0x9f, 0xc5, 0x54, 0xaf, 0x7c, 0xa6, 0x78, 0xac, 0x5f, 0xc0, 0xd2, 0xde, - 0xca, 0x02, 0x4d, 0x26, 0x72, 0x43, 0x60, 0x55, 0x24, 0x59, 0x10, 0x4e, - 0x0b, 0x36, 0x09, 0x15, 0x86, 0xf0, 0x98, 0xce, 0xe0, 0xb4, 0x9e, 0xa7, - 0x15, 0xa9, 0xf2, 0xb8, 0xa7, 0xd4, 0x8b, 0xf7, 0xd5, 0x1b, 0x86, 0x3b, - 0x49, 0x51, 0x97, 0x59, 0xf5, 0x52, 0x96, 0x3e, 0xc8, 0x1f, 0xb8, 0xfb, - 0x5c, 0xd8, 0x95, 0xbb, 0x2d, 0xaa, 0x0b, 0xa7, 0xa8, 0xb2, 0x2c, 0xcb, - 0x69, 0xec, 0xf1, 0x10, 0xa7, 0x32, 0xec, 0x4b, 0x9e, 0x58, 0x8d, 0x56, - 0x1f, 0x46, 0x0d, 0x2a, 0xf7, 0x06, 0xc1, 0xe2, 0x5f, 0xc3, 0x18, 0xae, - 0x65, 0xa6, 0x9d, 0xad, 0x7f, 0xc2, 0x9b, 0xe1, 0xc9, 0x05, 0xf6, 0x28, - 0x62, 0x45, 0x3b, 0x56, 0xc7, 0x58, 0x92, 0x4c, 0x9f, 0x33, 0x22, 0x12, - 0x96, 0xed, 0x25, 0xcc, 0x46, 0xb3, 0x32, 0xa7, 0xd5, 0xa9, 0xd2, 0xba, - 0x47, 0xd7, 0x85, 0xfa, 0xa7, 0x1e, 0xb8, 0x3d, 0x81, 0x52, 0x98, 0x59, - 0xce, 0x51, 0x64, 0x3c, 0x00, 0x1d, 0xba, 0xf8, 0xb7, 0xd5, 0xaf, 0xb9, - 0x61, 0xa9, 0x6f, 0xa7, 0x38, 0xb4, 0x9a, 0xcd, 0x55, 0xef, 0xdf, 0x13, - 0x13, 0x35, 0x79, 0x4d, 0x00, 0x59, 0xbe, 0x55, 0x38, 0x44, 0x67, 0x27, - 0xf9, 0x03, 0xf7, 0xdf, 0x32, 0xc1, 0xf1, 0xac, 0x6b, 0xa6, 0xd5, 0xae, - 0xb2, 0xc4, 0x71, 0xe4, 0xbd, 0x08, 0x9d, 0x2b, 0x37, 0x47, 0xff, 0x56, - 0x56, 0x58, 0xf5, 0x4a, 0x2e, 0x31, 0x2e, 0x0f, 0xb1, 0xea, 0xbb, 0xc9, - 0xc9, 0xb1, 0xd7, 0xa6, 0xb5, 0xaa, 0xc0, 0xbc, 0xf5, 0xd9, 0x81, 0xfd, - 0x6f, 0x21, 0xd9, 0x3f, 0x9e, 0x53, 0x86, 0x59, 0x88, 0x50, 0x2b, 0x3a, - 0x25, 0x1a, 0xc4, 0xf5, 0x1b, 0xd3, 0xe2, 0xb7, 0xa7, 0xa8, 0xf1, 0xa7, - 0xda, 0xb5, 0x18, 0xd0, 0x48, 0xf2, 0xc1, 0x16, 0x75, 0x37, 0xed, 0x4e, - 0x4c, 0x59, 0xd3, 0x54, 0x41, 0x42, 0xb0, 0x24, 0x03, 0x01, 0x2c, 0xdd, - 0x20, 0xbf, 0xda, 0xab, 0x8f, 0xa6, 0x23, 0xb0, 0xf6, 0xc6, 0x4c, 0xe7, - 0xb5, 0x0b, 0x30, 0x2e, 0xfe, 0x48, 0xa9, 0x57, 0xc8, 0x57, 0x49, 0x49, - 0xa9, 0x2e, 0x3a, 0x0c, 0xd0, 0xe7, 0x61, 0xc7, 0x61, 0xb0, 0x97, 0xa6, - 0xac, 0xab, 0xc1, 0xbe, 0xaf, 0xdc, 0x7b, 0x00, 0x31, 0x24, 0xe6, 0x41, - 0xa6, 0x54, 0x57, 0x59, 0x2e, 0x4f, 0xe0, 0x37, 0x44, 0x17, 0xd2, 0xf2, - 0x89, 0xd0, 0x28, 0xb6, 0x0b, 0xa8, 0x89, 0xa8, 0x92, 0xb7, 0xa3, 0xd2, - 0x3d, 0xf5, 0xa1, 0x19, 0xc4, 0x39, 0x4c, 0x50, 0x7e, 0x59, 0xd0, 0x53, - 0x38, 0x40, 0xee, 0x21, 0x0c, 0xfe, 0x6d, 0xda, 0x1e, 0xbd, 0xdd, 0xaa, - 0xcb, 0xa6, 0x87, 0xb1, 0x4d, 0xc9, 0x2d, 0xea, 0xa7, 0x0e, 0xba, 0x30, - 0xac, 0x4a, 0x3d, 0x58, 0x20, 0x57, 0x8a, 0x47, 0x13, 0x2c, 0x47, 0x09, - 0xf2, 0xe4, 0x1a, 0xc5, 0x0e, 0xaf, 0x71, 0xa6, 0xbb, 0xac, 0xd5, 0xc0, - 0x73, 0xdf, 0x74, 0x03, 0xea, 0x26, 0xdf, 0x43, 0x96, 0x55, 0x0f, 0x59, - 0xbe, 0x4d, 0x82, 0x35, 0x62, 0x14, 0xdd, 0xef, 0x0a, 0xce, 0x82, 0xb4, - 0x84, 0xa7, 0x3e, 0xa9, 0x5d, 0xb9, 0x3b, 0xd5, 0x36, 0xf8, 0x78, 0x1c, - 0x04, 0x3c, 0x94, 0x51, 0x96, 0x59, 0xb8, 0x52, 0x17, 0x3e, 0x2b, 0x1f, - 0x0a, 0xfb, 0xc6, 0xd7, 0x23, 0xbb, 0x01, 0xaa, 0x1b, 0xa7, 0x04, 0xb3, - 0xb3, 0xcb, 0x12, 0xed, 0x9a, 0x11, 0x30, 0x33, 0x4b, 0x4c, 0xb3, 0x58, - 0x62, 0x56, 0xb4, 0x45, 0x75, 0x29, 0x4c, 0x06, 0x20, 0xe2, 0xe1, 0xc2, - 0xd2, 0xad, 0x68, 0xa6, 0xdc, 0xad, 0x00, 0xc3, 0x3b, 0xe2, 0x72, 0x06, - 0x91, 0x29, 0xcb, 0x45, 0x6a, 0x56, 0xb0, 0x58, 0x36, 0x4c, 0x16, 0x33, - 0x77, 0x11, 0xf1, 0xec, 0x97, 0xcb, 0xf1, 0xb2, 0x1a, 0xa7, 0x05, 0xaa, - 0x40, 0xbb, 0xdf, 0xd7, 0x30, 0xfb, 0x49, 0x1f, 0x32, 0x3e, 0xc3, 0x52, - 0x98, 0x59, 0x84, 0x51, 0xea, 0x3b, 0x59, 0x1c, 0x13, 0xf8, 0x1c, 0xd5, - 0x4b, 0xb9, 0x30, 0xa9, 0x8f, 0xa7, 0x91, 0xb4, 0x27, 0xce, 0x01, 0xf0, - 0x81, 0x14, 0xa0, 0x35, 0xcd, 0x4d, 0x14, 0x59, 0x8a, 0x55, 0xcb, 0x43, - 0xc9, 0x26, 0x53, 0x03, 0x52, 0xdf, 0xbe, 0xc0, 0xac, 0xac, 0x74, 0xa6, - 0x1c, 0xaf, 0x34, 0xc5, 0x13, 0xe5, 0x6a, 0x09, 0x2f, 0x2c, 0xa1, 0x47, - 0x27, 0x57, 0x36, 0x58, 0x9b, 0x4a, 0x9b, 0x30, 0x86, 0x0e, 0x0d, 0xea, - 0x2f, 0xc9, 0x7a, 0xb1, 0xc4, 0xa6, 0xec, 0xaa, 0x30, 0xbd, 0x93, 0xda, - 0x27, 0xfe, 0x15, 0x22, 0x4a, 0x40, 0xe0, 0x53, 0x7c, 0x59, 0x3b, 0x50, - 0xac, 0x39, 0x7d, 0x19, 0x1f, 0xf5, 0x81, 0xd2, 0x81, 0xb7, 0x80, 0xa8, - 0x11, 0xa8, 0x3c, 0xb6, 0xa8, 0xd0, 0xf0, 0xf2, 0x6a, 0x17, 0xf7, 0x37, - 0x40, 0x4f, 0x58, 0x59, 0x9d, 0x54, 0xcc, 0x41, 0x15, 0x24, 0x55, 0x00, - 0x92, 0xdc, 0xa7, 0xbe, 0xa3, 0xab, 0x97, 0xa6, 0x73, 0xb0, 0x7b, 0xc7, - 0xef, 0xe7, 0x60, 0x0c, 0xc2, 0x2e, 0x60, 0x49, 0xcf, 0x57, 0x9f, 0x57, - 0xee, 0x48, 0x0e, 0x2e, 0x98, 0x0b, 0x26, 0xe7, 0xdf, 0xc6, 0x13, 0xb0, - 0x8c, 0xa6, 0xe7, 0xab, 0x37, 0xbf, 0x4c, 0xdd, 0x27, 0x01, 0xcd, 0x24, - 0x5a, 0x42, 0xdd, 0x54, 0x4a, 0x59, 0xdd, 0x4e, 0x59, 0x37, 0xa0, 0x16, - 0x27, 0xf2, 0xf9, 0xcf, 0xc9, 0xb5, 0xe8, 0xa7, 0xb1, 0xa8, 0xf7, 0xb7, - 0x36, 0xd3, 0xe9, 0xf5, 0x44, 0x1a, 0x45, 0x3a, 0x9b, 0x50, 0x81, 0x59, - 0x99, 0x53, 0xba, 0x3f, 0x55, 0x21, 0x5c, 0xfd, 0xd5, 0xd9, 0xac, 0xbc, - 0xa7, 0xaa, 0xdd, 0xa6, 0xd9, 0xb1, 0xd7, 0xc9, 0xd1, 0xea, 0x53, 0x0f, - 0x45, 0x31, 0x0e, 0x4b, 0x57, 0x58, 0xfa, 0x56, 0x21, 0x47, 0x7e, 0x2b, - 0x9c, 0x08, 0x4e, 0xe4, 0x9c, 0xc4, 0xc3, 0xae, 0x6d, 0xa6, 0xfb, 0xac, - 0x4e, 0xc1, 0x12, 0xe0, 0x23, 0x04, 0x7e, 0x27, 0x55, 0x44, 0xc3, 0x55, - 0xfe, 0x58, 0x67, 0x4d, 0xf9, 0x34, 0xba, 0x13, 0x36, 0xef, 0x7c, 0xcd, - 0x25, 0xb4, 0x6b, 0xa7, 0x68, 0xa9, 0xc7, 0xb9, 0xd3, 0xd5, 0xdf, 0xf8, - 0x1c, 0x1d, 0x82, 0x3c, 0xd9, 0x51, 0x9a, 0x59, 0x72, 0x52, 0xa1, 0x3d, - 0x84, 0x1e, 0x66, 0xfa, 0x25, 0xd7, 0xbd, 0xba, 0xcd, 0xa9, 0x34, 0xa7, - 0x5c, 0xb3, 0x3c, 0xcc, 0xbd, 0xed, 0x3e, 0x12, 0xc1, 0x33, 0xa0, 0x4c, - 0xcc, 0x58, 0x34, 0x56, 0x48, 0x45, 0xdc, 0x28, 0xa3, 0x05, 0x7c, 0xe1, - 0x66, 0xc2, 0x90, 0xad, 0x64, 0xa6, 0x26, 0xae, 0x7a, 0xc3, 0xe0, 0xe2, - 0x1b, 0x07, 0x2b, 0x2a, 0x32, 0x46, 0x9b, 0x56, 0x92, 0x58, 0xe0, 0x4b, - 0x87, 0x32, 0xd0, 0x10, 0x49, 0xec, 0x0d, 0xcb, 0x99, 0xb2, 0x06, 0xa7, - 0x37, 0xaa, 0xad, 0xbb, 0x79, 0xd8, 0xdb, 0xfb, 0xe9, 0x1f, 0xac, 0x3e, - 0x06, 0x53, 0x91, 0x59, 0x40, 0x51, 0x68, 0x3b, 0xb8, 0x1b, 0x66, 0xf7, - 0x89, 0xd4, 0xde, 0xb8, 0x0c, 0xa9, 0xa5, 0xa7, 0xf3, 0xb4, 0xb3, 0xce, - 0xa9, 0xf0, 0x2b, 0x15, 0x24, 0x36, 0x25, 0x4e, 0x24, 0x59, 0x57, 0x55, - 0x5b, 0x43, 0x2d, 0x26, 0xa9, 0x02, 0xb2, 0xde, 0x46, 0xc0, 0x6d, 0xac, - 0x7b, 0xa6, 0x65, 0xaf, 0xb8, 0xc5, 0xb5, 0xe5, 0x14, 0x0a, 0xc5, 0x2c, - 0x05, 0x48, 0x51, 0x57, 0x14, 0x58, 0x40, 0x4a, 0x05, 0x30, 0xe3, 0x0d, - 0x62, 0xe9, 0xab, 0xc8, 0x27, 0xb1, 0xb4, 0xa6, 0x24, 0xab, 0xa1, 0xbd, - 0x2f, 0xdb, 0xd4, 0xfe, 0xb0, 0x22, 0xc4, 0x40, 0x1a, 0x54, 0x71, 0x59, - 0xf2, 0x4f, 0x25, 0x39, 0xdb, 0x18, 0x72, 0xf4, 0xf2, 0xd1, 0x19, 0xb7, - 0x5f, 0xa8, 0x31, 0xa8, 0x9d, 0xb6, 0x3a, 0xd1, 0x9c, 0xf3, 0x0b, 0x18, - 0x81, 0x38, 0x8c, 0x4f, 0x67, 0x59, 0x60, 0x54, 0x5c, 0x41, 0x72, 0x23, - 0xb1, 0xff, 0xee, 0xdb, 0x39, 0xbe, 0x63, 0xab, 0xaa, 0xa6, 0xbc, 0xb0, - 0x05, 0xc8, 0x93, 0xe8, 0x09, 0x0d, 0x54, 0x2f, 0xc2, 0x49, 0xee, 0x57, - 0x80, 0x57, 0x84, 0x48, 0x81, 0x2d, 0xe8, 0x0a, 0x85, 0xe6, 0x5a, 0xc6, - 0xc6, 0xaf, 0x84, 0xa6, 0x22, 0xac, 0xae, 0xbf, 0xe9, 0xdd, 0xd3, 0x01, - 0x68, 0x25, 0xce, 0x42, 0x13, 0x55, 0x3b, 0x59, 0x8a, 0x4e, 0xd2, 0x36, - 0xfb, 0x15, 0x7e, 0xf1, 0x69, 0xcf, 0x68, 0xb5, 0xca, 0xa7, 0xd8, 0xa8, - 0x5e, 0xb8, 0xca, 0xd3, 0x93, 0xf6, 0xe8, 0x1a, 0xc8, 0x3a, 0xe2, 0x50, - 0x8c, 0x59, 0x56, 0x53, 0x45, 0x3f, 0xb3, 0x20, 0xb2, 0xfc, 0x3b, 0xd9, - 0x3a, 0xbc, 0x75, 0xaa, 0xee, 0xa6, 0x2d, 0xb2, 0x60, 0xca, 0x77, 0xeb, - 0xfc, 0x0f, 0xd6, 0x31, 0x67, 0x4b, 0x77, 0x58, 0xcb, 0x56, 0xbd, 0x46, - 0xe6, 0x2a, 0xf2, 0x07, 0xad, 0xe3, 0x18, 0xc4, 0x7f, 0xae, 0x68, 0xa6, - 0x3b, 0xad, 0xcb, 0xc1, 0xb1, 0xe0, 0xcd, 0x04, 0x1a, 0x28, 0xc0, 0x44, - 0xf6, 0x55, 0xea, 0x58, 0x0e, 0x4d, 0x70, 0x34, 0x12, 0x13, 0x8f, 0xee, - 0xee, 0xcc, 0xcb, 0xb3, 0x52, 0xa7, 0x94, 0xa9, 0x33, 0xba, 0x69, 0xd6, - 0x8b, 0xf9, 0xbd, 0x1d, 0x00, 0x3d, 0x1f, 0x52, 0x99, 0x59, 0x31, 0x52, - 0x21, 0x3d, 0xe6, 0x1d, 0xb8, 0xf9, 0x90, 0xd6, 0x4e, 0xba, 0xa1, 0xa9, - 0x4b, 0xa7, 0xb4, 0xb3, 0xca, 0xcc, 0x62, 0xee, 0xe9, 0x12, 0x48, 0x34, - 0xfd, 0x4c, 0xdf, 0x58, 0x06, 0x56, 0xda, 0x44, 0x44, 0x28, 0xf8, 0x04, - 0xdb, 0xe0, 0xeb, 0xc1, 0x4c, 0xad, 0x67, 0xa6, 0x6e, 0xae, 0xf5, 0xc3, - 0x85, 0xe3, 0xc5, 0x07, 0xc0, 0x2a, 0x9f, 0x46, 0xc4, 0x56, 0x79, 0x58, - 0x85, 0x4b, 0xf6, 0x31, 0x2a, 0x10, 0xa1, 0xeb, 0x84, 0xca, 0x44, 0xb2, - 0xf1, 0xa6, 0x6a, 0xaa, 0x1b, 0xbc, 0x14, 0xd9, 0x85, 0xfc, 0x8a, 0x20, - 0x26, 0x3f, 0x46, 0x53, 0x8c, 0x59, 0xf6, 0x50, 0xe9, 0x3a, 0x12, 0x1b, - 0xc0, 0xf6, 0xef, 0xd3, 0x7b, 0xb8, 0xdf, 0xa8, 0xc4, 0xa7, 0x50, 0xb5, - 0x43, 0xcf, 0x53, 0xf1, 0xce, 0x15, 0xaf, 0x36, 0x77, 0x4e, 0x34, 0x59, - 0x25, 0x55, 0xe6, 0x42, 0x94, 0x25, 0xfd, 0x01, 0x13, 0xde, 0xcf, 0xbf, - 0x2f, 0xac, 0x83, 0xa6, 0xb1, 0xaf, 0x38, 0xc6, 0x5b, 0xe6, 0xbe, 0x0a, - 0x58, 0x2d, 0x6d, 0x48, 0x73, 0x57, 0xf7, 0x57, 0xdc, 0x49, 0x79, 0x2f, - 0x36, 0x0d, 0xbd, 0xe8, 0x27, 0xc8, 0xd2, 0xb0, 0xab, 0xa6, 0x57, 0xab, - 0x17, 0xbe, 0xcb, 0xdb, 0x7f, 0xff, 0x4e, 0x23, 0x39, 0x41, 0x56, 0x54, - 0x66, 0x59, 0xa3, 0x4f, 0xa1, 0x38, 0x38, 0x18, 0xc8, 0xf3, 0x5f, 0xd1, - 0xb6, 0xb6, 0x3a, 0xa8, 0x57, 0xa8, 0xfe, 0xb6, 0xcd, 0xd1, 0x45, 0xf4, - 0xb0, 0x18, 0x05, 0x39, 0xdb, 0x4f, 0x70, 0x59, 0x29, 0x54, 0xe3, 0x40, - 0xd8, 0x22, 0x02, 0xff, 0x54, 0xdb, 0xc4, 0xbd, 0x2c, 0xab, 0xb7, 0xa6, - 0x0d, 0xb1, 0x8b, 0xc8, 0x38, 0xe9, 0xb2, 0x0d, 0xe6, 0x2f, 0x23, 0x4a, - 0x0d, 0x58, 0x5c, 0x57, 0x1d, 0x48, 0xed, 0x2c, 0x40, 0x0a, 0xdf, 0xe5, - 0xda, 0xc5, 0x78, 0xaf, 0x7d, 0xa6, 0x5d, 0xac, 0x27, 0xc0, 0x88, 0xde, - 0x7e, 0x02, 0x04, 0x26, 0x3e, 0x43, 0x4a, 0x55, 0x27, 0x59, 0x3c, 0x4e, - 0x47, 0x36, 0x57, 0x15, 0xd3, 0xf0, 0xda, 0xce, 0x0a, 0xb5, 0xad, 0xa7, - 0x01, 0xa9, 0xc3, 0xb8, 0x62, 0xd4, 0x3a, 0xf7, 0x8e, 0x1b, 0x47, 0x3b, - 0x2c, 0x51, 0x91, 0x59, 0x15, 0x53, 0xcd, 0x3e, 0x12, 0x20, 0x08, 0xfc, - 0xa0, 0xd8, 0xca, 0xbb, 0x44, 0xaa, 0xff, 0xa6, 0x86, 0xb2, 0xe5, 0xca, - 0x22, 0xec, 0xa1, 0x10, 0x65, 0x32, 0xc4, 0x4b, 0x90, 0x58, 0xa3, 0x56, - 0x50, 0x46, 0x50, 0x2a, 0x49, 0x07, 0x07, 0xe3, 0x9f, 0xc3, 0x33, 0xae, - 0x69, 0xa6, 0x7c, 0xad, 0x46, 0xc2, 0x54, 0xe1, 0x75, 0x05, 0xb6, 0x28, - 0x2a, 0x45, 0x29, 0x56, 0xd1, 0x58, 0xba, 0x4c, 0xe1, 0x33, 0x6d, 0x12, - 0xe5, 0xed, 0x63, 0xcc, 0x72, 0xb3, 0x39, 0xa7, 0xc3, 0xa9, 0x9e, 0xba, - 0x01, 0xd7, 0x37, 0xfa, 0x5c, 0x1e, 0x7f, 0x3d, 0x62, 0x52, 0x9a, 0x59, - 0xeb, 0x51, 0xa4, 0x3c, 0x44, 0x1d, 0x0c, 0xf9, 0xfb, 0xd5, 0xe1, 0xb9, - 0x75, 0xa9, 0x64, 0xa7, 0x0c, 0xb4, 0x5b, 0xcd, 0x06, 0xef, 0x93, 0x13, - 0xd2, 0x34, 0x53, 0x4d, 0xf5, 0x58, 0xd6, 0x55, 0x6b, 0x44, 0xac, 0x27, - 0x4b, 0x04, 0x3c, 0xe0, 0x70, 0xc1, 0x0a, 0xad, 0x6c, 0xa6, 0xb2, 0xae, - 0x78, 0xc4, 0x26, 0xe4, 0x6f, 0x08, 0x58, 0x2b, 0x06, 0x47, 0xee, 0x56, - 0x60, 0x58, 0x23, 0x4b, 0x6f, 0x31, 0x7b, 0x0f, 0xff, 0xea, 0xf7, 0xc9, - 0xf2, 0xb1, 0xdd, 0xa6, 0xa0, 0xaa, 0x89, 0xbc, 0xb0, 0xd9, 0x30, 0xfd, - 0x27, 0x21, 0xa3, 0x3f, 0x80, 0x53, 0x8a, 0x59, 0xa9, 0x50, 0x69, 0x3a, - 0x6f, 0x1a, 0x15, 0xf6, 0x5b, 0xd3, 0x13, 0xb8, 0xb9, 0xa8, 0xe2, 0xa7, - 0xaf, 0xb5, 0xd4, 0xcf, 0xfa, 0xf1, 0x77, 0x16, 0x34, 0x37, 0xc9, 0x4e, - 0x46, 0x59, 0xeb, 0x54, 0x78, 0x42, 0xf5, 0x24, 0x54, 0x01, 0x73, 0xdd, - 0x59, 0xbf, 0xf3, 0xab, 0x8c, 0xa6, 0xfd, 0xaf, 0xbd, 0xc6, 0xfd, 0xe6, - 0x69, 0x0b, 0xeb, 0x2d, 0xd0, 0x48, 0x9a, 0x57, 0xd6, 0x57, 0x78, 0x49, - 0xea, 0x2e, 0x8a, 0x0c, 0x1b, 0xe8, 0x9f, 0xc7, 0x83, 0xb0, 0x9f, 0xa6, - 0x90, 0xab, 0x8d, 0xbe, 0x64, 0xdc, 0x2e, 0x00, 0xe7, 0x23, 0xb3, 0x41, - 0x8a, 0x54, 0x5e, 0x59, 0x52, 0x4f, 0x1d, 0x38, 0x92, 0x17, 0x1e, 0xf3, - 0xcd, 0xd0, 0x55, 0xb6, 0x19, 0xa8, 0x79, 0xa8, 0x64, 0xb7, 0x5d, 0xd2, - 0xf2, 0xf4, 0x53, 0x19, 0x8a, 0x39, 0x26, 0x50, 0x7c, 0x59, 0xeb, 0x53, - 0x6f, 0x40, 0x39, 0x22, 0x56, 0xfe, 0xba, 0xda, 0x4e, 0xbd, 0xfa, 0xaa, - 0xc0, 0xa6, 0x64, 0xb1, 0x0c, 0xc9, 0xe3, 0xe9, 0x59, 0x0e, 0x77, 0x30, - 0x81, 0x4a, 0x2e, 0x58, 0x33, 0x57, 0xba, 0x47, 0x57, 0x2c, 0x95, 0x09, - 0x3d, 0xe5, 0x57, 0xc5, 0x2e, 0xaf, 0x76, 0xa6, 0x9c, 0xac, 0x9e, 0xc0, - 0x2a, 0xdf, 0x25, 0x03, 0xa2, 0x26, 0xae, 0x43, 0x7c, 0x55, 0x1a, 0x59, - 0xe2, 0x4d, 0xc5, 0x35, 0xaa, 0x14, 0x2f, 0xf0, 0x48, 0xce, 0xae, 0xb4, - 0x93, 0xa7, 0x26, 0xa9, 0x2f, 0xb9, 0xf5, 0xd4, 0xe8, 0xf7, 0x2d, 0x1c, - 0xca, 0x3b, 0x72, 0x51, 0x96, 0x59, 0xd6, 0x52, 0x4f, 0x3e, 0x75, 0x1f, - 0x5b, 0xfb, 0x07, 0xd8, 0x5d, 0xbb, 0x11, 0xaa, 0x15, 0xa7, 0xda, 0xb2, - 0x73, 0xcb, 0xc6, 0xec, 0x4b, 0x11, 0xf2, 0x32, 0x1e, 0x4c, 0xab, 0x58, - 0x75, 0x56, 0xe5, 0x45, 0xbc, 0x29, 0x9a, 0x06, 0x6b, 0xe2, 0x1b, 0xc3, - 0xf1, 0xad, 0x67, 0xa6, 0xbf, 0xad, 0xc5, 0xc2, 0xf1, 0xe1, 0x25, 0x06, - 0x49, 0x29, 0x9b, 0x45, 0x54, 0x56, 0xbc, 0x58, 0x5e, 0x4c, 0x5a, 0x33, - 0xbf, 0x11, 0x44, 0xed, 0xd2, 0xcb, 0x1d, 0xb3, 0x21, 0xa7, 0xf2, 0xa9, - 0x0b, 0xbb, 0x9a, 0xd7, 0xe1, 0xfa, 0xff, 0x1e, 0xf9, 0x3d, 0xa6, 0x52, - 0x96, 0x59, 0xa8, 0x51, 0x23, 0x3c, 0xa4, 0x1c, 0x60, 0xf8, 0x65, 0xd5, - 0x77, 0xb9, 0x48, 0xa9, 0x7e, 0xa7, 0x6a, 0xb4, 0xe5, 0xcd, 0xb3, 0xef, - 0x34, 0x14, 0x60, 0x35, 0xa8, 0x4d, 0x09, 0x59, 0xa4, 0x55, 0xfb, 0x43, - 0x13, 0x27, 0x9f, 0x03, 0x9e, 0xdf, 0xf4, 0xc0, 0xca, 0xac, 0x72, 0xa6, - 0xf8, 0xae, 0xfb, 0xc4, 0xc6, 0xe4, 0x1d, 0x09, 0xe9, 0x2b, 0x73, 0x47, - 0x13, 0x57, 0x45, 0x58, 0xc6, 0x4a, 0xdc, 0x30, 0xd7, 0x0e, 0x55, 0xea, - 0x72, 0xc9, 0x9c, 0xb1, 0xce, 0xa6, 0xd2, 0xaa, 0xfd, 0xbc, 0x49, 0xda, - 0xdc, 0xfd, 0xc7, 0x21, 0x19, 0x40, 0xbf, 0x53, 0x81, 0x59, 0x5f, 0x50, - 0xe8, 0x39, 0xc9, 0x19, 0x6b, 0xf5, 0xc8, 0xd2, 0xac, 0xb7, 0x95, 0xa8, - 0x01, 0xa8, 0x0d, 0xb6, 0x68, 0xd0, 0xa0, 0xf2, 0x20, 0x17, 0xb8, 0x37, - 0x1c, 0x4f, 0x52, 0x59, 0xb5, 0x54, 0x04, 0x42, 0x59, 0x24, 0xa9, 0x00, - 0xd6, 0xdc, 0xe1, 0xbe, 0xbb, 0xab, 0x92, 0xa6, 0x50, 0xb0, 0x3c, 0xc7, - 0xa6, 0xe7, 0x10, 0x0c, 0x7f, 0x2e, 0x34, 0x49, 0xbc, 0x57, 0xb4, 0x57, - 0x15, 0x49, 0x58, 0x2e, 0xe1, 0x0b, 0x75, 0xe7, 0x1c, 0xc7, 0x33, 0xb0, - 0x95, 0xa6, 0xc9, 0xab, 0x02, 0xbf, 0x03, 0xdd, 0xd8, 0x00, 0x86, 0x24, - 0x24, 0x42, 0xc5, 0x54, 0x4e, 0x59, 0x05, 0x4f, 0x94, 0x37, 0xef, 0x16, - 0x73, 0xf2, 0x3d, 0xd0, 0xf3, 0xb5, 0xf9, 0xa7, 0x9d, 0xa8, 0xcb, 0xb7, - 0xef, 0xd2, 0x9c, 0xf5, 0xf8, 0x19, 0x0b, 0x3a, 0x74, 0x50, 0x84, 0x59, - 0xad, 0x53, 0xfa, 0x3f, 0x96, 0x21, 0xb1, 0xfd, 0x18, 0xda, 0xe2, 0xbc, - 0xbf, 0xaa, 0xd6, 0xa6, 0xb1, 0xb1, 0x99, 0xc9, 0x83, 0xea, 0x07, 0x0f, - 0x04, 0x31, 0xe1, 0x4a, 0x4b, 0x58, 0x0a, 0x57, 0x54, 0x47, 0xc2, 0x2b, - 0xe9, 0x08, 0x9d, 0xe4, 0xd1, 0xc4, 0xe9, 0xae, 0x6d, 0xa6, 0xde, 0xac, - 0x16, 0xc1, 0xc9, 0xdf, 0xd3, 0x03, 0x38, 0x27, 0x21, 0x44, 0xad, 0x55, - 0x07, 0x59, 0x8f, 0x4d, 0x38, 0x35, 0x06, 0x14, 0x85, 0xef, 0xbc, 0xcd, - 0x51, 0xb4, 0x75, 0xa7, 0x55, 0xa9, 0x95, 0xb9, 0x8f, 0xd5, 0x8f, 0xf8, - 0xd2, 0x1c, 0x47, 0x3c, 0xba, 0x51, 0x99, 0x59, 0x91, 0x52, 0xd8, 0x3d, - 0xd1, 0x1e, 0xb1, 0xfa, 0x70, 0xd7, 0xeb, 0xba, 0xe5, 0xa9, 0x29, 0xa7, - 0x32, 0xb3, 0xfe, 0xcb, 0x6c, 0xed, 0xf5, 0x11, 0x7c, 0x33, 0x7c, 0x4c, - 0xbe, 0x58, 0x4a, 0x56, 0x7b, 0x45, 0x20, 0x29, 0xf3, 0x05, 0xc7, 0xe1, - 0x9e, 0xc2, 0xaf, 0xad, 0x64, 0xa6, 0x07, 0xae, 0x3e, 0xc3, 0x97, 0xe2, - 0xcc, 0x06, 0xe2, 0x29, 0x08, 0x46, 0x7f, 0x56, 0xa5, 0x58, 0x03, 0x4c, - 0xcd, 0x32, 0x18, 0x11, 0x9c, 0xec, 0x48, 0xcb, 0xc4, 0xb2, 0x0e, 0xa7, - 0x1f, 0xaa, 0x7c, 0xbb, 0x31, 0xd8, 0x8d, 0xfb, 0x9f, 0x1f, 0x74, 0x3e, - 0xe8, 0x52, 0x93, 0x59, 0x60, 0x51, 0xa4, 0x3b, 0x01, 0x1c, 0xb7, 0xf7, - 0xcc, 0xd4, 0x11, 0xb9, 0x1a, 0xa9, 0x9d, 0xa7, 0xc3, 0xb4, 0x76, 0xce, - 0x59, 0xf0, 0xde, 0x14, 0xe7, 0x35, 0xfd, 0x4d, 0x1d, 0x59, 0x6e, 0x55, - 0x8f, 0x43, 0x75, 0x26, 0xf7, 0x02, 0xfc, 0xde, 0x7c, 0xc0, 0x8b, 0xac, - 0x76, 0xa6, 0x46, 0xaf, 0x77, 0xc5, 0x6f, 0xe5, 0xc1, 0x09, 0x84, 0x2c, - 0xd5, 0x47, 0x3e, 0x57, 0x25, 0x58, 0x68, 0x4a, 0x4b, 0x30, 0x30, 0x0e, - 0xac, 0xe9, 0xee, 0xc8, 0x47, 0xb1, 0xc0, 0xa6, 0x06, 0xab, 0x70, 0xbd, - 0xe4, 0xda, 0x89, 0xfe, 0x64, 0x22, 0x90, 0x40, 0xfd, 0x53, 0x75, 0x59, - 0x17, 0x50, 0x5f, 0x39, 0x2b, 0x19, 0xbd, 0xf4, 0x37, 0xd2, 0x45, 0xb7, - 0x72, 0xa8, 0x20, 0xa8, 0x71, 0xb6, 0xf6, 0xd0, 0x4d, 0xf3, 0xc2, 0x17, - 0x40, 0x38, 0x6a, 0x4f, 0x60, 0x59, 0x7c, 0x54, 0x91, 0x41, 0xbb, 0x23, - 0xfe, 0xff, 0x38, 0xdc, 0x6d, 0xbe, 0x80, 0xab, 0xa1, 0xa6, 0x9a, 0xb0, - 0xc5, 0xc7, 0x48, 0xe8, 0xbc, 0x0c, 0x10, 0x2f, 0x96, 0x49, 0xdd, 0x57, - 0x92, 0x57, 0xb2, 0x48, 0xc4, 0x2d, 0x38, 0x0b, 0xce, 0xe6, 0x9a, 0xc6, - 0xe6, 0xaf, 0x8a, 0xa6, 0x05, 0xac, 0x76, 0xbf, 0xa4, 0xdd, 0x81, 0x01, - 0x22, 0x25, 0x99, 0x42, 0xf8, 0x54, 0x44, 0x59, 0xb1, 0x4e, 0x0d, 0x37, - 0x4c, 0x16, 0xc6, 0xf1, 0xb0, 0xcf, 0x91, 0xb5, 0xda, 0xa7, 0xc6, 0xa8, - 0x2c, 0xb8, 0x8a, 0xd3, 0x3f, 0xf6, 0xa2, 0x1a, 0x89, 0x3a, 0xc1, 0x50, - 0x8a, 0x59, 0x71, 0x53, 0x7e, 0x3f, 0xfc, 0x20, 0x01, 0xfd, 0x81, 0xd9, - 0x6f, 0xbc, 0x8b, 0xaa, 0xe7, 0xa6, 0x06, 0xb2, 0x21, 0xca, 0x29, 0xeb, - 0xb0, 0x0f, 0x92, 0x31, 0x40, 0x4b, 0x67, 0x58, 0xe2, 0x56, 0xea, 0x46, - 0x2d, 0x2b, 0x3f, 0x08, 0xf8, 0xe3, 0x55, 0xc4, 0x9e, 0xae, 0x6a, 0xa6, - 0x1e, 0xad, 0x8f, 0xc1, 0x6c, 0xe0, 0x7a, 0x04, 0xd7, 0x27, 0x8b, 0x44, - 0xe1, 0x55, 0xf3, 0x58, 0x36, 0x4d, 0xb1, 0x34, 0x5d, 0x13, 0xde, 0xee, - 0x2e, 0xcd, 0xf4, 0xb3, 0x60, 0xa7, 0x7c, 0xa9, 0x04, 0xba, 0x22, 0xd6, - 0x3d, 0xf9, 0x73, 0x1d, 0xc6, 0x3c, 0xfe, 0x51, 0x9c, 0x59, 0x4d, 0x52, - 0x5d, 0x3d, 0x2e, 0x1e, 0x08, 0xfa, 0xd6, 0xd6, 0x80, 0xba, 0xb5, 0xa9, - 0x40, 0xa7, 0x8a, 0xb3, 0x8c, 0xcc, 0x13, 0xee, 0x9d, 0x12, 0x07, 0x34, - 0xd3, 0x4c, 0xd9, 0x58, 0x18, 0x56, 0x11, 0x45, 0x85, 0x28, 0x4a, 0x05, - 0x24, 0xe1, 0x25, 0xc2, 0x68, 0xad, 0x6a, 0xa6, 0x47, 0xae, 0xc2, 0xc3, - 0x35, 0xe3, 0x78, 0x07, 0x7b, 0x2a, 0x6e, 0x46, 0xb0, 0x56, 0x87, 0x58, - 0xab, 0x4b, 0x3d, 0x32, 0x74, 0x10, 0xf0, 0xeb, 0xc1, 0xca, 0x6d, 0xb2, - 0xf9, 0xa6, 0x54, 0xaa, 0xe7, 0xbb, 0xcd, 0xd8, 0x37, 0xfc, 0x40, 0x20, - 0xee, 0x3e, 0x28, 0x53, 0x90, 0x59, 0x16, 0x51, 0x26, 0x3b, 0x5c, 0x1b, - 0x0e, 0xf7, 0x36, 0xd4, 0xa7, 0xb8, 0xf6, 0xa8, 0xb3, 0xa7, 0x25, 0xb5, - 0x03, 0xcf, 0x02, 0xf1, 0x86, 0x15, 0x6d, 0x36, 0x52, 0x4e, 0x2e, 0x59, - 0x3a, 0x55, 0x1f, 0x43, 0xd8, 0x25, 0x4d, 0x02, 0x5d, 0xde, 0x04, 0xc0, - 0x4d, 0xac, 0x7e, 0xa6, 0x8e, 0xaf, 0xfe, 0xc5, 0x0d, 0xe6, 0x71, 0x0a, - 0x12, 0x2d, 0x40, 0x48, 0x61, 0x57, 0x09, 0x58, 0x04, 0x4a, 0xbf, 0x2f, - 0x81, 0x0d, 0x0d, 0xe9, 0x61, 0xc8, 0xfa, 0xb0, 0xaf, 0xa6, 0x3f, 0xab, - 0xe2, 0xbd, 0x81, 0xdb, 0x32, 0xff, 0x04, 0x23, 0x06, 0x41, 0x36, 0x54, - 0x70, 0x59, 0xc4, 0x4f, 0xe0, 0x38, 0x82, 0x18, 0x16, 0xf4, 0xa4, 0xd1, - 0xe1, 0xb6, 0x4f, 0xa8, 0x40, 0xa8, 0xd7, 0xb6, 0x84, 0xd1, 0xfb, 0xf3, - 0x62, 0x18, 0xca, 0x38, 0xb5, 0x4f, 0x6e, 0x59, 0x41, 0x54, 0x1a, 0x41, - 0x22, 0x23, 0x4e, 0xff, 0xa0, 0xdb, 0xf5, 0xbd, 0x49, 0xab, 0xae, 0xa6, - 0xea, 0xb0, 0x4a, 0xc8, 0xef, 0xe8, 0x63, 0x0d, 0xa3, 0x2f, 0xf7, 0x49, - 0xfe, 0x57, 0x6d, 0x57, 0x4e, 0x48, 0x2e, 0x2d, 0x91, 0x0a, 0x29, 0xe6, - 0x16, 0xc6, 0x9c, 0xaf, 0x7e, 0xa6, 0x44, 0xac, 0xed, 0xbf, 0x42, 0xde, - 0x2c, 0x02, 0xbe, 0x25, 0x0a, 0x43, 0x2f, 0x55, 0x35, 0x59, 0x5b, 0x4e, - 0x8b, 0x36, 0x9f, 0x15, 0x24, 0xf1, 0x1c, 0xcf, 0x33, 0xb5, 0xbd, 0xa7, - 0xea, 0xa8, 0x98, 0xb8, 0x1a, 0xd4, 0xee, 0xf6, 0x41, 0x1b, 0x0d, 0x3b, - 0x09, 0x51, 0x90, 0x59, 0x33, 0x53, 0x04, 0x3f, 0x5d, 0x20, 0x55, 0xfc, - 0xe8, 0xd8, 0xfe, 0xbb, 0x59, 0xaa, 0xf9, 0xa6, 0x5b, 0xb2, 0xa9, 0xca, - 0xd3, 0xeb, 0x55, 0x10, 0x23, 0x32, 0x9b, 0x4b, 0x83, 0x58, 0xb6, 0x56, - 0x83, 0x46, 0x94, 0x2a, 0x97, 0x07, 0x56, 0xe3, 0xd2, 0xc3, 0x5a, 0xae, - 0x65, 0xa6, 0x60, 0xad, 0x0e, 0xc2, 0x06, 0xe1, 0x2b, 0x05, 0x6a, 0x28, - 0xfe, 0x44, 0x0f, 0x56, 0xde, 0x58, 0xe0, 0x4c, 0x23, 0x34, 0xb9, 0x12, - 0x34, 0xee, 0xa1, 0xcc, 0x9e, 0xb3, 0x41, 0xa7, 0xb0, 0xa9, 0x6a, 0xba, - 0xbd, 0xd6, 0xe6, 0xf9, 0x16, 0x1e, 0x41, 0x3d, 0x46, 0x52, 0x97, 0x59, - 0x0f, 0x52, 0xda, 0x3c, 0x91, 0x1d, 0x5a, 0xf9, 0x3f, 0xd6, 0x16, 0xba, - 0x85, 0xa9, 0x5a, 0xa7, 0xe5, 0xb3, 0x15, 0xcd, 0xbe, 0xee, 0x42, 0x13, - 0x93, 0x34, 0x2d, 0x4d, 0xeb, 0x58, 0xeb, 0x55, 0xa0, 0x44, 0xef, 0x27, - 0x9e, 0x04, 0x84, 0xe0, 0xa8, 0xc1, 0x28, 0xad, 0x6b, 0xa6, 0x90, 0xae, - 0x3f, 0xc4, 0xd8, 0xe3, 0x24, 0x08, 0x10, 0x2b, 0xd9, 0x46, 0xd9, 0x56, - 0x6c, 0x58, 0x50, 0x4b, 0xae, 0x31, 0xcb, 0x0f, 0x4b, 0xeb, 0x36, 0xca, - 0x18, 0xb2, 0xe7, 0xa6, 0x85, 0xaa, 0x58, 0xbc, 0x67, 0xd9, 0xe2, 0xfc, - 0xdf, 0x20, 0x69, 0x3f, 0x65, 0x53, 0x8d, 0x59, 0xca, 0x50, 0xa6, 0x3a, - 0xb9, 0x1a, 0x64, 0xf6, 0xa0, 0xd3, 0x41, 0xb8, 0xcc, 0xa8, 0xd2, 0xa7, - 0x86, 0xb5, 0x8f, 0xcf, 0xae, 0xf1, 0x29, 0x16, 0xf7, 0x36, 0xa4, 0x4e, - 0x3e, 0x59, 0x04, 0x55, 0xad, 0x42, 0x3d, 0x25, 0xa3, 0x01, 0xbc, 0xdd, - 0x8e, 0xbf, 0x10, 0xac, 0x86, 0xa6, 0xdd, 0xaf, 0x7d, 0xc6, 0xb4, 0xe6, - 0x19, 0x0b, 0xa8, 0x2d, 0xa3, 0x48, 0x87, 0x57, 0xe8, 0x57, 0xa4, 0x49, - 0x2d, 0x2f, 0xd8, 0x0c, 0x67, 0xe8, 0xdc, 0xc7, 0xaa, 0xb0, 0xa1, 0xa6, - 0x78, 0xab, 0x54, 0xbe, 0x21, 0xdc, 0xdb, 0xff, 0xa1, 0x23, 0x7c, 0x41, - 0x70, 0x54, 0x64, 0x59, 0x76, 0x4f, 0x5b, 0x38, 0xdc, 0x17, 0x6f, 0xf3, - 0x0e, 0xd1, 0x83, 0xb6, 0x28, 0xa8, 0x67, 0xa8, 0x37, 0xb7, 0x1b, 0xd2, - 0xa0, 0xf4, 0x0c, 0x19, 0x48, 0x39, 0x09, 0x50, 0x73, 0x59, 0x0a, 0x54, - 0xa2, 0x40, 0x84, 0x22, 0xa6, 0xfe, 0x00, 0xdb, 0x85, 0xbd, 0x0f, 0xab, - 0xbd, 0xa6, 0x3b, 0xb1, 0xd2, 0xc8, 0x93, 0xe9, 0x0c, 0x0e, 0x35, 0x30, - 0x55, 0x4a, 0x1f, 0x58, 0x47, 0x57, 0xe6, 0x47, 0x9e, 0x2c, 0xe3, 0x09, - 0x87, 0xe5, 0x93, 0xc5, 0x51, 0xaf, 0x78, 0xa6, 0x80, 0xac, 0x67, 0xc0, - 0xde, 0xde, 0xd9, 0x02, 0x5a, 0x26, 0x79, 0x43, 0x67, 0x55, 0x1e, 0x59, - 0x0e, 0x4e, 0xfe, 0x35, 0xfc, 0x14, 0x79, 0xf0, 0x8c, 0xce, 0xd9, 0xb4, - 0x9d, 0xa7, 0x15, 0xa9, 0xfe, 0xb8, 0xb1, 0xd4, 0x98, 0xf7, 0xe5, 0x1b, - 0x8b, 0x3b, 0x55, 0x51, 0x91, 0x59, 0xf5, 0x52, 0x88, 0x3e, 0xbf, 0x1f, - 0xa8, 0xfb, 0x50, 0xd8, 0x8c, 0xbb, 0x2c, 0xaa, 0x07, 0xa7, 0xb6, 0xb2, - 0x2f, 0xcb, 0x7d, 0xec, 0xfc, 0x10, 0xb0, 0x32, 0xf8, 0x4b, 0x9a, 0x58, - 0x8e, 0x56, 0x16, 0x46, 0xfe, 0x29, 0xee, 0x06, 0xaf, 0xe2, 0x5a, 0xc3, - 0x0f, 0xae, 0x67, 0xa6, 0xa1, 0xad, 0x89, 0xc2, 0xaa, 0xe1, 0xd3, 0x05, - 0x07, 0x29, 0x65, 0x45, 0x43, 0x56, 0xc2, 0x58, 0x8d, 0x4c, 0x93, 0x33, - 0x15, 0x12, 0x88, 0xed, 0x1a, 0xcc, 0x40, 0xb3, 0x2e, 0xa7, 0xdb, 0xa9, - 0xd8, 0xba, 0x55, 0xd7, 0x92, 0xfa, 0xb5, 0x1e, 0xc0, 0x3d, 0x87, 0x52, - 0x98, 0x59, 0xc7, 0x51, 0x5e, 0x3c, 0xef, 0x1c, 0xaf, 0xf8, 0xa8, 0xd5, - 0xaa, 0xb9, 0x5c, 0xa9, 0x70, 0xa7, 0x43, 0xb4, 0x9e, 0xcd, 0x6b, 0xef, - 0xe5, 0x13, 0x21, 0x35, 0x80, 0x4d, 0x00, 0x59, 0xbc, 0x55, 0x2e, 0x44, - 0x59, 0x27, 0xef, 0x03, 0xe7, 0xdf, 0x2c, 0xc1, 0xe8, 0xac, 0x6e, 0xa6, - 0xd9, 0xae, 0xbf, 0xc4, 0x7a, 0xe4, 0xcf, 0x08, 0xa5, 0x2b, 0x42, 0x47, - 0x03, 0x57, 0x4f, 0x58, 0xf3, 0x4a, 0x1e, 0x31, 0x24, 0x0f, 0xa2, 0xea, - 0xb1, 0xc9, 0xc1, 0xb1, 0xd7, 0xa6, 0xb9, 0xaa, 0xc7, 0xbc, 0x05, 0xda, - 0x8a, 0xfd, 0x81, 0x21, 0xde, 0x3f, 0xa7, 0x53, 0x82, 0x59, 0x84, 0x50, - 0x20, 0x3a, 0x19, 0x1a, 0xb5, 0xf5, 0x10, 0xd3, 0xd9, 0xb7, 0xa5, 0xa8, - 0xf4, 0xa7, 0xe1, 0xb5, 0x22, 0xd0, 0x57, 0xf2, 0xce, 0x16, 0x7f, 0x37, - 0xf5, 0x4e, 0x4b, 0x59, 0xd0, 0x54, 0x38, 0x42, 0xa2, 0x24, 0xf7, 0x00, - 0x1f, 0xdd, 0x17, 0xbf, 0xd5, 0xab, 0x90, 0xa6, 0x28, 0xb0, 0x03, 0xc7, - 0x58, 0xe7, 0xc2, 0x0b, 0x3d, 0x2e, 0x04, 0x49, 0xad, 0x57, 0xc4, 0x57, - 0x43, 0x49, 0x9c, 0x2e, 0x2d, 0x0c, 0xc3, 0xe7, 0x55, 0xc7, 0x5c, 0xb0, - 0x96, 0xa6, 0xb1, 0xab, 0xca, 0xbe, 0xbc, 0xdc, 0x87, 0x00, 0x40, 0x24, - 0xed, 0x41, 0xad, 0x54, 0x53, 0x59, 0x2a, 0x4f, 0xd4, 0x37, 0x38, 0x17, - 0xc3, 0xf2, 0x7f, 0xd0, 0x20, 0xb6, 0x07, 0xa8, 0x8e, 0xa8, 0x98, 0xb7, - 0xb1, 0xd2, 0x48, 0xf5, 0xb1, 0x19, 0xcb, 0x39, 0x55, 0x50, 0x7d, 0x59, - 0xcd, 0x53, 0x2c, 0x40, 0xe5, 0x21, 0xf9, 0xfd, 0x67, 0xda, 0x10, 0xbd, - 0xdd, 0xaa, 0xc9, 0xa6, 0x90, 0xb1, 0x57, 0xc9, 0x39, 0xea, 0xb8, 0x0e, - 0xc2, 0x30, 0xb6, 0x4a, 0x3d, 0x58, 0x1e, 0x57, 0x82, 0x47, 0x07, 0x2c, - 0x39, 0x09, 0xe5, 0xe4, 0x10, 0xc5, 0x08, 0xaf, 0x71, 0xa6, 0xc0, 0xac, - 0xde, 0xc0, 0x80, 0xdf, 0x83, 0x03, 0xf4, 0x26, 0xea, 0x43, 0x99, 0x55, - 0x0d, 0x59, 0xb9, 0x4d, 0x76, 0x35, 0x53, 0x14, 0xd2, 0xef, 0xfd, 0xcd, - 0x7c, 0xb4, 0x83, 0xa7, 0x3d, 0xa9, 0x69, 0xb9, 0x45, 0xd5, 0x45, 0xf8, - 0x85, 0x1c, 0x0d, 0x3c, 0x9a, 0x51, 0x97, 0x59, 0xb1, 0x52, 0x0f, 0x3e, - 0x1d, 0x1f, 0xfe, 0xfa, 0xb7, 0xd7, 0x1e, 0xbb, 0xf9, 0xa9, 0x21, 0xa7, - 0x07, 0xb3, 0xc1, 0xcb, 0x1c, 0xed, 0xab, 0x11, 0x39, 0x33, 0x53, 0x4c, - 0xb4, 0x58, 0x5f, 0x56, 0xab, 0x45, 0x69, 0x29, 0x3f, 0x06, 0x12, 0xe2, - 0xd9, 0xc2, 0xcc, 0xad, 0x65, 0xa6, 0xe6, 0xad, 0x06, 0xc3, 0x4c, 0xe2, - 0x7c, 0x06, 0xa0, 0x29, 0xd0, 0x45, 0x72, 0x56, 0xaa, 0x58, 0x32, 0x4c, - 0x09, 0x33, 0x6a, 0x11, 0xe4, 0xec, 0x8c, 0xcb, 0xea, 0xb2, 0x17, 0xa7, - 0x0b, 0xaa, 0x47, 0xbb, 0xec, 0xd7, 0x3e, 0xfb, 0x54, 0x1f, 0x3d, 0x3e, - 0xc8, 0x52, 0x97, 0x59, 0x80, 0x51, 0xde, 0x3b, 0x4e, 0x1c, 0x03, 0xf8, - 0x13, 0xd5, 0x3f, 0xb9, 0x31, 0xa9, 0x8d, 0xa7, 0x9c, 0xb4, 0x30, 0xce, - 0x0f, 0xf0, 0x90, 0x14, 0xa8, 0x35, 0xd6, 0x4d, 0x14, 0x59, 0x87, 0x55, - 0xc3, 0x43, 0xbb, 0x26, 0x47, 0x03, 0x44, 0xdf, 0xb4, 0xc0, 0xa9, 0xac, - 0x72, 0xa6, 0x24, 0xaf, 0x3e, 0xc5, 0x1f, 0xe5, 0x77, 0x09, 0x3d, 0x2c, - 0xa6, 0x47, 0x2e, 0x57, 0x30, 0x58, 0x97, 0x4a, 0x8b, 0x30, 0x7f, 0x0e, - 0xf8, 0xe9, 0x2c, 0xc9, 0x6d, 0xb1, 0xc7, 0xa6, 0xee, 0xaa, 0x3a, 0xbd, - 0x9f, 0xda, 0x36, 0xfe, 0x1f, 0x22, 0x56, 0x40, 0xe3, 0x53, 0x7b, 0x59, - 0x38, 0x50, 0x9d, 0x39, 0x75, 0x19, 0x0b, 0xf5, 0x7c, 0xd2, 0x74, 0xb7, - 0x81, 0xa8, 0x12, 0xa8, 0x43, 0xb6, 0xb4, 0xd0, 0xfe, 0xf2, 0x77, 0x17, - 0x02, 0x38, 0x46, 0x4f, 0x5a, 0x59, 0x96, 0x54, 0xc5, 0x41, 0x07, 0x24, - 0x48, 0x00, 0x85, 0xdc, 0xa0, 0xbe, 0x9b, 0xab, 0x9c, 0xa6, 0x74, 0xb0, - 0x8a, 0xc7, 0xfa, 0xe7, 0x70, 0x0c, 0xca, 0x2e, 0x6c, 0x49, 0xcc, 0x57, - 0xa3, 0x57, 0xe0, 0x48, 0x07, 0x2e, 0x87, 0x0b, 0x1b, 0xe7, 0xd4, 0xc6, - 0x0c, 0xb0, 0x8c, 0xa6, 0xeb, 0xab, 0x41, 0xbf, 0x58, 0xdd, 0x35, 0x01, - 0xda, 0x24, 0x62, 0x42, 0xe1, 0x54, 0x4a, 0x59, 0xd6, 0x4e, 0x4e, 0x37, - 0x95, 0x16, 0x15, 0xf2, 0xf3, 0xcf, 0xbb, 0xb5, 0xec, 0xa7, 0xaf, 0xa8, - 0x02, 0xb8, 0x40, 0xd3, 0xf6, 0xf5, 0x54, 0x1a, 0x4d, 0x3a, 0xa1, 0x50, - 0x84, 0x59, 0x8f, 0x53, 0xb6, 0x3f, 0x44, 0x21, 0x50, 0xfd, 0xca, 0xd9, - 0x9f, 0xbc, 0xa7, 0xaa, 0xdc, 0xa6, 0xe0, 0xb1, 0xe3, 0xc9, 0xdc, 0xea, - 0x61, 0x0f, 0x53, 0x31, 0x11, 0x4b, 0x5f, 0x58, 0xf1, 0x56, 0x1d, 0x47, - 0x6f, 0x2b, 0x90, 0x08, 0x42, 0xe4, 0x90, 0xc4, 0xbe, 0xae, 0x6e, 0xa6, - 0xfc, 0xac, 0x5e, 0xc1, 0x19, 0xe0, 0x34, 0x04, 0x89, 0x27, 0x5d, 0x44, - 0xca, 0x55, 0xf9, 0x58, 0x63, 0x4d, 0xeb, 0x34, 0xaf, 0x13, 0x28, 0xef, - 0x71, 0xcd, 0x1e, 0xb4, 0x68, 0xa7, 0x6c, 0xa9, 0xcf, 0xb9, 0xe0, 0xd5, - 0xed, 0xf8, 0x27, 0x1d, 0x8d, 0x3c, 0xdf, 0x51, 0x99, 0x59, 0x70, 0x52, - 0x92, 0x3d, 0x7d, 0x1e, 0x51, 0xfa, 0x21, 0xd7, 0xae, 0xba, 0xcd, 0xa9, - 0x35, 0xa7, 0x61, 0xb3, 0x4a, 0xcc, 0xc7, 0xed, 0x4e, 0x12, 0xca, 0x33, - 0xa8, 0x4c, 0xd0, 0x58, 0x2b, 0x56, 0x46, 0x45, 0xca, 0x28, 0x99, 0x05, - 0x6e, 0xe1, 0x5d, 0xc2, 0x89, 0xad, 0x67, 0xa6, 0x28, 0xae, 0x87, 0xc3, - 0xeb, 0xe2, 0x2a, 0x07, 0x35, 0x2a, 0x3c, 0x46, 0x9d, 0x56, 0x93, 0x58, - 0xd6, 0x4b, 0x7c, 0x32, 0xc3, 0x10, 0x3b, 0xec, 0x04, 0xcb, 0x91, 0xb2, - 0x04, 0xa7, 0x3c, 0xaa, 0xb4, 0xbb, 0x87, 0xd8, 0xe8, 0xfb, 0xf6, 0x1f, - 0xb6, 0x3e, 0x0b, 0x53, 0x91, 0x59, 0x39, 0x51, 0x60, 0x3b, 0xa8, 0x1b, - 0x5b, 0xf7, 0x7b, 0xd4, 0xd8, 0xb8, 0x06, 0xa9, 0xaa, 0xa7, 0xf7, 0xb4, - 0xc0, 0xce, 0xb8, 0xf0, 0x35, 0x15, 0x32, 0x36, 0x2a, 0x4e, 0x25, 0x59, - 0x55, 0x55, 0x50, 0x43, 0x21, 0x26, 0x9d, 0x02, 0xa2, 0xde, 0x40, 0xc0, - 0x66, 0xac, 0x7b, 0xa6, 0x6e, 0xaf, 0xbe, 0xc5, 0xc5, 0xe5, 0x20, 0x0a, - 0xd1, 0x2c, 0x0e, 0x48, 0x53, 0x57, 0x13, 0x58, 0x35, 0x4a, 0xfe, 0x2f, - 0xd2, 0x0d, 0x56, 0xe9, 0xa2, 0xc8, 0x1c, 0xb1, 0xb9, 0xa6, 0x23, 0xab, - 0xae, 0xbd, 0x39, 0xdb, 0xe3, 0xfe, 0xbb, 0x22, 0xcf, 0x40, 0x1e, 0x54, - 0x71, 0x59, 0xeb, 0x4f, 0x1a, 0x39, 0xd0, 0x18, 0x63, 0xf4, 0xe7, 0xd1, - 0x11, 0xb7, 0x5b, 0xa8, 0x35, 0xa8, 0xa5, 0xb6, 0x45, 0xd1, 0xa9, 0xf3, - 0x1a, 0x18, 0x89, 0x38, 0x94, 0x4f, 0x68, 0x59, 0x5b, 0x54, 0x53, 0x41, - 0x66, 0x23, 0xa0, 0xff, 0xe8, 0xdb, 0x29, 0xbe, 0x65, 0xab, 0xa4, 0xa6, - 0xc9, 0xb0, 0x0a, 0xc8, 0xa4, 0xe8, 0x14, 0x0d, 0x61, 0x2f, 0xca, 0x49, - 0xef, 0x57, 0x7e, 0x57, 0x7c, 0x48, 0x75, 0x2d, 0xda, 0x0a, 0x7a, 0xe6, - 0x4d, 0xc6, 0xc3, 0xaf, 0x81, 0xa6, 0x26, 0xac, 0xb9, 0xbf, 0xf6, 0xdd, - 0xe0, 0x01, 0x75, 0x25, 0xd6, 0x42, 0x17, 0x55, 0x3a, 0x59, 0x85, 0x4e, - 0xc6, 0x36, 0xef, 0x15, 0x6e, 0xf1, 0x5f, 0xcf, 0x60, 0xb5, 0xca, 0xa7, - 0xd8, 0xa8, 0x68, 0xb8, 0xd5, 0xd3, 0xa0, 0xf6, 0xf6, 0x1a, 0xd1, 0x3a, - 0xe9, 0x50, 0x8b, 0x59, 0x53, 0x53, 0x38, 0x3f, 0xaa, 0x20, 0xa1, 0xfc, - 0x32, 0xd9, 0x2d, 0xbc, 0x75, 0xaa, 0xed, 0xa6, 0x34, 0xb2, 0x6d, 0xca, - 0x81, 0xeb, 0x0c, 0x10, 0xe0, 0x31, 0x6f, 0x4b, 0x7a, 0x58, 0xc6, 0x56, - 0xb7, 0x46, 0xd7, 0x2a, 0xe7, 0x07, 0x9d, 0xe3, 0x12, 0xc4, 0x75, 0xae, - 0x6c, 0xa6, 0x3d, 0xad, 0xd6, 0xc1, 0xbd, 0xe0, 0xdb, 0x04, 0x27, 0x28, - 0xc7, 0x44, 0xfd, 0x55, 0xe4, 0x58, 0x0b, 0x4d, 0x64, 0x34, 0x03, 0x13, - 0x83, 0xee, 0xe2, 0xcc, 0xc5, 0xb3, 0x50, 0xa7, 0x97, 0xa9, 0x3a, 0xba, - 0x78, 0xd6, 0x96, 0xf9, 0xcc, 0x1d, 0x08, 0x3d, 0x25, 0x52, 0x9b, 0x59, - 0x29, 0x52, 0x1a, 0x3d, 0xd5, 0x1d, 0xaf, 0xf9, 0x7f, 0xd6, 0x4b, 0xba, - 0x99, 0xa9, 0x4e, 0xa7, 0xbd, 0xb3, 0xd2, 0xcc, 0x73, 0xee, 0xf2, 0x12, - 0x58, 0x34, 0xfe, 0x4c, 0xe7, 0x58, 0xfd, 0x55, 0xd7, 0x44, 0x31, 0x28, - 0xf1, 0x04, 0xc8, 0xe0, 0xe7, 0xc1, 0x42, 0xad, 0x6b, 0xa6, 0x70, 0xae, - 0x02, 0xc4, 0x91, 0xe3, 0xd1, 0x07, 0xce, 0x2a, 0xa6, 0x46, 0xc8, 0x56, - 0x78, 0x58, 0x7a, 0x4b, 0xf0, 0x31, 0x18, 0x10, 0x98, 0xeb, 0x76, 0xca, - 0x3e, 0xb2, 0xf0, 0xa6, 0x6d, 0xaa, 0x26, 0xbc, 0x1f, 0xd9, 0x93, 0xfc, - 0x96, 0x20, 0x30, 0x3f, 0x4a, 0x53, 0x8e, 0x59, 0xed, 0x50, 0xe2, 0x3a, - 0x03, 0x1b, 0xb3, 0xf6, 0xe4, 0xd3, 0x70, 0xb8, 0xe0, 0xa8, 0xc3, 0xa7, - 0x5a, 0xb5, 0x4d, 0xcf, 0x5f, 0xf1, 0xdf, 0x15, 0xb7, 0x36, 0x7f, 0x4e, - 0x35, 0x59, 0x1f, 0x55, 0xe0, 0x42, 0x86, 0x25, 0xf0, 0x01, 0x06, 0xde, - 0xc5, 0xbf, 0x2c, 0xac, 0x82, 0xa6, 0xb7, 0xaf, 0x44, 0xc6, 0x66, 0xe6, - 0xcd, 0x0a, 0x63, 0x2d, 0x75, 0x48, 0x76, 0x57, 0xf6, 0x57, 0xd2, 0x49, - 0x6f, 0x2f, 0x29, 0x0d, 0xaf, 0xe8, 0x1d, 0xc8, 0xca, 0xb0, 0xad, 0xa6, - 0x5a, 0xab, 0x22, 0xbe, 0xd5, 0xdb, 0x8e, 0xff, 0x59, 0x23, 0x45, 0x41, - 0x58, 0x54, 0x66, 0x59, 0x9f, 0x4f, 0x92, 0x38, 0x2f, 0x18, 0xb6, 0xf3, - 0x57, 0xd1, 0xab, 0xb6, 0x3c, 0xa8, 0x55, 0xa8, 0x0a, 0xb7, 0xd6, 0xd1, - 0x53, 0xf4, 0xbe, 0x18, 0x0f, 0x39, 0xe1, 0x4f, 0x73, 0x59, 0x21, 0x54, - 0xdc, 0x40, 0xcb, 0x22, 0xf3, 0xfe, 0x4b, 0xdb, 0xb6, 0xbd, 0x2d, 0xab, - 0xb3, 0xa6, 0x17, 0xb1, 0x94, 0xc8, 0x44, 0xe9, 0xc4, 0x0d, 0xec, 0x2f, - 0x2d, 0x4a, 0x11, 0x58, 0x56, 0x57, 0x18, 0x48, 0xdf, 0x2c, 0x33, 0x0a, - 0xd2, 0xe5, 0xd1, 0xc5, 0x70, 0xaf, 0x7d, 0xa6, 0x63, 0xac, 0x2f, 0xc0, - 0x97, 0xde, 0x88, 0x02, 0x13, 0x26, 0x46, 0x43, 0x4d, 0x55, 0x29, 0x59, - 0x32, 0x4e, 0x3e, 0x36, 0x49, 0x15, 0xc6, 0xf0, 0xcf, 0xce, 0x02, 0xb5, - 0xab, 0xa7, 0x04, 0xa9, 0xcb, 0xb8, 0x6e, 0xd4, 0x49, 0xf7, 0x99, 0x1b, - 0x53, 0x3b, 0x31, 0x51, 0x90, 0x59, 0x13, 0x53, 0xc1, 0x3e, 0x07, 0x20, - 0xf9, 0xfb, 0x94, 0xd8, 0xc3, 0xbb, 0x3d, 0xaa, 0x05, 0xa7, 0x87, 0xb2, - 0xf6, 0xca, 0x2b, 0xec, 0xb1, 0x10, 0x6f, 0x32, 0xcc, 0x4b, 0x92, 0x58, - 0x9f, 0x56, 0x49, 0x46, 0x42, 0x2a, 0x3d, 0x07, 0xfa, 0xe2, 0x95, 0xc3, - 0x2d, 0xae, 0x68, 0xa6, 0x83, 0xad, 0x4f, 0xc2, 0x60, 0xe1, 0x85, 0x05, - 0xbe, 0x28, 0x37, 0x45, 0x2a, 0x56, 0xd0, 0x58, 0xb3, 0x4c, 0xd6, 0x33, - 0x5f, 0x12, 0xd9, 0xed, 0x57, 0xcc, 0x6b, 0xb3, 0x38, 0xa7, 0xc5, 0xa9, - 0xa7, 0xba, 0x0f, 0xd7, 0x41, 0xfa, 0x6e, 0x1e, 0x83, 0x3d, 0x6c, 0x52, - 0x96, 0x59, 0xe8, 0x51, 0x98, 0x3c, 0x39, 0x1d, 0xfd, 0xf8, 0xf0, 0xd5, - 0xd8, 0xb9, 0x71, 0xa9, 0x65, 0xa7, 0x17, 0xb4, 0x61, 0xcd, 0x19, 0xef, - 0x9c, 0x13, 0xdf, 0x34, 0x58, 0x4d, 0xfb, 0x58, 0xcc, 0x55, 0x69, 0x44, - 0x99, 0x27, 0x42, 0x04, 0x2f, 0xe0, 0x63, 0xc1, 0x09, 0xad, 0x68, 0xa6, - 0xbc, 0xae, 0x7f, 0xc4, 0x34, 0xe4, 0x7c, 0x08, 0x64, 0x2b, 0x0f, 0x47, - 0xf1, 0x56, 0x5d, 0x58, 0x1d, 0x4b, 0x62, 0x31, 0x6f, 0x0f, 0xf1, 0xea, - 0xed, 0xc9, 0xeb, 0xb1, 0xdc, 0xa6, 0xa3, 0xaa, 0x93, 0xbc, 0xbb, 0xd9, - 0x3f, 0xfd, 0x34, 0x21, 0xaa, 0x3f, 0x89, 0x53, 0x84, 0x59, 0xa9, 0x50, - 0x5a, 0x3a, 0x65, 0x1a, 0x05, 0xf6, 0x51, 0xd3, 0x0b, 0xb8, 0xb5, 0xa8, - 0xe6, 0xa7, 0xb4, 0xb5, 0xe2, 0xcf, 0x06, 0xf2, 0x85, 0x16, 0x3f, 0x37, - 0xcf, 0x4e, 0x46, 0x59, 0xe8, 0x54, 0x6f, 0x42, 0xe9, 0x24, 0x45, 0x01, - 0x68, 0xdd, 0x4e, 0xbf, 0xef, 0xab, 0x8e, 0xa6, 0x01, 0xb0, 0xca, 0xc6, - 0x08, 0xe7, 0x78, 0x0b, 0xf5, 0x2d, 0xda, 0x48, 0x9c, 0x57, 0xd2, 0x57, - 0x73, 0x49, 0xdb, 0x2e, 0x80, 0x0c, 0x0b, 0xe8, 0x96, 0xc7, 0x7c, 0xb0, - 0x9f, 0xa6, 0x94, 0xab, 0x96, 0xbe, 0x71, 0xdc, 0x3b, 0x00, 0xf4, 0x23, - 0xbd, 0x41, 0x8d, 0x54, 0x5f, 0x59, 0x49, 0x4f, 0x15, 0x38, 0x82, 0x17, - 0x14, 0xf3, 0xbe, 0xd0, 0x51, 0xb6, 0x12, 0xa8, 0x7f, 0xa8, 0x6a, 0xb7, - 0x6b, 0xd2, 0xfd, 0xf4, 0x62, 0x19, 0x92, 0x39, 0x2f, 0x50, 0x7b, 0x59, - 0xe8, 0x53, 0x63, 0x40, 0x2e, 0x22, 0x49, 0xfe, 0xac, 0xda, 0x47, 0xbd, - 0xf3, 0xaa, 0xc4, 0xa6, 0x69, 0xb1, 0x17, 0xc9, 0xf0, 0xe9, 0x66, 0x0e, - 0x83, 0x30, 0x88, 0x4a, 0x32, 0x58, 0x2e, 0x57, 0xb3, 0x47, 0x49, 0x2c, - 0x8a, 0x09, 0x2f, 0xe5, 0x4c, 0xc5, 0x29, 0xaf, 0x74, 0xa6, 0xa2, 0xac, - 0xa8, 0xc0, 0x34, 0xdf, 0x37, 0x03, 0xaa, 0x26, 0xb9, 0x43, 0x80, 0x55, - 0x17, 0x59, 0xdf, 0x4d, 0xb4, 0x35, 0xa4, 0x14, 0x1b, 0xf0, 0x43, 0xce, - 0xa2, 0xb4, 0x92, 0xa7, 0x2a, 0xa9, 0x37, 0xb9, 0x01, 0xd5, 0xf6, 0xf7, - 0x39, 0x1c, 0xd6, 0x3b, 0x76, 0x51, 0x96, 0x59, 0xd0, 0x52, 0x49, 0x3e, - 0x63, 0x1f, 0x53, 0xfb, 0xf7, 0xd7, 0x54, 0xbb, 0x10, 0xaa, 0x13, 0xa7, - 0xe5, 0xb2, 0x7a, 0xcb, 0xd7, 0xec, 0x56, 0x11, 0xfd, 0x32, 0x28, 0x4c, - 0xa9, 0x58, 0x74, 0x56, 0xde, 0x45, 0xab, 0x29, 0x92, 0x06, 0x58, 0xe2, - 0x16, 0xc3, 0xea, 0xad, 0x66, 0xa6, 0xc6, 0xad, 0xcc, 0xc2, 0x01, 0xe2, - 0x30, 0x06, 0x58, 0x29, 0xa0, 0x45, 0x5b, 0x56, 0xb7, 0x58, 0x5b, 0x4c, - 0x49, 0x33, 0xb9, 0x11, 0x2f, 0xed, 0xcd, 0xcb, 0x12, 0xb3, 0x21, 0xa7, - 0xf6, 0xa9, 0x12, 0xbb, 0xa9, 0xd7, 0xeb, 0xfa, 0x0f, 0x1f, 0x01, 0x3e, - 0xab, 0x52, 0x98, 0x59, 0xa0, 0x51, 0x1a, 0x3c, 0x97, 0x1c, 0x53, 0xf8, - 0x57, 0xd5, 0x70, 0xb9, 0x45, 0xa9, 0x7f, 0xa7, 0x72, 0xb4, 0xef, 0xcd, - 0xc1, 0xef, 0x44, 0x14, 0x68, 0x35, 0xae, 0x4d, 0x0d, 0x59, 0x9d, 0x55, - 0xf7, 0x43, 0x02, 0x27, 0x95, 0x03, 0x8e, 0xdf, 0xed, 0xc0, 0xc3, 0xac, - 0x73, 0xa6, 0x00, 0xaf, 0x03, 0xc5, 0xd5, 0xe4, 0x28, 0x09, 0xf7, 0x2b, - 0x7a, 0x47, 0x17, 0x57, 0x43, 0x58, 0xbd, 0x4a, 0xd3, 0x30, 0xc6, 0x0e, - 0x4b, 0xea, 0x64, 0xc9, 0x98, 0xb1, 0xcc, 0xa6, 0xd5, 0xaa, 0x07, 0xbd, - 0x54, 0xda, 0xeb, 0xfd, 0xd3, 0x21, 0x23, 0x40, 0xc3, 0x53, 0x82, 0x59, - 0x57, 0x50, 0xdf, 0x39, 0xbb, 0x19, 0x60, 0xf5, 0xb9, 0xd2, 0xa7, 0xb7, - 0x8f, 0xa8, 0x05, 0xa8, 0x17, 0xb6, 0x70, 0xd0, 0xb0, 0xf2, 0x2a, 0x17, - 0xc5, 0x37, 0x22, 0x4f, 0x53, 0x59, 0xb1, 0x54, 0xf9, 0x41, 0x4f, 0x24, - 0x99, 0x00, 0xcb, 0xdc, 0xd8, 0xbe, 0xb3, 0xab, 0x98, 0xa6, 0x51, 0xb0, - 0x4c, 0xc7, 0xae, 0xe7, 0x20, 0x0c, 0x8a, 0x2e, 0x3b, 0x49, 0xc1, 0x57, - 0xae, 0x57, 0x11, 0x49, 0x4a, 0x2e, 0xd3, 0x0b, 0x6a, 0xe7, 0x0d, 0xc7, - 0x34, 0xb0, 0x8c, 0xa6, 0xd5, 0xab, 0x05, 0xbf, 0x15, 0xdd, 0xe2, 0x00, - 0x93, 0x24, 0x2e, 0x42, 0xc7, 0x54, 0x51, 0x59, 0xfb, 0x4e, 0x8b, 0x37, - 0xe2, 0x16, 0x63, 0xf2, 0x36, 0xd0, 0xe7, 0xb5, 0xf9, 0xa7, 0xa0, 0xa8, - 0xd1, 0xb7, 0xfe, 0xd2, 0xa7, 0xf5, 0x06, 0x1a, 0x15, 0x3a, 0x7c, 0x50, - 0x81, 0x59, 0xad, 0x53, 0xea, 0x3f, 0x91, 0x21, 0x9b, 0xfd, 0x15, 0xda, - 0xd1, 0xbc, 0xc1, 0xaa, 0xd2, 0xa6, 0xbb, 0xb1, 0xa3, 0xc9, 0x91, 0xea, - 0x13, 0x0f, 0x10, 0x31, 0xe8, 0x4a, 0x4f, 0x58, 0x06, 0x57, 0x4b, 0x47, - 0xb6, 0x2b, 0xdd, 0x08, 0x8d, 0xe4, 0xcb, 0xc4, 0xdf, 0xae, 0x71, 0xa6, - 0xdf, 0xac, 0x22, 0xc1, 0xd5, 0xdf, 0xdf, 0x03, 0x48, 0x27, 0x27, 0x44, - 0xb3, 0x55, 0x04, 0x59, 0x88, 0x4d, 0x2e, 0x35, 0xf9, 0x13, 0x78, 0xef, - 0xae, 0xcd, 0x4d, 0xb4, 0x71, 0xa7, 0x59, 0xa9, 0x9e, 0xb9, 0x99, 0xd5, - 0xa0, 0xf8, 0xdc, 0x1c, 0x53, 0x3c, 0xbe, 0x51, 0x9a, 0x59, 0x8c, 0x52, - 0xce, 0x3d, 0xc4, 0x1e, 0xa4, 0xfa, 0x63, 0xd7, 0xe4, 0xba, 0xdf, 0xa9, - 0x2d, 0xa7, 0x36, 0xb3, 0x0d, 0xcc, 0x77, 0xed, 0x04, 0x12, 0x85, 0x33, - 0x84, 0x4c, 0xc0, 0x58, 0x47, 0x56, 0x71, 0x45, 0x15, 0x29, 0xe6, 0x05, - 0xb7, 0xe1, 0x98, 0xc2, 0xa6, 0xad, 0x67, 0xa6, 0x0a, 0xae, 0x4a, 0xc3, - 0xa2, 0xe2, 0xdb, 0x06, 0xef, 0x29, 0x0d, 0x46, 0x86, 0x56, 0xa1, 0x58, - 0xfe, 0x4b, 0xbf, 0x32, 0x0e, 0x11, 0x8b, 0xec, 0x40, 0xcb, 0xbc, 0xb2, - 0x0c, 0xa7, 0x24, 0xaa, 0x84, 0xbb, 0x3e, 0xd8, 0x9a, 0xfb, 0xab, 0x1f, - 0x80, 0x3e, 0xeb, 0x52, 0x95, 0x59, 0x59, 0x51, 0x99, 0x3b, 0xf6, 0x1b, - 0xa8, 0xf7, 0xc1, 0xd4, 0x07, 0xb9, 0x1a, 0xa9, 0x9b, 0xa7, 0xce, 0xb4, - 0x7f, 0xce, 0x66, 0xf0, 0xee, 0x14, 0xee, 0x35, 0x07, 0x4e, 0x1d, 0x59, - 0x69, 0x55, 0x88, 0x43, 0x67, 0x26, 0xeb, 0x02, 0xef, 0xde, 0x71, 0xc0, - 0x88, 0xac, 0x76, 0xa6, 0x4b, 0xaf, 0x84, 0xc5, 0x78, 0xe5, 0xd2, 0x09, - 0x8e, 0x2c, 0xde, 0x47, 0x41, 0x57, 0x22, 0x58, 0x61, 0x4a, 0x41, 0x30, - 0x1f, 0x0e, 0xa4, 0xe9, 0xdd, 0xc8, 0x45, 0xb1, 0xbd, 0xa6, 0x0b, 0xab, - 0x79, 0xbd, 0xf0, 0xda, 0x96, 0xfe, 0x71, 0x22, 0x99, 0x40, 0x01, 0x54, - 0x78, 0x59, 0x0c, 0x50, 0x5a, 0x39, 0x18, 0x19, 0xb4, 0xf4, 0x28, 0xd2, - 0x41, 0xb7, 0x6b, 0xa8, 0x25, 0xa8, 0x79, 0xb6, 0x00, 0xd1, 0x5c, 0xf3, - 0xce, 0x17, 0x4b, 0x38, 0x71, 0x4f, 0x60, 0x59, 0x78, 0x54, 0x87, 0x41, - 0xaf, 0x23, 0xf1, 0xff, 0x2a, 0xdc, 0x66, 0xbe, 0x78, 0xab, 0xa5, 0xa6, - 0x9f, 0xb0, 0xd0, 0xc7, 0x56, 0xe8, 0xc6, 0x0c, 0x20, 0x2f, 0x9a, 0x49, - 0xe3, 0x57, 0x8e, 0x57, 0xa9, 0x48, 0xba, 0x2d, 0x29, 0x0b, 0xc2, 0xe6, - 0x90, 0xc6, 0xdf, 0xaf, 0x8a, 0xa6, 0x09, 0xac, 0x81, 0xbf, 0xaf, 0xdd, - 0x8f, 0x01, 0x30, 0x25, 0x9f, 0x42, 0x00, 0x55, 0x40, 0x59, 0xaa, 0x4e, - 0x07, 0x37, 0x38, 0x16, 0xbf, 0xf1, 0xa0, 0xcf, 0x8b, 0xb5, 0xd8, 0xa7, - 0xc8, 0xa8, 0x35, 0xb8, 0x94, 0xd3, 0x50, 0xf6, 0xaa, 0x1a, 0x98, 0x3a, - 0xc5, 0x50, 0x89, 0x59, 0x6e, 0x53, 0x73, 0x3f, 0xf1, 0x20, 0xf2, 0xfc, - 0x77, 0xd9, 0x62, 0xbc, 0x8b, 0xaa, 0xe6, 0xa6, 0x0e, 0xb2, 0x2b, 0xca, - 0x38, 0xeb, 0xbb, 0x0f, 0x9f, 0x31, 0x47, 0x4b, 0x69, 0x58, 0xde, 0x56, - 0xe4, 0x46, 0x1d, 0x2b, 0x36, 0x08, 0xe8, 0xe3, 0x4b, 0xc4, 0x9a, 0xae, - 0x67, 0xa6, 0x26, 0xad, 0x98, 0xc1, 0x77, 0xe0, 0x8a, 0x04, 0xe0, 0x27, - 0x97, 0x44, 0xe4, 0x55, 0xf0, 0x58, 0x32, 0x4d, 0xa2, 0x34, 0x54, 0x13, - 0xcd, 0xee, 0x25, 0xcd, 0xed, 0xb3, 0x5c, 0xa7, 0x81, 0xa9, 0x0c, 0xba, - 0x2f, 0xd6, 0x49, 0xf9, 0x81, 0x1d, 0xce, 0x3c, 0x06, 0x52, 0x9a, 0x59, - 0x49, 0x52, 0x52, 0x3d, 0x22, 0x1e, 0xfa, 0xf9, 0xca, 0xd6, 0x77, 0xba, - 0xb2, 0xa9, 0x42, 0xa7, 0x92, 0xb3, 0x94, 0xcc, 0x24, 0xee, 0xa6, 0x12, - 0x18, 0x34, 0xd6, 0x4c, 0xda, 0x58, 0x17, 0x56, 0x05, 0x45, 0x7d, 0x28, - 0x39, 0x05, 0x19, 0xe1, 0x19, 0xc2, 0x66, 0xad, 0x66, 0xa6, 0x51, 0xae, - 0xc8, 0xc3, 0x44, 0xe3, 0x87, 0x07, 0x83, 0x2a, 0x7b, 0x46, 0xaf, 0x56, - 0x88, 0x58, 0xa4, 0x4b, 0x2f, 0x32, 0x69, 0x10, 0xe0, 0xeb, 0xba, 0xca, - 0x62, 0xb2, 0xfc, 0xa6, 0x53, 0xaa, 0xf3, 0xbb, 0xd8, 0xd8, 0x46, 0xfc, - 0x4a, 0x20, 0xfc, 0x3e, 0x28, 0x53, 0x94, 0x59, 0x0e, 0x51, 0x1b, 0x3b, - 0x52, 0x1b, 0xfe, 0xf6, 0x2a, 0xd4, 0xa2, 0xb8, 0xed, 0xa8, 0xbc, 0xa7, - 0x28, 0xb5, 0x10, 0xcf, 0x10, 0xf1, 0x91, 0x15, 0x7b, 0x36, 0x57, 0x4e, - 0x2e, 0x59, 0x39, 0x55, 0x10, 0x43, 0xd3, 0x25, 0x3b, 0x02, 0x52, 0xde, - 0xf9, 0xbf, 0x49, 0xac, 0x7e, 0xa6, 0x96, 0xaf, 0x06, 0xc6, 0x1c, 0xe6, - 0x7d, 0x0a, 0x21, 0x2d, 0x44, 0x48, 0x67, 0x57, 0x04, 0x58, 0xff, 0x49, - 0xb2, 0x2f, 0x75, 0x0d, 0xfd, 0xe8, 0x5a, 0xc8, 0xf0, 0xb0, 0xb1, 0xa6, - 0x42, 0xab, 0xeb, 0xbd, 0x8e, 0xdb, 0x40, 0xff, 0x0f, 0x23, 0x10, 0x41, - 0x3c, 0x54, 0x6c, 0x59, 0xc1, 0x4f, 0xd4, 0x38, 0x74, 0x18, 0x0b, 0xf4, - 0x95, 0xd1, 0xdc, 0xb6, 0x4a, 0xa8, 0x45, 0xa8, 0xdc, 0xb6, 0x93, 0xd1, - 0x05, 0xf4, 0x72, 0x18, 0xd2, 0x38, 0xbe, 0x4f, 0x6c, 0x59, 0x3f, 0x54, - 0x0f, 0x41, 0x16, 0x23, 0x41, 0xff, 0x93, 0xdb, 0xec, 0xbd, 0x44, 0xab, - 0xb0, 0xa6, 0xee, 0xb0, 0x59, 0xc8, 0xf7, 0xe8, 0x74, 0x0d, 0xad, 0x2f, - 0xfe, 0x49, 0x03, 0x58, 0x67, 0x57, 0x47, 0x48, 0x24, 0x2d, 0x80, 0x0a, - 0x20, 0xe6, 0x08, 0xc6, 0x98, 0xaf, 0x7e, 0xa6, 0x47, 0xac, 0xf8, 0xbf, - 0x4d, 0xde, 0x3c, 0x02, 0xc8, 0x25, 0x15, 0x43, 0x32, 0x55, 0x33, 0x59, - 0x57, 0x4e, 0x7e, 0x36, 0x94, 0x15, 0x14, 0xf1, 0x11, 0xcf, 0x2d, 0xb5, - 0xba, 0xa7, 0xef, 0xa8, 0x9e, 0xb8, 0x27, 0xd4, 0xfa, 0xf6, 0x50, 0x1b, - 0x16, 0x3b, 0x10, 0x51, 0x90, 0x59, 0x2d, 0x53, 0xfb, 0x3e, 0x50, 0x20, - 0x48, 0xfc, 0xdc, 0xd8, 0xf3, 0xbb, 0x58, 0xaa, 0xf8, 0xa6, 0x63, 0xb2, - 0xb5, 0xca, 0xde, 0xeb, 0x63, 0x10, 0x30, 0x32, 0xa0, 0x4b, 0x86, 0x58, - 0xb3, 0x56, 0x78, 0x46, 0x8c, 0x2a, 0x87, 0x07, 0x48, 0xe3, 0xcc, 0xc3, - 0x4f, 0xae, 0x6a, 0xa6, 0x61, 0xad, 0x19, 0xc2, 0x15, 0xe1, 0x36, 0x05, - 0x79, 0x28, 0x03, 0x45, 0x16, 0x56, 0xd9, 0x58, 0xdc, 0x4c, 0x17, 0x34, - 0xab, 0x12, 0x27, 0xee, 0x97, 0xcc, 0x94, 0xb3, 0x43, 0xa7, 0xb0, 0xa9, - 0x74, 0xba, 0xca, 0xd6, 0xf2, 0xf9, 0x24, 0x1e, 0x4a, 0x3d, 0x4b, 0x52, - 0x99, 0x59, 0x06, 0x52, 0xd4, 0x3c, 0x81, 0x1d, 0x4f, 0xf9, 0x30, 0xd6, - 0x10, 0xba, 0x80, 0xa9, 0x5e, 0xa7, 0xea, 0xb3, 0x21, 0xcd, 0xcb, 0xee, - 0x50, 0x13, 0x9f, 0x34, 0x32, 0x4d, 0xed, 0x58, 0xe7, 0x55, 0x98, 0x44, - 0xe3, 0x27, 0x90, 0x04, 0x75, 0xe0, 0xa2, 0xc1, 0x20, 0xad, 0x6d, 0xa6, - 0x95, 0xae, 0x48, 0xc4, 0xe8, 0xe3, 0x2e, 0x08, 0x1f, 0x2b, 0xde, 0x46, - 0xdf, 0x56, 0x6a, 0x58, 0x47, 0x4b, 0xa4, 0x31, 0xbd, 0x0f, 0x3d, 0xeb, - 0x2d, 0xca, 0x10, 0xb2, 0xe5, 0xa6, 0x8c, 0xaa, 0x5d, 0xbc, 0x77, 0xd9, - 0xec, 0xfc, 0xef, 0x20, 0x6f, 0x3f, 0x6f, 0x53, 0x86, 0x59, 0xcb, 0x50, - 0x96, 0x3a, 0xb0, 0x1a, 0x54, 0xf6, 0x95, 0xd3, 0x39, 0xb8, 0xc8, 0xa8, - 0xd7, 0xa7, 0x8a, 0xb5, 0x9e, 0xcf, 0xb8, 0xf1, 0x38, 0x16, 0x02, 0x37, - 0xaa, 0x4e, 0x40, 0x59, 0xff, 0x54, 0xa4, 0x42, 0x30, 0x25, 0x96, 0x01, - 0xb0, 0xdd, 0x83, 0xbf, 0x0d, 0xac, 0x85, 0xa6, 0xe4, 0xaf, 0x87, 0xc6, - 0xc1, 0xe6, 0x26, 0x0b, 0xb6, 0x2d, 0xa7, 0x48, 0x90, 0x57, 0xdd, 0x57, - 0xa4, 0x49, 0x1b, 0x2f, 0xd0, 0x0c, 0x56, 0xe8, 0xd4, 0xc7, 0xa0, 0xb0, - 0xa4, 0xa6, 0x79, 0xab, 0x62, 0xbe, 0x27, 0xdc, 0xee, 0xff, 0xab, 0x23, - 0x86, 0x41, 0x75, 0x54, 0x62, 0x59, 0x71, 0x4f, 0x4e, 0x38, 0xd4, 0x17, - 0x5b, 0xf3, 0x08, 0xd1, 0x78, 0xb6, 0x25, 0xa8, 0x6d, 0xa8, 0x3c, 0xb7, - 0x28, 0xd2, 0xad, 0xf4, 0x18, 0x19, 0x54, 0x39, 0x0e, 0x50, 0x75, 0x59, - 0x04, 0x54, 0x99, 0x40, 0x77, 0x22, 0x98, 0xfe, 0xf4, 0xda, 0x7b, 0xbd, - 0x0c, 0xab, 0xbe, 0xa6, 0x40, 0xb1, 0xdf, 0xc8, 0x9c, 0xe9, 0x20, 0x0e, - 0x3a, 0x30, 0x62, 0x4a, 0x1f, 0x58, 0x44, 0x57, 0xdf, 0x47, 0x91, 0x2c, - 0xd6, 0x09, 0x7a, 0xe5, 0x89, 0xc5, 0x4a, 0xaf, 0x79, 0xa6, 0x84, 0xac, - 0x70, 0xc0, 0xeb, 0xde, 0xe7, 0x02, 0x65, 0x26, 0x84, 0x43, 0x69, 0x55, - 0x1f, 0x59, 0x04, 0x4e, 0xf6, 0x35, 0xed, 0x14, 0x6d, 0xf0, 0x81, 0xce, - 0xce, 0xb4, 0x9f, 0xa7, 0x16, 0xa9, 0x09, 0xb9, 0xba, 0xd4, 0xa7, 0xf7, - 0xf0, 0x1b, 0x99, 0x3b, 0x57, 0x51, 0x94, 0x59, 0xee, 0x52, 0x7f, 0x3e, - 0xb3, 0x1f, 0x99, 0xfb, 0x45, 0xd8, 0x83, 0xbb, 0x26, 0xaa, 0x0d, 0xa7, - 0xb8, 0xb2, 0x40, 0xcb, 0x83, 0xec, 0x0f, 0x11, 0xb9, 0x32, 0xff, 0x4b, - 0x9e, 0x58, 0x88, 0x56, 0x0e, 0x46, 0xf4, 0x29, 0xde, 0x06, 0xa4, 0xe2, - 0x4f, 0xc3, 0x08, 0xae, 0x6b, 0xa6, 0xa2, 0xad, 0x96, 0xc2, 0xb4, 0xe1, - 0xe2, 0x05, 0x12, 0x29, 0x71, 0x45, 0x41, 0x56, 0xc7, 0x58, 0x7f, 0x4c, - 0x8d, 0x33, 0x05, 0x12, 0x7d, 0xed, 0x0d, 0xcc, 0x3a, 0xb3, 0x2c, 0xa7, - 0xde, 0xa9, 0xe4, 0xba, 0x5d, 0xd7, 0xa3, 0xfa, 0xbf, 0x1e, 0xcb, 0x3d, - 0x8d, 0x52, 0x97, 0x59, 0xc2, 0x51, 0x54, 0x3c, 0xe0, 0x1c, 0xa4, 0xf8, - 0x9b, 0xd5, 0xa2, 0xb9, 0x57, 0xa9, 0x75, 0xa7, 0x45, 0xb4, 0xb1, 0xcd, - 0x71, 0xef, 0xf7, 0x13, 0x2a, 0x35, 0x87, 0x4d, 0x03, 0x59, 0xb6, 0x55, - 0x26, 0x44, 0x4d, 0x27, 0xe2, 0x03, 0xd9, 0xdf, 0x23, 0xc1, 0xe3, 0xac, - 0x6e, 0xa6, 0xdf, 0xae, 0xc8, 0xc4, 0x8a, 0xe4, 0xd9, 0x08, 0xb5, 0x2b, - 0x45, 0x47, 0x0a, 0x57, 0x4b, 0x58, 0xed, 0x4a, 0x12, 0x31, 0x16, 0x0f, - 0x96, 0xea, 0xa4, 0xc9, 0xbd, 0xb1, 0xd4, 0xa6, 0xbd, 0xaa, 0xd2, 0xbc, - 0x0f, 0xda, 0x98, 0xfd, 0x8f, 0x21, 0xe6, 0x3f, 0xad, 0x53, 0x80, 0x59, - 0x7f, 0x50, 0x15, 0x3a, 0x0d, 0x1a, 0xa7, 0xf5, 0x04, 0xd3, 0xd0, 0xb7, - 0xa4, 0xa8, 0xf4, 0xa7, 0xeb, 0xb5, 0x2d, 0xd0, 0x63, 0xf2, 0xde, 0x16, - 0x87, 0x37, 0xfd, 0x4e, 0x4d, 0x59, 0xc9, 0x54, 0x31, 0x42, 0x95, 0x24, - 0xe9, 0x00, 0x13, 0xdd, 0x0e, 0xbf, 0xcf, 0xab, 0x93, 0xa6, 0x2c, 0xb0, - 0x0f, 0xc7, 0x64, 0xe7, 0xd0, 0x0b, 0x49, 0x2e, 0x0c, 0x49, 0xb0, 0x57, - 0xc0, 0x57, 0x3e, 0x49, 0x8c, 0x2e, 0x24, 0x0c, 0xb2, 0xe7, 0x4e, 0xc7, - 0x53, 0xb0, 0x98, 0xa6, 0xb2, 0xab, 0xd7, 0xbe, 0xc5, 0xdc, 0x98, 0x00, - 0x49, 0x24, 0xfa, 0x41, 0xad, 0x54, 0x57, 0x59, 0x1e, 0x4f, 0xcd, 0x37, - 0x2a, 0x17, 0xb5, 0xf2, 0x75, 0xd0, 0x16, 0xb6, 0x06, 0xa8, 0x90, 0xa8, - 0xa1, 0xb7, 0xbc, 0xd2, 0x57, 0xf5, 0xbc, 0x19, 0xd7, 0x39, 0x5a, 0x50, - 0x7e, 0x59, 0xc9, 0x53, 0x21, 0x40, 0xd9, 0x21, 0xed, 0xfd, 0x58, 0xda, - 0x09, 0xbd, 0xd7, 0xaa, 0xcb, 0xa6, 0x97, 0xb1, 0x60, 0xc9, 0x49, 0xea, - 0xc2, 0x0e, 0xd0, 0x30, 0xbc, 0x4a, 0x41, 0x58, 0x19, 0x57, 0x7b, 0x47, - 0xfa, 0x2b, 0x2d, 0x09, 0xd6, 0xe4, 0x09, 0xc5, 0xfe, 0xae, 0x75, 0xa6, - 0xc0, 0xac, 0xec, 0xc0, 0x89, 0xdf, 0x94, 0x03, 0xfd, 0x26, 0xf6, 0x43, - 0x9b, 0x55, 0x0c, 0x59, 0xb3, 0x4d, 0x68, 0x35, 0x4b, 0x14, 0xc0, 0xef, - 0xf6, 0xcd, 0x71, 0xb4, 0x82, 0xa7, 0x42, 0xa9, 0x6f, 0xb9, 0x53, 0xd5, - 0x52, 0xf8, 0x91, 0x1c, 0x19, 0x3c, 0x9e, 0x51, 0x98, 0x59, 0xac, 0x52, - 0x05, 0x3e, 0x0f, 0x1f, 0xf2, 0xfa, 0xab, 0xd7, 0x13, 0xbb, 0xfa, 0xa9, - 0x1c, 0xa7, 0x15, 0xb3, 0xc7, 0xcb, 0x2c, 0xed, 0xb8, 0x11, 0x42, 0x33, - 0x5e, 0x4c, 0xb4, 0x58, 0x5b, 0x56, 0xa4, 0x45, 0x5a, 0x29, 0x35, 0x06, - 0x03, 0xe2, 0xcf, 0xc2, 0xc6, 0xad, 0x68, 0xa6, 0xe9, 0xad, 0x11, 0xc3, - 0x57, 0xe2, 0x8b, 0x06, 0xac, 0x29, 0xda, 0x45, 0x73, 0x56, 0xa9, 0x58, - 0x2b, 0x4c, 0xfe, 0x32, 0x5d, 0x11, 0xd6, 0xec, 0x81, 0xcb, 0xe2, 0xb2, - 0x19, 0xa7, 0x0b, 0xaa, 0x53, 0xbb, 0xf5, 0xd7, 0x4e, 0xfb, 0x5f, 0x1f, - 0x49, 0x3e, 0xcb, 0x52, 0x99, 0x59, 0x78, 0x51, 0xd6, 0x3b, 0x3f, 0x1c, - 0xf7, 0xf7, 0x06, 0xd5, 0x38, 0xb9, 0x2d, 0xa9, 0x8f, 0xa7, 0xa2, 0xb4, - 0x3e, 0xce, 0x19, 0xf0, 0xa0, 0x14, 0xb1, 0x35, 0xde, 0x4d, 0x16, 0x59, - 0x81, 0x55, 0xbb, 0x43, 0xae, 0x26, 0x3a, 0x03, 0x37, 0xdf, 0xac, 0xc0, - 0xa1, 0xac, 0x76, 0xa6, 0x26, 0xaf, 0x4b, 0xc5, 0x2b, 0xe5, 0x85, 0x09, - 0x48, 0x2c, 0xb0, 0x47, 0x2f, 0x57, 0x30, 0x58, 0x8c, 0x4a, 0x84, 0x30, - 0x6d, 0x0e, 0xf0, 0xe9, 0x1c, 0xc9, 0x6a, 0xb1, 0xc4, 0xa6, 0xf3, 0xaa, - 0x43, 0xbd, 0xaa, 0xda, 0x45, 0xfe, 0x2b, 0x22, 0x60, 0x40, 0xe7, 0x53, - 0x7b, 0x59, 0x30, 0x50, 0x95, 0x39, 0x67, 0x19, 0xff, 0xf4, 0x6e, 0xd2, - 0x6e, 0xb7, 0x7c, 0xa8, 0x15, 0xa8, 0x4d, 0xb6, 0xbc, 0xd0, 0x10, 0xf3, - 0x80, 0x17, 0x0e, 0x38, 0x4d, 0x4f, 0x5a, 0x59, 0x93, 0x54, 0xbb, 0x41, - 0xf9, 0x23, 0x3e, 0x00, 0x75, 0xdc, 0x9a, 0xbe, 0x92, 0xab, 0xa1, 0xa6, - 0x78, 0xb0, 0x96, 0xc7, 0x06, 0xe8, 0x7d, 0x0c, 0xd7, 0x2e, 0x72, 0x49, - 0xd2, 0x57, 0x9c, 0x57, 0xdb, 0x48, 0xf9, 0x2d, 0x7b, 0x0b, 0x0d, 0xe7, - 0xca, 0xc6, 0x05, 0xb0, 0x8d, 0xa6, 0xee, 0xab, 0x4b, 0xbf, 0x64, 0xdd, - 0x44, 0x01, 0xe4, 0x24, 0x6e, 0x42, 0xe4, 0x54, 0x49, 0x59, 0xcf, 0x4e, - 0x45, 0x37, 0x83, 0x16, 0x0f, 0xf2, 0xe1, 0xcf, 0xb8, 0xb5, 0xe5, 0xa7, - 0xb6, 0xa8, 0x07, 0xb8, 0x4f, 0xd3, 0x02, 0xf6, 0x5f, 0x1a, 0x5b, 0x3a, - 0xa4, 0x50, 0x86, 0x59, 0x8a, 0x53, 0xab, 0x3f, 0x3a, 0x21, 0x40, 0xfd, - 0xc0, 0xd9, 0x95, 0xbc, 0xa3, 0xaa, 0xdd, 0xa6, 0xe9, 0xb1, 0xe9, 0xc9, - 0xef, 0xea, 0x6b, 0x0f, 0x5f, 0x31, 0x1b, 0x4b, 0x5d, 0x58, 0xf0, 0x56, - 0x14, 0x47, 0x64, 0x2b, 0x83, 0x08, 0x34, 0xe4, 0x85, 0xc4, 0xba, 0xae, - 0x6b, 0xa6, 0x05, 0xad, 0x63, 0xc1, 0x2b, 0xe0, 0x3d, 0x04, 0x98, 0x27, - 0x64, 0x44, 0xce, 0x55, 0xfa, 0x58, 0x58, 0x4d, 0xe5, 0x34, 0x9c, 0x13, - 0x20, 0xef, 0x61, 0xcd, 0x1b, 0xb4, 0x64, 0xa7, 0x70, 0xa9, 0xd8, 0xb9, - 0xea, 0xd5, 0xfc, 0xf8, 0x34, 0x1d, 0x97, 0x3c, 0xe5, 0x51, 0x98, 0x59, - 0x6b, 0x52, 0x88, 0x3d, 0x6f, 0x1e, 0x48, 0xfa, 0x0e, 0xd7, 0xad, 0xba, - 0xc3, 0xa9, 0x3a, 0xa7, 0x68, 0xb3, 0x54, 0xcc, 0xd6, 0xed, 0x5b, 0x12, - 0xd3, 0x33, 0xb3, 0x4c, 0xcd, 0x58, 0x2d, 0x56, 0x38, 0x45, 0xc1, 0x28, - 0x8a, 0x05, 0x62, 0xe1, 0x53, 0xc2, 0x82, 0xad, 0x6a, 0xa6, 0x2b, 0xae, - 0x94, 0xc3, 0xf4, 0xe2, 0x3b, 0x07, 0x3e, 0x2a, 0x48, 0x46, 0x9e, 0x56, - 0x91, 0x58, 0xd0, 0x4b, 0x6f, 0x32, 0xb7, 0x10, 0x2e, 0xec, 0xf7, 0xca, - 0x8c, 0xb2, 0x01, 0xa7, 0x41, 0xaa, 0xbc, 0xbb, 0x94, 0xd8, 0xf4, 0xfb, - 0x04, 0x20, 0xc0, 0x3e, 0x0e, 0x53, 0x94, 0x59, 0x2f, 0x51, 0x5a, 0x3b, - 0x98, 0x1b, 0x4f, 0xf7, 0x70, 0xd4, 0xcd, 0xb8, 0x06, 0xa9, 0xa8, 0xa7, - 0x03, 0xb5, 0xc9, 0xce, 0xc6, 0xf0, 0x42, 0x15, 0x3d, 0x36, 0x30, 0x4e, - 0x28, 0x59, 0x4f, 0x55, 0x48, 0x43, 0x17, 0x26, 0x89, 0x02, 0x9f, 0xde, - 0x2c, 0xc0, 0x68, 0xac, 0x7b, 0xa6, 0x6f, 0xaf, 0xd0, 0xc5, 0xcb, 0xe5, - 0x33, 0x0a, 0xd9, 0x2c, 0x19, 0x48, 0x52, 0x57, 0x15, 0x58, 0x2b, 0x4a, - 0xf4, 0x2f, 0xc3, 0x0d, 0x49, 0xe9, 0x97, 0xc8, 0x18, 0xb1, 0xb4, 0xa6, - 0x2b, 0xab, 0xb4, 0xbd, 0x47, 0xdb, 0xf1, 0xfe, 0xc7, 0x22, 0xd8, 0x40, - 0x23, 0x54, 0x70, 0x59, 0xe6, 0x4f, 0x0e, 0x39, 0xc4, 0x18, 0x55, 0xf4, - 0xdb, 0xd1, 0x0a, 0xb7, 0x57, 0xa8, 0x39, 0xa8, 0xac, 0xb6, 0x51, 0xd1, - 0xb6, 0xf3, 0x27, 0x18, 0x94, 0x38, 0x9a, 0x4f, 0x68, 0x59, 0x59, 0x54, - 0x45, 0x41, 0x5f, 0x23, 0x8f, 0xff, 0xdc, 0xdb, 0x20, 0xbe, 0x60, 0xab, - 0xa7, 0xa6, 0xcc, 0xb0, 0x19, 0xc8, 0xac, 0xe8, 0x27, 0x0d, 0x68, 0x2f, - 0xd5, 0x49, 0xef, 0x57, 0x7d, 0x57, 0x72, 0x48, 0x6b, 0x2d, 0xcd, 0x0a, - 0x6b, 0xe6, 0x44, 0xc6, 0xbc, 0xaf, 0x80, 0xa6, 0x2e, 0xac, 0xbe, 0xbf, - 0x05, 0xde, 0xed, 0x01, 0x82, 0x25, 0xdf, 0x42, 0x1a, 0x55, 0x3a, 0x59, - 0x7c, 0x4e, 0xc0, 0x36, 0xdb, 0x15, 0x68, 0xf1, 0x4e, 0xcf, 0x5c, 0xb5, - 0xc5, 0xa7, 0xdd, 0xa8, 0x6f, 0xb8, 0xe2, 0xd3, 0xae, 0xf6, 0x02, 0x1b, - 0xdc, 0x3a, 0xee, 0x50, 0x8d, 0x59, 0x4b, 0x53, 0x34, 0x3f, 0x98, 0x20, - 0x97, 0xfc, 0x23, 0xd9, 0x27, 0xbc, 0x6e, 0xaa, 0xf1, 0xa6, 0x3a, 0xb2, - 0x76, 0xca, 0x93, 0xeb, 0x14, 0x10, 0xee, 0x31, 0x77, 0x4b, 0x79, 0x58, - 0xc7, 0x56, 0xaa, 0x46, 0xce, 0x2a, 0xd8, 0x07, 0x92, 0xe3, 0x05, 0xc4, - 0x73, 0xae, 0x67, 0xa6, 0x47, 0xad, 0xdc, 0xc1, 0xce, 0xe0, 0xe5, 0x04, - 0x35, 0x28, 0xcf, 0x44, 0x00, 0x56, 0xe5, 0x58, 0x02, 0x4d, 0x59, 0x34, - 0xf7, 0x12, 0x74, 0xee, 0xd8, 0xcc, 0xbe, 0xb3, 0x4c, 0xa7, 0x9e, 0xa9, - 0x41, 0xba, 0x83, 0xd6, 0xa6, 0xf9, 0xd5, 0x1d, 0x16, 0x3d, 0x29, 0x52, - 0x9a, 0x59, 0x25, 0x52, 0x0d, 0x3d, 0xcd, 0x1d, 0x9c, 0xf9, 0x79, 0xd6, - 0x3c, 0xba, 0x9b, 0xa9, 0x4c, 0xa7, 0xc5, 0xb3, 0xe0, 0xcc, 0x7b, 0xee, - 0x06, 0x13, 0x5c, 0x34, 0x0c, 0x4d, 0xe2, 0x58, 0x00, 0x56, 0xc6, 0x44, - 0x2e, 0x28, 0xdb, 0x04, 0xc2, 0xe0, 0xd9, 0xc1, 0x3e, 0xad, 0x6c, 0xa6, - 0x75, 0xae, 0x0c, 0xc4, 0x9e, 0xe3, 0xde, 0x07, 0xdc, 0x2a, 0xad, 0x46, - 0xcb, 0x56, 0x76, 0x58, 0x73, 0x4b, 0xe4, 0x31, 0x0c, 0x10, 0x89, 0xeb, - 0x6c, 0xca, 0x37, 0xb2, 0xee, 0xa6, 0x72, 0xaa, 0x2d, 0xbc, 0x2e, 0xd9, - 0x9e, 0xfc, 0xa4, 0x20, 0x39, 0x3f, 0x50, 0x53, 0x8c, 0x59, 0xea, 0x50, - 0xd3, 0x3a, 0xfb, 0x1a, 0xa1, 0xf6, 0xdc, 0xd3, 0x66, 0xb8, 0xdc, 0xa8, - 0xc8, 0xa7, 0x5f, 0xb5, 0x5a, 0xcf, 0x6d, 0xf1, 0xe9, 0x15, 0xc6, 0x36, - 0x82, 0x4e, 0x39, 0x59, 0x19, 0x55, 0xd7, 0x42, 0x7b, 0x25, 0xe1, 0x01, - 0xfa, 0xdd, 0xbb, 0xbf, 0x27, 0xac, 0x84, 0xa6, 0xbc, 0xaf, 0x4f, 0xc6, - 0x73, 0xe6, 0xda, 0x0a, 0x70, 0x2d, 0x79, 0x48, 0x7f, 0x57, 0xee, 0x57, - 0xce, 0x49, 0x62, 0x2f, 0x19, 0x0d, 0xa6, 0xe8, 0x0f, 0xc8, 0xc7, 0xb0, - 0xa8, 0xa6, 0x62, 0xab, 0x27, 0xbe, 0xe5, 0xdb, 0x9b, 0xff, 0x65, 0x23, - 0x4f, 0x41, 0x5b, 0x54, 0x67, 0x59, 0x96, 0x4f, 0x8d, 0x38, 0x1b, 0x18, - 0xaf, 0xf3, 0x46, 0xd1, 0xa8, 0xb6, 0x36, 0xa8, 0x58, 0xa8, 0x14, 0xb7, - 0xde, 0xd1, 0x64, 0xf4, 0xca, 0x18, 0x18, 0x39, 0xea, 0x4f, 0x70, 0x59, - 0x20, 0x54, 0xd0, 0x40, 0xc0, 0x22, 0xe5, 0xfe, 0x3e, 0xdb, 0xae, 0xbd, - 0x28, 0xab, 0xb4, 0xa6, 0x1e, 0xb1, 0x9d, 0xc8, 0x54, 0xe9, 0xce, 0x0d, - 0xfc, 0x2f, 0x30, 0x4a, 0x17, 0x58, 0x50, 0x57, 0x12, 0x48, 0xd3, 0x2c, - 0x25, 0x0a, 0xc6, 0xe5, 0xc4, 0xc5, 0x6c, 0xaf, 0x7d, 0xa6, 0x66, 0xac, - 0x3c, 0xc0, 0x9e, 0xde, 0x9c, 0x02, 0x1a, 0x26, 0x52, 0x43, 0x51, 0x55, - 0x26, 0x59, 0x2d, 0x4e, 0x32, 0x36, 0x3c, 0x15, 0xb8, 0xf0, 0xc5, 0xce, - 0xf8, 0xb4, 0xab, 0xa7, 0x05, 0xa9, 0xd6, 0xb8, 0x78, 0xd4, 0x57, 0xf7, - 0xa6, 0x1b, 0x5c, 0x3b, 0x39, 0x51, 0x8f, 0x59, 0x0e, 0x53, 0xb7, 0x3e, - 0xfb, 0x1f, 0xea, 0xfb, 0x8b, 0xd8, 0xb5, 0xbb, 0x3f, 0xaa, 0x01, 0xa7, - 0x92, 0xb2, 0xff, 0xca, 0x38, 0xec, 0xc1, 0x10, 0x76, 0x32, 0xd7, 0x4b, - 0x92, 0x58, 0x9c, 0x56, 0x41, 0x46, 0x36, 0x2a, 0x2e, 0x07, 0xef, 0xe2, - 0x89, 0xc3, 0x29, 0xae, 0x69, 0xa6, 0x85, 0xad, 0x5b, 0xc2, 0x6c, 0xe1, - 0x93, 0x05, 0xcb, 0x28, 0x3e, 0x45, 0x2f, 0x56, 0xcd, 0x58, 0xac, 0x4c, - 0xcd, 0x33, 0x4f, 0x12, 0xce, 0xed, 0x49, 0xcc, 0x67, 0xb3, 0x33, 0xa7, - 0xcc, 0xa9, 0xae, 0xba, 0x1a, 0xd7, 0x52, 0xfa, 0x76, 0x1e, 0x92, 0x3d, - 0x6e, 0x52, 0x98, 0x59, 0xe2, 0x51, 0x8e, 0x3c, 0x2b, 0x1d, 0xf3, 0xf8, - 0xdf, 0xd5, 0xd4, 0xb9, 0x6b, 0xa9, 0x69, 0xa7, 0x1c, 0xb4, 0x6d, 0xcd, - 0x26, 0xef, 0xaa, 0x13, 0xeb, 0x34, 0x5d, 0x4d, 0xfc, 0x58, 0xca, 0x55, - 0x5e, 0x44, 0x90, 0x27, 0x32, 0x04, 0x22, 0xe0, 0x5b, 0xc1, 0x02, 0xad, - 0x6a, 0xa6, 0xc1, 0xae, 0x8a, 0xc4, 0x40, 0xe4, 0x8a, 0x08, 0x70, 0x2b, - 0x17, 0x47, 0xf5, 0x56, 0x5a, 0x58, 0x16, 0x4b, 0x56, 0x31, 0x62, 0x0f, - 0xe3, 0xea, 0xe4, 0xc9, 0xe1, 0xb1, 0xdf, 0xa6, 0xa2, 0xaa, 0xa0, 0xbc, - 0xc6, 0xd9, 0x4c, 0xfd, 0x43, 0x21, 0xb0, 0x3f, 0x90, 0x53, 0x84, 0x59, - 0xa0, 0x50, 0x55, 0x3a, 0x52, 0x1a, 0xfb, 0xf5, 0x45, 0xd3, 0x00, 0xb8, - 0xb7, 0xa8, 0xe3, 0xa7, 0xbf, 0xb5, 0xed, 0xcf, 0x12, 0xf2, 0x95, 0x16, - 0x47, 0x37, 0xd6, 0x4e, 0x4a, 0x59, 0xdf, 0x54, 0x6a, 0x42, 0xd9, 0x24, - 0x3a, 0x01, 0x5b, 0xdd, 0x43, 0xbf, 0xec, 0xab, 0x8d, 0xa6, 0x09, 0xb0, - 0xd2, 0xc6, 0x18, 0xe7, 0x82, 0x0b, 0x05, 0x2e, 0xde, 0x48, 0xa0, 0x57, - 0xd0, 0x57, 0x6b, 0x49, 0xcf, 0x2e, 0x74, 0x0c, 0xfb, 0xe7, 0x8e, 0xc7, - 0x75, 0xb0, 0x9e, 0xa6, 0x97, 0xab, 0xa2, 0xbe, 0x7b, 0xdc, 0x4c, 0x00, - 0xfe, 0x23, 0xc6, 0x41, 0x93, 0x54, 0x5b, 0x59, 0x48, 0x4f, 0x04, 0x38, - 0x7c, 0x17, 0xff, 0xf2, 0xb9, 0xd0, 0x43, 0xb6, 0x15, 0xa8, 0x7e, 0xa8, - 0x75, 0xb7, 0x75, 0xd2, 0x0b, 0xf5, 0x6f, 0x19, 0x9c, 0x39, 0x36, 0x50, - 0x7b, 0x59, 0xe4, 0x53, 0x59, 0x40, 0x21, 0x22, 0x3c, 0xfe, 0x9f, 0xda, - 0x3f, 0xbd, 0xed, 0xaa, 0xc7, 0xa6, 0x6d, 0xb1, 0x26, 0xc9, 0xf9, 0xe9, - 0x77, 0x0e, 0x8b, 0x30, 0x93, 0x4a, 0x31, 0x58, 0x2e, 0x57, 0xa8, 0x47, - 0x40, 0x2c, 0x7b, 0x09, 0x22, 0xe5, 0x42, 0xc5, 0x23, 0xaf, 0x74, 0xa6, - 0xa7, 0xac, 0xb2, 0xc0, 0x40, 0xdf, 0x44, 0x03, 0xb8, 0x26, 0xc0, 0x43, - 0x86, 0x55, 0x14, 0x59, 0xd8, 0x4d, 0xab, 0x35, 0x94, 0x14, 0x10, 0xf0, - 0x36, 0xce, 0x9b, 0xb4, 0x90, 0xa7, 0x2d, 0xa9, 0x3f, 0xb9, 0x0f, 0xd5, - 0x00, 0xf8, 0x4b, 0x1c, 0xd9, 0x3b, 0x82, 0x51, 0x93, 0x59, 0xcc, 0x52, - 0x3e, 0x3e, 0x59, 0x1f, 0x41, 0xfb, 0xef, 0xd7, 0x49, 0xbb, 0x0b, 0xaa, - 0x19, 0xa7, 0xe6, 0xb2, 0x8b, 0xcb, 0xdf, 0xec, 0x67, 0x11, 0x07, 0x33, - 0x2f, 0x4c, 0xaa, 0x58, 0x73, 0x56, 0xd1, 0x45, 0xa6, 0x29, 0x7c, 0x06, - 0x54, 0xe2, 0x04, 0xc3, 0xe9, 0xad, 0x65, 0xa6, 0xcb, 0xad, 0xd7, 0xc2, - 0x0d, 0xe2, 0x3d, 0x06, 0x65, 0x29, 0xa8, 0x45, 0x60, 0x56, 0xb3, 0x58, - 0x55, 0x4c, 0x3f, 0x33, 0xa9, 0x11, 0x24, 0xed, 0xc1, 0xcb, 0x0a, 0xb3, - 0x22, 0xa7, 0xf7, 0xa9, 0x1d, 0xbb, 0xb3, 0xd7, 0xfa, 0xfa, 0x1b, 0x1f, - 0x0b, 0x3e, 0xb1, 0x52, 0x97, 0x59, 0x9a, 0x51, 0x11, 0x3c, 0x88, 0x1c, - 0x47, 0xf8, 0x4c, 0xd5, 0x65, 0xb9, 0x45, 0xa9, 0x7d, 0xa7, 0x7d, 0xb4, - 0xf9, 0xcd, 0xce, 0xef, 0x51, 0x14, 0x74, 0x35, 0xb5, 0x4d, 0x0d, 0x59, - 0x9c, 0x55, 0xe9, 0x43, 0xfc, 0x26, 0x82, 0x03, 0x85, 0xdf, 0xe2, 0xc0, - 0xbf, 0xac, 0x73, 0xa6, 0x04, 0xaf, 0x0f, 0xc5, 0xe2, 0xe4, 0x35, 0x09, - 0x04, 0x2c, 0x80, 0x47, 0x1b, 0x57, 0x42, 0x58, 0xb3, 0x4a, 0xca, 0x30, - 0xb7, 0x0e, 0x3e, 0xea, 0x5b, 0xc9, 0x90, 0xb1, 0xca, 0xa6, 0xdb, 0xaa, - 0x0f, 0xbd, 0x62, 0xda, 0xf8, 0xfd, 0xdf, 0x21, 0x2c, 0x40, 0xc9, 0x53, - 0x81, 0x59, 0x52, 0x50, 0xd3, 0x39, 0xaf, 0x19, 0x50, 0xf5, 0xb0, 0xd2, - 0x9e, 0xb7, 0x8c, 0xa8, 0x08, 0xa8, 0x1d, 0xb6, 0x7c, 0xd0, 0xc0, 0xf2, - 0x35, 0x17, 0xd0, 0x37, 0x2a, 0x4f, 0x50, 0x59, 0xb2, 0x54, 0xec, 0x41, - 0x45, 0x24, 0x8a, 0x00, 0xbf, 0xdc, 0xcd, 0xbe, 0xb2, 0xab, 0x96, 0xa6, - 0x59, 0xb0, 0x55, 0xc7, 0xbb, 0xe7, 0x31, 0x0c, 0x90, 0x2e, 0x4a, 0x49, - 0xbc, 0x57, 0xb2, 0x57, 0x04, 0x49, 0x42, 0x2e, 0xc4, 0x0b, 0x5d, 0xe7, - 0x04, 0xc7, 0x2a, 0xb0, 0x90, 0xa6, 0xd6, 0xab, 0x11, 0xbf, 0x1f, 0xdd, - 0xf1, 0x00, 0xa0, 0x24, 0x36, 0x42, 0xce, 0x54, 0x4c, 0x59, 0xf7, 0x4e, - 0x81, 0x37, 0xd3, 0x16, 0x58, 0xf2, 0x27, 0xd0, 0xe2, 0xb5, 0xf6, 0xa7, - 0xa3, 0xa8, 0xd8, 0xb7, 0x0c, 0xd3, 0xb1, 0xf5, 0x17, 0x1a, 0x1d, 0x3a, - 0x81, 0x50, 0x85, 0x59, 0xa4, 0x53, 0xe5, 0x3f, 0x80, 0x21, 0x92, 0xfd, - 0x04, 0xda, 0xca, 0xbc, 0xbe, 0xaa, 0xd0, 0xa6, 0xc7, 0xb1, 0xa8, 0xc9, - 0xa2, 0xea, 0x1f, 0x0f, 0x1c, 0x31, 0xf0, 0x4a, 0x4f, 0x58, 0x05, 0x57, - 0x42, 0x47, 0xaa, 0x2b, 0xd1, 0x08, 0x7e, 0xe4, 0xc2, 0xc4, 0xd9, 0xae, - 0x71, 0xa6, 0xe2, 0xac, 0x30, 0xc1, 0xdd, 0xdf, 0xf1, 0x03, 0x51, 0x27, - 0x30, 0x44, 0xb8, 0x55, 0x02, 0x59, 0x82, 0x4d, 0x22, 0x35, 0xec, 0x13, - 0x69, 0xef, 0xa6, 0xcd, 0x43, 0xb4, 0x70, 0xa7, 0x5e, 0xa9, 0xa4, 0xb9, - 0xa7, 0xd5, 0xac, 0xf8, 0xea, 0x1c, 0x5d, 0x3c, 0xc4, 0x51, 0x99, 0x59, - 0x88, 0x52, 0xc3, 0x3d, 0xb9, 0x1e, 0x94, 0xfa, 0x59, 0xd7, 0xd9, 0xba, - 0xdf, 0xa9, 0x2a, 0xa7, 0x43, 0xb3, 0x12, 0xcc, 0x89, 0xed, 0x0e, 0x12, - 0x94, 0x33, 0x87, 0x4c, 0xc6, 0x58, 0x3f, 0x56, 0x6c, 0x45, 0x07, 0x29, - 0xd9, 0x05, 0xaa, 0xe1, 0x8f, 0xc2, 0xa0, 0xad, 0x67, 0xa6, 0x10, 0xae, - 0x53, 0xc3, 0xb0, 0xe2, 0xe8, 0x06, 0xfb, 0x29, 0x17, 0x46, 0x88, 0x56, - 0x9e, 0x58, 0xfa, 0x4b, 0xaf, 0x32, 0x07, 0x11, 0x77, 0xec, 0x39, 0xcb, - 0xb4, 0xb2, 0x09, 0xa7, 0x2b, 0xaa, 0x89, 0xbb, 0x4d, 0xd8, 0xa5, 0xfb, - 0xbb, 0x1f, 0x86, 0x3e, 0xf4, 0x52, 0x91, 0x59, 0x56, 0x51, 0x8e, 0x3b, - 0xe9, 0x1b, 0x9b, 0xf7, 0xb4, 0xd4, 0x00, 0xb9, 0x15, 0xa9, 0xa0, 0xa7, - 0xd2, 0xb4, 0x8d, 0xce, 0x74, 0xf0, 0xf8, 0x14, 0xfe, 0x35, 0x08, 0x4e, - 0x22, 0x59, 0x64, 0x55, 0x80, 0x43, 0x59, 0x26, 0xe0, 0x02, 0xde, 0xde, - 0x6c, 0xc0, 0x7f, 0xac, 0x7a, 0xa6, 0x4f, 0xaf, 0x8e, 0xc5, 0x87, 0xe5, - 0xdc, 0x09, 0x9d, 0x2c, 0xe4, 0x47, 0x45, 0x57, 0x20, 0x58, 0x58, 0x4a, - 0x36, 0x30, 0x12, 0x0e, 0x96, 0xe9, 0xd3, 0xc8, 0x3e, 0xb1, 0xbd, 0xa6, - 0x0e, 0xab, 0x83, 0xbd, 0xfd, 0xda, 0xa1, 0xfe, 0x82, 0x22, 0x9e, 0x40, - 0x0a, 0x54, 0x73, 0x59, 0x09, 0x50, 0x4e, 0x39, 0x0c, 0x19, 0xa5, 0xf4, - 0x1e, 0xd2, 0x38, 0xb7, 0x69, 0xa8, 0x28, 0xa8, 0x7e, 0xb6, 0x0f, 0xd1, - 0x68, 0xf3, 0xdb, 0x17, 0x57, 0x38, 0x75, 0x4f, 0x62, 0x59, 0x75, 0x54, - 0x7a, 0x41, 0xa7, 0x23, 0xdf, 0xff, 0x22, 0xdc, 0x58, 0xbe, 0x79, 0xab, - 0xa0, 0xa6, 0xa9, 0xb0, 0xdb, 0xc7, 0x60, 0xe8, 0xd8, 0x0c, 0x27, 0x2f, - 0xa5, 0x49, 0xe5, 0x57, 0x89, 0x57, 0xa3, 0x48, 0xae, 0x2d, 0x1a, 0x0b, - 0xb9, 0xe6, 0x80, 0xc6, 0xdd, 0xaf, 0x87, 0xa6, 0x10, 0xac, 0x88, 0xbf, - 0xbe, 0xdd, 0x9a, 0x01, 0x3f, 0x25, 0xa6, 0x42, 0x06, 0x55, 0x3e, 0x59, - 0xa4, 0x4e, 0xfc, 0x36, 0x2a, 0x16, 0xb2, 0xf1, 0x95, 0xcf, 0x83, 0xb5, - 0xd6, 0xa7, 0xc9, 0xa8, 0x41, 0xb8, 0x9c, 0xd3, 0x60, 0xf6, 0xb7, 0x1a, - 0xa0, 0x3a, 0xce, 0x50, 0x87, 0x59, 0x6b, 0x53, 0x69, 0x3f, 0xe4, 0x20, - 0xe4, 0xfc, 0x6a, 0xd9, 0x5b, 0xbc, 0x86, 0xaa, 0xe7, 0xa6, 0x15, 0xb2, - 0x35, 0xca, 0x47, 0xeb, 0xc7, 0x0f, 0xac, 0x31, 0x4b, 0x4b, 0x6f, 0x58, - 0xd9, 0x56, 0xdb, 0x46, 0x13, 0x2b, 0x27, 0x08, 0xda, 0xe3, 0x45, 0xc4, - 0x8e, 0xae, 0x6d, 0xa6, 0x26, 0xad, 0xa5, 0xc1, 0x83, 0xe0, 0x97, 0x04, - 0xee, 0x27, 0x9d, 0x44, 0xea, 0x55, 0xed, 0x58, 0x2c, 0x4d, 0x97, 0x34, - 0x47, 0x13, 0xbe, 0xee, 0x1d, 0xcd, 0xe2, 0xb3, 0x5d, 0xa7, 0x84, 0xa9, - 0x13, 0xba, 0x3d, 0xd6, 0x56, 0xf9, 0x8d, 0x1d, 0xda, 0x3c, 0x0a, 0x52, - 0x9a, 0x59, 0x45, 0x52, 0x46, 0x3d, 0x18, 0x1e, 0xea, 0xf9, 0xbf, 0xd6, - 0x6e, 0xba, 0xaf, 0xa9, 0x42, 0xa7, 0x9d, 0xb3, 0x9b, 0xcc, 0x34, 0xee, - 0xb3, 0x12, 0x20, 0x34, 0xe1, 0x4c, 0xdb, 0x58, 0x12, 0x56, 0xfd, 0x44, - 0x71, 0x28, 0x2b, 0x05, 0x0d, 0xe1, 0x0f, 0xc2, 0x5f, 0xad, 0x6a, 0xa6, - 0x53, 0xae, 0xd4, 0xc3, 0x50, 0xe3, 0x94, 0x07, 0x92, 0x2a, 0x80, 0x46, - 0xb3, 0x56, 0x87, 0x58, 0x9a, 0x4b, 0x27, 0x32, 0x5a, 0x10, 0xd4, 0xeb, - 0xad, 0xca, 0x5d, 0xb2, 0xf8, 0xa6, 0x5a, 0xaa, 0xfb, 0xbb, 0xe4, 0xd8, - 0x53, 0xfc, 0x59, 0x20, 0x01, 0x3f, 0x33, 0x53, 0x8d, 0x59, 0x0e, 0x51, - 0x0e, 0x3b, 0x45, 0x1b, 0xf1, 0xf6, 0x1e, 0xd4, 0x99, 0xb8, 0xeb, 0xa8, - 0xbd, 0xa7, 0x30, 0xb5, 0x1c, 0xcf, 0x1c, 0xf1, 0xa0, 0x15, 0x83, 0x36, - 0x5f, 0x4e, 0x30, 0x59, 0x33, 0x55, 0x0c, 0x43, 0xbf, 0x25, 0x33, 0x02, - 0x42, 0xde, 0xf2, 0xbf, 0x43, 0xac, 0x80, 0xa6, 0x99, 0xaf, 0x14, 0xc6, - 0x26, 0xe6, 0x8b, 0x0a, 0x2e, 0x2d, 0x4b, 0x48, 0x6b, 0x57, 0x01, 0x58, - 0xf6, 0x49, 0xa8, 0x2f, 0x67, 0x0d, 0xf0, 0xe8, 0x50, 0xc8, 0xe8, 0xb0, - 0xb1, 0xa6, 0x46, 0xab, 0xf4, 0xbd, 0x9b, 0xdb, 0x4d, 0xff, 0x1c, 0x23, - 0x19, 0x41, 0x41, 0x54, 0x6b, 0x59, 0xbb, 0x4f, 0xc8, 0x38, 0x6b, 0x18, - 0xf8, 0xf3, 0x8e, 0xd1, 0xd2, 0xb6, 0x47, 0xa8, 0x49, 0xa8, 0xe3, 0xb6, - 0x9e, 0xd1, 0x15, 0xf4, 0x7c, 0x18, 0xdf, 0x38, 0xc1, 0x4f, 0x70, 0x59, - 0x39, 0x54, 0x06, 0x41, 0x0a, 0x23, 0x31, 0xff, 0x89, 0xdb, 0xe1, 0xbd, - 0x42, 0xab, 0xae, 0xa6, 0xf8, 0xb0, 0x60, 0xc8, 0x07, 0xe9, 0x80, 0x0d, - 0xba, 0x2f, 0x05, 0x4a, 0x05, 0x58, 0x65, 0x57, 0x3f, 0x48, 0x17, 0x2d, - 0x75, 0x0a, 0x0f, 0xe6, 0x03, 0xc6, 0x8d, 0xaf, 0x80, 0xa6, 0x4b, 0xac, - 0x01, 0xc0, 0x5c, 0xde, 0x46, 0x02, 0xd9, 0x25, 0x19, 0x43, 0x3b, 0x55, - 0x2e, 0x59, 0x52, 0x4e, 0x74, 0x36, 0x84, 0x15, 0x0a, 0xf1, 0x04, 0xcf, - 0x25, 0xb5, 0xb8, 0xa7, 0xf2, 0xa8, 0xa6, 0xb8, 0x34, 0xd4, 0x07, 0xf7, - 0x5c, 0x1b, 0x21, 0x3b, 0x17, 0x51, 0x8e, 0x59, 0x2b, 0x53, 0xee, 0x3e, - 0x46, 0x20, 0x39, 0xfc, 0xd0, 0xd8, 0xec, 0xbb, 0x51, 0xaa, 0xfc, 0xa6, - 0x69, 0xb2, 0xbe, 0xca, 0xee, 0xeb, 0x70, 0x10, 0x3a, 0x32, 0xa9, 0x4b, - 0x86, 0x58, 0xb1, 0x56, 0x70, 0x46, 0x7f, 0x2a, 0x7a, 0x07, 0x3a, 0xe3, - 0xc3, 0xc3, 0x49, 0xae, 0x6b, 0xa6, 0x64, 0xad, 0x25, 0xc2, 0x1f, 0xe1, - 0x47, 0x05, 0x83, 0x28, 0x0d, 0x45, 0x18, 0x56, 0xda, 0x58, 0xd2, 0x4c, - 0x0e, 0x34, 0x9d, 0x12, 0x19, 0xee, 0x8e, 0xcc, 0x8a, 0xb3, 0x43, 0xa7, - 0xb2, 0xa9, 0x7f, 0xba, 0xd5, 0xd6, 0xff, 0xf9, 0x31, 0x1e, 0x55, 0x3d, - 0x4f, 0x52, 0x9b, 0x59, 0xfe, 0x51, 0xcc, 0x3c, 0x73, 0x1d, 0x43, 0xf9, - 0x23, 0xd6, 0x07, 0xba, 0x7d, 0xa9, 0x60, 0xa7, 0xf0, 0xb3, 0x30, 0xcd, - 0xd4, 0xee, 0x5f, 0x13, 0xab, 0x34, 0x36, 0x4d, 0xf2, 0x58, 0xe2, 0x55, - 0x8d, 0x44, 0xdb, 0x27, 0x7e, 0x04, 0x6d, 0xe0, 0x93, 0xc1, 0x20, 0xad, - 0x68, 0xa6, 0xa0, 0xae, 0x4e, 0xc4, 0xf6, 0xe3, 0x3d, 0x08, 0x28, 0x2b, - 0xea, 0x46, 0xdf, 0x56, 0x69, 0x58, 0x41, 0x4b, 0x96, 0x31, 0xb1, 0x0f, - 0x2f, 0xeb, 0x23, 0xca, 0x09, 0xb2, 0xe4, 0xa6, 0x8d, 0xaa, 0x6b, 0xbc, - 0x7f, 0xd9, 0xfd, 0xfc, 0xf9, 0x20, 0x7a, 0x3f, 0x72, 0x53, 0x8a, 0x59, - 0xbf, 0x50, 0x92, 0x3a, 0x9e, 0x1a, 0x49, 0xf6, 0x88, 0xd3, 0x31, 0xb8, - 0xc6, 0xa8, 0xd8, 0xa7, 0x93, 0xb5, 0xa6, 0xcf, 0xcb, 0xf1, 0x40, 0x16, - 0x11, 0x37, 0xad, 0x4e, 0x42, 0x59, 0xfc, 0x54, 0x99, 0x42, 0x27, 0x25, - 0x85, 0x01, 0xa5, 0xdd, 0x79, 0xbf, 0x09, 0xac, 0x86, 0xa6, 0xea, 0xaf, - 0x91, 0xc6, 0xce, 0xe6, 0x35, 0x0b, 0xbf, 0x2d, 0xb3, 0x48, 0x8e, 0x57, - 0xe0, 0x57, 0x96, 0x49, 0x16, 0x2f, 0xbd, 0x0c, 0x4d, 0xe8, 0xc6, 0xc7, - 0x9d, 0xb0, 0xa1, 0xa6, 0x7f, 0xab, 0x69, 0xbe, 0x37, 0xdc, 0xf7, 0xff, - 0xbc, 0x23, 0x8b, 0x41, 0x7d, 0x54, 0x5e, 0x59, 0x6e, 0x4f, 0x41, 0x38, - 0xc7, 0x17, 0x50, 0xf3, 0xf7, 0xd0, 0x76, 0xb6, 0x1f, 0xa8, 0x72, 0xa8, - 0x42, 0xb7, 0x35, 0xd2, 0xba, 0xf4, 0x26, 0x19, 0x5e, 0x39, 0x13, 0x50, - 0x77, 0x59, 0xff, 0x53, 0x90, 0x40, 0x6b, 0x22, 0x88, 0xfe, 0xeb, 0xda, - 0x6f, 0xbd, 0x0a, 0xab, 0xbd, 0xa6, 0x49, 0xb1, 0xe7, 0xc8, 0xad, 0xe9, - 0x29, 0x0e, 0x49, 0x30, 0x66, 0x4a, 0x24, 0x58, 0x40, 0x57, 0xd8, 0x47, - 0x83, 0x2c, 0xca, 0x09, 0x6b, 0xe5, 0x82, 0xc5, 0x41, 0xaf, 0x7b, 0xa6, - 0x86, 0xac, 0x7c, 0xc0, 0xf7, 0xde, 0xf5, 0x02, 0x71, 0x26, 0x8d, 0x43, - 0x6d, 0x55, 0x1e, 0x59, 0xfd, 0x4d, 0xec, 0x35, 0xdf, 0x14, 0x60, 0xf0, - 0x75, 0xce, 0xc9, 0xb4, 0x99, 0xa7, 0x1e, 0xa9, 0x0b, 0xb9, 0xcc, 0xd4, - 0xb1, 0xf7, 0xff, 0x1b, 0xa1, 0x3b, 0x5f, 0x51, 0x92, 0x59, 0xeb, 0x52, - 0x75, 0x3e, 0xa4, 0x1f, 0x8f, 0xfb, 0x36, 0xd8, 0x7b, 0xbb, 0x23, 0xaa, - 0x0e, 0xa7, 0xbe, 0xb2, 0x4d, 0xcb, 0x8f, 0xec, 0x1d, 0x11, 0xc3, 0x32, - 0x07, 0x4c, 0xa0, 0x58, 0x85, 0x56, 0x06, 0x46, 0xe6, 0x29, 0xd1, 0x06, - 0x98, 0xe2, 0x44, 0xc3, 0x05, 0xae, 0x66, 0xa6, 0xac, 0xad, 0x9d, 0xc2, - 0xc4, 0xe1, 0xec, 0x05, 0x22, 0x29, 0x73, 0x45, 0x4d, 0x56, 0xbf, 0x58, - 0x7b, 0x4c, 0x82, 0x33, 0xf5, 0x11, 0x72, 0xed, 0x00, 0xcc, 0x36, 0xb3, - 0x26, 0xa7, 0xe8, 0xa9, 0xe4, 0xba, 0x72, 0xd7, 0xa9, 0xfa, 0xd1, 0x1e, - 0xd2, 0x3d, 0x93, 0x52, 0x97, 0x59, 0xbc, 0x51, 0x4a, 0x3c, 0xd4, 0x1c, - 0x95, 0xf8, 0x90, 0xd5, 0x98, 0xb9, 0x56, 0xa9, 0x74, 0xa7, 0x51, 0xb4, - 0xb7, 0xcd, 0x82, 0xef, 0x03, 0x14, 0x35, 0x35, 0x8e, 0x4d, 0x04, 0x59, - 0xb2, 0x55, 0x1f, 0x44, 0x3e, 0x27, 0xd7, 0x03, 0xc8, 0xdf, 0x1f, 0xc1, - 0xd9, 0xac, 0x72, 0xa6, 0xe3, 0xae, 0xd2, 0xc4, 0x97, 0xe4, 0xe8, 0x08, - 0xbe, 0x2b, 0x52, 0x47, 0x08, 0x57, 0x4c, 0x58, 0xe4, 0x4a, 0x07, 0x31, - 0x09, 0x0f, 0x88, 0xea, 0x9a, 0xc9, 0xb6, 0xb1, 0xd2, 0xa6, 0xc2, 0xaa, - 0xdb, 0xbc, 0x1b, 0xda, 0xa7, 0xfd, 0x99, 0x21, 0xf2, 0x3f, 0xb0, 0x53, - 0x81, 0x59, 0x77, 0x50, 0x0e, 0x3a, 0xfb, 0x19, 0xa0, 0xf5, 0xf1, 0xd2, - 0xcf, 0xb7, 0x9a, 0xa8, 0xfd, 0xa7, 0xee, 0xb5, 0x3b, 0xd0, 0x70, 0xf2, - 0xea, 0x16, 0x92, 0x37, 0x05, 0x4f, 0x4a, 0x59, 0xcb, 0x54, 0x22, 0x42, - 0x8c, 0x24, 0xda, 0x00, 0x07, 0xdd, 0x03, 0xbf, 0xcd, 0xab, 0x90, 0xa6, - 0x37, 0xb0, 0x16, 0xc7, 0x72, 0xe7, 0xde, 0x0b, 0x54, 0x2e, 0x13, 0x49, - 0xb6, 0x57, 0xb9, 0x57, 0x3a, 0x49, 0x7e, 0x2e, 0x18, 0x0c, 0xa4, 0xe7, - 0x44, 0xc7, 0x4d, 0xb0, 0x95, 0xa6, 0xba, 0xab, 0xdd, 0xbe, 0xd4, 0xdc, - 0xa4, 0x00, 0x57, 0x24, 0x00, 0x42, 0xb6, 0x54, 0x51, 0x59, 0x1d, 0x4f, - 0xbe, 0x37, 0x1f, 0x17, 0xa7, 0xf2, 0x69, 0xd0, 0x0e, 0xb6, 0x06, 0xa8, - 0x90, 0xa8, 0xab, 0xb7, 0xc6, 0xd2, 0x65, 0xf5, 0xc9, 0x19, 0xe4, 0x39, - 0x5b, 0x50, 0x84, 0x59, 0xbf, 0x53, 0x1c, 0x40, 0xca, 0x21, 0xe0, 0xfd, - 0x4b, 0xda, 0x01, 0xbd, 0xd2, 0xaa, 0xcd, 0xa6, 0x9d, 0xb1, 0x6c, 0xc9, - 0x53, 0xea, 0xd4, 0x0e, 0xd6, 0x30, 0xca, 0x4a, 0x3d, 0x58, 0x1a, 0x57, - 0x71, 0x47, 0xee, 0x2b, 0x21, 0x09, 0xc8, 0xe4, 0xfe, 0xc4, 0xfa, 0xae, - 0x72, 0xa6, 0xc8, 0xac, 0xf3, 0xc0, 0x98, 0xdf, 0xa0, 0x03, 0x0a, 0x27, - 0xff, 0x43, 0x9d, 0x55, 0x0f, 0x59, 0xa6, 0x4d, 0x64, 0x35, 0x37, 0x14, - 0xb8, 0xef, 0xe7, 0xcd, 0x6c, 0xb4, 0x7f, 0xa7, 0x46, 0xa9, 0x76, 0xb9, - 0x61, 0xd5, 0x5c, 0xf8, 0xa3, 0x1c, 0x1f, 0x3c, 0xa6, 0x51, 0x96, 0x59, - 0xa8, 0x52, 0xfb, 0x3d, 0x03, 0x1f, 0xe5, 0xfa, 0x9b, 0xd7, 0x0f, 0xbb, - 0xf2, 0xa9, 0x21, 0xa7, 0x1b, 0xb3, 0xd0, 0xcb, 0x3d, 0xed, 0xc0, 0x11, - 0x55, 0x33, 0x5d, 0x4c, 0xba, 0x58, 0x57, 0x56, 0x9a, 0x45, 0x51, 0x29, - 0x24, 0x06, 0xf9, 0xe1, 0xc3, 0xc2, 0xc3, 0xad, 0x66, 0xa6, 0xee, 0xad, - 0x1e, 0xc3, 0x60, 0xe2, 0x9d, 0x06, 0xb5, 0x29, 0xe3, 0x45, 0x77, 0x56, - 0xa8, 0x58, 0x22, 0x4c, 0xf5, 0x32, 0x4d, 0x11, 0xcc, 0xec, 0x73, 0xcb, - 0xdf, 0xb2, 0x11, 0xa7, 0x17, 0xaa, 0x53, 0xbb, 0x0a, 0xd8, 0x53, 0xfb, - 0x74, 0x1f, 0x4c, 0x3e, 0xd6, 0x52, 0x94, 0x59, 0x75, 0x51, 0xcb, 0x3b, - 0x32, 0x1c, 0xea, 0xf7, 0xfa, 0xd4, 0x2f, 0xb9, 0x2a, 0xa9, 0x90, 0xa7, - 0xac, 0xb4, 0x46, 0xce, 0x2b, 0xf0, 0xa8, 0x14, 0xc1, 0x35, 0xe0, 0x4d, - 0x1a, 0x59, 0x7d, 0x55, 0xb1, 0x43, 0xa3, 0x26, 0x2b, 0x03, 0x2c, 0xdf, - 0xa0, 0xc0, 0x9e, 0xac, 0x75, 0xa6, 0x2e, 0xaf, 0x52, 0xc5, 0x3b, 0xe5, - 0x91, 0x09, 0x54, 0x2c, 0xb9, 0x47, 0x30, 0x57, 0x2f, 0x58, 0x84, 0x4a, - 0x78, 0x30, 0x60, 0x0e, 0xe2, 0xe9, 0x13, 0xc9, 0x61, 0xb1, 0xc5, 0xa6, - 0xf5, 0xaa, 0x4f, 0xbd, 0xb4, 0xda, 0x55, 0xfe, 0x35, 0x22, 0x6c, 0x40, - 0xea, 0x53, 0x7c, 0x59, 0x29, 0x50, 0x8b, 0x39, 0x58, 0x19, 0xf4, 0xf4, - 0x61, 0xd2, 0x66, 0xb7, 0x7a, 0xa8, 0x17, 0xa8, 0x54, 0xb6, 0xca, 0xd0, - 0x1a, 0xf3, 0x8f, 0x17, 0x1a, 0x38, 0x50, 0x4f, 0x5f, 0x59, 0x8a, 0x54, - 0xb6, 0x41, 0xea, 0x23, 0x31, 0x00, 0x69, 0xdc, 0x8e, 0xbe, 0x93, 0xab, - 0x9c, 0xa6, 0x83, 0xb0, 0x9d, 0xc7, 0x15, 0xe8, 0x8a, 0x0c, 0xe4, 0x2e, - 0x78, 0x49, 0xd4, 0x57, 0x9c, 0x57, 0xd0, 0x48, 0xf1, 0x2d, 0x6a, 0x0b, - 0x02, 0xe7, 0xbf, 0xc6, 0xff, 0xaf, 0x8d, 0xa6, 0xf1, 0xab, 0x56, 0xbf, - 0x70, 0xdd, 0x51, 0x01, 0xf2, 0x24, 0x75, 0x42, 0xea, 0x54, 0x47, 0x59, - 0xc9, 0x4e, 0x39, 0x37, 0x78, 0x16, 0x00, 0xf2, 0xd6, 0xcf, 0xb1, 0xb5, - 0xe2, 0xa7, 0xb8, 0xa8, 0x12, 0xb8, 0x58, 0xd3, 0x12, 0xf6, 0x6a, 0x1a, - 0x66, 0x3a, 0xa9, 0x50, 0x88, 0x59, 0x85, 0x53, 0xa1, 0x3f, 0x2e, 0x21, - 0x31, 0xfd, 0xb4, 0xd9, 0x8c, 0xbc, 0xa0, 0xaa, 0xde, 0xa6, 0xef, 0xb1, - 0xf4, 0xc9, 0xfc, 0xea, 0x78, 0x0f, 0x6c, 0x31, 0x21, 0x4b, 0x5f, 0x58, - 0xee, 0x56, 0x0a, 0x47, 0x5a, 0x2b, 0x74, 0x08, 0x28, 0xe4, 0x7a, 0xc4, - 0xb4, 0xae, 0x6c, 0xa6, 0x0a, 0xad, 0x6c, 0xc1, 0x38, 0xe0, 0x4a, 0x04, - 0xa5, 0x27, 0x6e, 0x44, 0xcf, 0x55, 0xfb, 0x58, 0x4f, 0x4d, 0xdb, 0x34, - 0x90, 0x13, 0x10, 0xef, 0x58, 0xcd, 0x12, 0xb4, 0x63, 0xa7, 0x73, 0xa9, - 0xe1, 0xb9, 0xf5, 0xd5, 0x0b, 0xf9, 0x40, 0x1d, 0xa2, 0x3c, 0xe8, 0x51, - 0x9b, 0x59, 0x63, 0x52, 0x81, 0x3d, 0x60, 0x1e, 0x3b, 0xfa, 0x02, 0xd7, - 0xa4, 0xba, 0xbf, 0xa9, 0x3d, 0xa7, 0x6d, 0xb3, 0x62, 0xcc, 0xe1, 0xed, - 0x69, 0x12, 0xe0, 0x33, 0xb6, 0x4c, 0xd3, 0x58, 0x27, 0x56, 0x2f, 0x45, - 0xb8, 0x28, 0x79, 0x05, 0x57, 0xe1, 0x48, 0xc2, 0x7f, 0xad, 0x66, 0xa6, - 0x37, 0xae, 0x96, 0xc3, 0x08, 0xe3, 0x43, 0x07, 0x4e, 0x2a, 0x4f, 0x46, - 0xa1, 0x56, 0x90, 0x58, 0xc8, 0x4b, 0x65, 0x32, 0xa8, 0x10, 0x21, 0xec, - 0xee, 0xca, 0x82, 0xb2, 0x04, 0xa7, 0x40, 0xaa, 0xc9, 0xbb, 0x9e, 0xd8, - 0x02, 0xfc, 0x10, 0x20, 0xcb, 0x3e, 0x13, 0x53, 0x93, 0x59, 0x2b, 0x51, - 0x4c, 0x3b, 0x8f, 0x1b, 0x3f, 0xf7, 0x65, 0xd4, 0xc6, 0xb8, 0x00, 0xa9, - 0xad, 0xa7, 0x08, 0xb5, 0xd7, 0xce, 0xd1, 0xf0, 0x51, 0x15, 0x47, 0x36, - 0x37, 0x4e, 0x29, 0x59, 0x4b, 0x55, 0x3f, 0x43, 0x0a, 0x26, 0x7e, 0x02, - 0x8e, 0xde, 0x26, 0xc0, 0x62, 0xac, 0x7b, 0xa6, 0x77, 0xaf, 0xd7, 0xc5, - 0xda, 0xe5, 0x40, 0x0a, 0xe6, 0x2c, 0x20, 0x48, 0x56, 0x57, 0x11, 0x58, - 0x24, 0x4a, 0xe8, 0x2f, 0xb7, 0x0d, 0x3b, 0xe9, 0x8d, 0xc8, 0x0f, 0xb1, - 0xb7, 0xa6, 0x2b, 0xab, 0xc1, 0xbd, 0x51, 0xdb, 0xff, 0xfe, 0xd4, 0x22, - 0xe3, 0x40, 0x24, 0x54, 0x72, 0x59, 0xde, 0x4f, 0x05, 0x39, 0xb7, 0x18, - 0x46, 0xf4, 0xd1, 0xd1, 0x00, 0xb7, 0x57, 0xa8, 0x39, 0xa8, 0xb6, 0xb6, - 0x5c, 0xd1, 0xc3, 0xf3, 0x35, 0x18, 0x9d, 0x38, 0xa2, 0x4f, 0x67, 0x59, - 0x55, 0x54, 0x3d, 0x41, 0x50, 0x23, 0x84, 0xff, 0xcc, 0xdb, 0x1a, 0xbe, - 0x59, 0xab, 0xab, 0xa6, 0xd1, 0xb0, 0x23, 0xc8, 0xba, 0xe8, 0x33, 0x0d, - 0x76, 0x2f, 0xda, 0x49, 0xf5, 0x57, 0x77, 0x57, 0x6d, 0x48, 0x5c, 0x2d, - 0xc1, 0x0a, 0x5d, 0xe6, 0x3b, 0xc6, 0xb5, 0xaf, 0x80, 0xa6, 0x31, 0xac, - 0xca, 0xbf, 0x11, 0xde, 0xf8, 0x01, 0x92, 0x25, 0xe4, 0x42, 0x24, 0x55, - 0x34, 0x59, 0x79, 0x4e, 0xb0, 0x36, 0xd4, 0x15, 0x55, 0xf1, 0x47, 0xcf, - 0x52, 0xb5, 0xc3, 0xa7, 0xe1, 0xa8, 0x75, 0xb8, 0xf1, 0xd3, 0xb7, 0xf6, - 0x14, 0x1b, 0xe2, 0x3a, 0xf7, 0x50, 0x8c, 0x59, 0x46, 0x53, 0x2a, 0x3f, - 0x8b, 0x20, 0x8a, 0xfc, 0x17, 0xd9, 0x1e, 0xbc, 0x6a, 0xaa, 0xf1, 0xa6, - 0x43, 0xb2, 0x7f, 0xca, 0xa1, 0xeb, 0x22, 0x10, 0xf8, 0x31, 0x7f, 0x4b, - 0x7c, 0x58, 0xc2, 0x56, 0xa3, 0x46, 0xc2, 0x2a, 0xca, 0x07, 0x86, 0xe3, - 0xfa, 0xc3, 0x6d, 0xae, 0x69, 0xa6, 0x49, 0xad, 0xea, 0xc1, 0xd6, 0xe0, - 0xf6, 0x04, 0x3f, 0x28, 0xd9, 0x44, 0x04, 0x56, 0xe2, 0x58, 0xfb, 0x4c, - 0x4f, 0x34, 0xe8, 0x12, 0x6a, 0xee, 0xca, 0xcc, 0xb7, 0xb3, 0x4c, 0xa7, - 0x9d, 0xa9, 0x50, 0xba, 0x8a, 0xd6, 0xb7, 0xf9, 0xe0, 0x1d, 0x20, 0x3d, - 0x2e, 0x52, 0x9b, 0x59, 0x1f, 0x52, 0x05, 0x3d, 0xbc, 0x1d, 0x93, 0xf9, - 0x68, 0xd6, 0x3a, 0xba, 0x90, 0xa9, 0x55, 0xa7, 0xc7, 0xb3, 0xec, 0xcc, - 0x8d, 0xee, 0x0c, 0x13, 0x6e, 0x34, 0x0e, 0x4d, 0xe7, 0x58, 0xf9, 0x55, - 0xc2, 0x44, 0x1c, 0x28, 0xd2, 0x04, 0xb4, 0xe0, 0xce, 0xc1, 0x3a, 0xad, - 0x6c, 0xa6, 0x78, 0xae, 0x1c, 0xc4, 0xa5, 0xe3, 0xf1, 0x07, 0xe2, 0x2a, - 0xba, 0x46, 0xcb, 0x56, 0x78, 0x58, 0x68, 0x4b, 0xdb, 0x31, 0xfd, 0x0f, - 0x7c, 0xeb, 0x62, 0xca, 0x2f, 0xb2, 0xef, 0xa6, 0x73, 0xaa, 0x39, 0xbc, - 0x37, 0xd9, 0xae, 0xfc, 0xb1, 0x20, 0x41, 0x3f, 0x56, 0x53, 0x8b, 0x59, - 0xe4, 0x50, 0xca, 0x3a, 0xed, 0x1a, 0x93, 0xf6, 0xd1, 0xd3, 0x5d, 0xb8, - 0xda, 0xa8, 0xc9, 0xa7, 0x66, 0xb5, 0x67, 0xcf, 0x79, 0xf1, 0xf9, 0x15, - 0xce, 0x36, 0x89, 0x4e, 0x3c, 0x59, 0x12, 0x55, 0xd1, 0x42, 0x6c, 0x25, - 0xd5, 0x01, 0xed, 0xdd, 0xb3, 0xbf, 0x1f, 0xac, 0x88, 0xa6, 0xbf, 0xaf, - 0x5c, 0xc6, 0x7f, 0xe6, 0xe8, 0x0a, 0x7b, 0x2d, 0x83, 0x48, 0x7e, 0x57, - 0xf1, 0x57, 0xc2, 0x49, 0x58, 0x2f, 0x0d, 0x0d, 0x96, 0xe8, 0x06, 0xc8, - 0xc1, 0xb0, 0xa6, 0xa6, 0x67, 0xab, 0x31, 0xbe, 0xf0, 0xdb, 0xa9, 0xff, - 0x72, 0x23, 0x58, 0x41, 0x5f, 0x54, 0x67, 0x59, 0x8e, 0x4f, 0x83, 0x38, - 0x0f, 0x18, 0xa0, 0xf3, 0x3b, 0xd1, 0xa0, 0xb6, 0x32, 0xa8, 0x5e, 0xa8, - 0x17, 0xb7, 0xef, 0xd1, 0x6d, 0xf4, 0xda, 0x18, 0x21, 0x39, 0xf1, 0x4f, - 0x70, 0x59, 0x1b, 0x54, 0xc8, 0x40, 0xb2, 0x22, 0xd8, 0xfe, 0x32, 0xdb, - 0xa4, 0xbd, 0x24, 0xab, 0xb6, 0xa6, 0x22, 0xb1, 0xab, 0xc8, 0x5e, 0xe9, - 0xde, 0x0d, 0x05, 0x30, 0x3a, 0x4a, 0x18, 0x58, 0x4d, 0x57, 0x0b, 0x48, - 0xc6, 0x2c, 0x17, 0x0a, 0xbc, 0xe5, 0xb6, 0xc5, 0x6a, 0xaf, 0x79, 0xa6, - 0x6c, 0xac, 0x44, 0xc0, 0xaf, 0xde, 0xa4, 0x02, 0x2c, 0x26, 0x56, 0x43, - 0x57, 0x55, 0x26, 0x59, 0x24, 0x4e, 0x2a, 0x36, 0x2c, 0x15, 0xad, 0xf0, - 0xb7, 0xce, 0xf4, 0xb4, 0xa6, 0xa7, 0x0b, 0xa9, 0xda, 0xb8, 0x88, 0xd4, - 0x62, 0xf7, 0xb4, 0x1b, 0x67, 0x3b, 0x3d, 0x51, 0x90, 0x59, 0x0a, 0x53, - 0xac, 0x3e, 0xef, 0x1f, 0xdd, 0xfb, 0x7d, 0xd8, 0xae, 0xbb, 0x3b, 0xaa, - 0x01, 0xa7, 0x9c, 0xb2, 0x06, 0xcb, 0x48, 0xec, 0xcd, 0x10, 0x83, 0x32, - 0xde, 0x4b, 0x91, 0x58, 0x9c, 0x56, 0x35, 0x46, 0x2e, 0x2a, 0x1e, 0x07, - 0xe4, 0xe2, 0x7b, 0xc3, 0x28, 0xae, 0x65, 0xa6, 0x8e, 0xad, 0x63, 0xc2, - 0x79, 0xe1, 0xa0, 0x05, 0xd9, 0x28, 0x45, 0x45, 0x34, 0x56, 0xca, 0x58, - 0xa5, 0x4c, 0xc2, 0x33, 0x42, 0x12, 0xc0, 0xed, 0x3f, 0xcc, 0x5e, 0xb3, - 0x33, 0xa7, 0xce, 0xa9, 0xb7, 0xba, 0x28, 0xd7, 0x5c, 0xfa, 0x87, 0x1e, - 0x98, 0x3d, 0x76, 0x52, 0x95, 0x59, 0xdf, 0x51, 0x82, 0x3c, 0x21, 0x1d, - 0xe2, 0xf8, 0xd5, 0xd5, 0xcb, 0xb9, 0x67, 0xa9, 0x6c, 0xa7, 0x22, 0xb4, - 0x7a, 0xcd, 0x33, 0xef, 0xb6, 0x13, 0xf6, 0x34, 0x65, 0x4d, 0xfd, 0x58, - 0xc7, 0x55, 0x54, 0x44, 0x83, 0x27, 0x26, 0x04, 0x15, 0xe0, 0x51, 0xc1, - 0xfd, 0xac, 0x6b, 0xa6, 0xc3, 0xae, 0x99, 0xc4, 0x49, 0xe4, 0x9b, 0x08, - 0x7b, 0x2b, 0x1d, 0x47, 0xfb, 0x56, 0x56, 0x58, 0x0f, 0x4b, 0x4d, 0x31, - 0x50, 0x0f, 0xdb, 0xea, 0xd4, 0xc9, 0xde, 0xb1, 0xdc, 0xa6, 0xa7, 0xaa, - 0xa9, 0xbc, 0xd1, 0xd9, 0x5b, 0xfd, 0x4e, 0x21, 0xbc, 0x3f, 0x93, 0x53, - 0x84, 0x59, 0x9c, 0x50, 0x46, 0x3a, 0x4b, 0x1a, 0xe9, 0xf5, 0x3b, 0xd3, - 0xf8, 0xb7, 0xb2, 0xa8, 0xe8, 0xa7, 0xc7, 0xb5, 0xf5, 0xcf, 0x24, 0xf2, - 0x9d, 0x16, 0x56, 0x37, 0xdc, 0x4e, 0x49, 0x59, 0xdd, 0x54, 0x5e, 0x42, - 0xcf, 0x24, 0x2c, 0x01, 0x4d, 0xdd, 0x3c, 0xbf, 0xe6, 0xab, 0x8c, 0xa6, - 0x13, 0xb0, 0xd8, 0xc6, 0x29, 0xe7, 0x8e, 0x0b, 0x0f, 0x2e, 0xea, 0x48, - 0x9e, 0x57, 0xd2, 0x57, 0x5f, 0x49, 0xc7, 0x2e, 0x63, 0x0c, 0xf1, 0xe7, - 0x81, 0xc7, 0x71, 0xb0, 0x9b, 0xa6, 0x9e, 0xab, 0xa8, 0xbe, 0x8b, 0xdc, - 0x56, 0x00, 0x0d, 0x24, 0xce, 0x41, 0x98, 0x54, 0x5b, 0x59, 0x3f, 0x4f, - 0xfd, 0x37, 0x6a, 0x17, 0xf6, 0xf2, 0xab, 0xd0, 0x3d, 0xb6, 0x11, 0xa8, - 0x82, 0xa8, 0x7c, 0xb7, 0x81, 0xd2, 0x18, 0xf5, 0x7d, 0x19, 0xa7, 0x39, - 0x3b, 0x50, 0x7c, 0x59, 0xde, 0x53, 0x51, 0x40, 0x14, 0x22, 0x2e, 0xfe, - 0x93, 0xda, 0x35, 0xbd, 0xeb, 0xaa, 0xc5, 0xa6, 0x78, 0xb1, 0x2c, 0xc9, - 0x09, 0xea, 0x83, 0x0e, 0x98, 0x30, 0x9a, 0x4a, 0x33, 0x58, 0x2c, 0x57, - 0x9e, 0x47, 0x37, 0x2c, 0x6a, 0x09, 0x17, 0xe5, 0x38, 0xc5, 0x1c, 0xaf, - 0x76, 0xa6, 0xaa, 0xac, 0xbb, 0xc0, 0x4f, 0xdf, 0x4e, 0x03, 0xc9, 0x26, - 0xc5, 0x43, 0x8d, 0x55, 0x11, 0x59, 0xd1, 0x4d, 0xa1, 0x35, 0x85, 0x14, - 0x05, 0xf0, 0x28, 0xce, 0x98, 0xb4, 0x88, 0xa7, 0x36, 0xa9, 0x44, 0xb9, - 0x1b, 0xd5, 0x11, 0xf8, 0x51, 0x1c, 0xec, 0x3b, 0x80, 0x51, 0x9a, 0x59, - 0xc2, 0x52, 0x36, 0x3e, 0x4b, 0x1f, 0x34, 0xfb, 0xe3, 0xd7, 0x41, 0xbb, - 0x06, 0xaa, 0x1b, 0xa7, 0xed, 0xb2, 0x95, 0xcb, 0xee, 0xec, 0x74, 0x11, - 0x11, 0x33, 0x37, 0x4c, 0xad, 0x58, 0x6d, 0x56, 0xcc, 0x45, 0x95, 0x29, - 0x74, 0x06, 0x42, 0xe2, 0xfe, 0xc2, 0xe2, 0xad, 0x64, 0xa6, 0xd3, 0xad, - 0xde, 0xc2, 0x1c, 0xe2, 0x4a, 0x06, 0x70, 0x29, 0xb4, 0x45, 0x5e, 0x56, - 0xb7, 0x58, 0x49, 0x4c, 0x36, 0x33, 0x9d, 0x11, 0x15, 0xed, 0xb6, 0xcb, - 0x04, 0xb3, 0x1e, 0xa7, 0xfe, 0xa9, 0x23, 0xbb, 0xc1, 0xd7, 0x07, 0xfb, - 0x27, 0x1f, 0x16, 0x3e, 0xb6, 0x52, 0x95, 0x59, 0x98, 0x51, 0x03, 0x3c, - 0x7f, 0x1c, 0x37, 0xf8, 0x3f, 0xd5, 0x60, 0xb9, 0x3e, 0xa9, 0x83, 0xa7, - 0x82, 0xb4, 0x03, 0xce, 0xde, 0xef, 0x5d, 0x14, 0x7f, 0x35, 0xbc, 0x4d, - 0x10, 0x59, 0x92, 0x55, 0xe9, 0x43, 0xe7, 0x26, 0x7b, 0x03, 0x76, 0xdf, - 0xd5, 0xc0, 0xbf, 0xac, 0x6f, 0xa6, 0x0e, 0xaf, 0x17, 0xc5, 0xed, 0xe4, - 0x46, 0x09, 0x0d, 0x2c, 0x8c, 0x47, 0x1c, 0x57, 0x3f, 0x58, 0xad, 0x4a, - 0xbd, 0x30, 0xac, 0x0e, 0x2f, 0xea, 0x51, 0xc9, 0x88, 0xb1, 0xcc, 0xa6, - 0xdb, 0xaa, 0x1b, 0xbd, 0x6d, 0xda, 0x05, 0xfe, 0xee, 0x21, 0x33, 0x40, - 0xd0, 0x53, 0x7e, 0x59, 0x4e, 0x50, 0xc6, 0x39, 0xa5, 0x19, 0x40, 0xf5, - 0xa7, 0xd2, 0x92, 0xb7, 0x8d, 0xa8, 0x08, 0xa8, 0x25, 0xb6, 0x89, 0xd0, - 0xcc, 0xf2, 0x42, 0x17, 0xde, 0x37, 0x2a, 0x4f, 0x59, 0x59, 0xa5, 0x54, - 0xeb, 0x41, 0x32, 0x24, 0x81, 0x00, 0xb0, 0xdc, 0xc5, 0xbe, 0xac, 0xab, - 0x98, 0xa6, 0x5e, 0xb0, 0x61, 0xc7, 0xc8, 0xe7, 0x3d, 0x0c, 0x9f, 0x2e, - 0x4c, 0x49, 0xc5, 0x57, 0xaa, 0x57, 0x01, 0x49, 0x32, 0x2e, 0xba, 0x0b, - 0x4d, 0xe7, 0xfb, 0xc6, 0x25, 0xb0, 0x8c, 0xa6, 0xdd, 0xab, 0x19, 0xbf, - 0x2e, 0xdd, 0xfc, 0x00, 0xad, 0x24, 0x3f, 0x42, 0xd2, 0x54, 0x4c, 0x59, - 0xf0, 0x4e, 0x75, 0x37, 0xc6, 0x16, 0x4d, 0xf2, 0x18, 0xd0, 0xde, 0xb5, - 0xef, 0xa7, 0xaa, 0xa8, 0xde, 0xb7, 0x19, 0xd3, 0xbd, 0xf5, 0x25, 0x1a, - 0x27, 0x3a, 0x88, 0x50, 0x84, 0x59, 0xa0, 0x53, 0xdb, 0x3f, 0x74, 0x21, - 0x84, 0xfd, 0xf6, 0xd9, 0xc5, 0xbc, 0xb4, 0xaa, 0xd8, 0xa6, 0xc8, 0xb1, - 0xb6, 0xc9, 0xaf, 0xea, 0x2b, 0x0f, 0x28, 0x31, 0xf8, 0x4a, 0x51, 0x58, - 0x02, 0x57, 0x39, 0x47, 0xa0, 0x2b, 0xc0, 0x08, 0x76, 0xe4, 0xb3, 0xc4, - 0xd6, 0xae, 0x6f, 0xa6, 0xe9, 0xac, 0x38, 0xc1, 0xeb, 0xdf, 0xfe, 0x03, - 0x5c, 0x27, 0x3c, 0x44, 0xb8, 0x55, 0x04, 0x59, 0x79, 0x4d, 0x17, 0x35, - 0xe0, 0x13, 0x5a, 0xef, 0x9e, 0xcd, 0x36, 0xb4, 0x75, 0xa7, 0x5a, 0xa9, - 0xb2, 0xb9, 0xb1, 0xd5, 0xb8, 0xf8, 0xf9, 0x1c, 0x66, 0x3c, 0xc9, 0x51, - 0x9b, 0x59, 0x81, 0x52, 0xba, 0x3d, 0xab, 0x1e, 0x88, 0xfa, 0x4b, 0xd7, - 0xd3, 0xba, 0xd9, 0xa9, 0x2d, 0xa7, 0x49, 0xb3, 0x1e, 0xcc, 0x96, 0xed, - 0x1c, 0x12, 0x9d, 0x33, 0x90, 0x4c, 0xc6, 0x58, 0x3e, 0x56, 0x61, 0x45, - 0xfc, 0x28, 0xc9, 0x05, 0xa2, 0xe1, 0x80, 0xc2, 0x9f, 0xad, 0x63, 0xa6, - 0x19, 0xae, 0x5b, 0xc3, 0xbf, 0xe2, 0xf3, 0x06, 0x09, 0x2a, 0x1e, 0x46, - 0x8c, 0x56, 0x9e, 0x58, 0xee, 0x4b, 0xab, 0x32, 0xf2, 0x10, 0x70, 0xec, - 0x2b, 0xcb, 0xac, 0xb2, 0x0b, 0xa7, 0x2b, 0xaa, 0x95, 0xbb, 0x57, 0xd8, - 0xb4, 0xfb, 0xc6, 0x1f, 0x92, 0x3e, 0xf6, 0x52, 0x94, 0x59, 0x4e, 0x51, - 0x85, 0x3b, 0xdc, 0x1b, 0x8c, 0xf7, 0xaa, 0xd4, 0xf6, 0xb8, 0x15, 0xa9, - 0x9d, 0xa7, 0xe0, 0xb4, 0x92, 0xce, 0x85, 0xf0, 0x04, 0x15, 0x09, 0x36, - 0x0f, 0x4e, 0x24, 0x59, 0x5f, 0x55, 0x76, 0x43, 0x4f, 0x26, 0xcf, 0x02, - 0xd6, 0xde, 0x5f, 0xc0, 0x7c, 0xac, 0x78, 0xa6, 0x57, 0xaf, 0x98, 0xc5, - 0x93, 0xe5, 0xec, 0x09, 0xa6, 0x2c, 0xee, 0x47, 0x47, 0x57, 0x1e, 0x58, - 0x4f, 0x4a, 0x2e, 0x30, 0x00, 0x0e, 0x8d, 0xe9, 0xc6, 0xc8, 0x38, 0xb1, - 0xbb, 0xa6, 0x14, 0xab, 0x8a, 0xbd, 0x0b, 0xdb, 0xae, 0xfe, 0x8d, 0x22, - 0xab, 0x40, 0x0b, 0x54, 0x75, 0x59, 0x01, 0x50, 0x43, 0x39, 0x01, 0x19, - 0x97, 0xf4, 0x11, 0xd2, 0x31, 0xb7, 0x65, 0xa8, 0x2c, 0xa8, 0x85, 0xb6, - 0x1b, 0xd1, 0x74, 0xf3, 0xea, 0x17, 0x60, 0x38, 0x7d, 0x4f, 0x62, 0x59, - 0x6f, 0x54, 0x74, 0x41, 0x97, 0x23, 0xd5, 0xff, 0x11, 0xdc, 0x53, 0xbe, - 0x70, 0xab, 0xa6, 0xa6, 0xac, 0xb0, 0xe5, 0xc7, 0x70, 0xe8, 0xe3, 0x0c, - 0x33, 0x2f, 0xaf, 0x49, 0xe3, 0x57, 0x8c, 0x57, 0x97, 0x48, 0xa3, 0x2d, - 0x0e, 0x0b, 0xa9, 0xe6, 0x79, 0xc6, 0xd4, 0xaf, 0x89, 0xa6, 0x10, 0xac, - 0x98, 0xbf, 0xc4, 0xdd, 0xae, 0x01, 0x45, 0x25, 0xb5, 0x42, 0x05, 0x55, - 0x40, 0x59, 0x9d, 0x4e, 0xef, 0x36, 0x21, 0x16, 0xa1, 0xf1, 0x8b, 0xcf, - 0x7b, 0xb5, 0xd5, 0xa7, 0xc9, 0xa8, 0x4d, 0xb8, 0xa5, 0xd3, 0x70, 0xf6, - 0xc1, 0x1a, 0xad, 0x3a, 0xd1, 0x50, 0x8c, 0x59, 0x62, 0x53, 0x60, 0x3f, - 0xd9, 0x20, 0xd4, 0xfc, 0x61, 0xd9, 0x4f, 0xbc, 0x83, 0xaa, 0xe9, 0xa6, - 0x1b, 0xb2, 0x41, 0xca, 0x52, 0xeb, 0xd7, 0x0f, 0xb4, 0x31, 0x57, 0x4b, - 0x6d, 0x58, 0xd8, 0x56, 0xd2, 0x46, 0x07, 0x2b, 0x19, 0x08, 0xd0, 0xe3, - 0x36, 0xc4, 0x8e, 0xae, 0x68, 0xa6, 0x2e, 0xad, 0xad, 0xc1, 0x91, 0xe0, - 0xa4, 0x04, 0xfa, 0x27, 0xa6, 0x44, 0xee, 0x55, 0xea, 0x58, 0x28, 0x4d, - 0x88, 0x34, 0x3d, 0x13, 0xaf, 0xee, 0x11, 0xcd, 0xdd, 0xb3, 0x59, 0xa7, - 0x89, 0xa9, 0x1b, 0xba, 0x49, 0xd6, 0x62, 0xf9, 0x9d, 0x1d, 0xe1, 0x3c, - 0x12, 0x52, 0x99, 0x59, 0x3e, 0x52, 0x3f, 0x3d, 0x08, 0x1e, 0xdf, 0xf9, - 0xb2, 0xd6, 0x65, 0xba, 0xad, 0xa9, 0x42, 0xa7, 0xa4, 0xb3, 0xa8, 0xcc, - 0x3f, 0xee, 0xc3, 0x12, 0x2a, 0x34, 0xe7, 0x4c, 0xdd, 0x58, 0x0f, 0x56, - 0xf3, 0x44, 0x66, 0x28, 0x1d, 0x05, 0xff, 0xe0, 0x08, 0xc2, 0x56, 0xad, - 0x6d, 0xa6, 0x57, 0xae, 0xdf, 0xc3, 0x5e, 0xe3, 0x9f, 0x07, 0x9f, 0x2a, - 0x89, 0x46, 0xb6, 0x56, 0x87, 0x58, 0x90, 0x4b, 0x1e, 0x32, 0x49, 0x10, - 0xca, 0xeb, 0xa1, 0xca, 0x56, 0xb2, 0xf7, 0xa6, 0x5d, 0xaa, 0x04, 0xbc, - 0xf1, 0xd8, 0x5f, 0xfc, 0x67, 0x20, 0x0a, 0x3f, 0x38, 0x53, 0x8e, 0x59, - 0x06, 0x51, 0x06, 0x3b, 0x36, 0x1b, 0xe4, 0xf6, 0x14, 0xd4, 0x8e, 0xb8, - 0xeb, 0xa8, 0xbc, 0xa7, 0x3b, 0xb5, 0x24, 0xcf, 0x2d, 0xf1, 0xaa, 0x15, - 0x91, 0x36, 0x63, 0x4e, 0x33, 0x59, 0x2d, 0x55, 0x04, 0x43, 0xb3, 0x25, - 0x25, 0x02, 0x35, 0xde, 0xe9, 0xbf, 0x3e, 0xac, 0x80, 0xa6, 0xa1, 0xaf, - 0x1c, 0xc6, 0x35, 0xe6, 0x99, 0x0a, 0x37, 0x2d, 0x55, 0x48, 0x6f, 0x57, - 0xfb, 0x57, 0xf4, 0x49, 0x96, 0x2f, 0x5e, 0x0d, 0xe1, 0xe8, 0x44, 0xc8, - 0xe6, 0xb0, 0xac, 0xa6, 0x4c, 0xab, 0xfe, 0xbd, 0xa5, 0xdb, 0x5d, 0xff, - 0x28, 0x23, 0x21, 0x41, 0x47, 0x54, 0x6a, 0x59, 0xb4, 0x4f, 0xbf, 0x38, - 0x5b, 0x18, 0xef, 0xf3, 0x7d, 0xd1, 0xcf, 0xb6, 0x41, 0xa8, 0x4e, 0xa8, - 0xea, 0xb6, 0xaa, 0xd1, 0x21, 0xf4, 0x8c, 0x18, 0xe6, 0x38, 0xcc, 0x4f, - 0x6c, 0x59, 0x36, 0x54, 0xfe, 0x40, 0xf9, 0x22, 0x2a, 0xff, 0x76, 0xdb, - 0xdd, 0xbd, 0x3a, 0xab, 0xb1, 0xa6, 0xfd, 0xb0, 0x6b, 0xc8, 0x15, 0xe9, - 0x8d, 0x0d, 0xc5, 0x2f, 0x0d, 0x4a, 0x07, 0x58, 0x63, 0x57, 0x35, 0x48, - 0x0f, 0x2d, 0x62, 0x0a, 0x08, 0xe6, 0xf2, 0xc5, 0x8c, 0xaf, 0x7d, 0xa6, - 0x52, 0xac, 0x09, 0xc0, 0x69, 0xde, 0x53, 0x02, 0xe5, 0x25, 0x25, 0x43, - 0x3b, 0x55, 0x31, 0x59, 0x47, 0x4e, 0x6c, 0x36, 0x77, 0x15, 0xfa, 0xf0, - 0xfb, 0xce, 0x1c, 0xb5, 0xb6, 0xa7, 0xf5, 0xa8, 0xaf, 0xb8, 0x3e, 0xd4, - 0x18, 0xf7, 0x65, 0x1b, 0x2f, 0x3b, 0x18, 0x51, 0x93, 0x59, 0x22, 0x53, - 0xe9, 0x3e, 0x35, 0x20, 0x2d, 0xfc, 0xc3, 0xd8, 0xe3, 0xbb, 0x4f, 0xaa, - 0xfb, 0xa6, 0x71, 0xb2, 0xc9, 0xca, 0xfb, 0xeb, 0x7d, 0x10, 0x46, 0x32, - 0xaf, 0x4b, 0x8a, 0x58, 0xad, 0x56, 0x67, 0x46, 0x73, 0x2a, 0x6d, 0x07, - 0x2d, 0xe3, 0xb8, 0xc3, 0x47, 0xae, 0x65, 0xa6, 0x70, 0xad, 0x29, 0xc2, - 0x30, 0xe1, 0x52, 0x05, 0x90, 0x28, 0x16, 0x45, 0x1c, 0x56, 0xd6, 0x58, - 0xce, 0x4c, 0x01, 0x34, 0x90, 0x12, 0x0d, 0xee, 0x80, 0xcc, 0x86, 0xb3, - 0x3f, 0xa7, 0xb8, 0xa9, 0x85, 0xba, 0xe3, 0xd6, 0x0c, 0xfa, 0x3d, 0x1e, - 0x61, 0x3d, 0x53, 0x52, 0x9a, 0x59, 0xfc, 0x51, 0xbd, 0x3c, 0x6b, 0x1d, - 0x31, 0xf9, 0x1b, 0xd6, 0xfb, 0xb9, 0x7d, 0xa9, 0x5e, 0xa7, 0xfb, 0xb3, - 0x37, 0xcd, 0xe6, 0xee, 0x69, 0x13, 0xb7, 0x34, 0x3d, 0x4d, 0xf4, 0x58, - 0xdc, 0x55, 0x89, 0x44, 0xc9, 0x27, 0x75, 0x04, 0x5e, 0xe0, 0x8b, 0xc1, - 0x17, 0xad, 0x6e, 0xa6, 0xa0, 0xae, 0x5e, 0xc4, 0xff, 0xe3, 0x4b, 0x08, - 0x35, 0x2b, 0xf1, 0x46, 0xe4, 0x56, 0x65, 0x58, 0x3a, 0x4b, 0x8c, 0x31, - 0xa3, 0x0f, 0x21, 0xeb, 0x19, 0xca, 0x00, 0xb2, 0xe6, 0xa6, 0x90, 0xaa, - 0x73, 0xbc, 0x8c, 0xd9, 0x0a, 0xfd, 0x06, 0x21, 0x85, 0x3f, 0x75, 0x53, - 0x8a, 0x59, 0xba, 0x50, 0x86, 0x3a, 0x93, 0x1a, 0x3a, 0xf6, 0x7d, 0xd3, - 0x29, 0xb8, 0xc3, 0xa8, 0xd9, 0xa7, 0x9b, 0xb5, 0xb3, 0xcf, 0xd5, 0xf1, - 0x54, 0x16, 0x13, 0x37, 0xba, 0x4e, 0x40, 0x59, 0xf8, 0x54, 0x93, 0x42, - 0x16, 0x25, 0x7b, 0x01, 0x95, 0xdd, 0x74, 0xbf, 0xff, 0xab, 0x8d, 0xa6, - 0xe8, 0xaf, 0xa2, 0xc6, 0xd7, 0xe6, 0x44, 0x0b, 0xcc, 0x2d, 0xb8, 0x48, - 0x93, 0x57, 0xdc, 0x57, 0x90, 0x49, 0x08, 0x2f, 0xb1, 0x0c, 0x3f, 0xe8, - 0xbd, 0xc7, 0x96, 0xb0, 0x9e, 0xa6, 0x87, 0xab, 0x6f, 0xbe, 0x47, 0xdc, - 0x03, 0x00, 0xc8, 0x23, 0x95, 0x41, 0x82, 0x54, 0x5c, 0x59, 0x69, 0x4f, - 0x36, 0x38, 0xba, 0x17, 0x41, 0xf3, 0xef, 0xd0, 0x69, 0xb6, 0x22, 0xa8, - 0x70, 0xa8, 0x4e, 0xb7, 0x3d, 0xd2, 0xca, 0xf4, 0x31, 0x19, 0x6b, 0x39, - 0x17, 0x50, 0x79, 0x59, 0xf9, 0x53, 0x88, 0x40, 0x5d, 0x22, 0x7c, 0xfe, - 0xdd, 0xda, 0x68, 0xbd, 0x03, 0xab, 0xc1, 0xa6, 0x4d, 0xb1, 0xf3, 0xc8, - 0xba, 0xe9, 0x35, 0x0e, 0x57, 0x30, 0x6c, 0x4a, 0x28, 0x58, 0x3a, 0x57, - 0xd1, 0x47, 0x78, 0x2c, 0xbb, 0x09, 0x63, 0xe5, 0x70, 0xc5, 0x42, 0xaf, - 0x74, 0xa6, 0x91, 0xac, 0x81, 0xc0, 0x07, 0xdf, 0x01, 0x03, 0x7d, 0x26, - 0x96, 0x43, 0x71, 0x55, 0x1d, 0x59, 0xf7, 0x4d, 0xdf, 0x35, 0xd3, 0x14, - 0x52, 0xf0, 0x6b, 0xce, 0xbf, 0xb4, 0x9a, 0xa7, 0x1d, 0xa9, 0x19, 0xb9, - 0xd2, 0xd4, 0xc3, 0xf7, 0x09, 0x1c, 0xad, 0x3b, 0x64, 0x51, 0x92, 0x59, - 0xe6, 0x52, 0x6b, 0x3e, 0x97, 0x1f, 0x82, 0xfb, 0x2a, 0xd8, 0x72, 0xbb, - 0x1f, 0xaa, 0x0e, 0xa7, 0xc8, 0xb2, 0x54, 0xcb, 0xa1, 0xec, 0x26, 0x11, - 0xd2, 0x32, 0x0b, 0x4c, 0xa4, 0x58, 0x80, 0x56, 0xfe, 0x45, 0xdb, 0x29, - 0xc2, 0x06, 0x8d, 0xe2, 0x38, 0xc3, 0x00, 0xae, 0x67, 0xa6, 0xb0, 0xad, - 0xa9, 0xc2, 0xcd, 0xe1, 0xff, 0x05, 0x28, 0x29, 0x83, 0x45, 0x4a, 0x56, - 0xc0, 0x58, 0x74, 0x4c, 0x76, 0x33, 0xe9, 0x11, 0x64, 0xed, 0xf5, 0xcb, - 0x2e, 0xb3, 0x26, 0xa7, 0xe9, 0xa9, 0xf1, 0xba, 0x7a, 0xd7, 0xba, 0xfa, - 0xda, 0x1e, 0xe1, 0x3d, 0x93, 0x52, 0x9b, 0x59, 0xb5, 0x51, 0x3f, 0x3c, - 0xc9, 0x1c, 0x86, 0xf8, 0x83, 0xd5, 0x94, 0xb9, 0x4d, 0xa9, 0x7b, 0xa7, - 0x54, 0xb4, 0xc5, 0xcd, 0x8f, 0xef, 0x0f, 0x14, 0x42, 0x35, 0x93, 0x4d, - 0x07, 0x59, 0xac, 0x55, 0x19, 0x44, 0x2f, 0x27, 0xcb, 0x03, 0xbc, 0xdf, - 0x13, 0xc1, 0xd6, 0xac, 0x71, 0xa6, 0xe9, 0xae, 0xdd, 0xc4, 0xa3, 0xe4, - 0xf6, 0x08, 0xca, 0x2b, 0x59, 0x47, 0x0d, 0x57, 0x48, 0x58, 0xdf, 0x4a, - 0xf9, 0x30, 0xfe, 0x0e, 0x77, 0xea, 0x93, 0xc9, 0xac, 0xb1, 0xd4, 0xa6, - 0xc5, 0xaa, 0xe3, 0xbc, 0x28, 0xda, 0xb5, 0xfd, 0xa4, 0x21, 0xfe, 0x3f, - 0xb3, 0x53, 0x83, 0x59, 0x70, 0x50, 0x02, 0x3a, 0xf0, 0x19, 0x91, 0xf5, - 0xe7, 0xd2, 0xc5, 0xb7, 0x9a, 0xa8, 0xfc, 0xa7, 0xf8, 0xb5, 0x46, 0xd0, - 0x7d, 0xf2, 0xf7, 0x16, 0x9f, 0x37, 0x06, 0x4f, 0x53, 0x59, 0xbe, 0x54, - 0x20, 0x42, 0x7a, 0x24, 0xd0, 0x00, 0xf8, 0xdc, 0xfc, 0xbe, 0xc6, 0xab, - 0x93, 0xa6, 0x3b, 0xb0, 0x22, 0xc7, 0x7f, 0xe7, 0xeb, 0x0b, 0x60, 0x2e, - 0x1b, 0x49, 0xb8, 0x57, 0xb8, 0x57, 0x30, 0x49, 0x74, 0x2e, 0x09, 0x0c, - 0x98, 0xe7, 0x39, 0xc7, 0x47, 0xb0, 0x95, 0xa6, 0xbc, 0xab, 0xe9, 0xbe, - 0xdf, 0xdc, 0xb3, 0x00, 0x63, 0x24, 0x0a, 0x42, 0xb9, 0x54, 0x51, 0x59, - 0x16, 0x4f, 0xb4, 0x37, 0x11, 0x17, 0x9c, 0xf2, 0x5a, 0xd0, 0x0a, 0xb6, - 0xff, 0xa7, 0x96, 0xa8, 0xb2, 0xb7, 0xd2, 0xd2, 0x74, 0xf5, 0xd4, 0x19, - 0xef, 0x39, 0x62, 0x50, 0x83, 0x59, 0xbc, 0x53, 0x11, 0x40, 0xbf, 0x21, - 0xcf, 0xfd, 0x44, 0xda, 0xf3, 0xbc, 0xd1, 0xaa, 0xcd, 0xa6, 0xa2, 0xb1, - 0x79, 0xc9, 0x62, 0xea, 0xdc, 0x0e, 0xe8, 0x30, 0xca, 0x4a, 0x47, 0x58, - 0x12, 0x57, 0x6b, 0x47, 0xe1, 0x2b, 0x13, 0x09, 0xbd, 0xe4, 0xf1, 0xc4, - 0xf7, 0xae, 0x6f, 0xa6, 0xd0, 0xac, 0xfb, 0xc0, 0xa5, 0xdf, 0xad, 0x03, - 0x17, 0x27, 0x06, 0x44, 0xa6, 0x55, 0x06, 0x59, 0xa7, 0x4d, 0x52, 0x35, - 0x2f, 0x14, 0xa8, 0xef, 0xdc, 0xcd, 0x64, 0xb4, 0x7e, 0xa7, 0x48, 0xa9, - 0x81, 0xb9, 0x6a, 0xd5, 0x6d, 0xf8, 0xab, 0x1c, 0x2d, 0x3c, 0xa9, 0x51, - 0x99, 0x59, 0xa0, 0x52, 0xf5, 0x3d, 0xf1, 0x1e, 0xdb, 0xfa, 0x8d, 0xd7, - 0x08, 0xbb, 0xed, 0xa9, 0x24, 0xa7, 0x20, 0xb3, 0xdd, 0xcb, 0x4a, 0xed, - 0xce, 0x11, 0x5d, 0x33, 0x68, 0x4c, 0xb9, 0x58, 0x56, 0x56, 0x90, 0x45, - 0x45, 0x29, 0x16, 0x06, 0xec, 0xe1, 0xbb, 0xc2, 0xbb, 0xad, 0x68, 0xa6, - 0xf3, 0xad, 0x26, 0xc3, 0x71, 0xe2, 0xa6, 0x06, 0xc4, 0x29, 0xea, 0x45, - 0x7b, 0x56, 0xa7, 0x58, 0x18, 0x4c, 0xed, 0x32, 0x3d, 0x11, 0xbf, 0xec, - 0x6a, 0xcb, 0xd5, 0xb2, 0x13, 0xa7, 0x16, 0xaa, 0x61, 0xbb, 0x11, 0xd8, - 0x67, 0xfb, 0x7a, 0x1f, 0x5b, 0x3e, 0xd8, 0x52, 0x95, 0x59, 0x70, 0x51, - 0xc0, 0x3b, 0x25, 0x1c, 0xdd, 0xf7, 0xed, 0xd4, 0x28, 0xb9, 0x26, 0xa9, - 0x93, 0xa7, 0xb1, 0xb4, 0x55, 0xce, 0x34, 0xf0, 0xba, 0x14, 0xc7, 0x35, - 0xec, 0x4d, 0x16, 0x59, 0x7e, 0x55, 0xa3, 0x43, 0x9c, 0x26, 0x19, 0x03, - 0x22, 0xdf, 0x95, 0xc0, 0x9a, 0xac, 0x76, 0xa6, 0x31, 0xaf, 0x61, 0xc5, - 0x43, 0xe5, 0xa3, 0x09, 0x5d, 0x2c, 0xc2, 0x47, 0x33, 0x57, 0x2e, 0x58, - 0x7b, 0x4a, 0x6f, 0x30, 0x4f, 0x0e, 0xd8, 0xe9, 0x05, 0xc9, 0x5f, 0xb1, - 0xbf, 0xa6, 0xfe, 0xaa, 0x53, 0xbd, 0xc5, 0xda, 0x5e, 0xfe, 0x45, 0x22, - 0x74, 0x40, 0xf0, 0x53, 0x79, 0x59, 0x25, 0x50, 0x7e, 0x39, 0x4f, 0x19, - 0xe3, 0xf4, 0x56, 0xd2, 0x5f, 0xb7, 0x75, 0xa8, 0x1d, 0xa8, 0x59, 0xb6, - 0xd6, 0xd0, 0x28, 0xf3, 0x9d, 0x17, 0x22, 0x38, 0x5b, 0x4f, 0x59, 0x59, - 0x8d, 0x54, 0xa6, 0x41, 0xe3, 0x23, 0x20, 0x00, 0x5f, 0xdc, 0x82, 0xbe, - 0x8f, 0xab, 0x9e, 0xa6, 0x89, 0xb0, 0xa7, 0xc7, 0x25, 0xe8, 0x92, 0x0c, - 0xf5, 0x2e, 0x7c, 0x49, 0xd9, 0x57, 0x99, 0x57, 0xc7, 0x48, 0xe6, 0x2d, - 0x5c, 0x0b, 0xf6, 0xe6, 0xb4, 0xc6, 0xf9, 0xaf, 0x8b, 0xa6, 0xf7, 0xab, - 0x5f, 0xbf, 0x7d, 0xdd, 0x5e, 0x01, 0xfe, 0x24, 0x80, 0x42, 0xec, 0x54, - 0x48, 0x59, 0xc0, 0x4e, 0x31, 0x37, 0x69, 0x16, 0xf3, 0xf1, 0xca, 0xcf, - 0xaa, 0xb5, 0xe0, 0xa7, 0xbb, 0xa8, 0x19, 0xb8, 0x64, 0xd3, 0x20, 0xf6, - 0x76, 0x1a, 0x74, 0x3a, 0xab, 0x50, 0x8b, 0x59, 0x7e, 0x53, 0x98, 0x3f, - 0x22, 0x21, 0x23, 0xfd, 0xa9, 0xd9, 0x81, 0xbc, 0x9d, 0xaa, 0xdf, 0xa6, - 0xf4, 0xb1, 0x03, 0xca, 0x04, 0xeb, 0x8c, 0x0f, 0x70, 0x31, 0x2d, 0x4b, - 0x60, 0x58, 0xeb, 0x56, 0x03, 0x47, 0x4b, 0x2b, 0x69, 0x08, 0x19, 0xe4, - 0x73, 0xc4, 0xac, 0xae, 0x6d, 0xa6, 0x0e, 0xad, 0x75, 0xc1, 0x47, 0xe0, - 0x56, 0x04, 0xb3, 0x27, 0x74, 0x44, 0xd5, 0x55, 0xf8, 0x58, 0x4b, 0x4d, - 0xcc, 0x34, 0x86, 0x13, 0x00, 0xef, 0x50, 0xcd, 0x08, 0xb4, 0x63, 0xa7, - 0x75, 0xa9, 0xeb, 0xb9, 0x01, 0xd6, 0x17, 0xf9, 0x4e, 0x1d, 0xab, 0x3c, - 0xf0, 0x51, 0x99, 0x59, 0x60, 0x52, 0x72, 0x3d, 0x59, 0x1e, 0x28, 0xfa, - 0xfc, 0xd6, 0x95, 0xba, 0xc2, 0xa9, 0x37, 0xa7, 0x7c, 0xb3, 0x65, 0xcc, - 0xf5, 0xed, 0x73, 0x12, 0xeb, 0x33, 0xc0, 0x4c, 0xd1, 0x58, 0x25, 0x56, - 0x27, 0x45, 0xaa, 0x28, 0x6e, 0x05, 0x49, 0xe1, 0x3d, 0xc2, 0x7b, 0xad, - 0x65, 0xa6, 0x3e, 0xae, 0x9f, 0xc3, 0x16, 0xe3, 0x4f, 0x07, 0x5c, 0x2a, - 0x54, 0x46, 0xa9, 0x56, 0x8a, 0x58, 0xc4, 0x4b, 0x57, 0x32, 0x9c, 0x10, - 0x14, 0xec, 0xe1, 0xca, 0x7f, 0xb2, 0xfe, 0xa6, 0x47, 0xaa, 0xd1, 0xbb, - 0xa9, 0xd8, 0x12, 0xfc, 0x1b, 0x20, 0xd5, 0x3e, 0x18, 0x53, 0x92, 0x59, - 0x26, 0x51, 0x42, 0x3b, 0x82, 0x1b, 0x31, 0xf7, 0x58, 0xd4, 0xbf, 0xb8, - 0xfd, 0xa8, 0xaf, 0xa7, 0x10, 0xb5, 0xe0, 0xce, 0xe1, 0xf0, 0x5d, 0x15, - 0x52, 0x36, 0x3e, 0x4e, 0x2a, 0x59, 0x46, 0x55, 0x38, 0x43, 0xfa, 0x25, - 0x76, 0x02, 0x7b, 0xde, 0x23, 0xc0, 0x58, 0xac, 0x7c, 0xa6, 0x81, 0xaf, - 0xdc, 0xc5, 0xed, 0xe5, 0x48, 0x0a, 0xf4, 0x2c, 0x28, 0x48, 0x59, 0x57, - 0x10, 0x58, 0x19, 0x4a, 0xe0, 0x2f, 0xa6, 0x0d, 0x32, 0xe9, 0x7f, 0xc8, - 0x0c, 0xb1, 0xb2, 0xa6, 0x32, 0xab, 0xc9, 0xbd, 0x5e, 0xdb, 0x0d, 0xff, - 0xe0, 0x22, 0xeb, 0x40, 0x2c, 0x54, 0x6e, 0x59, 0xda, 0x4f, 0xf9, 0x38, - 0xaa, 0x18, 0x39, 0xf4, 0xc5, 0xd1, 0xfa, 0xb6, 0x52, 0xa8, 0x3d, 0xa8, - 0xbd, 0xb6, 0x67, 0xd1, 0xd3, 0xf3, 0x40, 0x18, 0xaa, 0x38, 0xa6, 0x4f, - 0x6a, 0x59, 0x4f, 0x54, 0x34, 0x41, 0x45, 0x23, 0x73, 0xff, 0xc5, 0xdb, - 0x0b, 0xbe, 0x5a, 0xab, 0xa6, 0xa6, 0xdc, 0xb0, 0x2b, 0xc8, 0xca, 0xe8, - 0x3d, 0x0d, 0x84, 0x2f, 0xe0, 0x49, 0xf9, 0x57, 0x74, 0x57, 0x63, 0x48, - 0x52, 0x2d, 0xb3, 0x0a, 0x50, 0xe6, 0x32, 0xc6, 0xac, 0xaf, 0x82, 0xa6, - 0x34, 0xac, 0xd5, 0xbf, 0x1c, 0xde, 0x08, 0x02, 0x9c, 0x25, 0xef, 0x42, - 0x26, 0x55, 0x35, 0x59, 0x70, 0x4e, 0xa9, 0x36, 0xc3, 0x15, 0x49, 0xf1, - 0x3c, 0xcf, 0x48, 0xb5, 0xc5, 0xa7, 0xe0, 0xa8, 0x80, 0xb8, 0xfa, 0xd3, - 0xc9, 0xf6, 0x1b, 0x1b, 0xf2, 0x3a, 0xf8, 0x50, 0x8f, 0x59, 0x42, 0x53, - 0x1e, 0x3f, 0x7f, 0x20, 0x7d, 0xfc, 0x09, 0xd9, 0x17, 0xbc, 0x66, 0xaa, - 0xf1, 0xa6, 0x4b, 0xb2, 0x8a, 0xca, 0xad, 0xeb, 0x32, 0x10, 0x01, 0x32, - 0x87, 0x4b, 0x7e, 0x58, 0xbf, 0x56, 0x9b, 0x46, 0xb5, 0x2a, 0xbd, 0x07, - 0x77, 0xe3, 0xf4, 0xc3, 0x64, 0xae, 0x6a, 0xa6, 0x4f, 0xad, 0xf0, 0xc1, - 0xe8, 0xe0, 0x01, 0x05, 0x4b, 0x28, 0xe4, 0x44, 0x04, 0x56, 0xe4, 0x58, - 0xf2, 0x4c, 0x45, 0x34, 0xdb, 0x12, 0x5b, 0xee, 0xc0, 0xcc, 0xaf, 0xb3, - 0x4b, 0xa7, 0xa1, 0xa9, 0x57, 0xba, 0x97, 0xd6, 0xc3, 0xf9, 0xef, 0x1d, - 0x29, 0x3d, 0x34, 0x52, 0x9a, 0x59, 0x1a, 0x52, 0xfa, 0x3c, 0xb2, 0x1d, - 0x82, 0xf9, 0x60, 0xd6, 0x2c, 0xba, 0x92, 0xa9, 0x52, 0xa7, 0xd2, 0xb3, - 0xf7, 0xcc, 0x96, 0xee, 0x1f, 0x13, 0x74, 0x34, 0x18, 0x4d, 0xe8, 0x58, - 0xf5, 0x55, 0xb8, 0x44, 0x12, 0x28, 0xc3, 0x04, 0xa7, 0xe0, 0xc5, 0xc1, - 0x36, 0xad, 0x69, 0xa6, 0x83, 0xae, 0x1f, 0xc4, 0xb8, 0xe3, 0xfa, 0x07, - 0xf2, 0x2a, 0xc0, 0x46, 0xd0, 0x56, 0x73, 0x58, 0x63, 0x4b, 0xce, 0x31, - 0xf1, 0x0f, 0x6e, 0xeb, 0x57, 0xca, 0x29, 0xb2, 0xec, 0xa6, 0x79, 0xaa, - 0x40, 0xbc, 0x43, 0xd9, 0xbf, 0xfc, 0xb9, 0x20, 0x50, 0x3f, 0x56, 0x53, - 0x8e, 0x59, 0xdb, 0x50, 0xc4, 0x3a, 0xdb, 0x1a, 0x8c, 0xf6, 0xbe, 0xd3, - 0x5a, 0xb8, 0xd3, 0xa8, 0xce, 0xa7, 0x6e, 0xb5, 0x71, 0xcf, 0x88, 0xf1, - 0x04, 0x16, 0xd9, 0x36, 0x92, 0x4e, 0x3a, 0x59, 0x12, 0x55, 0xc4, 0x42, - 0x60, 0x25, 0xca, 0x01, 0xde, 0xdd, 0xaa, 0xbf, 0x1b, 0xac, 0x86, 0xa6, - 0xca, 0xaf, 0x61, 0xc6, 0x90, 0xe6, 0xf2, 0x0a, 0x8a, 0x2d, 0x89, 0x48, - 0x83, 0x57, 0xea, 0x57, 0xbf, 0x49, 0x4a, 0x2f, 0xff, 0x0c, 0x8c, 0xe8, - 0xf7, 0xc7, 0xbf, 0xb0, 0xa2, 0xa6, 0x6c, 0xab, 0x3b, 0xbe, 0xfc, 0xdb, - 0xb7, 0xff, 0x7f, 0x23, 0x5f, 0x41, 0x67, 0x54, 0x63, 0x59, 0x8b, 0x4f, - 0x76, 0x38, 0x03, 0x18, 0x92, 0xf3, 0x2f, 0xd1, 0x9a, 0xb6, 0x2d, 0xa8, - 0x64, 0xa8, 0x1c, 0xb7, 0xfc, 0xd1, 0x7b, 0xf4, 0xe5, 0x18, 0x2f, 0x39, - 0xf3, 0x4f, 0x75, 0x59, 0x14, 0x54, 0xbf, 0x40, 0xa5, 0x22, 0xcc, 0xfe, - 0x23, 0xdb, 0x9e, 0xbd, 0x1c, 0xab, 0xba, 0xa6, 0x28, 0xb1, 0xb5, 0xc8, - 0x6c, 0xe9, 0xea, 0x0d, 0x12, 0x30, 0x43, 0x4a, 0x16, 0x58, 0x4f, 0x57, - 0xff, 0x47, 0xbc, 0x2c, 0x0b, 0x0a, 0xab, 0xe5, 0xb0, 0xc5, 0x60, 0xaf, - 0x7c, 0xa6, 0x6f, 0xac, 0x4e, 0xc0, 0xbb, 0xde, 0xb3, 0x02, 0x37, 0x26, - 0x62, 0x43, 0x58, 0x55, 0x25, 0x59, 0x1e, 0x4e, 0x1f, 0x36, 0x1f, 0x15, - 0xa0, 0xf0, 0xab, 0xce, 0xec, 0xb4, 0xa5, 0xa7, 0x0b, 0xa9, 0xe8, 0xb8, - 0x8f, 0xd4, 0x72, 0xf7, 0xc0, 0x1b, 0x71, 0x3b, 0x42, 0x51, 0x93, 0x59, - 0x01, 0x53, 0xa5, 0x3e, 0xe1, 0x1f, 0xcf, 0xfb, 0x71, 0xd8, 0xa7, 0xbb, - 0x32, 0xaa, 0x09, 0xa7, 0x9c, 0xb2, 0x17, 0xcb, 0x53, 0xec, 0xd9, 0x10, - 0x90, 0x32, 0xe4, 0x4b, 0x95, 0x58, 0x96, 0x56, 0x2f, 0x46, 0x20, 0x2a, - 0x12, 0x07, 0xd6, 0xe2, 0x71, 0xc3, 0x23, 0xae, 0x65, 0xa6, 0x93, 0xad, - 0x6c, 0xc2, 0x87, 0xe1, 0xac, 0x05, 0xe7, 0x28, 0x4c, 0x45, 0x38, 0x56, - 0xc9, 0x58, 0x9e, 0x4c, 0xb5, 0x33, 0x38, 0x12, 0xaf, 0xed, 0x37, 0xcc, - 0x55, 0xb3, 0x33, 0xa7, 0xd1, 0xa9, 0xbf, 0xba, 0x34, 0xd7, 0x6b, 0xfa, - 0x92, 0x1e, 0xa6, 0x3d, 0x75, 0x52, 0x9b, 0x59, 0xd6, 0x51, 0x79, 0x3c, - 0x14, 0x1d, 0xd4, 0xf8, 0xca, 0xd5, 0xc1, 0xb9, 0x66, 0xa9, 0x6a, 0xa7, - 0x2f, 0xb4, 0x80, 0xcd, 0x43, 0xef, 0xc1, 0x13, 0x04, 0x35, 0x6b, 0x4d, - 0xfe, 0x58, 0xc4, 0x55, 0x4a, 0x44, 0x77, 0x27, 0x19, 0x04, 0x07, 0xe0, - 0x4a, 0xc1, 0xf5, 0xac, 0x6d, 0xa6, 0xc9, 0xae, 0xa1, 0xc4, 0x59, 0xe4, - 0xa6, 0x08, 0x87, 0x2b, 0x28, 0x47, 0xfb, 0x56, 0x56, 0x58, 0x07, 0x4b, - 0x40, 0x31, 0x45, 0x0f, 0xcc, 0xea, 0xca, 0xc9, 0xd9, 0xb1, 0xd7, 0xa6, - 0xaf, 0xaa, 0xaf, 0xbc, 0xdf, 0xd9, 0x68, 0xfd, 0x5b, 0x21, 0xc6, 0x3f, - 0x98, 0x53, 0x82, 0x59, 0x98, 0x50, 0x39, 0x3a, 0x41, 0x1a, 0xd9, 0xf5, - 0x31, 0xd3, 0xef, 0xb7, 0xaf, 0xa8, 0xea, 0xa7, 0xcf, 0xb5, 0x01, 0xd0, - 0x30, 0xf2, 0xac, 0x16, 0x5e, 0x37, 0xe5, 0x4e, 0x47, 0x59, 0xdd, 0x54, - 0x51, 0x42, 0xc5, 0x24, 0x1d, 0x01, 0x41, 0xdd, 0x33, 0xbf, 0xe0, 0xab, - 0x90, 0xa6, 0x15, 0xb0, 0xe8, 0xc6, 0x31, 0xe7, 0x9e, 0x0b, 0x1c, 0x2e, - 0xee, 0x48, 0xa5, 0x57, 0xcd, 0x57, 0x58, 0x49, 0xbb, 0x2e, 0x56, 0x0c, - 0xe3, 0xe7, 0x77, 0xc7, 0x6b, 0xb0, 0x99, 0xa6, 0xa4, 0xab, 0xb0, 0xbe, - 0x99, 0xdc, 0x62, 0x00, 0x1c, 0x24, 0xd4, 0x41, 0xa0, 0x54, 0x56, 0x59, - 0x3c, 0x4f, 0xf1, 0x37, 0x5d, 0x17, 0xea, 0xf2, 0x9c, 0xd0, 0x38, 0xb6, - 0x0e, 0xa8, 0x84, 0xa8, 0x85, 0xb7, 0x8c, 0xd2, 0x27, 0xf5, 0x88, 0x19, - 0xb3, 0x39, 0x3f, 0x50, 0x7f, 0x59, 0xd7, 0x53, 0x4a, 0x40, 0x05, 0x22, - 0x22, 0xfe, 0x86, 0xda, 0x2c, 0xbd, 0xe6, 0xaa, 0xc9, 0xa6, 0x79, 0xb1, - 0x3e, 0xc9, 0x10, 0xea, 0x94, 0x0e, 0xa2, 0x30, 0xa2, 0x4a, 0x36, 0x58, - 0x26, 0x57, 0x99, 0x47, 0x28, 0x2c, 0x60, 0x09, 0x08, 0xe5, 0x2e, 0xc5, - 0x15, 0xaf, 0x77, 0xa6, 0xac, 0xac, 0xc9, 0xc0, 0x58, 0xdf, 0x60, 0x03, - 0xd0, 0x26, 0xd2, 0x43, 0x8e, 0x55, 0x12, 0x59, 0xc9, 0x4d, 0x97, 0x35, - 0x77, 0x14, 0xf9, 0xef, 0x1b, 0xce, 0x91, 0xb4, 0x87, 0xa7, 0x38, 0xa9, - 0x4d, 0xb9, 0x27, 0xd5, 0x1d, 0xf8, 0x61, 0x1c, 0xf3, 0x3b, 0x88, 0x51, - 0x97, 0x59, 0xc0, 0x52, 0x2a, 0x3e, 0x41, 0x1f, 0x24, 0xfb, 0xd8, 0xd7, - 0x37, 0xbb, 0x04, 0xaa, 0x1c, 0xa7, 0xf4, 0xb2, 0xa2, 0xcb, 0xf7, 0xec, - 0x85, 0x11, 0x1a, 0x33, 0x40, 0x4c, 0xaf, 0x58, 0x68, 0x56, 0xc3, 0x45, - 0x8b, 0x29, 0x65, 0x06, 0x35, 0xe2, 0xf6, 0xc2, 0xd9, 0xad, 0x69, 0xa6, - 0xd3, 0xad, 0xec, 0xc2, 0x27, 0xe2, 0x59, 0x06, 0x7b, 0x29, 0xbc, 0x45, - 0x64, 0x56, 0xb3, 0x58, 0x43, 0x4c, 0x2b, 0x33, 0x8d, 0x11, 0x0c, 0xed, - 0xa7, 0xcb, 0x00, 0xb3, 0x1b, 0xa7, 0x01, 0xaa, 0x2e, 0xbb, 0xca, 0xd7, - 0x17, 0xfb, 0x33, 0x1f, 0x1f, 0x3e, 0xbc, 0x52, 0x95, 0x59, 0x91, 0x51, - 0xfa, 0x3b, 0x71, 0x1c, 0x2a, 0xf8, 0x34, 0xd5, 0x56, 0xb9, 0x3b, 0xa9, - 0x85, 0xa7, 0x89, 0xb4, 0x11, 0xce, 0xe9, 0xef, 0x6a, 0x14, 0x8c, 0x35, - 0xbf, 0x4d, 0x16, 0x59, 0x8c, 0x55, 0xe0, 0x43, 0xda, 0x26, 0x6f, 0x03, - 0x66, 0xdf, 0xd2, 0xc0, 0xb2, 0xac, 0x77, 0xa6, 0x0c, 0xaf, 0x28, 0xc5, - 0xf7, 0xe4, 0x53, 0x09, 0x1c, 0x2c, 0x8f, 0x47, 0x24, 0x57, 0x39, 0x58, - 0xa7, 0x4a, 0xb3, 0x30, 0x9b, 0x0e, 0x26, 0xea, 0x41, 0xc9, 0x87, 0xb1, - 0xc5, 0xa6, 0xe6, 0xaa, 0x1e, 0xbd, 0x7e, 0xda, 0x10, 0xfe, 0xfa, 0x21, - 0x3f, 0x40, 0xd2, 0x53, 0x81, 0x59, 0x45, 0x50, 0xbe, 0x39, 0x95, 0x19, - 0x35, 0xf5, 0x9a, 0xd2, 0x8c, 0xb7, 0x87, 0xa8, 0x0d, 0xa8, 0x2b, 0xb6, - 0x96, 0xd0, 0xd8, 0xf2, 0x50, 0x17, 0xe8, 0x37, 0x31, 0x4f, 0x5a, 0x59, - 0xa0, 0x54, 0xe2, 0x41, 0x25, 0x24, 0x74, 0x00, 0xa3, 0xdc, 0xbc, 0xbe, - 0xa8, 0xab, 0x97, 0xa6, 0x67, 0xb0, 0x68, 0xc7, 0xd9, 0xe7, 0x47, 0x0c, - 0xad, 0x2e, 0x53, 0x49, 0xc7, 0x57, 0xa9, 0x57, 0xf8, 0x48, 0x27, 0x2e, - 0xab, 0x0b, 0x41, 0xe7, 0xf0, 0xc6, 0x1e, 0xb0, 0x8f, 0xa6, 0xdc, 0xab, - 0x28, 0xbf, 0x35, 0xdd, 0x0f, 0x01, 0xb5, 0x24, 0x4c, 0x42, 0xd3, 0x54, - 0x4e, 0x59, 0xe6, 0x4e, 0x6e, 0x37, 0xb6, 0x16, 0x41, 0xf2, 0x0d, 0xd0, - 0xd5, 0xb5, 0xef, 0xa7, 0xa9, 0xa8, 0xea, 0xb7, 0x21, 0xd3, 0xcf, 0xf5, - 0x2f, 0x1a, 0x32, 0x3a, 0x8f, 0x50, 0x82, 0x59, 0x9f, 0x53, 0xcd, 0x3f, - 0x6b, 0x21, 0x73, 0xfd, 0xee, 0xd9, 0xb8, 0xbc, 0xb4, 0xaa, 0xd5, 0xa6, - 0xd0, 0xb1, 0xc2, 0xc9, 0xba, 0xea, 0x3b, 0x0f, 0x32, 0x31, 0xff, 0x4a, - 0x55, 0x58, 0xfc, 0x56, 0x34, 0x47, 0x91, 0x2b, 0xb5, 0x08, 0x66, 0xe4, - 0xac, 0xc4, 0xcf, 0xae, 0x6f, 0xa6, 0xee, 0xac, 0x40, 0xc1, 0xfa, 0xdf, - 0x0a, 0x04, 0x69, 0x27, 0x45, 0x44, 0xbb, 0x55, 0x04, 0x59, 0x6f, 0x4d, - 0x10, 0x35, 0xcf, 0x13, 0x50, 0xef, 0x8f, 0xcd, 0x32, 0xb4, 0x70, 0xa7, - 0x60, 0xa9, 0xb9, 0xb9, 0xbd, 0xd5, 0xc7, 0xf8, 0x05, 0x1d, 0x6f, 0x3c, - 0xd1, 0x51, 0x98, 0x59, 0x7f, 0x52, 0xad, 0x3d, 0xa1, 0x1e, 0x79, 0xfa, - 0x3e, 0xd7, 0xcc, 0xba, 0xd2, 0xa9, 0x33, 0xa7, 0x4e, 0xb3, 0x28, 0xcc, - 0xa5, 0xed, 0x27, 0x12, 0xab, 0x33, 0x96, 0x4c, 0xc7, 0x58, 0x3c, 0x56, - 0x56, 0x45, 0xf3, 0x28, 0xba, 0x05, 0x94, 0xe1, 0x78, 0xc2, 0x98, 0xad, - 0x66, 0xa6, 0x1a, 0xae, 0x6a, 0xc3, 0xc6, 0xe2, 0x07, 0x07, 0x0f, 0x2a, - 0x2a, 0x46, 0x8f, 0x56, 0x9a, 0x58, 0xea, 0x4b, 0x9b, 0x32, 0xe9, 0x10, - 0x61, 0xec, 0x20, 0xcb, 0xa5, 0xb2, 0x0a, 0xa7, 0x2f, 0xaa, 0x9e, 0xbb, - 0x62, 0xd8, 0xc4, 0xfb, 0xd1, 0x1f, 0x9d, 0x3e, 0xfb, 0x52, 0x93, 0x59, - 0x48, 0x51, 0x7d, 0x3b, 0xcb, 0x1b, 0x83, 0xf7, 0x9c, 0xd4, 0xed, 0xb8, - 0x13, 0xa9, 0x9e, 0xa7, 0xe8, 0xb4, 0x9e, 0xce, 0x91, 0xf0, 0x13, 0x15, - 0x12, 0x36, 0x18, 0x4e, 0x22, 0x59, 0x5e, 0x55, 0x6b, 0x43, 0x43, 0x26, - 0xc2, 0x02, 0xc8, 0xde, 0x56, 0xc0, 0x78, 0xac, 0x78, 0xa6, 0x5c, 0xaf, - 0xa4, 0xc5, 0x9d, 0xe5, 0xfe, 0x09, 0xaf, 0x2c, 0xf7, 0x47, 0x4a, 0x57, - 0x1b, 0x58, 0x4a, 0x4a, 0x1e, 0x30, 0xf8, 0x0d, 0x7a, 0xe9, 0xbf, 0xc8, - 0x32, 0xb1, 0xb6, 0xa6, 0x1d, 0xab, 0x8f, 0xbd, 0x1a, 0xdb, 0xbc, 0xfe, - 0x98, 0x22, 0xb5, 0x40, 0x10, 0x54, 0x73, 0x59, 0xfe, 0x4f, 0x36, 0x39, - 0xf4, 0x18, 0x8b, 0xf4, 0x04, 0xd2, 0x2a, 0xb7, 0x62, 0xa8, 0x2d, 0xa8, - 0x91, 0xb6, 0x22, 0xd1, 0x86, 0xf3, 0xf3, 0x17, 0x6d, 0x38, 0x83, 0x4f, - 0x62, 0x59, 0x6d, 0x54, 0x68, 0x41, 0x8c, 0x23, 0xc7, 0xff, 0x05, 0xdc, - 0x4a, 0xbe, 0x6b, 0xab, 0xa8, 0xa6, 0xb1, 0xb0, 0xf2, 0xc7, 0x7a, 0xe8, - 0xf3, 0x0c, 0x3e, 0x2f, 0xb5, 0x49, 0xea, 0x57, 0x83, 0x57, 0x94, 0x48, - 0x94, 0x2d, 0x02, 0x0b, 0x9c, 0xe6, 0x6e, 0xc6, 0xce, 0xaf, 0x87, 0xa6, - 0x18, 0xac, 0x9e, 0xbf, 0xd3, 0xdd, 0xba, 0x01, 0x52, 0x25, 0xbd, 0x42, - 0x0d, 0x55, 0x3a, 0x59, 0x9a, 0x4e, 0xe2, 0x36, 0x15, 0x16, 0x94, 0xf1, - 0x7f, 0xcf, 0x74, 0xb5, 0xd0, 0xa7, 0xd2, 0xa8, 0x4e, 0xb8, 0xb8, 0xd3, - 0x77, 0xf6, 0xd3, 0x1a, 0xb4, 0x3a, 0xd9, 0x50, 0x8a, 0x59, 0x60, 0x53, - 0x55, 0x3f, 0xcb, 0x20, 0xc8, 0xfc, 0x54, 0xd9, 0x47, 0xbc, 0x7f, 0xaa, - 0xe9, 0xa6, 0x23, 0xb2, 0x4b, 0xca, 0x61, 0xeb, 0xe2, 0x0f, 0xc2, 0x31, - 0x5c, 0x4b, 0x71, 0x58, 0xd3, 0x56, 0xcb, 0x46, 0xfa, 0x2a, 0x0c, 0x08, - 0xc3, 0xe3, 0x2c, 0xc4, 0x87, 0xae, 0x6a, 0xa6, 0x31, 0xad, 0xb9, 0xc1, - 0x9c, 0xe0, 0xb2, 0x04, 0x07, 0x28, 0xae, 0x44, 0xf2, 0x55, 0xea, 0x58, - 0x1d, 0x4d, 0x82, 0x34, 0x2b, 0x13, 0xa5, 0xee, 0x04, 0xcd, 0xd7, 0xb3, - 0x56, 0xa7, 0x8d, 0xa9, 0x24, 0xba, 0x53, 0xd6, 0x73, 0xf9, 0xa7, 0x1d, - 0xec, 0x3c, 0x17, 0x52, 0x99, 0x59, 0x3a, 0x52, 0x33, 0x3d, 0xfd, 0x1d, - 0xcf, 0xf9, 0xa8, 0xd6, 0x5c, 0xba, 0xa8, 0xa9, 0x45, 0xa7, 0xab, 0xb3, - 0xb3, 0xcc, 0x4d, 0xee, 0xce, 0x12, 0x37, 0x34, 0xee, 0x4c, 0xde, 0x58, - 0x0c, 0x56, 0xea, 0x44, 0x58, 0x28, 0x13, 0x05, 0xef, 0xe0, 0x00, 0xc2, - 0x51, 0xad, 0x6b, 0xa6, 0x5f, 0xae, 0xe8, 0xc3, 0x6a, 0xe3, 0xae, 0x07, - 0xab, 0x2a, 0x90, 0x46, 0xbd, 0x56, 0x7e, 0x58, 0x90, 0x4b, 0x0d, 0x32, - 0x41, 0x10, 0xb8, 0xeb, 0x99, 0xca, 0x4e, 0xb2, 0xf6, 0xa6, 0x62, 0xaa, - 0x0a, 0xbc, 0x01, 0xd9, 0x69, 0xfc, 0x77, 0x20, 0x12, 0x3f, 0x3d, 0x53, - 0x8f, 0x59, 0xfd, 0x50, 0xff, 0x3a, 0x27, 0x1b, 0xd9, 0xf6, 0x04, 0xd4, - 0x8a, 0xb8, 0xe3, 0xa8, 0xc3, 0xa7, 0x3f, 0xb5, 0x32, 0xcf, 0x39, 0xf1, - 0xb7, 0x15, 0x9c, 0x36, 0x6a, 0x4e, 0x35, 0x59, 0x28, 0x55, 0xfa, 0x42, - 0xa9, 0x25, 0x14, 0x02, 0x2d, 0xde, 0xdc, 0xbf, 0x3a, 0xac, 0x82, 0xa6, - 0xa4, 0xaf, 0x29, 0xc6, 0x41, 0xe6, 0xa6, 0x0a, 0x45, 0x2d, 0x5a, 0x48, - 0x74, 0x57, 0xf7, 0x57, 0xed, 0x49, 0x8c, 0x2f, 0x4d, 0x0d, 0xd7, 0xe8, - 0x38, 0xc8, 0xdf, 0xb0, 0xac, 0xa6, 0x50, 0xab, 0x06, 0xbe, 0xb4, 0xdb, - 0x68, 0xff, 0x35, 0x23, 0x2d, 0x41, 0x48, 0x54, 0x6c, 0x59, 0xac, 0x4f, - 0xb5, 0x38, 0x4f, 0x18, 0xdf, 0xf3, 0x74, 0xd1, 0xc5, 0xb6, 0x40, 0xa8, - 0x4f, 0xa8, 0xf3, 0xb6, 0xb4, 0xd1, 0x31, 0xf4, 0x97, 0x18, 0xf2, 0x38, - 0xd0, 0x4f, 0x6f, 0x59, 0x30, 0x54, 0xf6, 0x40, 0xec, 0x22, 0x1c, 0xff, - 0x6a, 0xdb, 0xd3, 0xbd, 0x36, 0xab, 0xb2, 0xa6, 0x05, 0xb1, 0x75, 0xc8, - 0x22, 0xe9, 0x99, 0x0d, 0xd2, 0x2f, 0x14, 0x4a, 0x0c, 0x58, 0x5d, 0x57, - 0x2f, 0x48, 0x01, 0x2d, 0x57, 0x0a, 0xf9, 0xe5, 0xea, 0xc5, 0x83, 0xaf, - 0x80, 0xa6, 0x52, 0xac, 0x19, 0xc0, 0x6f, 0xde, 0x65, 0x02, 0xf0, 0x25, - 0x2b, 0x43, 0x47, 0x55, 0x26, 0x59, 0x4a, 0x4e, 0x58, 0x36, 0x70, 0x15, - 0xeb, 0xf0, 0xee, 0xce, 0x18, 0xb5, 0xb1, 0xa7, 0xfa, 0xa8, 0xb5, 0xb8, - 0x4d, 0xd4, 0x22, 0xf7, 0x76, 0x1b, 0x36, 0x3b, 0x1f, 0x51, 0x94, 0x59, - 0x1c, 0x53, 0xe0, 0x3e, 0x27, 0x20, 0x22, 0xfc, 0xb5, 0xd8, 0xda, 0xbb, - 0x4b, 0xaa, 0xfd, 0xa6, 0x78, 0xb2, 0xd5, 0xca, 0x06, 0xec, 0x8c, 0x10, - 0x50, 0x32, 0xb7, 0x4b, 0x8d, 0x58, 0xa8, 0x56, 0x60, 0x46, 0x67, 0x2a, - 0x5e, 0x07, 0x22, 0xe3, 0xad, 0xc3, 0x40, 0xae, 0x69, 0xa6, 0x71, 0xad, - 0x36, 0xc2, 0x3b, 0xe1, 0x5f, 0x05, 0x9f, 0x28, 0x1c, 0x45, 0x20, 0x56, - 0xd7, 0x58, 0xc2, 0x4c, 0xfb, 0x33, 0x7f, 0x12, 0x01, 0xee, 0x75, 0xcc, - 0x7f, 0xb3, 0x3d, 0xa7, 0xbb, 0xa9, 0x8f, 0xba, 0xed, 0xd6, 0x1b, 0xfa, - 0x4a, 0x1e, 0x69, 0x3d, 0x5c, 0x52, 0x97, 0x59, 0xf7, 0x51, 0xb3, 0x3c, - 0x5f, 0x1d, 0x22, 0xf9, 0x11, 0xd6, 0xf0, 0xb9, 0x7c, 0xa9, 0x5e, 0xa7, - 0x04, 0xb4, 0x40, 0xcd, 0xf5, 0xee, 0x76, 0x13, 0xc1, 0x34, 0x46, 0x4d, - 0xf3, 0x58, 0xdb, 0x55, 0x7d, 0x44, 0xbf, 0x27, 0x66, 0x04, 0x52, 0xe0, - 0x81, 0xc1, 0x13, 0xad, 0x6c, 0xa6, 0xa8, 0xae, 0x65, 0xc4, 0x0e, 0xe4, - 0x59, 0x08, 0x40, 0x2b, 0xfb, 0x46, 0xe4, 0x56, 0x67, 0x58, 0x30, 0x4b, - 0x81, 0x31, 0x97, 0x0f, 0x11, 0xeb, 0x11, 0xca, 0xf9, 0xb1, 0xe3, 0xa6, - 0x95, 0xaa, 0x7b, 0xbc, 0x9a, 0xd9, 0x16, 0xfd, 0x14, 0x21, 0x8c, 0x3f, - 0x7d, 0x53, 0x87, 0x59, 0xb6, 0x50, 0x7b, 0x3a, 0x85, 0x1a, 0x2e, 0xf6, - 0x70, 0xd3, 0x22, 0xb8, 0xbe, 0xa8, 0xdf, 0xa7, 0x9f, 0xb5, 0xc1, 0xcf, - 0xe2, 0xf1, 0x5e, 0x16, 0x24, 0x37, 0xba, 0x4e, 0x46, 0x59, 0xf2, 0x54, - 0x87, 0x42, 0x0e, 0x25, 0x69, 0x01, 0x8e, 0xdd, 0x65, 0xbf, 0xff, 0xab, - 0x88, 0xa6, 0xf4, 0xaf, 0xaa, 0xc6, 0xe5, 0xe6, 0x51, 0x0b, 0xd7, 0x2d, - 0xc1, 0x48, 0x96, 0x57, 0xd9, 0x57, 0x87, 0x49, 0xff, 0x2e, 0xa1, 0x0c, - 0x34, 0xe8, 0xaf, 0xc7, 0x92, 0xb0, 0x9e, 0xa6, 0x89, 0xab, 0x7b, 0xbe, - 0x4f, 0xdc, 0x14, 0x00, 0xd4, 0x23, 0x9f, 0x41, 0x84, 0x54, 0x5e, 0x59, - 0x5f, 0x4f, 0x2f, 0x38, 0xaa, 0x17, 0x36, 0xf3, 0xe1, 0xd0, 0x64, 0xb6, - 0x1c, 0xa8, 0x76, 0xa8, 0x53, 0xb7, 0x4c, 0xd2, 0xd5, 0xf4, 0x40, 0x19, - 0x73, 0x39, 0x1f, 0x50, 0x78, 0x59, 0xf7, 0x53, 0x7b, 0x40, 0x54, 0x22, - 0x6b, 0xfe, 0xd2, 0xda, 0x5f, 0xbd, 0xff, 0xaa, 0xc1, 0xa6, 0x56, 0xb1, - 0xfb, 0xc8, 0xc9, 0xe9, 0x43, 0x0e, 0x60, 0x30, 0x77, 0x4a, 0x27, 0x58, - 0x3a, 0x57, 0xc7, 0x47, 0x6d, 0x2c, 0xad, 0x09, 0x56, 0xe5, 0x66, 0xc5, - 0x3c, 0xaf, 0x73, 0xa6, 0x96, 0xac, 0x8c, 0xc0, 0x13, 0xdf, 0x0e, 0x03, - 0x8a, 0x26, 0x9f, 0x43, 0x75, 0x55, 0x1c, 0x59, 0xef, 0x4d, 0xd6, 0x35, - 0xc4, 0x14, 0x47, 0xf0, 0x5c, 0xce, 0xbc, 0xb4, 0x94, 0xa7, 0x22, 0xa9, - 0x21, 0xb9, 0xdf, 0xd4, 0xcf, 0xf7, 0x18, 0x1c, 0xb5, 0x3b, 0x6a, 0x51, - 0x94, 0x59, 0xdf, 0x52, 0x62, 0x3e, 0x8c, 0x1f, 0x71, 0xfb, 0x20, 0xd8, - 0x69, 0xbb, 0x1a, 0xaa, 0x12, 0xa7, 0xcd, 0xb2, 0x61, 0xcb, 0xab, 0xec, - 0x37, 0x11, 0xd9, 0x32, 0x17, 0x4c, 0xa3, 0x58, 0x7c, 0x56, 0xf8, 0x45, - 0xcb, 0x29, 0xba, 0x06, 0x7a, 0xe2, 0x32, 0xc3, 0xfa, 0xad, 0x65, 0xa6, - 0xb8, 0xad, 0xaf, 0xc2, 0xdf, 0xe1, 0x09, 0x06, 0x35, 0x29, 0x8b, 0x45, - 0x4e, 0x56, 0xbf, 0x58, 0x6d, 0x4c, 0x69, 0x33, 0xdd, 0x11, 0x56, 0xed, - 0xeb, 0xcb, 0x25, 0xb3, 0x28, 0xa7, 0xe8, 0xa9, 0xfe, 0xba, 0x83, 0xd7, - 0xc8, 0xfa, 0xe9, 0x1e, 0xe7, 0x3d, 0x9d, 0x52, 0x97, 0x59, 0xb0, 0x51, - 0x36, 0x3c, 0xbb, 0x1c, 0x7a, 0xf8, 0x76, 0xd5, 0x8b, 0xb9, 0x4b, 0xa9, - 0x7c, 0xa7, 0x5c, 0xb4, 0xd0, 0xcd, 0x9c, 0xef, 0x1e, 0x14, 0x4a, 0x35, - 0x9c, 0x4d, 0x07, 0x59, 0xab, 0x55, 0x0c, 0x44, 0x27, 0x27, 0xba, 0x03, - 0xb1, 0xdf, 0x09, 0xc1, 0xd1, 0xac, 0x71, 0xa6, 0xf1, 0xae, 0xe5, 0xc4, - 0xb1, 0xe4, 0x04, 0x09, 0xd4, 0x2b, 0x64, 0x47, 0x0d, 0x57, 0x4a, 0x58, - 0xd2, 0x4a, 0xf4, 0x30, 0xea, 0x0e, 0x70, 0xea, 0x83, 0xc9, 0xaa, 0xb1, - 0xcf, 0xa6, 0xcb, 0xaa, 0xec, 0xbc, 0x33, 0xda, 0xc5, 0xfd, 0xaf, 0x21, - 0x08, 0x40, 0xb8, 0x53, 0x81, 0x59, 0x6b, 0x50, 0xf7, 0x39, 0xe4, 0x19, - 0x82, 0xf5, 0xde, 0xd2, 0xb9, 0xb7, 0x9a, 0xa8, 0xfc, 0xa7, 0x02, 0xb6, - 0x50, 0xd0, 0x8b, 0xf2, 0x05, 0x17, 0xa8, 0x37, 0x0e, 0x4f, 0x53, 0x59, - 0xb9, 0x54, 0x19, 0x42, 0x6c, 0x24, 0xc2, 0x00, 0xee, 0xdc, 0xef, 0xbe, - 0xc5, 0xab, 0x92, 0xa6, 0x40, 0xb0, 0x30, 0xc7, 0x87, 0xe7, 0xff, 0x0b, - 0x65, 0x2e, 0x29, 0x49, 0xb5, 0x57, 0xbb, 0x57, 0x22, 0x49, 0x6e, 0x2e, - 0xf7, 0x0b, 0x8f, 0xe7, 0x2b, 0xc7, 0x43, 0xb0, 0x93, 0xa6, 0xc1, 0xab, - 0xf2, 0xbe, 0xeb, 0xdc, 0xc2, 0x00, 0x6e, 0x24, 0x14, 0x42, 0xbc, 0x54, - 0x51, 0x59, 0x10, 0x4f, 0xa8, 0x37, 0x06, 0x17, 0x8b, 0xf2, 0x51, 0xd0, - 0x02, 0xb6, 0xfc, 0xa7, 0x9b, 0xa8, 0xb7, 0xb7, 0xe1, 0xd2, 0x7d, 0xf5, - 0xe5, 0x19, 0xf7, 0x39, 0x6a, 0x50, 0x82, 0x59, 0xb8, 0x53, 0x05, 0x40, - 0xb6, 0x21, 0xbe, 0xfd, 0x3b, 0xda, 0xe7, 0xbc, 0xcd, 0xaa, 0xd1, 0xa6, - 0xa5, 0xb1, 0x89, 0xc9, 0x67, 0xea, 0xf2, 0x0e, 0xee, 0x30, 0xd4, 0x4a, - 0x49, 0x58, 0x0d, 0x57, 0x65, 0x47, 0xd4, 0x2b, 0x06, 0x09, 0xaf, 0xe4, - 0xe9, 0xc4, 0xef, 0xae, 0x71, 0xa6, 0xd2, 0xac, 0x06, 0xc1, 0xb3, 0xdf, - 0xb8, 0x03, 0x27, 0x27, 0x0c, 0x44, 0xa9, 0x55, 0x08, 0x59, 0x9d, 0x4d, - 0x4a, 0x35, 0x1f, 0x14, 0x9c, 0xef, 0xcf, 0xcd, 0x61, 0xb4, 0x76, 0xa7, - 0x52, 0xa9, 0x82, 0xb9, 0x7d, 0xd5, 0x75, 0xf8, 0xbc, 0x1c, 0x36, 0x3c, - 0xad, 0x51, 0x9c, 0x59, 0x99, 0x52, 0xea, 0x3d, 0xe8, 0x1e, 0xc9, 0xfa, - 0x85, 0xd7, 0xfd, 0xba, 0xea, 0xa9, 0x25, 0xa7, 0x28, 0xb3, 0xe7, 0xcb, - 0x59, 0xed, 0xd9, 0x11, 0x6b, 0x33, 0x6c, 0x4c, 0xbe, 0x58, 0x50, 0x56, - 0x88, 0x45, 0x3a, 0x29, 0x07, 0x06, 0xdf, 0xe1, 0xb2, 0xc2, 0xb6, 0xad, - 0x66, 0xa6, 0xfa, 0xad, 0x30, 0xc3, 0x7c, 0xe2, 0xb7, 0x06, 0xcc, 0x29, - 0xf6, 0x45, 0x7d, 0x56, 0xa5, 0x58, 0x13, 0x4c, 0xde, 0x32, 0x33, 0x11, - 0xb0, 0xec, 0x5f, 0xcb, 0xcf, 0xb2, 0x11, 0xa7, 0x19, 0xaa, 0x6a, 0xbb, - 0x1d, 0xd8, 0x74, 0xfb, 0x88, 0x1f, 0x64, 0x3e, 0xdd, 0x52, 0x95, 0x59, - 0x69, 0x51, 0xb7, 0x3b, 0x18, 0x1c, 0xcf, 0xf7, 0xe2, 0xd4, 0x1d, 0xb9, - 0x25, 0xa9, 0x94, 0xa7, 0xb9, 0xb4, 0x60, 0xce, 0x42, 0xf0, 0xc6, 0x14, - 0xd3, 0x35, 0xf2, 0x4d, 0x19, 0x59, 0x77, 0x55, 0x9f, 0x43, 0x89, 0x26, - 0x12, 0x03, 0x10, 0xdf, 0x90, 0xc0, 0x91, 0xac, 0x7a, 0xa6, 0x34, 0xaf, - 0x6d, 0xc5, 0x4f, 0xe5, 0xb1, 0x09, 0x69, 0x2c, 0xc9, 0x47, 0x38, 0x57, - 0x29, 0x58, 0x75, 0x4a, 0x63, 0x30, 0x42, 0x0e, 0xca, 0xe9, 0xfc, 0xc8, - 0x55, 0xb1, 0xc2, 0xa6, 0xff, 0xaa, 0x5e, 0xbd, 0xd1, 0xda -}; -unsigned int g8_sine_long_raw_len = 47878; diff --git a/examples/sandbox/streams-memory_wav-resample-audiokit/g8-sine.h b/examples/sandbox/streams-memory_wav-resample-audiokit/g8-sine.h deleted file mode 100644 index 8f2f1761a5..0000000000 --- a/examples/sandbox/streams-memory_wav-resample-audiokit/g8-sine.h +++ /dev/null @@ -1,11 +0,0 @@ -const unsigned char g8_sine_raw[] = { - 0x00, 0x00, 0xc1, 0x23, 0x93, 0x41, 0x7e, 0x54, 0x5e, 0x59, 0x6a, 0x4f, - 0x3b, 0x38, 0xc0, 0x17, 0x46, 0xf3, 0xf6, 0xd0, 0x69, 0xb6, 0x26, 0xa8, - 0x6d, 0xa8, 0x4b, 0xb7, 0x3a, 0xd2, 0xc2, 0xf4, 0x2c, 0x19, 0x67, 0x39, - 0x14, 0x50, 0x7a, 0x59, 0xf9, 0x53, 0x8e, 0x40, 0x5f, 0x22, 0x86, 0xfe, - 0xde, 0xda, 0x6f, 0xbd, 0x03, 0xab, 0xc1, 0xa6, 0x4b, 0xb1, 0xed, 0xc8, - 0xb6, 0xe9, 0x2e, 0x0e, 0x53, 0x30, 0x69, 0x4a, 0x26, 0x58, 0x3d, 0x57, - 0xd3, 0x47, 0x7e, 0x2c, 0xc1, 0x09, 0x66, 0xe5, 0x79, 0xc5, 0x3f, 0xaf, - 0x7a, 0xa6, 0x89, 0xac, 0x83, 0xc0, 0xfc, 0xde -}; -unsigned int g8_sine_raw_len = 92; diff --git a/examples/sandbox/streams-memory_wav-resample-audiokit/streams-memory_wav-resample-audiokit.ino b/examples/sandbox/streams-memory_wav-resample-audiokit/streams-memory_wav-resample-audiokit.ino deleted file mode 100644 index eea0905a0a..0000000000 --- a/examples/sandbox/streams-memory_wav-resample-audiokit/streams-memory_wav-resample-audiokit.ino +++ /dev/null @@ -1,71 +0,0 @@ -#include "AudioTools.h" -#include "AudioTools/AudioLibs/AudioBoardStream.h" -#include "g8-sine.h" -#include "g8-sine-long.h" -#include "g8-saw.h" - -// sawtooth note g8 recorded at 96000 -MemoryStream toneG8(g8_sine_raw, g8_sine_raw_len); -// playback at 24000 (4 times slower) -const AudioInfo info(24000,1,16); -AudioBoardStream i2s(AudioKitEs8388V1); -CsvOutput csv(Serial, 1); -//FilteredStream filter(i2s, 1); -ResampleStream resample(csv); // replace with i2s -StreamCopy copier(resample, toneG8, 2048); // copies sound to out -int idx_max = 100; -int idx = idx_max; -MusicalNotes notes; -uint32_t timeout = 0; - -void changeNote() { - const float from_tone = N_G8; - // e.g. 6271.93f / 16.35f * 4.0 = 0.01042741229 - float step_size = notes.frequency(idx) / from_tone * 4.0f; - Serial.print("playing note: "); - Serial.print(notes.noteAt(idx)); - Serial.print(" / step: "); - Serial.println(step_size); - - resample.setStepSize(step_size); - idx--; - if (idx < 0) { - idx = idx_max; - Serial.println("-----------------------"); - } - timeout = millis() + 1000; -} - -// Arduino Setup -void setup(void) { - // Open Serial - Serial.begin(115200); - AudioToolsLogger.begin(Serial, AudioToolsLogLevel::Warning); - - // filter.setFilter(0, new MedianFilter(7)); - // filter.begin(info); - - // open input - toneG8.begin(); - toneG8.setLoop(true); - - // open resample - auto rcfg = resample.defaultConfig(); - rcfg.copyFrom(info); - resample.begin(rcfg); - //resample.setBuffered(false); - - // open i2s output - auto cfg = i2s.defaultConfig(); - cfg.copyFrom(info); - i2s.begin(cfg); - i2s.setVolume(0.3); -} - -// Arduino loop - copy sound to out -void loop() { - if (millis() > timeout) { - changeNote(); - } - copier.copy(); -} \ No newline at end of file diff --git a/examples/sandbox/streams-mp34dt05-tf/streams-audiokit-tf.ino b/examples/sandbox/streams-mp34dt05-tf/streams-audiokit-tf.ino index 7d884e81c6..2bd2e207aa 100644 --- a/examples/sandbox/streams-mp34dt05-tf/streams-audiokit-tf.ino +++ b/examples/sandbox/streams-mp34dt05-tf/streams-audiokit-tf.ino @@ -9,8 +9,8 @@ * */ #include "AudioTools.h" -#include "AudioTools/AudioLibs/AudioMP34DT05.h" -#include "AudioTools/AudioLibs/TfLiteAudioStream.h" +#include "AudioLibs/AudioMP34DT05.h" +#include "AudioLibs/TfLiteAudioStream.h" #include "model.h" // tensorflow model AudioMP34DT05 mic; // Access I2S as stream @@ -39,7 +39,7 @@ void respondToCommand(const char* found_command, uint8_t score, void setup() { Serial.begin(115200); while(!Serial); // wait for serial to be ready - AudioToolsLogger.begin(Serial, AudioToolsLogLevel::Info); + AudioLogger::instance().begin(Serial, AudioLogger::Info); Serial.println("starting..."); diff --git a/examples/sandbox/test-container-avi/test-container-avi.ino b/examples/sandbox/test-container-avi/test-container-avi.ino index 76d37ac0a1..cc9d66edc1 100644 --- a/examples/sandbox/test-container-avi/test-container-avi.ino +++ b/examples/sandbox/test-container-avi/test-container-avi.ino @@ -9,11 +9,11 @@ * */ #include "AudioTools.h" -#include "AudioTools/AudioCodecs/ContainerAVI.h" -#include "AudioTools/AudioLibs/AudioBoardStream.h" +#include "AudioCodecs/ContainerAVI.h" +#include "AudioLibs/AudioKit.h" URLStream url("http://github.com/ssid%22,%22password"); // input -AudioBoardStream out(AudioKitEs8388V1); +AudioKitStream out; DecoderL8 l8(false); AVIDecoder codec(&l8); EncodedAudioStream avi(&out, &codec); @@ -21,7 +21,7 @@ StreamCopy copier(avi, url); void setup() { Serial.begin(115200); - AudioToolsLogger.begin(Serial, AudioToolsLogLevel::Info); + AudioLogger::instance().begin(Serial, AudioLogger::Info); // setup output using default settings out.begin(out.defaultConfig()); diff --git a/examples/sandbox/websockets/client-as-source/client/client.ino b/examples/sandbox/websockets/client-as-source/client/client.ino deleted file mode 100644 index b68a39ef0b..0000000000 --- a/examples/sandbox/websockets/client-as-source/client/client.ino +++ /dev/null @@ -1,50 +0,0 @@ -/** - * @brief A simple WebSocket client sketch that sends out some PCM audio - * data using the https://github.com/Links2004/arduinoWebSockets library.AdapterAudioStreamToAudioOutput.AdapterAudioStreamToAudioOutput - * The WebSocket API is asynchronous, so we need to slow down the sending - * to the playback speed, to prevent any buffer overflows at the receiver. - * @author Phil Schatzmann - */ - -#include // https://github.com/Links2004/arduinoWebSockets -#include -#include "AudioTools.h" -#include "AudioTools/Communication/WebSocketOutput.h" - -// websocket -WiFiMulti WiFiMulti; -WebSocketsClient webSocket; -WebSocketOutput out(webSocket); -// audio -AudioInfo info(44100, 2, 16); -SineWaveGenerator sineWave(32000); -GeneratedSoundStream sound(sineWave); -Throttle throttle(out) -StreamCopy copier(throttle, sound); - -void setup() { - Serial.begin(115200); - - // connect to wifi - WiFiMulti.addAP("SSID", "passpasspass"); - while (WiFiMulti.run() != WL_CONNECTED) { - delay(100); - } - WiFi.setSleep(false); - - // start client connecting to server address, port and URL - webSocket.begin("192.168.1.34", 81, "/"); - - // try ever 5000 again if connection has failed - webSocket.setReconnectInterval(5000); - - sineWave.begin(info, N_B4); - // WebSockets are async, so we need to slow down the sending - throttle.begin(info); -} - -void loop() { - webSocket.loop(); - // generate audio only when we are connected - if (webSocket.isConnected()) copier.copy(); -} \ No newline at end of file diff --git a/examples/sandbox/websockets/client-as-source/server/server.ino b/examples/sandbox/websockets/client-as-source/server/server.ino deleted file mode 100644 index 1e6de1534a..0000000000 --- a/examples/sandbox/websockets/client-as-source/server/server.ino +++ /dev/null @@ -1,49 +0,0 @@ -/** - * @brief A simple WebSocket server sketch that receives PCM audio data using the - * https://github.com/Links2004/arduinoWebSockets library and outputs it via i2s - * to an AudioKit for easy testing. Replace the output with whatever other class - * you like. - * @author Phil Schatzmann - */ -#include //https://github.com/Links2004/arduinoWebSockets -#include -#include -#include -#include "AudioTools.h" -#include "AudioTools/AudioLibs/AudioBoardStream.h" - -WiFiMulti WiFiMulti; -WebSocketsServer webSocket(81); -AudioInfo info(44100, 2, 16); -AudioBoardStream i2s(AudioKitEs8388V1); // Access I2S as stream - -/// Just output the audio data -void webSocketEvent(uint8_t num, WStype_t type, uint8_t* payload, - size_t length) { - if (type == WStype_BIN) { - i2s.write(payload, length); - } -} - -void setup() { - Serial.begin(115200); - AudioToolsLogger.begin(Serial, AudioToolsLogLevel::Info); - - WiFiMulti.addAP("SSID", "passpasspass"); - while (WiFiMulti.run() != WL_CONNECTED) { - delay(100); - } - WiFi.setSleep(false); - Serial.println(WiFi.localIP()); - - // start server - webSocket.begin(); - webSocket.onEvent(webSocketEvent); - - // start i2s - auto cfg = i2s.defaultConfig(TX_MODE); - cfg.copyFrom(info); - i2s.begin(cfg); -} - -void loop() { webSocket.loop(); } \ No newline at end of file diff --git a/examples/sandbox/websockets/server-as-source/client/client.ino b/examples/sandbox/websockets/server-as-source/client/client.ino deleted file mode 100644 index 4d779da163..0000000000 --- a/examples/sandbox/websockets/server-as-source/client/client.ino +++ /dev/null @@ -1,55 +0,0 @@ -/** - * @brief A simple WebSocket client sketch that receives PCM audio data using the - * https://github.com/Links2004/arduinoWebSockets library and outputs it via i2s - * to an AudioKit for easy testing. Replace the output with whatever other class - * you like. - * @author Phil Schatzmann - */ -#include // https://github.com/Links2004/arduinoWebSockets -#include -#include "AudioTools.h" -#include "AudioTools/AudioLibs/AudioBoardStream.h" - -// websocket -WiFiMulti WiFiMulti; -WebSocketsClient webSocket; -// audio -AudioInfo info(44100, 2, 16); -AudioBoardStream i2s(AudioKitEs8388V1); // Access I2S as stream - -// write audio to i2s -void webSocketEvent(WStype_t type, uint8_t* payload, size_t length) { - if (type == WStype_BIN) { - i2s.write(payload, length); - } -} - -void setup() { - Serial.begin(115200); - AudioToolsLogger.begin(Serial, AudioToolsLogLevel::Info); - - // connect to wifk - WiFiMulti.addAP("SSID", "passpasspass"); - while (WiFiMulti.run() != WL_CONNECTED) { - delay(100); - } - WiFi.setSleep(false); - - // start client connecting to server address, port and URL - webSocket.begin("192.168.1.34", 81, "/"); - - // event handler - webSocket.onEvent(webSocketEvent); - - // try ever 5000 again if connection has failed - webSocket.setReconnectInterval(5000); - - // start i2s - auto cfg = i2s.defaultConfig(TX_MODE); - cfg.copyFrom(info); - i2s.begin(cfg); -} - -void loop() { - webSocket.loop(); -} \ No newline at end of file diff --git a/examples/sandbox/websockets/server-as-source/server/server.ino b/examples/sandbox/websockets/server-as-source/server/server.ino deleted file mode 100644 index c3f720c82a..0000000000 --- a/examples/sandbox/websockets/server-as-source/server/server.ino +++ /dev/null @@ -1,53 +0,0 @@ -/** - * @brief A simple WebSocket server sketch using the - * https://github.com/Links2004/arduinoWebSockets library to send out some - * PCM audio data when some clients are connnected. - * The WebSocket API is asynchronous, so we need to slow down the sending - * to the playback speed, to prevent any buffer overflows at the receiver. - * @author Phil Schatzmann - */ - -#include // https://github.com/Links2004/arduinoWebSockets -#include -#include -#include "AudioTools.h" -#include "AudioTools/Communication/WebSocketOutput.h" - -WiFiMulti WiFiMulti; -WebSocketsServer webSocket(81); -WebSocketOutput out(webSocket); - -// audio -AudioInfo info(44100, 2, 16); -SineWaveGenerator sineWave(32000); -GeneratedSoundStream sound(sineWave); -Throttle throttle(out); -StreamCopy copier(throttle, sound); // copies sound into i2s - -void setup() { - // Serial.begin(921600); - Serial.begin(115200); - AudioToolsLogger.begin(Serial, AudioToolsLogLevel::Info); - - // connect to wifi - WiFiMulti.addAP("SSID", "passpasspass"); - while (WiFiMulti.run() != WL_CONNECTED) { - delay(100); - } - WiFi.setSleep(false); - Serial.println(WiFi.localIP()); - - // start server - webSocket.begin(); - - // start sine generation - sineWave.begin(info, N_B4); - throttle.begin(info); - -} - -void loop() { - webSocket.loop(); - // generate audio only when we have any clients - if (webSocket.connectedClients() > 0) copier.copy(); -} \ No newline at end of file diff --git a/examples/tests/basic/24bits-write/24bits-write.ino b/examples/tests/24bits/24bits-write/24bits-write.ino similarity index 78% rename from examples/tests/basic/24bits-write/24bits-write.ino rename to examples/tests/24bits/24bits-write/24bits-write.ino index 34383bf71c..4a1c40afc9 100644 --- a/examples/tests/basic/24bits-write/24bits-write.ino +++ b/examples/tests/24bits/24bits-write/24bits-write.ino @@ -1,8 +1,8 @@ #include "Arduino.h" #include "AudioTools.h" -#include "AudioTools/AudioLibs/AudioBoardStream.h" +#include "AudioLibs/AudioKit.h" -AudioBoardStream out(AudioKitEs8388V1); +AudioKitStream out; //CsvOutput out(Serial); SineWaveGenerator sine_wave; // subclass of SoundGenerator with max amplitude of 32000 GeneratedSoundStream in_stream(sine_wave); // Stream generated from sine wave @@ -11,7 +11,7 @@ StreamCopy copier(out, in_stream); // copies sound to out void setup(){ Serial.begin(115200); delay(2000); - AudioToolsLogger.begin(Serial, AudioToolsLogLevel::Warning); + AudioLogger::instance().begin(Serial, AudioLogger::Warning); auto cfg = out.defaultConfig(); cfg.bits_per_sample = 24; diff --git a/examples/tests/adc/read-csv/read-csv.ino b/examples/tests/adc/read-csv/read-csv.ino deleted file mode 100644 index 3d707a6ef7..0000000000 --- a/examples/tests/adc/read-csv/read-csv.ino +++ /dev/null @@ -1,44 +0,0 @@ -/** - * @file read-csv.ino - * @author Phil Schatzmann - * @brief Test case for the new continuous API of the ESP32: - * Default Pins ESP32: 34, 35 (ADC_CHANNEL_6, ADC_CHANNEL_7) - * Default Pins ESP32C3: 2, 3 (ADC_CHANNEL_2, ADC_CHANNEL_3) - * @version 0.1 - * @date 2023-11-01 - * - * @copyright Copyright (c) 2023 - */ -#include "AudioTools.h" - -typedef int16_t audio_t; -AudioInfo info(44100, 1, 8 * sizeof(audio_t)); -// input -AnalogAudioStream in; -CsvOutput csv(Serial); // ASCII output stream -StreamCopy copy_in(csv, in); - -// Arduino Setup -void setup(void) { - // Open Serial - Serial.begin(115200); - AudioToolsLogger.begin(Serial, AudioToolsLogLevel::Warning); - - - auto cfg_rx = in.defaultConfig(RX_MODE); - // cfg_rx.is_auto_center_read = false; - // cfg_rx.adc_calibration_active = true; - cfg_rx.copyFrom(info); - - in.begin(cfg_rx); - - // open output - csv.begin(info); - - Serial.println("started..."); -} - -// Arduino loop - copy sound to out -void loop() { - copy_in.copy(); -} \ No newline at end of file diff --git a/examples/tests/adc/read-csv_unsigned/read-csv_unsigned.ino b/examples/tests/adc/read-csv_unsigned/read-csv_unsigned.ino deleted file mode 100644 index e4025d2780..0000000000 --- a/examples/tests/adc/read-csv_unsigned/read-csv_unsigned.ino +++ /dev/null @@ -1,43 +0,0 @@ -/** - * @file read-csv.ino - * @author Phil Schatzmann - * @brief Test case for the new continuous API of the ESP32: - * Default Pins ESP32: 34, 35 (ADC_CHANNEL_6, ADC_CHANNEL_7) - * Default Pins ESP32C3: 2, 3 (ADC_CHANNEL_2, ADC_CHANNEL_3) - * @version 0.1 - * @date 2023-11-01 - * - * @copyright Copyright (c) 2023 - */ -#include "AudioTools.h" - -typedef uint16_t audio_t; -AudioInfo info(44100, 1, 8 * sizeof(audio_t)); -// input -AnalogAudioStream in; -CsvOutput csv(Serial); // ASCII output stream -StreamCopy copy_in(csv, in); - -// Arduino Setup -void setup(void) { - // Open Serial - Serial.begin(115200); - AudioToolsLogger.begin(Serial, AudioToolsLogLevel::Warning); - - - auto cfg_rx = in.defaultConfig(RX_MODE); - //cfg_rx.is_auto_center_read = false; - //cfg_rx.adc_calibration_active = true; - cfg_rx.copyFrom(info); - in.begin(cfg_rx); - - // open output - csv.begin(info); - - Serial.println("started..."); -} - -// Arduino loop - copy sound to out -void loop() { - copy_in.copy(); -} \ No newline at end of file diff --git a/examples/tests/adc/read-esp32-multi-channel-csv/read-esp32-multi-channel-csv.ino b/examples/tests/adc/read-esp32-multi-channel-csv/read-esp32-multi-channel-csv.ino deleted file mode 100644 index e12496678e..0000000000 --- a/examples/tests/adc/read-esp32-multi-channel-csv/read-esp32-multi-channel-csv.ino +++ /dev/null @@ -1,89 +0,0 @@ -/** - * @file read-esp32-multi-channel-csv.ino - * @author Urs Utzinger - * @copyright GPLv3 - */ - -// | attenuation | range | accurate range | -// | ------------ | --------| -------------- | -// | ADC_ATTEN_DB_0 | 0..1.1V | 100-950mV | -// | ADC_ATTEN_DB_2_5| 0..1.5V | 100-1250mV | -// | ADC_ATTEN_DB_6 | 0..2.2V | 150-1750mV | -// | ADC_ATTEN_DB_12 | 0..3.9V | 150-2450mV | - -#include "Arduino.h" -#include "AudioTools.h" - -#define NUM_CHANNELS 6 // number of channels -#define DEC_FACTOR 48 // decimating -#define ADC_ATTENUATION ADC_ATTEN_DB_12 // ADC attenuation, see above -#define ADC_BIT_WIDTH 12 // the ADC bit width 9..12, values will be stored in 16 bit integers -#define BIT_DEPTH 16 // default bit width of data returned from analgo_in. Do not change. - -// It looks like Buffer larger than 1024 is capped by stream copy for decimater or csvoutput -#define BUFFER_SIZE_FACTOR (1024/ (NUM_CHANNELS*DEC_FACTOR*BIT_DEPTH/8)) // 3.5 -#define ADC_BUFFER_SIZE (NUM_CHANNELS*DEC_FACTOR*BUFFER_SIZE_FACTOR) // Total number of samples (not bytes), needs to be divisible by 4 // 3*3*96 = 864 -#define BUFFER_SIZE ADC_BUFFER_SIZE*BIT_DEPTH/8 // Number of bytes for processing buffer -#define SERIAL_BUFFER_SIZE BUFFER_SIZE/DEC_FACTOR // There will be factor times less data after decimating - -// Sampling rates -#define SERIAL_SAMPLE_RATE 300 // samples per channel per second -#define SAMPLE_RATE SERIAL_SAMPLE_RATE*DEC_FACTOR // number of samples per channel before decimating -#define ADC_SAMPLE_RATE SAMPLE_RATE*NUM_CHANNELS // actual sampling rate of ADC -// BAUD rate required: SERIAL_SAMPLE_RATE * (NUM_CHANNELS * approx 6 chars + 2 EOL) * 10 approx 40,000 baud - -#define ADC0 ADC_CHANNEL_7 // ADC channel 0 -#define ADC1 ADC_CHANNEL_0 // ADC channel 1 -#define ADC2 ADC_CHANNEL_3 // ADC channel 2 -#define ADC3 ADC_CHANNEL_6 // ADC channel 3 -#define ADC4 ADC_CHANNEL_4 // ADC channel 4 -#define ADC5 ADC_CHANNEL_5 // ADC channel 5 -#define ADC6 ADC_CHANNEL_1 // ADC channel 6, not routed on the ESP32 WROOM module -#define ADC7 ADC_CHANNEL_2 // ADC channel 7, not routed on ESP32 WROOM module -#define ADC8 ADC_CHANNEL_8 // ADC channel 8, only ESP32S3,S2 -#define ADC9 ADC_CHANNEL_9 // ADC channel 9, only ESP32S3,S2 - -AudioInfo info_serial(SERIAL_SAMPLE_RATE, NUM_CHANNELS, BIT_DEPTH); - -AnalogAudioStream analog_in; // on board analog to digital converter -Decimate decimater(DEC_FACTOR, NUM_CHANNELS, BIT_DEPTH); // skip samples -ConverterStream decimated_stream(analog_in, decimater); // pipe the sound to the decimater -CsvOutput serial_out(Serial, NUM_CHANNELS, SERIAL_BUFFER_SIZE); // serial output -StreamCopy copier(serial_out, decimated_stream, BUFFER_SIZE); // stream the decimated output to serial port - -// Arduino Setup -void setup(void) { - - Serial.begin(115200); - while (!Serial); - - // Include logging to serial, If you want no logging comment this line - AudioToolsLogger.begin(Serial, AudioToolsLogLevel::Warning); // Error, Warning, Info, Debug - - auto adcConfig = analog_in.defaultConfig(RX_MODE); - - // For ESP32 by Espressif Systems version 3.0.0 and later: - // see examples/README_ESP32.md - adcConfig.sample_rate = SAMPLE_RATE; // sample rate per channel - adcConfig.channels = NUM_CHANNELS; // up to 6 channels - adcConfig.adc_bit_width = ADC_BIT_WIDTH; // Supports only 12 - adcConfig.buffer_size = ADC_BUFFER_SIZE; // in total number of samples - adcConfig.adc_calibration_active = true; // use and apply the calibration settings of ADC stored in ESP32 - adcConfig.is_auto_center_read = false; // do not engage auto-centering of signal (would subtract average of signal) - adcConfig.adc_attenuation = ADC_ATTENUATION; // set analog input range - adcConfig.adc_channels[0] = ADC0; // ensure configuration for each channel - adcConfig.adc_channels[1] = ADC1; - adcConfig.adc_channels[2] = ADC2; - adcConfig.adc_channels[3] = ADC3; - adcConfig.adc_channels[4] = ADC4; - adcConfig.adc_channels[5] = ADC5; - - analog_in.begin(adcConfig); - serial_out.begin(info_serial); - -} - -// Arduino loop -void loop() { - copier.copy(); -} \ No newline at end of file diff --git a/examples/tests/adc/read-speed/read-speed.ino b/examples/tests/adc/read-speed/read-speed.ino deleted file mode 100644 index 25a51484d9..0000000000 --- a/examples/tests/adc/read-speed/read-speed.ino +++ /dev/null @@ -1,45 +0,0 @@ -/** - * @file read.ino - * @author Phil Schatzmann - * @brief reads the data from the ADC - * Default Pins ESP32: 34, 35 (ADC_CHANNEL_6, ADC_CHANNEL_7) - * Default Pins ESP32C3: 2, 3 (ADC_CHANNEL_2, ADC_CHANNEL_3) - * @version 0.1 - * @date 2023-11-11 - * - * @copyright Copyright (c) 2023 - */ - -#include "AudioTools.h" - -AudioInfo info(44100, 2, 16); -AnalogAudioStream adc; -MeasuringStream out(10, &Serial); -StreamCopy copier(out, adc); - -// Arduino Setup -void setup(void) { - Serial.begin(115200); - AudioToolsLogger.begin(Serial, AudioToolsLogLevel::Info); - -#ifdef ESP32 - LOGI("Supported samples rates: %d - %d", SOC_ADC_SAMPLE_FREQ_THRES_LOW, SOC_ADC_SAMPLE_FREQ_THRES_HIGH); - LOGI("Supported bit width: %d - %d", SOC_ADC_DIGI_MIN_BITWIDTH, SOC_ADC_DIGI_MAX_BITWIDTH); -#endif - - auto cfg = adc.defaultConfig(RX_MODE); - cfg.copyFrom(info); - //cfg.use_apll = false; // try with yes - if (!adc.begin(cfg)) { - LOGE("adc.begin() failed"); - stop(); - } - - // make sure that we have the correct channels set up - out.begin(info); -} - -// Arduino loop - copy data -void loop() { - copier.copy(); -} \ No newline at end of file diff --git a/examples/tests/basic/test-allocator/test-allocator.ino b/examples/tests/basic/test-allocator/test-allocator.ino deleted file mode 100644 index 7002dbf6eb..0000000000 --- a/examples/tests/basic/test-allocator/test-allocator.ino +++ /dev/null @@ -1,25 +0,0 @@ -#include "AudioTools.h" - -struct X { - X() { value = 'a'; } - char value; -}; - -Vector vector{10, DefaultAllocator}; - -void setup() { - Serial.begin(115200); - - int count = 0; - //vector.resize(10); - for (auto &ref : vector) { - Serial.print(ref.value); - assert(ref.value == 'a'); - count++; - } - assert(count == 10); - Serial.println(); - Serial.println("test ok"); -} - -void loop() {} \ No newline at end of file diff --git a/examples/tests/basic/test-buffer/test-buffer.ino b/examples/tests/basic/test-buffer/test-buffer.ino deleted file mode 100644 index 7fd8e7dab7..0000000000 --- a/examples/tests/basic/test-buffer/test-buffer.ino +++ /dev/null @@ -1,50 +0,0 @@ -#include "AudioTools.h" - -// test different buffer implementations - -void test(BaseBuffer& b, const char* title) { - Serial.println(title); - assert(b.isEmpty()); - for (int j = 0; j < 200; j++) { - assert(b.write(j)); - } - assert(b.isFull()); - for (int j = 0; j < 200; j++) { - int16_t v = 0; - assert(b.read(v)); - assert(v == j); - } - assert(b.isEmpty()); - int16_t array[200]; - for (int j = 0; j < 200; j++) { - array[j] = j; - } - b.clear(); - int len = b.writeArray(array, 200); - Serial.println(len); - len = b.readArray(array, 200); - Serial.println(len); - for (int j = 0; j < 200; j++) { - assert(array[j] == j); - } - - Serial.println("Test OK"); -} - -void setup() { - Serial.begin(115200); - AudioToolsLogger.begin(Serial, AudioToolsLogLevel::Info); - - SingleBuffer b1(200); - test(b1, "SingleBuffer"); - - RingBuffer b2(200); - test(b2, "RingBuffer"); - - NBuffer b3(50, 4); - test(b3, "NBuffer"); - - Serial.println("Tests OK"); -} - -void loop() {} \ No newline at end of file diff --git a/examples/tests/basic/test-queue/test-queue.ino b/examples/tests/basic/test-queue/test-queue.ino deleted file mode 100644 index 0726b6f844..0000000000 --- a/examples/tests/basic/test-queue/test-queue.ino +++ /dev/null @@ -1,45 +0,0 @@ -#include "AudioTools.h" -#include "AudioTools/Concurrency/QueueLockFree.h" -#include "AudioTools/Concurrency/RTOS/QueueRTOS.h" - -// test different queue implementations: - -template -void test(T &q, const char* cls){ - Serial.println(cls); - assert(q.empty()); - - for (int j=0;j<10;j++){ - q.enqueue(j); - assert(!q.empty()); - assert(q.size()==j+1); - } - int number; - for (int j=0;j<10;j++){ - q.dequeue(number); - assert(number == j); - assert(q.size()==10-(j+1)); - } - assert(q.empty()); - Serial.println("ok"); -} - -void setup() { - Serial.begin(115200); - - Queue q1; - test>(q1,"Queue"); - - QueueFromVector q2{10, 0}; - test>(q2,"QueueFromVector"); - - QueueLockFree q3(10); - test>(q3,"QueueLockFree"); - - QueueRTOS q4(10); - test>(q4,"QueueRTOS"); - - Serial.println("all tests passed"); -} - -void loop(){} \ No newline at end of file diff --git a/examples/tests/basic/test-vector/test-vector.ino b/examples/tests/basic/test-vector/test-vector.ino deleted file mode 100644 index b53697a3d9..0000000000 --- a/examples/tests/basic/test-vector/test-vector.ino +++ /dev/null @@ -1,151 +0,0 @@ - -#include "AudioTools.h" - -void print(const char *title, Vector &vector) { - Serial.println(title); - for (int j = 0; j < vector.size(); j++) { - Serial.print(vector[j]); - Serial.print(" "); - } - Serial.println(); - Serial.println(); -} - -void testPushBack() { - Vector vector; - for (int j = 0; j < 10; j++) { - vector.push_back(j); - } - print("testPushBack", vector); - for (int j = 0; j < 10; j++) { - assert(vector[j] == j); - } -} - -void testPushFront() { - Vector vector; - for (int j = 0; j < 10; j++) { - vector.push_front(j); - } - print("testPushFront", vector); - - for (int j = 0; j < 10; j++) { - assert(vector[9 - j] == j); - } -} - -void testPopFront() { - Vector vector; - for (int j = 0; j < 10; j++) { - vector.push_back(j); - } - vector.pop_front(); - print("testPopFront", vector); - - assert(vector.size() == 9); - for (int j = 0; j < 9; j++) { - assert(vector[j] == j + 1); - } -} - -void testPopBack() { - Vector vector; - for (int j = 0; j < 10; j++) { - vector.push_back(j); - } - vector.pop_back(); - print("testPopBack", vector); - - assert(vector.size() == 9); - for (int j = 0; j < 9; j++) { - assert(vector[j] == j); - } -} - -void testErase() { - Vector vector; - for (int j = 0; j < 10; j++) { - vector.push_back(j); - } - vector.erase(0); - print("testErase", vector); - - assert(vector.size() == 9); - for (int j = 0; j < 9; j++) { - assert(vector[j] == j + 1); - } -} - -void testCopy() { - Vector vector; - for (int j = 0; j < 10; j++) { - vector.push_back(j); - } - print("testCopy", vector); - for (int j = 0; j < 9; j++) { - assert(vector[j] == j); - } - - Vector v1{vector}; - assert(v1.size()==10); - for (int j = 0; j < 9; j++) { - assert(v1[j] == j); - } - - Vector v2 = vector; - assert(v2.size()==10); - for (int j = 0; j < 9; j++) { - assert(v1[j] == j); - } - - vector.erase(0); - assert(v1.size()==10); - assert(v2.size()==10); -} - -void testArg1(Vector arg){ - print("testArg1", arg); - assert(arg.size()==10); -} - -void testArg2(Vector &arg){ - print("testArg2", arg); - assert(arg.size()==10); -} - - -Vector testArg3(Vector arg){ - print("testArg3", arg); - Vector result = arg; - assert(result.size()==10); - return result; -} - -void testArg() { - Vector vector; - for (int j = 0; j < 10; j++) { - vector.push_back(j); - } - testArg1(vector); - testArg2(vector); - Vector v3 = testArg3(vector); - assert(v3.size()==10); - for (int j = 0; j < 9; j++) { - assert(v3[j] == j); - } -} - - -void setup() { - Serial.begin(115200); - testPushBack(); - testPushFront(); - testPopFront(); - testPopBack(); - testErase(); - testCopy(); - testArg(); - Serial.print("All tests passed"); -} - -void loop() {} \ No newline at end of file diff --git a/examples/tests/etc/test-ringbufferfile/test-ringbufferfile.ino b/examples/tests/buffers/test-ringbufferfile/test-ringbufferfile.ino similarity index 82% rename from examples/tests/etc/test-ringbufferfile/test-ringbufferfile.ino rename to examples/tests/buffers/test-ringbufferfile/test-ringbufferfile.ino index 45c1febb39..20c95b246d 100644 --- a/examples/tests/etc/test-ringbufferfile/test-ringbufferfile.ino +++ b/examples/tests/buffers/test-ringbufferfile/test-ringbufferfile.ino @@ -4,16 +4,15 @@ * @brief genTest for the file backed ringbuffer * @version 0.1 * @date 2023-04-30 - * + * * @copyright Copyright (c) 2022 - * + * */ /** * Test for the file backed ringbuffer */ -#include - #include "AudioTools.h" +#include // SD pins #define PIN_SD_CARD_CS 13 #define PIN_SD_CARD_MISO 2 @@ -25,29 +24,25 @@ SdFs SD; FsFile file; RingBufferFile buffer(file); + void setup() { Serial.begin(115200); - AudioToolsLogger.begin(Serial, AudioToolsLogLevel::Info); + AudioLogger::instance().begin(Serial, AudioLogger::Info); // setup SD - SPI.begin(PIN_SD_CARD_CLK, PIN_SD_CARD_MISO, PIN_SD_CARD_MOSI, - PIN_SD_CARD_CS); + SPI.begin(PIN_SD_CARD_CLK, PIN_SD_CARD_MISO, PIN_SD_CARD_MOSI, PIN_SD_CARD_CS); while (!SD.begin(PIN_SD_CARD_CS, SPI_HALF_SPEED)) { Serial.println("Card Mount Failed"); delay(500); } // create file and setup buffer - file = SD.open(file_name, O_RDWR | O_CREAT); + file = SD.open(file_name, O_RDWR | O_CREAT ); if (!file) { Serial.println("Failed to open file for writing"); return; } - if (!buffer.begin(file)) { - Serial.println("Failed to create buffer"); - return; - } - + // test write for (int j = 0; j < 10; j++) { buffer.write(j); @@ -63,11 +58,9 @@ void setup() { // test read Serial.println("read"); for (int j = 0; j < 10; j++) { - int16_t result; - if (buffer.read(result)) { - Serial.print(result); - Serial.print(" "); - } + int16_t result = buffer.read(); + Serial.print(result); + Serial.print(" "); assert(result == j); } Serial.println(); @@ -77,7 +70,7 @@ void setup() { Serial.print(" "); Serial.println(buffer.available()); memset(tmp, 0, 10 * sizeof(int16_t)); - + int max = buffer.readArray(tmp, buffer.available()); for (int j = 0; j < max; j++) { Serial.print(tmp[j]); diff --git a/examples/tests/codecs/test-codec-aac-fdk-dec/test-codec-aac-fdk-dec.ino b/examples/tests/codecs/test-codec-aac-fdk-dec/test-codec-aac-fdk-dec.ino deleted file mode 100644 index 6f509af461..0000000000 --- a/examples/tests/codecs/test-codec-aac-fdk-dec/test-codec-aac-fdk-dec.ino +++ /dev/null @@ -1,35 +0,0 @@ -// FDK AAC decoding test: psram must be active - -#include "AudioTools.h" -#include "AudioTools/AudioCodecs/CodecAACFDK.h" -#include "AudioTools/AudioLibs/AudioBoardStream.h" - -SET_LOOP_TASK_STACK_SIZE(50 * 1024); - -URLStream url("http://github.com/ssid%22,%22password"); // or replace with ICYStream to get metadata -AudioBoardStream i2s(AudioKitEs8388V1); // final output of decoded stream -EncodedAudioStream dec(&i2s, new AACDecoderFDK()); // Decoding stream -StreamCopy copier(dec, url); // copy url to decoder - - -void setup(){ - Serial.begin(115200); - AudioToolsLogger.begin(Serial, AudioToolsLogLevel::Info); - - // setup i2s - auto config = i2s.defaultConfig(TX_MODE); - config.buffer_size = 1024; - config.buffer_count = 20; - i2s.begin(config); - - // setup I2S based on sampling rate provided by decoder - dec.begin(); - - // aac radio - url.begin("http://peacefulpiano.stream.publicradio.org/peacefulpiano.aac","audio/aac"); - -} - -void loop(){ - copier.copy(); -} diff --git a/examples/tests/codecs/test-codec-aac-fdk/test-codec-aac-fdk.ino b/examples/tests/codecs/test-codec-aac-fdk/test-codec-aac-fdk.ino deleted file mode 100644 index 1f06a44783..0000000000 --- a/examples/tests/codecs/test-codec-aac-fdk/test-codec-aac-fdk.ino +++ /dev/null @@ -1,30 +0,0 @@ -#include "AudioTools.h" -#include "AudioTools/AudioCodecs/CodecAACFDK.h" - -// test case for sine -> aac encoder -> hex output -AudioInfo info(44100,2,16); -AACEncoderFDK fdk; -SineWaveGenerator sineWave; -GeneratedSoundStream in(sineWave); -HexDumpOutput out(Serial); -EncodedAudioStream encoder(&out, &fdk); -StreamCopy copier(encoder, in); - - -void setup() { - Serial.begin(115200); - AudioLogger::instance().begin(Serial,AudioLogger::Warning); - - auto cfg = encoder.defaultConfig(); - cfg.copyFrom(info); - encoder.begin(cfg); - - // start generation of sound - sineWave.begin(info, N_B4); -} - - -// copy the data -void loop() { - copier.copy(); -} \ No newline at end of file diff --git a/examples/tests/codecs/test-codec-adpcm-xq/test-codec-adpcm-xq.ino b/examples/tests/codecs/test-codec-adpcm-xq/test-codec-adpcm-xq.ino index 69fdb5a438..184dd5c291 100644 --- a/examples/tests/codecs/test-codec-adpcm-xq/test-codec-adpcm-xq.ino +++ b/examples/tests/codecs/test-codec-adpcm-xq/test-codec-adpcm-xq.ino @@ -9,22 +9,22 @@ * */ #include "AudioTools.h" -#include "AudioTools/AudioCodecs/CodecADPCMXQ.h" -#include "AudioTools/AudioLibs/AudioBoardStream.h" +#include "AudioCodecs/CodecADPCMXQ.h" +#include "AudioLibs/AudioKit.h" AudioInfo info(44100, 2, 16); SineWaveGenerator sineWave( 32000); // subclass of SoundGenerator with max amplitude of 32000 GeneratedSoundStream sound( sineWave); // Stream generated from sine wave -//AudioBoardStream out(AudioKitEs8388V1); +//AudioKitStream out; //I2SStream out; -AudioBoardStream out(AudioKitEs8388V1); +AudioKitStream out; EncodedAudioStream decoder(&out, new ADPCMDecoderXQ()); // encode and write EncodedAudioStream encoder(&decoder, new ADPCMEncoderXQ()); // encode and write StreamCopy copier(encoder, sound); void setup() { Serial.begin(115200); - AudioToolsLogger.begin(Serial, AudioToolsLogLevel::Warning); + AudioLogger::instance().begin(Serial, AudioLogger::Warning); // start I2S Serial.println("starting I2S..."); diff --git a/examples/tests/codecs/test-codec-adpcm/test-codec-adpcm.ino b/examples/tests/codecs/test-codec-adpcm/test-codec-adpcm.ino index 4bbbf91a37..cf4c0490b3 100644 --- a/examples/tests/codecs/test-codec-adpcm/test-codec-adpcm.ino +++ b/examples/tests/codecs/test-codec-adpcm/test-codec-adpcm.ino @@ -10,23 +10,22 @@ * */ #include "AudioTools.h" -#include "AudioTools/AudioCodecs/CodecADPCM.h" // https://github.com/pschatzmann/adpcm -//#include "AudioTools/AudioLibs/AudioBoardStream.h" // +#include "AudioCodecs/CodecADPCM.h" // https://github.com/pschatzmann/adpcm +#include "AudioLibs/AudioKit.h" AudioInfo info(44100, 2, 16); SineWaveGenerator sineWave( 32000); // subclass of SoundGenerator with max amplitude of 32000 GeneratedSoundStream sound( sineWave); // Stream generated from sine wave +//AudioKitStream out; //I2SStream out; -//AudioBoardStream out(AudioKitEs8388V1); -CsvOutput out(Serial); -AVCodecID id = AV_CODEC_ID_ADPCM_IMA_WAV; -EncodedAudioStream decoder(&out, new ADPCMDecoder(id)); // encode and write -EncodedAudioStream encoder(&decoder, new ADPCMEncoder(id)); // encode and write +AudioKitStream out; +EncodedAudioStream decoder(&out, new ADPCMDecoder(AV_CODEC_ID_ADPCM_IMA_WAV)); // encode and write +EncodedAudioStream encoder(&decoder, new ADPCMEncoder(AV_CODEC_ID_ADPCM_IMA_WAV)); // encode and write StreamCopy copier(encoder, sound); void setup() { Serial.begin(115200); - AudioToolsLogger.begin(Serial, AudioToolsLogLevel::Warning); + AudioLogger::instance().begin(Serial, AudioLogger::Warning); // start I2S Serial.println("starting I2S..."); diff --git a/examples/tests/codecs/test-codec-alac/test-codec-alac.ino b/examples/tests/codecs/test-codec-alac/test-codec-alac.ino deleted file mode 100644 index 4902d6632f..0000000000 --- a/examples/tests/codecs/test-codec-alac/test-codec-alac.ino +++ /dev/null @@ -1,60 +0,0 @@ -/** - * @file test-codec-alac.ino - * @author Phil Schatzmann - * @brief generate sine wave -> encoder -> decoder -> audiokit (i2s) - * @note Activate PSRAM or dicrease the frame size e.g. by adding 1024 to the constructor of the enc_alac and dec_alac - * @version 0.1 - * - * @copyright Copyright (c) 2025 - * - */ -#include "AudioTools.h" -#include "AudioTools/AudioCodecs/CodecALAC.h" -#include "AudioTools/AudioLibs/AudioBoardStream.h" - -// SET_LOOP_TASK_STACK_SIZE(16*1024); // 16KB - not needed - -AudioInfo info(44100, 2, 16); -SineWaveGenerator sineWave( 32000); // subclass of SoundGenerator with max amplitude of 32000 -GeneratedSoundStream sound( sineWave); // Stream generated from sine wave -AudioBoardStream out(AudioKitEs8388V1); -//I2SStream out; -//CsvOutput out(Serial); -EncoderALAC enc_alac; -DecoderALAC dec_alac; -CodecNOP dec_nop; -EncodedAudioStream decoder(&out, &dec_alac); // encode and write -EncodedAudioStream encoder(&decoder, &enc_alac); // encode and write -StreamCopy copier(encoder, sound); - -void setup() { - Serial.begin(115200); - AudioToolsLogger.begin(Serial, AudioToolsLogLevel::Debug); - - // start I2S - Serial.println("starting I2S..."); - auto cfgi = out.defaultConfig(TX_MODE); - cfgi.copyFrom(info); - out.begin(cfgi); - - // Setup sine wave - sineWave.begin(info, N_B4); - - // start encoder - encoder.begin(info); - - // start decoder - decoder.begin(info); - - // optionally copy config from encoder to decoder - // since decoder already has audio info and frames - // dec_alac.setCodecConfig(enc_alac.config()); - // dec_alac.setCodecConfig(enc_alac.binaryConfig()); - - Serial.println("Test started..."); -} - - -void loop() { - copier.copy(); -} diff --git a/examples/tests/codecs/test-codec-aptx/test-codec-aptx.ino b/examples/tests/codecs/test-codec-aptx/test-codec-aptx.ino index 683619248c..0b290d40b9 100644 --- a/examples/tests/codecs/test-codec-aptx/test-codec-aptx.ino +++ b/examples/tests/codecs/test-codec-aptx/test-codec-aptx.ino @@ -9,22 +9,22 @@ * */ #include "AudioTools.h" -#include "AudioTools/AudioCodecs/CodecAPTX.h" -#include "AudioTools/AudioLibs/AudioBoardStream.h" +#include "AudioCodecs/CodecAPTX.h" +#include "AudioLibs/AudioKit.h" AudioInfo info(24000, 2, 16); SineWaveGenerator sineWave( 32000); // subclass of SoundGenerator with max amplitude of 32000 GeneratedSoundStream sound( sineWave); // Stream generated from sine wave -//AudioBoardStream out(AudioKitEs8388V1); +//AudioKitStream out; //I2SStream out; -AudioBoardStream out(AudioKitEs8388V1); +AudioKitStream out; EncodedAudioStream decoder(&out, new APTXDecoder()); // encode and write EncodedAudioStream encoder(&decoder, new APTXEncoder()); // encode and write StreamCopy copier(encoder, sound); void setup() { Serial.begin(115200); - AudioToolsLogger.begin(Serial, AudioToolsLogLevel::Warning); + AudioLogger::instance().begin(Serial, AudioLogger::Warning); // start I2S Serial.println("starting I2S..."); diff --git a/examples/tests/codecs/test-codec-base64/test-codec-base64.ino b/examples/tests/codecs/test-codec-base64/test-codec-base64.ino index 4f33db213c..463e7aac72 100644 --- a/examples/tests/codecs/test-codec-base64/test-codec-base64.ino +++ b/examples/tests/codecs/test-codec-base64/test-codec-base64.ino @@ -9,19 +9,19 @@ * */ #include "AudioTools.h" -#include "AudioTools/AudioLibs/AudioBoardStream.h" +#include "AudioLibs/AudioKit.h" AudioInfo info(8000, 1, 16); SineWaveGenerator sineWave( 32000); // subclass of SoundGenerator with max amplitude of 32000 GeneratedSoundStream sound( sineWave); // Stream generated from sine wave -AudioBoardStream out(AudioKitEs8388V1); +AudioKitStream out; EncodedAudioStream decoder(&out, new DecoderBase64()); // encode and write EncodedAudioStream encoder(&decoder, new EncoderBase64()); // encode and write StreamCopy copier(encoder, sound); void setup() { Serial.begin(115200); - AudioToolsLogger.begin(Serial, AudioToolsLogLevel::Warning); + AudioLogger::instance().begin(Serial, AudioLogger::Warning); // start I2S Serial.println("starting I2S..."); diff --git a/examples/tests/codecs/test-codec-codec2/test-codec-codec2.ino b/examples/tests/codecs/test-codec-codec2/test-codec-codec2.ino index f607b157e9..000a3a4e88 100644 --- a/examples/tests/codecs/test-codec-codec2/test-codec-codec2.ino +++ b/examples/tests/codecs/test-codec-codec2/test-codec-codec2.ino @@ -12,14 +12,14 @@ * */ #include "AudioTools.h" -#include "AudioTools/AudioCodecs/CodecCodec2.h" -#include "AudioTools/AudioLibs/AudioBoardStream.h" +#include "AudioCodecs/CodecCodec2.h" +#include "AudioLibs/AudioKit.h" AudioInfo info(8000, 1, 16); SineWaveGenerator sineWave( 32000); // subclass of SoundGenerator with max amplitude of 32000 GeneratedSoundStream sound( sineWave); // Stream generated from sine wave //CsvOutput out(Serial, channels); -AudioBoardStream out(AudioKitEs8388V1); +AudioKitStream out; EncodedAudioStream decoder(&out, new Codec2Decoder()); // encode and write EncodedAudioStream encoder(&decoder, new Codec2Encoder()); // encode and write StreamCopy copier(encoder, sound); @@ -39,7 +39,7 @@ void loop1(void*) { void setup() { Serial.begin(115200); - AudioToolsLogger.begin(Serial, AudioToolsLogLevel::Warning); + AudioLogger::instance().begin(Serial, AudioLogger::Warning); // Setup sine wave sineWave.begin(info, N_B4); diff --git a/examples/tests/codecs/test-codec-flac/test-codec-flac.ino b/examples/tests/codecs/test-codec-flac/test-codec-flac.ino index f502ec40ba..e0b40872b4 100644 --- a/examples/tests/codecs/test-codec-flac/test-codec-flac.ino +++ b/examples/tests/codecs/test-codec-flac/test-codec-flac.ino @@ -9,8 +9,8 @@ * */ #include "AudioTools.h" -#include "AudioTools/AudioCodecs/CodecFLAC.h" -#include "AudioTools/AudioLibs/AudioBoardStream.h" +#include "AudioCodecs/CodecFLAC.h" +#include "AudioLibs/AudioKit.h" AudioInfo info(44100, 2, 16); SineWaveGenerator sineWave( 32000); // subclass of SoundGenerator with max amplitude of 32000 @@ -21,7 +21,7 @@ StreamCopy copier(encoder, sound); void setup() { Serial.begin(115200); - AudioToolsLogger.begin(Serial, AudioToolsLogLevel::Warning); + AudioLogger::instance().begin(Serial, AudioLogger::Warning); // Setup sine wave sineWave.begin(info, N_B4); diff --git a/examples/tests/codecs/test-codec-g711_alaw/test-codec-g711_alaw.ino b/examples/tests/codecs/test-codec-g711_alaw/test-codec-g711_alaw.ino index fa964596b8..c4e3ec148a 100644 --- a/examples/tests/codecs/test-codec-g711_alaw/test-codec-g711_alaw.ino +++ b/examples/tests/codecs/test-codec-g711_alaw/test-codec-g711_alaw.ino @@ -9,20 +9,20 @@ * */ #include "AudioTools.h" -#include "AudioTools/AudioCodecs/CodecG7xx.h" -#include "AudioTools/AudioLibs/AudioBoardStream.h" +#include "AudioCodecs/CodecG7xx.h" +#include "AudioLibs/AudioKit.h" AudioInfo info(8000, 1, 16); SineWaveGenerator sineWave( 32000); // subclass of SoundGenerator with max amplitude of 32000 GeneratedSoundStream sound( sineWave); // Stream generated from sine wave -AudioBoardStream out(AudioKitEs8388V1); +AudioKitStream out; EncodedAudioStream decoder(&out, new G711_ALAWDecoder()); // encode and write EncodedAudioStream encoder(&decoder, new G711_ALAWEncoder()); // encode and write StreamCopy copier(encoder, sound); void setup() { Serial.begin(115200); - AudioToolsLogger.begin(Serial, AudioToolsLogLevel::Warning); + AudioLogger::instance().begin(Serial, AudioLogger::Warning); // start I2S Serial.println("starting I2S..."); diff --git a/examples/tests/codecs/test-codec-g711_ulaw/test-codec-g711_ulaw.ino b/examples/tests/codecs/test-codec-g711_ulaw/test-codec-g711_ulaw.ino index 67a7c18e99..f6898d9f93 100644 --- a/examples/tests/codecs/test-codec-g711_ulaw/test-codec-g711_ulaw.ino +++ b/examples/tests/codecs/test-codec-g711_ulaw/test-codec-g711_ulaw.ino @@ -9,20 +9,20 @@ * */ #include "AudioTools.h" -#include "AudioTools/AudioCodecs/CodecG7xx.h" -#include "AudioTools/AudioLibs/AudioBoardStream.h" +#include "AudioCodecs/CodecG7xx.h" +#include "AudioLibs/AudioKit.h" AudioInfo info(8000, 1, 16); SineWaveGenerator sineWave( 32000); // subclass of SoundGenerator with max amplitude of 32000 GeneratedSoundStream sound( sineWave); // Stream generated from sine wave -AudioBoardStream out(AudioKitEs8388V1); +AudioKitStream out; EncodedAudioStream decoder(&out, new G711_ULAWDecoder()); // encode and write EncodedAudioStream encoder(&decoder, new G711_ULAWEncoder()); // encode and write StreamCopy copier(encoder, sound); void setup() { Serial.begin(115200); - AudioToolsLogger.begin(Serial, AudioToolsLogLevel::Warning); + AudioLogger::instance().begin(Serial, AudioLogger::Warning); // start I2S Serial.println("starting I2S..."); diff --git a/examples/tests/codecs/test-codec-g721/test-codec-g721.ino b/examples/tests/codecs/test-codec-g721/test-codec-g721.ino index 12ddd5330f..6c25a72488 100644 --- a/examples/tests/codecs/test-codec-g721/test-codec-g721.ino +++ b/examples/tests/codecs/test-codec-g721/test-codec-g721.ino @@ -9,20 +9,20 @@ * */ #include "AudioTools.h" -#include "AudioTools/AudioCodecs/CodecG7xx.h" -#include "AudioTools/AudioLibs/AudioBoardStream.h" +#include "AudioCodecs/CodecG7xx.h" +#include "AudioLibs/AudioKit.h" AudioInfo info(8000, 1, 16); SineWaveGenerator sineWave( 32000); // subclass of SoundGenerator with max amplitude of 32000 GeneratedSoundStream sound( sineWave); // Stream generated from sine wave -AudioBoardStream out(AudioKitEs8388V1); +AudioKitStream out; EncodedAudioStream decoder(&out, new G721Decoder()); // encode and write EncodedAudioStream encoder(&decoder, new G721Encoder()); // encode and write StreamCopy copier(encoder, sound); void setup() { Serial.begin(115200); - AudioToolsLogger.begin(Serial, AudioToolsLogLevel::Warning); + AudioLogger::instance().begin(Serial, AudioLogger::Warning); // start I2S Serial.println("starting I2S..."); diff --git a/examples/tests/codecs/test-codec-g722/test-codec-g722.ino b/examples/tests/codecs/test-codec-g722/test-codec-g722.ino index c501f3587e..eca199b387 100644 --- a/examples/tests/codecs/test-codec-g722/test-codec-g722.ino +++ b/examples/tests/codecs/test-codec-g722/test-codec-g722.ino @@ -9,13 +9,13 @@ * */ #include "AudioTools.h" -#include "AudioTools/AudioCodecs/CodecG722.h" -#include "AudioTools/AudioLibs/AudioBoardStream.h" +#include "AudioCodecs/CodecG722.h" +#include "AudioLibs/AudioKit.h" AudioInfo info(24000, 1, 16); SineWaveGenerator sineWave( 32000); // subclass of SoundGenerator with max amplitude of 32000 GeneratedSoundStream sound( sineWave); // Stream generated from sine wave -AudioBoardStream out(AudioKitEs8388V1); +AudioKitStream out; EncodedAudioStream decoder(&out, new G722Decoder()); // encode and write EncodedAudioStream encoder(&decoder, new G722Encoder()); // encode and write StreamCopy copier(encoder, sound); @@ -23,7 +23,7 @@ StreamCopy copier(encoder, sound); void setup() { Serial.begin(115200); - AudioToolsLogger.begin(Serial, AudioToolsLogLevel::Warning); + AudioLogger::instance().begin(Serial, AudioLogger::Warning); // start I2S Serial.println("starting I2S..."); diff --git a/examples/tests/codecs/test-codec-g723_24/test-codec-g723_24.ino b/examples/tests/codecs/test-codec-g723_24/test-codec-g723_24.ino index 81e0fd8bbf..cc88c58a1e 100644 --- a/examples/tests/codecs/test-codec-g723_24/test-codec-g723_24.ino +++ b/examples/tests/codecs/test-codec-g723_24/test-codec-g723_24.ino @@ -9,20 +9,20 @@ * */ #include "AudioTools.h" -#include "AudioTools/AudioCodecs/CodecG7xx.h" -#include "AudioTools/AudioLibs/AudioBoardStream.h" +#include "AudioCodecs/CodecG7xx.h" +#include "AudioLibs/AudioKit.h" AudioInfo info(8000, 1, 16); SineWaveGenerator sineWave( 32000); // subclass of SoundGenerator with max amplitude of 32000 GeneratedSoundStream sound( sineWave); // Stream generated from sine wave -AudioBoardStream out(AudioKitEs8388V1); +AudioKitStream out; EncodedAudioStream decoder(&out, new G723_24Decoder()); // encode and write EncodedAudioStream encoder(&decoder, new G723_24Encoder()); // encode and write StreamCopy copier(encoder, sound); void setup() { Serial.begin(115200); - AudioToolsLogger.begin(Serial, AudioToolsLogLevel::Warning); + AudioLogger::instance().begin(Serial, AudioLogger::Warning); // start I2S Serial.println("starting I2S..."); diff --git a/examples/tests/codecs/test-codec-g723_40/test-codec-g723_40.ino b/examples/tests/codecs/test-codec-g723_40/test-codec-g723_40.ino index ac268dbf47..3197079ba6 100644 --- a/examples/tests/codecs/test-codec-g723_40/test-codec-g723_40.ino +++ b/examples/tests/codecs/test-codec-g723_40/test-codec-g723_40.ino @@ -9,20 +9,20 @@ * */ #include "AudioTools.h" -#include "AudioTools/AudioCodecs/CodecG7xx.h" -#include "AudioTools/AudioLibs/AudioBoardStream.h" +#include "AudioCodecs/CodecG7xx.h" +#include "AudioLibs/AudioKit.h" AudioInfo info(8000, 1, 16); SineWaveGenerator sineWave( 32000); // subclass of SoundGenerator with max amplitude of 32000 GeneratedSoundStream sound( sineWave); // Stream generated from sine wave -AudioBoardStream out(AudioKitEs8388V1); +AudioKitStream out; EncodedAudioStream decoder(&out, new G723_40Decoder()); // encode and write EncodedAudioStream encoder(&decoder, new G723_40Encoder()); // encode and write StreamCopy copier(encoder, sound); void setup() { Serial.begin(115200); - AudioToolsLogger.begin(Serial, AudioToolsLogLevel::Warning); + AudioLogger::instance().begin(Serial, AudioLogger::Warning); // start I2S Serial.println("starting I2S..."); diff --git a/examples/tests/codecs/test-codec-gsm/test-codec-gsm.ino b/examples/tests/codecs/test-codec-gsm/test-codec-gsm.ino index f79beba075..444a26dadc 100644 --- a/examples/tests/codecs/test-codec-gsm/test-codec-gsm.ino +++ b/examples/tests/codecs/test-codec-gsm/test-codec-gsm.ino @@ -9,20 +9,20 @@ * */ #include "AudioTools.h" -#include "AudioTools/AudioCodecs/CodecGSM.h" -#include "AudioTools/AudioLibs/AudioBoardStream.h" +#include "AudioCodecs/CodecGSM.h" +#include "AudioLibs/AudioKit.h" AudioInfo info(8000, 1, 16); SineWaveGenerator sineWave( 32000); // subclass of SoundGenerator with max amplitude of 32000 GeneratedSoundStream sound( sineWave); // Stream generated from sine wave -AudioBoardStream out(AudioKitEs8388V1); +AudioKitStream out; EncodedAudioStream decoder(&out, new GSMDecoder()); // encode and write EncodedAudioStream encoder(&decoder, new GSMEncoder()); // encode and write StreamCopy copier(encoder, sound); void setup() { Serial.begin(115200); - AudioToolsLogger.begin(Serial, AudioToolsLogLevel::Warning); + AudioLogger::instance().begin(Serial, AudioLogger::Warning); // start I2S Serial.println("starting I2S..."); diff --git a/examples/tests/codecs/test-codec-iLBC/test-codec-iLBC.ino b/examples/tests/codecs/test-codec-iLBC/test-codec-iLBC.ino index 6cc2c1bd5e..74a7e628a9 100644 --- a/examples/tests/codecs/test-codec-iLBC/test-codec-iLBC.ino +++ b/examples/tests/codecs/test-codec-iLBC/test-codec-iLBC.ino @@ -9,13 +9,13 @@ * */ #include "AudioTools.h" -#include "AudioTools/AudioCodecs/CodecILBC.h" -#include "AudioTools/AudioLibs/AudioBoardStream.h" +#include "AudioCodecs/CodecILBC.h" +#include "AudioLibs/AudioKit.h" AudioInfo info(8000, 1, 16); SineWaveGenerator sineWave( 32000); // subclass of SoundGenerator with max amplitude of 32000 GeneratedSoundStream sound( sineWave); // Stream generated from sine wave -AudioBoardStream out(AudioKitEs8388V1); +AudioKitStream out; EncodedAudioStream decoder(&out, new ILBCDecoder()); // encode and write EncodedAudioStream encoder(&decoder, new ILBCEncoder()); // encode and write StreamCopy copier(encoder, sound); @@ -33,7 +33,7 @@ void loop1(void*) { void setup() { Serial.begin(115200); - AudioToolsLogger.begin(Serial, AudioToolsLogLevel::Warning); + AudioLogger::instance().begin(Serial, AudioLogger::Warning); // start I2S Serial.println("starting I2S..."); diff --git a/examples/tests/codecs/test-codec-l8/test-codec-l8.ino b/examples/tests/codecs/test-codec-l8/test-codec-l8.ino index f918bde929..b1f14d2742 100644 --- a/examples/tests/codecs/test-codec-l8/test-codec-l8.ino +++ b/examples/tests/codecs/test-codec-l8/test-codec-l8.ino @@ -9,13 +9,13 @@ * */ #include "AudioTools.h" -#include "AudioTools/AudioCodecs/CodecL8.h" -#include "AudioTools/AudioLibs/AudioBoardStream.h" +#include "AudioCodecs/CodecL8.h" +#include "AudioLibs/AudioKit.h" AudioInfo info(24000, 1, 16); SineWaveGenerator sineWave( 32000); // subclass of SoundGenerator with max amplitude of 32000 GeneratedSoundStream sound( sineWave); // Stream generated from sine wave -AudioBoardStream out(AudioKitEs8388V1); +AudioKitStream out; //I2SStream out; //CsvOutput out(Serial,channels); EncodedAudioStream decoder(&out, new DecoderL8()); // encode and write @@ -24,7 +24,7 @@ StreamCopy copier(encoder, sound); void setup() { Serial.begin(115200); - AudioToolsLogger.begin(Serial, AudioToolsLogLevel::Warning); + AudioLogger::instance().begin(Serial, AudioLogger::Warning); // start I2S Serial.println("starting I2S..."); diff --git a/examples/tests/codecs/test-codec-lc3/test-codec-lc3.ino b/examples/tests/codecs/test-codec-lc3/test-codec-lc3.ino index 49e85e3411..0f01244b9e 100644 --- a/examples/tests/codecs/test-codec-lc3/test-codec-lc3.ino +++ b/examples/tests/codecs/test-codec-lc3/test-codec-lc3.ino @@ -9,13 +9,13 @@ * */ #include "AudioTools.h" -#include "AudioTools/AudioCodecs/CodecLC3.h" -#include "AudioTools/AudioLibs/AudioBoardStream.h" +#include "AudioCodecs/CodecLC3.h" +#include "AudioLibs/AudioKit.h" AudioInfo info(24000, 1, 16); SineWaveGenerator sineWave( 32000); // subclass of SoundGenerator with max amplitude of 32000 GeneratedSoundStream sound( sineWave); // Stream generated from sine wave -AudioBoardStream out(AudioKitEs8388V1); +AudioKitStream out; //I2SStream out; //CsvOutput out(Serial,channels); EncodedAudioStream decoder(&out, new LC3Decoder()); // encode and write @@ -24,7 +24,7 @@ StreamCopy copier(encoder, sound); void setup() { Serial.begin(115200); - AudioToolsLogger.begin(Serial, AudioToolsLogLevel::Warning); + AudioLogger::instance().begin(Serial, AudioLogger::Warning); // start I2S Serial.println("starting I2S..."); diff --git a/examples/tests/codecs/test-codec-opus/test-codec-opus.ino b/examples/tests/codecs/test-codec-opus/test-codec-opus.ino index 926bd6b0e1..61a324faf9 100644 --- a/examples/tests/codecs/test-codec-opus/test-codec-opus.ino +++ b/examples/tests/codecs/test-codec-opus/test-codec-opus.ino @@ -9,13 +9,13 @@ * */ #include "AudioTools.h" -#include "AudioTools/AudioLibs/AudioBoardStream.h" -#include "AudioTools/AudioCodecs/CodecOpus.h" +#include "AudioLibs/AudioKit.h" +#include "AudioCodecs/CodecOpus.h" AudioInfo info(24000, 1, 16); SineWaveGenerator sineWave( 32000); // subclass of SoundGenerator with max amplitude of 32000 GeneratedSoundStream sound( sineWave); // Stream generated from sine wave -AudioBoardStream out(AudioKitEs8388V1); +AudioKitStream out; OpusAudioDecoder dec; OpusAudioEncoder enc; EncodedAudioStream decoder(&out, &dec); // encode and write @@ -24,7 +24,7 @@ StreamCopy copier(encoder, sound); void setup() { Serial.begin(115200); - AudioToolsLogger.begin(Serial, AudioToolsLogLevel::Warning); + AudioLogger::instance().begin(Serial, AudioLogger::Warning); // start I2S Serial.println("starting I2S..."); diff --git a/examples/tests/codecs/test-codec-opusogg/test-codec-opusogg.ino b/examples/tests/codecs/test-codec-opusogg/test-codec-opusogg.ino index c056fe0ba3..f0c277ca70 100644 --- a/examples/tests/codecs/test-codec-opusogg/test-codec-opusogg.ino +++ b/examples/tests/codecs/test-codec-opusogg/test-codec-opusogg.ino @@ -9,13 +9,13 @@ * */ #include "AudioTools.h" -#include "AudioTools/AudioLibs/AudioBoardStream.h" -#include "AudioTools/AudioCodecs/CodecOpusOgg.h" +#include "AudioLibs/AudioKit.h" +#include "AudioCodecs/CodecOpusOgg.h" AudioInfo info(24000, 1, 16); SineWaveGenerator sineWave( 32000); // subclass of SoundGenerator with max amplitude of 32000 GeneratedSoundStream sound( sineWave); // Stream generated from sine wave -AudioBoardStream out(AudioKitEs8388V1); +AudioKitStream out; //CsvOutput out(Serial); OpusOggEncoder enc; OpusOggDecoder dec; @@ -25,7 +25,7 @@ StreamCopy copier(encoder, sound); void setup() { Serial.begin(115200); - AudioToolsLogger.begin(Serial, AudioToolsLogLevel::Warning); + AudioLogger::instance().begin(Serial, AudioLogger::Warning); // Setup output auto cfgo = out.defaultConfig(); diff --git a/examples/tests/codecs/test-codec-sbc/test-codec-sbc.ino b/examples/tests/codecs/test-codec-sbc/test-codec-sbc.ino index 50f1bf87a9..e570ae879a 100644 --- a/examples/tests/codecs/test-codec-sbc/test-codec-sbc.ino +++ b/examples/tests/codecs/test-codec-sbc/test-codec-sbc.ino @@ -9,20 +9,20 @@ * */ #include "AudioTools.h" -#include "AudioTools/AudioCodecs/CodecSBC.h" -#include "AudioTools/AudioLibs/AudioBoardStream.h" +#include "AudioCodecs/CodecSBC.h" +#include "AudioLibs/AudioKit.h" AudioInfo info(44100, 2, 16); SineWaveGenerator sineWave( 32000); // subclass of SoundGenerator with max amplitude of 32000 GeneratedSoundStream sound( sineWave); // Stream generated from sine wave -AudioBoardStream out(AudioKitEs8388V1); +AudioKitStream out; EncodedAudioStream decoder(&out, new SBCDecoder()); // encode and write EncodedAudioStream encoder(&decoder, new SBCEncoder()); // encode and write StreamCopy copier(encoder, sound); void setup() { Serial.begin(115200); - AudioToolsLogger.begin(Serial, AudioToolsLogLevel::Warning); + AudioLogger::instance().begin(Serial, AudioLogger::Warning); // start I2S Serial.println("starting I2S..."); diff --git a/examples/tests/codecs/test-codec-wav-adpcm/test-codec-wav-adpcm.ino b/examples/tests/codecs/test-codec-wav-adpcm/test-codec-wav-adpcm.ino deleted file mode 100644 index b9bed0ea13..0000000000 --- a/examples/tests/codecs/test-codec-wav-adpcm/test-codec-wav-adpcm.ino +++ /dev/null @@ -1,41 +0,0 @@ -#include "AudioTools.h" -#include "AudioTools/AudioCodecs/CodecWAV.h" -#include "AudioTools/AudioCodecs/CodecADPCM.h" - -AudioInfo info(16000, 2, 16); -SineWaveGenerator sineWave( 32000); -GeneratedSoundStream sound( sineWave); -CsvOutput out(Serial); -AVCodecID id = AV_CODEC_ID_ADPCM_IMA_WAV; -ADPCMDecoder adpcm_decoder(id); -ADPCMEncoder adpcm_encoder(id); -WAVDecoder wav_decoder(adpcm_decoder, AudioFormat::ADPCM); -WAVEncoder wav_encoder(adpcm_encoder, AudioFormat::ADPCM); -EncodedAudioStream decoder(&out, &wav_decoder); -EncodedAudioStream encoder(&decoder, &wav_encoder); -StreamCopy copier(encoder, sound); - -void setup() { - Serial.begin(115200); - AudioToolsLogger.begin(Serial, AudioToolsLogLevel::Warning); - - // start Output - auto cfgi = out.defaultConfig(TX_MODE); - cfgi.copyFrom(info); - out.begin(cfgi); - - // Setup sine wave - auto cfgs = sineWave.defaultConfig(); - cfgs.copyFrom(info); - sineWave.begin(info, N_B4); - - // start decoder - decoder.begin(info); - - // start encoder - encoder.begin(info); -} - -void loop() { - copier.copy(); -} diff --git a/examples/tests/codecs/test-compile-all/test-compile-all.ino b/examples/tests/codecs/test-compile-all/test-compile-all.ino deleted file mode 100644 index cb86dce93f..0000000000 --- a/examples/tests/codecs/test-compile-all/test-compile-all.ino +++ /dev/null @@ -1,10 +0,0 @@ -#include "AudioTools.h" -#include "AudioTools/AudioCodecs/All.h" - -void setup() { - -} - -void loop() { - -} diff --git a/examples/tests/codecs/test-container-binary-meta/test-container-binary-meta.ino b/examples/tests/codecs/test-container-binary-meta/test-container-binary-meta.ino index 79d43354f9..563113bc1a 100644 --- a/examples/tests/codecs/test-container-binary-meta/test-container-binary-meta.ino +++ b/examples/tests/codecs/test-container-binary-meta/test-container-binary-meta.ino @@ -11,9 +11,9 @@ * */ #include "AudioTools.h" -#include "AudioTools/AudioCodecs/CodecOpus.h" -#include "AudioTools/AudioCodecs/ContainerBinary.h" -#include "AudioTools/AudioLibs/AudioBoardStream.h" +#include "AudioCodecs/CodecOpus.h" +#include "AudioCodecs/ContainerBinary.h" +#include "AudioLibs/AudioKit.h" struct MetaData { MetaData(const char* typeArg, float vol) { @@ -37,7 +37,7 @@ struct MetaData { char type[5]; }; -void metaCallback(uint8_t* data, int len, void*ref) { +void metaCallback(uint8_t* data, int len) { assert(sizeof(MetaData)==len); MetaData meta((MetaData*)data); meta.log(); @@ -46,7 +46,7 @@ void metaCallback(uint8_t* data, int len, void*ref) { AudioInfo info(8000, 1, 16); SineWaveGenerator sineWave(32000); GeneratedSoundStream sound(sineWave); -AudioBoardStream out(AudioKitEs8388V1); +AudioKitStream out; BinaryContainerDecoder cont_dec(new OpusAudioDecoder()); BinaryContainerEncoder cont_enc(new OpusAudioEncoder()); EncodedAudioStream decoder(&out, &cont_dec); @@ -55,7 +55,7 @@ MetaData meta{"opus", 0.5}; void setup() { Serial.begin(115200); - AudioToolsLogger.begin(Serial, AudioToolsLogLevel::Warning); + AudioLogger::instance().begin(Serial, AudioLogger::Warning); // start I2S Serial.println("starting I2S..."); diff --git a/examples/tests/codecs/test-container-binary/test-container-binary.ino b/examples/tests/codecs/test-container-binary/test-container-binary.ino index 5d8c1ab931..11cd5a9302 100644 --- a/examples/tests/codecs/test-container-binary/test-container-binary.ino +++ b/examples/tests/codecs/test-container-binary/test-container-binary.ino @@ -9,21 +9,21 @@ * */ #include "AudioTools.h" -#include "AudioTools/AudioCodecs/ContainerBinary.h" -#include "AudioTools/AudioCodecs/CodecOpus.h" -#include "AudioTools/AudioLibs/AudioBoardStream.h" +#include "AudioCodecs/ContainerBinary.h" +#include "AudioCodecs/CodecOpus.h" +#include "AudioLibs/AudioKit.h" AudioInfo info(8000,1,16); SineWaveGenerator sineWave( 32000); // subclass of SoundGenerator with max amplitude of 32000 GeneratedSoundStream sound( sineWave); // Stream generated from sine wave -AudioBoardStream out(AudioKitEs8388V1); +AudioKitStream out; EncodedAudioStream decoder(&out, new BinaryContainerDecoder(new OpusAudioDecoder())); EncodedAudioStream encoder(&decoder, new BinaryContainerEncoder(new OpusAudioEncoder())); StreamCopy copier(encoder, sound); void setup() { Serial.begin(115200); - AudioToolsLogger.begin(Serial, AudioToolsLogLevel::Warning); + AudioLogger::instance().begin(Serial, AudioLogger::Warning); // start I2S Serial.println("starting I2S..."); diff --git a/examples/tests/codecs/test-container-ogg/test-container-ogg.ino b/examples/tests/codecs/test-container-ogg/test-container-ogg.ino index ecbe6a1f21..b9c0b20be4 100644 --- a/examples/tests/codecs/test-container-ogg/test-container-ogg.ino +++ b/examples/tests/codecs/test-container-ogg/test-container-ogg.ino @@ -9,14 +9,14 @@ * */ #include "AudioTools.h" -#include "AudioTools/AudioCodecs/ContainerOgg.h" -#include "AudioTools/AudioCodecs/CodecSBC.h" -#include "AudioTools/AudioLibs/AudioBoardStream.h" +#include "AudioCodecs/ContainerOgg.h" +#include "AudioCodecs/CodecSBC.h" +#include "AudioLibs/AudioKit.h" AudioInfo info(16000, 1, 16); SineWaveGenerator sineWave( 32000); // subclass of SoundGenerator with max amplitude of 32000 GeneratedSoundStream sound( sineWave); // Stream generated from sine wave -AudioBoardStream out(AudioKitEs8388V1); +AudioKitStream out; OggContainerEncoder enc(new SBCEncoder()); OggContainerDecoder dec(new SBCDecoder()); //OggContainerEncoder enc; @@ -27,7 +27,7 @@ StreamCopy copier(encoder, sound); void setup() { Serial.begin(115200); - AudioToolsLogger.begin(Serial, AudioToolsLogLevel::Warning); + AudioLogger::instance().begin(Serial, AudioLogger::Warning); // start I2S Serial.println("starting I2S..."); diff --git a/examples/tests/codecs/test-mp3-helix-reading/test-mp3-helix-reading.ino b/examples/tests/codecs/test-mp3-helix-reading/test-mp3-helix-reading.ino index 26fc3dec0c..3d0fdc9d6a 100644 --- a/examples/tests/codecs/test-mp3-helix-reading/test-mp3-helix-reading.ino +++ b/examples/tests/codecs/test-mp3-helix-reading/test-mp3-helix-reading.ino @@ -1,29 +1,29 @@ /** - * @brief Tesing the MP3DecoderHelix: Decoding - * on the input side. + * @brief Tesing the MP3DecoderHelix + * */ // install https://github.com/pschatzmann/arduino-libhelix.git #include "AudioTools.h" -#include "AudioTools/AudioLibs/AudioBoardStream.h" -#include "AudioTools/AudioCodecs/CodecMP3Helix.h" +#include "AudioLibs/AudioKit.h" +#include "AudioCodecs/CodecMP3Helix.h" #include "BabyElephantWalk60_mp3.h" MemoryStream mp3(BabyElephantWalk60_mp3, BabyElephantWalk60_mp3_len); //CsvOutput out(Serial,2); -AudioBoardStream out(AudioKitEs8388V1); +AudioKitStream out; EncodedAudioStream pcm_source(&mp3, new MP3DecoderHelix()); // output to decoder StreamCopy copier(out, pcm_source); void setup(){ Serial.begin(115200); - AudioToolsLogger.begin(Serial, AudioToolsLogLevel::Info); + AudioLogger::instance().begin(Serial, AudioLogger::Info); // setup I2s out.begin(out.defaultConfig()); // make sure that i2s is updated - pcm_source.addNotifyAudioChange(out); + pcm_source.setNotifyAudioChange(out); // setup pcm_source pcm_source.begin(); } diff --git a/examples/tests/codecs/test-mp3-helix/test-mp3-helix.ino b/examples/tests/codecs/test-mp3-helix/test-mp3-helix.ino index 4fd1925e33..63e9a5b84f 100644 --- a/examples/tests/codecs/test-mp3-helix/test-mp3-helix.ino +++ b/examples/tests/codecs/test-mp3-helix/test-mp3-helix.ino @@ -5,13 +5,13 @@ // install https://github.com/pschatzmann/arduino-libhelix.git #include "AudioTools.h" -#include "AudioTools/AudioLibs/AudioBoardStream.h" -#include "AudioTools/AudioCodecs/CodecMP3Helix.h" +#include "AudioLibs/AudioKit.h" +#include "AudioCodecs/CodecMP3Helix.h" #include "BabyElephantWalk60_mp3.h" MemoryStream mp3(BabyElephantWalk60_mp3, BabyElephantWalk60_mp3_len); //CsvOutput out(Serial,2); -AudioBoardStream out(AudioKitEs8388V1); +AudioKitStream out; EncodedAudioStream decoder(&out, new MP3DecoderHelix()); // output to decoder StreamCopy copier(decoder, mp3); @@ -21,7 +21,7 @@ void setup(){ // setup I2s out.begin(out.defaultConfig()); - AudioToolsLogger.begin(Serial, AudioToolsLogLevel::Info); + AudioLogger::instance().begin(Serial, AudioLogger::Info); decoder.begin(); } diff --git a/examples/tests/codecs/test-mp3-mad/test-mp3-mad.ino b/examples/tests/codecs/test-mp3-mad/test-mp3-mad.ino index 31f19d0f4c..564d21ede6 100644 --- a/examples/tests/codecs/test-mp3-mad/test-mp3-mad.ino +++ b/examples/tests/codecs/test-mp3-mad/test-mp3-mad.ino @@ -5,19 +5,19 @@ // install https://github.com/pschatzmann/arduino-libhelix.git #include "AudioTools.h" -#include "AudioTools/AudioLibs/AudioBoardStream.h" -#include "AudioTools/AudioCodecs/CodecMP3MAD.h" +#include "AudioLibs/AudioKit.h" +#include "AudioCodecs/CodecMP3MAD.h" #include "BabyElephantWalk60_mp3.h" MemoryStream mp3(BabyElephantWalk60_mp3, BabyElephantWalk60_mp3_len); //CsvOutput out(Serial,2); -AudioBoardStream out(AudioKitEs8388V1); +AudioKitStream out; EncodedAudioStream decoder(&out, new MP3DecoderMAD()); // output to decoder StreamCopy copier(decoder, mp3); void setup(){ Serial.begin(115200); - AudioToolsLogger.begin(Serial, AudioToolsLogLevel::Info); + AudioLogger::instance().begin(Serial, AudioLogger::Info); // setup I2s out.begin(out.defaultConfig()); diff --git a/examples/tests/codecs/test-mp3_parser/test-mp3_parser.ino b/examples/tests/codecs/test-mp3_parser/test-mp3_parser.ino deleted file mode 100644 index 2551298d6d..0000000000 --- a/examples/tests/codecs/test-mp3_parser/test-mp3_parser.ino +++ /dev/null @@ -1,41 +0,0 @@ - -#include "AudioTools.h" -#include "AudioTools/Disk/AudioSourceSD.h" // or AudioSourceIdxSD.h -#include "AudioTools/AudioLibs/AudioBoardStream.h" // for SD pins - -AudioSourceSD source("/", "", PIN_AUDIO_KIT_SD_CARD_CS); -HeaderParserMP3 mp3; - -void setup() { - Serial.begin(115200); - AudioToolsLogger.begin(Serial, AudioToolsLogLevel::Info); - - SPI.begin(PIN_AUDIO_KIT_SD_CARD_CLK, PIN_AUDIO_KIT_SD_CARD_MISO, PIN_AUDIO_KIT_SD_CARD_MOSI, PIN_AUDIO_KIT_SD_CARD_CS); - while (!SD.begin(PIN_AUDIO_KIT_SD_CARD_CS)) { - Serial.println("SD.begin failed"); - delay(1000); - } - - source.begin(); - -} - -void loop() { - - File* p_file = (File*) source.nextStream(); - if (p_file==nullptr) stop(); - File& file = *p_file; - - uint8_t tmp[1024]; - int len = file.readBytes((char*)tmp, 1024); - - // check if mp3 file - bool is_mp3 = mp3.isValid(tmp, len); - - // print result - Serial.print(" ==> "); - Serial.print(file.name()); - Serial.println(is_mp3 ? " +":" -"); - file.close(); - -} diff --git a/examples/tests/codecs/test-streaming-adapter/test-streaming-adapter.ino b/examples/tests/codecs/test-streaming-adapter/test-streaming-adapter.ino deleted file mode 100644 index 088721532b..0000000000 --- a/examples/tests/codecs/test-streaming-adapter/test-streaming-adapter.ino +++ /dev/null @@ -1,30 +0,0 @@ -#include "AudioTools.h" -#include "AudioTools/AudioCodecs/CodecMP3Helix.h" -#include "AudioTools/AudioLibs/AudioBoardStream.h" - -URLStream url("http://github.com/ssid%22,%22password"); // or replace with ICYStream to get metadata -AudioBoardStream i2s(AudioKitEs8388V1); // final output of decoded stream -MP3DecoderHelix helix; -StreamingDecoderAdapter decoder(helix); - -void setup(){ - Serial.begin(115200); - AudioToolsLogger.begin(Serial, AudioToolsLogLevel::Info); - - // setup i2s - auto config = i2s.defaultConfig(TX_MODE); - i2s.begin(config); - - // setup I2S based on sampling rate provided by decoder - decoder.setInput(url); - decoder.setOutput(i2s); - decoder.begin(); - - // mp3 radio - url.begin("http://stream.srg-ssr.ch/m/rsj/mp3_128","audio/mp3"); - -} - -void loop(){ - decoder.copy(); -} \ No newline at end of file diff --git a/examples/tests/concurrency/BufferRTOS/BufferRTOS.ino b/examples/tests/concurrency/BufferRTOS/BufferRTOS.ino deleted file mode 100644 index 25e9b5d4a2..0000000000 --- a/examples/tests/concurrency/BufferRTOS/BufferRTOS.ino +++ /dev/null @@ -1,67 +0,0 @@ -/** - * @file synchBufferRTOS.ino - * @author Phil Schatzmann - * @brief Multitask with BufferRTOS - * @version 0.1 - * @date 2022-11-25 - * - * @copyright Copyright (c) 2022 - * - */ -#include "AudioTools.h" -#include "AudioTools/Concurrency/RTOS.h" - -BufferRTOS buffer(512 * 10); - -Task writeTask("write", 3000, 10, 0); - -Task readTask("read", 3000, 10, 1); - -void setup() { - Serial.begin(115200); - - writeTask.begin([]() { - int16_t data[512]; - for (int j = 0; j < 512; j++) { - data[j] = j; - } - - size_t len = buffer.writeArray(data, 512); - - }); - - readTask.begin([]() { - static uint64_t start = millis(); - static size_t total_bytes = 0; - static size_t errors = 0; - static int16_t data[512]; - - // read data - size_t read = buffer.readArray(data, 512); - assert(read==512); - - // check data - for (int j = 0; j < 512; j++) { - if (data[j] != j) errors++; - } - // calculate bytes per second - total_bytes += sizeof(int16_t) * 512; - if (total_bytes >= 1024000) { - uint64_t duration = millis() - start; - float mbps = static_cast(total_bytes) / duration / 1000.0; - - // print result - Serial.print("Mbytes per second: "); - Serial.print(mbps); - Serial.print(" with "); - Serial.print(errors); - Serial.println(" errors"); - - start = millis(); - errors = 0; - total_bytes = 0; - } - }); -} - -void loop() { delay(1000); } \ No newline at end of file diff --git a/examples/tests/concurrency/NBuffer/NBuffer.ino b/examples/tests/concurrency/NBuffer/NBuffer.ino index a5ab24e705..3dcb9ff1f9 100644 --- a/examples/tests/concurrency/NBuffer/NBuffer.ino +++ b/examples/tests/concurrency/NBuffer/NBuffer.ino @@ -4,66 +4,64 @@ * @brief multi tasks test with unsynchronized NBuffer * @version 0.1 * @date 2022-11-25 - * + * * @copyright Copyright (c) 2022 - * + * */ #include "AudioTools.h" +#include "freertos-all.h" -NBuffer buffer(512, 4); +NBuffer buffer(512,4); -Task writeTask("write", 3000, 10, 0); - -Task readTask("read", 3000, 10, 1); - -void setup() { - Serial.begin(115200); - - writeTask.begin([]() { +Task writeTask("write",3000,10, [](){ int16_t data[512]; - for (int j = 0; j < 512; j++) { - data[j] = j; + for (int j=0;j<512;j++){ + data[j]=j; } - assert(buffer.writeArray(data, 512)==512); - // prevent watchdog - delay(1); + buffer.writeArray(data, 512); +}); - }); - readTask.begin([]() { +Task readTask("read",3000,10, [](){ static uint64_t start = millis(); - static size_t total_bytes = 0; - static size_t errors = 0; + static size_t total_bytes=0; + static size_t errors=0; static int16_t data[512]; // read data - size_t read = buffer.readArray(data, 512); - delay(1); - if (read==0) return; - assert(read==512); + buffer.readArray(data, 512); // check data - for (int j = 0; j < 512; j++) { - if (data[j] != j) errors++; + for (int j=0;j<512;j++){ + if (data[j]!=j) + errors++; } // calculate bytes per second - total_bytes += sizeof(int16_t) * 512; - if (total_bytes >= 1024000) { - uint64_t duration = millis() - start; - float mbps = static_cast(total_bytes) / duration / 1000.0; + total_bytes+=sizeof(int16_t)*512; + if (total_bytes>=1024000){ + uint64_t duration = millis()-start; + float mbps = static_cast(total_bytes) / duration / 1000.0; - // print result - Serial.print("Mbytes per second: "); - Serial.print(mbps); - Serial.print(" with "); - Serial.print(errors); - Serial.println(" errors"); + // print result + Serial.print("Mbytes per second: "); + Serial.print(mbps); + Serial.print(" with "); + Serial.print(errors); + Serial.println(" errors"); - start = millis(); - errors = 0; - total_bytes = 0; + start = millis(); + errors = 0; + total_bytes = 0; } - }); +}); + + +void setup(){ + Serial.begin(115200); + writeTask.Start(0); + readTask.Start(1); } -void loop() { delay(1000); } \ No newline at end of file +void loop(){ + delay(1000); +} \ No newline at end of file diff --git a/examples/tests/concurrency/audio-test/audio-test.ino b/examples/tests/concurrency/audio-test/audio-test.ino deleted file mode 100644 index dd15f6c8ee..0000000000 --- a/examples/tests/concurrency/audio-test/audio-test.ino +++ /dev/null @@ -1,50 +0,0 @@ - -#include "AudioTools.h" -#include "AudioTools/AudioLibs/AudioBoardStream.h" -#include "AudioTools/Concurrency/RTOS.h" - -AudioInfo info(44100, 2, 16); -// source and sink -SineWaveGenerator sineWave(32000); -GeneratedSoundStream sound(sineWave); -AudioBoardStream out(AudioKitEs8388V1); -// queue -// SynchronizedNBuffer buffer(1024, 10); -BufferRTOS buffer(1024 * 10); -QueueStream queue(buffer); -// copy -StreamCopy copierSource(queue, sound); -StreamCopy copierSink(out, queue); -// tasks -Task writeTask("write", 3000, 10, 0); -Task readTask("read", 3000, 10, 1); - -void setup() { - Serial.begin(115200); - AudioToolsLogger.begin(Serial, AudioToolsLogLevel::Warning); - // start Queue - queue.begin(); - - // start I2S - Serial.println("starting I2S..."); - auto config = out.defaultConfig(TX_MODE); - config.copyFrom(info); - out.begin(config); - - // Setup sine wave - sineWave.begin(info, N_B4); - Serial.println("started..."); - - writeTask.begin([]() { - copierSource.copy(); - }); - - readTask.begin([]() { - copierSink.copy(); - }); - - Serial.println("started..."); - -} - -void loop() { delay(1000); } \ No newline at end of file diff --git a/examples/tests/concurrency/synchBufferRTOS/synchBufferRTOS.ino b/examples/tests/concurrency/synchBufferRTOS/synchBufferRTOS.ino new file mode 100644 index 0000000000..ba992ca20d --- /dev/null +++ b/examples/tests/concurrency/synchBufferRTOS/synchBufferRTOS.ino @@ -0,0 +1,67 @@ +/** + * @file synchBufferRTOS.ino + * @author Phil Schatzmann + * @brief Multitask with SynchronizedBufferRTOS + * @version 0.1 + * @date 2022-11-25 + * + * @copyright Copyright (c) 2022 + * + */ +#include "AudioTools.h" +#include "freertos-all.h" + +SynchronizedBufferRTOS buffer(1024,512); + +Task writeTask("write",3000,10, [](){ + int16_t data[512]; + for (int j=0;j<512;j++){ + data[j]=j; + } + buffer.writeArray(data, 512); +}); + +Task readTask("read",3000,10, [](){ + static uint64_t start = millis(); + static size_t total_bytes=0; + static size_t errors=0; + static int16_t data[512]; + + // read data + buffer.readArray(data, 512); + + // check data + for (int j=0;j<512;j++){ + if (data[j]!=j) + errors++; + } + // calculate bytes per second + total_bytes+=sizeof(int16_t)*512; + if (total_bytes>=1024000){ + uint64_t duration = millis()-start; + float mbps = static_cast(total_bytes) / duration / 1000.0; + + // print result + Serial.print("Mbytes per second: "); + Serial.print(mbps); + Serial.print(" with "); + Serial.print(errors); + Serial.println(" errors"); + + start = millis(); + errors = 0; + total_bytes = 0; + } +}); + +void setup(){ + Serial.begin(115200); + AudioLogger::instance().begin(Serial, AudioLogger::Info); + + writeTask.Start(0); + readTask.Start(1); +} + +void loop(){ + delay(1000); +} \ No newline at end of file diff --git a/examples/tests/concurrency/synchNBuffer/synchNBuffer.ino b/examples/tests/concurrency/synchNBuffer/synchNBuffer.ino index da6044ec32..d98d9ff383 100644 --- a/examples/tests/concurrency/synchNBuffer/synchNBuffer.ino +++ b/examples/tests/concurrency/synchNBuffer/synchNBuffer.ino @@ -4,66 +4,66 @@ * @brief Multitask with SynchronizedBuffer using NBuffer * @version 0.1 * @date 2022-11-25 - * + * * @copyright Copyright (c) 2022 - * + * */ #include "AudioTools.h" -#include "AudioTools/Concurrency/RTOS.h" +#include "freertos-all.h" -MutexRTOS mutex; -NBuffer nbuffer(512, 10); +audio_tools::Mutex mutex; +NBuffer nbuffer(512,8); SynchronizedBuffer buffer(nbuffer, mutex); -Task writeTask("write", 3000, 10, 0); - -Task readTask("read", 3000, 10, 1); - -void setup() { - Serial.begin(115200); - - writeTask.begin([]() { +Task writeTask("write",3000,10, [](){ int16_t data[512]; - for (int j = 0; j < 512; j++) { - data[j] = j; + for (int j=0;j<512;j++){ + data[j]=j; } - assert(buffer.writeArray(data, 512)==512); - delay(1); + buffer.writeArray(data, 512); +}); - }); - readTask.begin([]() { +Task readTask("read",3000,10, [](){ static uint64_t start = millis(); - static size_t total_bytes = 0; - static size_t errors = 0; + static size_t total_bytes=0; + static size_t errors=0; static int16_t data[512]; // read data - assert(buffer.readArray(data, 512)==512); - delay(1); + buffer.readArray(data, 512); // check data - for (int j = 0; j < 512; j++) { - if (data[j] != j) errors++; + for (int j=0;j<512;j++){ + if (data[j]!=j) + errors++; } // calculate bytes per second - total_bytes += sizeof(int16_t) * 512; - if (total_bytes >= 1024000) { - uint64_t duration = millis() - start; - float mbps = static_cast(total_bytes) / duration / 1000.0; + total_bytes+=sizeof(int16_t)*512; + if (total_bytes>=1024000){ + uint64_t duration = millis()-start; + float mbps = static_cast(total_bytes) / duration / 1000.0; - // print result - Serial.print("Mbytes per second: "); - Serial.print(mbps); - Serial.print(" with "); - Serial.print(errors); - Serial.println(" errors"); + // print result + Serial.print("Mbytes per second: "); + Serial.print(mbps); + Serial.print(" with "); + Serial.print(errors); + Serial.println(" errors"); - start = millis(); - errors = 0; - total_bytes = 0; + start = millis(); + errors = 0; + total_bytes = 0; } - }); +}); + + +void setup(){ + Serial.begin(115200); + writeTask.Start(0); + readTask.Start(1); } -void loop() { delay(1000); } \ No newline at end of file +void loop(){ + delay(1000); +} \ No newline at end of file diff --git a/examples/tests/concurrency/synchRingBuffer/synchRingBuffer.ino b/examples/tests/concurrency/synchRingBuffer/synchRingBuffer.ino index 440a5faec9..f156404657 100644 --- a/examples/tests/concurrency/synchRingBuffer/synchRingBuffer.ino +++ b/examples/tests/concurrency/synchRingBuffer/synchRingBuffer.ino @@ -4,66 +4,66 @@ * @brief Multitask with SynchronizedBuffer using RingBuffer * @version 0.1 * @date 2022-11-25 - * + * * @copyright Copyright (c) 2022 - * + * */ #include "AudioTools.h" -#include "AudioTools/Concurrency/RTOS.h" +#include "freertos-all.h" -MutexRTOS mutex; -RingBuffer nbuffer(512 * 4); +audio_tools::Mutex mutex; +RingBuffer nbuffer(512*4); SynchronizedBuffer buffer(nbuffer, mutex); -Task writeTask("write", 3000, 10, 0); - -Task readTask("read", 3000, 10, 1); - -void setup() { - Serial.begin(115200); - - writeTask.begin([]() { +Task writeTask("write",3000,10, [](){ int16_t data[512]; - for (int j = 0; j < 512; j++) { - data[j] = j; + for (int j=0;j<512;j++){ + data[j]=j; } - assert(buffer.writeArray(data, 512)==512); - delay(1); + buffer.writeArray(data, 512); +}); - }); - readTask.begin([]() { +Task readTask("read",3000,10, [](){ static uint64_t start = millis(); - static size_t total_bytes = 0; - static size_t errors = 0; + static size_t total_bytes=0; + static size_t errors=0; static int16_t data[512]; // read data - assert(buffer.readArray(data, 512)==512); - delay(1); + buffer.readArray(data, 512); // check data - for (int j = 0; j < 512; j++) { - if (data[j] != j) errors++; + for (int j=0;j<512;j++){ + if (data[j]!=j) + errors++; } // calculate bytes per second - total_bytes += sizeof(int16_t) * 512; - if (total_bytes >= 1024000) { - uint64_t duration = millis() - start; - float mbps = static_cast(total_bytes) / duration / 1000.0; + total_bytes+=sizeof(int16_t)*512; + if (total_bytes>=1024000){ + uint64_t duration = millis()-start; + float mbps = static_cast(total_bytes) / duration / 1000.0; - // print result - Serial.print("Mbytes per second: "); - Serial.print(mbps); - Serial.print(" with "); - Serial.print(errors); - Serial.println(" errors"); + // print result + Serial.print("Mbytes per second: "); + Serial.print(mbps); + Serial.print(" with "); + Serial.print(errors); + Serial.println(" errors"); - start = millis(); - errors = 0; - total_bytes = 0; + start = millis(); + errors = 0; + total_bytes = 0; } - }); +}); + + +void setup(){ + Serial.begin(115200); + writeTask.Start(0); + readTask.Start(1); } -void loop() { delay(1000); } \ No newline at end of file +void loop(){ + delay(1000); +} \ No newline at end of file diff --git a/examples/tests/conversion/channel-converter-avg/channel-converter-avg.ino b/examples/tests/conversion/channel-converter-avg/channel-converter-avg.ino deleted file mode 100644 index 7c508004c7..0000000000 --- a/examples/tests/conversion/channel-converter-avg/channel-converter-avg.ino +++ /dev/null @@ -1,48 +0,0 @@ -/** - * @file channel-converter-avg.ino - * @brief Test calculating pairwise average of channels - * @author Urs Utzinger - * @copyright GPLv3 - **/ - -#include "AudioTools.h" - -AudioInfo info1(44100, 1, 16); -AudioInfo info2(44100, 2, 16); -SineWaveGenerator sineWave1(32000); // subclass of SoundGenerator with max amplitude of 32000 -SineWaveGenerator sineWave2(32000); // subclass of SoundGenerator with max amplitude of 32000 -GeneratedSoundStream sound1(sineWave1); // stream generated from sine wave1 -GeneratedSoundStream sound2(sineWave2); // stream generated from sine wave2 -InputMerge imerge; // merge two inputs to stereo -ChannelAvg averager; // channel averager -ConverterStream averaged_stream(imerge, averager); // pipe the sound to the averager -CsvOutput serial_out(Serial); // serial output -StreamCopy copier(serial_out, averaged_stream); // stream the binner output to serial port - -// Arduino Setup -void setup(void) { - - // Open Serial - Serial.begin(115200); - while(!Serial); // wait for Serial to be ready - - AudioToolsLogger.begin(Serial, AudioToolsLogLevel::Warning); - - // Setup sine waves - sineWave1.begin(info1, N_B4); - sineWave2.begin(info1, N_B5); - - // Merge input to stereo - imerge.add(sound1, 1); - imerge.add(sound2, 1); - imerge.begin(info2); - - // Define CSV Output - serial_out.begin(info1); - -} - -// Arduino loop - copy sound to out with conversion -void loop() { - copier.copy(); -} \ No newline at end of file diff --git a/examples/tests/conversion/channel-converter-bin/channel-converter-bin.ino b/examples/tests/conversion/channel-converter-bin/channel-converter-bin.ino deleted file mode 100644 index 5f545658d2..0000000000 --- a/examples/tests/conversion/channel-converter-bin/channel-converter-bin.ino +++ /dev/null @@ -1,57 +0,0 @@ -/** - * @file channel-converter-avg.ino - * @brief Test calculating average of two channels - * @author Urs Utzinger - * @copyright GPLv3 - **/ - -#include "AudioTools.h" - -#define BINSIZE 4 - -AudioInfo info1(44100, 1, 16); -AudioInfo info2(44100, 2, 16); -AudioInfo info_out(44100/BINSIZE, 2, 16); -SineWaveGenerator sineWave1(32000); // subclass of SoundGenerator with max amplitude of 32000 -SineWaveGenerator sineWave2(32000); // subclass of SoundGenerator with max amplitude of 32000 -GeneratedSoundStream sound1(sineWave1); // stream generated from sine wave1 -GeneratedSoundStream sound2(sineWave2); // stream generated from sine wave2 -InputMerge imerge; // merge two inputs to stereo -Bin binner; // channel averager -ConverterStream binned_stream(imerge, binner); // pipe the sound to the averager -CsvOutput serial_out(Serial); // serial output -StreamCopy copier(serial_out, binned_stream); // stream the binner output to serial port - -// Arduino Setup -void setup(void) { - - // Open Serial - Serial.begin(115200); - while(!Serial); // wait for Serial to be ready - - AudioToolsLogger.begin(Serial, AudioToolsLogLevel::Warning); - - // Setup sine waves - sineWave1.begin(info1, N_B4); - sineWave2.begin(info1, N_B5); - - // Merge input to stereo - imerge.add(sound1, 1); - imerge.add(sound2, 1); - imerge.begin(info2); - - // Configure binning - binner.setChannels(2); - binner.setBits(16); - binner.setBinSize(BINSIZE); - binner.setAverage(true); - - // Define CSV Output - serial_out.begin(info_out); - -} - -// Arduino loop - copy sound to out with conversion -void loop() { - copier.copy(); -} \ No newline at end of file diff --git a/examples/tests/conversion/channel-converter-bindiff/channel-converter-bindiff.ino b/examples/tests/conversion/channel-converter-bindiff/channel-converter-bindiff.ino deleted file mode 100644 index 7fd41118b1..0000000000 --- a/examples/tests/conversion/channel-converter-bindiff/channel-converter-bindiff.ino +++ /dev/null @@ -1,56 +0,0 @@ -/** - * @file channel-converter-bin-diff.ino - * @author Urs Utzinger - * @brief On two channels reduce number of samples by binning, then compute difference between two channels - * @copyright GPLv3 - **/ - -#include "AudioTools.h" - -#define BINSIZE 4 - -AudioInfo info1(44100, 1, 16); -AudioInfo info2(44100, 2, 16); -AudioInfo info_out(44100/BINSIZE, 1, 16); -SineWaveGenerator sineWave1(16000); // subclass of SoundGenerator with max amplitude of 32000 -SineWaveGenerator sineWave2(16000); // subclass of SoundGenerator with max amplitude of 32000 -GeneratedSoundStream sound1(sineWave1); // stream generated from sine wave -GeneratedSoundStream sound2(sineWave2); // stream generated from sine wave -InputMerge imerge; -ChannelBinDiff bindiffer; // Binning each channel by average length, setup see below -ConverterStream converted_stream(imerge, bindiffer); // pipe the merged sound to the converter -CsvOutput serial_out(Serial); // serial output -StreamCopy copier(serial_out, converted_stream); // stream the binner output to serial port - -// Arduino Setup -void setup(void) { - // Open Serial - Serial.begin(115200); - while(!Serial); - - AudioToolsLogger.begin(Serial, AudioToolsLogLevel::Debug); // Info, Warning, Error, Debug - - // Setup sine wave - sineWave1.begin(info1, N_B4); - sineWave2.begin(info1, N_B5); - - // Merge input to stereo - imerge.add(sound1, 1); - imerge.add(sound2, 1); - imerge.begin(info2); - - // Setup binning - bindiffer.setChannels(2); - bindiffer.setBits(16); - bindiffer.setBinSize(BINSIZE); - bindiffer.setAverage(true); - - // Define CSV Output - serial_out.begin(info_out); - -} - -// Arduino loop - copy sound to out with conversion -void loop() { - copier.copy(); -} \ No newline at end of file diff --git a/examples/tests/conversion/channel-converter-decimate/channel-converter-decimate.ino b/examples/tests/conversion/channel-converter-decimate/channel-converter-decimate.ino deleted file mode 100644 index 54fe66f92a..0000000000 --- a/examples/tests/conversion/channel-converter-decimate/channel-converter-decimate.ino +++ /dev/null @@ -1,48 +0,0 @@ -/** - * @file channel-converter-decimate.ino - * @author Urs Utzinger - * @brief Reduce samples by binning; which is summing consecutive samples and optionally dividing by the number of samples summed. - * @copyright GPLv3 - **/ - -#include "AudioTools.h" -#define FACTOR 4 - -AudioInfo info1(44100, 1, 16); -AudioInfo info2(44100, 2, 16); -AudioInfo info_out(44100/FACTOR, 2, 16); -SineWaveGenerator sineWave1(16000); // subclass of SoundGenerator with max amplitude of 16000 -SineWaveGenerator sineWave2(16000); // subclass of SoundGenerator with max amplitude of 16000 -GeneratedSoundStream sound1(sineWave1); // stream generated from sine wave -GeneratedSoundStream sound2(sineWave2); // stream generated from sine wave -InputMerge imerge(sound1,sound2); -Decimate decimater(FACTOR, 2, 16); // decimate by FACTOR on each channel -ConverterStream decimated_stream(imerge, decimater); // pipe the sounds to the decimater -CsvOutput serial_out(Serial); // serial output -StreamCopy copier(serial_out, decimated_stream); // stream the decimater output to serial port - -// Arduino Setup -void setup(void) { - - // Open Serial - Serial.begin(115200); - while(!Serial); - - AudioToolsLogger.begin(Serial, AudioToolsLogLevel::Warning); - - // Setup sine wave - sineWave1.begin(info1, N_B4); - sineWave2.begin(info1, N_B5); - - // Merge input to stereo - imerge.begin(info2); - - // Define CSV Output - serial_out.begin(info_out); - - } - -// Arduino loop - copy sound to out with conversion -void loop() { - copier.copy(); -} diff --git a/examples/tests/conversion/channel-converter-diff/channel-converter-diff.ino b/examples/tests/conversion/channel-converter-diff/channel-converter-diff.ino deleted file mode 100644 index 3287a55f3f..0000000000 --- a/examples/tests/conversion/channel-converter-diff/channel-converter-diff.ino +++ /dev/null @@ -1,48 +0,0 @@ -/** - * @file channel-converter-diff.ino - * @brief Test calculating parwise difference of channels - * @author Urs Utzinger - * @copyright GPLv3 - **/ - -#include "AudioTools.h" - -AudioInfo info1(44100, 1, 16); -AudioInfo info2(44100, 2, 16); -SineWaveGenerator sineWave1(16000); // subclass of SoundGenerator with max amplitude of 32000 -SineWaveGenerator sineWave2(16000); // subclass of SoundGenerator with max amplitude of 32000 -GeneratedSoundStream sound1(sineWave1); // stream generated from sine wave1 -GeneratedSoundStream sound2(sineWave2); // stream generated from sine wave2 -InputMerge imerge; // merge two inputs to stereo -ChannelDiff differ; // channel averager -ConverterStream diffed_stream(imerge, differ); // pipe the sound to the averager -CsvOutput serial_out(Serial); // serial output -StreamCopy copier(serial_out, diffed_stream); // stream the binner output to serial port - -// Arduino Setup -void setup(void) { - - // Open Serial - Serial.begin(115200); - while(!Serial); // wait for Serial to be ready - - AudioToolsLogger.begin(Serial, AudioToolsLogLevel::Warning); - - // Setup sine waves - sineWave1.begin(info1, N_B4); - sineWave2.begin(info1, N_B5); - - // Merge input to stereo - imerge.add(sound1, 1); - imerge.add(sound2, 1); - imerge.begin(info2); - - // Define CSV Output - serial_out.begin(info1); - -} - -// Arduino loop - copy sound to out with conversion -void loop() { - copier.copy(); -} \ No newline at end of file diff --git a/examples/tests/conversion/channel-converter-increase-in/channel-converter-increase-in.ino b/examples/tests/conversion/channel-converter-increase-in/channel-converter-increase-in.ino index 2298141a01..ba785881fe 100644 --- a/examples/tests/conversion/channel-converter-increase-in/channel-converter-increase-in.ino +++ b/examples/tests/conversion/channel-converter-increase-in/channel-converter-increase-in.ino @@ -10,7 +10,7 @@ StreamCopy copier(out, conv); // copies co void setup(){ Serial.begin(115200); - AudioToolsLogger.begin(Serial, AudioToolsLogLevel::Info); + AudioLogger::instance().begin(Serial, AudioLogger::Info); sine_wave.begin(info, N_B4); conv.begin(info, to_channels); diff --git a/examples/tests/conversion/channel-converter-increase-out/channel-converter-increase-out.ino b/examples/tests/conversion/channel-converter-increase-out/channel-converter-increase-out.ino index 1c98b80ece..3c451b9c8c 100644 --- a/examples/tests/conversion/channel-converter-increase-out/channel-converter-increase-out.ino +++ b/examples/tests/conversion/channel-converter-increase-out/channel-converter-increase-out.ino @@ -10,7 +10,7 @@ StreamCopy copier(conv, in_stream); // copies s void setup(){ Serial.begin(115200); - AudioToolsLogger.begin(Serial, AudioToolsLogLevel::Info); + AudioLogger::instance().begin(Serial, AudioLogger::Info); sine_wave.begin(info, N_B4); conv.begin(info, to_channels); diff --git a/examples/tests/conversion/channel-converter-reduce-in/channel-converter-reduce-in.ino b/examples/tests/conversion/channel-converter-reduce-in/channel-converter-reduce-in.ino index fc794afbae..ec52394249 100644 --- a/examples/tests/conversion/channel-converter-reduce-in/channel-converter-reduce-in.ino +++ b/examples/tests/conversion/channel-converter-reduce-in/channel-converter-reduce-in.ino @@ -10,7 +10,7 @@ StreamCopy copier(out, conv); // copies sound t void setup(){ Serial.begin(115200); - AudioToolsLogger.begin(Serial, AudioToolsLogLevel::Info); + AudioLogger::instance().begin(Serial, AudioLogger::Info); sine_wave.begin(info, N_B4); conv.begin(info, to_channels); diff --git a/examples/tests/conversion/channel-converter-reduce-out/channel-converter-reduce-out.ino b/examples/tests/conversion/channel-converter-reduce-out/channel-converter-reduce-out.ino index 5865673b97..c9f25f4710 100644 --- a/examples/tests/conversion/channel-converter-reduce-out/channel-converter-reduce-out.ino +++ b/examples/tests/conversion/channel-converter-reduce-out/channel-converter-reduce-out.ino @@ -10,7 +10,7 @@ StreamCopy copier(conv, in_stream); // copies s void setup(){ Serial.begin(115200); - AudioToolsLogger.begin(Serial, AudioToolsLogLevel::Info); + AudioLogger::instance().begin(Serial, AudioLogger::Info); sine_wave.begin(info, N_B4); conv.begin(info, to_channels); diff --git a/examples/tests/conversion/format-converter-in/format-converter-in.ino b/examples/tests/conversion/format-converter-in/format-converter-in.ino deleted file mode 100644 index 18f30d4b64..0000000000 --- a/examples/tests/conversion/format-converter-in/format-converter-in.ino +++ /dev/null @@ -1,27 +0,0 @@ -#include "AudioTools.h" - -AudioInfo from_info(44100, 2, 32); -AudioInfo to_info(44100, 2, 16); -SineWaveGenerator sine_wave; // subclass of SoundGenerator with max amplitude of 32000 -GeneratedSoundStream in_stream(sine_wave); // Stream generated from sine wave -CsvOutput out(Serial); // Output to Serial -FormatConverterStream conv(in_stream); -StreamCopy copier(out, conv); // copies sound to out - -void setup(){ - Serial.begin(115200); - AudioToolsLogger.begin(Serial, AudioToolsLogLevel::Info); - - sine_wave.begin(from_info, N_B4); - in_stream.begin(); - - out.begin(to_info); - - conv.begin(from_info, to_info); - assert(out.audioInfo() == to_info); - assert(sine_wave.audioInfo() == from_info); -} - -void loop(){ - copier.copy(); -} \ No newline at end of file diff --git a/examples/tests/conversion/numberformat-converter-typed/numberformat-converter-typed.ino b/examples/tests/conversion/numberformat-converter-typed/numberformat-converter-typed.ino deleted file mode 100644 index e8d7e48578..0000000000 --- a/examples/tests/conversion/numberformat-converter-typed/numberformat-converter-typed.ino +++ /dev/null @@ -1,28 +0,0 @@ -#include "AudioTools.h" - -using target_t = uint32_t; // uint8_t, int8_t, int16_t, uint16_t, int24_t, uint32_t, int32_t, FloatAudio -SineWaveGenerator sineWave; // subclass of SoundGenerator with max amplitude of 32000 -GeneratedSoundStream sound(sineWave); // Stream generated from sine wave -CsvOutput out(Serial, sound.audioInfo().channels); -NumberFormatConverterStreamT nfc(out); -StreamCopy copier(nfc, sound); // copies sound into i2s - -// Arduino Setup -void setup(void) { - // Open Serial - Serial.begin(115200); - AudioToolsLogger.begin(Serial, AudioToolsLogLevel::Warning); - - nfc.begin(); - out.begin(); - sineWave.begin(); - - // Setup sine wave - sineWave.setFrequency(N_B4); - Serial.println("started..."); -} - -// Arduino loop - copy sound to out -void loop() { - copier.copy(); -} diff --git a/examples/tests/conversion/pipeline-in/pipeline-in.ino b/examples/tests/conversion/pipeline-in/pipeline-in.ino deleted file mode 100644 index 877181f7ba..0000000000 --- a/examples/tests/conversion/pipeline-in/pipeline-in.ino +++ /dev/null @@ -1,40 +0,0 @@ -#include "AudioTools.h" -#include "AudioTools/AudioLibs/AudioBoardStream.h" - -int16_t AR1[] = { 0, 4560, 9031, 13327, 17363, 21062, 24350, 27165, 29450, 31163, 32269, 32747, 32587, 31793, 30381, 28377, 25820, 22761, 19259, 15383, 11206, 6812, 2285, -2285, -6812, -11206, -15383, -19259, -22761, -25820, -28377, -30381, -31793, -32587, -32747, -32269, -31163, -29450, -27165, -24350, -21062, -17363, -13327, -9031, -4560 }; - -AudioInfo info(44100, 2, 16); -GeneratorFromArray wave(AR1, 0, false); -GeneratedSoundStream snd(wave); -Pipeline pip; -ResampleStream resample; -VolumeStream volume; -ChannelFormatConverterStream channels; -NumberFormatConverterStream bits; -AudioBoardStream i2s(AudioKitEs8388V1); -//I2SStream i2s; -StreamCopy copier(pip, snd); - -// Arduino Setup -void setup(void) { - Serial.begin(115200); - AudioToolsLogger.begin(Serial, AudioToolsLogLevel::Warning); - - snd.begin(info); - resample.setStepSize(0.4f); - volume.setVolume(0.5); - channels.setToChannels(1); - bits.setToBits(16); - - pip.add(resample); - pip.add(volume); - pip.add(channels); - pip.add(bits); - pip.setOutput(i2s); - pip.begin(info); - -} - -void loop() { - copier.copy(); -} \ No newline at end of file diff --git a/examples/tests/conversion/pipeline-out/pipeline-out.ino b/examples/tests/conversion/pipeline-out/pipeline-out.ino deleted file mode 100644 index 6b061905d7..0000000000 --- a/examples/tests/conversion/pipeline-out/pipeline-out.ino +++ /dev/null @@ -1,40 +0,0 @@ -#include "AudioTools.h" -#include "AudioTools/AudioLibs/AudioBoardStream.h" - -int16_t AR1[] = { 0, 4560, 9031, 13327, 17363, 21062, 24350, 27165, 29450, 31163, 32269, 32747, 32587, 31793, 30381, 28377, 25820, 22761, 19259, 15383, 11206, 6812, 2285, -2285, -6812, -11206, -15383, -19259, -22761, -25820, -28377, -30381, -31793, -32587, -32747, -32269, -31163, -29450, -27165, -24350, -21062, -17363, -13327, -9031, -4560 }; - -AudioInfo info(44100, 2, 16); -GeneratorFromArray wave(AR1, 0, false); -GeneratedSoundStream snd(wave); -Pipeline pip; -ResampleStream resample; -VolumeStream volume; -ChannelFormatConverterStream channels; -NumberFormatConverterStream bits; -AudioBoardStream i2s(AudioKitEs8388V1); -//I2SStream i2s; -StreamCopy copier(i2s, pip); - -// Arduino Setup -void setup(void) { - Serial.begin(115200); - AudioToolsLogger.begin(Serial, AudioToolsLogLevel::Warning); - - i2s.begin(); - resample.setStepSize(0.4f); - volume.setVolume(0.5); - channels.setToChannels(1); - bits.setToBits(16); - - pip.setInput(snd); - pip.add(resample); - pip.add(volume); - pip.add(channels); - pip.add(bits); - pip.begin(info); - -} - -void loop() { - copier.copy(); -} \ No newline at end of file diff --git a/examples/tests/conversion/resample-mixer-in/resample-mixer-in.ino b/examples/tests/conversion/resample-mixer-in/resample-mixer-in.ino deleted file mode 100644 index 50f7836910..0000000000 --- a/examples/tests/conversion/resample-mixer-in/resample-mixer-in.ino +++ /dev/null @@ -1,44 +0,0 @@ -#include -#include "AudioTools/AudioLibs/AudioBoardStream.h" - -int16_t AR1[] = { 0, 4560, 9031, 13327, 17363, 21062, 24350, 27165, 29450, 31163, 32269, 32747, 32587, 31793, 30381, 28377, 25820, 22761, 19259, 15383, 11206, 6812, 2285, -2285, -6812, -11206, -15383, -19259, -22761, -25820, -28377, -30381, -31793, -32587, -32747, -32269, -31163, -29450, -27165, -24350, -21062, -17363, -13327, -9031, -4560 }; - -AudioInfo info(44100, 1, 16); -GeneratorFromArray wave(AR1, 0, false); -GeneratedSoundStream snd(wave); // Stream generated from array1 -VolumeStream volume(snd); -ResampleStream resample(volume); -InputMixer mixer; -AudioBoardStream i2s(AudioKitEs8388V1); -StreamCopy copier(i2s, mixer); - -// Arduino Setup - -void setup(void) { - Serial.begin(115200); - AudioToolsLogger.begin(Serial, AudioToolsLogLevel::Warning); - - auto rcfg1 = resample.defaultConfig(); - rcfg1.copyFrom(info); - rcfg1.step_size = 0.6; - resample.begin(rcfg1); - - // start I2S internal - auto config = i2s.defaultConfig(TX_MODE); - config.copyFrom(info); - config.use_apll = false; - i2s.begin(config); - - mixer.add(resample); //SOUND1 - mixer.begin(info); - - //waveAR1.begin(info); - snd.begin(info); - - volume.begin(info); - volume.setVolume(1.0); -} - -void loop() { - copier.copy(); -} \ No newline at end of file diff --git a/examples/tests/conversion/test-panning/test-panning.ino b/examples/tests/conversion/test-panning/test-panning.ino deleted file mode 100644 index e09e97a108..0000000000 --- a/examples/tests/conversion/test-panning/test-panning.ino +++ /dev/null @@ -1,32 +0,0 @@ -#include "AudioTools.h" -#include "AudioTools/AudioLibs/AudioBoardStream.h" - -AudioInfo info(44100, 2, 32); -SineWaveGenerator sineWave; // subclass of SoundGenerator with max amplitude of 32000 -GeneratedSoundStream sound(sineWave); // Stream generated from sine wave -AudioBoardStream out(AudioKitEs8388V1); -VolumeStream volume(out); -StreamCopy copier(volume, sound); // copies sound into i2s - -void setup(void) { - Serial.begin(115200); - //AudioToolsLogger.begin(Serial, AudioToolsLogLevel::Info); - // start input sound - sineWave.begin(info, N_B4); - - // start I2S in - auto cfg = out.defaultConfig(TX_MODE); - cfg.copyFrom(info); - out.begin(cfg); - // set AudioKit to full volume - out.setVolume(1.0); - - // setup volume - volume.begin(info); - volume.setVolume(0.0, 0); - volume.setVolume(0.3, 1); -} - -void loop() { - copier.copy(); // Arduino loop - copy sound to out -} \ No newline at end of file diff --git a/examples/tests/conversion/test-volumestream/test-volumestream.ino b/examples/tests/conversion/test-volumestream/test-volumestream.ino deleted file mode 100644 index d4b0e1cb75..0000000000 --- a/examples/tests/conversion/test-volumestream/test-volumestream.ino +++ /dev/null @@ -1,36 +0,0 @@ -/** - * @file test-volumestream.ino - * @author Phil Schatzmann - * @brief test for VolumeStream class - * @copyright GPLv3 - **/ - -#include "AudioTools.h" - -AudioInfo audio_info(44100, 2, 16); -SineWaveGenerator sineWave(32000); // subclass of SoundGenerator with max amplitude of 32000 -GeneratedSoundStream sound(sineWave); -CsvOutput out(Serial); -VolumeStream vol(out); -StreamCopy copier(vol, sound); // copies sound to out - -// Arduino Setup -void setup(void) { - // Open Serial - Serial.begin(115200); - AudioToolsLogger.begin(Serial, AudioToolsLogLevel::Warning); - - // Define CSV Output - out.begin(audio_info); - vol.begin(audio_info); - vol.setVolume(0.1); - - // Setup sine wave - sineWave.begin(audio_info, N_B4); - Serial.println("started..."); -} - -// Arduino loop - copy sound to out -void loop() { - copier.copy(); -} \ No newline at end of file diff --git a/examples/tests/effects/delay-in/delay-in.ino b/examples/tests/effects/delay-in/delay-in.ino index 473fca5e26..aa97239cf9 100644 --- a/examples/tests/effects/delay-in/delay-in.ino +++ b/examples/tests/effects/delay-in/delay-in.ino @@ -1,26 +1,29 @@ #include "AudioTools.h" +int channels = 1; +int rate = 44100; int hz = 200; -AudioInfo info(44100, 1, 16); SineWaveGenerator sine; GeneratedSoundStream gen(sine); AudioEffectStream effects(gen); // apply effects to input: reading from gen CsvOutput out(Serial); StreamCopy copier(out, effects); -Delay dly(998, 0.5, 1.0, info.sample_rate); +Delay dly(998, 0.5, 1.0,rate, true); void setup() { Serial.begin(115200); - // setup effects effects.addEffect(dly); // Setup audio objects - out.begin(info); - sine.begin(info, hz); - gen.begin(info); - effects.begin(info); + auto cfg = out.defaultConfig(); + cfg.channels = channels; + cfg.sample_rate = rate; + out.begin(cfg); + sine.begin(cfg, hz); + gen.begin(cfg); + effects.begin(cfg); } // copy the data void loop() { diff --git a/examples/tests/effects/delay-out/delay-out.ino b/examples/tests/effects/delay-out/delay-out.ino index 774613aa4e..3c91c699e9 100644 --- a/examples/tests/effects/delay-out/delay-out.ino +++ b/examples/tests/effects/delay-out/delay-out.ino @@ -1,13 +1,14 @@ #include "AudioTools.h" -AudioInfo info(44100, 1, 16); +int channels = 1; +int rate = 44100; int hz = 200; SineWaveGenerator sine; GeneratedSoundStream gen(sine); CsvOutput csv(Serial); AudioEffectStream effects(csv); // apply effects to output: writing to effects StreamCopy copier(effects, gen); -Delay dly(998, 0.5, 1.0,info.sample_rate); +Delay dly(998, 0.5, 1.0,rate, true); void setup() { @@ -16,10 +17,13 @@ void setup() { effects.addEffect(dly); // Setup audio objects - csv.begin(info); - sine.begin(info, hz); - gen.begin(info); - effects.begin(info); + auto cfg = csv.defaultConfig(); + cfg.channels = channels; + cfg.sample_rate = rate; + csv.begin(cfg); + sine.begin(cfg, hz); + gen.begin(cfg); + effects.begin(cfg); } // copy the data void loop() { diff --git a/examples/tests/etc/callback-write/callback-write.ino b/examples/tests/etc/callback-write/callback-write.ino deleted file mode 100644 index ae51738ec9..0000000000 --- a/examples/tests/etc/callback-write/callback-write.ino +++ /dev/null @@ -1,37 +0,0 @@ -#include "AudioTools.h" -#include "AudioTools/AudioLibs/AudioBoardStream.h" - -AudioInfo info(44100, 2, 16); -SineWaveGenerator sineWave; // subclass of SoundGenerator with max amplitude of 32000 -GeneratedSoundStream sound(sineWave); // Stream generated from sine wave -auto invert = [](uint8_t* data, size_t bytes) { - size_t sample_count = bytes / sizeof(int16_t); - int16_t* data16 = (int16_t*)data; - for (int j = 0; j < sample_count; j++) { - data16[j] = -data16[j]; - } - return bytes; -}; -AudioBoardStream out(AudioKitEs8388V1); -CallbackStream cb(out, invert); -StreamCopy copier(cb, sound); // copies sound into i2s - -void setup(void) { - Serial.begin(115200); - //AudioToolsLogger.begin(Serial, AudioToolsLogLevel::Info); - // start input sound - sineWave.begin(info, N_B4); - cb.begin(info); - - // start I2S in - auto cfg = out.defaultConfig(TX_MODE); - cfg.copyFrom(info); - out.begin(cfg); - // set AudioKit to full volume - out.setVolume(0.3); - -} - -void loop() { - copier.copy(); // Arduino loop - copy sound to out -} diff --git a/examples/tests/etc/test-audiolibs/test-audiolibs.ino b/examples/tests/etc/test-audiolibs/test-audiolibs.ino deleted file mode 100644 index 514367ed24..0000000000 --- a/examples/tests/etc/test-audiolibs/test-audiolibs.ino +++ /dev/null @@ -1,6 +0,0 @@ -#include "AudioTools.h" -#include "AudioTools/AudioLibs/All.h" - -void setup(){} - -void loop(){} \ No newline at end of file diff --git a/examples/tests/etc/test-tdm/test-tdm.ino b/examples/tests/etc/test-tdm/test-tdm.ino deleted file mode 100644 index edd3db27d6..0000000000 --- a/examples/tests/etc/test-tdm/test-tdm.ino +++ /dev/null @@ -1,57 +0,0 @@ -// TDM test using a RP2040 with a CS42448 DAC - -#include "AudioTools.h" -#include "AudioTools/AudioLibs/AudioBoardStream.h" - -const AudioInfo info_in(44100, 1, 16); -const AudioInfo info_out(44100, 8, 16); -const MusicalNotes notes; -Vector> sineWaves{info_out.channels}; -Vector> sound{info_out.channels}; -InputMerge imerge; // merge to 8 channels -DriverPins dac_pins; -AudioBoard board(AudioDriverCS42448, dac_pins); -AudioBoardStream tdm(board); -StreamCopy copier(tdm, imerge); - -// we use the AudioBoard API to set up the pins for the DAC -void setupDACPins() { - // add i2c codec pins: scl, sda, port - dac_pins.addI2C(PinFunction::CODEC, 4, 5); - // add i2s pins: mclk, bck, ws,data_out, data_in ,(port) - dac_pins.addI2S(PinFunction::CODEC,-1, 3, 4, 5, -1); -} - -// Arduino Setup -void setup(void) { - // Open Serial - Serial.begin(115200); - AudioToolsLogger.begin(Serial, AudioToolsLogLevel::Info); - - // setup input sound and merge -> we get an merge with 8 channels - for (int j = 0; j < info_out.channels; j++) { - // use a different tone for each channel - sineWaves[j].begin(info_in, notes.frequency(j * 2)); - // setup GeneratedSoundStream - sound[j].setInput(sineWaves[j]); - sound[j].begin(info_in); - // setup merge input stream channels - imerge.add(sound[j], 1); - } - - // setup DAC pins - setupDACPins(); - - // setup DAC & I2S - auto cfg = tdm.defaultConfig(); - cfg.copyFrom(info_out); - cfg.input_device = ADC_INPUT_NONE; - cfg.output_device = DAC_OUTPUT_ALL; - cfg.signal_type = TDM; - tdm.begin(); - - Serial.println("started..."); -} - -// Arduino loop - copy sound to out -void loop() { copier.copy(); } \ No newline at end of file diff --git a/examples/tests/etc/test-write-memory/test-write-memory.ino b/examples/tests/etc/test-write-memory/test-write-memory.ino deleted file mode 100644 index d7aba7b39b..0000000000 --- a/examples/tests/etc/test-write-memory/test-write-memory.ino +++ /dev/null @@ -1,30 +0,0 @@ -#include "AudioTools.h" - -#define SAMPLE_COUNT 2048 -#define SAMPLE_BUFFER_SIZE sizeof(int32_t) * SAMPLE_COUNT -unsigned char raw_data[SAMPLE_BUFFER_SIZE]; -AudioInfo info(8000, 1, 32); -I2SStream i2sStream; // Access I2S as stream -MemoryStream raw_samples(raw_data, SAMPLE_BUFFER_SIZE, true, RAM); -StreamCopy copier(raw_samples, i2sStream, 1024); // copies sound - -void setup() { - Serial.begin(115200); - AudioToolsLogger.begin(Serial, AudioToolsLogLevel::Info); - - auto cfg = i2sStream.defaultConfig(RX_MODE); - cfg.copyFrom(info); - i2sStream.begin(cfg); - - raw_samples.begin(); - - copier.copy(); - - assert(raw_samples.available()== 1024); - assert(raw_samples.availableForWrite()== 7168); - -} - -void loop() { - // put your main code here, to run repeatedly: -} diff --git a/examples/tests/fft/fft-cmsis/fft-cmsis.ino b/examples/tests/fft/fft-cmsis/fft-cmsis.ino index 4077ef797d..e447008219 100644 --- a/examples/tests/fft/fft-cmsis/fft-cmsis.ino +++ b/examples/tests/fft/fft-cmsis/fft-cmsis.ino @@ -1,5 +1,5 @@ #include "AudioTools.h" -#include "AudioTools/AudioLibs/AudioCmsisFFT.h" // using CMSIS DSP +#include "AudioLibs/AudioCmsisFFT.h" // using CMSIS DSP AudioCmsisFFT fft; // or AudioKissFFT SineWaveGenerator sineWave(32000); @@ -28,7 +28,7 @@ void fftResult(AudioFFTBase &fft) { void setup() { Serial.begin(115200); - AudioToolsLogger.begin(Serial, AudioToolsLogLevel::Warning); + AudioLogger::instance().begin(Serial, AudioLogger::Warning); while(!Serial); diff --git a/examples/tests/fft/fft-esp32/fft-esp32.ino b/examples/tests/fft/fft-esp32/fft-esp32.ino index 243f043fcd..503f7e75bf 100644 --- a/examples/tests/fft/fft-esp32/fft-esp32.ino +++ b/examples/tests/fft/fft-esp32/fft-esp32.ino @@ -1,5 +1,5 @@ #include "AudioTools.h" -#include "AudioTools/AudioLibs/AudioESP32FFT.h" // Using ESP32FFT +#include "AudioLibs/AudioESP32FFT.h" // Using ESP32FFT AudioESP32FFT fftc; // or AudioKissFFT SineWaveGenerator sineWave(32000); @@ -29,7 +29,7 @@ void fftcResult(AudioFFTBase &fftc) { void setup() { Serial.begin(115200); - AudioToolsLogger.begin(Serial, AudioToolsLogLevel::Warning); + AudioLogger::instance().begin(Serial, AudioLogger::Warning); // set the frequency sineWave.setFrequency(N_B4); diff --git a/examples/tests/fft/fft-espressif/fft-espressif.ino b/examples/tests/fft/fft-espressif/fft-espressif.ino index f8cfbffb72..3112f5c3b8 100644 --- a/examples/tests/fft/fft-espressif/fft-espressif.ino +++ b/examples/tests/fft/fft-espressif/fft-espressif.ino @@ -1,5 +1,5 @@ #include "AudioTools.h" -#include "AudioTools/AudioLibs/AudioEspressifFFT.h" // Using Espressif DSP Library +#include "AudioLibs/AudioEspressifFFT.h" // Using Espressif DSP Library AudioEspressifFFT fftc; SineWaveGenerator sineWave(32000); @@ -29,7 +29,7 @@ void fftcResult(AudioFFTBase &fftc) { void setup() { Serial.begin(115200); - AudioToolsLogger.begin(Serial, AudioToolsLogLevel::Warning); + AudioLogger::instance().begin(Serial, AudioLogger::Warning); // set the frequency sineWave.setFrequency(N_B4); diff --git a/examples/tests/fft/fft-ifft/fft-ifft.ino b/examples/tests/fft/fft-ifft/fft-ifft.ino deleted file mode 100644 index c7029c84ee..0000000000 --- a/examples/tests/fft/fft-ifft/fft-ifft.ino +++ /dev/null @@ -1,50 +0,0 @@ -#include "AudioTools.h" -#include "AudioTools/AudioLibs/AudioRealFFT.h" // using RealFFT -#include "AudioTools/AudioLibs/AudioBoardStream.h" - -AudioInfo info(8000, 1, 16); -AudioRealFFT afft; // or AudioKissFFT -Hann hann; -BufferedWindow buffered(&hann); -SineWaveGenerator sineWave(32000); -GeneratedSoundStream in(sineWave); -StreamCopy copier(afft, in); -//CsvOutput out(Serial); -//I2SStream out; -AudioBoardStream out(AudioKitEs8388V1); -StreamCopy copierIFFT(out, afft); - -// process fft result -void fftResult(AudioFFTBase &fft) { - // copy ifft result to output - while (copierIFFT.copy()); -} - -void setup() { - Serial.begin(115200); - AudioToolsLogger.begin(Serial, AudioToolsLogLevel::Warning); - - // set the frequency - sineWave.setFrequency(N_B4); - - // Setup sine wave - auto cfg = in.defaultConfig(); - cfg.copyFrom(info); - in.begin(cfg); - - // Setup FFT - auto tcfg = afft.defaultConfig(RXTX_MODE); - tcfg.copyFrom(info); - tcfg.length = 1024; - //tcfg.window_function = &buffered; - //tcfg.stride = 512; - tcfg.callback = fftResult; - afft.begin(tcfg); - - // setup output - auto ocfg = out.defaultConfig(TX_MODE); - ocfg.copyFrom(info); - out.begin(ocfg); -} - -void loop() { copier.copy(); } \ No newline at end of file diff --git a/examples/tests/fft/fft-kiss/fft-kiss.ino b/examples/tests/fft/fft-kiss/fft-kiss.ino index dfedbc5844..6dc2468fd6 100644 --- a/examples/tests/fft/fft-kiss/fft-kiss.ino +++ b/examples/tests/fft/fft-kiss/fft-kiss.ino @@ -1,5 +1,5 @@ #include "AudioTools.h" -#include "AudioTools/AudioLibs/AudioKissFFT.h" // Using KissFFT +#include "AudioLibs/AudioKissFFT.h" // Using KissFFT AudioKissFFT fft; // or AudioKissFFT SineWaveGenerator sineWave(32000); @@ -28,7 +28,7 @@ void fftResult(AudioFFTBase &fft) { void setup() { Serial.begin(115200); - AudioToolsLogger.begin(Serial, AudioToolsLogLevel::Warning); + AudioLogger::instance().begin(Serial, AudioLogger::Warning); // set the frequency sineWave.setFrequency(N_B4); diff --git a/examples/tests/fft/fft-real/fft-real.ino b/examples/tests/fft/fft-real/fft-real.ino index 4ee404eee3..ee48bb4b42 100644 --- a/examples/tests/fft/fft-real/fft-real.ino +++ b/examples/tests/fft/fft-real/fft-real.ino @@ -1,5 +1,5 @@ #include "AudioTools.h" -#include "AudioTools/AudioLibs/AudioRealFFT.h" // using RealFFT +#include "AudioLibs/AudioRealFFT.h" // using RealFFT AudioRealFFT fft; // or AudioKissFFT SineWaveGenerator sineWave(32000); @@ -28,7 +28,7 @@ void fftResult(AudioFFTBase &fft) { void setup() { Serial.begin(115200); - AudioToolsLogger.begin(Serial, AudioToolsLogLevel::Warning); + AudioLogger::instance().begin(Serial, AudioLogger::Warning); // set the frequency sineWave.setFrequency(N_B4); diff --git a/examples/tests/fft/fft-topn/fft-topn.ino b/examples/tests/fft/fft-topn/fft-topn.ino index 2a38073c05..4e78f2a0b3 100644 --- a/examples/tests/fft/fft-topn/fft-topn.ino +++ b/examples/tests/fft/fft-topn/fft-topn.ino @@ -1,6 +1,6 @@ #include "AudioTools.h" -#include "AudioTools/AudioLibs/AudioRealFFT.h" // using RealFFT +#include "AudioLibs/AudioRealFFT.h" // using RealFFT AudioRealFFT fft; // or AudioKissFFT SineWaveGenerator sineWave(32000); @@ -41,7 +41,7 @@ void fftResult(AudioFFTBase &fft) { void setup() { Serial.begin(115200); - AudioToolsLogger.begin(Serial, AudioToolsLogLevel::Warning); + AudioLogger::instance().begin(Serial, AudioLogger::Warning); // set the frequency sineWave.setFrequency(N_B4); diff --git a/examples/tests/fft/fft-window/fft-window.ino b/examples/tests/fft/fft-window/fft-window.ino index 03372488f6..54361c92ed 100644 --- a/examples/tests/fft/fft-window/fft-window.ino +++ b/examples/tests/fft/fft-window/fft-window.ino @@ -1,5 +1,5 @@ #include "AudioTools.h" -#include "AudioTools/AudioLibs/AudioRealFFT.h" // using RealFFT +#include "AudioLibs/AudioRealFFT.h" // using RealFFT AudioRealFFT fft; // or AudioKissFFT Hann hann; @@ -30,7 +30,7 @@ void fftResult(AudioFFTBase &fft) { void setup() { Serial.begin(115200); - AudioToolsLogger.begin(Serial, AudioToolsLogLevel::Warning); + AudioLogger::instance().begin(Serial, AudioLogger::Warning); // set the frequency sineWave.setFrequency(N_B4); diff --git a/examples/tests/filters/test-90deg/fir_coeffs_101Taps_44100_200_19000.h b/examples/tests/filters/test-90deg/fir_coeffs_101Taps_44100_200_19000.h deleted file mode 100644 index 54491ef4c1..0000000000 --- a/examples/tests/filters/test-90deg/fir_coeffs_101Taps_44100_200_19000.h +++ /dev/null @@ -1,206 +0,0 @@ -const float coeffs_hilbert_101Taps_44100_200_19000[] = { - 0.0001845375, - 0.0001450338, - -0.0001337144, - 0.0002717566, - -0.0002084398, - 0.0004367564, - -0.0002414144, - 0.0005601507, - -0.0001045179, - 0.0005116525, - 0.0003299699, - 0.0002667884, - 0.0009759046, - 0.0001011561, - 0.0014366282, - 0.0005237720, - 0.0012552952, - 0.0018758583, - 0.0004146737, - 0.0038272159, - -0.0002700868, - 0.0052244278, - 0.0006200192, - 0.0047704995, - 0.0040194220, - 0.0023590833, - 0.0091023742, - -0.0000317342, - 0.0130758105, - 0.0009912826, - 0.0127741163, - 0.0078721470, - 0.0074909738, - 0.0193576732, - 0.0010717165, - 0.0297314269, - 0.0009902233, - 0.0316786844, - 0.0139422831, - 0.0218861827, - 0.0402704410, - 0.0061432626, - 0.0711015341, - 0.0006455787, - 0.0911099193, - 0.0299764082, - 0.0863535827, - 0.1363124787, - 0.0527935654, - 0.6078831408, - 0.0000000000, - -0.6078831408, - -0.0527935654, - -0.1363124787, - -0.0863535827, - -0.0299764082, - -0.0911099193, - -0.0006455787, - -0.0711015341, - -0.0061432626, - -0.0402704410, - -0.0218861827, - -0.0139422831, - -0.0316786844, - -0.0009902233, - -0.0297314269, - -0.0010717165, - -0.0193576732, - -0.0074909738, - -0.0078721470, - -0.0127741163, - -0.0009912826, - -0.0130758105, - 0.0000317342, - -0.0091023742, - -0.0023590833, - -0.0040194220, - -0.0047704995, - -0.0006200192, - -0.0052244278, - 0.0002700868, - -0.0038272159, - -0.0004146737, - -0.0018758583, - -0.0012552952, - -0.0005237720, - -0.0014366282, - -0.0001011561, - -0.0009759046, - -0.0002667884, - -0.0003299699, - -0.0005116525, - 0.0001045179, - -0.0005601507, - 0.0002414144, - -0.0004367564, - 0.0002084398, - -0.0002717566, - 0.0001337144, - -0.0001450338, - -0.0001845375, -}; -const float coeffs_delay_101[] = { - 0.0000000000, - 0.0000000000, - 0.0000000000, - 0.0000000000, - 0.0000000000, - 0.0000000000, - 0.0000000000, - 0.0000000000, - 0.0000000000, - 0.0000000000, - 0.0000000000, - 0.0000000000, - 0.0000000000, - 0.0000000000, - 0.0000000000, - 0.0000000000, - 0.0000000000, - 0.0000000000, - 0.0000000000, - 0.0000000000, - 0.0000000000, - 0.0000000000, - 0.0000000000, - 0.0000000000, - 0.0000000000, - 0.0000000000, - 0.0000000000, - 0.0000000000, - 0.0000000000, - 0.0000000000, - 0.0000000000, - 0.0000000000, - 0.0000000000, - 0.0000000000, - 0.0000000000, - 0.0000000000, - 0.0000000000, - 0.0000000000, - 0.0000000000, - 0.0000000000, - 0.0000000000, - 0.0000000000, - 0.0000000000, - 0.0000000000, - 0.0000000000, - 0.0000000000, - 0.0000000000, - 0.0000000000, - 0.0000000000, - 0.0000000000, - 1.0000000000, - 0.0000000000, - 0.0000000000, - 0.0000000000, - 0.0000000000, - 0.0000000000, - 0.0000000000, - 0.0000000000, - 0.0000000000, - 0.0000000000, - 0.0000000000, - 0.0000000000, - 0.0000000000, - 0.0000000000, - 0.0000000000, - 0.0000000000, - 0.0000000000, - 0.0000000000, - 0.0000000000, - 0.0000000000, - 0.0000000000, - 0.0000000000, - 0.0000000000, - 0.0000000000, - 0.0000000000, - 0.0000000000, - 0.0000000000, - 0.0000000000, - 0.0000000000, - 0.0000000000, - 0.0000000000, - 0.0000000000, - 0.0000000000, - 0.0000000000, - 0.0000000000, - 0.0000000000, - 0.0000000000, - 0.0000000000, - 0.0000000000, - 0.0000000000, - 0.0000000000, - 0.0000000000, - 0.0000000000, - 0.0000000000, - 0.0000000000, - 0.0000000000, - 0.0000000000, - 0.0000000000, - 0.0000000000, - 0.0000000000, - 0.0000000000, -}; diff --git a/examples/tests/filters/test-90deg/fir_coeffs_161Taps_44100_200_19000.h b/examples/tests/filters/test-90deg/fir_coeffs_161Taps_44100_200_19000.h deleted file mode 100644 index 07ce701711..0000000000 --- a/examples/tests/filters/test-90deg/fir_coeffs_161Taps_44100_200_19000.h +++ /dev/null @@ -1,326 +0,0 @@ -const float coeffs_hilbert_161Taps_44100_200_19000[] = { - 0.0001075981, - 0.0000635907, - -0.0000542276, - 0.0000961953, - -0.0000716834, - 0.0001336577, - -0.0000789160, - 0.0001545559, - -0.0000439131, - 0.0001317663, - 0.0000491225, - 0.0000706399, - 0.0001755277, - 0.0000254666, - 0.0002580754, - 0.0000897251, - 0.0002169548, - 0.0003133418, - 0.0000581998, - 0.0006286168, - -0.0000754266, - 0.0008392822, - 0.0000402148, - 0.0007511820, - 0.0005300339, - 0.0003618778, - 0.0012546584, - -0.0000299573, - 0.0017994327, - 0.0000509181, - 0.0017333288, - 0.0009076391, - 0.0009870086, - 0.0023260763, - 0.0000899613, - 0.0035316813, - -0.0000373396, - 0.0036412806, - 0.0012852664, - 0.0023796747, - 0.0038172312, - 0.0005709556, - 0.0062487082, - -0.0001464004, - 0.0069161256, - 0.0016193144, - 0.0050623765, - 0.0057705049, - 0.0017956620, - 0.0102989249, - -0.0001816732, - 0.0122841073, - 0.0017915891, - 0.0099584706, - 0.0081873463, - 0.0045180791, - 0.0162287653, - 0.0001456066, - 0.0210527205, - 0.0017191509, - 0.0188841071, - 0.0112623705, - 0.0103599299, - 0.0255213793, - 0.0015590770, - 0.0367573548, - 0.0013623582, - 0.0371301257, - 0.0160506910, - 0.0245615957, - 0.0442212665, - 0.0067177403, - 0.0754282206, - 0.0007616364, - 0.0941804141, - 0.0307184143, - 0.0876509472, - 0.1374531039, - 0.0530080088, - 0.6084394667, - 0.0000000000, - -0.6084394667, - -0.0530080088, - -0.1374531039, - -0.0876509472, - -0.0307184143, - -0.0941804141, - -0.0007616364, - -0.0754282206, - -0.0067177403, - -0.0442212665, - -0.0245615957, - -0.0160506910, - -0.0371301257, - -0.0013623582, - -0.0367573548, - -0.0015590770, - -0.0255213793, - -0.0103599299, - -0.0112623705, - -0.0188841071, - -0.0017191509, - -0.0210527205, - -0.0001456066, - -0.0162287653, - -0.0045180791, - -0.0081873463, - -0.0099584706, - -0.0017915891, - -0.0122841073, - 0.0001816732, - -0.0102989249, - -0.0017956620, - -0.0057705049, - -0.0050623765, - -0.0016193144, - -0.0069161256, - 0.0001464004, - -0.0062487082, - -0.0005709556, - -0.0038172312, - -0.0023796747, - -0.0012852664, - -0.0036412806, - 0.0000373396, - -0.0035316813, - -0.0000899613, - -0.0023260763, - -0.0009870086, - -0.0009076391, - -0.0017333288, - -0.0000509181, - -0.0017994327, - 0.0000299573, - -0.0012546584, - -0.0003618778, - -0.0005300339, - -0.0007511820, - -0.0000402148, - -0.0008392822, - 0.0000754266, - -0.0006286168, - -0.0000581998, - -0.0003133418, - -0.0002169548, - -0.0000897251, - -0.0002580754, - -0.0000254666, - -0.0001755277, - -0.0000706399, - -0.0000491225, - -0.0001317663, - 0.0000439131, - -0.0001545559, - 0.0000789160, - -0.0001336577, - 0.0000716834, - -0.0000961953, - 0.0000542276, - -0.0000635907, - -0.0001075981, -}; -const float coeffs_delay_161[] = { - 0.0000000000, - 0.0000000000, - 0.0000000000, - 0.0000000000, - 0.0000000000, - 0.0000000000, - 0.0000000000, - 0.0000000000, - 0.0000000000, - 0.0000000000, - 0.0000000000, - 0.0000000000, - 0.0000000000, - 0.0000000000, - 0.0000000000, - 0.0000000000, - 0.0000000000, - 0.0000000000, - 0.0000000000, - 0.0000000000, - 0.0000000000, - 0.0000000000, - 0.0000000000, - 0.0000000000, - 0.0000000000, - 0.0000000000, - 0.0000000000, - 0.0000000000, - 0.0000000000, - 0.0000000000, - 0.0000000000, - 0.0000000000, - 0.0000000000, - 0.0000000000, - 0.0000000000, - 0.0000000000, - 0.0000000000, - 0.0000000000, - 0.0000000000, - 0.0000000000, - 0.0000000000, - 0.0000000000, - 0.0000000000, - 0.0000000000, - 0.0000000000, - 0.0000000000, - 0.0000000000, - 0.0000000000, - 0.0000000000, - 0.0000000000, - 0.0000000000, - 0.0000000000, - 0.0000000000, - 0.0000000000, - 0.0000000000, - 0.0000000000, - 0.0000000000, - 0.0000000000, - 0.0000000000, - 0.0000000000, - 0.0000000000, - 0.0000000000, - 0.0000000000, - 0.0000000000, - 0.0000000000, - 0.0000000000, - 0.0000000000, - 0.0000000000, - 0.0000000000, - 0.0000000000, - 0.0000000000, - 0.0000000000, - 0.0000000000, - 0.0000000000, - 0.0000000000, - 0.0000000000, - 0.0000000000, - 0.0000000000, - 0.0000000000, - 0.0000000000, - 1.0000000000, - 0.0000000000, - 0.0000000000, - 0.0000000000, - 0.0000000000, - 0.0000000000, - 0.0000000000, - 0.0000000000, - 0.0000000000, - 0.0000000000, - 0.0000000000, - 0.0000000000, - 0.0000000000, - 0.0000000000, - 0.0000000000, - 0.0000000000, - 0.0000000000, - 0.0000000000, - 0.0000000000, - 0.0000000000, - 0.0000000000, - 0.0000000000, - 0.0000000000, - 0.0000000000, - 0.0000000000, - 0.0000000000, - 0.0000000000, - 0.0000000000, - 0.0000000000, - 0.0000000000, - 0.0000000000, - 0.0000000000, - 0.0000000000, - 0.0000000000, - 0.0000000000, - 0.0000000000, - 0.0000000000, - 0.0000000000, - 0.0000000000, - 0.0000000000, - 0.0000000000, - 0.0000000000, - 0.0000000000, - 0.0000000000, - 0.0000000000, - 0.0000000000, - 0.0000000000, - 0.0000000000, - 0.0000000000, - 0.0000000000, - 0.0000000000, - 0.0000000000, - 0.0000000000, - 0.0000000000, - 0.0000000000, - 0.0000000000, - 0.0000000000, - 0.0000000000, - 0.0000000000, - 0.0000000000, - 0.0000000000, - 0.0000000000, - 0.0000000000, - 0.0000000000, - 0.0000000000, - 0.0000000000, - 0.0000000000, - 0.0000000000, - 0.0000000000, - 0.0000000000, - 0.0000000000, - 0.0000000000, - 0.0000000000, - 0.0000000000, - 0.0000000000, - 0.0000000000, - 0.0000000000, - 0.0000000000, - 0.0000000000, - 0.0000000000, - 0.0000000000, -}; diff --git a/examples/tests/filters/test-90deg/fir_coeffs_61Taps_44100_200_19000.h b/examples/tests/filters/test-90deg/fir_coeffs_61Taps_44100_200_19000.h deleted file mode 100644 index 022d90b5a4..0000000000 --- a/examples/tests/filters/test-90deg/fir_coeffs_61Taps_44100_200_19000.h +++ /dev/null @@ -1,126 +0,0 @@ -const float coeffs_hilbert_61Taps_44100_200_19000[] = { - 0.0001242450, - 0.0006874208, - -0.0001184992, - -0.0000561422, - 0.0010530539, - -0.0008422607, - 0.0025708659, - -0.0015788004, - 0.0043398859, - -0.0015410467, - 0.0053526877, - 0.0008794383, - 0.0043167306, - 0.0067832875, - 0.0016847233, - 0.0144120317, - 0.0014897183, - 0.0186966822, - 0.0101060312, - 0.0147341586, - 0.0313924993, - 0.0040699439, - 0.0605202855, - -0.0002146265, - 0.0831567061, - 0.0276660224, - 0.0829874083, - 0.1328479631, - 0.0522707209, - 0.6063302901, - 0.0000000000, - -0.6063302901, - -0.0522707209, - -0.1328479631, - -0.0829874083, - -0.0276660224, - -0.0831567061, - 0.0002146265, - -0.0605202855, - -0.0040699439, - -0.0313924993, - -0.0147341586, - -0.0101060312, - -0.0186966822, - -0.0014897183, - -0.0144120317, - -0.0016847233, - -0.0067832875, - -0.0043167306, - -0.0008794383, - -0.0053526877, - 0.0015410467, - -0.0043398859, - 0.0015788004, - -0.0025708659, - 0.0008422607, - -0.0010530539, - 0.0000561422, - 0.0001184992, - -0.0006874208, - -0.0001242450, -}; -const float coeffs_delay_61[] = { - 0.0000000000, - 0.0000000000, - 0.0000000000, - 0.0000000000, - 0.0000000000, - 0.0000000000, - 0.0000000000, - 0.0000000000, - 0.0000000000, - 0.0000000000, - 0.0000000000, - 0.0000000000, - 0.0000000000, - 0.0000000000, - 0.0000000000, - 0.0000000000, - 0.0000000000, - 0.0000000000, - 0.0000000000, - 0.0000000000, - 0.0000000000, - 0.0000000000, - 0.0000000000, - 0.0000000000, - 0.0000000000, - 0.0000000000, - 0.0000000000, - 0.0000000000, - 0.0000000000, - 0.0000000000, - 1.0000000000, - 0.0000000000, - 0.0000000000, - 0.0000000000, - 0.0000000000, - 0.0000000000, - 0.0000000000, - 0.0000000000, - 0.0000000000, - 0.0000000000, - 0.0000000000, - 0.0000000000, - 0.0000000000, - 0.0000000000, - 0.0000000000, - 0.0000000000, - 0.0000000000, - 0.0000000000, - 0.0000000000, - 0.0000000000, - 0.0000000000, - 0.0000000000, - 0.0000000000, - 0.0000000000, - 0.0000000000, - 0.0000000000, - 0.0000000000, - 0.0000000000, - 0.0000000000, - 0.0000000000, - 0.0000000000, -}; diff --git a/examples/tests/filters/test-90deg/test-90deg.ino b/examples/tests/filters/test-90deg/test-90deg.ino deleted file mode 100644 index 68294050c7..0000000000 --- a/examples/tests/filters/test-90deg/test-90deg.ino +++ /dev/null @@ -1,42 +0,0 @@ -#include "AudioTools.h" -//#include "fir_coeffs_61Taps_44100_200_19000.h" -//#include "fir_coeffs_101Taps_44100_200_19000.h" -#include "fir_coeffs_161Taps_44100_200_19000.h" - -AudioInfo from(44100, 2, 32); -AudioInfo to(44100, 2, 16); -SineWaveGenerator sineWave; -GeneratedSoundStream in(sineWave); -CsvOutput out; -FilteredStream filtered(out, from.channels); -NumberFormatConverterStream conv (filtered); -StreamCopy copier(conv, in); - - -// Arduino Setup -void setup(void) { - // Open Serial - Serial.begin(115200); - AudioToolsLogger.begin(Serial, AudioToolsLogLevel::Warning); - filtered.setFilter(0, new FIR(coeffs_hilbert_161Taps_44100_200_19000)); - filtered.setFilter(1, new FIR(coeffs_delay_161)); - - // start in - auto config_in = in.defaultConfig(); - config_in.copyFrom(from); - in.begin(config_in); - sineWave.begin(config_in, 8000.0); - - // start out - auto config_out = out.defaultConfig(TX_MODE); - config_out.copyFrom(to); - out.begin(config_out); - - // Start conversion - conv.begin(from, to); -} - -// Arduino loop - copy sound to out -void loop() { - copier.copy(); -} diff --git a/examples/tests/filters/test-lowpass/test-lowpass.ino b/examples/tests/filters/test-lowpass/test-lowpass.ino deleted file mode 100644 index f181e74144..0000000000 --- a/examples/tests/filters/test-lowpass/test-lowpass.ino +++ /dev/null @@ -1,49 +0,0 @@ -#include "AudioTools.h" - -AudioInfo info(44100, 2 , 16); -VolumeMeter meter; -FilteredStream filtered(meter); -SineWaveGenerator sine; -GeneratedSoundStream sine_stream(sine); -StreamCopy copier(filtered, sine_stream); -float filter_freq = 8000; - -// Filter: or replace with other filters -LowPassFilter filter1(filter_freq, info.sample_rate); -LowPassFilter filter2(filter_freq, info.sample_rate); - -void setup() { - Serial.begin(115200); - AudioToolsLogger.begin(Serial, AudioToolsLogLevel::Warning); - - sine.begin(info, 0); - if (!sine_stream.begin(info)){ - Serial.println("Generator error"); - stop(); - } - - if (!filtered.begin(info)){ - Serial.println("filter error"); - stop(); - } - filtered.setFilter(0, filter1); - filtered.setFilter(1, filter2); - - if (!meter.begin(info)){ - Serial.println("meter error"); - stop(); - } - -} - -void loop() { - for (int freq= 20; freq<20000; freq+=100){ - meter.clear(); - sine.setFrequency(freq); - copier.copyN(10); - Serial.print(freq); - Serial.print(": "); - Serial.println(meter.volumeRatio()); - } - stop(); -} \ No newline at end of file diff --git a/examples/tests/filters/test-median-filter/test-median-filter.ino b/examples/tests/filters/test-median-filter/test-median-filter.ino deleted file mode 100644 index c4330749de..0000000000 --- a/examples/tests/filters/test-median-filter/test-median-filter.ino +++ /dev/null @@ -1,51 +0,0 @@ -/** - * Test case for MedianFilter -*/ -#include "AudioTools.h" -#include "AudioTools/AudioLibs/AudioBoardStream.h" - -AudioInfo info(44100, 1, 16); - -// provide sine wave damaged with some rare random noise samples -class TestGenerator : public SoundGenerator { -public: - TestGenerator() { - gen_sine.begin(info); - gen_sine.setFrequency(N_B4); - gen_noise.begin(info); - } - virtual int16_t readSample() { - // proper signal - int16_t sample = gen_sine.readSample(); - if (count-- == 0){ - // plan for next random value - count = random(100, 10000); - // replace proper singal value with a random value - sample = gen_noise.readSample();; - } - return sample; - }; -protected: - SineWaveGenerator gen_sine; - WhiteNoiseGenerator gen_noise; - size_t count = 0; -} testSound; - -GeneratedSoundStream testStream(testSound); // Stream generated from sine wave -FilteredStream inFiltered(testStream, info.channels); // Defiles the filter as BaseConverter -MedianFilter medianFilter; -AudioBoardStream out(AudioKitEs8388V1); -StreamCopy copier(out, inFiltered); -//StreamCopy copier(out, testStream); //compare with this - - -void setup() { - inFiltered.setFilter(0, &medianFilter); - auto cfg = out.defaultConfig(TX_MODE); - cfg.copyFrom(info); - out.begin(cfg); -} - -void loop() { - copier.copy(); -} \ No newline at end of file diff --git a/examples/tests/performance/file-speeds-sd/file-speeds-sd.ino b/examples/tests/performance/file-speeds-sd/file-speeds-sd.ino deleted file mode 100644 index ebc898c5e0..0000000000 --- a/examples/tests/performance/file-speeds-sd/file-speeds-sd.ino +++ /dev/null @@ -1,76 +0,0 @@ -#include "SPI.h" -#include "SD.h" -#include "SD_MMC.h" - -#define PIN_AUDIO_KIT_SD_CARD_CS 13 -#define PIN_AUDIO_KIT_SD_CARD_MISO 2 -#define PIN_AUDIO_KIT_SD_CARD_MOSI 15 -#define PIN_AUDIO_KIT_SD_CARD_CLK 14 - -uint8_t data[1024 * 100]; -int len[] = { 1, 5, 10, 25, 100, 256, 512, 1024, 1024 * 10, 1024 * 100 }; -size_t totalSize = 1024 * 1024 * 1; -const char* test_file = "/test.txt"; - -void testWrite(Stream& file, int writeSize, int totalSize) { - memset(data, 0, sizeof(data)); - int32_t start = millis(); - while (totalSize > 0) { - int written = file.write(data, min(writeSize, totalSize)); - //Serial.println(written); - //assert(written > 0); - totalSize -= written; - } -} - -void testRead(Stream& file, int readSize, int totalSize) { - memset(data, 0, sizeof(data)); - while (totalSize > 0) { - int read = file.readBytes(data, min(readSize, totalSize)); - //assert(read>0); - totalSize -= read; - } -} - -void logTime(uint32_t start, int i, const char* name, const char* op) { - int32_t time = millis() - start; - float thru = (float)totalSize / (float)time / 1000.0; - Serial.printf("%s, %s, %d, %d, %f\n", op, name, i, time, thru); -} - -template -void testFS(const char* name, SD& sd, Open write, Open read) { - - for (int i : len) { - int32_t start = millis(); - auto file = sd.open(test_file, write); - file.seek(0); - assert(file); - testWrite(file, i, totalSize); - file.close(); - logTime(start, i, name, "Write"); - } - for (int i : len) { - int32_t start = millis(); - auto file = sd.open(test_file, read); - assert(file); - testRead(file, i, totalSize); - file.close(); - logTime(start, i, name, "Read"); - } - sd.end(); -} - - -void setup() { - Serial.begin(115200); - - SPI.begin(PIN_AUDIO_KIT_SD_CARD_CLK, PIN_AUDIO_KIT_SD_CARD_MISO, PIN_AUDIO_KIT_SD_CARD_MOSI, PIN_AUDIO_KIT_SD_CARD_CS); - while (!SD.begin(PIN_AUDIO_KIT_SD_CARD_CS)) { - Serial.println("SD error"); - delay(1000); - } - testFS("SD", SD, FILE_WRITE, FILE_READ); -} - -void loop() {} \ No newline at end of file diff --git a/examples/tests/performance/file-speeds-sd/result.txt b/examples/tests/performance/file-speeds-sd/result.txt deleted file mode 100644 index 494b540059..0000000000 --- a/examples/tests/performance/file-speeds-sd/result.txt +++ /dev/null @@ -1,21 +0,0 @@ -Write, SD, 1, 12813, 0.081837 -Write, SD, 5, 4578, 0.229047 -Write, SD, 10, 3550, 0.295374 -Write, SD, 25, 2927, 0.358243 -Write, SD, 100, 2623, 0.399762 -Write, SD, 256, 2607, 0.402216 -Write, SD, 512, 2571, 0.407848 -Write, SD, 1024, 2601, 0.403143 -Write, SD, 10240, 2531, 0.414293 -Write, SD, 102400, 2451, 0.427816 -Read, SD, 1, 13009, 0.080604 -Read, SD, 5, 4538, 0.231066 -Read, SD, 10, 3478, 0.301488 -Read, SD, 25, 2845, 0.368568 -Read, SD, 100, 2527, 0.414949 -Read, SD, 256, 2463, 0.425731 -Read, SD, 512, 2443, 0.429217 -Read, SD, 1024, 2432, 0.431158 -Read, SD, 10240, 2423, 0.432759 -Read, SD, 102400, 2421, 0.433117 - diff --git a/examples/tests/performance/file-speeds-sdfat/file-speeds-sdfat.ino b/examples/tests/performance/file-speeds-sdfat/file-speeds-sdfat.ino deleted file mode 100644 index 3665e5ca99..0000000000 --- a/examples/tests/performance/file-speeds-sdfat/file-speeds-sdfat.ino +++ /dev/null @@ -1,86 +0,0 @@ -#include "SPI.h" -#include "SdFat.h" - -#define PIN_AUDIO_KIT_SD_CARD_CS 13 -#define PIN_AUDIO_KIT_SD_CARD_MISO 2 -#define PIN_AUDIO_KIT_SD_CARD_MOSI 15 -#define PIN_AUDIO_KIT_SD_CARD_CLK 14 -#define SPI_CLOCK SD_SCK_MHZ(10) - -uint8_t* data = nullptr; -int len[] = { 1, 5, 10, 25, 100, 256, 512, 1024, 1024 * 10, 1024 * 100 }; -size_t totalSize = 1024 * 1024 * 1; -const char* test_file = "/test.txt"; - -void testWrite(Stream& file, int writeSize, int totalSize) { - memset(data, 0xff, sizeof(data)); - int32_t start = millis(); - while (totalSize > 0) { - int written = file.write(data, min(writeSize, totalSize)); - //Serial.println(written); - //assert(written > 0); - totalSize -= written; - } -} - -void testRead(Stream& file, int readSize, int totalSize) { - memset(data, 0, sizeof(data)); - while (totalSize > 0) { - int read = file.readBytes(data, min(readSize, totalSize)); - //assert(read>0); - totalSize -= read; - } -} - -void logTime(uint32_t start, int i, const char* name, const char* op) { - int32_t time = millis() - start; - float thru = (float)totalSize / (float)time / 1000.0; - Serial.printf("%s, %s, %d, %d, %f\n", op, name, i, time, thru); -} - - -template -void testFS(const char* name, SD& sd, Open write, Open read) { - SdSpiConfig cfg(PIN_AUDIO_KIT_SD_CARD_CS, DEDICATED_SPI, SPI_CLOCK, &SPI); - while (!sd.begin(cfg)) { - Serial.print(name); - Serial.println(" error"); - delay(1000); - } - - for (int i : len) { - int32_t start = millis(); - auto file = sd.open(test_file, write); - assert(file); - file.seek(0); - testWrite(file, len[i], totalSize); - file.close(); - logTime(start, i, name, "Write"); - } - for (int i : len) { - int32_t start = millis(); - auto file = sd.open(test_file, read); - assert(file); - testRead(file, len[i], totalSize); - file.close(); - logTime(start, i, name, "Read"); - } - sd.end(); -} - -void setup() { - Serial.begin(115200); - // allocate read/write buffer - data = new uint8_t[1024 * 100]; - assert(data != nullptr); - - // setup SPI pins - SPI.begin(PIN_AUDIO_KIT_SD_CARD_CLK, PIN_AUDIO_KIT_SD_CARD_MISO, PIN_AUDIO_KIT_SD_CARD_MOSI, PIN_AUDIO_KIT_SD_CARD_CS); - delay(1000); - - // test SD - SdFat sdfat; - testFS("SDFAT", sdfat, FILE_WRITE, FILE_READ); -} - -void loop() {} \ No newline at end of file diff --git a/examples/tests/performance/file-speeds-sdfat/result.txt b/examples/tests/performance/file-speeds-sdfat/result.txt deleted file mode 100644 index d3215b1332..0000000000 --- a/examples/tests/performance/file-speeds-sdfat/result.txt +++ /dev/null @@ -1 +0,0 @@ -There seems to be a bug in the library, since this is dumping! \ No newline at end of file diff --git a/examples/tests/performance/file-speeds-sdmmc/file-speeds-sdmmc.ino b/examples/tests/performance/file-speeds-sdmmc/file-speeds-sdmmc.ino deleted file mode 100644 index e529558013..0000000000 --- a/examples/tests/performance/file-speeds-sdmmc/file-speeds-sdmmc.ino +++ /dev/null @@ -1,75 +0,0 @@ -#include "SPI.h" -#include "SD.h" -#include "SD_MMC.h" - -#define PIN_AUDIO_KIT_SD_CARD_CS 13 -#define PIN_AUDIO_KIT_SD_CARD_MISO 2 -#define PIN_AUDIO_KIT_SD_CARD_MOSI 15 -#define PIN_AUDIO_KIT_SD_CARD_CLK 14 - -uint8_t data[1024 * 100]; -int len[] = { 1, 5, 10, 25, 100, 256, 512, 1024, 1024 * 10, 1024 * 100 }; -size_t totalSize = 1024 * 1024 * 1; -const char* test_file = "/test.txt"; - -void testWrite(Stream& file, int writeSize, int totalSize) { - memset(data, 0, sizeof(data)); - int32_t start = millis(); - while (totalSize > 0) { - int written = file.write(data, min(writeSize, totalSize)); - //Serial.println(written); - //assert(written > 0); - totalSize -= written; - } -} - -void testRead(Stream& file, int readSize, int totalSize) { - memset(data, 0, sizeof(data)); - while (totalSize > 0) { - int read = file.readBytes(data, min(readSize, totalSize)); - //assert(read>0); - totalSize -= read; - } -} - -void logTime(uint32_t start, int i, const char* name, const char* op) { - int32_t time = millis() - start; - float thru = (float)totalSize / (float)time / 1000.0; - Serial.printf("%s, %s, %d, %d, %f\n", op, name, i, time, thru); -} - -template -void testFS(const char* name, SD& sd, Open write, Open read) { - - for (int i : len) { - int32_t start = millis(); - auto file = sd.open(test_file, write); - file.seek(0); - assert(file); - testWrite(file, i, totalSize); - file.close(); - logTime(start, i, name, "Write"); - } - for (int i : len) { - int32_t start = millis(); - auto file = sd.open(test_file, read); - assert(file); - testRead(file, i, totalSize); - file.close(); - logTime(start, i, name, "Read"); - } - sd.end(); -} - - -void setup() { - Serial.begin(115200); - - while (!SD_MMC.begin()) { - Serial.println("SDMMC error"); - delay(1000); - } - testFS("SD_MMC", SD_MMC, FILE_WRITE, FILE_READ); -} - -void loop() {} \ No newline at end of file diff --git a/examples/tests/performance/file-speeds-sdmmc/result.txt b/examples/tests/performance/file-speeds-sdmmc/result.txt deleted file mode 100644 index f02426cc72..0000000000 --- a/examples/tests/performance/file-speeds-sdmmc/result.txt +++ /dev/null @@ -1,40 +0,0 @@ -Write, SD, 1, 12813, 0.081837 -Write, SD, 5, 4578, 0.229047 -Write, SD, 10, 3550, 0.295374 -Write, SD, 25, 2927, 0.358243 -Write, SD, 100, 2623, 0.399762 -Write, SD, 256, 2607, 0.402216 -Write, SD, 512, 2571, 0.407848 -Write, SD, 1024, 2601, 0.403143 -Write, SD, 10240, 2531, 0.414293 -Write, SD, 102400, 2451, 0.427816 -Read, SD, 1, 13009, 0.080604 -Read, SD, 5, 4538, 0.231066 -Read, SD, 10, 3478, 0.301488 -Read, SD, 25, 2845, 0.368568 -Read, SD, 100, 2527, 0.414949 -Read, SD, 256, 2463, 0.425731 -Read, SD, 512, 2443, 0.429217 -Read, SD, 1024, 2432, 0.431158 -Read, SD, 10240, 2423, 0.432759 -Read, SD, 102400, 2421, 0.433117 -Write, SD_MMC, 1, 10605, 0.098876 -Write, SD_MMC, 5, 2384, 0.439839 -Write, SD_MMC, 10, 1313, 0.798611 -Write, SD_MMC, 25, 728, 1.440352 -Write, SD_MMC, 100, 391, 2.681780 -Write, SD_MMC, 256, 351, 2.987396 -Write, SD_MMC, 512, 313, 3.350083 -Write, SD_MMC, 1024, 315, 3.328813 -Write, SD_MMC, 10240, 215, 4.877098 -Write, SD_MMC, 102400, 109, 9.619963 -Read, SD_MMC, 1, 10703, 0.097970 -Read, SD_MMC, 5, 2230, 0.470213 -Read, SD_MMC, 10, 1171, 0.895453 -Read, SD_MMC, 25, 537, 1.952656 -Read, SD_MMC, 100, 219, 4.788018 -Read, SD_MMC, 256, 155, 6.765007 -Read, SD_MMC, 512, 135, 7.767230 -Read, SD_MMC, 1024, 124, 8.456258 -Read, SD_MMC, 10240, 115, 9.118052 -Read, SD_MMC, 102400, 115, 9.118052 diff --git a/examples/tests/performance/file-speeds-vfssd/file-speeds-vfssd.ino b/examples/tests/performance/file-speeds-vfssd/file-speeds-vfssd.ino deleted file mode 100644 index f919d6d980..0000000000 --- a/examples/tests/performance/file-speeds-vfssd/file-speeds-vfssd.ino +++ /dev/null @@ -1,83 +0,0 @@ -#define AUDIOBOARD_SD -#include "AudioTools.h" -#include "AudioTools/Disk/VFS_SDSPI.h" -#include "AudioTools/Disk/VFSFile.h" - -#define PIN_AUDIO_KIT_SD_CARD_CS 13 -#define PIN_AUDIO_KIT_SD_CARD_MISO 2 -#define PIN_AUDIO_KIT_SD_CARD_MOSI 15 -#define PIN_AUDIO_KIT_SD_CARD_CLK 14 - -const size_t max_len = 1024 * 100; -uint8_t *data = nullptr; -int len[] = { 1, 5, 10, 25, 100, 256, 512, 1024, 1024 * 10, 1024 * 100 }; -size_t totalSize = 1024 * 1024 * 1; -const char* test_file = "/test.txt"; - -void testWrite(Stream& file, int writeSize, int totalSize) { - memset(data, 0, max_len); - int32_t start = millis(); - while (totalSize > 0) { - int written = file.write(data, min(writeSize, totalSize)); - //Serial.println(written); - //assert(written > 0); - totalSize -= written; - } -} - -void testRead(Stream& file, int readSize, int totalSize) { - memset(data, 0, max_len); - while (totalSize > 0) { - int read = file.readBytes(data, min(readSize, totalSize)); - //assert(read>0); - totalSize -= read; - } -} - -void logTime(uint32_t start, int i, const char* name, const char* op) { - int32_t time = millis() - start; - float thru = (float)totalSize / (float)time / 1000.0; - Serial.printf("%s, %s, %d, %d, %f\n", op, name, i, time, thru); -} - -template -void testFS(const char* name, SD& sd, Open write, Open read) { - while (!sd.begin()) { - Serial.print(name); - Serial.println(" error"); - delay(1000); - } - - for (int i : len) { - int32_t start = millis(); - auto file = sd.open(test_file, write); - file.seek(0); - assert(file); - testWrite(file, i, totalSize); - file.close(); - logTime(start, i, name, "Write"); - } - for (int i : len) { - int32_t start = millis(); - auto file = sd.open(test_file, read); - assert(file); - testRead(file, i, totalSize); - file.close(); - logTime(start, i, name, "Read"); - } - sd.end(); -} - -void setup() { - Serial.begin(115200); - - VFS_SDSPI sd; - - data = new uint8_t[max_len]; - assert(data!=nullptr); - - testFS("VFS_SDSPI", sd, VFS_FILE_WRITE, VFS_FILE_READ); - //testFS("VFS_SDMMC", sdmmc, VFS_FILE_WRITE, VFS_FILE_READ); -} - -void loop() {} \ No newline at end of file diff --git a/examples/tests/performance/file-speeds-vfssd/result.txt b/examples/tests/performance/file-speeds-vfssd/result.txt deleted file mode 100644 index 2dbe9c13c3..0000000000 --- a/examples/tests/performance/file-speeds-vfssd/result.txt +++ /dev/null @@ -1,22 +0,0 @@ -Write, VFS_SDSPI, 1, 3046, 0.344247 -Write, VFS_SDSPI, 5, 2949, 0.355570 -Write, VFS_SDSPI, 10, 2931, 0.357754 -Write, VFS_SDSPI, 25, 2926, 0.358365 -Write, VFS_SDSPI, 100, 2920, 0.359101 -Write, VFS_SDSPI, 256, 2925, 0.358488 -Write, VFS_SDSPI, 512, 2912, 0.360088 -Write, VFS_SDSPI, 1024, 2925, 0.358488 -Write, VFS_SDSPI, 10240, 2925, 0.358488 -Write, VFS_SDSPI, 102400, 2928, 0.358120 -Read, VFS_SDSPI, 1, 2500, 0.419430 -Read, VFS_SDSPI, 5, 1521, 0.689399 -Read, VFS_SDSPI, 10, 1398, 0.750054 -Read, VFS_SDSPI, 25, 1324, 0.791976 -Read, VFS_SDSPI, 100, 1287, 0.814744 -Read, VFS_SDSPI, 256, 1169, 0.896985 -Read, VFS_SDSPI, 512, 1127, 0.930413 -Read, VFS_SDSPI, 1024, 936, 1.120274 -Read, VFS_SDSPI, 10240, 665, 1.576806 -Read, VFS_SDSPI, 102400, 639, 1.640964 - - diff --git a/examples/tests/performance/file-speeds-vfssdmmc/file-speeds-vfssdmmc.ino b/examples/tests/performance/file-speeds-vfssdmmc/file-speeds-vfssdmmc.ino deleted file mode 100644 index 2340212096..0000000000 --- a/examples/tests/performance/file-speeds-vfssdmmc/file-speeds-vfssdmmc.ino +++ /dev/null @@ -1,78 +0,0 @@ -#define AUDIOBOARD_SD -#include "AudioTools.h" -#include "AudioTools/Disk/VFSFile.h" -#include "AudioTools/Disk/VFS_SDMMC.h" - -const size_t max_len = 1024 * 100; -uint8_t *data = nullptr; -int len[] = { 1, 5, 10, 25, 100, 256, 512, 1024, 1024 * 10, 1024 * 100 }; -size_t totalSize = 1024 * 1024 * 1; -const char* test_file = "/test.txt"; - -void testWrite(Stream& file, int writeSize, int totalSize) { - memset(data, 0, max_len); - int32_t start = millis(); - while (totalSize > 0) { - int written = file.write(data, min(writeSize, totalSize)); - //Serial.println(written); - //assert(written > 0); - totalSize -= written; - } -} - -void testRead(Stream& file, int readSize, int totalSize) { - memset(data, 0, max_len); - while (totalSize > 0) { - int read = file.readBytes(data, min(readSize, totalSize)); - //assert(read>0); - totalSize -= read; - } -} - -void logTime(uint32_t start, int i, const char* name, const char* op) { - int32_t time = millis() - start; - float thru = (float)totalSize / (float)time / 1000.0; - Serial.printf("%s, %s, %d, %d, %f\n", op, name, i, time, thru); -} - -template -void testFS(const char* name, SD& sd, Open write, Open read) { - while (!sd.begin()) { - Serial.print(name); - Serial.println(" error"); - delay(1000); - } - - for (int i : len) { - int32_t start = millis(); - auto file = sd.open(test_file, write); - file.seek(0); - assert(file); - testWrite(file, i, totalSize); - file.close(); - logTime(start, i, name, "Write"); - } - for (int i : len) { - int32_t start = millis(); - auto file = sd.open(test_file, read); - assert(file); - testRead(file, i, totalSize); - file.close(); - logTime(start, i, name, "Read"); - } - sd.end(); -} - -void setup() { - Serial.begin(115200); - - VFS_SDMMC sdmmc; - - data = new uint8_t[max_len]; - assert(data!=nullptr); - - // testFS("VFS_SDSPI", sd, VFS_FILE_WRITE, VFS_FILE_READ); - testFS("VFS_SDMMC", sdmmc, VFS_FILE_WRITE, VFS_FILE_READ); -} - -void loop() {} \ No newline at end of file diff --git a/examples/tests/performance/file-speeds-vfssdmmc/result.txt b/examples/tests/performance/file-speeds-vfssdmmc/result.txt deleted file mode 100644 index 9b91b919d7..0000000000 --- a/examples/tests/performance/file-speeds-vfssdmmc/result.txt +++ /dev/null @@ -1,21 +0,0 @@ - -Write, VFS_SDMMC, 1, 2478, 0.423154 -Write, VFS_SDMMC, 5, 2363, 0.443748 -Write, VFS_SDMMC, 10, 2342, 0.447727 -Write, VFS_SDMMC, 25, 2336, 0.448877 -Write, VFS_SDMMC, 100, 2331, 0.449840 -Write, VFS_SDMMC, 256, 2337, 0.448685 -Write, VFS_SDMMC, 512, 2325, 0.451000 -Write, VFS_SDMMC, 1024, 2335, 0.449069 -Write, VFS_SDMMC, 10240, 2337, 0.448685 -Write, VFS_SDMMC, 102400, 2336, 0.448877 -Read, VFS_SDMMC, 1, 1808, 0.579965 -Read, VFS_SDMMC, 5, 827, 1.267928 -Read, VFS_SDMMC, 10, 705, 1.487342 -Read, VFS_SDMMC, 25, 631, 1.661769 -Read, VFS_SDMMC, 100, 594, 1.765279 -Read, VFS_SDMMC, 256, 508, 2.064126 -Read, VFS_SDMMC, 512, 467, 2.245345 -Read, VFS_SDMMC, 1024, 277, 3.785473 -Read, VFS_SDMMC, 10240, 88, 11.915637 -Read, VFS_SDMMC, 102400, 69, 15.196754 diff --git a/examples/tests/performance/mp3-Speed/mp3-Speed.ino b/examples/tests/performance/mp3-Speed/mp3-Speed.ino index e0df5008a1..8b2c0f56f4 100644 --- a/examples/tests/performance/mp3-Speed/mp3-Speed.ino +++ b/examples/tests/performance/mp3-Speed/mp3-Speed.ino @@ -9,9 +9,9 @@ * */ #include "AudioTools.h" -#include "AudioTools/Disk/AudioSourceSDFAT.h" -#include "AudioTools/AudioCodecs/CodecMP3Helix.h" -#include "AudioTools/AudioLibs/AudioBoardStream.h" +#include "AudioLibs/AudioSourceSDFAT.h" +#include "AudioCodecs/CodecMP3Helix.h" +#include "AudioLibs/AudioKit.h" const char *startFilePath="/"; const char* ext="mp3"; @@ -23,11 +23,14 @@ int count=0; void setup() { Serial.begin(115200); - AudioToolsLogger.begin(Serial, AudioToolsLogLevel::Warning); + AudioLogger::instance().begin(Serial, AudioLogger::Warning); // sd_active is setting up SPI with the right SD pins by calling SPI.begin(PIN_AUDIO_KIT_SD_CARD_CLK, PIN_AUDIO_KIT_SD_CARD_MISO, PIN_AUDIO_KIT_SD_CARD_MOSI, PIN_AUDIO_KIT_SD_CARD_CS); + // convert bytes to samples (frames) + out.setBytesPerSample(2*16); + // setup player player.setDelayIfOutputFull(0); player.setVolume(0.1); diff --git a/examples/tests/performance/mp3-SynchronizedBufferRTOS/mp3-SynchronizedBufferRTOS.ino b/examples/tests/performance/mp3-SynchronizedBufferRTOS/mp3-SynchronizedBufferRTOS.ino index 2ca5e82116..dd84af838b 100644 --- a/examples/tests/performance/mp3-SynchronizedBufferRTOS/mp3-SynchronizedBufferRTOS.ino +++ b/examples/tests/performance/mp3-SynchronizedBufferRTOS/mp3-SynchronizedBufferRTOS.ino @@ -1,84 +1,82 @@ /** - * @file mp3-BufferRTOS.ino + * @file mp3-SynchronizedBufferRTOS.ino * @author Phil Schatzmann - * @brief Performance test for mp3 decoding form SD on core 1 and pass the data - * to core 0; + * @brief Performance test for mp3 decoding form SD on core 1 and pass the data to core 0; * @version 0.1 * @date 2022-12-06 - * + * * @copyright Copyright (c) 2022 - * + * */ #include "AudioTools.h" -#include "AudioTools/AudioCodecs/CodecMP3Helix.h" -#include "AudioTools/AudioLibs/AudioBoardStream.h" -#include "AudioTools/AudioLibs/Concurrency.h" -#include "AudioTools/Disk/AudioSourceSDFAT.h" +#include "AudioLibs/AudioSourceSDFAT.h" +#include "AudioCodecs/CodecMP3Helix.h" +#include "AudioLibs/AudioKit.h" +#include "freertos-all.h" const int buffer_count = 30; const int buffer_size = 512; -const char *startFilePath = "/"; -const char *ext = "mp3"; +const char *startFilePath="/"; +const char* ext="mp3"; AudioSourceSDFAT source(startFilePath, ext, PIN_AUDIO_KIT_SD_CARD_CS); MP3DecoderHelix decoder; -BufferRTOS buffer(buffer_size *buffer_count, buffer_size, - portMAX_DELAY, 10); // fast synchronized buffer -QueueStream out(buffer); // convert Buffer to Stream +SynchronizedBufferRTOS buffer(buffer_size*buffer_count, buffer_size, portMAX_DELAY, 10); // fast synchronized buffer +QueueStream out(buffer); // convert Buffer to Stream AudioPlayer player(source, out, decoder); int32_t get_data(uint8_t *data, int32_t bytes); uint8_t data[buffer_size]; // Performance measurement: Receive data on core 0 -Task task("name", 10000, 10, 0); +Task task("name",10000,10, [](){ + uint64_t start = millis(); + size_t bytes = 0; + for(int i=0;i<100;i++){ + bytes += get_data(data, buffer_size); + yield(); + } + uint64_t timeMs = millis()-start; + // prevent div by 0 + if (timeMs==0){ + timeMs=1; + } + // calculate samples per second + size_t samples_per_second = bytes / timeMs * 1000 / 4; + Serial.print("Samples per second: "); + Serial.println(samples_per_second); +}); + // Provide data to A2DP int32_t get_data(uint8_t *data, int32_t bytes) { - size_t result_bytes = buffer.readArray(data, bytes); - // LOGI("get_data_channels %d -> %d of (%d)", bytes, result_bytes , - // buffer.available()); - return result_bytes; + size_t result_bytes = buffer.readArray(data, bytes); + //LOGI("get_data_channels %d -> %d of (%d)", bytes, result_bytes , buffer.available()); + return result_bytes; } void setup() { - Serial.begin(115200); - AudioToolsLogger.begin(Serial, AudioToolsLogLevel::Warning); + Serial.begin(115200); + AudioLogger::instance().begin(Serial, AudioLogger::Warning); - // sd_active is setting up SPI with the right SD pins by calling - SPI.begin(PIN_AUDIO_KIT_SD_CARD_CLK, PIN_AUDIO_KIT_SD_CARD_MISO, - PIN_AUDIO_KIT_SD_CARD_MOSI, PIN_AUDIO_KIT_SD_CARD_CS); + // sd_active is setting up SPI with the right SD pins by calling + SPI.begin(PIN_AUDIO_KIT_SD_CARD_CLK, PIN_AUDIO_KIT_SD_CARD_MISO, PIN_AUDIO_KIT_SD_CARD_MOSI, PIN_AUDIO_KIT_SD_CARD_CS); - // start QueueStream - out.begin(); + // start QueueStream + out.begin(); - // setup player - player.setDelayIfOutputFull(0); - player.setVolume(0.1); - player.begin(); + // setup player + player.setDelayIfOutputFull(0); + player.setVolume(0.1); + player.begin(); + + // fill buffer with some data + player.getStreamCopy().copyN(5); - // fill buffer with some data - player.getStreamCopy().copyN(5); + // start the task on kernel 0 + task.Start(0); - // start the task on kernel 0 - task.begin([]() { - uint64_t start = millis(); - size_t bytes = 0; - for (int i = 0; i < 100; i++) { - bytes += get_data(data, buffer_size); - yield(); - } - uint64_t timeMs = millis() - start; - // prevent div by 0 - if (timeMs == 0) { - timeMs = 1; - } - // calculate samples per second - size_t samples_per_second = bytes / timeMs * 1000 / 4; - Serial.print("Samples per second: "); - Serial.println(samples_per_second); - }); } void loop() { - // decode data to buffer - player.copy(); + // decode data to buffer + player.copy(); } \ No newline at end of file diff --git a/examples/tests/performance/mp3-SynchronizedNBuffer/mp3-SynchronizedNBuffer.ino b/examples/tests/performance/mp3-SynchronizedNBuffer/mp3-SynchronizedNBuffer.ino index 2d16d05cdb..bc0378cd66 100644 --- a/examples/tests/performance/mp3-SynchronizedNBuffer/mp3-SynchronizedNBuffer.ino +++ b/examples/tests/performance/mp3-SynchronizedNBuffer/mp3-SynchronizedNBuffer.ino @@ -1,84 +1,82 @@ /** - * @file mp3-BufferRTOS.ino + * @file mp3-SynchronizedBufferRTOS.ino * @author Phil Schatzmann - * @brief Performance test for mp3 decoding form SD on core 1 and pass the data - * to core 0; + * @brief Performance test for mp3 decoding form SD on core 1 and pass the data to core 0; * @version 0.1 * @date 2022-12-06 - * + * * @copyright Copyright (c) 2022 - * + * */ #include "AudioTools.h" -#include "AudioTools/AudioCodecs/CodecMP3Helix.h" -#include "AudioTools/AudioLibs/AudioBoardStream.h" -#include "AudioTools/AudioLibs/Concurrency.h" -#include "AudioTools/Disk/AudioSourceSDFAT.h" +#include "AudioLibs/AudioSourceSDFAT.h" +#include "AudioCodecs/CodecMP3Helix.h" +#include "AudioLibs/AudioKit.h" +#include "freertos-all.h" const int buffer_count = 30; const int buffer_size = 512; -const char *startFilePath = "/"; -const char *ext = "mp3"; +const char *startFilePath="/"; +const char* ext="mp3"; AudioSourceSDFAT source(startFilePath, ext, PIN_AUDIO_KIT_SD_CARD_CS); MP3DecoderHelix decoder; -SynchronizedNBuffer buffer(buffer_size, buffer_count, portMAX_DELAY, - 10); -QueueStream out(buffer); // convert Buffer to Stream +SynchronizedNBuffer buffer(buffer_size, buffer_count, portMAX_DELAY, 10); +QueueStream out(buffer); // convert Buffer to Stream AudioPlayer player(source, out, decoder); int32_t get_data(uint8_t *data, int32_t bytes); uint8_t data[buffer_size]; // Performance measurement: Receive data on core 0 -Task task("name", 10000, 10, 0); +Task task("name",10000,10, [](){ + uint64_t start = millis(); + size_t bytes = 0; + for(int i=0;i<100;i++){ + bytes += get_data(data, buffer_size); + } + //yield(); + uint64_t timeMs = millis()-start; + // prevent div by 0 + if (timeMs==0){ + timeMs=1; + } + // calculate samples per second + size_t samples_per_second = bytes / timeMs * 1000 / 4; + Serial.print("Samples per second: "); + Serial.println(samples_per_second); +}); + // Provide data to A2DP int32_t get_data(uint8_t *data, int32_t bytes) { - size_t result_bytes = buffer.readArray(data, bytes); - // LOGI("get_data_channels %d -> %d of (%d)", bytes, result_bytes , - // buffer.available()); - return result_bytes; + size_t result_bytes = buffer.readArray(data, bytes); + //LOGI("get_data_channels %d -> %d of (%d)", bytes, result_bytes , buffer.available()); + return result_bytes; } void setup() { - Serial.begin(115200); - AudioToolsLogger.begin(Serial, AudioToolsLogLevel::Warning); + Serial.begin(115200); + AudioLogger::instance().begin(Serial, AudioLogger::Warning); - // sd_active is setting up SPI with the right SD pins by calling - SPI.begin(PIN_AUDIO_KIT_SD_CARD_CLK, PIN_AUDIO_KIT_SD_CARD_MISO, - PIN_AUDIO_KIT_SD_CARD_MOSI, PIN_AUDIO_KIT_SD_CARD_CS); + // sd_active is setting up SPI with the right SD pins by calling + SPI.begin(PIN_AUDIO_KIT_SD_CARD_CLK, PIN_AUDIO_KIT_SD_CARD_MISO, PIN_AUDIO_KIT_SD_CARD_MOSI, PIN_AUDIO_KIT_SD_CARD_CS); - // start QueueStream - out.begin(); + // start QueueStream + out.begin(); - // setup player - player.setDelayIfOutputFull(0); - player.setVolume(0.1); - player.begin(); + // setup player + player.setDelayIfOutputFull(0); + player.setVolume(0.1); + player.begin(); + + // fill buffer with some data + player.getStreamCopy().copyN(5); - // fill buffer with some data - player.getStreamCopy().copyN(5); + // start the task on kernel 0 + task.Start(0); - // start the task on kernel 0 - task.begin([]() { - uint64_t start = millis(); - size_t bytes = 0; - for (int i = 0; i < 100; i++) { - bytes += get_data(data, buffer_size); - } - // yield(); - uint64_t timeMs = millis() - start; - // prevent div by 0 - if (timeMs == 0) { - timeMs = 1; - } - // calculate samples per second - size_t samples_per_second = bytes / timeMs * 1000 / 4; - Serial.print("Samples per second: "); - Serial.println(samples_per_second); - }); } void loop() { - // decode data to buffer - player.copy(); + // decode data to buffer + player.copy(); } \ No newline at end of file diff --git a/examples/tests/performance/mp3-SynchronizedRingBuffer/mp3-SynchronizedRingBuffer.ino b/examples/tests/performance/mp3-SynchronizedRingBuffer/mp3-SynchronizedRingBuffer.ino index 22ad6c879a..981fd18d24 100644 --- a/examples/tests/performance/mp3-SynchronizedRingBuffer/mp3-SynchronizedRingBuffer.ino +++ b/examples/tests/performance/mp3-SynchronizedRingBuffer/mp3-SynchronizedRingBuffer.ino @@ -1,85 +1,84 @@ /** * @file mp3-SynchronizedRingBuffer.ino * @author Phil Schatzmann - * @brief Performance test for mp3 decoding form SD on core 1 and pass the data - * to core 0; + * @brief Performance test for mp3 decoding form SD on core 1 and pass the data to core 0; * @version 0.1 * @date 2022-12-06 - * + * * @copyright Copyright (c) 2022 - * + * */ #include "AudioTools.h" -#include "AudioTools/AudioCodecs/CodecMP3Helix.h" -#include "AudioTools/AudioLibs/AudioBoardStream.h" -#include "AudioTools/AudioLibs/Concurrency.h" -#include "AudioTools/Disk/AudioSourceSDFAT.h" +#include "AudioLibs/AudioSourceSDFAT.h" +#include "AudioCodecs/CodecMP3Helix.h" +#include "AudioLibs/AudioKit.h" +#include "freertos-all.h" const int buffer_count = 30; const int buffer_size = 512; -const char *startFilePath = "/"; -const char *ext = "mp3"; +const char *startFilePath="/"; +const char* ext="mp3"; AudioSourceSDFAT source(startFilePath, ext, PIN_AUDIO_KIT_SD_CARD_CS); MP3DecoderHelix decoder; audio_tools::Mutex mutex; -RingBuffer nbuffer(buffer_size *buffer_count); +RingBuffer nbuffer(buffer_size*buffer_count); SynchronizedBuffer buffer(nbuffer, mutex); -QueueStream out(buffer); // convert Buffer to Stream +QueueStream out(buffer); // convert Buffer to Stream AudioPlayer player(source, out, decoder); int32_t get_data(uint8_t *data, int32_t bytes); uint8_t data[buffer_size]; // Performance measurement: Receive data on core 0 -Task task("name", 10000, 10, 0); +Task task("name",10000,10, [](){ + uint64_t start = millis(); + size_t bytes = 0; + for(int i=0;i<100;i++){ + bytes += get_data(data, buffer_size); + yield(); + } + uint64_t timeMs = millis()-start; + // prevent div by 0 + if (timeMs==0){ + timeMs=1; + } + // calculate samples per second + size_t samples_per_second = bytes / timeMs * 1000 / 4; + Serial.print("Samples per second: "); + Serial.println(samples_per_second); +}); + // Provide data to A2DP int32_t get_data(uint8_t *data, int32_t bytes) { - size_t result_bytes = buffer.readArray(data, bytes); - // LOGI("get_data_channels %d -> %d of (%d)", bytes, result_bytes , - // buffer.available()); - return result_bytes; + size_t result_bytes = buffer.readArray(data, bytes); + //LOGI("get_data_channels %d -> %d of (%d)", bytes, result_bytes , buffer.available()); + return result_bytes; } void setup() { - Serial.begin(115200); - AudioToolsLogger.begin(Serial, AudioToolsLogLevel::Warning); + Serial.begin(115200); + AudioLogger::instance().begin(Serial, AudioLogger::Warning); - // sd_active is setting up SPI with the right SD pins by calling - SPI.begin(PIN_AUDIO_KIT_SD_CARD_CLK, PIN_AUDIO_KIT_SD_CARD_MISO, - PIN_AUDIO_KIT_SD_CARD_MOSI, PIN_AUDIO_KIT_SD_CARD_CS); + // sd_active is setting up SPI with the right SD pins by calling + SPI.begin(PIN_AUDIO_KIT_SD_CARD_CLK, PIN_AUDIO_KIT_SD_CARD_MISO, PIN_AUDIO_KIT_SD_CARD_MOSI, PIN_AUDIO_KIT_SD_CARD_CS); - // start QueueStream - out.begin(); + // start QueueStream + out.begin(); - // setup player - player.setDelayIfOutputFull(0); - player.setVolume(0.1); - player.begin(); + // setup player + player.setDelayIfOutputFull(0); + player.setVolume(0.1); + player.begin(); + + // fill buffer with some data + player.getStreamCopy().copyN(5); - // fill buffer with some data - player.getStreamCopy().copyN(5); + // start the task on kernel 0 + task.Start(0); - // start the task on kernel 0 - task.begin([]() { - uint64_t start = millis(); - size_t bytes = 0; - for (int i = 0; i < 100; i++) { - bytes += get_data(data, buffer_size); - yield(); - } - uint64_t timeMs = millis() - start; - // prevent div by 0 - if (timeMs == 0) { - timeMs = 1; - } - // calculate samples per second - size_t samples_per_second = bytes / timeMs * 1000 / 4; - Serial.print("Samples per second: "); - Serial.println(samples_per_second); - }); } void loop() { - // decode data to buffer - player.copy(); + // decode data to buffer + player.copy(); } \ No newline at end of file diff --git a/examples/tests/performance/sine/sine.ino b/examples/tests/performance/sine/sine.ino index ec90bc082a..7d878686df 100644 --- a/examples/tests/performance/sine/sine.ino +++ b/examples/tests/performance/sine/sine.ino @@ -5,7 +5,7 @@ SineWaveGenerator sine_wave(32000); // subclass of SoundGenerator SineFromTable sine_table(32000); // subclass of SoundGenerator with max amplitude of 32000 FastSineGenerator sine_fast(32000); -size_t measure(SoundGenerator *gen){ +size_t measure(int sec, SoundGenerator *gen){ uint64_t start = millis(); size_t count = 0; for(int i=0;i<1000000;i++){ @@ -28,10 +28,10 @@ void setup(){ } void loop(){ - Serial.print(resultStr("SineWaveGenerator", measure(&sine_wave))); + Serial.print(resultStr("SineWaveGenerator", measure(sec, &sine_wave))); Serial.print(" - "); - Serial.print(resultStr("SineFromTable", measure(&sine_table))); + Serial.print(resultStr("SineFromTable", measure(sec, &sine_table))); Serial.print(" - "); - Serial.print(resultStr("FastSineGenerator", measure(&sine_fast))); + Serial.print(resultStr("FastSineGenerator", measure(sec, &sine_fast))); Serial.println(); } \ No newline at end of file diff --git a/examples/tests/performance/throttle/throttle.ino b/examples/tests/performance/throttle/throttle.ino deleted file mode 100644 index 5a72822f60..0000000000 --- a/examples/tests/performance/throttle/throttle.ino +++ /dev/null @@ -1,20 +0,0 @@ -#include "AudioTools.h" - -AudioInfo info(44100, 2, 16); -SilenceGenerator silence; -GeneratedSoundStream sound(silence); -Throttle throttle(sound); -MeasuringStream out(200, &Serial); -StreamCopy copier(out, throttle); - -void setup() { - Serial.begin(115200); - AudioToolsLogger.begin(Serial, AudioToolsLogLevel::Warning); - - out.begin(info); - sound.begin(info); - throttle.begin(info); - -} - -void loop() { copier.copy(); } \ No newline at end of file diff --git a/examples/tests/performance/wifi/wifi.ino b/examples/tests/performance/wifi/wifi.ino deleted file mode 100644 index 3e1d207761..0000000000 --- a/examples/tests/performance/wifi/wifi.ino +++ /dev/null @@ -1,17 +0,0 @@ -#include "AudioTools.h" - -URLStream url("http://github.com/SSID%22,%22PASSWORD"); // or replace with ICYStream to get metadata -MeasuringStream out(50, &Serial); // final output of decoded stream -StreamCopy copier(out, url); // copy url to decoder - -void setup(){ - Serial.begin(115200); - AudioToolsLogger.begin(Serial, AudioToolsLogLevel::Warning); - while(!Serial); - - url.begin("http://stream.srg-ssr.ch/m/rsj/mp3_128","audio/mp3"); -} - -void loop(){ - copier.copy(); -} diff --git a/examples/tests/effects/pitch-shift-180/pitch-shift-180.ino b/examples/tests/pitch-shift/pitch-shift-180/pitch-shift-180.ino similarity index 91% rename from examples/tests/effects/pitch-shift-180/pitch-shift-180.ino rename to examples/tests/pitch-shift/pitch-shift-180/pitch-shift-180.ino index 91973249f3..456dd0835b 100644 --- a/examples/tests/effects/pitch-shift-180/pitch-shift-180.ino +++ b/examples/tests/pitch-shift/pitch-shift-180/pitch-shift-180.ino @@ -6,7 +6,7 @@ float pitch_shift = 1.5; AudioInfo info(44100, 1, 16); SineWaveGenerator sineWave(32000); // subclass of SoundGenerator with max amplitude of 32000 GeneratedSoundStream sound(sineWave); // Stream generated from sine wave -CsvOutput out(Serial); +CsvOutput out(Serial, 1); //PitchShiftOutput> pitchShift(out); PitchShiftOutput> pitchShift(out); //PitchShiftOutput> pitchShift(out); @@ -16,7 +16,7 @@ StreamCopy copier(pitchShift, sound); // copies sound to o void setup(void) { // Open Serial Serial.begin(115200); - AudioToolsLogger.begin(Serial, AudioToolsLogLevel::Warning); + AudioLogger::instance().begin(Serial, AudioLogger::Warning); // Define CSV Output out.begin(info); diff --git a/examples/tests/effects/pitch-shift-simple/pitch-shift-simple.ino b/examples/tests/pitch-shift/pitch-shift-simple/pitch-shift-simple.ino similarity index 91% rename from examples/tests/effects/pitch-shift-simple/pitch-shift-simple.ino rename to examples/tests/pitch-shift/pitch-shift-simple/pitch-shift-simple.ino index ce3eb6b2ee..f15ab7f000 100644 --- a/examples/tests/effects/pitch-shift-simple/pitch-shift-simple.ino +++ b/examples/tests/pitch-shift/pitch-shift-simple/pitch-shift-simple.ino @@ -6,7 +6,7 @@ float pitch_shift = 1.5; AudioInfo info(44100, 1, 16); SineWaveGenerator sineWave(32000); // subclass of SoundGenerator with max amplitude of 32000 GeneratedSoundStream sound(sineWave); // Stream generated from sine wave -CsvOutput out(Serial); +CsvOutput out(Serial, 1); PitchShiftOutput> pitchShift(out); //PitchShiftOutput> pitchShift(out); //PitchShiftOutput> pitchShift(out); @@ -16,7 +16,7 @@ StreamCopy copier(pitchShift, sound); // copies sound to o void setup(void) { // Open Serial Serial.begin(115200); - AudioToolsLogger.begin(Serial, AudioToolsLogLevel::Warning); + AudioLogger::instance().begin(Serial, AudioLogger::Warning); // Define CSV Output out.begin(info); diff --git a/examples/tests/effects/pitch-shift/pitch-shift.ino b/examples/tests/pitch-shift/pitch-shift/pitch-shift.ino similarity index 91% rename from examples/tests/effects/pitch-shift/pitch-shift.ino rename to examples/tests/pitch-shift/pitch-shift/pitch-shift.ino index 2e39dee891..2c7c8a8d5b 100644 --- a/examples/tests/effects/pitch-shift/pitch-shift.ino +++ b/examples/tests/pitch-shift/pitch-shift/pitch-shift.ino @@ -6,7 +6,7 @@ float pitch_shift = 1.5; AudioInfo info(44100, 1, 16); SineWaveGenerator sineWave(32000); // subclass of SoundGenerator with max amplitude of 32000 GeneratedSoundStream sound(sineWave); // Stream generated from sine wave -CsvOutput out(Serial); +CsvOutput out(Serial, 1); //PitchShiftOutput> pitchShift(out); //PitchShiftOutput> pitchShift(out); PitchShiftOutput> pitchShift(out); @@ -16,7 +16,7 @@ StreamCopy copier(pitchShift, sound); // copies sound to o void setup(void) { // Open Serial Serial.begin(115200); - AudioToolsLogger.begin(Serial, AudioToolsLogLevel::Warning); + AudioLogger::instance().begin(Serial, AudioLogger::Warning); // Define CSV Output out.begin(info); diff --git a/examples/tests/player/test-vfs/test-vfs.ino b/examples/tests/player/test-vfs/test-vfs.ino deleted file mode 100644 index a27b868346..0000000000 --- a/examples/tests/player/test-vfs/test-vfs.ino +++ /dev/null @@ -1,22 +0,0 @@ -#include "AudioTools.h" -#include "AudioTools/Disk/AudioSourceVFS.h" -#include "AudioTools/Disk/VFS_SDSPI.h" - -// We use an AudioKit or LyraT for the tests which uses the following pins -// CS, MOSI, MISO, SCK -VFS_SDSPI sd(13, 15, 2, 14); -AudioSourceVFS source(sd, "/sdcard", ".mp3"); - -void setup() { - Serial.begin(115200); - AudioToolsLogger.begin(Serial, AudioToolsLogLevel::Info); - - source.begin(); - for (int j = 0; j < 10; j++) { - VFSFile* p_file = (VFSFile*)source.selectStream(j); - if (p_file) - Serial.println(p_file->name()); - } -} - -void loop() {} \ No newline at end of file diff --git a/examples/tests/rp2040/buffer2040_1/buffer2040_1.ino b/examples/tests/rp2040/buffer2040_1/buffer2040_1.ino deleted file mode 100644 index 64fd24560e..0000000000 --- a/examples/tests/rp2040/buffer2040_1/buffer2040_1.ino +++ /dev/null @@ -1,36 +0,0 @@ -#include "AudioTools.h" -#include "AudioTools/Concurrency/RP2040.h" - -BufferRP2040T buffer(10, 20); //20 blocks with 10 ints->100 ints - -void setup() { - Serial.begin(115200); - AudioToolsLogger.begin(Serial, AudioToolsLogLevel::Info); - while (!Serial) - ; - delay(1000); - Serial.println("testing..."); - - assert(buffer.size() == 200); - assert(buffer.available() == 0); - assert(buffer.availableForWrite() == 200); - for (int j = 0; j < 10; j++) { - assert(buffer.write(j)); - } - assert(buffer.size() == 200); - assert(buffer.available() == 10); - - int tmp[20]; - assert(buffer.readArray(tmp, 20) == 10); - - for (int j = 0; j < 10; j++) { - assert(tmp[j] == j); - } - - assert(buffer.writeArray(tmp, 10) == 10); - - Serial.println("success!"); -} - -void loop() { -} \ No newline at end of file diff --git a/examples/tests/rp2040/buffer2040_2/buffer2040_2.ino b/examples/tests/rp2040/buffer2040_2/buffer2040_2.ino deleted file mode 100644 index 3941d754b2..0000000000 --- a/examples/tests/rp2040/buffer2040_2/buffer2040_2.ino +++ /dev/null @@ -1,31 +0,0 @@ - -#include "AudioTools.h" -#include "AudioTools/Concurrency/RP2040.h" - -AudioInfo info(44100, 2, 16); -SineWaveGenerator sineWave(32000); // subclass of SoundGenerator with max amplitude of 32000 -GeneratedSoundStream sound(sineWave); // Stream generated from sine wave -BufferRP2040 buffer(1024, 10); -QueueStream queue(buffer); -CsvOutput csv(Serial); -StreamCopy copierFill(queue, sound); // copies sound into i2s -StreamCopy copierConsume(csv, queue); // copies sound into i2s - - -void setup(){ - Serial.begin(115200); - while(!Serial); - AudioToolsLogger.begin(Serial, AudioToolsLogLevel::Info); - - queue.begin(); - sineWave.begin(info, N_B4); - csv.begin(info); -} - -void loop(){ - copierFill.copy(); -} - -void loop1(){ - copierConsume.copy(); -} \ No newline at end of file diff --git a/examples/tests/rp2040/mutex/mutex.ino b/examples/tests/rp2040/mutex/mutex.ino deleted file mode 100644 index ff9f7c816c..0000000000 --- a/examples/tests/rp2040/mutex/mutex.ino +++ /dev/null @@ -1,61 +0,0 @@ -#include "AudioTools.h" -#include "AudioTools/Concurrency/RP2040.h" - -SpinLock mutex; -NBuffer nbuffer(512, 10); -SynchronizedBuffer buffer(nbuffer, mutex); - -void setup() { - Serial.begin(115200); - while (!Serial); - Serial.println("starting..."); -} - -void loop() { - int16_t data[512]; - for (int j = 0; j < 512; j++) { - data[j] = j; - } - size_t result = buffer.writeArray(data, 512); - if(result != 512){ - // queue is full: give the reading queue some chance to empty - delay(5); - } -} - -void loop1() { - static uint64_t start = millis(); - static size_t total_bytes = 0; - static size_t errors = 0; - static int16_t data[512]; - - // read data - size_t result = buffer.readArray(data, 512); - if(result == 0){ - // reading queue is empty: give some time to fill - delay(5); - return; - } - - // check data - for (int j = 0; j < 512; j++) { - if (data[j] != j) errors++; - } - // calculate bytes per second - total_bytes += sizeof(int16_t) * 512; - if (total_bytes >= 1024000) { - uint64_t duration = millis() - start; - float mbps = static_cast(total_bytes) / duration / 1000.0; - - // print result - Serial.print("Mbytes per second: "); - Serial.print(mbps); - Serial.print(" with "); - Serial.print(errors); - Serial.println(" errors"); - - start = millis(); - errors = 0; - total_bytes = 0; - } -} \ No newline at end of file diff --git a/examples/tests/rp2040/mutex2040/mutex2040.ino b/examples/tests/rp2040/mutex2040/mutex2040.ino deleted file mode 100644 index 38f7bab2b7..0000000000 --- a/examples/tests/rp2040/mutex2040/mutex2040.ino +++ /dev/null @@ -1,60 +0,0 @@ -#include "AudioTools.h" -#include "AudioTools/Concurrency/RP2040.h" - -MutexRP2040 mutex; -NBuffer nbuffer(512, 10); -SynchronizedBuffer buffer(nbuffer, mutex); - -void setup() { - Serial.begin(115200); - while (!Serial); - Serial.println("starting..."); -} - -void loop() { - int16_t data[512]; - for (int j = 0; j < 512; j++) { - data[j] = j; - } - size_t result = buffer.writeArray(data, 512); - if(result != 512){ - Serial.print("write failed: "); - Serial.println(result); - } -} - -void loop1() { - static uint64_t start = millis(); - static size_t total_bytes = 0; - static size_t errors = 0; - static int16_t data[512]; - - // read data - size_t result = buffer.readArray(data, 512); - if(result != 512){ - Serial.print("read failed: "); - Serial.println(result); - } - - // check data - for (int j = 0; j < 512; j++) { - if (data[j] != j) errors++; - } - // calculate bytes per second - total_bytes += sizeof(int16_t) * 512; - if (total_bytes >= 1024000) { - uint64_t duration = millis() - start; - float mbps = static_cast(total_bytes) / duration / 1000.0; - - // print result - Serial.print("Mbytes per second: "); - Serial.print(mbps); - Serial.print(" with "); - Serial.print(errors); - Serial.println(" errors"); - - start = millis(); - errors = 0; - total_bytes = 0; - } -} diff --git a/examples/tests/player/test-index-sd/test-index-sd.ino b/examples/tests/sd/test-index-sd/test-index-sd.ino similarity index 75% rename from examples/tests/player/test-index-sd/test-index-sd.ino rename to examples/tests/sd/test-index-sd/test-index-sd.ino index 894b1d5681..25d79f45f5 100644 --- a/examples/tests/player/test-index-sd/test-index-sd.ino +++ b/examples/tests/sd/test-index-sd/test-index-sd.ino @@ -1,15 +1,15 @@ #include "AudioTools.h" -#include "AudioTools/AudioLibs/AudioBoardStream.h" +#include "AudioLibs/AudioKit.h" #include "FS.h" #include "SD.h" #include "SPI.h" -#include "AudioTools/Disk/SDIndex.h" +#include "AudioLibs/SDIndex.h" void setup() { Serial.begin(115200); while(!Serial); - AudioToolsLogger.begin(Serial, AudioToolsLogLevel::Warning); + AudioLogger::instance().begin(Serial, AudioLogger::Warning); SPI.begin(PIN_AUDIO_KIT_SD_CARD_CLK, PIN_AUDIO_KIT_SD_CARD_MISO, PIN_AUDIO_KIT_SD_CARD_MOSI, PIN_AUDIO_KIT_SD_CARD_CS); while (!SD.begin(PIN_AUDIO_KIT_SD_CARD_CS)) { diff --git a/examples/tests/player/test-index-sdfat/test-index-sdfat.ino b/examples/tests/sd/test-index-sdfat/test-index-sdfat.ino similarity index 85% rename from examples/tests/player/test-index-sdfat/test-index-sdfat.ino rename to examples/tests/sd/test-index-sdfat/test-index-sdfat.ino index d3d0a1384c..263f42fb35 100644 --- a/examples/tests/player/test-index-sdfat/test-index-sdfat.ino +++ b/examples/tests/sd/test-index-sdfat/test-index-sdfat.ino @@ -1,10 +1,10 @@ #include "AudioTools.h" -#include "AudioTools/AudioLibs/AudioBoardStream.h" +#include "AudioLibs/AudioKit.h" #define USE_SDFAT #include #include -#include "AudioTools/Disk/SDIndex.h" +#include "AudioLibs/SDIndex.h" #define SD_FAT_TYPE 3 #if SD_FAT_TYPE == 0 @@ -28,7 +28,7 @@ AudioFs sd; void setup() { Serial.begin(115200); while(!Serial); - AudioToolsLogger.begin(Serial, AudioToolsLogLevel::Warning); + AudioLogger::instance().begin(Serial, AudioLogger::Warning); SdSpiConfig cfg(PIN_AUDIO_KIT_SD_CARD_CS, DEDICATED_SPI, SD_SCK_MHZ(2)); SPI.begin(PIN_AUDIO_KIT_SD_CARD_CLK, PIN_AUDIO_KIT_SD_CARD_MISO, PIN_AUDIO_KIT_SD_CARD_MOSI, PIN_AUDIO_KIT_SD_CARD_CS); diff --git a/examples/tests/player/test-index-sdmmc/test-index-sdmmc.ino b/examples/tests/sd/test-index-sdmmc/test-index-sdmmc.ino similarity index 85% rename from examples/tests/player/test-index-sdmmc/test-index-sdmmc.ino rename to examples/tests/sd/test-index-sdmmc/test-index-sdmmc.ino index d3d0a1384c..263f42fb35 100644 --- a/examples/tests/player/test-index-sdmmc/test-index-sdmmc.ino +++ b/examples/tests/sd/test-index-sdmmc/test-index-sdmmc.ino @@ -1,10 +1,10 @@ #include "AudioTools.h" -#include "AudioTools/AudioLibs/AudioBoardStream.h" +#include "AudioLibs/AudioKit.h" #define USE_SDFAT #include #include -#include "AudioTools/Disk/SDIndex.h" +#include "AudioLibs/SDIndex.h" #define SD_FAT_TYPE 3 #if SD_FAT_TYPE == 0 @@ -28,7 +28,7 @@ AudioFs sd; void setup() { Serial.begin(115200); while(!Serial); - AudioToolsLogger.begin(Serial, AudioToolsLogLevel::Warning); + AudioLogger::instance().begin(Serial, AudioLogger::Warning); SdSpiConfig cfg(PIN_AUDIO_KIT_SD_CARD_CS, DEDICATED_SPI, SD_SCK_MHZ(2)); SPI.begin(PIN_AUDIO_KIT_SD_CARD_CLK, PIN_AUDIO_KIT_SD_CARD_MISO, PIN_AUDIO_KIT_SD_CARD_MOSI, PIN_AUDIO_KIT_SD_CARD_CS); diff --git a/examples/tests/etc/test-ads1015/test-ads1015.ino b/examples/tests/test-ads1015/test-ads1015.ino similarity index 92% rename from examples/tests/etc/test-ads1015/test-ads1015.ino rename to examples/tests/test-ads1015/test-ads1015.ino index 0b877bbc46..442075ece6 100644 --- a/examples/tests/etc/test-ads1015/test-ads1015.ino +++ b/examples/tests/test-ads1015/test-ads1015.ino @@ -1,6 +1,6 @@ #include -#include "ADS1X15.h" // https://github.com/pschatzmann/ADS1X15.git +#include "ADS1X15.h" ADS1115 ads1015(0x48); // ads1015 device diff --git a/examples/tests/test-bitvector/test-bitvector.ino b/examples/tests/test-bitvector/test-bitvector.ino new file mode 100644 index 0000000000..3e80db5303 --- /dev/null +++ b/examples/tests/test-bitvector/test-bitvector.ino @@ -0,0 +1,36 @@ + +#include "AudioTools.h" +#include "AudioBasic/Collections.h" + +BitVector bv; + +void printBitVector(){ + for (int j=0;j firf(coeff); unsigned long start = millis(); for (int j = 0; j < 44100; j++) { - auto result = firf.process(32767); + int16_t result = firf.process(32767); if (j < max_array) { result_array[j] = result; } @@ -92,7 +92,7 @@ void processDouble() { FIR fird(coefd); unsigned long start = millis(); for (int j = 0; j < 44100; j++) { - auto result = fird.process(32767); + int16_t result = fird.process(32767); if (j < max_array) { result_array[j] = result; } @@ -106,7 +106,7 @@ void processInt16() { FIR fir16(coef16, 32767); unsigned long start = millis(); for (int j = 0; j < 44100; j++) { - auto result = fir16.process(32767); + int16_t result = fir16.process(32767); if (j < max_array) { result_array[j] = result; } @@ -120,7 +120,7 @@ void processInt32() { FIR fir32(coef32, 32767); unsigned long start = millis(); for (int j = 0; j < 44100; j++) { - auto result = fir32.process(32767); + int16_t result = fir32.process(32767); if (j < max_array) { result_array[j] = result; } @@ -134,7 +134,7 @@ void processInt64() { FIR fir64(coef64, 32767); unsigned long start = millis(); for (int j = 0; j < 44100; j++) { - auto result = fir64.process(32767); + int16_t result = fir64.process(32767); if (j < max_array) { result_array[j] = result; } @@ -154,8 +154,8 @@ void setup() { listResult(); processDouble(); listResult(); - // processInt16(); - // listResult(); + processInt16(); + listResult(); processInt32(); listResult(); processInt64(); diff --git a/examples/tests/test-list/test-list.ino b/examples/tests/test-list/test-list.ino new file mode 100644 index 0000000000..f79a2b3cf7 --- /dev/null +++ b/examples/tests/test-list/test-list.ino @@ -0,0 +1,168 @@ +#include "AudioTools.h" +#include "AudioBasic/Collections.h" + +void testLoop() { + Serial.println(); + Serial.println("testLoop"); + List list({"a","b","c"}); + assert(list.size()==3); + for (int j=0;j list; + assert(list.size()==0); + assert(list.empty()); + list.push_back("a"); + assert(list.size()==1); + assert(!list.empty()); + + for (auto it=list.begin();it!=list.end();it++){ + Serial.print(*it); + } + + list.push_back("b"); + assert(list.size()==2); + assert(!list.empty()); + Serial.println(); + for (auto it=list.begin();it!=list.end();it++){ + Serial.print(*it); + } + + list.push_back("c"); + assert(list.size()==3); + assert(!list.empty()); + Serial.println(); + for (auto it=list.begin();it!=list.end();it++){ + Serial.print(*it); + } + Serial.println(); + for (auto it=list.rbegin();it!=list.rend();it--){ + Serial.print(*it); + } +} + +void testPushFront() { + Serial.println(); + Serial.println("testPushFront"); + List list; + assert(list.size()==0); + assert(list.empty()); + list.push_front("a"); + assert(list.size()==1); + assert(!list.empty()); + for (auto it=list.begin();it!=list.end();it++){ + Serial.print(*it); + } + + list.push_front("b"); + assert(list.size()==2); + assert(!list.empty()); + Serial.println(); + for (auto it=list.begin();it!=list.end();it++){ + Serial.print(*it); + } + + list.push_front("c"); + assert(list.size()==3); + assert(!list.empty()); + Serial.println(); + for (auto it=list.begin();it!=list.end();it++){ + Serial.print(*it); + } + Serial.println(); + for (auto it=list.rbegin();it!=list.rend();it--){ + Serial.print(*it); + } +} + +void testPopFront(){ + Serial.println(); + Serial.println("testPopFront"); + List list({"a","b","c"}); + assert(list.size()==3); + const char* var=""; + for (int j=0;j<3;j++){ + list.pop_front(var); + Serial.println(var); + assert(list.size()==2-j); + } +} + +void testPopBack(){ + Serial.println(); + Serial.println("testPopBack"); + List list({"a","b","c"}); + assert(list.size()==3); + const char* var=""; + for (int j=0;j<3;j++){ + list.pop_back(var); + Serial.println(var); + assert(list.size()==2-j); + } +} + +void testInsert(){ + Serial.println(); + Serial.println("testInsert"); + List list({"1","2","3"}); + + auto it=list.begin(); + list.insert(it,"0"); + list.insert(it+3,"4"); + + for (auto it=list.begin();it!=list.end();it++){ + Serial.print(*it); + } + Serial.println(); + for (auto it=list.rbegin();it!=list.rend();it--){ + Serial.print(*it); + } + + assert(list.size()==5); +} + +void testErase(){ + Serial.println(); + Serial.println("testErase"); + List list({"1","2","3"}); + + auto it = list.begin()+2; + assert(list.erase(it)); + + for (auto it=list.begin();it!=list.end();it++){ + Serial.print(*it); + } + assert(list.size()==2); + + list.clear(); + assert(list.empty()); + +} + +void setup(){ + Serial.begin(115200); + testLoop(); + testPushBack(); + testPushFront(); + testPopFront(); + testPopBack(); + testInsert(); + testErase(); +} + +void loop(){ + +} \ No newline at end of file diff --git a/examples/tests/codecs/test-memory-helix/test-memory-helix.ino b/examples/tests/test-memory-helix/test-memory-helix.ino similarity index 90% rename from examples/tests/codecs/test-memory-helix/test-memory-helix.ino rename to examples/tests/test-memory-helix/test-memory-helix.ino index c30a6ae3d9..21c5f0daa4 100644 --- a/examples/tests/codecs/test-memory-helix/test-memory-helix.ino +++ b/examples/tests/test-memory-helix/test-memory-helix.ino @@ -1,6 +1,6 @@ #include "AudioTools.h" -#include "AudioTools/AudioCodecs/CodecMP3Helix.h" +#include "AudioCodecs/CodecMP3Helix.h" /** * @brief Sketch to test the memory usage with libhelix with an ESP32 @@ -18,13 +18,14 @@ StreamCopy copier(dec, url); // copy url to decoder void setup(){ Serial.begin(115200); - AudioToolsLogger.begin(Serial, AudioToolsLogLevel::Info); + AudioLogger::instance().begin(Serial, AudioLogger::Info); // setup i2s auto config = i2s.defaultConfig(TX_MODE); i2s.begin(config); // setup I2S based on sampling rate provided by decoder + dec.setNotifyAudioChange(i2s); dec.begin(); // set initial volume diff --git a/examples/tests/etc/test-mulit-compilation-units/incl1.cpp b/examples/tests/test-mulit-compilation-units/incl1.cpp similarity index 100% rename from examples/tests/etc/test-mulit-compilation-units/incl1.cpp rename to examples/tests/test-mulit-compilation-units/incl1.cpp diff --git a/examples/tests/etc/test-mulit-compilation-units/incl2.cpp b/examples/tests/test-mulit-compilation-units/incl2.cpp similarity index 100% rename from examples/tests/etc/test-mulit-compilation-units/incl2.cpp rename to examples/tests/test-mulit-compilation-units/incl2.cpp diff --git a/examples/tests/etc/test-mulit-compilation-units/test-mulit-compilation-units.ino b/examples/tests/test-mulit-compilation-units/test-mulit-compilation-units.ino similarity index 100% rename from examples/tests/etc/test-mulit-compilation-units/test-mulit-compilation-units.ino rename to examples/tests/test-mulit-compilation-units/test-mulit-compilation-units.ino diff --git a/examples/tests/etc/test-pins/test-pins.ino b/examples/tests/test-pins/test-pins.ino similarity index 100% rename from examples/tests/etc/test-pins/test-pins.ino rename to examples/tests/test-pins/test-pins.ino diff --git a/examples/tests/player/test-player/test-player.ino b/examples/tests/test-player/test-player.ino similarity index 92% rename from examples/tests/player/test-player/test-player.ino rename to examples/tests/test-player/test-player.ino index 68f1c19422..4b29ea9524 100644 --- a/examples/tests/player/test-player/test-player.ino +++ b/examples/tests/test-player/test-player.ino @@ -12,8 +12,7 @@ // install https://github.com/greiman/SdFat.git #include "AudioTools.h" -#include "AudioTools/Disk/AudioSourceSDFAT.h" -#include "AudioTools/Disk/AudioSourceURL.h" +#include "AudioLibs/AudioSourceSDFAT.h" const char *urls[] = { @@ -74,7 +73,7 @@ void testSDNext() { void setup() { Serial.begin(115200); - AudioToolsLogger.begin(Serial, AudioToolsLogLevel::Error); + AudioLogger::instance().begin(Serial, AudioLogger::Error); //testUrl(); testSD(); testSDNext(); diff --git a/examples/tests/conversion/test-resample-in/test-resample-in.ino b/examples/tests/test-resample-in/test-resample-in.ino similarity index 84% rename from examples/tests/conversion/test-resample-in/test-resample-in.ino rename to examples/tests/test-resample-in/test-resample-in.ino index 2ccb0a5da6..1094407771 100644 --- a/examples/tests/conversion/test-resample-in/test-resample-in.ino +++ b/examples/tests/test-resample-in/test-resample-in.ino @@ -1,17 +1,17 @@ #include "AudioTools.h" -AudioInfo info(44100,2,16); SineWaveGenerator sineWave(32000); // subclass of SoundGenerator with max amplitude of 32000 GeneratedSoundStream sound(sineWave); // Stream generated from sine wave ResampleStream resample(sound); CsvOutput out(Serial); StreamCopy copier(out, resample); // copies sound to out +AudioInfo info(44100,2,16); // Arduino Setup void setup(void) { // Open Serial Serial.begin(115200); - AudioToolsLogger.begin(Serial, AudioToolsLogLevel::Warning); + AudioLogger::instance().begin(Serial, AudioLogger::Warning); // define resample auto rcfg = resample.defaultConfig(); @@ -20,7 +20,9 @@ void setup(void) { resample.begin(rcfg); // Define CSV Output - out.begin(info); + auto config = out.defaultConfig(); + config.copyFrom(info); + out.begin(config); // Setup sine wave sineWave.begin(info, N_B4); diff --git a/examples/tests/conversion/test-resample-out/test-resample-out.ino b/examples/tests/test-resample-out/test-resample-out.ino similarity index 93% rename from examples/tests/conversion/test-resample-out/test-resample-out.ino rename to examples/tests/test-resample-out/test-resample-out.ino index bb3013a799..65d2c1d977 100644 --- a/examples/tests/conversion/test-resample-out/test-resample-out.ino +++ b/examples/tests/test-resample-out/test-resample-out.ino @@ -12,7 +12,7 @@ AudioInfo info(44100,2,16); void setup(void) { // Open Serial Serial.begin(115200); - AudioToolsLogger.begin(Serial, AudioToolsLogLevel::Warning); + AudioLogger::instance().begin(Serial, AudioLogger::Warning); // define resample auto rcfg = resample.defaultConfig(); diff --git a/examples/tests/timer/test-timer/test-timer.ino b/examples/tests/timer/test-timer/test-timer.ino index f536a8efd1..634a7cb1b8 100644 --- a/examples/tests/timer/test-timer/test-timer.ino +++ b/examples/tests/timer/test-timer/test-timer.ino @@ -1,7 +1,7 @@ #include "AudioTools.h" uint32_t sampling_rate = 44100; -uint32_t delay_us = AudioTime::toTimeUs(sampling_rate); +uint32_t delay_us = AudioUtils::toTimeUs(sampling_rate); uint32_t count; TimerAlarmRepeating timer; @@ -11,12 +11,9 @@ void callback(void*ptr){ void setup() { Serial.begin(115200); - AudioToolsLogger.begin(Serial, AudioToolsLogLevel::Info); + AudioLogger::instance().begin(Serial, AudioLogger::Info); Serial.print("Delay us: "); Serial.println(delay_us); - // select timer function - timer.setTimerFunction(DirectTimerCallback); //DirectTimerCallback,TimerCallbackInThread, SimpleThreadLoop - } void loop() { @@ -24,7 +21,6 @@ void loop() { timer.begin(callback, delay_us, US); delay(10000); timer.end(); - char msg[80]; - sprintf(msg, "Sampling Rate %d vs eff: %d", sampling_rate, count / 10); - Serial.println(msg); + Serial.print("Sampling Rate 44100 vs eff: "); + Serial.println(count / 10); } \ No newline at end of file diff --git a/examples/tests/timer/test-timercb_rx/test-timercb_rx.ino b/examples/tests/timer/test-timercb_rx/test-timercb_rx.ino index d0cb3d63a6..fa97caee9e 100644 --- a/examples/tests/timer/test-timercb_rx/test-timercb_rx.ino +++ b/examples/tests/timer/test-timercb_rx/test-timercb_rx.ino @@ -1,6 +1,7 @@ #include "AudioTools.h" + TimerCallbackAudioStream timerStream; uint16_t IRAM_ATTR callback(uint8_t *data, uint16_t len){ diff --git a/examples/tests/timer/test-timercb_tx/test-timercb_tx.ino b/examples/tests/timer/test-timercb_tx/test-timercb_tx.ino index a9b11bd308..f8bb7e89f3 100644 --- a/examples/tests/timer/test-timercb_tx/test-timercb_tx.ino +++ b/examples/tests/timer/test-timercb_tx/test-timercb_tx.ino @@ -1,6 +1,8 @@ #include "AudioTools.h" + + TimerCallbackAudioStream timerStream; // Reads the data in the callback driven by a timer diff --git a/examples/tests/timer/test-timercbx_rx/test-timercbx_rx.ino b/examples/tests/timer/test-timercbx_rx/test-timercbx_rx.ino index e2a367b8e6..3bcca98c05 100644 --- a/examples/tests/timer/test-timercbx_rx/test-timercbx_rx.ino +++ b/examples/tests/timer/test-timercbx_rx/test-timercbx_rx.ino @@ -1,6 +1,9 @@ #include "AudioTools.h" + + + TimerCallbackAudioStream timerStream; uint16_t IRAM_ATTR callback(uint8_t *data, uint16_t len){ @@ -22,7 +25,7 @@ void setup(){ cfg.channels = 1; cfg.sample_rate = 5000; cfg.bits_per_sample = 16; - cfg.timer_function = TimerCallbackInThread; // Use separate Task + cfg.secure_timer = true; // Use separate Task cfg.callback = callback; timerStream.begin(cfg); diff --git a/examples/tests/timer/test-timercbx_tx/test-timercbx_tx.ino b/examples/tests/timer/test-timercbx_tx/test-timercbx_tx.ino index da32348d39..483b40d719 100644 --- a/examples/tests/timer/test-timercbx_tx/test-timercbx_tx.ino +++ b/examples/tests/timer/test-timercbx_tx/test-timercbx_tx.ino @@ -1,6 +1,8 @@ #include "AudioTools.h" + + TimerCallbackAudioStream timerStream; // Reads the data in the callback driven by a timer @@ -21,7 +23,7 @@ void setup(){ cfg.channels = 1; cfg.sample_rate = 5000; cfg.bits_per_sample = 16; - cfg.timer_function = TimerCallbackInThread; // Use separate Task + cfg.secure_timer = true; // Use separate Task cfg.callback = callback; timerStream.begin(cfg); } diff --git a/jupyter/Jupyter.ipynb b/jupyter/Jupyter.ipynb index c1802f43e6..4db643447e 100644 --- a/jupyter/Jupyter.ipynb +++ b/jupyter/Jupyter.ipynb @@ -21,7 +21,11 @@ "cell_type": "code", "execution_count": 1, "id": "ad51983a-9850-4087-910b-5aaf804b2a1d", - "metadata": {}, + "metadata": { + "vscode": { + "languageId": "c++" + } + }, "outputs": [], "source": [ "#pragma cling add_include_path(\"/home/pschatzmann/Arduino/libraries/arduino-audio-tools/src\")" @@ -39,11 +43,15 @@ "cell_type": "code", "execution_count": 2, "id": "2718c3e1-a7f2-4ea2-a649-2e045165f418", - "metadata": {}, + "metadata": { + "vscode": { + "languageId": "c++" + } + }, "outputs": [], "source": [ "#include \"AudioTools.h\"\n", - "#include \"AudioTools/AudioLibs/Jupyter.h\"" + "#include \"AudioLibs/Jupyter.h\"" ] }, { @@ -59,7 +67,11 @@ "cell_type": "code", "execution_count": 3, "id": "42fb7892-a018-4db1-98b1-0ed52cc2d1e3", - "metadata": {}, + "metadata": { + "vscode": { + "languageId": "c++" + } + }, "outputs": [], "source": [ "int frequency = 800;\n", @@ -81,7 +93,11 @@ "cell_type": "code", "execution_count": 4, "id": "f589d58e-1c76-498e-a25d-c9f23c3c55fe", - "metadata": {}, + "metadata": { + "vscode": { + "languageId": "c++" + } + }, "outputs": [], "source": [ "JupyterAudio audio(\"test1.wav\", sound, 600, 1024);" @@ -99,7 +115,11 @@ "cell_type": "code", "execution_count": 5, "id": "f8d797e4-66e5-4f38-ac6d-35b52ada5c90", - "metadata": {}, + "metadata": { + "vscode": { + "languageId": "c++" + } + }, "outputs": [ { "data": { @@ -126,9 +146,13 @@ }, { "cell_type": "code", - "execution_count": 7, + "execution_count": 6, "id": "ffb5ae33-399f-4db2-9c55-2141a52da62d", - "metadata": {}, + "metadata": { + "vscode": { + "languageId": "c++" + } + }, "outputs": [ { "data": { @@ -136,7 +160,7 @@ "
" ] }, - "execution_count": 7, + "execution_count": 6, "metadata": {}, "output_type": "execute_result" } @@ -157,9 +181,13 @@ }, { "cell_type": "code", - "execution_count": 8, + "execution_count": 7, "id": "768e9948-acf8-486f-8227-aaa7cd23dad2", - "metadata": {}, + "metadata": { + "vscode": { + "languageId": "c++" + } + }, "outputs": [ { "data": { @@ -167,7 +195,7 @@ "614444" ] }, - "execution_count": 8, + "execution_count": 7, "metadata": {}, "output_type": "execute_result" } @@ -179,9 +207,13 @@ }, { "cell_type": "code", - "execution_count": 9, + "execution_count": 8, "id": "415b514d-b2ff-44dd-8f5f-05becc5f206c", - "metadata": {}, + "metadata": { + "vscode": { + "languageId": "c++" + } + }, "outputs": [], "source": [ "file.close();\n", @@ -200,9 +232,13 @@ }, { "cell_type": "code", - "execution_count": 10, + "execution_count": 9, "id": "9996ea70-d774-4c79-b7d0-945aa9b85037", - "metadata": {}, + "metadata": { + "vscode": { + "languageId": "c++" + } + }, "outputs": [ { "name": "stdout", @@ -227,6 +263,55 @@ "}" ] }, + { + "cell_type": "code", + "execution_count": 10, + "id": "c684b4ec-f56f-481a-923b-a834d5d8f979", + "metadata": {}, + "outputs": [], + "source": [ + "BitVector bits(160);" + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "id": "8de172e1-e341-4b01-a843-696e6b9e4a8a", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + ] + } + ], + "source": [ + "cout << bits;" + ] + }, + { + "cell_type": "code", + "execution_count": 13, + "id": "a10e5c44-5d40-40be-bb03-42536a553f0f", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "0" + ] + }, + "execution_count": 13, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "bits.toInt(1)" + ] + }, { "cell_type": "code", "execution_count": null, diff --git a/library.properties b/library.properties index 33958b7a6f..f5b68113ce 100644 --- a/library.properties +++ b/library.properties @@ -1,5 +1,5 @@ name=audio-tools -version=1.1.1 +version=0.9.6 author=Phil Schatzmann maintainer=Phil Schatzmann sentence=Some useful audio processing classes @@ -7,3 +7,4 @@ paragraph=Arduino Audio Tools (Music Player, Music Recorder supporting I2S, Micr category=Signal Input/Output url=https://github.com/pschatzmann/arduino-audio-tools architectures=* +depends=I2S diff --git a/src/AudioTools/CoreAudio/AudioAnalog/AnalogAudioStream.h b/src/AudioAnalog/AnalogAudio.h similarity index 60% rename from src/AudioTools/CoreAudio/AudioAnalog/AnalogAudioStream.h rename to src/AudioAnalog/AnalogAudio.h index 681510546e..5df794b41b 100644 --- a/src/AudioTools/CoreAudio/AudioAnalog/AnalogAudioStream.h +++ b/src/AudioAnalog/AnalogAudio.h @@ -1,13 +1,10 @@ #pragma once -#include "AudioToolsConfig.h" - -// Support AnalogAudioStream -#if defined(USE_ANALOG) -# include "AnalogDriverBase.h" -# include "AnalogDriverESP32V1.h" -# include "AnalogDriverESP32.h" -# include "AnalogDriverMBED.h" -# include "AnalogDriverArduino.h" +#include "AudioConfig.h" +#if defined(USE_I2S_ANALOG) +#include "AudioAnalog/AnalogAudioBase.h" +#include "AudioAnalog/AnalogAudioESP32.h" +#include "AudioAnalog/AnalogAudioArduino.h" +#include "AudioAnalog/AnalogAudioMBED.h" namespace audio_tools { @@ -24,11 +21,6 @@ class AnalogAudioStream : public AudioStream { /// Default constructor AnalogAudioStream()=default; - /// Constructor to define a custom driver - AnalogAudioStream(AnalogDriverBase &driver){ - p_analog = &driver; - } - /// Destructor virtual ~AnalogAudioStream() { end(); @@ -64,53 +56,42 @@ class AnalogAudioStream : public AudioStream { /// starts the DAC bool begin(AnalogConfig cfg) { TRACEI(); - return p_analog->begin(cfg); + return driver.begin(cfg); } - /// stops the I2S and unistalls the analog + /// stops the I2S and unistalls the driver void end() override { TRACEI(); - p_analog->end(); + driver.end(); } + // /// Overides the sample rate and uses the max value which is around ~13MHz. Call this methd after begin(); + // void setMaxSampleRate() { + // driver.setMaxSampleRate(); + // } + AnalogConfig &config() { return adc_config; } /// ESP32 only: writes the data to the I2S interface - size_t write(const uint8_t *data, size_t len) override { + size_t write(const uint8_t *src, size_t size_bytes) override { TRACED(); - return p_analog->write(data, len); + return driver.write(src, size_bytes); } - size_t readBytes(uint8_t *data, size_t len) override { - return p_analog->readBytes(data, len); + size_t readBytes(uint8_t *dest, size_t size_bytes) override { + return driver.readBytes(dest, size_bytes); } int available() override { - return p_analog->available(); - } - - int availableForWrite() override { - return p_analog->availableForWrite(); - } - - /// Provides access to the driver - AnalogDriverBase* driver() { - return p_analog; + return driver.available(); } - protected: - AnalogDriver default_analog; - AnalogDriverBase* p_analog = &default_analog; + AnalogDriver driver; AnalogConfig adc_config; }; } -#endif - -// Support AnalogAudioArduino -#if defined(USE_TIMER) -# include "AnalogAudioArduino.h" -#endif +#endif \ No newline at end of file diff --git a/src/AudioAnalog/AnalogAudioArduino.h b/src/AudioAnalog/AnalogAudioArduino.h new file mode 100644 index 0000000000..6b52135c64 --- /dev/null +++ b/src/AudioAnalog/AnalogAudioArduino.h @@ -0,0 +1,103 @@ +#pragma once + +#include "AudioConfig.h" +#ifdef USE_ADC_ARDUINO +#include "AudioAnalog/AnalogAudioBase.h" +#include "AudioTimer/AudioTimer.h" +#include "AudioTools/AudioStreams.h" +#include "AudioTools/AudioTypes.h" +#include "AudioTools/Buffers.h" + +namespace audio_tools { + +/** + * @brief Please use the AnalogAudioStream: Reading Analog Data using a timer and the Arduino analogRead() method + * @ingroup platform + * @author Phil Schatzmann + * @copyright GPLv3 + * + */ +class AnalogDriverArduino : public AnalogDriverBase { + public: + AnalogDriverArduino() = default; + + bool begin(AnalogConfig cfg) { + TRACED(); + config = cfg; + if (buffer == nullptr) { + // allocate buffer_count + buffer = new NBuffer(cfg.buffer_size, cfg.buffer_count); + if (buffer==nullptr){ + LOGE("Not enough memory for buffer"); + return false; + } + // setup pins + setupPins(); + } else { + timer.end(); + } + + // (re)start timer + uint32_t time = AudioTime::toTimeUs(config.sample_rate); + LOGI("sample_rate: %d", cfg.sample_rate); + LOGI("time us: %lu", time); + timer.setCallbackParameter(this); + return timer.begin(callback, time, TimeUnit::US); + } + + int available() { return buffer==nullptr ? 0 : buffer->available()*2; }; + + /// Provides the sampled audio data + size_t readBytes(uint8_t *values, size_t len) { + if (buffer==nullptr) return 0; + int samples = len / 2; + return buffer->readArray((int16_t *)values, samples) * 2; + } + + protected: + AnalogConfig config; + TimerAlarmRepeating timer; + BaseBuffer *buffer = nullptr; + int16_t avg_value=0; + + /// Sample data and write to buffer + static void callback(void *arg) { + int16_t value = 0; + AnalogDriverArduino *self = (AnalogDriverArduino *)arg; + if (self->buffer != nullptr) { + int channels = self->config.channels; + for (int j = 0; j < channels; j++) { + // provides value in range 0…4095 + value = analogRead(self->config.start_pin + j); + value = (value - self->avg_value) * 16; + self->buffer->write(value); + } + } + } + + /// pinmode input for defined analog pins + void setupPins() { + TRACED(); + LOGI("start_pin: %d", config.start_pin); + // setup pins for read + for (int j = 0; j < config.channels; j++) { + int pin = config.start_pin + j; + pinMode(pin, INPUT); + LOGD("pinMode(%d, INPUT)",pin); + } + // calculate the avarage value to center the signal + float total=0; + for (int j=0;j<200;j++){ + total += analogRead(config.start_pin); + } + avg_value = total / 200.0; + LOGI("Avg Signal was %d", avg_value); + + } +}; + +using AnalogDriver = AnalogDriverArduino; + +} // namespace audio_tools + +#endif diff --git a/src/AudioAnalog/AnalogAudioBase.h b/src/AudioAnalog/AnalogAudioBase.h new file mode 100644 index 0000000000..5b639270f7 --- /dev/null +++ b/src/AudioAnalog/AnalogAudioBase.h @@ -0,0 +1,166 @@ +#pragma once +#include "AudioConfig.h" +#if defined(USE_I2S_ANALOG) +#if defined(ESP32) +# include "driver/i2s.h" +# include "driver/adc.h" +# include "soc/dac_channel.h" +# include "soc/adc_channel.h" +#endif +namespace audio_tools { + +/** + * @brief ESP32 specific configuration for i2s input via adc. The default input pin is GPIO34. We always use int16_t values. The default + * output pins are GPIO25 and GPIO26! + * + * @author Phil Schatzmann + * @copyright GPLv3 + */ +class AnalogConfig : public AudioInfo { + public: + int buffer_count = I2S_BUFFER_COUNT; + int buffer_size = I2S_BUFFER_SIZE; + RxTxMode rx_tx_mode; + +#if defined(ESP32) && defined(USE_I2S_ANALOG) + // allow ADC to access the protected methods + friend class AnalogDriverESP32; + + // public config parameters + int port_no = I2S_NUM_0; // Analog input and output only supports 0! + bool use_apll = false; + bool auto_clear = I2S_AUTO_CLEAR; + bool uninstall_driver_on_end = true; + int mode_internal; + + AnalogConfig() { + sample_rate = 44100; + bits_per_sample = 16; + channels = 2; + this->rx_tx_mode = TX_MODE; + // enable both channels + mode_internal = (I2S_MODE_MASTER | I2S_MODE_TX | I2S_MODE_DAC_BUILT_IN); + } + + /// Default constructor + AnalogConfig(RxTxMode rxtxMode) { + sample_rate = 44100; + bits_per_sample = 16; + channels = 2; + rx_tx_mode = rxtxMode; + if (rx_tx_mode == RX_MODE) { + mode_internal = (I2S_MODE_MASTER | I2S_MODE_RX | I2S_MODE_ADC_BUILT_IN); + setInputPin1(PIN_ADC1); + auto_clear = false; +// setInputPin2(PIN_ADC2); + LOGI("I2S_MODE_ADC_BUILT_IN"); + } else { + mode_internal = (I2S_MODE_MASTER | I2S_MODE_TX | I2S_MODE_DAC_BUILT_IN); + LOGI("I2S_MODE_DAC_BUILT_IN"); + } + } + + /// Copy constructor + AnalogConfig(const AnalogConfig &cfg) = default; + + /// Defines an alternative input pin (for the left channel) + void setInputPin1(int pin=PIN_ADC1){ + setInputPin1(pin,0); + } + + void logInfo() { + AudioInfo::logInfo(); + if (rx_tx_mode == TX_MODE){ + LOGI("analog left output pin: %d", 25); + LOGI("analog right output pin: %d", 26); + } else { + LOGI("input pin1: %d", adc_pin[0]); + if (channels==2){ + LOGI("input pin2: %d", adc_pin[1]); + } + } + } + + protected: + // input values + int adc_pin[2]; + adc_unit_t adc_unit[2]; + adc1_channel_t adc_channel[2]; + + /// Defines the current ADC pin. The following GPIO pins are supported: GPIO32-GPIO39 + void setInputPin1(int gpio, int channelIdx){ + this->adc_pin[channelIdx] = gpio; + switch(gpio){ + case 32: + adc_unit[channelIdx] = ADC_UNIT_1; + adc_channel[channelIdx] = ADC1_GPIO32_CHANNEL; + break; + case 33: + adc_unit[channelIdx] = ADC_UNIT_1; + adc_channel[channelIdx] = ADC1_GPIO33_CHANNEL; + break; + case 34: + adc_unit[channelIdx] = ADC_UNIT_1; + adc_channel[channelIdx] = ADC1_GPIO34_CHANNEL; + break; + case 35: + adc_unit[channelIdx] = ADC_UNIT_1; + adc_channel[channelIdx] = ADC1_GPIO35_CHANNEL; + break; + case 36: + adc_unit[channelIdx] = ADC_UNIT_1; + adc_channel[channelIdx] = ADC1_GPIO36_CHANNEL; + break; + case 37: + adc_unit[channelIdx] = ADC_UNIT_1; + adc_channel[channelIdx] = ADC1_GPIO37_CHANNEL; + break; + case 38: + adc_unit[channelIdx] = ADC_UNIT_1; + adc_channel[channelIdx] = ADC1_GPIO38_CHANNEL; + break; + case 39: + adc_unit[channelIdx] = ADC_UNIT_1; + adc_channel[channelIdx] = ADC1_GPIO39_CHANNEL; + break; + + default: + LOGE( "%s - pin GPIO%d is not supported", __func__,gpio); + } + } +#else + AnalogConfig() { + sample_rate = 10000; + bits_per_sample = 16; + channels = 2; + buffer_size = ADC_BUFFER_SIZE; + buffer_count = ADC_BUFFERS; + rx_tx_mode = RX_MODE; + } + /// Default constructor + AnalogConfig(RxTxMode rxtxMode) : AnalogConfig() { + rx_tx_mode = rxtxMode; +#ifdef USE_ADC_ARDUINO + if (rxtxMode != RX_MODE) { + LOGE("Only RX_MODE supported"); + } +#endif + } + int start_pin = PIN_ADC_START; + +#endif + +}; + +class AnalogDriverBase { +public: + virtual bool begin(AnalogConfig cfg); + virtual void end(); +// virtual void setMaxSampleRate() {} + virtual size_t write(const uint8_t *src, size_t size_bytes) { return 0;} + virtual size_t readBytes(uint8_t *dest, size_t size_bytes); + virtual int available(); +}; + +} // ns +#endif diff --git a/src/AudioTools/CoreAudio/AudioAnalog/AnalogDriverESP32.h b/src/AudioAnalog/AnalogAudioESP32.h similarity index 79% rename from src/AudioTools/CoreAudio/AudioAnalog/AnalogDriverESP32.h rename to src/AudioAnalog/AnalogAudioESP32.h index 83d7bf1697..2d08f242c7 100644 --- a/src/AudioTools/CoreAudio/AudioAnalog/AnalogDriverESP32.h +++ b/src/AudioAnalog/AnalogAudioESP32.h @@ -1,14 +1,14 @@ #pragma once -#include "AudioToolsConfig.h" -#if defined(ESP32) && defined(USE_ANALOG) && ESP_IDF_VERSION < ESP_IDF_VERSION_VAL(5, 0 , 0) || defined(DOXYGEN) -#include "AudioTools/CoreAudio/AudioAnalog/AnalogDriverBase.h" +#include "AudioConfig.h" +#if defined(ESP32) && defined(USE_I2S_ANALOG) +#include "AudioAnalog/AnalogAudioBase.h" #include "driver/i2s.h" #include "driver/adc.h" #include "soc/dac_channel.h" #include "soc/adc_channel.h" #include "soc/rtc.h" -#include "AudioTools/CoreAudio/AudioStreams.h" +#include "AudioTools/AudioStreams.h" namespace audio_tools { @@ -35,7 +35,8 @@ static inline uint16_t convert8DAC(int64_t value, int value_bits_per_sample){ class AnalogDriverESP32 : public AnalogDriverBase { public: /// Default constructor - AnalogDriverESP32() = default; + AnalogDriverESP32() { + } /// Destructor virtual ~AnalogDriverESP32() { @@ -43,15 +44,10 @@ class AnalogDriverESP32 : public AnalogDriverBase { } /// starts the DAC - bool begin(AnalogConfigESP32 cfg) { + bool begin(AnalogConfig cfg) { TRACEI(); cfg.logInfo(); - if (adc_config.is_auto_center_read){ - LOGI("auto_center") - auto_center.begin(cfg.channels, cfg.bits_per_sample); - } - if (!is_driver_installed){ port_no = (i2s_port_t) cfg.port_no; @@ -92,10 +88,8 @@ class AnalogDriverESP32 : public AnalogDriverBase { switch (cfg.rx_tx_mode) { case RX_MODE: LOGI("RX_MODE"); - - setupInputPin(cfg.adc_pin); - if (i2s_set_adc_mode(adc_unit, adc_channel)!=ESP_OK) { + if (i2s_set_adc_mode(cfg.adc_unit[0], cfg.adc_channel[0])!=ESP_OK) { LOGE( "%s - %s", __func__, "i2s_driver_install"); return false; } @@ -136,9 +130,7 @@ class AnalogDriverESP32 : public AnalogDriverBase { /// stops the I2S and unistalls the driver void end() override { LOGI(__func__); - if (active) { - i2s_zero_dma_buffer(port_no); - } + i2s_zero_dma_buffer(port_no); // close ADC if (adc_config.rx_tx_mode == RX_MODE){ @@ -187,13 +179,9 @@ class AnalogDriverESP32 : public AnalogDriverBase { size_t readBytes(uint8_t *dest, size_t size_bytes) override { TRACED(); size_t result = 0; - if (i2s_read(port_no, dest, size_bytes, &result, adc_config.timeout)!=ESP_OK){ + if (i2s_read(port_no, dest, size_bytes, &result, portMAX_DELAY)!=ESP_OK){ TRACEE(); } - // make sure that the center is at 0 - if (adc_config.is_auto_center_read){ - auto_center.convert(dest, result); - } LOGD( "%s - len: %d -> %d", __func__, size_bytes, result); return result; } @@ -203,59 +191,12 @@ class AnalogDriverESP32 : public AnalogDriverBase { } protected: - AnalogConfigESP32 adc_config; - ConverterAutoCenter auto_center; + AnalogConfig adc_config; i2s_port_t port_no; bool active = false; bool is_driver_installed = false; size_t result=0; - // input values - adc_unit_t adc_unit; - adc1_channel_t adc_channel; - - /// Defines the current ADC pin. The following GPIO pins are supported: GPIO32-GPIO39 - void setupInputPin(int gpio){ - TRACED(); - - switch(gpio){ - case 32: - adc_unit = ADC_UNIT_1; - adc_channel = ADC1_GPIO32_CHANNEL; - break; - case 33: - adc_unit = ADC_UNIT_1; - adc_channel = ADC1_GPIO33_CHANNEL; - break; - case 34: - adc_unit = ADC_UNIT_1; - adc_channel = ADC1_GPIO34_CHANNEL; - break; - case 35: - adc_unit = ADC_UNIT_1; - adc_channel = ADC1_GPIO35_CHANNEL; - break; - case 36: - adc_unit = ADC_UNIT_1; - adc_channel = ADC1_GPIO36_CHANNEL; - break; - case 37: - adc_unit = ADC_UNIT_1; - adc_channel = ADC1_GPIO37_CHANNEL; - break; - case 38: - adc_unit = ADC_UNIT_1; - adc_channel = ADC1_GPIO38_CHANNEL; - break; - case 39: - adc_unit = ADC_UNIT_1; - adc_channel = ADC1_GPIO39_CHANNEL; - break; - - default: - LOGE( "%s - pin GPIO%d is not supported", __func__,gpio); - } - } // The internal DAC only supports 8 bit values - so we need to convert the data size_t outputStereo(const void *src, size_t size_bytes) { @@ -288,7 +229,7 @@ class AnalogDriverESP32 : public AnalogDriverBase { } if (output_size>0){ - if (i2s_write(port_no, src, output_size, &result, adc_config.timeout)!=ESP_OK){ + if (i2s_write(port_no, src, output_size, &result, portMAX_DELAY)!=ESP_OK){ LOGE("%s: %d", LOG_METHOD, output_size); } } diff --git a/src/AudioTools/CoreAudio/AudioAnalog/AnalogDriverMBED.h b/src/AudioAnalog/AnalogAudioMBED.h similarity index 89% rename from src/AudioTools/CoreAudio/AudioAnalog/AnalogDriverMBED.h rename to src/AudioAnalog/AnalogAudioMBED.h index e0f82c2a38..ad5b1f1741 100644 --- a/src/AudioTools/CoreAudio/AudioAnalog/AnalogDriverMBED.h +++ b/src/AudioAnalog/AnalogAudioMBED.h @@ -1,8 +1,8 @@ #pragma once -#include "AudioToolsConfig.h" -#if defined(IS_MBED) && defined(USE_ANALOG) || defined(DOXYGEN) +#include "AudioConfig.h" +#if defined(IS_MBED) && defined(USE_I2S_ANALOG) namespace audio_tools { @@ -37,7 +37,6 @@ class AnalogDriverMBED : public AnalogDriverBase { return false; } info = cfg; - auto_center.begin(config.channels, config.bits_per.sample); int n_samples = cfg.buffer_size / (cfg.bits_per_sample / 8); ring_buffer.resize(n_samples); switch (info.channels) { @@ -115,12 +114,6 @@ class AnalogDriverMBED : public AnalogDriverBase { } break; } - - // make sure that the center is at 0 - if (adc_config.is_auto_center_read){ - auto_center.convert(dest, result); - } - return result; } @@ -129,11 +122,10 @@ class AnalogDriverMBED : public AnalogDriverBase { protected: audio_tools::RingBuffer ring_buffer{0}; AnalogConfig info; - ConverterAutoCenter auto_center; AdvancedDAC dac1{PIN_DAC_1}; AdvancedDAC dac2{PIN_DAC_2}; - AdvancedADC adc1{PIN_ANALOG_START}; - AdvancedADC adc2{PIN_ANALOG_START + 1}; + AdvancedADC adc1{PIN_ADC_START}; + AdvancedADC adc2{PIN_ADC_START + 1}; bool active = false; /// The ringbuffer is used to make sure that we can write full SampleBuffers @@ -170,4 +162,4 @@ using AnalogDriver = AnalogDriverMBED; } // namespace audio_tools -#endif // USE_ANALOG \ No newline at end of file +#endif // USE_I2S_ANALOG \ No newline at end of file diff --git a/src/AudioTools/CoreAudio/AudioAnalog/README.md b/src/AudioAnalog/README.md similarity index 100% rename from src/AudioTools/CoreAudio/AudioAnalog/README.md rename to src/AudioAnalog/README.md diff --git a/src/AudioBasic/Collections.h b/src/AudioBasic/Collections.h new file mode 100644 index 0000000000..dbfb35c061 --- /dev/null +++ b/src/AudioBasic/Collections.h @@ -0,0 +1,12 @@ +#pragma once +/** + * @defgroup collections Collections + * @ingroup tools + * @brief Audio Player + */ + +#include "AudioBasic/Collections/Vector.h" +#include "AudioBasic/Collections/List.h" +#include "AudioBasic/Collections/Stack.h" +#include "AudioBasic/Collections/Queue.h" +#include "AudioBasic/Collections/BitVector.h" \ No newline at end of file diff --git a/src/AudioTools/CoreAudio/AudioBasic/Collections/BitVector.h b/src/AudioBasic/Collections/BitVector.h similarity index 91% rename from src/AudioTools/CoreAudio/AudioBasic/Collections/BitVector.h rename to src/AudioBasic/Collections/BitVector.h index 695b37d658..18c8c285fd 100644 --- a/src/AudioTools/CoreAudio/AudioBasic/Collections/BitVector.h +++ b/src/AudioBasic/Collections/BitVector.h @@ -1,5 +1,4 @@ #pragma once -#ifdef HAS_IOSTRAM #include "Vector.h" #include #include @@ -106,6 +105,7 @@ class BitVector { return result; } + friend std::ostream &operator<<(std::ostream &os, BitVector &dt); protected: Vector vector; @@ -114,7 +114,11 @@ class BitVector { int64_t max_idx = 0; }; +std::ostream &operator<<(std::ostream &os, BitVector &bv) { + for (int j = 0; j < bv.size(); j++) { + os << bv.get(j); + } + return os; +} -} // namespace audio_tools - -#endif \ No newline at end of file +} // namespace audio_tools \ No newline at end of file diff --git a/src/AudioTools/CoreAudio/AudioBasic/Collections/InitializerList.h b/src/AudioBasic/Collections/InitializerList.h similarity index 100% rename from src/AudioTools/CoreAudio/AudioBasic/Collections/InitializerList.h rename to src/AudioBasic/Collections/InitializerList.h diff --git a/src/AudioTools/CoreAudio/AudioBasic/Collections/List.h b/src/AudioBasic/Collections/List.h similarity index 88% rename from src/AudioTools/CoreAudio/AudioBasic/Collections/List.h rename to src/AudioBasic/Collections/List.h index 9b8544d24a..f4bb3587ef 100644 --- a/src/AudioTools/CoreAudio/AudioBasic/Collections/List.h +++ b/src/AudioBasic/Collections/List.h @@ -3,7 +3,6 @@ # include "InitializerList.h" #endif #include -#include "Allocator.h" namespace audio_tools { @@ -21,6 +20,8 @@ class List { Node* next = nullptr; Node* prior = nullptr; T data; + + Node() = default; }; class Iterator { @@ -100,26 +101,18 @@ class List { }; /// Default constructor - List(Allocator &allocator=DefaultAllocator) { - p_allocator = &allocator; - link(); - }; + List() { link(); }; /// copy constructor List(List&ref) = default; /// Constructor using array template - List(const T (&a)[N], Allocator &allocator=DefaultAllocator) { - p_allocator = &allocator; + List(const T (&a)[N]) { link(); for(int i = 0; i < N; ++i) push_back(a[i]); } - ~List(){ - clear(); - } - #ifdef USE_INITIALIZER_LIST List(std::initializer_list iniList) { @@ -145,7 +138,7 @@ class List { } bool push_back(T data){ - Node *node = createNode(); + Node *node = new Node(); if (node==nullptr) return false; node->data = data; @@ -162,7 +155,7 @@ class List { } bool push_front(T data){ - Node *node = createNode(); + Node *node = new Node(); if (node==nullptr) return false; node->data = data; @@ -179,7 +172,7 @@ class List { } bool insert(Iterator it, const T& data){ - Node *node = createNode(); + Node *node = new Node(); if (node==nullptr) return false; node->data = data; @@ -221,8 +214,7 @@ class List { p_prior->next = p_next; p_next->prior = p_prior; - deleteNode(p_delete); - + delete p_delete; record_count--; validate(); @@ -242,8 +234,7 @@ class List { p_prior->next = p_next; p_next->prior = p_prior; - deleteNode(p_delete); - + delete p_delete; record_count--; validate(); @@ -263,8 +254,7 @@ class List { p_prior->next = p_next; p_next->prior = p_prior; - deleteNode(p_delete); - + delete p_delete; record_count--; return true; } @@ -316,39 +306,11 @@ class List { return n->data; } - void setAllocator(Allocator &allocator){ - p_allocator = &allocator; - } - - /// Provides the last element - T& back() { - return *rbegin(); - } - protected: Node first; // empty dummy first node which which is always before the first data node Node last; // empty dummy last node which which is always after the last data node size_t record_count=0; - Allocator *p_allocator = &DefaultAllocator; - - Node* createNode() { -#if USE_ALLOCATOR - Node *node = (Node*) p_allocator->allocate(sizeof(Node));// new Node(); -#else - Node *node = new Node(); -#endif - return node; - } - - void deleteNode(Node* p_delete){ -#if USE_ALLOCATOR - p_allocator->free(p_delete); //delete p_delete; -#else - delete p_delete; -#endif - - } void link(){ first.next = &last; diff --git a/src/AudioTools/CoreAudio/AudioBasic/Collections/Queue.h b/src/AudioBasic/Collections/Queue.h similarity index 83% rename from src/AudioTools/CoreAudio/AudioBasic/Collections/Queue.h rename to src/AudioBasic/Collections/Queue.h index 1511163815..4021c9f1da 100644 --- a/src/AudioTools/CoreAudio/AudioBasic/Collections/Queue.h +++ b/src/AudioBasic/Collections/Queue.h @@ -1,5 +1,5 @@ #pragma once -#include "AudioTools/CoreAudio/AudioBasic/Collections/List.h" +#include "AudioBasic/Collections/List.h" namespace audio_tools { @@ -41,10 +41,6 @@ class Queue { return l.empty(); } - void setAllocator(Allocator &allocator){ - l.setAllocator(allocator); - } - protected: List l; }; diff --git a/src/AudioBasic/Collections/QueueFreeRTOS.h b/src/AudioBasic/Collections/QueueFreeRTOS.h new file mode 100644 index 0000000000..c0628c9741 --- /dev/null +++ b/src/AudioBasic/Collections/QueueFreeRTOS.h @@ -0,0 +1,85 @@ +#pragma once +#include "AudioConfig.h" +#ifdef USE_CONCURRENCY +#include "freertos/FreeRTOS.h" + +namespace audio_tools { + +/** + * @brief FIFO Queue which is based on a List + * @ingroup collections + * @author Phil Schatzmann + * @copyright GPLv3 + * @tparam T + */ +template +class QueueFreeRTOS { + public: + QueueFreeRTOS(int size, TickType_t writeMaxWait=portMAX_DELAY, TickType_t readMaxWait=portMAX_DELAY) { + TRACED(); + if (size>0){ + xQueue = xQueueCreate( size+1, sizeof(T) ); + } + read_max_wait = readMaxWait; + write_max_wait = writeMaxWait; + }; + + ~QueueFreeRTOS(){ + TRACED(); + vQueueDelete(xQueue); + } + + void setReadMaxWait(TickType_t ticks){ + read_max_wait = ticks; + } + + void setWriteMaxWait(TickType_t ticks){ + write_max_wait = ticks; + } + + /// (Re-)defines the size + void resize(int size){ + TRACED(); + if (xQueue!=NULL) vQueueDelete(xQueue); + xQueue = xQueueCreate( size+1, sizeof(T) ); + } + + bool enqueue(T& data){ + TRACED(); + return xQueueSend( xQueue, ( void * ) &data, ( TickType_t ) write_max_wait ); + } + + bool peek(T& data){ + TRACED(); + return xQueuePeek(xQueue, &data, (TickType_t) read_max_wait ); + } + + bool dequeue(T& data){ + TRACED(); + return xQueueReceive(xQueue, &data, ( TickType_t ) read_max_wait ); + } + + size_t size() { + return uxQueueMessagesWaiting(xQueue); + } + + bool clear() { + TRACED(); + T data; + while(dequeue(data)); + return true; + } + + bool empty() { + return size()==0; + } + + protected: + QueueHandle_t xQueue = NULL; + TickType_t write_max_wait=portMAX_DELAY; + TickType_t read_max_wait=portMAX_DELAY; + +}; + +} +#endif \ No newline at end of file diff --git a/src/AudioBasic/Collections/QueueLockFree.h b/src/AudioBasic/Collections/QueueLockFree.h new file mode 100644 index 0000000000..dda9b934d3 --- /dev/null +++ b/src/AudioBasic/Collections/QueueLockFree.h @@ -0,0 +1,39 @@ +#pragma once + +namespace audio_tools { + +/** + * @brief Lock Free Queue + * @ingroup collections + * @tparam T + */ +template +class QueueLockFree { +public: + void enqueue(T const &data) { + std::atomic const new_node = new node(data); + node *old_tail = tail.load(); + while (!old_tail->next.compare_exchange_weak(nullptr, new_node)) { + node *old_tail = tail.load(); + } + tail.compare_exchange_weak(old_tail, new_node); + } + std::shared_ptr dequeue() { + node *old_head = head.load(); + while (old_head && !head.compare_exchange_weak(old_head, old_head->next)) { + old_head = head.load(); + } + return old_head ? old_head->data : std::shared_ptr(); + } + +protected: + struct node { + std::shared_ptr data; + std::atomic next; + node(T const &data_) : data(std::make_shared(data_)) {} + }; + std::atomic head; + std::atomic tail; +}; + +} // namespace audio_tools \ No newline at end of file diff --git a/src/AudioTools/CoreAudio/AudioBasic/Collections/Stack.h b/src/AudioBasic/Collections/Stack.h similarity index 82% rename from src/AudioTools/CoreAudio/AudioBasic/Collections/Stack.h rename to src/AudioBasic/Collections/Stack.h index 70ffcf3937..9d3b584638 100644 --- a/src/AudioTools/CoreAudio/AudioBasic/Collections/Stack.h +++ b/src/AudioBasic/Collections/Stack.h @@ -1,5 +1,5 @@ #pragma once -#include "AudioTools/CoreAudio/AudioBasic/Collections/List.h" +#include "AudioBasic/Collections/List.h" namespace audio_tools { @@ -40,11 +40,6 @@ class Stack { bool empty() { return l.empty(); } - - void setAllocator(Allocator &allocator){ - l.setAllocator(allocator); - } - protected: List l; }; diff --git a/src/AudioBasic/Collections/Vector.h b/src/AudioBasic/Collections/Vector.h new file mode 100644 index 0000000000..7d9e90a5e3 --- /dev/null +++ b/src/AudioBasic/Collections/Vector.h @@ -0,0 +1,340 @@ +#pragma once +#ifdef USE_INITIALIZER_LIST +# include "InitializerList.h" +#endif +namespace audio_tools { + +/** + * @brief Vector implementation which provides the most important methods as defined by std::vector. This class it is quite handy + * to have and most of the times quite better then dealing with raw c arrays. + * @ingroup collections + * @author Phil Schatzmann + * @copyright GPLv3 + **/ + +template +class Vector { + public: + /** + * @brief Iterator for the Vector class + * + * by Phil Schatzmann + **/ + + class iterator { + protected: + T *ptr; + size_t pos_; + public: + inline iterator(){ + } + inline iterator(T* parPtr, size_t pos){ + this->ptr = parPtr; + this->pos_ = pos; + } + // copy constructor + inline iterator(const iterator ©From){ + ptr = copyFrom.ptr; + pos_ = copyFrom.pos_; + } + inline iterator operator++(int n) { + ptr++; + pos_++; + return *this; + } + inline iterator operator++() { + ptr++; + pos_++; + return *this; + } + inline iterator operator--(int n) { + ptr--; + pos_--; + return *this; + } + inline iterator operator--() { + ptr--; + pos_--; + return *this; + } + inline iterator operator+(int offset) { + pos_ += offset; + return iterator(ptr+offset, offset); + } + inline bool operator==(iterator it) { + return ptr == it.getPtr(); + } + inline bool operator<(iterator it) { + return ptr < it.getPtr(); + } + inline bool operator<=(iterator it) { + return ptr <= it.getPtr(); + } + inline bool operator>(iterator it) { + return ptr > it.getPtr(); + } + inline bool operator>=(iterator it) { + return ptr >= it.getPtr(); + } + inline bool operator!=(iterator it) { + return ptr != it.getPtr(); + } + inline T &operator*() { + return *ptr; + } + inline T *operator->() { + return ptr; + } + inline T *getPtr() { + return ptr; + } + inline size_t pos() { + return pos_; + } + inline size_t operator-(iterator it) { + return (ptr - it.getPtr()); + } + + }; + +#ifdef USE_INITIALIZER_LIST + + /// support for initializer_list + inline Vector(std::initializer_list iniList) { + resize(iniList.size()); + int pos = 0; + for (auto &obj : iniList){ + p_data[pos++] = obj; + } + } + +#endif + + /// default constructor + inline Vector(size_t len = 20) { + resize_internal(len, false); + } + + /// allocate size and initialize array + inline Vector(int size, T value) { + resize(size); + for (int j=0;j< size;j++){ + p_data[j] = value; + } + } + + inline Vector(Vector &&moveFrom) = default; + + + /// copy constructor + inline Vector(Vector ©From) { + resize_internal(copyFrom.size(), false); + for (int j=0;jlen = copyFrom.size(); + } + + /// legacy constructor with pointer range + inline Vector(T *from, T *to) { + this->len = to - from; + resize_internal(this->len, false); + for (size_t j=0;jlen;j++){ + p_data[j] = from[j]; + } + } + + /// Destructor + virtual ~Vector() { + clear(); + shrink_to_fit(); + delete [] this->p_data; + } + + inline void clear() { + len = 0; + } + + inline int size() { + return len; + } + + inline bool empty() { + return size()==0; + } + + inline void push_back(T value){ + resize_internal(len+1, true); + p_data[len] = value; + len++; + } + + inline void push_front(T value){ + resize_internal(len+1, true); + memmove(p_data,p_data+1,len*sizeof(T)); + p_data[0] = value; + len++; + } + + inline void pop_back(){ + if (len>0) { + len--; + } + } + + inline void pop_front(){ + if (len>0) { + len--; + if (len>0){ + memmove(p_data, p_data+1,len*sizeof(T)); + } + } + } + + + inline void assign(iterator v1, iterator v2) { + size_t newLen = v2 - v1; + resize_internal(newLen, false); + this->len = newLen; + int pos = 0; + for (auto ptr = v1; ptr != v2; ptr++) { + p_data[pos++] = *ptr; + } + } + + inline void assign(size_t number, T value) { + resize_internal(number, false); + this->len = number; + for (int j=0;j &in){ + // save data + T *dataCpy = p_data; + int bufferLenCpy = bufferLen; + int lenCpy = len; + // swap this + p_data = in.p_data; + len = in.len; + bufferLen = in.bufferLen; + // swp in + in.p_data = dataCpy; + in.len = lenCpy; + in.bufferLen = bufferLenCpy; + } + + inline T &operator[](int index) { + assert(p_data!=nullptr); + return p_data[index]; + } + + inline Vector &operator=(Vector ©From) { + resize_internal(copyFrom.size(), false); + for (int j=0;jlen = copyFrom.size(); + return *this; + } + + inline T &operator[] (const int index) const { + return p_data[index]; + } + + inline bool resize(int newSize, T value){ + if (resize(newSize)){ + for (int j=0;jlen, true, true); + } + + int capacity(){ + return this->bufferLen; + } + + inline bool resize(int newSize){ + int oldSize = this->len; + resize_internal(newSize, true); + this->len = newSize; + return this->len!=oldSize; + } + + inline iterator begin(){ + return iterator(p_data, 0); + } + + inline T& back(){ + return *iterator(p_data+(len-1), len-1); + } + + inline iterator end(){ + return iterator(p_data+len, len); + } + + // removes a single element + inline void erase(iterator it) { + int pos = it.pos(); + if (posbufferLen || this->p_data==nullptr ||shrink){ + //withNewSize = true; + T* oldData = p_data; + int oldBufferLen = this->bufferLen; + this->p_data = new T[newSize+1]; + this->bufferLen = newSize; + if (oldData != nullptr) { + if(copy && this->len > 0){ + memcpy((void*)p_data,(void*) oldData, this->len*sizeof(T)); + } + if (shrink){ + cleanup(oldData, newSize, oldBufferLen); + } + delete [] oldData; + } + } + assert(p_data!=nullptr); + } + + void cleanup(T*p_data, int from, int to){ + for (int j=from;jvalue = float16::float_to_half(in); + } + float16(const float16 &value16){ + this->value = value16.value; + } + inline operator float() { + return half_to_float(value); + } + explicit inline operator double() { + return (double) float16::half_to_float(value); + } + explicit inline operator int() { + return (int) float16::half_to_float(value); + } + explicit inline operator int() const { + return (int) float16::half_to_float(value); + } + inline bool operator< (float16 other) const{ + return float() < (float)other; + } + inline bool operator<= (float16 other) const{ + return float() <= (float)other; + } + inline bool operator> (float16 other) const{ + return float() > (float)other; + } + inline bool operator>= (float16 other) const{ + return float() >= (float)other; + } + inline bool operator== (float16 other) const{ + return float() == (float)other; + } + inline bool operator!= (float16 other) const{ + return float() != (float)other; + } + + + protected: + uint16_t value=0; + + /// see https://stackoverflow.com/questions/1659440/32-bit-to-16-bit-floating-point-conversion + static uint32_t as_uint(const float x) { + return *(uint*)&x; + } + /// see https://stackoverflow.com/questions/1659440/32-bit-to-16-bit-floating-point-conversion + static float as_float(const uint32_t x) { + return *(float*)&x; + } + + /// see https://stackoverflow.com/questions/1659440/32-bit-to-16-bit-floating-point-conversion + static float half_to_float(const uint16_t x) { // IEEE-754 16-bit floating-point format (without infinity): 1-5-10, exp-15, +-131008.0, +-6.1035156E-5, +-5.9604645E-8, 3.311 digits + const uint32_t e = (x&0x7C00)>>10; // exponent + const uint32_t m = (x&0x03FF)<<13; // mantissa + const uint32_t v = as_uint((float)m)>>23; // evil log2 bit hack to count leading zeros in denormalized format + return as_float((x&0x8000)<<16 | (e!=0)*((e+112)<<23|m) | ((e==0)&(m!=0))*((v-37)<<23|((m<<(150-v))&0x007FE000))); // sign : normalized : denormalized + } + /// see https://stackoverflow.com/questions/1659440/32-bit-to-16-bit-floating-point-conversion + static uint16_t float_to_half(const float x) { // IEEE-754 16-bit floating-point format (without infinity): 1-5-10, exp-15, +-131008.0, +-6.1035156E-5, +-5.9604645E-8, 3.311 digits + const uint32_t b = as_uint(x)+0x00001000; // round-to-nearest-even: add last bit after truncated mantissa + const uint32_t e = (b&0x7F800000)>>23; // exponent + const uint32_t m = b&0x007FFFFF; // mantissa; in line below: 0x007FF000 = 0x00800000-0x00001000 = decimal indicator flag - initial rounding + return (b&0x80000000)>>16 | (e>112)*((((e-112)<<10)&0x7C00)|m>>13) | ((e<113)&(e>101))*((((0x007FF000+m)>>(125-e))+1)>>1) | (e>143)*0x7FFF; // sign : normalized : denormalized : saturate + } + +}; + +inline float operator+ (float16 one, float16 two) +{ + return (float)one + (float)two; +} +inline float operator- (float16 one, float16 two) +{ + return (float)one - (float)two; +} +inline float operator* (float16 one, float16 two) +{ + return (float)one * (float)two; +} +inline float operator/ (float16 one, float16 two) +{ + return (float)one / (float)two; +} +inline float operator+ (float16 one, float two) +{ + return (float)one + two; +} +inline float operator- (float16 one, float two) +{ + return (float)one - two; +} +inline float operator* (float16 one, float two) +{ + return (float)one * two; +} +inline float operator/ (float16 one, float two) +{ + return (float)one / two; +} +inline float operator+ (float one, float16 two) +{ + return two + float(one); +} +inline float operator- (float one, float16 two) +{ + return two - float(one); +} +inline float operator* (float one, float16 two) +{ + return two * float(one); +} +inline float operator/ (float one, float16 two) +{ + return two / float(one); +} + +} + +namespace std { + +inline float floor ( float16 arg ) { return std::floor((float)arg);} +inline float fabs ( float16 arg ) { return std::fabs((float)arg);} + +} diff --git a/src/AudioTools/CoreAudio/AudioBasic/Float32.h b/src/AudioBasic/Float32.h similarity index 98% rename from src/AudioTools/CoreAudio/AudioBasic/Float32.h rename to src/AudioBasic/Float32.h index 8ad06c6e44..7efdb43afb 100644 --- a/src/AudioTools/CoreAudio/AudioBasic/Float32.h +++ b/src/AudioBasic/Float32.h @@ -1,5 +1,5 @@ #pragma once -#include "AudioToolsConfig.h" +#include "AudioConfig.h" namespace audio_tools { diff --git a/src/AudioTools/CoreAudio/AudioBasic/Int24_4bytes_t.h b/src/AudioBasic/Int24.h similarity index 67% rename from src/AudioTools/CoreAudio/AudioBasic/Int24_4bytes_t.h rename to src/AudioBasic/Int24.h index 81704f9d54..74b47417eb 100644 --- a/src/AudioTools/CoreAudio/AudioBasic/Int24_4bytes_t.h +++ b/src/AudioBasic/Int24.h @@ -1,5 +1,5 @@ #pragma once -#include "AudioToolsConfig.h" +#include "AudioConfig.h" #define INT24_MAX 0x7FFFFF @@ -13,39 +13,39 @@ namespace audio_tools { * @copyright GPLv3 * */ -class int24_4bytes_t { +class int24_t { public: - int24_4bytes_t() { + int24_t() { value = 0; } - int24_4bytes_t(void *ptr) { + int24_t(void *ptr) { memcpy(&value, ptr, 4); } - int24_4bytes_t(const int16_t in) { + int24_t(const int16_t in) { set(in) ; } - int24_4bytes_t(const int32_t in) { + int24_t(const int32_t in) { set(in); } - int24_4bytes_t(const int64_t in) { + int24_t(const int64_t in) { set((int32_t)in) ; } - int24_4bytes_t(const int24_4bytes_t &in) { + int24_t(const int24_t &in) { value = in.value; } - int24_4bytes_t(const float in) { + int24_t(const float in) { set((int32_t)in); } #if defined(USE_INT24_FROM_INT) - int24_4bytes_t(const int in) { + int24_t(const int in) { set(in); } @@ -62,12 +62,12 @@ class int24_4bytes_t { } } - int24_4bytes_t& operator=(const int24_4bytes_t& other){ + int24_t& operator=(const int24_t& other){ value = other.value; return *this; } - int24_4bytes_t& operator=(const float& other){ + int24_t& operator=(const float& other){ set(((int32_t)other)); return *this; } @@ -84,14 +84,14 @@ class int24_4bytes_t { return toInt(); } - int24_4bytes_t& operator +=(int32_t valueA){ + int24_t& operator +=(int32_t valueA){ int32_t temp = toInt(); temp += valueA; set(temp); return *this; } - int24_4bytes_t& operator -=(int32_t valueA){ + int24_t& operator -=(int32_t valueA){ int32_t temp = toInt(); temp -= valueA; set(temp); @@ -134,18 +134,4 @@ class int24_4bytes_t { }; -} // namespace audio_tools - -#ifdef USE_TYPETRAITS -#include - -namespace std { - template<> class numeric_limits { - public: - static audio_tools::int24_4bytes_t lowest() {return audio_tools::int24_4bytes_t(-0x7FFFFF);}; - static audio_tools::int24_4bytes_t min() {return audio_tools::int24_4bytes_t(-0x7FFFFF);}; - static audio_tools::int24_4bytes_t max() {return audio_tools::int24_4bytes_t(0x7FFFFF);}; - }; -} - -#endif \ No newline at end of file +} // namespace audio_tools \ No newline at end of file diff --git a/src/AudioTools/CoreAudio/AudioBasic/Int24_3bytes_t.h b/src/AudioBasic/Int24x.h similarity index 65% rename from src/AudioTools/CoreAudio/AudioBasic/Int24_3bytes_t.h rename to src/AudioBasic/Int24x.h index b4bdd7dc0b..59b40456b2 100644 --- a/src/AudioTools/CoreAudio/AudioBasic/Int24_3bytes_t.h +++ b/src/AudioBasic/Int24x.h @@ -1,5 +1,5 @@ #pragma once -#include "AudioToolsConfig.h" +#include "AudioConfig.h" #define INT24_MAX 0x7FFFFF @@ -13,39 +13,31 @@ namespace audio_tools { * @copyright GPLv3 * */ -class int24_3bytes_t { +class int24x_t { public: - int24_3bytes_t() { + int24x_t() { value[0] = 0; value[1] = 0; value[2] = 0; } - int24_3bytes_t(void *ptr) { + int24x_t(void *ptr) { memcpy(value, ptr, 3); } - int24_3bytes_t(const int16_t &in) { + int24x_t(const int16_t &in) { value[2] = in > 0 ? 0 : 0xFF; value[1] = (in >> 8) & 0xFF; value[0] = in & 0xFF; } - int24_3bytes_t(const int32_t &in) { + int24x_t(const int32_t &in) { set(in); } - int24_3bytes_t(const int64_t &in) { - set(in); - } - - int24_3bytes_t(const float in) { - set((int32_t)in); - } +#if defined(STM32) || defined(ESP32C3) -#if defined(USE_INT24_FROM_INT) - - int24_3bytes_t(const int &in) { + int24x_t(const int &in) { set(in); } @@ -57,12 +49,12 @@ class int24_3bytes_t { value[0] = in & 0xFF; } - int24_3bytes_t& operator=(const int24_3bytes_t& other){ + int24x_t& operator=(const int24x_t& other){ set(other); return *this; } - int24_3bytes_t& operator=(const float& other){ + int24x_t& operator=(const float& other){ set((int32_t)other); return *this; } @@ -71,14 +63,14 @@ class int24_3bytes_t { return toInt(); } - int24_3bytes_t& operator +=(int32_t value){ + int24x_t& operator +=(int32_t value){ int32_t temp = toInt(); temp += value; set(temp); return *this; } - int24_3bytes_t& operator -=(int32_t value){ + int24x_t& operator -=(int32_t value){ int32_t temp = toInt(); temp -= value; set(temp); @@ -129,18 +121,4 @@ class int24_3bytes_t { }; -} // namespace audio_tools - -#ifdef USE_TYPETRAITS -#include - -namespace std { - template<> class numeric_limits { - public: - static audio_tools::int24_3bytes_t lowest() {return audio_tools::int24_3bytes_t(-0x7FFFFF);}; - static audio_tools::int24_3bytes_t min() {return audio_tools::int24_3bytes_t(-0x7FFFFF);}; - static audio_tools::int24_3bytes_t max() {return audio_tools::int24_3bytes_t(0x7FFFFF);}; - }; -} - -#endif \ No newline at end of file +} // namespace audio_tools \ No newline at end of file diff --git a/src/AudioTools/CoreAudio/AudioBasic/README.md b/src/AudioBasic/README.md similarity index 100% rename from src/AudioTools/CoreAudio/AudioBasic/README.md rename to src/AudioBasic/README.md diff --git a/src/AudioBasic/Str.h b/src/AudioBasic/Str.h new file mode 100644 index 0000000000..f2854a05b8 --- /dev/null +++ b/src/AudioBasic/Str.h @@ -0,0 +1,873 @@ +#pragma once + +#include +#include +#include "AudioTools/AudioLogger.h" + +/** + * @defgroup string Strings + * @ingroup tools + * @brief Strings + * This framework is avoiding the use of Arduino Strings, so that we can use it + * easily also on other platforms! + */ + +namespace audio_tools { + +/** + * @brief A simple wrapper to provide string functions on char*. + * If the underlying char* is a const we do not allow any updates; The ownership + * of the chr* must be managed externally! + * @ingroup string + * @author Phil Schatzmann + * @copyright GPLv3 + */ + +class Str { + public: + Str() = default; + + /// Creates a Str for string constant + Str(const char* chars){ + if (chars!=nullptr){ + int len = strlen(chars); + set((char*)chars, len, len, true); + } else { + this->is_const = true; + clear(); + } + } + + /// Creates a Str with the indicated buffer + Str(char chars[], int maxlen, int len=0){ + set(chars, maxlen, len, false); + } + + Str (const Str & ) = default; + Str (Str && ) = default; + Str& operator = (const Str & ) = default; + Str& operator = (Str && ) = default; + + /// assigs a value + virtual void set(const char *alt){ + if (alt==nullptr){ + this->len = 0; + } else { + int new_len = strlen(alt); + grow(new_len); + this->len = new_len; + if (this->isConst()){ + /// if the Str is a const we replace the pointer + this->maxlen = this->len; + this->chars = (char*) alt; + } else { + /// if the Str is an external buffer we need to copy + strncpy(this->chars,alt,this->maxlen); + this->chars[len]=0; + } + } + } + + /// assigs from another Str value + virtual void set(const Str &alt){ + grow(alt.len); + this->len = alt.len; + + if (this->isConst()){ + /// if the Str is a const we replace the pointer + this->chars = alt.chars; + } else { + /// if the Str is an external buffer we need to copy + strncpy(this->chars,alt.chars,this->maxlen); + this->chars[len]=0; + } + } + + virtual void set(const char c){ + clear(); + add(c); + } + + virtual void set(int value){ + clear(); + add(value); + } + + virtual void set(double value, int precision=2, int withd=0){ + clear(); + add(value); + } + + virtual void swap(Str &str){ + char* cpy_chars = chars;; + bool cpy_is_const = is_const; + int cpy_len = len; + int cpy_maxlen = maxlen; + + chars = str.chars; + is_const = str.is_const; + len = str.len; + maxlen = str.maxlen; + + str.chars = cpy_chars; + str.is_const = cpy_is_const; + str.len = cpy_len; + str.maxlen = cpy_maxlen; + } + + + /// assigns a memory buffer + virtual void set(char chars[], int maxlen, int len=0, bool isConst=false){ + this->chars = chars; + this->maxlen = maxlen; + this->len = len; + this->is_const = isConst; + if (len==0 && !isConst){ + this->chars[0] = 0; + } + } + + /// adds a int value + virtual void add(int value){ + if (!this->isConst()){ + grow(this->length()+11); + sprintf(this->chars+len,"%d",value); + len = strlen(chars); + } + } + + /// adds a double value + virtual void add(double value, int precision=2, int withd=0){ + if (!this->isConst()){ + grow(this->length()+20); + floatToString(this->chars+len, value, precision, withd); + len = strlen(chars); + } + } + + /// adds a string + virtual void add(const char* append){ + if (!isConst() && append!=nullptr){ + int append_len = strlen(append); + grow(this->length()+append_len+1); + int n = (len+append_len)length()+1); + chars[len] = c; + chars[++len]=0; + } + } + + /// checks if the string equals indicated parameter string + virtual bool equals(const char* str){ + if (str==nullptr) return false; + return strcmp(this->chars, str)==0; + } + + /// checks if the string starts with the indicated substring + virtual bool startsWith(const char* str){ + if (str==nullptr) return false; + int len = strlen(str); + return strncmp(this->chars,str, len)==0; + } + + /// checks if the string ends with the indicated substring + virtual bool endsWith(const char* str){ + if (str==nullptr) return false; + int endlen = strlen(str); + return strncmp(this->chars+(len-endlen),str, endlen)==0; + } + + /// checks if the string ends with the indicated substring + virtual bool endsWithIgnoreCase(const char* str){ + if (str==nullptr) return false; + int endlen = strlen(str); + return strncmp_i(this->chars+(len-endlen),str, endlen)==0; + } + + + /// virtual bool matches(const char* match){ + /// int m_size = strlen(match); + /// if (length() < m_size) + /// return false; + /// if (strncmp(this->chars,match,m_size-1)!=0) + /// return false; + /// if (match[m_size-1]=='*' || match[m_size-1]==this->chars[m_size-1] ){ + /// return true; + /// } + /// return false; + /// } + + /// file matching supporting * and ? - replacing regex which is not supported in all environments + virtual bool matches(const char* pattern) { + /// returns 1 (true) if there is a match + /// returns 0 if the pattern is not whitin the line + int wildcard = 0; + const char* line = this->chars; + + const char* last_pattern_start = 0; + const char* last_line_start = 0; + do{ + if (*pattern == *line){ + if(wildcard == 1) + last_line_start = line + 1; + + line++; + pattern++; + wildcard = 0; + } + else if (*pattern == '?'){ + if(*(line) == '\0') /// the line is ended but char was expected + return 0; + if(wildcard == 1) + last_line_start = line + 1; + line++; + pattern++; + wildcard = 0; + } + else if (*pattern == '*') { + if (*(pattern+1) == '\0'){ + return 1; + } + + last_pattern_start = pattern; + //last_line_start = line + 1; + wildcard = 1; + + pattern++; + } + else if (wildcard) { + if (*line == *pattern) + { + wildcard = 0; + line++; + pattern++; + last_line_start = line + 1 ; + } + else + { + line++; + } + } else + { + if ((*pattern) == '\0' && (*line) == '\0') /// end of mask + return 1; /// if the line also ends here then the pattern match + else + { + if (last_pattern_start != 0) /// try to restart the mask on the rest + { + pattern = last_pattern_start; + line = last_line_start; + last_line_start = 0; + } + else + { + return false; + } + } + } + + } while (*line); + + + if (*pattern == '\0'){ + return true; + } else { + return false; + } + } + + + + /// provides the position of the the indicated character after the indicated start position + virtual int indexOf(const char c, int start=0){ + for (int j=start;j=0;j--){ + if (strncmp(cont,chars+j,contLen)==0){ + return j; + } + } + return -1; + } + + /// we can assign a const char* + virtual void operator=(const char* str) { + set(str); + } + + /// we can assign a char* + virtual void operator=(char* str) { + set(str); + } + + /// we can assign a char + virtual void operator=(char c) { + set(c); + } + + /// we can assign a double + virtual void operator=(double val) { + set(val); + } + + /// we can assign an int + virtual void operator=(int value) { + set(value); + } + + /// shift characters to the right -> we just move the pointer + virtual void operator<<(int n){ + if (isConst()){ + this->chars+=n; + this->len-=n; + } else { + memmove(this->chars,this->chars+n,len+1); + } + } + + virtual char operator[](int index){ + return chars[index]; + } + + /// adds a substring at the end of the string + virtual void operator+=(const char* str) { + add(str); + } + + /// adds a int at the end of the string + virtual void operator+=(int value) { + add(value); + } + + /// adds a double at the end of the string + virtual void operator+=(double value) { + add(value); + } + + /// adds a character + virtual void operator+=(const char value) { + add(value); + } + + /// checks if the indicated string is equal to the current string + virtual bool operator==(const Str &alt) const { + if (this->len != alt.len) + return false; + return strncmp(this->chars, alt.chars, this->len)==0; + } + + /// checks if the indicated string is equal to the current string + virtual bool operator==(const char *alt) const { + return strncmp(this->chars, alt, this->len)==0; + } + + /// checks if the indicated string is different from the current string + virtual bool operator!=(const Str &alt) const { + return strncmp(this->chars, alt.chars, this->len)!=0; + } + + /// checks if the indicated string is different from the current string + virtual bool operator!=(const char *alt) const { + return strncmp(this->chars, alt, this->len)!=0; + } + + /// provides the string value as const char* + virtual const char* c_str() { + return chars; + } + + /// provides the current length (filled with characters) of the string - excluding the terminating 0 + virtual int length() { + return len; + } + + /// checks if the string is empty + virtual bool isEmpty(){ + return len==0; + } + + /// provides the maximum length of the string + virtual int maxLength() { + return maxlen; + } + + /// Replaces the first instance of toReplace with replaced + virtual bool replace(const char* toReplace, const char* replaced){ + bool result = false; + if (toReplace==nullptr||replaced==nullptr){ + return result; + } + if (!isConst()){ + int pos = indexOf(toReplace); + int old_len = length(); + int insert_len =0; + if (pos>=0){ + int len_replaced = strlen(replaced); + int len_to_replace = strlen(toReplace); + insert_len = len_replaced-len_to_replace; + grow(this->length()+insert_len); + // save remainder and create gap + memmove(this->chars+pos+len_replaced, this->chars+pos+len_to_replace, old_len-pos-len_to_replace+1); + // move new string into gap + memmove(this->chars+pos,replaced,len_replaced); + result = true; + len += insert_len; + } + } + return result; + } + + /// Replaces all instances of toReplace with replaced + virtual bool replaceAll(const char* toReplace, const char* replaced){ + if (indexOf(toReplace)==-1){ + return false; + } + while(replace(toReplace,replaced)); + return true; + } + + /// removes the indicated substring from the string + virtual void remove(const char* toRemove){ + if (!isConst() && chars!=nullptr){ + int removeLen = strlen(toRemove); + int pos = indexOf(toRemove); + if (pos>=0){ + memmove((void*) (chars+pos), (void*) (chars+pos+removeLen), len - (pos + removeLen)+1); + len -= removeLen; + } + } + } + + /// removes the indicated substring from the string + virtual void removeAll(const char* toRemove){ + if (!isConst() && chars!=nullptr){ + int removeLen = strlen(toRemove); + while(true){ + int pos = indexOf(toRemove); + if (pos==-1){ + break; + } + memmove((void*) (chars+pos), (void*) (chars+pos+removeLen), len - (pos + removeLen)+1); + len -= removeLen; + } + } + } + + /// limits the length of the string (by adding a delimiting 0) + virtual void setLength(int len, bool addZero=true){ + if (!isConst() && addZero){ + this->savedChar = chars[len]; + this->savedLen = len; + this->len = len; + chars[len]=0; + } + } + + /// undo the last setLength call + virtual void setLengthUndo() { + if (savedLen>=0){ + chars[len] = savedChar; + this->len = savedLen; + savedLen = -1; + } + } + + /// copies a substring into the current string + virtual void substring(Str& from, int start, int end){ + if (end>start){ + int len = end-start; + grow(len); + if (this->chars!=nullptr) { + len = len < this->maxlen ? len : this->maxlen; + strncpy(this->chars, from.chars+start, len); + this->len = len; + this->chars[len]=0; + } + } + } + + /// copies a substring into the current string + virtual void substring(const char* from, int start, int end){ + if (end>start){ + int len = end-start; + grow(len); + if (this->chars!=nullptr) { + strncpy(this->chars, from+start, len); + this->chars[len]=0; + this->len = len; + } + } + } + + /// remove leading and traling spaces + virtual void trim(){ + rtrim(); + ltrim(); + } + + /// count number of indicated characters as position + virtual int count(char c, int startPos) { + for (int j=startPos;jlength()+insert_len); + int move_len = this->len - pos + 1; + memmove(chars+pos+insert_len, chars+pos, move_len); + strncpy(chars+pos, str, insert_len); + } + } + + /// Compares the string ignoring the case + virtual bool equalsIgnoreCase(const char* alt){ + if ((size_t)len != strlen(alt)){ + return false; + } + for (int j=0;j= 0; i--) { + for (j = 7; j >= 0; j--) { + byte = (b[i] >> j) & 1; + result[idx++] = byte ? '1' : '0'; + } + } + result[idx]=0; + return result; + } + + bool containsNumber() { + for (int j=0;j1){ + result = false; + } + break; + default: + result = false; + break; + } + } + } + return result; + } + + /// Determines the number of decimals in the number string + int numberOfDecimals() { + int result = 0; + int pos = indexOf("."); + if (pos>=0){ + for (int j=pos+1;j1){ + result = false; + } + break; + case '.': + dot_count++; + if (dot_count>1){ + result = false; + } + break; + default: + result = false; + break; + } + } + } + return result; + } + + protected: + char* chars = nullptr; + bool is_const=false; + int len=0; + int maxlen = 0; + int savedLen = -1; + char savedChar; + + + /// only supported in subclasses + virtual bool grow(int newMaxLen){ + return false; + } + + static char* itoa(int n, char s[]) { + int i, sign; + if ((sign = n) < 0) /* record sign */ + n = -n; /* make n positive */ + i = 0; + do { /* generate digits in reverse order */ + s[i++] = n % 10 + '0'; /* get next digit */ + } while ((n /= 10) > 0); /* delete it */ + if (sign < 0) + s[i++] = '-'; + s[i] = '\0'; + reverse(s); + return s; + } + + static void reverse(char s[]) { + int i, j; + char c; + + for (i = 0, j = strlen(s)-1; i 0) { + strcat(outstr, ".\0"); /// print the decimal point + unsigned long frac; + unsigned long mult = 1; + int padding = precision -1; + while(precision--) + mult *=10; + + if(val >= 0) + frac = (val - int(val)) * mult; + else + frac = (int(val)- val ) * mult; + unsigned long frac1 = frac; + + while(frac1 /= 10) + padding--; + + while(padding--) + strcat(outstr,"0\0"); + + strcat(outstr,itoa(frac,temp)); + } + + /// generate space padding + if ((widthp != 0)&&( (size_t)widthp >= strlen(outstr))){ + int J=0; + J = widthp - strlen(outstr); + + for (i=0; i< J; i++) { + temp[i] = ' '; + } + + temp[i++] = '\0'; + strcat(temp,outstr); + strcpy(outstr,temp); + } + + return outstr; + } + + static int strncmp_i(const char* s1, const char* s2, int n) { + if (n == 0) return (0); + do { + if (tolower(*s1) != tolower(*s2++)) + return (*(unsigned char *)s1 - *(unsigned char *)--s2); + if (*s1++ == 0) + break; + } while (--n != 0); + return (0); + } + + +}; + + +} + diff --git a/src/AudioBasic/StrExt.h b/src/AudioBasic/StrExt.h new file mode 100644 index 0000000000..bd05a8cb4f --- /dev/null +++ b/src/AudioBasic/StrExt.h @@ -0,0 +1,167 @@ +#pragma once + +#include "AudioBasic/Str.h" + +namespace audio_tools { + +/** + * @brief Str which keeps the data on the heap. We grow the allocated + * memory only if the copy source is not fitting. + * + * While it should be avoided to use a lot of heap allocatioins in + * embedded devices it is sometimes more convinent to allocate a string + * once on the heap and have the insurance that it might grow + * if we need to process an unexpected size. + * + * We also need to use this if we want to manage a vecor of strings. + * + * @ingroup string + * @author Phil Schatzmann + * @copyright GPLv3 + */ + +class StrExt : public Str { + + public: + StrExt() = default; + + StrExt(int initialAllocatedLength) : Str() { + maxlen = initialAllocatedLength; + } + + StrExt(Str &source) : Str() { + set(source); + } + + StrExt(StrExt &source) : Str(){ + set(source); + } + + StrExt(const char* str) : Str() { + if (str!=nullptr){ + len = strlen(str); + maxlen = len; + grow(maxlen); + if (chars!=nullptr){ + strcpy(chars, str); + } + } + } + + // move constructor + StrExt (StrExt &&obj) = default; + + // move assignment + StrExt& operator = (StrExt &&obj) = default; + + // copy assingment + StrExt& operator = (StrExt &obj) { + set(obj.c_str()); + return *this; + }; + + + ~StrExt() { + if (chars!=nullptr){ + LOGD("delete %d",maxlen); + delete [] chars; + chars = nullptr; + } + } + + bool isOnHeap() { + return true; + } + + bool isConst() { + return false; + } + + void operator=(const char* str) { + set(str); + } + + void operator=(char* str) { + set(str); + } + + void operator=(int v) { + set(v); + } + + void operator=(double v) { + set(v); + } + + size_t capacity() { + return maxlen; + } + + void setCapacity(size_t newLen){ + grow(newLen); + } + + // make sure that the max size is allocated + void allocate(int len=-1) { + int new_size = len<0?maxlen:len; + grow(new_size); + this->len = new_size; + } + + /// assigns a memory buffer + void copyFrom(const char *source, int len, int maxlen=0){ + this->maxlen = maxlen==0? len : maxlen; + grow(this->maxlen); + if (this->chars!=nullptr){ + this->len = len; + this->is_const = false; + memmove(this->chars, source, len); + this->chars[len] = 0; + } + } + + /// Fills the string with len chars + void setChars(char c, int len){ + grow(this->maxlen); + if (this->chars!=nullptr){ + for (int j=0;jchars[j]=c; + } + this->len = len; + this->is_const = false; + this->chars[len]=0; + } + } + + protected: + + bool grow(int newMaxLen){ + bool grown = false; + + if (chars==nullptr || newMaxLen > maxlen ){ + LOGD("grow(%d)",newMaxLen); + + grown = true; + // we use at minimum the defined maxlen + int newSize = newMaxLen > maxlen ? newMaxLen : maxlen; + if (chars!=nullptr){ + char* tmp = chars; + chars = new char[newSize+1]; + if (chars!=nullptr){ + strcpy(chars,tmp); + } + delete [] tmp; + } else { + chars = new char[newSize+1]; + if (chars!=nullptr) + chars[0] = 0; + } + maxlen = newSize; + + } + return grown; + } +}; + +} + diff --git a/src/AudioCodecs/AudioCodecs.h b/src/AudioCodecs/AudioCodecs.h new file mode 100644 index 0000000000..2bf87631dd --- /dev/null +++ b/src/AudioCodecs/AudioCodecs.h @@ -0,0 +1,26 @@ +#pragma once + +/** + * @defgroup codecs Codecs + * @ingroup main + * @brief Audio Coder and Decoder +**/ + +/** + * @defgroup encoder Encoder + * @ingroup codecs + * @brief Audio Encoder +**/ + +/** + * @defgroup decoder Decoder + * @ingroup codecs + * @brief Audio Decoder +**/ + +#include "AudioCodecs/CodecWAV.h" +#include "AudioCodecs/CodecCopy.h" +#include "AudioCodecs/CodecL8.h" +#include "AudioCodecs/CodecFloat.h" +#include "AudioCodecs/CodecBase64.h" + diff --git a/src/AudioCodecs/AudioEncoded.h b/src/AudioCodecs/AudioEncoded.h new file mode 100644 index 0000000000..7cea333873 --- /dev/null +++ b/src/AudioCodecs/AudioEncoded.h @@ -0,0 +1,628 @@ +#pragma once + +#include "AudioConfig.h" +#include "AudioTools/AudioIO.h" +#include "AudioTools/AudioOutput.h" +#include "AudioTools/AudioStreams.h" +#include "AudioTools/AudioTypes.h" + +namespace audio_tools { + +/** + * @brief Docoding of encoded audio into PCM data + * @ingroup codecs + * @author Phil Schatzmann + * @copyright GPLv3 + */ +class AudioDecoder : public AudioWriter, public AudioInfoSource { + public: + AudioDecoder() = default; + virtual ~AudioDecoder() = default; + AudioDecoder(AudioDecoder const &) = delete; + AudioDecoder &operator=(AudioDecoder const &) = delete; + + virtual AudioInfo audioInfo() { return info; }; + + /// for most decoders this is not needed + virtual void setAudioInfo(AudioInfo from) override { + TRACED(); + if (info != from) { + if (p_notify != nullptr) { + p_notify->setAudioInfo(from); + } + } + info = from; + } + /// Defines where the decoded result is written to + virtual void setOutput(AudioStream &out_stream) { + Print *p_print = &out_stream; + setOutput(*p_print); + setNotifyAudioChange(out_stream); + } + + /// Defines where the decoded result is written to + virtual void setOutput(AudioOutput &out_stream) { + Print *p_print = &out_stream; + setOutput(*p_print); + setNotifyAudioChange(out_stream); + } + + /// Defines where the decoded result is written to + virtual void setOutput(Print &out_stream) override { + p_print = &out_stream; + } + /// If true, the decoding result is PCM data + virtual bool isResultPCM() { return true; } + + /// Registers an object that is notified if the audio format is changing + void setNotifyAudioChange(AudioInfoSupport ¬ify) override { + p_notify = ¬ify; + } + + protected: + Print *p_print = nullptr; + AudioInfo info; + AudioInfoSupport *p_notify = nullptr; +}; + +/** + * @brief Encoding of PCM data + * @ingroup codecs + * @author Phil Schatzmann + * @copyright GPLv3 + */ +class AudioEncoder : public AudioWriter { + public: + AudioEncoder() = default; + virtual ~AudioEncoder() = default; + AudioEncoder(AudioEncoder const &) = delete; + AudioEncoder &operator=(AudioEncoder const &) = delete; + /// Provides the mime type of the encoded result + virtual const char *mime() = 0; + /// Defines the sample rate, number of channels and bits per sample + void setAudioInfo(AudioInfo from) override{}; +}; + +class AudioDecoderExt : public AudioDecoder { + public: + virtual void setBlockSize(int blockSize) = 0; +}; + +class AudioEncoderExt : public AudioEncoder { + public: + virtual int blockSize() =0; +}; + + +/** + * @brief Dummy no implmentation Codec. This is used so that we can initialize + * some pointers to decoders and encoders to make sure that they do not point to + * null. + * @ingroup codecs + * @author Phil Schatzmann + * @copyright GPLv3 + */ +class CodecNOP : public AudioDecoder, public AudioEncoder { + public: + static CodecNOP *instance() { + static CodecNOP self; + return &self; + } + + virtual void begin() {} + virtual void end() {} + virtual void setOutput(Print &out_stream) {} + virtual void setNotifyAudioChange(AudioInfoSupport &bi) {} + virtual void setAudioInfo(AudioInfo info) {} + + virtual AudioInfo audioInfo() { + AudioInfo info; + return info; + } + virtual operator bool() { return false; } + virtual int readStream(Stream &in) { return 0; }; + + // just output silence + virtual size_t write(const void *in_ptr, size_t in_size) { + memset((void *)in_ptr, 0, in_size); + return in_size; + } + + virtual const char *mime() { return nullptr; } +}; + +/** + * @brief A Streaming Decoder where we provide both the input and output + * as streams. + * @ingroup codecs + * @author Phil Schatzmann + * @copyright GPLv3 + */ +class StreamingDecoder { + public: + /// Starts the processing + virtual void begin() = 0; + + /// Releases the reserved memory + virtual void end() = 0; + + /// Defines the output Stream + virtual void setOutput(Print &outStream) = 0; + + /// Register Output Stream to be notified about changes + virtual void setNotifyAudioChange(AudioInfoSupport &bi) = 0; + + /// Defines the output streams and register to be notified + virtual void setOutput(AudioStream &out_stream) { + Print *p_print = &out_stream; + setOutput(*p_print); + setNotifyAudioChange(out_stream); + } + + /// Defines the output streams and register to be notified + virtual void setOutput(AudioOutput &out_stream) { + Print *p_print = &out_stream; + setOutput(*p_print); + setNotifyAudioChange(out_stream); + } + + /// Defines the input data stream + virtual void setInputStream(Stream &inStream) = 0; + + /// Provides the last available MP3FrameInfo + virtual AudioInfo audioInfo() = 0; + + /// checks if the class is active + virtual operator bool() = 0; + + /// Process a single read operation - to be called in the loop + virtual bool copy() = 0; + + protected: + virtual size_t readBytes(uint8_t *buffer, size_t len) = 0; +}; + +/** + * @brief A more natural Print class to process encoded data (aac, wav, + * mp3...). Just define the output and the decoder and write the encoded + * data. + * @ingroup transform + * @author Phil Schatzmann + * @copyright GPLv3 + */ +class EncodedAudioOutput : public AudioStream { + public: + + EncodedAudioOutput() { + TRACED(); + active = false; + } + + EncodedAudioOutput(AudioDecoder *decoder) { + TRACED(); + decoder_ptr = decoder; + writer_ptr = decoder_ptr; + active = false; + } + + EncodedAudioOutput(AudioStream *outputStream, AudioDecoder *decoder) { + TRACED(); + ptr_out = outputStream; + decoder_ptr = decoder; + decoder_ptr->setOutput(*outputStream); + decoder_ptr->setNotifyAudioChange(*outputStream); + writer_ptr = decoder_ptr; + active = false; + } + + EncodedAudioOutput(AudioOutput *outputStream, AudioDecoder *decoder) { + TRACED(); + ptr_out = outputStream; + decoder_ptr = decoder; + decoder_ptr->setOutput(*outputStream); + decoder_ptr->setNotifyAudioChange(*outputStream); + writer_ptr = decoder_ptr; + active = false; + } + + EncodedAudioOutput(Print *outputStream, AudioDecoder *decoder) { + TRACED(); + ptr_out = outputStream; + decoder_ptr = decoder; + decoder_ptr->setOutput(*outputStream); + writer_ptr = decoder_ptr; + active = false; + } + + EncodedAudioOutput(Print *outputStream, AudioEncoder *encoder) { + TRACED(); + ptr_out = outputStream; + encoder_ptr = encoder; + encoder_ptr->setOutput(*outputStream); + writer_ptr = encoder_ptr; + active = false; + } + + EncodedAudioOutput(AudioOutput *outputStream, AudioEncoder *encoder) { + TRACED(); + ptr_out = outputStream; + encoder_ptr = encoder; + encoder_ptr->setOutput(*outputStream); + decoder_ptr->setNotifyAudioChange(*outputStream); + writer_ptr = encoder_ptr; + active = false; + } + + EncodedAudioOutput(AudioStream *outputStream, AudioEncoder *encoder) { + TRACED(); + ptr_out = outputStream; + encoder_ptr = encoder; + encoder_ptr->setOutput(*outputStream); + decoder_ptr->setNotifyAudioChange(*outputStream); + writer_ptr = encoder_ptr; + active = false; + } + + + /// Define object which need to be notified if the basinfo is changing + void setNotifyAudioChange(AudioInfoSupport &bi) override { + TRACEI(); + decoder_ptr->setNotifyAudioChange(bi); + } + + AudioInfo defaultConfig() { + AudioInfo cfg; + cfg.channels = 2; + cfg.sample_rate = 44100; + cfg.bits_per_sample = 16; + return cfg; + } + + virtual void setAudioInfo(AudioInfo info) override { + TRACED(); + if (this->info != info && info.channels!=0 && info.sample_rate!=0) { + this->info = info; + AudioStream::setAudioInfo(info); + decoder_ptr->setAudioInfo(info); + encoder_ptr->setAudioInfo(info); + } + } + + /// Defines the output + void setOutput(Print *outputStream) { + ptr_out = outputStream; + if (decoder_ptr != nullptr) { + decoder_ptr->setOutput(*ptr_out); + } + if (encoder_ptr != nullptr) { + encoder_ptr->setOutput(*ptr_out); + } + } + + /// The same as setOutput + void setStream(Print *outputStream) { setOutput(outputStream); } + + void setEncoder(AudioEncoder *encoder) { + if (encoder == nullptr) { + encoder = CodecNOP::instance(); + } + encoder_ptr = encoder; + writer_ptr = encoder; + if (ptr_out != nullptr) { + encoder_ptr->setOutput(*ptr_out); + } + } + + void setDecoder(AudioDecoder *decoder) { + if (decoder == nullptr) { + decoder = CodecNOP::instance(); + } + decoder_ptr = decoder; + writer_ptr = decoder; + if (ptr_out != nullptr) { + decoder_ptr->setOutput(*ptr_out); + } + } + + /// Starts the processing - sets the status to active + bool begin() override { + TRACED(); + + if (!active) { + const CodecNOP *nop = CodecNOP::instance(); + if (decoder_ptr != nop || encoder_ptr != nop) { + active = true; + decoder_ptr->begin(); + encoder_ptr->begin(); + } else { + LOGW("no decoder or encoder defined"); + } + } + return active; + } + + /// Starts the processing - sets the status to active + bool begin(AudioInfo cfg) { + TRACED(); + info = cfg; + const CodecNOP *nop = CodecNOP::instance(); + if (decoder_ptr != nop || encoder_ptr != nop) { + // some decoders need this - e.g. opus + decoder_ptr->begin(info); + encoder_ptr->begin(info); + active = true; + } else { + LOGW("no decoder or encoder defined"); + } + return active; + } + + /// Ends the processing + void end() override { + TRACEI(); + decoder_ptr->end(); + encoder_ptr->end(); + active = false; + } + + /// encodeor decode the data + virtual size_t write(const uint8_t *data, size_t len) override { + LOGD("EncodedAudioOutput::write: %zu", len); + if (len == 0) { + LOGI("write: %d", 0); + return 0; + } + + if (writer_ptr == nullptr || data == nullptr) { + LOGE("NPE"); + return 0; + } + + size_t result = writer_ptr->write(data, len); + return result; + } + + int availableForWrite() override { return ptr_out->availableForWrite(); } + + /// Returns true if status is active and we still have data to be processed + operator bool() { return active; } + + /// Provides the initialized decoder + AudioDecoder &decoder() { return *decoder_ptr; } + + /// Provides the initialized encoder + AudioEncoder &encoder() { return *encoder_ptr; } + + protected: + AudioInfo info; + AudioDecoder *decoder_ptr = CodecNOP::instance(); // decoder + AudioEncoder *encoder_ptr = CodecNOP::instance(); // decoder + AudioWriter *writer_ptr = nullptr; + Print *ptr_out = nullptr; + bool active; +}; + +// legacy name +using EncodedAudioPrint = EncodedAudioOutput; + +/** + * @brief A more natural Stream class to process encoded data (aac, wav, + * mp3...) which also supports the decoding by calling readBytes(). + * @ingroup transform + * @author Phil Schatzmann + * @copyright GPLv3 + */ +class EncodedAudioStream : public EncodedAudioOutput { + public: + + EncodedAudioStream() : EncodedAudioOutput() {} + + EncodedAudioStream(AudioStream *ioStream, AudioDecoder *decoder) + : EncodedAudioOutput(ioStream, decoder) { + // the indicated stream can be used as input + setStream(ioStream); + } + + EncodedAudioStream(Stream *ioStream, AudioDecoder *decoder) + : EncodedAudioOutput((Print *)ioStream, decoder) { + // the indicated stream can be used as input + setStream(ioStream); + } + + EncodedAudioStream(AudioDecoder *decoder) : EncodedAudioOutput() { + decoder_ptr = decoder; + writer_ptr = decoder_ptr; + active = false; + } + + EncodedAudioStream(AudioOutput *outputStream, AudioDecoder *decoder) + : EncodedAudioOutput(outputStream, decoder) {} + + EncodedAudioStream(Print *outputStream, AudioDecoder *decoder) + : EncodedAudioOutput(outputStream, decoder) {} + + + EncodedAudioStream(Print *outputStream, AudioEncoder *encoder) + : EncodedAudioOutput(outputStream, encoder) {} + + + /// Same as setStream() + void setInput(Stream *ioStream) { setStream(ioStream); } + + /// Defines the input/output stream for decoding + void setStream(Stream *ioStream) { + TRACED(); + EncodedAudioOutput::setStream(ioStream); + p_stream = ioStream; + } + + void setEncoder(AudioEncoder *encoder) { + EncodedAudioOutput::setEncoder(encoder); + is_setup = false; + } + + void setDecoder(AudioDecoder *decoder) { + EncodedAudioOutput::setDecoder(decoder); + is_setup = false; + } + + /// @brief Defines the buffer size + void resize(int size) { decoded_buffer.resize(size); } + + /// @brief setup default size for buffer + void resize() { resize(1024 * 10); } + + int available() override { + if (p_stream == nullptr) return 0; + decode(reqested_bytes); + return decoded_buffer.available(); + } + + size_t readBytes(uint8_t *buffer, size_t length) override { + LOGD("EncodedAudioStream::readBytes: %d", (int)length); + if (p_stream == nullptr) { + TRACEE(); + return 0; + } + decode(reqested_bytes); + return decoded_buffer.readArray(buffer, length); + } + + protected: + RingBuffer decoded_buffer{0}; + QueueStream queue_stream{decoded_buffer}; + Vector copy_buffer{DEFAULT_BUFFER_SIZE}; + Stream *p_stream = nullptr; + AudioWriter *p_write = nullptr; + int reqested_bytes = DEFAULT_BUFFER_SIZE; + bool is_setup = false; + int max_read_count = 5; + + // Fill the decoded_buffer so that we have data for readBytes call + void decode(int requestedBytes) { + TRACED(); + // setup buffer once + setupOnce(); + + // fill decoded_buffer if we do not have enough data + if (p_stream->available() > 0 && + decoded_buffer.available() < reqested_bytes) { + for (int j = 0; j < max_read_count; j++) { + int bytes_read = + p_stream->readBytes(copy_buffer.data(), DEFAULT_BUFFER_SIZE); + LOGD("bytes_read: %d", bytes_read); + int result = writer_ptr->write(copy_buffer.data(), bytes_read); + if (p_stream->available() == 0 || + decoded_buffer.available() >= reqested_bytes) { + break; + } + } + } + LOGD("available decoded data %d", decoded_buffer.available()); + } + + void setupOnce() { + if (!is_setup) { + is_setup = true; + LOGI("Setup reading support"); + resize(); + // make sure the result goes to out_stream + writer_ptr->setOutput(queue_stream); + queue_stream.begin(); + } + } +}; + +/** + * @brief Adapter class which lets an AudioWriter behave like a Print + * @author Phil Schatzmann + * @copyright GPLv3 + * + */ + +class AudioWriterToAudioOutput : public AudioOutputAdapter { + public: + void setWriter(AudioWriter *writer) { p_writer = writer; } + size_t write(const uint8_t *in_ptr, size_t in_size) { + return p_writer->write(in_ptr, in_size); + }; + + protected: + AudioWriter *p_writer = nullptr; +}; + +/** + * @brief ContainerTarget: forwards requests to both the output and the + * encoder/decoder and sets up the output chain for Containers. We also + * manage the proper sequence of the output classes + * @author Phil Schatzmann + * @copyright GPLv3 + */ +class ContainerTarget { + public: + virtual bool begin() = 0; + virtual void end() = 0; + virtual void setAudioInfo(AudioInfo info) { + if (this->info != info && info.channels!=0 && info.sample_rate!=0) { + this->info = info; + if (p_writer1 != nullptr) p_writer1->setAudioInfo(info); + if (p_writer2 != nullptr) p_writer2->setAudioInfo(info); + } + } + virtual size_t write(uint8_t *data, size_t size) = 0; + + protected: + AudioInfo info; + AudioWriter *p_writer1 = nullptr; + AudioWriter *p_writer2 = nullptr; + AudioWriterToAudioOutput print2; + bool active = false; +}; + +class ContainerTargetPrint : public ContainerTarget { + public: + void setupOutput(AudioWriter *writer1, AudioWriter *writer2, Print &print) { + p_print = &print; + p_writer1 = writer1; + p_writer2 = writer2; + print2.setWriter(p_writer2); + } + + void setupOutput(AudioWriter *writer1, Print &print) { + p_print = &print; + p_writer1 = writer1; + } + + virtual bool begin() { + if (!active) { + active = true; + if (p_writer2 != nullptr) { + p_writer1->setOutput(print2); + p_writer2->setOutput(*p_print); + p_writer1->begin(); + p_writer2->begin(); + } else { + p_writer1->setOutput(*p_print); + p_writer1->begin(); + } + } + return true; + } + virtual void end() { + if (active) { + if (p_writer1 != nullptr) p_writer1->end(); + if (p_writer2 != nullptr) p_writer2->end(); + } + active = false; + } + virtual size_t write(uint8_t *data, size_t size) { + TRACED(); + return p_writer1->write(data, size); + } + + protected: + Print *p_print = nullptr; + AudioWriterToAudioOutput print2; +}; + +} // namespace audio_tools diff --git a/src/AudioTools/AudioCodecs/AudioFormat.h b/src/AudioCodecs/AudioFormat.h similarity index 100% rename from src/AudioTools/AudioCodecs/AudioFormat.h rename to src/AudioCodecs/AudioFormat.h diff --git a/src/AudioTools/AudioCodecs/CodecAACFDK.h b/src/AudioCodecs/CodecAACFDK.h similarity index 82% rename from src/AudioTools/AudioCodecs/CodecAACFDK.h rename to src/AudioCodecs/CodecAACFDK.h index cfbbb41b7d..6183fbe027 100644 --- a/src/AudioTools/AudioCodecs/CodecAACFDK.h +++ b/src/AudioCodecs/CodecAACFDK.h @@ -1,6 +1,6 @@ #pragma once -#include "AudioTools/AudioCodecs/AudioCodecsBase.h" +#include "AudioCodecs/AudioEncoded.h" #include "AACDecoderFDK.h" #include "AACEncoderFDK.h" @@ -21,12 +21,12 @@ AudioInfoSupport *audioChangeFDK = nullptr; */ class AACDecoderFDK : public AudioDecoder { public: - AACDecoderFDK(int output_buffer_size = FDK_OUT_BUFFER_DEFAULT_SIZE){ + AACDecoderFDK(){ TRACED(); - dec = new aac_fdk::AACDecoderFDK(output_buffer_size); + dec = new aac_fdk::AACDecoderFDK(); } - AACDecoderFDK(Print &out_stream, int output_buffer_size = 2048){ + AACDecoderFDK(Print &out_stream, int output_buffer_size=2048){ TRACED(); dec = new aac_fdk::AACDecoderFDK(out_stream, output_buffer_size); } @@ -36,17 +36,17 @@ class AACDecoderFDK : public AudioDecoder { } /// Defines the output stream - void setOutput(Print &out_stream) override { + void setOutput(Print &out_stream){ dec->setOutput(out_stream); } - bool begin() override { - return dec->begin(TT_MP4_ADTS, 1); + void begin(){ + dec->begin(TT_MP4_ADTS, 1); } // opens the decoder - bool begin(TRANSPORT_TYPE transportType, UINT nrOfLayers){ - return dec->begin(transportType, nrOfLayers); + void begin(TRANSPORT_TYPE transportType, UINT nrOfLayers){ + dec->begin(transportType, nrOfLayers); } /** @@ -61,8 +61,8 @@ class AACDecoderFDK : public AudioDecoder { } // write AAC data to be converted to PCM data - virtual size_t write(const uint8_t *data, size_t len) override { - return dec->write(data, len); + virtual size_t write(const void *in_ptr, size_t in_size) { + return dec->write(in_ptr, in_size); } // provides detailed information about the stream @@ -71,7 +71,7 @@ class AACDecoderFDK : public AudioDecoder { } // provides common information - AudioInfo audioInfo() override { + AudioInfo audioInfo() { AudioInfo result; CStreamInfo i = audioInfoEx(); result.channels = i.numChannels; @@ -81,12 +81,12 @@ class AACDecoderFDK : public AudioDecoder { } // release the resources - void end() override { + void end(){ TRACED(); dec->end(); } - virtual operator bool() override { + virtual operator bool() { return (bool)*dec; } @@ -105,7 +105,7 @@ class AACDecoderFDK : public AudioDecoder { } } - void addNotifyAudioChange(AudioInfoSupport &bi) override { + virtual void setNotifyAudioChange(AudioInfoSupport &bi) { audioChangeFDK = &bi; // register audio change handler dec->setInfoCallback(audioChangeCallback); @@ -141,12 +141,11 @@ class AACEncoderFDK : public AudioEncoder { delete enc; } - /// Defines the output - void setOutput(Print &out_stream) override { + void setOutput(Print &out_stream){ enc->setOutput(out_stream); } - /** @brief Total encoder bitrate. This parameter is + /*!< Total encoder bitrate. This parameter is mandatory and interacts with ::AACENC_BITRATEMODE. - CBR: Bitrate in bits/second. - VBR: Variable bitrate. Bitrate argument will @@ -155,7 +154,7 @@ class AACEncoderFDK : public AudioEncoder { enc->setBitrate(bitrate); } - /** @brief Audio object type. See ::AUDIO_OBJECT_TYPE in FDK_audio.h. + /*!< Audio object type. See ::AUDIO_OBJECT_TYPE in FDK_audio.h. - 2: MPEG-4 AAC Low Complexity. - 5: MPEG-4 AAC Low Complexity with Spectral Band Replication (HE-AAC). @@ -180,7 +179,7 @@ class AACEncoderFDK : public AudioEncoder { enc->setAudioObjectType(aot); } - /** @brief This parameter controls the use of the afterburner feature. + /*!< This parameter controls the use of the afterburner feature. The afterburner is a type of analysis by synthesis algorithm which increases the audio quality but also the required processing power. It is recommended to always activate this if @@ -195,7 +194,7 @@ class AACEncoderFDK : public AudioEncoder { enc->setAfterburner(afterburner); } - /** @brief Configure SBR independently of the chosen Audio + /*!< Configure SBR independently of the chosen Audio Object Type ::AUDIO_OBJECT_TYPE. This parameter is for ELD audio object type only. - -1: Use ELD SBR auto configurator (default). @@ -205,7 +204,7 @@ class AACEncoderFDK : public AudioEncoder { enc->setSpectralBandReplication(eld_sbr); } - /** @brief Bitrate mode. Configuration can be different + /*!< Bitrate mode. Configuration can be different kind of bitrate configurations: - 0: Constant bitrate, use bitrate according to ::AACENC_BITRATE. (default) Within none @@ -237,9 +236,8 @@ class AACEncoderFDK : public AudioEncoder { } /// Defines the Audio Info - void setAudioInfo(AudioInfo from) override { + virtual void setAudioInfo(AudioInfo from) { TRACED(); - AudioEncoder::setAudioInfo(from); aac_fdk::AudioInfo info; info.channels = from.channels; info.sample_rate = from.sample_rate; @@ -253,9 +251,9 @@ class AACEncoderFDK : public AudioEncoder { * @param info * @return int */ - virtual bool begin(AudioInfo info) override { + virtual void begin(AudioInfo info) { TRACED(); - return enc->begin(info.channels,info.sample_rate, info.bits_per_sample); + enc->begin(info.channels,info.sample_rate, info.bits_per_sample); } /** @@ -266,25 +264,24 @@ class AACEncoderFDK : public AudioEncoder { * @param input_bits_per_sample * @return int 0 => ok; error with negative number */ - virtual bool begin(int input_channels=2, int input_sample_rate=44100, int input_bits_per_sample=16) { + virtual void begin(int input_channels=2, int input_sample_rate=44100, int input_bits_per_sample=16) { TRACED(); - return enc->begin(input_channels,input_sample_rate, input_bits_per_sample); + enc->begin(input_channels,input_sample_rate, input_bits_per_sample); } // starts the processing - bool begin() override { + void begin() { enc->begin(); - return true; } // convert PCM data to AAC - size_t write(const uint8_t *data, size_t len) override { - LOGD("write %d bytes", (int)len); - return enc->write((uint8_t*)data, len); + size_t write(const void *in_ptr, size_t in_size){ + LOGD("write %d bytes", (int)in_size); + return enc->write((uint8_t*)in_ptr, in_size); } // release resources - void end() override { + void end(){ TRACED(); enc->end(); } @@ -301,11 +298,11 @@ class AACEncoderFDK : public AudioEncoder { return enc; } - const char *mime() override { + const char *mime() { return "audio/aac"; } - operator bool() override { + operator bool(){ return (bool) *enc; } diff --git a/src/AudioTools/AudioCodecs/CodecAACHelix.h b/src/AudioCodecs/CodecAACHelix.h similarity index 65% rename from src/AudioTools/AudioCodecs/CodecAACHelix.h rename to src/AudioCodecs/CodecAACHelix.h index 85ab6693d8..eb7ca825d0 100644 --- a/src/AudioTools/AudioCodecs/CodecAACHelix.h +++ b/src/AudioCodecs/CodecAACHelix.h @@ -1,9 +1,7 @@ #pragma once -#include "AudioTools/AudioCodecs/AudioCodecsBase.h" -#ifndef HELIX_PRINT -# define HELIX_PRINT -#endif +//#include "Stream.h" +#include "AudioCodecs/AudioEncoded.h" #include "AACDecoderHelix.h" namespace audio_tools { @@ -19,12 +17,11 @@ namespace audio_tools { class AACDecoderHelix : public AudioDecoder { public: - AACDecoderHelix() { + AACDecoderHelix(bool raw=false) { TRACED(); aac = new libhelix::AACDecoderHelix(); - if (aac!=nullptr){ - aac->setReference(this); - } else { + setRaw(raw); + if (aac==nullptr){ LOGE("Not enough memory for libhelix"); } } @@ -36,9 +33,7 @@ class AACDecoderHelix : public AudioDecoder { AACDecoderHelix(Print &out_stream){ TRACED(); aac = new libhelix::AACDecoderHelix(out_stream); - if (aac!=nullptr){ - aac->setReference(this); - } else { + if (aac==nullptr){ LOGE("Not enough memory for libhelix"); } } @@ -53,12 +48,10 @@ class AACDecoderHelix : public AudioDecoder { AACDecoderHelix(Print &out_stream, AudioInfoSupport &bi){ TRACED(); aac = new libhelix::AACDecoderHelix(out_stream); - if (aac!=nullptr){ - aac->setReference(this); - } else { + if (aac==nullptr){ LOGE("Not enough memory for libhelix"); } - addNotifyAudioChange(bi); + setNotifyAudioChange(bi); } /** @@ -70,30 +63,27 @@ class AACDecoderHelix : public AudioDecoder { if (aac!=nullptr) delete aac; } - // void setRaw(bool flag){ - // if (aac!=nullptr) aac->setRaw(flag); - // } + void setRaw(bool flag){ + if (aac!=nullptr) aac->setRaw(flag); + } /// Defines the output Stream - virtual void setOutput(Print &out_stream) override { + virtual void setOutput(Print &out_stream){ TRACED(); - AudioDecoder::setOutput(out_stream); if (aac!=nullptr) aac->setOutput(out_stream); } /// Starts the processing - bool begin() override { + void begin(){ TRACED(); if (aac!=nullptr) { - //aac->setDelay(CODEC_DELAY_MS); - aac->setInfoCallback(infoCallback, this); + aac->setDelay(CODEC_DELAY_MS); aac->begin(); } - return true; } /// Releases the reserved memory - virtual void end() override { + virtual void end(){ TRACED(); if (aac!=nullptr) aac->end(); } @@ -102,7 +92,7 @@ class AACDecoderHelix : public AudioDecoder { return aac->audioInfo(); } - AudioInfo audioInfo() override{ + virtual AudioInfo audioInfo(){ AudioInfo result; auto i = audioInfoEx(); result.channels = i.nChans; @@ -111,30 +101,13 @@ class AACDecoderHelix : public AudioDecoder { return result; } - void setAudioInfo(AudioInfo info) override { - this->info = info; - if(info_notifications_active){ - notifyAudioChange(info); - } - } - /// Write AAC data to decoder - size_t write(const uint8_t* data, size_t len) override { - LOGD("AACDecoderHelix::write: %d", (int)len); - if (aac==nullptr) return 0; - int open = len; - int processed = 0; - uint8_t *data8 = (uint8_t*)data; - while(open>0){ - int act_write = aac->write(data8+processed, min(open, DEFAULT_BUFFER_SIZE)); - open -= act_write; - processed += act_write; - } - return processed; + size_t write(const void* aac_data, size_t len) { + return aac==nullptr ? 0 : aac->write(aac_data, len); } /// checks if the class is active - virtual operator bool() override { + virtual operator bool(){ return aac!=nullptr && (bool)*aac; } @@ -142,18 +115,23 @@ class AACDecoderHelix : public AudioDecoder { // aac->flush(); } + /// Defines the callback object to which the Audio information change is provided + virtual void setNotifyAudioChange(AudioInfoSupport &bi){ + TRACED(); + audioChangeAACHelix = &bi; + if (aac!=nullptr) aac->setInfoCallback(infoCallback, this); + } + /// notifies the subscriber about a change static void infoCallback(_AACFrameInfo &i, void* ref){ AACDecoderHelix *p_helix = (AACDecoderHelix *)ref; - if (p_helix!=nullptr){ + if (p_helix!=nullptr && p_helix->audioChangeAACHelix!=nullptr){ TRACED(); AudioInfo baseInfo; baseInfo.channels = i.nChans; baseInfo.sample_rate = i.sampRateOut; baseInfo.bits_per_sample = i.bitsPerSample; - //p_helix->audioChangeAACHelix->setAudioInfo(baseInfo); - LOGW("sample_rate: %d", i.sampRateOut); - p_helix->setAudioInfo(baseInfo); + p_helix->audioChangeAACHelix->setAudioInfo(baseInfo); } } @@ -167,10 +145,7 @@ class AACDecoderHelix : public AudioDecoder { aac->setMaxFrameSize(len); } - void setAudioInfoNotifications(bool active){ - info_notifications_active = active; - } - +#ifdef HELIX_PCM_CORRECTED /// Provides the maximum pwm buffer size - this is allocated on the heap and you can reduce the heap size my minimizing this value size_t maxPCMSize() { return aac->maxPCMSize(); @@ -180,10 +155,23 @@ class AACDecoderHelix : public AudioDecoder { void setMaxPCMSize(size_t len) { aac->setMaxPCMSize(len); } +#else + /// Provides the maximum pwm buffer size - this is allocated on the heap and you can reduce the heap size my minimizing this value + size_t maxPCMSize() { + return aac->maxPWMSize(); + } + + /// Define your optimized maximum pwm buffer size + void setMaxPCMSize(size_t len) { + aac->setMaxPWMSize(len); + } +#endif protected: libhelix::AACDecoderHelix *aac=nullptr; - bool info_notifications_active = true; + // audio change notification target + AudioInfoSupport *audioChangeAACHelix=nullptr; + }; diff --git a/src/AudioCodecs/CodecADPCM.h b/src/AudioCodecs/CodecADPCM.h new file mode 100644 index 0000000000..2e01106501 --- /dev/null +++ b/src/AudioCodecs/CodecADPCM.h @@ -0,0 +1,187 @@ +#pragma once +#include "ADPCM.h" // https://github.com/pschatzmann/adpcm +#include "AudioCodecs/AudioEncoded.h" + +namespace audio_tools { + +/** + * @brief Decoder for ADPCM. Depends on https://github.com/pschatzmann/adpcm + * @ingroup codecs + * @ingroup decoder + * @author Phil Schatzmann + * @copyright GPLv3 + */ +class ADPCMDecoder : public AudioDecoderExt { + public: + ADPCMDecoder(AVCodecID id, int blockSize = ADAPCM_DEFAULT_BLOCK_SIZE) { + info.sample_rate = 44100; + info.channels = 2; + info.bits_per_sample = 16; + decoder.setCodecID(id); + decoder.setBlockSize(blockSize); + } + + // defines the block size + void setBlockSize(int blockSize) override { + decoder.setBlockSize(blockSize); + } + + void begin() override { + TRACEI(); + current_byte = 0; + decoder.begin(info.sample_rate, info.channels); + LOGI("frameSize: %d", (int)decoder.frameSize()); + block_size = decoder.blockSize(); + assert(block_size > 0); + assert(decoder.frameSize() > 0); + adpcm_block.resize(block_size); + + if (p_notify != nullptr) { + p_notify->setAudioInfo(info); + } + is_started = true; + } + + void end() override { + TRACEI(); + decoder.end(); + adpcm_block.resize(0); + is_started = false; + } + + virtual void setOutput(Print &out_stream) override { p_print = &out_stream; } + + virtual size_t write(const void *input_buffer, size_t length) override { + TRACED(); + + uint8_t *input_buffer8 = (uint8_t *)input_buffer; + LOGD("write: %d", (int)length); + for (int j = 0; j < length; j++) { + decode(input_buffer8[j]); + } + return length; + } + + operator bool() override { return is_started; } + + protected: + adpcm_ffmpeg::ADPCMDecoder decoder; + Vector adpcm_block; + Print *p_print = nullptr; + int current_byte = 0; + int block_size = 0; + bool is_started = false; + + bool decode(uint8_t byte) { + adpcm_block[current_byte++] = byte; + + if (current_byte >= block_size) { + TRACED(); + AVFrame &frame = decoder.decode(&adpcm_block[0], block_size); + // print the result + int16_t *data = (int16_t *)frame.data[0]; + size_t byte_count = frame.nb_samples * sizeof(int16_t) * info.channels; + size_t written = p_print->write((uint8_t *)data, byte_count); + if (written != byte_count) { + LOGE("encode %d->%d", (int)byte_count, (int)written); + } + + // restart from array begin + current_byte = 0; + } + return true; + } +}; + +/** + * @brief Encoder for ADPCM - Depends on https://github.com/pschatzmann/adpcm + * @ingroup codecs + * @ingroup encoder + * @author Phil Schatzmann + * @copyright GPLv3 + */ +class ADPCMEncoder : public AudioEncoderExt { + public: + ADPCMEncoder(AVCodecID id, int blockSize = ADAPCM_DEFAULT_BLOCK_SIZE) { + info.sample_rate = 44100; + info.channels = 2; + info.bits_per_sample = 16; + encoder.setCodecID(id); + encoder.setBlockSize(blockSize); + } + + /// Provides the block size (only available after calling begin) + int blockSize() override { + return encoder.blockSize(); + } + + void begin(AudioInfo info) override { + setAudioInfo(info); + begin(); + } + + void begin() override { + TRACEI(); + encoder.begin(info.sample_rate, info.channels); + LOGI("frameSize: %d", (int)encoder.frameSize()); + assert(info.sample_rate != 0); + assert(encoder.frameSize() != 0); + total_samples = encoder.frameSize()*info.channels; + pcm_block.resize(total_samples); + current_sample = 0; + + is_started = true; + } + + void end() override { + TRACEI(); + pcm_block.resize(0); + encoder.end(); + is_started = false; + } + + const char *mime() override { return "audio/adpcm"; } + + void setAudioInfo(AudioInfo info) override { this->info = info; } + + void setOutput(Print &out_stream) override { p_print = &out_stream; } + + operator bool() override { return is_started; } + + size_t write(const void *in_ptr, size_t in_size) override { + LOGD("write: %d", (int)in_size); + int16_t *data16 = (int16_t *)in_ptr; + for (int j = 0; j < in_size / 2; j++) { + encode(data16[j]); + } + return in_size; + } + + protected: + AudioInfo info; + adpcm_ffmpeg::ADPCMEncoder encoder; + Vector pcm_block; + Print *p_print = nullptr; + bool is_started = false; + int current_sample = 0; + int total_samples=0; + + bool encode(int16_t sample) { + pcm_block[current_sample++] = sample; + if (current_sample >= total_samples) { + TRACED(); + AVPacket &packet = encoder.encode(&pcm_block[0], total_samples); + if (packet.size > 0) { + size_t written = p_print->write(packet.data, packet.size); + if (written != packet.size) { + LOGE("encode %d->%d", (int)packet.size, (int)written); + } + } + // restart from array begin + current_sample = 0; + } + return true; + } +}; + +} // namespace audio_tools \ No newline at end of file diff --git a/src/AudioTools/AudioCodecs/CodecADPCMXQ.h b/src/AudioCodecs/CodecADPCMXQ.h similarity index 90% rename from src/AudioTools/AudioCodecs/CodecADPCMXQ.h rename to src/AudioCodecs/CodecADPCMXQ.h index 6da8f439ca..46c31d58f3 100644 --- a/src/AudioTools/AudioCodecs/CodecADPCMXQ.h +++ b/src/AudioCodecs/CodecADPCMXQ.h @@ -1,5 +1,5 @@ #pragma once -#include "AudioTools/AudioCodecs/AudioCodecsBase.h" +#include "AudioCodecs/AudioEncoded.h" #include "adpcm-lib.h" // https://github.com/pschatzmann/arduino-adpcm-xq #define DEFAULT_NOISE_SHAPING NOISE_SHAPING_OFF @@ -47,7 +47,7 @@ class ADPCMDecoderXQ : public AudioDecoder { /// Defines the noise shaping void setNoiseShaping(ADPCMNoiseShaping ns) { noise_shaping = (int)ns; } - bool begin() override { + void begin() override { TRACEI(); current_byte = 0; if (adpcm_cnxt == nullptr) { @@ -67,8 +67,9 @@ class ADPCMDecoderXQ : public AudioDecoder { adpcm_block.resize(block_size); } - notifyAudioChange(info); - return true; + if (p_notify != nullptr) { + p_notify->setAudioInfo(info); + } } void end() override { @@ -85,17 +86,17 @@ class ADPCMDecoderXQ : public AudioDecoder { operator bool() override { return adpcm_cnxt != nullptr; } - virtual size_t write(const uint8_t *data, size_t len) { - uint8_t *input_buffer8 = (uint8_t *)data; - LOGD("write: %d", (int)len); - for (int j = 0; j < len; j++) { + virtual size_t write(const void *input_buffer, size_t length) { + uint8_t *input_buffer8 = (uint8_t *)input_buffer; + LOGD("write: %d", (int)length); + for (int j = 0; j < length; j++) { adpcm_block[current_byte++] = input_buffer8[j]; if (current_byte == block_size) { decode(current_byte); current_byte = 0; } } - return len; + return length; } protected: @@ -156,7 +157,12 @@ class ADPCMEncoderXQ : public AudioEncoder { /// Defines the noise shaping void setNoiseShaping(ADPCMNoiseShaping ns) { noise_shaping = (int)ns; } - bool begin() override { + void begin(AudioInfo info) { + setAudioInfo(info); + begin(); + } + + void begin() override { TRACEI(); if (block_size_pow2) @@ -171,7 +177,6 @@ class ADPCMEncoderXQ : public AudioEncoder { pcm_block.resize(samples_per_block * info.channels); adpcm_block.resize(block_size); current_sample = 0; - return true; } void end() override { @@ -186,25 +191,28 @@ class ADPCMEncoderXQ : public AudioEncoder { const char *mime() override { return "audio/adpcm"; } + void setAudioInfo(AudioInfo info) override { this->info = info; } + void setOutput(Print &out_stream) override { p_print = &out_stream; } operator bool() override { return adpcm_cnxt != nullptr; } - size_t write(const uint8_t *data, size_t len) override { - LOGD("write: %d", (int)len); - int16_t *input_buffer = (int16_t *)data; + size_t write(const void *in_ptr, size_t in_size) override { + LOGD("write: %d", (int)in_size); + int16_t *input_buffer = (int16_t *)in_ptr; pcm_block_size = samples_per_block * info.channels; - for (int j = 0; j < len / 2; j++) { + for (int j = 0; j < in_size / 2; j++) { pcm_block[current_sample++] = input_buffer[j]; if (current_sample == samples_per_block * info.channels) { encode(); current_sample = 0; } } - return len; + return in_size; } protected: + AudioInfo info; int current_sample = 0; void *adpcm_cnxt = nullptr; Vector pcm_block; diff --git a/src/AudioTools/AudioCodecs/CodecAPTX.h b/src/AudioCodecs/CodecAPTX.h similarity index 88% rename from src/AudioTools/AudioCodecs/CodecAPTX.h rename to src/AudioCodecs/CodecAPTX.h index c7d4bb75de..86adcb2a82 100644 --- a/src/AudioTools/AudioCodecs/CodecAPTX.h +++ b/src/AudioCodecs/CodecAPTX.h @@ -9,8 +9,8 @@ * */ #pragma once -#include "AudioToolsConfig.h" -#include "AudioTools/AudioCodecs/AudioCodecsBase.h" +#include "AudioCodecs/AudioEncoded.h" +#include "AudioBasic/Int24x.h" #include "openaptx.h" namespace audio_tools { @@ -32,12 +32,13 @@ class APTXDecoder : public AudioDecoder { info.bits_per_sample = isHd ? 24 : 16; } - bool begin() override { + void begin() override { TRACEI(); ctx = aptx_init(is_hd); is_first_write = true; - notifyAudioChange(info); - return ctx != nullptr; + if (p_notify != nullptr) { + p_notify->setAudioInfo(info); + } } void end() override { @@ -51,22 +52,22 @@ class APTXDecoder : public AudioDecoder { operator bool() { return ctx != nullptr; } - virtual size_t write(const uint8_t *data, size_t len) { - LOGI("write: %d", len); + virtual size_t write(const void *input_buffer, size_t length) { + LOGI("write: %d", length); bool is_ok = true; size_t dropped; int synced; if (is_first_write) { is_first_write = false; - if (!checkPrefix(data, len)) { + if (!checkPrefix(input_buffer, length)) { return 0; } } - output_buffer.resize(len * 10); + output_buffer.resize(length * 10); memset(output_buffer.data(), 0, output_buffer.size()); - processed = aptx_decode_sync(ctx, (const uint8_t *)data, len, + processed = aptx_decode_sync(ctx, (const uint8_t *)input_buffer, length, output_buffer.data(), output_buffer.size(), &written, &synced, &dropped); @@ -74,14 +75,14 @@ class APTXDecoder : public AudioDecoder { // If we have not decoded all supplied samples then decoding unrecoverable // failed - if (processed != len) { - LOGE("aptX decoding reqested: %d eff: %d", len, processed); + if (processed != length) { + LOGE("aptX decoding reqested: %d eff: %d", length, processed); is_ok = false; } writeData(written, is_ok); - return is_ok ? len : 0; + return is_ok ? length : 0; } protected: @@ -100,7 +101,7 @@ class APTXDecoder : public AudioDecoder { int samples = written / 3; LOGI("written: %d", written); LOGI("samples: %d", samples); - int24_t *p_int24 = (int24_t *)output_buffer.data(); + int24x_t *p_int24 = (int24x_t *)output_buffer.data(); int16_t *p_int16 = (int16_t *)output_buffer.data(); for (int j = 0; j < samples; j++) { p_int16[j] = p_int24[j].getAndScale16(); @@ -193,7 +194,12 @@ class APTXEncoder : public AudioEncoder { info.bits_per_sample = isHd ? 24 : 16; } - bool begin() { + void begin(AudioInfo info) { + setAudioInfo(info); + begin(); + } + + void begin() { TRACEI(); input_buffer.resize(4 * 2); output_buffer.resize(100 * (is_hd ? 6 : 4)); @@ -202,7 +208,6 @@ class APTXEncoder : public AudioEncoder { LOGI("output_buffer.size: %d", output_buffer.size()); LOGI("is_hd: %s", is_hd ? "true" : "false"); ctx = aptx_init(is_hd); - return ctx!=nullptr; } virtual void end() { @@ -227,7 +232,7 @@ class APTXEncoder : public AudioEncoder { virtual const char *mime() { return "audio/aptx"; } virtual void setAudioInfo(AudioInfo info) { - AudioEncoder::setAudioInfo(info); + this->info = info; switch (info.bits_per_sample) { case 16: is_hd = false; @@ -244,14 +249,14 @@ class APTXEncoder : public AudioEncoder { operator bool() { return ctx != nullptr; } - virtual size_t write(const uint8_t *data, size_t len) { - LOGI("write: %d", len); + virtual size_t write(const void *in_ptr, size_t in_size) { + LOGI("write: %d", in_size); if (ctx == nullptr) return 0; size_t output_written = 0; // process all bytes - int16_t *in_ptr16 = (int16_t *)data; - int in_samples = len / 2; + int16_t *in_ptr16 = (int16_t *)in_ptr; + int in_samples = in_size / 2; for (int j = 0; j < in_samples; j++) { input_buffer[input_pos++].setAndScale16(in_ptr16[j]); @@ -284,12 +289,13 @@ class APTXEncoder : public AudioEncoder { } } - return len; + return in_size; } protected: bool is_hd; - Vector input_buffer{4 * 2}; + AudioInfo info; + Vector input_buffer{4 * 2}; Vector output_buffer; int input_pos = 0; int output_pos = 0; diff --git a/src/AudioTools/AudioCodecs/CodecBase64.h b/src/AudioCodecs/CodecBase64.h similarity index 92% rename from src/AudioTools/AudioCodecs/CodecBase64.h rename to src/AudioCodecs/CodecBase64.h index 4c6de4d020..625d538f26 100644 --- a/src/AudioTools/AudioCodecs/CodecBase64.h +++ b/src/AudioCodecs/CodecBase64.h @@ -1,7 +1,8 @@ #pragma once -#include "AudioTools/AudioCodecs/AudioCodecsBase.h" +#include "AudioBasic/Str.h" +#include "AudioCodecs/AudioEncoded.h" namespace audio_tools { @@ -58,11 +59,10 @@ class DecoderBase64 : public AudioDecoder { /// We expect new lines to delimit the individual lines void setNewLine(Base46Logic logic) { newline_logic = logic; } - bool begin() override { + void begin() override { TRACED(); is_valid = newline_logic == NoCR; active = true; - return true; } void end() override { @@ -77,10 +77,10 @@ class DecoderBase64 : public AudioDecoder { buffer.resize(0); } - size_t write(const uint8_t *data, size_t len) override { + size_t write(const void *data, size_t byteCount) override { if (p_print == nullptr) return 0; TRACED(); - addToBuffer((uint8_t *)data, len); + addToBuffer((uint8_t *)data, byteCount); int decode_size = 4; // maybe we should increase this ? while (buffer.available() >= decode_size) { uint8_t tmp[decode_size]; @@ -88,7 +88,7 @@ class DecoderBase64 : public AudioDecoder { decodeLine(tmp, decode_size); } - return len; + return byteCount; } operator bool() override { return active; } @@ -194,36 +194,44 @@ class EncoderBase64 : public AudioEncoder { /// Provides "text/base64" const char *mime() override { return "text/base64"; } + /// We actually do nothing with this + virtual void setAudioInfo(AudioInfo from) override { info = from; } + /// We add a new line after each write void setNewLine(Base46Logic flag) { newline_logic = flag; } + virtual void begin(AudioInfo cfg) override { + setAudioInfo(cfg); + begin(); + } + /// starts the processing using the actual RAWAudioInfo - virtual bool begin() override { + virtual void begin() override { is_open = true; frame_size = info.bits_per_sample * info.channels / 8; if (newline_logic != NoCR) { if (frame_size==0){ - LOGW("AudioInfo not defined"); + LOGE("AudioInfo not defined"); // assume frame size frame_size = 4; } p_print->write('\n'); flush(); } - return true; } /// stops the processing void end() override { is_open = false; } /// Writes PCM data to be encoded as RAW - virtual size_t write(const uint8_t *data, size_t len) override { + virtual size_t write(const void *binary, size_t len) override { LOGD("EncoderBase64::write: %d", (int)len); + uint8_t *data = (uint8_t *)binary; switch (newline_logic) { case NoCR: case CRforWrite: - encodeLine(data, len); + encodeLine((uint8_t *)binary, len); break; case CRforFrame: { int frames = len / frame_size; @@ -264,7 +272,7 @@ class EncoderBase64 : public AudioEncoder { #endif } - void encodeLine(const uint8_t *data, size_t input_length) { + void encodeLine(uint8_t *data, size_t input_length) { LOGD("EncoderBase64::encodeLine: %d", (int)input_length); int output_length = 4 * ((input_length + 2) / 3); if (ret.size() < output_length + 1) { diff --git a/src/AudioTools/AudioCodecs/CodecBasic.h b/src/AudioCodecs/CodecBasic.h similarity index 80% rename from src/AudioTools/AudioCodecs/CodecBasic.h rename to src/AudioCodecs/CodecBasic.h index 67bc0ea975..3286fb9010 100644 --- a/src/AudioTools/AudioCodecs/CodecBasic.h +++ b/src/AudioCodecs/CodecBasic.h @@ -1,7 +1,7 @@ #pragma once -#include "AudioTools/AudioCodecs/AudioCodecsBase.h" -#include "AudioTools/AudioCodecs/CodecG7xx.h" +#include "AudioCodecs/AudioEncoded.h" +#include "AudioCodecs/CodecG7xx.h" namespace audio_tools { @@ -43,7 +43,7 @@ class DecoderBasic : public AudioDecoder { DecoderBasic(Print &out_stream, AudioInfoSupport &bi) { TRACED(); setOutput(out_stream); - addNotifyAudioChange(bi); + setNotifyAudioChange(bi); } /// Defines the output Stream @@ -51,26 +51,24 @@ class DecoderBasic : public AudioDecoder { decoder.setOutput(out_stream); } - void addNotifyAudioChange(AudioInfoSupport &bi) override { - decoder.addNotifyAudioChange(bi); + void setNotifyAudioChange(AudioInfoSupport &bi) override { + decoder.setNotifyAudioChange(bi); } AudioInfo audioInfo() override { return decoder.audioInfo(); } - bool begin(AudioInfo info) { - decoder.setAudioInfo(info); - return decoder.begin(); - } + void begin(AudioInfo info) { decoder.begin(info); } - bool begin() override { + void begin() override { TRACED(); - return decoder.begin(); + decoder.begin(); + ; } void end() override { decoder.end(); } - virtual size_t write(const uint8_t *data, size_t len) override { - return decoder.write((uint8_t *)data, len); + virtual size_t write(const void *in_ptr, size_t in_size) override { + return decoder.write((uint8_t *)in_ptr, in_size); } virtual operator bool() override { return decoder; } @@ -105,18 +103,17 @@ class EncoderBasic : public AudioEncoder { /// We actually do nothing with this virtual void setAudioInfo(AudioInfo from) override { - AudioEncoder::setAudioInfo(from); encoder.setAudioInfo(from); } /// starts the processing using the actual RAWAudioInfo - bool begin() override { return encoder.begin(); } + virtual void begin() override { encoder.begin(); } /// stops the processing void end() override { encoder.end(); } /// Writes PCM data to be encoded as RAW - virtual size_t write(const uint8_t *in_ptr, size_t in_size) override { + virtual size_t write(const void *in_ptr, size_t in_size) override { return encoder.write((uint8_t *)in_ptr, in_size); } diff --git a/src/AudioTools/AudioCodecs/CodecCodec2.h b/src/AudioCodecs/CodecCodec2.h similarity index 91% rename from src/AudioTools/AudioCodecs/CodecCodec2.h rename to src/AudioCodecs/CodecCodec2.h index 2db2a16c54..9a77c74c05 100644 --- a/src/AudioTools/AudioCodecs/CodecCodec2.h +++ b/src/AudioCodecs/CodecCodec2.h @@ -17,7 +17,7 @@ #pragma once -#include "AudioTools/AudioCodecs/AudioCodecsBase.h" +#include "AudioCodecs/AudioEncoded.h" #include "codec2.h" @@ -72,21 +72,26 @@ class Codec2Decoder : public AudioDecoder { int bitsPerSecond() { return bits_per_second; } - virtual bool begin() { + virtual void begin(AudioInfo cfg) { + setAudioInfo(cfg); + begin(); + } + + virtual void begin() { TRACEI(); int mode = getCodec2Mode(bits_per_second); if (mode == -1) { LOGE("invalid bits_per_second") - return false; + return; } if (info.channels != 1) { LOGE("Only 1 channel supported") - return false; + return; } if (info.bits_per_sample != 16) { LOGE("Only 16 bps are supported") - return false; + return; } if (info.sample_rate != 8000) { LOGW("Sample rate should be 8000: %d", info.sample_rate); @@ -95,7 +100,7 @@ class Codec2Decoder : public AudioDecoder { p_codec2 = codec2_create(mode); if (p_codec2 == nullptr) { LOGE("codec2_create"); - return false; + return; } result_buffer.resize(bytesCompressed()); @@ -104,11 +109,12 @@ class Codec2Decoder : public AudioDecoder { assert(input_buffer.size()>0); assert(result_buffer.size()>0); - notifyAudioChange(info); + if (p_notify != nullptr) { + p_notify->setAudioInfo(info); + } LOGI("bytesCompressed:%d", bytesCompressed()); LOGI("bytesUncompressed:%d", bytesUncompressed()); is_active = true; - return true; } int bytesCompressed() { @@ -132,19 +138,19 @@ class Codec2Decoder : public AudioDecoder { operator bool() { return is_active; } - size_t write(const uint8_t *data, size_t len) override { - LOGD("write: %d", len); + size_t write(const void *data, size_t length) override { + LOGD("write: %d", length); if (!is_active) { LOGE("inactive"); return 0; } uint8_t *p_byte = (uint8_t *)data; - for (int j = 0; j < len; j++) { + for (int j = 0; j < length; j++) { processByte(p_byte[j]); } - return len; + return length; } protected: @@ -199,6 +205,11 @@ class Codec2Encoder : public AudioEncoder { int bitsPerSecond() { return bits_per_second; } + void begin(AudioInfo bi) { + setAudioInfo(bi); + begin(); + } + int bytesCompressed() { return p_codec2 != nullptr ? codec2_bytes_per_frame(p_codec2) : 0; } @@ -209,21 +220,21 @@ class Codec2Encoder : public AudioEncoder { : 0; } - bool begin() { + void begin() { TRACEI(); int mode = getCodec2Mode(bits_per_second); if (mode == -1) { LOGE("invalid bits_per_second") - return false; + return; } if (info.channels != 1) { LOGE("Only 1 channel supported") - return false; + return; } if (info.bits_per_sample != 16) { LOGE("Only 16 bps are supported") - return false; + return; } if (info.sample_rate != 8000) { LOGW("Sample rate should be 8000: %d", info.sample_rate); @@ -232,7 +243,7 @@ class Codec2Encoder : public AudioEncoder { p_codec2 = codec2_create(mode); if (p_codec2 == nullptr) { LOGE("codec2_create"); - return false; + return; } input_buffer.resize(bytesCompressed()); @@ -242,7 +253,6 @@ class Codec2Encoder : public AudioEncoder { LOGI("bytesCompressed:%d", bytesCompressed()); LOGI("bytesUncompressed:%d", bytesUncompressed()); is_active = true; - return true; } virtual void end() { @@ -253,11 +263,13 @@ class Codec2Encoder : public AudioEncoder { virtual const char *mime() { return "audio/codec2"; } + virtual void setAudioInfo(AudioInfo cfg) { this->info = cfg; } + virtual void setOutput(Print &out_stream) { p_print = &out_stream; } operator bool() { return is_active; } - size_t write(const uint8_t *in_ptr, size_t in_size) override { + size_t write(const void *in_ptr, size_t in_size) override { LOGD("write: %d", in_size); if (!is_active) { LOGE("inactive"); @@ -272,6 +284,7 @@ class Codec2Encoder : public AudioEncoder { } protected: + AudioInfo info; Print *p_print = nullptr; struct CODEC2 *p_codec2 = nullptr; bool is_active = false; diff --git a/src/AudioTools/AudioCodecs/CodecCopy.h b/src/AudioCodecs/CodecCopy.h similarity index 56% rename from src/AudioTools/AudioCodecs/CodecCopy.h rename to src/AudioCodecs/CodecCopy.h index 469566651d..aa0368b545 100644 --- a/src/AudioTools/AudioCodecs/CodecCopy.h +++ b/src/AudioCodecs/CodecCopy.h @@ -1,15 +1,14 @@ #pragma once -#include "AudioTools/AudioCodecs/AudioCodecsBase.h" -#if defined(ARDUINO) && !defined(IS_MIN_DESKTOP) +#include "AudioCodecs/AudioEncoded.h" +#ifdef ARDUINO #include "Print.h" #endif namespace audio_tools { /** - * @brief Dummy Decoder which just copies the provided data to the output. - * You can define if it is PCM data. + * @brief Dummy Decoder which just copies the provided data to the output * @ingroup codecs * @ingroup decoder * @author Phil Schatzmann @@ -17,8 +16,9 @@ namespace audio_tools { */ class CopyDecoder : public AudioDecoder { public: + CopyDecoder() { TRACED(); } - CopyDecoder(bool isPcm = false){ + CopyDecoder(bool isPcm){ is_pcm = isPcm; } @@ -30,26 +30,20 @@ class CopyDecoder : public AudioDecoder { virtual void setOutput(Print &out_stream) {pt_print=&out_stream;} - bool begin() { return true; } + void begin() {} void end() {} - size_t write(const uint8_t *data, size_t len) { - TRACED(); - if (pt_print == nullptr) { - LOGE("No output stream defined for CopyDecoder"); - return 0; - } - return pt_print->write((uint8_t*)data,len); - } + AudioInfo audioInfo() { AudioInfo dummy; return dummy; } + + size_t write(const void *data, size_t len) { return pt_print->write((uint8_t*)data,len); } operator bool() { return true; } - /// The result is encoded data - by default this is false - virtual bool isResultPCM() { return is_pcm;} + void setNotifyAudioChange(AudioInfoSupport &bi) {} - /// Defines that the source and therefor the result is also PCM data - void setResultPCM(bool pcm){ is_pcm = pcm;} + // The result is encoded data + virtual bool isResultPCM() { return is_pcm;} protected: Print *pt_print=nullptr; @@ -75,28 +69,27 @@ class CopyEncoder : public AudioEncoder { virtual void setOutput(Print &out_stream) {pt_print=&out_stream;} - bool begin() { return true;} + void begin() {} void end() {} - size_t write(const uint8_t *data, size_t len) { return pt_print->write((uint8_t*)data,len); } + AudioInfo audioInfo() { return info; } + void setAudioInfo(AudioInfo ai) { info = ai; } + + size_t write(const void *data, size_t len) { return pt_print->write((uint8_t*)data,len); } operator bool() { return true; } - /// Provides the mime type of the encoded data - const char *mime() {return mime_type;} + void setNotifyAudioChange(AudioInfoSupport &bi) {} - /// Defines the mime type - void setMime(const char *mime) { mime_type = mime; } + const char *mime() {return nullptr;} protected: Print *pt_print=nullptr; - const char *mime_type = "audio/pcm"; + AudioInfo info; }; -using PCMEncoder = CopyEncoder; -using PCMDecoder = CopyDecoder; } // namespace audio_tools diff --git a/src/AudioTools/AudioCodecs/CodecFLAC.h b/src/AudioCodecs/CodecFLAC.h similarity index 76% rename from src/AudioTools/AudioCodecs/CodecFLAC.h rename to src/AudioCodecs/CodecFLAC.h index 06570527b1..d53851d079 100644 --- a/src/AudioTools/AudioCodecs/CodecFLAC.h +++ b/src/AudioCodecs/CodecFLAC.h @@ -8,9 +8,8 @@ #pragma once -#include "AudioTools/AudioCodecs/AudioCodecsBase.h" -#include "AudioTools/CoreAudio/Buffers.h" -#include "AudioTools/CoreAudio/AudioBasic/Net.h" +#include "AudioCodecs/AudioEncoded.h" +#include "AudioTools/Buffers.h" #include "flac.h" #ifndef FLAC_READ_TIMEOUT_MS @@ -35,13 +34,11 @@ namespace audio_tools { */ class FLACDecoder : public StreamingDecoder { public: - /// Default Constructor - FLACDecoder(bool isOgg=false) { - is_ogg = isOgg; + FLACDecoder(bool isFLAC=false) { + is_ogg = isFLAC; } - /// Destructor - calls end(); - ~FLACDecoder() { end(); } + ~FLACDecoder() {} void setTimeout(uint64_t readTimeout=FLAC_READ_TIMEOUT_MS) { read_timeout_ms = readTimeout; @@ -58,28 +55,21 @@ class FLACDecoder : public StreamingDecoder { return info; } - bool begin() { + void begin() { TRACEI(); - is_active = false; + is_active = true; + if (decoder == nullptr) { if ((decoder = FLAC__stream_decoder_new()) == NULL) { LOGE("ERROR: allocating decoder"); is_active = false; - return false; + return; } - LOGI("FLAC__stream_decoder_new"); } + LOGI("FLAC__stream_decoder_new"); - // if it is already active we close it - auto state = FLAC__stream_decoder_get_state(decoder); - if (state != FLAC__STREAM_DECODER_UNINITIALIZED){ - FLAC__stream_decoder_finish(decoder); - } + FLAC__stream_decoder_set_md5_checking(decoder, false); - // deactivate md5 checking - FLAC__stream_decoder_set_md5_checking(decoder, is_md5_checing); - - // init decoder if (is_ogg){ init_status = FLAC__stream_decoder_init_ogg_stream( decoder, read_callback, nullptr, nullptr, nullptr, nullptr, write_callback, nullptr, error_callback, this); } else { @@ -89,20 +79,15 @@ class FLACDecoder : public StreamingDecoder { if (init_status != FLAC__STREAM_DECODER_INIT_STATUS_OK) { LOGE("ERROR: initializing decoder: %s", FLAC__StreamDecoderInitStatusString[init_status]); is_active = false; - return false; + return; } - LOGI("FLAC is open"); - is_active = true; - return true; + LOGI("FLAC__stream_decoder_init_stream"); } void end() { TRACEI(); - if (decoder != nullptr){ - flush(); - FLAC__stream_decoder_delete(decoder); - decoder = nullptr; - } + flush(); + FLAC__stream_decoder_delete(decoder); is_active = false; } @@ -111,19 +96,30 @@ class FLACDecoder : public StreamingDecoder { while(FLAC__stream_decoder_process_single(decoder)); } + void setNotifyAudioChange(AudioInfoSupport &bi) { + p_notify = &bi; + } + + /// Stream Interfce: Decode directly by taking data from the stream. This is more efficient + /// then feeding the decoder with write: just call copy() in the loop + void setInputStream(Stream &input) { + p_input = &input; + } + + virtual void setOutput(Print &out_stream) { p_print = &out_stream; } operator bool() { return is_active; } /// Stream Interface: Process a single frame - only relevant when input stream has been defined bool copy() { - LOGD("copy"); + LOGI("copy"); if (!is_active) { - LOGW("FLAC not active"); + LOGE("not active"); return false; } if (p_input == nullptr) { - LOGE("setInput was not called"); + LOGE("setInputStream was not called"); return false; } if (!FLAC__stream_decoder_process_single(decoder)) { @@ -133,18 +129,15 @@ class FLACDecoder : public StreamingDecoder { return true; } - /// Activate/deactivate md5 checking: call this before calling begin() - void setMD5(bool flag){ - is_md5_checing = flag; - } - protected: bool is_active = false; bool is_ogg = false; - bool is_md5_checing = false; AudioInfo info; + AudioInfoSupport *p_notify = nullptr; FLAC__StreamDecoder *decoder = nullptr; FLAC__StreamDecoderInitStatus init_status; + Print *p_print = nullptr; + Stream *p_input = nullptr; uint64_t time_last_read = 0; uint64_t read_timeout_ms = FLAC_READ_TIMEOUT_MS; @@ -159,14 +152,14 @@ class FLACDecoder : public StreamingDecoder { LOGE(FLAC__StreamDecoderErrorStatusString[status]); } - size_t readBytes(uint8_t *data, size_t len) override { - return p_input->readBytes(data, len); + size_t readBytes(uint8_t *buffer, size_t len) override { + return p_input->readBytes(buffer, len); } /// Callback which reads from stream static FLAC__StreamDecoderReadStatus read_callback(const FLAC__StreamDecoder *decoder, FLAC__byte result_buffer[],size_t *bytes, void *client_data) { FLAC__StreamDecoderReadStatus result = FLAC__STREAM_DECODER_READ_STATUS_CONTINUE; - LOGD("read_callback: %d", (int) *bytes); + LOGI("read_callback: %d", *bytes); FLACDecoder *self = (FLACDecoder *)client_data; if (self == nullptr || !self->is_active) { return FLAC__STREAM_DECODER_READ_STATUS_ABORT; @@ -174,7 +167,7 @@ class FLACDecoder : public StreamingDecoder { // get data directly from stream *bytes = self->readBytes(result_buffer, *bytes); - LOGD("-> %d", (int) *bytes); + LOGD("-> %d", *bytes); if (self->isEof(*bytes)){ result = FLAC__STREAM_DECODER_READ_STATUS_END_OF_STREAM; self->is_active = false; @@ -196,9 +189,9 @@ class FLACDecoder : public StreamingDecoder { return result; } - /// Output decoded result to final output stream + /// Output decoded result to final output stream static FLAC__StreamDecoderWriteStatus write_callback(const FLAC__StreamDecoder *decoder, const FLAC__Frame *frame,const FLAC__int32 *const buffer[], void *client_data) { - LOGD("write_callback: %u", (unsigned)frame->header.blocksize); + LOGI("write_callback: %d", frame->header.blocksize); FLACDecoder *self = (FLACDecoder *)client_data; AudioInfo actual_info = self->audioInfo(); @@ -209,46 +202,50 @@ class FLACDecoder : public StreamingDecoder { if (bps!=16){ LOGI("Converting from %d bits", bps); } - self->info = actual_info; - self->notifyAudioChange(self->info); + if (self->p_notify != nullptr) { + self->info = actual_info; + self->p_notify->setAudioInfo(self->info); + } } // write audio data int bps = FLAC__stream_decoder_get_bits_per_sample(decoder); - int16_t result_frame[actual_info.channels]; - + int16_t sample; switch(bps){ case 8: for (int j = 0; j < frame->header.blocksize; j++) { for (int i = 0; i < actual_info.channels; i++) { //self->output_buffer[j*actual_info.channels + i] = buffer[i][j]<<8; - result_frame[i] = buffer[i][j]<<8; + sample = buffer[i][j]<<8;; + self->p_print->write((uint8_t *)&sample,2); } - self->p_print->write((uint8_t *)result_frame, sizeof(result_frame)); } break; case 16: for (int j = 0; j < frame->header.blocksize; j++) { for (int i = 0; i < actual_info.channels; i++) { - result_frame[i] = buffer[i][j]; + //self->output_buffer[j*actual_info.channels + i] = buffer[i][j]; + sample = buffer[i][j]; + self->p_print->write((uint8_t *)&sample,2); } - self->p_print->write((uint8_t *)result_frame, sizeof(result_frame)); } break; case 24: for (int j = 0; j < frame->header.blocksize; j++) { for (int i = 0; i < actual_info.channels; i++) { - result_frame[i] = buffer[i][j] >> 8; + //self->output_buffer[j*actual_info.channels + i] = buffer[i][j]>>8; + sample = buffer[i][j] >>8; + self->p_print->write((uint8_t *)&sample,2); } - self->p_print->write((uint8_t *)result_frame, sizeof(result_frame)); } break; case 32: for (int j = 0; j < frame->header.blocksize; j++) { for (int i = 0; i < actual_info.channels; i++) { - result_frame[i] = buffer[i][j] >> 16; + //self->output_buffer[j*actual_info.channels+ i] = buffer[i][j]>>16; + sample = buffer[i][j] >>16; + self->p_print->write((uint8_t *)&sample,2); } - self->p_print->write((uint8_t *)result_frame, sizeof(result_frame)); } break; default: @@ -257,9 +254,11 @@ class FLACDecoder : public StreamingDecoder { return FLAC__STREAM_DECODER_WRITE_STATUS_CONTINUE; } + }; + /** * @brief FLACEncoder * @ingroup codecs @@ -269,13 +268,8 @@ class FLACDecoder : public StreamingDecoder { */ class FLACEncoder : public AudioEncoder { public: - /// Default Constructor - FLACEncoder(bool isOgg = false) { - setOgg(isOgg); - } - - /// Destructor - calls end(); - ~FLACEncoder() { end(); } + // Empty Constructor - the output stream must be provided with begin() + FLACEncoder() {} void setOgg(bool isOgg) { is_ogg = isOgg; @@ -307,19 +301,23 @@ class FLACEncoder : public AudioEncoder { cfg.logInfo(); } + virtual void begin(AudioInfo from) { + setAudioInfo(from); + begin(); + } + /// starts the processing using the actual AudioInfo - virtual bool begin() override { + virtual void begin() override { TRACED(); + is_open = false; if (p_encoder==nullptr){ p_encoder = FLAC__stream_encoder_new(); if (p_encoder==nullptr){ LOGE("FLAC__stream_encoder_new"); - return false; + return; } } - is_open = false; - FLAC__stream_encoder_set_channels(p_encoder, cfg.channels); FLAC__stream_encoder_set_bits_per_sample(p_encoder, cfg.bits_per_sample); FLAC__stream_encoder_set_sample_rate(p_encoder, cfg.sample_rate); @@ -338,59 +336,55 @@ class FLACEncoder : public AudioEncoder { if (status==FLAC__STREAM_ENCODER_INIT_STATUS_ENCODER_ERROR){ LOGE(" -> %s", FLAC__StreamEncoderStateString[FLAC__stream_encoder_get_state(p_encoder)]); } - return false; + return; } is_open = true; - return true; } /// starts the processing - bool begin(Print &out) { + void begin(Print &out) { p_print = &out; - return begin(); + begin(); } /// stops the processing void end() override { TRACED(); - if (p_encoder != nullptr) { - FLAC__stream_encoder_delete(p_encoder); - p_encoder = nullptr; - is_open = false; - } + FLAC__stream_encoder_delete(p_encoder); + p_encoder = nullptr; + is_open = false; } /// Writes FLAC Packet - virtual size_t write(const uint8_t *data, size_t len) override { + virtual size_t write(const void *in_ptr, size_t in_size) override { if (!is_open || p_print == nullptr) return 0; - LOGD("write: %zu", len); + LOGD("write: %u", in_size); size_t result = 0; int samples=0; int frames=0; - int32_t *data32=nullptr; + int32_t *data=nullptr; switch(cfg.bits_per_sample){ case 16: - samples = len / sizeof(int16_t); + samples = in_size / sizeof(int16_t); frames = samples / cfg.channels; - writeBuffer((int16_t*)data, samples); - data32 = buffer.data(); + writeBuffer((int16_t*)in_ptr, samples); + data = buffer.data(); break; - case 24: case 32: - samples = len / sizeof(int32_t); + samples = in_size / sizeof(int32_t); frames = samples / cfg.channels; - data32 = (int32_t*) data; + data = (int32_t*) in_ptr; break; default: - LOGE("bits_per_sample not supported: %d", (int) cfg.bits_per_sample); + LOGE("bits_per_sample not supported: %d", cfg.bits_per_sample); break; } if (frames>0){ - if (FLAC__stream_encoder_process_interleaved(p_encoder, data32, frames)){ - result = len; + if (FLAC__stream_encoder_process_interleaved(p_encoder, data, frames)){ + result = in_size; } else { LOGE("FLAC__stream_encoder_process_interleaved"); } @@ -408,8 +402,8 @@ class FLACEncoder : public AudioEncoder { Vector buffer; Print *p_print = nullptr; FLAC__StreamEncoder *p_encoder=nullptr; - bool is_open = false; - bool is_ogg = false; + bool is_open; + bool is_ogg; int flac_block_size = 512; // small value to minimize allocated memory int flac_compression_level = 8; @@ -418,7 +412,7 @@ class FLACEncoder : public AudioEncoder { if (self->p_print!=nullptr){ size_t written = self->p_print->write((uint8_t*)buffer, bytes); if (written!=bytes){ - LOGE("write_callback %zu -> %zu", bytes, written); + LOGE("write_callback %d -> %d", bytes, written); return FLAC__STREAM_ENCODER_WRITE_STATUS_FATAL_ERROR; } } diff --git a/src/AudioTools/AudioCodecs/CodecFloat.h b/src/AudioCodecs/CodecFloat.h similarity index 65% rename from src/AudioTools/AudioCodecs/CodecFloat.h rename to src/AudioCodecs/CodecFloat.h index 9e4bf6db03..9bc612cdc6 100644 --- a/src/AudioTools/AudioCodecs/CodecFloat.h +++ b/src/AudioCodecs/CodecFloat.h @@ -1,6 +1,6 @@ #pragma once -#include "AudioTools/AudioCodecs/AudioCodecsBase.h" +#include "AudioCodecs/AudioEncoded.h" namespace audio_tools { @@ -14,8 +14,14 @@ namespace audio_tools { */ class DecoderFloat : public AudioDecoder { public: - /// Empty Constructor - DecoderFloat() = default; + /** + * @brief Construct a new DecoderFloat object + */ + + DecoderFloat(){ + TRACED(); + } + /** * @brief Construct a new DecoderFloat object * @@ -24,6 +30,7 @@ class DecoderFloat : public AudioDecoder { DecoderFloat(Print &out_stream, bool active=true){ TRACED(); p_print = &out_stream; + this->active = active; } /** @@ -36,7 +43,6 @@ class DecoderFloat : public AudioDecoder { DecoderFloat(Print &out_stream, AudioInfoSupport &bi){ TRACED(); p_print = &out_stream; - addNotifyAudioChange(bi); } /// Defines the output Stream @@ -44,24 +50,54 @@ class DecoderFloat : public AudioDecoder { p_print = &out_stream; } + void setNotifyAudioChange(AudioInfoSupport &bi) override { + this->bid = &bi; + } + + AudioInfo audioInfo() override { + return cfg; + } + + void begin(AudioInfo info) override { + TRACED(); + cfg = info; + if (bid!=nullptr){ + bid->setAudioInfo(cfg); + } + active = true; + } + + void begin() override { + TRACED(); + active = true; + } + + void end() override { + TRACED(); + active = false; + } + /// Converts data from float to int16_t - virtual size_t write(const uint8_t *data, size_t len) override { + virtual size_t write(const void *data, size_t in_size) override { if (p_print==nullptr) return 0; - int samples = len/sizeof(float); + int samples = in_size/sizeof(float); buffer.resize(samples); float* p_float = (float*) data; for (int j=0;jwrite((uint8_t*)buffer.data(), samples*sizeof(int16_t)) * 2; + return p_print->write((uint8_t*)buffer.data(), samples*sizeof(int16_t)); } virtual operator bool() override { - return p_print!=nullptr;; + return active; } protected: Print *p_print=nullptr; + AudioInfoSupport *bid=nullptr; + AudioInfo cfg; + bool active; Vector buffer; }; @@ -76,10 +112,11 @@ class DecoderFloat : public AudioDecoder { */ class EncoderFloat : public AudioEncoder { public: - /// Empty Constructor - EncoderFloat() = default; + // Empty Constructor - the output stream must be provided with begin() + EncoderFloat(){ + } - /// Constructor providing the output stream + // Constructor providing the output stream EncoderFloat(Print &out){ p_print = &out; } @@ -94,16 +131,19 @@ class EncoderFloat : public AudioEncoder { return mime_pcm; } + /// We actually do nothing with this + virtual void setAudioInfo(AudioInfo from) override { + } + /// starts the processing using the actual RAWAudioInfo - virtual bool begin() override{ + virtual void begin() override{ is_open = true; - return true; } /// starts the processing - bool begin(Print &out) { + void begin(Print &out) { p_print = &out; - return begin(); + begin(); } /// stops the processing @@ -112,10 +152,10 @@ class EncoderFloat : public AudioEncoder { } /// Converts data from int16_t to float - virtual size_t write(const uint8_t *data, size_t len) override { + virtual size_t write(const void *in_ptr, size_t in_size) override { if (p_print==nullptr) return 0; - int16_t *pt16 = (int16_t*)data; - size_t samples = len / sizeof(int16_t); + int16_t *pt16 = (int16_t*)in_ptr; + size_t samples = in_size / sizeof(int16_t); buffer.resize(samples); for (size_t j=0;j(pt16[j]) / 32768.0; diff --git a/src/AudioTools/AudioCodecs/CodecG722.h b/src/AudioCodecs/CodecG722.h similarity index 76% rename from src/AudioTools/AudioCodecs/CodecG722.h rename to src/AudioCodecs/CodecG722.h index 96474dd39f..38e0787e6d 100644 --- a/src/AudioTools/AudioCodecs/CodecG722.h +++ b/src/AudioCodecs/CodecG722.h @@ -8,7 +8,7 @@ #pragma once -#include "AudioTools/AudioCodecs/AudioCodecsBase.h" +#include "AudioCodecs/AudioEncoded.h" #include "g722_codec.h" // size in bytes @@ -30,25 +30,35 @@ class G722Decoder : public AudioDecoder { public: G722Decoder() = default; + virtual void setAudioInfo(AudioInfo cfg) { this->cfg = cfg; } + + virtual AudioInfo audioInfo() { return cfg; } + + virtual void begin(AudioInfo cfg) { + setAudioInfo(cfg); + begin(); + } + /// Defines the options for the G.722 Codec: G722_SAMPLE_RATE_8000,G722_PACKED void setOptions(int options){ this->options = options; } - virtual bool begin() { + virtual void begin() { TRACEI(); input_buffer.resize(10); result_buffer.resize(40); - g722_dctx = g722_decoder_new(info.sample_rate, options); + g722_dctx = g722_decoder_new(cfg.sample_rate, options); if (g722_dctx == nullptr) { LOGE("g722_decoder_new"); - return false; + return; } - notifyAudioChange(info); + if (p_notify != nullptr) { + p_notify->setAudioInfo(cfg); + } is_active = true; - return true; } virtual void end() { @@ -57,28 +67,34 @@ class G722Decoder : public AudioDecoder { is_active = false; } + virtual void setNotifyAudioChange(AudioInfoSupport &bi) { + p_notify = &bi; + } + virtual void setOutput(Print &out_stream) { p_print = &out_stream; } operator bool() { return is_active; } - virtual size_t write(const uint8_t *data, size_t len) { - LOGD("write: %d", len); + virtual size_t write(const void *data, size_t length) { + LOGD("write: %d", length); if (!is_active) { LOGE("inactive"); return 0; } uint8_t *p_byte = (uint8_t *)data; - for (int j = 0; j < len; j++) { + for (int j = 0; j < length; j++) { processByte(p_byte[j]); } - return len; + return length; } protected: Print *p_print = nullptr; G722_DEC_CTX *g722_dctx=nullptr; + AudioInfo cfg; + AudioInfoSupport *p_notify = nullptr; Vector input_buffer; Vector result_buffer; int options = G722_SAMPLE_RATE_8000; @@ -118,27 +134,31 @@ class G722Encoder : public AudioEncoder { public: G722Encoder() = default; + void begin(AudioInfo bi) { + setAudioInfo(bi); + begin(); + } + /// Defines the options for the G.722 Codec: G722_SAMPLE_RATE_8000,G722_PACKED void setOptions(int options){ this->options = options; } - bool begin() { + void begin() { TRACEI(); - if (info.channels != 1) { - LOGW("1 channel expected, was: %d", info.channels); + if (cfg.channels != 1) { + LOGW("1 channel expected, was: %d", cfg.channels); } - g722_ectx = g722_encoder_new(info.sample_rate, options); + g722_ectx = g722_encoder_new(cfg.sample_rate, options); if (g722_ectx == NULL) { LOGE("g722_encoder_new"); - return false; + return; } input_buffer.resize(G722_PCM_SIZE); result_buffer.resize(G722_ENC_SIZE); is_active = true; - return true; } virtual void end() { @@ -149,25 +169,28 @@ class G722Encoder : public AudioEncoder { virtual const char *mime() { return "audio/g722"; } + virtual void setAudioInfo(AudioInfo cfg) { this->cfg = cfg; } + virtual void setOutput(Print &out_stream) { p_print = &out_stream; } operator bool() { return is_active; } - virtual size_t write(const uint8_t *data, size_t len) { - LOGD("write: %d", len); + virtual size_t write(const void *in_ptr, size_t in_size) { + LOGD("write: %d", in_size); if (!is_active) { LOGE("inactive"); return 0; } // encode bytes - uint8_t *p_byte = (uint8_t *)data; - for (int j = 0; j < len; j++) { + uint8_t *p_byte = (uint8_t *)in_ptr; + for (int j = 0; j < in_size; j++) { processByte(p_byte[j]); } - return len; + return in_size; } protected: + AudioInfo cfg; Print *p_print = nullptr; G722_ENC_CTX *g722_ectx = nullptr; Vector input_buffer; diff --git a/src/AudioTools/AudioCodecs/CodecG7xx.h b/src/AudioCodecs/CodecG7xx.h similarity index 79% rename from src/AudioTools/AudioCodecs/CodecG7xx.h rename to src/AudioCodecs/CodecG7xx.h index 926a339c28..081811b96d 100644 --- a/src/AudioTools/AudioCodecs/CodecG7xx.h +++ b/src/AudioCodecs/CodecG7xx.h @@ -1,5 +1,4 @@ #pragma once -#include "AudioTools/AudioCodecs/AudioCodecsBase.h" extern "C"{ #include "g72x.h" @@ -27,9 +26,9 @@ enum G7xxCODEC_e {g723_24, g721, g723_40, others}; class G7xxDecoder : public AudioDecoder { public: G7xxDecoder(G7xxCODEC_e codec) { - info.channels = 1; - info.sample_rate = 8000; - info.bits_per_sample = 16; + cfg.channels = 1; + cfg.sample_rate = 8000; + cfg.bits_per_sample = 16; switch(codec){ case g723_24: @@ -49,24 +48,26 @@ class G7xxDecoder : public AudioDecoder { } } - void setAudioInfo(AudioInfo info) override { - bool ok = true; - if (info.channels!=1){ - LOGE("channels must be 1 instead of %d", info.channels); - ok = false; + void setAudioInfo(AudioInfo cfg) override { + if (cfg.channels!=1){ + LOGE("channels must be 1 instead of %d", cfg.channels); } - if (info.sample_rate!=8000){ - LOGE("sample_rate must be 8000 instead of %d", info.sample_rate); - ok = false; + if (cfg.sample_rate!=8000){ + LOGE("sample_rate must be 8000 instead of %d", cfg.sample_rate); } - if (info.bits_per_sample!=16){ - LOGE("bits_per_sample must be 16 instead of %d", info.bits_per_sample); - ok = false; + if (cfg.bits_per_sample!=16){ + LOGE("bits_per_sample must be 16 instead of %d", cfg.bits_per_sample); } - if (ok) AudioDecoder::setAudioInfo(info); } - bool begin() override { + AudioInfo audioInfo()override { return cfg; } + + virtual void begin(AudioInfo cfg) { + setAudioInfo(cfg); + begin(); + } + + void begin() override { TRACEI(); in_buffer = 0; in_bits = 0; @@ -74,7 +75,6 @@ class G7xxDecoder : public AudioDecoder { g72x_init_state(&state); is_active = true; - return true; } void end() override { @@ -82,28 +82,34 @@ class G7xxDecoder : public AudioDecoder { is_active = false; } + void setNotifyAudioChange(AudioInfoSupport &bi) override { + p_notify = &bi; + } + void setOutput(Print &out_stream) override { p_print = &out_stream; } operator bool() { return is_active; } - size_t write(const uint8_t *data, size_t len) override { - LOGD("write: %d", len); + size_t write(const void *data, size_t length) override { + LOGD("write: %d", length); if (!is_active) { LOGE("inactive"); return 0; } uint8_t *p_byte = (uint8_t *)data; - for (int j = 0; j < len; j++) { + for (int j = 0; j < length; j++) { sample = (*dec_routine)(p_byte[j], AUDIO_ENCODING_LINEAR, &state); p_print->write((uint8_t*)&sample, out_size); } - return len; + return length; } protected: Print *p_print = nullptr; + AudioInfo cfg; + AudioInfoSupport *p_notify = nullptr; int input_pos = 0; bool is_active = false; int16_t sample; @@ -128,9 +134,9 @@ class G7xxDecoder : public AudioDecoder { class G7xxEncoder : public AudioEncoder { public: G7xxEncoder(G7xxCODEC_e codec) { - info.channels = 1; - info.sample_rate = 8000; - info.bits_per_sample = 16; + cfg.channels = 1; + cfg.sample_rate = 8000; + cfg.bits_per_sample = 16; switch(codec){ @@ -154,14 +160,18 @@ class G7xxEncoder : public AudioEncoder { } } - bool begin() override { + virtual void begin(AudioInfo bi) { + setAudioInfo(bi); + begin(); + } + + void begin() override { TRACEI(); g72x_init_state(&state); out_buffer = 0; out_bits = 0; is_active = true; - return true; } void end() override { @@ -171,45 +181,41 @@ class G7xxEncoder : public AudioEncoder { const char *mime() override { return p_mime; } - virtual void setAudioInfo(AudioInfo info) { - bool ok = true; - if (info.channels!=1){ - LOGE("channels must be 1 instead of %d", info.channels); - ok = false; + virtual void setAudioInfo(AudioInfo cfg) { + if (cfg.channels!=1){ + LOGE("channels must be 1 instead of %d", cfg.channels); } - if (info.sample_rate!=8000){ - LOGE("sample_rate must be 8000 instead of %d", info.sample_rate); - ok = false; + if (cfg.sample_rate!=8000){ + LOGE("sample_rate must be 8000 instead of %d", cfg.sample_rate); } - if (info.bits_per_sample!=16){ - LOGE("bits_per_sample must be 16 instead of %d", info.bits_per_sample); - ok = false; + if (cfg.bits_per_sample!=16){ + LOGE("bits_per_sample must be 16 instead of %d", cfg.bits_per_sample); } - if (ok) AudioEncoder::setAudioInfo(info); } void setOutput(Print &out_stream) override { p_print = &out_stream; } operator bool() { return is_active; } - size_t write(const uint8_t *data, size_t len) override { - LOGD("write: %d", len); + size_t write(const void *in_ptr, size_t byte_count) override { + LOGD("write: %d", byte_count); if (!is_active) { LOGE("inactive"); return 0; } // encode bytes - int16_t *p_16 = (int16_t *)data; - int samples = len / sizeof(int16_t); + int16_t *p_16 = (int16_t *)in_ptr; + int samples = byte_count / sizeof(int16_t); for (int j = 0; j < samples; j++) { code = (*enc_routine)(p_16[j], AUDIO_ENCODING_LINEAR, &state); p_print->write(&code, 1); } - return len; + return byte_count; } protected: + AudioInfo cfg; Print *p_print = nullptr; bool is_active = false; const char *p_mime = nullptr; @@ -306,21 +312,21 @@ class G711Encoder : public G7xxEncoder { this->enc = enc; assert(this->enc!=nullptr); }; - size_t write(const uint8_t *data, size_t len) override { - LOGD("write: %d", len); + size_t write(const void *in_ptr, size_t in_size) override { + LOGD("write: %d", in_size); if (!is_active) { LOGE("inactive"); return 0; } // encode bytes - int samples = len/2; - int16_t *p_16 = (int16_t *)data; + int samples = in_size/2; + int16_t *p_16 = (int16_t *)in_ptr; uint8_t buffer[samples]; for (int j = 0; j < samples; j++) { buffer[j] = enc(p_16[j]); } p_print->write(buffer,samples); - return len; + return in_size; } protected: uint8_t(*enc)(int)=nullptr; @@ -341,19 +347,19 @@ class G711Decoder : public G7xxDecoder { assert(this->dec!=nullptr); }; - size_t write(const uint8_t *data, size_t len) override { - LOGD("write: %d", len); + size_t write(const void *in_ptr, size_t in_size) override { + LOGD("write: %d", in_size); if (!is_active) { LOGE("inactive"); return 0; } // decode bytes - uint8_t *p_8 = (uint8_t *)data; - for (int j = 0; j < len; j++) { + uint8_t *p_8 = (uint8_t *)in_ptr; + for (int j = 0; j < in_size; j++) { int16_t result = dec(p_8[j]); p_print->write((uint8_t*)&result,sizeof(int16_t)); } - return len; + return in_size; } protected: int (*dec)(uint8_t a_val)=nullptr; diff --git a/src/AudioTools/AudioCodecs/CodecGSM.h b/src/AudioCodecs/CodecGSM.h similarity index 62% rename from src/AudioTools/AudioCodecs/CodecGSM.h rename to src/AudioCodecs/CodecGSM.h index 347e67f580..8bc3109402 100644 --- a/src/AudioTools/AudioCodecs/CodecGSM.h +++ b/src/AudioCodecs/CodecGSM.h @@ -8,7 +8,7 @@ #pragma once -#include "AudioTools/AudioCodecs/AudioCodecsBase.h" +#include "AudioCodecs/AudioEncoded.h" #include "gsm.h" @@ -26,11 +26,20 @@ namespace audio_tools { class GSMDecoder : public AudioDecoder { public: GSMDecoder() { - info.sample_rate = 8000; - info.channels = 1; + cfg.sample_rate = 8000; + cfg.channels = 1; } - virtual bool begin() { + virtual void setAudioInfo(AudioInfo cfg) { this->cfg = cfg; } + + virtual AudioInfo audioInfo() { return cfg; } + + virtual void begin(AudioInfo cfg) { + setAudioInfo(cfg); + begin(); + } + + virtual void begin() { TRACEI(); // 160 13-bit samples result_buffer.resize(160 * sizeof(int16_t)); @@ -38,9 +47,10 @@ class GSMDecoder : public AudioDecoder { input_buffer.resize(33); v_gsm = gsm_create(); - notifyAudioChange(info); + if (p_notify!=nullptr){ + p_notify->setAudioInfo(cfg); + } is_active = true; - return true; } virtual void end() { @@ -49,27 +59,34 @@ class GSMDecoder : public AudioDecoder { is_active = false; } + virtual void setNotifyAudioChange(AudioInfoSupport &bi) { + p_notify = &bi; + } + virtual void setOutput(Print &out_stream) { p_print = &out_stream; } operator bool() { return is_active; } - virtual size_t write(const uint8_t *data, size_t len) { - LOGD("write: %d", len); + virtual size_t write(const void *data, size_t length) { + LOGD("write: %d", length); if (!is_active) { LOGE("inactive"); return 0; } - for (int j = 0; j < len; j++) { - processByte(data[j]); + uint8_t *p_byte = (uint8_t *) data; + for (int j = 0; j < length; j++) { + processByte(p_byte[j]); } - return len; + return length; } protected: Print *p_print = nullptr; gsm v_gsm; + AudioInfo cfg; + AudioInfoSupport *p_notify = nullptr; bool is_active = false; Vector input_buffer; Vector result_buffer; @@ -86,37 +103,16 @@ class GSMDecoder : public AudioDecoder { LOGE("gsm_decode"); } - //fromBigEndian(result_buffer); // scale to 13 to 16-bit samples - scale(result_buffer); + int16_t *pt16 = (int16_t *)input_buffer.data(); + for (int j = 0; j < input_buffer.size() / 2; j++) { + pt16[j] = pt16[j] * 8; + } p_print->write(result_buffer.data(), result_buffer.size()); input_pos = 0; } } - - void scale(Vector &vector){ - int16_t *pt16 = (int16_t *)vector.data(); - for (int j = 0; j < vector.size() / 2; j++) { - if (abs(pt16[j])<=4095){ - pt16[j] = pt16[j] * 8; - } else if(pt16[j]<0){ - pt16[j] = -32767; - } else if(pt16[j]>0){ - pt16[j] = 32767; - } - } - } - - void fromBigEndian(Vector &vector){ - int size = vector.size() / 2; - int16_t *data16 = (int16_t*) vector.data(); - for (int i=0; icfg = cfg; } + virtual void setOutput(Print &out_stream) { p_print = &out_stream; } operator bool() { return is_active; } - virtual size_t write(const uint8_t *data, size_t len) { - LOGD("write: %d", len); + virtual size_t write(const void *in_ptr, size_t in_size) { + LOGD("write: %d", in_size); if (!is_active) { LOGE("inactive"); return 0; } // encode bytes - for (int j = 0; j < len; j++) { - processByte(data[j]); + uint8_t *p_byte = (uint8_t *) in_ptr; + for (int j = 0; j < in_size; j++) { + processByte(p_byte[j]); } - return len; + return in_size; } protected: + AudioInfo cfg; Print *p_print = nullptr; gsm v_gsm; bool is_active = false; @@ -193,35 +197,24 @@ class GSMEncoder : public AudioEncoder { void processByte(uint8_t byte) { input_buffer[buffer_pos++] = byte; if (buffer_pos >= input_buffer.size()) { - scaleValues(input_buffer); - // toBigEndian(input_buffer); + scaleValues(); // encode gsm_encode(v_gsm, (gsm_signal*)input_buffer.data(), result_buffer.data()); - size_t written = p_print->write(result_buffer.data(), result_buffer.size()); - assert(written == result_buffer.size()); + p_print->write(result_buffer.data(), result_buffer.size()); buffer_pos = 0; } } - void toBigEndian(Vector &vector){ - int size = vector.size() / 2; - int16_t *data16 = (int16_t*) vector.data(); - for (int i=0; i &vector) { - int16_t *pt16 = (int16_t *)vector.data(); - int size = vector.size() / 2; + void scaleValues() { + int16_t *pt16 = (int16_t *)input_buffer.data(); if (scaling_active){ // scale to 16 to 13-bit samples - for (int j = 0; j < size; j++) { + for (int j = 0; j < input_buffer.size() / 2; j++) { pt16[j] = pt16[j] / 8; } } else { // clip value to 13-bits - for (int j = 0; j < size; j++) { + for (int j = 0; j < input_buffer.size() / 2; j++) { if ( pt16[j]>4095){ pt16[j] = 4095; } diff --git a/src/AudioCodecs/CodecHelix.h b/src/AudioCodecs/CodecHelix.h new file mode 100644 index 0000000000..c5687afabd --- /dev/null +++ b/src/AudioCodecs/CodecHelix.h @@ -0,0 +1,113 @@ +#pragma once + +#include "AudioCodecs/AudioEncoded.h" +#include "AudioCodecs/CodecWAV.h" +#include "AudioCodecs/CodecMP3Helix.h" +#include "AudioCodecs/CodecAACHelix.h" + +namespace audio_tools { + +/** + * @brief MP3 and AAC Decoder using libhelix: + * https://github.com/pschatzmann/arduino-libhelix. We dynamically create a MP3 or AAC decoder dependent on the provided audio format. + * @ingroup codecs + * @ingroup decoder + * @author Phil Schatzmann + * @copyright GPLv3 + */ + +class DecoderHelix : public AudioDecoder { +public: + DecoderHelix() { TRACED(); } + + DecoderHelix(Print &out_stream) { + TRACED(); + p_out_stream = &out_stream; + } + + DecoderHelix(Print &out_stream, AudioInfoSupport &bi) { + TRACED(); + p_out_stream = &out_stream; + p_bi = &bi; + } + + ~DecoderHelix() { resetDecoder(); } + + /// Defines the output Stream + virtual void setOutput(Print &outStream) { p_out_stream = &outStream; } + + /// Starts the processing + void begin() { + TRACED(); + // reset actual decoder so that we start a new determination + resetDecoder(); + } + + /// Releases the reserved memory + void end() { + TRACED(); + if (p_decoder!=nullptr){ + p_decoder->end(); + } + resetDecoder(); + } + + AudioInfo audioInfo() { + return p_decoder != nullptr ? p_decoder->audioInfo() : noInfo; + } + + /// Write mp3 data to decoder + size_t write(const void *data, size_t len) { + LOGD("%s: %zu", LOG_METHOD, len); + if (p_decoder == nullptr) { + setupDecoder((const byte *)data); + p_decoder->begin(); + } + return p_decoder->write(data, len); + } + + /// checks if the class is active + operator bool() { return p_decoder == nullptr ? false : *p_decoder; } + + /// Defines the callback object to which the Audio information change is + /// provided + void setNotifyAudioChange(AudioInfoSupport &bi) { p_bi = &bi; } + +protected: + AudioDecoder *p_decoder = nullptr; + Print *p_out_stream = nullptr; + AudioInfoSupport *p_bi = nullptr; + AudioInfo noInfo; + + /// Defines the decoder based on the audio format + void setupDecoder(const byte *start) { + if (start[0] == 0xFF && start[1] == 0xF1) { + p_decoder = new AACDecoderHelix(); + LOGI("using AACDecoderHelix"); + } else if (start[0] == 0xFF || start[0] == 0xFE || strncmp("ID3", (const char*)start, 3)==0) { + p_decoder = new MP3DecoderHelix(); + LOGI("using MP3DecoderHelix"); + } else if (strncmp("RIFF", (const char*)start, 4)==0) { + p_decoder = new WAVDecoder(); + LOGI("using WAVDecoder"); + } + // if we do not have a decoder yet we use a dummy to prevent NPE + if (p_decoder == nullptr) { + LOGW("Unknown Data Format: Content will be ignored...") + p_decoder = CodecNOP::instance(); + } + p_decoder->setOutput(*p_out_stream); + p_decoder->setNotifyAudioChange(*p_bi); + } + + /// Deletes the decoder + void resetDecoder() { + if (p_decoder != nullptr) { + delete p_decoder; + } + p_decoder = nullptr; + } +}; + +} // namespace audio_tools + diff --git a/src/AudioTools/AudioCodecs/CodecILBC.h b/src/AudioCodecs/CodecILBC.h similarity index 81% rename from src/AudioTools/AudioCodecs/CodecILBC.h rename to src/AudioCodecs/CodecILBC.h index 95e473e709..0007d22cb3 100644 --- a/src/AudioTools/AudioCodecs/CodecILBC.h +++ b/src/AudioCodecs/CodecILBC.h @@ -10,7 +10,7 @@ */ #pragma once -#include "AudioTools/AudioCodecs/AudioCodecsBase.h" +#include "AudioCodecs/AudioEncoded.h" #include "iLBC.h" @@ -38,11 +38,13 @@ class ILBCDecoder : public AudioDecoder { end(); } - virtual bool begin() { + virtual AudioInfo audioInfo() { return info; } + + virtual void begin() { TRACEI(); if (p_print==nullptr){ LOGE("Output not defined"); - return false; + return; } if (p_ilbc==nullptr){ @@ -54,8 +56,9 @@ class ILBCDecoder : public AudioDecoder { encoded_buffer.resize(p_ilbc->getEncodedBytes()); // update audio information - notifyAudioChange(info); - return true; + if (notify != nullptr) { + notify->setAudioInfo(info); + } } virtual void end() { @@ -64,15 +67,19 @@ class ILBCDecoder : public AudioDecoder { p_ilbc = nullptr; } + virtual void setNotifyAudioChange(AudioInfoSupport &bi) { + notify = &bi; + } + virtual void setOutput(Print &out_stream) { p_print = &out_stream; } operator bool() { return p_ilbc != nullptr; } - virtual size_t write(const uint8_t *data, size_t len) { + virtual size_t write(const void *input_buffer, size_t length) { if (p_ilbc==nullptr) return 0; - LOGI("write: %d", len); - int samples = len / sizeof(int16_t); - int16_t *p_samples = (int16_t *)data; + LOGI("write: %d", length); + int samples = length / sizeof(int16_t); + int16_t *p_samples = (int16_t *)input_buffer; for (int j=0;j=encoded_buffer.size()){ @@ -85,10 +92,12 @@ class ILBCDecoder : public AudioDecoder { encoded_buffer_pos = 0; } } - return len; + return length; } protected: + AudioInfo info; + AudioInfoSupport *notify = nullptr; Print *p_print = nullptr; iLBCDecode *p_ilbc = nullptr; Vector decoded_buffer{0}; @@ -120,15 +129,24 @@ class ILBCEncoder : public AudioEncoder { end(); } - bool begin() { + void begin(AudioInfo info) { + setAudioInfo(info); + begin(); + } + + void setAudioInfo(AudioInfo info) { + this->info = info; + } + + void begin() { TRACEI(); if (p_print==nullptr){ LOGE("Output not defined"); - return false; + return; } if (info.bits_per_sample!=16){ LOGE("bits_per_sample must be 16: %d",info.bits_per_sample); - return false; + return; } if (info.sample_rate!=8000){ LOGW("The sample rate should be 8000: %d", info.sample_rate); @@ -142,7 +160,6 @@ class ILBCEncoder : public AudioEncoder { decoded_buffer.resize(p_ilbc->getSamples()); encoded_buffer.resize(p_ilbc->getEncodedBytes()); decoded_buffer_pos = 0; - return true; } virtual void end() { @@ -159,12 +176,12 @@ class ILBCEncoder : public AudioEncoder { operator bool() { return p_ilbc != nullptr; } - virtual size_t write(const uint8_t *data, size_t len) { + virtual size_t write(const void *in_data, size_t in_size) { if (p_ilbc==nullptr) return 0; - LOGI("write: %d", len); + LOGI("write: %d", in_size); - int samples = len / sizeof(int16_t); - int16_t *p_samples = (int16_t *)data; + int samples = in_size / sizeof(int16_t); + int16_t *p_samples = (int16_t *)in_data; for (int j=0;j decoded_buffer{0}; diff --git a/src/AudioTools/AudioCodecs/CodecL16.h b/src/AudioCodecs/CodecL16.h similarity index 66% rename from src/AudioTools/AudioCodecs/CodecL16.h rename to src/AudioCodecs/CodecL16.h index 19bac449c4..919322bd43 100644 --- a/src/AudioTools/AudioCodecs/CodecL16.h +++ b/src/AudioCodecs/CodecL16.h @@ -1,6 +1,7 @@ #pragma once -#include "AudioTools/AudioCodecs/AudioCodecsBase.h" +#include "AudioCodecs/AudioEncoded.h" + namespace audio_tools { /** @@ -26,6 +27,7 @@ class DecoderL16 : public AudioDecoder { DecoderL16(Print &out_stream, bool active = true) { TRACED(); p_print = &out_stream; + this->active = active; } /** @@ -38,27 +40,52 @@ class DecoderL16 : public AudioDecoder { DecoderL16(Print &out_stream, AudioInfoSupport &bi) { TRACED(); setOutput(out_stream); - addNotifyAudioChange(bi); + setNotifyAudioChange(bi); } /// Defines the output Stream void setOutput(Print &out_stream) override { p_print = &out_stream; } - virtual size_t write(const uint8_t *data, size_t len) override { + void setNotifyAudioChange(AudioInfoSupport &bi) override { this->bid = &bi; } + + AudioInfo audioInfo() override { return cfg; } + + void begin(AudioInfo info) { + TRACED(); + cfg = info; + if (bid != nullptr) { + bid->setAudioInfo(cfg); + } + active = true; + } + + void begin() override { + TRACED(); + active = true; + } + + void end() override { + TRACED(); + active = false; + } + + virtual size_t write(const void *in_ptr, size_t in_size) override { if (p_print == nullptr) return 0; - int16_t *data16 = (int16_t *)data; - for (int j = 0; j < len / 2; j++) { + int16_t *data16 = (int16_t *)in_ptr; + for (int j = 0; j < in_size / 2; j++) { data16[j] = ntohs(data16[j]); } - return p_print->write((uint8_t *)data, len); + return p_print->write((uint8_t *)in_ptr, in_size); } - virtual operator bool() override { return p_print!=nullptr; } - + virtual operator bool() override { return active; } protected: Print *p_print = nullptr; + AudioInfoSupport *bid = nullptr; + AudioInfo cfg; + bool active; }; /** @@ -86,29 +113,32 @@ class EncoderL16 : public AudioEncoder { /// Provides "audio/pcm" const char *mime() override { return "audio/l16"; } + /// We actually do nothing with this + virtual void setAudioInfo(AudioInfo from) override {} + /// starts the processing using the actual RAWAudioInfo - virtual bool begin() override { is_open = true; return true;} + virtual void begin() override { is_open = true; } /// starts the processing - bool begin(Print &out) { + void begin(Print &out) { p_print = &out; - return begin(); + begin(); } /// stops the processing void end() override { is_open = false; } /// Writes PCM data to be encoded as RAW - virtual size_t write(const uint8_t *data, size_t len) override { + virtual size_t write(const void *in_ptr, size_t in_size) override { if (p_print == nullptr) return 0; - int16_t *data16 = (int16_t *)data; - for (int j = 0; j < len / 2; j++) { + int16_t *data16 = (int16_t *)in_ptr; + for (int j = 0; j < in_size / 2; j++) { data16[j] = htons(data16[j]); } - return p_print->write((uint8_t *)data, len); + return p_print->write((uint8_t *)in_ptr, in_size); } operator bool() override { return is_open; } diff --git a/src/AudioTools/AudioCodecs/CodecL8.h b/src/AudioCodecs/CodecL8.h similarity index 73% rename from src/AudioTools/AudioCodecs/CodecL8.h rename to src/AudioCodecs/CodecL8.h index e01fb22f81..b8ca79bbfc 100644 --- a/src/AudioTools/AudioCodecs/CodecL8.h +++ b/src/AudioCodecs/CodecL8.h @@ -1,6 +1,6 @@ #pragma once -#include "AudioTools/AudioCodecs/AudioCodecsBase.h" +#include "AudioCodecs/AudioEncoded.h" namespace audio_tools { @@ -35,6 +35,7 @@ class DecoderL8 : public AudioDecoder { DecoderL8(Print &out_stream, bool active = true) { TRACED(); p_print = &out_stream; + this->active = active; } /** @@ -47,45 +48,64 @@ class DecoderL8 : public AudioDecoder { DecoderL8(Print &out_stream, AudioInfoSupport &bi) { TRACED(); setOutput(out_stream); - addNotifyAudioChange(bi); + setNotifyAudioChange(bi); } /// By default the encoded values are unsigned, but you can change them to /// signed void setSigned(bool isSigned) { is_signed = isSigned; } + void begin(AudioInfo info1) override { + TRACED(); + info = info1; + info.bits_per_sample = 16; // + if (p_notify != nullptr) { + p_notify->setAudioInfo(info); + } + active = true; + } + + void begin() override { + TRACED(); + active = true; + } + + void end() override { + TRACED(); + active = false; + } + /// for most decoders this is not needed virtual void setAudioInfo(AudioInfo from) override { TRACED(); - if (from.bits_per_sample!=16){ - LOGE("Bits per sample not supported: %d", from.bits_per_sample); - } from.bits_per_sample = 16; if (info != from) { - notifyAudioChange(from); + if (p_notify != nullptr) { + p_notify->setAudioInfo(from); + } } info = from; } - virtual size_t write(const uint8_t *data, size_t len) override { + virtual size_t write(const void *in_ptr, size_t in_size) override { if (p_print == nullptr) return 0; - buffer.resize(len); - memset(buffer.data(), 0, len * 2); + buffer.resize(in_size); + memset(buffer.data(), 0, in_size * 2); if (is_signed) { - int8_t *pt8 = (int8_t *)data; - for (size_t j = 0; j < len; j++) { + int8_t *pt8 = (int8_t *)in_ptr; + for (size_t j = 0; j < in_size; j++) { buffer[j] = convertSample(pt8[j]); } } else { - uint8_t *pt8 = (uint8_t *)data; - for (size_t j = 0; j < len; j++) { + uint8_t *pt8 = (uint8_t *)in_ptr; + for (size_t j = 0; j < in_size; j++) { buffer[j] = convertSample(pt8[j]); } } - int write_byte_count = len * sizeof(int16_t); - size_t result = p_print->write((uint8_t *)buffer.data(), write_byte_count); - LOGD("DecoderL8 %d -> %d -> %d", (int)len, write_byte_count, (int)result); - return result / sizeof(int16_t); + size_t result = + p_print->write((uint8_t *)buffer.data(), in_size * sizeof(int16_t)); + LOGD("DecoderL8 %d -> %d", (int)in_size, (int)result); + return result * sizeof(int16_t); } int16_t convertSample(int16_t in) { @@ -93,13 +113,13 @@ class DecoderL8 : public AudioDecoder { if (!is_signed) { tmp -= 129; } - return NumberConverter::clipT(tmp * 258); + return NumberConverter::clip(tmp * 258); } - virtual operator bool() override { return p_print!=nullptr; } - + virtual operator bool() override { return active; } protected: + bool active; bool is_signed = false; Vector buffer; }; @@ -137,23 +157,26 @@ class EncoderL8 : public AudioEncoder { /// Provides "audio/pcm" const char *mime() override { return "audio/l8"; } + /// We actually do nothing with this + void setAudioInfo(AudioInfo from) override {} + /// starts the processing using the actual RAWAudioInfo - bool begin() override { is_open = true; return true;} + void begin() override { is_open = true; } /// starts the processing - bool begin(Print &out) { + void begin(Print &out) { p_print = &out; - return begin(); + begin(); } /// stops the processing void end() override { is_open = false; } /// Writes PCM data to be encoded as RAW - size_t write(const uint8_t *data, size_t len) override { + size_t write(const void *in_ptr, size_t in_size) override { if (p_print == nullptr) return 0; - int16_t *pt16 = (int16_t *)data; - size_t samples = len / 2; + int16_t *pt16 = (int16_t *)in_ptr; + size_t samples = in_size / 2; buffer.resize(samples); memset(buffer.data(), 0, samples); for (size_t j = 0; j < samples; j++) { @@ -161,14 +184,14 @@ class EncoderL8 : public AudioEncoder { } size_t result = p_print->write((uint8_t *)buffer.data(), samples); - LOGD("EncoderL8 %d -> %d -> %d", (int)len,(int) samples, (int)result); - return result * sizeof(int16_t); + LOGD("EncoderL8 %d -> %d", (int)in_size, (int)result); + return result / sizeof(int16_t); } operator bool() override { return is_open; } int16_t convertSample(int16_t sample) { - int16_t tmp = NumberConverter::clipT(sample / 258); + int16_t tmp = NumberConverter::clip(sample / 258); if (!is_signed) { tmp += 129; // clip to range diff --git a/src/AudioTools/AudioCodecs/CodecLC3.h b/src/AudioCodecs/CodecLC3.h similarity index 88% rename from src/AudioTools/AudioCodecs/CodecLC3.h rename to src/AudioCodecs/CodecLC3.h index 2c8aa781eb..7b9e9a8732 100644 --- a/src/AudioTools/AudioCodecs/CodecLC3.h +++ b/src/AudioCodecs/CodecLC3.h @@ -10,7 +10,7 @@ */ #pragma once -#include "AudioTools/AudioCodecs/AudioCodecsBase.h" +#include "AudioCodecs/AudioEncoded.h" #include "lc3.h" namespace audio_tools { @@ -46,7 +46,14 @@ class LC3Decoder : public AudioDecoder { info.channels = 1; } - virtual bool begin() { + virtual AudioInfo audioInfo() { return info; } + + void begin(AudioInfo bi) { + info = bi; + begin(); + } + + virtual void begin() { TRACEI(); // Return the number of PCM samples in a frame @@ -62,7 +69,7 @@ class LC3Decoder : public AudioDecoder { if (!checkValues()) { LOGE("Invalid Parameters"); - return false; + return; } // setup memory @@ -73,11 +80,12 @@ class LC3Decoder : public AudioDecoder { // setup decoder lc3_decoder = lc3_setup_decoder(dt_us, info.sample_rate, 0, (void *)lc3_decoder_memory.data()); - notifyAudioChange(info); + if (p_notify != nullptr) { + p_notify->setAudioInfo(info); + } input_pos = 0; active = true; - return true; } virtual void end() { @@ -85,17 +93,22 @@ class LC3Decoder : public AudioDecoder { active = false; } + virtual void setNotifyAudioChange(AudioInfoSupport &bi) { + TRACEI(); + p_notify = &bi; + } + virtual void setOutput(Print &out_stream) { p_print = &out_stream; } operator bool() { return active; } - virtual size_t write(const uint8_t *data, size_t len) { + virtual size_t write(const void *input, size_t length) { if (!active) return 0; - LOGD("write %u", len); + LOGD("write %u", length); - uint8_t *p_ptr8 = (uint8_t *)data; + uint8_t *p_ptr8 = (uint8_t *)input; - for (int j = 0; j < len; j++) { + for (int j = 0; j < length; j++) { input_buffer[input_pos++] = p_ptr8[j]; if (input_pos >= input_buffer.size()) { if (lc3_decode(lc3_decoder, input_buffer.data(), input_buffer.size(), @@ -113,7 +126,7 @@ class LC3Decoder : public AudioDecoder { input_pos = 0; } } - return len; + return length; } protected: @@ -194,7 +207,12 @@ class LC3Encoder : public AudioEncoder { output_byte_count = outputByteCount; } - bool begin() { + void begin(AudioInfo bi) { + setAudioInfo(bi); + begin(); + } + + void begin() { TRACEI(); unsigned enc_size = lc3_encoder_size(dt_us, info.sample_rate); @@ -208,7 +226,7 @@ class LC3Encoder : public AudioEncoder { LOGI("num_frames: %d", num_frames); if (!checkValues()) { - return false; + return; } // setup memory @@ -222,7 +240,6 @@ class LC3Encoder : public AudioEncoder { input_pos = 0; active = true; - return true; } virtual void end() { @@ -232,16 +249,18 @@ class LC3Encoder : public AudioEncoder { virtual const char *mime() { return "audio/lc3"; } + virtual void setAudioInfo(AudioInfo info) { this->info = info; } + virtual void setOutput(Print &out_stream) { p_print = &out_stream; } operator bool() { return lc3_encoder != nullptr; } - virtual size_t write(const uint8_t *data, size_t len) { + virtual size_t write(const void *in_ptr, size_t in_size) { if (!active) return 0; - LOGD("write %u", len); - uint8_t *p_ptr8 = (uint8_t *)data; + LOGD("write %u", in_size); + uint8_t *p_ptr8 = (uint8_t *)in_ptr; - for (int j = 0; j < len; j++) { + for (int j = 0; j < in_size; j++) { input_buffer[input_pos++] = p_ptr8[j]; if (input_pos >= num_frames * 2) { if (lc3_encode(lc3_encoder, pcm_format, @@ -259,10 +278,11 @@ class LC3Encoder : public AudioEncoder { input_pos = 0; } } - return len; + return in_size; } protected: + AudioInfo info; Print *p_print = nullptr; unsigned dt_us = 1000; uint16_t num_frames; diff --git a/src/AudioTools/AudioCodecs/CodecMP3Helix.h b/src/AudioCodecs/CodecMP3Helix.h similarity index 67% rename from src/AudioTools/AudioCodecs/CodecMP3Helix.h rename to src/AudioCodecs/CodecMP3Helix.h index f0386900ef..bac528fed0 100644 --- a/src/AudioTools/AudioCodecs/CodecMP3Helix.h +++ b/src/AudioCodecs/CodecMP3Helix.h @@ -1,9 +1,8 @@ #pragma once -#include "AudioTools/AudioCodecs/AudioCodecsBase.h" -#ifndef HELIX_PRINT -# define HELIX_PRINT -#endif +#include "Stream.h" +#include "AudioCodecs/AudioEncoded.h" +#include "AudioMetaData/MetaDataFilter.h" #include "MP3DecoderHelix.h" namespace audio_tools { @@ -22,9 +21,8 @@ class MP3DecoderHelix : public AudioDecoder { MP3DecoderHelix() { TRACED(); mp3 = new libhelix::MP3DecoderHelix(); - if (mp3!=nullptr){ - mp3->setReference(this); - } else { + filter.setDecoder(mp3); + if (mp3==nullptr){ LOGE("Not enough memory for libhelix"); } } @@ -36,9 +34,8 @@ class MP3DecoderHelix : public AudioDecoder { MP3DecoderHelix(Print &out_stream){ TRACED(); mp3 = new libhelix::MP3DecoderHelix(); - if (mp3!=nullptr){ - mp3->setReference(this); - } else { + filter.setDecoder(mp3); + if (mp3==nullptr){ LOGE("Not enough memory for libhelix"); } setOutput(out_stream); @@ -54,13 +51,12 @@ class MP3DecoderHelix : public AudioDecoder { MP3DecoderHelix(Print &out_stream, AudioInfoSupport &bi){ TRACED(); mp3 = new libhelix::MP3DecoderHelix(); - if (mp3!=nullptr){ - mp3->setReference(this); - } else { + filter.setDecoder(mp3); + if (mp3==nullptr){ LOGE("Not enough memory for libhelix"); } setOutput(out_stream); - addNotifyAudioChange(bi); + setNotifyAudioChange(bi); } /** @@ -72,23 +68,22 @@ class MP3DecoderHelix : public AudioDecoder { } /// Defines the output Stream - void setOutput(Print &outStream) override { - AudioDecoder::setOutput(outStream); + virtual void setOutput(Print &outStream){ if (mp3!=nullptr) mp3->setOutput(outStream); } /// Starts the processing - bool begin() override { + void begin(){ TRACED(); if (mp3!=nullptr) { - //mp3->setDelay(CODEC_DELAY_MS); + mp3->setDelay(CODEC_DELAY_MS); mp3->begin(); + filter.begin(); } - return true; } /// Releases the reserved memory - void end() override { + void end(){ TRACED(); if (mp3!=nullptr) mp3->end(); } @@ -97,7 +92,7 @@ class MP3DecoderHelix : public AudioDecoder { return mp3->audioInfo(); } - AudioInfo audioInfo() override { + AudioInfo audioInfo(){ MP3FrameInfo i = audioInfoEx(); AudioInfo baseInfo; baseInfo.channels = i.nChans; @@ -107,14 +102,14 @@ class MP3DecoderHelix : public AudioDecoder { } /// Write mp3 data to decoder - size_t write(const uint8_t* data, size_t len) override { + size_t write(const void* mp3Data, size_t len) { LOGD("%s: %zu", LOG_METHOD, len); if (mp3==nullptr) return 0; - return mp3->write((uint8_t*)data, len); + return use_filter ? filter.write((uint8_t*)mp3Data, len): mp3->write((uint8_t*)mp3Data, len); } /// checks if the class is active - operator bool() override { + operator bool(){ return mp3!=nullptr && (bool) *mp3; } @@ -123,28 +118,35 @@ class MP3DecoderHelix : public AudioDecoder { } /// Defines the callback object to which the Audio information change is provided - void addNotifyAudioChange(AudioInfoSupport &bi) override { + void setNotifyAudioChange(AudioInfoSupport &bi){ TRACED(); - AudioDecoder::addNotifyAudioChange(bi); - if (mp3!=nullptr) mp3->setInfoCallback(infoCallback, this); + audioChangeMP3Helix = &bi; + if (mp3!=nullptr) mp3->setInfoCallback(infoCallback, this); } /// notifies the subscriber about a change static void infoCallback(MP3FrameInfo &i, void * ref){ MP3DecoderHelix* p_helix = (MP3DecoderHelix* )ref; - if (p_helix!=nullptr){ + if (p_helix!=nullptr && p_helix->audioChangeMP3Helix!=nullptr){ TRACED(); AudioInfo baseInfo; baseInfo.channels = i.nChans; baseInfo.sample_rate = i.samprate; baseInfo.bits_per_sample = i.bitsPerSample; - baseInfo.logInfo("MP3DecoderHelix"); - p_helix->notifyAudioChange(baseInfo); - } else { - LOGE("Wrong Libhelix Version"); + p_helix->audioChangeMP3Helix->setAudioInfo(baseInfo); } } + /// Activates a filter that makes sure that helix does not get any metadata segments + void setFilterMetaData(bool filter){ + use_filter = filter; + } + + /// Check if the metadata filter is active + bool isFilterMetaData() { + return use_filter; + } + /// Provides the maximum frame size - this is allocated on the heap and you can reduce the heap size my minimizing this value size_t maxFrameSize() { return mp3->maxFrameSize(); @@ -155,6 +157,7 @@ class MP3DecoderHelix : public AudioDecoder { mp3->setMaxFrameSize(len); } +#ifdef HELIX_PCM_CORRECTED /// Provides the maximum pwm buffer size - this is allocated on the heap and you can reduce the heap size my minimizing this value size_t maxPCMSize() { return mp3->maxPCMSize(); @@ -164,8 +167,24 @@ class MP3DecoderHelix : public AudioDecoder { void setMaxPCMSize(size_t len) { mp3->setMaxPCMSize(len); } +#else + /// Provides the maximum pwm buffer size - this is allocated on the heap and you can reduce the heap size my minimizing this value + size_t maxPCMSize() { + return mp3->maxPWMSize(); + } + + /// Define your optimized maximum pwm buffer size + void setMaxPCMSize(size_t len) { + mp3->setMaxPWMSize(len); + } +#endif protected: libhelix::MP3DecoderHelix *mp3=nullptr; + MetaDataFilter filter; + bool use_filter = false; + // audio change notification target + AudioInfoSupport *audioChangeMP3Helix=nullptr; + }; diff --git a/src/AudioTools/AudioCodecs/CodecMP3LAME.h b/src/AudioCodecs/CodecMP3LAME.h similarity index 67% rename from src/AudioTools/AudioCodecs/CodecMP3LAME.h rename to src/AudioCodecs/CodecMP3LAME.h index f6b52fbca4..366cc75c31 100644 --- a/src/AudioTools/AudioCodecs/CodecMP3LAME.h +++ b/src/AudioCodecs/CodecMP3LAME.h @@ -1,6 +1,6 @@ #pragma once -#include "AudioTools/AudioCodecs/AudioCodecsBase.h" +#include "AudioCodecs/AudioEncoded.h" #include "MP3EncoderLAME.h" namespace audio_tools { @@ -58,34 +58,39 @@ class MP3EncoderLAME : public AudioEncoder { /// Defines the Audio Info void setAudioInfo(AudioInfo from) { TRACED(); - AudioEncoder::setAudioInfo(from); - lame_info.channels = from.channels; - lame_info.sample_rate = from.sample_rate; - lame_info.bits_per_sample = from.bits_per_sample; + info.channels = from.channels; + info.sample_rate = from.sample_rate; + info.bits_per_sample = from.bits_per_sample; } /// Defines the Audio Info void setAudioInfo(AudioInfoLAME from) { TRACED(); - lame_info = from; + info = from; } - bool begin(AudioInfoLAME from) { - setAudioInfo(from); - return begin(); + // starts the processing + void begin() { + createEnc(); + enc->begin(); } - // starts the processing - bool begin() { + /** + * @brief Opens the encoder + * + * @param info + * @return int + */ + void begin(AudioInfoLAME info) { + TRACED(); createEnc(); - if (enc==nullptr) return false; + setAudioInfo(info); enc->begin(); - return true; } - AudioInfoLAME &audioInfoExt(){ - return lame_info; + AudioInfoLAME &audioInfo(){ + return info; } AudioInfoLAME defaultConfig(){ @@ -94,10 +99,10 @@ class MP3EncoderLAME : public AudioEncoder { } // convert PCM data to convert into MP3 - size_t write(const uint8_t *data, size_t len){ + size_t write(const void *in_ptr, size_t in_size){ if (enc==nullptr) return 0; - LOGD("write %d bytes", (int) len); - return enc->write((uint8_t*)data, len); + LOGD("write %d bytes", (int) in_size); + return enc->write((uint8_t*)in_ptr, in_size); } // release resources @@ -124,7 +129,7 @@ class MP3EncoderLAME : public AudioEncoder { protected: liblame::MP3EncoderLAME *enc=nullptr; - AudioInfoLAME lame_info; + AudioInfoLAME info; Print *p_print=nullptr; // Create enc only at begin so that we can use psram @@ -137,11 +142,16 @@ class MP3EncoderLAME : public AudioEncoder { } else { LOGE("Output undefined"); } - LOGI("LibLAME channels: %d", lame_info.channels); - LOGI("LibLAME sample_rate: %d", lame_info.sample_rate); - LOGI("LibLAME bits_per_sample: %d", lame_info.bits_per_sample); - LOGI("LibLAME quality: %d", lame_info.quality); - enc->setAudioInfo(lame_info); + AudioInfoLAME tmp; + tmp.channels = info.channels; + tmp.sample_rate = info.sample_rate; + tmp.bits_per_sample = info.bits_per_sample; + tmp.quality = info.quality; + LOGI("LibLAME channels: %d", tmp.channels); + LOGI("LibLAME sample_rate: %d", tmp.sample_rate); + LOGI("LibLAME bits_per_sample: %d", tmp.bits_per_sample); + LOGI("LibLAME quality: %d", tmp.quality); + enc->setAudioInfo(tmp); } } diff --git a/src/AudioTools/AudioCodecs/CodecMP3MAD.h b/src/AudioCodecs/CodecMP3MAD.h similarity index 88% rename from src/AudioTools/AudioCodecs/CodecMP3MAD.h rename to src/AudioCodecs/CodecMP3MAD.h index 0ad219197c..93f25d6043 100755 --- a/src/AudioTools/AudioCodecs/CodecMP3MAD.h +++ b/src/AudioCodecs/CodecMP3MAD.h @@ -5,14 +5,14 @@ #define LOGGING_ACTIVE true #include "Stream.h" -#include "AudioTools/AudioCodecs/AudioCodecsBase.h" +#include "AudioCodecs/AudioEncoded.h" #include "MP3DecoderMAD.h" namespace audio_tools { // forward audio changes -static AudioInfoSupport *audioChangeMAD=nullptr; +AudioInfoSupport *audioChangeMAD; /** * @brief MP3 Decoder using https://github.com/pschatzmann/arduino-libmad @@ -44,7 +44,7 @@ class MP3DecoderMAD : public AudioDecoder { delete mad; } - void setOutput(Print &out) override { + void setOutput(Print &out){ TRACED(); mad->setOutput(out); } @@ -62,14 +62,13 @@ class MP3DecoderMAD : public AudioDecoder { } /// Starts the processing - bool begin() override { + void begin(){ TRACED(); mad->begin(); - return true; } /// Releases the reserved memory - void end() override{ + void end(){ TRACED(); mad->end(); } @@ -80,7 +79,7 @@ class MP3DecoderMAD : public AudioDecoder { return mad->audioInfo(); } - AudioInfo audioInfo() override { + AudioInfo audioInfo(){ TRACED(); libmad::MadAudioInfo info = audioInfoEx(); AudioInfo base; @@ -91,7 +90,7 @@ class MP3DecoderMAD : public AudioDecoder { } /// Makes the mp3 data available for decoding: however we recommend to provide the data via a callback or input stream - size_t write(const uint8_t *data, size_t len) override { + size_t write(const void *data, size_t len){ TRACED(); return mad->write(data,len); } @@ -103,7 +102,7 @@ class MP3DecoderMAD : public AudioDecoder { } /// Returns true as long as we are processing data - operator bool() override{ + operator bool(){ return (bool)*mad; } @@ -123,7 +122,7 @@ class MP3DecoderMAD : public AudioDecoder { } } - virtual void addNotifyAudioChange(AudioInfoSupport &bi) override { + virtual void setNotifyAudioChange(AudioInfoSupport &bi) { TRACED(); audioChangeMAD = &bi; // register audio change handler diff --git a/src/AudioTools/AudioCodecs/CodecMP3Mini.h b/src/AudioCodecs/CodecMP3Mini.h similarity index 84% rename from src/AudioTools/AudioCodecs/CodecMP3Mini.h rename to src/AudioCodecs/CodecMP3Mini.h index e50129744a..4b9c79483c 100644 --- a/src/AudioTools/AudioCodecs/CodecMP3Mini.h +++ b/src/AudioCodecs/CodecMP3Mini.h @@ -10,7 +10,7 @@ #define MINIMP3_MAX_SAMPLE_RATE 44100 #endif -#include "AudioTools/AudioCodecs/AudioCodecsBase.h" +#include "AudioCodecs/AudioEncoded.h" #include "minimp3.h" @@ -38,8 +38,12 @@ class MP3DecoderMini : public AudioDecoder { void setBufferLength(int len) { buffer_size = len; } + void setNotifyAudioChange(AudioInfoSupport &bi) { + audioBaseInfoSupport = &bi; + } + /// Starts the processing - bool begin() { + void begin() { TRACED(); //esp_task_wdt_delete(nullptr); ::mp3dec_init(&mp3d); @@ -47,7 +51,6 @@ class MP3DecoderMini : public AudioDecoder { pcm.resize(MINIMP3_MAX_SAMPLES_PER_FRAME); buffer_pos = 0; active = true; - return true; } /// Releases the reserved memory @@ -60,8 +63,11 @@ class MP3DecoderMini : public AudioDecoder { /// Defines the output Stream void setOutput(Print &outStream) { this->out = &outStream; } + /// Provides the last available MP3FrameInfo + AudioInfo audioInfo() { return audio_info; } + /// Write mp3 data to decoder - size_t write(const uint8_t *data, size_t len) { + size_t write(const void *data, size_t len) { LOGD("write: %zu", len); if (active) { if (buffer_pos+len>=buffer.size()){ @@ -89,6 +95,8 @@ class MP3DecoderMini : public AudioDecoder { } protected: + AudioInfo audio_info; + AudioInfoSupport *audioBaseInfoSupport = nullptr; Print *out = nullptr; mp3dec_t mp3d; mp3dec_frame_info_t mp3dec_info; @@ -130,18 +138,18 @@ class MP3DecoderMini : public AudioDecoder { /// Provides Metadata and PCM data void provideResult(int samples) { LOGD("provideResult: %d samples", samples); - AudioInfo tmp; - tmp.sample_rate = mp3dec_info.hz>sample_rate_limit ? sample_rate_limit : mp3dec_info.hz; - tmp.channels = mp3dec_info.channels; - tmp.bits_per_sample = 16; + AudioInfo info; + info.sample_rate = mp3dec_info.hz>sample_rate_limit ? sample_rate_limit : mp3dec_info.hz; + info.channels = mp3dec_info.channels; + info.bits_per_sample = 16; // notify about audio changes - if (tmp != info) { - tmp.logInfo(); - notifyAudioChange(tmp); + if (audioBaseInfoSupport != nullptr && info != audio_info) { + info.logInfo(); + audioBaseInfoSupport->setAudioInfo(info); } - // store last info so that we can detect any changes - info = info; + // store last audio_info so that we can detect any changes + audio_info = info; // provide result pwm data if (out != nullptr) { diff --git a/src/AudioTools/AudioCodecs/CodecOpus.h b/src/AudioCodecs/CodecOpus.h similarity index 84% rename from src/AudioTools/AudioCodecs/CodecOpus.h rename to src/AudioCodecs/CodecOpus.h index cbd860f591..648df97c57 100644 --- a/src/AudioTools/AudioCodecs/CodecOpus.h +++ b/src/AudioCodecs/CodecOpus.h @@ -1,6 +1,6 @@ #pragma once -#include "AudioTools/AudioCodecs/AudioCodecsBase.h" +#include "AudioCodecs/AudioEncoded.h" #include "Print.h" #include "opus.h" @@ -9,7 +9,7 @@ #endif #ifndef OPUS_DEC_MAX_BUFFER_SIZE -#define OPUS_DEC_MAX_BUFFER_SIZE 4*1024 +#define OPUS_DEC_MAX_BUFFER_SIZE 1024 #endif @@ -30,8 +30,6 @@ struct OpusSettings : public AudioInfo { bits_per_sample = 16; } int max_buffer_size = OPUS_DEC_MAX_BUFFER_SIZE; - int max_buffer_write_size = 512; - }; /** @@ -90,7 +88,7 @@ struct OpusEncoderSettings : public OpusSettings { /// OPUS_BANDWIDTH_FULLBAND int max_bandwidth = -1; /// OPUS_AUTO, OPUS_SIGNAL_VOICE, OPUS_SIGNAL_MUSIC - int signal = -1; + int singal = -1; /// 0, 1 int inband_fec = -1; /// 0, 1, 2, 5 @@ -117,7 +115,7 @@ class OpusAudioDecoder : public AudioDecoder { /** * @brief Construct a new OpusDecoder object */ - OpusAudioDecoder() = default; + OpusAudioDecoder() { TRACED(); } /** * @brief Construct a new OpusDecoder object @@ -132,53 +130,47 @@ class OpusAudioDecoder : public AudioDecoder { /// Defines the output Stream void setOutput(Print &out_stream) override { p_print = &out_stream; } + void setNotifyAudioChange(AudioInfoSupport &bi) override { + this->p_notify = &bi; + } + AudioInfo audioInfo() override { return cfg; } /// Provides access to the configuration OpusSettings &config() { return cfg; } OpusSettings &defaultConfig() { return cfg; } - bool begin(OpusSettings settings) { + void begin(OpusSettings settings) { TRACED(); AudioDecoder::setAudioInfo(settings); cfg = settings; - notifyAudioChange(cfg); - return begin(); + if (p_notify != nullptr) { + p_notify->setAudioInfo(cfg); + } + begin(); } - bool begin() override { + void begin() override { TRACED(); - if (!isValidRate(cfg.sample_rate)){ - LOGE("Sample rate not supported: %d", cfg.sample_rate); - return false; - } outbuf.resize(cfg.max_buffer_size); assert(outbuf.data() != nullptr); - // int err; - // dec = opus_decoder_create(cfg.sample_rate, cfg.channels, &err); - - size_t size = opus_decoder_get_size(cfg.channels); - decbuf.resize(size); - assert(decbuf.data() != nullptr); - dec = (OpusDecoder*)decbuf.data(); - int err = opus_decoder_init(dec, cfg.sample_rate, cfg.channels); - - + int err; + dec = opus_decoder_create(cfg.sample_rate, cfg.channels, &err); if (err != OPUS_OK) { LOGE("opus_decoder_create: %s for sample_rate: %d, channels:%d", opus_strerror(err), cfg.sample_rate, cfg.channels); - return false; + return; } active = true; - return true; } void end() override { TRACED(); - dec = nullptr; - outbuf.resize(0); - decbuf.resize(0); + if (dec) { + opus_decoder_destroy(dec); + dec = nullptr; + } active = false; } @@ -190,50 +182,33 @@ class OpusAudioDecoder : public AudioDecoder { cfg.bits_per_sample = from.bits_per_sample; } - size_t write(const uint8_t *data, size_t len) override { + size_t write(const void *in_ptr, size_t in_size) override { if (!active || p_print == nullptr) return 0; // decode data - LOGD("OpusAudioDecoder::write: %d", (int)len); + LOGD("OpusAudioDecoder::write: %d", (int)in_size); int in_band_forward_error_correction = 0; - int frame_count = cfg.max_buffer_size / cfg.channels / sizeof(opus_int16); int out_samples = opus_decode( - dec, (uint8_t *)data, len, (opus_int16 *)outbuf.data(), - frame_count, in_band_forward_error_correction); + dec, (uint8_t *)in_ptr, in_size, (opus_int16 *)outbuf.data(), + cfg.max_buffer_size, in_band_forward_error_correction); if (out_samples < 0) { LOGW("opus-decode: %s", opus_strerror(out_samples)); } else if (out_samples > 0) { // write data to final destination int out_bytes = out_samples * cfg.channels * sizeof(int16_t); LOGD("opus-decode: %d", out_bytes); - int open = out_bytes; - int processed = 0; - while(open>0){ - int to_write = std::min(open, cfg.max_buffer_write_size); - int written = p_print->write(outbuf.data()+processed, to_write); - open -= written; - processed += written; - } + p_print->write(outbuf.data(), out_bytes); } - return len; + return in_size; } operator bool() override { return active; } protected: Print *p_print = nullptr; - OpusDecoder *dec = nullptr; OpusSettings cfg; - bool active = false; + OpusDecoder *dec; + bool active; Vector outbuf{0}; - Vector decbuf{0}; - const uint32_t valid_rates[5] = {8000, 12000, 16000, 24000, 48000}; - - bool isValidRate(int rate){ - for (auto &valid : valid_rates){ - if (valid==rate) return true; - } - return false; - } }; /** @@ -246,7 +221,7 @@ class OpusAudioDecoder : public AudioDecoder { class OpusAudioEncoder : public AudioEncoder { public: // Empty Constructor - the output stream must be provided with begin() - OpusAudioEncoder() = default; + OpusAudioEncoder() {} // Constructor providing the output stream OpusAudioEncoder(Print &out) { setOutput(out); } @@ -259,14 +234,13 @@ class OpusAudioEncoder : public AudioEncoder { /// We actually do nothing with this void setAudioInfo(AudioInfo from) override { - AudioEncoder::setAudioInfo(from); cfg.sample_rate = from.sample_rate; cfg.channels = from.channels; cfg.bits_per_sample = from.bits_per_sample; } /// starts the processing using the actual OpusAudioInfo - bool begin() override { + void begin() override { int err; int size = getFrameSizeSamples(cfg.sample_rate) * 2; frame.resize(size); @@ -275,20 +249,18 @@ class OpusAudioEncoder : public AudioEncoder { if (err != OPUS_OK) { LOGE("opus_encoder_create: %s for sample_rate: %d, channels:%d", opus_strerror(err), cfg.sample_rate, cfg.channels); - return false; + return; } is_open = settings(); - return true; } /// Provides access to the configuration OpusEncoderSettings &config() { return cfg; } - OpusEncoderSettings &defaultConfig() { return cfg; } - bool begin(OpusEncoderSettings settings) { + void begin(OpusEncoderSettings settings) { cfg = settings; - return begin(); + begin(); } /// stops the processing @@ -301,15 +273,16 @@ class OpusAudioEncoder : public AudioEncoder { } /// Writes PCM data to be encoded as Opus - size_t write(const uint8_t *data, size_t len) override { + size_t write(const void *in_ptr, size_t in_size) override { if (!is_open || p_print == nullptr) return 0; - LOGD("OpusAudioEncoder::write: %d", (int)len); + LOGD("OpusAudioEncoder::write: %d", (int)in_size); // fill frame - for (int j = 0; j < len; j++) { - encodeByte(data[j]); + uint8_t *p_byte = (uint8_t *)in_ptr; + for (int j = 0; j < in_size; j++) { + encodeByte(p_byte[j]); } - return len; + return in_size; } operator bool() override { return is_open; } @@ -417,9 +390,9 @@ class OpusAudioEncoder : public AudioEncoder { LOGE("invalid max_bandwidth: %d", cfg.max_bandwidth); ok = false; } - if (cfg.signal >= 0 && - opus_encoder_ctl(enc, OPUS_SET_SIGNAL(cfg.signal)) != OPUS_OK) { - LOGE("invalid signal: %d", cfg.signal); + if (cfg.singal >= 0 && + opus_encoder_ctl(enc, OPUS_SET_SIGNAL(cfg.singal)) != OPUS_OK) { + LOGE("invalid singal: %d", cfg.singal); ok = false; } if (cfg.inband_fec >= 0 && diff --git a/src/AudioTools/AudioCodecs/CodecOpusOgg.h b/src/AudioCodecs/CodecOpusOgg.h similarity index 93% rename from src/AudioTools/AudioCodecs/CodecOpusOgg.h rename to src/AudioCodecs/CodecOpusOgg.h index d2a39f3644..486a2dcfc7 100644 --- a/src/AudioTools/AudioCodecs/CodecOpusOgg.h +++ b/src/AudioCodecs/CodecOpusOgg.h @@ -1,7 +1,7 @@ #pragma once -#include "AudioTools/AudioCodecs/CodecOpus.h" -#include "AudioTools/AudioCodecs/ContainerOgg.h" +#include "AudioCodecs/CodecOpus.h" +#include "AudioCodecs/ContainerOgg.h" namespace audio_tools { @@ -46,20 +46,20 @@ class OpusOggDecoder : public OggContainerDecoder { /// Provides access to the Opus configuration OpusSettings &config() { return dec.config(); } - bool begin(OpusSettings settings) { + void begin(OpusSettings settings) { OggContainerDecoder::begin(); - return dec.begin(settings); + dec.begin(settings); } - bool begin() override { + void begin() override { TRACED(); OggContainerDecoder::begin(); - return dec.begin(); + dec.begin(); } void end() override { TRACED(); - OggContainerDecoder::end(); + OggContainerDecoder::begin(); dec.end(); } diff --git a/src/AudioTools/AudioCodecs/CodecSBC.h b/src/AudioCodecs/CodecSBC.h similarity index 89% rename from src/AudioTools/AudioCodecs/CodecSBC.h rename to src/AudioCodecs/CodecSBC.h index 5161e637e5..4935befe15 100644 --- a/src/AudioTools/AudioCodecs/CodecSBC.h +++ b/src/AudioCodecs/CodecSBC.h @@ -8,7 +8,7 @@ #pragma once -#include "AudioTools/AudioCodecs/AudioCodecsBase.h" +#include "AudioCodecs/AudioEncoded.h" #include "sbc.h" #include "sbc/formats.h" @@ -38,12 +38,13 @@ class SBCDecoder : public AudioDecoder { delete[] input_buffer; } - virtual bool begin() { + virtual AudioInfo audioInfo() { return info; } + + virtual void begin() { TRACEI(); is_first = true; is_active = true; sbc_init(&sbc, 0L); - return true; } virtual void end() { @@ -52,26 +53,30 @@ class SBCDecoder : public AudioDecoder { is_active = false; } + virtual void setNotifyAudioChange(AudioInfoSupport &bi) { + p_notify = &bi; + } + virtual void setOutput(Print &out_stream) { p_print = &out_stream; } operator bool() { return is_active; } - virtual size_t write(const uint8_t *data, size_t len) { - LOGD("write: %d", len); + virtual size_t write(const void *data, size_t length) { + LOGD("write: %d", length); if (!is_active) { LOGE("inactive"); return 0; } uint8_t *start = (uint8_t *)data; - int count = len; + int count = length; if (is_first) { - framelen = firstWrite(data, len); + framelen = firstWrite(data, length); LOGI("framelen: %d", framelen); // check if we have a valid frame length if (isValidFrameLen(framelen)) { start = start + framelen; - count = len - framelen; + count = length - framelen; is_first = false; } } @@ -82,7 +87,7 @@ class SBCDecoder : public AudioDecoder { } } - return len; + return length; } @@ -97,6 +102,8 @@ class SBCDecoder : public AudioDecoder { protected: Print *p_print = nullptr; + AudioInfo info; + AudioInfoSupport *p_notify = nullptr; sbc_t sbc; bool is_first = true; bool is_active = false; @@ -136,7 +143,9 @@ class SBCDecoder : public AudioDecoder { break; } LOGI("sample_rate: %d", info.sample_rate); - notifyAudioChange(info); + if (p_notify != nullptr) { + p_notify->setAudioInfo(info); + } } bool isValidFrameLen(int len) { return len > 0 && len < 256; } @@ -234,15 +243,20 @@ class SBCEncoder : public AudioEncoder { } } + /// Starts the processing: channels 1 or 2, supported sample rates: 16000, 32000, 44100, 48000 + void begin(AudioInfo bi) { + setAudioInfo(bi); + begin(); + } + /// Restarts the processing - bool begin() { + void begin() { TRACEI(); is_first = true; is_active = setup(); current_codesize = codeSize(); buffer.resize(current_codesize); result_buffer.resize(frameLength()); - return true; } /// Ends the processing @@ -254,12 +268,14 @@ class SBCEncoder : public AudioEncoder { virtual const char *mime() { return "audio/sbc"; } + virtual void setAudioInfo(AudioInfo info) { this->info = info; } + virtual void setOutput(Print &out_stream) { p_print = &out_stream; } operator bool() { return is_active; } - virtual size_t write(const uint8_t *data, size_t len) { - LOGD("write: %d", len); + virtual size_t write(const void *in_ptr, size_t in_size) { + LOGD("write: %d", in_size); if (!is_active) { LOGE("inactive"); return 0; @@ -269,12 +285,13 @@ class SBCEncoder : public AudioEncoder { return 0; } + const uint8_t *start = (const uint8_t *)in_ptr; // encode bytes - for (int j = 0; j < len; j++) { - processByte(data[j]); + for (int j = 0; j < in_size; j++) { + processByte(start[j]); } - return len; + return in_size; } @@ -286,6 +303,7 @@ class SBCEncoder : public AudioEncoder { } protected: + AudioInfo info; Print *p_print = nullptr; sbc_t sbc; bool is_first = true; diff --git a/src/AudioTools/AudioCodecs/CodecVorbis.h b/src/AudioCodecs/CodecVorbis.h similarity index 55% rename from src/AudioTools/AudioCodecs/CodecVorbis.h rename to src/AudioCodecs/CodecVorbis.h index e70a0bb77e..f062f9a2ae 100644 --- a/src/AudioTools/AudioCodecs/CodecVorbis.h +++ b/src/AudioCodecs/CodecVorbis.h @@ -1,24 +1,21 @@ #pragma once -#include "AudioToolsConfig.h" -#include "AudioTools/AudioCodecs/AudioCodecsBase.h" -#include "vorbis-tremor.h" -// #include "AudioTools/AudioCodecs/ContainerOgg.h" -// #include "ivorbiscodec.h" -// #include "ivorbisfile.h" +#include "AudioCodecs/ContainerOgg.h" +#include "AudioCodecs/AudioEncoded.h" +#include "ivorbiscodec.h" +#include "ivorbisfile.h" namespace audio_tools { #ifndef VARBIS_MAX_READ_SIZE -# define VARBIS_MAX_READ_SIZE 256 +#define VARBIS_MAX_READ_SIZE 512 #endif -#define VORBIS_HEADER_OPEN_LIMIT 1024 - /** * @brief Vorbis Streaming Decoder using - * https://github.com/pschatzmann/arduino-libvorbis-tremor + * https://github.com/pschatzmann/arduino-libvorbis-idec + * https://github.com/pschatzmann/arduino-libopus.git * @ingroup codecs * @ingroup decoder * @author Phil Schatzmann @@ -35,33 +32,40 @@ class VorbisDecoder : public StreamingDecoder { } } + void setNotifyAudioChange(AudioInfoSupport &bi) override { + p_notify = &bi; + } + /// Starts the processing - bool begin() override { + void begin() override { LOGI("begin"); callbacks.read_func = read_func; callbacks.seek_func = seek_func; - callbacks.close_func = nullptr; + callbacks.close_func = close_func; callbacks.tell_func = tell_func; - if (p_input->available()>=VORBIS_HEADER_OPEN_LIMIT){ - ovOpen(); - } + int rc = ov_open_callbacks(this, &file, nullptr, 0, callbacks); + + pcm.resize(VARBIS_MAX_READ_SIZE); active = true; is_first = true; - return true; } /// Releases the reserved memory void end() override { LOGI("end"); - is_ov_open = false; - is_first = true; - if (active) ov_clear(&file); active = false; + ov_clear(&file); } + /// Defines the output Stream + void setOutput(Print &outStream) override { this->p_out = &outStream; } + + /// Defines the output Stream + void setInputStream(Stream &inStream) override { this->p_in = &inStream; } + /// Provides the last available MP3FrameInfo AudioInfo audioInfo() override { return cfg; } @@ -69,96 +73,69 @@ class VorbisDecoder : public StreamingDecoder { virtual operator bool() override { return active; } virtual bool copy() override { - // wait for data + LOGI("copy"); + if (is_first){ - // wait for some data - if(p_input->available()available()==0){ + delay(1); } - LOGI("available: %d", p_input->available()); is_first = false; } - // open if not already done - if (!is_ov_open){ - if (!ovOpen()) { - LOGE("not open"); - return false; - } - } - - if(pcm.data()==nullptr){ - LOGE("Not enough memory"); - return false; - } - // convert to pcm long result = ov_read(&file, (char *)pcm.data(), pcm.size(), &bitstream); - LOGI("copy: %ld", result); if (result > 0) { AudioInfo current = currentInfo(); if (current != cfg) { cfg = current; cfg.logInfo(); - notifyAudioChange(cfg); + if (p_notify!=nullptr){ + p_notify->setAudioInfo(cfg); + } else { + LOGW("p_notify is null"); + } } - p_print->write(pcm.data(), result); - delay(1); + p_out->write(pcm.data(), result); return true; } else { - if (result==0 || result==-3){ - // data interruption - LOGD("copy: %ld - %s", result, readError(result)); - } else { - LOGE("copy: %ld - %s", result, readError(result)); - } - + LOGE("copy: %ld - %s", result, readError(result)); return false; } } protected: AudioInfo cfg; + AudioInfoSupport *p_notify = nullptr; + Print *p_out = nullptr; + Stream *p_in = nullptr; Vector pcm; OggVorbis_File file; ov_callbacks callbacks; + bool active; int bitstream; - bool active = false; bool is_first = true; - bool is_ov_open = false; - - bool ovOpen(){ - pcm.resize(VARBIS_MAX_READ_SIZE); - int rc = ov_open_callbacks(this, &file, nullptr, 0, callbacks); - if (rc<0){ - LOGE("ov_open_callbacks: %d", rc); - } else { - is_ov_open = true; - } - return is_ov_open; - } AudioInfo currentInfo() { AudioInfo result; vorbis_info *info = ov_info(&file, -1); result.sample_rate = info->rate; result.channels = info->channels; - result.bits_per_sample = 16; + result.bits_per_sample = 2; return result; } - virtual size_t readBytes(uint8_t *data, size_t len) override { - size_t read_size = min(len,(size_t)VARBIS_MAX_READ_SIZE); - size_t result = p_input->readBytes((uint8_t *)data, read_size); - LOGD("readBytes: %zu",result); + virtual size_t readBytes(uint8_t *ptr, size_t size) override { + size_t read_size = min(size,(size_t)VARBIS_MAX_READ_SIZE); + size_t result = p_in->readBytes((uint8_t *)ptr, read_size); + LOGD("readBytes: %ld",result); return result; } static size_t read_func(void *ptr, size_t size, size_t nmemb, void *datasource) { VorbisDecoder *self = (VorbisDecoder *)datasource; - return self->readBytes((uint8_t *)ptr, size * nmemb); + return self->readBytes((uint8_t *)ptr, size); } static int seek_func(void *datasource, ogg_int64_t offset, int whence) { @@ -171,16 +148,13 @@ class VorbisDecoder : public StreamingDecoder { return -1; } - // static int close_func(void *datasource) { - // VorbisDecoder *self = (VorbisDecoder *)datasource; - // self->end(); - // return 0; - // } + static int close_func(void *datasource) { + VorbisDecoder *self = (VorbisDecoder *)datasource; + self->end(); + return 0; + } const char* readError(long error){ - if (error>=0){ - return "OK"; - } switch(error){ case OV_HOLE: return "Interruption in the data"; diff --git a/src/AudioTools/AudioCodecs/CodecWAV.h b/src/AudioCodecs/CodecWAV.h similarity index 63% rename from src/AudioTools/AudioCodecs/CodecWAV.h rename to src/AudioCodecs/CodecWAV.h index c769c4f0d3..54cb5815f5 100644 --- a/src/AudioTools/AudioCodecs/CodecWAV.h +++ b/src/AudioCodecs/CodecWAV.h @@ -1,12 +1,13 @@ #pragma once -#include "AudioTools/AudioCodecs/AudioCodecsBase.h" -#include "AudioTools/AudioCodecs/AudioFormat.h" -#include "AudioTools/AudioCodecs/AudioEncoded.h" -#include "AudioTools/CoreAudio/AudioBasic/StrView.h" +#include "AudioCodecs/AudioEncoded.h" +#include "AudioCodecs/AudioFormat.h" + +#define TAG(a, b, c, d) \ + ((static_cast(a) << 24) | (static_cast(b) << 16) | \ + (static_cast(c) << 8) | (d)) #define READ_BUFFER_SIZE 512 -#define MAX_WAV_HEADER_LEN 200 namespace audio_tools { @@ -49,54 +50,94 @@ class WAVHeader { /// Adds data to the 44 byte wav header data buffer and make it available for parsing int write(uint8_t *data, size_t data_len) { - return buffer.writeArray(data, data_len); + int write_len = min(data_len, 44 - len); + memmove(buffer, data + len, write_len); + len += write_len; + LOGI("WAVHeader::write: %u -> %d -> %d", (unsigned)data_len, write_len, + (int)len); + return write_len; } /// Call begin when header data is complete to parse the data - bool parse() { - LOGI("WAVHeader::begin: %u", (unsigned)buffer.available()); + void parse() { + LOGI("WAVHeader::begin: %u", (unsigned)len); this->data_pos = 0l; memset((void *)&headerInfo, 0, sizeof(WAVAudioInfo)); - - if (!setPos("RIFF")) return false; - headerInfo.file_size = read_int32(); - if (!setPos("WAVE")) return false; - if (!setPos("fmt ")) return false; - int fmt_length = read_int32(); - headerInfo.format = (AudioFormat)read_int16(); - headerInfo.channels = read_int16(); - headerInfo.sample_rate = read_int32(); - headerInfo.byte_rate = read_int32(); - headerInfo.block_align = read_int16(); - headerInfo.bits_per_sample = read_int16(); - if (!setPos("data")) return false; - headerInfo.data_length = read_int32(); - if (headerInfo.data_length==0 || headerInfo.data_length >= 0x7fff0000) { - headerInfo.is_streamed = true; - headerInfo.data_length = ~0; + while (!eof()) { + uint32_t tag, tag2, length; + tag = read_tag(); + if (eof()) break; + length = read_int32(); + if (!length || length >= 0x7fff0000) { + headerInfo.is_streamed = true; + length = ~0; + } + if (tag != TAG('R', 'I', 'F', 'F') || length < 4) { + seek(length, SEEK_CUR); + continue; + } + tag2 = read_tag(); + length -= 4; + if (tag2 != TAG('W', 'A', 'V', 'E')) { + seek(length, SEEK_CUR); + continue; + } + // RIFF chunk found, iterate through it + while (length >= 8) { + uint32_t subtag, sublength; + subtag = read_tag(); + if (eof()) break; + sublength = read_int32(); + length -= 8; + if (length < sublength) break; + if (subtag == TAG('f', 'm', 't', ' ')) { + if (sublength < 16) { + // Insufficient data for 'fmt ' + break; + } + headerInfo.format = (AudioFormat)read_int16(); + headerInfo.channels = read_int16(); + headerInfo.sample_rate = read_int32(); + headerInfo.byte_rate = read_int32(); + headerInfo.block_align = read_int16(); + headerInfo.bits_per_sample = read_int16(); + if (headerInfo.format == (AudioFormat) 0xfffe) { + if (sublength < 28) { + // Insufficient data for waveformatex + break; + } + skip(8); + headerInfo.format = (AudioFormat)read_int32(); + skip(sublength - 28); + } else { + skip(sublength - 16); + } + headerInfo.is_valid = true; + } else if (subtag == TAG('d', 'a', 't', 'a')) { + sound_pos = tell(); + headerInfo.data_length = sublength; + if (!headerInfo.data_length || headerInfo.is_streamed) { + headerInfo.is_streamed = true; + logInfo(); + return; + } + seek(sublength, SEEK_CUR); + } else { + skip(sublength); + } + length -= sublength; + } + if (length > 0) { + // Bad chunk? + seek(length, SEEK_CUR); + } } - logInfo(); - buffer.clear(); - return true; + len = 0; } - /// Returns true if the header is complete (containd data tag) - bool isDataComplete() { - int pos = getDataPos(); - return pos > 0 && buffer.available() >= pos; - } - - /// number of bytes available in the header buffer - size_t available(){ - return buffer.available(); - } - - /// Determines the data start position using the data tag - int getDataPos() { - int pos = StrView((char*)buffer.data(),MAX_WAV_HEADER_LEN, buffer.available()).indexOf("data"); - return pos > 0 ? pos + 8 : 0; - } + /// Returns true if the header is complete (with 44 bytes) + bool isDataComplete() { return len == 44; } /// provides the info from the header WAVAudioInfo &audioInfo() { return headerInfo; } @@ -106,57 +147,22 @@ class WAVHeader { headerInfo = info; } - /// Just write a wav header to the indicated outputbu - int writeHeader(Print *out) { + /// Just write a wav header to the indicated output + void writeHeader(Print *out) { + SingleBuffer buffer(50); writeRiffHeader(buffer); writeFMT(buffer); writeDataHeader(buffer); - int len = buffer.available(); + len = buffer.available(); out->write(buffer.data(), buffer.available()); - return len; - } - - void clear() { - data_pos = 0; - WAVAudioInfo empty; - empty.sample_rate = 0; - empty.channels = 0; - empty.bits_per_sample = 0; - headerInfo = empty; - buffer.setClearWithZero(true); - buffer.reset(); - } - - void dumpHeader() { - char msg[buffer.available()+1]; - memset(msg, 0, buffer.available()+1); - for (int j = 0; j< buffer.available();j++){ - char c = (char)buffer.data()[j]; - if (!isalpha(c)){ - c = '.'; - } - msg[j] = c; - } - LOGI("Header: %s", msg); } - protected: struct WAVAudioInfo headerInfo; - SingleBuffer buffer{MAX_WAV_HEADER_LEN}; + uint8_t buffer[44]; + size_t len = 0; size_t data_pos = 0; - - bool setPos(const char*id){ - int id_len = strlen(id); - int pos = indexOf(id); - if (pos < 0) return false; - data_pos = pos + id_len; - return true; - } - - int indexOf(const char* str){ - return StrView((char*)buffer.data(),MAX_WAV_HEADER_LEN, buffer.available()).indexOf(str); - } + size_t sound_pos = 0; uint32_t read_tag() { uint32_t tag = 0; @@ -191,8 +197,8 @@ class WAVHeader { } int getChar() { - if (data_pos < buffer.size()) - return buffer.data()[data_pos++]; + if (data_pos < len) + return buffer[data_pos++]; else return -1; } @@ -207,13 +213,13 @@ class WAVHeader { size_t tell() { return data_pos; } - bool eof() { return data_pos >= buffer.size() - 1; } + bool eof() { return data_pos >= len - 1; } void logInfo() { - LOGI("WAVHeader sound_pos: %d", getDataPos()); + LOGI("WAVHeader sound_pos: %lu", (unsigned long)sound_pos); LOGI("WAVHeader channels: %d ", headerInfo.channels); LOGI("WAVHeader bits_per_sample: %d", headerInfo.bits_per_sample); - LOGI("WAVHeader sample_rate: %d ", (int) headerInfo.sample_rate); + LOGI("WAVHeader sample_rate: %d ", headerInfo.sample_rate); LOGI("WAVHeader format: %d", (int)headerInfo.format); } @@ -261,8 +267,6 @@ class WAVHeader { * determine the format. If no AudioDecoderExt is specified we just write the PCM * data to the output that is defined by calling setOutput(). You can define a * ADPCM decoder to decode WAV files that contain ADPCM data. - * Please note that you need to call begin() everytime you process a new file to - * let the decoder know that we start with a new header. * @ingroup codecs * @ingroup decoder * @author Phil Schatzmann @@ -291,21 +295,22 @@ class WAVDecoder : public AudioDecoder { } /// Defines the output Stream - void setOutput(Print &out_stream) override { this->p_print = &out_stream; } + void setOutput(Print &out_stream) { this->p_print = &out_stream; } + + /// Defines the object that needs to be notified about audio format changes + void setNotifyAudioChange(AudioInfoSupport &bi) { + this->audioBaseInfoSupport = &bi; + } - bool begin() override { + void begin() { TRACED(); - header.clear(); setupEncodedAudio(); - buffer24.reset(); isFirst = true; active = true; - return true; } - void end() override { + void end() { TRACED(); - buffer24.reset(); active = false; } @@ -313,113 +318,53 @@ class WAVDecoder : public AudioDecoder { WAVAudioInfo &audioInfoEx() { return header.audioInfo(); } - AudioInfo audioInfo() override { return header.audioInfo(); } + AudioInfo audioInfo() { return header.audioInfo(); } - virtual size_t write(const uint8_t *data, size_t len) override { + virtual size_t write(const void *in_ptr, size_t in_size) { TRACED(); size_t result = 0; if (active) { if (isFirst) { - int data_start = decodeHeader((uint8_t*) data, len); - // we do not have the complete header yet: need more data - if (data_start == 0) return len; - // process the outstanding data - result = data_start + write_out((uint8_t *)data+data_start, len-data_start); - + result = decodeHeader((uint8_t*) in_ptr, in_size); } else if (isValid) { - result = write_out((uint8_t *)data, len); + result = out().write((uint8_t *)in_ptr, in_size); } } return result; } - virtual operator bool() override { return active; } + virtual operator bool() { return active; } protected: WAVHeader header; + AudioInfoSupport *audioBaseInfoSupport=nullptr; bool isFirst = true; bool isValid = true; bool active = false; AudioFormat decoder_format = AudioFormat::PCM; AudioDecoderExt *p_decoder = nullptr; EncodedAudioOutput dec_out; - SingleBuffer buffer24; Print& out() { return p_decoder==nullptr ? *p_print : dec_out; } - virtual size_t write_out(const uint8_t *in_ptr, size_t in_size) { - // check if we need to convert int24 data from 3 bytes to 4 bytes - size_t result = 0; - if (header.audioInfo().format == AudioFormat::PCM - && header.audioInfo().bits_per_sample == 24 && sizeof(int24_t)==4){ - write_out_24(in_ptr, in_size); - result = in_size; - } else { - result = out().write(in_ptr, in_size); - } - return result; - } - - // convert int24 to int32 - size_t write_out_24(const uint8_t *in_ptr, size_t in_size) { - // make sure we can store a frame of 24bit (3bytes) - AudioInfo& info = header.audioInfo(); - // in_size might be not a multiple of 3, so we use a buffer for a single frame - buffer24.resize(info.channels*3); - int result = 0; - int32_t frame[info.channels]; - uint8_t val24[3]={0}; - - // add all bytes to buffer - for (int j=0;j(byteArray[2]) << 24) - | (static_cast(byteArray[1]) << 16) - | (static_cast(byteArray[0]) << 8) - ); - } - - /// Decodes the header data: Returns the start pos of the data int decodeHeader(uint8_t *in_ptr, size_t in_size) { - int result = in_size; + int result = 0; // we expect at least the full header int written = header.write(in_ptr, in_size); if (!header.isDataComplete()) { - LOGW("WAV header misses 'data' section in len: %d", (int) header.available()); - header.dumpHeader(); - return 0; + return written; } // parse header - if (!header.parse()){ - LOGE("WAV header parsing failed"); - return 0; - } + header.parse(); + size_t len = in_size - written; + uint8_t *sound_ptr = (uint8_t *)in_ptr + written; isFirst = false; isValid = header.audioInfo().is_valid; - LOGI("WAV sample_rate: %d", (int) header.audioInfo().sample_rate); + LOGI("WAV sample_rate: %d", header.audioInfo().sample_rate); LOGI("WAV data_length: %u", (unsigned)header.audioInfo().data_length); LOGI("WAV is_streamed: %d", header.audioInfo().is_streamed); LOGI("WAV is_valid: %s", @@ -440,11 +385,24 @@ class WAVDecoder : public AudioDecoder { bi.sample_rate = header.audioInfo().sample_rate; bi.channels = header.audioInfo().channels; bi.bits_per_sample = header.audioInfo().bits_per_sample; - notifyAudioChange(bi); + // we provide some functionality so that we could check if the + // destination supports the requested format + if (audioBaseInfoSupport != nullptr) { + isValid = audioBaseInfoSupport->validate(bi); + if (isValid) { + LOGI("isValid: %s", isValid ? "true" : "false"); + audioBaseInfoSupport->setAudioInfo(bi); + // write prm data from first record + LOGI("WAVDecoder writing first sound data"); + result = out().write(sound_ptr, len); + } else { + LOGE("isValid: %s", isValid ? "true" : "false"); + } + } } else { LOGE("WAV format not supported: %d", (int)format); } - return header.getDataPos(); + return result; } void setupEncodedAudio() { @@ -452,7 +410,7 @@ class WAVDecoder : public AudioDecoder { assert(p_print!=nullptr); dec_out.setOutput(p_print); dec_out.setDecoder(p_decoder); - dec_out.begin(info); + dec_out.begin(); } } }; @@ -520,10 +478,8 @@ class WAVEncoder : public AudioEncoder { /// Defines the WAVAudioInfo virtual void setAudioInfo(WAVAudioInfo ai) { - AudioEncoder::setAudioInfo(ai); - if (p_encoder) p_encoder->setAudioInfo(ai); audioInfo = ai; - LOGI("sample_rate: %d", (int)audioInfo.sample_rate); + LOGI("sample_rate: %d", audioInfo.sample_rate); LOGI("channels: %d", audioInfo.channels); // bytes per second audioInfo.byte_rate = audioInfo.sample_rate * audioInfo.channels * audioInfo.bits_per_sample / 8; @@ -543,26 +499,24 @@ class WAVEncoder : public AudioEncoder { } /// starts the processing - bool begin(WAVAudioInfo ai) { - header.clear(); + void begin(WAVAudioInfo ai) { setAudioInfo(ai); - return begin(); + begin(); } /// starts the processing using the actual WAVAudioInfo - virtual bool begin() override { + virtual void begin() override { TRACED(); setupEncodedAudio(); header_written = false; is_open = true; - return true; } /// stops the processing void end() override { is_open = false; } /// Writes PCM data to be encoded as WAV - virtual size_t write(const uint8_t *data, size_t len) override { + virtual size_t write(const void *in_ptr, size_t in_size) override { if (!is_open) { LOGE("The WAVEncoder is not open - please call begin()"); return 0; @@ -576,18 +530,18 @@ class WAVEncoder : public AudioEncoder { if (!header_written) { LOGI("Writing Header"); header.setAudioInfo(audioInfo); - int len = header.writeHeader(p_print); - audioInfo.file_size -= len; + header.writeHeader(p_print); + audioInfo.file_size -= 44; header_written = true; } int32_t result = 0; Print *p_out = p_encoder==nullptr ? p_print : &enc_out;; if (audioInfo.is_streamed) { - result = p_out->write((uint8_t *)data, len); + result = p_out->write((uint8_t *)in_ptr, in_size); } else if (size_limit > 0) { - size_t write_size = min((size_t)len, (size_t)size_limit); - result = p_out->write((uint8_t *)data, write_size); + size_t write_size = min((size_t)in_size, (size_t)size_limit); + result = p_out->write((uint8_t *)in_ptr, write_size); size_limit -= result; if (size_limit <= 0) { diff --git a/src/AudioTools/AudioCodecs/CodecWavIMA.h b/src/AudioCodecs/CodecWavIMA.h similarity index 91% rename from src/AudioTools/AudioCodecs/CodecWavIMA.h rename to src/AudioCodecs/CodecWavIMA.h index 49f866e439..0f1e891e24 100644 --- a/src/AudioTools/AudioCodecs/CodecWavIMA.h +++ b/src/AudioCodecs/CodecWavIMA.h @@ -1,6 +1,6 @@ #pragma once -#include "AudioTools/AudioCodecs/AudioCodecsBase.h" +#include "AudioCodecs/AudioEncoded.h" #define WAVE_FORMAT_IMA_ADPCM 0x0011 #define TAG(a, b, c, d) ((static_cast(a) << 24) | (static_cast(b) << 16) | (static_cast(c) << 8) | (d)) @@ -291,6 +291,7 @@ class WavIMADecoder : public AudioDecoder { WavIMADecoder() { TRACED(); + this->audioBaseInfoSupport = nullptr; } /** @@ -301,6 +302,7 @@ class WavIMADecoder : public AudioDecoder { WavIMADecoder(Print &out_stream, bool active=true) { TRACED(); this->out = &out_stream; + this->audioBaseInfoSupport = nullptr; this->active = active; } @@ -314,7 +316,7 @@ class WavIMADecoder : public AudioDecoder { WavIMADecoder(Print &out_stream, AudioInfoSupport &bi) { TRACED(); this->out = &out_stream; - addNotifyAudioChange(bi); + this->audioBaseInfoSupport = &bi; } ~WavIMADecoder() { @@ -327,7 +329,12 @@ class WavIMADecoder : public AudioDecoder { this->out = &out_stream; } - bool begin() { + void setNotifyAudioChange(AudioInfoSupport &bi) { + this->audioBaseInfoSupport = &bi; + } + + + void begin() { TRACED(); ima_states[0].predictor = 0; ima_states[0].step_index = 0; @@ -336,7 +343,6 @@ class WavIMADecoder : public AudioDecoder { isFirst = true; active = true; header.clearHeader(); - return true; } void end() { @@ -352,29 +358,29 @@ class WavIMADecoder : public AudioDecoder { return header.audioInfo(); } - AudioInfo audioInfo() override { + AudioInfo audioInfo() { return header.audioInfo(); } - virtual size_t write(const uint8_t *data, size_t len) { + virtual size_t write(const void *in_ptr, size_t in_size) { TRACED(); if (active) { if (isFirst) { // we expect at least the full header - int written = header.write((uint8_t*)data, len); + int written = header.write((uint8_t*)in_ptr, in_size); if (written == IMA_ERR_INVALID_CONTAINER || written == IMA_ERR_INVALID_CHUNK) { isValid = false; isFirst = false; LOGE("File is not valid"); - return len; + return in_size; } if (!header.isDataComplete()) { - return len; + return in_size; } - size_t len_open = len - written; - uint8_t *sound_ptr = (uint8_t *) data + written; + size_t len = in_size - written; + uint8_t *sound_ptr = (uint8_t *) in_ptr + written; isFirst = false; isValid = header.audioInfo().is_valid; @@ -397,16 +403,25 @@ class WavIMADecoder : public AudioDecoder { bi.channels = header.audioInfo().channels; bi.bits_per_sample = 16; remaining_bytes = header.audioInfo().data_length; - notifyAudioChange(bi); - // write prm data from first record - LOGI("WavIMADecoder writing first sound data"); - processInput(sound_ptr, len_open); + // we provide some functionality so that we could check if the destination supports the requested format + if (audioBaseInfoSupport != nullptr) { + isValid = audioBaseInfoSupport->validate(bi); + if (isValid) { + LOGI("isValid: %s", isValid ? "true" : "false"); + audioBaseInfoSupport->setAudioInfo(bi); + // write prm data from first record + LOGI("WavIMADecoder writing first sound data"); + processInput(sound_ptr, len); + } else { + LOGE("isValid: %s", isValid ? "true" : "false"); + } + } } } else if (isValid) { - processInput((uint8_t*)data, len); + processInput((uint8_t*)in_ptr, in_size); } } - return len; + return in_size; } /// Alternative API which provides the data from an input stream @@ -424,6 +439,7 @@ class WavIMADecoder : public AudioDecoder { protected: WavIMAHeader header; Print *out; + AudioInfoSupport *audioBaseInfoSupport; bool isFirst = true; bool isValid = true; bool active; diff --git a/src/AudioTools/AudioCodecs/ContainerAVI.h b/src/AudioCodecs/ContainerAVI.h similarity index 95% rename from src/AudioTools/AudioCodecs/ContainerAVI.h rename to src/AudioCodecs/ContainerAVI.h index c7f443cffa..76f1f8fe9b 100644 --- a/src/AudioTools/AudioCodecs/ContainerAVI.h +++ b/src/AudioCodecs/ContainerAVI.h @@ -1,10 +1,10 @@ #pragma once #include -#include "AudioTools/AudioCodecs/AudioCodecsBase.h" -#include "AudioTools/CoreAudio/AudioBasic/Str.h" -#include "AudioTools/AudioCodecs/AudioFormat.h" -#include "AudioTools/Video/Video.h" -#include "AudioTools/CoreAudio/Buffers.h" + +#include "AudioBasic/StrExt.h" +#include "AudioCodecs/AudioFormat.h" +#include "Video/Video.h" +#include "AudioTools/Buffers.h" #define LIST_HEADER_SIZE 12 #define CHUNK_HEADER_SIZE 8 @@ -155,7 +155,7 @@ enum ParseState { */ class ParseObject { public: - void set(size_t currentPos, StrView id, size_t size, ParseObjectType type) { + void set(size_t currentPos, Str id, size_t size, ParseObjectType type) { set(currentPos, id.c_str(), size, type); } @@ -246,7 +246,7 @@ class ParseObject { * @copyright GPLv3 */ -class AVIDecoder : public ContainerDecoder { +class AVIDecoder : public AudioDecoder { public: AVIDecoder(int bufferSize = 1024) { parse_buffer.resize(bufferSize); @@ -269,7 +269,7 @@ class AVIDecoder : public ContainerDecoder { delete p_output_audio; } - bool begin() override { + void begin() override { parse_state = ParseHeader; header_is_avi = false; is_parsing_active = true; @@ -277,7 +277,6 @@ class AVIDecoder : public ContainerDecoder { header_is_avi = false; stream_header_idx = -1; is_metadata_ready = false; - return true; } /// Defines the audio output stream - usually called by EncodedAudioStream @@ -293,9 +292,9 @@ class AVIDecoder : public ContainerDecoder { p_output_video = &out_stream; } - virtual size_t write(const uint8_t *data, size_t len) override { - LOGD("write: %d", (int)len); - int result = parse_buffer.writeArray((uint8_t *)data, len); + virtual size_t write(const void *data, size_t length) override { + LOGD("write: %d", (int)length); + int result = parse_buffer.writeArray((uint8_t *)data, length); if (is_parsing_active) { // we expect the first parse to succeed if (parse()) { @@ -307,7 +306,7 @@ class AVIDecoder : public ContainerDecoder { } else { LOGD("Parse Error"); parse_buffer.clear(); - result = len; + result = length; is_parsing_active = false; } } @@ -367,8 +366,8 @@ class AVIDecoder : public ContainerDecoder { long open_subchunk_len = 0; long current_pos = 0; long movi_end_pos = 0; - Str spaces; - Str str; + StrExt spaces; + StrExt str; char video_format[5] = {0}; bool is_metadata_ready = false; bool (*validation_cb)(AVIDecoder &avi) = nullptr; @@ -452,9 +451,9 @@ class AVIDecoder : public ContainerDecoder { if (pos >= 0) { consume(pos); ParseObject tmp = tryParseList(); - if (StrView(tmp.id()).equals("strl")) { + if (Str(tmp.id()).equals("strl")) { parse_state = ParseStrl; - } else if (StrView(tmp.id()).equals("movi")) { + } else if (Str(tmp.id()).equals("movi")) { parse_state = ParseMovi; } else { // e.g. ignore info @@ -470,7 +469,7 @@ class AVIDecoder : public ContainerDecoder { case ParseMovi: { ParseObject movi = tryParseList(); - if (StrView(movi.id()).equals("movi")) { + if (Str(movi.id()).equals("movi")) { consume(LIST_HEADER_SIZE); is_metadata_ready = true; if (validation_cb) @@ -486,7 +485,7 @@ class AVIDecoder : public ContainerDecoder { case SubChunk: { // rec is optinal ParseObject hdrl = tryParseList(); - if (StrView(hdrl.id()).equals("rec")) { + if (Str(hdrl.id()).equals("rec")) { consume(CHUNK_HEADER_SIZE); processStack(hdrl); } @@ -551,7 +550,9 @@ class AVIDecoder : public ContainerDecoder { p_decoder->setAudioInfo(info); info = p_decoder->audioInfo(); } - notifyAudioChange(info); + if (p_notify) { + p_notify->setAudioInfo(info); + } } void setupVideoInfo() { @@ -623,7 +624,7 @@ class AVIDecoder : public ContainerDecoder { ParseObject tryParseList(const char *id) { ParseObject result; - StrView &list_id = getStr(8, 4); + Str &list_id = getStr(8, 4); if (list_id.equals(id) && getStr(0, 3).equals("LIST")) { result.set(current_pos, getStr(8, 4), getInt(4), AVIList); } @@ -693,7 +694,7 @@ class AVIDecoder : public ContainerDecoder { } /// Provides the string at the indicated byte offset with the indicated length - StrView &getStr(int offset, int len) { + Str &getStr(int offset, int len) { str.setCapacity(len + 1); const char *data = (const char *)parse_buffer.data(); str.copyFrom((data + offset), len, 5); diff --git a/src/AudioCodecs/ContainerBinary.h b/src/AudioCodecs/ContainerBinary.h new file mode 100644 index 0000000000..e43346d98a --- /dev/null +++ b/src/AudioCodecs/ContainerBinary.h @@ -0,0 +1,439 @@ +/** + * @file ContainerBinary.h + * @author Phil Schatzmann + * @brief A lean and efficient container format which provides Header records + * with audio info, Audio records with the audio and Meta which + * can contain any additional information. This can be used together with a + * codec which does not transmit the audio information or has variable frame + * lengths. We expect that a single write() is providing full frames. + * + * @version 0.1 + * @date 2022-05-04 + * + * @copyright Copyright (c) 2022 + * + */ +#pragma once +#include + +#include "AudioCodecs/AudioEncoded.h" + +namespace audio_tools { + +enum class ContainerType : uint8_t { Header = 1, Audio = 2, Meta = 3, Undefined = 0 }; + +struct CommonHeader { + CommonHeader() = default; + CommonHeader(ContainerType type, uint16_t len) { + this->type = type; + this->len = len; + } + char header = '\n'; + ContainerType type; + uint16_t len; +}; + +struct SimpleContainerConfig { + SimpleContainerConfig() = default; + CommonHeader common{ContainerType::Header, sizeof(AudioInfo)}; + AudioInfo info; +}; + +struct SimpleContainerDataHeader { + CommonHeader common{ContainerType::Audio, 0}; +}; + +struct SimpleContainerMetaDataHeader { + CommonHeader common{ContainerType::Meta, 0}; +}; + +struct ProcessedResult { + ContainerType type = ContainerType::Undefined; + // total length incl header + int total_len = 0; + // processed bytes incl header of last step + int processed = 0; + // still (total) open + int open = 0; +}; + +/** + * @brief Wraps the encoded data into Config, Data, and Meta segments so that we + * can recover the audio configuration and orignial segments if this is + * relevant. We assume that a full segment is written with each call of write(); + * The segments are separated with a new line character. + * @ingroup codecs + * @ingroup encoder + * @author Phil Schatzmann + * @copyright GPLv3 + */ +class BinaryContainerEncoder : public AudioEncoder { + public: + BinaryContainerEncoder() = default; + BinaryContainerEncoder(AudioEncoder &encoder) { p_codec = &encoder; } + BinaryContainerEncoder(AudioEncoder *encoder) { p_codec = encoder; } + + void setOutput(Print &outStream) { + LOGD("BinaryContainerEncoder::setOutput: %d",is_initial_output); + if (is_initial_output) { + setupIntialOutputStream(outStream); + } else { + p_final_print = &outStream; + } + } + + void begin(AudioInfo info) { + setAudioInfo(info); + begin(); + } + + void begin() override { + TRACED(); + target.begin(); + is_beginning = true; + } + + void setAudioInfo(AudioInfo info) override { + TRACED(); + if (info != audioInfo()) { + target.setAudioInfo(info); + cfg.info = info; + } + } + + AudioInfo audioInfo() { return cfg.info; } + + /// Adds meta data segment + size_t writeMeta(const uint8_t *data, size_t len) { + LOGD("BinaryContainerEncoder::writeMeta: %d", (int)len); + meta.common.len = len; + uint8_t tmp_array[sizeof(meta)+len]; + //output((uint8_t *)&meta, sizeof(meta)); + //output((uint8_t *)&data, len); + // output in one write + memcpy(tmp_array, &meta, sizeof(meta)); + memcpy(tmp_array+sizeof(meta), data, len); + output(tmp_array, sizeof(tmp_array)); + return len; + } + + /// Add data segment. On first write we also add a AudioInfo header + size_t write(const void *data, size_t len) { + LOGD("BinaryContainerEncoder::write: %d", (int)len); + if (is_beginning) { + writeHeader(); + is_beginning = false; + } + writeAudio((uint8_t *)data, len); + return len; + } + + void end() { + target.end(); + is_initial_output = true; + } + + operator bool() { return true; }; + + virtual const char *mime() { return "audio/binary"; }; + + protected: + uint64_t packet_count = 0; + bool is_beginning = true; + int repeat_header; + SimpleContainerConfig cfg; + SimpleContainerDataHeader dh; + SimpleContainerMetaDataHeader meta; + AudioEncoder *p_codec = nullptr; + AudioWriter *p_print1 = nullptr; + AudioWriter *p_print2 = nullptr; + ContainerTargetPrint target; + bool is_initial_output = true; + Print *p_final_print = nullptr; + + void setupIntialOutputStream(Print &outStream) { + p_print1 = p_codec; + p_print2 = this; + if (p_codec!=nullptr) + target.setupOutput(p_print1, p_print2, outStream); + else + target.setupOutput(p_print2, outStream); + + is_initial_output = false; + } + + void writeAudio(const uint8_t *data, size_t len) { + TRACED(); + // output of audio data header + dh.common.len = len; + output((uint8_t *)&dh, sizeof(dh)); + + // output of data + output(data, len); + } + + void writeHeader() { + TRACED(); + output((uint8_t *)&cfg, sizeof(cfg)); + } + + size_t output(const uint8_t *data, size_t len) { + TRACED(); + if (p_final_print != nullptr) + p_final_print->write((uint8_t *)data, len); + else + LOGW("output not defined"); + return len; + } +}; + +/** + * @brief Decodes the provided data from the DAT and CFG segments + * @ingroup codecs + * @ingroup decoder + * @author Phil Schatzmann + * @copyright GPLv3 + */ +class BinaryContainerDecoder : public AudioDecoder { + public: + BinaryContainerDecoder() = default; + BinaryContainerDecoder(AudioDecoder &decoder) { p_codec = &decoder; } + BinaryContainerDecoder(AudioDecoder *decoder) { p_codec = decoder; } + + // Defines the output: this method is called 2 times: first to define + // output defined in the EnocdedAudioStream and then to define the + // real output in the output chain. + void setOutput(Print &outStream) { + LOGD("BinaryContainerDecoder::setOutput: %d",is_initial_output); + if (is_initial_output) { + setupIntialOutputStream(outStream); + } else { + p_final_print = &outStream; + } + } + + void setMetaCallback(void (*callback)(uint8_t *, int)) { + meta_callback = callback; + } + + void setNotifyAudioChange(AudioInfoSupport &bi) {} + + void begin() { + is_first = true; + target.begin(); + } + + void end() { + target.end(); + is_initial_output = true; + } + + size_t write(const void *data, size_t len) { + uint8_t *data8 = (uint8_t *)data; + int open = len; + int processed = 0; + + // on first call we try to synchronize the delimiter; hopefully however + // the new line is on the first char. + if (is_first) { + is_first = false; + // ignore the data before the first newline + uint8_t *ptr = (uint8_t *)memchr(data8, '\n', len); + if (ptr != nullptr) { + processed = ptr - data8; + open -= processed; + } + } else { + // process remaining data from last run + while (result.open > 0) { + result = processOpen(result, data8 + processed, open); + open -= result.processed; + processed += result.processed; + } + } + + // process new data startubg with newline + while (open > 0) { + result = processData(data8 + processed, open); + open -= result.processed; + processed += result.processed; + } + return len; + } + + AudioInfo audioInfo() { return info; } + + operator bool() { return true; }; + + protected: + bool is_first = true; + ProcessedResult result; + AudioInfo info; + CommonHeader header; + const size_t header_size = sizeof(header); + AudioDecoder *p_codec = nullptr; + void (*meta_callback)(uint8_t *, int) = nullptr; + SingleBuffer frame{0}; + AudioWriter *p_print1 = nullptr; + AudioWriter *p_print2 = nullptr; + Print *p_out = nullptr; + ContainerTargetPrint target; + bool is_initial_output = true; + Print *p_final_print = nullptr; + + void setupIntialOutputStream(Print &outStream) { + p_out = &outStream; + if (p_codec != nullptr) { + p_print1 = p_codec; + p_print2 = this; + target.setupOutput(p_print1, p_print2, outStream); + } else { + p_print1 = this; + target.setupOutput(p_print1, outStream); + } + is_initial_output = false; + } + + // loads the data into the buffer and writes it if complete + ProcessedResult processData(uint8_t *data8, size_t len) { + TRACED(); + ProcessedResult result = loadData(data8, len); + writeData(result); + return result; + } + + // loads the data + ProcessedResult loadData(uint8_t *data8, size_t len) { + TRACED(); + ProcessedResult result; + if (data8[0] == '\n') { + memcpy(&header, data8, header_size); + result.total_len = header_size + header.len; + LOGD("header.len: %d, result.total_len: %d, len: %d", (int)header.len, + (int)result.total_len, (int)len); + if (result.total_len <= len) { + result.processed = result.total_len; + result.open = 0; + } else { + result.processed = len; + result.open = result.total_len - len; + } + result.type = checkType(header.type); + if (result.type != ContainerType::Undefined) { + LOGD("header.len: %d", header.len); + if (frame.size() < header.len) { + frame.resize(header.len); + } + if (result.processed - header_size > 0) + frame.writeArray(data8 + header_size, result.processed - header_size); + } + } else { + LOGW("data ignored"); + result.type = ContainerType::Undefined; + } + return result; + } + + // processes the completed data from frame buffer: e.g. writes it to the + // output + bool writeData(ProcessedResult result) { + bool rc = false; + if (result.open == 0 && frame.available() > 0) { + TRACED(); + switch (result.type) { + case ContainerType::Header: { + LOGD("Header"); + // We expect that the header is never split because it is at the + // start + SimpleContainerConfig config; + assert(result.open == 0); + memmove(&config, frame.data(), sizeof(config)); + info = config.info; + if (p_notify) { + p_notify->setAudioInfo(info); + } + info.logInfo(); + frame.clear(); + rc = true; + } break; + + case ContainerType::Audio: { + LOGD("Audio"); + output(frame.data(), frame.available()); + frame.clear(); + rc = true; + } break; + + case ContainerType::Meta: { + LOGD("Meta"); + if (meta_callback!=nullptr) { + meta_callback(frame.data(), frame.available()); + } + frame.clear(); + rc = true; + } break; + } + } + return rc; + } + + // processes the reaminder of a split segment + ProcessedResult processOpen(ProcessedResult in, uint8_t *data8, size_t len) { + TRACED(); + ProcessedResult result = loadOpen(in, data8, len); + writeData(result); + return result; + } + + // if a segment is split we process the remaining missing part + ProcessedResult loadOpen(ProcessedResult in, uint8_t *data8, size_t len) { + TRACED(); + ProcessedResult result = in; + int to_process; + if (in.open <= len) { + result.open = 0; + result.processed = in.open; + } else { + result.open = in.open - len; + result.processed = len; + } + LOGD("in.type: %d, len: %d", in.type, result.processed); + if (in.type != ContainerType::Undefined) { + frame.writeArray(data8, result.processed); + } else { + LOGW("Unsupported tye"); + } + return result; + } + + // checks the type + ContainerType checkType(ContainerType type) { + ContainerType result = ContainerType::Undefined; + switch (header.type) { + case ContainerType::Header: + result = ContainerType::Header; + break; + case ContainerType::Audio: + result = ContainerType::Audio; + break; + case ContainerType::Meta: + result = ContainerType::Meta; + break; + } + return result; + } + + // writes the data to the decoder which forwards it to the output; if there + // is no coded we write to the output instead + size_t output(uint8_t *data, size_t len) { + LOGD("BinaryContainerDecoder::output: %d", (int)len); + if (p_final_print != nullptr) + p_final_print->write((uint8_t *)data, len); + else + LOGW("output not defined"); + + return len; + } +}; + +} // namespace audio_tools \ No newline at end of file diff --git a/src/AudioTools/AudioCodecs/ContainerOgg.h b/src/AudioCodecs/ContainerOgg.h similarity index 85% rename from src/AudioTools/AudioCodecs/ContainerOgg.h rename to src/AudioCodecs/ContainerOgg.h index 6e777708e8..4e68316121 100644 --- a/src/AudioTools/AudioCodecs/ContainerOgg.h +++ b/src/AudioCodecs/ContainerOgg.h @@ -1,8 +1,8 @@ #pragma once -#include "AudioTools/AudioCodecs/AudioCodecsBase.h" -#include "AudioTools/AudioCodecs/CodecOpus.h" -#include "AudioTools/CoreAudio/Buffers.h" +#include "AudioCodecs/AudioEncoded.h" +#include "AudioCodecs/CodecOpus.h" +#include "AudioTools/Buffers.h" #include "oggz/oggz.h" #define OGG_READ_SIZE (1024) @@ -23,7 +23,7 @@ namespace audio_tools { * @author Phil Schatzmann * @copyright GPLv3 */ -class OggContainerDecoder : public ContainerDecoder { +class OggContainerDecoder : public AudioDecoder { public: /** * @brief Construct a new OggContainerDecoder object @@ -46,22 +46,22 @@ class OggContainerDecoder : public ContainerDecoder { /// Defines the output Stream void setOutput(Print &print) override { out.setOutput(&print); } - void addNotifyAudioChange(AudioInfoSupport &bi) override { - out.addNotifyAudioChange(bi); - ContainerDecoder::addNotifyAudioChange(bi); + void setNotifyAudioChange(AudioInfoSupport &bi) override { + out.setNotifyAudioChange(bi); + p_notify = &bi; } AudioInfo audioInfo() override { return out.audioInfo(); } - bool begin(AudioInfo info) override { + void begin(AudioInfo info) override { TRACED(); this->info = info; - return begin(); + out.setAudioInfo(info); + begin(); } - bool begin() override { + void begin() override { TRACED(); - out.setAudioInfo(info); out.begin(); if (p_oggz == nullptr) { p_oggz = oggz_new(OGGZ_READ | OGGZ_AUTO); // OGGZ_NONSTRICT @@ -82,7 +82,6 @@ class OggContainerDecoder : public ContainerDecoder { is_open = false; } } - return is_open; } void end() override { @@ -100,19 +99,19 @@ class OggContainerDecoder : public ContainerDecoder { ; } - virtual size_t write(const uint8_t *data, size_t len) override { - LOGD("write: %d", (int)len); + virtual size_t write(const void *in_ptr, size_t in_size) override { + LOGD("write: %d", (int)in_size); // fill buffer - size_t size_consumed = buffer.writeArray((uint8_t *)data, len); + size_t size_consumed = buffer.writeArray((uint8_t *)in_ptr, in_size); if (buffer.availableForWrite() == 0) { // Read all bytes into oggz, calling any read callbacks on the fly. flush(); } // write remaining bytes - if (size_consumed < len) { - size_consumed += buffer.writeArray((uint8_t *)data + size_consumed, - len - size_consumed); + if (size_consumed < in_size) { + size_consumed += buffer.writeArray((uint8_t *)in_ptr + size_consumed, + in_size - size_consumed); flush(); } return size_consumed; @@ -214,6 +213,11 @@ class OggContainerOutput : public AudioOutput { /// Defines the output Stream void setOutput(Print &print) { p_out = &print; } + virtual bool begin(AudioInfo from) { + setAudioInfo(from); + return begin(); + } + /// starts the processing using the actual AudioInfo virtual bool begin() override { TRACED(); @@ -247,14 +251,15 @@ class OggContainerOutput : public AudioOutput { } /// Writes raw data to be encoded and packaged - virtual size_t write(const uint8_t *data, size_t len) override { - if (data == nullptr) return 0; - LOGD("OggContainerOutput::write: %d", (int)len); + virtual size_t write(const uint8_t *in_ptr, size_t in_size) override { + if (in_ptr == nullptr) return 0; + LOGD("OggContainerOutput::write: %d", (int)in_size); assert(cfg.channels != 0); // encode the data + uint8_t *data = (uint8_t *)in_ptr; op.packet = (uint8_t *)data; - op.bytes = len; + op.bytes = in_size; if (op.bytes > 0) { int bytes_per_sample = cfg.bits_per_sample / 8; granulepos += op.bytes / bytes_per_sample; // sample @@ -268,10 +273,10 @@ class OggContainerOutput : public AudioOutput { } } // trigger pysical write - while ((oggz_write(p_oggz, len)) > 0) + while ((oggz_write(p_oggz, in_size)) > 0) ; - return len; + return in_size; } bool isOpen() { return is_open; } @@ -329,7 +334,7 @@ class OggContainerOutput : public AudioOutput { return 0; } // self->out.write((uint8_t *)buf, n); - writeData(self->p_out, (uint8_t *)buf, n); + writeSamples(self->p_out, (uint8_t *)buf, n); // 0 = continue return 0; } @@ -364,23 +369,23 @@ class OggContainerEncoder : public AudioEncoder { /// We actually do nothing with this virtual void setAudioInfo(AudioInfo info) override { - AudioEncoder::setAudioInfo(info); p_ogg->setAudioInfo(info); if (p_codec != nullptr) p_codec->setAudioInfo(info); } - virtual bool begin(AudioInfo from) override { + virtual void begin(AudioInfo from) override { setAudioInfo(from); - return begin(); + begin(); } /// starts the processing using the actual AudioInfo - virtual bool begin() override { + virtual void begin() override { TRACED(); p_ogg->begin(); - if (p_codec==nullptr) return false; - p_codec->setOutput(*p_ogg); - return p_codec->begin(p_ogg->audioInfo()); + if (p_codec != nullptr) { + p_codec->setOutput(*p_ogg); + p_codec->begin(p_ogg->audioInfo()); + } } /// stops the processing @@ -391,14 +396,14 @@ class OggContainerEncoder : public AudioEncoder { } /// Writes raw data to be encoded and packaged - virtual size_t write(const uint8_t *data, size_t len) override { - if (!p_ogg->isOpen() || data == nullptr) return 0; - LOGD("OggContainerEncoder::write: %d", (int)len); + virtual size_t write(const void *in_ptr, size_t in_size) override { + if (!p_ogg->isOpen() || in_ptr == nullptr) return 0; + LOGD("OggContainerEncoder::write: %d", (int)in_size); size_t result = 0; if (p_codec == nullptr) { - result = p_ogg->write((const uint8_t *)data, len); + result = p_ogg->write((const uint8_t *)in_ptr, in_size); } else { - result = p_codec->write(data, len); + result = p_codec->write(in_ptr, in_size); } return result; } diff --git a/src/AudioTools/AudioCodecs/README.md b/src/AudioCodecs/README.md similarity index 100% rename from src/AudioTools/AudioCodecs/README.md rename to src/AudioCodecs/README.md diff --git a/src/AudioConfig.h b/src/AudioConfig.h new file mode 100644 index 0000000000..ad63466782 --- /dev/null +++ b/src/AudioConfig.h @@ -0,0 +1,640 @@ +/** + * @author Phil Schatzmann + * @brief AutioTools Configuration + * @copyright GPLv3 + * + */ +#pragma once +#if defined(ARDUINO) +# include "Arduino.h" +#elif defined(IS_DESKTOP) +# include "Arduino.h" +#elif defined(IS_DESKTOP_WITH_TIME_ONLY) +# include "AudioLibs/Desktop/Millis.h" +# include "AudioLibs/Desktop/NoArduino.h" +#else +# include "AudioLibs/Desktop/NoArduino.h" +# define IS_JUPYTER +#endif +#include +#include +#include +#include "AudioTools/AudioRuntime.h" + +// If you don't want to use all the settings from here you can define your own local config settings in AudioConfigLocal.h +#if __has_include("AudioConfigLocal.h") +#include "AudioConfigLocal.h" +#endif + +#define AUDIOTOOLS_VERSION "0.9.4" + +/** + * ------------------------------------------------------------------------- + * @brief Logging + * Logging Configuration in Arduino -> set USE_AUDIO_LOGGING to false if you want to deactivate Logging. + * When using cmake you can set -DUSE_AUDIO_LOGGING=false + * You can also change the LOG_LEVEL and LOG_STREAM here. + * However it is recommended to do it in your Sketch e.g with AudioLogger::instance().begin(Serial,AudioLogger::Warning); + */ + +#ifndef USE_AUDIO_LOGGING +#define USE_AUDIO_LOGGING true +#endif + +#ifndef LOG_LEVEL +#define LOG_LEVEL AudioLogger::Warning +#endif + +#ifndef LOG_STREAM +#define LOG_STREAM Serial +#endif + +#define LOG_PRINTF_BUFFER_SIZE 256 +#define LOG_METHOD __PRETTY_FUNCTION__ + +// cheange USE_CHECK_MEMORY to 1 to activate memory checks +#define USE_CHECK_MEMORY 0 +#if USE_CHECK_MEMORY +# define CHECK_MEMORY() checkMemory(true) +#else +# define CHECK_MEMORY() +#endif + +// Change USE_INLINE_VARS to 1 if inline variables are supported +#ifndef USE_INLINE_VARS +# define USE_INLINE_VARS 0 +#endif +/** + * ------------------------------------------------------------------------- + * @brief Common Default Settings that can usually be changed in the API + */ + +#ifndef DEFAULT_BUFFER_SIZE +#define DEFAULT_BUFFER_SIZE 1024 +#endif + +#ifndef DEFAULT_SAMPLE_RATE +#define DEFAULT_SAMPLE_RATE 44100 +#endif + +#ifndef DEFAULT_CHANNELS +#define DEFAULT_CHANNELS 2 +#endif + +#ifndef DEFAULT_BITS_PER_SAMPLE +#define DEFAULT_BITS_PER_SAMPLE 16 +#endif + +#ifndef I2S_DEFAULT_PORT +#define I2S_DEFAULT_PORT 0 +#endif + +#ifndef I2S_BUFFER_SIZE +#define I2S_BUFFER_SIZE 512 +#endif + +#ifndef I2S_BUFFER_COUNT +#define I2S_BUFFER_COUNT 6 // 20 +#endif + +#ifndef A2DP_BUFFER_SIZE +#define A2DP_BUFFER_SIZE 512 +#endif + +#ifndef A2DP_BUFFER_COUNT +#define A2DP_BUFFER_COUNT 30 +#endif + +#ifndef CODEC_DELAY_MS +#define CODEC_DELAY_MS 10 +#endif + +#ifndef COPY_DELAY_ON_NODATA +#define COPY_DELAY_ON_NODATA 10 +#endif + +#ifndef COPY_RETRY_LIMIT +#define COPY_RETRY_LIMIT 20 +#endif + +#ifndef MAX_HTTP_HEADER_LINE_LENGTH +#define MAX_HTTP_HEADER_LINE_LENGTH 240 +#endif + +#ifndef MAX_SINGLE_CHARS +#define MAX_SINGLE_CHARS 8 +#endif + +/** + * ------------------------------------------------------------------------- + * @brief PWM + */ +#ifndef PWM_BUFFER_SIZE +#define PWM_BUFFER_SIZE 1024 +#endif + +#ifndef PWM_BUFFERS +#define PWM_BUFFERS 40 +#endif + +#ifndef PWM_AUDIO_FREQUENCY +#define PWM_AUDIO_FREQUENCY 30000 +#endif + + +/** + * ------------------------------------------------------------------------- + * @brief Activate Other Tools + */ + +//#define USE_STK +//#define USE_SDFAT +//#define USE_DELTASIGMA + + +/** + * ------------------------------------------------------------------------- + * @brief Platform specific Settings + */ + +//-------ESP32--------- +#if defined(ESP32) && defined(CONFIG_IDF_TARGET_ESP32C3) +#define ESP32C3 +#define ESP32X +#define USE_INT24_FROM_INT +#endif +#if defined(ESP32) && defined(CONFIG_IDF_TARGET_ESP32S2) +#define ESP32S2 +#define ESP32X +#endif +#if defined(ESP32) && defined(CONFIG_IDF_TARGET_ESP32S3) +#define ESP32S3 +#define ESP32X +#endif + +// ----- Regular ESP32 ----- +#if defined(ESP32) && !defined(ESP32X) +//#include "esp32-hal-log.h" +// optional libraries +//#define USE_A2DP +//#define USE_ESP8266_AUDIO + +#define USE_PWM +#define USE_URL_ARDUINO +#define USE_WIFI +#define USE_WIFI_CLIENT_SECURE +#define USE_I2S +#define USE_AUDIO_SERVER +#define USE_TYPETRAITS +#define USE_EFFECTS_SUITE +#define USE_TIMER +#define USE_I2S_ANALOG +#define USE_STREAM_WRITE_OVERRIDE +#define USE_STREAM_READ_OVERRIDE +#define USE_TOUCH_READ +#define USE_CONCURRENCY +#define USE_EXT_BUTTON_LOGIC + +#define PWM_FREQENCY 30000 +#define PIN_PWM_START 12 +#define PIN_I2S_BCK 14 +#define PIN_I2S_WS 15 +#define PIN_I2S_DATA_IN 32 +#define PIN_I2S_DATA_OUT 22 +#define PIN_I2S_MCK -1 +#define I2S_USE_APLL true +// Default Setting: The mute pin can be switched actovated by setting it to a gpio (e.g 23). Or you could drive the LED by assigning LED_BUILTIN +#define PIN_I2S_MUTE -1 +#define SOFT_MUTE_VALUE 0 +#define PIN_CS SS +#define PIN_ADC1 34 +#define PIN_ADC2 14 + +#define I2S_AUTO_CLEAR true + +// URLStream +//#define USE_URLSTREAM_TASK +#define URL_STREAM_CORE 0 +#define URL_STREAM_PRIORITY 2 +#define URL_STREAM_BUFFER_COUNT 10 +#define STACK_SIZE 30000 +#define URL_CLIENT_TIMEOUT 60000; +#define URL_HANDSHAKE_TIMEOUT 120000 + +// // Default LED +// #ifndef LED_BUILTIN +// # define LED_BUILTIN 13 // pin number is specific to your esp32 board +// #endif + +// support for old idf releases +#if !defined(USE_I2S_NEW) && ESP_IDF_VERSION_MAJOR < 4 && !defined(I2S_COMM_FORMAT_STAND_I2S) +# define I2S_COMM_FORMAT_STAND_I2S (I2S_COMM_FORMAT_I2S | I2S_COMM_FORMAT_I2S_MSB) +# define I2S_COMM_FORMAT_STAND_MSB (I2S_COMM_FORMAT_I2S | I2S_COMM_FORMAT_I2S_LSB) +# define I2S_COMM_FORMAT_STAND_PCM_LONG (I2S_COMM_FORMAT_PCM | I2S_COMM_FORMAT_PCM_LONG) +# define I2S_COMM_FORMAT_STAND_PCM_SHORT (I2S_COMM_FORMAT_PCM | I2S_COMM_FORMAT_PCM_SHORT) + +typedef int eps32_i2s_sample_rate_type; +#else +typedef uint32_t eps32_i2s_sample_rate_type; +#endif + +#endif + +//-------ESP32C3, ESP32S3, ESP32S2--------- + +#if defined(ESP32) && defined(ESP32X) +#include "esp32-hal-log.h" + +#define USE_PWM +#define USE_URL_ARDUINO +#define USE_WIFI +#define USE_WIFI_CLIENT_SECURE +#define USE_I2S +#define USE_AUDIO_SERVER +//#define USE_URLSTREAM_TASK +#define USE_TYPETRAITS +#define USE_EFFECTS_SUITE +#define USE_TIMER +#define USE_STREAM_WRITE_OVERRIDE +#define USE_STREAM_READ_OVERRIDE +#define USE_CONCURRENCY + +#define PWM_FREQENCY 30000 +#define PIN_PWM_START 1 +#define PIN_I2S_MCK -1 +#define PIN_I2S_BCK 6 +#define PIN_I2S_WS 7 +#define PIN_I2S_DATA_OUT 8 +#define PIN_I2S_DATA_IN 9 +#define I2S_USE_APLL true +// Default Setting: The mute pin can be switched actovated by setting it to a gpio (e.g 5). Or you could drive the LED by assigning LED_BUILTIN +#define PIN_I2S_MUTE -1 +#define SOFT_MUTE_VALUE 0 +#define PIN_CS SS +#define PIN_ADC1 21 +#define PIN_ADC2 22 + +#define I2S_AUTO_CLEAR true + +// URLStream +//#define USE_ESP8266_AUDIO +#define URL_STREAM_CORE 0 +#define URL_STREAM_PRIORITY 2 +#define URL_STREAM_BUFFER_COUNT 10 +#define STACK_SIZE 30000 +#define URL_CLIENT_TIMEOUT 60000; +#define URL_HANDSHAKE_TIMEOUT 120000 + +// // Default LED +// #ifndef LED_BUILTIN +// # define LED_BUILTIN 13 // pin number is specific to your esp32 board +// #endif + +typedef uint32_t eps32_i2s_sample_rate_type; + +#endif + +// --- ESP32 ------------ +// E.g when using the Espressif IDF. Use cmake for the necesseary defines +#if defined(ESP32_CMAKE) + +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" +#define ESP32 +#define DESKTOP_MILLIS_DEFINED + +typedef uint32_t eps32_i2s_sample_rate_type; +// forward declare app_amin +extern "C" void app_main(); +// delay and millis is needed by this framework +namespace audio_tools { + +void delay(uint64_t ms){ vTaskDelay(1000 / portTICK_PERIOD_MS);} +uint64_t millis() {return (xTaskGetTickCount() * portTICK_PERIOD_MS);} + +} +#endif + +//----- ESP8266 ----------- +#ifdef ESP8266 +# include +//#define USE_URL_ARDUINO // commented out because of compile errors +#define USE_I2S +#define USE_AUDIO_SERVER +#define USE_TYPETRAITS +#define USE_EFFECTS_SUITE +#define USE_TIMER +#define USE_URL_ARDUINO +#define USE_WIFI + +#define PIN_PWM_START 12 +#define PIN_I2S_BCK -1 +#define PIN_I2S_WS -1 +#define PIN_I2S_DATA_IN -1 +#define PIN_I2S_DATA_OUT -1 +#define I2S_USE_APLL false +#define PIN_I2S_MUTE 23 +#define SOFT_MUTE_VALUE 0 +#define PIN_CS SS + +#define URL_CLIENT_TIMEOUT 60000; +#define URL_HANDSHAKE_TIMEOUT 120000 + +#endif + +//------ NANO33BLE ---------- +#if defined(ARDUINO_SEEED_XIAO_NRF52840_SENSE) || defined(ARDUINO_ARDUINO_NANO33BLE) +#define USE_NANO33BLE +#define USE_I2S +#define USE_PWM +#define USE_TYPETRAITS +#define USE_EFFECTS_SUITE +#define USE_TIMER + +#define PIN_PWM_START 6 +#define PIN_I2S_BCK 2 +#define PIN_I2S_WS 1 +#define PIN_I2S_DATA_IN 3 +#define PIN_I2S_DATA_OUT 3 +// Default Setting: The mute pin can be switched actovated by setting it to a gpio (e.g 4). Or you could drive the LED by assigning LED_BUILTIN +#define PIN_I2S_MUTE -1 +#define SOFT_MUTE_VALUE 0 +#define PIN_CS SS +#endif + +//----- MBED ----------- +#if defined(ARDUINO_ARCH_MBED_RP2040) +// install https://github.com/pschatzmann/rp2040-i2s +#define RP2040_MBED +#define USE_I2S 1 +#define USE_PWM +#define USE_ADC_ARDUINO +#define USE_TYPETRAITS +#define USE_EFFECTS_SUITE +#define USE_TIMER +#define USE_INT24_FROM_INT + +#define PIN_ADC_START 26 +#define PIN_PWM_START 6 +#define PIN_I2S_BCK 26 +#define PIN_I2S_WS PIN_I2S_BCK+1 +#define PIN_I2S_DATA_IN 28 +#define PIN_I2S_DATA_OUT 28 +// Default Setting: The mute pin can be switched actovated by setting it to a gpio (e.g 4). Or you could drive the LED by assigning LED_BUILTIN +#define PIN_I2S_MUTE -1 +#define SOFT_MUTE_VALUE 0 +#define PIN_CS 1 //PIN_SPI0_SS + +// fix missing __sync_synchronize symbol +#define FIX_SYNC_SYNCHRONIZE +#define IRAM_ATTR +#ifndef ADC_BUFFER_SIZE +#define ADC_BUFFER_SIZE 1024 +#endif + +#ifndef ADC_BUFFERS +#define ADC_BUFFERS 50 +#endif + +//#define USE_ESP8266_AUDIO + +//----- RP2040 ----------- +#elif defined(ARDUINO_ARCH_RP2040) +#define RP2040_HOWER +#define USE_I2S +#define USE_PWM +#define USE_ADC_ARDUINO +#define USE_TYPETRAITS +#define USE_EFFECTS_SUITE +#define USE_TIMER +#define USE_INT24_FROM_INT + +#define PIN_ADC_START 26 +#define PIN_PWM_START 6 +#define PIN_I2S_BCK 26 +#define PIN_I2S_WS PIN_I2S_BCK+1 +#define PIN_I2S_DATA_IN 28 +#define PIN_I2S_DATA_OUT 28 +// Default Setting: The mute pin can be switched actovated by setting it to a gpio (e.g 4). Or you could drive the LED by assigning LED_BUILTIN +#define PIN_I2S_MUTE -1 +#define SOFT_MUTE_VALUE 0 +#define PIN_CS PIN_SPI0_SS + +// fix missing __sync_synchronize symbol +#define FIX_SYNC_SYNCHRONIZE +#define IRAM_ATTR + +#ifndef ADC_BUFFER_SIZE +#define ADC_BUFFER_SIZE 256 +#endif + +#ifndef ADC_BUFFERS +#define ADC_BUFFERS 100 +#endif + +//#define USE_ESP8266_AUDIO +#endif + +// The Pico W has WIFI support +#if defined(ARDUINO_ARCH_RP2040) && LWIP_IPV4==1 +# include +# define USE_URL_ARDUINO +# define USE_WIFI +# define USE_WIFI_CLIENT_SECURE +# define USE_AUDIO_SERVER +using WiFiServerSecure = BearSSL::WiFiServerSecure; +#endif + + +//----- AVR ----------- +#ifdef __AVR__ +#define USE_PWM +#define USE_TIMER +// Uncomment to activate network +//#include +//#define USE_URL_ARDUINO +#ifndef assert +#define assert(T) +#endif + +#define PIN_PWM_START 6 +#define PIN_CS SS + +#undef PWM_BUFFER_SIZE +#define PWM_BUFFER_SIZE 125 + +#undef DEFAULT_BUFFER_SIZE +#define DEFAULT_BUFFER_SIZE 125 + +// #undef USE_AUDIO_LOGGING +// logging is using too much memory +#undef LOG_PRINTF_BUFFER_SIZE +#define LOG_PRINTF_BUFFER_SIZE 80 + +#define NO_TRACED +#define NO_TRACEI + +// we use spi to emulate i2s +#define PIN_I2S_BCK 13 +#define PIN_I2S_WS 10 +#define PIN_I2S_DATA_IN 12 +#define PIN_I2S_DATA_OUT 11 +#define PIN_I2S_MUTE -1 + +#endif + + +//---- STM32 ------------ +#if defined(ARDUINO_ARCH_STM32F4) || defined(ARDUINO_ARCH_STM32) +#define STM32 +#endif + +#ifdef STM32 +#define USE_I2S +#define USE_PWM +#define USE_TIMER +#define USE_ADC_ARDUINO +#define USE_INT24_FROM_INT + +#define ADC_BUFFER_SIZE 1024 +#define ADC_BUFFERS 20 +#define PIN_ADC_START PA0 +#define PIN_PWM_START PA0 +#define PWM_DEFAULT_TIMER TIM2 +#define PWM_FREQ_TIMER_NO 3 + +#define PIN_I2S_BCK -1 +#define PIN_I2S_WS -1 +#define PIN_I2S_DATA_IN -1 +#define PIN_I2S_DATA_OUT -1 +#define PIN_I2S_MUTE -1 +#define SOFT_MUTE_VALUE 0 +#define PIN_CS 10 +#endif + +//---- SAMD ------------ + +#ifdef ARDUINO_ARCH_SAMD +#define USE_I2S +#define USE_INT24_FROM_INT +#define PIN_I2S_BCK 1 +#define PIN_I2S_WS PIN_I2S_BCK+1 +#define PIN_I2S_DATA_IN 3 +#define PIN_I2S_DATA_OUT 3 +#define PIN_I2S_MUTE -1 +#define SOFT_MUTE_VALUE 0 +#endif + +#ifdef ARDUINO_SAMD_MKRWIFI1010 +#include +#define USE_URL_ARDUINO +#define USE_AUDIO_SERVER +#endif + +//---- GIGA ------------ +// DRAFT Support - Not tested ! +#if defined(ARDUINO_GIGA) +#include +#include +#define IS_MBED +#define USE_INT24_FROM_INT +#define USE_TYPETRAITS +#define USE_EFFECTS_SUITE +#define USE_I2S_ANALOG +#define USE_STREAM_WRITE_OVERRIDE +#define ADC_BUFFER_SIZE 1024 +#define ADC_BUFFERS 10 +#define USE_URL_ARDUINO +#define USE_AUDIO_SERVER + +#define PIN_ADC_START A7 +#define PIN_DAC_1 A12 +#define PIN_DAC_2 A13 + +#endif + +// //---- Portenta ------------ +// // DRAFT: not tested +#if defined(ARDUINO_ARCH_MBED_PORTENTA) +#include +#include +#define IS_MBED +#define USE_INT24_FROM_INT +#define USE_TYPETRAITS +#define USE_EFFECTS_SUITE +#define USE_I2S_ANALOG +#define USE_TIMER +#define USE_PWM +#define USE_STREAM_WRITE_OVERRIDE +#define ADC_BUFFER_SIZE 1024 +#define ADC_BUFFERS 10 +#define USE_URL_ARDUINO +#define USE_AUDIO_SERVER + +#define PIN_ADC_START A0 +#define PIN_PWM_START D2 +#define PIN_DAC_1 D0 +#define PIN_DAC_2 D1 +#endif + + +//------ VS1053 ---------- + +// Default Pins for VS1053 +#define VS1053_CS 5 +#define VS1053_DCS 16 +#define VS1053_DREQ 4 +#define VS1053_RESET 15 +#define VS1053_CS_SD -1 + +// use 0 for https://github.com/baldram/ESP_VS1053_Library +// use 1 for https://github.com/pschatzmann/arduino-vs1053 +#define VS1053_EXT 1 +#define VS1053_DEFAULT_VOLUME 0.7 + + + +//---------------- + +// Full Arduino functionality using emulator +#ifdef IS_DESKTOP +# include +# include +# define USE_URL_ARDUINO +# define USE_STREAM_WRITE_OVERRIDE +# define USE_STREAM_READ_OVERRIDE +# define USE_STREAM_READCHAR_OVERRIDE +typedef WiFiClient WiFiClientSecure; +#endif + +// Minimum desktop functionality w/o Arduino emulator +#ifdef IS_MIN_DESKTOP +# include "AudioLibs/Desktop/NoArduino.h" +# include "AudioLibs/Desktop/Millis.h" +# include "AudioLibs/Desktop/Main.h" +# include "AudioLibs/Desktop/File.h" +# define USE_STREAM_READ_OVERRIDE +#endif + +#ifndef ARDUINO +# define USE_STREAM_WRITE_OVERRIDE +#endif + +#ifdef IS_JUPYTER +# define USE_STREAM_READ_OVERRIDE +#endif + +#ifdef AUDIOKIT_USE_IDF +# define USE_INT24_FROM_INT +#endif + +#if USE_INLINE_VARS && !defined(INGNORE_INLINE_VARS) +# define INLINE_VAR inline +#else +# define INLINE_VAR static +#endif + +#pragma GCC diagnostic ignored "-Wunused-variable" +#pragma GCC diagnostic ignored "-Wunused-function" +#pragma GCC diagnostic ignored "-Wvla" +#pragma GCC diagnostic ignored "-Wsign-compare" diff --git a/src/AudioTools/CoreAudio/AudioEffects/AudioEffect.h b/src/AudioEffects/AudioEffect.h similarity index 66% rename from src/AudioTools/CoreAudio/AudioEffects/AudioEffect.h rename to src/AudioEffects/AudioEffect.h index 6870685ae6..6de3a3cbfb 100644 --- a/src/AudioTools/CoreAudio/AudioEffects/AudioEffect.h +++ b/src/AudioEffects/AudioEffect.h @@ -1,9 +1,7 @@ #pragma once -#include "AudioParameters.h" -#include "PitchShift.h" -#include "AudioLogger.h" -#include "AudioTools/CoreAudio/AudioTypes.h" -#include "AudioTools/CoreAudio/AudioOutput.h" +#include "AudioEffects/AudioParameters.h" +#include "AudioEffects/PitchShift.h" +#include "AudioTools/AudioLogger.h" #include namespace audio_tools { @@ -71,24 +69,30 @@ class AudioEffect { * */ -class Boost : public AudioEffect, public VolumeSupport { +class Boost : public AudioEffect { public: /// Boost Constructor: volume 0.1 - 1.0: decrease result; volume >0: increase /// result - Boost(float volume = 1.0) { setVolume(volume); } + Boost(float volume = 1.0) { effect_value = volume; } Boost(const Boost ©) = default; + float volume() { return effect_value; } + + void setVolume(float volume) { effect_value = volume; } + effect_t process(effect_t input) { if (!active()) return input; - int32_t result = volume() * input; + int32_t result = effect_value * input; // clip to int16_t return clip(result); } Boost *clone() { return new Boost(*this); } +protected: + float effect_value; }; /** @@ -194,7 +198,6 @@ class Tremolo : public AudioEffect { Tremolo(const Tremolo ©) = default; void setDuration(int16_t ms) { - this->duration_ms = ms; int32_t rate_count = sampleRate * ms / 1000; rate_count_half = rate_count / 2; } @@ -250,7 +253,8 @@ class Delay : public AudioEffect { public: /// e.g. depth=0.5, ms=1000, sampleRate=44100 Delay(uint16_t duration_ms = 1000, float depth = 0.5, - float feedbackAmount = 1.0, uint32_t sampleRate = 44100) { + float feedbackAmount = 1.0, uint32_t sampleRate = 44100, + bool zeroIfBufferEmpty = false) { setSampleRate(sampleRate); setFeedback(feedbackAmount); setDepth(depth); @@ -273,20 +277,20 @@ class Delay : public AudioEffect { void setDepth(float value) { depth = value; - if (depth > 1.0f) - depth = 1.0f; - if (depth < 0.0f) - depth = 0.0f; + if (depth > 1.0) + depth = 1.0; + if (depth < 0) + depth = 0.0; } float getDepth() { return depth; } void setFeedback(float feed) { feedback = feed; - if (feedback > 1.0f) - feedback = 1.0f; - if (feedback < 0.0f) - feedback = 0.0f; + if (feedback > 1.0) + feedback = 1.0; + if (feedback < 0) + feedback = 0.0; } float getFeedback() { return feedback; } @@ -306,7 +310,7 @@ class Delay : public AudioEffect { int32_t delayed_value = buffer[delay_line_index]; // Mix the above with current audio and write the results back to output - int32_t out = ((1.0f - depth) * input) + (depth * delayed_value); + int32_t out = ((1.0 - depth) * input) + (depth * delayed_value); // Update each delay line buffer[delay_line_index] = clip(feedback * (delayed_value + input)); @@ -322,7 +326,7 @@ class Delay : public AudioEffect { protected: Vector buffer{0}; - float feedback = 0.0f, duration = 0.0f, sampleRate = 0.0f, depth = 0.0f; + float feedback = 0.0, duration = 0.0, sampleRate = 0.0, depth = 0.0; size_t delay_len_samples = 0; size_t delay_line_index = 0; @@ -391,8 +395,6 @@ class ADSRGain : public AudioEffect { void keyOff() { adsr->keyOff(); } effect_t process(effect_t input) { - if (!active()) - return input; effect_t result = factor * adsr->tick() * input; return result; } @@ -441,9 +443,7 @@ class PitchShift : public AudioEffect { if (!active()) return input; buffer.write(input); - effect_t result; - buffer.read(result); - return result; + return buffer.read(); } PitchShift *clone() { return new PitchShift(*this); } @@ -453,162 +453,4 @@ class PitchShift : public AudioEffect { float effect_value; int size; }; - - -/** - * @brief Compressor inspired by https://github.com/YetAnotherElectronicsChannel/STM32_DSP_COMPRESSOR/blob/master/code/Src/main.c - * @author Phil Schatzmann - * @ingroup effects - * @copyright GPLv3 -*/ - -class Compressor : public AudioEffect { -public: - /// Copy Constructor - Compressor(const Compressor ©) = default; - - /// Default Constructor - Compressor(uint32_t sampleRate = 44100, uint16_t attackMs=30, uint16_t releaseMs=20, uint16_t holdMs=10, uint8_t thresholdPercent=10, float compressionRatio=0.5){ - //assuming 1 sample = 1/96kHz = ~10us - //Attack -> 30 ms -> 3000 - //Release -> 20 ms -> 2000 - //Hold -> 10ms -> 1000 - sample_rate = sampleRate; - attack_count = sample_rate * attackMs / 1000; - release_count = sample_rate * releaseMs / 1000; - hold_count = sample_rate * holdMs / 1000; - - //threshold -20dB below limit -> 0.1 * 2^31 - threshold = 0.01f * thresholdPercent * NumberConverter::maxValueT(); - //compression ratio: 6:1 -> -6dB = 0.5 - gainreduce = compressionRatio; - //initial gain = 1.0 -> no compression - gain = 1.0f; - recalculate(); - } - - /// Defines the attack duration in ms - void setAttack(uint16_t attackMs){ - attack_count = sample_rate * attackMs / 1000; - recalculate(); - } - - /// Defines the release duration in ms - void setRelease(uint16_t releaseMs){ - release_count = sample_rate * releaseMs / 1000; - recalculate(); - } - - /// Defines the hold duration in ms - void setHold(uint16_t holdMs){ - hold_count = sample_rate * holdMs / 1000; - recalculate(); - } - - /// Defines the threshod in % - void setThresholdPercent(uint8_t thresholdPercent){ - threshold = 0.01f * thresholdPercent * NumberConverter::maxValueT(); - } - - /// Defines the compression ratio from 0 to 1 - void setCompressionRatio(float compressionRatio){ - if (compressionRatio < 1.0f){ - gainreduce = compressionRatio; - } - recalculate(); - } - - /// Processes the sample - effect_t process(effect_t input) { - if (!active()) - return input; - return compress(input); - } - - Compressor *clone() { return new Compressor(*this); } - -protected: - enum CompStates {S_NoOperation, S_Attack, S_GainReduction, S_Release }; - enum CompStates state = S_NoOperation; - - int32_t attack_count, release_count, hold_count, timeout; - float gainreduce, gain_step_attack, gain_step_release, gain, threshold; - uint32_t sample_rate; - - void recalculate() { - gain_step_attack = (1.0f - gainreduce) / attack_count; - gain_step_release = (1.0f - gainreduce) / release_count; - } - - float compress(float inSampleF){ - if (fabs(inSampleF) > threshold) { - if (gain >= gainreduce) { - if (state==S_NoOperation) { - state=S_Attack; - timeout = attack_count; - } - else if (state==S_Release) { - state=S_Attack; - timeout = attack_count; - } - } - if (state==S_GainReduction) timeout = hold_count; - - } - - if (fabs(inSampleF) < threshold && gain <= 1.0f) { - if ( timeout==0 && state==S_GainReduction) { - state=S_Release; - timeout = release_count; - } - } - - switch (state) { - case S_Attack: - if ( timeout>0 && gain > gainreduce) { - gain -= gain_step_attack; - timeout--; - } - else { - state=S_GainReduction; - timeout = hold_count; - } - break; - - - case S_GainReduction: - if ( timeout>0) timeout--; - else { - state=S_Release; - timeout = release_count; - } - break; - - - case S_Release: - if ( timeout>0 && gain<1.0f) { - timeout--; - gain += gain_step_release; - } - else { - state=S_NoOperation; - } - break; - - case S_NoOperation: - if (gain < 1.0f) gain = 1.0f; - break; - - default: - break; - - } - - float outSampleF = gain * inSampleF; - return outSampleF; - } - -}; - - } // namespace audio_tools diff --git a/src/AudioTools/CoreAudio/AudioEffects/AudioEffects.h b/src/AudioEffects/AudioEffects.h similarity index 88% rename from src/AudioTools/CoreAudio/AudioEffects/AudioEffects.h rename to src/AudioEffects/AudioEffects.h index dd36e1dc2a..95c0eaaf9d 100644 --- a/src/AudioTools/CoreAudio/AudioEffects/AudioEffects.h +++ b/src/AudioEffects/AudioEffects.h @@ -1,8 +1,8 @@ #pragma once -#include "AudioTools/CoreAudio/AudioBasic/Collections.h" -#include "AudioTools/CoreAudio/AudioStreams.h" -#include "SoundGenerator.h" -#include "AudioEffect.h" +#include "AudioBasic/Collections.h" +#include "AudioEffects/SoundGenerator.h" +#include "AudioEffects/AudioEffect.h" +#include "AudioTools/AudioStreams.h" #if defined(USE_VARIANTS) && __cplusplus >= 201703L # include #endif @@ -205,12 +205,13 @@ class AudioEffects : public SoundGenerator { * @copyright GPLv3*/ template -class AudioEffectStreamT : public ModifyingStream { +class AudioEffectStreamT : public AudioStream { public: AudioEffectStreamT() = default; AudioEffectStreamT(Stream &io){ - setStream(io); + setOutput(io); + setInput(io); } AudioEffectStreamT(Print &out){ @@ -227,15 +228,10 @@ class AudioEffectStreamT : public ModifyingStream { bool begin(AudioInfo cfg){ info = cfg; - return begin(); - } - - bool begin(){ - TRACEI(); - if (sizeof(T)==info.bits_per_sample/8){ + if (sizeof(T)==cfg.bits_per_sample/8){ active = true; } else { - LOGE("bits_per_sample not consistent: %d",info.bits_per_sample); + LOGE("bits_per_sample not consistent: %d",cfg.bits_per_sample); active = false; } return active; @@ -245,13 +241,15 @@ class AudioEffectStreamT : public ModifyingStream { active = false; } - void setStream(Stream &io) override { + void setInput(Stream &io){ p_io = &io; - p_print = &io; } + void setOutput(Stream &io){ + p_io = &io; + } - void setOutput(Print &print) override { + void setOutput(Print &print){ p_print = &print; } @@ -259,21 +257,21 @@ class AudioEffectStreamT : public ModifyingStream { * Provides the audio data by reading the assinged Stream and applying * the effects on that input */ - size_t readBytes(uint8_t *data, size_t len) override { + size_t readBytes(uint8_t *buffer, size_t length) override { if (!active || p_io==nullptr)return 0; + int frames = length / sizeof(T) / info.channels; size_t result_size = 0; - // read data from source - size_t result = p_io->readBytes((uint8_t*)data, len); - int frames = result / sizeof(T) / info.channels; - T* samples = (T*) data; + if (p_io->available()<(sizeof(T)*info.channels)){ + return 0; + } for (int count=0;countreadBytes((uint8_t*)&sample, sizeof(T)); + T sample; + p_io->readBytes((uint8_t*)&sample, sizeof(T)); result_sample += sample / info.channels; } @@ -283,7 +281,7 @@ class AudioEffectStreamT : public ModifyingStream { } // write result multiplying channels - T* p_buffer = ((T*)data)+(count*info.channels); + T* p_buffer = ((T*)buffer)+(count*info.channels); for (int ch=0;ch(); break; @@ -437,7 +430,7 @@ class AudioEffectStream : public ModifyingStream { } std::visit( [this](auto&& e) {return e.setOutput(*p_print);}, variant ); std::visit( [this](auto&& e) {return e.setInput(*p_io);}, variant ); - return std::visit( [cfg](auto&& e) {return e.begin(info);}, variant ); + return std::visit( [cfg](auto&& e) {return e.begin(cfg);}, variant ); } void end() override { @@ -445,10 +438,6 @@ class AudioEffectStream : public ModifyingStream { } void setInput(Stream &io){ - setStream(io); - } - - void setStream(Stream &io){ p_io = &io; } @@ -464,16 +453,16 @@ class AudioEffectStream : public ModifyingStream { * Provides the audio data by reading the assinged Stream and applying * the effects on that input */ - size_t readBytes(uint8_t *data, size_t len) override { - return std::visit( [data, len](auto&& e) {return e.readBytes(data, len);}, variant ); + size_t readBytes(uint8_t *buffer, size_t length) override { + return std::visit( [buffer, length](auto&& e) {return e.readBytes(buffer, length);}, variant ); } /** * Writes the samples passed in the buffer and applies the effects before writing the * result to the output defined in the constructor. */ - size_t write(const uint8_t *data, size_t len) override { - return std::visit( [data, len](auto&& e) {return e.write(data, len);}, variant ); + size_t write(const uint8_t *buffer, size_t length) override { + return std::visit( [buffer, length](auto&& e) {return e.write(buffer, length);}, variant ); } int available() override { diff --git a/src/AudioTools/CoreAudio/AudioEffects/AudioParameters.h b/src/AudioEffects/AudioParameters.h similarity index 98% rename from src/AudioTools/CoreAudio/AudioEffects/AudioParameters.h rename to src/AudioEffects/AudioParameters.h index 4d63294b91..f69325471f 100644 --- a/src/AudioTools/CoreAudio/AudioEffects/AudioParameters.h +++ b/src/AudioEffects/AudioParameters.h @@ -1,5 +1,5 @@ #pragma once -#include "AudioTools/CoreAudio/AudioLogger.h" +#include "AudioTools/AudioLogger.h" namespace audio_tools { diff --git a/src/AudioTools/CoreAudio/AudioEffects/PitchShift.h b/src/AudioEffects/PitchShift.h similarity index 82% rename from src/AudioTools/CoreAudio/AudioEffects/PitchShift.h rename to src/AudioEffects/PitchShift.h index 8e69f6757a..0cb6d2e4cf 100644 --- a/src/AudioTools/CoreAudio/AudioEffects/PitchShift.h +++ b/src/AudioEffects/PitchShift.h @@ -1,12 +1,10 @@ #pragma once +#include "AudioConfig.h" #include #include #include -#include "AudioTools/CoreAudio/AudioOutput.h" -#include "AudioTools/CoreAudio/AudioTypes.h" -#include "AudioToolsConfig.h" namespace audio_tools { @@ -33,37 +31,36 @@ struct PitchShiftInfo : public AudioInfo { */ template class VariableSpeedRingBufferSimple : public BaseBuffer { - public: +public: VariableSpeedRingBufferSimple(int size = 0, float increment = 1.0) { setIncrement(increment); - if (size > 0) resize(size); + if (size > 0) + resize(size); } void setIncrement(float increment) { read_increment = increment; } - bool resize(int size) { + void resize(int size) { buffer_size = size; - return buffer.resize(size); + buffer.resize(size); } - bool read(T &result) { - peek(result); + T read() { + T result = peek(); read_pos_float += read_increment; - // on buffer overflow reset to beginning + // on buffer owerflow reset to beginning if (read_pos_float > buffer_size) { read_pos_float -= buffer_size; } - return true; + return result; } - bool peek(T &result) { + T peek() { if (buffer.size() == 0) { LOGE("buffer has no memory"); - result = 0; - } else { - result = buffer[(int)read_pos_float]; + return 0; } - return true; + return buffer[(int)read_pos_float]; } bool write(T sample) { @@ -72,7 +69,7 @@ class VariableSpeedRingBufferSimple : public BaseBuffer { return false; } buffer[write_pos++] = sample; - // on buffer overflow reset to 0 + // on buffer owerflow reset to 0 if (write_pos >= buffer_size) { write_pos = 0; } @@ -90,9 +87,8 @@ class VariableSpeedRingBufferSimple : public BaseBuffer { virtual int available() { return buffer_size; } virtual int availableForWrite() { return buffer_size; } virtual T *address() { return nullptr; } - size_t size() { return buffer_size; } - protected: +protected: Vector buffer{0}; int buffer_size = 0; float read_pos_float = 0.0; @@ -107,28 +103,25 @@ class VariableSpeedRingBufferSimple : public BaseBuffer { * @ingroup buffers * @tparam T */ -template -class VariableSpeedRingBuffer180 : public BaseBuffer { - public: +template class VariableSpeedRingBuffer180 : public BaseBuffer { +public: VariableSpeedRingBuffer180(int size = 0, float increment = 1.0) { setIncrement(increment); - if (size > 0) resize(size); + if (size > 0) + resize(size); } void setIncrement(float increment) { pitch_shift = increment; } - bool resize(int size) { + void resize(int size) { buffer_size = size; overlap = buffer_size / 10; - return buffer.resize(size); + buffer.resize(size); } - bool read(T &result) { - result = pitchRead(); - return true; - } + T read() { return pitchRead(); } - bool peek(T &result) { return false; } + T peek() { return -1; } bool write(T sample) { if (buffer.size() == 0) { @@ -138,7 +131,7 @@ class VariableSpeedRingBuffer180 : public BaseBuffer { // write_pointer value is used in pitchRead() write_pointer = write_pos; buffer[write_pos++] = sample; - // on buffer overflow reset to 0 + // on buffer owerflow reset to 0 if (write_pos >= buffer_size) { write_pos = 0; } @@ -158,9 +151,8 @@ class VariableSpeedRingBuffer180 : public BaseBuffer { virtual int available() { return buffer_size; } virtual int availableForWrite() { return buffer_size; } virtual T *address() { return nullptr; } - size_t size() { return buffer_size; } - protected: +protected: Vector buffer{0}; float read_pos_float = 0.0; float cross_fade = 1.0; @@ -212,7 +204,8 @@ class VariableSpeedRingBuffer180 : public BaseBuffer { // increment fractional read-pointer and write-pointer read_pos_float += pitch_shift; - if (roundf(read_pos_float) >= buffer_size) read_pos_float = 0.0f; + if (roundf(read_pos_float) >= buffer_size) + read_pos_float = 0.0f; return sum; } @@ -225,48 +218,46 @@ class VariableSpeedRingBuffer180 : public BaseBuffer { * @ingroup buffers * @tparam T */ -template -class VariableSpeedRingBuffer : public BaseBuffer { - public: +template class VariableSpeedRingBuffer : public BaseBuffer { +public: VariableSpeedRingBuffer(int size = 0, float increment = 1.0) { setIncrement(increment); - if (size > 0) resize(size); + if (size > 0) + resize(size); } void setIncrement(float increment) { read_increment = increment; } - bool resize(int size) { + void resize(int size) { buffer_size = size; // prevent an overrun at the start read_pos_float = size / 2; - return buffer.resize(size); + buffer.resize(size); } - bool read(T &result) { - assert(read_increment != 0.0f); - peek(result); + T read() { + assert(read_increment != 0.0); + T result = peek(); read_pos_float += read_increment; handleReadWriteOverrun(last_value); if (read_pos_float > buffer_size) { read_pos_float -= buffer_size; } - return true; + return result; } - bool peek(T &result) { - if (buffer.size() == 0) { - result = 0; - } else { - result = interpolate(read_pos_float); - } - return true; + T peek() { + if (buffer.size() == 0) + return 0; + return interpolate(read_pos_float); } bool write(T sample) { - if (buffer.size() == 0) return false; + if (buffer.size() == 0) + return false; handleReadWriteOverrun(last_value); buffer[write_pos++] = sample; - // on buffer overflow reset to 0 + // on buffer owerflow reset to 0 if (write_pos >= buffer_size) { write_pos = 0; } @@ -284,17 +275,16 @@ class VariableSpeedRingBuffer : public BaseBuffer { virtual int available() { return buffer_size; } virtual int availableForWrite() { return buffer_size; } virtual T *address() { return nullptr; } - size_t size() { return buffer_size; } - protected: +protected: Vector buffer{0}; int buffer_size; - float read_pos_float = 0.0f; - float read_increment = 0.0f; + float read_pos_float = 0.0; + float read_increment = 0.0; int write_pos = 0; // used to handle overruns: - T last_value = 0; // record last read value - bool incrementing; // is last read increasing + T last_value = 0; // record last read value + bool incrementing; // is last read increasing /// Calculate exact sample value for float position T interpolate(float read_pos) { @@ -302,7 +292,7 @@ class VariableSpeedRingBuffer : public BaseBuffer { T value1 = getValue(read_pos_int); T value2 = getValue(read_pos_int + 1); incrementing = value2 - value1 >= 0; - + // make sure that value1 is smaller then value 2 if (value2 < value1) { T tmp = value2; @@ -310,15 +300,12 @@ class VariableSpeedRingBuffer : public BaseBuffer { value1 = tmp; } // the result must be between value 1 and value 2: linear interpolation - float offset_in = read_pos - read_pos_int; // calculate fraction: e.g 0.5 - LOGD("read_pos=%f read_pos_int=%d, offset_in=%f", read_pos, read_pos_int, - offset_in); - float diff_result = - abs(value2 - value1); // differrence between values: e.g. 10 - float offset_result = offset_in * diff_result; // 0.5 * 10 = 5 + float offset_in = read_pos - read_pos_int; // calculate fraction: e.g 0.5 + LOGD("read_pos=%f read_pos_int=%d, offset_in=%f", read_pos, read_pos_int, offset_in); + float diff_result = abs(value2 - value1); // differrence between values: e.g. 10 + float offset_result = offset_in * diff_result; // 0.5 * 10 = 5 float result = offset_result + value1; - LOGD("interpolate %d %d -> %f -> %f", value1, value2, offset_result, - result); + LOGD("interpolate %d %d -> %f -> %f", value1, value2, offset_result, result); last_value = result; @@ -347,7 +334,7 @@ class VariableSpeedRingBuffer : public BaseBuffer { /// pointer is overpassing the read pointer we need to phase shift void handleReadWriteOverrun(T last_value) { // handle overflow - we need to allign the phase - int read_pos_int = read_pos_float; // round down + int read_pos_int = read_pos_float; // round down if (write_pos == read_pos_int || write_pos == (buffer_size % (read_pos_int + 1))) { LOGD("handleReadWriteOverrun write_pos=%d read_pos_int=%d", write_pos, @@ -365,7 +352,7 @@ class VariableSpeedRingBuffer : public BaseBuffer { float diff_value = abs(v1 - v2); float diff_last_value = abs(v1 - last_value); float fraction = 0; - if (diff_value > 0) { + if (diff_value>0){ fraction = diff_last_value / diff_value; } @@ -376,8 +363,7 @@ class VariableSpeedRingBuffer : public BaseBuffer { if (read_pos_float > buffer_size) { read_pos_float -= buffer_size; } - LOGD("handleReadWriteOverrun -> read_pos pos=%d pos_float=%f", pos, - read_pos_float); + LOGD("handleReadWriteOverrun -> read_pos pos=%d pos_float=%f", pos, read_pos_float); found = true; break; } @@ -401,7 +387,7 @@ class VariableSpeedRingBuffer : public BaseBuffer { */ template class PitchShiftOutput : public AudioOutput { - public: +public: PitchShiftOutput(Print &out) { p_out = &out; } PitchShiftInfo defaultConfig() { @@ -423,7 +409,8 @@ class PitchShiftOutput : public AudioOutput { size_t write(const uint8_t *data, size_t len) override { TRACED(); - if (!active) return 0; + if (!active) + return 0; size_t result = 0; int channels = cfg.channels; @@ -440,7 +427,7 @@ class PitchShiftOutput : public AudioOutput { // output values T out_value = pitchShift(value); - LOGD("PitchShiftOutput %f -> %d", value, (int)out_value); + LOGD("PitchShiftOutput %d -> %d", value, out_value); T out_array[channels]; for (int ch = 0; ch < channels; ch++) { out_array[ch] = out_value; @@ -452,7 +439,7 @@ class PitchShiftOutput : public AudioOutput { void end() { active = false; } - protected: +protected: BufferT buffer; bool active; PitchShiftInfo cfg; @@ -462,11 +449,12 @@ class PitchShiftOutput : public AudioOutput { // shifted result sample T pitchShift(T value) { TRACED(); - if (!active) return 0; + if (!active) + return 0; buffer.write(value); - buffer.read(value); - return true; + T out_value = buffer.read(); + return out_value; } }; -} // namespace audio_tools \ No newline at end of file +} // namespace audio_tools \ No newline at end of file diff --git a/src/AudioTools/CoreAudio/AudioEffects/README.md b/src/AudioEffects/README.md similarity index 100% rename from src/AudioTools/CoreAudio/AudioEffects/README.md rename to src/AudioEffects/README.md diff --git a/src/AudioEffects/SoundGenerator.h b/src/AudioEffects/SoundGenerator.h new file mode 100644 index 0000000000..b1576d4f6a --- /dev/null +++ b/src/AudioEffects/SoundGenerator.h @@ -0,0 +1,830 @@ +#pragma once + +#include "AudioTools/AudioLogger.h" +#include "AudioTools/AudioTypes.h" +#include "AudioBasic/Collections.h" + +/** + * @defgroup generator Generators + * @ingroup dsp + * @brief Sound Generators +**/ + + +namespace audio_tools { + +/** + * @brief Base class to define the abstract interface for the sound generating classes + * @ingroup generator + * @author Phil Schatzmann + * @copyright GPLv3 + * + * @tparam T + */ +template +class SoundGenerator { + public: + SoundGenerator() { + info.bits_per_sample = sizeof(T)*8; + info.channels = 1; + info.sample_rate = 44100; + } + + virtual ~SoundGenerator() { + end(); + } + + virtual bool begin(AudioInfo info) { + this->info = info; + return begin(); + } + + virtual bool begin() { + TRACED(); + active = true; + activeWarningIssued = false; + LOGI("bits_per_sample: %d", info.bits_per_sample); + LOGI("channels: %d", info.channels); + LOGI("sample_rate: %d", info.sample_rate); + + // support bytes < framesize + ring_buffer.resize(info.channels*sizeof(T)); + + return true; + } + + /// ends the processing + virtual void end(){ + active = false; + } + + /// Checks if the begin method has been called - after end() isActive returns false + virtual bool isActive() { + return active; + } + + /// Provides a single sample + virtual T readSample() = 0; + + /// Provides the data as byte array with the requested number of channels + virtual size_t readBytes( uint8_t *buffer, size_t lengthBytes){ + LOGD("readBytes: %d", (int)lengthBytes); + int channels = audioInfo().channels; + int frame_size = sizeof(T) * channels; + int frames = lengthBytes / frame_size; + if (lengthBytes>=frame_size){ + return readBytesFrames(buffer, lengthBytes, frames, channels); + } + return readBytesFromBuffer(buffer, lengthBytes, frame_size, channels); + } + + virtual AudioInfo defaultConfig(){ + AudioInfo def; + def.bits_per_sample = sizeof(T)*8; + def.channels = 1; + def.sample_rate = 44100; + return def; + } + + virtual void setFrequency(float frequency) { + LOGE("setFrequency not supported"); + } + + + virtual AudioInfo audioInfo() { + return info; + } + + virtual void setAudioInfo(AudioInfo info){ + this->info = info; + if (info.bits_per_sample!=sizeof(T)*8){ + LOGE("invalid bits_per_sample: %d", info.channels); + } + } + + protected: + bool active = false; + bool activeWarningIssued = false; + int output_channels = 1; + AudioInfo info; + RingBuffer ring_buffer{0}; + + size_t readBytesFrames(uint8_t *buffer, size_t lengthBytes, int frames, int channels ){ + T* result_buffer = (T*)buffer; + for (int j=0;j +class SineWaveGenerator : public SoundGenerator{ + public: + + // the scale defines the max value which is generated + SineWaveGenerator(float amplitude = 0.9 * NumberConverter::maxValueT(), float phase = 0.0){ + LOGD("SineWaveGenerator"); + m_amplitude = amplitude; + m_phase = phase; + } + + bool begin() override { + TRACEI(); + SoundGenerator::begin(); + this->m_deltaTime = 1.0 / SoundGenerator::info.sample_rate; + return true; + } + + bool begin(AudioInfo info) override { + LOGI("%s::begin(channels=%d, sample_rate=%d)","SineWaveGenerator", info.channels, info.sample_rate); + SoundGenerator::begin(info); + this->m_deltaTime = 1.0 / SoundGenerator::info.sample_rate; + return true; + } + + bool begin(AudioInfo info, float frequency){ + LOGI("%s::begin(channels=%d, sample_rate=%d, frequency=%.2f)","SineWaveGenerator",info.channels, info.sample_rate,frequency); + SoundGenerator::begin(info); + this->m_deltaTime = 1.0 / SoundGenerator::info.sample_rate; + if (frequency>0){ + setFrequency(frequency); + } + return true; + } + + bool begin(int channels, int sample_rate, float frequency=0.0){ + SoundGenerator::info.channels = channels; + SoundGenerator::info.sample_rate = sample_rate; + return begin(SoundGenerator::info, frequency); + } + + // update m_deltaTime + virtual void setAudioInfo(AudioInfo info) override { + SoundGenerator::setAudioInfo(info); + this->m_deltaTime = 1.0 / SoundGenerator::info.sample_rate; + } + + virtual AudioInfo defaultConfig() override { + return SoundGenerator::defaultConfig(); + } + + /// Defines the frequency - after the processing has been started + void setFrequency(float frequency) override { + LOGI("setFrequency: %.2f", frequency); + LOGI( "active: %s", SoundGenerator::active ? "true" : "false" ); + m_frequency = frequency; + } + + /// Provides a single sample + virtual T readSample() override { + float angle = double_Pi * m_cycles + m_phase; + T result = m_amplitude * sinf(angle); + m_cycles += m_frequency * m_deltaTime; + if (m_cycles > 1.0) { + m_cycles -= 1.0; + } + return result; + } + + void setAmplitude(float amp){ + m_amplitude = amp; + } + + protected: + volatile float m_frequency = 0; + float m_cycles = 0.0; // Varies between 0.0 and 1.0 + float m_amplitude = 1.0; + float m_deltaTime = 0.0; + float m_phase = 0.0; + float double_Pi = PI * 2.0; + + + void logStatus() { + SoundGenerator::info.logStatus(); + LOGI( "amplitude: %f", this->m_amplitude ); + LOGI( "active: %s", SoundGenerator::active ? "true" : "false" ); + } + +}; + +/** + * @brief Generates a square wave sound. + * @ingroup generator + * @author Phil Schatzmann + * @copyright GPLv3 + * + */ +template +class SquareWaveGenerator : public SineWaveGenerator { + public: + SquareWaveGenerator(float amplitude = 32767.0, float phase = 0.0) : SineWaveGenerator(amplitude, phase) { + LOGD("SquareWaveGenerator"); + } + + virtual T readSample() { + return value(SineWaveGenerator::readSample(), SineWaveGenerator::m_amplitude); + } + + protected: + // returns amplitude for positive vales and -amplitude for negative values + T value(T value, T amplitude) { + return (value >= 0) ? amplitude : -amplitude; + } +}; + + +/** + * @brief Sine wave which is based on a fast approximation function. + * @ingroup generator + * @author Vivian Leigh Stewart + * @copyright GPLv3 + * @tparam T + */ +template +class FastSineGenerator : public SineWaveGenerator { + public: + FastSineGenerator(float amplitude = 32767.0, float phase = 0.0) : SineWaveGenerator(amplitude, phase) { + LOGD("FastSineGenerator"); + } + + virtual T readSample() override { + float angle = SineWaveGenerator::m_cycles + + SineWaveGenerator::m_phase; + T result = SineWaveGenerator::m_amplitude * sine(angle); + SineWaveGenerator::m_cycles += SineWaveGenerator::m_frequency * + SineWaveGenerator::m_deltaTime; + if (SineWaveGenerator::m_cycles > 1.0) { + SineWaveGenerator::m_cycles -= 1.0; + } + return result; + } + + protected: + /// sine approximation. + inline float sine(float t) { + float p = (t - (int)t) - 0.5f; // 0 <= p <= 1 + float pp = p * p; + return (p - 6.283211f * pp * p + 9.132843f * pp * pp * p) * -6.221086f; + } +}; + +/** + * @brief Generates a random noise sound with the help of rand() function. + * @ingroup generator + * @author Phil Schatzmann + * @copyright GPLv3 + * + */ +template +class WhiteNoiseGenerator : public SoundGenerator { + public: + /// the scale defines the max value which is generated + WhiteNoiseGenerator(T amplitude = 32767) { + this->amplitude = amplitude; + } + + /// Provides a single sample + T readSample() { + return (random(-amplitude, amplitude)); + } + + protected: + T amplitude; + // //range : [min, max] + int random(int min, int max) { + return min + rand() % (( max + 1 ) - min); + } + + +}; + +/** + * @brief Generates pink noise. + * @ingroup generator + * @author Phil Schatzmann + * @copyright GPLv3 + * + */ +template class PinkNoiseGenerator : public SoundGenerator { +public: + /// the amplitude defines the max value which is generated + PinkNoiseGenerator(T amplitude = 32767) { + this->amplitude = amplitude; + max_key = 0x1f; // Five bits set + key = 0; + for (int i = 0; i < 5; i++) + white_values[i] = rand() % (amplitude / 5); + } + + /// Provides a single sample + T readSample() { + T last_key = key; + unsigned int sum; + + key++; + if (key > max_key) + key = 0; + // Exclusive-Or previous value with current value. This gives + // a list of bits that have changed. + int diff = last_key ^ key; + sum = 0; + for (int i = 0; i < 5; i++) { + // If bit changed get new random number for corresponding + // white_value + if (diff & (1 << i)) + white_values[i] = rand() % (amplitude / 5); + sum += white_values[i]; + } + return sum; + + } + +protected: + T max_key; + T key; + unsigned int white_values[5]; + unsigned int amplitude; +}; + + +/** + * @brief Provides a fixed value (e.g. 0) as sound data. This can be used e.g. to test the output functionality which should optimally just output + * silence and no artifacts. + * @ingroup generator + * @author Phil Schatzmann + * @copyright GPLv3 + * + */ +template +class SilenceGenerator : public SoundGenerator { + public: + // the scale defines the max value which is generated + SilenceGenerator(T value=0) { + this->value = value; + } + + /// Provides a single sample + T readSample() { + return value; // return 0 + } + + protected: + T value; + +}; + +/** + * @brief An Adapter Class which lets you use any Stream as a Generator + * @ingroup generator + * @author Phil Schatzmann + * @copyright GPLv3 + * @tparam T + */ +template +class GeneratorFromStream : public SoundGenerator { + public: + GeneratorFromStream() { + maxValue = NumberConverter::maxValue(sizeof(T)*8); + }; + + /** + * @brief Constructs a new Generator from a Stream object that can be used e.g. as input for AudioEffectss. + * + * @param input Stream + * @param channels number of channels of the Stream + * @param volume factor my which the sample value is multiplied - default 1.0; Use it e.g. to reduce the volume (e.g. with 0.5) + */ + GeneratorFromStream(Stream &input, int channels=1, float volume=1.0){ + maxValue = NumberConverter::maxValue(sizeof(T)*8); + setStream(input); + setVolume(volume); + setChannels(channels); + } + + /// (Re-)Assigns a stream to the Adapter class + void setStream(Stream &input){ + this->p_stream = &input; + } + + void setChannels(int channels){ + this->channels = channels; + } + + /// Multiplies the input with the indicated factor (e.g. ) + void setVolume(float factor){ + this->volume = factor; + } + + + /// Provides a single sample from the stream + T readSample() { + T data = 0; + float total = 0; + if (p_stream!=nullptr) { + for (int j=0;jreadBytes((uint8_t*)&data, sizeof(T)); + total += data; + } + float avg = (total / channels) * volume; + if (avg>maxValue){ + data = maxValue; + } else if (avg < -maxValue){ + data = -maxValue; + } else { + data = avg; + } + } + return data; + } + + protected: + Stream *p_stream = nullptr; + int channels=1; + int volume=1.0; + float maxValue; + +}; + +/** + * @brief We generate the samples from an array which is provided in the constructor + * @ingroup generator + * @author Phil Schatzmann + * @copyright GPLv3 + * @tparam T + */ + +template +class GeneratorFromArray : public SoundGenerator { + public: + + GeneratorFromArray() = default; + /** + * @brief Construct a new Generator From Array object + * + * @tparam array array of audio data of the the type defined as class template parameter + * @param repeat number of repetions the array should be played (default 1), set to 0 for endless repeat. + * @param setInactiveAtEnd defines if the generator is set inactive when the array has played fully. Default is true. + * @param startIndex defines if the phase. Default is 0. + */ + + template + GeneratorFromArray(T(&array)[arrayLen], int repeat=0, bool setInactiveAtEnd=true, size_t startIndex=0) { + TRACED(); + this->max_repeat = repeat; + this->inactive_at_end = setInactiveAtEnd; + this->sound_index = startIndex; + setArray(array, arrayLen); + } + + ~GeneratorFromArray(){ + if (owns_data){ + delete[] table; + } + } + + template + void setArray(T(&array)[arrayLen]){ + TRACED(); + setArray(array, arrayLen); + } + + void setArray(T*array, size_t size){ + this->table_length = size; + this->table = array; + LOGI("table_length: %d", (int)size); + } + + virtual bool begin(AudioInfo info) override { + return SoundGenerator::begin(info); + } + + /// Starts the generation of samples + bool begin() override { + TRACEI(); + SoundGenerator::begin(); + sound_index = 0; + repeat_counter = 0; + is_running = true; + return true; + } + + /// Provides a single sample + T readSample() override { + // at end deactivate output + if (sound_index >= table_length) { + // LOGD("reset index - sound_index: %d, table_length: %d",sound_index,table_length); + sound_index = 0; + // deactivate when count has been used up + if (max_repeat>=1 && ++repeat_counter>=max_repeat){ + LOGD("atEnd"); + this->is_running = false; + if (inactive_at_end){ + this->active = false; + } + } + } + + //LOGD("index: %d - active: %d", sound_index, this->active); + T result = 0; + if (this->is_running) { + result = table[sound_index]; + sound_index+=index_increment; + } + + return result; + } + + // step size the sound index is incremented (default = 1) + void setIncrement(int inc){ + index_increment = inc; + } + + // Sets up a sine table - returns the effective frequency + int setupSine(int sampleRate, float reqFrequency, float amplitude=1.0){ + int sample_count = static_cast(sampleRate) / reqFrequency; // e.g. 44100 / 300hz = 147 samples per wave + float angle = 2.0*PI / sample_count; + table = new T[sample_count]; + for (int j=0;j +class GeneratorFixedValue : public SoundGenerator { + public: + + GeneratorFixedValue() = default; + + virtual bool begin(AudioInfo info) { + return SoundGenerator::begin(info); + } + + void setValue(T value){ + value_set = value; + } + + /// Starts the generation of samples + bool begin() override { + TRACEI(); + SoundGenerator::begin(); + is_running = true; + value_return = value_set; + return true; + } + + /// Provides a single sample + T readSample() override { + return value_return; + } + + // Similar like is active to check if the array is still playing. + bool isRunning() { + return is_running; + } + + protected: + T value_set = 0; + T value_return = 0; + bool is_running = false; +}; + +/** + * @brief A sine generator based on a table. The table is created based using degrees where one full wave is 360 degrees. + * @ingroup generator + * @author Phil Schatzmann + * @copyright GPLv3 + */ +template +class SineFromTable : public SoundGenerator { + public: + SineFromTable(float amplitude = 32767.0){ + this->amplitude = amplitude; + this->amplitude_to_be = amplitude; + } + + /// Defines the new amplitude (volume) + void setAmplitude(float amplitude){ + this->amplitude_to_be = amplitude; + } + + /// To avoid pops we do not allow to big amplitude changes at once and spread them over time + void setMaxAmplitudeStep(float step){ + max_amplitude_step = step; + } + + T readSample() { + // update angle + angle += step; + if (angle >= 360){ + while(angle>=360.0){ + angle -= 360.0; + } + // update frequency at start of circle (near 0 degrees) + step = step_new; + + updateAmplitudeInSteps(); + //amplitude = amplitude_to_be; + } + return interpolate(angle); + } + + bool begin() { + is_first = true; + SoundGenerator::begin(); + base_frequency = SoundGenerator::audioInfo().sample_rate / 360.0; //122.5 hz (at 44100); 61 hz (at 22050) + return true; + } + + bool begin(AudioInfo info, float frequency) { + SoundGenerator::begin(info); + base_frequency = SoundGenerator::audioInfo().sample_rate / 360.0; //122.5 hz (at 44100); 61 hz (at 22050) + setFrequency(frequency); + return true; + } + + bool begin(int channels, int sample_rate, uint16_t frequency=0){ + SoundGenerator::info.channels = channels; + SoundGenerator::info.sample_rate = sample_rate; + return begin(SoundGenerator::info, frequency); + } + + void setFrequency(float freq) { + step_new = freq / base_frequency; + if (is_first){ + step = step_new; + is_first = false; + } + LOGD("step: %f", step_new); + } + + protected: + bool is_first = true; + float amplitude; + float amplitude_to_be; + float max_amplitude_step = 50; + float base_frequency = 1.0; + float step = 1.0; + float step_new = 1.0; + float angle = 0; + //122.5 hz (at 44100); 61 hz (at 22050) + const float values[181] = {0, 0.0174524, 0.0348995, 0.052336, 0.0697565, 0.0871557, 0.104528, 0.121869, 0.139173, 0.156434, 0.173648, 0.190809, 0.207912, 0.224951, 0.241922, 0.258819, 0.275637, 0.292372, 0.309017, 0.325568, 0.34202, 0.358368, 0.374607, 0.390731, 0.406737, 0.422618, 0.438371, 0.45399, 0.469472, 0.48481, 0.5, 0.515038, 0.529919, 0.544639, 0.559193, 0.573576, 0.587785, 0.601815, 0.615661, 0.62932, 0.642788, 0.656059, 0.669131, 0.681998, 0.694658, 0.707107, 0.71934, 0.731354, 0.743145, 0.75471, 0.766044, 0.777146, 0.788011, 0.798636, 0.809017, 0.819152, 0.829038, 0.838671, 0.848048, 0.857167, 0.866025, 0.87462, 0.882948, 0.891007, 0.898794, 0.906308, 0.913545, 0.920505, 0.927184, 0.93358, 0.939693, 0.945519, 0.951057, 0.956305, 0.961262, 0.965926, 0.970296, 0.97437, 0.978148, 0.981627, 0.984808, 0.987688, 0.990268, 0.992546, 0.994522, 0.996195, 0.997564, 0.99863, 0.999391, 0.999848, 1, 0.999848, 0.999391, 0.99863, 0.997564, 0.996195, 0.994522, 0.992546, 0.990268, 0.987688, 0.984808, 0.981627, 0.978148, 0.97437, 0.970296, 0.965926, 0.961262, 0.956305, 0.951057, 0.945519, 0.939693, 0.93358, 0.927184, 0.920505, 0.913545, 0.906308, 0.898794, 0.891007, 0.882948, 0.87462, 0.866025, 0.857167, 0.848048, 0.838671, 0.829038, 0.819152, 0.809017, 0.798636, 0.788011, 0.777146, 0.766044, 0.75471, 0.743145, 0.731354, 0.71934, 0.707107, 0.694658, 0.681998, 0.669131, 0.656059, 0.642788, 0.62932, 0.615661, 0.601815, 0.587785, 0.573576, 0.559193, 0.544639, 0.529919, 0.515038, 0.5, 0.48481, 0.469472, 0.45399, 0.438371, 0.422618, 0.406737, 0.390731, 0.374607, 0.358368, 0.34202, 0.325568, 0.309017, 0.292372, 0.275637, 0.258819, 0.241922, 0.224951, 0.207912, 0.190809, 0.173648, 0.156434, 0.139173, 0.121869, 0.104528, 0.0871557, 0.0697565, 0.052336, 0.0348995, 0.0174524, 0}; + + T interpolate(float angle){ + bool positive = (angle<=180); + float angle_positive = positive ? angle : angle - 180.0f; + int angle_int1 = angle_positive; + int angle_int2 = angle_int1+1; + T v1 = values[angle_int1] * amplitude; + T v2 = values[angle_int2] * amplitude; + T result = v1 < v2 ? map(angle_positive,angle_int1,angle_int2,v1, v2) : map(angle_positive,angle_int1,angle_int2,v2, v1) ; + //float result = v1; + return positive ? result : -result; + } + + T map(T x, T in_min, T in_max, T out_min, T out_max) { + return (x - in_min) * (out_max - out_min) / (in_max - in_min) + out_min; + } + + void updateAmplitudeInSteps() { + float diff = amplitude_to_be - amplitude; + if (abs(diff) > max_amplitude_step){ + diff = (diff<0) ? -max_amplitude_step : max_amplitude_step; + } + if (abs(diff)>=1.0f){ + amplitude += diff; + } + } +}; + +/** + * @brief Mixer which combines multiple sound generators into one output + * @ingroup generator + * @author Phil Schatzmann + * @copyright GPLv3 + * @tparam T + */ +template +class GeneratorMixer : public SoundGenerator { + public: + GeneratorMixer() = default; + + void add(SoundGenerator &generator){ + vector.push_back(&generator); + } + void add(SoundGenerator *generator){ + vector.push_back(generator); + } + + void clear() { + vector.clear(); + } + + T readSample() { + T result; + int count = 0; + for (int j=0;jreadSample(); + if (j==actualChannel){ + result = tmp; + } + } + actualChannel++; + if (actualChannel>=vector.size()){ + actualChannel = 0; + } + return result;; + } + + protected: + Vector*> vector; + int actualChannel=0; + +}; + +/** + * @brief Generates a test signal which is easy to check because the values are incremented or decremented by 1 + * @ingroup generator + * @author Phil Schatzmann + * @copyright GPLv3 + * @tparam T + */ +template +class TestGenerator : public SoundGenerator{ + public: + TestGenerator(T max=1000, T inc=1){ + this->max=max; + } + + T readSample() override { + value += inc; + if (abs(value)>=max){ + inc = -inc; + value += (inc * 2); + } + return value; + } + + protected: + T max; + T value=0; + T inc=1; + +}; + +} diff --git a/src/AudioTools/CoreAudio/AudioEffects/Synthesizer.h b/src/AudioEffects/Synthesizer.h similarity index 96% rename from src/AudioTools/CoreAudio/AudioEffects/Synthesizer.h rename to src/AudioEffects/Synthesizer.h index 81e3ecf18a..5eef3fb8c3 100644 --- a/src/AudioTools/CoreAudio/AudioEffects/Synthesizer.h +++ b/src/AudioEffects/Synthesizer.h @@ -1,12 +1,12 @@ #pragma once -#include "AudioTools/CoreAudio/AudioLogger.h" -#include "AudioTools/CoreAudio/AudioTypes.h" -#include "AudioTools/CoreAudio/AudioBasic/Collections.h" -#include "AudioTools/CoreAudio/AudioActions.h" -#include "AudioTools/CoreAudio/AudioStreams.h" -#include "SoundGenerator.h" -#include "AudioEffects.h" +#include "AudioTools/AudioLogger.h" +#include "AudioTools/AudioTypes.h" +#include "AudioBasic/Collections.h" +#include "AudioTools/AudioActions.h" +#include "AudioTools/AudioStreams.h" +#include "AudioEffects/SoundGenerator.h" +#include "AudioEffects/AudioEffects.h" #ifdef USE_MIDI #include "Midi.h" #endif @@ -224,7 +224,7 @@ class Synthesizer : public SoundGenerator { // prevent divide by zero int result = 0; if (count>0){ - result = NumberConverter::clipT(total / count); + result = NumberConverter::clip(total / count); } return result; } diff --git a/src/AudioFilter/Equilizer.h b/src/AudioFilter/Equilizer.h new file mode 100644 index 0000000000..96d0598b8e --- /dev/null +++ b/src/AudioFilter/Equilizer.h @@ -0,0 +1,237 @@ +#pragma once +#include +#include "AudioTools/AudioStreams.h" +#include "AudioTools/AudioOutput.h" +#include "AudioBasic/Int24.h" + +/** + * @defgroup equilizer Equilizer + * @ingroup dsp + * @brief Digital Equilizer +**/ + +namespace audio_tools { + +/** + * @brief Configuration for 3 Band Equilizer: Set channels,bits_per_sample,sample_rate. Set and update gain_low, gain_medium and gain_high to value between 0 and 1.0 + * @ingroup equilizer + * @author pschatzmann + */ +struct ConfigEquilizer3Bands : public AudioInfo { + ConfigEquilizer3Bands(){ + channels = 2; + bits_per_sample = 16; + sample_rate = 44100; + } + + ConfigEquilizer3Bands(const ConfigEquilizer3Bands&) = delete; + + // Frequencies + int freq_low=880; + int freq_high=5000; + + // Gain Controls + float gain_low = 1.0; + float gain_medium = 1.0; + float gain_high = 1.0; +}; + +/** + * @brief 3 Band Equilizer inspired from https://www.musicdsp.org/en/latest/Filters/236-3-band-equaliser.html + * @ingroup equilizer + * @author pschatzmann + */ +class Equilizer3Bands : public AudioStream { + public: + + Equilizer3Bands(Print &out) { + p_print = &out; + } + + Equilizer3Bands(Stream &in) { + p_stream = ∈ + } + + Equilizer3Bands(AudioOutput &out) { + p_out = &out; + p_print = &out; + out.setNotifyAudioChange(*this); + } + + Equilizer3Bands(AudioStream &stream) { + p_in = &stream; + p_stream = &stream; + p_print = &stream; + stream.setNotifyAudioChange(*this); + } + + ~Equilizer3Bands(){ + if (state!=nullptr) delete[]state; + } + + ConfigEquilizer3Bands &config() { + return cfg; + } + + + ConfigEquilizer3Bands &defaultConfig() { + return config(); + } + + bool begin(ConfigEquilizer3Bands &config){ + p_cfg = &config; + if (p_cfg->channels>max_state_count){ + if (state!=nullptr) delete[]state; + state = new EQSTATE[p_cfg->channels]; + max_state_count = p_cfg->channels; + } + + // Setup state + for (int j=0;jfreq_low / (float)p_cfg->sample_rate)); + state[j].hf = 2 * sin((float)M_PI * ((float)p_cfg->freq_high / (float)p_cfg->sample_rate)); + } + return true; + } + + virtual void setAudioInfo(AudioInfo info) override { + p_cfg->sample_rate = info.sample_rate; + p_cfg->channels = info.channels; + p_cfg->bits_per_sample = info.bits_per_sample; + begin(*p_cfg); + } + + size_t write(const uint8_t *data, size_t len) override { + filterSamples(data, len); + return p_print->write(data, len); + } + + int availableForWrite() override { + return p_print->availableForWrite(); + } + + /// Provides the data from all streams mixed together + size_t readBytes(uint8_t* data, size_t len) override { + size_t result = 0; + if (p_stream!=nullptr){ + result = p_stream->readBytes(data, len); + filterSamples(data, len); + } + return result; + } + + int available() override { + return p_stream!=nullptr ? p_stream->available():0; + } + + + protected: + ConfigEquilizer3Bands cfg; + ConfigEquilizer3Bands *p_cfg=&cfg; + const float vsa = (1.0 / 4294967295.0); // Very small amount (Denormal Fix) + Print *p_print = nullptr; // support for write + Stream *p_stream = nullptr; // support for write + AudioOutput *p_out=nullptr; // support for write + AudioStream *p_in=nullptr; // support for readBytes + int max_state_count=0; + + struct EQSTATE { + // Filter #1 (Low band) + float lf; // Frequency + float f1p0; // Poles ... + float f1p1; + float f1p2; + float f1p3; + + // Filter #2 (High band) + float hf; // Frequency + float f2p0; // Poles ... + float f2p1; + float f2p2; + float f2p3; + + // Sample history buffer + float sdm1; // Sample data minus 1 + float sdm2; // 2 + float sdm3; // 3 + + } *state=nullptr; + + void filterSamples(const uint8_t *data, size_t len){ + switch(p_cfg->bits_per_sample){ + case 16: { + int16_t* p_dataT = (int16_t*)data; + size_t sample_count = len / sizeof(int16_t); + for (size_t j=0; jchannels){ + for (int ch=0; chchannels; ch++){ + //p_dataT[j+ch] = sample(state[ch], 1.0 / 32767.0 * p_dataT[j+ch]) * 32767; + p_dataT[j+ch] = toInt16(sample(state[ch], toFloat(p_dataT[j+ch]))); + } + } + } + break; + + default: + LOGE("Only 16 bits supported: %d", p_cfg->bits_per_sample); + break; + } + } + + /// convert float in the range -1 to 1 to a int16 and clip the values that are out of range + inline int16_t toInt16(float v){ + float result = v * 32767.0f; + // clip result + if (result>32767){ + result = 32767; + } else if (result<-32767){ + result = -32767; + } + return result; + } + + /// convert float in the range -1 to 1 to a int16 and clip the values that are out of range + inline float toFloat(int16_t v){ + return static_cast(v) / 32767.0f; + } + + // calculates a single sample using the indicated state + float sample(EQSTATE &es, float sample) { + // Locals + float l,m,h; // Low / Mid / High - Sample Values + // Filter #1 (lowpass) + es.f1p0 += (es.lf * (sample - es.f1p0)) + vsa; + es.f1p1 += (es.lf * (es.f1p0 - es.f1p1)); + es.f1p2 += (es.lf * (es.f1p1 - es.f1p2)); + es.f1p3 += (es.lf * (es.f1p2 - es.f1p3)); + + l = es.f1p3; + + // Filter #2 (highpass) + es.f2p0 += (es.hf * (sample - es.f2p0)) + vsa; + es.f2p1 += (es.hf * (es.f2p0 - es.f2p1)); + es.f2p2 += (es.hf * (es.f2p1 - es.f2p2)); + es.f2p3 += (es.hf * (es.f2p2 - es.f2p3)); + + h = es.sdm3 - es.f2p3; + // Calculate midrange (signal - (low + high)) + m = es.sdm3 - (h + l); + // Scale, Combine and store + l *= p_cfg->gain_low; + m *= p_cfg->gain_medium; + h *= p_cfg->gain_high; + + // Shuffle history buffer + es.sdm3 = es.sdm2; + es.sdm2 = es.sdm1; + es.sdm1 = sample; + + // Return result + return(l + m + h); + } + +}; + +} // namespace \ No newline at end of file diff --git a/src/AudioFilter/Filter.h b/src/AudioFilter/Filter.h new file mode 100644 index 0000000000..f853a9232b --- /dev/null +++ b/src/AudioFilter/Filter.h @@ -0,0 +1,400 @@ +#pragma once +#include "AudioConfig.h" +#ifdef USE_TYPETRAITS +#include +#endif + +/** + * @defgroup dsp DSP + * @ingroup main + * @brief Digital Signal Processing +**/ + +/** + * @defgroup filter Filters + * @ingroup dsp + * @brief Digital Filters +**/ + + +namespace audio_tools { + +/** + * @brief Abstract filter interface definition; + * @ingroup filter + * @author pschatzmann + */ +template +class Filter { + public: + // construct without coefs + Filter() = default; + virtual ~Filter() = default; + Filter(Filter const&) = delete; + Filter& operator=(Filter const&) = delete; + + virtual T process(T in) = 0; +}; + +/** + * @brief No change to the input + * @ingroup filter + * @author pschatzmann + * @tparam T + */ +template +class NoFilter : Filter { + public: + // construct without coefs + NoFilter() = default; + virtual T process(T in){return in;} +}; + +/** + * @brief FIR Filter + * Converted from + * https://github.com/sebnil/FIR-filter-Arduino-Library/tree/master/src + * You can use https://www.arc.id.au/FilterDesign.html to design the filter + * @ingroup filter + * @author Pieter P tttapa / pschatzmann + * @copyright GNU General Public License v3.0 + */ +template +class FIR : public Filter { + public: + template + FIR(const T (&b)[B], const T factor=1.0) : lenB(B), factor(factor) { + setValues(b); + } + + template + void setValues(const T (&b)[B]){ + if (x!=nullptr) delete x; + x = new T[lenB](); + coeff_b = new T[2*lenB-1]; + for (uint16_t i = 0; i < 2*lenB-1; i++) { + coeff_b[i] = b[(2*lenB - 1 - i)%lenB]; + } + + } + + ~FIR() { + delete[] x; + delete[] coeff_b; + } + T process(T value) { + x[i_b] = value; + T b_terms = 0; + T *b_shift = &coeff_b[lenB - i_b - 1]; + for (uint8_t i = 0; i < lenB; i++) { + b_terms += b_shift[i] * x[i] ; + } + i_b++; + if(i_b == lenB) + i_b = 0; + +#ifdef USE_TYPETRAITS + if (!(std::is_same::value || std::is_same::value)) { + b_terms = b_terms / factor; + } +#else + if (factor!=1.0) { + b_terms = b_terms / factor; + } +#endif + return b_terms; + } + private: + const uint8_t lenB; + uint8_t i_b = 0; + T *x = nullptr; + T *coeff_b; + T factor; +}; + + +/** + * @brief IIRFilter + * Converted from https://github.com/tttapa/Filters/blob/master/src/IIRFilter.h + * @ingroup filter + * @author Pieter P tttapa / pschatzmann + * @copyright GNU General Public License v3.0 + * @tparam T + */ +template +class IIR : public Filter { + public: + template + IIR(const T (&b)[B], const T (&_a)[A], T factor=1.0) : factor(factor), lenB(B), lenA(A - 1) { + x = new T[lenB](); + y = new T[lenA](); + coeff_b = new T[2 * lenB - 1]; + coeff_a = new T[2 * lenA - 1]; + T a0 = _a[0]; + const T *a = &_a[1]; + for (uint16_t i = 0; i < 2 * lenB - 1; i++) { + coeff_b[i] = b[(2 * lenB - 1 - i) % lenB] / a0; + } + for (uint16_t i = 0; i < 2 * lenA - 1; i++) { + coeff_a[i] = a[(2 * lenA - 2 - i) % lenA] / a0; + } + } + + ~IIR() { + delete[] x; + delete[] y; + delete[] coeff_a; + delete[] coeff_b; + } + + T process(T value) { + x[i_b] = value; + T b_terms = 0; + T *b_shift = &coeff_b[lenB - i_b - 1]; + + T a_terms = 0; + T *a_shift = &coeff_a[lenA - i_a - 1]; + + for (uint8_t i = 0; i < lenB; i++) { + b_terms += x[i] * b_shift[i]; + } + for (uint8_t i = 0; i < lenA; i++) { + a_terms += y[i] * a_shift[i]; + } + + T filtered = b_terms - a_terms; + y[i_a] = filtered; + i_b++; + if (i_b == lenB) i_b = 0; + i_a++; + if (i_a == lenA) i_a = 0; + +#ifdef USE_TYPETRAITS + if (!(std::is_same::value || std::is_same::value)) { + filtered = filtered / factor; + } +#else + if (factor!=1.0) { + filtered = filtered / factor; + } +#endif + return filtered; + } + + private: + T factor; + const uint8_t lenB, lenA; + uint8_t i_b = 0, i_a = 0; + T *x; + T *y; + T *coeff_b; + T *coeff_a; +}; + +/** + * @brief Biquad DF1 Filter. + * converted from https://github.com/tttapa/Filters/blob/master/src/BiQuad.h + * Use float or double (and not a integer type) as type parameter + * @ingroup filter + * @author Pieter P tttapa / pschatzmann + * @copyright GNU General Public License v3.0 + * @tparam T + */ +template +class BiQuadDF1 : public Filter { + public: + BiQuadDF1(const T (&b)[3], const T (&a)[3]) + : b_0(b[0] / a[0]), + b_1(b[1] / a[0]), + b_2(b[2] / a[0]), + a_1(a[1] / a[0]), + a_2(a[2] / a[0]) {} + BiQuadDF1(const T (&b)[3], const T (&a)[2]) + : b_0(b[0]), b_1(b[1]), b_2(b[2]), a_1(a[0]), a_2(a[1]) {} + BiQuadDF1(const T (&b)[3], const T (&a)[2], T gain) + : b_0(gain * b[0]), + b_1(gain * b[1]), + b_2(gain * b[2]), + a_1(a[0]), + a_2(a[1]) {} + BiQuadDF1(const T (&b)[3], const T (&a)[3], T gain) + : b_0(gain * b[0] / a[0]), + b_1(gain * b[1] / a[0]), + b_2(gain * b[2] / a[0]), + a_1(a[1] / a[0]), + a_2(a[2] / a[0]) {} + + T process(T value) { + T x_2 = x_1; + x_1 = x_0; + x_0 = value; + T b_terms = x_0 * b_0 + x_1 * b_1 + x_2 * b_2; + T a_terms = y_1 * a_1 + y_2 * a_2; + y_2 = y_1; + y_1 = b_terms - a_terms; + return y_1; + } + + private: + const T b_0; + const T b_1; + const T b_2; + const T a_1; + const T a_2; + + T x_0 = 0; + T x_1 = 0; + T y_1 = 0; + T y_2 = 0; +}; + + +/** + * @brief Biquad DF2 Filter. When dealing with high-order IIR filters, they can get unstable. + * To prevent this, BiQuadratic filters (second order) are used. + * Converted from https://github.com/tttapa/Filters/blob/master/src/BiQuad.h + * Use float or double (and not a integer type) as type parameter + * @ingroup filter + * @author Pieter P tttapa / pschatzmann + * @copyright GNU General Public License v3.0 + * @tparam T + */ +template +class BiQuadDF2 : public Filter { + public: + BiQuadDF2(const T (&b)[3], const T (&a)[3]) + : b_0(b[0] / a[0]), + b_1(b[1] / a[0]), + b_2(b[2] / a[0]), + a_1(a[1] / a[0]), + a_2(a[2] / a[0]) {} + BiQuadDF2(const T (&b)[3], const T (&a)[2]) + : b_0(b[0]), b_1(b[1]), b_2(b[2]), a_1(a[0]), a_2(a[1]) {} + BiQuadDF2(const T (&b)[3], const T (&a)[2], T gain) + : b_0(gain * b[0]), + b_1(gain * b[1]), + b_2(gain * b[2]), + a_1(a[0]), + a_2(a[1]) {} + BiQuadDF2(const T (&b)[3], const T (&a)[3], T gain) + : b_0(gain * b[0] / a[0]), + b_1(gain * b[1] / a[0]), + b_2(gain * b[2] / a[0]), + a_1(a[1] / a[0]), + a_2(a[2] / a[0]) {} + + T process(T value) { + T w_2 = w_1; + w_1 = w_0; + w_0 = value - a_1 * w_1 - a_2 * w_2; + T y = b_0 * w_0 + b_1 * w_1 + b_2 * w_2; + return y; + } + + private: + const T b_0; + const T b_1; + const T b_2; + const T a_1; + const T a_2; + + T w_0 = 0; + T w_1 = 0; +}; + +/** + * @brief Second Order Filter: Instead of manually cascading BiQuad filters, you can use a Second Order Sections filter (SOS). + * converted from https://github.com/tttapa/Filters/blob/master/src/SOSFilter.h + * Use float or double (and not a integer type) as type parameter + * @ingroup filter + * @author Pieter P tttapa / pschatzmann + * @copyright GNU General Public License v3.0 + */ + +template +class SOSFilter : public Filter +{ + public: + SOSFilter(const T (&b)[N][3], const T (&a)[N][3], const T (&gain)[N]) + { + for (size_t i = 0; i < N; i++) + filters[i] = new BiQuadDF2(b[i], a[i], gain[i]); + } + SOSFilter(const T (&sos)[N][6], const T (&gain)[N]) + { + for (size_t i = 0; i < N; i++) { + T b[3]; + T a[3]; + copy(b, &sos[i][0]); + copy(a, &sos[i][3]); + filters[i] = new BiQuadDF2(b, a, gain[i]); + } + } + SOSFilter(const T (&b)[N][3], const T (&a)[N][2], const T (&gain)[N]) + { + for (size_t i = 0; i < N; i++) + filters[i] = new BiQuadDF2(b[i], a[i], gain[i]); + } + SOSFilter(const T (&b)[N][3], const T (&a)[N][2]) + { + for (size_t i = 0; i < N; i++) + filters[i] = new BiQuadDF2(b[i], a[i]); + } + SOSFilter(const T (&b)[N][3], const T (&a)[N][3]) + { + for (size_t i = 0; i < N; i++) + filters[i] = new BiQuadDF2(b[i], a[i]); + } + ~SOSFilter() + { + for (size_t i = 0; i < N; i++) + delete filters[i]; + } + T process(T value) + { + for (Filter *&filter : filters) + value = filter->process(value); + return value; + } + + private: + Filter *filters[N]; + template + void copy(T (&dest)[M], const T *src) { + for (size_t i = 0; i < M; i++) + dest[i] = src[i]; + } +}; + +/** + * @brief FilterChain - A Cascade of multiple filters + * @ingroup filter + * @tparam T + * @tparam N + */ +template +class FilterChain : public Filter { + public: + FilterChain(Filter * (&&filters)[N]) + { + for (size_t i = 0; i < N; i++) { + this->filters[i] = filters[i]; + } + } + + T process(T value) + { + for (Filter *&filter : filters) { + if (filter!=nullptr){ + value = filter->process(value); + } + } + return value; + } + + private: + Filter *filters[N] = {0}; +}; + + +} // namespace audio_tools diff --git a/src/AudioHttp/AbstractURLStream.h b/src/AudioHttp/AbstractURLStream.h new file mode 100644 index 0000000000..5fa82fe2c1 --- /dev/null +++ b/src/AudioHttp/AbstractURLStream.h @@ -0,0 +1,39 @@ +#pragma once + +namespace audio_tools { + +/** + * @brief Abstract Base class for all URLStream implementations + * @author Phil Schatzmann + * @ingroup http + * @copyright GPLv3 + */ +class AbstractURLStream : public AudioStream { + public: + // executes the URL request + virtual bool begin(const char* urlStr, const char* acceptMime=nullptr, MethodID action=GET, const char* reqMime="", const char*reqData="") = 0; + // ends the request + virtual void end() override =0; + /// provides access to the HttpRequest + virtual HttpRequest &httpRequest()=0; + // only the ICYStream supports this + virtual bool setMetadataCallback(void (*fn)(MetaDataType info, const char* str, int len)) { + return false; + } + /// Writes are not supported + int availableForWrite() override { + return 0; + } + + /// (Re-)defines the client + virtual void setClient(Client &clientPar) = 0; + + /// Sets the ssid that will be used for logging in (when calling begin) + virtual void setSSID(const char* ssid) = 0; + + /// Sets the password that will be used for logging in (when calling begin) + virtual void setPassword(const char* password) = 0; + +}; + +} diff --git a/src/AudioHttp/AudioHttp.h b/src/AudioHttp/AudioHttp.h new file mode 100644 index 0000000000..1bc30d9501 --- /dev/null +++ b/src/AudioHttp/AudioHttp.h @@ -0,0 +1,12 @@ +#pragma once +/** + * @defgroup http Http + * @ingroup communications + * @brief Http client & server +**/ + +#include "AudioHttp/URLStream.h" +#include "AudioHttp/URLStreamBuffered.h" +#include "AudioHttp/AudioServer.h" +#include "AudioHttp/ICYStream.h" +#include "AudioHttp/ICYStreamBuffered.h" diff --git a/src/AudioHttp/AudioServer.h b/src/AudioHttp/AudioServer.h new file mode 100644 index 0000000000..5d26397886 --- /dev/null +++ b/src/AudioHttp/AudioServer.h @@ -0,0 +1,425 @@ +#pragma once + +#include "AudioConfig.h" +#ifdef USE_AUDIO_SERVER + +#ifdef ESP32 +#include +#endif +#include "AudioCodecs/CodecWAV.h" +#include "AudioTools.h" + +namespace audio_tools { + +/// Calback which writes the sound data to the stream +typedef void (*AudioServerDataCallback)(Print *out); + +/** + * @brief A simple Arduino Webserver which streams the result + * This class is based on the WiFiServer class. All you need to do is to provide the data + * with a callback method or from an Arduino Stream: in -copy> client + * + * @ingroup http + * @author Phil Schatzmann + * @copyright GPLv3 + */ +class AudioServer { + + public: + /** + * @brief Construct a new Audio W A V Server object + * We assume that the WiFi is already connected + */ + AudioServer(int port=80) { + // the client returns 0 for avialableForWrite() + copier.setCheckAvailableForWrite(false); + setupServer(port); + } + + + /** + * @brief Construct a new Audio W A V Server object + * + * @param network + * @param password + */ + AudioServer(const char* network, const char *password, int port=80) { + this->network = (char*)network; + this->password = (char*)password; + // the client returns 0 for avialableForWrite() + copier.setCheckAvailableForWrite(false); + setupServer(port); + } + + /** + * @brief Start the server. You need to be connected to WiFI before calling this method + * + * @param in + * @param contentType Mime Type of result + */ + void begin(Stream &in, const char* contentType) { + TRACED(); + this->in = ∈ + this->content_type = contentType; + + connectWiFi(); + + // start server + server.begin(); + } + + /** + * @brief Start the server. The data must be provided by a callback method + * + * @param cb + * @param contentType Mime Type of result + */ + void begin(AudioServerDataCallback cb, const char* contentType) { + TRACED(); + this->in =nullptr; + this->callback = cb; + this->content_type = contentType; + + connectWiFi(); + + // start server + server.begin(); + } + + /** + * @brief Add this method to your loop + * Returns true while the client is connected. (The same functionality like doLoop()) + * + * @return true + * @return false + */ + bool copy(){ + return doLoop(); + } + + /** + * @brief Add this method to your loop + * Returns true while the client is connected. + */ + bool doLoop() { + //LOGD("doLoop"); + bool active = true; + if (!client_obj.connected()) { + client_obj = server.available(); // listen for incoming clients + processClient(); + } else { + // We are connected: copy input from source to wav output + if (client_obj){ + if (callback==nullptr) { + LOGD("copy data..."); + if (converter_ptr==nullptr) { + copier.copy(); + }else { + copier.copy(*converter_ptr); + } + // if we limit the size of the WAV the encoder gets automatically closed when all has been sent + if (!client_obj) { + LOGI("stop client..."); + client_obj.stop(); + active = false; + } + } + } else { + LOGI("client was not connected"); + } + } + return active; + } + + // defines a converter that will be used when the audio is rendered + void setConverter(BaseConverter *c){ + converter_ptr = c; + } + + Stream& out() { + return client_obj; + } + + Stream* out_ptr() { + return &client_obj; + } + + protected: + // WIFI +#ifdef ESP32 + WiFiServer server; +#else + WiFiServer server{80}; +#endif + WiFiClient client_obj; + char *password = nullptr; + char *network = nullptr; + + // Content + const char *content_type=nullptr; + AudioServerDataCallback callback = nullptr; + Stream *in = nullptr; + StreamCopy copier; + BaseConverter *converter_ptr = nullptr; + + void setupServer(int port) { + WiFiServer tmp(port); + server = tmp; + } + + void connectWiFi() { + TRACED(); + if (WiFi.status() != WL_CONNECTED && network!=nullptr && password != nullptr){ + WiFi.begin(network, password); + //WiFi.setSleep(false); + while (WiFi.status() != WL_CONNECTED){ + Serial.print("."); + delay(500); + } + Serial.println(); + } + Serial.print("IP address: "); + Serial.println(WiFi.localIP()); + } + + virtual void sendReplyHeader(){ + TRACED(); + // HTTP headers always start with a response code (e.g. HTTP/1.1 200 OK) + // and a content-type so the client knows what's coming, then a blank line: + client_obj.println("HTTP/1.1 200 OK"); + if (content_type!=nullptr){ + client_obj.print("Content-type:"); + client_obj.println(content_type); + } + client_obj.println(); + + } + + virtual void sendReplyContent() { + TRACED(); + if (callback!=nullptr){ + // provide data via Callback + LOGI("sendReply - calling callback"); + callback(&client_obj); + client_obj.stop(); + } else if (in!=nullptr){ + // provide data for stream + LOGI("sendReply - Returning audio stream..."); + copier.begin(client_obj, *in); + } + } + + // Handle an new client connection and return the data + void processClient() { + //LOGD("processClient"); + if (client_obj) { // if you get a client, + LOGI("New Client."); // print a message out the serial port + String currentLine = ""; // make a String to hold incoming data from the client + while (client_obj.connected()) { // loop while the client's connected + if (client_obj.available()) { // if there's bytes to read from the client, + char c = client_obj.read(); // read a byte, then + if (c == '\n') { // if the byte is a newline character + + // if the current line is blank, you got two newline characters in a row. + // that's the end of the client HTTP request, so send a response: + if (currentLine.length() == 0){ + sendReplyHeader(); + sendReplyContent(); + // break out of the while loop: + break; + } else { // if you got a newline, then clear currentLine: + currentLine = ""; + } + } + else if (c != '\r') { // if you got anything else but a carriage return character, + currentLine += c; // add it to the end of the currentLine + } + } + } + } + } +}; + + +/** + * @brief A simple Arduino Webserver which streams the audio using the indicated encoder.. + * This class is based on the WiFiServer class. All you need to do is to provide the data + * with a callback method or from a Stream. + * + * @ingroup http + * @author Phil Schatzmann + * @copyright GPLv3 + */ +class AudioEncoderServer : public AudioServer { + + public: + + /** + * @brief Construct a new Audio W A V Server object + * We assume that the WiFi is already connected + */ + AudioEncoderServer(AudioEncoder *encoder, int port=80) : AudioServer(port) { + this->encoder = encoder; + } + + /** + * @brief Construct a new Audio W A V Server object + * + * @param network + * @param password + */ + AudioEncoderServer(AudioEncoder *encoder, const char* network, const char *password, int port=80) : AudioServer(network, password, port) { + this->encoder = encoder; + } + + /** + * @brief Destructor release the memory + **/ + ~AudioEncoderServer() { + } + + + /** + * @brief Start the server. You need to be connected to WiFI before calling this method + * + * @param in + * @param sample_rate + * @param channels + */ + void begin(Stream &in, int sample_rate, int channels, int bits_per_sample=16, BaseConverter *converter=nullptr) { + TRACED(); + this->in = ∈ + audio_info.sample_rate = sample_rate; + audio_info.channels = channels; + audio_info.bits_per_sample = bits_per_sample; + encoder->setAudioInfo(audio_info); + //encoded_stream.begin(&client_obj, encoder); + encoded_stream.setInput(&client_obj); + encoded_stream.setEncoder(encoder); + encoded_stream.begin(); + AudioServer::begin(in, encoder->mime()); + } + + /** + * @brief Start the server. You need to be connected to WiFI before calling this method + * + * @param in + * @param info + * @param converter + */ + void begin(Stream &in, AudioInfo info, BaseConverter *converter=nullptr) { + TRACED(); + this->in = ∈ + this->audio_info = info; + encoder->setAudioInfo(audio_info); + //encoded_stream.begin(&client_obj, encoder); + encoded_stream.setInput(&client_obj); + encoded_stream.setEncoder(encoder); + encoded_stream.begin(); + + AudioServer::begin(in, encoder->mime()); + } + + /** + * @brief Start the server. The data must be provided by a callback method + * + * @param cb + * @param sample_rate + * @param channels + */ + void begin(AudioServerDataCallback cb, int sample_rate, int channels, int bits_per_sample=16) { + TRACED(); + audio_info.sample_rate = sample_rate; + audio_info.channels = channels; + audio_info.bits_per_sample = bits_per_sample; + encoder->setAudioInfo(audio_info); + + AudioServer::begin(cb, encoder->mime()); + } + + // provides a pointer to the encoder + AudioEncoder *audioEncoder(){ + return encoder; + } + + + protected: + + // Sound Generation + EncodedAudioStream encoded_stream; + AudioInfo audio_info; + AudioEncoder *encoder = nullptr; + + + virtual void sendReplyContent() { + TRACED(); + if (callback!=nullptr){ + //encoded_stream.begin(out_ptr(), encoder); + encoded_stream.setOutput(out_ptr()); + encoded_stream.setEncoder(encoder); + encoded_stream.begin(); + + // provide data via Callback to encoded_stream + LOGI("sendReply - calling callback"); + callback(&encoded_stream); + client_obj.stop(); + } else if (in!=nullptr){ + // provide data for stream: in -copy> encoded_stream -> out + LOGI("sendReply - Returning encoded stream..."); + //encoded_stream.begin(out_ptr(), encoder); + encoded_stream.setOutput(out_ptr()); + encoded_stream.setEncoder(encoder); + encoded_stream.begin(); + + copier.begin(encoded_stream, *in); + } + } + + +}; + +/** + * @brief A simple Arduino Webserver which streams the audio as WAV data. + * This class is based on the AudioEncodedServer class. All you need to do is to provide the data + * with a callback method or from a Stream. + * @ingroup http + * @author Phil Schatzmann + * @copyright GPLv3 + */ +class AudioWAVServer : public AudioEncoderServer { + public: + /** + * @brief Construct a new Audio W A V Server object + * We assume that the WiFi is already connected + */ + AudioWAVServer(int port=80) : AudioEncoderServer(new WAVEncoder(), port){ + } + + /** + * @brief Construct a new Audio W A V Server object + * + * @param network + * @param password + */ + AudioWAVServer(const char* network, const char *password, int port=80) : AudioEncoderServer(new WAVEncoder(), network, password, port) { + } + + /// Destructor: release the allocated encoder + ~AudioWAVServer() { + AudioEncoder *encoder = audioEncoder(); + if (encoder!=nullptr){ + delete encoder; + } + } + + // provides a pointer to the encoder + WAVEncoder &wavEncoder(){ + return *static_cast(encoder); + } + + +}; + +} + +#endif diff --git a/src/AudioHttp/HttpChunkReader.h b/src/AudioHttp/HttpChunkReader.h new file mode 100644 index 0000000000..cb133ff518 --- /dev/null +++ b/src/AudioHttp/HttpChunkReader.h @@ -0,0 +1,130 @@ +#pragma once + + +#include "AudioHttp/HttpHeader.h" +#include "AudioHttp/HttpLineReader.h" + +namespace audio_tools { + +/** + * @brief Http might reply with chunks. So we need to dechunk the data. + * see https://en.wikipedia.org/wiki/Chunked_transfer_encoding + * @author Phil Schatzmann + * @copyright GPLv3 + */ +class HttpChunkReader : public HttpLineReader { + public: + /// default constructor + HttpChunkReader(){ + open_chunk_len = 0; + has_ended = false; + } + + /// constructor for processing final header information + HttpChunkReader(HttpReplyHeader &header){ + http_heaer_ptr = &header; + open_chunk_len = 0; + has_ended = false; + } + + void open(Client &client){ + LOGD("HttpChunkReader: %s", "open"); + has_ended = false; + readChunkLen(client); + + } + + // reads a block of data from the chunks + virtual int read(Client &client, uint8_t* str, int len) { + LOGD("HttpChunkReader: %s", "read"); + if (has_ended && open_chunk_len==0) return 0; + + // read the chunk data - but not more then available + int read_max = len < open_chunk_len ? len : open_chunk_len; + int len_processed = client.read(str, read_max); + // update current unprocessed chunk + open_chunk_len -= len_processed; + + // remove traling CR LF from data + if (open_chunk_len<=0){ + removeCRLF(client); + readChunkLen(client); + } + + return len_processed; + } + + // reads a single line from the chunks + virtual int readln(Client &client, uint8_t* str, int len, bool incl_nl=true){ + LOGD("HttpChunkReader: %s", "readln"); + if (has_ended && open_chunk_len==0) return 0; + + int read_max = len < open_chunk_len ? len : open_chunk_len; + int len_processed = readlnInternal(client, str, read_max, incl_nl); + open_chunk_len -= len_processed; + + // the chunks are terminated by a final CRLF + if (open_chunk_len<=0){ + removeCRLF(client); + readChunkLen(client); + } + + return len_processed; + + } + + int available() { + int result = has_ended ? 0 : open_chunk_len; + char msg[50]; + sprintf(msg,"available=>%d",result); + LOGD("HttpChunkReader: %s",msg); + + return result; + } + + + protected: + int open_chunk_len; + bool has_ended=false; + HttpReplyHeader *http_heaer_ptr; + + + void removeCRLF(Client &client){ + LOGD("HttpChunkReader: %s", "removeCRLF"); + // remove traling CR LF from data + if (client.peek()=='\r'){ + LOGD("HttpChunkReader: %s", "removeCR"); + client.read(); + } + if (client.peek()=='\n'){ + LOGD("HttpChunkReader: %s", "removeLF"); + client.read(); + } + } + + + // we read the chunk length which is indicated as hex value + virtual void readChunkLen(Client &client) { + LOGD("HttpChunkReader::readChunkLen"); + uint8_t len_str[51]; + readlnInternal(client, len_str, 50, false); + LOGD("HttpChunkReader::readChunkLen %s", (const char*)len_str); + open_chunk_len = strtol((char*)len_str, nullptr, 16); + + char msg[40]; + sprintf(msg, "chunk_len: %d",open_chunk_len); + LOGD("HttpChunkReader::readChunkLen->%s", msg); + + if (open_chunk_len==0){ + has_ended = true; + LOGD("HttpChunkReader::readChunkLen %s", "last chunk received"); + // processing of additinal final headers after the chunk end + if (http_heaer_ptr!=nullptr){ + http_heaer_ptr->readExt(client); + } + } + } +}; + +} + diff --git a/src/AudioHttp/HttpHeader.h b/src/AudioHttp/HttpHeader.h new file mode 100644 index 0000000000..3922e92537 --- /dev/null +++ b/src/AudioHttp/HttpHeader.h @@ -0,0 +1,478 @@ +#pragma once + +#include "AudioBasic/Collections.h" +#include "AudioBasic/StrExt.h" +#include "AudioConfig.h" +#include "AudioHttp/HttpLineReader.h" +#include "AudioHttp/Url.h" +#include "AudioHttp/HttpTypes.h" +#if defined(ARDUINO) || defined(IS_DESKTOP) +#include "Client.h" +#endif +namespace audio_tools { + +// Class Configuration + +// Define relevant header content +INLINE_VAR const char* CONTENT_TYPE = "Content-Type"; +INLINE_VAR const char* CONTENT_LENGTH = "Content-Length"; +INLINE_VAR const char* CONNECTION = "Connection"; +INLINE_VAR const char* CON_CLOSE = "close"; +INLINE_VAR const char* CON_KEEP_ALIVE = "keep-alive"; +INLINE_VAR const char* TRANSFER_ENCODING = "Transfer-Encoding"; +INLINE_VAR const char* CHUNKED = "chunked"; +INLINE_VAR const char* ACCEPT = "Accept"; +INLINE_VAR const char* ACCEPT_ALL = "*/*"; +INLINE_VAR const char* SUCCESS = "Success"; +INLINE_VAR const char* USER_AGENT = "User-Agent"; +INLINE_VAR const char* DEFAULT_AGENT = "Mozilla/5.0 (compatible; Googlebot/2.1; +http://www.google.com/bot.html)"; +INLINE_VAR const char* HOST_C = "Host"; +INLINE_VAR const char* ACCEPT_ENCODING = "Accept-Encoding"; +INLINE_VAR const char* IDENTITY = "identity"; +INLINE_VAR const char* LOCATION = "Location"; + + +// Http methods +INLINE_VAR const char* methods[] = {"?","GET","HEAD","POST","PUT","DELETE","TRACE","OPTIONS","CONNECT","PATCH",nullptr}; + +/** + * @brief A individual key - value header line + * + */ +struct HttpHeaderLine { + StrExt key; + StrExt value; + bool active; +}; + +/** + * @brief In a http request and reply we need to process header information. With this API + * we can define and query the header information. The individual header lines are stored + * in a vector. This is the common functionality for the HttpRequest and HttpReplyHeader + * subclasses + * @author Phil Schatzmann + * @copyright GPLv3 + * + */ +class HttpHeader { + public: + HttpHeader(){ + LOGI("HttpHeader"); + // set default values + protocol_str = "HTTP/1.1"; + url_path = "/"; + status_msg = ""; + } + ~HttpHeader(){ + LOGI("~HttpHeader"); + clear(true); + } + + /// clears the data - usually we do not delete but we just set the active flag + HttpHeader& clear(bool activeFlag=true) { + is_written = false; + is_chunked = false; + url_path = "/"; + for (auto it = lines.begin() ; it != lines.end(); ++it){ + if (activeFlag){ + (*it)->active = false; + } else { + delete *it; + } + } + if (!activeFlag){ + lines.clear(); + } + return *this; + } + + HttpHeader& put(const char* key, const char* value){ + if (value!=nullptr && strlen(value)>0){ + LOGD("HttpHeader::put %s %s", key, value); + HttpHeaderLine *hl = headerLine(key); + if (hl==nullptr){ + if (create_new_lines) + LOGE("HttpHeader::put - did not add HttpHeaderLine for %s", key); + return *this; + } + + // log entry + LOGD("HttpHeader::put -> '%s' : '%s'", key, value); + + hl->value = value; + hl->active = true; + + if (Str(key) == TRANSFER_ENCODING && Str(value) == CHUNKED){ + LOGD("HttpHeader::put -> is_chunked!!!"); + this->is_chunked = true; + } + } else { + LOGD("HttpHeader::put - value ignored because it is null for %s", key); + } + return *this; + } + + /// adds a new line to the header - e.g. for content size + HttpHeader& put(const char* key, int value){ + LOGD("HttpHeader::put %s %d", key, value); + HttpHeaderLine *hl = headerLine(key); + + if (value>1000){ + LOGW("value is > 1000"); + } + + // add value + hl->value = value; + hl->active = true; + LOGI("%s %s", key, hl->value.c_str()); + return *this; + } + + /// adds a received new line to the header + HttpHeader& put(const char* line){ + LOGD("HttpHeader::put -> %s", (const char*) line); + Str keyStr(line); + int pos = keyStr.indexOf(":"); + char *key = (char*)line; + key[pos] = 0; + + // usually there is a leading space - but unfurtunately not always + const char *value = line+pos+1; + if (value[0]==' '){ + value = line+pos+2; + } + return put((const char*)key, value); + } + + // determines a header value with the key + const char* get(const char* key){ + for (auto it = lines.begin() ; it != lines.end(); ++it){ + HttpHeaderLine *line = *it; + line->key.trim(); + if (line->key.equalsIgnoreCase(key)){ + const char* result = line->value.c_str(); + return line->active ? result : nullptr; + } + } + return nullptr; + } + + // reads a single header line + void readLine(Client &in, char* str, int len){ + reader.readlnInternal(in, (uint8_t*) str, len, false); + LOGI("HttpHeader::readLine -> %s",str); + } + + // writes a lingle header line + void writeHeaderLine(Client &out,HttpHeaderLine *header ){ + if (header==nullptr){ + LOGI("HttpHeader::writeHeaderLine: the value must not be null"); + return; + } + LOGD("HttpHeader::writeHeaderLine: %s",header->key.c_str()); + if (!header->active){ + LOGI("HttpHeader::writeHeaderLine - not active"); + return; + } + if (header->value.c_str() == nullptr){ + LOGI("HttpHeader::writeHeaderLine - ignored because value is null"); + return; + } + + char msg[200]; + Str msg_str(msg,200); + msg_str = header->key.c_str(); + msg_str += ": "; + msg_str += header->value.c_str(); + msg_str += CRLF; + out.print(msg); + + // remove crlf from log + int len = strlen(msg); + msg[len-2] = 0; + LOGI(" -> %s ", msg); + + // marke as processed + header->active = false; + } + + const char* urlPath() { + return url_path.c_str(); + } + + const char* protocol() { + return protocol_str.c_str(); + } + + MethodID method(){ + return method_id; + } + + int statusCode() { + return status_code; + } + + const char* statusMessage() { + return status_msg.c_str(); + } + + bool isChunked(){ + // the value is automatically set from the reply + return is_chunked; + } + + /// reads the full header from the request (stream) + void read(Client &in) { + LOGI("HttpHeader::read"); + // remove all existing value + clear(); + + char line[MAX_HTTP_HEADER_LINE_LENGTH]; + if (in.connected()){ + if (in.available()==0) { + LOGW("Waiting for data..."); + while(in.available()==0){ + delay(500); + } + } + readLine(in, line, MAX_HTTP_HEADER_LINE_LENGTH); + parse1stLine(line); + while (in.available()){ + readLine(in, line, MAX_HTTP_HEADER_LINE_LENGTH); + if (isValidStatus() || isRedirectStatus()){ + Str lineStr(line); + lineStr.ltrim(); + if (lineStr.isEmpty()){ + break; + } + put(line); + } + } + } else { + LOGW("connection lost"); + } + } + + /// writes the full header to the indicated HttpStreamedMultiOutput stream + void write(Client &out){ + LOGI("HttpHeader::write"); + write1stLine(out); + for (auto it = lines.begin() ; it != lines.end(); ++it){ + writeHeaderLine(out, *it); + } + // print empty line + crlf(out); + out.flush(); + is_written = true; + } + + /// automatically create new lines + void setAutoCreateLines(bool is_auto_line){ + create_new_lines = is_auto_line; + } + + /// returns true if status code >=200 and < 300 + bool isValidStatus() { + return status_code >= 200 && status_code < 300; + } + + bool isRedirectStatus() { + return status_code >= 300 && status_code < 400; + } + + protected: + int status_code = UNDEFINED; + bool is_written = false; + bool is_chunked = false; + bool create_new_lines = true; + MethodID method_id; + // we store the values on the heap. this is acceptable because we just have one instance for the + // requests and one for the replys: which needs about 2*100 bytes + StrExt protocol_str = StrExt(10); + StrExt url_path = StrExt(70); + StrExt status_msg = StrExt(20); + Vector lines; + HttpLineReader reader; + const char* CRLF = "\r\n"; + + // the headers need to delimited with CR LF + void crlf(Client &out) { + out.print(CRLF); + LOGI(" -> %s ", ""); + } + + // gets or creates a header line by key + HttpHeaderLine *headerLine(const char* key) { + if (key!=nullptr){ + for (auto it = lines.begin() ; it != lines.end(); ++it){ + HttpHeaderLine *pt = (*it); + if (pt!=nullptr && pt->key.c_str()!=nullptr){ + if (pt->key.equalsIgnoreCase(key)){ + pt->active = true; + return pt; + } + } + } + if (create_new_lines){ + HttpHeaderLine *newLine = new HttpHeaderLine(); + LOGD("HttpHeader::headerLine - new line created for %s", key); + newLine->active = true; + newLine->key = key; + lines.push_back(newLine); + return newLine; + } + } else { + LOGI("HttpHeader::headerLine %s", "The key must not be null"); + } + return nullptr; + } + + MethodID getMethod(const char* line){ + // set action + for (int j=0; methods[j]!=nullptr;j++){ + const char *action = methods[j]; + int len = strlen(action); + if (strncmp(action,line,len)==0){ + return (MethodID) j; + } + } + return (MethodID)0; + } + + + virtual void write1stLine(Client &out) = 0; + virtual void parse1stLine(const char *line) = 0; + + +}; + +/** + * @brief Reading and writing of Http Requests + * @author Phil Schatzmann + * @copyright GPLv3 + */ +class HttpRequestHeader : public HttpHeader { + public: + // Defines the action id, url path and http version for an request + HttpHeader& setValues(MethodID id, const char* urlPath, const char* protocol=nullptr){ + this->method_id = id; + this->url_path = urlPath; + + LOGI("HttpRequestHeader::setValues - path: %s",this->url_path.c_str()); + if (protocol!=nullptr){ + this->protocol_str = protocol; + } + return *this; + } + + // action path protocol + void write1stLine(Client &out){ + LOGI("HttpRequestHeader::write1stLine"); + char msg[200]; + Str msg_str(msg,200); + + const char* method_str = methods[this->method_id]; + msg_str = method_str; + msg_str += " "; + msg_str += this->url_path.c_str(); + msg_str += " "; + msg_str += this->protocol_str.c_str(); + msg_str += CRLF; + out.print(msg); + + int len = strlen(msg); + msg[len-2]=0; + LOGI("-> %s", msg); + } + + // parses the requestline + // Request-Line = Method SP Request-URI SP HTTP-Version CRLF + void parse1stLine(const char *line){ + LOGI("HttpRequestHeader::parse1stLine %s", line); + Str line_str(line); + int space1 = line_str.indexOf(" "); + int space2 = line_str.indexOf(" ", space1+1); + + this->method_id = getMethod(line); + this->protocol_str.substring(line_str, space2+1, line_str.length()); + this->url_path.substring(line_str,space1+1,space2); + this->url_path.trim(); + + LOGI("->method %s", methods[this->method_id]); + LOGI("->protocol %s", protocol_str.c_str()); + LOGI("->url_path %s", url_path.c_str()); + } + +}; + +/** + * @brief Reading and Writing of Http Replys + * @author Phil Schatzmann + * @copyright GPLv3 + */ +class HttpReplyHeader : public HttpHeader { + public: + // defines the values for the rely + void setValues(int statusCode, const char* msg="", const char* protocol=nullptr){ + LOGI("HttpReplyHeader::setValues"); + status_msg = msg; + status_code = statusCode; + if (protocol!=nullptr){ + this->protocol_str = protocol; + } + } + + // reads the final chunked reply headers + void readExt(Client &in) { + LOGI("HttpReplyHeader::readExt"); + char line[MAX_HTTP_HEADER_LINE_LENGTH]; + readLine(in, line, MAX_HTTP_HEADER_LINE_LENGTH); + while(strlen(line)!=0){ + put(line); + readLine(in, line, MAX_HTTP_HEADER_LINE_LENGTH); + } + } + + // HTTP-Version SP Status-Code SP Reason-Phrase CRLF + void write1stLine(Client &out){ + LOGI("HttpReplyHeader::write1stLine"); + char msg[200]; + Str msg_str(msg,200); + msg_str = this->protocol_str.c_str(); + msg_str += " "; + msg_str += this->status_code; + msg_str += " "; + msg_str += this->status_msg.c_str(); + LOGI("-> %s", msg); + out.print(msg); + crlf(out); + } + + + // HTTP-Version SP Status-Code SP Reason-Phrase CRLF + // we just update the pointers to point to the correct position in the + // http_status_line + void parse1stLine(const char *line){ + LOGD("HttpReplyHeader::parse1stLine: %s",line); + Str line_str(line); + int space1 = line_str.indexOf(' ',0); + int space2 = line_str.indexOf(' ',space1+1); + + // save http version + protocol_str.substring(line_str,0,space1); + + // find response status code after the first space + char status_c[6]; + Str status(status_c,6); + status.substring(line_str, space1+1, space2); + status_code = atoi(status_c); + + // get reason-phrase after last SP + status_msg.substring(line_str, space2+1, line_str.length()); + + } + + +}; + +} diff --git a/src/AudioHttp/HttpLineReader.h b/src/AudioHttp/HttpLineReader.h new file mode 100644 index 0000000000..315e5105ea --- /dev/null +++ b/src/AudioHttp/HttpLineReader.h @@ -0,0 +1,80 @@ +#pragma once + +#include "AudioTools/AudioLogger.h" + +namespace audio_tools { + +/** +* @brief We read a single line. A terminating 0 is added to the string to make it +* compliant for c string functions. +* @author Phil Schatzmann +* @copyright GPLv3 +*/ + +class HttpLineReader { + public: + HttpLineReader(){} + // reads up the the next CR LF - but never more then the indicated len. returns the number of characters read including crlf + virtual int readlnInternal(Stream &client, uint8_t* str, int len, bool incl_nl=true){ + int result = 0; + LOGD( "HttpLineReader %s","readlnInternal"); + // wait for first character + for (int w=0;w<20 && client.available()==0; w++){ + delay(100); + } + // if we do not have any data we stop + if (client.available()==0) { + LOGW( "HttpLineReader %s","readlnInternal->no Data"); + str[0]=0; + return 0; + } + + // process characters until we find a new line + bool is_buffer_owerflow = false; + int j=0; + while (true){ + int c = client.read(); + if (c==-1){ + break; + } + + if (j=1){ + if (str[j-1]=='\r'){ + // remove cr + str[j-1]=0; + break;; + } else { + // remove nl + str[j]=0; + break; + } + } + } + } + if (!is_buffer_owerflow){ + str[j] = c; + } + j++; + } + str[result-1]=0; + if (is_buffer_owerflow){ + LOGE("Line cut off: %s", str); + } + + return result; + } +}; + +} diff --git a/src/AudioHttp/HttpRequest.h b/src/AudioHttp/HttpRequest.h new file mode 100644 index 0000000000..9488cf6318 --- /dev/null +++ b/src/AudioHttp/HttpRequest.h @@ -0,0 +1,325 @@ +#pragma once +#include "AudioConfig.h" +#ifdef USE_URL_ARDUINO + +#include "AudioHttp/HttpTypes.h" +#include "AudioHttp/HttpHeader.h" +#include "AudioHttp/HttpChunkReader.h" +#include "AudioHttp/Url.h" +#include "AudioTools/AudioLogger.h" + +#ifndef URL_CLIENT_TIMEOUT +#define URL_CLIENT_TIMEOUT 60000 +#endif + +#ifndef URL_HANDSHAKE_TIMEOUT +#define URL_HANDSHAKE_TIMEOUT 120000 +#endif + +namespace audio_tools { + + +/** + * @brief Simple API to process get, put, post, del http requests + * I tried to use Arduino HttpClient, but I did not manage to extract the mime + * type from streaming get requests. + * + * The functionality is based on the Arduino Client class. + * @ingroup http + * @author Phil Schatzmann + * @copyright GPLv3 + */ + +class HttpRequest { + public: + friend class URLStream; + + HttpRequest() { + LOGI("HttpRequest"); + } + + HttpRequest(Client &client){ + LOGI("HttpRequest"); + setClient(client); + } + + void setClient(Client &client){ + this->client_ptr = &client; + this->client_ptr->setTimeout(clientTimeout); + } + + // the requests usually need a host. This needs to be set if we did not provide a URL + void setHost(const char* host){ + LOGI("setHost %s", host); + this->host_name = host; + } + + operator bool() { + return client_ptr != nullptr ? (bool)*client_ptr : false; + } + + virtual bool connected(){ + return client_ptr != nullptr ? (bool)*client_ptr && client_ptr->connected() : false; + } + + virtual int available() { + if (reply_header.isChunked()){ + return chunk_reader.available(); + } + return client_ptr != nullptr ? client_ptr->available() : 0; + } + + virtual void stop(){ + if (connected()){ + LOGI("stop"); + client_ptr->stop(); + } + } + + /// http post + virtual int post(Url &url, const char* mime, const char *data, int len=-1){ + LOGI("post %s", url.url()); + return process(POST, url, mime, data, len); + } + + /// http post + virtual int post(Url &url, const char* mime, Stream &data, int len=-1){ + LOGI("post %s", url.url()); + return process(POST, url, mime, data, len); + } + + /// http put + virtual int put(Url &url, const char* mime, const char *data, int len=-1){ + LOGI("put %s", url.url()); + return process(PUT, url, mime, data, len); + } + + /// http put + virtual int put(Url &url, const char* mime, Stream &data, int len=-1){ + LOGI("put %s", url.url()); + return process(PUT, url, mime, data, len); + } + + /// http del + virtual int del(Url &url,const char* mime=nullptr, const char *data=nullptr, int len=-1) { + LOGI("del %s", url.url()); + return process(DELETE, url, mime, data, len); + } + + /// http get + virtual int get(Url &url,const char* acceptMime=nullptr, const char *data=nullptr, int len=-1) { + LOGI("get %s", url.url()); + this->accept = acceptMime; + return process(GET, url, nullptr, data, len); + } + + /// http head + virtual int head(Url &url,const char* acceptMime=nullptr, const char *data=nullptr, int len=-1) { + LOGI("head %s", url.url()); + this->accept = acceptMime; + return process(HEAD, url, nullptr, data, len); + } + + // reads the reply data + virtual int read(uint8_t* str, int len){ + TRACED(); + if (reply_header.isChunked()){ + return chunk_reader.read(*client_ptr, str, len); + } else { + return client_ptr->read(str, len); + } + } + + size_t readBytesUntil(char terminator, char *buffer, size_t length){ + return client_ptr->readBytesUntil(terminator, buffer, length); + } + + // read the reply data up to the next new line. For Chunked data we provide + // the full chunk! + virtual int readln(uint8_t* str, int len, bool incl_nl=true){ + if (reply_header.isChunked()){ + return chunk_reader.readln(*client_ptr, str, len); + } else { + return chunk_reader.readlnInternal(*client_ptr, str, len, incl_nl); + } + } + + // provides the head information of the reply + virtual HttpReplyHeader &reply(){ + return reply_header; + } + + /// provides access to the request header + virtual HttpRequestHeader &header(){ + return request_header; + } + + /// Defines the agent + virtual void setAgent(const char* agent){ + this->agent = agent; + } + + virtual void setConnection(const char* connection){ + this->connection = connection; + } + + virtual void setAcceptsEncoding(const char* enc){ + this->accept_encoding = enc; + } + + virtual void setAcceptMime(const char* mime){ + this->accept = mime; + } + + size_t getReceivedContentLength() { + const char *len_str = reply().get(CONTENT_LENGTH); + int len = 0; + if (len_str != nullptr) { + len = atoi(len_str); + } else { + LOGW("no CONTENT_LENGTH found in reply"); + } + return len; + } + + /// returns true when the request has completed and ready for the data to be requested + bool isReady() { + return is_ready; + } + + /// Adds/Updates a request header + void addRequestHeader(const char* header, const char* value){ + request_header.put(header, value); + } + + Client &client() { + return *client_ptr; + } + + // process http request and reads the reply_header from the server + virtual int process(MethodID action, Url &url, const char* mime, const char *data, int lenData=-1){ + int len = lenData; + if (data!=nullptr && len<=0){ + len = strlen(data); + } + processBegin(action, url, mime, len); + // posting data parameter + if (len>0 && data!=nullptr){ + LOGI("Writing data: %d bytes", len); + client_ptr->write((const uint8_t*)data, len); + LOGD("%s",data); + } + return processEnd(); + } + + // process http request and reads the reply_header from the server + virtual int process(MethodID action, Url &url, const char* mime, Stream &stream, int len=-1){ + if (len==-1){ + len = stream.available(); + } + processBegin(action, url, mime, len); + processPost(stream); + return processEnd(); + } + + /// starts http request processing + virtual bool processBegin(MethodID action, Url &url, const char* mime, int lenData=-1){ + TRACED(); + int len = lenData; + is_ready = false; + if (client_ptr==nullptr){ + LOGE("The client has not been defined"); + return false; + } + if (!this->connected()){ + LOGI("process connecting to host %s port %d", url.host(), url.port()); + int is_connected = connect(url.host(), url.port(), clientTimeout); + if (is_connected!=1){ + LOGE("Connect failed"); + return false; + } + } else { + LOGI("process is already connected"); + } + +#if defined(ESP32) && defined(ARDUINO) + LOGI("Free heap: %u", ESP.getFreeHeap()); +#endif + + host_name = url.host(); + request_header.setValues(action, url.path()); + if (lenData>0){ + request_header.put(CONTENT_LENGTH, lenData); + } + request_header.put(HOST_C, host_name); + request_header.put(CONNECTION, connection); + request_header.put(USER_AGENT, agent); + request_header.put(ACCEPT_ENCODING, accept_encoding); + request_header.put(ACCEPT, accept); + request_header.put(CONTENT_TYPE, mime); + request_header.write(*client_ptr); + + return true; + } + + /// Posts the data of the indicated stream after calling processBegin + virtual void processPost(Stream &stream){ + uint8_t buffer[512]; + int total = 0; + while(stream.available()>0){ + int result_len = stream.readBytes(buffer, 512); + total += result_len; + client_ptr->write(buffer, result_len); + } + LOGI("Written data: %d bytes", total); + } + + /// Write data to the client: can be used to post data after calling processBegin + virtual size_t write(uint8_t* data, size_t len){ + return client_ptr->write(data, len); + } + + /// Ends the http request processing and returns the status code + virtual int processEnd(){ + LOGI("Request written ... waiting for reply") + //Commented out because this breaks the RP2040 W + //client_ptr->flush(); + reply_header.read(*client_ptr); + + // if we use chunked tranfer we need to read the first chunked length + if (reply_header.isChunked()){ + chunk_reader.open(*client_ptr); + }; + + // wait for data + is_ready = true; + return reply_header.statusCode(); + } + + protected: + Client *client_ptr; + Url url; + HttpRequestHeader request_header; + HttpReplyHeader reply_header; + HttpChunkReader chunk_reader = HttpChunkReader(reply_header); + const char *agent = nullptr; + const char *host_name=nullptr; + const char *connection = CON_CLOSE; + const char *accept = ACCEPT_ALL; + const char *accept_encoding = nullptr; + bool is_ready = false; + int32_t clientTimeout = URL_CLIENT_TIMEOUT; // 60000; + + // opens a connection to the indicated host + virtual int connect(const char *ip, uint16_t port, int32_t timeout) { + client_ptr->setTimeout(timeout); + int is_connected = this->client_ptr->connect(ip, port); + LOGI("connected %d timeout %d", is_connected, (int) timeout); + return is_connected; + } + +}; + +} + +#endif diff --git a/src/AudioHttp/HttpTypes.h b/src/AudioHttp/HttpTypes.h new file mode 100644 index 0000000000..f86624a170 --- /dev/null +++ b/src/AudioHttp/HttpTypes.h @@ -0,0 +1,3 @@ +#pragma once + +enum MethodID {UNDEFINED, GET,HEAD,POST,PUT,DELETE,TRACE,OPTIONS,CONNECT,PATCH}; diff --git a/src/AudioHttp/ICYStream.h b/src/AudioHttp/ICYStream.h new file mode 100644 index 0000000000..f633152f5b --- /dev/null +++ b/src/AudioHttp/ICYStream.h @@ -0,0 +1,162 @@ +#pragma once +#ifdef USE_URL_ARDUINO + +#include "AudioConfig.h" +#include "AudioHttp/URLStreamBuffered.h" +#include "AudioMetaData/MetaDataICY.h" + +namespace audio_tools { + +/** + * @brief Icecast/Shoutcast Audio Stream which splits the data into metadata and audio data. The Audio data is provided via the + * regular stream functions. The metadata is handled with the help of the MetaDataICY state machine and provided via a callback method. + * + * This is basically just a URLStream with the metadata turned on. + * @ingroup http + * @author Phil Schatzmann + * @copyright GPLv3 + */ + + +class ICYStream : public AbstractURLStream { + + public: + ICYStream(int readBufferSize=DEFAULT_BUFFER_SIZE){ + TRACEI(); + setReadBufferSize(readBufferSize); + } + + ICYStream(Client &clientPar, int readBufferSize=DEFAULT_BUFFER_SIZE){ + TRACEI(); + setReadBufferSize(readBufferSize); + setClient(clientPar); + } + + /// Default constructor + ICYStream(const char* network, const char *password, int readBufferSize=DEFAULT_BUFFER_SIZE) { + TRACEI(); + setReadBufferSize(readBufferSize); + setSSID(network); + setPassword(password); + } + + /// Defines the meta data callback function + virtual bool setMetadataCallback(void (*fn)(MetaDataType info, const char* str, int len)) override { + TRACED(); + callback = fn; + icy.setCallback(fn); + return true; + } + + // Icy http get request to the indicated url + virtual bool begin(const char* urlStr, const char* acceptMime=nullptr, MethodID action=GET, const char* reqMime="", const char*reqData="") override { + TRACED(); + // accept metadata + url.httpRequest().header().put("Icy-MetaData","1"); + bool result = url.begin(urlStr, acceptMime, action, reqMime, reqData); + + if (result) { + // setup icy + ICYUrlSetup icySetup; + int iceMetaint = icySetup.setup(url.httpRequest()); + // callbacks from http request + icySetup.executeCallback(callback); + icy.setIcyMetaInt(iceMetaint); + icy.begin(); + + if (!icy.hasMetaData()){ + LOGW("url does not provide metadata"); + } + } + return result; + } + + /// Ends the processing + virtual void end() override { + TRACED(); + url.end(); + icy.end(); + } + + /// provides the available method from the URLStream + virtual int available() override { + return url.available(); + } + + /// reads the audio bytes + virtual size_t readBytes(uint8_t* buffer, size_t len) override { + size_t result = 0; + if (icy.hasMetaData()){ + // get data + int read = url.readBytes(buffer, len); + // remove metadata from data + int pos = 0; + for (int j=0; j %zu", LOG_METHOD, len, result); + return result; + } + + // Read character and processes using the MetaDataICY state engine + virtual int read() override { + int ch = -1; + + // get next data byte + do { + ch = url.read(); + if (ch==-1){ + break; + } + + icy.processChar(ch); + } while (!icy.isData()); + return ch; + } + + operator bool() { + return url; + } + + /// provides access to the HttpRequest + virtual HttpRequest &httpRequest() override { + return url.httpRequest(); + } + + void setReadBufferSize(int readBufferSize) { + url.setReadBufferSize(readBufferSize); + } + + /// (Re-)defines the client + void setClient(Client &client) override { + url.setClient(client); + } + + /// Sets the ssid that will be used for logging in (when calling begin) + void setSSID(const char* ssid) override{ + url.setSSID(ssid); + } + + /// Sets the password that will be used for logging in (when calling begin) + void setPassword(const char* password) override { + url.setPassword(password); + } + + protected: + URLStream url; + MetaDataICY icy; // icy state machine + void (*callback)(MetaDataType info, const char* str, int len)=nullptr; + + +}; + +} // namespace +#endif \ No newline at end of file diff --git a/src/AudioHttp/ICYStreamBuffered.h b/src/AudioHttp/ICYStreamBuffered.h new file mode 100644 index 0000000000..a208f40b1e --- /dev/null +++ b/src/AudioHttp/ICYStreamBuffered.h @@ -0,0 +1,114 @@ +#pragma once +#if defined(ESP32) && defined(USE_URL_ARDUINO) +#include "AudioConfig.h" +#include "AudioHttp/ICYStream.h" + +namespace audio_tools { + +/** + * @brief ICYStream implementation for the ESP32 based on a FreeRTOS task + * This is a Icecast/Shoutcast Audio Stream which splits the data into metadata and audio data. The Audio data is provided via the + * regular stream functions. The metadata is handled with the help of the MetaDataICY state machine and provided via a callback method. + * + * This is basically just a URLStream with the metadata turned on. + * @ingroup http + * @author Phil Schatzmann + * @copyright GPLv3 + */ + +class ICYStreamBuffered : public AbstractURLStream { + public: + + ICYStreamBuffered(int readBufferSize=DEFAULT_BUFFER_SIZE){ + TRACEI(); + urlStream.setReadBufferSize(readBufferSize); + taskStream.setInput(urlStream); + } + + ICYStreamBuffered(Client &clientPar, int readBufferSize=DEFAULT_BUFFER_SIZE){ + TRACEI(); + urlStream.setReadBufferSize(readBufferSize); + setClient(clientPar); + taskStream.setInput(urlStream); + } + + ICYStreamBuffered(const char* network, const char *password, int readBufferSize=DEFAULT_BUFFER_SIZE) { + TRACEI(); + urlStream.setReadBufferSize(readBufferSize); + setSSID(network); + setPassword(password); + taskStream.setInput(urlStream); + } + + virtual bool setMetadataCallback(void (*fn)(MetaDataType info, const char* str, int len)) override { + TRACED(); + return urlStream.setMetadataCallback(fn); + } + + virtual bool begin(const char* urlStr, const char* acceptMime=nullptr, MethodID action=GET, const char* reqMime="", const char*reqData="") override { + TRACED(); + // start real stream + bool result = urlStream.begin(urlStr, acceptMime, action, reqMime, reqData); + // start reader task + taskStream.begin(); + return result; + } + + virtual void end() override{ + TRACED(); + taskStream.end(); + urlStream.end(); + } + + virtual int available() override { + return taskStream.available(); + } + + virtual size_t readBytes(uint8_t *buffer, size_t length) override { + size_t result = taskStream.readBytes(buffer, length); + LOGD("%s: %zu -> %zu", LOG_METHOD, length, result); + return result; + } + + virtual int read() override { + return taskStream.read(); + } + + virtual int peek() override { + return taskStream.peek(); + } + + /// Not implemented + virtual void flush() override { + } + + /// provides access to the HttpRequest + virtual HttpRequest &httpRequest() override { + return urlStream.httpRequest(); + } + + /// (Re-)defines the client + void setClient(Client &client) override { + urlStream.setClient(client); + } + + /// Sets the ssid that will be used for logging in (when calling begin) + void setSSID(const char* ssid) override{ + urlStream.setSSID(ssid); + } + + /// Sets the password that will be used for logging in (when calling begin) + void setPassword(const char* password) override { + urlStream.setPassword(password); + } + + + protected: + BufferedTaskStream taskStream; + ICYStream urlStream; + +}; + +} + +#endif // ESP32 \ No newline at end of file diff --git a/src/AudioTools/CoreAudio/AudioHttp/README.md b/src/AudioHttp/README.md similarity index 100% rename from src/AudioTools/CoreAudio/AudioHttp/README.md rename to src/AudioHttp/README.md diff --git a/src/AudioHttp/URLStream.h b/src/AudioHttp/URLStream.h new file mode 100644 index 0000000000..2ab0c3b366 --- /dev/null +++ b/src/AudioHttp/URLStream.h @@ -0,0 +1,360 @@ +#pragma once + +#include "AudioConfig.h" +#ifdef USE_URL_ARDUINO + +#if defined(ESP32) +# include +# include +# include +#endif + +#include "AudioHttp/HttpRequest.h" +#include "AudioHttp/AbstractURLStream.h" + +#ifndef URL_CLIENT_TIMEOUT +#define URL_CLIENT_TIMEOUT 60000 +#endif + +#ifndef URL_HANDSHAKE_TIMEOUT +#define URL_HANDSHAKE_TIMEOUT 120000 +#endif + + +namespace audio_tools { + +/** + * @brief Represents the content of a URL as Stream. We use the WiFi.h API + * @author Phil Schatzmann + * @ingroup http + * @copyright GPLv3 + * + */ +class URLStream : public AbstractURLStream { + public: + + URLStream(int readBufferSize=DEFAULT_BUFFER_SIZE){ + TRACEI(); + setReadBufferSize(readBufferSize); + } + + URLStream(Client &clientPar, int readBufferSize=DEFAULT_BUFFER_SIZE){ + TRACEI(); + setReadBufferSize(readBufferSize); + setClient(clientPar); + } + + URLStream(const char* network, const char *password, int readBufferSize=DEFAULT_BUFFER_SIZE) { + TRACEI(); + setReadBufferSize(readBufferSize); + setSSID(network); + setPassword(password); + } + + ~URLStream(){ + TRACEI(); + end(); +#ifdef USE_WIFI_CLIENT_SECURE + if (clientSecure!=nullptr){ + delete clientSecure; + clientSecure = nullptr; + } +#endif + if (clientInsecure!=nullptr){ + delete clientInsecure; + clientInsecure = nullptr; + } + } + + /// (Re-)defines the client + void setClient(Client &clientPar) override { + client = &clientPar; + } + + /// Sets the ssid that will be used for logging in (when calling begin) + void setSSID(const char* ssid) override { + this->network = ssid; + } + + /// Sets the password that will be used for logging in (when calling begin) + void setPassword(const char* password) override{ + this->password = password; + } + + void setReadBufferSize(int readBufferSize) { + read_buffer_size = readBufferSize; + } + + virtual bool begin(const char* urlStr, const char* acceptMime=nullptr, MethodID action=GET, const char* reqMime="", const char*reqData="") override{ + LOGI( "%s: %s",LOG_METHOD, urlStr); + + url.setUrl(urlStr); + int result = -1; + + // optional: login if necessary + login(); + + // check if we are connected + if (WiFi.status() != WL_CONNECTED){ + LOGE("Not connected"); + return false; + } + + //request.reply().setAutoCreateLines(false); + if (acceptMime!=nullptr){ + request.setAcceptMime(acceptMime); + } + result = process(action, url, reqMime, reqData); + if (result>0){ + size = request.getReceivedContentLength(); + LOGI("size: %d", (int)size); + if (size>=0){ + waitForData(); + } + } + active = result == 200; + return active; + } + + virtual void end() override { + active = false; + request.stop(); + } + + virtual int available() override { + if (!active || !request) return 0; + int result = request.available(); + LOGD("available: %d", result); + return result; + } + + virtual size_t readBytes(uint8_t *buffer, size_t length) override { + if (!active || !request) return 0; + + int read = request.read((uint8_t*)&buffer[0], length); + if (read<0){ + read = 0; + } + total_read+=read; + LOGD("readBytes %d -> %d", (int)length, read); + return read; + } + + virtual int read() override { + if (!active) return -1; + // lazy allocation since this is rarely used + read_buffer.resize(read_buffer_size); + + fillBuffer(); + total_read++; + return isEOS() ? -1 :read_buffer[read_pos++]; + } + + virtual int peek() override { + if (!active) return -1; + // lazy allocation since this is rarely used + read_buffer.resize(read_buffer_size); + + fillBuffer(); + return isEOS() ? -1 : read_buffer[read_pos]; + } + + virtual void flush() override { + } + + virtual size_t write(uint8_t) override { + return not_supported(0); + } + + virtual size_t write(const uint8_t*,size_t) override { + return not_supported(0); + } + + /// provides access to the HttpRequest + virtual HttpRequest &httpRequest() override { + return request; + } + + operator bool() { + return active && request.isReady(); + } + + /// Defines the client timeout + virtual void setTimeout(int ms){ + clientTimeout = ms; + } + + /// if set to true, it activates the power save mode which comes at the cost of performance! - By default this is deactivated. ESP32 Only! + void setPowerSave(bool ps){ + is_power_save = ps; + } + + /// If set to true, new undefined reply parameters will be stored + void setAutoCreateLines(bool flag){ + httpRequest().reply().setAutoCreateLines(flag); + } + + /// Sets if the connection should be close automatically + void setConnectionClose(bool close){ + httpRequest().setConnection(close ? CON_CLOSE : CON_KEEP_ALIVE); + } + + /// Releases the memory from the request and reply + void clear() { + httpRequest().reply().clear(false); + httpRequest().header().clear(false); + read_buffer.resize(0); + } + + /// Adds/Updates a request header + void addRequestHeader(const char* header, const char* value){ + request.header().put(header, value); + } + + protected: + HttpRequest request; + Url url; + long size; + long total_read; + // buffered single byte read + Vector read_buffer{0}; + uint16_t read_buffer_size=DEFAULT_BUFFER_SIZE; + uint16_t read_pos; + uint16_t read_size; + bool active = false; + // optional + const char* network=nullptr; + const char* password=nullptr; + Client *client=nullptr; + WiFiClient *clientInsecure=nullptr; +#ifdef USE_WIFI_CLIENT_SECURE + WiFiClientSecure *clientSecure=nullptr; +#endif + int clientTimeout = URL_CLIENT_TIMEOUT; // 60000; + unsigned long handshakeTimeout = URL_HANDSHAKE_TIMEOUT; //120000 + bool is_power_save = false; + + void setTimeouts() { + // set regular timeout + getClient(url.isSecure()).setTimeout(clientTimeout/1000); // this is in seconds + } + + /// Process the Http request and handle redirects + int process(MethodID action, Url &url, const char* reqMime, const char *reqData, int len=-1) { + request.setClient(getClient(url.isSecure())); + // keep icy across redirect requests ? + const char* icy = request.header().get("Icy-MetaData"); + + // set timeout + setTimeouts(); +#ifdef ESP32 + // There is a bug in IDF 4! + if (clientSecure!=nullptr){ + clientSecure->setHandshakeTimeout(handshakeTimeout); + } + + // Performance optimization for ESP32 + if (!is_power_save){ + esp_wifi_set_ps(WIFI_PS_NONE); + } +#endif + + int status_code = request.process(action, url, reqMime, reqData, len); + // redirect + while (request.reply().isRedirectStatus()){ + const char *redirect_url = request.reply().get(LOCATION); + if (redirect_url!=nullptr) { + LOGW("Redirected to: %s", redirect_url); + url.setUrl(redirect_url); + Client* p_client = &getClient(url.isSecure()); + p_client->stop(); + request.setClient(*p_client); + if (icy){ + request.header().put("Icy-MetaData", icy); + } + status_code = request.process(action, url, reqMime, reqData, len); + } else { + LOGE("Location is null"); + break; + } + } + return status_code; + } + + /// Determines the client + Client &getClient(bool isSecure){ + if (client!=nullptr) return *client; +#ifdef USE_WIFI_CLIENT_SECURE + if (isSecure){ + if (clientSecure==nullptr){ + clientSecure = new WiFiClientSecure(); + clientSecure->setInsecure(); + } + LOGI("WiFiClientSecure"); + return *clientSecure; + } +#endif +#ifdef USE_WIFI + if (clientInsecure==nullptr){ + clientInsecure = new WiFiClient(); + LOGI("WiFiClient"); + } + return *clientInsecure; +#else + LOGE("Client not set"); + stop(); + return *client; // to avoid compiler warning +#endif + } + + inline void fillBuffer() { + if (isEOS()){ + // if we consumed all bytes we refill the buffer + read_size = readBytes(&read_buffer[0],read_buffer_size); + read_pos = 0; + } + } + + inline bool isEOS() { + return read_pos>=read_size; + } + + void login(){ +#ifdef USE_WIFI + if (network!=nullptr && password != nullptr && WiFi.status() != WL_CONNECTED){ + TRACEI(); + WiFi.begin(network, password); + while (WiFi.status() != WL_CONNECTED){ + Serial.print("."); + delay(500); + } + Serial.println(); + delay(500); + } +#else + LOGE("login not supported"); +#endif + } + + /// waits for some data - returns false if the request has failed + virtual bool waitForData() { + TRACEI(); + if(request.available()==0 ){ + LOGI("Request written ... waiting for reply") + while(request.available()==0){ + // stop waiting if we got an error + if (request.reply().statusCode()>=300){ + LOGE("Error code recieved ... stop waiting for reply"); + break; + } + delay(500); + } + } + LOGI("available: %d", request.available()); + return request.available()>0; + } +}; + +} + +#endif diff --git a/src/AudioHttp/URLStreamBuffered.h b/src/AudioHttp/URLStreamBuffered.h new file mode 100644 index 0000000000..3b09333c4e --- /dev/null +++ b/src/AudioHttp/URLStreamBuffered.h @@ -0,0 +1,226 @@ +#pragma once +#if defined(ESP32) && defined(USE_URL_ARDUINO) +#include "AudioConfig.h" +#include "AudioTools/SynchronizedBuffers.h" +#include "AudioTools/AudioStreams.h" +#include "AudioHttp/URLStream.h" + +namespace audio_tools { + +/** + * @brief A FreeRTOS task is filling the buffer from the indicated stream. Only to be used on the ESP32 + * + * @author Phil Schatzmann + * @copyright GPLv3 + */ +class BufferedTaskStream : public AudioStream { + public: + BufferedTaskStream() { + TRACEI(); + }; + + BufferedTaskStream(AudioStream &input){ + TRACEI(); + setInput(input); + } + + ~BufferedTaskStream(){ + TRACEI(); + stop(); + } + + virtual void begin(bool wait=true) { + TRACED(); + active=true; + ready = false; + xTaskCreatePinnedToCore(task, "BufferedTaskStream", STACK_SIZE, this, URL_STREAM_PRIORITY, &xHandle, URL_STREAM_CORE); + if (!wait) ready=true; + } + + virtual void end() { + TRACED(); + if (xHandle!=NULL) vTaskDelete( xHandle ); + active = false; + ready = false; + } + + virtual void setInput(AudioStream &input) { + TRACED(); + p_stream = &input; + } + + /// writes a byte to the buffer + virtual size_t write(uint8_t c) override { + return 0; + } + + /// Use this method: write an array + virtual size_t write(const uint8_t* data, size_t len) override { + return 0; + } + + /// empties the buffer + virtual void flush() override { + } + + /// reads a byte - to be avoided + virtual int read() override { + //if (!ready) return -1; + int result = -1; + result = buffers.read(); + return result; + } + + /// peeks a byte - to be avoided + virtual int peek() override { + //if (!ready) return -1; + int result = -1; + result = buffers.peek(); + return result; + }; + + /// Use this method !! + virtual size_t readBytes( uint8_t *data, size_t length) override { + //if (!ready) return 0; + size_t result = 0; + result = buffers.readArray(data, length); + LOGD("%s: %zu -> %zu", LOG_METHOD, length, result); + return result; + } + + /// Returns the available bytes in the buffer: to be avoided + virtual int available() override { + //if (!ready) return 0; + int result = 0; + result = buffers.available(); + return result; + } + + protected: + AudioStream *p_stream=nullptr; + bool active = false; + TaskHandle_t xHandle = NULL; + SynchronizedNBuffer buffers {DEFAULT_BUFFER_SIZE, URL_STREAM_BUFFER_COUNT}; + bool ready = false; + + static void task(void * pvParameters){ + TRACED(); + static uint8_t buffer[512]; + BufferedTaskStream* self = (BufferedTaskStream*) pvParameters; + while(true){ + size_t available_to_write = self->buffers.availableForWrite(); + if (*(self->p_stream) && available_to_write>0){ + size_t to_read = min(available_to_write, (size_t) 512); + size_t avail_read = self->p_stream->readBytes((uint8_t*)buffer, to_read); + size_t written = self->buffers.writeArray(buffer, avail_read); + + if (written!=avail_read){ + LOGE("DATA Lost! %zu reqested, %zu written!",avail_read, written); + } + + } else { + delay(100); + } + // buffer is full we start to provide data + if (available_to_write==0){ + self->ready = true; + } + } + } +}; + +/** + * @brief URLStream implementation for the ESP32 based on a separate FreeRTOS task + * @ingroup http + * @author Phil Schatzmann + * @copyright GPLv3 + */ + +class URLStreamBuffered : public AbstractURLStream { + public: + URLStreamBuffered(int readBufferSize=DEFAULT_BUFFER_SIZE){ + TRACED(); + urlStream.setReadBufferSize(readBufferSize); + taskStream.setInput(urlStream); + } + + URLStreamBuffered(Client &clientPar, int readBufferSize=DEFAULT_BUFFER_SIZE){ + TRACED(); + urlStream.setReadBufferSize(readBufferSize); + setClient(clientPar); + taskStream.setInput(urlStream); + } + + URLStreamBuffered(const char* network, const char *password, int readBufferSize=DEFAULT_BUFFER_SIZE) { + TRACED(); + urlStream.setReadBufferSize(readBufferSize); + setSSID(network); + setPassword(password); + taskStream.setInput(urlStream); + } + + bool begin(const char* urlStr, const char* acceptMime=nullptr, MethodID action=GET, const char* reqMime="", const char*reqData="") { + TRACED(); + // start real stream + bool result = urlStream.begin(urlStr, acceptMime, action,reqMime, reqData ); + // start buffer task + taskStream.begin(); + return result; + } + + virtual int available() { + return taskStream.available(); + } + + virtual size_t readBytes(uint8_t *buffer, size_t length){ + size_t result = taskStream.readBytes(buffer, length); + LOGD("%s: %zu -> %zu", LOG_METHOD, length, result); + return result; + } + + virtual int read() { + return taskStream.read(); + } + + virtual int peek() { + return taskStream.peek(); + } + + virtual void flush(){ + } + + void end(){ + TRACED(); + taskStream.end(); + urlStream.end(); + } + + /// provides access to the HttpRequest + HttpRequest &httpRequest(){ + return urlStream.httpRequest(); + } + + /// (Re-)defines the client + void setClient(Client &client) override { + urlStream.setClient(client); + } + + /// Sets the ssid that will be used for logging in (when calling begin) + void setSSID(const char* ssid) override{ + urlStream.setSSID(ssid); + } + + /// Sets the password that will be used for logging in (when calling begin) + void setPassword(const char* password) override { + urlStream.setPassword(password); + } + + protected: + BufferedTaskStream taskStream; + URLStream urlStream; +}; + + +} + +#endif diff --git a/src/AudioHttp/Url.h b/src/AudioHttp/Url.h new file mode 100644 index 0000000000..d6879e57fb --- /dev/null +++ b/src/AudioHttp/Url.h @@ -0,0 +1,121 @@ +#pragma once + +#include "AudioBasic/StrExt.h" +#include "AudioTools/AudioLogger.h" + +namespace audio_tools { + + +/** + * @brief URL parser which breaks a full url string up into its individual parts + * + * http://pschatzmann.ch:80/path1/path2 + * -> protocol: http + * -> host: pschatzmann.ch + * -> port: 80 + * -> url: http://pschatzmann.ch:80/path1/path2 + * -> root: http://pschatzmann.ch:80 + * @ingroup http + * @author Phil Schatzmann + * @copyright GPLv3 + + */ +class Url { + public: + // empty url + Url() { + LOGI("Url"); + } + + ~Url() { + LOGI("~Url"); + pathStr.clear(); + hostStr.clear(); + protocolStr.clear(); + urlRootStr.clear(); + urlStr.clear(); + } + + // setup url with string + Url(const char *url){ + LOGI("Url %s",url); + setUrl(url); + } + + // copy constructor + Url(Url &url){ + LOGI("Url %s",url.url()); + setUrl(url.url()); + } + + const char* url() {return urlStr.c_str();} + const char* path() { return pathStr.c_str(); } + const char* host() { return hostStr.c_str();} + const char* protocol() {return protocolStr.c_str();} + const char* urlRoot() {return urlRootStr.c_str();} // prefix w/o path -> https://host:port + int port() {return portInt;} + bool isSecure() { return portInt==443; } + + void setUrl(const char* url){ + LOGD("setUrl %s",url); + this->urlStr = url; + parse(); + } + + protected: + StrExt pathStr = StrExt(40); + StrExt hostStr = StrExt(20); + StrExt protocolStr = StrExt(6); + StrExt urlRootStr = StrExt(40); + StrExt urlStr = StrExt(40); + int portInt; + + void parse() { + LOGI("Url::parse"); + + int protocolEnd = urlStr.indexOf("://"); + if (protocolEnd==-1){ + return; + } + protocolStr.substring(urlStr, 0,protocolEnd); + int portStart = urlStr.indexOf(":",protocolEnd+3); + int hostEnd = portStart!=-1 ? portStart : urlStr.indexOf("/",protocolEnd+4); + // we have not path -> so then host end is the end of string + if (hostEnd==-1){ + hostEnd = urlStr.length(); + } + hostStr.substring(urlStr, protocolEnd+3,hostEnd); + if (portStart>0){ + portInt = atoi(urlStr.c_str()+portStart+1); + } else { + if (protocolStr.startsWith("https")) + portInt = 443; + else if (protocolStr.startsWith("http")) + portInt = 80; + else if (protocolStr.startsWith("ftp")) + portInt = 21; + else + portInt = -1; // undefined + } + int pathStart = urlStr.indexOf("/",protocolEnd+4); + if (pathStart==0){ + // we have no path + pathStr = "/"; + urlRootStr = urlStr; + } else { + pathStr.substring(urlStr, pathStart, urlStr.length()); + pathStr.trim(); + urlRootStr.substring(urlStr, 0, pathStart); + } + LOGI("url->%s",url()); + LOGI("host->%s",host()); + LOGI("protocol->%s",protocol()); + LOGI("path->%s",path()); + LOGI("port->%d",port()); + + } + +}; + +} + diff --git a/src/AudioTools/CoreAudio/AudioI2S/I2SConfigESP32.h b/src/AudioI2S/I2SConfig.h similarity index 58% rename from src/AudioTools/CoreAudio/AudioI2S/I2SConfigESP32.h rename to src/AudioI2S/I2SConfig.h index 94fe9b54b6..443a264001 100644 --- a/src/AudioTools/CoreAudio/AudioI2S/I2SConfigESP32.h +++ b/src/AudioI2S/I2SConfig.h @@ -1,39 +1,63 @@ #pragma once -#include "AudioToolsConfig.h" -#include "AudioTools/CoreAudio/AudioTypes.h" +#include "AudioConfig.h" +#ifdef USE_I2S #ifndef PIN_I2S_MCK # define PIN_I2S_MCK -1 #endif -#if defined(ESP32) && ESP_IDF_VERSION < ESP_IDF_VERSION_VAL(5, 0 , 0) -# include "driver/i2s.h" // for I2S_CHANNEL_FMT_RIGHT_LEFT +#ifdef ESP32 +# include "driver/i2s.h" #endif namespace audio_tools { +/** + * @brief I2S Formats + */ +enum I2SFormat { + I2S_STD_FORMAT, + I2S_LSB_FORMAT, + I2S_MSB_FORMAT, + I2S_PHILIPS_FORMAT, + I2S_RIGHT_JUSTIFIED_FORMAT, + I2S_LEFT_JUSTIFIED_FORMAT, + I2S_PCM, +}; /** - * @brief Configuration for ESP32 legacy i2s - * @ingroup platform + * @brief I2S Signal Types: Digital, Analog, PDM + */ +enum I2SSignalType { + Digital, + Analog, + PDM, + TDM +}; + +INLINE_VAR const char* i2s_formats[] = {"I2S_STD_FORMAT","I2S_LSB_FORMAT","I2S_MSB_FORMAT","I2S_PHILIPS_FORMAT","I2S_RIGHT_JUSTIFIED_FORMAT","I2S_LEFT_JUSTIFIED_FORMAT","I2S_PCM"}; + + +/** + * @brief configuration for all common i2s settings * @author Phil Schatzmann * @copyright GPLv3 */ -class I2SConfigESP32 : public AudioInfo { +class I2SConfig : public AudioInfo { public: - I2SConfigESP32() { + I2SConfig() { channels = DEFAULT_CHANNELS; sample_rate = DEFAULT_SAMPLE_RATE; bits_per_sample = DEFAULT_BITS_PER_SAMPLE; } /// Default Copy Constructor - I2SConfigESP32(const I2SConfigESP32 &cfg) = default; + I2SConfig(const I2SConfig &cfg) = default; /// Constructor - I2SConfigESP32(RxTxMode mode) { + I2SConfig(RxTxMode mode) { channels = DEFAULT_CHANNELS; sample_rate = DEFAULT_SAMPLE_RATE; bits_per_sample = DEFAULT_BITS_PER_SAMPLE; @@ -41,14 +65,20 @@ class I2SConfigESP32 : public AudioInfo { switch(mode){ case RX_MODE: pin_data = PIN_I2S_DATA_IN; - auto_clear = false; + #ifdef ESP32 + auto_clear = false; + #endif break; case TX_MODE: pin_data = PIN_I2S_DATA_OUT; - auto_clear = I2S_AUTO_CLEAR; + #ifdef ESP32 + auto_clear = I2S_AUTO_CLEAR; + #endif break; default: - auto_clear = I2S_AUTO_CLEAR; + #ifdef ESP32 + auto_clear = I2S_AUTO_CLEAR; + #endif pin_data = PIN_I2S_DATA_OUT; pin_data_rx = PIN_I2S_DATA_IN; break; @@ -56,47 +86,57 @@ class I2SConfigESP32 : public AudioInfo { } /// public settings - RxTxMode rx_tx_mode = RXTX_MODE; - I2SFormat i2s_format = I2S_STD_FORMAT; - I2SSignalType signal_type = Digital; // e.g. the ESP32 supports analog input or output or PDM picrophones + RxTxMode rx_tx_mode = TX_MODE; bool is_master = true; int port_no = 0; // processor dependent port int pin_ws = PIN_I2S_WS; int pin_bck = PIN_I2S_BCK; - int pin_data = -1; // rx or tx pin dependent on mode: tx pin for RXTX_MODE - int pin_data_rx = -1; // rx pin for RXTX_MODE + int pin_data; // rx or tx pin dependent on mode: tx pin for RXTX_MODE + int pin_data_rx; // rx pin for RXTX_MODE int pin_mck = PIN_I2S_MCK; + I2SFormat i2s_format = I2S_STD_FORMAT; + +#if defined(STM32) int buffer_count = I2S_BUFFER_COUNT; int buffer_size = I2S_BUFFER_SIZE; +#elif defined(ESP32) + int channel_format = I2S_CHANNEL_FMT_RIGHT_LEFT; + int buffer_count = I2S_BUFFER_COUNT; + int buffer_size = I2S_BUFFER_SIZE; + I2SSignalType signal_type = Digital; // e.g. the ESP32 supports analog input or output or PDM picrophones bool auto_clear = I2S_AUTO_CLEAR; bool use_apll = I2S_USE_APLL; uint32_t fixed_mclk = 0; -#if ESP_IDF_VERSION < ESP_IDF_VERSION_VAL(5, 0 , 0) - int channel_format = I2S_CHANNEL_FMT_RIGHT_LEFT; + +#elif defined(ARDUINO_ARCH_RP2040) + int buffer_count = I2S_BUFFER_COUNT; + int buffer_size = I2S_BUFFER_SIZE; #endif - void logInfo(const char* source="") { - AudioInfo::logInfo(source); + void logInfo() { LOGI("rx/tx mode: %s", RxTxModeNames[rx_tx_mode]); LOGI("port_no: %d", port_no); LOGI("is_master: %s", is_master ? "Master":"Slave"); LOGI("sample rate: %d", sample_rate); LOGI("bits per sample: %d", bits_per_sample); LOGI("number of channels: %d", channels); - LOGI("signal_type: %s", i2s_signal_types[signal_type]); - if (signal_type==Digital){ - LOGI("i2s_format: %s", i2s_formats[i2s_format]); - } + LOGI("i2s_format: %s", i2s_formats[i2s_format]); +#ifdef ESP32 LOGI("auto_clear: %s",auto_clear? "true" : "false"); if (use_apll) { LOGI("use_apll: %s", use_apll ? "true" : "false"); } if (fixed_mclk){ - LOGI("fixed_mclk: %d", (int) fixed_mclk); + LOGI("fixed_mclk: %d", (int) fixed_mclk); } LOGI("buffer_count:%d",buffer_count); LOGI("buffer_size:%d",buffer_size); +#endif +#ifdef ARDUINO_ARCH_RP2040 + LOGI("buffer_count:%d",buffer_count); + LOGI("buffer_size:%d",buffer_size); +#endif if (pin_mck!=-1) LOGI("pin_mck: %d", pin_mck); if (pin_bck!=-1) @@ -112,7 +152,6 @@ class I2SConfigESP32 : public AudioInfo { }; -using I2SConfig = I2SConfigESP32; - } +#endif \ No newline at end of file diff --git a/src/AudioI2S/I2SESP32-New.h b/src/AudioI2S/I2SESP32-New.h new file mode 100644 index 0000000000..956921c637 --- /dev/null +++ b/src/AudioI2S/I2SESP32-New.h @@ -0,0 +1,379 @@ +#pragma once + +#if defined(ESP32) && defined(USE_I2S_NEW) + +#include "AudioConfig.h" +#include "AudioI2S/I2SConfig.h" +#include "driver/i2s_pdm.h" +#include "driver/i2s_std.h" +#include "driver/i2s_tdm.h" +#include "esp_system.h" + +namespace audio_tools { + +/** + * @brief Basic I2S API for the ESP32 (using the new API). + * https://docs.espressif.com/projects/esp-idf/en/v5.0.1/esp32/api-reference/peripherals/i2s.html#i2s-communication-mode + * @ingroup platform + * @author Phil Schatzmann + * @copyright GPLv3 + */ +class I2SDriverESP32New { + +public: + /// Provides the default configuration + I2SConfig defaultConfig(RxTxMode mode) { + I2SConfig c(mode); + return c; + } + + /// starts the DAC with the default config + bool begin(RxTxMode mode) { return begin(defaultConfig(mode)); } + + /// starts the DAC with the current config - if not started yet. If I2S has + /// been started there is no action and we return true + bool begin() { return !is_started ? begin(cfg) : true; } + + /// starts the DAC + bool begin(I2SConfig cfg) { + TRACED(); + this->cfg = cfg; + switch (cfg.rx_tx_mode) { + case TX_MODE: + return begin(cfg, cfg.pin_data, I2S_GPIO_UNUSED); + case RX_MODE: + // usually we expet cfg.pin_data but if the used assinged rx we might + // consider this one + return begin(cfg, I2S_GPIO_UNUSED, + cfg.pin_data != I2S_GPIO_UNUSED ? cfg.pin_data + : cfg.pin_data_rx); + default: + return begin(cfg, cfg.pin_data, cfg.pin_data_rx); + } + LOGE("Did not expect go get here"); + } + + /// we assume the data is already available in the buffer + int available() { return I2S_BUFFER_COUNT * I2S_BUFFER_SIZE; } + + /// We limit the write size to the buffer size + int availableForWrite() { return I2S_BUFFER_COUNT * I2S_BUFFER_SIZE; } + + /// stops the I2C and unistalls the driver + void end() { + TRACED(); + if (cfg.rx_tx_mode == RXTX_MODE || cfg.rx_tx_mode == TX_MODE) { + i2s_channel_disable(rx_chan); + i2s_del_channel(rx_chan); + } + if (cfg.rx_tx_mode == RXTX_MODE || cfg.rx_tx_mode == TX_MODE) { + i2s_channel_disable(tx_chan); + i2s_del_channel(tx_chan); + } + + is_started = false; + } + + /// provides the actual configuration + I2SConfig config() { return cfg; } + + /// writes the data to the I2S interface + size_t writeBytes(const void *src, size_t size_bytes) { + TRACED(); + size_t result; + if (i2s_channel_write(tx_chan, src, size_bytes, &result, portMAX_DELAY) != + ESP_OK) { + TRACEE(); + } + return result; + } + + size_t readBytes(void *dest, size_t size_bytes) { + size_t result = 0; + if (i2s_channel_read(rx_chan, dest, size_bytes, &result, portMAX_DELAY) != + ESP_OK) { + TRACEE(); + } + return result; + } + +protected: + I2SConfig cfg = defaultConfig(RX_MODE); + i2s_std_config_t i2s_config; + i2s_chan_handle_t tx_chan; // I2S tx channel handler + i2s_chan_handle_t rx_chan; // I2S rx channel handler + bool is_started = false; + + struct DriverCommon { + virtual i2s_chan_config_t getChannelConfig(I2SConfig &cfg) = 0; + virtual bool startChannels(I2SConfig &cfg, i2s_chan_handle_t &tx_chan, + i2s_chan_handle_t &rx_chan, int txPin, int rxPin) = 0; + }; + + struct DriverI2S : public DriverCommon { + i2s_std_slot_config_t getSlotConfig(I2SConfig &cfg) { + TRACED(); + switch (cfg.i2s_format) { + case I2S_RIGHT_JUSTIFIED_FORMAT: + case I2S_LSB_FORMAT: + case I2S_PHILIPS_FORMAT: + case I2S_STD_FORMAT: + return I2S_STD_PHILIPS_SLOT_DEFAULT_CONFIG( + (i2s_data_bit_width_t)cfg.bits_per_sample, + (i2s_slot_mode_t)cfg.channels); + case I2S_LEFT_JUSTIFIED_FORMAT: + case I2S_MSB_FORMAT: + return I2S_STD_MSB_SLOT_DEFAULT_CONFIG( + (i2s_data_bit_width_t)cfg.bits_per_sample, + (i2s_slot_mode_t)cfg.channels); + case I2S_PCM: + return I2S_STD_PCM_SLOT_DEFAULT_CONFIG( + (i2s_data_bit_width_t)cfg.bits_per_sample, + (i2s_slot_mode_t)cfg.channels); + } + // use default config + TRACEE(); + return I2S_STD_PHILIPS_SLOT_DEFAULT_CONFIG( + (i2s_data_bit_width_t)cfg.bits_per_sample, + (i2s_slot_mode_t)cfg.channels); + } + + i2s_chan_config_t getChannelConfig(I2SConfig &cfg) { + TRACED(); + return I2S_CHANNEL_DEFAULT_CONFIG((i2s_port_t)cfg.port_no, + cfg.is_master ? I2S_ROLE_MASTER + : I2S_ROLE_SLAVE); + } + + i2s_std_clk_config_t getClockConfig(I2SConfig &cfg) { + TRACED(); + return I2S_STD_CLK_DEFAULT_CONFIG((uint32_t)cfg.sample_rate); + } + + bool startChannels(I2SConfig &cfg, i2s_chan_handle_t &tx_chan, + i2s_chan_handle_t &rx_chan, int txPin, int rxPin) { + TRACED(); + i2s_std_config_t std_cfg = { + .clk_cfg = getClockConfig(cfg), + .slot_cfg = getSlotConfig(cfg), + .gpio_cfg = + { + .mclk = (gpio_num_t)cfg.pin_mck, + .bclk = (gpio_num_t)cfg.pin_bck, + .ws = (gpio_num_t)cfg.pin_ws, + .dout = (gpio_num_t)txPin, + .din = (gpio_num_t)rxPin, + .invert_flags = + { + .mclk_inv = false, + .bclk_inv = false, + .ws_inv = false, + }, + }, + }; + + if (cfg.rx_tx_mode == RXTX_MODE || cfg.rx_tx_mode == TX_MODE) { + if (i2s_channel_init_std_mode(tx_chan, &std_cfg) != ESP_OK) { + LOGE("i2s_channel_init_std_mode %s", "tx"); + return false; + } + if (i2s_channel_enable(tx_chan)!=ESP_OK){ + LOGE("i2s_channel_enable %s", "tx"); + return false; + } + } + + if (cfg.rx_tx_mode == RXTX_MODE || cfg.rx_tx_mode == RX_MODE) { + if (i2s_channel_init_std_mode(rx_chan, &std_cfg) != ESP_OK) { + LOGE("i2s_channel_init_std_mode %s", "rx"); + return false; + } + if (i2s_channel_enable(rx_chan)!=ESP_OK){ + LOGE("i2s_channel_enable %s", "tx"); + return false; + } + } + + LOGD("%s - %s", __func__, "started"); + return true; + } + } i2s; + + struct DriverPDM : public DriverCommon { + i2s_pdm_rx_slot_config_t getRxSlotConfig(I2SConfig &cfg) { + return I2S_PDM_RX_SLOT_DEFAULT_CONFIG( + (i2s_data_bit_width_t)cfg.bits_per_sample, + (i2s_slot_mode_t)cfg.channels); + } + + i2s_pdm_tx_slot_config_t getTxSlotConfig(I2SConfig &cfg) { + return I2S_PDM_TX_SLOT_DEFAULT_CONFIG( + (i2s_data_bit_width_t)cfg.bits_per_sample, + (i2s_slot_mode_t)cfg.channels); + } + + i2s_chan_config_t getChannelConfig(I2SConfig &cfg) { + return I2S_CHANNEL_DEFAULT_CONFIG((i2s_port_t)cfg.port_no, + cfg.is_master ? I2S_ROLE_MASTER + : I2S_ROLE_SLAVE); + } + + i2s_pdm_tx_clk_config_t getTxClockConfig(I2SConfig &cfg) { + return I2S_PDM_TX_CLK_DEFAULT_CONFIG((uint32_t)cfg.sample_rate); + } + + i2s_pdm_rx_clk_config_t getRxClockConfig(I2SConfig &cfg) { + return I2S_PDM_RX_CLK_DEFAULT_CONFIG((uint32_t)cfg.sample_rate); + } + + bool startChannels(I2SConfig &cfg, i2s_chan_handle_t &tx_chan, + i2s_chan_handle_t &rx_chan, int txPin, int rxPin) { + + if (cfg.rx_tx_mode == TX_MODE) { + i2s_pdm_tx_config_t pdm_tx_cfg = { + .clk_cfg = getTxClockConfig(cfg), + .slot_cfg = getTxSlotConfig(cfg), + .gpio_cfg = + { + .clk = (gpio_num_t)cfg.pin_bck, + .dout = (gpio_num_t)txPin, + .invert_flags = + { + .clk_inv = false, + }, + }, + }; + + if (i2s_channel_init_pdm_tx_mode(tx_chan, &pdm_tx_cfg) != ESP_OK) { + LOGE("i2s_channel_init_pdm_tx_mode %s", "tx"); + return false; + } + if (i2s_channel_enable(tx_chan)!=ESP_OK){ + LOGE("i2s_channel_enable %s", "tx"); + return false; + } + + } + if (cfg.rx_tx_mode == RX_MODE) { + i2s_pdm_rx_config_t pdm_rx_cfg = {.clk_cfg = getRxClockConfig(cfg), + .slot_cfg = getRxSlotConfig(cfg), + .gpio_cfg = { + .clk = (gpio_num_t)cfg.pin_bck, + .din = (gpio_num_t)rxPin, + .invert_flags = + { + .clk_inv = false, + }, + }}; + if (i2s_channel_init_pdm_rx_mode(rx_chan, &pdm_rx_cfg) != ESP_OK) { + LOGE("i2s_channel_init_pdm_tx_mode %s", "rx"); + return false; + } + if (i2s_channel_enable(rx_chan)!=ESP_OK){ + LOGE("i2s_channel_enable %s", "rx"); + return false; + } + return true; + } + TRACEE(); + return false; + } + } pdm; + + // struct DriverTDM : public DriverCommon { + // i2s_tdm_slot_config_t getSlotConfig(I2SConfig &cfg) { + // return I2S_TDM_SLOT_DEFAULT_CONFIG( + // (i2s_data_bit_width_t)cfg.bits_per_sample, + // (i2s_slot_mode_t)cfg.channels); + // } + + // i2s_chan_config_t getChannelConfig(I2SConfig &cfg) { + // return I2S_CHANNEL_DEFAULT_CONFIG((i2s_port_t)cfg.port_no, + // cfg.is_master ? I2S_ROLE_MASTER + // : I2S_ROLE_SLAVE); + // } + + // i2s_tdm_clk_config_t getClockConfig(I2SConfig &cfg) { + // return I2S_TDM_CLK_DEFAULT_CONFIG((uint32_t)cfg.sample_rate); + // } + + // bool startChannels(I2SConfig &cfg, i2s_chan_handle_t &tx_chan, + // i2s_chan_handle_t &rx_chan, int txPin, int rxPin) { + + // i2s_tdm_config_t tdm_cfg = { + // .clk_cfg = getClockConfig(cfg), + // .slot_cfg = getSlotConfig(cfg), + // .gpio_cfg = + // { + // .clk = (gpio_num_t)cfg.pin_bck, + // .dout = (gpio_num_t)txPin, + // .invert_flags = + // { + // .clk_inv = false, + // }, + // }, + // }; + + // if (cfg.rx_tx_mode == TX_MODE) { + + // if (i2s_channel_init_tdm_mode(tx_chan, &tdm_cfg) != ESP_OK) { + // LOGE("i2s_channel_init_tdm_tx_mode %s", "tx"); + // return false; + // } + // } + // if (cfg.rx_tx_mode == RX_MODE) { + // if (i2s_channel_init_tdm_mode(rx_chan, &tdm_cfg) != ESP_OK) { + // LOGE("i2s_channel_init_tdm_tx_mode %s", "rx"); + // return false; + // } + // return true; + // } + // return false; + // } + // } tdm; + + /// starts I2S + bool begin(I2SConfig cfg, int txPin, int rxPin) { + TRACED(); + cfg.logInfo(); + this->cfg = cfg; + if (cfg.channels <= 0 || cfg.channels > 2) { + LOGE("invalid channels: %d", cfg.channels); + return false; + } + + DriverCommon &driver = getDriver(cfg); + + i2s_chan_config_t chan_cfg = driver.getChannelConfig(cfg); + if (i2s_new_channel(&chan_cfg, &tx_chan, &rx_chan) != ESP_OK) { + LOGE("i2s_channel"); + return false; + } + + is_started = driver.startChannels(cfg, tx_chan, rx_chan, txPin, rxPin); + if(!is_started){ + LOGE("Channels not started"); + } + return is_started; + } + + DriverCommon &getDriver(I2SConfig &cfg) { + switch (cfg.signal_type) { + case Digital: + return i2s; + case Analog: + case PDM: + return pdm; + case TDM: + LOGW("TDM not supported"); + } + + return i2s; + } +}; + +using I2SDriver = I2SDriverESP32New; + +} // namespace audio_tools + +#endif \ No newline at end of file diff --git a/src/AudioI2S/I2SESP32.h b/src/AudioI2S/I2SESP32.h new file mode 100644 index 0000000000..2163998c5a --- /dev/null +++ b/src/AudioI2S/I2SESP32.h @@ -0,0 +1,371 @@ +#pragma once + +#if defined(ESP32) && !defined(USE_I2S_NEW) + +#include "AudioConfig.h" +#include "AudioI2S/I2SConfig.h" +#include "driver/i2s.h" +#include "esp_system.h" + +#ifndef I2S_MCLK_MULTIPLE_DEFAULT +#define I2S_MCLK_MULTIPLE_DEFAULT ((i2s_mclk_multiple_t)0) +#endif + +namespace audio_tools { + +/** + * @brief Basic I2S API - for the ESP32. If we receive 1 channel, we expand the result to 2 channels. + * @ingroup platform + * @author Phil Schatzmann + * @copyright GPLv3 + */ +class I2SDriverESP32 { + + friend class AnalogAudio; + friend class AudioKitStream; + + public: + + /// Provides the default configuration + I2SConfig defaultConfig(RxTxMode mode) { + I2SConfig c(mode); + return c; + } + + /// starts the DAC with the default config + bool begin(RxTxMode mode) { + return begin(defaultConfig(mode)); + } + + /// starts the DAC with the current config - if not started yet. If I2S has been started there is no action and we return true + bool begin() { + return !is_started ? begin(cfg) : true; + } + + /// starts the DAC + bool begin(I2SConfig cfg) { + TRACED(); + this->cfg = cfg; + switch(cfg.rx_tx_mode){ + case TX_MODE: + return begin(cfg, cfg.pin_data, I2S_PIN_NO_CHANGE); + case RX_MODE: + // usually we expet cfg.pin_data but if the used assinged rx we might consider this one + return begin(cfg, I2S_PIN_NO_CHANGE, cfg.pin_data!=I2S_PIN_NO_CHANGE?cfg.pin_data:cfg.pin_data_rx ); + default: + return begin(cfg, cfg.pin_data, cfg.pin_data_rx); + } + LOGE("Did not expect go get here"); + } + + /// we assume the data is already available in the buffer + int available() { + return I2S_BUFFER_COUNT*I2S_BUFFER_SIZE; + } + + /// We limit the write size to the buffer size + int availableForWrite() { + return I2S_BUFFER_COUNT*I2S_BUFFER_SIZE; + } + + /// stops the I2C and unistalls the driver + void end(){ + TRACED(); + i2s_driver_uninstall(i2s_num); + is_started = false; + } + + /// provides the actual configuration + I2SConfig config() { + return cfg; + } + + /// writes the data to the I2S interface + size_t writeBytes(const void *src, size_t size_bytes){ + TRACED(); + + size_t result = 0; + if (isNoChannelConversion(cfg)){ + if (i2s_write(i2s_num, src, size_bytes, &result, portMAX_DELAY)!=ESP_OK){ + TRACEE(); + } + LOGD("i2s_write %d -> %d bytes", size_bytes, result); + } else { + result = I2SDriverESP32::writeExpandChannel(i2s_num, cfg.bits_per_sample, src, size_bytes); + } + return result; + } + + size_t readBytes(void *dest, size_t size_bytes){ + size_t result = 0; + if (isNoChannelConversion(cfg)){ + if (i2s_read(i2s_num, dest, size_bytes, &result, portMAX_DELAY)!=ESP_OK){ + TRACEE(); + } + } else if (cfg.channels==1){ + // I2S has always 2 channels. We support to reduce it to 1 + uint8_t temp[size_bytes*2]; + if (i2s_read(i2s_num, temp, size_bytes*2, &result, portMAX_DELAY)!=ESP_OK){ + TRACEE(); + } + // convert to 1 channel + switch(cfg.bits_per_sample){ + case 16: { + ChannelReducer reducer16(1, 2); + result = reducer16.convert((uint8_t*)dest,temp, result); + } break; + case 24: { + ChannelReducer reducer24(1,2); + result = reducer24.convert((uint8_t*)dest,temp,result); + } break; + case 32: { + ChannelReducer reducer32(1, 2); + result = reducer32.convert((uint8_t*)dest, temp, result); + } break; + default: + LOGE("invalid bits_per_sample: %d", cfg.bits_per_sample); + break; + } + } else { + LOGE("Invalid channels: %d", cfg.channels); + } + return result; + } + + protected: + I2SConfig cfg = defaultConfig(RX_MODE); + i2s_port_t i2s_num; + i2s_config_t i2s_config; + bool is_started = false; + + bool isNoChannelConversion(I2SConfig cfg) { + if (cfg.channels==2) return true; + if (cfg.channels==1 && cfg.channel_format == I2S_CHANNEL_FMT_ONLY_RIGHT) return true; + if (cfg.channels==1 && cfg.channel_format == I2S_CHANNEL_FMT_ONLY_LEFT) return true; + return false; + } + + /// starts the DAC + bool begin(I2SConfig cfg, int txPin, int rxPin) { + TRACED(); + cfg.logInfo(); + this->cfg = cfg; + this->i2s_num = (i2s_port_t) cfg.port_no; + setChannels(cfg.channels); + + i2s_config_t i2s_config_new = { + .mode = toMode(cfg), + .sample_rate = (eps32_i2s_sample_rate_type)cfg.sample_rate, + .bits_per_sample = (i2s_bits_per_sample_t) cfg.bits_per_sample, + .channel_format = (i2s_channel_fmt_t) cfg.channel_format, + .communication_format = toCommFormat(cfg.i2s_format), + .intr_alloc_flags = 0, // default interrupt priority + .dma_buf_count = cfg.buffer_count, + .dma_buf_len = cfg.buffer_size, + .use_apll = (bool) cfg.use_apll, + .tx_desc_auto_clear = cfg.auto_clear, +#if ESP_IDF_VERSION_MAJOR >= 4 + .fixed_mclk = (int) (cfg.fixed_mclk>0 ? cfg.fixed_mclk : 0 ), + .mclk_multiple = I2S_MCLK_MULTIPLE_DEFAULT, + .bits_per_chan = I2S_BITS_PER_CHAN_DEFAULT, +#endif + }; + i2s_config = i2s_config_new; + + // We make sure that we can reconfigure + if (is_started) { + end(); + LOGD("%s", "I2S restarting"); + } + + // setup config + LOGD("i2s_driver_install"); + if (i2s_driver_install(i2s_num, &i2s_config, 0, NULL)!=ESP_OK){ + LOGE("%s - %s", __func__, "i2s_driver_install"); + } + + // setup pin config + if (this->cfg.signal_type == Digital || this->cfg.signal_type == PDM ) { + i2s_pin_config_t pin_config = { +#if ESP_IDF_VERSION > ESP_IDF_VERSION_VAL(4, 4, 0) + .mck_io_num = cfg.pin_mck, +#endif + .bck_io_num = cfg.pin_bck, + .ws_io_num = cfg.pin_ws, + .data_out_num = txPin, + .data_in_num = rxPin + }; + + LOGD("i2s_set_pin"); + if (i2s_set_pin(i2s_num, &pin_config)!= ESP_OK){ + LOGE("%s - %s", __func__, "i2s_set_pin"); + } + } else { + LOGD("Using built in DAC"); + //for internal DAC, this will enable both of the internal channels + i2s_set_pin(i2s_num, NULL); + } + + // clear initial buffer + LOGD("i2s_zero_dma_buffer"); + i2s_zero_dma_buffer(i2s_num); + + is_started = true; + LOGD("%s - %s", __func__, "started"); + return true; + } + + // update the cfg.i2s.channel_format based on the number of channels + void setChannels(int channels){ + cfg.channels = channels; + } + + + /// writes the data by making sure that we send 2 channels + static size_t writeExpandChannel(i2s_port_t i2s_num, const int bits_per_sample, const void *src, size_t size_bytes){ + size_t result = 0; + int j; + switch(bits_per_sample){ + + case 8: + for (j=0;j + + +namespace audio_tools { + +/** + * @brief Basic I2S API - for the ESP8266 + * Only 16 bits are supported ! + * @ingroup platform + * @author Phil Schatzmann + * @copyright GPLv3 + */ +class I2SDriverESP8266 { + friend class I2SStream; + public: + + /// Provides the default configuration + I2SConfig defaultConfig(RxTxMode mode) { + I2SConfig c(mode); + return c; + } + + /// starts the DAC with the default config + bool begin(RxTxMode mode = TX_MODE) { + return begin(defaultConfig(mode)); + } + + /// starts the DAC + bool begin(I2SConfig cfg) { + this->cfg = cfg; + i2s_set_rate(cfg.sample_rate); + cfg.bits_per_sample = 16; + if(!i2s_rxtx_begin(cfg.rx_tx_mode == RX_MODE, cfg.rx_tx_mode == TX_MODE)){ + LOGE("i2s_rxtx_begin failed"); + return false; + } + return true; + } + + /// stops the I2C and unistalls the driver + void end(){ + i2s_end(); + } + + /// we assume the data is already available in the buffer + int available() { + return I2S_BUFFER_COUNT*I2S_BUFFER_SIZE; + } + + /// We limit the write size to the buffer size + int availableForWrite() { + return I2S_BUFFER_COUNT*I2S_BUFFER_SIZE; + } + + /// provides the actual configuration + I2SConfig config() { + return cfg; + } + + protected: + I2SConfig cfg; + + /// writes the data to the I2S interface + size_t writeBytes(const void *src, size_t size_bytes){ + size_t result = 0; + size_t frame_size = cfg.channels * (cfg.bits_per_sample/8); + uint16_t frame_count = size_bytes / frame_size; + if (cfg.channels==2 && cfg.bits_per_sample==16){ + result = i2s_write_buffer(( int16_t *)src, frame_count ) * frame_size; + } else { + result = writeExt(src, size_bytes); + } + return result; + } + + /// writes the data by making shure that we send 2 channels 16 bit data + size_t writeExt(const void *src, size_t size_bytes){ + size_t result = 0; + int j; + + switch(cfg.bits_per_sample){ + + case 8: + for (j=0; j i2s_buffer(i2s_buffer_size, 5); + +/** + * @brief Mapping Frequency constants to available frequencies + */ +struct Nano_BLE_freq_info { + int id; + double freq; // in mhz +}; + +INLINE_VAR const Nano_BLE_freq_info freq_table[] = { + { I2S_CONFIG_MCKFREQ_MCKFREQ_32MDIV8, 32.0 / 8 }, + { I2S_CONFIG_MCKFREQ_MCKFREQ_32MDIV10, 32 / 10 }, + { I2S_CONFIG_MCKFREQ_MCKFREQ_32MDIV11, 32.0 / 11 }, + { I2S_CONFIG_MCKFREQ_MCKFREQ_32MDIV15, 32.0 / 15 }, + { I2S_CONFIG_MCKFREQ_MCKFREQ_32MDIV16, 32.0 / 16 }, + { I2S_CONFIG_MCKFREQ_MCKFREQ_32MDIV21, 32.0 / 21 }, + { I2S_CONFIG_MCKFREQ_MCKFREQ_32MDIV23, 32.0 / 23 }, + { I2S_CONFIG_MCKFREQ_MCKFREQ_32MDIV30, 32.0 / 30 }, + { I2S_CONFIG_MCKFREQ_MCKFREQ_32MDIV31, 32.0 / 31 }, + { I2S_CONFIG_MCKFREQ_MCKFREQ_32MDIV32, 32.0 / 32 }, + { I2S_CONFIG_MCKFREQ_MCKFREQ_32MDIV42, 32.0 / 42 }, + { I2S_CONFIG_MCKFREQ_MCKFREQ_32MDIV63, 32.0 / 63 }, + { I2S_CONFIG_MCKFREQ_MCKFREQ_32MDIV125, 32.0 / 125 } +}; + +/** + * @brief Mapping from Ratio Constants to frequency ratios + */ +struct Nano_BLE_ratio_info { + int id; + double ratio; +}; + +INLINE_VAR const Nano_BLE_ratio_info ratio_table[] = { + { I2S_CONFIG_RATIO_RATIO_32X, 32.0 }, + { I2S_CONFIG_RATIO_RATIO_48X, 48.0 }, + { I2S_CONFIG_RATIO_RATIO_64X, 64.0 }, + { I2S_CONFIG_RATIO_RATIO_96X, 96.0 }, + { I2S_CONFIG_RATIO_RATIO_128X, 128.0 }, + { I2S_CONFIG_RATIO_RATIO_192X, 192.0 }, + { I2S_CONFIG_RATIO_RATIO_256X, 256.0 }, + { I2S_CONFIG_RATIO_RATIO_384X, 384.0 }, + { I2S_CONFIG_RATIO_RATIO_512X, 512.0 } +}; + +/** + * I2S Event handler + */ + +extern "C" void I2S_IRQHandler(void) { + if(NRF_I2S->EVENTS_TXPTRUPD != 0) { + // reading from buffer to pins + NRF_I2S->TXD.PTR = (uint32_t) i2s_buffer.readEnd().address(); // last buffer was processed + NRF_I2S->EVENTS_TXPTRUPD = 0; + + } else if(NRF_I2S->EVENTS_RXPTRUPD != 0) { + // reading from pins writing to buffer + NRF_I2S->RXD.PTR = (uint32_t) i2s_buffer.writeEnd().address(); // last buffer was processed + NRF_I2S->EVENTS_RXPTRUPD = 0; + } +} + +/** + * @brief Basic I2S API - for the Arduino Nano BLE Sense + * @author Phil Schatzmann + * @ingroup platform + * @copyright GPLv3 + */ +class I2SDriverNanoBLE { + friend class I2SStream; + + public: + + I2SDriverNanoBLE(){ + // register IRQ for I2S + setupIRQ(); + } + + /// Provides the default configuration + I2SConfig defaultConfig(RxTxMode mode) { + I2SConfig c(mode); + return c; + } + + /// starts the DAC with the default config in TX Mode + bool begin(RxTxMode mode = TX_MODE) { + return begin(defaultConfig(mode)); + } + + /// starts the DAC + bool begin(I2SConfig cfg) { + LOGD(__func__); + this->cfg = cfg; + setupRxTx(cfg); + setupClock(cfg); + setupBitWidth(cfg); + setupMode(cfg); + setupPins(cfg); + setupData(cfg); + + // Use stereo + NRF_I2S->CONFIG.CHANNELS = I2S_CONFIG_CHANNELS_CHANNELS_Stereo << I2S_CONFIG_CHANNELS_CHANNELS_Pos; + // Setup master or slave mode + NRF_I2S->CONFIG.MODE = cfg.is_master ? 0 << I2S_CONFIG_MODE_MODE_Pos : 1 << I2S_CONFIG_MODE_MODE_Pos ; + // ensble I2S + NRF_I2S->ENABLE = 1; + // start task + NRF_I2S->TASKS_START = 1; + return true; + } + + int available() { + return i2s_buffer.available(); + } + + int availableForWrite() { + return i2s_buffer.availableForWrite(); + } + + /// stops the I2C and unistalls the driver + void end(){ + LOGD(__func__); + // stop task + NRF_I2S->TASKS_START = 0; + // ensble I2S + NRF_I2S->ENABLE = 0; + } + + /// provides the actual configuration + I2SConfig config() { + return cfg; + } + + protected: + I2SConfig cfg; + + /// writes the data to the I2S buffer + size_t writeBytes(const void *src, size_t size_bytes){ + size_t result = i2s_buffer.writeArray((uint8_t*)src, size_bytes); + return result; + } + + // reads the data from the I2S buffer + size_t readBytes(void *dest, size_t size_bytes){ + size_t result = i2s_buffer.readArray((uint8_t*)dest, size_bytes); + return result; + } + + void setupIRQ() { + TRACED(); + NVIC_EnableIRQ(I2S_IRQn); + } + + void setupRxTx(I2SConfig cfg) { + TRACED(); + if (cfg.rx_tx_mode == TX_MODE) { + // Enable transmission + NRF_I2S->CONFIG.TXEN = (I2S_CONFIG_TXEN_TXEN_Enabled << I2S_CONFIG_TXEN_TXEN_Pos); + } else { + // Enable reception + NRF_I2S->CONFIG.RXEN = (I2S_CONFIG_RXEN_RXEN_Enabled << I2S_CONFIG_RXEN_RXEN_Pos); + } + } + + void setupClock(I2SConfig cfg){ + TRACED(); + // Enable MCK generator if in master mode + if (cfg.is_master){ + NRF_I2S->CONFIG.MCKEN = (I2S_CONFIG_MCKEN_MCKEN_Enabled << I2S_CONFIG_MCKEN_MCKEN_Pos); + } + + // find closest frequency for requested sample_rate + float freq_requested = cfg.sample_rate * cfg.bits_per_sample ; + float selected_freq = 0; + for (auto freq : freq_table) { + for (auto div : ratio_table) { + float freq_value = freq.freq * 1000000 / div.ratio; + if (abs(freq_value-freq_requested) < abs(selected_freq-freq_requested)){ + // MCKFREQ + NRF_I2S->CONFIG.MCKFREQ = freq.id << I2S_CONFIG_MCKFREQ_MCKFREQ_Pos; + // Ratio + NRF_I2S->CONFIG.RATIO = div.id << I2S_CONFIG_RATIO_RATIO_Pos; + selected_freq = freq_value; + } + } + LOGI("frequency requested %f vs %f", freq_requested, selected_freq); + } + } + + void setupBitWidth(I2SConfig cfg) { + TRACED(); + uint16_t swidth = I2S_CONFIG_SWIDTH_SWIDTH_16Bit; + switch(cfg.bits_per_sample){ + case 8: + NRF_I2S->CONFIG.SWIDTH = I2S_CONFIG_SWIDTH_SWIDTH_8Bit << I2S_CONFIG_SWIDTH_SWIDTH_Pos; + break; + case 16: + NRF_I2S->CONFIG.SWIDTH = I2S_CONFIG_SWIDTH_SWIDTH_16Bit << I2S_CONFIG_SWIDTH_SWIDTH_Pos; + break; + case 24: + NRF_I2S->CONFIG.SWIDTH = I2S_CONFIG_SWIDTH_SWIDTH_24Bit << I2S_CONFIG_SWIDTH_SWIDTH_Pos; + break; + default: + LOGE("Unsupported bit width: %d", cfg.bits_per_sample); + } + } + + void setupMode(I2SConfig cfg){ + TRACED(); + // setup mode + switch(cfg.i2s_format){ + case I2S_STD_FORMAT: + case I2S_PHILIPS_FORMAT: + NRF_I2S->CONFIG.ALIGN = I2S_CONFIG_FORMAT_FORMAT_I2S << I2S_CONFIG_FORMAT_FORMAT_Pos; + break; + case I2S_MSB_FORMAT: + case I2S_LEFT_JUSTIFIED_FORMAT: + NRF_I2S->CONFIG.FORMAT = I2S_CONFIG_FORMAT_FORMAT_I2S << I2S_CONFIG_FORMAT_FORMAT_Pos; + NRF_I2S->CONFIG.ALIGN = I2S_CONFIG_ALIGN_ALIGN_Left << I2S_CONFIG_ALIGN_ALIGN_Pos;; + break; + case I2S_LSB_FORMAT: + case I2S_RIGHT_JUSTIFIED_FORMAT: + NRF_I2S->CONFIG.FORMAT = I2S_CONFIG_FORMAT_FORMAT_I2S << I2S_CONFIG_FORMAT_FORMAT_Pos; + NRF_I2S->CONFIG.ALIGN = I2S_CONFIG_ALIGN_ALIGN_Right << I2S_CONFIG_ALIGN_ALIGN_Pos;; + break; + } + } + + // setup pins + void setupPins(I2SConfig cfg){ + TRACED(); + // MCK routed to pin 0 + // if (cfg.is_master){ + // NRF_I2S->PSEL.MCK = (0 << I2S_PSEL_MCK_PIN_Pos) | (I2S_PSEL_MCK_CONNECT_Connected << I2S_PSEL_MCK_CONNECT_Pos); + // } + // SCK - bit clock - routed to pin 1 + NRF_I2S->PSEL.SCK = (cfg.pin_bck << I2S_PSEL_SCK_PIN_Pos) | (I2S_PSEL_SCK_CONNECT_Connected << I2S_PSEL_SCK_CONNECT_Pos); + // LRCK routed to pin 2 + NRF_I2S->PSEL.LRCK = (cfg.pin_ws << I2S_PSEL_LRCK_PIN_Pos) | (I2S_PSEL_LRCK_CONNECT_Connected << I2S_PSEL_LRCK_CONNECT_Pos); + if (cfg.rx_tx_mode == TX_MODE) { + // SDOUT routed to pin 3 + NRF_I2S->PSEL.SDOUT = (cfg.pin_data << I2S_PSEL_SDOUT_PIN_Pos) | (I2S_PSEL_SDOUT_CONNECT_Connected << I2S_PSEL_SDOUT_CONNECT_Pos); + } else { + // SDIN routed on pin 4 + NRF_I2S->PSEL.SDIN = (cfg.pin_data << I2S_PSEL_SDIN_PIN_Pos) |(I2S_PSEL_SDIN_CONNECT_Connected << I2S_PSEL_SDIN_CONNECT_Pos); + } + } + + // setup initial data pointers + void setupData(I2SConfig cfg) { + TRACED(); + NRF_I2S->TXD.PTR = (uint32_t) i2s_buffer.readEnd().address(); // last buffer was processed + NRF_I2S->RXD.PTR = (uint32_t) i2s_buffer.writeEnd().address(); // last buffer was processed + NRF_I2S->RXTXD.MAXCNT = i2s_buffer_size; + } + +}; + +using I2SDriver = I2SDriverNanoBLE; + + +} // namespace + +#endif diff --git a/src/AudioI2S/I2SRP2040.h b/src/AudioI2S/I2SRP2040.h new file mode 100644 index 0000000000..41ae1f7b82 --- /dev/null +++ b/src/AudioI2S/I2SRP2040.h @@ -0,0 +1,219 @@ +#pragma once + +#if defined(ARDUINO_ARCH_RP2040) +#include "AudioI2S/I2SConfig.h" +#if defined(ARDUINO_ARCH_MBED_RP2040) +# include "RP2040-I2S.h" +#else +# include +#endif + + +namespace audio_tools { + +/** + * @brief Basic I2S API - for the ... + * @ingroup platform + * @author Phil Schatzmann + * @author LinusHeu + * @copyright GPLv3 + */ +class I2SDriverRP2040 { + friend class I2SStream; + + public: + + /// Provides the default configuration + I2SConfig defaultConfig(RxTxMode mode) { + I2SConfig c(mode); + return c; + } + + /// starts the DAC with the default config in TX Mode + bool begin(RxTxMode mode = TX_MODE) { + TRACED(); + return begin(defaultConfig(mode)); + } + + /// starts the DAC + bool begin(I2SConfig cfg) { + TRACEI(); + this->cfg = cfg; + cfg.logInfo(); + switch (cfg.rx_tx_mode){ + case TX_MODE: + i2s = I2S(OUTPUT); + break; + //case RX_MODE: + // i2s = I2S(INPUT); + // break; + default: + LOGE("Unsupported mode: only TX_MODE is supported"); + return false; + break; + } + + if (cfg.pin_ws == cfg.pin_bck + 1){ //normal pin order + if(!i2s.setBCLK(cfg.pin_bck)){ + LOGE("Could not set bck pin: %d", cfg.pin_bck); + return false; + } + } else if(cfg.pin_ws == cfg.pin_bck - 1){ //reverse pin order + if (!i2s.swapClocks() || !i2s.setBCLK(cfg.pin_ws)){//setBCLK() actually sets the lower pin of bck/ws + LOGE("Could not set bck pin: %d", cfg.pin_bck); + return false; + } + } else{ + LOGE("pins bck: '%d' and ws: '%d' must be next to each other", cfg.pin_bck, cfg.pin_ws); + return false; + } + if (!i2s.setDATA(cfg.pin_data)){ + LOGE("Could not set data pin: %d", cfg.pin_data); + return false; + } + + if (!i2s.setBitsPerSample(cfg.bits_per_sample)){ + LOGE("Could not set bits per sample: %d", cfg.bits_per_sample); + return false; + } + + if (!i2s.setBuffers(cfg.buffer_count, cfg.buffer_size)){ + LOGE("Could not set buffers: Count: '%d', size: '%d'", cfg.buffer_count, cfg.buffer_size); + return false; + } + + if(cfg.i2s_format == I2S_LEFT_JUSTIFIED_FORMAT){ + if(!i2s.setLSBJFormat()){ + LOGE("Could not set LSB Format") + return false; + } + } else if(cfg.i2s_format != I2S_STD_FORMAT){ + LOGE("Unsupported I2S format"); + return false; + } + + if (cfg.channels < 1 || cfg.channels > 2 ){ + LOGE("Unsupported channels: '%d'", cfg.channels); + return false; + } + + if (!i2s.begin(cfg.sample_rate)){ + LOGE("Could not start I2S"); + return false; + } + return true; + } + + /// stops the I2C and uninstalls the driver + void end() { + flush(); + i2s.end(); + } + + /// provides the actual configuration + I2SConfig config() { + return cfg; + } + + /// writes the data to the I2S interface + size_t writeBytes(const void *src, size_t size_bytes) { + TRACED(); + size_t result = 0; + + if (cfg.channels==1){ + result = writeExpandChannel(src,size_bytes); + } else if (cfg.channels==2){ + const uint8_t *p = (const uint8_t*) src; + while(size_bytes >= sizeof(int32_t)){ + bool justWritten = i2s.write(*(int32_t*)p,true); //I2S::write(int32,bool) actually only returns 0 or 1 + if(justWritten){ + size_bytes -= sizeof(int32_t); + p += sizeof(int32_t); + result += sizeof(int32_t); + } else return result; + } + } + return result; + } + + size_t readBytes(void *dest, size_t size_bytes) { + TRACEE(); + size_t result = 0; + return result; + } + + int availableForWrite() { + if (cfg.channels == 1){ + return i2s.availableForWrite()/2;// return half of it because we double when writing + } else { + return i2s.availableForWrite(); + } + } + + int available() { + return 0; + } + + void flush() { + i2s.flush(); + } + + protected: + I2SConfig cfg; + I2S i2s; + + //writes 1 channel to I2S while expanding it to 2 channels + //writes multiple of 4 bytes like I2S::write() wants + //returns amount of bytes written from src to i2s + size_t writeExpandChannel(const void *src, size_t size_bytes) { + size_t writtenBytes = 0; + switch(cfg.bits_per_sample){ + case 8: + for(int i = 0; i handle just like 32bps + case 32: + for(int i = 0; i + + +namespace audio_tools { + +/** + * @brief Basic I2S API - for the SAMD + * @ingroup platform + * @author Phil Schatzmann + * @copyright GPLv3 + */ +class I2SDriverSAMD { + friend class I2SStream; + + public: + + /// Provides the default configuration + I2SConfig defaultConfig(RxTxMode mode) { + I2SConfig c(mode); + return c; + } + + /// starts the DAC with the default config + bool begin(RxTxMode mode) { + return begin(defaultConfig(mode)); + } + + /// starts the DAC + bool begin(I2SConfig cfg) { + this->cfg = cfg; + return I2S.begin(cfg.i2s_format, cfg.sample_rate, cfg.bits_per_sample); + } + + bool begin() { + return begin(cfg); + } + + /// stops the I2C and unistalls the driver + void end(){ + I2S.end(); + } + + /// provides the actual configuration + I2SConfig config() { + return cfg; + } + + size_t writeBytes(const void *src, size_t size_bytes){ + return I2S.write((const uint8_t *)src, size_bytes); + } + + size_t readBytes(void *src, size_t size_bytes){ + return I2S.read(src, size_bytes); + } + + int available() { + return I2S.available(); + } + + int availableForWrite() { + return I2S.availableForWrite(); + } + + protected: + I2SConfig cfg; + +}; + +using I2SDriver = I2SDriverSAMD; + +} + +#endif diff --git a/src/AudioI2S/I2SSTM32.h b/src/AudioI2S/I2SSTM32.h new file mode 100644 index 0000000000..8576742872 --- /dev/null +++ b/src/AudioI2S/I2SSTM32.h @@ -0,0 +1,260 @@ +#pragma once + +#ifdef STM32 +#include "AudioI2S/I2SConfig.h" + +namespace audio_tools { +#include "stm32-i2s.h" + +NBuffer *p_tx_buffer=nullptr; +NBuffer *p_rx_buffer=nullptr; + +/** + * @brief Basic I2S API - for the STM32 + * Depends on https://github.com/pschatzmann/stm32f411-i2s + * We just add a write and read buffer and pass some parameters to the STM32 API! + * @ingroup platform + * @author Phil Schatzmann + * @copyright GPLv3 + */ + +class I2SDriverSTM32 { + friend class I2SStream; + + public: + + /// Provides the default configuration + I2SConfig defaultConfig(RxTxMode mode = TX_MODE) { + I2SConfig c(mode); + return c; + } + + /// starts the DAC with the default config in TX Mode + bool begin(RxTxMode mode = TX_MODE) { + TRACED(); + return begin(defaultConfig(mode)); + } + + /// starts the DAC + bool begin(I2SConfig cfg) { + TRACED(); + bool result = false; + deleteBuffers(); + + if (cfg.bits_per_sample!=16){ + LOGE("Bits per sample not supported: %d", cfg.bits_per_sample); + return false; + } + if (cfg.channels>2 || cfg.channels<=0){ + LOGE("Channels not supported: %d", cfg.channels); + return false; + } + + setupDefaultI2SParameters(); + + switch(cfg.rx_tx_mode){ + case RX_MODE: + p_rx_buffer = new NBuffer(cfg.buffer_size, cfg.buffer_count); + result = startI2SReceive(&i2s_stm32, writeFromReceive, cfg.buffer_size); + break; + case TX_MODE: + p_tx_buffer = new NBuffer(cfg.buffer_size, cfg.buffer_count); + result = startI2STransmit(&i2s_stm32, readToTransmit, cfg.buffer_size); + break; + case RXTX_MODE: + p_tx_buffer = new NBuffer(cfg.buffer_size, cfg.buffer_count); + result = startI2STransmitReceive(&i2s_stm32, readToTransmit, writeFromReceive, cfg.buffer_size); + break; + default: + LOGE("Unsupported mode"); + return false; + + } + + return result; + } + + /// stops the I2C and unistalls the driver + void end(){ + TRACED(); + stopI2S(); + deleteBuffers(); + } + + /// we assume the data is already available in the buffer + int available() { + return I2S_BUFFER_COUNT*I2S_BUFFER_SIZE; + } + + /// We limit the write size to the buffer size + int availableForWrite() { + return I2S_BUFFER_SIZE; + } + + /// provides the actual configuration + I2SConfig config() { + return cfg; + } + + /// blocking writes for the data to the I2S interface + size_t writeBytes(const void *src, size_t size_bytes){ + TRACED(); + size_t result = 0; + int open = size_bytes; + while(open>0){ + int actual_written = writeBytesExt(src, size_bytes); + result+= actual_written; + open -= actual_written; + if (open>0){ + delay(10); + } + } + return result; + } + + size_t readBytes(void *dest, size_t size_bytes){ + TRACED(); + if (cfg.channels == 2){ + return p_rx_buffer->readArray((uint8_t*)dest, size_bytes); + } else { + // combine two channels to 1: so request double the amout + int req_bytes = size_bytes*2; + uint8_t tmp[req_bytes]; + int16_t *tmp_16 = (int16_t*) tmp; + int eff_bytes = p_rx_buffer->readArray((uint8_t*)tmp, req_bytes); + // add 2 channels together + int16_t *dest_16 = (int16_t *)dest; + int16_t eff_samples = eff_bytes / 2; + int16_t idx = 0; + for (int j=0;j(tmp_16[j])+tmp_16[j+1] / 2.0; + } + return eff_bytes / 2; + } + } + + /// @brief Callback function used by https://github.com/pschatzmann/stm32f411-i2s + static void writeFromReceive(uint8_t *buffer, uint16_t byteCount){ + p_rx_buffer->writeArray(buffer, byteCount); + } + + /// @brief Callback function used by https://github.com/pschatzmann/stm32f411-i2s + static void readToTransmit(uint8_t *buffer, uint16_t byteCount) { + memset(buffer,0,byteCount); + p_tx_buffer->readArray(buffer, byteCount); + } + + protected: + I2SConfig cfg; + I2SSettingsSTM32 i2s_stm32; + + void deleteBuffers() { + if (p_rx_buffer!=nullptr) { + delete p_rx_buffer; + p_rx_buffer = nullptr; + } + if (p_tx_buffer!=nullptr) { + delete p_tx_buffer; + p_tx_buffer = nullptr; + } + } + + void setupDefaultI2SParameters() { + i2s_stm32.sample_rate = getSampleRate(cfg); + i2s_stm32.mode = getMode(cfg); + i2s_stm32.standard = getStandard(cfg); + i2s_stm32.fullduplexmode = cfg.rx_tx_mode == RXTX_MODE ? I2S_FULLDUPLEXMODE_ENABLE : I2S_FULLDUPLEXMODE_DISABLE; + i2s_stm32.i2s = &hi2s3; + } + + uint32_t getMode(I2SConfig &cfg){ + if (cfg.is_master) { + switch(cfg.rx_tx_mode){ + case RX_MODE: + return I2S_MODE_MASTER_RX; + case TX_MODE: + return I2S_MODE_MASTER_TX; + default: + LOGE("RXTX_MODE not supported"); + return I2S_MODE_MASTER_TX; + } + } else { + switch(cfg.rx_tx_mode){ + case RX_MODE: + return I2S_MODE_SLAVE_RX; + case TX_MODE: + return I2S_MODE_SLAVE_TX; + default: + LOGE("RXTX_MODE not supported"); + return I2S_MODE_SLAVE_TX; + } + } + } + + + uint32_t getStandard(I2SConfig &cfg){ + uint32_t result; + switch(cfg.i2s_format) { + case I2S_PHILIPS_FORMAT: + return I2S_STANDARD_PHILIPS; + case I2S_STD_FORMAT: + case I2S_LSB_FORMAT: + case I2S_RIGHT_JUSTIFIED_FORMAT: + return I2S_STANDARD_MSB; + case I2S_MSB_FORMAT: + case I2S_LEFT_JUSTIFIED_FORMAT: + return I2S_STANDARD_LSB; + } + return I2S_STANDARD_PHILIPS; + } + + uint32_t getSampleRate(I2SConfig &cfg){ + switch(cfg.sample_rate){ + case I2S_AUDIOFREQ_192K: + case I2S_AUDIOFREQ_96K: + case I2S_AUDIOFREQ_48K: + case I2S_AUDIOFREQ_44K: + case I2S_AUDIOFREQ_32K: + case I2S_AUDIOFREQ_22K: + case I2S_AUDIOFREQ_16K: + case I2S_AUDIOFREQ_11K: + case I2S_AUDIOFREQ_8K: + return cfg.sample_rate; + default: + LOGE("Unsupported sample rate: %u", cfg.sample_rate); + return cfg.sample_rate; + } + } + + size_t writeBytesExt(const void *src, size_t size_bytes){ + size_t result = 0; + if (cfg.channels == 2){ + result = p_tx_buffer->writeArray((uint8_t*)src, size_bytes); + } else { + // write each sample 2 times + int samples = size_bytes / 2; + int16_t *src_16 = (int16_t *)src; + int16_t tmp[2]; + int result = 0; + for (int j=0;javailableForWrite()>=4){ + p_tx_buffer->writeArray((uint8_t*)tmp, 4); + result = j*2; + } else { + // abort if the buffer is full + break; + } + } + } + LOGD("writeBytesExt: %u", result) + return result; + } +}; + +using I2SDriver = I2SDriverSTM32; + +} + +#endif diff --git a/src/AudioI2S/I2SStream.h b/src/AudioI2S/I2SStream.h new file mode 100644 index 0000000000..55b63d909f --- /dev/null +++ b/src/AudioI2S/I2SStream.h @@ -0,0 +1,130 @@ + + + +#pragma once +#include "AudioConfig.h" + +#if defined(USE_I2S) +#include "AudioTools/AudioTypes.h" +#include "AudioTools/AudioStreams.h" +#include "AudioI2S/I2SConfig.h" +#include "AudioI2S/I2SESP32.h" +#include "AudioI2S/I2SESP32-New.h" +#include "AudioI2S/I2SESP8266.h" +#include "AudioI2S/I2SSAMD.h" +#include "AudioI2S/I2SNanoSenseBLE.h" +#include "AudioI2S/I2SRP2040.h" +#include "AudioI2S/I2SSTM32.h" + + +namespace audio_tools { + +/** + * @brief We support the Stream interface for the I2S access. In addition we allow a separate mute pin which might also be used + * to drive a LED... + * @ingroup io + * @tparam T + * @author Phil Schatzmann + * @copyright GPLv3 + */ + +class I2SStream : public AudioStream { + + public: + I2SStream() = default; + +#ifdef ARDUINO + I2SStream(int mute_pin) { + TRACED(); + this->mute_pin = mute_pin; + if (mute_pin>0) { + pinMode(mute_pin, OUTPUT); + mute(true); + } + } +#endif + + /// Provides the default configuration + I2SConfig defaultConfig(RxTxMode mode=TX_MODE) { + return i2s.defaultConfig(mode); + } + + bool begin() { + return i2s.begin(); + } + + /// Starts the I2S interface + bool begin(I2SConfig cfg) { + TRACED(); + bool result = i2s.begin(cfg); + // unmute + mute(false); + return result; + } + + /// Stops the I2S interface + void end() { + TRACED(); + mute(true); + i2s.end(); + } + + /// updates the sample rate dynamically + virtual void setAudioInfo(AudioInfo info) { + TRACEI(); + AudioStream::setAudioInfo(info); + I2SConfig cfg = i2s.config(); + if (cfg.sample_rate != info.sample_rate + || cfg.channels != info.channels + || cfg.bits_per_sample != info.bits_per_sample) { + cfg.sample_rate = info.sample_rate; + cfg.bits_per_sample = info.bits_per_sample; + cfg.channels = info.channels; + cfg.logInfo(); + + i2s.end(); + i2s.begin(cfg); + } + } + + /// Writes the audio data to I2S + virtual size_t write(const uint8_t *buffer, size_t size) { + LOGD("I2SStream::write: %d", size); + return i2s.writeBytes(buffer, size); + } + + /// Reads the audio data + virtual size_t readBytes( uint8_t *data, size_t length) override { + return i2s.readBytes(data, length); + } + + /// Provides the available audio data + virtual int available() override { + return i2s.available(); + } + + /// Provides the available audio data + virtual int availableForWrite() override { + return i2s.availableForWrite(); + } + + void flush() override {} + + protected: + I2SDriver i2s; + int mute_pin; + + /// set mute pin on or off + void mute(bool is_mute){ +#ifdef ARDUINO + if (mute_pin>0) { + digitalWrite(mute_pin, is_mute ? SOFT_MUTE_VALUE : !SOFT_MUTE_VALUE ); + } +#endif + } + +}; + +} + +#endif diff --git a/src/AudioTools/CoreAudio/AudioI2S/README.md b/src/AudioI2S/README.md similarity index 100% rename from src/AudioTools/CoreAudio/AudioI2S/README.md rename to src/AudioI2S/README.md diff --git a/src/AudioTools/AudioLibs/A2DPStream.h b/src/AudioLibs/AudioA2DP.h similarity index 59% rename from src/AudioTools/AudioLibs/A2DPStream.h rename to src/AudioLibs/AudioA2DP.h index 7b0d7ec9c0..04bc76f886 100644 --- a/src/AudioTools/AudioLibs/A2DPStream.h +++ b/src/AudioLibs/AudioA2DP.h @@ -1,5 +1,5 @@ /** - * @file A2DPStream.h + * @file AudioA2DP.h * @author Phil Schatzmann * @brief A2DP Support via Arduino Streams * @copyright GPLv3 @@ -7,31 +7,27 @@ */ #pragma once -#include "AudioToolsConfig.h" +#include "AudioConfig.h" #include "AudioTools.h" #include "BluetoothA2DPSink.h" #include "BluetoothA2DPSource.h" -#include "AudioTools/CoreAudio/AudioStreams.h" -#include "AudioTools/Concurrency/RTOS/BufferRTOS.h" -#include "AudioTools/CoreAudio/AudioBasic/StrView.h" +#include "AudioTools/AudioStreams.h" namespace audio_tools { class A2DPStream; -static A2DPStream *A2DPStream_self=nullptr; +A2DPStream *A2DPStream_self=nullptr; // buffer which is used to exchange data -static BufferRTOSa2dp_buffer{0, A2DP_BUFFER_SIZE, portMAX_DELAY, portMAX_DELAY}; +SynchronizedBufferRTOSa2dp_buffer{A2DP_BUFFER_SIZE * A2DP_BUFFER_COUNT, A2DP_BUFFER_SIZE, portMAX_DELAY, portMAX_DELAY}; // flag to indicated that we are ready to process data -static bool is_a2dp_active = false; +bool is_a2dp_active = false; int32_t a2dp_stream_source_sound_data(Frame* data, int32_t len); void a2dp_stream_sink_sound_data(const uint8_t* data, uint32_t len); -/// A2DP Startup Logic enum A2DPStartLogic {StartWhenBufferFull, StartOnConnect}; -/// A2DP Action when there is no data enum A2DPNoData {A2DPSilence, A2DPWhoosh}; /** @@ -41,37 +37,24 @@ enum A2DPNoData {A2DPSilence, A2DPWhoosh}; */ class A2DPConfig { public: - /// Logic when the processing is activated (default StartWhenBufferFull) - A2DPStartLogic startup_logic = StartWhenBufferFull; - /// Action when a2dp is not active yet (default A2DPSilence) - A2DPNoData startup_nodata = A2DPSilence; - /// Mode: TX_MODE or RX_MODE (default RX_MODE) + A2DPStartLogic startLogic = StartWhenBufferFull; + A2DPNoData noData = A2DPSilence; RxTxMode mode = RX_MODE; - /// A2DP name (default A2DP) const char* name = "A2DP"; - /// automatically reconnect if connection is lost (default false) bool auto_reconnect = false; - int buffer_size = A2DP_BUFFER_SIZE * A2DP_BUFFER_COUNT; - /// Delay in ms which is added to each write (default 1) - int delay_ms = 1; - /// when a2dp source is active but has no data we generate silence data (default false) - bool silence_on_nodata = false; - /// write timeout in ms: -1 is blocking write (default -1) - int tx_write_timeout_ms = -1; // no timeout - /// begin should wait for connection to be established (default true) - bool wait_for_connection=true; + int bufferSize = A2DP_BUFFER_SIZE * A2DP_BUFFER_COUNT; + int delay_ms = 5; }; /** - * @brief Stream support for A2DP using https://github.com/pschatzmann/ESP32-A2DP: - * begin(TX_MODE) opens a a2dp_source and begin(RX_MODE) a a2dp_sink. + * @brief Stream support for A2DP: begin(TX_MODE) uses a2dp_source - begin(RX_MODE) a a2dp_sink * The data is in int16_t with 2 channels at 44100 hertz. * We support only one instance of the class! * Please note that this is a conveniance class that supports the stream api, - * however this is rather inefficient, beause quite a big buffer needs to be allocated. - * It is recommended to use the API with the callbacks. Examples can be found in the a2dp - * examples directory starting with basic. + * however this is rather inefficient, beause quite a bit buffer needs to be allocated. + * It is recommended to use the API with the callbacks. Examples can be found in the examples-basic-api + * directory. * * Requires: https://github.com/pschatzmann/ESP32-A2DP * @@ -80,7 +63,7 @@ class A2DPConfig { * @author Phil Schatzmann * @copyright GPLv3 */ -class A2DPStream : public AudioStream, public VolumeSupport { +class A2DPStream : public AudioStream { public: A2DPStream() { @@ -110,7 +93,7 @@ class A2DPStream : public AudioStream, public VolumeSupport { return cfg; } - /// provides access to the BluetoothA2DPSource + /// provides access to the BluetoothA2DPSource &source() { if (a2dp_source==nullptr){ a2dp = a2dp_source = new BluetoothA2DPSource(); @@ -127,12 +110,11 @@ class A2DPStream : public AudioStream, public VolumeSupport { } /// Starts the processing - bool begin(RxTxMode mode, const char* name, bool wait_for_connection=true){ + void begin(RxTxMode mode, const char* name){ A2DPConfig cfg; cfg.mode = mode; cfg.name = name; - cfg.wait_for_connection = wait_for_connection; - return begin(cfg); + begin(cfg); } /// Starts the processing @@ -140,42 +122,27 @@ class A2DPStream : public AudioStream, public VolumeSupport { this->config = cfg; bool result = false; LOGI("Connecting to %s",cfg.name); - - if (!a2dp_buffer.resize(cfg.buffer_size)){ - LOGE("a2dp_buffer resize failed"); - return false; - } - - // initialize a2dp_silence_timeout - if (config.silence_on_nodata){ - LOGI("Using StartOnConnect") - config.startup_logic = StartOnConnect; - } + a2dp_buffer.resize(cfg.bufferSize); switch (cfg.mode){ case TX_MODE: LOGI("Starting a2dp_source..."); source(); // allocate object a2dp_source->set_auto_reconnect(cfg.auto_reconnect); - a2dp_source->set_volume(volume() * A2DP_MAX_VOL); - if(StrView(cfg.name).equals("[Unknown]")){ + a2dp_source->set_volume(volume * 100); + if(Str(cfg.name).equals("[Unknown]")){ //search next available device a2dp_source->set_ssid_callback(detected_device); } a2dp_source->set_on_connection_state_changed(a2dp_state_callback, this); - a2dp_source->start_raw((char*)cfg.name, a2dp_stream_source_sound_data); - if (cfg.wait_for_connection){ - while(!a2dp_source->is_connected()){ - LOGD("waiting for connection"); - delay(1000); - } - LOGI("a2dp_source is connected..."); - notify_base_Info(44100); - //is_a2dp_active = true; - } - else{ - LOGI("a2dp_source started without connecting"); + a2dp_source->start_raw((char*)cfg.name, a2dp_stream_source_sound_data); + while(!a2dp_source->is_connected()){ + LOGD("waiting for connection"); + delay(1000); } + LOGI("a2dp_source is connected..."); + notify_base_Info(44100); + //is_a2dp_active = true; result = true; break; @@ -184,20 +151,15 @@ class A2DPStream : public AudioStream, public VolumeSupport { sink(); // allocate object a2dp_sink->set_auto_reconnect(cfg.auto_reconnect); a2dp_sink->set_stream_reader(&a2dp_stream_sink_sound_data, false); - a2dp_sink->set_volume(volume() * A2DP_MAX_VOL); + a2dp_sink->set_volume(volume * 100); a2dp_sink->set_on_connection_state_changed(a2dp_state_callback, this); a2dp_sink->set_sample_rate_callback(sample_rate_callback); a2dp_sink->start((char*)cfg.name); - if (cfg.wait_for_connection){ - while(!a2dp_sink->is_connected()){ - LOGD("waiting for connection"); - delay(1000); - } - LOGI("a2dp_sink is connected..."); - } - else{ - LOGI("a2dp_sink started without connection"); + while(!a2dp_sink->is_connected()){ + LOGD("waiting for connection"); + delay(1000); } + LOGI("a2dp_sink is connected..."); is_a2dp_active = true; result = true; break; @@ -209,13 +171,6 @@ class A2DPStream : public AudioStream, public VolumeSupport { return result; } - void end() override { - if (a2dp != nullptr) { - a2dp->disconnect(); - } - AudioStream::end(); - } - /// checks if we are connected bool isConnected() { if (a2dp_source==nullptr && a2dp_sink==nullptr) return false; @@ -236,34 +191,21 @@ class A2DPStream : public AudioStream, public VolumeSupport { /// Writes the data into a temporary send buffer - where it can be picked up by the callback size_t write(const uint8_t* data, size_t len) override { LOGD("%s: %zu", LOG_METHOD, len); - if (config.mode == TX_MODE){ - // at 80% we activate the processing - if(!is_a2dp_active - && config.startup_logic == StartWhenBufferFull - && a2dp_buffer.available() >= 0.8f * a2dp_buffer.size()){ - LOGI("set active"); - is_a2dp_active = true; - } - - // blocking write: if buffer is full we wait - int timeout = config.tx_write_timeout_ms; - int wait_time = 5; - size_t free = a2dp_buffer.availableForWrite(); - while(len > free){ - LOGD("Waiting for buffer: writing %d > available %d", (int) len, (int) free); - if (timeout > 0) { - timeout -= wait_time; - if (timeout <= 0) return 0; + if (config.mode==TX_MODE){ + // if buffer is full and we are still not connected, we wait + while(len > a2dp_buffer.availableForWrite()){ + LOGI("waiting for buffer to be consumed...") + delay(200); + if (config.startLogic==StartWhenBufferFull){ + is_a2dp_active = true; } - delay(wait_time); - free = a2dp_buffer.availableForWrite(); } } // write to buffer size_t result = a2dp_buffer.writeArray(data, len); LOGD("write %d -> %d", len, result); - if (config.mode == TX_MODE){ + if (config.mode==TX_MODE){ // give the callback a chance to retrieve the data delay(config.delay_ms); } @@ -282,14 +224,12 @@ class A2DPStream : public AudioStream, public VolumeSupport { return result; } - /// Provides the number of bytes available to read int available() override { // only supported in tx mode if (config.mode!=RX_MODE) return 0; return a2dp_buffer.available(); } - /// Provides the number of bytes available to write int availableForWrite() override { // only supported in tx mode if (config.mode!=TX_MODE ) return 0; @@ -297,39 +237,21 @@ class A2DPStream : public AudioStream, public VolumeSupport { return a2dp_buffer.availableForWrite(); } - /// Define the volume (values between 0.0 and 1.0) - bool setVolume(float volume) override { - VolumeSupport::setVolume(volume); + // Define the volme (values between 0.0 and 1.0) + virtual void setVolume(float volume){ + this->volume = volume; // 128 is max volume - if (a2dp!=nullptr) a2dp->set_volume(volume * A2DP_MAX_VOL); - return true; - } - - /// Provides access to the buffer - BaseBuffer &buffer() { - return a2dp_buffer; + if (a2dp!=nullptr) a2dp->set_volume(volume * 128); } - /// Manage config.silence_on_nodata dynamically. - void setSilenceOnNoData(bool silence){ - config.silence_on_nodata = silence; - } - /// Clears the buffer - void clear(){ - // set inactive if necessary - if (config.startup_logic == StartWhenBufferFull){ - is_a2dp_active = false; - } - a2dp_buffer.clear(); - } protected: A2DPConfig config; BluetoothA2DPSource *a2dp_source = nullptr; BluetoothA2DPSink *a2dp_sink = nullptr; BluetoothA2DPCommon *a2dp=nullptr; - const int A2DP_MAX_VOL = 128; + float volume = 1.0; // auto-detect device to send audio to (TX-Mode) static bool detected_device(const char* ssid, esp_bd_addr_t address, int rssi){ @@ -341,7 +263,7 @@ class A2DPStream : public AudioStream, public VolumeSupport { static void a2dp_state_callback(esp_a2d_connection_state_t state, void *caller){ TRACED(); A2DPStream *self = (A2DPStream*)caller; - if (state==ESP_A2D_CONNECTION_STATE_CONNECTED && self->config.startup_logic==StartOnConnect){ + if (state==ESP_A2D_CONNECTION_STATE_CONNECTED && self->config.startLogic==StartOnConnect){ is_a2dp_active = true; } LOGW("==> state: %s", self->a2dp->to_str(state)); @@ -358,16 +280,10 @@ class A2DPStream : public AudioStream, public VolumeSupport { // the data in the file must be in int16 with 2 channels yield(); result_len = a2dp_buffer.readArray((uint8_t*)data, len); - - // provide silence data - if (config.silence_on_nodata && result_len == 0){ - memset(data,0, len); - result_len = len; - } } else { // prevent underflow on first call - switch (config.startup_nodata) { + switch (config.noData) { case A2DPSilence: memset(data, 0, len); break; @@ -398,17 +314,21 @@ class A2DPStream : public AudioStream, public VolumeSupport { /// notify subscriber with AudioInfo void notify_base_Info(int rate){ - AudioInfo info; - info.channels = 2; - info.bits_per_sample = 16; - info.sample_rate = rate; - notifyAudioChange(info); + if (p_notify!=nullptr){ + AudioInfo info; + info.channels = 2; + info.bits_per_sample = 16; + info.sample_rate = rate; + p_notify->setAudioInfo(info); + } } /// callback to update audio info with used a2dp sample rate static void sample_rate_callback(uint16_t rate) { A2DPStream_self->info.sample_rate = rate; - A2DPStream_self->notify_base_Info(rate); + if (A2DPStream_self->p_notify!=nullptr){ + A2DPStream_self->notify_base_Info(rate); + } } }; diff --git a/src/AudioTools/AudioLibs/AudioCmsisFFT.h b/src/AudioLibs/AudioCmsisFFT.h similarity index 75% rename from src/AudioTools/AudioLibs/AudioCmsisFFT.h rename to src/AudioLibs/AudioCmsisFFT.h index 322957f149..7cb63dfe81 100644 --- a/src/AudioTools/AudioLibs/AudioCmsisFFT.h +++ b/src/AudioLibs/AudioCmsisFFT.h @@ -1,11 +1,9 @@ #pragma once #include "AudioFFT.h" -#ifdef STM32 -# include "CMSIS_DSP.h" -#endif -#if defined(ARDUINO_ARCH_RENESAS) || defined(ARDUINO_ARCH_RP2040) -# include "arm_vec_fft.h" -#endif +//#ifdef STM32 +#include "CMSIS_DSP.h" +//#endif + /** * @defgroup fft-cmsis CMSIS * @ingroup fft @@ -47,23 +45,19 @@ class FFTDriverCmsisFFT : public FFTDriver { output_magn = nullptr; } - void setValue(int idx, float value) override{ + void setValue(int idx, int value) override{ input[idx] = value; } void fft() override { TRACED(); - arm_rfft_fast_f32(&fft_instance, input, output, false); + arm_rfft_fast_f32(&fft_instance, input, output, ifft); arm_cmplx_mag_f32(output, output_magn, len / 2); /* Calculates maxValue and returns corresponding BIN value */ arm_max_f32(output_magn, len / 2, &result_max_value, &result_index); TRACED(); }; - void rfft() override { - arm_rfft_fast_f32(&fft_instance, output, input, true); - } - float magnitude(int idx) override { return output_magn[idx]; } @@ -73,28 +67,12 @@ class FFTDriverCmsisFFT : public FFTDriver { return output_magn[idx]; } - float getValue(int idx) override { return input[idx];} - - bool setBin(int pos, float real, float img) override { - if (pos>=len) return false; - output[pos*2] = real; - output[pos*2+1] = img; - return true; - } - bool getBin(int pos, FFTBin &bin) override { - if (pos>=len) return false; - bin.real = output[pos*2]; - bin.img = output[pos*2+1]; - return true; - } - - bool isReverseFFT() override {return true;} - - bool isValid() override{ return status==ARM_MATH_SUCCESS; } + virtual bool isValid() override{ return status==ARM_MATH_SUCCESS; } arm_rfft_fast_instance_f32 fft_instance; arm_status status; int len; + bool ifft = false; float *input=nullptr; float *output_magn=nullptr; float *output=nullptr; diff --git a/src/AudioTools/AudioLibs/AudioESP32FFT.h b/src/AudioLibs/AudioESP32FFT.h similarity index 67% rename from src/AudioTools/AudioLibs/AudioESP32FFT.h rename to src/AudioLibs/AudioESP32FFT.h index 3ddcfd0d87..df49bafe7e 100644 --- a/src/AudioTools/AudioLibs/AudioESP32FFT.h +++ b/src/AudioLibs/AudioESP32FFT.h @@ -29,7 +29,7 @@ class FFTDriverESP32FFT : public FFTDriver { void end()override{ if (p_fft_object!=nullptr) fft_destroy(p_fft_object); } - void setValue(int idx, float value) override{ + void setValue(int idx, int value) override{ p_fft_object->input[idx] = value; } @@ -37,12 +37,8 @@ class FFTDriverESP32FFT : public FFTDriver { fft_execute(p_fft_object); }; - void rfft() override { - irfft(p_fft_object->input, p_fft_object->output, p_fft_object->twiddle_factors, p_fft_object->size); - } - float magnitude(int idx) override { - return sqrt(magnitudeFast(idx)); + return sqrt(pow(p_fft_object->output[2*idx],2) + pow(p_fft_object->output[2*idx+1],2)); } /// magnitude w/o sqrt @@ -50,24 +46,7 @@ class FFTDriverESP32FFT : public FFTDriver { return (pow(p_fft_object->output[2*idx],2) + pow(p_fft_object->output[2*idx+1],2)); } - float getValue(int idx) { return p_fft_object->input[idx];} - - bool setBin(int pos, float real, float img) override { - if (pos>=len) return false; - p_fft_object->output[2*pos] = real; - p_fft_object->output[2*pos+1] = img; - return true; - } - bool getBin(int pos, FFTBin &bin) override { - if (pos>=len) return false; - bin.real = p_fft_object->output[2*pos]; - bin.img = p_fft_object->output[2*pos+1]; - return true; - } - - bool isReverseFFT() override {return true;} - - bool isValid() override{ return p_fft_object!=nullptr; } + virtual bool isValid() override{ return p_fft_object!=nullptr; } fft_config_t *p_fft_object=nullptr; int len; diff --git a/src/AudioTools/AudioLibs/AudioESP32ULP.h b/src/AudioLibs/AudioESP32ULP.h similarity index 98% rename from src/AudioTools/AudioLibs/AudioESP32ULP.h rename to src/AudioLibs/AudioESP32ULP.h index e588d9dbf4..c87c93f22c 100644 --- a/src/AudioTools/AudioLibs/AudioESP32ULP.h +++ b/src/AudioLibs/AudioESP32ULP.h @@ -12,17 +12,12 @@ #ifndef ESP32 #error Only the ESP32 supports ULP audio output #endif -#include "AudioLogger.h" -#include "AudioTools/CoreAudio/AudioTypes.h" -#include "AudioTools/CoreAudio/AudioOutput.h" + #include #include #include #include #include -#include "soc/rtc_io_reg.h" - -namespace audio_tools { enum UlpDac { ULP_DAC1 = 1, ULP_DAC2 = 2 }; @@ -92,7 +87,7 @@ class AudioESP32ULP : public AudioOutput { return result < min_write_bytes ? 0 : result; } - void end() { + bool end() { TRACEI(); const ulp_insn_t stopulp[] = {// stop the timer I_END(), @@ -112,10 +107,12 @@ class AudioESP32ULP : public AudioOutput { if (activeDACs & 2) { dac_output_voltage(DAC_CHANNEL_2, 128); } + + return true; } -protected: +private: int lastFilledWord = 0; int hertz; int min_write_bytes = 128; @@ -359,5 +356,3 @@ class AudioESP32ULP : public AudioOutput { return (uint32_t)(recover_ins.ulp_bin); } }; - -} \ No newline at end of file diff --git a/src/AudioTools/AudioLibs/AudioESP8266.h b/src/AudioLibs/AudioESP8266.h similarity index 80% rename from src/AudioTools/AudioLibs/AudioESP8266.h rename to src/AudioLibs/AudioESP8266.h index 80ac73aba9..88883fd8b3 100644 --- a/src/AudioTools/AudioLibs/AudioESP8266.h +++ b/src/AudioLibs/AudioESP8266.h @@ -1,9 +1,9 @@ #pragma once -#include "AudioToolsConfig.h" -#include "AudioTools/CoreAudio/AudioOutput.h" -#include "AudioTools/CoreAudio/AudioStreams.h" -#include "AudioTools/CoreAudio/Buffers.h" +#include "AudioConfig.h" +#include "AudioTools/AudioOutput.h" +#include "AudioTools/AudioStreams.h" +#include "AudioTools/Buffers.h" #include "AudioOutput.h" #include "SoundData.h" @@ -21,7 +21,7 @@ namespace audio_tools { * @author Phil Schatzmann * @copyright GPLv3 */ -class AudioOutputWithCallback : public ::AudioOutput, public BufferedStream { +class AudioOutputWithCallback : public AudioOutput, public BufferedStream { public: // Default constructor AudioOutputWithCallback(int bufferSize, int bufferCount) @@ -76,16 +76,16 @@ class AudioOutputWithCallback : public ::AudioOutput, public BufferedStream { */ class ESP3288AudioOutput : public AudioStream { public: - ESP3288AudioOutput(::AudioOutput &out, int channels) { + ESP3288AudioOutput(AudioOutput &out, int channels) { p_out = &out; this->channels = channels; } - virtual size_t write(const uint8_t *data, size_t len) { + virtual size_t write(const uint8_t *buffer, size_t size) { size_t result = 0; - int16_t *v = (int16_t *)data; + int16_t *v = (int16_t *)buffer; if (channels == 2) { - result = p_out->ConsumeSamples(v, len / 2); + result = p_out->ConsumeSamples(v, size / 2); } else { LOGE("Only 2 Channels are supported"); result = 0; @@ -94,7 +94,7 @@ class ESP3288AudioOutput : public AudioStream { } protected: - ::AudioOutput *p_out = nullptr; + AudioOutput *p_out = nullptr; int channels; }; } // namespace audio_tools diff --git a/src/AudioTools/AudioLibs/AudioEffectsSuite.h b/src/AudioLibs/AudioEffectsSuite.h similarity index 97% rename from src/AudioTools/AudioLibs/AudioEffectsSuite.h rename to src/AudioLibs/AudioEffectsSuite.h index 877697c21c..ac10e5a649 100644 --- a/src/AudioTools/AudioLibs/AudioEffectsSuite.h +++ b/src/AudioLibs/AudioEffectsSuite.h @@ -20,12 +20,7 @@ #include #include #include -#include "AudioTools/CoreAudio/AudioEffects/AudioEffect.h" -#include "AudioTools/CoreAudio/AudioStreams.h" - -#ifndef PI -# define PI 3.141592653589793f -#endif +#include "AudioEffects/AudioEffect.h" namespace audio_tools { @@ -39,7 +34,7 @@ static effectsuite_t **interpolationTable = nullptr; /** * @brief Base Class for Effects - * @ingroup effects + * */ class EffectSuiteBase : public AudioEffect { @@ -68,7 +63,6 @@ class EffectSuiteBase : public AudioEffect { * to modulate The parameters of another effect. Class initialised with sample * rate. * @author Matthew Hamilton - * @ingroup effects * @copyright MIT License */ class ModulationBaseClass { @@ -366,7 +360,7 @@ class ModulationBaseClass { /** * @brief SoundGenerator using the ModulationBaseClass * to generate the samples. - * @ingroup effects + * * @tparam T */ template @@ -397,7 +391,6 @@ class ModulationBaseClass { * @version 0.1 * @see DelayEffectBase * @author Matthew Hamilton - * @ingroup effects * @copyright MIT License */ class DelayEffectBase { @@ -503,14 +496,14 @@ class DelayEffectBase { return interpTable; } - // void printInterpTable() { - // for (int j = 0; j < interpResolution; j++) { - // for (int i = 0; i < interpOrder; i++) { - // printf("index %d: %.2f \t", i, interpolationTable[i][j]); - // } - // printf("\n"); - // } - // } + void printInterpTable() { + for (int j = 0; j < interpResolution; j++) { + for (int i = 0; i < interpOrder; i++) { + printf("index %d: %.2f \t", i, interpolationTable[i][j]); + } + printf("\n"); + } + } protected: /** @@ -632,7 +625,6 @@ class DelayEffectBase { * high, low and band pass filtering * @see FilterEffectBase * @author Matthew Hamilton - * @ingroup effects * @copyright MIT License */ class FilterEffectBase : public EffectSuiteBase { @@ -683,21 +675,21 @@ class FilterEffectBase : public EffectSuiteBase { */ effectsuite_t envelope(effectsuite_t sample) { return applyFilter(rms(sample)); } - // void printBuffers() { - // printf("FIRb\t\tIIRb\n"); - // for (int i = 0; i < filterOrder; i++) { - // printf("%.4e\t%.4e\n", firBuffer[i], iirBuffer[i]); - // } - // printf("\n"); - // } - - // void printCoefs() { - // printf("FIR\t\tIIR\n"); - // for (int i = 0; i < filterOrder; i++) { - // printf("%.4e\t%.4e\n", firCoefficients[i], iirCoefficients[i]); - // } - // printf("\n"); - // } + void printBuffers() { + printf("FIRb\t\tIIRb\n"); + for (int i = 0; i < filterOrder; i++) { + printf("%.4e\t%.4e\n", firBuffer[i], iirBuffer[i]); + } + printf("\n"); + } + + void printCoefs() { + printf("FIR\t\tIIR\n"); + for (int i = 0; i < filterOrder; i++) { + printf("%.4e\t%.4e\n", firCoefficients[i], iirCoefficients[i]); + } + printf("\n"); + } /** changes the current Chebyshev type 1 coefficients without altering the * filter order. This allows for use in an audio process thread as it avoids @@ -733,8 +725,8 @@ class FilterEffectBase : public EffectSuiteBase { Kx = 1; } - const effectsuite_t T = 2.0f * tan(.5f); - const effectsuite_t W = 2.0f * PI * cutFreq; + const effectsuite_t T = 2 * tan(.5); + const effectsuite_t W = 2 * pi * cutFreq; effectsuite_t K; @@ -750,7 +742,7 @@ class FilterEffectBase : public EffectSuiteBase { ////// main algorithm for (int i = 0; i < (order / 2); i++) { ////// Sub routine - const effectsuite_t alpha = PI / (2 * poles) + (i - 1) * (PI / poles); + const effectsuite_t alpha = pi / (2 * poles) + (i - 1) * (pi / poles); effectsuite_t Rp, Ip; if (ripple != 0) { @@ -961,6 +953,7 @@ class FilterEffectBase : public EffectSuiteBase { return rmsValue; } +public: protected: /** Numerator coefficients in delay filter firCoefficients[0] z^0 coeffcieint @@ -994,12 +987,13 @@ class FilterEffectBase : public EffectSuiteBase { /** RMS window buffer */ effectsuite_t *rmsBuffer = new effectsuite_t[rmsWindowSize]; +protected: // variables + constexpr static const effectsuite_t pi = 3.141592653589793; }; /** * @brief SimpleLPF * @author Matthew Hamilton - * @ingroup effects * @copyright MIT License */ class SimpleLPF : public FilterEffectBase { @@ -1518,7 +1512,7 @@ class SimpleFlanger : public DelayEffectBase, public EffectSuiteBase { **/ void setAngleDelta() { const effectsuite_t cyclesPerSample = modulationRate * timeStep; - angleDelta = cyclesPerSample * 2.0f * PI; + angleDelta = cyclesPerSample * 2.0 * internal_Pi; } /** @@ -1535,6 +1529,9 @@ class SimpleFlanger : public DelayEffectBase, public EffectSuiteBase { } protected: + /** internal class declaration of pi it would likely make sense to have this + * moved to a higher class */ + constexpr static const effectsuite_t internal_Pi = 3.141592653589793; effectsuite_t modulationDepth = 1000, modulationRate = 0, effectGain = .01; @@ -1548,13 +1545,12 @@ class SimpleFlanger : public DelayEffectBase, public EffectSuiteBase { // const effectsuite_t cyclesPerSample = modulationRate * timeStep; /**increment value for modulation signal*/ - effectsuite_t angleDelta = 2.0f * PI * timeStep; + effectsuite_t angleDelta = 2 * internal_Pi * timeStep; }; /** * @brief EnvelopeFilter * @author Matthew Hamilton - * @ingroup effects * @copyright MIT License */ class EnvelopeFilter : public FilterEffectBase { @@ -1589,5 +1585,4 @@ class EnvelopeFilter : public FilterEffectBase { SimpleLPF envelopeFollower; }; -} // namespace effectsuite_tools - +} // namespace effectsuite_tools \ No newline at end of file diff --git a/src/AudioLibs/AudioEspressifFFT.h b/src/AudioLibs/AudioEspressifFFT.h new file mode 100644 index 0000000000..f11de72d54 --- /dev/null +++ b/src/AudioLibs/AudioEspressifFFT.h @@ -0,0 +1,107 @@ +#pragma once + +#include "AudioFFT.h" +#include "esp_dsp.h" + +/** + * @defgroup fft-dsp esp32-dsp + * @ingroup fft + * @brief FFT using esp32 esp-dsp library +**/ + +namespace audio_tools { + +/** + * @brief fft Driver for espressif dsp library: https://espressif-docs.readthedocs-hosted.com/projects/esp-dsp/en/latest/esp-dsp-apis.html + * @ingroup fft-dsp + * @author Phil Schatzmann + * @copyright GPLv3 + */ +class FFTDriverEspressifFFT : public FFTDriver { + public: + bool begin(int len) override { + N = len; + if (p_data==nullptr){ + p_data = new float[len*2]; + if (p_data==nullptr){ + LOGE("not enough memory"); + } + } + assert(p_data!=nullptr); + ret = dsps_fft2r_init_fc32(NULL, CONFIG_DSP_MAX_FFT_SIZE); + if (ret != ESP_OK){ + LOGE("dsps_fft2r_init_fc32 %d", ret); + } + return p_data!=nullptr && ret == ESP_OK; + } + + void end() override { + dsps_fft2r_deinit_fc32(); + if (p_data==nullptr){ + delete p_data; + p_data = nullptr; + } + } + + void setValue(int idx, int value) override { + if (idxp_data; + } + + FFTDriverEspressifFFT* driverEx() { + return (FFTDriverEspressifFFT*)driver(); + } +}; + + +} \ No newline at end of file diff --git a/src/AudioLibs/AudioFFT.h b/src/AudioLibs/AudioFFT.h new file mode 100644 index 0000000000..02d55deeab --- /dev/null +++ b/src/AudioLibs/AudioFFT.h @@ -0,0 +1,392 @@ +#pragma once + +#include "AudioTools/AudioOutput.h" +#include "AudioLibs/FFT/FFTWindows.h" + +/** + * @defgroup fft FFT + * @ingroup dsp + * @brief Fast Fourier Transform +**/ + +namespace audio_tools { + +// forward declaration +class AudioFFTBase; +MusicalNotes AudioFFTNotes; + +/** + * @brief Result of the FFT + * @ingroup fft +*/ +struct AudioFFTResult { + int bin; + float magnitude; + float frequency; + + int frequencyAsInt(){ + return round(frequency); + } + const char* frequencyAsNote() { + return AudioFFTNotes.note(frequency); + } + const char* frequencyAsNote(float &diff) { + return AudioFFTNotes.note(frequency, diff); + } +}; + +/** + * @brief Configuration for AudioFFT. If there are more then 1 channel the + * channel_used is defining which channel is used to perform the fft on. + * @ingroup fft + */ +struct AudioFFTConfig : public AudioInfo { + AudioFFTConfig(){ + channels = 2; + bits_per_sample = 16; + sample_rate = 44100; + } + /// Callback method which is called after we got a new result + void (*callback)(AudioFFTBase &fft) = nullptr; + /// Channel which is used as input + uint8_t channel_used = 0; + int length=8192; + int stride=0; + /// Optional window function + WindowFunction *window_function = nullptr; +}; + +/** + * @brief Abstract Class which defines the basic FFT functionality + * @ingroup fft + * @author Phil Schatzmann + * @copyright GPLv3 + */ +class FFTDriver { + public: + virtual bool begin(int len) =0; + virtual void end() =0; + virtual void setValue(int pos, int value) =0; + virtual void fft() = 0; + virtual float magnitude(int idx) = 0; + virtual float magnitudeFast(int idx) = 0; + virtual bool isValid() = 0; +}; + +/** + * @brief Executes FFT using audio data. The Driver which is passed in the constructor selects a specifc FFT implementation. + * @ingroup fft + * @author Phil Schatzmann + * @copyright GPLv3 + */ +class AudioFFTBase : public AudioOutput { + public: + /// Default Constructor. The len needs to be of the power of 2 (e.g. 512, 1024, 2048, 4096, 8192) + AudioFFTBase(FFTDriver* driver){ + p_driver = driver; + } + + ~AudioFFTBase() { + end(); + } + + /// Provides the default configuration + AudioFFTConfig defaultConfig() { + AudioFFTConfig info; + return info; + } + + /// starts the processing + bool begin(AudioFFTConfig info) { + cfg = info; + bins = cfg.length/2; + if (!isPowerOfTwo(cfg.length)){ + LOGE("Len must be of the power of 2: %d", cfg.length); + return false; + } + if (cfg.stride>0 && cfg.stridebegin(cfg.length)){ + LOGE("Not enough memory"); + } + if (cfg.window_function!=nullptr){ + cfg.window_function->begin(length()); + } + + current_pos = 0; + return p_driver->isValid(); + } + + /// Just resets the current_pos e.g. to start a new cycle + void reset(){ + current_pos = 0; + if (cfg.window_function!=nullptr){ + cfg.window_function->begin(length()); + } + } + + operator bool() { + return p_driver!=nullptr && p_driver->isValid(); + } + + /// Notify change of audio information + void setAudioInfo(AudioInfo info) override { + cfg.bits_per_sample = info.bits_per_sample; + cfg.sample_rate = info.sample_rate; + cfg.channels = info.channels; + begin(cfg); + } + + /// Release the allocated memory + void end() { + p_driver->end(); + if (p_magnitudes!=nullptr) delete []p_magnitudes; + } + + /// Provide the audio data as FFT input + size_t write(const uint8_t*data, size_t len) override { + size_t result = 0; + if (p_driver->isValid()){ + result = len; + switch(cfg.bits_per_sample){ + case 16: + processSamples(data, len/2); + break; + case 24: + processSamples(data, len/3); + break; + case 32: + processSamples(data, len/4); + break; + default: + LOGE("Unsupported bits_per_sample: %d",cfg.bits_per_sample); + break; + } + } + return result; + } + + /// We try to fill the buffer at once + int availableForWrite() override { + return cfg.bits_per_sample/8*cfg.length; + } + + /// The number of bins used by the FFT which are relevant for the result + int size() { + return bins; + } + + /// The number of samples + int length() { + return cfg.length; + } + + /// time after the fft: time when the last result was provided - you can poll this to check if we have a new result + unsigned long resultTime() { + return timestamp; + } + /// time before the fft + unsigned long resultTimeBegin() { + return timestamp_begin; + } + + /// Determines the frequency of the indicated bin + float frequency(int bin){ + if (bin>=bins){ + LOGE("Invalid bin %d", bin); + return 0; + } + return static_cast(bin) * cfg.sample_rate / cfg.length; + } + + /// Determines the result values in the max magnitude bin + AudioFFTResult result() { + AudioFFTResult ret_value; + ret_value.magnitude = 0; + ret_value.bin = 0; + // find max value and index + for (int j=0;jret_value.magnitude){ + ret_value.magnitude = m; + ret_value.bin = j; + } + } + ret_value.frequency = frequency(ret_value.bin); + return ret_value; + } + + + /// Determines the N biggest result values + template + void resultArray(AudioFFTResult (&result)[N]){ + // initialize to negative value + for (int j=0;j(result, act); + } + } + + /// provides access to the FFTDriver which implements the basic FFT functionality + FFTDriver *driver() { + return p_driver; + } + + /// Calculates the magnitude of the fft result to determine the max value (bin is 0 to size()) + float magnitude(int bin){ + if (bin>=bins){ + LOGE("Invalid bin %d", bin); + return 0; + } + return p_driver->magnitude(bin); + } + + float magnitudeFast(int bin){ + if (bin>=bins){ + LOGE("Invalid bin %d", bin); + return 0; + } + return p_driver->magnitudeFast(bin); + } + /// Provides the magnitudes as array of size size(). Please note that this method is allocating additinal memory! + float* magnitudes() { + if (p_magnitudes==nullptr){ + p_magnitudes = new float[size()]; + } + for (int j=0;j stride_buffer{0}; + float *p_magnitudes = nullptr; + int bins = 0; + + + // Add samples to input data p_x - and process them if full + template + void processSamples(const void *data, size_t samples) { + T *dataT = (T*) data; + T sample; + float sample_windowed; + for (int j=0; jsetValue(current_pos, windowedSample(sample)); + writeStrideBuffer((uint8_t*)&sample, sizeof(T)); + if (++current_pos>=cfg.length){ + // perform FFT + fft(); + + // reprocess data in stride buffer + if (stride_buffer.size()>0){ + // reload data from stride buffer + while (stride_buffer.available()){ + T sample; + stride_buffer.readArray((uint8_t*)&sample, sizeof(T)); + p_driver->setValue(current_pos, windowedSample(sample)); + current_pos++; + } + } + + } + } + } + + template + T windowedSample(T sample){ + T result = sample; + if (cfg.window_function!=nullptr){ + result = cfg.window_function->factor(current_pos) * sample; + } + return result; + } + + template + void fft() { + timestamp_begin = millis(); + p_driver->fft(); + timestamp = millis(); + if (cfg.callback!=nullptr){ + cfg.callback(*this); + } + current_pos = 0; + } + + int bytesPerSample() { + return cfg.bits_per_sample / 8; + } + + /// make sure that we do not reuse already found results + template + void insertSorted(AudioFFTResult(&result)[N], AudioFFTResult tmp){ + // find place where we need to insert new record + for (int j=0;jresult[j].magnitude){ + // shift existing values right + for (int i=N-2;i>=j;i--){ + result[i+1] = result[i]; + } + // insert new value + result[j]=tmp; + // stop after we found the correct index + break; + } + } + } + + void writeStrideBuffer(uint8_t* buffer, size_t len){ + if (stride_buffer.size()>0){ + int available = stride_buffer.availableForWrite(); + if (len>available){ + // clear oldest values to make space + int diff = len-available; + for(int j=0;jcfg = cfg; - this->bytes_per_sample = cfg.bits_per_sample / 8; - this->bytes_per_frame = bytes_per_sample * cfg.channels; - this->float_to_int_factor = NumberConverter::maxValue(cfg.bits_per_sample); + this->bytes_per_sample = cfg.bits_per_sample/8; if (p_dsp==nullptr){ #ifdef USE_MEMORY_MANAGER @@ -121,43 +119,35 @@ class FaustStream : public AudioStream { int samples = len / bytes_per_sample; allocateFloatBuffer(samples, false); p_dsp->compute(samples, nullptr, p_buffer); - // convert from float to int - switch(cfg.bits_per_sample){ - case 8: - convertFloatBufferToInt(samples, p_buffer, data); - break; - case 16: - convertFloatBufferToInt(samples, p_buffer, data); - break; - case 24: - convertFloatBufferToInt(samples, p_buffer, data); - break; - case 32: - convertFloatBufferToInt(samples, p_buffer, data); - break; - default: - TRACEE(); - } + // convert from float to int16 + convertFloatBufferToInt16(samples, data, p_buffer); } return result; } /// Used if FaustStream is used as audio sink or filter - size_t write(const uint8_t *data, size_t len) override { - LOGD("FaustStream::write: %d", len); - switch(cfg.bits_per_sample){ - case 8: - return writeT(data, len); - case 16: - return writeT(data, len); - case 24: - return writeT(data, len); - case 32: - return writeT(data, len); - default: - TRACEE(); + size_t write(const uint8_t *write_data, size_t len) override { + size_t result = 0; + if (is_write){ + TRACED(); + int samples = len / bytes_per_sample; + allocateFloatBuffer(samples, with_output_buffer); + int16_t *data16 = (int16_t*) write_data; + // convert to float + int frameCount = samples/cfg.channels; + for(int j=0;j(data16[(j*cfg.channels)+i])/32767.0; + } + } + FAUSTFLOAT** p_float_out = with_output_buffer ? p_buffer_out : p_buffer; + p_dsp->compute(samples, p_buffer, p_float_out); + // update buffer with data from faust + convertFloatBufferToInt16(samples,(void*) write_data, p_float_out); + // write data to final output + result = p_out->write(write_data, len); } - return 0; + return result; } int available() override { @@ -165,7 +155,7 @@ class FaustStream : public AudioStream { } int availableForWrite() override { - return DEFAULT_BUFFER_SIZE / bytes_per_frame; // we limit the write size + return DEFAULT_BUFFER_SIZE / 4; // we limit the write size to } /// Determines the value of a parameter @@ -227,9 +217,7 @@ class FaustStream : public AudioStream { bool gate_exists = false; bool with_output_buffer; int bytes_per_sample; - int bytes_per_frame; int buffer_allocated; - float float_to_int_factor = 32767; DSP *p_dsp = nullptr; AudioInfo cfg; Print *p_out=nullptr; @@ -271,63 +259,17 @@ class FaustStream : public AudioStream { return result; } - /// Converts the float buffer to int values - template - void convertFloatBufferToInt(int samples, FAUSTFLOAT**p_float_in, void *data_out){ - T *dataT = (T*) data_out; + /// Converts the float buffer to int16 values + void convertFloatBufferToInt16(int samples, void *data, FAUSTFLOAT**p_float_out){ + int16_t *data16 = (int16_t*) data; int frameCount = samples/cfg.channels; - for (int j=0; j 1.0f){ - sample = 1.0f; - } - if(sample < -1.0f){ - sample = -1.0f; - } - dataT[(j*cfg.channels)+i] = sample * float_to_int_factor; + data16[(j*cfg.channels)+i]=p_float_out[i][j]*32767; } } } - /// Converts the int buffer to float values - template - void convertIntBufferToFloat(int samples, void *data_in, FAUSTFLOAT**p_float_out ){ - T *dataT = (T*) data_in; - int frameCount = samples/cfg.channels; - for(int j=0;j(dataT[(j*cfg.channels)+i]) / float_to_int_factor; - } - } - } - - /// Used if FaustStream is used as audio sink or filter - template - size_t writeT(const uint8_t *write_data, size_t len) { - size_t result = 0; - if (is_write){ - TRACED(); - int samples = len / bytes_per_sample; - int frames = samples / cfg.channels; - // prepare float input for faust - allocateFloatBuffer(samples, with_output_buffer); - convertIntBufferToFloat(samples, (void*) write_data, p_buffer); - - // determine result - FAUSTFLOAT** p_float_buffer = with_output_buffer ? p_buffer_out : p_buffer; - p_dsp->compute(frames, p_buffer, p_float_buffer); - - // update buffer with data from faust - convertFloatBufferToInt(samples, p_float_buffer, (void*) write_data); - // write data to final output - result = p_out->write(write_data, len); - } - return result; - } - - /// Allocate the buffer that is needed by faust void allocateFloatBuffer(int samples, bool allocate_out){ if (samples>buffer_allocated){ diff --git a/src/AudioTools/AudioLibs/AudioFaustDSP.h b/src/AudioLibs/AudioFaustDSP.h similarity index 95% rename from src/AudioTools/AudioLibs/AudioFaustDSP.h rename to src/AudioLibs/AudioFaustDSP.h index 3d7a92678e..b5b02af33e 100644 --- a/src/AudioTools/AudioLibs/AudioFaustDSP.h +++ b/src/AudioLibs/AudioFaustDSP.h @@ -1,9 +1,8 @@ #pragma once -#include "AudioToolsConfig.h" -#include "AudioTools/CoreAudio/AudioBasic/Collections.h" -#include "AudioTools/CoreAudio/AudioBasic/Float16.h" -#include "AudioTools/CoreAudio/AudioBasic/StrView.h" +#include "AudioConfig.h" +#include "AudioBasic/Collections.h" +#include "AudioBasic/Float16.h" #ifndef FAUSTFLOAT #define FAUSTFLOAT float @@ -145,10 +144,10 @@ class UI { } protected: - audio_tools::Vector entries; + Vector entries; Entry *findEntry(const char* name){ - StrView nameStr(name); + Str nameStr(name); for (int j=0; jcount = count; total = 0; - return true; } /** diff --git a/src/AudioLibs/AudioKissFFT.h b/src/AudioLibs/AudioKissFFT.h new file mode 100644 index 0000000000..a4284582be --- /dev/null +++ b/src/AudioLibs/AudioKissFFT.h @@ -0,0 +1,81 @@ +#pragma once + +#include "kiss_fft.h" +#include "AudioFFT.h" + +/** + * @defgroup fft-kiss KISS + * @ingroup fft + * @brief FFT using KISS +**/ + + +namespace audio_tools { + +/** + * @brief Driver for RealFFT + * @ingroup fft-kiss + * @author Phil Schatzmann + * @copyright GPLv3 + */ +class FFTDriverKissFFT : public FFTDriver { + public: + bool begin(int len) override { + if (p_fft_object==nullptr) p_fft_object = kiss_fft_alloc(len,0,nullptr,nullptr); + if (p_data==nullptr) p_data = new kiss_fft_cpx[len]; + assert(p_fft_object!=nullptr); + assert(p_data!=nullptr); + return p_fft_object!=nullptr && p_data!=nullptr; + } + + void end() override { + if (p_fft_object!=nullptr) kiss_fft_free(p_fft_object); + if (p_data!=nullptr) delete[] p_data; + p_fft_object = nullptr; + p_data = nullptr; + } + void setValue(int idx, int value) override { + p_data[idx].r = value; + } + + void fft() override { + kiss_fft (p_fft_object, p_data, p_data); + }; + + float magnitude(int idx) override { + return sqrt(p_data[idx].r * p_data[idx].r + p_data[idx].i * p_data[idx].i); + } + + /// magnitude w/o sqrt + float magnitudeFast(int idx) override { + return (p_data[idx].r * p_data[idx].r + p_data[idx].i * p_data[idx].i); + } + + virtual bool isValid() override{ return p_fft_object!=nullptr; } + + kiss_fft_cfg p_fft_object=nullptr; + kiss_fft_cpx *p_data = nullptr; // real + +}; +/** + * @brief AudioFFT using FFTReal. The only specific functionality is the access to the dataArray + * @ingroup fft-kiss + * @author Phil Schatzmann + * @copyright GPLv3 + */ +class AudioKissFFT : public AudioFFTBase { + public: + AudioKissFFT():AudioFFTBase(new FFTDriverKissFFT()) {} + + /// Provides the complex array returned by the FFT + kiss_fft_cpx *dataArray() { + return driverEx()->p_data; + } + + FFTDriverKissFFT* driverEx() { + return (FFTDriverKissFFT*)driver(); + } +}; + + +} \ No newline at end of file diff --git a/src/AudioTools/AudioLibs/AudioKit.h b/src/AudioLibs/AudioKit.h similarity index 92% rename from src/AudioTools/AudioLibs/AudioKit.h rename to src/AudioLibs/AudioKit.h index bf359984fb..cde3134cbc 100644 --- a/src/AudioTools/AudioLibs/AudioKit.h +++ b/src/AudioLibs/AudioKit.h @@ -2,8 +2,8 @@ #include "AudioTools.h" #include "AudioKitHAL.h" -#include "AudioTools/CoreAudio/AudioI2S/I2SConfig.h" -#include "AudioTools/CoreAudio/AudioActions.h" +#include "AudioI2S/I2SConfig.h" +#include "AudioTools/AudioActions.h" #ifndef AUDIOKIT_V1 #error Upgrade the AudioKit library @@ -12,7 +12,7 @@ namespace audio_tools { class AudioKitStream; -static AudioKitStream *pt_AudioKitStream = nullptr; +AudioKitStream *pt_AudioKitStream = nullptr; /** * @brief Configuration for AudioKitStream: we use as subclass of I2SConfig @@ -25,7 +25,7 @@ class AudioKitStreamConfig : public I2SConfig { friend class AudioKitStream; public: - AudioKitStreamConfig(RxTxMode mode=RXTX_MODE) { setupI2SPins(mode); }; + AudioKitStreamConfig() { setupI2SPins(); }; // set adc channel with audio_hal_adc_input_t audio_hal_adc_input_t input_device = AUDIOKIT_DEFAULT_INPUT; // set dac channel @@ -71,10 +71,9 @@ friend class AudioKitStream; board_driver board; /// Defines the pins based on the information provided by the AudioKit project - void setupI2SPins(RxTxMode rxtx_mode) { + void setupI2SPins() { TRACED(); - this->rx_tx_mode = rxtx_mode; - i2s_pin_config_t i2s_pins = {}; + i2s_pin_config_t i2s_pins; board.setup(pins); board.get_i2s_pins((i2s_port_t)port_no, &i2s_pins); pin_mck = i2s_pins.mck_io_num; @@ -92,8 +91,8 @@ friend class AudioKitStream; // convert to audio_hal_iface_samples_t audio_hal_iface_bits_t toBits() { TRACED(); - static const int ia[] = {16, 24, 32}; - static const audio_hal_iface_bits_t oa[] = {AUDIO_HAL_BIT_LENGTH_16BITS, + const static int ia[] = {16, 24, 32}; + const static audio_hal_iface_bits_t oa[] = {AUDIO_HAL_BIT_LENGTH_16BITS, AUDIO_HAL_BIT_LENGTH_24BITS, AUDIO_HAL_BIT_LENGTH_32BITS}; for (int j = 0; j < 3; j++) { @@ -109,9 +108,9 @@ friend class AudioKitStream; /// Convert to audio_hal_iface_samples_t audio_hal_iface_samples_t toSampleRate() { TRACED(); - static const int ia[] = {8000, 11025, 16000, 22050, + const static int ia[] = {8000, 11025, 16000, 22050, 24000, 32000, 44100, 48000}; - static const audio_hal_iface_samples_t oa[] = { + const static audio_hal_iface_samples_t oa[] = { AUDIO_HAL_08K_SAMPLES, AUDIO_HAL_11K_SAMPLES, AUDIO_HAL_16K_SAMPLES, AUDIO_HAL_22K_SAMPLES, AUDIO_HAL_24K_SAMPLES, AUDIO_HAL_32K_SAMPLES, AUDIO_HAL_44K_SAMPLES, AUDIO_HAL_48K_SAMPLES}; @@ -122,7 +121,7 @@ friend class AudioKitStream; LOGD("-> %d",ia[j]) return oa[j]; } else { - int new_diff = abs((int)(oa[j] - sample_rate)); + int new_diff = abs(oa[j] - sample_rate); if (new_diff < diff) { result = j; diff = new_diff; @@ -136,14 +135,14 @@ friend class AudioKitStream; /// Convert to audio_hal_iface_format_t audio_hal_iface_format_t toFormat() { TRACED(); - static const int ia[] = {I2S_STD_FORMAT, + const static int ia[] = {I2S_STD_FORMAT, I2S_LSB_FORMAT, I2S_MSB_FORMAT, I2S_PHILIPS_FORMAT, I2S_RIGHT_JUSTIFIED_FORMAT, I2S_LEFT_JUSTIFIED_FORMAT, I2S_PCM}; - static const audio_hal_iface_format_t oa[] = { + const static audio_hal_iface_format_t oa[] = { AUDIO_HAL_I2S_NORMAL, AUDIO_HAL_I2S_LEFT, AUDIO_HAL_I2S_RIGHT, AUDIO_HAL_I2S_NORMAL, AUDIO_HAL_I2S_RIGHT, AUDIO_HAL_I2S_LEFT, AUDIO_HAL_I2S_DSP}; @@ -193,7 +192,7 @@ class AudioKitStream : public AudioStream { /// Provides the default configuration AudioKitStreamConfig defaultConfig(RxTxMode mode = RXTX_MODE) { TRACED(); - AudioKitStreamConfig result{mode}; + AudioKitStreamConfig result; result.rx_tx_mode = mode; return result; } @@ -204,7 +203,7 @@ class AudioKitStream : public AudioStream { cfg = config; AudioStream::setAudioInfo(config); - cfg.logInfo("AudioKitStream"); + cfg.logInfo(); // start codec auto kit_cfg = cfg.toAudioKitConfig(); @@ -245,18 +244,18 @@ class AudioKitStream : public AudioStream { return cfg.rx_tx_mode == TX_MODE ? 0 : DEFAULT_BUFFER_SIZE; } - size_t write(const uint8_t *data, size_t len) override { - return i2s_stream.write(data, len); + virtual size_t write(const uint8_t *data, size_t length) override { + return i2s_stream.write(data, length); } /// Reads the audio data - size_t readBytes(uint8_t *data, size_t len) override { - return i2s_stream.readBytes(data, len); + virtual size_t readBytes(uint8_t *data, size_t length) override { + return i2s_stream.readBytes(data, length); } /// Update the audio info with new values: e.g. new sample_rate, /// bits_per_samples or channels. - void setAudioInfo(AudioInfo info) override { + virtual void setAudioInfo(AudioInfo info) { TRACEI(); if (cfg.sample_rate != info.sample_rate @@ -264,8 +263,8 @@ class AudioKitStream : public AudioStream { && cfg.channels == info.channels && is_started) { // update sample rate only - LOGW("Update sample rate: %d", info.sample_rate); cfg.sample_rate = info.sample_rate; + cfg.logInfo(); i2s_stream.setAudioInfo(cfg); kit.setSampleRate(cfg.toSampleRate()); } else if (cfg.sample_rate != info.sample_rate @@ -276,7 +275,7 @@ class AudioKitStream : public AudioStream { cfg.sample_rate = info.sample_rate; cfg.bits_per_sample = info.bits_per_sample; cfg.channels = info.channels; - cfg.logInfo("AudioKit"); + cfg.logInfo(); // Stop first if(is_started){ @@ -569,7 +568,7 @@ class AudioKitStream : public AudioStream { protected: AudioKit kit; I2SStream i2s_stream; - AudioKitStreamConfig cfg = defaultConfig(RXTX_MODE); + AudioKitStreamConfig cfg; AudioActions actions; int volume_value = 40; bool active = true; @@ -620,7 +619,7 @@ class AudioKitStream : public AudioStream { LOGW("Headphone detection ignored because of conflict: %d ",kit.pinHeadphoneDetect()); } - // pin conflicts with SD Lyrat SD CS GpioPinand buttons / Conflict on Audiokit V. 2957 + // pin conflicts with SD Lyrat SD CS Pin and buttons / Conflict on Audiokit V. 2957 if (! (cfg.sd_active && (AUDIOKIT_BOARD==1 || AUDIOKIT_BOARD==7))){ LOGD("actionVolumeDown") addAction(kit.pinVolumeDown(), actionVolumeDown); diff --git a/src/AudioTools/AudioLibs/AudioMP34DT05.h b/src/AudioLibs/AudioMP34DT05.h similarity index 94% rename from src/AudioTools/AudioLibs/AudioMP34DT05.h rename to src/AudioLibs/AudioMP34DT05.h index 89c65f2257..e0ac940f44 100644 --- a/src/AudioTools/AudioLibs/AudioMP34DT05.h +++ b/src/AudioLibs/AudioMP34DT05.h @@ -1,5 +1,5 @@ #pragma once -#include "AudioTools/CoreAudio/AudioStreams.h" +#include "AudioTools/AudioStreams.h" #include "PDM.h" namespace audio_tools { @@ -82,9 +82,9 @@ class AudioMP34DT05 : public AudioStream { p_buffer=nullptr; } - size_t readBytes(uint8_t *data, size_t len) override { + size_t readBytes(uint8_t *buffer, size_t length) override { if (p_buffer == nullptr) return 0; - return p_buffer->readArray(data, len); + return p_buffer->readArray(buffer, length); } int available() override { diff --git a/src/AudioLibs/AudioMozzi.h b/src/AudioLibs/AudioMozzi.h new file mode 100644 index 0000000000..ae1949267e --- /dev/null +++ b/src/AudioLibs/AudioMozzi.h @@ -0,0 +1,294 @@ +#pragma once + +#include "AudioConfig.h" +#include "mozzi_config.h" +#include "hardware_defines.h" +#include "mozzi_analog.h" +#include "Mozzi.h" +#include "AudioTools/AudioStreams.h" + +namespace audio_tools { + +/** + * @brief Mozzi Configuration for input or output stream + * + */ +struct MozziConfig : AudioInfo { + uint16_t control_rate=CONTROL_RATE; + void (*updateControl)() = nullptr; //&::updateControl; + AudioOutput_t (*updateAudio)() = nullptr; // = &::updateAudio; + + MozziConfig(){ + channels = AUDIO_CHANNELS; + sample_rate = AUDIO_RATE; + bits_per_sample = 16; + } +}; +/** + * @brief Support for https://sensorium.github.io/Mozzi/ + * Define your updateControl() method. + * Define your updateAudio() method. + * Start by calling begin(control_rate) + * do not call audioHook(); in the loop ! + * @ingroup generator + * @author Phil Schatzmann + * @copyright GPLv3 + */ + +class MozziGenerator : public SoundGenerator { + public: + MozziGenerator(){ + TRACED(); + } + + MozziGenerator(MozziConfig config){ + begin(config); + } + + virtual ~MozziGenerator() { + end(); + } + + void begin(MozziConfig config){ + SoundGenerator::begin(); + info = config; + if (info.control_rate==0){ + info.control_rate = CONTROL_RATE; + } + control_counter_max = info.sample_rate / info.control_rate; + if (control_counter_max <= 0){ + control_counter_max = 1; + } + control_counter = control_counter_max; + } + + /// Provides some key audio information + MozziConfig config() { + return info; + } + + /// Provides a single sample + virtual int16_t readSample() { + if (info.updateAudio==nullptr){ + LOGE("The updateAudio method has not been defined in the configuration !"); + stop(); + return 0; + } + + // return value from buffer -> channel 2 + if (is_read_buffer_filled){ + // for stereo output we might have the value already + is_read_buffer_filled = false; + return read_buffer; + } + + // control update + if (--control_counter<0){ + control_counter = control_counter_max; + if (info.updateControl!=nullptr){ + LOGD("updateControl"); + info.updateControl(); + } + } + + // return left value + int16_t result = updateSample(); + return result; + } + + protected: + MozziConfig info; + int control_counter_max; + int control_counter; + int read_buffer; + bool is_read_buffer_filled = false; + + + // we make sure that we return one sample for each + // requested number of channels sequentially + int16_t updateSample(){ + AudioOutput out = info.updateAudio(); + // requested mono + int16_t result = 0; + #if (AUDIO_CHANNELS == MONO) + if (info.channels==2){ + result = out[0]; + read_buffer = out[0]; + is_read_buffer_filled = true; + } else if (info.channels==1){ + result = out[0]; + } + + // requested stereo + #elif (AUDIO_CHANNELS == STEREO) + result = out[0]; + if (info.channels==2){ + result = out[0]; + read_buffer = out[1]; + is_read_buffer_filled = true; + } else if (info.channels==1){ + result = out[0]/2 + out[1]/2; + } + #endif + return result; + } + + +}; + + + +/** + * @brief We use the output functionality of Mozzi to output audio data. We expect the data as array of int16_t with + * one or two channels. Though we support the setting of a sample rate, we recommend to use the default sample rate + * from Mozzi which is available with the AUDIO_RATE define. + * @ingroup dsp + */ +class MozziStream : public AudioStream { + + public: + MozziStream(){ + TRACED(); + } + + ~MozziStream(){ + TRACED(); + end(); + if (input_ptr!=nullptr){ + delete input_ptr; + input_ptr = nullptr; + } + } + + MozziConfig defaultConfig() { + MozziConfig default_config; + return default_config; + } + + /// Starts Mozzi with its default parameters + void begin(){ + begin(defaultConfig()); + } + + // Start Mozzi - if controlRate > 0 we actiavate the sound generation (=>allow reads); the parameters describe the values if the + // provided input stream or resulting output stream; + void begin(MozziConfig cfg){ + TRACED(); + config = cfg; + Mozzi.setAudioRate(config.sample_rate); + // in output mode we do not allocate any unnecessary functionality + if (cfg.channels != config.channels){ + LOGE("You need to change the AUDIO_CHANNELS in mozzi_config.h to %d", cfg.channels); + } + + Mozzi.start(config.control_rate); + } + + void end(){ + TRACED(); + Mozzi.stop(); + } + + /// unsupported operations + virtual int availableForWrite() { + return Mozzi.canWrite() ? sizeof(int) : 0; + } + + /// write an individual byte - if the frame is complete we pass it to Mozzi + virtual size_t write(uint8_t c) { + if (Mozzi.canWrite()){ + writeChar(c); + return 1; + } else { + return 0; + } + } + + virtual size_t write(const uint8_t *buffer, size_t size) { + for (size_t j=0;jreadBytes((uint8_t*)buffer, length, config.channels); + } + + virtual int read(){ + return not_supported(-1); + } + + virtual int peek(){ + return not_supported(-1); + } + + virtual void flush(){ + }; + + protected: + MozziConfig config; + MozziGenerator *input_ptr = nullptr; + NumberReader numberReader; + int32_t frame[2]; + uint8_t buffer[64]; + int buffer_pos; + + MozziGenerator *get_input_ptr(){ + if (input_ptr==nullptr){ + // allocate generator only when requested + if (config.control_rate>0){ + config.control_rate = CONTROL_RATE; + } + input_ptr = new MozziGenerator(config); + } + return input_ptr; + } + + // writes individual bytes and puts them together as MonoOutput or StereoOutput + void writeChar(uint8_t c){ + buffer[buffer_pos++] = c; + + #if (AUDIO_CHANNELS == MONO) + #warning "Mozzi is configured for Mono Output (to one channel only)" + if (config.channels == 1 && buffer_pos==config.bits_per_sample/8){ + numberReader.toNumbers(buffer, config.bits_per_sample,sizeof(AudioOutputStorage_t) * 8, true, 1, frame ); + MonoOutput out = MonoOutput(frame[0]); + Mozzi.write(out); + buffer_pos = 0; + } else if (config.channels == 2 && buffer_pos==config.bits_per_sample/8*2){ + numberReader.toNumbers(buffer, config.bits_per_sample,sizeof(AudioOutputStorage_t) * 8, true, 2, frame ); + MonoOutput out = MonoOutput(frame[0]/2 + frame[1]/2); + Mozzi.write(out); + buffer_pos = 0; + } + + #elif (AUDIO_CHANNELS == STEREO) + if (config.channels == 1 && buffer_pos==config.bits_per_sample/8){ + numberReader.toNumbers(buffer, config.bits_per_sample,sizeof(AudioOutputStorage_t) * 8, true, 1, frame ); + StereoOutput out = StereoOutput(frame[0],frame[0]); + Mozzi.write(out); + buffer_pos = 0; + } else if (config.channels == 2 && buffer_pos==config.bits_per_sample/8*2){ + numberReader.toNumbers(buffer, config.bits_per_sample,sizeof(AudioOutputStorage_t) * 8, true, 2, frame ); + StereoOutput out = StereoOutput(frame[0],frame[1]); + Mozzi.write(out); + buffer_pos = 0; + } + + #endif + } + +}; + + +} // Namespace + diff --git a/src/AudioLibs/AudioRealFFT.h b/src/AudioLibs/AudioRealFFT.h new file mode 100644 index 0000000000..3d327ca6aa --- /dev/null +++ b/src/AudioLibs/AudioRealFFT.h @@ -0,0 +1,100 @@ +#pragma once + +#include "AudioFFT.h" +#include "FFT/FFTReal.h" + +/** + * @defgroup fft-real Real + * @ingroup fft + * @brief FFT using Real FFT +**/ + +namespace audio_tools { + +/** + * @brief Driver for RealFFT + * @ingroup fft-real + * @author Phil Schatzmann + * @copyright GPLv3 + */ +class FFTDriverRealFFT : public FFTDriver { + public: + bool begin(int len) override { + this->len = len; + if (p_fft_object==nullptr) p_fft_object = new ffft::FFTReal(len); + if (p_x==nullptr) p_x = new float[len]; + if (p_f==nullptr) p_f = new float[len]; + assert(p_fft_object!=nullptr); + assert(p_x!=nullptr); + assert(p_f!=nullptr); + return p_fft_object!=nullptr && p_x!=nullptr && p_f!=nullptr; + } + void end()override{ + if (p_fft_object!=nullptr) delete p_fft_object; + if (p_x!=nullptr) delete[] p_x; + if (p_f!=nullptr) delete[] p_f; + p_fft_object = nullptr; + p_x = nullptr; + p_f = nullptr; + } + void setValue(int idx, int value) override{ + p_x[idx] = value; + } + + void fft() override{ + memset(p_f,0,len*sizeof(float)); + p_fft_object->do_fft(p_f, p_x); + }; + + float magnitude(int idx) override { + return sqrt(p_x[idx] * p_x[idx] + p_f[idx] * p_f[idx]); + } + + /// magnitude w/o sqrt + float magnitudeFast(int idx) override { + return (p_x[idx] * p_x[idx] + p_f[idx] * p_f[idx]); + } + + virtual bool isValid() override{ return p_fft_object!=nullptr; } + + ffft::FFTReal *p_fft_object=nullptr; + float *p_x = nullptr; // real + float *p_f = nullptr; // complex + int len; + +}; + +/** + * @brief AudioFFT using RealFFT + * @ingroup fft-real + * @author Phil Schatzmann + * @copyright GPLv3 + */ +class AudioRealFFT : public AudioFFTBase { + public: + AudioRealFFT():AudioFFTBase(new FFTDriverRealFFT()) {} + + /// Provides the real array returned by the FFT + float* realArray() { + return driverEx()->p_x; + } + + /// Provides the complex array returned by the FFT + float *imgArray() { + return driverEx()->p_f; + } + + /// Inverse fft - convert fft result back to time domain (samples) + float* ifft(float *real, float* complex){ + // update mirrored values + int len = length(); + static_cast(driver())->p_fft_object->do_ifft(real, complex); + return real; + } + + FFTDriverRealFFT* driverEx() { + return (FFTDriverRealFFT*)driver(); + } +}; + +} diff --git a/src/AudioTools/AudioLibs/AudioSTK.h b/src/AudioLibs/AudioSTK.h similarity index 85% rename from src/AudioTools/AudioLibs/AudioSTK.h rename to src/AudioLibs/AudioSTK.h index 6d802fc423..797f4aac65 100644 --- a/src/AudioTools/AudioLibs/AudioSTK.h +++ b/src/AudioLibs/AudioSTK.h @@ -1,9 +1,7 @@ #pragma once -#include "AudioToolsConfig.h" -#include "AudioTools/CoreAudio/AudioEffects/AudioEffect.h" -#include "AudioTools/CoreAudio/AudioStreams.h" - +#include "AudioConfig.h" +#include "AudioEffects/AudioEffect.h" #ifdef ESP32 # include "freertos/FreeRTOS.h" #endif @@ -13,7 +11,7 @@ namespace audio_tools { /** * @brief The Synthesis ToolKit in C++ (STK) is a set of open source audio signal processing and algorithmic synthesis classes - * written in the C++ programming language. You need to install https://github.com/pschatzmann/Arduino-STK + * written in the C++ programming language. * * You can find further informarmation in the original Readme of the STK Project * @@ -141,16 +139,11 @@ class STKEffect : public AudioEffect { class STKChorus : public AudioEffect, public stk::Chorus { public: STKChorus(float baseDelay = 6000) : stk::Chorus(baseDelay) {} - STKChorus(const STKChorus& copy) = default; - - AudioEffect* clone() override{ - return new STKChorus(*this); - } virtual effect_t process(effect_t in) { // just convert between int16 and float float value = static_cast(in) / 32767.0; - return stk::Chorus::tick(value) * 32767.0; + return tick(value) * 32767.0; } }; @@ -164,16 +157,11 @@ class STKEcho : public AudioEffect, public stk::Echo { public: STKEcho(unsigned long maximumDelay = (unsigned long)Stk::sampleRate()) : stk::Echo(maximumDelay) {} - STKEcho(const STKEcho& copy) = default; - - AudioEffect* clone() override{ - return new STKEcho(*this); - } virtual effect_t process(effect_t in) { // just convert between int16 and float float value = static_cast(in) / 32767.0; - return stk::Echo::tick(value) * 32767.0; + return tick(value) * 32767.0; } }; @@ -190,10 +178,14 @@ class STKFreeVerb : public AudioEffect, public stk::FreeVerb { AudioEffect* clone() override{ return new STKFreeVerb(*this); } + StkFloat tick (StkFloat input, unsigned int channel=0) override + { + return FreeVerb::tick(input, input, channel); + } virtual effect_t process(effect_t in) { // just convert between int16 and float float value = static_cast(in) / 32767.0; - return stk::FreeVerb::tick(value) * 32767.0; + return tick(value) * 32767.0; } }; @@ -214,7 +206,7 @@ class STKChowningReverb : public AudioEffect, public stk::JCRev { virtual effect_t process(effect_t in) { // just convert between int16 and float float value = static_cast(in) / 32767.0; - return stk::JCRev::tick(value) * 32767.0; + return tick(value) * 32767.0; } }; @@ -234,7 +226,7 @@ class STKNReverb : public AudioEffect, public stk::NRev { virtual effect_t process(effect_t in) { // just convert between int16 and float float value = static_cast(in) / 32767.0; - return stk::NRev::tick(value) * 32767.0; + return tick(value) * 32767.0; } }; @@ -254,7 +246,7 @@ class STKPerryReverb : public AudioEffect, public stk::PRCRev { virtual effect_t process(effect_t in) { // just convert between int16 and float float value = static_cast(in) / 32767.0; - return stk::PRCRev::tick(value) * 32767.0; + return tick(value) * 32767.0; } }; @@ -268,21 +260,16 @@ class STKLentPitShift : public AudioEffect, public stk::LentPitShift { public: STKLentPitShift(float periodRatio = 1.0, int tMax = 512) : stk::LentPitShift(periodRatio, tMax) {} - STKLentPitShift(const STKLentPitShift& copy) = default; - AudioEffect* clone() override{ - return new STKLentPitShift(*this); - } virtual effect_t process(effect_t in) { // just convert between int16 and float float value = static_cast(in) / 32767.0; - return stk::LentPitShift::tick(value) * 32767.0; + return tick(value) * 32767.0; } }; /** - * @brief Simple Pitch shifter effect class: This class implements a simple pitch - * shifter using a delay line. + * @brief Pitch shifter effect class based on the Lent algorithm * @ingroup effects * @author Phil Schatzmann * @copyright GPLv3 @@ -290,15 +277,10 @@ class STKLentPitShift : public AudioEffect, public stk::LentPitShift { class STKPitShift : public AudioEffect, public stk::PitShift { public: STKPitShift() = default; - STKPitShift(const STKPitShift& copy) = default; - - AudioEffect* clone() override{ - return new STKPitShift(*this); - } virtual effect_t process(effect_t in) { // just convert between int16 and float float value = static_cast(in) / 32767.0; - return stk::PitShift::tick(value) * 32767.0; + return tick(value) * 32767.0; } }; diff --git a/src/AudioTools/AudioLibs/AudioServerEx.h b/src/AudioLibs/AudioServerEx.h similarity index 92% rename from src/AudioTools/AudioLibs/AudioServerEx.h rename to src/AudioLibs/AudioServerEx.h index a5f3fa5a56..c096ec612f 100644 --- a/src/AudioTools/AudioLibs/AudioServerEx.h +++ b/src/AudioLibs/AudioServerEx.h @@ -1,9 +1,8 @@ #pragma once -#include "AudioToolsConfig.h" -#include "AudioTools/CoreAudio/AudioOutput.h" -#include "AudioTools/AudioCodecs/CodecWAV.h" -#include "AudioTools/CoreAudio/AudioBasic/StrView.h" +#include "AudioConfig.h" +#include "AudioTools/AudioOutput.h" +#include "AudioCodecs/CodecWAV.h" #include "HttpServer.h" #include "HttpExtensions.h" @@ -113,7 +112,7 @@ class AudioServerEx : public AudioOutput { HttpServer *p_server; ExtensionStream *p_stream=nullptr; - virtual tinyhttp::StrView* getReplyHeader() { + virtual tinyhttp::Str* getReplyHeader() { return nullptr; } @@ -158,15 +157,15 @@ class AudioWAVServerEx : public AudioServerEx { protected: // Dynamic memory - tinyhttp::Str header; + tinyhttp::StrExt header; // wav files start with a 44 bytes header - virtual tinyhttp::StrView* getReplyHeader() { + virtual tinyhttp::Str* getReplyHeader() { header.allocate(44); MemoryOutput mp{(uint8_t*)header.c_str(), 44}; - WAVHeader enc; + WAVEncoder enc; WAVAudioInfo wi; - wi.format = AudioFormat::PCM; + wi.format = WAV_FORMAT_PCM; wi.sample_rate = info.sample_rate; wi.bits_per_sample = info.bits_per_sample; wi.channels = info.channels; diff --git a/src/AudioTools/Disk/AudioSourceIdxSD.h b/src/AudioLibs/AudioSourceIdxSD.h similarity index 76% rename from src/AudioTools/Disk/AudioSourceIdxSD.h rename to src/AudioLibs/AudioSourceIdxSD.h index c02a4ab4f8..ab74446e7e 100644 --- a/src/AudioTools/Disk/AudioSourceIdxSD.h +++ b/src/AudioLibs/AudioSourceIdxSD.h @@ -1,9 +1,11 @@ #pragma once #include +#include #include #include "AudioLogger.h" -#include "AudioTools/Disk/AudioSource.h" -#include "AudioTools/Disk/SDIndex.h" +#include "AudioBasic/StrExt.h" +#include "AudioTools/AudioSource.h" +#include "AudioLibs/SDIndex.h" namespace audio_tools { @@ -33,33 +35,21 @@ class AudioSourceIdxSD : public AudioSource { /// Default constructor AudioSourceIdxSD(const char *startFilePath = "/", const char *ext = ".mp3", int chipSelect = PIN_CS, bool setupIndex=true) { start_path = startFilePath; - extension = ext; + exension = ext; setup_index = setupIndex; - p_spi = &SPI; cs = chipSelect; } -#ifdef USE_SD_SUPPORTS_SPI - // Pass your own spi instance, in case you need a dedicated one - AudioSourceIdxSD(const char *startFilePath, const char *ext, int chipSelect, SPIClass &spiInstance, bool setupIndex=true) { - start_path = startFilePath; - extension = ext; - setup_index = setupIndex; - p_spi = &spiInstance; - cs = chipSelect; - } -#endif - virtual void begin() override { TRACED(); if (!is_sd_setup) { - while (!start_sd()) { + while (!SD.begin(cs)) { LOGW("SD.begin cs=%d failed", cs); delay(500); } is_sd_setup = true; } - idx.begin(start_path, extension, file_name_pattern, setup_index); + idx.begin(start_path, exension, file_name_pattern, setup_index); idx_pos = 0; } @@ -111,29 +101,22 @@ class AudioSourceIdxSD : public AudioSource { long size() { return idx.size();} protected: -#if defined(USE_SD_NO_NS) - SDIndex idx{SD}; +#ifdef RP2040_HOWER + SDIndex idx{SD}; #else SDIndex idx{SD}; #endif File file; size_t idx_pos = 0; const char *file_name; - const char *extension = nullptr; + const char *exension = nullptr; const char *start_path = nullptr; const char *file_name_pattern = "*"; bool setup_index = true; bool is_sd_setup = false; - SPIClass *p_spi = nullptr; int cs; - bool start_sd(){ -#ifdef USE_SD_SUPPORTS_SPI - return SD.begin(cs, *p_spi); -#else - return SD.begin(cs); -#endif - } + }; } // namespace audio_tools \ No newline at end of file diff --git a/src/AudioTools/Disk/AudioSourceIdxSDFAT.h b/src/AudioLibs/AudioSourceIdxSDFAT.h similarity index 59% rename from src/AudioTools/Disk/AudioSourceIdxSDFAT.h rename to src/AudioLibs/AudioSourceIdxSDFAT.h index 990a6b1c08..89df229bf3 100644 --- a/src/AudioTools/Disk/AudioSourceIdxSDFAT.h +++ b/src/AudioLibs/AudioSourceIdxSDFAT.h @@ -1,14 +1,16 @@ #pragma once +#include "AudioConfig.h" + #include #include - -#include "AudioToolsConfig.h" #include "AudioLogger.h" -#include "AudioTools/Disk/AudioSource.h" +#include "AudioBasic/StrExt.h" +#include "AudioTools/AudioSource.h" #define USE_SDFAT -#include "AudioTools/Disk/SDIndex.h" +#include "AudioLibs/SDIndex.h" + // SD_FAT_TYPE = 0 for SdFat/File as defined in SdFatConfig.h, // 1 for FAT16/FAT32, 2 for exFAT, 3 for FAT16/FAT32 and exFAT. @@ -18,19 +20,20 @@ // Try max SPI clock for an SD. Reduce SPI_CLOCK if errors occur. (40?) #define SPI_CLOCK SD_SCK_MHZ(50) #if SD_FAT_TYPE == 0 -typedef SdFat AudioFs; -typedef File AudioFile; + typedef SdFat AudioFs; + typedef File AudioFile; #elif SD_FAT_TYPE == 1 -typedef SdFat32 AudioFs; -typedef File32 AudioFile; + typedef SdFat32 AudioFs; + typedef File32 AudioFile; #elif SD_FAT_TYPE == 2 -typedef SdExFat AudioFs; -typedef ExFile AudioFile; + typedef SdExFat AudioFs; + typedef ExFile AudioFile; #elif SD_FAT_TYPE == 3 -typedef SdFs AudioFs; -typedef FsFile AudioFile; + typedef SdFs AudioFs; + typedef FsFile AudioFile; #endif + namespace audio_tools { /** * @brief ESP32 AudioSource for AudioPlayer using an SD card as data source. @@ -41,40 +44,36 @@ namespace audio_tools { * @copyright GPLv3 */ class AudioSourceIdxSDFAT : public AudioSource { - public: +public: /// Default constructor - AudioSourceIdxSDFAT(const char *startFilePath = "/", const char *ext = ".mp3", - int chipSelect = PIN_CS, int speedMHz = 10, - bool setupIndex = true) { - TRACED(); - LOGI("SD chipSelect: %d", chipSelect); - LOGI("SD speedMHz: %d", speedMHz); - LOGI("ext: %s", ext); - p_cfg = new SdSpiConfig(chipSelect, DEDICATED_SPI, SD_SCK_MHZ(speedMHz)); - owns_cfg = true; - start_path = startFilePath; - exension = ext; - setup_index = setupIndex; + AudioSourceIdxSDFAT(const char* startFilePath = "/", const char* ext = ".mp3", int chipSelect = PIN_CS, int speedMHz = 2, bool setupIndex=true) { + TRACED(); + LOGI("SD chipSelect: %d", chipSelect); + LOGI("SD speedMHz: %d", speedMHz); + LOGI("ext: %s", ext); + p_cfg = new SdSpiConfig(chipSelect, DEDICATED_SPI, SD_SCK_MHZ(speedMHz)); + owns_cfg = true; + start_path = startFilePath; + exension = ext; + setup_index = setupIndex; } - /// Costructor with SdSpiConfig - AudioSourceIdxSDFAT(const char *startFilePath, const char *ext, - SdSpiConfig &config, bool setupIndex = true) { - TRACED(); - p_cfg = &config; - owns_cfg = false; - start_path = startFilePath; - exension = ext; - setup_index = setupIndex; + /// Costructor with SdSpiConfig + AudioSourceSDFAT(const char* startFilePath, const char* ext, SdSpiConfig &config, bool setupIndex=true) { + TRACED(); + p_cfg = &config; + owns_cfg = false; + start_path = startFilePath; + exension = ext; + setup_index = setupIndex; } - virtual ~AudioSourceIdxSDFAT() { end(); } virtual void begin() override { TRACED(); if (!is_sd_setup) { - if (!sd.begin(*p_cfg)) { - LOGE("sd.begin failed"); + if (!sd.begin(*p_cfg)) { + LOGE("sd.begin failed"); return; } is_sd_setup = true; @@ -84,13 +83,10 @@ class AudioSourceIdxSDFAT : public AudioSource { } void end() { - if (is_sd_setup) { -#ifdef ESP32 - sd.end(); -#endif - if (owns_cfg) delete (p_cfg); - is_sd_setup = false; - } + #ifdef ESP32 + sd.end(); + #endif + is_sd_setup = false; } virtual Stream *nextStream(int offset = 1) override { @@ -106,18 +102,18 @@ class AudioSourceIdxSDFAT : public AudioSource { virtual Stream *selectStream(const char *path) override { file.close(); - if (path == nullptr) { + if (path==nullptr){ LOGE("Filename is null") return nullptr; } - // AudioFile new_file; - if (!file.open(path, O_RDONLY)) { + AudioFile new_file; + if (!new_file.open(path, O_RDONLY)){ LOGE("Open error: '%s'", path); } LOGI("-> selectStream: %s", path); - // file = new_file; + file = new_file; return &file; } @@ -138,13 +134,13 @@ class AudioSourceIdxSDFAT : public AudioSource { virtual void setPath(const char *p) { start_path = p; } /// Provides the number of files (The max index is size()-1) - long size() { return idx.size(); } + long size() { return idx.size();} - protected: +protected: SdSpiConfig *p_cfg = nullptr; AudioFs sd; AudioFile file; - SDIndex idx{sd}; + SDIndex idx{sd}; size_t idx_pos = 0; char file_name[MAX_FILE_LEN]; const char *exension = nullptr; @@ -153,13 +149,14 @@ class AudioSourceIdxSDFAT : public AudioSource { bool setup_index = true; bool is_sd_setup = false; int cs; - bool owns_cfg = false; + bool owns_cfg=false; - const char *getFileName(AudioFile &file) { - static char name[MAX_FILE_LEN]; - file.getName(name, MAX_FILE_LEN); - return name; + const char* getFileName(AudioFile&file){ + static char name[MAX_FILE_LEN]; + file.getName(name,MAX_FILE_LEN); + return name; } + }; -} // namespace audio_tools \ No newline at end of file +} // namespace audio_tools \ No newline at end of file diff --git a/src/AudioTools/Disk/AudioSourceIdxSDMMC.h b/src/AudioLibs/AudioSourceIdxSDMMC.h similarity index 96% rename from src/AudioTools/Disk/AudioSourceIdxSDMMC.h rename to src/AudioLibs/AudioSourceIdxSDMMC.h index 6aeb200623..f58cdd2053 100644 --- a/src/AudioTools/Disk/AudioSourceIdxSDMMC.h +++ b/src/AudioLibs/AudioSourceIdxSDMMC.h @@ -3,8 +3,9 @@ #include #include #include "AudioLogger.h" -#include "AudioTools/Disk/AudioSource.h" -#include "AudioTools/Disk/SDIndex.h" +#include "AudioBasic/StrExt.h" +#include "AudioTools/AudioSource.h" +#include "AudioLibs/SDIndex.h" namespace audio_tools { diff --git a/src/AudioTools/Disk/AudioSourceLittleFS.h b/src/AudioLibs/AudioSourceLittleFS.h similarity index 96% rename from src/AudioTools/Disk/AudioSourceLittleFS.h rename to src/AudioLibs/AudioSourceLittleFS.h index 3f1115ad98..88f47ff69a 100644 --- a/src/AudioTools/Disk/AudioSourceLittleFS.h +++ b/src/AudioLibs/AudioSourceLittleFS.h @@ -1,9 +1,10 @@ #pragma once -#include +#include "AudioBasic/StrExt.h" #include "AudioLogger.h" -#include "AudioTools/Disk/AudioSource.h" -#include "AudioTools/Disk/SDDirect.h" +#include "AudioTools/AudioSource.h" +#include "AudioLibs/SDDirect.h" +#include namespace audio_tools { diff --git a/src/AudioTools/Disk/AudioSourceSD.h b/src/AudioLibs/AudioSourceSD.h similarity index 76% rename from src/AudioTools/Disk/AudioSourceSD.h rename to src/AudioLibs/AudioSourceSD.h index 2e3ef01979..a2758bd168 100644 --- a/src/AudioTools/Disk/AudioSourceSD.h +++ b/src/AudioLibs/AudioSourceSD.h @@ -1,9 +1,12 @@ #pragma once + +#include "AudioBasic/StrExt.h" +#include "AudioLogger.h" +#include "AudioTools/AudioSource.h" +#include "AudioLibs/SDDirect.h" +#include "FS.h" #include "SD.h" #include "SPI.h" -#include "AudioLogger.h" -#include "AudioTools/Disk/AudioSource.h" -#include "SDDirect.h" namespace audio_tools { @@ -34,35 +37,21 @@ class AudioSourceSD : public AudioSource { /// Default constructor AudioSourceSD(const char *startFilePath = "/", const char *ext = ".mp3", int chipSelect = PIN_CS, bool setupIndex=true) { start_path = startFilePath; - extension = ext; - setup_index = setupIndex; - p_spi = &SPI; - cs = chipSelect; - } - -#ifdef USE_SD_SUPPORTS_SPI - - // Pass your own spi instance, in case you need a dedicated one - AudioSourceSD(const char *startFilePath, const char *ext, int chipSelect, SPIClass &spiInstance, bool setupIndex=true) { - start_path = startFilePath; - extension = ext; + exension = ext; setup_index = setupIndex; - p_spi = &spiInstance; cs = chipSelect; } -#endif - virtual void begin() override { TRACED(); if (!is_sd_setup) { - while (!start_sd()) { - LOGE("SD.begin cs=%d failed", cs); + while (!SD.begin(cs)) { + LOGE("SD.begin cs=%d failed",cs); delay(1000); } is_sd_setup = true; } - idx.begin(start_path, extension, file_name_pattern); + idx.begin(start_path, exension, file_name_pattern); idx_pos = 0; } @@ -114,30 +103,22 @@ class AudioSourceSD : public AudioSource { long size() { return idx.size();} protected: -#if defined(USE_SD_NO_NS) - SDDirect idx{SD}; +#ifdef RP2040_HOWER + SDDirect idx{SD}; #else SDDirect idx{SD}; #endif File file; size_t idx_pos = 0; const char *file_name; - const char *extension = nullptr; + const char *exension = nullptr; const char *start_path = nullptr; const char *file_name_pattern = "*"; bool setup_index = true; bool is_sd_setup = false; int cs; - SPIClass *p_spi = nullptr; - bool start_sd(){ -#ifdef USE_SD_SUPPORTS_SPI - return SD.begin(cs, *p_spi); -#else - return SD.begin(cs); -#endif - } }; -} // namespace audio_tools +} // namespace audio_tools \ No newline at end of file diff --git a/src/AudioTools/Disk/AudioSourceSDFAT.h b/src/AudioLibs/AudioSourceSDFAT.h similarity index 50% rename from src/AudioTools/Disk/AudioSourceSDFAT.h rename to src/AudioLibs/AudioSourceSDFAT.h index ea7aa9d1ca..afc43df4e3 100644 --- a/src/AudioTools/Disk/AudioSourceSDFAT.h +++ b/src/AudioLibs/AudioSourceSDFAT.h @@ -1,14 +1,16 @@ #pragma once +#include "AudioConfig.h" + #include #include - +#include "AudioBasic/StrExt.h" #include "AudioLogger.h" -#include "AudioTools/Disk/AudioSource.h" -#include "AudioToolsConfig.h" +#include "AudioTools/AudioSource.h" #define USE_SDFAT -#include "AudioTools/Disk/SDDirect.h" +#include "AudioLibs/SDDirect.h" + // SD_FAT_TYPE = 0 for SdFat/File as defined in SdFatConfig.h, // 1 for FAT16/FAT32, 2 for exFAT, 3 for FAT16/FAT32 and exFAT. @@ -19,81 +21,61 @@ #define SPI_CLOCK SD_SCK_MHZ(50) #if SD_FAT_TYPE == 0 -typedef SdFat AudioFs; -typedef File AudioFile; + typedef SdFat AudioFs; + typedef File AudioFile; #elif SD_FAT_TYPE == 1 -typedef SdFat32 AudioFs; -typedef File32 AudioFile; + typedef SdFat32 AudioFs; + typedef File32 AudioFile; #elif SD_FAT_TYPE == 2 -typedef SdExFat AudioFs; -typedef ExFile AudioFile; + typedef SdExFat AudioFs; + typedef ExFile AudioFile; #elif SD_FAT_TYPE == 3 -typedef SdFs AudioFs; -typedef FsFile AudioFile; + typedef SdFs AudioFs; + typedef FsFile AudioFile; #endif + namespace audio_tools { /** * @brief ESP32 AudioSource for AudioPlayer using an SD card as data source. * This class is based on the Arduino SD implementation - * Connect the SD card. + * Connect the SD card. * For UTF8 Support change SdFatConfig.h #define USE_UTF8_LONG_NAMES 1 * @ingroup player * @author Phil Schatzmann * @copyright GPLv3 */ class AudioSourceSDFAT : public AudioSource { - public: +public: /// Default constructor - AudioSourceSDFAT(const char *startFilePath = "/", const char *ext = ".mp3", - int chipSelect = PIN_CS, int speedMHz = 10, - int spi_mode = DEDICATED_SPI, bool setupIndex = true) { - TRACED(); - LOGI("SD chipSelect: %d", chipSelect); - LOGI("SD speedMHz: %d", speedMHz); - LOGI("ext: %s", ext); - p_cfg = new SdSpiConfig(chipSelect, spi_mode, SD_SCK_MHZ(speedMHz)); - owns_cfg = true; - start_path = startFilePath; - exension = ext; - setup_index = setupIndex; - } - - /// Costructor with SdSpiConfig - AudioSourceSDFAT(const char *startFilePath, const char *ext, - SdSpiConfig &config, bool setupIndex = true) { - TRACED(); - p_cfg = &config; - owns_cfg = false; - start_path = startFilePath; - exension = ext; - setup_index = setupIndex; + AudioSourceSDFAT(const char* startFilePath = "/", const char* ext = ".mp3", int chipSelect = PIN_CS, int speedMHz = 2, bool setupIndex=true) { + TRACED(); + LOGI("SD chipSelect: %d", chipSelect); + LOGI("SD speedMHz: %d", speedMHz); + LOGI("ext: %s", ext); + p_cfg = new SdSpiConfig(chipSelect, DEDICATED_SPI, SD_SCK_MHZ(speedMHz)); + owns_cfg = true; + start_path = startFilePath; + exension = ext; + setup_index = setupIndex; } - /// Constructor for providing an open FS - AudioSourceSDFAT(AudioFs fs, const char *startFilePath="/", const char *ext="", bool setupIndex = true){ - TRACED(); - sd = fs; - p_cfg = nullptr; - owns_cfg = false; - start_path = startFilePath; - exension = ext; - setup_index = setupIndex; - is_sd_setup = true; - // since we expect an open fs we do not close it - is_close_sd = false; + /// Costructor with SdSpiConfig + AudioSourceSDFAT(const char* startFilePath, const char* ext, SdSpiConfig &config, bool setupIndex=true) { + TRACED(); + p_cfg = &config; + owns_cfg = false; + start_path = startFilePath; + exension = ext; + setup_index = setupIndex; } - virtual ~AudioSourceSDFAT() { - end(); - if (owns_cfg) delete (p_cfg); - } virtual void begin() override { TRACED(); if (!is_sd_setup) { - if (!sd.begin(*p_cfg)) { - LOGE("sd.begin failed"); + if (!sd.begin(*p_cfg)) { + LOGE("sd.begin failed"); return; } is_sd_setup = true; @@ -103,15 +85,13 @@ class AudioSourceSDFAT : public AudioSource { } void end() { - if (is_sd_setup) { - TRACEI(); #ifdef ESP32 - if (is_close_sd) sd.end(); + sd.end(); #endif - is_sd_setup = false; - } + is_sd_setup = false; } + virtual Stream *nextStream(int offset = 1) override { LOGI("nextStream: %d", offset); return selectStream(idx_pos + offset); @@ -119,7 +99,7 @@ class AudioSourceSDFAT : public AudioSource { virtual Stream *selectStream(int index) override { LOGI("selectStream SDFAT: %d", index); - if (index > -1) { // avoid invalid position + if(index > -1){ //avoid invalid position idx_pos = index; } return selectStream(idx[idx_pos]); @@ -127,19 +107,19 @@ class AudioSourceSDFAT : public AudioSource { virtual Stream *selectStream(const char *path) override { file.close(); - if (path == nullptr) { + if (path==nullptr){ LOGE("Filename is null") return nullptr; } - // AudioFile new_file; - if (!file.open(path, O_RDONLY)) { - LOGE("Open error: '%s'", path); + AudioFile new_file; + if (!new_file.open(path, O_RDONLY)){ + LOGE("Open error: '%s'", path); } LOGI("-> selectStream: %s", path); strncpy(file_name, path, MAX_FILE_LEN); - // file = new_file; + file = new_file; return &file; } @@ -159,18 +139,14 @@ class AudioSourceSDFAT : public AudioSource { /// Allows to "correct" the start path if not defined in the constructor virtual void setPath(const char *p) { start_path = p; } - /// Provides the number of files (The max index is size()-1): WARNING this is - /// very slow if you have a lot of files in many subdirectories - long size() { return idx.size(); } - - /// provides access to the AudioFs object - AudioFs &getAudioFs() { return sd; } + /// Provides the number of files (The max index is size()-1): WARNING this is very slow if you have a lot of files in many subdirectories + long size() { return idx.size();} - protected: +protected: SdSpiConfig *p_cfg = nullptr; AudioFs sd; AudioFile file; - SDDirect idx{sd}; + SDDirect idx{sd}; size_t idx_pos = 0; char file_name[MAX_FILE_LEN]; const char *exension = nullptr; @@ -178,15 +154,15 @@ class AudioSourceSDFAT : public AudioSource { const char *file_name_pattern = "*"; int cs; bool setup_index = true; - bool owns_cfg = false; + bool owns_cfg=false; bool is_sd_setup = false; - bool is_close_sd = true; - const char *getFileName(AudioFile &file) { - static char name[MAX_FILE_LEN]; - file.getName(name, MAX_FILE_LEN); - return name; + const char* getFileName(AudioFile&file){ + static char name[MAX_FILE_LEN]; + file.getName(name,MAX_FILE_LEN); + return name; } + }; -} // namespace audio_tools +} // namespace audio_tools diff --git a/src/AudioTools/Disk/AudioSourceSDMMC.h b/src/AudioLibs/AudioSourceSDMMC.h similarity index 96% rename from src/AudioTools/Disk/AudioSourceSDMMC.h rename to src/AudioLibs/AudioSourceSDMMC.h index 22a97b4cf9..31fbed444d 100644 --- a/src/AudioTools/Disk/AudioSourceSDMMC.h +++ b/src/AudioLibs/AudioSourceSDMMC.h @@ -1,10 +1,11 @@ #pragma once +#include "AudioBasic/StrExt.h" #include "AudioLogger.h" -#include "AudioTools/Disk/AudioSource.h" +#include "AudioTools/AudioSource.h" #include "FS.h" #include "SD_MMC.h" -#include "AudioTools/Disk/SDDirect.h" +#include "AudioLibs/SDDirect.h" namespace audio_tools { diff --git a/src/AudioTools/Disk/AudioSourceSPIFFS.h b/src/AudioLibs/AudioSourceSPIFFS.h similarity index 96% rename from src/AudioTools/Disk/AudioSourceSPIFFS.h rename to src/AudioLibs/AudioSourceSPIFFS.h index 529f6effaa..c291ffe1aa 100644 --- a/src/AudioTools/Disk/AudioSourceSPIFFS.h +++ b/src/AudioLibs/AudioSourceSPIFFS.h @@ -1,9 +1,10 @@ #pragma once -#include +#include "AudioBasic/StrExt.h" #include "AudioLogger.h" -#include "AudioTools/Disk/AudioSource.h" -#include "AudioTools/Disk/SDDirect.h" +#include "AudioTools/AudioSource.h" +#include "AudioLibs/SDDirect.h" +#include namespace audio_tools { diff --git a/src/AudioLibs/Communication.h b/src/AudioLibs/Communication.h new file mode 100644 index 0000000000..5a0e507f57 --- /dev/null +++ b/src/AudioLibs/Communication.h @@ -0,0 +1,794 @@ + +#pragma once +#include +#include + +#include "AudioTools/AudioStreams.h" +#include "AudioTools/Buffers.h" +#include "AudioBasic/Str.h" + +/** + * @defgroup communications Communications + * @ingroup main + * @brief Transmit Audio + * Please note that the standard Arduino WiFiClient and WifiServer (to use TCP/IP), Serial or BluetoothSerial are also supported. + */ + + +namespace audio_tools { + +/** + * @brief A simple RIA locking class for the ESP32 using _lock_t + * @ingroup concurrency + * @author Phil Schatzmann + * @copyright GPLv3 + */ +class Lock { + public: + Lock(_lock_t &lock) { + this->p_lock = &lock; + _lock_acquire(p_lock); + } + ~Lock() { _lock_release(p_lock); } + + protected: + _lock_t *p_lock = nullptr; +}; + +// forward declarations +class ESPNowStream; +ESPNowStream *ESPNowStreamSelf = nullptr; + +/** + * @brief Configuration for ESP-NOW protocol + * @author Phil Schatzmann + * @copyright GPLv3 + */ +struct ESPNowStreamConfig { + wifi_mode_t wifi_mode = WIFI_STA; + const char *mac_address = nullptr; + int channel = 0; + const char *ssid = nullptr; + const char *password = nullptr; + bool use_send_ack = true; // we wait for + uint16_t delay_after_write_ms = 2; + uint16_t delay_after_failed_write_ms = 2000; + uint16_t buffer_size = ESP_NOW_MAX_DATA_LEN; + uint16_t buffer_count = 400; + int write_retry_count = -1; // -1 endless + void (*recveive_cb)(const uint8_t *mac_addr, const uint8_t *data, + int data_len) = nullptr; + /// to encrypt set primary_master_key and local_master_key to 16 byte strings + const char *primary_master_key = nullptr; + const char *local_master_key = nullptr; + /// esp-now bit rate + wifi_phy_rate_t rate = WIFI_PHY_RATE_2M_S; +}; + +/** + * @brief ESPNow as Arduino Stream + * @ingroup communications + * @author Phil Schatzmann + * @copyright GPLv3 + */ +class ESPNowStream : public AudioStream { + public: + ESPNowStream() { ESPNowStreamSelf = this; }; + + ~ESPNowStream() { + if (p_buffer != nullptr) delete p_buffer; + } + + ESPNowStreamConfig defaultConfig() { + ESPNowStreamConfig result; + return result; + } + + /// Returns the mac address of the current ESP32 + const char *macAddress() { + static const char* result = WiFi.macAddress().c_str(); + return result; + } + + /// Defines an alternative send callback + void setSendCallback(esp_now_send_cb_t cb) { send = cb; } + + /// Defines the Receive Callback - Deactivates the readBytes and available() + /// methods! + void setReceiveCallback(esp_now_recv_cb_t cb) { receive = cb; } + + /// Initialization of ESPNow + bool begin() { return begin(cfg); } + + /// Initialization of ESPNow incl WIFI + bool begin(ESPNowStreamConfig cfg) { + this->cfg = cfg; + WiFi.mode(cfg.wifi_mode); + // set mac address + if (cfg.mac_address != nullptr) { + LOGI("setting mac %s", cfg.mac_address); + byte mac[ESP_NOW_KEY_LEN]; + str2mac(cfg.mac_address, mac); + if (esp_wifi_set_mac((wifi_interface_t)getInterface(), mac) != ESP_OK) { + LOGE("Could not set mac address"); + return false; + } + // checking if address has been updated + const char *addr = macAddress(); + if (strcmp(addr, cfg.mac_address) != 0) { + LOGE("Wrong mac address: %s", addr); + return false; + } + } + + if (WiFi.status() != WL_CONNECTED && cfg.ssid != nullptr && + cfg.password != nullptr) { + WiFi.begin(cfg.ssid, cfg.password); + while (WiFi.status() != WL_CONNECTED) { + Serial.print('.'); + delay(1000); + } + } + + LOGI("Setting ESP-NEW rate"); + if (esp_wifi_config_espnow_rate(getInterface(), cfg.rate) != + ESP_OK) { + LOGW("Could not set rate"); + } + + Serial.println(); + Serial.print("mac: "); + Serial.println(WiFi.macAddress()); + return setup(); + } + + /// DeInitialization + void end() { + if (esp_now_deinit() != ESP_OK) { + LOGE("esp_now_deinit"); + } + is_init = false; + } + + /// Adds a peer to which we can send info or from which we can receive info + bool addPeer(esp_now_peer_info_t &peer) { + if (!is_init) { + LOGE("addPeer before begin"); + return false; + } + esp_err_t result = esp_now_add_peer(&peer); + if (result == ESP_OK) { + LOGI("addPeer: %s", mac2str(peer.peer_addr)); + } else { + LOGE("addPeer: %d", result); + } + return result == ESP_OK; + } + + /// Adds an array of + template + bool addPeers(const char *(&array)[size]) { + bool result = true; + for (int j = 0; j < size; j++) { + const char *peer = array[j]; + if (peer != nullptr) { + if (!addPeer(peer)) { + result = false; + } + } + } + return result; + } + + /// Adds a peer to which we can send info or from which we can receive info + bool addPeer(const char *address) { + esp_now_peer_info_t peer; + peer.channel = cfg.channel; + peer.ifidx = getInterface(); + peer.encrypt = false; + + if (Str(address).equals(cfg.mac_address)){ + LOGW("Did not add own address as peer"); + return true; + } + + if (isEncrypted()) { + peer.encrypt = true; + strncpy((char *)peer.lmk, cfg.local_master_key, 16); + } + + if (!str2mac(address, peer.peer_addr)) { + LOGE("addPeer - Invalid address: %s", address); + return false; + } + return addPeer(peer); + } + + /// Writes the data - sends it to all the peers + size_t write(const uint8_t *data, size_t len) override { + int open = len; + size_t result = 0; + int retry_count = 0; + while (open > 0) { + if (available_to_write > 0) { + resetAvailableToWrite(); + size_t send_len = min(open, ESP_NOW_MAX_DATA_LEN); + esp_err_t rc = esp_now_send(nullptr, data + result, send_len); + // wait for confirmation + if (cfg.use_send_ack) { + while (available_to_write == 0) { + delay(1); + } + } else { + is_write_ok = true; + } + // check status + if (rc == ESP_OK && is_write_ok) { + open -= send_len; + result += send_len; + } else { + LOGW("Write failed - retrying again"); + retry_count++; + if (cfg.write_retry_count>0 && retry_count>=cfg.write_retry_count){ + LOGE("Write error after %d retries", cfg.write_retry_count); + // break loop + return 0; + } + } + // if we do have no partner to write we stall and retry later + } else { + delay(cfg.delay_after_write_ms); + } + + // Wait some time before we retry + if (!is_write_ok) { + delay(cfg.delay_after_failed_write_ms); + } + } + return result; + } + + /// Reeds the data from the peers + size_t readBytes(uint8_t *data, size_t len) override { + if (p_buffer == nullptr) return 0; + Lock lock(write_lock); + return p_buffer->readArray(data, len); + } + + int available() override { + return p_buffer == nullptr ? 0 : p_buffer->available(); + } + + int availableForWrite() override { + return cfg.use_send_ack ? available_to_write : cfg.buffer_size; + } + + protected: + ESPNowStreamConfig cfg; + BaseBuffer *p_buffer = nullptr; + esp_now_recv_cb_t receive = default_recv_cb; + esp_now_send_cb_t send = default_send_cb; + volatile size_t available_to_write; + bool is_init = false; + bool is_write_ok = false; + _lock_t write_lock; + + inline void setupReceiveBuffer(){ + // setup receive buffer + if (p_buffer == nullptr && cfg.buffer_count > 0) { + // p_buffer = new NBuffer(cfg.buffer_size , cfg.buffer_count); + p_buffer = new RingBuffer(cfg.buffer_size * cfg.buffer_count); + } + } + + inline void resetAvailableToWrite() { + if (cfg.use_send_ack) { + available_to_write = 0; + } + } + + bool isEncrypted() { + return cfg.primary_master_key != nullptr && cfg.local_master_key != nullptr; + } + + wifi_interface_t getInterface() { + // define wifi_interface_t + wifi_interface_t result; + switch (cfg.wifi_mode) { + case WIFI_STA: + result = (wifi_interface_t)ESP_IF_WIFI_STA; + break; + case WIFI_AP: + result = (wifi_interface_t)ESP_IF_WIFI_AP; + break; + default: + result = (wifi_interface_t)0; + break; + } + return result; + } + + /// Initialization + bool setup() { + esp_err_t result = esp_now_init(); + if (result == ESP_OK) { + LOGI("esp_now_init: %s", macAddress()); + } else { + LOGE("esp_now_init: %d", result); + } + + // encryption is optional + if (isEncrypted()) { + esp_now_set_pmk((uint8_t *)cfg.primary_master_key); + } + + if (cfg.recveive_cb != nullptr) { + esp_now_register_recv_cb(cfg.recveive_cb); + } else { + esp_now_register_recv_cb(receive); + } + if (cfg.use_send_ack) { + esp_now_register_send_cb(send); + } + available_to_write = cfg.buffer_size; + is_init = result == ESP_OK; + return is_init; + } + + bool str2mac(const char *mac, uint8_t *values) { + sscanf(mac, "%hhx:%hhx:%hhx:%hhx:%hhx:%hhx", &values[0], &values[1], + &values[2], &values[3], &values[4], &values[5]); + return strlen(mac) == 17; + } + + const char *mac2str(const uint8_t *array) { + static char macStr[18]; + memset(macStr, 0, 18); + snprintf(macStr, 18, "%02x:%02x:%02x:%02x:%02x:%02x", array[0], array[1], + array[2], array[3], array[4], array[5]); + return (const char *)macStr; + } + + static int bufferAvailableForWrite() { + Lock lock(ESPNowStreamSelf->write_lock); + return ESPNowStreamSelf->p_buffer->availableForWrite(); + } + + static void default_recv_cb(const uint8_t *mac_addr, const uint8_t *data, + int data_len) { + LOGD("rec_cb: %d", data_len); + // make sure that the receive buffer is available - moved from begin to make sure that it is only allocated when needed + ESPNowStreamSelf->setupReceiveBuffer(); + // blocking write + while (bufferAvailableForWrite() < data_len) { + delay(2); + } + Lock lock(ESPNowStreamSelf->write_lock); + size_t result = ESPNowStreamSelf->p_buffer->writeArray(data, data_len); + if (result!=data_len){ + LOGE("writeArray %d -> %d", data_len, result); + } + } + + static void default_send_cb(const uint8_t *mac_addr, + esp_now_send_status_t status) { + static uint8_t first_mac[ESP_NOW_KEY_LEN] = {0}; + // we use the first confirming mac_addr for further confirmations and ignore + // others + if (first_mac[0] == 0) { + strncpy((char *)first_mac, (char *)mac_addr, ESP_NOW_KEY_LEN); + } + LOGD("default_send_cb - %s -> %s", ESPNowStreamSelf->mac2str(mac_addr), + status == ESP_NOW_SEND_SUCCESS ? "+" : "-"); + + // ignore others + if (strncmp((char *)mac_addr, (char *)first_mac, ESP_NOW_KEY_LEN) == 0) { + ESPNowStreamSelf->available_to_write = ESPNowStreamSelf->cfg.buffer_size; + if (status == ESP_NOW_SEND_SUCCESS) { + ESPNowStreamSelf->is_write_ok = true; + } else { + ESPNowStreamSelf->is_write_ok = false; + } + } + } +}; + + +/** + * A Simple exension of the WiFiUDP class which makes sure that the basic Stream + * functioinaltiy which is used as AudioSource and AudioSink + * @ingroup communications + * @author Phil Schatzmann + * @copyright GPLv3 + */ + +class UDPStream : public WiFiUDP { + public: + UDPStream() = default; + + UDPStream(const char *ssid, const char* password){ + this->ssid = ssid; + this->password = password; + } + + /** + * Always return 1492 (MTU 1500 - 8 byte header) as UDP packet available to write + */ + int availableForWrite() { + return 1492; + } + + /** + * Provides the available size of the current package and if this is used up + * of the next package + */ + int available() override { + int size = WiFiUDP::available(); + // if the curren package is used up we prvide the info for the next + if (size == 0) { + size = parsePacket(); + } + return size; + } + + /// Starts to send data to the indicated address / port + uint8_t begin(IPAddress a, uint16_t port) { + connect(); + remote_address_ext = a; + remote_port_ext = port; + return WiFiUDP::begin(port); + } + + /// Starts to receive data from/with the indicated port + uint8_t begin(uint16_t port, uint16_t port_ext = 0) { + connect(); + remote_address_ext = 0u; + remote_port_ext = port_ext != 0 ? port_ext : port; + return WiFiUDP::begin(port); + } + + /// We use the same remote port as defined in begin for write + uint16_t remotePort() { + uint16_t result = WiFiUDP::remotePort(); + return result != 0 ? result : remote_port_ext; + } + + /// We use the same remote ip as defined in begin for write + IPAddress remoteIP() { + // Determine address if it has not been specified + if ((uint32_t)remote_address_ext == 0) { + remote_address_ext = WiFiUDP::remoteIP(); + } + // IPAddress result = WiFiUDP::remoteIP(); + // LOGI("ip: %u", result); + return remote_address_ext; + } + + /** + * Replys will be sent to the initial remote caller + */ + size_t write(const uint8_t *buffer, size_t size) override { + TRACED(); + beginPacket(remoteIP(), remotePort()); + size_t result = WiFiUDP::write(buffer, size); + endPacket(); + return result; + } + // Reads bytes using WiFi::readBytes + size_t readBytes(uint8_t *buffer, size_t length) override { + TRACED(); + size_t len = available(); + size_t bytes_read = 0; + if (len>0){ + // get the data now + bytes_read = WiFiUDP::readBytes((uint8_t*)buffer, length); + } + return bytes_read; +} + + protected: + uint16_t remote_port_ext; + IPAddress remote_address_ext; + const char* ssid = nullptr; + const char* password = nullptr; + + void connect() { + // connect to WIFI + if (WiFi.status() != WL_CONNECTED && ssid!=nullptr && password!=nullptr) { + WiFi.begin(ssid, password); + while (WiFi.status() != WL_CONNECTED) { + delay(500); + } + } + + // Performance Hack + //client.setNoDelay(true); + esp_wifi_set_ps(WIFI_PS_NONE); + + } +}; + +enum class RecordType : uint8_t { Undefined, Begin, Send, Receive, End }; +enum class AudioType : uint8_t { PCM, MP3, AAC, WAV }; +enum class TransmitRole : uint8_t { Sender, Receiver }; + +/// Common Header for all records +struct AudioHeader { + AudioHeader() = default; + uint8_t app = 123; + RecordType rec = RecordType::Undefined; + uint16_t seq = 0; + // record counter + void increment() { + static uint16_t static_count = 0; + seq = static_count++; + } +}; + +/// Protocal Record To Start +struct AudioDataBegin : public AudioHeader { + AudioDataBegin() { rec = RecordType::Begin; } + AudioInfo info; + AudioType type = AudioType::PCM; +}; + +/// Protocol Record for Data +struct AudioSendData : public AudioHeader { + AudioSendData() { + rec = RecordType::Send; + } + uint16_t size = 0; +}; + +/// Protocol Record for Request +struct AudioConfirmDataToReceive : public AudioHeader { + AudioConfirmDataToReceive() { rec = RecordType::Receive; } + uint16_t size = 0; +}; + +/// Protocol Record for End +struct AudioDataEnd : public AudioHeader { + AudioDataEnd() { rec = RecordType::End; } +}; + +/** + * @brief Audio Writer which is synchronizing the amount of data + * that can be processed with the AudioReceiver + * @author Phil Schatzmann + * @copyright GPLv3 + */ +class AudioSyncWriter : public AudioOutput { + public: + AudioSyncWriter(Stream &dest) { p_dest = &dest; } + + bool begin(AudioInfo &info, AudioType type) { + is_sync = true; + AudioDataBegin begin; + begin.info = info; + begin.type = type; + begin.increment(); + int write_len = sizeof(begin); + int len = p_dest->write((const uint8_t *)&begin, write_len); + return len == write_len; + } + + size_t write(const uint8_t *data, size_t len) override { + int written_len = 0; + int open_len = len; + AudioSendData send; + while (written_len < len) { + int available_to_write = waitForRequest(); + size_t to_write_len = DEFAULT_BUFFER_SIZE; + to_write_len = min(open_len, available_to_write); + send.increment(); + send.size = to_write_len; + p_dest->write((const uint8_t *)&send, sizeof(send)); + int w = p_dest->write(data + written_len, to_write_len); + written_len += w; + open_len -= w; + } + return written_len; + } + + int availableForWrite() override { return available_to_write; } + + void end() { + AudioDataEnd end; + end.increment(); + p_dest->write((const uint8_t *)&end, sizeof(end)); + } + + protected: + Stream *p_dest; + int available_to_write = 1024; + bool is_sync; + + /// Waits for the data to be available + void waitFor(int size) { + while (p_dest->available() < size) { + delay(10); + } + } + + int waitForRequest() { + AudioConfirmDataToReceive rcv; + size_t rcv_len = sizeof(rcv); + waitFor(rcv_len); + p_dest->readBytes((uint8_t *)&rcv, rcv_len); + return rcv.size; + } +}; + +/** + * @brief Receving Audio Data over the wire and requesting for more data when + * done to synchronize the processing with the sender. The audio data is + * processed by the EncodedAudioStream; If you have multiple readers, only one + * receiver should be used as confirmer! + * @author Phil Schatzmann + * @copyright GPLv3 + */ +class AudioSyncReader : public AudioStream { + public: + AudioSyncReader(Stream &in, EncodedAudioStream &out, + bool isConfirmer = true) { + p_in = ∈ + p_out = &out; + is_confirmer = isConfirmer; + } + + size_t copy() { + int processed = 0; + int header_size = sizeof(header); + waitFor(header_size); + readBytes((uint8_t *)&header, header_size); + + switch (header.rec) { + case RecordType::Begin: + audioDataBegin(); + break; + case RecordType::End: + audioDataEnd(); + break; + case RecordType::Send: + processed = receiveData(); + break; + } + return processed; + } + + protected: + Stream *p_in; + EncodedAudioStream *p_out; + AudioConfirmDataToReceive req; + AudioHeader header; + AudioDataBegin begin; + size_t available = 0; // initial value + bool is_started = false; + bool is_confirmer; + int last_seq = 0; + + /// Starts the processing + void audioDataBegin() { + readProtocol(&begin, sizeof(begin)); + p_out->begin(); + p_out->setAudioInfo(begin.info); + requestData(); + } + + /// Ends the processing + void audioDataEnd() { + AudioDataEnd end; + readProtocol(&end, sizeof(end)); + p_out->end(); + } + + // Receives audio data + int receiveData() { + AudioSendData data; + readProtocol(&data, sizeof(data)); + available = data.size; + // receive and process audio data + waitFor(available); + int max_gap = 10; + if (data.seq > last_seq || + (data.seq < max_gap && last_seq >= (32767 - max_gap))) { + uint8_t buffer[available]; + p_in->readBytes((uint8_t *)buffer, available); + p_out->write((const uint8_t *)buffer, available); + // only one reader should be used as confirmer + if (is_confirmer) { + requestData(); + } + last_seq = data.seq; + } + return available; + } + + /// Waits for the data to be available + void waitFor(int size) { + while (p_in->available() < size) { + delay(10); + } + } + + /// Request new data from writer + void requestData() { + req.size = p_out->availableForWrite(); + req.increment(); + p_in->write((const uint8_t *)&req, sizeof(req)); + p_in->flush(); + } + + /// Reads the protocol record + void readProtocol(AudioHeader *data, int len) { + const static int header_size = sizeof(header); + memcpy(data, &header, header_size); + int read_size = len - header_size; + waitFor(read_size); + readBytes((uint8_t *)data + header_size, read_size); + } +}; + +/** + * @brief Configure Throttle setting + * @author Phil Schatzmann + * @copyright GPLv3 + */ +struct ThrottleConfig : public AudioInfo { + ThrottleConfig() { + sample_rate = 44100; + bits_per_sample = 16; + channels = 2; + } + int correction_ms = 0; +}; + +/** + * @brief Throttle the sending of the audio data to limit it to the indicated + * sample rate. + * @author Phil Schatzmann + * @copyright GPLv3 + */ +class Throttle { + public: + Throttle() = default; + + ThrottleConfig defaultConfig() { + ThrottleConfig c; + return c; + } + + void begin(AudioInfo info) { + ThrottleConfig cfg; + cfg.copyFrom(info); + begin(cfg); + } + + void begin(ThrottleConfig info) { + this->info = info; + bytesPerSample = info.bits_per_sample / 8 * info.channels; + } + + // starts the timing + void startDelay() { start_time = millis(); } + + // delay + void delayBytes(size_t bytes) { delaySamples(bytes / bytesPerSample); } + + // delay + void delaySamples(size_t samples) { + int durationMsEff = millis() - start_time; + int durationToBe = (samples * 1000) / info.sample_rate; + int waitMs = durationToBe - durationMsEff + info.correction_ms; + if (waitMs > 0) { + delay(waitMs); + } + } + + protected: + unsigned long start_time; + ThrottleConfig info; + int bytesPerSample; +}; + +} // namespace audio_tools diff --git a/src/AudioLibs/Desktop/File.h b/src/AudioLibs/Desktop/File.h new file mode 100644 index 0000000000..d9487cb778 --- /dev/null +++ b/src/AudioLibs/Desktop/File.h @@ -0,0 +1,235 @@ +#pragma once +#include "AudioLibs/Desktop/NoArduino.h" +#include +#include +#include +#include + +namespace audio_tools { + +enum FileMode { FILE_READ='r', FILE_WRITE='w', FILE_APPEND='a'}; +enum SeekMode { SeekSet = 0, SeekCur = 1, SeekEnd = 2 }; + +/** + * @brief Arduino File support using std::fstream + * @author Phil Schatzmann + * @copyright GPLv3 + */ +class File : public Stream { + public: + File() = default; + File(const char* fn) { + open(fn, FILE_READ); + } + + File(File &file){ + open(file.name(), FILE_READ); + } + + File& operator =(File file){ + open(file.name(), FILE_READ); + return *this; + } + + + void open(const char* name, FileMode mode){ + file_path = name; + switch(mode){ + case FILE_READ: + stream.open(name, stream.binary | stream.in); + is_read = true; + break; + case FILE_WRITE: + stream.open(name, stream.binary | stream.trunc | stream.out); + is_read = false; + break; + case FILE_APPEND: + stream.open(name, stream.binary | stream.out); + is_read = false; + break; + } + } + + virtual bool begin(){ + // move to beginning + return seek(0); + } + + virtual void end() { + stream.close(); + } + + virtual int print(const char* str)override{ + stream << str; + return strlen(str); + } + + virtual int println(const char* str="")override{ + stream << str << "\n"; + return strlen(str)+1; + } + + virtual int print(int number)override{ + char buffer[80]; + int len = sprintf(buffer, "%d", number); + print(buffer); + return len; + } + + virtual int println(int number){ + char buffer[80]; + int len = sprintf(buffer, "%d\n", number); + print(buffer); + return len; + } + + virtual void flush() override { + stream.flush(); + } + + virtual void write(uint8_t* str, int len) { + stream.write((const char*)str, len); + } + + virtual size_t write(int32_t value){ + stream.put(value); + return 1; + } + + virtual size_t write(uint8_t value)override{ + stream.put(value); + return 1; + } + + virtual int available() override{ + return size()-position(); + }; + + virtual int read() override{ + return stream.get(); + } + + virtual size_t readBytes(uint8_t* buffer, size_t len) override { + stream.read((char*)buffer, len); + return stream?len : stream.gcount(); + } + + virtual int peek() override { + return stream.peek(); + } + + bool seek(uint32_t pos, SeekMode mode){ + if (is_read){ + switch(mode){ + case SeekSet: + stream.seekg(pos, ios_base::beg); + break; + case SeekCur: + stream.seekg(pos, std::ios_base::cur); + break; + case SeekEnd: + stream.seekg(pos, std::ios_base::end); + break; + } + } else { + switch(mode){ + case SeekSet: + stream.seekp(pos, ios_base::beg); + break; + case SeekCur: + stream.seekp(pos, std::ios_base::cur); + break; + case SeekEnd: + stream.seekp(pos, std::ios_base::end); + break; + } + } + return stream.fail()==false; + } + + bool seek(uint32_t pos){ + return seek(pos, SeekSet); + } + + size_t position() { + return stream.tellp(); + } + + size_t size() const { + std::streampos fsize = 0; + std::ifstream file( file_path, std::ios::binary ); + + fsize = file.tellg(); + file.seekg( 0, std::ios::end ); + fsize = file.tellg() - fsize; + file.close(); + + return fsize; + } + + void close() { + stream.close(); + } + + const char* name() { + return file_path; + } + + protected: + std::fstream stream; + bool is_read=true; + const char* file_path=nullptr; +}; + +/** + * @brief Eumlate FS using C++ or Posix functions + * @author Phil Schatzmann + * @copyright GPLv3 + */ +class FS { +public: + File open(const char* path, FileMode mode = FILE_READ){ + File file; + file.open(path, mode); + return file; + } + File open(const std::string& path, FileMode mode = FILE_READ){ + return open(path.c_str(), mode); + } + bool exists(const char* path){ + struct stat buffer; + return (stat (path, &buffer) == 0); + } + bool exists(const std::string& path){ + return exists(path.c_str()); + } + bool remove(const char* path) { + return ::remove(path)==0; + } + bool remove(const std::string& path){ + return remove(path.c_str()); + } + bool rename(const char* pathFrom, const char* pathTo) { + return ::rename(pathFrom, pathTo) == 0; + } + bool rename(const std::string& pathFrom, const std::string& pathTo){ + return rename(pathFrom.c_str(), pathTo.c_str()); + } + bool mkdir(const char *path) { + return ::mkdir(path, 0777)==0; + } + bool mkdir(const std::string &path){ + return mkdir(path.c_str()); + } + bool rmdir(const char *path){ + return ::rmdir(path)==0; + } + bool rmdir(const std::string &path){ + return rmdir(path.c_str()); + } +}; + +static FS SD; +static FS SDFAT; + +} diff --git a/src/AudioTools/AudioLibs/Desktop/JupyterAudio.h b/src/AudioLibs/Desktop/JupyterAudio.h similarity index 94% rename from src/AudioTools/AudioLibs/Desktop/JupyterAudio.h rename to src/AudioLibs/Desktop/JupyterAudio.h index 7d5810c285..9faa901d61 100644 --- a/src/AudioTools/AudioLibs/Desktop/JupyterAudio.h +++ b/src/AudioLibs/Desktop/JupyterAudio.h @@ -1,8 +1,8 @@ #pragma once -#include "AudioTools/AudioLibs/Desktop/NoArduino.h" -#include "AudioTools/CoreAudio/AudioStreams.h" -#include "AudioTools/CoreAudio/AudioOutput.h" -#include "AudioTools/AudioCodecs/CodecWAV.h" +#include "AudioLibs/Desktop/NoArduino.h" +#include "AudioTools/AudioStreams.h" +#include "AudioTools/AudioOutput.h" +#include "AudioCodecs/CodecWAV.h" #include #include #include @@ -21,9 +21,9 @@ class FileOutput : public Print { FileOutput(std::fstream &stream){ p_audio_stream = &stream; } - size_t write(const uint8_t *data, size_t len) override { - p_audio_stream->write((const char*)data,len); - return len; + size_t write(const uint8_t *buffer, size_t size) override { + p_audio_stream->write((const char*)buffer,size); + return size; } int availableForWrite() override { return 1024; diff --git a/src/AudioTools/AudioLibs/Desktop/Main.h b/src/AudioLibs/Desktop/Main.h similarity index 86% rename from src/AudioTools/AudioLibs/Desktop/Main.h rename to src/AudioLibs/Desktop/Main.h index 07e84f485b..847f204080 100644 --- a/src/AudioTools/AudioLibs/Desktop/Main.h +++ b/src/AudioLibs/Desktop/Main.h @@ -1,9 +1,8 @@ /** * Generic main for desktop arduino emulation */ -#ifndef NO_MAIN - #pragma once + void loop(); void setup(); @@ -12,6 +11,4 @@ int main (void) { while(true){ loop(); } - } - -#endif \ No newline at end of file + } \ No newline at end of file diff --git a/src/AudioLibs/Desktop/Millis.h b/src/AudioLibs/Desktop/Millis.h new file mode 100644 index 0000000000..8fe178a7e4 --- /dev/null +++ b/src/AudioLibs/Desktop/Millis.h @@ -0,0 +1,27 @@ +#pragma once +#include "AudioLibs/Desktop/NoArduino.h" +#include + +#define DESKTOP_MILLIS_DEFINED + +namespace audio_tools { + +/// Waits for the indicated milliseconds +void delay(uint64_t ms) { + //std::this_thread::sleep_for(std::chrono::milliseconds(ms)); + auto end = millis()+ms; + while(millis()<=end); +} + +/// Returns the milliseconds since the start +uint64_t millis(){ + using namespace std::chrono; + // Get current time with precision of milliseconds + auto now = time_point_cast(system_clock::now()); + // sys_milliseconds is type time_point + using sys_milliseconds = decltype(now); + // Convert time_point to signed integral type + return now.time_since_epoch().count(); +} + +} \ No newline at end of file diff --git a/src/AudioLibs/Desktop/NoArduino.h b/src/AudioLibs/Desktop/NoArduino.h new file mode 100644 index 0000000000..730a25743d --- /dev/null +++ b/src/AudioLibs/Desktop/NoArduino.h @@ -0,0 +1,203 @@ +#pragma once +/** + * @file NoArduino.h + * @author Phil Schatzmann + * @brief If you want to use the framework w/o Arduino you need to provide the implementation + * of a couple of classes and methods! + * @version 0.1 + * @date 2022-09-19 + * + * @copyright Copyright (c) 2022 + * + */ + +#include +#include // std::max +#include +#include +#include +#include +#include + +#define PSTR(fmt) fmt +#ifndef PI +# define PI 3.14159265359f +#endif + +#ifndef INPUT +# define INPUT 0x0 +#endif +#ifndef OUTPUT +# define OUTPUT 0x1 +#endif +#ifndef INPUT_PULLUP +# define INPUT_PULLUP 0x2 +#endif + +#ifndef HIGH +#define HIGH 0x1 +# endif +#ifndef LOW +# define LOW 0x0 +#endif + + +using namespace std; + +enum PrintCharFmt {DEC, HEX}; + +namespace audio_tools { + +class Print { +public: +#ifndef DOXYGEN + virtual size_t write(uint8_t ch){ + // not implememnted: to be overritten + return 0; + } + + virtual size_t write(const char *str) { + return write((const uint8_t *)str, strlen(str)); + } + + virtual size_t write(const char *buffer, size_t size) { + return write((const uint8_t *)buffer, size); + } + + virtual int print(const char* msg){ + int result = strlen(msg); + return write(msg, result); + } + + virtual int println(const char* msg="") { + int result = print(msg); + write('\n'); + return result+1; + } + + virtual int print(int number){ + char buffer[80]; + snprintf(buffer,80,"%d", number); + return print(buffer); + } + + virtual int print(char c, PrintCharFmt spec){ + char result[3]; + switch(spec){ + case DEC: + snprintf(result, 3,"%c", c); + return print(result); + case HEX: + snprintf(result, 3,"%x", c & 0xff); + return print(result); + } + return -1; + } + +#endif + + virtual size_t write(const uint8_t *buffer, size_t size){ + if (buffer == nullptr) return 0; + for (size_t j=0;j +#include @@ -244,11 +241,7 @@ DynArray ::DynArray (long size) assert (size >= 0); if (size > 0) { -#ifdef FFT_CUSTOM_ALLOC - _data_ptr = FFT_CUSTOM_ALLOC.createArray(size); -#else _data_ptr = new DataType [size]; -#endif _len = size; } } @@ -258,11 +251,7 @@ DynArray ::DynArray (long size) template DynArray ::~DynArray () { -#ifdef FFT_CUSTOM_ALLOC - FFT_CUSTOM_ALLOC.removeArray(_data_ptr, _len); -#else delete [] _data_ptr; -#endif _data_ptr = 0; _len = 0; } @@ -284,19 +273,12 @@ void DynArray ::resize (long size) if (size > 0) { DataType * old_data_ptr = _data_ptr; -#ifdef FFT_CUSTOM_ALLOC - DataType * tmp_data_ptr = FFT_CUSTOM_ALLOC.createArray(size); -#else DataType * tmp_data_ptr = new DataType [size]; -#endif + _data_ptr = tmp_data_ptr; _len = size; -#ifdef FFT_CUSTOM_ALLOC - FFT_CUSTOM_ALLOC.removeArray(old_data_ptr, _len); -#else delete [] old_data_ptr; -#endif } } @@ -479,11 +461,8 @@ To Public License, Version 2, as published by Sam Hocevar. See /*\\ INCLUDE FILES \\\\\\\\\\\\\\\\\\\\\\\\\\\\*/ -#ifdef AVR -# include -#else -# include -#endif + +#include namespace std { } @@ -716,6 +695,13 @@ To Public License, Version 2, as published by Sam Hocevar. See +/*\\ INCLUDE FILES \\\\\\\\\\\\\\\\\\\\\\\\\\\\*/ + +#include +#include + + + namespace ffft { @@ -1756,6 +1742,9 @@ To Public License, Version 2, as published by Sam Hocevar. See /*\\ INCLUDE FILES \\\\\\\\\\\\\\\\\\\\\\\\\\\\*/ +#include + + namespace ffft { @@ -1954,6 +1943,9 @@ To Public License, Version 2, as published by Sam Hocevar. See /*\\ INCLUDE FILES \\\\\\\\\\\\\\\\\\\\\\\\\\\\*/ +#include + + namespace ffft { @@ -1982,11 +1974,7 @@ DynArray ::DynArray (long size) assert (size >= 0); if (size > 0) { -#ifdef FFT_CUSTOM_ALLOC - _data_ptr = FFT_CUSTOM_ALLOC.createArray(size); -#else _data_ptr = new DataType [size]; -#endif _len = size; } } @@ -1996,11 +1984,7 @@ DynArray ::DynArray (long size) template DynArray ::~DynArray () { -#ifdef FFT_CUSTOM_ALLOC - FFT_CUSTOM_ALLOC.removeArray(_data_ptr, _len); -#else delete [] _data_ptr; -#endif _data_ptr = 0; _len = 0; } @@ -2022,18 +2006,12 @@ void DynArray ::resize (long size) if (size > 0) { DataType * old_data_ptr = _data_ptr; -#ifdef FFT_CUSTOM_ALLOC -#else DataType * tmp_data_ptr = new DataType [size]; -#endif _data_ptr = tmp_data_ptr; _len = size; -#ifdef FFT_CUSTOM_ALLOC -#else delete [] old_data_ptr; -#endif } } @@ -3690,6 +3668,9 @@ inline float * FFTRealSelect <0>::sel_bin (float *e_ptr, float *o_ptr) /*\\ EOF \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*/ +#include +#include + namespace std { } diff --git a/src/AudioTools/AudioLibs/FFT/FFTWindows.h b/src/AudioLibs/FFT/FFTWindows.h similarity index 70% rename from src/AudioTools/AudioLibs/FFT/FFTWindows.h rename to src/AudioLibs/FFT/FFTWindows.h index 8d9f33690a..0d7d9ff41d 100644 --- a/src/AudioTools/AudioLibs/FFT/FFTWindows.h +++ b/src/AudioLibs/FFT/FFTWindows.h @@ -11,7 +11,6 @@ #pragma once #include -#include "AudioTools/CoreAudio/AudioBasic/Collections/Vector.h" namespace audio_tools { @@ -29,23 +28,18 @@ class WindowFunction { virtual void begin(int samples) { this->samples_minus_1 = -1.0f + samples; this->i_samples = samples; - this->i_half_samples = samples / 2; + this->i_half_samples = samples / 2 - 1; } - /// Provides the multipication factor at the indicated position. The result is - /// symetrically mirrored around the center + /// Provides the multipication factor at the indicated position. The result is symetirically mirrored around the center inline float factor(int idx) { - assert(i_half_samples == i_samples / 2); - float result = idx <= i_half_samples ? factor_internal(idx) - : factor_internal(i_samples - idx - 1); - return result > 1.0f ? 1.0f : result; + float result = idx < i_half_samples ? factor_internal(idx) : factor_internal(i_samples-idx-1); + return result>1.0f ? 1.0f : result; } /// Provides the number of samples (fft length) inline int samples() { return i_samples; } - virtual const char* name() = 0; - protected: float samples_minus_1 = 0.0f; int i_samples = 0; @@ -61,6 +55,7 @@ class WindowFunction { inline float ratio(int idx) { return (static_cast(idx)) / samples_minus_1; } + }; /** @@ -72,24 +67,18 @@ class WindowFunction { class BufferedWindow : public WindowFunction { public: BufferedWindow(WindowFunction* wf) { p_wf = wf; } - - const char* name() override { - static char buffer[80] = "Buffered "; - strncpy(buffer + 9, p_wf->name(), 69); - return buffer; - } + BufferedWindow(BufferedWindow const&) = delete; + BufferedWindow& operator=(BufferedWindow const&) = delete; virtual void begin(int samples) override { // process only if there is a change WindowFunction::begin(samples); if (p_wf->samples() != samples) { p_wf->begin(samples); - int to_be_size = i_half_samples + 1; - if (buffer.size() != to_be_size) { - buffer.resize(to_be_size); - for (int j = 0; j <= i_half_samples; j++) { - buffer[j] = p_wf->factor(j); - } + len = samples / 2; + buffer.resize(len); + for (int j = 0; j < len; j++) { + buffer[j] = p_wf->factor(j); } } } @@ -97,10 +86,10 @@ class BufferedWindow : public WindowFunction { protected: WindowFunction* p_wf = nullptr; Vector buffer{0}; + int len; float factor_internal(int idx) override { - if (idx < 0 || idx > i_half_samples) return 0.0; - return buffer[idx]; + return idx < len ? buffer[idx] : buffer[i_samples - idx]; } }; @@ -112,11 +101,7 @@ class BufferedWindow : public WindowFunction { class Rectange : public WindowFunction { public: Rectange() = default; - float factor_internal(int idx) { - if (idx < 0 || idx >= i_samples) return 0; - return 1.0f; - } - const char* name() { return "Rectange"; } + float factor_internal(int idx) { return 1.0f; } }; /** @@ -130,7 +115,6 @@ class Hamming : public WindowFunction { float factor_internal(int idx) { return 0.54f - (0.46f * cos(twoPi * ratio(idx))); } - const char* name() { return "Hamming"; } }; /** @@ -141,8 +125,6 @@ class Hamming : public WindowFunction { class Hann : public WindowFunction { public: Hann() = default; - const char* name() { return "Hann"; } - float factor_internal(int idx) { return 0.54f * (1.0f - cos(twoPi * ratio(idx))); } @@ -156,11 +138,10 @@ class Hann : public WindowFunction { class Triangle : public WindowFunction { public: Triangle() = default; - const char* name() { return "Triangle"; } float factor_internal(int idx) { return 1.0f - ((2.0f * fabs((idx - 1) - - (static_cast(i_samples - 1) / 2.0f))) / - samples_minus_1); + (static_cast(i_samples - 1) / 2.0f))) / + samples_minus_1); } }; @@ -173,7 +154,6 @@ class Triangle : public WindowFunction { class Nuttall : public WindowFunction { public: Nuttall() = default; - const char* name() override { return "Nuttall"; } float factor_internal(int idx) override { float r = ratio(idx); return 0.355768f - (0.487396f * (cos(twoPi * r))) + @@ -190,8 +170,7 @@ class Nuttall : public WindowFunction { class Blackman : public WindowFunction { public: Blackman() = default; - const char* name() override { return "Blackman"; } - float factor_internal(int idx) override { + float factor_internal(int idx)override { float r = ratio(idx); return 0.42323f - (0.49755f * (cos(twoPi * r))) + (0.07922f * (cos(fourPi * r))); @@ -206,8 +185,7 @@ class Blackman : public WindowFunction { class BlackmanNuttall : public WindowFunction { public: BlackmanNuttall() = default; - const char* name() override { return "BlackmanNuttall"; } - float factor_internal(int idx) override { + float factor_internal(int idx) override{ float r = ratio(idx); return 0.3635819f - (0.4891775f * (cos(twoPi * r))) + (0.1365995f * (cos(fourPi * r))) - (0.0106411f * (cos(sixPi * r))); @@ -222,8 +200,7 @@ class BlackmanNuttall : public WindowFunction { class BlackmanHarris : public WindowFunction { public: BlackmanHarris() = default; - const char* name() override { return "BlackmanHarris"; } - float factor_internal(int idx) override { + float factor_internal(int idx) override{ float r = ratio(idx); return 0.35875f - (0.48829f * (cos(twoPi * r))) + (0.14128f * (cos(fourPi * r))) - (0.01168f * (cos(sixPi * r))); @@ -238,8 +215,7 @@ class BlackmanHarris : public WindowFunction { class FlatTop : public WindowFunction { public: FlatTop() = default; - const char* name() override { return "FlatTop"; } - float factor_internal(int idx) override { + float factor_internal(int idx) override{ float r = ratio(idx); return 0.2810639f - (0.5208972f * cos(twoPi * r)) + (0.1980399f * cos(fourPi * r)); @@ -254,11 +230,9 @@ class FlatTop : public WindowFunction { class Welch : public WindowFunction { public: Welch() = default; - const char* name() override { return "Welch"; } - float factor_internal(int idx) override { - float tmp = - (((idx - 1) - samples_minus_1 / 2.0f) / (samples_minus_1 / 2.0f)); - return 1.0f - (tmp * tmp); + float factor_internal(int idx) override{ + float tmp = (((idx - 1) - samples_minus_1 / 2.0f) / (samples_minus_1 / 2.0f)); + return 1.0f - (tmp*tmp); } }; diff --git a/src/AudioTools/Disk/FileLoop.h b/src/AudioLibs/FileLoop.h similarity index 82% rename from src/AudioTools/Disk/FileLoop.h rename to src/AudioLibs/FileLoop.h index 737679ce3a..1fc9d68f14 100644 --- a/src/AudioTools/Disk/FileLoop.h +++ b/src/AudioLibs/FileLoop.h @@ -1,7 +1,6 @@ #pragma once -#include "AudioTools/CoreAudio/BaseStream.h" -#include "AudioTools/CoreAudio/AudioBasic/StrView.h" +#include "AudioTools/AudioStreams.h" #ifdef ARDUINO # include "FS.h" # define READTYPE char @@ -21,35 +20,25 @@ namespace audio_tools { * @author Phil Schatzmann * @copyright GPLv3 */ -template class FileLoopT : public BaseStream { +template class FileLoopT : public AudioStream { public: FileLoopT() = default; - FileLoopT(FileType file, int count = -1, int rewindPos = -1) { + FileLoopT(FileType file, int count = -1, int rewindPos = 0) { setFile(file); setLoopCount(count); setStartPos(rewindPos); } // restarts the file from the beginning - bool begin() { + bool begin() override { TRACEI(); - // automatic determination of start pos - if (start_pos <= 0){ - current_file.seek(0); - char tmp[5] = {0}; - current_file.readBytes(tmp, 4); - // for wav files remove header - start_pos = StrView(tmp).equals("RIFF") ? 44 : 0; - current_file.seek(0); - } else { - current_file.seek(start_pos); - } + current_file.seek(start_pos); size_open = total_size; return current_file; } // closes the file - void end() { + void end() override { TRACEI(); current_file.close(); } @@ -106,9 +95,8 @@ template class FileLoopT : public BaseStream { int result1 = current_file.readBytes((READTYPE *)data, copy_len); int result2 = 0; int open = copy_len - result1; - if (isLoopActive() && open > 0) { - if (start_pos < 0) start_pos = 0; - LOGI("seek %d", start_pos); + if (isLoopActive() && open>0) { + LOGI("seek 0"); // looping logic -> rewind to beginning: read step 2 current_file.seek(start_pos); // notify user @@ -136,10 +124,8 @@ template class FileLoopT : public BaseStream { /// @return true as long as we are looping bool isLoopActive() { return loop_count > 0 || loop_count == -1; } - size_t write(const uint8_t* data, size_t len) { return current_file.write(data, len);} - protected: - int start_pos = -1; + int start_pos = 0; int loop_count = -1; int size_open = -1; int total_size = -1; diff --git a/src/AudioTools/AudioLibs/Jupyter.h b/src/AudioLibs/Jupyter.h similarity index 72% rename from src/AudioTools/AudioLibs/Jupyter.h rename to src/AudioLibs/Jupyter.h index cb5cffa530..20ef76a6c0 100644 --- a/src/AudioTools/AudioLibs/Jupyter.h +++ b/src/AudioLibs/Jupyter.h @@ -1,4 +1,4 @@ #pragma once -#include "Desktop/Time.h" +#include "Desktop/Millis.h" #include "Desktop/JupyterAudio.h" #include "Desktop/File.h" diff --git a/src/AudioTools/AudioLibs/LEDOutput.h b/src/AudioLibs/LEDOutput.h similarity index 62% rename from src/AudioTools/AudioLibs/LEDOutput.h rename to src/AudioLibs/LEDOutput.h index 1349eb91aa..9276cf9ba0 100644 --- a/src/AudioTools/AudioLibs/LEDOutput.h +++ b/src/AudioLibs/LEDOutput.h @@ -1,21 +1,20 @@ #pragma once +#include "AudioLibs/AudioFFT.h" #include -#include "AudioTools/CoreAudio/AudioBasic/Collections/Vector.h" -#include "AudioTools/AudioLibs/AudioFFT.h" -#include "FFTDisplay.h" - namespace audio_tools { - class LEDOutput; struct LEDOutputConfig; +LEDOutput *selfLEDOutput = nullptr; // default callback function which implements led update void fftLEDOutput(LEDOutputConfig *cfg, LEDOutput *matrix); // led update for volume void volumeLEDOutput(LEDOutputConfig *cfg, LEDOutput *matrix); // default color CHSV getDefaultColor(int x, int y, int magnitude); +// fft mutex +Mutex fft_mux; /** * LED Matrix Configuration. Provide the number of leds in x and y direction and @@ -34,32 +33,37 @@ struct LEDOutputConfig { /// fftLEDOutput() void (*update_callback)(LEDOutputConfig *cfg, LEDOutput *matrix) = nullptr; /// Update the leds only ever nth call - int update_frequency = 1; // update every call + int update_frequency = 1; // update every call bool is_serpentine_layout = true; bool is_matrix_vertical = true; + /// start bin which is displayed + int fft_start_bin = 0; + /// group result by adding subsequent bins + int fft_group_bin = 1; /// Influences the senitivity - int max_magnitude = 700; + int fft_max_magnitude = 700; }; /** - * @brief LED output using the FastLED library. + * @brief LEDOutput using the FastLED library. You write the data to the FFT Stream. + * This displays the result of the FFT to a LED matrix. + * @ingroup io * @author Phil Schatzmann */ class LEDOutput { - public: - /// @brief Default Constructor +public: LEDOutput() = default; - /// @brief Constructor for FFT scenario + /// @brief Default Constructor /// @param fft - LEDOutput(FFTDisplay &fft) { + LEDOutput(AudioFFTBase &fft) { + selfLEDOutput = this; p_fft = &fft; cfg.update_callback = fftLEDOutput; } - /// @brief Constructor for VolumeMeter scenario - /// @param vol - LEDOutput(VolumeMeter &vol) { + LEDOutput(VolumeOutput &vol) { + selfLEDOutput = this; p_vol = &vol; cfg.update_callback = volumeLEDOutput; } @@ -70,6 +74,10 @@ class LEDOutput { /// Setup Led matrix bool begin(LEDOutputConfig config) { cfg = config; + // if (!*p_fft) { + // LOGE("fft not started"); + // return false; + // } if (ledCount() == 0) { LOGE("x or y == 0"); return false; @@ -82,14 +90,20 @@ class LEDOutput { } // clear LED - FastLED.clear(); // clear all pixel data + FastLED.clear(); // clear all pixel data if (p_fft != nullptr) { - p_fft->begin(); + // assign fft callback + AudioFFTConfig &fft_cfg = p_fft->config(); + fft_cfg.callback = fftCallback; + + // number of bins + magnitudes.resize(p_fft->size()); + for (int j = 0; j < p_fft->size(); j++) { + magnitudes[j] = 0; + } } - max_column = -1; - return true; } @@ -114,95 +128,77 @@ class LEDOutput { if (cfg.update_callback != nullptr && count++ % cfg.update_frequency == 0) { // use custom update logic defined in config cfg.update_callback(&cfg, this); - } else { - display(); } } /// Determine the led with the help of the x and y pos CRGB &ledXY(uint8_t x, uint8_t y) { - if (x > cfg.x) x = cfg.x - 1; - if (x < 0) x = 0; - if (y > cfg.y) y = cfg.y - 1; - if (y < 0) y = 0; + if (x > cfg.x) + x = cfg.x - 1; + if (x < 0) + x = 0; + if (y > cfg.y) + y = cfg.y - 1; + if (y < 0) + y = 0; int index = xy(x, y); return leds[index]; } /// Determine the led with the help of the index pos CRGB &led(uint8_t index) { - if (index > cfg.x * cfg.y) return not_valid; + if (index > cfg.x * cfg.y) + return not_valid; return leds[index]; } - /// Update the indicated column with the indicated bar - void setColumnBar(int x, int currY) { - // update vertical bar - for (uint8_t y = 0; y < currY; y++) { - // determine color - CHSV color = cfg.color_callback(x, y, currY); - // update LED - ledXY(x, y) = color; - } - for (uint8_t y = currY; y < cfg.y; y++) { - ledXY(x, y) = CRGB::Black; - } - if (x > max_column) max_column = x; - } - - /// Update the last column with the indicated bar - void setColumnBar(int currY) { setColumnBar(cfg.x - 1, currY); } - - /// Update the last column with the indicated bar - void addColumnBar(int currY) { - max_column++; - if (max_column >= cfg.x) { - addEmptyColumn(); - } - if (max_column > cfg.x - 1) { - max_column = cfg.x - 1; + /// Returns the magnitude for the indicated led x position. We might + /// need to combine values from the magnitudes array if this is much bigger. + virtual float getMagnitude(int x) { + // get magnitude from fft + float total = 0; + for (int j = 0; j < cfg.fft_group_bin; j++) { + int idx = cfg.fft_start_bin + (x * cfg.fft_group_bin) + j; + if (idx >= magnitudes.size()) { + idx = magnitudes.size() - 1; + } + total += magnitudes[idx]; } - setColumnBar(max_column, currY); + return total / cfg.fft_group_bin; } - /// Provides access to the actual config object. E.g. to change the update logic - LEDOutputConfig &config() { return cfg; } - - ///Provodes the max magnitude for both the + /// @brief Provodes the max magnitude virtual float getMaxMagnitude() { // get magnitude from if (p_vol != nullptr) { return p_vol->volume(); } float max = 0; - if (p_fft != nullptr) { - for (int j = 0; j < cfg.x; j++) { - float value = p_fft->getMagnitude(j); - if (value > max) { - max = value; - } + for (int j = 0; j < cfg.x; j++) { + float value = getMagnitude(j); + if (value > max) { + max = value; } } return max; } - /// Update the led_matrix (calling FastLED.show(); - void display() { - FastLED.show(); + /// Update the indicated column with the indicated bar + void updateColumn(int x, int currY) { + // update vertical bar + for (uint8_t y = 0; y < currY; y++) { + // determine color + CHSV color = cfg.color_callback(x, y, currY); + // update LED + ledXY(x, y) = color; + } + for (uint8_t y = currY; y < cfg.y; y++) { + ledXY(x, y) = CRGB::Black; + } } - /// Provides acces to the FFTDisplay object - FFTDisplay &fftDisplay() { return *p_fft; } - - protected: - friend class AudioFFTBase; - CRGB not_valid; - Vector leds{0}; - LEDOutputConfig cfg; - VolumeMeter *p_vol = nullptr; - FFTDisplay *p_fft = nullptr; - uint64_t count = 0; - int max_column = -1; + /// Update the last column with the indicated bar + void updateColumn(int currY) { updateColumn(cfg.x - 1, currY); } /// Adds an empty column to the end shifting the content to the left void addEmptyColumn() { @@ -216,6 +212,22 @@ class LEDOutput { } } + /// Provides access to the actual config object. E.g. to change the update + /// logic + LEDOutputConfig &config() { return cfg; } + +protected: + friend class AudioFFTBase; + CRGB not_valid; + Vector leds{0}; + Vector magnitudes{0}; + LEDOutputConfig cfg; + AudioFFTBase *p_fft = nullptr; + VolumeOutput *p_vol = nullptr; + uint64_t count = 0; + + + uint16_t xy(uint8_t x, uint8_t y) { uint16_t i; @@ -237,7 +249,7 @@ class LEDOutput { // Even rows run forwards i = (y * cfg.x) + x; } - } else { // vertical positioning + } else { // vertical positioning if (x & 0x01) { i = cfg.y * (cfg.x - (x + 1)) + y; } else { @@ -248,17 +260,30 @@ class LEDOutput { return i; } + + /// callback method which provides updated data from fft + static void fftCallback(AudioFFTBase &fft) { + // just save magnitudes to be displayed + LockGuard guard(fft_mux); + for (int j = 0; j < fft.size(); j++) { + float value = fft.magnitude(j); + selfLEDOutput->magnitudes[j] = value; + } + }; }; /// Default update implementation which provides the fft result as "barchart" void fftLEDOutput(LEDOutputConfig *cfg, LEDOutput *matrix) { - // process horizontal - LockGuard guard(fft_mux); - for (int x = 0; x < cfg->x; x++) { - // max y determined by magnitude - int currY = matrix->fftDisplay().getMagnitudeScaled(x, cfg->y); - LOGD("x: %d, y: %d", x, currY); - matrix->setColumnBar(x, currY); + { + LockGuard guard(fft_mux); + // process horizontal + for (int x = 0; x < cfg->x; x++) { + // max y determined by magnitude + int currY = mapFloat(matrix->getMagnitude(x), 0, cfg->fft_max_magnitude, + 0.0f, static_cast(cfg->y)); + LOGD("x: %d, y: %d", x, currY); + matrix->updateColumn(x, currY); + } } FastLED.show(); } @@ -266,17 +291,17 @@ void fftLEDOutput(LEDOutputConfig *cfg, LEDOutput *matrix) { /// Default update implementation which provides the fft result as "barchart" void volumeLEDOutput(LEDOutputConfig *cfg, LEDOutput *matrix) { float vol = matrix->getMaxMagnitude(); - int currY = mapT(vol, 0, - cfg->max_magnitude, 0.0f, - static_cast(cfg->y)); - matrix->addColumnBar(currY); + int currY = mapFloat(matrix->getMagnitude(vol), 0, cfg->fft_max_magnitude, + 0.0f, static_cast(cfg->y)); + matrix->addEmptyColumn(); + matrix->updateColumn(currY); FastLED.show(); } -/// Default logic to update the color for the indicated x,y position +/// @brief Default logic to update the color for the indicated x,y position CHSV getDefaultColor(int x, int y, int magnitude) { int color = map(magnitude, 0, 7, 255, 0); - return CHSV(color, 255, 100); // blue CHSV(160, 255, 255 + return CHSV(color, 255, 100); // blue CHSV(160, 255, 255 } -} // namespace audio_tools \ No newline at end of file +} // namespace audio_tools \ No newline at end of file diff --git a/src/AudioTools/Disk/LegacyAudioSourceSDFAT.h b/src/AudioLibs/LegacyAudioSourceSDFAT.h similarity index 96% rename from src/AudioTools/Disk/LegacyAudioSourceSDFAT.h rename to src/AudioLibs/LegacyAudioSourceSDFAT.h index d3f8c4a2a4..058d3b2530 100644 --- a/src/AudioTools/Disk/LegacyAudioSourceSDFAT.h +++ b/src/AudioLibs/LegacyAudioSourceSDFAT.h @@ -1,8 +1,8 @@ #pragma once #include "AudioLogger.h" -#include "AudioTools/Disk/AudioSource.h" -#include "AudioTools/CoreAudio/AudioBasic/Str.h" +#include "AudioTools/AudioSource.h" +#include "AudioBasic/StrExt.h" #include #include namespace audio_tools { @@ -48,7 +48,7 @@ namespace audio_tools { class AudioSourceSDFAT : public AudioSource { public: /// Default constructor - AudioSourceSDFAT(const char* startFilePath = "/", const char* ext = ".mp3", int chipSelect = PIN_CS, int speedMHz = 10) { + AudioSourceSDFAT(const char* startFilePath = "/", const char* ext = ".mp3", int chipSelect = PIN_CS, int speedMHz = 2) { TRACED(); LOGI("SD chipSelect: %d", chipSelect); LOGI("SD speedMHz: %d", speedMHz); @@ -160,7 +160,7 @@ class AudioSourceSDFAT : public AudioSource { } char file_name[MAX_FILE_LEN]; file.getName(file_name, MAX_FILE_LEN); - StrView strFileName(file_name); + Str strFileName(file_name); bool result = strFileName.endsWithIgnoreCase(exension) && strFileName.matches(file_name_pattern); LOGD("-> isValidAudioFile: '%s': %d", file_name, result); return result; @@ -168,9 +168,9 @@ class AudioSourceSDFAT : public AudioSource { AudioFile getFileByPath(const char* path) { AudioFile dir; - StrView inPath(path); - Str strPath; - Str strfileName; + Str inPath(path); + StrExt strPath; + StrExt strfileName; int pos = inPath.lastIndexOf("/") + 1; strPath.substring(path, 0, pos); strfileName.substring(path, pos, inPath.length()); diff --git a/src/AudioTools/AudioLibs/MaximilianDSP.h b/src/AudioLibs/MaximilianDSP.h similarity index 70% rename from src/AudioTools/AudioLibs/MaximilianDSP.h rename to src/AudioLibs/MaximilianDSP.h index 9be7235b5c..3aba27d7a5 100644 --- a/src/AudioTools/AudioLibs/MaximilianDSP.h +++ b/src/AudioLibs/MaximilianDSP.h @@ -1,6 +1,5 @@ #pragma once -#include "AudioToolsConfig.h" -#include "AudioTools/CoreAudio/AudioTypes.h" +#include "AudioConfig.h" #include "maximilian.h" #include "libs/maxiClock.h" @@ -15,37 +14,35 @@ namespace audio_tools { * @brief AudioTools integration with Maximilian * @ingroup dsp */ -class Maximilian : public VolumeSupport { +class Maximilian { public: Maximilian(Print &out, int bufferSize=DEFAULT_BUFFER_SIZE, void (*callback)(maxi_float_t *channels)=play){ buffer_size = bufferSize; + p_buffer = new uint8_t[bufferSize]; p_sink = &out; this->callback = callback; } ~Maximilian() { + delete[] p_buffer; } /// Setup Maximilian with audio parameters void begin(AudioInfo cfg){ this->cfg = cfg; - buffer.resize(buffer_size); maxiSettings::setup(cfg.sample_rate, cfg.channels, DEFAULT_BUFFER_SIZE); } /// Defines the volume. The values are between 0.0 and 1.0 - bool setVolume(float f) override{ - if (f>1.0f){ - VolumeSupport::setVolume(1.0f); - return false; + void setVolume(float f){ + volume = f; + if (volume>1){ + volume = 1; } - if (f<0.0f){ - VolumeSupport::setVolume(0.0f); - return false; + if (volume<0){ + volume = 0; } - VolumeSupport::setVolume(f); - return true; } /// Copies the audio data from maximilian to the audio sink, Call this method from the Arduino Loop. @@ -53,21 +50,22 @@ class Maximilian : public VolumeSupport { // fill buffer with data maxi_float_t out[cfg.channels]; uint16_t samples = buffer_size / sizeof(uint16_t); - int16_t *p_samples = (int16_t *) buffer.data(); + int16_t *p_samples = (int16_t *)p_buffer; for (uint16_t j=0;jwrite(buffer.data(), buffer_size); + unsigned int result = p_sink->write(p_buffer, buffer_size); LOGI("bytes written %u", result) } protected: - Vector buffer; + uint8_t *p_buffer=nullptr; + float volume=1.0; int buffer_size=256; Print *p_sink=nullptr; AudioInfo cfg; diff --git a/src/AudioTools/AudioLibs/MemoryManager.h b/src/AudioLibs/MemoryManager.h similarity index 97% rename from src/AudioTools/AudioLibs/MemoryManager.h rename to src/AudioLibs/MemoryManager.h index f4f98723e7..6e68e75b77 100644 --- a/src/AudioTools/AudioLibs/MemoryManager.h +++ b/src/AudioLibs/MemoryManager.h @@ -13,7 +13,7 @@ namespace audio_tools { * to satisfy smaller allocation requests with internal memory and larger requests * with external memory. This sets the limit between the two, as well as generally * enabling allocation in external memory. - * @ingroup memorymgmt + * @ingroup tools */ class MemoryManager { public: diff --git a/src/AudioLibs/MiniAudioStream.h b/src/AudioLibs/MiniAudioStream.h new file mode 100644 index 0000000000..df0ba17634 --- /dev/null +++ b/src/AudioLibs/MiniAudioStream.h @@ -0,0 +1,216 @@ +#pragma once +/** + * @brief MiniAudio + * @author Phil Schatzmann + * @copyright GPLv3 + */ + +#include +#include +#include "AudioTools.h" + +#define MINIAUDIO_IMPLEMENTATION +#include "miniaudio.h" + +#define MA_BUFFER_COUNT 20 +#define MA_START_COUNT MA_BUFFER_COUNT-2 + +namespace audio_tools { +/** + * @brief Configuration for MiniAudio + * @author Phil Schatzmann + * @copyright GPLv3 + */ +class MiniAudioConfig : public AudioInfo { + public: + MiniAudioConfig() { + sample_rate = 44100; + channels = 2; + bits_per_sample = 16; + }; + MiniAudioConfig(const MiniAudioConfig &) = default; + MiniAudioConfig(const AudioInfo &in) { + sample_rate = in.sample_rate; + channels = in.channels; + bits_per_sample = in.bits_per_sample; + } + + bool is_input = false; + bool is_output = true; +}; + +/** + * @brief MiniAudio: https://miniaud.io/ + * @ingroup io + * @author Phil Schatzmann + * @copyright GPLv3 + */ +class MiniAudioStream : public AudioStream { + public: + MiniAudioStream() = default; + ~MiniAudioStream() { end(); }; + + MiniAudioConfig defaultConfig(RxTxMode mode = RXTX_MODE) { + MiniAudioConfig info; + info.sample_rate = 44100; + info.channels = 2; + info.bits_per_sample = 16; + switch (mode) { + case RX_MODE: + info.is_input = true; + info.is_output = false; + break; + case TX_MODE: + info.is_input = false; + info.is_output = true; + break; + case RXTX_MODE: + info.is_input = true; + info.is_output = true; + break; + default: + info.is_input = false; + info.is_output = false; + break; + } + return info; + } + + void setAudioInfo(AudioInfo in) override { + if (in.sample_rate != info.sample_rate || in.channels != info.channels || + in.bits_per_sample != info.bits_per_sample) { + end(); + begin(in); + } + } + + bool begin(MiniAudioConfig info) { + this->info = info; + this->config = info; + + if (info.is_output && !info.is_input) + config_ma = ma_device_config_init(ma_device_type_playback); + else if (!info.is_output && info.is_input) + config_ma = ma_device_config_init(ma_device_type_capture); + else if (info.is_output && info.is_input) + config_ma = ma_device_config_init(ma_device_type_duplex); + else if (!info.is_output && !info.is_input) + config_ma = ma_device_config_init(ma_device_type_loopback); + + + config_ma.playback.channels = info.channels; + config_ma.sampleRate = info.sample_rate; + config_ma.dataCallback = data_callback; + switch (info.bits_per_sample) { + case 16: + config_ma.playback.format = ma_format_s16; + break; + case 24: + config_ma.playback.format = ma_format_s24; + break; + case 32: + config_ma.playback.format = ma_format_s32; + break; + default: + LOGE("Invalid format"); + return false; + } + config_ma.pUserData = this; + + if (ma_device_init(NULL, &config_ma, &device_ma) != MA_SUCCESS) { + // Failed to initialize the device. + return false; + } + + // The device is sleeping by default so you'll need to start it manually. + ma_device_start(&device_ma); + + is_active = true; + return is_active; + } + + void end() { + is_active = false; + is_playing = false; + // This will stop the device so no need to do that manually. + ma_device_uninit(&device_ma); + // release buffer memory + delete (p_buffer_in); + p_buffer_in = nullptr; + delete (p_buffer_out); + p_buffer_out = nullptr; + } + + int availableForWrite() override { return p_buffer_out==nullptr? 0 : p_buffer_out->availableForWrite(); } + + size_t write(const uint8_t *data, size_t len) override { + if (p_buffer_out==nullptr) return 0; + LOGD("write: %zu", len); + //std::lock_guard qLock(mtxQueue); + size_t result = p_buffer_out->writeArray(data, len); + if (!is_playing && p_buffer_out->bufferCountFilled()>=MA_START_COUNT) { + is_playing = true; + } + //std::this_thread::yield(); + return result; + } + + int available() override { return p_buffer_in==nullptr ? 0 : p_buffer_in->available(); } + + size_t readBytes(uint8_t *data, size_t len) override { + if (p_buffer_in==nullptr) return 0; + LOGD("write: %zu", len); + //std::lock_guard qLock(mtxQueue); + return p_buffer_in->readArray(data, len); + } + + protected: + MiniAudioConfig config; + ma_device_config config_ma; + ma_device device_ma; + bool is_playing = false; + bool is_active = false; + bool is_buffers_setup = false; + NBuffer *p_buffer_out = nullptr; + NBuffer *p_buffer_in = nullptr; + //std::mutex mtxQueue; + + // In playback mode copy data to pOutput. In capture mode read data from + // pInput. In full-duplex mode, both pOutput and pInput will be valid and + // you can move data from pInput into pOutput. Never process more than + // frameCount frames. + + void setupBuffers(int size) { + if (is_buffers_setup) return; + if (p_buffer_out == nullptr && config.is_output) + p_buffer_out = new NBuffer(size, MA_BUFFER_COUNT); + if (p_buffer_in==nullptr && config.is_input) + p_buffer_in = new NBuffer(size, MA_BUFFER_COUNT); + is_buffers_setup = true; + } + + static void data_callback(ma_device *pDevice, void *pOutput, + const void *pInput, ma_uint32 frameCount) { + MiniAudioStream *self = (MiniAudioStream *)pDevice->pUserData; + AudioInfo cfg = self->audioInfo(); + + int bytes = frameCount * cfg.channels * cfg.bits_per_sample / 8; + self->setupBuffers(bytes); + + if (pInput) { + //std::unique_lock qLock(self->mtxQueue); + self->p_buffer_in->writeArray((uint8_t *)pInput, bytes); + } + + if (pOutput) { + memset(pOutput, 0, bytes); + if (self->is_playing ) { + //std::lock_guard qLock(self->mtxQueue); + self->p_buffer_out->readArray((uint8_t *)pOutput, bytes); + std::this_thread::yield(); + } + } + } +}; + +} // namespace audio_tools \ No newline at end of file diff --git a/src/AudioTools/AudioLibs/PortAudioStream.h b/src/AudioLibs/PortAudioStream.h similarity index 90% rename from src/AudioTools/AudioLibs/PortAudioStream.h rename to src/AudioLibs/PortAudioStream.h index cbde764035..5efe05cf9f 100644 --- a/src/AudioTools/AudioLibs/PortAudioStream.h +++ b/src/AudioLibs/PortAudioStream.h @@ -106,6 +106,7 @@ class PortAudioStream : public AudioStream { } // calculate frames + int bytes = info.bits_per_sample / 8; int buffer_frames = paFramesPerBufferUnspecified; //buffer_size / bytes / info.channels; // Open an audio I/O stream. @@ -113,7 +114,7 @@ class PortAudioStream : public AudioStream { err = Pa_OpenDefaultStream( &stream, info.is_input ? info.channels : 0, // no input channels info.is_output ? info.channels : 0, // no output - getFormat(), // format + getFormat(info.bits_per_sample), // format info.sample_rate, // sample rate buffer_frames, // frames per buffer nullptr, @@ -144,7 +145,7 @@ class PortAudioStream : public AudioStream { stream_started = false; } - operator bool() override { + operator bool() { return err == paNoError; } @@ -155,7 +156,8 @@ class PortAudioStream : public AudioStream { size_t result = 0; if (stream!=nullptr){ - int frames = len / bytesPerSample() / info.channels; + int bytes = info.bits_per_sample / 8; + int frames = len / bytes / info.channels; err = Pa_WriteStream( stream, data, frames); if( err == paNoError ) { LOGD("Pa_WriteStream: %zu", len); @@ -173,7 +175,8 @@ class PortAudioStream : public AudioStream { LOGD("readBytes: %zu", len); size_t result = 0; if (stream!=nullptr){ - int frames = len / bytesPerSample() / info.channels; + int bytes = info.bits_per_sample / 8; + int frames = len / bytes / info.channels; err = Pa_ReadStream( stream, data, frames ); if( err == paNoError ) { result = len; @@ -186,6 +189,10 @@ class PortAudioStream : public AudioStream { return len; } + int available() override { + return DEFAULT_BUFFER_SIZE; + } + protected: PaStream *stream = nullptr; @@ -194,21 +201,16 @@ class PortAudioStream : public AudioStream { bool stream_started = false; int buffer_size = 10*1024; - int bytesPerSample(){ - //return info.bits_per_sample / 8; - return info.bits_per_sample==24 ? sizeof(int24_t): info.bits_per_sample / 8; - } - - PaSampleFormat getFormat(){ - switch(bytesPerSample()){ - case 1: + PaSampleFormat getFormat(int bitLength){ + switch(bitLength){ + case 8: return paInt8; - case 2: + case 16: return paInt16; - case 3: + case 24: return paInt24; - case 4: + case 32: return paInt32; } // make sure that we return a valid value @@ -217,7 +219,7 @@ class PortAudioStream : public AudioStream { /// automatically start the stream when we start to get data void startStream() { - if (!stream_started && stream != nullptr) { + if (!stream_started) { TRACED(); err = Pa_StartStream( stream ); if( err == paNoError ) { diff --git a/src/AudioTools/AudioLibs/README.md b/src/AudioLibs/README.md similarity index 100% rename from src/AudioTools/AudioLibs/README.md rename to src/AudioLibs/README.md diff --git a/src/AudioTools/AudioLibs/RTSP.h b/src/AudioLibs/RTSP.h similarity index 98% rename from src/AudioTools/AudioLibs/RTSP.h rename to src/AudioLibs/RTSP.h index 658bf7b185..9d873000e7 100644 --- a/src/AudioTools/AudioLibs/RTSP.h +++ b/src/AudioLibs/RTSP.h @@ -1,8 +1,8 @@ #pragma once #include "AudioStreamer.h" -#include "AudioTools/CoreAudio/AudioPlayer.h" -#include "AudioTools/CoreAudio/AudioStreams.h" +#include "AudioTools/AudioPlayer.h" +#include "AudioTools/AudioStreams.h" #include "IAudioSource.h" #include "RTSPServer.h" @@ -156,7 +156,7 @@ class RTSPSourceFromAudioStream : public IAudioSource { protected: AudioStream *p_audiostream = nullptr; - uint32_t time_of_last_read = 0; + uint64_t time_of_last_read = 0; bool started = true; RTSPOutputPCMInfo pcmInfo; RTSPFormatPCM format{pcmInfo}; @@ -578,14 +578,14 @@ class RTSPOutput : public AudioOutput { * We write PCM data which is encoded on the fly by the indicated encoder. * This data is provided by the IAudioSource */ - size_t write(const uint8_t *data, size_t len) override { + size_t write(const uint8_t *buffer, size_t byteCount) override { TRACED(); - size_t result = p_encoder->write(data, len); + size_t result = p_encoder->write(buffer, byteCount); return result; } /// @brief Returns true if the server has been started - operator bool() { return rtps_source.isActive(); } + operator boolean() { return rtps_source.isActive(); } protected: RTSPFormatPCM pcm; @@ -598,5 +598,7 @@ class RTSPOutput : public AudioOutput { AudioStreamer rtsp_streamer; }; +// legacy name +using RTSPStream = RTSPOutput; } // namespace audio_tools diff --git a/src/AudioLibs/SDDirect.h b/src/AudioLibs/SDDirect.h new file mode 100644 index 0000000000..dd953849e4 --- /dev/null +++ b/src/AudioLibs/SDDirect.h @@ -0,0 +1,259 @@ +#pragma once + +#define MAX_FILE_LEN 256 +#define MAX_FILE_COUNT 1000000 +// Special logic for SDTFAT +#ifdef SDT_FAT_VERSION +#define USE_SDFAT +#endif + +namespace audio_tools { + +/** + * @brief We access the files directy with an index. The index is determined by a recurseve + * tree walk thru the directory. Unfortunatly the SDTFAT library has it's own API which is + * incompatible with the SDT API + */ +template +class SDDirect { + public: + SDDirect(SDT &sd) { + p_sd = &sd; + }; + + void begin(const char *startDir, const char *extension, + const char *file_name_pattern) { + TRACED(); + this->start_dir = startDir; + this->ext = extension; + this->file_name_pattern = file_name_pattern; + } + + + /// Access file name by index + const char *operator[](int idx) { + if (max_idx!=-1 && idx>max_idx){ + return nullptr; + } + + requested_idx = idx; + actual_idx = -1; + found = false; + listDir(start_dir); + if (!found) return nullptr; + return result.c_str(); + } + + /// Provides the number of files + long size() { + if (max_idx==-1){ + requested_idx = MAX_FILE_COUNT; + actual_idx = -1; + found = false; + listDir(start_dir); + max_idx = actual_idx; + } + return max_idx; + } + + protected: + String result; + const char* start_dir; + SDT *p_sd = nullptr; + int32_t actual_idx; + size_t requested_idx; + long max_idx=-1; + bool found = false; + List file_path_stack; + String file_path_str; + + const char *ext = nullptr; + const char *file_name_pattern = nullptr; + + /// Writes the index file + void listDir(const char *dirname) { + if (dirname==nullptr) return; + LOGD("listDir: %s", dirname); + FileT root = open(dirname); + if (!root) { + LOGE("Open failed: %s", dirname); + popPath(); + return; + } + if (!isDirectory(root)) { + LOGD("Is not directory: %s", dirname); + popPath(); + return; + } + if (Str(dirname).startsWith(".")) { + LOGD("Invalid file: %s", dirname); + popPath(); + return; + } + if (isDirectory(root)){ + rewind(root); + } + found = false; + FileT file = openNext(root); + while (file && !found) { + if (isDirectory(file)) { + String name = String(fileNamePath(file)); + LOGD("name: %s", name.c_str()); + pushPath(fileName(file)); + // recurseve call to get all files of this directory + listDir(name.c_str()); + } else { + const char* fn = fileNamePath(file); + if (isValidAudioFile(file)) { + actual_idx++; + LOGD("File %s at index: %d", fn, actual_idx); + if (actual_idx==requested_idx){ + result = String(fn); + found = true; + } + } else { + LOGD("Ignoring %s",fn); + } + } + file = openNext(root); + } + // stop processing and record maximum index + if (!found && file_path_stack.size()==0){ + max_idx = actual_idx; + } + popPath(); + } + + void rewind(FileT f){ + TRACED(); +#ifdef USE_SDFAT + f.rewind(); +#else + f.rewindDirectory(); +#endif + } + + bool isDirectory(FileT f) { + bool result; +#ifdef USE_SDFAT + result = f.isDir(); +#else + result = f.isDirectory(); +#endif + LOGD("isDirectory %s: %d", fileName(f), result); + return result; + } + + FileT openNext(FileT &dir) { + TRACED(); +#ifdef USE_SDFAT + FileT result; + if (!result.openNext(&dir, O_READ)){ + LOGD("No next file"); + } + return result; +#else + return dir.openNextFile(); +#endif + } + + void pushPath(const char* name){ + TRACED(); + LOGD("pushPath: %s", name); + String nameStr(name); + file_path_stack.push_back(nameStr); + } + + void popPath(){ + TRACED(); + String str; + file_path_stack.pop_back(str); + LOGD("popPath: %s", str.c_str()); + } + + /// checks if the file is a valid audio file + bool isValidAudioFile(FileT &file) { + const char *file_name = fileName(file); + if (file.isDirectory()) { + LOGD("-> isValidAudioFile: '%s': %d", file_name, false); + return false; + } + Str strFileTName(file_name); + bool result = strFileTName.endsWithIgnoreCase(ext) + && strFileTName.matches(file_name_pattern) + && !isHidden(file); + LOGD("-> isValidAudioFile: '%s': %d", file_name, result); + return result; + } + + + /// Returns the filename w/o path + const char* fileName(FileT&file){ +#ifdef USE_SDFAT + // add name + static char name[MAX_FILE_LEN]; + file.getName(name,MAX_FILE_LEN); + return name; +#else + Str tmp(file.name()); + int pos=0; + // remove directories + if (tmp.contains("/")){ + pos = tmp.lastIndexOf("/")+1; + } + return file.name()+pos; +#endif + } + + /// Returns the filename including the path + const char* fileNamePath(FileT &file){ +#if defined(USE_SDFAT) || ESP_IDF_VERSION_MAJOR >= 4 + LOGD("-> fileNamePath: %s", fileName(file)); + file_path_str = start_dir; + if (!file_path_str.endsWith("/")){ + file_path_str += "/"; + } + for (int j=0; jopen(name, "r"); +#endif + } + +}; + +} \ No newline at end of file diff --git a/src/AudioLibs/SDIndex.h b/src/AudioLibs/SDIndex.h new file mode 100644 index 0000000000..307da32b25 --- /dev/null +++ b/src/AudioLibs/SDIndex.h @@ -0,0 +1,322 @@ +#pragma once + +#define MAX_FILE_LEN 256 + +// Special logic for SDTFAT +#ifdef SDT_FAT_VERSION +#define USE_SDFAT +#endif + +namespace audio_tools { + + +/** + * @brief We store all the relevant file names in an sequential index + * file. Form there we can access them via an index + */ +template +class SDIndex { + public: + SDIndex(SDT &sd) { + p_sd = &sd; + }; + void begin(const char *startDir, const char *extension, + const char *file_name_pattern, bool setupIndex=true) { + TRACED(); + this->start_dir = startDir; + this->ext = extension; + this->file_name_pattern = file_name_pattern; + idx_path = filePathString(startDir,"idx.txt"); + idx_defpath = filePathString(startDir,"idx-def.txt"); + int idx_file_size = indexFileTSize(); + LOGI("Index file size: %d", idx_file_size); + String keyNew = String(startDir) + "|" + extension + "|" + file_name_pattern; + String keyOld = getIndexDef(); + if (setupIndex && (keyNew != keyOld || idx_file_size==0)) { + FileT idxfile = p_sd->open(idx_path.c_str(), FILE_WRITE); + LOGW("Creating index file"); + listDir(idxfile, startDir); + LOGI("Indexing completed"); + idxfile.close(); + // update index definition file + saveIndexDef(keyNew); + } + } + + void ls(Print &p, const char *startDir, const char *extension, + const char *file_name_pattern="*"){ + TRACED(); + this->ext = extension; + this->file_name_pattern = file_name_pattern; + listDir(p, startDir); + file_path_stack.clear(); + file_path_str.clear(); + + } + + /// Access file name by index + const char *operator[](int idx) { + // return null when inx too big + if (max_idx>=0 && idx>max_idx) { + LOGE("idx %d > size %d", idx, max_idx); + return nullptr; + } + // find record + FileT idxfile = p_sd->open(idx_path.c_str()); + int count=0; + + if (idxfile.available()==0){ + LOGE("Index file is empty"); + } + + bool found = false; + while (idxfile.available()>0 && !found) { + result = idxfile.readStringUntil('\n'); + + // result c-string + char *c_str = (char*)result.c_str(); + // remove potential cr character - hax to avoid any allocations + int lastPos = result.length()-1; + if (c_str[lastPos]==13){ + c_str[lastPos] = 0; + } + + LOGD("%d -> %s", count, c_str); + if (count==idx) { + found=true; + } + count++; + } + if (!found){ + max_idx = count; + } + idxfile.close(); + + return found ? result.c_str(): nullptr; + } + + long size() { + if (max_idx==-1){ + FileT idxfile = p_sd->open(idx_path.c_str()); + int count=0; + + while (idxfile.available()>0) { + result = idxfile.readStringUntil('\n'); + // result c-string + char *c_str = (char*)result.c_str(); + count++; + } + idxfile.close(); + max_idx = count; + } + return max_idx; + } + + protected: + String result; + String idx_path; + String idx_defpath; + SDT *p_sd = nullptr; + List file_path_stack; + String file_path_str; + const char* start_dir; + + const char *ext = nullptr; + const char *file_name_pattern = nullptr; + long max_idx=-1; + + String filePathString(const char* name, const char* suffix){ + String result = name; + return result.endsWith("/") ? result+suffix: result+"/"+suffix; + } + + /// Writes the index file + void listDir(Print &idxfile, const char *dirname) { + LOGD("listDir: %s", dirname); + FileT root = open(dirname); + if (!root) { + LOGE("Open failed: %s", dirname); + popPath(); + return; + } + if (!isDirectory(root)) { + LOGD("Is not directory: %s", dirname); + popPath(); + return; + } + if (Str(dirname).startsWith(".")) { + LOGD("Invalid file: %s", dirname); + popPath(); + return; + } + + rewind(root); + FileT file = openNext(root); + while (file) { + if (isDirectory(file)) { + String name = String(fileNamePath(file)); + LOGD("name: %s", name.c_str()); + pushPath(fileName(file)); + listDir(idxfile, name.c_str()); + } else { + const char* fn = fileNamePath(file); + if (isValidAudioFile(file)) { + LOGD("Adding file to index: %s", fn); + idxfile.println(fn); + } else { + LOGD("Ignoring %s",fn); + } + } + file = openNext(root); + } + popPath(); + } + + bool isDirectory(FileT f) { + bool result; +#ifdef USE_SDFAT + result = f.isDir(); +#else + result = f.isDirectory(); +#endif + LOGD("isDirectory %s: %d", fileName(f), result); + return result; + } + + FileT openNext(FileT &dir) { + TRACED(); +#ifdef USE_SDFAT + FileT result; + if (!result.openNext(&dir, O_READ)){ + LOGD("No next file"); + } + return result; +#else + return dir.openNextFile(); +#endif + } + + void pushPath(const char* name){ + LOGD("pushPath: %s", name); + LOGD("pushPath: %s", name); + String nameStr(name); + file_path_stack.push_back(nameStr); + } + + void popPath(){ + TRACED(); + String str; + file_path_stack.pop_back(str); + LOGD("popPath: %s", str.c_str()); + } + + /// checks if the file is a valid audio file + bool isValidAudioFile(FileT &file) { + TRACED(); + const char *file_name = fileName(file); + if (file.isDirectory()) { + LOGD("-> isValidAudioFile: '%s': %d", file_name, false); + return false; + } + Str strFileTName(file_name); + bool result = strFileTName.endsWithIgnoreCase(ext) + && strFileTName.matches(file_name_pattern) + && !isHidden(file); + LOGD("-> isValidAudioFile: '%s': %d", file_name, result); + return result; + } + + String getIndexDef() { + FileT idxdef = p_sd->open(idx_defpath.c_str()); + String key1 = idxdef.readString(); + idxdef.close(); + return key1; + } + void saveIndexDef(String keyNew){ + FileT idxdef = p_sd->open(idx_defpath.c_str(), FILE_WRITE); + idxdef.write((const uint8_t *)keyNew.c_str(), keyNew.length()); + idxdef.close(); + } + + size_t indexFileTSize() { + FileT idxfile = p_sd->open(idx_path.c_str()); + size_t result = idxfile.size(); + idxfile.close(); + return result; + } + + void rewind(FileT f){ + TRACED(); +#ifdef USE_SDFAT + f.rewind(); +#else + f.rewindDirectory(); +#endif + } + + /// Returns the filename w/o the path + const char* fileName(FileT&file){ +#ifdef USE_SDFAT + // add name + static char name[MAX_FILE_LEN]; + file.getName(name,MAX_FILE_LEN); + return name; +#else + Str tmp(file.name()); + int pos=0; + // remove directories + if (tmp.contains("/")){ + pos = tmp.lastIndexOf("/")+1; + } + return file.name()+pos; +#endif + } + + /// Returns the filename including the path + const char* fileNamePath(FileT &file){ +#if defined(USE_SDFAT) || ESP_IDF_VERSION_MAJOR >= 4 + LOGD("-> fileNamePath: %s", fileName(file)); + file_path_str = start_dir; + if (!file_path_str.endsWith("/")){ + file_path_str += "/"; + } + for (int j=0; jopen(name); +#endif + } + +}; + +} \ No newline at end of file diff --git a/src/AudioTools/AudioLibs/StdioStream.h b/src/AudioLibs/StdioStream.h similarity index 70% rename from src/AudioTools/AudioLibs/StdioStream.h rename to src/AudioLibs/StdioStream.h index c6b0c977f6..3c41fb40ec 100644 --- a/src/AudioTools/AudioLibs/StdioStream.h +++ b/src/AudioLibs/StdioStream.h @@ -1,17 +1,15 @@ #pragma once #include -#include "AudioTools/CoreAudio/AudioStreams.h" - -namespace audio_tools { +#include "AudioTools/AudioStreams.h" /** * @brief Direct binary Audio Output to stdout. On linux you can hear the audio e.g. with ./generator | aplay -f cd - * or reading data from stdin. * @author Phil Schatzmann * @ingroup io * @copyright GPLv3 */ -class StdioStream : public BaseStream { +namespace audio_tools { +class StdioStream : public AudioStream { public: AudioInfo defaultConfig() { AudioInfo def; @@ -21,26 +19,25 @@ class StdioStream : public BaseStream { return def; } - bool begin() override { + bool begin(AudioInfo cfg) { is_open = true; + info = cfg; return true; } - - int available() override { return DEFAULT_BUFFER_SIZE; } - size_t readBytes(uint8_t* data, size_t len) override { + size_t readBytes(uint8_t* buffer, size_t len) override { // read from stdin - return ::read(0, data, len); + return ::read(0, buffer, len); } int availableForWrite() override { return DEFAULT_BUFFER_SIZE; } - size_t write(const uint8_t *data, size_t len) override { + size_t write(const uint8_t *buffer, size_t len) override { if (!is_open) return 0; // write to stdout - return ::write(1, data, len); + return ::write(1, buffer, len); } void end() override { @@ -49,6 +46,7 @@ class StdioStream : public BaseStream { protected: bool is_open = false; + FILE *out; }; } // namespace audio_tools \ No newline at end of file diff --git a/src/AudioTools/AudioLibs/TfLiteAudioStream.h b/src/AudioLibs/TfLiteAudioStream.h similarity index 96% rename from src/AudioTools/AudioLibs/TfLiteAudioStream.h rename to src/AudioLibs/TfLiteAudioStream.h index 2c4909346c..920aa4eb52 100644 --- a/src/AudioTools/AudioLibs/TfLiteAudioStream.h +++ b/src/AudioLibs/TfLiteAudioStream.h @@ -3,18 +3,17 @@ // Configure FFT to output 16 bit fixed point. #define FIXED_POINT 16 -//#include #include #include #include -#include "AudioTools/CoreAudio/BaseStream.h" -#include "AudioTools/CoreAudio/AudioOutput.h" -#include "AudioTools/CoreAudio/Buffers.h" +#include "AudioTools/AudioOutput.h" +#include "AudioTools/Buffers.h" #include "tensorflow/lite/c/common.h" #include "tensorflow/lite/experimental/microfrontend/lib/frontend.h" #include "tensorflow/lite/experimental/microfrontend/lib/frontend_util.h" #include "tensorflow/lite/micro/all_ops_resolver.h" #include "tensorflow/lite/micro/kernels/micro_ops.h" +#include "tensorflow/lite/micro/micro_error_reporter.h" #include "tensorflow/lite/micro/micro_interpreter.h" #include "tensorflow/lite/micro/micro_mutable_op_resolver.h" #include "tensorflow/lite/micro/system_setup.h" @@ -56,6 +55,25 @@ class TfLiteWriter { virtual bool begin(TfLiteAudioStreamBase *parent) = 0; virtual bool write(const int16_t sample) = 0; }; +/** + * @brief Error Reporter using the Audio Tools Logger + * @ingroup tflite + * @author Phil Schatzmann + * @copyright GPLv3 + */ +class TfLiteAudioErrorReporter : public tflite::ErrorReporter { + public: + virtual ~TfLiteAudioErrorReporter() {} + virtual int Report(const char* format, va_list args) override { + int result = snprintf(msg, 200, format, args); + LOGE(msg); + return result; + } + + protected: + char msg[200]; +} my_error_reporter; +tflite::ErrorReporter* error_reporter = &my_error_reporter; /** * @brief Configuration settings for TfLiteAudioStream @@ -78,7 +96,7 @@ struct TfLiteConfig { // Create an area of memory to use for input, output, and intermediate arrays. // The size of this will depend on the model you’re using, and may need to be // determined by experimentation. - size_t kTensorArenaSize = 10 * 1024; + int kTensorArenaSize = 10 * 1024; // Keeping these as constant expressions allow us to allocate fixed-sized // arrays on the stack for our working memory. @@ -292,7 +310,6 @@ class TfLiteMicroSpeechRecognizeCommands : public TfLiteAbstractRecognizeCommand /// Removes obsolete records from the queue void deleteOldRecords(int32_t limit) { - if (result_queue.empty()) return; while (result_queue[0].time_mswrite(samples[j]); } - return len; + return bytes; } /// We can provide only some audio data when cfg.input is defined @@ -962,12 +979,14 @@ class TfLiteAudioStream : public TfLiteAudioStreamBase { TRACEI(); if (cfg.useAllOpsResolver) { tflite::AllOpsResolver resolver; - static tflite::MicroInterpreter static_interpreter{ - p_model, resolver, p_tensor_arena, cfg.kTensorArenaSize}; + static tflite::MicroInterpreter static_interpreter( + p_model, resolver, p_tensor_arena, cfg.kTensorArenaSize, + error_reporter); p_interpreter = &static_interpreter; } else { // NOLINTNEXTLINE(runtime-global-variables) - static tflite::MicroMutableOpResolver<4> micro_op_resolver{}; + static tflite::MicroMutableOpResolver<4> micro_op_resolver( + error_reporter); if (micro_op_resolver.AddDepthwiseConv2D() != kTfLiteOk) { return false; } @@ -981,8 +1000,9 @@ class TfLiteAudioStream : public TfLiteAudioStreamBase { return false; } // Build an p_interpreter to run the model with. - static tflite::MicroInterpreter static_interpreter{ - p_model, micro_op_resolver, p_tensor_arena, cfg.kTensorArenaSize}; + static tflite::MicroInterpreter static_interpreter( + p_model, micro_op_resolver, p_tensor_arena, cfg.kTensorArenaSize, + error_reporter); p_interpreter = &static_interpreter; } } diff --git a/src/AudioTools/AudioLibs/VS1053Stream.h b/src/AudioLibs/VS1053Stream.h similarity index 91% rename from src/AudioTools/AudioLibs/VS1053Stream.h rename to src/AudioLibs/VS1053Stream.h index 5ff18f859f..c9a3e48866 100644 --- a/src/AudioTools/AudioLibs/VS1053Stream.h +++ b/src/AudioLibs/VS1053Stream.h @@ -1,10 +1,7 @@ #pragma once -#include "AudioTools/CoreAudio/AudioStreams.h" -#include "AudioTools/AudioCodecs/CodecCopy.h" -#include "AudioTools/AudioCodecs/AudioEncoded.h" -#include "AudioTools/AudioCodecs/CodecWAV.h" - +#include "AudioTools/AudioStreams.h" +#include "AudioCodecs/CodecCopy.h" #if VS1053_EXT # include "VS1053Driver.h" #else @@ -52,7 +49,7 @@ class VS1053Config : public AudioInfo { * @author Phil Schatzmann * @copyright GPLv3 */ -class VS1053Stream : public AudioStream, public VolumeSupport { +class VS1053Stream : public AudioStream { /** * @brief Private output class for the EncodedAudioStream @@ -62,14 +59,10 @@ class VS1053Stream : public AudioStream, public VolumeSupport { VS1053StreamOut(VS1053 *vs){ p_VS1053 = vs; } - size_t write(const uint8_t *data, size_t len) override { - if (p_VS1053==nullptr) { - LOGE("NPE"); - return 0; - } - TRACED(); - p_VS1053->playChunk((uint8_t*)data, len); - return len; + size_t write(const uint8_t *buffer, size_t size) override { + if (p_VS1053==nullptr) return 0; + p_VS1053->playChunk((uint8_t*)buffer, size); + return size; } protected: VS1053 *p_VS1053=nullptr; @@ -194,7 +187,7 @@ class VS1053Stream : public AudioStream, public VolumeSupport { } /// value from 0 to 1.0 - bool setVolume(float vol) override { + void setVolume(float vol){ // make sure that value is between 0 and 1 float volume = vol; if (volume>1.0) volume = 1.0; @@ -204,11 +197,10 @@ class VS1053Stream : public AudioStream, public VolumeSupport { // Set the player volume.Level from 0-100, higher is louder p_vs1053->setVolume(volume*100.0); } - return true; } /// provides the volume - float volume() override { + float volume() { TRACED(); if (p_vs1053==nullptr) return -1.0; return p_vs1053->getVolume()/100.0;; @@ -232,14 +224,9 @@ class VS1053Stream : public AudioStream, public VolumeSupport { } /// Write audio data - virtual size_t write(const uint8_t *data, size_t len) override { - TRACED(); - if (len==0) return 0; - if (p_out==nullptr) { - LOGE("vs1053 is closed"); - return 0; - } - return p_out->write(data, len); + virtual size_t write(const uint8_t *buffer, size_t size) override{ + if (p_out==nullptr) return 0; + return p_out->write(buffer, size); } /// returns the VS1053 object @@ -317,7 +304,6 @@ class VS1053Stream : public AudioStream, public VolumeSupport { /// Sends a midi message to the VS1053 void sendMidiMessage(uint8_t cmd, uint8_t data1, uint8_t data2) { TRACEI(); -#if USE_MIDI if (!cfg.is_midi){ LOGE("start with is_midi=true"); return; @@ -327,7 +313,6 @@ class VS1053Stream : public AudioStream, public VolumeSupport { return; } p_vs1053->sendMidiMessage(cmd, data1, data2); -#endif } #endif @@ -366,16 +351,12 @@ class VS1053Stream : public AudioStream, public VolumeSupport { } bool beginMidi(){ -#if USE_MIDI TRACEI(); p_out->begin(cfg); bool result = p_vs1053->beginMidi(); delay(500); setVolume(VS1053_DEFAULT_VOLUME); return result; -#else - return false; -#endif } #endif diff --git a/src/AudioTools/AudioLibs/WM8960Stream.h b/src/AudioLibs/WM8960Stream.h similarity index 97% rename from src/AudioTools/AudioLibs/WM8960Stream.h rename to src/AudioLibs/WM8960Stream.h index 5e78fb239c..2b9a9d4e2e 100644 --- a/src/AudioTools/AudioLibs/WM8960Stream.h +++ b/src/AudioLibs/WM8960Stream.h @@ -1,7 +1,7 @@ #pragma once -#include "AudioTools/CoreAudio/AudioStreams.h" -#include "AudioTools/CoreAudio/AudioI2S/I2SStream.h" +#include "AudioTools/AudioStreams.h" +#include "AudioI2S/I2SStream.h" #include "mtb_wm8960.h" // https://github.com/pschatzmann/arduino-wm8960 namespace audio_tools { @@ -109,11 +109,10 @@ class WM8960Stream : public AudioStream { } /// Sets both input and output volume value (from 0 to 1.0) - bool setVolume(float vol){ + void setVolume(float vol){ // make sure that value is between 0 and 1 setVolumeIn(vol); setVolumeOut(vol); - return true; } void setVolumeIn(float vol){ diff --git a/src/AudioLogger.h b/src/AudioLogger.h index 3b8148cf8f..610c025e08 100644 --- a/src/AudioLogger.h +++ b/src/AudioLogger.h @@ -1,2 +1,3 @@ #pragma once -#include "AudioTools/CoreAudio/AudioLogger.h" +// added so that we can include the AudioLogger from Arduino w/o calling include "AudioTools.h" first. +#include "AudioTools/AudioLogger.h" \ No newline at end of file diff --git a/src/AudioTools/CoreAudio/AudioMetaData/AbstractMetaData.h b/src/AudioMetaData/AbstractMetaData.h similarity index 71% rename from src/AudioTools/CoreAudio/AudioMetaData/AbstractMetaData.h rename to src/AudioMetaData/AbstractMetaData.h index 38aa7ba3b8..402e17735c 100644 --- a/src/AudioTools/CoreAudio/AudioMetaData/AbstractMetaData.h +++ b/src/AudioMetaData/AbstractMetaData.h @@ -11,22 +11,13 @@ enum ID3TypeSelection { SELECT_ID3V1=0b001, SELECT_ID3V2=0b010, SELECT_ID3=0b011 enum MetaDataType { Title, Artist, Album, Genre, Name, Description }; // Description for meta info -static const char* MetaDataTypeStr[] = {"Title", "Artist", "Album", "Genre","Name", "Description"}; +INLINE_VAR const char* MetaDataTypeStr[] = {"Title", "Artist", "Album", "Genre","Name", "Description"}; /// Converts the MetaDataType to a string @ingroup metadata -static const char *toStr(MetaDataType t){ +INLINE_VAR const char *toStr(MetaDataType t){ return MetaDataTypeStr[t]; } -/// unfortunatly strnlen or strnlen_s is not available in all implementations -static size_t strnlength (const char* s, size_t n) { - size_t i; - for (i = 0; i < n && s[i] != '\0'; i++) - continue; - return i; -} - - /** * @brief Common Metadata methods * @ingroup metadata @@ -42,12 +33,10 @@ class AbstractMetaData { // ends the processing virtual void end() = 0; // provide audio data which contains the metadata to be extracted - virtual size_t write(const uint8_t *data, size_t len) = 0; + virtual size_t write(const uint8_t *data, size_t length) = 0; // select Icecast/Shoutcast Metadata virtual void setIcyMetaInt(int value){} }; - - } \ No newline at end of file diff --git a/src/AudioTools/CoreAudio/AudioMetaData/MetaData.h b/src/AudioMetaData/MetaData.h similarity index 81% rename from src/AudioTools/CoreAudio/AudioMetaData/MetaData.h rename to src/AudioMetaData/MetaData.h index 1ce8fee0bf..6236dea14b 100644 --- a/src/AudioTools/CoreAudio/AudioMetaData/MetaData.h +++ b/src/AudioMetaData/MetaData.h @@ -1,12 +1,11 @@ #pragma once #include -#include "AudioToolsConfig.h" -#include "AudioTools/CoreAudio/AudioTypes.h" -#include "AudioTools/CoreAudio/AudioStreams.h" -#include "AudioTools/CoreAudio/AudioHttp/HttpRequest.h" -#include "AudioTools/CoreAudio/AudioMetaData/MetaDataFilter.h" -#include "MetaDataICY.h" -#include "MetaDataID3.h" +#include "AudioConfig.h" +#include "AudioTools/AudioTypes.h" +#include "AudioTools/AudioStreams.h" +#include "AudioMetaData/MetaDataICY.h" +#include "AudioMetaData/MetaDataID3.h" +#include "AudioHttp/HttpRequest.h" /** * @defgroup metadata Metadata @@ -45,10 +44,10 @@ class MetaDataOutput : public AudioOutput { #ifdef USE_URL_ARDUINO /// Starts the processing - iceMetaint is determined from the HttpRequest - virtual void begin(AbstractURLStream &url) { + virtual void begin(HttpRequest &http) { TRACED(); ICYUrlSetup icySetup; - int metaInt = icySetup.setup(url); + int metaInt = icySetup.setup(http); icySetup.executeCallback(callback); begin(metaInt); } @@ -81,13 +80,13 @@ class MetaDataOutput : public AudioOutput { } /// Provide tha audio data to the API to parse for Meta Data - virtual size_t write(const uint8_t *data, size_t len){ - LOGD("%s: %d", LOG_METHOD, (int)len); + virtual size_t write(const uint8_t *data, size_t length){ + LOGD("%s: %d", LOG_METHOD, (int)length); if (callback!=nullptr){ if (meta!=nullptr){ //CHECK_MEMORY(); - if (meta->write(data, len)!=len){ + if (meta->write(data, length)!=length){ LOGE("Did not write all data"); } //CHECK_MEMORY(); @@ -95,7 +94,7 @@ class MetaDataOutput : public AudioOutput { LOGW("meta is null"); } } - return len; + return length; } virtual size_t write(uint8_t c) { @@ -109,5 +108,7 @@ class MetaDataOutput : public AudioOutput { }; +// legacy name +using MetaDataPrint = MetaDataOutput; } \ No newline at end of file diff --git a/src/AudioMetaData/MetaDataFilter.h b/src/AudioMetaData/MetaDataFilter.h new file mode 100644 index 0000000000..f3edae9106 --- /dev/null +++ b/src/AudioMetaData/MetaDataFilter.h @@ -0,0 +1,128 @@ +#pragma once +#include "AudioTools/AudioLogger.h" + +namespace audio_tools { + +/** + * @brief Class which filters out ID3v1 and ID3v2 Metadata and provides only the audio data to the decoder + * @ingroup metadata + * @author Phil Schatzmann + * @copyright GPLv3 + */ +template +class MetaDataFilter { + public: + /// Default Constructor + MetaDataFilter() = default; + + /// Constructor which assigns the decoder + MetaDataFilter(Decoder *decoder){ + setDecoder(decoder); + } + + /// Defines the decoder to which we write the data + void setDecoder(Decoder *decoder){ + p_decoder = decoder; + } + + /// (Re)starts the processing + void begin() { + TRACED(); + start = 0; + } + + /// Writes the data to the decoder + size_t write(uint8_t* data, size_t len){ + TRACED(); + if (p_decoder==nullptr) return 0; + int pos=0; int meta_len=0; + if (findTag(data, len, pos, meta_len)){ + LOGD("pos: %d len: %d",pos, meta_len); + if (startwrite(data+start,pos); + } + + int start_idx2 = pos+meta_len; + int len2 = len-start_idx2; + if (start_idx2write(data+start_idx2,len2); + } else { + // ignore audio of next write + start = meta_len - len2; + } + } else { + // ignore start number of characters + if (start>=len){ + start -= len; + } else { + p_decoder->write(data+start,len-start); + start = 0; + } + } + return len; + } + + protected: + Decoder *p_decoder=nullptr; + enum MetaType {TAG, TAG_PLUS, ID3}; + int start = 0; + /// ID3 verion 2 TAG Header (10 bytes) + struct ID3v2 { + uint8_t header[3]; // ID3 + uint8_t version[2]; + uint8_t flags; + uint8_t size[4]; + } tagv2; + + /// determines if the data conatins a ID3v1 or ID3v2 tag + bool findTag(uint8_t* data, size_t len, int &pos_tag, int &meta_len){ + MetaType tag_type; + if (find((const char*)data, len, pos_tag, tag_type)){ + switch(tag_type){ + case TAG: + LOGD("TAG"); + meta_len = 128; + break; + case TAG_PLUS: + LOGD("TAG+"); + meta_len = 227; + break; + case ID3: + LOGD("ID3"); + memcpy(&tagv2, data+pos_tag, sizeof(ID3v2)); + meta_len = calcSizeID3v2(tagv2.size); + break; + + } + return true; + } + return false; + } + + // calculate the synch save size for ID3v2 + uint32_t calcSizeID3v2(uint8_t chars[4]) { + uint32_t byte0 = chars[0]; + uint32_t byte1 = chars[1]; + uint32_t byte2 = chars[2]; + uint32_t byte3 = chars[3]; + return byte0 << 21 | byte1 << 14 | byte2 << 7 | byte3; + } + + /// find the tag position in the string; + bool find(const char*str, size_t len, int &pos, MetaType &type){ + if (str==nullptr || len<=0) return false; + for (size_t j=0;j<=len-3;j++){ + if (str[j]=='T' && str[j+1]=='A' && str[j+2]=='G'){ + type = str[j+3]=='+' ? TAG_PLUS : TAG; + return true; + } else if (str[j]=='I' && str[j+1]=='D' && str[j+2]=='3'){ + type = ID3; + return true; + } + } + return false; + } +}; + +} \ No newline at end of file diff --git a/src/AudioMetaData/MetaDataICY.h b/src/AudioMetaData/MetaDataICY.h new file mode 100644 index 0000000000..cfa2caeba6 --- /dev/null +++ b/src/AudioMetaData/MetaDataICY.h @@ -0,0 +1,312 @@ +#pragma once +#include "AudioConfig.h" +#ifdef USE_URL_ARDUINO + +#include "AudioMetaData/AbstractMetaData.h" +#include "AudioBasic/Str.h" +#include "AudioHttp/HttpRequest.h" + +namespace audio_tools { + +/** + * @defgroup metadata-icy ICY + * @ingroup metadata + * @brief Icecast/Shoutcast Metadata +**/ + + +/** + * @brief Icecast/Shoutcast Metadata Handling. + * Metadata class which splits the data into audio and metadata. The result is provided via callback methods. + * see https://www.codeproject.com/Articles/11308/SHOUTcast-Stream-Ripper + * @ingroup metadata-icy + * @author Phil Schatzmann + * @copyright GPLv3 + */ +class MetaDataICY : public AbstractMetaData { + + enum Status {ProcessData, ProcessMetaData, SetupSize}; + + public: + MetaDataICY() = default; + + /// We just process the Metadata and ignore the audio info + MetaDataICY(int metaint){ + setIcyMetaInt(metaint); + } + + ~MetaDataICY(){ + if (metaData!=nullptr) delete[]metaData; + } + + /// Defines the ICE metaint value which is provided by the web call! + virtual void setIcyMetaInt(int value) override { + this->mp3_blocksize = value; + } + + /// Defines the metadata callback function + virtual void setCallback(void (*fn)(MetaDataType info, const char* str, int len)) override { + callback = fn; + } + + /// Defines the audio callback function + virtual void setAudioDataCallback(void (*fn)(const uint8_t* str, int len), int bufferLen=1024) { + dataBuffer = new uint8_t[bufferLen]; + dataCallback = fn; + dataLen = 0; + dataPos = 0; + } + + /// Resets all counters and restarts the prcessing + virtual void begin() override { + clear(); + LOGI("mp3_blocksize: %d", mp3_blocksize); + } + + /// Resets all counters and restarts the prcessing + virtual void end() override { + clear(); + } + + /// Writes the data in order to retrieve the metadata and perform the corresponding callbacks + virtual size_t write(const uint8_t *buffer, size_t len) override { + if (callback!=nullptr){ + for (size_t j=0;jmp3_blocksize>0; + } + + /// provides the metaint + virtual int metaInt() { + return mp3_blocksize; + } + + /// character based state engine + virtual void processChar(char ch){ + switch(nextStatus){ + case ProcessData: + currentStatus = ProcessData; + processData(ch); + + // increment data counter and determine next status + ++totalData; + if (totalData>=mp3_blocksize){ + LOGI("Data ended") + totalData = 0; + nextStatus = SetupSize; + } + break; + + case SetupSize: + currentStatus = SetupSize; + totalData = 0; + metaDataPos = 0; + metaDataLen = metaSize(ch); + LOGI("metaDataLen: %d", metaDataLen); + if (metaDataLen>0){ + if (metaDataLen>200){ + LOGI("Unexpected metaDataLen -> processed as data"); + nextStatus = ProcessData; + } else { + LOGI("Metadata found"); + setupMetaData(metaDataLen); + nextStatus = ProcessMetaData; + } + } else { + LOGI("Data found"); + nextStatus = ProcessData; + } + break; + + case ProcessMetaData: + currentStatus = ProcessMetaData; + metaData[metaDataPos++]=ch; + if (metaDataPos>=metaDataLen){ + processMetaData(metaData, metaDataLen); + LOGI("Metadata ended") + nextStatus = ProcessData; + } + break; + } + } + + + protected: + Status nextStatus = ProcessData; + Status currentStatus = ProcessData; + void (*callback)(MetaDataType info, const char* str, int len) = nullptr; + char* metaData=nullptr; + int totalData = 0; + int mp3_blocksize = 0; + int metaDataMaxLen = 0; + int metaDataLen = 0; + int metaDataPos = 0; + bool is_data; // indicates if the current byte is a data byte + // data + uint8_t *dataBuffer=nullptr; + void (*dataCallback)(const uint8_t* str, int len) = nullptr; + int dataLen = 0; + int dataPos = 0; + + virtual void clear() { + nextStatus = ProcessData; + totalData = 0; + metaDataLen = 0; + metaDataPos = 0; + dataLen = 0; + dataPos = 0; + } + + /// determines the meta data size from the size byte + virtual int metaSize(uint8_t metaSize){ + return metaSize*16; + } + + /// Make sure that the result is a valid ASCII string + virtual bool isAscii(char* result, int l){ + // check on first 10 characters + int m = l < 5 ? l : 10; + for (int j=0; j0){ + if (metaData==nullptr){ + metaData = new char[meta_size+1]; + metaDataMaxLen = meta_size; + LOGD("metaDataMaxLen: %d", metaDataMaxLen); + } else { + if (meta_size>metaDataMaxLen){ + delete [] metaData; + metaData = new char[meta_size+1]; + metaDataMaxLen = meta_size; + LOGD("metaDataMaxLen: %d", metaDataMaxLen); + } + } + memset(metaData, 0, meta_size); + } + } + + /// e.g. StreamTitle=' House Bulldogs - But your love (Radio Edit)';StreamUrl=''; + virtual void processMetaData( char* metaData, int len) { + //CHECK_MEMORY(); + TRACED(); + metaData[len]=0; + if (isAscii(metaData, 12)){ + LOGI("%s", metaData); + Str meta(metaData,len+1, len); + int start = meta.indexOf("StreamTitle="); + if (start>=0){ + start+=12; + } + int end = meta.indexOf("';"); + if (start>=0 && end>start){ + metaData[end]=0; + if (callback!=nullptr){ + callback(Title, (const char*)metaData+start+1, end-start); + } + } + // CHECK_MEMORY(); + } else { + // CHECK_MEMORY(); + LOGW("Unexpected Data: %s", metaData); + } + } + + /// Collects the data in a buffer and executes the callback when the buffer is full + virtual void processData(char ch){ + if (dataBuffer!=nullptr){ + dataBuffer[dataPos++] = ch; + // output data via callback + if (dataPos>=dataLen){ + dataCallback(dataBuffer, dataLen); + dataPos = 0; + } + } + } +}; + + +/** + * @brief Resolve icy-metaint from HttpRequest and execute metadata callbacks + * @ingroup metadata-icy + * @author Phil Schatzmann + * @copyright GPLv3 + */ + +class ICYUrlSetup { + public: + /// Fills the metaint from the Http Request and executes metadata callbacks on http reply parameters + int setup(HttpRequest &http ) { + TRACED(); + p_http = &http; + const char* iceMetaintStr = http.reply().get("icy-metaint"); + if (iceMetaintStr){ + LOGI("icy-metaint: %s", iceMetaintStr); + } else { + LOGE("icy-metaint not defined"); + } + Str value(iceMetaintStr); + int iceMetaint = value.toInt(); + return iceMetaint; + } + + /// Executes the metadata callbacks with data provided from the http request result parameter + void executeCallback(void (*callback)(MetaDataType info, const char* str, int len)) { + TRACEI(); + if (callback==nullptr){ + LOGW("callback not defined") + } + if (p_http==nullptr){ + LOGW("http not defined") + } + // Callbacks filled from url reply for icy + if (callback!=nullptr && p_http!=nullptr) { + // handle icy parameters + Str genre(p_http->reply().get("icy-genre")); + if (!genre.isEmpty()){ + callback(Genre, genre.c_str(), genre.length()); + } + + Str descr(p_http->reply().get("icy-description")); + if (!descr.isEmpty()){ + callback(Description, descr.c_str(), descr.length()); + } + + Str name(p_http->reply().get("icy-name")); + if (!name.isEmpty()){ + callback(Name, name.c_str(), name.length()); + } + } + } + + protected: + HttpRequest *p_http = nullptr; + +}; + +} + +#endif \ No newline at end of file diff --git a/src/AudioTools/CoreAudio/AudioMetaData/MetaDataID3.h b/src/AudioMetaData/MetaDataID3.h similarity index 75% rename from src/AudioTools/CoreAudio/AudioMetaData/MetaDataID3.h rename to src/AudioMetaData/MetaDataID3.h index abf93943d4..4dadd527d5 100644 --- a/src/AudioTools/CoreAudio/AudioMetaData/MetaDataID3.h +++ b/src/AudioMetaData/MetaDataID3.h @@ -11,15 +11,6 @@ #include #include "AbstractMetaData.h" -/** - * An optional flag to disable the Non-ASCII character check on IDv3 metadata checks. - * Set this to `true` if having issues parsing Unicode metatags in MP3 files. - * Feature-flagged to prevent breaking changes to existing users. -akasaka/2025 - **/ -#ifndef AUDIOTOOLS_ID3_TAG_ALLOW_NONASCII -#define AUDIOTOOLS_ID3_TAG_ALLOW_NONASCII false -#endif - /** * @defgroup metadata-id3 ID3 * @ingroup metadata @@ -30,7 +21,7 @@ namespace audio_tools { // String array with genres -static const char *genres[] = { "Classic Rock", "Country", "Dance", "Disco", "Funk", "Grunge", "Hip-Hop", "Jazz", "Metal", "New Age", "Oldies", "Other", "Pop", "R&B", "Rap", "Reggae", "Rock", "Techno", "Industrial", "Alternative", "Ska", "Death Metal", "Pranks", "Soundtrack", "Euro-Techno", "Ambient", "Trip-Hop", "Vocal", "Jazz+Funk", "Fusion", "Trance", "Classical", "Instrumental", "Acid", "House", "Game", "Sound Clip", "Gospel", "Noise", "Alternative Rock", "Bass", "Soul", "Punk", "Space", "Meditative", "Instrumental Pop", "Instrumental Rock", "Ethnic", "Gothic", "Darkwave", "Techno-Insdustiral", "Electronic", "Pop-Folk", "Eurodance", "Dream", "Southern Rock", "Comedy", "Cult", "Gangsta", "Top 40", "Christian Rap", "Pop/Funk", "Jungle", "Native US", "Cabaret", "New Wave", "Psychadelic", "Rave", "Showtunes", "Trailer", "Lo-Fi", "Tribal", "Acid Punk", "Acid Jazz", "Polka", "Retro", "Musical", "Rock & Roll", "Hard Rock", "Folk", "Folk-Rock", "National Folk", "Swing", "Fast Fusion", "Bebob", "Latin", "Revival", "Celtic", "Bluegrass", "Avantgarde", "Gothic Rock", "Progressive Rock", "Psychedelic Rock", "Symphonic Rock", "Slow Rock", "Big Band", "Chorus", "Easy Listening", "Acoustic","Humour", "Speech", "Chanson", "Opera", "Chamber Music", "Sonata", "Symphony", "Booty Bass", "Primus", "Porn Groove", "Satire", "Slow Jam", "Club", "Tango", "Samba", "Folklore", "Ballad", "Power Ballad", "Rhytmic Soul", "Freestyle", "Duet", "Punk Rock", "Drum Solo", "Acapella", "Euro-House", "Dance Hall", "Goa", "Drum & Bass", "Club-House", "Hardcore", "Terror", "Indie", "BritPop", "Negerpunk", "Polsk Punk", "Beat", "Christian Gangsta", "Heavy Metal", "Black Metal", "Crossover", "Contemporary C", "Christian Rock", "Merengue", "Salsa", "Thrash Metal", "Anime", "JPop", "SynthPop" }; +INLINE_VAR const char *genres[] = { "Classic Rock", "Country", "Dance", "Disco", "Funk", "Grunge", "Hip-Hop", "Jazz", "Metal", "New Age", "Oldies", "Other", "Pop", "R&B", "Rap", "Reggae", "Rock", "Techno", "Industrial", "Alternative", "Ska", "Death Metal", "Pranks", "Soundtrack", "Euro-Techno", "Ambient", "Trip-Hop", "Vocal", "Jazz+Funk", "Fusion", "Trance", "Classical", "Instrumental", "Acid", "House", "Game", "Sound Clip", "Gospel", "Noise", "Alternative Rock", "Bass", "Soul", "Punk", "Space", "Meditative", "Instrumental Pop", "Instrumental Rock", "Ethnic", "Gothic", "Darkwave", "Techno-Insdustiral", "Electronic", "Pop-Folk", "Eurodance", "Dream", "Southern Rock", "Comedy", "Cult", "Gangsta", "Top 40", "Christian Rap", "Pop/Funk", "Jungle", "Native US", "Cabaret", "New Wave", "Psychadelic", "Rave", "Showtunes", "Trailer", "Lo-Fi", "Tribal", "Acid Punk", "Acid Jazz", "Polka", "Retro", "Musical", "Rock & Roll", "Hard Rock", "Folk", "Folk-Rock", "National Folk", "Swing", "Fast Fusion", "Bebob", "Latin", "Revival", "Celtic", "Bluegrass", "Avantgarde", "Gothic Rock", "Progressive Rock", "Psychedelic Rock", "Symphonic Rock", "Slow Rock", "Big Band", "Chorus", "Easy Listening", "Acoustic","Humour", "Speech", "Chanson", "Opera", "Chamber Music", "Sonata", "Symphony", "Booty Bass", "Primus", "Porn Groove", "Satire", "Slow Jam", "Club", "Tango", "Samba", "Folklore", "Ballad", "Power Ballad", "Rhytmic Soul", "Freestyle", "Duet", "Punk Rock", "Drum Solo", "Acapella", "Euro-House", "Dance Hall", "Goa", "Drum & Bass", "Club-House", "Hardcore", "Terror", "Indie", "BritPop", "Negerpunk", "Polsk Punk", "Beat", "Christian Gangsta", "Heavy Metal", "Black Metal", "Crossover", "Contemporary C", "Christian Rock", "Merengue", "Salsa", "Thrash Metal", "Anime", "JPop", "SynthPop" }; /// current status of the parsing @ingroup metadata-id3 enum ParseStatus { TagNotFound, PartialTagAtTail, TagFoundPartial, TagFoundComplete, TagProcessed}; @@ -88,13 +79,12 @@ class MetaDataID3Base { /// find the tag position in the string - if not found we return -1; int findTag(const char* tag, const char*str, size_t len){ - if (tag==nullptr || str==nullptr || len<=0) return -1; + if (str==nullptr || len<=0) return -1; // The tags are usally in the first 500 bytes - we limit the search if (len>1600){ len = 1600; } size_t tag_len = strlen(tag); - if (tag_len >= len) return -1; for (size_t j=0;j<=len-tag_len-1;j++){ if (memcmp(str+j,tag, tag_len)==0){ return j; @@ -176,7 +166,7 @@ class MetaDataID3V1 : public MetaDataID3Base { if (tag_ext!=nullptr){ if (len-pos>=sizeof(ID3v1Enhanced)){ memcpy(tag,data+pos,sizeof(ID3v1Enhanced)); - processnotifyAudioChange(); + processNotify(); } else { use_bytes_of_next_write = min(sizeof(ID3v1Enhanced), len-pos); memcpy(tag_ext, data+pos, use_bytes_of_next_write); @@ -190,7 +180,7 @@ class MetaDataID3V1 : public MetaDataID3Base { if (tag!=nullptr){ if (len-pos>=sizeof(ID3v1)){ memcpy(tag,data+pos,sizeof(ID3v1)); - processnotifyAudioChange(); + processNotify(); } else { use_bytes_of_next_write = min(sizeof(ID3v1), len-pos); memcpy(tag,data+pos,use_bytes_of_next_write); @@ -225,12 +215,12 @@ class MetaDataID3V1 : public MetaDataID3Base { tag_ext = new ID3v1Enhanced(); memcpy(tag,tag_str, 4); memcpy(tag,data+len,sizeof(ID3v1Enhanced)); - processnotifyAudioChange(); + processNotify(); } else if (strncmp((char*)tag_str,"TAG",3)==0){ tag = new ID3v1(); memcpy(tag,tag_str, 3); memcpy(tag,data+len,sizeof(ID3v1)); - processnotifyAudioChange(); + processNotify(); } } @@ -239,34 +229,34 @@ class MetaDataID3V1 : public MetaDataID3Base { if (tag!=nullptr){ int remainder = sizeof(ID3v1) - use_bytes_of_next_write; memcpy(tag,data+use_bytes_of_next_write,remainder); - processnotifyAudioChange(); + processNotify(); use_bytes_of_next_write = 0; } else if (tag_ext!=nullptr){ int remainder = sizeof(ID3v1Enhanced) - use_bytes_of_next_write; memcpy(tag_ext,data+use_bytes_of_next_write,remainder); - processnotifyAudioChange(); + processNotify(); use_bytes_of_next_write = 0; } } /// executes the callbacks - void processnotifyAudioChange() { + void processNotify() { if (callback==nullptr) return; if (tag_ext!=nullptr){ - callback(Title, tag_ext->title,strnlength(tag_ext->title,60)); - callback(Artist, tag_ext->artist,strnlength(tag_ext->artist,60)); - callback(Album, tag_ext->album,strnlength(tag_ext->album,60)); - callback(Genre, tag_ext->genre,strnlength(tag_ext->genre,30)); + callback(Title, tag_ext->title,strnlen(tag_ext->title,60)); + callback(Artist, tag_ext->artist,strnlen(tag_ext->artist,60)); + callback(Album, tag_ext->album,strnlen(tag_ext->album,60)); + callback(Genre, tag_ext->genre,strnlen(tag_ext->genre,30)); delete tag_ext; tag_ext = nullptr; status = TagProcessed; } if (tag!=nullptr){ - callback(Title, tag->title,strnlength(tag->title,30)); - callback(Artist, tag->artist,strnlength(tag->artist,30)); - callback(Album, tag->album,strnlength(tag->album,30)); + callback(Title, tag->title,strnlen(tag->title,30)); + callback(Artist, tag->artist,strnlen(tag->artist,30)); + callback(Album, tag->album,strnlen(tag->album,30)); uint16_t genre = tag->genre; if (genre < sizeof(genres)){ const char* genre_str = genres[genre]; @@ -287,7 +277,7 @@ class MetaDataID3V1 : public MetaDataID3Base { #define ExperimentalIndicatorFlag 0x10 // Relevant v2 Tags -static const char* id3_v2_tags[] = {"TALB", "TOPE", "TPE1", "TIT2", "TCON"}; +INLINE_VAR const char* id3_v2_tags[] = {"TALB", "TOPE", "TPE1", "TIT2", "TCON"}; // ID3 verion 2 TAG Header (10 bytes) @ingroup metadata-id3 @@ -327,7 +317,7 @@ struct ID3v2FrameString { uint8_t encoding; // encoding byte for strings }; -static const int ID3FrameSize = 11; +INLINE_VAR const int ID3FrameSize = 11; /** * @brief Simple ID3 Meta Data API which supports ID3 V2: We only support the "TALB", "TOPE", "TIT2", "TCON" tags @@ -346,7 +336,6 @@ class MetaDataID3V2 : public MetaDataID3Base { actual_tag = nullptr; tag_active = false; tag_processed = false; - result.resize(result_size); } /// Ends the processing and releases the memory @@ -391,14 +380,6 @@ class MetaDataID3V2 : public MetaDataID3Base { return tag_processed; } - /// Defines the result buffer size (default is 256); - void resize(int size){ - result_size = size; - if (result.size()==0) { - result.resize(result_size); - } - } - protected: ID3v2 tagv2; bool tag_active = false; @@ -407,8 +388,7 @@ class MetaDataID3V2 : public MetaDataID3Base { const char* actual_tag; ID3v2FrameString frame_header; int use_bytes_of_next_write = 0; - int result_size = 256; - Vector result{0}; + char result[256]; uint64_t total_len = 0; uint64_t end_len = 0; @@ -458,12 +438,12 @@ class MetaDataID3V2 : public MetaDataID3Base { // get tag content if(calcSize(frame_header.size) <= len){ - int l = min(calcSize(frame_header.size)-1, (uint32_t) result.size()); - memset(result.data(), 0, result.size()); - strncpy((char*)result.data(), (char*) data+tag_pos+ID3FrameSize, l); + int l = min(calcSize(frame_header.size)-1, (uint32_t) 256); + memset(result,0,256); + strncpy((char*)result, (char*) data+tag_pos+ID3FrameSize, l); int checkLen = min(l, 10); - if (isAscii(checkLen) || AUDIOTOOLS_ID3_TAG_ALLOW_NONASCII){ - processnotifyAudioChange(); + if (isAscii(checkLen)){ + processNotify(); } else { LOGW("TAG %s ignored", tag); } @@ -479,7 +459,7 @@ class MetaDataID3V2 : public MetaDataID3Base { int tag_pos = findTag(partial_tag, (const char*) data, len); memmove(&frame_header, data+tag_pos, sizeof(ID3v2FrameString)); int size = min(len - tag_pos, (size_t) calcSize(frame_header.size)-1); - strncpy((char*)result.data(), (char*)data+tag_pos+ID3FrameSize, size); + strncpy((char*)result, (char*)data+tag_pos+ID3FrameSize, size); use_bytes_of_next_write = size; status = PartialTagAtTail; } @@ -488,15 +468,13 @@ class MetaDataID3V2 : public MetaDataID3Base { total_len += len; } - - int isCharAscii(int ch) {return ch >= 0 && ch < 128; } /// Make sure that the result is a valid ASCII string bool isAscii(int l){ // check on first 10 characters int m = l < 5 ? l : 10; for (int j=0; j0){ // we just use the first entry result[end_pos]=0; - int idx = atoi(result.data()+1); + int idx = atoi(result+1); if (idx>=0 && idx< (int)sizeof(genres)){ - strncpy((char*)result.data(), genres[idx], result.size()); + strncpy((char*)result,genres[idx],256); } } } - callback(Genre, result.data(),strnlength(result.data(), result.size())); + callback(Genre, result,strnlen(result, 256)); } } } @@ -595,18 +573,13 @@ class MetaDataID3 : public AbstractMetaData { } /// Provide tha audio data to the API to parse for Meta Data - virtual size_t write(const uint8_t *data, size_t len){ + virtual size_t write(const uint8_t *data, size_t length){ TRACED(); - if (filter & SELECT_ID3V2) id3v2.write(data, len); + if (filter & SELECT_ID3V2) id3v2.write(data, length); if (!id3v2.isProcessed()) { - if (filter & SELECT_ID3V1) id3v1.write(data, len); + if (filter & SELECT_ID3V1) id3v1.write(data, length); } - return len; - } - - /// Defines the ID3V3 result buffer size (default is 256); - void resize(int size){ - id3v2.resize(size); + return length; } protected: @@ -615,4 +588,4 @@ class MetaDataID3 : public AbstractMetaData { int filter = SELECT_ID3; }; -} // namespace +} // namespace \ No newline at end of file diff --git a/src/AudioTools/CoreAudio/AudioMetaData/README.md b/src/AudioMetaData/README.md similarity index 100% rename from src/AudioTools/CoreAudio/AudioMetaData/README.md rename to src/AudioMetaData/README.md diff --git a/src/AudioPWM/AudioPWM.h b/src/AudioPWM/AudioPWM.h new file mode 100644 index 0000000000..b6c73bb2b5 --- /dev/null +++ b/src/AudioPWM/AudioPWM.h @@ -0,0 +1,107 @@ +#pragma once +#include "AudioConfig.h" +#if defined(USE_PWM) + +#include "AudioPWM/PWMAudioESP32.h" +#include "AudioPWM/PWMAudioRP2040.h" +#include "AudioPWM/PWMAudioMBED.h" +#include "AudioPWM/PWMAudioSTM32.h" +// this is experimental at the moment +#include "AudioPWM/PWMAudioAVR.h" + + +namespace audio_tools { + +/** + * @brief Common functionality for PWM output. + * Please use the PWMAudioOutput typedef instead which references the implementation + * @ingroup io + */ +class PWMAudioOutput : public AudioOutput { + public: + ~PWMAudioOutput(){ + if (driver.isTimerStarted()){ + end(); + } + } + + virtual PWMConfig defaultConfig() { + return default_config; + } + + PWMConfig config() { + return audio_config; + } + + /// updates the sample rate dynamically + virtual void setAudioInfo(AudioInfo info) { + TRACEI(); + PWMConfig cfg = audio_config; + if (cfg.sample_rate != info.sample_rate + || cfg.channels != info.channels + || cfg.bits_per_sample != info.bits_per_sample) { + cfg.sample_rate = info.sample_rate; + cfg.bits_per_sample = info.bits_per_sample; + cfg.channels = info.channels; + cfg.logInfo(); + end(); + begin(cfg); + } + } + + // /// Starts the PWMAudio using callbacks + bool begin(uint16_t sampleRate, uint8_t channels, PWMCallbackType cb) { + TRACED(); + audio_config.channels = channels; + audio_config.sample_rate = sampleRate; + driver.setUserCallback(cb); + return begin(); + } + + + /// starts the processing using Streams + bool begin(PWMConfig config ) { + TRACED(); + this->audio_config = config; + return driver.begin(audio_config); + } + + bool begin() { + TRACED(); + return driver.begin(audio_config); + } + + virtual void end() { + driver.end(); + } + + int availableForWrite() override { + return driver.availableForWrite(); + } + + // blocking write for an array: we expect a singed value and convert it into a unsigned + size_t write(const uint8_t *wrt_buffer, size_t size) override{ + return driver.write(wrt_buffer, size); + } + + // When the timer does not have enough data we increase the underflow_count; + uint32_t underflowsPerSecond(){ + return driver.underflowsPerSecond(); + } + // provides the effectivly measured output frames per second + uint32_t framesPerSecond(){ + return driver.framesPerSecond(); + } + + protected: + PWMConfig audio_config; + PWMDriver driver; // platform specific driver + +}; + +// legacy name +using PWMAudioStream = PWMAudioOutput; + +} + +#endif diff --git a/src/AudioPWM/PWMAudioAVR.h b/src/AudioPWM/PWMAudioAVR.h new file mode 100644 index 0000000000..23b77b2aa7 --- /dev/null +++ b/src/AudioPWM/PWMAudioAVR.h @@ -0,0 +1,151 @@ + +#pragma once +#include "AudioConfig.h" +#if defined(USE_PWM) && defined(__AVR__) +#include "AudioPWM/PWMAudioBase.h" +#include "AudioTimer/AudioTimerAVR.h" + +namespace audio_tools { + +class PWMDriverAVR; +using PWMDriver = PWMDriverAVR; +static PWMDriverAVR *accessAudioPWM = nullptr; + + +/** + * @brief Experimental: Audio output to PWM pins for the AVR. The AVR supports only up to 2 channels. + * @ingroup platform + * @author Phil Schatzmann + * @copyright GPLv3 + */ + +class PWMDriverAVR : public DriverPWMBase { + friend void defaultPWMAudioOutputCallback(); + + public: + + PWMDriverAVR(){ + LOGD("PWMDriverAVR"); + accessAudioPWM = this; + } + + virtual int maxChannels() { + return 2; + }; + + // Ends the output + virtual void end(){ + TRACED(); + noInterrupts(); + // stop timer callback + TCCR1B = 0; + // stop pwm timers + TCCR2A = 0; + interrupts(); // enable all interrupts + + is_timer_started = false; + } + + void setupTimer() { + TRACED(); + // CPU Frequency 16 MHz + // prescaler 1, 256 or 1024 => no prescaling + uint32_t steps = F_CPU / 8 / audio_config.sample_rate; // e.g. (16000000/8/44100=>45) + if (steps>65535){ + LOGE("requested sample rate not supported: %d - we use %d",audio_config.sample_rate, F_CPU / 65536); + steps = 65535; + } else { + LOGD("compare match register set to %d",steps); + } + + // setup timer intterupt + noInterrupts(); + TCCR1B = 0; + //compare match register + OCR1A = steps; + TCCR1B |= (1 << WGM12); // CTC mode + //TCCR1B |= (1 << CS10); // prescaler 1 + TCCR1B |= (1 << CS11); // prescaler 8 + TIMSK1 |= (1 << OCIE1A); // enable timer compare interrupt + interrupts(); // enable all interrupts + } + + /// Setup LED PWM + void setupPWM(){ + TRACED(); + if (audio_config.channels>2) { + LOGW("Max 2 channels supported - you requested %d", audio_config.channels); + audio_config.channels = 2; + } + + for (int j=0;j only Timer2 is available for PWM + void setupPin(int pin){ + switch(pin){ + case 3: + case 11: + // switch PWM frequency to 62500.00 Hz + TCCR2B = TCCR2B & B11111000 | B00000001; + LOGI("PWM Frequency changed for D3 and D11"); + break; + + default: + LOGE("PWM Unsupported pin: %d", pin); + break; + + } + pinMode(pin, OUTPUT); + } + + virtual void pwmWrite(int channel, int value){ + analogWrite(pins[channel], value); + } + + void logConfig(){ + audio_config.logConfig(); + LOGI("pwm freq: %f khz", 62.5); + if (audio_config.channels==1) { + LOGI("output pin: %d", pins[0]); + } else { + LOGI("output pins: %d / %d", pins[0],pins[1]); + } + } + + protected: + int pins[2] = {3, 11}; + + virtual int maxOutputValue(){ + return 255; + } + +}; + +/// separate method that can be defined as friend so that we can access protected information +void defaultPWMAudioOutputCallback(){ + if (accessAudioPWM!=nullptr && accessAudioPWM->is_timer_started){ + accessAudioPWM->playNextFrame(); + } +} + +/// timer callback: write the next frame to the pins +ISR(TIMER1_COMPA_vect){ + defaultPWMAudioOutputCallback(); + TimerAlarmRepeatingDriverAVR::tickerCallback(); + +} + + +} // Namespace + + +#endif + diff --git a/src/AudioPWM/PWMAudioBase.h b/src/AudioPWM/PWMAudioBase.h new file mode 100644 index 0000000000..dfbbbed45a --- /dev/null +++ b/src/AudioPWM/PWMAudioBase.h @@ -0,0 +1,285 @@ +#pragma once + +#include "AudioConfig.h" +#ifdef USE_PWM + +#include "AudioTools.h" +#include "AudioTools/AudioLogger.h" +#include "AudioTools/AudioOutput.h" +#include "AudioTools/AudioTypes.h" +#include "AudioBasic/Collections.h" + +#define READ_ERROR_MSG "Could not read full data" + +namespace audio_tools { + +// forward declarations +// Callback for user +typedef bool (*PWMCallbackType)(uint8_t channels, int16_t* data); +// Callback used by system +static void defaultPWMAudioOutputCallback(); +// Driver classes +class PWMDriverESP32; +class PWMDriverRP2040; +class PWMDriverMBED; +class PWMDriverSTM32; + +/** + * @brief Configuration data for PWM audio output + * @author Phil Schatzmann + * @copyright GPLv3 + */ +struct PWMConfig : public AudioInfo { + + PWMConfig(){ + // default basic information + sample_rate = 8000u; // sample rate in Hz + channels = 1; + bits_per_sample = 16; + } + + // basic pwm information + uint16_t buffer_size = PWM_BUFFER_SIZE; + uint8_t buffers = PWM_BUFFERS; + + // additinal info + uint16_t pwm_frequency = PWM_AUDIO_FREQUENCY; // audable range is from 20 to 20,000Hz (not used by ESP32) + uint8_t resolution = 8; // Only used by ESP32: must be between 8 and 11 -> drives pwm frequency + uint8_t timer_id = 0; // Only used by ESP32 must be between 0 and 3 + +#ifndef __AVR__ + uint16_t start_pin = PIN_PWM_START; + + /// Defines the pins and the corresponding number of channels (=number of pins) + void setPins(Pins &pins){ + channels = pins.size(); + pins_data = pins; + } + + /// Determines the pins (for all channels) + Pins &pins() { + if (pins_data.size()==0){ + pins_data.resize(channels); + for (int j=0;jmaxChannels()){ + LOGE("Only max %d channels are supported!",maxChannels()); + return false; + } + // allocate buffer if necessary + if (user_callback==nullptr) { + if (buffer!=nullptr){ + delete buffer; + buffer = nullptr; + } + LOGI("->Allocating new buffer %d * %d bytes",audio_config.buffers, audio_config.buffer_size); + buffer = new NBuffer(audio_config.buffer_size, audio_config.buffers); + } + + // initialize if necessary + if (!isTimerStarted()){ + audio_config.logConfig(); + setupPWM(); + setupTimer(); + } + + // reset class variables + underflow_count = 0; + underflow_per_second = 0; + frame_count = 0; + frames_per_second = 0; + + LOGI("->Buffer available: %d", buffer->available()); + LOGI("->Buffer available for write: %d", buffer->availableForWrite()); + LOGI("->is_timer_started: %s ", isTimerStarted() ? "true" : "false"); + return true; + } + + virtual int availableForWrite() { + return buffer==nullptr ? 0 : buffer->availableForWrite(); + } + + // blocking write for an array: we expect a singed value and convert it into a unsigned + virtual size_t write(const uint8_t *wrt_buffer, size_t size){ + size_t available = min((size_t)availableForWrite(),size); + LOGD("write: %u bytes -> %u", (unsigned int)size, (unsigned int)available); + size_t result = buffer->writeArray(wrt_buffer, available); + if (result!=available){ + LOGW("Could not write all data: %u -> %d", (unsigned int) size, result); + } + // activate the timer now - if not already done + startTimer(); + return result; + } + + // When the timer does not have enough data we increase the underflow_count; + uint32_t underflowsPerSecond(){ + return underflow_per_second; + } + // provides the effectivly measured output frames per second + uint32_t framesPerSecond(){ + return frames_per_second; + } + + inline void updateStatistics(){ + frame_count++; + if (millis()>=time_1_sec){ + time_1_sec = millis()+1000; + frames_per_second = frame_count; + underflow_per_second = underflow_count; + underflow_count = 0; + frame_count = 0; + } + } + + void setUserCallback(PWMCallbackType cb){ + user_callback = cb; + } + + bool isTimerStarted() { + return is_timer_started; + } + + virtual void setupPWM() = 0; + virtual void setupTimer() = 0; + virtual void startTimer() = 0; + virtual int maxChannels() = 0; + virtual int maxOutputValue() = 0; + virtual void end() = 0; + + virtual void pwmWrite(int channel, int value) = 0; + + protected: + PWMConfig audio_config; + NBuffer *buffer = nullptr; + PWMCallbackType user_callback = nullptr; + uint32_t underflow_count = 0; + uint32_t underflow_per_second = 0; + uint32_t frame_count = 0; + uint32_t frames_per_second = 0; + uint32_t time_1_sec; + bool is_timer_started = false; + + void playNextFrameCallback(){ + //TRACED(); + uint8_t channels = audio_config.channels; + int16_t data[channels]; + if (user_callback(channels, data)){ + for (uint8_t j=0;javailable() >= required){ + for (int j=0;jread(); + if (value<0){ + LOGE(READ_ERROR_MSG); + value = 0; + } + result = map(value, -NumberConverter::maxValue(8), NumberConverter::maxValue(8), 0, maxOutputValue()); + break; + } + case 16: { + int16_t value; + if (buffer->readArray((uint8_t*)&value,2)!=2){ + LOGE(READ_ERROR_MSG); + } + result = map(value, -NumberConverter::maxValue(16), NumberConverter::maxValue(16), 0, maxOutputValue()); + break; + } + case 24: { + int24_t value; + if (buffer->readArray((uint8_t*)&value,3)!=3){ + LOGE(READ_ERROR_MSG); + } + result = map((int32_t)value, -NumberConverter::maxValue(24), NumberConverter::maxValue(24), 0, maxOutputValue()); + break; + } + case 32: { + int32_t value; + if (buffer->readArray((uint8_t*)&value,4)!=4){ + LOGE(READ_ERROR_MSG); + } + result = map(value, -NumberConverter::maxValue(32), NumberConverter::maxValue(32), 0, maxOutputValue()); + break; + } + } + return result; + } +}; + +} // ns + +#endif diff --git a/src/AudioPWM/PWMAudioESP32.h b/src/AudioPWM/PWMAudioESP32.h new file mode 100644 index 0000000000..57f025796a --- /dev/null +++ b/src/AudioPWM/PWMAudioESP32.h @@ -0,0 +1,163 @@ + +#pragma once +#ifdef ESP32 +#include "AudioPWM/PWMAudioBase.h" + + +namespace audio_tools { + +// forward declaration +class PWMDriverESP32; +/** + * @typedef DriverPWMBase + * @brief Please use DriverPWMBase! + */ +using PWMDriver = PWMDriverESP32; +static PWMDriverESP32 *accessAudioPWM = nullptr; +void IRAM_ATTR defaultPWMAudioOutputCallback(); + + +/** + * @brief Information for a PIN + * @author Phil Schatzmann + * @copyright GPLv3 + */ +struct PinInfoESP32 { + int pwm_channel; + int gpio; +}; + +typedef PinInfoESP32 PinInfo; + +/** + * @brief Audio output to PWM pins for the ESP32. The ESP32 supports up to 16 channels. + * @ingroup platform + * @author Phil Schatzmann + * @copyright GPLv3 + */ + +class PWMDriverESP32 : public DriverPWMBase { + public: + friend void defaultPWMAudioOutputCallback(); + + PWMDriverESP32(){ + TRACED(); + accessAudioPWM = this; + } + + // Ends the output + virtual void end(){ + TRACED(); + timerAlarmDisable(timer); + for (int j=0;j we activate the timer to start with the output of data + virtual void startTimer(){ + if (!is_timer_started){ + audio_config = audioInfo(); + LOGI("timerAlarmEnable"); + is_timer_started = true; + timerAlarmEnable(timer); + } + } + + /// Setup LED PWM + virtual void setupPWM(){ + TRACED(); + // frequency is driven by selected resolution + uint32_t freq = frequency(audio_config.resolution)*1000; + audio_config.pwm_frequency = freq; + + pins.resize(audio_config.channels); + for (int j=0;j ledcSetup: frequency=%d / resolution=%d", freq, audio_config.resolution); + ledcSetup(pwmChannel, freq, audio_config.resolution); + LOGD("-> ledcAttachPin: %d", pins[j].gpio); + ledcAttachPin(pins[j].gpio, pwmChannel); + } + logPins(); + } + + void logPins(){ + for (int j=0;j timer counter is %zu", counter); + LOGD("-> timerAttachInterrupt"); + bool interrupt_edge_type = true; + timerAttachInterrupt(timer, defaultPWMAudioOutputCallback, interrupt_edge_type); + LOGD("-> timerAlarmWrite"); + bool auto_reload = true; + timerAlarmWrite(timer, counter, auto_reload); // Timer fires at ~44100Hz [40Mhz / 907] + } + + /// write a pwm value to the indicated channel. The max value depends on the resolution + virtual void pwmWrite(int channel, int value){ + ledcWrite(pins[channel].pwm_channel, value); + } + + protected: + Vector pins; + hw_timer_t * timer = nullptr; + portMUX_TYPE timerMux = portMUX_INITIALIZER_UNLOCKED; + + /// provides the max value for the indicated resulution + int maxUnsignedValue(int resolution){ + return pow(2,resolution); + } + + virtual int maxChannels() { + return 16; + }; + + /// provides the max value for the configured resulution + virtual int maxOutputValue(){ + return maxUnsignedValue(audio_config.resolution); + } + + /// determiens the PWM frequency based on the requested resolution + float frequency(int resolution) { + switch (resolution){ + case 8: return 312.5; + case 9: return 156.25; + case 10: return 78.125; + case 11: return 39.0625; + } + return 312.5; + } + + +}; + /// timer callback: write the next frame to the pins +void IRAM_ATTR defaultPWMAudioOutputCallback() { + if (accessAudioPWM!=nullptr){ + portENTER_CRITICAL_ISR(&(accessAudioPWM->timerMux)); + accessAudioPWM->playNextFrame(); + portEXIT_CRITICAL_ISR(&(accessAudioPWM->timerMux)); + } +} + + +} + +#endif + diff --git a/src/AudioPWM/PWMAudioMBED.h b/src/AudioPWM/PWMAudioMBED.h new file mode 100644 index 0000000000..342117b807 --- /dev/null +++ b/src/AudioPWM/PWMAudioMBED.h @@ -0,0 +1,119 @@ + +#pragma once +#if defined(__arm__) && __has_include("mbed.h") +#include "AudioPWM/PWMAudioBase.h" +#include "AudioTimer/AudioTimer.h" +#include "mbed.h" + + +namespace audio_tools { + +// forward declaration +class PWMDriverMBED; +/** + * @typedef DriverPWMBase + * @brief Please use DriverPWMBase! + */ +using PWMDriver = PWMDriverMBED; + +/** + * @brief Audio output to PWM pins for MBED based Arduino implementations + * @ingroup platform + * @author Phil Schatzmann + * @copyright GPLv3 + */ + +class PWMDriverMBED : public DriverPWMBase { + + public: + + PWMDriverMBED(){ + LOGD("PWMDriverMBED"); + } + + // Ends the output + virtual void end() override { + TRACED(); + ticker.end(); // it does not hurt to call this even if it has not been started + is_timer_started = false; + + // stop and release pins + for (int j=0;jsuspend(); + delete pins[j]; + pins[j] = nullptr; + } + } + pins.clear(); + //pins.shrink_to_fit(); + } + + + protected: + Vector pins; + TimerAlarmRepeating ticker; // calls a callback repeatedly with a timeout + + /// when we get the first write -> we activate the timer to start with the output of data + virtual void startTimer() override { + if (!is_timer_started){ + TRACED(); + long wait_time = 1000000l / audio_config.sample_rate; + ticker.setCallbackParameter(this); + ticker.begin(defaultPWMAudioOutputCallback, wait_time, US); + is_timer_started = true; + } + } + + /// Setup PWM Pins + virtual void setupPWM(){ + TRACED(); + unsigned long period = 1000000l / audio_config.pwm_frequency; // -> 30.517578125 microseconds + pins.resize(audio_config.channels); + for (int j=0;jperiod_us(period); + pin->write(0.0f); // 0% duty cycle -> + pin->resume(); // in case it was suspended before + pins[j] = pin; + } + } + + /// not used -> see startTimer(); + virtual void setupTimer() { + } + + virtual int maxChannels() { + return 16; + }; + + /// provides the max value for the configured resulution + virtual int maxOutputValue(){ + return 1000; + } + + /// write a pwm value to the indicated channel. The max value depends on the resolution + virtual void pwmWrite(int channel, int value){ + float float_value = static_cast(value) / maxOutputValue(); + pins[channel]->write(float_value); // pwm the value is between 0.0 and 1.0 + } + + /// timer callback: write the next frame to the pins + static void defaultPWMAudioOutputCallback(void *obj) { + PWMDriverMBED* accessAudioPWM = (PWMDriverMBED*) obj; + if (accessAudioPWM!=nullptr){ + accessAudioPWM->playNextFrame(); + } + } + +}; + + +} // Namespace + + +#endif + diff --git a/src/AudioPWM/PWMAudioRP2040.h b/src/AudioPWM/PWMAudioRP2040.h new file mode 100644 index 0000000000..82712b60b4 --- /dev/null +++ b/src/AudioPWM/PWMAudioRP2040.h @@ -0,0 +1,169 @@ + +#pragma once +#if defined(RP2040_HOWER) +#include "AudioPWM/PWMAudioBase.h" +#include "hardware/gpio.h" +#include "hardware/adc.h" +#include "hardware/pwm.h" +#include "pico/time.h" +#include "hardware/structs/clocks.h" +#include "hardware/clocks.h" +#include "hardware/structs/clocks.h" + +namespace audio_tools { + +// forwrd declaratioin of callback +class PWMDriverRP2040; +/** + * @typedef DriverPWMBase + * @brief Please use DriverPWMBase! + */ +using PWMDriver = PWMDriverRP2040; + +/** + * @brief Rasperry Pico Channel to pin assignments + * @author Phil Schatzmann + * @copyright GPLv3 + */ +struct PicoChannelOut { + int gpio = -1; + int audioChannel; + uint slice; // pico pwm slice + uint channel; // pico pwm channel +}; + + +/** + * @brief Audio output for the Rasperry Pico to PWM pins. + The Raspberry Pi Pico has 8 PWM blocks/slices(1-8) and each PWM block provides up to two PWM outputs(A-B). + * @ingroup platform + * @author Phil Schatzmann + * @copyright GPLv3 + + */ + +class PWMDriverRP2040 : public DriverPWMBase { + //friend bool defaultPWMAudioOutputCallbackPico(repeating_timer* ptr); + + public: + + PWMDriverRP2040(){ + TRACED(); + } + + /// Ends the output -> resets the timer and the pins + void end() override { + TRACED(); + ticker.end(); // it does not hurt to call this even if it has not been started + is_timer_started = false; + for(auto pin : pins) { + if (pin.gpio!=-1){ + pwm_set_enabled(pin.slice, false); + } + } + } + + protected: + Vector pins; + TimerAlarmRepeating ticker; + + virtual void startTimer() override { + if (!is_timer_started){ + TRACED(); + long wait_time = 1000000l / audio_config.sample_rate; + ticker.setCallbackParameter(this); + ticker.begin(defaultPWMAudioOutputCallbackPico, wait_time, US); + is_timer_started = true; + } + is_timer_started = true; + } + + // setup pwm config and all pins + void setupPWM() override { + TRACED(); + pwm_config cfg = setupPWMConfig(); + + // initialize empty pins + PicoChannelOut empty; + pins.resize(audio_config.channels, empty); + + // setup pin values + for (int j=0;j< audio_config.channels;j++) { + int channel = j; + int gpio = audio_config.pins()[j]; + LOGI("PWM pin %d",gpio); + pins[channel].slice = pwm_gpio_to_slice_num(gpio); + pins[channel].channel = pwm_gpio_to_channel(gpio); + pins[channel].audioChannel = j; + pins[channel].gpio = gpio; + + setupPWMPin(cfg, pins[channel]); + } + } + + // defines the pwm_config which will be used to drive the pins + pwm_config setupPWMConfig() { + TRACED(); + // setup pwm frequency + pwm_config pico_pwm_config = pwm_get_default_config(); + int wrap_value = maxOutputValue(); // amplitude of square wave (pwm values -amplitude to amplitude) for one byte + float pwmClockDivider = static_cast(clock_get_hz(clk_sys)) / (audio_config.pwm_frequency * wrap_value); + float clock_speed = static_cast(clock_get_hz(clk_sys)); + LOGI("->wrap_value = %d", wrap_value); + LOGI("->max clock speed = %f", clock_speed); + LOGI("->divider = %f", pwmClockDivider); + LOGI("->clock speed = %f", clock_speed/pwmClockDivider); + pwm_config_set_clkdiv(&pico_pwm_config, pwmClockDivider); + pwm_config_set_clkdiv_mode(&pico_pwm_config, PWM_DIV_FREE_RUNNING); + //pwm_config_set_phase_correct(&pico_pwm_config, false); + pwm_config_set_wrap (&pico_pwm_config, wrap_value); + return pico_pwm_config; + } + + // set up pwm + void setupPWMPin(pwm_config &cfg, PicoChannelOut &pinInfo) { + LOGD("%s for gpio %d",LOG_METHOD, pinInfo.gpio); + // setup pwm pin + int gpio = pinInfo.gpio; + gpio_set_function(gpio, GPIO_FUNC_PWM); + pinInfo.slice = pwm_gpio_to_slice_num(gpio); + pinInfo.channel = pwm_gpio_to_channel(gpio); + pwm_init(pinInfo.slice, &cfg, true); + + // set initial output value + pwm_set_chan_level(pinInfo.slice, pinInfo.channel, 0); + } + + void setupTimer() override { + } + + /// The pico supports max 16 pwm pins + virtual int maxChannels() override{ + return 16; + }; + + /// Max pwm output value + virtual int maxOutputValue()override{ + return std::pow(audio_config.resolution,2)-1; + } + + /// write a pwm value to the indicated channel. The values are between 0 and 255 + void pwmWrite(int audioChannel, int value)override{ + pwm_set_chan_level(pins[audioChannel].slice, pins[audioChannel].channel, value); + } + + // timed output executed at the sampleRate + static void defaultPWMAudioOutputCallbackPico(void* ptr) { + PWMDriverRP2040 *self = (PWMDriverRP2040*) ptr; + self->playNextFrame(); + } + +}; + + + +} // Namespace + + +#endif + diff --git a/src/AudioPWM/PWMAudioSTM32.h b/src/AudioPWM/PWMAudioSTM32.h new file mode 100644 index 0000000000..cc4fa39329 --- /dev/null +++ b/src/AudioPWM/PWMAudioSTM32.h @@ -0,0 +1,212 @@ + +#pragma once +#if defined(STM32) +#include "AudioPWM/PWMAudioBase.h" +#include "AudioTimer/AudioTimer.h" + +namespace audio_tools { + +// forward declaration +class PWMDriverSTM32; +/** + * @typedef DriverPWMBase + * @brief Please use DriverPWMBase! + */ +using PWMDriver = PWMDriverSTM32; + +/** + * @brief Audio output to PWM pins for STM32. We use one timer to generate the sample rate and + * one timer for the PWM signal. + * @ingroup platform + * @author Phil Schatzmann + * @copyright GPLv3 + */ + +class PWMDriverSTM32 : public DriverPWMBase { + + /// @brief PWM information for a single pin + struct PWMPin { + HardwareTimer *p_timer; + int channel; + int max_value; + bool active=false; + int pin; + int pwm_frequency; + + PWMPin() = default; + + PWMPin(HardwareTimer *p_timer, int channel, int pin, int maxValue, int pwmFrequency=30000){ + this->p_timer = p_timer; + this->channel = channel; + this->pin = pin; + this->max_value = maxValue; + this->pwm_frequency = pwmFrequency; + } + + void begin(){ + TRACEI(); + p_timer->setPWM(channel, pin, pwm_frequency, 50); // 30k Hertz, 50% dutycycle + active = true; + } + + void setRate(int rate){ + if (active){ + uint16_t sample = 100.0 * rate / max_value; + p_timer->setCaptureCompare(channel, sample, PERCENT_COMPARE_FORMAT); // 50% + } + } + }; + + class PWM { + public: + + PWM() = default; + + void begin(HardwareTimer *pwm_timer, int pwm_frequency, int maxValue){ + + this->p_timer = pwm_timer; + this->pwm_frequency = pwm_frequency; + this->max_value = maxValue; + } + + void end() { + p_timer->pause(); + } + + bool addPin(int pin){ + LOGI("addPin: %d", pin); + TIM_TypeDef *p_instance = (TIM_TypeDef *)pinmap_peripheral(digitalPinToPinName(pin), PinMap_PWM); + channel = STM_PIN_CHANNEL(pinmap_function(digitalPinToPinName(pin), PinMap_PWM)); + PWMPin pwm_pin{p_timer, channel, pin, max_value, pwm_frequency}; + pins.push_back(pwm_pin); + // make sure that all pins use the same timer ! + if (p_timer->getHandle()->Instance!=p_instance){ + LOGE("Invalid pin %d with timer %s for timer %s",pin, getTimerStr(p_instance), getTimerStr(p_timer->getHandle()->Instance)); + return false; + } + LOGI("Using Timer %s for PWM",getTimerStr(p_instance)); + pins[pins.size()-1].begin(); + return true; + } + + void setRate(int idx, int rate){ + if (idx < pins.size()){ + pins[idx].setRate(rate); + } else { + LOGE("Invalid index: %d", idx); + } + } + + protected: + HardwareTimer *p_timer; + Vector pins; + int channel; + int max_value; + int pwm_frequency; + + const char* getTimerStr(TIM_TypeDef* inst){ + if (inst==TIM1) return "TIM1"; + else if (inst==TIM2) return "TIM2"; + else if (inst==TIM3) return "TIM3"; + else if (inst==TIM4) return "TIM4"; + else if (inst==TIM5) return "TIM5"; + return "N/A"; + } + }; + + public: + + PWMDriverSTM32(){ + TRACED(); + ticker.setTimer(PWM_FREQ_TIMER_NO); + } + + // Ends the output + virtual void end() override { + TRACED(); + ticker.end(); // it does not hurt to call this even if it has not been started + pwm.end(); // stop pwm timer + is_timer_started = false; + if (buffer!=nullptr){ + delete buffer; + buffer = nullptr; + } + } + + /// Defines the timer which is used to generate the PWM signal + void setPWMTimer(HardwareTimer &t){ + p_pwm_timer = &t; + } + + + protected: + TimerAlarmRepeating ticker; // calls a callback repeatedly with a timeout + HardwareTimer *p_pwm_timer=nullptr; + PWM pwm; + int64_t max_value; + + /// when we get the first write -> we activate the timer to start with the output of data + virtual void startTimer() override { + if (!is_timer_started){ + TRACED(); + uint32_t time = AudioTime::toTimeUs(audio_config.sample_rate); + ticker.setCallbackParameter(this); + ticker.begin(defaultPWMAudioOutputCallback, time, US); + is_timer_started = true; + } + } + + /// Setup PWM Pins + virtual void setupPWM(){ + TRACED(); + + // setup pwm timer + if (p_pwm_timer==nullptr){ + p_pwm_timer = new HardwareTimer(PWM_DEFAULT_TIMER); + } + + // setup pins for output + int ch = 0; + pwm.begin(p_pwm_timer, audio_config.pwm_frequency, maxOutputValue()); + for (auto gpio : audio_config.pins()){ + LOGD("Processing channel %d -> pin: %d", ch++, gpio); + pwm.addPin(gpio); + } + } + + + virtual void setupTimer() { + } + + /// One timer supports max 4 output pins + virtual int maxChannels() { + return 4; + }; + + /// provides the max value for the configured resulution + virtual int maxOutputValue(){ + return 10000; + } + + /// write a pwm value to the indicated channel. The max value depends on the resolution + virtual void pwmWrite(int channel, int value){ + //analogWrite(pins[channel], value); + pwm.setRate(channel, value); + } + + /// timer callback: write the next frame to the pins + static void defaultPWMAudioOutputCallback(void *obj) { + PWMDriverSTM32* accessAudioPWM = (PWMDriverSTM32*) obj; + if (accessAudioPWM!=nullptr){ + accessAudioPWM->playNextFrame(); + } + } + +}; + + +} // Namespace + + +#endif + diff --git a/src/AudioTools/CoreAudio/AudioPWM/README.md b/src/AudioPWM/README.md similarity index 100% rename from src/AudioTools/CoreAudio/AudioPWM/README.md rename to src/AudioPWM/README.md diff --git a/src/AudioTimer/AudioTimer.h b/src/AudioTimer/AudioTimer.h new file mode 100644 index 0000000000..f5e47c54dd --- /dev/null +++ b/src/AudioTimer/AudioTimer.h @@ -0,0 +1,71 @@ +#pragma once +/** + * @defgroup timer Timers + * @ingroup tools + * @brief Platform specific timers + */ +#include "AudioConfig.h" +#if defined(USE_TIMER) +#include "AudioTools/AudioLogger.h" +#include "AudioTimer/AudioTimerBase.h" +#include "AudioTimer/AudioTimerESP32.h" +#include "AudioTimer/AudioTimerESP8266.h" +#include "AudioTimer/AudioTimerRP2040.h" +#include "AudioTimer/AudioTimerMBED.h" +#include "AudioTimer/AudioTimerAVR.h" +#include "AudioTimer/AudioTimerSTM32.h" + +namespace audio_tools { + +/** + * @brief Common Interface definition for TimerAlarmRepeating + * @ingroup timer + */ +class TimerAlarmRepeating { + public: + /// @brief Default constructor + TimerAlarmRepeating() = default; + /** + * @brief Construct a new Timer Alarm Repeating object by passing your object + * which has been customized with some special platform specific parameters + * @param driver + */ + TimerAlarmRepeating(TimerAlarmRepeatingDriverBase &driver) { + p_driver = &driver; + }; + virtual ~TimerAlarmRepeating() = default; + + bool begin(repeating_timer_callback_t callback_f, uint32_t time, TimeUnit unit = MS) { + return p_driver->begin(callback_f, time, unit); + } + bool end() { + return p_driver->end(); + }; + + void setCallbackParameter(void* obj){ + p_driver->setCallbackParameter(obj); + } + + void *callbackParameter(){ + return p_driver->callbackParameter(); + } + + virtual void setTimer(int timer){ + p_driver->setTimer(timer); + } + + virtual void setTimerFunction(TimerFunction function=DirectTimerCallback){ + p_driver->setTimerFunction(function); + } + + protected: + void* object=nullptr; + TimerAlarmRepeatingDriver driver; // platform specific driver + TimerAlarmRepeatingDriverBase *p_driver=&driver; + +}; + +} // namespace + +#endif + diff --git a/src/AudioTimer/AudioTimerAVR.h b/src/AudioTimer/AudioTimerAVR.h new file mode 100644 index 0000000000..a622f26713 --- /dev/null +++ b/src/AudioTimer/AudioTimerAVR.h @@ -0,0 +1,95 @@ +#pragma once + +#ifdef __AVR__ +#include "AudioTimer/AudioTimerBase.h" + +namespace audio_tools { +typedef void (* repeating_timer_callback_t )(void* obj); +class TimerAlarmRepeatingDriverAVR; +static TimerAlarmRepeatingDriverAVR *timerAlarmRepeatingRef = nullptr; + + +/** + * @brief Repeating Timer functions for repeated execution: Plaease use the typedef TimerAlarmRepeating + * @ingroup platform + * @author Phil Schatzmann + * @copyright GPLv3 + * + */ +class TimerAlarmRepeatingDriverAVR : public TimerAlarmRepeatingDriverBase { + public: + + TimerAlarmRepeatingDriverAVR() { + timerAlarmRepeatingRef = this; + } + + /** + * Starts the alarm timer + */ + bool begin(repeating_timer_callback_t callback_f, uint32_t time, TimeUnit unit = MS) override { + callback = callback_f; + + // we determine the time in microseconds + uint32_t timeUs = 0; + switch(unit){ + case MS: + timeUs = time * 1000; + break; + case US: + timeUs = time; + break; + } + // frequency = beats / second + setupTimer(1000000 / time); + return true; + } + + // ends the timer and if necessary the task + bool end() override { + TRACED(); + noInterrupts(); + // end timer callback + TCCR1B = 0; + interrupts(); // enable all interrupts + return true; + } + + static void tickerCallback(){ + if (timerAlarmRepeatingRef != nullptr && timerAlarmRepeatingRef->callback!=nullptr) + timerAlarmRepeatingRef->callback(timerAlarmRepeatingRef); + } + + protected: + repeating_timer_callback_t callback = nullptr; + + void setupTimer(uint64_t sample_rate) { + TRACED(); + // CPU Frequency 16 MHz + // prescaler 1, 256 or 1024 => no prescaling + uint32_t steps = F_CPU / 8 / sample_rate; // e.g. (16000000/8/44100=>45) + if (steps>65535){ + LOGE("requested sample rate not supported: %d - we use %d",sample_rate, F_CPU / 65536); + steps = 65535; + } else { + LOGD("compare match register set to %d",steps); + } + + // setup timer intterupt + noInterrupts(); + TCCR1B = 0; + //compare match register + OCR1A = steps; + TCCR1B |= (1 << WGM12); // CTC mode + //TCCR1B |= (1 << CS10); // prescaler 1 + TCCR1B |= (1 << CS11); // prescaler 8 + TIMSK1 |= (1 << OCIE1A); // enable timer compare interrupt + interrupts(); // enable all interrupts + } + +}; + +using TimerAlarmRepeatingDriver = TimerAlarmRepeatingDriverAVR; + +} // namespace + +#endif \ No newline at end of file diff --git a/src/AudioTimer/AudioTimerBase.h b/src/AudioTimer/AudioTimerBase.h new file mode 100644 index 0000000000..1044a2afa5 --- /dev/null +++ b/src/AudioTimer/AudioTimerBase.h @@ -0,0 +1,46 @@ +#pragma once +#include "AudioConfig.h" +#ifdef USE_TIMER +#include "AudioTools/AudioTypes.h" + +/** + * @defgroup timer Timer + * @ingroup tools + * @brief Timer + */ + +namespace audio_tools { + +typedef void (*repeating_timer_callback_t )(void* obj); + +enum TimerFunction {DirectTimerCallback, TimerCallbackInThread, SimpleThreadLoop }; + + +class TimerAlarmRepeatingDriverBase { + public: + virtual ~TimerAlarmRepeatingDriverBase(){ + end(); + } + + virtual bool begin(repeating_timer_callback_t callback_f, uint32_t time, TimeUnit unit = MS) = 0; + virtual bool end() { return false;}; + + void setCallbackParameter(void* obj){ + object = obj; + } + + void *callbackParameter(){ + return object; + } + + virtual void setTimer(int timer){} + virtual void setTimerFunction(TimerFunction function=DirectTimerCallback){} + + protected: + void* object=nullptr; +}; + +} // namespace + +#endif + diff --git a/src/AudioTimer/AudioTimerESP32.h b/src/AudioTimer/AudioTimerESP32.h new file mode 100644 index 0000000000..a431e2a513 --- /dev/null +++ b/src/AudioTimer/AudioTimerESP32.h @@ -0,0 +1,299 @@ +#pragma once + +#if defined(ESP32) && defined(ARDUINO) +#include "AudioTimer/AudioTimerBase.h" +#include + +namespace audio_tools { + +typedef void (* simple_callback )(void); + + +/** + * @brief Internal class to manage User callbacks. An optinal parameter can be passed to the callback method + * @author Phil Schatzmann + * @copyright GPLv3 + */ +class UserCallback { + + public: + void setup(repeating_timer_callback_t my_callback, void *user_data, bool lock ){ + TRACED(); + this->my_callback = my_callback; + this->user_data = user_data; + this->lock = lock; // false when called from Task + } + + IRAM_ATTR void call() { + if (my_callback!=nullptr){ + if (lock) portENTER_CRITICAL_ISR(&timerMux); + my_callback(user_data); + if (lock) portEXIT_CRITICAL_ISR(&timerMux); + } + } + + protected: + repeating_timer_callback_t my_callback = nullptr; + void *user_data = nullptr; + portMUX_TYPE timerMux = portMUX_INITIALIZER_UNLOCKED; + bool lock; + +} INLINE_VAR *simpleUserCallback = nullptr; + + +static IRAM_ATTR void userCallback0() { + simpleUserCallback[0].call(); +} +static IRAM_ATTR void userCallback1() { + simpleUserCallback[1].call(); +} +static IRAM_ATTR void userCallback2() { + simpleUserCallback[2].call(); +} +static IRAM_ATTR void userCallback3() { + simpleUserCallback[3].call(); +} + + +/** + * @brief Internal class to manage the different timer callbacks for the 4 hardware timers + * @author Phil Schatzmann + * @copyright GPLv3 + */ +class TimerCallback { + public: + TimerCallback() { + TRACED(); + timerMux = portMUX_INITIALIZER_UNLOCKED; + p_handler_task = nullptr; + } + + void setup(TaskHandle_t &handler_task){ + TRACED(); + p_handler_task = &handler_task; + } + + IRAM_ATTR void call() { + if (p_handler_task!=nullptr && *p_handler_task!=nullptr) { + // A mutex protects the handler from reentry (which shouldn't happen, but just in case) + portENTER_CRITICAL_ISR(&timerMux); + BaseType_t xHigherPriorityTaskWoken = pdFALSE; + vTaskNotifyGiveFromISR(*p_handler_task, &xHigherPriorityTaskWoken); + if (xHigherPriorityTaskWoken) { + portYIELD_FROM_ISR(); + } + portEXIT_CRITICAL_ISR(&timerMux); + } + } + + protected: + portMUX_TYPE timerMux = portMUX_INITIALIZER_UNLOCKED; + TaskHandle_t *p_handler_task=nullptr; + +} INLINE_VAR *timerCallbackArray = nullptr; + + +static IRAM_ATTR void timerCallback0() { + timerCallbackArray[0].call(); +} +static IRAM_ATTR void timerCallback1() { + timerCallbackArray[1].call(); +} +static IRAM_ATTR void timerCallback2() { + timerCallbackArray[2].call(); +} +static IRAM_ATTR void timerCallback3() { + timerCallbackArray[3].call(); +} + + + +/** + * @brief Repeating Timer functions for simple scheduling of repeated execution. + * The basic logic is taken from https://www.toptal.com/embedded/esp32-audio-sampling. + * Plaease use the typedef TimerAlarmRepeating. + * @ingroup platform + * @author Phil Schatzmann + * @copyright GPLv3 + * + */ +class TimerAlarmRepeatingDriverESP32 : public TimerAlarmRepeatingDriverBase { + + public: + TimerAlarmRepeatingDriverESP32(){ + setTimerFunction(DirectTimerCallback); + setTimer(0); + } + + void setTimer(int id) override { + if (id>=0 && id<4) { + this->timer_id = id; + handler_task = nullptr; + } else { + LOGE("Invalid timer id %d", timer_id); + } + } + + virtual void setTimerFunction(TimerFunction function=DirectTimerCallback) override{ + this->function = function; + } + + /// Starts the alarm timer + bool begin(repeating_timer_callback_t callback_f, uint32_t time, TimeUnit unit = MS) override { + TRACED(); + + // we determine the time in microseconds + switch(unit){ + case MS: + timeUs = time / 1000; + break; + case US: + timeUs = time; + break; + } + LOGI("Timer every: %u us", timeUs); + uint32_t cpu_freq = getCpuFrequencyMhz(); // 80 ? + adc_timer = timerBegin(0, cpu_freq, true); // divider=80 -> 1000000 calls per second + + + switch (function) { + case DirectTimerCallback: + setupDirectTimerCallback(callback_f); + break; + + case TimerCallbackInThread: + setupTimerCallbackInThread(callback_f); + break; + + case SimpleThreadLoop: + setupSimpleThreadLoop(callback_f); + break; + + } + + started = true; + return true; + } + + + /// ends the timer and if necessary the task + bool end() override { + TRACED(); + if (started){ + timerEnd(adc_timer); + if (handler_task!=nullptr){ + vTaskDelete(handler_task); + handler_task = nullptr; + } + } + started = false; + return true; + } + + void setCore(int core){ + this->core = core; + } + + protected: + int timer_id=0; + volatile bool started = false; + TaskHandle_t handler_task = nullptr; + hw_timer_t* adc_timer = nullptr; // our timer + UserCallback user_callback; + TimerFunction function; + int core = 1; + int priority = configMAX_PRIORITIES -1; + uint32_t timeUs; + + + /// direct timer callback + void setupDirectTimerCallback(repeating_timer_callback_t callback_f){ + TRACED(); + // we start the timer which runs the callback in a seprate task + if (timerCallbackArray==nullptr){ + timerCallbackArray = new TimerCallback[4]; + } + + if (timer_id==0) timerAttachInterrupt(adc_timer, timerCallback0, true); + else if (timer_id==1) timerAttachInterrupt(adc_timer, timerCallback1, true); + else if (timer_id==2) timerAttachInterrupt(adc_timer, timerCallback2, true); + else if (timer_id==3) timerAttachInterrupt(adc_timer, timerCallback3, true); + + // we record the callback method and user data + user_callback.setup(callback_f, object, false); + timerCallbackArray[timer_id].setup(handler_task); + timerAlarmWrite(adc_timer, timeUs, true); + + // setup the timercallback + xTaskCreatePinnedToCore(complexTaskHandler, "TimerAlarmRepeatingTask", configMINIMAL_STACK_SIZE+10000, &user_callback, priority, &handler_task, core); + LOGI("Task created on core %d", core); + + timerAlarmEnable(adc_timer); + } + + // timer callback is notifiying task + void setupTimerCallbackInThread(repeating_timer_callback_t callback_f){ + TRACED(); + // We start the timer which executes the callbacks directly + if (simpleUserCallback==nullptr){ + simpleUserCallback = new UserCallback[4]; + } + simpleUserCallback[timer_id].setup(callback_f, object, true); + if (timer_id==0) timerAttachInterrupt(adc_timer, userCallback0, true); + else if (timer_id==1) timerAttachInterrupt(adc_timer, userCallback1, true); + else if (timer_id==2) timerAttachInterrupt(adc_timer, userCallback2, true); + else if (timer_id==3) timerAttachInterrupt(adc_timer, userCallback3, true); + + timerAlarmWrite(adc_timer, timeUs, true); + timerAlarmEnable(adc_timer); + + } + + /// No timer - just a simple task loop + void setupSimpleThreadLoop(repeating_timer_callback_t callback_f){ + TRACED(); + user_callback.setup(callback_f, object, false); + xTaskCreatePinnedToCore(simpleTaskLoop, "TimerAlarmRepeatingTask", configMINIMAL_STACK_SIZE+10000, this, priority, &handler_task, core); + LOGI("Task created on core %d", core); + } + + + /// We can not do any I2C calls in the interrupt handler so we need to do this in a separate task + static void complexTaskHandler(void *param) { + TRACEI(); + UserCallback* cb = (UserCallback*) param; + uint32_t thread_notification; + + while (true) { + // Sleep until the ISR gives us something to do + thread_notification = ulTaskNotifyTake(pdTRUE, pdMS_TO_TICKS(1000)); + // Do something complex and CPU-intensive + if (thread_notification){ + cb->call(); + } + } + } + + /// We can not do any I2C calls in the interrupt handler so we need to do this in a separate task. + static void simpleTaskLoop(void *param) { + TRACEI(); + TimerAlarmRepeatingDriverESP32* ta = (TimerAlarmRepeatingDriverESP32*) param; + + while (true) { + unsigned long end = micros() + ta->timeUs; + ta->user_callback.call(); + long waitUs = end - micros(); + if (waitUs>0){ + delayMicroseconds(waitUs); + } + } + } +}; + +/// @brief use TimerAlarmRepeating! @ingroup timer_esp32 +using TimerAlarmRepeatingDriver = TimerAlarmRepeatingDriverESP32; + + +} + +#endif diff --git a/src/AudioTimer/AudioTimerESP8266.h b/src/AudioTimer/AudioTimerESP8266.h new file mode 100644 index 0000000000..f5af515d66 --- /dev/null +++ b/src/AudioTimer/AudioTimerESP8266.h @@ -0,0 +1,69 @@ +#pragma once + +#ifdef ESP8266 +#include "AudioTimer/AudioTimerBase.h" +#include "Ticker.h" + +namespace audio_tools { + +typedef void (*repeating_timer_callback_t )(void* obj); + +class TimerAlarmRepeatingDriverESP8266; +TimerAlarmRepeatingDriverESP8266 *self; + +/** + * @brief Repeating Timer functions for repeated execution: Plaease use the typedef TimerAlarmRepeating + * @ingroup platform + * @author Phil Schatzmann + * @copyright GPLv3 + * + */ +class TimerAlarmRepeatingDriverESP8266 : public TimerAlarmRepeatingDriverBase { + public: + /** + * We can not do any I2C calls in the interrupt handler so we need to do this in a separate task + */ + static void complexHandler(void *param) { + } + + /** + * Starts the alarm timer + */ + bool begin(repeating_timer_callback_t callback_f, uint32_t time, TimeUnit unit = MS) override { + uint32_t timeUs; + + // we determine the time in microseconds + switch(unit){ + case MS: + timeUs = time / 1000; + break; + case US: + timeUs = time; + break; + } + + ticker.attach_ms(timeUs / 1000, callback_f, (void*)this); + + return true; + } + + // ends the timer and if necessary the task + bool end() override { + ticker.detach(); + return true; + } + + protected: + void (*current_timer_callback)(); + Ticker ticker; +}; + +/// @brief use TimerAlarmRepeating! @ingroup timer_esp8266 +using TimerAlarmRepeatingDriver = TimerAlarmRepeatingDriverESP8266; + + +} + + + +#endif \ No newline at end of file diff --git a/src/AudioTimer/AudioTimerMBED.h b/src/AudioTimer/AudioTimerMBED.h new file mode 100644 index 0000000000..4607fa02e2 --- /dev/null +++ b/src/AudioTimer/AudioTimerMBED.h @@ -0,0 +1,68 @@ +#pragma once + +#if defined(__arm__) && __has_include("mbed.h") +#include "AudioTimer/AudioTimerBase.h" +#include "mbed.h" + +namespace audio_tools { + +class TimerAlarmRepeatingDriverMBED; +static TimerAlarmRepeatingDriverMBED *timerAlarmRepeating = nullptr; + +/** + * @brief Repeating Timer functions for repeated execution: Plaease use the typedef TimerAlarmRepeating + * @ingroup platform + * @author Phil Schatzmann + * @copyright GPLv3 + * + */ +class TimerAlarmRepeatingDriverMBED : public TimerAlarmRepeatingDriverBase { + public: + + TimerAlarmRepeatingDriverMBED() { + timerAlarmRepeating = this; + } + + /** + * Starts the alarm timer + */ + bool begin(repeating_timer_callback_t callback_f, uint32_t time, TimeUnit unit = MS) override { + callback = callback_f; + + // we determine the time in microseconds + switch(unit){ + case MS: + ticker.attach_us(tickerCallback, (us_timestamp_t) time * 1000); + break; + case US: + ticker.attach_us(tickerCallback,(us_timestamp_t) time); + break; + } + return true; + } + + // ends the timer and if necessary the task + bool end(){ + ticker.detach(); + return true; + } + + protected: + mbed::Ticker ticker; + repeating_timer_callback_t callback; + + inline static void tickerCallback(){ + timerAlarmRepeating->callback(timerAlarmRepeating->object); + } + +}; + +/// @brief use TimerAlarmRepeating! @ingroup timer_mbed +using TimerAlarmRepeatingDriver = TimerAlarmRepeatingDriverMBED; + + +} + + + +#endif \ No newline at end of file diff --git a/src/AudioTimer/AudioTimerRP2040.h b/src/AudioTimer/AudioTimerRP2040.h new file mode 100644 index 0000000000..c88d6e84ae --- /dev/null +++ b/src/AudioTimer/AudioTimerRP2040.h @@ -0,0 +1,76 @@ +#pragma once + +#if defined(ARDUINO_ARCH_RP2040) && !defined(ARDUINO_ARCH_MBED) +#include "AudioTimer/AudioTimerBase.h" +#include "hardware/timer.h" +#include "pico/time.h" +#include +#include + +namespace audio_tools { + +typedef void (* my_repeating_timer_callback_t )(void* obj); + +/** + * @brief Repeating Timer functions for repeated execution: Plaease use the typedef TimerAlarmRepeating + * @ingroup platform + * @author Phil Schatzmann + * @copyright GPLv3 + * + */ +class TimerAlarmRepeatingDriverRP2040 : public TimerAlarmRepeatingDriverBase { + public: + + TimerAlarmRepeatingDriverRP2040(){ + alarm_pool_init_default(); + ap = alarm_pool_get_default(); + } + + /** + * Starts the alarm timer + */ + bool begin(const my_repeating_timer_callback_t callback_f, uint32_t time, TimeUnit unit = MS) override { + bool result = false; + LOGI("timer time: %u %s",(unsigned int)time, unit==MS ? "ms": "us"); + this->instanceCallback = callback_f; + + // we determine the time in microseconds + switch(unit){ + case MS: + result = alarm_pool_add_repeating_timer_ms(ap, time, &staticCallback, this, &timer); + break; + case US: + result = alarm_pool_add_repeating_timer_us(ap, time, &staticCallback, this, &timer); + break; + default: + LOGE("Undefined Unit"); + } + + return result; + } + + inline static bool staticCallback(repeating_timer *ptr) { + TimerAlarmRepeatingDriverRP2040 *self = (TimerAlarmRepeatingDriverRP2040 *)ptr->user_data; + self->instanceCallback(self->object); + return true; + } + + // ends the timer and if necessary the task + bool end(){ + return cancel_repeating_timer(&timer); + } + + protected: + alarm_pool_t *ap = nullptr; + repeating_timer_t timer; + my_repeating_timer_callback_t instanceCallback=nullptr; +}; + +/// @brief use TimerAlarmRepeating! @ingroup timer_rp2040 +using TimerAlarmRepeatingDriver = TimerAlarmRepeatingDriverRP2040; + +} + + + +#endif \ No newline at end of file diff --git a/src/AudioTimer/AudioTimerSTM32.h b/src/AudioTimer/AudioTimerSTM32.h new file mode 100644 index 0000000000..b533d46d03 --- /dev/null +++ b/src/AudioTimer/AudioTimerSTM32.h @@ -0,0 +1,80 @@ +#pragma once + +#if defined(STM32) +#include "AudioTimer/AudioTimerBase.h" + +namespace audio_tools { + +class TimerAlarmRepeatingDriverSTM32; +static TimerAlarmRepeatingDriverSTM32 *timerAlarmRepeating = nullptr; +typedef void (* repeating_timer_callback_t )(void* obj); + +/** + * @brief STM32 Repeating Timer functions for repeated execution: Plaease use the typedef TimerAlarmRepeating + * @ingroup platform + * @author Phil Schatzmann + * @copyright GPLv3 + */ +class TimerAlarmRepeatingDriverSTM32 : public TimerAlarmRepeatingDriverBase { + public: + TimerAlarmRepeatingDriverSTM32(){ + setTimer(1); + } + + ~TimerAlarmRepeatingDriverSTM32(){ + end(); + delete this->timer; + } + /// selects the timer: 0 = TIM1, 1 = TIM2,2 = TIM3, 3 = TIM4, 4 = TIM5 + void setTimer(int timerIdx) override { + if (this->timer!=nullptr){ + delete this->timer; + } + this->timer = new HardwareTimer(timers[timerIdx]); + timer_index = timerIdx; + timer->pause(); + } + + /** + * Starts the alarm timer + */ + bool begin(repeating_timer_callback_t callback_f, uint32_t time, TimeUnit unit = MS) override { + TRACEI(); + LOGI("Using timer TIM%d", timer_index+1); + timer->attachInterrupt(std::bind(callback_f, object)); + + // we determine the time in microseconds + switch(unit){ + case MS: + timer->setOverflow(time * 1000, MICROSEC_FORMAT); // 10 Hz + break; + case US: + timer->setOverflow(time, MICROSEC_FORMAT); // 10 Hz + break; + } + timer->resume(); + return true; + } + + // ends the timer and if necessary the task + bool end() override { + TRACEI(); + timer->pause(); + return true; + } + + protected: + HardwareTimer *timer=nullptr; + int timer_index; + TIM_TypeDef *timers[6] = {TIM1, TIM2, TIM3, TIM4, TIM5 }; + +}; + +/// @brief use TimerAlarmRepeating! @ingroup timer_stm32 +using TimerAlarmRepeatingDriver = TimerAlarmRepeatingDriverSTM32; + +} + + + +#endif \ No newline at end of file diff --git a/src/AudioTools/CoreAudio/AudioTimer/README.md b/src/AudioTimer/README.md similarity index 100% rename from src/AudioTools/CoreAudio/AudioTimer/README.md rename to src/AudioTimer/README.md diff --git a/src/AudioTools.h b/src/AudioTools.h index dec4787d82..d710f3f1e3 100644 --- a/src/AudioTools.h +++ b/src/AudioTools.h @@ -20,19 +20,6 @@ * @brief Input/Output */ -/** - * @defgroup communications Communications - * @ingroup main - * @brief Transmit Audio - * Please note that the standard Arduino WiFiClient and WifiServer (to use TCP/IP), Serial or BluetoothSerial are also supported. - */ - -/** - * @defgroup fec FEC - * @ingroup communications - * @brief Forward Error Correction - */ - /** * @defgroup transform Converting Streams * @ingroup main @@ -61,29 +48,57 @@ * @ingroup main * @brief Basic Concepts */ +#include "AudioConfig.h" +#include "AudioTimer/AudioTimer.h" +#include "AudioTools/AudioTypes.h" +#include "AudioTools/Buffers.h" +#include "AudioTools/SynchronizedBuffers.h" +#include "AudioTools/BaseConverter.h" +#include "AudioFilter/Filter.h" +#include "AudioFilter/Equilizer.h" +#include "AudioTools/MusicalNotes.h" +#include "AudioI2S/I2SStream.h" +#include "AudioPWM/AudioPWM.h" +#include "AudioAnalog/AnalogAudio.h" +#include "AudioTools/AudioLogger.h" +#include "AudioTools/AudioStreams.h" +#include "AudioTools/AudioStreamsConverter.h" +#include "AudioTools/AudioOutput.h" +#include "AudioTools/VolumeStream.h" +#include "AudioTools/AudioIO.h" +#include "AudioTools/ResampleStream.h" +#include "AudioTools/StreamCopy.h" +#include "AudioCodecs/AudioEncoded.h" +#include "AudioCodecs/AudioCodecs.h" +#include "AudioEffects/SoundGenerator.h" +#include "AudioEffects/AudioEffects.h" +#include "AudioEffects/PitchShift.h" +#include "AudioMetaData/MetaData.h" +#include "AudioHttp/AudioHttp.h" +#include "AudioTools/Fade.h" +#include "AudioTools/AudioPlayer.h" +/** + * ------------------------------------------------------------------------- + * @brief Optional external libraries + * + */ -#include "AudioToolsConfig.h" - -#if AUDIO_INCLUDE_CORE +#if defined(ARDUINO) && !defined(IS_DESKTOP) +# include "AudioEffects/Synthesizer.h" +#endif -#ifdef USE_CONCURRENCY -# include "AudioTools/AudioLibs/Concurrency.h" +#if defined(USE_I2S) +# include "AudioTools/AudioSPDIF.h" #endif -#include "AudioTools/CoreAudio.h" -#include "AudioTools/AudioCodecs/AudioEncoded.h" -#include "AudioTools/AudioCodecs/AudioCodecs.h" /** * ------------------------------------------------------------------------- - * @brief Optional external libraries + * @brief Set namespace * */ - -#if defined(ARDUINO) && !defined(IS_DESKTOP) -# include "AudioTools/CoreAudio/AudioEffects/Synthesizer.h" +#ifndef NO_AUDIOTOOLS_NS +using namespace audio_tools; #endif -#endif // AUDIO_INCLUDE_CORE - diff --git a/src/AudioTools/AudioActions.h b/src/AudioTools/AudioActions.h new file mode 100644 index 0000000000..1538baac97 --- /dev/null +++ b/src/AudioTools/AudioActions.h @@ -0,0 +1,190 @@ +#pragma once +#include "AudioTools/AudioLogger.h" + +#ifndef ACTIONS_MAX +#define ACTIONS_MAX 20 +#endif + +#ifndef TOUCH_LIMIT +#define TOUCH_LIMIT 20 +#endif + +#ifndef DEBOUNCE_DELAY +#define DEBOUNCE_DELAY 500 +#endif + +namespace audio_tools { + +/** + * @brief A simple class to assign Functions to Pins e.g. to implement a simple + * navigation control or volume control with buttons + * @ingroup tools + */ +class AudioActions { + public: + enum ActiveLogic : uint8_t { + ActiveLow, + ActiveHigh, + ActiveChange, + ActiveTouch + }; + + /// Adds an action + void add(int pin, void (*actionOn)(bool pinStatus, int pin, void* ref), + ActiveLogic activeLogic = ActiveLow, void* ref = nullptr) { + add(pin, actionOn, nullptr, activeLogic, ref); + } + + /// Adds an action + void add(int pin, void (*actionOn)(bool pinStatus, int pin, void* ref), + void (*actionOff)(bool pinStatus, int pin, void* ref), + ActiveLogic activeLogicPar = ActiveLow, void* ref = nullptr) { + LOGI("ActionLogic::add pin: %d / logic: %d", pin, activeLogicPar); + if (maxIdx + 1 >= ACTIONS_MAX) { + LOGE("Too many actions: please increase ACTIONS_MAX") + return; + } + if (pin>0) { + int pos = findPin(pin); + // setup pin mode + if (activeLogicPar == ActiveLow) { + pinMode(pin, INPUT_PULLUP); + LOGI("pin %d -> INPUT_PULLUP", pin); + } else { + pinMode(pin, INPUT); + LOGI("pin %d -> INPUT", pin); + } + + if (pos != -1) { + actions[pos].actionOn = actionOn; + actions[pos].actionOff = actionOff; + actions[pos].activeLogic = activeLogicPar; + actions[pos].ref = ref; + } else { + + actions[maxIdx].pin = pin; + actions[maxIdx].actionOn = actionOn; + actions[maxIdx].actionOff = actionOff; + actions[maxIdx].activeLogic = activeLogicPar; + actions[maxIdx].ref = ref; + maxIdx++; + } + } else { + LOGW("pin %d -> Ignored", pin); + } + } + + /// enable/disable pin actions + void setEnabled(int pin, bool enabled) { + int pos = findPin(pin); + if (pos != -1) { + actions[pos].enabled = enabled; + } + } + + /** + * @brief Execute all actions if the corresponding pin is low + * To minimize the runtime: With each call we process a different pin + */ + void processActions() { + static int pos = 0; + + // execute action + Action* a = &(actions[pos]); + if (a->enabled) { + bool value = readValue(a); + if (a->actionOn != nullptr && a->actionOff != nullptr) { + // we have on and off action defined + if (value != a->lastState) { + // LOGI("processActions: case with on and off"); + // execute action -> reports active instead of pin state + if ((value && a->activeLogic == ActiveHigh) || + (!value && a->activeLogic == ActiveLow)) { + a->actionOn(true, a->pin, a->ref); + } else { + a->actionOff(false, a->pin, a->ref); + } + a->lastState = value; + } + } else if (a->activeLogic == ActiveChange) { + bool active = (a->activeLogic == ActiveLow) ? !value : value; + // reports pin state + if (value != a->lastState && millis() > a->debounceTimeout) { + //LOGI("processActions: ActiveChange"); + // execute action + a->actionOn(active, a->pin, a->ref); + a->lastState = value; + a->debounceTimeout = millis() + DEBOUNCE_DELAY; + } + } else { + bool active = (a->activeLogic == ActiveLow) ? !value : value; + if (active && + (active != a->lastState || millis() > a->debounceTimeout)) { + //LOGI("processActions: %d Active %d - %d", a->pin, value, digitalRead(a->pin)); + // execute action + a->actionOn(active, a->pin, a->ref); + a->lastState = active; + a->debounceTimeout = millis() + DEBOUNCE_DELAY; + } + } + } + pos++; + if (pos >= maxIdx) { + pos = 0; + } + } + + /// Defines the debounce delay + void setDebounceDelay(int value) { debounceDelayValue = value; } + /// Defines the touch limit (Default 20) + void setTouchLimit(int value) { touchLimit = value; } + + protected: + int maxIdx = 0; + int debounceDelayValue = DEBOUNCE_DELAY; + int touchLimit = TOUCH_LIMIT; + + struct Action { + int16_t pin; + void (*actionOn)(bool pinStatus, int pin, void* ref) = nullptr; + void (*actionOff)(bool pinStatus, int pin, void* ref) = nullptr; + void* ref = nullptr; + unsigned long debounceTimeout; + ActiveLogic activeLogic; + bool lastState; + bool enabled = true; + } actions[ACTIONS_MAX]; + + /// determines the value for the action + bool readValue(Action* a) { +#ifdef USE_TOUCH_READ + bool result; + if (a->activeLogic == ActiveTouch) { + int value = touchRead(a->pin); + result = value <= touchLimit; + if (result){ + // retry to confirm reading + value = touchRead(a->pin); + result = value <= touchLimit; + LOGI("touch pin: %d value %d (limit: %d) -> %s", a->pin, value, touchLimit, result ? "true":"false"); + } + } else { + result = digitalRead(a->pin); + } + return result; +#else + return digitalRead(a->pin); +#endif + } + + int findPin(int pin) { + for (int j = 0; j < maxIdx; j++) { + if (actions[j].pin == pin) { + return j; + } + } + return -1; + } +}; + +} // namespace audio_tools diff --git a/src/AudioTools/AudioCodecs/All.h b/src/AudioTools/AudioCodecs/All.h deleted file mode 100644 index 0b183f9bc6..0000000000 --- a/src/AudioTools/AudioCodecs/All.h +++ /dev/null @@ -1,43 +0,0 @@ -#pragma once - -// All codecs, so that we can find any compile errors easily -// This only works, when you have all codecs installed! -#include "AudioTools/AudioCodecs/AudioEncoded.h" -#include "AudioTools/AudioCodecs/ContainerOgg.h" -#include "AudioTools/AudioCodecs/CodecOpus.h" -#include "AudioTools/AudioCodecs/CodecOpusOgg.h" -#include "AudioTools/AudioCodecs/CodecFLAC.h" -#include "AudioTools/AudioCodecs/CodecVorbis.h" -#include "AudioTools/AudioCodecs/CodecADPCM.h" -//#include "AudioTools/AudioCodecs/CodecCodec2.h" -#include "AudioTools/AudioCodecs/CodecGSM.h" -#include "AudioTools/AudioCodecs/CodecMP3Helix.h" -#include "AudioTools/AudioCodecs/CodecAACHelix.h" -#include "AudioTools/AudioCodecs/ContainerBinary.h" -#include "AudioTools/AudioCodecs/CodecADPCMXQ.h" -#include "AudioTools/AudioCodecs/CodecCopy.h" -#include "AudioTools/AudioCodecs/CodecHelix.h" -#include "AudioTools/AudioCodecs/CodecMP3LAME.h" -#include "AudioTools/AudioCodecs/CodecSBC.h" -#include "AudioTools/AudioCodecs/ContainerMP4.h" -#include "AudioTools/AudioCodecs/AudioFormat.h" -#include "AudioTools/AudioCodecs/CodecADTS.h" -#include "AudioTools/AudioCodecs/CodecILBC.h" -#include "AudioTools/AudioCodecs/CodecMP3MAD.h" -#include "AudioTools/AudioCodecs/CodecAACFAAD.h" -#include "AudioTools/AudioCodecs/CodecAPTX.h" -#include "AudioTools/AudioCodecs/CodecFloat.h" -#include "AudioTools/AudioCodecs/CodecL16.h" -#include "AudioTools/AudioCodecs/CodecWAV.h" -#include "AudioTools/AudioCodecs/CodecAACFDK.h" -#include "AudioTools/AudioCodecs/CodecBase64.h" -#include "AudioTools/AudioCodecs/CodecG722.h" -#include "AudioTools/AudioCodecs/CodecL8.h" -#include "AudioTools/AudioCodecs/CodecMTS.h" -#include "AudioTools/AudioCodecs/CodecWavIMA.h" -#include "AudioTools/AudioCodecs/CodecBasic.h" -#include "AudioTools/AudioCodecs/CodecG7xx.h" -#include "AudioTools/AudioCodecs/CodecLC3.h" -#include "AudioTools/AudioCodecs/ContainerAVI.h" -#include "AudioTools/AudioCodecs/DecoderFromStreaming.h" -//#include "AudioTools/AudioCodecs/CodecMP3Mini.h" diff --git a/src/AudioTools/AudioCodecs/AudioCodecs.h b/src/AudioTools/AudioCodecs/AudioCodecs.h deleted file mode 100644 index 896020a02e..0000000000 --- a/src/AudioTools/AudioCodecs/AudioCodecs.h +++ /dev/null @@ -1,32 +0,0 @@ -#pragma once - -/** - * @defgroup codecs Codecs - * @ingroup main - * @brief Audio Coder and Decoder -**/ - -/** - * @defgroup encoder Encoder - * @ingroup codecs - * @brief Audio Encoder -**/ - -/** - * @defgroup decoder Decoder - * @ingroup codecs - * @brief Audio Decoder -**/ - -// codecs that do not require any additional library -#include "AudioTools/AudioCodecs/CodecWAV.h" -#include "AudioTools/AudioCodecs/CodecCopy.h" -#include "AudioTools/AudioCodecs/CodecL8.h" -#include "AudioTools/AudioCodecs/CodecFloat.h" -#include "AudioTools/AudioCodecs/CodecBase64.h" -#include "AudioTools/AudioCodecs/DecoderFromStreaming.h" -#include "AudioTools/AudioCodecs/MultiDecoder.h" -#include "AudioTools/AudioCodecs/CodecMTS.h" -#include "AudioTools/AudioCodecs/CodecADTS.h" -#include "AudioTools/AudioCodecs/CodecNetworkFormat.h" -#include "AudioTools/AudioCodecs/CodecFactory.h" diff --git a/src/AudioTools/AudioCodecs/AudioCodecsBase.h b/src/AudioTools/AudioCodecs/AudioCodecsBase.h deleted file mode 100644 index 7680dd7f02..0000000000 --- a/src/AudioTools/AudioCodecs/AudioCodecsBase.h +++ /dev/null @@ -1,271 +0,0 @@ -#pragma once - -#include "AudioToolsConfig.h" -#include "AudioLogger.h" -#include "AudioTools/CoreAudio/AudioBasic/Collections/Vector.h" -#include "AudioTools/CoreAudio/AudioTypes.h" -#include "AudioTools/CoreAudio/BaseStream.h" -#include "AudioTools/CoreAudio/AudioOutput.h" - -namespace audio_tools { - -/** - * @brief Decoding of encoded audio into PCM data - * @ingroup codecs - * @author Phil Schatzmann - * @copyright GPLv3 - */ -class AudioDecoder : public AudioWriter, public AudioInfoSource { - public: - AudioDecoder() = default; - virtual ~AudioDecoder() = default; - AudioDecoder(AudioDecoder const &) = delete; - AudioDecoder &operator=(AudioDecoder const &) = delete; - - AudioInfo audioInfo() override { return info; }; - - /// for most decoders this is not needed - void setAudioInfo(AudioInfo from) override { - TRACED(); - if (info != from) { - notifyAudioChange(from); - } - info = from; - } - /// Defines where the decoded result is written to - virtual void setOutput(AudioStream &out_stream) { - Print *p_print = &out_stream; - setOutput(*p_print); - addNotifyAudioChange(out_stream); - } - - /// Defines where the decoded result is written to - virtual void setOutput(AudioOutput &out_stream) { - Print *p_print = &out_stream; - setOutput(*p_print); - addNotifyAudioChange(out_stream); - } - - /// Defines where the decoded result is written to - virtual void setOutput(Print &out_stream) override { p_print = &out_stream; } - - /// Returns true to indicate that the decoding result is PCM data - virtual bool isResultPCM() { return true; } - virtual bool begin(AudioInfo info) override { - setAudioInfo(info); - return begin(); - } - bool begin() override { return true; } - void end() override {} - - /// custom id to be used by application - int id; - - Print* getOutput(){ - return p_print; - } - - /// Some decoders need e.g. a magic cookie to provide the relevant info for decoding - virtual bool setCodecConfig(const uint8_t* data, size_t len){ - LOGE("not implemented"); - return false; - } - - protected: - Print *p_print = nullptr; - AudioInfo info; -}; - -/** - * @brief Parent class for all container formats - * @ingroup codecs - * @author Phil Schatzmann - * @copyright GPLv3 - */ - -class ContainerDecoder : public AudioDecoder { - bool isResultPCM() override { return true; } -}; - -/** - * @brief Encoding of PCM data - * @ingroup codecs - * @author Phil Schatzmann - * @copyright GPLv3 - */ -class AudioEncoder : public AudioWriter { - public: - AudioEncoder() = default; - virtual ~AudioEncoder() = default; - AudioEncoder(AudioEncoder const &) = delete; - AudioEncoder &operator=(AudioEncoder const &) = delete; - /// Provides the mime type of the encoded result - virtual const char *mime() = 0; - /// Defines the sample rate, number of channels and bits per sample - void setAudioInfo(AudioInfo from) override { info = from; } - AudioInfo audioInfo() override { return info; } - - protected: - AudioInfo info; -}; - -class AudioDecoderExt : public AudioDecoder { - public: - virtual void setBlockSize(int blockSize) = 0; -}; - -class AudioEncoderExt : public AudioEncoder { - public: - virtual int blockSize() = 0; -}; - -/** - * @brief Dummy no implmentation Codec. This is used so that we can initialize - * some pointers to decoders and encoders to make sure that they do not point to - * null. - * @ingroup codecs - * @author Phil Schatzmann - * @copyright GPLv3 - */ -class CodecNOP : public AudioDecoder, public AudioEncoder { - public: - static CodecNOP *instance() { - static CodecNOP self; - return &self; - } - - virtual bool begin() { return true; } - virtual void end() {} - virtual void setOutput(Print &out_stream) {} - virtual void addNotifyAudioChange(AudioInfoSupport &bi) {} - virtual void setAudioInfo(AudioInfo info) {} - - virtual AudioInfo audioInfo() { - AudioInfo info; - return info; - } - virtual operator bool() { return false; } - virtual int readStream(Stream &in) { return 0; }; - - // just output silence - virtual size_t write(const uint8_t *data, size_t len) { - memset((void *)data, 0, len); - return len; - } - - virtual const char *mime() { return nullptr; } -}; - -/** - * @brief A Streaming Decoder where we provide both the input and output - * as streams. - * @ingroup codecs - * @author Phil Schatzmann - * @copyright GPLv3 - */ -class StreamingDecoder : public AudioInfoSource { - public: - /// Starts the processing - virtual bool begin() = 0; - - /// Releases the reserved memory - virtual void end() = 0; - - /// Defines the output Stream - virtual void setOutput(Print &out_stream) { p_print = &out_stream; } - - /// Defines the output streams and register to be notified - virtual void setOutput(AudioStream &out_stream) { - Print *p_print = &out_stream; - setOutput(*p_print); - addNotifyAudioChange(out_stream); - } - - /// Defines the output streams and register to be notified - virtual void setOutput(AudioOutput &out_stream) { - Print *p_print = &out_stream; - setOutput(*p_print); - addNotifyAudioChange(out_stream); - } - - /// Stream Interface: Decode directly by taking data from the stream. This is - /// more efficient then feeding the decoder with write: just call copy() in - /// the loop - void setInput(Stream &inStream) { this->p_input = &inStream; } - - /// Provides the last available MP3FrameInfo - virtual AudioInfo audioInfo() = 0; - - /// checks if the class is active - virtual operator bool() = 0; - - /// Process a single read operation - to be called in the loop - virtual bool copy() = 0; - - /// Process all data - bool copyAll() { - bool result = false; - while (copy()) { - result = true; - } - return result; - } - - protected: - virtual size_t readBytes(uint8_t *data, size_t len) = 0; - Print *p_print = nullptr; - Stream *p_input = nullptr; -}; - -/** - * @brief Converts any AudioDecoder to a StreamingDecoder - * @ingroup codecs - * @author Phil Schatzmann - * @copyright GPLv3 - */ -class StreamingDecoderAdapter : public StreamingDecoder { - public: - StreamingDecoderAdapter(AudioDecoder &decoder, - int copySize = DEFAULT_BUFFER_SIZE) { - p_decoder = &decoder; - if (copySize > 0) resize(copySize); - } - /// Starts the processing - bool begin() override { return p_input != nullptr && p_decoder->begin(); } - - /// Releases the reserved memory - void end() override { p_decoder->end(); } - - /// Defines the output Stream - void setOutput(Print &out_stream) override { - p_decoder->setOutput(out_stream); - } - - /// Provides the last available MP3FrameInfo - AudioInfo audioInfo() override { return p_decoder->audioInfo(); } - - /// checks if the class is active - virtual operator bool() override { return *p_decoder; } - - /// Process a single read operation - to be called in the loop - virtual bool copy() override { - int read = readBytes(&buffer[0], buffer.size()); - int written = 0; - if (read > 0) written = p_decoder->write(&buffer[0], read); - return written > 0; - } - - /// Adjust the buffer size: the existing content of the buffer is lost! - void resize(int bufferSize) { buffer.resize(bufferSize); } - - protected: - AudioDecoder *p_decoder = nullptr; - Vector buffer{0}; - - size_t readBytes(uint8_t *data, size_t len) override { - if (p_input == nullptr) return 0; - return p_input->readBytes(data, len); - } -}; - -} // namespace audio_tools \ No newline at end of file diff --git a/src/AudioTools/AudioCodecs/AudioEncoded.h b/src/AudioTools/AudioCodecs/AudioEncoded.h deleted file mode 100644 index 10989234c6..0000000000 --- a/src/AudioTools/AudioCodecs/AudioEncoded.h +++ /dev/null @@ -1,450 +0,0 @@ -#pragma once - -#include "AudioCodecsBase.h" -#include "AudioToolsConfig.h" -#include "AudioTools/CoreAudio/AudioLogger.h" -#include "AudioTools/CoreAudio/AudioIO.h" -#include "AudioTools/CoreAudio/AudioOutput.h" -#include "AudioTools/CoreAudio/AudioStreams.h" -#include "AudioTools/CoreAudio/AudioTypes.h" - -namespace audio_tools { - -/** - * @brief A more natural Print class to process encoded data (aac, wav, - * mp3...). Just define the output and the decoder and write the encoded - * data. - * @ingroup transform - * @author Phil Schatzmann - * @copyright GPLv3 - */ -class EncodedAudioOutput : public ModifyingOutput { - public: - EncodedAudioOutput() { active = false; } - - EncodedAudioOutput(AudioDecoder *decoder) { - setDecoder(decoder); - active = false; - } - - EncodedAudioOutput(AudioEncoder *encoder) { - setEncoder(encoder); - active = false; - } - - EncodedAudioOutput(AudioStream *outputStream, AudioDecoder *decoder) { - setDecoder(decoder); - setOutput(outputStream); - active = false; - } - - EncodedAudioOutput(AudioOutput *outputStream, AudioDecoder *decoder) { - setDecoder(decoder); - setOutput(outputStream); - active = false; - } - - EncodedAudioOutput(Print *outputStream, AudioDecoder *decoder) { - setDecoder(decoder); - setOutput(outputStream); - active = false; - } - - EncodedAudioOutput(Print *outputStream, AudioEncoder *encoder) { - setEncoder(encoder); - setOutput(outputStream); - active = false; - } - - EncodedAudioOutput(AudioOutput *outputStream, AudioEncoder *encoder) { - setEncoder(encoder); - setOutput(outputStream); - active = false; - } - - EncodedAudioOutput(AudioStream *outputStream, AudioEncoder *encoder) { - setEncoder(encoder); - setOutput(outputStream); - active = false; - } - - virtual ~EncodedAudioOutput() { end(); } - - /// Define object which need to be notified if the basinfo is changing - void addNotifyAudioChange(AudioInfoSupport &bi) override { - TRACEI(); - decoder_ptr->addNotifyAudioChange(bi); - } - - AudioInfo defaultConfig() { - AudioInfo cfg; - cfg.channels = 2; - cfg.sample_rate = 44100; - cfg.bits_per_sample = 16; - return cfg; - } - - virtual void setAudioInfo(AudioInfo newInfo) override { - TRACED(); - if (this->cfg != newInfo && newInfo.channels != 0 && - newInfo.sample_rate != 0) { - this->cfg = newInfo; - decoder_ptr->setAudioInfo(cfg); - encoder_ptr->setAudioInfo(cfg); - } - } - - void setOutput(Print &outputStream) override { setOutput(&outputStream); } - - /// Defines the output - void setOutput(Print *outputStream) { - ptr_out = outputStream; - if (decoder_ptr != nullptr) { - decoder_ptr->setOutput(*ptr_out); - } - if (encoder_ptr != nullptr) { - encoder_ptr->setOutput(*ptr_out); - } - } - - void setEncoder(AudioEncoder *encoder) { - if (encoder == nullptr) { - encoder = CodecNOP::instance(); - } - encoder_ptr = encoder; - writer_ptr = encoder; - if (ptr_out != nullptr) { - encoder_ptr->setOutput(*ptr_out); - } - } - - AudioEncoder *getEncoder() { return encoder_ptr; } - - void setDecoder(AudioDecoder *decoder) { - if (decoder == nullptr) { - decoder = CodecNOP::instance(); - } - decoder_ptr = decoder; - writer_ptr = decoder; - if (ptr_out != nullptr) { - decoder_ptr->setOutput(*ptr_out); - } - } - - AudioDecoder *getDecoder() { return decoder_ptr; } - - /// Starts the processing - sets the status to active - bool begin() override { - TRACED(); - if (!active) { - TRACED(); - const CodecNOP *nop = CodecNOP::instance(); - if (decoder_ptr != nop || encoder_ptr != nop) { - active = true; - if (!decoder_ptr->begin(cfg)) active = false; - if (!encoder_ptr->begin(cfg)) active = false; - } else { - LOGW("no decoder or encoder defined"); - } - } - return active; - } - - /// Starts the processing - sets the status to active - virtual bool begin(AudioInfo newInfo) override { - cfg = newInfo; - return begin(); - } - - /// Ends the processing - void end() override { - if (active) { - TRACEI(); - decoder_ptr->end(); - encoder_ptr->end(); - active = false; - } - } - - /// encoder decode the data - virtual size_t write(const uint8_t *data, size_t len) override { - if (len == 0) { - // LOGI("write: %d", 0); - return 0; - } - LOGD("EncodedAudioOutput::write: %d", (int)len); - - if (writer_ptr == nullptr || data == nullptr) { - LOGE("NPE"); - return 0; - } - - if (check_available_for_write && availableForWrite() == 0) { - return 0; - } - - size_t result = writer_ptr->write(data, len); - LOGD("EncodedAudioOutput::write: %d -> %d", (int)len, (int)result); - return result; - } - - int availableForWrite() override { - if (!check_available_for_write) return frame_size; - return min(ptr_out->availableForWrite(), frame_size); - } - - /// Returns true if status is active and we still have data to be processed - operator bool() override { return active; } - - /// Provides the initialized decoder - AudioDecoder &decoder() { return *decoder_ptr; } - - /// Provides the initialized encoder - AudioEncoder &encoder() { return *encoder_ptr; } - - /// Is Available for Write check activated ? - bool isCheckAvailableForWrite() { return check_available_for_write; } - - /// defines the size of the decoded frame in bytes - void setFrameSize(int size) { frame_size = size; } - - protected: - // AudioInfo info; - AudioDecoder *decoder_ptr = CodecNOP::instance(); // decoder - AudioEncoder *encoder_ptr = CodecNOP::instance(); // decoder - AudioWriter *writer_ptr = nullptr; - Print *ptr_out = nullptr; - bool active = false; - bool check_available_for_write = false; - int frame_size = DEFAULT_BUFFER_SIZE; -}; - -// legacy name -using EncodedAudioPrint = EncodedAudioOutput; - -/** - * @brief A more natural Stream class to process encoded data (aac, wav, - * mp3...) which also supports the decoding by calling readBytes(). - * @ingroup transform - * @author Phil Schatzmann - * @copyright GPLv3 - */ -class EncodedAudioStream : public ReformatBaseStream { - public: - EncodedAudioStream() = default; - - EncodedAudioStream(AudioStream *ioStream, AudioDecoder *decoder) { - setDecoder(decoder); - setStream(*ioStream); - } - - EncodedAudioStream(Stream *ioStream, AudioDecoder *decoder) { - setDecoder(decoder); - setStream(*ioStream); - } - - EncodedAudioStream(AudioOutput *outputStream, AudioDecoder *decoder) { - setDecoder(decoder); - setOutput(*outputStream); - } - - EncodedAudioStream(Print *outputStream, AudioDecoder *decoder) { - setDecoder(decoder); - setOutput(*outputStream); - } - - EncodedAudioStream(Print *outputStream, AudioEncoder *encoder) { - setEncoder(encoder); - setOutput(*outputStream); - } - - EncodedAudioStream(AudioDecoder *decoder) { setDecoder(decoder); } - - EncodedAudioStream(AudioEncoder *encoder) { setEncoder(encoder); } - - virtual ~EncodedAudioStream() { end(); } - - void setEncoder(AudioEncoder *encoder) { enc_out.setEncoder(encoder); } - - void setDecoder(AudioDecoder *decoder) { enc_out.setDecoder(decoder); } - - AudioEncoder *getEncoder() { return enc_out.getEncoder(); } - - AudioDecoder *getDecoder() { return enc_out.getDecoder(); } - - /// Provides the initialized decoder - AudioDecoder &decoder() { return *getDecoder(); } - - /// Provides the initialized encoder - AudioEncoder &encoder() { return *getEncoder(); } - - void setStream(Stream *stream) { setStream(*stream); } - - void setStream(AudioStream *stream) { setStream(*stream); } - - void setOutput(AudioOutput *stream) { setOutput(*stream); } - - void setOutput(Print *stream) { setOutput(*stream); } - - void setStream(AudioStream &stream) override { - ReformatBaseStream::setStream(stream); - enc_out.setOutput(&stream); - } - - void setStream(Stream &stream) override { - ReformatBaseStream::setStream(stream); - enc_out.setOutput(&stream); - } - - void setOutput(AudioOutput &stream) override { - ReformatBaseStream::setOutput(stream); - enc_out.setOutput(&stream); - } - - void setOutput(Print &out) override { - ReformatBaseStream::setOutput(out); - enc_out.setOutput(&out); - } - - AudioInfo defaultConfig() { - AudioInfo ai; - return ai; - } - - bool begin(AudioInfo info) { - setAudioInfo(info); - return begin(); - } - - bool begin() override { - // is_output_notify = false; - setupReader(); - ReformatBaseStream::begin(); - return enc_out.begin(audioInfo()); - } - - void end() override { - enc_out.end(); - reader.end(); - } - - int availableForWrite() override { return enc_out.availableForWrite(); } - - size_t write(const uint8_t *data, size_t len) override { - // addNotifyOnFirstWrite(); - return enc_out.write(data, len); - } - - size_t readBytes(uint8_t *data, size_t len) override { - return reader.readBytes(data, len); - } - - void addNotifyAudioChange(AudioInfoSupport &bi) override { - enc_out.addNotifyAudioChange(bi); - } - - /// approx compression factor: e.g. mp3 is around 4 - float getByteFactor() override { return byte_factor; } - void setByteFactor(float factor) { byte_factor = factor; } - - /// defines the size of the decoded frame in bytes - void setFrameSize(int size) { enc_out.setFrameSize(size); } - - protected: - EncodedAudioOutput enc_out; - float byte_factor = 2.0f; -}; - -/** - * @brief Adapter class which lets an AudioWriter behave like a Print - * @author Phil Schatzmann - * @copyright GPLv3 - * - */ - -class AudioWriterToAudioOutput : public AudioOutputAdapter { - public: - void setWriter(AudioWriter *writer) { p_writer = writer; } - size_t write(const uint8_t *data, size_t len) { - return p_writer->write(data, len); - }; - - protected: - AudioWriter *p_writer = nullptr; -}; - -/** - * @brief ContainerTarget: forwards requests to both the output and the - * encoder/decoder and sets up the output chain for Containers. We also - * manage the proper sequence of the output classes - * @author Phil Schatzmann - * @copyright GPLv3 - */ -class ContainerTarget { - public: - virtual bool begin() = 0; - virtual void end() = 0; - virtual void setAudioInfo(AudioInfo info) { - if (this->info != info && info.channels != 0 && info.sample_rate != 0) { - this->info = info; - if (p_writer1 != nullptr) p_writer1->setAudioInfo(info); - if (p_writer2 != nullptr) p_writer2->setAudioInfo(info); - } - } - virtual size_t write(uint8_t *data, size_t size) = 0; - - protected: - AudioInfo info; - AudioWriter *p_writer1 = nullptr; - AudioWriter *p_writer2 = nullptr; - AudioWriterToAudioOutput print2; - bool active = false; -}; - -class ContainerTargetPrint : public ContainerTarget { - public: - void setupOutput(AudioWriter *writer1, AudioWriter *writer2, Print &print) { - p_print = &print; - p_writer1 = writer1; - p_writer2 = writer2; - print2.setWriter(p_writer2); - } - - void setupOutput(AudioWriter *writer1, Print &print) { - p_print = &print; - p_writer1 = writer1; - } - - virtual bool begin() { - if (!active) { - active = true; - if (p_writer2 != nullptr) { - p_writer1->setOutput(print2); - p_writer2->setOutput(*p_print); - p_writer1->begin(); - p_writer2->begin(); - } else { - p_writer1->setOutput(*p_print); - p_writer1->begin(); - } - } - return true; - } - virtual void end() { - if (active) { - if (p_writer1 != nullptr) p_writer1->end(); - if (p_writer2 != nullptr) p_writer2->end(); - } - active = false; - } - virtual size_t write(uint8_t *data, size_t size) { - TRACED(); - return p_writer1->write(data, size); - } - - protected: - Print *p_print = nullptr; - AudioWriterToAudioOutput print2; -}; - -} // namespace audio_tools diff --git a/src/AudioTools/AudioCodecs/CodecAACFAAD.h b/src/AudioTools/AudioCodecs/CodecAACFAAD.h deleted file mode 100644 index c83f348d62..0000000000 --- a/src/AudioTools/AudioCodecs/CodecAACFAAD.h +++ /dev/null @@ -1,191 +0,0 @@ -#pragma once - -// #include "Stream.h" -#include "AudioTools/AudioCodecs/AudioCodecsBase.h" -#include "faad.h" - -#ifndef FAAD_INPUT_BUFFER_SIZE -#define FAAD_INPUT_BUFFER_SIZE 1024*2 -#endif - -// to prevent Decoding error: Maximum number of bitstream elements exceeded -#ifndef FAAD_UNDERFLOW_LIMIT -#define FAAD_UNDERFLOW_LIMIT 500 -#endif - - -namespace audio_tools { - -/** - * @brief AAC Decoder using faad: https://github.com/pschatzmann/arduino-libfaad - * This needs a stack of around 60000 and you need to make sure that memory is allocated on PSRAM. - * See https://www.pschatzmann.ch/home/2023/09/12/arduino-audio-tools-faat-aac-decoder/ - * @ingroup codecs - * @ingroup decoder - * @author Phil Schatzmann - * @copyright GPLv3 - */ -class AACDecoderFAAD : public AudioDecoder { - public: - AACDecoderFAAD() { - info.channels = 2; - info.sample_rate = 44100; - info.bits_per_sample = 16; - }; - - ~AACDecoderFAAD() { end(); } - - /// Starts the processing - bool begin() { - TRACED(); - - unsigned long cap = NeAACDecGetCapabilities(); - // Check if decoder has the needed capabilities - - if (!cap & FIXED_POINT_CAP) { - LOGE("Fixed Point"); - return false; - } - - // Open the library - hAac = NeAACDecOpen(); - - // // Get the current config - conf = NeAACDecGetCurrentConfiguration(hAac); - - // // If needed change some of the values in conf - conf->outputFormat = FAAD_FMT_16BIT; - //conf->defObjectType = LC; - conf->defSampleRate = info.sample_rate; - conf->downMatrix = true; // 5.1 channel downmatrixed to 2 channel - conf->useOldADTSFormat = false; - conf->dontUpSampleImplicitSBR = false; - - // Set the new configuration - if (!NeAACDecSetConfiguration(hAac, conf)) { - LOGE("NeAACDecSetConfiguration"); - return false; - } - - // setup input buffer - if (input_buffer.size() != buffer_size_input){ - input_buffer.resize(buffer_size_input); - } - is_init = false; - return true; - } - - /// Releases the reserved memory - virtual void end() { - TRACED(); - flush(); - if (hAac != nullptr) { - NeAACDecClose(hAac); - hAac = nullptr; - } - } - - /// Write AAC data to decoder - size_t write(const uint8_t *data, size_t len) { - // Write supplied data to input buffer - size_t result = input_buffer.writeArray((uint8_t *)data, len); - // Decode from input buffer - decode(underflow_limit); - - return result; - } - - void flush() { - decode(0); - } - - /// Defines the input buffer size - void setInputBufferSize(int len){ - buffer_size_input = len; - } - - /// Defines the min number of bytes that are submitted to the decoder - void setUnderflowLimit(int len){ - underflow_limit = len; - } - - /// checks if the class is active - virtual operator bool() { return hAac != nullptr; } - - protected: - int buffer_size_input = FAAD_INPUT_BUFFER_SIZE; - int underflow_limit = FAAD_UNDERFLOW_LIMIT; - NeAACDecHandle hAac = nullptr; - NeAACDecConfigurationPtr conf; - SingleBuffer input_buffer{0}; - bool is_init = false; - - void init(uint8_t *data, size_t len) { - TRACEI(); - // Initialise the library using one of the initialization functions - unsigned long samplerate = info.sample_rate; - unsigned char channels = info.channels; - - if (NeAACDecInit(hAac, data, len, &samplerate, &channels)==-1) { - LOGE("NeAACDecInit"); - } - info.sample_rate = samplerate; - info.channels = channels; - is_init = true; - } - - void decode(int minBufferSize) { - TRACED(); - NeAACDecFrameInfo hInfo; - - // decode until we do not conume any bytes - while (input_buffer.available()>minBufferSize) { - int eff_len = input_buffer.available(); - - if (!is_init) { - init(input_buffer.data(), eff_len); - } - - uint8_t *sample_buffer=(uint8_t *)NeAACDecDecode(hAac, &hInfo, input_buffer.address(), eff_len); - - LOGD("bytesconsumed: %d of %d", (int)hInfo.bytesconsumed, (int)eff_len); - if (hInfo.error != 0) { - LOGW("Decoding error: %s", NeAACDecGetErrorMessage(hInfo.error)); - } - - if (hInfo.bytesconsumed == 0 ) { - break; - } - - LOGD("Decoded %lu samples", hInfo.samples); - LOGD(" bytesconsumed: %lu", hInfo.bytesconsumed); - LOGD(" channels: %d", hInfo.channels); - LOGD(" samplerate: %lu", hInfo.samplerate); - LOGD(" sbr: %u", hInfo.sbr); - LOGD(" object_type: %u", hInfo.object_type); - LOGD(" header_type: %u", hInfo.header_type); - LOGD(" num_front_channels: %u", hInfo.num_front_channels); - LOGD(" num_side_channels: %u", hInfo.num_side_channels); - LOGD(" num_back_channels: %u", hInfo.num_back_channels); - LOGD(" num_lfe_channels: %u", hInfo.num_lfe_channels); - LOGD(" ps: %u", hInfo.ps); - - // removed consumed data - input_buffer.clearArray(hInfo.bytesconsumed); - - // check for changes in config - AudioInfo tmp{(sample_rate_t)hInfo.samplerate, hInfo.channels, 16}; - if (tmp != info) { - setAudioInfo(tmp); - } - - int bytes = hInfo.samples * sizeof(int16_t); - size_t len = p_print->write(sample_buffer, bytes); - if (len != bytes) { - TRACEE(); - } - } - } -}; - -} // namespace audio_tools diff --git a/src/AudioTools/AudioCodecs/CodecADPCM.h b/src/AudioTools/AudioCodecs/CodecADPCM.h deleted file mode 100644 index 4823c7530d..0000000000 --- a/src/AudioTools/AudioCodecs/CodecADPCM.h +++ /dev/null @@ -1,314 +0,0 @@ -#pragma once -#include "ADPCM.h" // https://github.com/pschatzmann/adpcm -#include "AudioTools/AudioCodecs/AudioCodecsBase.h" - -namespace audio_tools { - -/** - * @brief Decoder for ADPCM. Depends on https://github.com/pschatzmann/adpcm - * @ingroup codecs - * @ingroup decoder - * @author Phil Schatzmann - * @copyright GPLv3 - */ -class ADPCMDecoder : public AudioDecoderExt { - public: - ADPCMDecoder() = default; - - ADPCMDecoder(AVCodecID id, int blockSize = ADAPCM_DEFAULT_BLOCK_SIZE) { - setBlockSize(blockSize); - setId(id); - } - - /// Destructor - ~ADPCMDecoder() { - if (p_decoder) delete p_decoder; - } - - // (re) defines the codec id: set the block size first - void setId(AVCodecID id) { - codec_id = id; - if (p_decoder != nullptr) { - setImplementation(); - } - } - - // defines the block size (= size of encoded frame) - void setBlockSize(int blockSize) override { - block_size = blockSize; - if (p_decoder == nullptr) return; - p_decoder->setBlockSize(blockSize); - } - - /// Provides the block size (= size of encoded frame) (only available after - /// calling begin) - int blockSize() { - if (p_decoder == nullptr) return block_size; - return p_decoder->blockSize(); - } - - /// Provides the frame size (size of decoded frame) (only available after - /// calling begin) - int frameSize() { - if (p_decoder == nullptr) return 0; - return p_decoder->frameSize() * 2; - } - - bool begin() override { - TRACEI(); - if (p_decoder == nullptr) { - setImplementation(); - } - if (is_started) return true; - current_byte = 0; - LOGI("sample_rate: %d, channels: %d", info.sample_rate, info.channels); - p_decoder->begin(info.sample_rate, info.channels); - LOGI("frameSize: %d", (int)frameSize()); - LOGI("blockSize: %d", (int)blockSize()); - block_size = p_decoder->blockSize(); - assert(block_size > 0); - assert(p_decoder->frameSize() > 0); - adpcm_block.resize(block_size); - - notifyAudioChange(info); - is_started = true; - return true; - } - - void end() override { - TRACEI(); - if (p_decoder != nullptr) p_decoder->end(); - adpcm_block.resize(0); - is_started = false; - } - - virtual void setOutput(Print &out_stream) override { p_print = &out_stream; } - - virtual size_t write(const uint8_t *data, size_t len) override { - TRACED(); - - uint8_t *input_buffer8 = (uint8_t *)data; - LOGD("write: %d", (int)len); - for (int j = 0; j < len; j++) { - decode(input_buffer8[j]); - } - return len; - } - - void flush() { - if (p_decoder != nullptr) p_decoder->flush(); - } - - operator bool() override { return is_started; } - - protected: - adpcm_ffmpeg::ADPCMDecoder *p_decoder = nullptr; - Vector adpcm_block; - Print *p_print = nullptr; - int current_byte = 0; - int block_size = ADAPCM_DEFAULT_BLOCK_SIZE; - AVCodecID codec_id = AV_CODEC_ID_ADPCM_MS; - bool is_started = false; - - virtual bool decode(uint8_t byte) { - if (p_decoder == nullptr) return false; - adpcm_block[current_byte++] = byte; - - if (current_byte >= block_size) { - TRACED(); - adpcm_ffmpeg::AVFrame &frame = - p_decoder->decode(&adpcm_block[0], block_size); - // print the result - int16_t *data = (int16_t *)frame.data[0]; - size_t byte_count = frame.nb_samples * sizeof(int16_t) * info.channels; - size_t written = p_print->write((uint8_t *)data, byte_count); - if (written != byte_count) { - LOGE("decode %d -> %d -> %d", block_size, (int)byte_count, - (int)written); - } else { - LOGD("decode %d -> %d -> %d", block_size, (int)byte_count, - (int)written); - } - - // restart from array begin - current_byte = 0; - } - return true; - } - - /// change the decoder implementation - void setImplementation() { - // delete the old decoder - if (p_decoder != nullptr) { - p_decoder->end(); - delete p_decoder; - p_decoder = nullptr; - } - - if (codec_id == AV_CODEC_ID_ADPCM_IMA_AMV) { - info.sample_rate = 22050; - info.channels = 1; - info.bits_per_sample = 16; - } - p_decoder = adpcm_ffmpeg::ADPCMDecoderFactory::create(codec_id); - if (p_decoder != nullptr) { - p_decoder->setCodecID(codec_id); - p_decoder->setBlockSize(block_size); - } else { - LOGE("Decoder not implemented"); - } - } -}; - -/** - * @brief Encoder for ADPCM - Depends on https://github.com/pschatzmann/adpcm - * @ingroup codecs - * @ingroup p_encoder-> - * @author Phil Schatzmann - * @copyright GPLv3 - */ -class ADPCMEncoder : public AudioEncoderExt { - public: - ADPCMEncoder() = default; - - ADPCMEncoder(AVCodecID id, int blockSize = ADAPCM_DEFAULT_BLOCK_SIZE) { - setId(id); - setBlockSize(blockSize); - } - - /// Destructor - ~ADPCMEncoder() { - if (p_encoder != nullptr) delete p_encoder; - } - - /// (re) defines the codec id - void setId(AVCodecID id) { - codec_id = id; - if (p_encoder != nullptr) { - setImplementation(); - } - } - - /// (re) defines the block size - void setBlockSize(int blockSize) { - block_size = blockSize; - if (p_encoder == nullptr) return; - p_encoder->setBlockSize(blockSize); - } - - /// Provides the block size (size of encoded frame) (only available after - /// calling begin) - int blockSize() override { - if (p_encoder == nullptr) return 0; - return p_encoder->blockSize(); - } - - /// Provides the frame size (size of decoded frame) (only available after - /// calling begin) - int frameSize() { - if (p_encoder == nullptr) return 0; - return p_encoder->frameSize() * 2; - } - - bool begin() override { - TRACEI(); - if (p_encoder == nullptr) { - setImplementation(); - }; - if (is_started) return true; - LOGI("sample_rate: %d, channels: %d", info.sample_rate, info.channels); - p_encoder->begin(info.sample_rate, info.channels); - LOGI("frameSize: %d", (int)frameSize()); - LOGI("blockSize: %d", (int)blockSize()); - assert(info.sample_rate != 0); - assert(p_encoder->frameSize() != 0); - total_samples = p_encoder->frameSize() * info.channels; - pcm_block.resize(total_samples); - current_sample = 0; - - is_started = true; - return true; - } - - void end() override { - TRACEI(); - pcm_block.resize(0); - if (p_encoder == nullptr) return; - p_encoder->end(); - is_started = false; - } - - const char *mime() override { return "audio/adpcm"; } - - void setOutput(Print &out_stream) override { p_print = &out_stream; } - - operator bool() override { return is_started; } - - size_t write(const uint8_t *data, size_t len) override { - LOGD("write: %d", (int)len); - int16_t *data16 = (int16_t *)data; - for (int j = 0; j < len / 2; j++) { - encode(data16[j]); - } - return len; - } - - protected: - AVCodecID codec_id = AV_CODEC_ID_ADPCM_MS; - adpcm_ffmpeg::ADPCMEncoder *p_encoder = nullptr; - Vector pcm_block; - Print *p_print = nullptr; - bool is_started = false; - int current_sample = 0; - int total_samples = 0; - int current_id = -1; - int block_size = ADAPCM_DEFAULT_BLOCK_SIZE; - - virtual bool encode(int16_t sample) { - if (p_encoder == nullptr) return false; - pcm_block[current_sample++] = sample; - if (current_sample >= total_samples) { - TRACED(); - adpcm_ffmpeg::AVPacket &packet = - p_encoder->encode(&pcm_block[0], total_samples); - if (packet.size > 0) { - size_t written = p_print->write(packet.data, packet.size); - if (written != packet.size) { - LOGE("encode %d->%d->%d", 2 * total_samples, (int)packet.size, - (int)written); - } else { - LOGD("encode %d->%d->%d", 2 * total_samples, (int)packet.size, - (int)written); - } - } - // restart from array begin - current_sample = 0; - } - return true; - } - - /// change the encoder implementation - bool setImplementation() { - // delete the old encoder - if (p_encoder != nullptr) { - p_encoder->end(); - delete p_encoder; - p_encoder = nullptr; - } - - if (codec_id == AV_CODEC_ID_ADPCM_IMA_AMV) { - info.sample_rate = 22050; - info.channels = 1; - info.bits_per_sample = 16; - } - p_encoder = adpcm_ffmpeg::ADPCMEncoderFactory::create(codec_id); - if (p_encoder != nullptr) { - p_encoder->setCodecID(codec_id); - p_encoder->setBlockSize(block_size); - } else { - LOGE("Encoder not implemented"); - } - } -}; - -} // namespace audio_tools \ No newline at end of file diff --git a/src/AudioTools/AudioCodecs/CodecADTS.h b/src/AudioTools/AudioCodecs/CodecADTS.h deleted file mode 100644 index 8cbd1720bf..0000000000 --- a/src/AudioTools/AudioCodecs/CodecADTS.h +++ /dev/null @@ -1,341 +0,0 @@ -#pragma once -#include "AudioTools/AudioCodecs/AudioCodecsBase.h" - -namespace audio_tools { - -#ifndef SYNCWORDH -#define SYNCWORDH 0xff -#define SYNCWORDL 0xf0 -#endif - -#define ERROR_FMT_CHANGE "- Invalid ADTS change: %s" -#define ERROR_FMT "- Invalid ADTS: %s (0x%x)" - -/** - * @brief Structure to hold ADTS header field values - */ - -class ADTSParser { - public: - struct ADTSHeader { - uint16_t syncword = 0; - uint8_t id = 0; - uint8_t layer = 0; - uint8_t protection_absent = 0; - uint8_t profile = 0; - uint8_t sampling_freq_idx = 0; - uint8_t private_bit = 0; - uint8_t channel_cfg = 0; - uint8_t original_copy = 0; - uint8_t home = 0; - uint8_t copyright_id_bit = 0; - uint8_t copyright_id_start = 0; - uint16_t frame_length = 0; - uint8_t adts_buf_fullness = 0; - uint8_t num_rawdata_blocks = 0; - }; - - bool begin() { - is_first = true; - is_valid = true; - return true; - } - - bool parse(uint8_t *hdr) { - header.syncword = (hdr[0] << 4) | (hdr[1] >> 4); - // parse fixed header - header.id = (hdr[1] >> 3) & 0b1; - header.layer = (hdr[1] >> 1) & 0b11; - header.protection_absent = (hdr[1]) & 0b1; - header.profile = (hdr[2] >> 6) & 0b11; - header.sampling_freq_idx = (hdr[2] >> 2) & 0b1111; - header.private_bit = (hdr[2] >> 1) & 0b1; - header.channel_cfg = ((hdr[2] & 0x01) << 2) | ((hdr[3] & 0xC0) >> 6); - header.original_copy = (hdr[3] >> 5) & 0b1; - header.home = (hdr[3] >> 4) & 0b1; - // parse variable header - header.copyright_id_bit = (hdr[3] >> 3) & 0b1; - header.copyright_id_start = (hdr[3] >> 2) & 0b1; - header.frame_length = ((((unsigned int)hdr[3] & 0x3)) << 11) | - (((unsigned int)hdr[4]) << 3) | (hdr[5] >> 5); - header.adts_buf_fullness = ((hdr[5] & 0b11111) << 6) | (hdr[6] >> 2); - header.num_rawdata_blocks = (hdr[6]) & 0b11; - - LOGD("id:%d layer:%d profile:%d freq:%d channel:%d frame_length:%d", - header.id, header.layer, header.profile, getSampleRate(), - header.channel_cfg, header.frame_length); - - // check - is_valid = check(); - return is_valid; - } - - uint32_t getFrameLength() { return header.frame_length; }; - - void log() { - LOGI("%s id:%d layer:%d profile:%d freq:%d channel:%d frame_length:%d", - is_valid ? "+" : "-", header.id, header.layer, header.profile, - getSampleRate(), header.channel_cfg, header.frame_length); - } - - int getSampleRate() { - return header.sampling_freq_idx > 12 - ? header.sampling_freq_idx - : (int)adtsSamplingRates[header.sampling_freq_idx]; - } - - bool isSyncWord(const uint8_t *buf) { - return ((buf[0] & SYNCWORDH) == SYNCWORDH && - (buf[1] & SYNCWORDL) == SYNCWORDL); - } - - int findSyncWord(const uint8_t *buf, int nBytes, int start = 0) { - /* find byte-aligned syncword (12 bits = 0xFFF) */ - for (int i = start; i < nBytes - 1; i++) { - if (isSyncWord(buf + i)) return i; - } - return -1; - } - - ADTSHeader &data() { return header; } - - protected: - const int adtsSamplingRates[13] = {96000, 88200, 64000, 48000, 44100, - 32000, 24000, 22050, 16000, 12000, - 11025, 8000, 7350}; - - ADTSHeader header; - ADTSHeader header_ref; - bool is_first = true; - bool is_valid = false; - - bool check() { - if (header.syncword != 0b111111111111) { - LOGW(ERROR_FMT, "sync", (int)header.syncword); - is_valid = false; - } - if (header.id > 6) { - LOGW(ERROR_FMT, "id", (int)header.id); - is_valid = false; - } - if (header.sampling_freq_idx > 0xb) { - LOGW(ERROR_FMT, "freq", (int)header.sampling_freq_idx); - is_valid = false; - } - // valid value 0-7 - // if (header.channel_cfg == 0 || header.channel_cfg > 7) { - if (header.channel_cfg > 7) { - LOGW(ERROR_FMT, "channels", (int)header.channel_cfg); - is_valid = false; - } - if (header.frame_length > 8191) { // tymically <= 768 - LOGW(ERROR_FMT, "frame_length", (int)header.frame_length); - is_valid = false; - } - // on subsequent checks we need to compare with the first header - if (!is_first) { - is_valid = checkRef(); - } - if (is_valid) { - is_first = false; - header_ref = header; - } - return is_valid; - } - - bool checkRef() { - char msg[200] = ""; - bool is_valid = true; - if (header.id != header_ref.id) { - strcat(msg, "id "); - is_valid = false; - } - if (header.layer != header_ref.layer) { - strcat(msg, "layer "); - is_valid = false; - } - if (header.profile != header_ref.profile) { - strcat(msg, "profile "); - is_valid = false; - } - if (header.sampling_freq_idx != header_ref.sampling_freq_idx) { - strcat(msg, "freq "); - is_valid = false; - } - if (header.channel_cfg != header_ref.channel_cfg) { - strcat(msg, "channel "); - is_valid = false; - } - if (header.adts_buf_fullness != header_ref.adts_buf_fullness) { - strcat(msg, "fullness"); - is_valid = false; - } - if (!is_valid) { - LOGW(ERROR_FMT_CHANGE, msg); - } - return is_valid; - } -}; - -/** - * @brief Audio Data Transport Stream (ADTS) is a format similar to Audio Data - * Interchange Format (ADIF), used by MPEG TS or Shoutcast to stream audio - * defined in MPEG-2 Part 7, usually AAC. This parser extracts all valid ADTS - * frames from the data stream ignoring other data. - * - * @ingroup codecs - * @ingroup decoder - * @author Phil Schatzmann - * @copyright GPLv3 - */ -class ADTSDecoder : public AudioDecoder { - public: - ADTSDecoder() = default; - ADTSDecoder(AudioDecoder &dec) { p_dec = &dec; }; - - bool begin() override { - parser.begin(); - if (p_dec) p_dec->begin(); - return true; - } - - void end() override { - parseBuffer(); - writeData(out_buffer.data(), out_buffer.available()); - out_buffer.reset(); - buffer.resize(0); - if (p_dec) p_dec->end(); - } - - /// Write AAC data to decoder - size_t write(const uint8_t *data, size_t len) override { - LOGI("AACDecoderADTS::write: %d", (int)len); - - parseBuffer(); - - // write data to buffer - size_t result = buffer.writeArray(data, len); - // assert(result == len); - LOGD("buffer size: %d", buffer.available()); - - return result; - } - - /// checks if the class is active - operator bool() override { return true; } - - /// By default we write the parsed frames directly to the output: - /// alternatively you can activate a buffer here - void setOutputBufferSize(int size) { out_buffer.resize(size); } - - /// Defines the parse buffer size: default is 1024 - void setParseBufferSize(int size) { buffer.resize(size); } - - /// Defines where the decoded result is written to - void setOutput(AudioStream &out_stream) override { - if (p_dec) { - p_dec->setOutput(out_stream); - } else { - AudioDecoder::setOutput(out_stream); - } - } - - /// Defines where the decoded result is written to - void setOutput(AudioOutput &out_stream) override { - if (p_dec) { - p_dec->setOutput(out_stream); - } else { - AudioDecoder::setOutput(out_stream); - } - } - - /// Defines where the decoded result is written to - void setOutput(Print &out_stream) override { - if (p_dec) { - p_dec->setOutput(out_stream); - } else { - AudioDecoder::setOutput(out_stream); - } - } - - protected: - SingleBuffer buffer{DEFAULT_BUFFER_SIZE}; - SingleBuffer out_buffer; - ADTSParser parser; - AudioDecoder *p_dec = nullptr; - - void parseBuffer() { - TRACED(); - - // Need at least 7 bytes for a valid ADTS header - while (true) { - if (buffer.available() <= 5) return; - // Needs to contain sync word - int syncPos = parser.findSyncWord(buffer.data(), buffer.available()); - if (syncPos < 0) { - return; - } - // buffer needs to start with sync word - if (syncPos > 0) { - buffer.clearArray(syncPos); - LOGI("Cleared %d bytes", syncPos); - } - // assert(parser.findSyncWord(buffer.data(), buffer.available()) == 0); - // Try to parse the header - if (parser.parse(buffer.data())) { - // Get the frame length which includes the header - uint16_t frameLength = parser.getFrameLength(); - if (frameLength > buffer.available()) { - // not enough data - return; - } - // write data to decoder - if (out_buffer.size() > 0) { - writeDataBuffered(buffer.data(), frameLength); - } else { - writeData(buffer.data(), frameLength); - } - buffer.clearArray(frameLength); - } else { - LOGI("Invalid ADTS header"); - // ignore data and move to next synch word - int pos = parser.findSyncWord(buffer.data(), buffer.available(), 5); - if (pos < 0) { - // no more sync word found - buffer.reset(); - } else { - buffer.clearArray(pos); - } - } - } - } - - size_t writeDataBuffered(uint8_t *data, size_t size) { - LOGI("writeDataBuffered: %d", (int)size); - for (int j = 0; j < size; j++) { - out_buffer.write(data[j]); - if (out_buffer.isFull()) { - writeData(out_buffer.data(), out_buffer.available()); - out_buffer.reset(); - } - } - return size; - } - size_t writeData(uint8_t *data, size_t size) { - LOGI("writeData: %d", (int)size); - if (p_print) { - size_t len = ::writeData(p_print, data, size); - assert(len == size); - return (len == size); - } - if (p_dec) { - LOGI("write to decoder: %d", (int)size); - size_t len = writeDataT(p_dec, data, size); - assert(len == size); - return (len == size); - } - return 0; - } -}; - -} // namespace audio_tools diff --git a/src/AudioTools/AudioCodecs/CodecALAC.h b/src/AudioTools/AudioCodecs/CodecALAC.h deleted file mode 100644 index 9847459d99..0000000000 --- a/src/AudioTools/AudioCodecs/CodecALAC.h +++ /dev/null @@ -1,383 +0,0 @@ - -#pragma once - -#include "ALAC.h" // https://github.com/pschatzmann/codec-alac -#include "AudioTools/AudioCodecs/AudioCodecsBase.h" - -namespace audio_tools { - -/// Magic Cookie -class ALACBinaryConfig { - public: - void setChannels(int inNumChannels) { - int size = (inNumChannels > 2) - ? sizeof(ALACSpecificConfig) + kChannelAtomSize + - sizeof(ALACAudioChannelLayout) - : sizeof(ALACSpecificConfig); - vector.resize(size); - } - - uint32_t size() { return vector.size(); } - uint8_t* data() { return vector.data(); } - - protected: - Vector vector; -}; - -/** - * @brief ALAC (Apple Lossless Audio Codec) decoder. This class depends on - * https://github.com/pschatzmann/codec-alac. This implementaion is based on - * https://github.com/macosforge/alac - * @note Please note that this codec usually needs a container: - * The write() method expects a complete frame to be written! - * The decoder also expects to get the config from the encoder, however we have - * some fallback functionality that uses the AudioInfo and the frame size - * defined in the constructor. - * @ingroup codecs - * @author Phil Schatzmann - */ -class DecoderALAC : public AudioDecoder { - public: - /// Default constructor: you can define your own optimized frame size - DecoderALAC(int frameSize = kALACDefaultFrameSize) { - // this is used when setCodecConfig() is not called with encoder info - setFrameSize(frameSize); - //setDefaultConfig(); - } - - // define ALACSpecificConfig - bool setCodecConfig(ALACSpecificConfig config) { - return setCodecConfig((uint8_t*)&config, sizeof(config)); - } - - /// write Magic Cookie (ALACSpecificConfig) - bool setCodecConfig(ALACBinaryConfig cfg) { - size_t result = setCodecConfig(cfg.data(), cfg.size()); - is_init = true; - return result; - } - - /// write Magic Cookie (ALACSpecificConfig) - bool setCodecConfig(const uint8_t* data, size_t len) override { - LOGI("DecoderALAC::setCodecConfig: %d", (int)len); - // Call Init() to set up the decoder - int32_t rc = dec.Init((void*)data, len); - if (rc != 0) { - LOGE("Init failed"); - return false; - } - LOGI("ALAC Decoder Setup - SR: %d, Channels: %d, Bits: %d, Frame Size: %d", - (int)dec.mConfig.sampleRate, (int)dec.mConfig.numChannels, - (int)dec.mConfig.bitDepth, (int)dec.mConfig.frameLength); - AudioInfo tmp; - tmp.bits_per_sample = dec.mConfig.bitDepth; - tmp.channels = dec.mConfig.numChannels; - tmp.sample_rate = dec.mConfig.sampleRate; - setAudioInfo(tmp); - is_init = true; - return true; - } - - /// Update the global decoder info - void setAudioInfo(AudioInfo from) override { - AudioDecoder::setAudioInfo(from); - dec.mConfig.sampleRate = from.sample_rate; - dec.mConfig.numChannels = from.channels; - dec.mConfig.bitDepth = from.bits_per_sample; - } - - - /// we expect the write is called for a complete frame! - size_t write(const uint8_t* encodedFrame, size_t encodedLen) override { - LOGD("DecoderALAC::write: %d", (int)encodedLen); - // Make sure we have a config: we can't do this in begin because the setConfig() - // might be called after begin() - if (!is_init) setDefaultConfig(); - - // Make sure we have the output buffer set up - if (result_buffer.size() != outputBufferSize()) { - result_buffer.resize(outputBufferSize()); - } - - // Init bit buffer - BitBufferInit(&bits, (uint8_t*)encodedFrame, encodedLen); - - // Decode - uint32_t outNumSamples = 0; - int32_t status = - dec.Decode(&bits, result_buffer.data(), dec.mConfig.frameLength, - dec.mConfig.numChannels, &outNumSamples); - - if (status != 0) { - LOGE("Decode failed with error: %d", status); - return 0; - } - - // Process result - size_t outputSize = - outNumSamples * dec.mConfig.numChannels * dec.mConfig.bitDepth / 8; - LOGI("DecoderALAC::write-pcm: %d", (int)outputSize); - - // Output the result in chunks of 1k - int open = outputSize; - int processed = 0; - while (open > 0) { - int writeSize = MIN(1024, open); - size_t written = - p_print->write(result_buffer.data() + processed, writeSize); - if (writeSize != written) { - LOGE("write error: %d -> %d", (int)outputSize, (int)written); - } - open -= written; - processed += written; - } - return encodedLen; - } - - operator bool() { return true; } - - /// Set the default frame size: this will be overwritten if you call - /// setCodecConfig() - void setFrameSize(int frames) { dec.mConfig.frameLength = frames; } - - /// Provides the actual frame size - int frameSize() { return dec.mConfig.frameLength; } - - protected: - ALACDecoder dec; - Vector result_buffer; - bool is_init = false; - struct BitBuffer bits; - - void setDefaultConfig() { - // LOGW("Setting up default ALAC config") - AudioInfo info = audioInfo(); - ALACSpecificConfig tmp; - // Essential parameters for ALAC compression - tmp.frameLength = frameSize(); - tmp.compatibleVersion = 0; - tmp.bitDepth = info.bits_per_sample; - tmp.pb = 40; // Rice parameter limit - tmp.mb = 10; // Maximum prefix length for Rice coding - tmp.kb = 14; // History multiplier - tmp.numChannels = info.channels; - tmp.maxRun = 255; // Maximum run length supported - tmp.avgBitRate = 0; - - tmp.sampleRate = info.sample_rate; - - // Calculate max frame bytes - must account for: - // 1. Uncompressed frame size - // 2. ALAC frame headers - // 3. Potential compression inefficiency - uint32_t bytesPerSample = info.bits_per_sample / 8; - uint32_t uncompressedFrameSize = - frameSize() * info.channels * bytesPerSample; - - // Add safety margins: - // - ALAC header (~50 bytes) - // - Worst case compression overhead (50%) - // - Alignment padding (64 bytes) - tmp.maxFrameBytes = - uncompressedFrameSize + (uncompressedFrameSize / 2) + 64 + 50; - - convertToNetworkFormat(tmp); - setCodecConfig(tmp); - } - - /// Calculate the output buffer size based on the current configuration - int outputBufferSize() { - return dec.mConfig.frameLength * dec.mConfig.numChannels * - dec.mConfig.bitDepth / 8; - } - - /// Convert to big endian so that we can use it in Init() - void convertToNetworkFormat(ALACSpecificConfig& config) { - config.frameLength = Swap32NtoB(config.frameLength); - config.maxRun = Swap16NtoB((uint16_t)config.maxRun); - config.maxFrameBytes = Swap32NtoB(config.maxFrameBytes); - config.avgBitRate = Swap32NtoB(config.avgBitRate); - config.sampleRate = Swap32NtoB(config.sampleRate); - } -}; - -/** - * @brief ALAC (Apple Lossless Audio Codec) encoder. This class is responsible - * for encoding audio data into ALAC format. - * The implementaion is based on https://github.com/macosforge/alac - * @ingroup codecs - * @author Phil Schatzmann - */ -class EncoderALAC : public AudioEncoder { - public: - /// Default constructor: you can define your own optimized frame size - EncoderALAC(int frameSize = kALACDefaultFrameSize) { - setFrameSize(frameSize); - } - void setOutput(Print& out_stream) override { p_print = &out_stream; }; - - bool begin() override { - if (p_print == nullptr) { - LOGE("No output stream set"); - return false; - } - // define input format - input_format = getInputFormat(); - out_format = getOutputFormat(); - - // Setup Encoder - enc.SetFrameSize(frame_size); - int rc = enc.InitializeEncoder(out_format); - - // Calculate exact buffer sizes based on frame settings - uint32_t bytesPerSample = info.bits_per_sample / 8; - uint32_t inputBufferSize = frame_size * info.channels * bytesPerSample; - // Calculate output buffer size - uint32_t outputBufferSize = inputBufferSize * 2; // Ensure enough space - - LOGI( - "ALAC Encoder: frame_size=%d, inputBuf=%d, outputBuf=%d, channels=%d, " - "bits=%d", - frame_size, inputBufferSize, outputBufferSize, info.channels, - info.bits_per_sample); - - in_buffer.resize(inputBufferSize); - out_buffer.resize(outputBufferSize); - is_started = rc == 0; - return is_started; - } - - void end() override { - enc.Finish(); - is_started = false; - } - - /// Encode the audio samples into ALAC format - size_t write(const uint8_t* data, size_t len) override { - if (!is_started) return 0; - LOGD("EncoderALAC::write: %d", (int)len); - for (int j = 0; j < len; j++) { - in_buffer.write(data[j]); - if (in_buffer.isFull()) { - // provide available encoded data length - int32_t ioNumBytes = in_buffer.size(); - int rc = enc.Encode(input_format, out_format, (uint8_t*)in_buffer.data(), - out_buffer.data(), &ioNumBytes); - // Output encoded data - size_t written = p_print->write(out_buffer.data(), ioNumBytes); - if (ioNumBytes != written) { - LOGE("write error: %d -> %d", (int)ioNumBytes, (int)written); - } - in_buffer.reset(); - } - } - return len; - } - - /// Provide the configuration of the encoder - ALACSpecificConfig config() { - enc.GetConfig(cfg); - return cfg; - } - - /// Provide the magic coookie for the decoder - ALACBinaryConfig& binaryConfig() { - bin.setChannels(info.channels); - uint32_t size = bin.size(); - enc.GetMagicCookie(bin.data(), &size); - return bin; - } - - /// Check if the encoder is ready to encode - operator bool() { return is_started && p_print != nullptr; } - - /// Mime type: returns audio/alac - const char* mime() override { return "audio/alac"; } - - /// Defines if the encoder should use fast mode - void setFastMode(bool fast) { - enc.SetFastMode(fast); - } - - /// Defines the frame size for the decoder: default is 4096 frames - void setFrameSize(int frames) { - if (is_started) { - LOGE("Can't change frame size on started encoder") - return; - } - frame_size = frames; - } - - /// Determins the actually defined number of frames - int frameSize() { return frame_size; } - - protected: - int frame_size = kALACDefaultFrameSize; - ALACEncoder enc; - SingleBuffer in_buffer; - Vector out_buffer; - AudioFormatDescription input_format; - AudioFormatDescription out_format; - ALACSpecificConfig cfg; - ALACBinaryConfig bin; - Print* p_print = nullptr; - bool is_started = false; - - AudioFormatDescription getInputFormat() { - AudioFormatDescription result; - memset(&result, 0, sizeof(AudioFormatDescription)); - result.mSampleRate = info.sample_rate; - result.mFormatID = kALACFormatLinearPCM; - result.mFormatFlags = - kALACFormatFlagIsSignedInteger | - kALACFormatFlagIsPacked; // Native endian, signed integer - result.mBytesPerPacket = info.channels * (info.bits_per_sample / 8); - result.mFramesPerPacket = 1; - result.mBytesPerFrame = info.channels * (info.bits_per_sample / 8); - result.mChannelsPerFrame = info.channels; - result.mBitsPerChannel = info.bits_per_sample; - - return result; - } - - AudioFormatDescription getOutputFormat() { - AudioFormatDescription result; - memset(&result, 0, sizeof(AudioFormatDescription)); - result.mSampleRate = info.sample_rate; - result.mFormatID = kALACCodecFormat; - result.mFormatFlags = getOutputFormatFlags(info.bits_per_sample); // or 0 ? - result.mBytesPerPacket = 0; // Variable for compressed format - result.mFramesPerPacket = frame_size; // Common ALAC frame size - result.mBytesPerFrame = 0; // Variable for compressed format - result.mChannelsPerFrame = info.channels; - result.mBitsPerChannel = info.bits_per_sample; - return result; - } - - // Adapted from CoreAudioTypes.h - enum { - kFormatFlag_16BitSourceData = 1, - kFormatFlag_20BitSourceData = 2, - kFormatFlag_24BitSourceData = 3, - kFormatFlag_32BitSourceData = 4 - }; - - uint32_t getOutputFormatFlags(uint32_t bits) { - switch (bits) { - case 16: - return kFormatFlag_16BitSourceData; - case 20: - return kFormatFlag_20BitSourceData; - case 24: - return kFormatFlag_24BitSourceData; - case 32: - return kFormatFlag_32BitSourceData; - break; - default: - LOGE("Unsupported bit depth: %d", bits); - return 0; - } - } -}; - -} // namespace audio_tools \ No newline at end of file diff --git a/src/AudioTools/AudioCodecs/CodecAMRNB.h b/src/AudioTools/AudioCodecs/CodecAMRNB.h deleted file mode 100644 index c2c9fc7ea9..0000000000 --- a/src/AudioTools/AudioCodecs/CodecAMRNB.h +++ /dev/null @@ -1,174 +0,0 @@ -#pragma once -#include "AMRNB.h" // https://github.com/pschatzmann/codec-amr -#include "AudioTools/AudioCodecs/AudioCodecsBase.h" - -namespace audio_tools { - -/** - * @brief AMR Narrowband Decoder - * See https://github.com/pschatzmann/codec-amr - * @ingroup codecs - * @ingroup decoder - * @author Phil Schatzmann - * @copyright GPLv3 - */ - -class AMRNBDecoder : public AudioDecoder { - public: - /// Default Constructor with valid mode values: - /// NB_475,NB_515,NB_59,NB_67,NB_74,NB_795,NB_102,NB_122 (e.g. - /// AMRNB::Mode::NB_475) - AMRNBDecoder(AMRNB::Mode mode) { - setMode(mode); - info.channels = 1; - info.sample_rate = 8000; - } - - ~AMRNBDecoder() override = default; - - void setMode(AMRNB::Mode mode) { - this->mode = mode; - amr.setMode(mode); - } - - bool begin() { - notifyAudioChange(audioInfo()); - buffer.resize(amr.getEncodedFrameSizeBytes()); - return getOutput() != nullptr; - } - - void setAudioInfo(AudioInfo from) { - if (from.bits_per_sample != 16) { - LOGE("Invalid bits per sample: %d", from.bits_per_sample); - } - if (from.sample_rate != 8000) { - LOGE("Invalid sample rate: %d", from.sample_rate); - } - if (from.channels != 1) { - LOGE("Invalid channels: %d", from.channels); - } - } - - size_t write(const uint8_t *data, size_t len) override { - for (size_t j = 0; j < len; j++) { - buffer.write(data[j]); - if (buffer.isFull()) { - int result_samples = amr.getFrameSizeSamples(); - int16_t result[result_samples]; - int size = - amr.decode(buffer.data(), buffer.size(), result, result_samples); - if (size > 0) { - if (getOutput() != nullptr) { - getOutput()->write((uint8_t *)result, size * sizeof(int16_t)); - } - } - buffer.clear(); - } - } - return len; - } - - /// Provides the block size (size of encoded frame) - int blockSize() { - amr.setMode(mode); - return amr.getEncodedFrameSizeBytes(); - } - - /// Provides the frame size (size of decoded frame) - int frameSize() { return amr.getFrameSizeSamples() * sizeof(int16_t); } - - operator bool() override { return getOutput() != nullptr; } - - protected: - AMRNB amr; - AMRNB::Mode mode; - SingleBuffer buffer{0}; -}; - -/** - * @brief AMR NB Encoder - * See https://github.com/pschatzmann/codec-amr - * @ingroup codecs - * @ingroup decoder - * @author Phil Schatzmann - * @copyright GPLv3 - */ - -class AMRNBEncoder : public AudioEncoder { - public: - /// Default Constructor with valid mode values: - /// NB_475,NB_515,NB_59,NB_67,NB_74,NB_795,NB_102,NB_122 (e.g. - /// AMRNB::Mode::NB_475) AMRNBDecoder(AMRNB::Mode mode) { - AMRNBEncoder(AMRNB::Mode mode) { - setMode(mode); - info.channels = 1; - info.sample_rate = 8000; - } - - ~AMRNBEncoder() override = default; - - void setMode(AMRNB::Mode mode) { - this->mode = mode; - amr.setMode(mode); - } - - bool begin() { - buffer.resize(frameSize()); - return getOutput() != nullptr; - } - - void setAudioInfo(AudioInfo from) { - if (from.bits_per_sample != 16) { - LOGE("Invalid bits per sample: %d", from.bits_per_sample); - } - if (from.sample_rate != 8000) { - LOGE("Invalid sample rate: %d", from.sample_rate); - } - if (from.channels != 1) { - LOGE("Invalid channels: %d", from.channels); - } - } - - size_t write(const uint8_t *data, size_t len) override { - for (size_t j = 0; j < len; j++) { - buffer.write(data[j]); - if (buffer.isFull()) { - int result_bytes = blockSize(); - uint8_t result[result_bytes]; - int size = - amr.encode((int16_t *)buffer.data(), - buffer.size() / sizeof(int16_t), result, result_bytes); - if (size > 0) { - if (getOutput() != nullptr) { - getOutput()->write(result, size); - } - } - buffer.clear(); - } - } - return len; - } - - /// Provides the block size (size of encoded frame) - int blockSize() { - amr.setMode(mode); - return amr.getEncodedFrameSizeBytes(); - } - - /// Provides the frame size (size of decoded frame) - int frameSize() { return amr.getFrameSizeSamples() * sizeof(int16_t); } - - const char *mime() { return "audio/amr"; } - - void setOutput(Print &out_stream) override { p_print = &out_stream; } - - Print *getOutput() { return p_print; } - - protected: - AMRNB amr; - AMRNB::Mode mode; - SingleBuffer buffer{0}; - Print *p_print = nullptr; -}; - -} // namespace audio_tools \ No newline at end of file diff --git a/src/AudioTools/AudioCodecs/CodecAMRWB.h b/src/AudioTools/AudioCodecs/CodecAMRWB.h deleted file mode 100644 index 4ac12244e2..0000000000 --- a/src/AudioTools/AudioCodecs/CodecAMRWB.h +++ /dev/null @@ -1,169 +0,0 @@ -#pragma once -#include "AMRWB.h" // https://github.com/pschatzmann/codec-amr -#include "AudioTools/AudioCodecs/AudioCodecsBase.h" - -namespace audio_tools { - -/** - * @brief AMR Wideband Decoder - * See https://github.com/pschatzmann/codec-amr - * @ingroup codecs - * @ingroup decoder - * @author Phil Schatzmann - * @copyright GPLv3 - */ - -class AMRWBDecoder : public AudioDecoder { - public: - /// Default constructor with valid mode values: WB_6_60,WB_8_85,WB_12_65,WB_14_25, - /// WB_15_85,WB_18_25,WB_19_85,WB_23_05,WB_23_85 (e.g. AMRWB::Mode::WB_6_60) - AMRWBDecoder(AMRWB::Mode mode) { - setMode(mode); - info.channels = 1; - info.sample_rate = 16000; - } - - ~AMRWBDecoder() override = default; - - bool begin() { - notifyAudioChange(audioInfo()); - buffer.resize(amr.getEncodedFrameSizeBytes()); - return getOutput() != nullptr; - } - - void setAudioInfo(AudioInfo from) { - if (from.bits_per_sample != 16) { - LOGE("Invalid bits per sample: %d", from.bits_per_sample); - } - if (from.sample_rate != 8000) { - LOGE("Invalid sample rate: %d", from.sample_rate); - } - if (from.channels != 1) { - LOGE("Invalid channels: %d", from.channels); - } - } - - size_t write(const uint8_t *data, size_t len) override { - for (size_t j = 0; j < len; j++) { - buffer.write(data[j]); - if (buffer.isFull()) { - int result_samples = amr.getFrameSizeSamples(); - int16_t result[result_samples]; - int size = - amr.decode(buffer.data(), buffer.size(), result, result_samples); - if (size > 0) { - if (getOutput() != nullptr) { - getOutput()->write((uint8_t *)result, size * sizeof(int16_t)); - } - } - buffer.clear(); - } - } - return len; - } - - /// Provides the block size (size of encoded frame) - int blickSize() { return amr.getEncodedFrameSizeBytes(); } - - /// Provides the frame size (size of decoded frame) - int frameSize() { return amr.getFrameSizeSamples() * sizeof(int16_t); } - - void setMode(AMRWB::Mode mode) { - this->mode = mode; - amr.setMode(mode); - } - - operator bool() override { return getOutput() != nullptr; } - - protected: - AMRWB amr; - AMRWB::Mode mode; - SingleBuffer buffer{0}; -}; - -/** - * @brief AMR Wideband Encoder - * See https://github.com/pschatzmann/codec-amr - * @ingroup codecs - * @ingroup decoder - * @author Phil Schatzmann - * @copyright GPLv3 - */ - -class AMRWBEncoder : public AudioEncoder { - public: - /// Default constructor with valid mode values: WB_6_60,WB_8_85,WB_12_65,WB_14_25, - /// WB_15_85,WB_18_25,WB_19_85,WB_23_05,WB_23_85 (e.g. AMRWB::Mode::WB_6_60) - AMRWBEncoder(AMRWB::Mode mode) { - setMode(mode); - info.channels = 1; - info.sample_rate = 16000; - } - - ~AMRWBEncoder() override = default; - - void setMode(AMRWB::Mode mode) { - this->mode = mode; - amr.setMode(mode); - } - - bool begin() { - buffer.resize(frameSize()); - return getOutput() != nullptr; - } - - void setAudioInfo(AudioInfo from) { - if (from.bits_per_sample != 16) { - LOGE("Invalid bits per sample: %d", from.bits_per_sample); - } - if (from.sample_rate != 8000) { - LOGE("Invalid sample rate: %d", from.sample_rate); - } - if (from.channels != 1) { - LOGE("Invalid channels: %d", from.channels); - } - } - - size_t write(const uint8_t *data, size_t len) override { - for (size_t j = 0; j < len; j++) { - buffer.write(data[j]); - if (buffer.isFull()) { - int result_bytes = blockSize(); - uint8_t result[result_bytes]; - int size = - amr.encode((int16_t *)buffer.data(), - buffer.size() / sizeof(int16_t), result, result_bytes); - if (size > 0) { - if (getOutput() != nullptr) { - getOutput()->write(result, size); - } - } - buffer.clear(); - } - } - return len; - } - - /// Provides the block size (size of encoded frame) - int blockSize() { - amr.setMode(mode); - return amr.getEncodedFrameSizeBytes(); - } - - /// Provides the frame size (size of decoded frame) - int frameSize() { return amr.getFrameSizeSamples() * sizeof(int16_t); } - - const char *mime() { return "audio/amr"; } - - void setOutput(Print &out_stream) override { p_print = &out_stream; } - - Print *getOutput() { return p_print; } - - protected: - AMRWB amr; - AMRWB::Mode mode; - SingleBuffer buffer{0}; - Print *p_print = nullptr; -}; - -} // namespace audio_tools \ No newline at end of file diff --git a/src/AudioTools/AudioCodecs/CodecFLACFoxen.h b/src/AudioTools/AudioCodecs/CodecFLACFoxen.h deleted file mode 100644 index adaf2c59e9..0000000000 --- a/src/AudioTools/AudioCodecs/CodecFLACFoxen.h +++ /dev/null @@ -1,216 +0,0 @@ -#pragma once - -#include "AudioTools/AudioCodecs/AudioCodecsBase.h" -#include "AudioTools/CoreAudio/Buffers.h" -#include "foxen-flac.h" - -namespace audio_tools { - -#define FOXEN_IN_BUFFER_SIZE 1024 * 2 -#define FOXEN_OUT_BUFFER_SIZE 1024 * 4 - -/** - * @brief Foxen FLAC Decoder. - * @ingroup codecs - * @ingroup decoder - * @author Phil Schatzmann - * @copyright GPLv3 - */ -class FLACDecoderFoxen : public AudioDecoder { - public: - FLACDecoderFoxen() = default; - - /// Default Constructor - FLACDecoderFoxen(int maxBlockSize, int maxChannels, - bool convertTo16Bits = true) { - is_convert_to_16 = convertTo16Bits; - max_block_size = maxBlockSize; - max_channels = maxChannels; - }; - - /// Destructor - calls end(); - ~FLACDecoderFoxen() { end(); } - - bool begin() { - TRACEI(); - is_active = false; - if (flac == nullptr) { - size_t foxen_size = fx_flac_size(max_block_size, max_channels); - if (foxen_size > 0) { - foxen_data.resize(foxen_size); - } - if (foxen_data.data() != nullptr) { - flac = fx_flac_init(foxen_data.data(), max_block_size, max_channels); - } - } - if (flac != nullptr) { - is_active = true; - } else { - LOGE("not enough memory"); - if (is_stop_on_error) stop(); - } - if (buffer.size() == 0) { - buffer.resize(in_buffer_size); - out.resize(FOXEN_OUT_BUFFER_SIZE); - } - - return is_active; - } - - void end() { - TRACEI(); - if (flac != nullptr) { - flush(); - foxen_data.resize(0); - flac = nullptr; - } - buffer.resize(0); - out.resize(0); - is_active = false; - } - - size_t write(const uint8_t *data, size_t len) override { - LOGD("write: %d", len); - // no processing if not active - if (!is_active) return 0; - - size_t result = buffer.writeArray(data, len); - LOGD("buffer availabe: %d", buffer.available()); - - while (buffer.available() > 0) { - if (!decode()) break; - } - - // if the buffer is full we could not decode anything - if (buffer.available() == buffer.size()) { - LOGE("Decoder did not consume any data"); - if (is_stop_on_error) stop(); - } - - LOGD("write: %d -> %d", len, result); - return result; - } - - void flush() { decode(); } - - operator bool() override { return is_active; } - - /// Defines the input buffer size (default is 2k) - void setInBufferSize(int size){ - in_buffer_size = size; - } - - /// Defines the number of 32 bit samples for providing the result (default is 4k) - void setOutBufferSize(int size){ - out_buffer_size = size; - } - - /// Defines the maximum FLAC blocksize: drives the buffer allocation - void setMaxBlockSize(int size){ - max_block_size = size; - } - - /// Defines the maximum number of channels: drives the buffer allocation - void setMaxChannels(int ch){ - max_channels = ch; - } - - /// Select between 16 and 32 bit output: the default is 16 bits - void set32Bit(bool flag) { - is_convert_to_16 = !flag; - } - - protected: - fx_flac_t *flac = nullptr; - SingleBuffer buffer{0}; - Vector out; - Vector foxen_data{0}; - bool is_active = false; - bool is_convert_to_16 = true; - bool is_stop_on_error = true; - int bits_eff = 0; - int max_block_size = 5 * 1024; - int max_channels = 2; - int in_buffer_size = FOXEN_IN_BUFFER_SIZE; - int out_buffer_size = FOXEN_OUT_BUFFER_SIZE; - - bool decode() { - TRACED(); - if (flac == nullptr) return false; - uint32_t out_len = out.size(); - uint32_t buf_len = buffer.available(); - uint32_t buf_len_result = buf_len; - int rc = fx_flac_process(flac, buffer.data(), &buf_len_result, out.data(), - &out_len); - // assert(out_len <= FOXEN_OUT_BUFFER_SIZE); - - switch (rc) { - case FLAC_END_OF_METADATA: { - processMetadata(); - } break; - - case FLAC_ERR: { - LOGE("FLAC decoder in error state!"); - if (is_stop_on_error) stop(); - } break; - - default: { - if (out_len > 0) { - LOGD("Providing data: %d samples", out_len); - if (is_convert_to_16) { - write16BitData(out_len); - } else { - write32BitData(out_len); - } - } - } break; - } - LOGD("processed: %d bytes of %d -> %d samples", buf_len_result, buf_len, - out_len); - // removed processed bytes from buffer - buffer.clearArray(buf_len_result); - return buf_len_result > 0 || out_len > 0; - } - - void write32BitData(int out_len) { - TRACED(); - // write the result to the output destination - writeBlocking(p_print, (uint8_t *)out.data(), out_len * sizeof(int32_t)); - } - - void write16BitData(int out_len) { - TRACED(); - // in place convert to 16 bits - int16_t *out16 = (int16_t *)out.data(); - for (int j = 0; j < out_len; j++) { - out16[j] = out.data()[j] >> 16; // 65538; - } - // write the result to the output destination - LOGI("writeBlocking: %d", out_len * sizeof(int16_t)); - writeBlocking(p_print, (uint8_t *)out.data(), out_len * sizeof(int16_t)); - } - - void processMetadata() { - bits_eff = fx_flac_get_streaminfo(flac, FLAC_KEY_SAMPLE_SIZE); - int info_blocksize = fx_flac_get_streaminfo(flac, FLAC_KEY_MAX_BLOCK_SIZE); - - LOGI("bits: %d", bits_eff); - LOGI("blocksize: %d", info_blocksize); - // assert(bits_eff == 32); - info.sample_rate = fx_flac_get_streaminfo(flac, FLAC_KEY_SAMPLE_RATE); - info.channels = fx_flac_get_streaminfo(flac, FLAC_KEY_N_CHANNELS); - info.bits_per_sample = is_convert_to_16 ? 16 : bits_eff; - info.logInfo(); - if (info.channels > max_channels) { - LOGE("max channels too low: %d -> %d", max_channels, info.channels); - if (is_stop_on_error) stop(); - } - if (info_blocksize > max_block_size) { - LOGE("max channels too low: %d -> %d", max_block_size, info_blocksize); - if (is_stop_on_error) stop(); - } - notifyAudioChange(info); - } -}; - -} // namespace audio_tools diff --git a/src/AudioTools/AudioCodecs/CodecFactory.h b/src/AudioTools/AudioCodecs/CodecFactory.h deleted file mode 100644 index 098469508e..0000000000 --- a/src/AudioTools/AudioCodecs/CodecFactory.h +++ /dev/null @@ -1,64 +0,0 @@ -#pragma once - -#include "AudioTools/AudioCodecs/AudioCodecsBase.h" - -namespace audio_tools { -/** - * @brief Factory for creating new decoders based on the mime type or id - * @ingroup codecs - * @ingroup decoder - * @author Phil Schatzmann - */ -class CodecFactory { - public: - bool addDecoder(const char* id, AudioDecoder* (*cb)()) { - if (id == nullptr || cb == nullptr) return false; - DecoderFactoryLine line; - line.id = id; - line.cb = cb; - decoders.push_back(line); - return true; - } - - bool addEncoder(const char* id, AudioEncoder* (*cb)()) { - if (id == nullptr || cb == nullptr) return false; - EncoderFactoryLine line; - line.id = id; - line.cb = cb; - encoders.push_back(line); - return true; - } - - /// create a new decoder instance - AudioDecoder* createDecoder(const char* str) { - for (auto& line : decoders) { - if (line.id.equals(str)) { - return line.cb(); - } - } - return nullptr; - } - /// create a new encoder instance - AudioEncoder* createEncoder(const char* str) { - for (auto& line : encoders) { - if (line.id.equals(str)) { - return line.cb(); - } - } - return nullptr; - } - - protected: - struct DecoderFactoryLine { - Str id; - AudioDecoder* (*cb)() = nullptr; - }; - struct EncoderFactoryLine { - Str id; - AudioEncoder* (*cb)() = nullptr; - }; - Vector decoders; - Vector encoders; -}; - -} // namespace audio_tools diff --git a/src/AudioTools/AudioCodecs/CodecHelix.h b/src/AudioTools/AudioCodecs/CodecHelix.h deleted file mode 100644 index 8001455318..0000000000 --- a/src/AudioTools/AudioCodecs/CodecHelix.h +++ /dev/null @@ -1,37 +0,0 @@ -#pragma once - -#include "AudioTools/AudioCodecs/AudioCodecsBase.h" -#include "AudioTools/AudioCodecs/CodecAACHelix.h" -#include "AudioTools/AudioCodecs/CodecMP3Helix.h" -#include "AudioTools/AudioCodecs/CodecWAV.h" -#include "AudioTools/AudioCodecs/MultiDecoder.h" - -namespace audio_tools { - -/** - * @brief MP3 and AAC Decoder using libhelix: - * https://github.com/pschatzmann/arduino-libhelix. We dynamically create a MP3 - * or AAC decoder dependent on the provided audio format. In addition WAV files - * are also supported - * @ingroup codecs - * @ingroup decoder - * @author Phil Schatzmann - * @copyright GPLv3 - */ - -class DecoderHelix : public MultiDecoder { - public: - DecoderHelix() { - // register supported codecs with their mime type - addDecoder(mp3, "audio/mpeg"); - addDecoder(aac, "audio/aac"); - addDecoder(wav, "audio/vnd.wave"); - } - - protected: - MP3DecoderHelix mp3; - AACDecoderHelix aac; - WAVDecoder wav; -}; - -} // namespace audio_tools diff --git a/src/AudioTools/AudioCodecs/CodecMTS.h b/src/AudioTools/AudioCodecs/CodecMTS.h deleted file mode 100644 index 8b6fd3f417..0000000000 --- a/src/AudioTools/AudioCodecs/CodecMTS.h +++ /dev/null @@ -1,490 +0,0 @@ -#pragma once - -#define TS_PACKET_SIZE 188 - -#ifndef MTS_WRITE_BUFFER_SIZE -#define MTS_WRITE_BUFFER_SIZE 2000 -#endif - -#include "AudioTools/AudioCodecs/AudioCodecsBase.h" -#include "AudioTools/CoreAudio/AudioTypes.h" -#include "AudioToolsConfig.h" -#include "stdlib.h" - -namespace audio_tools { - -/** - * @brief PMT Program Element Stream Types - * @ingroup basic - */ -enum class MTSStreamType { - VIDEO = 0x01, - VIDEO_H262 = 0x02, - AUDIO_MP3 = 0x03, - AUDIO_MP3_LOW_BITRATE = 0x04, - PRV_SECTIONS = 0x05, - PES_PRV = 0x06, - MHEG = 0x07, - H222_0_DSM_CC = 0x08, - H222_1 = 0x09, - A = 0x0A, - B = 0x0B, - C = 0x0C, - D = 0x0D, - H222_0_AUX = 0x0E, - AUDIO_AAC = 0x0F, - VISUAL = 0x10, - AUDIO_AAC_LATM = 0x11, - SL_PES = 0x12, - SL_SECTIONS = 0x13, - SYNC_DOWNLOAD = 0x14, - PES_METADATA = 0x15, - METDATA_SECTIONS = 0x16, - METADATA_DATA_CAROUSEL = 0x17, - METADATA_OBJ_CAROUSEL = 0x18, - METADATA_SYNC_DOWNLOAD = 0x19, - IPMP = 0x1A, - VIDEO_AVC = 0X1B, - VIDEO_H222_0 = 0x1C, - DCII_VIDEO = 0x80, - AUDIO_A53 = 0x81, - SCTE_STD_SUBTITLE = 0x82, - SCTE_ISOCH_DATA = 0x83, - ATSC_PROG_ID = 0x85, - SCTE_25 = 0x86, - AUDIO_EAC3 = 0x87, - AUDIO_DTS_HD = 0x88, - DVB_MPE_FEC = 0x90, - ULE = 0x91, - VEI = 0x92, - ATSC_DATA_SERVICE_TABLE = 0x95, - SCTE_IP_DATA = 0xA0, - DCII_TEXT = 0xC0, - ATSC_SYNC_DATA = 0xC2, - SCTE_AYSNC_DATA = 0xC3, - ATSC_USER_PRIV_PROG_ELEMENTS = 0xC4, - VC1 = 0xEA, - ATSC_USER_PRIV = 0xEB, -}; - -// enum class AACProfile : uint8_t { -// MAIN = 0, // AAC Main (High complexity, rarely used) -// LC = 1, // AAC Low Complexity (Most common) -// SSR = 2, // AAC Scalable Sample Rate (Rare) -// LTP = 3 // AAC Long Term Prediction (Not widely supported) -// }; - -/** - * @brief MPEG-TS (MTS) decoder. Extracts (demuxes) the indicated audio/video - * data from a MPEG-TS (MTS) data stream. You can define the relevant stream - * types via the API: addStreamType(MTSStreamType). By default, the - * decoder selects the AUDIO_AAC, AUDIO_AAC_LATM stream types. - * - * @ingroup codecs - * @ingroup decoder - * @author Phil Schatzmann - * @copyright GPLv3 - **/ - -class MTSDecoder : public AudioDecoder { - public: - /// Default constructor - MTSDecoder() = default; - /// Provide the AAC decoder (or MP3 Decoder) to receive the extracted content - MTSDecoder(AudioDecoder &dec) { p_dec = &dec; }; - /// Start the prcessor - bool begin() override { - TRACED(); - pmt_pid = 0xFFFF; // undefined - pes_count = 0; - is_adts_missing = false; - open_pes_data_size = 0; - frame_length = 0; - - // default supported stream types - if (stream_types.empty()) { - addStreamType(MTSStreamType::AUDIO_AAC); - addStreamType(MTSStreamType::AUDIO_AAC_LATM); - } - - // automatically close when called multiple times - if (is_active) { - end(); - } - - if (p_dec) p_dec->begin(); - is_active = true; - return true; - } - - /// Stops the processing - void end() override { - TRACED(); - if (p_dec) p_dec->end(); - is_active = false; - } - - virtual operator bool() override { return is_active; } - - /// Provides the mime type: "video/MP2T"; - const char *mime() { return "video/MP2T"; } - - size_t write(const uint8_t *data, size_t len) override { - // only process when open - if (!is_active) { - TRACEE(); - return 0; - } - - // wait until we have enough data - if (buffer.availableForWrite() < len) { - LOGI("MTSDecoder::write: Buffer full"); - demux(); - return 0; - } - LOGI("MTSDecoder::write: %d", (int)len); - size_t result = buffer.writeArray((uint8_t *)data, len); - demux(); - return result; - } - - /// Set a new write buffer size (default is 2000) - void resizeBuffer(int size) { buffer.resize(size); } - - /// Clears the stream type filter - void clearStreamTypes() { - TRACED(); - stream_types.clear(); - } - - /// Defines the stream type that should be extracted - void addStreamType(MTSStreamType type) { - TRACED(); - stream_types.push_back(type); - } - - /// Checks if the stream type is active - bool isStreamTypeActive(MTSStreamType type) { - for (int j = 0; j < stream_types.size(); j++) { - if (stream_types[j] == type) return true; - } - return false; - } - - /// Defines where the decoded result is written to - void setOutput(AudioStream &out_stream) override { - if (p_dec) { - p_dec->setOutput(out_stream); - } else { - AudioDecoder::setOutput(out_stream); - } - } - - /// Defines where the decoded result is written to - void setOutput(AudioOutput &out_stream) override { - if (p_dec) { - p_dec->setOutput(out_stream); - } else { - AudioDecoder::setOutput(out_stream); - } - } - - /// Defines where the decoded result is written to - void setOutput(Print &out_stream) override { - if (p_dec) { - p_dec->setOutput(out_stream); - } else { - AudioDecoder::setOutput(out_stream); - } - } - - protected: - bool is_active = false; - SingleBuffer buffer{MTS_WRITE_BUFFER_SIZE}; - Vector stream_types; - Vector pids{0}; - AudioDecoder *p_dec = nullptr; - uint16_t pmt_pid = 0xFFFF; - // AACProfile aac_profile = AACProfile::LC; - MTSStreamType selected_stream_type; - int open_pes_data_size = 0; - int frame_length = 0; - bool is_adts_missing = false; - size_t pes_count = 0; - - /// Add the PID for which we want to extract the audio data from the PES - /// packets - void addPID(uint16_t pid) { - if (pid == 0) return; - for (int j = 0; j < pids.size(); j++) { - if (pids[j] == pid) return; - } - LOGI("-> PMT PID: 0x%04X(%d)", pid, pid); - pids.push_back(pid); - } - - /// demux the available data - void demux() { - TRACED(); - int count = 0; - while (parse()) { - LOGI("demux: step #%d with PES #%d", ++count, (int)pes_count); - } - LOGI("Number of demux calls: %d", count); - } - - /// Find the position of the next sync byte: Usually on position 0 - int syncPos() { - int len = buffer.available(); - if (len < TS_PACKET_SIZE) return -1; - for (int j = 0; j < len; j++) { - if (buffer.data()[j] == 0x47) { - return j; - } - } - return -1; - } - - /// Parse a single packet and remove the processed data - bool parse() { - int pos = syncPos(); - if (pos < 0) return false; - if (pos != 0) { - LOGW("Sync byte not found at position 0. Skipping %d bytes", pos); - buffer.clearArray(pos); - } - // parse data - uint8_t *packet = buffer.data(); - int pid = ((packet[1] & 0x1F) << 8) | (packet[2] & 0xFF); - LOGI("PID: 0x%04X(%d)", pid, pid); - - // PES contains the audio data - if (!is_adts_missing && pids.contains(pid)) { - parsePES(packet, pid); - } else { - parsePacket(packet, pid); - } - - // remove processed data - buffer.clearArray(TS_PACKET_SIZE); - return true; - } - - /// Detailed processing for parsing a single packet - void parsePacket(uint8_t *packet, int pid) { - TRACEI(); - bool payloadUnitStartIndicator = false; - - int payloadStart = - getPayloadStart(packet, false, payloadUnitStartIndicator); - int len = TS_PACKET_SIZE - payloadStart; - - // if we are at the beginning we start with a pat - if (pid == 0 && payloadUnitStartIndicator) { - pids.clear(); - } - - // PID 0 is for PAT - if (pid == 0) { - parsePAT(&packet[payloadStart], len); - } else if (pid == pmt_pid && packet[payloadStart] == 0x02) { - parsePMT(&packet[payloadStart], len); - } else { - LOGE("-> Packet ignored for PID 0x%x", pid); - } - } - - int getPayloadStart(uint8_t *packet, bool isPES, - bool &payloadUnitStartIndicator) { - uint8_t adaptionField = (packet[3] & 0x30) >> 4; - int adaptationSize = 0; - int offset = 4; // Start after TS header (4 bytes) - - // Check for adaptation field - // 00 (0) → Invalid (should never happen). - // 01 (1) → Payload only (no adaptation field). - // 10 (2) → Adaptation field only (no payload). - // 11 (3) → Adaptation field + payload. - if (adaptionField == 0b11) { // Adaptation field exists - adaptationSize = packet[4] + 1; - offset += adaptationSize; - } - - // If PUSI is set, there's a pointer field (skip it) - if (packet[1] & 0x40) { - if (!isPES) offset += packet[offset] + 1; - payloadUnitStartIndicator = true; - } - - LOGI("Payload Unit Start Indicator (PUSI): %d", payloadUnitStartIndicator); - LOGI("Adaption Field Control: 0x%x / size: %d", adaptionField, - adaptationSize); - - return offset; - } - - void parsePAT(uint8_t *pat, int len) { - TRACEI(); - assert(pat[0] == 0); // Program Association section - int startOfProgramNums = 8; - int lengthOfPATValue = 4; - int sectionLength = ((pat[1] & 0x0F) << 8) | (pat[2] & 0xFF); - LOGI("PAT Section Length: %d", sectionLength); - if (sectionLength >= len) { - LOGE("Unexpected PAT Section Length: %d", sectionLength); - sectionLength = len; - } - int indexOfPids = 0; - for (int i = startOfProgramNums; i <= sectionLength; - i += lengthOfPATValue) { - int program_number = ((pat[i] & 0xFF) << 8) | (pat[i + 1] & 0xFF); - int pid = ((pat[i + 2] & 0x1F) << 8) | (pat[i + 3] & 0xFF); - LOGI("Program Num: 0x%04X(%d) / PID: 0x%04X(%d) ", program_number, - program_number, pid, pid); - - if (pmt_pid == 0xFFFF && pid >= 0x0020 && pid <= 0x1FFE) { - pmt_pid = pid; - } - } - LOGI("Using PMT PID: 0x%04X(%d)", pmt_pid, pmt_pid); - } - - void parsePMT(uint8_t *pmt, int len) { - TRACEI(); - assert(pmt[0] == 0x02); // Program Association section - int staticLengthOfPMT = 12; - int sectionLength = ((pmt[1] & 0x0F) << 8) | (pmt[2] & 0xFF); - LOGI("- PMT Section Length: %d", sectionLength); - int programInfoLength = ((pmt[10] & 0x0F) << 8) | (pmt[11] & 0xFF); - LOGI("- PMT Program Info Length: %d", programInfoLength); - - int cursor = staticLengthOfPMT + programInfoLength; - while (cursor < sectionLength - 1) { - MTSStreamType streamType = static_cast(pmt[cursor] & 0xFF); - int elementaryPID = - ((pmt[cursor + 1] & 0x1F) << 8) | (pmt[cursor + 2] & 0xFF); - LOGI("-- Stream Type: 0x%02X(%d) [%s] for Elementary PID: 0x%04X(%d)", - (int)streamType, (int)streamType, toStr(streamType), elementaryPID, - elementaryPID); - - if (isStreamTypeActive(streamType)) { - selected_stream_type = streamType; - addPID(elementaryPID); - } - - int esInfoLength = - ((pmt[cursor + 3] & 0x0F) << 8) | (pmt[cursor + 4] & 0xFF); - LOGI("-- ES Info Length: 0x%04X(%d)", esInfoLength, esInfoLength); - cursor += 5 + esInfoLength; - } - } - - void parsePES(uint8_t *packet, int pid) { - LOGI("parsePES: %d", pid); - ++pes_count; - - // calculate payload start - bool payloadUnitStartIndicator = false; - int payloadStart = getPayloadStart(packet, true, payloadUnitStartIndicator); - - // PES - uint8_t *pes = packet + payloadStart; - int len = TS_PACKET_SIZE - payloadStart; - // PES (AAC) data - uint8_t *pesData = nullptr; - int pesDataSize = 0; - - if (payloadUnitStartIndicator) { - assert(len >= 6); - // PES header is not alligned correctly - if (!isPESStartCodeValid(pes)) { - LOGE("PES header not aligned correctly"); - return; - } - - int pesPacketLength = - (static_cast(pes[4]) << 8) | static_cast(pes[5]); - - // PES Header size is at least 6 bytes, but can be larger with optional - // fields - int pesHeaderSize = 6; - if ((pes[6] & 0xC0) != 0) { // Check for PTS/DTS flags - pesHeaderSize += 3 + ((pes[7] & 0xC0) == 0xC0 ? 5 : 0); - pesHeaderSize += pes[8]; // PES header stuffing size - } - LOGI("- PES Header Size: %d", pesHeaderSize); - pesData = pes + pesHeaderSize; - pesDataSize = len - pesHeaderSize; - - assert(pesHeaderSize < len); - assert(pesDataSize > 0); - - /// Check for ADTS - if (pes_count == 1 && selected_stream_type == MTSStreamType::AUDIO_AAC) { - is_adts_missing = findSyncWord(pesData, pesDataSize) == -1; - } - - open_pes_data_size = pesPacketLength; - - } else { - pesData = pes; - pesDataSize = len; - } - - // Recalculate the open data - open_pes_data_size -= pesDataSize; - if (open_pes_data_size < 0) { - return; - } - - /// Write the data - LOGI("- writing %d bytes (open: %d)", pesDataSize, open_pes_data_size); - if (p_print) { - size_t result = writeData(p_print, pesData, pesDataSize); - assert(result == pesDataSize); - } - if (p_dec) { - size_t result = - writeDataT(p_dec, pesData, pesDataSize); - assert(result == pesDataSize); - } - } - - /// check for PES packet start code prefix - bool isPESStartCodeValid(uint8_t *pes) { - if (pes[0] != 0) return false; - if (pes[1] != 0) return false; - if (pes[2] != 0x1) return false; - return true; - } - - /// Convert the relevant MTSStreamType to a string - const char *toStr(MTSStreamType type) { - switch (type) { - case MTSStreamType::AUDIO_MP3: - return "AUDIO_MP3"; - case MTSStreamType::AUDIO_MP3_LOW_BITRATE: - return "AUDIO_MP3_LOW_BITRATE"; - case MTSStreamType::AUDIO_AAC: - return "AUDIO_AAC"; - case MTSStreamType::AUDIO_AAC_LATM: - return "AUDIO_AAC_LATM"; - default: - return "UNKNOWN"; - } - } - - /// Finds the mp3/aac sync word - int findSyncWord(const uint8_t *buf, size_t nBytes, uint8_t synch = 0xFF, - uint8_t syncl = 0xF0) { - for (int i = 0; i < nBytes - 1; i++) { - if ((buf[i + 0] & synch) == synch && (buf[i + 1] & syncl) == syncl) - return i; - } - return -1; - } -}; - -using MPEG_TSDecoder = MTSDecoder; - -} // namespace audio_tools diff --git a/src/AudioTools/AudioCodecs/CodecNetworkFormat.h b/src/AudioTools/AudioCodecs/CodecNetworkFormat.h deleted file mode 100644 index 2f0bc6c5b2..0000000000 --- a/src/AudioTools/AudioCodecs/CodecNetworkFormat.h +++ /dev/null @@ -1,138 +0,0 @@ -#pragma once - -#include "AudioTools/AudioCodecs/AudioCodecsBase.h" -#include "AudioTools/CoreAudio/AudioBasic/Net.h" -#if defined(ARDUINO) && !defined(IS_MIN_DESKTOP) -#include "Print.h" -#endif - -namespace audio_tools { - -/** - * @brief PCM decoder which converts from network format to the host format. - * @ingroup codecs - * @ingroup decoder - * @author Phil Schatzmann - * @copyright GPLv3 - */ -class DecoderNetworkFormat : public AudioDecoder { - public: - DecoderNetworkFormat() = default; - - DecoderNetworkFormat(Print &out_stream) { - TRACED(); - pt_print = &out_stream; - } - - DecoderNetworkFormat(Print &out_stream, AudioInfoSupport &bi) { - pt_print = &out_stream; - } - - ~DecoderNetworkFormat() {} - - virtual void setOutput(Print &out_stream) { pt_print = &out_stream; } - - bool begin() { return true; } - - void end() {} - - size_t write(const uint8_t *data, size_t len) { - TRACED(); - switch (audioInfo().bits_per_sample) { - case 8: - // nothing to do - break; - case 16: { - int16_t *data16 = (int16_t *)data; - for (int i = 0; i < len / sizeof(int16_t); i++) { - data16[i] = ntohs(data16[i]); - } - } break; - case 24: - case 32: { - int32_t *data32 = (int32_t *)data; - for (int i = 0; i < len / sizeof(int32_t); i++) { - data32[i] = ntohl(data32[i]); - } - } break; - default: - LOGE("bits_per_sample not supported: %d", - (int)audioInfo().bits_per_sample); - break; - } - return pt_print->write((uint8_t *)data, len); - } - - operator bool() { return true; } - - /// The result is encoded data - by default this is false - virtual bool isResultPCM() { return true; } - - protected: - Print *pt_print = nullptr; -}; - -/** - * @brief Encoder which converts from the host format to the network format. - * @ingroup codecs - * @ingroup encoder - * @author Phil Schatzmann - * @copyright GPLv3 - */ -class EncoderNetworkFormat : public AudioEncoder { - public: - EncoderNetworkFormat() { TRACED(); } - - EncoderNetworkFormat(Print &out_stream) { - TRACED(); - pt_print = &out_stream; - } - - EncoderNetworkFormat(Print &out_stream, AudioInfoSupport &bi) { - pt_print = &out_stream; - } - - ~EncoderNetworkFormat() {} - - virtual void setOutput(Print &out_stream) { pt_print = &out_stream; } - - bool begin() { return true; } - - void end() {} - - size_t write(const uint8_t *data, size_t len) { - TRACED(); - switch (audioInfo().bits_per_sample) { - case 8: - // nothing to do - break; - case 16: { - int16_t *data16 = (int16_t *)data; - for (int i = 0; i < len / sizeof(int16_t); i++) { - data16[i] = htons(data16[i]); - } - } break; - case 24: - case 32: { - int32_t *data32 = (int32_t *)data; - for (int i = 0; i < len / sizeof(int32_t); i++) { - data32[i] = htonl(data32[i]); - } - } break; - default: - LOGE("bits_per_sample not supported: %d", - (int)audioInfo().bits_per_sample); - break; - } - return pt_print->write((uint8_t *)data, len); - } - - operator bool() { return true; } - - const char *mime() { return "audio/pcm"; } - - protected: - Print *pt_print = nullptr; -}; - -} // namespace audio_tools diff --git a/src/AudioTools/AudioCodecs/CodecTSDemux.h b/src/AudioTools/AudioCodecs/CodecTSDemux.h deleted file mode 100644 index 82a5e3a1c6..0000000000 --- a/src/AudioTools/AudioCodecs/CodecTSDemux.h +++ /dev/null @@ -1,1022 +0,0 @@ -#pragma once - -#include "AudioTools/AudioCodecs/AudioCodecsBase.h" - -namespace audio_tools { - -#include "AudioTools/AudioCodecs/AudioEncoded.h" -#include "stdlib.h" -#include "tsdemux.h" - -#ifndef MTS_PRINT_PIDS_LEN -#define MTS_PRINT_PIDS_LEN (16) -#endif - -#ifndef MTS_UNDERFLOW_LIMIT -#define MTS_UNDERFLOW_LIMIT 188 -#endif - -#ifndef MTS_WRITE_BUFFER_SIZE -#define MTS_WRITE_BUFFER_SIZE 2000 -#endif - -#ifndef ALLOC_MEM_INIT -#define ALLOC_MEM_INIT 0 -#endif - -struct AllocSize { - void *data = nullptr; - size_t size = 0; - - AllocSize() = default; - AllocSize(void *data, size_t size) { - this->data = data; - this->size = size; - } -}; - -/** - * @brief MPEG-TS (MTS) decoder. Extracts the AAC audio data from a MPEG-TS - * (MTS) data stream. You can define the relevant stream types via the API. - * Required dependency: https://github.com/pschatzmann/arduino-tsdemux - * @ingroup codecs - * @ingroup decoder - * @author Phil Schatzmann - * @copyright GPLv3 - */ - -class MTSDecoderTSDemux : public AudioDecoder { - public: - MTSDecoderTSDemux() { self = this; }; - MTSDecoderTSDemux(AudioDecoder &dec) { p_dec = &dec; }; - - bool begin() override { - TRACED(); - // automatically close when called multiple times - if (is_active) { - end(); - } - - if (p_dec) p_dec->begin(); - - is_active = true; - - // create the pids we plan on printing - memset(print_pids, 0, sizeof(print_pids)); - - // set default values onto the context. - if (tsd_context_init(&ctx) != TSD_OK) { - TRACEE(); - is_active = false; - } - - // log memory allocations ? - if (is_alloc_active) { - ctx.malloc = log_malloc; - ctx.realloc = log_realloc; - ctx.calloc = log_calloc; - ctx.free = log_free; - } - - // default supported stream types - if (stream_types.empty()) { - addStreamType(TSD_PMT_STREAM_TYPE_PES_METADATA); - addStreamType(TSD_PMT_STREAM_TYPE_AUDIO_AAC); - } - - // add a callback. - // the callback is used to determine which PIDs contain the data we want - // to demux. We also receive PES data for any PIDs that we register later - // on. - if (tsd_set_event_callback(&ctx, event_cb) != TSD_OK) { - TRACEE(); - is_active = false; - } - return true; - } - - void end() override { - TRACED(); - if (p_dec) p_dec->begin(); - - // finally end the demux process which will flush any remaining PES data. - tsd_demux_end(&ctx); - - // destroy context - tsd_context_destroy(&ctx); - - is_active = false; - } - - virtual operator bool() override { return is_active; } - - const char *mime() { return "video/MP2T"; } - - size_t write(const uint8_t *data, size_t len) override { - if (!is_active) return 0; - LOGD("MTSDecoderTSDemux::write: %d", (int)len); - size_t result = buffer.writeArray((uint8_t *)data, len); - // demux - demux(underflowLimit); - return result; - } - - void flush() { demux(0); } - - void clearStreamTypes() { - TRACED(); - stream_types.clear(); - } - - void addStreamType(TSDPMTStreamType type) { - TRACED(); - stream_types.push_back(type); - } - - bool isStreamTypeActive(TSDPMTStreamType type) { - for (int j = 0; j < stream_types.size(); j++) { - if (stream_types[j] == type) return true; - } - return false; - } - - /// Set a new write buffer size (default is 2000) - void resizeBuffer(int size) { buffer.resize(size); } - - /// Activate logging for memory allocations - void setMemoryAllocationLogging(bool flag) { is_alloc_active = flag; } - - /// Defines where the decoded result is written to - void setOutput(AudioStream &out_stream) override { - if (p_dec) { - p_dec->setOutput(out_stream); - } else { - AudioDecoder::setOutput(out_stream); - } - } - - /// Defines where the decoded result is written to - void setOutput(AudioOutput &out_stream) override { - if (p_dec) { - p_dec->setOutput(out_stream); - } else { - AudioDecoder::setOutput(out_stream); - } - } - - /// Defines where the decoded result is written to - void setOutput(Print &out_stream) override { - if (p_dec) { - p_dec->setOutput(out_stream); - } else { - AudioDecoder::setOutput(out_stream); - } - } - - protected: - static MTSDecoderTSDemux *self; - int underflowLimit = MTS_UNDERFLOW_LIMIT; - bool is_active = false; - bool is_write_active = false; - bool is_alloc_active = false; - TSDemuxContext ctx; - uint16_t print_pids[MTS_PRINT_PIDS_LEN] = {0}; - SingleBuffer buffer{MTS_WRITE_BUFFER_SIZE}; - Vector stream_types; - Vector alloc_vector; - AudioDecoder *p_dec = nullptr; - - void set_write_active(bool flag) { - // LOGD("is_write_active: %s", flag ? "true":"false"); - is_write_active = flag; - } - - /// Determines if we are at the beginning of a new file - bool is_new_file(uint8_t *data) { - bool payloadUnitStartIndicator = (data[1] & 0x40) >> 6; - bool result = data[0] == 0x47 && payloadUnitStartIndicator; - return result; - } - - void demux(int limit) { - TRACED(); - TSDCode res = TSD_OK; - int count = 0; - while (res == TSD_OK && buffer.available() >= limit) { - // Unfortunatly we need to reset the demux after each file - if (is_new_file(buffer.data())) { - LOGD("parsing new file"); - begin(); - } - size_t len = 0; - res = tsd_demux(&ctx, (void *)buffer.data(), buffer.available(), &len); - // remove processed bytes - buffer.clearArray(len); - // get next bytes - count++; - if (res != TSD_OK) logResult(res); - } - LOGD("Number of demux calls: %d", count); - } - - void logResult(TSDCode code) { - switch (code) { - case TSD_OK: - LOGD("TSD_OK"); - break; - case TSD_INVALID_SYNC_BYTE: - LOGW("TSD_INVALID_SYNC_BYTE"); - break; - case TSD_INVALID_CONTEXT: - LOGW("TSD_INVALID_CONTEXT"); - break; - case TSD_INVALID_DATA: - LOGW("TSD_INVALID_DATA"); - break; - case TSD_INVALID_DATA_SIZE: - LOGW("TSD_INVALID_DATA_SIZE"); - break; - case TSD_INVALID_ARGUMENT: - LOGW("TSD_INVALID_ARGUMENT"); - break; - case TSD_INVALID_START_CODE_PREFIX: - LOGW("TSD_INVALID_START_CODE_PREFIX"); - break; - case TSD_OUT_OF_MEMORY: - LOGW("TSD_OUT_OF_MEMORY"); - break; - case TSD_INCOMPLETE_TABLE: - LOGW("TSD_INCOMPLETE_TABLE"); - break; - case TSD_NOT_A_TABLE_PACKET: - LOGW("TSD_NOT_A_TABLE_PACKET"); - break; - case TSD_PARSE_ERROR: - LOGW("TSD_PARSE_ERROR"); - break; - case TSD_PID_ALREADY_REGISTERED: - LOGW("TSD_PID_ALREADY_REGISTERED"); - break; - case TSD_TSD_MAX_PID_REGS_REACHED: - LOGW("TSD_TSD_MAX_PID_REGS_REACHED"); - break; - case TSD_PID_NOT_FOUND: - LOGW("TSD_PID_NOT_FOUND"); - break; - case TSD_INVALID_POINTER_FIELD: - LOGW("TSD_INVALID_POINTER_FIELD"); - break; - } - } - - // event callback - static void event_cb(TSDemuxContext *ctx, uint16_t pid, TSDEventId event_id, - void *data) { - TRACED(); - if (MTSDecoderTSDemux::self != nullptr) { - MTSDecoderTSDemux::self->event_cb_local(ctx, pid, event_id, data); - } - } - - void event_cb_local(TSDemuxContext *ctx, uint16_t pid, TSDEventId event_id, - void *data) { - if (event_id == TSD_EVENT_PAT) { - set_write_active(false); - print_pat(ctx, data); - } else if (event_id == TSD_EVENT_PMT) { - set_write_active(false); - print_pmt(ctx, data); - } else if (event_id == TSD_EVENT_PES) { - TSDPESPacket *pes = (TSDPESPacket *)data; - // This is where we write the PES data into our buffer. - LOGD("===================="); - LOGD("PID %x PES Packet, Size: %zu, stream_id=%u, pts=%lu, dts=%lu", pid, - pes->data_bytes_length, pes->stream_id, (unsigned long)pes->pts, - (unsigned long)pes->dts); - // print out the PES Packet data if it's in our print list - int i; - AudioLogger logger = AudioLogger::instance(); - for (i = 0; i < MTS_PRINT_PIDS_LEN; ++i) { - if (print_pids[i] == pid) { - // log data - if (logger.isLogging(AudioLogger::Debug)) { - logger.print(" PES data"); - logger.print(is_write_active ? "active:" : "inactive:"); - int j = 0; - while (j < pes->data_bytes_length) { - char n = pes->data_bytes[j]; - logger.printCharHex(n); - ++j; - } - logger.printChar('\n'); - } - // output data - if (p_print != nullptr) { - // size_t eff = p_print->write(pes->data_bytes, - // pes->data_bytes_length); - size_t eff = writeData( - p_print, (uint8_t *)pes->data_bytes, pes->data_bytes_length); - if (eff != pes->data_bytes_length) { - // we should not get here - TRACEE(); - } - } - if (p_dec != nullptr) { - // size_t eff = p_print->write(pes->data_bytes, - // pes->data_bytes_length); - size_t eff = writeDataT( - p_dec, (uint8_t *)pes->data_bytes, pes->data_bytes_length); - if (eff != pes->data_bytes_length) { - // we should not get here - TRACEE(); - } - } - } - } - - } else if (event_id == TSD_EVENT_ADAP_FIELD_PRV_DATA) { - set_write_active(false); - // we're only watching for SCTE Adaptions Field Private Data, - // so we know that we must parse it as a list of descritors. - TSDAdaptationField *adap_field = (TSDAdaptationField *)data; - TSDDescriptor *descriptors = NULL; - size_t descriptors_length = 0; - tsd_descriptor_extract(ctx, adap_field->private_data_bytes, - adap_field->transport_private_data_length, - &descriptors, &descriptors_length); - - LOGD("===================="); - LOGD("Descriptors - Adaptation Fields"); - int i = 0; - for (; i < descriptors_length; ++i) { - TSDDescriptor *des = &descriptors[i]; - LOGD(" %d) tag: (0x%04X) %s", i, des->tag, - descriptor_tag_to_str(des->tag)); - LOGD(" length: %d", des->length); - print_descriptor_info(des); - } - } - } - - void print_pat(TSDemuxContext *ctx, void *data) { - LOGD("===================="); - TSDPATData *pat = (TSDPATData *)data; - size_t len = pat->length; - size_t i; - LOGD("PAT, Length %d", (int)pat->length); - - if (len > 1) { - LOGD("number of progs: %d", (int)len); - } - for (i = 0; i < len; ++i) { - LOGD(" %d) prog num: 0x%X, pid: 0x%X", (int)i, pat->program_number[i], - pat->pid[i]); - } - } - - void print_pmt(TSDemuxContext *ctx, void *data) { - LOGD("===================="); - LOGD("PMT"); - TSDPMTData *pmt = (TSDPMTData *)data; - LOGD("PCR PID: 0x%04X", pmt->pcr_pid); - LOGD("program info length: %d", (int)pmt->program_info_length); - LOGD("descriptors length: %d", (int)pmt->descriptors_length); - size_t i; - - for (i = 0; i < pmt->descriptors_length; ++i) { - TSDDescriptor *des = &pmt->descriptors[i]; - LOGD(" %d) tag: (0x%04X) %s", (int)i, des->tag, - descriptor_tag_to_str(des->tag)); - LOGD(" length: %d", des->length); - print_descriptor_info(des); - } - - LOGD("program elements length: %d", (int)pmt->program_elements_length); - for (i = 0; i < pmt->program_elements_length; ++i) { - TSDProgramElement *prog = &pmt->program_elements[i]; - LOGD(" -----Program #%d", (int)i); - LOGD(" stream type: (0x%04X) %s", prog->stream_type, - stream_type_to_str((TSDPESStreamId)(prog->stream_type))); - LOGD(" elementary pid: 0x%04X", prog->elementary_pid); - LOGD(" es info length: %d", prog->es_info_length); - LOGD(" descriptors length: %d", (int)prog->descriptors_length); - - if (isStreamTypeActive((TSDPMTStreamType)prog->stream_type)) { - set_write_active(true); - } - - // keep track of metadata pids, we'll print the data for these - for (int j = 0; j < stream_types.size(); j++) { - add_print_pid(prog, stream_types[j]); - } - - // we'll register to listen to the PES data for this program. - int reg_types = TSD_REG_PES; - - size_t j; - for (j = 0; j < prog->descriptors_length; ++j) { - TSDDescriptor *des = &prog->descriptors[j]; - LOGD(" %d) tag: (0x%04X) %s", (int)j, des->tag, - descriptor_tag_to_str(des->tag)); - LOGD(" length: %d", des->length); - print_descriptor_info(des); - - // if this tag is the SCTE Adaption field private data descriptor, - // we'll also register for the Adaptation Field Privae Data. - if (des->tag == 0x97) { - reg_types |= TSD_REG_ADAPTATION_FIELD; - } - } - - // register all the PIDs we come across. - tsd_register_pid(ctx, prog->elementary_pid, reg_types); - } - } - - void add_print_pid(TSDProgramElement *prog, TSDPMTStreamType type) { - if (prog->stream_type == type) { - int k; - for (k = 0; k < MTS_PRINT_PIDS_LEN; ++k) { - // find a spare slot in the pids - if (print_pids[k] == 0) { - print_pids[k] = prog->elementary_pid; - break; - } - } - } - } - - const char *stream_type_to_str(TSDPESStreamId stream_id) { - if (stream_id >= 0x1C && stream_id <= 0x7F && stream_id != 0x24 && - stream_id != 0x42) { - stream_id = (TSDPESStreamId)0x1C; - } else if (stream_id >= 0x8A && stream_id <= 0x8F) { - stream_id = (TSDPESStreamId)0x8A; - } else if (stream_id >= 0x93 && stream_id <= 0x94) { - stream_id = (TSDPESStreamId)0x93; - } else if (stream_id >= 0x96 && stream_id <= 0x9F) { - stream_id = (TSDPESStreamId)0x96; - } else if (stream_id >= 0xA1 && stream_id <= 0xBF) { - stream_id = (TSDPESStreamId)0xA1; - } else if (stream_id >= 0xC4 && stream_id <= 0xE9) { - stream_id = (TSDPESStreamId)0xC4; - } else if (stream_id >= 0xEB && stream_id <= 0xFF) { - stream_id = (TSDPESStreamId)0xEB; - } - -#pragma GCC diagnostic push -#pragma GCC diagnostic ignored "-Wswitch" - switch (stream_id) { - case 0x00: - return "ITU-T | ISO/IEC Reserved"; - case 0x01: - return "ISO/IEC 11172 Video"; - case 0x02: - return "ITU-T Rec. H.262 | ISO/IEC 13818-2 Video"; - case 0x03: - return "ISO/IEC 11172 Audio"; - case 0x04: - return "ISO/IEC 13818-3 Audio"; - case 0x05: - return "ITU-T Rec. H.222.0 | ISO/IEC 13818-1 private sections"; - case 0x06: - return "ITU-T Rec. H.222.0 | ISO/IEC 13818-1 PES packets containing " - "private data"; - case 0x07: - return "ISO/IEC 13522 MHEG"; - case 0x08: - return "ITU-T Rec. H.222.0 | ISO/IEC 13818-1 DSM-CC"; - case 0x09: - return "ITU-T Rec. H.222.0 | ISO/IEC 13818-1/11172-1 auxiliary"; - case 0x0A: - return "ISO/IEC 13818-6 Multi-protocol Encapsulation"; - case 0x0B: - return "ISO/IEC 13818-6 DSM-CC U-N Messages"; - case 0x0C: - return "ISO/IEC 13818-6 Stream Descriptors"; - case 0x0D: - return "ISO/IEC 13818-6 Sections (any type, including private data)"; - case 0x0E: - return "ISO/IEC 13818-1 auxiliary"; - case 0x0F: - return "ISO/IEC 13818-7 Audio (AAC) with ADTS transport"; - case 0x10: - return "ISO/IEC 14496-2 Visual"; - case 0x11: - return "ISO/IEC 14496-3 Audio with the LATM transport syntax as " - "defined in ISO/IEC 14496-3"; - case 0x12: - return "ISO/IEC 14496-1 SL-packetized stream or FlexMux stream carried " - "in PES packets"; - case 0x13: - return "ISO/IEC 14496-1 SL-packetized stream or FlexMux stream carried " - "in ISO/IEC 14496_sections"; - case 0x14: - return "ISO/IEC 13818-6 DSM-CC Synchronized Download Protocol"; - case 0x15: - return "Metadata carried in PES packets"; - case 0x16: - return "Metadata carried in metadata_sections"; - case 0x17: - return "Metadata carried in ISO/IEC 13818-6 Data Carousel"; - case 0x18: - return "Metadata carried in ISO/IEC 13818-6 Object Carousel"; - case 0x19: - return "Metadata carried in ISO/IEC 13818-6 Synchronized Download " - "Protocol"; - case 0x1A: - return "IPMP stream (defined in ISO/IEC 13818-11, MPEG-2 IPMP)"; - case 0X1B: - return "AVC video stream as defined in ITU-T Rec. H.264 | ISO/IEC " - "14496-10 Video"; - case 0x1C: - return "ITU-T Rec. H.222.0 | ISO/IEC 13818-1 Reserved"; - case 0x24: - return "ITU-T Rec. H.265 and ISO/IEC 23008-2 (Ultra HD video) in a " - "packetized stream"; - case 0x42: - return "Chinese Video Standard in a packetized stream"; - case 0x80: - return "DigiCipher® II video | Identical to ITU-T Rec. H.262 | ISO/IEC " - "13818-2 Video"; - case 0x81: - return "ATSC A/53 audio [2] | AC-3 audio"; - case 0x82: - return "SCTE Standard Subtitle"; - case 0x83: - return "SCTE Isochronous Data | Reserved"; - case 0x84: - return "ATSC/SCTE reserved"; - case 0x85: - return "ATSC Program Identifier , SCTE Reserved"; - case 0x86: - return "SCTE 35 splice_information_table | [Cueing]"; - case 0x87: - return "E-AC-3"; - case 0x88: - return "DTS HD Audio"; - case 0x89: - return "ATSC Reserved"; - case 0x8A: - return "ATSC Reserved"; - case 0x90: - return "DVB stream_type value for Time Slicing / MPE-FEC"; - case 0x91: - return "IETF Unidirectional Link Encapsulation (ULE)"; - case 0x92: - return "VEI stream_type"; - case 0x93: - return "ATSC Reserved"; - case 0x95: - return "ATSC Data Service Table, Network Resources Table"; - case 0x96: - return "ATSC Reserved"; - case 0xA0: - return "SCTE [IP Data] | ATSC Reserved"; - case 0xA1: - return "ATSC Reserved"; - case 0xC0: - return "DCII (DigiCipher®) Text"; - case 0xC1: - return "ATSC Reserved"; - case 0xC2: - return "ATSC synchronous data stream | [Isochronous Data]"; - case 0xC3: - return "SCTE Asynchronous Data"; - case 0xC4: - return "ATSC User Private Program Elements"; - case 0xEA: - return "VC-1 Elementary Stream per RP227"; - case 0xEB: - return "ATSC User Private Program Elements"; - default: - return "Unknown"; - } -#pragma GCC diagnostic pop - } - - const char *descriptor_tag_to_str(uint8_t tag) { - if (tag >= 0x24 && tag <= 0x27) { - tag = 0x24; - } else if (tag >= 0x29 && tag <= 0x35) { - tag = 0x29; - } else if (tag >= 0x3A && tag <= 0x3F) { - tag = 0x3A; - } else if (tag >= 0x40 && tag <= 0x51) { - tag = 0x40; - } else if (tag >= 0x98 && tag <= 0x9F) { - tag = 0x98; - } - -#pragma GCC diagnostic push -#pragma GCC diagnostic ignored "-Wswitch" - - switch (tag) { - case 0x00: - case 0x01: - return "ISO/IEC 13818 Reserved"; - case 0x02: - return "video_stream_descriptor"; - case 0x03: - return "audio_stream_descriptor"; - case 0x04: - return "hierarchy_descriptor"; - case 0x05: - return "registration_descriptor"; - case 0x06: - return "data_stream_alignment_descriptor"; - case 0x07: - return "target_background_grid_descriptor"; - case 0x08: - return "video_window_descriptor"; - case 0x09: - return "CA_descriptor"; - case 0x0A: - return "ISO_639_language_descriptor"; - case 0x0B: - return "system_clock_descriptor"; - case 0x0C: - return "multiplex_buffer_utilization_descriptor"; - case 0x0D: - return "copyright_descriptor"; - case 0x0E: - return "Maximum_bitrate_descriptor"; - case 0x0F: - return "Private_data_indicator_descriptor"; - case 0x10: - return "smoothing_buffer_descriptor"; - case 0x11: - return "STD_descriptor"; - case 0x12: - return "IBP descriptor"; - case 0x13: - return "DSM-CC carousel_identifier_descriptor"; - case 0x14: - return "DSM-CC association_tag_descriptor"; - case 0x15: - return "DSM-CC deferred_association_tags_descriptor"; - case 0x16: - return "ISO/IEC 13818-6 reserved"; - case 0x17: - return "NPT Reference descriptor"; - case 0x18: - return "NPT Endpoint descriptor"; - case 0x19: - return "Stream Mode descriptor"; - case 0x1A: - return "Stream Event descriptor"; - case 0x1B: - return "MPEG-4_video_descriptor"; - case 0x1C: - return "MPEG-4_audio_descriptor"; - case 0x1D: - return "IOD_descriptor"; - case 0x1E: - return "SL_descriptor"; - case 0x1F: - return "FMC_descriptor"; - case 0x20: - return "External_ES_ID_descriptor"; - case 0x21: - return "MuxCode_descriptor"; - case 0x22: - return "FmxBufferSize_descriptor"; - case 0x23: - return "MultiplexBuffer_descriptor"; - case 0x24: - return "Reserved for ISO/IEC 13818-1 use"; - case 0x28: - return "AVC_video_descriptor()"; - case 0x29: - return "Reserved for ISO/IEC 13818-1 use"; - case 0x36: - return "content_labeling_descriptor"; - case 0x37: - return "Metadata_location_descriptor"; - case 0x3A: - return "ISO/IEC 13818 Reserved"; - case 0x40: - return "User Private"; - case 0x52: - return "SCTE 35 Stream Identifier Descriptor"; - case 0x60: - return "ACAP-X Application Descriptor"; - case 0x61: - return "ACAP-X Application Location Descriptor"; - case 0x62: - return "ACAP-X Application Boundary Descriptor"; - case 0x80: - return "Stuffing_descriptor"; - case 0x81: - return "AC3_audio_descriptor"; - case 0x82: - return "SCTE Frame_rate_descriptor"; - case 0x83: - return "SCTE Extended_video_descriptor"; - case 0x84: - return "SCTE Component_name_descriptor"; - case 0x85: - return "ATSC program_identifier"; - case 0x86: - return "Caption_service_descriptor"; - case 0x87: - return "Content_advisory_descriptor"; - case 0x88: - return "ATSC CA_descriptor"; - case 0x89: - return "ATSC Descriptor_tag"; - case 0x8A: - return "SCTE 35 cue identifier descriptor"; - case 0x8B: - return "ATSC/SCTE Reserved"; - case 0x8C: - return "TimeStampDescriptor"; - case 0x8D: - return "parameterized_service_descriptor() "; - case 0x8E: - return "Interactive Services Filtering Criteria descriptor"; - case 0x8F: - return "Interactive Services NRT Services Summary descriptor"; - case 0x90: - return "SCTE Frequency_spec_descriptor"; - case 0x91: - return "SCTE Modulation_params_descriptor"; - case 0x92: - return "SCTE Transport_stream_id_descriptor"; - case 0x93: - return "SCTE Revision detection descriptor"; - case 0x94: - return "SCTE Two part channel number descriptor"; - case 0x95: - return "SCTE Channel properties descriptor"; - case 0x96: - return "SCTE Daylight Savings Time Descriptor"; - case 0x97: - return "SCTE_adaptation_field_data_descriptor()"; - case 0x98: - return "SCTE Reserved"; - case 0xA0: - return "extended_channel_name_descriptor"; - case 0xA1: - return "ATSC service_location_descriptor"; - case 0xA2: - return "time_shifted_service_descriptor"; - case 0xA3: - return "component_name_descriptor"; - case 0xA4: - return "ATSC data_service_descriptor"; - case 0xA5: - return "ATSC PID Count descriptor"; - case 0xA6: - return "ATSC Download descriptor"; - case 0xA7: - return "ATSC Multiprotocol Encapsulation descriptor"; - case 0xA8: - return "ATSC dcc_departing_request_descriptor"; - case 0xA9: - return "ATSC dcc_arriving_request_descriptor"; - case 0xAA: - return "ATSC rc_descriptor"; - case 0xAB: - return "ATSC Genre descriptor"; - case 0xAC: - return "SCTE MAC Address List"; - case 0xAD: - return "ATSC private information descriptor"; - case 0xAE: - return "ATSC compatibility wrapper descriptor"; - case 0xAF: - return "ATSC broadcaster policy descriptor"; - case 0xB0: - return "ATSC service name descriptor"; - case 0xB1: - return "ATSC URI descriptor"; - case 0xB2: - return "ATSC enhanced signaling descriptor"; - case 0xB3: - return "ATSC M/H string mapping descriptor"; - case 0xB4: - return "ATSC Module Link descriptor"; - case 0xB5: - return "ATSC CRC32 descriptor"; - case 0xB6: - return "ATSC Content Identifier Descriptor"; - case 0xB7: - return "ModuleInfoDescriptor"; - case 0xB8: - return "ATSC Group Link descriptor"; - case 0xB9: - return "ATSC Time Stamp descriptor"; - case 0xBA: - return "ScheduleDescriptor"; - case 0xBB: - return "Component list descriptor"; - case 0xBC: - return "ATSC M/H component descriptor"; - case 0xBD: - return "ATSC M/H rights issuer descriptor"; - case 0xBE: - return "ATSC M/H current program descriptor"; - case 0xBF: - return "ATSC M/H original service identification descriptor"; - case 0xC0: - return "protection_descriptor"; - case 0xC1: - return "MH_SG_bootstrap_descriptor"; - case 0xC2: - return "Service ID descriptor"; - case 0xC3: - return "Protocol Version descriptor"; - case 0xC4: - return "NRT Service descriptor"; - case 0xC5: - return "Capabilities descriptor"; - case 0xC6: - return "Icon descriptor"; - case 0xC7: - return "Receiver Targeting descriptor"; - case 0xC8: - return "Time Slot descriptor"; - case 0xC9: - return "Internet Location Descriptor"; - case 0xCA: - return "Associated Service descriptor"; - case 0xCB: - return "Eye Identification Descriptor tag"; - case 0xCC: - return "E-AC-3 descriptor (A/52 Annex G)"; - case 0xCD: - return "2D 3D Corresponding Content Descriptor"; - case 0xCE: - return "Multimedia EPG Linkage Descriptor"; - case 0xE0: - return "etv_application_information_descriptor()"; - case 0xE1: - return "etv_media_time_descriptor()"; - case 0xE2: - return "etv_stream_event_descriptor()"; - case 0xE3: - return "etv_application_descriptor()"; - case 0xE4: - return "RBI_signaling_descriptor()"; - case 0xE5: - return "etv_application_metadata_descriptor()"; - case 0xE6: - return "etv_bif_platform_descriptor()"; - case 0xE7: - return "etv_integrated_signaling_descriptor()"; - case 0xE8: - return "3d_MPEG2_descriptor()"; - case 0XE9: - return "ebp_descriptor()"; - case 0xEA: - return "MPEG_AAC_descriptor"; - case 0xEB: - return "IC3D_event_info_descriptor"; - case 0xEC: - return "MDTV hybrid stereoscopic service descriptor"; - } -#pragma GCC diagnostic pop - return "Unknown"; - } - - void print_descriptor_info(TSDDescriptor *desc) { - // print out some interesting descriptor data - switch (desc->tag) { - case 0x05: // Registration descriptor - { - TSDDescriptorRegistration res; - if (TSD_OK == tsd_parse_descriptor_registration( - desc->data, desc->data_length, &res)) { - LOGD("\n format identififer: 0x%08X", res.format_identifier); - } - } break; - case 0x0A: // ISO 639 Language descriptor - { - TSDDescriptorISO639Language res; - if (TSD_OK == tsd_parse_descriptor_iso639_language( - desc->data, desc->data_length, &res)) { - LOGD("\n"); - int i = 0; - for (; i < res.language_length; ++i) { - LOGD(" ISO Language Code: 0x%08X, audio type: 0x%02x", - res.iso_language_code[i], res.audio_type[i]); - } - LOGD("\n"); - } - } break; - case 0x0E: // Maximum bitrate descriptor - { - TSDDescriptorMaxBitrate res; - if (TSD_OK == tsd_parse_descriptor_max_bitrate( - desc->data, desc->data_length, &res)) { - LOGD(" Maximum Bitrate: %d x 50 bytes/second", res.max_bitrate); - } - } break; - default: { - LOGW(" Unknown Descriptor: 0x%x ", desc->tag); - } break; - } - } - - static void *log_malloc(size_t size) { - void *result = nullptr; -#if defined(ESP32) - result = ps_malloc(size); - if (result != nullptr) return result; -#endif - result = malloc(size); - LOGI("malloc(%d) -> %p %s", (int)size, result, - result != NULL ? "OK" : "ERROR"); - return result; - } - - static void *log_calloc(size_t num, size_t size) { - void *result = nullptr; -#if defined(ESP32) - result = ps_calloc(num, size); - if (result != nullptr) return result; -#endif - result = calloc(num, size); - LOGI("calloc(%d) -> %p %s", (int)(num * size), result, - result != NULL ? "OK" : "ERROR"); - return result; - } - - static void *log_realloc(void *ptr, size_t size) { - void *result = nullptr; -#if defined(ESP32) - result = ps_realloc(ptr, size); - if (result != nullptr) return result; -#endif - result = realloc(ptr, size); - LOGI("realloc(%d) -> %p %s", (int)size, result, - result != NULL ? "OK" : "ERROR"); - return result; - } - - static void log_free(void *mem) { - LOGD("free(%p)", mem); - free(mem); - } - - // // store allocated size in first bytes - // static void* log_malloc (size_t size) { - // void *result = malloc(size); - // memset(result, 0, size); - // AllocSize entry{result, size}; - // self->alloc_vector.push_back(entry); - // assert(find_size(result)>=0); - // LOGI("malloc(%d) -> %p %s\n", (int)size,result, - // result!=NULL?"OK":"ERROR"); return result; - // } - - // static void* log_calloc(size_t num, size_t size){ - // return log_malloc(num*size); - // } - - // static int find_size(void *ptr){ - // for (int j=0;jalloc_vector.size();j++){ - // if (self->alloc_vector[j].data==ptr) return j; - // } - // return -1; - // } - - // static void* log_realloc(void *ptr, size_t size){ - // int pos = find_size(ptr); - // void *result = nullptr; - // if (pos>=0){ - // result = realloc(ptr, size); - // // store size in header - // size_t old_size = self->alloc_vector[pos].size; - // memset(result+old_size, 0, size-old_size); - // self->alloc_vector[pos].size = size; - // } else { - // LOGE("realloc of unallocatd memory %p", ptr); - // result = realloc(ptr, size); - // AllocSize entry{result, size}; - // self->alloc_vector.push_back(entry); - // assert(find_size(result)>=0); - // } - - // LOGI("realloc(%d) -> %p %s\n", (int)size, result, - // result!=NULL?"OK":"ERROR"); return result; - // } - - // static void log_free (void *mem){ - // LOGD("free(%p)\n", mem); - // free(mem); - // int pos = find_size(mem); - // if (pos>=0){ - // self->alloc_vector.erase(pos); - // assert(find_size(mem)==-1); - - // } else { - // LOGE("free of unallocatd memory %p", mem); - // } - // } -}; -// init static variable -MTSDecoderTSDemux *MTSDecoderTSDemux::self = nullptr; - -} // namespace audio_tools \ No newline at end of file diff --git a/src/AudioTools/AudioCodecs/ContainerBinary.h b/src/AudioTools/AudioCodecs/ContainerBinary.h deleted file mode 100644 index 77097a9ec0..0000000000 --- a/src/AudioTools/AudioCodecs/ContainerBinary.h +++ /dev/null @@ -1,407 +0,0 @@ -/** - * @file ContainerBinary.h - * @author Phil Schatzmann - * @brief A lean and efficient container format which provides Header records - * with audio info, Audio records with the audio and Meta which - * can contain any additional information. This can be used together with a - * codec which does not transmit the audio information or has variable frame - * lengths. We expect that a single write() is providing full frames. - * - * @version 0.1 - * @date 2022-05-04 - * - * @copyright Copyright (c) 2022 - * - */ -#pragma once -#include - -#include "AudioTools/AudioCodecs/AudioCodecsBase.h" -#include "AudioTools/CoreAudio/AudioBasic/StrView.h" - -namespace audio_tools { - -enum class ContainerType : uint8_t { - Header = 1, - Audio = 2, - Meta = 3, - Undefined = 0 -}; - -struct CommonHeader { - CommonHeader() = default; - CommonHeader(ContainerType type, uint16_t len) { - this->type = type; - this->len = len; - } - char header[2] = {'\r','\n'}; - ContainerType type; - uint16_t len; - uint8_t checksum = 0; -}; - -struct SimpleContainerConfig { - SimpleContainerConfig() = default; - CommonHeader common{ContainerType::Header, sizeof(AudioInfo)}; - AudioInfo info; -}; - -struct SimpleContainerDataHeader { - CommonHeader common{ContainerType::Audio, 0}; -}; - -struct SimpleContainerMetaDataHeader { - CommonHeader common{ContainerType::Meta, 0}; -}; - -// struct ProcessedResult { -// ContainerType type = ContainerType::Undefined; -// // total length incl header -// int total_len = 0; -// // processed bytes incl header of last step -// int processed = 0; -// // still (total) open -// int open = 0; -// }; - -/// @brief Calculates the checksum -static uint8_t checkSum(const uint8_t *data, size_t len) { - uint8_t result = 0; - for (int j = 0; j < len; j++) { - result ^= data[j]; - } - return result; -} -/// @brief Error types -enum BinaryContainerEncoderError { InvalidHeader, InvalidChecksum, DataMissing}; - -/** - * @brief Wraps the encoded data into Config, Data, and Meta segments so that we - * can recover the audio configuration and orignial segments if this is - * relevant. We assume that a full segment is written with each call of write(); - * The segments are separated with a new line character. - * @ingroup codecs - * @ingroup encoder - * @author Phil Schatzmann - * @copyright GPLv3 - */ -class BinaryContainerEncoder : public AudioEncoder { -public: - BinaryContainerEncoder() = default; - BinaryContainerEncoder(AudioEncoder &encoder) { p_codec = &encoder; } - BinaryContainerEncoder(AudioEncoder *encoder) { p_codec = encoder; } - - void setEncoder(AudioEncoder *encoder) { p_codec = encoder; } - - void setOutput(Print &outStream) { - LOGD("BinaryContainerEncoder::setOutput"); - p_out = &outStream; - } - - bool begin() override { - TRACED(); - // target.begin(); - bool rc = p_codec->begin(); - p_codec->setAudioInfo(cfg.info); - is_beginning = true; - return rc; - } - - void setAudioInfo(AudioInfo info) override { - TRACED(); - if (info != audioInfo()) { - cfg.info = info; - } - } - - AudioInfo audioInfo() override { return cfg.info; } - - /// Adds meta data segment - size_t writeMeta(const uint8_t *data, size_t len) { - LOGD("BinaryContainerEncoder::writeMeta: %d", (int)len); - meta.common.len = len + sizeof(SimpleContainerMetaDataHeader); - uint8_t tmp_array[meta.common.len]; - memcpy(tmp_array, &meta, sizeof(meta)); - memcpy(tmp_array + sizeof(meta), data, len); - output(tmp_array, meta.common.len); - return len; - } - - /// Add data segment. On first write we also add a AudioInfo header - size_t write(const uint8_t *data, size_t len) { - LOGD("BinaryContainerEncoder::write: %d", (int)len); - if (is_beginning) { - writeHeader(); - is_beginning = false; - } - writeAudio((uint8_t *)data, len); - return len; - } - - void end() { p_codec->end(); } - - operator bool() { return true; }; - - virtual const char *mime() { return "audio/binary"; }; - -protected: - uint64_t packet_count = 0; - bool is_beginning = true; - int repeat_header; - SimpleContainerConfig cfg; - SimpleContainerDataHeader dh; - SimpleContainerMetaDataHeader meta; - AudioEncoder *p_codec = nullptr; - Print *p_out = nullptr; - - void writeAudio(const uint8_t *data, size_t len) { - LOGD("writeAudio: %d", (int)len); - // encode data - SingleBuffer tmp_buffer{(int)len}; - QueueStream tmp{tmp_buffer}; - tmp.begin(); - p_codec->setOutput(tmp); - p_codec->write(data, len); - - // output of audio data header - dh.common.len = tmp.available() + sizeof(CommonHeader); - dh.common.checksum = checkSum(tmp_buffer.data(), tmp_buffer.available()); - output((uint8_t *)&dh, sizeof(dh)); - - // output of data - output(tmp_buffer.data(), tmp_buffer.available()); - } - - void writeHeader() { - LOGD("writeHeader"); - output((uint8_t *)&cfg, sizeof(cfg)); - } - - size_t output(const uint8_t *data, size_t len) { - if (p_out != nullptr) { - int written = p_out->write((uint8_t *)data, len); - LOGD("output: %d -> %d", (int)len, written); - } else - LOGW("output not defined"); - return len; - } -}; - -/** - * @brief Decodes the provided data from the DAT and CFG segments - * @ingroup codecs - * @ingroup decoder - * @author Phil Schatzmann - * @copyright GPLv3 - */ -class BinaryContainerDecoder : public ContainerDecoder { -public: - BinaryContainerDecoder() = default; - BinaryContainerDecoder(AudioDecoder &decoder) { p_codec = &decoder; } - BinaryContainerDecoder(AudioDecoder *decoder) { p_codec = decoder; } - - void setDecoder(AudioDecoder *decoder){ - p_codec = decoder; - } - - // Defines the output: this method is called 2 times: first to define - // output defined in the EnocdedAudioStream and then to define the - // real output in the output chain. - void setOutput(Print &outStream) { - LOGD("BinaryContainerDecoder::setOutput") - p_out = &outStream; - } - - void setMetaCallback(void (*callback)(uint8_t*, int, void*)) { - meta_callback = callback; - } - - bool begin() { - TRACED(); - is_first = true; - return true; - } - - void end() { TRACED(); } - - size_t write(const uint8_t *data, size_t len) { - LOGD("write: %d", (int)len); - uint8_t *data8 = (uint8_t *)data; - if (buffer.size() < len) { - buffer.resize( - std::max(static_cast(DEFAULT_BUFFER_SIZE + header_size), - static_cast(len * 4 + header_size))); - } - - size_t result = buffer.writeArray(data8, len); - while (parseBuffer()) - ; - return ignore_write_errors ? len : result; - } - - operator bool() { return true; }; - - void addErrorHandler(void (*error_handler)(BinaryContainerEncoderError error, BinaryContainerDecoder* source, void* ref)){ - this->error_handler = error_handler; - } - - /// If set to true we do not expect a retry to write the missing data but continue just with the next. (Default is true); - void setIgnoreWriteErrors(bool flag){ - ignore_write_errors = flag; - } - - /// Provide additional information for callback - void setReference(void* ref){ - reference = ref; - } - -protected: - bool is_first = true; - CommonHeader header; - const size_t header_size = sizeof(header); - AudioDecoder *p_codec = nullptr; - SingleBuffer buffer{0}; - Print *p_out = nullptr; - void (*meta_callback)(uint8_t* data, int len, void* ref) = nullptr; - void (*error_handler)(BinaryContainerEncoderError error, BinaryContainerDecoder* source, void* ref) = nullptr; - bool ignore_write_errors = true; - void * reference = nullptr; - - - bool parseBuffer() { - LOGD("parseBuffer"); - bool result = false; - - StrView str{(const char *)buffer.data()}; - int start = str.indexOf("\r\n"); - LOGD("start: %d", start); - if (start < 0) { - return false; - } - // get next record - if (buffer.available() - start > sizeof(header)) { - // determine header - memmove((uint8_t *)&header, buffer.data() + start, sizeof(header)); - - // check header - if (!isValidHeader()) { - LOGW("invalid header: %d", header.type); - if (error_handler) error_handler(InvalidHeader, this, reference); - nextRecord(); - return false; - }; - - if (buffer.available() - start >= header.len) { - // move to start of frame - buffer.clearArray(start); - // process frame - result = processData(); - } else { - LOGD("not enough data - available %d / req: %d", buffer.available(), - header.len); - if (error_handler) error_handler(DataMissing, this, reference); - - } - } else { - LOGD("not enough data for header: %d", buffer.available()); - if (error_handler) error_handler(DataMissing, this, reference); - } - return result; - } - - // processes the completed data from the buffer: e.g. writes it - bool processData() { - LOGD("processData"); - bool rc = false; - switch (header.type) { - case ContainerType::Header: { - LOGD("Header"); - SimpleContainerConfig config; - buffer.readArray((uint8_t *)&config, sizeof(config)); - info = config.info; - notifyAudioChange(info); - info.logInfo(); - p_codec->setAudioInfo(info); - p_codec->begin(); - rc = true; - } break; - - case ContainerType::Audio: { - LOGD("Audio"); - buffer.clearArray(sizeof(header)); - int data_len = header.len - header_size; - uint8_t crc = checkSum(buffer.data(), data_len); - if (header.checksum == crc) { - // decode - SingleBuffer tmp_buffer{data_len * 5}; - QueueStream tmp{tmp_buffer}; - tmp.begin(); - p_codec->setOutput(tmp); - p_codec->write(buffer.data(), data_len); - - // output decoded data - output(tmp_buffer.data(), tmp_buffer.available()); - buffer.clearArray(data_len); - } else { - LOGW("invalid checksum"); - if (error_handler) error_handler(InvalidChecksum, this, reference); - // move to next record - nextRecord(); - return false; - } - rc = true; - } break; - - case ContainerType::Meta: { - LOGD("Meta"); - buffer.clearArray(sizeof(header)); - int data_len = header.len - header_size; - if (meta_callback != nullptr) { - meta_callback(buffer.data(), data_len, reference); - } - buffer.clearArray(data_len); - rc = true; - } break; - } - return rc; - } - - bool isValidHeader() { - switch (header.type) { - case ContainerType::Header: - return header.checksum == 0; - case ContainerType::Audio: - return true; - case ContainerType::Meta: - return header.checksum == 0; - } - return false; - } - - uint8_t peekBufferValue(){ - uint8_t byte_value=0; - buffer.peek(byte_value); - return byte_value; - } - - void nextRecord() { - TRACED(); - uint8_t byte_value; - while (buffer.available() && peekBufferValue() != '\n') - buffer.read(byte_value); - } - - // writes the data to the decoder which forwards it to the output; if there - // is no coded we write to the output instead - size_t output(uint8_t *data, size_t len) { - LOGD("output: %d", (int)len); - if (p_out != nullptr) - p_out->write((uint8_t *)data, len); - else - LOGW("output not defined"); - - return len; - } -}; - -} // namespace audio_tools \ No newline at end of file diff --git a/src/AudioTools/AudioCodecs/ContainerM4A.h b/src/AudioTools/AudioCodecs/ContainerM4A.h deleted file mode 100644 index ecc9ea5405..0000000000 --- a/src/AudioTools/AudioCodecs/ContainerM4A.h +++ /dev/null @@ -1,167 +0,0 @@ -#pragma once - -#include "AudioTools/AudioCodecs/AudioCodecsBase.h" -#include "AudioTools/AudioCodecs/M4AAudioDemuxer.h" -#include "AudioTools/AudioCodecs/MultiDecoder.h" - -namespace audio_tools { - -/** - * @brief M4A Demuxer that extracts audio from M4A/MP4 containers. - * The audio is decoded into pcm with the help of the provided decoder. - * format. - * @ingroup codecs - * @author Phil Schatzmann - * @copyright GPLv3 - */ -class ContainerM4A : public ContainerDecoder { - public: - /** - * @brief Default constructor: If no decoder is provided, the - * raw audio data is provided to the defined output. - */ - ContainerM4A() { - demux.setReference(this); - demux.setCallback(decodeAudio); - }; - - /** - * @brief Constructor with decoder. Sets up the demuxer and decoder - * notification. - * @param decoder Reference to a MultiDecoder for PCM output. - */ - ContainerM4A(MultiDecoder& decoder) : ContainerM4A() { setDecoder(decoder); } - - /** - * @brief Set the output stream for decoded or raw audio. - * @param out_stream Output AudioStream. - */ - void setOutput(Print& out_stream) override { - if (p_decoder != nullptr) p_decoder->setOutput(out_stream); - ContainerDecoder::setOutput(out_stream); - } - - /** - * @brief Returns true if the result is PCM (decoder is present). - * @return true if PCM output, false otherwise. - */ - bool isResultPCM() override { return p_decoder != nullptr ? true : false; } - - /** - * @brief Initialize the demuxer and decoder. - * @return true on success. - */ - bool begin() override { - demux.begin(); - if (p_decoder) p_decoder->begin(); - is_active = true; - return true; - } - - /** - * @brief End the demuxer and decoder, releasing resources. - */ - void end() override { - TRACED(); - is_active = false; - is_magic_cookie_processed = false; - if (p_decoder) p_decoder->end(); - } - - /** - * @brief Feed data to the demuxer for parsing. - * @param data Pointer to input data. - * @param len Length of input data. - * @return Number of bytes processed (always len). - */ - size_t write(const uint8_t* data, size_t len) override { - if (is_active == false) return len; - demux.write(data, len); - return len; - } - - /** - * @brief Returns true if the demuxer is active. - * @return true if active, false otherwise. - */ - operator bool() override { return is_active; } - /** - * @brief Sets the buffer to use for sample sizes. - * You can use this to provide a custom buffer that - * does not rely on RAM (e.g a file based buffer or - * one using Redis) - * @param buffer Reference to the buffer to use. - */ - virtual void setSampleSizesBuffer(BaseBuffer& buffer) { - demux.setSampleSizesBuffer(buffer); - } - /** - * @brief Sets the buffer to use for sample sizes. This is currently - * not used! - * @param buffer Reference to the buffer to use. - */ - virtual void setChunkOffsetsBuffer(BaseBuffer& buffer) { - demux.setChunkOffsetsBuffer(buffer); - } - - /** - * @brief Sets the decoder to use for audio frames. - * @param decoder Reference to a MultiDecoder for PCM output. - * @return true if set successfully, false otherwise. - */ - bool setDecoder(MultiDecoder& decoder) { - p_decoder = &decoder; - p_decoder->addNotifyAudioChange(*this); - return true; - } - - protected: - bool is_active = false; ///< True if demuxer is active. - bool is_magic_cookie_processed = - false; ///< True if ALAC magic cookie has been processed. - MultiDecoder* p_decoder = nullptr; ///< Pointer to the MultiDecoder. - M4AAudioDemuxer demux; ///< Internal demuxer instance. - - /** - * @brief Static callback for demuxed audio frames. - * Handles decoder selection and magic cookie for ALAC. - * @param frame The demuxed audio frame. - * @param ref Reference to the ContainerM4A instance. - */ - static void decodeAudio(const M4AAudioDemuxer::Frame& frame, void* ref) { - ContainerM4A* self = static_cast(ref); - if (self->p_decoder == nullptr) { - self->p_print->write(frame.data, frame.size); - return; - } - MultiDecoder& dec = *(self->p_decoder); - const char* current_mime = dec.selectedMime(); - - // select decoder based on mime type - if (!dec.selectDecoder(frame.mime)) { - const char* mime = frame.mime ? frame.mime : "(nullptr)"; - LOGE("No decoder found for mime type: %s", mime); - return; - } - - // for ALAC only: process magic cookie if not done yet - if (StrView(frame.mime) == "audio/alac" && - !self->is_magic_cookie_processed) { - auto& magic_cookie = self->demux.getALACMagicCookie(); - if (magic_cookie.size() > 0) { - if (!dec.setCodecConfig(magic_cookie.data(), magic_cookie.size())) { - LOGE("Failed to set ALAC magic cookie for decoder: %s", - dec.selectedMime()); - } - } - self->is_magic_cookie_processed = true; - } - // write encoded data to decoder - dec.write(frame.data, frame.size); - - // restore the original mime type - dec.selectDecoder(current_mime); - } -}; - -} // namespace audio_tools \ No newline at end of file diff --git a/src/AudioTools/AudioCodecs/ContainerOSC.h b/src/AudioTools/AudioCodecs/ContainerOSC.h deleted file mode 100644 index 74db037e0d..0000000000 --- a/src/AudioTools/AudioCodecs/ContainerOSC.h +++ /dev/null @@ -1,331 +0,0 @@ -/** - * @file ContainerOSC.h - * @author Phil Schatzmann - * @brief A simple container format which uses OSC messages to - * tramsmit Header records with audio info and Audio records with the audio - * data. - * - * @version 0.1 - * @date 2025-05-20 - * - * @copyright Copyright (c) 2022 - * - */ -#pragma once -#include - -#include "AudioTools/AudioCodecs/AudioCodecsBase.h" -#include "AudioTools/AudioCodecs/MultiDecoder.h" -#include "AudioTools/Communication/OSCData.h" -#include "AudioTools/CoreAudio/AudioBasic/StrView.h" - -namespace audio_tools { - -/** - * @brief Wraps the encoded data into OSC info and data segments so that the - * receiver can recover the audio configuration and orignial segments. - * @ingroup codecs - * @ingroup encoder - * @author Phil Schatzmann - * @copyright GPLv3 - */ -class OSCContainerEncoder : public AudioEncoder { - public: - OSCContainerEncoder() = default; - OSCContainerEncoder(AudioEncoder &encoder) { p_codec = &encoder; } - - void setEncoder(AudioEncoder *encoder) { p_codec = encoder; } - - void setOutput(Print &outStream) { p_out = &outStream; } - - bool begin() override { - TRACED(); - if (p_codec == nullptr) return false; - osc_out.setOutput(*p_out); - osc_out.begin(); - p_codec->setOutput(osc_out); - p_codec->setAudioInfo(audioInfo()); - is_active = p_codec->begin(); - writeAudioInfo(audioInfo(), p_codec->mime()); - return is_active; - } - - void setAudioInfo(AudioInfo info) override { - TRACED(); - if (is_active) writeAudioInfo(audioInfo(), p_codec->mime()); - AudioWriter::setAudioInfo(info); - } - - /// Add data segment. On first write we also add a AudioInfo header - size_t write(const uint8_t *data, size_t len) { - LOGD("OSCContainerEncoder::write: %d", (int)len); - if ((repeat_info > 0) && (packet_count % repeat_info == 0)) { - writeAudioInfo(audioInfo(), p_codec->mime()); - } - p_codec->write(data, len); - packet_count++; - return len; - } - - void end() { - p_codec->end(); - is_active = false; - } - - operator bool() { return is_active; }; - - virtual const char *mime() { return "audio/OSC"; }; - - /// Activate/deactivate the sending of the audio info - void setInfoActive(bool flag) { is_send_info_active = flag; } - /// Automatically resend audio info ever nth write. - void setRepeatInfoEvery(int packet_count) { - this->repeat_info = packet_count; - } - - /// Returns the sequence number of the next packet - uint64_t getSequenceNumber() { return osc_out.getSequenceNumber(); } - - /// Define a reference object to be provided by the callback - void setReference(void *ref) { osc_out.setReference(ref); } - - /// Get informed about the encoded packages - void setEncodedWriteCallback(void (*write_callback)(uint8_t *data, size_t len, - uint64_t seq, - void *ref)) { - osc_out.setEncodedWriteCallback(write_callback); - } - - /// Resend the encoded data - size_t resendEncodedData(uint8_t *data, size_t len, uint64_t seq) { - return osc_out.write(data, len, seq); - } - - protected: - uint64_t packet_count = 0; - int repeat_info = 0; - bool is_active = false; - bool is_send_info_active = true; - AudioEncoder *p_codec = nullptr; - Print *p_out = nullptr; - - /// Output Encoded Audio via OSC - class OSCOutput : public AudioOutput { - public: - void setReference(void *ref) { this->ref = ref; } - void setOutput(Print &outStream) { p_out = &outStream; } - void setEncodedWriteCallback(void (*write_callback)( - uint8_t *data, size_t len, uint64_t seq, void *ref)) { - this->encoded_write_callback = write_callback; - } - uint64_t getSequenceNumber() { return sequence_number; } - bool begin() { - sequence_number = 0; - return true; - } - size_t write(const uint8_t *data, size_t len) override { - size_t result = write(data, len); - sequence_number++; - return result; - } - size_t write(const uint8_t *data, size_t len, uint64_t seq) { - LOGD("writeAudio: %d", (int)len); - if (encoded_write_callback != nullptr) { - encoded_write_callback((uint8_t *)data, len, sequence_number, ref); - } - uint8_t osc_data[len + 20]; // 20 is guess to cover address & fmt - OSCData osc{osc_data, sizeof(osc_data)}; - osc.setAddress("/audio/data"); - osc.setFormat("ttb"); - osc.write((uint64_t)millis()); - // we use a uint64_t for a sequence number - osc.write(sequence_number); - osc.write(data, len); - p_out->write(osc_data, osc.size()); - return len; - } - - protected: - void (*encoded_write_callback)(uint8_t *data, size_t len, uint64_t seq, - void *ref) = nullptr; - Print *p_out = nullptr; - uint64_t sequence_number = 0; - void *ref = nullptr; - } osc_out; - - /// OUtput AudioInfo via OSC - void writeAudioInfo(AudioInfo info, const char *mime) { - if (is_send_info_active) { - LOGD("writeAudioInfo"); - uint8_t osc_data[100]; - OSCData osc{osc_data, sizeof(osc_data)}; - osc.setAddress("/audio/info"); - osc.setFormat("iiis"); - osc.write((int32_t)info.sample_rate); - osc.write((int32_t)info.channels); - osc.write((int32_t)info.bits_per_sample); - osc.write(mime); - p_out->write(osc_data, osc.size()); - } - } -}; - -/** - * @brief Decodes the provided data from the OSC segments. I recommend to - * assign a MultiDecoder so that we can support muiltiple audio types. - * @ingroup codecs - * @ingroup decoder - * @author Phil Schatzmann - * @copyright GPLv3 - */ -class OSCContainerDecoder : public ContainerDecoder { - public: - OSCContainerDecoder() = default; - OSCContainerDecoder(AudioDecoder &decoder) { - setDecoder(decoder); - } - OSCContainerDecoder(MultiDecoder &decoder) { - setDecoder(decoder); - } - - /// Defines the decoder to be used - void setDecoder(AudioDecoder &decoder) { p_codec = &decoder; } - - /// Defines the decoder to be used: special logic for multidecoder - void setDecoder(MultiDecoder &decoder) { - p_codec = &decoder; - is_multi_decoder = true; - } - - /// Optionally define you own OSCData object - void setOSCData(OSCData &osc) { p_osc = &osc; } - - void setOutput(Print &outStream) { - LOGD("OSCContainerDecoder::setOutput") - p_out = &outStream; - } - - bool begin() { - TRACED(); - if (p_codec == nullptr || p_osc == nullptr) return false; - p_osc->setReference(this); - p_osc->addCallback("/audio/info", parseInfo, OSCCompare::StartsWith); - p_osc->addCallback("/audio/data", parseData, OSCCompare::StartsWith); - is_active = true; - return true; - } - - void end() { is_active = false; } - - size_t write(const uint8_t *data, size_t len) { - if (!is_active) return 0; - LOGD("write: %d", (int)len); - if (!p_osc->parse((uint8_t *)data, len)) { - return 0; - } - return len; - } - - operator bool() { return is_active; }; - - /// Provides the mime type from the encoder - const char *mime() { return mime_str.c_str(); }; - - /// Provides the sequence number of the last packet - uint64_t getSequenceNumber() { return seq_no; } - - /// Adds an new parser callback for a specific address matching string - bool addParserCallback(const char *address, - bool (*callback)(OSCData &data, void *ref), - OSCCompare compare = OSCCompare::Matches) { - if (p_osc == nullptr) return false; - p_osc->addCallback(address, callback, compare); - return true; - } - - /// Replace the write to the decoder with a callback: - void setWriteCallback(bool (*write_callback)(uint64_t time, uint64_t seq, - uint8_t *data, size_t len, - void *ref)) { - this->write_callback = write_callback; - } - - /// Callback to be called when data is missing - void setMissingDataCallback(void (*missing_data_callback)(uint64_t from_seq, - uint64_t to_seq, - void *ref)) { - this->missing_data_callback = missing_data_callback; - } - - /// Provide a reference object to the callback - void setReference(void *ref) { this->ref = ref; } - - protected: - bool is_active = false; - bool is_multi_decoder = false; - AudioDecoder *p_codec = nullptr; - SingleBuffer buffer{0}; - Print *p_out = nullptr; - OSCData osc_default; - OSCData *p_osc = &osc_default; - Str mime_str; - uint64_t seq_no = 0; - /// Return false to complete the processing w/o writing to the decoder - bool (*write_callback)(uint64_t time, uint64_t seq, uint8_t *data, size_t len, - void *ref) = nullptr; - void (*missing_data_callback)(uint64_t from_seq, uint64_t to_seq, - void *ref) = missingDataCallback; - void *ref = nullptr; - - /// Default callback for missing data: just log the missing range - static void missingDataCallback(uint64_t from_seq, uint64_t to_seq, - void *ref) { - LOGW("Missing sequence numbers %d - %d", from_seq, to_seq); - } - - static bool parseData(OSCData &osc, void *ref) { - uint64_t time = osc.readTime(); - uint64_t seq = osc.readTime(); - OSCBinaryData data = osc.readData(); - OSCContainerDecoder *self = static_cast(ref); - // Check for missing sequence numbers - if (self->seq_no + 1 != seq) { - self->missing_data_callback(self->seq_no + 1, seq - 1, self->ref); - } - // store the actual sequence number - self->seq_no = seq; - // call write callbak if defined - if (self->write_callback != nullptr) { - bool ok = self->write_callback(time, seq, data.data, data.len, ref); - if (!ok) return true; - } - // output to decoder - if (self->p_codec != nullptr) { - self->p_codec->write(data.data, data.len); - } - - return true; - } - - static bool parseInfo(OSCData &osc, void *ref) { - AudioInfo info; - info.sample_rate = osc.readInt32(); - info.channels = osc.readInt32(); - info.bits_per_sample = osc.readInt32(); - const char *mime = osc.readString(); - - OSCContainerDecoder *self = static_cast(ref); - if (self != nullptr) { - self->setAudioInfo(info); - self->mime_str = mime; - LOGI("mime: %s", mime); - // select the right decoder based on the mime type - if (self->is_multi_decoder) - static_cast(self->p_codec)->selectDecoder(mime); - } - - return true; - } -}; - -} // namespace audio_tools \ No newline at end of file diff --git a/src/AudioTools/AudioCodecs/DecoderFromStreaming.h b/src/AudioTools/AudioCodecs/DecoderFromStreaming.h deleted file mode 100644 index d2a3d5da09..0000000000 --- a/src/AudioTools/AudioCodecs/DecoderFromStreaming.h +++ /dev/null @@ -1,93 +0,0 @@ -#pragma once - -#include "AudioTools/AudioCodecs/AudioCodecsBase.h" -#include "AudioTools/CoreAudio/Buffers.h" -//#include "AudioTools/CoreAudio/AudioStreams.h" - -namespace audio_tools { - -/** - * @brief Adapter class which allows the AudioDecoder API on a StreamingDecoder - * @ingroup codecs - * @ingroup decoder - * @author Phil Schatzmann - * @copyright GPLv3 - */ -class DecoderAdapter : public AudioDecoder { -public: - /// Default Constructor - DecoderAdapter(StreamingDecoder &dec, int bufferSize) { - TRACED(); - p_dec = &dec; - p_dec->setInput(queue); - resize(bufferSize); - } - - /// Defines the output Stream - void setOutput(Print &out) override { - p_dec->setOutput(out); - } - - void setInput(Stream& in){ - p_dec->setInput(in); - } - - bool begin() override { - TRACED(); - active = true; - bool rc = p_dec->begin(); - return rc; - } - - void end() override { - TRACED(); - active = false; - } - - /// resize the buffer - void resize(int size){ - buffer_size = size; - // setup the buffer only if needed - if (is_setup) rbuffer.resize(size); - } - - size_t write(const uint8_t *data, size_t len) override { - TRACED(); - setupLazy(); - size_t result = queue.write((uint8_t *)data, len); - // trigger processing - we leave byteCount in the buffer - // while(queue.available()>byteCount){ - // p_dec->copy(); - // delay(1); - // } - while(p_dec->copy()); - - return result; - } - - StreamingDecoder* getStreamingDecoder(){ - return p_dec; - } - - operator bool() override { return active; } - -protected: - bool active = false; - bool is_setup = false; - int buffer_size; - StreamingDecoder *p_dec = nullptr; - RingBuffer rbuffer{0}; - QueueStream queue{rbuffer}; // convert Buffer to Stream - - void setupLazy(){ - if (!is_setup){ - rbuffer.resize(buffer_size); - queue.begin(); - is_setup = true; - } - } -}; - -using DecoderFromStreaming = DecoderAdapter; - -} // namespace audio_tools \ No newline at end of file diff --git a/src/AudioTools/AudioCodecs/HeaderParserAAC.h b/src/AudioTools/AudioCodecs/HeaderParserAAC.h deleted file mode 100644 index f24b2ebc31..0000000000 --- a/src/AudioTools/AudioCodecs/HeaderParserAAC.h +++ /dev/null @@ -1,51 +0,0 @@ -#pragma once -#include "AudioTools/AudioCodecs/CodecADTS.h" -#include "AudioTools/CoreAudio/AudioBasic/StrView.h" - -namespace audio_tools { - -/** - * @brief AAC header parser to check if the data is a valid ADTS aac which - * can extract some relevant audio information. - * @ingroup codecs - * @ingroup decoder - * @author Phil Schatzmann - * @copyright GPLv3 - */ - -class HeaderParserAAC { - public: - /// parses the header string and returns true if this is a valid aac adts - /// stream - bool isValid(const uint8_t* data, int len) { - if (len < 7) return false; - parser.begin(); - // regular validation - if (!parser.parse((uint8_t*)data)) return false; - // check if we have a valid 2nd frame - if (len > getFrameLength()) { - int pos = findSyncWord(data, len, getFrameLength()); - if (pos == -1) return false; - } - return true; - } - - int getSampleRate() { return parser.getSampleRate(); } - - uint8_t getChannels() { return parser.data().channel_cfg; } - - /// Determines the frame length - int getFrameLength() { return parser.getFrameLength(); } - - /// Finds the mp3/aac sync word - int findSyncWord(const uint8_t* buf, int nBytes, int start = 0) { - return parser.findSyncWord(buf, nBytes, start); - } - - ADTSParser::ADTSHeader getHeader() { return parser.data(); } - - protected: - ADTSParser parser; -}; - -} // namespace audio_tools \ No newline at end of file diff --git a/src/AudioTools/AudioCodecs/HeaderParserMP3.h b/src/AudioTools/AudioCodecs/HeaderParserMP3.h deleted file mode 100644 index dc2b4a5687..0000000000 --- a/src/AudioTools/AudioCodecs/HeaderParserMP3.h +++ /dev/null @@ -1,426 +0,0 @@ -#pragma once -#include "AudioTools/CoreAudio/AudioBasic/StrView.h" - -namespace audio_tools { - -/** - * @brief MP3 header parser to check if the data is a valid mp3 and - * to extract some relevant audio information. We try to find some valid - * frames with a valid sync in the beginning and the end. - * See https://www.codeproject.com/KB/audio-video/mpegaudioinfo.aspx - * @ingroup codecs - * @ingroup decoder - * @author Phil Schatzmann - * @copyright GPLv3 - */ - -class HeaderParserMP3 { - // MPEG audio frame header - // variables are declared in their serialized order - // includes crc value - struct __attribute__((packed)) FrameHeader { - static const unsigned int SERIALIZED_SIZE = 4; - - // bitmasks for frame header fields grouped by byte - static const unsigned char FRAMESYNC_FIRST_BYTEMASK = 0b11111111; - - static const unsigned char FRAMESYNC_SECOND_BYTEMASK = 0b1110000; - static const unsigned char AUDIO_VERSION_MASK = 0b00011000; - static const unsigned char LAYER_DESCRIPTION_MASK = 0b00000110; - static const unsigned char PROTECTION_BIT_MASK = 0b00000001; - - static const unsigned char BITRATE_INDEX_MASK = 0b11110000; - static const unsigned char SAMPLERATE_INDEX_MASK = 0b00001100; - static const unsigned char PADDING_BIT_MASK = 0b00000010; - static const unsigned char PRIVATE_BIT_MASK = 0b00000001; - - static const unsigned char CHANNEL_MODE_MASK = 0b11000000; - static const unsigned char MODE_EXTENTION_MASK = 0b00110000; - static const unsigned char COPYRIGHT_BIT_MASK = 0b00001000; - static const unsigned char ORIGINAL_BIT_MASK = 0b00000100; - static const unsigned char EMPHASIS_MASK = 0b00000011; - - char FrameSyncByte; - bool FrameSyncBits : 3; - - // indicates MPEG standard version - enum class AudioVersionID : unsigned { - MPEG_2_5 = 0b00, - INVALID = 0b01, // reserved - MPEG_2 = 0b10, - MPEG_1 = 0b11, - } AudioVersion : 2; - - // indicates which audio layer of the MPEG standard - enum class LayerID : unsigned { - INVALID = 0b00, // reserved - LAYER_3 = 0b01, - LAYER_2 = 0b10, - LAYER_1 = 0b11, - } Layer : 2; - - // indicates whether theres a 16 bit crc checksum following the header - bool Protection : 1; - - // sample & bitrate indexes meaning differ depending on MPEG version - // use getBitRate() and GetSamplerate() - bool BitrateIndex : 4; - bool SampleRateIndex : 2; - - // indicates whether the audio data is padded with 1 extra byte (slot) - bool Padding : 1; - - // this is only informative - bool Private : 1; - - // indicates channel mode - enum class ChannelModeID : unsigned { - STEREO = 0b00, - JOINT = 0b01, // joint stereo - DUAL = 0b10, // dual channel (2 mono channels) - SINGLE = 0b11, // single channel (mono) - } ChannelMode : 2; - - // Only used in joint channel mode. Meaning differ depending on audio layer - // Use GetExtentionMode() - bool ExtentionMode : 2; - - // indicates whether the audio is copyrighted - bool Copyright : 1; - - // indicates whether the frame is located on the original media or a copy - bool Original : 1; - - // indicates to the decoder that the file must be de-emphasized, ie the - // decoder must 're-equalize' the sound after a Dolby-like noise supression. - // It is rarely used. - enum class EmphasisID : unsigned { - NONE = 0b00, - MS_50_15 = 0b01, - INVALID = 0b10, - CCIT_J17 = 0b10, - } Emphasis : 2; - - enum SpecialBitrate { - INVALID = -8000, - ANY = 0, - }; - - signed int getBitRate() const { - // version, layer, bit index - static signed char rateTable[4][4][16] = { - // version[00] = MPEG_2_5 - { - // layer[00] = INVALID - {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, - // layer[01] = LAYER_3 - {0, 1, 2, 3, 4, 5, 6, 7, 8, 10, 12, 14, 16, 18, 20, -1}, - // layer[10] = LAYER_2 - {0, 1, 2, 3, 4, 5, 6, 7, 8, 10, 12, 14, 16, 18, 20, -1}, - // layer[11] = LAYER_1 - {0, 4, 6, 7, 8, 10, 12, 14, 16, 18, 20, 22, 24, 28, 32, -1}, - }, - - // version[01] = INVALID - { - {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, - {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, - {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, - {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, - }, - - // version[10] = MPEG_2 - { - // layer[00] = INVALID - {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, - // layer[01] = LAYER_3 - {0, 1, 2, 3, 4, 5, 6, 7, 8, 10, 12, 14, 16, 18, 20, -1}, - // layer[10] = LAYER_2 - {0, 1, 2, 3, 4, 5, 6, 7, 8, 10, 12, 14, 16, 18, 20, -1}, - // layer[11] = LAYER_1 - {0, 4, 6, 7, 8, 10, 12, 14, 16, 18, 20, 22, 24, 28, 32, -1}, - }, - - // version[11] = MPEG_1 - { - // layer[00] = INVALID - {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, - // layer[01] = LAYER_3 - {0, 4, 5, 6, 7, 8, 10, 12, 14, 16, 20, 24, 28, 32, 40, -1}, - // layer[10] = LAYER_2 - {0, 4, 6, 7, 8, 10, 12, 14, 16, 20, 24, 28, 32, 40, 48, -1}, - // layer[11] = LAYER_1 - {0, 4, 8, 12, 16, 20, 24, 28, 32, 36, 40, 44, 48, 52, 56, -1}, - }, - }; - signed char rate_byte = rateTable[(int)AudioVersion][(int)Layer][(int)BitrateIndex]; - if (rate_byte == -1) { - LOGE("Unsupported bitrate"); - return 0; - } - return rate_byte * 8000; - } - - enum SpecialSampleRate { - RESERVED = 0, - }; - - unsigned short getSampleRate() const { - // version, sample rate index - static unsigned short rateTable[4][4] = { - // version[00] = MPEG_2_5 - {11025, 12000, 8000, 0}, - // version[01] = INVALID - {0, 0, 0, 0}, - // version[10] = MPEG_2 - {22050, 24000, 16000, 0}, - // version[11] = MPEG_1 - {44100, 48000, 32000, 0}, - }; - - return rateTable[(int)AudioVersion][(int)SampleRateIndex]; - } - - int getFrameLength() { - int sample_rate = getSampleRate(); - if (sample_rate == 0) return 0; - return int((144 * getBitRate() / sample_rate) + Padding); - } - }; - - public: - /// parses the header string and returns true if this is a valid mp3 file - bool isValid(const uint8_t* data, int len) { - memset(&header, 0, sizeof(header)); - - // if we start with ID3 -> valid mp3 - if (memcmp(data, "ID3", 3) == 0) { - LOGI("ID3 found"); - return true; - } - - int sync_pos = seekFrameSync(data, len); - if (sync_pos == -1) { - LOGE("Could not find FrameSync"); - return false; - } - - // xing header -> valid mp3 - if (sync_pos >= 0 && contains(data, "Xing", len)) { - LOGI("Xing found"); - return true; - } - - // xing header -> valid mp3 - if (sync_pos >= 0 && contains(data, "Info", len)) { - LOGI("Xing Info found"); - return true; - } - - // find valid segement in available data - bool is_valid_mp3 = false; - while (true) { - LOGI("checking header at %d", sync_pos); - int len_available = len - sync_pos; - - // check if we have enough data for header - if (len_available < sizeof(header)) { - LOGE("Not enough data to determine mp3 header"); - break; - } - - readFrameHeader(data + sync_pos); - is_valid_mp3 = validate(data + sync_pos, len_available); - - // check expected expected end of frame ( next frame) - int frame_len = getFrameLength(); - if (is_valid_mp3 && frame_len > 0) { - int expected_next_frame = sync_pos + getFrameLength(); - int pos = seekFrameSync(data + expected_next_frame, - len - expected_next_frame); - LOGI("- end frame found: %s", pos == 0 ? "yes" : "no"); - if (pos != 0) is_valid_mp3 = false; - } - - // find end sync - int pos = seekFrameSync(data + sync_pos + 2, len_available - 2); - // no more data to be validated - if (pos == -1) break; - // calculate new sync_pos - sync_pos = pos + sync_pos + 2; - - // success and we found an end sync with a bit rate - if (is_valid_mp3 && getSampleRate() != 0) break; - } - if (is_valid_mp3) { - LOGI("-------------------"); - LOGI("is mp3: %s", is_valid_mp3 ? "yes" : "no"); - LOGI("frame size: %d", getFrameLength()); - LOGI("sample rate: %u", getSampleRate()); - // LOGI("bit rate index: %d", getFrameHeader().BitrateIndex); - LOGI("bit rate: %d", getBitRate()); - LOGI("Padding: %d", getFrameHeader().Padding); - LOGI("Layer: %s (0x%x)", getLayerStr(), (int)getFrameHeader().Layer); - LOGI("Version: %s (0x%x)", getVersionStr(), - (int)getFrameHeader().AudioVersion); - LOGI("-------------------"); - } - return is_valid_mp3; - } - - uint16_t getSampleRate() const { return header.getSampleRate(); } - - int getBitRate() const { return header.getBitRate(); } - - /// Determines the frame length - int getFrameLength() { return header.getFrameLength(); } - - /// Provides the estimated playing time in seconds based on the bitrate of the - /// first segment - size_t getPlayingTime(size_t fileSizeBytes) { - int bitrate = getBitRate(); - if (bitrate == 0) return 0; - return fileSizeBytes / bitrate; - } - - const char* getVersionStr() const { - return header.AudioVersion == FrameHeader::AudioVersionID::MPEG_1 ? "1" - : header.AudioVersion == FrameHeader::AudioVersionID::MPEG_2 ? "2" - : header.AudioVersion == FrameHeader::AudioVersionID::MPEG_2_5 - ? "2.5" - : "INVALID"; - } - - const char* getLayerStr() const { - return header.Layer == FrameHeader::LayerID::LAYER_1 ? "1" - : header.Layer == FrameHeader::LayerID::LAYER_2 ? "2" - : header.Layer == FrameHeader::LayerID::LAYER_3 ? "3" - : "INVALID"; - } - - // provides the parsed MP3 frame header - FrameHeader getFrameHeader() { return header; } - - /// Finds the mp3/aac sync word - int findSyncWord(const uint8_t* buf, size_t nBytes, uint8_t synch = 0xFF, - uint8_t syncl = 0xF0) { - for (int i = 0; i < nBytes - 1; i++) { - if ((buf[i + 0] & synch) == synch && (buf[i + 1] & syncl) == syncl) - return i; - } - return -1; - } - - protected: - FrameHeader header; - - bool validate(const uint8_t* data, size_t len) { - assert(header.FrameSyncByte = 0xFF); - // check end of frame: it must contains a sync word - return FrameReason::VALID == validateFrameHeader(header); - } - - bool contains(const uint8_t* data, const char* toFind, size_t len) { - if (data == nullptr || len == 0) return false; - int find_str_len = strlen(toFind); - for (int j = 0; j < len - find_str_len; j++) { - if (memcmp(data + j, toFind, find_str_len) == 0) return true; - } - return false; - } - - // Seeks to the byte at the end of the next continuous run of 11 set bits. - //(ie. after seeking the cursor will be on the byte of which its 3 most - // significant bits are part of the frame sync) - int seekFrameSync(const uint8_t* str, size_t len) { - char cur; - for (int j = 0; j < len - 1; j++) { - cur = str[j]; - // read bytes until EOF or a byte with all bits set is encountered - if ((cur & 0b11111111) != 0b11111111) continue; - - if ((str[j + 1] & 0b11100000) != 0b11100000) { - // if the next byte does not have its 3 most significant bits set it is - // not the end of the framesync and it also cannot be the start of a - // framesync so just skip over it here without the check - continue; - } - return j; - } - - return -1; - } - - void readFrameHeader(const uint8_t* data) { - assert(data[0] == 0xFF); - assert((data[1] & 0b11100000) == 0b11100000); - - memcpy(&header, data, sizeof(header)); - - LOGI("- sample rate: %u", getSampleRate()); - LOGI("- bit rate: %d", getBitRate()); - } - - enum class FrameReason { - VALID, - INVALID_BITRATE_FOR_VERSION, - INVALID_SAMPLERATE_FOR_VERSION, - INVALID_MPEG_VERSION, - INVALID_LAYER, - INVALID_LAYER_II_BITRATE_AND_MODE, - INVALID_EMPHASIS, - INVALID_CRC, - }; - - FrameReason validateFrameHeader(const FrameHeader& header) { - if (header.AudioVersion == FrameHeader::AudioVersionID::INVALID) { - LOGI("invalid mpeg version"); - return FrameReason::INVALID_MPEG_VERSION; - } - - if (header.Layer == FrameHeader::LayerID::INVALID) { - LOGI("invalid layer"); - return FrameReason::INVALID_LAYER; - } - - if (header.getBitRate() == FrameHeader::SpecialBitrate::INVALID) { - LOGI("invalid bitrate"); - return FrameReason::INVALID_BITRATE_FOR_VERSION; - } - - if (header.getSampleRate() == FrameHeader::SpecialSampleRate::RESERVED) { - LOGI("invalid samplerate"); - return FrameReason::INVALID_SAMPLERATE_FOR_VERSION; - } - - // For Layer II there are some combinations of bitrate and mode which are - // not allowed - if (header.Layer == FrameHeader::LayerID::LAYER_2) { - if (header.ChannelMode == FrameHeader::ChannelModeID::SINGLE) { - if (header.getBitRate() >= 224000) { - LOGI("invalid bitrate >224000"); - return FrameReason::INVALID_LAYER_II_BITRATE_AND_MODE; - } - } else { - if (header.getBitRate() >= 32000 && header.getBitRate() <= 56000) { - LOGI("invalid bitrate >32000"); - return FrameReason::INVALID_LAYER_II_BITRATE_AND_MODE; - } - - if (header.getBitRate() == 80000) { - LOGI("invalid bitrate >80000"); - return FrameReason::INVALID_LAYER_II_BITRATE_AND_MODE; - } - } - } - - if (header.Emphasis == FrameHeader::EmphasisID::INVALID) { - LOGI("invalid Emphasis"); - return FrameReason::INVALID_EMPHASIS; - } - - return FrameReason::VALID; - } -}; - -} // namespace audio_tools \ No newline at end of file diff --git a/src/AudioTools/AudioCodecs/M4AAudioDemuxer.h b/src/AudioTools/AudioCodecs/M4AAudioDemuxer.h deleted file mode 100644 index 29500bef9d..0000000000 --- a/src/AudioTools/AudioCodecs/M4AAudioDemuxer.h +++ /dev/null @@ -1,220 +0,0 @@ -#pragma once - -#include "AudioTools/AudioCodecs/M4ACommonDemuxer.h" - -namespace audio_tools { - -/** - * @brief A simple M4A audio data demuxer which is providing - * AAC, MP3 and ALAC frames. - */ -class M4AAudioDemuxer : public M4ACommonDemuxer { - public: - /** - * @brief Constructor. Sets up parser callbacks. - */ - M4AAudioDemuxer() { setupParser(); } - - /** - * @brief Defines the callback that returns the audio frames. - * @param cb Frame callback function. - */ - void setCallback(FrameCallback cb) override { - sampleExtractor.setReference(ref); - sampleExtractor.setCallback(cb); - } - - /** - * @brief Initializes the demuxer and resets state. - */ - bool begin() { - codec = Codec::Unknown; - alacMagicCookie.clear(); - resize(default_size); - - stsz_processed = false; - stco_processed = false; - - // When codec/sampleSizes/callback/ref change, update the extractor: - parser.begin(); - sampleExtractor.begin(); - return true; - } - - /** - * @brief Writes data to the demuxer for parsing. - * @param data Pointer to input data. - * @param len Length of input data. - */ - void write(const uint8_t* data, size_t len) { parser.write(data, len); } - - /** - * @brief Returns the available space for writing. - * @return Number of bytes available for writing. - */ - int availableForWrite() { return parser.availableForWrite(); } - - /** - * @brief Returns the ALAC magic cookie (codec config). - * @return Reference to the ALAC magic cookie vector. - */ - Vector& getALACMagicCookie() { return alacMagicCookie; } - - /** - * @brief Sets a reference pointer for callbacks. - * @param ref Reference pointer. - */ - void setReference(void* ref) { this->ref = ref; } - - /** - * @brief Resizes the internal buffer. - * @param size New buffer size. - */ - void resize(int size) { - default_size = size; - if (buffer.size() < size) { - buffer.resize(size); - } - } - - protected: - SingleBuffer buffer; ///< Buffer for incremental data. - void* ref = nullptr; ///< Reference pointer for callbacks. - size_t default_size = 2 * 1024; ///< Default buffer size. - - /** - * @brief Setup all parser callbacks - */ - virtual void setupParser() { - // global box data callback to get sizes - parser.setReference(this); - parser.setCallback(boxDataSetupCallback); - - // incremental data callback - parser.setIncrementalDataCallback(incrementalBoxDataCallback); - - // Register a specific incremental data callback for mdat - parser.setIncrementalDataCallback( - "mdat", - [](MP4Parser::Box& box, const uint8_t* data, size_t len, bool is_final, - void* ref) { - auto* self = static_cast(ref); - LOGI("*Box: %s, size: %u bytes", box.type, (unsigned)len); - self->sampleExtractor.write(data, len, is_final); - }, - false); - - // parsing for content of stsd (Sample Description Box) - parser.setCallback("esds", [](MP4Parser::Box& box, void* ref) { - static_cast(ref)->onEsds(box); - }); - parser.setCallback("mp4a", [](MP4Parser::Box& box, void* ref) { - static_cast(ref)->onMp4a(box); - }); - parser.setCallback("alac", [](MP4Parser::Box& box, void* ref) { - static_cast(ref)->onAlac(box); - }); - parser.setCallback( - "mdat", - [](MP4Parser::Box& box, void* ref) { - M4AAudioDemuxer& self = *static_cast(ref); - // mdat must not be buffered - LOGI("Box: %s, size: %u bytes", box.type, (unsigned)box.size); - self.sampleExtractor.setMaxSize(box.size); - }, - false); // 'false' prevents the generic callback from being executed - } - - /** - * @brief Checks if a box type is relevant for audio demuxing. - * @param type Box type string. - * @return True if relevant, false otherwise. - */ - static bool isRelevantBox(const char* type) { - // Check if the box is relevant for audio demuxing - return (StrView(type) == "stsd" || StrView(type) == "stsz" || - StrView(type) == "stco"); - } - - /** - * @brief Callback for box data setup. If we contain data we add - * it to the buffer. If there is no data we set up the buffer to - * receive incremental data. - * @param box MP4 box. - * @param ref Reference pointer. - */ - static void boxDataSetupCallback(MP4Parser::Box& box, void* ref) { - M4AAudioDemuxer& self = *static_cast(ref); - - bool is_relevant = isRelevantBox(box.type); - if (is_relevant) { - LOGI("Box: %s, size: %u bytes", box.type, (unsigned)box.size); - if (box.data_size == 0) { - // setup for incremental processing - self.resize(box.size); - self.buffer.clear(); - } else { - // we have the complete box data - self.processBox(box); - } - } - } - - /** - * @brief Callback for incremental box data. - * @param box MP4 box. - * @param data Pointer to data. - * @param len Length of data. - * @param is_final True if this is the last chunk. - * @param ref Reference pointer. - */ - static void incrementalBoxDataCallback(MP4Parser::Box& box, - const uint8_t* data, size_t len, - bool is_final, void* ref) { - M4AAudioDemuxer& self = *static_cast(ref); - - // mdat is now handled by its specific incremental callback, so remove logic - // here - - // only process relevant boxes - if (!isRelevantBox(box.type)) return; - - LOGI("*Box: %s, size: %u bytes", box.type, (unsigned)len); - - // others fill buffer incrementally - if (len > 0) { - size_t written = self.buffer.writeArray(data, len); - if (written != len) { - LOGE("Failed to write all data to buffer, written: %zu, expected: %zu", - written, len); - } - } - - // on last chunk, call the specific box handler - if (is_final) { - MP4Parser::Box complete_box = box; - complete_box.data = self.buffer.data(); - complete_box.data_size = self.buffer.size(); - self.processBox(complete_box); - // The buffer might be quite large, so we resize it to the default size - self.resize(self.default_size); - } - } - - /** - * @brief Processes a parsed MP4 box. - * @param box MP4 box. - */ - void processBox(MP4Parser::Box& box) { - if (StrView(box.type) == "stsd") { - onStsd(box); - } else if (StrView(box.type) == "stsz") { - onStsz(box); - } else if (StrView(box.type) == "stco") { - // onStco(box); // currently not supported - } - } - -}; - -} // namespace audio_tools \ No newline at end of file diff --git a/src/AudioTools/AudioCodecs/M4AAudioFileDemuxer.h b/src/AudioTools/AudioCodecs/M4AAudioFileDemuxer.h deleted file mode 100644 index 6c680384bc..0000000000 --- a/src/AudioTools/AudioCodecs/M4AAudioFileDemuxer.h +++ /dev/null @@ -1,304 +0,0 @@ -#pragma once - -#include -#include - -#include "M4AAudioDemuxer.h" - -namespace audio_tools { - -/** - * @brief Demuxer for M4A/MP4 files to extract audio data using an Arduino File. - * This class locates the mdat and stsz boxes using MP4Parser. - * - * It provides a copy() method to extract frames from the file by reading - * sample sizes directly from the stsz box in the file. This class is quite - * memory efficient because no table of sample sizes are kept in memory. It just - * reads the sample sizes from the stsz box and uses the mdat offset to read the - * sample data directly from the file. - * - * The result will either be provided via the frame_callback or - * can be processed by the user. - * - * @author Phil Schatzmann - */ -class M4AAudioFileDemuxer : public M4ACommonDemuxer { - public: - using M4ACommonDemuxer::Frame; - using M4ACommonDemuxer::FrameCallback; - - /** - * @brief Default constructor. Sets up parser callbacks. - */ - M4AAudioFileDemuxer() { setupParser(); }; - - /** - * @brief Constructor with decoder. - * @param decoder Reference to MultiDecoder. - */ - M4AAudioFileDemuxer(MultiDecoder& decoder) : M4AAudioFileDemuxer() { - setDecoder(decoder); - } - - /** - * @brief Sets the decoder to use for audio frames. Please note that - * calls setCallback() to register the decoder callback. - * @param decoder Reference to MultiDecoder. - * @return true if set successfully. - */ - bool setDecoder(MultiDecoder& decoder) { - this->p_decoder = &decoder; - if (decoder.getOutput() == nullptr) { - LOGE("No output defined for MultiDecoder"); - return false; - } - setCallback([&decoder](const Frame& frame, void* /*ref*/) { - LOGI("Decoding frame: %s with %d bytes", frame.mime, (int)frame.size); - if (!decoder.selectDecoder(frame.mime)){ - LOGE("Failed to select decoder for %s", frame.mime); - return; - } - decoder.write(frame.data, frame.size); - }); - return true; - } - - /** - * @brief Sets the callback for extracted audio frames. - * @param cb Frame callback function. - */ - void setCallback(FrameCallback cb) override { frame_callback = cb; } - - /** - * @brief Sets the size of the samples buffer (in bytes). - * @param size Buffer size in bytes. - */ - void setSamplesBufferSize(int size) { - stsz_bufsize = size / 4; - stsz_buf.resize(stsz_bufsize); - } - - /** - * @brief Open and parse the given file. - * @param file Reference to an open Arduino File object. - * @return true on success, false on failure. - */ - bool begin(File& file) { - this->file = &file; - parser.begin(); - if (!file) return false; - end(); - if (p_decoder) p_decoder->begin(); - if (!parseFile(file)) return false; - if (!readStszHeader()) return false; - if (!checkMdat()) return false; - mdat_sample_pos = mdat_offset + mdat_pos; - - return true; - } - - /** - * @brief End demuxing and reset state. - */ - void end() { - codec = M4ACommonDemuxer::Codec::Unknown; - alacMagicCookie.clear(); - // resize(default_size); - sample_index = 0; - mdat_pos = 0; - stsd_processed = false; - mdat_offset = 0; - mdat_size = 0; - stsz_offset = 0; - stsz_size = 0; - sample_index = 0; - mdat_pos = 0; - sample_count = 0; - fixed_sample_size = 0; - } - - /** - * @brief Copies the next audio frame from the file using the sample size - * table and mdat offset. Calls the frame callback if set. - * @return true if a frame was copied and callback called, false if end of - * samples or error. - */ - bool copy() { - if (!file || sample_index >= sample_count) return false; - size_t currentSize = getNextSampleSize(); - if (currentSize == 0) return false; - if (!file->seek(mdat_sample_pos)) return false; - if (buffer.size() < currentSize) buffer.resize(currentSize); - size_t bytesRead = file->read(buffer.data(), currentSize); - if (bytesRead != currentSize) return false; - buffer.setWritePos(bytesRead); - executeCallback(currentSize, buffer); - mdat_sample_pos += currentSize; - return true; - } - - protected: - File* file = nullptr; ///< Pointer to the open file - uint64_t mdat_offset = 0; ///< Offset of mdat box payload - uint64_t mdat_size = 0; ///< Size of mdat box payload - uint64_t stsz_offset = 0; ///< Offset of stsz box - uint64_t stsz_size = 0; ///< Size of stsz box - size_t sample_index = 0; ///< Current sample index - uint64_t mdat_pos = 0; ///< Current position in mdat box - SingleBuffer buffer; ///< Buffer for sample data - int stsz_bufsize = 256; ///< Number of sample sizes to buffer - SingleBuffer stsz_buf{ - stsz_bufsize}; ///< Buffer for stsz sample sizes - uint32_t sample_count = 0; ///< Number of samples in stsz - uint32_t fixed_sample_size = 0; ///< Fixed sample size (if nonzero) - bool stsd_processed = false; ///< True if stsd box processed - MultiDecoder* p_decoder = nullptr; ///< Pointer to decoder -uint64_t mdat_sample_pos = 0; - /** - * @brief Sets up the MP4 parser and registers box callbacks. - */ - void setupParser() { - parser.setReference(this); - - // Callback for ESDS box (AAC config) - parser.setCallback( - "esds", - [](MP4Parser::Box& box, void* ref) { - static_cast(ref)->onEsds(box); - }, - false); - - // Callback for MP4A box (AAC sample entry) - parser.setCallback( - "mp4a", - [](MP4Parser::Box& box, void* ref) { - static_cast(ref)->onMp4a(box); - }, - false); - - // Callback for ALAC box (ALAC sample entry) - parser.setCallback( - "alac", - [](MP4Parser::Box& box, void* ref) { - static_cast(ref)->onAlac(box); - }, - false); - - // Callback for STSZ box (sample sizes) - parser.setCallback( - "stsz", - [](MP4Parser::Box& box, void* ref) { - auto* self = static_cast(ref); - self->stsz_offset = box.file_offset; - self->stsz_size = box.size; - }, - false); - - // Callback for MDAT box (media data) - parser.setCallback( - "mdat", - [](MP4Parser::Box& box, void* ref) { - auto* self = static_cast(ref); - self->mdat_offset = box.file_offset + 8; // skip box header - self->mdat_size = box.size; - }, - false); - - // Callback for STSD box (sample description) - parser.setCallback( - "stsd", - [](MP4Parser::Box& box, void* ref) { - auto* self = static_cast(ref); - self->onStsd(box); // for aac and alac - self->stsd_processed = true; - }, - false); - } - - /** - * @brief Parses the file and feeds data to the parser until we have - * all the necessary data: 1) stsd box processed, 2) mdat offset found, - * 3) stsz offset found. - * @param file Reference to the file to parse. - */ - bool parseFile(File& file) { - uint8_t buffer[1024]; - file.seek(0); - while (file.available()) { - int to_read = min(sizeof(buffer), parser.availableForWrite()); - size_t len = file.read(buffer, to_read); - parser.write(buffer, len); - // stop if we have all the data - if (stsd_processed && mdat_offset && stsz_offset) return true; - } - return false; - } - - /** - * @brief Executes the callback for a completed frame. - * @param size Size of the frame. - * @param buffer Buffer containing the frame data. - */ - void executeCallback(size_t size, SingleBuffer& buffer) { - Frame frame = sampleExtractor.getFrame(size, buffer); - if (frame_callback) - frame_callback(frame, nullptr); - else - LOGW("No frame callback defined"); - } - - /** - * @brief Provides the next sample size from the stsz box. - * @return stsz sample size in bytes. - */ - uint32_t getNextSampleSize() { - if (sample_index >= sample_count) return 0; - uint32_t currentSize = 0; - if (fixed_sample_size) { - currentSize = fixed_sample_size; - } else { - // if buffer is empty, fill it again - if (stsz_buf.isEmpty()) { - uint64_t pos = stsz_offset + 20 + sample_index * 4; - if (!file->seek(pos)) return false; - stsz_buf.clear(); - size_t read_bytes = file->read( - reinterpret_cast(stsz_buf.data()), stsz_bufsize * 4); - stsz_buf.setWritePos(read_bytes / 4); - if (stsz_buf.isEmpty()) return 0; - } - // provide next size - uint32_t val = 0; - if (!stsz_buf.read(val)) return 0; - currentSize = readU32(val); - } - sample_index++; - return currentSize; - } - - /** - * @brief Reads the stsz header (sample count and fixed sample size) from - * the file. - * @return true if successful, false otherwise. - */ - bool readStszHeader() { - if (!file || stsz_offset == 0) return false; - uint8_t buffer[20]; - if (!file->seek(stsz_offset)) return false; - if (file->read(buffer, 20) != 20) return false; - if (!checkType(buffer, "stsz", 4)) return false; - uint8_t* cont = buffer + 8; - fixed_sample_size = readU32(cont + 4); - sample_count = readU32(cont + 8); - return true; - } - - bool checkMdat() { - file->seek(mdat_offset - 8); - uint8_t buffer[8]; - if (file->read(buffer, 8) != 8) return false; - return checkType(buffer, "mdat", 4); - } -}; - -} // namespace audio_tools \ No newline at end of file diff --git a/src/AudioTools/AudioCodecs/M4ACommonDemuxer.h b/src/AudioTools/AudioCodecs/M4ACommonDemuxer.h deleted file mode 100644 index 7382a8b73c..0000000000 --- a/src/AudioTools/AudioCodecs/M4ACommonDemuxer.h +++ /dev/null @@ -1,602 +0,0 @@ -#pragma once - -#include -#include -#include - -#include "AudioTools/AudioCodecs/MP4ParserIncremental.h" -#include "AudioTools/CoreAudio/Buffers.h" -#include "MP4ParserIncremental.h" - -namespace audio_tools { - -/// The stsz sample size type should usually be uint32_t: However for audio -/// we expect that the sample size is usually aound 1 - 2k, so uint16_t -/// should be more then sufficient! Microcontolles only have a limited -/// amount of RAM, so this makes a big difference! -using stsz_sample_size_t = uint16_t; - -/** - * @brief Abstract base class for M4A/MP4 demuxers. - * Provides shared functionality for both file-based and stream-based demuxers. - */ -class M4ACommonDemuxer { - public: - enum class Codec { Unknown, AAC, ALAC, MP3 }; - - struct Frame { - Codec codec; - const char* mime = nullptr; - const uint8_t* data; - size_t size; - }; - /** - * @brief A parser for the ESDS segment to extract the relevant aac - * information. - * - */ - struct ESDSParser { - uint8_t audioObjectType; - uint8_t samplingRateIndex; - uint8_t channelConfiguration; - bool isValid = false; ///< True if the ESDP is valid - - // Parses esds content to extract audioObjectType, frequencyIndex, and - // channelConfiguration - bool parse(const uint8_t* data, size_t size) { - const uint8_t* ptr = data; - const uint8_t* end = data + size; - - if (ptr + 4 > end) return false; - ptr += 4; // skip version + flags - - if (ptr >= end || *ptr++ != 0x03) return false; - size_t es_len = parse_descriptor_length(ptr, end); - if (ptr + es_len > end) return false; - - ptr += 2; // skip ES_ID - ptr += 1; // skip flags - - if (ptr >= end || *ptr++ != 0x04) return false; - size_t dec_len = parse_descriptor_length(ptr, end); - if (ptr + dec_len > end) return false; - - ptr += 13; // skip objectTypeIndication, streamType, bufferSizeDB, - // maxBitrate, avgBitrate - - if (ptr >= end || *ptr++ != 0x05) return false; - size_t dsi_len = parse_descriptor_length(ptr, end); - if (ptr + dsi_len > end || dsi_len < 2) return false; - - uint8_t byte1 = ptr[0]; - uint8_t byte2 = ptr[1]; - - audioObjectType = (byte1 >> 3) & 0x1F; - samplingRateIndex = ((byte1 & 0x07) << 1) | ((byte2 >> 7) & 0x01); - channelConfiguration = (byte2 >> 3) & 0x0F; - return true; - } - - protected: - // Helper to decode variable-length descriptor lengths (e.g. 0x80 80 80 05) - inline size_t parse_descriptor_length(const uint8_t*& ptr, - const uint8_t* end) { - size_t len = 0; - for (int i = 0; i < 4 && ptr < end; ++i) { - uint8_t b = *ptr++; - len = (len << 7) | (b & 0x7F); - if ((b & 0x80) == 0) break; - } - return len; - } - }; - - /** - * @brief Extracts audio data based on the sample sizes defined in the stsz - * box. It collects the data from the mdat box and calls the callback with the - * extracted frames. - */ - class SampleExtractor { - public: - using Frame = M4ACommonDemuxer::Frame; - using Codec = M4ACommonDemuxer::Codec; - using FrameCallback = std::function; - - /** - * @brief Constructor. Initializes the extractor. - */ - SampleExtractor() { begin(); } - - /** - * @brief Resets the extractor state. - */ - void begin() { - sampleIndex = 0; - buffer.clear(); - p_chunk_offsets->clear(); - p_sample_sizes->clear(); - buffer.resize(1024); - current_size = 0; - box_pos = 0; - box_size = 0; - } - - /** - * @brief Sets the codec for extraction. - * @param c Codec type. - */ - void setCodec(M4ACommonDemuxer::Codec c) { codec = c; } - - /** - * @brief Sets the callback to be called for each extracted frame. - * @param cb Callback function. - */ - void setCallback(FrameCallback cb) { callback = cb; } - - /** - * @brief Sets a reference pointer passed to the callback. - * @param r Reference pointer. - */ - void setReference(void* r) { ref = r; } - - /** - * @brief Sets the maximum box size (e.g., for mdat). This is called before - * the mdat data is posted. In order to be able to play a file multiple - * times we just reset the sampleIndex! - * @param size Maximum size in bytes. - */ - void setMaxSize(size_t size) { - box_size = size; - sampleIndex = 0; - } - - /** - * @brief Writes data to the extractor, extracting frames as sample sizes - * are met. Provides the data via the callback. - * @param data Pointer to input data. - * @param len Length of input data. - * @param is_final True if this is the last chunk of the box. - * @return Number of bytes processed. - */ - size_t write(const uint8_t* data, size_t len, bool is_final) { - // Resize buffer to the current sample size - size_t currentSize = currentSampleSize(); - if (currentSize == 0) { - LOGE("No sample size defined: e.g. mdat before stsz!"); - return 0; - } - resize(currentSize); - - /// fill buffer up to the current sample size - for (int j = 0; j < len; j++) { - buffer.write(data[j]); - if (buffer.available() >= currentSize) { - LOGI("Sample# %zu: size %zu bytes", sampleIndex, currentSize); - executeCallback(currentSize); - buffer.clear(); - box_pos += currentSize; - ++sampleIndex; - currentSize = currentSampleSize(); - if (box_pos >= box_size) { - LOGI("Reached end of box: %s write", - is_final ? "final" : "not final"); - return j; - } - if (currentSize == 0) { - LOGE("No sample size defined, cannot write data"); - return j; - } - } - } - return len; - } - - /** - * @brief Returns the buffer of sample sizes. - * @return Reference to the buffer of sample sizes. - */ - BaseBuffer& getSampleSizesBuffer() { return *p_sample_sizes; } - - /** - * @brief Sets the buffer to use for sample sizes. - * @param buffer Reference to the buffer to use. - */ - void setSampleSizesBuffer(BaseBuffer &buffer){ p_sample_sizes = &buffer;} - - /** - * @brief Returns the buffer of chunk offsets. - * @return Reference to the buffer of chunk offsets. - */ - BaseBuffer& getChunkOffsetsBuffer() { return *p_chunk_offsets; } - - /** - * @brief Sets the buffer to use for chunk offsets. - * @param buffer Reference to the buffer to use. - */ - void setChunkOffsetsBuffer(BaseBuffer&buffer){ p_chunk_offsets = &buffer;} - - /** - * @brief Sets a fixed sample size/count instead of using the sampleSizes - * table. - * @param sampleSize Size of each sample. - * @param sampleCount Number of samples. - */ - void setFixedSampleCount(uint32_t sampleSize, uint32_t sampleCount) { - fixed_sample_size = sampleSize; - fixed_sample_count = sampleCount; - } - - /** - * @brief Sets the AAC configuration for ADTS header generation. - * @param profile AAC profile. - * @param srIdx Sample rate index. - * @param chCfg Channel configuration. - */ - void setAACConfig(int profile, int srIdx, int chCfg) { - aacProfile = profile; - sampleRateIdx = srIdx; - channelCfg = chCfg; - } - - /** - * @brief Constructs a Frame object for the current codec. - * @param size Size of the frame. - * @param buffer SingleBuffer with data. - * @return Frame object. - */ - Frame getFrame(size_t size, SingleBuffer& buffer) { - Frame frame; - frame.codec = codec; - frame.data = buffer.data(); - frame.size = size; - switch (codec) { - case Codec::AAC: { - // Prepare ADTS header + AAC frame - tmp.resize(size + 7); - writeAdtsHeader(tmp.data(), aacProfile, sampleRateIdx, channelCfg, - size); - memcpy(tmp.data() + 7, buffer.data(), size); - frame.data = tmp.data(); - frame.size = size + 7; - frame.mime = "audio/aac"; - break; - } - case Codec::ALAC: - frame.mime = "audio/alac"; - break; - case Codec::MP3: - frame.mime = "audio/mpeg"; - break; - default: - frame.mime = nullptr; - break; - } - return frame; - } - - protected: - SingleBuffer defaultSampleSizes; ///< Table of sample sizes. - SingleBuffer defaultChunkOffsets; ///< Table of chunk offsets. - BaseBuffer *p_sample_sizes = &defaultSampleSizes; - BaseBuffer *p_chunk_offsets = &defaultChunkOffsets; - Vector tmp; - Codec codec = Codec::Unknown; ///< Current codec. - FrameCallback callback = nullptr; ///< Frame callback. - void* ref = nullptr; ///< Reference pointer for callback. - size_t sampleIndex = 0; ///< Current sample index. - SingleBuffer buffer; ///< Buffer for accumulating sample data. - int aacProfile = 2, sampleRateIdx = 4, channelCfg = 2; ///< AAC config. - uint32_t fixed_sample_size = 0; ///< Fixed sample size (if used). - uint32_t fixed_sample_count = 0; ///< Fixed sample count (if used). - size_t current_size = 0; ///< Current sample size. - size_t box_size = 0; ///< Maximum size of the current sample. - size_t box_pos = 0; ///< Current position in the box. - - /** - * @brief Executes the callback for a completed frame. - * @param size Size of the frame. - */ - void executeCallback(size_t size) { - Frame frame = getFrame(size, buffer); - if (callback) - callback(frame, ref); - else - LOGE("No callback defined for audio frame extraction"); - } - - /** - * @brief Resizes the internal buffer if needed. - * @param newSize New buffer size. - */ - void resize(size_t newSize) { - if (buffer.size() < newSize) { - buffer.resize(newSize); - } - } - - /** - * @brief Returns the current sample size. - * @return Size of the current sample. - */ - size_t currentSampleSize() { - static size_t last_index = -1; - static size_t last_size = -1; - - // Return cached size - if (sampleIndex == last_index) { - return last_size; - } - - // using fixed sizes w/o table - if (fixed_sample_size > 0 && fixed_sample_count > 0 && - sampleIndex < fixed_sample_count) { - return fixed_sample_size; - } - stsz_sample_size_t nextSize = 0; - if (p_sample_sizes->read(nextSize)) { - last_index = sampleIndex; - last_size = nextSize; - return nextSize; - } - return 0; - } - - /** - * @brief Writes an ADTS header for an AAC frame. - * @param adts Output buffer for the header. - * @param aacProfile AAC profile. - * @param sampleRateIdx Sample rate index. - * @param channelCfg Channel configuration. - * @param frameLen Frame length. - */ - static void writeAdtsHeader(uint8_t* adts, int aacProfile, - int sampleRateIdx, int channelCfg, - int frameLen) { - adts[0] = 0xFF; - adts[1] = 0xF1; - adts[2] = ((aacProfile - 1) << 6) | (sampleRateIdx << 2) | - ((channelCfg >> 2) & 0x1); - adts[3] = ((channelCfg & 0x3) << 6) | ((frameLen + 7) >> 11); - adts[4] = ((frameLen + 7) >> 3) & 0xFF; - adts[5] = (((frameLen + 7) & 0x7) << 5) | 0x1F; - adts[6] = 0xFC; - } - }; - - using FrameCallback = std::function; - - virtual ~M4ACommonDemuxer() = default; - - /** - * @brief Sets the callback for extracted audio frames. - * @param cb Frame callback function. - */ - virtual void setCallback(FrameCallback cb) { frame_callback = cb; } - /** - * @brief Sets the buffer to use for sample sizes. - * @param buffer Reference to the buffer to use. - */ - void setSampleSizesBuffer(BaseBuffer &buffer){ sampleExtractor.setSampleSizesBuffer(buffer);} - /** - * @brief Sets the buffer to use for sample sizes. - * @param buffer Reference to the buffer to use. - */ - void setChunkOffsetsBuffer(BaseBuffer &buffer){ sampleExtractor.setChunkOffsetsBuffer(buffer);} - - protected: - FrameCallback frame_callback = nullptr; - SampleExtractor sampleExtractor; ///< Extractor for audio samples. - MP4ParserIncremental parser; ///< Underlying MP4 parser. - Codec codec = Codec::Unknown; ///< Current codec. - bool stsz_processed = false; ///< Marks the stsz table as processed - bool stco_processed = false; ///< Marks the stco table as processed - Vector alacMagicCookie; ///< ALAC codec config. - - /** - * @brief Reads a 32-bit big-endian unsigned integer from a buffer. - * @param p Pointer to buffer. - * @return 32-bit unsigned integer. - */ - static uint32_t readU32(const uint8_t* p) { - return (p[0] << 24) | (p[1] << 16) | (p[2] << 8) | p[3]; - } - static uint32_t readU32(const uint32_t num) { - uint8_t* p = (uint8_t*) # - return (p[0] << 24) | (p[1] << 16) | (p[2] << 8) | p[3]; - } - - /** - * @brief Checks if the buffer at the given offset matches the specified type. - * @param buffer Pointer to the buffer. - * @param type 4-character type string (e.g. "mp4a"). - * @param offset Offset in the buffer to check. - * @return true if the type matches, false otherwise. - */ - bool checkType(uint8_t* buffer, const char* type, int offset) { - if (buffer == nullptr || type == nullptr) return false; - bool result = buffer[offset] == type[0] && buffer[offset + 1] == type[1] && - buffer[offset + 2] == type[2] && - buffer[offset + 3] == type[3]; - return result; - } - - /** - * @brief Handles the stsd (Sample Description) box. - * @param box MP4 box. - */ - void onStsd(const MP4Parser::Box& box) { - LOGI("onStsd: %s, size: %zu bytes", box.type, box.data_size); - // printHexDump(box); - if (box.data_size < 8) return; - uint32_t entryCount = readU32(box.data + 4); - // One or more sample entry boxes (e.g. mp4a, .mp3, alac) - parser.parseString(box.data + 8, box.data_size - 8, box.file_offset + 8 + 8, - box.level + 1); - } - - /** - * @brief Handles the mp4a box. - * @param box MP4 box. - */ - void onMp4a(const MP4Parser::Box& box) { - LOGI("onMp4a: %s, size: %zu bytes", box.type, box.data_size); - // printHexDump(box); - if (box.data_size < 36) return; // Minimum size for mp4a box - - // use default configuration - int aacProfile = 2; // Default: AAC LC - int sampleRateIdx = 4; // Default: 44100 Hz - int channelCfg = 2; // Default: Stereo - sampleExtractor.setAACConfig(aacProfile, sampleRateIdx, channelCfg); - codec = Codec::AAC; - sampleExtractor.setCodec(codec); - - /// for mp4a we expect to contain a esds: child boxes start at 36 - int pos = 36 - 8; - parser.parseString(box.data + pos, box.data_size - pos, box.level + 1); - } - - /** - * @brief Handles the esds (Elementary Stream Descriptor) box. - * @param box MP4 box. - */ - void onEsds(const MP4Parser::Box& box) { - LOGI("onEsds: %s, size: %zu bytes", box.type, box.data_size); - // printHexDump(box); - ESDSParser esdsParser; - if (!esdsParser.parse(box.data, box.data_size)) { - LOGE("Failed to parse esds box"); - return; - } - LOGI( - "-> esds: AAC objectType: %u, samplingRateIdx: %u, " - "channelCfg: %u", - esdsParser.audioObjectType, esdsParser.samplingRateIndex, - esdsParser.channelConfiguration); - sampleExtractor.setAACConfig(esdsParser.audioObjectType, - esdsParser.samplingRateIndex, - esdsParser.channelConfiguration); - } - - void fixALACMagicCookie(uint8_t* cookie, size_t len) { - if (len < 28) { - return; - } - - // Helper to read/write big-endian - auto read32 = [](uint8_t* p) -> uint32_t { - return (p[0] << 24) | (p[1] << 16) | (p[2] << 8) | p[3]; - }; - auto write32 = [](uint8_t* p, uint32_t val) { - p[0] = (val >> 24) & 0xFF; - p[1] = (val >> 16) & 0xFF; - p[2] = (val >> 8) & 0xFF; - p[3] = val & 0xFF; - }; - auto read16 = [](uint8_t* p) -> uint16_t { return (p[0] << 8) | p[1]; }; - auto write16 = [](uint8_t* p, uint16_t val) { - p[0] = (val >> 8) & 0xFF; - p[1] = val & 0xFF; - }; - - // Fix values if zero or invalid - if (read32(cookie + 0) == 0) write32(cookie + 0, 4096); // frameLength - if (cookie[6] == 0) cookie[6] = 16; // bitDepth - if (cookie[7] == 0 || cookie[7] > 32) cookie[7] = 10; // pb - if (cookie[8] == 0 || cookie[8] > 32) cookie[8] = 14; // mb - if (cookie[9] == 0 || cookie[9] > 32) cookie[9] = 10; // kb - if (cookie[10] == 0 || cookie[10] > 8) cookie[10] = 2; // numChannels - if (read16(cookie + 11) == 0) write16(cookie + 11, 255); // maxRun - if (read32(cookie + 13) == 0) write32(cookie + 13, 8192); // maxFrameBytes - if (read32(cookie + 17) == 0) write32(cookie + 17, 512000); // avgBitRate - if (read32(cookie + 21) == 0) write32(cookie + 21, 44100); // sampleRate - } - - /** - * @brief Handles the alac box. - * @param box MP4 box. - */ - void onAlac(const MP4Parser::Box& box) { - LOGI("onAlac: %s, size: %zu bytes", box.type, box.data_size); - codec = Codec::ALAC; - sampleExtractor.setCodec(codec); - - // only alac box in alac contains magic cookie - MP4Parser::Box alac; - if (parser.findBox("alac", box.data, box.data_size, alac)) { - // fixALACMagicCookie((uint8_t*)alac.data, alac.data_size); - alacMagicCookie.resize(alac.data_size - 4); - std::memcpy(alacMagicCookie.data(), alac.data + 4, alac.data_size - 4); - } - } - - /** - * @brief Handles the stsz (Sample Size) box. - * @param box MP4 box. - */ - void onStsz(MP4Parser::Box& box) { - LOGI("onStsz: %s, size: %zu bytes", box.type, box.data_size); - if (stsz_processed) return; - // Parse stsz box and fill sampleSizes - const uint8_t* data = box.data; - uint32_t sampleSize = readU32(data + 4); - uint32_t sampleCount = readU32(data + 8); - sampleExtractor.begin(); - BaseBuffer& sampleSizes = sampleExtractor.getSampleSizesBuffer(); - if (sampleSize == 0) { - LOGI("-> Sample Sizes Count: %u", sampleCount); - sampleSizes.resize(sampleCount); - for (uint32_t i = 0; i < sampleCount; ++i) { - uint32_t sampleSizes32 = readU32(data + 12 + i * 4); - // if this is giving an error change the stsz_sample_size_t - assert(sampleSizes32 <= UINT16_MAX); - stsz_sample_size_t sampleSizes16 = sampleSizes32; - assert(sampleSizes.write(sampleSizes16)); - } - } else { - sampleExtractor.setFixedSampleCount(sampleSize, sampleCount); - } - stsz_processed = true; - } - - /** - * @brief Handles the stco (Chunk Offset) box. - * @param box MP4 box. - */ - void onStco(MP4Parser::Box& box) { - LOGI("onStco: %s, size: %zu bytes", box.type, box.data_size); - if (stco_processed) return; - // Parse stco box and fill chunkOffsets - const uint8_t* data = box.data + 4; - size_t size = box.data_size; - if (size < 4) return; - uint32_t entryCount = readU32(data); - BaseBuffer& chunkOffsets = sampleExtractor.getChunkOffsetsBuffer(); - if (size < 4 + 4 * entryCount) return; - chunkOffsets.resize(entryCount); - LOGI("-> Chunk offsets count: %u", entryCount); - for (uint32_t i = 0; i < entryCount; ++i) { - chunkOffsets.write(readU32(data + 4 + i * 4)); - } - stco_processed = true; - } - - void printHexDump(const MP4Parser::Box& box) { - const uint8_t* data = box.data; - size_t len = box.data_size; - LOGI("==========================="); - for (size_t i = 0; i < len; i += 16) { - char hex[49] = {0}; - char ascii[17] = {0}; - for (size_t j = 0; j < 16 && i + j < len; ++j) { - sprintf(hex + j * 3, "%02X ", data[i + j]); - ascii[j] = (data[i + j] >= 32 && data[i + j] < 127) ? data[i + j] : '.'; - } - ascii[16] = 0; - LOGI("%04zx: %-48s |%s|", i, hex, ascii); - } - LOGI("==========================="); - } -}; - -} // namespace audio_tools \ No newline at end of file diff --git a/src/AudioTools/AudioCodecs/MP4Parser.h b/src/AudioTools/AudioCodecs/MP4Parser.h deleted file mode 100644 index aed4b9554c..0000000000 --- a/src/AudioTools/AudioCodecs/MP4Parser.h +++ /dev/null @@ -1,465 +0,0 @@ -#pragma once - -#include -#include -#include -#include - -#include "AudioTools/CoreAudio/Buffers.h" - -namespace audio_tools { - -/** - * @brief MP4Parser is a class that parses MP4 container files and extracts - * boxes (atoms). It provides a callback mechanism to process each box as it is - * parsed. You can define specific callbacks for individual box types or use a - * generic callback for the undefined boxes: By default it just prints the box - * information to Serial. If a container box contains data, it will be processed - * recursively and if it contains data itself, it might be reported in a second - * callback call. - * @note This parser expect the mdat box to be the last box in the file. This - * can be achieve with the following ffmpeg commands: - * - ffmpeg -i ../sine.wav -c:a alac -movflags +faststart alac.m4a - * - ffmpeg -i ../sine.wav -c:a aac -movflags +faststart aac.m4a - * - * @ingroup codecs - * @author Phil Schatzmann - */ -class MP4Parser { - public: - /** - * @brief Represents an individual box in the MP4 file. - */ - struct Box { - friend class MP4Parser; ///< Allow MP4Parser to access private members - friend class MP4ParserExt; ///< Allow MP4ParserExt to access private - ///< members - size_t id = 0; ///< Unique box ID - char type[5]; ///< 4-character box type (null-terminated) - const uint8_t* data = - nullptr; ///< Pointer to box payload (not including header) - size_t data_size = 0; ///< Size of payload (not including header) - size_t size = - 0; ///< Size of payload including subboxes (not including header) - int level = 0; ///< Nesting depth - uint64_t file_offset = 0; ///< File offset where box starts - bool is_complete = false; ///< True if the box data is complete - bool is_container = false; ///< True if the box is a container - }; - - using BoxCallback = std::function; - - /** - * @brief Structure for type-specific callbacks. - */ - struct CallbackEntry { - char type[5]; ///< 4-character box type - BoxCallback cb; ///< Callback function - bool callGeneric = - true; ///< If true, also call the generic callback after this one - }; - - /** - * @brief Defines an optional reference. By default it is the parser itself. - * @param ref Pointer to reference object. - */ - void setReference(void* ref) { this->ref = ref; } - - /** - * @brief Defines the generic callback for all boxes. - * @param cb Callback function for all boxes. - */ - void setCallback(BoxCallback cb) { callback = cb; } - - /** - * @brief Defines a specific callback for a box type. - * @param type 4-character box type (e.g. "moov", "mdat"). - * @param cb Callback function for this box type. - * @param callGeneric If true, the generic callback will also be called after - * the type-specific callback. - */ - void setCallback(const char* type, BoxCallback cb, bool callGeneric = true) { - CallbackEntry entry; - strncpy(entry.type, type, 4); - entry.type[4] = '\0'; // Ensure null-termination - entry.cb = cb; - entry.callGeneric = callGeneric; - callbacks.push_back(entry); - }; - - /** - * @brief Defines a specific buffer size. - * @param size Buffer size in bytes. - * @return true if the buffer was resized successfully. - */ - bool resize(size_t size) { - buffer.resize(size); - return buffer.size() == size; - } - - /** - * @brief Initializes the parser. - * @return true on success. - */ - bool begin() { - buffer.clear(); - if (buffer.size() == 0) buffer.resize(1024); - parseOffset = 0; - fileOffset = 0; - levelStack.clear(); - box.is_complete = true; // Start with no open box - box.data = nullptr; - box.size = 0; - box.level = 0; - box.file_offset = 0; - box.id = 0; - return true; - } - - /** - * @brief Provide the data to the parser (in chunks if needed). - * @param data Pointer to input data. - * @param len Length of input data. - * @return Number of bytes written to the buffer. - */ - size_t write(const uint8_t* data, size_t len) { - if (is_error) return len; // If an error occurred, skip writing - size_t result = buffer.writeArray(data, len); - parse(); - return result; - } - - /** - * @brief Provide the data to the parser (in chunks if needed). - * @param data Pointer to input data (char*). - * @param len Length of input data. - * @return Number of bytes written to the buffer. - */ - size_t write(const char* data, size_t len) { - return write(reinterpret_cast(data), len); - } - - /** - * @brief Returns the available space for writing. - * @return Number of bytes available for writing. - */ - int availableForWrite() { return buffer.availableForWrite(); } - - /** - * @brief Adds a box name that will be interpreted as a container. - * @param name Name of the container box. - * @param start Offset of child boxes (default 0). - */ - void addContainer(const char* name, int start = 0) { - ContainerInfo info; - info.name = name; - info.start = start; // offset of child boxes - } - - /** - * @brief Trigger separate parsing (and callbacks) on the indicated string. - * @param str Pointer to the string data. - * @param len Length of the string data. - * @return Number of bytes parsed. - */ - int parseString(const uint8_t* str, int len, int fileOffset = 0, - int level = 0) { - char type[5]; - int idx = 0; - Box box; - while (true) { - if (!isValidType((const char*)str + idx + 4)) { - return idx; - } - size_t box_size = readU32(str + idx) - 8; - box.data = str + 8 + idx; - box.size = box_size; - box.level = level; - box.data_size = box.size; - box.file_offset = fileOffset + idx; - strncpy(box.type, (char*)(str + idx + 4), 4); - box.type[4] = '\0'; - idx += box.size; - processCallback(box); - if (idx >= len) break; // No more data to parse - } - return idx; - } - - /// find box in box - bool findBox(const char* name, const uint8_t* data, size_t len, Box& result) { - for (int j = 0; j < len - 4; j++) { - if (!isValidType((const char*)data + j + 4)) { - continue; // Skip invalid types - } - size_t box_size = readU32(data + j) - 8; - if (box_size < 8) continue; // Invalid box size - Box box; - box.data = data + j + 8; - box.size = box_size; - box.data_size = box.size; - strncpy(box.type, (char*)(data + j + 4), 4); - box.type[4] = '\0'; - if (StrView(box.type) == name) { - result = box; - return true; // Found the box - } - } - return false; - } - - protected: - BoxCallback callback = defaultCallback; ///< Generic callback for all boxes - Vector callbacks; ///< List of type-specific callbacks - SingleBuffer buffer; ///< Buffer for incoming data - Vector levelStack; ///< Stack for container box levels - size_t parseOffset = 0; ///< Current parse offset in buffer - uint64_t fileOffset = 0; ///< Current file offset - void* ref = this; ///< Reference pointer for callbacks - Box box; ///< Current box being processed - bool is_error = false; ///< True if an error occurred - - /** - * @brief Structure for container box information. - */ - struct ContainerInfo { - const char* name = nullptr; ///< Name of the container box - int start = 0; ///< Offset of child boxes - }; - Vector containers; ///< List of container box info - - /** - * @brief Returns the current file offset (absolute position in file). - * @return Current file offset. - */ - uint64_t currentFileOffset() { return fileOffset + parseOffset; } - - /** - * @brief Default callback that prints box information to Serial. - * @param box The box being processed. - * @param ref Optional reference pointer. - */ - static void defaultCallback(const Box& box, void* ref) { - char space[box.level * 2 + 1]; - char str_buffer[200]; - memset(space, ' ', box.level * 2); - space[box.level * 2] = '\0'; // Null-terminate the string - snprintf(str_buffer, sizeof(str_buffer), - "%s- #%u %s, Offset: %u, Size: %u, Data Size: %u", space, - (unsigned)box.id, box.type, (unsigned)box.file_offset, - (unsigned)box.size, (unsigned)box.data_size); -#ifdef ARDUINO - Serial.println(str_buffer); -#else - printf("%s\n", str_buffer); -#endif - } - - /** - * @brief Reads a 32-bit big-endian unsigned integer from a buffer. - * @param p Pointer to buffer. - * @return 32-bit unsigned integer. - */ - static uint32_t readU32(const uint8_t* p) { - return (p[0] << 24) | (p[1] << 16) | (p[2] << 8) | p[3]; - } - - /** - * @brief Reads a 64-bit big-endian unsigned integer from a buffer. - * @param p Pointer to buffer. - * @return 64-bit unsigned integer. - */ - static uint64_t readU64(const uint8_t* p) { - return ((uint64_t)readU32(p) << 32) | readU32(p + 4); - } - - /** - * @brief Main parsing loop. Handles parsing of boxes in the buffer. - */ - virtual void parse() { - while (true) { - size_t bufferSize = buffer.available(); - // not enough data to parse a box header - if (parseOffset + 8 > bufferSize) break; - - parseOffset = checkParseOffset(); - - const uint8_t* p = buffer.data() + parseOffset; - uint32_t size32 = readU32(p); - char type[5]; - strncpy(type, (char*)(p + 4), 4); - - uint64_t boxSize = size32; - size_t headerSize = 8; - bool is_container = isContainerBox(type); - - // check data in buffer - if (!is_container) { - // not enough data to provide data - if (boxSize > buffer.size()) { - LOGE("MP4Parser: '%s' box size %u exceeds buffer size %u", type, - (unsigned)boxSize, (unsigned)buffer.size()); - is_error = true; - return; - } - // not enough data in buffer - if (parseOffset + boxSize > bufferSize) break; - } - - // fill box with data - // Calculate absolute file offset for this box - uint64_t absBoxOffset = fileOffset + parseOffset; - int level = levelStack.size(); - - strcpy(box.type, type); - box.id++; - box.data = p + headerSize; - box.size = static_cast(boxSize - headerSize); - box.data_size = box.size; - box.level = level; - box.file_offset = fileOffset + parseOffset; - box.is_complete = (parseOffset + boxSize <= bufferSize); - box.is_container = is_container; - - // Special logic for container: usually no data - if (box.is_container) { - box.data_size = getContainerDataLength(box.type); - if (box.data_size == 0) box.data = nullptr; - box.is_complete = true; - } - - // Regular logic for box with complete data - processCallback(box); - - // Recurse into container - if (box.is_container) { - levelStack.push_back(absBoxOffset + boxSize); - parseOffset += (headerSize + box.data_size); - continue; - } - - parseOffset += boxSize; - - popLevels(); - } - - if (parseOffset > 0) { - fileOffset += parseOffset; - buffer.clearArray(parseOffset); - parseOffset = 0; - } - } - - /** - * @brief Pops levels from the stack if we've passed their bounds. - */ - void popLevels() { - // Pop levels if we've passed their bounds (absolute file offset) - while (!levelStack.empty() && - (fileOffset + parseOffset) >= levelStack.back()) { - levelStack.pop_back(); - } - } - - /** - * @brief Processes the callback for a box. - * Calls the type-specific callback if present, and the generic callback if - * allowed. - * @param box The box being processed. - */ - void processCallback(Box& box) { - bool is_called = false; - bool call_generic = true; - for (const auto& entry : callbacks) { - if (strncmp(entry.type, box.type, 4) == 0) { - entry.cb(box, ref); - is_called = true; - if (!entry.callGeneric) call_generic = false; - } - } - /// call generic callback if allowed - if ((!is_called || call_generic) && callback) callback(box, ref); - } - - /** - * @brief Checks if a box type is a container box. - * @param type Box type string. - * @return true if container box, false otherwise. - */ - bool isContainerBox(const char* type) { - // fill with default values if nothing has been defined - if (containers.empty()) { - // pure containers - static const char* containers_str[] = { - "moov", "trak", "mdia", "minf", "stbl", "edts", "dinf", "udta", - "ilst", "moof", "traf", "mfra", "tref", "iprp", "sinf", "schi"}; - for (const char* c : containers_str) { - ContainerInfo info; - info.name = c; - info.start = 0; - containers.push_back(info); - } - // container with data - ContainerInfo info; - info.name = "meta"; - info.start = 4; // 4 bytes: version (1 byte) + flags (3 bytes) - containers.push_back(info); - } - // find the container by name - for (auto& cont : containers) { - if (StrView(type) == cont.name) return true; - } - return false; - } - - /** - * @brief Gets the start offset for a subcontainer. - * @param type Box type string. - * @return Offset of the subcontainer. - */ - int getContainerDataLength(const char* type) { - for (auto& cont : containers) { - if (StrView(type) == cont.name) return cont.start; - } - return 0; - } - - /** - * @brief Checks if a type string is a valid 4-character box type. - * @param type Pointer to type string. - * @param offset Offset in the string. - * @return true if valid, false otherwise. - */ - bool isValidType(const char* type, int offset = 0) const { - // Check if the type is a valid 4-character string - return (type != nullptr && isalnum(type[offset]) && - isalnum(type[offset + 1]) && isalnum(type[offset + 2]) && - isalnum(type[offset + 3])); - } - - /** - * @brief Checks and adjusts the parse offset for valid box types. - * @return Adjusted parse offset. - */ - size_t checkParseOffset() { - size_t current = parseOffset; - const char* type = (char*)(buffer.data() + parseOffset + 4); - for (int j = 0; j < buffer.available() - parseOffset - 4; j += 4) { - if (isValidType(type, j)) { - if (j != 0) { - // report the data under the last valid box - box.size = 0; - box.data_size = j; - box.level = static_cast(levelStack.size()) + 1; - box.data = buffer.data() + parseOffset; - processCallback(box); - } - - return j + parseOffset; - } - } - return parseOffset; - } -}; - -} // namespace audio_tools diff --git a/src/AudioTools/AudioCodecs/MP4ParserIncremental.h b/src/AudioTools/AudioCodecs/MP4ParserIncremental.h deleted file mode 100644 index d8bf07de31..0000000000 --- a/src/AudioTools/AudioCodecs/MP4ParserIncremental.h +++ /dev/null @@ -1,318 +0,0 @@ -#pragma once -#include "MP4Parser.h" - -namespace audio_tools { - -/** - * @brief MP4ParserIncremental is a class that extends MP4Parser to support - * incremental parsing of MP4 boxes. - * - * It allows for processing boxes as they are received, which is useful for - * large files or streaming scenarios. It provides a callback mechanism to - * process box data incrementally. The default callback prints the box - * information to Serial. - * @ingroup codecs - */ -class MP4ParserIncremental : public MP4Parser { - public: - using IncrementalDataCallback = std::function; - - /** - * @brief Defines the callback for all incremental box data. - * @param cb Callback function for all boxes. - */ - void setIncrementalDataCallback(IncrementalDataCallback cb) { incremental_data_callback = cb; } - - /** - * @brief Defines a specific callback for incremental data of a box type. - * @param type 4-character box type (e.g. "mdat"). - * @param cb Callback function for this box type. - * @param callGeneric If true, the generic callback will also be called after the type-specific callback. - */ - void setIncrementalDataCallback(const char* type, IncrementalDataCallback cb, bool callGeneric = true) { - CallbackEntry entry; - strncpy(entry.type, type, 4); - entry.type[4] = '\0'; // Ensure null-termination - entry.cb = cb; - entry.callGeneric = callGeneric; - incremental_data_callbacks.push_back(entry); - } - - /** - * @brief Defines the generic callback for all boxes. - * @param cb Callback function for all boxes. - */ - void setCallback(BoxCallback cb) { MP4Parser::setCallback(cb); } - - /** - * @brief Defines a specific callback for a box type. - * @param type 4-character box type (e.g. "moov", "mdat"). - * @param cb Callback function for this box type. - */ - void setCallback(const char* type, BoxCallback cb, bool callGeneric = true) { - MP4Parser::setCallback(type, cb, callGeneric); - } - - protected: - /** - * @brief Structure for type-specific incremental data callbacks. - */ - struct CallbackEntry { - char type[5]; ///< 4-character box type - IncrementalDataCallback cb; ///< Callback function - bool callGeneric = true; ///< If true, also call the generic callback after this one - }; - - IncrementalDataCallback incremental_data_callback = defaultIncrementalDataCallback; ///< Generic incremental data callback - Vector incremental_data_callbacks; ///< List of type-specific incremental data callbacks - bool box_in_progress = false; ///< True if currently parsing a box incrementally - size_t box_bytes_received = 0; ///< Bytes received so far for the current box - size_t box_bytes_expected = 0; ///< Total expected bytes for the current box - char box_type[5] = {0}; ///< Current box type - int box_level = 0; ///< Current box level (nesting) - - /** - * @brief Default incremental data callback. Prints box info. - * @param box The box being processed. - * @param data Pointer to box data. - * @param len Length of data. - * @param is_final True if this is the last chunk for this box. - * @param ref Optional reference pointer. - */ - static void defaultIncrementalDataCallback(Box& box, const uint8_t* data, size_t len, - bool is_final, void* ref) { - char space[box.level * 2 + 1]; - memset(space, ' ', box.level * 2); - space[box.level * 2] = '\0'; // Null-terminate the string - - char str_buffer[200]; - snprintf(str_buffer, sizeof(str_buffer), "%s -> Incremental Data: %s %u ", - space, box.type, (unsigned)len); - -#ifdef ARDUINO - Serial.println(str_buffer); -#else - printf("%s\n", str_buffer); -#endif - } - - /** - * @brief Main parsing loop. Handles incremental and complete boxes. - */ - void parse() override { - while (true) { - size_t bufferSize = buffer.available(); - if (!box_in_progress) { - if (!tryStartNewBox(bufferSize)) break; - } else { - if (!continueIncrementalBox()) break; - } - popLevels(); - } - finalizeParse(); - } - - /** - * @brief Try to start parsing a new box. Returns false if not enough data. - * @param bufferSize Number of bytes available in the buffer. - * @return True if a box was started, false otherwise. - */ - bool tryStartNewBox(size_t bufferSize) { - if (parseOffset + 8 > bufferSize) return false; - char type[5]; - - // get basic box information - parseOffset = checkParseOffset(); - const uint8_t* p = buffer.data() + parseOffset; - uint32_t size32 = readU32(p); - strncpy(type, (char*)(p + 4), 4); - type[4] = '\0'; - uint64_t boxSize = size32; - size_t headerSize = 8; - - if (boxSize < headerSize) return false; - - int level = static_cast(levelStack.size()); - bool is_container = isContainerBox(type); - - if (is_container) { - handleContainerBox(type, boxSize, level); - return true; - } - - size_t payload_size = static_cast(boxSize - headerSize); - if (parseOffset + boxSize <= bufferSize) { - // start with full buffer! - handleCompleteBox(type, p, headerSize, payload_size, level); - parseOffset += boxSize; - } else { - startIncrementalBox(type, p, headerSize, payload_size, level, bufferSize); - return false; // Wait for more data - } - return true; - } - - /** - * @brief Handles a container box (box with children). - * @param type Box type string. - * @param boxSize Size of the box. - * @param level Nesting level of the box. - */ - void handleContainerBox(const char* type, uint64_t boxSize, int level) { - strcpy(box.type, type); - box.id = ++this->box.id; - box.data = nullptr; - box.size = static_cast(boxSize - 8); - box.data_size = 0; - box.level = level; - box.file_offset = fileOffset + parseOffset; - box.is_complete = true; - box.is_container = true; - processCallback(box); - - uint64_t absBoxOffset = fileOffset + parseOffset; - levelStack.push_back(absBoxOffset + boxSize); - parseOffset += 8; - } - - /** - * @brief Handles a complete (non-incremental) box. - * @param type Box type string. - * @param p Pointer to the start of the box in the buffer. - * @param headerSize Size of the box header. - * @param payload_size Size of the box payload. - * @param level Nesting level of the box. - */ - void handleCompleteBox(const char* type, const uint8_t* p, size_t headerSize, - size_t payload_size, int level) { - strcpy(box.type, type); - box.id = ++this->box.id; - box.data = p + headerSize; - box.size = payload_size; - box.data_size = payload_size; - box.level = level; - box.file_offset = fileOffset + parseOffset; - box.is_complete = true; - box.is_container = false; - processCallback(box); - } - - /** - * @brief Starts parsing a box incrementally. - * @param type Box type string. - * @param p Pointer to the start of the box in the buffer. - * @param headerSize Size of the box header. - * @param payload_size Size of the box payload. - * @param level Nesting level of the box. - * @param bufferSize Number of bytes available in the buffer. - */ - void startIncrementalBox(const char* type, const uint8_t* p, - size_t headerSize, size_t payload_size, int level, - size_t bufferSize) { - box_in_progress = true; - box_bytes_received = 0; - box_bytes_expected = payload_size; - strncpy(box_type, type, 5); - box_level = level; - - size_t available_payload = bufferSize - parseOffset - headerSize; - - if (available_payload > 0) { - box_bytes_received += available_payload; - if (incremental_data_callback) { - strcpy(box.type, box_type); - box.id = ++this->box.id; - box.data = nullptr; - box.data_size = available_payload; - box.level = box_level; - box.file_offset = fileOffset + parseOffset; - box.is_complete = false; - box.is_container = false; - - // regular callback from parent - box.size = payload_size; - box.data_size = 0; - processCallback(box); - - // incremental callback - box.size = box_bytes_expected; - box.data_size = available_payload; - processIncrementalDataCallback(box, p + headerSize, available_payload, false, ref); - } - } - //fileOffset += (bufferSize - buffer.available()); - fileOffset += (parseOffset + payload_size + 8); - buffer.clear(); - parseOffset = 0; - } - - /** - * @brief Continue filling an incremental box. Returns false if not enough data. - * @return True if more data was processed, false otherwise. - */ - bool continueIncrementalBox() { - size_t to_read = std::min((size_t)box_bytes_expected - box_bytes_received, - (size_t)buffer.available()); - if (to_read == 0) return false; - if (incremental_data_callback) { - strcpy(box.type, box_type); - box.id = ++this->box.id; - box.data = nullptr; - box.size = box_bytes_expected; - box.data_size = to_read; - box.level = box_level; - box.file_offset = 0; - box.is_complete = (box_bytes_received + to_read == box_bytes_expected); - box.is_container = false; - processIncrementalDataCallback(box, buffer.data(), to_read, box.is_complete, ref); - } - box_bytes_received += to_read; - //fileOffset += to_read; - buffer.clearArray(to_read); - - if (box_bytes_received >= box_bytes_expected) { - box_in_progress = false; - } - return true; - } - - /** - * @brief Processes the incremental data callback for a box. - * Calls the type-specific callback if present, and the generic callback if allowed. - * @param box The box being processed. - * @param data Pointer to box data. - * @param len Length of data. - * @param is_final True if this is the last chunk for this box. - * @param ref Optional reference pointer. - */ - void processIncrementalDataCallback(Box& box, const uint8_t* data, size_t len, - bool is_final, void* ref) { - bool is_called = false; - bool call_generic = true; - for (auto& entry : incremental_data_callbacks) { - if (StrView(entry.type) == box.type) { - entry.cb(box, data, len, is_final, ref); - is_called = true; - if (!entry.callGeneric) call_generic = false; - break; - } - } - if ((!is_called || call_generic) && incremental_data_callback) { - incremental_data_callback(box, data, len, is_final, ref); - } - } - - /** - * @brief Finalizes parsing, updating file offset and clearing buffer. - */ - void finalizeParse() { - if (parseOffset > 0) { - fileOffset += parseOffset; - buffer.clearArray(parseOffset); - parseOffset = 0; - } - } -}; - -} // namespace audio_tools diff --git a/src/AudioTools/AudioCodecs/MultiDecoder.h b/src/AudioTools/AudioCodecs/MultiDecoder.h deleted file mode 100644 index 706c54a5cc..0000000000 --- a/src/AudioTools/AudioCodecs/MultiDecoder.h +++ /dev/null @@ -1,176 +0,0 @@ - -#pragma once - -#include "AudioTools/AudioCodecs/AudioCodecsBase.h" -#include "AudioTools/CoreAudio/AudioBasic/StrView.h" -#include "AudioTools/CoreAudio/AudioMetaData/MimeDetector.h" -#include "AudioTools/CoreAudio/AudioHttp/AbstractURLStream.h" - -namespace audio_tools { - -/** - * @brief Manage multiple decoders: the actual decoder is only opened when it - * has been selected. The relevant decoder is determined dynamically at the - * first write from the determined mime type. You can add your own custom mime - * type determination logic. - * @ingroup codecs - * @ingroup decoder - * @author Phil Schatzmann - * @copyright GPLv3 - */ -class MultiDecoder : public AudioDecoder { - public: - /// Default constructor - MultiDecoder() = default; - /// Provides a URLStream to look up the mime type from the http reply header - MultiDecoder(AbstractURLStream& url) { setMimeSource(url); } - - /// Enables the automatic mime type determination - bool begin() override { - mime_detector.begin(); - is_first = true; - if (p_print==nullptr) { - LOGE("No output defined"); - return false; - } - return true; - } - - /// closes the actual decoder - void end() override { - if (actual_decoder.decoder != nullptr && actual_decoder.is_open) { - actual_decoder.decoder->end(); - } - actual_decoder.is_open = false; - actual_decoder.decoder = nullptr; - actual_decoder.mime = nullptr; - is_first = true; - } - - /// Adds a decoder that will be selected by it's mime type - void addDecoder(AudioDecoder& decoder, const char* mime) { - DecoderInfo info{mime, &decoder}; - decoder.addNotifyAudioChange(*this); - decoders.push_back(info); - } - - /// Adds a decoder that will be selected by it's mime type and defines the - /// mime checking logic - void addDecoder(AudioDecoder& decoder, const char* mime, - bool (*check)(uint8_t* data, size_t len)) { - addDecoder(decoder, mime); - mime_detector.setCheck(mime, check); - } - - void setOutput(Print& out_stream) override { - p_print = &out_stream; - for (int j = 0; j < decoders.size(); j++) { - decoders[j].decoder->setOutput(out_stream); - } - } - - /// Defines url stream from which we determine the mime type from the reply header - void setMimeSource(AbstractURLStream& url) { p_url_stream = &url; } - - /// selects the actual decoder by mime type - this is usually called - /// automatically from the determined mime type - bool selectDecoder(const char* mime) { - bool result = false; - if (mime == nullptr) return false; - // do nothing if no change - if (StrView(mime).equals(actual_decoder.mime)) { - is_first = false; - return true; - } - // close actual decoder - end(); - - // find the corresponding decoder - selected_mime = nullptr; - for (int j = 0; j < decoders.size(); j++) { - DecoderInfo info = decoders[j]; - if (StrView(info.mime).equals(mime)) { - LOGI("New decoder found for %s (%s)", info.mime, mime); - actual_decoder = info; - // define output if it has not been defined - if (p_print != nullptr && - actual_decoder.decoder->getOutput() == nullptr) { - actual_decoder.decoder->setOutput(*p_print); - } - actual_decoder.decoder->begin(); - result = true; - selected_mime = mime; - } - } - is_first = false; - return result; - } - - const char* selectedMime() { - return selected_mime; - } - - size_t write(const uint8_t* data, size_t len) override { - if (is_first) { - const char* mime = nullptr; - if (p_url_stream != nullptr) { - // get content type from http header - mime = p_url_stream->getReplyHeader(CONTENT_TYPE); - if (mime) LOGI("mime from http request: %s", mime); - } - if (mime == nullptr) { - // use the mime detector - mime_detector.write((uint8_t*)data, len); - mime = mime_detector.mime(); - if (mime) LOGI("mime from mime_detector: %s", mime); - } - if (mime != nullptr) { - // select the decoder based on the detemined mime type - if (!selectDecoder(mime)) { - LOGE("The decoder could not be found for %s", mime); - actual_decoder.decoder = &nop; - actual_decoder.is_open = true; - } - } - is_first = false; - } - // check if we have a decoder - if (actual_decoder.decoder == nullptr) return 0; - // decode the data - return actual_decoder.decoder->write(data, len); - } - - virtual operator bool() override { - if (actual_decoder.decoder == &nop) return false; - return is_first || actual_decoder.is_open; - }; - - /// Sets the config to the selected decoder - bool setCodecConfig(const uint8_t* data, size_t len) override { - if (actual_decoder.decoder == nullptr) { - LOGE("No decoder defined, cannot set codec config"); - return false; - } - return actual_decoder.decoder->setCodecConfig(data, len); - } - - protected: - struct DecoderInfo { - const char* mime = nullptr; - AudioDecoder* decoder = nullptr; - bool is_open = false; - DecoderInfo() = default; - DecoderInfo(const char* mime, AudioDecoder* decoder) { - this->mime = mime; - this->decoder = decoder; - } - } actual_decoder; - Vector decoders{0}; - MimeDetector mime_detector; - CodecNOP nop; - AbstractURLStream* p_url_stream = nullptr; - bool is_first = true; - const char* selected_mime = nullptr; -}; - -} // namespace audio_tools \ No newline at end of file diff --git a/src/AudioTools/AudioIO.h b/src/AudioTools/AudioIO.h new file mode 100644 index 0000000000..bb42f6687d --- /dev/null +++ b/src/AudioTools/AudioIO.h @@ -0,0 +1,304 @@ +#pragma once +#include "AudioTools/AudioOutput.h" +#include "AudioTools/AudioStreams.h" + +namespace audio_tools { + +/** + * @brief Base class for Output Adpapters + * + */ +class AudioOutputAdapter : public AudioOutput { +}; + +/** + * @brief Wrapper which converts a Print to a AudioOutput + * @ingroup tools + */ +class AdapterPrintToAudioOutput : public AudioOutputAdapter { + public: + AdapterPrintToAudioOutput(Print &print){ + p_print = &print; + } + void setAudioInfo(AudioInfo info){ + } + size_t write(const uint8_t *buffer, size_t size){ + return p_print->write(buffer,size); + } + /// If true we need to release the related memory in the destructor + virtual bool isDeletable() { + return true; + } + protected: + Print *p_print=nullptr; +}; + + +/** + * @brief Wrapper which converts a AudioStream to a AudioOutput + * @ingroup tools + */ +class AdapterAudioStreamToAudioOutput : public AudioOutputAdapter { + public: + AdapterAudioStreamToAudioOutput() = default; + + AdapterAudioStreamToAudioOutput(AudioStream &stream){ + setStream(stream); + } + + void setStream(AudioStream &stream){ + p_stream = &stream; + } + + void setAudioInfo(AudioInfo info){ + p_stream->setAudioInfo(info); + } + size_t write(const uint8_t *buffer, size_t size){ + return p_stream->write(buffer,size); + } + + /// If true we need to release the related memory in the destructor + virtual bool isDeletable() { + return true; + } + + protected: + AudioStream *p_stream=nullptr; +}; + +/** + * @brief Replicates the output to multiple destinations. + * @ingroup transform + * @author Phil Schatzmann + * @copyright GPLv3 + */ +class MultiOutput : public AudioOutput { + public: + + /// Defines a MultiOutput with no final output: Define your outputs with add() + MultiOutput() = default; + + /// Defines a MultiOutput with a single final outputs, + MultiOutput(AudioOutput &out){ + add(out); + } + + MultiOutput(AudioStream &out){ + add(out); + } + + /// Defines a MultiOutput with 2 final outputs + MultiOutput(AudioOutput &out1, AudioOutput &out2){ + add(out1); + add(out2); + } + + /// Defines a MultiOutput with 2 final outputs + MultiOutput(AudioStream &out1, AudioStream &out2){ + add(out1); + add(out2); + } + + virtual ~MultiOutput() { + for (int j=0;jisDeletable()){ + delete vector[j]; + } + } + } + + bool begin(AudioInfo info){ + setAudioInfo(info); + return true; + } + + /// Add an additional AudioOutput output + void add(AudioOutput &out){ + vector.push_back(&out); + } + + /// Add an AudioStream to the output + void add(AudioStream &stream){ + AdapterAudioStreamToAudioOutput* out = new AdapterAudioStreamToAudioOutput(stream); + vector.push_back(out); + } + + void add(Print &print){ + AdapterPrintToAudioOutput* out = new AdapterPrintToAudioOutput(print); + vector.push_back(out); + } + + void flush() { + for (int j=0;jflush(); + } + } + + void setAudioInfo(AudioInfo info){ + for (int j=0;jsetAudioInfo(info); + } + } + + size_t write(const uint8_t *buffer, size_t size){ + for (int j=0;j0){ + int written = vector[j]->write(buffer+start, open); + open -= written; + start += written; + } + } + return size; + } + + size_t write(uint8_t ch){ + for (int j=0;j0){ + open -= vector[j]->write(ch); + } + } + return 1; + } + + protected: + Vector vector; + +}; + + + +/** + * @brief AudioStream class that can define a start and (an optional) stop time + * Usually it is used to wrap an Audio Sink (e.g. I2SStream), but wrapping an + * Audio Source is supported as well. Only wrap classes which represent PCM + * data! + * @ingroup transform + * @author Phil Schatzmann + * @copyright GPLv3 + */ +class TimedStream : public AudioStream { + public: + TimedStream(AudioStream &io, long startSeconds = 0, long endSeconds = -1) { + p_stream = &io; + p_print = &io; + p_info = &io; + setStartSecond(startSeconds); + setEndSecond(endSeconds); + } + + TimedStream(AudioOutput &o, long startSeconds = 0, long endSeconds = -1) { + p_print = &o; + p_info = &o; + setStartSecond(startSeconds); + setEndSecond(endSeconds); + } + + /// Defines the start time in seconds. The audio before the start time will be + /// skipped + void setStartSecond(long startSeconds) { start_seconds = startSeconds; } + + /// Defines (an optional) the end time in seconds. After the end time no audio + /// is played and available() will return 0 + void setEndSecond(long endSeconds) { end_seconds = endSeconds; } + + /// Returns true if we are in a valid time range and are still playing sound + bool isPlaying() { + if (current_bytes < start_bytes) return false; + if (end_bytes > 0 && current_bytes > end_bytes) return false; + return true; + } + + /// Returns true if we are not past the end time; + bool isActive() { + return (current_bytes < end_bytes && current_bytes > start_bytes); + } + + bool begin(AudioInfo info) { + setAudioInfo(info); + return begin(); + } + + bool begin() override { + calculateByteLimits(); + current_bytes = 0; + return true; + } + + operator bool() { return isActive(); } + + /// Provides only data for the indicated start and end time. Only supported + /// for data which does not contain any heder information: so PCM, mp3 should + /// work! + size_t readBytes(uint8_t *buffer, size_t length) override { + // if reading is not supported we stop + if (p_stream == nullptr) return 0; + // if we are past the end we stop + if (!isActive()) return 0; + // read the data now + size_t result = 0; + do { + result = p_stream->readBytes(buffer, length); + current_bytes += length; + // ignore data before start time + } while (result > 0 && current_bytes < start_bytes); + return isPlaying() ? result : 0; + } + + /// Plays only data for the indiated start and end time + size_t write(const uint8_t *buffer, size_t length) override { + current_bytes += length; + return isPlaying() ? p_print->write(buffer, length) : length; + } + + /// Provides the available bytes until the end time has reached + int available() override { + if (p_stream == nullptr) return 0; + return isActive() ? p_stream->available() : 0; + } + + /// Updates the AudioInfo in the current object and in the source or target + void setAudioInfo(AudioInfo info) override { + AudioStream::setAudioInfo(info); + p_info->setAudioInfo(info); + calculateByteLimits(); + } + + int availableForWrite() override { return p_print->availableForWrite(); } + + /// Experimental: if used on mp3 you can set the compression ratio e.g. to 11 + /// which will be used to approximate the time + void setCompressionRatio(float ratio) { compression_ratio = ratio; } + + /// Calculates the bytes per second from the AudioInfo + int bytesPerSecond() { + return info.sample_rate * info.channels * info.bits_per_sample / 8; + } + + protected: + Stream *p_stream = nullptr; + Print *p_print = nullptr; + AudioInfoSupport *p_info = nullptr; + uint32_t start_seconds = 0; + uint32_t end_seconds = UINT32_MAX; + uint32_t start_bytes = 0; + uint32_t end_bytes = UINT32_MAX; + uint32_t current_bytes = 0; + float compression_ratio = 1.0; + + void calculateByteLimits() { + int bytes_per_second = bytesPerSecond(); + if (bytes_per_second > 0) { + start_bytes = bytes_per_second * start_seconds / compression_ratio; + end_bytes = bytes_per_second * end_seconds / compression_ratio; + } else { + LOGE("AudioInfo not defined"); + } + } +}; + +} + + diff --git a/src/AudioTools/AudioLibs/All.h b/src/AudioTools/AudioLibs/All.h deleted file mode 100644 index 5b40c0015e..0000000000 --- a/src/AudioTools/AudioLibs/All.h +++ /dev/null @@ -1,43 +0,0 @@ -#pragma once -// include to identify future compile errors: if you don't have all dependencies -// installed this will generate a lot of compile errors! -#include "MozziStream.h" -#include "A2DPStream.h" -#include "AudioBoardStream.h" -#include "AudioClientRTSP.h" -#include "AudioEffectsSuite.h" -#include "AudioFFT.h" -#include "AudioSTK.h" -#include "Concurrency.h" -#include "FFTEffects.h" -#include "HLSStream.h" -#include "I2SCodecStream.h" -#include "LEDOutput.h" -#include "MaximilianDSP.h" -#include "MemoryManager.h" -#include "PIDController.h" -#include "R2ROutput.h" -#include "SPDIFOutput.h" -#include "StdioStream.h" -#include "VBANStream.h" -#include "VS1053Stream.h" -// #include "TfLiteAudioStream.h" // takes too much time -// #include "AudioServerEx.h" -// #include "WM8960Stream.h" // driver part of AudioBoardStream -// #include "AudioFaust.h" -// #include "RTSP.h" // conflit with AudioClientRTSP -// #include "AudioESP32ULP.h" // using obsolete functioinality -// #include "PureDataStream.h" -// #include "Jupyter.h" // only for desktop -// #include "PortAudioStream.h" // only for desktop -// #include "MiniAudioStream.h" // only for desktop -// #include "AudioKissFFT.h" // select on fft implementation -// #include "AudioCmsisFFT.h" // select on fft implementation -// #include "AudioRealFFT.h" // select on fft implementation -// #include "AudioESP32FFT.h" // select on fft implementation -// #include "AudioEspressifFFT.h" // select on fft implementation -// #include "FFTDisplay.h" -// #include "LEDOutputUnoR4.h" // only for uno r4 -// #include "AudioMP34DT05.h" // only for nano ble sense -// #include "AudioESP8266.h" -// #include "AudioKit.h" // obsolete diff --git a/src/AudioTools/AudioLibs/AudioBoardStream.h b/src/AudioTools/AudioLibs/AudioBoardStream.h deleted file mode 100644 index 75de451ba3..0000000000 --- a/src/AudioTools/AudioLibs/AudioBoardStream.h +++ /dev/null @@ -1,421 +0,0 @@ -#pragma once - -#include "AudioToolsConfig.h" -#include "AudioTools/AudioLibs/I2SCodecStream.h" -#include "AudioTools/CoreAudio/AudioActions.h" - -namespace audio_tools { - -/** - * @brief New functionality which replaces the AudioKitStream that is based on - * the legacy AudioKit library. This functionality uses the new - * arduino-audio-driver library! It is the same as I2SCodecStream extended by - * some AudioActions and some method calls to determine defined pin values. - * See https://github.com/pschatzmann/arduino-audio-driver - * @ingroup io - * @author Phil Schatzmann - * @copyright GPLv3 - */ -class AudioBoardStream : public I2SCodecStream { - struct AudioBoardAction : public AudioActions::Action { - AudioBoardAction(AudioBoard &board, AudioDriverKey key) { - this->key = key; - this->p_board = &board; - } - AudioDriverKey key; - AudioBoard *p_board; - int id() override { return key | 0x400; } - bool readValue() override { return p_board->isKeyPressed(key); } - }; - - public: - /** - * @brief Default constructor: for available AudioBoard values check - * the audioboard variables in - * https://pschatzmann.github.io/arduino-audio-driver/html/group__audio__driver.html - * Further information can be found in - * https://github.com/pschatzmann/arduino-audio-driver/wiki - */ - AudioBoardStream(audio_driver::AudioBoard &board) : I2SCodecStream(board) { - // pin mode already set up by driver library - actions.setPinMode(false); - } - - bool begin() override { return I2SCodecStream::begin(); } - - bool begin(I2SCodecConfig cfg) override { return I2SCodecStream::begin(cfg); } - - /** - * @brief Process input keys and pins - * - */ - void processActions() { - // TRACED(); - actions.processActions(); - delay(1); - } - - - /** - * @brief Defines a new action that is executed when the Button is pressed - */ - void addAction(AudioDriverKey key, void (*action)(bool, int, void *), - void *ref = nullptr) { - AudioBoardAction *abo = new AudioBoardAction(board(), key); - abo->actionOn = action; - abo->ref = (ref == nullptr) ? this : ref; - actions.add(*abo); - } - - /** - * @brief Defines a new action that is executed when the Button is pressed and released - */ - void addAction(AudioDriverKey key, void (*actionOn)(bool, int, void *), - void (*actionOff)(bool, int, void *), - void *ref = nullptr) { - - AudioBoardAction *abo = new AudioBoardAction(board(), key); - abo->actionOn = actionOn; - abo->actionOn = actionOff; - abo->ref = (ref == nullptr) ? this : ref; - actions.add(*abo); - } - - /** - * @brief Defines a new action that is executed when the indicated pin is - * active - * - * @param pin - * @param action - * @param ref - */ - void addAction(int pin, void (*action)(bool, int, void *), - void *ref = nullptr) { - TRACEI(); - // determine logic from config - AudioActions::ActiveLogic activeLogic = getActionLogic(pin); - actions.add(pin, action, activeLogic, ref == nullptr ? this : ref); - } - - /** - * @brief Defines a new action that is executed when the indicated pin is - * active - * - * @param pin - * @param action - * @param activeLogic - * @param ref - */ - void addAction(int pin, void (*action)(bool, int, void *), - AudioActions::ActiveLogic activeLogic, void *ref = nullptr) { - TRACEI(); - actions.add(pin, action, activeLogic, ref == nullptr ? this : ref); - } - - /// Provides access to the AudioActions - AudioActions &audioActions() { return actions; } - - AudioActions &getActions() { return actions; } - - /** - * @brief Relative volume control - * - * @param vol - */ - void incrementVolume(float inc) { - float current_volume = getVolume(); - float new_volume = current_volume + inc; - LOGI("incrementVolume: %f -> %f", current_volume, new_volume); - setVolume(new_volume); - } - - /** - * @brief Increase the volume - * - */ - static void actionVolumeUp(bool, int, void *ref) { - TRACEI(); - AudioBoardStream *self = (AudioBoardStream *)ref; - self->incrementVolume(+self->actionVolumeIncrementValue()); - } - - /** - * @brief Decrease the volume - * - */ - static void actionVolumeDown(bool, int, void *ref) { - TRACEI(); - AudioBoardStream *self = (AudioBoardStream *)ref; - self->incrementVolume(-self->actionVolumeIncrementValue()); - } - - /** - * @brief Toggle start stop - * - */ - static void actionStartStop(bool, int, void *ref) { - TRACEI(); - AudioBoardStream *self = (AudioBoardStream *)ref; - self->active = !self->active; - self->setActive(self->active); - } - - /** - * @brief Start - * - */ - static void actionStart(bool, int, void *ref) { - TRACEI(); - AudioBoardStream *self = (AudioBoardStream *)ref; - self->active = true; - self->setActive(self->active); - } - - /** - * @brief Stop - */ - static void actionStop(bool, int, void *ref) { - TRACEI(); - AudioBoardStream *self = (AudioBoardStream *)ref; - self->active = false; - self->setActive(self->active); - } - - /** - * @brief Switch off the PA if the headphone in plugged in - * and switch it on again if the headphone is unplugged. - * This method complies with the - */ - static void actionHeadphoneDetection(bool, int, void *ref) { - AudioBoardStream *self = (AudioBoardStream *)ref; - if (self->pinHeadphoneDetect() >= 0) { - // detect changes - bool isConnected = self->headphoneStatus(); - if (self->headphoneIsConnected != isConnected) { - self->headphoneIsConnected = isConnected; - - // update if things have stabilized - bool powerActive = !isConnected; - LOGW("Headphone jack has been %s", - isConnected ? "inserted" : "removed"); - self->setSpeakerActive(powerActive); - } - } - delay(1); - } - - /** - * @brief Get the gpio number for auxin detection - * - * @return -1 non-existent - * Others gpio number - */ - GpioPin pinAuxin() { return getPinID(PinFunction::AUXIN_DETECT); } - - /** - * @brief Get the gpio number for headphone detection - * - * @return -1 non-existent - * Others gpio number - */ - GpioPin pinHeadphoneDetect() { - return getPinID(PinFunction::HEADPHONE_DETECT); - } - - /** - * @brief Get the gpio number for PA enable - * - * @return -1 non-existent - * Others gpio number - */ - GpioPin pinPaEnable() { return getPinID(PinFunction::PA); } - - // /** - // * @brief Get the gpio number for adc detection - // * - // * @return -1 non-existent - // * Others gpio number - // */ - // GpioPin pinAdcDetect() { return getPin(AUXIN_DETECT); } - - /** - * @brief Get the record-button id for adc-button - * - * @return -1 non-existent - * Others button id - */ - GpioPin pinInputRec() { return getPinID(PinFunction::KEY, 1); } - - /** - * @brief Get the number for mode-button - * - * @return -1 non-existent - * Others number - */ - GpioPin pinInputMode() { return getPinID(PinFunction::KEY, 2); } - - /** - * @brief Get number for set function - * - * @return -1 non-existent - * Others number - */ - GpioPin pinInputSet() { return getPinID(PinFunction::KEY, 4); } - - /** - * @brief Get number for play function - * - * @return -1 non-existent - * Others number - */ - GpioPin pinInputPlay() { return getPinID(PinFunction::KEY, 3); } - - /** - * @brief number for volume up function - * - * @return -1 non-existent - * Others number - */ - GpioPin pinVolumeUp() { return getPinID(PinFunction::KEY, 6); } - - /** - * @brief Get number for volume down function - * - * @return -1 non-existent - * Others number - */ - GpioPin pinVolumeDown() { return getPinID(PinFunction::KEY, 5); } - - /** - * @brief Get LED pin - * - * @return -1 non-existent - * Others gpio number - */ - GpioPin pinLed(int idx) { return getPinID(PinFunction::LED, idx); } - - /// the same as setPAPower() - void setSpeakerActive(bool active) { setPAPower(active); } - - /** - * @brief Returns true if the headphone was detected - * - * @return true - * @return false - */ - bool headphoneStatus() { - int headphoneGpioPin = pinHeadphoneDetect(); - return headphoneGpioPin > 0 ? !digitalRead(headphoneGpioPin) : false; - } - - /** - * @brief The oposite of setMute(): setActive(true) calls setMute(false) - */ - void setActive(bool active) { setMute(!active); } - - /// add start/stop on inputMode - void addStartStopAction() { - // pin conflicts for pinInputMode() with the SD CS pin for AIThinker and - // buttons - int sd_cs = getSdCsPin(); - int input_mode = pinInputMode(); - if (input_mode != -1 && (input_mode != sd_cs || !cfg.sd_active)) { - LOGD("actionInputMode") - addAction(input_mode, actionStartStop); - } - } - - /// add volume up and volume down action - void addVolumeActions() { - // pin conflicts with SD Lyrat SD CS GpioPin and buttons / Conflict on - // Audiokit V. 2957 - int sd_cs = getSdCsPin(); - int vol_up = pinVolumeUp(); - int vol_down = pinVolumeDown(); - if ((vol_up != -1 && vol_down != -1) && - (!cfg.sd_active || (vol_down != sd_cs && vol_up != sd_cs))) { - LOGD("actionVolumeDown") - addAction(vol_down, actionVolumeDown); - LOGD("actionVolumeUp") - addAction(vol_up, actionVolumeUp); - } else { - LOGW("Volume Buttons ignored because of conflict: %d ", pinVolumeDown()); - } - } - - /// Adds headphone determination - void addHeadphoneDetectionAction() { - // pin conflicts with AIThinker A101: key6 and headphone detection - int head_phone = pinHeadphoneDetect(); - if (head_phone != -1 && (getPinID(PinFunction::KEY, 6) != head_phone)) { - actions.add(head_phone, actionHeadphoneDetection, - AudioActions::ActiveChange, this); - } - } - - /** - * @brief Setup the supported default actions (volume, start/stop, headphone - * detection) - */ - void addDefaultActions() { - TRACEI(); - addHeadphoneDetectionAction(); - addStartStopAction(); - addVolumeActions(); - } - - /// Defines the increment value used by actionVolumeDown/actionVolumeUp - void setActionVolumeIncrementValue(float value) { - action_increment_value = value; - } - - float actionVolumeIncrementValue() { return action_increment_value; } - - bool isKeyPressed(int key) { - if (!board()) return false; - return board().isKeyPressed(key); - } - - protected: - AudioActions actions; - bool headphoneIsConnected = false; - bool active = true; - float action_increment_value = 0.02; - - int getSdCsPin() { - static GpioPin sd_cs = -2; - // execute only once - if (sd_cs != -2) return sd_cs; - - auto sd_opt = getPins().getSPIPins(PinFunction::SD); - if (sd_opt) { - sd_cs = sd_opt.value().cs; - } else { - // no spi -> no sd - LOGI("No sd defined -> sd_active=false") - cfg.sd_active = false; - sd_cs = -1; - } - return sd_cs; - } - - /// Determines the action logic (ActiveLow or ActiveTouch) for the pin - AudioActions::ActiveLogic getActionLogic(int pin) { - auto opt = board().getPins().getPin(pin); - PinLogic logic = PinLogic::Input; - if (opt) logic = opt.value().pin_logic; - switch (logic) { - case PinLogic::Input: - case PinLogic::InputActiveLow: - return AudioActions::ActiveLow; - case PinLogic::InputActiveHigh: - return AudioActions::ActiveHigh; - case PinLogic::InputActiveTouch: - return AudioActions::ActiveTouch; - default: - return AudioActions::ActiveLow; - } - } -}; - -} // namespace audio_tools diff --git a/src/AudioTools/AudioLibs/AudioClientRTSP.h b/src/AudioTools/AudioLibs/AudioClientRTSP.h deleted file mode 100644 index 27517807a1..0000000000 --- a/src/AudioTools/AudioLibs/AudioClientRTSP.h +++ /dev/null @@ -1,721 +0,0 @@ - -#pragma once - -/** -This library is free software; you can redistribute it and/or modify it under -the terms of the GNU Lesser General Public License as published by the -Free Software Foundation; either version 3 of the License, or (at your -option) any later version. (See .) - -This library is distributed in the hope that it will be useful, but WITHOUT -ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS -FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for -more details. - -You should have received a copy of the GNU Lesser General Public License -along with this library; if not, write to the Free Software Foundation, Inc., -51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA -**/ - -// Copyright (c) 1996-2023, Live Networks, Inc. All rights reserved -// A demo application, showing how to create and run a RTSP client (that can -// potentially receive multiple streams concurrently). -// - -#include "AudioLogger.h" -#include "Print.h" // Arduino Print -// include live555 -#include "BasicUsageEnvironment.hh" -//#include "liveMedia.hh" -#include "RTSPClient.hh" - -// By default, we request that the server stream its data using RTP/UDP. -// If, instead, you want to request that the server stream via RTP-over-TCP, -// change the following to True: -#define REQUEST_STREAMING_OVER_TCP false - -// by default, print verbose output from each "RTSPClient" -#define RTSP_CLIENT_VERBOSITY_LEVEL 1 -// Even though we're not going to be doing anything with the incoming data, we -// still need to receive it. Define the size of the buffer that we'll use: -#define RTSP_SINK_BUFFER_SIZE 1024 - -// If you don't want to see debugging output for each received frame, then -// comment out the following line: -#undef DEBUG_PRINT_EACH_RECEIVED_FRAME -#define DEBUG_PRINT_EACH_RECEIVED_FRAME 0 - -/// @brief AudioTools internal: rtsp -namespace audiotools_rtsp { - -class OurRTSPClient; -// The main streaming routine (or each "rtsp://" URL): -OurRTSPClient * openURL(UsageEnvironment& env, char const* progName, char const* rtspURL); -// Counts how many streams (i.e., "RTSPClient"s) are currently in use. -static unsigned rtspClientCount = 0; -static char rtspEventLoopWatchVariable = 0; -static Print* rtspOutput = nullptr; -static uint32_t rtspSinkReceiveBufferSize = 0; -static bool rtspUseTCP = REQUEST_STREAMING_OVER_TCP; - -} // namespace audiotools_rtsp - -namespace audio_tools { - -/** - * @brief A simple RTSPClient using https://github.com/pschatzmann/arduino-live555 - * @ingroup communications - * @author Phil Schatzmann - * @copyright GPLv3 -*/ -class AudioClientRTSP { - public: - AudioClientRTSP(uint32_t receiveBufferSize = RTSP_SINK_BUFFER_SIZE, bool useTCP=REQUEST_STREAMING_OVER_TCP, bool blocking = false) { - setBufferSize(receiveBufferSize); - useTCP ? setTCP() : setUDP(); - setBlocking(blocking); - } - - void setBufferSize(int size){ - audiotools_rtsp::rtspSinkReceiveBufferSize = size; - } - - void setTCP(){ - audiotools_rtsp::rtspUseTCP = true; - } - - void setUDP(){ - audiotools_rtsp::rtspUseTCP = false; - } - - void setBlocking(bool flag){ - is_blocking = flag; - } - - /// login to wifi: optional convinience method. You can also just start Wifi the normal way - void setLogin(const char* ssid, const char* password){ - this->ssid = ssid; - this->password = password; - } - - /// Starts the processing - bool begin(const char* url, Print &out) { - audiotools_rtsp::rtspOutput = &out; - if (url==nullptr) { - return false; - } - if (!login()){ - LOGE("wifi down"); - return false; - } - // Begin by setting up our usage environment: - scheduler = BasicTaskScheduler::createNew(); - env = BasicUsageEnvironment::createNew(*scheduler); - - // There are argc-1 URLs: argv[1] through argv[argc-1]. Open and start - // streaming each one: - rtsp_client = audiotools_rtsp::openURL(*env, "RTSPClient", url); - - // All subsequent activity takes place within the event loop: - if (is_blocking) env->taskScheduler().doEventLoop(&audiotools_rtsp::rtspEventLoopWatchVariable); - // This function call does not return, unless, at some point in time, - // "rtspEventLoopWatchVariable" gets set to something non-zero. - - return true; - } - - /// to be called in Arduino loop when blocking = false - void loop() { - if (audiotools_rtsp::rtspEventLoopWatchVariable==0) scheduler->SingleStep(); - } - - void end() { - audiotools_rtsp::rtspEventLoopWatchVariable = 1; - env->reclaim(); - env = NULL; - delete scheduler; - scheduler = NULL; - bool is_blocking = false; - } - - audiotools_rtsp::OurRTSPClient *client() { - return rtsp_client; - } - - protected: - audiotools_rtsp::OurRTSPClient* rtsp_client; - UsageEnvironment* env=nullptr; - BasicTaskScheduler* scheduler=nullptr; - const char* ssid=nullptr; - const char* password = nullptr; - bool is_blocking = false; - - /// login to wifi: optional convinience method. You can also just start Wifi the normal way - bool login(){ - if(WiFi.status() != WL_CONNECTED && ssid!=nullptr && password!=nullptr){ - WiFi.mode(WIFI_STA); - WiFi.begin(ssid, password); - while(WiFi.status() != WL_CONNECTED){ - Serial.print("."); - delay(100); - } - Serial.println(); - Serial.print("Local Address: "); - Serial.println(WiFi.localIP()); - } - return WiFi.status() == WL_CONNECTED; - } - - -}; - -} // namespace audio_tools - -namespace audiotools_rtsp { -// Define a class to hold per-stream state that we maintain throughout each -// stream's lifetime: - -// Forward function definitions: - -// RTSP 'response handlers': -void continueAfterDESCRIBE(RTSPClient* rtspClient, int resultCode, - char* resultString); -void continueAfterSETUP(RTSPClient* rtspClient, int resultCode, - char* resultString); -void continueAfterPLAY(RTSPClient* rtspClient, int resultCode, - char* resultString); - -// Other event handler functions: -void subsessionAfterPlaying( - void* clientData); // called when a stream's subsession (e.g., audio or - // video substream) ends -void subsessionByeHandler(void* clientData, char const* reason); -// called when a RTCP "BYE" is received for a subsession -void streamTimerHandler(void* clientData); -// called at the end of a stream's expected duration (if the stream has not -// already signaled its end using a RTCP "BYE") - -// Used to iterate through each stream's 'subsessions', setting up each one: -void setupNextSubsession(RTSPClient* rtspClient); - -// Used to shut down and close a stream (including its "RTSPClient" object): -void shutdownStream(RTSPClient* rtspClient, int exitCode = 1); - -// A function that outputs a string that identifies each stream (for debugging -// output). Modify this if you wish: -UsageEnvironment& operator<<(UsageEnvironment& env, - const RTSPClient& rtspClient) { - return env << "[URL:\"" << rtspClient.url() << "\"]: "; -} - -// A function that outputs a string that identifies each subsession (for -// debugging output). Modify this if you wish: -UsageEnvironment& operator<<(UsageEnvironment& env, - const MediaSubsession& subsession) { - return env << subsession.mediumName() << "/" << subsession.codecName(); -} - -class StreamClientState { - public: - StreamClientState(); - virtual ~StreamClientState(); - - public: - MediaSubsessionIterator* iter; - MediaSession* session; - MediaSubsession* subsession; - TaskToken streamTimerTask; - double duration; -}; - -// If you're streaming just a single stream (i.e., just from a single URL, -// once), then you can define and use just a single "StreamClientState" -// structure, as a global variable in your application. However, because - in -// this demo application - we're showing how to play multiple streams, -// concurrently, we can't do that. Instead, we have to have a separate -// "StreamClientState" structure for each "RTSPClient". To do this, we subclass -// "RTSPClient", and add a "StreamClientState" field to the subclass: - -class OurRTSPClient : public RTSPClient { - public: - static OurRTSPClient* createNew(UsageEnvironment& env, char const* rtspURL, - int verbosityLevel = 0, - char const* applicationName = NULL, - portNumBits tunnelOverHTTPPortNum = 0); - - protected: - OurRTSPClient(UsageEnvironment& env, char const* rtspURL, int verbosityLevel, - char const* applicationName, portNumBits tunnelOverHTTPPortNum); - // called only by createNew(); - virtual ~OurRTSPClient(); - - public: - StreamClientState scs; -}; - -// Define a data sink (a subclass of "MediaSink") to receive the data for each -// subsession (i.e., each audio or video 'substream'). In practice, this might -// be a class (or a chain of classes) that decodes and then renders the incoming -// audio or video. Or it might be a "FileSink", for outputting the received data -// into a file (as is done by the "openRTSP" application). In this example code, -// however, we define a simple 'dummy' sink that receives incoming data, but -// does nothing with it. - -class OurSink : public MediaSink { - public: - static OurSink* createNew( - UsageEnvironment& env, - MediaSubsession& - subsession, // identifies the kind of data that's being received - char const* streamId = NULL); // identifies the stream itself (optional) - - private: - OurSink(UsageEnvironment& env, MediaSubsession& subsession, - char const* streamId); - // called only by "createNew()" - virtual ~OurSink(); - - static void afterGettingFrame(void* clientData, unsigned frameSize, - unsigned numTruncatedBytes, - struct timeval presentationTime, - unsigned durationInMicroseconds); - void afterGettingFrame(unsigned frameSize, unsigned numTruncatedBytes, - struct timeval presentationTime, - unsigned durationInMicroseconds); - - private: - // redefined virtual functions: - virtual Boolean continuePlaying(); - - private: - u_int8_t* fReceiveBuffer; - MediaSubsession& fSubsession; - char* fStreamId; -}; - -OurRTSPClient* openURL(UsageEnvironment& env, char const* progName, char const* rtspURL) { - // Begin by creating a "RTSPClient" object. Note that there is a separate - // "RTSPClient" object for each stream that we wish to receive (even if more - // than stream uses the same "rtsp://" URL). - OurRTSPClient* rtspClient = OurRTSPClient::createNew( - env, rtspURL, RTSP_CLIENT_VERBOSITY_LEVEL, progName); - if (rtspClient == NULL) { - env << "Failed to create a RTSP client for URL \"" << rtspURL - << "\": " << env.getResultMsg() << "\n"; - return nullptr; - } - - ++rtspClientCount; - - // Next, send a RTSP "DESCRIBE" command, to get a SDP description for the - // stream. Note that this command - like all RTSP commands - is sent - // asynchronously; we do not block, waiting for a response. Instead, the - // following function call returns immediately, and we handle the RTSP - // response later, from within the event loop: - rtspClient->sendDescribeCommand(continueAfterDESCRIBE); - return rtspClient; -} - -// Implementation of the RTSP 'response handlers': - -void continueAfterDESCRIBE(RTSPClient* rtspClient, int resultCode, - char* resultString) { - do { - UsageEnvironment& env = rtspClient->envir(); // alias - StreamClientState& scs = ((OurRTSPClient*)rtspClient)->scs; // alias - - if (resultCode != 0) { - env << *rtspClient << "Failed to get a SDP description: " << resultString - << "\n"; - delete[] resultString; - break; - } - - char* const sdpDescription = resultString; - env << *rtspClient << "Got a SDP description:\n" << sdpDescription << "\n"; - - // Create a media session object from this SDP description: - scs.session = MediaSession::createNew(env, sdpDescription); - delete[] sdpDescription; // because we don't need it anymore - if (scs.session == NULL) { - env << *rtspClient - << "Failed to create a MediaSession object from the SDP description: " - << env.getResultMsg() << "\n"; - break; - } else if (!scs.session->hasSubsessions()) { - env << *rtspClient - << "This session has no media subsessions (i.e., no \"m=\" lines)\n"; - break; - } - - // Then, create and set up our data source objects for the session. We do - // this by iterating over the session's 'subsessions', calling - // "MediaSubsession::initiate()", and then sending a RTSP "SETUP" command, - // on each one. (Each 'subsession' will have its own data source.) - scs.iter = new MediaSubsessionIterator(*scs.session); - setupNextSubsession(rtspClient); - return; - } while (0); - - // An unrecoverable error occurred with this stream. - shutdownStream(rtspClient); -} - -void setupNextSubsession(RTSPClient* rtspClient) { - UsageEnvironment& env = rtspClient->envir(); // alias - StreamClientState& scs = ((OurRTSPClient*)rtspClient)->scs; // alias - - scs.subsession = scs.iter->next(); - if (scs.subsession != NULL) { - if (!scs.subsession->initiate()) { - env << *rtspClient << "Failed to initiate the \"" << *scs.subsession - << "\" subsession: " << env.getResultMsg() << "\n"; - setupNextSubsession( - rtspClient); // give up on this subsession; go to the next one - } else { - env << *rtspClient << "Initiated the \"" << *scs.subsession - << "\" subsession ("; - if (scs.subsession->rtcpIsMuxed()) { - env << "client port " << scs.subsession->clientPortNum(); - } else { - env << "client ports " << scs.subsession->clientPortNum() << "-" - << scs.subsession->clientPortNum() + 1; - } - env << ")\n"; - - // Continue setting up this subsession, by sending a RTSP "SETUP" command: - rtspClient->sendSetupCommand(*scs.subsession, continueAfterSETUP, False, - rtspUseTCP); - } - return; - } - - // We've finished setting up all of the subsessions. Now, send a RTSP "PLAY" - // command to start the streaming: - if (scs.session->absStartTime() != NULL) { - // Special case: The stream is indexed by 'absolute' time, so send an - // appropriate "PLAY" command: - rtspClient->sendPlayCommand(*scs.session, continueAfterPLAY, - scs.session->absStartTime(), - scs.session->absEndTime()); - } else { - scs.duration = scs.session->playEndTime() - scs.session->playStartTime(); - rtspClient->sendPlayCommand(*scs.session, continueAfterPLAY); - } -} - -void continueAfterSETUP(RTSPClient* rtspClient, int resultCode, - char* resultString) { - do { - UsageEnvironment& env = rtspClient->envir(); // alias - StreamClientState& scs = ((OurRTSPClient*)rtspClient)->scs; // alias - - if (resultCode != 0) { - env << *rtspClient << "Failed to set up the \"" << *scs.subsession - << "\" subsession: " << resultString << "\n"; - break; - } - - env << *rtspClient << "Set up the \"" << *scs.subsession - << "\" subsession ("; - if (scs.subsession->rtcpIsMuxed()) { - env << "client port " << scs.subsession->clientPortNum(); - } else { - env << "client ports " << scs.subsession->clientPortNum() << "-" - << scs.subsession->clientPortNum() + 1; - } - env << ")\n"; - - // Having successfully setup the subsession, create a data sink for it, and - // call "startPlaying()" on it. (This will prepare the data sink to receive - // data; the actual flow of data from the client won't start happening until - // later, after we've sent a RTSP "PLAY" command.) - - scs.subsession->sink = - OurSink::createNew(env, *scs.subsession, rtspClient->url()); - // perhaps use your own custom "MediaSink" subclass instead - if (scs.subsession->sink == NULL) { - env << *rtspClient << "Failed to create a data sink for the \"" - << *scs.subsession << "\" subsession: " << env.getResultMsg() << "\n"; - break; - } - - env << *rtspClient << "Created a data sink for the \"" << *scs.subsession - << "\" subsession\n"; - scs.subsession->miscPtr = - rtspClient; // a hack to let subsession handler functions get the - // "RTSPClient" from the subsession - scs.subsession->sink->startPlaying(*(scs.subsession->readSource()), - subsessionAfterPlaying, scs.subsession); - // Also set a handler to be called if a RTCP "BYE" arrives for this - // subsession: - if (scs.subsession->rtcpInstance() != NULL) { - scs.subsession->rtcpInstance()->setByeWithReasonHandler( - subsessionByeHandler, scs.subsession); - } - } while (0); - delete[] resultString; - - // Set up the next subsession, if any: - setupNextSubsession(rtspClient); -} - -void continueAfterPLAY(RTSPClient* rtspClient, int resultCode, - char* resultString) { - Boolean success = False; - - do { - UsageEnvironment& env = rtspClient->envir(); // alias - StreamClientState& scs = ((OurRTSPClient*)rtspClient)->scs; // alias - - if (resultCode != 0) { - env << *rtspClient << "Failed to start playing session: " << resultString - << "\n"; - break; - } - - // Set a timer to be handled at the end of the stream's expected duration - // (if the stream does not already signal its end using a RTCP "BYE"). This - // is optional. If, instead, you want to keep the stream active - e.g., so - // you can later 'seek' back within it and do another RTSP "PLAY" - then you - // can omit this code. (Alternatively, if you don't want to receive the - // entire stream, you could set this timer for some shorter value.) - if (scs.duration > 0) { - unsigned const delaySlop = - 2; // number of seconds extra to delay, after the stream's expected - // duration. (This is optional.) - scs.duration += delaySlop; - unsigned uSecsToDelay = (unsigned)(scs.duration * 1000000); - scs.streamTimerTask = env.taskScheduler().scheduleDelayedTask( - uSecsToDelay, (TaskFunc*)streamTimerHandler, rtspClient); - } - - env << *rtspClient << "Started playing session"; - if (scs.duration > 0) { - env << " (for up to " << scs.duration << " seconds)"; - } - env << "...\n"; - - success = True; - } while (0); - delete[] resultString; - - if (!success) { - // An unrecoverable error occurred with this stream. - shutdownStream(rtspClient); - } -} - -// Implementation of the other event handlers: - -void subsessionAfterPlaying(void* clientData) { - MediaSubsession* subsession = (MediaSubsession*)clientData; - RTSPClient* rtspClient = (RTSPClient*)(subsession->miscPtr); - - // Begin by closing this subsession's stream: - Medium::close(subsession->sink); - subsession->sink = NULL; - - // Next, check whether *all* subsessions' streams have now been closed: - MediaSession& session = subsession->parentSession(); - MediaSubsessionIterator iter(session); - while ((subsession = iter.next()) != NULL) { - if (subsession->sink != NULL) return; // this subsession is still active - } - - // All subsessions' streams have now been closed, so shutdown the client: - shutdownStream(rtspClient); -} - -void subsessionByeHandler(void* clientData, char const* reason) { - MediaSubsession* subsession = (MediaSubsession*)clientData; - RTSPClient* rtspClient = (RTSPClient*)subsession->miscPtr; - UsageEnvironment& env = rtspClient->envir(); // alias - - env << *rtspClient << "Received RTCP \"BYE\""; - if (reason != NULL) { - env << " (reason:\"" << reason << "\")"; - delete[] (char*)reason; - } - env << " on \"" << *subsession << "\" subsession\n"; - - // Now act as if the subsession had closed: - subsessionAfterPlaying(subsession); -} - -void streamTimerHandler(void* clientData) { - OurRTSPClient* rtspClient = (OurRTSPClient*)clientData; - StreamClientState& scs = rtspClient->scs; // alias - - scs.streamTimerTask = NULL; - - // Shut down the stream: - shutdownStream(rtspClient); -} - -void shutdownStream(RTSPClient* rtspClient, int exitCode) { - UsageEnvironment& env = rtspClient->envir(); // alias - StreamClientState& scs = ((OurRTSPClient*)rtspClient)->scs; // alias - - // First, check whether any subsessions have still to be closed: - if (scs.session != NULL) { - Boolean someSubsessionsWereActive = False; - MediaSubsessionIterator iter(*scs.session); - MediaSubsession* subsession; - - while ((subsession = iter.next()) != NULL) { - if (subsession->sink != NULL) { - Medium::close(subsession->sink); - subsession->sink = NULL; - - if (subsession->rtcpInstance() != NULL) { - subsession->rtcpInstance()->setByeHandler( - NULL, NULL); // in case the server sends a RTCP "BYE" while - // handling "TEARDOWN" - } - - someSubsessionsWereActive = True; - } - } - - if (someSubsessionsWereActive) { - // Send a RTSP "TEARDOWN" command, to tell the server to shutdown the - // stream. Don't bother handling the response to the "TEARDOWN". - rtspClient->sendTeardownCommand(*scs.session, NULL); - } - } - - env << *rtspClient << "Closing the stream.\n"; - Medium::close(rtspClient); - // Note that this will also cause this stream's "StreamClientState" structure - // to get reclaimed. - - if (--rtspClientCount == 0) { - // The final stream has ended, so exit the application now. - // (Of course, if you're embedding this code into your own application, you - // might want to comment this out, and replace it with - // "rtspEventLoopWatchVariable = 1;", so that we leave the LIVE555 event loop, - // and continue running "main()".) - // exit(exitCode); - rtspEventLoopWatchVariable = 1; - return; - } -} - -// Implementation of "OurRTSPClient": - -OurRTSPClient* OurRTSPClient::createNew(UsageEnvironment& env, - char const* rtspURL, int verbosityLevel, - char const* applicationName, - portNumBits tunnelOverHTTPPortNum) { - return new OurRTSPClient(env, rtspURL, verbosityLevel, applicationName, - tunnelOverHTTPPortNum); -} - -OurRTSPClient::OurRTSPClient(UsageEnvironment& env, char const* rtspURL, - int verbosityLevel, char const* applicationName, - portNumBits tunnelOverHTTPPortNum) - : RTSPClient(env, rtspURL, verbosityLevel, applicationName, - tunnelOverHTTPPortNum, -1) {} - -OurRTSPClient::~OurRTSPClient() {} - -// Implementation of "StreamClientState": - -StreamClientState::StreamClientState() - : iter(NULL), - session(NULL), - subsession(NULL), - streamTimerTask(NULL), - duration(0.0) {} - -StreamClientState::~StreamClientState() { - delete iter; - if (session != NULL) { - // We also need to delete "session", and unschedule "streamTimerTask" (if - // set) - UsageEnvironment& env = session->envir(); // alias - - env.taskScheduler().unscheduleDelayedTask(streamTimerTask); - Medium::close(session); - } -} - -// Implementation of "OurSink": - -OurSink* OurSink::createNew(UsageEnvironment& env, - MediaSubsession& subsession, - char const* streamId) { - return new OurSink(env, subsession, streamId); -} - -OurSink::OurSink(UsageEnvironment& env, MediaSubsession& subsession, - char const* streamId) - : MediaSink(env), fSubsession(subsession) { - fStreamId = strDup(streamId); - fReceiveBuffer = new u_int8_t[rtspSinkReceiveBufferSize]; -} - -OurSink::~OurSink() { - delete[] fReceiveBuffer; - delete[] fStreamId; -} - -void OurSink::afterGettingFrame(void* clientData, unsigned frameSize, - unsigned numTruncatedBytes, - struct timeval presentationTime, - unsigned durationInMicroseconds) { - OurSink* sink = (OurSink*)clientData; - sink->afterGettingFrame(frameSize, numTruncatedBytes, presentationTime, - durationInMicroseconds); -} - -void OurSink::afterGettingFrame(unsigned frameSize, - unsigned numTruncatedBytes, - struct timeval presentationTime, - unsigned /*durationInMicroseconds*/) { - // We've just received a frame of data. (Optionally) print out information - // about it: -#ifdef DEBUG_PRINT_EACH_RECEIVED_FRAME - if (fStreamId != NULL) envir() << "Stream \"" << fStreamId << "\"; "; - envir() << fSubsession.mediumName() << "/" << fSubsession.codecName() - << ":\tReceived " << frameSize << " bytes"; - if (numTruncatedBytes > 0) - envir() << " (with " << numTruncatedBytes << " bytes truncated)"; - char uSecsStr[6 + 1]; // used to output the 'microseconds' part of the - // presentation time - snprintf(uSecsStr,7 , "%06u", (unsigned)presentationTime.tv_usec); - envir() << ".\tPresentation time: " << (int)presentationTime.tv_sec << "." - << uSecsStr; - if (fSubsession.rtpSource() != NULL && - !fSubsession.rtpSource()->hasBeenSynchronizedUsingRTCP()) { - envir() << "!"; // mark the debugging output to indicate that this - // presentation time is not RTCP-synchronized - } -#ifdef DEBUG_PRINT_NPT - envir() << "\tNPT: " << fSubsession.getNormalPlayTime(presentationTime); -#endif - envir() << "\n"; -#endif - - // Decode the data - if (rtspOutput) { - size_t writtenSize = rtspOutput->write(fReceiveBuffer, frameSize); - assert(writtenSize == frameSize); - } - - // Then continue, to request the next frame of data: - continuePlaying(); -} - -Boolean OurSink::continuePlaying() { - if (fSource == NULL) return False; // sanity check (should not happen) - - // Request the next frame of data from our input source. "afterGettingFrame()" - // will get called later, when it arrives: - fSource->getNextFrame(fReceiveBuffer, rtspSinkReceiveBufferSize, - afterGettingFrame, this, onSourceClosure, this); - return True; -} - -} // namespace audiotools_rtsp \ No newline at end of file diff --git a/src/AudioTools/AudioLibs/AudioEspressifFFT.h b/src/AudioTools/AudioLibs/AudioEspressifFFT.h deleted file mode 100644 index ced49c665f..0000000000 --- a/src/AudioTools/AudioLibs/AudioEspressifFFT.h +++ /dev/null @@ -1,151 +0,0 @@ -#pragma once - -#include "AudioFFT.h" -#include "esp_dsp.h" - -/** - * @defgroup fft-dsp esp32-dsp - * @ingroup fft - * @brief FFT using esp32 esp-dsp library -**/ - -namespace audio_tools { - -/** - * @brief fft Driver for espressif dsp library: https://espressif-docs.readthedocs-hosted.com/projects/esp-dsp/en/latest/esp-dsp-apis.html - * @ingroup fft-dsp - * @author Phil Schatzmann - * @copyright GPLv3 - */ -class FFTDriverEspressifFFT : public FFTDriver { - public: - bool begin(int len) override { - this->len = len; - int alloc_size = len * 2; - fft_data.resize(alloc_size); - table_buffer.resize(CONFIG_DSP_MAX_FFT_SIZE); - assert(table_buffer.data() != nullptr); - assert(fft_data.data() != nullptr); - ret = dsps_fft2r_init_fc32(table_buffer.data(), CONFIG_DSP_MAX_FFT_SIZE); - if (ret != ESP_OK){ - LOGE("dsps_fft2r_init_fc32 %d", ret); - } - return fft_data.data()!=nullptr && ret == ESP_OK; - } - - void end() override { - dsps_fft2r_deinit_fc32(); - fft_data.resize(0); - table_buffer.resize(0); - } - - void setValue(int idx, float value) override { - if (idx=len) return false; - fft_data[pos*2] = real; - fft_data[pos*2+1] = img; - return true; - } - - bool setBin(int pos, FFTBin &bin) { return FFTDriver::setBin(pos, bin);} - - bool getBin(int pos, FFTBin &bin) override { - if (pos>=len) return false; - bin.real = fft_data[pos*2]; - bin.img = fft_data[pos*2+1]; - return true; - } - - bool isReverseFFT() override {return true;} - - bool isValid() override{ return fft_data.data()!=nullptr && ret==ESP_OK; } - - esp_err_t ret; - Vector fft_data{0}; - Vector table_buffer{0}; - int len=0; - -}; -/** - * @brief AudioFFT using FFTReal. The only specific functionality is the access to the dataArray - * @ingroup fft-dsp - * @author Phil Schatzmann - * @copyright GPLv3 - */ -class AudioEspressifFFT : public AudioFFTBase { - public: - AudioEspressifFFT():AudioFFTBase(new FFTDriverEspressifFFT()) {} - - /// Provides the complex array returned by the FFT - float *dataArray() { - return driverEx()->fft_data.data(); - } - - FFTDriverEspressifFFT* driverEx() { - return (FFTDriverEspressifFFT*)driver(); - } -}; - - -} \ No newline at end of file diff --git a/src/AudioTools/AudioLibs/AudioFFT.h b/src/AudioTools/AudioLibs/AudioFFT.h deleted file mode 100644 index 9b9431df10..0000000000 --- a/src/AudioTools/AudioLibs/AudioFFT.h +++ /dev/null @@ -1,633 +0,0 @@ -#pragma once - -#include "AudioTools/AudioLibs/FFT/FFTWindows.h" -#include "AudioTools/CoreAudio/AudioStreams.h" -#include "AudioTools/CoreAudio/MusicalNotes.h" - -/** - * @defgroup fft FFT - * @ingroup dsp - * @brief Fast Fourier Transform - **/ - -namespace audio_tools { - -// forward declaration -class AudioFFTBase; -static MusicalNotes AudioFFTNotes; - -/** - * @brief Result of the FFT - * @ingroup fft - */ -struct AudioFFTResult { - int bin; - float magnitude; - float frequency; - - int frequencyAsInt() { return round(frequency); } - const char *frequencyAsNote() { return AudioFFTNotes.note(frequency); } - const char *frequencyAsNote(float &diff) { - return AudioFFTNotes.note(frequency, diff); - } -}; - -/** - * @brief Configuration for AudioFFT. If there are more then 1 channel the - * channel_used is defining which channel is used to perform the fft on. - * @ingroup fft - */ -struct AudioFFTConfig : public AudioInfo { - AudioFFTConfig() { - channels = 2; - bits_per_sample = 16; - sample_rate = 44100; - } - /// Callback method which is called after we got a new result - void (*callback)(AudioFFTBase &fft) = nullptr; - /// Channel which is used as input - uint8_t channel_used = 0; - int length = 8192; - int stride = 0; - /// Optional window function for both fft and ifft - WindowFunction *window_function = nullptr; - /// Optional window function for fft only - WindowFunction *window_function_fft = nullptr; - /// Optional window function for ifft only - WindowFunction *window_function_ifft = nullptr; - /// TX_MODE = FFT, RX_MODE = IFFT - RxTxMode rxtx_mode = TX_MODE; - /// caller - void* ref = nullptr; -}; - -/// And individual FFT Bin -struct FFTBin { - float real; - float img; - - FFTBin() = default; - - FFTBin(float r, float i) { - real = r; - img = i; - } - - void multiply(float f) { - real *= f; - img *= f; - } - - void conjugate() { img = -img; } - - void clear() { real = img = 0.0f; } -}; - -/// Inverse FFT Overlapp Add -class FFTInverseOverlapAdder { - public: - FFTInverseOverlapAdder(int size = 0) { - if (size > 0) resize(size); - } - - /// Initilze data by defining new size - void resize(int size) { - // reset max for new scaling - rfft_max = 0.0; - // define new size - len = size; - data.resize(size); - for (int j = 0; j < data.size(); j++) { - data[j] = 0.0; - } - } - - // adds the values to the array (by applying the window function) - void add(float value, int pos, WindowFunction *window_function) { - float add_value = value; - if (window_function != nullptr) { - add_value = value * window_function->factor(pos); - } - assert(pos < len); - data[pos] += add_value; - } - - // gets the scaled audio data as result - void getStepData(float *result, int stride, float maxResult) { - for (int j = 0; j < stride; j++) { - // determine max value to scale - if (data[j] > rfft_max) rfft_max = data[j]; - } - for (int j = 0; j < stride; j++) { - result[j] = data[j] / rfft_max * maxResult; - // clip - if (result[j] > maxResult) { - result[j] = maxResult; - } - if (result[j] < -maxResult) { - result[j] = -maxResult; - } - } - // copy data to head - for (int j = 0; j < len - stride; j++) { - data[j] = data[j + stride]; - } - // clear tail - for (int j = len - stride; j < len; j++) { - data[j] = 0.0; - } - } - - /// provides the actual size - int size() { return data.size(); } - - protected: - Vector data{0}; - int len = 0; - float rfft_max = 0; -}; - -/** - * @brief Abstract Class which defines the basic FFT functionality - * @ingroup fft - * @author Phil Schatzmann - * @copyright GPLv3 - */ -class FFTDriver { - public: - virtual bool begin(int len) = 0; - virtual void end() = 0; - /// Sets the real value - virtual void setValue(int pos, float value) = 0; - /// Perform FFT - virtual void fft() = 0; - /// Calculate the magnitude (fft result) at index (sqr(i² + r²)) - virtual float magnitude(int idx) = 0; - /// Calculate the magnitude w/o sqare root - virtual float magnitudeFast(int idx) = 0; - virtual bool isValid() = 0; - /// Returns true if reverse FFT is supported - virtual bool isReverseFFT() { return false; } - /// Calculate reverse FFT - virtual void rfft() { LOGE("Not implemented"); } - /// Get result value from Reverse FFT - virtual float getValue(int pos) = 0; - /// sets the value of a bin - virtual bool setBin(int idx, float real, float img) { return false; } - /// sets the value of a bin - bool setBin(int pos, FFTBin &bin) { return setBin(pos, bin.real, bin.img); } - /// gets the value of a bin - virtual bool getBin(int pos, FFTBin &bin) { return false; } -}; - -/** - * @brief Executes FFT using audio data privded by write() and/or an inverse FFT - * where the samples are made available via readBytes(). The Driver which is - * passed in the constructor selects a specifc FFT implementation. - * @ingroup fft - * @author Phil Schatzmann - * @copyright GPLv3 - */ -class AudioFFTBase : public AudioStream { - public: - /// Default Constructor. The len needs to be of the power of 2 (e.g. 512, - /// 1024, 2048, 4096, 8192) - AudioFFTBase(FFTDriver *driver) { p_driver = driver; } - - ~AudioFFTBase() { end(); } - - /// Provides the default configuration - AudioFFTConfig defaultConfig(RxTxMode mode = TX_MODE) { - AudioFFTConfig info; - info.rxtx_mode = mode; - return info; - } - - /// starts the processing - bool begin(AudioFFTConfig info) { - cfg = info; - return begin(); - } - - /// starts the processing - bool begin() override { - bins = cfg.length / 2; - // define window functions - if (cfg.window_function_fft==nullptr) cfg.window_function_fft = cfg.window_function; - if (cfg.window_function_ifft==nullptr) cfg.window_function_ifft = cfg.window_function; - // define default stride value if not defined - if (cfg.stride == 0) cfg.stride = cfg.length; - - if (!isPowerOfTwo(cfg.length)) { - LOGE("Len must be of the power of 2: %d", cfg.length); - return false; - } - if (!p_driver->begin(cfg.length)) { - LOGE("Not enough memory"); - } - - if (cfg.window_function_fft != nullptr) { - cfg.window_function_fft->begin(cfg.length); - } - if (cfg.window_function_ifft != nullptr - && cfg.window_function_ifft != cfg.window_function_fft) { - cfg.window_function_ifft->begin(cfg.length); - } - - bool is_valid_rxtx = false; - if (cfg.rxtx_mode == TX_MODE || cfg.rxtx_mode == RXTX_MODE) { - // holds last N bytes that need to be reprocessed - stride_buffer.resize((cfg.length) * bytesPerSample()); - is_valid_rxtx = true; - } - if (cfg.rxtx_mode == RX_MODE || cfg.rxtx_mode == RXTX_MODE) { - rfft_data.resize(cfg.channels * bytesPerSample() * cfg.stride); - rfft_add.resize(cfg.length); - step_data.resize(cfg.stride); - is_valid_rxtx = true; - } - - if (!is_valid_rxtx){ - LOGE("Invalid rxtx_mode"); - return false; - } - - current_pos = 0; - return p_driver->isValid(); - } - - /// Just resets the current_pos e.g. to start a new cycle - void reset() { - current_pos = 0; - if (cfg.window_function_fft != nullptr) { - cfg.window_function_fft->begin(cfg.length); - } - if (cfg.window_function_ifft != nullptr) { - cfg.window_function_ifft->begin(cfg.length); - } - } - - operator bool() override { return p_driver != nullptr && p_driver->isValid(); } - - /// Notify change of audio information - void setAudioInfo(AudioInfo info) override { - cfg.bits_per_sample = info.bits_per_sample; - cfg.sample_rate = info.sample_rate; - cfg.channels = info.channels; - begin(cfg); - } - - /// Release the allocated memory - void end() override { - p_driver->end(); - l_magnitudes.resize(0); - rfft_data.resize(0); - rfft_add.resize(0); - step_data.resize(0); - } - - /// Provide the audio data as FFT input - size_t write(const uint8_t *data, size_t len) override { - size_t result = 0; - if (p_driver->isValid()) { - result = len; - switch (cfg.bits_per_sample) { - case 8: - processSamples(data, len); - break; - case 16: - processSamples(data, len / 2); - break; - case 24: - processSamples(data, len / 3); - break; - case 32: - processSamples(data, len / 4); - break; - default: - LOGE("Unsupported bits_per_sample: %d", cfg.bits_per_sample); - break; - } - } - return result; - } - - /// Provides the result of a reverse FFT - size_t readBytes(uint8_t *data, size_t len) override { - TRACED(); - if (rfft_data.size() == 0) return 0; - - // get data via callback if there is no more data - if (cfg.rxtx_mode == RX_MODE && cfg.callback != nullptr && rfft_data.available() == 0) { - cfg.callback(*this); - } - - // execute rfft when we consumed all data - if (has_rfft_data && rfft_data.available() == 0) { - rfft(); - } - return rfft_data.readArray(data, len); - } - - /// We try to fill the buffer at once - int availableForWrite() override { - return cfg.length * cfg.channels * bytesPerSample(); - } - - /// Data available for reverse fft - int available() override { - assert(cfg.stride != 0); - return cfg.stride * cfg.channels * bytesPerSample(); - } - - /// The number of bins used by the FFT which are relevant for the result - int size() { return bins; } - - /// The number of samples - int length() { return cfg.length; } - - /// time after the fft: time when the last result was provided - you can poll - /// this to check if we have a new result - unsigned long resultTime() { return timestamp; } - /// time before the fft - unsigned long resultTimeBegin() { return timestamp_begin; } - - - /// Determines the result values in the max magnitude bin - AudioFFTResult result() { - AudioFFTResult ret_value; - ret_value.magnitude = 0; - ret_value.bin = 0; - // find max value and index - for (int j = 0; j < size(); j++) { - float m = magnitude(j); - if (m > ret_value.magnitude) { - ret_value.magnitude = m; - ret_value.bin = j; - } - } - ret_value.frequency = frequency(ret_value.bin); - return ret_value; - } - - /// Determines the N biggest result values - template - void resultArray(AudioFFTResult (&result)[N]) { - // initialize to negative value - for (int j = 0; j < N; j++) { - result[j].magnitude = -1000000; - } - // find top n values - AudioFFTResult act; - for (int j = 0; j < size(); j++) { - act.magnitude = magnitude(j); - act.bin = j; - act.frequency = frequency(j); - insertSorted(result, act); - } - } - - /// provides access to the FFTDriver which implements the basic FFT - /// functionality - FFTDriver *driver() { return p_driver; } - - /// Determines the frequency of the indicated bin - float frequency(int bin) { - if (bin >= bins) { - LOGE("Invalid bin %d", bin); - return 0; - } - return static_cast(bin) * cfg.sample_rate / cfg.length; - } - - /// Determine the bin number from the frequency - int frequencyToBin(int freq){ - int max_freq = cfg.sample_rate / 2; - return map(freq, 0, max_freq, 0, size()); - } - - /// Calculates the magnitude of the fft result to determine the max value (bin - /// is 0 to size()) - float magnitude(int bin) { - if (bin >= bins) { - LOGE("Invalid bin %d", bin); - return 0; - } - return p_driver->magnitude(bin); - } - - float magnitudeFast(int bin) { - if (bin >= bins) { - LOGE("Invalid bin %d", bin); - return 0; - } - return p_driver->magnitudeFast(bin); - } - - /// calculates the phase - float phase(int bin){ - FFTBin fft_bin; - getBin(bin, fft_bin); - return atan2(fft_bin.img, fft_bin.real); - } - - /// Provides the magnitudes as array of size size(). Please note that this - /// method is allocating additinal memory! - float *magnitudes() { - if (l_magnitudes.size() == 0) { - l_magnitudes.resize(size()); - } - for (int j = 0; j < size(); j++) { - l_magnitudes[j] = magnitude(j); - } - return l_magnitudes.data(); - } - - /// Provides the magnitudes w/o calling the square root function as array of - /// size size(). Please note that this method is allocating additinal memory! - float *magnitudesFast() { - if (l_magnitudes.size() == 0) { - l_magnitudes.resize(size()); - } - for (int j = 0; j < size(); j++) { - l_magnitudes[j] = magnitudeFast(j); - } - return l_magnitudes.data(); - } - - /// sets the value of a bin - bool setBin(int idx, float real, float img) { - has_rfft_data = true; - if (idx < 0 || idx >= size()) return false; - bool rc_first_half = p_driver->setBin(idx, real, img); - bool rc_2nd_half = p_driver->setBin(cfg.length - idx, real, img); - return rc_first_half && rc_2nd_half; - } - /// sets the value of a bin - bool setBin(int pos, FFTBin &bin) { - return setBin(pos, bin.real, bin.img); - } - /// gets the value of a bin - bool getBin(int pos, FFTBin &bin) { return p_driver->getBin(pos, bin); } - - /// clears the fft data - void clearBins(){ - FFTBin empty{0,0}; - for (int j=0; j< size(); j++){ - setBin(j, empty); - } - } - - /// Provides the actual configuration - AudioFFTConfig &config() { return cfg; } - - protected: - FFTDriver *p_driver = nullptr; - int current_pos = 0; - int bins = 0; - unsigned long timestamp_begin = 0l; - unsigned long timestamp = 0l; - AudioFFTConfig cfg; - FFTInverseOverlapAdder rfft_add{0}; - Vector l_magnitudes{0}; - Vector step_data{0}; - SingleBuffer stride_buffer{0}; - RingBuffer rfft_data{0}; - bool has_rfft_data = false; - - // Add samples to input data p_x - and process them if full - template - void processSamples(const void *data, size_t count) { - T *dataT = (T *)data; - T sample; - for (int j = 0; j < count; j += cfg.channels) { - sample = dataT[j + cfg.channel_used]; - if (writeStrideBuffer((uint8_t *)&sample, sizeof(T))){ - // process data if buffer is full - T* samples = (T*) stride_buffer.data(); - int sample_count = stride_buffer.size() / sizeof(T); - assert(sample_count == cfg.length); - for (int j=0; j< sample_count; j++){ - T out_sample = samples[j]; - p_driver->setValue(j, windowedSample(out_sample, j)); - } - - fft(); - - // remove stride samples - stride_buffer.clearArray(cfg.stride * sizeof(T)); - - // validate available data in stride buffer - if (cfg.stride == cfg.length) assert(stride_buffer.available()==0); - - } - } - } - - template - T windowedSample(T sample, int pos) { - T result = sample; - if (cfg.window_function_fft != nullptr) { - result = cfg.window_function_fft->factor(pos) * sample; - } - return result; - } - - template - void fft() { - timestamp_begin = millis(); - p_driver->fft(); - has_rfft_data = true; - timestamp = millis(); - if (cfg.callback != nullptr) { - cfg.callback(*this); - } - } - - /// reverse fft - void rfft() { - TRACED(); - // execute reverse fft - p_driver->rfft(); - has_rfft_data = false; - // add data to sum buffer - for (int j = 0; j < cfg.length; j++) { - float value = p_driver->getValue(j); - rfft_add.add(value, j, cfg.window_function_ifft); - } - // get result data from sum buffer - rfftWriteData(rfft_data); - } - - /// write reverse fft result to buffer to make it available for readBytes - void rfftWriteData(BaseBuffer &data) { - // get data to result buffer - // for (int j = 0; j < cfg.stride; j++) { - // step_data[j] = 0.0; - // } - rfft_add.getStepData(step_data.data(), cfg.stride, - NumberConverter::maxValue(cfg.bits_per_sample)); - - switch (cfg.bits_per_sample) { - case 8: - writeIFFT(step_data.data(), cfg.stride); - break; - case 16: - writeIFFT(step_data.data(), cfg.stride); - break; - case 24: - writeIFFT(step_data.data(), cfg.stride); - break; - case 32: - writeIFFT(step_data.data(), cfg.stride); - break; - default: - LOGE("Unsupported bits: %d", cfg.bits_per_sample); - } - } - - template - void writeIFFT(float *data, int len) { - for (int j = 0; j < len; j++) { - T sample = data[j]; - T out_data[cfg.channels]; - for (int ch = 0; ch < cfg.channels; ch++) { - out_data[ch] = sample; - } - int result = rfft_data.writeArray((uint8_t *)out_data, sizeof(out_data)); - assert(result == sizeof(out_data)); - } - } - - inline int bytesPerSample() { return cfg.bits_per_sample / 8; } - - /// make sure that we do not reuse already found results - template - void insertSorted(AudioFFTResult (&result)[N], AudioFFTResult tmp) { - // find place where we need to insert new record - for (int j = 0; j < N; j++) { - // insert when biggen then current record - if (tmp.magnitude > result[j].magnitude) { - // shift existing values right - for (int i = N - 2; i >= j; i--) { - result[i + 1] = result[i]; - } - // insert new value - result[j] = tmp; - // stop after we found the correct index - break; - } - } - } - - // adds samples to stride buffer, returns true if the buffer is full - bool writeStrideBuffer(uint8_t *buffer, size_t len) { - assert(stride_buffer.availableForWrite() >= len); - stride_buffer.writeArray(buffer, len); - return stride_buffer.isFull(); - } - - bool isPowerOfTwo(uint16_t x) { return (x & (x - 1)) == 0; } -}; - -} // namespace audio_tools diff --git a/src/AudioTools/AudioLibs/AudioKissFFT.h b/src/AudioTools/AudioLibs/AudioKissFFT.h deleted file mode 100644 index 6cadc8c27e..0000000000 --- a/src/AudioTools/AudioLibs/AudioKissFFT.h +++ /dev/null @@ -1,109 +0,0 @@ -#pragma once - -#include "kiss_fix.h" -#include "AudioFFT.h" - -/** - * @defgroup fft-kiss KISS - * @ingroup fft - * @brief FFT using KISS -**/ - -namespace audio_tools { - -/** - * @brief Driver for RealFFT - * @ingroup fft-kiss - * @author Phil Schatzmann - * @copyright GPLv3 - */ -class FFTDriverKissFFT : public FFTDriver { - public: - bool begin(int len) override { - this->len = len; - k_data.resize(len); - if (p_fft_object==nullptr) p_fft_object = cpp_kiss_fft_alloc(len,false,nullptr,nullptr); - assert(p_fft_object!=nullptr); - return p_fft_object!=nullptr; - } - - void end() override { - if (p_fft_object!=nullptr) kiss_fft_free(p_fft_object); - if (p_fft_object_inv!=nullptr) kiss_fft_free(p_fft_object_inv); - - p_fft_object = nullptr; - k_data.resize(0); - } - void setValue(int idx, float value) override { - k_data[idx].r = value; - } - - void fft() override { - cpp_kiss_fft (p_fft_object, k_data.data(), k_data.data()); - }; - - void rfft() override { - if(p_fft_object_inv==nullptr) { - p_fft_object_inv = cpp_kiss_fft_alloc(len,true,nullptr,nullptr); - } - cpp_kiss_fft (p_fft_object_inv, k_data.data(), k_data.data()); - }; - - float magnitude(int idx) override { - return sqrt(magnitudeFast(idx)); - } - - /// magnitude w/o sqrt - float magnitudeFast(int idx) override { - return (k_data[idx].r * k_data[idx].r + k_data[idx].i * k_data[idx].i); - } - - bool isValid() override{ return p_fft_object!=nullptr; } - - bool isReverseFFT() override {return true;} - - float getValue(int idx) override { return k_data[idx].r; } - - bool setBin(int pos, FFTBin &bin) { return FFTDriver::setBin(pos, bin);} - - bool setBin(int pos, float real, float img) override { - if (pos>=len) return false; - k_data[pos].r = real; - k_data[pos].i = img; - return true; - } - bool getBin(int pos, FFTBin &bin) override { - if (pos>=len) return false; - bin.real = k_data[pos].r; - bin.img = k_data[pos].i; - return true; - } - - kiss_fft_cfg p_fft_object=nullptr; - kiss_fft_cfg p_fft_object_inv=nullptr; - Vector k_data{0}; // real - int len = 0; - -}; -/** - * @brief AudioFFT using FFTReal. The only specific functionality is the access to the dataArray - * @ingroup fft-kiss - * @author Phil Schatzmann - * @copyright GPLv3 - */ -class AudioKissFFT : public AudioFFTBase { - public: - AudioKissFFT():AudioFFTBase(new FFTDriverKissFFT()) {} - - /// Provides the complex array returned by the FFT - kiss_fft_cpx *dataArray() { - return driverEx()->k_data.data(); - } - - FFTDriverKissFFT* driverEx() { - return (FFTDriverKissFFT*)driver(); - } -}; - - -} \ No newline at end of file diff --git a/src/AudioTools/AudioLibs/AudioPlayerProtocol.h b/src/AudioTools/AudioLibs/AudioPlayerProtocol.h deleted file mode 100644 index 96ab89449b..0000000000 --- a/src/AudioTools/AudioLibs/AudioPlayerProtocol.h +++ /dev/null @@ -1,45 +0,0 @@ -#pragma once -#include "AudioTools/CoreAudio/AudioPlayer.h" - -namespace audio_tools { - -/*** - * @brief Abstract class for protocol to control the audio player. - * @ingroup player - * @author Phil Schatzmann - */ -class AudioPlayerProtocol { - public: - /// processes the commands and returns the result output via the Print - /// object - virtual bool processCommand(const char* input, Print& result) = 0; - - /// Proceess commands passed by Stream (e.g. Serial) - virtual bool processCommand(Stream& input, Print& result) { - char buffer[max_input_buffer_size]; - int len = readLine(input, buffer, max_input_buffer_size); - if (len == 0) return false; - return processCommand(buffer, result); - } - /// Defines the player - virtual void setPlayer(AudioPlayer& player) { p_player = &player; } - - /// Defines the input buffer size used by the readLine function (default 256) - void setMaxInputBufferSize(int size) { max_input_buffer_size = size; } - - protected: - AudioPlayer* p_player = nullptr; - int max_input_buffer_size = 256; - - /// Reads a line delimited by '\n' from the stream - int readLine(Stream& in, char* str, int max) { - int index = 0; - if (in.available() > 0) { - index = in.readBytesUntil('\n', str, max); - str[index] = '\0'; // null termination character - } - return index; - } -}; - -} // namespace audio_tools \ No newline at end of file diff --git a/src/AudioTools/AudioLibs/AudioPlayerProtocolServer.h b/src/AudioTools/AudioLibs/AudioPlayerProtocolServer.h deleted file mode 100644 index 0a5e03df88..0000000000 --- a/src/AudioTools/AudioLibs/AudioPlayerProtocolServer.h +++ /dev/null @@ -1,90 +0,0 @@ -#pragma once -#include "AudioPlayerProtocol.h" -#include "AudioTools/CoreAudio/BaseStream.h" -#include "AudioTools/CoreAudio/Buffers.h" -#include "HttpServer.h" - -namespace audio_tools { - -/*** - * @brief Audio Player Protocol Server: We can use the indicated protocol over - * http to control the audio player provided by the audiotools. - * @author Phil Schatzmann - */ -class AudioPlayerProtocolServer { - public: - /// Default constructor - AudioPlayerProtocolServer(AudioPlayerProtocol& protocol, - AudioPlayer& player, int port = 80, - const char* ssid = nullptr, - const char* pwd = nullptr) { - setProtocol(protocol); - setPlayer(player); - setPort(port); - setSSID(ssid); - setPassword(pwd); - } - - /// Empty constructor: call setPlayer to define the player - AudioPlayerProtocolServer() = default; - - /// Defines the player - void setPlayer(AudioPlayer& player) { p_protocol->setPlayer(player); } - - void setPort(int port) { this->port = port; } - void setSSID(const char* ssid) { this->ssid = ssid; } - void setPassword(const char* password) { this->password = password; } - void setSSID(const char* ssid, const char* password) { - this->ssid = ssid; - this->password = password; - } - - bool begin() { - context[0] = this; - server.on("/", T_GET, parse, context.data(), context.size()); - - // connect to WIFI - if (ssid != nullptr && password != nullptr) { - return server.begin(port, ssid, password); - } - return server.begin(port); - } - - void loop() { server.copy(); } - void copy() { server.copy(); } - - /// Defines the buffer size that is made available for the http reply - void setBufferSize(int size) { buffer_size = size; } - - void setProtocol(AudioPlayerProtocol& protocol) { - this->p_protocol = &protocol; - } - - protected: - WiFiServer wifi; - HttpServer server{wifi}; - AudioPlayerProtocol* p_protocol; - RingBuffer ringBuffer{0}; - QueueStream queueStream{ringBuffer}; - Vector context{1}; - int port = 80; - const char* ssid = nullptr; - const char* password = nullptr; - int buffer_size = 512; - - static void parse(HttpServer* server, const char* requestPath, - HttpRequestHandlerLine* hl) { - LOGI("parse: %s", requestPath); - AudioPlayerProtocolServer* self = (AudioPlayerProtocolServer*)hl->context[0]; - self->ringBuffer.resize(self->buffer_size); - QueueStream& queueStream = self->queueStream; - queueStream.begin(); - bool ok = self->p_protocol->processCommand(requestPath, queueStream); - LOGI("available: %d", queueStream.available()); - server->reply("text/plain", queueStream, queueStream.available(), - ok ? 200 : 400, ok ? SUCCESS : "Error"); - self->ringBuffer.resize(0); - } -}; - -} // namespace audio_tools \ No newline at end of file diff --git a/src/AudioTools/AudioLibs/AudioRealFFT.h b/src/AudioTools/AudioLibs/AudioRealFFT.h deleted file mode 100644 index bbd776f213..0000000000 --- a/src/AudioTools/AudioLibs/AudioRealFFT.h +++ /dev/null @@ -1,114 +0,0 @@ -#pragma once - -#include "AudioFFT.h" -#include "FFT/FFTReal.h" - -/** - * @defgroup fft-real Real - * @ingroup fft - * @brief FFT using Real FFT -**/ - -namespace audio_tools { - -/** - * @brief Driver for RealFFT - * @ingroup fft-real - * @author Phil Schatzmann - * @copyright GPLv3 - */ -class FFTDriverRealFFT : public FFTDriver { - public: - bool begin(int len) override { - this->len = len; - v_x.resize(len); - v_f.resize(len); - if (p_fft_object==nullptr) p_fft_object = new ffft::FFTReal(len); - assert(p_fft_object!=nullptr); - return p_fft_object!=nullptr; - } - void end()override{ - if (p_fft_object!=nullptr) { - delete p_fft_object; - p_fft_object = nullptr; - } - v_x.resize(0); - v_f.resize(0); - } - void setValue(int idx, float value) override{ - v_x[idx] = value; - } - - void fft() override{ - memset(v_f.data(),0,len*sizeof(float)); - p_fft_object->do_fft(v_f.data(), v_x.data()); - }; - - /// Inverse fft - convert fft result back to time domain (samples) - void rfft() override{ - // ifft - p_fft_object->do_ifft(v_f.data(), v_x.data()); - } - - bool isReverseFFT() override { return true;} - - float magnitude(int idx) override { - return sqrt(magnitudeFast(idx)); - } - - /// magnitude w/o sqrt - float magnitudeFast(int idx) override { - return ((v_x[idx] * v_x[idx]) + (v_f[idx] * v_f[idx])); - } - - bool isValid() override{ return p_fft_object!=nullptr; } - - /// get Real value - float getValue(int idx) override { return v_x[idx];} - - bool setBin(int pos, float real, float img) override { - if (pos < 0 || pos >= len) return false; - v_x[pos] = real; - v_f[pos] = img; - return true; - } - bool getBin(int pos, FFTBin &bin) override { - if (pos>=len) return false; - bin.real = v_x[pos]; - bin.img = v_f[pos]; - return true; - } - - ffft::FFTReal *p_fft_object=nullptr; - Vector v_x{0}; // real - Vector v_f{0}; // complex - int len; - -}; - -/** - * @brief AudioFFT using RealFFT - * @ingroup fft-real - * @author Phil Schatzmann - * @copyright GPLv3 - */ -class AudioRealFFT : public AudioFFTBase { - public: - AudioRealFFT():AudioFFTBase(new FFTDriverRealFFT()) {} - - /// Provides the real array returned by the FFT - float* realArray() { - return driverEx()->v_x.data(); - } - - /// Provides the complex array returned by the FFT - float *imgArray() { - return driverEx()->v_f.data(); - } - - FFTDriverRealFFT* driverEx() { - return (FFTDriverRealFFT*)driver(); - } -}; - -} diff --git a/src/AudioTools/AudioLibs/Concurrency.h b/src/AudioTools/AudioLibs/Concurrency.h deleted file mode 100644 index 50c8755b6d..0000000000 --- a/src/AudioTools/AudioLibs/Concurrency.h +++ /dev/null @@ -1,7 +0,0 @@ -#pragma once -/** - * @defgroup concurrency Concurrency - * @ingroup main - * @brief Multicore support - */ -#include "AudioTools/Concurrency/RTOS.h" diff --git a/src/AudioTools/AudioLibs/Desktop/File.h b/src/AudioTools/AudioLibs/Desktop/File.h deleted file mode 100644 index aca3ff36a6..0000000000 --- a/src/AudioTools/AudioLibs/Desktop/File.h +++ /dev/null @@ -1,13 +0,0 @@ -#pragma once -#define VFS_SD SD -#include "AudioTools/Disk/VFSFile.h" -#include "AudioTools/Disk/VFS.h" - -// We allow the access to the files via the global SD object - -namespace audio_tools { - -using File = VFSFile; -using FS = VFS; - -} diff --git a/src/AudioTools/AudioLibs/Desktop/NoArduino.h b/src/AudioTools/AudioLibs/Desktop/NoArduino.h deleted file mode 100644 index 919f145499..0000000000 --- a/src/AudioTools/AudioLibs/Desktop/NoArduino.h +++ /dev/null @@ -1,239 +0,0 @@ -#pragma once -/** - * @file NoArduino.h - * @author Phil Schatzmann - * @brief If you want to use the framework w/o Arduino you need to provide the - * implementation of a couple of classes and methods! - * @version 0.1 - * @date 2022-09-19 - * - * @copyright Copyright (c) 2022 - * - */ -#include "AudioToolsConfig.h" -#ifdef IS_DESKTOP -# error We should not get here! -#endif -#include -#include -#include -#include -#include - -#include // std::max -#include - - -#define IS_NOARDUINO - -#ifndef PSTR -#define PSTR(fmt) fmt -#endif - -#ifndef PI -#define PI 3.14159265359f -#endif - -#ifndef INPUT -#define INPUT 0x0 -#endif - -#ifndef OUTPUT -#define OUTPUT 0x1 -#endif - -#ifndef INPUT_PULLUP -#define INPUT_PULLUP 0x2 -#endif - -#ifndef HIGH -#define HIGH 0x1 -#endif -#ifndef LOW -#define LOW 0x0 -#endif - -using namespace std; - -enum PrintCharFmt { DEC=10, HEX=16 }; - -namespace audio_tools { - -class Print { - public: -#ifndef DOXYGEN - virtual size_t write(uint8_t ch) { - // not implememnted: to be overritten - return 0; - } - - virtual size_t write(const char *str) { - return write((const uint8_t *)str, strlen(str)); - } - - virtual size_t write(const char *buffer, size_t size) { - return write((const uint8_t *)buffer, size); - } - - virtual int print(const char *msg) { - int result = strlen(msg); - return write(msg, result); - } - - virtual int println(const char *msg = "") { - int result = print(msg); - write('\n'); - return result + 1; - } - - virtual int println(float number) { - char buffer[120]; - snprintf(buffer, 120, "%f", number); - return println(buffer); - } - - virtual int print(float number) { - char buffer[120]; - snprintf(buffer, 120, "%f", number); - return print(buffer); - } - - virtual int print(int number) { - char buffer[80]; - snprintf(buffer, 80, "%d", number); - return print(buffer); - } - - virtual int print(char c, PrintCharFmt spec) { - char result[5]; - switch (spec) { - case DEC: - snprintf(result, 3, "%c", c); - return print(result); - case HEX: - snprintf(result, 3, "%x", c); - return print(result); - } - return -1; - } - - int println(int value, PrintCharFmt fmt) { - return print(value, fmt) + println(); - } - -#endif - - virtual size_t write(const uint8_t *data, size_t len) { - if (data == nullptr) return 0; - for (size_t j = 0; j < len; j++) { - write(data[j]); - } - return len; - } - - virtual int availableForWrite() { return 1024; } - - virtual void flush() { /* Empty implementation for backward compatibility */ } - - protected: - int _timeout = 10; -}; - -class Stream : public Print { - - public: - virtual ~Stream() = default; - virtual int available() { return 0; } - virtual size_t readBytes(uint8_t *data, size_t len) { return 0; } -#ifndef DOXYGEN - virtual int read() { return -1; } - virtual int peek() { return -1; } - virtual void setTimeout(size_t timeoutMs) {} - size_t readBytesUntil(char terminator, char *buffer, size_t length) { - for (int j=0;j -#include - -#ifndef DESKTOP_MILLIS_DEFINED -#define DESKTOP_MILLIS_DEFINED - -namespace audio_tools { - -/// Returns the milliseconds since the start -inline uint32_t millis(){ - using namespace std::chrono; - // Get current time with precision of milliseconds - auto now = time_point_cast(system_clock::now()); - // sys_milliseconds is type time_point - using sys_milliseconds = decltype(now); - // Convert time_point to signed integral type - return now.time_since_epoch().count(); -} - -// sleep ms milliseconds -void delay(unsigned long ms){ - std::this_thread::sleep_for(std::chrono::milliseconds(ms)); -} - -// sleep us milliseconds -void delayMicroseconds(unsigned int us){ - std::this_thread::sleep_for(std::chrono::microseconds(us)); -} - -// Returns the micros of milliseconds passed since epich -inline unsigned long micros(void){ - using namespace std::chrono; - // Get current time with precision of milliseconds - auto now = time_point_cast(system_clock::now()); - // sys_milliseconds is type time_point - using sys_milliseconds = decltype(now); - // Convert time_point to signed integral type - return now.time_since_epoch().count(); -} - -} - -#endif \ No newline at end of file diff --git a/src/AudioTools/AudioLibs/FFTDisplay.h b/src/AudioTools/AudioLibs/FFTDisplay.h deleted file mode 100644 index 4688ca2e37..0000000000 --- a/src/AudioTools/AudioLibs/FFTDisplay.h +++ /dev/null @@ -1,90 +0,0 @@ -#pragma once -#include "AudioTools/AudioLibs/AudioFFT.h" -#include "AudioTools/Concurrency/LockGuard.h" - -namespace audio_tools { - -class FFTDisplay; -static FFTDisplay *selfFFTDisplay = nullptr; -#if defined(USE_CONCURRENCY) -// fft mutex -static Mutex fft_mux; -#endif -/** - * Display FFT result: we can define a start bin and group susequent bins for a - * combined result. - */ - -class FFTDisplay { - public: - FFTDisplay(AudioFFTBase &fft) { - p_fft = &fft; - selfFFTDisplay = this; - } - - /// start bin which is displayed - int fft_start_bin = 0; - /// group result by adding subsequent bins - int fft_group_bin = 1; - /// Influences the senitivity - float fft_max_magnitude = 700.0f; - - void begin() { - // assign fft callback - AudioFFTConfig &fft_cfg = p_fft->config(); - fft_cfg.callback = fftCallback; - - // number of bins - magnitudes.resize(p_fft->size()); - for (int j = 0; j < p_fft->size(); j++) { - magnitudes[j] = 0; - } - } - - /// Returns the magnitude for the indicated led x position. We might - /// need to combine values from the magnitudes array if this is much bigger. - float getMagnitude(int x) { - // get magnitude from fft - float total = 0; - for (int j = 0; j < fft_group_bin; j++) { - int idx = fft_start_bin + (x * fft_group_bin) + j; - if (idx >= magnitudes.size()) { - idx = magnitudes.size() - 1; - } - total += magnitudes[idx]; - } - return total / fft_group_bin; - } - - int getMagnitudeScaled(int x, int max) { - int result = mapT(getMagnitude(x), 0, fft_max_magnitude, 0.0f, - static_cast(max)); - if (result > max){ - LOGD("fft_max_magnitude too small: current value is %f", getMagnitude(x)) - } - // limit value to max - return min(result, max); - } - - /// callback method which provides updated data from fft - static void fftCallback(AudioFFTBase &fft) { - selfFFTDisplay->loadMangnitudes(); - }; - - protected: - AudioFFTBase *p_fft = nullptr; - Vector magnitudes{0}; - - void loadMangnitudes() { - // just save magnitudes to be displayed -#if defined(USE_CONCURRENCY) - LockGuard guard(fft_mux); -#endif - for (int j = 0; j < p_fft->size(); j++) { - float value = p_fft->magnitude(j); - magnitudes[j] = value; - } - } -}; - -} // namespace audio_tools diff --git a/src/AudioTools/AudioLibs/FFTEffects.h b/src/AudioTools/AudioLibs/FFTEffects.h deleted file mode 100644 index d04691031b..0000000000 --- a/src/AudioTools/AudioLibs/FFTEffects.h +++ /dev/null @@ -1,274 +0,0 @@ -#pragma once - -#include "AudioTools/AudioLibs/AudioRealFFT.h" // using RealFFT -#include "AudioTools/CoreAudio/AudioOutput.h" -#include "AudioTools/CoreAudio/StreamCopy.h" - -namespace audio_tools { - -static Hann fft_effects_hann; -static BufferedWindow fft_effects_buffered_window{&fft_effects_hann}; - -/** - * @brief Common configuration for FFT effects - * @ingroup transform - * @author phil schatzmann - */ -struct FFTEffectConfig : public AudioInfo { - int length = 1024; - int stride = 512; - WindowFunction *window_function = &fft_effects_buffered_window; -}; - -/*** - * @brief Abstract class for common Logic for FFT based effects. The effect is - * applied after the fft to the frequency domain before executing the ifft. - * Please note that this is quite processing time intensitive: so you might keep - * the sample rate quite low if the processor is not fast enough! - * @ingroup transform - * @author phil schatzmann - */ - -class FFTEffect : public AudioOutput { - public: - FFTEffect(Print &out) { - p_out = &out; - fft_cfg.ref = this; - } - - FFTEffectConfig defaultConfig() { - FFTEffectConfig c; - return c; - } - - bool begin(FFTEffectConfig info) { - copier.setLogName("ifft"); - setAudioInfo(info); - fft_cfg.length = info.length; - fft_cfg.stride = info.stride > 0 ? info.stride : info.length; - fft_cfg.window_function = info.window_function; - return begin(); - } - - bool begin() override { - TRACED(); - // copy result to output - copier.begin(*p_out, fft); - - // setup fft - fft_cfg.copyFrom(audioInfo()); - fft_cfg.callback = effect_callback; - LOGI("length: %d", fft_cfg.length); - LOGI("stride: %d", fft_cfg.stride); - LOGI("window_function: %s", (fft_cfg.window_function != nullptr) - ? fft_cfg.window_function->name() - : "-"); - return fft.begin(fft_cfg); - } - - size_t write(const uint8_t *data, size_t len) override { - TRACED(); - return fft.write(data, len); - } - - protected: - Print *p_out = nullptr; - AudioRealFFT fft; - AudioFFTConfig fft_cfg{fft.defaultConfig(RXTX_MODE)}; - Hann hann; - BufferedWindow buffered{&hann}; - StreamCopy copier; - - virtual void effect(AudioFFTBase &fft) = 0; - - static void effect_callback(AudioFFTBase &fft) { - TRACED(); - FFTEffect *ref = (FFTEffect *)fft.config().ref; - // execute effect - ref->effect(fft); - // write ifft to output - ref->processOutput(); - } - - void processOutput() { - TRACED(); - while (copier.copy()); - } -}; - -/** - * @brief Apply Robotize FFT Effect on frequency domain data. See - * https://learn.bela.io/tutorials/c-plus-plus-for-real-time-audio-programming/phase-vocoder-part-3/ - * @ingroup transform - * @author phil schatzmann - */ -class FFTRobotize : public FFTEffect { - friend FFTEffect; - - public: - FFTRobotize(AudioStream &out) : FFTEffect(out) { addNotifyAudioChange(out); }; - FFTRobotize(AudioOutput &out) : FFTEffect(out) { addNotifyAudioChange(out); }; - FFTRobotize(Print &out) : FFTEffect(out) {}; - - protected: - /// Robotise the output - void effect(AudioFFTBase &fft) { - TRACED(); - AudioFFTResult best = fft.result(); - - FFTBin bin; - for (int n = 0; n < fft.size(); n++) { - float amplitude = fft.magnitude(n); - - // update new bin value - bin.real = amplitude / best.magnitude; - bin.img = 0.0; - Serial.println(bin.real); - - fft.setBin(n, bin); - } - } -}; - -/** - * @brief Apply Robotize FFT Effect on frequency domain data. See - * https://learn.bela.io/tutorials/c-plus-plus-for-real-time-audio-programming/phase-vocoder-part-3/ - * @ingroup transform - * @author phil schatzmann - */ -class FFTWhisper : public FFTEffect { - friend FFTEffect; - - public: - FFTWhisper(AudioStream &out) : FFTEffect(out) { addNotifyAudioChange(out); }; - FFTWhisper(AudioOutput &out) : FFTEffect(out) { addNotifyAudioChange(out); }; - FFTWhisper(Print &out) : FFTEffect(out) {}; - - protected: - /// Robotise the output - void effect(AudioFFTBase &fft) { - TRACED(); - FFTBin bin; - for (int n = 0; n < fft.size(); n++) { - float amplitude = fft.magnitude(n); - float phase = rand() / (float)RAND_MAX * 2.f * PI; - - // update new bin value - bin.real = cosf(phase) * amplitude; - bin.img = sinf(phase) * amplitude; - fft.setBin(n, bin); - } - } -}; - -/** - * @brief Apply FFT and IFFT w/o any changes to the frequency domain - * @ingroup transform - * @author phil schatzmann - */ - -class FFTNop : public FFTEffect { - friend FFTEffect; - - public: - FFTNop(AudioStream &out) : FFTEffect(out) { addNotifyAudioChange(out); }; - FFTNop(AudioOutput &out) : FFTEffect(out) { addNotifyAudioChange(out); }; - FFTNop(Print &out) : FFTEffect(out) {}; - - protected: - /// Do nothing - void effect(AudioFFTBase &fft) {} -}; - -/** - * @brief Pitch Shift FFT Effect Configuration - * @ingroup transform - * @author phil schatzmann - */ - -struct FFTPitchShiftConfig : public FFTEffectConfig { - int shift = 1; -}; - -/** - * @brief Apply Pitch Shift FFT Effect on frequency domain data: we just move - * the bins up or down - * @ingroup transform - * @author phil schatzmann - */ -class FFTPitchShift : public FFTEffect { - friend FFTEffect; - - public: - FFTPitchShift(AudioStream &out) : FFTEffect(out) { - addNotifyAudioChange(out); - }; - FFTPitchShift(AudioOutput &out) : FFTEffect(out) { - addNotifyAudioChange(out); - }; - FFTPitchShift(Print &out) : FFTEffect(out) {}; - - FFTPitchShiftConfig defaultConfig() { - FFTPitchShiftConfig result; - result.shift = shift; - return result; - } - - bool begin(FFTPitchShiftConfig psConfig) { - setShift(psConfig.shift); - FFTEffect::begin(psConfig); - return begin(); - } - - bool begin() override { - bool rc = FFTEffect::begin(); - // you can not shift more then you have bins - assert(abs(shift) < fft.size()); - return rc; - } - - /// defines how many bins should be shifted up (>0) or down (<0); - void setShift(int bins) { shift = bins; } - - protected: - int shift = 1; - - /// Pitch Shift - void effect(AudioFFTBase &fft) override { - TRACED(); - FFTBin bin; - int max = fft.size(); - - if (shift < 0) { - // copy bins: left shift - for (int n = -shift; n < max; n++) { - int to_bin = n + shift; - assert(to_bin >= 0); - assert(to_bin < max); - fft.getBin(n, bin); - fft.setBin(to_bin, bin); - } - // clear tail - bin.clear(); - for (int n = max + shift; n < max; n++) { - fft.setBin(n, bin); - } - } else if (shift > 0) { - // copy bins: right shift - for (int n = max - shift - 1; n >= 0; n--) { - int to_bin = n + shift; - assert(to_bin >= 0); - assert(to_bin < max); - fft.getBin(n, bin); - fft.setBin(to_bin, bin); - } - // clear head - bin.clear(); - for (int n = 0; n < shift; n++) { - fft.setBin(n, bin); - } - } - } -}; - -} // namespace audio_tools \ No newline at end of file diff --git a/src/AudioTools/AudioLibs/HLSStream.h b/src/AudioTools/AudioLibs/HLSStream.h deleted file mode 100644 index 50aff75668..0000000000 --- a/src/AudioTools/AudioLibs/HLSStream.h +++ /dev/null @@ -1,781 +0,0 @@ -#pragma once -#include "AudioTools/AudioCodecs/AudioEncoded.h" -#include "AudioTools/CoreAudio/AudioBasic/Str.h" -#include "AudioTools/CoreAudio/AudioHttp/URLStream.h" -#include "AudioTools/CoreAudio/StreamCopy.h" -#include "AudioToolsConfig.h" - -#define MAX_HLS_LINE 512 -#define START_URLS_LIMIT 4 -#define HLS_BUFFER_COUNT 2 -#define HLS_MAX_NO_READ 2 -#define HLS_MAX_URL_LEN 256 -#define HLS_TIMEOUT 5000 -#define HLS_UNDER_OVERFLOW_WAIT_TIME 10 - -/// hide hls implementation in it's own namespace - -namespace audio_tools_hls { - -/*** - * @brief We feed the URLLoaderHLS with some url strings. The data of the - * related segments are provided via the readBytes() method. - * @author Phil Schatzmann - * @copyright GPLv3 - */ - -template -class URLLoaderHLS { - public: - URLLoaderHLS() = default; - - ~URLLoaderHLS() { end(); } - - bool begin() { - TRACED(); - buffer.resize(buffer_size * buffer_count); - - active = true; - return true; - } - - void end() { - TRACED(); - url_stream.end(); - buffer.clear(); - active = false; - } - - /// Adds the next url to be played in sequence - void addUrl(const char *url) { - LOGI("Adding %s", url); - StrView url_str(url); - char *str = new char[url_str.length() + 1]; - memcpy(str, url_str.c_str(), url_str.length() + 1); - urls.push_back((const char *)str); - } - - /// Provides the number of open urls which can be played. Refills them, when - /// min limit is reached. - int urlCount() { return urls.size(); } - - /// Available bytes of the audio stream - int available() { - if (!active) return 0; - TRACED(); - bufferRefill(); - - return buffer.available(); - } - - /// Provides data from the audio stream - size_t readBytes(uint8_t *data, size_t len) { - if (!active) return 0; - TRACED(); - bufferRefill(); - - if (buffer.available() < len) LOGW("Buffer underflow"); - return buffer.readArray(data, len); - } - - const char *contentType() { - return url_stream.httpRequest().reply().get(CONTENT_TYPE); - } - - int contentLength() { return url_stream.contentLength(); } - - void setBufferSize(int size, int count) { - buffer_size = size; - buffer_count = count; - // support call after begin()! - if (buffer.size() != 0) { - buffer.resize(buffer_size * buffer_count); - } - } - - void setCACert(const char *cert) { url_stream.setCACert(cert); } - - protected: - Vector urls{10}; - RingBuffer buffer{0}; - bool active = false; - int buffer_size = DEFAULT_BUFFER_SIZE; - int buffer_count = HLS_BUFFER_COUNT; - URLStream url_stream; - const char *url_to_play = nullptr; - - /// try to keep the buffer filled - void bufferRefill() { - TRACED(); - // we have nothing to do - if (urls.empty()) { - LOGD("urls empty"); - delay(HLS_UNDER_OVERFLOW_WAIT_TIME); - return; - } - if (buffer.availableForWrite() == 0) { - LOGD("buffer full"); - delay(HLS_UNDER_OVERFLOW_WAIT_TIME); - return; - } - - // switch current stream if we have no more data - if (!url_stream && !urls.empty()) { - LOGD("Refilling"); - if (url_to_play != nullptr) { - delete url_to_play; - } - url_to_play = urls[0]; - LOGI("playing %s", url_to_play); - url_stream.end(); - url_stream.setConnectionClose(true); - url_stream.setTimeout(HLS_TIMEOUT); - url_stream.begin(url_to_play); - url_stream.waitForData(HLS_TIMEOUT); - urls.pop_front(); - // assert(urls[0]!=url); - - LOGI("Playing %s of %d", url_stream.urlStr(), (int)urls.size()); - } - - int total = 0; - int failed = 0; - int to_write = min(buffer.availableForWrite(), DEFAULT_BUFFER_SIZE); - // try to keep the buffer filled - while (to_write > 0) { - uint8_t tmp[to_write]; - memset(tmp, 0, to_write); - int read = url_stream.readBytes(tmp, to_write); - total += read; - if (read > 0) { - failed = 0; - buffer.writeArray(tmp, read); - LOGD("buffer add %d -> %d:", read, buffer.available()); - - to_write = min(buffer.availableForWrite(), DEFAULT_BUFFER_SIZE); - } else { - delay(10); - } - // After we processed all data we close the stream to get a new url - if (url_stream.totalRead() == url_stream.contentLength()) { - LOGI("Closing stream because all bytes were processed: available: %d", - url_stream.available()); - url_stream.end(); - break; - } - LOGD("Refilled with %d now %d available to write", total, - buffer.availableForWrite()); - } - } -}; - -/** - * Prevent that the same url is loaded twice. We limit the history to - * 20 entries. - */ -class URLHistory { - public: - bool add(const char *url) { - if (url == nullptr) return true; - bool found = false; - StrView url_str(url); - for (int j = 0; j < history.size(); j++) { - if (url_str.equals(history[j])) { - found = true; - break; - } - } - if (!found) { - char *str = new char[url_str.length() + 1]; - memcpy(str, url, url_str.length() + 1); - history.push_back((const char *)str); - if (history.size() > 20) { - delete (history[0]); - history.pop_front(); - } - } - return !found; - } - - void clear() { history.clear(); } - - int size() { return history.size(); } - - protected: - Vector history; -}; - -/** - * @brief Simple Parser for HLS data. - * @author Phil Schatzmann - * @copyright GPLv3 - */ -template -class HLSParser { - public: - // loads the index url - bool begin(const char *urlStr) { - index_url_str = urlStr; - return begin(); - } - - bool begin() { - TRACEI(); - segments_url_str = ""; - bandwidth = 0; - total_read = 0; - - if (!parseIndex()) { - TRACEE(); - return false; - } - - // in some exceptional cases the index provided segement info - if (url_loader.urlCount() == 0) { - if (!parseSegments()) { - TRACEE(); - return false; - } - } else { - segments_url_str = index_url_str; - segmentsActivate(); - } - - if (!url_loader.begin()) { - TRACEE(); - return false; - } - - return true; - } - - int available() { - TRACED(); - int result = 0; - reloadSegments(); - - if (active) result = url_loader.available(); - return result; - } - - size_t readBytes(uint8_t *data, size_t len) { - TRACED(); - size_t result = 0; - reloadSegments(); - - if (active) result = url_loader.readBytes(data, len); - total_read += result; - return result; - } - - const char *indexUrl() { return index_url_str; } - - const char *segmentsUrl() { return segments_url_str.c_str(); } - - /// Provides the codec - const char *getCodec() { return codec.c_str(); } - - /// Provides the content type of the audio data - const char *contentType() { return url_loader.contentType(); } - - /// Provides the http content lengh - int contentLength() { return url_loader.contentLength(); } - - /// Closes the processing - void end() { - TRACEI(); - codec.clear(); - segments_url_str.clear(); - url_stream.end(); - url_loader.end(); - url_history.clear(); - active = false; - } - - /// Defines the number of urls that are preloaded in the URLLoaderHLS - void setUrlCount(int count) { url_count = count; } - - /// Redefines the buffer size - void setBufferSize(int size, int count) { - url_loader.setBufferSize(size, count); - } - - void setCACert(const char *cert) { - url_stream.setCACert(cert); - url_loader.setCACert(cert); - } - - void setPowerSave(bool flag) { url_stream.setPowerSave(flag); } - - void setURLResolver(const char *(*cb)(const char *segment, - const char *reqURL)) { - resolve_url = cb; - } - /// Provides the hls url as string - const char *urlStr() { return url_str.c_str(); } - - /// Povides the number of bytes read - size_t totalRead() { return total_read; }; - - protected: - enum class URLType { Undefined, Index, Segment }; - URLType next_url_type = URLType::Undefined; - int bandwidth = 0; - int url_count = 5; - size_t total_read = 0; - bool url_active = false; - bool is_extm3u = false; - Str codec; - Str segments_url_str; - Str url_str; - const char *index_url_str = nullptr; - URLStream url_stream; - URLLoaderHLS url_loader; - URLHistory url_history; - bool active = false; - bool parse_segments_active = false; - int media_sequence = 0; - int segment_count = 0; - uint64_t next_sement_load_time_planned = 0; - float play_time = 0; - uint64_t next_sement_load_time = 0; - const char *(*resolve_url)(const char *segment, - const char *reqURL) = resolveURL; - - /// Default implementation for url resolver: determine absolue url from - /// relative url - static const char *resolveURL(const char *segment, const char *reqURL) { - // avoid dynamic memory allocation - static char result[HLS_MAX_URL_LEN] = {0}; - StrView result_str(result, HLS_MAX_URL_LEN); - StrView index_url(http://github.com/reqURL); - // Use prefix up to ? or laast / - int end = index_url.lastIndexOf("?"); - if (end >= 0) { - result_str.substring(reqURL, 0, end); - } else { - end = index_url.lastIndexOf("/"); - if (end >= 0) { - result_str.substring(reqURL, 0, end); - } - } - // Use the full url - if (result_str.isEmpty()) { - result_str = reqURL; - } - // add trailing / - if (!result_str.endsWith("/")) { - result_str.add("/"); - } - // add relative segment - result_str.add(segment); - LOGI(">> relative addr: %s for %s", segment, reqURL); - LOGD(">> -> %s", result); - return result; - } - - /// trigger the reloading of segments if the limit is underflowing - void reloadSegments() { - TRACED(); - // get new urls - if (!segments_url_str.isEmpty()) { - parseSegments(); - } - } - - /// parse the index file and the segments - bool parseIndex() { - TRACED(); - url_stream.end(); - url_stream.setTimeout(HLS_TIMEOUT); - url_stream.setConnectionClose(true); - if (!url_stream.begin(index_url_str)) return false; - url_active = true; - return parseIndexLines(); - } - - /// parse the index file - bool parseIndexLines() { - TRACEI(); - char tmp[MAX_HLS_LINE]; - bool result = true; - is_extm3u = false; - - // parse lines - memset(tmp, 0, MAX_HLS_LINE); - while (true) { - memset(tmp, 0, MAX_HLS_LINE); - size_t len = - url_stream.httpRequest().readBytesUntil('\n', tmp, MAX_HLS_LINE); - // stop when there is no more data - if (len == 0 && url_stream.available() == 0) break; - StrView str(tmp); - - // check header - if (str.startsWith("#EXTM3U")) { - is_extm3u = true; - // reset timings - resetTimings(); - } - - if (is_extm3u) { - if (!parseIndexLine(str)) { - return false; - } - } - } - return result; - } - - /// Determine codec for min bandwidth - bool parseIndexLine(StrView &str) { - TRACED(); - LOGI("> %s", str.c_str()); - parseIndexLineMetaData(str); - // in some exceptional cases the index provided segement info - parseSegmentLineMetaData(str); - parseLineURL(str); - return true; - } - - bool parseIndexLineMetaData(StrView &str) { - int tmp_bandwidth; - if (str.startsWith("#")) { - if (str.indexOf("EXT-X-STREAM-INF") >= 0) { - next_url_type = URLType::Index; - // determine min bandwidth - int pos = str.indexOf("BANDWIDTH="); - if (pos > 0) { - StrView num(str.c_str() + pos + 10); - tmp_bandwidth = num.toInt(); - url_active = (tmp_bandwidth < bandwidth || bandwidth == 0); - if (url_active) { - bandwidth = tmp_bandwidth; - LOGD("-> bandwith: %d", bandwidth); - } - } - - pos = str.indexOf("CODECS="); - if (pos > 0) { - int start = pos + 8; - int end = str.indexOf('"', pos + 10); - codec.substring(str, start, end); - LOGI("-> codec: %s", codec.c_str()); - } - } - } - return true; - } - - void resetTimings() { - next_sement_load_time_planned = millis(); - play_time = 0; - next_sement_load_time = 0xFFFFFFFFFFFFFFFF; - } - - /// parse the segment url provided by the index - bool parseSegments() { - TRACED(); - if (parse_segments_active) { - return false; - } - - // make sure that we load at relevant schedule - if (millis() < next_sement_load_time && url_loader.urlCount() > 1) { - delay(1); - return false; - } - parse_segments_active = true; - - LOGI("Available urls: %d", url_loader.urlCount()); - - if (url_stream) url_stream.clear(); - LOGI("parsing %s", segments_url_str.c_str()); - - if (segments_url_str.isEmpty()) { - TRACEE(); - parse_segments_active = false; - return false; - } - - if (!url_stream.begin(segments_url_str.c_str())) { - TRACEE(); - parse_segments_active = false; - return false; - } - - segment_count = 0; - if (!parseSegmentLines()) { - TRACEE(); - parse_segments_active = false; - // do not display as error - return true; - } - - segmentsActivate(); - return true; - } - - void segmentsActivate() { - LOGI("Reloading in %f sec", play_time / 1000.0); - if (play_time > 0) { - next_sement_load_time = next_sement_load_time_planned + play_time; - } - - // we request a minimum of collected urls to play before we start - if (url_history.size() > START_URLS_LIMIT) active = true; - parse_segments_active = false; - } - - /// parse the segments - bool parseSegmentLines() { - TRACEI(); - char tmp[MAX_HLS_LINE]; - bool result = true; - is_extm3u = false; - - // parse lines - memset(tmp, 0, MAX_HLS_LINE); - while (true) { - memset(tmp, 0, MAX_HLS_LINE); - size_t len = - url_stream.httpRequest().readBytesUntil('\n', tmp, MAX_HLS_LINE); - if (len == 0 && url_stream.available() == 0) break; - StrView str(tmp); - - // check header - if (str.startsWith("#EXTM3U")) { - is_extm3u = true; - resetTimings(); - } - - if (is_extm3u) { - if (!parseSegmentLine(str)) { - return false; - } - } - } - return result; - } - - /// Add all segments to queue - bool parseSegmentLine(StrView &str) { - TRACED(); - LOGI("> %s", str.c_str()); - if (!parseSegmentLineMetaData(str)) return false; - parseLineURL(str); - return true; - } - - bool parseSegmentLineMetaData(StrView &str) { - if (str.startsWith("#")) { - if (str.startsWith("#EXT-X-MEDIA-SEQUENCE:")) { - int new_media_sequence = atoi(str.c_str() + 22); - LOGI("media_sequence: %d", new_media_sequence); - if (new_media_sequence == media_sequence) { - LOGW("MEDIA-SEQUENCE already loaded: %d", media_sequence); - return false; - } - media_sequence = new_media_sequence; - } - - // add play time to next_sement_load_time_planned - if (str.startsWith("#EXTINF")) { - next_url_type = URLType::Segment; - StrView sec_str(str.c_str() + 8); - float sec = sec_str.toFloat(); - LOGI("adding play time: %f sec", sec); - play_time += (sec * 1000.0); - } - } - return true; - } - - bool parseLineURL(StrView &str) { - if (!str.startsWith("#")) { - switch (next_url_type) { - case URLType::Undefined: - // we should not get here - assert(false); - break; - case URLType::Index: - if (str.startsWith("http")) { - segments_url_str.set(str); - } else { - segments_url_str.set(resolve_url(http://github.com/str.c_str(), index_url_str)); - } - LOGD("segments_url_str = %s", segments_url_str.c_str()); - break; - case URLType::Segment: - segment_count++; - if (url_history.add(str.c_str())) { - // provide audio urls to the url_loader - if (str.startsWith("http")) { - url_str = str; - } else { - // we create the complete url - url_str = resolve_url(http://github.com/str.c_str(), index_url_str); - } - url_loader.addUrl(url_str.c_str()); - } else { - LOGD("Duplicate ignored: %s", str.c_str()); - } - } - // clear url type - next_url_type = URLType::Undefined; - } - return true; - } -}; - -} // namespace audio_tools_hls - -namespace audio_tools { -/** - * @brief HTTP Live Streaming using HLS: The resulting .ts data is provided - * via readBytes() that dynamically reload new Segments. Please note that - * this reloading adds a considerable delay: So if you want to play back the - * audio, you should buffer the content in a seaparate task. - * - * @author Phil Schatzmann - * @ingroup http *@copyright GPLv3 - */ - -template -class HLSStreamT : public AbstractURLStream { - public: - /// Empty constructor - HLSStreamT() = default; - - /// Convenience constructor which logs in to the WiFi - HLSStreamT(const char *ssid, const char *password) { - setSSID(ssid); - setPassword(password); - } - - /// Open an HLS url - bool begin(const char *urlStr) { - TRACEI(); - login(); - // parse the url to the HLS - bool rc = parser.begin(urlStr); - return rc; - } - - /// Reopens the last url - bool begin() override { - TRACEI(); - login(); - bool rc = parser.begin(); - return rc; - } - - /// ends the request - void end() override { parser.end(); } - - /// Sets the ssid that will be used for logging in (when calling begin) - void setSSID(const char *ssid) override { this->ssid = ssid; } - - /// Sets the password that will be used for logging in (when calling begin) - void setPassword(const char *password) override { this->password = password; } - - /// Returns the string representation of the codec of the audio stream - const char *codec() { return parser.getCodec(); } - - /// Provides the content type from the http reply - const char *contentType() { return parser.contentType(); } - - /// Provides the content length of the actual .ts Segment - int contentLength() override { return parser.contentLength(); } - - /// Provides number of available bytes in the read buffer - int available() override { - TRACED(); - return parser.available(); - } - - /// Provides the data fro the next .ts Segment - size_t readBytes(uint8_t *data, size_t len) override { - TRACED(); - return parser.readBytes(data, len); - } - - /// Redefines the read buffer size - void setBufferSize(int size, int count) { parser.setBufferSize(size, count); } - - /// Defines the certificate - void setCACert(const char *cert) override { parser.setCACert(cert); } - - /// Changes the Wifi to power saving mode - void setPowerSave(bool flag) override { parser.setPowerSave(flag); } - - /// Custom logic to provide the codec as Content-Type to support the - /// MultiCodec - const char *getReplyHeader(const char *header) override { - const char *codec = parser.getCodec(); - const char *result = nullptr; - if (StrView(header).equalsIgnoreCase(CONTENT_TYPE)) { - result = parser.contentType(); - } - if (result) LOGI("-> Format: %s", result); - return result; - } - - /// The resolving of relative addresses can be quite tricky: you can provide - /// your custom resolver implementation - void setURLResolver(const char *(*cb)(const char *segment, - const char *reqURL)) { - parser.setURLResolver(cb); - } - - const char *urlStr() override { return parser.urlStr(); } - - size_t totalRead() override { return parser.totalRead(); }; - /// not implemented - void setConnectionClose(bool flag) override {}; - /// not implemented - bool waitForData(int timeout) override { return false; } - - protected: - audio_tools_hls::HLSParser parser; - const char *ssid = nullptr; - const char *password = nullptr; - - void login() { -#ifdef USE_WIFI - if (ssid != nullptr && password != nullptr && - WiFi.status() != WL_CONNECTED) { - TRACED(); - delay(1000); - WiFi.begin(ssid, password); - while (WiFi.status() != WL_CONNECTED) { - Serial.print("."); - delay(500); - } - } -#else - LOGW("login not supported"); -#endif - } - - /// Added to comply with AbstractURLStream - bool begin(const char *urlStr, const char *acceptMime, MethodID action = GET, - const char *reqMime = "", const char *reqData = "") override { - return begin(urlStr); - } - - HttpRequest &httpRequest() override { - static HttpRequest dummy; - return dummy; - } - - /// Not implemented: potential future improvement - void setClient(Client &clientPar) override {} - - /// Not implemented - void addRequestHeader(const char *header, const char *value) override {} -}; - -using HLSStream = HLSStreamT; - -} // namespace audio_tools diff --git a/src/AudioTools/AudioLibs/HLSStreamESP32.h b/src/AudioTools/AudioLibs/HLSStreamESP32.h deleted file mode 100644 index ea83435e59..0000000000 --- a/src/AudioTools/AudioLibs/HLSStreamESP32.h +++ /dev/null @@ -1,9 +0,0 @@ -#pragma once -#include "AudioTools/CoreAudio/AudioHttp/URLStreamESP32.h" -#include "HLSStream.h" - -namespace audio_tools { - -using HLSStreamESP32 = HLSStreamT; - -} // namespace audio_tools \ No newline at end of file diff --git a/src/AudioTools/AudioLibs/I2SCodecStream.h b/src/AudioTools/AudioLibs/I2SCodecStream.h deleted file mode 100644 index e9af251df0..0000000000 --- a/src/AudioTools/AudioLibs/I2SCodecStream.h +++ /dev/null @@ -1,387 +0,0 @@ -#pragma once -#include "AudioBoard.h" // install audio-driver library -#include "AudioToolsConfig.h" -#include "AudioTools/CoreAudio/AudioI2S/I2SStream.h" - -//#pragma GCC diagnostic ignored "-Wclass-memaccess" - -// Added to be compatible with the AudioKitStream.h -#ifndef PIN_AUDIO_KIT_SD_CARD_CS -#define PIN_AUDIO_KIT_SD_CARD_CS 13 -#define PIN_AUDIO_KIT_SD_CARD_MISO 2 -#define PIN_AUDIO_KIT_SD_CARD_MOSI 15 -#define PIN_AUDIO_KIT_SD_CARD_CLK 14 -#endif - -namespace audio_tools { - -/** - * @brief Configuration for I2SCodecStream - * @ingroup io - * @author Phil Schatzmann - * @copyright GPLv3 - */ -struct I2SCodecConfig : public I2SConfig { - input_device_t input_device = ADC_INPUT_LINE1; - output_device_t output_device = DAC_OUTPUT_ALL; - // to be compatible with the AudioKitStream -> do not activate SD spi if false - bool sd_active = true; - // define pin source in driver configuration - PinFunction i2s_function = PinFunction::UNDEFINED; //CODEC; - bool operator==(I2SCodecConfig alt) { - return input_device == alt.input_device && - output_device == alt.output_device && *((AudioInfo *)this) == alt; - } - - bool operator!=(I2SCodecConfig alt) { return !(*this == alt); } -}; - -/** - * @brief I2S Stream which also sets up a codec chip and i2s - * @ingroup io - * @author Phil Schatzmann - * @copyright GPLv3 - */ -class I2SCodecStream : public AudioStream, public VolumeSupport { - public: - /// Default Constructor (w/o codec) - I2SCodecStream() = default; - /** - * @brief Default constructor: for available AudioBoard values check - * audioboard variables in - * https://pschatzmann.github.io/arduino-audio-driver/html/group__audio__driver.html - * Further information can be found in - * https://github.com/pschatzmann/arduino-audio-driver/wiki - */ - I2SCodecStream(AudioBoard &board) { setBoard(board); } - /// Provide board via pointer - I2SCodecStream(AudioBoard *board) { setBoard(board); } - - /// Provides the default configuration - I2SCodecConfig defaultConfig(RxTxMode mode = TX_MODE) { - auto cfg1 = i2s.defaultConfig(mode); - I2SCodecConfig cfg; - memcpy(&cfg, &cfg1, sizeof(cfg1)); - cfg.input_device = ADC_INPUT_LINE1; - cfg.output_device = DAC_OUTPUT_ALL; - cfg.sd_active = true; - cfg.rx_tx_mode = mode; - return cfg; - } - - bool begin() { - TRACED(); - return begin(cfg); - } - - /// Starts the I2S interface - virtual bool begin(I2SCodecConfig cfg) { - TRACED(); - this->cfg = cfg; - this->info = cfg; - return begin1(); - } - - /// Stops the I2S interface - void end() { - TRACED(); - if (p_board) p_board->end(); - i2s.end(); - is_active = false; - } - - /// updates the sample rate dynamically - virtual void setAudioInfo(AudioInfo info) { - TRACEI(); - AudioStream::setAudioInfo(info); - i2s.setAudioInfo(info); - - cfg.sample_rate = info.sample_rate; - cfg.bits_per_sample = info.bits_per_sample; - cfg.channels = info.channels; - - // update codec_cfg - codec_cfg.i2s.bits = toCodecBits(cfg.bits_per_sample); - codec_cfg.i2s.rate = toRate(cfg.sample_rate); - - // return if we we are not ready - if (!is_active || p_board == nullptr) { - return; - } - - // return if there is nothing to do - if (cfg.sample_rate == info.sample_rate && - cfg.bits_per_sample == info.bits_per_sample && - cfg.channels == info.channels) { - return; - } - - // update cfg - p_board->setConfig(codec_cfg); - } - - /// Writes the audio data to I2S - virtual size_t write(const uint8_t *data, size_t len) { - LOGD("I2SStream::write: %d", len); - return i2s.write(data, len); - } - - /// Reads the audio data - virtual size_t readBytes(uint8_t *data, size_t len) override { - return i2s.readBytes(data, len); - } - - /// Provides the available audio data - virtual int available() override { return i2s.available(); } - - /// Provides the available audio data - virtual int availableForWrite() override { return i2s.availableForWrite(); } - - /// sets the volume (range 0.0f - 1.0f) - bool setVolume(float vol) override { - VolumeSupport::setVolume(vol); - if (!is_active || p_board == nullptr) return false; - return p_board->setVolume(vol * 100.0); - } - - /// Provides the actual volume (0.0f - 1.0f) - float volume() override { - if (p_board == nullptr) return 0.0f; - return static_cast(p_board->getVolume()) / 100.0f; - } - /// legacy: same as volume() - float getVolume() { return volume(); } - - /// Mute / unmote - bool setMute(bool mute) { - if (p_board == nullptr) return false; - return p_board->setMute(mute); - } - /// Mute / unmute of an individual line (codec) - bool setMute(bool mute, int line) { - if (p_board == nullptr) return false; - return p_board->setMute(mute, line); - } - - /// Sets the output of the PA Power Pin - bool setPAPower(bool active) { - if (p_board == nullptr) return false; - return p_board->setPAPower(active); - } - - /// Sets the volume of the microphone (if available) - bool setInputVolume(float vol){ - if (!is_active || p_board == nullptr) return false; - return p_board->setInputVolume(100.0 * vol); - } - - /// Provides the board - AudioBoard &board() { return *p_board; } - /// (re)defines the board - void setBoard(AudioBoard &board) { p_board = &board; } - /// (re)defines the board - void setBoard(AudioBoard *board) { p_board = board; } - /// checks if a board has been defined - bool hasBoard() { return p_board != nullptr; } - - /// Provides the gpio for the indicated function - GpioPin getPinID(PinFunction function) { - if (p_board == nullptr) return -1; - return p_board->getPins().getPinID(function); - } - - /// Provides the gpio for the indicated function - GpioPin getPinID(PinFunction function, int pos) { - if (p_board == nullptr) return -1; - return p_board->getPins().getPinID(function, pos); - } - - /// Provides the gpio for the indicated key pos - GpioPin getKey(int pos) { return getPinID(PinFunction::KEY, pos); } - - /// Provides access to the pin information - DriverPins &getPins() { return p_board->getPins(); } - - /// Provides the i2s driver - I2SDriver *driver() { return i2s.driver(); } - - protected: - I2SStream i2s; - I2SCodecConfig cfg; - CodecConfig codec_cfg; - AudioBoard *p_board = nullptr; - bool is_active = false; - - bool begin1() { - TRACED(); - setupI2SFunction(); - setupI2SPins(); - if (!beginCodec(cfg)) { - TRACEE(); - is_active = false; - return false; - } - is_active = i2s.begin(cfg); - - // if setvolume was called before begin - float tobeVol = VolumeSupport::volume(); - if (is_active && tobeVol >= 0.0f) { - setVolume(tobeVol); - } - return is_active; - } - - /// if the cfg.i2s_function was not defined we determine the "correct" default value - void setupI2SFunction() { - if (cfg.i2s_function == PinFunction::UNDEFINED){ - if (cfg.rx_tx_mode == RX_MODE){ - auto i2s = p_board->getPins().getI2SPins(PinFunction::CODEC_ADC); - if (i2s){ - cfg.i2s_function = PinFunction::CODEC_ADC; - LOGI("using i2s_function: CODEC_ADC"); - } else { - cfg.i2s_function = PinFunction::CODEC; - } - } else { - cfg.i2s_function = PinFunction::CODEC; - } - } - } - - /// We use the board pins if they are available - void setupI2SPins() { - TRACED(); - // determine relevant I2S pins from driver configuration - auto i2s = getI2SPins(); - if (i2s) { - // determine i2s pins from board definition - PinsI2S i2s_pins = i2s.value(); - cfg.pin_bck = i2s_pins.bck; - cfg.pin_mck = i2s_pins.mclk; - cfg.pin_ws = i2s_pins.ws; - switch (cfg.rx_tx_mode) { - case RX_MODE: - cfg.pin_data = i2s_pins.data_in; - break; - case TX_MODE: - cfg.pin_data = i2s_pins.data_out; - break; - default: - cfg.pin_data = i2s_pins.data_out; - cfg.pin_data_rx = i2s_pins.data_in; - break; - } - } - } - - audio_driver_local::Optional getI2SPins(){ - TRACED(); - audio_driver_local::Optional i2s; - // Deterine I2S pins - return p_board->getPins().getI2SPins(cfg.i2s_function); - } - - bool beginCodec(I2SCodecConfig info) { - TRACED(); - switch (cfg.rx_tx_mode) { - case RX_MODE: - codec_cfg.input_device = info.input_device; - codec_cfg.output_device = DAC_OUTPUT_NONE; - break; - case TX_MODE: - codec_cfg.output_device = info.output_device; - codec_cfg.input_device = ADC_INPUT_NONE; - break; - default: - codec_cfg.input_device = info.input_device; - codec_cfg.output_device = info.output_device; - break; - } - codec_cfg.sd_active = info.sd_active; - LOGD("input: %d", info.input_device); - LOGD("output: %d", info.output_device); - codec_cfg.i2s.bits = toCodecBits(info.bits_per_sample); - codec_cfg.i2s.rate = toRate(info.sample_rate); - codec_cfg.i2s.fmt = toFormat(info.i2s_format); - codec_cfg.i2s.signal_type = (signal_t) info.signal_type; - // use reverse logic for codec setting - codec_cfg.i2s.mode = info.is_master ? MODE_SLAVE : MODE_MASTER; - if (p_board == nullptr) return false; - - // setup driver only on changes - return p_board->begin(codec_cfg); - } - - sample_bits_t toCodecBits(int bits) { - switch (bits) { - case 16: - LOGD("BIT_LENGTH_16BITS"); - return BIT_LENGTH_16BITS; - case 24: - LOGD("BIT_LENGTH_24BITS"); - return BIT_LENGTH_24BITS; - case 32: - LOGD("BIT_LENGTH_32BITS"); - return BIT_LENGTH_32BITS; - } - LOGE("Unsupported bits: %d", bits); - return BIT_LENGTH_16BITS; - } - samplerate_t toRate(int rate) { - if (rate <= 8000) { - LOGD("RATE_8K"); - return RATE_8K; - } - if (rate <= 11000) { - LOGD("RATE_11K"); - return RATE_11K; - } - if (rate <= 16000) { - LOGD("RATE_16K"); - return RATE_16K; - } - if (rate <= 22050) { - LOGD("RATE_22K"); - return RATE_22K; - } - if (rate <= 32000) { - LOGD("RATE_32K"); - return RATE_32K; - } - if (rate <= 44100) { - LOGD("RATE_44K"); - return RATE_44K; - } - if (rate <= 48000 || rate > 48000) { - LOGD("RATE_48K"); - return RATE_44K; - } - LOGE("Invalid rate: %d using 44K", rate); - return RATE_44K; - } - - i2s_format_t toFormat(I2SFormat fmt) { - switch (fmt) { - case I2S_PHILIPS_FORMAT: - case I2S_STD_FORMAT: - LOGD("I2S_NORMAL"); - return I2S_NORMAL; - case I2S_LEFT_JUSTIFIED_FORMAT: - case I2S_MSB_FORMAT: - LOGD("I2S_LEFT"); - return I2S_LEFT; - case I2S_RIGHT_JUSTIFIED_FORMAT: - case I2S_LSB_FORMAT: - LOGD("I2S_RIGHT"); - return I2S_RIGHT; - case I2S_PCM: - LOGD("I2S_DSP"); - return I2S_DSP; - default: - LOGE("unsupported mode"); - return I2S_NORMAL; - } - } -}; - -} // namespace audio_tools \ No newline at end of file diff --git a/src/AudioTools/AudioLibs/KARadioProtocol.h b/src/AudioTools/AudioLibs/KARadioProtocol.h deleted file mode 100644 index af45f46ac1..0000000000 --- a/src/AudioTools/AudioLibs/KARadioProtocol.h +++ /dev/null @@ -1,254 +0,0 @@ -#pragma once - -#include - -#include "AudioTools/CoreAudio/AudioBasic/Str.h" -#include "AudioTools/CoreAudio/AudioPlayer.h" -#include "AudioTools/Disk/AudioSource.h" -#include "AudioPlayerProtocol.h" - -#define KA_VERSION "Release: 2.4, Revision: R0" - -namespace audio_tools { - -/** - * @brief KA-Radio Protocol: We can use the KA-Radio protocol to control the - * audio player provided by the audiotools. - * Supported commands: play, instant, volume, volume+, volume-, pause, - * resume, stop, start, next, prev, mute, infos, version. - * Example: volume=50&play=128&infos - * See https://github.com/karawin/Ka-Radio32/blob/master/Interface.md - * - * @ingroup player - * @author Phil Schatzmann - */ - -class KARadioProtocol : public AudioPlayerProtocol { - public: - /// Empty constructor: call setPlayer to define the player - KARadioProtocol() { - addCommand("play", [](AudioPlayer& player, Str& cmd, Str& par, Print& out, - KARadioProtocol* self) { - if (!par.isEmpty()) { - int idx = par.toInt(); - player.setIndex(idx); - } - return true; - }); - addCommand("instant", [](AudioPlayer& player, Str& cmd, Str& par, - Print& out, KARadioProtocol* self) { - player.setPath(par.c_str()); - return true; - }); - addCommand("volume", [](AudioPlayer& player, Str& cmd, Str& par, Print& out, - KARadioProtocol* self) { - if (!par.isEmpty()) { - int volume = par.toInt(); - player.setVolume(static_cast(volume) / 254.0f); - } - return true; - }); - addCommand("volume+", [](AudioPlayer& player, Str& cmd, Str& par, - Print& out, KARadioProtocol* self) { - int volume = player.volume() * 254.0f; - volume += 5; - if (volume > 245) { - volume = 254; - } - player.setVolume(static_cast(volume) / 254.0f); - return true; - }); - addCommand("volume-", [](AudioPlayer& player, Str& cmd, Str& par, - Print& out, KARadioProtocol* self) { - int volume = player.volume() * 254.0f; - volume -= 5; - if (volume < 0) { - volume = 0; - } - player.setVolume(static_cast(volume) / 254.0f); - return true; - }); - addCommand("pause", [](AudioPlayer& player, Str& cmd, Str& par, Print& out, - KARadioProtocol* self) { - player.setActive(false); - return true; - }); - addCommand("resume", [](AudioPlayer& player, Str& cmd, Str& par, Print& out, - KARadioProtocol* self) { - player.setActive(true); - return true; - }); - addCommand("stop", [](AudioPlayer& player, Str& cmd, Str& par, Print& out, - KARadioProtocol* self) { - player.setActive(false); - return true; - }); - addCommand("start", [](AudioPlayer& player, Str& cmd, Str& par, Print& out, - KARadioProtocol* self) { - player.setActive(true); - return true; - }); - addCommand("next", [](AudioPlayer& player, Str& cmd, Str& par, Print& out, - KARadioProtocol* self) { - player.next(); - return true; - }); - addCommand("prev", [](AudioPlayer& player, Str& cmd, Str& par, Print& out, - KARadioProtocol* self) { - player.previous(); - return true; - }); - addCommand("mute", [](AudioPlayer& player, Str& cmd, Str& par, Print& out, - KARadioProtocol* self) { - if (!par.isEmpty()) { - player.setActive(!(par.toInt() == 1)); - } - return true; - }); - addCommand("infos", [](AudioPlayer& player, Str& cmd, Str& par, - Print& result, KARadioProtocol* self) { - result.print("vol: "); - result.println(self->volume); - result.print("num: "); - result.println(self->index()); - result.print("stn: "); // station - result.println(self->stationName()); - result.print("tit: "); // title - result.println(self->title()); - result.print("sts: "); // status - result.println(player.isActive()); - return true; - }); - addCommand("version", [](AudioPlayer& player, Str& cmd, Str& par, - Print& result, KARadioProtocol* self) { - result.print("version: "); - result.println(KA_VERSION); - return true; - }); - addCommand("list", - [](AudioPlayer& player, Str& cmd, Str& par, Print& result, - KARadioProtocol* self) { // arg: 0 to 254 - if (!par.isEmpty()) { - player.setIndex(par.toInt()); - } - result.println(self->stationName()); - return true; - }); - } - - /// Default constructor - KARadioProtocol(AudioPlayer& player) : KARadioProtocol() { - setPlayer(player); - } - - /// Defines the player - void setPlayer(AudioPlayer& player) override { - AudioPlayerProtocol::setPlayer(player); - volume = player.volume() * 254.0f; - } - - bool processCommand(Stream& input, Print& result) override { - return AudioPlayerProtocol::processCommand(input, result); - } - - /// processes the commands and returns the result output via the Print - /// object - bool processCommand(const char* input, Print& result) override { - if (p_player == nullptr) { - LOGE("player not set"); - return false; - } - - Str name; - Str arg; - StrView line = input; - - int start = line.indexOf('?'); - if (start < 0) start = 0; - int endPos = line.length(); - bool rc = true; - while (start >= 0 && start < endPos) { - int eqPos = line.indexOf('=', start); - int toPos = getEndPos(line, start + 1); - if (eqPos >= 0) { - name.substring(line, start + 1, eqPos); - arg.substring(line, eqPos + 1, toPos); - } else { - name.substring(line, start + 1, toPos); - arg = ""; - } - LOGD("start=%d, eqPos=%d, toPos=%d", start, eqPos, toPos); - // remove leading and trailing spaces - name.trim(); - arg.trim(); - // execute the command - rc = processCommand(name, arg, result); - // calculate new positions - start = toPos; - toPos = getEndPos(line, start + 1); - } - return rc; - } - - /// Processes a single command - bool processCommand(Str& name, Str& arg, Print& result) { - LOGI("command: %s (%s)", name.c_str(), arg.c_str()); - assert(p_player != nullptr); - - // ignore empty commands - if (name.isEmpty()) return false; - - for (Action& act : actions) { - if (name.equals(act.cmd)) { - return act.callback(*p_player, name, arg, result, this); - } else { - LOGD("-> %s vs %s: failed", name.c_str(), act.cmd); - } - } - - LOGE("Invalid command:", name.c_str()); - return false; - } - - /// Provides the actual index - int index() { return p_player->audioSource().index(); } - - /// Provides the actual title - const char* title() { return title_str.c_str(); } - - /// Add a new command - void addCommand(const char* cmd, - bool (*cb)(AudioPlayer& player, Str& cmd, Str& par, - Print& out, KARadioProtocol* self)) { - Action act; - act.cmd = cmd; - act.callback = cb; - actions.push_back(act); - } - - protected: - int volume = 0; - Str title_str = "n/a"; - struct Action { - const char* cmd; - bool (*callback)(AudioPlayer& player, Str& cmd, Str& par, Print& out, - KARadioProtocol* self) = nullptr; - }; - Vector actions; - - int getEndPos(StrView& line, int start) { - int endPos = line.indexOf('&', start); - if (endPos < 0) { - endPos = line.length(); - } - return endPos; - } - - const char* stationName() { - if (p_player != nullptr) { - return p_player->audioSource().toStr(); - } - return ""; - } -}; -} // namespace audio_tools \ No newline at end of file diff --git a/src/AudioTools/AudioLibs/KARadioProtocolServer.h b/src/AudioTools/AudioLibs/KARadioProtocolServer.h deleted file mode 100644 index a742753a5e..0000000000 --- a/src/AudioTools/AudioLibs/KARadioProtocolServer.h +++ /dev/null @@ -1,32 +0,0 @@ -#pragma once -#include "AudioPlayerProtocolServer.h" -#include "KARadioProtocol.h" - -namespace audio_tools { - -/** - * @brief KA-Radio Protocol Server which provides the KARadioProtocol over - * http to control the audio player provided by the audiotools. - * @ingroup player - * @author Phil Schatzmann - */ - -class KARadioProtocolServer : public AudioPlayerProtocolServer { - public: - /// Default constructor - KARadioProtocolServer(AudioPlayer& player, int port = 80, - const char* ssid = nullptr, const char* pwd = nullptr) { - setProtocol(protocol); - setPlayer(player); - setPort(port); - setSSID(ssid); - setPassword(pwd); - } - - /// Empty constructor: call setPlayer to define the player - KARadioProtocolServer() = default; - - protected: - KARadioProtocol protocol; -}; -} // namespace audio_tools \ No newline at end of file diff --git a/src/AudioTools/AudioLibs/LEDOutputUnoR4.h b/src/AudioTools/AudioLibs/LEDOutputUnoR4.h deleted file mode 100644 index 39dfc1ba26..0000000000 --- a/src/AudioTools/AudioLibs/LEDOutputUnoR4.h +++ /dev/null @@ -1,193 +0,0 @@ -#pragma once -#include "Arduino_LED_Matrix.h" -#include "AudioTools/AudioLibs/AudioFFT.h" -#include "FFTDisplay.h" - -namespace audio_tools { -class LEDOutputUnoR4; -struct LEDOutputUnoR4Config; - -// default callback function which implements led update based on fft -void fftLEDOutputUnoR4(LEDOutputUnoR4Config *cfg, LEDOutputUnoR4 *matrix); -// led update for volume -void volumeLEDOutputUnoR4(LEDOutputUnoR4Config *cfg, LEDOutputUnoR4 *matrix); - -/** - * LED Matrix Configuration. Provide the number of leds in x and y direction and - * the data pin. - * @author Phil Schatzmann - */ -struct LEDOutputUnoR4Config { - /// Custom callback logic to update the LEDs when update() is called - void (*update_callback)(LEDOutputUnoR4Config *cfg, - LEDOutputUnoR4 *matrix) = nullptr; - /// Update the leds only ever nth call - int update_frequency = 1; // update every call - /// Number of LEDs in a rows - int x = 12; - /// Number of LEDs in a column - int y = 8; - /// when true 0,0 is in the lower left corder - bool y_mirror = true; - /// Influences the senitivity - int max_magnitude = 700; -}; - -/** - * @brief LED output using the R4 LED matrix library. - * @ingroup io - * @author Phil Schatzmann - */ -class LEDOutputUnoR4 { - - public: - /// @brief Default Constructor - LEDOutputUnoR4() = default; - - /// @brief Constructor for FFT scenario - /// @param fft - LEDOutputUnoR4(FFTDisplay &fft) { - p_fft = &fft; - cfg.update_callback = fftLEDOutputUnoR4; - } - - /// @brief Constructor for VolumeMeter scenario - /// @param vol - LEDOutputUnoR4(VolumeMeter &vol) { - p_vol = &vol; - cfg.update_callback = volumeLEDOutputUnoR4; - } - - /// Provides the default config object - LEDOutputUnoR4Config defaultConfig() { return cfg; } - - /// Starts the processing with the default configuration - bool begin() { return begin(defaultConfig()); } - - /// Setup Led matrix - bool begin(LEDOutputUnoR4Config config) { - cfg = config; - frame.resize(cfg.x * cfg.y); - led_matrix.begin(); - max_column = -1; - return true; - } - - /// Updates the display by calling the update callback method: call this method in your loop - virtual void update() { - if (cfg.update_callback != nullptr && count++ % cfg.update_frequency == 0) { - // use custom update logic defined in config - cfg.update_callback(&cfg, this); - } else { - display(); - } - } - - /// Determine the led with the help of the x and y pos - bool &ledXY(uint8_t x, uint8_t y) { - if (cfg.y_mirror) y = cfg.y - y - 1; - return frame[x + (y * cfg.x)]; - } - - /// Provodes the max magnitude for the VolumeMeter and FFT scenario - virtual float getMaxMagnitude() { - // get magnitude from - if (p_vol != nullptr) { - return p_vol->volume(); - } - float max = 0; - if (p_fft != nullptr) { - for (int j = 0; j < cfg.x; j++) { - float value = p_fft->getMagnitude(j); - if (value > max) { - max = value; - } - } - } - return max; - } - - /// Update the indicated column with the indicated bar - void setColumnBar(int x, int currY) { - // update vertical bar - for (uint8_t y = 0; y < currY; y++) { - // update LED - ledXY(x, y) = true; - } - for (uint8_t y = currY; y < cfg.y; y++) { - ledXY(x, y) = false; - } - if (x > max_column) max_column = x; - } - - /// Update the last column with the indicated bar - void addColumnBar(int currY) { - max_column++; - if (max_column >= cfg.x) { - addEmptyColumn(); - } - if (max_column > cfg.x - 1) { - max_column = cfg.x - 1; - } - setColumnBar(max_column, currY); - } - - /// Provides access to the actual config object. E.g. to change the update logic - LEDOutputUnoR4Config &config() { return cfg; } - - /// Update the led_matrix - void display() { - led_matrix.loadPixels((uint8_t *)frame.data(), cfg.x * cfg.y); - } - - /// Provides access to the FFTDisplay object - FFTDisplay& fftDisplay() { - return *p_fft; - } - - protected: - friend class AudioFFTBase; - LEDOutputUnoR4Config cfg; - FFTDisplay *p_fft = nullptr; - VolumeMeter *p_vol = nullptr; - uint64_t count = 0; - ArduinoLEDMatrix led_matrix; - Vector frame{0}; - int max_column = -1; - - /// Adds an empty column to the end shifting the content to the left - void addEmptyColumn() { - for (int x = 1; x < cfg.x; x++) { - for (int y = 0; y < cfg.y; y++) { - ledXY(x - 1, y) = ledXY(x, y); - } - } - for (int y = 0; y < cfg.y; y++) { - ledXY(cfg.x - 1, y) = false; - } - } -}; - -/// Default update implementation which provides the fft result as "barchart" -void fftLEDOutputUnoR4(LEDOutputUnoR4Config *cfg, LEDOutputUnoR4 *matrix) { - // process horizontal - for (int x = 0; x < cfg->x; x++) { - // max y determined by magnitude - int currY = matrix->fftDisplay().getMagnitudeScaled(x, cfg->y); - LOGD("x: %d, y: %d", x, currY); - matrix->setColumnBar(x, currY); - } - matrix->display(); -} - -/// Default update implementation which provides the fft result as "barchart" -void volumeLEDOutputUnoR4(LEDOutputUnoR4Config *cfg, LEDOutputUnoR4 *matrix) { - float vol = matrix->getMaxMagnitude(); - int currY = mapT(vol, 0.0, - cfg->max_magnitude, 0.0f, - static_cast(cfg->y)); - matrix->addColumnBar(currY); - matrix->display(); -} - -} // namespace audio_tools \ No newline at end of file diff --git a/src/AudioTools/AudioLibs/MiniAudioStream.h b/src/AudioTools/AudioLibs/MiniAudioStream.h deleted file mode 100644 index 198efd3ccd..0000000000 --- a/src/AudioTools/AudioLibs/MiniAudioStream.h +++ /dev/null @@ -1,289 +0,0 @@ -#pragma once -/** - * @brief MiniAudio - * @author Phil Schatzmann - * @copyright GPLv3 - */ - -#include "AudioTools.h" -#include - -#define MINIAUDIO_IMPLEMENTATION -#include "miniaudio.h" - -#define MA_BUFFER_COUNT 100 -#define MA_START_COUNT MA_BUFFER_COUNT - 2 -#define MA_DELAY 10 - -namespace audio_tools { - -/** - * @brief Configuration for MiniAudio - * @author Phil Schatzmann - * @copyright GPLv3 - */ -class MiniAudioConfig : public AudioInfo { - public: - MiniAudioConfig() { - sample_rate = 44100; - channels = 2; - bits_per_sample = 16; - }; - MiniAudioConfig(const MiniAudioConfig &) = default; - MiniAudioConfig(const AudioInfo &in) { - sample_rate = in.sample_rate; - channels = in.channels; - bits_per_sample = in.bits_per_sample; - } - - bool is_input = false; - bool is_output = true; - int delay_ms_if_buffer_full = MA_DELAY; - int buffer_count = MA_BUFFER_COUNT; - int buffer_start_count = MA_START_COUNT; -}; - -/** - * @brief MiniAudio: https://miniaud.io/ - * @ingroup io - * @author Phil Schatzmann - * @copyright GPLv3 - */ -class MiniAudioStream : public AudioStream { - public: - MiniAudioStream() = default; - ~MiniAudioStream() { end(); }; - - MiniAudioConfig defaultConfig(RxTxMode mode = RXTX_MODE) { - MiniAudioConfig info; - info.sample_rate = 44100; - info.channels = 2; - info.bits_per_sample = 16; - switch (mode) { - case RX_MODE: - info.is_input = true; - info.is_output = false; - break; - case TX_MODE: - info.is_input = false; - info.is_output = true; - break; - case RXTX_MODE: - info.is_input = true; - info.is_output = true; - break; - default: - info.is_input = false; - info.is_output = false; - break; - } - return info; - } - - void setAudioInfo(AudioInfo in) override { - AudioStream::setAudioInfo(in); - if (in.sample_rate != config.sample_rate || - in.channels != config.channels || - in.bits_per_sample != config.bits_per_sample) { - config.copyFrom(in); - if (is_active) { - is_active = false; - is_playing = false; - // This will stop the device so no need to do that manually. - ma_device_uninit(&device_ma); - - begin(); - } - } - } - - bool begin(MiniAudioConfig info) { - AudioStream::setAudioInfo(info); - this->config = info; - return begin(); - } - - bool begin() override { - TRACEI(); - if (config.is_output && !config.is_input) - config_ma = ma_device_config_init(ma_device_type_playback); - else if (!config.is_output && config.is_input) - config_ma = ma_device_config_init(ma_device_type_capture); - else if (config.is_output && config.is_input) - config_ma = ma_device_config_init(ma_device_type_duplex); - else if (!config.is_output && !config.is_input) - config_ma = ma_device_config_init(ma_device_type_loopback); - - config_ma.pUserData = this; - config_ma.playback.channels = config.channels; - config_ma.sampleRate = config.sample_rate; - config_ma.dataCallback = data_callback; - switch (config.bits_per_sample) { - case 8: - config_ma.playback.format = ma_format_u8; - break; - case 16: - config_ma.playback.format = ma_format_s16; - break; - case 24: - config_ma.playback.format = ma_format_s24; - break; - case 32: - config_ma.playback.format = ma_format_s32; - break; - default: - LOGE("Invalid format"); - return false; - } - - if (ma_device_init(NULL, &config_ma, &device_ma) != MA_SUCCESS) { - // Failed to initialize the device. - return false; - } - - // The device is sleeping by default so you'll need to start it manually. - if (ma_device_start(&device_ma) != MA_SUCCESS) { - // Failed to initialize the device. - return false; - } - - is_active = true; - return is_active; - } - - void end() override { - is_active = false; - is_playing = false; - // This will stop the device so no need to do that manually. - ma_device_uninit(&device_ma); - // release buffer memory - buffer_in.resize(0); - buffer_out.resize(0); - } - - int availableForWrite() override { - return buffer_out.size() == 0 ? 0 : DEFAULT_BUFFER_SIZE; - } - - size_t write(const uint8_t *data, size_t len) override { - if (buffer_out.size() == 0) return 0; - LOGD("write: %zu", len); - - // write data to buffer - int open = len; - int written = 0; - while (open > 0) { - size_t result = 0; - { - std::lock_guard guard(write_mtx); - result = buffer_out.writeArray(data + written, open); - open -= result; - written += result; - } - - if (result != len) doWait(); - } - - // activate playing - // if (!is_playing && buffer_out.bufferCountFilled()>=MA_START_COUNT) { - if (!is_playing && buffer_out.available() >= config.buffer_start_count * buffer_size) { - LOGI("starting audio"); - is_playing = true; - } - // std::this_thread::yield(); - return written; - } - - int available() override { - return buffer_in.size() == 0 ? 0 : buffer_in.available(); - } - - size_t readBytes(uint8_t *data, size_t len) override { - if (buffer_in.size() == 0) return 0; - LOGD("write: %zu", len); - std::lock_guard guard(read_mtx); - return buffer_in.readArray(data, len); - } - - protected: - MiniAudioConfig config; - ma_device_config config_ma; - ma_device device_ma; - bool is_playing = false; - bool is_active = false; - bool is_buffers_setup = false; - RingBuffer buffer_out{0}; - RingBuffer buffer_in{0}; - std::mutex write_mtx; - std::mutex read_mtx; - int buffer_size = 0; - - // In playback mode copy data to pOutput. In capture mode read data from - // pInput. In full-duplex mode, both pOutput and pInput will be valid and - // you can move data from pInput into pOutput. Never process more than - // frameCount frames. - - void setupBuffers(int size) { - if (is_buffers_setup) return; - buffer_size = size; - int buffer_count = config.buffer_count; - LOGI("setupBuffers: %d * %d", size, buffer_count); - if (buffer_out.size() == 0 && config.is_output) - buffer_out.resize(size * buffer_count); - if (buffer_in.size() == 0 && config.is_input) - buffer_in.resize(size * buffer_count); - is_buffers_setup = true; - } - - void doWait() { - //std::this_thread::yield(); - delay(config.delay_ms_if_buffer_full); - //std::this_thread::sleep_for (std::chrono::milliseconds(MA_DELAY)); - } - - static void data_callback(ma_device *pDevice, void *pOutput, - const void *pInput, ma_uint32 frameCount) { - MiniAudioStream *self = (MiniAudioStream *)pDevice->pUserData; - AudioInfo cfg = self->audioInfo(); - - int bytes = frameCount * cfg.channels * cfg.bits_per_sample / 8; - self->setupBuffers(bytes); - - if (pInput) { - int open = bytes; - int processed = 0; - while (open > 0) { - int len = 0; - { - std::unique_lock guard(self->read_mtx); - int len = - self->buffer_in.writeArray((uint8_t *)pInput + processed, open); - open -= len; - processed += len; - } - if (len == 0) self->doWait(); - } - } - - if (pOutput) { - memset(pOutput, 0, bytes); - if (self->is_playing) { - int open = bytes; - int processed = 0; - while (open > 0) { - size_t len = 0; - { - std::lock_guard guard(self->write_mtx); - len = self->buffer_out.readArray((uint8_t *)pOutput + processed, - bytes); - open -= len; - processed += len; - } - if (len != bytes) self->doWait(); - } - } - } - } -}; - -} // namespace audio_tools \ No newline at end of file diff --git a/src/AudioTools/AudioLibs/MozziStream.h b/src/AudioTools/AudioLibs/MozziStream.h deleted file mode 100644 index 212415323e..0000000000 --- a/src/AudioTools/AudioLibs/MozziStream.h +++ /dev/null @@ -1,158 +0,0 @@ - - -#include "AudioTools.h" - -// prevent naming conflict with audiotools -#define AudioOutput AudioOutputMozzi -#include -#undef AudioOutput - -void updateControl(); -AudioOutputMozzi updateAudio(); - -namespace audio_tools { - -struct MozziConfig : AudioInfo { - uint16_t control_rate = CONTROL_RATE; - int input_range_from = 0; - int input_range_to = 1023; - int16_t output_volume = 10; -}; - -/** - * @brief Stream that provides audio information that was generated using the - * Mozzi API using the updateControl() and updateAudio() methods. - * @author Phil Schatzmann - * @copyright GPLv3 - */ -class MozziStream : public AudioStream, public VolumeSupport { - public: - MozziStream() = default; - - MozziStream(Stream& in) { setInput(in); } - - MozziConfig defaultConfig() { return cfg; } - - void setAudioInfo(AudioInfo info) { cfg.copyFrom(info); } - - int audioRate() { return cfg.sample_rate; } - - void setInput(Stream& in) { p_input = ∈ } - - AudioInfo audioInfo() { return cfg; } - - bool begin(MozziConfig cfg) { - this->cfg = cfg; - return begin(); - } - - bool begin() { - if (cfg.bits_per_sample != 16) { - LOGE("bits_per_sample must be 16 and not %d", cfg.bits_per_sample); - return false; - } - - // initialize range for input range determination - input_min = 32767; - input_max = -32768; - - // setup control counter - control_counter_max = info.sample_rate / cfg.control_rate; - if (control_counter_max <= 0) { - control_counter_max = 1; - } - control_counter = control_counter_max; - active = true; - return true; - } - - void end() { active = false; } - - /// Defines the multiplication factor to scale the Mozzi value range to int16_t - bool setVolume(int16_t vol) { - cfg.output_volume = vol; - return VolumeSupport::setVolume(vol); - } - - /// Provides the data filled by calling updateAudio() - size_t readBytes(uint8_t* data, size_t len) { - if (!active) return 0; - int samples = len / sizeof(int16_t); - int frames = samples / cfg.channels; - int16_t* data16 = (int16_t*)data; - int idx = 0; - for (int j = 0; j < frames; j++) { - int16_t sample = nextSample(); - for (int ch = 0; ch < cfg.channels; ch++) { - data16[idx++] = sample; - } - } - return idx * sizeof(int16_t); - } - - /// Write data to buffer so that we can access them by calling getAudioInput() - size_t write(const uint8_t* data, size_t len) { - if (!active) return 0; - if (buffer.size() == 0) { - buffer.resize(len * 2); - } - return buffer.writeArray(data, len); - } - - /// Gets the next audio value either from the assigned Input Stream or the - /// buffer that was filled by write(). The data range is defined in - /// MozziConfig - int getAudioInput() { - int16_t result[cfg.channels] = {0}; - if (p_input != nullptr) { - p_input->readBytes((uint8_t*)&result, sizeof(int16_t) * cfg.channels); - } else { - buffer.readArray((uint8_t*)&result, sizeof(int16_t) * cfg.channels); - } - // when we have multiple channels we provide the avg value - int sample = avg(result); - // calculate dynamic range - if (sample < input_min) input_min = sample; - if (sample > input_max) input_max = sample; - int sample1 = map(sample, input_min, input_max, cfg.input_range_from, - cfg.input_range_to); - return sample1; - } - - protected: - MozziConfig cfg; - int control_counter_max; - int control_counter; - RingBuffer buffer{0}; - Stream* p_input = nullptr; - bool active = false; - int input_min = 32767; - int input_max = -32768; - - int16_t avg(int16_t* values) { - int total = 0; - for (int ch = 0; ch < cfg.channels; ch++) { - total += values[ch]; - } - return total / cfg.channels; - } - - int16_t nextSample() { - // control update - if (--control_counter < 0) { - control_counter = control_counter_max; - LOGD("updateControl"); - updateControl(); - } - - // updateAudio() STANDARD mode is between -244 and 243 inclusive - auto result = updateAudio() * cfg.output_volume; - // clip result - if (result > 32767) result = 32767; - if (result < -32768) result = -32768; - // Serial.println(result); - return result; - } -}; - -} // namespace audio_tools \ No newline at end of file diff --git a/src/AudioTools/AudioLibs/PIDController.h b/src/AudioTools/AudioLibs/PIDController.h deleted file mode 100644 index e09b7044df..0000000000 --- a/src/AudioTools/AudioLibs/PIDController.h +++ /dev/null @@ -1,80 +0,0 @@ - - -#include - -#pragma once - -namespace audio_tools { - -/** - * @brief A simple header only PID Controller - * @author Phil Schatzmann - * @copyright GPLv3 - */ - -class PIDController { - public: - // dt - loop interval time - // max - maximum value of manipulated variable - // min - minimum value of manipulated variable - // kp - proportional gain - // ki - Integral gain - // kd - derivative gain - bool begin(float dt, float max, float min, float kp, float ki, float kd) { - this->dt = dt; - this->max = max; - this->min = min; - this->kp = kp; - this->kd = kd; - this->ki = ki; - return true; - } - - // target = desired process value - // measured = current process value: - // returns new process value - float calculate(float target, float measured) { - // Calculate errori - float error = target - measured; - - // Proportional term - float pout = kp * error; - - // Interal term - integral += error * dt; - float Iout = ki * integral; - - // Derivative term - assert(dt != 0.0); - - float derivative = (error - preerror) / dt; - float dout = kd * derivative; - - // Calculate total output - float output = pout + Iout + dout; - - // Restrict to max/min - if (output > max) - output = max; - else if (output < min) - output = min; - - // Save error to previous error - preerror = error; - - return output; - } - - protected: - float dt = 1.0f; - float max = 0.0f; - float min = 0.0f; - float kp = 0.0f; - float kd = 0.0f; - float ki = 0.0f; - float preerror = 0.0f; - float integral = 0.0f; - -}; // namespace audiotools - -} // namespace audio_tools diff --git a/src/AudioTools/AudioLibs/PureDataStream.h b/src/AudioTools/AudioLibs/PureDataStream.h deleted file mode 100644 index cb68844de5..0000000000 --- a/src/AudioTools/AudioLibs/PureDataStream.h +++ /dev/null @@ -1,130 +0,0 @@ -#include "AudioTools/CoreAudio/AudioStreams.h" -#include "AudioTools/CoreAudio/Buffers.h" - -namespace audio_tools { - -/** - * @brief Input and output of Pure Data PD using code generated by the hvcc - * compiler. The audio format is defined by the sample rate in the Heavy - * constructor, the number of channels in the ADC and DAC and the - * bits_per_sample of 16. Therefore the audio format can not be changed - * dynamically. I recommend to make sure that the input format and - * output format is consistent, otherwise you need to change the format - * in PD. - * @ingroup io - * @author phil schatzmann - */ -class PureDataStream : public AudioStream { - public: - PureDataStream(HeavyContextInterface &heavy, int bufferSize = 1024 * 2) { - p_heavy = &heavy; - buffer_size = bufferSize; - } - - AudioInfo audioInfoOut() override { - AudioInfo result(p_heavy->getSampleRate(), p_heavy->getNumOutputChannels(), - sample_size * 8); - return result; - } - - AudioInfo audioInfo() override { - AudioInfo result(p_heavy->getSampleRate(), p_heavy->getNumInputChannels(), - sample_size * 8); - return result; - } - - void setAudioInfo(AudioInfo newInfo) override { - if (audioInfo() != newInfo) { - LOGE("The audio format in wrong and can not be changed"); - } - } - - size_t readBytes(uint8_t *data, size_t len) { - int len_max = std::min(len, buffer_read.size()); - if (buffer_read.isEmpty()) readWrite(len_max); - return buffer_read.readArray(data, len_max); - } - - size_t write(const uint8_t *data, size_t len) { - int len_max = std::min(len, buffer_write.size()); - if (!buffer_write.isEmpty()) readWrite(len_max); - return buffer_write.writeArray(data, len_max); - } - - bool begin() { - int sample_rate = p_heavy->getSampleRate(); - in_channels = p_heavy->getNumInputChannels(); - out_channels = p_heavy->getNumOutputChannels(); - if (out_channels > 0) buffer_read.resize(buffer_size); - if (in_channels > 0) buffer_write.resize(buffer_size); - if (audioInfo() != audioInfoOut()) { - LOGW("rate: %d, channels: in=%d, out=%d", sample_rate, in_channels, - out_channels); - } else { - LOGI("rate: %d, channels: in=%d, out=%d", sample_rate, in_channels, - out_channels); - } - return in_channels > 0 || out_channels > 0; - } - - void flush() { readWrite(buffer_write.available() / sample_size); } - - void end() { - flush(); - buffer_read.resize(0); - buffer_write.resize(0); - in.resize(0); - out.resize(0); - } - - protected: - HeavyContextInterface *p_heavy = nullptr; - RingBuffer buffer_write{0}; - RingBuffer buffer_read{0}; - Vector in{0}; - Vector out{0}; - float volume = 1.0f; - int buffer_size; - const float max_int = 32767.0; - const int sample_size = sizeof(int16_t); - int in_channels = 0; - int out_channels = 0; - - // returns bytes - void readWrite(int bytes) { - if (bytes == 0) return; - int samples = bytes / sample_size; - // size must be multiple of HV_N_SIMD - samples = samples / HV_N_SIMD * HV_N_SIMD; - int frames = samples / out_channels; - - if (in.size() 0) { - for (int j = 0; j < samples; j++) { - int16_t tmp16 = 0; - size_t eff = buffer_write.readArray((uint8_t *)&tmp16, sample_size); - assert(eff == sample_size); - in[j] = volume * tmp16 / max_int; - } - } - - // process data - int frames_eff = p_heavy->processInlineInterleaved(in.data(), out.data(), frames); - // LOGI("%d -> %d", frames, frames_eff); - assert(frames == frames_eff); - - // convert intput to int16 - if (buffer_read.size() > 0) { - for (int j = 0; j < samples; j++) { - int16_t tmp16 = volume * out[j] * max_int; - size_t eff = buffer_read.writeArray((uint8_t *)&tmp16, sample_size); - assert(eff == sample_size); - } - } - } -}; - -} // namespace audio_tools \ No newline at end of file diff --git a/src/AudioTools/AudioLibs/R2ROutput.h b/src/AudioTools/AudioLibs/R2ROutput.h deleted file mode 100644 index ac5fa0f6a7..0000000000 --- a/src/AudioTools/AudioLibs/R2ROutput.h +++ /dev/null @@ -1,230 +0,0 @@ -#pragma once - -#include "AudioToolsConfig.h" -#include "AudioTools/CoreAudio/AudioTimer/AudioTimer.h" -#include "AudioTools/CoreAudio/AudioLogger.h" -#include "AudioTools/CoreAudio/AudioOutput.h" -#include "AudioTools/CoreAudio/Buffers.h" -#include "AudioTools/CoreAudio/AudioBasic/Collections/Vector.h" -namespace audio_tools { - -/** - * @brief R2R driver base class - * @ingroup platform - * @author Phil Schatzmann - * @copyright GPLv3 - */ - -class R2RDriverBase { - public: - virtual void setupPins(Vector &channel1_pins, - Vector &channel2_pins) = 0; - - virtual void writePins(int channels, int channel, unsigned uvalue) = 0; -}; - -/** - * @brief R2R driver which uses the Arduino API to setup and write to the - * digital pins - * @ingroup platform - * @author Phil Schatzmann - * @copyright GPLv3 - */ - -class R2RDriver : public R2RDriverBase { - public: - void setupPins(Vector &channel1_pins, - Vector &channel2_pins) override { - TRACED(); - p_channel1_pins = &channel1_pins; - p_channel2_pins = &channel2_pins; - - for (auto pin : channel1_pins) { - LOGI("Setup channel1 pin %d", pin); - pinMode(pin, OUTPUT); - } - for (int pin : channel2_pins) { - LOGI("Setup channel2 pin %d", pin); - pinMode(pin, OUTPUT); - } - }; - - void writePins(int channels, int channel, unsigned uvalue) override { - switch (channel) { - case 0: - for (int j = 0; j < (*p_channel1_pins).size(); j++) { - int pin = (*p_channel1_pins)[j]; - if (pin >= 0) digitalWrite(pin, (uvalue >> j) & 1); - } - break; - case 1: - for (int j = 0; j < (*p_channel2_pins).size(); j++) { - int pin = (*p_channel2_pins)[j]; - if (pin >= 0) digitalWrite(pin, (uvalue >> j) & 1); - } - break; - } - } - - protected: - Vector *p_channel1_pins = nullptr; - Vector *p_channel2_pins = nullptr; -} r2r_driver; - -/** - * @brief R2R configuration - * @author Phil Schatzmann - * @copyright GPLv3 - */ - -class R2RConfig : public AudioInfo { - public: - Vector channel1_pins; - Vector channel2_pins; - uint16_t buffer_size = DEFAULT_BUFFER_SIZE; - uint16_t buffer_count = 2; // double buffer - R2RDriverBase *driver = &r2r_driver; // by default use Arduino driver - bool is_blocking = true; - int blocking_retry_delay_ms = 5; - int timer_id = 0; -}; - -/** - * @brief Output to R-2R DAC. - * You need to define the used digital pins in the configuration. Any - * number of bits is supported on max 2 channels. For a 4 bit single - * channel, you need to define 4 digital pins. - * see https://www.electronics-tutorials.ws/combination/r-2r-dac.html - * The default driver implementation uses Arduino digitalWrite(). You - * can provide your own optimized driver. - * @ingroup io - * @author Phil Schatzmann - * @copyright GPLv3 - */ - -class R2ROutput : public AudioOutput { - public: - R2RConfig defaultConfig() { - R2RConfig r; - return r; - } - - bool begin(R2RConfig c) { - TRACED(); - cfg = c; - rcfg = c; - return begin(); - } - - bool begin() override { - TRACED(); - if (cfg.channels == 0 || cfg.channels > 2) { - LOGE("channels is %d", cfg.channels); - return false; - } - if (rcfg.channel1_pins.size() == 0) { - LOGE("channel1_pins not defined"); - return false; - } - if (cfg.channels == 2 && - rcfg.channel2_pins.size() != rcfg.channel1_pins.size()) { - LOGE("channel2_pins not defined"); - return false; - } - if (rcfg.buffer_size * rcfg.buffer_count == 0) { - LOGE("buffer_size or buffer_count is 0"); - return false; - } - buffer.resize(rcfg.buffer_size, rcfg.buffer_count); - rcfg.driver->setupPins(rcfg.channel1_pins, rcfg.channel2_pins); - - // setup timer - timer.setCallbackParameter(this); - timer.setIsSave(true); - timer.setTimer(rcfg.timer_id); - return timer.begin(r2r_timer_callback, cfg.sample_rate, HZ); - } - - size_t write(const uint8_t *data, size_t len) override { - LOGD("write: %d", len); - size_t result = 0; - // if buffer has not been allocated (buffer_size==0) - if (len > rcfg.buffer_size) { - LOGE("buffer_size %d too small for write size: %d", rcfg.buffer_size, - len); - return len; - } - - if (rcfg.is_blocking){ - // write of all bytes - int open = len; - while(open > 0){ - int written = buffer.writeArray(data + result, open); - open -= written; - result += written; - if (open > 0){ - delay(rcfg.blocking_retry_delay_ms); - } - } - } else { - // write as much as possible - result = buffer.writeArray(data, len); - } - // activate output when buffer is half full - if (!is_active && buffer.bufferCountFilled() >= rcfg.buffer_count / 2) { - LOGI("is_active = true"); - is_active = true; - } - - return result; - } - - protected: - TimerAlarmRepeating timer; - // Double buffer - NBuffer buffer{DEFAULT_BUFFER_SIZE, 0}; - R2RConfig rcfg; - - void writeValue(int channel) { - switch (cfg.bits_per_sample) { - case 8: - return writeValueT(channel); - case 16: - return writeValueT(channel); - case 24: - return writeValueT(channel); - case 32: - return writeValueT(channel); - } - } - - template - void writeValueT(int channel) { - // don't do anything if we do not have enough data - if (buffer.available() < sizeof(T)) return; - - // get next value from buffer - T value = 0; - buffer.readArray((uint8_t *)&value, sizeof(T)); - // convert to unsigned - unsigned uvalue = (int)value + NumberConverter::maxValueT() + 1; - // scale value - uvalue = uvalue >> ((sizeof(T) * 8) - rcfg.channel1_pins.size()); - // Serial.println(uvalue); - - // output pins - rcfg.driver->writePins(cfg.channels, channel, uvalue); - } - - static void r2r_timer_callback(void *ptr) { - R2ROutput *self = (R2ROutput *)ptr; - if (self->is_active) { - // output channel 1 - self->writeValue(0); - // output channel 2 - if (self->cfg.channels == 2) self->writeValue(1); - }; - } -}; - -} // namespace audio_tools \ No newline at end of file diff --git a/src/AudioTools/AudioLibs/VBANStream.h b/src/AudioTools/AudioLibs/VBANStream.h deleted file mode 100644 index 315d5fbfbc..0000000000 --- a/src/AudioTools/AudioLibs/VBANStream.h +++ /dev/null @@ -1,444 +0,0 @@ - -#include -#include - -#include "AudioTools/AudioLibs/vban/vban.h" -#include "AudioTools/CoreAudio/AudioStreams.h" -#include "AudioTools/Concurrency/RTOS/BufferRTOS.h" - -namespace audio_tools { - -class VBANConfig : public AudioInfo { - public: - VBANConfig() { - sample_rate = 11025; - channels = 1; - bits_per_sample = 16; - } - RxTxMode mode; - /// name of the stream - const char* stream_name = "Stream1"; - /// default port is 6980 - uint16_t udp_port = 6980; - /// Use {0,0,0,0}; as broadcast address - IPAddress target_ip{0, 0, 0, 0}; - /// ssid for wifi connection - const char* ssid = nullptr; - /// password for wifi connection - const char* password = nullptr; - int rx_buffer_count = 30; - // set to true if samples are generated faster then sample rate - bool throttle_active = false; - // when negative the number of ms that are subtracted from the calculated wait - // time to fine tune Overload and Underruns - int throttle_correction_us = 0; - // defines the max write size - int max_write_size = - DEFAULT_BUFFER_SIZE * 2; // just good enough for 44100 stereo - uint8_t format = 0; -}; - -/** - * @brief VBAN Audio Source and Sink for the ESP32. For further details please - * see https://vb-audio.com/Voicemeeter/vban.htm . - * Inspired by https://github.com/rkinnett/ESP32-VBAN-Audio-Source/tree/master - * and https://github.com/rkinnett/ESP32-VBAN-Network-Audio-Player - * @ingroup communications - * @author Phil Schatzmann - * @copyright GPLv3 - */ - -class VBANStream : public AudioStream { - public: - VBANConfig defaultConfig(RxTxMode mode = TX_MODE) { - VBANConfig def; - def.mode = mode; - return def; - } - - void setOutput(Print &out){ - p_out = &out; - } - - void setAudioInfo(AudioInfo info) override { - cfg.copyFrom(info); - AudioStream::setAudioInfo(info); - auto thc = throttle.defaultConfig(); - thc.copyFrom(info); - thc.correction_us = cfg.throttle_correction_us; - throttle.begin(thc); - if (cfg.mode == TX_MODE) { - configure_tx(); - } - } - - bool begin(VBANConfig cfg) { - this->cfg = cfg; - setAudioInfo(cfg); - return begin(); - } - - bool begin() { - if (cfg.mode == TX_MODE) { - if (cfg.bits_per_sample != 16) { - LOGE("Only 16 bits supported") - return false; - } - tx_buffer.resize(VBAN_PACKET_NUM_SAMPLES); - return begin_tx(); - } else { -#ifdef ESP32 - rx_buffer.resize(DEFAULT_BUFFER_SIZE * cfg.rx_buffer_count); - rx_buffer.setReadMaxWait(10); -#else - rx_buffer.resize(DEFAULT_BUFFER_SIZE, cfg.rx_buffer_count); -#endif - return begin_rx(); - } - } - - size_t write(const uint8_t* data, size_t len) override { - if (!udp_connected) return 0; - - int16_t* adc_data = (int16_t*)data; - size_t samples = len / (cfg.bits_per_sample/8); - - // limit output speed - if (cfg.throttle_active) { - throttle.delayFrames(samples / cfg.channels); - } - - for (int j = 0; j < samples; j++) { - tx_buffer.write(adc_data[j]); - if (tx_buffer.availableForWrite() == 0) { - memcpy(vban.data_frame, tx_buffer.data(), vban.packet_data_bytes); - *vban.packet_counter = packet_counter; // increment packet counter - // Send packet - if (cfg.target_ip == broadcast_address) { - udp.broadcastTo((uint8_t*)&vban.packet, vban.packet_total_bytes, - cfg.udp_port); - } else { - udp.writeTo((uint8_t*)&vban.packet, vban.packet_total_bytes, - cfg.target_ip, cfg.udp_port); - } - // defile delay start time - packet_counter++; - tx_buffer.reset(); - } - } - return len; - } - - int availableForWrite() { return cfg.max_write_size; } - - size_t readBytes(uint8_t* data, size_t len) override { - TRACED(); - size_t samples = len / (cfg.bits_per_sample/8); - if (cfg.throttle_active) { - throttle.delayFrames(samples / cfg.channels); - } - return rx_buffer.readArray(data, len); - } - - int available() { return available_active ? rx_buffer.available() : 0; } - - protected: - const IPAddress broadcast_address{0, 0, 0, 0}; - AsyncUDP udp; - VBan vban; - VBANConfig cfg; - SingleBuffer tx_buffer{0}; - #ifdef ESP32 - BufferRTOS rx_buffer{ 0}; - #else - NBuffer rx_buffer{DEFAULT_BUFFER_SIZE, 0}; - #endif - bool udp_connected = false; - uint32_t packet_counter = 0; - Throttle throttle; - size_t bytes_received = 0; - bool available_active = false; - Print *p_out = nullptr; - - bool begin_tx() { - if (!configure_tx()) { - return false; - } - start_wifi(); - if (WiFi.status() != WL_CONNECTED) { - LOGE("Wifi not connected"); - return false; - } - WiFi.setSleep(false); - IPAddress myIP = WiFi.localIP(); - udp_connected = udp.connect(myIP, cfg.udp_port); - return udp_connected; - } - - bool begin_rx() { - start_wifi(); - if (WiFi.status() != WL_CONNECTED) { - LOGE("Wifi not connected"); - return false; - } - WiFi.setSleep(false); - bytes_received = 0; - this->available_active = false; - // connect to target - if (!udp.listen(cfg.udp_port)) { - LOGE("Could not connect to '%s:%d' target", toString(cfg.target_ip), - cfg.udp_port); - } - // handle data - udp.onPacket([this](AsyncUDPPacket packet) { receive_udp(packet); }); - - return true; - } - - bool configure_tx() { - int rate = vban_sample_rate(); - if (rate < 0) { - LOGE("Invalid sample rate: %d", cfg.sample_rate); - return false; - } - configure_vban((VBanSampleRates)rate); - return true; - } - - void start_wifi() { - if (cfg.ssid == nullptr) return; - if (cfg.password == nullptr) return; - LOGI("ssid %s", cfg.ssid); - // Setup Wifi: - WiFi.begin(cfg.ssid, cfg.password); // Connect to your WiFi router - while (WiFi.status() != WL_CONNECTED) { // Wait for connection - delay(500); - Serial.print("."); - } - Serial.println(); - - LOGI("Wifi connected to IP (%d.%d.%d.%d)", WiFi.localIP()[0], - WiFi.localIP()[1], WiFi.localIP()[2], WiFi.localIP()[3]); - } - - void configure_vban(VBanSampleRates rate) { - // Set vban packet header, counter, and data frame pointers to respective - // parts of packet: - vban.hdr = (VBanHeader*)&vban.packet[0]; - vban.packet_counter = (uint32_t*)&vban.packet[VBAN_PACKET_HEADER_BYTES]; - vban.data_frame = - (uint8_t*)&vban - .packet[VBAN_PACKET_HEADER_BYTES + VBAN_PACKET_COUNTER_BYTES]; - - // Setup the packet header: - strncpy(vban.hdr->preamble, "VBAN", 4); - vban.hdr->sample_rate = - static_cast(VBAN_PROTOCOL_AUDIO) | - rate; // 11025 Hz, which matches default sample rate for soundmodem - vban.hdr->num_samples = - (VBAN_PACKET_NUM_SAMPLES / cfg.channels) - 1; // 255 = 256 samples - vban.hdr->num_channels = cfg.channels - 1; // 0 = 1 channel - vban.hdr->sample_format = - static_cast(VBAN_BITFMT_16_INT) | VBAN_CODEC_PCM; // int16 PCM - strncpy(vban.hdr->stream_name, cfg.stream_name, - min((int)strlen(cfg.stream_name), VBAN_STREAM_NAME_SIZE)); - - vban.packet_data_bytes = - (vban.hdr->num_samples + 1) * (vban.hdr->num_channels + 1) * - ((vban.hdr->sample_format & VBAN_BIT_RESOLUTION_MASK) + 1); - vban.packet_total_bytes = vban.packet_data_bytes + - VBAN_PACKET_HEADER_BYTES + - VBAN_PACKET_COUNTER_BYTES; - } - - int vban_sample_rate() { - int result = -1; - switch (cfg.sample_rate) { - case 6000: - result = SAMPLE_RATE_6000_HZ; - break; - case 12000: - result = SAMPLE_RATE_12000_HZ; - break; - case 24000: - result = SAMPLE_RATE_24000_HZ; - break; - case 48000: - result = SAMPLE_RATE_48000_HZ; - break; - case 96000: - result = SAMPLE_RATE_96000_HZ; - break; - case 192000: - result = SAMPLE_RATE_192000_HZ; - break; - case 384000: - result = SAMPLE_RATE_384000_HZ; - break; - case 8000: - result = SAMPLE_RATE_8000_HZ; - break; - case 16000: - result = SAMPLE_RATE_16000_HZ; - break; - case 32000: - result = SAMPLE_RATE_32000_HZ; - break; - case 64000: - result = SAMPLE_RATE_64000_HZ; - break; - case 128000: - result = SAMPLE_RATE_128000_HZ; - break; - case 256000: - result = SAMPLE_RATE_256000_HZ; - break; - case 512000: - result = SAMPLE_RATE_512000_HZ; - break; - case 11025: - result = SAMPLE_RATE_11025_HZ; - break; - case 22050: - result = SAMPLE_RATE_22050_HZ; - break; - case 44100: - result = SAMPLE_RATE_44100_HZ; - break; - case 88200: - result = SAMPLE_RATE_88200_HZ; - break; - case 176400: - result = SAMPLE_RATE_176400_HZ; - break; - case 352800: - result = SAMPLE_RATE_352800_HZ; - break; - case 705600: - result = SAMPLE_RATE_705600_HZ; - break; - } - return result; - } - - const char* toString(IPAddress adr) { - static char str[11] = {0}; - snprintf(str, 11, "%d.%d.%d.%d", adr[0], adr[1], adr[2], adr[3]); - return str; - } - - /** - * @brief VBAN adjusts the number of samples per packet according to sample - *rate. Assuming 16-bit PCM mono, sample rates 11025, 22050, 44100, and 88200 - *yield packets containing 64, 128, 256, and 256 samples per packet, - *respectively. The even-thousands sample rates below 48000 yield - *non-power-of-2 lengths. For example, sample rate 24000 yields 139 samples - *per packet. This VBAN->DMA->DAC method seems to require the dma buffer - *length be set equal to the number of samples in each VBAN packet. ESP32 - *I2S/DMA does not seem to handle non-power-of-2 buffer lengths well. Sample - *rate 24000 doesn't work reliably at all. Sample rate 32000 is stable but - *stutters. Recommend selecting from sample rates 11025, 22050, 44100, and - *above And set samplesPerPacket to 64 for 11025, 128 for 22050, or 256 for - *all else. - **/ - - void receive_udp(AsyncUDPPacket& packet) { - uint16_t vban_rx_data_bytes, vban_rx_sample_count; - int16_t* vban_rx_data; - uint32_t* vban_rx_pkt_nbr; - uint16_t outBuf[VBAN_PACKET_MAX_SAMPLES + 1]; - size_t bytesOut; - - int len = packet.length(); - if (len > 0) { - LOGD("receive_udp %d", len); - uint8_t* udpIncomingPacket = packet.data(); - - // receive incoming UDP packet - // Check if packet length meets VBAN specification: - if (len <= (VBAN_PACKET_HEADER_BYTES + VBAN_PACKET_COUNTER_BYTES) || - len > VBAN_PACKET_MAX_LEN_BYTES) { - LOGE("Packet length %u bytes", len); - rx_buffer.reset(); - return; - } - - // Check if preamble matches VBAN format: - if (strncmp("VBAN", (const char*)udpIncomingPacket, 4) != 0) { - LOGE("Unrecognized preamble %.4s", udpIncomingPacket); - return; - } - - vban_rx_data_bytes = - len - (VBAN_PACKET_HEADER_BYTES + VBAN_PACKET_COUNTER_BYTES); - vban_rx_pkt_nbr = (uint32_t*)&udpIncomingPacket[VBAN_PACKET_HEADER_BYTES]; - vban_rx_data = (int16_t*)&udpIncomingPacket[VBAN_PACKET_HEADER_BYTES + - VBAN_PACKET_COUNTER_BYTES]; - vban_rx_sample_count = vban_rx_data_bytes / (cfg.bits_per_sample / 8); - uint8_t vbanSampleRateIdx = udpIncomingPacket[4] & VBAN_SR_MASK; - uint8_t vbchannels = udpIncomingPacket[6] + 1; - uint8_t vbframes = udpIncomingPacket[5] + 1; - uint8_t vbformat = udpIncomingPacket[7] & VBAN_PROTOCOL_MASK;; - uint8_t vbformat_bits = udpIncomingPacket[7] & VBAN_BIT_RESOLUTION_MASK;; - uint32_t vbanSampleRate = VBanSRList[vbanSampleRateIdx]; - - //LOGD("sample_count: %d - frames: %d", vban_rx_sample_count, vbframes); - //assert (vban_rx_sample_count == vbframes*vbchannels); - - // E.g. do not process any text - if (vbformat != cfg.format){ - LOGE("Format ignored: 0x%x", vbformat); - return; - } - - // Currently we support only 16 bits. - if (vbformat_bits != VBAN_BITFMT_16_INT){ - LOGE("Format only 16 bits supported"); - return; - } - - // Just to be safe, re-check sample count against max sample count to - // avoid overrunning outBuf later - if (vban_rx_sample_count > VBAN_PACKET_MAX_SAMPLES) { - LOGE("unexpected packet size: %u", vban_rx_sample_count); - return; - } - - // update sample rate - if (cfg.sample_rate != vbanSampleRate || cfg.channels != vbchannels) { - // update audio info - cfg.sample_rate = vbanSampleRate; - cfg.channels = vbchannels; - setAudioInfo(cfg); - // remove any buffered data - rx_buffer.reset(); - available_active = false; - } - - if (p_out!=nullptr){ - int size_written = p_out->write((uint8_t*)vban_rx_data, vban_rx_data_bytes); - if (size_written != vban_rx_data_bytes) { - LOGE("buffer overflow %d -> %d", vban_rx_data_bytes, size_written); - } - return; - } - - // write data to buffer - int size_written = rx_buffer.writeArray((uint8_t*)vban_rx_data, vban_rx_data_bytes); - if (size_written != vban_rx_data_bytes) { - LOGE("buffer overflow %d -> %d", vban_rx_data_bytes, size_written); - } - - // report available bytes only when buffer is 50% full - if (!available_active) { - bytes_received += vban_rx_data_bytes; - if (bytes_received >= cfg.rx_buffer_count * DEFAULT_BUFFER_SIZE * 0.75){ - available_active = true; - LOGI("Activating vban"); - } - } - } - } -}; - -} // namespace audio_tools \ No newline at end of file diff --git a/src/AudioTools/AudioLibs/vban/vban.h b/src/AudioTools/AudioLibs/vban/vban.h deleted file mode 100644 index 59785c2853..0000000000 --- a/src/AudioTools/AudioLibs/vban/vban.h +++ /dev/null @@ -1,205 +0,0 @@ -/* - * This file is part of vban. - * Copyright (c) 2015 by Benoît Quiniou - * - * vban is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * vban is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with vban. If not, see . - */ - - -/////////////////////////////////////////////////////////////////////// -// -// MODIFIED by R. Kinnett, https://github.com/rkinnett, 2020 -// -/////////////////////////////////////////////////////////////////////// - - -#ifndef __VBAN_H__ -#define __VBAN_H__ - -#include - - -#define VBAN_HEADER_SIZE (4 + 1 + 1 + 1 + 1 + 16) -#define VBAN_STREAM_NAME_SIZE 16 -#define VBAN_PROTOCOL_MAX_SIZE 1464 -#define VBAN_DATA_MAX_SIZE (VBAN_PROTOCOL_MAX_SIZE - VBAN_HEADER_SIZE) -#define VBAN_CHANNELS_MAX_NB 256 -#define VBAN_SAMPLES_MAX_NB 256 - - - -#define VBAN_PACKET_NUM_SAMPLES 256 -#define VBAN_PACKET_MAX_SAMPLES 256 -#define VBAN_PACKET_HEADER_BYTES 24 -#define VBAN_PACKET_COUNTER_BYTES 4 -#define VBAN_PACKET_MAX_LEN_BYTES (VBAN_PACKET_HEADER_BYTES + VBAN_PACKET_COUNTER_BYTES + VBAN_PACKET_MAX_SAMPLES*2) - - -typedef struct -{ - char preamble[4]; /* contains 'V' 'B', 'A', 'N' */ - uint8_t sample_rate; /* SR index (see SRList above) */ - uint8_t num_samples; /* nb sample per frame (1 to 256) */ - uint8_t num_channels; /* nb channel (1 to 256) */ - uint8_t sample_format; /* mask = 0x07 (nb Byte integer from 1 to 4) */ - char stream_name[VBAN_STREAM_NAME_SIZE]; /* stream name */ -} VBanHeader; - - -typedef struct { - VBanHeader* hdr; - uint32_t* packet_counter; - uint8_t* data_frame; - uint8_t packet[VBAN_PROTOCOL_MAX_SIZE]; - uint16_t packet_data_bytes; - uint16_t packet_total_bytes; -} VBan; - - - - -#define VBAN_SR_MASK 0x1F -#define VBAN_SR_MAXNUMBER 21 -static long const VBanSRList[VBAN_SR_MAXNUMBER]= -{ - 6000, 12000, 24000, 48000, 96000, 192000, 384000, - 8000, 16000, 32000, 64000, 128000, 256000, 512000, - 11025, 22050, 44100, 88200, 176400, 352800, 705600 -}; - -enum VBanSampleRates -{ - SAMPLE_RATE_6000_HZ, - SAMPLE_RATE_12000_HZ, - SAMPLE_RATE_24000_HZ, - SAMPLE_RATE_48000_HZ, - SAMPLE_RATE_96000_HZ, - SAMPLE_RATE_192000_HZ, - SAMPLE_RATE_384000_HZ, - SAMPLE_RATE_8000_HZ, - SAMPLE_RATE_16000_HZ, - SAMPLE_RATE_32000_HZ, - SAMPLE_RATE_64000_HZ, - SAMPLE_RATE_128000_HZ, - SAMPLE_RATE_256000_HZ, - SAMPLE_RATE_512000_HZ, - SAMPLE_RATE_11025_HZ, - SAMPLE_RATE_22050_HZ, - SAMPLE_RATE_44100_HZ, - SAMPLE_RATE_88200_HZ, - SAMPLE_RATE_176400_HZ, - SAMPLE_RATE_352800_HZ, - SAMPLE_RATE_705600_HZ -}; - - -#define VBAN_PROTOCOL_MASK 0xE0 -enum VBanProtocol -{ - VBAN_PROTOCOL_AUDIO = 0x00, - VBAN_PROTOCOL_SERIAL = 0x20, - VBAN_PROTOCOL_TXT = 0x40, - VBAN_PROTOCOL_UNDEFINED_1 = 0x80, - VBAN_PROTOCOL_UNDEFINED_2 = 0xA0, - VBAN_PROTOCOL_UNDEFINED_3 = 0xC0, - VBAN_PROTOCOL_UNDEFINED_4 = 0xE0 -}; - -#define VBAN_BIT_RESOLUTION_MASK 0x07 -enum VBanBitResolution -{ - VBAN_BITFMT_8_INT = 0, - VBAN_BITFMT_16_INT, - VBAN_BITFMT_24_INT, - VBAN_BITFMT_32_INT, - VBAN_BITFMT_32_FLOAT, - VBAN_BITFMT_64_FLOAT, - VBAN_BITFMT_12_INT, - VBAN_BITFMT_10_INT, - VBAN_BIT_RESOLUTION_MAX -}; - -static int const VBanBitResolutionSize[VBAN_BIT_RESOLUTION_MAX] = -{ - 1, 2, 3, 4, 4, 8, -}; - -#define VBAN_RESERVED_MASK 0x08 - -#define VBAN_CODEC_MASK 0xF0 -enum VBanCodec -{ - VBAN_CODEC_PCM = 0x00, - VBAN_CODEC_VBCA = 0x10, - VBAN_CODEC_VBCV = 0x20, - VBAN_CODEC_UNDEFINED_3 = 0x30, - VBAN_CODEC_UNDEFINED_4 = 0x40, - VBAN_CODEC_UNDEFINED_5 = 0x50, - VBAN_CODEC_UNDEFINED_6 = 0x60, - VBAN_CODEC_UNDEFINED_7 = 0x70, - VBAN_CODEC_UNDEFINED_8 = 0x80, - VBAN_CODEC_UNDEFINED_9 = 0x90, - VBAN_CODEC_UNDEFINED_10 = 0xA0, - VBAN_CODEC_UNDEFINED_11 = 0xB0, - VBAN_CODEC_UNDEFINED_12 = 0xC0, - VBAN_CODEC_UNDEFINED_13 = 0xD0, - VBAN_CODEC_UNDEFINED_14 = 0xE0, - VBAN_CODEC_USER = 0xF0 -}; - - -/******************************************************** - * TEXT SUB PROTOCOL * - ********************************************************/ - -#define VBAN_BPS_MASK 0xE0 -#define VBAN_BPS_MAXNUMBER 25 -static long const VBanBPSList[VBAN_BPS_MAXNUMBER] = -{ - 0, 110, 150, 300, 600, - 1200, 2400, 4800, 9600, 14400, - 19200, 31250, 38400, 57600, 115200, - 128000, 230400, 250000, 256000, 460800, - 921600,1000000,1500000,2000000, 3000000 -}; - -#define VBAN_DATATYPE_MASK 0x07 -#define VBAN_DATATYPE_MAXNUMBER 1 -enum VBanDataTypeList -{ - VBAN_DATATYPE_8BITS = 0 -}; - -#define VBAN_STREAMTYPE_MASK 0xF0 -enum VBanStreamType -{ - VBAN_TXT_ASCII = 0x00, - VBAN_TXT_UTF8 = 0x10, - VBAN_TXT_WCHAR = 0x20, - VBAN_TXT_UNDEFINED_3 = 0x30, - VBAN_TXT_UNDEFINED_4 = 0x40, - VBAN_TXT_UNDEFINED_5 = 0x50, - VBAN_TXT_UNDEFINED_6 = 0x60, - VBAN_TXT_UNDEFINED_7 = 0x70, - VBAN_TXT_UNDEFINED_8 = 0x80, - VBAN_TXT_UNDEFINED_9 = 0x90, - VBAN_TXT_UNDEFINED_10 = 0xA0, - VBAN_TXT_UNDEFINED_11 = 0xB0, - VBAN_TXT_UNDEFINED_12 = 0xC0, - VBAN_TXT_UNDEFINED_13 = 0xD0, - VBAN_TXT_UNDEFINED_14 = 0xE0, - VBAN_TXT_USER = 0xF0 -}; - -#endif /*__VBAN_H__*/ diff --git a/src/AudioTools/AudioLogger.h b/src/AudioTools/AudioLogger.h new file mode 100644 index 0000000000..2b318727ce --- /dev/null +++ b/src/AudioTools/AudioLogger.h @@ -0,0 +1,182 @@ +#pragma once + +#include "AudioConfig.h" +#ifdef ARDUINO +# include "Stream.h" +#endif +// Logging Implementation +#if USE_AUDIO_LOGGING + +namespace audio_tools { + +#if defined(ESP32) && defined(SYNCHRONIZED_LOGGING) +# include "freertos/FreeRTOS.h" +# include "freertos/task.h" +static portMUX_TYPE mutex_logger = portMUX_INITIALIZER_UNLOCKED; +#endif + +/** + * @brief A simple Logger that writes messages dependent on the log level + * @ingroup tools + * @author Phil Schatzmann + * @copyright GPLv3 + * + */ +class AudioLogger { + public: + /** + * @brief Supported log levels. You can change the default log level with the help of the PICO_LOG_LEVEL define. + * + */ + enum LogLevel { + Debug, + Info, + Warning, + Error + }; + + /// activate the logging + void begin(Stream& out, LogLevel level=LOG_LEVEL) { + this->log_stream_ptr = &out; + this->log_level = level; + } + + /// checks if the logging is active + bool isLogging(LogLevel level = Info){ + return log_stream_ptr!=nullptr && level >= log_level; + } + + AudioLogger &prefix(const char* file, int line, LogLevel current_level){ + lock(); + printPrefix(file,line,current_level); + return *this; + } + + void println(){ +#if defined(IS_DESKTOP) || defined(IS_DESKTOP_WITH_TIME_ONLY) + fprintf( stderr, "%s\n", print_buffer); +#else + log_stream_ptr->println(print_buffer); +#endif + print_buffer[0]=0; + unlock(); + } + + char* str() { + return print_buffer; + } + + /// provides the singleton instance + static AudioLogger &instance(){ + static AudioLogger *ptr; + if (ptr==nullptr){ + ptr = new AudioLogger; + } + return *ptr; + } + + LogLevel level() { + return log_level; + } + + protected: + Stream *log_stream_ptr = &LOG_STREAM; + const char* TAG = "AudioTools"; + LogLevel log_level = LOG_LEVEL; + char print_buffer[LOG_PRINTF_BUFFER_SIZE]; + + AudioLogger() {} + + const char* levelName(LogLevel level) const { + switch(level){ + case Debug: + return "D"; + case Info: + return "I"; + case Warning: + return "W"; + case Error: + return "E"; + } + return ""; + } + + int printPrefix(const char* file, int line, LogLevel current_level) const { + const char* file_name = strrchr(file, '/') ? strrchr(file, '/') + 1 : file; + const char* level_code = levelName(current_level); + int len = log_stream_ptr->print("["); + len += log_stream_ptr->print(level_code); + len += log_stream_ptr->print("] "); + len += log_stream_ptr->print(file_name); + len += log_stream_ptr->print(" : "); + len += log_stream_ptr->print(line); + len += log_stream_ptr->print(" - "); + return len; + } + + void lock(){ + #if defined(ESP32) && defined(SYNCHRONIZED_LOGGING) + portENTER_CRITICAL(&mutex_logger); + #endif + } + + void unlock(){ + #if defined(ESP32) && defined(SYNCHRONIZED_LOGGING) + portEXIT_CRITICAL(&mutex_logger); + #endif + } +}; + +} + + +//#define LOG_OUT(level, fmt, ...) {AudioLogger::instance().prefix(__FILE__,__LINE__, level);cont char PROGMEM *fmt_P=F(fmt); snprintf_P(AudioLogger::instance().str(), LOG_PRINTF_BUFFER_SIZE, fmt, ##__VA_ARGS__); AudioLogger::instance().println();} +#define LOG_OUT_PGMEM(level, fmt, ...) { \ + AudioLogger::instance().prefix(__FILE__,__LINE__, level); \ + snprintf(AudioLogger::instance().str(), LOG_PRINTF_BUFFER_SIZE, PSTR(fmt), ##__VA_ARGS__); \ + AudioLogger::instance().println();\ +} + +#define LOG_OUT(level, fmt, ...) { \ + AudioLogger::instance().prefix(__FILE__,__LINE__, level); \ + snprintf(AudioLogger::instance().str(), LOG_PRINTF_BUFFER_SIZE, fmt, ##__VA_ARGS__); \ + AudioLogger::instance().println();\ +} + +// Log statments which store the fmt string in Progmem +#define LOGD(fmt, ...) if (AudioLogger::instance().level()<=AudioLogger::Debug) { LOG_OUT_PGMEM(AudioLogger::Debug, fmt, ##__VA_ARGS__);} +#define LOGI(fmt, ...) if (AudioLogger::instance().level()<=AudioLogger::Info) { LOG_OUT_PGMEM(AudioLogger::Info, fmt, ##__VA_ARGS__);} +#define LOGW(fmt, ...) if (AudioLogger::instance().level()<=AudioLogger::Warning) { LOG_OUT_PGMEM(AudioLogger::Warning, fmt, ##__VA_ARGS__);} +#define LOGE(fmt, ...) if (AudioLogger::instance().level()<=AudioLogger::Error) { LOG_OUT_PGMEM(AudioLogger::Error, fmt, ##__VA_ARGS__);} + +// Just log file and line +#if defined(NO_TRACED) +# define TRACED() +#else +# define TRACED() if (AudioLogger::instance().level()<=AudioLogger::Debug) { LOG_OUT(AudioLogger::Debug, LOG_METHOD);} +#endif + +#if defined(NO_TRACEI) +# define TRACEI() +#else +# define TRACEI() if (AudioLogger::instance().level()<=AudioLogger::Info) { LOG_OUT(AudioLogger::Info, LOG_METHOD);} +#endif +#define TRACEW() if (AudioLogger::instance().level()<=AudioLogger::Warning) { LOG_OUT(AudioLogger::Warning, LOG_METHOD);} +#define TRACEE() if (AudioLogger::instance().level()<=AudioLogger::Error) { LOG_OUT(AudioLogger::Error, LOG_METHOD);} + + +#else + +// Switch off logging +#define LOGD(...) +#define LOGI(...) +#define LOGW(...) +#define LOGE(...) +#define TRACED() +#define TRACEI() +#define TRACEW() +#define TRACEE() + +#endif + + diff --git a/src/AudioTools/CoreAudio/AudioOutput.h b/src/AudioTools/AudioOutput.h similarity index 59% rename from src/AudioTools/CoreAudio/AudioOutput.h rename to src/AudioTools/AudioOutput.h index f66e25dca8..8509ec45cc 100644 --- a/src/AudioTools/CoreAudio/AudioOutput.h +++ b/src/AudioTools/AudioOutput.h @@ -1,15 +1,16 @@ #pragma once -#include "AudioToolsConfig.h" -#include "AudioTools/CoreAudio/AudioTypes.h" -#include "AudioTools/CoreAudio/BaseConverter.h" -#include "AudioTools/CoreAudio/Buffers.h" +#include "AudioBasic/Int24.h" +#include "AudioConfig.h" +#include "AudioTools/AudioTypes.h" +#include "AudioTools/BaseConverter.h" +#include "AudioTools/Buffers.h" namespace audio_tools { -#if USE_PRINT_FLUSH -#define PRINT_FLUSH_OVERRIDE override +#if !defined(ARDUINO) || defined(IS_DESKTOP) +#define FLUSH_OVERRIDE override #else -#define PRINT_FLUSH_OVERRIDE +#define FLUSH_OVERRIDE #endif /** @@ -23,7 +24,7 @@ class AudioOutput : public Print, public: virtual ~AudioOutput() = default; - virtual size_t write(const uint8_t *data, size_t len) override = 0; + virtual size_t write(const uint8_t *buffer, size_t size) override = 0; virtual size_t write(uint8_t ch) override { if (tmp.isFull()) { @@ -36,21 +37,24 @@ class AudioOutput : public Print, // removed override because some old implementation did not define this method // as virtual - virtual void flush() PRINT_FLUSH_OVERRIDE { + virtual void flush() FLUSH_OVERRIDE { if (tmp.available() > 0) { write((const uint8_t *)tmp.address(), tmp.available()); } } // overwrite to do something useful - virtual void setAudioInfo(AudioInfo newInfo) override { + virtual void setAudioInfo(AudioInfo info) override { TRACED(); - if (cfg != newInfo){ - cfg = newInfo; - cfg.logInfo(); + cfg = info; + info.logInfo(); + if (p_notify != nullptr) { + p_notify->setAudioInfo(info); } - AudioInfo out = audioInfoOut(); - if (out) notifyAudioChange(out); + } + + virtual void setNotifyAudioChange(AudioInfoSupport &bi) override { + p_notify = &bi; } /// If true we need to release the related memory in the destructor @@ -67,40 +71,24 @@ class AudioOutput : public Print, } } - virtual bool begin(AudioInfo info) { + virtual bool begin(AudioInfo info){ setAudioInfo(info); return begin(); } - virtual bool begin() { - is_active = true; - return true; - } - virtual void end() { is_active = false; } + virtual bool begin() { is_active = true; return true; } + virtual void end() { is_active = false;} + operator bool() {return is_active;} - virtual operator bool() { return is_active; } protected: int tmpPos = 0; + AudioInfoSupport *p_notify = nullptr; AudioInfo cfg; SingleBuffer tmp{MAX_SINGLE_CHARS}; bool is_active = false; }; -/** - * @brief Abstract class: Objects can be put into a pipleline. - * @ingroup io - * @author Phil Schatzmann - * @copyright GPLv3 - */ - -class ModifyingOutput : public AudioOutput { - public: - /// Defines/Changes the output target - virtual void setOutput(Print& out) = 0; -}; - - /** * @brief Stream Wrapper which can be used to print the values as readable ASCII * to the screen to be analyzed in the Serial Plotter The frames are separated @@ -130,6 +118,7 @@ template class CsvOutput : public AudioOutput { /// Provides the current column delimiter const char *delimiter() { return delimiter_str; } + AudioInfo defaultConfig(RxTxMode mode) { return defaultConfig(); } /// Provides the default configuration @@ -142,44 +131,32 @@ template class CsvOutput : public AudioOutput { } /// Starts the processing with the defined number of channels - bool begin(AudioInfo info) override { return begin(info.channels); } + bool begin(AudioInfo info, Print &out = Serial) { + return begin(info.channels, out); + } /// Starts the processing with the defined number of channels - bool begin(int channels) { + bool begin(int channels, Print &out = Serial) { TRACED(); - cfg.channels = channels; - return begin(); - } - - /// (Re)start (e.g. if channels is set in constructor) - bool begin() override { + this->out_ptr = &out; this->is_active = true; - // if (out_ptr == &Serial){ - // Serial.setTimeout(60000); - // } + cfg.channels = channels; return true; } + /// defines the number of channels - virtual void setAudioInfo(AudioInfo info) override { + virtual void setAudioInfo(AudioInfo info) { TRACEI(); - this->is_active = true; info.logInfo(); cfg = info; }; /// Writes the data - formatted as CSV - to the output stream - virtual size_t write(const uint8_t *data, size_t len) override { - LOGD("CsvOutput::write: %d", (int)len); - if (!is_active) { - LOGE("is not active"); - return 0; - } - - if (len==0){ + virtual size_t write(const uint8_t *data, size_t len) { + if (!is_active) return 0; - } - + LOGD("CsvOutput::write: %d", (int)len); if (cfg.channels == 0) { LOGW("Channels not defined: using 2"); cfg.channels = 2; @@ -202,13 +179,10 @@ template class CsvOutput : public AudioOutput { LOGE("Unsupported size: %d for channels %d and bits: %d", (int)len, cfg.channels, cfg.bits_per_sample); } -#if USE_PRINT_FLUSH - out_ptr->flush(); -#endif return len; } - int availableForWrite() override { return 1024; } + int availableForWrite() { return 1024; } protected: T *data_ptr; @@ -220,18 +194,20 @@ template class CsvOutput : public AudioOutput { for (size_t j = 0; j < frameCount; j++) { for (int ch = 0; ch < cfg.channels; ch++) { if (out_ptr != nullptr && data_ptr != nullptr) { - T value = *data_ptr; + int value = *data_ptr; out_ptr->print(value); } data_ptr++; if (ch < cfg.channels - 1) - this->out_ptr->print(delimiter_str); + this->out_ptr->print(","); } this->out_ptr->println(); } } }; +// legacy name +template using CsvStream = CsvOutput; /** * @brief Creates a Hex Dump @@ -252,19 +228,20 @@ class HexDumpOutput : public AudioOutput { this->is_active = active; } - bool begin() override { + bool begin() { TRACED(); this->is_active = true; pos = 0; return is_active; } - void flush() override { - out_ptr->println(); + + void flush() { + Serial.println(); pos = 0; } - virtual size_t write(const uint8_t *data, size_t len) override { + virtual size_t write(const uint8_t *data, size_t len) { if (!is_active) return 0; TRACED(); @@ -273,10 +250,10 @@ class HexDumpOutput : public AudioOutput { out_ptr->print(" "); pos++; if (pos == 8) { - out_ptr->print(" - "); + Serial.print(" - "); } if (pos == 16) { - out_ptr->println(); + Serial.println(); pos = 0; } } @@ -294,17 +271,17 @@ class HexDumpOutput : public AudioOutput { int pos = 0; }; +// legacy name +using HexDumpStream = HexDumpOutput; /** - * @brief Mixing of multiple outputs to one final output. - * By default a RingBuffer is used as buffer type. + * @brief Mixing of multiple outputs to one final output * @ingroup transform * @author Phil Schatzmann * @copyright GPLv3 * @tparam T */ -template -class OutputMixer : public Print { +template class OutputMixer : public Print { public: OutputMixer() = default; @@ -329,8 +306,8 @@ class OutputMixer : public Print { update_total_weights(); } - /// Defines a new weight for the indicated channel: If you set it to 0.0 it is - /// muted. The initial value is 1.0 + /// Defines a new weight for the indicated channel: If you set it to 0 it is + /// muted. void setWeight(int channel, float weight) { if (channel < size()) { weights[channel] = weight; @@ -340,12 +317,13 @@ class OutputMixer : public Print { update_total_weights(); } - /// Starts the processing. - bool begin(int copy_buffer_size_bytes = DEFAULT_BUFFER_SIZE) { + bool begin(int copy_buffer_size_bytes = DEFAULT_BUFFER_SIZE, + MemoryType memoryType = PS_RAM) { is_active = true; size_bytes = copy_buffer_size_bytes; stream_idx = 0; - allocate_buffers(size_bytes); + memory_type = memoryType; + allocate_buffers(); return true; } @@ -364,31 +342,29 @@ class OutputMixer : public Print { /// Write the data from a simgle stream which will be mixed together (the /// stream idx is increased) - size_t write(const uint8_t *data, size_t len) override { - size_t result = write(stream_idx, data, len); + size_t write(const uint8_t *buffer_c, size_t bytes) override { + size_t result = write(stream_idx, buffer_c, bytes); // after writing the last stream we flush - if (is_auto_index) { - stream_idx++; - if (stream_idx >= output_count) { - flushMixer(); - } + stream_idx++; + if (stream_idx >= output_count) { + flushMixer(); } return result; } /// Write the data for an individual stream idx which will be mixed together size_t write(int idx, const uint8_t *buffer_c, size_t bytes) { - LOGD("write idx %d: %d", idx, (int)bytes); + LOGD("write idx %d: %d", stream_idx, bytes); size_t result = 0; - BaseBuffer *p_buffer = idx < output_count ? buffers[idx] : nullptr; + RingBuffer *p_buffer = idx < output_count ? buffers[idx] : nullptr; assert(p_buffer != nullptr); size_t samples = bytes / sizeof(T); - if (p_buffer->availableForWrite() >= samples) { - result = p_buffer->writeArray((T *)buffer_c, samples) * sizeof(T); + if (p_buffer->availableForWrite() < samples) { + LOGW("Available Buffer too small %d: requested: %d -> increase the " + "buffer size", + p_buffer->availableForWrite(), samples); } else { - LOGW("Available Buffer %d too small %d: requested: %d -> increase the " - "buffer size", (int) idx, - static_cast(p_buffer->availableForWrite()*sizeof(T)), (int)bytes); + result = p_buffer->writeArray((T *)buffer_c, samples) * sizeof(T); } return result; } @@ -400,23 +376,10 @@ class OutputMixer : public Print { /// Provides the bytes available to write for the indicated stream index int availableForWrite(int idx) { - BaseBuffer *p_buffer = buffers[idx]; + RingBuffer *p_buffer = buffers[idx]; if (p_buffer == nullptr) return 0; - return p_buffer->availableForWrite() * sizeof(T); - } - - /// Provides the available bytes in the buffer - int available(int idx){ - BaseBuffer *p_buffer = buffers[idx]; - if (p_buffer == nullptr) - return 0; - return p_buffer->available() * sizeof(T); - } - - /// Provides the % fill level of the buffer for the indicated index - int availablePercent(int idx){ - return 100.0 * available(idx) / size_bytes; + return p_buffer->availableForWrite(); } /// Force output to final destination @@ -425,8 +388,11 @@ class OutputMixer : public Print { bool result = false; // determine ringbuffer with mininum available data - size_t samples = availableSamples(); - // sum up samples + size_t samples = size_bytes / sizeof(T); + for (int j = 0; j < output_count; j++) { + samples = MIN(samples, (size_t)buffers[j]->available()); + } + if (samples > 0) { result = true; // mix data from ringbuffers to output @@ -436,95 +402,30 @@ class OutputMixer : public Print { float weight = weights[j]; // sum up input samples to result samples for (int i = 0; i < samples; i++) { - T sample = 0;; - buffers[j]->read(sample); - output[i] += weight * sample / total_weights; + output[i] += weight * buffers[j]->read() / total_weights; } } // write output - LOGD("write to final out: %d", static_cast(samples * sizeof(T))); + LOGD("write to final out: %d", samples * sizeof(T)); p_final_output->write((uint8_t *)output.data(), samples * sizeof(T)); } stream_idx = 0; return; } - int availableSamples() { - size_t samples = 0; - for (int j = 0; j < output_count; j++) { - int available_samples = buffers[j]->available(); - if (available_samples > 0){ - samples = MIN(size_bytes / sizeof(T), (size_t)available_samples); - } - } - return samples; - } - - /// Resizes the buffer to the indicated number of bytes - void resize(int size) { - if (size != size_bytes) { - allocate_buffers(size); - } - size_bytes = size; - } - - size_t writeSilence(size_t bytes) { - if (bytes == 0) return 0; - uint8_t silence[bytes]; - memset(silence, 0, bytes); - return write(stream_idx, silence, bytes); - } - - size_t writeSilence(int idx, size_t bytes){ - if (bytes == 0) return 0; - uint8_t silence[bytes]; - memset(silence, 0, bytes); - return write(idx, silence, bytes); - } - - /// Automatically increment mixing index after each write - void setAutoIndex(bool flag){ - is_auto_index = flag; - } - - /// Sets the Output Stream index - void setIndex(int idx){ - stream_idx = idx; - } - - /// Moves to the next mixing index - void next() { - stream_idx++; - } - - /// Define callback to allocate custum buffer types - void setCreateBufferCallback(BaseBuffer* (*cb)(int size) ){ - create_buffer_cb = cb; - } - - /// Provides the write buffer for the indicated index - BaseBuffer* getBuffer(int idx){ - return idx < output_count ? buffers[idx] : nullptr; - } - protected: - Vector *> buffers{0}; + Vector *> buffers{0}; Vector output{0}; Vector weights{0}; Print *p_final_output = nullptr; float total_weights = 0.0; bool is_active = false; int stream_idx = 0; - int size_bytes = 0; - int output_count = 0; + int size_bytes; + int output_count; + MemoryType memory_type; void *p_memory = nullptr; - bool is_auto_index = true; - BaseBuffer* (*create_buffer_cb)(int size) = create_buffer; - - static BaseBuffer* create_buffer(int size) { - return new RingBuffer(size / sizeof(T)); - } void update_total_weights() { total_weights = 0.0; @@ -533,13 +434,28 @@ class OutputMixer : public Print { } } - void allocate_buffers(int size) { + void allocate_buffers() { // allocate ringbuffers for each output for (int j = 0; j < output_count; j++) { if (buffers[j] != nullptr) { delete buffers[j]; } - buffers[j] = create_buffer(size); +#if defined(ESP32) && defined(ARDUINO) + if (memory_type == PS_RAM && ESP.getFreePsram() >= size_bytes) { + p_memory = ps_malloc(size_bytes); + LOGI("Buffer %d allocated %d bytes in PS_RAM", j, size_bytes); + } else { + p_memory = malloc(size_bytes); + LOGI("Buffer %d allocated %d bytes in RAM", j, size_bytes); + } + if (p_memory != nullptr) { + buffers[j] = new (p_memory) RingBuffer(size_bytes / sizeof(T)); + } else { + LOGE("Not enough memory to allocate %d bytes", size_bytes); + } +#else + buffers[j] = new RingBuffer(size_bytes / sizeof(T)); +#endif } } @@ -548,12 +464,118 @@ class OutputMixer : public Print { for (int j = 0; j < output_count; j++) { if (buffers[j] != nullptr) { delete buffers[j]; +#ifdef ESP32 + if (p_memory != nullptr) { + free(p_memory); + } +#endif buffers[j] = nullptr; } } } }; +/** + * @brief A simple class to determine the volume + * @ingroup io + * @author Phil Schatzmann + * @copyright GPLv3 + */ +class VolumeOutput : public AudioOutput { +public: + + void setAudioInfo(AudioInfo info) { + this->info = info; + if (info.channels > 0) { + volumes.resize(info.channels); + volumes_tmp.resize(info.channels); + } + } + + size_t write(const uint8_t *buffer, size_t size) { + clear(); + switch (info.bits_per_sample) { + case 16: + updateVolumes(buffer, size); + break; + case 24: + updateVolumes(buffer, size); + break; + case 32: + updateVolumes(buffer, size); + break; + default: + LOGE("Unsupported bits_per_sample: %d", info.bits_per_sample); + break; + } + return size; + } + + /// Determines the volume (max amplitude). The range depends on the + /// bits_per_sample. + float volume() { return f_volume; } + + /// Determines the volume for the indicated channel. You must call the begin + /// method to define the number of channels + float volume(int channel) { + if (volumes.size() == 0) { + LOGE("begin not called!"); + return 0.0f; + } + if (channel >= volumes.size()) { + LOGE("invalid channel %d", channel); + return 0.0f; + } + return volumes[channel]; + } + + /// Resets the actual volume + void clear() { + f_volume_tmp = 0; + for (int j = 0; j < info.channels; j++) { + volumes_tmp[j] = 0; + } + } + +protected: + AudioInfo info; + float f_volume_tmp = 0; + float f_volume = 0; + Vector volumes{0}; + Vector volumes_tmp{0}; + + template void updateVolumes(const uint8_t *buffer, size_t size) { + T *bufferT = (T *)buffer; + int samplesCount = size / sizeof(T); + for (int j = 0; j < samplesCount; j++) { + float tmp = abs(static_cast(bufferT[j])); + updateVolume(tmp, j); + } + commit(); + } + + void updateVolume(float tmp, int j) { + if (tmp > f_volume_tmp) { + f_volume_tmp = tmp; + } + if (volumes_tmp.size() > 0 && info.channels > 0) { + int ch = j % info.channels; + if (tmp>volumes_tmp[ch]){ + volumes_tmp[ch] = tmp; + } + } + } + + void commit() { + f_volume = f_volume_tmp; + for (int j = 0; j < info.channels; j++) { + volumes[j] = volumes_tmp[j]; + } + } +}; + +// legacy name +using VolumePrint = VolumeOutput; /** * @brief Writes to a preallocated memory @@ -565,24 +587,16 @@ class MemoryOutput : public AudioOutput { p_start = start; p_next = start; max_size = len; - is_active = true; if (p_next == nullptr) { LOGE("start must not be null"); } } - bool begin() override { - is_active = true; - p_next = p_start; - pos = 0; - return true; - } - - size_t write(const uint8_t *data, size_t len) override { + size_t write(const uint8_t *buffer, size_t len) override { if (p_next == nullptr) return 0; if (pos + len <= max_size) { - memcpy(p_next, data, len); + memcpy(p_next, buffer, len); pos += len; p_next += len; return len; @@ -603,75 +617,104 @@ class MemoryOutput : public AudioOutput { size_t max_size; }; +// legacy name +using MemoryPrint = MemoryOutput; /** - * @brief Simple functionality to extract mono streams from a multichannel (e.g. - * stereo) signal. + * @brief Switched Output class. Class which can be used to filter the output + * out based on a switch: If the switch is on the output is forwarded. If it is + * off it is just ignored. + * @ingroup transform + * @author Phil Schatzmann + * @copyright GPLv3 + */ +class OnOffOutput : public AudioOutput { +public: + OnOffOutput() = default; + OnOffOutput(Print &out) { setOutput(out); } + + /// Same as setActive(true) + bool begin() override { + setActive(true); + return true; + } + /// Same as setActive(false) + void end() override { setActive(false); } + + /// Redefines the final output + void setOutput(Print &out) { p_output = &out; } + + /// set the sitch to on or off + void setActive(bool on) { is_active = on; } + + /// Determines if the switch is on + bool isActive() { return is_active; } + + size_t write(const uint8_t *buffer, size_t len) override { + if (p_output == nullptr) + return 0; + size_t result = len; + if (is_active) { + len = p_output->write(buffer, len); + } + return len; + } + +protected: + Print *p_output = nullptr; +}; + +/** + * @brief Simple functionality to extract mono streams from a multichannel (e.g. stereo) + * signal. * @ingroup transform * @author Phil Schatzmann * @copyright GPLv3 * @tparam T */ +template class ChannelSplitOutput : public AudioOutput { public: ChannelSplitOutput() = default; - ChannelSplitOutput(Print &out, int channel) { addOutput(out, channel); } + ChannelSplitOutput(int channel, Print &out) { addOutput(channel, out); } - /// Define the channel to be sent to the specified output. 0: first (=left) - /// channel, 1: second (=right) channel - void addOutput(Print &out, int channel) { + /// Define the channel to be sent to the specified output. 0: first (=left) channel, 1: second (=right) channel + void addOutput(int channel, Print &out) { ChannelSelectionOutputDef def; def.channel = channel; def.p_out = &out; - out_channels.push_back(def); - } - - size_t write(const uint8_t *data, size_t len) override { - switch(cfg.bits_per_sample){ - case 16: - return writeT(data, len); - case 24: - return writeT(data, len); - case 32: - return writeT(data, len); - default: - return 0; - } + out_chanels.push_back(def); } -protected: - struct ChannelSelectionOutputDef { - Print *p_out = nullptr; - int channel; - }; - Vector out_channels; - - template - size_t writeT(const uint8_t *buffer, size_t size) { + virtual size_t write(const uint8_t *buffer, size_t size) override { int sample_count = size / sizeof(T); int result_size = sample_count / cfg.channels; T *data = (T *)buffer; T result[result_size]; - for (int ch = 0; ch < out_channels.size(); ch++) { - ChannelSelectionOutputDef &def = out_channels[ch]; + for (int ch = 0; ch < out_chanels.size(); ch++) { + ChannelSelectionOutputDef &def = out_chanels[ch]; // extract mono result int i = 0; for (int j = def.channel; j < sample_count; j += cfg.channels) { result[i++] = data[j]; } - // write mono result - size_t written = - def.p_out->write((uint8_t *)result, result_size * sizeof(T)); - if (written != result_size * sizeof(T)) { + // write mono result + size_t written = def.p_out->write((uint8_t *)result, result_size * sizeof(T)); + if (written!=result_size * sizeof(T)){ LOGW("Could not write all samples"); } } return size; } +protected: + struct ChannelSelectionOutputDef { + Print *p_out = nullptr; + int channel; + }; + Vector out_chanels; }; - } // namespace audio_tools \ No newline at end of file diff --git a/src/AudioTools/AudioPlayer.h b/src/AudioTools/AudioPlayer.h new file mode 100644 index 0000000000..25bde2323e --- /dev/null +++ b/src/AudioTools/AudioPlayer.h @@ -0,0 +1,526 @@ +#pragma once + +#include "AudioConfig.h" +#include "AudioBasic/Str.h" +#include "AudioBasic/Debouncer.h" +#include "AudioTools/AudioTypes.h" +#include "AudioTools/Buffers.h" +#include "AudioTools/BaseConverter.h" +#include "AudioTools/AudioLogger.h" +#include "AudioTools/AudioStreams.h" +#include "AudioTools/StreamCopy.h" +#include "AudioHttp/AudioHttp.h" +#include "AudioTools/AudioSource.h" +#include "AudioTools/Fade.h" +// support for legacy USE_SDFAT +#ifdef USE_SDFAT +#include "AudioLibs/AudioSourceSDFAT.h" +#endif + +/** + * @defgroup player Player + * @ingroup main + * @brief Audio Player + */ + + +namespace audio_tools { + + /** + * @brief Implements a simple audio player which supports the following commands: + * - begin + * - play + * - stop + * - next + * - set Volume + * @ingroup player + * @author Phil Schatzmann + * @copyright GPLv3 + */ + class AudioPlayer : public AudioInfoSupport { + + public: + + /// Default constructur + AudioPlayer() { + TRACED(); + } + + /** + * @brief Construct a new Audio Player object. The processing chain is + * AudioSource -> Stream -copy> EncodedAudioStream -> VolumeOutput -> Print + * + * @param source + * @param output + * @param decoder + */ + AudioPlayer(AudioSource& source, AudioOutput& output, AudioDecoder& decoder) { + TRACED(); + this->p_source = &source; + this->p_decoder = &decoder; + setOutput(output); + // notification for audio configuration + decoder.setNotifyAudioChange(*this); + } + + /** + * @brief Construct a new Audio Player object. The processing chain is + * AudioSource -> Stream -copy> EncodedAudioStream -> VolumeOutput -> Print + * + * @param source + * @param output + * @param decoder + * @param notify + */ + AudioPlayer(AudioSource& source, Print& output, AudioDecoder& decoder, AudioInfoSupport* notify = nullptr) { + TRACED(); + this->p_source = &source; + this->p_decoder = &decoder; + setOutput(output); + setNotify(notify); + } + + /** + * @brief Construct a new Audio Player object. The processing chain is + * AudioSource -> Stream -copy> EncodedAudioStream -> VolumeOutput -> Print + * + * @param source + * @param output + * @param decoder + */ + AudioPlayer(AudioSource& source, AudioStream& output, AudioDecoder& decoder) { + TRACED(); + this->p_source = &source; + this->p_decoder = &decoder; + setOutput(output); + // notification for audio configuration + decoder.setNotifyAudioChange(*this); + } + + AudioPlayer(AudioPlayer const&) = delete; + AudioPlayer& operator=(AudioPlayer const&) = delete; + + /// Default destructor + virtual ~AudioPlayer() { + if (p_out_decoding != nullptr) { + delete p_out_decoding; + } + } + + void setOutput(AudioOutput& output){ + if (p_decoder->isResultPCM()){ + this->fade.setTarget(output); + this->volume_out.setTarget(fade); + this->p_out_decoding = new EncodedAudioStream(&volume_out, p_decoder); + } else { + this->p_out_decoding = new EncodedAudioStream(&output, p_decoder); + } + this->p_final_print = &output; + } + + void setOutput(Print &output){ + if (p_decoder->isResultPCM()){ + this->fade.setTarget(output); + this->volume_out.setTarget(fade); + this->p_out_decoding = new EncodedAudioStream(&volume_out, p_decoder); + } else { + this->p_out_decoding = new EncodedAudioStream(&output, p_decoder); + } + } + + void setOutput(AudioStream& output){ + if (p_decoder->isResultPCM()){ + this->fade.setTarget(output); + this->volume_out.setTarget(fade); + this->p_out_decoding = new EncodedAudioStream(&volume_out, p_decoder); + } else { + this->p_out_decoding = new EncodedAudioStream(&output, p_decoder); + } + this->p_final_stream = &output; + } + + /// Defines the number of bytes used by the copier + virtual void setBufferSize(int size){ + copier.resize(size); + } + + /// (Re)Starts the playing of the music (from the beginning) + virtual bool begin(int index=0, bool isActive = true) { + TRACED(); + bool result = false; + // initilaize volume + if (current_volume==-1){ + setVolume(1.0); + } else { + setVolume(current_volume); + } + + // take definition from source + autonext = p_source->isAutoNext(); + + // initial audio info for fade from output when not defined yet + if (fade.audioInfo().channels==0){ + setupFade(); + } + + // start dependent objects + p_out_decoding->begin(); + p_source->begin(); + meta_out.begin(); + + if (index >= 0) { + p_input_stream = p_source->selectStream(index); + if (p_input_stream != nullptr) { + if (meta_active) { + copier.setCallbackOnWrite(decodeMetaData, this); + } + copier.begin(*p_out_decoding, *p_input_stream); + timeout = millis() + p_source->timeoutAutoNext(); + active = isActive; + result = true; + } else { + LOGW("-> begin: no data found"); + active = false; + result = false; + } + } else { + LOGW("-> begin: no stream selected"); + active = isActive; + result = false; + } + return result; + } + + virtual void end() { + TRACED(); + active = false; + p_out_decoding->end(); + meta_out.end(); + // remove any data in the decoder + if (p_decoder!=nullptr){ + LOGI("reset codec"); + p_decoder->end(); + p_decoder->begin(); + } + + } + + /// (Re)defines the audio source + void setAudioSource(AudioSource& source){ + this->p_source = &source; + } + + // /// (Re)defines the output + // void setOutput(Print& output){ + // this->volume_out.setTarget(output); + // } + + /// (Re)defines the decoder + void setDecoder(AudioDecoder& decoder){ + if (this->p_out_decoding!=nullptr){ + delete p_out_decoding; + } + this->p_decoder = &decoder; + this->p_out_decoding = new EncodedAudioStream(&volume_out, p_decoder); + } + + /// (Re)defines the notify + void setNotify(AudioInfoSupport* notify){ + this->p_final_notify = notify; + // notification for audio configuration + if (p_decoder!=nullptr){ + p_decoder->setNotifyAudioChange(*this); + } + } + + /// Updates the audio info in the related objects + virtual void setAudioInfo(AudioInfo info) override { + TRACED(); + LOGI("sample_rate: %d", info.sample_rate); + LOGI("bits_per_sample: %d", info.bits_per_sample); + LOGI("channels: %d", info.channels); + this->info = info; + // notifiy volume + volume_out.setAudioInfo(info); + fade.setAudioInfo(info); + // notifiy final ouput: e.g. i2s + if (p_final_print != nullptr) p_final_print->setAudioInfo(info); + if (p_final_stream != nullptr) p_final_stream->setAudioInfo(info); + if (p_final_notify != nullptr) p_final_notify->setAudioInfo(info); + }; + + virtual AudioInfo audioInfo() override { + return info; + } + + /// starts / resumes the playing of a matching song + virtual void play() { + TRACED(); + setActive(true); + } + + /// halts the playing + virtual void stop() { + TRACED(); + setActive(false); + } + + /// moves to next file or nth next file when indicating an offset. Negative values are supported to move back. + virtual bool next(int offset=1) { + TRACED(); + writeEnd(); + stream_increment = offset >= 0 ? 1 : -1; + active = setStream(p_source->nextStream(offset)); + return active; + } + + /// moves to selected file + virtual bool setIndex(int idx) { + TRACED(); + writeEnd(); + stream_increment = 1; + active = setStream(p_source->selectStream(idx)); + return active; + } + + /// moves to selected file + virtual bool setPath(const char* path) { + TRACED(); + writeEnd(); + stream_increment = 1; + active = setStream(p_source->selectStream(path)); + return active; + } + + /// moves to previous file + virtual bool previous(int offset=1) { + TRACED(); + writeEnd(); + stream_increment = -1; + active = setStream(p_source->previousStream(abs(offset))); + return active; + } + + /// start selected input stream + virtual bool setStream(Stream *input) { + end(); + p_out_decoding->begin(); + p_input_stream = input; + if (p_input_stream != nullptr) { + LOGD("open selected stream"); + meta_out.begin(); + copier.begin(*p_out_decoding, *p_input_stream); + } + return p_input_stream != nullptr; + } + + /// Provides the actual stream (=e.g.file) + virtual Stream* getStream(){ + return p_input_stream; + } + + /// determines if the player is active + virtual bool isActive() { + return active; + } + + /// determines if the player is active + operator bool() { + return isActive(); + } + + /// The same like start() / stop() + virtual void setActive(bool isActive){ + if (isActive){ + fade.setFadeInActive(true); + } else { + fade.setFadeOutActive(true); + copier.copy(); + writeSilence(2048); + } + active = isActive; + } + + /// sets the volume - values need to be between 0.0 and 1.0 + virtual void setVolume(float volume) { + if (volume >= 0.0f && volume <= 1.0f) { + if (abs(volume - current_volume) > 0.01f) { + LOGI("setVolume(%f)", volume); + volume_out.setVolume(volume); + current_volume = volume; + } + } else { + LOGE("setVolume value '%f' out of range (0.0 -1.0)", volume); + } + } + + /// Determines the actual volume + virtual float volume() { + return current_volume; + } + + /// Set automatically move to next file and end of current file: This is determined from the AudioSource. If you want to override it call this method after calling begin()! + virtual void setAutoNext(bool next) { + autonext = next; + } + + /// Defines the wait time in ms if the target output is full + virtual void setDelayIfOutputFull(int delayMs){ + delay_if_full = delayMs; + } + + /// Call this method in the loop. + virtual size_t copy() { + size_t result = 0; + if (active) { + TRACED(); + if (delay_if_full!=0 && p_final_print!=nullptr && p_final_print->availableForWrite()==0){ + // not ready to do anything - so we wait a bit + delay(delay_if_full); + return 0; + } + // handle sound + result = copier.copy(); + if (result>0 || timeout == 0) { + // reset timeout if we had any data + timeout = millis() + p_source->timeoutAutoNext(); + } + // move to next stream after timeout + moveToNextFileOnTimeout(); + } else { + // e.g. A2DP should still receive data to keep the connection open + if (silence_on_inactive){ + if (p_final_print!=nullptr){ + p_final_print->writeSilence(1024); + } else if (p_final_stream!=nullptr){ + p_final_stream->writeSilence(1024); + } + } + } + return result; + } + + /// Defines the medatadata callback + virtual void setMetadataCallback(void (*callback)(MetaDataType type, const char* str, int len), ID3TypeSelection sel=SELECT_ID3) { + TRACEI(); + // setup metadata. + if (p_source->setMetadataCallback(callback)){ + // metadata is handled by source + LOGI("Using ICY Metadata"); + meta_active = false; + } else { + // metadata is handled here + meta_out.setCallback(callback); + meta_out.setFilter(sel); + meta_active = true; + } + } + + /// Change the VolumeControl implementation + virtual void setVolumeControl(VolumeControl& vc) { + volume_out.setVolumeControl(vc); + } + + /// Provides access to the StreamCopy, so that we can register additinal callbacks + StreamCopy &getStreamCopy(){ + return copier; + } + + /// If set to true the player writes 0 values instead of no data if the player is inactive + void setSilenceOnInactive(bool active){ + silence_on_inactive = active; + } + + /// Checks if silence_on_inactive has been activated (default false) + bool isSilenceOnInactive(){ + return silence_on_inactive; + } + + void writeSilence(size_t bytes) { + TRACEI(); + if (p_final_print!=nullptr){ + p_final_print->writeSilence(bytes); + } else if (p_final_stream!=nullptr){ + p_final_stream->writeSilence(bytes); + } + } + + /// Provides the Print object to which we send the decoding result + Print* getVolumeOutput(){ + return &volume_out; + } + + protected: + bool active = false; + bool autonext = true; + bool silence_on_inactive = false; + AudioSource* p_source = nullptr; + VolumeStream volume_out; // Volume control + FadeStream fade; // Phase in / Phase Out to avoid popping noise + MetaDataID3 meta_out; // Metadata parser + EncodedAudioStream* p_out_decoding = nullptr; // Decoding stream + CopyDecoder no_decoder{true}; + AudioDecoder* p_decoder = &no_decoder; + Stream* p_input_stream = nullptr; + AudioOutput* p_final_print = nullptr; + AudioStream* p_final_stream = nullptr; + AudioInfoSupport* p_final_notify = nullptr; + StreamCopy copier; // copies sound into i2s + AudioInfo info; + bool meta_active = false; + uint32_t timeout = 0; + int stream_increment = 1; // +1 moves forward; -1 moves backward + float current_volume = -1.0; // illegal value which will trigger an update + int delay_if_full = 100; + + void setupFade() { + if (p_final_print != nullptr) { + fade.setAudioInfo(p_final_print->audioInfo()); + } else if (p_final_stream != nullptr){ + fade.setAudioInfo(p_final_stream->audioInfo()); + } + } + + + virtual void moveToNextFileOnTimeout(){ + if (!autonext) return; + if (p_final_stream==nullptr) return; + if (p_final_stream->availableForWrite()==0) return; + if (p_input_stream == nullptr || millis() > timeout) { + fade.setFadeInActive(true); + if (autonext) { + LOGI("-> timeout - moving by %d", stream_increment); + // open next stream + if (!next(stream_increment)) { + LOGD("stream is null"); + } + } else { + active = false; + } + timeout = millis() + p_source->timeoutAutoNext(); + } + } + + + void writeEnd() { + // end silently + TRACEI(); + fade.setFadeOutActive(true); + copier.copy(); + // start by fading in + fade.setFadeInActive(true); + // restart the decoder to make sure it does not contain any audio when we continue + p_decoder->begin(); + } + + /// Callback implementation which writes to metadata + static void decodeMetaData(void* obj, void* data, size_t len) { + LOGD("%s, %zu", LOG_METHOD, len); + AudioPlayer* p = (AudioPlayer*)obj; + if (p->meta_active) { + p->meta_out.write((const uint8_t*)data, len); + } + } + }; + +} diff --git a/src/AudioTools/CoreAudio/AudioRuntime.cpp b/src/AudioTools/AudioRuntime.cpp similarity index 91% rename from src/AudioTools/CoreAudio/AudioRuntime.cpp rename to src/AudioTools/AudioRuntime.cpp index 509d2e5d2f..ecdde70823 100644 --- a/src/AudioTools/CoreAudio/AudioRuntime.cpp +++ b/src/AudioTools/AudioRuntime.cpp @@ -8,7 +8,7 @@ * @copyright Copyright (c) 2022 * */ -#include "AudioToolsConfig.h" +#include "AudioConfig.h" #if defined(ARDUINO_ARCH_RP2040) && defined(FIX_SYNC_SYNCHRONIZE) extern "C" void __sync_synchronize(){ diff --git a/src/AudioTools/AudioRuntime.h b/src/AudioTools/AudioRuntime.h new file mode 100644 index 0000000000..2239dd281c --- /dev/null +++ b/src/AudioTools/AudioRuntime.h @@ -0,0 +1,30 @@ +#pragma once + +#include "AudioConfig.h" + +/** + * @brief Public generic methods + * @author Phil Schatzmann + * @copyright GPLv3 + */ + +/// stops any further processing by spinning in an endless loop @ingroup basic +inline void stop() { + #ifdef EXIT_ON_STOP + exit(0); + #else + while(true){ + delay(1000); + } + #endif +} + +/// Executes heap_caps_check_integrity_all() @ingroup basic +inline static void checkMemory(bool memoryCheck=false) { + #if defined(ESP32) && defined(ARDUINO) + assert(heap_caps_check_integrity_all(true)); + if (memoryCheck) printf("==> Available stack: %d - heap: %d\n", uxTaskGetStackHighWaterMark(NULL), ESP.getFreeHeap()); + #endif +} + + diff --git a/src/AudioTools/AudioLibs/SPDIFOutput.h b/src/AudioTools/AudioSPDIF.h similarity index 85% rename from src/AudioTools/AudioLibs/SPDIFOutput.h rename to src/AudioTools/AudioSPDIF.h index 1174450fde..4216a724cc 100644 --- a/src/AudioTools/AudioLibs/SPDIFOutput.h +++ b/src/AudioTools/AudioSPDIF.h @@ -17,11 +17,11 @@ #pragma once -#include "AudioToolsConfig.h" -#include "AudioTools/CoreAudio/AudioLogger.h" -#include "AudioTools/CoreAudio/AudioStreams.h" -#include "AudioTools/CoreAudio/AudioI2S/I2SConfig.h" -#include "AudioTools/CoreAudio/AudioI2S/I2SStream.h" +#include "AudioConfig.h" +#include "AudioTools/AudioLogger.h" +#include "AudioTools/AudioStreams.h" +#include "AudioI2S/I2SConfig.h" +#include "AudioI2S/I2SStream.h" // Default Data Pin #ifndef SPDIF_DATA_PIN @@ -93,7 +93,7 @@ static const uint16_t bmc_tab_uint[256] = { static const int16_t *bmc_tab = (int16_t *)bmc_tab_uint; static uint32_t spdif_buf[SPDIF_BUF_ARRAY_SIZE]; -static uint32_t *spdif_ptr = nullptr; +static uint32_t *spdif_ptr; /** * @brief SPDIF configuration @@ -108,17 +108,10 @@ struct SPDIFConfig : public AudioInfo { } int port_no = 0; // processor dependent port int pin_data = SPDIF_DATA_PIN; - int buffer_count = 30; - int buffer_size = 384; -#if defined(RP2040_HOWER) - int pin_bck = -1; - uint32_t sys_clock = 192000; -#endif }; /** - * @brief Output as 16 bit stereo SPDIF on the I2S data output pin. For the - * time beeing only the ESP32 is officially supported. + * @brief Output as 16 bit stereo SPDIF on the I2S data output pin * @ingroup io * @author Phil Schatzmann * @copyright GPLv3 @@ -132,8 +125,8 @@ class SPDIFOutput : public AudioStream { /// destructor virtual ~SPDIFOutput() { end(); } - /// Starting with last or default settings - bool begin() { return begin(cfg); } + /// Starting with default settings + bool begin() { return begin(defaultConfig()); } /// Start with the provided parameters bool begin(SPDIFConfig config) { @@ -160,45 +153,23 @@ class SPDIFOutput : public AudioStream { // Setup I2S int sample_rate = cfg.sample_rate * BMC_BITS_FACTOR; - if (sample_rate==0){ - TRACEE(); - return false; - } int bclk = sample_rate * I2S_BITS_PER_SAMPLE * I2S_CHANNELS; int mclk = (I2S_BUG_MAGIC / bclk) * bclk; // use mclk for avoiding I2S bug I2SConfig i2s_cfg; i2s_cfg.sample_rate = sample_rate; i2s_cfg.channels = cfg.channels; -#if defined(RP2040_HOWER) - if (cfg.sys_clock > 0) set_sys_clock_khz(cfg.sys_clock, false); - // RP2040 does not support -1 for no pin - if (cfg.pin_bck >= 0) { - i2s_cfg.pin_bck = cfg.pin_bck; - i2s_cfg.pin_ws = cfg.pin_bck + 1; - } else { - LOGE("pin_bck not defined"); - } -#elif defined(STM32) - // pins are fixed -#else - // default logic for ESP32 + i2s_cfg.i2s_format = I2S_STD_FORMAT; + i2s_cfg.bits_per_sample = I2S_BITS_PER_SAMPLE; i2s_cfg.pin_ws = -1; i2s_cfg.pin_bck = -1; i2s_cfg.pin_data = cfg.pin_data; -#endif - i2s_cfg.buffer_count = cfg.buffer_count; - i2s_cfg.buffer_size = cfg.buffer_size; - i2s_cfg.bits_per_sample = I2S_BITS_PER_SAMPLE; - i2s_cfg.i2s_format = I2S_STD_FORMAT; - i2s_cfg.rx_tx_mode = TX_MODE; #ifdef ESP32 i2s_cfg.use_apll = true; -# if ESP_IDF_VERSION < ESP_IDF_VERSION_VAL(5, 0 , 0) i2s_cfg.fixed_mclk = mclk; -# endif #endif - i2sOn = i2s.begin(i2s_cfg); + i2s.begin(i2s_cfg); + i2sOn = true; return i2sOn; } @@ -233,12 +204,12 @@ class SPDIFOutput : public AudioStream { } /// Writes the audio data as SPDIF to the defined output pin - size_t write(const uint8_t *data, size_t len) { + size_t write(const uint8_t *src, size_t size) { if (!i2sOn) return 0; - const uint8_t *p = data; + const uint8_t *p = src; size_t result = 0; - while (p < (uint8_t *)data + len) { + while (p < (uint8_t *)src + size) { // convert PCM 16bit data to BMC 32bit pulse pattern if (cfg.channels == 2) { *(spdif_ptr + 1) = diff --git a/src/AudioTools/AudioSource.h b/src/AudioTools/AudioSource.h new file mode 100644 index 0000000000..ff9b5dbb61 --- /dev/null +++ b/src/AudioTools/AudioSource.h @@ -0,0 +1,258 @@ +#pragma once + +namespace audio_tools { + +/** + * @brief Abstract Audio Data Source for the AudioPlayer which is used by the Audio Players + * @ingroup player + * @author Phil Schatzmann + * @copyright GPLv3 + * + */ +class AudioSource { +public: + /// Reset actual stream and move to root + virtual void begin() = 0; + + /// Returns next audio stream + virtual Stream* nextStream(int offset) = 0; + + /// Returns previous audio stream + virtual Stream* previousStream(int offset) { + return nextStream(-offset); + }; + + /// Returns audio stream at the indicated index (the index is zero based, so the first value is 0!) + virtual Stream* selectStream(int index) { + LOGE("Not Supported!"); + return nullptr; + } + + /// same as selectStream - I just prefer this name + virtual Stream* setIndex(int index) { + return selectStream(index); + } + + /// Returns audio stream by path + virtual Stream* selectStream(const char* path) = 0; + + /// Sets the timeout which is triggering to move to the next stream. - the default value is 500 ms + virtual void setTimeoutAutoNext(int millisec) { + timeout_auto_next_value = millisec; + } + + /// Provides the timeout which is triggering to move to the next stream. + virtual int timeoutAutoNext() { + return timeout_auto_next_value; + } + + // only the ICYStream supports this + virtual bool setMetadataCallback(void (*fn)(MetaDataType info, const char* str, int len), ID3TypeSelection sel=SELECT_ICY) { + return false; + } + + /// Sets the timeout of Stream in milliseconds + virtual void setTimeout(int millisec) {}; + + /// Returns default setting go to the next + virtual bool isAutoNext() {return true; } + + +protected: + int timeout_auto_next_value = 500; +}; + +/** + * @brief Callback Audio Data Source which is used by the Audio Players + * @ingroup player + * @author Phil Schatzmann + * @copyright GPLv3 + */ +class AudioSourceCallback : public AudioSource { +public: + AudioSourceCallback() { + } + + AudioSourceCallback(Stream* (*nextStreamCallback)(int offset), void (*onStartCallback)() = nullptr) { + TRACED(); + this->onStartCallback = onStartCallback; + this->nextStreamCallback = nextStreamCallback; + } + + /// Reset actual stream and move to root + virtual void begin() override { + TRACED(); + if (onStartCallback != nullptr) onStartCallback(); + }; + + /// Returns next (with positive index) or previous stream (with negative index) + virtual Stream* nextStream(int offset) override { + TRACED(); + return nextStreamCallback == nullptr ? nullptr : nextStreamCallback(offset); + } + + /// Returns selected audio stream + virtual Stream* selectStream(int index) override { + LOGI("selectStream: %d", index); + if (indexStreamCallback==nullptr){ + LOGI("setCallbackSelectStream not provided"); + if (index>0) { + begin(); + return nextStream(index); + } else { + // nextStream(0) will return the directory but we need a file + return nextStream(1); + } + } + return indexStreamCallback(index); + } + /// Returns audio stream by path + virtual Stream* selectStream(const char* path) override { + this->path = path; + return indexStreamCallback == nullptr ? nullptr : indexStreamCallback(-1); + }; + + void setCallbackOnStart(void (*callback)()) { + onStartCallback = callback; + } + + void setCallbackNextStream(Stream* (*callback)(int offset)) { + nextStreamCallback = callback; + } + + void setCallbackSelectStream(Stream* (*callback)(int idx)) { + indexStreamCallback = callback; + } + + virtual bool isAutoNext() override { + return auto_next; + } + + virtual void setAutoNext(bool a){ + auto_next = a; + } + + // returns the requested path: relevant when provided idx in callback is -1 + virtual const char* getPath() { + return path; + } + +protected: + void (*onStartCallback)() = nullptr; + bool auto_next = true; + Stream* (*nextStreamCallback)(int offset) = nullptr; + Stream* (*indexStreamCallback)(int index) = nullptr; + const char*path=nullptr; +}; + +#if defined(USE_URL_ARDUINO) && ( defined(ESP32) || defined(ESP8266) ) + +/** + * @brief Audio Source which provides the data via the network from an URL + * @ingroup player + * @author Phil Schatzmann + * @copyright GPLv3 + */ +class AudioSourceURL : public AudioSource { +public: + template + AudioSourceURL(AbstractURLStream& urlStream, T(&urlArray)[N], const char* mime, int startPos = 0) { + TRACED(); + this->actual_stream = &urlStream; + this->mime = mime; + this->urlArray = urlArray; + this->max = N; + this->pos = startPos - 1; + this->timeout_auto_next_value = 20000; + } + + /// Setup Wifi URL + virtual void begin() override { + TRACED(); + this->pos = 0; + } + + /// Opens the selected url from the array + Stream* selectStream(int idx) override { + pos = idx; + if (pos < 0) { + pos = 0; + LOGI("url array out of limits: %d -> %d", idx, pos); + } + if (pos >= max) { + pos = max-1; + LOGI("url array out of limits: %d -> %d", idx, pos); + } + LOGI("selectStream: %d/%d -> %s", pos, max-1, urlArray[pos]); + if (started) actual_stream->end(); + actual_stream->begin(urlArray[pos], mime); + started = true; + return actual_stream; + } + + /// Opens the next url from the array + Stream* nextStream(int offset) override { + pos += offset; + if (pos < 0 || pos >= max) { + pos = 0; + } + LOGI("nextStream: %d/%d -> %s", pos, max-1, urlArray[pos]); + return selectStream(pos); + } + + /// Opens the Previous url from the array + Stream* previousStream(int offset) override { + pos -= offset; + if (pos < 0 || pos >= max) { + pos = max-1; + } + LOGI("previousStream: %d/%d -> %s", pos, max-1, urlArray[pos]); + return selectStream(pos); + } + + /// Opens the selected url + Stream* selectStream(const char* path) override { + LOGI("selectStream: %s", path); + if (started) actual_stream->end(); + actual_stream->begin(path, mime); + started = true; + return actual_stream; + } + + int index() { + return pos; + } + + const char *toStr() { + return urlArray[pos]; + } + + /// Sets the timeout of the URL Stream in milliseconds + void setTimeout(int millisec){ + actual_stream->setTimeout(millisec); + } + + // provides go not to the next on error + virtual bool isAutoNext() { + return true; + }; + + // only the ICYStream supports this + bool setMetadataCallback(void (*fn)(MetaDataType info, const char* str, int len), ID3TypeSelection sel=SELECT_ICY) { + TRACEI(); + return actual_stream->setMetadataCallback(fn); + } + + +protected: + AbstractURLStream* actual_stream = nullptr; + const char** urlArray; + int pos = 0; + int max = 0; + const char* mime = nullptr; + bool started = false; +}; + +#endif + +} \ No newline at end of file diff --git a/src/AudioTools/AudioStreams.h b/src/AudioTools/AudioStreams.h new file mode 100644 index 0000000000..33c061d811 --- /dev/null +++ b/src/AudioTools/AudioStreams.h @@ -0,0 +1,1881 @@ +#pragma once +#include "AudioConfig.h" +#include "AudioTimer/AudioTimer.h" +#include "AudioTools/AudioTypes.h" +#include "AudioTools/Buffers.h" +#include "AudioTools/AudioLogger.h" +#include "AudioEffects/SoundGenerator.h" + +#ifndef URL_CLIENT_TIMEOUT +# define URL_CLIENT_TIMEOUT 60000 +#endif + +#ifndef URL_HANDSHAKE_TIMEOUT +# define URL_HANDSHAKE_TIMEOUT 120000 +#endif + +#ifndef IRAM_ATTR +# define IRAM_ATTR +#endif + +#ifdef USE_STREAM_WRITE_OVERRIDE +# define STREAM_WRITE_OVERRIDE override +#else +# define STREAM_WRITE_OVERRIDE +#endif + +#ifdef USE_STREAM_READ_OVERRIDE +# define STREAM_READ_OVERRIDE override +#else +# define STREAM_READ_OVERRIDE +#endif + +#ifdef USE_STREAM_READCHAR_OVERRIDE +# define STREAM_READCHAR_OVERRIDE override +#else +# define STREAM_READCHAR_OVERRIDE +#endif + +namespace audio_tools { + +/** + * @brief Base class for all Audio Streams. It support the boolean operator to + * test if the object is ready with data + * @author Phil Schatzmann + * @copyright GPLv3 + */ +class AudioStream : public Stream, public AudioInfoSupport, public AudioInfoSource { + public: + AudioStream() = default; + virtual ~AudioStream() = default; + AudioStream(AudioStream const&) = delete; + AudioStream& operator=(AudioStream const&) = delete; + + virtual bool begin(){return true;} + virtual void end(){} + + // Call from subclass or overwrite to do something useful + virtual void setAudioInfo(AudioInfo info) override { + TRACED(); + this->info = info; + info.logInfo(); + if (p_notify!=nullptr){ + p_notify->setAudioInfo(info); + } + } + + virtual void setNotifyAudioChange(AudioInfoSupport &bi) override { + p_notify = &bi; + } + + virtual size_t readBytes(uint8_t *buffer, size_t length) STREAM_READ_OVERRIDE { return not_supported(0, "readBytes"); } + + virtual size_t write(const uint8_t *buffer, size_t size) override{ return not_supported(0,"write"); } + + virtual size_t write(uint8_t ch) override { + tmp_out.resize(MAX_SINGLE_CHARS); + if (tmp_out.isFull()){ + flush(); + } + return tmp_out.write(ch); + } + + virtual int available() override { return DEFAULT_BUFFER_SIZE; }; + + + operator bool() { return available() > 0; } + + virtual AudioInfo audioInfo() override { + return info; + } + + virtual int availableForWrite() override { return DEFAULT_BUFFER_SIZE; } + + virtual void flush() override { + if (tmp_out.available()>0){ + write((const uint8_t*)tmp_out.address(), tmp_out.available()); + } + } + + /// Writes len bytes of silence (=0). + virtual void writeSilence(size_t len){ + int16_t zero = 0; + for (int j=0;j tmp_in{0}; + RingBuffer tmp_out{0}; + + + virtual int not_supported(int out, const char* msg="") { + LOGE("AudioStream: %s unsupported operation!", msg); + // trigger stacktrace + //assert(false); + return out; + } + + void refillReadBuffer() { + tmp_in.resize(MAX_SINGLE_CHARS); + if (tmp_in.isEmpty()){ + TRACED(); + const int len = tmp_in.size(); + uint8_t bytes[len]; + int len_eff = readBytes(bytes, len); + //LOGD("tmp_in available: %d / size: %d / to be written %d", tmp_in.available(), tmp_in.size(), len_eff); + tmp_in.writeArray(bytes,len_eff); + } + } + +}; + +/** + * @brief To be used to support implementations where the readBytes is not + * virtual + * + */ +class AudioStreamWrapper : public AudioStream { + public: + AudioStreamWrapper(Stream& s) { + TRACED(); + p_stream = &s; + p_stream->setTimeout(clientTimeout); + } + + virtual bool begin(){return true;} + virtual void end(){} + + virtual size_t readBytes(uint8_t *buffer, size_t length) { + //Serial.print("Timeout audiostream: "); + //Serial.println(p_stream->getTimeout()); + return p_stream->readBytes(buffer, length); + } + + int read() { return p_stream->read(); } + + int peek() { return p_stream->peek(); } + + int available() { return p_stream->available(); } + + virtual size_t write(uint8_t c) { return p_stream->write(c); } + + virtual size_t write(const uint8_t *buffer, size_t size) { + return p_stream->write(buffer, size); + } + + virtual int availableForWrite() { return p_stream->availableForWrite(); } + + virtual void flush() { p_stream->flush(); } + + protected: + Stream *p_stream; + int32_t clientTimeout = URL_CLIENT_TIMEOUT; // 60000; +}; + + +/** + * @brief A simple Stream implementation which is backed by allocated memory + * @ingroup io + * @author Phil Schatzmann + * @copyright GPLv3 + * + */ +class MemoryStream : public AudioStream { + public: + MemoryStream(int buffer_size = 512, MemoryType memoryType = RAM) { + LOGD("MemoryStream: %d", buffer_size); + this->buffer_size = buffer_size; + this->memory_type = memoryType; + resize(buffer_size); + } + + MemoryStream(const uint8_t *buffer, int buffer_size, MemoryType memoryType = FLASH_RAM) { + LOGD("MemoryStream: %d", buffer_size); + setValue(buffer, buffer_size, memoryType); + } + + ~MemoryStream() { + TRACED(); + if (memoryCanChange() && buffer!=nullptr) free(buffer); + } + + // resets the read pointer + bool begin() override { + TRACED(); + write_pos = memoryCanChange() ? 0 : buffer_size; + if (this->buffer==nullptr && memoryCanChange()){ + resize(buffer_size); + } + read_pos = 0; + return true; + } + + virtual size_t write(uint8_t byte) override { + if (buffer==nullptr) return 0; + int result = 0; + if (write_pos < buffer_size) { + result = 1; + buffer[write_pos] = byte; + write_pos++; + } + return result; + } + + virtual size_t write(const uint8_t *buffer, size_t size) override { + size_t result = 0; + for (size_t j = 0; j < size; j++) { + if (!write(buffer[j])) { + break; + } + result = j; + } + return result; + } + + virtual int available() override { + if (buffer==nullptr) return 0; + int result = write_pos - read_pos; + if (result<=0 && is_loop){ + // rewind to start + read_pos = 0; + result = write_pos - read_pos; + // call callback + if (rewind!=nullptr) rewind(); + } + return result; + } + + virtual int availableForWrite() override { + return buffer_size - write_pos; + } + + virtual int read() override { + int result = peek(); + if (result >= 0) { + read_pos++; + } + return result; + } + + virtual size_t readBytes(uint8_t *buffer, size_t length) override { + size_t count = 0; + while (count < length) { + int c = read(); + if (c < 0) break; + *buffer++ = (char)c; + count++; + } + return count; + } + + virtual int peek() override { + int result = -1; + if (available() > 0) { + result = buffer[read_pos]; + } + return result; + } + + virtual void flush() override {} + + virtual void end() override { + read_pos = 0; + } + + /// clears the audio data: sets all values to 0 + virtual void clear(bool reset = false) { + if (memoryCanChange()){ + write_pos = 0; + read_pos = 0; + if (buffer==nullptr){ + resize(buffer_size); + } + if (reset) { + // we clear the buffer data + memset(buffer, 0, buffer_size); + } + } else { + read_pos = 0; + LOGW("data is read only"); + } + } + + /// Automatically rewinds to the beginning when reaching the end + virtual void setLoop(bool loop){ + is_loop = loop; + } + + virtual void resize(size_t size){ + if (memoryCanChange()){ + buffer_size = size; + switch(memory_type){ + #if defined(ESP32) && defined(ARDUINO) + case PS_RAM: + buffer = (buffer==nullptr) ? (uint8_t*)ps_calloc(size,1) : (uint8_t*)ps_realloc(buffer, size); + assert(buffer!=nullptr); + break; + #endif + default: + buffer = (buffer==nullptr) ? (uint8_t*)calloc(size,1) : (uint8_t*)realloc(buffer, size); + assert(buffer!=nullptr); + break; + } + } + } + + virtual uint8_t* data(){ + return buffer; + } + + /// Callback which is executed when we rewind (in loop mode) to the beginning + void setRewindCallback(void (*cb)()){ + this->rewind = cb; + } + + /// Update the values (buffer and size) + void setValue(const uint8_t *buffer, int buffer_size, MemoryType memoryType = FLASH_RAM) { + this->buffer_size = buffer_size; + this->read_pos = 0; + this->write_pos = buffer_size; + this->buffer = (uint8_t *)buffer; + this->memory_type = memoryType; + } + + protected: + int write_pos = 0; + int read_pos = 0; + int buffer_size = 0; + uint8_t *buffer = nullptr; + MemoryType memory_type = RAM; + bool is_loop = false; + void (*rewind)() = nullptr; + + bool memoryCanChange() { + return memory_type!=FLASH_RAM; + } +}; + +/** + * @brief MemoryStream which is written and read using the internal RAM. For each write the data is allocated + * on the heap. + * @ingroup io + * @author Phil Schatzmann + * @copyright GPLv3 + */ +class DynamicMemoryStream : public AudioStream { +public: + struct DataNode { + size_t len=0; + uint8_t* data=nullptr; + + DataNode() = default; + /// Constructor + DataNode(void*inData, int len){ + this->len = len; + this->data = (uint8_t*) malloc(len); + assert(this->data!=nullptr); + memcpy(this->data, inData, len); + } + + ~DataNode(){ + if (data!=nullptr) { + free(data); + data = nullptr; + } + } + }; + + DynamicMemoryStream() = default; + + DynamicMemoryStream(bool isLoop, int defaultBufferSize=DEFAULT_BUFFER_SIZE ) { + this->default_buffer_size = defaultBufferSize; + is_loop = isLoop; + } + // Assign values from ref, clearing the original ref + void assign(DynamicMemoryStream &ref){ + audio_list.swap(ref.audio_list); + it = ref.it; + total_available=ref.total_available; + default_buffer_size = ref.default_buffer_size; + alloc_failed = ref.alloc_failed;; + is_loop = ref.is_loop; + ref.clear(); + } + + /// Intializes the processing + virtual bool begin() override { + clear(); + temp_audio.resize(default_buffer_size); + return true; + } + + virtual void end() override { + clear(); + } + + /// Automatically rewinds to the beginning when reaching the end + virtual void setLoop(bool loop){ + is_loop = loop; + } + + void clear() { + DataNode *p_node; + bool ok; + do{ + ok = audio_list.pop_front(p_node); + if (ok){ + delete p_node; + } + } while (ok); + + temp_audio.reset(); + total_available = 0; + alloc_failed = false; + rewind(); + } + + size_t size(){ + return total_available; + } + + /// Sets the read position to the beginning + void rewind() { + it = audio_list.begin(); + } + + virtual size_t write(const uint8_t *buffer, size_t size) override { + DataNode *p_node = new DataNode((void*)buffer, size); + if (p_node->data!=nullptr){ + alloc_failed = false; + total_available += size; + audio_list.push_back(p_node); + + // setup interator to point to first record + if (it == audio_list.end()){ + it = audio_list.begin(); + } + + return size; + } + alloc_failed = true; + return 0; + } + + virtual int availableForWrite() override { + return alloc_failed ? 0 : default_buffer_size; + } + + virtual int available() override { + if (it == audio_list.end()){ + if (is_loop) rewind(); + if (it == audio_list.end()) { + return 0; + } + } + return (*it)->len; + } + + virtual size_t readBytes(uint8_t *buffer, size_t length) override { + // provide unprocessed data + if (temp_audio.available()>0){ + return temp_audio.readArray(buffer, length); + } + + // We have no more data + if (it==audio_list.end()){ + if (is_loop){ + rewind(); + } else { + // stop the processing + return 0; + } + } + + // provide data from next node + DataNode *p_node = *it; + int result_len = min(length, (size_t) p_node->len); + memcpy(buffer, p_node->data, result_len); + // save unprocessed data to temp buffer + if (p_node->len>length){ + uint8_t *start = p_node->data+result_len; + int uprocessed_len = p_node->len - length; + temp_audio.writeArray(start, uprocessed_len); + } + //move to next pos + ++it; + return result_len; + } + + List &list() { + return audio_list; + } + + /// @brief Post processing after the recording. We add a smooth transition at the beginning and at the end + /// @tparam T + /// @param factor + template + void postProcessSmoothTransition(int channels, float factor = 0.01, int remove=0){ + if (remove>0){ + for (int j=0;j clean_start(channels, true, false, factor); + auto first = *list().begin(); + if (first!=nullptr){ + clean_start.convert(first->data,first->len); + } + + SmoothTransition clean_end(channels, false, true, factor); + auto last = * (--(list().end())); + if (last!=nullptr){ + clean_end.convert(last->data,last->len); + } + } + + +protected: + List audio_list; + List::Iterator it = audio_list.end(); + size_t total_available=0; + int default_buffer_size=DEFAULT_BUFFER_SIZE; + bool alloc_failed = false; + RingBuffer temp_audio{DEFAULT_BUFFER_SIZE}; + bool is_loop = false; + +}; + +/** + * @brief Source for reading generated tones. Please note + * - that the output is for one channel only! + * - we do not support reading of individual characters! + * - we do not support any write operations + * @ingroup io + * @param generator + * @author Phil Schatzmann + * @copyright GPLv3 + */ + +template +class GeneratedSoundStream : public AudioStream { + public: + GeneratedSoundStream() = default; + + GeneratedSoundStream(SoundGenerator &generator) { + TRACED(); + setInput(generator); + } + + void setInput(SoundGenerator &generator){ + this->generator_ptr = &generator; + } + + AudioInfo defaultConfig() { return this->generator_ptr->defaultConfig(); } + + /// start the processing + bool begin() override { + TRACED(); + if (generator_ptr==nullptr){ + LOGE("%s",source_not_defined_error); + return false; + } + generator_ptr->begin(); + if (audioBaseInfoDependent != nullptr) + audioBaseInfoDependent->setAudioInfo(generator_ptr->audioInfo()); + active = true; + return active; + } + + /// start the processing + bool begin(AudioInfo cfg) { + TRACED(); + if (generator_ptr==nullptr){ + LOGE("%s",source_not_defined_error); + return false; + } + generator_ptr->begin(cfg); + if (audioBaseInfoDependent != nullptr) + audioBaseInfoDependent->setAudioInfo(generator_ptr->audioInfo()); + active = true; + return active; + } + + /// stop the processing + void end() override { + TRACED(); + generator_ptr->end(); + active = false; + } + + virtual void setNotifyAudioChange(AudioInfoSupport &bi) override { + audioBaseInfoDependent = &bi; + } + + AudioInfo audioInfo() override { + return generator_ptr->audioInfo(); + } + + /// This is unbounded so we just return the buffer size + virtual int available() override { return DEFAULT_BUFFER_SIZE; } + + /// privide the data as byte stream + size_t readBytes(uint8_t *buffer, size_t length) override { + LOGD("GeneratedSoundStream::readBytes: %u", (unsigned int)length); + return generator_ptr->readBytes(buffer, length); + } + + bool isActive() {return active && generator_ptr->isActive();} + + operator bool() { return isActive(); } + + void flush() override {} + + protected: + bool active = false; + SoundGenerator *generator_ptr; + AudioInfoSupport *audioBaseInfoDependent = nullptr; + const char* source_not_defined_error = "Source not defined"; + +}; + +/** + * @brief The Arduino Stream supports operations on single characters. This is + * usually not the best way to push audio information, but we will support it + * anyway - by using a buffer. On reads: if the buffer is empty it gets refilled + * - for writes if it is full it gets flushed. + * @author Phil Schatzmann + * @copyright GPLv3 + */ +class BufferedStream : public AudioStream { + public: + BufferedStream(size_t buffer_size) { + TRACED(); + buffer = new SingleBuffer(buffer_size); + } + + ~BufferedStream() { + TRACED(); + if (buffer != nullptr) { + delete buffer; + } + } + + /// writes a byte to the buffer + virtual size_t write(uint8_t c) override { + if (buffer->isFull()) { + flush(); + } + return buffer->write(c); + } + + /// Use this method: write an array + virtual size_t write(const uint8_t *data, size_t len) override { + LOGD("%s: %zu", LOG_METHOD, len); + flush(); + return writeExt(data, len); + } + + /// empties the buffer + virtual void flush() override { + // just dump the memory of the buffer and clear it + if (buffer->available() > 0) { + writeExt(buffer->address(), buffer->available()); + buffer->reset(); + } + } + + /// reads a byte - to be avoided + virtual int read() override { + if (buffer->isEmpty()) { + refill(); + } + return buffer->read(); + } + + /// peeks a byte - to be avoided + virtual int peek() override{ + if (buffer->isEmpty()) { + refill(); + } + return buffer->peek(); + }; + + /// Use this method !! + size_t readBytes(uint8_t *data, size_t length) override { + if (buffer->isEmpty()) { + return readExt(data, length); + } else { + return buffer->readArray(data, length); + } + } + + /// Returns the available bytes in the buffer: to be avoided + virtual int available() override { + if (buffer->isEmpty()) { + refill(); + } + return buffer->available(); + } + + /// Clears all the data in the buffer + void clear() { buffer->reset(); } + + protected: + SingleBuffer *buffer = nullptr; + + // refills the buffer with data from i2s + void refill() { + size_t result = readExt(buffer->address(), buffer->size()); + buffer->setAvailable(result); + } + + virtual size_t writeExt(const uint8_t *data, size_t len) = 0; + virtual size_t readExt(uint8_t *data, size_t length) = 0; +}; + +/** + * @brief The Arduino Stream which provides silence and simulates a null device + * when used as audio target or audio source + * @ingroup io + * @author Phil Schatzmann + * @copyright GPLv3 + */ +class NullStream : public AudioStream { + public: + + bool begin(AudioInfo info) { + this->info = info; + return true; + } + + AudioInfo defaultConfig() { + AudioInfo info; + return info; + } + + size_t write(const uint8_t *buffer, size_t len) override{ + return len; + } + + size_t readBytes(uint8_t *buffer, size_t len) override{ + memset(buffer,0, len); + return len; + } + + /// Define object which need to be notified if the basinfo is changing + void setNotifyAudioChange(AudioInfoSupport &bi) override {} + + void setAudioInfo(AudioInfo info) override { + this->info = info; + } +}; + +/** + * @brief A Stream backed by a Ringbuffer. We can write to the end and read from + * the beginning of the stream + * @ingroup io + * @author Phil Schatzmann + * @copyright GPLv3 + */ +class RingBufferStream : public AudioStream { + public: + RingBufferStream(int size = DEFAULT_BUFFER_SIZE) { + resize(size); + } + + virtual int available() override { + // LOGD("RingBufferStream::available: %zu",buffer->available()); + return buffer.available(); + } + + virtual int availableForWrite() override { + return buffer.availableForWrite(); + } + + virtual void flush() override {} + virtual int peek() override { return buffer.peek(); } + virtual int read() override { return buffer.read(); } + + virtual size_t readBytes(uint8_t *data, size_t length) override { + return buffer.readArray(data, length); + } + + virtual size_t write(const uint8_t *data, size_t len) override { + // LOGD("RingBufferStream::write: %zu",len); + return buffer.writeArray(data, len); + } + + virtual size_t write(uint8_t c) override { return buffer.write(c); } + + void resize(int size){ + buffer.resize(size); + } + + size_t size() { + return buffer.size(); + } + + + protected: + RingBuffer buffer{0}; +}; + + +/** + * @brief AudioStream class which stores the data in a temporary queue buffer. + * The queue can be consumed e.g. by a callback function by calling readBytes(); + * @ingroup io + * @author Phil Schatzmann + * @copyright GPLv3 + */ +template +class QueueStream : public AudioStream { + public: + /// Default constructor + QueueStream(int bufferSize, int bufferCount, bool autoRemoveOldestDataIfFull=false) + : AudioStream() { + owns_buffer = true; + callback_buffer_ptr = new NBuffer(bufferSize, bufferCount); + remove_oldest_data = autoRemoveOldestDataIfFull; + } + /// Create stream from any BaseBuffer subclass + QueueStream(BaseBuffer &buffer){ + owns_buffer = false; + callback_buffer_ptr = &buffer; + } + + virtual ~QueueStream() { + if(owns_buffer) { + delete callback_buffer_ptr; + } + } + + /// Activates the output + virtual bool begin() override { + TRACEI(); + active = true; + return true; + } + + /// stops the processing + virtual void end() override { + TRACEI(); + active = false; + }; + + int available() override { + return callback_buffer_ptr->available()*sizeof(T); + } + + int availableForWrite() override { + return callback_buffer_ptr->availableForWrite()*sizeof(T); + } + + virtual size_t write(const uint8_t *data, size_t len) override { + if (!active) return 0; + + // make space by deleting oldest entries + if (remove_oldest_data){ + int available_bytes = callback_buffer_ptr->availableForWrite()*sizeof(T); + if ((int)len>available_bytes){ + int gap = len-available_bytes; + uint8_t tmp[gap]; + readBytes(tmp, gap); + } + } + + return callback_buffer_ptr->writeArray(data, len / sizeof(T)); + } + + virtual size_t readBytes(uint8_t *data, size_t len) override { + if (!active) return 0; + return callback_buffer_ptr->readArray(data, len / sizeof(T)); + } + + /// Clears the data in the buffer + void clear() { + if (active){ + callback_buffer_ptr->reset(); + } + } + + /// Returns true if active + operator bool(){ + return active; + } + + protected: + BaseBuffer *callback_buffer_ptr; + bool active; + bool remove_oldest_data; + bool owns_buffer; + +}; + +// support legacy name +template +using CallbackBufferedStream = QueueStream; + +/** + * @brief Both the data of the read or write + * operations will be converted with the help of the indicated converter. + * @ingroup transform + * @tparam T + * @param out + * @param converter + */ +template +class ConverterStream : public AudioStream { + + public: + ConverterStream(Stream &stream, ConverterT &converter) : AudioStream() { + p_converter = &converter; + p_stream = &stream; + } + + virtual int availableForWrite() { return p_stream->availableForWrite(); } + + virtual size_t write(const uint8_t *buffer, size_t size) { + size_t result = p_converter->convert((uint8_t *)buffer, size); + if (result>0) { + size_t result_written = p_stream->write(buffer, result); + return size * result_written / result; + } + return 0; + } + + size_t readBytes(uint8_t *data, size_t length) override { + size_t result; p_stream->readBytes(data, length); + return p_converter->convert(data, result); + } + + + /// Returns the available bytes in the buffer: to be avoided + virtual int available() override { + return p_stream->available(); + } + + protected: + Stream *p_stream; + ConverterT *p_converter; + +}; + +/** + * @brief Class which measures the truput + * @author Phil Schatzmann + * @copyright GPLv3 + * @ingroup io + */ +class MeasuringStream : public AudioStream { + public: + MeasuringStream(int count=10, Print *logOut=nullptr){ + this->count = count; + this->max_count = count; + p_stream = &null; + p_print = &null; + start_time = millis(); + p_logout = logOut; + } + + MeasuringStream(Print &print, int count=10, Print *logOut=nullptr){ + this->count = count; + this->max_count = count; + p_print =&print; + start_time = millis(); + p_logout = logOut; + } + + MeasuringStream(Stream &stream, int count=10, Print *logOut=nullptr){ + this->count = count; + this->max_count = count; + p_stream =&stream; + p_print = &stream; + start_time = millis(); + p_logout = logOut; + } + + /// Provides the data from all streams mixed together + size_t readBytes(uint8_t* data, size_t len) override { + return measure(p_stream->readBytes(data, len)); + } + + int available() override { + return p_stream->available(); + } + + /// Writes raw PCM audio data, which will be the input for the volume control + virtual size_t write(const uint8_t *buffer, size_t size) override { + return measure(p_print->write(buffer, size)); + } + + /// Provides the nubmer of bytes we can write + virtual int availableForWrite() override { + return p_print->availableForWrite(); + } + + /// Returns the actual thrughput in bytes per second + int bytesPerSecond() { + return bytes_per_second; + } + + /// Provides the time when the last measurement was started + uint64_t startTime() { + return start_time; + } + + void setBytesPerSample(int size){ + sample_div = size; + } + + protected: + int max_count=0; + int count=0; + Stream *p_stream=nullptr; + Print *p_print=nullptr; + uint64_t start_time; + int total_bytes = 0; + int bytes_per_second = 0; + int sample_div = 0; + NullStream null; + Print *p_logout=nullptr; + + size_t measure(size_t len) { + count--; + total_bytes+=len; + + if (count<0){ + uint64_t end_time = millis(); + int time_diff = end_time - start_time; // in ms + if (time_diff>0){ + bytes_per_second = total_bytes / time_diff * 1000; + printResult(); + count = max_count; + total_bytes = 0; + start_time = end_time; + } + } + return len; + } + + void printResult() { + char msg[70]; + if (sample_div==0){ + sprintf(msg, "==> Bytes per second: %d", bytes_per_second); + } else { + sprintf(msg, "==> Samples per second: %d", bytes_per_second/sample_div); + } + if (p_logout!=nullptr){ + p_logout->println(msg); + } else { + LOGI("%s",msg); + } + } +}; + +/** + * @brief Configuration for ProgressStream + * @author Phil Schatzmann + * @copyright GPLv3 + * + */ +class ProgressStreamInfo : public AudioInfo { + public: + size_t total_size = 0; +}; +/** + * @brief Generic calss to measure the the total bytes which were processed in order to + * calculate the progress as a percentage of the total size. + * @author Phil Schatzmann + * @copyright GPLv3 + * @ingroup io + */ +class ProgressStream : public AudioStream { + public: + ProgressStream() = default; + + ProgressStream(Print &print){ + setPrint(print); + } + + ProgressStream(Stream &stream){ + setStream(stream); + } + + ProgressStream(AudioStream &stream){ + setStream(stream); + p_info_from = &stream; + } + + ProgressStreamInfo& defaultConfig() { + return progress_info; + } + + void setAudioInfo(AudioInfo info) override { + AudioStream::setAudioInfo(info); + progress_info.copyFrom(info); + } + + void setStream(Stream &stream){ + p_stream =&stream; + p_print = &stream; + } + + void setStream(Print &print){ + p_print =&print; + } + + void setPrint(Print &print){ + p_print =&print; + } + + bool begin() override { + if (p_info_from!=nullptr){ + setAudioInfo(p_info_from->audioInfo()); + } + return AudioStream::begin(); + } + + /// Updates the total size and restarts the percent calculation: Same as calling setSize() + bool begin(size_t len){ + setSize(len); + return begin(); + } + + bool begin(ProgressStreamInfo info){ + progress_info = info; + setAudioInfo(info); + return begin(); + } + + /// Updates the total size and restarts the percent calculation + void setSize(size_t len){ + total_processed = 0; + progress_info.total_size = len; + } + + /// Provides the current total size (defined by setSize) + size_t size(){ + return progress_info.total_size; + } + + /// Provides the number of processed bytes + size_t processedBytes() { + return total_processed; + } + + /// Provides the number of processed seconds + size_t processedSecs() { + return total_processed / byteRate(); + } + + /// Provides the total_size provided in the configuration + size_t totalBytes() { + return progress_info.total_size; + } + + /// Converts the totalBytes() to seconds + size_t totalSecs() { + return totalBytes() / byteRate(); + } + + /// Provides the processed percentage: If no size has been defined we return 0 + float percentage() { + if (progress_info.total_size==0) return 0; + return 100.0 * total_processed / progress_info.total_size; + } + + /// Provides the data from all streams mixed together + size_t readBytes(uint8_t* data, size_t len) override { + if (p_stream==nullptr) return 0; + return measure(p_stream->readBytes(data, len)); + } + + int available() override { + if (p_stream==nullptr) return 0; + return p_stream->available(); + } + + /// Writes raw PCM audio data, which will be the input for the volume control + virtual size_t write(const uint8_t *buffer, size_t size) override { + if (p_print==nullptr) return 0; + return measure(p_print->write(buffer, size)); + } + + /// Provides the nubmer of bytes we can write + virtual int availableForWrite() override { + if (p_print==nullptr) return 0; + return p_print->availableForWrite(); + } + + protected: + ProgressStreamInfo progress_info; + Stream *p_stream=nullptr; + Print *p_print=nullptr; + AudioInfoSupport *p_info_from=nullptr; + size_t total_processed = 0; + + size_t measure(size_t len) { + total_processed += len; + return len; + } + + size_t byteRate() { + AudioInfo info = audioInfo(); + int byte_rate = info.sample_rate * info.bits_per_sample * info.channels / 8; + if (byte_rate==0){ + LOGE("Audio Info not defined"); + return 0; + } + return byte_rate; + } + +}; + + + +/** + * @brief MixerStream is mixing the input from Multiple Input Streams. + * All streams must have the same audo format (sample rate, channels, bits per sample). + * @ingroup transform + * @author Phil Schatzmann + * @copyright GPLv3 + */ + +template +class InputMixer : public AudioStream { + public: + InputMixer() = default; + + /// Adds a new input stream + void add(Stream &in, int weight=100){ + streams.push_back(&in); + weights.push_back(weight); + total_weights += weight; + } + + virtual bool begin(AudioInfo info) { + setAudioInfo(info); + frame_size = info.bits_per_sample/8 * info.channels; + LOGI("frame_size: %d",frame_size); + return frame_size>0; + } + + /// Dynamically update the new weight for the indicated channel: If you set it to 0 it is muted (and the stream is not read any more). We recommend to use values between 1 and 100 + void setWeight(int channel, int weight){ + if (channelavailable(); + // if (result>0) + // break; + // } + // return result; + // } + + protected: + Vector streams{10}; + Vector weights{10}; + int total_weights = 0; + int frame_size = 4; + + Vector result_vect; + Vector current_vect; + + /// mixing using a vector of samples + void readBytesVector(T* p_data, int byteCount) { + int samples = byteCount / sizeof(T); + result_vect.resize(samples); + current_vect.resize(samples); + int stream_count = size(); + resultClear(); + for (int j=0;j0){ + readSamples(streams[j],current_vect.data(), samples); + float fact = static_cast(weights[j]) / total_weights; + resultAdd(fact); + } + } + // copy result + for (int j=0;j(streams[i]); + // sample_total += weights[i] * sample / total_weights ; + // } + // p_data[j] = sample_total; + // } + // } + +}; + +/** + * @brief Merges multiple input channels. The input must be mono! + * So if you provide 2 mono channels you get a stereo signal as result + * with the left channel from channel 0 and the right from channel 1 + * @ingroup transform + * @author Phil Schatzmann + * @copyright GPLv3 + * @tparam T + */ +template +class InputMerge : public AudioStream { + public: + /// Default constructor + InputMerge() = default; + + /// @brief Constructor for stereo signal from to mono input stream + /// @param left + /// @param right + InputMerge(Stream &left, Stream &right) { + add(left); + add(right); + }; + + virtual bool begin(AudioInfo info) { + if (size()!=info.channels){ + info.channels = size(); + LOGW("channels corrected to %d", size()); + } + setAudioInfo(info); + return true; + } + + /// Provides the data from all streams mixed together + size_t readBytes(uint8_t* data, size_t len) override { + LOGD("readBytes: %d",(int)len); + T *p_data = (T*) data; + int result_len = MIN(available(), len/size()); + int sample_count = result_len / sizeof(T); + int size_value = size(); + int result_idx = 0; + for (int j=0;j(streams[i]); + } + } + return result_idx*sizeof(T); + } + + /// Adds a new input stream + void add(Stream &in, float weight=1.0){ + streams.push_back(&in); + weights.push_back(weight); + } + + /// Defines a new weight for the indicated channel: If you set it to 0 it is muted. + void setWeight(int channel, float weight){ + if (channelavailable(); + for (int j=1;javailable(); + if (tmp streams{10}; + Vector weights{10}; +}; + + +/** + * @brief CallbackStream: A Stream that allows to register callback methods for accessing and providing data + * @ingroup io + * @author Phil Schatzmann + * @copyright GPLv3 + */ +class CallbackStream : public AudioStream { + public: + CallbackStream() = default; + + CallbackStream(size_t (*cb_read)(uint8_t* data, size_t len), size_t (*cb_write)(const uint8_t* data, size_t len)) { + setWriteCallback(cb_write); + setReadCallback(cb_read); + } + + void setWriteCallback(size_t (*cb_write)(const uint8_t* data, size_t len)){ + this->cb_write = cb_write; + } + + void setReadCallback(size_t (*cb_read)(uint8_t* data, size_t len)){ + this->cb_read = cb_read; + } + + virtual bool begin(AudioInfo info) { + setAudioInfo(info); + return begin(); + } + + virtual bool begin() override { + active = true; + return true; + } + void end() override { active = false;} + + size_t readBytes(uint8_t* data, size_t len) override { + if (!active) return 0; + if (cb_read){ + return cb_read(data, len); + } + return 0; + } + + size_t write(const uint8_t* data, size_t len) override { + if (!active) return 0; + if (cb_write){ + return cb_write(data, len); + } + return 0; + } + + protected: + bool active=true; + size_t (*cb_write)(const uint8_t* data, size_t len) = nullptr; + size_t (*cb_read)(uint8_t* data, size_t len) = nullptr; +}; + +/** + * @brief Stream to which we can apply Filters for each channel. The filter + * might change the result size! + * @ingroup transform + * @author Phil Schatzmann + * @copyright GPLv3 + */ +template +class FilteredStream : public AudioStream { + public: + FilteredStream(Stream &stream) : AudioStream() { + p_stream = &stream; + } + FilteredStream(Stream &stream, int channels) : AudioStream() { + this->channels = channels; + p_stream = &stream; + p_converter = new ConverterNChannels(channels); + } + + bool begin(AudioInfo info){ + setAudioInfo(info); + this->channels = info.channels; + if (p_converter !=nullptr && info.channels!=channels){ + LOGE("Inconsistent number of channels"); + return false; + } + return begin(); + } + + bool begin() override { + if (channels ==0){ + LOGE("channels must not be 0"); + return false; + } + if (p_converter==nullptr){ + p_converter = new ConverterNChannels(channels); + } + return AudioStream::begin(); + } + + + virtual size_t write(const uint8_t *buffer, size_t size) override { + if (p_converter==nullptr) return 0; + size_t result = p_converter->convert((uint8_t *)buffer, size); + return p_stream->write(buffer, result); + } + + size_t readBytes(uint8_t *data, size_t length) override { + if (p_converter==nullptr) return 0; + size_t result = p_stream->readBytes(data, length); + result = p_converter->convert(data, result); + return result; + } + + virtual int available() override { + return p_stream->available(); + } + + virtual int availableForWrite() override { + return p_stream->availableForWrite(); + } + + /// defines the filter for an individual channel - the first channel is 0. The number of channels must have + /// been defined before we can call this function. + void setFilter(int channel, Filter *filter) { + if (p_converter!=nullptr){ + p_converter->setFilter(channel, filter); + } else { + LOGE("p_converter is null"); + } + } + + protected: + int channels=0; + Stream *p_stream; + ConverterNChannels *p_converter; + +}; + +#ifdef USE_TIMER +/** + * @brief TimerCallbackAudioStream Configuration + * @author Phil Schatzmann + * @copyright GPLv3 + */ +struct TimerCallbackAudioStreamInfo : public AudioInfo { + RxTxMode rx_tx_mode = RX_MODE; + uint16_t buffer_size = DEFAULT_BUFFER_SIZE; + bool use_timer = true; + int timer_id = -1; + TimerFunction timer_function = DirectTimerCallback; + bool adapt_sample_rate = false; + uint16_t (*callback)(uint8_t *data, uint16_t len) = nullptr; +}; + +// forward declaration: relevant only if use_timer == true +// void IRAM_ATTR timerCallback(void* obj); + +/** + * @brief Callback driven Audio Source (rx_tx_mode==RX_MODE) or Audio Sink + * (rx_tx_mode==TX_MODE). This class allows to to integrate external libraries + * in order to consume or generate a data stream which is based on a timer + * @ingroup io + * @author Phil Schatzmann + * @copyright GPLv3 + * + */ +class TimerCallbackAudioStream : public BufferedStream { + friend void IRAM_ATTR timerCallback(void *obj); + + public: + TimerCallbackAudioStream() : BufferedStream(80) { TRACED(); } + + ~TimerCallbackAudioStream() { + TRACED(); + if (timer != nullptr) delete timer; + if (buffer != nullptr) delete buffer; + if (frame != nullptr) delete[] frame; + } + + /// Provides the default configuration + TimerCallbackAudioStreamInfo defaultConfig() { + TimerCallbackAudioStreamInfo def; + return def; + } + + /// updates the audio information + virtual void setAudioInfo(AudioInfo info) { + TRACED(); + if (cfg.sample_rate != info.sample_rate || cfg.channels != info.channels || + cfg.bits_per_sample != info.bits_per_sample) { + bool do_restart = active; + if (do_restart) end(); + cfg.sample_rate = info.sample_rate; + cfg.channels = info.channels; + cfg.bits_per_sample = info.bits_per_sample; + if (do_restart) begin(cfg); + } + } + + /// Defines the target that needs to be notified + void setNotifyAudioChange(AudioInfoSupport &bi) { notifyTarget = &bi; } + + /// Provides the current audio information + TimerCallbackAudioStreamInfo audioInfoExt() { return cfg; } + AudioInfo audioInfo() { return cfg; } + + void begin(TimerCallbackAudioStreamInfo config) { + LOGD("%s: %s", LOG_METHOD, + config.rx_tx_mode == RX_MODE ? "RX_MODE" : "TX_MODE"); + this->cfg = config; + this->frameCallback = config.callback; + if (cfg.use_timer) { + frameSize = cfg.bits_per_sample * cfg.channels / 8; + frame = new uint8_t[frameSize]; + buffer = new RingBuffer(cfg.buffer_size); + timer = new TimerAlarmRepeating(); + timer->setTimerFunction(cfg.timer_function); + if (cfg.timer_id>=0){ + timer->setTimer(cfg.timer_id); + } + time = AudioTime::toTimeUs(cfg.sample_rate); + LOGI("sample_rate: %u -> time: %u milliseconds", (unsigned int)cfg.sample_rate, (unsigned int)time); + timer->setCallbackParameter(this); + timer->begin(timerCallback, time, TimeUnit::US); + } + + notifyAudioChange(); + active = true; + } + + /// Restart the processing + bool begin() { + TRACED(); + if (this->frameCallback != nullptr) { + if (cfg.use_timer) { + timer->begin(timerCallback, time, TimeUnit::US); + } + active = true; + } + return active; + } + + /// Stops the processing + void end() { + TRACED(); + if (cfg.use_timer) { + timer->end(); + } + active = false; + } + + /// Provides the effective sample rate + uint16_t currentSampleRate() { return currentRateValue; } + + protected: + TimerCallbackAudioStreamInfo cfg; + AudioInfoSupport *notifyTarget = nullptr; + bool active = false; + uint16_t (*frameCallback)(uint8_t *data, uint16_t len); + // below only relevant with timer + TimerAlarmRepeating *timer = nullptr; + RingBuffer *buffer = nullptr; + uint8_t *frame = nullptr; + uint16_t frameSize = 0; + uint32_t time = 0; + unsigned long lastTimestamp = 0u; + uint32_t currentRateValue = 0; + uint32_t printCount = 0; + + // used for audio sink + virtual size_t writeExt(const uint8_t *data, size_t len) override { + if (!active) return 0; + TRACED(); + size_t result = 0; + if (!cfg.use_timer) { + result = frameCallback((uint8_t *)data, len); + } else { + result = buffer->writeArray((uint8_t *)data, len); + } + if (++printCount % 10000 == 0) printSampleRate(); + return result; + } + + // used for audio source + virtual size_t readExt(uint8_t *data, size_t len) override { + if (!active) return 0; + TRACED(); + + size_t result = 0; + if (!cfg.use_timer) { + result = frameCallback(data, len); + } else { + result = buffer->readArray(data, len); + } + if (++printCount % 10000 == 0) printSampleRate(); + return result; + } + + /// calculates the effective sample rate + virtual void measureSampleRate() { + unsigned long ms = millis(); + if (lastTimestamp > 0u) { + uint32_t diff = ms - lastTimestamp; + if (diff > 0) { + uint16_t rate = 1 * 1000 / diff; + + if (currentRateValue == 0) { + currentRateValue = rate; + } else { + currentRateValue = (currentRateValue + rate) / 2; + } + } + } + lastTimestamp = ms; + } + + /// log and update effective sample rate + virtual void printSampleRate() { + LOGI("effective sample rate: %u", (unsigned int)currentRateValue); + if (cfg.adapt_sample_rate && + abs((int)currentRateValue - cfg.sample_rate) > 200) { + cfg.sample_rate = currentRateValue; + notifyAudioChange(); + } + } + + /// Update Audio Information in target device + virtual void notifyAudioChange() { + if (notifyTarget != nullptr) { + notifyTarget->setAudioInfo(cfg); + } + } + + inline static void IRAM_ATTR timerCallback(void *obj); +}; + +// relevant only if use_timer == true +inline void TimerCallbackAudioStream::timerCallback(void *obj) { + TimerCallbackAudioStream *src = (TimerCallbackAudioStream *)obj; + if (src != nullptr) { + // LOGD("%s: %s", LOG_METHOD, src->cfg.rx_tx_mode==RX_MODE ? + // "RX_MODE":"TX_MODE"); + if (src->cfg.rx_tx_mode == RX_MODE) { + // input + uint16_t available_bytes = src->frameCallback(src->frame, src->frameSize); + uint16_t buffer_available = src->buffer->availableForWrite(); + if (buffer_available < available_bytes) { + // if buffer is full make space + uint16_t to_clear = available_bytes - buffer_available; + uint8_t tmp[to_clear]; + src->buffer->readArray(tmp, to_clear); + } + if (src->buffer->writeArray(src->frame, available_bytes) != + available_bytes) { + assert(false); + } + } else { + // output + if (src->buffer != nullptr && src->frame != nullptr && + src->frameSize > 0) { + uint16_t available_bytes = + src->buffer->readArray(src->frame, src->frameSize); + if (available_bytes != + src->frameCallback(src->frame, available_bytes)) { + LOGE("data underflow"); + } + } + } + src->measureSampleRate(); + } +} + +#endif + +} // namespace audio_tools diff --git a/src/AudioTools/CoreAudio/AudioStreamsConverter.h b/src/AudioTools/AudioStreamsConverter.h similarity index 51% rename from src/AudioTools/CoreAudio/AudioStreamsConverter.h rename to src/AudioTools/AudioStreamsConverter.h index ea80931d90..fe0e677330 100644 --- a/src/AudioTools/CoreAudio/AudioStreamsConverter.h +++ b/src/AudioTools/AudioStreamsConverter.h @@ -1,7 +1,6 @@ #pragma once -#include "AudioIO.h" -#include "AudioTools/CoreAudio/AudioStreams.h" -#include "AudioTools/CoreAudio/ResampleStream.h" +#include "AudioTools/AudioStreams.h" +#include "AudioTools/ResampleStream.h" namespace audio_tools { @@ -15,14 +14,13 @@ template class ChannelFormatConverterStreamT : public ReformatBaseStream { public: ChannelFormatConverterStreamT(Stream &stream) { setStream(stream); } - ChannelFormatConverterStreamT(Print &print) { setOutput(print); } + ChannelFormatConverterStreamT(Print &print) { setStream(print); } ChannelFormatConverterStreamT(ChannelFormatConverterStreamT const &) = delete; - ChannelFormatConverterStreamT &operator=( - ChannelFormatConverterStreamT const &) = delete; + ChannelFormatConverterStreamT &operator=(ChannelFormatConverterStreamT const &) = delete; + bool begin(int fromChannels, int toChannels) { LOGI("begin %d -> %d channels", fromChannels, toChannels); - // is_output_notify = false; from_channels = fromChannels; to_channels = toChannels; factor = static_cast(toChannels) / static_cast(fromChannels); @@ -33,48 +31,35 @@ class ChannelFormatConverterStreamT : public ReformatBaseStream { return true; } - bool begin() override { return begin(from_channels, to_channels); } - - void setToChannels(uint16_t channels) { to_channels = channels; } - - virtual size_t write(const uint8_t *data, size_t len) override { + virtual size_t write(const uint8_t *data, size_t size) override { TRACED(); - if (p_print == nullptr) return 0; - // addNotifyOnFirstWrite(); if (from_channels == to_channels) { - return p_print->write(data, len); + return p_print->write(data, size); } - size_t resultBytes = convert(data, len); - // assert(resultBytes == factor * len); + size_t resultBytes = convert(data, size); + assert(resultBytes = factor * size); p_print->write((uint8_t *)buffer.data(), resultBytes); - return len; + return size; } - size_t readBytes(uint8_t *data, size_t len) override { + size_t readBytes(uint8_t *data, size_t size) override { TRACED(); if (p_stream == nullptr) return 0; if (from_channels == to_channels) { - return p_stream->readBytes(data, len); + return p_stream->readBytes(data, size); } - size_t in_bytes = 1.0f / factor * len; + size_t in_bytes = 1.0f / factor * size; bufferTmp.resize(in_bytes); p_stream->readBytes(bufferTmp.data(), in_bytes); size_t resultBytes = convert(bufferTmp.data(), in_bytes); - assert(len == resultBytes); - memcpy(data, (uint8_t *)buffer.data(), len); - return len; + assert(size == resultBytes); + memcpy(data, (uint8_t *)buffer.data(), size); + return size; } void setAudioInfo(AudioInfo cfg) override { - from_channels = cfg.channels; AudioStream::setAudioInfo(cfg); - } - - /// Returns the AudioInfo with the to_channels - AudioInfo audioInfoOut() override { - AudioInfo out = audioInfo(); - out.channels = to_channels; - return out; + to_channels = cfg.channels; } virtual int available() override { @@ -82,14 +67,9 @@ class ChannelFormatConverterStreamT : public ReformatBaseStream { } virtual int availableForWrite() override { - if (p_print == nullptr) return 0; return 1.0f / factor * p_print->availableForWrite(); } - float getByteFactor() override { - return static_cast(to_channels) / static_cast(from_channels); - } - protected: int from_channels = 2; int to_channels = 2; @@ -122,16 +102,12 @@ class ChannelFormatConverterStream : public ReformatBaseStream { public: ChannelFormatConverterStream() = default; ChannelFormatConverterStream(Stream &stream) { setStream(stream); } - ChannelFormatConverterStream(Print &print) { setOutput(print); } + ChannelFormatConverterStream(Print &print) { setStream(print); } ChannelFormatConverterStream(ChannelFormatConverterStream const &) = delete; - virtual ~ChannelFormatConverterStream() { end(); } ChannelFormatConverterStream &operator=( ChannelFormatConverterStream const &) = delete; void setAudioInfo(AudioInfo cfg) override { - TRACED(); - from_channels = cfg.channels; - LOGI("--> ChannelFormatConverterStream"); AudioStream::setAudioInfo(cfg); switch (bits_per_sample) { case 8: @@ -149,34 +125,10 @@ class ChannelFormatConverterStream : public ReformatBaseStream { } } - /// Returns the AudioInfo with the to_channels - AudioInfo audioInfoOut() override { - AudioInfo out = audioInfo(); - out.channels = to_channels; - return out; - } - - bool begin(AudioInfo from, AudioInfo to) { - if (from.sample_rate != to.sample_rate) { - LOGE("invalid sample_rate: %d", (int)to.sample_rate); - return false; - } - if (from.bits_per_sample != to.bits_per_sample) { - LOGE("invalid bits_per_sample: %d", (int)to.bits_per_sample); - return false; - } - return begin(from, to.channels); - } - bool begin(AudioInfo cfg, int toChannels) { - assert(toChannels != 0); - // is_output_notify = false; - to_channels = toChannels; - from_channels = cfg.channels; - bits_per_sample = cfg.bits_per_sample; - LOGI("--> ChannelFormatConverterStream"); AudioStream::setAudioInfo(cfg); LOGI("begin %d -> %d channels", cfg.channels, toChannels); + bits_per_sample = cfg.bits_per_sample; bool result = setupConverter(cfg.channels, toChannels); if (!result) { TRACEE() @@ -184,41 +136,33 @@ class ChannelFormatConverterStream : public ReformatBaseStream { return result; } - bool begin() override { return begin(audioInfo(), to_channels); } - - void end() override { cleanupConverter(); } - - void setToChannels(uint16_t channels) { to_channels = channels; } - - virtual size_t write(const uint8_t *data, size_t len) override { - LOGD("ChannelFormatConverterStream::write: %d", (int)len); - if (p_print == nullptr) return 0; - // addNotifyOnFirstWrite(); + virtual size_t write(const uint8_t *data, size_t size) override { + LOGD("ChannelFormatConverterStream::write: %d", (int)size); switch (bits_per_sample) { case 8: - return getConverter()->write(data, len); + return getConverter()->write(data, size); case 16: - return getConverter()->write(data, len); + return getConverter()->write(data, size); case 24: - return getConverter()->write(data, len); + return getConverter()->write(data, size); case 32: - return getConverter()->write(data, len); + return getConverter()->write(data, size); default: return 0; } } - size_t readBytes(uint8_t *data, size_t len) override { - LOGD("ChannelFormatConverterStream::readBytes: %d", (int)len); + size_t readBytes(uint8_t *data, size_t size) override { + LOGD("ChannelFormatConverterStream::readBytes: %d", (int)size); switch (bits_per_sample) { case 8: - return getConverter()->readBytes(data, len); + return getConverter()->readBytes(data, size); case 16: - return getConverter()->readBytes(data, len); + return getConverter()->readBytes(data, size); case 24: - return getConverter()->readBytes(data, len); + return getConverter()->readBytes(data, size); case 32: - return getConverter()->readBytes(data, len); + return getConverter()->readBytes(data, size); default: return 0; } @@ -254,126 +198,92 @@ class ChannelFormatConverterStream : public ReformatBaseStream { } } - float getByteFactor() override { - return static_cast(to_channels) / static_cast(from_channels); - } - protected: - void *converter = nullptr; + void *converter; int bits_per_sample = 0; - int to_channels; - int from_channels; template ChannelFormatConverterStreamT *getConverter() { return (ChannelFormatConverterStreamT *)converter; } - void cleanupConverter() { - if (converter == nullptr) return; - switch (bits_per_sample) { - case 8: - delete getConverter(); - converter = nullptr; - break; - case 16: - delete getConverter(); - converter = nullptr; - break; - case 24: - delete getConverter(); - converter = nullptr; - break; - case 32: - delete getConverter(); - converter = nullptr; - break; - } - } - bool setupConverter(int fromChannels, int toChannels) { bool result = true; - cleanupConverter(); - switch (bits_per_sample) { - case 8: - converter = new ChannelFormatConverterStreamT(*p_stream); - getConverter()->begin(fromChannels, toChannels); - break; - case 16: - converter = new ChannelFormatConverterStreamT(*p_stream); - getConverter()->begin(fromChannels, toChannels); - break; - case 24: - converter = new ChannelFormatConverterStreamT(*p_stream); - getConverter()->begin(fromChannels, toChannels); - break; - case 32: - converter = new ChannelFormatConverterStreamT(*p_stream); - getConverter()->begin(fromChannels, toChannels); - break; - default: - result = false; + if (p_stream != nullptr) { + switch (bits_per_sample) { + case 8: + converter = new ChannelFormatConverterStreamT(*p_stream); + getConverter()->begin(fromChannels, toChannels); + break; + case 16: + converter = new ChannelFormatConverterStreamT(*p_stream); + getConverter()->begin(fromChannels, toChannels); + break; + case 24: + converter = new ChannelFormatConverterStreamT(*p_stream); + getConverter()->begin(fromChannels, toChannels); + break; + case 32: + converter = new ChannelFormatConverterStreamT(*p_stream); + getConverter()->begin(fromChannels, toChannels); + break; + default: + result = false; + } + } else { + switch (bits_per_sample) { + case 8: + converter = new ChannelFormatConverterStreamT(*p_print); + getConverter()->begin(fromChannels, toChannels); + break; + case 16: + converter = new ChannelFormatConverterStreamT(*p_print); + getConverter()->begin(fromChannels, toChannels); + break; + case 24: + converter = new ChannelFormatConverterStreamT(*p_print); + getConverter()->begin(fromChannels, toChannels); + break; + case 32: + converter = new ChannelFormatConverterStreamT(*p_print); + getConverter()->begin(fromChannels, toChannels); + break; + default: + result = false; + } } return result; } }; /** - * @brief A more generic templated Converter which converts from a source type - * to a target type: You can use e.g. uint8_t, int8_t, int16_t, uint16_t, - * int24_t, uint32_t, int32_t, FloatAudio.AbstractMetaDat. This is quite handy - * because unsigned values and floating values are supported and you do not need - * to resort to use a Codec. + * @brief Converter which converts from source bits_per_sample to target + * bits_per_sample * @ingroup transform * @author Phil Schatzmann * @copyright GPLv3 - * @tparam TFrom specifies the source data type - * @tparam TTo spesifies the target data type. + * @tparam T specifies the current data type for the result of the read or + * write. + * @tparam TArg is the data type of the Stream or Print Object that is passed in + * the Constructor */ template class NumberFormatConverterStreamT : public ReformatBaseStream { public: NumberFormatConverterStreamT() = default; - NumberFormatConverterStreamT(float gain) { setGain(gain); } NumberFormatConverterStreamT(Stream &stream) { setStream(stream); } - NumberFormatConverterStreamT(AudioStream &stream) { setStream(stream); } - NumberFormatConverterStreamT(Print &print) { setOutput(print); } - NumberFormatConverterStreamT(AudioOutput &print) { setOutput(print); } - - void setAudioInfo(AudioInfo newInfo) override { - TRACED(); - if (newInfo.bits_per_sample == sizeof(TFrom) * 8) { - LOGE("Invalid bits_per_sample %d", newInfo.bits_per_sample); - } - ReformatBaseStream::setAudioInfo(newInfo); - } - - AudioInfo audioInfoOut() override { - AudioInfo to_format; - to_format.copyFrom(info); - to_format.bits_per_sample = sizeof(TTo) * 8; - return to_format; - } + NumberFormatConverterStreamT(Print &print) { setStream(print); } bool begin() override { - LOGI("begin %d -> %d bits", (int)sizeof(TFrom), (int)sizeof(TTo)); - // is_output_notify = false; + LOGI("begin %d -> %d bits", (int) sizeof(TFrom),(int) sizeof(TTo)); return true; } - virtual size_t write(const uint8_t *data, size_t len) override { + virtual size_t write(const uint8_t *data, size_t size) override { TRACED(); - if (p_print == nullptr) return 0; - // addNotifyOnFirstWrite(); - -#ifdef USE_TYPETRAITS - if (std::is_same::value) return p_print->write(data, len); -#else - if (sizeof(TFrom) == sizeof(TTo)) return p_print->write(data, len); -#endif - - size_t samples = len / sizeof(TFrom); + if (sizeof(TFrom) == sizeof(TTo)) return p_print->write(data, size); + size_t samples = size / sizeof(TFrom); size_t result_size = 0; TFrom *data_source = (TFrom *)data; @@ -383,21 +293,22 @@ class NumberFormatConverterStreamT : public ReformatBaseStream { result_size += p_print->write((uint8_t *)&value, sizeof(TTo)); } } else { - int size_bytes = sizeof(TTo) * samples; - buffer.resize(size_bytes); - NumberConverter::convertArray( - data_source, (TTo *)buffer.data(), samples, gain); - p_print->write((uint8_t *)buffer.address(), size_bytes); + buffer.resize(sizeof(TTo) * samples); + for (size_t j = 0; j < samples; j++) { + TTo value = NumberConverter::convert(data_source[j]); + result_size += buffer.writeArray((uint8_t *)&value, sizeof(TTo)); + } + p_print->write((uint8_t *)buffer.address(), result_size); buffer.reset(); } - return len; + return size; } - size_t readBytes(uint8_t *data, size_t len) override { - LOGD("NumberFormatConverterStreamT::readBytes: %d", (int)len); + size_t readBytes(uint8_t *data, size_t size) override { + LOGD("NumberFormatConverterStreamT::readBytes: %d", (int)size); if (p_stream == nullptr) return 0; - size_t samples = len / sizeof(TTo); + size_t samples = size / sizeof(TTo); TTo *data_target = (TTo *)data; TFrom source; if (!is_buffered) { @@ -410,11 +321,11 @@ class NumberFormatConverterStreamT : public ReformatBaseStream { buffer.resize(sizeof(TFrom) * samples); readSamples(p_stream, (TFrom *)buffer.address(), samples); TFrom *data = (TFrom *)buffer.address(); - NumberConverter::convertArray(data, data_target, samples, - gain); - buffer.reset(); + for (size_t j = 0; j < samples; j++) { + data_target[j] = NumberConverter::convert(data[j]); + } } - return len; + return size; } virtual int available() override { @@ -422,24 +333,16 @@ class NumberFormatConverterStreamT : public ReformatBaseStream { } virtual int availableForWrite() override { - return p_print == nullptr ? 0 : p_print->availableForWrite(); + return p_print->availableForWrite(); } /// if set to true we do one big write, else we get a lot of single writes per /// sample void setBuffered(bool flag) { is_buffered = flag; } - /// Defines the gain (only available when buffered is true) - void setGain(float value) { gain = value; } - - float getByteFactor() override { - return static_cast(sizeof(TTo)) / static_cast(sizeof(TFrom)); - } - protected: SingleBuffer buffer{0}; - bool is_buffered = true; - float gain = 1.0f; + bool is_buffered = false; }; /** @@ -454,60 +357,21 @@ class NumberFormatConverterStream : public ReformatBaseStream { public: NumberFormatConverterStream() = default; NumberFormatConverterStream(Stream &stream) { setStream(stream); } - NumberFormatConverterStream(AudioStream &stream) { setStream(stream); } - NumberFormatConverterStream(Print &print) { setOutput(print); } - NumberFormatConverterStream(AudioOutput &print) { setOutput(print); } - virtual ~NumberFormatConverterStream() { end(); } - - void setAudioInfo(AudioInfo newInfo) override { - TRACED(); - this->from_bit_per_samples = newInfo.bits_per_sample; - LOGI("-> NumberFormatConverterStream:") - AudioStream::setAudioInfo(newInfo); - } - - AudioInfo audioInfoOut() override { - AudioInfo result = audioInfo(); - result.bits_per_sample = to_bit_per_samples; - return result; - } - - bool begin(AudioInfo info, AudioInfo to, float gain = 1.0f) { - if (info.sample_rate != to.sample_rate) { - LOGE("sample_rate does not match") - return false; - } - if (info.channels != to.channels) { - LOGE("channels do not match") - return false; - } - return begin(info, to.bits_per_sample, gain); - } + NumberFormatConverterStream(Print &print) { setStream(print); } - bool begin(AudioInfo info, int toBits, float gain = 1.0f) { - setAudioInfo(info); - return begin(info.bits_per_sample, toBits, gain); + void setAudioInfo (AudioInfo info) override { + this->from_bit_per_samples = info.sample_rate; + AudioStream::setAudioInfo(info); } - bool begin() override { - return begin(from_bit_per_samples, to_bit_per_samples, gain); + bool begin(AudioInfo info, int to_bit_per_samples) { + AudioStream::setAudioInfo(info); + return begin(info.bits_per_sample, to_bit_per_samples); } - void end() override { cleanupConverter(); } - - void setToBits(uint8_t bits) { to_bit_per_samples = bits; } - - bool begin(int from_bit_per_samples, int to_bit_per_samples, - float gain = 1.0) { + bool begin(int from_bit_per_samples, int to_bit_per_samples) { LOGI("begin %d -> %d bits", from_bit_per_samples, to_bit_per_samples); bool result = true; - assert(to_bit_per_samples > 0); - - /// cleanup if we call begin() multiple times w/o end - if (p_converter != nullptr) end(); - - // store variables - this->gain = gain; this->from_bit_per_samples = from_bit_per_samples; this->to_bit_per_samples = to_bit_per_samples; @@ -515,26 +379,24 @@ class NumberFormatConverterStream : public ReformatBaseStream { LOGI("no bit conversion: %d -> %d", from_bit_per_samples, to_bit_per_samples); } else if (from_bit_per_samples == 8 && to_bit_per_samples == 16) { - p_converter = new NumberFormatConverterStreamT(gain); + converter = new NumberFormatConverterStreamT(); } else if (from_bit_per_samples == 16 && to_bit_per_samples == 8) { - p_converter = new NumberFormatConverterStreamT(gain); + converter = new NumberFormatConverterStreamT(); } else if (from_bit_per_samples == 24 && to_bit_per_samples == 16) { - p_converter = new NumberFormatConverterStreamT(gain); + converter = new NumberFormatConverterStreamT(); } else if (from_bit_per_samples == 16 && to_bit_per_samples == 24) { - p_converter = new NumberFormatConverterStreamT(gain); + converter = new NumberFormatConverterStreamT(); } else if (from_bit_per_samples == 32 && to_bit_per_samples == 16) { - p_converter = new NumberFormatConverterStreamT(gain); + converter = new NumberFormatConverterStreamT(); } else if (from_bit_per_samples == 16 && to_bit_per_samples == 32) { - p_converter = new NumberFormatConverterStreamT(gain); + converter = new NumberFormatConverterStreamT(); } else { result = false; LOGE("bit combination not supported %d -> %d", from_bit_per_samples, to_bit_per_samples); } - if (from_bit_per_samples != to_bit_per_samples) { - setupStream(); - } + setupStream(); if (!result) { TRACEE() @@ -542,49 +404,47 @@ class NumberFormatConverterStream : public ReformatBaseStream { return result; } - virtual size_t write(const uint8_t *data, size_t len) override { - LOGD("NumberFormatConverterStream::write: %d", (int)len); - if (p_print == nullptr) return 0; + virtual size_t write(const uint8_t *data, size_t size) override { + LOGD("NumberFormatConverterStream::write: %d", (int) size); if (from_bit_per_samples == to_bit_per_samples) { - return p_print->write(data, len); + return p_print->write(data, size); } if (from_bit_per_samples == 8 && to_bit_per_samples == 16) { - return getConverter()->write(data, len); + return getConverter()->write(data, size); } else if (from_bit_per_samples == 16 && to_bit_per_samples == 8) { - return getConverter()->write(data, len); + return getConverter()->write(data, size); } else if (from_bit_per_samples == 24 && to_bit_per_samples == 16) { - return getConverter()->write(data, len); + return getConverter()->write(data, size); } else if (from_bit_per_samples == 16 && to_bit_per_samples == 24) { - return getConverter()->write(data, len); + return getConverter()->write(data, size); } else if (from_bit_per_samples == 32 && to_bit_per_samples == 16) { - return getConverter()->write(data, len); + return getConverter()->write(data, size); } else if (from_bit_per_samples == 16 && to_bit_per_samples == 32) { - return getConverter()->write(data, len); + return getConverter()->write(data, size); } else { - LOGE("bit combination not supported %d -> %d", from_bit_per_samples, - to_bit_per_samples); + TRACEE(); return 0; } } - size_t readBytes(uint8_t *data, size_t len) override { - LOGD("NumberFormatConverterStream::readBytes: %d", (int)len); + size_t readBytes(uint8_t *data, size_t size) override { + LOGD("NumberFormatConverterStream::readBytes: %d", (int)size); if (from_bit_per_samples == to_bit_per_samples) { - return p_stream->readBytes(data, len); + return p_stream->readBytes(data, size); } if (from_bit_per_samples == 8 && to_bit_per_samples == 16) { - return getConverter()->readBytes(data, len); + return getConverter()->readBytes(data, size); } else if (from_bit_per_samples == 16 && to_bit_per_samples == 8) { - return getConverter()->readBytes(data, len); + return getConverter()->readBytes(data, size); } else if (from_bit_per_samples == 24 && to_bit_per_samples == 16) { - return getConverter()->readBytes(data, len); + return getConverter()->readBytes(data, size); } else if (from_bit_per_samples == 16 && to_bit_per_samples == 24) { - return getConverter()->readBytes(data, len); + return getConverter()->readBytes(data, size); } else if (from_bit_per_samples == 32 && to_bit_per_samples == 16) { - return getConverter()->readBytes(data, len); + return getConverter()->readBytes(data, size); } else if (from_bit_per_samples == 16 && to_bit_per_samples == 32) { - return getConverter()->readBytes(data, len); + return getConverter()->readBytes(data, size); } else { TRACEE(); return 0; @@ -614,7 +474,6 @@ class NumberFormatConverterStream : public ReformatBaseStream { } virtual int availableForWrite() override { - if (p_print == nullptr) return 0; if (from_bit_per_samples == to_bit_per_samples) { return p_print->availableForWrite(); } @@ -652,47 +511,14 @@ class NumberFormatConverterStream : public ReformatBaseStream { } } - float getByteFactor() override { - return static_cast(to_bit_per_samples) / - static_cast(from_bit_per_samples); - } - protected: - void *p_converter = nullptr; - int from_bit_per_samples = 16; + void *converter = nullptr; + int from_bit_per_samples = 0; int to_bit_per_samples = 0; - float gain = 1.0; - - void cleanupConverter() { - if (p_converter == nullptr) return; - - if (from_bit_per_samples == 8 && to_bit_per_samples == 16) { - delete static_cast *>( - p_converter); - } else if (from_bit_per_samples == 16 && to_bit_per_samples == 8) { - delete static_cast *>( - p_converter); - } else if (from_bit_per_samples == 24 && to_bit_per_samples == 16) { - delete static_cast *>( - p_converter); - } else if (from_bit_per_samples == 16 && to_bit_per_samples == 24) { - delete static_cast *>( - p_converter); - } else if (from_bit_per_samples == 32 && to_bit_per_samples == 16) { - delete static_cast *>( - p_converter); - } else if (from_bit_per_samples == 16 && to_bit_per_samples == 32) { - delete static_cast *>( - p_converter); - } else { - TRACEE(); - } - p_converter = nullptr; - } template NumberFormatConverterStreamT *getConverter() { - return (NumberFormatConverterStreamT *)p_converter; + return (NumberFormatConverterStreamT *)converter; } void setupStream() { @@ -714,17 +540,17 @@ class NumberFormatConverterStream : public ReformatBaseStream { } } else { if (from_bit_per_samples == 8 && to_bit_per_samples == 16) { - getConverter()->setOutput(*p_print); + getConverter()->setStream(*p_print); } else if (from_bit_per_samples == 16 && to_bit_per_samples == 8) { - getConverter()->setOutput(*p_print); + getConverter()->setStream(*p_print); } else if (from_bit_per_samples == 24 && to_bit_per_samples == 16) { - getConverter()->setOutput(*p_print); + getConverter()->setStream(*p_print); } else if (from_bit_per_samples == 16 && to_bit_per_samples == 24) { - getConverter()->setOutput(*p_print); + getConverter()->setStream(*p_print); } else if (from_bit_per_samples == 32 && to_bit_per_samples == 16) { - getConverter()->setOutput(*p_print); + getConverter()->setStream(*p_print); } else if (from_bit_per_samples == 16 && to_bit_per_samples == 32) { - getConverter()->setOutput(*p_print); + getConverter()->setStream(*p_print); } else { TRACEE(); } @@ -744,76 +570,51 @@ class FormatConverterStream : public ReformatBaseStream { public: FormatConverterStream() = default; FormatConverterStream(Stream &stream) { setStream(stream); } - FormatConverterStream(Print &print) { setOutput(print); } + FormatConverterStream(Print &print) { setStream(print); } FormatConverterStream(AudioStream &stream) { - to_cfg = stream.audioInfo(); - from_cfg = stream.audioInfo(); + setSourceAudioInfo(stream.audioInfo()); setStream(stream); } FormatConverterStream(AudioOutput &print) { - to_cfg = print.audioInfo(); - setOutput(print); + setSourceAudioInfo(print.audioInfo()); + setStream(print); } void setStream(Stream &io) override { TRACED(); + //p_print = &print; ReformatBaseStream::setStream(io); sampleRateConverter.setStream(io); } - void setStream(AudioStream &io) override { - TRACED(); - ReformatBaseStream::setStream(io); - sampleRateConverter.setStream(io); - } - - void setOutput(Print &print) override { + void setStream(Print &print) override { TRACED(); - ReformatBaseStream::setOutput(print); - sampleRateConverter.setOutput(print); + //p_print = &print; + ReformatBaseStream::setStream(print); + sampleRateConverter.setStream(print); } - void setOutput(AudioOutput &print) override { - TRACED(); - ReformatBaseStream::setOutput(print); - sampleRateConverter.setOutput(print); - } - - void setAudioInfo(AudioInfo info) override { - TRACED(); - from_cfg = info; - sampleRateConverter.setAudioInfo(info); - numberFormatConverter.setAudioInfo(info); - channelFormatConverter.setAudioInfo(info); - ReformatBaseStream::setAudioInfo(info); - } - - void setAudioInfoOut(AudioInfo to) { to_cfg = to; } - AudioInfo audioInfoOut() override { return to_cfg; } + /// Defines the audio info of the stream which has been defined in the + /// constructor + void setSourceAudioInfo(AudioInfo from) { from_cfg = from; } bool begin(AudioInfo from, AudioInfo to) { - TRACED(); - setAudioInfoOut(to); - return begin(from); + setSourceAudioInfo(from); + return begin(to); } - /// Starts the processing: call setAudioInfoOut before to define the target - bool begin(AudioInfo from) { - setAudioInfo(from); - return begin(); - } - - /// (Re-)Starts the processing: call setAudioInfo and setAudioInfoOut before - bool begin() override { + bool begin(AudioInfo to) { TRACED(); - // is_output_notify = false; - // build output chain - if (getStream() != nullptr) { + setAudioInfo(to); + to_cfg = to; + + // build output chain + if (getStream()!=nullptr){ sampleRateConverter.setStream(*getStream()); } - if (getPrint() != nullptr) { - sampleRateConverter.setOutput(*getPrint()); + if(getPrint()!=nullptr){ + sampleRateConverter.setStream(*getPrint()); } numberFormatConverter.setStream(sampleRateConverter); channelFormatConverter.setStream(numberFormatConverter); @@ -826,14 +627,15 @@ class FormatConverterStream : public ReformatBaseStream { result &= numberFormatConverter.begin(from_actual_cfg.bits_per_sample, to_cfg.bits_per_sample); - numberFormatConverter.setBuffered(is_buffered); - sampleRateConverter.setBuffered(is_buffered); + numberFormatConverter.setBuffered(true); from_actual_cfg.bits_per_sample = to_cfg.bits_per_sample; result &= sampleRateConverter.begin(from_actual_cfg, to_cfg.sample_rate); // setup reader to support readBytes() - setupReader(); + if (getStream()!=nullptr){ + setupReader(); + } if (!result) { LOGE("begin failed"); @@ -841,18 +643,9 @@ class FormatConverterStream : public ReformatBaseStream { return result; } - virtual size_t write(const uint8_t *data, size_t len) override { - LOGD("FormatConverterStream::write: %d", (int)len); - // addNotifyOnFirstWrite(); - return channelFormatConverter.write(data, len); - } - - /// Buffering is active by default to minimize the number of output calls - void setBuffered(bool active) { is_buffered = active; } - - float getByteFactor() override { - return numberFormatConverter.getByteFactor() * - channelFormatConverter.getByteFactor(); + virtual size_t write(const uint8_t *data, size_t size) override { + LOGD("FormatConverterStream::write: %d", (int)size); + return channelFormatConverter.write(data, size); } protected: @@ -861,7 +654,6 @@ class FormatConverterStream : public ReformatBaseStream { NumberFormatConverterStream numberFormatConverter; ChannelFormatConverterStream channelFormatConverter; ResampleStream sampleRateConverter; - bool is_buffered = true; /// e.g if we do channels 2->1 we nead to double the input data /// @return diff --git a/src/AudioTools/AudioTypes.h b/src/AudioTools/AudioTypes.h new file mode 100644 index 0000000000..442ed6f5df --- /dev/null +++ b/src/AudioTools/AudioTypes.h @@ -0,0 +1,349 @@ +#pragma once + +#ifdef USE_TYPETRAITS +# include +#endif +#include "AudioConfig.h" +#include "AudioTools/AudioLogger.h" +#include "AudioBasic/Int24.h" +#include "AudioBasic/Collections/Vector.h" + +namespace audio_tools { + +/** + * @brief Audio Source (TX_MODE) or Audio Sink (RX_MODE). RXTX_MODE is Source and Sink at the same time! + * @ingroup basic + */ +enum RxTxMode {UNDEFINED_MODE=0, TX_MODE=1, RX_MODE=2, RXTX_MODE=3 }; + +/** + * @brief Memory types + * @ingroup basic + */ + +enum MemoryType {RAM, PS_RAM, FLASH_RAM}; + +/** + * @brief Text string (description) for RxTxMode +*/ +INLINE_VAR const char* RxTxModeNames[]={"UNDEFINED_MODE","TX_MODE","RX_MODE","RXTX_MODE" }; +/** + * @brief Time Units + * @ingroup basic + */ +enum TimeUnit {MS, US}; + +/** + * @brief Basic Audio information which drives e.g. I2S + * @ingroup basic + */ +struct AudioInfo { + /// Default constructor + AudioInfo() = default; + + /// Constructor which supports all attribures as parameters + AudioInfo(int sampleRate, int channelCount, int bitsPerSample) { + sample_rate = sampleRate; + channels = channelCount; + bits_per_sample = bitsPerSample; + }; + + /// Copy constructor + AudioInfo(const AudioInfo &) = default; + + bool operator==(AudioInfo alt){ + return sample_rate==alt.sample_rate && channels == alt.channels && bits_per_sample == alt.bits_per_sample; + } + + bool operator!=(AudioInfo alt){ + return !(*this == alt); + } + + /// Copies the values from info + void set(AudioInfo info) { + sample_rate = info.sample_rate; + channels = info.channels; + bits_per_sample = info.bits_per_sample; + } + + /// Same as set + void setAudioInfo(AudioInfo info) { + set(info); + } + + /// Same as set + void copyFrom(AudioInfo info){ + setAudioInfo(info); + } + + AudioInfo& operator= (const AudioInfo& info){ + setAudioInfo(info); + return *this; + } + + virtual void logInfo() { + LOGI("sample_rate: %d", sample_rate); + LOGI("channels: %d", channels); + LOGI("bits_per_sample: %d", bits_per_sample); + } + + // public attributes + int sample_rate = 0; // undefined + int channels = 0; // undefined + int bits_per_sample=16; // we assume int16_t + +}; + +/** + * @brief Supports changes to the sampling rate, bits and channels + * @ingroup basic + */ +class AudioInfoSupport { + public: + virtual void setAudioInfo(AudioInfo info)=0; + virtual AudioInfo audioInfo() = 0; + virtual bool validate(AudioInfo &info){ + return true; + } +}; + +// Support legacy name +using AudioBaseInfo = AudioInfo; +using AudioBaseInfoDependent = AudioInfoSupport; +using AudioInfoDependent = AudioInfoSupport; + + +/** + * @brief Supports the subscription to audio change notifications + * @ingroup basic + */ +class AudioInfoSource { + public: + virtual void setNotifyAudioChange(AudioInfoSupport &bi) = 0; +}; + + +/** + * @brief E.g. used by Encoders and Decoders + * @ingroup basic + */ +class AudioWriter { + public: + virtual size_t write(const void *in_ptr, size_t in_size) = 0; + virtual void setAudioInfo(AudioInfo from) = 0; + virtual void setOutput(Print &out_stream) = 0; + virtual operator bool() = 0; + virtual void begin() = 0; + virtual void begin(AudioInfo info) { + setAudioInfo(info); + begin(); + } + virtual void end() = 0; + protected: + void writeBlocking(Print *out, uint8_t* data, size_t len){ + TRACED(); + int open = len; + int written = 0; + while(open>0){ + int result = out->write(data+written, open); + open -= result; + written += result; + } + } +}; + +/** + * @brief Tools for calculating timer values + * @ingroup timer + */ +class AudioTime { + public: + /// converts sampling rate to delay in microseconds (μs) + static uint32_t toTimeUs(uint32_t samplingRate, uint8_t limit=10){ + uint32_t result = 1000000l / samplingRate; + if (1000000l % samplingRate!=0){ + result++; + } + if (result <= limit){ + LOGW("Time for samplingRate %u -> %u is < %u μs - we rounded up", (unsigned int)samplingRate, (unsigned int)result, (unsigned int)limit); + result = limit; + } + return result; + } + + /// converts milliseconds to bytes + static uint32_t toBytes(uint32_t millis, AudioInfo info){ + size_t samples = info.sample_rate * millis / 1000; + size_t bytes = samples * info.channels * info.bits_per_sample / 8; + return bytes; + } + + static uint32_t toTimeMs(uint32_t samplingRate, uint8_t limit=1){ + uint32_t result = 1000l / samplingRate; + if (1000000l % samplingRate!=0){ + result++; + } + if (result <= limit){ + LOGW("Time for samplingRate %u -> %u is < %u μs - we rounded up", (unsigned int)samplingRate, (unsigned int)result, (unsigned int)limit); + result = limit; + } + return result; + } +}; + +/** + * @brief Converts from a source to a target number with a different type + * @ingroup basic + */ +class NumberConverter { + public: + static int32_t convertFrom24To32(int24_t value) { + return value.scale32(); + } + + static int16_t convertFrom24To16(int24_t value) { + return value.scale16(); + } + + static float convertFrom24ToFloat(int24_t value) { + return value.scaleFloat(); + } + + static int16_t convertFrom32To16(int32_t value) { + return static_cast(value) / INT32_MAX * INT16_MAX; + } + + static int16_t convert16(int value, int value_bits_per_sample){ + return value * NumberConverter::maxValue(16) / NumberConverter::maxValue(value_bits_per_sample); + } + + static int16_t convert8(int value, int value_bits_per_sample){ + return value * NumberConverter::maxValue(8) / NumberConverter::maxValue(value_bits_per_sample); + } + + /// provides the biggest number for the indicated number of bits + static int64_t maxValue(int value_bits_per_sample){ + switch(value_bits_per_sample){ + case 8: + return 127; + case 16: + return 32767; + case 24: + return 8388607; + case 32: + return 2147483647; + } + return 32767; + } + + /// provides the biggest number for the indicated type + template + static int64_t maxValueT(){ +#ifdef USE_TYPETRAITS + // int24_t uses 4 bytes instead of 3! + return (std::is_same::value ) ? 8388607 : maxValue(sizeof(T)*8); +#else + return maxValue(sizeof(T)*8); +#endif + } + + /// Clips the value to avoid any over or underflows + template + static T clip(int64_t value){ + T mv = maxValue(sizeof(T)*8); + if (value > mv){ + return mv; + } else if (value < -mv){ + return -mv; + } + return value; + } + + /// Convert a number from one type to another + template + static ToT convert(FromT value){ + int64_t value1 = value; + return clip(value1 * maxValueT() / maxValueT()); + } + +}; + +/// guaranteed to return the requested data +template +T readSample(Stream* p_stream){ + T result=0; + uint8_t *p_result = (uint8_t*) &result; + int open = sizeof(T); + int total = 0; + // copy missing data + while (open>0){ + int read = p_stream->readBytes(p_result+total, open); + open -= read; + total += read; + } + return result; +} + +/// guaranteed to return the requested data +template +size_t readSamples(Stream* p_stream, T* data, int samples){ + uint8_t *p_result = (uint8_t*) data; + int open = samples*sizeof(T); + int total = 0; + // copy missing data + while (open>0){ + int read = p_stream->readBytes(p_result+total, open); + open -= read; + total += read; + } + return samples; +} + +/// guaranteed to return the requested data +template +size_t writeSamples(Print* p_out, T* data, int samples){ + uint8_t *p_result = (uint8_t*) data; + int open = samples*sizeof(T); + int total = 0; + // copy missing data + while (open>0){ + int written = p_out->write(p_result+total, open); + open -= written; + total += written; + } + return samples; +} + + +/// @brief Similar to Arduino map function but using floats +/// @param x +/// @param in_min +/// @param in_max +/// @param out_min +/// @param out_max +/// @return +inline float mapFloat(float x, float in_min, float in_max, float out_min, float out_max) { + return (x - in_min) * (out_max - out_min) / (in_max - in_min) + out_min; +} + +/// @brief Mime type for PCM +static const char* mime_pcm = "audio/pcm"; + + +#ifndef IS_DESKTOP +/// wait for the Output to be ready +inline void waitFor(HardwareSerial &out){ + while(!out); +} +#endif + +/// wait for flag to be active @ingroup basic +inline void waitFor(bool &flag){ + while(!flag); +} + +/// Pins @ingroup basic +using Pins = Vector; + + +} \ No newline at end of file diff --git a/src/AudioTools/BaseConverter.h b/src/AudioTools/BaseConverter.h new file mode 100644 index 0000000000..574df872f8 --- /dev/null +++ b/src/AudioTools/BaseConverter.h @@ -0,0 +1,938 @@ +#pragma once +#include "AudioTypes.h" +#include "AudioBasic/Collections.h" +#include "AudioFilter/Filter.h" + +/** + * @defgroup convert Converters + * @ingroup tools + * @brief Convert Audio + * You can add a converter as argument to the StreamCopy::copy() or better use is with + * a ConverterStream. + */ + +namespace audio_tools { + + +/** + * @brief Abstract Base class for Converters + * A converter is processing the data in the indicated array + * @ingroup convert + * @author Phil Schatzmann + * @copyright GPLv3 + * @tparam T + */ +template +class BaseConverter { + public: + BaseConverter() = default; + BaseConverter(BaseConverter const&) = delete; + BaseConverter& operator=(BaseConverter const&) = delete; + + virtual size_t convert(uint8_t *src, size_t size) = 0; +}; + + +/** + * @brief Dummy converter which does nothing + * @ingroup convert + * @tparam T + */ +template +class NOPConverter : public BaseConverter { + public: + virtual size_t convert(uint8_t(*src), size_t size) {return size;}; +}; + +/** + * @brief Multiplies the values with the indicated factor adds the offset and clips at maxValue. To mute use a factor of 0.0! + * @ingroup convert + * @author Phil Schatzmann + * @copyright GPLv3 + * + * @tparam T + */ +template +class ConverterScaler : public BaseConverter { + public: + ConverterScaler(float factor, T offset, T maxValue, int channels=2){ + this->factor_value = factor; + this->maxValue = maxValue; + this->offset_value = offset; + this->channels = channels; + } + + size_t convert(uint8_t*src, size_t byte_count) { + T *sample = (T*)src; + int size = byte_count / channels / sizeof(T); + for (size_t j=0;jmaxValue){ + *sample = maxValue; + } else if (*sample<-maxValue){ + *sample = -maxValue; + } + sample++; + } + } + return byte_count; + } + + /// Defines the factor (volume) + void setFactor(T factor){ + this->factor_value = factor; + } + + /// Defines the offset + void setOffset(T offset) { + this->offset_value = offset; + } + + /// Determines the actual factor (volume) + float factor() { + return factor_value; + } + + /// Determines the offset value + T offset() { + return offset_value; + } + + protected: + int channels; + float factor_value; + T maxValue; + T offset_value; +}; + +/** + * @brief Makes sure that the avg of the signal is set to 0 + * @ingroup convert + * @tparam T + */ +template +class ConverterAutoCenter : public BaseConverter { + public: + ConverterAutoCenter(int channels=2){ + this->channels = channels; + } + + size_t convert(uint8_t(*src), size_t byte_count) { + int size = byte_count / channels / sizeof(T); + T *sample = (T*) src; + setup((T*)src, size); + if (is_setup){ + for (size_t j=0; j0){ + offset = left; + is_setup = true; + } else if (right>0){ + offset = right; + is_setup = true; + } + } + } +}; + +/** + * @brief Switches the left and right channel + * @ingroup convert + * @author Phil Schatzmann + * @copyright GPLv3 + * + * @tparam T + */ +template +class ConverterSwitchLeftAndRight : public BaseConverter { + public: + ConverterSwitchLeftAndRight(int channels=2){ + this->channels = channels; + } + + size_t convert(uint8_t*src, size_t byte_count) { + if (channels==2){ + int size = byte_count / channels / sizeof(T); + T *sample = (T*) src; + for (size_t j=0;j +class ConverterFillLeftAndRight : public BaseConverter { + public: + ConverterFillLeftAndRight(FillLeftAndRightStatus config=Auto, int channels=2){ + this->channels = channels; + switch(config){ + case LeftIsEmpty: + left_empty = true; + right_empty = false; + is_setup = true; + break; + case RightIsEmpty: + left_empty = false; + right_empty = true; + is_setup = true; + break; + case Auto: + is_setup = false; + break; + } + } + + size_t convert(uint8_t*src, size_t byte_count) { + if (channels==2){ + int size = byte_count / channels / sizeof(T); + setup((T*)src, size); + if (left_empty && !right_empty){ + T *sample = (T*)src; + for (size_t j=0;j +class ConverterToInternalDACFormat : public BaseConverter { + public: + ConverterToInternalDACFormat(int channels=2){ + this->channels = channels; + } + + size_t convert(uint8_t*src, size_t byte_count) { + int size = byte_count / channels / sizeof(T); + T *sample = (T*) src; + for (int i=0; i +class ChannelReducer : public BaseConverter { + public: + ChannelReducer() = default; + + ChannelReducer(int channelCountOfTarget, int channelCountOfSource){ + from_channels = channelCountOfSource; + to_channels = channelCountOfTarget; + } + + void setSourceChannels(int channelCountOfSource) { + from_channels = channelCountOfSource; + } + + void setTargetChannels(int channelCountOfTarget) { + to_channels = channelCountOfTarget; + } + + size_t convert(uint8_t*src, size_t size) { + return convert(src,src,size); + } + + size_t convert(uint8_t*target, uint8_t*src, size_t size) { + int frame_count = size/(sizeof(T)*from_channels); + size_t result_size=0; + T* result = (T*)target; + T* source = (T*)src; + int reduceDiv = from_channels-to_channels+1; + + for(int i=0; i < frame_count; i++){ + // copy first to_channels-1 + for (int j=0;j +class ChannelEnhancer { + public: + ChannelEnhancer() = default; + + ChannelEnhancer(int channelCountOfTarget, int channelCountOfSource){ + from_channels = channelCountOfSource; + to_channels = channelCountOfTarget; + } + + void setSourceChannels(int channelCountOfSource) { + from_channels = channelCountOfSource; + } + + void setTargetChannels(int channelCountOfTarget) { + to_channels = channelCountOfTarget; + } + + size_t convert(uint8_t*target, uint8_t*src, size_t size) { + int frame_count = size/(sizeof(T)*from_channels); + size_t result_size=0; + T* result = (T*)target; + T* source = (T*)src; + T value = (int16_t)0; + for(int i=0; i < frame_count; i++){ + // copy available channels + for (int j=0;j +class ChannelConverter { + public: + ChannelConverter() = default; + + ChannelConverter(int channelCountOfTarget, int channelCountOfSource){ + from_channels = channelCountOfSource; + to_channels = channelCountOfTarget; + } + + void setSourceChannels(int channelCountOfSource) { + from_channels = channelCountOfSource; + } + + void setTargetChannels(int channelCountOfTarget) { + to_channels = channelCountOfTarget; + } + + size_t convert(uint8_t*target, uint8_t*src, size_t size) { + if (from_channels==to_channels){ + memcpy(target,src,size); + return size; + } + // setup channels + if (from_channels>to_channels){ + reducer.setSourceChannels(from_channels); + reducer.setTargetChannels(to_channels); + } else { + enhancer.setSourceChannels(from_channels); + enhancer.setTargetChannels(to_channels); + } + + // execute conversion + if (from_channels>to_channels){ + return reducer.convert(target, src, size); + } else { + return enhancer.convert(target, src, size); + } + } + + protected: + ChannelEnhancer enhancer; + ChannelReducer reducer; + int from_channels; + int to_channels; + +}; + +/** + * @brief Combines multiple converters + * @ingroup convert + * @tparam T + */ +template +class MultiConverter : public BaseConverter { + public: + MultiConverter(){ + } + + MultiConverter(BaseConverter &c1){ + add(c1); + } + + MultiConverter(BaseConverter &c1, BaseConverter &c2){ + add(c1); + add(c2); + } + + MultiConverter(BaseConverter &c1, BaseConverter &c2, BaseConverter &c3){ + add(c1); + add(c2); + add(c3); + } + + // adds a converter + void add(BaseConverter &converter){ + converters.push_back(&converter); + } + + // The data is provided as int24_t tgt[][2] but returned as int24_t + size_t convert(uint8_t*src, size_t size) { + for(int i=0; i < converters.size(); i++){ + converters[i]->convert(src, size); + } + return size; + } + + private: + Vector*> converters; + +}; + +// /** +// * @brief Converts e.g. 24bit data to the indicated smaller or bigger data type +// * @ingroup convert +// * @author Phil Schatzmann +// * @copyright GPLv3 +// * +// * @tparam T +// */ +// template +// class FormatConverter { +// public: +// FormatConverter(ToType (*converter)(FromType v)){ +// this->convert_ptr = converter; +// } + +// FormatConverter( float factor, float clip){ +// this->factor = factor; +// this->clip = clip; +// } + +// // The data is provided as int24_t tgt[][2] but returned as int24_t +// size_t convert(uint8_t *src, uint8_t *target, size_t byte_count_src) { +// return convert((FromType *)src, (ToType*)target, byte_count_src ); +// } + +// // The data is provided as int24_t tgt[][2] but returned as int24_t +// size_t convert(FromType *src, ToType *target, size_t byte_count_src) { +// int size = byte_count_src / sizeof(FromType); +// FromType *s = src; +// ToType *t = target; +// if (convert_ptr!=nullptr){ +// // standard conversion +// for (int i=size; i>0; i--) { +// *t = (*convert_ptr)(*s); +// t++; +// s++; +// } +// } else { +// // conversion using indicated factor +// for (int i=size; i>0; i--) { +// float tmp = factor * *s; +// if (tmp>clip){ +// tmp=clip; +// } else if (tmp<-clip){ +// tmp = -clip; +// } +// *t = tmp; +// t++; +// s++; +// } +// } +// return size*sizeof(ToType); +// } + +// private: +// ToType (*convert_ptr)(FromType v) = nullptr; +// float factor=0; +// float clip=0; + +// }; + + + +/** + * @brief Reads n numbers from an Arduino Stream + * + */ +class NumberReader { + public: + NumberReader(Stream &in) { + stream_ptr = ∈ + } + + NumberReader() { + } + + bool read(int inBits, int outBits, bool outSigned, int n, int32_t *result){ + bool result_bool = false; + int len = inBits/8 * n; + if (stream_ptr!=nullptr && stream_ptr->available()>len){ + uint8_t buffer[len]; + stream_ptr->readBytes((uint8_t*)buffer, n * len); + result_bool = toNumbers((void*)buffer, inBits, outBits, outSigned, n, result); + } + return result_bool; + } + + /// converts a buffer to a number array + bool toNumbers(void *bufferIn, int inBits, int outBits, bool outSigned, int n, int32_t *result){ + bool result_bool = false; + switch(inBits){ + case 8: { + int8_t *buffer=(int8_t *)bufferIn; + for (int j=0;j(value) / NumberConverter::maxValue(inBits) * NumberConverter::maxValue(outBits); + if (!outSigned){ + result += (NumberConverter::maxValue(outBits) / 2); + } + return result; + } + +}; + + +/** + * @brief Converter for 1 Channel which applies the indicated Filter + * @ingroup convert + * @author pschatzmann + * @tparam T + */ +template +class Converter1Channel : public BaseConverter { + public: + Converter1Channel(Filter &filter) { this->p_filter = &filter; } + + size_t convert(uint8_t *src, size_t size) { + T *data = (T *)src; + for (size_t j = 0; j < size; j++) { + data[j] = p_filter->process(data[j]); + } + return size; + } + + protected: + Filter *p_filter = nullptr; +}; + +/** + * @brief Converter for n Channels which applies the indicated Filter + * @ingroup convert + * @author pschatzmann + * @tparam T + */ +template +class ConverterNChannels : public BaseConverter { + public: + /// Default Constructor + ConverterNChannels(int channels) { + this->channels = channels; + filters = new Filter *[channels]; + // make sure that we have 1 filter per channel + for (int j = 0; j < channels; j++) { + filters[j] = nullptr; + } + } + + /// Destrucotr + ~ConverterNChannels() { + for (int j = 0; j < channels; j++) { + if (filters[j]!=nullptr){ + delete filters[j]; + } + } + delete[] filters; + filters = 0; + } + + /// defines the filter for an individual channel - the first channel is 0 + void setFilter(int channel, Filter *filter) { + if (channelprocess(*sample); + } + sample++; + } + } + return size; + } + + int getChannels() { + return channels; + } + + protected: + Filter **filters = nullptr; + int channels; +}; + +/** + * @brief Removes any silence from the buffer that is longer then n samples with a amplitude + * below the indicated threshhold. If you process multiple channels you need to multiply the + * channels with the number of samples to indicate n + * @ingroup convert + * @tparam T + */ + +template +class SilenceRemovalConverter : public BaseConverter { + public: + + SilenceRemovalConverter(int n = 8, int aplidudeLimit = 2) { + set(n, aplidudeLimit); + } + + virtual size_t convert(uint8_t *data, size_t size) override { + if (!active) { + // no change to the data + return size; + } + size_t sample_count = size / sizeof(T); + size_t write_count = 0; + T *audio = (T *)data; + + // find relevant data + T *p_buffer = (T *)data; + for (int j = 0; j < sample_count; j++) { + int pos = findLastAudioPos(audio, j); + if (pos < n) { + write_count++; + *p_buffer++ = audio[j]; + } + } + + // write audio data w/o silence + size_t write_size = write_count * sizeof(T); + LOGI("filtered silence from %d -> %d", (int)size, (int)write_size); + + // number of empty samples of prior buffer + priorLastAudioPos = findLastAudioPos(audio, sample_count - 1); + + // return new data size + return write_size; + } + + protected: + bool active = false; + const uint8_t *buffer = nullptr; + int n; + int priorLastAudioPos = 0; + int amplidude_limit = 0; + + void set(int n = 5, int aplidudeLimit = 2) { + LOGI("begin(n=%d, aplidudeLimit=%d", n, aplidudeLimit); + this->n = n; + this->amplidude_limit = aplidudeLimit; + this->priorLastAudioPos = n+1; // ignore first values + this->active = n > 0; + } + + + // find last position which contains audible data + int findLastAudioPos(T *audio, int pos) { + for (int j = 0; j < n; j++) { + // we are before the start of the current buffer + if (pos - j <= 0) { + return priorLastAudioPos; + } + // we are in the current buffer + if (abs(audio[pos - j]) > amplidude_limit) { + return j; + } + } + return n + 1; + } +}; + +/** + * @brief Big value gaps (at the beginning and the end of a recording) can lead to some popping sounds. + * We will try to set the values to 0 until the first transition thru 0 of the audio curve + * @ingroup convert + * @tparam T + */ +template +class PoppingSoundRemover : public BaseConverter { + public: + PoppingSoundRemover(int channels, bool fromBeginning, bool fromEnd){ + this->channels = channels; + from_beginning = fromBeginning; + from_end = fromEnd; + } + virtual size_t convert(uint8_t *src, size_t size) { + for (int ch=0;ch= 0.0) || (first>=0.0 && act <= 0.0)){ + // we found the last transition so we are done + break; + } else { + values[j] = 0; + } + } + } + + void clearAfterLastTransition(int channels, int channel, T* values, int sampleCount){ + int lastPos = sampleCount - channels + channel; + T last = values[lastPos]; + for (int j=lastPos;j>=0;j-=channels){ + T act = values[j]; + if ((last <= 0.0 && act >= 0.0) || (last>=0.0 && act <= 0.0)){ + // we found the last transition so we are done + break; + } else { + values[j] = 0; + } + } + } +}; + +/** + * @brief Changes the samples at the beginning or at the end to slowly ramp up the volume + * @ingroup convert + * @tparam T + */ +template +class SmoothTransition : public BaseConverter { + public: + SmoothTransition(int channels, bool fromBeginning, bool fromEnd, float inc=0.01){ + this->channels = channels; + this->inc = inc; + from_beginning = fromBeginning; + from_end = fromEnd; + } + virtual size_t convert(uint8_t *src, size_t size) { + for (int ch=0;ch=0.8){ + break; + } else { + values[j]=factor*values[j]; + } + factor += inc; + } + } + + void processEnd(int channels, int channel, T* values, int sampleCount){ + int lastPos = sampleCount - channels + channel; + for (int j=lastPos;j>=0;j-=channels){ + if (factor>=0.8){ + break; + } else { + values[j]=factor*values[j]; + } + } + } +}; + + + +} \ No newline at end of file diff --git a/src/AudioTools/Buffers.h b/src/AudioTools/Buffers.h new file mode 100644 index 0000000000..70f7484998 --- /dev/null +++ b/src/AudioTools/Buffers.h @@ -0,0 +1,777 @@ + +#pragma once + +#include "AudioBasic/Collections.h" +#include "AudioTools/AudioLogger.h" +#include /* For INT_MAX */ + +#undef MIN +#define MIN(A, B) ((A) < (B) ? (A) : (B)) + +/** + * @defgroup buffers Buffers + * @ingroup tools + * @brief Different Buffer Implementations + */ + +namespace audio_tools { + +// forward declaration +template +class NBuffer; + +/** + * @brief Shared functionality of all buffers + * @ingroup buffers + * @author Phil Schatzmann + * @copyright GPLv3 + */ +template +class BaseBuffer { + public: + BaseBuffer() = default; + virtual ~BaseBuffer() = default; + BaseBuffer(BaseBuffer const &) = delete; + BaseBuffer &operator=(BaseBuffer const &) = delete; + + /// reads a single value + virtual T read() = 0; + + /// reads multiple values + virtual int readArray(T data[], int len) { + int lenResult = MIN(len, available()); + for (int j = 0; j < lenResult; j++) { + data[j] = read(); + } + LOGD("readArray %d -> %d", len, lenResult); + return lenResult; + } + + /// Fills the data from the buffer + virtual int writeArray(const T data[], int len) { + // LOGD("%s: %d", LOG_METHOD, len); + // CHECK_MEMORY(); + + int result = 0; + for (int j = 0; j < len; j++) { + if (!write(data[j])) { + break; + } + result = j + 1; + } + // CHECK_MEMORY(); + LOGD("writeArray %d -> %d", len, result); + return result; + } + + /// reads multiple values for array of 2 dimensional frames + int readFrames(T data[][2], int len) { + LOGD("%s: %d", LOG_METHOD, len); + // CHECK_MEMORY(); + int result = MIN(len, available()); + for (int j = 0; j < result; j++) { + T sample = read(); + data[j][0] = sample; + data[j][1] = sample; + } + // CHECK_MEMORY(); + return result; + } + + template + int readFrames(T (&data)[rows][channels]) { + int lenResult = MIN(rows, available()); + for (int j = 0; j < lenResult; j++) { + T sample = read(); + for (int i = 0; i < channels; i++) { + // data[j][i] = htons(sample); + data[j][i] = sample; + } + } + return lenResult; + } + + /// peeks the actual entry from the buffer + virtual T peek() = 0; + + /// checks if the buffer is full + virtual bool isFull() = 0; + + bool isEmpty() { return available() == 0; } + + /// write add an entry to the buffer + virtual bool write(T data) = 0; + + /// clears the buffer + virtual void reset() = 0; + + /// same as reset + void clear() { reset(); } + + /// provides the number of entries that are available to read + virtual int available() = 0; + + /// provides the number of entries that are available to write + virtual int availableForWrite() = 0; + + /// returns the address of the start of the physical read buffer + virtual T *address() = 0; + + protected: + void setWritePos(int pos){}; + + friend NBuffer; +}; + +/** + * @brief A simple Buffer implementation which just uses a (dynamically sized) + * array + * @ingroup buffers + * @author Phil Schatzmann + * @copyright GPLv3 + */ + +template +class SingleBuffer : public BaseBuffer { + public: + /** + * @brief Construct a new Single Buffer object + * + * @param size + */ + SingleBuffer(int size) { + this->max_size = size; + buffer.resize(max_size); + reset(); + } + + /** + * @brief Construct a new Single Buffer w/o allocating any memory + */ + SingleBuffer() { reset(); } + + /// notifies that the external buffer has been refilled + void onExternalBufferRefilled(void *data, int len) { + this->owns_buffer = false; + this->buffer = (uint8_t *)data; + this->current_read_pos = 0; + this->current_write_pos = len; + } + + bool write(T sample) { + bool result = false; + if (current_write_pos < max_size) { + buffer[current_write_pos++] = sample; + result = true; + } + return result; + } + + T read() { + T result = 0; + if (current_read_pos < current_write_pos) { + result = buffer[current_read_pos++]; + } + return result; + } + + T peek() { + T result = 0; + if (current_read_pos < current_write_pos) { + result = buffer[current_read_pos]; + } + return result; + } + + int available() { + int result = current_write_pos - current_read_pos; + return max(result, 0); + } + + int availableForWrite() { return max_size - current_write_pos; } + + bool isFull() { return availableForWrite() <= 0; } + + T *address() { return buffer.data(); } + T *data() { return buffer.data(); } + + void reset() { + current_read_pos = 0; + current_write_pos = 0; + } + + /// If we load values directly into the address we need to set the avialeble + /// size + void setAvailable(size_t available_size) { + current_read_pos = 0; + current_write_pos = available_size; + } + + size_t size() { return max_size; } + + void resize(int size) { + if (buffer.size() != size) { + TRACED(); + buffer.resize(size); + max_size = size; + } + } + + protected: + int max_size = 0; + int current_read_pos = 0; + int current_write_pos = 0; + bool owns_buffer = true; + Vector buffer{0}; + + void setWritePos(int pos) { current_write_pos = pos; } +}; + +/** + * @brief Implements a typed Ringbuffer + * @ingroup buffers + * @tparam T + */ +template +class RingBuffer : public BaseBuffer { + public: + RingBuffer(int size) { + resize(size); + reset(); + } + + virtual T read() { + if (isEmpty()) return -1; + + T value = _aucBuffer[_iTail]; + _iTail = nextIndex(_iTail); + _numElems--; + + return value; + } + + // peeks the actual entry from the buffer + virtual T peek() { + if (isEmpty()) return -1; + + return _aucBuffer[_iTail]; + } + + // checks if the buffer is full + virtual bool isFull() { return available() == max_size; } + + bool isEmpty() { return available() == 0; } + + // write add an entry to the buffer + virtual bool write(T data) { + bool result = false; + if (!isFull()) { + _aucBuffer[_iHead] = data; + _iHead = nextIndex(_iHead); + _numElems++; + result = true; + } + return result; + } + + // clears the buffer + virtual void reset() { + _iHead = 0; + _iTail = 0; + _numElems = 0; + } + + // provides the number of entries that are available to read + virtual int available() { return _numElems; } + + // provides the number of entries that are available to write + virtual int availableForWrite() { return (max_size - _numElems); } + + // returns the address of the start of the physical read buffer + virtual T *address() { return _aucBuffer.data(); } + + virtual void resize(int len) { + if (max_size != len) { + LOGI("resize: %d", len); + _aucBuffer.resize(len); + max_size = len; + } + } + + /// Returns the maximum capacity of the buffer + virtual int size() { return max_size; } + + protected: + Vector _aucBuffer; + int _iHead; + int _iTail; + int _numElems; + int max_size = 0; + + int nextIndex(int index) { return (uint32_t)(index + 1) % max_size; } +}; + +/** + * @brief An File backed Ring Buffer that we can use to receive + * streaming audio. We expect an open p_file as parameter. + * + * If you want to keep the processed data, call setAutoRewind(false) and + * call p_file->save() when you are done! + * @ingroup buffers + * @tparam File + * @tparam T + */ +template +class RingBufferFile : public BaseBuffer { + public: + RingBufferFile(bool autoRewind = true) { setAutoRewind(autoRewind); } + RingBufferFile(File &file, bool autoRewind = true) { + setFile(file); + setAutoRewind(autoRewind); + } + + /// if the full buffer has been consumed we restart from the 0 p_file position + /// Set this value to false if you want to keep the full processed data in the + /// p_file + void setAutoRewind(bool flag) { auto_rewind = true; } + + /// Assigns the p_file to be used. + void setFile(File &bufferFile, bool clear = false) { + p_file = &bufferFile; + if (!*p_file) { + LOGE("file is not valid"); + } + // if no clear has been requested we can access the existing data in the + // p_file + if (!clear) { + element_count = p_file->size() / sizeof(T); + LOGI("existing elements: %s", element_count); + read_pos = element_count; + } + } + + T read() override { + if (isEmpty()) return -1; + + T result = peek(); + read_pos++; + element_count--; + // the buffer is empty + if (auto_rewind && isEmpty()) { + LOGI("pos 0"); + write_pos = 0; + read_pos = 0; + } + + return result; + } + + /// reads multiple values + int readArray(T data[], int count) override { + if (p_file == nullptr) return 0; + int read_count = min(count, element_count); + file_seek(read_pos); + int elements_processed = file_read(data, read_count); + read_pos += elements_processed; + element_count -= elements_processed; + return elements_processed; + } + + // peeks the actual entry from the buffer + T peek() override { + if (p_file == nullptr) return 0; + if (isEmpty()) return -1; + + file_seek(read_pos); + T result; + size_t count = file_read(&result, 1); + return result; + } + + // checks if the buffer is full + bool isFull() override { return available() == max_size; } + + bool isEmpty() { return available() == 0; } + + // write add an entry to the buffer + virtual bool write(T data) override { return writeArray(&data, 1); } + + /// Fills the data from the buffer + int writeArray(const T data[], int len) override { + if (p_file == nullptr) return 0; + file_seek(write_pos); + int elements_written = file_write(data, len); + write_pos += elements_written; + element_count += elements_written; + return elements_written; + } + + // clears the buffer + void reset() override { + write_pos = 0; + read_pos = 0; + element_count = 0; + if (p_file != nullptr) file_seek(0); + } + + // provides the number of entries that are available to read + int available() override { return element_count; } + + // provides the number of entries that are available to write + int availableForWrite() override { return (max_size - element_count); } + + // /// Returns the maximum capacity of the buffer + // int size() override { return max_size; } + + // not supported + T *address() override { return nullptr; } + + protected: + File *p_file = nullptr; + int write_pos; + int read_pos; + int element_count; + int max_size = INT_MAX; + bool auto_rewind = true; + + void file_seek(int pos) { + if (p_file->position() != pos * sizeof(T)) { + LOGD("file_seek: %d", pos); + if (!p_file->seek(pos * sizeof(T))) { + LOGE("seek %d", pos * sizeof(T)) + } + } + } + + int file_write(const T *data, int count) { + LOGD("file_write: %d", count); + if (p_file == nullptr) return 0; + int to_write = sizeof(T) * count; + int bytes_written = p_file->write((const uint8_t *)data, to_write); + // p_file->flush(); + int elements_written = bytes_written / sizeof(T); + if (bytes_written != to_write) { + LOGE("write: %d -> %d", to_write, bytes_written); + } + return elements_written; + } + + int file_read(T *result, int count) { + LOGD("file_read: %d", count); + size_t result_bytes = p_file->read((uint8_t *)result, sizeof(T) * count); + if (result_bytes != count * sizeof(T)) { + LOGE("readBytes: %d -> %d", (int)sizeof(T) * count, (int)result_bytes); + result = 0; + } + return count; + } +}; + +/** + * @brief A lock free N buffer. If count=2 we create a DoubleBuffer, if + * count=3 a TripleBuffer etc. + * @ingroup buffers + * @author Phil Schatzmann + * @copyright GPLv3 + */ +template +class NBuffer : public BaseBuffer { + public: + NBuffer(int size, int count) { + filled_buffers.resize(count); + avaliable_buffers.resize(count); + + write_buffer_count = 0; + buffer_count = count; + buffer_size = size; + for (int j = 0; j < count; j++) { + avaliable_buffers[j] = new SingleBuffer(size); + if (avaliable_buffers[j] == nullptr) { + LOGE("Not Enough Memory for buffer %d", j); + } + } + } + + virtual ~NBuffer() { + delete actual_write_buffer; + delete actual_read_buffer; + + BaseBuffer *ptr = getNextAvailableBuffer(); + while (ptr != nullptr) { + delete ptr; + ptr = getNextAvailableBuffer(); + } + + ptr = getNextFilledBuffer(); + while (ptr != nullptr) { + delete ptr; + ptr = getNextFilledBuffer(); + } + } + + // reads an entry from the buffer + T read() { + T result = 0; + if (available() > 0) { + result = actual_read_buffer->read(); + } + return result; + } + + // peeks the actual entry from the buffer + T peek() { + T result = 0; + if (available() > 0) { + result = actual_read_buffer->peek(); + } + return result; + } + + // checks if the buffer is full + bool isFull() { return availableForWrite() == 0; } + + // write add an entry to the buffer + bool write(T data) { + bool result = false; + if (actual_write_buffer == nullptr) { + actual_write_buffer = getNextAvailableBuffer(); + } + if (actual_write_buffer != nullptr) { + result = actual_write_buffer->write(data); + // if buffer is full move to next available + if (actual_write_buffer->isFull()) { + addFilledBuffer(actual_write_buffer); + actual_write_buffer = getNextAvailableBuffer(); + } + } else { + // Logger.debug("actual_write_buffer is full"); + } + + if (start_time == 0l) { + start_time = millis(); + } + sample_count++; + + return result; + } + + // determines the available entries for the current read buffer + int available() { + if (actual_read_buffer == nullptr) { + actual_read_buffer = getNextFilledBuffer(); + } + if (actual_read_buffer == nullptr) { + return 0; + } + int result = actual_read_buffer->available(); + if (result == 0) { + // make current read buffer available again + resetCurrent(); + result = + actual_read_buffer == nullptr ? 0 : actual_read_buffer->available(); + } + return result; + } + + // deterMINes the available entries for the write buffer + int availableForWrite() { + if (actual_write_buffer == nullptr) { + actual_write_buffer = getNextAvailableBuffer(); + } + // if we used up all buffers - there is nothing available any more + if (actual_write_buffer == nullptr) { + return 0; + } + // check on actual buffer + if (actual_write_buffer->isFull()) { + // if buffer is full we move it to filled buffers ang get the next + // available + addFilledBuffer(actual_write_buffer); + actual_write_buffer = getNextAvailableBuffer(); + } + return actual_write_buffer->availableForWrite(); + } + + // resets all buffers + void reset() { + TRACED(); + while (actual_read_buffer != nullptr) { + actual_read_buffer->reset(); + addAvailableBuffer(actual_read_buffer); + // get next read buffer + actual_read_buffer = getNextFilledBuffer(); + } + } + + // provides the actual sample rate + unsigned long sampleRate() { + unsigned long run_time = (millis() - start_time); + return run_time == 0 ? 0 : sample_count * 1000 / run_time; + } + + // returns the address of the start of the phsical read buffer + T *address() { + return actual_read_buffer == nullptr ? nullptr + : actual_read_buffer->address(); + } + + // Alternative interface using address: the current buffer has been filled + BaseBuffer &writeEnd() { + if (actual_write_buffer != nullptr) { + actual_write_buffer->setWritePos(buffer_size); + addFilledBuffer(actual_write_buffer); + } + actual_write_buffer = getNextAvailableBuffer(); + return *actual_write_buffer; + } + + // Alternative interface using address: marks actual buffer as processed and + // provides access to next read buffer + BaseBuffer &readEnd() { + // make current read buffer available again + resetCurrent(); + return *actual_read_buffer; + } + + int bufferCountFilled() { + int result = 0; + for (int j = 0; j < buffer_count; j++) { + if (filled_buffers[j] != nullptr) { + result++; + } + } + return result; + } + + int bufferCountEmpty() { + int result = 0; + for (int j = 0; j < buffer_count; j++) { + if (avaliable_buffers[j] != nullptr) { + result++; + } + } + return result; + } + + void setBufferSize(int size) { buffer_size = size; } + + protected: + int buffer_size = 0; + uint16_t buffer_count = 0; + uint16_t write_buffer_count = 0; + BaseBuffer *actual_read_buffer = nullptr; + BaseBuffer *actual_write_buffer = nullptr; + Vector *> avaliable_buffers; + Vector *> filled_buffers; + unsigned long start_time = 0; + unsigned long sample_count = 0; + + // empty constructor only allowed by subclass + NBuffer() = default; + + void resetCurrent() { + if (actual_read_buffer != nullptr) { + actual_read_buffer->reset(); + addAvailableBuffer(actual_read_buffer); + } + // get next read buffer + actual_read_buffer = getNextFilledBuffer(); + } + + virtual BaseBuffer *getNextAvailableBuffer() { + BaseBuffer *result = nullptr; + for (int j = 0; j < buffer_count; j++) { + result = avaliable_buffers[j]; + if (result != nullptr) { + avaliable_buffers[j] = nullptr; + break; + } + } + return result; + } + + virtual bool addAvailableBuffer(BaseBuffer *buffer) { + bool result = false; + for (int j = 0; j < buffer_count; j++) { + if (avaliable_buffers[j] == nullptr) { + avaliable_buffers[j] = buffer; + result = true; + break; + } + } + return result; + } + + virtual BaseBuffer *getNextFilledBuffer() { + BaseBuffer *result = nullptr; + if (write_buffer_count > 0) { + // get oldest entry + result = filled_buffers[0]; + // move data by 1 entry to the left + for (int j = 0; j < write_buffer_count - 1; j++) + filled_buffers[j] = filled_buffers[j + 1]; + // clear last enty + filled_buffers[write_buffer_count - 1] = nullptr; + write_buffer_count--; + } + return result; + } + + virtual bool addFilledBuffer(BaseBuffer *buffer) { + bool result = false; + if (write_buffer_count < buffer_count) { + filled_buffers[write_buffer_count++] = buffer; + result = true; + } + return result; + } +}; + +/** + * @brief Class which is usfull ot provide incremental data access e.g. for + * EdgeImpulse which request data with an offset and length starting from 0 up + * to the buffer length, restarting at 0 again. + * @ingroup buffers + * @author Phil Schatzmann + * @copyright GPLv3 + */ + +template +class BufferedArray { + public: + BufferedArray(Stream &input, int len) { + LOGI("BufferedArray(%d)", len); + array.resize(len); + p_stream = &input; + } + // access values, the offset and length are specified in samples of type + int16_t *getValues(size_t offset, size_t length) { + LOGD("getValues(%d,%d) - max %d", offset, length, array.size()); + if (offset == 0) { + // we restart at the beginning + last_end = 0; + actual_end = length; + } else { + // if first position is at end we do not want to read the full buffer + last_end = actual_end >= 0 ? actual_end : offset; + // increase actual end if bigger then old + actual_end = offset + length > actual_end ? offset + length : actual_end; + } + int size = actual_end - last_end; + if (size > 0) { + LOGD("readBytes(%d,%d)", last_end, size); + assert(last_end + size <= array.size()); + p_stream->readBytes((uint8_t *)(&array[last_end]), size * 2); + } + assert(offset < actual_end); + return &array[offset]; + } + + protected: + int actual_end = -1; + int last_end = 0; + Vector array; + Stream *p_stream = nullptr; +}; + +} // namespace audio_tools diff --git a/src/AudioTools/Communication/AudioFEC.h b/src/AudioTools/Communication/AudioFEC.h deleted file mode 100644 index 3a0b917822..0000000000 --- a/src/AudioTools/Communication/AudioFEC.h +++ /dev/null @@ -1,4 +0,0 @@ -#pragma once -// Forward Error Corrections -#include "AudioTools/Communication/ReedSolomonFEC.h" -#include "AudioTools/Communication/HammingFEC.h" \ No newline at end of file diff --git a/src/AudioTools/Communication/AudioLoRa.h b/src/AudioTools/Communication/AudioLoRa.h deleted file mode 100644 index c374698efe..0000000000 --- a/src/AudioTools/Communication/AudioLoRa.h +++ /dev/null @@ -1,138 +0,0 @@ -#pragma once - -#include "AudioTools/CoreAudio/Buffers.h" -#include "AudioTools/CoreAudio/BaseStream.h" -#include "LoRa.h" - -// define the default pins used by the transceiver module -#define ss 8 -#define rst 12 -#define dio0 14 - -namespace audio_tools { - -/** - * @brief LoRa Audio Configuration with default values maximised for - * speed. - * @author Phil Schatzmann - * @copyright GPLv3 - - Heltec LoRa 32 Lora Pins: - NSS: 8 - SCK: 9 - MOSI: 10 - MISO: 11 - RST: 12 - BUSY: 13 - DIO1: 14 - - */ - -struct AudioLoRaConfig : public AudioInfo { - int32_t spi_speed = 8E6; - int max_size = 200; - int frequency = 868E6; // (433E6, 868E6, 915E6) - int sync_word = 0xF3; - int tx_power = 20; // 2-20; - int spreading_factor = 12; // 6-12 - int signal_bandwidth = - 7.8E3; // 7.8E3, 10.4E3, 15.6E3, 20.8E3, 31.25E3, 41.7E3, - // 62.5E3, 125E3, 250E3, and 500E3. - int pin_ss = ss; - int pin_rst = rst; - int pin_dio0 = dio0; - bool process_audio_info = true; -}; - -/** - * @brief LoRa Audio Sending and Receiving - * @author Phil Schatzmann - * @copyright GPLv3 - */ -class AudioLoRa : public AudioStream { - public: - AudioLoRaConfig defaultConfig() { - AudioLoRaConfig rc; - return rc; - } - - void setAudioInfo(AudioInfo info) { - cfg.sample_rate = info.sample_rate; - cfg.channels = info.channels; - cfg.bits_per_sample = info.bits_per_sample; - AudioStream::setAudioInfo(info); - } - - bool begin(AudioLoRaConfig config) { - cfg = config; - AudioStream::setAudioInfo(config); - return begin(); - } - - bool begin() { - TRACEI(); - buffer.resize(cfg.max_size); - LoRa.setSignalBandwidth(cfg.signal_bandwidth); - LoRa.setSpreadingFactor(cfg.spreading_factor); - LoRa.setTxPower(cfg.tx_power); - LoRa.setSPIFrequency(cfg.spi_speed); - LoRa.setPins(cfg.pin_ss, cfg.pin_rst, cfg.pin_dio0); - LoRa.setSyncWord(cfg.sync_word); - bool rc = LoRa.begin(cfg.frequency); - if (cfg.process_audio_info) { - writeAudioInfo(); - } - return rc; - } - - void end() { LoRa.end(); } - - size_t readBytes(uint8_t* data, size_t len) { - TRACEI(); - size_t packetSize = LoRa.parsePacket(); - if (cfg.process_audio_info && packetSize == sizeof(AudioInfo)) { - readAudioInfo(); - packetSize = LoRa.parsePacket(); - } - int toRead = min(len, packetSize); - int read = LoRa.readBytes(data, toRead); - return read; - } - - int available() { return cfg.max_size; } - - int availableForWrite() { return cfg.max_size; } - - size_t write(const uint8_t* data, size_t len) { - TRACEI(); - for (int j = 0; j < len; j++) { - buffer.write(data[j]); - if (buffer.isFull()) { - LoRa.beginPacket(); - LoRa.write(buffer.data(), buffer.available()); - LoRa.endPacket(); - buffer.clear(); - } - } - return len; - } - - protected: - AudioLoRaConfig cfg; - SingleBuffer buffer; - - void readAudioInfo() { - AudioInfo tmp; - int read = LoRa.readBytes((uint8_t*)&tmp, sizeof(AudioInfo)); - setAudioInfo(tmp); - } - - void writeAudioInfo() { - LoRa.beginPacket(); - AudioInfo ai = audioInfo(); - LoRa.write((uint8_t*)&ai, sizeof(ai)); - LoRa.endPacket(); - } -}; - -} // namespace audio_tools \ No newline at end of file diff --git a/src/AudioTools/Communication/AudioSync.h b/src/AudioTools/Communication/AudioSync.h deleted file mode 100644 index eb8edaf7c4..0000000000 --- a/src/AudioTools/Communication/AudioSync.h +++ /dev/null @@ -1,234 +0,0 @@ - -#pragma once -#include "AudioTools/CoreAudio/AudioStreams.h" -#include "AudioTools/CoreAudio/AudioOutput.h" -#include "AudioTools/CoreAudio/Buffers.h" -#include "AudioTools/AudioCodecs/AudioEncoded.h" - - -namespace audio_tools { - - -enum class RecordType : uint8_t { Undefined, Begin, Send, Receive, End }; -enum class AudioType : uint8_t { PCM, MP3, AAC, WAV, ADPC }; -enum class TransmitRole : uint8_t { Sender, Receiver }; - -/// Common Header for all records -struct AudioHeader { - AudioHeader() = default; - uint8_t app = 123; - RecordType rec = RecordType::Undefined; - uint16_t seq = 0; - // record counter - void increment() { - static uint16_t static_count = 0; - seq = static_count++; - } -}; - -/// Protocal Record To Start -struct AudioDataBegin : public AudioHeader { - AudioDataBegin() { rec = RecordType::Begin; } - AudioInfo info; - AudioType type = AudioType::PCM; -}; - -/// Protocol Record for Data -struct AudioSendData : public AudioHeader { - AudioSendData() { - rec = RecordType::Send; - } - uint16_t size = 0; -}; - -/// Protocol Record for Request -struct AudioConfirmDataToReceive : public AudioHeader { - AudioConfirmDataToReceive() { rec = RecordType::Receive; } - uint16_t size = 0; -}; - -/// Protocol Record for End -struct AudioDataEnd : public AudioHeader { - AudioDataEnd() { rec = RecordType::End; } -}; - - -/** - * @brief Audio Writer which is synchronizing the amount of data - * that can be processed with the AudioReceiver - * @author Phil Schatzmann - * @copyright GPLv3 - */ -class AudioSyncWriter : public AudioOutput { - public: - AudioSyncWriter(Stream &dest) { p_dest = &dest; } - - bool begin(AudioInfo &info, AudioType type) { - is_sync = true; - AudioDataBegin begin; - begin.info = info; - begin.type = type; - begin.increment(); - int write_len = sizeof(begin); - int len = p_dest->write((const uint8_t *)&begin, write_len); - return len == write_len; - } - - size_t write(const uint8_t *data, size_t len) override { - int written_len = 0; - int open_len = len; - AudioSendData send; - while (written_len < len) { - int available_to_write = waitForRequest(); - size_t to_write_len = DEFAULT_BUFFER_SIZE; - to_write_len = min(open_len, available_to_write); - send.increment(); - send.size = to_write_len; - p_dest->write((const uint8_t *)&send, sizeof(send)); - int w = p_dest->write(data + written_len, to_write_len); - written_len += w; - open_len -= w; - } - return written_len; - } - - int availableForWrite() override { return available_to_write; } - - void end() { - AudioDataEnd end; - end.increment(); - p_dest->write((const uint8_t *)&end, sizeof(end)); - } - - protected: - Stream *p_dest; - int available_to_write = 1024; - bool is_sync; - - /// Waits for the data to be available - void waitFor(int size) { - while (p_dest->available() < size) { - delay(10); - } - } - - int waitForRequest() { - AudioConfirmDataToReceive rcv; - size_t rcv_len = sizeof(rcv); - waitFor(rcv_len); - p_dest->readBytes((uint8_t *)&rcv, rcv_len); - return rcv.size; - } -}; - -/** - * @brief Receving Audio Data over the wire and requesting for more data when - * done to synchronize the processing with the sender. The audio data is - * processed by the EncodedAudioStream; If you have multiple readers, only one - * receiver should be used as confirmer! - * @author Phil Schatzmann - * @copyright GPLv3 - */ -class AudioSyncReader : public AudioStream { - public: - AudioSyncReader(Stream &in, EncodedAudioStream &out, - bool isConfirmer = true) { - p_in = ∈ - p_out = &out; - is_confirmer = isConfirmer; - } - - size_t copy() { - int processed = 0; - int header_size = sizeof(header); - waitFor(header_size); - readBytes((uint8_t *)&header, header_size); - - switch (header.rec) { - case RecordType::Begin: - audioDataBegin(); - break; - case RecordType::End: - audioDataEnd(); - break; - case RecordType::Send: - processed = receiveData(); - break; - } - return processed; - } - - protected: - Stream *p_in; - EncodedAudioStream *p_out; - AudioConfirmDataToReceive req; - AudioHeader header; - AudioDataBegin begin; - size_t available = 0; // initial value - bool is_started = false; - bool is_confirmer; - int last_seq = 0; - - /// Starts the processing - void audioDataBegin() { - readProtocol(&begin, sizeof(begin)); - p_out->begin(); - p_out->setAudioInfo(begin.info); - requestData(); - } - - /// Ends the processing - void audioDataEnd() { - AudioDataEnd end; - readProtocol(&end, sizeof(end)); - p_out->end(); - } - - // Receives audio data - int receiveData() { - AudioSendData data; - readProtocol(&data, sizeof(data)); - available = data.size; - // receive and process audio data - waitFor(available); - int max_gap = 10; - if (data.seq > last_seq || - (data.seq < max_gap && last_seq >= (32767 - max_gap))) { - uint8_t buffer[available]; - p_in->readBytes((uint8_t *)buffer, available); - p_out->write((const uint8_t *)buffer, available); - // only one reader should be used as confirmer - if (is_confirmer) { - requestData(); - } - last_seq = data.seq; - } - return available; - } - - /// Waits for the data to be available - void waitFor(int size) { - while (p_in->available() < size) { - delay(10); - } - } - - /// Request new data from writer - void requestData() { - req.size = p_out->availableForWrite(); - req.increment(); - p_in->write((const uint8_t *)&req, sizeof(req)); - p_in->flush(); - } - - /// Reads the protocol record - void readProtocol(AudioHeader *data, int len) { - static const int header_size = sizeof(header); - memcpy(data, &header, header_size); - int read_size = len - header_size; - waitFor(read_size); - readBytes((uint8_t *)data + header_size, read_size); - } -}; - -} // namespace audio_tools diff --git a/src/AudioTools/Communication/ESPNowStream.h b/src/AudioTools/Communication/ESPNowStream.h deleted file mode 100644 index 5cf9cd02f9..0000000000 --- a/src/AudioTools/Communication/ESPNowStream.h +++ /dev/null @@ -1,388 +0,0 @@ -#pragma once -#include -#include -#include - -#include "AudioTools/CoreAudio/AudioBasic/StrView.h" -#include "AudioTools/CoreAudio/BaseStream.h" -#include "AudioTools/Concurrency/RTOS.h" - -namespace audio_tools { - -// forward declarations -class ESPNowStream; -static ESPNowStream *ESPNowStreamSelf = nullptr; - -/** - * @brief Configuration for ESP-NOW protocolö.W - * @author Phil Schatzmann - * @copyright GPLv3 - */ -struct ESPNowStreamConfig { - wifi_mode_t wifi_mode = WIFI_STA; - const char *mac_address = nullptr; - int channel = 0; - const char *ssid = nullptr; - const char *password = nullptr; - bool use_send_ack = true; // we wait for - uint16_t delay_after_failed_write_ms = 2000; - uint16_t buffer_size = ESP_NOW_MAX_DATA_LEN; - uint16_t buffer_count = 400; - int write_retry_count = -1; // -1 endless -#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 0, 0) - void (*recveive_cb)(const esp_now_recv_info *info, const uint8_t *data, - int data_len) = nullptr; -#else - void (*recveive_cb)(const uint8_t *mac_addr, const uint8_t *data, - int data_len) = nullptr; -#endif - /// to encrypt set primary_master_key and local_master_key to 16 byte strings - const char *primary_master_key = nullptr; - const char *local_master_key = nullptr; - /// esp-now bit rate - wifi_phy_rate_t rate = WIFI_PHY_RATE_2M_S; -}; - -/** - * @brief ESPNow as Arduino Stream. - * @ingroup communications - * @author Phil Schatzmann - * @copyright GPLv3 - */ -class ESPNowStream : public BaseStream { - public: - ESPNowStream() { ESPNowStreamSelf = this; }; - - ~ESPNowStream() { - if (xSemaphore != nullptr) vSemaphoreDelete(xSemaphore); - } - - ESPNowStreamConfig defaultConfig() { - ESPNowStreamConfig result; - return result; - } - - /// Returns the mac address of the current ESP32 - const char *macAddress() { - static const char *result = WiFi.macAddress().c_str(); - return result; - } - - /// Defines an alternative send callback - void setSendCallback(esp_now_send_cb_t cb) { send = cb; } - - /// Defines the Receive Callback - Deactivates the readBytes and available() - /// methods! - void setReceiveCallback(esp_now_recv_cb_t cb) { receive = cb; } - - /// Initialization of ESPNow - bool begin() { return begin(cfg); } - - /// Initialization of ESPNow incl WIFI - bool begin(ESPNowStreamConfig cfg) { - this->cfg = cfg; - WiFi.mode(cfg.wifi_mode); - // set mac address - if (cfg.mac_address != nullptr) { - LOGI("setting mac %s", cfg.mac_address); - byte mac[ESP_NOW_KEY_LEN]; - str2mac(cfg.mac_address, mac); - if (esp_wifi_set_mac((wifi_interface_t)getInterface(), mac) != ESP_OK) { - LOGE("Could not set mac address"); - return false; - } - delay(500); // On some boards calling macAddress to early leads to a race - // condition. - // checking if address has been updated - const char *addr = macAddress(); - if (strcmp(addr, cfg.mac_address) != 0) { - LOGE("Wrong mac address: %s", addr); - return false; - } - } - - if (WiFi.status() != WL_CONNECTED && cfg.ssid != nullptr && - cfg.password != nullptr) { - WiFi.begin(cfg.ssid, cfg.password); - while (WiFi.status() != WL_CONNECTED) { - Serial.print('.'); - delay(1000); - } - } - -#if ESP_IDF_VERSION < ESP_IDF_VERSION_VAL(5, 0, 0) - LOGI("Setting ESP-NEW rate"); - if (esp_wifi_config_espnow_rate(getInterface(), cfg.rate) != ESP_OK) { - LOGW("Could not set rate"); - } -#endif - - Serial.println(); - Serial.print("mac: "); - Serial.println(WiFi.macAddress()); - return setup(); - } - - /// DeInitialization - void end() { - if (is_init){ - if (esp_now_deinit() != ESP_OK) { - LOGE("esp_now_deinit"); - } - if (buffer.size()>0) buffer.resize(0); - is_init = false; - } - } - - /// Adds a peer to which we can send info or from which we can receive info - bool addPeer(esp_now_peer_info_t &peer) { - if (!is_init) { - LOGE("addPeer before begin"); - return false; - } - esp_err_t result = esp_now_add_peer(&peer); - if (result == ESP_OK) { - LOGI("addPeer: %s", mac2str(peer.peer_addr)); - } else { - LOGE("addPeer: %d", result); - } - return result == ESP_OK; - } - - /// Adds an array of - template - bool addPeers(const char *(&array)[size]) { - bool result = true; - for (int j = 0; j < size; j++) { - const char *peer = array[j]; - if (peer != nullptr) { - if (!addPeer(peer)) { - result = false; - } - } - } - return result; - } - - /// Adds a peer to which we can send info or from which we can receive info - bool addPeer(const char *address) { - esp_now_peer_info_t peer; - peer.channel = cfg.channel; - peer.ifidx = getInterface(); - peer.encrypt = false; - - if (StrView(address).equals(cfg.mac_address)) { - LOGW("Did not add own address as peer"); - return true; - } - - if (isEncrypted()) { - peer.encrypt = true; - strncpy((char *)peer.lmk, cfg.local_master_key, 16); - } - - if (!str2mac(address, peer.peer_addr)) { - LOGE("addPeer - Invalid address: %s", address); - return false; - } - return addPeer(peer); - } - - /// Writes the data - sends it to all the peers - size_t write(const uint8_t *data, size_t len) override { - setupSemaphore(); - int open = len; - size_t result = 0; - int retry_count = cfg.write_retry_count; - while (open > 0) { - resetAvailableToWrite(); - // wait for confirmation - if (cfg.use_send_ack) { - xSemaphoreTake(xSemaphore, portMAX_DELAY); - } - size_t send_len = min(open, ESP_NOW_MAX_DATA_LEN); - esp_err_t rc = esp_now_send(nullptr, data + result, send_len); - // check status - if (rc == ESP_OK) { - open -= send_len; - result += send_len; - retry_count = 0; - } else { - LOGW("Write failed - retrying again"); - retry_count++; - if (retry_count-- < 0 ) { - LOGE("Write error after %d retries", cfg.write_retry_count); - // break loop - return 0; - } - delay(cfg.delay_after_failed_write_ms); - } - } - return result; - } - - /// Reeds the data from the peers - size_t readBytes(uint8_t *data, size_t len) override { - if (buffer.size()==0) return 0; - return buffer.readArray(data, len); - } - - int available() override { - if (!buffer) return 0; - return buffer.size() == 0? 0 : buffer.available(); - } - - int availableForWrite() override { - if (!buffer) return 0; - return cfg.use_send_ack ? available_to_write : cfg.buffer_size; - } - - /// provides how much the receive buffer is filled (in percent) - float getBufferPercent() { - int size = buffer.size(); - // prevent div by 0 - if (size==0) return 0.0; - // calculate percent - return 100.0 * buffer.available() / size; - } - - protected: - ESPNowStreamConfig cfg; - BufferRTOS buffer{0}; - esp_now_recv_cb_t receive = default_recv_cb; - esp_now_send_cb_t send = default_send_cb; - volatile size_t available_to_write = 0; - bool is_init = false; - SemaphoreHandle_t xSemaphore = nullptr; - - inline void setupSemaphore() { - // use semaphore for confirmations - if (cfg.use_send_ack && xSemaphore==nullptr) { - xSemaphore = xSemaphoreCreateBinary(); - xSemaphoreGive(xSemaphore); - } - } - - inline void setupReceiveBuffer() { - // setup receive buffer - if (!buffer) { - LOGI("setupReceiveBuffer: %d", cfg.buffer_size * cfg.buffer_count); - buffer.resize(cfg.buffer_size * cfg.buffer_count); - } - } - - inline void resetAvailableToWrite() { - if (cfg.use_send_ack) { - available_to_write = 0; - } - } - - bool isEncrypted() { - return cfg.primary_master_key != nullptr && cfg.local_master_key != nullptr; - } - - wifi_interface_t getInterface() { - // define wifi_interface_t - wifi_interface_t result; - switch (cfg.wifi_mode) { - case WIFI_STA: - result = (wifi_interface_t)ESP_IF_WIFI_STA; - break; - case WIFI_AP: - result = (wifi_interface_t)ESP_IF_WIFI_AP; - break; - default: - result = (wifi_interface_t)0; - break; - } - return result; - } - - /// Initialization - bool setup() { - esp_err_t result = esp_now_init(); - if (result == ESP_OK) { - LOGI("esp_now_init: %s", macAddress()); - } else { - LOGE("esp_now_init: %d", result); - } - - // encryption is optional - if (isEncrypted()) { - esp_now_set_pmk((uint8_t *)cfg.primary_master_key); - } - - if (cfg.recveive_cb != nullptr) { - esp_now_register_recv_cb(cfg.recveive_cb); - } else { - esp_now_register_recv_cb(receive); - } - if (cfg.use_send_ack) { - esp_now_register_send_cb(send); - } - available_to_write = cfg.buffer_size; - is_init = result == ESP_OK; - return is_init; - } - - bool str2mac(const char *mac, uint8_t *values) { - sscanf(mac, "%hhx:%hhx:%hhx:%hhx:%hhx:%hhx", &values[0], &values[1], - &values[2], &values[3], &values[4], &values[5]); - return strlen(mac) == 17; - } - - const char *mac2str(const uint8_t *array) { - static char macStr[18]; - memset(macStr, 0, 18); - snprintf(macStr, 18, "%02x:%02x:%02x:%02x:%02x:%02x", array[0], array[1], - array[2], array[3], array[4], array[5]); - return (const char *)macStr; - } - - static int bufferAvailableForWrite() { - return ESPNowStreamSelf->buffer.availableForWrite(); - } - -#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 0, 0) - static void default_recv_cb(const esp_now_recv_info *info, - const uint8_t *data, int data_len) -#else - static void default_recv_cb(const uint8_t *mac_addr, const uint8_t *data, - int data_len) -#endif - { - LOGD("rec_cb: %d", data_len); - // make sure that the receive buffer is available - moved from begin to make - // sure that it is only allocated when needed - ESPNowStreamSelf->setupReceiveBuffer(); - // blocking write - size_t result = ESPNowStreamSelf->buffer.writeArray(data, data_len); - if (result != data_len) { - LOGE("writeArray %d -> %d", data_len, result); - } - } - - static void default_send_cb(const uint8_t *mac_addr, - esp_now_send_status_t status) { - static uint8_t first_mac[ESP_NOW_KEY_LEN] = {0}; - // we use the first confirming mac_addr for further confirmations and ignore - // others - if (first_mac[0] == 0) { - strncpy((char *)first_mac, (char *)mac_addr, ESP_NOW_KEY_LEN); - } - LOGD("default_send_cb - %s -> %s", ESPNowStreamSelf->mac2str(mac_addr), - status == ESP_NOW_SEND_SUCCESS ? "+" : "-"); - - // ignore others - if (strncmp((char *)mac_addr, (char *)first_mac, ESP_NOW_KEY_LEN) == 0) { - ESPNowStreamSelf->available_to_write = ESPNowStreamSelf->cfg.buffer_size; - if (status == ESP_NOW_SEND_SUCCESS) { - xSemaphoreGive(ESPNowStreamSelf->xSemaphore); - } else { - LOGE("Send Error!"); - } - } - } -}; - -} // namespace audio_tools diff --git a/src/AudioTools/Communication/FEC/ReedSolomon/LICENSE b/src/AudioTools/Communication/FEC/ReedSolomon/LICENSE deleted file mode 100644 index aaffd33cde..0000000000 --- a/src/AudioTools/Communication/FEC/ReedSolomon/LICENSE +++ /dev/null @@ -1,21 +0,0 @@ -Copyright © 2015 Mike Lubinets, github.com/mersinvald - -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. diff --git a/src/AudioTools/Communication/FEC/ReedSolomon/README.md b/src/AudioTools/Communication/FEC/ReedSolomon/README.md deleted file mode 100644 index 3ee64453f8..0000000000 --- a/src/AudioTools/Communication/FEC/ReedSolomon/README.md +++ /dev/null @@ -1,61 +0,0 @@ -# Reed-Solomon -Reed Solomon BCH encoder and decoder library - -## Overview - -This RS implementation was designed for embedded purposes, so all the memory allocations performed on the stack.
-If somebody wants to reimplement memory management with heap usage, pull requests are welcome - -## Getting the source - -If you want only Reed-Solomon code, just clone repository.
-If you want to get tests and examples also, do -``` -git clone --recursive git@github.com:mersinvald/Reed-Solomon.git -``` - -## Build - -There is no need in building RS library, cause all the implementation is in headers.
-To build tests and examples simply run make in the folder with cloned repo and executables will emerge in the -./build folder - -## Usage - -All the Reed-Solomon code is in folder **include**, you just need to include header rs.hpp - -Template class ReedSolomon accepts two template arguments: message length and ecc length.
-Simple example:
-``` - char message[] = "Some very important message ought to be delivered"; - const int msglen = sizeof(message); - const int ecclen = 8; - - char repaired[msglen]; - char encoded[msglen + ecclen]; - - - RS::ReedSolomon rs; - - rs.Encode(message, encoded); - - // Corrupting first 8 bytes of message (any 4 bytes can be repaired, no more) - for(uint i = 0; i < ecclen / 2; i++) { - encoded[i] = 'E'; - } - - rs.Decode(encoded, repaired); - - std::cout << "Original: " << message << std::endl; - std::cout << "Corrupted: " << encoded << std::endl; - std::cout << "Repaired: " << repaired << std::endl; - - std::cout << ((memcmp(message, repaired, msglen) == 0) ? "SUCCESS" : "FAILURE") << std::endl; -``` - -## Regards - -Huge thanks to authors of [wikiversity page about Reed-Solomon BCH](https://en.wikiversity.org/wiki/Reed–Solomon_codes_for_coders) -## Related projects - -[Arduino Reed-Solomon Forward Error Correction library](https://github.com/simonyipeter/Arduino-FEC) diff --git a/src/AudioTools/Communication/FEC/ReedSolomon/gf.hpp b/src/AudioTools/Communication/FEC/ReedSolomon/gf.hpp deleted file mode 100644 index 6a25693945..0000000000 --- a/src/AudioTools/Communication/FEC/ReedSolomon/gf.hpp +++ /dev/null @@ -1,223 +0,0 @@ -/* Author: Mike Lubinets (aka mersinvald) - * Date: 29.12.15 - * - * See LICENSE */ - -#ifndef GF_H -#define GF_H -#include -#include -#include "poly.hpp" - -#if !defined DEBUG && !defined __CC_ARM -#include -#else -#define assert(dummy) -#endif - -/// @brief AudioTools internal: Reed-Solomon -namespace RS { - -namespace gf { - - -/* GF tables pre-calculated for 0x11d primitive polynomial */ - -const uint8_t exp[255] = { - 0x1, 0x2, 0x4, 0x8, 0x10, 0x20, 0x40, 0x80, 0x1d, 0x3a, 0x74, 0xe8, 0xcd, 0x87, 0x13, 0x26, 0x4c, - 0x98, 0x2d, 0x5a, 0xb4, 0x75, 0xea, 0xc9, 0x8f, 0x3, 0x6, 0xc, 0x18, 0x30, 0x60, 0xc0, 0x9d, - 0x27, 0x4e, 0x9c, 0x25, 0x4a, 0x94, 0x35, 0x6a, 0xd4, 0xb5, 0x77, 0xee, 0xc1, 0x9f, 0x23, 0x46, - 0x8c, 0x5, 0xa, 0x14, 0x28, 0x50, 0xa0, 0x5d, 0xba, 0x69, 0xd2, 0xb9, 0x6f, 0xde, 0xa1, 0x5f, - 0xbe, 0x61, 0xc2, 0x99, 0x2f, 0x5e, 0xbc, 0x65, 0xca, 0x89, 0xf, 0x1e, 0x3c, 0x78, 0xf0, 0xfd, - 0xe7, 0xd3, 0xbb, 0x6b, 0xd6, 0xb1, 0x7f, 0xfe, 0xe1, 0xdf, 0xa3, 0x5b, 0xb6, 0x71, 0xe2, 0xd9, - 0xaf, 0x43, 0x86, 0x11, 0x22, 0x44, 0x88, 0xd, 0x1a, 0x34, 0x68, 0xd0, 0xbd, 0x67, 0xce, 0x81, - 0x1f, 0x3e, 0x7c, 0xf8, 0xed, 0xc7, 0x93, 0x3b, 0x76, 0xec, 0xc5, 0x97, 0x33, 0x66, 0xcc, 0x85, - 0x17, 0x2e, 0x5c, 0xb8, 0x6d, 0xda, 0xa9, 0x4f, 0x9e, 0x21, 0x42, 0x84, 0x15, 0x2a, 0x54, 0xa8, - 0x4d, 0x9a, 0x29, 0x52, 0xa4, 0x55, 0xaa, 0x49, 0x92, 0x39, 0x72, 0xe4, 0xd5, 0xb7, 0x73, 0xe6, - 0xd1, 0xbf, 0x63, 0xc6, 0x91, 0x3f, 0x7e, 0xfc, 0xe5, 0xd7, 0xb3, 0x7b, 0xf6, 0xf1, 0xff, 0xe3, - 0xdb, 0xab, 0x4b, 0x96, 0x31, 0x62, 0xc4, 0x95, 0x37, 0x6e, 0xdc, 0xa5, 0x57, 0xae, 0x41, 0x82, - 0x19, 0x32, 0x64, 0xc8, 0x8d, 0x7, 0xe, 0x1c, 0x38, 0x70, 0xe0, 0xdd, 0xa7, 0x53, 0xa6, 0x51, - 0xa2, 0x59, 0xb2, 0x79, 0xf2, 0xf9, 0xef, 0xc3, 0x9b, 0x2b, 0x56, 0xac, 0x45, 0x8a, 0x9, 0x12, - 0x24, 0x48, 0x90, 0x3d, 0x7a, 0xf4, 0xf5, 0xf7, 0xf3, 0xfb, 0xeb, 0xcb, 0x8b, 0xb, 0x16, 0x2c, - 0x58, 0xb0, 0x7d, 0xfa, 0xe9, 0xcf, 0x83, 0x1b, 0x36, 0x6c, 0xd8, 0xad, 0x47, 0x8e -}; - -const uint8_t log[256] = { - 0x0, 0x0, 0x1, 0x19, 0x2, 0x32, 0x1a, 0xc6, 0x3, 0xdf, 0x33, 0xee, 0x1b, 0x68, 0xc7, 0x4b, 0x4, - 0x64, 0xe0, 0xe, 0x34, 0x8d, 0xef, 0x81, 0x1c, 0xc1, 0x69, 0xf8, 0xc8, 0x8, 0x4c, 0x71, 0x5, - 0x8a, 0x65, 0x2f, 0xe1, 0x24, 0xf, 0x21, 0x35, 0x93, 0x8e, 0xda, 0xf0, 0x12, 0x82, 0x45, 0x1d, - 0xb5, 0xc2, 0x7d, 0x6a, 0x27, 0xf9, 0xb9, 0xc9, 0x9a, 0x9, 0x78, 0x4d, 0xe4, 0x72, 0xa6, 0x6, - 0xbf, 0x8b, 0x62, 0x66, 0xdd, 0x30, 0xfd, 0xe2, 0x98, 0x25, 0xb3, 0x10, 0x91, 0x22, 0x88, 0x36, - 0xd0, 0x94, 0xce, 0x8f, 0x96, 0xdb, 0xbd, 0xf1, 0xd2, 0x13, 0x5c, 0x83, 0x38, 0x46, 0x40, 0x1e, - 0x42, 0xb6, 0xa3, 0xc3, 0x48, 0x7e, 0x6e, 0x6b, 0x3a, 0x28, 0x54, 0xfa, 0x85, 0xba, 0x3d, 0xca, - 0x5e, 0x9b, 0x9f, 0xa, 0x15, 0x79, 0x2b, 0x4e, 0xd4, 0xe5, 0xac, 0x73, 0xf3, 0xa7, 0x57, 0x7, - 0x70, 0xc0, 0xf7, 0x8c, 0x80, 0x63, 0xd, 0x67, 0x4a, 0xde, 0xed, 0x31, 0xc5, 0xfe, 0x18, 0xe3, - 0xa5, 0x99, 0x77, 0x26, 0xb8, 0xb4, 0x7c, 0x11, 0x44, 0x92, 0xd9, 0x23, 0x20, 0x89, 0x2e, 0x37, - 0x3f, 0xd1, 0x5b, 0x95, 0xbc, 0xcf, 0xcd, 0x90, 0x87, 0x97, 0xb2, 0xdc, 0xfc, 0xbe, 0x61, 0xf2, - 0x56, 0xd3, 0xab, 0x14, 0x2a, 0x5d, 0x9e, 0x84, 0x3c, 0x39, 0x53, 0x47, 0x6d, 0x41, 0xa2, 0x1f, - 0x2d, 0x43, 0xd8, 0xb7, 0x7b, 0xa4, 0x76, 0xc4, 0x17, 0x49, 0xec, 0x7f, 0xc, 0x6f, 0xf6, 0x6c, - 0xa1, 0x3b, 0x52, 0x29, 0x9d, 0x55, 0xaa, 0xfb, 0x60, 0x86, 0xb1, 0xbb, 0xcc, 0x3e, 0x5a, 0xcb, - 0x59, 0x5f, 0xb0, 0x9c, 0xa9, 0xa0, 0x51, 0xb, 0xf5, 0x16, 0xeb, 0x7a, 0x75, 0x2c, 0xd7, 0x4f, - 0xae, 0xd5, 0xe9, 0xe6, 0xe7, 0xad, 0xe8, 0x74, 0xd6, 0xf4, 0xea, 0xa8, 0x50, 0x58, 0xaf -}; - - - -/* ################################ - * # OPERATIONS OVER GALOIS FIELDS # - * ################################ */ - -/* @brief Addition in Galois Fields - * @param x - left operand - * @param y - right operand - * @return x + y */ -inline uint8_t add(uint8_t x, uint8_t y) { - return x^y; -} - -/* ##### GF subtraction ###### */ -/* @brief Subtraction in Galois Fields - * @param x - left operand - * @param y - right operand - * @return x - y */ -inline uint8_t sub(uint8_t x, uint8_t y) { - return x^y; -} - -/* @brief Multiplication in Galois Fields - * @param x - left operand - * @param y - right operand - * @return x * y */ -inline uint8_t mul(uint16_t x, uint16_t y){ - if (x == 0 || y == 0) - return 0; - return exp[(log[x] + log[y]) % 255]; -} - -/* @brief Division in Galois Fields - * @param x - dividend - * @param y - divisor - * @return x / y */ -inline uint8_t div(uint8_t x, uint8_t y){ - assert(y != 0); - if(x == 0) return 0; - return exp[(log[x] + 255 - log[y]) % 255]; -} - -/* @brief X in power Y w - * @param x - operand - * @param power - power - * @return x^power */ -inline uint8_t pow(uint8_t x, intmax_t power){ - intmax_t i = log[x]; - i *= power; - i %= 255; - if(i < 0) i = i + 255; - return exp[i % 255]; -} - -/* @brief Inversion in Galois Fields - * @param x - number - * @return inversion of x */ -inline uint8_t inverse(uint8_t x){ - return exp[(255 - log[x]) % 255]; /* == div(1, x); */ -} - -/* ########################## - * # POLYNOMIALS OPERATIONS # - * ########################## */ - -/* @brief Multiplication polynomial by scalar - * @param &p - source polynomial - * @param &newp - destination polynomial - * @param x - scalar */ -inline void -poly_scale(const Poly *p, Poly *newp, uint16_t x) { - newp->length = p->length; - for(uint16_t i = 0; i < p->length; i++){ - newp->at(i) = mul(p->at(i), x); - } -} - -/* @brief Addition of two polynomials - * @param &p - right operand polynomial - * @param &q - left operand polynomial - * @param &newp - destination polynomial */ -inline void -poly_add(const Poly *p, const Poly *q, Poly *newp) { - newp->length = poly_max(p->length, q->length); - memset(newp->ptr(), 0, newp->length * sizeof(uint8_t)); - - for(uint8_t i = 0; i < p->length; i++){ - newp->at(i + newp->length - p->length) = p->at(i); - } - - for(uint8_t i = 0; i < q->length; i++){ - newp->at(i + newp->length - q->length) ^= q->at(i); - } -} - - -/* @brief Multiplication of two polynomials - * @param &p - right operand polynomial - * @param &q - left operand polynomial - * @param &newp - destination polynomial */ -inline void -poly_mul(const Poly *p, const Poly *q, Poly *newp) { - newp->length = p->length + q->length - 1; - memset(newp->ptr(), 0, newp->length * sizeof(uint8_t)); - /* Compute the polynomial multiplication (just like the outer product of two vectors, - * we multiply each coefficients of p with all coefficients of q) */ - for(uint8_t j = 0; j < q->length; j++){ - for(uint8_t i = 0; i < p->length; i++){ - newp->at(i+j) ^= mul(p->at(i), q->at(j)); /* == r[i + j] = gf_add(r[i+j], gf_mul(p[i], q[j])) */ - } - } -} - -/* @brief Division of two polynomials - * @param &p - right operand polynomial - * @param &q - left operand polynomial - * @param &newp - destination polynomial */ -inline void -poly_div(const Poly *p, const Poly *q, Poly *newp) { - if(p->ptr() != newp->ptr()) { - memcpy(newp->ptr(), p->ptr(), p->length*sizeof(uint8_t)); - } - - newp->length = p->length; - - uint8_t coef; - - for(int i = 0; i < (p->length-(q->length-1)); i++){ - coef = newp->at(i); - if(coef != 0){ - for(uint8_t j = 1; j < q->length; j++){ - if(q->at(j) != 0) - newp->at(i+j) ^= mul(q->at(j), coef); - } - } - } - - size_t sep = p->length-(q->length-1); - memmove(newp->ptr(), newp->ptr()+sep, (newp->length-sep) * sizeof(uint8_t)); - newp->length = newp->length-sep; -} - -/* @brief Evaluation of polynomial in x - * @param &p - polynomial to evaluate - * @param x - evaluation point */ -inline int8_t -poly_eval(const Poly *p, uint16_t x) { - uint8_t y = p->at(0); - for(uint8_t i = 1; i < p->length; i++){ - y = mul(y, x) ^ p->at(i); - } - return y; -} - -} /* end of gf namespace */ - -} -#endif // GF_H - diff --git a/src/AudioTools/Communication/FEC/ReedSolomon/poly.hpp b/src/AudioTools/Communication/FEC/ReedSolomon/poly.hpp deleted file mode 100644 index e382101200..0000000000 --- a/src/AudioTools/Communication/FEC/ReedSolomon/poly.hpp +++ /dev/null @@ -1,99 +0,0 @@ -/* Author: Mike Lubinets (aka mersinvald) - * Date: 29.12.15 - * - * See LICENSE */ - -#ifndef POLY_H -#define POLY_H -#include -#include - -#if !defined DEBUG && !defined __CC_ARM -#include -#else -#define assert(dummy) -#endif - -/// @brief AudioTools internal: Reed-Solomon -namespace RS { - -struct Poly { - Poly() - : length(0), _memory(NULL) {} - - Poly(uint8_t id, uint16_t offset, uint8_t size) \ - : length(0), _id(id), _size(size), _offset(offset), _memory(NULL) {} - - /* @brief Append number at the end of polynomial - * @param num - number to append - * @return false if polynomial can't be stretched */ - inline bool Append(uint8_t num) { - assert(length+1 < _size); - ptr()[length++] = num; - return true; - } - - /* @brief Polynomial initialization */ - inline void Init(uint8_t id, uint16_t offset, uint8_t size, uint8_t** memory_ptr) { - this->_id = id; - this->_offset = offset; - this->_size = size; - this->length = 0; - this->_memory = memory_ptr; - } - - /* @brief Polynomial memory zeroing */ - inline void Reset() { - memset((void*)ptr(), 0, this->_size); - } - - /* @brief Copy polynomial to memory - * @param src - source byte-sequence - * @param size - size of polynomial - * @param offset - write offset */ - inline void Set(const uint8_t* src, uint8_t len, uint8_t offset = 0) { - assert(src && len <= this->_size-offset); - memcpy(ptr()+offset, src, len * sizeof(uint8_t)); - length = len + offset; - } - - #define poly_max(a, b) ((a > b) ? (a) : (b)) - - inline void Copy(const Poly* src) { - length = poly_max(length, src->length); - Set(src->ptr(), length); - } - - inline uint8_t& at(uint8_t i) const { - assert(i < _size); - return ptr()[i]; - } - - inline uint8_t id() const { - return _id; - } - - inline uint8_t size() const { - return _size; - } - - // Returns pointer to memory of this polynomial - inline uint8_t* ptr() const { - assert(_memory && *_memory); - return (*_memory) + _offset; - } - - uint8_t length; - -protected: - - uint8_t _id; - uint8_t _size; // Size of reserved memory for this polynomial - uint16_t _offset; // Offset in memory - uint8_t** _memory; // Pointer to pointer to memory -}; - - -} - -#endif // POLY_H diff --git a/src/AudioTools/Communication/FEC/ReedSolomon/rs.hpp b/src/AudioTools/Communication/FEC/ReedSolomon/rs.hpp deleted file mode 100644 index 34fc38136f..0000000000 --- a/src/AudioTools/Communication/FEC/ReedSolomon/rs.hpp +++ /dev/null @@ -1,530 +0,0 @@ -/* Author: Mike Lubinets (aka mersinvald) - * Date: 29.12.15 - * - * See LICENSE */ - -#ifndef RS_HPP -#define RS_HPP -#include -#include -#include "poly.hpp" -#include "gf.hpp" - -#if !defined DEBUG && !defined __CC_ARM -#include -#else -#define assert(dummy) -#endif - -/// @brief AudioTools internal: Reed-Solomon -namespace RS { - -#define MSG_CNT 3 // message-length polynomials count -#define POLY_CNT 14 // (ecc_length*2)-length polynomials count - -template // Length of correction code - -class ReedSolomon { -public: - ReedSolomon() { - const uint8_t enc_len = msg_length + ecc_length; - const uint8_t poly_len = ecc_length * 2; - uint8_t** memptr = &memory; - uint16_t offset = 0; - - /* Initialize first six polys manually cause their amount depends on template parameters */ - - polynoms[0].Init(ID_MSG_IN, offset, enc_len, memptr); - offset += enc_len; - - polynoms[1].Init(ID_MSG_OUT, offset, enc_len, memptr); - offset += enc_len; - - for(uint8_t i = ID_GENERATOR; i < ID_MSG_E; i++) { - polynoms[i].Init(i, offset, poly_len, memptr); - offset += poly_len; - } - - polynoms[5].Init(ID_MSG_E, offset, enc_len, memptr); - offset += enc_len; - - for(uint8_t i = ID_TPOLY3; i < ID_ERR_EVAL+2; i++) { - polynoms[i].Init(i, offset, poly_len, memptr); - offset += poly_len; - } - } - - ~ReedSolomon() { - // Dummy destructor, gcc-generated one crashes program - memory = NULL; - } - - /* @brief Message block encoding - * @param *src - input message buffer (msg_length size) - * @param *dst - output buffer for ecc (ecc_length size at least) */ - void EncodeBlock(const void* src, void* dst) { - assert(msg_length + ecc_length < 256); - - /* Generator cache, it dosn't change for one template parameters */ - static uint8_t generator_cache[ecc_length+1] = {0}; - static bool generator_cached = false; - - /* Allocating memory on stack for polynomials storage */ - uint8_t stack_memory[MSG_CNT * msg_length + POLY_CNT * ecc_length * 2]; - this->memory = stack_memory; - - const uint8_t* src_ptr = (const uint8_t*) src; - uint8_t* dst_ptr = (uint8_t*) dst; - - Poly *msg_in = &polynoms[ID_MSG_IN]; - Poly *msg_out = &polynoms[ID_MSG_OUT]; - Poly *gen = &polynoms[ID_GENERATOR]; - - // Weird shit, but without resetting msg_in it simply doesn't work - msg_in->Reset(); - msg_out->Reset(); - - // Using cached generator or generating new one - if(generator_cached) { - gen->Set(generator_cache, sizeof(generator_cache)); - } else { - GeneratorPoly(); - memcpy(generator_cache, gen->ptr(), gen->length); - generator_cached = true; - } - - // Copying input message to internal polynomial - msg_in->Set(src_ptr, msg_length); - msg_out->Set(src_ptr, msg_length); - msg_out->length = msg_in->length + ecc_length; - - // Here all the magic happens - uint8_t coef = 0; // cache - for(uint8_t i = 0; i < msg_length; i++){ - coef = msg_out->at(i); - if(coef != 0){ - for(uint32_t j = 1; j < gen->length; j++){ - msg_out->at(i+j) ^= gf::mul(gen->at(j), coef); - } - } - } - - // Copying ECC to the output buffer - memcpy(dst_ptr, msg_out->ptr()+msg_length, ecc_length * sizeof(uint8_t)); - } - - /* @brief Message encoding - * @param *src - input message buffer (msg_length size) - * @param *dst - output buffer (msg_length + ecc_length size at least) */ - void Encode(const void* src, void* dst) { - uint8_t* dst_ptr = (uint8_t*) dst; - - // Copying message to the output buffer - memcpy(dst_ptr, src, msg_length * sizeof(uint8_t)); - - // Calling EncodeBlock to write ecc to out[ut buffer - EncodeBlock(src, dst_ptr+msg_length); - } - - /* @brief Message block decoding - * @param *src - encoded message buffer (msg_length size) - * @param *ecc - ecc buffer (ecc_length size) - * @param *msg_out - output buffer (msg_length size at least) - * @param *erase_pos - known errors positions - * @param erase_count - count of known errors - * @return RESULT_SUCCESS if successful, error code otherwise */ - int DecodeBlock(const void* src, const void* ecc, void* dst, uint8_t* erase_pos = NULL, size_t erase_count = 0) { - assert(msg_length + ecc_length < 256); - - const uint8_t *src_ptr = (const uint8_t*) src; - const uint8_t *ecc_ptr = (const uint8_t*) ecc; - uint8_t *dst_ptr = (uint8_t*) dst; - - const uint8_t src_len = msg_length + ecc_length; - const uint8_t dst_len = msg_length; - - bool ok; - - /* Allocation memory on stack */ - uint8_t stack_memory[MSG_CNT * msg_length + POLY_CNT * ecc_length * 2]; - this->memory = stack_memory; - - Poly *msg_in = &polynoms[ID_MSG_IN]; - Poly *msg_out = &polynoms[ID_MSG_OUT]; - Poly *epos = &polynoms[ID_ERASURES]; - - // Copying message to polynomials memory - msg_in->Set(src_ptr, msg_length); - msg_in->Set(ecc_ptr, ecc_length, msg_length); - msg_out->Copy(msg_in); - - // Copying known errors to polynomial - if(erase_pos == NULL) { - epos->length = 0; - } else { - epos->Set(erase_pos, erase_count); - for(uint8_t i = 0; i < epos->length; i++){ - msg_in->at(epos->at(i)) = 0; - } - } - - // Too many errors - if(epos->length > ecc_length) return 1; - - Poly *synd = &polynoms[ID_SYNDROMES]; - Poly *eloc = &polynoms[ID_ERRORS_LOC]; - Poly *reloc = &polynoms[ID_TPOLY1]; - Poly *err = &polynoms[ID_ERRORS]; - Poly *forney = &polynoms[ID_FORNEY]; - - // Calculating syndrome - CalcSyndromes(msg_in); - - // Checking for errors - bool has_errors = false; - for(uint8_t i = 0; i < synd->length; i++) { - if(synd->at(i) != 0) { - has_errors = true; - break; - } - } - - // Going to exit if no errors - if(!has_errors) goto return_corrected_msg; - - CalcForneySyndromes(synd, epos, src_len); - FindErrorLocator(forney, NULL, epos->length); - - // Reversing syndrome - // TODO optimize through special Poly flag - reloc->length = eloc->length; - for(int8_t i = eloc->length-1, j = 0; i >= 0; i--, j++){ - reloc->at(j) = eloc->at(i); - } - - // Find errors - ok = FindErrors(reloc, src_len); - if(!ok) return 1; - - // Error happened while finding errors (so helpful :D) - if(err->length == 0) return 1; - - /* Adding found errors with known */ - for(uint8_t i = 0; i < err->length; i++) { - epos->Append(err->at(i)); - } - - // Correcting errors - CorrectErrata(synd, epos, msg_in); - - return_corrected_msg: - // Writing corrected message to output buffer - msg_out->length = dst_len; - memcpy(dst_ptr, msg_out->ptr(), msg_out->length * sizeof(uint8_t)); - return 0; - } - - /* @brief Message block decoding - * @param *src - encoded message buffer (msg_length + ecc_length size) - * @param *msg_out - output buffer (msg_length size at least) - * @param *erase_pos - known errors positions - * @param erase_count - count of known errors - * @return RESULT_SUCCESS if successful, error code otherwise */ - int Decode(const void* src, void* dst, uint8_t* erase_pos = NULL, size_t erase_count = 0) { - const uint8_t *src_ptr = (const uint8_t*) src; - const uint8_t *ecc_ptr = src_ptr + msg_length; - - return DecodeBlock(src, ecc_ptr, dst, erase_pos, erase_count); - } - -#ifndef DEBUG -private: -#endif - - enum POLY_ID { - ID_MSG_IN = 0, - ID_MSG_OUT, - ID_GENERATOR, // 3 - ID_TPOLY1, // T for Temporary - ID_TPOLY2, - - ID_MSG_E, // 5 - - ID_TPOLY3, // 6 - ID_TPOLY4, - - ID_SYNDROMES, - ID_FORNEY, - - ID_ERASURES_LOC, - ID_ERRORS_LOC, - - ID_ERASURES, - ID_ERRORS, - - ID_COEF_POS, - ID_ERR_EVAL - }; - - // Pointer for polynomials memory on stack - uint8_t* memory; - Poly polynoms[MSG_CNT + POLY_CNT]; - - void GeneratorPoly() { - Poly *gen = polynoms + ID_GENERATOR; - gen->at(0) = 1; - gen->length = 1; - - Poly *mulp = polynoms + ID_TPOLY1; - Poly *temp = polynoms + ID_TPOLY2; - mulp->length = 2; - - for(int8_t i = 0; i < ecc_length; i++){ - mulp->at(0) = 1; - mulp->at(1) = gf::pow(2, i); - - gf::poly_mul(gen, mulp, temp); - - gen->Copy(temp); - } - } - - void CalcSyndromes(const Poly *msg) { - Poly *synd = &polynoms[ID_SYNDROMES]; - synd->length = ecc_length+1; - synd->at(0) = 0; - for(uint8_t i = 1; i < ecc_length+1; i++){ - synd->at(i) = gf::poly_eval(msg, gf::pow(2, i-1)); - } - } - - void FindErrataLocator(const Poly *epos) { - Poly *errata_loc = &polynoms[ID_ERASURES_LOC]; - Poly *mulp = &polynoms[ID_TPOLY1]; - Poly *addp = &polynoms[ID_TPOLY2]; - Poly *apol = &polynoms[ID_TPOLY3]; - Poly *temp = &polynoms[ID_TPOLY4]; - - errata_loc->length = 1; - errata_loc->at(0) = 1; - - mulp->length = 1; - addp->length = 2; - - for(uint8_t i = 0; i < epos->length; i++){ - mulp->at(0) = 1; - addp->at(0) = gf::pow(2, epos->at(i)); - addp->at(1) = 0; - - gf::poly_add(mulp, addp, apol); - gf::poly_mul(errata_loc, apol, temp); - - errata_loc->Copy(temp); - } - } - - void FindErrorEvaluator(const Poly *synd, const Poly *errata_loc, Poly *dst, uint8_t ecclen) { - Poly *mulp = &polynoms[ID_TPOLY1]; - gf::poly_mul(synd, errata_loc, mulp); - - Poly *divisor = &polynoms[ID_TPOLY2]; - divisor->length = ecclen+2; - - divisor->Reset(); - divisor->at(0) = 1; - - gf::poly_div(mulp, divisor, dst); - } - - void CorrectErrata(const Poly *synd, const Poly *err_pos, const Poly *msg_in) { - Poly *c_pos = &polynoms[ID_COEF_POS]; - Poly *corrected = &polynoms[ID_MSG_OUT]; - c_pos->length = err_pos->length; - - for(uint8_t i = 0; i < err_pos->length; i++) - c_pos->at(i) = msg_in->length - 1 - err_pos->at(i); - - /* uses t_poly 1, 2, 3, 4 */ - FindErrataLocator(c_pos); - Poly *errata_loc = &polynoms[ID_ERASURES_LOC]; - - /* reversing syndromes */ - Poly *rsynd = &polynoms[ID_TPOLY3]; - rsynd->length = synd->length; - - for(int8_t i = synd->length-1, j = 0; i >= 0; i--, j++) { - rsynd->at(j) = synd->at(i); - } - - /* getting reversed error evaluator polynomial */ - Poly *re_eval = &polynoms[ID_TPOLY4]; - - /* uses T_POLY 1, 2 */ - FindErrorEvaluator(rsynd, errata_loc, re_eval, errata_loc->length-1); - - /* reversing it back */ - Poly *e_eval = &polynoms[ID_ERR_EVAL]; - e_eval->length = re_eval->length; - for(int8_t i = re_eval->length-1, j = 0; i >= 0; i--, j++) { - e_eval->at(j) = re_eval->at(i); - } - - Poly *X = &polynoms[ID_TPOLY1]; /* this will store errors positions */ - X->length = 0; - - int16_t l; - for(uint8_t i = 0; i < c_pos->length; i++){ - l = 255 - c_pos->at(i); - X->Append(gf::pow(2, -l)); - } - - /* Magnitude polynomial - Shit just got real */ - Poly *E = &polynoms[ID_MSG_E]; - E->Reset(); - E->length = msg_in->length; - - uint8_t Xi_inv; - - Poly *err_loc_prime_temp = &polynoms[ID_TPOLY2]; - - uint8_t err_loc_prime; - uint8_t y; - - for(uint8_t i = 0; i < X->length; i++){ - Xi_inv = gf::inverse(X->at(i)); - - err_loc_prime_temp->length = 0; - for(uint8_t j = 0; j < X->length; j++){ - if(j != i){ - err_loc_prime_temp->Append(gf::sub(1, gf::mul(Xi_inv, X->at(j)))); - } - } - - err_loc_prime = 1; - for(uint8_t j = 0; j < err_loc_prime_temp->length; j++){ - err_loc_prime = gf::mul(err_loc_prime, err_loc_prime_temp->at(j)); - } - - y = gf::poly_eval(re_eval, Xi_inv); - y = gf::mul(gf::pow(X->at(i), 1), y); - - E->at(err_pos->at(i)) = gf::div(y, err_loc_prime); - } - - gf::poly_add(msg_in, E, corrected); - } - - bool FindErrorLocator(const Poly *synd, Poly *erase_loc = NULL, size_t erase_count = 0) { - Poly *error_loc = &polynoms[ID_ERRORS_LOC]; - Poly *err_loc = &polynoms[ID_TPOLY1]; - Poly *old_loc = &polynoms[ID_TPOLY2]; - Poly *temp = &polynoms[ID_TPOLY3]; - Poly *temp2 = &polynoms[ID_TPOLY4]; - - if(erase_loc != NULL) { - err_loc->Copy(erase_loc); - old_loc->Copy(erase_loc); - } else { - err_loc->length = 1; - old_loc->length = 1; - err_loc->at(0) = 1; - old_loc->at(0) = 1; - } - - uint8_t synd_shift = 0; - if(synd->length > ecc_length) { - synd_shift = synd->length - ecc_length; - } - - uint8_t K = 0; - uint8_t delta = 0; - uint8_t index; - - for(uint8_t i = 0; i < ecc_length - erase_count; i++){ - if(erase_loc != NULL) - K = erase_count + i + synd_shift; - else - K = i + synd_shift; - - delta = synd->at(K); - for(uint8_t j = 1; j < err_loc->length; j++) { - index = err_loc->length - j - 1; - delta ^= gf::mul(err_loc->at(index), synd->at(K-j)); - } - - old_loc->Append(0); - - if(delta != 0) { - if(old_loc->length > err_loc->length) { - gf::poly_scale(old_loc, temp, delta); - gf::poly_scale(err_loc, old_loc, gf::inverse(delta)); - err_loc->Copy(temp); - } - gf::poly_scale(old_loc, temp, delta); - gf::poly_add(err_loc, temp, temp2); - err_loc->Copy(temp2); - } - } - - uint32_t shift = 0; - while(err_loc->length && err_loc->at(shift) == 0) shift++; - - uint32_t errs = err_loc->length - shift - 1; - if(((errs - erase_count) * 2 + erase_count) > ecc_length){ - return false; /* Error count is greater than we can fix! */ - } - - memcpy(error_loc->ptr(), err_loc->ptr() + shift, (err_loc->length - shift) * sizeof(uint8_t)); - error_loc->length = (err_loc->length - shift); - return true; - } - - bool FindErrors(const Poly *error_loc, size_t msg_in_size) { - Poly *err = &polynoms[ID_ERRORS]; - - uint8_t errs = error_loc->length - 1; - err->length = 0; - - for(uint8_t i = 0; i < msg_in_size; i++) { - if(gf::poly_eval(error_loc, gf::pow(2, i)) == 0) { - err->Append(msg_in_size - 1 - i); - } - } - - /* Sanity check: - * the number of err/errata positions found - * should be exactly the same as the length of the errata locator polynomial */ - if(err->length != errs) - /* couldn't find error locations */ - return false; - return true; - } - - void CalcForneySyndromes(const Poly *synd, const Poly *erasures_pos, size_t msg_in_size) { - Poly *erase_pos_reversed = &polynoms[ID_TPOLY1]; - Poly *forney_synd = &polynoms[ID_FORNEY]; - erase_pos_reversed->length = 0; - - for(uint8_t i = 0; i < erasures_pos->length; i++){ - erase_pos_reversed->Append(msg_in_size - 1 - erasures_pos->at(i)); - } - - forney_synd->Reset(); - forney_synd->Set(synd->ptr()+1, synd->length-1); - - uint8_t x; - for(uint8_t i = 0; i < erasures_pos->length; i++) { - x = gf::pow(2, erase_pos_reversed->at(i)); - for(int8_t j = 0; j < forney_synd->length - 1; j++){ - forney_synd->at(j) = gf::mul(forney_synd->at(j), x) ^ forney_synd->at(j+1); - } - } - } -}; - -} - -#endif // RS_HPP - diff --git a/src/AudioTools/Communication/HammingFEC.h b/src/AudioTools/Communication/HammingFEC.h deleted file mode 100644 index 20e7a14cbf..0000000000 --- a/src/AudioTools/Communication/HammingFEC.h +++ /dev/null @@ -1,316 +0,0 @@ -#pragma once -#include "AudioToolsConfig.h" -#include "AudioTools/CoreAudio/BaseStream.h" - -#include -#include -#include -#include -#include - -namespace audio_tools { - -/** - * @brief Hamming forware error correction. Inspired by - * https://github.com/nasserkessas/hamming-codes - * - * Hamming<1024,uint16_t> hamming; // block_ts of 1k with block_tsize 16bits = 31.25% redundency - * - * Block size (bits) Redundant bits Redundancy percentage - * 4 3 75% - * 8 4 50% - * 16 5 31.25% - * 32 6 18.75% - * 64 7 10.94% - * - * @ingroup fec -*/ - -template -class HammingFEC : public BaseStream { - public: - HammingFEC(Stream &stream){ - p_stream = &stream; - p_print = &stream; - } - - HammingFEC(Print &print){ - p_print = &print; - } - - int availableForWrite() override { - return bytecount; - } - - size_t write(const uint8_t* data, size_t len)override{ - if (p_print==nullptr) return 0; - for (int j=0;jreadBytes(encoded.data(), encodedSize()); - encoded.setAvailable(encodedSize()); - // decode data - if(decode((block_t*)encoded.data(), bytecount)){ - raw.setAvailable(bytecount); - } - } - return raw.readArray(data, len); - } - - protected: - SingleBuffer raw{bytecount}; - SingleBuffer encoded{encodedSize()}; - Stream* p_stream = nullptr; - Print* p_print = nullptr; - - int getBlocks(){ - // Amount of bits in a block_t // - int bits = sizeof(block_t) * 8; - - // Amount of bits per block_t used to carry the message // - int messageBits = bits - log2(bits) - 1; - - // Amount of block_ts needed to encode message // - int block_ts = ceil((float) bytecount / messageBits); - - return block_ts; - } - - int encodedSize(){ - return getBlocks()+1*sizeof(block_t); - } - - int encode(uint8_t *input, int len) { - - // Amount of bits in a block_t // - int bits = sizeof(block_t) * 8; - - // Amount of bits per block_t used to carry the message // - int messageBits = bits - log2(bits) - 1; - - // Amount of block_ts needed to encode message // - int block_ts = ceil((float) len / messageBits); - - // Array of encoded block_ts // - block_t encoded[block_ts+1]; - - // Loop through each block_t // - for (int i = 0; i < block_ts+1; i++) { - - // Final encoded block_t variable // - block_t thisBlock = 0; - - // Amount of "skipped" bits (used for parity) // - int skipped = 0; - - // Count of how many bits are "on" // - int onCount = 0; - - // Array of "on" bits // - int onList[bits]; - - // Loop through each message bit in this block_t to populate final block_t // - for (int j = 0; j < bits; j++) { - - // Skip bit if reserved for parity bit // - if ((j & (j - 1)) == 0) { // Check if j is a power of two or 0 - skipped++; - continue; - } - - bool thisBit; - - if (i != block_ts) { - - // Current overall bit number // - int currentBit = i*messageBits + (j-skipped); - - // Current uint8_tacter // - int currentChar = currentBit/(sizeof(uint8_t)*8); // int division - - // Value of current bit // - thisBit = currentBit < len*sizeof(uint8_t)*8 ? getCharBit(input[currentChar], currentBit-currentChar*8) : 0; - } - - else { - thisBit = getBit(len/8, j-skipped+(sizeof(block_t)*8-messageBits)); - } - - // If bit is "on", add to onList and onCount // - if (thisBit) { - onList[onCount] = j; - onCount++; - } - - // Populate final message block_t // - thisBlock = modifyBit(thisBlock, j, thisBit); - } - - // Calculate values of parity bits // - block_t parityBits = multipleXor(onList, onCount); - - // Loop through skipped bits (parity bits) // - for (int k = 1; k < skipped; k++) { // skip bit 0 - - // If bit is "on", add to onCount - if (getBit(parityBits, sizeof(block_t)*8-skipped+k)) { - onCount++; - } - - // Add parity bit to final block_t // - thisBlock = modifyBit(thisBlock, (int) pow(2, skipped-k-1) , getBit(parityBits, sizeof(block_t)*8-skipped+k)); - } - - // Add overall parity bit (total parity of onCount) // - thisBlock = modifyBit(thisBlock, 0, onCount & 1); - - // Add block_t to encoded block_ts // - encoded[i] = thisBlock; - } - - // Write encoded message to file / - return p_print->write((const uint8_t*)encoded, sizeof(block_t)*block_ts+1); - } - - bool decode(block_t input[], int len) { - uint8_t* output = (uint8_t*)raw.data(); - - // Amount of bits in a block_t // - int bits = sizeof(block_t) * 8; - - for (int b = 0; b < (len/sizeof(block_t)); b++) { - - // Count of how many bits are "on" // - int onCount = 0; - - // Array of "on" bits // - int onList[bits]; - - // Populate onCount and onList // - for (int i = 1; i < bits; i++) { - getBit(input[b], i); - if (getBit(input[b], i)) { - onList[onCount] = i; - onCount++; - } - } - - // Check for single errors // - int errorLoc = multipleXor(onList, onCount); - - if (errorLoc) { - - // Check for multiple errors // - if (!(onCount & 1 ^ getBit(input[b], 0))) { // last bit of onCount (total parity) XOR first bit of block_t (parity bit) - LOGE("\nMore than one error detected. Aborting.\n"); - return false; - } - - // Flip error bit // - else { - input[b] = toggleBit(input[b], (bits-1) - errorLoc); - } - } - } - - // Amount of bits per block_t used to carry the message // - int messageBits = bits - log2(bits) - 1; - - int currentBit, currentChar; - - int uint8_ts = 0; - - for (int i = 0; i < len/sizeof(block_t); i++) { - - // Initialise variable to store amount of parity bits passed // - int skipped = 0; - - // Loop through each message bit in this block_t to populate final block_t // - for (int j = 0; j < bits; j++) { - - // Skip bit if reserved for parity bit // - if ((j & (j - 1)) == 0) { // Check if j is a power of two or 0 - skipped++; - continue; - } - - // Current overall bit number // - currentBit = i*messageBits + (j-skipped); - - // Current uint8_tacter // - currentChar = currentBit/(sizeof(uint8_t)*8); // int division - - // Value of current bit // - bool thisBit = getBit(input[i], j); - - if (i != len/sizeof(block_t)-1) { - - // Populate final decoded uint8_tacter // - output[currentChar] = modifyCharBit(output[currentChar], currentBit-currentChar*sizeof(uint8_t)*8, thisBit); - } - - else { - uint8_ts = modifyBit(uint8_ts, j-skipped+(sizeof(block_t)*8-messageBits), thisBit); - } - - } - } - - - return uint8_ts; - } - - int multipleXor(int *indicies, int len) { - int val = indicies[0]; - for (int i = 1; i < len; i++) { - val = val ^ indicies[i]; - } - return val; - } - - block_t modifyBit(block_t n, int p, bool b) { - return ((n & ~(1 << (sizeof(block_t)*8-1-p))) | (b << (sizeof(block_t)*8-1-p))); - } - - uint8_t modifyCharBit(uint8_t n, int p, bool b) { - return ((n & ~(1 << (sizeof(uint8_t)*8-1-p))) | (b << (sizeof(uint8_t)*8-1-p))); - } - - bool getBit(block_t b, int i) { - return (b << i) & (int) pow(2, (sizeof(block_t)*8-1)); - } - - bool getCharBit(uint8_t b, int i) { - return (b << i) & (int) pow(2, (sizeof(uint8_t)*8-1)); - } - - block_t toggleBit(block_t b, int i) { - return b ^ (1 << i); - } - - int inList(uint8_t **list, uint8_t *testString, int listLen) { - for (int i = 0; i < listLen; i++) { - if (strcmp(list[i], testString) == 0) { - return i; - } - } - return 0; - } -}; - -} // ns \ No newline at end of file diff --git a/src/AudioTools/Communication/OSCData.h b/src/AudioTools/Communication/OSCData.h deleted file mode 100644 index 9851e5f54b..0000000000 --- a/src/AudioTools/Communication/OSCData.h +++ /dev/null @@ -1,392 +0,0 @@ -#pragma once -#include -#include -#include "AudioTools/CoreAudio/AudioBasic/Collections/Vector.h" - -namespace audio_tools { - -/** - * @brief Simple structure to hold binary data. - * @ingroup aoo-utils - * @author Phil Schatzmann - * @copyright GPLv3 - */ - -struct OSCBinaryData { - uint8_t *data = nullptr; - size_t len = 0; - OSCBinaryData(uint8_t *data, int len) { - this->data = data; - this->len = len; - } - OSCBinaryData() = default; -}; - -enum class OSCCompare { Matches, Equals, StartsWith, EndsWith, Contains }; - -/** - * @brief A simple OSC Data composer and parser. A OSC data starts with an - * address string followed by a format string. This is followed by the data. - * You need to call the read and write methods in the sequence defined by the - * format string. There is no validation for this, so you need to be careful - * and test your code properly. - * To compose a message call: - * - setAddress() to set the address - * - setFormat() to set the format string - * - write() to write the data - * ... - * you can access the content via data() and size() - * - * To parse a message call: - * - parse() to parse the message - * - getAddress() to get the address - * - getFormat() to get the format string - * - readXxxx() to read the data - * ... - * I recommend to register for each address and process the reads in the - * callback - * - * OSC V1.0 Specification: https://opensoundcontrol.stanford.edu/spec-1_0.html - * - * @ingroup aoo-utils - * @author Phil Schatzmann - * @copyright GPLv3 - */ - -class OSCData { - public: - /// Data for receiving: - OSCData() = default; - /// Data for sending - OSCData(uint8_t *data, uint32_t maxlen) { - binary_content.data = data; - binary_content.len = maxlen; - } - - /// Defines the address string (e.g. /test) - void setAddress(const char *address) { - int adr_len = OSCData::oscSize(address); - memset(binary_content.data, 0, adr_len); - strcpy((char *)binary_content.data, address); - write_pos = adr_len; - } - - /// Defines the format string (e.g. ,iif) - void setFormat(const char *format) { - int fmt_len = OSCData::oscFormatSize(strlen(format)); - memset(binary_content.data + write_pos, 0, fmt_len); - binary_content.data[write_pos] = ','; - read_format_start = binary_content.data + write_pos + 1; - strcpy((char *)read_format_start, format); - write_pos += fmt_len; - read_data = binary_content.data + write_pos; - } - - /// write an int32_t number (i) - bool write(int32_t number) { - int32_t number_net = htonl(number); - if (write_pos + sizeof(int32_t) > binary_content.len) { - return false; - } - memcpy(binary_content.data + write_pos, &number_net, sizeof(int32_t)); - write_pos += sizeof(int32_t); - return true; - } - - /// write an int64_t number - bool write(int64_t number) { - if (write_pos + sizeof(int64_t) > binary_content.len) { - return false; - } - int64_t number_net = htonll(number); - memcpy(binary_content.data + write_pos, &number_net, sizeof(int64_t)); - write_pos += sizeof(int64_t); - return true; - } - -#ifndef IS_DESKTOP - - /// write a unsigned long number - bool write(unsigned long number) { return write((uint64_t)number); } - -#endif - - /// write a timetag (t) data type - bool write(uint64_t number) { - if (write_pos + sizeof(uint64_t) > binary_content.len) { - return false; - } - uint64_t number_net = htonll(number); - memcpy(binary_content.data + write_pos, &number_net, sizeof(uint64_t)); - write_pos += sizeof(uint64_t); - return true; - } - - /// write a 64bit double number (d) - bool write(double fp64) { return write(*(int64_t *)&fp64); } - - /// write a string (s) - bool write(const char *str) { - int str_len = OSCData::oscSize(str); - if (write_pos + str_len > binary_content.len) { - return false; - } - memset(binary_content.data + write_pos, 0, str_len); - strcpy((char *)binary_content.data + write_pos, str); - write_pos += str_len; - return true; - } - - /// write a binary blob (b) - bool write(OSCBinaryData &data) { return write(data.data, data.len); } - - /// write a binary blob (b) - bool write(const uint8_t *dataArg, int len) { - uint32_t size = OSCData::oscSize(len) + sizeof(int32_t); - if (write_pos + size > binary_content.len) { - return false; - } - memset(binary_content.data + write_pos, 0, size); - int32_t len_net = htonl(len); - memcpy((binary_content.data + write_pos), &len_net, sizeof(int32_t)); - memcpy((binary_content.data + write_pos + sizeof(int32_t)), dataArg, len); - write_pos += size; - return true; - } - - /// clears all data - void clear() { - write_pos = 0; - read_format_start = nullptr; - read_data = nullptr; - binary_content.data = nullptr; - binary_content.len = 0; - callbacks.clear(); - } - - /// provides access to the original binary message (defined in the constructor - /// or via parse()) - uint8_t *data() { return binary_content.data; } - - /// returns the number of bytes written (or parsed) - int size() { return write_pos; } - - /// parse the data to start for reading - bool parse(uint8_t *data, size_t len) { - // store data - binary_content.data = data; - binary_content.len = len; - - // to support size() - write_pos = len; - - // Print frist 20 digits - if (is_log_active) logMsg(data, 20); - - if (data[0] != '/') return false; - - // determine address length 4 byte aligned - int addr_len = OSCData::oscSize((char *)binary_content.data); - - // determine start of format - read_format_start = (uint8_t *)getAddress() + addr_len; - - // check start of format - if (*read_format_start != ',') { - return false; - } - - int format_len = - OSCData::oscFormatSize(strlen((char *)read_format_start + 1)); - - // determine start of data - read_data = read_format_start + format_len; - - // move past , - read_format_start++; - - /// call callback if there are any - if (callbacks.size() > 0) { - for (auto &cb : callbacks) { - if (compare(cb.compare, cb.address, getAddress())) { - if (cb.callback != nullptr) { - if (cb.callback(*this, reference)) { - return true; - } - } - } - } - // return false if no callback was called successfully - return false; - } - - return true; - } - - void logMsg(uint8_t *data, int len) { - Serial.print("OSCData: "); - for (int i = 0; i < len; i++) { - Serial.print((char)data[i]); - } -#ifndef IS_DESKTOP - Serial.println(); - Serial.print("Hex Data: "); - for (int i = 0; i < len; i++) { - Serial.print(data[i], HEX); - } - Serial.println(); -#endif - } - - /// provides the address: after calling parse - const char *getAddress() { return (const char *)binary_content.data; } - - /// provides the format string: after calling parse - const char *getFormat() { return (const char *)read_format_start; } - - /// reads the next attributes as float - float readFloat() { - int64_t number = readInt32(); - return *((float *)&number); - } - - /// reads the next attribute as int32 - int32_t readInt32() { - int32_t number; - memcpy(&number, read_data, sizeof(int32_t)); - read_data += sizeof(int32_t); - return ntohl(number); - } - - /// reads the next attribute as long - int64_t readInt64() { - int64_t number; - memcpy(&number, read_data, sizeof(int64_t)); - read_data += sizeof(int64_t); - return ntohll(number); - } - - /// reads the next attribute as uint64_t - uint64_t readTime() { return (uint64_t)readInt64(); } - - /// reads the next attribute as double - double readDouble() { - int64_t number = readInt64(); - return *((double *)&number); - } - - /// reads the next string - const char *readString() { - const char *str = (const char *)read_data; - int str_len = OSCData::oscSize(str); - read_data += str_len; - return str; - } - - /// reads the next attribute as binary data blob. - const OSCBinaryData readData() { - OSCBinaryData result; - int32_t len = 0; - memcpy(&len, read_data, sizeof(int32_t)); - result.len = ntohl(len); - result.data = read_data + sizeof(int32_t); - read_data += (OSCData::oscSize(result.len) + sizeof(int32_t)); - return result; - } - - /// Log the beginning of the received messages - void setLogActive(bool active) { is_log_active = active; } - - /// provides access to the original binary message (defined in the constructor - /// or via parse()) - OSCBinaryData &messageData() { return binary_content; } - - /// storage size (multiple of 4) - static int oscSize(int len) { - if (len % 4 == 0) return len; - int padding = 4 - (len % 4); - return len + padding; - } - - /// storage size (multiple of 4) for binary blob data - static int oscSize(OSCBinaryData data) { - return sizeof(uint32_t) + OSCData::oscSize(data.len); - } - - /// storage size (multiple of 4) for string - static int oscSize(const char *str) { - // +1 for trailing 0 - return OSCData::oscSize(strlen(str) + 1); - } - - /// storage size (multiple of 4) for format string (w/o the leading ,) - static int oscFormatSize(int len) { - //+2 for , and trailing 0 - return oscSize(len + 2); - } - - /// storage size (multiple of 4) for format string (w/o the leading ,) - static int oscFormatSize(const char *str) { - return oscFormatSize(strlen(str)); - } - - /// store a reference object (for callback) - void setReference(void *ref) { reference = ref; } - - /// register a parsing callback for a specific address matching string - bool addCallback(const char *address, - bool (*callback)(OSCData &data, void *ref), - OSCCompare compare = OSCCompare::Matches) { - if (address == nullptr || callback == nullptr) return false; - - /// replace existing callback - for (auto &cb : callbacks) { - if (cb.address != nullptr && strcmp(cb.address, address) == 0) { - cb.callback = callback; - cb.compare = compare; - return true; - } - } - // add new entry - Callback cb; - cb.address = address; - cb.callback = callback; - callbacks.push_back(cb); - return true; - } - - protected: - int write_pos = 0; - uint8_t *read_format_start = nullptr; - uint8_t *read_data = nullptr; - bool is_log_active = false; - OSCBinaryData binary_content; - void *reference = nullptr; - - struct Callback { - const char *address = nullptr; - bool (*callback)(OSCData &data, void *ref) = nullptr; - OSCCompare compare; - }; - Vector callbacks; - - bool compare(OSCCompare compare, const char *strRef, const char *strCompare) { - switch (compare) { - case OSCCompare::Matches: - return StrView(strRef).matches(strCompare); - case OSCCompare::Equals: - return StrView(strRef).equals(strCompare); - case OSCCompare::StartsWith: - return StrView(strRef).startsWith(strCompare); - case OSCCompare::EndsWith: - return StrView(strRef).endsWith(strCompare); - case OSCCompare::Contains: - return StrView(strRef).contains(strCompare); - } - return false; - } - - uint8_t *getEnd() { return binary_content.data + binary_content.len; } -}; - -} // namespace audio_tools \ No newline at end of file diff --git a/src/AudioTools/Communication/ObjectStream.h b/src/AudioTools/Communication/ObjectStream.h deleted file mode 100644 index ce04b13486..0000000000 --- a/src/AudioTools/Communication/ObjectStream.h +++ /dev/null @@ -1,76 +0,0 @@ -#include "AudioTools/CoreAudio/BaseStream.h" -namespace audio_tools { -/** - * @brief A Arduino Stream which makes sure that we read back the same size - * as we wrote. It adds a size prefix to the data stream. - * @author Phil Schatzmann - */ -class ObjectStream : public BaseStream { - ObjectStream(Stream &stream) { - p_in = &stream; - p_out = &stream; - } - ObjectStream(Print &stream) { p_out = &stream; } - - /// reads the data from the input stream: I recommend to set the len to the - /// max object size, to avoid that we read only a part of the object. - size_t readBytes(uint8_t *data, size_t len) { - if (p_in == nullptr) return 0; - // read the size prefix if necessary - int to_read = available(); - size_t result = 0; - if (to_read > 0) { - if (to_read > len) to_read = len; - // read the data - result = p_in->readBytes(data, to_read); - // determe the open number of bytes - n_open_read -= result; - is_complete = n_open_read == 0; - // if we have read all data we need to read the size prefix again - if (is_complete) n_open_read = -1; - } - return result; - } - - size_t write(const uint8_t *data, size_t len) { - if (p_out == nullptr) return 0; - // write the size prefix - p_out->write((uint8_t *)&len, sizeof(size_t)); - // write the data - return p_out->write(data, len); - } - - int available() { - if (p_in == nullptr) return 0; - if (n_open_read >= 0) return n_open_read; - // make sure that we can read the size prefix - if (p_in->available() < sizeof(size_t)) return 0; - // read the size prefix - p_in->readBytes((uint8_t *)&n_open_read, sizeof(size_t)); - return n_open_read; - } - - // not supported - virtual size_t write(uint8_t ch) { return 0; } - - int availableForWrite() override { - if (max_object_size > 0) return max_object_size; - if (p_out == nullptr) return DEFAULT_BUFFER_SIZE; - return p_out->availableForWrite(); - } - - /// When value is 0 (default) we determine it from the output, otherwise we - /// return the defined value. - void setMaxObjectSize(size_t size) { max_object_size = size; } - - /// Determine if we processed the complete object - bool isObjectComplete() { return is_complete; } - - protected: - Stream *p_in = nullptr; - Print *p_out = nullptr; - int n_open_read = -1; - int max_object_size = 0; // 0 u - bool is_complete = true; -}; -} // namespace audio_tools \ No newline at end of file diff --git a/src/AudioTools/Communication/RCAudioPlayerOSC.h b/src/AudioTools/Communication/RCAudioPlayerOSC.h deleted file mode 100644 index fc077e8d48..0000000000 --- a/src/AudioTools/Communication/RCAudioPlayerOSC.h +++ /dev/null @@ -1,216 +0,0 @@ - -#include "AudioTools/CoreAudio/AudioPlayer.h" -#include "OSCData.h" - -namespace audio_tools { - -/** - * @brief Audio Player OSC Sender: Remote control for AudioPlayer class. Sends - * OSC messages to the RCAudioPlayerOSCReceiver. - * @ingroup player - * @ingroup communication - * @author Phil Schatzmann - */ -class RCAudioPlayerOSCSender { - RCAudioPlayerOSCSender() = default; - RCAudioPlayerOSCSender(Print &out) { setOutput(out); } - - void setOutput(Print &out) { p_out = &out; } - - bool setActive(bool active) { return active ? play() : stop(); } - - bool play() { - if (p_out == nullptr) return false; - - uint8_t data[20]; - OSCData msg{data, sizeof(data)}; - - msg.setAddress("/play"); - msg.setFormat(""); - p_out->write(msg.data(), msg.size()); - return true; - } - - /// halts the playing: same as setActive(false) - bool stop() { - if (p_out == nullptr) return false; - uint8_t data[20]; - OSCData msg{data, sizeof(data)}; - - msg.setAddress("/stop"); - msg.setFormat(""); - p_out->write(msg.data(), msg.size()); - return true; - } - - /// moves to next file or nth next file when indicating an offset. Negative - /// values are supported to move back. - bool next(int offset = 1) { - if (p_out == nullptr) return false; - uint8_t data[80]; - OSCData msg{data, sizeof(data)}; - - msg.setAddress("/next"); - msg.setFormat("i"); - msg.write((int32_t)offset); - p_out->write(msg.data(), msg.size()); - return true; - } - - bool previous(int offset = 1) { - if (p_out == nullptr) return false; - uint8_t data[80]; - OSCData msg{data, sizeof(data)}; - - msg.setAddress("/previous"); - msg.setFormat("i"); - msg.write((int32_t)offset); - p_out->write(msg.data(), msg.size()); - return true; - } - - /// moves to the selected file position - bool setIndex(int idx) { - if (p_out == nullptr) return false; - uint8_t data[80]; - OSCData msg{data, sizeof(data)}; - - msg.setAddress("/index"); - msg.setFormat("i"); - msg.write((int32_t)idx); - p_out->write(msg.data(), msg.size()); - return true; - } - - /// Moves to the selected file w/o updating the actual file position - bool setPath(const char *path) { - if (p_out == nullptr) return false; - uint8_t data[strlen(path) + 20]; - OSCData msg{data, sizeof(data)}; - - msg.setAddress("/path"); - msg.setFormat("s"); - msg.write(path); - p_out->write(msg.data(), msg.size()); - return true; - } - - bool setVolume(float volume) { - if (p_out == nullptr) return false; - uint8_t data[80]; - OSCData msg{data, sizeof(data)}; - - msg.setAddress("/volume"); - msg.setFormat("f"); - msg.write(volume); - p_out->write(msg.data(), msg.size()); - return true; - } - - protected: - Print *p_out = nullptr; -}; - -/** - * @brief Audio Player OSC Receiver: Receives OSC messages from the - * AudioPlayerOSCSender and applies it to the AudioPlayer. - * @ingroup player - * @ingroup communication - * @author Phil Schatzmann - */ -class RCAudioPlayerOSCReceiver { - RCAudioPlayerOSCReceiver() { registerCallbacks(); } - - RCAudioPlayerOSCReceiver(AudioPlayer &player) : RCAudioPlayerOSCReceiver() { - setAudioPlayer(player); - } - - void setAudioPlayer(AudioPlayer &player) { p_player = &player; } - - /// Process the incoming OSC messages - bool processInputMessage(Stream &in) { - if (!is_active) return false; - - uint8_t data[80]; - size_t len = in.readBytes(data, sizeof(data)); - if (len > 0) { - if (osc.parse(data, len)) { - return true; - } - } - return false; - } - - /// Starts the processing of the incoming OSC messages - bool begin() { - if (p_player == nullptr) { - LOGE("RCAudioPlayerOSCReceiver: player is null"); - return false; - } - osc.setReference(p_player); - is_active = true; - return true; - } - - /// stops the processing of the incoming OSC messages - void end() { - is_active = false; - osc.clear(); - } - - protected: - AudioPlayer *p_player = nullptr; - bool is_active = false; - OSCData osc; - - static bool play(OSCData &data, void *ref) { - AudioPlayer *p_player = (AudioPlayer *)ref; - p_player->play(); - return true; - } - - static bool stop(OSCData &data, void *ref) { - AudioPlayer *p_player = (AudioPlayer *)ref; - p_player->stop(); - return true; - } - - static bool next(OSCData &data, void *ref) { - AudioPlayer *p_player = (AudioPlayer *)ref; - int offset = data.readInt32(); - return p_player->next(offset); - } - - static bool previous(OSCData &data, void *ref) { - AudioPlayer *p_player = (AudioPlayer *)ref; - int offset = data.readInt32(); - return p_player->previous(offset); - } - static bool setIndex(OSCData &data, void *ref) { - AudioPlayer *p_player = (AudioPlayer *)ref; - int idx = data.readInt32(); - return p_player->setIndex(idx); - } - static bool setPath(OSCData &data, void *ref) { - AudioPlayer *p_player = (AudioPlayer *)ref; - const char *path = data.readString(); - return p_player->setPath(path); - } - static bool setVolume(OSCData &data, void *ref) { - AudioPlayer *p_player = (AudioPlayer *)ref; - float volume = data.readFloat(); - return p_player->setVolume(volume); - } - - void registerCallbacks() { - osc.addCallback("/play", play, OSCCompare::StartsWith); - osc.addCallback("/stop", stop, OSCCompare::StartsWith); - osc.addCallback("/next", next, OSCCompare::StartsWith); - osc.addCallback("/previous", previous, OSCCompare::StartsWith); - osc.addCallback("/index", setIndex, OSCCompare::StartsWith); - osc.addCallback("/path", setPath, OSCCompare::StartsWith); - osc.addCallback("/volume", setVolume, OSCCompare::StartsWith); - } -}; - -} // namespace audio_tools diff --git a/src/AudioTools/Communication/README.md b/src/AudioTools/Communication/README.md deleted file mode 100644 index 97e4b99ed1..0000000000 --- a/src/AudioTools/Communication/README.md +++ /dev/null @@ -1,2 +0,0 @@ - -Different classes to send and receive audio over the wire \ No newline at end of file diff --git a/src/AudioTools/Communication/RadioHeadStream.h b/src/AudioTools/Communication/RadioHeadStream.h deleted file mode 100644 index 13e83691cc..0000000000 --- a/src/AudioTools/Communication/RadioHeadStream.h +++ /dev/null @@ -1,72 +0,0 @@ -#pragma once -#include "AudioTools/CoreAudio/BaseStream.h" -#include "RHGenericDriver.h" - -namespace audio_tools { - -/** - * @brief Arduino Stream which is using the RadioHead library to send and - * receive data. We use the river API directly. - * @ingroup communications - * @author Phil Schatzmann - * @copyright GPLv3 - */ -class ReadioHeadStream : public Stream { - public: - ReadioHeadStream(RHGenericDriver &driver) { setDriver(driver); } - - void setDriver(RHGenericDriver &driver) { p_driver = &driver; } - - bool begin() { - if (p_driver == nullptr) return false; - p_driver->setMode(mode == RX_MODE ? RHGenericDriver::RHMode::RHModeRx - : RHGenericDriver::RHMode::RHModeTx); - return p_driver->init(); - } - - void end() { p_driver->setMode(RHGenericDriver::RHMode::RHModeSleep); } - - int available() override { - if (mode == TX_MODE) return 0; - return p_driver->available() ? p_driver->maxMessageLength() : 0; - } - - size_t readBytes(uint8_t *data, size_t len) override { - if (mode == TX_MODE) return 0; - int open = len; - int processed = 0; - while (open > 0) { - uint8_t av = available(); - if (av == 0) break; - p_driver->recv(data + processed, &av); - open -= av; - processed += av; - } - return processed; - } - - int availableForWrite() override { - if (mode == RX_MODE) return 0; - return p_driver->maxMessageLength(); - } - - size_t write(const uint8_t *data, size_t len) override { - if (mode == RX_MODE) return 0; - int open = len; - int processed = 0; - while (open > 0) { - int av = available(); - if (av == 0) break; - p_driver->send(data + processed, av); - open -= av; - processed += av; - } - return processed; - } - - protected: - RHGenericDriver *p_driver = nullptr; - RxTxMode mode; -}; - -} // namespace audio_tools \ No newline at end of file diff --git a/src/AudioTools/Communication/RedisBuffer.h b/src/AudioTools/Communication/RedisBuffer.h deleted file mode 100644 index 00ab0aa4cb..0000000000 --- a/src/AudioTools/Communication/RedisBuffer.h +++ /dev/null @@ -1,317 +0,0 @@ -#pragma once -#include "AudioTools/CoreAudio/Buffers.h" - -namespace audio_tools { - -/** - * @brief Buffer implementation that stores and retrieves data from a Redis server using Arduino Client. - * - * This buffer uses a Redis list as a circular buffer and batches read/write operations for efficiency. - * Individual write/read calls are buffered locally using SingleBuffer and only sent to Redis in bulk - * when writeArray/readArray is called or when the buffer is full/empty. This reduces network overhead - * and improves performance for streaming scenarios. - * - * - Uses RPUSH for writing and LRANGE/LTRIM for reading from Redis. - * - All Redis commands are constructed using the RESP protocol and sent via the Arduino Client API. - * - The buffer size for local batching can be configured via the constructor. - * - Supports automatic expiration of the Redis key after a specified number of seconds. - * - * @tparam T Data type to buffer (e.g., uint8_t, int16_t) - * @ingroup buffers - */ -template -class RedisBuffer : public BaseBuffer { - public: - /** - * @brief Constructs a RedisBuffer. - * @param client Reference to a connected Arduino Client (e.g., WiFiClient, EthernetClient). - * @param key Redis key to use for the buffer (list). - * @param max_size Maximum number of elements in the buffer. - * @param local_buf_size Size of the local buffer for batching (default: 32). - * @param expire_seconds Number of seconds after which the Redis key should expire (0 = no expiration). - */ - RedisBuffer(Client& client, const String& key, size_t max_size, size_t local_buf_size = 32, int expire_seconds = 0) - : client(client), key(key), max_size(max_size), local_buf_size(local_buf_size), expire_seconds(expire_seconds), - write_buf(local_buf_size), read_buf(local_buf_size) {} - - /** - * @brief Sets the expiration time (in seconds) for the Redis key. - * The expiration will be refreshed on every write/flush. - * @param seconds Expiration time in seconds (0 = no expiration). - */ - void setExpire(int seconds) { expire_seconds = seconds; } - - /** - * @brief Buffers a single value for writing to Redis. - * Data is only sent to Redis when the local buffer is full or writeArray is called. - * @param data Value to write. - * @return true if buffered successfully. - */ - bool write(T data) override { - write_buf.write(data); - if (write_buf.isFull()) { - flushWrite(); - } - return true; - } - - /** - * @brief Writes multiple values to Redis in one batch. - * Flushes any pending writes before sending the new data. - * @param data Array of values to write. - * @param len Number of values to write. - * @return Number of values written. - */ - int writeArray(const T data[], int len) override { - flushWrite(); // flush any pending writes first - int written = 0; - for (int i = 0; i < len; ++i) { - write_buf.write(data[i]); - if (write_buf.isFull()) { - flushWrite(); - } - ++written; - } - flushWrite(); // flush remaining - return written; - } - - /** - * @brief Reads a single value from the buffer. - * If the local read buffer is empty, fetches a batch from Redis. - * Flushes any pending writes before reading. - * @param result Reference to store the read value. - * @return true if a value was read, false otherwise. - */ - bool read(T& result) override { - if (read_buf.isEmpty()) { - flushWrite(); // flush any pending writes before reading - fillReadBuffer(); - } - if (read_buf.isEmpty()) return false; - read_buf.read(result); - return true; - } - - /** - * @brief Reads multiple values from the buffer in one batch. - * Flushes any pending writes before reading. - * @param data Array to store the read values. - * @param len Maximum number of values to read. - * @return Number of values actually read. - */ - int readArray(T data[], int len) override { - flushWrite(); // flush any pending writes before reading - int read_count = 0; - while (read_count < len) { - if (read_buf.isEmpty()) { - fillReadBuffer(); - if (read_buf.isEmpty()) break; // nothing left in Redis - } - read_buf.read(data[read_count++]); - } - return read_count; - } - - /** - * @brief Peeks at the next value in the buffer without removing it. - * If the local read buffer is empty, fetches a batch from Redis. - * Flushes any pending writes before peeking. - * @param result Reference to store the peeked value. - * @return true if a value was available, false otherwise. - */ - bool peek(T& result) override { - if (read_buf.isEmpty()) { - flushWrite(); - fillReadBuffer(); - } - if (read_buf.isEmpty()) return false; - return read_buf.peek(result); - } - - /** - * @brief Clears the buffer both locally and on the Redis server. - * Flushes any pending writes before clearing. - */ - void reset() override { - flushWrite(); - String cmd = redisCommand("DEL", key); - sendCommand(cmd); - read_buf.reset(); - write_buf.reset(); - } - - /** - * @brief Returns the number of elements available to read (local + Redis). - * Flushes any pending writes before checking. - * @return Number of available elements. - */ - int available() override { - flushWrite(); - String cmd = redisCommand("LLEN", key); - if (!sendCommand(cmd)) return 0; - String resp = readResponse(); - return resp.toInt() + read_buf.available(); - } - - /** - * @brief Returns the number of elements that can be written before reaching max_size. - * @return Number of available slots for writing. - */ - int availableForWrite() override { - return max_size - available(); - } - - /** - * @brief Returns the address of the start of the physical read buffer (not supported). - * @return nullptr. - */ - T* address() override { return nullptr; } - - /** - * @brief Returns the maximum capacity of the buffer. - * @return Maximum number of elements. - */ - size_t size() override { return max_size; } - - /** - * @brief Resizes the maximum buffer size. - * @param size New maximum size. - * @return true if resized. - */ - bool resize(int size) override { - max_size = size; - return true; - } - - protected: - Client& client; ///< Reference to the Arduino Client for Redis communication. - String key; ///< Redis key for the buffer. - size_t max_size; ///< Maximum number of elements in the buffer. - size_t local_buf_size; ///< Local buffer size for batching. - int expire_seconds = 0; ///< Expiration time in seconds (0 = no expiration). - SingleBuffer write_buf; ///< Local buffer for pending writes. - SingleBuffer read_buf; ///< Local buffer for pending reads. - - /** - * @brief Constructs a Redis command in RESP format. - * @param cmd Redis command (e.g., "RPUSH"). - * @param arg1 First argument. - * @param arg2 Second argument. - * @param arg3 Third argument. - * @return Command string in RESP format. - */ - String redisCommand(const String& cmd, const String& arg1 = "", const String& arg2 = "", const String& arg3 = "") { - String out = "*" + String(1 + (arg1.length() > 0) + (arg2.length() > 0) + (arg3.length() > 0)) + "\r\n"; - out += "$" + String(cmd.length()) + "\r\n" + cmd + "\r\n"; - if (arg1.length()) out += "$" + String(arg1.length()) + "\r\n" + arg1 + "\r\n"; - if (arg2.length()) out += "$" + String(arg2.length()) + "\r\n" + arg2 + "\r\n"; - if (arg3.length()) out += "$" + String(arg3.length()) + "\r\n" + arg3 + "\r\n"; - return out; - } - - /** - * @brief Sends a command to the Redis server. - * @param cmd Command string in RESP format. - * @return true if sent successfully. - */ - bool sendCommand(const String& cmd) { - client.print(cmd); - client.flush(); - return true; - } - - /** - * @brief Reads a single line response from the Redis server. - * @return Response string. - */ - String readResponse() { - String line = ""; - unsigned long start = millis(); - while (client.connected() && (millis() - start < 1000)) { - if (client.available()) { - char c = client.read(); - if (c == '\r') continue; - if (c == '\n') break; - line += c; - } - } - // Remove RESP prefix if present - if (line.length() && (line[0] == ':' || line[0] == '$' || line[0] == '+')) { - int idx = 1; - while (idx < line.length() && (line[idx] < '0' || line[idx] > '9')) ++idx; - return line.substring(idx); - } - return line; - } - - /** - * @brief Flushes buffered writes to Redis using RPUSH and sets expiration if configured. - */ - void flushWrite() { - if (write_buf.isEmpty()) return; - // Use RPUSH with multiple arguments - String cmd = "*" + String(2 + write_buf.available()) + "\r\n"; - cmd += "$5\r\nRPUSH\r\n"; - cmd += "$" + String(key.length()) + "\r\n" + key + "\r\n"; - T value; - for (int i = 0; i < write_buf.available(); ++i) { - write_buf.peek(value); // always peeks the first - String sval = String(value); - cmd += "$" + String(sval.length()) + "\r\n" + sval + "\r\n"; - write_buf.read(value); // remove after sending - } - sendCommand(cmd); - - // Set expiration if needed - if (expire_seconds > 0) { - String expireCmd = redisCommand("EXPIRE", key, String(expire_seconds)); - sendCommand(expireCmd); - } - } - - /** - * @brief Fills the local read buffer from Redis using LRANGE. - * After reading, removes the items from Redis using LTRIM. - */ - void fillReadBuffer() { - // Read up to local_buf_size items from Redis - String cmd = redisCommand("LRANGE", key, "0", String(local_buf_size - 1)); - if (!sendCommand(cmd)) return; - // Parse RESP array - int count = 0; - String line; - while (client.connected() && count < local_buf_size) { - line = ""; - unsigned long start = millis(); - while (client.connected() && (millis() - start < 1000)) { - if (client.available()) { - char c = client.read(); - if (c == '\r') continue; - if (c == '\n') break; - line += c; - } - } - if (line.length() == 0) break; - if (line[0] == '$') { - int len = line.substring(1).toInt(); - if (len <= 0) break; - String value = ""; - while (value.length() < len) { - if (client.available()) value += (char)client.read(); - } - client.read(); // \r - client.read(); // \n - read_buf.write((T)value.toInt()); - ++count; - } - } - // Remove the read items from Redis - if (count > 0) { - String ltrimCmd = redisCommand("LTRIM", key, String(count), "-1"); - sendCommand(ltrimCmd); - } - } -}; - -} \ No newline at end of file diff --git a/src/AudioTools/Communication/ReedSolomonFEC.h b/src/AudioTools/Communication/ReedSolomonFEC.h deleted file mode 100644 index 262696f593..0000000000 --- a/src/AudioTools/Communication/ReedSolomonFEC.h +++ /dev/null @@ -1,70 +0,0 @@ -#pragma once - -#include "AudioToolsConfig.h" -#include "AudioTools/CoreAudio/BaseStream.h" -#include "FEC/ReedSolomon/rs.hpp" - -namespace audio_tools { - -/** - * @brief Forward error correction using Reed-Solomon: - * write is encoding and readBytes does the decoding. - * @ingroup fec - * @author Phil Schatzmann - * @copyright GPLv3 - **/ -template -class ReedSolomonFEC : public BaseStream { - public: - - ReedSolomonFEC(Stream &stream){ - p_stream = &stream; - p_print = &stream; - } - - ReedSolomonFEC(Print &print){ - p_print = &print; - } - - int availableForWrite() override { - return bytecount; - } - - size_t write(const uint8_t* data, size_t len) override { - if (p_print==nullptr) return 0; - for (int j=0;jwrite(encoded.data(), bytecount+additional_bytes); - raw.reset(); - } - } - return len; - } - - int available()override { - return bytecount; - } - - size_t readBytes(uint8_t* data, size_t len) override{ - if (p_stream==nullptr) return 0; - if (encoded.isEmpty()){ - int read_len = bytecount + additional_bytes; - p_stream->readBytes(encoded.data(), read_len); - encoded.setAvailable(read_len); - } - return encoded.readArray(data, len); - } - - - protected: - SingleBuffer raw{bytecount}; - SingleBuffer encoded{bytecount+additional_bytes}; - RS::ReedSolomon rs; - Stream* p_stream = nullptr; - Print* p_print = nullptr; - -}; - -} \ No newline at end of file diff --git a/src/AudioTools/Communication/UDPStream.h b/src/AudioTools/Communication/UDPStream.h deleted file mode 100644 index e326c1a6b4..0000000000 --- a/src/AudioTools/Communication/UDPStream.h +++ /dev/null @@ -1,160 +0,0 @@ -#pragma once -#include -#include -#if defined(ESP32) -# include -#endif -#include "AudioTools/CoreAudio/BaseStream.h" -#include "AudioTools/CoreAudio/Buffers.h" - -namespace audio_tools { - -/** - * @brief A UDP class which makes sure that we can use UDP as - * AudioSource and AudioSink. By default the WiFiUDP object is used and we login - * to wifi if the ssid and password is provided and we are not already - * connected. - * @ingroup communications - * @author Phil Schatzmann - * @copyright GPLv3 - */ - -class UDPStream : public BaseStream { -public: - /// Default Constructor - UDPStream() = default; - - /// @brief Convinience constructor which defines the optional ssid and - /// password - /// @param ssid - /// @param password - UDPStream(const char *ssid, const char *password) { - setSSID(ssid); - setPassword(password); - } - - /// @brief Constructor which defines an alternative UDP object. By default we - /// use WiFiUDP - /// @param udp - UDPStream(UDP &udp) { setUDP(udp); } - - /// @brief Defines an alternative UDP object. By default we use WiFiUDP - /// @param udp - void setUDP(UDP &udp) { p_udp = &udp; }; - - /// Always return 1492 (MTU 1500 - 8 byte header) as UDP packet available to - /// write - int availableForWrite() { return 1492; } - - /** - * Provides the available size of the current package and if this is used up - * of the next package - */ - int available() override { - int size = p_udp->available(); - // if the curren package is used up we prvide the info for the next - if (size == 0) { - size = p_udp->parsePacket(); - } - return size; - } - - /// Starts to send data to the indicated address / port - bool begin(IPAddress a, uint16_t port) { - connect(); - remote_address_ext = a; - remote_port_ext = port; - return p_udp->begin(port); - } - - /// Starts to receive data from/with the indicated port - bool begin(uint16_t port, uint16_t port_ext = 0) { - connect(); - remote_address_ext = IPAddress((uint32_t)0); - remote_port_ext = port_ext != 0 ? port_ext : port; - printIP(); - return p_udp->begin(port); - } - - /// Starts to receive data in multicast from/with the indicated address / port - bool beginMulticast(IPAddress address, uint16_t port) { - connect(); - return p_udp->beginMulticast(address,port); - } - - /// We use the same remote port as defined in begin for write - uint16_t remotePort() { - uint16_t result = p_udp->remotePort(); - return result != 0 ? result : remote_port_ext; - } - - /// We use the same remote ip as defined in begin for write - IPAddress remoteIP() { - // Determine address if it has not been specified - if ((uint32_t)remote_address_ext == 0) { - remote_address_ext = p_udp->remoteIP(); - } - // IPAddress result = p_udp->remoteIP(); - // LOGI("ip: %u", result); - return remote_address_ext; - } - - /// Replys will be sent to the initial remote caller - size_t write(const uint8_t *data, size_t len) override { - TRACED(); - p_udp->beginPacket(remoteIP(), remotePort()); - size_t result = p_udp->write(data, len); - p_udp->endPacket(); - return result; - } - - /// Reads bytes using WiFi::readBytes - size_t readBytes(uint8_t *data, size_t len) override { - TRACED(); - size_t avail = available(); - size_t bytes_read = 0; - if (avail > 0) { - // get the data now - bytes_read = p_udp->readBytes((uint8_t *)data, len); - } - return bytes_read; - } - - void setSSID(const char *ssid) { this->ssid = ssid; } - - void setPassword(const char *pwd) { this->password = pwd; } - -protected: - WiFiUDP default_udp; - UDP *p_udp = &default_udp; - uint16_t remote_port_ext = 0; - IPAddress remote_address_ext; - const char *ssid = nullptr; - const char *password = nullptr; - - void printIP(){ - Serial.print(WiFi.localIP()); - Serial.print(":"); - Serial.println(remote_port_ext); - } - - /// connect to WIFI if necessary - void connect() { - if (WiFi.status() != WL_CONNECTED && ssid != nullptr && - password != nullptr) { - WiFi.begin(ssid, password); - while (WiFi.status() != WL_CONNECTED) { - delay(500); - } - } -#if defined(ESP32) - if (WiFi.status() == WL_CONNECTED) { - esp_wifi_set_ps(WIFI_PS_NONE); - // Performance Hack - // client.setNoDelay(true); - } -#endif - } -}; - -} // namespace audio_tools diff --git a/src/AudioTools/Communication/WebSocketOutput.h b/src/AudioTools/Communication/WebSocketOutput.h deleted file mode 100644 index 763a3dd468..0000000000 --- a/src/AudioTools/Communication/WebSocketOutput.h +++ /dev/null @@ -1,60 +0,0 @@ -#pragma once -#include "AudioTools/CoreAudio/AudioOutput.h" -#include "WebSocketsClient.h" // https://github.com/Links2004/arduinoWebSockets -#include "WebSocketsServer.h" // https://github.com/Links2004/arduinoWebSockets - -namespace audio_tools { - -/** - * @brief A simple wrapper class that lets you use the standard - * Arduino Print class output commands to send audio data over a WebSocket - * connection. - * Uses https://github.com/Links2004/arduinoWebSockets - * @ingroup communications - * @author Phil Schatzmann - * @copyright GPLv3 - */ - -class WebSocketOutput : public AudioOutput { - public: - WebSocketOutput() = default; - /// @brief Constructor which defines an alternative WebSocket object. By - /// default we use WebSocket - WebSocketOutput(WebSocketsClient &ws) { setWebSocket(ws); } - /// @brief Constructor which defines an alternative WebSocket object. By - /// default we use WebSocket - WebSocketOutput(WebSocketsServer &ws) { setWebSocket(ws); } - - /// @brief Defines an alternative WebSocket object. By default we use - /// WebSocket - void setWebSocket(WebSocketsClient &ws) { p_ws = &ws; }; - /// @brief Defines an alternative WebSocket object. By default we use - /// WebSocket - void setWebSocket(WebSocketsServer &ws) { p_ws_server = &ws; }; - - /// Replys will be sent to the initial remote caller - size_t write(const uint8_t *data, size_t len) override { - bool rc = false; - if (p_ws != nullptr) rc = p_ws->sendBIN(data, len); - if (p_ws_server != nullptr) { - if (clientNo >= 0) { - rc = p_ws_server->sendBIN(clientNo, data, len); - } else { - // broadcast to all clients - rc = p_ws_server->broadcastBIN(data, len); - } - rc = p_ws_server->broadcastBIN(data, len); - } - return rc ? len : 0; - } - - /// For WebSocketServer we can define an individual recipient! - void setTargetNo(int clientNo) { this->clientNo = clientNo; } - - protected: - WebSocketsClient *p_ws = nullptr; - WebSocketsServer *p_ws_server = nullptr; - int clientNo = -1; -}; - -} // namespace audio_tools diff --git a/src/AudioTools/Concurrency/LockGuard.h b/src/AudioTools/Concurrency/LockGuard.h deleted file mode 100644 index b043384535..0000000000 --- a/src/AudioTools/Concurrency/LockGuard.h +++ /dev/null @@ -1,37 +0,0 @@ -#pragma once -#include "AudioToolsConfig.h" -#include "Mutex.h" - -namespace audio_tools { - -/** - * @brief RAII implementaion using a Mutex: Only a few microcontrollers provide - * lock guards, so I decided to roll my own solution where we can just use a - * dummy Mutex implementation that does nothing for the cases where this is not - * needed. - * @ingroup concurrency - * @author Phil Schatzmann - * @copyright GPLv3 * - */ - -class LockGuard { - public: - LockGuard(MutexBase &mutex) { - p_mutex = &mutex; - p_mutex->lock(); - } - - LockGuard(MutexBase *mutex) { - p_mutex = mutex; - if (p_mutex != nullptr) p_mutex->lock(); - } - - ~LockGuard() { - if (p_mutex != nullptr) p_mutex->unlock(); - } - - protected: - MutexBase *p_mutex = nullptr; -}; - -} // namespace audio_tools diff --git a/src/AudioTools/Concurrency/Mutex.h b/src/AudioTools/Concurrency/Mutex.h deleted file mode 100644 index 99cd75e2f1..0000000000 --- a/src/AudioTools/Concurrency/Mutex.h +++ /dev/null @@ -1,74 +0,0 @@ -#pragma once - -#include "AudioToolsConfig.h" - -#ifdef USE_STD_CONCURRENCY -#include -#include -#endif - -namespace audio_tools { - -/** - * @brief Empty Mutex implementation which does nothing - * @ingroup concurrency - * @author Phil Schatzmann - * @copyright GPLv3 - */ -class MutexBase { - public: - virtual void lock() {} - virtual void unlock() {} -}; - -#if defined(USE_STD_CONCURRENCY) - -class SpinLock : public MutexBase { - void lock() { - for (;;) { - // Optimistically assume the lock is free on the first try - if (!lock_.exchange(true, std::memory_order_acquire)) { - return; - } - // Wait for lock to be released without generating cache misses - while (lock_.load(std::memory_order_relaxed)) { - // Issue X86 PAUSE or ARM YIELD instruction to reduce contention between - // hyper-threads - //__builtin_ia32_pause(); - delay(1); - } - } - } - - bool try_lock() { - // First do a relaxed load to check if lock is free in order to prevent - // unnecessary cache misses if someone does while(!try_lock()) - return !lock_.load(std::memory_order_relaxed) && - !lock_.exchange(true, std::memory_order_acquire); - } - - void unlock() { lock_.store(false, std::memory_order_release); } - - protected: - volatile std::atomic lock_ = {0}; -}; - - -/** - * @brief Mutex implemntation based on std::mutex - * @ingroup concurrency - * @author Phil Schatzmann - * @copyright GPLv3 - */ -class StdMutex : public MutexBase { - public: - void lock() override { std_mutex.lock(); } - void unlock() override { std_mutex.unlock(); } - - protected: - std::mutex std_mutex; -}; - -#endif - -} // namespace audio_tools \ No newline at end of file diff --git a/src/AudioTools/Concurrency/QueueLockFree.h b/src/AudioTools/Concurrency/QueueLockFree.h deleted file mode 100644 index 4adc594a2b..0000000000 --- a/src/AudioTools/Concurrency/QueueLockFree.h +++ /dev/null @@ -1,111 +0,0 @@ - -#pragma once -#include - -#include -#include - -namespace audio_tools { - -/** - * @brief A simple single producer, single consumer lock free queue - */ -template -class QueueLockFree { - public: - QueueLockFree(size_t capacity, Allocator& allocator = DefaultAllocator) { - setAllocator(allocator); - resize(capacity); - } - - ~QueueLockFree() { - for (size_t i = head_pos; i != tail_pos; ++i) - (&p_node[i & capacity_mask].data)->~T(); - - //delete[] (char*)p_node; - } - - void setAllocator(Allocator& allocator) { vector.setAllocator(allocator); } - - void resize(size_t capacity) { - capacity_mask = capacity - 1; - for (size_t i = 1; i <= sizeof(void*) * 4; i <<= 1) - capacity_mask |= capacity_mask >> i; - capacity_value = capacity_mask + 1; - - vector.resize(capacity); - p_node = vector.data(); - - for (size_t i = 0; i < capacity; ++i) { - p_node[i].tail.store(i, std::memory_order_relaxed); - p_node[i].head.store(-1, std::memory_order_relaxed); - } - - tail_pos.store(0, std::memory_order_relaxed); - head_pos.store(0, std::memory_order_relaxed); - } - - size_t capacity() const { return capacity_value; } - - bool empty() { return size() == 0;} - - size_t size() const { - size_t head = head_pos.load(std::memory_order_acquire); - return tail_pos.load(std::memory_order_relaxed) - head; - } - - bool enqueue(const T&& data) { - return enqueue(data); - } - - bool enqueue(const T& data) { - Node* node; - size_t tail = tail_pos.load(std::memory_order_relaxed); - for (;;) { - node = &p_node[tail & capacity_mask]; - if (node->tail.load(std::memory_order_relaxed) != tail) return false; - if ((tail_pos.compare_exchange_weak(tail, tail + 1, - std::memory_order_relaxed))) - break; - } - new (&node->data) T(data); - node->head.store(tail, std::memory_order_release); - return true; - } - - bool dequeue(T& result) { - Node* node; - size_t head = head_pos.load(std::memory_order_relaxed); - for (;;) { - node = &p_node[head & capacity_mask]; - if (node->head.load(std::memory_order_relaxed) != head) return false; - if (head_pos.compare_exchange_weak(head, head + 1, - std::memory_order_relaxed)) - break; - } - result = node->data; - (&node->data)->~T(); - node->tail.store(head + capacity_value, std::memory_order_release); - return true; - } - - void clear() { - T tmp; - while (dequeue(tmp)); - } - - protected: - struct Node { - T data; - std::atomic tail; - std::atomic head; - }; - - Node* p_node = nullptr; - size_t capacity_mask; - size_t capacity_value; - std::atomic tail_pos; - std::atomic head_pos; - Vector vector; -}; -} // namespace audio_tools \ No newline at end of file diff --git a/src/AudioTools/Concurrency/RP2040.h b/src/AudioTools/Concurrency/RP2040.h deleted file mode 100644 index 016581ea16..0000000000 --- a/src/AudioTools/Concurrency/RP2040.h +++ /dev/null @@ -1,6 +0,0 @@ -#pragma once -#include "AudioTools/Concurrency/Mutex.h" -#include "AudioTools/Concurrency/SynchronizedBuffer.h" -#include "AudioTools/Concurrency/SynchronizedQueue.h" -#include "AudioTools/Concurrency/RP2040/BufferRP2040.h" -#include "AudioTools/Concurrency/RP2040/MutexRP2040.h" diff --git a/src/AudioTools/Concurrency/RP2040/BufferRP2040.h b/src/AudioTools/Concurrency/RP2040/BufferRP2040.h deleted file mode 100644 index 1d19f3989d..0000000000 --- a/src/AudioTools/Concurrency/RP2040/BufferRP2040.h +++ /dev/null @@ -1,223 +0,0 @@ -#pragma once - -#ifndef ARDUINO_ARCH_RP2040 -# error "Unsupported architecture" -#endif - -#include "AudioToolsConfig.h" -#include "AudioTools/CoreAudio/AudioLogger.h" -#include "AudioTools/CoreAudio/Buffers.h" - -namespace audio_tools { - -// #if ESP_IDF_VERSION_MAJOR >= 4 - -/** - * @brief Buffer implementation which is based on a RP2040 queue. This - * class is intended to be used to exchange data between the 2 different - * cores. Multi-core and IRQ safe queue implementation! - * - * In order to increase the efficiency we to not enqueue individual items - * but write them into a temporary buffer of bufferSize and write this - * array to the queue when it is full. - * - * @ingroup buffers - * @ingroup concurrency - * @author Phil Schatzmann - * @copyright GPLv3 * - * @tparam T - */ -template -class BufferRP2040T : public BaseBuffer { - public: - BufferRP2040T(int bufferCount) : BaseBuffer() { - buffer_size = 1; - buffer_size_bytes = sizeof(T); - buffer_size_req_bytes = buffer_size_bytes * bufferCount; - } - - BufferRP2040T(size_t bufferSize, int bufferCount) : BaseBuffer() { - buffer_size = bufferSize; - buffer_size_bytes = bufferSize * sizeof(T); - buffer_size_req_bytes = buffer_size_bytes * bufferCount; - } - - ~BufferRP2040T() { reset(); } - - /// Re-Allocats the memory and the queue (size is in entries) - bool resize(size_t size) { - int req_bytes = size * sizeof(T); - if (buffer_size_alloc_bytes < req_bytes) { - LOGI("resize %d -> %d", buffer_size_alloc_bytes / sizeof(T), size); - assert(buffer_size_bytes > 0); - write_buffer.resize(buffer_size); - read_buffer.resize(buffer_size * 2); - // create new queu - if (req_bytes > buffer_size_alloc_bytes) { - // release existing queue - if (buffer_size_alloc_bytes > 0) { - queue_free(&queue); - } - - int count = req_bytes / buffer_size_bytes; - LOGI("queue_init(size:%d, count:%d)", buffer_size_bytes, count); - queue_init(&queue, buffer_size_bytes, count); - buffer_size_alloc_bytes = req_bytes; - } - } - return true; - } - - // reads a single value - bool read(T& data) { - return readArray(&data, 1)==1; - } - - - // peeks the actual entry from the buffer - bool peek(T &result) override { - LOGE("peek not implemented"); - return false; - } - - // reads multiple values - int readArray(T data[], int len) override { - LOGD("readArray: %d", len); - // handle unalloc;ated queue - if (buffer_size_alloc_bytes == 0) return 0; - - // blocking read - while (is_blocking_read && read_buffer.available() + available() < len) - delay(1); - - // fill read buffer if necessary - while (read_buffer.availableForWrite() >= buffer_size) { - LOGD("reading %d %d ", buffer_size, read_buffer.availableForWrite()); - T tmp[buffer_size]; - if (queue_try_remove(&queue, tmp)){ - LOGD("queue_try_remove -> success"); - read_buffer.writeArray(tmp, buffer_size); - } else { - LOGD("queue_try_remove -> failed"); - break; - } - } - LOGD("read_buffer.available: %d, availableForWrite: %d ", read_buffer.available(), read_buffer.availableForWrite()); - int result = read_buffer.readArray(data, len); - LOGD("=> readArray: %d -> %d", len, result); - return result; - } - - int writeArray(const T data[], int len) override { - LOGD("writeArray: %d", len); - int result = 0; - // make sure that we have the data allocated - resize(buffer_size_req_bytes / sizeof(T)); - - if (is_blocking_write) { - result = writeBlocking(data, len); - } else { - result = writeNonBlocking(data, len); - } - - return result; - } - - // checks if the buffer is full - bool isFull() override { - if (buffer_size_alloc_bytes == 0) return false; - return queue_is_full(&queue); - } - - bool isEmpty() { - if (buffer_size_alloc_bytes == 0) return true; - return queue_is_empty(&queue); - } - - // write add an entry to the buffer - bool write(T data) override { return writeArray(&data, 1) == 1; } - - // clears the buffer - void reset() override { - queue_free(&queue); - buffer_size_alloc_bytes = 0; - } - - // provides the number of entries that are available to read - int available() override { - if (buffer_size_alloc_bytes == 0) return 0; - return (queue_get_level(&queue) * buffer_size); - } - - // provides the number of entries that are available to write - int availableForWrite() override { - if (buffer_size_alloc_bytes == 0) return size(); - return size() - available(); } - - // returns the address of the start of the physical read buffer - T *address() override { - LOGE("address() not implemented"); - return nullptr; - } - - size_t size() { return buffer_size_alloc_bytes / sizeof(T); } - - /// When we use a non blocking write, the write size must be identical with the buffer size - void setBlockingWrite(bool flag){ - is_blocking_write = flag; - } - - /// When we use a blockingread, the we wait for the data to be available - void setBlockingRead(bool flag){ - is_blocking_read = flag; - } - - - protected: - queue_t queue; - int buffer_size_alloc_bytes = 0; - int buffer_size_req_bytes = 0; - int buffer_size_bytes = 0; - int buffer_size = 0; - SingleBuffer write_buffer{0}; - audio_tools::RingBuffer read_buffer{0}; - bool is_blocking_write = true; - bool is_blocking_read = false; - - int writeBlocking(const T data[], int len) { - LOGD("writeArray: %d", len); - - if (len > buffer_size_bytes){ - LOGE("write %d too big for buffer_size: %d", len, buffer_size_bytes); - return 0; - } - - // fill the write buffer and when it is full flush it to the queue - for (int j = 0; j < len; j++) { - write_buffer.write(data[j]); - if (write_buffer.isFull()) { - LOGD("queue_add_blocking"); - queue_add_blocking(&queue, write_buffer.data()); - write_buffer.reset(); - } - } - return len; - } - - int writeNonBlocking(const T data[], int len) { - if (len != buffer_size_bytes){ - LOGE("write %d must be buffer_size: %d", len, buffer_size_bytes); - return 0; - } - - if (queue_try_add(&queue, write_buffer.data())){ - return len; - } - return 0; - } - -}; - -using BufferRP2040 = BufferRP2040T; - -} // namespace audio_tools diff --git a/src/AudioTools/Concurrency/RP2040/MutexRP2040.h b/src/AudioTools/Concurrency/RP2040/MutexRP2040.h deleted file mode 100644 index 53627db614..0000000000 --- a/src/AudioTools/Concurrency/RP2040/MutexRP2040.h +++ /dev/null @@ -1,60 +0,0 @@ -#pragma once -#include "AudioLogger.h" -#include "AudioTools/Concurrency/Mutex.h" - -namespace audio_tools { - -/** - * @brief Disable, enable interrupts (only on the actual core) - * @ingroup concurrency - * @author Phil Schatzmann - * @copyright GPLv3 * - */ -class NoInterruptHandler : public MutexBase { - public: - void lock() override { - TRACED(); - noInterrupts(); - } - void unlock() override { - TRACED(); - interrupts(); - } -}; - -/** - * @brief Mutex API for non IRQ mutual exclusion between cores. - * Mutexes are application level locks usually used protecting data structures - * that might be used by multiple threads of execution. Unlike critical - * sections, the mutex protected code is not necessarily required/expected - * to complete quickly, as no other sytem wide locks are held on account of - * an acquired mutex. - * @ingroup concurrency - * @author Phil Schatzmann - * @copyright GPLv3 - */ - -class MutexRP2040 : public MutexBase { - public: - MutexRP2040() { - TRACED(); - mutex_init(&mtx); - } - virtual ~MutexRP2040() = default; - - void lock() override { - TRACED(); - mutex_enter_blocking(&mtx); - } - void unlock() override { - TRACED(); - mutex_exit(&mtx); - } - - protected: - mutex_t mtx; -}; - -using Mutex = MutexRP2040; - -} // namespace audio_tools \ No newline at end of file diff --git a/src/AudioTools/Concurrency/RTOS.h b/src/AudioTools/Concurrency/RTOS.h deleted file mode 100644 index 7c34768b3d..0000000000 --- a/src/AudioTools/Concurrency/RTOS.h +++ /dev/null @@ -1,9 +0,0 @@ -#pragma once -#include "AudioTools/Concurrency/RTOS/QueueRTOS.h" -#include "AudioTools/Concurrency/RTOS/BufferRTOS.h" -#include "AudioTools/Concurrency/RTOS/Task.h" -#include "AudioTools/Concurrency/RTOS/MutexRTOS.h" -#include "AudioTools/Concurrency/RTOS/SynchronizedNBufferRTOS.h" -#include "AudioTools/Concurrency/LockGuard.h" -#include "AudioTools/Concurrency/SynchronizedQueue.h" -#include "AudioTools/Concurrency/SynchronizedBuffer.h" diff --git a/src/AudioTools/Concurrency/RTOS/BufferRTOS.h b/src/AudioTools/Concurrency/RTOS/BufferRTOS.h deleted file mode 100644 index 6b31127833..0000000000 --- a/src/AudioTools/Concurrency/RTOS/BufferRTOS.h +++ /dev/null @@ -1,214 +0,0 @@ -#pragma once -#include "AudioToolsConfig.h" -#include "AudioTools/CoreAudio/Buffers.h" -#include "AudioTools/CoreAudio/AudioLogger.h" -#include "AudioTools/CoreAudio/AudioBasic/Collections/Allocator.h" - -#ifdef ESP32 -# include -# include "freertos/FreeRTOS.h" -#else -# include "FreeRTOS.h" -# include "stream_buffer.h" -#endif - -namespace audio_tools { - -// #if ESP_IDF_VERSION_MAJOR >= 4 - -/** - * @brief Buffer implementation which is using a FreeRTOS StreamBuffer. The - * default allocator uses psram is available. - * @ingroup buffers - * @ingroup concurrency - * @author Phil Schatzmann - * @copyright GPLv3 * - * @tparam T - */ -template -class BufferRTOS : public BaseBuffer { - public: - BufferRTOS(size_t streamBufferSize, size_t xTriggerLevel = 1, - TickType_t writeMaxWait = portMAX_DELAY, - TickType_t readMaxWait = portMAX_DELAY, - Allocator &allocator = DefaultAllocator) - : BaseBuffer() { - readWait = readMaxWait; - writeWait = writeMaxWait; - current_size_bytes = (streamBufferSize+1) * sizeof(T); - trigger_level = xTriggerLevel; - p_allocator = &allocator; - - if (streamBufferSize > 0) { - setup(); - } - } - - ~BufferRTOS() { end(); } - - /// Re-Allocats the memory and the queue - bool resize(size_t size) { - bool result = true; - int req_size_bytes = (size + 1)*sizeof(T); - if (current_size_bytes != req_size_bytes) { - end(); - current_size_bytes = req_size_bytes; - result = setup(); - } - return result; - } - - void setReadMaxWait(TickType_t ticks) { readWait = ticks; } - - void setWriteMaxWait(TickType_t ticks) { writeWait = ticks; } - - void setWriteFromISR(bool active) { write_from_isr = active; } - - void setReadFromISR(bool active) { read_from_isr = active; } - - // reads a single value - bool read(T &result) override { - T data = 0; - return readArray(&data, 1)==1; - } - - // reads multiple values - int readArray(T data[], int len) { - if (read_from_isr) { - xHigherPriorityTaskWoken = pdFALSE; - int result = xStreamBufferReceiveFromISR(xStreamBuffer, (void *)data, - sizeof(T) * len, - &xHigherPriorityTaskWoken); -#ifdef ESP32X - portYIELD_FROM_ISR(); -#else - portYIELD_FROM_ISR(xHigherPriorityTaskWoken); -#endif - return result / sizeof(T); - } else { - return xStreamBufferReceive(xStreamBuffer, (void *)data, sizeof(T) * len, - readWait) / sizeof(T); - } - } - - int writeArray(const T data[], int len) { - LOGD("%s: %d", LOG_METHOD, len); - if (write_from_isr) { - xHigherPriorityTaskWoken = pdFALSE; - int result = - xStreamBufferSendFromISR(xStreamBuffer, (void *)data, sizeof(T) * len, - &xHigherPriorityTaskWoken); -#ifdef ESP32X - portYIELD_FROM_ISR(); -#else - portYIELD_FROM_ISR(xHigherPriorityTaskWoken); -#endif - return result / sizeof(T); - } else { - return xStreamBufferSend(xStreamBuffer, (void *)data, sizeof(T) * len, - writeWait) / sizeof(T); - } - } - - // peeks the actual entry from the buffer - bool peek(T &result) override { - LOGE("peek not implemented"); - return false; - } - - // checks if the buffer is full - bool isFull() override { - return xStreamBufferIsFull(xStreamBuffer) == pdTRUE; - } - - bool isEmpty() { return xStreamBufferIsEmpty(xStreamBuffer) == pdTRUE; } - - // write add an entry to the buffer - bool write(T data) override { - int len = sizeof(T); - return writeArray(&data, len) == len; - } - - // clears the buffer - void reset() override { xStreamBufferReset(xStreamBuffer); } - - // provides the number of entries that are available to read - int available() override { - return xStreamBufferBytesAvailable(xStreamBuffer) / sizeof(T); - } - - // provides the number of entries that are available to write - int availableForWrite() override { - return xStreamBufferSpacesAvailable(xStreamBuffer) / sizeof(T); - } - - // returns the address of the start of the physical read buffer - T *address() override { - LOGE("address() not implemented"); - return nullptr; - } - - size_t size() { return current_size_bytes / sizeof(T); } - - operator bool() { return xStreamBuffer != nullptr && size()>0;} - - protected: - StreamBufferHandle_t xStreamBuffer = nullptr; - StaticStreamBuffer_t static_stream_buffer; - uint8_t *p_data = nullptr; - Allocator *p_allocator = nullptr; - BaseType_t xHigherPriorityTaskWoken = pdFALSE; // Initialised to pdFALSE. - int readWait = portMAX_DELAY; - int writeWait = portMAX_DELAY; - bool read_from_isr = false; - bool write_from_isr = false; - size_t current_size_bytes = 0; - size_t trigger_level = 0; - - /// The allocation has been postponed to be done here, so that we can e.g. use - /// psram - bool setup() { - if (current_size_bytes == 0) return true; - - // allocate data if necessary - int size = (current_size_bytes + 1) * sizeof(T); - if (p_data == nullptr) { - p_data = (uint8_t *)p_allocator->allocate(size); - // check allocation - if (p_data == nullptr) { - LOGE("allocate falied for %d bytes", size) - return false; - } - } - - - // create stream buffer if necessary - if (xStreamBuffer == nullptr) { - xStreamBuffer = xStreamBufferCreateStatic(current_size_bytes, trigger_level, - p_data, &static_stream_buffer); - } - if (xStreamBuffer == nullptr) { - LOGE("xStreamBufferCreateStatic failed"); - return false; - } - // make sure that the data is empty - reset(); - return true; - } - - /// Release resurces: call resize to restart again - void end() { - if (xStreamBuffer != nullptr) vStreamBufferDelete(xStreamBuffer); - p_allocator->free(p_data); - current_size_bytes = 0; - p_data = nullptr; - xStreamBuffer = nullptr; - } -}; -// #endif // ESP_IDF_VERSION_MAJOR >= 4 - -template -using SynchronizedBufferRTOS = BufferRTOS; - -} // namespace audio_tools - diff --git a/src/AudioTools/Concurrency/RTOS/MutexRTOS.h b/src/AudioTools/Concurrency/RTOS/MutexRTOS.h deleted file mode 100644 index aa8f66c652..0000000000 --- a/src/AudioTools/Concurrency/RTOS/MutexRTOS.h +++ /dev/null @@ -1,43 +0,0 @@ -#pragma once -#include "AudioToolsConfig.h" -#include "AudioTools/Concurrency/Mutex.h" - -#ifdef ESP32 -# include "freertos/FreeRTOS.h" -# include "freertos/semphr.h" -#else -# include "FreeRTOS.h" -# include "semphr.h" -#endif - -namespace audio_tools { - -/** - * @brief Mutex implemntation using FreeRTOS - * @ingroup concurrency - * @author Phil Schatzmann - * @copyright GPLv3 * - */ -class MutexRTOS : public MutexBase { -public: - MutexRTOS() { - xSemaphore = xSemaphoreCreateBinary(); - xSemaphoreGive(xSemaphore); - } - virtual ~MutexRTOS() { - vSemaphoreDelete(xSemaphore); - } - void lock() override { - xSemaphoreTake(xSemaphore, portMAX_DELAY); - } - void unlock() override { - xSemaphoreGive(xSemaphore); - } - -protected: - SemaphoreHandle_t xSemaphore = NULL; -}; - -using Mutex = MutexRTOS; - -} \ No newline at end of file diff --git a/src/AudioTools/Concurrency/RTOS/QueueRTOS.h b/src/AudioTools/Concurrency/RTOS/QueueRTOS.h deleted file mode 100644 index 7347ad9f48..0000000000 --- a/src/AudioTools/Concurrency/RTOS/QueueRTOS.h +++ /dev/null @@ -1,118 +0,0 @@ -#pragma once -#include "AudioTools/CoreAudio/AudioBasic/Collections/Allocator.h" -#include "AudioToolsConfig.h" - -#ifdef ESP32 -# include -# include "freertos/FreeRTOS.h" -#else -# include "FreeRTOS.h" -# include "queue.h" -#endif - -namespace audio_tools { - -/** - * @brief FIFO Queue whch is based on the FreeRTOS queue API. - * The default allocator will allocate the memory from psram if available - * @ingroup collections - * @ingroup concurrency - * @author Phil Schatzmann - * @copyright GPLv3 - * @tparam T - */ -template -class QueueRTOS { - public: - QueueRTOS(int size, TickType_t writeMaxWait = portMAX_DELAY, - TickType_t readMaxWait = portMAX_DELAY, - Allocator& allocator = DefaultAllocator) { - TRACED(); - p_allocator = &allocator; - read_max_wait = readMaxWait; - write_max_wait = writeMaxWait; - queue_size = size; - setup(); - }; - - ~QueueRTOS() { - TRACED(); - end(); - } - - void setReadMaxWait(TickType_t ticks) { read_max_wait = ticks; } - - void setWriteMaxWait(TickType_t ticks) { write_max_wait = ticks; } - - /// (Re-)defines the size - bool resize(int size) { - bool result = true; - TRACED(); - if (size != queue_size) { - end(); - queue_size = size; - result = setup(); - } - return result; - } - - bool enqueue(T& data) { - TRACED(); - if (xQueue==nullptr) return false; - return xQueueSend(xQueue, (void*)&data, (TickType_t)write_max_wait); - } - - bool peek(T& data) { - TRACED(); - if (xQueue==nullptr) return false; - return xQueuePeek(xQueue, &data, (TickType_t)read_max_wait); - } - - bool dequeue(T& data) { - TRACED(); - if (xQueue==nullptr) return false; - return xQueueReceive(xQueue, &data, (TickType_t)read_max_wait); - } - - size_t size() { return queue_size; } - - bool clear() { - TRACED(); - if (xQueue==nullptr) return false; - xQueueReset(xQueue); - return true; - } - - bool empty() { return size() == 0; } - - protected: - QueueHandle_t xQueue = nullptr; - TickType_t write_max_wait = portMAX_DELAY; - TickType_t read_max_wait = portMAX_DELAY; - Allocator* p_allocator = nullptr; - int queue_size; - uint8_t* p_data = nullptr; - StaticQueue_t queue_buffer; - - bool setup() { - if (queue_size > 0) { -#if configSUPPORT_STATIC_ALLOCATION - p_data = (uint8_t*)p_allocator->allocate((queue_size + 1) * sizeof(T)); - if (p_data == nullptr) return false; - xQueue = xQueueCreateStatic(queue_size, sizeof(T), p_data, &queue_buffer); -#else - xQueue = xQueueCreate(queue_size, sizeof(T)); -#endif - if (xQueue == nullptr) return false; - } - return true; - } - - void end() { - if (xQueue != nullptr) vQueueDelete(xQueue); - if (p_data != nullptr) p_allocator->free(p_data); - } -}; - -} // namespace audio_tools - diff --git a/src/AudioTools/Concurrency/RTOS/SynchronizedNBufferRTOS.h b/src/AudioTools/Concurrency/RTOS/SynchronizedNBufferRTOS.h deleted file mode 100644 index d23cb4f72b..0000000000 --- a/src/AudioTools/Concurrency/RTOS/SynchronizedNBufferRTOS.h +++ /dev/null @@ -1,124 +0,0 @@ -#pragma once -#include "AudioToolsConfig.h" -#include "QueueRTOS.h" - -namespace audio_tools { - -/** - * @brief NBuffer which uses some RTOS queues to manage the available and filled buffers - * @ingroup buffers - * @ingroup concurrency - * @tparam T - * @tparam COUNT number of buffers - */ -template -class SynchronizedNBufferRTOST : public NBuffer { -public: - SynchronizedNBufferRTOST(int bufferSize, int bufferCount, int writeMaxWait=portMAX_DELAY, int readMaxWait=portMAX_DELAY) { - TRACED(); - read_max_wait = readMaxWait; - write_max_wait = writeMaxWait; - resize(bufferSize, bufferCount); - } - ~SynchronizedNBufferRTOST(){ - cleanup(); - } - - bool resize(int bufferSize, int bufferCount) { - TRACED(); - if (buffer_size == bufferSize && buffer_count == bufferCount){ - return true; - } - - max_size = bufferSize * bufferCount; - NBuffer::buffer_count = bufferCount; - NBuffer::buffer_size = bufferSize; - - cleanup(); - available_buffers.resize(bufferCount); - filled_buffers.resize(bufferCount); - - setReadMaxWait(read_max_wait); - setWriteMaxWait(write_max_wait); - - // setup buffers - for (int j = 0; j < bufferCount; j++) { - BaseBuffer *tmp = new SingleBuffer(bufferSize); - if (tmp != nullptr) { - available_buffers.enqueue(tmp); - } else { - LOGE("Not Enough Memory for buffer %d", j); - return false; - } - } - return true; - } - - void setReadMaxWait(TickType_t ticks){ - available_buffers.setReadMaxWait(ticks); - filled_buffers.setReadMaxWait(ticks); - } - - void setWriteMaxWait(TickType_t ticks){ - available_buffers.setWriteMaxWait(ticks); - filled_buffers.setWriteMaxWait(ticks); - } - - size_t size() { - return max_size; - } - - int bufferCountFilled() { - return filled_buffers.size(); - } - - int bufferCountEmpty() { - return available_buffers.size(); - } - -protected: - QueueRTOS*> available_buffers{0,portMAX_DELAY,0}; - QueueRTOS*> filled_buffers{0,portMAX_DELAY,0}; - size_t max_size; - size_t read_max_wait, write_max_wait; - int buffer_size = 0, buffer_count = 0; - - /// Removes all allocated buffers - void cleanup(){ - TRACED(); - BaseBuffer* buffer = nullptr;; - while (available_buffers.dequeue(buffer)){ - delete buffer; - } - while (filled_buffers.dequeue(buffer)){ - delete buffer; - } - } - - BaseBuffer *getNextAvailableBuffer() { - TRACED(); - BaseBuffer* result; - return available_buffers.dequeue(result) ? result : nullptr; - } - - bool addAvailableBuffer(BaseBuffer *buffer) { - TRACED(); - return available_buffers.enqueue(buffer); - } - - BaseBuffer *getNextFilledBuffer() { - TRACED(); - BaseBuffer* result; - return filled_buffers.dequeue(result) ? result : nullptr; - } - - bool addFilledBuffer(BaseBuffer *buffer) { - TRACED(); - return filled_buffers.enqueue(buffer); - } -}; - -using SynchronizedNBufferRTOS = SynchronizedNBufferRTOST; -using SynchronizedNBuffer = SynchronizedNBufferRTOS; - -} // namespace audio_tools \ No newline at end of file diff --git a/src/AudioTools/Concurrency/RTOS/Task.h b/src/AudioTools/Concurrency/RTOS/Task.h deleted file mode 100644 index 084dda2f08..0000000000 --- a/src/AudioTools/Concurrency/RTOS/Task.h +++ /dev/null @@ -1,104 +0,0 @@ -#pragma once - -#ifdef ESP32 -#include "freertos/FreeRTOS.h" -#include "freertos/task.h" -#else -#include "FreeRTOS.h" -#include "task.h" -#endif -#include - -namespace audio_tools { - -/** - * @brief FreeRTOS task - * @ingroup concurrency - * @author Phil Schatzmann - * @copyright GPLv3 * - */ -class Task { - public: - /// Defines and creates a FreeRTOS task - Task(const char* name, int stackSize, int priority = 1, int core = -1) { - create(name, stackSize, priority, core); - } - - Task() = default; - ~Task() { remove(); } - - /// If you used the empty constructor, you need to call create! - bool create(const char* name, int stackSize, int priority = 1, - int core = -1) { - if (xHandle != 0) return false; -#ifdef ESP32 - if (core >= 0) - xTaskCreatePinnedToCore(task_loop, name, stackSize, this, priority, - &xHandle, core); - else - xTaskCreate(task_loop, name, stackSize, this, priority, &xHandle); -#else - xTaskCreate(task_loop, name, stackSize, this, priority, &xHandle); -#endif - suspend(); - return true; - } - - /// deletes the FreeRTOS task - void remove() { - if (xHandle != nullptr) { - suspend(); - vTaskDelete(xHandle); - xHandle = nullptr; - } - } - - bool begin(std::function process) { - LOGI("staring task"); - loop_code = process; - resume(); - return true; - } - - /// suspends the task - void end() { suspend(); } - - void suspend() { vTaskSuspend(xHandle); } - - void resume() { vTaskResume(xHandle); } - - TaskHandle_t &getTaskHandle() { - return xHandle; - } - - void setReference(void *r){ - ref = r; - } - - void *getReference(){ - return ref; - } - -#ifdef ESP32 - int getCoreID() { - return xPortGetCoreID(); - } -#endif - - protected: - TaskHandle_t xHandle = nullptr; - std::function loop_code = nop; - void *ref; - - static void nop() { delay(100); } - - static void task_loop(void* arg) { - Task* self = (Task*)arg; - while (true) { - self->loop_code(); - } - } -}; - -} // namespace audio_tools - diff --git a/src/AudioTools/Concurrency/SynchronizedBuffer.h b/src/AudioTools/Concurrency/SynchronizedBuffer.h deleted file mode 100644 index f349a699f2..0000000000 --- a/src/AudioTools/Concurrency/SynchronizedBuffer.h +++ /dev/null @@ -1,108 +0,0 @@ -#pragma once -#include "AudioToolsConfig.h" -#include "AudioTools/CoreAudio/AudioTypes.h" -#include "AudioTools/CoreAudio/Buffers.h" -#include "AudioTools/CoreAudio/AudioLogger.h" -#include "Mutex.h" -#include "LockGuard.h" - -namespace audio_tools { - -/** - * @brief Wrapper class that can turn any Buffer into a thread save - * implementation. - * @ingroup buffers - * @ingroup concurrency - * @author Phil Schatzmann - * @copyright GPLv3 * - * @tparam T - */ -template -class SynchronizedBuffer : public BaseBuffer { -public: - SynchronizedBuffer(BaseBuffer &buffer, MutexBase &mutex, bool syncAvailable=false) { - p_buffer = &buffer; - p_mutex = &mutex; - is_sync_available = syncAvailable; - } - - // reads a single value - bool read(T &result) override { - TRACED(); - LockGuard guard(p_mutex); - return p_buffer->read(result); - } - - // reads multiple values - int readArray(T data[], int len) { - TRACED(); - LockGuard guard(p_mutex); - int lenResult = MIN(len, available()); - return p_buffer->readArray(data, lenResult); - } - - int writeArray(const T data[], int len) { - LOGD("%s: %d", LOG_METHOD, len); - LockGuard guard(p_mutex); - return p_buffer->writeArray(data, len); - } - - // peeks the actual entry from the buffer - bool peek(T &result) override { - TRACED(); - LockGuard guard(p_mutex); - return p_buffer->peek(result); - } - - // checks if the buffer is full - bool isFull() override { return p_buffer->isFull(); } - - bool isEmpty() { return available() == 0; } - - // write add an entry to the buffer - bool write(T data) override { - TRACED(); - LockGuard guard(p_mutex); - return p_buffer->write(data); - } - - // clears the buffer - void reset() override { - TRACED(); - LockGuard guard(p_mutex); - p_buffer->reset(); - } - - // provides the number of entries that are available to read - int available() override { - TRACED(); - if (is_sync_available) LockGuard guard(p_mutex); - return p_buffer->available(); - } - - // provides the number of entries that are available to write - int availableForWrite() override { - TRACED(); - if (is_sync_available) LockGuard guard(p_mutex); - return p_buffer->availableForWrite(); - } - - // returns the address of the start of the physical read buffer - T *address() override { - TRACED(); - return p_buffer->address(); - } - - size_t size() { - return p_buffer->size(); - } - -protected: - BaseBuffer *p_buffer = nullptr; - MutexBase *p_mutex = nullptr; - bool is_sync_available = false; -}; - - -} // namespace audio_tools - diff --git a/src/AudioTools/Concurrency/SynchronizedQueue.h b/src/AudioTools/Concurrency/SynchronizedQueue.h deleted file mode 100644 index 527f8b3c6f..0000000000 --- a/src/AudioTools/Concurrency/SynchronizedQueue.h +++ /dev/null @@ -1,59 +0,0 @@ -#pragma once -#include "AudioTools/CoreAudio/AudioBasic/Collections/List.h" - -namespace audio_tools { - -/** - * @brief FIFO Queue which is based on a List that is thread - * save. - * @ingroup collections - * @author Phil Schatzmann - * @copyright GPLv3 - * @tparam T - * @tparam TMutex - */ -template -class SynchronizedQueue { - public: - SynchronizedQueue() = default; - - bool enqueue(T& data) { - LockGuard guard{mutex};; - return l.push_front(data); - } - - bool peek(T& data) { - LockGuard guard{mutex};; - if (l.end()->prior == nullptr) return false; - data = *(l.end()->prior); - return true; - } - - bool dequeue(T& data) { - LockGuard guard{mutex};; - return l.pop_back(data); - } - - size_t size() { - LockGuard guard{mutex};; - return l.size(); - } - - bool clear() { - LockGuard guard{mutex};; - return l.clear(); - } - - bool empty() { - LockGuard guard{mutex};; - return l.empty(); - } - - void setAllocator(Allocator& allocator) { l.setAllocator(allocator); } - - protected: - List l; - TMutex mutex; -}; - -} // namespace audio_tools \ No newline at end of file diff --git a/src/AudioTools/Concurrency/SynchronizedStream.h b/src/AudioTools/Concurrency/SynchronizedStream.h deleted file mode 100644 index 0b299f983b..0000000000 --- a/src/AudioTools/Concurrency/SynchronizedStream.h +++ /dev/null @@ -1,79 +0,0 @@ -#pragma once -#include "AudioTools/CoreAudio/Buffers.h" -#include "LockGuard.h" -#include "Mutex.h" -#include "Stream.h" - -namespace audio_tools { - -/*** - * @brief Wrapper class that can turn any Stream into a thread save - * implementation. This is done by adding a Mutex to the Stream. The - * read and write operations are buffered and the access to the stream is - * protected by the Mutex. - * @ingroup streams - * @ingroup concurrency - * @author Phil Schatzmann - */ - -class SynchronizedStream : public Stream { - public: - SynchronizedStream(Stream &stream, MutexBase &mutex) { - p_stream = &stream; - p_mutex = &mutex; - } - - // reads a single value - int read() override { - if (read_buffer.isEmpty()) { - LockGuard guard(p_mutex); - p_stream->readBytes(read_buffer.address(), read_buffer.size()); - } - return read_buffer.read(); - } - - // peeks the actual entry from the buffer - int peek() override { - LockGuard guard(p_mutex); - return p_stream->peek(); - } - - // write add an entry to the buffer - size_t write(uint8_t data) override { - write_buffer.write(data); - if (write_buffer.isFull()) { - LockGuard guard(p_mutex); - size_t written = p_stream->write((const uint8_t *)write_buffer.data(), - write_buffer.size()); - assert(written == write_buffer.size()); - write_buffer.reset(); - } - return 1; - } - - // provides the number of entries that are available to read - int available() override { - LockGuard guard(p_mutex); - return p_stream->available(); - } - - // provides the number of entries that are available to write - int availableForWrite() override { - LockGuard guard(p_mutex); - return p_stream->availableForWrite(); - } - - /// Defines the size of the internal buffers - void setBufferSize(int size) { - read_buffer.resize(size); - write_buffer.resize(size); - } - - protected: - Stream *p_stream = nullptr; - MutexBase *p_mutex = nullptr; - SingleBuffer read_buffer; - SingleBuffer write_buffer; -}; - -} // namespace audio_tools diff --git a/src/AudioTools/CoreAudio.h b/src/AudioTools/CoreAudio.h deleted file mode 100644 index dc82d54384..0000000000 --- a/src/AudioTools/CoreAudio.h +++ /dev/null @@ -1,25 +0,0 @@ -#pragma once - -#include "AudioTools/CoreAudio/AudioTypes.h" -#include "AudioTools/CoreAudio/Buffers.h" -#include "AudioTools/CoreAudio/BaseConverter.h" -#include "AudioTools/CoreAudio/AudioLogger.h" -#include "AudioTools/CoreAudio/AudioStreams.h" -#include "AudioTools/CoreAudio/AudioStreamsConverter.h" -#include "AudioTools/CoreAudio/AudioOutput.h" -#include "AudioTools/CoreAudio/VolumeStream.h" -#include "AudioTools/CoreAudio/AudioIO.h" -#include "AudioTools/CoreAudio/ResampleStream.h" -#include "AudioTools/CoreAudio/StreamCopy.h" -#include "AudioTools/CoreAudio/MusicalNotes.h" -#include "AudioTools/CoreAudio/Fade.h" -#include "AudioTools/CoreAudio/Pipeline.h" -#include "AudioTools/CoreAudio/AudioPlayer.h" -#include "AudioTools/CoreAudio/AudioTimer.h" -#include "AudioTools/CoreAudio/AudioFilter.h" -#include "AudioTools/CoreAudio/I2SStream.h" -#include "AudioTools/CoreAudio/AudioPWM.h" -#include "AudioTools/CoreAudio/AnalogAudioStream.h" -#include "AudioTools/CoreAudio/AudioEffects.h" -#include "AudioTools/CoreAudio/AudioMetaData.h" -#include "AudioTools/CoreAudio/AudioHttp.h" diff --git a/src/AudioTools/CoreAudio/AnalogAudioStream.h b/src/AudioTools/CoreAudio/AnalogAudioStream.h deleted file mode 100644 index ef3631945e..0000000000 --- a/src/AudioTools/CoreAudio/AnalogAudioStream.h +++ /dev/null @@ -1,3 +0,0 @@ -#pragma once - -#include "AudioTools/CoreAudio/AudioAnalog/AnalogAudioStream.h" diff --git a/src/AudioTools/CoreAudio/AudioActions.h b/src/AudioTools/CoreAudio/AudioActions.h deleted file mode 100644 index 32290e701c..0000000000 --- a/src/AudioTools/CoreAudio/AudioActions.h +++ /dev/null @@ -1,278 +0,0 @@ -#pragma once -#include "AudioTools/CoreAudio/AudioBasic/Collections/Vector.h" -#include "AudioTools/CoreAudio/AudioLogger.h" - -#ifndef TOUCH_LIMIT -#define TOUCH_LIMIT 20 -#endif - -#ifndef DEBOUNCE_DELAY -#define DEBOUNCE_DELAY 500 -#endif - -#if defined(IS_MIN_DESKTOP) -extern "C" void pinMode(int, int); -extern "C" int digitalRead(int); -#endif - -namespace audio_tools { - -// global reference to access from static callback methods -class AudioActions; -static AudioActions *selfAudioActions = nullptr; - -/** - * @brief A simple class to assign functions to gpio pins e.g. to implement a - * simple navigation control or volume control with buttons - * @ingroup tools - */ -class AudioActions { -public: - enum ActiveLogic : uint8_t { - ActiveLow, - ActiveHigh, - ActiveChange, - ActiveTouch - }; - - struct Action { - Action() = default; - virtual ~Action() {} - int16_t pin = -1; - void (*actionOn)(bool pinStatus, int pin, void *ref) = nullptr; - void (*actionOff)(bool pinStatus, int pin, void *ref) = nullptr; - void *ref = nullptr; - unsigned long debounceTimeout = 0; - ActiveLogic activeLogic = ActiveHigh; - bool lastState = true; - bool enabled = true; - - /// determines the value for the action - int debounceDelayValue = DEBOUNCE_DELAY; - int touchLimit = TOUCH_LIMIT; - - virtual int id() { - return pin; - } - - virtual bool readValue() { -#if defined(USE_TOUCH_READ) - bool result; - if (this->activeLogic == ActiveTouch) { - int value = touchRead(this->pin); - result = value <= touchLimit; - if (result) { - // retry to confirm reading - value = touchRead(this->pin); - result = value <= touchLimit; - LOGI("touch pin: %d value %d (limit: %d) -> %s", this->pin, value, - touchLimit, result ? "true" : "false"); - } - } else { - result = digitalRead(this->pin); - } - return result; -#else - return digitalRead(this->pin); -#endif - } - - virtual void process() { - if (this->enabled) { - bool value = readValue(); - if (this->actionOn != nullptr && this->actionOff != nullptr) { - // we have on and off action defined - if (value != this->lastState) { - //LOGI("processActions: case with on and off"); - // execute action -> reports active instead of pin state - if ((value && this->activeLogic == ActiveHigh) || - (!value && this->activeLogic == ActiveLow)) { - this->actionOn(true, this->pin, this->ref); - } else { - this->actionOff(false, this->pin, this->ref); - } - this->lastState = value; - } - } else if (this->activeLogic == ActiveChange) { - bool active = value; - // reports pin state - if (value != this->lastState && millis() > this->debounceTimeout) { - //LOGI("processActions: ActiveChange"); - // execute action - this->actionOn(active, this->pin, this->ref); - this->lastState = value; - this->debounceTimeout = millis() + debounceDelayValue; - } - } else { - bool active = (this->activeLogic == ActiveLow) ? !value : value; - if (active && - (active != this->lastState || millis() > this->debounceTimeout)) { - // LOGI("processActions: %d Active %d - %d", this->pin, value, - // execute action - this->actionOn(active, this->pin, this->ref); - this->lastState = active; - this->debounceTimeout = millis() + debounceDelayValue; - } - } - } - } - }; - - /// Default constructor - AudioActions(bool useInterrupt = false) { - selfAudioActions = this; - setUsePinInterrupt(useInterrupt); - } - - /// deletes all actions - virtual ~AudioActions() { - clear(); - } - - /// Adds an Action - void add(Action &action){ - insertAction(action); - } - - /// Adds an action - void add(int pin, void (*actionOn)(bool pinStatus, int pin, void *ref), - ActiveLogic activeLogic = ActiveLow, void *ref = nullptr) { - add(pin, actionOn, nullptr, activeLogic, ref); - } - - /// Adds an action - void add(int pin, void (*actionOn)(bool pinStatus, int pin, void *ref), - void (*actionOff)(bool pinStatus, int pin, void *ref), - ActiveLogic activeLogicPar = ActiveLow, void *ref = nullptr) { - LOGI("ActionLogic::add pin: %d / logic: %d", pin, activeLogicPar); - if (pin >= 0) { - // setup pin mode - setupPin(pin, activeLogicPar); - - // add value - Action& action = *new Action(); - action.pin = pin; - action.actionOn = actionOn; - action.actionOff = actionOff; - action.activeLogic = activeLogicPar; - action.ref = ref; - action.debounceDelayValue = debounceDelayValue; - action.touchLimit = touchLimit; - - insertAction(action); - } else { - LOGW("pin %d -> Ignored", pin); - } - } - - /// enable/disable pin actions - void setEnabled(int pin, bool enabled) { - Action *p_action = findAction(pin); - if (p_action) { - p_action->enabled = enabled; - } - } - - /** - * @brief Execute all actions if the corresponding pin is low - * To minimize the runtime: With each call we process a different pin - */ - void processActions() { - static int pos = 0; - if (actions.empty()) - return; - // execute action - actions[pos]->process(); - pos++; - if (pos >= actions.size()) { - pos = 0; - } - } - - /// Execute all actions - void processAllActions() { - for (Action *action : actions) { - action->process(); - } - } - - /// Determines the action for the pin/id - Action *findAction(int id) { - for (Action *action : actions) { - if (action->id() == id) { - return action; - } - } - return nullptr; - } - - /// Determines the action for the pin/id - int findActionIdx(int id) { - int pos = 0; - for (Action *action : actions) { - if (action->id() == id) { - return pos; - } - pos++; - } - return -1; - } - - /// Defines the debounce delay - void setDebounceDelay(int value) { debounceDelayValue = value; } - /// Defines the touch limit (Default 20) - void setTouchLimit(int value) { touchLimit = value; } - /// Use interrupts instead of processActions() call in loop - void setUsePinInterrupt(bool active) { use_pin_interrupt = active; } - /// setup pin mode when true - void setPinMode(bool active) { use_pin_mode = active; } - - void clear() { - for (Action *act : actions){ - delete(act); - } - actions.reset(); - } - -protected: - int debounceDelayValue = DEBOUNCE_DELAY; - int touchLimit = TOUCH_LIMIT; - bool use_pin_interrupt = false; - bool use_pin_mode = true; - Vector actions{0}; - - void insertAction(Action& action){ - int idx = findActionIdx(action.id()); - if (idx >= 0) { - // replace old action - delete(actions[idx]); - actions[idx] = &action; - } else { - // add new action - actions.push_back(&action); - } - } - - static void audioActionsISR() { selfAudioActions->processAllActions(); } - - void setupPin(int pin, ActiveLogic logic) { - // in the audio-driver library the pins are already set up - if (use_pin_mode) { - if (logic == ActiveLow) { - pinMode(pin, INPUT_PULLUP); - LOGI("pin %d -> INPUT_PULLUP", pin); - } else { - pinMode(pin, INPUT); - LOGI("pin %d -> INPUT", pin); - } - } - -#if defined(ARDUINO) && !defined(IS_MIN_DESKTOP) - if (use_pin_interrupt) { - attachInterrupt(digitalPinToInterrupt(pin), audioActionsISR, CHANGE); - } -#endif - } -}; - -} // namespace audio_tools diff --git a/src/AudioTools/CoreAudio/AudioAnalog/AnalogAudioArduino.h b/src/AudioTools/CoreAudio/AudioAnalog/AnalogAudioArduino.h deleted file mode 100644 index db1b0fc834..0000000000 --- a/src/AudioTools/CoreAudio/AudioAnalog/AnalogAudioArduino.h +++ /dev/null @@ -1,330 +0,0 @@ -#pragma once - -#include // for INT_MIN and INT_MAX - -#include "AnalogConfigStd.h" -#include "AudioTools/CoreAudio/AudioAnalog/AnalogDriverBase.h" -#include "AudioTools/CoreAudio/AudioTimer/AudioTimer.h" -#include "AudioTools/CoreAudio/AudioStreams.h" -#include "AudioTools/CoreAudio/Buffers.h" - -namespace audio_tools { - -/** - * @brief Analog Data IO using a timer - * and the Arduino analogRead() method and writing using analogWrite(); - * @ingroup io - * @author Phil Schatzmann - * @copyright GPLv3 - * - */ -class AnalogAudioArduino : public AudioStream { - public: - AnalogAudioArduino() = default; - - /// provides the default configuration - AnalogConfigStd defaultConfig() { - AnalogConfigStd def; - return def; - } - - void setAudioInfo(AudioInfo info) override { - TRACEI(); - if (config.sample_rate != info.sample_rate || - config.channels != info.channels || - config.bits_per_sample != info.bits_per_sample) { - config.sample_rate = info.sample_rate; - config.bits_per_sample = info.bits_per_sample; - config.channels = info.channels; - config.logInfo(); - setupTimer(); - } - } - - /// Reopen with last config - bool begin() override { return begin(config); } - - bool begin(AnalogConfigStd cfg) { - TRACED(); - - config = cfg; - if (config.rx_tx_mode == RXTX_MODE) { - LOGE("RXTX not supported"); - return false; - } - - frame_size = config.channels * (config.bits_per_sample / 8); - result_factor = 1; - - if (!setupPins()) return false; - - if (!setupTx()) return false; - - if (!setupBuffer()) return false; - - // (re)start timer - return setupTimer(); - } - - void end() override { timer.end(); } - - int available() override { - if (config.rx_tx_mode == TX_MODE) return 0; - return buffer == nullptr ? 0 : buffer->available() * 2; - }; - - /// Provides the sampled audio data - size_t readBytes(uint8_t *data, size_t len) override { - if (config.rx_tx_mode == TX_MODE) return 0; - if (buffer == nullptr) return 0; - int bytes = len / frame_size * frame_size; - return buffer->readArray(data, bytes); - } - - int availableForWrite() override { - if (config.rx_tx_mode == RX_MODE) return 0; - if (buffer == nullptr) return 0; - return config.is_blocking_write ? config.buffer_size - : buffer->availableForWrite(); - } - - size_t write(const uint8_t *data, size_t len) override { - LOGD("write: %d", (int)len); - if (config.rx_tx_mode == RX_MODE) return 0; - // only process full frames - len = len / frame_size * frame_size; - - if (isCombinedChannel()) { - ChannelReducer cr(1, 2, config.bits_per_sample); - len = cr.convert((uint8_t *)data, len); - LOGD("ChannelReducer len: %d", (int)len); - } - - if (isDecimateActive()) { - Decimate dec(decim, 1, config.bits_per_sample); - len = dec.convert((uint8_t *)data, len); - LOGD("Decimate len: %d for factor %d", (int)len, decim); - } - - // blocking write ? - if (config.is_blocking_write) { - LOGD("Waiting for buffer to be available"); - while (buffer->availableForWrite() < len) { - delay(10); - } - } - - size_t result = 0;; - switch(config.bits_per_sample){ - case 8: { - result = buffer->writeArray(data, len); - } break; - case 16: { - size_t samples = len / 2; - int16_t *p16 = (int16_t*)data; - for (int j=0;jwrite(sample)){ - result += 2; - } else { - break; - } - } - } break; - case 24: { - size_t samples = len / 3; - int24_t *p24 = (int24_t*)data; - for (int j=0;jwrite(sample)){ - result += 3; - } else { - break; - } - } - - } break; - case 32: { - size_t samples = len / 4; - int32_t *p32 = (int32_t*)data; - for (int j=0;jwrite(sample)){ - result += 4; - } else { - break; - } - } - - } break; - } - - // write data - return result * result_factor; - } - - protected: - AnalogConfigStd config; - TimerAlarmRepeating timer; - BaseBuffer *buffer = nullptr; - int avg_value, min, max, count; - bool is_combined_channels = false; - uint16_t frame_size = 0; - int result_factor = 1; - int decim = 1; - bool is_active = false; - - - bool setupTx() { - if (config.rx_tx_mode == TX_MODE) { - // check channels - if (config.channels > ANALOG_MAX_OUT_CHANNELS) { - if (config.channels == 2) { - is_combined_channels = true; - config.channels = 1; - } else { - LOGE("Unsupported channels"); - return false; - } - } - if (isDecimateActive()) { - LOGI("Using reduced sample rate: %d", effectiveOutputSampleRate()); - decim = decimation(); - result_factor = result_factor * decim; - } - if (isCombinedChannel()) { - LOGI("Combining channels"); - result_factor = result_factor * 2; - } - } - return true; - } - - bool setupBuffer() { - if (buffer == nullptr) { - // allocate buffer_count - buffer = new RingBuffer(config.buffer_size * config.buffer_count); - if (buffer == nullptr) { - LOGE("Not enough memory for buffer"); - return false; - } - } - return true; - } - - bool setupTimer() { - int sample_rate = config.rx_tx_mode == TX_MODE ? effectiveOutputSampleRate() - : config.sample_rate; - LOGI("sample_rate: %d", sample_rate); - timer.setCallbackParameter(this); - return timer.begin(callback, sample_rate, TimeUnit::HZ); - } - - /// Sample data and write to buffer - static void callback(void *arg) { - int16_t value = 0; - AnalogAudioArduino *self = (AnalogAudioArduino *)arg; - // prevent NPE - if (self->buffer == nullptr) return; - - // Logic for reading audio data - if (self->config.rx_tx_mode == RX_MODE) { - int channels = self->config.channels; - for (int j = 0; j < channels; j++) { - // provides value in range 0…4095 - value = analogRead(self->config.pins_data[j]); - if (self->config.is_auto_center_read) { - self->updateMinMax(value); - } - value = (value - self->avg_value) * 16; - self->buffer->write(value); - } - // Logic for writing audio data - } else if (self->config.rx_tx_mode == TX_MODE) { - int channels = self->config.channels; - uint8_t sample = 0; - for (int j = 0; j < channels; j++) { - self->buffer->read(sample); - int pin = self->config.pins_data[j]; - analogWrite(pin, sample); - //LOGW("analogWrite(%d, %d)", pin, sample); - } - } - } - - /// pinmode input for defined analog pins - bool setupPins() { - TRACED(); - - Pins& pins = config.pins(); - if (pins.size() max) max = value; - if (count++ == 1024) updateAvg(); - } - - void updateAvg() { - avg_value = (max + min) / 2; - min = INT_MAX; - max = INT_MIN; - count = 0; - } - - /// The requested sampling rate is too hight: we only process half of the - /// samples so we can half the sampling rate - bool isDecimateActive() { - return config.sample_rate >= config.max_sample_rate; - } - - // combined stereo channel to mono - bool isCombinedChannel() { return is_combined_channels; } - - /// Returns the effective output sample rate - int effectiveOutputSampleRate() { return config.sample_rate / decimation(); } - - int decimation() { - if (config.sample_rate <= config.max_sample_rate) return 1; - for (int j = 2; j < 6; j += 2) { - if (config.sample_rate / j <= config.max_sample_rate) { - return j; - } - } - return 6; - } -}; - -} // namespace audio_tools diff --git a/src/AudioTools/CoreAudio/AudioAnalog/AnalogConfigESP32.h b/src/AudioTools/CoreAudio/AudioAnalog/AnalogConfigESP32.h deleted file mode 100644 index 55f576f346..0000000000 --- a/src/AudioTools/CoreAudio/AudioAnalog/AnalogConfigESP32.h +++ /dev/null @@ -1,83 +0,0 @@ -#pragma once - -#include "AudioToolsConfig.h" -#if defined(USE_ANALOG) && defined(ESP32) && ESP_IDF_VERSION < ESP_IDF_VERSION_VAL(5, 0 , 0) || defined(DOXYGEN) -#include "AudioTools/CoreAudio/AudioTypes.h" - -# include "driver/i2s.h" -# include "driver/adc.h" -# include "soc/dac_channel.h" -# include "soc/adc_channel.h" - -namespace audio_tools { - -/** - * @brief ESP32 specific configuration for i2s input via adc. The default input pin is GPIO34. We always use int16_t values. The default - * output pins are GPIO25 and GPIO26! - * - * @ingroup platform - * @author Phil Schatzmann - * @copyright GPLv3 - */ -class AnalogConfigESP32 : public AudioInfo { - public: - TickType_t timeout = portMAX_DELAY; - int buffer_count = ANALOG_BUFFER_COUNT; - int buffer_size = ANALOG_BUFFER_SIZE; - RxTxMode rx_tx_mode; - bool is_blocking_write = true; - bool is_auto_center_read = true; - - // allow ADC to access the protected methods - friend class AnalogDriverESP32; - bool use_apll = false; - - // public config parameters - int port_no = I2S_NUM_0; // Analog input and output only supports 0! - bool auto_clear = I2S_AUTO_CLEAR; - bool uninstall_driver_on_end = true; - int mode_internal; - int adc_pin; - - /// Default constructor - AnalogConfigESP32(RxTxMode rxtxMode=TX_MODE) { - sample_rate = 44100; - bits_per_sample = 16; - channels = 2; - rx_tx_mode = rxtxMode; - if (rx_tx_mode == RX_MODE) { - mode_internal = (I2S_MODE_MASTER | I2S_MODE_RX | I2S_MODE_ADC_BUILT_IN); - adc_pin = PIN_ADC1; - auto_clear = false; - LOGI("I2S_MODE_ADC_BUILT_IN"); - } else { - mode_internal = (I2S_MODE_MASTER | I2S_MODE_TX | I2S_MODE_DAC_BUILT_IN); - LOGI("I2S_MODE_DAC_BUILT_IN"); - } - } - - /// Copy constructor - AnalogConfigESP32(const AnalogConfigESP32 &cfg) = default; - - void logInfo() { - AudioInfo::logInfo(); - if (rx_tx_mode == TX_MODE){ - LOGI("analog left output pin: %d", 25); - LOGI("analog right output pin: %d", 26); - } - } - - /// Defines an alternative input pin (for the left channel) - void setInputPin1(int pin){ - this->adc_pin = pin; - } - -}; - -#ifndef ANALOG_CONFIG -#define ANALOG_CONFIG -using AnalogConfig = AnalogConfigESP32; -#endif - -} // ns -#endif diff --git a/src/AudioTools/CoreAudio/AudioAnalog/AnalogConfigESP32V1.h b/src/AudioTools/CoreAudio/AudioAnalog/AnalogConfigESP32V1.h deleted file mode 100644 index 77645a8e1e..0000000000 --- a/src/AudioTools/CoreAudio/AudioAnalog/AnalogConfigESP32V1.h +++ /dev/null @@ -1,182 +0,0 @@ -#pragma once - -#include "AudioToolsConfig.h" -#if defined(USE_ANALOG) && defined(ESP32) && \ - ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 0, 0) || \ - defined(DOXYGEN) - - #include "AudioTools/CoreAudio/AudioTypes.h" - -#include "esp_adc/adc_cali_scheme.h" -#include "esp_adc/adc_continuous.h" -#ifdef ARDUINO -#include "esp32-hal-periman.h" -#endif - -#if CONFIG_IDF_TARGET_ESP32 -#define ADC_CONV_MODE ADC_CONV_SINGLE_UNIT_1 -#define ADC_OUTPUT_TYPE ADC_DIGI_OUTPUT_FORMAT_TYPE1 -#define ADC_CHANNELS \ - {ADC_CHANNEL_0, ADC_CHANNEL_3, ADC_CHANNEL_4, \ - ADC_CHANNEL_5, ADC_CHANNEL_6, ADC_CHANNEL_7} -#define NUM_ADC_CHANNELS 6 -#define AUDIO_ADC_GET_CHANNEL(p_data) ((p_data)->type1.channel) -#define AUDIO_ADC_GET_DATA(p_data) ((p_data)->type1.data) -#define HAS_ESP32_DAC -#define ADC_CHANNEL_TYPE uint16_t -#define ADC_DATA_TYPE uint16_t -#elif CONFIG_IDF_TARGET_ESP32S2 -#define ADC_CONV_MODE ADC_CONV_SINGLE_UNIT_1 -#define ADC_OUTPUT_TYPE ADC_DIGI_OUTPUT_FORMAT_TYPE2 -#define AUDIO_ADC_GET_CHANNEL(p_data) ((p_data)->type2.channel) -#define AUDIO_ADC_GET_DATA(p_data) ((p_data)->type2.data) -#define ADC_CHANNELS \ - {ADC_CHANNEL_0, ADC_CHANNEL_1, ADC_CHANNEL_2, ADC_CHANNEL_3, ADC_CHANNEL_4, \ - ADC_CHANNEL_5, ADC_CHANNEL_6, ADC_CHANNEL_7, ADC_CHANNEL_8, ADC_CHANNEL_9} -#define NUM_ADC_CHANNELS 10 -#define HAS_ESP32_DAC -#define ADC_CHANNEL_TYPE uint16_t -#define ADC_DATA_TYPE uint16_t -#elif CONFIG_IDF_TARGET_ESP32C3 || CONFIG_IDF_TARGET_ESP32H2 || \ - CONFIG_IDF_TARGET_ESP32C2 || CONFIG_IDF_TARGET_ESP32C6 -#define ADC_CONV_MODE ADC_CONV_ALTER_UNIT // ESP32C3 only supports alter mode -#define ADC_OUTPUT_TYPE ADC_DIGI_OUTPUT_FORMAT_TYPE2 -#define AUDIO_ADC_GET_CHANNEL(p_data) ((p_data)->type2.channel) -#define AUDIO_ADC_GET_DATA(p_data) ((p_data)->type2.data) -#define ADC_CHANNELS \ - {ADC_CHANNEL_0, ADC_CHANNEL_1, ADC_CHANNEL_2, ADC_CHANNEL_3, ADC_CHANNEL_4} -#define NUM_ADC_CHANNELS 5 -#define ADC_CHANNEL_TYPE uint32_t -#define ADC_DATA_TYPE uint32_t -#elif CONFIG_IDF_TARGET_ESP32C6 -#define ADC_CONV_MODE ADC_CONV_ALTER_UNIT -#define ADC_OUTPUT_TYPE ADC_DIGI_OUTPUT_FORMAT_TYPE2 -#define AUDIO_ADC_GET_CHANNEL(p_data) ((p_data)->type2.channel) -#define AUDIO_ADC_GET_DATA(p_data) ((p_data)->type2.data) -#define ADC_CHANNELS \ - {ADC_CHANNEL_0, ADC_CHANNEL_1, ADC_CHANNEL_2, ADC_CHANNEL_3, \ - ADC_CHANNEL_4, ADC_CHANNEL_5, ADC_CHANNEL_6} -#define NUM_ADC_CHANNELS 7 -#define ADC_CHANNEL_TYPE uint32_t -#define ADC_DATA_TYPE uint32_t -#elif CONFIG_IDF_TARGET_ESP32S3 -#define ADC_CONV_MODE ADC_CONV_SINGLE_UNIT_1 -#define ADC_OUTPUT_TYPE ADC_DIGI_OUTPUT_FORMAT_TYPE2 -#define AUDIO_ADC_GET_CHANNEL(p_data) ((p_data)->type2.channel) -#define AUDIO_ADC_GET_DATA(p_data) ((p_data)->type2.data) -#define ADC_CHANNELS \ - {ADC_CHANNEL_0, ADC_CHANNEL_1, ADC_CHANNEL_2, ADC_CHANNEL_3, ADC_CHANNEL_4, \ - ADC_CHANNEL_5, ADC_CHANNEL_6, ADC_CHANNEL_7, ADC_CHANNEL_8, ADC_CHANNEL_9} -#define NUM_ADC_CHANNELS 10 -#define ADC_CHANNEL_TYPE uint32_t -#define ADC_DATA_TYPE uint32_t -#elif CONFIG_IDF_TARGET_ESP32P4 -#define ADC_CONV_MODE ADC_CONV_SINGLE_UNIT_1 -#define ADC_OUTPUT_TYPE ADC_DIGI_OUTPUT_FORMAT_TYPE2 -#define AUDIO_ADC_GET_CHANNEL(p_data) ((p_data)->type2.channel) -#define AUDIO_ADC_GET_DATA(p_data) ((p_data)->type2.data) -#define ADC_CHANNELS \ - {ADC_CHANNEL_0, ADC_CHANNEL_1, ADC_CHANNEL_2, ADC_CHANNEL_3, \ - ADC_CHANNEL_4, ADC_CHANNEL_5, ADC_CHANNEL_6, ADC_CHANNEL_7} -#define NUM_ADC_CHANNELS 8 -#define ADC_CHANNEL_TYPE uint32_t -#define ADC_DATA_TYPE uint32_t -#endif - -// continuous ADC API should run on ADC1 - -#define ADC_UNIT ADC_UNIT_1 -#ifdef HAS_ESP32_DAC -#include "driver/dac_continuous.h" -#endif - -namespace audio_tools { - -/** - * @brief ESP32 specific configuration for i2s input via adc using the - * adc_continuous API - * @author Phil Schatzmann - * @ingroup platform - * @copyright GPLv3 - */ -class AnalogConfigESP32V1 : public AudioInfo { - // allow ADC to access the protected methods - friend class AnalogDriverESP32; - - public: - int buffer_count = ANALOG_BUFFER_COUNT; - int buffer_size = ANALOG_BUFFER_SIZE; - RxTxMode rx_tx_mode; - TickType_t timeout = portMAX_DELAY; - -#ifdef HAS_ESP32_DAC - bool is_blocking_write = true; - bool use_apll = false; - /// ESP32: DAC_CHANNEL_MASK_CH0 or DAC_CHANNEL_MASK_CH1 - dac_channel_mask_t dac_mono_channel = DAC_CHANNEL_MASK_CH0; -#endif - - // ADC config parameters - bool adc_calibration_active = false; - bool is_auto_center_read = false; - adc_digi_convert_mode_t adc_conversion_mode = ADC_CONV_MODE; - adc_digi_output_format_t adc_output_type = ADC_OUTPUT_TYPE; - uint8_t adc_attenuation = ADC_ATTEN_DB_12; // full voltage range of 3.9V - uint8_t adc_bit_width = SOC_ADC_DIGI_MAX_BITWIDTH; - - adc_unit_t adc_unit = ADC_UNIT; - adc_channel_t adc_channels[NUM_ADC_CHANNELS] = ADC_CHANNELS; - - /// Default constructor - AnalogConfigESP32V1(RxTxMode rxtxMode = TX_MODE) { - sample_rate = 44100; - channels = 2; - bits_per_sample = 16; - rx_tx_mode = rxtxMode; - switch (rx_tx_mode) { - case RX_MODE: { - // make sure that the proposed sample rate is valid - if (sample_rate * channels > SOC_ADC_SAMPLE_FREQ_THRES_HIGH) { - sample_rate = SOC_ADC_SAMPLE_FREQ_THRES_HIGH / channels; - } - LOGI("I2S_MODE_ADC_BUILT_IN"); - break; - } -#ifdef HAS_ESP32_DAC - case TX_MODE: - use_apll = true; - LOGI("I2S_MODE_DAC_BUILT_IN"); - break; -#endif - - default: - LOGE("RxTxMode not supported: %d", rx_tx_mode); - break; - } - } - - /// Copy constructor - AnalogConfigESP32V1(const AnalogConfigESP32V1 &cfg) = default; - - void logInfo() { - AudioInfo::logInfo(); -#if !defined(ESP32X) - if (rx_tx_mode == TX_MODE) { - LOGI("analog left output pin: %d", 25); - LOGI("analog right output pin: %d", 26); - } -#endif -#ifdef HAS_ESP32_DAC - LOGI("use_apll: %d", use_apll); -#endif - } -}; - -#ifndef ANALOG_CONFIG -#define ANALOG_CONFIG -using AnalogConfig = AnalogConfigESP32V1; -#endif - -} // namespace audio_tools - -#endif diff --git a/src/AudioTools/CoreAudio/AudioAnalog/AnalogConfigStd.h b/src/AudioTools/CoreAudio/AudioAnalog/AnalogConfigStd.h deleted file mode 100644 index 626043abb0..0000000000 --- a/src/AudioTools/CoreAudio/AudioAnalog/AnalogConfigStd.h +++ /dev/null @@ -1,88 +0,0 @@ -#pragma once -#include "AudioToolsConfig.h" -#include "AudioTools/CoreAudio/AudioTypes.h" - -#ifndef PIN_ANALOG_START -# define PIN_ANALOG_START -1 -#endif - -#ifndef ANALOG_BUFFERS -# define ANALOG_BUFFERS 10 -#endif - -#ifndef ANALOG_MAX_OUT_CHANNELS -# define ANALOG_MAX_OUT_CHANNELS 10 -#endif - -#include "AudioTools/CoreAudio/AudioTypes.h" - - -namespace audio_tools { - -class AnalogAudioArduino; - -/** - * @brief Generic ADC and DAC configuration - * - * @author Phil Schatzmann - * @ingroup platform - * @copyright GPLv3 - */ -class AnalogConfigStd : public AudioInfo { - friend class AnalogAudioArduino; - public: - int buffer_count = ANALOG_BUFFERS; - int buffer_size = ANALOG_BUFFER_SIZE; - RxTxMode rx_tx_mode = RX_MODE; - bool is_blocking_write = true; - bool is_auto_center_read = true; - int max_sample_rate = ANALOG_MAX_SAMPLE_RATE; - int start_pin = PIN_ANALOG_START; - - AnalogConfigStd() = default; - AnalogConfigStd(RxTxMode rxtxMode) : AudioInfo() { - rx_tx_mode = rxtxMode; - } - - /// support assignament of int array - template - void setPins(T (&a)[N]) { - pins_data.clear(); - pins_data.resize(N); - for (int i = 0; i < N; ++i) { - pins_data[i] = a[i]; // reset all elements - } - } - - // /// Defines the pins and the corresponding number of channels (=number of - // /// pins) - // void setPins(Pins &pins) { - // pins_data.clear(); - // for (int i = 0; i < pins.size(); i++) { - // pins_data.push_back(pins[i]); - // } - // } - - /// Determines the pins (for all channels) - Pins &pins() { - if (pins_data.size() == 0 && start_pin >= 0) { - pins_data.resize(channels); - for (int j = 0; j < channels; j++) { - pins_data[j] = start_pin + j; - } - } - return pins_data; - } - - protected: - Pins pins_data{0}; - -}; - -#ifndef ANALOG_CONFIG -#define ANALOG_CONFIG -using AnalogConfig = AnalogConfigStd; -#endif - -} // ns -#//endif diff --git a/src/AudioTools/CoreAudio/AudioAnalog/AnalogDriverArduino.h b/src/AudioTools/CoreAudio/AudioAnalog/AnalogDriverArduino.h deleted file mode 100644 index 8e20e634b6..0000000000 --- a/src/AudioTools/CoreAudio/AudioAnalog/AnalogDriverArduino.h +++ /dev/null @@ -1,52 +0,0 @@ -#pragma once - -#include "AudioToolsConfig.h" -#if defined(USE_ANALOG_ARDUINO) || defined(DOXYGEN) - -#include // for INT_MIN and INT_MAX -#include "AudioTools/CoreAudio/AudioAnalog/AnalogAudioArduino.h" -#include "AudioTools/CoreAudio/AudioTimer/AudioTimer.h" -#include "AudioTools/CoreAudio/AudioStreams.h" -#include "AudioTools/CoreAudio/AudioTypes.h" -#include "AudioTools/CoreAudio/Buffers.h" - -namespace audio_tools { - -/** - * @brief Please use the AnalogAudioStream: Reading Analog Data using a timer - * and the Arduino analogRead() method and writing using analogWrite(); - * @ingroup platform - * @author Phil Schatzmann - * @copyright GPLv3 - * - */ -class AnalogDriverArduino : public AnalogDriverBase { - public: - AnalogDriverArduino() = default; - - bool begin(AnalogConfig cfg) { return drv.begin(cfg); } - - void end() override { drv.end(); } - - int available() override { return drv.available(); }; - - /// Provides the sampled audio data - size_t readBytes(uint8_t *data, size_t len) override { - return drv.write(data, len); - } - - int availableForWrite() override { return drv.availableForWrite(); } - - size_t write(const uint8_t *data, size_t len) override { - return drv.write(data, len); - } - - protected: - AnalogAudioArduino drv; -}; - -using AnalogDriver = AnalogDriverArduino; - -} // namespace audio_tools - -#endif \ No newline at end of file diff --git a/src/AudioTools/CoreAudio/AudioAnalog/AnalogDriverBase.h b/src/AudioTools/CoreAudio/AudioAnalog/AnalogDriverBase.h deleted file mode 100644 index e9a0267c4f..0000000000 --- a/src/AudioTools/CoreAudio/AudioAnalog/AnalogDriverBase.h +++ /dev/null @@ -1,24 +0,0 @@ -#pragma once -#include "AudioToolsConfig.h" -#if defined(USE_ANALOG) -#if defined(ESP32) -# include "AnalogConfigESP32.h" -# include "AnalogConfigESP32V1.h" -#else -# include "AnalogConfigStd.h" -#endif - -namespace audio_tools { - -class AnalogDriverBase { -public: - virtual bool begin(AnalogConfig cfg) = 0; - virtual void end() = 0; - virtual size_t write(const uint8_t *src, size_t size_bytes) { return 0;} - virtual size_t readBytes(uint8_t *dest, size_t size_bytes) = 0; - virtual int available() = 0; - virtual int availableForWrite() { return DEFAULT_BUFFER_SIZE; } -}; - -} // ns -#endif diff --git a/src/AudioTools/CoreAudio/AudioAnalog/AnalogDriverESP32V1.h b/src/AudioTools/CoreAudio/AudioAnalog/AnalogDriverESP32V1.h deleted file mode 100644 index dd2f02e953..0000000000 --- a/src/AudioTools/CoreAudio/AudioAnalog/AnalogDriverESP32V1.h +++ /dev/null @@ -1,797 +0,0 @@ -#pragma once - -#include "AudioToolsConfig.h" - -#if defined(ESP32) && defined(USE_ANALOG) && \ - ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 0, 0) || defined(DOXYGEN) - -#ifdef ARDUINO - #ifndef perimanClearPinBus - #define perimanClearPinBus(p) perimanSetPinBus(p, ESP32_BUS_TYPE_INIT, NULL) - #endif -#endif - -#include "AudioTools/CoreAudio/AudioAnalog/AnalogDriverBase.h" -#include "AudioTools/CoreAudio/AudioAnalog/AnalogConfigESP32V1.h" -#include "AudioTools/CoreAudio/AudioStreams.h" -#include "AudioTools/CoreAudio/AudioStreamsConverter.h" - -namespace audio_tools { - -/** - * @brief AnalogAudioStream: A very fast DAC using DMA using the new - * dac_continuous API - * @ingroup platform - * @author Phil Schatzmann - * @copyright GPLv3 - */ -class AnalogDriverESP32V1 : public AnalogDriverBase { -public: - /// Default constructor - AnalogDriverESP32V1() {} - - /// Destructor - virtual ~AnalogDriverESP32V1() { - end(); - } - - /// Start the Analog driver - /// ---------------------------------------------------------- - bool begin(AnalogConfigESP32V1 cfg) { - TRACEI(); - bool result = true; - this->cfg = cfg; - - switch (cfg.rx_tx_mode) { - case TX_MODE: - if (!setup_tx()) return false; - // convert to 16 bits - if (!converter.begin(cfg, 16)) { - LOGE("converter"); - return false; - } - active_tx = true; - break; - case RX_MODE: - if (!setup_rx()) return false; - active_rx = true; - break; - default: - LOGE( "Unsupported MODE: %d", cfg.rx_tx_mode); - return false; - } - - active = true; - return active; - } - - /// Stop and uninstalls the driver - /// ---------------------------------------------------------- - void end() override { - TRACEI(); - if (active_tx) { - cleanup_tx(); - } - if (active_rx) { - cleanup_rx(); - } - - converter.end(); - - active_tx = false; - active_rx = false; - active = false; - } - - // Writes the data to the Digital to Analog Converter - // ---------------------------------------------------------- - size_t write(const uint8_t *src, size_t size_bytes) override { - // TRACED(); - // convert any format to int16_t - return converter.write(src, size_bytes); - } - - // Reads data from DMA buffer of the Analog to Digital Converter - // ---------------------------------------------------------- - size_t readBytes(uint8_t *dest, size_t size_bytes) override { - // TRACED(); - // Use the IO16Bit class for reading - return io.readBytes(dest, size_bytes); - } - - // How much data will there be available after reading ADC buffer - // ---------------------------------------------------------- - int available() override { - return active_rx ? (uint32_t)(cfg.buffer_size * sizeof(int16_t)) : 0; - } - -protected: - - /** - * @brief Custom FIFO class - */ - template - class FIFO { - public: - - FIFO() : size_(0), buffer_(nullptr), head_(0), tail_(0), count_(0) {} - - FIFO(size_t size) : size_(size), buffer_(new T[size]), head_(0), tail_(0), count_(0) {} - - ~FIFO() { - delete[] buffer_; - //LOGD("FIFO destroyed: size: %d, count: %d", size_, count_); - } - - bool push(const T& value) { - if (count_ < size_) { - buffer_[tail_] = value; - //LOGD("FIFO push - Value: %d at location: %d", value, tail_); - tail_ = (tail_ + 1) % size_; - count_++; - //LOGD("FIFO push updated tail: %d, count: %d", tail_, count_); - return true; - } - //LOGD("FIFO push failed - count %d > size %d", value, count_, size_); - return false; // Buffer full - } - - bool pop(T& value) { - if (count_ > 0) { - value = buffer_[head_]; - //LOGD("FIFO pop - Value: %d at location: %d", value, head_); - head_ = (head_ + 1) % size_; - count_--; - //LOGD("FIFO pop updated head: %d, count: %d", head_, count_); - return true; - } - //LOGD("FIFO pop failed - count %d == 0", count_); - return false; // Buffer empty - } - - size_t size() const { - return count_; - } - - bool empty() const { - return count_ == 0; - } - - bool full() const { - return count_ == size_; - } - - void clear() { - head_ = 0; - tail_ = 0; - count_ = 0; - } - - private: - size_t size_; - T* buffer_; - size_t head_; - size_t tail_; - size_t count_; - }; - - adc_continuous_handle_t adc_handle = nullptr; - adc_cali_handle_t adc_cali_handle = nullptr; - AnalogConfigESP32V1 cfg; - bool active = false; - bool active_tx = false; - bool active_rx = false; - ConverterAutoCenter auto_center; - #ifdef HAS_ESP32_DAC - dac_continuous_handle_t dac_handle = nullptr; - #endif - - // create array of FIFO buffers, one for each channel - FIFO** fifo_buffers; - - // 16Bit Audiostream for ESP32 - // ---------------------------------------------------------- - class IO16Bit : public AudioStream { - public: - IO16Bit(AnalogDriverESP32V1 *driver) { self = driver; } - - // Write int16_t data to the Digital to Analog Converter - // ---------------------------------------------------------- - size_t write(const uint8_t *src, size_t size_bytes) override { - // TRACED(); - #ifdef HAS_ESP32_DAC - size_t result = 0; - // Convert signed 16-bit to unsigned 8-bit - int16_t *data16 = (int16_t *)src; - uint8_t *data8 = (uint8_t *)src; - int samples = size_bytes / 2; - - // Process data in batches to reduce the number of conversions and writes - for (int j = 0; j < samples; j++) { - data8[j] = (32768u + data16[j]) >> 8; - } - - if (dac_continuous_write(self->dac_handle, data8, samples, &result, self->cfg.timeout) != ESP_OK) { - result = 0; - } - return result * 2; - #else - return 0; - #endif - } - - // Read int16_t data from Analog to Digital Converter - // ---------------------------------------------------------- - // FYI - // typedef struct { - // union { - // struct { - // uint16_t data: 12; /*! ADC_CHANNEL_MAX), The data is invalid. */ - // uint16_t unit: 1; /*!cfg.channels; - // for the adc_continuous_read function - adc_digi_output_data_t* result_data = (adc_digi_output_data_t*)malloc(samples_requested * sizeof(adc_digi_output_data_t)); - if (result_data == NULL) { - LOGE("Failed to allocate memory for result_data"); - return 0; // Handle memory allocation failure - } - memset(result_data, 0, samples_requested * sizeof(adc_digi_output_data_t)); - uint32_t bytes_read; // bytes from ADC buffer read - - // for output buffer - uint16_t *result16 = (uint16_t *)dest; // pointer to the destination buffer - uint16_t *end = (uint16_t *)(dest + size_bytes); // pointer to the end of the destination buffer - - // 1) read the requested bytes from the buffer - // LOGI("adc_continuous_read request:%d samples %d bytes requested", samples_requested, (uint32_t)(samples_requested * sizeof(adc_digi_output_data_t))); - if (adc_continuous_read(self->adc_handle, (uint8_t *)result_data, (uint32_t)(samples_requested * sizeof(adc_digi_output_data_t)), &bytes_read, (uint32_t)self->cfg.timeout) == ESP_OK) { - samples_read = bytes_read / sizeof(adc_digi_output_data_t); - LOGD("adc_continuous_read -> %u bytes / %d samples of %d bytes requested", (unsigned)bytes_read, samples_read, (int)(samples_requested * sizeof(adc_digi_output_data_t))); - - // Parse and store data in FIFO buffers - for (int i = 0; i < samples_read; i++) { - adc_digi_output_data_t *p = &result_data[i]; - ADC_CHANNEL_TYPE chan_num = AUDIO_ADC_GET_CHANNEL(p); - ADC_DATA_TYPE data = AUDIO_ADC_GET_DATA(p); - - // Find the index of the channel in the configured channels - idx = -1; - for (int j = 0; j < self->cfg.channels; ++j) { - if (self->cfg.adc_channels[j] == chan_num) { - idx = j; - break; - } - } - // Push the data to the corresponding FIFO buffer - if (idx >= 0) { - if (self->fifo_buffers[idx]->push(data)) { - LOGD("Sample %d, FIFO %d, ch %u, d %u", i, idx, (unsigned)chan_num, (unsigned)data); - } else { - LOGE("Sample %d, FIFO buffer is full, ch %u, d %u", i, (unsigned)chan_num, (unsigned)data); - } - } else { - LOGE("Sample %d, ch %u not found in configuration, d: %u", i, (unsigned)chan_num, (unsigned)data); - for (int k = 0; k < self->cfg.channels; ++k) { - LOGE("Available config ch: %u", self->cfg.adc_channels[k]); - } - } - - } - - // Determine min number of samples from all FIFO buffers - min_samples_in_fifo_per_channel = self->fifo_buffers[0]->size(); - for (int i = 1; i < self->cfg.channels; i++) { - fifo_size = self->fifo_buffers[i]->size(); - if (fifo_size < min_samples_in_fifo_per_channel) { - min_samples_in_fifo_per_channel = fifo_size; - } - } - - // 2) If necessary, top off the FIFO buffers to return the requested number of bytes - while (samples_requested_per_channel > min_samples_in_fifo_per_channel) { - - // obtain two extra sets of data (2 because number of bytes requested from ADC buffer needs to be divisible by 4) - // LOGI("adc_continuous_read request:%d samples %d bytes requested", samples_requested, (uint32_t)(samples_requested * sizeof(adc_digi_output_data_t))); - if (adc_continuous_read(self->adc_handle, (uint8_t *)result_data, (uint32_t)(2*self->cfg.channels * sizeof(adc_digi_output_data_t)), &bytes_read, (uint32_t)self->cfg.timeout) != ESP_OK) { - LOGE("Top off, adc_continuous_read unsuccessful"); - break; - } - - // Parse the additional data - samples_read = bytes_read / sizeof(adc_digi_output_data_t); - LOGD("Top Off: Requested %d samples per Channel, Min samples in FIFO: %d, Read additional %d bytes / %d samples", samples_requested_per_channel, min_samples_in_fifo_per_channel, (unsigned)bytes_read, samples_read); - - for (int i = 0; i < samples_read; i++) { - adc_digi_output_data_t *p = &result_data[i]; - ADC_CHANNEL_TYPE chan_num = AUDIO_ADC_GET_CHANNEL(p); - ADC_DATA_TYPE data = AUDIO_ADC_GET_DATA(p); - - // Find the index of the channel in the configured channels - idx = -1; - for (int j = 0; j < self->cfg.channels; ++j) { - if (self->cfg.adc_channels[j] == chan_num) { - idx = j; - break; - } - } - // Push the data to the corresponding FIFO buffer - if (idx >= 0) { - if (self->fifo_buffers[idx]->push(data)) { - LOGD("Top Off Sample %d, FIFO %d, ch %u, d %u", i, idx, (unsigned)chan_num, (unsigned)data); - } else { - LOGE("Top Off Sample %d, FIFO buffer is full, ch %u, d %u", i, (unsigned)chan_num, (unsigned)data); - } - } else { - LOGE("Top Off Sample %d, ch %u not found in configuration, d %u", i, (unsigned)chan_num, (unsigned)data); - for (int k = 0; k < self->cfg.channels; ++k) { - LOGE("Available config ch: %u", self->cfg.adc_channels[k]); - } - } - } - - // Determine the updated minimal number of samples in FIFO buffers - min_samples_in_fifo_per_channel = self->fifo_buffers[0]->size(); - max_samples_in_fifo_per_channel = self->fifo_buffers[0]->size(); - for (int i = 1; i < self->cfg.channels; i++) { - fifo_size = self->fifo_buffers[i]->size(); - if (fifo_size < min_samples_in_fifo_per_channel) { - min_samples_in_fifo_per_channel = fifo_size; - } - if (fifo_size > max_samples_in_fifo_per_channel) { - max_samples_in_fifo_per_channel = fifo_size; - } - } - LOGD("Min # of samples in FIFO: %d, Max # of samples in FIFO: %d", min_samples_in_fifo_per_channel, max_samples_in_fifo_per_channel); - } - - // 3) Calibrate and copy data to the output buffer - if (samples_requested_per_channel <= min_samples_in_fifo_per_channel) { - LOGD("Going to copying %d samples of %d samples/channel to output buffer", samples_requested, samples_requested_per_channel); - samples_provided_per_channel = samples_requested_per_channel; - } else { - // This should not happen as we topped off the FIFO buffers in step 2) - LOGE("Only %d samples per channel available for output buffer", min_samples_in_fifo_per_channel); - samples_provided_per_channel = min_samples_in_fifo_per_channel; - } - - for (int i = 0; i < samples_provided_per_channel; i++) { - for (int j = 0; j < self->cfg.channels; j++) { - ADC_DATA_TYPE data; - self->fifo_buffers[j]->pop(data); - if (result16 < end) { - if (self->cfg.adc_calibration_active) { - // Provide result in millivolts - auto err = adc_cali_raw_to_voltage(self->adc_cali_handle, (int)data, &data_milliVolts); - if (err == ESP_OK) { - *result16 = static_cast(data_milliVolts); - } else { - LOGE("adc_cali_raw_to_voltage error: %d", err); - *result16 = 0; - } - } else { - *result16 = data; - } - result16++; - } else { - LOGE("Buffer write overflow, skipping data"); - } - } - } - - bytes_provided = samples_provided_per_channel * self->cfg.channels * sizeof(int16_t); - - // 4) Engage centering if enabled - if (self->cfg.is_auto_center_read) { - self->auto_center.convert(dest, bytes_provided); - } - - } else { - LOGE("adc_continuous_read unsuccessful"); - bytes_provided = 0; - } - free(result_data); - return bytes_provided; - } - - protected: - AnalogDriverESP32V1 *self = nullptr; - - } io{this}; - - NumberFormatConverterStream converter{io}; - - // Setup Digital to Analog - // ---------------------------------------------------------- - #ifdef HAS_ESP32_DAC - bool setup_tx() { - dac_continuous_config_t cont_cfg = { - .chan_mask = cfg.channels == 1 ? cfg.dac_mono_channel : DAC_CHANNEL_MASK_ALL, - .desc_num = (uint32_t)cfg.buffer_count, - .buf_size = (size_t)cfg.buffer_size, - .freq_hz = (uint32_t)cfg.sample_rate, - .offset = 0, - .clk_src = cfg.use_apll ? DAC_DIGI_CLK_SRC_APLL : DAC_DIGI_CLK_SRC_DEFAULT, // Using APLL as clock source to get a wider frequency range - .chan_mode = cfg.channels == 1 ? DAC_CHANNEL_MODE_SIMUL : DAC_CHANNEL_MODE_ALTER, - }; - // Allocate continuous channels - if (dac_continuous_new_channels(&cont_cfg, &dac_handle) != ESP_OK) { - LOGE("new_channels"); - return false; - } - if (dac_continuous_enable(dac_handle) != ESP_OK) { - LOGE("enable"); - return false; - } - return true; - } - #else - bool setup_tx() { - LOGE("DAC not supported"); - return false; - } - #endif - - // Setup Analog to Digital Converter - // ---------------------------------------------------------- - bool setup_rx() { - adc_channel_t adc_channel; - int io_pin; - esp_err_t err; - - // Check the configuration - if (!checkADCChannels()) return false; - if (!checkADCSampleRate()) return false; - if (!checkADCBitWidth()) return false; - if (!checkADCBitsPerSample()) return false; - - if (adc_handle != nullptr) { - LOGE("adc unit %u continuous is already initialized. Please call end() first!", cfg.adc_unit); - return false; - } - - #ifdef ARDUINO - // Set periman deinit callback - // TODO, currently handled in end() method - - // Set the pins/channels to INIT state - for (int i = 0; i < cfg.channels; i++) { - adc_channel = cfg.adc_channels[i]; - adc_continuous_channel_to_io(cfg.adc_unit, adc_channel, &io_pin); - if (!perimanClearPinBus(io_pin)) { - LOGE("perimanClearPinBus failed!"); - return false; - } - } - #endif - - // Determine conv_frame_size which must be multiple of SOC_ADC_DIGI_DATA_BYTES_PER_CONV - // Old Code - uint32_t conv_frame_size = (uint32_t)cfg.buffer_size * SOC_ADC_DIGI_RESULT_BYTES; - #if CONFIG_IDF_TARGET_ESP32 || CONFIG_IDF_TARGET_ESP32S2 - uint8_t calc_multiple = conv_frame_size % SOC_ADC_DIGI_DATA_BYTES_PER_CONV; - if (calc_multiple != 0) { - conv_frame_size = (uint32_t) (conv_frame_size + calc_multiple); - } - #endif - - // Proposed new Code - // uint32_t conv_frame_size = (uint32_t)cfg.buffer_size * SOC_ADC_DIGI_RESULT_BYTES; - // #if CONFIG_IDF_TARGET_ESP32 || CONFIG_IDF_TARGET_ESP32S2 - // uint32_t conv_frame_size = (uint32_t)cfg.buffer_size * SOC_ADC_DIGI_DATA_BYTES_PER_CONV - // #endif - - // Conversion frame size buffer can't be bigger than 4092 bytes - if (conv_frame_size > 4092) { - LOGE("buffer_size is too big. Please set lower buffer_size."); - return false; - } else { - LOGI("buffer_size: %u samples, conv_frame_size: %u bytes", cfg.buffer_size, (unsigned)conv_frame_size); - } - - // Create adc_continuous handle - adc_continuous_handle_cfg_t adc_config; - adc_config.max_store_buf_size = (uint32_t)conv_frame_size * 2; - adc_config.conv_frame_size = (uint32_t) conv_frame_size; -#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 2, 0) - adc_config.flags.flush_pool = true; -#endif - err = adc_continuous_new_handle(&adc_config, &adc_handle); - if (err != ESP_OK) { - LOGE("adc_continuous_new_handle failed with error: %d", err); - return false; - } else { - LOGI("adc_continuous_new_handle successful"); - } - - // Configure the ADC patterns - adc_digi_pattern_config_t adc_pattern[cfg.channels] = {}; - for (int i = 0; i < cfg.channels; i++) { - uint8_t ch = cfg.adc_channels[i]; - adc_pattern[i].atten = (uint8_t) cfg.adc_attenuation; - adc_pattern[i].channel = (uint8_t)ch; - adc_pattern[i].unit = (uint8_t) cfg.adc_unit; - adc_pattern[i].bit_width = (uint8_t) cfg.adc_bit_width; - } - - // Configure the ADC - adc_continuous_config_t dig_cfg = { - .pattern_num = (uint32_t) cfg.channels, - .adc_pattern = adc_pattern, - .sample_freq_hz = (uint32_t)cfg.sample_rate * cfg.channels, - .conv_mode = (adc_digi_convert_mode_t) cfg.adc_conversion_mode, - .format = (adc_digi_output_format_t) cfg.adc_output_type, - }; - - // Log the configuration - LOGI("dig_cfg.sample_freq_hz: %u", (unsigned)dig_cfg.sample_freq_hz); - LOGI("dig_cfg.conv_mode: %u (1: unit 1, 2: unit 2, 3: both)", dig_cfg.conv_mode); - LOGI("dig_cfg.format: %u (0 is type1: [12bit data, 4bit channel])", dig_cfg.format); - for (int i = 0; i < cfg.channels; i++) { - LOGI("dig_cfg.adc_pattern[%d].atten: %u", i, dig_cfg.adc_pattern[i].atten); - LOGI("dig_cfg.adc_pattern[%d].channel: %u", i, dig_cfg.adc_pattern[i].channel); - LOGI("dig_cfg.adc_pattern[%d].unit: %u", i, dig_cfg.adc_pattern[i].unit); - LOGI("dig_cfg.adc_pattern[%d].bit_width: %u", i, dig_cfg.adc_pattern[i].bit_width); - } - - // Initialize ADC - err = adc_continuous_config(adc_handle, &dig_cfg); - if (err != ESP_OK) { - LOGE("adc_continuous_config unsuccessful with error: %d", err); - return false; - } - LOGI("adc_continuous_config successful"); - - // Set up optional calibration - if (!setupADCCalibration()) { - return false; - } - - // Attach the pins to the ADC unit - #ifdef ARDUINO - for (int i = 0; i < cfg.channels; i++) { - adc_channel = cfg.adc_channels[i]; - adc_continuous_channel_to_io(cfg.adc_unit, adc_channel, &io_pin); - // perimanSetPinBus: uint8_t pin, peripheral_bus_type_t type, void * bus, int8_t bus_num, int8_t bus_channel - if (!perimanSetPinBus(io_pin, ESP32_BUS_TYPE_ADC_CONT, (void *)(cfg.adc_unit + 1), cfg.adc_unit, adc_channel)) { - LOGE("perimanSetPinBus to Continuous an ADC Unit %u failed!", cfg.adc_unit); - return false; - } - } - #endif - - // Start ADC - err = adc_continuous_start(adc_handle); - if (err != ESP_OK) { - LOGE("adc_continuous_start unsuccessful with error: %d", err); - return false; - } - - // Setup up optimal auto center which puts the avg at 0 - auto_center.begin(cfg.channels, cfg.bits_per_sample, true); - - // Initialize the FIFO buffers - size_t fifo_size = (cfg.buffer_size / cfg.channels) + 8; // Add a few extra elements - fifo_buffers = new FIFO*[cfg.channels]; // Allocate an array of FIFO objects - for (int i = 0; i < cfg.channels; ++i) { - fifo_buffers[i] = new FIFO(fifo_size); - } - LOGI("%d FIFO buffers allocated of size %d", cfg.channels, fifo_size); - - LOGI("Setup ADC successful"); - - return true; - } - - /// Cleanup dac - bool cleanup_tx() { - bool ok = true; -#ifdef HAS_ESP32_DAC - if (dac_handle==nullptr) return true; - if (dac_continuous_disable(dac_handle) != ESP_OK){ - ok = false; - LOGE("dac_continuous_disable failed"); - } - if (dac_continuous_del_channels(dac_handle) != ESP_OK){ - ok = false; - LOGE("dac_continuous_del_channels failed"); - } - dac_handle = nullptr; -#endif - return ok; - } - -#ifdef ARDUINO - // dummy detach: w/o this it's failing - static bool adcDetachBus(void *bus) { - LOGD("===> adcDetachBus: %d", (int) bus); - return true; - } -#endif - - /// Cleanup Analog to Digital Converter - bool cleanup_rx() { - if (adc_handle==nullptr) return true; - adc_continuous_stop(adc_handle); - adc_continuous_deinit(adc_handle); - if (cfg.adc_calibration_active) { - #if ADC_CALI_SCHEME_CURVE_FITTING_SUPPORTED - adc_cali_delete_scheme_curve_fitting(adc_cali_handle); - #elif !defined(CONFIG_IDF_TARGET_ESP32H2) && !defined(CONFIG_IDF_TARGET_ESP32P4) - adc_cali_delete_scheme_line_fitting(adc_cali_handle); - #endif - } - - // Clean up the FIFO buffers - if (fifo_buffers != nullptr) { - for (int i = 0; i < cfg.channels; ++i) { - delete fifo_buffers[i]; - } - delete[] fifo_buffers; - fifo_buffers = nullptr; - } - -#ifdef ARDUINO - // Set all used pins/channels to INIT state - perimanSetBusDeinit(ESP32_BUS_TYPE_ADC_CONT, adcDetachBus); - for (int i = 0; i < cfg.channels; i++) { - adc_channel_t adc_channel = cfg.adc_channels[i]; - int io_pin; - adc_continuous_channel_to_io(cfg.adc_unit, adc_channel, &io_pin); - if (perimanGetPinBusType(io_pin) == ESP32_BUS_TYPE_ADC_CONT) { - if (!perimanClearPinBus(io_pin)) { - LOGE("perimanClearPinBus failed!"); - } - } - } -#endif - adc_handle = nullptr; - return true; // Return true to indicate successful cleanup - } - - /// ---------------------------------------------------------- - bool checkADCBitWidth() { - if ((cfg.adc_bit_width < SOC_ADC_DIGI_MIN_BITWIDTH) || - (cfg.adc_bit_width > SOC_ADC_DIGI_MAX_BITWIDTH)) { - LOGE("adc bit width: %u cannot be set, range: %u to %u", cfg.adc_bit_width, - (unsigned)SOC_ADC_DIGI_MIN_BITWIDTH, (unsigned)SOC_ADC_DIGI_MAX_BITWIDTH); - return false; - } - LOGI("adc bit width: %u, range: %u to %u", cfg.adc_bit_width, - (unsigned)SOC_ADC_DIGI_MIN_BITWIDTH, (unsigned)SOC_ADC_DIGI_MAX_BITWIDTH); - return true; - } - - /// ---------------------------------------------------------- - bool checkADCChannels() { - int io_pin; - adc_channel_t adc_channel; - - int max_channels = sizeof(cfg.adc_channels) / sizeof(adc_channel_t); - if (cfg.channels > max_channels) { - LOGE("number of channels: %d, max: %d", cfg.channels, max_channels); - return false; - } - LOGI("channels: %d, max: %d", cfg.channels, max_channels); - - // Lets make sure the adc channels are available - for (int i = 0; i < cfg.channels; i++) { - adc_channel = cfg.adc_channels[i]; - auto err = adc_continuous_channel_to_io(cfg.adc_unit, adc_channel, &io_pin); - if (err != ESP_OK) { - LOGE("ADC channel %u is not available on ADC unit %u", adc_channel, cfg.adc_unit); - return false; - } else { - LOGI("ADC channel %u is on pin %u", adc_channel, io_pin); - } - } - return true; - } - - /// ---------------------------------------------------------- - bool checkADCSampleRate() { - int sample_rate = cfg.sample_rate * cfg.channels; - if ((sample_rate < SOC_ADC_SAMPLE_FREQ_THRES_LOW) || - (sample_rate > SOC_ADC_SAMPLE_FREQ_THRES_HIGH)) { - LOGE("sample rate eff: %u can not be set, range: %u to %u", sample_rate, - SOC_ADC_SAMPLE_FREQ_THRES_LOW, SOC_ADC_SAMPLE_FREQ_THRES_HIGH); - return false; - } - LOGI("sample rate eff: %u, range: %u to %u", sample_rate, - SOC_ADC_SAMPLE_FREQ_THRES_LOW, SOC_ADC_SAMPLE_FREQ_THRES_HIGH); - - return true; - } - - /// ---------------------------------------------------------- - bool checkADCBitsPerSample() { - int supported_bits = 16; // for the time being we support only 16 bits! - - // calculated default value if nothing is specified - if (cfg.bits_per_sample == 0) { - cfg.bits_per_sample = supported_bits; - LOGI("bits per sample set to: %d", cfg.bits_per_sample); - } - - // check bits_per_sample - if (cfg.bits_per_sample != supported_bits) { - LOGE("bits per sample: error. It should be: %d but is %d", - supported_bits, cfg.bits_per_sample); - return false; - } - LOGI("bits per sample: %d", cfg.bits_per_sample); - return true; - } - - /// ---------------------------------------------------------- - bool setupADCCalibration() { - if (!cfg.adc_calibration_active) - return true; - - // Initialize ADC calibration handle - // Calibration is applied to an ADC unit (not per channel). - - // setup calibration only when requested - esp_err_t err = ESP_OK; - - if (adc_cali_handle == NULL) { - #if ADC_CALI_SCHEME_CURVE_FITTING_SUPPORTED - // curve fitting is preferred - adc_cali_curve_fitting_config_t cali_config; - cali_config.unit_id = cfg.adc_unit; - cali_config.atten = (adc_atten_t)cfg.adc_attenuation; - cali_config.bitwidth = (adc_bitwidth_t)cfg.adc_bit_width; - err = adc_cali_create_scheme_curve_fitting(&cali_config, &adc_cali_handle); - #elif !defined(CONFIG_IDF_TARGET_ESP32H2) && !defined(CONFIG_IDF_TARGET_ESP32P4) - // line fitting is the alternative - adc_cali_line_fitting_config_t cali_config; - cali_config.unit_id = cfg.adc_unit; - cali_config.atten = (adc_atten_t)cfg.adc_attenuation; - cali_config.bitwidth = (adc_bitwidth_t)cfg.adc_bit_width; - err = adc_cali_create_scheme_line_fitting(&cali_config, &adc_cali_handle); - #endif - if (err != ESP_OK) { - LOGE("creating calibration handle failed for ADC%d with atten %d and bitwidth %d", - cfg.adc_unit, cfg.adc_attenuation, cfg.adc_bit_width); - return false; - } else { - LOGI("enabled calibration for ADC%d with atten %d and bitwidth %d", - cfg.adc_unit, cfg.adc_attenuation, cfg.adc_bit_width); - } - } - return true; - } - -}; - -/// @brief AnalogAudioStream -using AnalogDriver = AnalogDriverESP32V1; - -} // namespace audio_tools - -#endif diff --git a/src/AudioTools/CoreAudio/AudioAnalog/AnalogDriverESP32V2.h b/src/AudioTools/CoreAudio/AudioAnalog/AnalogDriverESP32V2.h deleted file mode 100644 index b6e5fcf528..0000000000 --- a/src/AudioTools/CoreAudio/AudioAnalog/AnalogDriverESP32V2.h +++ /dev/null @@ -1,621 +0,0 @@ -#pragma once - -#include "AudioToolsConfig.h" - -#if defined(ESP32) && defined(USE_ANALOG) && \ - ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 0, 0) || defined(DOXYGEN) - -#ifdef ARDUINO - #ifndef perimanClearPinBus - #define perimanClearPinBus(p) perimanSetPinBus(p, ESP32_BUS_TYPE_INIT, NULL) - #endif -#endif - -#include "AudioTools/CoreAudio/AudioAnalog/AnalogDriverBase.h" -#include "AudioTools/CoreAudio/AudioAnalog/AnalogConfigESP32V1.h" -#include "AudioTools/CoreAudio/AudioStreams.h" -#include "AudioTools/CoreAudio/AudioStreamsConverter.h" - -namespace audio_tools { - -/** - * @brief AnalogAudioStream: A very fast DAC using DMA using the new - * dac_continuous API. This implementation assumes that the issue with - * the different number of samples per channel has been resolved! - * https://github.com/espressif/esp-idf/issues/12337 - * @ingroup platform - * @author Phil Schatzmann - * @copyright GPLv3 - */ -class AnalogDriverESP32V2 : public AnalogDriverBase { -public: - /// Default constructor - AnalogDriverESP32V2() = default; - - /// Destructor - virtual ~AnalogDriverESP32V2() { - end(); - } - - /// Start the Analog driver - /// ---------------------------------------------------------- - bool begin(AnalogConfigESP32V1 cfg) { - TRACEI(); - bool result = true; - this->cfg = cfg; - - switch (cfg.rx_tx_mode) { - case TX_MODE: - if (!setup_tx()) return false; - // convert to 16 bits - if (!converter.begin(cfg, 16)) { - LOGE("converter"); - return false; - } - active_tx = true; - break; - case RX_MODE: - if (!setup_rx()) return false; - active_rx = true; - break; - default: - LOGE( "Unsupported MODE: %d", cfg.rx_tx_mode); - return false; - } - - active = true; - return active; - } - - /// Stop and uninstalls the driver - /// ---------------------------------------------------------- - void end() override { - TRACEI(); - if (active_tx) { - cleanup_tx(); - } - if (active_rx) { - cleanup_rx(); - } - - converter.end(); - - active_tx = false; - active_rx = false; - active = false; - } - - // Writes the data to the Digital to Analog Converter - // ---------------------------------------------------------- - size_t write(const uint8_t *src, size_t size_bytes) override { - // TRACED(); - // convert any format to int16_t - return converter.write(src, size_bytes); - } - - // Reads data from DMA buffer of the Analog to Digital Converter - // ---------------------------------------------------------- - size_t readBytes(uint8_t *dest, size_t size_bytes) override { - TRACED(); - // Use the IO16Bit class for reading - return io.readBytes(dest, size_bytes); - } - - // How much data will there be available after reading ADC buffer - // ---------------------------------------------------------- - int available() override { - return active_rx ? (uint32_t)(cfg.buffer_size * sizeof(int16_t)) : 0; - } - -protected: - - adc_continuous_handle_t adc_handle = nullptr; - adc_cali_handle_t adc_cali_handle = nullptr; - AnalogConfigESP32V1 cfg; - bool active = false; - bool active_tx = false; - bool active_rx = false; - ConverterAutoCenter auto_center; - #ifdef HAS_ESP32_DAC - dac_continuous_handle_t dac_handle = nullptr; - #endif - - - // 16Bit Audiostream for ESP32 - // ---------------------------------------------------------- - class IO16Bit : public AudioStream { - public: - IO16Bit(AnalogDriverESP32V2 *driver) { self = driver; } - - /// Write int16_t data to the Digital to Analog Converter - size_t write(const uint8_t *src, size_t size_bytes) override { - // TRACED(); -#ifdef HAS_ESP32_DAC - size_t result = 0; - // Convert signed 16-bit to unsigned 8-bit - int16_t *data16 = (int16_t *)src; - uint8_t *data8 = (uint8_t *)src; - int samples = size_bytes / 2; - - // Process data in batches to reduce the number of conversions and writes - for (int j = 0; j < samples; j++) { - data8[j] = (32768u + data16[j]) >> 8; - } - - if (dac_continuous_write(self->dac_handle, data8, samples, &result, self->cfg.timeout) != ESP_OK) { - result = 0; - } - return result * 2; -#else - return 0; -#endif - } - - /// Read int16_t data from ADC - size_t readBytes(uint8_t *dest, size_t size_bytes) { - LOGD("readBytes: %d", (int) size_bytes); - size_t result = 0; - int channels = self->cfg.channels; - assert(channels > 0); - sampleIndex.resize(channels); - for(int ch=0;ch < channels;ch++){ - sampleIndex[ch] = 0; - } - uint16_t *result16 = (uint16_t *)dest; // pointer to the destination buffer - int samples_requested = size_bytes / sizeof(int16_t); - int samples_requested_per_channel = samples_requested / channels; - int buffer_size = samples_requested * sizeof(adc_digi_output_data_t); - if (result_data.size() < buffer_size){ - result_data.resize(buffer_size); - } - - int read_size_bytes = buffer_size; - - // read the requested bytes into the buffer - while (read_size_bytes > 0){ - uint32_t bytes_read = 0; - if (adc_continuous_read(self->adc_handle, (uint8_t *)result_data.data(), (uint32_t)read_size_bytes, &bytes_read, (uint32_t)self->cfg.timeout) == ESP_OK) { - int samples_read = bytes_read / sizeof(adc_digi_output_data_t); - LOGD("adc_continuous_read -> %u bytes / %d samples of %d bytes requested", (unsigned)bytes_read, samples_read, (int)(samples_requested * sizeof(adc_digi_output_data_t))); - - // Provide samples to dest - for (int i = 0; i < samples_read; i++) { - adc_digi_output_data_t *p = (adc_digi_output_data_t*) &result_data[i]; - ADC_CHANNEL_TYPE chan_num = AUDIO_ADC_GET_CHANNEL(p); - ADC_DATA_TYPE sample_value = AUDIO_ADC_GET_DATA(p); - int channel = getChannelIdx(chan_num); - - // provide data to dest - if (channel >=0 && channel < channels){ - int idx = channel + (sampleIndex[channel] * channels); - LOGI("idx for %d: %d", channel, idx); - if(idx < samples_requested){ - // Provide result in millivolts - if (self->cfg.adc_calibration_active) { - result16[idx] = getCalibratedValue(sample_value); - } else { - result16[idx] = sample_value; - } - // increase index for the actual channel - sampleIndex[channel]++; - } else { - LOGE("Invalid idx: %d / max %d", idx, samples_requested); - } - } else { - LOGE("Invalid channel: %d", channel); - } - } - /// find channel with min samples - int samples_available = getMinSamplesForAllChannels(channels); - result = samples_available * channels * sizeof(int16_t); - - /// Read some more samples if we do not have the requested size for all channels - read_size_bytes = result - size_bytes; - if (read_size_bytes > 0) - LOGI("read missing samples: %d", samples_requested_per_channel - samples_available); - } else { - LOGE("adc_continuous_read error"); - break; - } - } - - - // Centering if enabled - if (self->cfg.is_auto_center_read) { - self->auto_center.convert(dest, result); - } - - return result; - } - - protected: - AnalogDriverESP32V2 *self = nullptr; - Vector result_data{0}; - Vector sampleIndex{0}; - - int getChannelIdx(ADC_CHANNEL_TYPE chan_num){ - for (int j = 0; j < self->cfg.channels; ++j) { - if (self->cfg.adc_channels[j] == chan_num) { - return j; - } - } - return -1; - } - - int16_t getCalibratedValue(ADC_DATA_TYPE sample_value){ - int data_milliVolts = 0; - int16_t result = 0; - auto err = adc_cali_raw_to_voltage(self->adc_cali_handle, sample_value, &data_milliVolts); - if (err == ESP_OK) { - result = static_cast(data_milliVolts); - } else { - LOGE("adc_cali_raw_to_voltage error: %d", err); - } - return result; - } - - int getMinSamplesForAllChannels(int channels) { - int result = 100000; - for (int ch = 0; ch < channels; ch++){ - if (sampleIndex[ch] < result){ - result = sampleIndex[ch]; - } - } - return result; - } - - } io{this}; - - NumberFormatConverterStream converter{io}; - - // Setup Digital to Analog - // ---------------------------------------------------------- - #ifdef HAS_ESP32_DAC - bool setup_tx() { - dac_continuous_config_t cont_cfg = { - .chan_mask = cfg.channels == 1 ? cfg.dac_mono_channel : DAC_CHANNEL_MASK_ALL, - .desc_num = (uint32_t)cfg.buffer_count, - .buf_size = (size_t)cfg.buffer_size, - .freq_hz = (uint32_t)cfg.sample_rate, - .offset = 0, - .clk_src = cfg.use_apll ? DAC_DIGI_CLK_SRC_APLL : DAC_DIGI_CLK_SRC_DEFAULT, // Using APLL as clock source to get a wider frequency range - .chan_mode = cfg.channels == 1 ? DAC_CHANNEL_MODE_SIMUL : DAC_CHANNEL_MODE_ALTER, - }; - // Allocate continuous channels - if (dac_continuous_new_channels(&cont_cfg, &dac_handle) != ESP_OK) { - LOGE("new_channels"); - return false; - } - if (dac_continuous_enable(dac_handle) != ESP_OK) { - LOGE("enable"); - return false; - } - return true; - } - #else - bool setup_tx() { - LOGE("DAC not supported"); - return false; - } - #endif - - // Setup Analog to Digital Converter - // ---------------------------------------------------------- - bool setup_rx() { - adc_channel_t adc_channel; - int io_pin; - esp_err_t err; - - // Check the configuration - if (!checkADCChannels()) return false; - if (!checkADCSampleRate()) return false; - if (!checkADCBitWidth()) return false; - if (!checkADCBitsPerSample()) return false; - - if (adc_handle != nullptr) { - LOGE("adc unit %u continuous is already initialized. Please call end() first!", cfg.adc_unit); - return false; - } - - #ifdef ARDUINO - // Set periman deinit callback - // TODO, currently handled in end() method - - // Set the pins/channels to INIT state - for (int i = 0; i < cfg.channels; i++) { - adc_channel = cfg.adc_channels[i]; - adc_continuous_channel_to_io(cfg.adc_unit, adc_channel, &io_pin); - if (!perimanClearPinBus(io_pin)) { - LOGE("perimanClearPinBus failed!"); - return false; - } - } - #endif - - // Determine conv_frame_size which must be multiple of SOC_ADC_DIGI_DATA_BYTES_PER_CONV - // Old Code - uint32_t conv_frame_size = (uint32_t)cfg.buffer_size * SOC_ADC_DIGI_RESULT_BYTES; - #if CONFIG_IDF_TARGET_ESP32 || CONFIG_IDF_TARGET_ESP32S2 - uint8_t calc_multiple = conv_frame_size % SOC_ADC_DIGI_DATA_BYTES_PER_CONV; - if (calc_multiple != 0) { - conv_frame_size = (uint32_t) (conv_frame_size + calc_multiple); - } - #endif - - // Proposed new Code - // uint32_t conv_frame_size = (uint32_t)cfg.buffer_size * SOC_ADC_DIGI_RESULT_BYTES; - // #if CONFIG_IDF_TARGET_ESP32 || CONFIG_IDF_TARGET_ESP32S2 - // uint32_t conv_frame_size = (uint32_t)cfg.buffer_size * SOC_ADC_DIGI_DATA_BYTES_PER_CONV - // #endif - - // Conversion frame size buffer can't be bigger than 4092 bytes - if (conv_frame_size > 4092) { - LOGE("buffer_size is too big. Please set lower buffer_size."); - return false; - } else { - LOGI("buffer_size: %u samples, conv_frame_size: %u bytes", cfg.buffer_size, (unsigned)conv_frame_size); - } - - // Create adc_continuous handle - adc_continuous_handle_cfg_t adc_config; - adc_config.max_store_buf_size = (uint32_t)conv_frame_size * 2; - adc_config.conv_frame_size = (uint32_t) conv_frame_size; -#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 2, 0) - adc_config.flags.flush_pool = true; -#endif - err = adc_continuous_new_handle(&adc_config, &adc_handle); - if (err != ESP_OK) { - LOGE("adc_continuous_new_handle failed with error: %d", err); - return false; - } else { - LOGI("adc_continuous_new_handle successful"); - } - - // Configure the ADC patterns - adc_digi_pattern_config_t adc_pattern[cfg.channels] = {}; - for (int i = 0; i < cfg.channels; i++) { - uint8_t ch = cfg.adc_channels[i]; - adc_pattern[i].atten = (uint8_t) cfg.adc_attenuation; - adc_pattern[i].channel = (uint8_t)ch; - adc_pattern[i].unit = (uint8_t) cfg.adc_unit; - adc_pattern[i].bit_width = (uint8_t) cfg.adc_bit_width; - } - - // Configure the ADC - adc_continuous_config_t dig_cfg = { - .pattern_num = (uint32_t) cfg.channels, - .adc_pattern = adc_pattern, - .sample_freq_hz = (uint32_t)cfg.sample_rate * cfg.channels, - .conv_mode = (adc_digi_convert_mode_t) cfg.adc_conversion_mode, - .format = (adc_digi_output_format_t) cfg.adc_output_type, - }; - - // Log the configuration - LOGI("dig_cfg.sample_freq_hz: %u", (unsigned)dig_cfg.sample_freq_hz); - LOGI("dig_cfg.conv_mode: %u (1: unit 1, 2: unit 2, 3: both)", dig_cfg.conv_mode); - LOGI("dig_cfg.format: %u (0 is type1: [12bit data, 4bit channel])", dig_cfg.format); - for (int i = 0; i < cfg.channels; i++) { - LOGI("dig_cfg.adc_pattern[%d].atten: %u", i, dig_cfg.adc_pattern[i].atten); - LOGI("dig_cfg.adc_pattern[%d].channel: %u", i, dig_cfg.adc_pattern[i].channel); - LOGI("dig_cfg.adc_pattern[%d].unit: %u", i, dig_cfg.adc_pattern[i].unit); - LOGI("dig_cfg.adc_pattern[%d].bit_width: %u", i, dig_cfg.adc_pattern[i].bit_width); - } - - // Initialize ADC - err = adc_continuous_config(adc_handle, &dig_cfg); - if (err != ESP_OK) { - LOGE("adc_continuous_config unsuccessful with error: %d", err); - return false; - } - LOGI("adc_continuous_config successful"); - - // Set up optional calibration - if (!setupADCCalibration()) { - return false; - } - - // Attach the pins to the ADC unit -#ifdef ARDUINO - for (int i = 0; i < cfg.channels; i++) { - adc_channel = cfg.adc_channels[i]; - adc_continuous_channel_to_io(cfg.adc_unit, adc_channel, &io_pin); - // perimanSetPinBus: uint8_t pin, peripheral_bus_type_t type, void * bus, int8_t bus_num, int8_t bus_channel - if (!perimanSetPinBus(io_pin, ESP32_BUS_TYPE_ADC_CONT, (void *)(cfg.adc_unit + 1), cfg.adc_unit, adc_channel)) { - LOGE("perimanSetPinBus to Continuous an ADC Unit %u failed!", cfg.adc_unit); - return false; - } - } -#endif - - // Start ADC - err = adc_continuous_start(adc_handle); - if (err != ESP_OK) { - LOGE("adc_continuous_start unsuccessful with error: %d", err); - return false; - } - - // Setup up optimal auto center which puts the avg at 0 - auto_center.begin(cfg.channels, cfg.bits_per_sample, true); - - LOGI("Setup ADC successful"); - - return true; - } - - /// Cleanup dac - bool cleanup_tx() { - bool ok = true; -#ifdef HAS_ESP32_DAC - if (dac_handle==nullptr) return true; - if (dac_continuous_disable(dac_handle) != ESP_OK){ - ok = false; - LOGE("dac_continuous_disable failed"); - } - if (dac_continuous_del_channels(dac_handle) != ESP_OK){ - ok = false; - LOGE("dac_continuous_del_channels failed"); - } - dac_handle = nullptr; -#endif - return ok; - } - -#ifdef ARDUINO - // dummy detach: w/o this it's failing - static bool adcDetachBus(void *bus) { - LOGD("===> adcDetachBus: %d", (int) bus); - return true; - } -#endif - - /// Cleanup Analog to Digital Converter - bool cleanup_rx() { - if (adc_handle==nullptr) return true; - adc_continuous_stop(adc_handle); - adc_continuous_deinit(adc_handle); - if (cfg.adc_calibration_active) { - #if ADC_CALI_SCHEME_CURVE_FITTING_SUPPORTED - adc_cali_delete_scheme_curve_fitting(adc_cali_handle); - #elif !defined(CONFIG_IDF_TARGET_ESP32H2) - adc_cali_delete_scheme_line_fitting(adc_cali_handle); - #endif - } - -#ifdef ARDUINO - // Set all used pins/channels to INIT state - perimanSetBusDeinit(ESP32_BUS_TYPE_ADC_CONT, adcDetachBus); - for (int i = 0; i < cfg.channels; i++) { - adc_channel_t adc_channel = cfg.adc_channels[i]; - int io_pin; - adc_continuous_channel_to_io(cfg.adc_unit, adc_channel, &io_pin); - if (perimanGetPinBusType(io_pin) == ESP32_BUS_TYPE_ADC_CONT) { - if (!perimanClearPinBus(io_pin)) { - LOGE("perimanClearPinBus failed!"); - } - } - } -#endif - adc_handle = nullptr; - return true; // Return true to indicate successful cleanup - } - - /// ---------------------------------------------------------- - bool checkADCBitWidth() { - if ((cfg.adc_bit_width < SOC_ADC_DIGI_MIN_BITWIDTH) || - (cfg.adc_bit_width > SOC_ADC_DIGI_MAX_BITWIDTH)) { - LOGE("adc bit width: %u cannot be set, range: %u to %u", cfg.adc_bit_width, - (unsigned)SOC_ADC_DIGI_MIN_BITWIDTH, (unsigned)SOC_ADC_DIGI_MAX_BITWIDTH); - return false; - } - LOGI("adc bit width: %u, range: %u to %u", cfg.adc_bit_width, - (unsigned)SOC_ADC_DIGI_MIN_BITWIDTH, (unsigned)SOC_ADC_DIGI_MAX_BITWIDTH); - return true; - } - - /// ---------------------------------------------------------- - bool checkADCChannels() { - int io_pin; - adc_channel_t adc_channel; - - int max_channels = sizeof(cfg.adc_channels) / sizeof(adc_channel_t); - if (cfg.channels > max_channels) { - LOGE("number of channels: %d, max: %d", cfg.channels, max_channels); - return false; - } - LOGI("channels: %d, max: %d", cfg.channels, max_channels); - - // Lets make sure the adc channels are available - for (int i = 0; i < cfg.channels; i++) { - adc_channel = cfg.adc_channels[i]; - auto err = adc_continuous_channel_to_io(cfg.adc_unit, adc_channel, &io_pin); - if (err != ESP_OK) { - LOGE("ADC channel %u is not available on ADC unit %u", adc_channel, cfg.adc_unit); - return false; - } else { - LOGI("ADC channel %u is on pin %u", adc_channel, io_pin); - } - } - return true; - } - - /// ---------------------------------------------------------- - bool checkADCSampleRate() { - LOGI("sample rate (audio): %d", cfg.sample_rate); - int sample_rate = cfg.sample_rate * cfg.channels; - if ((sample_rate < SOC_ADC_SAMPLE_FREQ_THRES_LOW) || - (sample_rate > SOC_ADC_SAMPLE_FREQ_THRES_HIGH)) { - LOGE("sample rate eff: %u can not be set, range: %u to %u", sample_rate, - SOC_ADC_SAMPLE_FREQ_THRES_LOW, SOC_ADC_SAMPLE_FREQ_THRES_HIGH); - return false; - } - LOGI("sample rate eff: %u, range: %u to %u", sample_rate, - SOC_ADC_SAMPLE_FREQ_THRES_LOW, SOC_ADC_SAMPLE_FREQ_THRES_HIGH); - - return true; - } - - /// ---------------------------------------------------------- - bool checkADCBitsPerSample() { - int supported_bits = 16; // for the time being we support only 16 bits! - - // calculated default value if nothing is specified - if (cfg.bits_per_sample == 0) { - cfg.bits_per_sample = supported_bits; - LOGI("bits per sample set to: %d", cfg.bits_per_sample); - } - - // check bits_per_sample - if (cfg.bits_per_sample != supported_bits) { - LOGE("bits per sample: error. It should be: %d but is %d", - supported_bits, cfg.bits_per_sample); - return false; - } - LOGI("bits per sample: %d", cfg.bits_per_sample); - return true; - } - - /// ---------------------------------------------------------- - bool setupADCCalibration() { - if (!cfg.adc_calibration_active) - return true; - - // Initialize ADC calibration handle - // Calibration is applied to an ADC unit (not per channel). - - // setup calibration only when requested - if (adc_cali_handle == NULL) { - #if ADC_CALI_SCHEME_CURVE_FITTING_SUPPORTED - // curve fitting is preferred - adc_cali_curve_fitting_config_t cali_config; - cali_config.unit_id = cfg.adc_unit; - cali_config.atten = (adc_atten_t)cfg.adc_attenuation; - cali_config.bitwidth = (adc_bitwidth_t)cfg.adc_bit_width; - auto err = adc_cali_create_scheme_curve_fitting(&cali_config, &adc_cali_handle); - #elif !defined(CONFIG_IDF_TARGET_ESP32H2) - // line fitting is the alternative - adc_cali_line_fitting_config_t cali_config; - cali_config.unit_id = cfg.adc_unit; - cali_config.atten = (adc_atten_t)cfg.adc_attenuation; - cali_config.bitwidth = (adc_bitwidth_t)cfg.adc_bit_width; - auto err = adc_cali_create_scheme_line_fitting(&cali_config, &adc_cali_handle); - #endif - if (err != ESP_OK) { - LOGE("creating calibration handle failed for ADC%d with atten %d and bitwidth %d", - cfg.adc_unit, cfg.adc_attenuation, cfg.adc_bit_width); - return false; - } else { - LOGI("enabled calibration for ADC%d with atten %d and bitwidth %d", - cfg.adc_unit, cfg.adc_attenuation, cfg.adc_bit_width); - } - } - return true; - } - -}; - -/// @brief AnalogAudioStream -using AnalogDriver = AnalogDriverESP32V2; - -} // namespace audio_tools - -#endif diff --git a/src/AudioTools/CoreAudio/AudioBasic/Collections.h b/src/AudioTools/CoreAudio/AudioBasic/Collections.h deleted file mode 100644 index 01909cf620..0000000000 --- a/src/AudioTools/CoreAudio/AudioBasic/Collections.h +++ /dev/null @@ -1,15 +0,0 @@ -#pragma once -/** - * @defgroup collections Collections - * @ingroup tools - * @brief Vector, List, Queue, Stack... - */ - -#include "AudioTools/CoreAudio/AudioBasic/Collections/Vector.h" -#include "AudioTools/CoreAudio/AudioBasic/Collections/List.h" -#include "AudioTools/CoreAudio/AudioBasic/Collections/Stack.h" -#include "AudioTools/CoreAudio/AudioBasic/Collections/Queue.h" -#include "AudioTools/CoreAudio/AudioBasic/Collections/QueueFromVector.h" -#include "AudioTools/CoreAudio/AudioBasic/Collections/BitVector.h" -#include "AudioTools/CoreAudio/AudioBasic/Collections/Slice.h" -#include "AudioTools/CoreAudio/AudioBasic/Collections/PriorityQueue.h" \ No newline at end of file diff --git a/src/AudioTools/CoreAudio/AudioBasic/Collections/Allocator.h b/src/AudioTools/CoreAudio/AudioBasic/Collections/Allocator.h deleted file mode 100644 index f110581cef..0000000000 --- a/src/AudioTools/CoreAudio/AudioBasic/Collections/Allocator.h +++ /dev/null @@ -1,176 +0,0 @@ -#pragma once -#include - -#include "AudioToolsConfig.h" -#include "AudioTools/CoreAudio/AudioLogger.h" -#include "AudioTools/CoreAudio/AudioRuntime.h" - -namespace audio_tools { - -/** - * @defgroup memorymgmt Memory Management - * @ingroup tools - * @brief Allocators and Memory Manager - */ - -/** - * @brief Memory allocateator which uses malloc. - * @ingroup memorymgmt - * @author Phil Schatzmann - * @copyright GPLv3 - */ - -class Allocator { - public: - // creates an object - template - T* create() { - void* addr = allocate(sizeof(T)); - // call constructor - T* ref = new (addr) T(); - return ref; - } - - /// deletes an object - template - void remove(T* obj) { - if (obj == nullptr) return; - obj->~T(); - free((void*)obj); - } - - // creates an array of objects - template - T* createArray(int len) { - void* addr = allocate(sizeof(T) * len); - T* addrT = (T*)addr; - // call constructor -#ifndef NO_INPLACE_INIT_SUPPORT - for (int j = 0; j < len; j++) new (addrT + j) T(); -#else - T default_value; - for (int j = 0; j < len; j++) { - memcpy((uint8_t*)addr+(j*sizeof(T)), &default_value, sizeof(T)); - } -#endif - return (T*)addr; - } - - // deletes an array of objects - template - void removeArray(T* obj, int len) { - if (obj == nullptr) return; - for (int j = 0; j < len; j++) { - obj[j].~T(); - } - free((void*)obj); - } - - /// Allocates memory - virtual void* allocate(size_t size) { - void* result = do_allocate(size); - if (result == nullptr) { - LOGE("Allocateation failed for %zu bytes", size); - stop(); - } else { - LOGD("Allocated %zu", size); - } - return result; - } - - /// frees memory - virtual void free(void* memory) { - if (memory != nullptr) ::free(memory); - } - - protected: - virtual void* do_allocate(size_t size) { - return calloc(1, size == 0 ? 1 : size); - } -}; - -/** - * @brief Memory allocateator which uses ps_malloc (on the ESP32) and if this - * fails it resorts to malloc. - * @ingroup memorymgmt - * @author Phil Schatzmann - * @copyright GPLv3 - */ -class AllocatorExt : public Allocator { - void* do_allocate(size_t size) { - void* result = nullptr; - if (size == 0) size = 1; -#if (defined(RP2040) || defined(ESP32)) && defined(ARDUINO) - result = ps_malloc(size); -#endif - if (result == nullptr) result = malloc(size); - if (result == nullptr) { - LOGE("allocateation failed for %zu bytes", size); - stop(); - } - // initialize object - memset(result, 0, size); - return result; - } -}; - - -#if (defined(ESP32)) && defined(ARDUINO) - -/** - * @brief ESP32 Memory allocateator which forces the allocation with the defined - * attributes (default MALLOC_CAP_8BIT | MALLOC_CAP_INTERNAL) - * @ingroup memorymgmt - * @author Phil Schatzmann - * @copyright GPLv3 - */ - - class AllocatorESP32 : public Allocator { - AllocatorESP32(int caps = MALLOC_CAP_8BIT | MALLOC_CAP_INTERNAL) { - this->caps = caps; - } - void* do_allocate(size_t size) { - void* result = nullptr; - if (size == 0) size = 1; - result = heap_caps_calloc(1, size, caps); - if (result == nullptr) { - LOGE("alloc failed for %zu bytes", size); - stop(); - } - return result; - } - - protected: - int caps = 0; -}; - -#endif - -#if (defined(RP2040) || defined(ESP32)) && defined(ARDUINO) - -/** - * @brief Memory allocateator which uses ps_malloc to allocate the memory in - * PSRAM on the ESP32 - * @ingroup memorymgmt - * @author Phil Schatzmann - * @copyright GPLv3 - **/ - -class AllocatorPSRAM : public Allocator { - void* do_allocate(size_t size) { - if (size == 0) size = 1; - void* result = nullptr; - result = ps_calloc(1, size); - if (result == nullptr) { - LOGE("allocateation failed for %zu bytes", size); - stop(); - } - return result; - } -}; - -#endif - -static AllocatorExt DefaultAllocator; - -} // namespace audio_tools \ No newline at end of file diff --git a/src/AudioTools/CoreAudio/AudioBasic/Collections/PriorityQueue.h b/src/AudioTools/CoreAudio/AudioBasic/Collections/PriorityQueue.h deleted file mode 100644 index 04388257a8..0000000000 --- a/src/AudioTools/CoreAudio/AudioBasic/Collections/PriorityQueue.h +++ /dev/null @@ -1,52 +0,0 @@ -#pragma once -#include "AudioTools/CoreAudio/AudioBasic/Collections/List.h" - -namespace audio_tools { - -/** - * @brief Priority Queue which is based on a List. The order of the elements is - * defined by a compare function which is provided in the constructor. If the - * function returns > 0 if v1 > v2, the data will be provided in increasing - * order. - * @ingroup collections - * @author Phil Schatzmann - * @copyright GPLv3 - * @tparam T - */ -template -class PriorityQueue { - public: - PriorityQueue(int (*compare)(T &v1, T &v2)) { compare_cb = compare; }; - - bool enqueue(T &&data) { - for (auto it = l.begin(); it != l.end(); ++it) { - if (compare_cb(*it, data) > 0) { - l.insert(it, data); - return true; - } - } - return l.push_back(data); - } - - bool peek(T &data) { - if (l.end()->prior == nullptr) return false; - data = *(l.end()->prior); - return true; - } - - bool dequeue(T &data) { return l.pop_front(data); } - - size_t size() { return l.size(); } - - bool clear() { return l.clear(); } - - bool empty() { return l.empty(); } - - void setAllocator(Allocator &allocator) { l.setAllocator(allocator); } - - protected: - List l; - int (*compare_cb)(T &v1, T &v2); -}; - -} // namespace audio_tools \ No newline at end of file diff --git a/src/AudioTools/CoreAudio/AudioBasic/Collections/QueueFromVector.h b/src/AudioTools/CoreAudio/AudioBasic/Collections/QueueFromVector.h deleted file mode 100644 index e941f612e8..0000000000 --- a/src/AudioTools/CoreAudio/AudioBasic/Collections/QueueFromVector.h +++ /dev/null @@ -1,96 +0,0 @@ -#pragma once -#include "AudioTools/CoreAudio/AudioBasic/Collections/List.h" - -namespace audio_tools { - -/** - * @brief FIFO Queue which is based on a Vector - * @ingroup collections - * @author Phil Schatzmann - * @copyright GPLv3 - * @tparam T - */ -template -class QueueFromVector { - public: - QueueFromVector(size_t size, T empty) { - null_value = empty; - resize(size); - }; - - bool enqueue(T& data){ - if (is_full()) - return false; - vector[_end_pos++] = data; - return true; - } - - bool peek(T& data){ - if (_end_pos <= 0 ) { - data = null_value; - _end_pos = 0; - return false; - } - data = vector[0]; - return true; - } - - bool dequeue(T& data){ - if (_end_pos <= 0 ) { - data = null_value; - _end_pos = 0; - return false; - } - // provide data at haed - data = vector[0]; - // shift all data to the left by 1 position - memmove(&vector[0], &vector[1], (_end_pos-1)*sizeof(T)); - vector[--_end_pos] = null_value; - return true; - } - - size_t size() { - return _end_pos < 0 ? 0 : _end_pos; - } - - bool resize(size_t size) { - if (!vector.resize(size)){ - return false; - } - return clear(); - } - - bool clear() { - for (int j=0;j= vector.size(); - } - - size_t capacity() { return vector.capacity(); } - - void setAllocator(Allocator &allocator){ - vector.setAllocator(allocator); - } - - Vector& toVector() { - return vector; - } - - protected: - Vector vector; - int32_t _end_pos = 0; - int empty_pos = 0; - T null_value; -}; - -} \ No newline at end of file diff --git a/src/AudioTools/CoreAudio/AudioBasic/Collections/Slice.h b/src/AudioTools/CoreAudio/AudioBasic/Collections/Slice.h deleted file mode 100644 index 64cf38b3d9..0000000000 --- a/src/AudioTools/CoreAudio/AudioBasic/Collections/Slice.h +++ /dev/null @@ -1,59 +0,0 @@ -#pragma once -#include -#include - -namespace audio_tools { - -/** - * @brief Helps to split up a big memory array into smaller slices. There are no - * additinal heap allocations! - * Example: if we have an array with 9 entries (1,2,3,4,5,6,7,8,9): slices(5) gives 2. - * slice(5,0) returns size 5 with 1,2,3,4,5 and slice(5,1) returns size 4 with 6,7,8,9! - * @ingroup collections - * @author Phil Schatzmann - */ -template -class Slice { - public: - Slice(T* start, size_t len) { - this->start = start; - this->len = len; - } - - /// Returns the data - T* data() { return start; } - - /// Returns the (result) data size is number of entries - size_t size() { return len; } - - /// Returns the number of slices of the indicated size - size_t slices(int sliceSize){ - int result = len / sliceSize; - return len % sliceSize == 0 ? result : result+1; - } - - /// Returns true if we contain any valid data - operator bool() { return len > 0 && start!=nullptr; } - - /// Returns the slice at the indicated index for the indicated slize size; - Slice slice(int sliceSize, int idx) { - int start_pos = idx * sliceSize; - int end_pos = start_pos + sliceSize; - if (end_pos > len) { - end_pos = len; - } - - if (start_pos < len) { - assert(start!=nullptr); - return Slice(start + start_pos, end_pos - start_pos); - } else { - return Slice(nullptr, 0); - } - } - - protected: - T* start = nullptr; - size_t len = 0; -}; - -} // namespace audio_tools \ No newline at end of file diff --git a/src/AudioTools/CoreAudio/AudioBasic/Collections/Vector.h b/src/AudioTools/CoreAudio/AudioBasic/Collections/Vector.h deleted file mode 100644 index b84b7efd08..0000000000 --- a/src/AudioTools/CoreAudio/AudioBasic/Collections/Vector.h +++ /dev/null @@ -1,409 +0,0 @@ -#pragma once -#ifdef USE_INITIALIZER_LIST -#include "InitializerList.h" -#endif -#include - -#include "Allocator.h" - -namespace audio_tools { - -/** - * @brief Vector implementation which provides the most important methods as - *defined by std::vector. This class it is quite handy to have and most of the - *times quite better then dealing with raw c arrays. - * @ingroup collections - * @author Phil Schatzmann - * @copyright GPLv3 - **/ - -template -class Vector { - public: - /** - * @brief Iterator for the Vector class - * - * by Phil Schatzmann - **/ - - class iterator { - protected: - T *ptr; - size_t pos_; - - public: - iterator() {} - iterator(T *parPtr, size_t pos) { - this->ptr = parPtr; - this->pos_ = pos; - } - // copy constructor - iterator(const iterator ©From) { - ptr = copyFrom.ptr; - pos_ = copyFrom.pos_; - } - iterator operator++(int n) { - ptr++; - pos_++; - return *this; - } - iterator operator++() { - ptr++; - pos_++; - return *this; - } - iterator operator--(int n) { - ptr--; - pos_--; - return *this; - } - iterator operator--() { - ptr--; - pos_--; - return *this; - } - iterator operator+(int offset) { - pos_ += offset; - return iterator(ptr + offset, offset); - } - bool operator==(iterator it) { return ptr == it.getPtr(); } - bool operator<(iterator it) { return ptr < it.getPtr(); } - bool operator<=(iterator it) { return ptr <= it.getPtr(); } - bool operator>(iterator it) { return ptr > it.getPtr(); } - bool operator>=(iterator it) { return ptr >= it.getPtr(); } - bool operator!=(iterator it) { return ptr != it.getPtr(); } - T &operator*() { return *ptr; } - T *operator->() { return ptr; } - T *getPtr() { return ptr; } - size_t pos() { return pos_; } - size_t operator-(iterator it) { return (ptr - it.getPtr()); } - }; - -#ifdef USE_INITIALIZER_LIST - - /// support for initializer_list - Vector(std::initializer_list iniList) { - resize(iniList.size()); - int pos = 0; - for (auto &obj : iniList) { - p_data[pos++] = obj; - } - } - -#endif - - /// Default constructor: size 0 with DefaultAllocator: The len defines the capacity - Vector(size_t len = 0, Allocator &allocator = DefaultAllocator) { - setAllocator(allocator); - resize_internal(len, false, false); - } - - - /// Constructor with only allocator - Vector(Allocator &allocator) { - setAllocator(allocator); - } - - /// Allocate size and initialize array - Vector(int size, T value, Allocator &allocator = DefaultAllocator) { - setAllocator(allocator); - resize(size); - for (int j = 0; j < size; j++) { - p_data[j] = value; - } - } - - /// Move constructor - Vector(Vector &&moveFrom) { - swap(moveFrom); - moveFrom.clear(); - }; - - /// Move operator - Vector &operator=(Vector &&moveFrom) { - swap(moveFrom); - moveFrom.clear(); - return *this; - } - - /// copy constructor - Vector(Vector ©From) { - this->p_allocator = copyFrom.p_allocator; - resize_internal(copyFrom.size(), false); - for (int j = 0; j < copyFrom.size(); j++) { - p_data[j] = copyFrom[j]; - } - this->len = copyFrom.size(); - } - - /// convert from c array - template - Vector(TT (&a)[N]) { - resize_internal(N, false); - for (int j = 0; j < N; j++) { - p_data[j] = a[j]; - } - this->len = N; - } - - /// copy operator - Vector &operator=(Vector ©From) { - resize_internal(copyFrom.size(), false); - for (int j = 0; j < copyFrom.size(); j++) { - p_data[j] = copyFrom[j]; - } - this->len = copyFrom.size(); - return *this; - } - - /// legacy constructor with pointer range - Vector(T *from, T *to, Allocator &allocator = DefaultAllocator) { - this->p_allocator = &allocator; - this->len = to - from; - resize_internal(this->len, false); - for (size_t j = 0; j < this->len; j++) { - p_data[j] = from[j]; - } - } - - /// Destructor - virtual ~Vector() { reset(); } - - void setAllocator(Allocator &allocator) { - p_allocator = &allocator; - } - - void clear() { len = 0; } - - int size() { return len; } - - bool empty() { return size() == 0; } - - void push_back(T &&value) { - resize_internal(len + 1, true); - p_data[len] = value; - len++; - } - - void push_back(T &value) { - resize_internal(len + 1, true); - p_data[len] = value; - len++; - } - - void push_front(T &value) { - resize_internal(len + 1, true); - // memmove(p_data,p_data+1,len*sizeof(T)); - for (int j = len; j >= 0; j--) { - p_data[j + 1] = p_data[j]; - } - p_data[0] = value; - len++; - } - - void push_front(T &&value) { - resize_internal(len + 1, true); - // memmove(p_data,p_data+1,len*sizeof(T)); - for (int j = len; j >= 0; j--) { - p_data[j + 1] = p_data[j]; - } - p_data[0] = value; - len++; - } - - void pop_back() { - if (len > 0) { - len--; - } - } - - void pop_front() { erase(0); } - - void assign(iterator v1, iterator v2) { - size_t newLen = v2 - v1; - resize_internal(newLen, false); - this->len = newLen; - int pos = 0; - for (auto ptr = v1; ptr != v2; ptr++) { - p_data[pos++] = *ptr; - } - } - - void assign(size_t number, T value) { - resize_internal(number, false); - this->len = number; - for (int j = 0; j < number; j++) { - p_data[j] = value; - } - } - - void swap(Vector &in) { - // save data - T *dataCpy = p_data; - int bufferLenCpy = bufferLen; - int lenCpy = len; - // swap this - p_data = in.p_data; - len = in.len; - bufferLen = in.bufferLen; - // swp in - in.p_data = dataCpy; - in.len = lenCpy; - in.bufferLen = bufferLenCpy; - } - - T &operator[](int index) { - assert(p_data != nullptr); - assert(index < len); - return p_data[index]; - } - - T &operator[](const int index) const { - assert(index < len); - return p_data[index]; - } - - bool resize(int newSize, T value) { - if (resize(newSize)) { - for (int j = 0; j < newSize; j++) { - p_data[j] = value; - } - return true; - } - return false; - } - - void shrink_to_fit() { resize_internal(this->len, true, true); } - - int capacity() { return this->bufferLen; } - - bool resize(int newSize) { - int oldSize = this->len; - resize_internal(newSize, true); - this->len = newSize; - return this->len != oldSize; - } - - iterator begin() { return iterator(p_data, 0); } - - T &back() { return *iterator(p_data + (len - 1), len - 1); } - - iterator end() { return iterator(p_data + len, len); } - - // removes a single element - void erase(iterator it) { return erase(it.pos()); } - - // removes a single element - void erase(int pos) { - if (pos < len) { - int lenToEnd = len - pos - 1; - // call destructor on data to be erased - p_data[pos].~T(); - // shift values by 1 position - memmove((void *)&p_data[pos], (void *)(&p_data[pos + 1]), - lenToEnd * sizeof(T)); - - // make sure that we have a valid object at the end -#if defined(NO_INPLACE_INIT_SUPPORT) - p_data[len - 1] = T(); -#else - new (&(p_data[len - 1])) T(); -#endif - len--; - } - } - - T *data() { return p_data; } - - operator bool() const { return p_data != nullptr; } - - int indexOf(T obj) { - for (int j = 0; j < size(); j++) { - if (p_data[j] == obj) return j; - } - return -1; - } - - bool contains(T obj){ - return indexOf(obj) >= 0; - } - - - void swap(T &other) { - // save values - int temp_blen = bufferLen; - int temp_len = len; - T *temp_data = p_data; - // swap from other - bufferLen = other.bufferLen; - len = other.len; - p_data = other.p_data; - // set other - other.bufferLen = temp_blen; - other.len = temp_len; - other.p_data = temp_data; - } - - void reset() { - clear(); - shrink_to_fit(); - deleteArray(p_data, size()); // delete [] this->p_data; - p_data = nullptr; - } - - protected: - int bufferLen = 0; - int len = 0; - T *p_data = nullptr; - Allocator *p_allocator = &DefaultAllocator; - - void resize_internal(int newSize, bool copy, bool shrink = false) { - if (newSize <= 0) return; - if (newSize > bufferLen || this->p_data == nullptr || shrink) { - T *oldData = p_data; - int oldBufferLen = this->bufferLen; - p_data = newArray(newSize); // new T[newSize+1]; - assert(p_data != nullptr); - this->bufferLen = newSize; - if (oldData != nullptr) { - if (copy && this->len > 0) { - // save existing data - memmove((void*)p_data, (void*)oldData, len * sizeof(T)); - // clear to prevent double release - memset((void*)oldData, 0, len * sizeof(T)); - } - if (shrink) { - cleanup(oldData, newSize, oldBufferLen); - } - deleteArray(oldData, oldBufferLen); // delete [] oldData; - } - } - } - - T *newArray(int newSize) { - T *data; -#if USE_ALLOCATOR - data = p_allocator->createArray(newSize); // new T[newSize+1]; -#else - data = new T[newSize]; -#endif - return data; - } - - void deleteArray(T *oldData, int oldBufferLen) { -#if USE_ALLOCATOR - p_allocator->removeArray(oldData, oldBufferLen); // delete [] oldData; -#else - delete[] oldData; -#endif - } - - - void cleanup(T *data, int from, int to) { - for (int j = from; j < to; j++) { - data[j].~T(); - } - } -}; - -} // namespace audio_tools \ No newline at end of file diff --git a/src/AudioTools/CoreAudio/AudioBasic/Float16.h b/src/AudioTools/CoreAudio/AudioBasic/Float16.h deleted file mode 100644 index a094492243..0000000000 --- a/src/AudioTools/CoreAudio/AudioBasic/Float16.h +++ /dev/null @@ -1,115 +0,0 @@ -#pragma once - -namespace audio_tools { - -/** - * @brief Stores float values with 2 bytes - * @author Phil Schatzmann - * @copyright GPLv3 - */ - -class float16 { - public: - float16() = default; - float16(float in) { this->value = float16::float_to_half(in); } - float16(const float16 &value16) { this->value = value16.value; } - inline operator float() { return half_to_float(value); } - explicit inline operator double() { - return (double)float16::half_to_float(value); - } - explicit inline operator int() { return (int)float16::half_to_float(value); } - explicit inline operator int() const { - return (int)float16::half_to_float(value); - } - inline bool operator<(float16 other) const { return float() < (float)other; } - inline bool operator<=(float16 other) const { - return float() <= (float)other; - } - inline bool operator>(float16 other) const { return float() > (float)other; } - inline bool operator>=(float16 other) const { - return float() >= (float)other; - } - inline bool operator==(float16 other) const { - return float() == (float)other; - } - inline bool operator!=(float16 other) const { - return float() != (float)other; - } - - protected: - uint16_t value = 0; - - /// see - /// https://stackoverflow.com/questions/1659440/32-bit-to-16-bit-floating-point-conversion - static uint32_t as_uint(const float x) { return *(uint *)&x; } - /// see - /// https://stackoverflow.com/questions/1659440/32-bit-to-16-bit-floating-point-conversion - static float as_float(const uint32_t x) { return *(float *)&x; } - - /// see - /// https://stackoverflow.com/questions/1659440/32-bit-to-16-bit-floating-point-conversion - static float half_to_float( - const uint16_t x) { // IEEE-754 16-bit floating-point format (without - // infinity): 1-5-10, exp-15, +-131008.0, - // +-6.1035156E-5, +-5.9604645E-8, 3.311 digits - const uint32_t e = (x & 0x7C00) >> 10; // exponent - const uint32_t m = (x & 0x03FF) << 13; // mantissa - const uint32_t v = - as_uint((float)m) >> - 23; // evil log2 bit hack to count leading zeros in denormalized format - return as_float((x & 0x8000) << 16 | (e != 0) * ((e + 112) << 23 | m) | - ((e == 0) & (m != 0)) * - ((v - 37) << 23 | - ((m << (150 - v)) & - 0x007FE000))); // sign : normalized : denormalized - } - /// see - /// https://stackoverflow.com/questions/1659440/32-bit-to-16-bit-floating-point-conversion - static uint16_t float_to_half( - const float x) { // IEEE-754 16-bit floating-point format (without - // infinity): 1-5-10, exp-15, +-131008.0, - // +-6.1035156E-5, +-5.9604645E-8, 3.311 digits - const uint32_t b = - as_uint(x) + 0x00001000; // round-to-nearest-even: add last bit after - // truncated mantissa - const uint32_t e = (b & 0x7F800000) >> 23; // exponent - const uint32_t m = b & 0x007FFFFF; // mantissa; in line below: 0x007FF000 = - // 0x00800000-0x00001000 = decimal - // indicator flag - initial rounding - return (b & 0x80000000) >> 16 | - (e > 112) * ((((e - 112) << 10) & 0x7C00) | m >> 13) | - ((e < 113) & (e > 101)) * - ((((0x007FF000 + m) >> (125 - e)) + 1) >> 1) | - (e > 143) * 0x7FFF; // sign : normalized : denormalized : saturate - } -}; - -inline float operator+(float16 one, float16 two) { - return (float)one + (float)two; -} -inline float operator-(float16 one, float16 two) { - return (float)one - (float)two; -} -inline float operator*(float16 one, float16 two) { - return (float)one * (float)two; -} -inline float operator/(float16 one, float16 two) { - return (float)one / (float)two; -} -inline float operator+(float16 one, float two) { return (float)one + two; } -inline float operator-(float16 one, float two) { return (float)one - two; } -inline float operator*(float16 one, float two) { return (float)one * two; } -inline float operator/(float16 one, float two) { return (float)one / two; } -inline float operator+(float one, float16 two) { return two + float(one); } -inline float operator-(float one, float16 two) { return two - float(one); } -inline float operator*(float one, float16 two) { return two * float(one); } -inline float operator/(float one, float16 two) { return two / float(one); } - -} // namespace audio_tools - -namespace std { - -inline float floor(float16 arg) { return std::floor((float)arg); } -inline float fabs(float16 arg) { return std::fabs((float)arg); } - -} // namespace std diff --git a/src/AudioTools/CoreAudio/AudioBasic/FloatAudio.h b/src/AudioTools/CoreAudio/AudioBasic/FloatAudio.h deleted file mode 100644 index b22a36c447..0000000000 --- a/src/AudioTools/CoreAudio/AudioBasic/FloatAudio.h +++ /dev/null @@ -1,56 +0,0 @@ -#pragma once -#include "AudioToolsConfig.h" - -namespace audio_tools { - -/*** - * A simple float number (in the range of -1.0 to 1.0) that supports the conversion to - * it's corresponding scaled int values. - */ - -class FloatAudio { - public: - FloatAudio() = default; - FloatAudio(float in) { this->value = in; } - - inline operator float() { return value; } - - explicit inline operator int8_t() { return value * 127; } - - explicit inline operator int16_t() { return value * 32767; } - - explicit inline operator int24_3bytes_t() { - return value * 8388607; - } - - explicit inline operator int24_4bytes_t() { - return value * 8388607; - } - - explicit inline operator int32_t() { return value * 2147483647.0f; } - - protected: - float value = 0.0f; -}; - -} // namespace audio_tools - -#ifdef USE_TYPETRAITS - -namespace std { -template <> -class numeric_limits { - public: - static audio_tools::FloatAudio lowest() { - return audio_tools::FloatAudio(-1.0f); - }; - static audio_tools::FloatAudio min() { - return audio_tools::FloatAudio(-1.0f); - }; - static audio_tools::FloatAudio max() { - return audio_tools::FloatAudio(1.0f); - }; -}; -} // namespace std - -#endif diff --git a/src/AudioTools/CoreAudio/AudioBasic/MovingAverage.h b/src/AudioTools/CoreAudio/AudioBasic/MovingAverage.h deleted file mode 100644 index ee3c54af19..0000000000 --- a/src/AudioTools/CoreAudio/AudioBasic/MovingAverage.h +++ /dev/null @@ -1,45 +0,0 @@ - -#include "Collections/List.h" - -namespace audio_tools { - -/** - * @brief Caclulates the moving average of a number of values - * @ingroup basic - * @author Phil Schatzmann - * @copyright GPLv3 - */ - -template -class MovingAverage { - public: - MovingAverage(size_t size) { - setSize(size); - } - - void add(N value) { - if (this->values.size() == this->size) { - this->values.pop_front(); - } - this->values.push_back(value); - } - - float average() { - float sum = 0; - for (int i = 0; i < this->values.size(); i++) { - sum += this->values[i]; - } - return sum / this->values.size(); - } - - /// Defines the number of values - void setSize(size_t size) { - this->size = size; - } - - protected: - List values; - size_t size = 0;; -}; - -} // namespace audio_tools \ No newline at end of file diff --git a/src/AudioTools/CoreAudio/AudioBasic/Net.h b/src/AudioTools/CoreAudio/AudioBasic/Net.h deleted file mode 100644 index f633141574..0000000000 --- a/src/AudioTools/CoreAudio/AudioBasic/Net.h +++ /dev/null @@ -1,27 +0,0 @@ -#pragma once - - -#ifndef htons -#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ -# define htonl(x) (((x) << 24 & 0xFF000000UL) | ((x) << 8 & 0x00FF0000UL) | ((x) >> 8 & 0x0000FF00UL) | ((x) >> 24 & 0x000000FFUL)) -# define htons(x) (((uint16_t)(x)&0xff00) >> 8) | (((uint16_t)(x)&0X00FF) << 8) -# define ntohl(x) htonl(x) -# define ntohs(x) htons(x) -#elif __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__ -# define htonl(x) x -# define htons(x) x -# define ntohl(x) x -# define ntohs(x) x -#else -#error Could not determine byte order -#endif -#endif - -/// support for 64 bytes -#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ -# define htonll(x) ((1==htonl(1)) ? (x) : ((uint64_t)htonl((x) & 0xFFFFFFFF) << 32) | htonl((x) >> 32)) -# define ntohll(x) ((1==ntohl(1)) ? (x) : ((uint64_t)ntohl((x) & 0xFFFFFFFF) << 32) | ntohl((x) >> 32)) -#elif __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__ -# define htonll(x) x -# define ntohll(x) x -#endif diff --git a/src/AudioTools/CoreAudio/AudioBasic/Str.h b/src/AudioTools/CoreAudio/AudioBasic/Str.h deleted file mode 100644 index 2b1d070c72..0000000000 --- a/src/AudioTools/CoreAudio/AudioBasic/Str.h +++ /dev/null @@ -1,244 +0,0 @@ -#pragma once - -#include "AudioTools/CoreAudio/AudioBasic/StrView.h" -#include "AudioTools/CoreAudio/AudioBasic/Collections/Vector.h" - -namespace audio_tools { - -/** - * @brief Str which keeps the data on the heap. We grow the allocated - * memory only if the copy source is not fitting. - * - * While it should be avoided to use a lot of heap allocatioins in - * embedded devices it is sometimes more convinent to allocate a string - * once on the heap and have the insurance that it might grow - * if we need to process an unexpected size. - * - * We also need to use this if we want to manage a vecor of strings. - * - * @ingroup string - * @author Phil Schatzmann - * @copyright GPLv3 - */ - -class Str : public StrView { - public: - Str() = default; - - Str(int initialAllocatedLength) : StrView() { - maxlen = initialAllocatedLength; - is_const = false; - grow(maxlen); - } - - Str(const char *str) : StrView() { - if (str != nullptr) { - len = strlen(str); - maxlen = len; - grow(maxlen); - if (chars != nullptr) { - strcpy(chars, str); - } - } - } - - /// Convert StrView to Str - Str(StrView &source) : StrView() { set(source); } - - /// Copy constructor - Str(Str &source) : StrView() { set(source); } - - /// Move constructor - Str(Str &&obj) { move(obj); } - - /// Destructor - ~Str() { - len = maxlen = 0; - chars = nullptr; - } - - /// Move assignment - Str &operator=(Str &&obj) { - return move(obj); - } - - /// Copy assingment - Str &operator=(Str &obj) { - //assert(&obj!=nullptr); - set(obj.c_str()); - return *this; - }; - - bool isOnHeap() override { return true; } - - bool isConst() override { return false; } - - void operator=(const char *str) override { set(str); } - - void operator=(char *str) override { set(str); } - - void operator=(int v) override { set(v); } - - void operator=(double v) override { set(v); } - - size_t capacity() { return maxlen; } - - void setCapacity(size_t newLen) { grow(newLen); } - - // make sure that the max size is allocated - void allocate(int len = -1) { - int new_size = len < 0 ? maxlen : len; - grow(new_size); - this->len = new_size; - } - - /// assigns a memory buffer - void copyFrom(const char *source, int len, int maxlen = 0) { - this->maxlen = maxlen == 0 ? len : maxlen; - grow(this->maxlen); - if (this->chars != nullptr) { - this->len = len; - this->is_const = false; - memmove(this->chars, source, len); - this->chars[len] = 0; - } - } - - /// Fills the string with len chars - void setChars(char c, int len) { - grow(this->maxlen); - if (this->chars != nullptr) { - for (int j = 0; j < len; j++) { - this->chars[j] = c; - } - this->len = len; - this->is_const = false; - this->chars[len] = 0; - } - } - - /// url encode the string - void urlEncode() { - char temp[4]; - int new_size = 0; - // Calculate the new size - for (size_t i = 0; i < len; i++) { - urlEncodeChar(chars[i], temp, 4); - new_size += strlen(temp); - } - // build new string - char result[new_size + 1]; - memset(result,0, new_size+1); - for (size_t i = 0; i < len; i++) { - urlEncodeChar(chars[i], temp, 4); - strcat(result, temp); - } - // save result - grow(new_size); - strcpy(chars, result); - this->len = strlen(temp); - } - - /// decodes a url encoded string - void urlDecode() { - char szTemp[2]; - size_t i = 0; - size_t result_idx = 0; - while (i < len) { - if (chars[i] == '%') { - szTemp[0] = chars[i + 1]; - szTemp[1] = chars[i + 2]; - chars[result_idx] = strToBin(szTemp); - i = i + 3; - } else if (chars[i] == '+') { - chars[result_idx] = ' '; - i++; - } else { - chars[result_idx] += chars[i]; - i++; - } - result_idx++; - } - chars[result_idx] = 0; - this->len = result_idx; - } - - void clear() override { - len = 0; - maxlen = 0; - vector.resize(0); - chars = nullptr; - } - - void swap(Str &other){ - int tmp_len = len; - int tmp_maxlen = maxlen; - len = other.len; - maxlen = other.maxlen; - vector.swap(other.vector); - chars = vector.data(); - other.chars = other.vector.data(); - } - - - protected: - Vector vector; - - Str& move(Str &other) { - swap(other); - other.clear(); - return *this; - } - - bool grow(int newMaxLen) override { - bool grown = false; - assert(newMaxLen < 1024 * 10); - if (newMaxLen < 0) return false; - - if (chars == nullptr || newMaxLen > maxlen) { - LOGD("grow(%d)", newMaxLen); - - grown = true; - // we use at minimum the defined maxlen - int newSize = newMaxLen > maxlen ? newMaxLen : maxlen; - vector.resize(newSize + 1); - chars = &vector[0]; - maxlen = newSize; - } - return grown; - } - - void urlEncodeChar(char c, char *result, int maxLen) { - if (isalnum(c)) { - snprintf(result, maxLen, "%c", c); - } else if (isspace(c)) { - snprintf(result, maxLen, "%s", "+"); - } else { - snprintf(result, maxLen, "%%%X%X", c >> 4, c % 16); - } - } - - char charToInt(char ch) { - if (ch >= '0' && ch <= '9') { - return (char)(ch - '0'); - } - if (ch >= 'a' && ch <= 'f') { - return (char)(ch - 'a' + 10); - } - if (ch >= 'A' && ch <= 'F') { - return (char)(ch - 'A' + 10); - } - return -1; - } - - char strToBin(char *pString) { - char szBuffer[2]; - char ch; - szBuffer[0] = charToInt(pString[0]); // make the B to 11 -- 00001011 - szBuffer[1] = charToInt(pString[1]); // make the 0 to 0 -- 00000000 - ch = (szBuffer[0] << 4) | szBuffer[1]; // to change the BO to 10110000 - return ch; - } -}; - -} // namespace audio_tools \ No newline at end of file diff --git a/src/AudioTools/CoreAudio/AudioBasic/StrView.h b/src/AudioTools/CoreAudio/AudioBasic/StrView.h deleted file mode 100644 index 05f0dbb20b..0000000000 --- a/src/AudioTools/CoreAudio/AudioBasic/StrView.h +++ /dev/null @@ -1,838 +0,0 @@ -#pragma once - -#include -#include -#include -#include "AudioTools/CoreAudio/AudioLogger.h" - -/** - * @defgroup string Strings - * @ingroup tools - * @brief Strings - * This framework is avoiding the use of Arduino Strings, so that we can use it - * easily also on other platforms! - */ - -namespace audio_tools { - -/** - * @brief A simple wrapper to provide string functions on existing allocated char*. - * If the underlying char* is a const we do not allow any updates; The ownership - * of the char* must be managed externally! - * - * @ingroup string - * @author Phil Schatzmann - * @copyright GPLv3 - */ - -class StrView { - public: - StrView() = default; - - /// Creates a Str for string constant - StrView(const char* chars) { - if (chars != nullptr) { - int len = strlen(chars); - set((char*)chars, len, len, true); - } else { - this->is_const = true; - clear(); - } - } - - /// Creates a Str with the indicated buffer - StrView(char chars[], int maxlen, int len = 0) { set(chars, maxlen, len, false); } - - /// assigs a value - virtual void set(const char* alt) { - if (alt == nullptr) { - this->len = 0; - } else { - int new_len = strlen(alt); - grow(new_len); - this->len = new_len; - if (this->isConst()) { - /// if the Str is a const we replace the pointer - this->maxlen = this->len; - this->chars = (char*)alt; - } else { - /// if the Str is an external buffer we need to copy - strncpy(this->chars, alt, this->maxlen); - this->chars[len] = 0; - } - } - } - /// assigs from another Str value - virtual void set(const StrView& alt) { - grow(alt.len); - this->len = alt.len; - - if (this->isConst()) { - /// if the Str is a const we replace the pointer - this->chars = alt.chars; - } else { - /// if the Str is an external buffer we need to copy - strncpy(this->chars, alt.chars, this->maxlen); - this->chars[len] = 0; - } - } - - virtual void set(const char c) { - clear(); - add(c); - } - - virtual void set(int value) { - clear(); - add(value); - } - - virtual void set(double value, int precision = 2, int withd = 0) { - clear(); - add(value); - } - - virtual void swap(StrView& str) { - char* cpy_chars = chars; - ; - bool cpy_is_const = is_const; - int cpy_len = len; - int cpy_maxlen = maxlen; - - chars = str.chars; - is_const = str.is_const; - len = str.len; - maxlen = str.maxlen; - - str.chars = cpy_chars; - str.is_const = cpy_is_const; - str.len = cpy_len; - str.maxlen = cpy_maxlen; - } - - /// assigns a memory buffer - virtual void set(char chars[], int maxlen, int len = 0, - bool isConst = false) { - this->chars = chars; - this->maxlen = maxlen; - this->len = len; - this->is_const = isConst; - if (len == 0 && !isConst) { - this->chars[0] = 0; - } - } - - /// adds a int value - virtual void add(int value) { - if (!this->isConst()) { - grow(this->length() + 11); - snprintf(this->chars + len, 10, "%d", value); - len = strlen(chars); - } - } - - /// adds a double value - virtual void add(double value, int precision = 2, int withd = 0) { - if (!this->isConst()) { - grow(this->length() + 20); - floatToString(this->chars + len, value, precision, withd); - len = strlen(chars); - } - } - - /// adds a string - virtual void add(const char* append) { - if (!isConst() && append != nullptr) { - int append_len = strlen(append); - grow(this->length() + append_len + 1); - int n = (len + append_len) < maxlen - 1 ? append_len : maxlen - len - 1; - strncat(chars, append, n); - chars[len + n] = 0; - len = strlen(chars); - } - } - - /// adds a character - virtual void add(const char c) { - if (!isConst() && len < maxlen - 1) { - grow(this->length() + 1); - chars[len] = c; - chars[++len] = 0; - } - } - - /// checks if the string equals indicated parameter string - virtual bool equals(const char* str) { - if (str == nullptr) return false; - return strcmp(this->chars, str) == 0; - } - - /// checks if the string starts with the indicated substring - virtual bool startsWith(const char* str) { - if (str == nullptr) return false; - int len = strlen(str); - return strncmp(this->chars, str, len) == 0; - } - - /// checks if the string ends with the indicated substring - virtual bool endsWith(const char* str) { - if (str == nullptr) return false; - int endlen = strlen(str); - return strncmp(this->chars + (len - endlen), str, endlen) == 0; - } - - /// checks if the string ends with the indicated substring - virtual bool endsWithIgnoreCase(const char* str) { - if (str == nullptr) return false; - int endlen = strlen(str); - return strncmp_i(this->chars + (len - endlen), str, endlen) == 0; - } - - /// file matching supporting * and ? - replacing regex which is not supported - /// in all environments - virtual bool matches(const char* pattern) { - /// returns 1 (true) if there is a match - /// returns 0 if the pattern is not whitin the line - int wildcard = 0; - const char* line = this->chars; - - const char* last_pattern_start = 0; - const char* last_line_start = 0; - do { - if (*pattern == *line) { - if (wildcard == 1) last_line_start = line + 1; - - line++; - pattern++; - wildcard = 0; - } else if (*pattern == '?') { - if (*(line) == '\0') /// the line is ended but char was expected - return 0; - if (wildcard == 1) last_line_start = line + 1; - line++; - pattern++; - wildcard = 0; - } else if (*pattern == '*') { - if (*(pattern + 1) == '\0') { - return 1; - } - - last_pattern_start = pattern; - // last_line_start = line + 1; - wildcard = 1; - - pattern++; - } else if (wildcard) { - if (*line == *pattern) { - wildcard = 0; - line++; - pattern++; - last_line_start = line + 1; - } else { - line++; - } - } else { - if ((*pattern) == '\0' && (*line) == '\0') /// end of mask - return 1; /// if the line also ends here then the pattern match - else { - if (last_pattern_start != 0) /// try to restart the mask on the rest - { - pattern = last_pattern_start; - line = last_line_start; - last_line_start = 0; - } else { - return false; - } - } - } - - } while (*line); - - if (*pattern == '\0') { - return true; - } else { - return false; - } - } - - /// provides the position of the the indicated character after the indicated - /// start position - virtual int indexOf(const char c, int start = 0) { - for (int j = start; j < len; j++) { - if (c == chars[j]) { - return j; - } - } - return -1; - } - - /// searches for the nth occurence of the indicated character - virtual int nIndexOf(const char c, int n){ - int result = -1; - for (int j=0; j < n; j++){ - result = indexOf(c, result + 1); - if (result < 0) break; - } - return result; - } - - /// checks if the string contains a substring - virtual bool contains(const char* str) { return indexOf(str) != -1; } - - /// provides the position of the the indicated substring after the indicated - /// start position - virtual int indexOf(const char* cont, int start = 0) { - if (chars == nullptr || cont == nullptr) return -1; - int contLen = strlen(cont); - for (int j = start; j < len; j++) { - char* pt = chars + j; - if (strncmp(pt, cont, contLen) == 0) { - return j; - } - } - return -1; - } - - /// provides the position of the last occurrence of the indicated substring - virtual int lastIndexOf(const char* cont) { - if (cont == nullptr) return -1; - int contLen = strlen(cont); - for (int j = (len - contLen); j >= 0; j--) { - if (strncmp(cont, chars + j, contLen) == 0) { - return j; - } - } - return -1; - } - - /// searches for the nth occurence of the indicated character - virtual int nIndexOf(const char* cont, int n){ - int result = -1; - for (int j=0; j < n; j++){ - result = indexOf(cont, result + 1); - if (result < 0) break; - } - return result; - } - - /// we can assign a const char* - virtual void operator=(const char* str) { set(str); } - - /// we can assign a char* - virtual void operator=(char* str) { set(str); } - - /// we can assign a char - virtual void operator=(char c) { set(c); } - - /// we can assign a double - virtual void operator=(double val) { set(val); } - - /// we can assign an int - virtual void operator=(int value) { set(value); } - - /// shift characters to the right -> we just move the pointer - virtual void operator<<(int n) { - if (isConst()) { - this->chars += n; - this->len -= n; - } else { - memmove(this->chars, this->chars + n, len + 1); - } - } - - virtual char operator[](int index) { return chars[index]; } - - /// adds a substring at the end of the string - virtual void operator+=(const char* str) { add(str); } - - /// adds a int at the end of the string - virtual void operator+=(int value) { add(value); } - - /// adds a double at the end of the string - virtual void operator+=(double value) { add(value); } - - /// adds a character - virtual void operator+=(const char value) { add(value); } - - /// checks if the indicated string is equal to the current string - virtual bool operator==(const StrView& alt) const { - if (this->len != alt.len) return false; - return strncmp(this->chars, alt.chars, this->len) == 0; - } - - /// checks if the indicated string is equal to the current string - virtual bool operator==(const char* alt) const { - return strncmp(this->chars, alt, this->len) == 0; - } - - /// checks if the indicated string is different from the current string - virtual bool operator!=(const StrView& alt) const { - return strncmp(this->chars, alt.chars, this->len) != 0; - } - - /// checks if the indicated string is different from the current string - virtual bool operator!=(const char* alt) const { - return strncmp(this->chars, alt, this->len) != 0; - } - - /// provides the string value as const char* - virtual const char* c_str() { return chars; } - - /// provides the current length (filled with characters) of the string - - /// excluding the terminating 0 - virtual int length() { return len; } - - /// checks if the string is empty - virtual bool isEmpty() { return len == 0; } - - /// provides the maximum length of the string - virtual int maxLength() { return maxlen; } - - /// Replaces the first instance of toReplace with replaced - virtual bool replace(const char* toReplace, const char* replaced) { - bool result = false; - if (toReplace == nullptr || replaced == nullptr) { - return result; - } - if (!isConst()) { - int pos = indexOf(toReplace); - int old_len = length(); - int insert_len = 0; - if (pos >= 0) { - int len_replaced = strlen(replaced); - int len_to_replace = strlen(toReplace); - insert_len = len_replaced - len_to_replace; - grow(this->length() + insert_len); - // save remainder and create gap - memmove(this->chars + pos + len_replaced, - this->chars + pos + len_to_replace, - old_len - pos - len_to_replace + 1); - // move new string into gap - memmove(this->chars + pos, replaced, len_replaced); - result = true; - len += insert_len; - } - } - return result; - } - - /// Replaces all instances of toReplace with replaced - virtual bool replaceAll(const char* toReplace, const char* replaced) { - if (indexOf(toReplace) == -1) { - return false; - } - while (replace(toReplace, replaced)); - return true; - } - - /// removes the indicated substring from the string - virtual void remove(const char* toRemove) { - if (!isConst() && chars != nullptr) { - int removeLen = strlen(toRemove); - int pos = indexOf(toRemove); - if (pos >= 0) { - memmove((void*)(chars + pos), (void*)(chars + pos + removeLen), - len - (pos + removeLen) + 1); - len -= removeLen; - } - } - } - - /// removes the indicated substring from the string - virtual void removeAll(const char* toRemove) { - if (!isConst() && chars != nullptr) { - int removeLen = strlen(toRemove); - while (true) { - int pos = indexOf(toRemove); - if (pos == -1) { - break; - } - memmove((void*)(chars + pos), (void*)(chars + pos + removeLen), - len - (pos + removeLen) + 1); - len -= removeLen; - } - } - } - - /// limits the length of the string (by adding a delimiting 0) - virtual void setLength(int len, bool addZero = true) { - if (!isConst() && addZero) { - this->savedChar = chars[len]; - this->savedLen = len; - this->len = len; - chars[len] = 0; - } - } - - /// undo the last setLength call - virtual void setLengthUndo() { - if (savedLen >= 0) { - chars[len] = savedChar; - this->len = savedLen; - savedLen = -1; - } - } - - /// copies a substring into the current string - virtual void substring(StrView& from, int start, int end) { - if (end > start) { - int len = end - start; - grow(len); - if (this->chars != nullptr) { - len = len < this->maxlen ? len : this->maxlen; - strncpy(this->chars, from.chars + start, len); - this->len = len; - this->chars[len] = 0; - } - } - } - - /// copies a substring into the current string - virtual void substring(const char* from, int start, int end) { - if (end > start) { - int len = end - start; - grow(len); - if (this->chars != nullptr) { - strncpy(this->chars, from + start, len); - this->chars[len] = 0; - this->len = len; - } - } - } - - /// remove leading and traling spaces - virtual void trim() { - if (chars == nullptr) return; - rtrim(); - ltrim(); - } - - /// count number of indicated characters as position - virtual int count(char c, int startPos) { - for (int j = startPos; j < len; j++) { - if (chars[j] != c) { - return j; - } - } - return 0; - } - - /// remove leading spaces - virtual void ltrim() { - if (chars == nullptr) return; - int n = count(' ', 0); - if (n > 0) *this << n; - } - - /// remove trailing spaces - virtual void rtrim() { - if (chars == nullptr) return; - if (!isConst()) { - while (isspace(chars[len])) { - len--; - chars[len] = 0; - } - } - } - - /// clears the string by setting the terminating 0 at the beginning - virtual void clear() { - if (chars != nullptr && !isConst()) { - chars[0] = 0; - } - len = 0; - } - - /// checks if the string is on the heap - virtual bool isOnHeap() { return false; } - - /// checks if the string is a constant that must not be changed - virtual bool isConst() { return is_const; } - - /// inserts a substring into the string - virtual void insert(int pos, const char* str) { - if (!isConst()) { - int insert_len = strlen(str); - grow(this->length() + insert_len); - int move_len = this->len - pos + 1; - memmove(chars + pos + insert_len, chars + pos, move_len); - strncpy(chars + pos, str, insert_len); - } - } - - /// Compares the string ignoring the case - virtual bool equalsIgnoreCase(const char* alt) const { - if ((size_t)len != strlen(alt)) { - return false; - } - for (int j = 0; j < len; j++) { - if (tolower(chars[j]) != tolower(alt[j])) return false; - } - return true; - } - - /// Converts the string to an int - int toInt() { - int result = 0; - if (!isEmpty()) { - result = atoi(chars); - } - return result; - } - - /// Converts the string to an long - long toLong() { - long result = 0; - if (!isEmpty()) { - result = atol(chars); - } - return result; - } - - /// Converts the string to a double - double toDouble() { - double result = 0; - char* eptr; - if (!isEmpty()) { - result = strtod(chars, &eptr); - } - return result; - } - - /// Converts the string to a float - float toFloat() { - float result = 0; - char* eptr; - if (!isEmpty()) { -#ifdef USE_STRTOD - result = strtof(chars, &eptr); -#else - result = strtod(chars, &eptr); -#endif - } - return result; - } - - /// Converts the string to lowercase letters - void toLowerCase() { - if (chars != nullptr) { - for (int j = 0; j < len; j++) { - chars[j] = tolower(chars[j]); - } - } - } - - /// Converts the string to uppercase letters - void toUpperCase() { - if (chars != nullptr) { - for (int j = 0; j < len; j++) { - chars[j] = toupper(chars[j]); - } - } - } - - /// provides a binary string represntation - static const char* toBinary(void const* const ptr, size_t const size) { - static char result[160]; - unsigned char* b = (unsigned char*)ptr; - unsigned char byte; - int i, j, idx = 0; - - for (i = size - 1; i >= 0; i--) { - for (j = 7; j >= 0; j--) { - byte = (b[i] >> j) & 1; - result[idx++] = byte ? '1' : '0'; - } - } - result[idx] = 0; - return result; - } - - bool containsNumber() { - for (int j = 0; j < len; j++) { - if (isdigit(chars[j])) { - return true; - } - } - return false; - } - - /// Returns true if the string is an integer - bool isInteger() { - bool result = containsNumber(); - int minus_count = 0; - for (int j = 0; j < len; j++) { - char c = chars[j]; - if (!isdigit(c)) { - switch (c) { - case '-': - minus_count++; - if (minus_count > 1) { - result = false; - } - break; - default: - result = false; - break; - } - } - } - return result; - } - - /// Determines the number of decimals in the number string - int numberOfDecimals() { - int result = 0; - int pos = indexOf("."); - if (pos >= 0) { - for (int j = pos + 1; j < len; j++) { - if (isdigit(chars[j])) { - pos++; - } else { - break; - } - } - } - return result; - } - - // Returns true if the string is a number - bool isNumber() { - bool result = containsNumber(); - int dot_count = 0; - int minus_count = 0; - for (int j = 0; j < len; j++) { - char c = chars[j]; - if (!isdigit(c)) { - switch (c) { - case '-': - minus_count++; - if (minus_count > 1) { - result = false; - } - break; - case '.': - dot_count++; - if (dot_count > 1) { - result = false; - } - break; - default: - result = false; - break; - } - } - } - return result; - } - - - protected: - char* chars = nullptr; - bool is_const = false; - int len = 0; - int maxlen = 0; - int savedLen = -1; - char savedChar; - - /// only supported in subclasses - virtual bool grow(int newMaxLen) { return false; } - - static char* itoa(int n, char s[]) { - int i, sign; - if ((sign = n) < 0) /* record sign */ - n = -n; /* make n positive */ - i = 0; - do { /* generate digits in reverse order */ - s[i++] = n % 10 + '0'; /* get next digit */ - } while ((n /= 10) > 0); /* delete it */ - if (sign < 0) s[i++] = '-'; - s[i] = '\0'; - reverse(s); - return s; - } - - static void reverse(char s[]) { - int i, j; - char c; - - for (i = 0, j = strlen(s) - 1; i < j; i++, j--) { - c = s[i]; - s[i] = s[j]; - s[j] = c; - } - } - - static char* floatToString(char* outstr, double val, int precision, - int widthp) { - char temp[16]; - int i; - - /// compute the rounding factor and fractional multiplier - double roundingFactor = 0.5; - unsigned long mult = 1; - for (i = 0; i < precision; i++) { - roundingFactor /= 10.0; - mult *= 10; - } - - temp[0] = '\0'; - outstr[0] = '\0'; - - if (val < 0.0) { - strcpy(outstr, "-\0"); - val = -val; - } - - val += roundingFactor; - - strcat(outstr, itoa(int(val), temp)); // prints the int part - if (precision > 0) { - strcat(outstr, ".\0"); /// print the decimal point - unsigned long frac; - unsigned long mult = 1; - int padding = precision - 1; - while (precision--) mult *= 10; - - if (val >= 0) - frac = (val - int(val)) * mult; - else - frac = (int(val) - val) * mult; - unsigned long frac1 = frac; - - while (frac1 /= 10) padding--; - - while (padding--) strcat(outstr, "0\0"); - - strcat(outstr, itoa(frac, temp)); - } - - /// generate space padding - if ((widthp != 0) && ((size_t)widthp >= strlen(outstr))) { - int J = 0; - J = widthp - strlen(outstr); - - for (i = 0; i < J; i++) { - temp[i] = ' '; - } - - temp[i++] = '\0'; - strcat(temp, outstr); - strcpy(outstr, temp); - } - - return outstr; - } - - static int strncmp_i(const char* s1, const char* s2, int n) { - if (n == 0) return (0); - do { - if (tolower(*s1) != tolower(*s2++)) - return (*(unsigned char*)s1 - *(unsigned char*)--s2); - if (*s1++ == 0) break; - } while (--n != 0); - return (0); - } -}; - -} // namespace audio_tools \ No newline at end of file diff --git a/src/AudioTools/CoreAudio/AudioEffects.h b/src/AudioTools/CoreAudio/AudioEffects.h deleted file mode 100644 index fd7992b0c9..0000000000 --- a/src/AudioTools/CoreAudio/AudioEffects.h +++ /dev/null @@ -1,5 +0,0 @@ -#pragma once - -#include "AudioTools/CoreAudio/AudioEffects/SoundGenerator.h" -#include "AudioTools/CoreAudio/AudioEffects/AudioEffects.h" -#include "AudioTools/CoreAudio/AudioEffects/PitchShift.h" diff --git a/src/AudioTools/CoreAudio/AudioEffects/SoundGenerator.h b/src/AudioTools/CoreAudio/AudioEffects/SoundGenerator.h deleted file mode 100644 index 89c51c2d03..0000000000 --- a/src/AudioTools/CoreAudio/AudioEffects/SoundGenerator.h +++ /dev/null @@ -1,842 +0,0 @@ -#pragma once - -#include - -#include "AudioTools/CoreAudio/AudioBasic/Collections.h" -#include "AudioTools/CoreAudio/AudioLogger.h" -#include "AudioTools/CoreAudio/AudioTypes.h" -#include "AudioTools/CoreAudio/Buffers.h" - -/** - * @defgroup generator Generators - * @ingroup dsp - * @brief Sound Generators - **/ - -namespace audio_tools { - -/** - * @brief Base class to define the abstract interface for the sound generating - * classes - * @ingroup generator - * @author Phil Schatzmann - * @copyright GPLv3 - * - * @tparam T - */ -template -class SoundGenerator { - public: - SoundGenerator() { info.bits_per_sample = sizeof(T) * 8; } - - virtual ~SoundGenerator() { end(); } - - virtual bool begin(AudioInfo info) { - this->info = info; - return begin(); - } - - virtual bool begin() { - TRACED(); - active = true; - activeWarningIssued = false; - info.logInfo("SoundGenerator:"); - - // support bytes < framesize - ring_buffer.resize(info.channels * sizeof(T)); - - return true; - } - - /// ends the processing - virtual void end() { active = false; } - - /// Checks if the begin method has been called - after end() isActive returns - /// false - virtual bool isActive() { return active; } - - /// Provides a single sample - virtual T readSample() = 0; - - /// Provides the data as byte array with the requested number of channels - virtual size_t readBytes(uint8_t *data, size_t len) { - LOGD("readBytes: %d", (int)len); - if (!active) return 0; - int channels = audioInfo().channels; - int frame_size = sizeof(T) * channels; - int frames = len / frame_size; - if (len >= frame_size) { - return readBytesFrames(data, len, frames, channels); - } - return readBytesFromBuffer(data, len, frame_size, channels); - } - - /// Provides the default configuration - virtual AudioInfo defaultConfig() { - AudioInfo def; - def.bits_per_sample = sizeof(T) * 8; - return def; - } - - /// Abstract method: not implemented! Just provides an error message... - virtual void setFrequency(float frequency) { - LOGE("setFrequency not supported"); - } - - /// Provides the AudioInfo - virtual AudioInfo audioInfo() { return info; } - - /// Defines/updates the AudioInfo - virtual void setAudioInfo(AudioInfo info) { - this->info = info; - if (info.bits_per_sample != sizeof(T) * 8) { - LOGE("invalid bits_per_sample: %d", info.channels); - } - } - - protected: - bool active = false; - bool activeWarningIssued = false; - // int output_channels = 1; - AudioInfo info; - RingBuffer ring_buffer{0}; - - size_t readBytesFrames(uint8_t *buffer, size_t lengthBytes, int frames, - int channels) { - T *result_buffer = (T *)buffer; - for (int j = 0; j < frames; j++) { - T sample = readSample(); - for (int ch = 0; ch < channels; ch++) { - *result_buffer++ = sample; - } - } - return frames * sizeof(T) * channels; - } - - size_t readBytesFromBuffer(uint8_t *buffer, size_t lengthBytes, - int frame_size, int channels) { - // fill ringbuffer with one frame - if (ring_buffer.isEmpty()) { - uint8_t tmp[frame_size]; - readBytesFrames(tmp, frame_size, 1, channels); - ring_buffer.writeArray(tmp, frame_size); - } - // provide result - return ring_buffer.readArray(buffer, lengthBytes); - } -}; - -/** - * @brief Generates a Sound with the help of sin() function. If you plan to - * change the amplitude or frequency (incrementally), I suggest to use - * SineFromTable instead. - * @ingroup generator - * @author Phil Schatzmann - * @copyright GPLv3 - * - */ -template -class SineWaveGenerator : public SoundGenerator { - public: - // the scale defines the max value which is generated - SineWaveGenerator(float amplitude = 0.9f * NumberConverter::maxValueT(), - float phase = 0.0f) { - LOGD("SineWaveGenerator"); - m_amplitude = amplitude; - m_phase = phase; - } - - bool begin() override { - TRACEI(); - SoundGenerator::begin(); - this->m_deltaTime = 1.0f / SoundGenerator::info.sample_rate; - return true; - } - - bool begin(AudioInfo info) override { - LOGI("%s::begin(channels=%d, sample_rate=%d)", "SineWaveGenerator", - (int)info.channels, (int)info.sample_rate); - SoundGenerator::begin(info); - this->m_deltaTime = 1.0f / SoundGenerator::info.sample_rate; - return true; - } - - bool begin(AudioInfo info, float frequency) { - LOGI("%s::begin(channels=%d, sample_rate=%d, frequency=%.2f)", - "SineWaveGenerator", (int)info.channels, (int)info.sample_rate, - frequency); - SoundGenerator::begin(info); - this->m_deltaTime = 1.0f / SoundGenerator::info.sample_rate; - if (frequency > 0.0f) { - setFrequency(frequency); - } - return true; - } - - bool begin(int channels, int sample_rate, float frequency) { - SoundGenerator::info.channels = channels; - SoundGenerator::info.sample_rate = sample_rate; - return begin(SoundGenerator::info, frequency); - } - - // update m_deltaTime - virtual void setAudioInfo(AudioInfo info) override { - SoundGenerator::setAudioInfo(info); - this->m_deltaTime = 1.0f / SoundGenerator::info.sample_rate; - } - - virtual AudioInfo defaultConfig() override { - return SoundGenerator::defaultConfig(); - } - - /// Defines the frequency - after the processing has been started - void setFrequency(float frequency) override { - LOGI("setFrequency: %.2f", frequency); - LOGI("active: %s", SoundGenerator::active ? "true" : "false"); - m_frequency = frequency; - } - - /// Provides a single sample - virtual T readSample() override { - float angle = double_Pi * m_cycles + m_phase; - T result = m_amplitude * sinf(angle); - m_cycles += m_frequency * m_deltaTime; - if (m_cycles > 1.0f) { - m_cycles -= 1.0f; - } - return result; - } - - void setAmplitude(float amp) { m_amplitude = amp; } - - protected: - volatile float m_frequency = 0.0f; - float m_cycles = 0.0f; // Varies between 0.0 and 1.0 - float m_amplitude = 1.0f; - float m_deltaTime = 0.0f; - float m_phase = 0.0f; - const float double_Pi = 2.0f * PI; - - void logStatus() { - SoundGenerator::info.logStatus(); - LOGI("amplitude: %f", this->m_amplitude); - LOGI("active: %s", SoundGenerator::active ? "true" : "false"); - } -}; - -/** - * @brief Sine wave which is based on a fast approximation function. - * @ingroup generator - * @author Vivian Leigh Stewart - * @copyright GPLv3 - * @tparam T - */ -template -class FastSineGenerator : public SineWaveGenerator { - public: - FastSineGenerator(float amplitude = 32767.0, float phase = 0.0) - : SineWaveGenerator(amplitude, phase) { - LOGD("FastSineGenerator"); - } - - virtual T readSample() override { - float angle = - SineWaveGenerator::m_cycles + SineWaveGenerator::m_phase; - T result = SineWaveGenerator::m_amplitude * sine(angle); - SineWaveGenerator::m_cycles += - SineWaveGenerator::m_frequency * SineWaveGenerator::m_deltaTime; - if (SineWaveGenerator::m_cycles > 1.0f) { - SineWaveGenerator::m_cycles -= 1.0f; - } - return result; - } - - protected: - /// sine approximation. - inline float sine(float t) { - float p = (t - (int)t) - 0.5f; // 0 <= p <= 1 - float pp = p * p; - return (p - 6.283211f * pp * p + 9.132843f * pp * pp * p) * -6.221086f; - } -}; - -/** - * @brief Generates a square wave sound. - * @ingroup generator - * @author Phil Schatzmann - * @copyright GPLv3 - * - */ -template -class SquareWaveGenerator : public FastSineGenerator { - public: - SquareWaveGenerator(float amplitude = 32767.0f, float phase = 0.0f) - : FastSineGenerator(amplitude, phase) { - LOGD("SquareWaveGenerator"); - } - - virtual T readSample() { - return value(FastSineGenerator::readSample(), - FastSineGenerator::m_amplitude); - } - - protected: - // returns amplitude for positive vales and -amplitude for negative values - T value(T value, T amplitude) { - return (value >= 0) ? amplitude : -amplitude; - } -}; - -/** - * @brief SawToothGenerator - * @ingroup generator - * @author Phil Schatzmann - * @copyright GPLv3 - * @tparam T - */ -template -class SawToothGenerator : public SineWaveGenerator { - public: - SawToothGenerator(float amplitude = 32767.0, float phase = 0.0) - : SineWaveGenerator(amplitude, phase) { - LOGD("SawToothGenerator"); - } - - virtual T readSample() override { - float angle = - SineWaveGenerator::m_cycles + SineWaveGenerator::m_phase; - T result = SineWaveGenerator::m_amplitude * saw(angle); - SineWaveGenerator::m_cycles += - SineWaveGenerator::m_frequency * SineWaveGenerator::m_deltaTime; - if (SineWaveGenerator::m_cycles > 1.0) { - SineWaveGenerator::m_cycles -= 1.0; - } - return result; - } - - protected: - /// sine approximation. - inline float saw(float t) { - float p = (t - (int)t) - 0.5f; // 0 <= p <= 1 - return p; - } -}; - -/** - * @brief Generates a random noise sound with the help of rand() function. - * @ingroup generator - * @author Phil Schatzmann - * @copyright GPLv3 - * - */ -template -class WhiteNoiseGenerator : public SoundGenerator { - public: - /// the scale defines the max value which is generated - WhiteNoiseGenerator(T amplitude = 32767) { this->amplitude = amplitude; } - - /// Provides a single sample - T readSample() { return (random(-amplitude, amplitude)); } - - protected: - T amplitude; - // //range : [min, max] - int random(int min, int max) { return min + rand() % ((max + 1) - min); } -}; - -/** - * @brief Generates pink noise. - * @ingroup generator - * @author Phil Schatzmann - * @copyright GPLv3 - * - */ -template -class PinkNoiseGenerator : public SoundGenerator { - public: - /// the amplitude defines the max value which is generated - PinkNoiseGenerator(T amplitude = 32767) { - this->amplitude = amplitude; - max_key = 0x1f; // Five bits set - key = 0; - for (int i = 0; i < 5; i++) white_values[i] = rand() % (amplitude / 5); - } - - /// Provides a single sample - T readSample() { - T last_key = key; - unsigned int sum; - - key++; - if (key > max_key) key = 0; - // Exclusive-Or previous value with current value. This gives - // a list of bits that have changed. - int diff = last_key ^ key; - sum = 0; - for (int i = 0; i < 5; i++) { - // If bit changed get new random number for corresponding - // white_value - if (diff & (1 << i)) white_values[i] = rand() % (amplitude / 5); - sum += white_values[i]; - } - return sum; - } - - protected: - T max_key; - T key; - unsigned int white_values[5]; - unsigned int amplitude; -}; - -/** - * @brief Provides a fixed value (e.g. 0) as sound data. This can be used e.g. - * to test the output functionality which should optimally just output silence - * and no artifacts. - * @ingroup generator - * @author Phil Schatzmann - * @copyright GPLv3 - * - */ -template -class SilenceGenerator : public SoundGenerator { - public: - // the scale defines the max value which is generated - SilenceGenerator(T value = 0) { this->value = value; } - - /// Provides a single sample - T readSample() { - return value; // return 0 - } - - protected: - T value; -}; - -/** - * @brief An Adapter Class which lets you use any Stream as a Generator - * @ingroup generator - * @author Phil Schatzmann - * @copyright GPLv3 - * @tparam T - */ -template -class GeneratorFromStream : public SoundGenerator, public VolumeSupport { - public: - GeneratorFromStream() { - maxValue = NumberConverter::maxValue(sizeof(T) * 8); - }; - - /** - * @brief Constructs a new Generator from a Stream object that can be used - * e.g. as input for AudioEffectss. - * - * @param input Stream - * @param channels number of channels of the Stream - * @param volume factor my which the sample value is multiplied - default 1.0; - * Use it e.g. to reduce the volume (e.g. with 0.5) - */ - GeneratorFromStream(Stream &input, int channels = 1, float volume = 1.0) { - maxValue = NumberConverter::maxValue(sizeof(T) * 8); - setStream(input); - setVolume(volume); - setChannels(channels); - } - - /// (Re-)Assigns a stream to the Adapter class - void setStream(Stream &input) { this->p_stream = &input; } - - void setChannels(int channels) { this->channels = channels; } - - /// Provides a single sample from the stream - T readSample() { - T data = 0; - float total = 0; - if (p_stream != nullptr) { - for (int j = 0; j < channels; j++) { - p_stream->readBytes((uint8_t *)&data, sizeof(T)); - total += data; - } - float avg = (total / channels) * volume(); - if (avg > maxValue) { - data = maxValue; - } else if (avg < -maxValue) { - data = -maxValue; - } else { - data = avg; - } - } - return data; - } - - protected: - Stream *p_stream = nullptr; - int channels = 1; - float maxValue; -}; - -/** - * @brief We generate the samples from an array which is provided in the - * constructor - * @ingroup generator - * @author Phil Schatzmann - * @copyright GPLv3 - * @tparam T - */ - -template -class GeneratorFromArray : public SoundGenerator { - public: - GeneratorFromArray() = default; - /** - * @brief Construct a new Generator from an array - * - * @tparam array array of audio data of the the type defined as class template - * parameter - * @param repeat number of repetions the array should be played, - * set to 0 for endless repeat. (default 0) - * @param setInactiveAtEnd defines if the generator is set inactive when the - * array has played fully. Default is false. - * @param startIndex defines if the phase. Default is 0. - */ - - template - GeneratorFromArray(T (&array)[arrayLen], int repeat = 0, - bool setInactiveAtEnd = false, size_t startIndex = 0) { - TRACED(); - this->max_repeat = repeat; - this->inactive_at_end = setInactiveAtEnd; - this->sound_index = startIndex; - setArray(array, arrayLen); - } - - template - void setArray(T (&array)[arrayLen]) { - TRACED(); - setArray(array, arrayLen); - } - - void setArray(T *array, size_t size) { - table.resize(size); - for (int j = 0; j < size; j++) { - table[j] = array[j]; - } - LOGI("table_length: %d", (int)size); - } - - virtual bool begin(AudioInfo info) override { - return SoundGenerator::begin(info); - } - - /// Starts the generation of samples - bool begin() override { - TRACEI(); - SoundGenerator::begin(); - sound_index = 0; - repeat_counter = 0; - is_running = true; - return true; - } - - void end() override { table.resize(0); } - - /// Provides a single sample - T readSample() override { - // at end deactivate output - if (sound_index >= table.size()) { - // LOGD("reset index - sound_index: %d, table_length: - // %d",sound_index,table_length); - sound_index = 0; - // deactivate when count has been used up - if (max_repeat >= 1 && ++repeat_counter >= max_repeat) { - LOGD("atEnd"); - this->is_running = false; - if (inactive_at_end) { - this->active = false; - } - } - } - - // LOGD("index: %d - active: %d", sound_index, this->active); - T result = 0; - if (this->is_running) { - result = table[sound_index]; - sound_index += index_increment; - } - - return result; - } - - // step size the sound index is incremented (default = 1) - void setIncrement(int inc) { index_increment = inc; } - - // Sets up a sine table - returns the effective frequency - int setupSine(int sampleRate, float reqFrequency, float amplitude = 1.0) { - int sample_count = - static_cast(sampleRate) / - reqFrequency; // e.g. 44100 / 300hz = 147 samples per wave - float angle = 2.0 * PI / sample_count; - table.resize(sample_count); - for (int j = 0; j < sample_count; j++) { - table[j] = sinf(j * angle) * amplitude; - } - // calculate effective frequency - return sampleRate / sample_count; - } - - // Similar like is active to check if the array is still playing. - bool isRunning() { return is_running; } - - protected: - int sound_index = 0; - int max_repeat = 0; - int repeat_counter = 0; - bool inactive_at_end; - bool is_running = false; - bool owns_data = false; - Vector table; - int index_increment = 1; -}; - -/** - * @brief Just returns a constant value - * @ingroup generator - * @author Phil Schatzmann - * @copyright GPLv3 - * @tparam T - */ -template -class GeneratorFixedValue : public SoundGenerator { - public: - GeneratorFixedValue() = default; - - virtual bool begin(AudioInfo info) { return SoundGenerator::begin(info); } - - void setValue(T value) { value_set = value; } - - /// Starts the generation of samples - bool begin() override { - TRACEI(); - SoundGenerator::begin(); - is_running = true; - value_return = value_set; - return true; - } - - /// Provides a single sample - T readSample() override { return value_return; } - - // Similar like is active to check if the array is still playing. - bool isRunning() { return is_running; } - - protected: - T value_set = 0; - T value_return = 0; - bool is_running = false; -}; - -/** - * @brief A sine generator based on a table. The table is created using - * degrees where one full wave is 360 degrees. - * @ingroup generator - * @author Phil Schatzmann - * @copyright GPLv3 - */ -template -class SineFromTable : public SoundGenerator { - public: - SineFromTable(float amplitude = 32767.0) { - this->amplitude = amplitude; - this->amplitude_to_be = amplitude; - } - - /// Defines the new amplitude (volume) - void setAmplitude(float amplitude) { this->amplitude_to_be = amplitude; } - - /// To avoid pops we do not allow to big amplitude changes at once and spread - /// them over time - void setMaxAmplitudeStep(float step) { max_amplitude_step = step; } - - T readSample() { - // update angle - angle += step; - if (angle >= 360.0f) { - while (angle >= 360.0f) { - angle -= 360.0f; - } - // update frequency at start of circle (near 0 degrees) - step = step_new; - - updateAmplitudeInSteps(); - // amplitude = amplitude_to_be; - } - return interpolate(angle); - } - - bool begin() { - is_first = true; - SoundGenerator::begin(); - base_frequency = SoundGenerator::audioInfo().sample_rate / - 360.0f; // 122.5 hz (at 44100); 61 hz (at 22050) - return true; - } - - bool begin(AudioInfo info, float frequency) { - SoundGenerator::begin(info); - base_frequency = SoundGenerator::audioInfo().sample_rate / - 360.0f; // 122.5 hz (at 44100); 61 hz (at 22050) - setFrequency(frequency); - return true; - } - - bool begin(int channels, int sample_rate, uint16_t frequency = 0) { - SoundGenerator::info.channels = channels; - SoundGenerator::info.sample_rate = sample_rate; - return begin(SoundGenerator::info, frequency); - } - - void setFrequency(float freq) { - step_new = freq / base_frequency; - if (is_first) { - step = step_new; - is_first = false; - } - LOGD("step: %f", step_new); - } - - protected: - bool is_first = true; - float amplitude; - float amplitude_to_be; - float max_amplitude_step = 50.0f; - float base_frequency = 1.0f; - float step = 1.0f; - float step_new = 1.0f; - float angle = 0.0f; - // 122.5 hz (at 44100); 61 hz (at 22050) - const float values[181] = { - 0, 0.0174524, 0.0348995, 0.052336, 0.0697565, 0.0871557, - 0.104528, 0.121869, 0.139173, 0.156434, 0.173648, 0.190809, - 0.207912, 0.224951, 0.241922, 0.258819, 0.275637, 0.292372, - 0.309017, 0.325568, 0.34202, 0.358368, 0.374607, 0.390731, - 0.406737, 0.422618, 0.438371, 0.45399, 0.469472, 0.48481, - 0.5, 0.515038, 0.529919, 0.544639, 0.559193, 0.573576, - 0.587785, 0.601815, 0.615661, 0.62932, 0.642788, 0.656059, - 0.669131, 0.681998, 0.694658, 0.707107, 0.71934, 0.731354, - 0.743145, 0.75471, 0.766044, 0.777146, 0.788011, 0.798636, - 0.809017, 0.819152, 0.829038, 0.838671, 0.848048, 0.857167, - 0.866025, 0.87462, 0.882948, 0.891007, 0.898794, 0.906308, - 0.913545, 0.920505, 0.927184, 0.93358, 0.939693, 0.945519, - 0.951057, 0.956305, 0.961262, 0.965926, 0.970296, 0.97437, - 0.978148, 0.981627, 0.984808, 0.987688, 0.990268, 0.992546, - 0.994522, 0.996195, 0.997564, 0.99863, 0.999391, 0.999848, - 1, 0.999848, 0.999391, 0.99863, 0.997564, 0.996195, - 0.994522, 0.992546, 0.990268, 0.987688, 0.984808, 0.981627, - 0.978148, 0.97437, 0.970296, 0.965926, 0.961262, 0.956305, - 0.951057, 0.945519, 0.939693, 0.93358, 0.927184, 0.920505, - 0.913545, 0.906308, 0.898794, 0.891007, 0.882948, 0.87462, - 0.866025, 0.857167, 0.848048, 0.838671, 0.829038, 0.819152, - 0.809017, 0.798636, 0.788011, 0.777146, 0.766044, 0.75471, - 0.743145, 0.731354, 0.71934, 0.707107, 0.694658, 0.681998, - 0.669131, 0.656059, 0.642788, 0.62932, 0.615661, 0.601815, - 0.587785, 0.573576, 0.559193, 0.544639, 0.529919, 0.515038, - 0.5, 0.48481, 0.469472, 0.45399, 0.438371, 0.422618, - 0.406737, 0.390731, 0.374607, 0.358368, 0.34202, 0.325568, - 0.309017, 0.292372, 0.275637, 0.258819, 0.241922, 0.224951, - 0.207912, 0.190809, 0.173648, 0.156434, 0.139173, 0.121869, - 0.104528, 0.0871557, 0.0697565, 0.052336, 0.0348995, 0.0174524, - 0}; - - T interpolate(float angle) { - bool positive = (angle <= 180.0f); - float angle_positive = positive ? angle : angle - 180.0f; - int angle_int1 = angle_positive; - int angle_int2 = angle_int1 + 1; - T v1 = values[angle_int1] * amplitude; - T v2 = values[angle_int2] * amplitude; - T result = v1 < v2 ? map(angle_positive, angle_int1, angle_int2, v1, v2) - : map(angle_positive, angle_int1, angle_int2, v2, v1); - // float result = v1; - return positive ? result : -result; - } - - T map(T x, T in_min, T in_max, T out_min, T out_max) { - return (x - in_min) * (out_max - out_min) / (in_max - in_min) + out_min; - } - - void updateAmplitudeInSteps() { - float diff = amplitude_to_be - amplitude; - if (abs(diff) > max_amplitude_step) { - diff = (diff < 0) ? -max_amplitude_step : max_amplitude_step; - } - if (abs(diff) >= 1.0f) { - amplitude += diff; - } - } -}; - -/** - * @brief Generator which combines (mixes) multiple sound generators into one - * output - * @ingroup generator - * @author Phil Schatzmann - * @copyright GPLv3 - * @tparam T - */ -template -class GeneratorMixer : public SoundGenerator { - public: - GeneratorMixer() = default; - - void add(SoundGenerator &generator) { vector.push_back(&generator); } - void add(SoundGenerator *generator) { vector.push_back(generator); } - - void clear() { vector.clear(); } - - T readSample() { - float total = 0.0f; - float count = 0.0f; - for (auto &generator : vector) { - if (generator->isActive()) { - T sample = generator->readSample(); - total += sample; - count += 1.0f; - } - } - return count > 0.0f ? total / count : 0; - } - - protected: - Vector *> vector; - int actualChannel = 0; -}; - -/** - * @brief Generates a test signal which is easy to check because the values are - * incremented or decremented by 1 - * @ingroup generator - * @author Phil Schatzmann - * @copyright GPLv3 - * @tparam T - */ -template -class TestGenerator : public SoundGenerator { - public: - TestGenerator(T max = 1000, T inc = 1) { this->max = max; } - - T readSample() override { - value += inc; - if (abs(value) >= max) { - inc = -inc; - value += (inc * 2); - } - return value; - } - - protected: - T max; - T value = 0; - T inc = 1; -}; - -} // namespace audio_tools diff --git a/src/AudioTools/CoreAudio/AudioFilter.h b/src/AudioTools/CoreAudio/AudioFilter.h deleted file mode 100644 index 45685b39e7..0000000000 --- a/src/AudioTools/CoreAudio/AudioFilter.h +++ /dev/null @@ -1,5 +0,0 @@ -#pragma once - -#include "AudioTools/CoreAudio/AudioFilter/Filter.h" -#include "AudioTools/CoreAudio/AudioFilter/Equalizer.h" -#include "AudioTools/CoreAudio/AudioFilter/MedianFilter.h" diff --git a/src/AudioTools/CoreAudio/AudioFilter/Equalizer.h b/src/AudioTools/CoreAudio/AudioFilter/Equalizer.h deleted file mode 100644 index 0f89e01b92..0000000000 --- a/src/AudioTools/CoreAudio/AudioFilter/Equalizer.h +++ /dev/null @@ -1,240 +0,0 @@ -#pragma once -#include - -#include "AudioToolsConfig.h" -#include "AudioTools/CoreAudio/AudioOutput.h" -#include "AudioTools/CoreAudio/AudioStreams.h" - -/** - * @defgroup equilizer Equalizer - * @ingroup dsp - * @brief Digital Equalizer - **/ - -namespace audio_tools { - -/** - * @brief Configuration for 3 Band Equalizer: Set - * channels,bits_per_sample,sample_rate. Set and update gain_low, gain_medium - * and gain_high to value between 0 and 1.0 - * @ingroup equilizer - * @author pschatzmann - */ -struct ConfigEqualizer3Bands : public AudioInfo { - ConfigEqualizer3Bands() { - channels = 2; - bits_per_sample = 16; - sample_rate = 44100; - } - - ConfigEqualizer3Bands(const ConfigEqualizer3Bands &) = delete; - - // Frequencies - int freq_low = 880; - int freq_high = 5000; - - // Gain Controls - float gain_low = 1.0; - float gain_medium = 1.0; - float gain_high = 1.0; -}; - -/** - * @brief 3 Band Equalizer inspired from - * https://www.musicdsp.org/en/latest/Filters/236-3-band-equaliser.html - * @ingroup equilizer - * @author pschatzmann - */ -class Equalizer3Bands : public ModifyingStream { - public: - Equalizer3Bands(Print &out) { setOutput(out); } - - Equalizer3Bands(Stream &in) { setStream(in); } - - Equalizer3Bands(AudioOutput &out) { - setOutput(out); - out.addNotifyAudioChange(*this); - } - - Equalizer3Bands(AudioStream &stream) { - setStream(stream); - stream.addNotifyAudioChange(*this); - } - - ~Equalizer3Bands() { - if (state != nullptr) delete[] state; - } - - /// Defines/Changes the input & output - void setStream(Stream &io) override { - p_print = &io; - p_stream = &io; - }; - - /// Defines/Changes the output target - void setOutput(Print &out) override { p_print = &out; } - - ConfigEqualizer3Bands &config() { return cfg; } - - ConfigEqualizer3Bands &defaultConfig() { return config(); } - - bool begin(ConfigEqualizer3Bands &config) { - p_cfg = &config; - if (p_cfg->channels > max_state_count) { - if (state != nullptr) delete[] state; - state = new EQSTATE[p_cfg->channels]; - max_state_count = p_cfg->channels; - } - - // Setup state - for (int j = 0; j < max_state_count; j++) { - memset(&state[j], 0, sizeof(EQSTATE)); - - // Calculate filter cutoff frequencies - state[j].lf = - 2 * - sin((float)PI * ((float)p_cfg->freq_low / (float)p_cfg->sample_rate)); - state[j].hf = 2 * sin((float)PI * ((float)p_cfg->freq_high / - (float)p_cfg->sample_rate)); - } - return true; - } - - virtual void setAudioInfo(AudioInfo info) override { - p_cfg->sample_rate = info.sample_rate; - p_cfg->channels = info.channels; - p_cfg->bits_per_sample = info.bits_per_sample; - begin(*p_cfg); - } - - size_t write(const uint8_t *data, size_t len) override { - filterSamples(data, len); - return p_print->write(data, len); - } - - int availableForWrite() override { return p_print->availableForWrite(); } - - /// Provides the data from all streams mixed together - size_t readBytes(uint8_t *data, size_t len) override { - size_t result = 0; - if (p_stream != nullptr) { - result = p_stream->readBytes(data, len); - filterSamples(data, len); - } - return result; - } - - int available() override { - return p_stream != nullptr ? p_stream->available() : 0; - } - - protected: - ConfigEqualizer3Bands cfg; - ConfigEqualizer3Bands *p_cfg = &cfg; - const float vsa = (1.0 / 4294967295.0); // Very small amount (Denormal Fix) - Print *p_print = nullptr; // support for write - Stream *p_stream = nullptr; // support for write - // AudioOutput *p_out=nullptr; // support for write - // AudioStream *p_in=nullptr; // support for readBytes - int max_state_count = 0; - - struct EQSTATE { - // Filter #1 (Low band) - float lf; // Frequency - float f1p0; // Poles ... - float f1p1; - float f1p2; - float f1p3; - - // Filter #2 (High band) - float hf; // Frequency - float f2p0; // Poles ... - float f2p1; - float f2p2; - float f2p3; - - // Sample history buffer - float sdm1; // Sample data minus 1 - float sdm2; // 2 - float sdm3; // 3 - - } *state = nullptr; - - void filterSamples(const uint8_t *data, size_t len) { - if (state == nullptr){ - LOGE("You need to call begin() before using the equalizer"); - return; - } - switch (p_cfg->bits_per_sample) { - case 16: { - int16_t *p_dataT = (int16_t *)data; - size_t sample_count = len / sizeof(int16_t); - for (size_t j = 0; j < sample_count; j += p_cfg->channels) { - for (int ch = 0; ch < p_cfg->channels; ch++) { - p_dataT[j + ch] = NumberConverter::fromFloat(sample(state[ch], NumberConverter::toFloat(p_dataT[j + ch], 16)), 16); - } - } - } break; - case 24: { - int24_t *p_dataT = (int24_t *)data; - size_t sample_count = len / sizeof(int24_t); - for (size_t j = 0; j < sample_count; j += p_cfg->channels) { - for (int ch = 0; ch < p_cfg->channels; ch++) { - p_dataT[j + ch] = NumberConverter::fromFloat(sample(state[ch], NumberConverter::toFloat(p_dataT[j + ch], 24)), 24); - } - } - } break; - case 32: { - int32_t *p_dataT = (int32_t *)data; - size_t sample_count = len / sizeof(int32_t); - for (size_t j = 0; j < sample_count; j += p_cfg->channels) { - for (int ch = 0; ch < p_cfg->channels; ch++) { - p_dataT[j + ch] = NumberConverter::fromFloat(sample(state[ch], NumberConverter::toFloat(p_dataT[j + ch], 32)), 32); - } - } - } break; - - default: - LOGE("Only 16 bits supported: %d", p_cfg->bits_per_sample); - break; - } - } - - - // calculates a single sample using the indicated state - float sample(EQSTATE &es, float sample) { - // Locals - float l, m, h; // Low / Mid / High - Sample Values - // Filter #1 (lowpass) - es.f1p0 += (es.lf * (sample - es.f1p0)) + vsa; - es.f1p1 += (es.lf * (es.f1p0 - es.f1p1)); - es.f1p2 += (es.lf * (es.f1p1 - es.f1p2)); - es.f1p3 += (es.lf * (es.f1p2 - es.f1p3)); - - l = es.f1p3; - - // Filter #2 (highpass) - es.f2p0 += (es.hf * (sample - es.f2p0)) + vsa; - es.f2p1 += (es.hf * (es.f2p0 - es.f2p1)); - es.f2p2 += (es.hf * (es.f2p1 - es.f2p2)); - es.f2p3 += (es.hf * (es.f2p2 - es.f2p3)); - - h = es.sdm3 - es.f2p3; - // Calculate midrange (signal - (low + high)) - m = es.sdm3 - (h + l); - // Scale, Combine and store - l *= p_cfg->gain_low; - m *= p_cfg->gain_medium; - h *= p_cfg->gain_high; - - // Shuffle history buffer - es.sdm3 = es.sdm2; - es.sdm2 = es.sdm1; - es.sdm1 = sample; - - // Return result - return (l + m + h); - } -}; - -} // namespace audio_tools \ No newline at end of file diff --git a/src/AudioTools/CoreAudio/AudioFilter/Filter.h b/src/AudioTools/CoreAudio/AudioFilter/Filter.h deleted file mode 100644 index 0ceef7be14..0000000000 --- a/src/AudioTools/CoreAudio/AudioFilter/Filter.h +++ /dev/null @@ -1,591 +0,0 @@ -#pragma once -#include "AudioToolsConfig.h" -#include -#ifdef USE_TYPETRAITS -#include -#endif - -/** - * @defgroup dsp DSP - * @ingroup main - * @brief Digital Signal Processing - **/ - -/** - * @defgroup filter Filters - * @ingroup dsp - * @brief Digital Filters - **/ - -namespace audio_tools { - -/** - * @brief Abstract filter interface definition; - * @ingroup filter - * @author pschatzmann - */ -template -class Filter { - public: - // construct without coefs - Filter() = default; - virtual ~Filter() = default; - Filter(Filter const &) = delete; - Filter &operator=(Filter const &) = delete; - - virtual T process(T in) = 0; -}; - -/** - * @brief No change to the input - * @ingroup filter - * @author pschatzmann - * @tparam T - */ -template -class NoFilter : Filter { - public: - // construct without coefs - NoFilter() = default; - virtual T process(T in) { return in; } -}; - -/** - * @brief FIR Filter - * Converted from - * https://github.com/sebnil/FIR-filter-Arduino-Library/tree/master/src - * You can use https://www.arc.id.au/FilterDesign.html to design the filter - * @ingroup filter - * @author Pieter P tttapa / pschatzmann - * @copyright GNU General Public License v3.0 - */ -template -class FIR : public Filter { - public: - template - FIR(const T (&b)[B], const T factor = 1.0) : lenB(B), factor(factor) { - setValues(b); - } - - template - void setValues(const T (&b)[B]) { - x.resize(lenB); - coeff_b.resize(2 * lenB - 1); - for (uint16_t i = 0; i < 2 * lenB - 1; i++) { - coeff_b[i] = b[(2 * lenB - 1 - i) % lenB]; - } - } - - T process(T value) { - x[i_b] = value; - T b_terms = 0; - T *b_shift = &coeff_b[lenB - i_b - 1]; - for (uint8_t i = 0; i < lenB; i++) { - b_terms += b_shift[i] * x[i]; - } - i_b++; - if (i_b == lenB) i_b = 0; - -#ifdef USE_TYPETRAITS - if (!(std::is_same::value || std::is_same::value)) { - b_terms = b_terms / factor; - } -#else - if (factor != 1.0) { - b_terms = b_terms / factor; - } -#endif - return b_terms; - } - - private: - const uint8_t lenB; - uint8_t i_b = 0; - Vector x; - Vector coeff_b; - T factor; -}; - -/** - * @brief IIRFilter - * Converted from https://github.com/tttapa/Filters/blob/master/src/IIRFilter.h - * @ingroup filter - * @author Pieter P tttapa / pschatzmann - * @copyright GNU General Public License v3.0 - * @tparam T - */ -template -class IIR : public Filter { - public: - template - IIR(const T (&b)[B], const T (&_a)[A], T factor = 1.0) - : factor(factor), lenB(B), lenA(A - 1) { - x.resize(lenB); - y.resize(lenA); - coeff_b.resize(2 * lenB - 1); - coeff_a.resize(2 * lenA - 1); - T a0 = _a[0]; - const T *a = &_a[1]; - for (uint16_t i = 0; i < 2 * lenB - 1; i++) { - coeff_b[i] = b[(2 * lenB - 1 - i) % lenB] / a0; - } - for (uint16_t i = 0; i < 2 * lenA - 1; i++) { - coeff_a[i] = a[(2 * lenA - 2 - i) % lenA] / a0; - } - } - - - T process(T value) { - x[i_b] = value; - T b_terms = 0; - T *b_shift = &coeff_b[lenB - i_b - 1]; - - T a_terms = 0; - T *a_shift = &coeff_a[lenA - i_a - 1]; - - for (uint8_t i = 0; i < lenB; i++) { - b_terms += x[i] * b_shift[i]; - } - for (uint8_t i = 0; i < lenA; i++) { - a_terms += y[i] * a_shift[i]; - } - - T filtered = b_terms - a_terms; - y[i_a] = filtered; - i_b++; - if (i_b == lenB) i_b = 0; - i_a++; - if (i_a == lenA) i_a = 0; - -#ifdef USE_TYPETRAITS - if (!(std::is_same::value || std::is_same::value)) { - filtered = filtered / factor; - } -#else - if (factor != 1.0) { - filtered = filtered / factor; - } -#endif - return filtered; - } - - private: - T factor; - const uint8_t lenB, lenA; - uint8_t i_b = 0, i_a = 0; - Vector x; - Vector y; - Vector coeff_b; - Vector coeff_a; -}; - -/** - * @brief Biquad DF1 Filter. - * converted from https://github.com/tttapa/Filters/blob/master/src/BiQuad.h - * Use float or double (and not a integer type) as type parameter - * @ingroup filter - * @author Pieter P tttapa / pschatzmann - * @copyright GNU General Public License v3.0 - * @tparam T - */ -template -class BiQuadDF1 : public Filter { - public: - BiQuadDF1(const T (&b)[3], const T (&a)[3]) - : b_0(b[0] / a[0]), - b_1(b[1] / a[0]), - b_2(b[2] / a[0]), - a_1(a[1] / a[0]), - a_2(a[2] / a[0]) {} - BiQuadDF1(const T (&b)[3], const T (&a)[2]) - : b_0(b[0]), b_1(b[1]), b_2(b[2]), a_1(a[0]), a_2(a[1]) {} - BiQuadDF1(const T (&b)[3], const T (&a)[2], T gain) - : b_0(gain * b[0]), - b_1(gain * b[1]), - b_2(gain * b[2]), - a_1(a[0]), - a_2(a[1]) {} - BiQuadDF1(const T (&b)[3], const T (&a)[3], T gain) - : b_0(gain * b[0] / a[0]), - b_1(gain * b[1] / a[0]), - b_2(gain * b[2] / a[0]), - a_1(a[1] / a[0]), - a_2(a[2] / a[0]) {} - - T process(T value) { - T x_2 = x_1; - x_1 = x_0; - x_0 = value; - T b_terms = x_0 * b_0 + x_1 * b_1 + x_2 * b_2; - T a_terms = y_1 * a_1 + y_2 * a_2; - y_2 = y_1; - y_1 = b_terms - a_terms; - return y_1; - } - - private: - T b_0; - T b_1; - T b_2; - T a_1; - T a_2; - - T x_0 = 0; - T x_1 = 0; - T y_1 = 0; - T y_2 = 0; -}; - -/** - * @brief Biquad DF2 Filter. When dealing with high-order IIR filters, they can - * get unstable. To prevent this, BiQuadratic filters (second order) are used. - * Converted from https://github.com/tttapa/Filters/blob/master/src/BiQuad.h - * Use float or double (and not a integer type) as type parameter - * @ingroup filter - * @author Pieter P tttapa / pschatzmann - * @copyright GNU General Public License v3.0 - * @tparam T - */ -template -class BiQuadDF2 : public Filter { - public: - BiQuadDF2(const T (&b)[3], const T (&a)[3]) - : b_0(b[0] / a[0]), - b_1(b[1] / a[0]), - b_2(b[2] / a[0]), - a_1(a[1] / a[0]), - a_2(a[2] / a[0]) {} - BiQuadDF2(const T (&b)[3], const T (&a)[2]) - : b_0(b[0]), b_1(b[1]), b_2(b[2]), a_1(a[0]), a_2(a[1]) {} - BiQuadDF2(const T (&b)[3], const T (&a)[2], T gain) - : b_0(gain * b[0]), - b_1(gain * b[1]), - b_2(gain * b[2]), - a_1(a[0]), - a_2(a[1]) {} - BiQuadDF2(const T (&b)[3], const T (&a)[3], T gain) - : b_0(gain * b[0] / a[0]), - b_1(gain * b[1] / a[0]), - b_2(gain * b[2] / a[0]), - a_1(a[1] / a[0]), - a_2(a[2] / a[0]) {} - - T process(T value) { - T w_2 = w_1; - w_1 = w_0; - w_0 = value - a_1 * w_1 - a_2 * w_2; - T y = b_0 * w_0 + b_1 * w_1 + b_2 * w_2; - return y; - } - - protected: - T b_0 = 0; - T b_1 = 0; - T b_2 = 0; - T a_1 = 0; - T a_2 = 0; - - // allow constructor w/o parameter in subclasses - BiQuadDF2() = default; - - T w_0 = 0; - T w_1 = 0; -}; - -/** - * @brief Biquad DF2 Low Pass Filter. When dealing with high-order IIR - * filters, they can get unstable. To prevent this, BiQuadratic filters (second - * order) are used. Converted from - * https://github.com/tttapa/Filters/blob/master/src/BiQuad.h Use float or - * double (and not a integer type) as type parameter - * @ingroup filter - * @author pschatzmann - * @copyright GNU General Public License v3.0 - * @tparam T - */ - -template -class LowPassFilter : public BiQuadDF2 { - public: - LowPassFilter(float frequency, float sampleRate, float q = 0.7071f) - : BiQuadDF2() { - begin(frequency, sampleRate, q); - } - void begin(float frequency, float sampleRate, float q = 0.7071f) { - T w0 = frequency * (2.0f * 3.141592654f / sampleRate); - T sinW0 = sin(w0); - T alpha = sinW0 / ((float)q * 2.0); - T cosW0 = cos(w0); - T scale = 1.0 / (1.0 + alpha); - BiQuadDF2::b_0 = ((1.0 - cosW0) / 2.0) * scale; - BiQuadDF2::b_1 = (1.0 - cosW0) * scale; - BiQuadDF2::b_2 = BiQuadDF2::b_0; - BiQuadDF2::a_1 = (-2.0 * cosW0) * scale; - BiQuadDF2::a_2 = (1.0 - alpha) * scale; - } -}; - -/** - * @brief Biquad DF2 High Pass Filter. When dealing with high-order IIR - * filters, they can get unstable. To prevent this, BiQuadratic filters (second - * order) are used. Converted from - * https://github.com/tttapa/Filters/blob/master/src/BiQuad.h Use float or - * double (and not a integer type) as type parameter - * @ingroup filter - * @author pschatzmann - * @copyright GNU General Public License v3.0 - * @tparam T - */ - -template -class HighPassFilter : public BiQuadDF2 { - public: - HighPassFilter(float frequency, float sampleRate, float q = 0.7071) - : BiQuadDF2() { - begin(frequency, sampleRate, q); - } - void begin(float frequency, float sampleRate, float q = 0.7071) { - T w0 = frequency * (2.0f * 3.141592654f / sampleRate); - T sinW0 = sin(w0); - T alpha = sinW0 / ((float)q * 2.0); - T cosW0 = cos(w0); - T scale = 1.0 / (1.0 + alpha); - BiQuadDF2::b_0 = ((1.0 + cosW0) / 2.0) * scale; - BiQuadDF2::b_1 = -(1.0 + cosW0) * scale; - BiQuadDF2::b_2 = BiQuadDF2::b_0; - BiQuadDF2::a_1 = (-2.0 * cosW0) * scale; - BiQuadDF2::a_2 = (1.0 - alpha) * scale; - } -}; - -/** - * @brief Biquad DF2 Band Pass Filter. When dealing with high-order IIR - * filters, they can get unstable. To prevent this, BiQuadratic filters (second - * order) are used. Converted from - * https://github.com/tttapa/Filters/blob/master/src/BiQuad.h Use float or - * double (and not a integer type) as type parameter - * @ingroup filter - * @author pschatzmann - * @copyright GNU General Public License v3.0 - * @tparam T - */ - -template -class BandPassFilter : public BiQuadDF2 { - public: - BandPassFilter(float frequency, float sampleRate, float q = 1.0) - : BiQuadDF2() { - begin(frequency, sampleRate, q); - } - void begin(float frequency, float sampleRate, float q = 1.0) { - T w0 = frequency * (2.0f * 3.141592654f / sampleRate); - T sinW0 = sin(w0); - T alpha = sinW0 / ((T)q * 2.0); - T cosW0 = cos(w0); - T scale = 1.0 / (1.0 + alpha); - BiQuadDF2::b_0 = alpha * scale; - BiQuadDF2::b_1 = 0; - BiQuadDF2::b_2 = (-alpha) * scale; - BiQuadDF2::a_1 = (-2.0 * cosW0) * scale; - BiQuadDF2::a_2 = (1.0 - alpha) * scale; - } -}; - -/** - * @brief Biquad DF2 Notch Filter. When dealing with high-order IIR - * filters, they can get unstable. To prevent this, BiQuadratic filters (second - * order) are used. Converted from - * https://github.com/tttapa/Filters/blob/master/src/BiQuad.h Use float or - * double (and not a integer type) as type parameter - * @ingroup filter - * @author pschatzmann - * @copyright GNU General Public License v3.0 - * @tparam T - */ - -template -class NotchFilter : public BiQuadDF2 { - public: - NotchFilter(float frequency, float sampleRate, float q = 1.0) - : BiQuadDF2() { - begin(frequency, sampleRate, q); - } - - void begin(float frequency, float sampleRate, float q = 1.0) { - T w0 = frequency * (2.0f * 3.141592654f / sampleRate); - T sinW0 = sin(w0); - T alpha = sinW0 / ((float)q * 2.0); - T cosW0 = cos(w0); - T scale = 1.0 / (1.0 + alpha); - BiQuadDF2::b_0 = scale; - BiQuadDF2::b_1 = (-2.0 * cosW0) * scale; - BiQuadDF2::b_2 = BiQuadDF2::b_0; - BiQuadDF2::a_1 = (-2.0 * cosW0) * scale; - BiQuadDF2::a_2 = (1.0 - alpha) * scale; - } -}; - -/** - * @brief Biquad DF2 Low Shelf Filter. When dealing with high-order IIR - * filters, they can get unstable. To prevent this, BiQuadratic filters (second - * order) are used. Converted from - * https://github.com/tttapa/Filters/blob/master/src/BiQuad.h Use float or - * double (and not a integer type) as type parameter - * @ingroup filter - * @author pschatzmann - * @copyright GNU General Public License v3.0 - * @tparam T - */ - -template -class LowShelfFilter : public BiQuadDF2 { - public: - LowShelfFilter(float frequency, float sampleRate, float gain, - float slope = 1.0f) - : BiQuadDF2() { - begin(frequency, sampleRate, gain, slope); - } - - void begin(float frequency, float sampleRate, float gain, - float slope = 1.0f) { - T a = pow(10.0, gain / 40.0f); - T w0 = frequency * (2.0f * 3.141592654f / sampleRate); - T sinW0 = sin(w0); - // float alpha = (sinW0 * sqrt((a+1/a)*(1/slope-1)+2) ) / 2.0; - T cosW0 = cos(w0); - // generate three helper-values (intermediate results): - T sinsq = sinW0 * - sqrt((pow(a, 2.0) + 1.0) * (1.0 / (float)slope - 1.0) + 2.0 * a); - T aMinus = (a - 1.0) * cosW0; - T aPlus = (a + 1.0) * cosW0; - T scale = 1.0 / ((a + 1.0) + aMinus + sinsq); - BiQuadDF2::b_0 = a * ((a + 1.0) - aMinus + sinsq) * scale; - BiQuadDF2::b_1 = 2.0 * a * ((a - 1.0) - aPlus) * scale; - BiQuadDF2::b_2 = a * ((a + 1.0) - aMinus - sinsq) * scale; - BiQuadDF2::a_1 = -2.0 * ((a - 1.0) + aPlus) * scale; - BiQuadDF2::a_2 = ((a + 1.0) + aMinus - sinsq) * scale; - } -}; - -/** - * @brief Biquad DF2 High Shelf Filter. When dealing with high-order IIR - * filters, they can get unstable. To prevent this, BiQuadratic filters (second - * order) are used. Converted from - * https://github.com/tttapa/Filters/blob/master/src/BiQuad.h Use float or - * double (and not a integer type) as type parameter - * @ingroup filter - * @author pschatzmann - * @copyright GNU General Public License v3.0 - * @tparam T - */ - -template -class HighShelfFilter : public BiQuadDF2 { - public: - HighShelfFilter(float frequency, float sampleRate, float gain, - float slope = 1.0f) - : BiQuadDF2() { - begin(frequency, sampleRate, gain, slope); - } - void begin(float frequency, float sampleRate, float gain, - float slope = 1.0f) { - T a = pow(10.0, gain / 40.0f); - T w0 = frequency * (2.0f * 3.141592654f / sampleRate); - T sinW0 = sin(w0); - // float alpha = (sinW0 * sqrt((a+1/a)*(1/slope-1)+2) ) / 2.0; - T cosW0 = cos(w0); - // generate three helper-values (intermediate results): - T sinsq = sinW0 * - sqrt((pow(a, 2.0) + 1.0) * (1.0 / (float)slope - 1.0) + 2.0 * a); - T aMinus = (a - 1.0) * cosW0; - T aPlus = (a + 1.0) * cosW0; - T scale = 1.0 / ((a + 1.0) - aMinus + sinsq); - BiQuadDF2::b_0 = a * ((a + 1.0) + aMinus + sinsq) * scale; - BiQuadDF2::b_1 = -2.0 * a * ((a - 1.0) + aPlus) * scale; - BiQuadDF2::b_2 = a * ((a + 1.0) + aMinus - sinsq) * scale; - BiQuadDF2::a_1 = 2.0 * ((a - 1.0) - aPlus) * scale; - BiQuadDF2::a_2 = ((a + 1.0) - aMinus - sinsq) * scale; - } -}; - -/** - * @brief Second Order Filter: Instead of manually cascading BiQuad filters, you - * can use a Second Order Sections filter (SOS). converted from - * https://github.com/tttapa/Filters/blob/master/src/SOSFilter.h Use float or - * float (and not a integer type) as type parameter - * @ingroup filter - * @author Pieter P tttapa / pschatzmann - * @copyright GNU General Public License v3.0 - */ - -template -class SOSFilter : public Filter { - public: - SOSFilter(const T (&b)[N][3], const T (&a)[N][3], const T (&gain)[N]) { - for (size_t i = 0; i < N; i++) - filters[i] = new BiQuadDF2(b[i], a[i], gain[i]); - } - SOSFilter(const T (&sos)[N][6], const T (&gain)[N]) { - for (size_t i = 0; i < N; i++) { - T b[3]; - T a[3]; - copy(b, &sos[i][0]); - copy(a, &sos[i][3]); - filters[i] = new BiQuadDF2(b, a, gain[i]); - } - } - SOSFilter(const T (&b)[N][3], const T (&a)[N][2], const T (&gain)[N]) { - for (size_t i = 0; i < N; i++) - filters[i] = new BiQuadDF2(b[i], a[i], gain[i]); - } - SOSFilter(const T (&b)[N][3], const T (&a)[N][2]) { - for (size_t i = 0; i < N; i++) filters[i] = new BiQuadDF2(b[i], a[i]); - } - SOSFilter(const T (&b)[N][3], const T (&a)[N][3]) { - for (size_t i = 0; i < N; i++) filters[i] = new BiQuadDF2(b[i], a[i]); - } - ~SOSFilter() { - for (size_t i = 0; i < N; i++) delete filters[i]; - } - T process(T value) { - for (Filter *&filter : filters) value = filter->process(value); - return value; - } - - private: - Filter *filters[N]; - template - void copy(T (&dest)[M], const T *src) { - for (size_t i = 0; i < M; i++) dest[i] = src[i]; - } -}; - -/** - * @brief FilterChain - A Cascade of multiple filters - * @ingroup filter - * @tparam T - * @tparam N - */ -template -class FilterChain : public Filter { - public: - FilterChain(Filter *(&&filters)[N]) { - for (size_t i = 0; i < N; i++) { - this->filters[i] = filters[i]; - } - } - - T process(T value) { - for (Filter *&filter : filters) { - if (filter != nullptr) { - value = filter->process(value); - } - } - return value; - } - - private: - Filter *filters[N] = {0}; -}; - -} // namespace audio_tools diff --git a/src/AudioTools/CoreAudio/AudioFilter/MedianFilter.h b/src/AudioTools/CoreAudio/AudioFilter/MedianFilter.h deleted file mode 100644 index 153fb10ccd..0000000000 --- a/src/AudioTools/CoreAudio/AudioFilter/MedianFilter.h +++ /dev/null @@ -1,125 +0,0 @@ -#pragma once -#include "Filter.h" -#include "AudioTools/CoreAudio/AudioBasic/Collections/Vector.h" - -namespace audio_tools { - -/** - * @brief An embedded friendly, fast one-dimensional median filter algorithm - *implementation in C and C++ Useful for spike and noise removal from analog - *signals or other DSP Also known as "salt-and-pepper noise" or "impulse noise" - *filter - * @ingroup filter - **/ -template -class MedianFilter : public Filter { - public: - MedianFilter(int size = 7) { - medianBuffer.resize(size); - medianFilter.numNodes = size; - medianFilter.medianBuffer = medianBuffer.data(); - init(&medianFilter); - }; - - virtual T process(T in) override { return insert(&medianFilter, in); } - - protected: - struct MedianNode_t { - T value = 0; // sample value - struct MedianNode_t *nextAge = nullptr; // pointer to next oldest value - struct MedianNode_t *nextValue = nullptr; // pointer to next smallest value - struct MedianNode_t *prevValue = - nullptr; // pointer to previous smallest value - }; - - struct MedianFilter_t { - unsigned int numNodes = 0; // median node buffer length - MedianNode_t *medianBuffer = nullptr; // median node buffer - MedianNode_t *ageHead = nullptr; // pointer to oldest value - MedianNode_t *valueHead = nullptr; // pointer to smallest value - MedianNode_t *medianHead = nullptr; // pointer to median value - }; - - MedianFilter_t medianFilter; - Vector medianBuffer{0}; - - int init(MedianFilter_t *medianFilter) { - if (medianFilter && medianFilter->medianBuffer && - (medianFilter->numNodes % 2) && (medianFilter->numNodes > 1)) { - // initialize buffer nodes - for (unsigned int i = 0; i < medianFilter->numNodes; i++) { - medianFilter->medianBuffer[i].value = 0; - medianFilter->medianBuffer[i].nextAge = - &medianFilter->medianBuffer[(i + 1) % medianFilter->numNodes]; - medianFilter->medianBuffer[i].nextValue = - &medianFilter->medianBuffer[(i + 1) % medianFilter->numNodes]; - medianFilter->medianBuffer[(i + 1) % medianFilter->numNodes].prevValue = - &medianFilter->medianBuffer[i]; - } - // initialize heads - medianFilter->ageHead = medianFilter->medianBuffer; - medianFilter->valueHead = medianFilter->medianBuffer; - medianFilter->medianHead = - &medianFilter->medianBuffer[medianFilter->numNodes / 2]; - - return 0; - } - - return -1; - } - - int insert(MedianFilter_t *medianFilter, T sample) { - unsigned int i; - MedianNode_t *newNode=nullptr, *it=nullptr; - - if (medianFilter->ageHead == medianFilter->valueHead) { - // if oldest node is also the smallest node, increment value head - medianFilter->valueHead = medianFilter->valueHead->nextValue; - } - - if ((medianFilter->ageHead == medianFilter->medianHead) || - (medianFilter->ageHead->value > medianFilter->medianHead->value)) { - // prepare for median correction - medianFilter->medianHead = medianFilter->medianHead->prevValue; - } - - // replace age head with new sample - newNode = medianFilter->ageHead; - newNode->value = sample; - - // remove age head from list - medianFilter->ageHead->nextValue->prevValue = - medianFilter->ageHead->prevValue; - medianFilter->ageHead->prevValue->nextValue = - medianFilter->ageHead->nextValue; - // increment age head - medianFilter->ageHead = medianFilter->ageHead->nextAge; - - // find new node position - it = medianFilter->valueHead; // set iterator as value head - for (i = 0; i < medianFilter->numNodes - 1; i++) { - if (sample < it->value) { - if (i == 0) { // replace value head if new node is the smallest - medianFilter->valueHead = newNode; - } - break; - } - it = it->nextValue; - } - - // insert new node in list - it->prevValue->nextValue = newNode; - newNode->prevValue = it->prevValue; - it->prevValue = newNode; - newNode->nextValue = it; - - // adjust median node - if (i >= (medianFilter->numNodes / 2)) { - medianFilter->medianHead = medianFilter->medianHead->nextValue; - } - - return medianFilter->medianHead->value; - } -}; - -} // namespace audio_tools \ No newline at end of file diff --git a/src/AudioTools/CoreAudio/AudioHttp.h b/src/AudioTools/CoreAudio/AudioHttp.h deleted file mode 100644 index 570f61d65d..0000000000 --- a/src/AudioTools/CoreAudio/AudioHttp.h +++ /dev/null @@ -1,3 +0,0 @@ -#pragma once - -#include "AudioTools/CoreAudio/AudioHttp/AudioHttp.h" diff --git a/src/AudioTools/CoreAudio/AudioHttp/AbstractURLStream.h b/src/AudioTools/CoreAudio/AudioHttp/AbstractURLStream.h deleted file mode 100644 index a1ebf59b4f..0000000000 --- a/src/AudioTools/CoreAudio/AudioHttp/AbstractURLStream.h +++ /dev/null @@ -1,78 +0,0 @@ -#pragma once - -#include "AudioTools/CoreAudio/BaseStream.h" -#include "AudioTools/CoreAudio/AudioMetaData/AbstractMetaData.h" // for MetaDataType -#include "HttpTypes.h" -#include "HttpRequest.h" -#include "AudioClient.h" - -namespace audio_tools { - -/** - * @brief Abstract Base class for all URLStream implementations - * @author Phil Schatzmann - * @ingroup http - * @copyright GPLv3 - */ -class AbstractURLStream : public AudioStream { - public: - // executes the URL request - virtual bool begin(const char* urlStr, const char* acceptMime = nullptr, - MethodID action = GET, const char* reqMime = "", - const char* reqData = "") = 0; - // ends the request - virtual void end() override = 0; - - /// Adds/Updates a request header - virtual void addRequestHeader(const char* header, const char* value) = 0; - - /// Provides reply header information - virtual const char* getReplyHeader(const char* header) = 0; - - // only the ICYStream supports this - virtual bool setMetadataCallback(void (*fn)(MetaDataType info, - const char* str, int len)) { - return false; - } - /// Writes are not supported - int availableForWrite() override { return 0; } - - /// Sets the ssid that will be used for logging in (when calling begin) - virtual void setSSID(const char* ssid) = 0; - - /// Sets the password that will be used for logging in (when calling begin) - virtual void setPassword(const char* password) = 0; - - /// if set to true, it activates the power save mode which comes at the cost - /// of performance! - By default this is deactivated. ESP32 Only! - virtual void setPowerSave(bool ps) = 0; - - /// Define the Root PEM Certificate for SSL - virtual void setCACert(const char* cert) = 0; - - /// provides access to the HttpRequest - virtual HttpRequest& httpRequest() = 0; - - /// (Re-)defines the client - virtual void setClient(Client& clientPar) = 0; - - /// Add Connection: close to request header - virtual void setConnectionClose(bool flag) = 0; - - /// Provides the url as string - virtual const char* urlStr() = 0; - - /// Total amout of data that was consumed so far - virtual size_t totalRead() = 0; - - /// Provides the reported data size from the http reply - virtual int contentLength() = 0; - - /// Waits the indicated time for the data to be available - /// waits for some data - returns false if the request has failed - virtual bool waitForData(int timeout) = 0; - -}; - - -} // namespace audio_tools diff --git a/src/AudioTools/CoreAudio/AudioHttp/AudioClient.h b/src/AudioTools/CoreAudio/AudioHttp/AudioClient.h deleted file mode 100644 index befd07e7cf..0000000000 --- a/src/AudioTools/CoreAudio/AudioHttp/AudioClient.h +++ /dev/null @@ -1,8 +0,0 @@ -#pragma once -#if defined(ARDUINO) || defined(IS_DESKTOP) -# include "Client.h" -#else -// e.g. IDF does not know about the Arduino Client -# include "AudioTools/AudioLibs/Desktop/NoArduino.h" -#endif - diff --git a/src/AudioTools/CoreAudio/AudioHttp/AudioHttp.h b/src/AudioTools/CoreAudio/AudioHttp/AudioHttp.h deleted file mode 100644 index 9d4a0c3e84..0000000000 --- a/src/AudioTools/CoreAudio/AudioHttp/AudioHttp.h +++ /dev/null @@ -1,13 +0,0 @@ -#pragma once -/** - * @defgroup http Http - * @ingroup communications - * @brief Http client & server -**/ - -#include "URLStream.h" -#include "AudioServer.h" - -#if ((defined(ESP32) && defined(USE_URL_ARDUINO)) || defined(ESP32_CMAKE)) -# include "URLStreamESP32.h" -#endif \ No newline at end of file diff --git a/src/AudioTools/CoreAudio/AudioHttp/AudioServer.h b/src/AudioTools/CoreAudio/AudioHttp/AudioServer.h deleted file mode 100644 index e24b162c44..0000000000 --- a/src/AudioTools/CoreAudio/AudioHttp/AudioServer.h +++ /dev/null @@ -1,501 +0,0 @@ -#pragma once - -#include "AudioToolsConfig.h" -#if defined(USE_AUDIO_SERVER) && (defined(USE_ETHERNET) || defined(USE_WIFI)) - -#ifdef USE_WIFI -# ifdef ESP8266 -# include -# else -# include -# endif -#endif - -#ifdef USE_ETHERNET -# include -#endif - -#include "AudioTools/AudioCodecs/CodecWAV.h" -#include "AudioTools.h" - -namespace audio_tools { - -/// Calback which writes the sound data to the stream -typedef void (*AudioServerDataCallback)(Print *out); - -/** - * @brief A simple Arduino Webserver which streams the result - * This class is based on the WiFiServer class. All you need to do is to provide - * the data with a callback method or from an Arduino Stream: in -copy> client - * - * @ingroup http - * @author Phil Schatzmann - * @copyright GPLv3 - */ -template -class AudioServerT { - public: - /** - * @brief Construct a new Audio Server object - * We assume that the WiFi is already connected - */ - AudioServerT(int port = 80) { - // the client returns 0 for avialableForWrite() - copier.setCheckAvailableForWrite(false); - setupServer(port); - } - - /** - * @brief Construct a new Audio WAV Server object - * - * @param network - * @param password - */ - AudioServerT(const char *network, const char *password, int port = 80) { - this->network = (char *)network; - this->password = (char *)password; - // the client returns 0 for avialableForWrite() - copier.setCheckAvailableForWrite(false); - setupServer(port); - } - - /** - * @brief Start the server. You need to be connected to WiFI before calling - * this method - * - * @param in - * @param contentType Mime Type of result - */ - bool begin(Stream &in, const char *contentType) { - TRACED(); - this->in = ∈ - this->content_type = contentType; - -#ifdef USE_WIFI - connectWiFi(); -#endif - // start server - server.begin(); - return true; - } - - /** - * @brief Start the server. The data must be provided by a callback method - * - * @param cb - * @param contentType Mime Type of result - */ - bool begin(AudioServerDataCallback cb, const char *contentType) { - TRACED(); - this->in = nullptr; - this->callback = cb; - this->content_type = contentType; - -#ifdef USE_WIFI - connectWiFi(); -#endif - - // start server - server.begin(); - return true; - } - - /** - * @brief Add this method to your loop - * Returns true while the client is connected. (The same functionality like - * doLoop()) - * - * @return true - * @return false - */ - bool copy() { return doLoop(); } - - /** - * @brief Add this method to your loop - * Returns true while the client is connected. - */ - bool doLoop() { - // LOGD("doLoop"); - bool active = true; - if (!client_obj.connected()) { -#if USE_SERVER_ACCEPT - client_obj = server.accept(); // listen for incoming clients -#else - client_obj = server.available(); // listen for incoming clients -#endif - processClient(); - } else { - // We are connected: copy input from source to wav output - if (client_obj) { - if (callback == nullptr) { - LOGD("copy data..."); - if (converter_ptr == nullptr) { - copier.copy(); - } else { - copier.copy(*converter_ptr); - } - // if we limit the size of the WAV the encoder gets automatically - // closed when all has been sent - if (!client_obj) { - LOGI("stop client..."); - client_obj.stop(); - active = false; - } - } - } else { - LOGI("client was not connected"); - } - } - return active; - } - - /// defines a converter that will be used when the audio is rendered - void setConverter(BaseConverter *c) { converter_ptr = c; } - - /// Provides the output stream - Stream &out() { return client_obj; } - - /// Provides a pointer to the WiFiClient - Client *out_ptr() { return &client_obj; } - - /// Checks if any clinent has connnected - bool isClientConnected() { return client_obj.connected(); } - - /// Changes the copy buffer size - void setCopyBufferSize(int size){ - copier.resize(size); - } - - protected: - // WIFI -#ifdef ESP32 - Server server; -#else - Server server{80}; -#endif - Client client_obj; - char *password = nullptr; - char *network = nullptr; - - // Content - const char *content_type = nullptr; - AudioServerDataCallback callback = nullptr; - Stream *in = nullptr; - StreamCopy copier; - BaseConverter *converter_ptr = nullptr; - - void setupServer(int port) { - Server tmp(port); - server = tmp; - } - -#ifdef USE_WIFI - void connectWiFi() { - TRACED(); - if (WiFi.status() != WL_CONNECTED && network != nullptr && - password != nullptr) { - WiFi.begin(network, password); - while (WiFi.status() != WL_CONNECTED) { - Serial.print("."); - delay(500); - } -#ifdef ESP32 - WiFi.setSleep(false); -#endif - Serial.println(); - } - Serial.print("IP address: "); - Serial.println(WiFi.localIP()); - } -#endif - - virtual void sendReplyHeader() { - TRACED(); - // HTTP headers always start with a response code (e.g. HTTP/1.1 200 OK) - // and a content-type so the client knows what's coming, then a blank line: - client_obj.println("HTTP/1.1 200 OK"); - LOGI("Reply: HTTP/1.1 200 OK"); - if (content_type != nullptr) { - client_obj.print("Content-type:"); - client_obj.println(content_type); - LOGI("Content-type: %s", content_type); - } - client_obj.println(); - if (!client_obj.connected()){ - LOGE("connection was closed"); - } - } - - virtual void sendReplyContent() { - TRACED(); - if (callback != nullptr) { - // provide data via Callback - LOGI("sendReply - calling callback"); - callback(&client_obj); - client_obj.stop(); - } else if (in != nullptr) { - // provide data for stream - LOGI("sendReply - Returning audio stream..."); - copier.begin(client_obj, *in); - if (!client_obj.connected()){ - LOGE("connection was closed"); - } - } - } - - // Handle an new client connection and return the data - void processClient() { - // LOGD("processClient"); - if (client_obj) { // if you get a client, - LOGI("New Client:"); // print a message out the serial port - String currentLine = - ""; // make a String to hold incoming data from the client - while (client_obj.connected()) { // loop while the client's connected - if (client_obj - .available()) { // if there's bytes to read from the client, - char c = client_obj.read(); // read a byte, then - if (c == '\n') { // if the byte is a newline character - LOGI("Request: %s", currentLine.c_str()); - // if the current line is blank, you got two newline characters in a - // row. that's the end of the client HTTP request, so send a - // response: - if (currentLine.length() == 0) { - sendReplyHeader(); - sendReplyContent(); - // break out of the while loop: - break; - } else { // if you got a newline, then clear currentLine: - currentLine = ""; - } - } else if (c != '\r') { // if you got anything else but a carriage - // return character, - currentLine += c; // add it to the end of the currentLine - } - } - } - } - } -}; - -#ifdef USE_WIFI -using AudioServer = AudioServerT; -using AudioServerWiFi = AudioServerT; -#endif - -#ifdef USE_ETHERNET -using AudioServer = AudioServerT; -using AudioServerEthernet = AudioServerT; -#endif - -/** - * @brief A simple Arduino Webserver which streams the audio using the indicated - * encoder.. This class is based on the WiFiServer class. All you need to do is - * to provide the data with a callback method or from a Stream. - * - * @ingroup http - * @author Phil Schatzmann - * @copyright GPLv3 - */ -class AudioEncoderServer : public AudioServer { - public: - /** - * @brief Construct a new Audio Server object that supports an AudioEncoder - * We assume that the WiFi is already connected - */ - AudioEncoderServer(AudioEncoder *encoder, int port = 80) : AudioServer(port) { - this->encoder = encoder; - } - - /** - * @brief Construct a new Audio Server object - * - * @param network - * @param password - */ - AudioEncoderServer(AudioEncoder *encoder, const char *network, - const char *password, int port = 80) - : AudioServer(network, password, port) { - this->encoder = encoder; - } - - /** - * @brief Destructor release the memory - **/ - ~AudioEncoderServer() {} - - /** - * @brief Start the server. You need to be connected to WiFI before calling - * this method - * - * @param in - * @param sample_rate - * @param channels - */ - bool begin(Stream &in, int sample_rate, int channels, - int bits_per_sample = 16, BaseConverter *converter = nullptr) { - TRACED(); - this->in = ∈ - setConverter(converter); - audio_info.sample_rate = sample_rate; - audio_info.channels = channels; - audio_info.bits_per_sample = bits_per_sample; - encoder->setAudioInfo(audio_info); - // encoded_stream.begin(&client_obj, encoder); - encoded_stream.setOutput(&client_obj); - encoded_stream.setEncoder(encoder); - encoded_stream.begin(audio_info); - return AudioServer::begin(in, encoder->mime()); - } - - /** - * @brief Start the server. You need to be connected to WiFI before calling - * this method - * - * @param in - * @param info - * @param converter - */ - bool begin(Stream &in, AudioInfo info, BaseConverter *converter = nullptr) { - TRACED(); - this->in = ∈ - this->audio_info = info; - setConverter(converter); - encoder->setAudioInfo(audio_info); - encoded_stream.setOutput(&client_obj); - encoded_stream.setEncoder(encoder); - if (!encoded_stream.begin(audio_info)){ - LOGE("encoder begin failed"); - stop(); - } - - return AudioServer::begin(in, encoder->mime()); - } - - /** - * @brief Start the server. You need to be connected to WiFI before calling - * this method - * - * @param in - * @param converter - */ - bool begin(AudioStream &in, BaseConverter *converter = nullptr) { - TRACED(); - this->in = ∈ - this->audio_info = in.audioInfo(); - setConverter(converter); - encoder->setAudioInfo(audio_info); - encoded_stream.setOutput(&client_obj); - encoded_stream.setEncoder(encoder); - encoded_stream.begin(audio_info); - - return AudioServer::begin(in, encoder->mime()); - } - - /** - * @brief Start the server. The data must be provided by a callback method - * - * @param cb - * @param sample_rate - * @param channels - */ - bool begin(AudioServerDataCallback cb, int sample_rate, int channels, - int bits_per_sample = 16) { - TRACED(); - audio_info.sample_rate = sample_rate; - audio_info.channels = channels; - audio_info.bits_per_sample = bits_per_sample; - encoder->setAudioInfo(audio_info); - - return AudioServer::begin(cb, encoder->mime()); - } - - // provides a pointer to the encoder - AudioEncoder *audioEncoder() { return encoder; } - - protected: - // Sound Generation - use EncodedAudioOutput with is more efficient then EncodedAudioStream - EncodedAudioOutput encoded_stream; - AudioInfo audio_info; - AudioEncoder *encoder = nullptr; - - // moved to be part of reply content to avoid timeout issues in Chrome - void sendReplyHeader() override {} - - void sendReplyContent() override { - TRACED(); - // restart encoder - if (encoder) { - encoder->end(); - encoder->begin(); - } - - if (callback != nullptr) { - // encoded_stream.begin(out_ptr(), encoder); - encoded_stream.setOutput(out_ptr()); - encoded_stream.setEncoder(encoder); - encoded_stream.begin(); - - // provide data via Callback to encoded_stream - LOGI("sendReply - calling callback"); - // Send delayed header - AudioServer::sendReplyHeader(); - callback(&encoded_stream); - client_obj.stop(); - } else if (in != nullptr) { - // provide data for stream: in -copy> encoded_stream -> out - LOGI("sendReply - Returning encoded stream..."); - // encoded_stream.begin(out_ptr(), encoder); - encoded_stream.setOutput(out_ptr()); - encoded_stream.setEncoder(encoder); - encoded_stream.begin(); - - copier.begin(encoded_stream, *in); - if (!client_obj.connected()){ - LOGE("connection was closed"); - } - // Send delayed header - AudioServer::sendReplyHeader(); - } - } -}; - -/** - * @brief A simple Arduino Webserver which streams the audio as WAV data. - * This class is based on the AudioEncodedServer class. All you need to do is to - * provide the data with a callback method or from a Stream. - * @ingroup http - * @author Phil Schatzmann - * @copyright GPLv3 - */ -class AudioWAVServer : public AudioEncoderServer { - public: - /** - * @brief Construct a new Audio WAV Server object - * We assume that the WiFi is already connected - */ - AudioWAVServer(int port = 80) : AudioEncoderServer(new WAVEncoder(), port) {} - - /** - * @brief Construct a new Audio WAV Server object - * - * @param network - * @param password - */ - AudioWAVServer(const char *network, const char *password, int port = 80) - : AudioEncoderServer(new WAVEncoder(), network, password, port) {} - - /// Destructor: release the allocated encoder - ~AudioWAVServer() { - AudioEncoder *encoder = audioEncoder(); - if (encoder != nullptr) { - delete encoder; - } - } - - // provides a pointer to the encoder - WAVEncoder &wavEncoder() { return *static_cast(encoder); } -}; - -} // namespace audio_tools - -#endif diff --git a/src/AudioTools/CoreAudio/AudioHttp/HttpChunkReader.h b/src/AudioTools/CoreAudio/AudioHttp/HttpChunkReader.h deleted file mode 100644 index 32348d4445..0000000000 --- a/src/AudioTools/CoreAudio/AudioHttp/HttpChunkReader.h +++ /dev/null @@ -1,124 +0,0 @@ -#pragma once - -#include "HttpHeader.h" -#include "HttpLineReader.h" - -namespace audio_tools { - -/** - * @brief Http might reply with chunks. So we need to dechunk the data. - * see https://en.wikipedia.org/wiki/Chunked_transfer_encoding - * @author Phil Schatzmann - * @copyright GPLv3 - */ -class HttpChunkReader : public HttpLineReader { - public: - /// default constructor - HttpChunkReader() { - open_chunk_len = 0; - has_ended = false; - } - - /// constructor for processing final header information - HttpChunkReader(HttpReplyHeader &header) { - http_heaer_ptr = &header; - open_chunk_len = 0; - has_ended = false; - } - - void open(Client &client) { - LOGD("HttpChunkReader: %s", "open"); - has_ended = false; - readChunkLen(client); - } - - // reads a block of data from the chunks - virtual int read(Client &client, uint8_t *str, int len) { - LOGD("HttpChunkReader: %s", "read"); - if (has_ended && open_chunk_len == 0) return 0; - - // read the chunk data - but not more then available - int read_max = len < open_chunk_len ? len : open_chunk_len; - int len_processed = client.read(str, read_max); - // update current unprocessed chunk - open_chunk_len -= len_processed; - - // remove traling CR LF from data - if (open_chunk_len <= 0) { - removeCRLF(client); - readChunkLen(client); - } - - return len_processed; - } - - // reads a single line from the chunks - virtual int readln(Client &client, uint8_t *str, int len, - bool incl_nl = true) { - LOGD("HttpChunkReader: %s", "readln"); - if (has_ended && open_chunk_len == 0) return 0; - - int read_max = len < open_chunk_len ? len : open_chunk_len; - int len_processed = readlnInternal(client, str, read_max, incl_nl); - open_chunk_len -= len_processed; - - // the chunks are terminated by a final CRLF - if (open_chunk_len <= 0) { - removeCRLF(client); - readChunkLen(client); - } - - return len_processed; - } - - int available() { - int result = has_ended ? 0 : open_chunk_len; - char msg[50]; - snprintf(msg, 50, "available=>%d", result); - LOGD("HttpChunkReader: %s", msg); - - return result; - } - - protected: - int open_chunk_len; - bool has_ended = false; - HttpReplyHeader *http_heaer_ptr; - - void removeCRLF(Client &client) { - LOGD("HttpChunkReader: %s", "removeCRLF"); - // remove traling CR LF from data - if (client.peek() == '\r') { - LOGD("HttpChunkReader: %s", "removeCR"); - client.read(); - } - if (client.peek() == '\n') { - LOGD("HttpChunkReader: %s", "removeLF"); - client.read(); - } - } - - // we read the chunk length which is indicated as hex value - virtual void readChunkLen(Client &client) { - LOGD("HttpChunkReader::readChunkLen"); - uint8_t len_str[HTTP_CHUNKED_SIZE_MAX_LEN+1] = {0}; - readlnInternal(client, len_str, HTTP_CHUNKED_SIZE_MAX_LEN, false); - LOGD("HttpChunkReader::readChunkLen %s", (const char *)len_str); - open_chunk_len = strtol((char *)len_str, nullptr, 16); - - char msg[80] = {0}; - snprintf(msg, 80, "chunk_len: %d", open_chunk_len); - LOGD("HttpChunkReader::readChunkLen->%s", msg); - - if (open_chunk_len == 0) { - has_ended = true; - LOGD("HttpChunkReader::readChunkLen %s", "last chunk received"); - // processing of additinal final headers after the chunk end - if (http_heaer_ptr != nullptr) { - http_heaer_ptr->readExt(client); - } - } - } -}; - -} // namespace audio_tools diff --git a/src/AudioTools/CoreAudio/AudioHttp/HttpHeader.h b/src/AudioTools/CoreAudio/AudioHttp/HttpHeader.h deleted file mode 100644 index d51c282d1d..0000000000 --- a/src/AudioTools/CoreAudio/AudioHttp/HttpHeader.h +++ /dev/null @@ -1,506 +0,0 @@ -#pragma once - -#include "AudioTools/CoreAudio/AudioBasic/Collections.h" -#include "AudioTools/CoreAudio/AudioBasic/Str.h" -#include "AudioToolsConfig.h" -#include "AudioClient.h" // for Client -#include "HttpLineReader.h" -#include "HttpTypes.h" -#include "Url.h" - -namespace audio_tools { - -// Class Configuration - -// Define relevant header content -static const char* CONTENT_TYPE = "Content-Type"; -static const char* CONTENT_LENGTH = "Content-Length"; -static const char* CONNECTION = "Connection"; -static const char* CON_CLOSE = "close"; -static const char* CON_KEEP_ALIVE = "keep-alive"; -static const char* TRANSFER_ENCODING = "Transfer-Encoding"; -static const char* CHUNKED = "chunked"; -static const char* ACCEPT = "Accept"; -static const char* ACCEPT_ALL = "*/*"; -static const char* SUCCESS = "Success"; -static const char* USER_AGENT = "User-Agent"; -static const char* DEFAULT_AGENT = - "Mozilla/5.0 (compatible; Googlebot/2.1; +http://www.google.com/bot.html)"; -static const char* HOST_C = "Host"; -static const char* ACCEPT_ENCODING = "Accept-Encoding"; -static const char* IDENTITY = "identity"; -static const char* LOCATION = "Location"; - -// Http methods -static const char* methods[] = {"?", "GET", "HEAD", "POST", - "PUT", "DELETE", "TRACE", "OPTIONS", - "CONNECT", "PATCH", nullptr}; - -/** - * @brief A individual key - value header line - * - */ -struct HttpHeaderLine { - Str key; - Str value; - bool active = true; - HttpHeaderLine(const char* k) { key = k; } -}; - -/** - * @brief In a http request and reply we need to process header information. - * With this API we can define and query the header information. The individual - * header lines are stored in a vector. This is the common functionality for the - * HttpRequest and HttpReplyHeader subclasses - * @author Phil Schatzmann - * @copyright GPLv3 - * - */ -class HttpHeader { - public: - HttpHeader() { - LOGD("HttpHeader"); - // set default values - protocol_str = "HTTP/1.1"; - url_path = "/"; - status_msg = ""; - } - ~HttpHeader() { - LOGD("~HttpHeader"); - clear(); - } - - /// clears the data - HttpHeader& clear() { - is_written = false; - is_chunked = false; - url_path = "/"; - // delete HttpHeaderLine objects - for (auto& ptr : lines){ - delete ptr; - } - lines.clear(); - return *this; - } - - HttpHeader& put(const char* key, const char* value) { - if (value != nullptr && strlen(value) > 0) { - LOGD("HttpHeader::put %s %s", key, value); - HttpHeaderLine* hl = headerLine(key); - if (hl == nullptr) { - if (create_new_lines){ - LOGE("HttpHeader::put - did not add HttpHeaderLine for %s", key); - } - return *this; - } - - // log entry - LOGD("HttpHeader::put -> '%s' : '%s'", key, value); - - hl->value = value; - hl->active = true; - - // determine if chunked - if (StrView(key).equalsIgnoreCase(TRANSFER_ENCODING) && StrView(value) == CHUNKED) { - LOGI("- is_chunked!!!"); - this->is_chunked = true; - } - // log content type - if (StrView(key).equalsIgnoreCase(CONTENT_TYPE)) { - LOGI("%s: %s", CONTENT_TYPE, value); - } - } else { - LOGD("HttpHeader::put - value ignored because it is null for %s", key); - } - return *this; - } - - /// adds a new line to the header - e.g. for content size - HttpHeader& put(const char* key, int value) { - LOGD("HttpHeader::put %s %d", key, value); - HttpHeaderLine* hl = headerLine(key); - - if (value > 1000) { - LOGW("value is > 1000"); - } - - // add value - hl->value = value; - hl->active = true; - LOGI("%s %s", key, hl->value.c_str()); - return *this; - } - - /// adds a received new line to the header - HttpHeader& put(const char* line) { - LOGD("HttpHeader::put -> %s", (const char*)line); - StrView keyStr(line); - int pos = keyStr.indexOf(":"); - char* key = (char*)line; - key[pos] = 0; - - // usually there is a leading space - but unfurtunately not always - const char* value = line + pos + 1; - if (value[0] == ' ') { - value = line + pos + 2; - } - return put((const char*)key, value); - } - - // determines a header value with the key - const char* get(const char* key) { - for (auto& line_ptr : lines) { - StrView trimmed{line_ptr->key.c_str()}; - trimmed.trim(); - line_ptr->key = trimmed.c_str(); - //line->key.rtrim(); - if (StrView(line_ptr->key.c_str()).equalsIgnoreCase(key)) { - const char* result = line_ptr->value.c_str(); - return line_ptr->active ? result : nullptr; - } - } - return nullptr; - } - - // reads a single header line - int readLine(Client& in, char* str, int len) { - int result = reader.readlnInternal(in, (uint8_t*)str, len, false); - LOGD("HttpHeader::readLine -> %s", str); - return result; - } - - // writes a lingle header line - void writeHeaderLine(Client& out, HttpHeaderLine& header) { - LOGD("HttpHeader::writeHeaderLine: %s", header.key.c_str()); - if (!header.active) { - LOGD("HttpHeader::writeHeaderLine - not active"); - return; - } - if (header.value.c_str() == nullptr) { - LOGD("HttpHeader::writeHeaderLine - ignored because value is null"); - return; - } - - char* msg = tempBuffer(); - StrView msg_str(msg, HTTP_MAX_LEN); - msg_str = header.key.c_str(); - msg_str += ": "; - msg_str += header.value.c_str(); - msg_str += CRLF; - out.print(msg_str.c_str()); - - // remove crlf from log - - int len = strlen(msg); - msg[len - 2] = 0; - LOGI(" -> %s ", msg); - - // mark as processed - // header->active = false; - } - - const char* urlPath() { return url_path.c_str(); } - - MethodID method() { return method_id; } - - int statusCode() { return status_code; } - - const char* statusMessage() { return status_msg.c_str(); } - - bool isChunked() { - // the value is automatically set from the reply - return is_chunked; - } - - /// reads the full header from the request (stream) - bool read(Client& in) { - LOGD("HttpHeader::read"); - // remove all existing value - clear(); - - char* line = tempBuffer(); - if (in.connected()) { - if (in.available() == 0) { - int count = 0; - uint32_t timeout = millis() + timeout_ms; - while (in.available() == 0) { - delay(50); - count++; - if (count == 2) { - LOGI("Waiting for data..."); - } - // If we dont get an answer, we abort - if (millis() > timeout) { - LOGE("Request timed out after %d ms", (int)timeout_ms); - status_code = 401; - return false; - } - } - LOGI("Data available: %d", in.available()); - } - - readLine(in, line, HTTP_MAX_LEN); - parse1stLine(line); - while (true) { - int len = readLine(in, line, HTTP_MAX_LEN); - if (len == 0 && in.available() == 0) break; - if (isValidStatus() || isRedirectStatus()) { - StrView lineStr(line); - lineStr.ltrim(); - if (lineStr.isEmpty()) { - break; - } - put(line); - } - } - } - return true; - } - - /// writes the full header to the indicated HttpStreamedMultiOutput stream - void write(Client& out) { - LOGI("HttpHeader::write"); - write1stLine(out); - for (auto& line_ptr : lines) { - writeHeaderLine(out, *line_ptr); - } - // print empty line - crlf(out); - out.flush(); - is_written = true; - } - - void setProcessed() { - for (auto& line :lines) { - line->active = false; - } - } - - /// automatically create new lines - void setAutoCreateLines(bool is_auto_line) { - create_new_lines = is_auto_line; - } - - /// returns true if status code >=200 and < 300 - bool isValidStatus() { return status_code >= 200 && status_code < 300; } - - /// returns true if the status code is >=300 and < 400 - bool isRedirectStatus() { return status_code >= 300 && status_code < 400; } - - /// release static temp buffer - void end() { temp_buffer.resize(0); } - - /// Set the timout - void setTimeout(int timeoutMs) { timeout_ms = timeoutMs; } - - /// Provide the protocol - const char* protocol() { return protocol_str.c_str(); } - - /// Defines the protocol: e.g. HTTP/1.1 - void setProtocol(const char* protocal) { protocol_str = protocal; } - - /// Resizes the internal read buffer - void resize(int bufferSize){ - temp_buffer.resize(bufferSize); - } - - /// Provides the http parameter lines - List &getHeaderLines(){ - return lines; - } - - protected: - int status_code = UNDEFINED; - bool is_written = false; - bool is_chunked = false; - bool create_new_lines = true; - MethodID method_id; - // we store the values on the heap. this is acceptable because we just have - // one instance for the requests and one for the replys: which needs about - // 2*100 bytes - Str protocol_str{10}; - Str url_path{70}; - Str status_msg{20}; - List lines; - HttpLineReader reader; - const char* CRLF = "\r\n"; - int timeout_ms = URL_CLIENT_TIMEOUT; - Vector temp_buffer{HTTP_MAX_LEN}; - - char* tempBuffer() { - temp_buffer.clear(); - return temp_buffer.data(); - } - - // the headers need to delimited with CR LF - void crlf(Client& out) { - out.print(CRLF); - LOGI(" -> %s ", ""); - } - - // gets or creates a header line by key - HttpHeaderLine* headerLine(const char* key) { - if (key != nullptr) { - for (auto& line_ptr : lines) { - if (line_ptr->key.c_str() != nullptr) { - if (line_ptr->key.equalsIgnoreCase(key)) { - line_ptr->active = true; - return line_ptr; - } - } - } - if (create_new_lines || StrView(key).equalsIgnoreCase(CONTENT_LENGTH) || - StrView(key).equalsIgnoreCase(CONTENT_TYPE)) { - HttpHeaderLine *new_line = new HttpHeaderLine(key); - lines.push_back(new_line); - return new_line; - } - } else { - LOGI("HttpHeader::headerLine %s", "The key must not be null"); - } - return nullptr; - } - - MethodID getMethod(const char* line) { - // set action - for (int j = 0; methods[j] != nullptr; j++) { - const char* action = methods[j]; - int len = strlen(action); - if (strncmp(action, line, len) == 0) { - return (MethodID)j; - } - } - return (MethodID)0; - } - - virtual void write1stLine(Client& out) = 0; - virtual void parse1stLine(const char* line) = 0; -}; - -/** - * @brief Reading and writing of Http Requests - * @author Phil Schatzmann - * @copyright GPLv3 - */ -class HttpRequestHeader : public HttpHeader { - public: - // Defines the action id, url path and http version for an request - HttpHeader& setValues(MethodID id, const char* urlPath, - const char* protocol = nullptr) { - this->method_id = id; - this->url_path = urlPath; - - LOGD("HttpRequestHeader::setValues - path: %s", this->url_path.c_str()); - if (protocol != nullptr) { - this->protocol_str = protocol; - } - return *this; - } - - // action path protocol - void write1stLine(Client& out) { - LOGD("HttpRequestHeader::write1stLine"); - char* msg = tempBuffer(); - StrView msg_str(msg, HTTP_MAX_LEN); - - const char* method_str = methods[this->method_id]; - msg_str = method_str; - msg_str += " "; - msg_str += this->url_path.c_str(); - msg_str += " "; - msg_str += this->protocol_str.c_str(); - msg_str += CRLF; - out.print(msg); - - int len = strlen(msg); - msg[len - 2] = 0; - LOGI("-> %s", msg); - } - - // parses the requestline - // Request-Line = Method SP Request-URI SP HTTP-Version CRLF - void parse1stLine(const char* line) { - LOGD("HttpRequestHeader::parse1stLine %s", line); - StrView line_str(line); - int space1 = line_str.indexOf(" "); - int space2 = line_str.indexOf(" ", space1 + 1); - - this->method_id = getMethod(line); - this->protocol_str.substring(line_str, space2 + 1, line_str.length()); - this->url_path.substring(line_str, space1 + 1, space2); - this->url_path.trim(); - - LOGD("->method %s", methods[this->method_id]); - LOGD("->protocol %s", protocol_str.c_str()); - LOGD("->url_path %s", url_path.c_str()); - } -}; - -/** - * @brief Reading and Writing of Http Replys - * @author Phil Schatzmann - * @copyright GPLv3 - */ -class HttpReplyHeader : public HttpHeader { - public: - // defines the values for the rely - void setValues(int statusCode, const char* msg = "", - const char* protocol = nullptr) { - LOGI("HttpReplyHeader::setValues"); - status_msg = msg; - status_code = statusCode; - if (protocol != nullptr) { - this->protocol_str = protocol; - } - } - - // reads the final chunked reply headers - void readExt(Client& in) { - LOGI("HttpReplyHeader::readExt"); - char* line = tempBuffer(); - readLine(in, line, HTTP_MAX_LEN); - while (strlen(line) != 0) { - put(line); - readLine(in, line, HTTP_MAX_LEN); - } - } - - // HTTP-Version SP Status-Code SP Reason-Phrase CRLF - void write1stLine(Client& out) { - LOGI("HttpReplyHeader::write1stLine"); - char* msg = tempBuffer(); - StrView msg_str(msg, HTTP_MAX_LEN); - msg_str = this->protocol_str.c_str(); - msg_str += " "; - msg_str += this->status_code; - msg_str += " "; - msg_str += this->status_msg.c_str(); - LOGI("-> %s", msg); - out.print(msg); - crlf(out); - } - - // HTTP-Version SP Status-Code SP Reason-Phrase CRLF - // we just update the pointers to point to the correct position in the - // http_status_line - void parse1stLine(const char* line) { - LOGD("HttpReplyHeader::parse1stLine: %s", line); - StrView line_str(line); - int space1 = line_str.indexOf(' ', 0); - int space2 = line_str.indexOf(' ', space1 + 1); - - // save http version - protocol_str.substring(line_str, 0, space1); - - // find response status code after the first space - char status_c[6]; - StrView status(status_c, 6); - status.substring(line_str, space1 + 1, space2); - status_code = atoi(status_c); - - // get reason-phrase after last SP - status_msg.substring(line_str, space2 + 1, line_str.length()); - } -}; - -} // namespace audio_tools \ No newline at end of file diff --git a/src/AudioTools/CoreAudio/AudioHttp/HttpLineReader.h b/src/AudioTools/CoreAudio/AudioHttp/HttpLineReader.h deleted file mode 100644 index f7afa90b23..0000000000 --- a/src/AudioTools/CoreAudio/AudioHttp/HttpLineReader.h +++ /dev/null @@ -1,82 +0,0 @@ -#pragma once - -#include "AudioLogger.h" - -namespace audio_tools { - -/** - * @brief We read a single line. A terminating 0 is added to the string to make - * it compliant for c string functions. - * @author Phil Schatzmann - * @copyright GPLv3 - */ - -class HttpLineReader { - public: - HttpLineReader() {} - // reads up the the next CR LF - but never more then the indicated len. - // returns the number of characters read including crlf - virtual int readlnInternal(Stream& client, uint8_t* str, int len, - bool incl_nl = true) { - int result = 0; - LOGD("HttpLineReader %s", "readlnInternal"); - // wait for first character - for (int w = 0; w < 200 && client.available() == 0; w++) { - delay(10); - } - // if we do not have any data we stop - if (client.available() == 0) { - LOGW("HttpLineReader %s", "readlnInternal->no Data"); - str[0] = 0; - return 0; - } - - // process characters until we find a new line - bool is_buffer_overflow = false; - int j = 0; - while (true) { - int c = client.read(); - if (c == -1) { - break; - } - - if (j < len) { - result++; - } else { - is_buffer_overflow = true; - } - - if (c == '\n') { - if (incl_nl) { - str[j] = c; - break; - } else { - // remove cr lf - if (j >= 1) { - if (str[j - 1] == '\r') { - // remove cr - str[j - 1] = 0; - break; - } else { - // remove nl - str[j] = 0; - break; - } - } - } - } - if (!is_buffer_overflow) { - str[j] = c; - } - j++; - } - str[result - 1] = 0; - if (is_buffer_overflow) { - LOGE("Line cut off: %s", str); - } - - return result; - } -}; - -} // namespace audio_tools diff --git a/src/AudioTools/CoreAudio/AudioHttp/HttpRequest.h b/src/AudioTools/CoreAudio/AudioHttp/HttpRequest.h deleted file mode 100644 index 0374336dca..0000000000 --- a/src/AudioTools/CoreAudio/AudioHttp/HttpRequest.h +++ /dev/null @@ -1,373 +0,0 @@ -#pragma once - -#include "AudioToolsConfig.h" -#include "HttpChunkReader.h" -#include "HttpHeader.h" -#include "HttpTypes.h" -#include "Url.h" -#include "AudioLogger.h" - -#define CHUNK_SIZE 1024 - -namespace audio_tools { - -/** - * @brief Simple API to process get, put, post, del http requests - * I tried to use Arduino HttpClient, but I did not manage to extract the mime - * type from streaming get requests. - * - * The functionality is based on the Arduino Client class. - * @ingroup http - * @author Phil Schatzmann - * @copyright GPLv3 - */ - -class HttpRequest : public BaseStream { - public: -// friend class URLStream; - - HttpRequest() = default; - - ~HttpRequest() { end(); } - - HttpRequest(Client &client) { setClient(client); } - - void setClient(Client &client) { - this->client_ptr = &client; - this->client_ptr->setTimeout(clientTimeout); - } - - // the requests usually need a host. This needs to be set if we did not - // provide a URL - void setHost(const char *host) { - LOGI("setHost %s", host); - this->host_name = host; - } - - operator bool() { return client_ptr != nullptr ? (bool)*client_ptr : false; } - - virtual bool connected() { - return client_ptr == nullptr ? false : client_ptr->connected(); - } - - virtual int available() override { - if (reply_header.isChunked()) { - return chunk_reader.available(); - } - return client_ptr != nullptr ? client_ptr->available() : 0; - } - - /// same as end() - void end() override { - if (connected()) { - // write final 0 chunk if necessary - if (is_chunked_output_active) { - client_ptr->println(0, HEX); - client_ptr->println(); - client_ptr->flush(); - is_chunked_output_active = false; - } - LOGI("stop"); - client_ptr->stop(); - } - } - - virtual void stop() { - end(); - } - - /// http post - virtual int post(Url &url, const char *mime, const char *data, int len = -1) { - LOGI("post %s", url.url()); - return process(POST, url, mime, data, len); - } - - /// http post - virtual int post(Url &url, const char *mime, Stream &data, int len = -1) { - LOGI("post %s", url.url()); - return process(POST, url, mime, data, len); - } - - /// http put - virtual int put(Url &url, const char *mime, const char *data, int len = -1) { - LOGI("put %s", url.url()); - return process(PUT, url, mime, data, len); - } - - /// http put - virtual int put(Url &url, const char *mime, Stream &data, int len = -1) { - LOGI("put %s", url.url()); - return process(PUT, url, mime, data, len); - } - - /// http del - virtual int del(Url &url, const char *mime = nullptr, - const char *data = nullptr, int len = -1) { - LOGI("del %s", url.url()); - return process(DELETE, url, mime, data, len); - } - - /// http get - virtual int get(Url &url, const char *acceptMime = nullptr, - const char *data = nullptr, int len = -1) { - LOGI("get %s", url.url()); - this->accept = acceptMime; - return process(GET, url, nullptr, data, len); - } - - /// http head - virtual int head(Url &url, const char *acceptMime = nullptr, - const char *data = nullptr, int len = -1) { - LOGI("head %s", url.url()); - this->accept = acceptMime; - return process(HEAD, url, nullptr, data, len); - } - - // reads the reply data - virtual int read(uint8_t *str, int len) { - TRACED(); - if (reply_header.isChunked()) { - return chunk_reader.read(*client_ptr, str, len); - } else { - return client_ptr->read(str, len); - } - } - - size_t readBytes(uint8_t *str, size_t len) override { - return read(str, len); - } - - size_t readBytesUntil(char terminator, char *buffer, size_t length) { - TRACED(); - return client_ptr->readBytesUntil(terminator, buffer, length); - } - - // read the reply data up to the next new line. For Chunked data we provide - // the full chunk! - virtual int readln(uint8_t *str, int len, bool incl_nl = true) { - TRACED(); - if (reply_header.isChunked()) { - return chunk_reader.readln(*client_ptr, str, len); - } else { - return chunk_reader.readlnInternal(*client_ptr, str, len, incl_nl); - } - } - - // provides the head information of the reply - virtual HttpReplyHeader &reply() { return reply_header; } - - /// provides access to the request header - virtual HttpRequestHeader &header() { return request_header; } - - /// Defines the agent - virtual void setAgent(const char *agent) { this->agent = agent; } - - virtual void setConnection(const char *connection) { - this->connection = connection; - } - - virtual void setAcceptsEncoding(const char *enc) { - this->accept_encoding = enc; - } - - virtual void setAcceptMime(const char *mime) { this->accept = mime; } - - size_t contentLength() { - const char *len_str = reply().get(CONTENT_LENGTH); - int len = 0; - if (len_str != nullptr) { - len = atoi(len_str); - } else { - LOGI("no CONTENT_LENGTH found in reply"); - } - return len; - } - - /// returns true when the request has completed and ready for the data to be - /// requested - bool isReady() { return is_ready; } - - /// Adds/Updates a request header - void addRequestHeader(const char *key, const char *value) { - request_header.put(key, value); - } - const char* getReplyHeader(const char *key) { - return reply_header.get(key); - } - - Client &client() { return *client_ptr; } - - // process http request and reads the reply_header from the server - virtual int process(MethodID action, Url &url, const char *mime, - const char *data, int lenData = -1) { - int len = lenData; - if (data != nullptr && len <= 0) { - len = strlen(data); - } - processBegin(action, url, mime, len); - // posting data parameter - if (len > 0 && data != nullptr) { - LOGI("Writing data: %d bytes", len); - client_ptr->write((const uint8_t *)data, len); - LOGD("%s", data); - } - return processEnd(); - } - - // process http request and reads the reply_header from the server - virtual int process(MethodID action, Url &url, const char *mime, - Stream &stream, int len = -1) { - if (!processBegin(action, url, mime, len)) - return -1; - processWrite(stream); - return processEnd(); - } - - /// starts http request processing - virtual bool processBegin(MethodID action, Url &url, const char *mime, - int lenData = -1) { - TRACED(); - int len = lenData; - is_ready = false; - if (client_ptr == nullptr) { - LOGE("The client has not been defined"); - return false; - } - if (http_connect_callback) { - http_connect_callback(*this, url, request_header); - } - if (!this->connected()) { - LOGI("process connecting to host %s port %d", url.host(), url.port()); - int is_connected = connect(url.host(), url.port(), clientTimeout); - if (!is_connected) { - LOGE("Connect failed"); - return false; - } - } else { - LOGI("process is already connected"); - } - -#if defined(ESP32) && defined(ARDUINO) - LOGI("Free heap: %u", (unsigned)ESP.getFreeHeap()); -#endif - - reply_header.setProcessed(); - - host_name = url.host(); - request_header.setValues(action, url.path()); - if (lenData > 0) { - request_header.put(CONTENT_LENGTH, lenData); - } - request_header.put(HOST_C, host_name); - request_header.put(CONNECTION, connection); - request_header.put(USER_AGENT, agent); - request_header.put(ACCEPT_ENCODING, accept_encoding); - request_header.put(ACCEPT, accept); - request_header.put(CONTENT_TYPE, mime); - request_header.write(*client_ptr); - - return true; - } - - /// Writes (Posts) the data of the indicated stream after calling processBegin - virtual void processWrite(Stream &stream) { - uint8_t buffer[CHUNK_SIZE]; - int total = 0; - int total_written = 0; - while (*client_ptr && stream.available() > 0) { - int result_len = stream.readBytes(buffer, CHUNK_SIZE); - total += result_len; - int written = write(buffer, result_len); - total_written += written; - LOGI("--> Bytes read %d vs written %d", result_len, written); - delay(1); - } - client_ptr->flush(); - LOGI("Total bytes read %d vs written %d", total, total_written); - } - - /// Write data to the client: can be used to post data after calling - /// processBegin - size_t write(const uint8_t *data, size_t len) override { - TRACED(); - size_t result = 0; - if (isChunked()) { - if (len > 0) { - is_chunked_output_active = true; - client_ptr->println(len, HEX); - result = client_ptr->write(data, len); - client_ptr->println(); - } - } else { - result = client_ptr->write(data, len); - } - return result; - } - - /// Ends the http request processing and returns the status code - virtual int processEnd() { - TRACED(); - // if sending is chunked we terminate with an empty chunk - if (isChunked()) { - if (is_chunked_output_active) client_ptr->println(0, HEX); - client_ptr->println(); - is_chunked_output_active = false; - } - LOGI("Request written ... waiting for reply"); - // Commented out because this breaks the RP2040 W - // client_ptr->flush(); - reply_header.read(*client_ptr); - - // if we use chunked tranfer we need to read the first chunked length - if (reply_header.isChunked()) { - chunk_reader.open(*client_ptr); - }; - - // wait for data - is_ready = true; - return reply_header.statusCode(); - } - - /// Callback which allows you to add additional paramters dynamically - void setOnConnectCallback(void (*callback)( - HttpRequest &request, Url &url, HttpRequestHeader &request_header)) { - http_connect_callback = callback; - } - - /// Defines the client timeout in ms - void setTimeout(size_t timeoutMs) { clientTimeout = timeoutMs; } - - /// we are sending the data chunked - bool isChunked() { return request_header.isChunked(); } - - protected: - Client *client_ptr = nullptr; - Url url; - HttpRequestHeader request_header; - HttpReplyHeader reply_header; - HttpChunkReader chunk_reader = HttpChunkReader(reply_header); - const char *agent = nullptr; - const char *host_name = nullptr; - const char *connection = CON_KEEP_ALIVE; - const char *accept = ACCEPT_ALL; - const char *accept_encoding = IDENTITY; - bool is_ready = false; - size_t clientTimeout = URL_CLIENT_TIMEOUT; // 60000; - void (*http_connect_callback)(HttpRequest &request, Url &url, - HttpRequestHeader &request_header) = nullptr; - bool is_chunked_output_active = false; - - // opens a connection to the indicated host - virtual int connect(const char *ip, uint16_t port, int32_t timeout) { - TRACED(); - client_ptr->setTimeout(timeout / 1000); // client timeout is in seconds! - request_header.setTimeout(timeout); - reply_header.setTimeout(timeout); - int is_connected = this->client_ptr->connect(ip, port); - LOGI("is connected %s with timeout %d", is_connected ? "true" : "false", (int)timeout); - return is_connected; - } -}; - -} // namespace audio_tools - diff --git a/src/AudioTools/CoreAudio/AudioHttp/HttpTypes.h b/src/AudioTools/CoreAudio/AudioHttp/HttpTypes.h deleted file mode 100644 index c2543cddf8..0000000000 --- a/src/AudioTools/CoreAudio/AudioHttp/HttpTypes.h +++ /dev/null @@ -1,14 +0,0 @@ -#pragma once -/** @brief supported http methods */ -enum MethodID { - UNDEFINED, - GET, - HEAD, - POST, - PUT, - DELETE, - TRACE, - OPTIONS, - CONNECT, - PATCH -}; diff --git a/src/AudioTools/CoreAudio/AudioHttp/ICYStreamT.h b/src/AudioTools/CoreAudio/AudioHttp/ICYStreamT.h deleted file mode 100644 index 06fc9ca87d..0000000000 --- a/src/AudioTools/CoreAudio/AudioHttp/ICYStreamT.h +++ /dev/null @@ -1,177 +0,0 @@ -#pragma once -#include "AudioTools/CoreAudio/AudioHttp/AbstractURLStream.h" -#include "AudioTools/CoreAudio/AudioMetaData/MetaDataICY.h" -#include "AudioToolsConfig.h" - -namespace audio_tools { - -/** - * @brief Icecast/Shoutcast Audio Stream which splits the data into metadata and - * audio data. The Audio data is provided via the regular stream functions. The - * metadata is handled with the help of the MetaDataICY state machine and - * provided via a callback method. - * - * This is basically just a URLStream with the metadata turned on. - * - * If you run into performance issues, check if the data is provided chunked. - * In this chase you can check if setting the protocol to "HTTP/1.0" improves - * the situation. - * - * @ingroup http - * @author Phil Schatzmann - * @copyright GPLv3 - */ - -template -class ICYStreamT : public AbstractURLStream { - public: - ICYStreamT(int readBufferSize = DEFAULT_BUFFER_SIZE) { - TRACEI(); - setReadBufferSize(readBufferSize); - } - - /// Default constructor - ICYStreamT(const char* ssid, const char* password, - int readBufferSize = DEFAULT_BUFFER_SIZE) - : ICYStreamT(readBufferSize) { - TRACEI(); - setSSID(ssid); - setPassword(password); - } - - ICYStreamT(Client& clientPar, int readBufferSize = DEFAULT_BUFFER_SIZE) - : ICYStreamT(readBufferSize) { - TRACEI(); - setClient(clientPar); - } - - /// Defines the meta data callback function - virtual bool setMetadataCallback(void (*fn)(MetaDataType info, - const char* str, - int len)) override { - TRACED(); - callback = fn; - icy.setCallback(fn); - return true; - } - - // Icy http get request to the indicated url - virtual bool begin(const char* urlStr, const char* acceptMime = nullptr, - MethodID action = GET, const char* reqMime = "", - const char* reqData = "") override { - TRACED(); - // accept metadata - addRequestHeader("Icy-MetaData", "1"); - bool result = url.begin(urlStr, acceptMime, action, reqMime, reqData); - - if (result) { - // setup icy - ICYUrlSetup icySetup; - int iceMetaint = icySetup.setup(*this); - // callbacks from http request - icySetup.executeCallback(callback); - icy.setIcyMetaInt(iceMetaint); - icy.begin(); - - if (!icy.hasMetaData()) { - LOGW("url does not provide metadata"); - } - } - return result; - } - - /// Ends the processing - virtual void end() override { - TRACED(); - url.end(); - icy.end(); - } - - /// provides the available method from the URLStream - virtual int available() override { return url.available(); } - - /// reads the audio bytes - virtual size_t readBytes(uint8_t* data, size_t len) override { - size_t result = 0; - if (icy.hasMetaData()) { - // get data - int read = url.readBytes(data, len); - // remove metadata from data - int pos = 0; - for (int j = 0; j < read; j++) { - icy.processChar(data[j]); - if (icy.isData()) { - data[pos++] = data[j]; - } - } - result = pos; - } else { - // fast access if there is no metadata - result = url.readBytes(data, len); - } - LOGD("%s: %zu -> %zu", LOG_METHOD, len, result); - return result; - } - - // Read character and processes using the MetaDataICY state engine - virtual int read() override { - int ch = -1; - - // get next data byte - do { - ch = url.read(); - if (ch == -1) { - break; - } - - icy.processChar(ch); - } while (!icy.isData()); - return ch; - } - - operator bool() { return url; } - - void setReadBufferSize(int readBufferSize) { - url.setReadBufferSize(readBufferSize); - } - - /// Sets the ssid that will be used for logging in (when calling begin) - void setSSID(const char* ssid) override { url.setSSID(ssid); } - - /// Sets the password that will be used for logging in (when calling begin) - void setPassword(const char* password) override { url.setPassword(password); } - - /// if set to true, it activates the power save mode which comes at the cost - /// of performance! - By default this is deactivated. ESP32 Only! - void setPowerSave(bool active) { url.setPowerSave(active); } - - /// Define the Root PEM Certificate for SSL: - void setCACert(const char* cert) { url.setCACert(cert); } - /// Adds/Updates a request header - void addRequestHeader(const char* key, const char* value) override { - url.addRequestHeader(key, value); - } - /// Provides reply header info - const char* getReplyHeader(const char* key) override { - return url.getReplyHeader(key); - } - - /// provides access to the HttpRequest - virtual HttpRequest& httpRequest() override { return url.httpRequest(); } - - /// (Re-)defines the client - void setClient(Client& client) override { url.setClient(client); } - - void setConnectionClose(bool flag) override { url.setConnectionClose(flag); } - const char* urlStr() override { return url.urlStr(); } - size_t totalRead() override { return url.totalRead(); }; - int contentLength() override { return url.contentLength(); }; - bool waitForData(int timeout) override { return url.waitForData(timeout); } - - protected: - T url; - MetaDataICY icy; // icy state machine - void (*callback)(MetaDataType info, const char* str, int len) = nullptr; -}; - -} // namespace audio_tools diff --git a/src/AudioTools/CoreAudio/AudioHttp/URLStream.h b/src/AudioTools/CoreAudio/AudioHttp/URLStream.h deleted file mode 100644 index fe606ef5b8..0000000000 --- a/src/AudioTools/CoreAudio/AudioHttp/URLStream.h +++ /dev/null @@ -1,437 +0,0 @@ -#pragma once - -#include "AudioToolsConfig.h" -#ifdef USE_URL_ARDUINO - -#if defined(ESP32) -# include -# include -# include -# include -#endif - -#include "AudioTools/CoreAudio/AudioBasic/Str.h" -#include "AudioTools/CoreAudio/AudioHttp/AbstractURLStream.h" -#include "AudioTools/CoreAudio/AudioHttp/HttpRequest.h" -#include "AudioTools/CoreAudio/AudioHttp/ICYStreamT.h" -#include "AudioTools/CoreAudio/AudioHttp/URLStreamBufferedT.h" - - -namespace audio_tools { - -/** - * @brief Represents the content of a URL as Stream. We use the WiFi.h API. - * If you run into performance issues, check if the data is provided chunked. - * In this chase you can check if setting the protocol to "HTTP/1.0" improves - * the situation. - * @author Phil Schatzmann - * @ingroup http - * @copyright GPLv3 - * - */ -class URLStream : public AbstractURLStream { - public: - URLStream(int readBufferSize = DEFAULT_BUFFER_SIZE) { - TRACED(); - setReadBufferSize(readBufferSize); - } - - URLStream(Client& clientPar, int readBufferSize = DEFAULT_BUFFER_SIZE) { - TRACED(); - setReadBufferSize(readBufferSize); - setClient(clientPar); - } - - URLStream(const char* network, const char* password, - int readBufferSize = DEFAULT_BUFFER_SIZE) { - TRACED(); - setReadBufferSize(readBufferSize); - setSSID(network); - setPassword(password); - } - - URLStream(const URLStream&) = delete; - - ~URLStream() { - TRACED(); - end(); -#ifdef USE_WIFI_CLIENT_SECURE - if (clientSecure != nullptr) { - delete clientSecure; - clientSecure = nullptr; - } -#endif -#ifdef USE_WIFI - if (clientInsecure != nullptr) { - delete clientInsecure; - clientInsecure = nullptr; - } -#endif - } - - /// (Re-)defines the client - void setClient(Client& clientPar) override { client = &clientPar; } - - /// Sets the ssid that will be used for logging in (when calling begin) - void setSSID(const char* ssid) override { this->network = ssid; } - - /// Sets the password that will be used for logging in (when calling begin) - void setPassword(const char* password) override { this->password = password; } - - /// Defines the buffer that is used by individual read() or peek() calls - void setReadBufferSize(int readBufferSize) { - read_buffer_size = readBufferSize; - } - - /// Execute http request: by default we use a GET request - virtual bool begin(const char* urlStr, const char* acceptMime = nullptr, - MethodID action = GET, const char* reqMime = "", - const char* reqData = "") override { - LOGI("%s: %s", LOG_METHOD, urlStr); - if (!preProcess(urlStr, acceptMime)) { - LOGE("preProcess failed"); - return false; - } - int result = process(action, url, reqMime, reqData); - if (result > 0) { - size = request.contentLength(); - LOGI("contentLength: %d", (int)size); - if (size >= 0 && wait_for_data) { - waitForData(clientTimeout); - } - } - total_read = 0; - active = result == 200; - LOGI("==> http status: %d", result); - return active; - } - - /// Execute e.g. http POST request which submits the content as a stream - virtual bool begin(const char* urlStr, const char* acceptMime, - MethodID action, const char* reqMime, Stream& reqData, - int len = -1) { - LOGI("%s: %s", LOG_METHOD, urlStr); - if (!preProcess(urlStr, acceptMime)) { - LOGE("preProcess failed"); - return false; - } - int result = process(action, url, reqMime, reqData, len); - if (result > 0) { - size = request.contentLength(); - LOGI("size: %d", (int)size); - if (size >= 0 && wait_for_data) { - waitForData(clientTimeout); - } - } - total_read = 0; - active = result == 200; - LOGI("==> http status: %d", result); - return active; - } - - virtual void end() override { - if (active) request.stop(); - active = false; - clear(); - } - - virtual int available() override { - if (!active) return 0; - - int result = request.available(); - LOGD("available: %d", result); - return result; - } - - virtual size_t readBytes(uint8_t* data, size_t len) override { - if (!active) return 0; - - int read = request.read((uint8_t*)&data[0], len); - if (read < 0) { - read = 0; - } - total_read += read; - LOGD("readBytes %d -> %d", (int)len, read); - return read; - } - - virtual int read() override { - if (!active) return -1; - // lazy allocation since this is rarely used - read_buffer.resize(read_buffer_size); - - fillBuffer(); - total_read++; - return isEOS() ? -1 : read_buffer[read_pos++]; - } - - virtual int peek() override { - if (!active) return -1; - // lazy allocation since this is rarely used - read_buffer.resize(read_buffer_size); - - fillBuffer(); - return isEOS() ? -1 : read_buffer[read_pos]; - } - - virtual void flush() override {} - - virtual size_t write(uint8_t) override { return not_supported(0); } - - virtual size_t write(const uint8_t*, size_t len) override { - return not_supported(0); - } - - /// provides access to the HttpRequest - virtual HttpRequest& httpRequest() override { return request; } - - operator bool() override { return active && request.isReady(); } - - /// Defines the client timeout - virtual void setTimeout(int ms) { clientTimeout = ms; } - - /// if set to true, it activates the power save mode which comes at the cost - /// of performance! - By default this is deactivated. ESP32 Only! - void setPowerSave(bool ps) override { is_power_save = ps; } - - /// If set to true, new undefined reply parameters will be stored - void setAutoCreateLines(bool flag) { - httpRequest().reply().setAutoCreateLines(flag); - } - - /// Sets if the connection should be close automatically - void setConnectionClose(bool close) override { - httpRequest().setConnection(close ? CON_CLOSE : CON_KEEP_ALIVE); - } - - /// Releases the memory from the request and reply - void clear() { - httpRequest().reply().clear(); - httpRequest().header().clear(); - read_buffer.resize(0); - read_pos = 0; - read_size = 0; - } - - /// Adds/Updates a request header - void addRequestHeader(const char* key, const char* value) override { - request.header().put(key, value); - } - - const char* getReplyHeader(const char* key) override { - return request.reply().get(key); - } - - /// Callback which allows you to add additional paramters dynamically - void setOnConnectCallback(void (*callback)( - HttpRequest& request, Url& url, HttpRequestHeader& request_header)) { - request.setOnConnectCallback(callback); - } - - void setWaitForData(bool flag) { wait_for_data = flag; } - - int contentLength() override { return size; } - - size_t totalRead() override { return total_read; } - /// waits for some data - returns false if the request has failed - bool waitForData (int timeout) override{ - TRACED(); - uint32_t end = millis() + timeout; - if (request.available() == 0) { - LOGI("Request written ... waiting for reply"); - while (request.available() == 0) { - if (millis() > end) break; - // stop waiting if we got an error - if (request.reply().statusCode() >= 300) { - LOGE("Error code recieved ... stop waiting for reply"); - break; - } - delay(500); - } - } - LOGD("available: %d", request.available()); - return request.available() > 0; - } - - - const char* urlStr() override { return url_str.c_str(); } - -/// Define the Root PEM Certificate for SSL - void setCACert(const char* cert) override{ - #ifdef USE_WIFI_CLIENT_SECURE - if (clientSecure!=nullptr) clientSecure->setCACert(cert); - #endif - } - - protected: - HttpRequest request; - Str url_str; - Url url; - long size; - long total_read; - // buffered single byte read - Vector read_buffer{0}; - uint16_t read_buffer_size = DEFAULT_BUFFER_SIZE; - uint16_t read_pos; - uint16_t read_size; - bool active = false; - bool wait_for_data = true; - // optional - const char* network = nullptr; - const char* password = nullptr; - Client* client = nullptr; // client defined via setClient -#ifdef USE_WIFI - WiFiClient* clientInsecure = nullptr; // wifi client for http -#endif -#ifdef USE_WIFI_CLIENT_SECURE - WiFiClientSecure* clientSecure = nullptr; // wifi client for https -#endif - int clientTimeout = URL_CLIENT_TIMEOUT; // 60000; - unsigned long handshakeTimeout = URL_HANDSHAKE_TIMEOUT; // 120000 - bool is_power_save = false; - - bool preProcess(const char* urlStr, const char* acceptMime) { - TRACED(); - url_str = urlStr; - url.setUrl(url_str.c_str()); - int result = -1; - - // close it - if we have an active connection - if (active) end(); - -#ifdef USE_WIFI - // optional: login if necessary if no external client is defined - if (client == nullptr){ - if (!login()){ - LOGE("Not connected"); - return false; - } - } -#endif - - // request.reply().setAutoCreateLines(false); - if (acceptMime != nullptr) { - request.setAcceptMime(acceptMime); - } - - // setup client - Client& client = getClient(url.isSecure()); - request.setClient(client); - - // set timeout - client.setTimeout(clientTimeout / 1000); - request.setTimeout(clientTimeout); - -#if defined(ESP32) && defined(USE_WIFI_CLIENT_SECURE) - // There is a bug in IDF 4! - if (clientSecure != nullptr) { - clientSecure->setHandshakeTimeout(handshakeTimeout); - } - - // Performance optimization for ESP32 - if (!is_power_save) { - esp_wifi_set_ps(WIFI_PS_NONE); - } -#endif - - return true; - } - - /// Process the Http request and handle redirects - template - int process(MethodID action, Url& url, const char* reqMime, T reqData, - int len = -1) { - TRACED(); - // keep icy across redirect requests ? - const char* icy = request.header().get("Icy-MetaData"); - - int status_code = request.process(action, url, reqMime, reqData, len); - // redirect - while (request.reply().isRedirectStatus()) { - const char* redirect_url = request.reply().get(LOCATION); - if (redirect_url != nullptr) { - LOGW("Redirected to: %s", redirect_url); - url.setUrl(redirect_url); - Client* p_client = &getClient(url.isSecure()); - p_client->stop(); - request.setClient(*p_client); - if (icy) { - request.header().put("Icy-MetaData", icy); - } - status_code = request.process(action, url, reqMime, reqData, len); - } else { - LOGE("Location is null"); - break; - } - } - return status_code; - } - - /// Determines the client - Client& getClient(bool isSecure) { -#ifdef USE_WIFI_CLIENT_SECURE - if (isSecure) { - if (clientSecure == nullptr) { - clientSecure = new WiFiClientSecure(); - clientSecure->setInsecure(); - } - LOGI("WiFiClientSecure"); - return *clientSecure; - } -#endif -#ifdef USE_WIFI - if (clientInsecure == nullptr) { - clientInsecure = new WiFiClient(); - LOGI("WiFiClient"); - } - return *clientInsecure; -#else - if (client == nullptr){ - LOGE("Client not set"); - stop(); - } - return *client; // to avoid compiler warning -#endif - } - - inline void fillBuffer() { - if (isEOS()) { - // if we consumed all bytes we refill the buffer - read_size = readBytes(&read_buffer[0], read_buffer_size); - read_pos = 0; - } - } - - inline bool isEOS() { return read_pos >= read_size; } - - bool login() { -#ifdef USE_WIFI - if (network != nullptr && password != nullptr && - WiFi.status() != WL_CONNECTED) { - TRACEI(); - WiFi.begin(network, password); - while (WiFi.status() != WL_CONNECTED) { - Serial.print("."); - delay(500); - } - Serial.println(); - delay(10); - return WiFi.status() == WL_CONNECTED; - } - return WiFi.status() == WL_CONNECTED; -#else - return false; -#endif - } -}; - -using ICYStream = ICYStreamT; - -#if defined(USE_CONCURRENCY) -using URLStreamBuffered = URLStreamBufferedT; -using ICYStreamBuffered = URLStreamBufferedT; -#endif - -} // namespace audio_tools - -#endif diff --git a/src/AudioTools/CoreAudio/AudioHttp/URLStreamBufferedT.h b/src/AudioTools/CoreAudio/AudioHttp/URLStreamBufferedT.h deleted file mode 100644 index 02fbecaf41..0000000000 --- a/src/AudioTools/CoreAudio/AudioHttp/URLStreamBufferedT.h +++ /dev/null @@ -1,254 +0,0 @@ -#pragma once -#include "AudioToolsConfig.h" -#if defined(USE_CONCURRENCY) -#include "AudioTools/AudioLibs/Concurrency.h" -#include "AudioTools/CoreAudio/AudioHttp/AbstractURLStream.h" -#include "AudioTools/CoreAudio/BaseStream.h" - -#ifndef URL_STREAM_CORE -#define URL_STREAM_CORE 0 -#endif - -#ifndef URL_STREAM_PRIORITY -#define URL_STREAM_PRIORITY 2 -#endif - -#ifndef URL_STREAM_BUFFER_COUNT -#define URL_STREAM_BUFFER_COUNT 10 -#endif - -#ifndef STACK_SIZE -#define STACK_SIZE 30000 -#endif - -namespace audio_tools { - -/** - * @brief A FreeRTOS task is filling the buffer from the indicated stream. Only - * to be used on the ESP32 - * - * @author Phil Schatzmann - * @copyright GPLv3 - */ -class BufferedTaskStream : public AudioStream { - public: - BufferedTaskStream() { TRACEI(); }; - - BufferedTaskStream(AudioStream &input) { - TRACEI(); - setInput(input); - } - - ~BufferedTaskStream() { - TRACEI(); - stop(); - } - - /// Define an explicit the buffer size in bytes - void setBufferSize(int bufferSize, int bufferCount) { - buffers.resize(bufferSize, bufferCount); - } - - virtual void begin(bool wait = true) { - TRACED(); - active = true; - ready = false; - task.begin(std::bind(&BufferedTaskStream::processTask, this)); - if (!wait) ready = true; - } - - virtual void end() { - TRACED(); - task.end(); - active = false; - ready = false; - } - - virtual void setInput(AudioStream &input) { - TRACED(); - p_stream = &input; - } - - /// writes a byte to the buffer - virtual size_t write(uint8_t c) override { return 0; } - - /// Use this method: write an array - virtual size_t write(const uint8_t *data, size_t len) override { return 0; } - - /// empties the buffer - virtual void flush() override {} - - /// reads a byte - to be avoided - virtual int read() override { - // if (!ready) return -1; - uint8_t result = 0; - if(!buffers.read(result)) return -1; - return result; - } - - /// peeks a byte - to be avoided - virtual int peek() override { - // if (!ready) return -1; - uint8_t result = 0; - if(!buffers.peek(result)) return -1; - return result; - }; - - /// Use this method !! - virtual size_t readBytes(uint8_t *data, size_t len) override { - // if (!ready) return 0; - size_t result = 0; - result = buffers.readArray(data, len); - LOGD("%s: %zu -> %zu", LOG_METHOD, len, result); - return result; - } - - /// Returns the available bytes in the buffer: to be avoided - virtual int available() override { - // if (!ready) return 0; - int result = 0; - result = buffers.available(); - return result; - } - - protected: - AudioStream *p_stream = nullptr; - bool active = false; - Task task{"BufferedTaskStream", STACK_SIZE, URL_STREAM_PRIORITY, - URL_STREAM_CORE}; - SynchronizedNBuffer buffers{DEFAULT_BUFFER_SIZE, URL_STREAM_BUFFER_COUNT}; - bool ready = false; - - void processTask() { - size_t available_to_write = this->buffers.availableForWrite(); - if (*(this->p_stream) && available_to_write > 0) { - size_t to_read = min(available_to_write, (size_t)512); - uint8_t buffer[to_read]; - size_t avail_read = this->p_stream->readBytes((uint8_t *)buffer, to_read); - size_t written = this->buffers.writeArray(buffer, avail_read); - - if (written != avail_read) { - LOGE("DATA Lost! %zu reqested, %zu written!", avail_read, written); - } - - } else { - // 3ms at 44100 stereo is about 529.2 bytes - delay(3); - } - // buffer is full we start to provide data - if (available_to_write == 0) { - this->ready = true; - } - } -}; - -/** - * @brief URLStream implementation for the ESP32 based on a separate FreeRTOS - * task: the - * @ingroup http - * @author Phil Schatzmann - * @copyright GPLv3 - */ - -template -class URLStreamBufferedT : public AbstractURLStream { - public: - URLStreamBufferedT(int readBufferSize = DEFAULT_BUFFER_SIZE) { - TRACED(); - urlStream.setReadBufferSize(readBufferSize); - taskStream.setInput(urlStream); - } - - URLStreamBufferedT(const char *network, const char *password, - int readBufferSize = DEFAULT_BUFFER_SIZE) { - TRACED(); - urlStream.setReadBufferSize(readBufferSize); - setSSID(network); - setPassword(password); - taskStream.setInput(urlStream); - } - -#ifdef ARDUINO - - URLStreamBufferedT(Client &clientPar, - int readBufferSize = DEFAULT_BUFFER_SIZE) { - TRACED(); - urlStream.setReadBufferSize(readBufferSize); - setClient(clientPar); - taskStream.setInput(urlStream); - } -#endif - - /// Defines the buffer that holds the with encoded data - void setBufferSize(int bufferSize, int bufferCount) { - taskStream.setBufferSize(bufferSize, bufferCount); - } - - bool begin(const char *urlStr, const char *acceptMime = nullptr, - MethodID action = GET, const char *reqMime = "", - const char *reqData = "") { - TRACED(); - // start real stream - bool result = urlStream.begin(urlStr, acceptMime, action, reqMime, reqData); - // start buffer task - taskStream.begin(); - return result; - } - - virtual int available() { return taskStream.available(); } - - virtual size_t readBytes(uint8_t *data, size_t len) { - size_t result = taskStream.readBytes(data, len); - LOGD("%s: %zu -> %zu", LOG_METHOD, len, result); - return result; - } - - virtual int read() { return taskStream.read(); } - - virtual int peek() { return taskStream.peek(); } - - virtual void flush() {} - - void end() { - TRACED(); - taskStream.end(); - urlStream.end(); - } - - /// Sets the ssid that will be used for logging in (when calling begin) - void setSSID(const char *ssid) override { urlStream.setSSID(ssid); } - - /// Sets the password that will be used for logging in (when calling begin) - void setPassword(const char *password) override { - urlStream.setPassword(password); - } - - /// ESP32 only: PowerSave off (= default setting) is much faster - void setPowerSave(bool ps) override { urlStream.setPowerSave(ps); } - - /// Define the Root PEM Certificate for SSL - void setCACert(const char *cert) { urlStream.setCACert(cert); } - - /// Adds/Updates a request header - void addRequestHeader(const char* key, const char* value) override { - urlStream.addRequestHeader(key, value); - } - /// Provides reply header info - const char* getReplyHeader(const char* key) override { - return urlStream.getReplyHeader(key); - } - - /// provides access to the HttpRequest - HttpRequest &httpRequest() { return urlStream.httpRequest(); } - - /// (Re-)defines the client - void setClient(Client &client) override { urlStream.setClient(client); } - - protected: - BufferedTaskStream taskStream; - T urlStream; -}; - -} // namespace audio_tools - -#endif diff --git a/src/AudioTools/CoreAudio/AudioHttp/URLStreamESP32.h b/src/AudioTools/CoreAudio/AudioHttp/URLStreamESP32.h deleted file mode 100644 index 66a7a2b4b4..0000000000 --- a/src/AudioTools/CoreAudio/AudioHttp/URLStreamESP32.h +++ /dev/null @@ -1,431 +0,0 @@ -#pragma once - -#include "AudioTools/CoreAudio/AudioHttp/AbstractURLStream.h" -#include "AudioTools/CoreAudio/AudioHttp/HttpRequest.h" -#include "AudioTools/CoreAudio/AudioHttp/ICYStreamT.h" -#include "AudioTools/CoreAudio/AudioHttp/URLStreamBufferedT.h" -#include "esp_http_client.h" -#include "esp_idf_version.h" -#include "esp_system.h" -#include "esp_wifi.h" -#include "nvs_flash.h" - -namespace audio_tools { - -/** - * @brief Login to Wifi using the ESP32 IDF functionality. This can be - * accessed with the global object IDF_WIFI - * @author Phil Schatzmann - * @ingroup http - * @copyright GPLv3 - */ -class WiFiESP32 { - public: - bool begin(const char* ssid, const char* password) { - TRACEI(); - if (is_open) return true; - if (!setupWIFI(ssid, password)) { - LOGE("setupWIFI failed"); - return false; - } - - return true; - } - - void end() { - TRACED(); - if (is_open) { - TRACEI(); - esp_wifi_stop(); - esp_wifi_deinit(); - } - is_open = false; - } - - void setPowerSave(wifi_ps_type_t powerSave) { power_save = powerSave; } - - bool isConnected() { return is_open; } - - protected: - volatile bool is_open = false; - esp_ip4_addr_t ip = {0}; - wifi_ps_type_t power_save = WIFI_PS_NONE; - - bool setupWIFI(const char* ssid, const char* password) { - assert(ssid != nullptr); - assert(password != nullptr); - LOGI("setupWIFI: %s", ssid); - esp_err_t ret = nvs_flash_init(); - if (ret == ESP_ERR_NVS_NO_FREE_PAGES || - ret == ESP_ERR_NVS_NEW_VERSION_FOUND) { - nvs_flash_erase(); - ret = nvs_flash_init(); - } - - if (esp_netif_init() != ESP_OK) { - LOGE("esp_netif_init"); - return false; - }; - - if (esp_event_loop_create_default() != ESP_OK) { - LOGE("esp_event_loop_create_default"); - return false; - }; - - esp_netif_t* itf = esp_netif_create_default_wifi_sta(); - if (itf == nullptr) { - LOGE("esp_netif_create_default_wifi_sta"); - return false; - }; - - esp_event_handler_instance_register(WIFI_EVENT, ESP_EVENT_ANY_ID, - &wifi_sta_event_handler, this, NULL); - esp_event_handler_instance_register(IP_EVENT, IP_EVENT_STA_GOT_IP, - &wifi_sta_event_handler, this, NULL); - wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT(); - if (esp_wifi_init(&cfg) != ESP_OK) { - LOGE("esp_wifi_init"); - return false; - } - - esp_wifi_set_mode(WIFI_MODE_STA); - esp_wifi_set_ps(power_save); - - wifi_config_t sta_config; - memset(&sta_config, 0, sizeof(wifi_config_t)); - strncpy((char*)sta_config.sta.ssid, ssid, 32); - strncpy((char*)sta_config.sta.password, password, 32); - sta_config.sta.threshold.authmode = WIFI_AUTH_WPA_WPA2_PSK; - esp_wifi_set_config(WIFI_IF_STA, &sta_config); - - // start wifi - bool rc = esp_wifi_start() == ESP_OK; - if (!rc) { - LOGE("esp_wifi_start"); - } - return rc; - } - - static void wifi_sta_event_handler(void* arg, esp_event_base_t event_base, - int32_t event_id, void* event_data) { - WiFiESP32* self = (WiFiESP32*)arg; - - if (event_base == WIFI_EVENT) { - switch (event_id) { - case WIFI_EVENT_STA_START: - LOGI("WIFI_EVENT_STA_START"); - esp_wifi_connect(); - break; - case WIFI_EVENT_STA_DISCONNECTED: - LOGI("WIFI_EVENT_STA_DISCONNECTED"); - esp_wifi_connect(); - break; - } - } else if (event_base == IP_EVENT) { - switch (event_id) { - case IP_EVENT_STA_GOT_IP: { - ip_event_got_ip_t* event = (ip_event_got_ip_t*)event_data; - self->ip = event->ip_info.ip; - self->is_open = true; - LOGI("==> Station connected with IP: " IPSTR ", GW: " IPSTR - ", Mask: " IPSTR ".", - IP2STR(&event->ip_info.ip), IP2STR(&event->ip_info.gw), - IP2STR(&event->ip_info.netmask)); - break; - } - } - } - } - -} static IDF_WIFI; - -class URLStreamESP32; -static URLStreamESP32* actualURLStreamESP32 = nullptr; - -/** - * @brief URLStream using the ESP32 IDF API. - * - * For Https you need to provide the certificate. - * Execute: openssl s_client -showcerts -connect www.howsmyssl.com:443 - * & lines = request.header().getHeaderLines(); - for (auto it = lines.begin(); it != lines.end(); ++it) { - if ((*it)->active) { - esp_http_client_set_header(client_handle, (*it)->key.c_str(), - (*it)->value.c_str()); - } - } - - // Open http - if (esp_http_client_open(client_handle, 0) != ESP_OK) { - LOGE("esp_http_client_open"); - return false; - } - - // Determine the result - content_length = esp_http_client_fetch_headers(client_handle); - int status_code = esp_http_client_get_status_code(client_handle); - LOGI("status_code: %d / content_length: %d", status_code, content_length); - - // Process post/put data - StrView data(reqData); - if (!data.isEmpty()) { - write((const uint8_t*)reqData, data.length()); - } - - return status_code == 200; - } - // ends the request - virtual void end() override { - esp_http_client_close(client_handle); - esp_http_client_cleanup(client_handle); - } - - /// Writes are not supported - int availableForWrite() override { return 1024; } - - /// Sets the ssid that will be used for logging in (when calling begin) - virtual void setSSID(const char* ssid) { this->ssid = ssid; } - - /// Sets the password that will be used for logging in (when calling begin) - virtual void setPassword(const char* password) { this->password = password; } - - /// Sets the power save mode (default false)! - virtual void setPowerSave(bool ps) { - IDF_WIFI.setPowerSave(ps ? WIFI_PS_MAX_MODEM : WIFI_PS_NONE); - } - - size_t write(const uint8_t* data, size_t len) override { - TRACED(); - return esp_http_client_write(client_handle, (const char*)data, len); - } - - size_t readBytes(uint8_t* data, size_t len) override { - TRACED(); - size_t read = esp_http_client_read(client_handle, (char*)data, len); - total_read += read; - return read; - } - - /// Adds/Updates a request header - void addRequestHeader(const char* key, const char* value) override { - TRACED(); - request.addRequestHeader(key, value); - } - /// Provides a header entry - const char* getReplyHeader(const char* key) override { - return request.getReplyHeader(key); - } - - /// Define the Root PEM Certificate for SSL: Method compatible with Arduino - /// WiFiClientSecure API - void setCACert(const char* cert) override { - int len = strlen(cert); - setCACert((const uint8_t*)cert, len + 1); - } - - /// Attach and enable use of a bundle for certificate verification e.g. - /// esp_crt_bundle_attach or arduino_esp_crt_bundle_attach - void setCACert(esp_err_t (*cb)(void* conf)) { crt_bundle_attach = cb; } - - /// Defines the read buffer size - void setReadBufferSize(int size) { buffer_size = size; } - - /// Used for request and reply header parameters - HttpRequest& httpRequest() override { return request; } - - /// Does nothing - void setClient(Client& client) override {} - - /// Provides the url as string - const char* urlStr() override { return (char*)url_str; } - - /// Total amout of data that was consumed so far - size_t totalRead() override { return total_read; } - - /// Provides the reported data size from the http reply - int contentLength() override { return content_length; } - - /// Not used - virtual bool waitForData(int timeout) override{ return false; } - /// Not used - void setConnectionClose(bool flag) override {} - - protected: - int id = 0; - HttpRequest request; - esp_http_client_handle_t client_handle = nullptr; - bool is_power_save = false; - const char* ssid = nullptr; - const char* password = nullptr; - int buffer_size = DEFAULT_BUFFER_SIZE; - const uint8_t* pem_cert = nullptr; - int pem_cert_len = 0; - esp_err_t (*crt_bundle_attach)(void* conf) = nullptr; - size_t total_read = 0; - const char* url_str = nullptr; - int content_length = 0; - - /// Define the Root PEM Certificate for SSL: the last byte must be null, - /// the len is including the ending null - void setCACert(const uint8_t* cert, int len) { - pem_cert_len = len; - pem_cert = cert; - // certificate must end with traling null - assert(cert[len - 1] == 0); - } - - static esp_err_t http_event_handler(esp_http_client_event_t* evt) { - switch (evt->event_id) { - case HTTP_EVENT_ERROR: - LOGI("HTTP_EVENT_ERROR"); - break; - case HTTP_EVENT_ON_CONNECTED: - LOGD("HTTP_EVENT_ON_CONNECTED"); - break; - case HTTP_EVENT_HEADER_SENT: - LOGD("HTTP_EVENT_HEADER_SENT"); - break; - case HTTP_EVENT_ON_HEADER: - LOGI("HTTP_EVENT_ON_HEADER, key=%s, value=%s", evt->header_key, - evt->header_value); - // store reply headers - actualURLStreamESP32->request.reply().put(evt->header_key, - evt->header_value); - break; - case HTTP_EVENT_ON_DATA: - LOGD("HTTP_EVENT_ON_DATA, len=%d", evt->data_len); - break; - case HTTP_EVENT_ON_FINISH: - LOGI("HTTP_EVENT_ON_FINISH"); - break; - case HTTP_EVENT_DISCONNECTED: - LOGI("HTTP_EVENT_DISCONNECTED"); - break; -#if ESP_IDF_VERSION > ESP_IDF_VERSION_VAL(5, 3, 7) - case HTTP_EVENT_REDIRECT: - LOGI("HTTP_EVENT_REDIRECT"); - break; -#endif - } - return ESP_OK; - } -}; - -/// ICYStream -using ICYStreamESP32 = ICYStreamT; -#if defined(USE_CONCURRENCY) -using URLStreamBufferedESP32 = URLStreamBufferedT; -using ICYStreamBufferedESP32 = URLStreamBufferedT; -#endif - -/// Support URLStream w/o Arduino -#if !defined(ARDUINO) -using URLStream = URLStreamESP32; -using URLStreamBuffered = URLStreamBufferedESP32; -using ICYStreamBuffered = ICYStreamBufferedESP32; -#endif - -} // namespace audio_tools \ No newline at end of file diff --git a/src/AudioTools/CoreAudio/AudioHttp/Url.h b/src/AudioTools/CoreAudio/AudioHttp/Url.h deleted file mode 100644 index f62d863d8d..0000000000 --- a/src/AudioTools/CoreAudio/AudioHttp/Url.h +++ /dev/null @@ -1,104 +0,0 @@ -#pragma once - -#include "AudioTools/CoreAudio/AudioBasic/Str.h" -#include "AudioTools/CoreAudio/AudioLogger.h" - -namespace audio_tools { - -/** - * @brief URL parser which breaks a full url string up into its individual parts - * - * http://pschatzmann.ch:80/path1/path2 - * -> protocol: http - * -> host: pschatzmann.ch - * -> port: 80 - * -> url: http://pschatzmann.ch:80/path1/path2 - * -> root: http://pschatzmann.ch:80 - * @ingroup http - * @author Phil Schatzmann - * @copyright GPLv3 - - */ -class Url { - public: - /// Allow Empty constructor - Url() = default; - - /// setup url from string - Url(const char* url) { - LOGD("Url %s", url); - setUrl(url); - } - - const char* url() { return urlStr.c_str(); } - const char* path() { return pathStr.c_str(); } - const char* host() { return hostStr.c_str(); } - const char* protocol() { return protocolStr.c_str(); } - const char* urlRoot() { - return urlRootStr.c_str(); - } // prefix w/o path -> https://host:port - int port() { return portInt; } - bool isSecure() { return portInt == 443; } - - void setUrl(const char* url) { - LOGD("setUrl %s", url); - this->urlStr = url; - parse(); - } - - protected: - Str pathStr{0}; - Str hostStr{0}; - Str protocolStr{0}; - Str urlRootStr{0}; - Str urlStr{0}; - int portInt = 0; - - void parse() { - LOGI("Url::parse"); - - int protocolEnd = urlStr.indexOf("://"); - if (protocolEnd == -1) { - return; - } - protocolStr.substring(urlStr, 0, protocolEnd); - int pathStart = urlStr.indexOf("/", protocolEnd + 4); - int portStart = urlStr.indexOf(":", protocolEnd + 3); - // check if the found portStart isn't part of the path - if (pathStart>=0) portStart = portStart < pathStart ? portStart : -1; - int hostEnd = portStart != -1 ? portStart : pathStart; - // we have not path -> so then host end is the end of string - if (hostEnd == -1) { - hostEnd = urlStr.length(); - } - hostStr.substring(urlStr, protocolEnd + 3, hostEnd); - if (portStart > 0) { - portInt = atoi(urlStr.c_str() + portStart + 1); - } else { - if (protocolStr.startsWith("https")) - portInt = 443; - else if (protocolStr.startsWith("http")) - portInt = 80; - else if (protocolStr.startsWith("ftp")) - portInt = 21; - else - portInt = -1; // undefined - } - if (pathStart <= 0) { - // we have no path - pathStr = "/"; - urlRootStr = urlStr; - } else { - pathStr.substring(urlStr, pathStart, urlStr.length()); - pathStr.trim(); - urlRootStr.substring(urlStr, 0, pathStart); - } - LOGI("url->%s", url()); - LOGI("host->%s", host()); - LOGI("protocol->%s", protocol()); - LOGI("path->%s", path()); - LOGI("port->%d", port()); - } -}; - -} // namespace audio_tools diff --git a/src/AudioTools/CoreAudio/AudioI2S/I2SConfig.h b/src/AudioTools/CoreAudio/AudioI2S/I2SConfig.h deleted file mode 100644 index 7969f3fad3..0000000000 --- a/src/AudioTools/CoreAudio/AudioI2S/I2SConfig.h +++ /dev/null @@ -1,15 +0,0 @@ -#pragma once -#include "AudioToolsConfig.h" -#ifdef USE_I2S - -#if defined(ESP32) -# if ESP_IDF_VERSION < ESP_IDF_VERSION_VAL(5, 0 , 0) -# include "I2SConfigESP32.h" -# else -# include "I2SConfigESP32V1.h" -# endif -#else -# include "I2SConfigStd.h" -#endif - -#endif \ No newline at end of file diff --git a/src/AudioTools/CoreAudio/AudioI2S/I2SConfigESP32V1.h b/src/AudioTools/CoreAudio/AudioI2S/I2SConfigESP32V1.h deleted file mode 100644 index 3edb0b0df8..0000000000 --- a/src/AudioTools/CoreAudio/AudioI2S/I2SConfigESP32V1.h +++ /dev/null @@ -1,118 +0,0 @@ -#pragma once -#include "AudioToolsConfig.h" -#include "AudioTools/CoreAudio/AudioTypes.h" - -#ifndef PIN_I2S_MCK -# define PIN_I2S_MCK -1 -#endif - - - -namespace audio_tools { - -/// Select left or right channel when number of channels = 1 -enum class I2SChannelSelect { Stereo, Left, Right, Default }; - -/** - * @brief Configuration for ESP32 i2s for IDF > 5.0 - * @ingroup platform - * @author Phil Schatzmann - * @copyright GPLv3 - */ -class I2SConfigESP32V1 : public AudioInfo { - public: - - I2SConfigESP32V1() { - channels = DEFAULT_CHANNELS; - sample_rate = DEFAULT_SAMPLE_RATE; - bits_per_sample = DEFAULT_BITS_PER_SAMPLE; - } - - /// Default Copy Constructor - I2SConfigESP32V1(const I2SConfigESP32V1 &cfg) = default; - - /// Constructor - I2SConfigESP32V1(RxTxMode mode) { - channels = DEFAULT_CHANNELS; - sample_rate = DEFAULT_SAMPLE_RATE; - bits_per_sample = DEFAULT_BITS_PER_SAMPLE; - rx_tx_mode = mode; - switch(mode){ - case RX_MODE: - pin_data = PIN_I2S_DATA_IN; - break; - case TX_MODE: - pin_data = PIN_I2S_DATA_OUT; - break; - default: - pin_data = PIN_I2S_DATA_OUT; - pin_data_rx = PIN_I2S_DATA_IN; - break; - } - } - - /// public settings - RxTxMode rx_tx_mode = TX_MODE; - I2SFormat i2s_format = I2S_STD_FORMAT; - I2SSignalType signal_type = Digital; // e.g. the ESP32 supports analog input or output or PDM picrophones - bool is_master = true; - int port_no = 0; // processor dependent port - int pin_ws = PIN_I2S_WS; - int pin_bck = PIN_I2S_BCK; - int pin_data = -1; // rx or tx pin dependent on mode: tx pin for RXTX_MODE - int pin_data_rx = -1; // rx pin for RXTX_MODE - int pin_mck = PIN_I2S_MCK; - /// not used any more - int buffer_count = I2S_BUFFER_COUNT; - /// not used any more - int buffer_size = I2S_BUFFER_SIZE; - bool use_apll = I2S_USE_APLL; - bool auto_clear = I2S_AUTO_CLEAR; - /// Select left or right channel when channels == 1 - I2SChannelSelect channel_format = I2SChannelSelect::Default; - /// masterclock multiple (-1 = use default) - int mclk_multiple = -1; - - void logInfo(const char* source="") { - AudioInfo::logInfo(source); - LOGI("rx/tx mode: %s", RxTxModeNames[rx_tx_mode]); - LOGI("port_no: %d", port_no); - LOGI("is_master: %s", is_master ? "Master":"Slave"); - LOGI("sample rate: %d", (int) sample_rate); - LOGI("bits per sample: %d", bits_per_sample); - LOGI("number of channels: %d", channels); - LOGI("signal_type: %s", i2s_signal_types[signal_type]); - LOGI("buffer_count:%d", buffer_count); - LOGI("buffer_size:%d", buffer_size); - LOGI("auto_clear: %s",auto_clear? "true" : "false"); - if (signal_type==Digital){ - LOGI("i2s_format: %s", i2s_formats[i2s_format]); - } - if (use_apll) { - LOGI("use_apll: %s", use_apll ? "true" : "false"); - } - // LOGI("buffer_count:%d",buffer_count); - // LOGI("buffer_size:%d",buffer_size); - - if (pin_mck!=-1){ - LOGI("pin_mck: %d", pin_mck); - } - if (pin_bck!=-1){ - LOGI("pin_bck: %d", pin_bck); - } - if (pin_ws!=-1){ - LOGI("pin_ws: %d", pin_ws); - } - if (pin_data!=-1){ - LOGI("pin_data: %d", pin_data); - } - if (pin_data_rx!=-1){ - LOGI("pin_data_rx: %d", pin_data_rx); - } - } -}; - -using I2SConfig = I2SConfigESP32V1; - -} - diff --git a/src/AudioTools/CoreAudio/AudioI2S/I2SConfigStd.h b/src/AudioTools/CoreAudio/AudioI2S/I2SConfigStd.h deleted file mode 100644 index fd06840824..0000000000 --- a/src/AudioTools/CoreAudio/AudioI2S/I2SConfigStd.h +++ /dev/null @@ -1,105 +0,0 @@ -#pragma once -#include "AudioToolsConfig.h" -#include "AudioTools/CoreAudio/AudioTypes.h" - -#ifndef PIN_I2S_MCK -# define PIN_I2S_MCK -1 -#endif - -namespace audio_tools { - -/** - * @brief Configuration for i2s - * @ingroup platform - * @author Phil Schatzmann - * @copyright GPLv3 - */ -class I2SConfigStd : public AudioInfo { - public: - - I2SConfigStd() = default; - /// Default Copy Constructor - I2SConfigStd(const I2SConfigStd &cfg) = default; - - /// Constructor - I2SConfigStd(RxTxMode mode) { - channels = DEFAULT_CHANNELS; - sample_rate = DEFAULT_SAMPLE_RATE; - bits_per_sample = DEFAULT_BITS_PER_SAMPLE; - rx_tx_mode = mode; - -#ifndef STM32 - switch(mode){ - case RX_MODE: - pin_data = PIN_I2S_DATA_IN; - break; - case TX_MODE: - pin_data = PIN_I2S_DATA_OUT; - break; - default: - pin_data = PIN_I2S_DATA_OUT; - pin_data_rx = PIN_I2S_DATA_IN; - break; - } -#endif - } - - /// public settings - RxTxMode rx_tx_mode = TX_MODE; - bool is_master = true; - I2SFormat i2s_format = I2S_STD_FORMAT; - int buffer_count = I2S_BUFFER_COUNT; - int buffer_size = I2S_BUFFER_SIZE; - - int pin_ws = PIN_I2S_WS; - int pin_bck = PIN_I2S_BCK; - int pin_data = -1; // rx or tx pin dependent on mode: tx pin for RXTX_MODE - int pin_data_rx = -1; // rx pin for RXTX_MODE - int pin_mck = PIN_I2S_MCK; -#ifdef STM32 - int pin_alt_function = -1; -#endif - -#if defined(RP2040_HOWER) - /// materclock multiplier for RP2040: must be multiple of 64 - int mck_multiplier = 64; - I2SSignalType signal_type = Digital; // e.g. the RP2040 supports digial or PDM -#endif - -#if defined(USE_ALT_PIN_SUPPORT) - bool is_arduino_pin_numbers = true; -#endif - - void logInfo(const char* source="") { - AudioInfo::logInfo(source); - LOGI("rx/tx mode: %s", RxTxModeNames[rx_tx_mode]); - //LOGI("port_no: %d", port_no); - LOGI("is_master: %s", is_master ? "Master":"Slave"); - LOGI("sample rate: %d", (int)sample_rate); - LOGI("bits per sample: %d", (int)bits_per_sample); - LOGI("number of channels: %d", (int)channels); - LOGI("i2s_format: %s", i2s_formats[i2s_format]); - LOGI("buffer_count:%d",buffer_count); - LOGI("buffer_size:%d",buffer_size); - -#ifndef STM32 - if (pin_mck!=-1) - LOGI("pin_mck: %d", pin_mck); - if (pin_bck!=-1) - LOGI("pin_bck: %d", pin_bck); - if (pin_ws!=-1) - LOGI("pin_ws: %d", pin_ws); - if (pin_data!=-1) - LOGI("pin_data: %d", pin_data); - if (pin_data_rx!=-1 && rx_tx_mode==RXTX_MODE){ - LOGI("pin_data_rx: %d", pin_data_rx); - } -#endif - } - -}; - -using I2SConfig = I2SConfigStd; - -} - diff --git a/src/AudioTools/CoreAudio/AudioI2S/I2SESP32.h b/src/AudioTools/CoreAudio/AudioI2S/I2SESP32.h deleted file mode 100644 index 1241bdc88e..0000000000 --- a/src/AudioTools/CoreAudio/AudioI2S/I2SESP32.h +++ /dev/null @@ -1,406 +0,0 @@ -#pragma once - -#include "AudioToolsConfig.h" -#if defined(ESP32) && defined(USE_I2S) && \ - ESP_IDF_VERSION < ESP_IDF_VERSION_VAL(5, 0, 0) || \ - defined(DOXYGEN) - -#include "AudioTools/CoreAudio/AudioI2S/I2SConfig.h" -#include "driver/i2s.h" -#include "esp_system.h" - -#ifndef I2S_MCLK_MULTIPLE_DEFAULT -# define I2S_MCLK_MULTIPLE_DEFAULT ((i2s_mclk_multiple_t)0) -#endif - -#define IS_I2S_IMPLEMENTED - -namespace audio_tools { - -/** - * @brief Basic I2S API - for the ESP32. If we receive 1 channel, we expand the - * result to 2 channels. - * @ingroup platform - * @author Phil Schatzmann - * @copyright GPLv3 - */ -class I2SDriverESP32 { - friend class AnalogAudio; - friend class AudioKitStream; - - public: - /// Provides the default configuration - I2SConfigESP32 defaultConfig(RxTxMode mode) { - I2SConfigESP32 c(mode); - return c; - } - - /// Potentially updates the sample rate (if supported) - bool setAudioInfo(AudioInfo info) { - // nothing to do - if (is_started) { - if (info.equals(cfg)) return true; - if (info.equalsExSampleRate(cfg)) { - cfg.sample_rate = info.sample_rate; - LOGI("i2s_set_sample_rates: %d", info.sample_rate); - return i2s_set_sample_rates((i2s_port_t)cfg.port_no, cfg.sample_rate) == ESP_OK; - } - } else { - LOGE("not started"); - } - return false; - } - - /// starts the DAC with the default config - bool begin(RxTxMode mode) { return begin(defaultConfig(mode)); } - - /// starts the DAC with the current config - if not started yet. If I2S has - /// been started there is no action and we return true - bool begin() { return !is_started ? begin(cfg) : true; } - - /// starts the DAC - bool begin(I2SConfigESP32 cfg) { - TRACED(); - this->cfg = cfg; - switch (cfg.rx_tx_mode) { - case TX_MODE: - return begin(cfg, cfg.pin_data, I2S_PIN_NO_CHANGE); - case RX_MODE: - // usually we expet cfg.pin_data but if the used assinged rx we might - // consider this one - return begin( - cfg, I2S_PIN_NO_CHANGE, - cfg.pin_data != I2S_PIN_NO_CHANGE ? cfg.pin_data : cfg.pin_data_rx); - default: - return begin(cfg, cfg.pin_data, cfg.pin_data_rx); - } - LOGE("Did not expect go get here"); - } - - /// we assume the data is already available in the buffer - int available() { return I2S_BUFFER_COUNT * I2S_BUFFER_SIZE; } - - /// We limit the write size to the buffer size - int availableForWrite() { return I2S_BUFFER_COUNT * I2S_BUFFER_SIZE; } - - /// stops the I2C and unistalls the driver - void end() { - TRACED(); - i2s_driver_uninstall(i2s_num); - is_started = false; - } - - /// provides the actual configuration - I2SConfigESP32 config() { return cfg; } - - /// writes the data to the I2S interface - size_t writeBytes(const void *src, size_t size_bytes) { - TRACED(); - - size_t result = 0; - if (isNoChannelConversion(cfg)) { - if (i2s_write(i2s_num, src, size_bytes, &result, ticks_to_wait_write) != - ESP_OK) { - TRACEE(); - } - LOGD("i2s_write %d -> %d bytes", size_bytes, result); - } else { - result = - writeExpandChannel(i2s_num, cfg.bits_per_sample, src, size_bytes); - } - return result; - } - - size_t readBytes(void *dest, size_t size_bytes) { - size_t result = 0; - if (isNoChannelConversion(cfg)) { - if (i2s_read(i2s_num, dest, size_bytes, &result, ticks_to_wait_read) != - ESP_OK) { - TRACEE(); - } - } else if (cfg.channels == 1) { - // I2S has always 2 channels. We support to reduce it to 1 - uint8_t temp[size_bytes * 2]; - if (i2s_read(i2s_num, temp, size_bytes * 2, &result, - ticks_to_wait_read) != ESP_OK) { - TRACEE(); - } - // convert to 1 channel - switch (cfg.bits_per_sample) { - case 16: { - ChannelReducerT reducer16(1, 2); - result = reducer16.convert((uint8_t *)dest, temp, result); - } break; - case 24: { - ChannelReducerT reducer24(1, 2); - result = reducer24.convert((uint8_t *)dest, temp, result); - } break; - case 32: { - ChannelReducerT reducer32(1, 2); - result = reducer32.convert((uint8_t *)dest, temp, result); - } break; - default: - LOGE("invalid bits_per_sample: %d", cfg.bits_per_sample); - break; - } - } else { - LOGE("Invalid channels: %d", cfg.channels); - } - return result; - } - - void setWaitTimeReadMs(TickType_t ms) { - ticks_to_wait_read = pdMS_TO_TICKS(ms); - } - void setWaitTimeWriteMs(TickType_t ms) { - ticks_to_wait_write = pdMS_TO_TICKS(ms); - } - - protected: - I2SConfigESP32 cfg = defaultConfig(RX_MODE); - i2s_port_t i2s_num; - i2s_config_t i2s_config; - bool is_started = false; - TickType_t ticks_to_wait_read = portMAX_DELAY; - TickType_t ticks_to_wait_write = portMAX_DELAY; - - bool isNoChannelConversion(I2SConfigESP32 cfg) { - if (cfg.channels == 2) return true; - if (cfg.channels == 1 && cfg.channel_format == I2S_CHANNEL_FMT_ALL_RIGHT) - return true; - if (cfg.channels == 1 && cfg.channel_format == I2S_CHANNEL_FMT_ALL_LEFT) - return true; - if (cfg.channels == 1 && cfg.channel_format == I2S_CHANNEL_FMT_ONLY_RIGHT) - return true; - if (cfg.channels == 1 && cfg.channel_format == I2S_CHANNEL_FMT_ONLY_LEFT) - return true; - return false; - } - - /// starts the DAC - bool begin(I2SConfigESP32 cfg, int txPin, int rxPin) { - TRACED(); - cfg.logInfo(); - this->cfg = cfg; - this->i2s_num = (i2s_port_t)cfg.port_no; - setChannels(cfg.channels); - - i2s_config_t i2s_config_new = { - .mode = toMode(cfg), - .sample_rate = (eps32_i2s_sample_rate_type)cfg.sample_rate, - .bits_per_sample = (i2s_bits_per_sample_t)cfg.bits_per_sample, - .channel_format = (i2s_channel_fmt_t)cfg.channel_format, - .communication_format = toCommFormat(cfg.i2s_format), - .intr_alloc_flags = 0, // default interrupt priority - .dma_buf_count = cfg.buffer_count, - .dma_buf_len = cfg.buffer_size, - .use_apll = (bool)cfg.use_apll, - .tx_desc_auto_clear = cfg.auto_clear, -#if ESP_IDF_VERSION_MAJOR >= 4 - .fixed_mclk = (int)(cfg.fixed_mclk > 0 ? cfg.fixed_mclk : 0), - .mclk_multiple = I2S_MCLK_MULTIPLE_DEFAULT, - .bits_per_chan = I2S_BITS_PER_CHAN_DEFAULT, -#endif - }; - i2s_config = i2s_config_new; - - // We make sure that we can reconfigure - if (is_started) { - end(); - LOGD("%s", "I2S restarting"); - } - - // setup config - LOGD("i2s_driver_install"); - if (i2s_driver_install(i2s_num, &i2s_config, 0, NULL) != ESP_OK) { - LOGE("%s - %s", __func__, "i2s_driver_install"); - } - - // setup pin config - if (this->cfg.signal_type == Digital || this->cfg.signal_type == PDM) { - i2s_pin_config_t pin_config = { -#if ESP_IDF_VERSION > ESP_IDF_VERSION_VAL(4, 4, 0) - .mck_io_num = cfg.pin_mck, -#endif - .bck_io_num = cfg.pin_bck, - .ws_io_num = cfg.pin_ws, - .data_out_num = txPin, - .data_in_num = rxPin}; - - LOGD("i2s_set_pin"); - if (i2s_set_pin(i2s_num, &pin_config) != ESP_OK) { - LOGE("%s - %s", __func__, "i2s_set_pin"); - } - } else { - LOGD("Using built in DAC"); - // for internal DAC, this will enable both of the internal channels - i2s_set_pin(i2s_num, NULL); - } - - // clear initial buffer - LOGD("i2s_zero_dma_buffer"); - i2s_zero_dma_buffer(i2s_num); - - is_started = true; - LOGD("%s - %s", __func__, "started"); - return true; - } - - // update the cfg.i2s.channel_format based on the number of channels - void setChannels(int channels) { cfg.channels = channels; } - - /// writes the data by making sure that we send 2 channels - size_t writeExpandChannel(i2s_port_t i2s_num, const int bits_per_sample, - const void *src, size_t size_bytes) { - size_t result = 0; - int j; - switch (bits_per_sample) { - case 8: - for (j = 0; j < size_bytes; j++) { - int8_t frame[2]; - int8_t *data = (int8_t *)src; - frame[0] = data[j]; - frame[1] = data[j]; - size_t result_call = 0; - if (i2s_write(i2s_num, frame, sizeof(int8_t) * 2, &result_call, - ticks_to_wait_write) != ESP_OK) { - TRACEE(); - } else { - result += result_call; - } - } - break; - - case 16: - for (j = 0; j < size_bytes / 2; j++) { - int16_t frame[2]; - int16_t *data = (int16_t *)src; - frame[0] = data[j]; - frame[1] = data[j]; - size_t result_call = 0; - if (i2s_write(i2s_num, frame, sizeof(int16_t) * 2, &result_call, - ticks_to_wait_write) != ESP_OK) { - TRACEE(); - } else { - result += result_call; - } - } - break; - - case 24: - for (j = 0; j < size_bytes / 4; j++) { - int24_t frame[2]; - int24_t *data = (int24_t *)src; - frame[0] = data[j]; - frame[1] = data[j]; - size_t result_call = 0; - if (i2s_write(i2s_num, frame, sizeof(int24_t) * 2, &result_call, - ticks_to_wait_write) != ESP_OK) { - TRACEE(); - } else { - result += result_call; - } - } - break; - - case 32: - for (j = 0; j < size_bytes / 4; j++) { - int32_t frame[2]; - int32_t *data = (int32_t *)src; - frame[0] = data[j]; - frame[1] = data[j]; - size_t result_call = 0; - if (i2s_write(i2s_num, frame, sizeof(int32_t) * 2, &result_call, - ticks_to_wait_write) != ESP_OK) { - TRACEE(); - } else { - result += result_call; - } - } - break; - } - return size_bytes; - } - -#pragma GCC diagnostic push -#pragma GCC diagnostic ignored "-Wdeprecated-declarations" - - // determines the i2s_comm_format_t - by default we use - // I2S_COMM_FORMAT_STAND_I2S - i2s_comm_format_t toCommFormat(I2SFormat mode) { - switch (mode) { - case I2S_PHILIPS_FORMAT: - case I2S_STD_FORMAT: - return (i2s_comm_format_t)I2S_COMM_FORMAT_STAND_I2S; - case I2S_LEFT_JUSTIFIED_FORMAT: - case I2S_MSB_FORMAT: - return (i2s_comm_format_t)(I2S_COMM_FORMAT_I2S | - I2S_COMM_FORMAT_I2S_MSB); - case I2S_RIGHT_JUSTIFIED_FORMAT: - case I2S_LSB_FORMAT: - return (i2s_comm_format_t)(I2S_COMM_FORMAT_I2S | - I2S_COMM_FORMAT_I2S_LSB); - case I2S_PCM: - return (i2s_comm_format_t)I2S_COMM_FORMAT_STAND_PCM_SHORT; - - default: - LOGE("unsupported mode"); - return (i2s_comm_format_t)I2S_COMM_FORMAT_STAND_I2S; - } - } -#pragma GCC diagnostic pop - - int getModeDigital(I2SConfigESP32 &cfg) { - int i2s_format = cfg.is_master ? I2S_MODE_MASTER : I2S_MODE_SLAVE; - int i2s_rx_tx = 0; - switch (cfg.rx_tx_mode) { - case TX_MODE: - i2s_rx_tx = I2S_MODE_TX; - break; - case RX_MODE: - i2s_rx_tx = I2S_MODE_RX; - break; - case RXTX_MODE: - i2s_rx_tx = I2S_MODE_RX | I2S_MODE_TX; - break; - default: - LOGE("Undefined rx_tx_mode: %d", cfg.rx_tx_mode); - } - return (i2s_format | i2s_rx_tx); - } - - // determines the i2s_format_t - i2s_mode_t toMode(I2SConfigESP32 &cfg) { - i2s_mode_t mode = (i2s_mode_t)(I2S_MODE_MASTER | I2S_MODE_TX | I2S_MODE_RX); - switch (cfg.signal_type) { - case Digital: - mode = (i2s_mode_t)getModeDigital(cfg); - break; - - case PDM: - mode = (i2s_mode_t)(getModeDigital(cfg) | I2S_MODE_PDM); - break; - - case Analog: -#if defined(USE_ANALOG) - mode = (i2s_mode_t)(cfg.rx_tx_mode ? I2S_MODE_DAC_BUILT_IN - : I2S_MODE_ADC_BUILT_IN); -#else - LOGE("mode not supported"); -#endif - break; - - default: - LOGW("signal_type undefined"); - mode = (i2s_mode_t)(I2S_MODE_MASTER | I2S_MODE_TX); - break; - } - return mode; - } -}; - -using I2SDriver = I2SDriverESP32; - -} // namespace audio_tools - -#endif \ No newline at end of file diff --git a/src/AudioTools/CoreAudio/AudioI2S/I2SESP32V1.h b/src/AudioTools/CoreAudio/AudioI2S/I2SESP32V1.h deleted file mode 100644 index 212157d82c..0000000000 --- a/src/AudioTools/CoreAudio/AudioI2S/I2SESP32V1.h +++ /dev/null @@ -1,606 +0,0 @@ -#pragma once - -#include "AudioToolsConfig.h" -#if defined(ESP32) && defined(USE_I2S) && \ - ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 0, 0) || \ - defined(DOXYGEN) - -#include "AudioTools/CoreAudio/AudioI2S/I2SConfig.h" -#include "driver/i2s_pdm.h" -#include "driver/i2s_std.h" -#include "driver/i2s_tdm.h" -#include "esp_system.h" - -#define IS_I2S_IMPLEMENTED - -namespace audio_tools { - -/** - * @brief Basic I2S API for the ESP32 (using the new API). - * https://docs.espressif.com/projects/esp-idf/en/v5.0.1/esp32/api-reference/peripherals/i2s.html#i2s-communication-mode - * @ingroup platform - * @author Phil Schatzmann - * @copyright GPLv3 - */ -class I2SDriverESP32V1 { - public: - /// Provides the default configuration - I2SConfigESP32V1 defaultConfig(RxTxMode mode) { - I2SConfigESP32V1 c(mode); - return c; - } - /// Potentially updates the sample rate (if supported) - bool setAudioInfo(AudioInfo info) { - // nothing to do - if (is_started) { - if (info.equals(cfg)) return true; - if (info.equalsExSampleRate(cfg)) { - cfg.sample_rate = info.sample_rate; - LOGI("i2s_set_sample_rates: %d", (int)info.sample_rate); - return getDriver(cfg).changeSampleRate(cfg, rx_chan, tx_chan); - } - } else { - LOGE("not started"); - } - return false; - } - - /// starts the DAC with the default config - bool begin(RxTxMode mode) { return begin(defaultConfig(mode)); } - - /// starts the DAC with the current config - if not started yet. If I2S has - /// been started there is no action and we return true - bool begin() { return (!is_started) ? begin(cfg) : true; } - - /// starts the DAC - bool begin(I2SConfigESP32V1 cfg) { - TRACED(); - this->cfg = cfg; - - // stop if it is already open - if (is_started) end(); - - switch (cfg.rx_tx_mode) { - case TX_MODE: - return begin(cfg, cfg.pin_data, I2S_GPIO_UNUSED); - case RX_MODE: - // usually we expet cfg.pin_data but if the used assinged rx we might - // consider this one - return begin(cfg, I2S_GPIO_UNUSED, - cfg.pin_data_rx != I2S_GPIO_UNUSED ? cfg.pin_data_rx - : cfg.pin_data); - default: - return begin(cfg, cfg.pin_data, cfg.pin_data_rx); - } - LOGE("Did not expect go get here"); - } - - /// we assume the data is already available in the buffer - int available() { return I2S_BUFFER_COUNT * I2S_BUFFER_SIZE; } - - /// We limit the write size to the buffer size - int availableForWrite() { return I2S_BUFFER_COUNT * I2S_BUFFER_SIZE; } - - /// stops the I2C and unistalls the driver - void end() { - TRACED(); - if (rx_chan != nullptr) { - i2s_channel_disable(rx_chan); - i2s_del_channel(rx_chan); - rx_chan = nullptr; - } - if (tx_chan != nullptr) { - i2s_channel_disable(tx_chan); - i2s_del_channel(tx_chan); - tx_chan = nullptr; - } - - is_started = false; - } - - /// provides the actual configuration - I2SConfigESP32V1 config() { return cfg; } - - /// writes the data to the I2S interface - size_t writeBytes(const void *src, size_t size_bytes) { - TRACED(); - size_t result; - assert(tx_chan != nullptr); - if (i2s_channel_write(tx_chan, src, size_bytes, &result, - ticks_to_wait_write) != ESP_OK) { - TRACEE(); - } - return result; - } - - size_t readBytes(void *dest, size_t size_bytes) { - size_t result = 0; - if (i2s_channel_read(rx_chan, dest, size_bytes, &result, - ticks_to_wait_read) != ESP_OK) { - TRACEE(); - } - return result; - } - - void setWaitTimeReadMs(TickType_t ms) { - ticks_to_wait_read = pdMS_TO_TICKS(ms); - } - void setWaitTimeWriteMs(TickType_t ms) { - ticks_to_wait_write = pdMS_TO_TICKS(ms); - } - - protected: - I2SConfigESP32V1 cfg = defaultConfig(RXTX_MODE); - i2s_std_config_t i2s_config; - i2s_chan_handle_t tx_chan = nullptr; // I2S tx channel handler - i2s_chan_handle_t rx_chan = nullptr; // I2S rx channel handler - bool is_started = false; - TickType_t ticks_to_wait_read = portMAX_DELAY; - TickType_t ticks_to_wait_write = portMAX_DELAY; - - struct DriverCommon { - virtual bool startChannels(I2SConfigESP32V1 &cfg, - i2s_chan_handle_t &tx_chan, - i2s_chan_handle_t &rx_chan, int txPin, - int rxPin) = 0; - - virtual i2s_chan_config_t getChannelConfig(I2SConfigESP32V1 &cfg) = 0; - // changes the sample rate - virtual bool changeSampleRate(I2SConfigESP32V1 &cfg, - i2s_chan_handle_t &tx_chan, - i2s_chan_handle_t &rx_chan) { - return false; - } - - protected: - /// 24 bits are stored in a 32 bit integer - int get_bits_eff(int bits) { return (bits == 24) ? 32 : bits; } - }; - - struct DriverI2S : public DriverCommon { - bool startChannels(I2SConfigESP32V1 &cfg, i2s_chan_handle_t &tx_chan, - i2s_chan_handle_t &rx_chan, int txPin, int rxPin) { - TRACED(); - LOGI("tx: %d, rx: %d", txPin, rxPin); - i2s_std_config_t std_cfg; - std_cfg.clk_cfg = getClockConfig(cfg); - std_cfg.slot_cfg = getSlotConfig(cfg); - std_cfg.gpio_cfg = { - .mclk = (gpio_num_t)cfg.pin_mck, - .bclk = (gpio_num_t)cfg.pin_bck, - .ws = (gpio_num_t)cfg.pin_ws, - .dout = (gpio_num_t)txPin, - .din = (gpio_num_t)rxPin, - .invert_flags = - { - .mclk_inv = false, - .bclk_inv = false, - .ws_inv = false, - }, - }; - - - if (cfg.rx_tx_mode == RXTX_MODE || cfg.rx_tx_mode == TX_MODE) { - if (i2s_channel_init_std_mode(tx_chan, &std_cfg) != ESP_OK) { - LOGE("i2s_channel_init_std_mode %s", "tx"); - return false; - } - if (i2s_channel_enable(tx_chan) != ESP_OK) { - LOGE("i2s_channel_enable %s", "tx"); - return false; - } - } - - if (cfg.rx_tx_mode == RXTX_MODE || cfg.rx_tx_mode == RX_MODE) { - if (i2s_channel_init_std_mode(rx_chan, &std_cfg) != ESP_OK) { - LOGE("i2s_channel_init_std_mode %s", "rx"); - return false; - } - if (i2s_channel_enable(rx_chan) != ESP_OK) { - LOGE("i2s_channel_enable %s", "rx"); - return false; - } - } - - LOGD("%s - %s", __func__, "started"); - return true; - } - - protected: - i2s_std_slot_config_t getSlotConfig(I2SConfigESP32V1 &cfg) { - TRACED(); - i2s_std_slot_config_t result; - switch (cfg.i2s_format) { - case I2S_LEFT_JUSTIFIED_FORMAT: - case I2S_MSB_FORMAT: - result = I2S_STD_MSB_SLOT_DEFAULT_CONFIG( - (i2s_data_bit_width_t)cfg.bits_per_sample, - (i2s_slot_mode_t)cfg.channels); - break; - case I2S_PCM: - result = I2S_STD_PCM_SLOT_DEFAULT_CONFIG( - (i2s_data_bit_width_t)cfg.bits_per_sample, - (i2s_slot_mode_t)cfg.channels); - break; - default: - result = I2S_STD_PHILIPS_SLOT_DEFAULT_CONFIG( - (i2s_data_bit_width_t)cfg.bits_per_sample, - (i2s_slot_mode_t)cfg.channels); - } - - // Update slot_mask if only one channel - if (cfg.channels == 1) { - switch (cfg.channel_format) { - case I2SChannelSelect::Left: - result.slot_mask = I2S_STD_SLOT_LEFT; - break; - case I2SChannelSelect::Right: - result.slot_mask = I2S_STD_SLOT_RIGHT; - break; - default: - LOGW("Using channel_format: I2SChannelSelect::Left for mono"); - result.slot_mask = I2S_STD_SLOT_LEFT; - break; - } - } - - return result; - } - - i2s_chan_config_t getChannelConfig(I2SConfigESP32V1 &cfg) { - TRACED(); - i2s_chan_config_t result = I2S_CHANNEL_DEFAULT_CONFIG( - (i2s_port_t)cfg.port_no, - cfg.is_master ? I2S_ROLE_MASTER : I2S_ROLE_SLAVE); - // use the legicy size parameters for frame num - int size = cfg.buffer_size * cfg.buffer_count; - int frame_size = get_bits_eff(cfg.bits_per_sample) * cfg.channels / 8; - if (size > 0) result.dma_frame_num = size / frame_size; - LOGI("dma_frame_num: %d", (int)result.dma_frame_num); - result.auto_clear = cfg.auto_clear; - return result; - } - - i2s_std_clk_config_t getClockConfig(I2SConfigESP32V1 &cfg) { - TRACED(); - i2s_std_clk_config_t clk_cfg;// = I2S_STD_CLK_DEFAULT_CONFIG((uint32_t)cfg.sample_rate); - memset(&clk_cfg, 0, sizeof(i2s_std_clk_config_t)); - clk_cfg.sample_rate_hz = cfg.sample_rate; - clk_cfg.clk_src = getClockSource(cfg); - // clk_cfg.ext_clk_freq_hz = 0; - - if (cfg.mclk_multiple > 0) { - clk_cfg.mclk_multiple = (i2s_mclk_multiple_t)cfg.mclk_multiple; - LOGI("mclk_multiple=%d", clk_cfg.mclk_multiple); - } else { - if (cfg.bits_per_sample == 24) { - // mclk_multiple' should be the multiple of 3 while using 24-bit - clk_cfg.mclk_multiple = I2S_MCLK_MULTIPLE_384; - LOGI("mclk_multiple=384"); - } else { - // when use_appll is true, the multiple of 128 gives 256kHz - clk_cfg.mclk_multiple = cfg.use_apll ? I2S_MCLK_MULTIPLE_128 : I2S_MCLK_MULTIPLE_256; - LOGI("mclk_multiple=%d", clk_cfg.mclk_multiple); - } - } - - return clk_cfg; - } - - /// select clock source dependent on is_master and use_apll - soc_periph_i2s_clk_src_t getClockSource(I2SConfigESP32V1 &cfg){ - soc_periph_i2s_clk_src_t result = I2S_CLK_SRC_DEFAULT; - // use mclk pin as input in slave mode if supported - if (cfg.pin_mck != -1 && !cfg.is_master) { -#if SOC_I2S_HW_VERSION_2 - LOGI("pin_mclk is input"); - result = I2S_CLK_SRC_EXTERNAL; - return result; -#else - LOGE("pin_mclk as input not supported"); -#endif - } - - // select APLL clock if possible - if (cfg.use_apll) { - // select clock source -#if SOC_I2S_SUPPORTS_APLL - result = I2S_CLK_SRC_APLL; - LOGI("clk_src is I2S_CLK_SRC_APLL"); -#elif SOC_I2S_SUPPORTS_PLL_F160M - result = I2S_CLK_SRC_PLL_160M; - LOGI("clk_src is I2S_CLK_SRC_PLL_160M"); -#endif - } - - return result; - } - - bool changeSampleRate(I2SConfigESP32V1 &cfg, i2s_chan_handle_t &tx_chan, - i2s_chan_handle_t &rx_chan) override { - bool rc = false; - auto clock_cfg = getClockConfig(cfg); - if (tx_chan != nullptr) { - i2s_channel_disable(tx_chan); - rc = i2s_channel_reconfig_std_clock(tx_chan, &clock_cfg) == ESP_OK; - i2s_channel_enable(tx_chan); - } - if (rx_chan != nullptr) { - i2s_channel_disable(rx_chan); - rc = i2s_channel_reconfig_std_clock(rx_chan, &clock_cfg) == ESP_OK; - i2s_channel_enable(rx_chan); - } - return rc; - } - - } i2s; - -#ifdef USE_PDM - - struct DriverPDM : public DriverCommon { - bool startChannels(I2SConfigESP32V1 &cfg, i2s_chan_handle_t &tx_chan, - i2s_chan_handle_t &rx_chan, int txPin, int rxPin) { - if (cfg.rx_tx_mode == TX_MODE) { - return startTX(cfg, tx_chan, txPin); - } else if (cfg.rx_tx_mode == RX_MODE) { - return startRX(cfg, rx_chan, rxPin); - } - LOGE("Only RX and TX is supported for PDM") - return false; - } - - protected: - i2s_pdm_tx_slot_config_t getTxSlotConfig(I2SConfigESP32V1 &cfg) { - return I2S_PDM_TX_SLOT_DEFAULT_CONFIG( - (i2s_data_bit_width_t)cfg.bits_per_sample, - (i2s_slot_mode_t)cfg.channels); - } - - i2s_chan_config_t getChannelConfig(I2SConfigESP32V1 &cfg) { - return I2S_CHANNEL_DEFAULT_CONFIG( - (i2s_port_t)cfg.port_no, - cfg.is_master ? I2S_ROLE_MASTER : I2S_ROLE_SLAVE); - } - - i2s_pdm_tx_clk_config_t getTxClockConfig(I2SConfigESP32V1 &cfg) { - return I2S_PDM_TX_CLK_DEFAULT_CONFIG((uint32_t)cfg.sample_rate); - } - - bool startTX(I2SConfigESP32V1 &cfg, i2s_chan_handle_t &tx_chan, int txPin) { - i2s_pdm_tx_config_t pdm_tx_cfg = { - .clk_cfg = getTxClockConfig(cfg), - .slot_cfg = getTxSlotConfig(cfg), - .gpio_cfg = - { - .clk = (gpio_num_t)cfg.pin_bck, - .dout = (gpio_num_t)txPin, - .invert_flags = - { - .clk_inv = false, - }, - }, - }; - - if (i2s_channel_init_pdm_tx_mode(tx_chan, &pdm_tx_cfg) != ESP_OK) { - LOGE("i2s_channel_init_pdm_tx_mode %s", "tx"); - return false; - } - if (i2s_channel_enable(tx_chan) != ESP_OK) { - LOGE("i2s_channel_enable %s", "tx"); - return false; - } - return true; - } - -#if defined(USE_PDM_RX) - i2s_pdm_rx_slot_config_t getRxSlotConfig(I2SConfigESP32V1 &cfg) { - return I2S_PDM_RX_SLOT_DEFAULT_CONFIG( - (i2s_data_bit_width_t)cfg.bits_per_sample, - (i2s_slot_mode_t)cfg.channels); - } - i2s_pdm_rx_clk_config_t getRxClockConfig(I2SConfigESP32V1 &cfg) { - return I2S_PDM_RX_CLK_DEFAULT_CONFIG((uint32_t)cfg.sample_rate); - } - bool startRX(I2SConfigESP32V1 &cfg, i2s_chan_handle_t &rx_chan, int rxPin) { - i2s_pdm_rx_config_t pdm_rx_cfg = { - .clk_cfg = getRxClockConfig(cfg), - .slot_cfg = getRxSlotConfig(cfg), - .gpio_cfg = - { - .clk = (gpio_num_t)cfg.pin_bck, - .din = (gpio_num_t)rxPin, - .invert_flags = - { - .clk_inv = false, - }, - }, - }; - - if (i2s_channel_init_pdm_rx_mode(rx_chan, &pdm_rx_cfg) != ESP_OK) { - LOGE("i2s_channel_init_pdm_rx_mode %s", "rx"); - return false; - } - if (i2s_channel_enable(rx_chan) != ESP_OK) { - LOGE("i2s_channel_enable %s", "tx"); - return false; - } - return true; - } -#else - bool startRX(I2SConfigESP32V1 &cfg, i2s_chan_handle_t &rx_chan, int rxPin) { - LOGE("PDM RX not supported"); - return false; - } -#endif - } pdm; - -#endif - -#ifdef USE_TDM - // example at - // https://github.com/espressif/esp-idf/blob/v5.3-dev/examples/peripherals/i2s/i2s_basic/i2s_tdm/main/i2s_tdm_example_main.c - struct DriverTDM : public DriverCommon { - bool startChannels(I2SConfigESP32V1 &cfg, i2s_chan_handle_t &tx_chan, - i2s_chan_handle_t &rx_chan, int txPin, int rxPin) { - i2s_tdm_config_t tdm_cfg = { - .clk_cfg = getClockConfig(cfg), - .slot_cfg = getSlotConfig(cfg), - .gpio_cfg = - { - .mclk = (gpio_num_t)cfg.pin_mck, - .bclk = (gpio_num_t)cfg.pin_bck, - .ws = (gpio_num_t)cfg.pin_ws, - .dout = (gpio_num_t)txPin, - .din = (gpio_num_t)rxPin, - .invert_flags = - { - .mclk_inv = false, - .bclk_inv = false, - .ws_inv = false, - }, - }, - }; - - if (cfg.rx_tx_mode == TX_MODE || cfg.rx_tx_mode == RXTX_MODE) { - if (i2s_channel_init_tdm_mode(tx_chan, &tdm_cfg) != ESP_OK) { - LOGE("i2s_channel_init_tdm_tx_mode %s", "tx"); - return false; - } - } - if (cfg.rx_tx_mode == RX_MODE || cfg.rx_tx_mode == RXTX_MODE) { - if (i2s_channel_init_tdm_mode(rx_chan, &tdm_cfg) != ESP_OK) { - LOGE("i2s_channel_init_tdm_tx_mode %s", "rx"); - return false; - } - } - return true; - } - - protected: - i2s_tdm_slot_config_t getSlotConfig(I2SConfigESP32V1 &cfg) { - int slots = 0; - for (int j = 0; j < cfg.channels; j++) { - slots |= 1 << j; - } - // setup default format - i2s_tdm_slot_config_t slot_cfg = I2S_TDM_PHILIPS_SLOT_DEFAULT_CONFIG( - (i2s_data_bit_width_t)cfg.bits_per_sample, I2S_SLOT_MODE_STEREO, - (i2s_tdm_slot_mask_t)slots); - - switch (cfg.i2s_format) { - case I2S_RIGHT_JUSTIFIED_FORMAT: - case I2S_LSB_FORMAT: - case I2S_PHILIPS_FORMAT: - case I2S_STD_FORMAT: - slot_cfg = I2S_TDM_PHILIPS_SLOT_DEFAULT_CONFIG( - (i2s_data_bit_width_t)cfg.bits_per_sample, I2S_SLOT_MODE_STEREO, - (i2s_tdm_slot_mask_t)slots); - break; - case I2S_LEFT_JUSTIFIED_FORMAT: - case I2S_MSB_FORMAT: - slot_cfg = I2S_TDM_MSB_SLOT_DEFAULT_CONFIG( - (i2s_data_bit_width_t)cfg.bits_per_sample, I2S_SLOT_MODE_STEREO, - (i2s_tdm_slot_mask_t)slots); - break; - case I2S_PCM: - slot_cfg = I2S_TDM_PCM_LONG_SLOT_DEFAULT_CONFIG( - (i2s_data_bit_width_t)cfg.bits_per_sample, I2S_SLOT_MODE_STEREO, - (i2s_tdm_slot_mask_t)slots); - break; - default: - LOGE("TDM: Unsupported format"); - } - - return slot_cfg; - } - - i2s_chan_config_t getChannelConfig(I2SConfigESP32V1 &cfg) { - return I2S_CHANNEL_DEFAULT_CONFIG( - (i2s_port_t)cfg.port_no, - cfg.is_master ? I2S_ROLE_MASTER : I2S_ROLE_SLAVE); - } - - i2s_tdm_clk_config_t getClockConfig(I2SConfigESP32V1 &cfg) { - return I2S_TDM_CLK_DEFAULT_CONFIG((uint32_t)cfg.sample_rate); - } - - } tdm; - -#endif - - /// -> protected methods from I2SDriverESP32V1 - - /// starts I2S - bool begin(I2SConfigESP32V1 cfg, int txPin, int rxPin) { - TRACED(); - cfg.logInfo(); - this->cfg = cfg; - if (cfg.channels <= 0 || cfg.channels > 2) { - LOGE("invalid channels: %d", cfg.channels); - return false; - } - - DriverCommon &driver = getDriver(cfg); - if (!newChannels(cfg, driver)) { - end(); - return false; - } - - is_started = driver.startChannels(cfg, tx_chan, rx_chan, txPin, rxPin); - if (!is_started) { - end(); - LOGE("Channels not started"); - } - return is_started; - } - - bool newChannels(I2SConfigESP32V1 &cfg, DriverCommon &driver) { - i2s_chan_config_t chan_cfg = driver.getChannelConfig(cfg); - switch (cfg.rx_tx_mode) { - case RX_MODE: - if (i2s_new_channel(&chan_cfg, NULL, &rx_chan) != ESP_OK) { - LOGE("i2s_channel"); - return false; - } - break; - case TX_MODE: - if (i2s_new_channel(&chan_cfg, &tx_chan, NULL) != ESP_OK) { - LOGE("i2s_channel"); - return false; - } - break; - default: - if (i2s_new_channel(&chan_cfg, &tx_chan, &rx_chan) != ESP_OK) { - LOGE("i2s_channel"); - return false; - } - } - return true; - } - - DriverCommon &getDriver(I2SConfigESP32V1 &cfg) { - switch (cfg.signal_type) { - case Digital: - return i2s; -#ifdef USE_PDM - case Analog: - case PDM: - return pdm; -#endif -#ifdef USE_TDM - case TDM: - return tdm; -#endif - default: - break; - } - LOGE("Unsupported signal_type"); - return i2s; - } -}; - -using I2SDriver = I2SDriverESP32V1; - -} // namespace audio_tools - -#endif \ No newline at end of file diff --git a/src/AudioTools/CoreAudio/AudioI2S/I2SESP8266.h b/src/AudioTools/CoreAudio/AudioI2S/I2SESP8266.h deleted file mode 100644 index e7b04f93da..0000000000 --- a/src/AudioTools/CoreAudio/AudioI2S/I2SESP8266.h +++ /dev/null @@ -1,181 +0,0 @@ -#pragma once - -#ifdef ESP8266 -#include - -#include "AudioTools/CoreAudio/AudioI2S/I2SConfig.h" -#include "AudioTools/CoreAudio/AudioLogger.h" - -#define IS_I2S_IMPLEMENTED - -namespace audio_tools { - -/** - * @brief Basic I2S API - for the ESP8266 - * Only 16 bits are supported ! - * @ingroup platform - * @author Phil Schatzmann - * @copyright GPLv3 - */ -class I2SDriverESP8266 { - friend class I2SStream; - - public: - /// Provides the default configuration - I2SConfigStd defaultConfig(RxTxMode mode) { - I2SConfigStd c(mode); - return c; - } - - /// Potentially updates the sample rate (if supported) - bool setAudioInfo(AudioInfo) { return false; } - - /// starts the DAC with the default config - bool begin(RxTxMode mode = TX_MODE) { return begin(defaultConfig(mode)); } - - /// starts the DAC - bool begin(I2SConfigStd cfg) { - this->cfg = cfg; - i2s_set_rate(cfg.sample_rate); - cfg.bits_per_sample = 16; - if (!i2s_rxtx_begin(cfg.rx_tx_mode == RX_MODE, cfg.rx_tx_mode == TX_MODE)) { - LOGE("i2s_rxtx_begin failed"); - return false; - } - return true; - } - - /// stops the I2C and unistalls the driver - void end() { i2s_end(); } - - /// we assume the data is already available in the buffer - int available() { return I2S_BUFFER_COUNT * I2S_BUFFER_SIZE; } - - /// We limit the write size to the buffer size - int availableForWrite() { return I2S_BUFFER_COUNT * I2S_BUFFER_SIZE; } - - /// provides the actual configuration - I2SConfigStd config() { return cfg; } - - protected: - I2SConfigStd cfg; - - /// writes the data to the I2S interface - size_t writeBytes(const void *src, size_t size_bytes) { - size_t result = 0; - size_t frame_size = cfg.channels * (cfg.bits_per_sample / 8); - uint16_t frame_count = size_bytes / frame_size; - if (cfg.channels == 2 && cfg.bits_per_sample == 16) { - result = i2s_write_buffer((int16_t *)src, frame_count) * frame_size; - } else { - result = writeExt(src, size_bytes); - } - return result; - } - - /// writes the data by making shure that we send 2 channels 16 bit data - size_t writeExt(const void *src, size_t size_bytes) { - size_t result = 0; - int j; - - switch (cfg.bits_per_sample) { - case 8: - for (j = 0; j < size_bytes; j += cfg.channels) { - int8_t *data = (int8_t *)src; - int16_t frame[2]; - frame[0] = data[j] * 256; - if (cfg.channels == 1) { - frame[1] = data[j] * 256; - } else { - frame[1] = data[j + 1] * 256; - } - uint32_t *frame_ptr = (uint32_t *)frame; - if (i2s_write_sample(*frame_ptr)) { - result += 2; - } - } - break; - - case 16: - for (j = 0; j < size_bytes / 2; j += cfg.channels) { - int16_t *data = (int16_t *)src; - int16_t frame[2]; - frame[0] = data[j]; - if (cfg.channels == 1) { - frame[1] = data[j]; - } else { - frame[1] = data[j + 1]; - } - uint32_t *frame_ptr = (uint32_t *)frame; - if (i2s_write_sample(*frame_ptr)) { - result += 2; - } - } - break; - - case 24: - for (j = 0; j < size_bytes / 3; j += cfg.channels) { - int24_t *data = (int24_t *)src; - int24_t frame[2]; - int32_t value = data[j]; - frame[0] = value; - if (cfg.channels == 1) { - frame[1] = value; - } else { - value = data[j + 1]; - frame[1] = value / 256; - } - uint32_t *frame_ptr = (uint32_t *)frame; - if (i2s_write_sample(*frame_ptr)) { - result += 2; - } - } - break; - - case 32: - for (j = 0; j < size_bytes / 4; j += cfg.channels) { - int32_t *data = (int32_t *)src; - int32_t frame[2]; - frame[0] = data[j] / 65538; - if (cfg.channels == 1) { - frame[1] = data[j] / 65538; - } else { - frame[1] = data[j + 1] / 65538; - } - uint32_t *frame_ptr = (uint32_t *)frame; - if (i2s_write_sample(*frame_ptr)) { - result += 2; - } - } - break; - } - return result; - } - - /// reads the data from the I2S interface - size_t readBytes(void *dest, size_t size_bytes) { - size_t result_bytes = 0; - uint16_t frame_size = cfg.channels * (cfg.bits_per_sample / 8); - size_t frames = size_bytes / frame_size; - int16_t *ptr = (int16_t *)dest; - - for (int j = 0; j < frames; j++) { - int16_t *left = ptr; - int16_t *right = ptr + 1; - bool ok = i2s_read_sample( - left, right, false); // RX data returned in both 16-bit outputs. - if (!ok) { - break; - } - ptr += 2; - result_bytes += frame_size; - } - return result_bytes; - } -}; - -using I2SDriver = I2SDriverESP8266; - -} // namespace audio_tools - -#endif diff --git a/src/AudioTools/CoreAudio/AudioI2S/I2SNanoSenseBLE.h b/src/AudioTools/CoreAudio/AudioI2S/I2SNanoSenseBLE.h deleted file mode 100644 index a58e6850bd..0000000000 --- a/src/AudioTools/CoreAudio/AudioI2S/I2SNanoSenseBLE.h +++ /dev/null @@ -1,460 +0,0 @@ -#pragma once - -#include "AudioToolsConfig.h" - -#if defined(USE_NANO33BLE) - -#include "AudioTools/CoreAudio/AudioI2S/I2SConfig.h" -#include "AudioTools/CoreAudio/AudioLogger.h" -#include "AudioTools/CoreAudio/AudioTypes.h" -#include "AudioTools/CoreAudio/Buffers.h" - -#define IS_I2S_IMPLEMENTED - -namespace audio_tools { - -static int i2s_buffer_size = 0; -static BaseBuffer *p_i2s_buffer = nullptr; -static uint8_t *p_i2s_array = nullptr; // current array -static uint8_t *p_i2s_array_1 = nullptr; // array 1 -static uint8_t *p_i2s_array_2 = nullptr; // array 2 -static uint32_t i2s_underflow_count = 0; -// alternative API -static Stream *p_nano_ble_stream = nullptr; - -/** - * @brief Mapping Frequency constants to available frequencies - */ -struct Nano_BLE_freq_info { - int id; - float freq; // in mhz -}; - -static const Nano_BLE_freq_info freq_table[] = { - {I2S_CONFIG_MCKFREQ_MCKFREQ_32MDIV8, 32.0 / 8}, - {I2S_CONFIG_MCKFREQ_MCKFREQ_32MDIV10, 32 / 10}, - {I2S_CONFIG_MCKFREQ_MCKFREQ_32MDIV11, 32.0 / 11}, - {I2S_CONFIG_MCKFREQ_MCKFREQ_32MDIV15, 32.0 / 15}, - {I2S_CONFIG_MCKFREQ_MCKFREQ_32MDIV16, 32.0 / 16}, - {I2S_CONFIG_MCKFREQ_MCKFREQ_32MDIV21, 32.0 / 21}, - {I2S_CONFIG_MCKFREQ_MCKFREQ_32MDIV23, 32.0 / 23}, - {I2S_CONFIG_MCKFREQ_MCKFREQ_32MDIV30, 32.0 / 30}, - {I2S_CONFIG_MCKFREQ_MCKFREQ_32MDIV31, 32.0 / 31}, - {I2S_CONFIG_MCKFREQ_MCKFREQ_32MDIV32, 32.0 / 32}, - {I2S_CONFIG_MCKFREQ_MCKFREQ_32MDIV42, 32.0 / 42}, - {I2S_CONFIG_MCKFREQ_MCKFREQ_32MDIV63, 32.0 / 63}, - {I2S_CONFIG_MCKFREQ_MCKFREQ_32MDIV125, 32.0 / 125}}; - -/** - * @brief Mapping from Ratio Constants to frequency ratios - */ -struct Nano_BLE_ratio_info { - int id; - float ratio; -}; - -static const Nano_BLE_ratio_info ratio_table[] = { - {I2S_CONFIG_RATIO_RATIO_32X, 32.0}, {I2S_CONFIG_RATIO_RATIO_48X, 48.0}, - {I2S_CONFIG_RATIO_RATIO_64X, 64.0}, {I2S_CONFIG_RATIO_RATIO_96X, 96.0}, - {I2S_CONFIG_RATIO_RATIO_128X, 128.0}, {I2S_CONFIG_RATIO_RATIO_192X, 192.0}, - {I2S_CONFIG_RATIO_RATIO_256X, 256.0}, {I2S_CONFIG_RATIO_RATIO_384X, 384.0}, - {I2S_CONFIG_RATIO_RATIO_512X, 512.0}}; - -void I2S_IRQWrite(void) { - // Handle Wrtie - if (NRF_I2S->EVENTS_TXPTRUPD == 1) { - size_t eff_read = 0; - - // toggle arrays to avoid noise - p_i2s_array = p_i2s_array == p_i2s_array_1 ? p_i2s_array_2 : p_i2s_array_1; - - if (p_nano_ble_stream != nullptr) { - // Alternative API via Stream - eff_read = p_nano_ble_stream->readBytes(p_i2s_array, i2s_buffer_size); - } else { - // Using readArray - eff_read = p_i2s_buffer->readArray(p_i2s_array, i2s_buffer_size); - } - // if we did not get any valid data we provide silence - if (eff_read < i2s_buffer_size) { - memset(p_i2s_array, 0, i2s_buffer_size); - // allow checking for underflows - i2s_underflow_count++; - } - NRF_I2S->TXD.PTR = (uint32_t)p_i2s_array; - NRF_I2S->EVENTS_TXPTRUPD = 0; - } -} - -void I2S_IRQRead(void) { - // Handle Read - if (NRF_I2S->EVENTS_RXPTRUPD == 1) { - // reading from pins writing to buffer - overwrite oldest data on overflow - p_i2s_buffer->writeArrayOverwrite(p_i2s_array, i2s_buffer_size); - // switch buffer assuming that this is necessary like in the write case - p_i2s_array = p_i2s_array == p_i2s_array_1 ? p_i2s_array_2 : p_i2s_array_1; - NRF_I2S->RXD.PTR = (uint32_t)p_i2s_array; - NRF_I2S->EVENTS_RXPTRUPD = 0; - } -} - -/** - * I2S Event handler - */ -void I2S_IRQHandler(void) { - // prevent NPE - if (p_i2s_buffer == nullptr || p_i2s_array == 0) { - NRF_I2S->EVENTS_TXPTRUPD = 0; - NRF_I2S->EVENTS_RXPTRUPD = 0; - return; - } - - I2S_IRQWrite(); - I2S_IRQRead(); -} - -/** - * @brief Basic I2S API - for the Arduino Nano BLE Sense - * See https://content.arduino.cc/assets/Nano_BLE_MCU-nRF52840_PS_v1.1.pdf - * Douplex mode (RXTX_MODE) is currently not supported, but it should be quite - * easy to implement. - * @author Phil Schatzmann - * @ingroup platform - * @copyright GPLv3 - */ -class I2SDriverNanoBLE { - friend class I2SStream; - - public: - I2SDriverNanoBLE() = default; - - /// Provides the default configuration - I2SConfigStd defaultConfig(RxTxMode mode) { - I2SConfigStd c(mode); - return c; - } - /// Potentially updates the sample rate (if supported) - bool setAudioInfo(AudioInfo) { return false; } - - /// starts the I2S with the default config in TX Mode - bool begin(RxTxMode mode = TX_MODE) { return begin(defaultConfig(mode)); } - - /// starts the I2S - bool begin(I2SConfigStd cfg) { - TRACEI(); - cfg.logInfo(); - this->cfg = cfg; - - if (cfg.bits_per_sample == 32) { - LOGE("32 bits not supported"); - return false; - } - - if (!setupBuffers()) { - LOGE("out of memory"); - return false; - } - - // setup IRQ - NVIC_SetVector(I2S_IRQn, (uint32_t)I2S_IRQHandler); - NVIC_EnableIRQ(I2S_IRQn); - - if (!setupRxTx(cfg)) { - return false; - } - setupClock(cfg); - setupBitWidth(cfg); - setupMode(cfg); - setupPins(cfg); - - // TX_MODE is started with first write - if (cfg.rx_tx_mode == RX_MODE || p_nano_ble_stream != nullptr) { - startI2SActive(); - } - - return true; - } - - int available() { - if (cfg.rx_tx_mode == TX_MODE) return 0; - return p_i2s_buffer->available(); - } - - int availableForWrite() { - if (cfg.rx_tx_mode == RX_MODE) return 0; - return max(i2s_buffer_size, p_i2s_buffer->availableForWrite()); - } - - /// stops the I2S - void end() { - LOGD(__func__); - // stop task - NRF_I2S->TASKS_START = 0; - // disble I2S - NRF_I2S->ENABLE = 0; - - releaseBuffers(); - - is_active = false; - } - - /// provides the actual configuration - I2SConfigStd config() { return cfg; } - - /// writes the data to the I2S buffer - size_t writeBytes(const void *src, size_t size_bytes) { - size_t result = p_i2s_buffer->writeArray((uint8_t *)src, size_bytes); - - // activate I2S when the buffer is full - if (!is_active && result < size_bytes) { - startI2SActive(); - } - return result; - } - - /// reads the data from the I2S buffer - size_t readBytes(void *dest, size_t size_bytes) { - size_t result = p_i2s_buffer->readArray((uint8_t *)dest, size_bytes); - return result; - } - - /// alternative API which provides the data directly via a Stream - void setStream(Stream &stream) { p_nano_ble_stream = &stream; } - - /// Deactivate alternative API: don't forget to call begin() - void clearStream() { p_nano_ble_stream = nullptr; } - - void setBufferSize(int size) { i2s_buffer_size = size; } - - protected: - I2SConfigStd cfg; - bool is_active = false; - - /// setup TXEN or RXEN - bool setupRxTx(I2SConfigStd cfg) { - TRACED(); - switch (cfg.rx_tx_mode) { - case TX_MODE: - // Enable transmission - NRF_I2S->CONFIG.TXEN = - (I2S_CONFIG_TXEN_TXEN_Enabled << I2S_CONFIG_TXEN_TXEN_Pos); - return true; - case RX_MODE: - // Enable reception - NRF_I2S->CONFIG.RXEN = - (I2S_CONFIG_RXEN_RXEN_Enabled << I2S_CONFIG_RXEN_RXEN_Pos); - return true; - default: - LOGE("rx_tx_mode not supported"); - return false; - } - } - - /// setup MCKFREQ and RATIO - void setupClock(I2SConfigStd cfg) { - TRACED(); - - // Enable MCK generator if in master mode - if (cfg.is_master) { - NRF_I2S->CONFIG.MCKEN = - (I2S_CONFIG_MCKEN_MCKEN_Enabled << I2S_CONFIG_MCKEN_MCKEN_Pos); - } - - // find closest frequency for requested sample_rate - float freq_requested = cfg.sample_rate; // * cfg.bits_per_sample ; - float selected_freq = 0; - for (auto freq : freq_table) { - for (auto div : ratio_table) { - float freq_value = freq.freq * 1000000 / div.ratio; - if (abs(freq_value - freq_requested) < - abs(selected_freq - freq_requested)) { - // MCKFREQ - NRF_I2S->CONFIG.MCKFREQ = freq.id << I2S_CONFIG_MCKFREQ_MCKFREQ_Pos; - // Ratio - NRF_I2S->CONFIG.RATIO = div.id << I2S_CONFIG_RATIO_RATIO_Pos; - selected_freq = freq_value; - LOGD("frequency requested %f vs %f", freq_requested, selected_freq); - } - } - } - LOGI("Frequency req. %f vs eff. %f", freq_requested, selected_freq); - } - - /// setup SWIDTH - void setupBitWidth(I2SConfigStd cfg) { - TRACED(); - uint16_t swidth = I2S_CONFIG_SWIDTH_SWIDTH_16Bit; - switch (cfg.bits_per_sample) { - case 8: - NRF_I2S->CONFIG.SWIDTH = I2S_CONFIG_SWIDTH_SWIDTH_8Bit - << I2S_CONFIG_SWIDTH_SWIDTH_Pos; - break; - case 16: - NRF_I2S->CONFIG.SWIDTH = I2S_CONFIG_SWIDTH_SWIDTH_16Bit - << I2S_CONFIG_SWIDTH_SWIDTH_Pos; - break; - case 24: - NRF_I2S->CONFIG.SWIDTH = I2S_CONFIG_SWIDTH_SWIDTH_24Bit - << I2S_CONFIG_SWIDTH_SWIDTH_Pos; - break; - default: - LOGE("Unsupported bit width: %d", cfg.bits_per_sample); - } - } - - /// setup format and align - void setupMode(I2SConfigStd cfg) { - TRACED(); - // setup mode - switch (cfg.i2s_format) { - case I2S_STD_FORMAT: - case I2S_PHILIPS_FORMAT: - case I2S_MSB_FORMAT: - case I2S_LEFT_JUSTIFIED_FORMAT: - NRF_I2S->CONFIG.FORMAT = I2S_CONFIG_FORMAT_FORMAT_I2S - << I2S_CONFIG_FORMAT_FORMAT_Pos; - NRF_I2S->CONFIG.ALIGN = I2S_CONFIG_ALIGN_ALIGN_Left - << I2S_CONFIG_ALIGN_ALIGN_Pos; - ; - break; - case I2S_LSB_FORMAT: - case I2S_RIGHT_JUSTIFIED_FORMAT: - NRF_I2S->CONFIG.FORMAT = I2S_CONFIG_FORMAT_FORMAT_I2S - << I2S_CONFIG_FORMAT_FORMAT_Pos; - NRF_I2S->CONFIG.ALIGN = I2S_CONFIG_ALIGN_ALIGN_Right - << I2S_CONFIG_ALIGN_ALIGN_Pos; - ; - break; - default: - LOGW("i2s_format not supported"); - } - } -#ifdef IS_ZEPHYR - int digitalPinToPinName(int pin) {return pin;} -#endif - /// Provides the arduino or unconverted pin name - int getPinName(int pin) { -#if defined(USE_ALT_PIN_SUPPORT) - return cfg.is_arduino_pin_numbers ? digitalPinToPinName(pin) : pin; -#else - return digitalPinToPinName(pin); -#endif - } - - /// setup pins - void setupPins(I2SConfigStd cfg) { - TRACED(); - - // MCK - if (cfg.is_master && cfg.pin_mck >= 0) { - NRF_I2S->PSEL.MCK = getPinName(cfg.pin_mck) << I2S_PSEL_MCK_PIN_Pos; - } - // SCK - bit clock - NRF_I2S->PSEL.SCK = getPinName(cfg.pin_bck) << I2S_PSEL_SCK_PIN_Pos; - // LRCK - NRF_I2S->PSEL.LRCK = getPinName(cfg.pin_ws) << I2S_PSEL_LRCK_PIN_Pos; - // i2s Data Pins - switch (cfg.rx_tx_mode) { - case TX_MODE: - NRF_I2S->PSEL.SDOUT = getPinName(cfg.pin_data) - << I2S_PSEL_SDOUT_PIN_Pos; - break; - case RX_MODE: - NRF_I2S->PSEL.SDIN = getPinName(cfg.pin_data) << I2S_PSEL_SDIN_PIN_Pos; - break; - default: - TRACEW(); - } - } - - /// Determine the INTENSET value - unsigned long getINTENSET() { - unsigned long result = 0; - switch (cfg.rx_tx_mode) { - case TX_MODE: - result = I2S_INTENSET_TXPTRUPD_Enabled << I2S_INTENSET_TXPTRUPD_Pos; - break; - case RX_MODE: - result = I2S_INTENSET_RXPTRUPD_Enabled << I2S_INTENSET_RXPTRUPD_Pos; - break; - default: - TRACEE(); - } - return result; - } - - /// Start IRQ and I2S - void startI2SActive() { - TRACED(); - // Use stereo - NRF_I2S->CONFIG.CHANNELS = I2S_CONFIG_CHANNELS_CHANNELS_Stereo - << I2S_CONFIG_CHANNELS_CHANNELS_Pos; - // Setup master or slave mode - NRF_I2S->CONFIG.MODE = - cfg.is_master ? I2S_CONFIG_MODE_MODE_MASTER << I2S_CONFIG_MODE_MODE_Pos - : I2S_CONFIG_MODE_MODE_Slave << I2S_CONFIG_MODE_MODE_Pos; - - // initial empty buffer - NRF_I2S->TXD.PTR = (uint32_t)p_i2s_array; - NRF_I2S->RXD.PTR = (uint32_t)p_i2s_array; - // define copy size (always defined as number of 32 bits) - NRF_I2S->RXTXD.MAXCNT = i2s_buffer_size / 4; - - NRF_I2S->INTENSET = getINTENSET(); - - // ensble I2S - NRF_I2S->ENABLE = 1; - // start task - NRF_I2S->TASKS_START = 1; - - is_active = true; - } - - /// dynamic buffer management - bool setupBuffers() { - TRACED(); - i2s_buffer_size = cfg.buffer_size; - - if (p_i2s_array == nullptr) { - p_i2s_array_1 = new uint8_t[i2s_buffer_size]{0}; - p_i2s_array_2 = new uint8_t[i2s_buffer_size]{0}; - p_i2s_array = p_i2s_array_1; - } else { - memset(p_i2s_array_1, 0, i2s_buffer_size); - memset(p_i2s_array_2, 0, i2s_buffer_size); - } - - // allocate buffer only when needed - if (p_i2s_buffer == nullptr && p_nano_ble_stream == nullptr) { - p_i2s_buffer = new NBuffer(cfg.buffer_size, i2s_buffer_size); - } - - // on stream option we only need to have the arrays allocated - if (p_nano_ble_stream != nullptr) { - return p_i2s_array_1 != nullptr && p_i2s_array_2 != nullptr; - } - return p_i2s_array_1 != nullptr && p_i2s_array_2 != nullptr && - p_i2s_buffer != nullptr; - } - - /// Release buffers - void releaseBuffers() { - TRACED(); - i2s_buffer_size = 0; - - p_i2s_array = nullptr; - delete p_i2s_array_1; - p_i2s_array_1 = nullptr; - delete p_i2s_array_2; - p_i2s_array_2 = nullptr; - - delete p_i2s_buffer; - p_i2s_buffer = nullptr; - } -}; - -using I2SDriver = I2SDriverNanoBLE; - -} // namespace audio_tools - -#endif \ No newline at end of file diff --git a/src/AudioTools/CoreAudio/AudioI2S/I2SRP2040-MBED.h b/src/AudioTools/CoreAudio/AudioI2S/I2SRP2040-MBED.h deleted file mode 100644 index 9df35f4822..0000000000 --- a/src/AudioTools/CoreAudio/AudioI2S/I2SRP2040-MBED.h +++ /dev/null @@ -1,162 +0,0 @@ -#pragma once - -#include "AudioTools/CoreAudio/AudioI2S/I2SConfig.h" -#if defined(RP2040_MBED) -#include "RP2040-I2S.h" - -#define IS_I2S_IMPLEMENTED - -namespace audio_tools { - -//#if !defined(ARDUINO_ARCH_MBED_RP2040) -//static ::I2S I2S; -//#endif - -/** - * @brief Basic I2S API - for the ... - * @ingroup platform - * @author Phil Schatzmann - * @copyright GPLv3 - */ -class I2SDriverRP2040MBED { - friend class I2SStream; - - public: - /// Provides the default configuration - I2SConfigStd defaultConfig(RxTxMode mode) { - I2SConfigStd c(mode); - return c; - } - - /// Potentially updates the sample rate (if supported) - bool setAudioInfo(AudioInfo) { return false; } - - /// starts the DAC with the default config in TX Mode - bool begin(RxTxMode mode = TX_MODE) { - TRACED(); - return begin(defaultConfig(mode)); - } - - /// starts the DAC - bool begin(I2SConfigStd cfg) { - TRACEI(); - this->cfg = cfg; - cfg.logInfo(); - if (cfg.rx_tx_mode != TX_MODE) { - LOGE("Unsupported mode: only TX_MODE is supported"); - return false; - } - if (!I2S.setBCLK(cfg.pin_bck)) { - LOGE("Could not set bck pin: %d", cfg.pin_bck); - return false; - } - if (!I2S.setDATA(cfg.pin_data)) { - LOGE("Could not set data pin: %d", cfg.pin_data); - return false; - } - if (cfg.bits_per_sample != 16) { - LOGE("Unsupported bits_per_sample: %d", (int)cfg.bits_per_sample); - return false; - } - - if (cfg.channels < 1 || cfg.channels > 2) { - LOGE("Unsupported channels: '%d' - only 2 is supported", (int)cfg.channels); - return false; - } - - int rate = cfg.sample_rate; - if (cfg.channels == 1) { - rate = rate / 2; - } - - if (!I2S.begin(rate)) { - LOGE("Could not start I2S"); - return false; - } - return true; - } - - /// stops the I2C and unistalls the driver - void end() { I2S.end(); } - - /// provides the actual configuration - I2SConfigStd config() { return cfg; } - - /// writes the data to the I2S interface - size_t writeBytes(const void *src, size_t size_bytes) { - TRACED(); - size_t result = 0; - int16_t *p16 = (int16_t *)src; - - if (cfg.channels == 1) { - int samples = size_bytes / 2; - // multiply 1 channel into 2 - int16_t buffer[samples * 2]; // from 1 byte to 2 bytes - for (int j = 0; j < samples; j++) { - buffer[j * 2] = p16[j]; - buffer[j * 2 + 1] = p16[j]; - } - result = I2S.write((const uint8_t *)buffer, size_bytes * 2) * 2; - } else if (cfg.channels == 2) { - result = I2S.write((const uint8_t *)src, size_bytes) * 4; - } - return result; - } - - size_t readBytes(void *dest, size_t size_bytes) { - TRACEE(); - size_t result = 0; - return result; - } - - int availableForWrite() { - int result = 0; - if (cfg.channels == 1) { - // it should be a multiple of 2 - result = I2S.availableForWrite() / 2 * 2; - // return half of it because we double when writing - result = result / 2; - } else { - // it should be a multiple of 4 - result = I2S.availableForWrite() / 4 * 4; - } - if (result < 4) { - result = 0; - } - return result; - } - - int available() { return 0; } - - void flush() { return I2S.flush(); } - - protected: - I2SConfigStd cfg; - - // blocking write - void writeSample(int16_t sample) { - int written = I2S.write(sample); - while (!written) { - delay(5); - LOGW("written: %d ", written); - written = I2S.write(sample); - } - } - - int writeSamples(int samples, int16_t *values) { - int result = 0; - for (int j = 0; j < samples; j++) { - int16_t sample = values[j]; - writeSample(sample); - writeSample(sample); - result++; - } - return result; - } -}; - -using I2SDriver = I2SDriverRP2040MBED; - -} // namespace audio_tools - -#endif diff --git a/src/AudioTools/CoreAudio/AudioI2S/I2SRP2040.h b/src/AudioTools/CoreAudio/AudioI2S/I2SRP2040.h deleted file mode 100644 index a88414b92a..0000000000 --- a/src/AudioTools/CoreAudio/AudioI2S/I2SRP2040.h +++ /dev/null @@ -1,398 +0,0 @@ -#pragma once - -#include "AudioTools/CoreAudio/AudioI2S/I2SConfig.h" -#if defined(RP2040_HOWER) -#include - -#define IS_I2S_IMPLEMENTED - -namespace audio_tools { - -/** - * @brief Basic I2S API - for the ... - * @ingroup platform - * @author Phil Schatzmann - * @author LinusHeu - * @copyright GPLv3 - */ -class I2SDriverRP2040 { - friend class I2SStream; - - public: - /// Provides the default configuration - I2SConfigStd defaultConfig(RxTxMode mode) { - I2SConfigStd c(mode); - return c; - } - /// Potentially updates values dynamically - bool setAudioInfo(AudioInfo info) { - if (info.sample_rate != cfg.sample_rate && !i2s.setFrequency(info.sample_rate)) { - LOGI("i2s.setFrequency %d failed", info.sample_rate); - return false; - } - if (info.bits_per_sample != cfg.bits_per_sample && !i2s.setBitsPerSample(info.bits_per_sample)) { - LOGI("i2s.setBitsPerSample %d failed", info.bits_per_sample); - return false; - } - cfg.sample_rate = info.sample_rate; - cfg.bits_per_sample = info.bits_per_sample; - cfg.channels = info.channels; - return true; - } - - /// starts the DAC with the default config in TX Mode - bool begin(RxTxMode mode = TX_MODE) { - TRACED(); - return begin(defaultConfig(mode)); - } - - /// starts the DAC - bool begin(I2SConfigStd cfg) { - TRACEI(); - // prevent multiple begins w/o calling end - if (is_active) end(); - this->cfg = cfg; - cfg.logInfo(); - switch (cfg.rx_tx_mode) { - case TX_MODE: - i2s = I2S(OUTPUT); - break; - case RX_MODE: - i2s = I2S(INPUT); - has_input[0] = has_input[1] = false; - break; - default: - LOGE("Unsupported mode: only TX_MODE is supported"); - return false; - break; - } - - if (cfg.pin_ws == cfg.pin_bck + 1) { // normal pin order - if (!i2s.setBCLK(cfg.pin_bck)) { - LOGE("Could not set bck pin: %d", cfg.pin_bck); - return false; - } - } else if (cfg.pin_ws == cfg.pin_bck - 1) { // reverse pin order - if (!i2s.swapClocks() || - !i2s.setBCLK( - cfg.pin_ws)) { // setBCLK() actually sets the lower pin of bck/ws - LOGE("Could not set bck pin: %d", cfg.pin_bck); - return false; - } - } else { - LOGE("pins bck: '%d' and ws: '%d' must be next to each other", - cfg.pin_bck, cfg.pin_ws); - return false; - } - if (!i2s.setDATA(cfg.pin_data)) { - LOGE("Could not set data pin: %d", cfg.pin_data); - return false; - } - if (cfg.pin_mck != -1) { - i2s.setMCLKmult(cfg.mck_multiplier); - if (!i2s.setMCLK(cfg.pin_mck)) { - LOGE("Could not set data pin: %d", cfg.pin_mck); - return false; - } - } - - if (cfg.bits_per_sample == 8 || - !i2s.setBitsPerSample(cfg.bits_per_sample)) { - LOGE("Could not set bits per sample: %d", cfg.bits_per_sample); - return false; - } - - if (!i2s.setBuffers(cfg.buffer_count, cfg.buffer_size)) { - LOGE("Could not set buffers: Count: '%d', size: '%d'", cfg.buffer_count, - cfg.buffer_size); - return false; - } - - // setup format - if (cfg.i2s_format == I2S_STD_FORMAT || - cfg.i2s_format == I2S_PHILIPS_FORMAT) { - // default setting: do nothing - } else if (cfg.i2s_format == I2S_LEFT_JUSTIFIED_FORMAT || - cfg.i2s_format == I2S_LSB_FORMAT) { - if (!i2s.setLSBJFormat()) { - LOGE("Could not set LSB Format") - return false; - } - } else { - LOGE("Unsupported I2S format"); - return false; - } - -#if defined(RP2040_HOWER) - - if (cfg.signal_type != TDM && (cfg.channels < 1 || cfg.channels > 2)) { - LOGE("Unsupported channels: '%d'", cfg.channels); - return false; - } - - if (cfg.signal_type == TDM) { - i2s.setTDMFormat(); - i2s.setTDMChannels(cfg.channels); - } - -#else - - if (cfg.channels < 1 || cfg.channels > 2) { - LOGE("Unsupported channels: '%d'", cfg.channels); - return false; - } - - if (cfg.signal_type != Digital) { - LOGE("Unsupported signal_type: '%d'", cfg.signal_type); - return false; - } - -#endif - if (!i2s.begin(cfg.sample_rate)) { - LOGE("Could not start I2S"); - return false; - } - is_active = true; - return true; - } - - /// stops the I2C and uninstalls the driver - void end() { - flush(); - i2s.end(); - is_active = false; - } - - /// provides the actual configuration - I2SConfigStd config() { return cfg; } - - /// writes the data to the I2S interface - size_t writeBytes(const void *src, size_t size_bytes) { - LOGD("writeBytes(%d)", size_bytes); - size_t result = 0; - - if (cfg.channels == 1) { - result = writeExpandChannel(src, size_bytes); - } else if (cfg.channels == 2) { - const uint8_t *p = (const uint8_t *)src; - while (size_bytes >= sizeof(int32_t)) { - bool justWritten = i2s.write( - *(int32_t *)p, - true); // I2S::write(int32,bool) actually only returns 0 or 1 - if (justWritten) { - size_bytes -= sizeof(int32_t); - p += sizeof(int32_t); - result += sizeof(int32_t); - } else - return result; - } - } - return result; - } - - size_t readBytes(void *dest, size_t size_bytes) { - TRACED(); - switch (cfg.channels) { - case 1: - return read1Channel(dest, size_bytes); - case 2: - return read2Channels(dest, size_bytes); - } - return 0; - } - - int availableForWrite() { - if (cfg.channels == 1) { - return cfg.buffer_size; - } else { - return i2s.availableForWrite(); - } - } - - int available() { return min(i2s.available(), cfg.buffer_size); } - - void flush() { i2s.flush(); } - - bool getOverUnderflow() { - return i2s.getOverUnderflow() ; - } - - protected: - I2SConfigStd cfg; - I2S i2s; - bool has_input[2]; - bool is_active = false; - - /// writes 1 channel to I2S while expanding it to 2 channels - // returns amount of bytes written from src to i2s - size_t writeExpandChannel(const void *src, size_t size_bytes) { - switch (cfg.bits_per_sample) { - // case 8: { - // int8_t *pt8 = (int8_t*) src; - // int16_t sample16 = static_cast(*pt8) << 8; - // for (int j=0;j - T mix(T left, T right) { - if (left != 0) has_input[0] = true; - if (right != 0) has_input[1] = true; - - // if right is always empty we return left - if (has_input[0] && !has_input[1]) { - return left; - } - - // if left is always empty we return right - if (!has_input[0] && has_input[1]) { - return right; - } - - return (left / 2) + (right / 2); - } -}; - -using I2SDriver = I2SDriverRP2040; - -} // namespace audio_tools - -#endif diff --git a/src/AudioTools/CoreAudio/AudioI2S/I2SSAMD.h b/src/AudioTools/CoreAudio/AudioI2S/I2SSAMD.h deleted file mode 100644 index 29aa799173..0000000000 --- a/src/AudioTools/CoreAudio/AudioI2S/I2SSAMD.h +++ /dev/null @@ -1,67 +0,0 @@ -#pragma once - -#if defined(ARDUINO_ARCH_SAMD) -#include -#include "AudioTools/CoreAudio/AudioI2S/I2SConfig.h" - -#define IS_I2S_IMPLEMENTED - -namespace audio_tools { - -/** - * @brief Basic I2S API - for the SAMD - * @ingroup platform - * @author Phil Schatzmann - * @copyright GPLv3 - */ -class I2SDriverSAMD { - friend class I2SStream; - - public: - /// Provides the default configuration - I2SConfigStd defaultConfig(RxTxMode mode) { - I2SConfigStd c(mode); - return c; - } - - /// Potentially updates the sample rate (if supported) - bool setAudioInfo(AudioInfo) { return false; } - - /// starts the DAC with the default config - bool begin(RxTxMode mode) { return begin(defaultConfig(mode)); } - - /// starts the DAC - bool begin(I2SConfigStd cfg) { - this->cfg = cfg; - return I2S.begin(cfg.i2s_format, cfg.sample_rate, cfg.bits_per_sample); - } - - bool begin() { return begin(cfg); } - - /// stops the I2C and unistalls the driver - void end() { I2S.end(); } - - /// provides the actual configuration - I2SConfigStd config() { return cfg; } - - size_t writeBytes(const void *src, size_t size_bytes) { - return I2S.write((const uint8_t *)src, size_bytes); - } - - size_t readBytes(void *src, size_t size_bytes) { - return I2S.read(src, size_bytes); - } - - int available() { return I2S.available(); } - - int availableForWrite() { return I2S.availableForWrite(); } - - protected: - I2SConfigStd cfg; -}; - -using I2SDriver = I2SDriverSAMD; - -} // namespace audio_tools - -#endif diff --git a/src/AudioTools/CoreAudio/AudioI2S/I2SSTM32.h b/src/AudioTools/CoreAudio/AudioI2S/I2SSTM32.h deleted file mode 100644 index 58d0c00e78..0000000000 --- a/src/AudioTools/CoreAudio/AudioI2S/I2SSTM32.h +++ /dev/null @@ -1,464 +0,0 @@ -#pragma once - -#ifdef STM32 -#include "AudioTools/CoreAudio/AudioI2S/I2SConfig.h" -#include "stm32-i2s.h" - -#ifdef STM_I2S_PINS -#define IS_I2S_IMPLEMENTED - -namespace audio_tools { - -/** - * @brief Basic I2S API - for the STM32 - * Depends on https://github.com/pschatzmann/stm32f411-i2s - * We provide a direct and a DMA implementation. - * When using DMA, we just add a write and read buffer and pass some parameters - * to the STM32 API! Alternatively we can define the input stream or the output. - * - * @ingroup platform - * @author Phil Schatzmann - * @copyright GPLv3 - */ - -class I2SDriverSTM32 { - friend class I2SStream; - - public: - /// Provides the default configuration - I2SConfigStd defaultConfig(RxTxMode mode = TX_MODE) { - I2SConfigStd c(mode); - return c; - } - - /// Potentially updates the sample rate (if supported) - bool setAudioInfo(AudioInfo) { return false;} - - /// starts the DAC with the default config in TX Mode - bool begin(RxTxMode mode = TX_MODE) { - TRACED(); - return begin(defaultConfig(mode)); - } - - /// starts the DAC - bool begin(I2SConfigStd cfg) { - // TRACED(); - this->cfg = cfg; - bool result = false; - deleteBuffers(); - LOGI("buffer_size: %d", cfg.buffer_size); - LOGI("buffer_count: %d", cfg.buffer_count); - - if (cfg.channels > 2 || cfg.channels <= 0) { - LOGE("Channels not supported: %d", cfg.channels); - return false; - } - - setupDefaultI2SParameters(); - setupPins(); - result = use_dma ? startI2SDMA() : startI2S(); - this->active = result; - return result; - } - - /// stops the I2C and unistalls the driver - void end() { - // TRACED(); - i2s.end(); - deleteBuffers(); - active = false; - } - - /// we assume the data is already available in the buffer - int available() { - if (!active) return 0; - if (use_dma && p_rx_buffer == nullptr) return 0; - return cfg.buffer_size; - } - - /// We limit the write size to the buffer size - int availableForWrite() { - if (!active) return 0; - if (use_dma && p_tx_buffer == nullptr) return 0; - return cfg.buffer_size; - } - - /// provides the actual configuration - I2SConfigStd config() { return cfg; } - - /// blocking writes for the data to the I2S interface - size_t writeBytes(const void *src, size_t size_bytes) { - TRACED(); - size_t result = 0; - if (!use_dma) { - result = i2s.write((uint8_t *)src, size_bytes); - } else { - // if we have an input stream we do not need to fill the buffer - if (p_dma_in != nullptr) { - // by calling writeBytes we activate the automatic timeout handling - // and expect further writes to continue the output - last_write_ms = millis(); - result = size_bytes; - } else { - // fill buffer - result = writeBytesDMA(src, size_bytes); - } - } - return result; - } - - size_t readBytes(void *dest, size_t size_bytes) { - TRACED(); - if (!use_dma) { - return i2s.readBytes((uint8_t *)dest, size_bytes); - } else { - if (cfg.channels == 2) { - return p_rx_buffer->readArray((uint8_t *)dest, size_bytes); - } else { - return readBytesDMA(dest, size_bytes); - } - } - } - - /// @brief Callback function used by - /// https://github.com/pschatzmann/stm32f411-i2s - static void writeFromReceive(uint8_t *buffer, uint16_t byteCount, void *ref) { - I2SDriverSTM32 *self = (I2SDriverSTM32 *)ref; - uint16_t written = 0; - if (self->p_dma_out != nullptr) - written = self->p_dma_out->write(buffer, byteCount); - else - written = self->p_rx_buffer->writeArray(buffer, byteCount); - - // check for overflow - if (written != byteCount) { - LOGW("Buffer overflow: written %d of %d", written, byteCount); - } - } - - /// @brief Callback function used by - /// https://github.com/pschatzmann/stm32f411-i2s - static void readToTransmit(uint8_t *buffer, uint16_t byteCount, void *ref) { - I2SDriverSTM32 *self = (I2SDriverSTM32 *)ref; - static size_t count = 0; - size_t read = 0; - if (self->p_dma_in != nullptr) { - // stop reading if timout is relevant - if (self->isWriteTimedOut()) { - // we just provide silence - read = byteCount; - } else { - // get data from stream - read = self->p_dma_in->readBytes(buffer, byteCount); - } - } else { - // get data from buffer - if (self->stm32_write_active) { - read = self->p_tx_buffer->readArray(buffer, byteCount); - } - } - memset(buffer+read, 0, byteCount-read); - - // check for underflow - count++; - if (read != byteCount) { - LOGW("Buffer underflow at %lu: %d for %d", count, read, byteCount); - } - } - - /// Checks if timout has been activated and if so, if it is timed out - bool isWriteTimedOut() { - return last_write_ms != 0 && last_write_ms + 500 < millis(); - } - - /// activate DMA using a read and write buffer - void setDMAActive(bool flag) { use_dma = flag; } - - /// activate DMA and defines the input - void setDMAInputStream(Stream &in) { - use_dma = true; - p_dma_in = ∈ - } - - /// activate DMA and defines the output - void setDMAOutput(Print &out) { - use_dma = true; - p_dma_out = &out; - } - - protected: - stm32_i2s::Stm32I2sClass i2s; - stm32_i2s::I2SSettingsSTM32 i2s_stm32; - I2SConfigStd cfg; - bool active = false; - bool result = true; - BaseBuffer *p_tx_buffer = nullptr; - BaseBuffer *p_rx_buffer = nullptr; - volatile bool stm32_write_active = false; - bool use_dma = true; - Print *p_dma_out = nullptr; - Stream *p_dma_in = nullptr; - uint32_t last_write_ms = 0; - - size_t writeBytesDMA(const void *src, size_t size_bytes) { - size_t result = 0; - // fill the tx buffer - int open = size_bytes; - while (open > 0) { - int actual_written = writeBytesExt(src, size_bytes); - result += actual_written; - open -= actual_written; - if (open > 0) { - stm32_write_active = true; - //delay(1); - } - } - - // start output of data only when buffer has been filled - if (!stm32_write_active && p_tx_buffer->availableForWrite() == 0) { - stm32_write_active = true; - LOGI("Buffer is full->starting i2s output"); - } - - return size_bytes; - } - - size_t readBytesDMA(void *dest, size_t size_bytes) { - // combine two channels to 1: so request double the amout - int req_bytes = size_bytes * 2; - uint8_t tmp[req_bytes]; - int16_t *tmp_16 = (int16_t *)tmp; - int eff_bytes = p_rx_buffer->readArray((uint8_t *)tmp, req_bytes); - // add 2 channels together - int16_t *dest_16 = (int16_t *)dest; - int16_t eff_samples = eff_bytes / 2; - int16_t idx = 0; - for (int j = 0; j < eff_samples; j += 2) { - dest_16[idx++] = static_cast(tmp_16[j]) + tmp_16[j + 1] / 2.0; - } - return eff_bytes / 2; - } - - bool startI2S() { - switch (cfg.rx_tx_mode) { - case RX_MODE: - result = i2s.begin(i2s_stm32, false, true); - break; - case TX_MODE: - result = i2s.begin(i2s_stm32, true, false); - break; - case RXTX_MODE: - default: - result = i2s.begin(i2s_stm32, true, true); - break; - } - return result; - } - - bool startI2SDMA() { - switch (cfg.rx_tx_mode) { - case RX_MODE: - if (use_dma && p_rx_buffer == nullptr) - p_rx_buffer = allocateBuffer(); - result = i2s.beginReadDMA(i2s_stm32, writeFromReceive); - break; - case TX_MODE: - stm32_write_active = false; - if (use_dma && p_tx_buffer == nullptr) - p_tx_buffer = allocateBuffer(); - result = i2s.beginWriteDMA(i2s_stm32, readToTransmit); - break; - - case RXTX_MODE: - if (use_dma) { - stm32_write_active = false; - if (p_rx_buffer == nullptr) - p_rx_buffer = allocateBuffer(); - if (p_tx_buffer == nullptr) - p_tx_buffer = allocateBuffer(); - } - result = i2s.beginReadWriteDMA( - i2s_stm32, readToTransmit, writeFromReceive); - break; - - default: - LOGE("Unsupported mode"); - return false; - } - return result; - } - - uint32_t toDataFormat(int bits_per_sample) { - switch (bits_per_sample) { - case 16: - return I2S_DATAFORMAT_16B; - case 24: - return I2S_DATAFORMAT_24B; - case 32: - return I2S_DATAFORMAT_32B; - } - return I2S_DATAFORMAT_16B; - } - - void deleteBuffers() { - if (p_rx_buffer != nullptr) { - delete p_rx_buffer; - p_rx_buffer = nullptr; - } - if (p_tx_buffer != nullptr) { - delete p_tx_buffer; - p_tx_buffer = nullptr; - } - } - - void setupDefaultI2SParameters() { - i2s_stm32.sample_rate = getSampleRate(cfg); - i2s_stm32.data_format = toDataFormat(cfg.bits_per_sample); - i2s_stm32.mode = getMode(cfg); - i2s_stm32.standard = getStandard(cfg); - i2s_stm32.fullduplexmode = cfg.rx_tx_mode == RXTX_MODE - ? I2S_FULLDUPLEXMODE_ENABLE - : I2S_FULLDUPLEXMODE_DISABLE; - i2s_stm32.hardware_config.buffer_size = cfg.buffer_size; - // provide ourself as parameter to callback - i2s_stm32.ref = this; - } - - void setupPins(){ - if (cfg.pin_bck == -1 || cfg.pin_ws == -1 || cfg.pin_data == -1) { - LOGW("pins ignored: used from stm32-i2s"); - } else { - LOGI("setting up pins for stm32-i2s"); - i2s_stm32.hardware_config.pins[0].function = stm32_i2s::mclk; - i2s_stm32.hardware_config.pins[0].pin = digitalPinToPinName(cfg.pin_mck); - i2s_stm32.hardware_config.pins[0].altFunction = cfg.pin_alt_function; - - i2s_stm32.hardware_config.pins[1].function = stm32_i2s::bck; - i2s_stm32.hardware_config.pins[1].pin = digitalPinToPinName(cfg.pin_bck); - i2s_stm32.hardware_config.pins[1].altFunction = cfg.pin_alt_function; - - i2s_stm32.hardware_config.pins[2].function = stm32_i2s::ws; - i2s_stm32.hardware_config.pins[2].pin = digitalPinToPinName(cfg.pin_ws); - i2s_stm32.hardware_config.pins[2].altFunction = cfg.pin_alt_function; - - switch (cfg.rx_tx_mode) { - case TX_MODE: - i2s_stm32.hardware_config.pins[3].function = stm32_i2s::data_out; - i2s_stm32.hardware_config.pins[3].pin = digitalPinToPinName(cfg.pin_data); - i2s_stm32.hardware_config.pins[3].altFunction = cfg.pin_alt_function; - break; - case RX_MODE: - i2s_stm32.hardware_config.pins[4].function = stm32_i2s::data_in; - i2s_stm32.hardware_config.pins[4].pin = digitalPinToPinName(cfg.pin_data); - i2s_stm32.hardware_config.pins[4].altFunction = cfg.pin_alt_function; - break; - case RXTX_MODE: - i2s_stm32.hardware_config.pins[3].function = stm32_i2s::data_out; - i2s_stm32.hardware_config.pins[3].pin = digitalPinToPinName(cfg.pin_data); - i2s_stm32.hardware_config.pins[3].altFunction = cfg.pin_alt_function; - - i2s_stm32.hardware_config.pins[4].function = stm32_i2s::data_in; - i2s_stm32.hardware_config.pins[4].pin = digitalPinToPinName(cfg.pin_data); - i2s_stm32.hardware_config.pins[4].altFunction = cfg.pin_alt_function; - break; - }; - - } - } - - uint32_t getMode(I2SConfigStd &cfg) { - if (cfg.is_master) { - switch (cfg.rx_tx_mode) { - case RX_MODE: - return I2S_MODE_MASTER_RX; - case TX_MODE: - return I2S_MODE_MASTER_TX; - default: - LOGE("RXTX_MODE not supported"); - return I2S_MODE_MASTER_TX; - } - } else { - switch (cfg.rx_tx_mode) { - case RX_MODE: - return I2S_MODE_SLAVE_RX; - case TX_MODE: - return I2S_MODE_SLAVE_TX; - default: - LOGE("RXTX_MODE not supported"); - return I2S_MODE_SLAVE_TX; - } - } - } - - uint32_t getStandard(I2SConfigStd &cfg) { - uint32_t result; - switch (cfg.i2s_format) { - case I2S_PHILIPS_FORMAT: - return I2S_STANDARD_PHILIPS; - case I2S_STD_FORMAT: - case I2S_LSB_FORMAT: - case I2S_RIGHT_JUSTIFIED_FORMAT: - return I2S_STANDARD_MSB; - case I2S_MSB_FORMAT: - case I2S_LEFT_JUSTIFIED_FORMAT: - return I2S_STANDARD_LSB; - } - return I2S_STANDARD_PHILIPS; - } - - uint32_t getSampleRate(I2SConfigStd &cfg) { - switch (cfg.sample_rate) { - case I2S_AUDIOFREQ_192K: - case I2S_AUDIOFREQ_96K: - case I2S_AUDIOFREQ_48K: - case I2S_AUDIOFREQ_44K: - case I2S_AUDIOFREQ_32K: - case I2S_AUDIOFREQ_22K: - case I2S_AUDIOFREQ_16K: - case I2S_AUDIOFREQ_11K: - case I2S_AUDIOFREQ_8K: - return cfg.sample_rate; - default: - LOGE("Unsupported sample rate: %u", cfg.sample_rate); - return cfg.sample_rate; - } - } - - size_t writeBytesExt(const void *src, size_t size_bytes) { - size_t result = 0; - if (cfg.channels == 2) { - result = p_tx_buffer->writeArray((uint8_t *)src, size_bytes); - } else { - // write each sample 2 times - int samples = size_bytes / 2; - int16_t *src_16 = (int16_t *)src; - int16_t tmp[2]; - int result = 0; - for (int j = 0; j < samples; j++) { - tmp[0] = src_16[j]; - tmp[1] = src_16[j]; - if (p_tx_buffer->availableForWrite() >= 4) { - p_tx_buffer->writeArray((uint8_t *)tmp, 4); - result = j * 2; - } else { - // abort if the buffer is full - break; - } - } - } - LOGD("writeBytesExt: %u", result) - return result; - } - - BaseBuffer* allocateBuffer() { - //return new RingBuffer(cfg.buffer_size * cfg.buffer_count); - return new NBuffer(cfg.buffer_size, cfg.buffer_count); - } -}; - -using I2SDriver = I2SDriverSTM32; - -} // namespace audio_tools - -#endif -#endif diff --git a/src/AudioTools/CoreAudio/AudioI2S/I2SStream.h b/src/AudioTools/CoreAudio/AudioI2S/I2SStream.h deleted file mode 100644 index abcb062c07..0000000000 --- a/src/AudioTools/CoreAudio/AudioI2S/I2SStream.h +++ /dev/null @@ -1,164 +0,0 @@ - - -#pragma once -#include "AudioToolsConfig.h" - -#if defined(USE_I2S) - -#include "AudioTools/CoreAudio/AudioI2S/I2SConfig.h" -#include "AudioTools/CoreAudio/AudioI2S/I2SESP32.h" -#include "AudioTools/CoreAudio/AudioI2S/I2SESP32V1.h" -#include "AudioTools/CoreAudio/AudioI2S/I2SESP8266.h" -#include "AudioTools/CoreAudio/AudioI2S/I2SNanoSenseBLE.h" -#include "AudioTools/CoreAudio/AudioI2S/I2SRP2040-MBED.h" -#include "AudioTools/CoreAudio/AudioI2S/I2SRP2040.h" -#include "AudioTools/CoreAudio/AudioI2S/I2SSAMD.h" -#include "AudioTools/CoreAudio/AudioI2S/I2SSTM32.h" -#include "AudioTools/CoreAudio/AudioStreams.h" -#include "AudioTools/CoreAudio/AudioTypes.h" - -#if defined(IS_I2S_IMPLEMENTED) - -namespace audio_tools { - -/** - * @brief We support the Stream interface for the I2S access. In addition we - * allow a separate mute pin which might also be used to drive a LED... - * @ingroup io - * @tparam T - * @author Phil Schatzmann - * @copyright GPLv3 - */ - -class I2SStream : public AudioStream { - public: - I2SStream() = default; - ~I2SStream() { end(); } - -#ifdef ARDUINO - I2SStream(int mute_pin) { - TRACED(); - this->mute_pin = mute_pin; - if (mute_pin > 0) { - pinMode(mute_pin, OUTPUT); - mute(true); - } - } -#endif - - /// Provides the default configuration - I2SConfig defaultConfig(RxTxMode mode = TX_MODE) { - return i2s.defaultConfig(mode); - } - - bool begin() { - TRACEI(); - I2SConfig cfg = i2s.config(); - if (!cfg){ - LOGE("unsuported AudioInfo: sample_rate: %d / channels: %d / bits_per_sample: %d", (int) cfg.sample_rate, (int)cfg.channels, (int)cfg.bits_per_sample); - return false; - } - cfg.copyFrom(audioInfo()); - if (cfg.rx_tx_mode == UNDEFINED_MODE){ - cfg.rx_tx_mode = RXTX_MODE; - } - is_active = i2s.begin(cfg); - mute(false); - return is_active; - } - - /// Starts the I2S interface - bool begin(I2SConfig cfg) { - TRACED(); - if (!cfg){ - LOGE("unsuported AudioInfo: sample_rate: %d / channels: %d / bits_per_sample: %d", (int) cfg.sample_rate, (int)cfg.channels, (int)cfg.bits_per_sample); - return false; - } - AudioStream::setAudioInfo(cfg); - is_active = i2s.begin(cfg); - // unmute - mute(false); - return is_active; - } - - /// Stops the I2S interface - void end() { - if (is_active) { - TRACEI(); - is_active = false; - mute(true); - i2s.end(); - } - } - - /// updates the sample rate dynamically - virtual void setAudioInfo(AudioInfo info) { - TRACEI(); - assert(info.sample_rate != 0); - assert(info.channels != 0); - assert(info.bits_per_sample != 0); - AudioStream::setAudioInfo(info); - if (is_active) { - if (!i2s.setAudioInfo(info)) { - I2SConfig current_cfg = i2s.config(); - if (!info.equals(current_cfg)) { - LOGI("restarting i2s"); - info.logInfo("I2SStream"); - i2s.end(); - current_cfg.copyFrom(info); - i2s.begin(current_cfg); - } else { - LOGI("no change"); - } - } - } - } - - /// Writes the audio data to I2S - virtual size_t write(const uint8_t *data, size_t len) { - LOGD("I2SStream::write: %d", len); - if (data == nullptr || len == 0 || !is_active) return 0; - return i2s.writeBytes(data, len); - } - - /// Reads the audio data - virtual size_t readBytes(uint8_t *data, size_t len) override { - return i2s.readBytes(data, len); - } - - /// Provides the available audio data - virtual int available() override { return i2s.available(); } - - /// Provides the available audio data - virtual int availableForWrite() override { return i2s.availableForWrite(); } - - void flush() override {} - - /// Provides access to the driver - I2SDriver *driver() { return &i2s; } - - /// Returns true if i2s is active - operator bool() override { return is_active; } - - /// Returns true if i2s is active - bool isActive() { return is_active;} - - protected: - I2SDriver i2s; - int mute_pin = -1; - bool is_active = false; - - /// set mute pin on or off - void mute(bool is_mute) { -#ifdef ARDUINO - if (mute_pin > 0) { - digitalWrite(mute_pin, is_mute ? SOFT_MUTE_VALUE : !SOFT_MUTE_VALUE); - } -#endif - } -}; - -} // namespace audio_tools - -#endif -#endif diff --git a/src/AudioTools/CoreAudio/AudioIO.h b/src/AudioTools/CoreAudio/AudioIO.h deleted file mode 100644 index 3b44e8e190..0000000000 --- a/src/AudioTools/CoreAudio/AudioIO.h +++ /dev/null @@ -1,805 +0,0 @@ -#pragma once -#include "AudioTools/CoreAudio/AudioOutput.h" -#include "AudioTools/CoreAudio/AudioStreams.h" - -#ifndef MAX_ZERO_READ_COUNT -# define MAX_ZERO_READ_COUNT 3 -#endif - -#ifndef CHANNEL_SELECT_BUFFER_SIZE -# define CHANNEL_SELECT_BUFFER_SIZE 256 -#endif - - -namespace audio_tools { -/** - * @brief ConverterStream Helper class which implements the - * readBytes with the help of write. - * @author Phil Schatzmann - * @copyright GPLv3 - * @tparam T class name of the original transformer stream - */ -template -class TransformationReader { - public: - /// @brief setup of the TransformationReader class - /// @param transform The original transformer stream - /// @param source The data source of the data to be converted - /// the requested converted bytes - void begin(T *transform, Stream *source) { - TRACED(); - active = true; - p_stream = source; - p_transform = transform; - if (transform == nullptr) { - LOGE("transform is NULL"); - active = false; - } - if (p_stream == nullptr) { - LOGE("p_stream is NULL"); - active = false; - } - } - - /// Defines the read buffer size for individual reads - void resizeReadBuffer(int size) { - buffer.resize(size); - } - /// Defines the queue size for result - void resizeResultQueue(int size) { - result_queue_buffer.resize(size); - result_queue.begin(); - } - - - size_t readBytes(uint8_t *data, size_t len) { - LOGD("TransformationReader::readBytes: %d", (int)len); - if (!active) { - LOGE("inactive"); - return 0; - } - if (p_stream == nullptr) { - LOGE("p_stream is NULL"); - return 0; - } - - // we read half the necessary bytes - if (buffer.size() == 0) { - int size = (0.5f / p_transform->getByteFactor() * len); - // process full samples/frames - size = size / 4 * 4; - LOGI("read size: %d", size); - buffer.resize(size); - } - - if (result_queue_buffer.size() == 0) { - // make sure that the ring buffer is big enough - int rb_size = len * result_queue_factor; - LOGI("buffer size: %d", rb_size); - result_queue_buffer.resize(rb_size); - result_queue.begin(); - } - - if (result_queue.available() < len) { - Print *tmp = setupOutput(); - int zero_count = 0; - while (result_queue.available() < len) { - int read_eff = p_stream->readBytes(buffer.data(), buffer.size()); - if (read_eff > 0) { - zero_count = 0; // reset 0 count - if (read_eff != buffer.size()){ - LOGD("readBytes %d -> %d", buffer.size(), read_eff); - } - int write_eff = p_transform->write(buffer.data(), read_eff); - if (write_eff != read_eff){ - LOGE("write %d -> %d", read_eff, write_eff); - } - } else { - // limit the number of reads which provide 0; - if (++zero_count > MAX_ZERO_READ_COUNT){ - break; - } - // wait for some more data - delay(5); - } - } - restoreOutput(tmp); - } - - int result_len = min((int)len, result_queue.available()); - result_len = result_queue.readBytes(data, result_len); - LOGD("TransformationReader::readBytes: %d -> %d", (int)len, - result_len); - - return result_len; - } - - void end() { - result_queue_buffer.resize(0); - buffer.resize(0); - } - - /// Defines the queue size dependent on the read size - void setResultQueueFactor(int factor) { result_queue_factor = factor; } - - protected: - RingBuffer result_queue_buffer{0}; - QueueStream result_queue{result_queue_buffer}; // - Stream *p_stream = nullptr; - Vector buffer{0}; // we allocate memory only when needed - T *p_transform = nullptr; - bool active = false; - int result_queue_factor = 5; - - /// Makes sure that the data is written to the array - /// @param data - /// @return original output of the converter class - Print *setupOutput() { - Print *result = p_transform->getPrint(); - p_transform->setOutput((Print &)result_queue); - - return result; - } - /// @brief restores the original output in the converter class - /// @param out - void restoreOutput(Print *out) { - if (out) p_transform->setOutput(*out); - } -}; - -/** - * @brief Base class for chained converting streams - * @author Phil Schatzmann - * @copyright GPLv3 - * - */ -class ReformatBaseStream : public ModifyingStream { - public: - virtual void setStream(Stream &stream) override { - TRACED(); - p_stream = &stream; - p_print = &stream; - } - - virtual void setStream(AudioStream &stream) { - TRACED(); - p_stream = &stream; - p_print = &stream; - //setNotifyOnOutput(stream); - addNotifyAudioChange(stream); - } - - virtual void setOutput(AudioOutput &print) { - TRACED(); - p_print = &print; - addNotifyAudioChange(print); - } - - virtual void setOutput(Print &print) override { - TRACED(); - p_print = &print; - } - - virtual Print *getPrint() { return p_print; } - - virtual Stream *getStream() { return p_stream; } - - size_t readBytes(uint8_t *data, size_t len) override { - LOGD("ReformatBaseStream::readBytes: %d", (int)len); - return reader.readBytes(data, len); - } - - int available() override { - return DEFAULT_BUFFER_SIZE; // reader.availableForWrite(); - } - - int availableForWrite() override { - return DEFAULT_BUFFER_SIZE; // reader.availableForWrite(); - } - - virtual float getByteFactor() = 0; - - void end() override { - TRACED(); - AudioStream::end(); - reader.end(); - } - - /// Define the size of the interal read result queue: same as transformationReader().resizeResultQueue(size) - void resizeReadResultQueue(int size) { reader.resizeResultQueue(size);} - - /// Provides access to the TransformationReader - virtual TransformationReader &transformationReader() {return reader;} - - - protected: - TransformationReader reader; - Stream *p_stream = nullptr; - Print *p_print = nullptr; - - void setupReader() { - if (getStream() != nullptr) { - reader.begin(this, getStream()); - } - } -}; - -/** - * @brief Base class for Output Adpapters - * - */ -class AudioOutputAdapter : public AudioOutput {}; - -/** - * @brief Wrapper which converts a Print to a AudioOutput - * @ingroup tools - */ -class AdapterPrintToAudioOutput : public AudioOutputAdapter { - public: - AdapterPrintToAudioOutput() = default; - AdapterPrintToAudioOutput(Print &print) { setStream(print); } - void setStream(Print& out) { p_print = &out; } - void setAudioInfo(AudioInfo info) { cfg = info;} - size_t write(const uint8_t *data, size_t len) { - return p_print->write(data, len); - } - /// If true we need to release the related memory in the destructor - virtual bool isDeletable() { return true; } - - AudioInfo audioInfo() {return cfg; } - - protected: - Print *p_print = nullptr; - AudioInfo cfg; -}; - -/** - * @brief Wrapper which converts a AudioStream to a AudioOutput - * @ingroup tools - */ -class AdapterAudioStreamToAudioOutput : public AudioOutputAdapter { - public: - AdapterAudioStreamToAudioOutput() = default; - - AdapterAudioStreamToAudioOutput(AudioStream &stream) { setStream(stream); } - - void setStream(AudioStream &stream) { p_stream = &stream; } - - void setAudioInfo(AudioInfo info) override { p_stream->setAudioInfo(info); } - - AudioInfo audioInfo() override { return p_stream->audioInfo(); } - - size_t write(const uint8_t *data, size_t len) override { - return p_stream->write(data, len); - } - - int availableForWrite() override { return p_stream->availableForWrite(); } - - bool begin() override { return p_stream->begin(); } - - void end() override { p_stream->end(); } - - /// If true we need to release the related memory in the destructor - virtual bool isDeletable() override { return true; } - - operator bool() override { return *p_stream; } - - protected: - AudioStream *p_stream = nullptr; -}; - -/** - * @brief Wrapper which converts a AudioStream to a AudioOutput - * @ingroup tools - */ -class AdapterAudioOutputToAudioStream : public AudioStream { - public: - AdapterAudioOutputToAudioStream() = default; - - AdapterAudioOutputToAudioStream(AudioOutput &stream) { setOutput(stream); } - - void setOutput(AudioOutput &stream) { p_stream = &stream; } - - void setAudioInfo(AudioInfo info) override { p_stream->setAudioInfo(info); } - - AudioInfo audioInfo() override { return p_stream->audioInfo(); } - - size_t write(const uint8_t *data, size_t len) override { - return p_stream->write(data, len); - } - - bool begin() override { return p_stream->begin(); } - - void end() override { p_stream->end(); } - - /// If true we need to release the related memory in the destructor - virtual bool isDeletable() { return true; } - - operator bool() override { return *p_stream; } - - protected: - AudioOutput *p_stream = nullptr; -}; - -/** - * @brief Replicates the output to multiple destinations. - * @ingroup transform - * @author Phil Schatzmann - * @copyright GPLv3 - */ -class MultiOutput : public ModifyingOutput { - public: - /// Defines a MultiOutput with no final output: Define your outputs with add() - MultiOutput() = default; - - MultiOutput(Print &out) { add(out); } - - /// Defines a MultiOutput with a single final outputs, - MultiOutput(AudioOutput &out) { add(out); } - - MultiOutput(AudioStream &out) { add(out); } - - /// Defines a MultiOutput with 2 final outputs - MultiOutput(AudioOutput &out1, AudioOutput &out2) { - add(out1); - add(out2); - } - - /// Defines a MultiOutput with 2 final outputs - MultiOutput(AudioStream &out1, AudioStream &out2) { - add(out1); - add(out2); - } - - /// Defines a MultiOutput with 2 final outputs: Warning no support for - /// AudioInfo notifications. It is recommended to use individual add calls. - MultiOutput(Print &out1, Print &out2) { - add(out1); - add(out2); - } - - virtual ~MultiOutput() { - for (int j = 0; j < vector.size(); j++) { - if (vector[j]->isDeletable()) { - delete vector[j]; - } - } - } - - /// Add an additional AudioOutput output - void add(AudioOutput &out) { vector.push_back(&out); } - - /// Add an AudioStream to the output - void add(AudioStream &stream) { - AdapterAudioStreamToAudioOutput *out = - new AdapterAudioStreamToAudioOutput(stream); - vector.push_back(out); - } - - void add(Print &print) { - AdapterPrintToAudioOutput *out = new AdapterPrintToAudioOutput(print); - vector.push_back(out); - } - - void flush() { - for (int j = 0; j < vector.size(); j++) { - vector[j]->flush(); - } - } - - void setAudioInfo(AudioInfo info) { - for (int j = 0; j < vector.size(); j++) { - vector[j]->setAudioInfo(info); - } - } - - size_t write(const uint8_t *data, size_t len) { - for (int j = 0; j < vector.size(); j++) { - int open = len; - int start = 0; - while (open > 0) { - int written = vector[j]->write(data + start, open); - open -= written; - start += written; - } - } - return len; - } - - size_t write(uint8_t ch) { - for (int j = 0; j < vector.size(); j++) { - int open = 1; - while (open > 0) { - open -= vector[j]->write(ch); - } - } - return 1; - } - - protected: - Vector vector; - /// support for Pipleline - void setOutput(Print &out) { add(out); } -}; - -/** - * @brief AudioStream class that can define a start and (an optional) stop time - * Usually it is used to wrap an Audio Sink (e.g. I2SStream), but wrapping an - * Audio Source is supported as well. Only wrap classes which represent PCM - * data! - * @ingroup transform - * @author Phil Schatzmann - * @copyright GPLv3 - */ -class TimedStream : public ModifyingStream { - public: - TimedStream() = default; - - TimedStream(AudioStream &io, long startSeconds = 0, long endSeconds = -1) { - p_stream = &io; - p_print = &io; - p_info = &io; - setStartSec(startSeconds); - setEndSec(endSeconds); - } - - TimedStream(AudioOutput &o, long startSeconds = 0, long endSeconds = -1) { - p_print = &o; - p_info = &o; - setStartSec(startSeconds); - setEndSec(endSeconds); - } - - /// Defines the start time in seconds. The audio before the start time will be - /// skipped - void setStartSec(uint32_t startSeconds) { - start_ms = startSeconds * 1000; - calculateByteLimits(); - } - - /// Defines the start time in milliseconds - void setStartMs(uint32_t ms) { - start_ms = ms; - calculateByteLimits(); - } - - /// Defines (an optional) the end time in seconds. After the end time no audio - /// is played and available() will return 0 - void setEndSec(uint32_t endSeconds) { - end_ms = endSeconds * 1000; - calculateByteLimits(); - } - - /// Defines the (optional) end time in milliseconds - void setEndMs(uint32_t ms) { - end_ms = ms; - calculateByteLimits(); - } - - /// Returns true if we are in a valid time range and are still playing sound - bool isPlaying() { - if (current_bytes < start_bytes) return false; - if (end_bytes > 0 && current_bytes > end_bytes) return false; - return true; - } - - /// Returns true if we are not past the end time; - bool isActive() { - return (current_bytes < end_bytes && current_bytes >= start_bytes); - } - - bool begin(AudioInfo info) { - setAudioInfo(info); - return begin(); - } - - bool begin() override { - calculateByteLimits(); - current_bytes = 0; - LOGI("byte range %u - %u",(unsigned) start_bytes,(unsigned) end_bytes); - return true; - } - - operator bool() override { return isActive(); } - - /// Provides only data for the indicated start and end time. Only supported - /// for data which does not contain any heder information: so PCM, mp3 should - /// work! - size_t readBytes(uint8_t *data, size_t len) override { - // if reading is not supported we stop - if (p_stream == nullptr) return 0; - // Positioin to start - if (start_bytes > current_bytes){ - consumeBytes(start_bytes - current_bytes); - } - // if we are past the end we stop - if (!isActive()) return 0; - // read the data now - size_t result = 0; - do { - result = p_stream->readBytes(data, len); - current_bytes += len; - // ignore data before start time - } while (result > 0 && current_bytes < start_bytes); - return isPlaying() ? result : 0; - } - - /// Plays only data for the indiated start and end time - size_t write(const uint8_t *data, size_t len) override { - if (current_bytes >= end_bytes) return 0; - current_bytes += len; - if (current_bytes < start_bytes) return len; - return p_print->write(data, len); - } - - /// Provides the available bytes until the end time has reached - int available() override { - if (p_stream == nullptr) return 0; - return current_bytes < end_bytes ? p_stream->available() : 0; - } - - /// Updates the AudioInfo in the current object and in the source or target - void setAudioInfo(AudioInfo info) override { - AudioStream::setAudioInfo(info); - if (p_info) p_info->setAudioInfo(info); - calculateByteLimits(); - } - - int availableForWrite() override { - return current_bytes < end_bytes ? p_print->availableForWrite() : 0; - } - - /// Experimental: if used on mp3 you can set the compression ratio e.g. to 11 - /// which will be used to approximate the time - void setCompressionRatio(float ratio) { compression_ratio = ratio; } - - /// Calculates the bytes per second from the AudioInfo - int bytesPerSecond() { - return info.sample_rate * info.channels * info.bits_per_sample / 8; - } - - void setOutput(Print &out) override { p_print = &out; } - - void setStream(Stream &stream) override { - p_print = &stream; - p_stream = &stream; - } - - void setOutput(AudioOutput &out) { - p_print = &out; - p_info = &out; - } - - void setStream(AudioOutput &out) { - p_print = &out; - p_info = &out; - } - - void setStream(AudioStream &stream) { - p_print = &stream; - p_stream = &stream; - p_info = &stream; - } - - size_t size() { - return end_bytes - start_bytes; - } - - protected: - Stream *p_stream = nullptr; - Print *p_print = nullptr; - AudioInfoSupport *p_info = nullptr; - uint32_t start_ms = 0; - uint32_t end_ms = UINT32_MAX; - uint32_t start_bytes = 0; - uint32_t end_bytes = UINT32_MAX; - uint32_t current_bytes = 0; - float compression_ratio = 1.0; - - void consumeBytes(uint32_t len){ - int open = len; - uint8_t buffer[1024]; - while (open > 0){ - int toread = min(1024, open); - p_stream->readBytes(buffer, toread); - open -= toread; - } - current_bytes += len; - LOGD("consumed %u -> %u",(unsigned) len, (unsigned)current_bytes); - } - - void calculateByteLimits() { - float bytes_per_second = bytesPerSecond(); - if (bytes_per_second > 0) { - start_bytes = bytes_per_second * start_ms / compression_ratio / 1000; - end_bytes = bytes_per_second * end_ms / compression_ratio / 1000; - } else { - LOGE("AudioInfo not defined"); - } - } -}; - -/** - * @brief Flexible functionality to extract one or more channels from a - * multichannel signal. Warning: the destinatios added with addOutput - * are not automatically notified about audio changes. - * @ingroup transform - * @author Phil Schatzmann - * @copyright GPLv3 - * @tparam T - */ -class ChannelsSelectOutput : public AudioOutput { - public: - ChannelsSelectOutput() = default; - - bool begin(AudioInfo info) override { - setAudioInfo(info); - return begin(); - } - - bool begin() override { - AudioOutput::begin(); - // make sure that selected channels are valid - for (auto &out : out_channels) { - for (auto &ch : out.channels) { - if (ch > cfg.channels - 1) { - LOGE("Channel '%d' not valid for max %d channels", ch, cfg.channels); - return false; - } - } - } - return true; - } - - /// Define the channel to be selected to the specified output. 0: first - /// (=left) channel, 1: second (=right) channel - void addOutput(AudioOutput &out, uint16_t channel) { - Vector channels; - channels.push_back(channel); - ChannelSelectionOutputDef def; - def.channels = channels; - def.p_out = &out; - def.p_audio_info = &out; - out_channels.push_back(def); - } - - /// Define the channel to be selected to the specified output. 0: first - /// (=left) channel, 1: second (=right) channel - void addOutput(AudioStream &out, uint16_t channel) { - Vector channels; - channels.push_back(channel); - ChannelSelectionOutputDef def; - def.channels = channels; - def.p_out = &out; - def.p_audio_info = &out; - out_channels.push_back(def); - } - - /// Define the channel to be selected to the specified output. 0: first - /// (=left) channel, 1: second (=right) channel - void addOutput(Print &out, uint16_t channel) { - Vector channels; - channels.push_back(channel); - ChannelSelectionOutputDef def; - def.channels = channels; - def.p_out = &out; - out_channels.push_back(def); - } - - /// Define the stereo channels to be selected to the specified output. 0: - /// first (=left) channel, 1: second (=right) channel - void addOutput(Print &out, uint16_t left, uint16_t right) { - Vector channels; - channels.push_back(left); - channels.push_back(right); - ChannelSelectionOutputDef def; - def.channels = channels; - def.p_out = &out; - out_channels.push_back(def); - } - - /// Define the stereo channels to be selected to the specified output. 0: - /// first (=left) channel, 1: second (=right) channel - void addOutput(AudioOutput &out, uint16_t left, uint16_t right) { - Vector channels; - channels.push_back(left); - channels.push_back(right); - ChannelSelectionOutputDef def; - def.channels = channels; - def.p_out = &out; - def.p_audio_info = &out; - out_channels.push_back(def); - } - - /// Define the stereo channels to be selected to the specified output. 0: - /// first (=left) channel, 1: second (=right) channel - void addOutput(AudioStream &out, uint16_t left, uint16_t right) { - Vector channels; - channels.push_back(left); - channels.push_back(right); - ChannelSelectionOutputDef def; - def.channels = channels; - def.p_out = &out; - def.p_audio_info = &out; - out_channels.push_back(def); - } - - size_t write(const uint8_t *data, size_t len) override { - if (!is_active) return false; - LOGD("write %d", (int)len); - switch (cfg.bits_per_sample) { - case 16: - return writeT(data, len); - case 24: - return writeT(data, len); - case 32: - return writeT(data, len); - default: - return 0; - } - } - - void setAudioInfo(AudioInfo ai) override { - this->cfg = ai; - //notifyAudioChange(ai); - for (auto &info : out_channels) { - auto p_notify = info.p_audio_info; - if (p_notify != nullptr) { - AudioInfo result{ai}; - result.channels = info.channels.size(); - p_notify->setAudioInfo(result); - } - } - } - - protected: - struct ChannelSelectionOutputDef { - Print *p_out = nullptr; - AudioInfoSupport *p_audio_info = nullptr; - SingleBuffer buffer{CHANNEL_SELECT_BUFFER_SIZE}; - Vector channels{0}; - }; - Vector out_channels{0}; - - template - size_t writeT(const uint8_t *buffer, size_t size) { - if (!is_active) return 0; - int sample_count = size / sizeof(T); - //int result_size = sample_count / cfg.channels; - T *data = (T *)buffer; - - for (int i = 0; i < sample_count; i += cfg.channels) { - T *frame = data + i; - for (auto &out : out_channels) { - T out_frame[out.channels.size()]; - int ch_out = 0; - for (auto &ch : out.channels) { - // make sure we have a valid channel - int channel = (ch < cfg.channels) ? ch : cfg.channels - 1; - out_frame[ch_out++] = frame[channel]; - } - // write to buffer - size_t written = out.buffer.writeArray((const uint8_t *)&out_frame, sizeof(out_frame)); - // write buffer to final output - if (out.buffer.availableForWrite()write(out.buffer.data(), out.buffer.available()); - out.buffer.reset(); - } - // if (written != sizeof(out_frame)) { - // LOGW("Could not write all samples %d -> %d", sizeof(out_frame), written); - // } - } - } - return size; - } - - /// Determine number of channels for destination - int getChannels(Print *out, int defaultChannels) { - for (auto &channels_select : out_channels) { - if (channels_select.p_out == out) return channels_select.channels.size(); - } - return defaultChannels; - } -}; - -} // namespace audio_tools diff --git a/src/AudioTools/CoreAudio/AudioLogger.h b/src/AudioTools/CoreAudio/AudioLogger.h deleted file mode 100644 index 6f0596bf9d..0000000000 --- a/src/AudioTools/CoreAudio/AudioLogger.h +++ /dev/null @@ -1,7 +0,0 @@ -#pragma once -#include "AudioToolsConfig.h" -#if defined(USE_IDF_LOGGER) -# include "AudioTools/CoreAudio/AudioLoggerIDF.h" -#else -# include "AudioTools/CoreAudio/AudioLoggerSTD.h" -#endif \ No newline at end of file diff --git a/src/AudioTools/CoreAudio/AudioLoggerIDF.h b/src/AudioTools/CoreAudio/AudioLoggerIDF.h deleted file mode 100644 index 5dcfdd298b..0000000000 --- a/src/AudioTools/CoreAudio/AudioLoggerIDF.h +++ /dev/null @@ -1,36 +0,0 @@ -#pragma once - -#include "AudioToolsConfig.h" -#include "esp_log.h" - -#if USE_AUDIO_LOGGING - -#ifndef LOG_METHOD -# define LOG_METHOD __PRETTY_FUNCTION__ -#endif - -#define TAG_AUDIO "audio-tools" - -#define LOGD(...) ESP_LOGD(TAG_AUDIO,__VA_ARGS__); -#define LOGI(...) ESP_LOGI(TAG_AUDIO,__VA_ARGS__); -#define LOGW(...) ESP_LOGW(TAG_AUDIO,__VA_ARGS__); -#define LOGE(...) ESP_LOGE(TAG_AUDIO,__VA_ARGS__); - -#define TRACED() ESP_LOGD(TAG_AUDIO, "%s", LOG_METHOD); -#define TRACEI() ESP_LOGI(TAG_AUDIO, "%s", LOG_METHOD); -#define TRACEW() ESP_LOGW(TAG_AUDIO, "%s", LOG_METHOD); -#define TRACEE() ESP_LOGE(TAG_AUDIO, "%s", LOG_METHOD); - -#else - -// Switch off logging -#define LOGD(...) -#define LOGI(...) -#define LOGW(...) -#define LOGE(...) -#define TRACED() -#define TRACEI() -#define TRACEW() -#define TRACEE() - -#endif diff --git a/src/AudioTools/CoreAudio/AudioLoggerSTD.h b/src/AudioTools/CoreAudio/AudioLoggerSTD.h deleted file mode 100644 index bbd58a75ab..0000000000 --- a/src/AudioTools/CoreAudio/AudioLoggerSTD.h +++ /dev/null @@ -1,284 +0,0 @@ -#pragma once - -#include "AudioToolsConfig.h" -#if defined(ARDUINO) && !defined(IS_MIN_DESKTOP) -#include "Print.h" -#endif - -#include "AudioTools/Concurrency/LockGuard.h" -#if defined(RP2040) -#include "AudioTools/Concurrency/RP2040/MutexRP2040.h" -#elif defined(ESP32) -#include "AudioTools/Concurrency/RTOS/MutexRTOS.h" -#include "esp_log.h" -#endif - -#if USE_AUDIO_LOGGING - -namespace audio_tools { - -#if defined(ESP32) -static MutexRTOS audio_logger_mutex; -#elif defined(RP2040) -static MutexRP2040 audio_logger_mutex; -#else -static MutexBase audio_logger_mutex; // no locking -#endif - -/** - * @brief A simple Logger that writes messages dependent on the log level - * @ingroup tools - * @author Phil Schatzmann - * @copyright GPLv3 - * - */ -class AudioLogger { - public: - /** - * @brief Supported log levels. You can change the default log level with the - * help of the PICO_LOG_LEVEL define. - * - */ - enum LogLevel { Debug, Info, Warning, Error }; - - /// activate the logging - void begin(Print& out, LogLevel level = LOG_LEVEL) { - this->log_print_ptr = &out; - this->log_level = level; - } - - // defines the log level - void setLogLevel(LogLevel level) { this->log_level = level; } - - /// checks if the logging is active - bool isLogging(LogLevel level = Info) { - return log_print_ptr != nullptr && level >= log_level; - } - - AudioLogger& prefix(const char* file, int line, LogLevel current_level) { - printPrefix(file, line, current_level); - return *this; - } - - void println() { -#if defined(IS_DESKTOP) || defined(IS_DESKTOP_WITH_TIME_ONLY) - fprintf(stderr, "%s\n", print_buffer); - fflush(stderr); -#else - log_print_ptr->println(print_buffer); - log_print_ptr->flush(); -#endif - print_buffer[0] = 0; - } - - char* str() { return print_buffer; } - - /// provides the singleton instance - static AudioLogger& instance() { - static AudioLogger* ptr; - if (ptr == nullptr) { - ptr = new AudioLogger; - } - return *ptr; - } - - LogLevel level() { return log_level; } - - void print(const char* c) { log_print_ptr->print(c); } - - void printChar(char c) { log_print_ptr->print(c); } - - void printCharHex(char c) { - char tmp[5]; - unsigned char val = c; - snprintf(tmp, 5, "%02X ", val); - log_print_ptr->print(tmp); - } - - protected: - Print* log_print_ptr = &LOG_STREAM; - LogLevel log_level = LOG_LEVEL; - char print_buffer[LOG_PRINTF_BUFFER_SIZE]; - - AudioLogger() {} - - const char* levelName(LogLevel level) const { - switch (level) { - case Debug: - return "D"; - case Info: - return "I"; - case Warning: - return "W"; - case Error: - return "E"; - } - return ""; - } - - int printPrefix(const char* file, int line, LogLevel current_level) const { - const char* file_name = strrchr(file, '/') ? strrchr(file, '/') + 1 : file; - const char* level_code = levelName(current_level); - int len = log_print_ptr->print("["); - len += log_print_ptr->print(level_code); - len += log_print_ptr->print("] "); - len += log_print_ptr->print(file_name); - len += log_print_ptr->print(" : "); - len += log_print_ptr->print(line); - len += log_print_ptr->print(" - "); - return len; - } -}; - -static AudioLogger& AudioToolsLogger = AudioLogger::instance(); -using AudioToolsLogLevel = AudioLogger::LogLevel; - -/// Class specific custom log level -class CustomLogLevel { - public: - AudioLogger::LogLevel getActual() { return actual; } - - /// Defines a custom level - void set(AudioLogger::LogLevel level) { - active = true; - original = AudioLogger::instance().level(); - actual = level; - } - - /// sets the defined log level - void set() { - if (active) { - AudioLogger::instance().begin(Serial, actual); - } - } - /// resets to the original log level - void reset() { - if (active) { - AudioLogger::instance().begin(Serial, original); - } - } - - protected: - bool active = false; - AudioLogger::LogLevel original; - AudioLogger::LogLevel actual; -}; - -} // namespace audio_tools - -// #define LOG_OUT(level, fmt, ...) -// {AudioLogger::instance().prefix(__FILE__,__LINE__, level);cont char PROGMEM -// *fmt_P=F(fmt); snprintf_P(AudioLogger::instance().str(), -// LOG_PRINTF_BUFFER_SIZE, fmt, ##__VA_ARGS__); -// AudioLogger::instance().println();} -#define LOG_OUT_PGMEM(level, fmt, ...) \ - { \ - LockGuard guard{audio_logger_mutex}; \ - AudioLogger::instance().prefix(__FILE__, __LINE__, level); \ - snprintf(AudioLogger::instance().str(), LOG_PRINTF_BUFFER_SIZE, PSTR(fmt), \ - ##__VA_ARGS__); \ - AudioLogger::instance().println(); \ - } - -#define LOG_OUT(level, fmt, ...) \ - { \ - LockGuard guard{audio_logger_mutex}; \ - AudioLogger::instance().prefix(__FILE__, __LINE__, level); \ - snprintf(AudioLogger::instance().str(), LOG_PRINTF_BUFFER_SIZE, fmt, \ - ##__VA_ARGS__); \ - AudioLogger::instance().println(); \ - } -#define LOG_MIN(level) \ - { \ - LockGuard guard{audio_logger_mutex}; \ - AudioLogger::instance().prefix(__FILE__, __LINE__, level); \ - AudioLogger::instance().println(); \ - } - -#ifdef LOG_NO_MSG -#define LOGD(fmt, ...) \ - if (AudioLogger::instance().level() <= AudioLogger::Debug) { \ - LOG_MIN(AudioLogger::Debug); \ - } -#define LOGI(fmt, ...) \ - if (AudioLogger::instance().level() <= AudioLogger::Info) { \ - LOG_MIN(AudioLogger::Info); \ - } -#define LOGW(fmt, ...) \ - if (AudioLogger::instance().level() <= AudioLogger::Warning) { \ - LOG_MIN(AudioLogger::Warning); \ - } -#define LOGE(fmt, ...) \ - if (AudioLogger::instance().level() <= AudioLogger::Error) { \ - LOG_MIN(AudioLogger::Error); \ - } -#else -// Log statments which store the fmt string in Progmem -#define LOGD(fmt, ...) \ - if (AudioLogger::instance().level() <= AudioLogger::Debug) { \ - LOG_OUT_PGMEM(AudioLogger::Debug, fmt, ##__VA_ARGS__); \ - } -#define LOGI(fmt, ...) \ - if (AudioLogger::instance().level() <= AudioLogger::Info) { \ - LOG_OUT_PGMEM(AudioLogger::Info, fmt, ##__VA_ARGS__); \ - } -#define LOGW(fmt, ...) \ - if (AudioLogger::instance().level() <= AudioLogger::Warning) { \ - LOG_OUT_PGMEM(AudioLogger::Warning, fmt, ##__VA_ARGS__); \ - } -#define LOGE(fmt, ...) \ - if (AudioLogger::instance().level() <= AudioLogger::Error) { \ - LOG_OUT_PGMEM(AudioLogger::Error, fmt, ##__VA_ARGS__); \ - } -#endif - -// Just log file and line -#if defined(NO_TRACED) || defined(NO_TRACE) -#define TRACED() -#else -#define TRACED() \ - if (AudioLogger::instance().level() <= AudioLogger::Debug) { \ - LOG_OUT(AudioLogger::Debug, LOG_METHOD); \ - } -#endif - -#if defined(NO_TRACEI) || defined(NO_TRACE) -#define TRACEI() -#else -#define TRACEI() \ - if (AudioLogger::instance().level() <= AudioLogger::Info) { \ - LOG_OUT(AudioLogger::Info, LOG_METHOD); \ - } -#endif - -#if defined(NO_TRACEW) || defined(NO_TRACE) -#define TRACEW() -#else -#define TRACEW() \ - if (AudioLogger::instance().level() <= AudioLogger::Warning) { \ - LOG_OUT(AudioLogger::Warning, LOG_METHOD); \ - } -#endif - -#if defined(NO_TRACEE) || defined(NO_TRACE) -#define TRACEE() -#else -#define TRACEE() \ - if (AudioLogger::instance().level() <= AudioLogger::Error) { \ - LOG_OUT(AudioLogger::Error, LOG_METHOD); \ - } -#endif - -#else - -// Switch off logging -#define LOGD(...) -#define LOGI(...) -#define LOGW(...) -#define LOGE(...) -#define TRACED() -#define TRACEI() -#define TRACEW() -#define TRACEE() - -#endif diff --git a/src/AudioTools/CoreAudio/AudioMetaData.h b/src/AudioTools/CoreAudio/AudioMetaData.h deleted file mode 100644 index 54d9e45028..0000000000 --- a/src/AudioTools/CoreAudio/AudioMetaData.h +++ /dev/null @@ -1,2 +0,0 @@ -#pragma once -#include "AudioTools/CoreAudio/AudioMetaData/MetaData.h" diff --git a/src/AudioTools/CoreAudio/AudioMetaData/MetaDataFilter.h b/src/AudioTools/CoreAudio/AudioMetaData/MetaDataFilter.h deleted file mode 100644 index 7f5f821d0f..0000000000 --- a/src/AudioTools/CoreAudio/AudioMetaData/MetaDataFilter.h +++ /dev/null @@ -1,233 +0,0 @@ -#pragma once -#include "AudioLogger.h" -#include "AudioTools/AudioCodecs/AudioCodecsBase.h" -#include "AudioTools/CoreAudio/AudioOutput.h" -#include "AudioTools/CoreAudio/Buffers.h" - -namespace audio_tools { - -/** - * @brief Class which filters out ID3v1 and ID3v2 Metadata and provides only the - * audio data to the decoder - * @ingroup metadata - * @author Phil Schatzmann - * @copyright GPLv3 - */ -class MetaDataFilter : public AudioOutput { - public: - /// Default Constructor - MetaDataFilter() = default; - - /// Constructor which assigns the decoder - MetaDataFilter(Print &out) { setOutput(out); } - /// Constructor which assigns the decoder - MetaDataFilter(AudioWriter &out) { setOutput(out); } - - /// Defines the decoder to which we write the data - void setOutput(Print &out) { p_out = &out; } - /// Defines the decoder to which we write the data - void setOutput(AudioWriter &out) { p_writer = &out; } - - /// (Re)starts the processing - bool begin() override { - TRACED(); - start = 0; - if (p_writer) p_writer->begin(); - return true; - } - - void end() override { - if (p_writer) p_writer->end(); - } - - /// Writes the data to the decoder - size_t write(const uint8_t *data, size_t len) override { - LOGI("write: %u", (unsigned)len); - size_t result = len; - // prevent npe - if ((p_out == nullptr && p_writer == nullptr) || (data == nullptr) || - (len == 0)) - return 0; - - // find tag - int meta_len = 0; - if (findTag(data, len, metadata_range.from, meta_len)) { - current_pos = 0; - metadata_range.setLen(meta_len); - LOGI("ignoring metadata at pos: %d len: %d", metadata_range.from, - meta_len); - } - - // nothing to ignore - if (!metadata_range.isDefined()) { - if (p_out) return p_out->write(data, len); - if (p_writer) return p_writer->write(data, len); - } - - // ignore data in metadata range - SingleBuffer tmp(len); - for (int j = 0; j < len; j++) { - if (!metadata_range.inRange(current_pos)) { - tmp.write(data[j]); - } - current_pos++; - } - - // write partial data - size_t to_write = tmp.available(); - if (to_write > 0) { - LOGI("output: %u", (unsigned)to_write); - size_t written = 0; - if (p_out) written = p_out->write(tmp.data(), to_write); - if (p_writer) written = p_writer->write(tmp.data(), to_write); - assert(to_write == written); - metadata_range.clear(); - } else { - LOGI("output ignored"); - } - - // reset for next run - if (current_pos > metadata_range.to) { - current_pos = 0; - metadata_range.clear(); - } - - return result; - } - - protected: - Print *p_out = nullptr; - AudioWriter *p_writer = nullptr; - int current_pos = 0; - enum MetaType { TAG, TAG_PLUS, ID3 }; - int start = 0; - /// Metadata range - struct Range { - int from = -1; - int to = -1; - - bool inRange(int pos) { return pos >= from && pos < to; } - void setLen(int len) { to = from + len; } - - void clear() { - from = -1; - to = -1; - } - bool isDefined() { return from != -1; } - } metadata_range; - - /// ID3 verion 2 TAG Header (10 bytes) - struct ID3v2 { - uint8_t header[3]; // ID3 - uint8_t version[2]; - uint8_t flags; - uint8_t size[4]; - } tagv2; - - /// determines if the data conatins a ID3v1 or ID3v2 tag - bool findTag(const uint8_t *data, size_t len, int &pos_tag, int &meta_len) { - MetaType tag_type; - if (find((const char *)data, len, pos_tag, tag_type)) { - switch (tag_type) { - case TAG: - LOGD("TAG"); - meta_len = 128; - break; - case TAG_PLUS: - LOGD("TAG+"); - meta_len = 227; - break; - case ID3: - LOGD("ID3"); - memcpy(&tagv2, data + pos_tag, sizeof(ID3v2)); - meta_len = calcSizeID3v2(tagv2.size); - break; - } - return true; - } - return false; - } - - // calculate the synch save size for ID3v2 - uint32_t calcSizeID3v2(uint8_t chars[4]) { - uint32_t byte0 = chars[0]; - uint32_t byte1 = chars[1]; - uint32_t byte2 = chars[2]; - uint32_t byte3 = chars[3]; - return byte0 << 21 | byte1 << 14 | byte2 << 7 | byte3; - } - - /// find the tag position in the string; - bool find(const char *str, size_t len, int &pos, MetaType &type) { - if (str == nullptr || len <= 0) return false; - for (size_t j = 0; j <= len - 3; j++) { - if (str[j] == 'T' && str[j + 1] == 'A' && str[j + 2] == 'G') { - type = str[j + 3] == '+' ? TAG_PLUS : TAG; - pos = j; - return true; - } else if (str[j] == 'I' && str[j + 1] == 'D' && str[j + 2] == '3') { - type = ID3; - pos = j; - return true; - } - } - return false; - } -}; - -/*** - * MetaDataFiler applied to the indicated decoder: Class which filters out ID3v1 - * and ID3v2 Metadata and provides only the audio data to the decoder - * @ingroup metadata - * @ingroup codecs - * @author Phil Schatzmann - * @copyright GPLv3 - */ -class MetaDataFilterDecoder : public AudioDecoder { - public: - MetaDataFilterDecoder(AudioDecoder &decoder) { - p_decoder = &decoder; - filter.setOutput(decoder); - } - - bool begin() override { - is_active = true; - filter.begin(); - p_decoder->begin(); - return AudioDecoder::begin(); - } - - void end() override { - is_active = false; - filter.end(); - AudioDecoder::end(); - } - - size_t write(const uint8_t *data, size_t len) override { - return filter.write(data, len); - } - - void setOutput(AudioStream &out_stream) override { - p_decoder->setOutput(out_stream); - addNotifyAudioChange(out_stream); - } - - virtual void setOutput(AudioOutput &out_stream) override { - p_decoder->setOutput(out_stream); - addNotifyAudioChange(out_stream); - } - - /// Defines where the decoded result is written to - virtual void setOutput(Print &out_stream) override { - p_decoder->setOutput(out_stream); - } - - operator bool() override { return p_print != nullptr && is_active; } - - protected: - AudioDecoder *p_decoder = nullptr; - MetaDataFilter filter; - bool is_active = false; -}; - -} // namespace audio_tools \ No newline at end of file diff --git a/src/AudioTools/CoreAudio/AudioMetaData/MetaDataICY.h b/src/AudioTools/CoreAudio/AudioMetaData/MetaDataICY.h deleted file mode 100644 index 218305c78d..0000000000 --- a/src/AudioTools/CoreAudio/AudioMetaData/MetaDataICY.h +++ /dev/null @@ -1,288 +0,0 @@ -#pragma once -#include //isascii -#include "AudioToolsConfig.h" -#include "AbstractMetaData.h" -#include "AudioTools/CoreAudio/AudioBasic/StrView.h" -#include "AudioTools/CoreAudio/AudioHttp/AbstractURLStream.h" - -namespace audio_tools { - -/** - * @defgroup metadata-icy ICY - * @ingroup metadata - * @brief Icecast/Shoutcast Metadata - **/ - -/** - * @brief Icecast/Shoutcast Metadata Handling. - * Metadata class which splits the data into audio and metadata. The result is - * provided via callback methods. see - * https://www.codeproject.com/Articles/11308/SHOUTcast-Stream-Ripper - * @ingroup metadata-icy - * @author Phil Schatzmann - * @copyright GPLv3 - */ -class MetaDataICY : public AbstractMetaData { - enum Status { ProcessData, ProcessMetaData, SetupSize }; - - public: - MetaDataICY() = default; - - /// We just process the Metadata and ignore the audio info - MetaDataICY(int metaint) { setIcyMetaInt(metaint); } - - virtual ~MetaDataICY() {} - - /// Defines the ICE metaint value which is provided by the web call! - virtual void setIcyMetaInt(int value) override { - this->mp3_blocksize = value; - } - - /// Defines the metadata callback function - virtual void setCallback(void (*fn)(MetaDataType info, const char* str, - int len)) override { - callback = fn; - } - - /// Defines the audio callback function - virtual void setAudioDataCallback(void (*fn)(const uint8_t* str, int len), - int bufferLen = 1024) { - dataBuffer = new uint8_t[bufferLen]; - dataCallback = fn; - dataLen = 0; - dataPos = 0; - } - - /// Resets all counters and restarts the prcessing - virtual void begin() override { - clear(); - LOGI("mp3_blocksize: %d", mp3_blocksize); - } - - /// Resets all counters and restarts the prcessing - virtual void end() override { clear(); } - - /// Writes the data in order to retrieve the metadata and perform the - /// corresponding callbacks - virtual size_t write(const uint8_t* data, size_t len) override { - if (callback != nullptr) { - for (size_t j = 0; j < len; j++) { - processChar((char)data[j]); - } - } - return len; - } - - /// Returns the actual status of the state engine for the current byte - virtual Status status() { return currentStatus; } - - /// returns true if the actual bytes is an audio data byte (e.g.mp3) - virtual bool isData() { return currentStatus == ProcessData; } - - /// Returns true if the ICY stream contains metadata - virtual bool hasMetaData() { return this->mp3_blocksize > 0; } - - /// provides the metaint - virtual int metaInt() { return mp3_blocksize; } - - /// character based state engine - virtual void processChar(char ch) { - switch (nextStatus) { - case ProcessData: - currentStatus = ProcessData; - processData(ch); - - // increment data counter and determine next status - ++totalData; - if (totalData >= mp3_blocksize) { - LOGI("Data ended") - totalData = 0; - nextStatus = SetupSize; - } - break; - - case SetupSize: - currentStatus = SetupSize; - totalData = 0; - metaDataPos = 0; - metaDataLen = metaSize(ch); - LOGI("metaDataLen: %d", metaDataLen); - if (metaDataLen > 0) { - if (metaDataLen > 200) { - LOGI("Unexpected metaDataLen -> processed as data"); - nextStatus = ProcessData; - } else { - LOGI("Metadata found"); - setupMetaData(metaDataLen); - nextStatus = ProcessMetaData; - } - } else { - LOGI("Data found"); - nextStatus = ProcessData; - } - break; - - case ProcessMetaData: - currentStatus = ProcessMetaData; - metaData[metaDataPos++] = ch; - if (metaDataPos >= metaDataLen) { - processMetaData(metaData.data(), metaDataLen); - LOGI("Metadata ended") - nextStatus = ProcessData; - } - break; - } - } - - protected: - Status nextStatus = ProcessData; - Status currentStatus = ProcessData; - void (*callback)(MetaDataType info, const char* str, int len) = nullptr; - Vector metaData{0}; - int totalData = 0; - int mp3_blocksize = 0; - int metaDataMaxLen = 0; - int metaDataLen = 0; - int metaDataPos = 0; - bool is_data; // indicates if the current byte is a data byte - // data - uint8_t* dataBuffer = nullptr; - void (*dataCallback)(const uint8_t* str, int len) = nullptr; - int dataLen = 0; - int dataPos = 0; - - virtual void clear() { - nextStatus = ProcessData; - totalData = 0; - metaData.resize(0); - metaDataLen = 0; - metaDataPos = 0; - dataLen = 0; - dataPos = 0; - } - - /// determines the meta data size from the size byte - virtual int metaSize(uint8_t metaSize) { return metaSize * 16; } - - inline bool isAscii(uint8_t ch){ return ch < 128;} - - /// Make sure that the result is a valid ASCII string - virtual bool isAscii(char* result, int l) { - // check on first 10 characters - int m = l < 5 ? l : 10; - for (int j = 0; j < m; j++) { - if (!isAscii(result[j])) return false; - } - return true; - } - - /// allocates the memory to store the metadata / we support changing sizes - virtual void setupMetaData(int meta_size) { - TRACED(); - metaData.resize(meta_size + 1); - metaDataMaxLen = meta_size; - memset(metaData.data(), 0, meta_size + 1); - } - - /// e.g. StreamTitle=' House Bulldogs - But your love (Radio - /// Edit)';StreamUrl=''; - virtual void processMetaData(char* metaData, int len) { - // CHECK_MEMORY(); - TRACED(); - metaData[len] = 0; - if (isAscii(metaData, 12)) { - LOGI("%s", metaData); - StrView meta(metaData, len + 1, len); - int start = meta.indexOf("StreamTitle="); - if (start >= 0) { - start += 12; - } - int end = meta.indexOf("';"); - if (start >= 0 && end > start) { - metaData[end] = 0; - if (callback != nullptr) { - callback(Title, (const char*)metaData + start + 1, end - start); - } - } - // CHECK_MEMORY(); - } else { - // CHECK_MEMORY(); - LOGW("Unexpected Data: %s", metaData); - } - } - - /// Collects the data in a buffer and executes the callback when the buffer is - /// full - virtual void processData(char ch) { - if (dataBuffer != nullptr) { - dataBuffer[dataPos++] = ch; - // output data via callback - if (dataPos >= dataLen) { - dataCallback(dataBuffer, dataLen); - dataPos = 0; - } - } - } -}; - -/** - * @brief Resolve icy-metaint from HttpRequest and execute metadata callbacks - * @ingroup metadata-icy - * @author Phil Schatzmann - * @copyright GPLv3 - */ - -class ICYUrlSetup { - public: - /// Fills the metaint from the Http Request and executes metadata callbacks on - /// http reply parameters - int setup(AbstractURLStream& url) { - TRACED(); - p_url = &url; - const char* iceMetaintStr = url.getReplyHeader("icy-metaint"); - if (iceMetaintStr) { - LOGI("icy-metaint: %s", iceMetaintStr); - } else { - LOGE("icy-metaint not defined"); - } - StrView value(iceMetaintStr); - int iceMetaint = value.toInt(); - return iceMetaint; - } - - /// Executes the metadata callbacks with data provided from the http request - /// result parameter - void executeCallback(void (*callback)(MetaDataType info, const char* str, - int len)) { - TRACEI(); - if (callback == nullptr) { - LOGW("callback not defined") - } - if (p_url == nullptr) { - LOGW("http not defined") - } - // Callbacks filled from url reply for icy - if (callback != nullptr && p_url != nullptr) { - // handle icy parameters - StrView genre(p_url->getReplyHeader("icy-genre")); - if (!genre.isEmpty()) { - callback(Genre, genre.c_str(), genre.length()); - } - - StrView descr(p_url->getReplyHeader("icy-description")); - if (!descr.isEmpty()) { - callback(Description, descr.c_str(), descr.length()); - } - - StrView name(p_url->getReplyHeader("icy-name")); - if (!name.isEmpty()) { - callback(Name, name.c_str(), name.length()); - } - } - } - - protected: - AbstractURLStream* p_url = nullptr; -}; - -} // namespace audio_tools diff --git a/src/AudioTools/CoreAudio/AudioMetaData/MimeDetector.h b/src/AudioTools/CoreAudio/AudioMetaData/MimeDetector.h deleted file mode 100644 index 5de5681186..0000000000 --- a/src/AudioTools/CoreAudio/AudioMetaData/MimeDetector.h +++ /dev/null @@ -1,182 +0,0 @@ -#pragma once - -#include "AudioTools/AudioCodecs/HeaderParserAAC.h" -#include "AudioTools/AudioCodecs/HeaderParserMP3.h" -#include "AudioTools/CoreAudio/AudioBasic/StrView.h" - -namespace audio_tools { - -/** - * @brief Logic to detemine the mime type from the content. - * By default the following mime types are supported (audio/aac, audio/mpeg, - * audio/vnd.wave, audio/ogg). You can register your own custom detection logic - * to cover additional file types. - * - * Please not that the distinction between mp3 and aac is difficult and might - * fail is some cases - * @ingroup codecs - * @ingroup decoder - * @author Phil Schatzmann - * @copyright GPLv3 - */ - -class MimeDetector { - public: - MimeDetector() { - setCheck("audio/vnd.wave", checkWAV); - setCheck("audio/ogg", checkOGG); - setCheck("video/MP2T", checkMP2T); - setCheck("audio/prs.sid", checkSID); - setCheck("audio/mpeg", checkMP3Ext); - setCheck("audio/m4a", checkM4A); - setCheck("audio/aac", checkAACExt); - } - - bool begin() { - is_first = true; - return true; - } - - /// write the header to determine the mime - size_t write(uint8_t* data, size_t len) { - actual_mime = default_mime; - determineMime(data, len); - return len; - } - - /// adds/updates the checking logic for the indicated mime - void setCheck(const char* mime, bool (*check)(uint8_t* start, size_t len)) { - StrView mime_str{mime}; - for (int j = 0; j < checks.size(); j++) { - Check l_check = checks[j]; - if (mime_str.equals(l_check.mime)) { - l_check.check = check; - return; - } - } - Check check_to_add{mime, check}; - checks.push_back(check_to_add); - } - - // /// Define the callback that will notify about mime changes - void setMimeCallback(void (*callback)(const char*)) { - TRACED(); - this->notifyMimeCallback = callback; - } - - /// Provides the actual mime type, that was determined from the first - /// available data - const char* mime() { return actual_mime; } - - static bool checkAAC(uint8_t* start, size_t len) { - return start[0] == 0xFF && - (start[1] == 0xF0 || start[1] == 0xF1 || start[1] == 0xF9); - } - - static bool checkAACExt(uint8_t* start, size_t len) { - // checking logic for files - if (memcmp(start + 4, "ftypM4A", 7) == 0) { - return true; - } - // check for streaming - HeaderParserAAC aac; - // it should start with a synch word - int pos = aac.findSyncWord((const uint8_t*)start, len); - if (pos == -1) { - return false; - } - // make sure that it is not an mp3 - if (aac.isValid(start + pos, len - pos)) { - return false; - } - return true; - } - - static bool checkMP3(uint8_t* start, size_t len) { - return memcmp(start, "ID3", 3) == 0 || - (start[0] == 0xFF && ((start[1] & 0xE0) == 0xE0)); - } - - static bool checkMP3Ext(uint8_t* start, size_t len) { - HeaderParserMP3 mp3; - return mp3.isValid(start, len); - } - - static bool checkWAV(uint8_t* start, size_t len) { - return memcmp(start, "RIFF", 4) == 0; - } - - static bool checkOGG(uint8_t* start, size_t len) { - return memcmp(start, "OggS", 4) == 0; - } - - /// MPEG-2 TS Byte Stream Format - static bool checkMP2T(uint8_t* start, size_t len) { - if (len < 189) return start[0] == 0x47; - - return start[0] == 0x47 && start[188] == 0x47; - } - - /// Commodore 64 SID File - static bool checkSID(uint8_t* start, size_t len) { - return memcmp(start, "PSID", 4) == 0 || memcmp(start, "RSID", 4) == 0; - } - - static bool checkM4A(uint8_t* header, size_t len) { - if (len < 12) return false; - - // Check for "ftyp" at offset 4 - if (memcmp(header + 4, "ftyp", 4) != 0) return false; - - // Check for "M4A " or similar major brand - if (memcmp(header + 8, "M4A ", 4) == 0 || - memcmp(header + 8, "mp42", 4) == 0 || - memcmp(header + 8, "isom", 4) == 0) - return true; - - return false; - } - - /// Provides the default mime type if no mime could be determined - void setDefaultMime(const char* mime) { default_mime = mime; } - - protected: - struct Check { - const char* mime = nullptr; - bool (*check)(uint8_t* data, size_t len) = nullptr; - Check(const char* mime, bool (*check)(uint8_t* data, size_t len)) { - this->mime = mime; - this->check = check; - } - Check() = default; - }; - Vector checks{0}; - bool is_first = false; - const char* actual_mime = nullptr; - const char* default_mime = nullptr; - void (*notifyMimeCallback)(const char* mime) = nullptr; - - /// Update the mime type - void determineMime(void* data, size_t len) { - if (is_first) { - actual_mime = lookupMime((uint8_t*)data, len); - if (notifyMimeCallback != nullptr && actual_mime != nullptr) { - notifyMimeCallback(actual_mime); - } - is_first = false; - } - } - - /// Default logic which supports aac, mp3, wav and ogg - const char* lookupMime(uint8_t* data, size_t len) { - for (int j = 0; j < checks.size(); j++) { - Check l_check = checks[j]; - if (l_check.check(data, len)) { - return l_check.mime; - } - } - return default_mime; - } -}; - -} // namespace audio_tools diff --git a/src/AudioTools/CoreAudio/AudioPWM.h b/src/AudioTools/CoreAudio/AudioPWM.h deleted file mode 100644 index 0a732925a9..0000000000 --- a/src/AudioTools/CoreAudio/AudioPWM.h +++ /dev/null @@ -1,3 +0,0 @@ -#pragma once - -#include "AudioTools/CoreAudio/AudioPWM/AudioPWM.h" diff --git a/src/AudioTools/CoreAudio/AudioPWM/AudioPWM.h b/src/AudioTools/CoreAudio/AudioPWM/AudioPWM.h deleted file mode 100644 index 4822f87e90..0000000000 --- a/src/AudioTools/CoreAudio/AudioPWM/AudioPWM.h +++ /dev/null @@ -1,101 +0,0 @@ -#pragma once -#include "AudioToolsConfig.h" -#if defined(USE_PWM) - -#include "AudioTools/CoreAudio/AudioPWM/PWMAudioESP32.h" -#include "AudioTools/CoreAudio/AudioPWM/PWMAudioMBED.h" -#include "AudioTools/CoreAudio/AudioPWM/PWMAudioRP2040.h" -#include "AudioTools/CoreAudio/AudioPWM/PWMAudioRenesas.h" -#include "AudioTools/CoreAudio/AudioPWM/PWMAudioSTM32.h" -// this is experimental at the moment -#include "AudioTools/CoreAudio/AudioPWM/PWMAudioAVR.h" - -namespace audio_tools { - -/** - * @brief Common functionality for PWM output. We generate audio using PWM - * with a frequency that is above the hearing range. The sample rate is - * usually quite restricted, so we also automatically decimate the data. - * Further info see PWMConfig - * @ingroup io - */ -class PWMAudioOutput : public AudioOutput { - public: - ~PWMAudioOutput() { - if (pwm.isTimerStarted()) { - end(); - } - } - - virtual PWMConfig defaultConfig(RxTxMode mode=TX_MODE) { - if (mode!=TX_MODE) LOGE("mode not supported: using TX_MODE"); - return pwm.defaultConfig(); - } - - PWMConfig config() { return audio_config; } - - /// updates the sample rate dynamically - void setAudioInfo(AudioInfo info) override { - TRACEI(); - AudioOutput::cfg = info; - PWMConfig cfg = audio_config; - if (cfg.sample_rate != info.sample_rate || cfg.channels != info.channels || - cfg.bits_per_sample != info.bits_per_sample) { - cfg.sample_rate = info.sample_rate; - cfg.bits_per_sample = info.bits_per_sample; - cfg.channels = info.channels; - end(); - begin(cfg); - cfg.logInfo(); - } - } - - AudioInfo audioInfoOut() override { - AudioInfo result = audioInfo(); - result.sample_rate = pwm.effectiveOutputSampleRate(); - return result; - } - - /// starts the processing using Streams - bool begin(PWMConfig config) { - TRACED(); - this->audio_config = config; - return begin(); - } - - bool begin() { - TRACED(); - AudioOutput::setAudioInfo(audio_config); - return pwm.begin(audio_config); - } - - virtual void end() override { pwm.end(); } - - int availableForWrite() override { return pwm.availableForWrite(); } - - // blocking write for an array: we expect a singed value and convert it into a - // unsigned - size_t write(const uint8_t *data, size_t len) override { - return pwm.write(data, len); - } - - // When the timer does not have enough data we increase the underflow_count; - uint32_t underflowsPerSecond() { return pwm.underflowsPerSecond(); } - // provides the effectivly measured output frames per second - uint32_t framesPerSecond() { return pwm.framesPerSecond(); } - - /// Provides access to the driver - PWMDriver *driver() { return &pwm; } - /// You can assign your own custom buffer impelementation: must be allocated - /// on the heap and will be cleaned up by this class - void setBuffer(BaseBuffer *buffer) { pwm.setBuffer(buffer); } - - protected: - PWMConfig audio_config; - PWMDriver pwm; // platform specific pwm -}; - - -} // namespace audio_tools - -#endif diff --git a/src/AudioTools/CoreAudio/AudioPWM/PWMAudioAVR.h b/src/AudioTools/CoreAudio/AudioPWM/PWMAudioAVR.h deleted file mode 100644 index aac358c1e9..0000000000 --- a/src/AudioTools/CoreAudio/AudioPWM/PWMAudioAVR.h +++ /dev/null @@ -1,146 +0,0 @@ - -#pragma once -#include "AudioToolsConfig.h" -#if defined(USE_PWM) && defined(__AVR__) -#include "AudioTools/CoreAudio/AudioPWM/PWMAudioBase.h" -#include "AudioTools/CoreAudio/AudioTimer/AudioTimerAVR.h" - -namespace audio_tools { - -class PWMDriverAVR; -using PWMDriver = PWMDriverAVR; -static PWMDriverAVR *accessAudioPWM = nullptr; - -/** - * @brief Experimental: Audio output to PWM pins for the AVR. The AVR supports - * only up to 2 channels. - * @ingroup platform - * @author Phil Schatzmann - * @copyright GPLv3 - */ - -class PWMDriverAVR : public DriverPWMBase { - friend void defaultPWMAudioOutputCallback(); - - public: - PWMDriverAVR() { - accessAudioPWM = this; - } - - virtual int maxChannels() { return 2; }; - - // Ends the output - virtual void end() { - TRACED(); - noInterrupts(); - // stop timer callback - TCCR1B = 0; - // stop pwm timers - TCCR2A = 0; - interrupts(); // enable all interrupts - - is_timer_started = false; - deleteBuffer(); - } - - void setupTimer() { - TRACED(); - // CPU Frequency 16 MHz - // prescaler 1, 256 or 1024 => no prescaling - uint32_t steps = - F_CPU / 8 / audio_config.sample_rate; // e.g. (16000000/8/44100=>45) - if (steps > 65535) { - LOGE("requested sample rate not supported: %d - we use %d", - audio_config.sample_rate, F_CPU / 65536); - steps = 65535; - } else { - LOGD("compare match register set to %d", steps); - } - - // setup timer intterupt - noInterrupts(); - TCCR1B = 0; - // compare match register - OCR1A = steps; - TCCR1B |= (1 << WGM12); // CTC mode - // TCCR1B |= (1 << CS10); // prescaler 1 - TCCR1B |= (1 << CS11); // prescaler 8 - TIMSK1 |= (1 << OCIE1A); // enable timer compare interrupt - interrupts(); // enable all interrupts - } - - /// Setup LED PWM - void setupPWM() { - TRACED(); - audio_config.pwm_frequency = 62500; - - if (audio_config.channels > 2) { - LOGW("Max 2 channels supported - you requested %d", - audio_config.channels); - audio_config.channels = 2; - } - - for (int j = 0; j < audio_config.channels; j++) { - LOGD("Processing channel %d", j); - setupPin(pins[j]); - } - } - - void startTimer() {} - - // Timer 0 is used by Arduino! - // Timer 1 is used to drive output in sample_rate - // => only Timer2 is available for PWM - void setupPin(int pin) { - switch (pin) { - case 3: - case 11: - // switch PWM frequency to 62500.00 Hz - TCCR2B = TCCR2B & B11111000 | B00000001; - LOGI("PWM Frequency changed for D3 and D11"); - break; - - default: - LOGE("PWM Unsupported pin: %d", pin); - break; - } - pinMode(pin, OUTPUT); - } - - virtual void pwmWrite(int channel, int value) { - analogWrite(pins[channel], value); - } - - void logConfig() { - audio_config.logConfig(); - LOGI("pwm freq: %f khz", 62.5); - if (audio_config.channels == 1) { - LOGI("output pin: %d", pins[0]); - } else { - LOGI("output pins: %d / %d", pins[0], pins[1]); - } - } - - protected: - int pins[2] = {3, 11}; - - virtual int maxOutputValue() { return 255; } -}; - -/// separate method that can be defined as friend so that we can access -/// protected information -void defaultPWMAudioOutputCallback() { - if (accessAudioPWM != nullptr && accessAudioPWM->is_timer_started) { - accessAudioPWM->playNextFrame(); - } -} - -/// timer callback: write the next frame to the pins -ISR(TIMER1_COMPA_vect) { - defaultPWMAudioOutputCallback(); - TimerAlarmRepeatingDriverAVR::tickerCallback(); -} - -} // namespace audio_tools - -#endif diff --git a/src/AudioTools/CoreAudio/AudioPWM/PWMAudioBase.h b/src/AudioTools/CoreAudio/AudioPWM/PWMAudioBase.h deleted file mode 100644 index 3d4eef8605..0000000000 --- a/src/AudioTools/CoreAudio/AudioPWM/PWMAudioBase.h +++ /dev/null @@ -1,359 +0,0 @@ -#pragma once - -#include "AudioToolsConfig.h" -#ifdef USE_PWM - -#include "AudioTools/CoreAudio/AudioBasic/Collections.h" -#include "AudioTools.h" -#include "AudioTools/CoreAudio/AudioLogger.h" -#include "AudioTools/CoreAudio/AudioOutput.h" -#include "AudioTools/CoreAudio/AudioTypes.h" - -#define READ_ERROR_MSG "Could not read full data" - -namespace audio_tools { - -// forward declarations -// Callback for user -typedef bool (*PWMCallbackType)(uint8_t channels, int16_t *data); -// Callback used by system -static void defaultPWMAudioOutputCallback(); -// Driver classes -class PWMDriverESP32; -class PWMDriverRP2040; -class PWMDriverMBED; -class PWMDriverSTM32; - -/** - * @brief Configuration data for PWM audio output - * @author Phil Schatzmann - * @copyright GPLv3 - */ -struct PWMConfig : public AudioInfo { - PWMConfig() { - // default basic information - sample_rate = 8000u; // sample rate in Hz - channels = 1; - bits_per_sample = 16; - } - - /// size of an inidividual buffer - uint16_t buffer_size = PWM_BUFFER_SIZE; - /// number of buffers - uint8_t buffers = PWM_BUFFER_COUNT; - - /// additinal info which might not be used by all processors - uint32_t pwm_frequency = 0; // audable range is from 20 to - /// Only used by ESP32: must be between 8 and 11 -> drives pwm frequency // 20,000Hz (not used by ESP32) - uint8_t resolution = 8; - /// Timer used: Only used by ESP32 must be between 0 and 3 - uint8_t timer_id = 0; - - /// max sample sample rate that still produces good audio - uint32_t max_sample_rate = PWM_MAX_SAMPLE_RATE; - -#ifndef __AVR__ - /// GPIO of starting pin - uint16_t start_pin = PIN_PWM_START; - - /// support assignament of int array - template - void setPins(T (&a)[N]) { - pins_data.clear(); - pins_data.resize(N); - for (int i = 0; i < N; ++i) { - pins_data[i] = a[i]; // reset all elements - } - } - - /// Defines the pins and the corresponding number of channels (=number of - /// pins) - void setPins(Pins &pins) { - pins_data.clear(); - for (int i = 0; i < pins.size(); i++) { - pins_data.push_back(pins[i]); - } - } - - /// Determines the pins (for all channels) - Pins &pins() { - if (pins_data.size() == 0) { - pins_data.resize(channels); - for (int j = 0; j < channels; j++) { - pins_data[j] = start_pin + j; - } - } - return pins_data; - } - -#endif - - void logConfig() { - LOGI("sample_rate: %d", (int) sample_rate); - LOGI("channels: %d", channels); - LOGI("bits_per_sample: %u", bits_per_sample); - LOGI("buffer_size: %u", buffer_size); - LOGI("buffer_count: %u", buffers); - LOGI("pwm_frequency: %u", (unsigned)pwm_frequency); - LOGI("resolution: %d", resolution); - // LOGI("timer_id: %d", timer_id); - } - - protected: - Pins pins_data; -}; - -/** - * @brief Base Class for all PWM drivers - * - */ -class DriverPWMBase { - public: - DriverPWMBase() = default; - virtual ~DriverPWMBase() { end(); } - - PWMConfig &audioInfo() { return audio_config; } - - virtual PWMConfig defaultConfig() { - PWMConfig cfg; - return cfg; - } - - // restart with prior definitions - bool begin(PWMConfig cfg) { - TRACEI(); - - decimation_factor = 0; - audio_config = cfg; - decimate.setChannels(cfg.channels); - decimate.setBits(cfg.bits_per_sample); - decimate.setFactor(decimation()); - frame_size = audio_config.channels * (audio_config.bits_per_sample / 8); - if (audio_config.channels > maxChannels()) { - LOGE("Only max %d channels are supported!", maxChannels()); - return false; - } - - if (buffer == nullptr) { - LOGI("->Allocating new buffer %d * %d bytes", audio_config.buffers, - audio_config.buffer_size); - // buffer = new NBuffer(audio_config.buffer_size, - // audio_config.buffers); - buffer = new RingBuffer(audio_config.buffer_size * - audio_config.buffers); - } - - // initialize if necessary - if (!isTimerStarted() || !cfg.equals(actual_info)) { - audio_config.logConfig(); - actual_info = cfg; - setupPWM(); - setupTimer(); - } - - // reset class variables - underflow_count = 0; - underflow_per_second = 0; - frame_count = 0; - frames_per_second = 0; - - LOGI("->Buffer available: %d", buffer->available()); - LOGI("->Buffer available for write: %d", buffer->availableForWrite()); - LOGI("->is_timer_started: %s ", isTimerStarted() ? "true" : "false"); - return true; - } - - virtual int availableForWrite() { - // return is_blocking_write - // ? audio_config.buffer_size - // : buffer->availableForWrite() / frame_size * frame_size; - // we must not write anything bigger then the buffer size - return buffer->size() / frame_size * frame_size; - } - - // blocking write for an array: we expect a singed value and convert it into a - // unsigned - virtual size_t write(const uint8_t *data, size_t len) { - size_t size = len; - - // only allow full frame - size = (size / frame_size) * frame_size; - LOGD("adjusted size: %d", (int)size); - - if (isDecimateActive()) { - size = decimate.convert((uint8_t *)data, size); - LOGD("decimated size: %d", (int)size); - } - - if (is_blocking_write && buffer->availableForWrite() < size) { - LOGD("Waiting for buffer to be available"); - while (buffer->availableForWrite() < size) delay(5); - } else { - size = min((size_t)availableForWrite(), size); - } - - size_t result = buffer->writeArray(data, size); - if (result != size) { - LOGW("Could not write all data: %u -> %d", (unsigned)size, result); - } - // activate the timer now - if not already done - if (!is_timer_started) startTimer(); - - // adjust the result by the descimation - if (isDecimateActive()) { - result = result * decimation(); - } - //LOGD("write %u -> %u", len, result); - return result; - } - - // When the timer does not have enough data we increase the underflow_count; - uint32_t underflowsPerSecond() { return underflow_per_second; } - // provides the effectivly measured output frames per second - uint32_t framesPerSecond() { return frames_per_second; } - - inline void updateStatistics() { - frame_count++; - if (millis() >= time_1_sec) { - time_1_sec = millis() + 1000; - frames_per_second = frame_count; - underflow_per_second = underflow_count; - underflow_count = 0; - frame_count = 0; - } - } - - bool isTimerStarted() { return is_timer_started; } - - virtual void setupPWM() = 0; - virtual void setupTimer() = 0; - virtual void startTimer() = 0; - virtual int maxChannels() = 0; - virtual int maxOutputValue() = 0; - virtual void end() {}; - - virtual void pwmWrite(int channel, int value) = 0; - - /// You can assign your own custom buffer impelementation: must be allocated - /// on the heap and will be cleaned up by this class - void setBuffer(BaseBuffer *buffer) { this->buffer = buffer; } - - /// Provides the effective sample rate - virtual int effectiveOutputSampleRate() { - return audio_config.sample_rate / decimation(); - } - - protected: - PWMConfig audio_config; - AudioInfo actual_info; - BaseBuffer *buffer = nullptr; - uint32_t underflow_count = 0; - uint32_t underflow_per_second = 0; - uint32_t frame_count = 0; - uint32_t frames_per_second = 0; - uint32_t frame_size = 0; - uint32_t time_1_sec; - bool is_timer_started = false; - bool is_blocking_write = true; - Decimate decimate; - int decimation_factor = 0; - - void deleteBuffer() { - // delete buffer if necessary - if (buffer != nullptr) { - delete buffer; - buffer = nullptr; - } - } - - /// writes the next frame to the output pins - void playNextFrame() { - if (isTimerStarted() && buffer != nullptr) { - // TRACED(); - int required = (audio_config.bits_per_sample / 8) * audio_config.channels; - if (buffer->available() >= required) { - for (int j = 0; j < audio_config.channels; j++) { - int value = nextValue(); - pwmWrite(j, value); - } - } else { - underflow_count++; - } - updateStatistics(); - } - } - - /// determines the next scaled value - virtual int nextValue() { - int result = 0; - switch (audio_config.bits_per_sample) { - case 8: { - int8_t value; - if (buffer->readArray((uint8_t *)&value, 1) != 1) { - LOGE(READ_ERROR_MSG); - } - result = map(value, -NumberConverter::maxValue(8), - NumberConverter::maxValue(8), 0, maxOutputValue()); - break; - } - case 16: { - int16_t value; - if (buffer->readArray((uint8_t *)&value, 2) != 2) { - LOGE(READ_ERROR_MSG); - } - result = map(value, -NumberConverter::maxValue(16), - NumberConverter::maxValue(16), 0, maxOutputValue()); - break; - } - case 24: { - int24_t value; - if (buffer->readArray((uint8_t *)&value, 3) != 3) { - LOGE(READ_ERROR_MSG); - } - result = map((int32_t)value, -NumberConverter::maxValue(24), - NumberConverter::maxValue(24), 0, maxOutputValue()); - break; - } - case 32: { - int32_t value; - if (buffer->readArray((uint8_t *)&value, 4) != 4) { - LOGE(READ_ERROR_MSG); - } - result = map(value, -NumberConverter::maxValue(32), - NumberConverter::maxValue(32), 0, maxOutputValue()); - break; - } - } - return result; - } - - /// Provides the max working sample rate - virtual int maxSampleRate() { return audio_config.max_sample_rate; } - - /// The requested sampling rate is too hight: we only process half of the - /// samples so we can half the sampling rate - virtual bool isDecimateActive() { - return audio_config.sample_rate >= audio_config.max_sample_rate; - } - - /// Decimation factor to reduce the sample rate - virtual int decimation() { - if (decimation_factor == 0){ - for (int j = 1; j < 20; j++){ - if (audio_config.sample_rate / j <= audio_config.max_sample_rate){ - decimation_factor = j; - LOGI("Decimation factor: %d" ,j); - return j; - } - } - decimation_factor = 1; - LOGI("Decimation factor: %d", (int)decimation_factor); - } - - return decimation_factor; - } -}; - -} // namespace audio_tool - -#endif diff --git a/src/AudioTools/CoreAudio/AudioPWM/PWMAudioESP32.h b/src/AudioTools/CoreAudio/AudioPWM/PWMAudioESP32.h deleted file mode 100644 index 79020d39cb..0000000000 --- a/src/AudioTools/CoreAudio/AudioPWM/PWMAudioESP32.h +++ /dev/null @@ -1,182 +0,0 @@ - -#pragma once -#ifdef ESP32 -#include "AudioTools/CoreAudio/AudioPWM/PWMAudioBase.h" - -namespace audio_tools { - -// forward declaration -class PWMDriverESP32; -/** - * @typedef DriverPWMBase - * @brief Please use DriverPWMBase! - */ -using PWMDriver = PWMDriverESP32; - -/** - * @brief Information for a PIN - * @author Phil Schatzmann - * @copyright GPLv3 - */ -struct PinInfoESP32 { - int pwm_channel; - int gpio; -}; - -typedef PinInfoESP32 PinInfo; - -/** - * @brief Audio output to PWM pins for the ESP32. The ESP32 supports up to 16 - * channels. - * @ingroup platform - * @author Phil Schatzmann - * @copyright GPLv3 - */ - -class PWMDriverESP32 : public DriverPWMBase { - public: - // friend void pwm_callback(void*ptr); - - PWMDriverESP32() { TRACED(); } - - // Ends the output - virtual void end() { - TRACED(); - timer.end(); - is_timer_started = false; - for (int j = 0; j < audio_config.channels; j++) { -#if ESP_IDF_VERSION > ESP_IDF_VERSION_VAL(5, 0, 0) - ledcDetach(pins[j].gpio); -#else - ledcDetachPin(pins[j].gpio); -#endif - } - deleteBuffer(); - } - - /// when we get the first write -> we activate the timer to start with the - /// output of data - virtual void startTimer() { - if (!timer) { - TRACEI(); - timer.begin(pwm_callback, effectiveOutputSampleRate(), HZ); - actual_timer_frequency = effectiveOutputSampleRate(); - is_timer_started = true; - } - } - - /// Setup LED PWM - virtual void setupPWM() { - // frequency is driven by selected resolution - if (audio_config.pwm_frequency == 0){ - audio_config.pwm_frequency = frequency(audio_config.resolution) * 1000; - } - - pins.resize(audio_config.channels); - for (int j = 0; j < audio_config.channels; j++) { - pins[j].gpio = audio_config.pins()[j]; -#if ESP_IDF_VERSION > ESP_IDF_VERSION_VAL(5, 0, 0) - if (!ledcAttach(pins[j].gpio, audio_config.pwm_frequency, - audio_config.resolution)) { - LOGE("ledcAttach: %d", pins[j].gpio); - } -#else - int pwmChannel = j; - pins[j].pwm_channel = pwmChannel; - ledcSetup(pins[j].pwm_channel, audio_config.pwm_frequency, - audio_config.resolution); - ledcAttachPin(pins[j].gpio, pins[j].pwm_channel); -#endif - LOGI("setupPWM: pin=%d, channel=%d, frequency=%u, resolution=%d", - pins[j].gpio, pins[j].pwm_channel, (unsigned)audio_config.pwm_frequency, - audio_config.resolution); - } - logPins(); - } - - void logPins() { - for (int j = 0; j < pins.size(); j++) { - LOGI("pin%d: %d", j, pins[j].gpio); - } - } - - /// Setup ESP32 timer with callback - virtual void setupTimer() { - timer.setCallbackParameter(this); - timer.setIsSave(false); - - if (actual_timer_frequency != effectiveOutputSampleRate()){ - timer.end(); - startTimer(); - } - } - - /// write a pwm value to the indicated channel. The max value depends on the - /// resolution - virtual void pwmWrite(int channel, int value) { -#if ESP_IDF_VERSION > ESP_IDF_VERSION_VAL(5, 0, 0) - ledcWrite(pins[channel].gpio, value); -#else - ledcWrite(pins[channel].pwm_channel, value); -#endif - } - - protected: - Vector pins; - TimerAlarmRepeating timer; - uint32_t actual_timer_frequency = 0; - - /// provides the max value for the indicated resulution - int maxUnsignedValue(int resolution) { return pow(2, resolution); } - - virtual int maxChannels() { return 16; }; - - /// provides the max value for the configured resulution - virtual int maxOutputValue() { - return maxUnsignedValue(audio_config.resolution); - } - - /// determiens the PWM frequency based on the requested resolution - float frequency(int resolution) { -// On ESP32S2 and S3, the frequncy seems off by a factor of 2 -#if defined(ESP32S2) || defined(ESP32S3) - switch (resolution) { - case 7: - return 312.5; - case 8: - return 156.25; - case 9: - return 78.125; - case 10: - return 39.0625; - case 11: - return 19.53125; - } - return 312.5; -#else - switch (resolution) { - case 8: - return 312.5; - case 9: - return 156.25; - case 10: - return 78.125; - case 11: - return 39.0625; - } - return 312.5; -#endif - } - - /// timer callback: write the next frame to the pins - static void pwm_callback(void *ptr) { - PWMDriverESP32 *accessAudioPWM = (PWMDriverESP32 *)ptr; - if (accessAudioPWM != nullptr) { - accessAudioPWM->playNextFrame(); - } - } -}; - -} // namespace audio_tools - -#endif diff --git a/src/AudioTools/CoreAudio/AudioPWM/PWMAudioMBED.h b/src/AudioTools/CoreAudio/AudioPWM/PWMAudioMBED.h deleted file mode 100644 index 3d3b88fa61..0000000000 --- a/src/AudioTools/CoreAudio/AudioPWM/PWMAudioMBED.h +++ /dev/null @@ -1,114 +0,0 @@ - -#pragma once -#if defined(ARDUINO_ARCH_MBED) -#include "AudioTools/CoreAudio/AudioPWM/PWMAudioBase.h" -#include "AudioTools/CoreAudio/AudioTimer/AudioTimer.h" -#include "mbed.h" - -namespace audio_tools { - -// forward declaration -class PWMDriverMBED; -/** - * @typedef DriverPWMBase - * @brief Please use DriverPWMBase! - */ -using PWMDriver = PWMDriverMBED; - -/** - * @brief Audio output to PWM pins for MBED based Arduino implementations - * @ingroup platform - * @author Phil Schatzmann - * @copyright GPLv3 - */ - -class PWMDriverMBED : public DriverPWMBase { - public: - PWMDriverMBED() = default; - - // Ends the output - virtual void end() override { - TRACED(); - ticker.end(); // it does not hurt to call this even if it has not been - // started - is_timer_started = false; - - // stop and release pins - for (int j = 0; j < audio_config.channels; j++) { - if (pins[j] != nullptr) { - pins[j]->suspend(); - delete pins[j]; - pins[j] = nullptr; - } - } - pins.clear(); - // pins.shrink_to_fit(); - deleteBuffer(); - } - - protected: - Vector pins; - TimerAlarmRepeating ticker; // calls a callback repeatedly with a timeout - - /// when we get the first write -> we activate the timer to start with the - /// output of data - virtual void startTimer() override { - if (!is_timer_started) { - TRACED(); - long wait_time = 1000000l / audio_config.sample_rate; - ticker.setCallbackParameter(this); - ticker.begin(defaultPWMAudioOutputCallback, wait_time, US); - is_timer_started = true; - } - } - - /// Setup PWM Pins - virtual void setupPWM() { - TRACED(); - if (audio_config.pwm_frequency == 0){ - audio_config.pwm_frequency = PWM_AUDIO_FREQUENCY; - } - - unsigned long period = - 1000000l / audio_config.pwm_frequency; // -> 30.517578125 microseconds - pins.resize(audio_config.channels); - for (int j = 0; j < audio_config.channels; j++) { - LOGD("Processing channel %d", j); - auto gpio = audio_config.pins()[j]; - mbed::PwmOut* pin = new mbed::PwmOut(digitalPinToPinName(gpio)); - LOGI("PWM Pin: %d", gpio); - pin->period_us(period); - pin->write(0.0f); // 0% duty cycle -> - pin->resume(); // in case it was suspended before - pins[j] = pin; - } - } - - /// not used -> see startTimer(); - virtual void setupTimer() {} - - /// Maximum supported channels - virtual int maxChannels() { return 16; }; - - /// provides the max value for the configured resulution - virtual int maxOutputValue() { return 1000; } - - /// write a pwm value to the indicated channel. The max value depends on the - /// resolution - virtual void pwmWrite(int channel, int value) { - float float_value = static_cast(value) / maxOutputValue(); - pins[channel]->write(float_value); // pwm the value is between 0.0 and 1.0 - } - - /// timer callback: write the next frame to the pins - static void defaultPWMAudioOutputCallback(void* obj) { - PWMDriverMBED* accessAudioPWM = (PWMDriverMBED*)obj; - if (accessAudioPWM != nullptr) { - accessAudioPWM->playNextFrame(); - } - } -}; - -} // namespace audio_tools - -#endif diff --git a/src/AudioTools/CoreAudio/AudioPWM/PWMAudioRP2040.h b/src/AudioTools/CoreAudio/AudioPWM/PWMAudioRP2040.h deleted file mode 100644 index 019c02c21e..0000000000 --- a/src/AudioTools/CoreAudio/AudioPWM/PWMAudioRP2040.h +++ /dev/null @@ -1,163 +0,0 @@ - -#pragma once -#if defined(RP2040_HOWER) -#include "AudioTools/CoreAudio/AudioPWM/PWMAudioBase.h" -#include "hardware/adc.h" -#include "hardware/clocks.h" -#include "hardware/gpio.h" -#include "hardware/pwm.h" -#include "hardware/structs/clocks.h" -#include "pico/time.h" - -namespace audio_tools { - -// forwrd declaratioin of callback -class PWMDriverRP2040; -/** - * @typedef DriverPWMBase - * @brief Please use DriverPWMBase! - */ -using PWMDriver = PWMDriverRP2040; - -/** - * @brief Rasperry Pico Channel to pin assignments - * @author Phil Schatzmann - * @copyright GPLv3 - */ -struct PicoChannelOut { - int gpio = -1; - int audioChannel; - uint slice; // pico pwm slice - uint channel; // pico pwm channel -}; - -/** - * @brief Audio output for the Rasperry Pico to PWM pins. - The Raspberry Pi Pico has 8 PWM blocks/slices(1-8) and each PWM block - provides up to two PWM outputs(A-B). - * @ingroup platform - * @author Phil Schatzmann - * @copyright GPLv3 - - */ - -class PWMDriverRP2040 : public DriverPWMBase { - // friend bool defaultPWMAudioOutputCallbackPico(repeating_timer* ptr); - - public: - PWMDriverRP2040() { TRACED(); } - - /// Ends the output -> resets the timer and the pins - void end() override { - TRACED(); - ticker.end(); // it does not hurt to call this even if it has not been - // started - is_timer_started = false; - for (auto pin : pins) { - if (pin.gpio != -1) { - pwm_set_enabled(pin.slice, false); - } - } - deleteBuffer(); - } - - protected: - Vector pins; - TimerAlarmRepeating ticker; - - virtual void startTimer() override { - TRACED(); - ticker.setCallbackParameter(this); - ticker.begin(defaultPWMAudioOutputCallbackPico, audio_config.sample_rate, HZ); - - is_timer_started = true; - } - - // setup pwm config and all pins - void setupPWM() override { - TRACED(); - pwm_config cfg = setupPWMConfig(); - - // initialize empty pins - PicoChannelOut empty; - pins.resize(audio_config.channels, empty); - - // setup pin values - for (int j = 0; j < audio_config.channels; j++) { - int channel = j; - int gpio = audio_config.pins()[j]; - LOGI("PWM pin %d", gpio); - pins[channel].slice = pwm_gpio_to_slice_num(gpio); - pins[channel].channel = pwm_gpio_to_channel(gpio); - pins[channel].audioChannel = j; - pins[channel].gpio = gpio; - - setupPWMPin(cfg, pins[channel]); - } - } - - // defines the pwm_config which will be used to drive the pins - pwm_config setupPWMConfig() { - TRACED(); - if (audio_config.pwm_frequency == 0){ - audio_config.pwm_frequency = PWM_AUDIO_FREQUENCY; - } - // setup pwm frequency - pwm_config pico_pwm_config = pwm_get_default_config(); - int wrap_value = maxOutputValue(); // amplitude of square wave (pwm values - // -amplitude to amplitude) for one byte - float pwmClockDivider = static_cast(clock_get_hz(clk_sys)) / - (audio_config.pwm_frequency * wrap_value); - float clock_speed = static_cast(clock_get_hz(clk_sys)); - LOGI("->wrap_value = %d", wrap_value); - LOGI("->max clock speed = %f", clock_speed); - LOGI("->divider = %f", pwmClockDivider); - LOGI("->clock speed = %f", clock_speed / pwmClockDivider); - pwm_config_set_clkdiv(&pico_pwm_config, pwmClockDivider); - pwm_config_set_clkdiv_mode(&pico_pwm_config, PWM_DIV_FREE_RUNNING); - // pwm_config_set_phase_correct(&pico_pwm_config, false); - pwm_config_set_wrap(&pico_pwm_config, wrap_value); - return pico_pwm_config; - } - - // set up pwm - void setupPWMPin(pwm_config &cfg, PicoChannelOut &pinInfo) { - LOGD("%s for gpio %d", LOG_METHOD, pinInfo.gpio); - // setup pwm pin - int gpio = pinInfo.gpio; - gpio_set_function(gpio, GPIO_FUNC_PWM); - pinInfo.slice = pwm_gpio_to_slice_num(gpio); - pinInfo.channel = pwm_gpio_to_channel(gpio); - pwm_init(pinInfo.slice, &cfg, true); - - // set initial output value - pwm_set_chan_level(pinInfo.slice, pinInfo.channel, 0); - } - - void setupTimer() override {} - - /// The pico supports max 16 pwm pins - virtual int maxChannels() override { return 16; }; - - /// Max pwm output value - virtual int maxOutputValue() override { - return std::pow(audio_config.resolution, 2) - 1; - } - - /// write a pwm value to the indicated channel. The values are between 0 and - /// 255 - void pwmWrite(int audioChannel, int value) override { - pwm_set_chan_level(pins[audioChannel].slice, pins[audioChannel].channel, - value); - } - - // timed output executed at the sampleRate - static void defaultPWMAudioOutputCallbackPico(void *ptr) { - PWMDriverRP2040 *self = (PWMDriverRP2040 *)ptr; - self->playNextFrame(); - } -}; - -} // namespace audio_tools - -#endif diff --git a/src/AudioTools/CoreAudio/AudioPWM/PWMAudioRenesas.h b/src/AudioTools/CoreAudio/AudioPWM/PWMAudioRenesas.h deleted file mode 100644 index ca45735e71..0000000000 --- a/src/AudioTools/CoreAudio/AudioPWM/PWMAudioRenesas.h +++ /dev/null @@ -1,134 +0,0 @@ -#pragma once -#if defined(ARDUINO_ARCH_RENESAS) -#include "AudioTools/CoreAudio/AudioPWM/PWMAudioBase.h" -#include "AudioTools/CoreAudio/AudioTimer/AudioTimer.h" -#include "pwm.h" - -namespace audio_tools { - -// forward declaration -class PWMDriverRenesas; -/** - * @typedef DriverPWMBase - * @brief Please use DriverPWMBase! - */ -using PWMDriver = PWMDriverRenesas; - -/** - * @brief Audio output to PWM pins for Renesas based Arduino implementations - * @ingroup platform - * @author Phil Schatzmann - * @copyright GPLv3 - */ - -class PWMDriverRenesas : public DriverPWMBase { - public: - PWMDriverRenesas() { LOGD("PWMDriverRenesas"); } - - virtual PWMConfig defaultConfig() { - PWMConfig cfg; - Pins pwm_pins; - // add default pins - for (int j = 2; j < 13; j += 2) pwm_pins.push_back(j); - cfg.setPins(pwm_pins); - return cfg; - } - - // Ends the output - virtual void end() override { - TRACED(); - ticker.end(); // it does not hurt to call this even if it has not been - // started - is_timer_started = false; - - // stop and release pins - for (int j = 0; j < audio_config.channels; j++) { - if (pins[j] != nullptr) { - pins[j]->suspend(); - pins[j]->end(); - delete pins[j]; - pins[j] = nullptr; - } - } - pins.clear(); - // pins.shrink_to_fit(); - deleteBuffer(); - } - - protected: - Vector pins; - TimerAlarmRepeating ticker; // calls a callback repeatedly with a timeout - - /// when we get the first write -> we activate the timer to start with the - /// output of data - virtual void startTimer() override { - TRACED(); - if (!is_timer_started) { - TRACED(); - ticker.setCallbackParameter(this); - int sample_rate = effectiveOutputSampleRate(); - if (isDecimateActive()) { - LOGI("Using reduced sample rate: %d", effectiveOutputSampleRate()); - } - ticker.begin(defaultPWMAudioOutputCallback, sample_rate, HZ); - is_timer_started = true; - } - } - - /// Setup PWM Pins - virtual void setupPWM() { - TRACED(); - if (audio_config.pwm_frequency == 0){ - audio_config.pwm_frequency = PWM_AUDIO_FREQUENCY; - } - - pins.resize(audio_config.channels); - for (int j = 0; j < audio_config.channels; j++) { - LOGD("Processing channel %d", j); - auto gpio = audio_config.pins()[j]; - PwmOut* pin = new PwmOut(gpio); - LOGI("PWM Pin: %d", gpio); - pin->begin(20000.0f, 0.0f); // 50: 20000hz at 0% - pins[j] = pin; - } - } - - /// not used -> see startTimer(); - virtual void setupTimer() {} - - virtual int maxChannels() { return PIN_PWM_COUNT; }; - - /// provides the max value for the configured resulution - virtual int maxOutputValue() { - return 100; // percent - } - - /// write a pwm value to the indicated channel. The max value depends on the - /// resolution - virtual void pwmWrite(int channel, int value) { - pins[channel]->pulse_perc(value); - } - - /// timer callback: write the next frame to the pins - static void defaultPWMAudioOutputCallback(void* obj) { - PWMDriverRenesas* accessAudioPWM = (PWMDriverRenesas*)obj; - if (accessAudioPWM != nullptr) { - accessAudioPWM->playNextFrame(); - } - } - - int maxSampleRate() override { return audio_config.max_sample_rate; } - - int decimation() override { - for (int j = 1; j < 5; j++) { - if (j % 2 == 0 && audio_config.sample_rate / j <= maxSampleRate()) { - return j; - } - } - return 5; - } -}; - -} // namespace audio_tools - -#endif diff --git a/src/AudioTools/CoreAudio/AudioPWM/PWMAudioSTM32.h b/src/AudioTools/CoreAudio/AudioPWM/PWMAudioSTM32.h deleted file mode 100644 index f8fec6bb3c..0000000000 --- a/src/AudioTools/CoreAudio/AudioPWM/PWMAudioSTM32.h +++ /dev/null @@ -1,214 +0,0 @@ - -#pragma once -#if defined(STM32) -#include "AudioTools/CoreAudio/AudioPWM/PWMAudioBase.h" -#include "AudioTools/CoreAudio/AudioTimer/AudioTimer.h" - -namespace audio_tools { - -// forward declaration -class PWMDriverSTM32; -/** - * @typedef DriverPWMBase - * @brief Please use DriverPWMBase! - */ -using PWMDriver = PWMDriverSTM32; - -/** - * @brief Audio output to PWM pins for STM32. We use one timer to generate the - * sample rate and one timer for the PWM signal. - * @ingroup platform - * @author Phil Schatzmann - * @copyright GPLv3 - */ - -class PWMDriverSTM32 : public DriverPWMBase { - /// @brief PWM information for a single pin - struct PWMPin { - HardwareTimer *p_timer; - int channel; - int max_value; - bool active = false; - int pin; - int pwm_frequency; - - PWMPin() = default; - - PWMPin(HardwareTimer *p_timer, int channel, int pin, int maxValue, - int pwmFrequency = 30000) { - this->p_timer = p_timer; - this->channel = channel; - this->pin = pin; - this->max_value = maxValue; - this->pwm_frequency = pwmFrequency; - } - - void begin() { - TRACEI(); - p_timer->setPWM(channel, pin, pwm_frequency, - 50); // 30k Hertz, 50% dutycycle - active = true; - } - - void setRate(int rate) { - if (active) { - uint16_t sample = 100.0 * rate / max_value; - p_timer->setCaptureCompare(channel, sample, - PERCENT_COMPARE_FORMAT); // 50% - } - } - }; - - class PWM { - public: - PWM() = default; - - void begin(HardwareTimer *pwm_timer, int pwm_frequency, int maxValue) { - this->p_timer = pwm_timer; - this->pwm_frequency = pwm_frequency; - this->max_value = maxValue; - } - - void end() { - p_timer->pause(); - } - - bool addPin(int pin) { - LOGI("addPin: %d", pin); - TIM_TypeDef *p_instance = (TIM_TypeDef *)pinmap_peripheral( - digitalPinToPinName(pin), PinMap_PWM); - channel = STM_PIN_CHANNEL( - pinmap_function(digitalPinToPinName(pin), PinMap_PWM)); - PWMPin pwm_pin{p_timer, channel, pin, max_value, pwm_frequency}; - pins.push_back(pwm_pin); - // make sure that all pins use the same timer ! - if (p_timer->getHandle()->Instance != p_instance) { - LOGE("Invalid pin %d with timer %s for timer %s", pin, - getTimerStr(p_instance), - getTimerStr(p_timer->getHandle()->Instance)); - return false; - } - LOGI("Using Timer %s for PWM", getTimerStr(p_instance)); - pins[pins.size() - 1].begin(); - return true; - } - - void setRate(int idx, int rate) { - if (idx < pins.size()) { - pins[idx].setRate(rate); - } else { - LOGE("Invalid index: %d", idx); - } - } - - protected: - HardwareTimer *p_timer; - audio_tools::Vector pins; - int channel; - int max_value; - int pwm_frequency; - - const char *getTimerStr(TIM_TypeDef *inst) { - if (inst == TIM1) - return "TIM1"; - else if (inst == TIM2) - return "TIM2"; - else if (inst == TIM3) - return "TIM3"; - else if (inst == TIM4) - return "TIM4"; - else if (inst == TIM5) - return "TIM5"; - return "N/A"; - } - }; - - public: - PWMDriverSTM32() { - TRACED(); - ticker.setTimer(PWM_FREQ_TIMER_NO); - } - - // Ends the output - virtual void end() override { - TRACED(); - ticker.end(); // it does not hurt to call this even if it has not been - // started - pwm.end(); // stop pwm timer - deleteBuffer(); - is_timer_started = false; - if (buffer != nullptr) { - delete buffer; - buffer = nullptr; - } - } - - /// Defines the timer which is used to generate the PWM signal - void setPWMTimer(HardwareTimer &t) { p_pwm_timer = &t; } - - protected: - TimerAlarmRepeating ticker; // calls a callback repeatedly with a timeout - HardwareTimer *p_pwm_timer = nullptr; - PWM pwm; - int64_t max_value; - - /// when we get the first write -> we activate the timer to start with the - /// output of data - virtual void startTimer() override { - if (!is_timer_started) { - TRACED(); - uint32_t time = AudioTime::toTimeUs(audio_config.sample_rate); - ticker.setCallbackParameter(this); - ticker.begin(defaultPWMAudioOutputCallback, time, US); - is_timer_started = true; - } - } - - /// Setup PWM Pins - virtual void setupPWM() { - TRACED(); - if (audio_config.pwm_frequency == 0){ - audio_config.pwm_frequency = PWM_AUDIO_FREQUENCY; - } - - // setup pwm timer - if (p_pwm_timer == nullptr) { - p_pwm_timer = new HardwareTimer(PWM_DEFAULT_TIMER); - } - - // setup pins for output - int ch = 0; - pwm.begin(p_pwm_timer, audio_config.pwm_frequency, maxOutputValue()); - for (auto gpio : audio_config.pins()) { - LOGD("Processing channel %d -> pin: %d", ch++, gpio); - pwm.addPin(gpio); - } - } - - virtual void setupTimer() {} - - /// One timer supports max 4 output pins - virtual int maxChannels() { return 4; }; - - /// provides the max value for the configured resulution - virtual int maxOutputValue() { return 10000; } - - /// write a pwm value to the indicated channel. The max value depends on the - /// resolution - virtual void pwmWrite(int channel, int value) { - // analogWrite(pins[channel], value); - pwm.setRate(channel, value); - } - - /// timer callback: write the next frame to the pins - static void defaultPWMAudioOutputCallback(void *obj) { - PWMDriverSTM32 *accessAudioPWM = (PWMDriverSTM32 *)obj; - if (accessAudioPWM != nullptr) { - accessAudioPWM->playNextFrame(); - } - } -}; - -} // namespace audio_tools - -#endif diff --git a/src/AudioTools/CoreAudio/AudioPlayer.h b/src/AudioTools/CoreAudio/AudioPlayer.h deleted file mode 100644 index f7a29ae03f..0000000000 --- a/src/AudioTools/CoreAudio/AudioPlayer.h +++ /dev/null @@ -1,592 +0,0 @@ -#pragma once - -#include "AudioToolsConfig.h" -#include "AudioTools/CoreAudio/AudioBasic/Debouncer.h" -#include "AudioTools/CoreAudio/AudioHttp/AudioHttp.h" -#include "AudioTools/CoreAudio/AudioLogger.h" -#include "AudioTools/CoreAudio/AudioStreams.h" -#include "AudioTools/CoreAudio/AudioTypes.h" -#include "AudioTools/CoreAudio/BaseConverter.h" -#include "AudioTools/CoreAudio/Buffers.h" -#include "AudioTools/CoreAudio/Fade.h" -#include "AudioTools/CoreAudio/StreamCopy.h" -#include "AudioTools/CoreAudio/AudioMetaData/MetaData.h" -#include "AudioTools/CoreAudio/VolumeStream.h" -#include "AudioTools/Disk/AudioSource.h" -#include "AudioTools/AudioCodecs/AudioCodecs.h" - -/** - * @defgroup player Player - * @ingroup main - * @brief Audio Player - */ - -namespace audio_tools { - -/** - * @brief Implements a simple audio player which supports the following - * commands: - * - begin - * - play - * - stop - * - next - * - set Volume - * @ingroup player - * @author Phil Schatzmann - * @copyright GPLv3 - */ -class AudioPlayer : public AudioInfoSupport, public VolumeSupport { - -public: - /// Default constructor - AudioPlayer() { TRACED(); } - - /** - * @brief Construct a new Audio Player object. The processing chain is - * AudioSource -> Stream-copy -> EncodedAudioStream -> VolumeStream -> - * FadeStream -> Print - * - * @param source - * @param output - * @param decoder - */ - AudioPlayer(AudioSource &source, AudioOutput &output, AudioDecoder &decoder) { - TRACED(); - this->p_source = &source; - this->p_decoder = &decoder; - setOutput(output); - // notification for audio configuration - decoder.addNotifyAudioChange(*this); - } - - /** - * @brief Construct a new Audio Player object. The processing chain is - * AudioSource -> Stream-copy -> EncodedAudioStream -> VolumeStream -> - * FadeStream -> Print - * - * @param source - * @param output - * @param decoder - * @param notify - */ - AudioPlayer(AudioSource &source, Print &output, AudioDecoder &decoder, - AudioInfoSupport *notify = nullptr) { - TRACED(); - this->p_source = &source; - this->p_decoder = &decoder; - setOutput(output); - addNotifyAudioChange(notify); - } - - /** - * @brief Construct a new Audio Player object. The processing chain is - * AudioSource -> Stream-copy -> EncodedAudioStream -> VolumeStream -> - * FadeStream -> Print - * - * @param source - * @param output - * @param decoder - */ - AudioPlayer(AudioSource &source, AudioStream &output, AudioDecoder &decoder) { - TRACED(); - this->p_source = &source; - this->p_decoder = &decoder; - setOutput(output); - // notification for audio configuration - decoder.addNotifyAudioChange(*this); - } - - AudioPlayer(AudioPlayer const &) = delete; - - AudioPlayer &operator=(AudioPlayer const &) = delete; - - void setOutput(AudioOutput &output) { - if (p_decoder->isResultPCM()) { - this->fade.setOutput(output); - this->volume_out.setOutput(fade); - out_decoding.setOutput(&volume_out); - out_decoding.setDecoder(p_decoder); - } else { - out_decoding.setOutput(&output); - out_decoding.setDecoder(p_decoder); - } - this->p_final_print = &output; - this->p_final_stream = nullptr; - } - - void setOutput(Print &output) { - if (p_decoder->isResultPCM()) { - this->fade.setOutput(output); - this->volume_out.setOutput(fade); - out_decoding.setOutput(&volume_out); - out_decoding.setDecoder(p_decoder); - } else { - out_decoding.setOutput(&output); - out_decoding.setDecoder(p_decoder); - } - this->p_final_print = nullptr; - this->p_final_stream = nullptr; - } - - void setOutput(AudioStream &output) { - if (p_decoder->isResultPCM()) { - this->fade.setOutput(output); - this->volume_out.setOutput(fade); - out_decoding.setOutput(&volume_out); - out_decoding.setDecoder(p_decoder); - } else { - out_decoding.setOutput(&output); - out_decoding.setDecoder(p_decoder); - } - this->p_final_print = nullptr; - this->p_final_stream = &output; - } - - /// Defines the number of bytes used by the copier - void setBufferSize(int size) { copier.resize(size); } - - /// (Re)Starts the playing of the music (from the beginning or the indicated index) - bool begin(int index = 0, bool isActive = true) { - TRACED(); - bool result = false; - // initilaize volume - if (current_volume == -1.0f) { - setVolume(1.0f); - } else { - setVolume(current_volume); - } - - // take definition from source - autonext = p_source->isAutoNext(); - - // initial audio info for fade from output when not defined yet - setupFade(); - - // start dependent objects - out_decoding.begin(); - p_source->begin(); - meta_out.begin(); - volume_out.begin(); - - if (index >= 0) { - p_input_stream = p_source->selectStream(index); - if (p_input_stream != nullptr) { - if (meta_active) { - copier.setCallbackOnWrite(decodeMetaData, this); - } - copier.begin(out_decoding, *p_input_stream); - timeout = millis() + p_source->timeoutAutoNext(); - active = isActive; - result = true; - } else { - LOGW("-> begin: no data found"); - active = false; - result = false; - } - } else { - LOGW("-> begin: no stream selected"); - active = isActive; - result = false; - } - return result; - } - - void end() { - TRACED(); - active = false; - out_decoding.end(); - meta_out.end(); - // remove any data in the decoder - if (p_decoder != nullptr) { - LOGI("reset codec"); - p_decoder->end(); - p_decoder->begin(); - } - } - - /// Provides the actual audio source - AudioSource &audioSource() { return *p_source; } - - /// (Re)defines the audio source - void setAudioSource(AudioSource &source) { this->p_source = &source; } - - /// (Re)defines the decoder - void setDecoder(AudioDecoder &decoder) { - this->p_decoder = &decoder; - out_decoding.setDecoder(p_decoder); - } - - /// (Re)defines the notify - void addNotifyAudioChange(AudioInfoSupport *notify) { - this->p_final_notify = notify; - // notification for audio configuration - if (p_decoder != nullptr) { - p_decoder->addNotifyAudioChange(*this); - } - } - - /// Updates the audio info in the related objects - void setAudioInfo(AudioInfo info) override { - TRACED(); - LOGI("sample_rate: %d", (int) info.sample_rate); - LOGI("bits_per_sample: %d", (int) info.bits_per_sample); - LOGI("channels: %d", (int) info.channels); - this->info = info; - // notifiy volume - volume_out.setAudioInfo(info); - fade.setAudioInfo(info); - // notifiy final ouput: e.g. i2s - if (p_final_print != nullptr) - p_final_print->setAudioInfo(info); - if (p_final_stream != nullptr) - p_final_stream->setAudioInfo(info); - if (p_final_notify != nullptr) - p_final_notify->setAudioInfo(info); - }; - - AudioInfo audioInfo() override { return info; } - - /// starts / resumes the playing after calling stop(): same as setActive(true) - void play() { - TRACED(); - setActive(true); - } - - /// plays a complete audio file from start to finish (blocking call) - /// @param filePath path to the audio file to play - /// @return true if file was found and played successfully, false otherwise - bool playFile(const char *filePath) { - TRACED(); - if (!setPath(filePath)) { - LOGW("Could not open file: %s", filePath); - return false; - } - - LOGI("Playing %s", filePath); - play(); - - while (isActive()) { - size_t bytes_copied = copy(); - // if no bytes were copied, the file may have ended - if (bytes_copied == 0) { - stop(); - break; - } - } - - LOGI("%s has finished playing", filePath); - return true; - } - - /// halts the playing: same as setActive(false) - void stop() { - TRACED(); - setActive(false); - } - - /// moves to next file or nth next file when indicating an offset. Negative - /// values are supported to move back. - bool next(int offset = 1) { - TRACED(); - writeEnd(); - stream_increment = offset >= 0 ? 1 : -1; - active = setStream(p_source->nextStream(offset)); - return active; - } - - /// moves to the selected file position - bool setIndex(int idx) { - TRACED(); - writeEnd(); - stream_increment = 1; - active = setStream(p_source->selectStream(idx)); - return active; - } - - /// Moves to the selected file w/o updating the actual file position - bool setPath(const char *path) { - TRACED(); - writeEnd(); - stream_increment = 1; - active = setStream(p_source->selectStream(path)); - return active; - } - - /// moves to previous file - bool previous(int offset = 1) { - TRACED(); - writeEnd(); - stream_increment = -1; - active = setStream(p_source->previousStream(abs(offset))); - return active; - } - - /// start selected input stream - bool setStream(Stream *input) { - end(); - out_decoding.begin(); - p_input_stream = input; - if (p_input_stream != nullptr) { - LOGD("open selected stream"); - meta_out.begin(); - copier.begin(out_decoding, *p_input_stream); - } - // execute callback if defined - if (on_stream_change_callback) on_stream_change_callback(p_input_stream, p_reference); - return p_input_stream != nullptr; - } - - /// Provides the actual stream (=e.g.file) - Stream *getStream() { return p_input_stream; } - - /// determines if the player is active - bool isActive() { return active; } - - /// determines if the player is active - operator bool() { return isActive(); } - - /// The same like start() / stop() - void setActive(bool isActive) { - if (is_auto_fade) { - if (isActive) { - fade.setFadeInActive(true); - } else { - fade.setFadeOutActive(true); - copier.copy(); - writeSilence(2048); - } - } - active = isActive; - } - - /// sets the volume - values need to be between 0.0 and 1.0 - bool setVolume(float volume) override { - bool result = true; - if (volume >= 0.0f && volume <= 1.0f) { - if (abs(volume - current_volume) > 0.01f) { - LOGI("setVolume(%f)", volume); - volume_out.setVolume(volume); - current_volume = volume; - } - } else { - LOGE("setVolume value '%f' out of range (0.0 -1.0)", volume); - result = false; - } - return result; - } - - /// Determines the actual volume - float volume() override { return current_volume; } - - /// Set automatically move to next file and end of current file: This is - /// determined from the AudioSource. If you want to override it call this - /// method after calling begin()! - void setAutoNext(bool next) { autonext = next; } - - /// Defines the wait time in ms if the target output is full - void setDelayIfOutputFull(int delayMs) { delay_if_full = delayMs; } - - /// Copies DEFAULT_BUFFER_SIZE (=1024 bytes) from the source to the decoder: Call this method in the loop. - size_t copy() { - return copy(copier.bufferSize()); - } - - /// Copies all the data - size_t copyAll() { - size_t result = 0; - size_t step = copy(); - while(step > 0){ - result += step; - step = copy(); - } - return result; - } - - /// Copies the indicated number of bytes from the source to the decoder: Call this method in the loop. - size_t copy(size_t bytes) { - size_t result = 0; - if (active) { - TRACED(); - if (delay_if_full != 0 && ((p_final_print != nullptr && - p_final_print->availableForWrite() == 0) || - (p_final_stream != nullptr && - p_final_stream->availableForWrite() == 0))) { - // not ready to do anything - so we wait a bit - delay(delay_if_full); - return 0; - } - // handle sound - result = copier.copyBytes(bytes); - if (result > 0 || timeout == 0) { - // reset timeout if we had any data - timeout = millis() + p_source->timeoutAutoNext(); - } - // move to next stream after timeout - moveToNextFileOnTimeout(); - - // return silence when there was no data - if (result < bytes && silence_on_inactive){ - writeSilence(bytes - result); - } - - } else { - // e.g. A2DP should still receive data to keep the connection open - if (silence_on_inactive) { - writeSilence(1024); - } - } - return result; - } - - /// Change the VolumeControl implementation - void setVolumeControl(VolumeControl &vc) { - volume_out.setVolumeControl(vc); - } - - /// Provides access to the StreamCopy, so that we can register additinal - /// callbacks - StreamCopy &getStreamCopy() { return copier; } - - /// If set to true the player writes 0 values instead of no data if the player - /// is inactive - void setSilenceOnInactive(bool active) { silence_on_inactive = active; } - - /// Checks if silence_on_inactive has been activated (default false) - bool isSilenceOnInactive() { return silence_on_inactive; } - - /// Sends the requested bytes as 0 values to the output - void writeSilence(size_t bytes) { - TRACEI(); - if (p_final_print != nullptr) { - p_final_print->writeSilence(bytes); - } else if (p_final_stream != nullptr) { - p_final_stream->writeSilence(bytes); - } - } - - // /// Provides the Print object to which we send the decoding result - // Print *getVolumeOutput() { return &volume_out; } - - /// Provides the reference to the volume stream - VolumeStream &getVolumeStream() { return volume_out; } - - /// Activates/deactivates the automatic fade in and fade out to prevent - /// popping sounds: default is active - void setAutoFade(bool active) { is_auto_fade = active; } - - bool isAutoFade() { return is_auto_fade; } - - /// Change the default ID3 max metadata size (256) - void setMetaDataSize(int size){ - meta_out.resize(size); - } - - /// this is used to set the reference for the stream change callback - void setReference(void* ref) { - p_reference = ref; - } - - /// Defines the medatadata callback - void setMetadataCallback(void (*callback)(MetaDataType type, - const char *str, int len), - ID3TypeSelection sel = SELECT_ID3) { - TRACEI(); - // setup metadata. - if (p_source->setMetadataCallback(callback)) { - // metadata is handled by source - LOGI("Using ICY Metadata"); - meta_active = false; - } else { - // metadata is handled here - meta_out.setCallback(callback); - meta_out.setFilter(sel); - meta_active = true; - } - } - - /// Defines a callback that is called when the stream is changed - void setOnStreamChangeCallback( void(*callback)(Stream*stream_ptr,void* reference)){ - on_stream_change_callback = callback; - } - - -protected: - bool active = false; - bool autonext = true; - bool silence_on_inactive = false; - AudioSource *p_source = nullptr; - VolumeStream volume_out; // Volume control - FadeStream fade; // Phase in / Phase Out to avoid popping noise - MetaDataID3 meta_out; // Metadata parser - EncodedAudioOutput out_decoding; // Decoding stream - CopyDecoder no_decoder{true}; - AudioDecoder *p_decoder = &no_decoder; - Stream *p_input_stream = nullptr; - AudioOutput *p_final_print = nullptr; - AudioStream *p_final_stream = nullptr; - AudioInfoSupport *p_final_notify = nullptr; - StreamCopy copier; // copies sound into i2s - AudioInfo info; - bool meta_active = false; - uint32_t timeout = 0; - int stream_increment = 1; // +1 moves forward; -1 moves backward - float current_volume = -1.0f; // illegal value which will trigger an update - int delay_if_full = 100; - bool is_auto_fade = true; - void* p_reference = nullptr; - void(*on_stream_change_callback)(Stream*stream_ptr,void* reference) = nullptr; - - void setupFade() { - if (p_final_print != nullptr) { - fade.setAudioInfo(p_final_print->audioInfo()); - } else if (p_final_stream != nullptr) { - fade.setAudioInfo(p_final_stream->audioInfo()); - } - } - - void moveToNextFileOnTimeout() { - if (!autonext) - return; - if (p_final_stream != nullptr && p_final_stream->availableForWrite() == 0) - return; - if (p_input_stream == nullptr || millis() > timeout) { - if (is_auto_fade) - fade.setFadeInActive(true); - if (autonext) { - LOGI("-> timeout - moving by %d", stream_increment); - // open next stream - if (!next(stream_increment)) { - LOGD("stream is null"); - } - } else { - active = false; - } - timeout = millis() + p_source->timeoutAutoNext(); - } - } - - void writeEnd() { - // end silently - TRACEI(); - if (is_auto_fade) { - fade.setFadeOutActive(true); - copier.copy(); - // start by fading in - fade.setFadeInActive(true); - } - // restart the decoder to make sure it does not contain any audio when we - // continue - p_decoder->begin(); - } - - /// Callback implementation which writes to metadata - static void decodeMetaData(void *obj, void *data, size_t len) { - LOGD("%s, %zu", LOG_METHOD, len); - AudioPlayer *p = (AudioPlayer *)obj; - if (p->meta_active) { - p->meta_out.write((const uint8_t *)data, len); - } - } -}; - -} // namespace audio_tools diff --git a/src/AudioTools/CoreAudio/AudioRuntime.h b/src/AudioTools/CoreAudio/AudioRuntime.h deleted file mode 100644 index 31294315b2..0000000000 --- a/src/AudioTools/CoreAudio/AudioRuntime.h +++ /dev/null @@ -1,62 +0,0 @@ -#pragma once - -#include "AudioToolsConfig.h" - -namespace audio_tools { - -/** - * @brief Public generic methods - * @author Phil Schatzmann - * @copyright GPLv3 - */ - -/// stops any further processing by spinning in an endless loop @ingroup basic -inline void stop() { - #ifdef EXIT_ON_STOP - exit(0); - #else - while(true){ - delay(1000); - } - #endif -} - -/// Executes heap_caps_check_integrity_all() @ingroup basic -inline static void checkMemory(bool printMemory=false) { - #if defined(ESP32) && defined(ARDUINO) - assert(heap_caps_check_integrity_all(true)); - if (printMemory) Serial.printf("==> Available stack: %d - heap: %u - psram: %u\n",(int) uxTaskGetStackHighWaterMark(NULL), (unsigned)ESP.getFreeHeap(),(unsigned)ESP.getFreePsram()); - #endif -} - -#ifdef ARDUINO -inline void printNChar(char ch, int n){ - for (int j=0;jsetTimeout(clientTimeout); - } - - virtual bool begin() { return true; } - virtual void end() {} - - virtual size_t readBytes(uint8_t *data, size_t len) { - // Serial.print("Timeout audiostream: "); - // Serial.println(p_stream->getTimeout()); - return p_stream->readBytes(data, len); - } - - int read() { return p_stream->read(); } - - int peek() { return p_stream->peek(); } - - int available() { return p_stream->available(); } - - virtual size_t write(uint8_t c) { return p_stream->write(c); } - - virtual size_t write(const uint8_t *data, size_t len) { - return p_stream->write(data, len); - } - - virtual int availableForWrite() { return p_stream->availableForWrite(); } - - virtual void flush() { p_stream->flush(); } - - protected: - Stream *p_stream; - int32_t clientTimeout = URL_CLIENT_TIMEOUT; // 60000; -}; - -/** - * @brief Abstract class: Objects can be put into a pipleline. - * @ingroup io - * @author Phil Schatzmann - * @copyright GPLv3 - */ - -class ModifyingStream : public AudioStream { - public: - /// Defines/Changes the input & output - virtual void setStream(Stream &in) = 0; - /// Defines/Changes the output target - virtual void setOutput(Print &out) = 0; -}; - -/** - * @brief A simple Stream implementation which is backed by allocated memory. - * @ingroup io - * @author Phil Schatzmann - * @copyright GPLv3 - * - */ -class MemoryStream : public AudioStream { - public: - // Default constructor - MemoryStream() = default; - /// Constructor for alloction in RAM - MemoryStream(int buffer_size, MemoryType memoryType) { - LOGD("MemoryStream: %d", buffer_size); - this->buffer_size = buffer_size; - this->memory_type = memoryType; - resize(buffer_size); - info.clear(); // mark audio info as unknown - } - - /// Constructor for data from Progmem, active is set to true automatically by - /// default. - MemoryStream(const uint8_t *buffer, int buffer_size, bool isActive = true, - MemoryType memoryType = FLASH_RAM) { - LOGD("MemoryStream: %d", buffer_size); - setValue(buffer, buffer_size, memoryType); - is_active = isActive; - info.clear(); // mark audio info as unknown - } - - /// Copy Constructor - MemoryStream(MemoryStream &source) : AudioStream() { copy(source); } - - /// Move Constructor - MemoryStream(MemoryStream &&source) { - setValue(source.buffer, source.buffer_size, source.memory_type); - // clear source data - source.setValue(nullptr, 0, source.memory_type); - } - - ~MemoryStream() { - TRACED(); - if (memoryCanChange() && buffer != nullptr) free(buffer); - } - - /// copy assignement operator - MemoryStream &operator=(MemoryStream &other) { - copy(other); - return *this; - } - - /// Returns true if there is still some more data - operator bool() override { return available() > 0; } - - /// Define some audio info and start the processing - bool begin(AudioInfo info) { - this->info = info; - return begin(); - } - - /// resets the read pointer - bool begin() override { - TRACED(); - write_pos = memoryCanChange() ? 0 : buffer_size; - if (this->buffer == nullptr && memoryCanChange()) { - resize(buffer_size); - } - read_pos = 0; - is_active = true; - return true; - } - - virtual size_t write(uint8_t byte) override { - if (!is_active) return 0; - if (memory_type == FLASH_RAM) return 0; - if (buffer == nullptr) return 0; - int result = 0; - if (write_pos < buffer_size) { - result = 1; - buffer[write_pos] = byte; - write_pos++; - } - return result; - } - - virtual size_t write(const uint8_t *data, size_t len) override { - if (!is_active) return 0; - if (memory_type == FLASH_RAM) return 0; - size_t result = 0; - for (size_t j = 0; j < len; j++) { - if (!write(data[j])) { - break; - } - result = j + 1; - } - return result; - } - - virtual int available() override { - if (!is_active) return 0; - if (buffer == nullptr) return 0; - int result = write_pos - read_pos; - if (result <= 0 && is_loop) { - // rewind to start - read_pos = rewind_pos; - result = write_pos - read_pos; - // call callback - if (rewind != nullptr) rewind(); - } - return is_loop ? DEFAULT_BUFFER_SIZE : result; - } - - virtual int availableForWrite() override { - if (!is_active) return 0; - if (memory_type == FLASH_RAM) return 0; - return buffer_size - write_pos; - } - - virtual int read() override { - int result = peek(); - if (result >= 0) { - read_pos++; - } - return result; - } - - virtual size_t readBytes(uint8_t *data, size_t len) override { - if (!is_active) return 0; - size_t count = 0; - while (count < len) { - int c = read(); - if (c < 0) break; - *data++ = (char)c; - count++; - } - return count; - } - - virtual int peek() override { - if (!is_active) return -1; - int result = -1; - if (available() > 0) { - result = buffer[read_pos]; - } - return result; - } - - virtual void flush() override {} - - virtual void end() override { - read_pos = 0; - is_active = false; - } - - /// clears the audio data: sets all values to 0 - virtual void clear(bool reset = false) { - if (memoryCanChange()) { - write_pos = 0; - read_pos = 0; - if (buffer == nullptr) { - resize(buffer_size); - } - if (reset) { - // we clear the buffer data - memset(buffer, 0, buffer_size); - } - } else { - read_pos = 0; - LOGW("data is read only"); - } - } - - /// Automatically rewinds to the beginning when reaching the end. For wav - /// files we move to pos 44 to ignore the header! - virtual void setLoop(bool loop) { - is_loop = loop; - rewind_pos = 0; - if (buffer != nullptr && buffer_size > 12) { - if (memcmp("WAVE", buffer + 8, 4) == 0) { - rewind_pos = 44; - } - } - } - - /// Automatically rewinds to the indicated position when reaching the end - virtual void setLoop(bool loop, int rewindPos) { - is_loop = loop; - rewind_pos = rewindPos; - } - - /// Resizes the available memory. Returns false for PROGMEM or when allocation - /// failed - virtual bool resize(size_t size) { - if (!memoryCanChange()) return false; - - buffer_size = size; - switch (memory_type) { -#if defined(ESP32) && defined(ARDUINO) - case PS_RAM: - buffer = (buffer == nullptr) ? (uint8_t *)ps_calloc(size, 1) - : (uint8_t *)ps_realloc(buffer, size); - break; -#endif - default: - buffer = (buffer == nullptr) ? (uint8_t *)calloc(size, 1) - : (uint8_t *)realloc(buffer, size); - break; - } - return buffer != nullptr; - } - - /// Provides access to the data array - virtual uint8_t *data() { return buffer; } - - /// update the write_pos (e.g. when we used data() to update the array) - virtual void setAvailable(size_t len) { this->write_pos = len; } - - /// Callback which is executed when we rewind (in loop mode) to the beginning - void setRewindCallback(void (*cb)()) { this->rewind = cb; } - - /// Update the values (buffer and size) - void setValue(const uint8_t *buffer, int buffer_size, - MemoryType memoryType = FLASH_RAM) { - this->buffer_size = buffer_size; - this->read_pos = 0; - this->write_pos = buffer_size; - this->buffer = (uint8_t *)buffer; - this->memory_type = memoryType; - } - - protected: - int write_pos = 0; - int read_pos = 0; - int buffer_size = 0; - int rewind_pos = 0; - uint8_t *buffer = nullptr; - MemoryType memory_type = RAM; - bool is_loop = false; - void (*rewind)() = nullptr; - bool is_active = false; - - bool memoryCanChange() { return memory_type != FLASH_RAM; } - - void copy(MemoryStream &source) { - if (this == &source) return; - if (source.memory_type == FLASH_RAM) { - setValue(source.buffer, source.buffer_size, source.memory_type); - } else { - setValue(nullptr, source.buffer_size, source.memory_type); - resize(buffer_size); - memcpy(buffer, source.buffer, buffer_size); - } - } -}; - -/** - * @brief An AudioStream backed by a Ringbuffer. We can write to the end and - * read from the beginning of the stream - * @ingroup io - * @author Phil Schatzmann - * @copyright GPLv3 - */ -class RingBufferStream : public AudioStream { - public: - RingBufferStream(int size = DEFAULT_BUFFER_SIZE) { resize(size); } - - virtual int available() override { - // LOGD("RingBufferStream::available: %zu",buffer->available()); - return buffer.available(); - } - - virtual int availableForWrite() override { - return buffer.availableForWrite(); - } - - virtual void flush() override {} - virtual int peek() override { - uint8_t data = 0; - if (!buffer.peek(data)) return -1; - return data; - } - virtual int read() override { - uint8_t data = 0; - if (!buffer.read(data)) return -1; - return data; - } - - virtual size_t readBytes(uint8_t *data, size_t len) override { - return buffer.readArray(data, len); - } - - virtual size_t write(const uint8_t *data, size_t len) override { - // LOGD("RingBufferStream::write: %zu",len); - return buffer.writeArray(data, len); - } - - virtual size_t write(uint8_t c) override { return buffer.write(c); } - - void resize(int size) { buffer.resize(size); } - - size_t size() { return buffer.size(); } - - protected: - RingBuffer buffer{0}; -}; - -/** - * @brief Source for reading generated tones. Please note - * - that the output is for one channel only! - * - we do not support reading of individual characters! - * - we do not support any write operations - * @ingroup io - * @param generator - * @author Phil Schatzmann - * @copyright GPLv3 - */ - -template -class GeneratedSoundStream : public AudioStream { - public: - GeneratedSoundStream() = default; - - GeneratedSoundStream(SoundGenerator &generator) { - TRACED(); - setInput(generator); - } - - void setInput(SoundGenerator &generator) { - this->generator_ptr = &generator; - } - - AudioInfo defaultConfig() { return this->generator_ptr->defaultConfig(); } - - void setAudioInfo(AudioInfo newInfo) override { - if (newInfo.bits_per_sample != sizeof(T) * 8) { - LOGE("Wrong bits_per_sample: %d", newInfo.bits_per_sample); - } - AudioStream::setAudioInfo(newInfo); - } - - /// start the processing - bool begin() override { - TRACED(); - if (generator_ptr == nullptr) { - LOGE("%s", source_not_defined_error); - return false; - } - generator_ptr->begin(); - notifyAudioChange(generator_ptr->audioInfo()); - active = true; - return active; - } - - /// start the processing - bool begin(AudioInfo cfg) { - TRACED(); - if (generator_ptr == nullptr) { - LOGE("%s", source_not_defined_error); - return false; - } - generator_ptr->begin(cfg); - notifyAudioChange(generator_ptr->audioInfo()); - active = true; - return active; - } - - /// stop the processing - void end() override { - TRACED(); - generator_ptr->end(); - active = true; // legacy support - most sketches do not call begin - } - - AudioInfo audioInfo() override { return generator_ptr->audioInfo(); } - - /// This is unbounded so we just return the buffer size - virtual int available() override { - return active ? DEFAULT_BUFFER_SIZE * 2 : 0; - } - - /// privide the data as byte stream - size_t readBytes(uint8_t *data, size_t len) override { - if (!active) return 0; - LOGD("GeneratedSoundStream::readBytes: %u", (unsigned int)len); - return generator_ptr->readBytes(data, len); - } - - bool isActive() { return active && generator_ptr->isActive(); } - - operator bool() override { return isActive(); } - - void flush() override {} - - protected: - bool active = true; // support for legacy sketches - SoundGenerator *generator_ptr; - const char *source_not_defined_error = "Source not defined"; -}; - -/** - * @brief The Arduino Stream supports operations on single characters. This is - * usually not the best way to push audio information, but we will support it - * anyway - by using a buffer. On reads: if the buffer is empty it gets refilled - * - for writes if it is full it gets flushed. - * @author Phil Schatzmann - * @copyright GPLv3 - */ -class BufferedStream : public ModifyingStream { - public: - BufferedStream(size_t buffer_size) { - TRACED(); - buffer.resize(buffer_size); - } - - BufferedStream(Print &out, size_t buffer_size = 1024) { - TRACED(); - setOutput(out); - buffer.resize(buffer_size); - } - - BufferedStream(Stream &io, size_t buffer_size = 1024) { - TRACED(); - setStream(io); - buffer.resize(buffer_size); - } - - BufferedStream(size_t buffer_size, Print &out) { - TRACED(); - setOutput(out); - buffer.resize(buffer_size); - } - - BufferedStream(size_t buffer_size, Stream &io) { - TRACED(); - setStream(io); - buffer.resize(buffer_size); - } - - void setOutput(Print &out) override { p_out = &out; } - void setStream(Print &out) { setOutput(out); } - void setStream(Stream &io) override { - p_in = &io; - p_out = &io; - } - - /// writes a byte to the buffer - size_t write(uint8_t c) override { - if (buffer.isFull()) { - flush(); - } - return buffer.write(c); - } - - /// Use this method: write an array - size_t write(const uint8_t *data, size_t len) override { - LOGD("%s: %zu", LOG_METHOD, len); - int result = 0; - for (int j = 0; j < len; j++) { - result += write(data[j]); - } - return result; - } - - /// empties the buffer - void flush() override { - // just dump the memory of the buffer and clear it - if (buffer.available() > 0) { - writeExt(buffer.address(), buffer.available()); - buffer.reset(); - } - } - - /// reads a byte - to be avoided - int read() override { - if (buffer.isEmpty()) { - refill(); - } - uint8_t result = 0; - if (!buffer.read(result)) return -1; - return result; - } - - /// peeks a byte - to be avoided - int peek() override { - if (buffer.isEmpty()) { - refill(); - } - uint8_t result = 0; - if (!buffer.peek(result)) return -1; - return result; - }; - - /// Use this method !! - size_t readBytes(uint8_t *data, size_t len) override { - if (buffer.isEmpty()) { - return readExt(data, len); - } else { - refill(); - return buffer.readArray(data, len); - } - } - - /// Returns the available bytes in the buffer: to be avoided - int available() override { - if (buffer.isEmpty()) { - refill(); - } - return buffer.available(); - } - - /// Clears all the data in the buffer - void clear() { buffer.reset(); } - - protected: - SingleBuffer buffer; - Print *p_out = nullptr; - Stream *p_in = nullptr; - - // refills the buffer with data from the source - void refill() { - size_t result = readExt(buffer.address(), buffer.size()); - buffer.setAvailable(result); - } - - virtual size_t writeExt(const uint8_t *data, size_t len) { - return p_out == nullptr ? 0 : p_out->write(data, len); - } - virtual size_t readExt(uint8_t *data, size_t len) { - return p_in == nullptr ? 0 : p_in->readBytes(data, len); - } -}; - -/** - * @brief Both the data of the read or write - * operations will be converted with the help of the indicated converter. - * @ingroup transform - * @tparam T - * @param out - * @param converter - */ -template -class ConverterStream : public ModifyingStream { - public: - ConverterStream() = default; - - ConverterStream(BaseConverter &converter) { setConverter(converter); } - - ConverterStream(Stream &stream, BaseConverter &converter) { - setConverter(converter); - setStream(stream); - } - - ConverterStream(Print &out, BaseConverter &converter) { - setConverter(converter); - setOutput(out); - } - - void setStream(Stream &stream) { - TRACEI(); - p_stream = &stream; - p_out = &stream; - } - - void setOutput(Print &out) { - TRACEI(); - p_out = &out; - } - - void setConverter(BaseConverter &cnv) { p_converter = &cnv; } - - virtual int availableForWrite() { return p_out->availableForWrite(); } - - virtual size_t write(const uint8_t *data, size_t len) { - size_t result = p_converter->convert((uint8_t *)data, len); - if (result > 0) { - size_t result_written = p_out->write(data, result); - return len * result_written / result; - } - return 0; - } - - size_t readBytes(uint8_t *data, size_t len) override { - if (p_stream == nullptr) return 0; - size_t result = p_stream->readBytes(data, len); - return p_converter->convert(data, result); - } - - /// Returns the available bytes in the buffer: to be avoided - virtual int available() override { - if (p_stream == nullptr) return 0; - return p_stream->available(); - } - - protected: - Stream *p_stream = nullptr; - Print *p_out = nullptr; - BaseConverter *p_converter; -}; - -/** - * @brief Class which measures the thruput - * @author Phil Schatzmann - * @copyright GPLv3 - * @ingroup io - */ -class MeasuringStream : public ModifyingStream { - public: - MeasuringStream(int count = 10, Print *logOut = nullptr) { - this->count = count; - this->max_count = count; - p_stream = &null; - p_print = &null; - start_time = millis(); - p_logout = logOut; - } - - MeasuringStream(Print &print, int count = 10, Print *logOut = nullptr) { - this->count = count; - this->max_count = count; - setOutput(print); - start_time = millis(); - p_logout = logOut; - } - - MeasuringStream(Stream &stream, int count = 10, Print *logOut = nullptr) { - this->count = count; - this->max_count = count; - setStream(stream); - start_time = millis(); - p_logout = logOut; - } - - /// Defines the logging output - void setLogOutput(Print &out) { p_logout = &out; } - - /// Defines/Changes the input & output - void setStream(Stream &io) override { - p_print = &io; - p_stream = &io; - }; - - /// Defines/Changes the output target - void setOutput(Print &out) override { p_print = &out; } - - /// Provides the data from all streams mixed together - size_t readBytes(uint8_t *data, size_t len) override { - total_bytes_since_begin += len; - return measure(p_stream->readBytes(data, len)); - } - - int available() override { return p_stream->available(); } - - /// Writes raw PCM audio data, which will be the input for the volume control - virtual size_t write(const uint8_t *data, size_t len) override { - total_bytes_since_begin += len; - return measure(p_print->write(data, len)); - } - - /// Provides the nubmer of bytes we can write - virtual int availableForWrite() override { - return p_print->availableForWrite(); - } - - /// Returns the actual thrughput in bytes per second - int bytesPerSecond() { return bytes_per_second; } - - /// Returns the actual thrughput in frames (samples) per second - int framesPerSecond() { - if (frame_size == 0) return 0; - return bytes_per_second / frame_size; - } - - /// Provides the time when the last measurement was started - uint32_t startTime() { return start_time; } - - void setAudioInfo(AudioInfo info) override { - AudioStream::info = info; - setFrameSize(info.bits_per_sample / 8 * info.channels); - } - - bool begin() override { - total_bytes_since_begin = 0; - ms_at_begin = millis(); - return AudioStream::begin(); - } - - bool begin(AudioInfo info) { - setAudioInfo(info); - return begin(); - } - - /// Trigger reporting in frames (=samples) per second - void setFrameSize(int size) { frame_size = size; } - - /// Report in bytes instead of samples - void setReportBytes(bool flag) { report_bytes = flag; } - - void setName(const char *name) { this->name = name; } - - /// Provides the time in ms since the last call of begin() - uint32_t timeSinceBegin() { return millis() - ms_at_begin; } - - /// Provides the total processed bytes since the last call of begin() - uint32_t bytesSinceBegin() { return total_bytes_since_begin; } - - /// Provides the estimated runtime in milliseconds for the indicated total - uint32_t estimatedTotalTimeFor(uint32_t totalBytes) { - if (bytesSinceBegin() == 0) return 0; - return static_cast(timeSinceBegin()) / bytesSinceBegin() * - totalBytes; - } - - /// Provides the estimated time from now to the end in ms - uint32_t estimatedOpenTimeFor(uint32_t totalBytes) { - if (bytesSinceBegin() == 0) return 0; - return estimatedTotalTimeFor(totalBytes) - timeSinceBegin(); - } - - /// Alternative update method: e.g report actual file positon: returns true if - /// the file position was increased - bool setProcessedBytes(uint32_t pos) { - bool is_regular_update = true; - if (pos < total_bytes_since_begin) { - begin(); - is_regular_update = false; - } - total_bytes_since_begin = pos; - return is_regular_update; - } - - protected: - int max_count = 0; - int count = 0; - Stream *p_stream = nullptr; - Print *p_print = nullptr; - uint32_t start_time; - int total_bytes = 0; - int bytes_per_second = 0; - int frame_size = 0; - NullStream null; - Print *p_logout = nullptr; - bool report_bytes = false; - const char *name = ""; - uint32_t ms_at_begin = 0; - uint32_t total_bytes_since_begin = 0; - - size_t measure(size_t len) { - count--; - total_bytes += len; - - if (count <= 0) { - uint32_t end_time = millis(); - int time_diff = end_time - start_time; // in ms - if (time_diff > 0) { - bytes_per_second = total_bytes / time_diff * 1000; - printResult(); - count = max_count; - total_bytes = 0; - start_time = end_time; - } - } - return len; - } - - void printResult() { - char msg[70]; - if (report_bytes || frame_size == 0) { - snprintf(msg, 70, "%s ==> Bytes per second: %d", name, bytes_per_second); - } else { - snprintf(msg, 70, "%s ==> Samples per second: %d", name, - bytes_per_second / frame_size); - } - if (p_logout != nullptr) { - p_logout->println(msg); - } else { - LOGI("%s", msg); - } - } -}; - -/** - * @brief Configuration for ProgressStream - * @author Phil Schatzmann - * @copyright GPLv3 - * - */ -class ProgressStreamInfo : public AudioInfo { - public: - size_t total_size = 0; -}; -/** - * @brief Generic calss to measure the the total bytes which were processed in - * order to calculate the progress as a percentage of the total size. - * @author Phil Schatzmann - * @copyright GPLv3 - * @ingroup io - */ -class ProgressStream : public ModifyingStream { - public: - ProgressStream() = default; - - ProgressStream(Print &print) { setPrint(print); } - - ProgressStream(Stream &stream) { setStream(stream); } - - ProgressStream(AudioStream &stream) { - setStream(stream); - p_info_from = &stream; - } - - ProgressStreamInfo &defaultConfig() { return progress_info; } - - void setAudioInfo(AudioInfo info) override { - AudioStream::setAudioInfo(info); - progress_info.copyFrom(info); - } - - void setStream(Stream &stream) override { - p_stream = &stream; - p_print = &stream; - } - - void setStream(Print &print) { p_print = &print; } - - void setPrint(Print &print) { p_print = &print; } - - bool begin() override { - if (p_info_from != nullptr) { - setAudioInfo(p_info_from->audioInfo()); - } - return AudioStream::begin(); - } - - /// Updates the total size and restarts the percent calculation: Same as - /// calling setSize() - bool begin(size_t len) { - setSize(len); - return begin(); - } - - bool begin(ProgressStreamInfo info) { - progress_info = info; - setAudioInfo(info); - return begin(); - } - - /// Updates the total size and restarts the percent calculation - void setSize(size_t len) { - total_processed = 0; - progress_info.total_size = len; - } - - /// Provides the current total size (defined by setSize) - size_t size() { return progress_info.total_size; } - - /// Provides the number of processed bytes - size_t processedBytes() { return total_processed; } - - /// Provides the number of processed seconds - size_t processedSecs() { return total_processed / byteRate(); } - - /// Provides the total_size provided in the configuration - size_t totalBytes() { return progress_info.total_size; } - - /// Converts the totalBytes() to seconds - size_t totalSecs() { return totalBytes() / byteRate(); } - - /// Provides the processed percentage: If no size has been defined we return 0 - float percentage() { - if (progress_info.total_size == 0) return 0; - return 100.0 * total_processed / progress_info.total_size; - } - - /// Provides the data from all streams mixed together - size_t readBytes(uint8_t *data, size_t len) override { - if (p_stream == nullptr) return 0; - return measure(p_stream->readBytes(data, len)); - } - - int available() override { - if (p_stream == nullptr) return 0; - return p_stream->available(); - } - - /// Writes raw PCM audio data, which will be the input for the volume control - virtual size_t write(const uint8_t *data, size_t len) override { - if (p_print == nullptr) return 0; - return measure(p_print->write(data, len)); - } - - /// Provides the nubmer of bytes we can write - virtual int availableForWrite() override { - if (p_print == nullptr) return 0; - return p_print->availableForWrite(); - } - - protected: - ProgressStreamInfo progress_info; - Stream *p_stream = nullptr; - Print *p_print = nullptr; - AudioInfoSupport *p_info_from = nullptr; - size_t total_processed = 0; - - size_t measure(size_t len) { - total_processed += len; - return len; - } - - size_t byteRate() { - AudioInfo info = audioInfo(); - int byte_rate = info.sample_rate * info.bits_per_sample * info.channels / 8; - if (byte_rate == 0) { - LOGE("Audio Info not defined"); - return 0; - } - return byte_rate; - } -}; - -/** - * @brief Configure Throttle setting - * @author Phil Schatzmann - * @copyright GPLv3 - */ -struct ThrottleConfig : public AudioInfo { - ThrottleConfig() { - sample_rate = 44100; - bits_per_sample = 16; - channels = 2; - } - int correction_us = 0; -}; - -/** - * @brief Throttle the sending or receiving of the audio data to limit it to the - * indicated sample rate. - * @author Phil Schatzmann - * @copyright GPLv3 - */ -class Throttle : public ModifyingStream { - public: - Throttle() = default; - Throttle(Print &out) { setOutput(out); } - Throttle(Stream &io) { setStream(io); } - - /// Defines/Changes the input & output - void setStream(Stream &io) override { - p_out = &io; - p_in = &io; - }; - - /// Defines/Changes the output target - void setOutput(Print &out) override { p_out = &out; } - - ThrottleConfig defaultConfig() { - ThrottleConfig c; - return c; - } - - bool begin(ThrottleConfig cfg) { - LOGI("begin sample_rate: %d, channels: %d, bits: %d", (int)info.sample_rate, - (int)info.channels, (int)info.bits_per_sample); - this->info = cfg; - this->cfg = cfg; - return begin(); - } - - bool begin(AudioInfo info) { - LOGI("begin sample_rate: %d, channels: %d, bits: %d", (int)info.sample_rate, - (int)info.channels, (int)info.bits_per_sample); - this->info = info; - this->cfg.copyFrom(info); - return begin(); - } - - bool begin() override { - frame_size = cfg.bits_per_sample / 8 * cfg.channels; - startDelay(); - return true; - } - - // (re)starts the timing - void startDelay() { - start_time = micros(); - sum_frames = 0; - } - - int availableForWrite() override { - if (p_out) { - return p_out->availableForWrite(); - } - return DEFAULT_BUFFER_SIZE; - } - - size_t write(const uint8_t *data, size_t len) override { - size_t result = p_out->write(data, len); - delayBytes(len); - return result; - } - - int available() override { - if (p_in == nullptr) return 0; - return p_in->available(); - } - - size_t readBytes(uint8_t *data, size_t len) override { - if (p_in == nullptr) { - delayBytes(len); - return 0; - } - size_t result = p_in->readBytes(data, len); - delayBytes(len); - return result; - } - - // delay - void delayBytes(size_t bytes) { delayFrames(bytes / frame_size); } - - // delay - void delayFrames(size_t frames) { - sum_frames += frames; - uint64_t durationUsEff = micros() - start_time; - uint64_t durationUsToBe = getDelayUs(sum_frames); - int64_t waitUs = durationUsToBe - durationUsEff + cfg.correction_us; - LOGD("wait us: %ld", static_cast(waitUs)); - if (waitUs > 0) { - int64_t waitMs = waitUs / 1000; - if (waitMs > 0) delay(waitMs); - delayMicroseconds(waitUs - (waitMs * 1000)); - } else { - LOGD("negative delay!") - } - } - - inline int64_t getDelayUs(uint64_t frames) { - return (frames * 1000000) / cfg.sample_rate; - } - - inline int64_t getDelayMs(uint64_t frames) { - return getDelayUs(frames) / 1000; - } - - inline int64_t getDelaySec(uint64_t frames) { - return getDelayUs(frames) / 1000000l; - } - - protected: - uint32_t start_time = 0; - uint32_t sum_frames = 0; - ThrottleConfig cfg; - int frame_size = 0; - Print *p_out = nullptr; - Stream *p_in = nullptr; -}; - -/** - * @brief MixerStream is mixing the input from Multiple Input Streams. - * All streams must have the same audo format (sample rate, channels, bits per - * sample). - * @ingroup transform - * @author Phil Schatzmann - * @copyright GPLv3 - */ - -template -class InputMixer : public AudioStream { - public: - InputMixer() = default; - - /// Adds a new input stream and returns it's actual index position - int add(Stream &in, int weight = 100) { - streams.push_back(&in); - weights.push_back(weight); - total_weights += weight; - return streams.indexOf(&in); - } - - /// Replaces a stream at the indicated index - bool set(int index, Stream &in) { - if (index < size()) { - streams[index] = ∈ - return true; - } else { - LOGE("Invalid index %d - max is %d", index, size() - 1); - return false; - } - } - - virtual bool begin(AudioInfo info) { - setAudioInfo(info); - frame_size = info.bits_per_sample / 8 * info.channels; - LOGI("frame_size: %d", frame_size); - return frame_size > 0; - } - - /// Dynamically update the new weight for the indicated channel: If you set it - /// to 0 it is muted (and the stream is not read any more). We recommend to - /// use values between 1 and 100 - void setWeight(int index, int weight) { - if (index < streams.size()) { - weights[index] = weight; - recalculateWeights(); - } else { - LOGE("Invalid index %d - max is %d", index, size() - 1); - } - } - - /// Remove all input streams - void end() override { - streams.clear(); - weights.clear(); - result_vect.clear(); - current_vect.clear(); - total_weights = 0.0; - } - - /// Number of stremams to which are mixed together - int size() { return streams.size(); } - - /// Provides the data from all streams mixed together - size_t readBytes(uint8_t *data, size_t len) override { - if (total_weights == 0 || frame_size == 0 || len == 0) { - LOGW("readBytes: %d", (int)len); - return 0; - } - - if (limit_available_data) { - len = min((int)len, availableBytes()); - } - - int result_len = 0; - - if (len > 0) { - // result_len must be full frames - result_len = len * frame_size / frame_size; - // replace sample based with vector based implementation - // readBytesSamples((T*)data, result_len)); - result_len = readBytesVector((T *)data, result_len); - } - return result_len; - } - - /// Limit the copy to the available data of all streams: stops to provide data - /// when any stream has ended - void setLimitToAvailableData(bool flag) { limit_available_data = flag; } - - /// Defines the maximum number of retrys to get data from an input before we - /// abort the read and provide empty data - void setRetryCount(int retry) { retry_count = retry; } - - /// Removes a stream by index position - bool remove(int idx){ - if (idx < 0 || idx >= size()) { - return false; - } - streams.erase(idx); - weights.erase(idx); - recalculateWeights(); - return true; - } - - /// Removes all streams which have no data available - bool remove() { - bool rc = false; - int idx = nextEmptyIndex(); - while (idx >= 0) { - rc = true; - streams.erase(idx); - weights.erase(idx); - idx = nextEmptyIndex(); - } - recalculateWeights(); - return rc; - } - - /// Provides the actual index of the stream - int indexOf(Stream &stream) { return streams.indexOf(&stream); } - - /// Provides the stream pointer at the indicated index - Stream *operator[](int idx) { - if (idx < 0 || idx >= size()) return nullptr; - return streams[idx]; - } - - /// Provides you the index of the next empty stream. -1 when none is found. - int nextEmptyIndex() { - for (int i = 0; i < streams.size(); i++) { - if (streams[i]->available() == 0) { - return i; - } - } - return -1; - } - - protected: - Vector streams{0}; - Vector weights{0}; - int total_weights = 0; - int frame_size = 4; - bool limit_available_data = false; - int retry_count = 5; - Vector result_vect; - Vector current_vect; - - /// Recalculate the weights - void recalculateWeights() { - int total = 0; - for (int j = 0; j < weights.size(); j++) { - total += weights[j]; - } - total_weights = total; - } - - /// mixing using a vector of samples - int readBytesVector(T *p_data, int byteCount) { - int samples = byteCount / sizeof(T); - result_vect.resize(samples); - current_vect.resize(samples); - int stream_count = size(); - resultClear(); - int samples_eff_max = 0; - for (int j = 0; j < stream_count; j++) { - if (weights[j] > 0) { - int samples_eff = - readSamples(streams[j], current_vect.data(), samples, retry_count); - if (samples_eff > samples_eff_max) samples_eff_max = samples_eff; - // if all weights are 0.0 we stop to output - float factor = total_weights == 0.0f - ? 0.0f - : static_cast(weights[j]) / total_weights; - resultAdd(factor); - } - } - // copy result - for (int j = 0; j < samples; j++) { - p_data[j] = result_vect[j]; - } - return samples_eff_max * sizeof(T); - } - - /// Provides the available bytes from the first stream with data - int availableBytes() { - int result = DEFAULT_BUFFER_SIZE; - for (int j = 0; j < size(); j++) { - result = min(result, streams[j]->available()); - } - return result; - } - - void resultAdd(float fact) { - for (int j = 0; j < current_vect.size(); j++) { - current_vect[j] *= fact; - result_vect[j] += current_vect[j]; - } - } - - void resultClear() { - memset(result_vect.data(), 0, sizeof(int) * result_vect.size()); - } -}; - -/** - * @brief Merges multiple input streams. - * So if you provide 2 mono channels you get a stereo signal as result - * with the left channel from channel 0 and the right from channel 1 - * @ingroup transform - * @author Phil Schatzmann - * @copyright GPLv3 - * @tparam T - */ -template -class InputMerge : public AudioStream { - public: - /// Default constructor - InputMerge() = default; - - /// @brief Constructor for stereo signal from to mono input stream - /// @param left - /// @param right - InputMerge(Stream &left, Stream &right) { - add(left, 1); - add(right, 1); - }; - - /// Provides the audio info with the total channel count - AudioInfo audioInfo() override { - AudioInfo info = AudioStream::audioInfo(); - info.channels = total_channel_count; - return info; - } - - virtual bool begin(AudioInfo info) { - setAudioInfo(info); - return begin(); - } - - virtual bool begin() override { - // make sure that we use the correct channel count - info.channels = channelCount(); - return AudioStream::begin(); - } - - /// Provides the data from all streams mixed together - size_t readBytes(uint8_t *data, size_t len) override { - LOGD("readBytes: %d", (int)len); - T *p_data = (T *)data; - int result_len = MIN(available(), len); - int frames = result_len / (sizeof(T) * total_channel_count); - int result_idx = 0; - for (int j = 0; j < frames; j++) { - for (int i = 0; i < records.size(); i++) { - for (int ch = 0; ch < records[i].channels; ch++) { - p_data[result_idx++] = - records[i].weight * readSample(records[i].stream); - } - } - } - return result_idx * sizeof(T); - } - - /// Adds a new input stream with 1 channel - void add(Stream &in, int channelCount, float weight = 1.0) { - MergeRecord rec(&in, channelCount, weight); - records.push_back(rec); - total_channel_count += channelCount; - } - - /// Defines a new weight for the indicated channel: If you set it to 0 it is - /// muted. - void setWeight(int channel, float weight) { - if (channel < channelCount()) { - records[channel].weight = weight; - } else { - LOGE("Invalid channel %d - max is %d", channel, channelCount() - 1); - } - } - - /// Remove all input streams - void end() override { records.clear(); } - - /// Number of channels to which are mixed together = number of result channels - int channelCount() { return total_channel_count; } - - /// Provides the min available data from all streams - int available() override { - int result = records[0].stream->available(); - for (int j = 1; j < channelCount(); j++) { - int tmp = records[j].stream->available(); - if (tmp < result) { - result = tmp; - } - } - return result; - } - - protected: - struct MergeRecord { - Stream *stream = nullptr; - int channels = 0; - float weight = 1.0; - MergeRecord() = default; - MergeRecord(Stream *str, int ch, float w) { - stream = str; - channels = ch; - weight = w; - } - }; - Vector records; - int total_channel_count = 0; -}; - -/** - * @brief CallbackStream: A Stream that allows to register callback methods for - * accessing and providing data. The callbacks can be lambda expressions. - * Warning: this class does not propagate audio info changes to the target - * stream. You need to do this manually. - * @ingroup io - * @author Phil Schatzmann - * @copyright GPLv3 - */ -class CallbackStream : public ModifyingStream { - public: - CallbackStream() = default; - - /// Allows to change the audio before sending it to the output or before - /// getting it from the original input - CallbackStream(Stream &io, size_t (*cb_update)(uint8_t *data, size_t len)) { - p_stream = &io; - p_out = &io; - setUpdateCallback(cb_update); - } - - /// Allows to change the audio before sending it to the output - CallbackStream(Print &out, size_t (*cb_update)(uint8_t *data, size_t len)) { - p_out = &out; - setUpdateCallback(cb_update); - } - - CallbackStream(size_t (*cb_read)(uint8_t *data, size_t len), - size_t (*cb_write)(const uint8_t *data, size_t len)) { - setWriteCallback(cb_write); - setReadCallback(cb_read); - } - - void setWriteCallback(size_t (*cb_write)(const uint8_t *data, size_t len)) { - this->cb_write = cb_write; - } - - void setReadCallback(size_t (*cb_read)(uint8_t *data, size_t len)) { - this->cb_read = cb_read; - } - - void setUpdateCallback(size_t (*cb_update)(uint8_t *data, size_t len)) { - this->cb_update = cb_update; - } - - // callback result negative -> no change; callbeack result >=0 provides the - // result - void setAvailableCallback(int (*cb)()) { this->cb_available = cb; } - - /// defines the callback to receive the actual audio info - void setAudioInfoCallback(void (*cb)(AudioInfo info)) { - this->cb_audio_info = cb; - } - - /// Updates the audio info and calls the callback - void setAudioInfo(AudioInfo info) override { - ModifyingStream::setAudioInfo(info); - if (cb_audio_info != nullptr) { - cb_audio_info(info); - } - } - - virtual bool begin(AudioInfo info) { - setAudioInfo(info); - return begin(); - } - virtual bool begin() override { - active = true; - return true; - } - - void end() override { active = false; } - - int available() override { - int result = AudioStream::available(); - // determine value from opional variable - if (available_bytes >= 0) return available_bytes; - // check if there is a callback - if (cb_available == nullptr) return result; - // determine value from callback - int tmp_available = cb_available(); - if (tmp_available < 0) return result; - - return tmp_available; - } - - size_t readBytes(uint8_t *data, size_t len) override { - if (!active) return 0; - // provide data from callback - if (cb_read) { - return cb_read(data, len); - } - // provide data from source - size_t result = 0; - if (p_stream) { - result = p_stream->readBytes(data, len); - } - if (cb_update) { - result = cb_update(data, result); - } - return result; - } - - size_t write(const uint8_t *data, size_t len) override { - if (!active) return 0; - // write to callback - if (cb_write) { - return cb_write(data, len); - } - // write to output - if (p_out) { - size_t result = len; - if (cb_update) { - result = cb_update((uint8_t *)data, len); - } - return p_out->write(data, result); - } - // no processing possible - return 0; - } - - /// Defines/Changes the input & output - void setStream(Stream &in) override { - p_stream = ∈ - p_out = ∈ - } - - /// Defines/Changes the output target - void setOutput(Print &out) override { p_out = &out; } - - /// same as setStream - void setOutput(Stream &in) { - p_stream = ∈ - p_out = ∈ - } - - /// same as set Output - void setStream(Print &out) { p_out = &out; } - - /// optioinally define available bytes for next read - void setAvailable(int val) { available_bytes = val; } - - protected: - bool active = true; - size_t (*cb_write)(const uint8_t *data, size_t len) = nullptr; - size_t (*cb_read)(uint8_t *data, size_t len) = nullptr; - size_t (*cb_update)(uint8_t *data, size_t len) = nullptr; - void (*cb_audio_info)(AudioInfo info) = nullptr; - int (*cb_available)() = nullptr; - Stream *p_stream = nullptr; - Print *p_out = nullptr; - int available_bytes = -1; -}; - -/** - * @brief Stream to which we can apply Filters for each channel. The filter - * might change the result size! - * @ingroup transform - * @author Phil Schatzmann - * @copyright GPLv3 - */ -template -class FilteredStream : public ModifyingStream { - public: - FilteredStream() = default; - FilteredStream(Stream &stream) : ModifyingStream() { setStream(stream); } - FilteredStream(Stream &stream, int channels) : ModifyingStream() { - this->channels = channels; - setStream(stream); - p_converter = new ConverterNChannels(channels); - } - FilteredStream(Print &stream) : ModifyingStream() { setOutput(stream); } - FilteredStream(Print &stream, int channels) : ModifyingStream() { - this->channels = channels; - setOutput(stream); - p_converter = new ConverterNChannels(channels); - } - - virtual ~FilteredStream() { end(); } - - void setStream(Stream &stream) override { - p_stream = &stream; - p_print = &stream; - } - - void setOutput(Print &stream) override { p_print = &stream; } - - bool begin(AudioInfo info) { - setAudioInfo(info); - this->channels = info.channels; - if (p_converter != nullptr && p_converter->getChannels() != channels) { - LOGE("Inconsistent number of channels"); - return false; - } - return begin(); - } - - bool begin() override { - if (channels == 0) { - LOGE("channels must not be 0"); - return false; - } - if (p_converter == nullptr) { - p_converter = new ConverterNChannels(channels); - } - return AudioStream::begin(); - } - - void end() override { - ModifyingStream::end(); - if (p_converter != nullptr) { - delete p_converter; - p_converter = nullptr; - } - } - - virtual size_t write(const uint8_t *data, size_t len) override { - if (p_converter == nullptr) return 0; - size_t result = p_converter->convert((uint8_t *)data, len); - return p_print->write(data, result); - } - - size_t readBytes(uint8_t *data, size_t len) override { - if (p_converter == nullptr) return 0; - if (p_stream == nullptr) return 0; - size_t result = p_stream->readBytes(data, len); - result = p_converter->convert(data, result); - return result; - } - - virtual int available() override { - if (p_stream == nullptr) return 0; - return p_stream->available(); - } - - virtual int availableForWrite() override { - return p_print->availableForWrite(); - } - - /// defines the filter for an individual channel - the first channel is 0. The - /// number of channels must have been defined before we can call this - /// function. - void setFilter(int channel, Filter *filter) { - if (p_converter != nullptr) { - p_converter->setFilter(channel, filter); - } else { - LOGE("p_converter is null"); - } - } - - /// defines the filter for an individual channel - the first channel is 0. The - /// number of channels must have been defined before we can call this - /// function. - void setFilter(int channel, Filter &filter) { - setFilter(channel, &filter); - } - - protected: - int channels = 0; - Stream *p_stream = nullptr; - Print *p_print = nullptr; - ConverterNChannels *p_converter = nullptr; -}; - -/** - * @brief A simple class to determine the volume. You can use it as - * final output or as output or input in your audio chain. - * @ingroup io - * @ingroup volume - * @author Phil Schatzmann - * @copyright GPLv3 - */ -class VolumeMeter : public ModifyingStream { - public: - VolumeMeter() = default; - VolumeMeter(AudioStream &as) { - addNotifyAudioChange(as); - setStream(as); - } - VolumeMeter(AudioOutput &ao) { - addNotifyAudioChange(ao); - setOutput(ao); - } - VolumeMeter(Print &print) { setOutput(print); } - VolumeMeter(Stream &stream) { setStream(stream); } - - bool begin(AudioInfo info) { - setAudioInfo(info); - return begin(); - } - - bool begin() override { - setAudioInfo(audioInfo()); - return true; - } - - void setAudioInfo(AudioInfo info) override { - int channels = info.channels; - LOGI("VolumeMeter::setAudioInfo: channels %d", channels); - ModifyingStream::setAudioInfo(info); - if (channels > 0) { - volumes.resize(channels); - volumes_tmp.resize(channels); - sum.resize(channels); - sum_tmp.resize(channels); - } - } - - size_t write(const uint8_t *data, size_t len) override { - updateVolumes(data, len); - size_t result = len; - if (p_out != nullptr) { - result = p_out->write(data, len); - } - return result; - } - - size_t readBytes(uint8_t *data, size_t len) override { - if (p_stream == nullptr) return 0; - size_t result = p_stream->readBytes(data, len); - updateVolumes((const uint8_t *)data, len); - return result; - } - - /// Determines the volume (max amplitude). The range depends on the - /// bits_per_sample. - float volume() { return f_volume; } - - /// Determines the volume for the indicated channel. You must call the begin - /// method to define the number of channels - float volume(int channel) { - if (volumes.size() == 0) { - LOGE("begin not called!"); - return 0.0f; - } - if (channel >= volumes.size()) { - LOGE("invalid channel %d", channel); - return 0.0f; - } - return volumes[channel]; - } - - /// Volume Ratio: max amplitude is 1.0 - float volumeRatio() { - return volume() / NumberConverter::maxValue(info.bits_per_sample); - } - - /// Volume Ratio of indicated channel: max amplitude is 1.0 - float volumeRatio(int channel) { - return volume(channel) / NumberConverter::maxValue(info.bits_per_sample); - } - - /// Volume in db: max amplitude is 0 (range: -1000 to 0) - float volumeDB() { - // prevent infinite value - if (volumeRatio() == 0) return -1000; - return 20.0f * log10(volumeRatio()); - } - - /// Volume of indicated channel in db: max amplitude is 0 - float volumeDB(int channel) { - // prevent infinite value - if (volumeRatio(channel) == 0) return -1000; - return 20.0f * log10(volumeRatio(channel)); - } - - /// Volume in %: max amplitude is 100 - float volumePercent() { return 100.0f * volumeRatio(); } - - /// Volume of indicated channel in %: max amplitude is 100 - float volumePercent(int channel) { return 100.0f * volumeRatio(channel); } - - /// Average volume of all channels - float volumeAvg() { - float total = 0; - size_t count = 0; - for (int j = 0; j < info.channels; j++) { - total += sum[j]; - count += sample_count_per_channel; - } - return total / count; - } - - /// Average volume of indicated channel - float volumeAvg(int channel) { - return sum[channel] / sample_count_per_channel; - } - - /// Resets the actual volume - void clear() { - f_volume_tmp = 0; - for (int j = 0; j < info.channels; j++) { - volumes_tmp[j] = 0; - sum_tmp[j] = 0; - } - } - - void setOutput(Print &out) override { p_out = &out; } - void setStream(Stream &io) override { - p_out = &io; - p_stream = &io; - } - - protected: - float f_volume_tmp = 0; - float f_volume = 0; - Vector volumes{0}; - Vector volumes_tmp{0}; - Vector sum{0}; - Vector sum_tmp{0}; - Print *p_out = nullptr; - Stream *p_stream = nullptr; - size_t sample_count_per_channel = 0; - - void updateVolumes(const uint8_t *data, size_t len) { - if (data == nullptr || len == 0) return; - clear(); - switch (info.bits_per_sample) { - case 8: - updateVolumesT(data, len); - break; - case 16: - updateVolumesT(data, len); - break; - case 24: - updateVolumesT(data, len); - break; - case 32: - updateVolumesT(data, len); - break; - default: - LOGE("Unsupported bits_per_sample: %d", info.bits_per_sample); - break; - } - } - - template - void updateVolumesT(const uint8_t *buffer, size_t size) { - T *bufferT = (T *)buffer; - int samplesCount = size / sizeof(T); - sample_count_per_channel = samplesCount / info.channels; - for (int j = 0; j < samplesCount; j++) { - float tmp = abs(static_cast(bufferT[j])); - updateVolume(tmp, j); - } - commit(); - } - - void updateVolume(float tmp, int j) { - if (tmp > f_volume_tmp) { - f_volume_tmp = tmp; - } - if (volumes_tmp.size() > 0 && info.channels > 0) { - int ch = j % info.channels; - if (tmp > volumes_tmp[ch]) { - volumes_tmp[ch] = tmp; - sum_tmp[ch] = tmp; - } - } - } - - void commit() { - f_volume = f_volume_tmp; - for (int j = 0; j < info.channels; j++) { - volumes[j] = volumes_tmp[j]; - sum[j] = sum_tmp[j]; - } - } -}; - -// legacy names -using VolumePrint = VolumeMeter; -using VolumeOutput = VolumeMeter; - -#ifdef USE_TIMER -/** - * @brief TimerCallbackAudioStream Configuration - * @author Phil Schatzmann - * @copyright GPLv3 - */ -struct TimerCallbackAudioStreamInfo : public AudioInfo { - RxTxMode rx_tx_mode = RX_MODE; - uint16_t buffer_size = DEFAULT_BUFFER_SIZE; - bool use_timer = true; - int timer_id = -1; - TimerFunction timer_function = DirectTimerCallback; - bool adapt_sample_rate = false; - uint16_t (*callback)(uint8_t *data, uint16_t len) = nullptr; -}; - -// forward declaration: relevant only if use_timer == true -static void timerCallback(void *obj); -/** - * @brief Callback driven Audio Source (rx_tx_mode==RX_MODE) or Audio Sink - * (rx_tx_mode==TX_MODE). This class allows to to integrate external libraries - * in order to consume or generate a data stream which is based on a timer - * @ingroup io - * @author Phil Schatzmann - * @copyright GPLv3 - * - */ -class TimerCallbackAudioStream : public BufferedStream { - friend void timerCallback(void *obj); - - public: - TimerCallbackAudioStream() : BufferedStream(80) { TRACED(); } - - ~TimerCallbackAudioStream() { - TRACED(); - if (timer != nullptr) delete timer; - if (buffer != nullptr) delete buffer; - if (frame != nullptr) delete[] frame; - } - - /// Provides the default configuration - TimerCallbackAudioStreamInfo defaultConfig() { - TimerCallbackAudioStreamInfo def; - return def; - } - - /// updates the audio information - virtual void setAudioInfo(AudioInfo info) { - TRACED(); - if (cfg.sample_rate != info.sample_rate || cfg.channels != info.channels || - cfg.bits_per_sample != info.bits_per_sample) { - bool do_restart = active; - if (do_restart) end(); - cfg.sample_rate = info.sample_rate; - cfg.channels = info.channels; - cfg.bits_per_sample = info.bits_per_sample; - if (do_restart) begin(cfg); - } - } - - /// Provides the current audio information - TimerCallbackAudioStreamInfo audioInfoExt() { return cfg; } - AudioInfo audioInfo() { return cfg; } - - void begin(TimerCallbackAudioStreamInfo config) { - LOGD("%s: %s", LOG_METHOD, - config.rx_tx_mode == RX_MODE ? "RX_MODE" : "TX_MODE"); - this->cfg = config; - this->frameCallback = config.callback; - if (cfg.use_timer) { - frameSize = cfg.bits_per_sample * cfg.channels / 8; - frame = new uint8_t[frameSize]; - buffer = new RingBuffer(cfg.buffer_size); - timer = new TimerAlarmRepeating(); - timer->setTimerFunction(cfg.timer_function); - if (cfg.timer_id >= 0) { - timer->setTimer(cfg.timer_id); - } - time = AudioTime::toTimeUs(cfg.sample_rate); - LOGI("sample_rate: %u -> time: %u milliseconds", - (unsigned int)cfg.sample_rate, (unsigned int)time); - timer->setCallbackParameter(this); - timer->begin(timerCallback, time, TimeUnit::US); - } - - notifyAudioChange(cfg); - active = true; - } - - /// Restart the processing - bool begin() { - TRACED(); - if (this->frameCallback != nullptr) { - if (cfg.use_timer) { - timer->begin(timerCallback, time, TimeUnit::US); - } - active = true; - } - return active; - } - - /// Stops the processing - void end() { - TRACED(); - if (cfg.use_timer) { - timer->end(); - } - active = false; - } - - /// Provides the effective sample rate - uint16_t currentSampleRate() { return currentRateValue; } - - protected: - TimerCallbackAudioStreamInfo cfg; - bool active = false; - uint16_t (*frameCallback)(uint8_t *data, uint16_t len); - // below only relevant with timer - TimerAlarmRepeating *timer = nullptr; - RingBuffer *buffer = nullptr; - uint8_t *frame = nullptr; - uint16_t frameSize = 0; - uint32_t time = 0; - unsigned long lastTimestamp = 0u; - uint32_t currentRateValue = 0; - uint32_t printCount = 0; - - // used for audio sink - virtual size_t writeExt(const uint8_t *data, size_t len) override { - if (!active) return 0; - TRACED(); - size_t result = 0; - if (!cfg.use_timer) { - result = frameCallback((uint8_t *)data, len); - } else { - result = buffer->writeArray((uint8_t *)data, len); - } - if (++printCount % 10000 == 0) printSampleRate(); - return result; - } - - // used for audio source - virtual size_t readExt(uint8_t *data, size_t len) override { - if (!active) return 0; - TRACED(); - - size_t result = 0; - if (!cfg.use_timer) { - result = frameCallback(data, len); - } else { - result = buffer->readArray(data, len); - } - if (++printCount % 10000 == 0) printSampleRate(); - return result; - } - - /// calculates the effective sample rate - virtual void measureSampleRate() { - unsigned long ms = millis(); - if (lastTimestamp > 0u) { - uint32_t diff = ms - lastTimestamp; - if (diff > 0) { - uint16_t rate = 1 * 1000 / diff; - - if (currentRateValue == 0) { - currentRateValue = rate; - } else { - currentRateValue = (currentRateValue + rate) / 2; - } - } - } - lastTimestamp = ms; - } - - /// log and update effective sample rate - virtual void printSampleRate() { - LOGI("effective sample rate: %u", (unsigned int)currentRateValue); - if (cfg.adapt_sample_rate && - abs((int)currentRateValue - (int)cfg.sample_rate) > 200) { - cfg.sample_rate = currentRateValue; - notifyAudioChange(cfg); - } - } -}; - -// relevant only if use_timer == true -void IRAM_ATTR timerCallback(void *obj) { - TimerCallbackAudioStream *src = (TimerCallbackAudioStream *)obj; - if (src != nullptr) { - // LOGD("%s: %s", LOG_METHOD, src->cfg.rx_tx_mode==RX_MODE ? - // "RX_MODE":"TX_MODE"); - if (src->cfg.rx_tx_mode == RX_MODE) { - // input - uint16_t available_bytes = src->frameCallback(src->frame, src->frameSize); - uint16_t buffer_available = src->buffer->availableForWrite(); - if (buffer_available < available_bytes) { - // if buffer is full make space - uint16_t to_clear = available_bytes - buffer_available; - uint8_t tmp[to_clear]; - src->buffer->readArray(tmp, to_clear); - } - if (src->buffer->writeArray(src->frame, available_bytes) != - available_bytes) { - assert(false); - } - } else { - // output - if (src->buffer != nullptr && src->frame != nullptr && - src->frameSize > 0) { - uint16_t available_bytes = - src->buffer->readArray(src->frame, src->frameSize); - if (available_bytes != - src->frameCallback(src->frame, available_bytes)) { - LOGE("data underflow"); - } - } - } - src->measureSampleRate(); - } -} - -#endif - -} // namespace audio_tools diff --git a/src/AudioTools/CoreAudio/AudioTimer.h b/src/AudioTools/CoreAudio/AudioTimer.h deleted file mode 100644 index 740a4484b0..0000000000 --- a/src/AudioTools/CoreAudio/AudioTimer.h +++ /dev/null @@ -1,3 +0,0 @@ -#pragma once - -#include "AudioTools/CoreAudio/AudioTimer/AudioTimer.h" diff --git a/src/AudioTools/CoreAudio/AudioTimer/AudioTimer.h b/src/AudioTools/CoreAudio/AudioTimer/AudioTimer.h deleted file mode 100644 index 8d6650c004..0000000000 --- a/src/AudioTools/CoreAudio/AudioTimer/AudioTimer.h +++ /dev/null @@ -1,79 +0,0 @@ -#pragma once -/** - * @defgroup timer Timers - * @ingroup tools - * @brief Platform independent timer API - */ -#include "AudioToolsConfig.h" -#if defined(USE_TIMER) -#include "AudioTools/CoreAudio/AudioTimer/AudioTimerAVR.h" -#include "AudioTools/CoreAudio/AudioTimer/AudioTimerBase.h" -#include "AudioTools/CoreAudio/AudioTimer/AudioTimerESP32.h" -#include "AudioTools/CoreAudio/AudioTimer/AudioTimerESP8266.h" -#include "AudioTools/CoreAudio/AudioTimer/AudioTimerMBED.h" -#include "AudioTools/CoreAudio/AudioTimer/AudioTimerRP2040.h" -#include "AudioTools/CoreAudio/AudioTimer/AudioTimerRenesas.h" -#include "AudioTools/CoreAudio/AudioTimer/AudioTimerSTM32.h" -#include "AudioTools/CoreAudio/AudioLogger.h" - -namespace audio_tools { - -/** - * @brief Common Interface definition for TimerAlarmRepeating - * @ingroup timer - */ -class TimerAlarmRepeating { - public: - /// @brief Default constructor - TimerAlarmRepeating() = default; - /** - * @brief Construct a new Timer Alarm Repeating object by passing your object - * which has been customized with some special platform specific parameters - * @param timer - */ - TimerAlarmRepeating(TimerAlarmRepeatingDriverBase& timer) { - p_timer = &timer; - }; - virtual ~TimerAlarmRepeating() = default; - - bool begin(repeating_timer_callback_t callback_f, uint32_t time, - TimeUnit unit = MS) { - // stop timer if it is already active - if (is_active) end(); - // start timer - is_active = p_timer->begin(callback_f, time, unit); - return is_active; - } - bool end() { - is_active = false; - return p_timer->end(); - }; - - void setCallbackParameter(void* obj) { p_timer->setCallbackParameter(obj); } - - void* callbackParameter() { return p_timer->callbackParameter(); } - - virtual void setTimer(int timer) { p_timer->setTimer(timer); } - - virtual void setTimerFunction(TimerFunction function = DirectTimerCallback) { - p_timer->setTimerFunction(function); - } - - void setIsSave(bool is_save) { p_timer->setIsSave(is_save); } - - /// @brief Returns true if the timer is active - operator bool() { return is_active; } - - /// Provides access to the driver - TimerAlarmRepeatingDriverBase* driver() { return p_timer; } - - protected: - void* object = nullptr; - bool is_active = false; - TimerAlarmRepeatingDriver timer; // platform specific timer - TimerAlarmRepeatingDriverBase* p_timer = &timer; -}; - -} // namespace audio_tools - -#endif diff --git a/src/AudioTools/CoreAudio/AudioTimer/AudioTimerAVR.h b/src/AudioTools/CoreAudio/AudioTimer/AudioTimerAVR.h deleted file mode 100644 index 77740167de..0000000000 --- a/src/AudioTools/CoreAudio/AudioTimer/AudioTimerAVR.h +++ /dev/null @@ -1,98 +0,0 @@ -#pragma once - -#ifdef __AVR__ -#include "AudioTools/CoreAudio/AudioTimer/AudioTimerBase.h" - -namespace audio_tools { -typedef void (*repeating_timer_callback_t)(void* obj); -class TimerAlarmRepeatingDriverAVR; -static TimerAlarmRepeatingDriverAVR* timerAlarmRepeatingRef = nullptr; - -/** - * @brief Repeating Timer functions for repeated execution: Plaease use the - * typedef TimerAlarmRepeating - * @ingroup platform - * @author Phil Schatzmann - * @copyright GPLv3 - * - */ -class TimerAlarmRepeatingDriverAVR : public TimerAlarmRepeatingDriverBase { - public: - TimerAlarmRepeatingDriverAVR() { timerAlarmRepeatingRef = this; } - - /** - * Starts the alarm timer - */ - bool begin(repeating_timer_callback_t callback_f, uint32_t time, - TimeUnit unit = MS) override { - callback = callback_f; - - // we determine the time in microseconds - uint32_t timeUs = 0; - switch (unit) { - case MS: - timeUs = time * 1000; - break; - case US: - timeUs = time; - break; - case HZ: - // convert hz to time in us - timeUs = AudioTime::toTimeUs(time); - break; - } - // frequency = beats / second - setupTimer(1000000 / time); - return true; - } - - // ends the timer and if necessary the task - bool end() override { - TRACED(); - noInterrupts(); - // end timer callback - TCCR1B = 0; - interrupts(); // enable all interrupts - return true; - } - - static void tickerCallback() { - if (timerAlarmRepeatingRef != nullptr && - timerAlarmRepeatingRef->callback != nullptr) - timerAlarmRepeatingRef->callback(timerAlarmRepeatingRef); - } - - protected: - repeating_timer_callback_t callback = nullptr; - - void setupTimer(uint64_t sample_rate) { - TRACED(); - // CPU Frequency 16 MHz - // prescaler 1, 256 or 1024 => no prescaling - uint32_t steps = F_CPU / 8 / sample_rate; // e.g. (16000000/8/44100=>45) - if (steps > 65535) { - LOGE("requested sample rate not supported: %d - we use %d", sample_rate, - F_CPU / 65536); - steps = 65535; - } else { - LOGD("compare match register set to %d", steps); - } - - // setup timer intterupt - noInterrupts(); - TCCR1B = 0; - // compare match register - OCR1A = steps; - TCCR1B |= (1 << WGM12); // CTC mode - // TCCR1B |= (1 << CS10); // prescaler 1 - TCCR1B |= (1 << CS11); // prescaler 8 - TIMSK1 |= (1 << OCIE1A); // enable timer compare interrupt - interrupts(); // enable all interrupts - } -}; - -using TimerAlarmRepeatingDriver = TimerAlarmRepeatingDriverAVR; - -} // namespace audio_tools - -#endif \ No newline at end of file diff --git a/src/AudioTools/CoreAudio/AudioTimer/AudioTimerBase.h b/src/AudioTools/CoreAudio/AudioTimer/AudioTimerBase.h deleted file mode 100644 index ef698463f4..0000000000 --- a/src/AudioTools/CoreAudio/AudioTimer/AudioTimerBase.h +++ /dev/null @@ -1,49 +0,0 @@ -#pragma once -#include "AudioToolsConfig.h" -#ifdef USE_TIMER -#include "AudioTools/CoreAudio/AudioTypes.h" - -/** - * @defgroup timer Timer - * @ingroup tools - * @brief Timer - */ - -namespace audio_tools { - -typedef void (*repeating_timer_callback_t)(void* obj); - -enum TimerFunction { - DirectTimerCallback, - TimerCallbackInThread, - SimpleThreadLoop -}; - -class TimerAlarmRepeatingDriverBase { - public: - virtual ~TimerAlarmRepeatingDriverBase() { end(); } - - virtual bool begin(repeating_timer_callback_t callback_f, uint32_t time, - TimeUnit unit = MS) = 0; - virtual bool end() { return false; }; - - void setCallbackParameter(void* obj) { object = obj; } - - void* callbackParameter() { return object; } - - virtual void setTimer(int timer) {} - virtual void setTimerFunction(TimerFunction function = DirectTimerCallback) {} - /// @brief Not used - virtual void setIsSave(bool is_save) {} - - protected: - void* object = nullptr; - - const char* toString(TimeUnit unit){ - return TimeUnitStr[(int)unit]; - } -}; - -} // namespace audio_tools - -#endif diff --git a/src/AudioTools/CoreAudio/AudioTimer/AudioTimerESP32.h b/src/AudioTools/CoreAudio/AudioTimer/AudioTimerESP32.h deleted file mode 100644 index 841dfb3809..0000000000 --- a/src/AudioTools/CoreAudio/AudioTimer/AudioTimerESP32.h +++ /dev/null @@ -1,334 +0,0 @@ -#pragma once - -#if defined(ESP32) && defined(ARDUINO) -#include - -#include "AudioTools/CoreAudio/AudioTimer/AudioTimerBase.h" - -namespace audio_tools { - -/** - * @brief Internal class to manage User callbacks. An optinal parameter can be - * passed to the callback method We support 4 timers - * @author Phil Schatzmann - * @copyright GPLv3 - */ -class UserCallback { - public: - void setup(repeating_timer_callback_t my_callback, void *user_data, - bool lock) { - TRACED(); - this->my_callback = my_callback; - this->user_data = user_data; - this->lock = lock; // false when called from Task - } - - IRAM_ATTR void call() { - if (my_callback != nullptr) { - if (lock) portENTER_CRITICAL_ISR(&timerMux); - my_callback(user_data); - if (lock) portEXIT_CRITICAL_ISR(&timerMux); - } - } - - protected: - repeating_timer_callback_t my_callback = nullptr; - void *user_data = nullptr; - portMUX_TYPE timerMux = portMUX_INITIALIZER_UNLOCKED; - bool lock; - -} static *simpleUserCallback = nullptr; - -static IRAM_ATTR void userCallback0() { simpleUserCallback[0].call(); } -static IRAM_ATTR void userCallback1() { simpleUserCallback[1].call(); } -static IRAM_ATTR void userCallback2() { simpleUserCallback[2].call(); } -static IRAM_ATTR void userCallback3() { simpleUserCallback[3].call(); } - -/** - * @brief Internal class to manage the different timer callbacks for the 4 - * hardware timers - * @author Phil Schatzmann - * @copyright GPLv3 - */ -class TimerCallback { - public: - TimerCallback() { - TRACED(); - timerMux = portMUX_INITIALIZER_UNLOCKED; - p_handler_task = nullptr; - } - - void setup(TaskHandle_t handler_task) { - TRACED(); - p_handler_task = handler_task; - assert(handler_task != nullptr); - } - - IRAM_ATTR void call() { - if (p_handler_task != nullptr) { - // A mutex protects the handler from reentry (which shouldn't happen, but - // just in case) - portENTER_CRITICAL_ISR(&timerMux); - BaseType_t xHigherPriorityTaskWoken = pdFALSE; - vTaskNotifyGiveFromISR(p_handler_task, &xHigherPriorityTaskWoken); - if (xHigherPriorityTaskWoken) { - portYIELD_FROM_ISR(); - } - portEXIT_CRITICAL_ISR(&timerMux); - } - } - - protected: - portMUX_TYPE timerMux = portMUX_INITIALIZER_UNLOCKED; - TaskHandle_t p_handler_task = nullptr; - -} static *timerCallbackArray = nullptr; - -static IRAM_ATTR void timerCallback0() { timerCallbackArray[0].call(); } -static IRAM_ATTR void timerCallback1() { timerCallbackArray[1].call(); } -static IRAM_ATTR void timerCallback2() { timerCallbackArray[2].call(); } -static IRAM_ATTR void timerCallback3() { timerCallbackArray[3].call(); } - -/** - * @brief Repeating Timer functions for simple scheduling of repeated execution. - * The basic logic is taken from - * https://www.toptal.com/embedded/esp32-audio-sampling. Plaease use the typedef - * TimerAlarmRepeating. - * @ingroup platform - * @author Phil Schatzmann - * @copyright GPLv3 - * - */ -class TimerAlarmRepeatingDriverESP32 : public TimerAlarmRepeatingDriverBase { - public: - TimerAlarmRepeatingDriverESP32() { - setTimerFunction(DirectTimerCallback); - setTimer(0); - } - - TimerAlarmRepeatingDriverESP32(int timer, TimerFunction function) { - setTimerFunction(function); - setTimer(timer); - } - - void setTimer(int id) override { - if (id >= 0 && id < 4) { - this->timer_id = id; - handler_task = nullptr; - } else { - LOGE("Invalid timer id %d", timer_id); - } - } - - void setTimerFunction(TimerFunction function = DirectTimerCallback) override { - this->function = function; - } - - /// Starts the alarm timer - bool begin(repeating_timer_callback_t callback_f, uint32_t time, - TimeUnit unit = MS) override { - TRACED(); - - // we determine the time in microseconds - switch (unit) { - case MS: - timeUs = time * 1000; - break; - case US: - timeUs = time; - break; - case HZ: - // convert hz to time in us - timeUs = AudioTime::toTimeUs(time); - break; - } -#if ESP_IDF_VERSION < ESP_IDF_VERSION_VAL(5, 1, 0) - LOGI("Timer every: %u us", timeUs); - adc_timer = timerBegin(timer_id, 80, - true); // divider=80 -> 1000000 calls per second -#else - uint32_t freq = AudioTime::AudioTime::toRateUs(timeUs); - LOGI("Timer freq: %u hz", (unsigned)freq); - adc_timer = timerBegin(1000000); // divider=80 -> 1000000 calls per second -#endif - switch (function) { - case DirectTimerCallback: - setupDirectTimerCallback(callback_f); - break; - - case TimerCallbackInThread: - setupTimerCallbackInThread(callback_f); - break; - - case SimpleThreadLoop: - setupSimpleThreadLoop(callback_f); - break; - } - - started = true; - return true; - } - - /// ends the timer and if necessary the task - bool end() override { - TRACED(); - if (started) { - timerDetachInterrupt(adc_timer); - timerEnd(adc_timer); - if (handler_task != nullptr) { - vTaskDelete(handler_task); - handler_task = nullptr; - } - } - started = false; - return true; - } - - void setCore(int core) { this->core = core; } - - /// Selects the TimerFunction: default is DirectTimerCallback, when set to - /// save we use TimerCallbackInThread - void setIsSave(bool is_save) override { - setTimerFunction(is_save ? TimerCallbackInThread : DirectTimerCallback); - } - - protected: - int timer_id = 0; - volatile bool started = false; - TaskHandle_t handler_task = nullptr; - hw_timer_t *adc_timer = nullptr; // our timer - UserCallback user_callback; // for task - TimerFunction function; - int core = 1; - int priority = 1; // configMAX_PRIORITIES - 1; - uint32_t timeUs; - - /// call timerAttachInterrupt - void attach(hw_timer_t *timer, void (*cb)()) { -#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 1, 0) - timerAttachInterrupt(timer, cb); -#else - timerAttachInterrupt(timer, cb, false); -#endif - } - - /// direct timer callback - void setupDirectTimerCallback(repeating_timer_callback_t callback_f) { - TRACED(); - // We start the timer which executes the callbacks directly - if (simpleUserCallback == nullptr) { - simpleUserCallback = new UserCallback[4]; - } - simpleUserCallback[timer_id].setup(callback_f, object, true); - - if (timer_id == 0) - attach(adc_timer, userCallback0); - else if (timer_id == 1) - attach(adc_timer, userCallback1); - else if (timer_id == 2) - attach(adc_timer, userCallback2); - else if (timer_id == 3) - attach(adc_timer, userCallback3); - -#if ESP_IDF_VERSION < ESP_IDF_VERSION_VAL(5, 1, 0) - timerAlarmWrite(adc_timer, timeUs, true); - timerAlarmEnable(adc_timer); - -#else - // timerStart(adc_timer); - // timerWrite(adc_timer,timeUs); - timerAlarm(adc_timer, timeUs, true, 0); -#endif - } - - /// timer callback is notifiying task: supports functionality which can not be - /// called in an interrupt e.g. i2c - void setupTimerCallbackInThread(repeating_timer_callback_t callback_f) { - TRACED(); - // we start the timer which runs the callback in a seprate task - if (timerCallbackArray == nullptr) { - timerCallbackArray = new TimerCallback[4]; - } - - if (timer_id == 0) - attach(adc_timer, timerCallback0); - else if (timer_id == 1) - attach(adc_timer, timerCallback1); - else if (timer_id == 2) - attach(adc_timer, timerCallback2); - else if (timer_id == 3) - attach(adc_timer, timerCallback3); - - // we record the callback method and user data - user_callback.setup(callback_f, object, false); - // setup the timercallback - xTaskCreatePinnedToCore(complexTaskHandler, "TimerAlarmRepeatingTask", - configMINIMAL_STACK_SIZE + 10000, &user_callback, - priority, &handler_task, core); - LOGI("Task created on core %d", core); - timerCallbackArray[timer_id].setup(handler_task); - -#if ESP_IDF_VERSION < ESP_IDF_VERSION_VAL(5, 1, 0) - timerAlarmWrite(adc_timer, timeUs, true); - timerAlarmEnable(adc_timer); -#else - timerAlarm(adc_timer, timeUs, true, 0); // set time in us -#endif - } - - /// No timer - just a simple task loop - void setupSimpleThreadLoop(repeating_timer_callback_t callback_f) { - TRACED(); - user_callback.setup(callback_f, object, false); - xTaskCreatePinnedToCore(simpleTaskLoop, "TimerAlarmRepeatingTask", - configMINIMAL_STACK_SIZE + 10000, this, priority, - &handler_task, core); - LOGI("Task created on core %d", core); - } - - /// We can not do any I2C calls in the interrupt handler so we need to do this - /// in a separate task - static void complexTaskHandler(void *param) { - TRACEI(); - UserCallback *cb = (UserCallback *)param; - - while (true) { - // Sleep until the ISR gives us something to do - uint32_t thread_notification = - ulTaskNotifyTake(pdTRUE, pdMS_TO_TICKS(1000)); - // Do something complex and CPU-intensive - if (thread_notification) { - cb->call(); - } - delay(0); - } - } - - /// We can not do any I2C calls in the interrupt handler so we need to do this - /// in a separate task. - static void simpleTaskLoop(void *param) { - TRACEI(); - TimerAlarmRepeatingDriverESP32 *ta = - (TimerAlarmRepeatingDriverESP32 *)param; - - while (true) { - unsigned long end = micros() + ta->timeUs; - ta->user_callback.call(); - long waitUs = end - micros(); - long waitMs = waitUs / 1000; - waitUs = waitUs - (waitMs * 1000); - delay(waitMs); - waitUs = end - micros(); - if (waitUs > 0) { - delayMicroseconds(waitUs); - } - } - } -}; - -/// @brief use TimerAlarmRepeating! @ingroup timer_esp32 -using TimerAlarmRepeatingDriver = TimerAlarmRepeatingDriverESP32; - -} // namespace audio_tools - -#endif diff --git a/src/AudioTools/CoreAudio/AudioTimer/AudioTimerESP8266.h b/src/AudioTools/CoreAudio/AudioTimer/AudioTimerESP8266.h deleted file mode 100644 index fe1d86543d..0000000000 --- a/src/AudioTools/CoreAudio/AudioTimer/AudioTimerESP8266.h +++ /dev/null @@ -1,71 +0,0 @@ -#pragma once - -#ifdef ESP8266 -#include "AudioTools/CoreAudio/AudioTimer/AudioTimerBase.h" -#include "Ticker.h" - -namespace audio_tools { - -typedef void (*repeating_timer_callback_t)(void* obj); - -class TimerAlarmRepeatingDriverESP8266; - -/** - * @brief Repeating Timer functions for repeated execution: Plaease use the - * typedef TimerAlarmRepeating - * @ingroup platform - * @author Phil Schatzmann - * @copyright GPLv3 - * - */ -class TimerAlarmRepeatingDriverESP8266 : public TimerAlarmRepeatingDriverBase { - public: - /** - * We can not do any I2C calls in the interrupt handler so we need to do this - * in a separate task - */ - static void complexHandler(void* param) {} - - /** - * Starts the alarm timer - */ - bool begin(repeating_timer_callback_t callback_f, uint32_t time, - TimeUnit unit = MS) override { - uint32_t timeUs; - - // we determine the time in microseconds - switch (unit) { - case MS: - timeUs = time / 1000; - break; - case US: - timeUs = time; - break; - case HZ: - // convert hz to time in us - timeUs = AudioTime::toTimeUs(time); - break; - } - - ticker.attach_ms(timeUs / 1000, callback_f, (void*)this); - - return true; - } - - // ends the timer and if necessary the task - bool end() override { - ticker.detach(); - return true; - } - - protected: - void (*current_timer_callback)(); - Ticker ticker; -}; - -/// @brief use TimerAlarmRepeating! @ingroup timer_esp8266 -using TimerAlarmRepeatingDriver = TimerAlarmRepeatingDriverESP8266; - -} // namespace audio_tools - -#endif \ No newline at end of file diff --git a/src/AudioTools/CoreAudio/AudioTimer/AudioTimerMBED.h b/src/AudioTools/CoreAudio/AudioTimer/AudioTimerMBED.h deleted file mode 100644 index ea29274762..0000000000 --- a/src/AudioTools/CoreAudio/AudioTimer/AudioTimerMBED.h +++ /dev/null @@ -1,68 +0,0 @@ -#pragma once - -#if defined(ARDUINO_ARCH_MBED) -#include "AudioTools/CoreAudio/AudioTimer/AudioTimerBase.h" -#include "mbed.h" - -namespace audio_tools { - -class TimerAlarmRepeatingDriverMBED; -static TimerAlarmRepeatingDriverMBED *timerAlarmRepeating = nullptr; - -/** - * @brief Repeating Timer functions for repeated execution: Plaease use the - * typedef TimerAlarmRepeating - * @ingroup platform - * @author Phil Schatzmann - * @copyright GPLv3 - * - */ -class TimerAlarmRepeatingDriverMBED : public TimerAlarmRepeatingDriverBase { - public: - TimerAlarmRepeatingDriverMBED() { timerAlarmRepeating = this; } - - /** - * Starts the alarm timer - */ - bool begin(repeating_timer_callback_t callback_f, uint32_t time, - TimeUnit unit = MS) override { - callback = callback_f; - - // we determine the time in microseconds - switch (unit) { - case MS: - ticker.attach(tickerCallback, std::chrono::microseconds(time * 1000)); - break; - case US: - ticker.attach(tickerCallback, std::chrono::microseconds(time)); - break; - case HZ: - // convert hz to time in us - uint64_t time_us = AudioTime::toTimeUs(time); - ticker.attach(tickerCallback, std::chrono::microseconds(time_us)); - break; - } - return true; - } - - // ends the timer and if necessary the task - bool end() { - ticker.detach(); - return true; - } - - protected: - mbed::Ticker ticker; - repeating_timer_callback_t callback; - - inline static void tickerCallback() { - timerAlarmRepeating->callback(timerAlarmRepeating->object); - } -}; - -/// @brief use TimerAlarmRepeating! @ingroup timer_mbed -using TimerAlarmRepeatingDriver = TimerAlarmRepeatingDriverMBED; - -} // namespace audio_tools - -#endif \ No newline at end of file diff --git a/src/AudioTools/CoreAudio/AudioTimer/AudioTimerRP2040.h b/src/AudioTools/CoreAudio/AudioTimer/AudioTimerRP2040.h deleted file mode 100644 index 3bf5b46c39..0000000000 --- a/src/AudioTools/CoreAudio/AudioTimer/AudioTimerRP2040.h +++ /dev/null @@ -1,82 +0,0 @@ -#pragma once - -#if defined(ARDUINO_ARCH_RP2040) && !defined(ARDUINO_ARCH_MBED) -#include - -#include - -#include "AudioTools/CoreAudio/AudioTimer/AudioTimerBase.h" -#include "hardware/timer.h" -#include "pico/time.h" - -namespace audio_tools { - -typedef void (*my_repeating_timer_callback_t)(void *obj); - -/** - * @brief Repeating Timer functions for repeated execution: Plaease use the - * typedef TimerAlarmRepeating - * @ingroup platform - * @author Phil Schatzmann - * @copyright GPLv3 - * - */ -class TimerAlarmRepeatingDriverRP2040 : public TimerAlarmRepeatingDriverBase { - public: - TimerAlarmRepeatingDriverRP2040() { - alarm_pool_init_default(); - ap = alarm_pool_get_default(); - } - - /** - * Starts the alarm timer - */ - bool begin(const my_repeating_timer_callback_t callback_f, uint32_t time, - TimeUnit unit = MS) override { - bool result = false; - LOGI("timer time: %u %s", (unsigned int)time, toString(unit)); - this->instanceCallback = callback_f; - - // we determine the time in microseconds - switch (unit) { - case MS: - result = alarm_pool_add_repeating_timer_ms(ap, time, &staticCallback, - this, &timer); - break; - case US: - result = alarm_pool_add_repeating_timer_us(ap, time, &staticCallback, - this, &timer); - break; - case HZ: - // convert hz to time in us - uint64_t time_us = AudioTime::toTimeUs(time); - result = alarm_pool_add_repeating_timer_us(ap, time_us, &staticCallback, - this, &timer); - break; - } - - return result; - } - - inline static bool staticCallback(repeating_timer *ptr) { - TimerAlarmRepeatingDriverRP2040 *self = - (TimerAlarmRepeatingDriverRP2040 *)ptr->user_data; - self->instanceCallback(self->object); - return true; - } - - // ends the timer and if necessary the task - bool end() { return cancel_repeating_timer(&timer); } - - protected: - alarm_pool_t *ap = nullptr; - repeating_timer_t timer; - my_repeating_timer_callback_t instanceCallback = nullptr; -}; - -/// @brief use TimerAlarmRepeating! @ingroup timer_rp2040 -using TimerAlarmRepeatingDriver = TimerAlarmRepeatingDriverRP2040; - -} // namespace audio_tools - -#endif \ No newline at end of file diff --git a/src/AudioTools/CoreAudio/AudioTimer/AudioTimerRenesas.h b/src/AudioTools/CoreAudio/AudioTimer/AudioTimerRenesas.h deleted file mode 100644 index 92bf075653..0000000000 --- a/src/AudioTools/CoreAudio/AudioTimer/AudioTimerRenesas.h +++ /dev/null @@ -1,147 +0,0 @@ -#pragma once - -#if defined(ARDUINO_ARCH_RENESAS) -#include "AudioTools/CoreAudio/AudioTimer/AudioTimerBase.h" -#include "FspTimer.h" -#include "IRQManager.h" - -namespace audio_tools { - -typedef void (*my_repeating_timer_callback_t)(void *obj); - -/** - * @brief Repeating Timer functions for repeated execution: Plaease use the - * typedef TimerAlarmRepeating. By default we use a new GPT timer. You can - * also request 1 AGT timer by calling setTimer(1); - * @ingroup platform - * @author Phil Schatzmann - * @copyright GPLv3 - * - */ -class TimerAlarmRepeatingDriverRenesas : public TimerAlarmRepeatingDriverBase { - public: - TimerAlarmRepeatingDriverRenesas() {} - - /** - * Starts the alarm timer - */ - bool begin(const my_repeating_timer_callback_t callback_f, uint32_t time, - TimeUnit unit = MS) override { - bool result = false; - LOGI("timer time: %u %s", (unsigned int)time, TimeUnitStr[unit]); - this->instanceCallback = callback_f; - - float rate; - // we determine the time in microseconds - switch (unit) { - case MS: - rate = AudioTime::toRateMs(time); - break; - case US: - rate = AudioTime::toRateUs(time); - break; - case HZ: - rate = time; - break; - default: - LOGE("Undefined Unit"); - return false; - } - if (rate < 550 || rate > 100000) { - LOGE("Unsupported rate: %f hz", rate); - return false; - } else { - LOGI("rate is %f hz", rate); - } - - // stop timer if it is active - if (timer_active) { - end(); - } - - LOGI("Using %s", timer_type == 1 ? "AGT" : "GPT") - timer_active = timer_type == 1 ? startAGTTimer(rate) : startGPTTimer(rate); - return timer_active; - } - - inline static void staticCallback(timer_callback_args_t *ptr) { - TimerAlarmRepeatingDriverRenesas *self = - (TimerAlarmRepeatingDriverRenesas *)ptr->p_context; - self->instanceCallback(self->object); - } - - /// ends the timer and if necessary the task - bool end() { - TRACED(); - audio_timer.end(); - timer_active = false; - return true; - } - - /// Selects the timer type: 0=GPT and 1=AGT - void setTimer(int timer) override { timer_type = timer; } - - protected: - FspTimer audio_timer; - my_repeating_timer_callback_t instanceCallback = nullptr; - uint8_t timer_type = 0; // Should be 0 - but this is currently not working - bool timer_active = false; - - // starts Asynchronous General Purpose Timer (AGT) timer - bool startAGTTimer(float rate) { - TRACED(); - uint8_t timer_type = AGT_TIMER; - // only channel 1 is available - int timer_channel = 1; - audio_timer.begin(TIMER_MODE_PERIODIC, timer_type, timer_channel, - rate * 2.0, 0.0f, staticCallback, this); - IRQManager::getInstance().addPeripheral(IRQ_AGT, audio_timer.get_cfg()); - audio_timer.open(); - bool result = audio_timer.start(); - return result; - } - - // setup General PWM Timer (GPT timer - bool startGPTTimer(float rate) { - TRACED(); - uint8_t timer_type = GPT_TIMER; - int8_t tindex = FspTimer::get_available_timer(timer_type); - if (tindex < 0) { - LOGE("Using pwm reserved timer"); - tindex = FspTimer::get_available_timer(timer_type, true); - } - if (tindex < 0) { - LOGE("no timer"); - return false; - } - FspTimer::force_use_of_pwm_reserved_timer(); - LOGI("timer idx: %d", tindex); - if (!audio_timer.begin(TIMER_MODE_PERIODIC, timer_type, tindex, rate, 0.0f, - staticCallback, this)) { - LOGE("error:begin"); - return false; - } - - if (!audio_timer.setup_overflow_irq()) { - LOGE("error:setup_overflow_irq"); - return false; - } - - if (!audio_timer.open()) { - LOGE("error:open"); - return false; - } - if (!audio_timer.start()) { - LOGE("error:start"); - return false; - } - return true; - } -}; - -/// @brief use TimerAlarmRepeating! @ingroup timer_rp2040 -using TimerAlarmRepeatingDriver = TimerAlarmRepeatingDriverRenesas; - -} // namespace audio_tools - -#endif \ No newline at end of file diff --git a/src/AudioTools/CoreAudio/AudioTimer/AudioTimerSTM32.h b/src/AudioTools/CoreAudio/AudioTimer/AudioTimerSTM32.h deleted file mode 100644 index 5b5c10edc1..0000000000 --- a/src/AudioTools/CoreAudio/AudioTimer/AudioTimerSTM32.h +++ /dev/null @@ -1,90 +0,0 @@ -#pragma once - -#if defined(STM32) -#include "AudioTools/CoreAudio/AudioTimer/AudioTimerBase.h" - -namespace audio_tools { - -class TimerAlarmRepeatingDriverSTM32; -static TimerAlarmRepeatingDriverSTM32 *timerAlarmRepeating = nullptr; -typedef void (*repeating_timer_callback_t)(void *obj); - -/** - * @brief STM32 Repeating Timer functions for repeated execution: Please use - * the typedef TimerAlarmRepeating. - * By default the TIM1 is used. - * @ingroup platform - * @author Phil Schatzmann - * @copyright GPLv3 - */ -class TimerAlarmRepeatingDriverSTM32 : public TimerAlarmRepeatingDriverBase { - public: - TimerAlarmRepeatingDriverSTM32() { setTimer(1); } - - ~TimerAlarmRepeatingDriverSTM32() { - end(); - delete this->timer; - } - /// selects the timer: 0 = TIM1, 1 = TIM2,2 = TIM3, 3 = TIM4, 4 = TIM5 - void setTimer(int timerIdx) override { - setTimer(timers[timerIdx]); - timer_index = timerIdx; - } - - /// select the timer - void setTimer(TIM_TypeDef *timerDef) { - if (this->timer != nullptr) { - delete this->timer; - } - this->timer = new HardwareTimer(timerDef); - timer_index = -1; - timer->pause(); - } - - - /** - * Starts the alarm timer - */ - bool begin(repeating_timer_callback_t callback_f, uint32_t time, - TimeUnit unit = MS) override { - TRACEI(); - if (timer_index>=0) LOGI("Using timer TIM%d", timer_index + 1); - timer->attachInterrupt(std::bind(callback_f, object)); - - // we determine the time in microseconds - switch (unit) { - case MS: - timer->setOverflow(time * 1000, MICROSEC_FORMAT); // 10 Hz - break; - case US: - timer->setOverflow(time, MICROSEC_FORMAT); // 10 Hz - break; - case HZ: - // convert hz to time in us - uint64_t time_us = AudioTime::toTimeUs(time); - timer->setOverflow(time_us, MICROSEC_FORMAT); // 10 Hz - break; - } - timer->resume(); - return true; - } - - // ends the timer and if necessary the task - bool end() override { - TRACEI(); - timer->pause(); - return true; - } - - protected: - HardwareTimer *timer = nullptr; - int timer_index; - TIM_TypeDef *timers[6] = {TIM1, TIM2, TIM3, TIM4, TIM5}; -}; - -/// @brief use TimerAlarmRepeating! @ingroup timer_stm32 -using TimerAlarmRepeatingDriver = TimerAlarmRepeatingDriverSTM32; - -} // namespace audio_tools - -#endif \ No newline at end of file diff --git a/src/AudioTools/CoreAudio/AudioTypes.h b/src/AudioTools/CoreAudio/AudioTypes.h deleted file mode 100644 index 92adc2dd21..0000000000 --- a/src/AudioTools/CoreAudio/AudioTypes.h +++ /dev/null @@ -1,526 +0,0 @@ -#pragma once - -#include "AudioToolsConfig.h" -#ifdef USE_TYPETRAITS -#include -#include -#endif - -#include "AudioTools/CoreAudio/AudioBasic/Collections/Vector.h" -#include "AudioTools/CoreAudio/AudioLogger.h" - -// fix compile error for ESP32 C3 -#undef HZ - -// MIN -#undef MIN -#define MIN(A, B) ((A) < (B) ? (A) : (B)) - -namespace audio_tools { - -using sample_rate_t = uint32_t; - -/** - * @brief The Microcontroller is the Audio Source (TX_MODE) or Audio Sink - * (RX_MODE). RXTX_MODE is Source and Sink at the same time! - * @ingroup basic - */ -enum RxTxMode { UNDEFINED_MODE = 0, TX_MODE = 1, RX_MODE = 2, RXTX_MODE = 3 }; - -/** - * @brief Memory types - * @ingroup basic - */ - -enum MemoryType { RAM, PS_RAM, FLASH_RAM }; - -/** - * @brief Text string (description) for RxTxMode - */ -static const char* RxTxModeNames[4] = {"UNDEFINED_MODE", "TX_MODE", "RX_MODE", - "RXTX_MODE"}; -/** - * @brief Time Units - * @ingroup basic - */ -enum TimeUnit { MS, US, HZ }; -static const char* TimeUnitStr[3]{"MS", "US", "HZ"}; - -/** - * @brief Basic Audio information which drives e.g. I2S - * @ingroup basic - */ -struct AudioInfo { - /// Sample Rate: e.g 44100 - sample_rate_t sample_rate = DEFAULT_SAMPLE_RATE; - /// Number of channels: 2=stereo, 1=mono - uint16_t channels = DEFAULT_CHANNELS; - /// Number of bits per sample (int16_t = 16 bits) - uint8_t bits_per_sample = DEFAULT_BITS_PER_SAMPLE; - - /// Default constructor - AudioInfo() = default; - - /// Constructor which supports all attribures as parameters - AudioInfo(sample_rate_t sampleRate, uint16_t channelCount, - uint8_t bitsPerSample) { - sample_rate = sampleRate; - channels = channelCount; - bits_per_sample = bitsPerSample; - }; - - /// Copy constructor - AudioInfo(const AudioInfo&) = default; - - /// Returns true if alt values are the same like the current values - bool operator==(AudioInfo alt) { - return sample_rate == alt.sample_rate && channels == alt.channels && - bits_per_sample == alt.bits_per_sample; - } - - /// Returns true if alt values are the different from the current values - bool operator!=(AudioInfo alt) { return !(*this == alt); } - - /// Returns true if alt values are the same like the current values - bool equals(AudioInfo alt) { return *this == alt; } - - /// Checks if only the sample rate is different - bool equalsExSampleRate(AudioInfo alt) { - return channels == alt.channels && bits_per_sample == alt.bits_per_sample; - } - - /// Copies the values from info - void set(AudioInfo info) { - sample_rate = info.sample_rate; - channels = info.channels; - bits_per_sample = info.bits_per_sample; - } - - /// Same as set - void setAudioInfo(AudioInfo info) { set(info); } - - /// Same as set - void copyFrom(AudioInfo info) { setAudioInfo(info); } - -#ifndef SWIG - /// Same as set - AudioInfo& operator=(const AudioInfo& info) { - setAudioInfo(info); - return *this; - } -#endif - /// Returns true if all components are defined (no component is 0) - operator bool() { - return sample_rate > 0 && channels > 0 && bits_per_sample > 0; - } - - virtual void clear() { - sample_rate = 0; - channels = 0; - bits_per_sample = 0; - } - - virtual void logInfo(const char* source = "") { - LOGI("%s sample_rate: %d / channels: %d / bits_per_sample: %d", source, - (int)sample_rate, (int)channels, (int)bits_per_sample); - } -}; - -/** - * @brief Supports changes to the sampling rate, bits and channels - * @ingroup basic - */ -class AudioInfoSupport { - public: - /// Defines the input AudioInfo - virtual void setAudioInfo(AudioInfo info) = 0; - /// provides the actual input AudioInfo - virtual AudioInfo audioInfo() = 0; - /// provides the actual output AudioInfo: this is usually the same as - /// audioInfo() unless we use a transforming stream - virtual AudioInfo audioInfoOut() { return audioInfo(); } -}; - -/** - * @brief Supports the subscription to audio change notifications - * @ingroup basic - */ -class AudioInfoSource { - public: - /// Adds target to be notified about audio changes - virtual void addNotifyAudioChange(AudioInfoSupport& bi) { - if (!notify_vector.contains(&bi)) notify_vector.push_back(&bi); - } - - /// Removes a target in order not to be notified about audio changes - virtual bool removeNotifyAudioChange(AudioInfoSupport& bi) { - int pos = notify_vector.indexOf(&bi); - if (pos < 0) return false; - notify_vector.erase(pos); - return true; - } - - /// Deletes all change notify subscriptions - virtual void clearNotifyAudioChange() { notify_vector.clear(); } - - /// Deactivate/Reactivate automatic AudioInfo updates: (default is active) - void setNotifyActive(bool flag) { is_notify_active = flag; } - - /// Checks if the automatic AudioInfo update is active - bool isNotifyActive() { return is_notify_active; } - - protected: - Vector notify_vector; - bool is_notify_active = true; - - void notifyAudioChange(AudioInfo info) { - if (isNotifyActive()) { - for (auto n : notify_vector) { - n->setAudioInfo(info); - } - } - } -}; - -/** - * @brief Supports the setting and getting of the volume - * @ingroup volume - */ -class VolumeSupport { - public: - /// provides the actual volume in the range of 0.0f to 1.0f - virtual float volume() { return volume_value; } - /// define the actual volume in the range of 0.0f to 1.0f - virtual bool setVolume(float volume) { - volume_value = volume; - return true; - } - - protected: - float volume_value = 1.0f; -}; - -/** - * @brief E.g. used by Encoders and Decoders - * @ingroup basic - */ -class AudioWriter : public AudioInfoSupport { - public: - virtual size_t write(const uint8_t* data, size_t len) = 0; - virtual void setAudioInfo(AudioInfo from) = 0; - virtual void setOutput(Print& out_stream) = 0; - virtual operator bool() = 0; - virtual bool begin() = 0; - virtual bool begin(AudioInfo info) { - setAudioInfo(info); - return begin(); - } - virtual void end() = 0; - - protected: - void writeBlocking(Print* out, uint8_t* data, size_t len) { - TRACED(); - int open = len; - int written = 0; - while (open > 0) { - int result = out->write(data + written, open); - open -= result; - written += result; - } - } -}; - -/** - * @brief Tools for calculating timer values - * @ingroup timer - */ -class AudioTime { - public: - /// converts sampling rate to delay in microseconds (μs) - static uint32_t toTimeUs(uint32_t samplingRate, uint8_t limit = 10) { - uint32_t result = 1000000l / samplingRate; - if (1000000l % samplingRate != 0) { - result++; - } - if (result <= limit) { - LOGW("Time for samplingRate %u -> %u is < %u μs - we rounded up", - (unsigned int)samplingRate, (unsigned int)result, - (unsigned int)limit); - result = limit; - } - return result; - } - - /// converts milliseconds to bytes - static uint32_t toBytes(uint32_t millis, AudioInfo info) { - size_t samples = info.sample_rate * millis / 1000; - size_t bytes = samples * info.channels * info.bits_per_sample / 8; - return bytes; - } - - static uint32_t toTimeMs(uint32_t samplingRate, uint8_t limit = 1) { - uint32_t result = 1000l / samplingRate; - if (1000000l % samplingRate != 0) { - result++; - } - if (result <= limit) { - LOGW("Time for samplingRate %u -> %u is < %u μs - we rounded up", - (unsigned int)samplingRate, (unsigned int)result, - (unsigned int)limit); - result = limit; - } - return result; - } - - static float toRateUs(uint32_t time_us) { return 1000000.0 / time_us; } - - static float toRateMs(uint32_t time_ms) { return 1000.0 / time_ms; } -}; - -/// @brief Similar to Arduino map function but using floats -/// @param x -/// @param in_min -/// @param in_max -/// @param out_min -/// @param out_max -/// @return -template -inline T mapT(T x, T in_min, T in_max, T out_min, T out_max) { - return (x - in_min) * (out_max - out_min) / (in_max - in_min) + out_min; -} - -/** - * @brief Converts from a source to a target number with a different type - * @ingroup basic - */ -class NumberConverter { - public: - /// provides the biggest number for the indicated number of bits - static int64_t maxValue(int value_bits_per_sample) { - switch (value_bits_per_sample) { - case 8: - return 127; - case 16: - return 32767; - case 24: - return 8388607; - case 32: - return 2147483647; - } - return 32767; - } - - /// provides the biggest number for the indicated type - template - static float maxValueT() { -#ifdef USE_TYPETRAITS - // int24_t uses 4 bytes instead of 3! - // return (std::is_same::value ) ? 8388607 : - // maxValue(sizeof(T)*8); - return std::numeric_limits::max(); -#else - return maxValue(sizeof(T) * 8); -#endif - } - - template - static float minValueT() { -#ifdef USE_TYPETRAITS - // int24_t uses 4 bytes instead of 3! - // return (std::is_same::value ) ? 8388607 : - // maxValue(sizeof(T)*8); - return std::numeric_limits::min(); -#else - return -maxValue(sizeof(T) * 8); -#endif - } - - /// Clips the value to avoid any over or underflows - template - static T clipT(float value) { - float mv = maxValueT(); - if (value > mv) { - return mv; - } else if (value < -mv) { - return -mv; - } - return value; - } - - /// clips a value - inline static int32_t clip(float value, int bits) { - float mv = maxValue(bits); - if (value > mv) { - return mv; - } else if (value < -mv) { - return -mv; - } - return value; - } - - /// Convert an integer integer autio type to a float (with max 1.0) - template - static float toFloatT(T value) { - return static_cast(value) / maxValueT(); - } - - /// Convert a float (with max 1.0) to an integer autio type - template - static T fromFloatT(float value) { - return value * maxValueT(); - } - - /// Convert an integer integer autio type to a float (with max 1.0) - inline static float toFloat(int32_t value, int bits) { - return static_cast(value) / maxValue(bits); - } - - /// Convert a float (with max 1.0) to an integer autio type - inline static int32_t fromFloat(float value, int bits) { - return clip(value * maxValue(bits), bits); - } - - /// Convert an int number from one type to another - template - static ToT convert(FromT value) { - float value1 = value; - float minTo = minValueT(); - float maxTo = maxValueT(); - float maxFrom = maxValueT(); - float minFrom = minValueT(); - - if (maxTo - minTo > 1.0f || maxFrom - minFrom > 1.0f) { - return mapT(value1, minFrom, maxFrom, minTo, maxTo); - } - - return value1 * maxValueT() / maxValueT(); - } - - /// Convert an array of int types - template - static void convertArray(FromT* from, ToT* to, int samples, - float vol = 1.0f) { - float factor = static_cast(maxValueT()) / maxValueT(); - float vol_factor = factor * vol; - for (int j = 0; j < samples; j++) { - to[j] = clipT(vol_factor * convert(from[j])); - } - } -}; - -#if defined(USE_I2S) - -/** - * @brief I2S Formats - */ -enum I2SFormat { - I2S_STD_FORMAT, - I2S_LSB_FORMAT, - I2S_MSB_FORMAT, - I2S_PHILIPS_FORMAT, - I2S_RIGHT_JUSTIFIED_FORMAT, - I2S_LEFT_JUSTIFIED_FORMAT, - I2S_PCM, -}; - -static const char* i2s_formats[] = {"I2S_STD_FORMAT", - "I2S_LSB_FORMAT", - "I2S_MSB_FORMAT", - "I2S_PHILIPS_FORMAT", - "I2S_RIGHT_JUSTIFIED_FORMAT", - "I2S_LEFT_JUSTIFIED_FORMAT", - "I2S_PCM"}; - -/** - * @brief I2S Signal Types: Digital, Analog, PDM - */ -enum I2SSignalType { - Digital, - Analog, - PDM, - TDM, -}; - -static const char* i2s_signal_types[] = {"Digital", "Analog", "PDM", "TDM"}; - -#endif - -/// guaranteed to return the requested data -template -T readSample(Stream* p_stream) { - T result = 0; - uint8_t* p_result = (uint8_t*)&result; - int open = sizeof(T); - int total = 0; - // copy missing data - while (open > 0) { - int read = p_stream->readBytes(p_result + total, open); - open -= read; - total += read; - } - return result; -} - -/// guaranteed to return the requested data -template -size_t readSamples(Stream* p_stream, T* data, int samples, - int retryCount = -1) { - uint8_t* p_result = (uint8_t*)data; - int open = samples * sizeof(T); - int total = 0; - int count0 = 0; - // copy missing data - we abort after 5 x read returned 0 bytes - while (open > 0) { - int read = p_stream->readBytes(p_result + total, open); - open -= read; - total += read; - if (read == 0) { - count0++; - delay(1); - } else { - count0 = 0; - } - // abort loop - if (retryCount >= 0 && count0 > retryCount) break; - } - // convert bytes to samples - return total / sizeof(T); -} - -/// guaranteed to return the requested data -template -size_t writeDataT(P* p_out, T* data, int samples, int maxSamples = 512) { - uint8_t* p_result = (uint8_t*)data; - int open = samples * sizeof(T); - int total = 0; - // copy missing data - while (open > 0) { - int to_write = min(open, static_cast(maxSamples * sizeof(T))); - int written = p_out->write(p_result + total, to_write); - open -= written; - total += written; - } - // convert bytes to samples - return total / sizeof(T); -} - -template -size_t writeData(Print* p_out, T* data, int samples, int maxSamples = 512) { - return writeDataT(p_out, data, samples, maxSamples); -} - -/// @brief Mime type for PCM -static const char* mime_pcm = "audio/pcm"; - -#ifndef IS_DESKTOP -/// wait for the Output to be ready -inline void waitFor(HardwareSerial& out) { while (!out); } -#endif - -/// wait for flag to be active @ingroup basic -inline void waitFor(bool& flag) { while (!flag); } - -/// Pins @ingroup basic -using Pins = Vector; - -} // namespace audio_tools \ No newline at end of file diff --git a/src/AudioTools/CoreAudio/BaseConverter.h b/src/AudioTools/CoreAudio/BaseConverter.h deleted file mode 100644 index 283f5fe357..0000000000 --- a/src/AudioTools/CoreAudio/BaseConverter.h +++ /dev/null @@ -1,1898 +0,0 @@ -#pragma once -#include "AudioFilter/Filter.h" -#include "AudioTools/CoreAudio/AudioBasic/Collections.h" -#include "AudioTypes.h" - -/** - * @defgroup convert Converters - * @ingroup tools - * @brief Convert Audio - * You can add a converter as argument to the StreamCopy::copy() or better use - * is with a ConverterStream. - */ - -namespace audio_tools { - -/** - * @brief Abstract Base class for Converters - * A converter is processing the data in the indicated array - * @ingroup convert - * @author Phil Schatzmann - * @copyright GPLv3 - * @tparam T - */ -class BaseConverter { - public: - BaseConverter() = default; - BaseConverter(BaseConverter const &) = delete; - virtual ~BaseConverter() = default; - - BaseConverter &operator=(BaseConverter const &) = delete; - - virtual size_t convert(uint8_t *src, size_t size) = 0; -}; - -/** - * @brief Dummy converter which does nothing - * @ingroup convert - * @tparam T - */ -class NOPConverter : public BaseConverter { - public: - size_t convert(uint8_t(*src), size_t size) override { return size; }; -}; - -/** - * @brief Multiplies the values with the indicated factor adds the offset and - * clips at maxValue. To mute use a factor of 0.0! - * @ingroup convert - * @author Phil Schatzmann - * @copyright GPLv3 - * - * @tparam T - */ -template -class ConverterScaler : public BaseConverter { - public: - ConverterScaler(float factor, T offset, T maxValue, int channels = 2) { - this->factor_value = factor; - this->maxValue = maxValue; - this->offset_value = offset; - this->channels = channels; - } - - size_t convert(uint8_t *src, size_t byte_count) { - T *sample = (T *)src; - int size = byte_count / channels / sizeof(T); - for (size_t j = 0; j < size; j++) { - for (int i = 0; i < channels; i++) { - *sample = (*sample + offset_value) * factor_value; - if (*sample > maxValue) { - *sample = maxValue; - } else if (*sample < -maxValue) { - *sample = -maxValue; - } - sample++; - } - } - return byte_count; - } - - /// Defines the factor (volume) - void setFactor(float factor) { this->factor_value = factor; } - - /// Defines the offset - void setOffset(T offset) { this->offset_value = offset; } - - /// Determines the actual factor (volume) - float factor() { return factor_value; } - - /// Determines the offset value - T offset() { return offset_value; } - - protected: - int channels; - float factor_value; - T maxValue; - T offset_value; -}; - -/** - * @brief Makes sure that the avg of the signal is set to 0 - * @ingroup convert - * @tparam T - */ -template -class ConverterAutoCenterT : public BaseConverter { - public: - ConverterAutoCenterT(int channels = 2, bool isDynamic = false) { - this->channels = channels; - this->is_dynamic = isDynamic; - } - - size_t convert(uint8_t(*src), size_t byte_count) override { - size_t size = byte_count / channels / sizeof(T); - T *sample = (T *)src; - setup((T *)src, size); - // convert data - if (is_setup) { - if (!is_dynamic) { - for (size_t j = 0; j < size; j++) { - for (int ch = 0; ch < channels; ch++) { - sample[(j * channels) + ch] = - sample[(j * channels) + ch] - offset_to[ch]; - } - } - } else { - for (size_t j = 0; j < size; j++) { - for (int ch = 0; ch < channels; ch++) { - sample[(j * channels) + ch] = sample[(j * channels) + ch] - - offset_from[ch] + - (offset_step[ch] * size); - } - } - } - } - return byte_count; - } - - protected: - Vector offset_from{0}; - Vector offset_to{0}; - Vector offset_step{0}; - Vector total{0}; - float left = 0.0; - float right = 0.0; - bool is_setup = false; - bool is_dynamic; - int channels; - - void setup(T *src, size_t size) { - if (size == 0) return; - if (!is_setup || is_dynamic) { - if (offset_from.size() == 0) { - offset_from.resize(channels); - offset_to.resize(channels); - offset_step.resize(channels); - total.resize(channels); - } - - // save last offset - for (int ch = 0; ch < channels; ch++) { - offset_from[ch] = offset_to[ch]; - total[ch] = 0.0; - } - - // calculate new offset - for (size_t j = 0; j < size; j++) { - for (int ch = 0; ch < channels; ch++) { - total[ch] += src[(j * channels) + ch]; - } - } - for (int ch = 0; ch < channels; ch++) { - offset_to[ch] = total[ch] / size; - offset_step[ch] = (offset_to[ch] - offset_from[ch]) / size; - } - is_setup = true; - } - } -}; - -/** - * @brief Makes sure that the avg of the signal is set to 0 - * @ingroup convert - */ -class ConverterAutoCenter : public BaseConverter { - public: - ConverterAutoCenter() = default; - - ConverterAutoCenter(AudioInfo info) { - begin(info.channels, info.bits_per_sample); - } - - ConverterAutoCenter(int channels, int bitsPerSample) { - begin(channels, bitsPerSample); - } - ~ConverterAutoCenter() { end(); } - - void end() { - if (p_converter != nullptr) { - delete p_converter; - p_converter = nullptr; - } - } - - bool begin(AudioInfo info, bool isDynamic = false) { - return begin(info.channels, info.bits_per_sample, isDynamic); - } - - bool begin(int channels, int bitsPerSample, bool isDynamic = false) { - // check if we need to create a new converter - if (p_converter != nullptr && channels == this->channels && bitsPerSample == this->bits_per_sample){ - return true; - } - this->channels = channels; - this->bits_per_sample = bitsPerSample; - end(); - assert(p_converter == nullptr); - switch (bits_per_sample) { - case 8: { - p_converter = new ConverterAutoCenterT(channels, isDynamic); - break; - } - case 16: { - p_converter = new ConverterAutoCenterT(channels, isDynamic); - break; - } - case 24: { - p_converter = new ConverterAutoCenterT(channels, isDynamic); - break; - } - case 32: { - p_converter = new ConverterAutoCenterT(channels, isDynamic); - break; - } - } - return p_converter != nullptr; - } - - size_t convert(uint8_t *src, size_t size) override { - if (p_converter == nullptr) return 0; - return p_converter->convert(src, size); - } - - protected: - int channels = 0; - int bits_per_sample = 0; - BaseConverter *p_converter = nullptr; -}; - -/** - * @brief Switches the left and right channel - * @ingroup convert - * @author Phil Schatzmann - * @copyright GPLv3 - * - * @tparam T - */ -template -class ConverterSwitchLeftAndRight : public BaseConverter { - public: - ConverterSwitchLeftAndRight(int channels = 2) { this->channels = channels; } - - size_t convert(uint8_t *src, size_t byte_count) override { - if (channels == 2) { - int size = byte_count / channels / sizeof(T); - T *sample = (T *)src; - for (size_t j = 0; j < size; j++) { - T temp = *sample; - *sample = *(sample + 1); - *(sample + 1) = temp; - sample += 2; - } - } - return byte_count; - } - - protected: - int channels = 2; -}; - -/** - * @brief Configure ConverterFillLeftAndRight - * @ingroup convert - */ -enum FillLeftAndRightStatus { Auto, LeftIsEmpty, RightIsEmpty }; - -/** - * @brief Make sure that both channels contain any data - * @ingroup convert - * @author Phil Schatzmann - * @copyright GPLv3 - * - * @tparam T - */ - -template -class ConverterFillLeftAndRight : public BaseConverter { - public: - ConverterFillLeftAndRight(FillLeftAndRightStatus config = Auto, - int channels = 2) { - this->channels = channels; - switch (config) { - case LeftIsEmpty: - left_empty = true; - right_empty = false; - is_setup = true; - break; - case RightIsEmpty: - left_empty = false; - right_empty = true; - is_setup = true; - break; - case Auto: - is_setup = false; - break; - } - } - - size_t convert(uint8_t *src, size_t byte_count) { - if (channels == 2) { - int size = byte_count / channels / sizeof(T); - setup((T *)src, size); - if (left_empty && !right_empty) { - T *sample = (T *)src; - for (size_t j = 0; j < size; j++) { - *sample = *(sample + 1); - sample += 2; - } - } else if (!left_empty && right_empty) { - T *sample = (T *)src; - for (size_t j = 0; j < size; j++) { - *(sample + 1) = *sample; - sample += 2; - } - } - } - return byte_count; - } - - private: - bool is_setup = false; - bool left_empty = true; - bool right_empty = true; - int channels; - - void setup(T *src, size_t size) { - if (!is_setup) { - for (int j = 0; j < size; j++) { - if (*src != 0) { - left_empty = false; - break; - } - src += 2; - } - for (int j = 0; j < size - 1; j++) { - if (*(src) != 0) { - right_empty = false; - break; - } - src += 2; - } - // stop setup as soon as we found some data - if (!right_empty || !left_empty) { - is_setup = true; - } - } - } -}; - -/** - * @brief special case for internal DAC output, the incomming PCM buffer needs - * to be converted from signed 16bit to unsigned - * @ingroup convert - * @author Phil Schatzmann - * @copyright GPLv3 - * - * @tparam T - */ -template -class ConverterToInternalDACFormat : public BaseConverter { - public: - ConverterToInternalDACFormat(int channels = 2) { this->channels = channels; } - - size_t convert(uint8_t *src, size_t byte_count) override { - int size = byte_count / channels / sizeof(T); - T *sample = (T *)src; - for (int i = 0; i < size; i++) { - for (int j = 0; j < channels; j++) { - *sample = *sample + 0x8000; - sample++; - } - } - return byte_count; - } - - protected: - int channels; -}; - -/** - * @brief We combine a datastream which consists of multiple channels into less - * channels. E.g. 2 to 1 The last target channel will contain the combined - * values of the exceeding source channels. - * @ingroup convert - * @tparam T - */ -template -class ChannelReducerT : public BaseConverter { - public: - ChannelReducerT() = default; - - ChannelReducerT(int channelCountOfTarget, int channelCountOfSource) { - from_channels = channelCountOfSource; - to_channels = channelCountOfTarget; - } - - void setSourceChannels(int channelCountOfSource) { - from_channels = channelCountOfSource; - } - - void setTargetChannels(int channelCountOfTarget) { - to_channels = channelCountOfTarget; - } - - size_t convert(uint8_t *src, size_t size) { return convert(src, src, size); } - - size_t convert(uint8_t *target, uint8_t *src, size_t size) { - LOGD("convert %d -> %d", from_channels, to_channels); - assert(to_channels <= from_channels); - int frame_count = size / (sizeof(T) * from_channels); - size_t result_size = 0; - T *result = (T *)target; - T *source = (T *)src; - int reduceDiv = from_channels - to_channels + 1; - - for (int i = 0; i < frame_count; i++) { - // copy first to_channels-1 - for (int j = 0; j < to_channels - 1; j++) { - *result++ = *source++; - result_size += sizeof(T); - } - // commbined last channels - T total = (int16_t)0; - for (int j = to_channels - 1; j < from_channels; j++) { - total += *source++ / reduceDiv; - } - *result++ = total; - result_size += sizeof(T); - } - return result_size; - } - - protected: - int from_channels; - int to_channels; -}; - -/** - * @brief We combine a datastream which consists of multiple channels into less - * channels. E.g. 2 to 1 The last target channel will contain the combined - * values of the exceeding source channels. - * @ingroup convert - */ -class ChannelReducer : public BaseConverter { - public: - ChannelReducer(int channelCountOfTarget, int channelCountOfSource, - int bitsPerSample) { - from_channels = channelCountOfSource; - to_channels = channelCountOfTarget; - bits = bitsPerSample; - } - - size_t convert(uint8_t *src, size_t size) { return convert(src, src, size); } - - size_t convert(uint8_t *target, uint8_t *src, size_t size) { - switch (bits) { - case 8: { - ChannelReducerT cr8(to_channels, from_channels); - return cr8.convert(target, src, size); - } - case 16: { - ChannelReducerT cr16(to_channels, from_channels); - return cr16.convert(target, src, size); - } - case 24: { - ChannelReducerT cr24(to_channels, from_channels); - return cr24.convert(target, src, size); - } - case 32: { - ChannelReducerT cr32(to_channels, from_channels); - return cr32.convert(target, src, size); - } - } - return 0; - } - - protected: - int from_channels; - int to_channels; - int bits; -}; - -/** - * @brief Provides reduced sampling rates - * @ingroup convert - */ -template -class DecimateT : public BaseConverter { - public: - DecimateT(int factor, int channels) { - setChannels(channels); - setFactor(factor); - count = 0; // Initialize count to 0 - } - - /// Defines the number of channels - void setChannels(int channels) { this->channels = channels; } - - /// Sets the factor: e.g. with 4 we keep every fourth sample - void setFactor(int factor) { this->factor = factor; } - - size_t convert(uint8_t *src, size_t size) { return convert(src, src, size); } - - size_t convert(uint8_t *target, uint8_t *src, size_t size) { - if (size % (sizeof(T) * channels) > 0) { - LOGE("Buffer size %d is not a multiple of the number of channels %d", - (int)size, channels); - return 0; - } - - int frame_count = size / (sizeof(T) * channels); - T *p_target = (T *)target; - T *p_source = (T *)src; - size_t result_size = 0; - - for (int i = 0; i < frame_count; i++) { - if (++count == factor) { - count = 0; - // Only keep every "factor" samples - for (int ch = 0; ch < channels; ch++) { - *p_target++ = p_source[i * channels + ch]; // Corrected indexing - result_size += sizeof(T); - } - } - } - - LOGD("decimate %d: %d -> %d bytes", factor, (int)size, (int)result_size); - return result_size; - } - - operator bool() { return factor > 1; } - - protected: - int channels = 2; - int factor = 1; - uint16_t count; -}; - -/** - * @brief Provides a reduced sampling rate by taking a sample at every factor - * location (ingoring factor-1 samples) - * @ingroup convert - */ - -class Decimate : public BaseConverter { - public: - Decimate() = default; - Decimate(int factor, int channels, int bits_per_sample) { - setFactor(factor); - setChannels(channels); - setBits(bits_per_sample); - } - /// Defines the number of channels - void setChannels(int channels) { this->channels = channels; } - void setBits(int bits) { this->bits = bits; } - /// Sets the factor: e.g. with 4 we keep every forth sample - void setFactor(int factor) { this->factor = factor; } - - size_t convert(uint8_t *src, size_t size) { return convert(src, src, size); } - size_t convert(uint8_t *target, uint8_t *src, size_t size) { - switch (bits) { - case 8: { - DecimateT dec8(factor, channels); - return dec8.convert(target, src, size); - } - case 16: { - DecimateT dec16(factor, channels); - return dec16.convert(target, src, size); - } - case 24: { - DecimateT dec24(factor, channels); - return dec24.convert(target, src, size); - } - case 32: { - DecimateT dec32(factor, channels); - return dec32.convert(target, src, size); - } - } - return 0; - } - - operator bool() { return factor > 1; }; - - protected: - int channels = 2; - int bits = 16; - int factor = 1; -}; - -/** - * @brief We reduce the number of samples in a datastream by summing (binning) - * or averaging. This will result in the same number of channels but binSize - * times less samples. If Average is true the sum is divided by binSize. - * @author Urs Utzinger - * @ingroup convert - * @tparam T - */ - -// Helper template to define the integer type for the summation based on input -// data type T -template -struct AppropriateSumType { - using type = T; -}; - -template <> -struct AppropriateSumType { - using type = int16_t; -}; - -template <> -struct AppropriateSumType { - using type = int32_t; -}; - -template <> -struct AppropriateSumType { - using type = int32_t; // Assuming int24_t is a custom 24-bit integer type -}; - -template <> -struct AppropriateSumType { - using type = int64_t; -}; - -template -class BinT : public BaseConverter { - public: - BinT() = default; - BinT(int binSize, int channels, bool average) { - setChannels(channels); - setBinSize(binSize); - setAverage(average); - this->partialBinSize = 0; - this->partialBin = new T[channels](); - } - - ~BinT() { delete[] this->partialBin; } - - void setChannels(int channels) { this->channels = channels; } - void setBinSize(int binSize) { this->binSize = binSize; } - void setAverage(bool average) { this->average = average; } - - size_t convert(uint8_t *src, size_t size) { return convert(src, src, size); } - - size_t convert(uint8_t *target, uint8_t *src, size_t size) { - // The binning takes the following into account - // 1) if size is too small it will add up data to partialBin and return 0 - // size 2) if there is sufficient data to fill Bins but there is partial - // data remaining it will be added to the partial Bin 3) if there was - // previous partial Bin it will be filled with the new data 4) if there is - // insufficient new data to fill the partial Bin it will fill the partial - // Bin with the new data - - if (size % (sizeof(T) * channels) > 0) { - LOGE("Buffer size %d is not a multiple of the number of channels %d", - (int)size, channels); - return 0; - } - - int sample_count = - size / (sizeof(T) * channels); // new available samples in each channel - int total_samples = - partialBinSize + - sample_count; // total samples available for each channel including - // previous number of sample in partial bin - int bin_count = total_samples / binSize; // number of bins we can make - int remaining_samples = - total_samples % binSize; // remaining samples after binning - T *p_target = (T *)target; - T *p_source = (T *)src; - size_t result_size = 0; - - // Allocate sum for each channel with appropriate type - typename AppropriateSumType::type sums[channels]; - int current_sample = 0; // current sample index - - // Is there a partial bin from the previous call? - // ---- - if (partialBinSize > 0) { - int samples_needed = binSize - partialBinSize; - bool have_enough_samples = (samples_needed < sample_count); - int samples_to_bin = have_enough_samples ? samples_needed : sample_count; - - for (int ch = 0; ch < channels; ch++) { - sums[ch] = partialBin[ch]; - } - - for (int j = 0; j < samples_to_bin; j++) { - for (int ch = 0; ch < channels; ch++) { - sums[ch] += p_source[current_sample * channels + ch]; - } - current_sample++; - } - - if (have_enough_samples) { - // Store the completed bin - if (average) { - for (int ch = 0; ch < channels; ch++) { - p_target[result_size / sizeof(T)] = - static_cast(sums[ch] / binSize); - result_size += sizeof(T); - } - } else { - for (int ch = 0; ch < channels; ch++) { - p_target[result_size / sizeof(T)] = static_cast(sums[ch]); - result_size += sizeof(T); - } - } - partialBinSize = 0; - } else { - // Not enough samples to complete the bin, update partialBin - for (int ch = 0; ch < channels; ch++) { - partialBin[ch] = sums[ch]; - } - partialBinSize += current_sample; - - LOGD("bin %d: %d of %d remaining %d samples, %d > %d bytes", binSize, - current_sample, total_samples, partialBinSize, (int)size, - (int)result_size); - return result_size; - } - } - - // Fill bins - // ---- - for (int i = 0; i < bin_count; i++) { - for (int ch = 0; ch < channels; ch++) { - sums[ch] = p_source[current_sample * channels + - ch]; // Initialize sums with first value in the - // input buffer - } - - for (int j = 1; j < binSize; j++) { - for (int ch = 0; ch < channels; ch++) { - sums[ch] += p_source[(current_sample + j) * channels + ch]; - } - } - current_sample += binSize; - - // Store the bin result - if (average) { - for (int ch = 0; ch < channels; ch++) { - p_target[result_size / sizeof(T)] = - static_cast(sums[ch] / binSize); - result_size += sizeof(T); - } - } else { - for (int ch = 0; ch < channels; ch++) { - p_target[result_size / sizeof(T)] = static_cast(sums[ch]); - result_size += sizeof(T); - } - } - } - - // Store the remaining samples in the partial bin - // ---- - for (int i = 0; i < remaining_samples; i++) { - for (int ch = 0; ch < channels; ch++) { - partialBin[ch] += p_source[(current_sample + i) * channels + ch]; - } - } - partialBinSize = remaining_samples; - - LOGD("bin %d: %d of %d remaining %d samples, %d > %d bytes", binSize, - current_sample, total_samples, partialBinSize, (int)size, - (int)result_size); - - return result_size; - } - - protected: - int channels = 2; - int binSize = 1; - bool average = true; - T *partialBin; - int partialBinSize; -}; - -/** - * @brief Provides reduced sampling rates through binning - * @ingroup convert - */ - -class Bin : public BaseConverter { - public: - Bin() = default; - Bin(int binSize, int channels, bool average, int bits_per_sample) { - setChannels(channels); - setBinSize(binSize); - setAverage(average); - setBits(bits_per_sample); - } - - void setChannels(int channels) { this->channels = channels; } - void setBits(int bits) { this->bits = bits; } - void setBinSize(int binSize) { this->binSize = binSize; } - void setAverage(bool average) { this->average = average; } - - size_t convert(uint8_t *src, size_t size) { return convert(src, src, size); } - size_t convert(uint8_t *target, uint8_t *src, size_t size) { - switch (bits) { - case 8: { - BinT bin8(binSize, channels, average); - return bin8.convert(target, src, size); - } - case 16: { - BinT bin16(binSize, channels, average); - return bin16.convert(target, src, size); - } - case 24: { - BinT bin24(binSize, channels, average); - return bin24.convert(target, src, size); - } - case 32: { - BinT bin32(binSize, channels, average); - return bin32.convert(target, src, size); - } - default: { - LOGE("Number of bits %d not supported.", bits); - return 0; - } - } - } - - protected: - int channels = 2; - int bits = 16; - int binSize = 1; - bool average = false; -}; - -/** - * @brief We calculate the difference between pairs of channels in a datastream. - * E.g. if we have 4 channels we end up with 2 channels. - * The channels will be - * channel_1 - channel_2 - * channel_3 - channel_4 - * This is similar to background subtraction between two channels but will - * also work for quadric, sexic or octic audio. - * This will not work if you provide single channel data! - * @author Urs Utzinger - * @ingroup convert - * @tparam T - */ - -template -class ChannelDiffT : public BaseConverter { - public: - ChannelDiffT() {} - - size_t convert(uint8_t *src, size_t size) override { - return convert(src, src, size); - } - - size_t convert(uint8_t *target, uint8_t *src, size_t size) { - LOGD("channel subtract %d samples, %d bytes", (int)(size / sizeof(T)), - (int)size); - - // Ensure the buffer size is even for pairs of channels - if (size % (sizeof(T) * 2) > 0) { - LOGE("Buffer size is not even"); - return 0; - } - - int sample_count = - size / - (sizeof(T) * 2); // Each pair of channels produces one output sample - T *p_result = (T *)target; - T *p_source = (T *)src; - - for (int i = 0; i < sample_count; i++) { - // *p_result++ = *p_source++ - *p_source++; - auto tmp = *p_source++; - tmp -= *p_source++; - *p_result++ = tmp; - } - - return sizeof(T) * sample_count; - } -}; - -class ChannelDiff : public BaseConverter { - public: - ChannelDiff() = default; - ChannelDiff(int bitsPerSample) { setBits(bitsPerSample); } - void setBits(int bits) { this->bits = bits; } - - size_t convert(uint8_t *src, size_t size) { return convert(src, src, size); } - size_t convert(uint8_t *target, uint8_t *src, size_t size) { - switch (bits) { - case 8: { - ChannelDiffT cd8; - return cd8.convert(target, src, size); - } - case 16: { - ChannelDiffT cd16; - return cd16.convert(target, src, size); - } - case 24: { - ChannelDiffT cd24; - return cd24.convert(target, src, size); - } - case 32: { - ChannelDiffT cd32; - return cd32.convert(target, src, size); - } - default: { - LOGE("Number of bits %d not supported.", bits); - return 0; - } - } - } - - protected: - int bits = 16; -}; - -/** - * @brief We average pairs of channels in a datastream. - * E.g. if we have 4 channels we end up with 2 channels. - * The channels will be - * (channel_1 + channel_2)/2 - * (channel_3 - channel_4)/2. - * This is equivalent of stereo to mono conversion but will - * also work for quadric, sexic or octic audio. - * This will not work if you provide single channel data! - * @author Urs Utzinger - * @ingroup convert - * @tparam T - */ -template -class ChannelAvgT : public BaseConverter { - public: - ChannelAvgT() {} - - size_t convert(uint8_t *src, size_t size) override { - return convert(src, src, size); - } - - size_t convert(uint8_t *target, uint8_t *src, size_t size) { - if (size % (sizeof(T) * 2) > 0) { - LOGE("Buffer size is not even"); - return 0; - } - - int sample_count = - size / - (sizeof(T) * 2); // Each pair of channels produces one output sample - T *p_result = (T *)target; - T *p_source = (T *)src; - - for (int i = 0; i < sample_count; i++) { - // *p_result++ = (*p_source++ + *p_source++) / 2; // Average the pair of - // channels - auto tmp = *p_source++; - tmp += *p_source++; - *p_result++ = tmp / 2; - } - - LOGD("channel average %d samples, %d bytes", sample_count, (int)size); - - return sizeof(T) * sample_count; - } -}; - -class ChannelAvg : public BaseConverter { - public: - ChannelAvg() = default; - ChannelAvg(int bitsPerSample) { setBits(bitsPerSample); } - void setBits(int bits) { this->bits = bits; } - - size_t convert(uint8_t *src, size_t size) { return convert(src, src, size); } - size_t convert(uint8_t *target, uint8_t *src, size_t size) { - switch (bits) { - case 8: { - ChannelAvgT ca8; - return ca8.convert(target, src, size); - } - case 16: { - ChannelAvgT ca16; - return ca16.convert(target, src, size); - } - case 24: { - ChannelAvgT ca24; - return ca24.convert(target, src, size); - } - case 32: { - ChannelAvgT ca32; - return ca32.convert(target, src, size); - } - default: { - LOGE("Number of bits %d not supported.", bits); - return 0; - } - } - } - - protected: - int bits = 16; -}; - -/** - * @brief We first bin the channels then we calculate the difference between - * pairs of channels in a datastream. E.g. For binning, if we bin 4 samples in - * each channel we will have 4 times less samples per channel E.g. For - * subtracting if we have 4 channels we end up with 2 channels. The channels - * will be channel_1 - channel_2 channel_3 - channel_4 This is the same as - * combining binning and subtracting channels. This will not work if you provide - * single channel data! - * @author Urs Utzinger - * @ingroup convert - * @tparam T - */ - -template -class ChannelBinDiffT : public BaseConverter { - public: - ChannelBinDiffT() = default; - ChannelBinDiffT(int binSize, int channels, bool average) { - setChannels(channels); - setBinSize(binSize); - setAverage(average); - this->partialBinSize = 0; - // this->partialBin = new T[channels]; - // std::fill(this->partialBin, this->partialBin + channels, 0); // - // Initialize partialBin with zeros - this->partialBin = new T[channels](); - } - - ~ChannelBinDiffT() { delete[] this->partialBin; } - - void setChannels(int channels) { - if ((channels % 2) > 0) { - LOGE("Number of channels needs to be even"); - this->channels = channels + 1; - } - this->channels = channels; - } - void setBinSize(int binSize) { this->binSize = binSize; } - void setAverage(bool average) { this->average = average; } - - size_t convert(uint8_t *src, size_t size) { return convert(src, src, size); } - - size_t convert(uint8_t *target, uint8_t *src, size_t size) { - // The binning works the same as in the BinT class - // Here we add subtraction before we store the bins - - if (size % (sizeof(T) * channels) > 0) { - LOGE("Buffer size needs to be multiple of channels") - return 0; - } - - int sample_count = - size / (sizeof(T) * channels); // new available samples in each channel - int total_samples = - partialBinSize + - sample_count; // total samples available for each channel including - // previous number of sample in partial bin - int bin_count = total_samples / binSize; // number of bins we can make - int remaining_samples = - total_samples % binSize; // remaining samples after binning - T *p_target = (T *)target; - T *p_source = (T *)src; - size_t result_size = 0; - - // Allocate sum for each channel with appropriate type - typename AppropriateSumType::type sums[channels]; - int current_sample = 0; // current sample index - - // Is there a partial bin from the previous call? - // ---- - if (partialBinSize > 0) { - // LOGD("Deal with partial bins"); - - int samples_needed = binSize - partialBinSize; - bool have_enough_samples = (samples_needed < sample_count); - int samples_to_bin = have_enough_samples ? samples_needed : sample_count; - - // initialize - for (int ch = 0; ch < channels; ch++) { - sums[ch] = partialBin[ch]; - } - - // continue binning - for (int j = 0; j < samples_to_bin; j++) { - for (int ch = 0; ch < channels; ch++) { - sums[ch] += p_source[current_sample * channels + ch]; - } - current_sample++; - } - - // store the bin results or update the partial bin - if (have_enough_samples) { - // Subtract two channels and store the completed bin - if (average) { - for (int ch = 0; ch < channels; ch += 2) { - p_target[result_size / sizeof(T)] = - static_cast((sums[ch] - sums[ch + 1]) / binSize); - result_size += sizeof(T); - } - } else { - for (int ch = 0; ch < channels; ch += 2) { - p_target[result_size / sizeof(T)] = - static_cast((sums[ch] - sums[ch + 1])); - result_size += sizeof(T); - } - } - partialBinSize = 0; - // LOGD("Partial bins are empty"); - - } else { - // Not enough samples to complete the bin, update partialBin - for (int ch = 0; ch < channels; ch++) { - partialBin[ch] = sums[ch]; - } - partialBinSize += current_sample; - LOGD( - "bin & channel subtract %d: %d of %d remaining %d samples, %d > %d " - "bytes", - binSize, current_sample, total_samples, partialBinSize, (int)size, - (int)result_size); - // LOGD("Partial bins were updated"); - return result_size; - } - } - - // Fill bins - // ---- - // LOGD("Fillin bins"); - for (int i = 0; i < bin_count; i++) { - // LOGD("Current sample %d", current_sample); - - for (int ch = 0; ch < channels; ch++) { - sums[ch] = p_source[current_sample * channels + - ch]; // Initialize sums with first value in the - // input buffer - } - - for (int j = 1; j < binSize; j++) { - for (int ch = 0; ch < channels; ch++) { - sums[ch] += p_source[(current_sample + j) * channels + ch]; - } - } - current_sample += binSize; - - // Finish binning, then subtact two channel and store the result - if (average) { - for (int ch = 0; ch < channels; ch += 2) { - p_target[result_size / sizeof(T)] = - static_cast((sums[ch] - sums[ch + 1]) / binSize); - result_size += sizeof(T); - } - } else { - for (int ch = 0; ch < channels; ch += 2) { - p_target[result_size / sizeof(T)] = - static_cast((sums[ch] - sums[ch + 1])); - result_size += sizeof(T); - } - } - } - - // Store the remaining samples in the partial bin - // ---- - // LOGD("Updating partial bins"); - for (int i = 0; i < remaining_samples; i++) { - for (int ch = 0; ch < channels; ch++) { - partialBin[ch] += p_source[(current_sample + i) * channels + ch]; - } - } - partialBinSize = remaining_samples; - - LOGD( - "bin & channel subtract %d: %d of %d remaining %d samples, %d > %d " - "bytes", - binSize, current_sample, total_samples, partialBinSize, (int)size, - (int)result_size); - - return result_size; - } - - protected: - int channels = 2; - int binSize = 4; - bool average = true; - T *partialBin; - int partialBinSize; -}; - -/** - * @brief Provides combination of binning and subtracting channels - * @author Urs Utzinger - * @ingroup convert - * @tparam T - */ - -class ChannelBinDiff : public BaseConverter { - public: - ChannelBinDiff() = default; - ChannelBinDiff(int binSize, int channels, bool average, int bits_per_sample) { - setChannels(channels); - setBinSize(binSize); - setAverage(average); - setBits(bits_per_sample); - } - - void setChannels(int channels) { - if ((channels % 2) == 0) { - this->channels = channels; - } else { - LOGE("Number of channels needs to be even"); - this->channels = channels + 1; - } - } - - void setBits(int bits) { this->bits = bits; } - void setBinSize(int binSize) { this->binSize = binSize; } - void setAverage(bool average) { this->average = average; } - - size_t convert(uint8_t *src, size_t size) { return convert(src, src, size); } - size_t convert(uint8_t *target, uint8_t *src, size_t size) { - switch (bits) { - case 8: { - ChannelBinDiffT bd8(binSize, channels, average); - return bd8.convert(target, src, size); - } - case 16: { - ChannelBinDiffT bd16(binSize, channels, average); - return bd16.convert(target, src, size); - } - case 24: { - ChannelBinDiffT bd24(binSize, channels, average); - return bd24.convert(target, src, size); - } - case 32: { - ChannelBinDiffT bd32(binSize, channels, average); - return bd32.convert(target, src, size); - } - default: { - LOGE("Number of bits %d not supported.", bits); - return 0; - } - } - } - - protected: - int channels = 2; - int bits = 16; - int binSize = 4; - bool average = true; -}; - -/** - * @brief Increases the channel count - * @ingroup convert - * @tparam T - */ -template -class ChannelEnhancer { - public: - ChannelEnhancer() = default; - - ChannelEnhancer(int channelCountOfTarget, int channelCountOfSource) { - from_channels = channelCountOfSource; - to_channels = channelCountOfTarget; - } - - void setSourceChannels(int channelCountOfSource) { - from_channels = channelCountOfSource; - } - - void setTargetChannels(int channelCountOfTarget) { - to_channels = channelCountOfTarget; - } - - size_t convert(uint8_t *target, uint8_t *src, size_t size) { - int frame_count = size / (sizeof(T) * from_channels); - size_t result_size = 0; - T *result = (T *)target; - T *source = (T *)src; - T value = (int16_t)0; - for (int i = 0; i < frame_count; i++) { - // copy available channels - for (int j = 0; j < from_channels; j++) { - value = *source++; - *result++ = value; - result_size += sizeof(T); - } - // repeat last value - for (int j = from_channels; j < to_channels; j++) { - *result++ = value; - result_size += sizeof(T); - } - } - return result_size; - } - - /// Determine the size of the conversion result - size_t resultSize(size_t inSize) { - return inSize * to_channels / from_channels; - } - - protected: - int from_channels; - int to_channels; -}; - -/** - * @brief Increasing or decreasing the number of channels - * @ingroup convert - * @tparam T - */ -template -class ChannelConverter { - public: - ChannelConverter() = default; - - ChannelConverter(int channelCountOfTarget, int channelCountOfSource) { - from_channels = channelCountOfSource; - to_channels = channelCountOfTarget; - } - - void setSourceChannels(int channelCountOfSource) { - from_channels = channelCountOfSource; - } - - void setTargetChannels(int channelCountOfTarget) { - to_channels = channelCountOfTarget; - } - - size_t convert(uint8_t *target, uint8_t *src, size_t size) { - if (from_channels == to_channels) { - memcpy(target, src, size); - return size; - } - // setup channels - if (from_channels > to_channels) { - reducer.setSourceChannels(from_channels); - reducer.setTargetChannels(to_channels); - } else { - enhancer.setSourceChannels(from_channels); - enhancer.setTargetChannels(to_channels); - } - - // execute conversion - if (from_channels > to_channels) { - return reducer.convert(target, src, size); - } else { - return enhancer.convert(target, src, size); - } - } - - protected: - ChannelEnhancer enhancer; - ChannelReducerT reducer; - int from_channels; - int to_channels; -}; - -/** - * @brief Combines multiple converters - * @ingroup convert - * @tparam T - */ -template -class MultiConverter : public BaseConverter { - public: - MultiConverter() {} - - MultiConverter(BaseConverter &c1) { add(c1); } - - MultiConverter(BaseConverter &c1, BaseConverter &c2) { - add(c1); - add(c2); - } - - MultiConverter(BaseConverter &c1, BaseConverter &c2, BaseConverter &c3) { - add(c1); - add(c2); - add(c3); - } - - // adds a converter - void add(BaseConverter &converter) { converters.push_back(&converter); } - - // The data is provided as int24_t tgt[][2] but returned as int24_t - size_t convert(uint8_t *src, size_t size) { - for (int i = 0; i < converters.size(); i++) { - converters[i]->convert(src, size); - } - return size; - } - - private: - Vector converters; -}; - -// /** -// * @brief Converts e.g. 24bit data to the indicated smaller or bigger data -// type -// * @ingroup convert -// * @author Phil Schatzmann -// * @copyright GPLv3 -// * -// * @tparam T -// */ -// template -// class FormatConverter { -// public: -// FormatConverter(ToType (*converter)(FromType v)){ -// this->convert_ptr = converter; -// } - -// FormatConverter( float factor, float clip){ -// this->factor = factor; -// this->clip = clip; -// } - -// // The data is provided as int24_t tgt[][2] but returned as int24_t -// size_t convert(uint8_t *src, uint8_t *target, size_t byte_count_src) -// { -// return convert((FromType *)src, (ToType*)target, byte_count_src -// ); -// } - -// // The data is provided as int24_t tgt[][2] but returned as int24_t -// size_t convert(FromType *src, ToType *target, size_t byte_count_src) -// { -// int size = byte_count_src / sizeof(FromType); -// FromType *s = src; -// ToType *t = target; -// if (convert_ptr!=nullptr){ -// // standard conversion -// for (int i=size; i>0; i--) { -// *t = (*convert_ptr)(*s); -// t++; -// s++; -// } -// } else { -// // conversion using indicated factor -// for (int i=size; i>0; i--) { -// float tmp = factor * *s; -// if (tmp>clip){ -// tmp=clip; -// } else if (tmp<-clip){ -// tmp = -clip; -// } -// *t = tmp; -// t++; -// s++; -// } -// } -// return size*sizeof(ToType); -// } - -// private: -// ToType (*convert_ptr)(FromType v) = nullptr; -// float factor=0; -// float clip=0; - -// }; - -/** - * @brief Reads n numbers from an Arduino Stream - * - */ -class NumberReader { - public: - NumberReader(Stream &in) { stream_ptr = ∈ } - - NumberReader() {} - - bool read(int inBits, int outBits, bool outSigned, int n, int32_t *result) { - bool result_bool = false; - int len = inBits / 8 * n; - if (stream_ptr != nullptr && stream_ptr->available() > len) { - uint8_t buffer[len]; - stream_ptr->readBytes((uint8_t *)buffer, n * len); - result_bool = - toNumbers((void *)buffer, inBits, outBits, outSigned, n, result); - } - return result_bool; - } - - /// converts a buffer to a number array - bool toNumbers(void *bufferIn, int inBits, int outBits, bool outSigned, int n, - int32_t *result) { - bool result_bool = false; - switch (inBits) { - case 8: { - int8_t *buffer = (int8_t *)bufferIn; - for (int j = 0; j < n; j++) { - result[j] = scale(buffer[j], inBits, outBits, outSigned); - } - result_bool = true; - } break; - case 16: { - int16_t *buffer = (int16_t *)bufferIn; - for (int j = 0; j < n; j++) { - result[j] = scale(buffer[j], inBits, outBits, outSigned); - } - result_bool = true; - } break; - case 32: { - int32_t *buffer = (int32_t *)bufferIn; - for (int j = 0; j < n; j++) { - result[j] = scale(buffer[j], inBits, outBits, outSigned); - } - result_bool = true; - } break; - } - return result_bool; - } - - protected: - Stream *stream_ptr = nullptr; - - /// scale the value - int32_t scale(int32_t value, int inBits, int outBits, bool outSigned = true) { - int32_t result = static_cast(value) / - NumberConverter::maxValue(inBits) * - NumberConverter::maxValue(outBits); - if (!outSigned) { - result += (NumberConverter::maxValue(outBits) / 2); - } - return result; - } -}; - -/** - * @brief Converter for 1 Channel which applies the indicated Filter - * @ingroup convert - * @author pschatzmann - * @tparam T - */ -template -class Converter1Channel : public BaseConverter { - public: - Converter1Channel(Filter &filter) { this->p_filter = &filter; } - - size_t convert(uint8_t *src, size_t size) override { - T *data = (T *)src; - for (size_t j = 0; j < size; j++) { - data[j] = p_filter->process(data[j]); - } - return size; - } - - protected: - Filter *p_filter = nullptr; -}; - -/** - * @brief Converter for n Channels which applies the indicated Filter - * @ingroup convert - * @author pschatzmann - * @tparam T - */ -template -class ConverterNChannels : public BaseConverter { - public: - /// Default Constructor - ConverterNChannels(int channels) { - this->channels = channels; - filters = new Filter *[channels]; - // make sure that we have 1 filter per channel - for (int j = 0; j < channels; j++) { - filters[j] = nullptr; - } - } - - /// Destructor - ~ConverterNChannels() { - for (int j = 0; j < channels; j++) { - if (filters[j] != nullptr) { - delete filters[j]; - } - } - delete[] filters; - filters = 0; - } - - /// defines the filter for an individual channel - the first channel is 0 - void setFilter(int channel, Filter *filter) { - if (channel < channels) { - if (filters[channel] != nullptr) { - delete filters[channel]; - } - filters[channel] = filter; - } else { - LOGE("Invalid channel nummber %d - max channel is %d", channel, - channels - 1); - } - } - - // convert all samples for each channel separately - size_t convert(uint8_t *src, size_t size) { - int count = size / channels / sizeof(T); - T *sample = (T *)src; - for (size_t j = 0; j < count; j++) { - for (int channel = 0; channel < channels; channel++) { - if (filters[channel] != nullptr) { - *sample = filters[channel]->process(*sample); - } - sample++; - } - } - return size; - } - - int getChannels() { return channels; } - - protected: - Filter **filters = nullptr; - int channels; -}; - -/** - * @brief Removes any silence from the buffer that is longer then n samples with - * a amplitude below the indicated threshhold. If you process multiple channels - * you need to multiply the channels with the number of samples to indicate n - * @ingroup convert - * @tparam T - */ - -template -class SilenceRemovalConverter : public BaseConverter { - public: - SilenceRemovalConverter(int n = 8, int aplidudeLimit = 2) { - set(n, aplidudeLimit); - } - - virtual size_t convert(uint8_t *data, size_t size) override { - if (!active) { - // no change to the data - return size; - } - size_t sample_count = size / sizeof(T); - size_t write_count = 0; - T *audio = (T *)data; - - // find relevant data - T *p_buffer = (T *)data; - for (int j = 0; j < sample_count; j++) { - int pos = findLastAudioPos(audio, j); - if (pos < n) { - write_count++; - *p_buffer++ = audio[j]; - } - } - - // write audio data w/o silence - size_t write_size = write_count * sizeof(T); - LOGI("filtered silence from %d -> %d", (int)size, (int)write_size); - - // number of empty samples of prior buffer - priorLastAudioPos = findLastAudioPos(audio, sample_count - 1); - - // return new data size - return write_size; - } - - protected: - bool active = false; - const uint8_t *buffer = nullptr; - int n; - int priorLastAudioPos = 0; - int amplidude_limit = 0; - - void set(int n = 5, int aplidudeLimit = 2) { - LOGI("begin(n=%d, aplidudeLimit=%d", n, aplidudeLimit); - this->n = n; - this->amplidude_limit = aplidudeLimit; - this->priorLastAudioPos = n + 1; // ignore first values - this->active = n > 0; - } - - // find last position which contains audible data - int findLastAudioPos(T *audio, int pos) { - for (int j = 0; j < n; j++) { - // we are before the start of the current buffer - if (pos - j <= 0) { - return priorLastAudioPos; - } - // we are in the current buffer - if (abs(audio[pos - j]) > amplidude_limit) { - return j; - } - } - return n + 1; - } -}; - -/** - * @brief Big value gaps (at the beginning and the end of a recording) can lead - * to some popping sounds. We will try to set the values to 0 until the first - * transition thru 0 of the audio curve - * @ingroup convert - * @tparam T - */ -template -class PoppingSoundRemover : public BaseConverter { - public: - PoppingSoundRemover(int channels, bool fromBeginning, bool fromEnd) { - this->channels = channels; - from_beginning = fromBeginning; - from_end = fromEnd; - } - virtual size_t convert(uint8_t *src, size_t size) { - for (int ch = 0; ch < channels; ch++) { - if (from_beginning) - clearUpTo1stTransition(channels, ch, (T *)src, size / sizeof(T)); - if (from_end) - clearAfterLastTransition(channels, ch, (T *)src, size / sizeof(T)); - } - return size; - } - - protected: - bool from_beginning; - bool from_end; - int channels; - - void clearUpTo1stTransition(int channels, int channel, T *values, - int sampleCount) { - T first = values[channel]; - for (int j = 0; j < sampleCount; j += channels) { - T act = values[j]; - if ((first <= 0.0 && act >= 0.0) || (first >= 0.0 && act <= 0.0)) { - // we found the last transition so we are done - break; - } else { - values[j] = 0; - } - } - } - - void clearAfterLastTransition(int channels, int channel, T *values, - int sampleCount) { - int lastPos = sampleCount - channels + channel; - T last = values[lastPos]; - for (int j = lastPos; j >= 0; j -= channels) { - T act = values[j]; - if ((last <= 0.0 && act >= 0.0) || (last >= 0.0 && act <= 0.0)) { - // we found the last transition so we are done - break; - } else { - values[j] = 0; - } - } - } -}; - -/** - * @brief Changes the samples at the beginning or at the end to slowly ramp up - * the volume - * @ingroup convert - * @tparam T - */ -template -class SmoothTransition : public BaseConverter { - public: - SmoothTransition(int channels, bool fromBeginning, bool fromEnd, - float inc = 0.01) { - this->channels = channels; - this->inc = inc; - from_beginning = fromBeginning; - from_end = fromEnd; - } - virtual size_t convert(uint8_t *src, size_t size) { - for (int ch = 0; ch < channels; ch++) { - if (from_beginning) - processStart(channels, ch, (T *)src, size / sizeof(T)); - if (from_end) processEnd(channels, ch, (T *)src, size / sizeof(T)); - } - return size; - } - - protected: - bool from_beginning; - bool from_end; - int channels; - float inc = 0.01; - float factor = 0; - - void processStart(int channels, int channel, T *values, int sampleCount) { - for (int j = 0; j < sampleCount; j += channels) { - if (factor >= 0.8) { - break; - } else { - values[j] = factor * values[j]; - } - factor += inc; - } - } - - void processEnd(int channels, int channel, T *values, int sampleCount) { - int lastPos = sampleCount - channels + channel; - for (int j = lastPos; j >= 0; j -= channels) { - if (factor >= 0.8) { - break; - } else { - values[j] = factor * values[j]; - } - } - } -}; - -/** - * @brief Copy channel Cx value of type T shifted by S bits to all Cn channels - * @ingroup convert - * @tparam T, Cn, Cx, S - */ -template -class CopyChannels : public BaseConverter { - public: - CopyChannels() : _max_val(0), _counter(0), _prev_ms(0) {} - - size_t convert(uint8_t *src, size_t size) { - T *chan = (T *)src; - size_t samples = (size / Cn) / sizeof(T); - for (size_t s = 0; s < samples; s++) { - chan[s * Cn + Cx] = (Cx < Cn) ? chan[s * Cn + Cx] << S : 0; - - for (size_t c = 0; c < Cn; c++) { - if (c != Cx) { - chan[s * Cn + c] = chan[s * Cn + Cx]; - } - } - - if (_max_val < chan[s * Cn]) { - _max_val = chan[s * Cn]; - } - - _counter++; - uint32_t now = millis(); - if (now - _prev_ms > 1000) { - _prev_ms = now; - LOGI("CopyChannels samples: %u, amplitude: %d", _counter, _max_val); - _max_val = 0; - } - } - return samples * Cn * sizeof(T); - } - - private: - T _max_val; - uint32_t _counter; - uint32_t _prev_ms; -}; - -/** - * @brief You can provide a lambda expression to conver the data - * @ingroup convert - * @tparam T - */ -template -class CallbackConverterT : public BaseConverter { - public: - CallbackConverterT(T (*callback)(T in, int channel), int channels = 2) { - this->callback = callback; - this->channels = channels; - } - - size_t convert(uint8_t *src, size_t size) { - int samples = size / sizeof(T); - for (int j = 0; j < samples; j++) { - src[j] = callback(src[j], j % channels); - } - return size; - } - - protected: - T (*callback)(T in, int channel); - int channels; -}; - -} // namespace audio_tools diff --git a/src/AudioTools/CoreAudio/BaseStream.h b/src/AudioTools/CoreAudio/BaseStream.h deleted file mode 100644 index 0c5d5f2897..0000000000 --- a/src/AudioTools/CoreAudio/BaseStream.h +++ /dev/null @@ -1,616 +0,0 @@ -#pragma once -#include "AudioTools/CoreAudio/Buffers.h" -#include "AudioTools/CoreAudio/AudioTypes.h" -#include "AudioTools/CoreAudio/BaseConverter.h" - -#ifdef ARDUINO -#include "Stream.h" -#endif - -#ifdef USE_STREAM_WRITE_OVERRIDE -#define STREAM_WRITE_OVERRIDE override -#else -#define STREAM_WRITE_OVERRIDE -#endif - -#ifdef USE_STREAM_READ_OVERRIDE -#define STREAM_READ_OVERRIDE override -#else -#define STREAM_READ_OVERRIDE -#endif - -#ifdef USE_STREAM_READCHAR_OVERRIDE -#define STREAM_READCHAR_OVERRIDE override -#else -#define STREAM_READCHAR_OVERRIDE -#endif - -namespace audio_tools { - -/** - * @brief Base class for all Streams. It relies on write(const uint8_t *buffer, - * size_t size) and readBytes(uint8_t *buffer, size_t length). - * @author Phil Schatzmann - * @copyright GPLv3 - */ -class BaseStream : public Stream { - public: - BaseStream() = default; - virtual ~BaseStream() = default; - BaseStream(BaseStream const &) = delete; - BaseStream &operator=(BaseStream const &) = delete; - - virtual bool begin(){return true;} - virtual void end(){} - - virtual size_t readBytes(uint8_t *data, - size_t len) STREAM_READ_OVERRIDE = 0; - virtual size_t write(const uint8_t *data, size_t len) override = 0; - - virtual size_t write(uint8_t ch) override { - tmp_out.resize(MAX_SINGLE_CHARS); - if (tmp_out.isFull()) { - flush(); - } - return tmp_out.write(ch); - } - - virtual int available() override { return DEFAULT_BUFFER_SIZE; }; - - virtual int availableForWrite() override { return DEFAULT_BUFFER_SIZE; } - - virtual void flush() override { - if (tmp_out.available() > 0) { - write((const uint8_t *)tmp_out.address(), tmp_out.available()); - } - } - -// Methods which should be suppressed in the documentation -#ifndef DOXYGEN - - virtual size_t readBytes(char *data, size_t len) STREAM_READCHAR_OVERRIDE { - return readBytes((uint8_t *)data, len); - } - - virtual int read() override { - refillReadBuffer(); - // if it is empty we need to return an int -1 - if (tmp_in.isEmpty()) return -1; - uint8_t result = 0; - if (!tmp_in.read(result)) return -1; - return result; - } - - virtual int peek() override { - refillReadBuffer(); - // if it is empty we need to return an int -1 - if (tmp_in.isEmpty()) return -1; - uint8_t result = 0; - if (!tmp_in.peek(result)) return -1; - return result; - } - -#endif - - protected: - RingBuffer tmp_in{0}; - RingBuffer tmp_out{0}; - - void refillReadBuffer() { - tmp_in.resize(DEFAULT_BUFFER_SIZE); - if (tmp_in.isEmpty()) { - TRACED(); - const int len = tmp_in.size(); - uint8_t bytes[len]; - int len_eff = readBytes(bytes, len); - // LOGD("tmp_in available: %d / size: %d / to be written %d", - // tmp_in.available(), tmp_in.size(), len_eff); - tmp_in.writeArray(bytes, len_eff); - } - } -}; - -/** - * @brief Base class for all Audio Streams. It support the boolean operator to - * test if the object is ready with data - * @author Phil Schatzmann - * @copyright GPLv3 - */ -class AudioStream : public BaseStream, public AudioInfoSupport, public AudioInfoSource { - public: - AudioStream() = default; - virtual ~AudioStream() = default; - AudioStream(AudioStream const&) = delete; - AudioStream& operator=(AudioStream const&) = delete; - - // Call from subclass or overwrite to do something useful - virtual void setAudioInfo(AudioInfo newInfo) override { - TRACED(); - - if (info != newInfo){ - info = newInfo; - info.logInfo("in:"); - } - // replicate information - AudioInfo out_new = audioInfoOut(); - if (out_new) { - out_new.logInfo("out:"); - notifyAudioChange(out_new); - } - - } - - virtual size_t readBytes(uint8_t *data, size_t len) override { return not_supported(0, "readBytes"); } - - virtual size_t write(const uint8_t *data, size_t len) override{ return not_supported(0,"write"); } - - - virtual operator bool() { return info && available() > 0; } - - virtual AudioInfo audioInfo() override { - return info; - } - - - /// Writes len bytes of silence (=0). - virtual void writeSilence(size_t len){ - int16_t zero = 0; - for (int j=0;jreadBytes(data, len); - } - - /// Returns true if active and we still have data - operator bool() { return is_active && available() > 0; } - - void setOnBeginCallback(void (*callback)(Stream *stream)) { - begin_callback = callback; - } - void setOnEndCallback(void (*callback)(Stream *stream)) { - end_callback = callback; - } - - /// Defines the timout the system waits for data when moving to the next stream - void setTimeout(size_t t) { _timeout = t; } - - /// not supported - size_t write(const uint8_t *data, size_t size) override { return 0;}; - - protected: - Vector input_streams; - Stream *p_current_stream = nullptr; - bool is_active = false; - void (*begin_callback)(Stream *stream) = nullptr; - void (*end_callback)(Stream *stream) = nullptr; - size_t _timeout = 0; - - /// moves to the next stream if necessary: returns true if we still have a - /// valid stream - bool moveToNextStreamOnEnd() { - // keep on running - if (p_current_stream != nullptr && p_current_stream->available() > 0) - return true; - // at end? - if ((p_current_stream == nullptr || availableWithTimout() == 0)) { - if (end_callback && p_current_stream) end_callback(p_current_stream); - if (!input_streams.empty()) { - LOGI("using next stream"); - p_current_stream = input_streams[0]; - input_streams.pop_front(); - if (begin_callback && p_current_stream) - begin_callback(p_current_stream); - } else { - p_current_stream = nullptr; - } - } - // returns true if we have a valid stream - return p_current_stream != nullptr; - } - - int availableWithTimout() { - int result = p_current_stream->available(); - if (result == 0) { - for (int j = 0; j < _timeout / 10; j++) { - delay(10); - result = p_current_stream->available(); - if (result != 0) break; - } - } - return result; - } -}; - -/** - * @brief The Arduino Stream which provides silence and simulates a null device - * when used as audio target or audio source - * @ingroup io - * @author Phil Schatzmann - * @copyright GPLv3 - */ -class NullStream : public BaseStream { - public: - size_t write(const uint8_t *data, size_t len) override { return len; } - - size_t readBytes(uint8_t *data, size_t len) override { - memset(data, 0, len); - return len; - } -}; - - -/** - * @brief Stream class which stores the data in a temporary queue buffer. - * The queue can be consumed e.g. by a callback function by calling readBytes(); - * @ingroup io - * @author Phil Schatzmann - * @copyright GPLv3 - */ -template -class QueueStream : public BaseStream { - public: - /// Empty Constructor: call setBuffer() to set the buffer - QueueStream() = default; - /// Default constructor - QueueStream(int bufferSize, int bufferCount, - bool autoRemoveOldestDataIfFull = false) { - owns_buffer = true; - callback_buffer_ptr = new NBuffer(bufferSize, bufferCount); - remove_oldest_data = autoRemoveOldestDataIfFull; - } - /// Create stream from any BaseBuffer subclass - QueueStream(BaseBuffer &buffer) { - setBuffer(buffer); - } - - virtual ~QueueStream() { - if (owns_buffer) { - delete callback_buffer_ptr; - } - } - - void setBuffer(BaseBuffer &buffer){ - owns_buffer = false; - callback_buffer_ptr = &buffer; - } - - /// Activates the output - virtual bool begin() override { - TRACED(); - total_written = 0; - active = true; - return true; - } - - /// Activate only when filled buffer reached % - virtual bool begin(size_t activeWhenPercentFilled) { - total_written = 0; - // determine total buffer size in bytes - size_t size = callback_buffer_ptr->size() * sizeof(T); - // calculate limit - active_limit = size * activeWhenPercentFilled / 100; - LOGI("activate after: %u bytes",(unsigned)active_limit); - return true; - } - - /// stops the processing - virtual void end() override { - TRACED(); - active = false; - }; - - int available() override { - return active ? callback_buffer_ptr->available() * sizeof(T) : 0; - } - - int availableForWrite() override { - if (!active && active_limit > 0) return DEFAULT_BUFFER_SIZE; - return callback_buffer_ptr->availableForWrite() * sizeof(T); - } - - virtual size_t write(const uint8_t *data, size_t len) override { - if (len == 0) return 0; - if (active_limit == 0 && !active) return 0; - - // activate automaticaly when limit has been reached - total_written += len; - if (!active && active_limit > 0 && total_written >= active_limit) { - this->active = true; - LOGI("setting active"); - } - - // make space by deleting oldest entries - if (remove_oldest_data) { - int available_bytes = - callback_buffer_ptr->availableForWrite() * sizeof(T); - if ((int)len > available_bytes) { - int gap = len - available_bytes; - uint8_t tmp[gap]; - readBytes(tmp, gap); - } - } - - return callback_buffer_ptr->writeArray(data, len / sizeof(T)); - } - - virtual size_t readBytes(uint8_t *data, size_t len) override { - if (!active) return 0; - return callback_buffer_ptr->readArray(data, len / sizeof(T)); - } - - /// Clears the data in the buffer - void clear() { - if (active) { - callback_buffer_ptr->reset(); - } - } - - /// Returns true if active - operator bool() { return active; } - - /// Returns the fill level in percent - int levelPercent() {return callback_buffer_ptr->levelPercent();} - - protected: - BaseBuffer *callback_buffer_ptr; - size_t active_limit = 0; - size_t total_written = 0; - bool active = false; - bool remove_oldest_data = false; - bool owns_buffer = false; -}; - - -#ifndef SWIG - -struct DataNode { - size_t len=0; - Vector data{0}; - - DataNode() = default; - /// Constructor - DataNode(void*inData, int len){ - this->len = len; - this->data.resize(len); - memcpy(&data[0], inData, len); - } -}; - -/** - * @brief MemoryStream which is written and read using the internal RAM. For each write the data is allocated - * on the heap. - * @ingroup io - * @author Phil Schatzmann - * @copyright GPLv3 - */ -class DynamicMemoryStream : public BaseStream { -public: - - DynamicMemoryStream() = default; - - DynamicMemoryStream(bool isLoop, int defaultBufferSize=DEFAULT_BUFFER_SIZE ) { - this->default_buffer_size = defaultBufferSize; - is_loop = isLoop; - } - // Assign values from ref, clearing the original ref - void assign(DynamicMemoryStream &ref){ - audio_list.swap(ref.audio_list); - it = ref.it; - total_available=ref.total_available; - default_buffer_size = ref.default_buffer_size; - alloc_failed = ref.alloc_failed;; - is_loop = ref.is_loop; - ref.clear(); - } - - /// Intializes the processing - virtual bool begin() override { - clear(); - temp_audio.resize(default_buffer_size); - return true; - } - - virtual void end() override { - clear(); - } - - /// Automatically rewinds to the beginning when reaching the end - virtual void setLoop(bool loop){ - is_loop = loop; - } - - void clear() { - DataNode *p_node; - bool ok; - do{ - ok = audio_list.pop_front(p_node); - if (ok){ - delete p_node; - } - } while (ok); - - temp_audio.reset(); - total_available = 0; - alloc_failed = false; - rewind(); - } - - size_t size(){ - return total_available; - } - - /// Sets the read position to the beginning - void rewind() { - it = audio_list.begin(); - } - - virtual size_t write(const uint8_t *data, size_t len) override { - DataNode *p_node = new DataNode((void*)data, len); - if (p_node->data){ - alloc_failed = false; - total_available += len; - audio_list.push_back(p_node); - - // setup interator to point to first record - if (it == audio_list.end()){ - it = audio_list.begin(); - } - - return len; - } - alloc_failed = true; - return 0; - } - - virtual int availableForWrite() override { - return alloc_failed ? 0 : default_buffer_size; - } - - virtual int available() override { - if (it == audio_list.end()){ - if (is_loop) rewind(); - if (it == audio_list.end()) { - return 0; - } - } - return (*it)->len; - } - - virtual size_t readBytes(uint8_t *data, size_t len) override { - // provide unprocessed data - if (temp_audio.available()>0){ - return temp_audio.readArray(data, len); - } - - // We have no more data - if (it==audio_list.end()){ - if (is_loop){ - rewind(); - } else { - // stop the processing - return 0; - } - } - - // provide data from next node - DataNode *p_node = *it; - int result_len = min(len, (size_t) p_node->len); - memcpy(data, &p_node->data[0], result_len); - // save unprocessed data to temp buffer - if (p_node->len>len){ - uint8_t *start = &p_node->data[result_len]; - int uprocessed_len = p_node->len - len; - temp_audio.writeArray(start, uprocessed_len); - } - //move to next pos - ++it; - return result_len; - } - - List &list() { - return audio_list; - } - - /// @brief Post processing after the recording. We add a smooth transition at the beginning and at the end - /// @tparam T - /// @param factor - template - void postProcessSmoothTransition(int channels, float factor = 0.01, int remove=0){ - if (remove>0){ - for (int j=0;j clean_start(channels, true, false, factor); - auto first = *list().begin(); - if (first!=nullptr){ - clean_start.convert(&(first->data[0]),first->len); - } - - SmoothTransition clean_end(channels, false, true, factor); - auto last = * (--(list().end())); - if (last!=nullptr){ - clean_end.convert(&(last->data[0]),last->len); - } - } - - -protected: - List audio_list; - List::Iterator it = audio_list.end(); - size_t total_available=0; - int default_buffer_size=DEFAULT_BUFFER_SIZE; - bool alloc_failed = false; - RingBuffer temp_audio{DEFAULT_BUFFER_SIZE}; - bool is_loop = false; - -}; - -#endif - -} // namespace audio_tools \ No newline at end of file diff --git a/src/AudioTools/CoreAudio/Buffers.h b/src/AudioTools/CoreAudio/Buffers.h deleted file mode 100644 index c4a2d7b81d..0000000000 --- a/src/AudioTools/CoreAudio/Buffers.h +++ /dev/null @@ -1,1089 +0,0 @@ -#pragma once - -#include "AudioTools/CoreAudio/AudioBasic/Collections.h" -#include "AudioTools/CoreAudio/AudioBasic/Str.h" -#include "AudioTools/CoreAudio/AudioLogger.h" - -/** - * @defgroup buffers Buffers - * @ingroup tools - * @brief Different Buffer Implementations - */ - -namespace audio_tools { - -/** - * @brief Shared functionality of all buffers - * @ingroup buffers - * @author Phil Schatzmann - * @copyright GPLv3 - */ -template -class BaseBuffer { - public: - BaseBuffer() = default; - virtual ~BaseBuffer() = default; - BaseBuffer(BaseBuffer const &) = delete; - // BaseBuffer &operator=(BaseBuffer const &) = delete; - - /// reads a single value - virtual bool read(T &result) = 0; - - /// reads multiple values - virtual int readArray(T data[], int len) { - if (data == nullptr) { - LOGE("NPE"); - return 0; - } - int lenResult = min(len, available()); - for (int j = 0; j < lenResult; j++) { - read(data[j]); - } - LOGD("readArray %d -> %d", len, lenResult); - return lenResult; - } - - /// Removes the next len entries - virtual int clearArray(int len) { - int lenResult = min(len, available()); - T dummy[lenResult]; - readArray(dummy, lenResult); - return lenResult; - } - - /// Fills the buffer data - virtual int writeArray(const T data[], int len) { - // LOGD("%s: %d", LOG_METHOD, len); - // CHECK_MEMORY(); - - int result = 0; - for (int j = 0; j < len; j++) { - if (!write(data[j])) { - break; - } - result = j + 1; - } - // CHECK_MEMORY(); - LOGD("writeArray %d -> %d", len, result); - return result; - } - - /// Fills the buffer data and overwrites the oldest data if the buffer is full - virtual int writeArrayOverwrite(const T data[], int len) { - int to_delete = len - availableForWrite(); - if (to_delete > 0) { - clearArray(to_delete); - } - return writeArray(data, len); - } - - /// peeks the actual entry from the buffer - virtual bool peek(T &result) = 0; - - /// checks if the buffer is full - virtual bool isFull() { return availableForWrite() == 0; } - - bool isEmpty() { return available() == 0; } - - /// write add an entry to the buffer - virtual bool write(T data) = 0; - - /// clears the buffer - virtual void reset() = 0; - - /// same as reset - void clear() { reset(); } - - /// provides the number of entries that are available to read - virtual int available() = 0; - - /// provides the number of entries that are available to write - virtual int availableForWrite() = 0; - - /// returns the address of the start of the physical read buffer - virtual T *address() = 0; - - virtual size_t size() = 0; - - /// Returns the level of the buffer in % - virtual float levelPercent() { - // prevent div by 0. - if (size() == 0) return 0.0f; - return 100.0f * static_cast(available()) / - static_cast(size()); - } - - /// Resizes the buffer if supported: returns false if not supported - virtual bool resize(int bytes) { - LOGE("resize not implemented for this buffer"); - return false; - } -}; - -/*** - * @brief A FrameBuffer reads multiple values for array of 2 dimensional frames - */ -template -class FrameBuffer { - public: - FrameBuffer(BaseBuffer &buffer) { p_buffer = &buffer; } - /// reads multiple values for array of 2 dimensional frames - int readFrames(T data[][2], int len) { - LOGD("%s: %d", LOG_METHOD, len); - // CHECK_MEMORY(); - int result = min(len, p_buffer->available()); - for (int j = 0; j < result; j++) { - T sample = 0; - p_buffer->read(sample); - data[j][0] = sample; - data[j][1] = sample; - } - // CHECK_MEMORY(); - return result; - } - - template - int readFrames(T (&data)[rows][channels]) { - int lenResult = min(rows, p_buffer->available()); - for (int j = 0; j < lenResult; j++) { - T sample = 0; - p_buffer->read(sample); - for (int i = 0; i < channels; i++) { - // data[j][i] = htons(sample); - data[j][i] = sample; - } - } - return lenResult; - } - - protected: - BaseBuffer *p_buffer = nullptr; -}; - -/** - * @brief A simple Buffer implementation which just uses a (dynamically sized) - * array - * @ingroup buffers - * @author Phil Schatzmann - * @copyright GPLv3 - */ - -template -class SingleBuffer : public BaseBuffer { - public: - /** - * @brief Construct a new Single Buffer object - * - * @param size in entries - */ - SingleBuffer(int size) { - buffer.resize(size); - reset(); - } - - /** - * @brief Construct a new Single Buffer w/o allocating any memory - */ - SingleBuffer() { reset(); } - - /// notifies that the external buffer has been refilled - void onExternalBufferRefilled(void *data, int len) { - this->owns_buffer = false; - this->buffer = (uint8_t *)data; - this->current_read_pos = 0; - this->current_write_pos = len; - } - - int writeArray(const T data[], int len) override { - if (size() == 0) resize(len); - return BaseBuffer::writeArray(data, len); - } - - bool write(T sample) override { - bool result = false; - if (current_write_pos < buffer.size()) { - buffer[current_write_pos++] = sample; - result = true; - } - return result; - } - - bool read(T &result) override { - bool success = false; - if (current_read_pos < current_write_pos) { - result = buffer[current_read_pos++]; - success = true; - } - return success; - } - - bool peek(T &result) override { - bool success = false; - if (current_read_pos < current_write_pos) { - result = buffer[current_read_pos]; - success = true; - } - return success; - } - - int available() override { - int result = current_write_pos - current_read_pos; - return max(result, 0); - } - - int availableForWrite() override { return buffer.size() - current_write_pos; } - - bool isFull() override { return availableForWrite() <= 0; } - - /// consumes len bytes and moves current data to the beginning - int clearArray(int len) override { - int len_available = available(); - if (len > available()) { - reset(); - return len_available; - } - current_read_pos += len; - len_available -= len; - memmove(buffer.data(), buffer.data() + current_read_pos, len_available); - current_read_pos = 0; - current_write_pos = len_available; - - if (is_clear_with_zero) { - memset(buffer.data() + current_write_pos, 0, - buffer.size() - current_write_pos); - } - - return len; - } - - /// Provides address to beginning of the buffer - T *address() override { return buffer.data(); } - - /// Provides address of actual data - T *data() { return buffer.data() + current_read_pos; } - - void reset() override { - current_read_pos = 0; - current_write_pos = 0; - if (is_clear_with_zero) { - memset(buffer.data(), 0, buffer.size()); - } - } - - /// If we load values directly into the address we need to set the avialeble - /// size - size_t setAvailable(size_t available_size) { - size_t result = min(available_size, (size_t)buffer.size()); - current_read_pos = 0; - current_write_pos = result; - return result; - } - - size_t size() override { return buffer.size(); } - - bool resize(int size) { - if (buffer.size() != size) { - TRACED(); - buffer.resize(size); - } - return true; - } - - /// Sets the buffer to 0 on clear - void setClearWithZero(bool flag) { is_clear_with_zero = flag; } - - /// Updates the actual available data size - void setWritePos(int pos) { current_write_pos = pos; } - - /// Optional ID - int id = 0; - /// Optional active/inactive status - bool active = true; - /// Optional timestamp - uint64_t timestamp = 0; - - protected: - int current_read_pos = 0; - int current_write_pos = 0; - bool owns_buffer = true; - bool is_clear_with_zero = false; - Vector buffer{0}; -}; - -/** - * @brief Implements a typed Ringbuffer - * @ingroup buffers - * @tparam T - */ -template -class RingBuffer : public BaseBuffer { - public: - RingBuffer(int size) { - resize(size); - reset(); - } - - bool read(T &result) override { - if (isEmpty()) { - return false; - } - - result = _aucBuffer[_iTail]; - _iTail = nextIndex(_iTail); - _numElems--; - - return true; - } - - // peeks the actual entry from the buffer - bool peek(T &result) override { - if (isEmpty()) { - return false; - } - - result = _aucBuffer[_iTail]; - return true; - } - - virtual int peekArray(T *data, int n) { - if (isEmpty()) return -1; - int result = 0; - int count = _numElems; - int tail = _iTail; - for (int j = 0; j < n; j++) { - data[j] = _aucBuffer[tail]; - tail = nextIndex(tail); - count--; - result++; - if (count == 0) break; - } - return result; - } - - // checks if the buffer is full - virtual bool isFull() override { return available() == max_size; } - - bool isEmpty() { return available() == 0; } - - // write add an entry to the buffer - virtual bool write(T data) override { - bool result = false; - if (!isFull()) { - _aucBuffer[_iHead] = data; - _iHead = nextIndex(_iHead); - _numElems++; - result = true; - } - return result; - } - - // clears the buffer - virtual void reset() override { - _iHead = 0; - _iTail = 0; - _numElems = 0; - } - - // provides the number of entries that are available to read - virtual int available() override { return _numElems; } - - // provides the number of entries that are available to write - virtual int availableForWrite() override { return (max_size - _numElems); } - - // returns the address of the start of the physical read buffer - virtual T *address() override { return _aucBuffer.data(); } - - virtual bool resize(int len) { - if (max_size != len) { - LOGI("resize: %d", len); - _aucBuffer.resize(len); - max_size = len; - } - return true; - } - - /// Returns the maximum capacity of the buffer - virtual size_t size() override { return max_size; } - - protected: - Vector _aucBuffer; - int _iHead; - int _iTail; - int _numElems; - int max_size = 0; - - int nextIndex(int index) { return (uint32_t)(index + 1) % max_size; } -}; - -/** - * @brief An File backed Ring Buffer that we can use to receive - * streaming audio. We expect an open file as parameter. - * - * @ingroup buffers - * @tparam File: file class - * @tparam T: the buffered object type - */ -template -class RingBufferFile : public BaseBuffer { - public: - RingBufferFile(int size) { resize(size); } - RingBufferFile(int size, File &file) { - resize(size); - begin(file); - } - ~RingBufferFile() { - if (p_file) p_file->close(); - } - - /// Assigns the p_file to be used. - bool begin(File &bufferFile) { - if (bufferFile) { - p_file = &bufferFile; - } else { - LOGE("file is not valid"); - } - return bufferFile; - } - - /// Reads a single value from the buffer - bool read(T &result) override { return readArray(&result, 1) == 1; } - - /// reads multiple values - int readArray(T data[], int count) override { - if (p_file == nullptr) return 0; - int read_count = min(count, available()); - - OffsetInfo offset = getOffset(read_pos, read_count); - if (!file_seek(offset.pos)) return false; - int n = file_read(data, offset.len); - if (offset.len1 > 0) { - file_seek(0); - n += file_read(data + offset.len, offset.len1); - read_pos = offset.len1; - } else { - read_pos += read_count; - } - assert(n == read_count); - element_count -= read_count; - return read_count; - } - - /// peeks the actual entry from the buffer - bool peek(T &result) override { - if (p_file == nullptr || isEmpty()) { - return false; - } - - if (!file_seek(read_pos)) return false; - size_t count = file_read(&result, 1); - return count == 1; - } - - /// gets multiple values w/o removing them - int peekArray(T data[], int count) { - if (p_file == nullptr) return 0; - int read_count = min(count, available()); - - OffsetInfo offset = getOffset(read_pos, read_count); - if (!file_seek(offset.pos)) return false; - int n = file_read(data, offset.len); - if (offset.len1 > 0) { - file_seek(0); - n += file_read(data + offset.len, offset.len1); - } - assert(n == read_count); - return read_count; - } - - /// write add a single entry to the buffer - bool write(T data) override { return writeArray(&data, 1); } - - /// Fills the data from the buffer - int writeArray(const T data[], int len) override { - if (p_file == nullptr) return 0; - - int write_count = min(len, availableForWrite()); - OffsetInfo offset = getOffset(write_pos, write_count); - - if (!file_seek(offset.pos)) return false; - int n = file_write(data, offset.len); - if (offset.len1 > 0) { - file_seek(0); - n += file_write(data + offset.len, offset.len1); - write_pos = offset.len1; - } else { - write_pos += write_count; - } - assert(n == write_count); - element_count += write_count; - return write_count; - } - - /// checks if the buffer is full - bool isFull() override { return available() == max_size; } - - bool isEmpty() { return available() == 0; } - - /// clears the buffer - void reset() override { - write_pos = 0; - read_pos = 0; - element_count = 0; - if (p_file != nullptr) file_seek(0); - } - - /// provides the number of entries that are available to read - int available() override { return element_count; } - - /// provides the number of entries that are available to write - int availableForWrite() override { return (max_size - element_count); } - - /// Provides the capacity - size_t size() override { return max_size; } - - /// Defines the capacity - bool resize(int size) { max_size = size; return true; } - - // not supported - T *address() override { return nullptr; } - - protected: - File *p_file = nullptr; - int write_pos = 0; - int read_pos = 0; - int element_count = 0; - int max_size = 0; - - struct OffsetInfo { - int pos = 0; // start pos - int len = 0; // length of first part - int len1 = 0; // length of second part on overflow - }; - - /// Get positons and sizes to handle overflow wrapping to prevent writing past - /// max_size - OffsetInfo getOffset(int pos, int len) { - OffsetInfo result; - result.pos = pos; - int overflow = (pos + len) - max_size; - if (overflow <= 0) { - // we can write the complete data - result.len = len; - result.len1 = 0; - } else { - // we need to split the data - result.len = len - overflow; - result.len1 = overflow; - } - return result; - } - - /// Seeks to the given object position - bool file_seek(int pos) { - int file_pos = pos * sizeof(T); - if (p_file->position() != file_pos) { - LOGD("file_seek: %d", pos); - if (!p_file->seek(file_pos)) { - LOGE("seek %d", file_pos); - return false; - } - } - return true; - } - - /// Reed the indicated number of objects - int file_write(const T *data, int count) { - LOGD("file_write: %d", count); - if (p_file == nullptr) return 0; - int to_write = sizeof(T) * count; - int bytes_written = p_file->write((const uint8_t *)data, to_write); - p_file->flush(); - int elements_written = bytes_written / sizeof(T); - if (bytes_written != to_write) { - LOGE("write: %d -> %d", to_write, bytes_written); - } - return elements_written; - } - - /// Writes the indicated number of objects - int file_read(T *result, int count) { - LOGD("file_read: %d", count); - int read_bytes = count * sizeof(T); - int result_bytes = p_file->readBytes((char *)result, read_bytes); - int result_count = result_bytes / sizeof(T); - if (result_count != count) { - LOGE("readBytes: %d -> %d", read_bytes, result_bytes); - } - return result_count; - } -}; - -/** - * @brief A lock free N buffer. If count=2 we create a DoubleBuffer, if - * count=3 a TripleBuffer etc. - * @ingroup buffers - * @author Phil Schatzmann - * @copyright GPLv3 - */ -template -class NBuffer : public BaseBuffer { - public: - NBuffer(int size, int count) { resize(size, count); } - - virtual ~NBuffer() { freeMemory(); } - - /// reads an entry from the buffer - bool read(T &result) override { - if (available() == 0) return false; - return actual_read_buffer->read(result); - } - - /// peeks the actual entry from the buffer - bool peek(T &result) override { - if (available() == 0) return false; - return actual_read_buffer->peek(result); - } - - /// checks if the buffer is full - bool isFull() { return availableForWrite() == 0; } - - /// write add an entry to the buffer - bool write(T data) { - bool result = false; - if (actual_write_buffer == nullptr) { - actual_write_buffer = getNextAvailableBuffer(); - } - if (actual_write_buffer != nullptr) { - result = actual_write_buffer->write(data); - // if buffer is full move to next available - if (actual_write_buffer->isFull()) { - addFilledBuffer(actual_write_buffer); - actual_write_buffer = getNextAvailableBuffer(); - } - } - - if (start_time == 0l) { - start_time = millis(); - } - if (result) sample_count++; - - return result; - } - - /// determines the available entries for the current read buffer - int available() { - if (actual_read_buffer == nullptr) { - actual_read_buffer = getNextFilledBuffer(); - } - if (actual_read_buffer == nullptr) { - return 0; - } - int result = actual_read_buffer->available(); - if (result == 0) { - // make current read buffer available again - resetCurrent(); - result = - (actual_read_buffer == nullptr) ? 0 : actual_read_buffer->available(); - } - return result; - } - - /// determines the available entries for the write buffer - int availableForWrite() { - if (actual_write_buffer == nullptr) { - actual_write_buffer = getNextAvailableBuffer(); - } - // if we used up all buffers - there is nothing available any more - if (actual_write_buffer == nullptr) { - return 0; - } - // check on actual buffer - if (actual_write_buffer->isFull()) { - // if buffer is full we move it to filled buffers ang get the next - // available - addFilledBuffer(actual_write_buffer); - actual_write_buffer = getNextAvailableBuffer(); - } - return actual_write_buffer->availableForWrite(); - } - - /// resets all buffers - void reset() { - TRACED(); - while (actual_read_buffer != nullptr) { - actual_read_buffer->reset(); - addAvailableBuffer(actual_read_buffer); - // get next read buffer - actual_read_buffer = getNextFilledBuffer(); - } - } - - /// provides the actual sample rate - unsigned long sampleRate() { - unsigned long run_time = (millis() - start_time); - return run_time == 0 ? 0 : sample_count * 1000 / run_time; - } - - /// returns the address of the start of the phsical read buffer - T *address() { - return actual_read_buffer == nullptr ? nullptr - : actual_read_buffer->address(); - } - - /// Provides the number of entries that are available to read - virtual int bufferCountFilled() { return filled_buffers.size(); } - - /// Provides the number of entries that are available to write - virtual int bufferCountEmpty() { return available_buffers.size(); } - - virtual bool resize(int bytes){ - int count = bytes / buffer_size; - return resize(buffer_size, count); - } - - /// Resize the buffers by defining a new buffer size and buffer count - virtual bool resize(int size, int count) { - if (buffer_size == size && buffer_count == count) return true; - freeMemory(); - filled_buffers.resize(count); - available_buffers.resize(count); - // filled_buffers.clear(); - // available_buffers.clear(); - - buffer_count = count; - buffer_size = size; - for (int j = 0; j < count; j++) { - BaseBuffer *buffer = new SingleBuffer(size); - LOGD("new buffer %p", buffer); - available_buffers.enqueue(buffer); - } - return true; - } - - /// Provides the total capacity (=buffer size * buffer count) - size_t size() { return buffer_size * buffer_count; } - - protected: - int buffer_size = 1024; - uint16_t buffer_count = 0; - BaseBuffer *actual_read_buffer = nullptr; - BaseBuffer *actual_write_buffer = nullptr; - QueueFromVector *> available_buffers{0, nullptr}; - QueueFromVector *> filled_buffers{0, nullptr}; - unsigned long start_time = 0; - unsigned long sample_count = 0; - - /// empty constructor only allowed by subclass - NBuffer() = default; - - void freeMemory() { - if (actual_write_buffer) { - LOGD("deleting %p", actual_write_buffer); - delete actual_write_buffer; - actual_write_buffer = nullptr; - } - if (actual_read_buffer) { - LOGD("deleting %p", actual_read_buffer); - delete actual_read_buffer; - actual_read_buffer = nullptr; - } - - BaseBuffer *ptr = getNextAvailableBuffer(); - while (ptr != nullptr) { - LOGD("deleting %p", ptr); - delete ptr; - ptr = getNextAvailableBuffer(); - } - - ptr = getNextFilledBuffer(); - while (ptr != nullptr) { - LOGD("deleting %p", ptr); - delete ptr; - ptr = getNextFilledBuffer(); - } - } - - void resetCurrent() { - if (actual_read_buffer != nullptr) { - actual_read_buffer->reset(); - addAvailableBuffer(actual_read_buffer); - } - // get next read buffer - actual_read_buffer = getNextFilledBuffer(); - } - - virtual BaseBuffer *getNextAvailableBuffer() { - if (available_buffers.empty()) return nullptr; - BaseBuffer *result = nullptr; - available_buffers.dequeue(result); - return result; - } - - virtual bool addAvailableBuffer(BaseBuffer *buffer) { - return available_buffers.enqueue(buffer); - } - - virtual BaseBuffer *getNextFilledBuffer() { - if (filled_buffers.empty()) return nullptr; - BaseBuffer *result = nullptr; - filled_buffers.dequeue(result); - return result; - } - - virtual bool addFilledBuffer(BaseBuffer *buffer) { - return filled_buffers.enqueue(buffer); - } -}; - -/** - * @brief A NBufferExt is a subclass of NBuffer which allows to use a - * direct access API to the BaseBuffer. - * @ingroup buffers - * @tparam T: buffered data type - */ -template -class NBufferExt : public NBuffer { - public: - NBufferExt(int size, int count) { resize(size, count); } - - /// Alternative interface: Provides access to the next write - /// buffer - SingleBuffer *writeEnd() { - actual_write_buffer = getNextAvailableBuffer(); - if (actual_write_buffer != nullptr) { - addFilledBuffer(actual_write_buffer); - } - return (SingleBuffer *)actual_write_buffer; - } - - /// Alternative interface using address: provides access to the next read buffer - SingleBuffer *readEnd() { - // make current read buffer available again - resetCurrent(); - return (SingleBuffer *)actual_read_buffer; - } - - /// Provides the buffer with the indicated id - SingleBuffer *getBuffer(int id) { - for (auto &buffer : this->filled_buffers.toVector()) { - SingleBuffer* sbuffer = (SingleBuffer *)&buffer; - if (sbuffer->id == id) { - return sbuffer; - } - } - return nullptr; - } - - using NBuffer::resize; - - protected: - using NBuffer::resetCurrent; - using NBuffer::addFilledBuffer; - using NBuffer::getNextAvailableBuffer; - using NBuffer::actual_write_buffer; - using NBuffer::actual_read_buffer; - using NBuffer::buffer_size; -}; - -/*** - * @brief A File backed buffer which uses the provided files for buffering with - * the indicated max size. A file is made available for reading as soon as it - * reached the size limit. You must provide the files opened in "Write" mode - * with the addFile() method! - * @ingroup buffers - * @tparam File: file class - * @tparam T: buffered data type - */ -template -class NBufferFile : public BaseBuffer { - public: - /// Provide the file size in objects! - NBufferFile(int fileSize) { number_of_objects_per_file = fileSize; } - /// RAII close the files - ~NBufferFile() { end(); } - - /// Determines the next unique file name (after calling addFile) - const char *nextFileName() { - next_file_name.set("buffer-"); - char number[40]; - snprintf(number, 40, "%d", file_count); - next_file_name.add(number); - next_file_name.add(".tmp"); - return next_file_name.c_str(); - } - - /// add a buffer file, opened in Write mode. If it already contains any - /// content it will be overwritten - bool addFile(File &file) { - if (!file) return false; - empty_files.enqueue(file); - file_count++; - return true; - } - - bool read(T &result) override { return readArray(&result, 1) == 1; } - - int readArray(T data[], int len) override { - // make sure we have a read file - if (!read_file) { - if (!filled_files.dequeue(read_file)) { - // no more data - return 0; - } - read_file.seek(0); - } - // read the data - int result = read_file.readBytes((char *)data, len * sizeof(T)) / sizeof(T); - - // if we have consumed all content - if (result < len) { - read_file.seek(0); - empty_files.enqueue(read_file); - read_file = empty; - } - return result; - } - - bool peek(T &data) override { - size_t pos = read_file.position(); - bool result = read(data); - read_file.seek(pos); - return result; - } - - bool write(T sample) override { return writeArray(&sample, 1) == 1; } - - int writeArray(const T data[], int len) override { - if (!write_file || write_file.size() + len > number_of_objects_per_file) { - // moved to filled files - if (write_file) { - write_file.seek(0); - filled_files.enqueue(write_file); - } - // get next empty file - if (!empty_files.dequeue(write_file)) return false; - } - int result = write_file.write((uint8_t *)data, len * sizeof(T)); - return result / sizeof(T); - } - - int available() override { - return filled_files.size() * number_of_objects_per_file + - (read_file.available() / sizeof(T)); - } - - // provides the number of entries that are available to write - int availableForWrite() override { - int open_current = - number_of_objects_per_file - (write_file.available() / sizeof(T)); - return empty_files.size() * number_of_objects_per_file + - write_file.available() + open_current; - } - - size_t size() override { return number_of_objects_per_file * file_count; } - - /// clean up files - void end() { - cleanupFile(read_file); - cleanupFile(write_file); - File file; - while (empty_files.dequeue(file)) cleanupFile(file); - while (filled_files.dequeue(file)) cleanupFile(file); - } - - /// Define the file delete operation - void setFileDeleteCallback(void (*cb)(const char *filename)) { - file_delete_callback = cb; - } - - void reset() { - if (read_file) { - read_file.seek(0); - empty_files.enqueue(read_file); - read_file = empty; - } - if (write_file) { - write_file.seek(0); - empty_files.enqueue(write_file); - write_file = empty; - } - File file; - while (filled_files.dequeue(file)) { - file.seek(0); - empty_files.enqueue(file); - } - } - /// not supported - T *address() { return nullptr; } - - protected: - Queue empty_files; - Queue filled_files; - File read_file; - File write_file; - File empty; - int number_of_objects_per_file = 0; // number of objects per file - int file_count = 0; // number of files - const uint16_t max_file_name = 256; - Str next_file_name; - void (*file_delete_callback)(const char *filename); - - void cleanupFile(File &file) { - if (!file) return; - // after close the file name is gone - int len = strlen(file.name()); - char file_name[len + 1]; - strncpy(file_name, file.name(), len); - file.close(); - file_delete_callback(file_name); - } -}; - -/** - * @brief Class which is usfull ot provide incremental data access e.g. for - * EdgeImpulse which request data with an offset and length starting from 0 up - * to the buffer length, restarting at 0 again. - * @ingroup buffers - * @author Phil Schatzmann - * @copyright GPLv3 - */ - -template -class BufferedArray { - public: - BufferedArray(Stream &input, int len) { - LOGI("BufferedArray(%d)", len); - array.resize(len); - p_stream = &input; - } - // access values, the offset and length are specified in samples of type - int16_t *getValues(size_t offset, size_t length) { - LOGD("getValues(%d,%d) - max %d", offset, length, array.size()); - if (offset == 0) { - // we restart at the beginning - last_end = 0; - actual_end = length; - } else { - // if first position is at end we do not want to read the full buffer - last_end = actual_end >= 0 ? actual_end : offset; - // increase actual end if bigger then old - actual_end = offset + length > actual_end ? offset + length : actual_end; - } - int size = actual_end - last_end; - if (size > 0) { - LOGD("readBytes(%d,%d)", last_end, size); - assert(last_end + size <= array.size()); - p_stream->readBytes((uint8_t *)(&array[last_end]), size * 2); - } - assert(offset < actual_end); - return &array[offset]; - } - - protected: - int actual_end = -1; - int last_end = 0; - Vector array; - Stream *p_stream = nullptr; -}; - -} // namespace audio_tools diff --git a/src/AudioTools/CoreAudio/I2SStream.h b/src/AudioTools/CoreAudio/I2SStream.h deleted file mode 100644 index 3ae8e7554f..0000000000 --- a/src/AudioTools/CoreAudio/I2SStream.h +++ /dev/null @@ -1,3 +0,0 @@ -#pragma once - -#include "AudioTools/CoreAudio/AudioI2S/I2SStream.h" diff --git a/src/AudioTools/CoreAudio/Pipeline.h b/src/AudioTools/CoreAudio/Pipeline.h deleted file mode 100644 index 150db55d2e..0000000000 --- a/src/AudioTools/CoreAudio/Pipeline.h +++ /dev/null @@ -1,341 +0,0 @@ -#pragma once - -#include "AudioTools/CoreAudio/AudioIO.h" -#include "AudioTools/CoreAudio/AudioOutput.h" -#include "AudioTools/CoreAudio/AudioStreams.h" - -namespace audio_tools { - -/** - * @brief We can build a input or an output chain: an input chain starts with - * setInput(); followed by add() an output chain consinsts of add() and ends - * with setOutput(); - * @ingroup transform - * @author Phil Schatzmann - * @copyright GPLv3 - */ -class Pipeline : public AudioStream { - friend MultiOutput; - - public: - Pipeline() = default; - ~Pipeline() { end(); } - /// adds a component - bool add(ModifyingStream& io) { - if (has_output) { - LOGE("Output already defined"); - is_ok = false; - return false; - } - if (has_input) { - if (p_stream == nullptr) { - LOGE("Input not defined"); - is_ok = false; - return false; - } - - // predecessor notifies successor - if (p_ai_source != nullptr) { - p_ai_source->addNotifyAudioChange(io); - } - - // we set up an input chain - components.push_back(&io); - io.setStream(*p_stream); - p_stream = &io; - p_ai_source = &io; - } else { - // we assume an output chain - if (size() > 0) { - auto& last_c = last(); - last_c.setOutput(io); - last_c.addNotifyAudioChange(io); - } - components.push_back(&io); - } - return true; - } - - /// adds a component - bool add(ModifyingOutput& out) { - if (has_output) { - LOGE("Output already defined"); - is_ok = false; - return false; - } - if (has_input) { - LOGE("Input not supported"); - is_ok = false; - return false; - } - ModifyingStream* io = new ModifyingStreamAdapter(out); - cleanup.push_back(io); - return add(*io); - } - - /// Defines the output for an output pipeline: must be last call after add() - bool setOutput(AudioOutput& out) { - p_out_print = &out; - if (size() > 0) { - last().addNotifyAudioChange(out); - } - return setOutput((Print&)out); - } - - /// Defines the output for an output pipeline: must be last call after add() - bool setOutput(AudioStream& out) { - p_out_stream = &out; - if (size() > 0) { - last().addNotifyAudioChange(out); - } - return setOutput((Print&)out); - } - - /// Defines the output for an output pipeline: must be last call after add() - bool setOutput(Print& out) { - if (has_output) { - LOGE("Output already defined"); - is_ok = false; - return false; - } - p_print = &out; - if (size() > 0) { - last().setOutput(out); - } - // must be last element - has_output = true; - return true; - } - - /// Defines the input for an input pipeline: must be first call before add() - bool setInput(AudioStream& in) { - p_ai_source = ∈ - p_ai_input = ∈ - return setInput((Stream&)in); - } - - /// Defines the input for an input pipeline: must be first call before add() - bool setInput(Stream& in) { - if (has_output) { - LOGE("Defined as output"); - is_ok = false; - return false; - } - if (has_input) { - LOGE("Input already defined"); - is_ok = false; - return false; - } - // must be first - has_input = true; - p_stream = ∈ - return true; - } - - int availableForWrite() override { - if (!is_active) return 0; - if (size() == 0) { - if (p_print != nullptr) return p_print->availableForWrite(); - return 0; - } - return components[0]->availableForWrite(); - } - - size_t write(const uint8_t* data, size_t len) override { - if (!is_active) return 0; - if (size() == 0) { - if (p_print != nullptr) return p_print->write(data, len); - return 0; - } - LOGD("write: %u", (unsigned)len); - return components[0]->write(data, len); - } - - int available() override { - if (!is_active) return 0; - Stream* in = getInput(); - if (in == nullptr) return 0; - return in->available(); - } - - size_t readBytes(uint8_t* data, size_t len) override { - if (!is_active) return 0; - Stream* in = getInput(); - if (in == nullptr) return 0; - return in->readBytes(data, len); - } - - /// Optional method: Calls begin on all components and setAudioInfo on first - /// coponent to update the full chain - bool begin(AudioInfo info) { - LOGI("begin"); - bool rc = begin(); - setAudioInfo(info); - audioInfoOut().logInfo("pipeline out:"); - return rc; - } - - /// Optional method: Calls begin on all components - bool begin() override { - has_output = false; - bool ok = true; - - // avoid too much noise - setNotifyActive(false); - - // start components - for (auto c : components) { - ok = ok && c->begin(); - } - // start output - if (p_out_stream != nullptr) { - ok = ok && p_out_stream->begin(); - } else if (p_out_print != nullptr) { - ok = ok && p_out_print->begin(); - } - // start input - if (p_ai_input != nullptr) { - ok = ok && p_ai_input->begin(); - } - - setNotifyActive(true); - is_active = ok; - is_ok = ok; - return ok; - } - - /// Calls end on all components - void end() override { - for (auto c : components) { - c->end(); - } - components.clear(); - for (auto& c : cleanup) { - delete c; - } - cleanup.clear(); - - has_output = false; - has_input = false; - p_out_print = nullptr; - p_out_stream = nullptr; - p_print = nullptr; - p_stream = nullptr; - p_ai_source = nullptr; - p_ai_input = nullptr; - is_ok = false; - is_active = true; - } - - /// Defines the AudioInfo for the first node - void setAudioInfo(AudioInfo newInfo) override { - this->info = newInfo; - if (has_input && p_ai_input != nullptr) { - p_ai_input->setAudioInfo(info); - } else if (has_output) { - components[0]->setAudioInfo(info); - } - } - - /// Provides the resulting AudioInfo from the last node - AudioInfo audioInfoOut() override { - AudioInfo info = audioInfo(); - if (size() > 0) { - info = last().audioInfoOut(); - } - return info; - } - - /// Returns true if we have at least 1 component - bool hasComponents() { return size() > 0; } - - /// Provides the number of components - int size() { return components.size(); } - - /// Provides the last component - ModifyingStream& last() { return *components[size() - 1]; } - - /// Access to the components by index - ModifyingStream& operator[](int idx) { return *components[idx]; } - - /// Subscribes to notifications on last component of the chain - void addNotifyAudioChange(AudioInfoSupport& bi) override { - if (size() > 0) last().addNotifyAudioChange(bi); - } - - /// Activates/deactivates notifications - void setNotifyActive(bool flag) { - is_notify_active = flag; - for (auto c : components) { - c->setNotifyActive(flag); - } - } - - /// Activates/deactivates the pipeline (default is active) - void setActive(bool flag) { is_active = flag; } - - /// Determines if the pipeline is active - bool isActive() { return is_active; } - - /// Returns true if pipeline is correctly set up - bool isOK() { return is_ok; } - - /// Returns true if pipeline is correctly set up and is active - operator bool() override { return is_ok && is_active; } - - protected: - Vector components{0}; - Vector cleanup{0}; - bool has_output = false; - bool has_input = false; - bool is_ok = true; - bool is_active = true; - // prior input for input pipline - Stream* p_stream = nullptr; - AudioInfoSource* p_ai_source = nullptr; - AudioStream* p_ai_input = nullptr; - // output for notifications, begin and end calls - AudioOutput* p_out_print = nullptr; - AudioStream* p_out_stream = nullptr; - Print* p_print = nullptr; - - /// Support for ModifyingOutput - struct ModifyingStreamAdapter : public ModifyingStream { - ModifyingStreamAdapter(ModifyingOutput& out) { p_out = &out; } - /// Defines/Changes the input & output - void setStream(Stream& in) override { p_out->setOutput(in); } - /// Defines/Changes the output target - void setOutput(Print& out) override { p_out->setOutput(out); } - /// Read not supported - int available() override { return 0; } - - size_t write(const uint8_t* data, size_t len) override { - return p_out->write(data, len); - } - - bool begin() override { return p_out->begin(); } - - void end() override { p_out->end(); } - - void setAudioInfo(AudioInfo info) override { p_out->setAudioInfo(info); } - - void addNotifyAudioChange(AudioInfoSupport& bi) override { - p_out->addNotifyAudioChange(bi); - } - - protected: - ModifyingOutput* p_out = nullptr; - }; - - /// we read from the last node or the defined input: null if no input is - /// available - Stream* getInput() { - Stream* in = p_stream; - if (size() > 0) { - in = &last(); - } - return in; - } -}; - -} // namespace audio_tools diff --git a/src/AudioTools/CoreAudio/ResampleStream.h b/src/AudioTools/CoreAudio/ResampleStream.h deleted file mode 100644 index 86e0dc37cf..0000000000 --- a/src/AudioTools/CoreAudio/ResampleStream.h +++ /dev/null @@ -1,331 +0,0 @@ -#pragma once - -#include "AudioTools/CoreAudio/AudioIO.h" - -#if USE_PRINT_FLUSH -# define PRINT_FLUSH_OVERRIDE override -#else -# define PRINT_FLUSH_OVERRIDE -#endif - -namespace audio_tools { - -/** - * @brief Optional Configuration object. The critical information is the - * channels and the step_size. All other information is not used. - * - */ -struct ResampleConfig : public AudioInfo { - float step_size = 1.0f; - /// Optional fixed target sample rate - int to_sample_rate = 0; - int buffer_size = DEFAULT_BUFFER_SIZE; -}; - -/** - * @brief Dynamic Resampling. We can use a variable factor to speed up or slow - * down the playback. - * @author Phil Schatzmann - * @ingroup transform - * @copyright GPLv3 - * @tparam T - */ -class ResampleStream : public ReformatBaseStream { - public: - ResampleStream() = default; - - /// Support for resampling via write. - ResampleStream(Print &out) { setOutput(out); } - /// Support for resampling via write. The audio information is copied from the - /// io - ResampleStream(AudioOutput &out) { - setAudioInfo(out.audioInfo()); - setOutput(out); - } - - /// Support for resampling via write and read. - ResampleStream(Stream &io) { setStream(io); } - - /// Support for resampling via write and read. The audio information is copied - /// from the io - ResampleStream(AudioStream &io) { - setAudioInfo(io.audioInfo()); - setStream(io); - } - - /// Provides the default configuraiton - ResampleConfig defaultConfig() { - ResampleConfig cfg; - cfg.copyFrom(audioInfo()); - return cfg; - } - - bool begin(ResampleConfig cfg) { - LOGI("begin step_size: %f", cfg.step_size); - //is_output_notify = false; - to_sample_rate = cfg.to_sample_rate; - out_buffer.resize(cfg.buffer_size); - - setupLastSamples(cfg); - setStepSize(cfg.step_size); - is_first = true; - idx = 0; - // step_dirty = true; - bytes_per_frame = info.bits_per_sample / 8 * info.channels; - - setupReader(); - - setAudioInfo(cfg); - - return true; - } - - bool begin(AudioInfo from, AudioInfo to) { - if (from.bits_per_sample != to.bits_per_sample){ - LOGE("invalid bits_per_sample: %d", (int) to.bits_per_sample); - return false; - } - if (from.channels != to.channels){ - LOGE("invalid channels: %d", (int) to.channels); - return false; - } - return begin(from, (sample_rate_t)to.sample_rate); - } - - bool begin(AudioInfo from, int toRate) { - return begin(from, (sample_rate_t)toRate); - } - - bool begin(AudioInfo from, sample_rate_t toRate) { - ResampleConfig rcfg; - rcfg.copyFrom(from); - rcfg.to_sample_rate = toRate; - rcfg.step_size = getStepSize(from.sample_rate, toRate); - return begin(rcfg); - } - - virtual bool begin(AudioInfo info) { - if (to_sample_rate != 0) return begin(info, to_sample_rate); - return begin(info, step_size); - } - - bool begin() override { - return begin(audioInfo()); - } - - bool begin(AudioInfo info, float step) { - ResampleConfig rcfg; - rcfg.copyFrom(info); - step_size = step; - return begin(rcfg); - } - - void setAudioInfo(AudioInfo newInfo) override { - // update the step size if a fixed to_sample_rate has been defined - if (to_sample_rate != 0) { - setStepSize(getStepSize(newInfo.sample_rate, to_sample_rate)); - } - // notify about changes - LOGI("-> ResampleStream:") - AudioStream::setAudioInfo(newInfo); - } - - AudioInfo audioInfoOut() override { - AudioInfo out = audioInfo(); - if (to_sample_rate != 0) { - out.sample_rate = to_sample_rate; - } else { - out.sample_rate = out.sample_rate * step_size; - } - return out; - } - - /// influence the sample rate - void setStepSize(float step) { - LOGI("setStepSize: %f", step); - step_size = step; - } - - void setTargetSampleRate(int rate) { to_sample_rate = rate; } - - /// calculate the step size the sample rate: e.g. from 44200 to 22100 gives a - /// step size of 2 in order to provide fewer samples - float getStepSize(float sampleRateFrom, float sampleRateTo) { - return sampleRateFrom / sampleRateTo; - } - - /// Returns the actual step size - float getStepSize() { return step_size; } - - // int availableForWrite() override { return p_print->availableForWrite(); } - - size_t write(const uint8_t *data, size_t len) override { - LOGD("ResampleStream::write: %d", (int)len); - //addNotifyOnFirstWrite(); - size_t written = 0; - switch (info.bits_per_sample) { - case 16: - return write(p_print, data, len, written); - case 24: - return write(p_print, data, len, written); - case 32: - return write(p_print, data, len, written); - default: - TRACEE(); - } - return 0; - } - - /// Activates buffering to avoid small incremental writes - void setBuffered(bool active) { is_buffer_active = active; } - - /// When buffering is active, writes the buffered audio to the output - void flush() PRINT_FLUSH_OVERRIDE { - if (p_out != nullptr && !out_buffer.isEmpty()) { - TRACED(); -#if USE_PRINT_FLUSH - p_out->flush(); -#endif - int rc = p_out->write(out_buffer.data(), out_buffer.available()); - if (rc != out_buffer.available()) { - LOGE("write error %d vs %d", rc, out_buffer.available()); - } - out_buffer.reset(); - } - } - - float getByteFactor() override { return 1.0f / step_size; } - - protected: - Vector last_samples{0}; - float idx = 0; - bool is_first = true; - float step_size = 1.0; - int to_sample_rate = 0; - int bytes_per_frame = 0; - // optional buffering - bool is_buffer_active = USE_RESAMPLE_BUFFER; - SingleBuffer out_buffer{0}; - Print *p_out = nullptr; - - /// Sets up the buffer for the rollover samples - void setupLastSamples(AudioInfo cfg) { - int bytes_per_sample = cfg.bits_per_sample / 8; - int last_samples_size = cfg.channels * bytes_per_sample; - last_samples.resize(last_samples_size); - memset(last_samples.data(), 0, last_samples_size); - } - - /// Writes the buffer to defined output after resampling - template - size_t write(Print *p_out, const uint8_t *buffer, size_t bytes, - size_t &written) { - this->p_out = p_out; - if (step_size == 1.0f) { - written = p_out->write(buffer, bytes); - return written; - } - // prevent npe - if (info.channels == 0) { - LOGE("channels is 0"); - return 0; - } - T *data = (T *)buffer; - int samples = bytes / sizeof(T); - size_t frames = samples / info.channels; - written = 0; - - // avoid noise if audio does not start with 0 - if (is_first) { - is_first = false; - setupLastSamples(data, 0); - } - - T frame[info.channels]; - size_t frame_size = sizeof(frame); - - // process all samples - while (idx < frames - 1) { - for (int ch = 0; ch < info.channels; ch++) { - T result = getValue(data, idx, ch); - frame[ch] = result; - } - - if (is_buffer_active) { - // if buffer is full we send it to output - if (out_buffer.availableForWrite() <= frame_size) { - flush(); - } - - // we use a buffer to minimize the number of output calls - int tmp_written = - out_buffer.writeArray((const uint8_t *)&frame, frame_size); - written += tmp_written; - if (frame_size != tmp_written) { - TRACEE(); - } - } else { - int tmp = p_out->write((const uint8_t *)&frame, frame_size); - written += tmp; - if (tmp != frame_size) { - LOGE("Failed to write %d bytes: %d", (int)frame_size, tmp); - } - } - - idx += step_size; - } - - flush(); - - // save last samples to be made available at index position -1; - setupLastSamples(data, frames - 1); - idx -= frames; - - if (bytes != (written * step_size)) { - LOGD("write: %d vs %d", (int)bytes, (int)written); - } - - // returns requested bytes to avoid rewriting of processed bytes - return frames * info.channels * sizeof(T); - } - - /// get the interpolated value for indicated (float) index value - template - T getValue(T *data, float frame_idx, int channel) { - // interpolate value - int frame_idx0 = frame_idx; - // e.g. index -0.5 should be determined from -1 to 0 range - if (frame_idx0 == 0 && frame_idx < 0) frame_idx0 = -1; - int frame_idx1 = frame_idx0 + 1; - T val0 = lookup(data, frame_idx0, channel); - T val1 = lookup(data, frame_idx1, channel); - - float result = mapT(frame_idx, frame_idx0, frame_idx1, val0, val1); - LOGD("getValue idx: %d:%d / val: %d:%d / %f -> %f", frame_idx0, frame_idx1, - (int)val0, (int)val1, frame_idx, result) - return (float)round(result); - } - - /// lookup value for indicated frame & channel: index starts with -1; - template - T lookup(T *data, int frame, int channel) { - if (frame >= 0) { - return data[frame * info.channels + channel]; - } else { - // index -1 (get last sample from previos run) - T *pt_last_samples = (T *)last_samples.data(); - return pt_last_samples[channel]; - } - } - /// store last samples to provide values for index -1 - template - void setupLastSamples(T *data, int frame) { - for (int ch = 0; ch < info.channels; ch++) { - T *pt_last_samples = (T *)last_samples.data(); - pt_last_samples[ch] = data[(frame * info.channels) + ch]; - LOGD("setupLastSamples ch:%d - %d", ch, (int)pt_last_samples[ch]) - } - } -}; - -} // namespace audio_tools diff --git a/src/AudioTools/Disk/AudioSource.h b/src/AudioTools/Disk/AudioSource.h deleted file mode 100644 index e74cb5975c..0000000000 --- a/src/AudioTools/Disk/AudioSource.h +++ /dev/null @@ -1,150 +0,0 @@ -#pragma once -#include "AudioTools/CoreAudio/AudioBasic/Str.h" -#include "AudioTools/CoreAudio/AudioMetaData/AbstractMetaData.h" - -namespace audio_tools { - -/** - * @brief Abstract Audio Data Source for the AudioPlayer which is used by the - * Audio Players - * @ingroup player - * @author Phil Schatzmann - * @copyright GPLv3 - * - */ -class AudioSource { - public: - /// Reset actual stream and move to root - virtual void begin() = 0; - - /// Returns next audio stream - virtual Stream* nextStream(int offset) = 0; - - /// Returns previous audio stream - virtual Stream* previousStream(int offset) { return nextStream(-offset); }; - - /// Returns audio stream at the indicated index (the index is zero based, so - /// the first value is 0!) - virtual Stream* selectStream(int index) { - LOGE("Not Supported!"); - return nullptr; - } - - /// same as selectStream - I just prefer this name - virtual Stream* setIndex(int index) { return selectStream(index); } - - /// Returns the actual index of the stream - virtual int index() { return -1; } - - /// Returns audio stream by path - virtual Stream* selectStream(const char* path) = 0; - - /// Sets the timeout which is triggering to move to the next stream. - the - /// default value is 500 ms - virtual void setTimeoutAutoNext(int millisec) { - timeout_auto_next_value = millisec; - } - - /// Provides the timeout which is triggering to move to the next stream. - virtual int timeoutAutoNext() { return timeout_auto_next_value; } - - // only the ICYStream supports this - virtual bool setMetadataCallback(void (*fn)(MetaDataType info, - const char* str, int len), - ID3TypeSelection sel = SELECT_ICY) { - return false; - } - - /// Sets the timeout of Stream in milliseconds - virtual void setTimeout(int millisec) {}; - - /// Returns default setting go to the next - virtual bool isAutoNext() { return true; } - - /// access with array syntax - Stream* operator[](int idx) { return setIndex(idx); } - - /// provides the actual stream (e.g. file) name or url - virtual const char* toStr() { return nullptr; } - - protected: - int timeout_auto_next_value = 500; -}; - -/** - * @brief Callback Audio Data Source which is used by the Audio Players - * @ingroup player - * @author Phil Schatzmann - * @copyright GPLv3 - */ -class AudioSourceCallback : public AudioSource { - public: - AudioSourceCallback() {} - - AudioSourceCallback(Stream* (*nextStreamCallback)(int offset), - void (*onStartCallback)() = nullptr) { - TRACED(); - this->onStartCallback = onStartCallback; - this->nextStreamCallback = nextStreamCallback; - } - - /// Reset actual stream and move to root - virtual void begin() override { - TRACED(); - if (onStartCallback != nullptr) onStartCallback(); - }; - - /// Returns next (with positive index) or previous stream (with negative - /// index) - virtual Stream* nextStream(int offset) override { - TRACED(); - return nextStreamCallback == nullptr ? nullptr : nextStreamCallback(offset); - } - - /// Returns selected audio stream - virtual Stream* selectStream(int index) override { - LOGI("selectStream: %d", index); - if (indexStreamCallback == nullptr) { - LOGI("setCallbackSelectStream not provided"); - if (index > 0) { - begin(); - return nextStream(index); - } else { - // nextStream(0) will return the directory but we need a file - return nextStream(1); - } - } - return indexStreamCallback(index); - } - /// Returns audio stream by path - virtual Stream* selectStream(const char* path) override { - this->path = path; - return indexStreamCallback == nullptr ? nullptr : indexStreamCallback(-1); - }; - - void setCallbackOnStart(void (*callback)()) { onStartCallback = callback; } - - void setCallbackNextStream(Stream* (*callback)(int offset)) { - nextStreamCallback = callback; - } - - void setCallbackSelectStream(Stream* (*callback)(int idx)) { - indexStreamCallback = callback; - } - - virtual bool isAutoNext() override { return auto_next; } - - virtual void setAutoNext(bool a) { auto_next = a; } - - /// Returns the requested path: relevant when provided idx in callback is -1 - virtual const char* getPath() { return path; } - - protected: - void (*onStartCallback)() = nullptr; - bool auto_next = true; - Stream* (*nextStreamCallback)(int offset) = nullptr; - Stream* (*indexStreamCallback)(int index) = nullptr; - const char* path = nullptr; -}; - -} // namespace audio_tools \ No newline at end of file diff --git a/src/AudioTools/Disk/AudioSourceFTP.h b/src/AudioTools/Disk/AudioSourceFTP.h deleted file mode 100644 index 350fbb47de..0000000000 --- a/src/AudioTools/Disk/AudioSourceFTP.h +++ /dev/null @@ -1,145 +0,0 @@ -#include "FTPClient.h" -#include "AudioSource.h" -#include "vector" - -namespace audio_tools { - -/** - * @brief An AudioSource that uses the - * https://github.com/pschatzmann/TinyFTPClient library to retrieve files from a - * FTP Server. You need to provide an FTPClient object to the constructor and - * make sure that you open it before you access the files. The storage of the - * expanded file names is done on the heap, so in order to limit the requested - * memory we can limit the number of files. - * - * @ingroup player - * @author Phil Schatzmann - * @copyright GPLv3 - */ - -template -class AudioSourceFTP : public AudioSource { - public: - /// Default constructor: Provide the client class as template argument e.g. AudioSourceFTP source(client, path, ext);. - AudioSourceFTP(FTPClient& client, const char* path, const char* ext, - int files = 0) { - p_client = &client; - timeout_auto_next_value = 5000; - if (path) p_path = path; - setMaxFiles(files); - } - - /// Resets the actual data - void begin()override { - TRACED(); - idx = 0; - files.clear(); - addDirectory(p_path); - } - - /// Resets the actual data - void end() { - idx = 0; - files.clear(); - } - - /// Returns next audio stream - Stream* nextStream(int offset) override { - int tmp_idx = idx + offset; - if (!isValidIdx(tmp_idx)) return nullptr; - return selectStream(tmp_idx); - }; - - /// Returns previous audio stream - Stream* previousStream(int offset) override { return nextStream(-offset); }; - - /// Returns audio stream at the indicated index (the index is zero based, so - /// the first value is 0!) - Stream* selectStream(int index) override { - if (!isValidIdx(index)) return nullptr; - idx = index; - if (file) { - file.close(); // close the previous file - } - file = p_client->open(files[idx].name()); - return &file; - } - - /// Retrieves all files and returns the actual index of the stream - int index() override { return idx; } - - /// Returns the FTPFile for the indicated path - Stream* selectStream(const char* path) override { - TRACED(); - files.clear(); - idx = 0; - addDirectory(path); - return selectStream(0); - } - - /// provides the actual stream (e.g. file) name or url - const char* toStr() override { - return file.name(); - } - - /// Defines the max number of files (if value is >0) - void setMaxFiles(int maxCount) { max_files = maxCount; } - - /// Adds all the files of a directory - bool addDirectory(const char* path) { - TRACED(); - if (p_client == nullptr) return false; - FTPFile dir = p_client->open(path); - addFiles(dir, 1); - return true; - } - - /// Returns the number of available files - size_t size() { return files.size(); } - - protected: - std::vector files; - FTPClient* p_client = nullptr; - FTPFile file; - int idx = 0; - size_t max_files = 0; - const char* p_ext = nullptr; - const char* p_path = "/"; - bool is_first = true; - - /// Adds all files recursively - void addFiles(FTPFile& dir, int level) { - if (!endsWith(dir.name(), p_ext)) { - LOGI("adding file %s", dir.name()); - files.push_back(std::move(dir)); - } else { - for (const auto& file : p_client->ls(dir.name())) { - if (endsWith(file.name(), p_ext)) { - LOGI("adding file %s", file.name()); - files.push_back(std::move(file)); - } - if (max_files > 0 && files.size() >= max_files) { - LOGI("max files reached: %d", max_files); - return; // stop if we reached the max number of files - } - } - } - } - - bool isValidIdx(int index) { - if (index < 0) return false; - if (index >= files.size()) { - LOGE("index %d is out of range (size: %d)", index, files.size()); - return false; - } - return true; - } - - bool endsWith(const char* file, const char* ext) { - if (file == nullptr) return false; - if (p_ext == nullptr) return true; - return (StrView(file).endsWith(ext)); - } -}; - -} // namespace audio_tools \ No newline at end of file diff --git a/src/AudioTools/Disk/AudioSourceSTD.h b/src/AudioTools/Disk/AudioSourceSTD.h deleted file mode 100644 index e8bd0e5bd6..0000000000 --- a/src/AudioTools/Disk/AudioSourceSTD.h +++ /dev/null @@ -1,127 +0,0 @@ -#pragma once - -#include "AudioLogger.h" -#include "AudioTools/Disk/AudioSource.h" -#include "AudioTools/AudioLibs/Desktop/File.h" -#include "AudioTools/CoreAudio/AudioBasic/StrView.h" -#include - -namespace audio_tools { - -namespace fs = std::filesystem; - -/** - * @brief AudioSource using the standard C++ api - * @ingroup player - * @author Phil Schatzmann - * @copyright GPLv3 - */ -class AudioSourceSTD : public AudioSource { -public: - /// Default constructor - AudioSourceSTD(const char *startFilePath = "/", const char *ext = ".mp3") { - start_path = startFilePath; - exension = ext; - timeout_auto_next_value = 600000; - } - - virtual void begin() override { - TRACED(); - idx_pos = 0; - } - - virtual void end() { - } - - virtual Stream *nextStream(int offset = 1) override { - LOGI("nextStream: %d", offset); - return selectStream(idx_pos + offset); - } - - virtual Stream *selectStream(int index) override { - LOGI("selectStream: %d", index); - idx_pos = index; - file_name = get(index); - if (file_name==nullptr) return nullptr; - LOGI("Using file %s", file_name); - file = SD.open(file_name); - return file ? &file : nullptr; - } - - virtual Stream *selectStream(const char *path) override { - file.close(); - file = SD.open(path); - file_name = file.name(); - LOGI("-> selectStream: %s", path); - return file ? &file : nullptr; - } - - /// Defines the regex filter criteria for selecting files. E.g. ".*Bob - /// Dylan.*" - void setFileFilter(const char *filter) { file_name_pattern = filter; } - - /// Provides the current index position - int index() { return idx_pos; } - - /// provides the actual file name - const char *toStr() { return file_name; } - - // provides default setting go to the next - virtual bool isAutoNext() { return true; } - - /// Allows to "correct" the start path if not defined in the constructor - virtual void setPath(const char *p) { start_path = p; } - - /// Provides the number of files (The max index is size()-1): WARNING this is very slow if you have a lot of files in many subdirectories - long size() { - long count = 0; - for (auto const& dir_entry : fs::recursive_directory_iterator(start_path)){ - if (isValidAudioFile(dir_entry)) - count++; - } - return count; - } - -protected: - File file; - size_t idx_pos = 0; - const char *file_name; - const char *exension = ""; - const char *start_path = nullptr; - const char *file_name_pattern = "*"; - fs::directory_entry entry; - - const char* get(int idx){ - int count = 0; - const char* result = nullptr; - for (auto const& dir_entry : fs::recursive_directory_iterator(start_path)){ - if (isValidAudioFile(dir_entry)){ - if (count++==idx){ - entry = dir_entry; - result = entry.path().c_str(); - break; - } - } - } - return result; - } - - /// checks if the file is a valid audio file - bool isValidAudioFile(fs::directory_entry file) { - const std::filesystem::path& path = file.path(); - - const char *file_name = path.filename().c_str(); - if (file.is_directory()) { - LOGD("-> isValidAudioFile: '%s': %d", file_name, false); - return false; - } - StrView strFileTName(file_name); - bool result = strFileTName.endsWithIgnoreCase(exension) - && strFileTName.matches(file_name_pattern); - LOGD("-> isValidAudioFile: '%s': %d", file_name, result); - return result; - } - -}; - -} // namespace audio_tools \ No newline at end of file diff --git a/src/AudioTools/Disk/AudioSourceURL.h b/src/AudioTools/Disk/AudioSourceURL.h deleted file mode 100644 index 142f11ede4..0000000000 --- a/src/AudioTools/Disk/AudioSourceURL.h +++ /dev/null @@ -1,163 +0,0 @@ - - -#pragma once -#include "AudioToolsConfig.h" -#include "AudioSource.h" -#include "AudioTools/CoreAudio/AudioHttp/AbstractURLStream.h" - -namespace audio_tools { - -/** - * @brief Audio Source which provides the data via the network from an URL - * @ingroup player - * @author Phil Schatzmann - * @copyright GPLv3 - */ -class AudioSourceURL : public AudioSource { - public: - template - AudioSourceURL(AbstractURLStream& urlStream, T (&urlArray)[N], - const char* mime, int startPos = 0) { - TRACED(); - this->actual_stream = &urlStream; - this->mime = mime; - this->urlArray = urlArray; - this->max = N; - this->pos = startPos - 1; - this->timeout_auto_next_value = 20000; - } - - /// Setup Wifi URL - virtual void begin() override { - TRACED(); - this->pos = 0; - } - - /// Opens the selected url from the array - Stream* selectStream(int idx) override { - pos = idx; - if (pos < 0) { - pos = 0; - LOGI("url array out of limits: %d -> %d", idx, pos); - } - if (pos >= size()) { - pos = size() - 1; - LOGI("url array out of limits: %d -> %d", idx, pos); - } - LOGI("selectStream: %d/%d -> %s", pos, size() - 1, value(pos)); - if (started) actual_stream->end(); - actual_stream->begin(value(pos), mime); - started = true; - return actual_stream; - } - - /// Opens the next url from the array - Stream* nextStream(int offset) override { - pos += offset; - if (pos < 0 || pos >= size()) { - pos = 0; - } - LOGI("nextStream: %d/%d -> %s", pos, max - 1, value(pos)); - return selectStream(pos); - } - - /// Opens the Previous url from the array - Stream* previousStream(int offset) override { - pos -= offset; - if (pos < 0 || pos >= size()) { - pos = size() - 1; - } - LOGI("previousStream: %d/%d -> %s", pos, size() - 1, value(pos)); - return selectStream(pos); - } - - /// Opens the selected url - Stream* selectStream(const char* path) override { - LOGI("selectStream: %s", path); - if (started) actual_stream->end(); - actual_stream->begin(path, mime); - started = true; - return actual_stream; - } - - int index() { return pos; } - - const char* toStr() { return value(pos); } - - /// Sets the timeout of the URL Stream in milliseconds - void setTimeout(int millisec) { actual_stream->setTimeout(millisec); } - - // provides go not to the next on error - virtual bool isAutoNext() { return true; }; - - // only the ICYStream supports this - bool setMetadataCallback(void (*fn)(MetaDataType info, const char* str, - int len), - ID3TypeSelection sel = SELECT_ICY) { - TRACEI(); - return actual_stream->setMetadataCallback(fn); - } - - protected: - AbstractURLStream* actual_stream = nullptr; - const char** urlArray = nullptr; - int pos = 0; - int max = 0; - const char* mime = nullptr; - bool started = false; - - /// allow use with empty constructor in subclasses - AudioSourceURL() = default; - - virtual const char* value(int pos) { - if (urlArray == nullptr) return nullptr; - return urlArray[pos]; - } - - virtual int size() { return max; } -}; - -/** - * @brief Audio Source which provides the data via the network from an URL. - * The URLs are stored in an Vector of dynamically allocated strings - * @ingroup player - * @author Phil Schatzmann - * @copyright GPLv3 - */ - -class AudioSourceDynamicURL : public AudioSourceURL { - public: - template - AudioSourceDynamicURL(AbstractURLStream& urlStream, T (&urlArray)[N], - const char* mime, int startPos = 0) { - this->actual_stream = &urlStream; - this->mime = mime; - this->pos = startPos - 1; - this->timeout_auto_next_value = 20000; - for (int j = 0; j < N; j++) { - addURL(urlArray[j]); - } - } - - AudioSourceDynamicURL(AbstractURLStream& urlStream, const char* mime, - int startPos = 0) { - this->actual_stream = &urlStream; - this->mime = mime; - this->pos = startPos - 1; - this->timeout_auto_next_value = 20000; - } - - /// add a new url: a copy of the string will be stored on the heap - void addURL(const char* url) { url_vector.push_back(url); } - - void clear() { url_vector.clear(); } - - protected: - Vector url_vector; - - const char* value(int pos) override { return url_vector[pos].c_str(); } - - int size() override { return url_vector.size(); } -}; - -} // namespace audio_tools diff --git a/src/AudioTools/Disk/AudioSourceVFS.h b/src/AudioTools/Disk/AudioSourceVFS.h deleted file mode 100644 index f6a7d74bd7..0000000000 --- a/src/AudioTools/Disk/AudioSourceVFS.h +++ /dev/null @@ -1,147 +0,0 @@ -#pragma once - -#include - -#include "AudioLogger.h" -#include "AudioTools/CoreAudio/AudioBasic/StrView.h" -#include "AudioTools/Disk/AudioSource.h" -#include "AudioTools/Disk/VFS.h" -#include "AudioTools/Disk/VFSFile.h" - -namespace audio_tools { - -namespace fs = std::filesystem; - -/** - * @brief AudioSource using the standard C++ api. In order to make this work - * you need to configure and provide a VFS object! - * @ingroup player - * @author Phil Schatzmann - * @copyright GPLv3 - */ -class AudioSourceVFS : public AudioSource { - public: - /// Default constructor with VFS - AudioSourceVFS(VFS &vfs, const char *startFilePath = "/", - const char *ext = "") { - start_path = startFilePath; - exension = ext; - setVFS(vfs); - } - - /// Default constructor w/o VFS - AudioSourceVFS(const char *startFilePath = "/", - const char *ext = "") { - start_path = startFilePath; - exension = ext; - } - - virtual void begin() override { - TRACED(); - idx_pos = 0; - if (p_vfs) p_vfs->begin(); - } - - virtual void end() { - if (p_vfs) p_vfs->end(); - } - - virtual Stream *nextStream(int offset = 1) override { - LOGI("nextStream: %d", offset); - return selectStream(idx_pos + offset); - } - - virtual Stream *selectStream(int index) override { - LOGI("selectStream: %d", index); - idx_pos = index; - file_name = get(index); - if (file_name == nullptr) return nullptr; - LOGI("Using file %s", file_name); - assert(p_vfs != nullptr); - file = p_vfs->open(file_name); - return file ? &file : nullptr; - } - - virtual Stream *selectStream(const char *path) override { - file.close(); - assert(p_vfs != nullptr); - file = p_vfs->open(path); - file_name = file.name(); - LOGI("-> selectStream: %s", path); - return file ? &file : nullptr; - } - - /// Defines the regex filter criteria for selecting files. E.g. ".*Bob - /// Dylan.*" - void setFileFilter(const char *filter) { file_name_pattern = filter; } - - /// Provides the current index position - int index() { return idx_pos; } - - /// provides the actual file name - const char *toStr() { return file_name; } - - // provides default setting go to the next - virtual bool isAutoNext() { return true; } - - /// Allows to "correct" the start path if not defined in the constructor - virtual void setPath(const char *p) { start_path = p; } - - /// Provides the number of files (The max index is size()-1): WARNING this is - /// very slow if you have a lot of files in many subdirectories - long size() { - long count = 0; - for (auto const &dir_entry : fs::recursive_directory_iterator(start_path)) { - if (isValidAudioFile(dir_entry)) count++; - } - return count; - } - - /// Assign the VFS: to be used before calling begin - void setVFS(VFS &vfs){ - p_vfs = &vfs; - } - - protected: - VFSFile file; - size_t idx_pos = 0; - const char *file_name; - const char *exension = ""; - const char *start_path = "/"; - const char *file_name_pattern = "*"; - fs::directory_entry entry; - VFS *p_vfs = nullptr; - - const char *get(int idx) { - int count = 0; - const char *result = nullptr; - for (auto const &dir_entry : fs::recursive_directory_iterator(start_path)) { - if (isValidAudioFile(dir_entry)) { - if (count++ == idx) { - entry = dir_entry; - result = entry.path().c_str(); - break; - } - } - } - return result; - } - - /// checks if the file is a valid audio file - bool isValidAudioFile(fs::directory_entry file) { - const std::filesystem::path &path = file.path(); - - const char *file_name = path.filename().c_str(); - if (file.is_directory()) { - LOGD("-> isValidAudioFile: '%s': %d", file_name, false); - return false; - } - StrView strFileTName(file_name); - bool result = strFileTName.endsWithIgnoreCase(exension) && - strFileTName.matches(file_name_pattern); - LOGD("-> isValidAudioFile: '%s': %d", file_name, result); - return result; - } -}; - -} // namespace audio_tools \ No newline at end of file diff --git a/src/AudioTools/Disk/SDDirect.h b/src/AudioTools/Disk/SDDirect.h deleted file mode 100644 index 3eeca71e8b..0000000000 --- a/src/AudioTools/Disk/SDDirect.h +++ /dev/null @@ -1,257 +0,0 @@ -#pragma once - -#include "AudioTools/CoreAudio/AudioBasic/Str.h" -#include "AudioTools/CoreAudio/AudioBasic/Collections/List.h" - -#define MAX_FILE_LEN 256 -#define MAX_FILE_COUNT 1000000 -// Special logic for SDTFAT -#ifdef SDT_FAT_VERSION -# define USE_SDFAT -#endif - - -namespace audio_tools { - -/** - * @brief We access the files directy with an index. The index is determined by - * a recurseve tree walk thru the directory. Unfortunatly the SDTFAT library has - * it's own API which is incompatible with the SD API - */ -template -class SDDirect { - public: - SDDirect(SDT &sd) { p_sd = &sd; }; - - void begin(const char *startDir, const char *extension, - const char *file_name_pattern) { - TRACED(); - this->start_dir = startDir; - this->ext = extension; - this->file_name_pattern = file_name_pattern; - this->max_idx = -1; - } - - /// Access file name by index - const char *operator[](int idx) { - if (max_idx != -1 && idx > max_idx) { - return nullptr; - } - - requested_idx = idx; - actual_idx = -1; - found = false; - listDir(start_dir); - if (!found) return nullptr; - return result.c_str(); - } - - /// Provides the number of files - long size() { - if (max_idx == -1) { - requested_idx = MAX_FILE_COUNT; - actual_idx = -1; - found = false; - listDir(start_dir); - max_idx = actual_idx; - } - return max_idx + 1; - } - - protected: - String result; - const char *start_dir; - SDT *p_sd = nullptr; - int32_t actual_idx; - size_t requested_idx; - long max_idx = -1; - bool found = false; - List file_path_stack; - String file_path_str; - - const char *ext = nullptr; - const char *file_name_pattern = nullptr; - - /// Writes the index file - void listDir(const char *dirname) { - if (dirname == nullptr) return; - LOGD("listDir: %s", dirname); - FileT root = open(dirname); - if (!root) { - LOGE("Open failed: %s", dirname); - popPath(); - return; - } - if (!isDirectory(root)) { - LOGD("Is not directory: %s", dirname); - popPath(); - return; - } - if (StrView(dirname).startsWith(".")) { - LOGD("Invalid file: %s", dirname); - popPath(); - return; - } - if (isDirectory(root)) { - rewind(root); - } - found = false; - FileT file = openNext(root); - while (file && !found) { - if (isDirectory(file)) { - String name = String(fileNamePath(file)); - LOGD("name: %s", name.c_str()); - pushPath(fileName(file)); - // recurseve call to get all files of this directory - listDir(name.c_str()); - } else { - const char *fn = fileNamePath(file); - if (isValidAudioFile(file)) { - actual_idx++; - LOGD("File %s at index: %d", fn, actual_idx); - if (actual_idx == requested_idx) { - result = String(fn); - found = true; - } - } else { - LOGD("Ignoring %s", fn); - } - } - file = openNext(root); - } - // stop processing and record maximum index - if (!found && file_path_stack.size() == 0) { - max_idx = actual_idx; - } - popPath(); - } - - void rewind(FileT &f) { - TRACED(); -#ifdef USE_SDFAT - f.rewind(); -#else - f.rewindDirectory(); -#endif - } - - bool isDirectory(FileT &f) { - bool result; -#ifdef USE_SDFAT - result = f.isDir(); -#else - result = f.isDirectory(); -#endif - LOGD("isDirectory %s: %d", fileName(f), result); - return result; - } - - FileT openNext(FileT &dir) { - TRACED(); -#ifdef USE_SDFAT - FileT result; - if (!result.openNext(&dir, O_READ)) { - LOGD("No next file"); - } - return result; -#else - return dir.openNextFile(); -#endif - } - - void pushPath(const char *name) { - TRACED(); - LOGD("pushPath: %s", name); - String nameStr(name); - file_path_stack.push_back(nameStr); - } - - void popPath() { - TRACED(); - String str; - file_path_stack.pop_back(str); - LOGD("popPath: %s", str.c_str()); - } - - /// checks if the file is a valid audio file - bool isValidAudioFile(FileT &file) { - const char *file_name = fileName(file); - if (file.isDirectory()) { - LOGD("-> isValidAudioFile: '%s': %d", file_name, false); - return false; - } - StrView strFileTName(file_name); - bool result = strFileTName.endsWithIgnoreCase(ext) && - strFileTName.matches(file_name_pattern) && !isHidden(file); - LOGD("-> isValidAudioFile: '%s': %d", file_name, result); - return result; - } - - /// Returns the filename w/o path - const char *fileName(FileT &file) { -#ifdef USE_SDFAT - // add name - static char name[MAX_FILE_LEN]; - file.getName(name, MAX_FILE_LEN); - return name; -#else - StrView tmp(file.name()); - int pos = 0; - // remove directories - if (tmp.contains("/")) { - pos = tmp.lastIndexOf("/") + 1; - } - return file.name() + pos; -#endif - } - - /// Returns the filename including the path - const char *fileNamePath(FileT &file) { -#if defined(USE_SDFAT) || ESP_IDF_VERSION_MAJOR >= 4 - LOGD("-> fileNamePath: %s", fileName(file)); - file_path_str = start_dir; - if (!file_path_str.endsWith("/")) { - file_path_str += "/"; - } - for (int j = 0; j < file_path_stack.size(); j++) { - file_path_str += file_path_stack[j] + "/"; - } - // add name - static char name[MAX_FILE_LEN]; - strncpy(name, fileName(file), MAX_FILE_LEN); - file_path_str += name; - const char *result = file_path_str.c_str(); - LOGD("<- fileNamePath: %s", result); - return result; -#else - return file.name(); -#endif - } - - bool isHidden(FileT &f) { -#ifdef USE_SDFAT - return f.isHidden(); -#else - return StrView(fileNamePath(f)).contains("/."); -#endif - } - - FileT open(const char *name) { - TRACED(); -#ifdef USE_SDFAT - FileT result; - if (!result.open(name)) { - if (name != nullptr) { - LOGE("File open error: %s", name); - } else { - LOGE("File open error: name is null"); - } - } - return result; -#else - return p_sd->open(name, FILE_READ); -#endif - } -}; - -} // namespace audio_tools \ No newline at end of file diff --git a/src/AudioTools/Disk/SDIndex.h b/src/AudioTools/Disk/SDIndex.h deleted file mode 100644 index 02825e2573..0000000000 --- a/src/AudioTools/Disk/SDIndex.h +++ /dev/null @@ -1,319 +0,0 @@ -#pragma once - -#include "AudioTools/CoreAudio/AudioBasic/Str.h" -#include "AudioTools/CoreAudio/AudioBasic/Collections/List.h" - -#define MAX_FILE_LEN 256 - -// Special logic for SDTFAT -#ifdef SDT_FAT_VERSION -# define USE_SDFAT -#endif - -namespace audio_tools { - -/** - * @brief We store all the relevant file names in an sequential index - * file. Form there we can access them via an index - */ -template -class SDIndex { - public: - SDIndex(SDT &sd) { p_sd = &sd; }; - void begin(const char *startDir, const char *extension, - const char *file_name_pattern, bool setupIndex = true) { - TRACED(); - this->start_dir = startDir; - this->ext = extension; - this->file_name_pattern = file_name_pattern; - idx_path = filePathString(startDir, "idx.txt"); - idx_defpath = filePathString(startDir, "idx-def.txt"); - int idx_file_size = indexFileTSize(); - LOGI("Index file size: %d", idx_file_size); - String keyNew = - String(startDir) + "|" + extension + "|" + file_name_pattern; - String keyOld = getIndexDef(); - if (setupIndex && (keyNew != keyOld || idx_file_size == 0)) { - FileT idxfile = p_sd->open(idx_path.c_str(), FILE_WRITE); - LOGW("Creating index file"); - listDir(idxfile, startDir); - LOGI("Indexing completed"); - idxfile.close(); - // update index definition file - saveIndexDef(keyNew); - } - } - - void ls(Print &p, const char *startDir, const char *extension, - const char *file_name_pattern = "*") { - TRACED(); - this->ext = extension; - this->file_name_pattern = file_name_pattern; - listDir(p, startDir); - file_path_stack.clear(); - file_path_str.clear(); - } - - /// Access file name by index - const char *operator[](int idx) { - // return null when inx too big - if (max_idx >= 0 && idx > max_idx) { - LOGE("idx %d > size %d", idx, max_idx); - return nullptr; - } - // find record - FileT idxfile = p_sd->open(idx_path.c_str()); - int count = 0; - - if (idxfile.available() == 0) { - LOGE("Index file is empty"); - } - - bool found = false; - while (idxfile.available() > 0 && !found) { - result = idxfile.readStringUntil('\n'); - - // result c-string - char *c_str = (char *)result.c_str(); - // remove potential cr character - hax to avoid any allocations - int lastPos = result.length() - 1; - if (c_str[lastPos] == 13) { - c_str[lastPos] = 0; - } - - LOGD("%d -> %s", count, c_str); - if (count == idx) { - found = true; - } - count++; - } - if (!found) { - max_idx = count; - } - idxfile.close(); - - return found ? result.c_str() : nullptr; - } - - long size() { - if (max_idx == -1) { - FileT idxfile = p_sd->open(idx_path.c_str()); - int count = 0; - - while (idxfile.available() > 0) { - result = idxfile.readStringUntil('\n'); - // result c-string - char *c_str = (char *)result.c_str(); - count++; - } - idxfile.close(); - max_idx = count; - } - return max_idx; - } - - protected: - String result; - String idx_path; - String idx_defpath; - SDT *p_sd = nullptr; - List file_path_stack; - String file_path_str; - const char *start_dir; - - const char *ext = nullptr; - const char *file_name_pattern = nullptr; - long max_idx = -1; - - String filePathString(const char *name, const char *suffix) { - String result = name; - return result.endsWith("/") ? result + suffix : result + "/" + suffix; - } - - /// Writes the index file - void listDir(Print &idxfile, const char *dirname) { - LOGD("listDir: %s", dirname); - FileT root = open(dirname); - if (!root) { - LOGE("Open failed: %s", dirname); - popPath(); - return; - } - if (!isDirectory(root)) { - LOGD("Is not directory: %s", dirname); - popPath(); - return; - } - if (StrView(dirname).startsWith(".")) { - LOGD("Invalid file: %s", dirname); - popPath(); - return; - } - - rewind(root); - FileT file = openNext(root); - while (file) { - if (isDirectory(file)) { - String name = String(fileNamePath(file)); - LOGD("name: %s", name.c_str()); - pushPath(fileName(file)); - listDir(idxfile, name.c_str()); - } else { - const char *fn = fileNamePath(file); - if (isValidAudioFile(file)) { - LOGD("Adding file to index: %s", fn); - idxfile.println(fn); - } else { - LOGD("Ignoring %s", fn); - } - } - file = openNext(root); - } - popPath(); - } - - bool isDirectory(FileT &f) { - bool result; -#ifdef USE_SDFAT - result = f.isDir(); -#else - result = f.isDirectory(); -#endif - LOGD("isDirectory %s: %d", fileName(f), result); - return result; - } - - FileT openNext(FileT &dir) { - TRACED(); -#ifdef USE_SDFAT - FileT result; - if (!result.openNext(&dir, O_READ)) { - LOGD("No next file"); - } - return result; -#else - return dir.openNextFile(); -#endif - } - - void pushPath(const char *name) { - LOGD("pushPath: %s", name); - LOGD("pushPath: %s", name); - String nameStr(name); - file_path_stack.push_back(nameStr); - } - - void popPath() { - TRACED(); - String str; - file_path_stack.pop_back(str); - LOGD("popPath: %s", str.c_str()); - } - - /// checks if the file is a valid audio file - bool isValidAudioFile(FileT &file) { - TRACED(); - const char *file_name = fileName(file); - if (file.isDirectory()) { - LOGD("-> isValidAudioFile: '%s': %d", file_name, false); - return false; - } - StrView strFileTName(file_name); - bool result = strFileTName.endsWithIgnoreCase(ext) && - strFileTName.matches(file_name_pattern) && !isHidden(file); - LOGD("-> isValidAudioFile: '%s': %d", file_name, result); - return result; - } - - String getIndexDef() { - FileT idxdef = p_sd->open(idx_defpath.c_str()); - String key1 = idxdef.readString(); - idxdef.close(); - return key1; - } - void saveIndexDef(String keyNew) { - FileT idxdef = p_sd->open(idx_defpath.c_str(), FILE_WRITE); - idxdef.write((const uint8_t *)keyNew.c_str(), keyNew.length()); - idxdef.close(); - } - - size_t indexFileTSize() { - FileT idxfile = p_sd->open(idx_path.c_str()); - size_t result = idxfile.size(); - idxfile.close(); - return result; - } - - void rewind(FileT &f) { - TRACED(); -#ifdef USE_SDFAT - f.rewind(); -#else - f.rewindDirectory(); -#endif - } - - /// Returns the filename w/o the path - const char *fileName(FileT &file) { -#ifdef USE_SDFAT - // add name - static char name[MAX_FILE_LEN]; - file.getName(name, MAX_FILE_LEN); - return name; -#else - StrView tmp(file.name()); - int pos = 0; - // remove directories - if (tmp.contains("/")) { - pos = tmp.lastIndexOf("/") + 1; - } - return file.name() + pos; -#endif - } - - /// Returns the filename including the path - const char *fileNamePath(FileT &file) { -#if defined(USE_SDFAT) || ESP_IDF_VERSION_MAJOR >= 4 - LOGD("-> fileNamePath: %s", fileName(file)); - file_path_str = start_dir; - if (!file_path_str.endsWith("/")) { - file_path_str += "/"; - } - for (int j = 0; j < file_path_stack.size(); j++) { - file_path_str += file_path_stack[j] + "/"; - } - // add name - static char name[MAX_FILE_LEN]; - strncpy(name, fileName(file), MAX_FILE_LEN); - file_path_str += name; - const char *result = file_path_str.c_str(); - LOGD("<- fileNamePath: %s", result); - return result; -#else - return file.name(); -#endif - } - - bool isHidden(FileT &f) { -#ifdef USE_SDFAT - return f.isHidden(); -#else - return StrView(fileNamePath(f)).contains("/."); -#endif - } - - FileT open(const char *name) { - TRACED(); -#ifdef USE_SDFAT - FileT result; - if (!result.open(name)) { - LOGE("FileT open error: %s", name); - } - return result; -#else - return p_sd->open(name, "r"); -#endif - } -}; - -} // namespace audio_tools \ No newline at end of file diff --git a/src/AudioTools/Disk/VFS.h b/src/AudioTools/Disk/VFS.h deleted file mode 100644 index a1e0487d3f..0000000000 --- a/src/AudioTools/Disk/VFS.h +++ /dev/null @@ -1,75 +0,0 @@ -#pragma once -#include "AudioTools/CoreAudio/AudioBasic/Str.h" -#include "VFSFile.h" - -namespace audio_tools { - -/*** - * @brief Abstract Base class which represents an ESP32 Virtual File System. - * After initializing the VFS the regular c file operations are supported. - * @brief Abstract Base class which represents an ESP32 Virtual File System. - * After initializing the VFS the regular c file operations are supported. - * @ingroup player - * @author Phil Schatzmann - * @copyright GPLv3 - */ -class VFS { - public: - /// mount the file systems - virtual bool begin() {return true;} - /// unmount the file system - virtual void end() {} - /// provide the mount point (root directory for the file system) - virtual void setMountPoint(const char* mp) = 0; - - VFSFile open(const char* file, FileMode mode = VFS_FILE_READ) { - VFSFile vfs_file; - vfs_file.open(expand(file), mode); - return vfs_file; - } - VFSFile open(const std::string& path, FileMode mode = VFS_FILE_READ) { - const char* path_str = path.c_str(); - const char* path_str_exanded = expand(path_str); - LOGI("open: %s", path_str_exanded); - return this->open(path_str_exanded, mode); - } - bool exists(const char* path) { - struct stat buffer; - return (stat(expand(path), &buffer) == 0); - } - bool exists(const std::string& path) { return exists(path.c_str()); } - bool remove(const char* path) { return ::remove(expand(path)) == 0; } - bool remove(const std::string& path) { return remove(path.c_str()); } - bool rename(const char* pathFrom, const char* pathTo) { - return ::rename(expand(pathFrom), expand(pathTo)) == 0; - } - bool rename(const std::string& pathFrom, const std::string& pathTo) { - return rename(pathFrom.c_str(), pathTo.c_str()); - } - bool mkdir(const char* path) { return ::mkdir(expand(path), 0777) == 0; } - bool mkdir(const std::string& path) { return mkdir(path.c_str()); } - bool rmdir(const char* path) { return ::rmdir(expand(path)) == 0; } - bool rmdir(const std::string& path) { return rmdir(path.c_str()); } - - /// provides the actual mount point - const char* mountPoint() { return mount_point; } - - protected: - const char* mount_point = "/"; - - Str tmp; - - /// expands the file name with the mount point - const char* expand(const char* file) { - assert(mount_point != nullptr); - assert(file != nullptr); - tmp = mount_point; - if (!StrView(file).startsWith("/") && !StrView(mount_point).endsWith("/")) { - tmp.add("/"); - } - tmp.add(file); - return tmp.c_str(); - } -}; - -} // namespace audio_tools diff --git a/src/AudioTools/Disk/VFSFile.h b/src/AudioTools/Disk/VFSFile.h deleted file mode 100644 index ad505488b6..0000000000 --- a/src/AudioTools/Disk/VFSFile.h +++ /dev/null @@ -1,192 +0,0 @@ -#pragma once -#include -#include - -#include -#include - -#include "AudioToolsConfig.h" - -#ifdef IS_NOARDUINO -#define NOARD_OVR override -#else -#define NOARD_OVR -#endif - -#ifndef FILE_READ -#define FILE_READ VFS_FILE_READ -#define FILE_WRITE VFS_FILE_WRITE -#define FILE_APPEND VFS_FILE_APPEND -#endif - -namespace audio_tools { - -enum FileMode { VFS_FILE_READ = 'r', VFS_FILE_WRITE = 'w', VFS_FILE_APPEND = 'a' }; -enum SeekMode { SeekSet = 0, SeekCur = 1, SeekEnd = 2 }; - -/** - * @brief Arduino File support using std::fstream - * @author Phil Schatzmann - * @ingroup player - * @copyright GPLv3 - */ -class VFSFile : public Stream { - public: - VFSFile() = default; - VFSFile(const char* fn) { open(fn, VFS_FILE_READ); } - VFSFile(const VFSFile& file) { open(file.name(), VFS_FILE_READ); } - ~VFSFile() { end();} - - VFSFile& operator=(VFSFile file) { - open(file.name(), VFS_FILE_READ); - return *this; - } - - void open(const char* name, FileMode mode = VFS_FILE_READ) { - file_path = name; - switch (mode) { - case VFS_FILE_READ: - stream.open(name, stream.binary | stream.in); - is_read = true; - break; - case VFS_FILE_WRITE: - stream.open(name, stream.binary | stream.trunc | stream.out); - is_read = false; - break; - case VFS_FILE_APPEND: - stream.open(name, stream.binary | stream.out); - is_read = false; - break; - } - } - - virtual bool begin() { - // move to beginning - return seek(0); - } - - virtual void end() { stream.close(); } - - virtual int print(const char* str) NOARD_OVR { - stream << str; - return strlen(str); - } - - virtual int println(const char* str = "") NOARD_OVR { - stream << str << "\n"; - return strlen(str) + 1; - } - - virtual int print(int number) NOARD_OVR { - char buffer[80]; - int len = snprintf(buffer, 80, "%d", number); - print(buffer); - return len; - } - - virtual int println(int number) { - char buffer[80]; - int len = snprintf(buffer, 80, "%d\n", number); - print(buffer); - return len; - } - - virtual void flush() override { stream.flush(); } - - - virtual size_t write(uint8_t* str, size_t len) { - stream.write((const char*)str, len); - return len; - } - - virtual size_t write(const uint8_t* str, size_t len) override { - stream.write((const char*)str, len); - return len; - } - - virtual size_t write(int32_t value) { - stream.put(value); - return 1; - } - - virtual size_t write(uint8_t value) override { - stream.put(value); - return 1; - } - - virtual int available() override { - int result = size() - position(); - return result; - }; - - virtual int read() override { return stream.get(); } - - virtual size_t readBytes(char* data, size_t len) { - return readBytes((uint8_t*)data, len); - } - - virtual size_t readBytes(uint8_t* data, size_t len) override { - stream.read((char*)data, len); - return stream ? len : stream.gcount(); - } - - virtual int peek() override { return stream.peek(); } - - bool seek(uint32_t pos, SeekMode mode) { - if (is_read) { - switch (mode) { - case SeekSet: - stream.seekg(pos, std::ios_base::beg); - break; - case SeekCur: - stream.seekg(pos, std::ios_base::cur); - break; - case SeekEnd: - stream.seekg(pos, std::ios_base::end); - break; - } - } else { - switch (mode) { - case SeekSet: - stream.seekp(pos, std::ios_base::beg); - break; - case SeekCur: - stream.seekp(pos, std::ios_base::cur); - break; - case SeekEnd: - stream.seekp(pos, std::ios_base::end); - break; - } - } - return stream.fail() == false; - } - - bool seek(uint32_t pos) { return seek(pos, SeekSet); } - - size_t position() { return stream.tellp(); } - - size_t size() const { - std::streampos fsize = 0; - std::ifstream file(file_path, std::ios::binary); - - fsize = file.tellg(); - file.seekg(0, std::ios::end); - fsize = file.tellg() - fsize; - file.close(); - - return fsize; - } - - void close() { stream.close(); } - - const char* name() const { return file_path; } - - operator bool() { return stream.is_open(); } - - protected: - std::fstream stream; - bool is_read = true; - const char* file_path = nullptr; -}; - -} // namespace audio_tools \ No newline at end of file diff --git a/src/AudioTools/Disk/VFS_LittleFS.h b/src/AudioTools/Disk/VFS_LittleFS.h deleted file mode 100644 index aa32cd8615..0000000000 --- a/src/AudioTools/Disk/VFS_LittleFS.h +++ /dev/null @@ -1,79 +0,0 @@ -#pragma once -#include -#include -#include - -#include "AudioTools/Disk/VFS.h" -#include "esp_err.h" -#include "esp_littlefs.h" -#include "esp_system.h" - -namespace audio_tools { - -/** - * @brief ESP32 Virtual File System for the LittleFS. The default mount point is - * "/littlefs" DRAFT implementation: not tested See - * https://github.com/espressif/esp-idf/tree/master/examples/storage/sd_card/sdspi - * @ingroup player - * @author Phil Schatzmann - * @copyright GPLv3 - */ - -class VFS_LittleFS : public VFS { - public: - VFS_LittleFS(const char* mountPoint = "/littlefs") { - mount_point = mountPoint; - }; - void setMountPoint(const char* mp) { mount_point = mp; } - bool begin() { - LOGI("Initializing LittleFS"); - - esp_vfs_littlefs_conf_t conf = { - .base_path = mount_point, - .partition_label = "storage", - .format_if_mount_failed = format_if_mount_failed, - .dont_mount = false, - }; - - // Use settings defined above to initialize and mount LittleFS filesystem. - // Note: esp_vfs_littlefs_register is an all-in-one convenience function. - esp_err_t ret = esp_vfs_littlefs_register(&conf); - - if (ret != ESP_OK) { - if (ret == ESP_FAIL) { - LOGE("Failed to mount or format filesystem"); - } else if (ret == ESP_ERR_NOT_FOUND) { - LOGE("Failed to find LittleFS partition"); - } else { - LOGE("Failed to initialize LittleFS (%s)", esp_err_to_name(ret)); - } - return false; - } - - size_t total = 0, used = 0; - ret = esp_littlefs_info(conf.partition_label, &total, &used); - if (ret != ESP_OK) { - LOGE("Failed to get LittleFS partition information (%s)", - esp_err_to_name(ret)); - // esp_littlefs_format(conf.partition_label); - return false; - } else { - LOGI("Partition size: total: %d, used: %d", total, used); - } - - return true; - } - - void end() { - // All done, unmount partition and disable SPIFFS - // All done, unmount partition and disable LittleFS - esp_vfs_littlefs_unregister(conf.partition_label); - LOGI("LittleFS unmounted"); - } - - protected: - esp_vfs_littlefs_conf_t conf; - int max_files = 5; - bool format_if_mount_failed = true; -}; -} // namespace audio_tools \ No newline at end of file diff --git a/src/AudioTools/Disk/VFS_Multi.h b/src/AudioTools/Disk/VFS_Multi.h deleted file mode 100644 index e02be5f19a..0000000000 --- a/src/AudioTools/Disk/VFS_Multi.h +++ /dev/null @@ -1,42 +0,0 @@ -#pragma once -#include "AudioTools/CoreAudio/AudioBasic/Collections/Vector.h" -#include "AudioTools/Disk/VFS.h" - -namespace audio_tools { - -/** - * @brief Define multipe VFS with their mount point - * @ingroup player - * @author Phil Schatzmann - * @copyright GPLv3 - */ - -class VFS_Multi : public VFS { - public: - /// adds a vfs with the corresponding mount point - void add(VFS& vfs, const char* mountPoint) { - vfs.setMountPoint(mountPoint); - vfs_vector.push_back(&vfs); - } - /// mount the file systems - bool begin() override { - bool result = true; - for (int j = 0; j < vfs_vector.size(); j++) { - result = result && vfs_vector[j]->begin(); - } - return result; - } - // unmount the file systems - void end() override { - for (int j = 0; j < vfs_vector.size(); j++) { - vfs_vector[j]->end(); - } - } - /// Not used! - virtual void setMountPoint(const char* mp) { LOGE("not supported"); } - - protected: - Vector vfs_vector; -}; - -} // namespace audio_tools \ No newline at end of file diff --git a/src/AudioTools/Disk/VFS_SDMMC.h b/src/AudioTools/Disk/VFS_SDMMC.h deleted file mode 100644 index 947f1f1c3c..0000000000 --- a/src/AudioTools/Disk/VFS_SDMMC.h +++ /dev/null @@ -1,222 +0,0 @@ -#pragma once -#include - -#include "AudioTools/Disk/VFS.h" -#include "driver/sdmmc_host.h" -#include "esp_vfs_fat.h" -#include "sd_protocol_types.h" -#include "sdmmc_cmd.h" - -#ifdef SOC_SDMMC_IO_POWER_EXTERNAL -#include "sd_pwr_ctrl_by_on_chip_ldo.h" -#endif - -#define SDMMC_FREQ_DEFAULT \ - 20000 /*!< SD/MMC Default speed (limited by clock divider) */ -#define SDMMC_FREQ_HIGHSPEED \ - 40000 /*!< SD High speed (limited by clock divider) */ -#define SDMMC_FREQ_PROBING 400 /*!< SD/MMC probing speed */ -#define SDMMC_FREQ_52M 52000 /*!< MMC 52MHz speed */ -#define SDMMC_FREQ_26M 26000 /*!< MMC 26MHz speed */ -#define SDMMC_FREQ_DDR50 50000 /*!< MMC 50MHz speed */ -#define SDMMC_FREQ_SDR50 100000 /*!< MMC 100MHz speed */ - -#define DEFAULT_CLK 14 -#define DEFAULT_CMD 15 -#define DEFAULT_D0 2 -#define DEFAULT_D1 4 -#define DEFAULT_D2 12 -#define DEFAULT_D3 13 - -#ifndef DEFAULT_ALLOCATION_SIZE -#define DEFAULT_ALLOCATION_SIZE 16 * 1024 -#endif -#ifndef DEFAULT_MAX_FILES -#define DEFAULT_MAX_FILES 5 -#endif - -namespace audio_tools { - -/** - * @brief ESP32 Virtual File System for SDMMC. The default mount point is - * "/sdcard" DRAFT implementation: not tested see - * https://github.com/espressif/esp-idf/blob/master/examples/storage/sd_card/sdmmc/README.md - * @ingroup player - * @author Phil Schatzmann - * @copyright GPLv3 - */ - -class VFS_SDMMC : public VFS { - public: - enum class Speed { HS, UHS_SDR, UHS_DDR }; - enum class BusWidth { Byte1 = 1, Byte4 = 4 }; - VFS_SDMMC(const char* mountPoint = "/sdmmc") { mount_point = mountPoint; } - VFS_SDMMC(int pinClk, int pinCmd, int pinD0, int pinD1, int pinD2 = -1, - int pinD3 = -1, const char* mountPoint = "/sdmmc") - : VFS_SDMMC(mountPoint) { - setBusWidth(pinD1 != -1 && pinD2 != -1 && pinD3 != -1 ? BusWidth::Byte4 - : BusWidth::Byte1); - setPins(pinClk, pinCmd, pinD0, pinD1, pinD2, pinD3); - } - - void setPins(int pinClk, int pinCmd, int pinD0, int pinD1, int pinD2 = -1, - int pinD3 = -1) { - setClk(pinClk); - setCmd(pinCmd); - setD0(pinD0); - setD1(pinD1); - setD2(pinD2); - setD3(pinD3); - } - void setClk(int pin) { pin_clk = (gpio_num_t)pin; } - void setCmd(int pin) { pin_clk = (gpio_num_t)pin; } - void setD0(int pin) { pin_d0 = (gpio_num_t)pin; } - void setD1(int pin) { pin_d1 = (gpio_num_t)pin; } - void setD2(int pin) { pin_d2 = (gpio_num_t)pin; } - void setD3(int pin) { pin_d3 = (gpio_num_t)pin; } - - void setMountPoint(const char* mp) { mount_point = mp; } - void setSpeed(Speed speed) { this->speed = speed; } - void setBusWidth(BusWidth bits) { bus_width = bits; } - - bool begin() { - esp_err_t ret; - - // Options for mounting the filesystem. - // If format_if_mount_failed is set to true, SD card will be partitioned and - // formatted in case when mounting fails. - esp_vfs_fat_sdmmc_mount_config_t mount_config = { - .format_if_mount_failed = false, - .max_files = max_files, - .allocation_unit_size = allocation_unit_size}; - LOGI("Initializing SD card"); - - // Use settings defined above to initialize SD card and mount FAT - // filesystem. Note: esp_vfs_fat_sdmmc/sdspi_mount is all-in-one convenience - // functions. Please check its source code and implement error recovery when - // developing production applications. - - LOGI("Using SDMMC peripheral"); - - // By default, SD card frequency is initialized to SDMMC_FREQ_DEFAULT - // (20MHz) For setting a specific frequency, use host.max_freq_khz (range - // 400kHz - 40MHz for SDMMC) Example: for fixed frequency of 10MHz, use - // host.max_freq_khz = 10000; - host = SDMMC_HOST_DEFAULT(); - - switch (speed) { - case Speed::HS: - host.max_freq_khz = SDMMC_FREQ_HIGHSPEED; - break; - case Speed::UHS_SDR: - host.slot = SDMMC_HOST_SLOT_0; - host.max_freq_khz = SDMMC_FREQ_SDR50; - host.flags &= ~SDMMC_HOST_FLAG_DDR; - break; - case Speed::UHS_DDR: - host.slot = SDMMC_HOST_SLOT_0; - host.max_freq_khz = SDMMC_FREQ_DDR50; - break; - } - - // For SoCs where the SD power can be supplied both via an internal or - // external (e.g. on-board LDO) power supply. When using specific IO pins - // (which can be used for ultra high-speed SDMMC) to connect to the SD - // card and the internal LDO power supply, we need to initialize the power - // supply first. -#ifdef CONFIG_SD_PWR_CTRL_LDO_IO_ID - - sd_pwr_ctrl_ldo_config_t ldo_config = { - .ldo_chan_id = CONFIG_SD_PWR_CTRL_LDO_IO_ID, - }; - pwr_ctrl_handle = NULL; - - ret = sd_pwr_ctrl_new_on_chip_ldo(&ldo_config, &pwr_ctrl_handle); - if (ret != ESP_OK) { - LOGE("Failed to create a new on-chip LDO power control driver"); - return; - } - host.pwr_ctrl_handle = pwr_ctrl_handle; -#endif - - // This initializes the slot without card detect (CD) and write protect (WP) - // signals. Modify slot_config.gpio_cd and slot_config.gpio_wp if your board - // has these signals. - sdmmc_slot_config_t slot_config = SDMMC_SLOT_CONFIG_DEFAULT(); - // #if EXAMPLE_IS_UHS1 - // slot_config.flags |= SDMMC_SLOT_FLAG_UHS1; - // #endif - - // Set bus width to use: - slot_config.width = (int)bus_width; - - // On chips where the GPIOs used for SD card can be configured, set them in - // the slot_config structure: -#ifdef CONFIG_SOC_SDMMC_USE_GPIO_MATRIX - slot_config.clk = pin_clk; - slot_config.cmd = pin_cmd; - slot_config.d0 = pin_d0; - slot_config.d1 = pin_d1; - slot_config.d2 = pin_d2; - slot_config.d3 = pin_d3; -#endif // CONFIG_SOC_SDMMC_USE_GPIO_MATRIX - - // Enable internal pullups on enabled pins. The internal pullups - // are insufficient however, please make sure 10k external pullups are - // connected on the bus. This is for debug / example purpose only. - slot_config.flags |= SDMMC_SLOT_FLAG_INTERNAL_PULLUP; - - LOGI("Mounting filesystem"); - ret = esp_vfs_fat_sdmmc_mount(mount_point, &host, &slot_config, - &mount_config, &card); - - if (ret != ESP_OK) { - if (ret == ESP_FAIL) { - LOGE("Failed to mount filesystem. "); - } else { - LOGE("Failed to initialize the card (%s). ", esp_err_to_name(ret)); - } - return false; - } - LOGI("Filesystem mounted"); - - // Card has been initialized, print its properties - sdmmc_card_print_info(stdout, card); - return true; - } - - void end() { - if (card == nullptr) return; - // All done, unmount partition and disable SDMMC peripheral - esp_vfs_fat_sdcard_unmount(mount_point, card); - LOGI("Card unmounted"); - card = nullptr; - - // Deinitialize the power control driver if it was used -#ifdef CONFIG_SD_PWR_CTRL_LDO_IO_ID - - ret = sd_pwr_ctrl_del_on_chip_ldo(pwr_ctrl_handle); - if (ret != ESP_OK) { - LOGE("Failed to delete the on-chip LDO power control driver"); - return; - } -#endif - } - - protected: - sdmmc_card_t* card = nullptr; - sdmmc_host_t host; - sd_pwr_ctrl_handle_t pwr_ctrl_handle; - int max_files = DEFAULT_MAX_FILES; - size_t allocation_unit_size = DEFAULT_ALLOCATION_SIZE; - Speed speed = Speed::HS; - BusWidth bus_width = BusWidth::Byte4; - gpio_num_t pin_clk = (gpio_num_t)DEFAULT_CLK; - gpio_num_t pin_cmd = (gpio_num_t)DEFAULT_CMD; - gpio_num_t pin_d0 = (gpio_num_t)DEFAULT_D0; - gpio_num_t pin_d1 = (gpio_num_t)DEFAULT_D1; - gpio_num_t pin_d2 = (gpio_num_t)DEFAULT_D2; - gpio_num_t pin_d3 = (gpio_num_t)DEFAULT_D3; -}; - -} // namespace audio_tools \ No newline at end of file diff --git a/src/AudioTools/Disk/VFS_SDSPI.h b/src/AudioTools/Disk/VFS_SDSPI.h deleted file mode 100644 index e67328091a..0000000000 --- a/src/AudioTools/Disk/VFS_SDSPI.h +++ /dev/null @@ -1,184 +0,0 @@ -#pragma once -#include - -#include "AudioTools/Disk/VFS.h" -#include "esp_vfs_fat.h" -#include "sdmmc_cmd.h" -#ifdef SOC_SDMMC_IO_POWER_EXTERNAL -#include "sd_pwr_ctrl_by_on_chip_ldo.h" -#endif - -#if !defined(DEFAULT_CS) -#if defined(AUDIOBOARD_SD) -#define DEFAULT_CS 13 -#define DEFAULT_MOSI 15 -#define DEFAULT_MISO 2 -#define DEFAULT_CLK 14 -#else -#define DEFAULT_CS SS -#define DEFAULT_MOSI MOSI -#define DEFAULT_MISO MISO -#define DEFAULT_CLK SCK -#endif -#endif - -#ifndef DEFAULT_MAX_TRANSFER_SIZE -#define DEFAULT_MAX_TRANSFER_SIZE 4000 -#endif - -namespace audio_tools { - -/** - * @brief ESP32 Virtual File System for SPI SD. The default mount point is - * "/sdcard" DRAFT implementation: not tested See - * https://github.com/espressif/esp-idf/tree/master/examples/storage/sd_card/sdspi - * @ingroup player - * @author Phil Schatzmann - * @copyright GPLv3 - */ - -class VFS_SDSPI : public VFS { - public: - VFS_SDSPI(const char* mountPoint = "/sd") { mount_point = mountPoint; }; - VFS_SDSPI(int CS, int MOSI, int MISO, int SCK, - const char* mountPoint = "/sd") - : VFS_SDSPI(mountPoint) { - setPins(CS, MOSI, MISO, SCK); - } - void setPins(int CS, int MOSI, int MISO, int SCK) { - setCS(CS); - setMosi(MOSI); - setMiso(MISO); - setClk(SCK); - } - void setCS(int pin) { pin_cs = (gpio_num_t)pin; } - void setMosi(int pin) { pin_mosi = (gpio_num_t)pin; } - void setMiso(int pin) { pin_miso = (gpio_num_t)pin; } - void setClk(int pin) { pin_clk = (gpio_num_t)pin; } - void setMountPoint(const char* mp) { mount_point = mp; } - bool begin() { - esp_err_t ret; - - // Options for mounting the filesystem. - // If format_if_mount_failed is set to true, SD card will be partitioned and - // formatted in case when mounting fails. - esp_vfs_fat_sdmmc_mount_config_t mount_config = { - .format_if_mount_failed = format_if_mount_failed, - .max_files = max_files, - .allocation_unit_size = allocation_unit_size}; - LOGI("Initializing SD card"); - - // Use settings defined above to initialize SD card and mount FAT - // filesystem. Note: esp_vfs_fat_sdmmc/sdspi_mount is all-in-one convenience - // functions. Please check its source code and implement error recovery when - // developing production applications. - LOGI("Using SPI peripheral"); - - // By default, SD card frequency is initialized to SDMMC_FREQ_DEFAULT - // (20MHz) For setting a specific frequency, use host.max_freq_khz (range - // 400kHz - 20MHz for SDSPI) Example: for fixed frequency of 10MHz, use - // host.max_freq_khz = 10000; - host = SDSPI_HOST_DEFAULT(); - - // For SoCs where the SD power can be supplied both via an internal or - // external (e.g. on-board LDO) power supply. When using specific IO pins - // (which can be used for ultra high-speed SDMMC) to connect to the SD card - // and the internal LDO power supply, we need to initialize the power supply - // first. -#ifdef CONFIG_SD_PWR_CTRL_LDO_IO_ID - - sd_pwr_ctrl_ldo_config_t ldo_config = { - .ldo_chan_id = CONFIG_SD_PWR_CTRL_LDO_IO_ID, - }; - pwr_ctrl_handle = NULL; - - ret = sd_pwr_ctrl_new_on_chip_ldo(&ldo_config, &pwr_ctrl_handle); - if (ret != ESP_OK) { - LOGE("Failed to create a new on-chip LDO power control driver"); - return false; - } - host.pwr_ctrl_handle = pwr_ctrl_handle; -#endif - - spi_bus_config_t bus_cfg = { - .mosi_io_num = pin_mosi, - .miso_io_num = pin_miso, - .sclk_io_num = pin_clk, - .quadwp_io_num = -1, - .quadhd_io_num = -1, - .max_transfer_sz = max_transfer_sz, - }; - - ret = spi_bus_initialize((spi_host_device_t)host.slot, &bus_cfg, - SDSPI_DEFAULT_DMA); - if (ret != ESP_OK) { - LOGE("Failed to initialize bus."); - return false; - } - - // This initializes the slot without card detect (CD) and write protect (WP) - // signals. Modify slot_config.gpio_cd and slot_config.gpio_wp if your board - // has these signals. - sdspi_device_config_t slot_config = SDSPI_DEVICE_CONFIG_DEFAULT(); - slot_config.gpio_cs = pin_cs; - slot_config.host_id = (spi_host_device_t)host.slot; - - LOGI("Mounting filesystem at %s", mount_point); - ret = esp_vfs_fat_sdspi_mount(mount_point, &host, &slot_config, - &mount_config, &card); - - if (ret != ESP_OK) { - if (ret == ESP_FAIL) { - LOGE("Failed to mount filesystem"); - } else { - LOGE("Failed to initialize the card (%s)", esp_err_to_name(ret)); - } - return false; - } - LOGI("Filesystem mounted"); - - // Card has been initialized, print its properties - sdmmc_card_print_info(stdout, card); - return true; - } - - void end() { - if (card == nullptr) return; - // All done, unmount partition and disable SPI peripheral - esp_vfs_fat_sdcard_unmount(mount_point, card); - card = nullptr; - - LOGI("Card unmounted"); - - // deinitialize the bus after all devices are removed - spi_bus_free((spi_host_device_t)host.slot); - - // Deinitialize the power control driver if it was used -#ifdef CONFIG_SD_PWR_CTRL_LDO_IO_ID - - ret = sd_pwr_ctrl_del_on_chip_ldo(pwr_ctrl_handle); - if (ret != ESP_OK) { - LOGE("Failed to delete the on-chip LDO power control driver"); - return; - } -#endif - } - - void setAllocationUnitSize(int size) { allocation_unit_size = size; } - void setMaxFiles(int files) { max_files = files; } - void setFormatIfMountFailed(bool format) { format_if_mount_failed = format; } - - protected: - sdmmc_card_t* card = nullptr; - sd_pwr_ctrl_handle_t pwr_ctrl_handle; - sdmmc_host_t host; - gpio_num_t pin_cs = (gpio_num_t)DEFAULT_CS; - gpio_num_t pin_mosi = (gpio_num_t)DEFAULT_MOSI; - gpio_num_t pin_miso = (gpio_num_t)DEFAULT_MISO; - gpio_num_t pin_clk = (gpio_num_t)DEFAULT_CLK; - int max_transfer_sz = DEFAULT_MAX_TRANSFER_SIZE; - int max_files = 5; - bool format_if_mount_failed = false; - int allocation_unit_size = 16 * 1024; -}; -} // namespace audio_tools \ No newline at end of file diff --git a/src/AudioTools/Disk/VFS_SPFFS.h b/src/AudioTools/Disk/VFS_SPFFS.h deleted file mode 100644 index 100a05561f..0000000000 --- a/src/AudioTools/Disk/VFS_SPFFS.h +++ /dev/null @@ -1,102 +0,0 @@ -#pragma once -#include -#include -#include - -#include "AudioTools/Disk/VFS.h" -#include "esp_err.h" -#include "esp_spiffs.h" - -namespace audio_tools { - -/** - * @brief ESP32 Virtual File System for the SPFFS. The default mount point is - * "/spiffs" DRAFT implementation: not tested See - * https://github.com/espressif/esp-idf/tree/master/examples/storage/sd_card/sdspi - * @ingroup player - * @author Phil Schatzmann - * @copyright GPLv3 - */ - -class VFS_SPFFS : public VFS { - public: - VFS_SPFFS(const char* mountPoint = "/spiffs") { mount_point = mountPoint; }; - void setMountPoint(const char* mp) { mount_point = mp; } - bool begin() { - LOGI("Initializing SPIFFS"); - - conf = {.base_path = mount_point, - .partition_label = NULL, - .max_files = max_files, - .format_if_mount_failed = format_if_mount_failed}; - - // Use settings defined above to initialize and mount SPIFFS filesystem. - // Note: esp_vfs_spiffs_register is an all-in-one convenience function. - LOGI("Mounting filesystem at %s", mount_point); - esp_err_t ret = esp_vfs_spiffs_register(&conf); - - if (ret != ESP_OK) { - if (ret == ESP_FAIL) { - LOGE("Failed to mount or format filesystem"); - } else if (ret == ESP_ERR_NOT_FOUND) { - LOGE("Failed to find SPIFFS partition"); - } else { - LOGE("Failed to initialize SPIFFS (%s)", esp_err_to_name(ret)); - } - return false; - } - - LOGI("Performing SPIFFS_check()."); - ret = esp_spiffs_check(conf.partition_label); - if (ret != ESP_OK) { - LOGE("SPIFFS_check() failed (%s)", esp_err_to_name(ret)); - return false; - } else { - LOGI("SPIFFS_check() successful"); - } - - size_t total = 0, used = 0; - ret = esp_spiffs_info(conf.partition_label, &total, &used); - if (ret != ESP_OK) { - LOGE("Failed to get SPIFFS partition information (%s). Formatting...", - esp_err_to_name(ret)); - // esp_spiffs_format(conf.partition_label); - return false; - } else { - LOGI("Partition size: total: %d, used: %d", total, used); - } - - // Check consistency of reported partition size info. - if (used > total) { - LOGW( - "Number of used bytes cannot be larger than total. Performing " - "SPIFFS_check()."); - ret = esp_spiffs_check(conf.partition_label); - // Could be also used to mend broken files, to clean unreferenced pages, - // etc. More info at - // https://github.com/pellepl/spiffs/wiki/FAQ#powerlosses-contd-when-should-i-run-spiffs_check - if (ret != ESP_OK) { - LOGE("SPIFFS_check() failed (%s)", esp_err_to_name(ret)); - return false; - } else { - LOGI("SPIFFS_check() successful"); - } - } - return true; - } - - void end() { - // All done, unmount partition and disable SPIFFS - esp_vfs_spiffs_unregister(conf.partition_label); - LOGI("SPIFFS unmounted"); - } - - void setMaxFile(int files) { max_files = files; } - void setFormatIfMountFailed(bool format) { format_if_mount_failed = format; } - - protected: - esp_vfs_spiffs_conf_t conf; - int max_files = 5; - bool format_if_mount_failed = true; -}; -} // namespace audio_tools \ No newline at end of file diff --git a/src/AudioTools/CoreAudio/Fade.h b/src/AudioTools/Fade.h similarity index 87% rename from src/AudioTools/CoreAudio/Fade.h rename to src/AudioTools/Fade.h index 71ca5ceba7..8f660a0eb7 100644 --- a/src/AudioTools/CoreAudio/Fade.h +++ b/src/AudioTools/Fade.h @@ -1,5 +1,5 @@ #pragma once -#include "AudioToolsConfig.h" +#include "AudioBasic/Int24.h" #include "AudioStreams.h" namespace audio_tools { @@ -44,7 +44,6 @@ class Fade { /// @param channels /// @param bitsPerSample void convert(uint8_t *data, int bytes, int channels, int bitsPerSample) { - this->channels = channels; int bytes_per_sample = bitsPerSample / 8; switch (bitsPerSample) { case 16: @@ -102,7 +101,6 @@ class Fade { } } } - is_fade_out = false; LOGI("faded out %d frames to volume %f",frames, volume); } @@ -237,32 +235,18 @@ class LastSampleFader { * @author Phil Schatzmann * @copyright GPLv3 */ -class FadeStream : public ModifyingStream { +class FadeStream : public AudioStream { public: FadeStream() = default; - FadeStream(Print &out) { setOutput(out); } - FadeStream(Stream &io) { setStream(io); } + FadeStream(Print &out) { setTarget(out); } + FadeStream(Stream &io) { setTarget(io); } - void setStream(Stream &io) override { + void setTarget(Stream &io) { p_io = &io; p_out = &io; } - void setOutput(Print &out) override { p_out = &out; } - - /// same as setStream - void setOutput(Stream &io) { - p_io = &io; - p_out = &io; - } - - /// same as setOutput - void setStream(Print &out) { p_out = &out; } - - bool begin(AudioInfo info){ - setAudioInfo(info); - return AudioStream::begin(); - } + void setTarget(Print &out) { p_out = &out; } void setAudioInfo(AudioInfo info) override { AudioStream::setAudioInfo(info); @@ -270,31 +254,30 @@ class FadeStream : public ModifyingStream { active = true; } - size_t readBytes(uint8_t *data, size_t len) override { + size_t readBytes(uint8_t *buffer, size_t length) override { if (!active) { LOGE("%s", error_msg); return 0; } - fade.convert(data, len, info.channels, info.bits_per_sample); - fade_last.write(data, len); - return p_io->readBytes(data, len); + fade.convert(buffer, length, info.channels, info.bits_per_sample); + fade_last.write(buffer, length); + return p_io->readBytes(buffer, length); } int available() override { return p_io == nullptr ? 0 : p_io->available(); } - size_t write(const uint8_t *data, size_t len) override { - if (p_out==nullptr) return 0; + size_t write(const uint8_t *buffer, size_t size) override { if (!active) { LOGE("%s", error_msg); return 0; } if (fade.isFadeInActive() || fade.isFadeOutActive()){ - fade.convert((uint8_t *)data, len, info.channels, info.bits_per_sample); + fade.convert((uint8_t *)buffer, size, info.channels, info.bits_per_sample); } // update last information - fade_last.write((uint8_t *)data, len); + fade_last.write((uint8_t *)buffer, size); // write faded data - return p_out->write(data, len); + return p_out->write(buffer, size); } int availableForWrite() override { @@ -311,7 +294,7 @@ class FadeStream : public ModifyingStream { bool isFadeComplete() { return fade.isFadeComplete(); } - /// If you can not provide any more samples we bring the last sample slowy back to 0 + // If can not provide any more samples we bring the last sample slowy back to 0 void writeEnd(Print &print, int steps = 200) { fade_last.end(print, steps); } @@ -330,7 +313,7 @@ class FadeStream : public ModifyingStream { * @ingroup convert * @tparam T */ -template class FadeConverter : public BaseConverter{ +template class FadeConverter : public BaseConverter { public: FadeConverter(int channels) { setChannels(channels); } diff --git a/src/AudioTools/CoreAudio/MusicalNotes.h b/src/AudioTools/MusicalNotes.h similarity index 86% rename from src/AudioTools/CoreAudio/MusicalNotes.h rename to src/AudioTools/MusicalNotes.h index 0b90c114dc..44a1646c85 100644 --- a/src/AudioTools/CoreAudio/MusicalNotes.h +++ b/src/AudioTools/MusicalNotes.h @@ -128,25 +128,25 @@ class MusicalNotes { enum MusicalNotesEnum {C, CS, D, DS, E, F, FS, G, GS, A, AS, B}; /// Determines the frequency of the indicate note and octave (0-8) - float frequency(MusicalNotesEnum note, uint8_t octave) const { + float frequency(MusicalNotesEnum note, uint8_t octave){ if (note>11) return 0; if (octave>8) return 0; return notes[octave][note]; } /// Determines the frequency of the indicate note index from 0 to 107 - float frequency(uint16_t idx) const { + float frequency(uint16_t idx){ MusicalNotesEnum mainNote = (MusicalNotesEnum) (idx % 12); uint8_t octave = idx / 12; return frequency(mainNote, octave); } - int frequencyCount() const { + int frequencyCount() { return 108; } /// Determines the frequency of the indicate main note index (0-6) and octave (0-8) - float mainFrequency(uint8_t mainNoteIdx, uint8_t octave) const { + float mainFrequency(uint8_t mainNoteIdx, uint8_t octave){ if (mainNoteIdx>6) return 0; static int mainNotes[] = {0,2,4,5,7,9,11}; MusicalNotesEnum note = (MusicalNotesEnum) mainNotes[mainNoteIdx]; @@ -154,19 +154,19 @@ class MusicalNotes { } /// Determines the frequency of the indicate main note index from 0 to 62 - float mainFrequency(uint64_t idx) const { + float mainFrequency(uint64_t idx){ uint8_t mainNote = idx % 7; uint8_t level = idx /7; return mainFrequency(mainNote, level); } /// Returns true if the frequency is audible (in the range of 20 Hz to 20 kHz) - bool isAudible(float frequency) const { + bool isAudible(float frequency){ return frequency >= 20 && frequency<=20000; } /// Determines the closes note for a frequency. We also return the frequency difference - const char* note(float frequency, float &diff) const { + const char* note(float frequency, float &diff){ float* all_notes = (float*) notes; const int note_count = 12*9; @@ -187,29 +187,24 @@ class MusicalNotes { } /// Determines the closes note for a frequency - const char* note(float frequency) const { + const char* note(float frequency){ float diff; return note(frequency, diff); } - /// Provides the note name for an index position - const char* noteAt(int idx) { - return notes_str[idx]; - } - /// Determine frequency of MIDI note - float midiNoteToFrequency(int x) const { - float a = 440.0f; //frequency of A (coomon value is 440Hz) - return (a / 32.0f) * powf(2.0f, ((x - 9) / 12.0f)); + float midiNoteToFrequency(int x) { + float a = 440; //frequency of A (coomon value is 440Hz) + return (a / 32) * pow(2, ((x - 9) / 12.0f)); } /// Provide MIDI note for frequency - int frequencyToMidiNote(float freq) const { - return logf(freq/440.0f)/logf(2) * 12.0f + 69.0f; + int frequencyToMidiNote(float freq) { + return log(freq/440.0f)/log(2) * 12.0f + 69.0f; } - float stkNoteToFrequency(int noteNumber) const { - return 220.0f * powf( 2.0f, (noteNumber - 57.0f) / 12.0f ); + float stkNoteToFrequency(int noteNumber){ + return 220.0f * pow( 2.0f, (noteNumber - 57.0f) / 12.0f ); } protected: diff --git a/src/AudioTools/CoreAudio/README.md b/src/AudioTools/README.md similarity index 100% rename from src/AudioTools/CoreAudio/README.md rename to src/AudioTools/README.md diff --git a/src/AudioTools/ResampleStream.h b/src/AudioTools/ResampleStream.h new file mode 100644 index 0000000000..5b36f7fa68 --- /dev/null +++ b/src/AudioTools/ResampleStream.h @@ -0,0 +1,391 @@ +#pragma once + +#include "AudioTools/AudioIO.h" + +namespace audio_tools { + +/** + * @brief ConverterStream Helper class which implements the converting + * readBytes with the help of write. + * @author Phil Schatzmann + * @copyright GPLv3 + * @tparam T class name of the original transformer stream + */ +template +class TransformationReader { + public: + /// @brief setup of the TransformationReader class + /// @param transform The original transformer stream + /// @param byteFactor The factor of how much more data we need to read to get + /// the requested converted bytes + /// @param source The data source of the data to be converted + void begin(T *transform, Stream *source) { + TRACED(); + active = true; + p_stream = source; + p_transform = transform; + if (transform == nullptr) { + LOGE("transform is NULL"); + active = false; + } + if (p_stream == nullptr) { + LOGE("p_stream is NULL"); + active = false; + } + byte_factor = getByteFactor(); + } + + size_t readBytes(uint8_t *data, size_t byteCount) { + LOGD("TransformationReader::readBytes: %d", (int)byteCount); + if (!active) { + LOGE("inactive"); + return 0; + } + if (p_stream == nullptr) { + LOGE("p_stream is NULL"); + return 0; + } + int read_size = byte_factor * byteCount; + LOGD("factor %f -> buffer %d bytes", byte_factor, read_size); + buffer.resize(read_size); + int read = p_stream->readBytes(buffer.data(), read_size); + assert(read == read_size); + Print *tmp = setupOutput(data, byteCount); + p_transform->write(buffer.data(), read_size); + restoreOutput(tmp); + return print_to_array.totalBytesWritten(); + } + + int availableForWrite() { return print_to_array.availableForWrite(); } + + protected: + class AdapterPrintToArray : public AudioOutputAdapter { + public: + void begin(uint8_t *array, size_t data_len) { + TRACED(); + p_data = array; + max_len = data_len; + pos = 0; + } + + int availableForWrite() override { return max_len; } + + size_t write(const uint8_t *data, size_t byteCount) override { + LOGD("AdapterPrintToArray::write: %d (%d)", (int) byteCount, (int) pos); + if (pos + byteCount > max_len) return 0; + memcpy(p_data + pos, data, byteCount); + + pos += byteCount; + return byteCount; + } + + int totalBytesWritten() { return pos; } + + protected: + uint8_t *p_data; + size_t max_len; + size_t pos = 0; + } print_to_array; + float byte_factor = 0.0f; + Stream *p_stream = nullptr; + Vector buffer{0}; // we allocate memory only when needed + T *p_transform = nullptr; + bool active = false; + + /// Makes sure that the data is written to the array + /// @param data + /// @param byteCount + /// @return original output of the converter class + Print *setupOutput(uint8_t *data, size_t byteCount) { + Print *result = p_transform->getPrint(); + p_transform->setStream(print_to_array); + print_to_array.begin(data, byteCount); + + return result; + } + /// @brief restores the original output in the converter class + /// @param out + void restoreOutput(Print *out) { + if (out) p_transform->setStream(*out); + } + + float getByteFactor(){ + buffer.resize(64); + float input_size = 8.0; + Print *tmp = setupOutput(buffer.data(), 64); + p_transform->write(buffer.data(), input_size); + restoreOutput(tmp); + float output_size = print_to_array.totalBytesWritten(); + float factor = input_size/output_size; + LOGI("input_size: %f / output_size: %f / factor (eff): %f", input_size, output_size, factor); + return factor; + } +}; + +/** + * @brief Base class for chained converting streams + * @author Phil Schatzmann + * @copyright GPLv3 + * + */ +class ReformatBaseStream : public AudioStream { + public: + void setupReader() { + assert(getStream() != nullptr); + reader.begin(this, getStream()); + } + + virtual void setStream(Stream &stream) { + p_stream = &stream; + p_print = &stream; + } + + virtual void setStream(Print &print) { p_print = &print; } + + virtual Print *getPrint() { return p_print; } + + virtual Stream *getStream() { return p_stream; } + + size_t readBytes(uint8_t *data, size_t size) override { + LOGD("ReformatBaseStream::readBytes: %d", (int)size); + return reader.readBytes(data, size); + } + + int available() override { + return DEFAULT_BUFFER_SIZE; // reader.availableForWrite(); + } + + int availableForWrite() override { + return DEFAULT_BUFFER_SIZE; // reader.availableForWrite(); + } + + protected: + TransformationReader reader; + Stream *p_stream = nullptr; + Print *p_print = nullptr; + float factor; +}; + +/** + * @brief Optional Configuration object. The critical information is the + * channels and the step_size. All other information is not used. + * + */ +struct ResampleConfig : public AudioInfo { + float step_size = 1.0f; + /// Optional fixed target sample rate + int to_sample_rate = 0; +}; + +/** + * @brief Dynamic Resampling. We can use a variable factor to speed up or slow + * down the playback. + * @author Phil Schatzmann + * @ingroup transform + * @copyright GPLv3 + * @tparam T + */ +class ResampleStream : public ReformatBaseStream { + public: + ResampleStream() = default; + + /// Support for resampling via write. + ResampleStream(Print &out) { setStream(out); } + /// Support for resampling via write. The audio information is copied from the + /// io + ResampleStream(AudioOutput &out) { + setAudioInfo(out.audioInfo()); + setStream(out); + } + + /// Support for resampling via write and read. + ResampleStream(Stream &io) { setStream(io); } + + /// Support for resampling via write and read. The audio information is copied + /// from the io + ResampleStream(AudioStream &io) { + setAudioInfo(io.audioInfo()); + setStream(io); + } + + /// Provides the default configuraiton + ResampleConfig defaultConfig() { + ResampleConfig cfg; + cfg.copyFrom(audioInfo()); + return cfg; + } + + bool begin(ResampleConfig cfg) { + LOGI("begin step_size: %f", cfg.step_size); + setAudioInfo(cfg); + to_sample_rate = cfg.to_sample_rate; + + setupLastSamples(cfg); + setStepSize(cfg.step_size); + is_first = true; + // step_dirty = true; + bytes_per_frame = info.bits_per_sample / 8 * info.channels; + + // setup reader: e.g. if step size is 2 we need to double the input data + // reader.begin(this, step_size, p_stream); + if (p_stream != nullptr) { + setupReader(); + } + + return true; + } + + bool begin(AudioInfo from, int toRate) { + ResampleConfig rcfg; + rcfg.copyFrom(from); + rcfg.to_sample_rate = toRate; + rcfg.step_size = getStepSize(from.sample_rate, toRate); + return begin(rcfg); + } + + bool begin(AudioInfo info, float step) { + ResampleConfig rcfg; + rcfg.copyFrom(info); + rcfg.step_size = step; + return begin(rcfg); + } + + void setAudioInfo(AudioInfo info) override { + AudioStream::setAudioInfo(info); + // update the step size if a fixed to_sample_rate has been defined + if (to_sample_rate != 0) { + setStepSize(getStepSize(info.sample_rate, to_sample_rate)); + } + } + + /// influence the sample rate + void setStepSize(float step) { + LOGI("setStepSize: %f", step); + step_size = step; + } + + /// calculate the step size the sample rate: e.g. from 44200 to 22100 gives a + /// step size of 2 in order to provide fewer samples + float getStepSize(float sampleRateFrom, float sampleRateTo) { + return sampleRateFrom / sampleRateTo; + } + + /// Returns the actual step size + float getStepSize() { return step_size; } + + // int availableForWrite() override { return p_print->availableForWrite(); } + + size_t write(const uint8_t *buffer, size_t bytes) override { + LOGD("ResampleStream::write: %d", (int)bytes); + size_t written; + switch (info.bits_per_sample) { + case 16: + return write(p_print, buffer, bytes, written); + case 24: + return write(p_print, buffer, bytes, written); + case 32: + return write(p_print, buffer, bytes, written); + default: + TRACEE(); + } + return 0; + } + + protected: + Vector last_samples{0}; + float idx = 0; + bool is_first = true; + float step_size = 1.0; + int to_sample_rate = 0; + int bytes_per_frame = 0; + TransformationReader reader; + + /// Sets up the buffer for the rollover samples + void setupLastSamples(AudioInfo cfg) { + int bytes_per_sample = cfg.bits_per_sample / 8; + int last_samples_size = cfg.channels * bytes_per_sample; + last_samples.resize(last_samples_size); + memset(last_samples.data(), 0, last_samples_size); + } + + /// Writes the buffer to p_print after resampling + template + size_t write(Print *p_out, const uint8_t *buffer, size_t bytes, + size_t &written) { + if (step_size == 1.0) { + return p_print->write(buffer, bytes); + } + // prevent npe + if (info.channels == 0) { + LOGE("channels is 0"); + return 0; + } + T *data = (T *)buffer; + int samples = bytes / sizeof(T); + size_t frames = samples / info.channels; + written = 0; + + // avoid noise if audio does not start with 0 + if (is_first) { + is_first = false; + setupLastSamples(data, 0); + } + + info.logInfo(); + + // process all samples + while (idx < frames) { + T frame[info.channels]; + for (int ch = 0; ch < info.channels; ch++) { + T result = getValue(data, idx, ch); + if (p_out->availableForWrite() < sizeof(T)) { + LOGE("Could not write"); + } + frame[ch] = result; + } + written += p_out->write((const uint8_t *)&frame, (size_t)sizeof(frame)); + idx += step_size; + } + + // save last samples + setupLastSamples(data, frames - 1); + idx -= frames; + // returns requested bytes to avoid rewriting of processed bytes + return bytes; + } + + /// get the interpolated value for indicated (float) index value + template + T getValue(T *data, float frame_idx, int channel) { + // interpolate value + int frame_idx1 = frame_idx; + int frame_idx0 = frame_idx1 - 1; + T val0 = lookup(data, frame_idx0, channel); + T val1 = lookup(data, frame_idx1, channel); + + float result = mapFloat(frame_idx, frame_idx1, frame_idx0, val0, val1); + return (float)round(result); + } + + // lookup value for indicated frame & channel: index starts with -1; + template + T lookup(T *data, int frame, int channel) { + if (frame >= 0) { + return data[frame * info.channels + channel]; + } else { + // index -1 + T *pt_last_samples = (T *)last_samples.data(); + return pt_last_samples[channel]; + } + } + // store last samples to provide values for index -1 + template + void setupLastSamples(T *data, int frame) { + for (int ch = 0; ch < info.channels; ch++) { + T *pt_last_samples = (T *)last_samples.data(); + pt_last_samples[ch] = data[(frame * info.channels) + ch]; + } + } +}; + +} // namespace audio_tools diff --git a/src/AudioTools/Sandbox/BLE/AudioBLE.h b/src/AudioTools/Sandbox/BLE/AudioBLE.h deleted file mode 100644 index c98478b76b..0000000000 --- a/src/AudioTools/Sandbox/BLE/AudioBLE.h +++ /dev/null @@ -1,12 +0,0 @@ -#pragma once - -#ifdef ESP32 -//# include "AudioBLEServer.h" -//# include "AudioBLEClient.h" - # include "AudioBLEServerESP32.h" - # include "AudioBLEClientESP32.h" -#else -# include "AudioBLEServer.h" -# include "AudioBLEClient.h" -#endif - diff --git a/src/AudioTools/Sandbox/BLE/AudioBLEClient.h b/src/AudioTools/Sandbox/BLE/AudioBLEClient.h deleted file mode 100644 index 8b0aff9ad7..0000000000 --- a/src/AudioTools/Sandbox/BLE/AudioBLEClient.h +++ /dev/null @@ -1,187 +0,0 @@ -#pragma once - -#include "AudioBLEStream.h" -#include "ConstantsArduino.h" -#include - -namespace audio_tools { - -class AudioBLEClient; -static AudioBLEClient *selfAudioBLEClient = nullptr; - -/** - * @brief A simple BLE client that implements the serial protocol, so that it - * can be used to send and recevie audio. In BLE terminology this is a Central - * @ingroup communications - * @author Phil Schatzmann - * @copyright GPLv3 - */ - -class AudioBLEClient : public AudioBLEStream { -public: - AudioBLEClient(int mtu = BLE_MTU) : AudioBLEStream(mtu) { - selfAudioBLEClient = this; - max_transfer_size = mtu; - } - - /// starts a BLE client - bool begin(const char *localName, int seconds) { - TRACEI(); - // Init BLE device - BLE.begin(); - BLE.setLocalName(localName); - BLE.scanForUuid(BLE_AUDIO_SERVICE_UUID); - return true; - } - - void end() override { - TRACEI(); - flush(); - BLE.end(); - } - - size_t readBytes(uint8_t *data, size_t len) override { - TRACED(); - if (!setupBLEClient()) { - return 0; - } - - if (!ch01_char.canRead()) - return 0; - - return ch01_char.readValue(data, len); - } - - int available() override { return BLE_MTU - BLE_MTU_OVERHEAD; } - - size_t write(const uint8_t *data, size_t len) override { - TRACED(); - if (!setupBLEClient()) { - return 0; - } - - if (!ch02_char.canWrite()) { - return 0; - } - - if (is_framed) { - writeChannel2Characteristic(data, len); - delay(1); - } else { - // send only data with max mtu - for (int j = 0; j < len; j++) { - write_buffer.write(data[j]); - if (write_buffer.isFull()) { - writeChannel2Characteristic(write_buffer.data(), - write_buffer.available()); - write_buffer.reset(); - } - } - } - return len; - } - - int availableForWrite() override { - return is_framed ? (BLE_MTU - BLE_MTU_OVERHEAD) : DEFAULT_BUFFER_SIZE; - } - - bool connected() override { return setupBLEClient(); } - - void setWriteThrottle(int ms) { write_throttle = ms; } - - void setConfirmWrite(bool flag) { write_confirmation_flag = flag; } - -protected: - BLEDevice peripheral; - BLECharacteristic ch01_char; - BLECharacteristic ch02_char; - BLECharacteristic info_char; - SingleBuffer write_buffer{0}; - int write_throttle = 0; - bool write_confirmation_flag = false; - - void writeAudioInfoCharacteristic(AudioInfo info) override { - TRACEI(); - // send update via BLE - info_char.writeValue((uint8_t *)&info, sizeof(AudioInfo)); - } - - void writeChannel2Characteristic(const uint8_t *data, size_t len) { - if (ch02_char.canWrite()) { - ch02_char.writeValue((uint8_t *)data, len, write_confirmation_flag); - delay(write_throttle); - } - } - - bool readAudioInfoCharacteristic() { - if (!info_char.canRead()) - return false; - const uint8_t *str = info_char.value(); - int len = info_char.valueLength(); - if (len > 0) { - setAudioInfo(str, len); - return true; - } - return false; - } - - static void onInfoUpdated(BLEDevice central, - BLECharacteristic characteristic) { - selfAudioBLEClient->setAudioInfo(characteristic.value(), - characteristic.valueLength()); - } - - bool setupBLEClient() { - // if we are already connected there is nothing to do - if (peripheral.connected()) { - return true; - } - - TRACEI(); - - // setup buffer - if (write_buffer.size() == 0) { - write_buffer.resize(getMTU() - BLE_MTU_OVERHEAD); - } - - peripheral = BLE.available(); - if (!peripheral) { - return false; - } - - BLE.stopScan(); - - if (!peripheral.connect()) { - return false; - } - - if (peripheral.discoverAttributes()) { - LOGI("Attributes discovered"); - } else { - LOGE("Attribute discovery failed!"); - peripheral.disconnect(); - return false; - } - - ch01_char = peripheral.characteristic(BLE_CH1_UUID); - if (!ch01_char) { - peripheral.disconnect(); - return false; - } - - ch02_char = peripheral.characteristic(BLE_CH2_UUID); - if (!ch02_char) { - peripheral.disconnect(); - return false; - } - - if (is_audio_info_active) { - info_char = peripheral.characteristic(BLE_INFO_UUID); - info_char.setEventHandler(BLEUpdated, onInfoUpdated); - } - - return true; - } -}; - -} // namespace audio_tools \ No newline at end of file diff --git a/src/AudioTools/Sandbox/BLE/AudioBLEClientESP32.h b/src/AudioTools/Sandbox/BLE/AudioBLEClientESP32.h deleted file mode 100644 index 7902c31c33..0000000000 --- a/src/AudioTools/Sandbox/BLE/AudioBLEClientESP32.h +++ /dev/null @@ -1,274 +0,0 @@ -#pragma once - -#include "AudioBLEStream.h" -#include "ConstantsESP32.h" -//#include -#include -#include -#include - -namespace audio_tools { - -class AudioBLEClient; -static AudioBLEClient *selfAudioBLEClient = nullptr; - -/** - * @brief A simple BLE client that implements the serial protocol, so that it - * can be used to send and recevie audio. In BLE terminology this is a Central - * @ingroup communications - * @author Phil Schatzmann - * @copyright GPLv3 - */ - -class AudioBLEClient : public AudioBLEStream, - public BLEClientCallbacks, - public BLEAdvertisedDeviceCallbacks { -public: - AudioBLEClient(int mtu = BLE_MTU) : AudioBLEStream(mtu) { - selfAudioBLEClient = this; - max_transfer_size = mtu; - } - - /// starts a BLE client - bool begin(const char *localName, int seconds) { - TRACEI(); - // Init BLE device - BLEDevice::init(localName); - - // Retrieve a Scanner and set the callback we want to use to be informed - // when we have detected a new device. - BLEScan *pBLEScan = BLEDevice::getScan(); - pBLEScan->setAdvertisedDeviceCallbacks(this); - pBLEScan->setActiveScan(true); - pBLEScan->start(seconds); - return true; - } - - void end() override { - TRACEI(); - flush(); - BLEDevice::deinit(); - } - - size_t readBytes(uint8_t *data, size_t len) override { - TRACED(); - setupBLEClient(); - if (!is_client_connected || !is_client_set_up) - return 0; - if (!ch01_char->canRead()) - return 0; - // changed to auto to be version independent (it changed from std::string to - // String) - auto str = ch01_char->readValue(); - if (str.length() > 0) { - memcpy(data, str.c_str(), str.length()); - } - return str.length(); - } - - int available() override { return BLE_MTU - BLE_MTU_OVERHEAD; } - - size_t write(const uint8_t *data, size_t len) override { - TRACED(); - setupBLEClient(); - if (!is_client_connected || !is_client_set_up) - return 0; - if (!ch02_char->canWrite()){ - return 0; - } - - if (is_framed){ - writeChannel2Characteristic(data, len); - delay(1); - } else { - // send only data with max mtu - for (int j=0; j write_buffer{0}; - int write_throttle = 0; - bool write_confirmation_flag = false; - - volatile bool is_client_connected = false; - bool is_client_set_up = false; - - void onConnect(BLEClient *pClient) override { - TRACEI(); - is_client_connected = true; - } - - void onDisconnect(BLEClient *pClient) override { - TRACEI(); - is_client_connected = false; - }; - - void writeAudioInfoCharacteristic(AudioInfo info) override { - TRACEI(); - // send update via BLE - info_char->writeValue((uint8_t *)&info, sizeof(AudioInfo)); - } - - void writeChannel2Characteristic(const uint8_t*data, size_t len){ - if (ch02_char->canWrite()) { - ch02_char->writeValue((uint8_t *)data, len, write_confirmation_flag); - delay(write_throttle); - } - } - - bool readAudioInfoCharacteristic(){ - if (!info_char->canRead()) - return false; - auto str = info_char->readValue(); - if (str.length() > 0) { - setAudioInfo((const uint8_t*)str.c_str(), str.length()); - return true; - } - return false; - } - - // Scanning Results - void onResult(BLEAdvertisedDevice advertisedDevice) override { - TRACEI(); - // Check if the name of the advertiser matches - if (advertisedDevice.haveServiceUUID() && - advertisedDevice.isAdvertisingService(BLUEID_AUDIO_SERVICE_UUID)) { - LOGI("Service '%s' found!", BLE_AUDIO_SERVICE_UUID); - // save advertised_device in class variable - advertised_device = advertisedDevice; - // Scan can be stopped, we found what we are looking for - advertised_device.getScan()->stop(); - } - delay(10); - } - - static void notifyCallback(BLERemoteCharacteristic *pBLERemoteCharacteristic, - uint8_t *pData, size_t length, bool isNotify) { - TRACEI(); - if (pBLERemoteCharacteristic->getUUID().toString() == - selfAudioBLEClient->BLE_INFO_UUID) { - selfAudioBLEClient->setAudioInfo(pData, length); - } - } - - bool setupBLEClient() { - if (is_client_set_up) - return true; - - TRACEI(); - - // setup buffer - if (write_buffer.size()==0){ - write_buffer.resize(getMTU() - BLE_MTU_OVERHEAD); - } - - if (p_client == nullptr) - p_client = BLEDevice::createClient(); - - // onConnect and on onDisconnect support - p_client->setClientCallbacks(this); - - // Connect to the remove BLE Server. - LOGI("Connecting to %s ...", - advertised_device.getAddress().toString().c_str()); - // p_client->connect(advertised_device.getAddress(),BLE_ADDR_TYPE_RANDOM); - p_client->connect(&advertised_device); - if (!p_client->isConnected()) { - LOGE("Connect failed"); - return false; - } - LOGI("Connected to %s ...", - advertised_device.getAddress().toString().c_str()); - - LOGI("Setting mtu to %d", max_transfer_size); - assert(max_transfer_size > 0); - p_client->setMTU(max_transfer_size); - - // Obtain a reference to the service we are after in the remote BLE - // server. - if (p_remote_service == nullptr) { - p_remote_service = p_client->getService(BLUEID_AUDIO_SERVICE_UUID); - if (p_remote_service == nullptr) { - LOGE("Failed to find our service UUID: %s", BLE_AUDIO_SERVICE_UUID); - return (false); - } - } - - if (ch01_char == nullptr) { - ch01_char = p_remote_service->getCharacteristic(BLUEID_CH1_UUID); - if (ch01_char == nullptr) { - LOGE("Failed to find char. UUID: %s", BLE_CH1_UUID); - return false; - } - } - - if (ch02_char == nullptr) { - ch02_char = p_remote_service->getCharacteristic(BLUEID_CH2_UUID); - if (ch02_char == nullptr) { - LOGE("Failed to find char. UUID: %s", BLE_CH2_UUID); - return false; - } - } - - if (is_audio_info_active && info_char == nullptr) { - info_char = p_remote_service->getCharacteristic(BLUEID_INFO_UUID); - if (info_char == nullptr) { - LOGE("Failed to find char. UUID: %s", BLE_INFO_UUID); - return false; - } - info_char->registerForNotify(notifyCallback); - readAudioInfoCharacteristic(); - - } - LOGI("Connected to server: %s", is_client_connected ? "true" : "false"); - is_client_set_up = true; - is_client_connected = true; - return is_client_connected; - } - - int getMTU() override { return BLE_MTU; } - - -}; - -} // namespace audio_tools \ No newline at end of file diff --git a/src/AudioTools/Sandbox/BLE/AudioBLEServer.h b/src/AudioTools/Sandbox/BLE/AudioBLEServer.h deleted file mode 100644 index 242423faca..0000000000 --- a/src/AudioTools/Sandbox/BLE/AudioBLEServer.h +++ /dev/null @@ -1,297 +0,0 @@ -#pragma once - -#include "AudioBLEStream.h" -#include "ConstantsArduino.h" -#include -#include "AudioTools/CoreAudio/AudioBasic/StrView.h" - -namespace audio_tools { - -class AudioBLEServer; -class AudioBLEServer *selfAudioBLEServer = nullptr; - -/** - * @brief A simple BLE server that implements the serial protocol, so that it - * can be used to send and recevie audio. In BLE terminologiy this is a - * Peripheral. - * This implementation uses the ArduinoBLE library! - * This is working only correctly if the client sets the max MTU to a value >= 256. - * Otherwise some of the transmitted information gets silently dropped - * @ingroup communications - * @author Phil Schatzmann - * @copyright GPLv3 - */ - -class AudioBLEServer : public AudioBLEStream { -public: - AudioBLEServer(int mtu = 0) : AudioBLEStream(mtu) { - selfAudioBLEServer = this; - } - - // starts a BLE server with the indicated name - bool begin(const char *name) { - TRACEI(); - ble_server_name = name; - - if (!BLE.begin()) { - LOGE("starting BLE failed"); - return false; - } - - // set the local name peripheral advertises - BLE.setLocalName(ble_server_name); - - // assign event handlers for connected, disconnected to peripheral - BLE.setEventHandler(BLEConnected, blePeripheralConnectHandler); - BLE.setEventHandler(BLEDisconnected, blePeripheralDisconnectHandler); - - // setup serice with characteristics - setupBLEService(); - - // start advertising - BLE.advertise(); - - return true; - } - - void end() override { - TRACEI(); - flush(); - BLE.end(); - } - - size_t readBytes(uint8_t *data, size_t len) override { - TRACED(); - if (!checkCentralConnected()) - return 0; - size_t read_size = getReadSize(len); - return receive_buffer.readArray(data, read_size); - } - - int available() override { - if (!checkCentralConnected()) - return 0; - if (is_framed) - return receive_sizes.peek(); - return this->receive_buffer.available(); - } - - size_t write(const uint8_t *data, size_t len) override { - LOGD("AudioBLEStream::write: %d", len); - if (!checkCentralConnected()) - return 0; - if (is_framed && availableForWrite() < len) { - return 0; - } - return transmit_buffer.writeArray(data, len); - } - - int availableForWrite() override { - if (!checkCentralConnected()) - return 0; - setupTXBuffer(); - int result = transmit_buffer.availableForWrite(); - // make sure we copy always a consistent amount of data - if (result < DEFAULT_BUFFER_SIZE) - result = 0; - return result; - } - - bool connected() override { return checkCentralConnected(); } - -protected: - // server - BLEDevice central; - BLEService service{BLE_AUDIO_SERVICE_UUID}; // create service - BLECharacteristic ch01_char{BLE_CH1_UUID, BLERead, getMTU()}; - BLECharacteristic ch02_char{BLE_CH2_UUID, BLEWrite, getMTU()}; - BLECharacteristic info_char{BLE_INFO_UUID, BLERead | BLEWrite | BLENotify, - 80}; - BLEDescriptor ch01_desc{"2901", "channel 1"}; - BLEDescriptor ch02_desc{"2901", "channel 2"}; - BLEDescriptor info_desc{"2901", "info"}; - - RingBuffer receive_buffer{0}; - RingBuffer receive_sizes{0}; - RingBuffer transmit_buffer{0}; - RingBuffer transmit_buffer_sizes{0}; - - static void blePeripheralConnectHandler(BLEDevice device) { - selfAudioBLEServer->onConnect(device); - } - - static void blePeripheralDisconnectHandler(BLEDevice device) { - selfAudioBLEServer->onDisconnect(device); - } - - static void bleOnWrite(BLEDevice device, BLECharacteristic characteristic) { - selfAudioBLEServer->onWrite(characteristic); - } - - static void bleOnRead(BLEDevice device, BLECharacteristic characteristic) { - TRACED(); - selfAudioBLEServer->onRead(characteristic); - } - - void onConnect(BLEDevice device) { TRACEI(); } - - void onDisconnect(BLEDevice device) { - TRACEI(); - BLE.advertise(); - } - - /// store the next batch of data - void onWrite(BLECharacteristic characteristic) { - TRACED(); - setupRXBuffer(); - // changed to auto to be version independent (it changed from std::string to - // String) - if (StrView(BLE_INFO_UUID).equals(characteristic.uuid())) { - setAudioInfo((uint8_t *)characteristic.value(), - characteristic.valueLength()); - } else { - receiveAudio((uint8_t *)characteristic.value(), - characteristic.valueLength()); - } - } - - /// provide the next batch of audio data - void onRead(BLECharacteristic characteristic) { - TRACED(); - auto uuid = StrView(characteristic.uuid()); - if (uuid == BLE_CH1_UUID || uuid == BLE_CH2_UUID) { - TRACEI(); - int len = std::min(getMTU(), (int)transmit_buffer.available()); - if (is_framed) { - len = transmit_buffer_sizes.read(); - } - LOGI("%s: len: %d, buffer: %d", uuid.c_str(), len, - transmit_buffer.size()); - if (len > 0) { - uint8_t tmp[len]; - transmit_buffer.peekArray(tmp, len); - if (characteristic.writeValue(tmp, len)) { - transmit_buffer.readArray(tmp, len); - } else { - LOGW("writeValue failed") - } - } - } - } - - bool checkCentralConnected() { - central = BLE.central(); - // if a central is connected to the peripheral: - if (central) - return central.connected(); - return false; - } - - virtual void receiveAudio(const uint8_t *data, size_t size) { - while (receive_buffer.availableForWrite() < size) { - // wait for ringbuffer to get freed up - delay(10); - } - if (is_framed) - receive_sizes.write(size); - receive_buffer.writeArray(data, size); - } - - void writeAudioInfoCharacteristic(AudioInfo info) { - TRACEI(); - // send update via BLE - StrView str = toStr(info); - LOGI("AudioInfo: %s", str.c_str()); - info_char.setValue((uint8_t *)str.c_str(), str.length() + 1); - } - - int getMTU() override { - TRACED(); - if (max_transfer_size == 0) { - // int peer_max_transfer_size = - // p_server->getPeerMTU(p_server->getConnId()) - BLE_MTU_OVERHEAD; - // max_transfer_size = std::min(BLE_MTU - BLE_MTU, - // peer_max_transfer_size); - // max_transfer_size = central.mtu() - BLE_MTU_OVERHEAD; - max_transfer_size = BLE_MTU - BLE_MTU_OVERHEAD; - - LOGI("max_transfer_size: %d", max_transfer_size); - } - return max_transfer_size; - } - - void setupBLEService() { - TRACEI(); - // set the UUID for the service this peripheral advertises - BLE.setAdvertisedService(service); - - ch01_char.addDescriptor(ch01_desc); - ch02_char.addDescriptor(ch02_desc); - - // add the characteristic to the service - service.addCharacteristic(ch01_char); - service.addCharacteristic(ch02_char); - - // assign event handlers for characteristic - ch02_char.setEventHandler(BLEWritten, bleOnWrite); - ch01_char.setEventHandler(BLERead, bleOnRead); - - if (is_audio_info_active) { - info_char.addDescriptor(info_desc); - service.addCharacteristic(info_char); - } - - // add service - BLE.addService(service); - - // provide AudioInfo - if (is_audio_info_active) { - writeAudioInfoCharacteristic(info); - } - - // Read callback works only when we provide some initial data - uint8_t tmp[512] = {0xFF}; - ch01_char.writeValue(tmp, 512, false); - } - - void setupTXBuffer() { - if (transmit_buffer.size() == 0) { - LOGI("Setting transmit_buffer to %d", RX_BUFFER_SIZE); - transmit_buffer.resize(TX_BUFFER_SIZE); - if (is_framed) { - transmit_buffer_sizes.resize(TX_COUNT); - } - } - } - - void setupRXBuffer() { - if (receive_buffer.size() == 0) { - LOGI("Setting receive_buffer to %d for mtu %d", RX_BUFFER_SIZE, getMTU()); - receive_buffer.resize(RX_BUFFER_SIZE); - if (is_framed) { - receive_sizes.resize(RX_COUNT); - } - } - } - - size_t getReadSize(size_t dataSize) { - size_t read_size = dataSize; - if (is_framed) { - read_size = 0; - if (receive_sizes.available() > 0) { - read_size = receive_sizes.read(); - } - if (dataSize < read_size) { - LOGE("read size too small: %d - it must be >= %d", dataSize, read_size); - return 0; - } - if (receive_buffer.available() < read_size) { - LOGE("missing data in buffer"); - return 0; - } - } - return read_size; - } -}; - -} // namespace audio_tools \ No newline at end of file diff --git a/src/AudioTools/Sandbox/BLE/AudioBLEServerESP32.h b/src/AudioTools/Sandbox/BLE/AudioBLEServerESP32.h deleted file mode 100644 index c272459a89..0000000000 --- a/src/AudioTools/Sandbox/BLE/AudioBLEServerESP32.h +++ /dev/null @@ -1,252 +0,0 @@ -#pragma once - -#include "AudioBLEStream.h" -#include "ConstantsESP32.h" -//#include -#include -#include -#include -#include "AudioTools/CoreAudio/AudioBasic/StrView.h" - -namespace audio_tools { -/** - * @brief A simple BLE server that implements the serial protocol, so that it - * can be used to send and recevie audio. In BLE terminologiy this is a Peripheral. - * @ingroup communications - * @author Phil Schatzmann - * @copyright GPLv3 - */ - -class AudioBLEServer : public AudioBLEStream, - public BLECharacteristicCallbacks, - public BLEServerCallbacks { -public: - AudioBLEServer(int mtu = BLE_MTU) : AudioBLEStream(mtu) {} - - // starts a BLE server with the indicated name - bool begin(const char *name) { - TRACEI(); - ble_server_name = name; - BLEDevice::init(name); - - p_server = BLEDevice::createServer(); - p_server->setCallbacks(this); - - setupBLEService(); - p_advertising = BLEDevice::getAdvertising(); - p_advertising->addServiceUUID(BLE_AUDIO_SERVICE_UUID); - BLEDevice::startAdvertising(); - return true; - } - - void end() override { - TRACEI(); - flush(); - BLEDevice::deinit(); - } - - size_t readBytes(uint8_t *data, size_t len) override { - TRACED(); - size_t read_size = getReadSize(len); - return receive_buffer.readArray(data, read_size); - } - - int available() override { - if (is_framed) - return receive_sizes.peek(); - return this->receive_buffer.available(); - } - - size_t write(const uint8_t *data, size_t dataSize) override { - LOGD("AudioBLEStream::write: %d", dataSize); - if (!connected()) { - return 0; - } - if (is_framed && availableForWrite() < dataSize) { - return 0; - } - return transmit_buffer.writeArray(data, dataSize); - } - - int availableForWrite() override { - int result = transmit_buffer.availableForWrite(); - // make sure we copy always a consistent amount of data - if (result < DEFAULT_BUFFER_SIZE) result = 0; - return result ; - } - - bool connected() override { return p_server->getConnectedCount() > 0; } - -protected: - // server - BLEServer *p_server = nullptr; - BLEService *p_service = nullptr; - BLEAdvertising *p_advertising = nullptr; - BLECharacteristic *ch01_char; - BLECharacteristic *ch02_char; - BLECharacteristic *info_char; - BLEDescriptor ch01_desc{"2901"}; - BLEDescriptor ch02_desc{"2901"}; - BLEDescriptor info_desc{"2901"}; - RingBuffer receive_buffer{0}; - RingBuffer receive_sizes{0}; - RingBuffer transmit_buffer{0}; - RingBuffer transmit_buffer_sizes{0}; - - virtual void receiveAudio(const uint8_t *data, size_t size) { - while (receive_buffer.availableForWrite() < size) { - // wait for ringbuffer to get freed up - delay(10); - } - if (is_framed) - receive_sizes.write(size); - receive_buffer.writeArray(data, size); - } - - void writeAudioInfoCharacteristic(AudioInfo info) { - TRACEI(); - // send update via BLE - StrView str = toStr(info); - LOGI("AudioInfo: %s", str.c_str()); - info_char->setValue((uint8_t *)str.c_str(), str.length() + 1); - info_char->notify(); - } - - int getMTU() override { - TRACED(); - if (max_transfer_size == 0) { - int peer_max_transfer_size = - p_server->getPeerMTU(p_server->getConnId()) - BLE_MTU_OVERHEAD; - max_transfer_size = std::min(BLE_MTU - BLE_MTU, peer_max_transfer_size); - - LOGI("max_transfer_size: %d", max_transfer_size); - } - return max_transfer_size; - } - - void setupBLEService() { - TRACEI(); - // characteristic property is what the other device does. - - if (p_service == nullptr) { - p_service = p_server->createService(BLE_AUDIO_SERVICE_UUID); - - ch01_char = p_service->createCharacteristic( - BLE_CH1_UUID, BLECharacteristic::PROPERTY_READ ); - ch01_desc.setValue("Channel 1"); - ch01_char->addDescriptor(&ch01_desc); - ch01_char->setCallbacks(this); - - ch02_char = p_service->createCharacteristic( - BLE_CH2_UUID, BLECharacteristic::PROPERTY_WRITE); - ch02_desc.setValue("Channel 2"); - ch02_char->addDescriptor(&ch02_desc); - ch02_char->setCallbacks(this); - - // optional setup of audio info notifications - if (is_audio_info_active && info_char == nullptr) { - - info_char = p_service->createCharacteristic( - BLE_INFO_UUID, BLECharacteristic::PROPERTY_NOTIFY | - BLECharacteristic::PROPERTY_READ | - BLECharacteristic::PROPERTY_NOTIFY | - BLECharacteristic::PROPERTY_INDICATE); - info_desc.setValue("Audio Info"); - info_char->addDescriptor(&info_desc); - info_char->setCallbacks(this); - - } - - p_service->start(); - - getMTU(); - - if (info_char != nullptr) { - writeAudioInfoCharacteristic(info); - } - } - } - - void onConnect(BLEServer *pServer) override { - TRACEI(); - } - - void onDisconnect(BLEServer *pServer) override { - TRACEI(); - BLEDevice::startAdvertising(); - } - - /// store the next batch of data - void onWrite(BLECharacteristic *pCharacteristic) override { - TRACED(); - setupRXBuffer(); - // changed to auto to be version independent (it changed from std::string to String) - auto value = pCharacteristic->getValue(); - if (pCharacteristic->getUUID().toString() == BLE_INFO_UUID) { - setAudioInfo((uint8_t *)&value[0], value.length()); - } else { - receiveAudio((uint8_t *)&value[0], value.length()); - } - } - - /// provide the next batch of audio data - void onRead(BLECharacteristic *pCharacteristic) override { - TRACED(); - // changed to auto to be version independent (it changed from std::string to String) - auto uuid = pCharacteristic->getUUID().toString(); - if (uuid == BLE_CH1_UUID || uuid == BLE_CH2_UUID) { - setupTXBuffer(); - int len = std::min(getMTU() - BLE_MTU_OVERHEAD, (int)transmit_buffer.available()); - if (is_framed) { - len = transmit_buffer_sizes.read(); - } - LOGD("%s: len: %d, buffer: %d", uuid.c_str(), len, - transmit_buffer.size()); - uint8_t tmp[len]; - transmit_buffer.readArray(tmp, len); - pCharacteristic->setValue(tmp, len); - } - } - - void setupTXBuffer() { - if (transmit_buffer.size() == 0) { - LOGI("Setting transmit_buffer to %d for mtu %d", RX_BUFFER_SIZE, getMTU()); - transmit_buffer.resize(TX_BUFFER_SIZE); - if (is_framed) { - transmit_buffer_sizes.resize(TX_COUNT); - } - } - } - - void setupRXBuffer() { - if (receive_buffer.size() == 0) { - LOGI("Setting receive_buffer to %d for mtu %d", RX_BUFFER_SIZE, getMTU()); - receive_buffer.resize(RX_BUFFER_SIZE); - if (is_framed) { - receive_sizes.resize(RX_COUNT); - } - } - } - - size_t getReadSize(size_t dataSize) { - size_t read_size = dataSize; - if (is_framed) { - read_size = 0; - if (receive_sizes.available() > 0) { - read_size = receive_sizes.read(); - } - if (dataSize < read_size) { - LOGE("read size too small: %d - it must be >= %d", dataSize, read_size); - return 0; - } - if (receive_buffer.available() < read_size) { - LOGE("missing data in buffer"); - return 0; - } - } - return read_size; - } -}; - - -} // namespace audio_tools \ No newline at end of file diff --git a/src/AudioTools/Sandbox/BLE/AudioBLEStream.h b/src/AudioTools/Sandbox/BLE/AudioBLEStream.h deleted file mode 100644 index 49e289fdaa..0000000000 --- a/src/AudioTools/Sandbox/BLE/AudioBLEStream.h +++ /dev/null @@ -1,93 +0,0 @@ -#pragma once - -#include "AudioToolsConfig.h" -#include "AudioTools/CoreAudio/AudioBasic/Collections/Vector.h" -#include "AudioTools/CoreAudio/Buffers.h" -#include "AudioTools/CoreAudio/AudioBasic/StrView.h" - - -namespace audio_tools { - -/** - * @ingroup main - * @brief Transmit and receive data via BLE using a Serial API. - * The following additional experimental features are offered: - * setFramed(true) tries to keep the original write sizes; - * setAudioInfoActive(true) informs about changes in the audio info - */ - -class AudioBLEStream : public AudioStream { -public: - AudioBLEStream(int defaultMTU) { max_transfer_size = defaultMTU; }; - - virtual void end() = 0; - - virtual bool connected() = 0; - - void setAudioInfo(AudioInfo info) { - if (is_audio_info_active && this->info != info) { - TRACED(); - AudioStream::setAudioInfo(info); - writeAudioInfoCharacteristic(info); - } - } - - operator bool() { return connected(); } - - void setServiceUUID(const char *uuid) { BLE_AUDIO_SERVICE_UUID = uuid; } - - void setRxUUID(const char *uuid) { BLE_CH2_UUID = uuid; } - - void setTxUUID(const char *uuid) { BLE_CH1_UUID = uuid; } - - void setAudioInfoUUID(const char *uuid) { BLE_INFO_UUID = uuid; } - - void setAudioInfoActive(bool flag) { is_audio_info_active = flag; } - - void setFramed(bool flag) { is_framed = flag; } - - StrView toStr(AudioInfo info) { - snprintf(audio_info_str, 40, "%d:%d:%d", info.sample_rate, info.channels, - info.bits_per_sample); - return StrView(audio_info_str); - } - - AudioInfo toInfo(const uint8_t *str) { - AudioInfo result; - sscanf((char*)str,"%d:%d:%d", &result.sample_rate, &result.channels, &result.bits_per_sample); - return result; - } - -protected: - // disable copy constructor - AudioBLEStream(AudioBLEStream const &other) = delete; - // disable assign constructor - void operator=(AudioBLEStream const &other) = delete; - const char *ble_server_name = nullptr; - uint16_t max_transfer_size = 0; - bool is_started = false; - bool is_audio_info_active = false; - bool is_framed = false; - char audio_info_str[40]; - - // Bluetooth LE GATT UUIDs for the Nordic UART profile Change UUID here if - // required - const char *BLE_AUDIO_SERVICE_UUID = "6e400001-b5a3-f393-e0a9-e50e24dcca9e"; - const char *BLE_CH1_UUID = "6e400002-b5a3-f393-e0a9-e50e24dcca9e"; // RX - const char *BLE_CH2_UUID = "6e400003-b5a3-f393-e0a9-e50e24dcca9e"; // TX - const char *BLE_INFO_UUID = "6e400004-b5a3-f393-e0a9-e50e24dcca9e"; - - virtual int getMTU() = 0; - - // override to implement your own extended logic - virtual void setAudioInfo(const uint8_t *data, size_t size) { - if (is_audio_info_active) { - AudioInfo ai = toInfo(data); - setAudioInfo(ai); - } - } - // override to implement your own extended logic - virtual void writeAudioInfoCharacteristic(AudioInfo info) = 0; -}; - -} // namespace audio_tools \ No newline at end of file diff --git a/src/AudioTools/Sandbox/BLE/ConstantsArduino.h b/src/AudioTools/Sandbox/BLE/ConstantsArduino.h deleted file mode 100644 index 2f31440089..0000000000 --- a/src/AudioTools/Sandbox/BLE/ConstantsArduino.h +++ /dev/null @@ -1,9 +0,0 @@ -#pragma once - -// must be greater than MTU, less than ESP_GATT_MAX_ATTR_LEN -#define BLE_MTU 242 -#define BLE_MTU_OVERHEAD 5 -#define RX_BUFFER_SIZE 4096 -#define RX_COUNT 100 -#define TX_BUFFER_SIZE 4096 -#define TX_COUNT 100 diff --git a/src/AudioTools/Sandbox/BLE/ConstantsESP32.h b/src/AudioTools/Sandbox/BLE/ConstantsESP32.h deleted file mode 100644 index 4bf6963709..0000000000 --- a/src/AudioTools/Sandbox/BLE/ConstantsESP32.h +++ /dev/null @@ -1,9 +0,0 @@ -#pragma once - -// must be greater than MTU, less than ESP_GATT_MAX_ATTR_LEN -#define BLE_MTU 517 -#define BLE_MTU_OVERHEAD 5 -#define RX_BUFFER_SIZE 4096 -#define RX_COUNT 100 -#define TX_BUFFER_SIZE 4096 -#define TX_COUNT 100 diff --git a/src/AudioTools/Sandbox/DynamicResamplingQueueStream.h b/src/AudioTools/Sandbox/DynamicResamplingQueueStream.h deleted file mode 100644 index d383a2df45..0000000000 --- a/src/AudioTools/Sandbox/DynamicResamplingQueueStream.h +++ /dev/null @@ -1,104 +0,0 @@ -#pragma once - -#include "AudioTools/AudioLibs/PIDController.h" -#include "AudioTools/CoreAudio/AudioBasic/MovingAverage.h" -#include "AudioTools/CoreAudio/AudioStreams.h" -#include "AudioTools/CoreAudio/ResampleStream.h" - -namespace audio_tools { -/** - * @brief An Audio Stream backed by a buffer (queue) which tries to correct - * jitter and automatically adjusts for the slightly different clock rates - * between an audio source and audio target. Use separate tasks to write and - * read the data. Also make sure that you protect the access with a mutex or - * provide a thread save buffer! - * - * The resamping step size is calculated with the help of a PID controller. - * - * @ingroup buffers - * @author Phil Schatzmann - */ -class DynamicResamplingQueueStream : public AudioStream { - public: - DynamicResamplingQueueStream(BaseBuffer& buffer, - float stepRangePercent = 0.05) { - p_buffer = &buffer; - setStepRangePercent(stepRangePercent); - addNotifyAudioChange(resample_stream); - } - - bool begin() { - if (p_buffer == nullptr) return false; - queue_stream.setBuffer(*p_buffer); - queue_stream.begin(); - resample_stream.setAudioInfo(audioInfo()); - resample_stream.setStream(queue_stream); - resample_stream.begin(audioInfo()); - float from_step = 1.0 - resample_range; - float to_step = 1.0 + resample_range; - return pid.begin(1.0, from_step, to_step, p, i, d); - } - - /// Fill the buffer - size_t write(const uint8_t* data, size_t len) override { - if (p_buffer == 0) return 0; - return p_buffer->writeArray(data, len); - } - - void end() { - queue_stream.end(); - resample_stream.end(); - } - - /// Read resampled data from the buffer - size_t readBytes(uint8_t* data, size_t len) override { - if (p_buffer->available() == 0) return 0; - - // calculate new resampling step size - moving_average_level_percent.add(p_buffer->levelPercent()); - step_size = pid.calculate(50.0, moving_average_level_percent.average()); - - // log step size every 10th read - if (read_count++ % 10 == 0) { - LOGI("step_size: %f", step_size); - } - - // return resampled result - resample_stream.setStepSize(step_size); - return resample_stream.readBytes(data, len); - } - - /// Defines the number of historic %fill levels that will be used to calculate - /// the moving avg - void setMovingAvgCount(int size) { - moving_average_level_percent.setSize(size); - } - - /// e.g. a value of 0.0005 means that we allow to resample for 44100 by - /// +- 22.05 from 44077.95 to 44122.05 - void setStepRangePercent(float rangePercent) { - resample_range = rangePercent / 100.0; - } - - /// Define the PID parameters - void setPIDParameters(float p_value, float i_value, float d_value) { - p = p_value; - i = i_value; - d = d_value; - } - - protected: - PIDController pid; // p=0.005, i=0.00005, d=0.0001 - QueueStream queue_stream; - BaseBuffer* p_buffer = nullptr; - MovingAverage moving_average_level_percent{50}; - ResampleStream resample_stream; - float step_size = 1.0; - float resample_range = 0; - float p = 0.005; - float i = 0.00005; - float d = 0.0001; - uint32_t read_count = 0; -}; - -} // namespace audio_tools diff --git a/src/AudioTools/Sandbox/FrequencyDetection.h b/src/AudioTools/Sandbox/FrequencyDetection.h deleted file mode 100644 index b3b8165f92..0000000000 --- a/src/AudioTools/Sandbox/FrequencyDetection.h +++ /dev/null @@ -1,244 +0,0 @@ -#include "AudioTools/CoreAudio/AudioStreams.h" - -namespace audio_tools { - -/** - * @brief Determine Frequency using Audio Correlation. - * based on https://github.com/akellyirl/AutoCorr_Freq_detect. - * @author Phil Schatzmann - * @copyright GPLv3 - * @ingroup io -*/ -class FrequncyAutoCorrelationStream : public AudioStream { - public: - FrequncyAutoCorrelationStream() = default; - - FrequncyAutoCorrelationStream(Print& out) { - p_out = &out; - }; - - FrequncyAutoCorrelationStream(Stream& in) { - p_out = ∈ - p_in = ∈ - }; - - bool begin(AudioInfo info){ - setAudioInfo(info); - return AudioStream::begin(); - } - - int available()override{ - if (p_in) return p_in->available(); - return 0; - } - - int availableForWrite()override{ - if (p_out) return p_out->availableForWrite(); - return DEFAULT_BUFFER_SIZE; - } - - size_t readBytes(uint8_t *data, size_t len) override { - size_t result = p_in->readBytes(data, len); - switch(info.bits_per_sample){ - case 16: - detect((int16_t*)data, len/sizeof(int16_t)); - break; - case 24: - detect((int24_t*)data, len/sizeof(int24_t)); - break; - case 32: - detect((int32_t*)data, len/sizeof(int32_t)); - break; - } - return result; - } - - virtual size_t write(const uint8_t *data, size_t len) override { - switch(info.bits_per_sample){ - case 16: - detect((int16_t*)data, len/sizeof(int16_t)); - break; - case 24: - detect((int24_t*)data, len/sizeof(int24_t)); - break; - case 32: - detect((int32_t*)data, len/sizeof(int32_t)); - break; - } - - size_t result = len; - if (p_out!=nullptr) result = p_out->write(data, len); - return result; - } - - /// provides the determined frequncy - float frequency(int channel) { - if(channel>=info.channels) { - LOGE("Invalid channel: %d", channel); - return 0; - } - return freq[channel]; - } - - protected: - Vector freq; - Print *p_out=nullptr; - Stream *p_in = nullptr; - float sum=0, sum_old=0; - - template - void detect(T* samples, size_t len) { - freq.resize(info.channels); - for (int ch=0; ch - float detectChannel(int channel, T* samples, size_t len) { - int thresh = 0; - uint8_t pd_state = 0; - int period = 0; - T max_value = NumberConverter::maxValue(info.bits_per_sample); - // Autocorrelation - for(int i = channel; i < len; i+=info.channels) { - sum_old = sum; - sum = 0; - for(int k = channel; k < len-i; k+=info.channels) { - sum += (samples[k])*(samples[k+i]); // / max_value; - } - - // Peak Detect State Machine - if (pd_state == 2 && (sum-sum_old) <=0) { - period = i; - pd_state = 3; - } - if (pd_state == 1 && (sum > thresh) && (sum-sum_old) > 0) { - pd_state = 2; - } - if (pd_state == 0) { - thresh = sum * 0.5; - pd_state = 1; - } - } - // Frequency identified in Hz - return static_cast(info.sample_rate) / period; - } -}; - -/** - * @brief Determine Frequency using upward 0 crossings. - * @author Phil Schatzmann - * @copyright GPLv3 - * @ingroup io -*/ - -class FrequncyZeroCrossingStream : public AudioStream { - public: - FrequncyZeroCrossingStream() = default; - - FrequncyZeroCrossingStream(Print& out) { - p_out = &out; - }; - - FrequncyZeroCrossingStream(Stream& in) { - p_out = ∈ - p_in = ∈ - }; - - bool begin(AudioInfo info){ - setAudioInfo(info); - return AudioStream::begin(); - } - - int available()override{ - if (p_in) return p_in->available(); - return 0; - } - - int availableForWrite()override{ - if (p_out) return p_out->availableForWrite(); - return DEFAULT_BUFFER_SIZE; - } - - size_t readBytes(uint8_t *data, size_t len) override { - size_t result = p_in->readBytes(data, len); - switch(info.bits_per_sample){ - case 16: - detect((int16_t*)data, len/sizeof(int16_t)); - break; - case 24: - detect((int24_t*)data, len/sizeof(int24_t)); - break; - case 32: - detect((int32_t*)data, len/sizeof(int32_t)); - break; - } - return result; - } - - virtual size_t write(const uint8_t *data, size_t len) override { - switch(info.bits_per_sample){ - case 16: - detect((int16_t*)data, len/sizeof(int16_t)); - break; - case 24: - detect((int24_t*)data, len/sizeof(int24_t)); - break; - case 32: - detect((int32_t*)data, len/sizeof(int32_t)); - break; - } - - size_t result = len; - if (p_out!=nullptr) result = p_out->write(data, len); - return result; - } - - /// provides the determined frequncy - float frequency(int channel) { - if(channel>=info.channels) { - LOGE("Invalid channel: %d", channel); - return 0; - } - return freq[channel]; - } - - void setFrequencyCallback(void (*callback)(int channel, float freq)){ - notify = callback; - } - - protected: - Vector freq; - Print *p_out=nullptr; - Stream *p_in = nullptr; - int count = 0; - bool active = false; - void (*notify)(int channel, float freq); - - template - void detect(T* samples, size_t len) { - freq.resize(info.channels); - for (int ch=0; ch - void detectChannel(int channel, T* samples, size_t len) { - for(int i = channel; i < (len-info.channels); i+=info.channels) { - // start counter at first crossing - if (active) count++; - // update frequncy at each upward zero crossing - if (samples[i]<=0 && samples[i+info.channels]>0) { - freq[channel] = (1.0f * info.sample_rate) / count; - if (notify) notifyAudioChange(channel, freq[channel]); - count = 0; - active = true; - } - } - } -}; - - -} diff --git a/src/AudioTools/Sandbox/HDLCStream.h b/src/AudioTools/Sandbox/HDLCStream.h deleted file mode 100644 index 12ce75efd4..0000000000 --- a/src/AudioTools/Sandbox/HDLCStream.h +++ /dev/null @@ -1,273 +0,0 @@ -#include "AudioToolsConfig.h" -#include "AudioTools/CoreAudio/Buffers.h" -#include -#include - -/// HDLC Asynchronous framing: The frame boundary octet is 01111110, (7E in -/// hexadecimal notation) -#define FRAME_BOUNDARY_OCTET 0x7E - -/// A "control escape octet", has the bit sequence '01111101', (7D hexadecimal) -#define CONTROL_ESCAPE_OCTET 0x7D - -/// If either of these two octets appears in the transmitted data, an escape -/// octet is sent, followed by the original data octet with bit 5 inverted -#define INVERT_OCTET 0x20 - -/// The frame check sequence (FCS) is a 16-bit CRC-CCITT -/// AVR Libc CRC function is _crc_ccitt_update() -/// Corresponding CRC function in Qt (www.qt.io) is qChecksum() -#define CRC16_CCITT_INIT_VAL 0xFFFF - -/// 16bit low and high bytes copier -#define low(x) ((x)&0xFF) -#define high(x) (((x) >> 8) & 0xFF) -#define lo8(x) ((x)&0xff) -#define hi8(x) ((x) >> 8) - -namespace audio_tools { - -/** - * @brief High-Level Data Link Control (HDLC) is a bit-oriented code-transparent - * synchronous data link layer protocol - * @ingroup communications - */ - -class HDLCStream : public Stream { -public: - /// Defines the output for the hdlc encoding - HDLCStream(Print &out, uint16_t max_frame_length) { - setOutput(out); - this->max_frame_length = max_frame_length; - begin(); - } - - /// Defines the input for the hdlc decoding - HDLCStream(Stream &io, uint16_t max_frame_length) { - setStream(io); - this->max_frame_length = max_frame_length; - begin(); - } - - bool begin() { - this->frame_position = 0; - this->frame_checksum = CRC16_CCITT_INIT_VAL; - this->escape_character = false; - if (frame_buffer.size() == 0) { - frame_buffer.resize(max_frame_length + 1); - } - return p_out != nullptr || p_in != nullptr; - } - - void end() { this->frame_buffer.resize(0); } - - int availableForWrite() override { - return p_out == nullptr ? 0 : DEFAULT_BUFFER_SIZE; - } - - /// Sends the encoded data to the defined output - size_t write(const uint8_t *data, size_t len) override { - LOGD("HDLCStream::write: %zu", len); - - for (int j = 0; j < len; j++) { - bool ok = frame_buffer.write(data[j]); - assert(ok); - if (frame_buffer.available() == max_frame_length) { - sendFrame(frame_buffer.data(), max_frame_length); - frame_buffer.reset(); - } - } - return len; - } - - int available() override { - return p_in == nullptr ? 0 : max_frame_length; - } - - /// Provides the decoded data - size_t readBytes(uint8_t *data, size_t len) override { - if (p_in == nullptr) { - LOGI("No data source"); - return 0; - } - - int result = 0; - // process bytes from input - while (result == 0) { - int ch = p_in->read(); - // ch is -1 when no data - if (ch >= 0){ - result = charReceiver(ch); - if (result > 0) { - result = frame_buffer.readArray(data, result); - break; - } - } else { - break; - } - } - LOGD("HDLCStream::readBytes: %zu -> %d", len, result); - return result; - ; - } - - void setStream(Stream &io) { - p_out = &io; - p_in = &io; - } - - void setStream(Print &out) { p_out = &out; } - - void setOutput(Print &out) { p_out = &out; } - - size_t write(uint8_t ch) override { return write(&ch, 1); }; - - int read() override { - uint8_t c[1]; - if (readBytes(c, 1) == 0) { - return -1; - } - return c[0]; - } - - /// not supported - int peek() override { return -1; } - -private: - Print *p_out = nullptr; - Stream *p_in = nullptr; - bool escape_character = false; - SingleBuffer frame_buffer{0}; - uint8_t frame_position = 0; - // 16bit CRC sum for _crc_ccitt_update - uint16_t frame_checksum; - uint16_t max_frame_length; - - /// Function to find valid HDLC frame from incoming data: returns the available result bytes in the buffer - int charReceiver(uint8_t data) { - int result = 0; - uint8_t *frame_buffer_data = frame_buffer.address(); - LOGD("charReceiver: %c", data); - - if (data == FRAME_BOUNDARY_OCTET) { - if (escape_character == true) { - escape_character = false; - } else if ((frame_position >= 2) && - (frame_checksum == - ((frame_buffer_data[frame_position - 1] << 8) | - (frame_buffer_data[frame_position - 2] & 0xff)))) { - // trigger processing of result data - frame_buffer.setAvailable(frame_position - 2); - result = frame_buffer.available(); - LOGD("==> Result: %d", result); - } - frame_position = 0; - frame_checksum = CRC16_CCITT_INIT_VAL; - return result; - } - - if (escape_character) { - escape_character = false; - data ^= INVERT_OCTET; - } else if (data == CONTROL_ESCAPE_OCTET) { - escape_character = true; - return result; - } - - frame_buffer_data[frame_position] = data; - - if (frame_position - 2 >= 0) { - frame_checksum = _crc_ccitt_update(frame_checksum, - frame_buffer_data[frame_position - 2]); - } - - frame_position++; - - if (frame_position == max_frame_length) { - frame_position = 0; - frame_checksum = CRC16_CCITT_INIT_VAL; - } - return result; - } - - /// Wrap given data in HDLC frame and send it out byte at a time - void sendFrame(const uint8_t *framebuffer, size_t frame_length) { - LOGD("HDLCStream::sendFrame: %zu", frame_length); - uint8_t data; - uint16_t fcs = CRC16_CCITT_INIT_VAL; - - p_out->write((uint8_t)FRAME_BOUNDARY_OCTET); - - while (frame_length) { - data = *framebuffer++; - fcs = HDLCStream::_crc_ccitt_update(fcs, data); - if ((data == CONTROL_ESCAPE_OCTET) || (data == FRAME_BOUNDARY_OCTET)) { - p_out->write((uint8_t)CONTROL_ESCAPE_OCTET); - data ^= INVERT_OCTET; - } - p_out->write((uint8_t)data); - frame_length--; - } - data = low(fcs); - if ((data == CONTROL_ESCAPE_OCTET) || (data == FRAME_BOUNDARY_OCTET)) { - p_out->write((uint8_t)CONTROL_ESCAPE_OCTET); - data ^= (uint8_t)INVERT_OCTET; - } - p_out->write((uint8_t)data); - data = high(fcs); - if ((data == CONTROL_ESCAPE_OCTET) || (data == FRAME_BOUNDARY_OCTET)) { - p_out->write(CONTROL_ESCAPE_OCTET); - data ^= INVERT_OCTET; - } - p_out->write(data); - p_out->write(FRAME_BOUNDARY_OCTET); - p_out->flush(); - } - - static uint16_t crc16_update(uint16_t crc, uint8_t a) { - int i; - crc ^= a; - for (i = 0; i < 8; ++i) { - if (crc & 1) - crc = (crc >> 1) ^ 0xA001; - else - crc = (crc >> 1); - } - return crc; - } - - static uint16_t crc_xmodem_update(uint16_t crc, uint8_t data) { - int i; - - crc = crc ^ ((uint16_t)data << 8); - for (i = 0; i < 8; i++) { - if (crc & 0x8000) - crc = (crc << 1) ^ 0x1021; - else - crc <<= 1; - } - - return crc; - } - - static uint16_t _crc_ccitt_update(uint16_t crc, uint8_t data) { - data ^= lo8(crc); - data ^= data << 4; - return ((((uint16_t)data << 8) | hi8(crc)) ^ (uint8_t)(data >> 4) ^ - ((uint16_t)data << 3)); - } - - static uint8_t _crc_ibutton_update(uint8_t crc, uint8_t data) { - uint8_t i; - crc = crc ^ data; - for (i = 0; i < 8; i++) { - if (crc & 0x01) - crc = (crc >> 1) ^ 0x8C; - else - crc >>= 1; - } - return crc; - } -}; - -} // namespace audio_tools \ No newline at end of file diff --git a/src/AudioTools/Sandbox/LoRaStream.h b/src/AudioTools/Sandbox/LoRaStream.h deleted file mode 100644 index b46a4fb959..0000000000 --- a/src/AudioTools/Sandbox/LoRaStream.h +++ /dev/null @@ -1,164 +0,0 @@ -#pragma once - -#include "AudioTools/CoreAudio/Buffers.h" -#include "LoRa.h" // https://github.com/sandeepmistry/arduino-LoRa - -// define the default pins used by the transceiver module -#define rst 12 -#define dio0 14 - -namespace audio_tools { - -/** - * @brief LoRa Audio Configuration with default values maximised for - * speed using the LoRa library from sandeepmistry: - * https://github.com/sandeepmistry/arduino-LoRa - * @author Phil Schatzmann - * @copyright GPLv3 - - Heltec LoRa 32 Lora Pins: - SS: 8 - SCK: 9 - MOSI: 10 - MISO: 11 - RST: 12 - BUSY: 13 - DIO1: 14 - - */ - -struct LoRaConfig : public AudioInfo { - LoRaConfig() { - sample_rate = 0; - channels = 0; - bits_per_sample = 0; - } - int sync_word = 0xF3; // 0x0-0xFF - int32_t spi_speed = 8000000; - int max_size = 200; // max 256 bytes - int frequency = 868E6; // (Asio:433E6, EU:868E6, US: 915E6) - int tx_power = 20; // 2-20; - int spreading_factor = 6; // 6-12 (6 fastest) - // 7.8E3, 10.4E3, 15.6E3, 20.8E3, 31.25E3, 41.7E3, 62.5E3, 125E3, 250E3, and - // 500E3 (fastest) - int signal_bandwidth = 500E3; - int pin_ss = SS; - int pin_rst = rst; - int pin_dio0 = dio0; - int max_begin_retry = 10; -}; - -/** - * @brief LoRa: Sending and Receiving Audio - * @ingroup communications - * @author Phil Schatzmann - * @copyright GPLv3 - */ -class LoRaStream : public AudioStream { - public: - LoRaConfig defaultConfig() { - LoRaConfig rc; - return rc; - } - - void setAudioInfo(AudioInfo info) { - cfg.sample_rate = info.sample_rate; - cfg.channels = info.channels; - cfg.bits_per_sample = info.bits_per_sample; - AudioStream::setAudioInfo(info); - } - - bool begin(LoRaConfig config) { - cfg = config; - AudioStream::setAudioInfo(config); - return begin(); - } - - bool begin() { - TRACEI(); - is_audio_info_sent = false; - buffer.resize(cfg.max_size); - bool rc; - int count = 0; - LOGI("LoRa begin..."); - LoRa.setPins(cfg.pin_ss, cfg.pin_rst, cfg.pin_dio0); - do { - rc = LoRa.begin(cfg.frequency); - if (++count > cfg.max_begin_retry) return false; - if (!rc){ - LOGI("LoRa begin failed"); - delay(800); - } - } while(!rc); - LoRa.setSignalBandwidth(cfg.signal_bandwidth); - LoRa.setSpreadingFactor(cfg.spreading_factor); - LoRa.setTxPower(cfg.tx_power); - LoRa.setSPIFrequency(cfg.spi_speed); - LoRa.setSyncWord(cfg.sync_word); - if (rc) LOGI("LoRa begin success"); - return rc; - } - - void end() { - TRACEI(); - LoRa.end(); - } - - size_t readBytes(uint8_t* data, size_t len) { - LOGI("LoRaStream::readBytes: %d", len); - size_t packetSize = LoRa.parsePacket(); - if (!cfg && packetSize == sizeof(AudioInfo)) { - readAudioInfo(); - packetSize = LoRa.parsePacket(); - } - int toRead = min(len, packetSize); - int read = LoRa.readBytes(data, toRead); - return read; - } - - int available() { return cfg.max_size; } - - int availableForWrite() { return cfg.max_size; } - - size_t write(const uint8_t* data, size_t len) { - LOGI("LoRaStream::write: %d", len); - - if (cfg && !is_audio_info_sent) { - writeAudioInfo(); - is_audio_info_sent = true; - } - - for (int j = 0; j < len; j++) { - buffer.write(data[j]); - if (buffer.isFull()) { - LoRa.beginPacket(); - LoRa.write(buffer.data(), buffer.available()); - LoRa.endPacket(); - buffer.clear(); - } - } - return len; - } - - protected: - LoRaConfig cfg; - SingleBuffer buffer; - bool is_audio_info_sent = false; - - void readAudioInfo() { - TRACED(); - AudioInfo tmp; - int read = LoRa.readBytes((uint8_t*)&tmp, sizeof(AudioInfo)); - if (tmp) setAudioInfo(tmp); - } - - void writeAudioInfo() { - TRACED(); - LoRa.beginPacket(); - AudioInfo ai = audioInfo(); - LoRa.write((uint8_t*)&ai, sizeof(ai)); - LoRa.endPacket(); - } -}; - -} // namespace audio_tools \ No newline at end of file diff --git a/src/AudioTools/Sandbox/PDMStream.h b/src/AudioTools/Sandbox/PDMStream.h deleted file mode 100644 index e0db2a7f00..0000000000 --- a/src/AudioTools/Sandbox/PDMStream.h +++ /dev/null @@ -1,265 +0,0 @@ -#include "AudioToolsConfig.h" -#include "Stream.h" - -#define DELAY(j) {for(volatile int i=0;i -class DecimationStreamExt : public AudioStream { - public: - DecimationStreamExt() = default; - DecimationStreamExt(Stream &in) { setStream(in); } - - void setStream(Stream &in) { p_in = ∈ } - - virtual bool begin(AudioInfo cfg) { - setAudioInfo(cfg); - return true; - } - - // defines the decimation factor: as multiple of bits_per sample - void setDecimationFactor(int factor) { dec_factor = factor; } - - size_t readBytes(uint8_t *data, size_t len) { - LOGD("readBytes:%d", len); - assert(data != nullptr); - // make sure that we read full samples - int samples = len / sizeof(T); - int result_bytes = samples * sizeof(T); - - // calculated scaling factor and offset for unsigned to signed conversion - int max_value = sizeof(T) * 8 * dec_factor; // e.g. 16 * 32 = 512 - int scaled_max = pow(2, sizeof(T) * 8); // e.g. 65536 - int factor = scaled_max / max_value; // e.g. 128 - int unsigned_to_signed = scaled_max / 2; // e.g. 32768 - - // provide subsequent samples - T *data_typed = (T *)data; - T tmp_in; - for (int idx = 0; idx < samples; idx++) { - // decimate subsequent samples - unsigned decimated = 0; - for (int dec = 0; dec < dec_factor; dec++) { - int sample_bytes = sizeof(T); - LOGD("readBytes: %d", sample_bytes); - if (p_in->readBytes((uint8_t *)&tmp_in, sample_bytes) != sample_bytes) { - LOGE("readBytes failed"); - } - decimated += countSetBits(tmp_in); - } - // store scaled decimated as singned value - //data[idx] = (decimated * factor) - unsigned_to_signed; - data_typed[idx] = decimated; - } - - return result_bytes; - } - - int available() { - TRACED(); - return 1024; - } - - AudioInfo audioInfoPDM() { - AudioInfo result = audioInfo(); - // sample_rate_pcm = sample_rate_pdm / decimation - result.sample_rate = info.sample_rate * dec_factor; - return result; - } - - protected: - Stream *p_in = nullptr; - int dec_factor = 32; // usually set between 25 and 64× - - unsigned countSetBits(unsigned n) { - TRACED(); - T count = 0; - while (n) { - count += n & 1; - n >>= 1; - } - return count; - } -}; - - -/* -We read the raw PDM data with the help of the Arduino digitalRead(). The SEL pin -needs to be connected to GND, so that the data is valid at clock pin low. - -COMMON CLOCK AND DECIMATION RATIO SETTINGS -PDM Clock Frequency(fPDM)Decimation Ratio Baseband Sampling Rate (fs) Audio Signal Bandwidth (fs/2)Applications -4.8 MHz 25× 192 kHz 96 kHz Ultrasound -4.8 MHz 50× 96 kHz 48 kHz -3.072 MHz 64× 48 kHz 24 kHz Full-bandwidth audio -2.4 MHz 50× 48 kHz 24 kHz -1.536 MHz 48× 32 kHz 16 kHz Wide-bandwidth audio -1.024 MHz 64× 16 kHz 8 kHz High-quality voice -768 kHz 48× 16 kHz 8 kHz -512 kHz 32× 16 kHz 8 kHz -512 kHz 64× 8 kHz 4 kHz Standard voice -*/ - -template -class BitBangDecimationStream : public DecimationStreamExt { - -public: - bool begin(AudioInfo cfg) { - DecimationStreamExt::setAudioInfo(cfg); - pinMode(pin_clock, OUTPUT); - pinMode(pin_data, INPUT); - return true; - } - - size_t readBytes(uint8_t *data, size_t len) { - LOGD("readBytes:%d", len); - assert(data != nullptr); - // make sure that we read full samples - int samples = len / sizeof(T); - int result_bytes = samples * sizeof(T); - - // calculated scaling factor and offset for unsigned to signed conversion - int max_value = sizeof(T) * 8 * DecimationStreamExt::dec_factor; // e.g. 16 * 32 = 512 - int scaled_max = pow(2, sizeof(T) * 8); // e.g. 65536 - int factor = scaled_max / max_value; // e.g. 128 - int unsigned_to_signed = scaled_max / 2; // e.g. 32768 - - // provide subsequent samples - T *data_typed = (T *)data; - T tmp_in; - for (int idx = 0; idx < samples; idx++) { - // decimate subsequent samples - unsigned decimated = 0; - for (int dec = 0; dec < DecimationStreamExt::dec_factor * sizeof(T)*8; dec++) { - int sample_bytes = sizeof(T); - digitalWrite(pin_clock, HIGH); - DELAY(1); - digitalWrite(pin_clock, LOW); - bool bit = digitalRead(pin_data); - DELAY(1); - // sum up active bits - decimated += bit; - } - // store scaled decimated as singned value - //data[idx] = (decimated * factor) - unsigned_to_signed; - data_typed[idx] = decimated; - } - - return result_bytes; - } - -protected: - int pin_clock = 14; - int pin_data = 32; -}; - -/** - * @brief Applies low pass filter to a decimated pdm signal to convert it - * to pcm - * @author Phil Schatzmann - * @ingroup io - * @copyright GPLv3 - */ - -template -class PDMMonoStreamT : public AudioStream { - public: - PDMMonoStreamT(Stream &in) { - decimation_stream.setStream(in); - info.sample_rate = 44100; - info.channels = 1; - info.bits_per_sample = bitsPerSample(); - } - - // provides the audio info of the PDM stream (with the much higher sample - // rate) - AudioInfo audioInfoPDM() { - AudioInfo result = audioInfo(); - // sample_rate_pcm = sample_rate_pdm / decimation - result.sample_rate = info.sample_rate * decimation(); - return result; - } - - // provides the decimation factor that was used in the processing - int decimation() { return decimation_factor; } - - // defines the decimation factor: must be multiple of bits_per sample - void setDecimationFactor(int factor) { - decimation_factor = factor; - } - - bool begin(AudioInfo info) { - setAudioInfo(info); - if (info.channels != 1) { - LOGE("channels must be 1"); - } - // set the factor for the decimator - decimation_stream.setDecimationFactor(decimation_factor); - return begin(); - } - - bool begin() { - decimation_stream.begin(info); - bool rc = in_filtered.begin(info); - in_filtered.setFilter(0, fir); - return rc; - } - - size_t readBytes(uint8_t *data, size_t len) { - LOGD("readBytes:%d", len); - assert(data != nullptr); - // make sure that we read full samples - int samples = len / sizeof(T); - int result_bytes = samples * sizeof(T); - assert(result_bytes <= len); - - if (in_filtered.readBytes(data, result_bytes) != result_bytes) { - LOGE("readBytes failed"); - } - - return result_bytes; - } - - int available() { - TRACED(); - return in_filtered.available(); - } - - template - void setFilterValues(const T (&b)[B]) { - fir.setValues(b); - } - - protected: - int decimation_factor = sizeof(T) * 2; - // 44100 hz FIR: cut off: 18040, transition bandwidth: 7380 - float coef[19] = { - -0.000704420658475743, -0.000537879918926308, 0.004114637509913062, - -0.012685775806621488, 0.027889173789107543, -0.049285026985058301, - 0.074005079283040689, -0.097330704866957815, 0.114052040962871595, - 0.880965753382213723, 0.114052040962871595, -0.097330704866957843, - 0.074005079283040717, -0.049285026985058301, 0.027889173789107550, - -0.012685775806621504, 0.004114637509913064, -0.000537879918926308, - -0.000704420658475743}; - DecimationStreamExt decimation_stream; - FilteredStream in_filtered{decimation_stream, 1}; - FIR fir{coef}; - - constexpr int8_t bitsPerSample() { return sizeof(T) * 8; } -}; - -// Define PDMStream -using PDMMonoStream = PDMMonoStreamT; - -} // namespace audio_tools \ No newline at end of file diff --git a/src/AudioTools/Sandbox/ResampleAlgorithms.h b/src/AudioTools/Sandbox/ResampleAlgorithms.h deleted file mode 100644 index c9d1a1bc30..0000000000 --- a/src/AudioTools/Sandbox/ResampleAlgorithms.h +++ /dev/null @@ -1,133 +0,0 @@ -#pragma once -namespace audio_tools { - -/// range 0:1 -struct ReasampleLinearInterpolation { - constexpr int getFrom() { return -getFrameCountSave(); }; - constexpr int getTo(int frames) { return frames - getFrameCountSave(); } - constexpr int getFrameCountSave() { return 1; } - float value(float* y, float xf) { - int x = xf; - int dx = xf - x; - return y[x] + dx * (y[x + 1] - y[x]); - } -}; - -/// range -1:2 -struct ResampleBSpline { - constexpr int getFrom() { return -1; }; - constexpr int getTo(int frames) { return frames - getFrameCountSave(); } - constexpr int getFrameCountSave() { return 2; } - float value(float* y, float xf) { - int x = xf; - int dx = xf - x; - // 4-point, 3rd-order B-spline (x-form) - float ym1py1 = y[x - 1] + y[x + 1]; - float c0 = 1.0f / 6.0f * ym1py1 + 2.0f / 3.0f * y[x]; - float c1 = 1.0f / 2.0f * (y[x + 1] - y[x - 1]); - float c2 = 1.0f / 2.0f * ym1py1 - y[x]; - float c3 = - 1.0f / 2.0f * (y[x] - y[x + 1]) + 1.0f / 6.0f * (y[x + 2] - y[x - 1]); - return ((c3 * dx + c2) * dx + c1) * dx + c0; - } -}; - -/// range -1 : 2 -struct ResampleLagrange { - constexpr int getFrom() { return -1; }; - constexpr int getTo(int frames) { return frames - getFrameCountSave(); } - constexpr int getFrameCountSave() { return 2; } - float value(float* y, float xf) { - int x = xf; - int dx = xf - x; - // 4-point, 3rd-order Lagrange (x-form) - float c0 = y[x]; - float c1 = y[x + 1] - 1.0f / 3.0f * y[x - 1] - 1.0f / 2.0f * y[x] - - 1.0f / 6.0f * y[x + 2]; - float c2 = 1.0f / 2.0f * (y[x - 1] + y[x + 1]) - y[x]; - float c3 = - 1.0f / 6.0f * (y[x + 2] - y[x - 1]) + 1 / 2.0 * (y[x] - y[x + 1]); - return ((c3 * dx + c2) * dx + c1) * dx + c0; - } -}; - -// Range -1:2 -struct ResmpleHermite { - constexpr int getFrom() { return -1; }; - constexpr int getTo(int frames) { return frames - getFrameCountSave(); } - constexpr int getFrameCountSave() { return 2; } - float value(float* y, float xf) { - int x = xf; - int dx = xf - x; - // 4-point, 3rd-order Hermite (x-form) - float c0 = y[x]; - float c1 = 1.0f / 2.0f * (y[x + 1] - y[x - 1]); - float c2 = y[x - 1] - 5.0f / 2.0f * y[x] + 2.0f * y[x + 1] - - 1.0f / 2.0f * y[x + 2]; - float c3 = - 1.0f / 2.0f * (y[x + 2] - y[x - 1]) + 3.0f / 2.0f * (y[x] - y[x + 1]); - return ((c3 * dx + c2) * dx + c1) * dx + c0; - } -}; - -// range -1:2 -struct ResampleParabolic { - constexpr int getFrom() { return -1; }; - constexpr int getTo(int frames) { return frames - getFrameCountSave(); } - constexpr int getFrameCountSave() { return 2; } - - float value(float* y, float xf) { - int x = xf; - int dx = xf - x; - // 4-point, 2nd-order parabolic 2x (x-form) - float y1mym1 = y[x + 1] - y[x - 1]; - float c0 = 1.0f / 2.0f * y[x] + 1 / 4.0 * (y[x - 1] + y[x + 1]); - float c1 = 1.0 / 2.0f * y1mym1; - float c2 = 1.0f / 4.0f * (y[x + 2] - y[x] - y1mym1); - return (c2 * dx + c1) * dx + c0; - } -}; - -// range 0:1 -struct Resample2Point3Order { - constexpr int getFrom() { return -getFrameCountSave(); }; - constexpr int getTo(int frames) { return frames - getFrameCountSave(); } - constexpr int getFrameCountSave() { return 1; } - - float value(float* y, float xf) { - int x = xf; - int dx = xf - x; - // Optimal 2x (2-point, 3rd-order) (z-form) - float z = dx - 0.5f; - float even1 = y[x + 1] + y[x]; - float odd1 = y[x + 1] - y[x]; - float c0 = even1 * 0.50037842517188658f; - float c1 = odd1 * 1.00621089801788210f; - float c2 = even1 * -0.004541102062639801f; - float c3 = odd1 * -1.57015627178718420f; - return ((c3 * z + c2) * z + c1) * z + c0; - } -}; - -// Range -1 : 2 -struct Resample4Point2Order { - constexpr int getFrom() { return -1; }; - constexpr int getTo(int frames) { return frames - getFrameCountSave(); } - constexpr int getFrameCountSave() { return 2; } - - float value(float* y, float xf) { - int x = xf; - int dx = xf - x; - - // Optimal 2x (4-point, 2nd-order) (z-form) - float z = dx - 0.5f; - float even1 = y[x + 1] + y[x], odd1 = y[x + 1] - y[x]; - float even2 = y[x + 2] + y[x - 1], odd2 = y[x + 2] - y[x - 1]; - float c0 = even1 * 0.42334633257225274 + even2 * 0.07668732202139628; - float c1 = odd1 * 0.26126047291143606 + odd2 * 0.24778879018226652; - float c2 = even1 * -0.213439787561776841 + even2 * 0.21303593243799016; - return (c2 * z + c1) * z + c0; - } -}; - -} // namespace audio_tools \ No newline at end of file diff --git a/src/AudioTools/Sandbox/USB/USBDeviceAudio.h b/src/AudioTools/Sandbox/USB/USBDeviceAudio.h deleted file mode 100644 index 1fbc66eb28..0000000000 --- a/src/AudioTools/Sandbox/USB/USBDeviceAudio.h +++ /dev/null @@ -1,1439 +0,0 @@ -#pragma once - -#include - -#include "Arduino.h" -#include "USBDeviceAudioAPI.h" -#include "common/tusb_types.h" - -//-------------------------------------------------------------------- -// Debugging Logging and Testing -//-------------------------------------------------------------------- -#define AUDIO_LOG(...) \ - { \ - char msg[160]; \ - snprintf(msg, 160, __VA_ARGS__); \ - LOG_AUDIO_OUTPUT.println(msg); \ - LOG_AUDIO_OUTPUT.flush(); \ - } -#define AUDIO_NO_LOG(...) - -#define LOG_AUDIO_OUTPUT Serial -#define AUDIO_DEBUG false -#define LOG_AUDIO_ERROR AUDIO_LOG -#define LOG_AUDIO_DEBUG AUDIO_LOG - -#ifdef ESP32 -#define ISO_FB_EP 3 -#else -#define ISO_FB_EP 3 -#endif - -//-------------------------------------------------------------------- -// Unit numbers are arbitrary selected -//-------------------------------------------------------------------- - -#define UAC2_ENTITY_CLOCK 0x10 - -// Speaker path -#define UAC2_ENTITY_SPK_INPUT_TERMINAL 0x15 -#define UAC2_ENTITY_SPK_FEATURE_UNIT 0x16 -#define UAC2_ENTITY_SPK_OUTPUT_TERMINAL 0x17 - -// Microphone path -#define UAC2_ENTITY_MIC_INPUT_TERMINAL 0x11 -#define UAC2_ENTITY_MIC_FEATURE_UNIT 0x12 -#define UAC2_ENTITY_MIC_OUTPUT_TERMINAL 0x13 - -// output using 5 debug pins -#if AUDIO_DEBUG -#define debugWrite(pin, active) digitalWrite(pin, active); -#else -#define debugWrite(pin, active) -#endif - -/// Status management e.g. for LED delays -enum class AudioProcessingStatus { - INACTIVE = 0, - ERROR = 500, - PLAYING = 1000, - ACTIVE = 2000 -}; - -/// A resizable buffer of bytes which manages the available bytes -class ByteBuffer { - public: - // resizes the buffer - void resize(int size) { vector.resize(size); } - // provides access to the first byte of the data - uint8_t *data() { return vector.data(); } - // remove first n bytes - void consume(int n) { - pos -= n; - if (pos > 0) { - memmove(data(), data() + n, pos); - } - } - // provides the available byte count - int available() { return pos; } - // provides the size of the buffer - int size() { return vector.size(); } - // sets the available byte count to 0 - void reset() { int pos = 0; } - // clears the memory by setting the content to 0 - void clear() { memset(data(), 0, size()); } - // inform about number of available bytes - void setAvailable(int av) { pos = av; } - - protected: - std::vector vector; - int pos = 0; -}; - -/*** - * USB Audio Device: - * - provide data access via callbacks - * - configure audio info via begin method - * - provide all potential methods so that we can easily overwrite them - * - implement Speaker (device is audio sink) - * - implement Microphone (device is audio source) - * - dont change the audio based on mute or volume changes: this is the - * respondibility of the implementor - */ - -class USBDeviceAudio : public USBAudioCB { - public: - //USBDeviceAudio() { setupDebugPins(); } - USBDeviceAudio() = default; - - /// callback for audio sink (speaker): we write out the received data e.g. to - /// a dac - void setWriteCallback(size_t (*write_cb)(const uint8_t *data, size_t len, - USBDeviceAudio &ref)) { - cfg.p_write_callback = write_cb; - } - - /// callback for audio source (microphone): we provide the audio data e.g. - /// from the adc - void setReadCallback(size_t (*read_cb)(uint8_t *data, size_t len, - USBDeviceAudio &ref)) { - cfg.p_read_callback = read_cb; - } - - /// Alternative to setWriteCallback - void setOutput(Print &out) { - p_print = &out; - setWriteCallback(defaultWriteCB); - } - - /// Alternaive to setReadCallback - void setInput(Stream &in) { - p_stream = ∈ - setReadCallback(defaultReadCB); - } - - USBAudioConfig defaultConfig() { - USBAudioConfig result; - return result; - } - - /*------------- MAIN -------------*/ - virtual bool begin(USBAudioConfig config) { - _itf_number_total = 0; - cfg = config; - - setupDebugPins(); - - _api.begin(this, cfg); - _mute.resize(cfg.channels + 1); - _volume.resize(cfg.channels + 1); - - // check data - if (!isMicrophone() && !isSpeaker()) { - LOG_AUDIO_ERROR("No callback has been defined"); - setStatus(AudioProcessingStatus::ERROR); - return false; - } - - // calculate descriptor length; - if (interfaceDescriptor(nullptr, 1024) == 0) { - setStatus(AudioProcessingStatus::ERROR); - LOG_AUDIO_ERROR("Interface Descriptor length was 0"); - return false; - } - - _clk_is_valid = 1; - setStatus(AudioProcessingStatus::ACTIVE); - - return true; - } - - void end() { - tud_deinit(cfg.rh_port); - setStatus(AudioProcessingStatus::INACTIVE); - if (_out_buffer.size() > 0) _out_buffer.resize(0); - if (_in_buffer.size() > 0) _out_buffer.resize(0); - } - - // If is mounted - bool active(void) { - return status() == AudioProcessingStatus::ACTIVE || - status() == AudioProcessingStatus::PLAYING; - } - - operator bool() { return active(); } - - // get sample rate - uint32_t rate() { return cfg.sample_rate; } - - // get number of channels - int channels() { return cfg.channels; } - - // provides the volume for the indicated channel (from 1 to 100) - uint16_t volume(int channel) { return _volume[channel]; } - - // checks if the channel is muted - bool isMute(int channel) { return _mute[channel]; } - - /// Call from loop to blink led - bool updateLED(int pin) { - if (_is_led_setup) { - pinMode(pin, OUTPUT); - _is_led_setup = false; - } - - // led must be active - if (_processing_status != AudioProcessingStatus::INACTIVE && - millis() > _led_timeout) { - _led_timeout = millis() + (uint16_t)_processing_status; - _led_active = !_led_active; - digitalWrite(pin, _led_active); - return true; - } - - // led is inactive - if (_processing_status == AudioProcessingStatus::INACTIVE) { - if (_led_active) { - _led_active = false; - digitalWrite(pin, _led_active); - } - } - return false; - } - - /// Provide the actual status - AudioProcessingStatus status() { return _processing_status; } - - bool isMicrophone() { - return (cfg.p_read_callback != nullptr && cfg.p_read_callback != defaultReadCB) || - (cfg.p_read_callback == defaultReadCB && p_stream != nullptr); - } - - bool isSpeaker() { - return (cfg.p_write_callback != nullptr && - cfg.p_write_callback != defaultWriteCB) || - (cfg.p_write_callback == defaultWriteCB && p_print != nullptr); - } - - bool isHeadset() { return isSpeaker() && isMicrophone(); } - - // USBD_Device device() { return TinyUSBDevice; } - - //--------------------------------------------------------------------+ - // Application Callback API Implementations - //--------------------------------------------------------------------+ - // from USBD_Interface - - virtual uint16_t getMaxEPSize() { - return TUD_AUDIO_EP_SIZE(cfg.sample_rate, cfg.bits_per_sample / 8, - cfg.channels); - } - - virtual uint16_t getIOSize() { - return TUD_AUDIO_EP_SIZE(cfg.sample_rate, cfg.bits_per_sample / 8, - cfg.channels); - } - - virtual uint8_t getFeatureUnitLength() { return (6 + (channels() + 1) * 4); } - - // Invoked when set interface is called, typically on start/stop streaming or - // format change - bool set_itf_cb(uint8_t rhport, - tusb_control_request_t const *p_request) override { - uint8_t const itf = tu_u16_low(tu_le16toh(p_request->wIndex)); - uint8_t const alt = tu_u16_low(tu_le16toh(p_request->wValue)); - - if (alt != 0) setStatus(AudioProcessingStatus::PLAYING); - - return true; - } - - // Invoked when audio class specific set request received for an EP - bool set_req_ep_cb(uint8_t rhport, tusb_control_request_t const *p_request, - uint8_t *pBuff) override { - (void)rhport; - (void)pBuff; - - // We do not support any set range requests here, only current value - // requests - TU_VERIFY(p_request->bRequest == AUDIO_CS_REQ_CUR); - - // Page 91 in UAC2 specification - uint8_t channelNum = TU_U16_LOW(p_request->wValue); - uint8_t ctrlSel = TU_U16_HIGH(p_request->wValue); - uint8_t ep = TU_U16_LOW(p_request->wIndex); - - (void)channelNum; - (void)ctrlSel; - (void)ep; - - return false; // Yet not implemented - } - - // Invoked when audio class specific set request received for an interface - bool set_req_itf_cb(uint8_t rhport, tusb_control_request_t const *p_request, - uint8_t *pBuff) override { - (void)rhport; - (void)pBuff; - - // We do not support any set range requests here, only current value - // requests - TU_VERIFY(p_request->bRequest == AUDIO_CS_REQ_CUR); - - // Page 91 in UAC2 specification - uint8_t channelNum = TU_U16_LOW(p_request->wValue); - uint8_t ctrlSel = TU_U16_HIGH(p_request->wValue); - uint8_t itf = TU_U16_LOW(p_request->wIndex); - - (void)channelNum; - (void)ctrlSel; - (void)itf; - - return false; // Yet not implemented - } - - // Invoked when audio class specific set request received for an entity - bool set_req_entity_cb(uint8_t rhport, - tusb_control_request_t const *p_request, - uint8_t *buf) override { - // Page 91 in UAC2 specification - uint8_t channelNum = TU_U16_LOW(p_request->wValue); - uint8_t ctrlSel = TU_U16_HIGH(p_request->wValue); - uint8_t itf = TU_U16_LOW(p_request->wIndex); - uint8_t entityID = TU_U16_HIGH(p_request->wIndex); - audio_control_request_t const *request = - (audio_control_request_t const *)p_request; - - debugWrite(5, HIGH); - - // for speaker - if (request->bEntityID == UAC2_ENTITY_SPK_FEATURE_UNIT) { - bool rc = feature_unit_set_request(rhport, p_request, buf); - debugWrite(5, LOW); - return rc; - } - if (request->bEntityID == UAC2_ENTITY_CLOCK) { - bool rc = clock_set_request(rhport, p_request, buf); - debugWrite(5, LOW); - return rc; - } - - debugWrite(5, HIGH); - LOG_AUDIO_DEBUG( - "Set request not handled, entity = %d, selector = %d, request = %d", - request->bEntityID, request->bControlSelector, request->bRequest); - return false; // Yet not implemented - } - - // Invoked when audio class specific get request received for an EP - bool get_req_ep_cb(uint8_t rhport, - tusb_control_request_t const *p_request) override { - (void)rhport; - - // Page 91 in UAC2 specification - uint8_t channelNum = TU_U16_LOW(p_request->wValue); - uint8_t ctrlSel = TU_U16_HIGH(p_request->wValue); - uint8_t ep = TU_U16_LOW(p_request->wIndex); - - (void)channelNum; - (void)ctrlSel; - (void)ep; - - // return tud_control_xfer(rhport, p_request, &tmp, 1); - - return false; // Yet not implemented - } - - // Invoked when audio class specific get request received for an interface - bool get_req_itf_cb(uint8_t rhport, - tusb_control_request_t const *p_request) override { - (void)rhport; - - // Page 91 in UAC2 specification - uint8_t channelNum = TU_U16_LOW(p_request->wValue); - uint8_t ctrlSel = TU_U16_HIGH(p_request->wValue); - uint8_t itf = TU_U16_LOW(p_request->wIndex); - - (void)channelNum; - (void)ctrlSel; - (void)itf; - - return false; // Yet not implemented - } - - // Invoked when audio class specific get request received for an entity - bool get_req_entity_cb(uint8_t rhport, - tusb_control_request_t const *p_request) override { - cfg.rh_port = rhport; - // Page 91 in UAC2 specification - uint8_t channelNum = TU_U16_LOW(p_request->wValue); - uint8_t ctrlSel = TU_U16_HIGH(p_request->wValue); - // uint8_t itf = TU_U16_LOW(p_request->wIndex); // Since - // we have only one audio function implemented, we do not need the itf value - uint8_t entityID = TU_U16_HIGH(p_request->wIndex); - audio_control_request_t const *request = - (audio_control_request_t const *)p_request; - - debugWrite(6, HIGH); - - // Clock Source - if (request->bEntityID == UAC2_ENTITY_CLOCK) { - bool rc = clock_get_request(rhport, p_request); - if (rc) debugWrite(6, LOW); - return rc; - } - - // Fueature unit speaker - if (request->bEntityID == UAC2_ENTITY_SPK_FEATURE_UNIT) { - bool rc = feature_unit_get_request(rhport, p_request); - if (rc) debugWrite(6, LOW); - return rc; - } - - // Feature unit mic - if (entityID == UAC2_ENTITY_MIC_FEATURE_UNIT) { - bool rc = feature_unit_get_request(rhport, p_request); - if (rc) debugWrite(6, LOW); - return rc; - } - - // Input terminal (Microphone input) - if (entityID == UAC2_ENTITY_MIC_INPUT_TERMINAL || - entityID == UAC2_ENTITY_SPK_INPUT_TERMINAL) { - switch (ctrlSel) { - case AUDIO_TE_CTRL_CONNECTOR: { - // The terminal connector control only has a get request with only the - // CUR attribute. - audio_desc_channel_cluster_t ret; - ret.bNrChannels = cfg.channels; - ret.bmChannelConfig = (audio_channel_config_t)0; - ret.iChannelNames = 0; - - LOG_AUDIO_DEBUG(" Get terminal connector"); - - bool rc = _api.tud_audio_buffer_and_schedule_control_xfer( - rhport, p_request, (void *)&ret, sizeof(ret)); - if (rc) debugWrite(6, LOW); - return rc; - } break; - - // Unknown/Unsupported control selector - default: - LOG_AUDIO_DEBUG(" Unsupported selector: %d", entityID); - debugWrite(6, HIGH); - return false; - } - } - - LOG_AUDIO_DEBUG(" Unsupported entity: %d", entityID); - debugWrite(6, HIGH); - return false; // Yet not implemented - } - - bool tx_done_pre_load_cb(uint8_t rhport, uint8_t itf, uint8_t ep_in, - uint8_t cur_alt_setting) override { - // (void)rhport; - // (void)itf; - // (void)ep_in; - // (void)cur_alt_setting; - - // fill buffer from "microphone" input - if (isMicrophone()) { - debugWrite(1, HIGH); - // manage buffer size - uint16_t len = getIOSize() - 2; // CFG_TUD_AUDIO_EP_SZ_IN - 2; - if (_out_buffer.size() < len) _out_buffer.resize(len); - - // return if the buffer is already filled - if (_out_buffer.available() != 0) { - return true; - } - - // fill the buffer with data - uint8_t *adr = _out_buffer.data(); - _out_buffer.clear(); - int len_eff = (*cfg.p_read_callback)(adr, len, *this); - _out_buffer.setAvailable(len_eff); - debugWrite(1, LOW); - } - - return true; - } - - bool tx_done_post_load_cb(uint8_t rhport, uint16_t n_bytes_copied, - uint8_t itf, uint8_t ep_in, - uint8_t cur_alt_setting) override { - (void)rhport; - (void)itf; - (void)ep_in; - (void)cur_alt_setting; - - // output audio from "microphone" buffer to usb - if (isMicrophone()) { - debugWrite(2, HIGH); - uint8_t *adr = _out_buffer.data(); - _api.tud_audio_n_write(func_id, adr, _out_buffer.available()); - _out_buffer.reset(); - debugWrite(2, LOW); - } - - return true; - } - - bool rx_done_pre_read_cb(uint8_t rhport, uint16_t n_bytes_received, - uint8_t func_id, uint8_t ep_out, - uint8_t cur_alt_setting) override { - // read audio from usb - if (isSpeaker() && _in_buffer.available() == 0) { - debugWrite(3, HIGH); - uint16_t len = _api.tud_audio_n_available(func_id); - if (len > 0) { - if (_in_buffer.size() < len) _in_buffer.resize(len); - uint8_t *adr = _in_buffer.data(); - int len_eff = _api.tud_audio_n_read(func_id, adr, len); - _in_buffer.setAvailable(len_eff); - } - debugWrite(3, LOW); - return true; - } - return true; - } - - bool rx_done_post_read_cb(uint8_t rhport, uint16_t n_bytes_received, - uint8_t func_id, uint8_t ep_out, - uint8_t cur_alt_setting) override { - // read audio from usb - if (isSpeaker() && _in_buffer.available() > 0) { - debugWrite(4, HIGH); - uint8_t *adr = _in_buffer.data(); - size_t rc = cfg.p_write_callback(adr, _in_buffer.available(), *this); - _in_buffer.consume(rc); - debugWrite(4, LOW); - } - return true; - } - - bool set_itf_close_EP_cb(uint8_t rhport, - tusb_control_request_t const *p_request) override { - (void)rhport; - uint8_t const itf = tu_u16_low(tu_le16toh(p_request->wIndex)); - uint8_t const alt = tu_u16_low(tu_le16toh(p_request->wValue)); - if (alt == 0) setStatus(AudioProcessingStatus::ACTIVE); - - return true; - } - // for speaker - void feedback_params_cb(uint8_t func_id, uint8_t alt_itf, - audio_feedback_params_t *feedback_param) override { - (void)func_id; - (void)alt_itf; - // Set feedback method to fifo counting - feedback_param->method = AUDIO_FEEDBACK_METHOD_FIFO_COUNT; - feedback_param->sample_freq = cfg.sample_rate; - } - - /// Determine the interface descriptor length - size_t getInterfaceDescriptorLength(uint8_t itfnum) override { - return getInterfaceDescriptor(nullptr, 0); - } - - // build interface descriptor - uint16_t getInterfaceDescriptor(uint8_t *buf, - uint16_t bufsize) { - // if no source or sink then audio is not active - if (!isMicrophone() && !isSpeaker()) { - return 0; - } - - // if we know the len, we can return it - if (buf == nullptr && _desc_len > 0) { - return _desc_len; - } - - // the buffer was too small - if (_desc_len > 0 && bufsize < _desc_len) { - return 0; - } - - return interfaceDescriptor(buf, bufsize); - } - - inline USBDeviceAudioAPI& api() { return _api; } - - protected: - bool _is_led_setup = true; - AudioProcessingStatus _processing_status = AudioProcessingStatus::INACTIVE; - std::vector _mute; // +1 for master channel 0 - std::vector _volume; // +1 for master channel 0 - uint8_t _clk_is_valid = true; - ByteBuffer _in_buffer; - ByteBuffer _out_buffer; - bool _led_active = false; - uint64_t _led_timeout = 0; - - // persisted descriptor data - uint8_t _itfnum_spk = 0; - uint8_t _itfnum_mic = 0; - uint8_t _itf_number_total = 0; - uint8_t _itfnum_ctl = 0; - uint8_t _ep_ctl = 0; - uint8_t _ep_mic = 0; - uint8_t _ep_spk = 0; - uint8_t _ep_fb = 0; - uint8_t _ep_int = 0; - uint8_t _stridx = 0; - int _desc_append_pos = 0; - int _desc_len = 0; - - // input/output callbacks - Stream *p_stream = nullptr; - Print *p_print = nullptr; - USBDeviceAudioAPI _api; - USBAudioConfig cfg; - std::vector interface_descriptor; - - /// Define the led delay - void setStatus(AudioProcessingStatus status) { _processing_status = status; } - - /// We can use 8 debug pins with a logic analyser - /// We can use 8 debug pins with a logic analyser - void setupDebugPins() { -#if AUDIO_DEBUG - for (int j = 0; j < 8; j++) { - pinMode(j, OUTPUT); - } -#endif - } - - void append(uint8_t *to, uint8_t *str, int len) { - if (to != nullptr) memcpy(to + _desc_append_pos, str, len); - _desc_append_pos += len; - } - - static size_t defaultWriteCB(const uint8_t *data, size_t len, - USBDeviceAudio &ref) { - Print *p_print = ref.p_print; - if (p_print) return p_print->write((const uint8_t *)data, len); - return 0; - } - - static size_t defaultReadCB(uint8_t *data, size_t len, USBDeviceAudio &ref) { - Stream *p_stream = ref.p_stream; - if (p_stream) return p_stream->readBytes((uint8_t *)data, len); - return 0; - } - - // Helper methods - virtual bool feature_unit_get_request( - uint8_t rhport, tusb_control_request_t const *p_request) { - // Page 91 in UAC2 specification - uint8_t channelNum = TU_U16_LOW(p_request->wValue); - uint8_t ctrlSel = TU_U16_HIGH(p_request->wValue); - // uint8_t itf = TU_U16_LOW(p_request->wIndex); // Since - // we have only one audio function implemented, we do not need the itf value - uint8_t entityID = TU_U16_HIGH(p_request->wIndex); - switch (ctrlSel) { - case AUDIO_FU_CTRL_MUTE: { - // Audio control mute cur parameter block consists of only one byte - we - // thus can send it right away There does not exist a range parameter - // block for mute - LOG_AUDIO_DEBUG(" Get Mute of channel: %u", channelNum); - bool current_mute = _mute[channelNum]; - bool rc = tud_control_xfer(rhport, p_request, ¤t_mute, 1); - _mute[channelNum] = current_mute; - return rc; - } - - case AUDIO_FU_CTRL_VOLUME: - switch (p_request->bRequest) { - case AUDIO_CS_REQ_CUR: - LOG_AUDIO_DEBUG(" Get Volume of channel: %u", channelNum); - return tud_control_xfer(rhport, p_request, &_volume[channelNum], - sizeof(_volume[channelNum])); - - case AUDIO_CS_REQ_RANGE: - LOG_AUDIO_DEBUG(" Get Volume range of channel: %u", channelNum); - - // Copy values - only for testing - better is version below - audio_control_range_2_n_t(1) ret; - - ret.wNumSubRanges = 1; - ret.subrange[0].bMin = 0; // -90 dB - ret.subrange[0].bMax = 100; // +90 dB - ret.subrange[0].bRes = 1; // 1 dB steps - - return _api.tud_audio_buffer_and_schedule_control_xfer( - rhport, p_request, (void *)&ret, sizeof(ret)); - - // Unknown/Unsupported control - default: - // TU_BREAKPOINT(); - return false; - } - break; - - // Unknown/Unsupported control - default: - // TU_BREAKPOINT(); - return false; - } - return false; - } - - virtual bool feature_unit_set_request(uint8_t rhport, - tusb_control_request_t const *p_request, - uint8_t const *buf) { - (void)rhport; - audio_control_request_t const *request = - (audio_control_request_t const *)p_request; - - // TU_ASSERT(request->bEntityID == UAC2_ENTITY_SPK_FEATURE_UNIT); - TU_VERIFY(request->bRequest == AUDIO_CS_REQ_CUR); - - if (request->bControlSelector == AUDIO_FU_CTRL_MUTE) { - TU_VERIFY(request->wLength == sizeof(audio_control_cur_1_t)); - - _mute[request->bChannelNumber] = - ((audio_control_cur_1_t const *)buf)->bCur; - - LOG_AUDIO_DEBUG("Set channel %d Mute: %d", request->bChannelNumber, - _mute[request->bChannelNumber]); - - return true; - } else if (request->bControlSelector == AUDIO_FU_CTRL_VOLUME) { - TU_VERIFY(request->wLength == sizeof(audio_control_cur_2_t)); - - _volume[request->bChannelNumber] = - ((audio_control_cur_2_t const *)buf)->bCur; - - LOG_AUDIO_DEBUG("Set channel %d volume: %d dB", request->bChannelNumber, - _volume[request->bChannelNumber] / 256); - - return true; - } else { - LOG_AUDIO_DEBUG( - "Feature unit set request not supported, entity = %u, selector = %u, " - "request = %u", - request->bEntityID, request->bControlSelector, request->bRequest); - return false; - } - } - - virtual bool clock_get_request(uint8_t rhport, - tusb_control_request_t const *p_request) { - uint8_t channelNum = TU_U16_LOW(p_request->wValue); - uint8_t ctrlSel = TU_U16_HIGH(p_request->wValue); - // uint8_t itf = TU_U16_LOW(p_request->wIndex); // Since - // we have only one audio function implemented, we do not need the itf value - uint8_t entityID = TU_U16_HIGH(p_request->wIndex); - switch (ctrlSel) { - case AUDIO_CS_CTRL_SAM_FREQ: - // channelNum is always zero in this case - switch (p_request->bRequest) { - case AUDIO_CS_REQ_CUR: - LOG_AUDIO_DEBUG(" Get Sample Freq. -> %d", cfg.sample_rate); - // Buffered control transfer is needed for IN flow control to work - return _api.tud_audio_buffer_and_schedule_control_xfer( - rhport, p_request, &cfg.sample_rate, sizeof(cfg.sample_rate)); - - case AUDIO_CS_REQ_RANGE: - LOG_AUDIO_DEBUG(" Get Sample Freq. range -> %d - %d", - cfg.sample_rate, cfg.sample_rate); - audio_control_range_4_n_t(2) sampleFreqRng; - sampleFreqRng.wNumSubRanges = 1; - sampleFreqRng.subrange[0].bMin = cfg.sample_rate; - sampleFreqRng.subrange[0].bMax = cfg.sample_rate; - sampleFreqRng.subrange[0].bRes = 0; - // sampleFreqRng.subrange[1].bMin = cfg.sample_rate; - // sampleFreqRng.subrange[1].bMax = AUDIO_FREQ_MAX; - // sampleFreqRng.subrange[1].bRes = 0; - return tud_control_xfer(rhport, p_request, &sampleFreqRng, - sizeof(sampleFreqRng)); - - // Unknown/Unsupported control - default: - // TU_BREAKPOINT(); - return false; - } - break; - - case AUDIO_CS_CTRL_CLK_VALID: - // Only cur attribute exists for this request - LOG_AUDIO_DEBUG(" Get Sample Freq. valid"); - return tud_control_xfer(rhport, p_request, &_clk_is_valid, - sizeof(_clk_is_valid)); - - // Unknown/Unsupported control - default: - // TU_BREAKPOINT(); - return false; - } - } - - virtual bool clock_set_request(uint8_t rhport, - tusb_control_request_t const *p_request, - uint8_t const *buf) { - (void)rhport; - uint8_t channelNum = TU_U16_LOW(p_request->wValue); - uint8_t ctrlSel = TU_U16_HIGH(p_request->wValue); - // uint8_t itf = TU_U16_LOW(p_request->wIndex); // Since - // we have only one audio function implemented, we do not need the itf value - uint8_t entityID = TU_U16_HIGH(p_request->wIndex); - - // TU_ASSERT(request->bEntityID == UAC2_ENTITY_CLOCK); - // TU_VERIFY(p_request->bRequest == AUDIO_CS_REQ_CUR); - - if (ctrlSel == AUDIO_CS_CTRL_SAM_FREQ) { - TU_VERIFY(p_request->wLength == sizeof(audio_control_cur_4_t)); - cfg.sample_rate = (uint32_t)((audio_control_cur_4_t const *)buf)->bCur; - LOG_AUDIO_DEBUG("Clock set current freq: %ld", cfg.sample_rate); - return true; - } else { - LOG_AUDIO_DEBUG( - "Clock set request not supported, entity = %u, selector = %u, " - "request " - "= %u", - entityID, ctrlSel, p_request->bRequest); - return false; - } - } - - uint16_t interfaceDescriptor(uint8_t *buf, uint16_t bufsize) { - // Generate the descriptor and calculate it's length - uint8_t feature_unit_len = getFeatureUnitLength(); - _desc_append_pos = 0; - - // setup control interface and endpoint - if (_desc_len == 0) { - _itf_number_total = 1; - _itfnum_ctl = allocInterface(); - _ep_ctl = allocEndpoint(TUSB_DIR_IN); - } - - // Setup endpints and interfaces - if (isHeadset() && _desc_len == 0) { - _itfnum_mic = allocInterface(); // input interface - _itfnum_spk = allocInterface(); // output interface - _ep_mic = allocEndpoint(TUSB_DIR_IN); // intput - _ep_int = allocEndpoint(TUSB_DIR_IN); // input - _ep_spk = allocEndpoint(TUSB_DIR_OUT); // output - _itf_number_total += 2; - } else if (isMicrophone() && _desc_len == 0) { - _itfnum_mic = allocInterface(); - _ep_mic = allocEndpoint(TUSB_DIR_IN); - _itf_number_total++; - } else if (isSpeaker() && _desc_len == 0) { - _itfnum_spk = allocInterface(); // output interface - _ep_spk = allocEndpoint(TUSB_DIR_OUT); - _ep_fb = allocEndpoint(TUSB_DIR_IN); - _itf_number_total++; - } - - // generate descriptor - if (isHeadset()) { - uint8_t total_len = TUD_AUDIO_DESC_CLK_SRC_LEN + feature_unit_len + - (2 * (TUD_AUDIO_DESC_INPUT_TERM_LEN + - TUD_AUDIO_DESC_OUTPUT_TERM_LEN)); - interfaceDescriptorHeadset(buf, total_len); - } else if (isMicrophone()) { - uint8_t total_len = TUD_AUDIO_DESC_CLK_SRC_LEN + feature_unit_len + - TUD_AUDIO_DESC_INPUT_TERM_LEN + - TUD_AUDIO_DESC_OUTPUT_TERM_LEN; - interfaceDescriptorMicrophone(buf, total_len); - } else if (isSpeaker()) { - uint8_t total_len = TUD_AUDIO_DESC_CLK_SRC_LEN + feature_unit_len + - TUD_AUDIO_DESC_INPUT_TERM_LEN + - TUD_AUDIO_DESC_OUTPUT_TERM_LEN; - interfaceDescriptorSpeaker(buf, total_len); - } - - // record descriptor length - if (_desc_len == 0) { - _desc_len = _desc_append_pos; - } - - return _desc_len; - } - - void interfaceDescriptorHeader(uint8_t *buf, uint8_t total_len, - uint8_t category) { - /* Standard Interface Association Descriptor (IAD) */ - uint8_t d1[] = {TUD_AUDIO_DESC_IAD(/*_firstitfs*/ _itfnum_ctl, - /*_nitfs*/ _itf_number_total, - /*_stridx*/ 0)}; - append(buf, d1, sizeof(d1)); - - /* Standard AC Interface Descriptor(4.7.1) */ - uint8_t d2[] = {TUD_AUDIO_DESC_STD_AC(/*_itfnum*/ _itfnum_ctl, - /*_nEPs*/ 0x00, /*_stridx*/ _stridx)}; - append(buf, d2, sizeof(d2)); - - /* Class-Specific AC Interface Header Descriptor(4.7.2) AUDIO_FUNC_OTHER */ - uint8_t d3[] = {TUD_AUDIO_DESC_CS_AC( - /*_bcdADC*/ 0x0200, /*_category*/ category, /*_totallen*/ total_len, - /*_ctrl*/ AUDIO_CTRL_NONE << AUDIO_CS_AS_INTERFACE_CTRL_LATENCY_POS)}; - append(buf, d3, sizeof(d3)); - } - void interfaceDescriptorMicrophone(uint8_t *buf, uint8_t total_len) { - interfaceDescriptorHeader(buf, total_len, AUDIO_FUNC_MICROPHONE); - /* Clock Source Descriptor(4.7.2.1) */ - // TUD_AUDIO_DESC_CLK_SRC(/*_clkid*/ 0x04, /*_attr*/ - // AUDIO_CLOCK_SOURCE_ATT_INT_FIX_CLK, /*_ctrl*/ (AUDIO_CTRL_R << - // AUDIO_CLOCK_SOURCE_CTRL_CLK_FRQ_POS), /*_assocTerm*/ 0x01, /*_stridx*/ - // 0x00), - uint8_t d4[] = {TUD_AUDIO_DESC_CLK_SRC( - /*_clkid*/ UAC2_ENTITY_CLOCK, - /*_attr*/ AUDIO_CLOCK_SOURCE_ATT_INT_FIX_CLK, - /*_ctrl*/ (AUDIO_CTRL_R << AUDIO_CLOCK_SOURCE_CTRL_CLK_FRQ_POS), - /*_assocTerm*/ UAC2_ENTITY_MIC_INPUT_TERMINAL, /*_stridx*/ 0x00)}; - append(buf, d4, sizeof(d4)); - - /* Input Terminal Descriptor(4.7.2.4) */ - // TUD_AUDIO_DESC_INPUT_TERM(/*_termid*/ 0x01, /*_termtype*/ - // AUDIO_TERM_TYPE_IN_GENERIC_MIC, /*_assocTerm*/ 0x03, /*_clkid*/ 0x04, - // /*_nchannelslogical*/ 0x02, /*_channelcfg*/ - // AUDIO_CHANNEL_CONFIG_NON_PREDEFINED, /*_idxchannelnames*/ 0x00, /*_ctrl*/ - // AUDIO_CTRL_R << AUDIO_IN_TERM_CTRL_CONNECTOR_POS, /*_stridx*/ 0x00), - uint8_t d7[] = {TUD_AUDIO_DESC_INPUT_TERM( - /*_termid*/ UAC2_ENTITY_MIC_INPUT_TERMINAL, - /*_termtype*/ UAC2_ENTITY_SPK_OUTPUT_TERMINAL, - /*_assocTerm*/ UAC2_ENTITY_MIC_OUTPUT_TERMINAL, - /*_clkid*/ UAC2_ENTITY_CLOCK, /*_nchannelslogical*/ cfg.channels, - /*_channelcfg*/ AUDIO_CHANNEL_CONFIG_NON_PREDEFINED, - /*_idxchannelnames*/ 0x00, - /*_ctrl*/ AUDIO_CTRL_R << AUDIO_IN_TERM_CTRL_CONNECTOR_POS, - /*_stridx*/ 0x00)}; - append(buf, d7, sizeof(d7)); - /* Output Terminal Descriptor(4.7.2.5) */ - // TUD_AUDIO_DESC_OUTPUT_TERM(/*_termid*/ 0x03, /*_termtype*/ - // AUDIO_TERM_TYPE_USB_STREAMING, /*_assocTerm*/ 0x01, /*_srcid*/ 0x02, - // /*_clkid*/ 0x04, /*_ctrl*/ 0x0000, /*_stridx*/ 0x00), - uint8_t d8[] = {TUD_AUDIO_DESC_OUTPUT_TERM( - /*_termid*/ UAC2_ENTITY_MIC_OUTPUT_TERMINAL, - /*_termtype*/ AUDIO_TERM_TYPE_USB_STREAMING, - /*_assocTerm*/ UAC2_ENTITY_SPK_INPUT_TERMINAL, - /*_srcid*/ UAC2_ENTITY_MIC_FEATURE_UNIT, /*_clkid*/ UAC2_ENTITY_CLOCK, - /*_ctrl*/ 0x0000, /*_stridx*/ 0x00)}; - append(buf, d8, sizeof(d8)); - - /* Feature Unit Descriptor(4.7.2.8) */ - // #define TUD_AUDIO_DESC_FEATURE_UNIT_TWO_CHANNEL(_unitid, _srcid, - // _ctrlch0master, _ctrlch1, _ctrlch2, _stridx) - // TUD_AUDIO_DESC_FEATURE_UNIT_TWO_CHANNEL_LEN, TUSB_DESC_CS_INTERFACE, - // AUDIO_CS_AC_INTERFACE_FEATURE_UNIT, _unitid, _srcid, - // U32_TO_U8S_LE(_ctrlch0master), U32_TO_U8S_LE(_ctrlch1), - // U32_TO_U8S_LE(_ctrlch2), _stridx - // TUD_AUDIO_DESC_FEATURE_UNIT_TWO_CHANNEL(/*_unitid*/ 0x02, /*_srcid*/ - // 0x01, /*_ctrlch0master*/ AUDIO_CTRL_RW << - // AUDIO_FEATURE_UNIT_CTRL_MUTE_POS | AUDIO_CTRL_RW << - // AUDIO_FEATURE_UNIT_CTRL_VOLUME_POS, /*_ctrlch1*/ AUDIO_CTRL_RW << - // AUDIO_FEATURE_UNIT_CTRL_MUTE_POS | AUDIO_CTRL_RW << - // AUDIO_FEATURE_UNIT_CTRL_VOLUME_POS, /*_ctrlch2*/ AUDIO_CTRL_RW << - // AUDIO_FEATURE_UNIT_CTRL_MUTE_POS | AUDIO_CTRL_RW << - // AUDIO_FEATURE_UNIT_CTRL_VOLUME_POS, /*_stridx*/ 0x00),\ - - uint8_t feature_unit_len = getFeatureUnitLength(); - uint8_t df1[] = {feature_unit_len, TUSB_DESC_CS_INTERFACE, - AUDIO_CS_AC_INTERFACE_FEATURE_UNIT, - /*_unitid*/ UAC2_ENTITY_MIC_FEATURE_UNIT, - /*_srcid*/ UAC2_ENTITY_MIC_INPUT_TERMINAL}; - append(buf, df1, sizeof(df1)); - for (int j = 0; j < cfg.channels + 1; j++) { - uint8_t df2[] = { - U32_TO_U8S_LE(AUDIO_CTRL_RW << AUDIO_FEATURE_UNIT_CTRL_MUTE_POS | - AUDIO_CTRL_RW << AUDIO_FEATURE_UNIT_CTRL_VOLUME_POS)}; - append(buf, df2, sizeof(df2)); - } - // _stridx - uint8_t df3[1] = {0x00}; - append(buf, df3, sizeof(df3)); - - /* Standard AS Interface Descriptor(4.9.1) */ /* Interface 2, Alternate 0 - - default alternate setting - with 0 bandwidth */ - // TUD_AUDIO_DESC_STD_AS_INT(/*_itfnum*/ - // (uint8_t)((_itfnum)+1), /*_altset*/ 0x00, /*_nEPs*/ 0x00, - // /*_stridx*/ 0x00), - uint8_t d15[] = {TUD_AUDIO_DESC_STD_AS_INT( - /*_itfnum*/ (uint8_t)(_itfnum_mic), /*_altset*/ 0x00, /*_nEPs*/ 0x00, - /*_stridx*/ 0x00)}; - append(buf, d15, sizeof(d15)); - /* Standard AS Interface Descriptor(4.9.1) */ - /* Interface 2, Alternate 1 - alternate interface for data streaming */ - // TUD_AUDIO_DESC_STD_AS_INT(/*_itfnum*/ - // (uint8_t)((_itfnum)+1), /*_altset*/ 0x00, /*_nEPs*/ 0x00, - // /*_stridx*/ 0x00), - uint8_t d16[] = {TUD_AUDIO_DESC_STD_AS_INT( - /*_itfnum*/ (uint8_t)(_itfnum_mic), /*_altset*/ 0x01, /*_nEPs*/ 0x01, - /*_stridx*/ 0x00)}; - append(buf, d16, sizeof(d16)); - /* Class-Specific AS Interface Descriptor(4.9.2) */ - // TUD_AUDIO_DESC_CS_AS_INT(/*_termid*/ 0x03, /*_ctrl*/ - // AUDIO_CTRL_NONE, /*_formattype*/ AUDIO_FORMAT_TYPE_I, - // /*_formats*/ AUDIO_DATA_FORMAT_TYPE_I_PCM, - // /*_nchannelsphysical*/ 0x02, /*_channelcfg*/ - // AUDIO_CHANNEL_CONFIG_NON_PREDEFINED, /*_stridx*/ 0x00), - uint8_t d17[] = {TUD_AUDIO_DESC_CS_AS_INT( - /*_termid*/ UAC2_ENTITY_MIC_OUTPUT_TERMINAL, /*_ctrl*/ AUDIO_CTRL_NONE, - /*_formattype*/ AUDIO_FORMAT_TYPE_I, - /*_formats*/ AUDIO_DATA_FORMAT_TYPE_I_PCM, - /*_nchannelsphysical*/ cfg.channels, - /*_channelcfg*/ AUDIO_CHANNEL_CONFIG_NON_PREDEFINED, /*_stridx*/ 0x00)}; - append(buf, d17, sizeof(d17)); - /* Type I Format Type Descriptor(2.3.1.6 - Audio Formats) */ - // TUD_AUDIO_DESC_TYPE_I_FORMAT(_nBytesPerSample, - // _nBitsUsedPerSample), - uint8_t d18[] = {TUD_AUDIO_DESC_TYPE_I_FORMAT( - (uint8_t)(cfg.bits_per_sample / 8), (uint8_t)cfg.bits_per_sample)}; - append(buf, d18, sizeof(d18)); - /* Standard AS Isochronous Audio Data Endpoint Descriptor(4.10.1.1) */ - // TUD_AUDIO_DESC_STD_AS_ISO_EP(/*_ep*/ _epin, /*_attr*/ - // (uint8_t) ((uint8_t)TUSB_XFER_ISOCHRONOUS | - // (uint8_t)TUSB_ISO_EP_ATT_ASYNCHRONOUS | - // (uint8_t)TUSB_ISO_EP_ATT_DATA), /*_maxEPsize*/ _epsize, - // /*_interval*/ 0x01), - uint8_t attr = static_cast(TUSB_XFER_ISOCHRONOUS) | - static_cast(TUSB_ISO_EP_ATT_ASYNCHRONOUS) | - static_cast(TUSB_ISO_EP_ATT_DATA); - uint8_t d19[] = {TUD_AUDIO_DESC_STD_AS_ISO_EP( - /*_ep*/ _ep_mic, /*_attr*/ attr, /*_maxEPsize*/ getMaxEPSize(), - /*_interval*/ 0x01)}; - append(buf, d19, sizeof(d19)); - /* Class-Specific AS Isochronous Audio Data Endpoint Descriptor(4.10.1.2) */ - // TUD_AUDIO_DESC_CS_AS_ISO_EP(/*_attr*/ - // AUDIO_CS_AS_ISO_DATA_EP_ATT_NON_MAX_PACKETS_OK, /*_ctrl*/ - // AUDIO_CTRL_NONE, /*_lockdelayunit*/ - // AUDIO_CS_AS_ISO_DATA_EP_LOCK_DELAY_UNIT_UNDEFINED, - // /*_lockdelay*/ 0x0000) - uint8_t d20[] = {TUD_AUDIO_DESC_CS_AS_ISO_EP( - /*_attr*/ AUDIO_CS_AS_ISO_DATA_EP_ATT_NON_MAX_PACKETS_OK, - /*_ctrl*/ AUDIO_CTRL_NONE, - /*_lockdelayunit*/ AUDIO_CS_AS_ISO_DATA_EP_LOCK_DELAY_UNIT_UNDEFINED, - /*_lockdelay*/ 0x0000)}; - append(buf, d20, sizeof(d20)); - } - - void interfaceDescriptorSpeaker(uint8_t *buf, uint8_t total_len) { - interfaceDescriptorHeader(buf, total_len, AUDIO_FUNC_DESKTOP_SPEAKER); - /* Clock Source Descriptor(4.7.2.1) */ - // TUD_AUDIO_DESC_CLK_SRC(/*_clkid*/ 0x04, /*_attr*/ - // AUDIO_CLOCK_SOURCE_ATT_INT_FIX_CLK, /*_ctrl*/ (AUDIO_CTRL_R << - // AUDIO_CLOCK_SOURCE_CTRL_CLK_FRQ_POS), /*_assocTerm*/ 0x01, /*_stridx*/ - // 0x00), - uint8_t d4[] = {TUD_AUDIO_DESC_CLK_SRC( - /*_clkid*/ UAC2_ENTITY_CLOCK, - /*_attr*/ AUDIO_CLOCK_SOURCE_ATT_INT_PRO_CLK, - /*_ctrl*/ (AUDIO_CTRL_RW << AUDIO_CLOCK_SOURCE_CTRL_CLK_FRQ_POS), - /*_assocTerm*/ UAC2_ENTITY_SPK_INPUT_TERMINAL, /*_stridx*/ 0x00)}; - append(buf, d4, sizeof(d4)); - /* Input Terminal Descriptor(4.7.2.4) */ - // TUD_AUDIO_DESC_INPUT_TERM(/*_termid*/ 0x01, /*_termtype*/ - // AUDIO_TERM_TYPE_USB_STREAMING, /*_assocTerm*/ 0x00, - // /*_clkid*/ 0x04, /*_nchannelslogical*/ 0x02, /*_channelcfg*/ - // AUDIO_CHANNEL_CONFIG_NON_PREDEFINED, /*_idxchannelnames*/ - // 0x00, /*_ctrl*/ 0 * (AUDIO_CTRL_R << - // AUDIO_IN_TERM_CTRL_CONNECTOR_POS), /*_stridx*/ 0x00), - uint8_t d7[] = {TUD_AUDIO_DESC_INPUT_TERM( - /*_termid*/ UAC2_ENTITY_SPK_INPUT_TERMINAL, - /*_termtype*/ AUDIO_TERM_TYPE_USB_STREAMING, /*_assocTerm*/ 0x00, - /*_clkid*/ UAC2_ENTITY_CLOCK, /*_nchannelslogical*/ cfg.channels, - /*_channelcfg*/ AUDIO_CHANNEL_CONFIG_NON_PREDEFINED, - /*_idxchannelnames*/ 0x00, - /*_ctrl*/ 0 * (AUDIO_CTRL_R << AUDIO_IN_TERM_CTRL_CONNECTOR_POS), - /*_stridx*/ 0x00)}; - append(buf, d7, sizeof(d7)); - /* Output Terminal Descriptor(4.7.2.5) */ - // TUD_AUDIO_DESC_OUTPUT_TERM(/*_termid*/ 0x03, /*_termtype*/ - // AUDIO_TERM_TYPE_OUT_DESKTOP_SPEAKER, /*_assocTerm*/ 0x01, - // /*_srcid*/ 0x02, /*_clkid*/ 0x04, /*_ctrl*/ 0x0000, - // /*_stridx*/ 0x00), - uint8_t d8[] = {TUD_AUDIO_DESC_OUTPUT_TERM( - /*_termid*/ UAC2_ENTITY_SPK_OUTPUT_TERMINAL, - /*_termtype*/ AUDIO_TERM_TYPE_OUT_DESKTOP_SPEAKER, - /*_assocTerm*/ UAC2_ENTITY_SPK_INPUT_TERMINAL, - /*_srcid*/ UAC2_ENTITY_SPK_FEATURE_UNIT, /*_clkid*/ UAC2_ENTITY_CLOCK, - /*_ctrl*/ 0x0000, /*_stridx*/ 0x00)}; - append(buf, d8, sizeof(d8)); - - /* Feature Unit Descriptor(4.7.2.8) */ - // #define TUD_AUDIO_DESC_FEATURE_UNIT_TWO_CHANNEL(_unitid, _srcid, - // _ctrlch0master, _ctrlch1, _ctrlch2, _stridx) - // TUD_AUDIO_DESC_FEATURE_UNIT_TWO_CHANNEL_LEN, TUSB_DESC_CS_INTERFACE, - // AUDIO_CS_AC_INTERFACE_FEATURE_UNIT, _unitid, _srcid, - // U32_TO_U8S_LE(_ctrlch0master), U32_TO_U8S_LE(_ctrlch1), - // U32_TO_U8S_LE(_ctrlch2), _stridx - // TUD_AUDIO_DESC_FEATURE_UNIT_TWO_CHANNEL(/*_unitid*/ 0x02, /*_srcid*/ - // 0x01, /*_ctrlch0master*/ AUDIO_CTRL_RW << - // AUDIO_FEATURE_UNIT_CTRL_MUTE_POS | AUDIO_CTRL_RW << - // AUDIO_FEATURE_UNIT_CTRL_VOLUME_POS, /*_ctrlch1*/ AUDIO_CTRL_RW << - // AUDIO_FEATURE_UNIT_CTRL_MUTE_POS | AUDIO_CTRL_RW << - // AUDIO_FEATURE_UNIT_CTRL_VOLUME_POS, /*_ctrlch2*/ AUDIO_CTRL_RW << - // AUDIO_FEATURE_UNIT_CTRL_MUTE_POS | AUDIO_CTRL_RW << - // AUDIO_FEATURE_UNIT_CTRL_VOLUME_POS,/*_stridx*/ 0x00),\ uint8_t dfu[] = - // {TUD_AUDIO_DESC_FEATURE_UNIT_TWO_CHANNEL(/*_unitid*/ - // UAC2_ENTITY_SPK_FEATURE_UNIT, /*_srcid*/ UAC2_ENTITY_SPK_INPUT_TERMINAL, - // /*_ctrlch0master*/ AUDIO_CTRL_RW << AUDIO_FEATURE_UNIT_CTRL_MUTE_POS | - // AUDIO_CTRL_RW << AUDIO_FEATURE_UNIT_CTRL_VOLUME_POS, /*_ctrlch1*/ - // AUDIO_CTRL_RW << AUDIO_FEATURE_UNIT_CTRL_MUTE_POS | AUDIO_CTRL_RW << - // AUDIO_FEATURE_UNIT_CTRL_VOLUME_POS, /*_ctrlch2*/ AUDIO_CTRL_RW << - // AUDIO_FEATURE_UNIT_CTRL_MUTE_POS | AUDIO_CTRL_RW << - // AUDIO_FEATURE_UNIT_CTRL_VOLUME_POS, /*_stridx*/ 0x00)}; append(buf, dfu, - // sizeof(dfu)); - - uint8_t feature_unit_len = getFeatureUnitLength(); - uint8_t df1[] = {feature_unit_len, TUSB_DESC_CS_INTERFACE, - AUDIO_CS_AC_INTERFACE_FEATURE_UNIT, - /*_unitid*/ UAC2_ENTITY_SPK_FEATURE_UNIT, - /*_srcid*/ UAC2_ENTITY_SPK_INPUT_TERMINAL}; - append(buf, df1, sizeof(df1)); - for (int j = 0; j < cfg.channels + 1; j++) { - uint8_t df2[] = { - U32_TO_U8S_LE(AUDIO_CTRL_RW << AUDIO_FEATURE_UNIT_CTRL_MUTE_POS | - AUDIO_CTRL_RW << AUDIO_FEATURE_UNIT_CTRL_VOLUME_POS)}; - append(buf, df2, sizeof(df2)); - } - /*_stridx 0x00*/ - uint8_t df3[1] = {0x00}; - append(buf, df3, sizeof(df3)); - - /* Standard AS Interface Descriptor(4.9.1) */ /* Interface 2, Alternate 0 - - default alternate setting - with 0 bandwidth */ - // TUD_AUDIO_DESC_STD_AS_INT(/*_itfnum*/ (uint8_t)((_itfnum) - // + 1), /*_altset*/ 0x00, /*_nEPs*/ 0x00, /*_stridx*/ 0x00), - uint8_t d15[] = {TUD_AUDIO_DESC_STD_AS_INT( - /*_itfnum*/ (uint8_t)(_itfnum_spk), /*_altset*/ 0x00, /*_nEPs*/ 0x00, - /*_stridx*/ 0x00)}; - append(buf, d15, sizeof(d15)); - /* Standard AS Interface Descriptor(4.9.1) */ - /* Interface 2, Alternate 1 - alternate interface for data streaming */ - // TUD_AUDIO_DESC_STD_AS_INT(/*_itfnum*/ (uint8_t)((_itfnum) - // + 1), /*_altset*/ 0x01, /*_nEPs*/ 0x02, /*_stridx*/ 0x00), - uint8_t d16[] = {TUD_AUDIO_DESC_STD_AS_INT( - /*_itfnum*/ (uint8_t)(_itfnum_spk), /*_altset*/ 0x01, /*_nEPs*/ 0x02, - /*_stridx*/ 0x00)}; - append(buf, d16, sizeof(d16)); - // // TUD_AUDIO_DESC_CS_AS_INT(/*_termid*/ 0x03, /*_ctrl*/ - // AUDIO_CTRL_NONE, /*_formattype*/ AUDIO_FORMAT_TYPE_I, /*_formats*/ - // AUDIO_DATA_FORMAT_TYPE_I_PCM, /*_nchannelsphysical*/ 0x02, - // /*_channelcfg*/ AUDIO_CHANNEL_CONFIG_NON_PREDEFINED, /*_stridx*/ 0x00), - uint8_t d17[] = {TUD_AUDIO_DESC_CS_AS_INT( - /*_termid*/ UAC2_ENTITY_SPK_INPUT_TERMINAL, /*_ctrl*/ AUDIO_CTRL_NONE, - /*_formattype*/ AUDIO_FORMAT_TYPE_I, - /*_formats*/ AUDIO_DATA_FORMAT_TYPE_I_PCM, - /*_nchannelsphysical*/ cfg.channels, - /*_channelcfg*/ AUDIO_CHANNEL_CONFIG_NON_PREDEFINED, /*_stridx*/ 0x00)}; - append(buf, d17, sizeof(d17)); - - /* Type I Format Type Descriptor(2.3.1.6 - Audio Formats) */ - // TUD_AUDIO_DESC_TYPE_I_FORMAT(_nBytesPerSample, - // _nBitsUsedPerSample), - uint8_t d18[] = {TUD_AUDIO_DESC_TYPE_I_FORMAT( - (uint8_t)(cfg.bits_per_sample / 8), (uint8_t)cfg.bits_per_sample)}; - append(buf, d18, sizeof(d18)); - // /* Class-Specific AS Interface Descriptor(4.9.2) */ - /* Standard AS Isochronous Audio Data Endpoint Descriptor(4.10.1.1) */ - // TUD_AUDIO_DESC_STD_AS_ISO_EP(/*_ep*/ _epout, /*_attr*/ - // (uint8_t) ((uint8_t)TUSB_XFER_ISOCHRONOUS | - // (uint8_t)TUSB_ISO_EP_ATT_ASYNCHRONOUS | - // (uint8_t)TUSB_ISO_EP_ATT_DATA), /*_maxEPsize*/ _epoutsize, - // /*_interval*/ 0x01), - uint8_t d19[] = {TUD_AUDIO_DESC_STD_AS_ISO_EP( - /*_ep*/ _ep_spk, /*_attr*/ - (uint8_t)((uint8_t)TUSB_XFER_ISOCHRONOUS | - (uint8_t)TUSB_ISO_EP_ATT_ASYNCHRONOUS | - (uint8_t)TUSB_ISO_EP_ATT_DATA), - /*_maxEPsize*/ getMaxEPSize(), /*_interval*/ 0x01)}; - append(buf, d19, sizeof(d19)); - /* Class-Specific AS Isochronous Audio Data Endpoint Descriptor(4.10.1.2) */ - // TUD_AUDIO_DESC_CS_AS_ISO_EP(/*_attr*/ - // AUDIO_CS_AS_ISO_DATA_EP_ATT_NON_MAX_PACKETS_OK, /*_ctrl*/ - // AUDIO_CTRL_NONE, /*_lockdelayunit*/ - // AUDIO_CS_AS_ISO_DATA_EP_LOCK_DELAY_UNIT_MILLISEC, - // /*_lockdelay*/ 0x0001) - uint8_t d20[] = {TUD_AUDIO_DESC_CS_AS_ISO_EP( - /*_attr*/ AUDIO_CS_AS_ISO_DATA_EP_ATT_NON_MAX_PACKETS_OK, - /*_ctrl*/ AUDIO_CTRL_NONE, - /*_lockdelayunit*/ AUDIO_CS_AS_ISO_DATA_EP_LOCK_DELAY_UNIT_MILLISEC, - /*_lockdelay*/ 0x0001)}; - append(buf, d20, sizeof(d20)); - - // /* Standard AS Isochronous Feedback Endpoint Descriptor(4.10.2.1) */ -#if ISO_FB_EP==3 - // TUD_AUDIO_DESC_STD_AS_ISO_FB_EP(/*_ep*/ _epfb, /*_epsize*/ _epfbsize, /*_interval*/ TUD_OPT_HIGH_SPEED ? 4 : 1) - uint8_t d21[] = {TUD_AUDIO_DESC_STD_AS_ISO_FB_EP( - /*_ep*/ _ep_fb, /*_epsize*/ 0X04, - /*_interval*/ TUD_OPT_HIGH_SPEED ? 4 : 1)}; -#else - uint8_t d21[] = {TUD_AUDIO_DESC_STD_AS_ISO_FB_EP( - /*_ep*/ _ep_fb, /*_epsize 0X04,*/ - /*_interval*/ TUD_OPT_HIGH_SPEED ? 4 : 1)}; -#endif - append(buf, d21, sizeof(d21)); - } - - void interfaceDescriptorHeadset(uint8_t *buf, uint8_t total_len) { - interfaceDescriptorHeader(buf, total_len, AUDIO_FUNC_HEADSET); - - /* Clock Source Descriptor(4.7.2.1) */ - // TUD_AUDIO_DESC_CLK_SRC(/*_clkid*/ UAC2_ENTITY_CLOCK, /*_attr*/ 3, - // /*_ctrl*/ 7, /*_assocTerm*/ 0x00, /*_stridx*/ 0x00), - uint8_t d1[] = {TUD_AUDIO_DESC_CLK_SRC( - /*_clkid*/ UAC2_ENTITY_CLOCK, - /*_attr*/ AUDIO_CLOCK_SOURCE_ATT_INT_PRO_CLK, /*_ctrl*/ 7, - /*_assocTerm*/ 00, /*_stridx*/ 0x00)}; - append(buf, d1, sizeof(d1)); - - /* Input Terminal Descriptor(4.7.2.4) */ - // TUD_AUDIO_DESC_INPUT_TERM(/*_termid*/ UAC2_ENTITY_SPK_INPUT_TERMINAL, - // /*_termtype*/ AUDIO_TERM_TYPE_USB_STREAMING, /*_assocTerm*/ 0x00, - // /*_clkid*/ UAC2_ENTITY_CLOCK, /*_nchannelslogical*/ 0x02, /*_channelcfg*/ - // AUDIO_CHANNEL_CONFIG_NON_PREDEFINED, /*_idxchannelnames*/ 0x00, /*_ctrl*/ - // 0 * (AUDIO_CTRL_R << AUDIO_IN_TERM_CTRL_CONNECTOR_POS), /*_stridx*/ - // 0x00), - uint8_t d2[] = {TUD_AUDIO_DESC_INPUT_TERM( - /*_termid*/ UAC2_ENTITY_SPK_INPUT_TERMINAL, - /*_termtype*/ AUDIO_TERM_TYPE_USB_STREAMING, /*_assocTerm*/ 0x00, - /*_clkid*/ UAC2_ENTITY_CLOCK, /*_nchannelslogical*/ cfg.channels, - /*_channelcfg*/ AUDIO_CHANNEL_CONFIG_NON_PREDEFINED, - /*_idxchannelnames*/ 0x00, - /*_ctrl*/ 0 * (AUDIO_CTRL_R << AUDIO_IN_TERM_CTRL_CONNECTOR_POS), - /*_stridx*/ 0x00)}; - append(buf, d2, sizeof(d2)); - - /* Feature Unit Descriptor(4.7.2.8) */ - // TUD_AUDIO_DESC_FEATURE_UNIT_TWO_CHANNEL(/*_unitid*/ - // UAC2_ENTITY_SPK_FEATURE_UNIT, /*_srcid*/ UAC2_ENTITY_SPK_INPUT_TERMINAL, - // /*_ctrlch0master*/ (AUDIO_CTRL_RW << AUDIO_FEATURE_UNIT_CTRL_MUTE_POS | - // AUDIO_CTRL_RW << AUDIO_FEATURE_UNIT_CTRL_VOLUME_POS), /*_ctrlch1*/ - // (AUDIO_CTRL_RW << AUDIO_FEATURE_UNIT_CTRL_MUTE_POS | AUDIO_CTRL_RW << - // AUDIO_FEATURE_UNIT_CTRL_VOLUME_POS), /*_ctrlch2*/ (AUDIO_CTRL_RW << - // AUDIO_FEATURE_UNIT_CTRL_MUTE_POS | AUDIO_CTRL_RW << - // AUDIO_FEATURE_UNIT_CTRL_VOLUME_POS), /*_stridx*/ 0x00), - uint8_t feature_unit_len = getFeatureUnitLength(); - uint8_t df1[] = {feature_unit_len, TUSB_DESC_CS_INTERFACE, - AUDIO_CS_AC_INTERFACE_FEATURE_UNIT, - /*_unitid*/ UAC2_ENTITY_SPK_FEATURE_UNIT, - /*_srcid*/ UAC2_ENTITY_SPK_INPUT_TERMINAL}; - append(buf, df1, sizeof(df1)); - // first is master channel - for (int j = 0; j < cfg.channels + 1; j++) { - uint8_t df2[] = { - U32_TO_U8S_LE((AUDIO_CTRL_RW << AUDIO_FEATURE_UNIT_CTRL_MUTE_POS | - AUDIO_CTRL_RW << AUDIO_FEATURE_UNIT_CTRL_VOLUME_POS))}; - append(buf, df2, sizeof(df2)); - } - /*_stridx 0x00*/ - uint8_t df3[1] = {0x00}; - append(buf, df3, sizeof(df3)); - - //-- out --- - - /* Output Terminal Descriptor(4.7.2.5) */ - // TUD_AUDIO_DESC_OUTPUT_TERM(/*_termid*/ UAC2_ENTITY_SPK_OUTPUT_TERMINAL, - // /*_termtype*/ AUDIO_TERM_TYPE_OUT_HEADPHONES, /*_assocTerm*/ 0x00, - // /*_srcid*/ UAC2_ENTITY_SPK_FEATURE_UNIT, /*_clkid*/ UAC2_ENTITY_CLOCK, - // /*_ctrl*/ 0x0000, /*_stridx*/ 0x00), - uint8_t d3[] = {TUD_AUDIO_DESC_OUTPUT_TERM( - /*_termid*/ UAC2_ENTITY_SPK_OUTPUT_TERMINAL, - /*_termtype*/ AUDIO_TERM_TYPE_OUT_HEADPHONES, /*_assocTerm*/ 00, - /*_srcid*/ UAC2_ENTITY_SPK_FEATURE_UNIT, /*_clkid*/ UAC2_ENTITY_CLOCK, - /*_ctrl*/ 0x0000, /*_stridx*/ 0x00)}; - append(buf, d3, sizeof(d3)); - - /* Input Terminal Descriptor(4.7.2.4) */ - // TUD_AUDIO_DESC_INPUT_TERM(/*_termid*/ UAC2_ENTITY_MIC_INPUT_TERMINAL, - // /*_termtype*/ AUDIO_TERM_TYPE_IN_GENERIC_MIC, /*_assocTerm*/ 0x00, - // /*_clkid*/ UAC2_ENTITY_CLOCK, /*_nchannelslogical*/ 0x01, /*_channelcfg*/ - // AUDIO_CHANNEL_CONFIG_NON_PREDEFINED, /*_idxchannelnames*/ 0x00, /*_ctrl*/ - // 0 * (AUDIO_CTRL_R << AUDIO_IN_TERM_CTRL_CONNECTOR_POS), /*_stridx*/ - // 0x00), - uint8_t d4[] = {TUD_AUDIO_DESC_INPUT_TERM( - /*_termid*/ UAC2_ENTITY_MIC_INPUT_TERMINAL, - /*_termtype*/ AUDIO_TERM_TYPE_IN_GENERIC_MIC, /*_assocTerm*/ 00, - /*_clkid*/ UAC2_ENTITY_CLOCK, /*_nchannelslogical*/ cfg.channels, - /*_channelcfg*/ AUDIO_CHANNEL_CONFIG_NON_PREDEFINED, - /*_idxchannelnames*/ 0x00, - /*_ctrl*/ 0 * (AUDIO_CTRL_R << AUDIO_IN_TERM_CTRL_CONNECTOR_POS), - /*_stridx*/ 0x00)}; - append(buf, d4, sizeof(d4)); - - /* Output Terminal Descriptor(4.7.2.5) */ - // TUD_AUDIO_DESC_OUTPUT_TERM(/*_termid*/ UAC2_ENTITY_MIC_OUTPUT_TERMINAL, - // /*_termtype*/ AUDIO_TERM_TYPE_USB_STREAMING, /*_assocTerm*/ 0x00, - // /*_srcid*/ UAC2_ENTITY_MIC_INPUT_TERMINAL, /*_clkid*/ UAC2_ENTITY_CLOCK, - // /*_ctrl*/ 0x0000, /*_stridx*/ 0x00), - uint8_t d5[] = {TUD_AUDIO_DESC_OUTPUT_TERM( - /*_termid*/ UAC2_ENTITY_MIC_OUTPUT_TERMINAL, - /*_termtype*/ AUDIO_TERM_TYPE_USB_STREAMING, /*_assocTerm*/ 00, - /*_srcid*/ UAC2_ENTITY_MIC_INPUT_TERMINAL, /*_clkid*/ UAC2_ENTITY_CLOCK, - /*_ctrl*/ 0x0000, /*_stridx*/ 0x00)}; - append(buf, d5, sizeof(d5)); - - /* Standard AC Interrupt Endpoint Descriptor(4.8.2.1) */ - // TUD_AUDIO_DESC_STD_AC_INT_EP(/*_ep*/ _epint, /*_interval*/ 0x01), - uint8_t d6[] = { - TUD_AUDIO_DESC_STD_AC_INT_EP(/*_ep*/ _ep_int, /*_interval*/ 0x01)}; - append(buf, d6, sizeof(d6)); - - // -- SPK --- - - /* Standard AS Interface Descriptor(4.9.1) */ - /* Interface 1, Alternate 0 - default alternate setting with 0 bandwidth */ - // TUD_AUDIO_DESC_STD_AS_INT(/*_itfnum*/ - // (uint8_t)(ITF_NUM_AUDIO_STREAMING_SPK), /*_altset*/ 0x00, /*_nEPs*/ 0x00, - // /*_stridx*/ 0x05), - uint8_t d7[] = {TUD_AUDIO_DESC_STD_AS_INT( - /*_itfnum*/ (uint8_t)(_itfnum_spk), /*_altset*/ 0x00, /*_nEPs*/ 0x00, - /*_stridx*/ 0x05)}; - append(buf, d7, sizeof(d7)); - - /* Standard AS Interface Descriptor(4.9.1) */ - /* Interface 1, Alternate 1 - alternate interface for data streaming */ - // TUD_AUDIO_DESC_STD_AS_INT(/*_itfnum*/ - // (uint8_t)(ITF_NUM_AUDIO_STREAMING_SPK), /*_altset*/ 0x01, /*_nEPs*/ 0x01, - // /*_stridx*/ 0x05), - uint8_t d8[] = {TUD_AUDIO_DESC_STD_AS_INT( - /*_itfnum*/ (uint8_t)(_itfnum_spk), /*_altset*/ 0x01, /*_nEPs*/ 0x01, - /*_stridx*/ 0x05)}; - append(buf, d8, sizeof(d8)); - - /* Class-Specific AS Interface Descriptor(4.9.2) */ - // TUD_AUDIO_DESC_CS_AS_INT(/*_termid*/ UAC2_ENTITY_SPK_INPUT_TERMINAL, - // /*_ctrl*/ AUDIO_CTRL_NONE, /*_formattype*/ AUDIO_FORMAT_TYPE_I, - // /*_formats*/ AUDIO_DATA_FORMAT_TYPE_I_PCM, /*_nchannelsphysical*/ - // CFG_TUD_AUDIO_FUNC_1_N_CHANNELS_RX, /*_channelcfg*/ - // AUDIO_CHANNEL_CONFIG_NON_PREDEFINED, /*_stridx*/ 0x00), - uint8_t d9[] = {TUD_AUDIO_DESC_CS_AS_INT( - /*_termid*/ UAC2_ENTITY_SPK_INPUT_TERMINAL, /*_ctrl*/ AUDIO_CTRL_NONE, - /*_formattype*/ AUDIO_FORMAT_TYPE_I, - /*_formats*/ AUDIO_DATA_FORMAT_TYPE_I_PCM, - /*_nchannelsphysical*/ cfg.channels, - /*_channelcfg*/ AUDIO_CHANNEL_CONFIG_NON_PREDEFINED, /*_stridx*/ 0x00)}; - append(buf, d9, sizeof(d9)); - - /* Type I Format Type Descriptor(2.3.1.6 - Audio Formats) */ - // TUD_AUDIO_DESC_TYPE_I_FORMAT(CFG_TUD_AUDIO_FUNC_1_FORMAT_1_N_BYTES_PER_SAMPLE_RX, - // CFG_TUD_AUDIO_FUNC_1_FORMAT_1_RESOLUTION_RX), - uint8_t d10[] = {TUD_AUDIO_DESC_TYPE_I_FORMAT( - (uint8_t)(cfg.bits_per_sample / 8), (uint8_t)cfg.bits_per_sample)}; - append(buf, d10, sizeof(d10)); - - /* Standard AS Isochronous Audio Data Endpoint Descriptor(4.10.1.1) */ - // TUD_AUDIO_DESC_STD_AS_ISO_EP(/*_ep*/ _epout, /*_attr*/ (uint8_t) - // ((uint8_t)TUSB_XFER_ISOCHRONOUS | (uint8_t)TUSB_ISO_EP_ATT_ADAPTIVE | - // (uint8_t)TUSB_ISO_EP_ATT_DATA), /*_maxEPsize*/ - // TUD_AUDIO_EP_SIZE(CFG_TUD_AUDIO_FUNC_1_MAX_SAMPLE_RATE, - // CFG_TUD_AUDIO_FUNC_1_FORMAT_1_N_BYTES_PER_SAMPLE_RX, - // CFG_TUD_AUDIO_FUNC_1_N_CHANNELS_RX), /*_interval*/ 0x01), - uint8_t d11[] = {TUD_AUDIO_DESC_STD_AS_ISO_EP( - /*_ep*/ _ep_spk, /*_attr*/ - (uint8_t)((uint8_t)TUSB_XFER_ISOCHRONOUS | - (uint8_t)TUSB_ISO_EP_ATT_ADAPTIVE | - (uint8_t)TUSB_ISO_EP_ATT_DATA), - /*_maxEPsize*/ getMaxEPSize(), /*_interval*/ 0x01)}; - append(buf, d11, sizeof(d11)); - - /* Class-Specific AS Isochronous Audio Data Endpoint Descriptor(4.10.1.2) */ - // TUD_AUDIO_DESC_CS_AS_ISO_EP(/*_attr*/ - // AUDIO_CS_AS_ISO_DATA_EP_ATT_NON_MAX_PACKETS_OK, /*_ctrl*/ - // AUDIO_CTRL_NONE, /*_lockdelayunit*/ - // AUDIO_CS_AS_ISO_DATA_EP_LOCK_DELAY_UNIT_MILLISEC, /*_lockdelay*/ 0x0001), - uint8_t d12[] = {TUD_AUDIO_DESC_CS_AS_ISO_EP( - /*_attr*/ AUDIO_CS_AS_ISO_DATA_EP_ATT_NON_MAX_PACKETS_OK, - /*_ctrl*/ AUDIO_CTRL_NONE, - /*_lockdelayunit*/ AUDIO_CS_AS_ISO_DATA_EP_LOCK_DELAY_UNIT_MILLISEC, - /*_lockdelay*/ 0x0001)}; - append(buf, d12, sizeof(d12)); - - // -- MIC --- - /* Standard AS Interface Descriptor(4.9.1) */ - /* Interface 2, Alternate 0 - default alternate setting with 0 bandwidth */ - // TUD_AUDIO_DESC_STD_AS_INT(/*_itfnum*/ - // (uint8_t)(ITF_NUM_AUDIO_STREAMING_MIC), /*_altset*/ 0x00, /*_nEPs*/ 0x00, - // /*_stridx*/ 0x04), - uint8_t d13[] = {TUD_AUDIO_DESC_STD_AS_INT( - /*_itfnum*/ (uint8_t)(_itfnum_mic), /*_altset*/ 0x00, /*_nEPs*/ 0x00, - /*_stridx*/ 0x04)}; - append(buf, d13, sizeof(d13)); - - /* Standard AS Interface Descriptor(4.9.1) */ - /* Interface 2, Alternate 1 - alternate interface for data streaming */ - // TUD_AUDIO_DESC_STD_AS_INT(/*_itfnum*/ - // (uint8_t)(ITF_NUM_AUDIO_STREAMING_MIC), /*_altset*/ 0x01, /*_nEPs*/ 0x01, - // /*_stridx*/ 0x04), - uint8_t d14[] = {TUD_AUDIO_DESC_STD_AS_INT( - /*_itfnum*/ (uint8_t)(_itfnum_mic), /*_altset*/ 0x01, /*_nEPs*/ 0x01, - /*_stridx*/ 0x04)}; - append(buf, d14, sizeof(d14)); - - /* Class-Specific AS Interface Descriptor(4.9.2) */ - // TUD_AUDIO_DESC_CS_AS_INT(/*_termid*/ UAC2_ENTITY_MIC_OUTPUT_TERMINAL, - // /*_ctrl*/ AUDIO_CTRL_NONE, /*_formattype*/ AUDIO_FORMAT_TYPE_I, - // /*_formats*/ AUDIO_DATA_FORMAT_TYPE_I_PCM, /*_nchannelsphysical*/ - // CFG_TUD_AUDIO_FUNC_1_N_CHANNELS_TX, /*_channelcfg*/ - // AUDIO_CHANNEL_CONFIG_NON_PREDEFINED, /*_stridx*/ 0x00), - uint8_t d15[] = {TUD_AUDIO_DESC_CS_AS_INT( - /*_termid*/ UAC2_ENTITY_MIC_OUTPUT_TERMINAL, /*_ctrl*/ AUDIO_CTRL_NONE, - /*_formattype*/ AUDIO_FORMAT_TYPE_I, - /*_formats*/ AUDIO_DATA_FORMAT_TYPE_I_PCM, - /*_nchannelsphysical*/ cfg.channels, - /*_channelcfg*/ AUDIO_CHANNEL_CONFIG_NON_PREDEFINED, /*_stridx*/ 0x00)}; - append(buf, d15, sizeof(d15)); - - /* Type I Format Type Descriptor(2.3.1.6 - Audio Formats) */ - // TUD_AUDIO_DESC_TYPE_I_FORMAT(CFG_TUD_AUDIO_FUNC_1_FORMAT_1_N_BYTES_PER_SAMPLE_TX, - // CFG_TUD_AUDIO_FUNC_1_FORMAT_1_RESOLUTION_TX), - uint8_t d16[] = {TUD_AUDIO_DESC_TYPE_I_FORMAT( - (uint8_t)(cfg.bits_per_sample / 8), (uint8_t)cfg.bits_per_sample)}; - append(buf, d16, sizeof(d16)); - - /* Standard AS Isochronous Audio Data Endpoint Descriptor(4.10.1.1) */ - // TUD_AUDIO_DESC_STD_AS_ISO_EP(/*_ep*/ _epin, /*_attr*/ (uint8_t) - // ((uint8_t)TUSB_XFER_ISOCHRONOUS | (uint8_t)TUSB_ISO_EP_ATT_ASYNCHRONOUS | - // (uint8_t)TUSB_ISO_EP_ATT_DATA), /*_maxEPsize*/ - // TUD_AUDIO_EP_SIZE(CFG_TUD_AUDIO_FUNC_1_MAX_SAMPLE_RATE, - // CFG_TUD_AUDIO_FUNC_1_FORMAT_1_N_BYTES_PER_SAMPLE_TX, - // CFG_TUD_AUDIO_FUNC_1_N_CHANNELS_TX), /*_interval*/ 0x01), - uint8_t d17[] = {TUD_AUDIO_DESC_STD_AS_ISO_EP( - /*_ep*/ _ep_mic, /*_attr*/ - (uint8_t)((uint8_t)TUSB_XFER_ISOCHRONOUS | - (uint8_t)TUSB_ISO_EP_ATT_ASYNCHRONOUS | - (uint8_t)TUSB_ISO_EP_ATT_DATA), - /*_maxEPsize*/ getMaxEPSize(), /*_interval*/ 0x01)}; - append(buf, d17, sizeof(d17)); - - /* Class-Specific AS Isochronous Audio Data Endpoint Descriptor(4.10.1.2) */ - // TUD_AUDIO_DESC_CS_AS_ISO_EP(/*_attr*/ - // AUDIO_CS_AS_ISO_DATA_EP_ATT_NON_MAX_PACKETS_OK, /*_ctrl*/ - // AUDIO_CTRL_NONE, /*_lockdelayunit*/ - // AUDIO_CS_AS_ISO_DATA_EP_LOCK_DELAY_UNIT_UNDEFINED, /*_lockdelay*/ - // 0x0000), - uint8_t d18[] = {TUD_AUDIO_DESC_CS_AS_ISO_EP( - /*_attr*/ AUDIO_CS_AS_ISO_DATA_EP_ATT_NON_MAX_PACKETS_OK, - /*_ctrl*/ AUDIO_CTRL_NONE, - /*_lockdelayunit*/ AUDIO_CS_AS_ISO_DATA_EP_LOCK_DELAY_UNIT_UNDEFINED, - /*_lockdelay*/ 0x0000)}; - append(buf, d18, sizeof(d18)); - } -}; diff --git a/src/AudioTools/Sandbox/USB/USBDeviceAudioAPI.h b/src/AudioTools/Sandbox/USB/USBDeviceAudioAPI.h deleted file mode 100644 index 972c04ab73..0000000000 --- a/src/AudioTools/Sandbox/USB/USBDeviceAudioAPI.h +++ /dev/null @@ -1,1992 +0,0 @@ -#pragma once - -#include "class/audio/audio.h" -#include "common/tusb_mcu.h" -#include "common/tusb_verify.h" -#include "device/usbd.h" -#include "device/usbd_pvt.h" -#include "osal/osal.h" -#include "tusb.h" -#include "tusb_option.h" -#include "vector" - -#undef OUT_SW_BUF_MEM_SECTION -#undef CFG_TUSB_MEM_ALIGN -#define OUT_SW_BUF_MEM_SECTION -#define CFG_TUSB_MEM_ALIGN - -class USBDeviceAudio; - -enum { - AUDIO_FEEDBACK_METHOD_DISABLED, - AUDIO_FEEDBACK_METHOD_FREQUENCY_FIXED, - AUDIO_FEEDBACK_METHOD_FREQUENCY_FLOAT, - AUDIO_FEEDBACK_METHOD_FREQUENCY_POWER_OF_2, // For driver internal use only - AUDIO_FEEDBACK_METHOD_FIFO_COUNT -}; - -struct audio_feedback_params_t { - uint8_t method; - uint32_t sample_freq; // sample frequency in Hz - - union { - struct { - uint32_t mclk_freq; // Main clock frequency in Hz i.e. master clock to - // which sample clock is based on - } frequency; - }; -}; - -/** - * @brief Configuration for TinyUSB Audio - */ -class USBAudioConfig { - public: - int rh_port = 0; - uint8_t channels = 2; - uint32_t sample_rate = 48000; - uint8_t bits_per_sample = 16; - bool enable_feedback_ep = true; - bool enable_interrupt_ep = true; - bool enable_feedback_forward_correction = false; - bool enable_feedback_interval_isr = false; - bool enable_ep_in_flow_control = true; - bool enable_linear_buffer_tx = true; - bool enable_linear_buffer_rx = true; - bool enable_fifo_mutex = CFG_FIFO_MUTEX; - int func_n_as_int = 1; - int func_ctl_buffer_size = 0; - int func_ep_in_sw_buffer_size = 0; - int func_ep_out_sw_buffer_size = 0; - int func_ep_in_size_max = 0; // CFG_TUD_AUDIO_EP_SZ_IN - int func_ep_out_size_max = 0; // CFG_TUD_AUDIO_EP_SZ_OUT - size_t (*p_write_callback)(const uint8_t *data, size_t len, - USBDeviceAudio &ref) = nullptr; - size_t (*p_read_callback)(uint8_t *data, size_t len, - USBDeviceAudio &ref) = nullptr; - - bool is_ep_out() { return p_write_callback != nullptr; } - bool is_ep_in() { return p_read_callback != nullptr; }; - - // setup (missing) default values - void begin() { - if (func_ctl_buffer_size == 0) func_ctl_buffer_size = 64; - if (func_ep_in_size_max == 0) - func_ep_in_size_max = - TUD_AUDIO_EP_SIZE(sample_rate, bits_per_sample / 8, channels); - if (func_ep_out_size_max == 0) - func_ep_out_size_max = - TUD_AUDIO_EP_SIZE(sample_rate, bits_per_sample / 8, channels); - if (func_ep_out_size_max == 0) - func_ep_in_sw_buffer_size = - (TUD_OPT_HIGH_SPEED ? 32 : 4) * - func_ep_in_size_max; // Example write FIFO every 1ms, so it should be - // 8 times larger for HS device - if (func_ep_out_sw_buffer_size == 0) - func_ep_out_sw_buffer_size = - (TUD_OPT_HIGH_SPEED ? 32 : 4) * - func_ep_out_size_max; // Example write FIFO every 1ms, so it should - // be 8 times larger for HS device - } - void clear() { - func_ctl_buffer_size = 0; - func_ep_in_size_max = 0; - func_ep_out_size_max = 0; - func_ep_in_sw_buffer_size = 0; - func_ep_out_sw_buffer_size = 0; - } -}; - -/*** - * @brief Basic TinyUSB Audio User Callbacks - */ -class USBAudioCB { - public: - USBAudioCB() = default; - virtual uint16_t getInterfaceDescriptor(uint8_t itfnum, uint8_t *buf, - uint16_t bufsize) = 0; - - virtual size_t getInterfaceDescriptorLength(uint8_t itfnum) = 0; - - // Invoked when set interface is called, typically on start/stop streaming or - // format change - virtual bool set_itf_cb(uint8_t rhport, - tusb_control_request_t const *p_request) = 0; - - // Invoked when audio class specific set request received for an EP - virtual bool set_req_ep_cb(uint8_t rhport, - tusb_control_request_t const *p_request, - uint8_t *pBuff) = 0; - - // Invoked when audio class specific set request received for an interface - virtual bool set_req_itf_cb(uint8_t rhport, - tusb_control_request_t const *p_request, - uint8_t *pBuff) = 0; - - // Invoked when audio class specific set request received for an entity - virtual bool set_req_entity_cb(uint8_t rhport, - tusb_control_request_t const *p_request, - uint8_t *pBuff) = 0; - // Invoked when audio class specific get request received for an EP - virtual bool get_req_ep_cb(uint8_t rhport, - tusb_control_request_t const *p_request) = 0; - - // Invoked when audio class specific get request received for an interface - virtual bool get_req_itf_cb(uint8_t rhport, - tusb_control_request_t const *p_request) = 0; - - // Invoked when audio class specific get request received for an entity - virtual bool get_req_entity_cb(uint8_t rhport, - tusb_control_request_t const *p_request) = 0; - virtual bool tx_done_pre_load_cb(uint8_t rhport, uint8_t itf, uint8_t ep_in, - uint8_t cur_alt_setting) = 0; - - virtual bool tx_done_post_load_cb(uint8_t rhport, uint16_t n_bytes_copied, - uint8_t itf, uint8_t ep_in, - uint8_t cur_alt_setting) = 0; - - virtual bool rx_done_pre_read_cb(uint8_t rhport, uint16_t n_bytes_received, - uint8_t func_id, uint8_t ep_out, - uint8_t cur_alt_setting) = 0; - virtual bool rx_done_post_read_cb(uint8_t rhport, uint16_t n_bytes_received, - uint8_t func_id, uint8_t ep_out, - uint8_t cur_alt_setting) = 0; - - virtual bool set_itf_close_EP_cb(uint8_t rhport, - tusb_control_request_t const *p_request) = 0; - - // for speaker - virtual void feedback_params_cb(uint8_t func_id, uint8_t alt_itf, - audio_feedback_params_t *feedback_param) = 0; - - virtual void int_done_cb(uint8_t rhport) {}; - virtual void fb_done_cb(uint8_t func_id) {}; - virtual void feedback_interval_isr(uint8_t func_id, uint32_t frame_number, - uint8_t interval_shift) {} - - virtual uint8_t allocInterface(uint8_t count = 1) = 0; - virtual uint8_t allocEndpoint(uint8_t in) = 0; - - int func_id = 0; -}; - -/*** - * @brief Baisc TinyUSB Audio Device Driver as C++ class which does not rely on - * preprocesser defines!. - */ -class USBDeviceAudioAPI { - public: - bool tud_audio_n_mounted(uint8_t func_id) { - TU_VERIFY(func_id < cfg.func_n_as_int); - audiod_function_t *audio = &_audiod_fct[func_id]; - - return audio->mounted; - } - - //--------------------------------------------------------------------+ - // READ API - //--------------------------------------------------------------------+ - - uint16_t tud_audio_n_available(uint8_t func_id) { - TU_VERIFY(func_id < cfg.func_n_as_int && - _audiod_fct[func_id].p_desc != NULL); - return tu_fifo_count(&_audiod_fct[func_id].ep_out_ff); - } - - uint16_t tud_audio_n_read(uint8_t func_id, void *buffer, uint16_t bufsize) { - TU_VERIFY(func_id < cfg.func_n_as_int && - _audiod_fct[func_id].p_desc != NULL); - return tu_fifo_read_n(&_audiod_fct[func_id].ep_out_ff, buffer, bufsize); - } - - bool tud_audio_n_clear_ep_out_ff(uint8_t func_id) { - TU_VERIFY(func_id < cfg.func_n_as_int && - _audiod_fct[func_id].p_desc != NULL); - return tu_fifo_clear(&_audiod_fct[func_id].ep_out_ff); - } - - tu_fifo_t *tud_audio_n_get_ep_out_ff(uint8_t func_id) { - if (func_id < cfg.func_n_as_int && _audiod_fct[func_id].p_desc != NULL) - return &_audiod_fct[func_id].ep_out_ff; - return NULL; - } - - //--------------------------------------------------------------------+ - // WRITE API - //--------------------------------------------------------------------+ - - /** - * \brief Write data to EP in buffer - * - * Write data to buffer. If it is full, new data can be inserted once a - * transmit was scheduled. See audiod_tx_done_cb(). If TX FIFOs are used, this - * function is not available in order to not let the user mess up the encoding - * process. - * - * \param[in] func_id: Index of audio function interface - * \param[in] data: Pointer to data array to be copied from - * \param[in] len: # of array elements to copy - * \return Number of bytes actually written - */ - uint16_t tud_audio_n_write(uint8_t func_id, const void *data, uint16_t len) { - TU_VERIFY(func_id < cfg.func_n_as_int && - _audiod_fct[func_id].p_desc != NULL); - return tu_fifo_write_n(&_audiod_fct[func_id].ep_in_ff, data, len); - } - - bool tud_audio_n_clear_ep_in_ff( - uint8_t func_id) // Delete all content in the EP IN FIFO - { - TU_VERIFY(func_id < cfg.func_n_as_int && - _audiod_fct[func_id].p_desc != NULL); - return tu_fifo_clear(&_audiod_fct[func_id].ep_in_ff); - } - - tu_fifo_t *tud_audio_n_get_ep_in_ff(uint8_t func_id) { - if (func_id < cfg.func_n_as_int && _audiod_fct[func_id].p_desc != NULL) - return &_audiod_fct[func_id].ep_in_ff; - return NULL; - } - - // If no interrupt transmit is pending bytes get written into buffer and a - // transmit is scheduled - once transmit completed tud_audio_int_done_cb() is - // called in inform user - bool tud_audio_int_n_write(uint8_t func_id, - const audio_interrupt_data_t *data) { - TU_VERIFY(func_id < cfg.func_n_as_int && - _audiod_fct[func_id].p_desc != NULL); - - TU_VERIFY(_audiod_fct[func_id].ep_int != 0); - - // We write directly into the EP's buffer - abort if previous transfer not - // complete - TU_VERIFY(usbd_edpt_claim(_audiod_fct[func_id].rhport, - _audiod_fct[func_id].ep_int)); - - // Check length - if (tu_memcpy_s(_audiod_fct[func_id].ep_int_buf, - sizeof(_audiod_fct[func_id].ep_int_buf), data, - sizeof(audio_interrupt_data_t)) == 0) { - // Schedule transmit - TU_ASSERT(usbd_edpt_xfer(_audiod_fct[func_id].rhport, - _audiod_fct[func_id].ep_int, - _audiod_fct[func_id].ep_int_buf, - sizeof(_audiod_fct[func_id].ep_int_buf)), - 0); - } else { - // Release endpoint since we don't make any transfer - usbd_edpt_release(_audiod_fct[func_id].rhport, - _audiod_fct[func_id].ep_int); - } - - return true; - } - - //--------------------------------------------------------------------+ - // USBD Driver API - //--------------------------------------------------------------------+ - void begin(USBAudioCB *cb, USBAudioConfig config) { - p_cb = cb; - cfg = config; - cfg.begin(); - } - - void audiod_init() { - if (p_cb == nullptr) return; - _audiod_fct.resize(cfg.func_n_as_int); - ctrl_buf_1.resize(cfg.func_ctl_buffer_size); - alt_setting_1.resize(cfg.func_n_as_int); - - if (cfg.is_ep_in()) { - if (cfg.enable_linear_buffer_rx) - lin_buf_in_1.resize(cfg.func_ep_in_size_max); - audio_ep_in_sw_buf_1.resize(cfg.func_ep_in_sw_buffer_size); - } - - if (cfg.is_ep_out()) { - if (cfg.enable_linear_buffer_tx) - audio_ep_out_sw_buf_1.resize(cfg.func_ep_out_sw_buffer_size); - lin_buf_out_1.resize(cfg.func_ep_out_sw_buffer_size); - } - - audiod_function_t *audio = &_audiod_fct[0]; - - audio->ctrl_buf = ctrl_buf_1.data(); - audio->ctrl_buf_sz = cfg.func_ctl_buffer_size; - audio->alt_setting = alt_setting_1.data(); - - // Initialize IN EP FIFO if required - if (cfg.is_ep_in()) { - tu_fifo_config(&audio->ep_in_ff, audio_ep_in_sw_buf_1.data(), - cfg.func_ep_in_sw_buffer_size, 1, true); - if (cfg.enable_fifo_mutex) - tu_fifo_config_mutex(&audio->ep_in_ff, - osal_mutex_create(&ep_in_ff_mutex_wr_1), NULL); - } - // cfg.is_ep_in() && !ENABLE_ENCODING - - // Initialize linear buffers - if (cfg.enable_linear_buffer_tx) audio->lin_buf_in = lin_buf_in_1.data(); - - // Initialize OUT EP FIFO if required - if (cfg.is_ep_out()) { - tu_fifo_config(&audio->ep_out_ff, audio_ep_out_sw_buf_1.data(), - cfg.func_ep_in_sw_buffer_size, 1, true); - if (cfg.enable_fifo_mutex) - tu_fifo_config_mutex(&audio->ep_out_ff, NULL, - osal_mutex_create(&ep_out_ff_mutex_rd_1)); - } - - // Initialize linear buffers - if (cfg.enable_linear_buffer_rx) audio->lin_buf_out = lin_buf_out_1.data(); - } - - bool audiod_deinit(void) { return true; } - - void audiod_reset(uint8_t rhport) { - (void)rhport; - - for (uint8_t i = 0; i < cfg.func_n_as_int; i++) { - audiod_function_t *audio = &_audiod_fct[i]; - tu_memclr(audio, sizeof(audiod_function_t)); - - if (cfg.is_ep_in()) tu_fifo_clear(&audio->ep_in_ff); - - if (cfg.is_ep_out()) tu_fifo_clear(&audio->ep_out_ff); - } - } - - uint16_t audiod_open(uint8_t rhport, tusb_desc_interface_t const *itf_desc, - uint16_t max_len) { - (void)max_len; - if (p_cb == nullptr) return 0; - - int cls_tobe = TUSB_CLASS_AUDIO; - int cls_is = itf_desc->bInterfaceClass; - - // TU_VERIFY(TUSB_CLASS_AUDIO == itf_desc->bInterfaceClass && - // AUDIO_SUBCLASS_CONTROL == itf_desc->bInterfaceSubClass); - - // // Verify version is correct - this check can be omitted - // TU_VERIFY(itf_desc->bInterfaceProtocol == AUDIO_INT_PROTOCOL_CODE_V2); - - // Verify interrupt control EP is enabled if demanded by descriptor - TU_ASSERT(itf_desc->bNumEndpoints <= 1); // 0 or 1 EPs are allowed - if (itf_desc->bNumEndpoints == 1) { - TU_ASSERT(cfg.enable_interrupt_ep); - } - - // Alternate setting MUST be zero - this check can be omitted - TU_VERIFY(itf_desc->bAlternateSetting == 0); - - // Find available audio driver interface - uint8_t i; - for (i = 0; i < cfg.func_n_as_int; i++) { - if (!_audiod_fct[i].p_desc) { - int len = p_cb->getInterfaceDescriptor(i, nullptr, 0); - _audiod_fct[i].desc_length = len; - descriptor.resize(len); - _audiod_fct[i].p_desc = descriptor.data(); - p_cb->getInterfaceDescriptor(i, descriptor.data(), len); - _audiod_fct[i].rhport = rhport; - -#ifdef TUP_DCD_EDPT_ISO_ALLOC - { - uint8_t ep_in = 0; - uint16_t ep_in_size = 0; - - uint8_t ep_out = 0; - uint16_t ep_out_size = 0; - - uint8_t ep_fb = 0; - uint8_t const *p_desc = _audiod_fct[i].p_desc; - uint8_t const *p_desc_end = - p_desc + _audiod_fct[i].desc_length - TUD_AUDIO_DESC_IAD_LEN; - // Condition modified from p_desc < p_desc_end to prevent gcc>=12 - // strict-overflow warning - while (p_desc_end - p_desc > 0) { - if (tu_desc_type(p_desc) == TUSB_DESC_ENDPOINT) { - tusb_desc_endpoint_t const *desc_ep = - (tusb_desc_endpoint_t const *)p_desc; - if (desc_ep->bmAttributes.xfer == TUSB_XFER_ISOCHRONOUS) { - if (cfg.enable_feedback_ep) { - // Explicit feedback EP - if (desc_ep->bmAttributes.usage == 1) { - ep_fb = desc_ep->bEndpointAddress; - } - } - // Data EP - if (desc_ep->bmAttributes.usage == 0) { - if (tu_edpt_dir(desc_ep->bEndpointAddress) == TUSB_DIR_IN) { - if (cfg.is_ep_in()) { - ep_in = desc_ep->bEndpointAddress; - ep_in_size = - TU_MAX(tu_edpt_packet_size(desc_ep), ep_in_size); - } - } else { - if (cfg.is_ep_out()) { - ep_out = desc_ep->bEndpointAddress; - ep_out_size = - TU_MAX(tu_edpt_packet_size(desc_ep), ep_out_size); - } - } - } - } - } - - p_desc = tu_desc_next(p_desc); - } - - if (cfg.is_ep_in() && ep_in) { - usbd_edpt_iso_alloc(rhport, ep_in, ep_in_size); - } - - - if (cfg.is_ep_out() && ep_out) { - usbd_edpt_iso_alloc(rhport, ep_out, ep_out_size); - } - - if (cfg.enable_feedback_ep) { - if (ep_fb) { - usbd_edpt_iso_alloc(rhport, ep_fb, 4); - } - } - } - -#endif // TUP_DCD_EDPT_ISO_ALLOC - - if (cfg.is_ep_in() && cfg.enable_ep_in_flow_control) { - uint8_t const *p_desc = _audiod_fct[i].p_desc; - uint8_t const *p_desc_end = - p_desc + _audiod_fct[i].desc_length - TUD_AUDIO_DESC_IAD_LEN; - // Condition modified from p_desc < p_desc_end to prevent gcc>=12 - // strict-overflow warning - while (p_desc_end - p_desc > 0) { - if (tu_desc_type(p_desc) == TUSB_DESC_ENDPOINT) { - tusb_desc_endpoint_t const *desc_ep = - (tusb_desc_endpoint_t const *)p_desc; - if (desc_ep->bmAttributes.xfer == TUSB_XFER_ISOCHRONOUS) { - if (desc_ep->bmAttributes.usage == 0) { - if (tu_edpt_dir(desc_ep->bEndpointAddress) == TUSB_DIR_IN) { - _audiod_fct[i].interval_tx = desc_ep->bInterval; - } - } - } - } else if (tu_desc_type(p_desc) == TUSB_DESC_CS_INTERFACE && - tu_desc_subtype(p_desc) == - AUDIO_CS_AC_INTERFACE_OUTPUT_TERMINAL) { - if (tu_unaligned_read16(p_desc + 4) == - AUDIO_TERM_TYPE_USB_STREAMING) { - _audiod_fct[i].bclock_id_tx = p_desc[8]; - } - } - p_desc = tu_desc_next(p_desc); - } - } // CFG_TUD_AUDIO_EP_IN_FLOW_CONTROL - - if (cfg.enable_interrupt_ep) { - uint8_t const *p_desc = _audiod_fct[i].p_desc; - uint8_t const *p_desc_end = - p_desc + _audiod_fct[i].desc_length - TUD_AUDIO_DESC_IAD_LEN; - // Condition modified from p_desc < p_desc_end to prevent gcc>=12 - // strict-overflow warning - while (p_desc_end - p_desc > 0) { - // For each endpoint - if (tu_desc_type(p_desc) == TUSB_DESC_ENDPOINT) { - tusb_desc_endpoint_t const *desc_ep = - (tusb_desc_endpoint_t const *)p_desc; - uint8_t const ep_addr = desc_ep->bEndpointAddress; - // If endpoint is input-direction and interrupt-type - if (tu_edpt_dir(ep_addr) == TUSB_DIR_IN && - desc_ep->bmAttributes.xfer == TUSB_XFER_INTERRUPT) { - // Store endpoint number and open endpoint - _audiod_fct[i].ep_int = ep_addr; - TU_ASSERT(usbd_edpt_open(_audiod_fct[i].rhport, desc_ep)); - } - } - p_desc = tu_desc_next(p_desc); - } - } - - _audiod_fct[i].mounted = true; - break; - } - } - - // Verify we found a free one - TU_ASSERT(i < cfg.func_n_as_int); - - // This is all we need so far - the EPs are setup by a later set_interface - // request (as per UAC2 specification) - uint16_t drv_len = - _audiod_fct[i].desc_length - - TUD_AUDIO_DESC_IAD_LEN; // - TUD_AUDIO_DESC_IAD_LEN since tinyUSB - // already handles the IAD descriptor - - return drv_len; - } - - // Handle class co - bool audiod_control_xfer_cb(uint8_t rhport, uint8_t stage, - tusb_control_request_t const *request) { - if (stage == CONTROL_STAGE_SETUP) { - return audiod_control_request(rhport, request); - } else if (stage == CONTROL_STAGE_DATA) { - return audiod_control_complete(rhport, request); - } - - return true; - } - - bool audiod_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t result, - uint32_t xferred_bytes) { - (void)result; - (void)xferred_bytes; - - // Search for interface belonging to given end point address and proceed - // as required - for (uint8_t func_id = 0; func_id < cfg.func_n_as_int; func_id++) { - audiod_function_t *audio = &_audiod_fct[func_id]; - - if (cfg.enable_interrupt_ep) { - // Data transmission of control interrupt finished - if (audio->ep_int == ep_addr) { - // According to USB2 specification, maximum payload of interrupt EP - // is 8 bytes on low speed, 64 bytes on full speed, and 1024 bytes - // on high speed (but only if an alternate interface other than 0 is - // used - // - see specification p. 49) In case there is nothing to send we - // have to return a NAK - this is taken care of by PHY ??? In case - // of an erroneous transmission a retransmission is conducted - this - // is taken care of by PHY ??? - - // I assume here, that things above are handled by PHY - // All transmission is done - what remains to do is to inform job - // was completed - - if (p_cb) p_cb->int_done_cb(rhport); - return true; - } - } - if (cfg.is_ep_in()) { - // Data transmission of audio packet finished - if (audio->ep_in == ep_addr && audio->alt_setting != 0) { - // USB 2.0, section 5.6.4, third paragraph, states "An isochronous - // endpoint must specify its required bus access period. However, an - // isochronous endpoint must be prepared to handle poll rates faster - // than the one specified." That paragraph goes on to say "An - // isochronous IN endpoint must return a zero-length packet whenever - // data is requested at a faster interval than the specified - // interval and data is not available." This can only be solved - // reliably if we load a ZLP after every IN transmission since we - // can not say if the host requests samples earlier than we - // declared! Once all samples are collected we overwrite the loaded - // ZLP. - - // Check if there is data to load into EPs buffer - if not load it - // with ZLP Be aware - we as a device are not able to know if the - // host polls for data with a faster rate as we stated this in the - // descriptors. Therefore we always have to put something into the - // EPs buffer. However, once we did that, there is no way of - // aborting this or replacing what we put into the buffer before! - // This is the only place where we can fill something into the EPs - // buffer! - - // Load new data - TU_VERIFY(audiod_tx_done_cb(rhport, audio)); - - // Transmission of ZLP is done by audiod_tx_done_cb() - return true; - } - } - - if (cfg.is_ep_out()) { - // New audio packet received - if (audio->ep_out == ep_addr) { - TU_VERIFY(audiod_rx_done_cb(rhport, audio, (uint16_t)xferred_bytes)); - return true; - } - - if (cfg.enable_feedback_ep) { - // Transmission of feedback EP finished - if (audio->ep_fb == ep_addr) { - if (p_cb) p_cb->fb_done_cb(func_id); - - // Schedule a transmit with the new value if EP is not busy - if (!usbd_edpt_busy(rhport, audio->ep_fb)) { - // Schedule next transmission - value is changed - // bytud_audio_n_fb_set() in the meantime or the old value gets - // sent - return audiod_fb_send(rhport, audio); - } - } - } - } - } - - return false; - } - - bool tud_audio_buffer_and_schedule_control_xfer( - uint8_t rhport, tusb_control_request_t const *p_request, void *data, - uint16_t len) { - // Handles only sending of data not receiving - if (p_request->bmRequestType_bit.direction == TUSB_DIR_OUT) return false; - - // Get corresponding driver index - uint8_t func_id; - uint8_t itf = TU_U16_LOW(p_request->wIndex); - - // Conduct checks which depend on the recipient - switch (p_request->bmRequestType_bit.recipient) { - case TUSB_REQ_RCPT_INTERFACE: { - uint8_t entityID = TU_U16_HIGH(p_request->wIndex); - - // Verify if entity is present - if (entityID != 0) { - // Find index of audio driver structure and verify entity really - // exists - TU_VERIFY(audiod_verify_entity_exists(itf, entityID, &func_id)); - } else { - // Find index of audio driver structure and verify interface really - // exists - TU_VERIFY(audiod_verify_itf_exists(itf, &func_id)); - } - } break; - - case TUSB_REQ_RCPT_ENDPOINT: { - uint8_t ep = TU_U16_LOW(p_request->wIndex); - - // Find index of audio driver structure and verify EP really exists - TU_VERIFY(audiod_verify_ep_exists(ep, &func_id)); - } break; - - // Unknown/Unsupported recipient - default: - TU_LOG2(" Unsupported recipient: %d\r\n", - p_request->bmRequestType_bit.recipient); - TU_BREAKPOINT(); - return false; - } - - // Crop length - if (len > _audiod_fct[func_id].ctrl_buf_sz) - len = _audiod_fct[func_id].ctrl_buf_sz; - - // Copy into buffer - TU_VERIFY(0 == tu_memcpy_s(_audiod_fct[func_id].ctrl_buf, - _audiod_fct[func_id].ctrl_buf_sz, data, - (size_t)len)); - - if (cfg.is_ep_in() && cfg.enable_ep_in_flow_control) { - // Find data for sampling_frequency_control - if (p_request->bmRequestType_bit.type == TUSB_REQ_TYPE_CLASS && - p_request->bmRequestType_bit.recipient == TUSB_REQ_RCPT_INTERFACE) { - uint8_t entityID = TU_U16_HIGH(p_request->wIndex); - uint8_t ctrlSel = TU_U16_HIGH(p_request->wValue); - if (_audiod_fct[func_id].bclock_id_tx == entityID && - ctrlSel == AUDIO_CS_CTRL_SAM_FREQ && - p_request->bRequest == AUDIO_CS_REQ_CUR) { - _audiod_fct[func_id].sample_rate_tx = - tu_unaligned_read32(_audiod_fct[func_id].ctrl_buf); - } - } - } - - // Schedule transmit - return tud_control_xfer(rhport, p_request, - (void *)_audiod_fct[func_id].ctrl_buf, len); - } - - bool tud_audio_n_fb_set(uint8_t func_id, uint32_t feedback) { - TU_VERIFY(func_id < cfg.func_n_as_int && - _audiod_fct[func_id].p_desc != NULL); - - // Format the feedback value - if (cfg.enable_feedback_forward_correction) { - if (TUSB_SPEED_FULL == tud_speed_get()) { - uint8_t *fb = (uint8_t *)&_audiod_fct[func_id].feedback.value; - - // For FS format is 10.14 - *(fb++) = (feedback >> 2) & 0xFF; - *(fb++) = (feedback >> 10) & 0xFF; - *(fb++) = (feedback >> 18) & 0xFF; - // 4th byte is needed to work correctly with MS Windows - *fb = 0; - } - } else { - // Send value as-is, caller will choose the appropriate format - _audiod_fct[func_id].feedback.value = feedback; - } - // Schedule a transmit with the new value if EP is not busy - this - // triggers repetitive scheduling of the feedback value - if (!usbd_edpt_busy(_audiod_fct[func_id].rhport, - _audiod_fct[func_id].ep_fb)) { - return audiod_fb_send(_audiod_fct[func_id].rhport, &_audiod_fct[func_id]); - } - - return true; - } - - void audiod_sof_isr(uint8_t rhport, uint32_t frame_count) { - (void)rhport; - (void)frame_count; - - if (cfg.is_ep_out() && cfg.enable_feedback_ep) { - // Determine feedback value - The feedback method is described - // in 5.12.4.2 of the USB 2.0 spec Boiled down, the feedback value Ff = - // n_samples / (micro)frame. Since an accuracy of less than 1 Sample / - // second is desired, at least n_frames = ceil(2^K * f_s / f_m) frames - // need to be measured, where K = 10 for full speed and K = 13 for high - // speed, f_s is the sampling frequency e.g. 48 kHz and f_m is the cpu - // clock frequency e.g. 100 MHz (or any other master clock whose clock - // count is available and locked to f_s) The update interval in the - // (4.10.2.1) Feedback Endpoint Descriptor must be less or equal to 2^(K - // - P), where P = min( ceil(log2(f_m / f_s)), K) feedback = n_cycles / - // n_frames * f_s / f_m in 16.16 format, where n_cycles are the number - // of main clock cycles within fb_n_frames - - // Iterate over audio functions and set feedback value - for (uint8_t i = 0; i < cfg.func_n_as_int; i++) { - audiod_function_t *audio = &_audiod_fct[i]; - - if (audio->ep_fb != 0) { - // HS shift need to be adjusted since SOF event is generated for - // frame only - uint8_t const hs_adjust = - (TUSB_SPEED_HIGH == tud_speed_get()) ? 3 : 0; - uint32_t const interval = - 1UL << (audio->feedback.frame_shift - hs_adjust); - if (0 == (frame_count & (interval - 1))) { - if (cfg.enable_feedback_interval_isr && p_cb) - p_cb->feedback_interval_isr(i, frame_count, - audio->feedback.frame_shift); - } - } - } - } // cfg.is_ep_out() && cfg.enable_feedback_ep - } - - USBAudioConfig &config() { return cfg; } - - protected: - USBAudioCB *p_cb = nullptr; - USBAudioConfig cfg; - // Linear buffer TX in case: - // - target MCU is not capable of handling a ring buffer FIFO e.g. no - // hardware buffer is available or driver is would need to be changed - // dramatically OR - // - the software encoding is used - in this case the linear buffers serve - // as a target memory where logical channels are encoded into - std::vector - lin_buf_in_1; //[cfg.func_ep_in_size_max]; - - std::vector - audio_ep_in_sw_buf_1; //[cfg.func_ep_in_sw_buffer_size]; - - std::vector - audio_ep_out_sw_buf_1; //[cfg.func_ep_in_sw_buffer_size]; - - // Linear buffer RX in case: - // - target MCU is not capable of handling a ring buffer FIFO e.g. no - // hardware buffer is available or driver is would need to be changed - // dramatically OR - // - the software encoding is used - in this case the linear buffers serve - // as a target memory where logical channels are encoded into - std::vector - lin_buf_out_1; //[FUNC_1_EP_OUT_SZ_MAX]; - - // Control buffers - std::vector - ctrl_buf_1; //[cfg.func_ctl_buffer_size]; - - // Active alternate setting of interfaces - std::vector alt_setting_1; //[cfg.func_n_as_int]; - - // buffer for descriptor - std::vector descriptor; - - // EP IN software buffers and mutexes - // TUP_DCD_EDPT_ISO_ALLOC[cfg.func_ep_in_sw_buffer_size]; - osal_mutex_def_t ep_in_ff_mutex_wr_1; // No need for read mutex as only USB - // driver reads from FIFO - - osal_mutex_def_t ep_out_ff_mutex_rd_1; // No need for write mutex as only - // USB driver writes into FIFO - - struct audiod_function_t { - audiod_function_t() { - memset(this,0, sizeof(audiod_function_t)); - } - uint8_t n_bytes_per_sample_tx; - uint8_t n_channels_tx; - uint8_t format_type_tx = AUDIO_FORMAT_TYPE_I; - - uint8_t rhport; - uint8_t const - *p_desc = nullptr; // Pointer pointing to Standard AC Interface - // Descriptor(4.7.1) - // - Audio Control descriptor defining audio function - - uint8_t ep_in; // TX audio data EP. - uint16_t ep_in_sz; // Current size of TX EP - uint8_t - ep_in_as_intf_num; // Corresponding Standard AS Interface Descriptor - // (4.9.1) belonging to output terminal to which - // this EP belongs - 0 is invalid (this fits to - // UAC2 specification since AS interfaces can not - // have interface number equal to zero) - uint8_t ep_out; // Incoming (into uC) audio data EP. - uint16_t ep_out_sz; // Current size of RX EP - uint8_t - ep_out_as_intf_num; // Corresponding Standard AS Interface Descriptor - // (4.9.1) belonging to input terminal to which - // this EP belongs - 0 is invalid (this fits to - // UAC2 specification since AS interfaces can not - // have interface number equal to zero) - - uint8_t ep_fb; // Feedback EP. - - uint8_t ep_int; // Audio control interrupt EP. - - bool mounted; // Device opened - - /*------------- From this point, data is not cleared by bus reset - * -------------*/ - - uint16_t desc_length; // Length of audio function descriptor - - struct feedback { - CFG_TUSB_MEM_ALIGN uint32_t - value; // Feedback value for asynchronous mode (in 16.16 format). - uint32_t - min_value; // min value according to UAC2 FMT-2.0 section 2.3.1.1. - uint32_t - max_value; // max value according to UAC2 FMT-2.0 section 2.3.1.1. - - uint8_t - frame_shift; // bInterval-1 in unit of frame (FS), micro-frame (HS) - uint8_t compute_method; - - union { - uint8_t power_of_2; // pre-computed power of 2 shift - float float_const; // pre-computed float constant - - struct { - uint32_t sample_freq; - uint32_t mclk_freq; - } fixed; - - } compute; - - } feedback; - - // Decoding parameters - parameters are set when alternate AS interface is - // set by host Coding is currently only supported for EP. Software coding - // corresponding to AS interfaces without EPs are not supported currently. - uint32_t sample_rate_tx; - uint16_t packet_sz_tx[3]; - uint8_t bclock_id_tx; - uint8_t interval_tx; - - // Encoding parameters - parameters are set when alternate AS interface is - // set by host - - // Buffer for control requests - uint8_t *ctrl_buf; - uint8_t ctrl_buf_sz; - - // Current active alternate settings - uint8_t *alt_setting; // We need to save the current alternate setting - // this way, because it is possible that there are - // AS interfaces which do not have an EP! - - // EP Transfer buffers and FIFOs - tu_fifo_t ep_out_ff; - tu_fifo_t ep_in_ff; - - // Audio control interrupt buffer - no FIFO - 6 Bytes according to UAC 2 - // specification (p. 74) - CFG_TUSB_MEM_ALIGN uint8_t ep_int_buf[6]; - - // Linear buffer in case target MCU is not capable of handling a ring - // buffer FIFO e.g. no hardware buffer is available or driver is would - // need to be changed dramatically OR the support FIFOs are used - uint8_t *lin_buf_out; - uint8_t *lin_buf_in; - }; - - //--------------------------------------------------------------------+ - // INTERNAL OBJECT & FUNCTION DECLARATION - //--------------------------------------------------------------------+ - std::vector _audiod_fct; - - // No security checks here - internal function only which should always - // succeed - uint8_t audiod_get_audio_fct_idx(audiod_function_t *audio) { - for (uint8_t cnt = 0; cnt < cfg.func_n_as_int; cnt++) { - if (&_audiod_fct[cnt] == audio) return cnt; - } - return 0; - } - - inline uint8_t tu_desc_subtype(void const *desc) { - return ((uint8_t const *)desc)[2]; - } - - bool audiod_rx_done_cb(uint8_t rhport, audiod_function_t *audio, - uint16_t n_bytes_received) { - uint8_t idxItf = 0; - uint8_t const *dummy2; - uint8_t idx_audio_fct = 0; - - if (p_cb) { - idx_audio_fct = audiod_get_audio_fct_idx(audio); - TU_VERIFY(audiod_get_AS_interface_index(audio->ep_out_as_intf_num, audio, - &idxItf, &dummy2)); - } - - // Call a weak callback here - a possibility for user to get informed an - // audio packet was received and data gets now loaded into EP FIFO (or - // decoded into support RX software FIFO) - if (p_cb) { - TU_VERIFY(p_cb->rx_done_pre_read_cb(rhport, n_bytes_received, - idx_audio_fct, audio->ep_out, - audio->alt_setting[idxItf])); - } - - if (cfg.enable_linear_buffer_rx) { - // Data currently is in linear buffer, copy into EP OUT FIFO - TU_VERIFY(tu_fifo_write_n(&audio->ep_out_ff, audio->lin_buf_out, - n_bytes_received)); - - // Schedule for next receive - TU_VERIFY(usbd_edpt_xfer(rhport, audio->ep_out, audio->lin_buf_out, - audio->ep_out_sz), - false); - } else { - // Data is already placed in EP FIFO, schedule for next receive - TU_VERIFY(usbd_edpt_xfer_fifo(rhport, audio->ep_out, &audio->ep_out_ff, - audio->ep_out_sz), - false); - } - - // Call a weak callback here - a possibility for user to get informed - // decoding was completed - if (p_cb) { - TU_VERIFY(p_cb->rx_done_post_read_cb(rhport, n_bytes_received, - idx_audio_fct, audio->ep_out, - audio->alt_setting[idxItf])); - } - - return true; - } - - // This function is called once a transmit of an audio packet was - // successfully completed. Here, we encode samples and place it in IN EP's - // buffer for next transmission. If you prefer your own (more efficient) - // implementation suiting your purpose set ENABLE_ENCODING = 0 and use - // tud_audio_n_write. - - // n_bytes_copied - Informs caller how many bytes were loaded. In case - // n_bytes_copied = 0, a ZLP is scheduled to inform host no data is - // available for current frame. - - bool audiod_tx_done_cb(uint8_t rhport, audiod_function_t *audio) { - uint8_t idxItf; - uint8_t const *dummy2; - - uint8_t idx_audio_fct = audiod_get_audio_fct_idx(audio); - TU_VERIFY(audiod_get_AS_interface_index(audio->ep_in_as_intf_num, audio, - &idxItf, &dummy2)); - - // Only send something if current alternate interface is not 0 as in this - // case nothing is to be sent due to UAC2 specifications - if (audio->alt_setting[idxItf] == 0) return false; - - // Call a weak callback here - a possibility for user to get informed - // former TX was completed and data gets now loaded into EP in buffer (in - // case FIFOs are used) or if no FIFOs are used the user may use this call - // back to load its data into the EP IN buffer by use of - // tud_audio_n_write_ep_in_buffer(). - if (p_cb) - TU_VERIFY(p_cb->tx_done_pre_load_cb(rhport, idx_audio_fct, audio->ep_in, - audio->alt_setting[idxItf])); - - // Send everything in ISO EP FIFO - uint16_t n_bytes_tx; - - // If support FIFOs are used, encode and schedule transmit - // No support FIFOs, if no linear buffer required schedule transmit, else - // put data into linear buffer and schedule - if (cfg.enable_ep_in_flow_control) { - // packet_sz_tx is based on total packet size, here we want size for - // each support buffer. - n_bytes_tx = audiod_tx_packet_size( - audio->packet_sz_tx, tu_fifo_count(&audio->ep_in_ff), - audio->ep_in_ff.depth, audio->ep_in_sz); - } else { - n_bytes_tx = tu_min16(tu_fifo_count(&audio->ep_in_ff), - audio->ep_in_sz); // Limit up to max packet size, - // more can not be done for ISO - } - if (cfg.enable_linear_buffer_tx) { - tu_fifo_read_n(&audio->ep_in_ff, audio->lin_buf_in, n_bytes_tx); - TU_VERIFY( - usbd_edpt_xfer(rhport, audio->ep_in, audio->lin_buf_in, n_bytes_tx)); - } else { - // Send everything in ISO EP FIFO - TU_VERIFY(usbd_edpt_xfer_fifo(rhport, audio->ep_in, &audio->ep_in_ff, - n_bytes_tx)); - } - - // Call a weak callback here - a possibility for user to get informed - // former TX was completed and how many bytes were loaded for the next - // frame - if (p_cb) - TU_VERIFY(p_cb->tx_done_post_load_cb(rhport, n_bytes_tx, idx_audio_fct, - audio->ep_in, - audio->alt_setting[idxItf])); - return true; - } - - // This function is called once a transmit of a feedback packet was - // successfully completed. Here, we get the next feedback value to be sent - - inline bool audiod_fb_send(uint8_t rhport, audiod_function_t *audio) { - return usbd_edpt_xfer(rhport, audio->ep_fb, - (uint8_t *)&audio->feedback.value, 4); - } - - bool audiod_set_interface(uint8_t rhport, - tusb_control_request_t const *p_request) { - (void)rhport; - - // Here we need to do the following: - - // 1. Find the audio driver assigned to the given interface to be set - // Since one audio driver interface has to be able to cover an unknown - // number of interfaces (AC, AS + its alternate settings), the best memory - // efficient way to solve this is to always search through the - // descriptors. The audio driver is mapped to an audio function by a - // reference pointer to the corresponding AC interface of this audio - // function which serves as a starting point for searching - - // 2. Close EPs which are currently open - // To do so it is not necessary to know the current active alternate - // interface since we already save the current EP addresses - we simply - // close them - - // 3. Open new EP - - uint8_t const itf = tu_u16_low(p_request->wIndex); - uint8_t const alt = tu_u16_low(p_request->wValue); - - TU_LOG2(" Set itf: %u - alt: %u\r\n", itf, alt); - - // Find index of audio streaming interface and index of interface - uint8_t func_id, idxItf; - uint8_t const *p_desc; - TU_VERIFY( - audiod_get_AS_interface_index_global(itf, &func_id, &idxItf, &p_desc)); - - audiod_function_t *audio = &_audiod_fct[func_id]; - - // Look if there is an EP to be closed - for this driver, there are only 3 - // possible EPs which may be closed (only AS related EPs can be closed, AC - // EP (if present) is always open) - if (cfg.is_ep_in()) { - if (audio->ep_in_as_intf_num == itf) { - audio->ep_in_as_intf_num = 0; -#ifndef TUP_DCD_EDPT_ISO_ALLOC - usbd_edpt_close(rhport, audio->ep_in); -#endif - - // Clear FIFOs, since data is no longer valid - tu_fifo_clear(&audio->ep_in_ff); - - // Invoke callback - can be used to stop data sampling - if (p_cb) TU_VERIFY(p_cb->set_itf_close_EP_cb(rhport, p_request)); - - audio->ep_in = 0; // Necessary? - - if (cfg.enable_ep_in_flow_control) { - audio->packet_sz_tx[0] = 0; - audio->packet_sz_tx[1] = 0; - audio->packet_sz_tx[2] = 0; - } - } - } - - if (cfg.is_ep_out()) { - if (audio->ep_out_as_intf_num == itf) { - audio->ep_out_as_intf_num = 0; -#ifndef TUP_DCD_EDPT_ISO_ALLOC - usbd_edpt_close(rhport, audio->ep_out); -#endif - - // Clear FIFOs, since data is no longer valid - tu_fifo_clear(&audio->ep_out_ff); - // Invoke callback - can be used to stop data sampling - if (p_cb) TU_VERIFY(p_cb->set_itf_close_EP_cb(rhport, p_request)); - - audio->ep_out = 0; // Necessary? - - // Close corresponding feedback EP - if (cfg.enable_feedback_ep) { -#ifndef TUP_DCD_EDPT_ISO_ALLOC - usbd_edpt_close(rhport, audio->ep_fb); -#endif - audio->ep_fb = 0; - tu_memclr(&audio->feedback, sizeof(audio->feedback)); - } - } - } // cfg.is_ep_out() - - // Save current alternative interface setting - audio->alt_setting[idxItf] = alt; - - // Open new EP if necessary - EPs are only to be closed or opened for AS - // interfaces - Look for AS interface with correct alternate interface Get - // pointer at end - uint8_t const *p_desc_end = - audio->p_desc + audio->desc_length - TUD_AUDIO_DESC_IAD_LEN; - - // p_desc starts at required interface with alternate setting zero - // Condition modified from p_desc < p_desc_end to prevent gcc>=12 - // strict-overflow warning - while (p_desc_end - p_desc > 0) { - // Find correct interface - if (tu_desc_type(p_desc) == TUSB_DESC_INTERFACE && - ((tusb_desc_interface_t const *)p_desc)->bInterfaceNumber == itf && - ((tusb_desc_interface_t const *)p_desc)->bAlternateSetting == alt) { - uint8_t const *p_desc_parse_for_params = p_desc; - // From this point forward follow the EP descriptors associated to the - // current alternate setting interface - Open EPs if necessary - uint8_t foundEPs = 0, - nEps = ((tusb_desc_interface_t const *)p_desc)->bNumEndpoints; - // Condition modified from p_desc < p_desc_end to prevent gcc>=12 - // strict-overflow warning - while (foundEPs < nEps && (p_desc_end - p_desc > 0)) { - if (tu_desc_type(p_desc) == TUSB_DESC_ENDPOINT) { - tusb_desc_endpoint_t const *desc_ep = - (tusb_desc_endpoint_t const *)p_desc; -#ifdef TUP_DCD_EDPT_ISO_ALLOC - TU_ASSERT(usbd_edpt_iso_activate(rhport, desc_ep)); -#else - TU_ASSERT(usbd_edpt_open(rhport, desc_ep)); -#endif - uint8_t const ep_addr = desc_ep->bEndpointAddress; - - // TODO: We need to set EP non busy since this is not taken care - // of right now in ep_close() - THIS IS A WORKAROUND! - usbd_edpt_clear_stall(rhport, ep_addr); - - if (cfg.is_ep_in()) { - if (tu_edpt_dir(ep_addr) == TUSB_DIR_IN && - desc_ep->bmAttributes.usage == - 0x00) // Check if usage is data EP - { - // Save address - audio->ep_in = ep_addr; - audio->ep_in_as_intf_num = itf; - audio->ep_in_sz = tu_edpt_packet_size(desc_ep); - - // If software encoding is enabled, parse for the - // corresponding parameters - doing this here means only AS - // interfaces with EPs get scanned for parameters - if (cfg.enable_ep_in_flow_control) - audiod_parse_for_AS_params(audio, p_desc_parse_for_params, - p_desc_end, itf); - - // Reconfigure size of support FIFOs - this is necessary to - // avoid samples to get split in case of a wrap - - // Schedule first transmit if alternate interface is not zero - // i.e. streaming is disabled - in case no sample data is - // available a ZLP is loaded It is necessary to trigger this - // here since the refill is done with an RX FIFO empty - // interrupt which can only trigger if something was in there - TU_VERIFY(audiod_tx_done_cb(rhport, &_audiod_fct[func_id])); - } - } // cfg.is_ep_in() - - if (cfg.is_ep_out()) { - if (tu_edpt_dir(ep_addr) == - TUSB_DIR_OUT) // Checking usage not necessary - { - // Save address - audio->ep_out = ep_addr; - audio->ep_out_as_intf_num = itf; - audio->ep_out_sz = tu_edpt_packet_size(desc_ep); - - // Prepare for incoming data - if (cfg.enable_linear_buffer_rx) { - TU_VERIFY( - usbd_edpt_xfer(rhport, audio->ep_out, audio->lin_buf_out, - audio->ep_out_sz), - false); - } else { - TU_VERIFY( - usbd_edpt_xfer_fifo(rhport, audio->ep_out, - &audio->ep_out_ff, audio->ep_out_sz), - false); - } - } - - if (cfg.enable_feedback_ep) { - if (tu_edpt_dir(ep_addr) == TUSB_DIR_IN && - desc_ep->bmAttributes.usage == - 1) // Check if usage is explicit data feedback - { - audio->ep_fb = ep_addr; - audio->feedback.frame_shift = desc_ep->bInterval - 1; - - // Enable SOF interrupt if callback is implemented - if (cfg.enable_feedback_interval_isr) - usbd_sof_enable(rhport, SOF_CONSUMER_AUDIO, true); - } - } - } // cfg.is_ep_out() - - foundEPs += 1; - } - p_desc = tu_desc_next(p_desc); - } - - TU_VERIFY(foundEPs == nEps); - - // Invoke one callback for a final set interface - if (p_cb) TU_VERIFY(p_cb->set_itf_cb(rhport, p_request)); - - if (cfg.enable_feedback_ep) { - // Prepare feedback computation if callback is available - if (p_cb) { - audio_feedback_params_t fb_param; - - p_cb->feedback_params_cb(func_id, alt, &fb_param); - audio->feedback.compute_method = fb_param.method; - - // Minimal/Maximum value in 16.16 format for full speed (1ms per - // frame) or high speed (125 us per frame) - uint32_t const frame_div = - (TUSB_SPEED_FULL == tud_speed_get()) ? 1000 : 8000; - audio->feedback.min_value = (fb_param.sample_freq / frame_div - 1) - << 16; - audio->feedback.max_value = (fb_param.sample_freq / frame_div + 1) - << 16; - - switch (fb_param.method) { - case AUDIO_FEEDBACK_METHOD_FREQUENCY_FIXED: - case AUDIO_FEEDBACK_METHOD_FREQUENCY_FLOAT: - case AUDIO_FEEDBACK_METHOD_FREQUENCY_POWER_OF_2: - set_fb_params_freq(audio, fb_param.sample_freq, - fb_param.frequency.mclk_freq); - break; - - // nothing to do - default: - break; - } - } - } // cfg.enable_feedback_ep - - // We are done - abort loop - break; - } - - // Moving forward - p_desc = tu_desc_next(p_desc); - } - - if (cfg.enable_feedback_ep) { - // Disable SOF interrupt if no driver has any enabled feedback EP - bool disable = true; - for (uint8_t i = 0; i < cfg.func_n_as_int; i++) { - if (_audiod_fct[i].ep_fb != 0) { - disable = false; - break; - } - } - if (disable) usbd_sof_enable(rhport, SOF_CONSUMER_AUDIO, false); - } - - if (cfg.is_ep_in() && cfg.enable_ep_in_flow_control) - audiod_calc_tx_packet_sz(audio); - - tud_control_status(rhport, p_request); - - return true; - } - - // Invoked when class request DATA stage is finished. - // return false to stall control EP (e.g Host send non-sense DATA) - bool audiod_control_complete(uint8_t rhport, - tusb_control_request_t const *p_request) { - // Handle audio class specific set requests - if (p_request->bmRequestType_bit.type == TUSB_REQ_TYPE_CLASS && - p_request->bmRequestType_bit.direction == TUSB_DIR_OUT) { - uint8_t func_id; - - switch (p_request->bmRequestType_bit.recipient) { - case TUSB_REQ_RCPT_INTERFACE: { - uint8_t itf = TU_U16_LOW(p_request->wIndex); - uint8_t entityID = TU_U16_HIGH(p_request->wIndex); - - if (entityID != 0) { - if (p_cb) { - // Check if entity is present and get corresponding driver index - TU_VERIFY(audiod_verify_entity_exists(itf, entityID, &func_id)); - - // Invoke callback - return p_cb->set_req_entity_cb(rhport, p_request, - _audiod_fct[func_id].ctrl_buf); - } else { - TU_LOG2(" No entity set request callback available!\r\n"); - return false; // In case no callback function is present or - // request can not be conducted we stall it - } - } else { - if (p_cb) { - // Find index of audio driver structure and verify interface - // really exists - TU_VERIFY(audiod_verify_itf_exists(itf, &func_id)); - - // Invoke callback - return p_cb->set_req_itf_cb(rhport, p_request, - _audiod_fct[func_id].ctrl_buf); - } else { - TU_LOG2(" No interface set request callback available!\r\n"); - return false; // In case no callback function is present or - // request can not be conducted we stall it - } - } - } break; - - case TUSB_REQ_RCPT_ENDPOINT: { - uint8_t ep = TU_U16_LOW(p_request->wIndex); - - if (p_cb) { - // Check if entity is present and get corresponding driver index - TU_VERIFY(audiod_verify_ep_exists(ep, &func_id)); - - // Invoke callback - return p_cb->set_req_ep_cb(rhport, p_request, - _audiod_fct[func_id].ctrl_buf); - } else { - TU_LOG2(" No EP set request callback available!\r\n"); - return false; // In case no callback function is present or - // request can not be conducted we stall it - } - } break; - // Unknown/Unsupported recipient - default: - TU_BREAKPOINT(); - return false; - } - } - return true; - } - - // return false to stall control endpoint (e.g unsupported request) - bool audiod_control_request(uint8_t rhport, - tusb_control_request_t const *p_request) { - (void)rhport; - - // Handle standard requests - standard set requests usually have no data - // stage so we also handle set requests here - if (p_request->bmRequestType_bit.type == TUSB_REQ_TYPE_STANDARD) { - switch (p_request->bRequest) { - case TUSB_REQ_GET_INTERFACE: - return audiod_get_interface(rhport, p_request); - - case TUSB_REQ_SET_INTERFACE: - return audiod_set_interface(rhport, p_request); - - case TUSB_REQ_CLEAR_FEATURE: - return true; - - // Unknown/Unsupported request - default: - TU_BREAKPOINT(); - return false; - } - } - - // Handle class requests - if (p_request->bmRequestType_bit.type == TUSB_REQ_TYPE_CLASS) { - uint8_t itf = TU_U16_LOW(p_request->wIndex); - uint8_t func_id; - - // Conduct checks which depend on the recipient - switch (p_request->bmRequestType_bit.recipient) { - case TUSB_REQ_RCPT_INTERFACE: { - uint8_t entityID = TU_U16_HIGH(p_request->wIndex); - - // Verify if entity is present - if (entityID != 0) { - // Find index of audio driver structure and verify entity really - // exists - TU_VERIFY(audiod_verify_entity_exists(itf, entityID, &func_id)); - - // In case we got a get request invoke callback - callback needs - // to answer as defined in UAC2 specification page 89 - 5. - // Requests - if (p_request->bmRequestType_bit.direction == TUSB_DIR_IN) { - if (p_cb) { - return p_cb->get_req_entity_cb(rhport, p_request); - } else { - TU_LOG2(" No entity get request callback available!\r\n"); - return false; // Stall - } - } - } else { - // Find index of audio driver structure and verify interface - // really exists - TU_VERIFY(audiod_verify_itf_exists(itf, &func_id)); - - // In case we got a get request invoke callback - callback needs - // to answer as defined in UAC2 specification page 89 - 5. - // Requests - if (p_request->bmRequestType_bit.direction == TUSB_DIR_IN) { - if (p_cb) { - return p_cb->set_itf_cb(rhport, p_request); - } else { - TU_LOG2(" No interface get request callback available!\r\n"); - return false; // Stall - } - } - } - } break; - - case TUSB_REQ_RCPT_ENDPOINT: { - uint8_t ep = TU_U16_LOW(p_request->wIndex); - - // Find index of audio driver structure and verify EP really exists - TU_VERIFY(audiod_verify_ep_exists(ep, &func_id)); - - // In case we got a get request invoke callback - callback needs to - // answer as defined in UAC2 specification page 89 - 5. Requests - if (p_request->bmRequestType_bit.direction == TUSB_DIR_IN) { - if (p_cb) { - return p_cb->get_req_ep_cb(rhport, p_request); - } else { - TU_LOG2(" No EP get request callback available!\r\n"); - return false; // Stall - } - } - } break; - - // Unknown/Unsupported recipient - default: - TU_LOG2(" Unsupported recipient: %d\r\n", - p_request->bmRequestType_bit.recipient); - TU_BREAKPOINT(); - return false; - } - - // If we end here, the received request is a set request - we schedule a - // receive for the data stage and return true here. We handle the rest - // later in audiod_control_complete() once the data stage was finished - TU_VERIFY(tud_control_xfer(rhport, p_request, - _audiod_fct[func_id].ctrl_buf, - _audiod_fct[func_id].ctrl_buf_sz)); - return true; - } - - // There went something wrong - unsupported control request type - TU_BREAKPOINT(); - return false; - } - - bool audiod_get_interface(uint8_t rhport, - tusb_control_request_t const *p_request) { - uint8_t const itf = tu_u16_low(p_request->wIndex); - - // Find index of audio streaming interface - uint8_t func_id, idxItf; - uint8_t const *dummy; - - TU_VERIFY( - audiod_get_AS_interface_index_global(itf, &func_id, &idxItf, &dummy)); - TU_VERIFY(tud_control_xfer(rhport, p_request, - &_audiod_fct[func_id].alt_setting[idxItf], 1)); - - TU_LOG2(" Get itf: %u - current alt: %u\r\n", itf, - _audiod_fct[func_id].alt_setting[idxItf]); - - return true; - } - - // Invoked when class request DATA stage is finished. - // return false to stall control EP (e.g Host send non-sense DATA) - bool audiod_control_completeX(uint8_t rhport, - tusb_control_request_t const *p_request) { - // Handle audio class specific set requests - if (p_request->bmRequestType_bit.type == TUSB_REQ_TYPE_CLASS && - p_request->bmRequestType_bit.direction == TUSB_DIR_OUT) { - uint8_t func_id; - - switch (p_request->bmRequestType_bit.recipient) { - case TUSB_REQ_RCPT_INTERFACE: { - uint8_t itf = TU_U16_LOW(p_request->wIndex); - uint8_t entityID = TU_U16_HIGH(p_request->wIndex); - - if (entityID != 0) { - if (p_cb) { - // Check if entity is present and get corresponding driver index - TU_VERIFY(audiod_verify_entity_exists(itf, entityID, &func_id)); - - // Invoke callback - return p_cb->set_req_entity_cb(rhport, p_request, - _audiod_fct[func_id].ctrl_buf); - } else { - TU_LOG2(" No entity set request callback available!\r\n"); - return false; // In case no callback function is present or - // request can not be conducted we stall it - } - } else { - if (p_cb) { - // Find index of audio driver structure and verify interface - // really exists - TU_VERIFY(audiod_verify_itf_exists(itf, &func_id)); - - // Invoke callback - return p_cb->set_req_itf_cb(rhport, p_request, - _audiod_fct[func_id].ctrl_buf); - } else { - TU_LOG2(" No interface set request callback available!\r\n"); - return false; // In case no callback function is present or - // request can not be conducted we stall it - } - } - } break; - - case TUSB_REQ_RCPT_ENDPOINT: { - uint8_t ep = TU_U16_LOW(p_request->wIndex); - - if (p_cb) { - // Check if entity is present and get corresponding driver index - TU_VERIFY(audiod_verify_ep_exists(ep, &func_id)); - - // Invoke callback - return p_cb->set_req_ep_cb(rhport, p_request, - _audiod_fct[func_id].ctrl_buf); - } else { - TU_LOG2(" No EP set request callback available!\r\n"); - return false; // In case no callback function is present or - // request can not be conducted we stall it - } - } break; - // Unknown/Unsupported recipient - default: - TU_BREAKPOINT(); - return false; - } - } - return true; - } - - bool set_fb_params_freq(audiod_function_t *audio, uint32_t sample_freq, - uint32_t mclk_freq) { - // Check if frame interval is within sane limits - // The interval value n_frames was taken from the descriptors within - // audiod_set_interface() - - // n_frames_min is ceil(2^10 * f_s / f_m) for full speed and ceil(2^13 * - // f_s / f_m) for high speed this lower limit ensures the measures - // feedback value has sufficient precision - uint32_t const k = (TUSB_SPEED_FULL == tud_speed_get()) ? 10 : 13; - uint32_t const n_frame = (1UL << audio->feedback.frame_shift); - - if ((((1UL << k) * sample_freq / mclk_freq) + 1) > n_frame) { - TU_LOG1(" UAC2 feedback interval too small\r\n"); - TU_BREAKPOINT(); - return false; - } - - // Check if parameters really allow for a power of two division - if ((mclk_freq % sample_freq) == 0 && - tu_is_power_of_two(mclk_freq / sample_freq)) { - audio->feedback.compute_method = - AUDIO_FEEDBACK_METHOD_FREQUENCY_POWER_OF_2; - audio->feedback.compute.power_of_2 = - 16 - audio->feedback.frame_shift - tu_log2(mclk_freq / sample_freq); - } else if (audio->feedback.compute_method == - AUDIO_FEEDBACK_METHOD_FREQUENCY_FLOAT) { - audio->feedback.compute.float_const = - (float)sample_freq / mclk_freq * - (1UL << (16 - audio->feedback.frame_shift)); - } else { - audio->feedback.compute.fixed.sample_freq = sample_freq; - audio->feedback.compute.fixed.mclk_freq = mclk_freq; - } - - return true; - } - - uint32_t tud_audio_feedback_update(uint8_t func_id, uint32_t cycles) { - audiod_function_t *audio = &_audiod_fct[func_id]; - uint32_t feedback; - - switch (audio->feedback.compute_method) { - case AUDIO_FEEDBACK_METHOD_FREQUENCY_POWER_OF_2: - feedback = (cycles << audio->feedback.compute.power_of_2); - break; - - case AUDIO_FEEDBACK_METHOD_FREQUENCY_FLOAT: - feedback = - (uint32_t)((float)cycles * audio->feedback.compute.float_const); - break; - - case AUDIO_FEEDBACK_METHOD_FREQUENCY_FIXED: { - uint64_t fb64 = - (((uint64_t)cycles) * audio->feedback.compute.fixed.sample_freq) - << (16 - audio->feedback.frame_shift); - feedback = (uint32_t)(fb64 / audio->feedback.compute.fixed.mclk_freq); - } break; - - default: - return 0; - } - - // For Windows: - // https://docs.microsoft.com/en-us/windows-hardware/drivers/audio/usb-2-0-audio-drivers - // The size of isochronous packets created by the device must be within - // the limits specified in FMT-2.0 section 2.3.1.1. This means that the - // deviation of actual packet size from nominal size must not exceed ± - // one audio slot (audio slot = channel count samples). - if (feedback > audio->feedback.max_value) - feedback = audio->feedback.max_value; - if (feedback < audio->feedback.min_value) - feedback = audio->feedback.min_value; - - tud_audio_n_fb_set(func_id, feedback); - - return feedback; - } - - // This helper function finds for a given audio function and AS interface - // number the index of the attached driver structure, the index of the - // interface in the audio function (e.g. the std. AS interface with - // interface number 15 is the first AS interface for the given audio - // function and thus gets index zero), and finally a pointer to the std. AS - // interface, where the pointer always points to the first alternate setting - // i.e. alternate interface zero. - bool audiod_get_AS_interface_index(uint8_t itf, audiod_function_t *audio, - uint8_t *idxItf, - uint8_t const **pp_desc_int) { - if (audio->p_desc) { - // Get pointer at end - uint8_t const *p_desc_end = - audio->p_desc + audio->desc_length - TUD_AUDIO_DESC_IAD_LEN; - - // Advance past AC descriptors - uint8_t const *p_desc = tu_desc_next(audio->p_desc); - p_desc += ((audio_desc_cs_ac_interface_t const *)p_desc)->wTotalLength; - - uint8_t tmp = 0; - // Condition modified from p_desc < p_desc_end to prevent gcc>=12 - // strict-overflow warning - while (p_desc_end - p_desc > 0) { - // We assume the number of alternate settings is increasing thus we - // return the index of alternate setting zero! - if (tu_desc_type(p_desc) == TUSB_DESC_INTERFACE && - ((tusb_desc_interface_t const *)p_desc)->bAlternateSetting == 0) { - if (((tusb_desc_interface_t const *)p_desc)->bInterfaceNumber == - itf) { - *idxItf = tmp; - *pp_desc_int = p_desc; - return true; - } - // Increase index, bytes read, and pointer - tmp++; - } - p_desc = tu_desc_next(p_desc); - } - } - return false; - } - - // This helper function finds for a given AS interface number the index of - // the attached driver structure, the index of the interface in the audio - // function (e.g. the std. AS interface with interface number 15 is the - // first AS interface for the given audio function and thus gets index - // zero), and finally a pointer to the std. AS interface, where the pointer - // always points to the first alternate setting i.e. alternate interface - // zero. - bool audiod_get_AS_interface_index_global(uint8_t itf, uint8_t *func_id, - uint8_t *idxItf, - uint8_t const **pp_desc_int) { - // Loop over audio driver interfaces - uint8_t i; - for (i = 0; i < cfg.func_n_as_int; i++) { - if (audiod_get_AS_interface_index(itf, &_audiod_fct[i], idxItf, - pp_desc_int)) { - *func_id = i; - return true; - } - } - - return false; - } - - // Verify an entity with the given ID exists and returns also the - // corresponding driver index - bool audiod_verify_entity_exists(uint8_t itf, uint8_t entityID, - uint8_t *func_id) { - uint8_t i; - for (i = 0; i < cfg.func_n_as_int; i++) { - // Look for the correct driver by checking if the unique standard AC - // interface number fits - if (_audiod_fct[i].p_desc && - ((tusb_desc_interface_t const *)_audiod_fct[i].p_desc) - ->bInterfaceNumber == itf) { - // Get pointers after class specific AC descriptors and end of AC - // descriptors - entities are defined in between - uint8_t const *p_desc = - tu_desc_next(_audiod_fct[i].p_desc); // Points to CS AC descriptor - uint8_t const *p_desc_end = - ((audio_desc_cs_ac_interface_t const *)p_desc)->wTotalLength + - p_desc; - p_desc = tu_desc_next(p_desc); // Get past CS AC descriptor - - // Condition modified from p_desc < p_desc_end to prevent gcc>=12 - // strict-overflow warning - while (p_desc_end - p_desc > 0) { - if (p_desc[3] == entityID) // Entity IDs are always at offset 3 - { - *func_id = i; - return true; - } - p_desc = tu_desc_next(p_desc); - } - } - } - return false; - } - - bool audiod_verify_itf_exists(uint8_t itf, uint8_t *func_id) { - uint8_t i; - for (i = 0; i < cfg.func_n_as_int; i++) { - if (_audiod_fct[i].p_desc) { - // Get pointer at beginning and end - uint8_t const *p_desc = _audiod_fct[i].p_desc; - uint8_t const *p_desc_end = _audiod_fct[i].p_desc + - _audiod_fct[i].desc_length - - TUD_AUDIO_DESC_IAD_LEN; - // Condition modified from p_desc < p_desc_end to prevent gcc>=12 - // strict-overflow warning - while (p_desc_end - p_desc > 0) { - if (tu_desc_type(p_desc) == TUSB_DESC_INTERFACE && - ((tusb_desc_interface_t const *)_audiod_fct[i].p_desc) - ->bInterfaceNumber == itf) { - *func_id = i; - return true; - } - p_desc = tu_desc_next(p_desc); - } - } - } - return false; - } - - bool audiod_verify_ep_exists(uint8_t ep, uint8_t *func_id) { - uint8_t i; - for (i = 0; i < cfg.func_n_as_int; i++) { - if (_audiod_fct[i].p_desc) { - // Get pointer at end - uint8_t const *p_desc_end = - _audiod_fct[i].p_desc + _audiod_fct[i].desc_length; - - // Advance past AC descriptors - EP we look for are streaming EPs - uint8_t const *p_desc = tu_desc_next(_audiod_fct[i].p_desc); - p_desc += ((audio_desc_cs_ac_interface_t const *)p_desc)->wTotalLength; - - // Condition modified from p_desc < p_desc_end to prevent gcc>=12 - // strict-overflow warning - while (p_desc_end - p_desc > 0) { - if (tu_desc_type(p_desc) == TUSB_DESC_ENDPOINT && - ((tusb_desc_endpoint_t const *)p_desc)->bEndpointAddress == ep) { - *func_id = i; - return true; - } - p_desc = tu_desc_next(p_desc); - } - } - } - return false; - } - - // p_desc points to the AS interface of alternate setting zero - // itf is the interface number of the corresponding interface - we check if - // the interface belongs to EP in or EP out to see if it is a TX or RX - // parameter Currently, only AS interfaces with an EP (in or out) are - // supposed to be parsed for! - void audiod_parse_for_AS_params(audiod_function_t *audio, - uint8_t const *p_desc, - uint8_t const *p_desc_end, - uint8_t const as_itf) { - if (cfg.is_ep_in() && cfg.is_ep_out()) { - if (as_itf != audio->ep_in_as_intf_num && - as_itf != audio->ep_out_as_intf_num) - return; // Abort, this interface has no EP, this driver does not - // support this currently - } - if (cfg.is_ep_in() && !cfg.is_ep_out()) { - if (as_itf != audio->ep_in_as_intf_num) return; - } - if (!cfg.is_ep_in() && cfg.is_ep_out()) { - if (as_itf != audio->ep_out_as_intf_num) return; - } - - p_desc = tu_desc_next(p_desc); // Exclude standard AS interface descriptor - // of current alternate interface descriptor - // Condition modified from p_desc < p_desc_end to prevent gcc>=12 - // strict-overflow warning - while (p_desc_end - p_desc > 0) { - // Abort if follow up descriptor is a new standard interface descriptor - // - indicates the last AS descriptor was already finished - if (tu_desc_type(p_desc) == TUSB_DESC_INTERFACE) break; - - // Look for a Class-Specific AS Interface Descriptor(4.9.2) to verify - // format type and format and also to get number of physical channels - if (tu_desc_type(p_desc) == TUSB_DESC_CS_INTERFACE && - tu_desc_subtype(p_desc) == AUDIO_CS_AS_INTERFACE_AS_GENERAL) { - if (cfg.is_ep_in()) { - if (as_itf == audio->ep_in_as_intf_num) { - audio->n_channels_tx = - ((audio_desc_cs_as_interface_t const *)p_desc)->bNrChannels; - audio->format_type_tx = - (audio_format_type_t)(((audio_desc_cs_as_interface_t const *) - p_desc) - ->bFormatType); - } - } - - // Look for a Type I Format Type Descriptor(2.3.1.6 - Audio Formats) - if (cfg.enable_ep_in_flow_control) { - if (tu_desc_type(p_desc) == TUSB_DESC_CS_INTERFACE && - tu_desc_subtype(p_desc) == AUDIO_CS_AS_INTERFACE_FORMAT_TYPE && - ((audio_desc_type_I_format_t const *)p_desc)->bFormatType == - AUDIO_FORMAT_TYPE_I) { - if (cfg.is_ep_in() && cfg.is_ep_out()) { - if (as_itf != audio->ep_in_as_intf_num && - as_itf != audio->ep_out_as_intf_num) - break; // Abort loop, this interface has no EP, this driver - // does not support this currently - } - if (cfg.is_ep_in() && !cfg.is_ep_out()) { - if (as_itf != audio->ep_in_as_intf_num) break; - } - if (!cfg.is_ep_in() && cfg.is_ep_out()) { - if (as_itf != audio->ep_out_as_intf_num) break; - } - - if (cfg.is_ep_in()) { - if (as_itf == audio->ep_in_as_intf_num) { - audio->n_bytes_per_sample_tx = - ((audio_desc_type_I_format_t const *)p_desc)->bSubslotSize; - } - } - } - } - } - // Other format types are not supported yet - - p_desc = tu_desc_next(p_desc); - } - } - - bool audiod_calc_tx_packet_sz(audiod_function_t *audio) { - TU_VERIFY(audio->format_type_tx == AUDIO_FORMAT_TYPE_I); - TU_VERIFY(audio->n_channels_tx); - TU_VERIFY(audio->n_bytes_per_sample_tx); - TU_VERIFY(audio->interval_tx); - TU_VERIFY(audio->sample_rate_tx); - - const uint8_t interval = (tud_speed_get() == TUSB_SPEED_FULL) - ? audio->interval_tx - : 1 << (audio->interval_tx - 1); - - const uint16_t sample_normimal = - (uint16_t)(audio->sample_rate_tx * interval / - ((tud_speed_get() == TUSB_SPEED_FULL) ? 1000 : 8000)); - const uint16_t sample_reminder = - (uint16_t)(audio->sample_rate_tx * interval % - ((tud_speed_get() == TUSB_SPEED_FULL) ? 1000 : 8000)); - - const uint16_t packet_sz_tx_min = - (uint16_t)((sample_normimal - 1) * audio->n_channels_tx * - audio->n_bytes_per_sample_tx); - const uint16_t packet_sz_tx_norm = - (uint16_t)(sample_normimal * audio->n_channels_tx * - audio->n_bytes_per_sample_tx); - const uint16_t packet_sz_tx_max = - (uint16_t)((sample_normimal + 1) * audio->n_channels_tx * - audio->n_bytes_per_sample_tx); - - // Endpoint size must larger than packet size - TU_ASSERT(packet_sz_tx_max <= audio->ep_in_sz); - - // Frmt20.pdf 2.3.1.1 USB Packets - if (sample_reminder) { - // All virtual frame packets must either contain INT(nav) audio slots - // (small VFP) or INT(nav)+1 (large VFP) audio slots - audio->packet_sz_tx[0] = packet_sz_tx_norm; - audio->packet_sz_tx[1] = packet_sz_tx_norm; - audio->packet_sz_tx[2] = packet_sz_tx_max; - } else { - // In the case where nav = INT(nav), ni may vary between INT(nav)-1 - // (small VFP), INT(nav) (medium VFP) and INT(nav)+1 (large VFP). - audio->packet_sz_tx[0] = packet_sz_tx_min; - audio->packet_sz_tx[1] = packet_sz_tx_norm; - audio->packet_sz_tx[2] = packet_sz_tx_max; - } - - return true; - } - - uint16_t audiod_tx_packet_size(const uint16_t *norminal_size, - uint16_t data_count, uint16_t fifo_depth, - uint16_t max_depth) { - // Flow control need a FIFO size of at least 4*Navg - if (norminal_size[1] && norminal_size[1] <= fifo_depth * 4) { - // Use blackout to prioritize normal size packet - int ctrl_blackout = 0; - uint16_t packet_size; - uint16_t slot_size = norminal_size[2] - norminal_size[1]; - if (data_count < norminal_size[0]) { - // If you get here frequently, then your I2S clock deviation is too - // big ! - packet_size = 0; - } else if (data_count < fifo_depth / 2 - slot_size && !ctrl_blackout) { - packet_size = norminal_size[0]; - ctrl_blackout = 10; - } else if (data_count > fifo_depth / 2 + slot_size && !ctrl_blackout) { - packet_size = norminal_size[2]; - if (norminal_size[0] == norminal_size[1]) { - // nav > INT(nav), eg. 44.1k, 88.2k - ctrl_blackout = 0; - } else { - // nav = INT(nav), eg. 48k, 96k - ctrl_blackout = 10; - } - } else { - packet_size = norminal_size[1]; - if (ctrl_blackout) { - ctrl_blackout--; - } - } - // Normally this cap is not necessary - return tu_min16(packet_size, max_depth); - } else { - return tu_min16(data_count, max_depth); - } - } -}; - -// TU_ATTR_FAST_FUNC void tud_audio_feedback_interval_isr(uint8_t func_id, -// uint32_t frame_number, uint8_t interval_shift){ -// // call audiod_sof_isr -// } diff --git a/src/AudioTools/Sandbox/USB/USBDeviceAudioAdafruit.h b/src/AudioTools/Sandbox/USB/USBDeviceAudioAdafruit.h deleted file mode 100644 index c7b2e1771a..0000000000 --- a/src/AudioTools/Sandbox/USB/USBDeviceAudioAdafruit.h +++ /dev/null @@ -1,91 +0,0 @@ -#pragma once -#define CFG_TUSB_DEBUG_PRINTF 3 -#include "Adafruit_TinyUSB.h" -#include "USBDeviceAudio.h" -// Error message for Core that must select TinyUSB via menu -#if !defined(USE_TINYUSB) && \ - (defined(ARDUINO_ARCH_SAMD) || (defined(ARDUINO_ARCH_RP2040) && !defined(ARDUINO_ARCH_MBED))) -# error TinyUSB is not selected, please select it in "Tools->Menu->USB Stack" -#endif - -class USBDeviceAudioAdafruit; -USBDeviceAudioAdafruit *self_USBDeviceAudioAdafruit = nullptr; - -/*** - * @brief Adafruit TinyUSB Initialization logic. - */ -class USBDeviceAudioAdafruit : public USBDeviceAudio, public Adafruit_USBD_Interface { - public: - USBDeviceAudioAdafruit() : USBDeviceAudio() { - self_USBDeviceAudioAdafruit = this; - } - - bool begin(USBAudioConfig config) override { - // setup config buffer - if (interface_descriptor.size() != 512){ - interface_descriptor.resize(512); - TinyUSBDevice.setConfigurationBuffer(interface_descriptor.data(), 512); - } - - // add string descriptor - if (_stridx == 0) { - _stridx = TinyUSBDevice.addStringDescriptor("TinyUSB Audio"); - } - - if (!USBDeviceAudio::begin(config)) { - LOG_AUDIO_ERROR("begin failed"); - return false; - } - - // add the interface - if (!TinyUSBDevice.addInterface(*self_USBDeviceAudioAdafruit)) { - setStatus(AudioProcessingStatus::ERROR); - LOG_AUDIO_ERROR("addInterface failed"); - return false; - } - - return true; - } - - uint8_t allocInterface(uint8_t count = 1) override { - return TinyUSBDevice.allocInterface(count); - } - - uint8_t allocEndpoint(uint8_t in) override { - return TinyUSBDevice.allocEndpoint(in); - } - - uint16_t getInterfaceDescriptor(uint8_t itfnum, uint8_t *buf, - uint16_t bufsize) override { - return USBDeviceAudio::getInterfaceDescriptor(buf, bufsize); - } -}; - - -// callback to register custom application driver for AUDIO -usbd_class_driver_t const* usbd_app_driver_get_cb(uint8_t* driver_count) { - static usbd_class_driver_t audio_class_driver; - audio_class_driver.name = "AUDIO"; - audio_class_driver.init = []() { - self_USBDeviceAudioAdafruit->begin(self_USBDeviceAudioAdafruit->api().config()); - self_USBDeviceAudioAdafruit->api().audiod_init(); - }; - audio_class_driver.deinit = []() { return self_USBDeviceAudioAdafruit->api().audiod_deinit(); }; - audio_class_driver.reset = [](uint8_t rhport) { self_USBDeviceAudioAdafruit->api().audiod_reset(rhport); }; - audio_class_driver.open = [](uint8_t rhport, tusb_desc_interface_t const * desc_intf, uint16_t max_len) { - return self_USBDeviceAudioAdafruit->api().audiod_open(rhport, desc_intf, max_len); - }; - audio_class_driver.control_xfer_cb = [](uint8_t rhport, uint8_t stage, tusb_control_request_t const * request){ - return self_USBDeviceAudioAdafruit->api().audiod_control_xfer_cb(rhport, stage, request); - }; - audio_class_driver.xfer_cb = [](uint8_t rhport, uint8_t ep_addr, xfer_result_t result, uint32_t xferred_bytes){ - return self_USBDeviceAudioAdafruit->api().audiod_xfer_cb(rhport, ep_addr, result, xferred_bytes); - }; - audio_class_driver.sof = [](uint8_t rhport, uint32_t frame_count) { self_USBDeviceAudioAdafruit->api().audiod_sof_isr(self_USBDeviceAudioAdafruit->api().config().rh_port, 0); }; - - - - *driver_count = 1; - return &audio_class_driver; -} - diff --git a/src/AudioTools/Sandbox/USB/USBDeviceAudioESP32.h b/src/AudioTools/Sandbox/USB/USBDeviceAudioESP32.h deleted file mode 100644 index 23a7e82e42..0000000000 --- a/src/AudioTools/Sandbox/USB/USBDeviceAudioESP32.h +++ /dev/null @@ -1,123 +0,0 @@ -#pragma once -#include "USBDeviceAudio.h" -#include "USB.h" -#include "esp32-hal-tinyusb.h" - -#ifndef ARDUINO_USB_MODE -#error This ESP32 SoC has no Native USB interface -#else -#if ARDUINO_USB_MODE == 1 -#error This sketch should be used when USB is in OTG mode -#endif -#endif - - -struct USBConfigESP32 { - uint8_t *descr = nullptr; - int descr_len = 0; - int itf_count = 0; -}; - -static USBConfigESP32 usb_audio_config_esp32; - -/// for the ESP32 the interface descriptor must be provided via a callback -uint16_t tinyusb_audio_descriptor_cb(uint8_t *dst, uint8_t *itf) { - uint8_t str_index = tinyusb_add_string_descriptor("TinyUSB Audio"); - *itf += usb_audio_config_esp32.itf_count; - memcpy(dst, usb_audio_config_esp32.descr, usb_audio_config_esp32.descr_len); - return usb_audio_config_esp32.descr_len; -}; - -/*** - * @brief ESP32 Initialization logic: We need to provide the USBAudioConfig in the - * constructor, so that we can determine the descriptor properly when the - * object is constructed. - */ -class USBDeviceAudioESP32 : public USBDeviceAudio { - public: - USBDeviceAudioESP32(USBAudioConfig config) { - begin(config); - - int len = setupDescriptorCB(); - - tinyusb_enable_interface(USB_INTERFACE_CUSTOM, len, - tinyusb_audio_descriptor_cb); - // arduino_usb_event_handler_register_with(ARDUINO_USB_EVENTS, - // ARDUINO_USB_STOPPED_EVENT, - // usb_unplugged_cb, this); - setupDebugPins(); - } - - virtual uint8_t allocInterface(uint8_t count = 1) { - uint8_t ret = _itf_count; - _itf_count += count; - return ret; - } - - virtual uint8_t allocEndpoint(uint8_t in) { - uint8_t ret = in ? (0x80 | _epin_count++) : _epout_count++; -#if defined(ARDUINO_ARCH_ESP32) && ARDUINO_USB_CDC_ON_BOOT && !ARDUINO_USB_MODE - // ESP32 reserves 0x03, 0x84, 0x85 for CDC Serial - if (ret == 0x03) { - ret = _epout_count++; - } else if (ret == 0x84 || ret == 0x85) { - // Note: ESP32 does not have this much of EP IN - _epin_count = 6; - ret = 0x86; - } -#endif - return ret; - } - - protected: - int _itf_count = 0; - int _epout_count = 0; - int _epin_count = 0; - - int setupDescriptorCB() { - int len = getInterfaceDescriptorLength(0); - usb_audio_config_esp32.descr_len = len; - interface_descriptor.resize(len); - usb_audio_config_esp32.descr = interface_descriptor.data(); - usb_audio_config_esp32.itf_count = _itf_count; - getInterfaceDescriptor(0, usb_audio_config_esp32.descr, len); - return len; - } - -}; - - -static USBDeviceAudioAdafruit USBAudio; -static usbd_class_driver_t audio_class_driver; - -TU_ATTR_FAST_FUNC void audiod_init() { USBAudio.api().audiod_init(); } -TU_ATTR_FAST_FUNC bool audiod_deinit() { return USBAudio.api().audiod_deinit(); } -TU_ATTR_FAST_FUNC void audiod_reset(uint8_t rhport) { USBAudio.api().audiod_reset(rhport); } -TU_ATTR_FAST_FUNC uint16_t audiod_open(uint8_t rhport, tusb_desc_interface_t const * desc_intf, uint16_t max_len) { - return USBAudio.api().audiod_open(rhport, desc_intf, max_len); -} -TU_ATTR_FAST_FUNC bool audiod_control_xfer_cb(uint8_t rhport, uint8_t stage, tusb_control_request_t const * request){ - return USBAudio.api().audiod_control_xfer_cb(rhport, stage, request); -} -TU_ATTR_FAST_FUNC bool audiod_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t result, uint32_t xferred_bytes){ - return USBAudio.api().audiod_xfer_cb(rhport, ep_addr, result, xferred_bytes); -} - -TU_ATTR_FAST_FUNC void tud_audio_feedback_interval_isr(uint8_t func_id, -uint32_t frame_number, uint8_t interval_shift){ - return USBAudio.audiod_sof_isr(USBAudio.cfg.rh_port, 0); -} - - -// callback to register custom application driver for AUDIO -usbd_class_driver_t const* usbd_app_driver_get_cb(uint8_t* driver_count) { - audio_class_driver.name = "AUDIO"; - audio_class_driver.init = audiod_init; - audio_class_driver.deinit = audiod_deinit; - audio_class_driver.open = audiod_open; - audio_class_driver.control_xfer_cb = audiod_control_xfer_cb; - audio_class_driver.xfer_cb = audiod_xfer_cb; - //audio_class_driver.sof = audiod_sof; - return &audio_class_driver; -} - diff --git a/src/AudioTools/CoreAudio/StreamCopy.h b/src/AudioTools/StreamCopy.h similarity index 50% rename from src/AudioTools/CoreAudio/StreamCopy.h rename to src/AudioTools/StreamCopy.h index e2e3e92bdc..d18d8e1871 100644 --- a/src/AudioTools/CoreAudio/StreamCopy.h +++ b/src/AudioTools/StreamCopy.h @@ -1,12 +1,11 @@ #pragma once -#include "AudioToolsConfig.h" -#include "AudioTools/CoreAudio/AudioTypes.h" -#include "AudioTools/CoreAudio/Buffers.h" -#include "AudioTools/CoreAudio/BaseConverter.h" -#include "AudioTools/CoreAudio/AudioLogger.h" -#include "AudioTools/CoreAudio/AudioStreams.h" -#include "AudioTools/CoreAudio/AudioMetaData/MimeDetector.h" +#include "AudioConfig.h" +#include "AudioTools/AudioTypes.h" +#include "AudioTools/Buffers.h" +#include "AudioTools/BaseConverter.h" +#include "AudioTools/AudioLogger.h" +#include "AudioTools/AudioStreams.h" #define NOT_ENOUGH_MEMORY_MSG "Could not allocate enough memory: %d bytes" @@ -23,41 +22,42 @@ namespace audio_tools { template class StreamCopyT { public: - - StreamCopyT(Print &to, AudioStream &from, int bufferSize=DEFAULT_BUFFER_SIZE){ + StreamCopyT(Print &to, AudioStream &from, int buffer_size=DEFAULT_BUFFER_SIZE){ TRACED(); - this->buffer_size = bufferSize; begin(to, from); + this->buffer_size = buffer_size; + buffer.resize(buffer_size); + if (!buffer){ + LOGE(NOT_ENOUGH_MEMORY_MSG, buffer_size); + } } - StreamCopyT(Print &to, Stream &from, int bufferSize=DEFAULT_BUFFER_SIZE){ + StreamCopyT(Print &to, Stream &from, int buffer_size=DEFAULT_BUFFER_SIZE){ TRACED(); - this->buffer_size = bufferSize; begin(to, from); + this->buffer_size = buffer_size; + buffer.resize(buffer_size); + if (!buffer){ + LOGE(NOT_ENOUGH_MEMORY_MSG, buffer_size); + } } - StreamCopyT(int bufferSize=DEFAULT_BUFFER_SIZE){ + StreamCopyT(int buffer_size=DEFAULT_BUFFER_SIZE){ TRACED(); - this->buffer_size = bufferSize; - begin(); + this->buffer_size = buffer_size; + buffer.resize(buffer_size); + if (!buffer){ + LOGE(NOT_ENOUGH_MEMORY_MSG, buffer_size); + } } - ~StreamCopyT() { - end(); - } + StreamCopyT(StreamCopyT const&) = delete; + StreamCopyT& operator=(StreamCopyT const&) = delete; /// (Re)starts the processing void begin(){ - TRACED(); - if (p_mime_detector!=nullptr) { - p_mime_detector->begin(); - }; - resize(buffer_size); - if (buffer){ - LOGI("buffer_size=%d",buffer_size); - } else { - LOGE(NOT_ENOUGH_MEMORY_MSG, buffer_size); - } + is_first = true; + LOGI("buffer_size=%d",buffer_size); } /// Ends the processing @@ -68,17 +68,18 @@ class StreamCopyT { /// assign a new output and input stream void begin(Print &to, Stream &from){ - this->from = &from; + this->from = new AudioStreamWrapper(from); this->to = &to; - begin(); + is_first = true; + LOGI("buffer_size=%d",buffer_size); } /// assign a new output and input stream void begin(Print &to, AudioStream &from){ this->from = &from; - this->from_audio = &from; this->to = &to; - begin(); + is_first = true; + LOGI("buffer_size=%d",buffer_size); } /// Provides a pointer to the copy source. Can be used to check if the source is defined. @@ -91,27 +92,11 @@ class StreamCopyT { return to; } - /// copies the data from the source to the destination and returns the processed number of bytes - inline size_t copy() { - p_converter = nullptr; - return copyBytes(buffer_size); - } - - /// copies the data from the source to the destination and applies the converter - the result is the processed number of bytes - inline size_t copy(BaseConverter &converter) { - p_converter = &converter; - return copyBytes(buffer_size); - } - - /// copies the inicated number of bytes from the source to the destination and returns the processed number of bytes - inline size_t copyBytes(size_t bytes){ - LOGD("copy %d bytes %s", (int) bytes, log_name); - if (!active) return 0; + /// copies the data from the source to the destination - the result is in bytes + inline size_t copy(){ + TRACED(); // if not initialized we do nothing - if (from==nullptr && to==nullptr) return 0; - - // synchronize AudioInfo - syncAudioInfo(); + if (from==nullptr || to==nullptr) return 0; // E.g. if we try to write to a server we might not have any output destination yet int to_write = to->availableForWrite(); @@ -120,35 +105,21 @@ class StreamCopyT { return 0; } - // resize copy buffer if necessary - if (buffer.size() < bytes){ - LOGI("Resize to %d", (int) bytes); - buffer.resize(bytes); - } - size_t result = 0; size_t delayCount = 0; - size_t len = bytes; - if (check_available) { - len = available(); - } - size_t bytes_to_read = bytes; + size_t len = available(); + size_t bytes_to_read = buffer_size; size_t bytes_read = 0; - if (len > 0){ + if (len>0){ bytes_to_read = min(len, static_cast(buffer_size)); + size_t samples = bytes_to_read / sizeof(T); + bytes_to_read = samples * sizeof(T); // don't overflow buffer - if (to_write > 0){ + if (to_write>0){ bytes_to_read = min((int)bytes_to_read, to_write); } - // round to full frames - int copy_size = minCopySize(); - if (copy_size > 0){ - size_t samples = bytes_to_read / minCopySize(); - bytes_to_read = samples * minCopySize(); - } - // get the data now bytes_read = 0; if (bytes_to_read>0){ @@ -156,12 +127,7 @@ class StreamCopyT { } // determine mime - if (p_mime_detector != nullptr){ - p_mime_detector->write(buffer.data(), bytes_to_read); - } - - // convert data - if (p_converter!=nullptr) p_converter->convert((uint8_t*)buffer.data(), bytes_read ); + notifyMime(buffer.data(), bytes_to_read); // write data result = write(bytes_read, delayCount); @@ -170,30 +136,43 @@ class StreamCopyT { if (onWrite!=nullptr) onWrite(onWriteObj, &buffer[0], result); #ifndef COPY_LOG_OFF - LOGI("StreamCopy::copy %s %u -> %u -> %u bytes - in %u hops",log_name, (unsigned int)bytes_to_read,(unsigned int) bytes_read, (unsigned int)result, (unsigned int)delayCount); + LOGI("StreamCopy::copy %u -> %u -> %u bytes - in %u hops", (unsigned int)bytes_to_read,(unsigned int) bytes_read, (unsigned int)result, (unsigned int)delayCount); #endif - //TRACED(); - if (result == 0){ - TRACED(); + if (result==0){ // give the processor some time delay(delay_on_no_data); } - //TRACED(); CHECK_MEMORY(); } else { // give the processor some time delay(delay_on_no_data); - LOGD("no data %s", log_name); } - //TRACED(); return result; } - /// Copies pages * buffersize samples: returns the processed number of bytes + + /// available bytes of the data source + int available() { + int result = 0; + if (from!=nullptr) { + if (availableCallback!=nullptr){ + result = availableCallback((Stream*)from); + } else { + result = from->available(); + } + } + return result; + } + + /// Defines the dealy that is used if no data is available + void setDelayOnNoData(int delayMs){ + delay_on_no_data = delayMs; + } + + /// Copies pages * buffersize samples size_t copyN(size_t pages){ - if (!active) return 0; size_t total=0; for (size_t j=0;javailable(); - } - } else { - LOGW("source not defined"); - } - LOGD("available: %d", result); - return result; + /// Provides the actual mime type, that was determined from the first available data + const char* mime() { + return actual_mime; } - /// Defines the dealy that is used if no data is available - void setDelayOnNoData(int delayMs){ - delay_on_no_data = delayMs; + /// Define the callback that will notify about mime changes + void setMimeCallback(void (*callback)(const char*)){ + TRACED(); + this->notifyMimeCallback = callback; } /// Defines a callback that is notified with the wirtten data @@ -291,143 +258,131 @@ class StreamCopyT { return check_available_for_write; } - /// Activates the check that we copy only if available returns a value - void setCheckAvailable(bool flag){ - check_available = flag; - } - - /// Is Available check activated ? - bool isCheckAvailable() { - return check_available; - } - /// resizes the copy buffer void resize(int len){ buffer_size = len; buffer.resize(buffer_size); } - /// deactivate/activate copy - active by default - void setActive(bool flag){ - active = flag; - } - - /// Check if copier is active - bool isActive(){ - return active; - } - - /// Defines a name which will be printed in the log to identify the copier - void setLogName(const char* name){ - log_name = name; - } - - /// Defines the delay that is added before we retry an incomplete copy - void setRetryDelay(int delay){ - retry_delay = delay; - } - - /// Determine frame size - int minCopySize() { - if (min_copy_size==0 && from_audio != nullptr){ - AudioInfo info = from_audio->audioInfoOut(); - min_copy_size = info.bits_per_sample / 8 * info.channels; - } - return min_copy_size; - } - - /// Defines the minimum frame size that is used to round the copy size: 0 will automatically try to determine the value - void setMinCopySize(int size){ - min_copy_size = size; - } - - /// Activate the synchronization from the AudioInfo form the source to the target - void setSynchAudioInfo(bool active){ - is_sync_audio_info = active; - } - - /// Define a mime detector - void setMimeDetector(MimeDetector &mime){ - p_mime_detector = &mime; - } - protected: - Stream *from = nullptr; - AudioStream *from_audio = nullptr; + AudioStream *from = nullptr; Print *to = nullptr; Vector buffer{0}; - int buffer_size = DEFAULT_BUFFER_SIZE; + int buffer_size; void (*onWrite)(void*obj, void*buffer, size_t len) = nullptr; + void (*notifyMimeCallback)(const char*mime) = nullptr; int (*availableCallback)(Stream*stream)=nullptr; void *onWriteObj = nullptr; + bool is_first = false; bool check_available_for_write = false; - bool check_available = true; + const char* actual_mime = nullptr; int retryLimit = COPY_RETRY_LIMIT; int delay_on_no_data = COPY_DELAY_ON_NODATA; - bool active = true; - const char* log_name = ""; - int retry_delay = 10; - int channels = 0; - int min_copy_size = 1; - bool is_sync_audio_info = false; - AudioInfoSupport *p_audio_info_support = nullptr; - BaseConverter* p_converter = nullptr; - MimeDetector* p_mime_detector = nullptr; - - void syncAudioInfo(){ - // synchronize audio info - if (is_sync_audio_info && from_audio != nullptr && p_audio_info_support != nullptr){ - AudioInfo info_from = from_audio->audioInfoOut(); - AudioInfo info_to = p_audio_info_support->audioInfo(); - if (info_from != info_to){ - LOGI("--> StreamCopy: "); - p_audio_info_support->setAudioInfo(info_from); - } - } - } /// blocking write - until everything is processed size_t write(size_t len, size_t &delayCount ){ if (!buffer || len==0) return 0; - LOGD("write: %d", (int)len); size_t total = 0; long open = len; int retry = 0; - while(open > 0){ + while(open>0){ size_t written = to->write((const uint8_t*)buffer.data()+total, open); - LOGD("write: %d -> %d", (int) open, (int) written); total += written; open -= written; delayCount++; - if (open > 0){ - // if we still have progress we reset the retry counter - if (written>0) retry = 0; - - // abort if we reached the retry limit - if (retry++ > retryLimit){ - LOGE("write %s to target has failed after %d retries! (%ld bytes)", log_name, retry, open); - break; - } - - // wait a bit - if (retry>1) { - delay(retry_delay); - LOGI("try write %s - %d (open %ld bytes) ",log_name, retry, open); - } + if (retry++ > retryLimit){ + LOGE("write to target has failed! (%ld bytes)", open); + break; + } + + if (retry>1) { + delay(5); + LOGI("try write - %d (open %ld bytes) ",retry, open); } + + CHECK_MEMORY(); } return total; } + + /// Update the mime type + void notifyMime(void* data, size_t len){ + if (is_first && len>4) { + const uint8_t *start = (const uint8_t *) data; + actual_mime = "audio/basic"; + if (start[0]==0xFF && start[1]==0xF1){ + actual_mime = "audio/aac"; + } else if (memcmp(start,"ID3",3) || start[0]==0xFF || start[0]==0xFE ){ + actual_mime = "audio/mpeg"; + } else if (memcmp(start,"RIFF",4)){ + actual_mime = "audio/vnd.wave"; + } + if (notifyMimeCallback!=nullptr){ + notifyMimeCallback(actual_mime); + } + } + is_first = false; + } + }; /** - * @brief We provide the typeless StreamCopy + * @brief We provide the typeless StreamCopy as a subclass of StreamCopyT * @ingroup tools * @author Phil Schatzmann * @copyright GPLv3 */ -using StreamCopy = StreamCopyT; +class StreamCopy : public StreamCopyT { + public: + StreamCopy(int buffer_size=DEFAULT_BUFFER_SIZE): StreamCopyT(buffer_size) { + TRACED(); + } + + StreamCopy(Print &to, AudioStream &from, int buffer_size=DEFAULT_BUFFER_SIZE) : StreamCopyT(to, from, buffer_size){ + TRACED(); + } + + StreamCopy(Print &to, Stream &from, int buffer_size=DEFAULT_BUFFER_SIZE) : StreamCopyT(to, from, buffer_size){ + TRACED(); + } + + /// copies a buffer length of data and applies the converter + template + size_t copy(BaseConverter &converter) { + size_t result = available(); + size_t delayCount = 0; + + BaseConverter *coverter_ptr = &converter; + if (result>0){ + size_t bytes_to_read = min(result, static_cast(buffer_size) ); + result = from->readBytes((uint8_t*)&buffer[0], bytes_to_read); + + // determine mime + notifyMime(buffer.data(), bytes_to_read); + is_first = false; + // callback with unconverted data + if (onWrite!=nullptr) onWrite(onWriteObj, buffer.data(), result); + + // convert data + coverter_ptr->convert((uint8_t*)buffer.data(), result ); + write(result, delayCount); + #ifndef COPY_LOG_OFF + LOGI("StreamCopy::copy %u bytes - in %u hops", (unsigned int)result,(unsigned int) delayCount); + #endif + } else { + // give the processor some time + delay(delay_on_no_data); + } + return result; + } + + /// Copies all bytes from the input to the output + inline size_t copy() { + return StreamCopyT::copy(); + } + +}; } // Namespace \ No newline at end of file diff --git a/src/AudioTools/SynchronizedBuffers.h b/src/AudioTools/SynchronizedBuffers.h new file mode 100644 index 0000000000..902985bc1e --- /dev/null +++ b/src/AudioTools/SynchronizedBuffers.h @@ -0,0 +1,463 @@ + +#pragma once +#include "AudioConfig.h" +#ifdef USE_CONCURRENCY +#include "AudioTools/Buffers.h" +#include "AudioTools/AudioLogger.h" + +#ifdef ESP32 +# include "freertos/FreeRTOS.h" +# include "AudioBasic/Collections/QueueFreeRTOS.h" +# if ESP_IDF_VERSION_MAJOR >= 4 +# include +# endif +#endif + +#ifdef USE_STD_CONCURRENCY +#include +#endif + +/** + * @defgroup concurrency Concurrency + * @ingroup tools + * @brief Multicore support + */ + + +namespace audio_tools { + +/** + * @brief Empty Mutex implementation which does nothing + * @ingroup concurrency + * @author Phil Schatzmann + * @copyright GPLv3 + */ +class MutexBase { +public: + virtual void lock() {} + virtual void unlock() {} +}; + +#if defined(USE_STD_CONCURRENCY) + +/** + * @brief Mutex implemntation based on std::mutex + * @ingroup concurrency + * @author Phil Schatzmann + * @copyright GPLv3 + */ +class StdMutex : public MutexBase { +public: + void lock() override { std_mutex.lock(); } + void unlock() override { std_mutex.unlock(); } + +protected: + std::mutex std_mutex; +}; + +#endif + +#if defined(ESP32) + +/** + * @brief Mutex implemntation using FreeRTOS + * @ingroup concurrency + * @author Phil Schatzmann + * @copyright GPLv3 * + */ +class Mutex : public MutexBase { +public: + Mutex() { + TRACED(); + xSemaphore = xSemaphoreCreateBinary(); + xSemaphoreGive(xSemaphore); + } + ~Mutex() { + TRACED(); + vSemaphoreDelete(xSemaphore); + } + void lock() override { + TRACED(); + xSemaphoreTake(xSemaphore, portMAX_DELAY); + } + void unlock() override { + TRACED(); + xSemaphoreGive(xSemaphore); + } + +protected: + SemaphoreHandle_t xSemaphore = NULL; +}; + +//#elif defined(ARDUINO_ARCH_RP2040) +// /** +// * @brief Mutex implemntation using RP2040 API +// * @ingroup concurrency +// * @author Phil Schatzmann +// * @copyright GPLv3 * +// */ +// class Mutex : public MutexBase { +// public: +// Mutex() { +// TRACED(); +// mutex_init(&mtx); +// } +// ~Mutex() { TRACED(); } +// void lock() override { +// TRACED(); +// mutex_enter_blocking(&mtx); +// } +// void unlock() override { +// TRACED(); +// mutex_exit(&mtx); +// } + +// protected: +// mutex_t mtx; +// }; + +#else + +using Mutex = MutexBase; + +#endif + +/** + * @brief RAII implementaion using a Mutex: Only a few microcontrollers provide + * lock guards, so I decided to roll my own solution where we can just use a + * dummy Mutex implementation that does nothing for the cases where this is not + * needed. + * @ingroup concurrency + * @author Phil Schatzmann + * @copyright GPLv3 * + */ +class LockGuard { +public: + LockGuard(Mutex &mutex) { + TRACED(); + p_mutex = &mutex; + p_mutex->lock(); + } + LockGuard(Mutex *mutex) { + TRACED(); + p_mutex = mutex; + p_mutex->lock(); + } + ~LockGuard() { + TRACED(); + p_mutex->unlock(); + } + +protected: + Mutex *p_mutex = nullptr; +}; + +/** + * @brief Wrapper class that can turn any Buffer into a thread save + * implementation. + * @ingroup buffers + * @author Phil Schatzmann + * @copyright GPLv3 * + * @tparam T + */ +template +class SynchronizedBuffer : public BaseBuffer { +public: + SynchronizedBuffer(BaseBuffer &buffer, Mutex &mutex) { + p_buffer = &buffer; + p_mutex = &mutex; + } + + // reads a single value + T read() override { + TRACED(); + LockGuard guard(p_mutex); + return p_buffer->read(); + } + + // reads multiple values + int readArray(T data[], int len) { + TRACED(); + LockGuard guard(p_mutex); + int lenResult = MIN(len, available()); + for (int j = 0; j < lenResult; j++) { + data[j] = p_buffer->read(); + } + return lenResult; + } + + int writeArray(const T data[], int len) { + LOGD("%s: %d", LOG_METHOD, len); + LockGuard guard(p_mutex); + int result = 0; + for (int j = 0; j < len; j++) { + if (p_buffer->write(data[j]) == 0) { + break; + } + result = j + 1; + } + return result; + } + + // peeks the actual entry from the buffer + T peek() override { + TRACED(); + LockGuard guard(p_mutex); + return p_buffer->peek(); + } + + // checks if the buffer is full + bool isFull() override { return p_buffer->isFull(); } + + bool isEmpty() { return available() == 0; } + + // write add an entry to the buffer + bool write(T data) override { + TRACED(); + LockGuard guard(p_mutex); + return p_buffer->write(data); + } + + // clears the buffer + void reset() override { + TRACED(); + LockGuard guard(p_mutex); + p_buffer->reset(); + } + + // provides the number of entries that are available to read + int available() override { + TRACED(); + return p_buffer->available(); + } + + // provides the number of entries that are available to write + int availableForWrite() override { + TRACED(); + LockGuard guard(p_mutex); + return p_buffer->availableForWrite(); + } + + // returns the address of the start of the physical read buffer + T *address() override { + TRACED(); + return p_buffer->address(); + } + +protected: + BaseBuffer *p_buffer = nullptr; + Mutex *p_mutex = nullptr; +}; + +#if defined(ESP32) + +/** + * @brief NBuffer which uses some RTOS queues to manage the available and filled buffers + * @ingroup buffers + * @tparam T + * @tparam COUNT number of buffers + */ +template +class SynchronizedNBuffer : public NBuffer { +public: + SynchronizedNBuffer(int bufferSize, int bufferCount, int writeMaxWait=portMAX_DELAY, int readMaxWait=portMAX_DELAY) { + TRACED(); + NBuffer::buffer_count = bufferCount; + NBuffer::buffer_size = bufferSize; + + available_buffers.resize(bufferCount); + filled_buffers.resize(bufferCount); + + setReadMaxWait(readMaxWait); + setWriteMaxWait(writeMaxWait); + + // setup buffers + NBuffer::write_buffer_count = 0; + for (int j = 0; j < bufferCount; j++) { + BaseBuffer *tmp = new SingleBuffer(bufferSize); + if (tmp != nullptr) { + available_buffers.enqueue(tmp); + } else { + LOGE("Not Enough Memory for buffer %d", j); + } + } + } + + void setReadMaxWait(TickType_t ticks){ + available_buffers.setReadMaxWait(ticks); + filled_buffers.setReadMaxWait(ticks); + } + + void setWriteMaxWait(TickType_t ticks){ + available_buffers.setWriteMaxWait(ticks); + filled_buffers.setWriteMaxWait(ticks); + } + + +protected: + QueueFreeRTOS*> available_buffers{0,portMAX_DELAY,0}; + QueueFreeRTOS*> filled_buffers{0,portMAX_DELAY,0}; + + BaseBuffer *getNextAvailableBuffer() { + TRACED(); + BaseBuffer* result; + return available_buffers.dequeue(result) ? result : nullptr; + } + + bool addAvailableBuffer(BaseBuffer *buffer) { + TRACED(); + return available_buffers.enqueue(buffer); + } + + BaseBuffer *getNextFilledBuffer() { + TRACED(); + BaseBuffer* result; + return filled_buffers.dequeue(result) ? result : nullptr; + } + + bool addFilledBuffer(BaseBuffer *buffer) { + TRACED(); + return filled_buffers.enqueue(buffer); + } +}; + +#if ESP_IDF_VERSION_MAJOR >= 4 + +/** + * @brief Buffer implementation which is using a FreeRTOS StreamBuffer + * @ingroup buffers + * @author Phil Schatzmann + * @copyright GPLv3 * + * @tparam T + */ +template +class SynchronizedBufferRTOS : public BaseBuffer { +public: + SynchronizedBufferRTOS(size_t xStreamBufferSizeBytes, size_t xTriggerLevel=256, TickType_t writeMaxWait=portMAX_DELAY, TickType_t readMaxWait=portMAX_DELAY) + : BaseBuffer() { + xStreamBuffer = xStreamBufferCreate(xStreamBufferSizeBytes, xTriggerLevel); + readWait = readMaxWait; + writeWait = writeMaxWait; + current_size = xStreamBufferSizeBytes; + trigger_level = xTriggerLevel; + } + ~SynchronizedBufferRTOS() { vStreamBufferDelete(xStreamBuffer); } + + void resize(size_t size){ + if (current_size != size){ + vStreamBufferDelete(xStreamBuffer); + xStreamBuffer = xStreamBufferCreate(size, trigger_level); + current_size = size; + } + } + + void setReadMaxWait(TickType_t ticks){ + readWait = ticks; + } + + void setWriteMaxWait(TickType_t ticks){ + writeWait = ticks; + } + + void setWriteFromISR(bool active){ + write_from_isr = active; + } + + void setReadFromISR(bool active){ + read_from_isr = active; + } + + // reads a single value + T read() override { + T data = 0; + readArray(&data, sizeof(T)); + return data; + } + + // reads multiple values + int readArray(T data[], int len) { + if (read_from_isr){ + xHigherPriorityTaskWoken = pdFALSE; + int result = xStreamBufferReceiveFromISR(xStreamBuffer, (void *)data, sizeof(T) * len, &xHigherPriorityTaskWoken); +#ifdef ESP32X + portYIELD_FROM_ISR( ); +#else + portYIELD_FROM_ISR( xHigherPriorityTaskWoken ); +#endif + return result; + } else { + return xStreamBufferReceive(xStreamBuffer, (void *)data, sizeof(T) * len, readWait); + } + } + + int writeArray(const T data[], int len) { + LOGD("%s: %d", LOG_METHOD, len); + if (write_from_isr){ + xHigherPriorityTaskWoken = pdFALSE; + int result = xStreamBufferSendFromISR(xStreamBuffer, (void *)data, sizeof(T) * len, &xHigherPriorityTaskWoken); +#ifdef ESP32X + portYIELD_FROM_ISR( ); +#else + portYIELD_FROM_ISR( xHigherPriorityTaskWoken ); +#endif + return result; + } else { + return xStreamBufferSend(xStreamBuffer, (void *)data, sizeof(T) * len, writeWait); + } + } + + // peeks the actual entry from the buffer + T peek() override { + LOGE("peek not implmented"); + return 0; + } + + // checks if the buffer is full + bool isFull() override { + return xStreamBufferIsFull(xStreamBuffer) == pdTRUE; + } + + bool isEmpty() { return xStreamBufferIsEmpty(xStreamBuffer) == pdTRUE; } + + // write add an entry to the buffer + bool write(T data) override { + int len = sizeof(T); + return writeArray(&data, len) == len; + } + + // clears the buffer + void reset() override { xStreamBufferReset(xStreamBuffer); } + + // provides the number of entries that are available to read + int available() override { + return xStreamBufferBytesAvailable(xStreamBuffer); + } + + // provides the number of entries that are available to write + int availableForWrite() override { + return xStreamBufferSpacesAvailable(xStreamBuffer); + } + + // returns the address of the start of the physical read buffer + T *address() override { + LOGE("address() not implemented"); + return nullptr; + } + +protected: + StreamBufferHandle_t xStreamBuffer; + BaseType_t xHigherPriorityTaskWoken = pdFALSE; // Initialised to pdFALSE. + int readWait = portMAX_DELAY; + int writeWait = portMAX_DELAY; + bool read_from_isr = false; + bool write_from_isr = false; + size_t current_size=0; + size_t trigger_level=0; +}; +#endif // ESP_IDF_VERSION_MAJOR >= 4 +#endif // ESP32 + +} // namespace audio_tools + +#endif + diff --git a/src/AudioTools/CoreAudio/VolumeControl.h b/src/AudioTools/VolumeControl.h similarity index 91% rename from src/AudioTools/CoreAudio/VolumeControl.h rename to src/AudioTools/VolumeControl.h index 0b4e381bda..f9e0f29717 100644 --- a/src/AudioTools/CoreAudio/VolumeControl.h +++ b/src/AudioTools/VolumeControl.h @@ -1,7 +1,5 @@ #pragma once -#include "AudioTools/CoreAudio/AudioTypes.h" - /** * @defgroup volume Volume * @ingroup dsp @@ -56,12 +54,13 @@ class CachedVolumeControl : public VolumeControl { /// determines a multiplication factor (0.0 to 1.0) from an input value (0.0 to 1.0). virtual float getVolumeFactor(float volume) { if (p_vc==nullptr) return 1.0f; // prevent NPE - if (fabs(volume-in)<0.01f){ + if (abs(volume-in)<0.01f){ + return out; + } else { + in = volume; + out = p_vc->getVolumeFactor(volume); return out; - } - in = volume; - out = p_vc->getVolumeFactor(volume); - return out; + } } protected: @@ -87,9 +86,9 @@ class LogarithmicVolumeControl : public VolumeControl { // provides a factor in the range of 0.0 to 1.0 virtual float getVolumeFactor(float input) { - float b = powf(((1/ym)-1), 2); + float b = pow(((1/ym)-1), 2); float a = 1.0f / (b - 1.0f); - float volumeFactor = powf(b,input) * a - a; + float volumeFactor = pow(b,input) * a - a; return limit(volumeFactor); } @@ -130,9 +129,9 @@ class SimulatedAudioPot : public VolumeControl { virtual float getVolumeFactor(float volume) { float result = 0; if (volume<=x){ - result = mapT(volume, 0.0, x, 0, y ); + result = mapFloat(volume, 0.0, x, 0, y ); } else { - result = mapT(volume, x, 1.0, y, 1.0); + result = mapFloat(volume, x, 1.0, y, 1.0); } return limit(result); } diff --git a/src/AudioTools/CoreAudio/VolumeStream.h b/src/AudioTools/VolumeStream.h similarity index 64% rename from src/AudioTools/CoreAudio/VolumeStream.h rename to src/AudioTools/VolumeStream.h index 3cce4d08e6..c024223e04 100644 --- a/src/AudioTools/CoreAudio/VolumeStream.h +++ b/src/AudioTools/VolumeStream.h @@ -1,9 +1,8 @@ #pragma once -#include "AudioTools/CoreAudio/AudioStreams.h" -#include "AudioTools/CoreAudio/AudioOutput.h" -#include "AudioTools/CoreAudio/VolumeControl.h" -#include "AudioTools/CoreAudio/AudioTypes.h" +#include "AudioTools/AudioStreams.h" +#include "AudioTools/AudioOutput.h" +#include "AudioTools/VolumeControl.h" namespace audio_tools { @@ -27,18 +26,17 @@ struct VolumeStreamConfig : public AudioInfo { * bits per sample and number of channels! * AudioChanges are forwareded to the related Print or Stream class. * @ingroup transform - * @ingroup volume * @author Phil Schatzmann * @copyright GPLv3 */ -class VolumeStream : public ModifyingStream, public VolumeSupport { +class VolumeStream : public AudioStream { public: /// Default Constructor VolumeStream() = default; /// Constructor which assigns Print output VolumeStream(Print &out) { - setOutput(out); + setTarget(out); } /// Constructor which assigns Stream input or output @@ -49,37 +47,32 @@ class VolumeStream : public ModifyingStream, public VolumeSupport { /// Constructor which assigns Print output VolumeStream(AudioOutput &out) { Print *p_print = &out; - setOutput(*p_print); - addNotifyAudioChange(out); + setTarget(*p_print); + p_notify = (AudioInfoSupport*) &out; } /// Constructor which assigns Stream input or output VolumeStream(AudioStream &io) { Stream *p_stream = &io; setStream(*p_stream); - addNotifyAudioChange(io); - } - - /// Defines/Changes the input & output - void setStream(Stream &in) override { - p_in = ∈ - p_out = p_in; + p_notify = (AudioInfoSupport *)&io; } /// Defines/Changes the output target - void setOutput(Print &out) override { + void setTarget(Print &out){ p_out = &out; } - + /// same as setStream - void setOutput(Stream &in){ + void setTarget(Stream &in){ p_in = ∈ p_out = p_in; } - /// same as set Output - void setStream(Print &out){ - p_out = &out; + /// Defines/Changes the input & output + void setStream(Stream &in){ + p_in = ∈ + p_out = p_in; } VolumeStreamConfig defaultConfig() { @@ -129,26 +122,26 @@ class VolumeStream : public ModifyingStream, public VolumeSupport { } /// Read raw PCM audio data, which will be the input for the volume control - virtual size_t readBytes(uint8_t *data, size_t len) override { + virtual size_t readBytes(uint8_t *buffer, size_t length) override { TRACED(); - if (data==nullptr || p_in==nullptr){ + if (buffer==nullptr || p_in==nullptr){ LOGE("NPE"); return 0; } - size_t result = p_in->readBytes(data, len); - if (isVolumeUpdate()) applyVolume(data, result); + size_t result = p_in->readBytes(buffer, length); + if (is_started) applyVolume(buffer, result); return result; } /// Writes raw PCM audio data, which will be the input for the volume control - virtual size_t write(const uint8_t *data, size_t len) override { - LOGD("VolumeStream::write: %zu", len); - if (data==nullptr || p_out==nullptr){ + virtual size_t write(const uint8_t *buffer, size_t size) override { + TRACED(); + if (buffer==nullptr || p_out==nullptr){ LOGE("NPE"); return 0; } - if (isVolumeUpdate()) applyVolume(data,len); - return p_out->write(data, len); + if (is_started) applyVolume(buffer,size); + return p_out->write(buffer, size); } /// Provides the nubmer of bytes we can write @@ -165,7 +158,9 @@ class VolumeStream : public ModifyingStream, public VolumeSupport { void setAudioInfo(AudioInfo cfg) override { TRACED(); // pass on notification - notifyAudioChange(cfg); + if (p_notify!=nullptr){ + p_notify->setAudioInfo(cfg); + } if (is_started){ VolumeStreamConfig cfg1 = setupAudioInfo(cfg); @@ -175,58 +170,32 @@ class VolumeStream : public ModifyingStream, public VolumeSupport { } } - /// Defines the volume for all channels: needs to be in the range of 0 to 1.0 (if allow boost has not been set) - bool setVolume(float vol) override { - bool result = true; + /// Defines the volume for all channels: needs to be in the range of 0 to 1.0 + void setVolume(float vol){ // just to make sure that we have a valid start volume before begin info.volume = vol; for (int j=0;j 1.0f && !info.allow_boost) || vol < 0.0f) { - LOGE("Invalid volume: %f", vol); - return false; - } - if (channel < info.channels){ + void setVolume(float vol, int channel){ + if (channel 4.0) factor = 4.0;//factor can only be >1 if allow_boost == true TODO: should we update volume_values[channel] if factor got clipped to 4.0? - uint8_t factorF2P6 = (uint8_t) (factor*(1<<6)); - factor_for_channel[channel] = factorF2P6; - #else - factor_for_channel[channel]=factor; - #endif - } - return true; + LOGI("setVolume: %f", volume_value); + float factor = volumeControl().getVolumeFactor(volume_value); + volume_values[channel]=volume_value; + factor_for_channel[channel]=factor; } else { LOGE("Invalid channel %d - max: %d", channel, info.channels-1); - return false; } } - /// Provides the current (avg) volume accross all channels - float volume() override { - // prevent npe - if (volume_values.size()==0) return 0; - // calculate avg - float total = 0; - int cnt = volume_values.size(); - for (int j=0; j(cnt); + /// Provides the current volume setting + float volume() { + return volume_values.size()==0? 0: volume_values[0]; } /// Provides the current volume setting for the indicated channel @@ -242,28 +211,10 @@ class VolumeStream : public ModifyingStream, public VolumeSupport { SimulatedAudioPot pot_vc; CachedVolumeControl cached_volume{pot_vc}; Vector volume_values; - #if PREFER_FIXEDPOINT - Vector factor_for_channel; //Fixed point 2.6 - #else - Vector factor_for_channel; - #endif + Vector factor_for_channel; bool is_started = false; float max_value = 32767; // max value for clipping - int max_channels = 0; - - // checks if volume needs to be updated - bool isVolumeUpdate(){ - if (!is_started) return false; - if (isAllChannelsFullVolume()) return false; - return true; - } - - bool isAllChannelsFullVolume(){ - for (int ch=0;ch> 6; //Fixedpoint-Math from https://github.com/earlephilhower/ESP8266Audio/blob/0abcf71012f6128d52a6bcd155ed1404d6cc6dcd/src/AudioOutput.h#L67 - #else float result = factorForChannel(j%info.channels) * data[j]; - #endif if (!info.allow_boost){ if (result>max_value) result = max_value; if (result<-max_value) result = -max_value; @@ -346,11 +289,7 @@ class VolumeStream : public ModifyingStream, public VolumeSupport { void applyVolume24(int24_t* data, size_t size) { for (size_t j=0;j> 6; //8bits * 24bits = fits into 32 - #else float result = factorForChannel(j%info.channels) * data[j]; - #endif if (!info.allow_boost){ if (result>max_value) result = max_value; if (result<-max_value) result = -max_value; @@ -362,11 +301,7 @@ class VolumeStream : public ModifyingStream, public VolumeSupport { void applyVolume32(int32_t* data, size_t size) { for (size_t j=0;j(data[j]) * static_cast(factorForChannel(j%info.channels))) >> 6; - #else float result = factorForChannel(j%info.channels) * data[j]; - #endif if (!info.allow_boost){ if (result>max_value) result = max_value; if (result<-max_value) result = -max_value; diff --git a/src/AudioToolsConfig.h b/src/AudioToolsConfig.h deleted file mode 100644 index 37383789b5..0000000000 --- a/src/AudioToolsConfig.h +++ /dev/null @@ -1,902 +0,0 @@ -/** - * @author Phil Schatzmann - * @brief AutioTools Configuration - * @copyright GPLv3 - * - */ -#pragma once - -#define AUDIOTOOLS_VERSION "1.0.1" -#define AUDIOTOOLS_MAJOR_VERSION 1 -#define AUDIOTOOLS_MIOR_VERSION 0 - - -#if defined(IS_MIN_DESKTOP) -# include "AudioTools/AudioLibs/Desktop/NoArduino.h" -# include "AudioTools/AudioLibs/Desktop/Time.h" -# include "AudioTools/AudioLibs/Desktop/Main.h" -# define USE_STREAM_READ_OVERRIDE -# ifndef EXIT_ON_STOP -# define EXIT_ON_STOP -# endif -#elif defined(IS_DESKTOP_WITH_TIME_ONLY) -# include "AudioTools/AudioLibs/Desktop/Time.h" -# include "AudioTools/AudioLibs/Desktop/NoArduino.h" -# ifndef EXIT_ON_STOP -# define EXIT_ON_STOP -# endif -#elif defined(IS_DESKTOP) -# include "Arduino.h" -# include -# include -# define USE_WIFI -# define USE_URL_ARDUINO -# define USE_STREAM_WRITE_OVERRIDE -# define USE_STREAM_READ_OVERRIDE -# define USE_STREAM_READCHAR_OVERRIDE -# ifndef EXIT_ON_STOP -# define EXIT_ON_STOP -# endif -//# define USE_3BYTE_INT24 -typedef WiFiClient WiFiClientSecure; -#elif defined(ARDUINO) -# include "Arduino.h" -// --- ESP32 ------------ -// E.g when using the Espressif IDF. Use cmake for the necesseary defines -#elif defined(ESP32_CMAKE) -# define ESP32 -# include "esp_idf_version.h" -# include "AudioTools/AudioLibs/Desktop/NoArduino.h" -#else -# include "AudioTools/AudioLibs/Desktop/NoArduino.h" -# define IS_JUPYTER -# define USE_STREAM_READ_OVERRIDE -#endif - -#include -#include -#include -#include "AudioTools/CoreAudio/AudioRuntime.h" - - -// If you don't want to use all the settings from here you can define your own local config settings in AudioConfigLocal.h -#if __has_include("AudioConfigLocal.h") -#include "AudioConfigLocal.h" -#endif - -// Automatically include all core audio functionality -#ifndef AUDIO_INCLUDE_CORE -# define AUDIO_INCLUDE_CORE true -#endif - -// Use fixed point multiplication instead float for VolumeStream for slightly better performance on platforms without float hardware. Tested on RP2040 at 16 bit per second (still too slow for 32bit) -#ifndef PREFER_FIXEDPOINT -# define PREFER_FIXEDPOINT false -#endif - -// Add automatic using namespace audio_tools; -#ifndef USE_AUDIOTOOLS_NS -# define USE_AUDIOTOOLS_NS true -#endif - -/** - * @brief Logging - * Logging Configuration in Arduino -> set USE_AUDIO_LOGGING to false if you want to deactivate Logging. - * When using cmake you can set -DUSE_AUDIO_LOGGING=false - * You can also change the LOG_LEVEL and LOG_STREAM here. - * However it is recommended to do it in your Sketch e.g with AudioLogger::instance().begin(Serial,AudioLogger::Warning); - */ - -#ifndef USE_AUDIO_LOGGING -# define USE_AUDIO_LOGGING true -#endif - -#ifndef LOG_LEVEL -# define LOG_LEVEL AudioLogger::Warning -#endif - -#ifndef LOG_STREAM -# define LOG_STREAM Serial -#endif - -#ifndef LOG_PRINTF_BUFFER_SIZE -# define LOG_PRINTF_BUFFER_SIZE 303 -#endif - -#ifndef LOG_METHOD -# define LOG_METHOD __PRETTY_FUNCTION__ -#endif - -// cheange USE_CHECK_MEMORY to true to activate memory checks -#ifndef USE_CHECK_MEMORY -# define USE_CHECK_MEMORY false -#endif - - -// Activate/deactivate obsolete functionality -#ifndef USE_OBSOLETE -# define USE_OBSOLETE false -#endif - -/** - * @brief Common Default Settings that can usually be changed in the API - */ - -#ifndef DEFAULT_BUFFER_SIZE -# define DEFAULT_BUFFER_SIZE 1024 -#endif - -#ifndef DEFAULT_SAMPLE_RATE -# define DEFAULT_SAMPLE_RATE 44100 -#endif - -#ifndef DEFAULT_CHANNELS -# define DEFAULT_CHANNELS 2 -#endif - -#ifndef DEFAULT_BITS_PER_SAMPLE -# define DEFAULT_BITS_PER_SAMPLE 16 -#endif - -#ifndef I2S_DEFAULT_PORT -# define I2S_DEFAULT_PORT 0 -#endif - -#ifndef I2S_BUFFER_SIZE -# define I2S_BUFFER_SIZE 512 -#endif - -#ifndef I2S_BUFFER_COUNT -# define I2S_BUFFER_COUNT 6 // 20 -#endif - -#ifndef ANALOG_BUFFER_SIZE -# define ANALOG_BUFFER_SIZE 512 -#endif - -#ifndef ANALOG_BUFFER_COUNT -# define ANALOG_BUFFER_COUNT 6 // 20 -#endif - -#ifndef A2DP_BUFFER_SIZE -# define A2DP_BUFFER_SIZE 512 -#endif - -#ifndef A2DP_BUFFER_COUNT -# define A2DP_BUFFER_COUNT 30 -#endif - -#ifndef CODEC_DELAY_MS -# define CODEC_DELAY_MS 10 -#endif - -#ifndef COPY_DELAY_ON_NODATA -# define COPY_DELAY_ON_NODATA 10 -#endif - -#ifndef COPY_RETRY_LIMIT -# define COPY_RETRY_LIMIT 20 -#endif - -#ifndef MAX_SINGLE_CHARS -# define MAX_SINGLE_CHARS 8 -#endif - -// max size of http processing buffer -#ifndef HTTP_MAX_LEN -# define HTTP_MAX_LEN 1024 -#endif - -// max size of chunked size line -#ifndef HTTP_CHUNKED_SIZE_MAX_LEN -# define HTTP_CHUNKED_SIZE_MAX_LEN 80 -#endif - - -#ifndef USE_RESAMPLE_BUFFER -# define USE_RESAMPLE_BUFFER true -#endif - -/** - * @brief PWM - */ -#ifndef PWM_BUFFER_SIZE -# define PWM_BUFFER_SIZE 1024 -#endif - -#ifndef PWM_BUFFER_COUNT -# define PWM_BUFFER_COUNT 4 -#endif - -#ifndef PWM_AUDIO_FREQUENCY -# define PWM_AUDIO_FREQUENCY 30000 -#endif - -// Activate Networking for All Processors -// #define USE_ETHERNET -// #define USE_AUDIO_SERVER -// #define USE_URL_ARDUINO - -/** - * ------------------------------------------------------------------------- - * @brief Platform specific Settings - */ -//-------ESP32--------- -#if defined(ESP32) && defined(CONFIG_IDF_TARGET_ESP32C3) -# define ESP32C3 -# define ESP32X -# define USE_INT24_FROM_INT -# define USE_TDM -# define USE_PDM -#endif -#if defined(ESP32) && defined(CONFIG_IDF_TARGET_ESP32S2) -# define ESP32S2 -# define ESP32X -#endif -#if defined(ESP32) && defined(CONFIG_IDF_TARGET_ESP32S3) -# define ESP32S3 -# define ESP32X -# define USE_TDM -# define USE_PDM -# define USE_PDM_RX -#endif -#if defined(ESP32) && defined(CONFIG_IDF_TARGET_ESP32C6) -# define ESP32C6 -# define ESP32X -# define USE_TDM -# define USE_PDM -#endif -#if defined(ESP32) && defined(CONFIG_IDF_TARGET_ESP32P4) -# define ESP32P4 -# define ESP32X -# define USE_TDM -# define USE_PDM -# define USE_PDM_RX -#endif - -// for all ESP32 families -#if defined(ESP32) -# define USE_STRTOD -// We need to use accept instead of available -# if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 0, 0) -# define USE_SERVER_ACCEPT true -# endif -# if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(4, 0, 0) -# define USE_CONCURRENCY -// Print has been fixed -# define USE_PRINT_FLUSH true -# else -# define USE_PRINT_FLUSH false -# endif -# define USE_SD_SUPPORTS_SPI -//# define USE_ESP32_LOGGER true -# if !defined(ARDUINO) -# define USE_IDF_LOGGER -# endif -# if !defined(I2S_USE_APLL) -# define I2S_USE_APLL false -# endif -#endif - -// ----- Regular ESP32 ----- -#if defined(ESP32) && !defined(ESP32X) && !defined(CONFIG_IDF_TARGET_ESP32H2) -#if ESP_IDF_VERSION > ESP_IDF_VERSION_VAL(5, 0 , 0) -# define USE_INT24_FROM_INT -#endif - -#define USE_ANALOG -#define USE_I2S -#define USE_PDM_RX - -#ifdef ARDUINO -# define USE_PWM -# define USE_WIFI -# define USE_WIFI_CLIENT_SECURE -# define USE_URL_ARDUINO -# define USE_AUDIO_SERVER -# define USE_TIMER -# define USE_TOUCH_READ -#endif - -#define USE_TYPETRAITS -#define USE_STREAM_WRITE_OVERRIDE -#define USE_STREAM_READ_OVERRIDE -#define USE_EXT_BUTTON_LOGIC -// support for psram -> set to true -#define USE_ALLOCATOR true -#define HAS_IOSTRAM -#define USE_TASK false - -#define PWM_FREQENCY 30000 -#define PIN_PWM_START 12 -#define PIN_I2S_BCK 14 -#define PIN_I2S_WS 15 -#define PIN_I2S_DATA_IN 32 -#define PIN_I2S_DATA_OUT 22 -#define PIN_I2S_MCK -1 -// Default Setting: The mute pin can be switched actovated by setting it to a gpio (e.g 23). Or you could drive the LED by assigning LED_BUILTIN -#define PIN_I2S_MUTE -1 -#define SOFT_MUTE_VALUE 0 -#define PIN_CS SS -#define PIN_ADC1 34 - -#define I2S_AUTO_CLEAR true - -// URLStream -#define URL_STREAM_CORE 0 -#define URL_STREAM_PRIORITY 2 -#define URL_STREAM_BUFFER_COUNT 10 -#define STACK_SIZE 30000 -#define URL_CLIENT_TIMEOUT 60000; -#define URL_HANDSHAKE_TIMEOUT 120000 - -// // Default LED -// #ifndef LED_BUILTIN -// # define LED_BUILTIN 13 // pin number is specific to your esp32 board -// #endif - -// support for old idf releases -#if ESP_IDF_VERSION_MAJOR < 4 && !defined(I2S_COMM_FORMAT_STAND_I2S) -# define I2S_COMM_FORMAT_STAND_I2S (I2S_COMM_FORMAT_I2S | I2S_COMM_FORMAT_I2S_MSB) -# define I2S_COMM_FORMAT_STAND_MSB (I2S_COMM_FORMAT_I2S | I2S_COMM_FORMAT_I2S_LSB) -# define I2S_COMM_FORMAT_STAND_PCM_LONG (I2S_COMM_FORMAT_PCM | I2S_COMM_FORMAT_PCM_LONG) -# define I2S_COMM_FORMAT_STAND_PCM_SHORT (I2S_COMM_FORMAT_PCM | I2S_COMM_FORMAT_PCM_SHORT) - -typedef int eps32_i2s_sample_rate_type; -#else -typedef uint32_t eps32_i2s_sample_rate_type; -#endif - -#endif - -//-------ESP32C3, ESP32S3, ESP32S2--------- - -#if defined(ESP32) && defined(ESP32X) -# ifdef ARDUINO -# include "esp32-hal-log.h" -# endif -# if ESP_IDF_VERSION > ESP_IDF_VERSION_VAL(5, 0 , 0) -# define USE_INT24_FROM_INT -# define USE_ANALOG -# endif - -#define USE_PWM -#define USE_URL_ARDUINO -#define USE_WIFI -#define USE_WIFI_CLIENT_SECURE -#define USE_I2S -#define USE_AUDIO_SERVER -#define USE_TYPETRAITS -#define USE_TIMER -#define USE_STREAM_WRITE_OVERRIDE -#define USE_STREAM_READ_OVERRIDE -// support for psram -> set to true -#define USE_ALLOCATOR true -//#define USE_INITIALIZER_LIST - -#define PWM_FREQENCY 30000 -#define PIN_PWM_START 1 -#define PIN_I2S_MCK -1 -#define PIN_I2S_BCK 6 -#define PIN_I2S_WS 7 -#define PIN_I2S_DATA_OUT 8 -#define PIN_I2S_DATA_IN 9 -// Default Setting: The mute pin can be switched actovated by setting it to a gpio (e.g 5). Or you could drive the LED by assigning LED_BUILTIN -#define PIN_I2S_MUTE -1 -#define SOFT_MUTE_VALUE 0 -#define PIN_CS SS -#define PIN_ADC1 21 - -#define I2S_AUTO_CLEAR true - -// URLStream -//#define USE_ESP8266_AUDIO -#define URL_STREAM_CORE 0 -#define URL_STREAM_PRIORITY 2 -#define URL_STREAM_BUFFER_COUNT 10 -#define STACK_SIZE 30000 -#define URL_CLIENT_TIMEOUT 60000; -#define URL_HANDSHAKE_TIMEOUT 120000 - -// // Default LED -// #ifndef LED_BUILTIN -// # define LED_BUILTIN 13 // pin number is specific to your esp32 board -// #endif - -typedef uint32_t eps32_i2s_sample_rate_type; - -#endif - -//-------ESP32H2--------- - -#if defined(ESP32) && defined(CONFIG_IDF_TARGET_ESP32H2) -#include "esp32-hal-log.h" -#if ESP_IDF_VERSION > ESP_IDF_VERSION_VAL(5, 0 , 0) -# define USE_INT24_FROM_INT -# define USE_ANALOG -#endif - -#define ESP32H2 -#define USE_TDM -#define USE_PWM -#define USE_I2S -#define USE_PDM -#define USE_TYPETRAITS -#define USE_TIMER -#define USE_STREAM_WRITE_OVERRIDE -#define USE_STREAM_READ_OVERRIDE -// support for psram -> set to true -#define USE_ALLOCATOR true -//#define USE_INITIALIZER_LIST - -#define PWM_FREQENCY 30000 -#define PIN_PWM_START 1 -#define PIN_I2S_MCK -1 -#define PIN_I2S_BCK 6 -#define PIN_I2S_WS 7 -#define PIN_I2S_DATA_OUT 8 -#define PIN_I2S_DATA_IN 9 -// Default Setting: The mute pin can be switched actovated by setting it to a gpio (e.g 5). Or you could drive the LED by assigning LED_BUILTIN -#define PIN_I2S_MUTE -1 -#define SOFT_MUTE_VALUE 0 -#define PIN_CS SS -#define PIN_ADC1 21 -#define I2S_AUTO_CLEAR true - -typedef uint32_t eps32_i2s_sample_rate_type; - -#endif - - -//----- ESP8266 ----------- -#ifdef ESP8266 -# include -//#define USE_URL_ARDUINO // commented out because of compile errors -#define USE_I2S -#define USE_TYPETRAITS -#define USE_TIMER -#define USE_WIFI -#define USE_AUDIO_SERVER -#define USE_URL_ARDUINO - -#define PIN_PWM_START 12 -#define PIN_I2S_BCK -1 -#define PIN_I2S_WS -1 -#define PIN_I2S_DATA_IN -1 -#define PIN_I2S_DATA_OUT -1 -#define I2S_USE_APLL false -#define PIN_I2S_MUTE 23 -#define SOFT_MUTE_VALUE 0 -#define PIN_CS SS -#define USE_SERVER_ACCEPT 1 - -#define URL_CLIENT_TIMEOUT 60000; -#define URL_HANDSHAKE_TIMEOUT 120000 -#define USE_SD_SUPPORTS_SPI - -#endif - -//------ NANO33BLE ---------- -#if (defined(ARDUINO_SEEED_XIAO_NRF52840_SENSE) || defined(ARDUINO_ARDUINO_NANO33BLE) || defined(ARDUINO_ARCH_MBED_NANO)) && !defined(ARDUINO_ARCH_ZEPHYR) -#define USE_NANO33BLE -#define USE_INT24_FROM_INT -#define USE_I2S -#define USE_PWM -#define USE_TYPETRAITS -#define USE_TIMER -//#define USE_INITIALIZER_LIST -#define USE_ALT_PIN_SUPPORT - -#define PIN_PWM_START 5 -#define PIN_I2S_BCK 2 -#define PIN_I2S_WS 3 -#define PIN_I2S_DATA_IN 4 -#define PIN_I2S_DATA_OUT 4 -// Default Setting: The mute pin can be switched actovated by setting it to a gpio (e.g 4). Or you could drive the LED by assigning LED_BUILTIN -#define PIN_I2S_MUTE -1 -#define SOFT_MUTE_VALUE 0 -#define PIN_CS SS -#endif - -//----- RP2040 MBED ----------- -#if defined(ARDUINO_ARCH_MBED_RP2040) -// install https://github.com/pschatzmann/rp2040-i2s -#define RP2040_MBED -#define USE_I2S 1 -#define USE_PWM -#define USE_ANALOG_ARDUINO -#define USE_TYPETRAITS -#define USE_TIMER -#define USE_INT24_FROM_INT - -#define PIN_ANALOG_START 26 -#define PIN_PWM_START 6 -#define PIN_I2S_BCK 26 -#define PIN_I2S_WS PIN_I2S_BCK+1 -#define PIN_I2S_DATA_IN 28 -#define PIN_I2S_DATA_OUT 28 -// Default Setting: The mute pin can be switched actovated by setting it to a gpio (e.g 4). Or you could drive the LED by assigning LED_BUILTIN -#define PIN_I2S_MUTE -1 -#define SOFT_MUTE_VALUE 0 -#define PIN_CS 1 //PIN_SPI0_SS - -// fix missing __sync_synchronize symbol -#define FIX_SYNC_SYNCHRONIZE -#define IRAM_ATTR -#ifndef ANALOG_BUFFER_SIZE -#define ANALOG_BUFFER_SIZE 1024 -#endif - -#ifndef ANALOG_BUFFERS -#define ANALOG_BUFFERS 50 -#endif - -//#define USE_ESP8266_AUDIO - -//----- RP2040 ----------- -#elif defined(ARDUINO_ARCH_RP2040) -#define RP2040_HOWER -#define USE_SD_NO_NS -#define USE_I2S -#define USE_PWM -#define USE_ANALOG_ARDUINO -#define USE_TYPETRAITS -#define USE_TIMER -#define USE_INT24_FROM_INT - -#define PIN_ANALOG_START 26 -#define PIN_PWM_START 6 -#define PIN_I2S_BCK 26 -#define PIN_I2S_WS PIN_I2S_BCK+1 -#define PIN_I2S_DATA_IN 28 -#define PIN_I2S_DATA_OUT 28 -#define PIN_I2S_MCK -1 -// Default Setting: The mute pin can be switched actovated by setting it to a gpio (e.g 4). Or you could drive the LED by assigning LED_BUILTIN -#define PIN_I2S_MUTE -1 -#define SOFT_MUTE_VALUE 0 -#define PIN_CS PIN_SPI0_SS -#define USE_SERVER_ACCEPT true - -// fix missing __sync_synchronize symbol -//#define FIX_SYNC_SYNCHRONIZE -#define IRAM_ATTR - -#ifndef ANALOG_BUFFER_SIZE -#define ANALOG_BUFFER_SIZE 256 -#endif - -#ifndef ANALOG_BUFFERS -#define ANALOG_BUFFERS 100 -#endif - -//#define USE_CONCURRENCY -#define USE_SD_SUPPORTS_SPI - -// default pins for VS1053 shield -#define VS1053_CS 17 -#define VS1053_DCS 9 -#define VS1053_DREQ 10 -#define VS1053_CS_SD -1 -#define VS1053_RESET 11 -#define VS1053_DEFINED -#endif - -// The Pico W has WIFI support: but platformio is messing up, so we support NO_WIFI -#if (defined(ARDUINO_NANO_RP2040_CONNECT) || defined(ARDUINO_RASPBERRY_PI_PICO_W) || defined(ARDUINO_ARCH_RP2040)) && (LWIP_IPV4==1 || LWIP_IPV6==1) &&!defined(NO_WIFI) -# include -# define USE_WIFI -# define USE_WIFI_CLIENT_SECURE -# define USE_URL_ARDUINO -# define USE_AUDIO_SERVER -using WiFiServerSecure = BearSSL::WiFiServerSecure; -#endif - - -//----- AVR ----------- -#ifdef __AVR__ -#define USE_SD_NO_NS -#define USE_PWM -#define USE_TIMER -#define NO_INPLACE_INIT_SUPPORT -// Uncomment to activate network -//#include -//#define USE_URL_ARDUINO -#ifndef assert -# define assert(T) -#endif - -#define PIN_PWM_START 6 -#define PIN_CS SS - -#undef PWM_BUFFER_SIZE -#define PWM_BUFFER_SIZE 125 - -#undef DEFAULT_BUFFER_SIZE -#define DEFAULT_BUFFER_SIZE 125 - -// logging is using too much memory -#undef USE_AUDIO_LOGGING -#undef LOG_PRINTF_BUFFER_SIZE -#define LOG_PRINTF_BUFFER_SIZE 60 -#define NO_TRACED -#define NO_TRACEI -#define NO_TRACEE - -// we use spi to emulate i2s -#define PIN_I2S_BCK 13 -#define PIN_I2S_WS 10 -#define PIN_I2S_DATA_IN 12 -#define PIN_I2S_DATA_OUT 11 -#define PIN_I2S_MUTE -1 - -#endif - - -//---- STM32 ------------ -#if defined(ARDUINO_ARCH_STM32) -#define STM32 -#endif - -#ifdef STM32 -#define USE_I2S -#define USE_PWM -#define USE_TIMER -#define USE_ANALOG -#define USE_ANALOG_ARDUINO -#define USE_INT24_FROM_INT - -#define PIN_ANALOG_START PA0 -#define PIN_PWM_START PA0 -#define PWM_DEFAULT_TIMER TIM2 -#define PWM_FREQ_TIMER_NO 3 -#define USE_SD_NO_NS - -#define PIN_I2S_BCK -1 -#define PIN_I2S_WS -1 -#define PIN_I2S_DATA_IN -1 -#define PIN_I2S_DATA_OUT -1 -#define PIN_I2S_MUTE -1 -#define SOFT_MUTE_VALUE 0 -#define PIN_CS -1 - -#define USE_ETHERNET -#define USE_URL_ARDUINO -#define USE_AUDIO_SERVER - - -#endif - -//---- SAMD ------------ - -#ifdef ARDUINO_ARCH_SAMD -#define NO_INPLACE_INIT_SUPPORT -#ifndef SEEED_XIAO_M0 -# define USE_I2S -#endif -#define USE_INT24_FROM_INT -#define PIN_I2S_BCK 1 -#define PIN_I2S_WS PIN_I2S_BCK+1 -#define PIN_I2S_DATA_IN 3 -#define PIN_I2S_DATA_OUT 3 -#define PIN_I2S_MUTE -1 -#define SOFT_MUTE_VALUE 0 -#define USE_SD_NO_NS -#define PIN_CS 4 -#endif - -#ifdef ARDUINO_SAMD_MKRWIFI1010 -#include -#define USE_URL_ARDUINO -#define USE_AUDIO_SERVER -#endif - -//---- GIGA ------------ -// DRAFT Support - Not tested ! -#if defined(ARDUINO_GIGA) -#include -#include -#define IS_MBED -#define USE_INT24_FROM_INT -#define USE_TYPETRAITS -#define USE_ANALOG -#define USE_STREAM_WRITE_OVERRIDE -#define ANALOG_BUFFER_SIZE 1024 -#define ANALOG_BUFFERS 10 -#define USE_URL_ARDUINO -#define USE_AUDIO_SERVER - -#define PIN_ANALOG_START A7 -#define PIN_DAC_1 A12 -#define PIN_DAC_2 A13 - -#endif - -// //---- Portenta ------------ -// // DRAFT: not tested -#if defined(ARDUINO_ARCH_MBED_PORTENTA) -#include -#include -#define IS_MBED -#define USE_INT24_FROM_INT -#define USE_TYPETRAITS -#define USE_ANALOG -#define USE_TIMER -#define USE_PWM -#define USE_STREAM_WRITE_OVERRIDE -#define ANALOG_BUFFER_SIZE 1024 -#define ANALOG_BUFFERS 10 -#define USE_URL_ARDUINO -#define USE_AUDIO_SERVER - -#define PIN_ANALOG_START A0 -#define PIN_PWM_START D2 -#define PIN_DAC_1 D0 -#define PIN_DAC_2 D1 -#endif - -//------ RENESAS ---------- -// Arduino UNO R4 -#if defined(ARDUINO_ARCH_RENESAS) || defined(_RENESAS_RA_) -// no trace to save on memory -#define NO_TRACE -#define LOG_NO_MSG // around 4K less - -#define USE_INT24_FROM_INT -#define IS_RENESAS 1 -#define USE_TYPETRAITS -#define USE_TIMER -#define USE_PWM -#define PIN_PWM_START D2 -#define PIN_PWM_COUNT 12 -#define USE_ANALOG -#define USE_ANALOG_ARDUINO -#define USE_SD_NO_NS -#define PIN_ANALOG_START A0 -#define ANALOG_BUFFER_SIZE 512 -#define ANALOG_BUFFERS 5 -#define ANALOG_MAX_OUT_CHANNELS 1 -#define ANALOG_MAX_SAMPLE_RATE 16000 -// default pins for UNO VS1053 shield -#define VS1053_CS 6 -#define VS1053_DCS 7 -#define VS1053_DREQ 2 -#define VS1053_CS_SD 9 -#define VS1053_RESET 8 -#define VS1053_DEFINED -#define PIN_CS 9 - -#if defined(ARDUINO) && !defined(ARDUINO_MINIMA) -# define USE_WIFI -# define USE_URL_ARDUINO -# define USE_AUDIO_SERVER -# include "WiFiS3.h" -#endif - -#endif - - -// ------ Zephyr ------- -#ifdef ARDUINO_ARCH_ZEPHYR -# define IS_ZEPHYR -# define NO_INPLACE_INIT_SUPPORT -# define USE_TYPETRAITS -# define USE_I2S -# define PIN_I2S_BCK 2 -# define PIN_I2S_WS 3 -# define PIN_I2S_DATA_IN 4 -# define PIN_I2S_DATA_OUT 4 -// Default Setting: The mute pin can be switched actovated by setting it to a gpio (e.g 4). Or you could drive the LED by assigning LED_BUILTIN -# define PIN_I2S_MUTE -1 -# define SOFT_MUTE_VALUE 0 -# define USE_NANO33BLE -# define USE_ALT_PIN_SUPPORT -#endif - -//------ VS1053 ---------- -// see https://github.com/pschatzmann/arduino-vs1053/wiki/Pinouts-for-Processors-and-Tested-Boards#microcontrollers -// Default Pins for VS1053 -#ifndef VS1053_DEFINED -# define VS1053_CS 5 -# define VS1053_DCS 16 -# define VS1053_DREQ 4 -# define VS1053_RESET 15 -# define VS1053_CS_SD -1 -#endif - -// use 0 for https://github.com/baldram/ESP_VS1053_Library -// use 1 for https://github.com/pschatzmann/arduino-vs1053 -#define VS1053_EXT 1 -#define VS1053_DEFAULT_VOLUME 0.7 - - -//---------------- -// Fallback defined if nothing was defined in the platform - -#ifndef ARDUINO -# define USE_STREAM_WRITE_OVERRIDE -#endif - -#ifndef ANALOG_MAX_SAMPLE_RATE -# define ANALOG_MAX_SAMPLE_RATE 44100 -#endif - -#ifndef PWM_MAX_SAMPLE_RATE -# define PWM_MAX_SAMPLE_RATE 8000 -#endif - - -#ifndef URL_CLIENT_TIMEOUT -# define URL_CLIENT_TIMEOUT 60000; -# define URL_HANDSHAKE_TIMEOUT 120000 -#endif - -#ifndef USE_TASK -# define USE_TASK false -#endif - -#ifndef USE_SERVER_ACCEPT -# define USE_SERVER_ACCEPT false -#endif - -#ifndef USE_ALLOCATOR -# define USE_ALLOCATOR false -#endif - -#ifndef USE_ESP32_LOGGER -# define USE_ESP32_LOGGER false -#endif - -// Standard Arduino Print provides flush function -#ifndef USE_PRINT_FLUSH -# define USE_PRINT_FLUSH true -#endif - -#ifndef ESP_IDF_VERSION_VAL -# define ESP_IDF_VERSION_VAL(a, b , c) 0 -#endif - -#if USE_CHECK_MEMORY -# define CHECK_MEMORY() checkMemory(true) -#else -# define CHECK_MEMORY() -#endif - -#pragma GCC diagnostic ignored "-Wunused-variable" -#pragma GCC diagnostic ignored "-Wunused-function" -#pragma GCC diagnostic ignored "-Wvla" -#pragma GCC diagnostic ignored "-Wsign-compare" -#pragma GCC diagnostic ignored "-Woverloaded-virtual" -#pragma GCC diagnostic ignored "-Wdouble-promotion" - -#ifdef USE_NO_MEMACCESS -#pragma GCC diagnostic ignored "-Wclass-memaccess" -#endif - -#ifdef USE_INITIALIZER_LIST -#pragma GCC diagnostic ignored "-Wnarrowing" -#endif - -#undef rewind - -// select int24 implementation -#include "AudioTools/CoreAudio/AudioBasic/Int24_3bytes_t.h" -#include "AudioTools/CoreAudio/AudioBasic/Int24_4bytes_t.h" -#include "AudioTools/CoreAudio/AudioBasic/FloatAudio.h" - -namespace audio_tools { - #ifdef USE_3BYTE_INT24 - using int24_t = audio_tools::int24_3bytes_t; - #else - using int24_t = audio_tools::int24_4bytes_t; - #endif -} - -/** - * ------------------------------------------------------------------------- - * @brief Set namespace - * - */ -#if USE_AUDIOTOOLS_NS -using namespace audio_tools; -#endif - diff --git a/src/Experiments/AudioDAC.h b/src/Experiments/AudioDAC.h new file mode 100644 index 0000000000..36e3beac2c --- /dev/null +++ b/src/Experiments/AudioDAC.h @@ -0,0 +1,672 @@ +#pragma once + +#include "AudioTools/AudioLogger.h" +#include "AudioTools/Buffers.h" +#include "AudioTimer/AudioTimer.h" +#include "AudioTools/AudioOutput.h" + +namespace audio_tools { + +// forward declaration +class OversamplingDAC; +volatile uint32_t output_frame_count = 0; + +/** + * @brief Config info for DeltaSigma DAC + * @author Phil Schatzmann + * @copyright GPLv3 + * + */ +class DACInfo : public AudioInfo { + public: + friend class OversamplingDAC; + + DACInfo() { + sample_rate = 44100; + channels = 2; + } + + /// By default we do not oversample + int oversample_factor = 1; + + /// Defines the pins: channel 0 is start_pin, channel 1 is start_pin+1 etc. + int start_pin = PIN_PWM_START; + + /// Max number of bits used to output signal + int output_bits = 64; + + /// Provides the update bit rate + uint32_t outputBitRate() { + const int bits = sizeof(int32_t) * 8; + return sample_rate * oversample_factor * bits; + } + + /// Provides the update frame rate + uint32_t outputSampleRate() { + return (uint32_t)sample_rate * oversample_factor; + } + + /// Logs the configuration settings to the console (if logging is active) + void logInfo(bool withPins=false) { + AudioInfo::logInfo(); + LOGI("oversample_factor: %d",oversample_factor ); + LOGI("output_bits: %d",output_bits ); + if (withPins){ + for (int j=0;jinfo = cfg; + + // check audio data + if (info.bits_per_sample!=16){ + LOGE("Only 16 Bits per sample are supported - you requested %d", info.bits_per_sample); + return false; + } + + if (info.sample_rate <= 0){ + LOGE("invalid sample_rate: %d", info.sample_rate); + return false; + } + + // setup memory + const int channels = info.channels; + current_values = new int32_t[channels]; + last_values = new int32_t[channels]; + cummulated_error = new int32_t[channels]; + startTimer(); + active = true; + return true; + } + + virtual uint32_t outputRate() = 0; + + /// Stops the output + virtual void end() { + TRACED(); + active = false; + reset(); + timer_object.end(); + } + + /// Writes a single byte (of audio data) to the output buffer + virtual size_t write(uint8_t c){ + int result = 1; + write_buffer[write_buffer_pos++] = c; + if (write_buffer_pos==4){ + result = write(write_buffer, 4); + if (result<=0){ + // write failed - so we need to trigger a re-write + write_buffer_pos=3; + } else{ + result = 1; + } + } + return result; + } + + /// Writes the audio data to the output buffer + virtual size_t write(const uint8_t *data, size_t size){ + TRACED(); + if (size==0) return 0; + int16_t *ptr = (int16_t *)data; + + // fill buffer with delta sigma data + int frames = std::min(size/(bytes_per_sample*info.channels), (size_t) availableFramesToWrite()); + while(is_blocking && frames==0){ + delay(10); + frames = std::min(size/(bytes_per_sample*info.channels), (size_t) availableFramesToWrite()); + } + int samples = frames * info.channels; + for (int j=0; j0){ + uint32_t timeUs = AudioTime::toTimeUs(outputRate()); + auto dacTimerCallback = [] (void *ptr)->void IRAM_ATTR { + output_frame_count++; + ((SimpleDAC*) ptr)->writePins(); + }; + timer_object.setCallbackParameter(this); + timer_object.begin(dacTimerCallback, timeUs, US); + LOGI("Timer started"); + } else { + LOGW("No output because output Rate <=0"); + } + } + + + protected: + RingBuffer buffer = RingBuffer(DEFAULT_BUFFER_SIZE); + int bit_counter = 0; + uint8_t *active_count=nullptr; + DACOut out; + + void writePins() { + int channels = info.channels; + int32_t current_values[channels]; + if (active && buffer.available()>=2){ + // determine number of hight bits + if (bit_counter==0){ + active_count[0]=buffer.read(); + active_count[1]=buffer.read(); + } + + // write bits for all channels + for (int j=0;j=info.output_bits){ + bit_counter = 0; + } + } + } + size_t availableFramesToWrite() override { + // 1 sample = 1 byte -> divide by channels + return buffer.availableForWrite() / info.channels; + } + + /// updates the buffer with analog value (represented by number of 1) + void quantize(int16_t audioValue, int left_right_idx) override { + // map to values from intput to number of active output_bits; + uint16_t sample = map(audioValue, -32768, 32767, 0, info.output_bits ); + buffer.write(sample); + } + + +}; + + +/** + * @brief Software Implementation of a Simple DAC - We quantize a digital int16_t sample my mapping the value to the range of 0b0 to 0b11111111111111111111111111111111, + * where the intensity is represented by the number of ones. This gives an overall resultion of 5 bits and just uses one single timer. + * + * @author Phil Schatzmann + * @copyright GPLv3 + * + */ + +class OversamplingDAC32 : public OversamplingDAC { + public: + OversamplingDAC32() : OversamplingDAC() { + } + + bool begin(DACInfo cfg) override { + TRACED(); + cfg.logInfo(true); + // default processing + return OversamplingDAC::begin(cfg); + } + + virtual uint32_t outputRate() override { + return info.outputBitRate(); + } + + virtual void startTimer() { + TRACED(); + // start (optional) timer + if (outputRate()>0){ + uint32_t timeUs = AudioTime::toTimeUs(outputRate()); + auto dacTimerCallback = [] (void *ptr)->void IRAM_ATTR { + output_frame_count++; + ((OversamplingDAC32*) ptr)->writePins(); + }; + timer_object.setCallbackParameter(this); + timer_object.begin(dacTimerCallback, timeUs, US); + LOGI("Timer started"); + } else { + LOGW("No output because output Rate <=0"); + } + } + + + protected: + RingBuffer buffer = RingBuffer(DEFAULT_BUFFER_SIZE); + DACOut out; + + + size_t availableFramesToWrite() override { + // 1 sample = 4 types -> divede by 4 and channels + return buffer.availableForWrite() / (sizeof(int32_t) * info.channels); + } + + /// Default output + void writePins() { + int channels = info.channels; + int32_t current_values[channels]; + if (active && buffer.available()>=2){ + // determine new values for all channels when all bits have been written + if (current_bit < 0){ + for (int j=0;j> (current_bit) & 1); + } + // move to next bit + current_bit--; + } + } + + /// updates the buffer with analog value (represented by number of 1) + virtual void quantize(int16_t newSamp, int left_right_idx) override{ + // map to unsigned + uint16_t sample = newSamp + (0xFFFF / 2); + // Don't need lastSamp anymore, store this one for next round + last_values[left_right_idx] = sample; + + // How much the comparison signal changes each oversample step + int32_t diffPerStep = (sample - last_values[left_right_idx]) >> (4 + info.oversample_factor); + for (int j = 0; j < info.oversample_factor; j++) { + // convert to bits + uint32_t bits = 0xFFFFFFFF; // max value + bits = bits >> (32 - (sample/2048)); + buffer.write(bits); + } + } +}; + + + +/** + * @brief A SimpleDAC which uses the Serial UART to output values. This implementation is not using any timers and therefore should + * work on any microcontroller. + * + * @author Phil Schatzmann + * @copyright GPLv3 + */ +class SerialDAC : public OversamplingDAC32 { + public: + SerialDAC() : OversamplingDAC32() { + serial = &Serial; + } + + SerialDAC(HardwareSerial &out) : OversamplingDAC32() { + serial = &out; + } + + DACInfo defaultConfig() override { + DACInfo result; + result.oversample_factor = 10; + return result; + } + + bool begin(DACInfo info) override { + TRACED(); + info.logInfo(false); + this->cfg = &info; + // setup baud rate in the uart + int rate = info.sample_rate * 8 * info.oversample_factor ; //outputSampleRate(); + LOGI("Setting Baudrate: %d", rate); + if (rate>0){ + serial->begin(rate); + } else { + LOGE("sample_rate not defined"); + } + // default processing + return OversamplingDAC::begin(info); + } + + /// just write the data to the UART w/o buffering + virtual size_t write(const uint8_t *data, size_t size) override { + TRACED(); + return serial->write(data, size); + } + + virtual uint32_t outputRate() override { + return 0; + } + + + protected: + HardwareSerial *serial = nullptr; + int16_t totalSamples; + DACInfo *cfg; + + /// updates the buffer with analog value (represented by number of 1) + virtual void quantize(int16_t newSamp, int left_right_idx) override { + if (left_right_idx==0){ + totalSamples = newSamp; + } else { + totalSamples += newSamp; + } + // on last channel we output + if (left_right_idx==cfg->channels-1) { + // combine left and right and map to unsigned + uint16_t sample = (totalSamples / cfg->channels) + (0xFFFF / 2); + // How much the comparison signal changes each oversample step + int32_t diffPerStep = (sample - last_values[left_right_idx]) >> (4 + info.oversample_factor); + for (int j = 0; j < info.oversample_factor; j++) { + // convert to bits + uint32_t bits = 0xFFFFFFFF; // max value + bits = bits >> (32 - (sample/2048)); + buffer.write(bits); + } + } + } +}; + +#ifdef USE_DELTASIGMA + +/** + * @brief Software Implementation of a Delta Sigma DAC - The advantage of this implementation is that it works with any + * digital output pin and that it supports as many channels as necessary! + * The logic is inspired by Earl Phil Howers ESP8266Audio's AudioOutputI2SNoDAC implementation. + * @author Phil Schatzmann + * @copyright GPLv3 + * + */ + +class DeltaSigmaDAC : public OversamplingDAC32 { + public: + DeltaSigmaDAC() : OversamplingDAC32() {} + + DACInfo defaultConfig() override { + DACInfo result; + result.oversample_factor = 2; + return result; + } + + bool begin(DACInfo cfg) override { + TRACED(); + cfg.logInfo(true); + // default processing + return OversamplingDAC::begin(cfg); + } + + virtual uint32_t outputRate() override { + return info.outputBitRate(); + } + + + protected: + /// updates the buffer with delta sigma values + virtual void quantize(int16_t newSamp, int left_right_idx) override { + // Don't need lastSamp anymore, store this one for next round + last_values[left_right_idx] = newSamp; + + // How much the comparison signal changes each oversample step + int32_t diffPerStep = (newSamp - last_values[left_right_idx]) >> (4 + info.oversample_factor); + for (int j = 0; j < info.oversample_factor; j++) { + uint32_t bits = 0; // The bits we convert the sample into, MSB to go on the wire first + + for (int i = 32; i > 0; i--) { + bits = bits << 1; + if (cummulated_error[left_right_idx] < 0) { + bits |= 1; + cummulated_error[left_right_idx] += fixedPosValue - newSamp; + } else { + // Bits[0] = 0 handled already by left shift + cummulated_error[left_right_idx] -= fixedPosValue + newSamp; + } + newSamp += diffPerStep; // Move the reference signal towards destination + } + buffer.write(bits); + } + } +}; + +#endif + +#ifdef ESP32 + + +/** + * @brief Audio Output with PWM signal + * + */ +class PWMDAC : public OversamplingDAC { + public: + PWMDAC(int pwmFrequency=PWM_FREQENCY) : OversamplingDAC() { + pmw_frequency = pwmFrequency; + } + + virtual ~PWMDAC(){ + end(); + } + + virtual DACInfo defaultConfig() { + DACInfo result; + // we use 16bits by default + result.output_bits = 16; + return result; + } + + virtual bool begin(DACInfo cfg) override { + TRACED(); + cfg.logInfo(true); + // default processing + bool result = OversamplingDAC::begin(cfg); + // automatic scaling + max_pwm_value = pow(2, info.output_bits); + setupPins(); + + return result; + } + + /// We use the sample rate to output values + virtual uint32_t outputRate() override { + return info.sample_rate; + } + + protected: + RingBuffer buffer = RingBuffer(DEFAULT_BUFFER_SIZE); + uint32_t max_pwm_value; + uint32_t pmw_frequency; + + virtual size_t availableFramesToWrite() override { + // 1 sample = 1 byte -> divide by channels + return buffer.availableForWrite() / info.channels; + } + + /// updates the buffer with analog value (represented by number of 1) + virtual void quantize(int16_t audioValue, int left_right_idx) override { + // map to values from intput to number of active output_bits; + uint16_t sample = map(audioValue, -32768, 32767, 0, max_pwm_value ); + buffer.write(sample); + } + + void setupPins() { + TRACED(); + LOGI("pmw_frequency: %u", pmw_frequency) + LOGI("max_pwm_value: %u", max_pwm_value) + for (int ch=0;ch=info.channels){ + for (int ch=0;ch0){ + uint32_t timeUs = AudioTime::toTimeUs(outputRate()); + auto dacTimerCallback = [] (void *ptr)->void IRAM_ATTR { + output_frame_count++; + ((PWMDAC*) ptr)->writePins(); + }; + timer_object.setCallbackParameter(this); + timer_object.begin(dacTimerCallback, timeUs, US); + LOGI("Timer started"); + } else { + LOGW("No output because output Rate <=0"); + } + } +}; + + +#endif + +} // namesapce + diff --git a/src/Experiments/AudioUSB.h b/src/Experiments/AudioUSB.h new file mode 100644 index 0000000000..9f3c0fda52 --- /dev/null +++ b/src/Experiments/AudioUSB.h @@ -0,0 +1,710 @@ +#pragma once +#include +#include +#include + +//--------------------------------------------------------------------+ +// MACRO CONSTANT TYPEDEF PROTYPES +//--------------------------------------------------------------------+ + +#ifndef AUDIO_SAMPLE_RATE +#define AUDIO_SAMPLE_RATE 48000 +#endif + +//--------------------------------------------------------------------+ +// Board Specific Configuration +//--------------------------------------------------------------------+ + +// RHPort number used for device can be defined by board.mk, default to port 0 +#ifndef BOARD_TUD_RHPORT +#define BOARD_TUD_RHPORT 0 +#endif + +// RHPort max operational speed can defined by board.mk +#ifndef BOARD_TUD_MAX_SPEED +#define BOARD_TUD_MAX_SPEED OPT_MODE_DEFAULT_SPEED +#endif + +//-------------------------------------------------------------------- +// COMMON CONFIGURATION +//-------------------------------------------------------------------- + +// defined by compiler flags for flexibility +#ifndef CFG_TUSB_MCU +#ifdef ESP32 +#define CFG_TUSB_MCU OPT_MCU_ESP32S2 +#else +#error CFG_TUSB_MCU must be defined +#endif + +#endif + +#if !defined(CFG_TUSB_OS) && !defined(ESP32) +#define CFG_TUSB_OS OPT_OS_NONE +#endif + +#ifndef CFG_TUSB_DEBUG +#define CFG_TUSB_DEBUG 0 +#endif + +// Enable Device stack +#define CFG_TUD_ENABLED 1 + +// Default is max speed that hardware controller could support with on-chip PHY +#define CFG_TUD_MAX_SPEED BOARD_TUD_MAX_SPEED + +// CFG_TUSB_DEBUG is defined by compiler in DEBUG build +// #define CFG_TUSB_DEBUG 0 + +/* USB DMA on some MCUs can only access a specific SRAM region with restriction + * on alignment. Tinyusb use follows macros to declare transferring memory so + * that they can be put into those specific section. e.g + * - CFG_TUSB_MEM SECTION : __attribute__ (( section(".usb_ram") )) + * - CFG_TUSB_MEM_ALIGN : __attribute__ ((aligned(4))) + */ +#ifndef CFG_TUSB_MEM_SECTION +#define CFG_TUSB_MEM_SECTION +#endif + +#ifndef CFG_TUSB_MEM_ALIGN +#define CFG_TUSB_MEM_ALIGN __attribute__((aligned(4))) +#endif + +//-------------------------------------------------------------------- +// DEVICE CONFIGURATION +//-------------------------------------------------------------------- +#define _TUSB_CONFIG_H_ + +#ifndef CFG_TUD_ENDPOINT0_SIZE +#define CFG_TUD_ENDPOINT0_SIZE 64 +#endif + +//------------- CLASS -------------// +#define CFG_TUD_AUDIO 1 +#ifndef ESP32 +#define CFG_TUD_CDC 0 +#define CFG_TUD_MSC 0 +#define CFG_TUD_HID 0 +#define CFG_TUD_MIDI 0 +#define CFG_TUD_VENDOR 0 +#endif +//-------------------------------------------------------------------- +// AUDIO CLASS DRIVER CONFIGURATION +//-------------------------------------------------------------------- + +// Have a look into audio_device.h for all configurations + +#define CFG_TUD_AUDIO_FUNC_1_DESC_LEN TUD_AUDIO_MIC_ONE_CH_DESC_LEN +#define CFG_TUD_AUDIO_FUNC_1_N_AS_INT \ + 1 // Number of Standard AS Interface Descriptors (4.9.1) defined per audio + // function - this is required to be able to remember the current alternate + // settings of these interfaces - We restrict us here to have a constant + // number for all audio functions (which means this has to be the maximum + // number of AS interfaces an audio function has and a second audio function + // with less AS interfaces just wastes a few bytes) +#define CFG_TUD_AUDIO_FUNC_1_CTRL_BUF_SZ 64 // Size of control request buffer + +#define CFG_TUD_AUDIO_ENABLE_EP_IN 1 +#define CFG_TUD_AUDIO_FUNC_1_N_BYTES_PER_SAMPLE_TX \ + 2 // Driver gets this info from the descriptors - we define it here to use it + // to setup the descriptors and to do calculations with it below +#define CFG_TUD_AUDIO_FUNC_1_N_CHANNELS_TX \ + 1 // Driver gets this info from the descriptors - we define it here to use it + // to setup the descriptors and to do calculations with it below - be aware: + // for different number of channels you need another descriptor! +#define CFG_TUD_AUDIO_EP_SZ_IN \ + (48 + 1) * \ + CFG_TUD_AUDIO_FUNC_1_N_BYTES_PER_SAMPLE_TX * \ + CFG_TUD_AUDIO_FUNC_1_N_CHANNELS_TX // 48 Samples (48 kHz) x 2 + // Bytes/Sample x + // CFG_TUD_AUDIO_N_CHANNELS_TX + // Channels - One extra sample is + // needed for asynchronous transfer + // adjustment, see feedback EP +#define CFG_TUD_AUDIO_FUNC_1_EP_IN_SZ_MAX \ + CFG_TUD_AUDIO_EP_SZ_IN // Maximum EP IN size for all AS alternate settings + // used +#define CFG_TUD_AUDIO_FUNC_1_EP_IN_SW_BUF_SZ CFG_TUD_AUDIO_EP_SZ_IN + 1 + +/* A combination of interfaces must have a unique product id, since PC will save + * device driver after the first plug. Same VID/PID with different interface e.g + * MSC (first), then CDC (later) will possibly cause system error on PC. + * + * Auto ProductID layout's Bitmap: + * [MSB] AUDIO | MIDI | HID | MSC | CDC [LSB] + */ +#ifndef ESP32 +#define _PID_MAP(itf, n) ((CFG_TUD_##itf) << (n)) +#define USB_PID \ + (0x4000 | _PID_MAP(CDC, 0) | _PID_MAP(MSC, 1) | _PID_MAP(HID, 2) | \ + _PID_MAP(MIDI, 3) | _PID_MAP(AUDIO, 4) | _PID_MAP(VENDOR, 5)) +#endif + +#include "class/audio/audio_device.h" +#include "tusb.h" + +namespace audio_tools { + +//--------------------------------------------------------------------+ +// Device Descriptors +//--------------------------------------------------------------------+ +tusb_desc_device_t const desc_device = { + .bLength = sizeof(tusb_desc_device_t), + .bDescriptorType = TUSB_DESC_DEVICE, + .bcdUSB = 0x0200, + + // Use Interface Association Descriptor (IAD) for Audio + // As required by USB Specs IAD's subclass must be common class (2) and + // protocol must be IAD (1) + .bDeviceClass = TUSB_CLASS_MISC, + .bDeviceSubClass = MISC_SUBCLASS_COMMON, + .bDeviceProtocol = MISC_PROTOCOL_IAD, + .bMaxPacketSize0 = CFG_TUD_ENDPOINT0_SIZE, + + .idVendor = 0xCafe, + .idProduct = USB_PID, + .bcdDevice = 0x0100, + + .iManufacturer = 0x01, + .iProduct = 0x02, + .iSerialNumber = 0x03, + + .bNumConfigurations = 0x01}; + +// Invoked when received GET DEVICE DESCRIPTOR +// Application return pointer to descriptor +uint8_t const *tud_descriptor_device_cb(void) { + return (uint8_t const *)&desc_device; +} + +//--------------------------------------------------------------------+ +// Configuration Descriptor +//--------------------------------------------------------------------+ +enum { ITF_NUM_AUDIO_CONTROL = 0, ITF_NUM_AUDIO_STREAMING, ITF_NUM_TOTAL }; + +#define CONFIG_TOTAL_LEN \ + (TUD_CONFIG_DESC_LEN + CFG_TUD_AUDIO * TUD_AUDIO_MIC_ONE_CH_DESC_LEN) + +#if CFG_TUSB_MCU == OPT_MCU_LPC175X_6X || \ + CFG_TUSB_MCU == OPT_MCU_LPC177X_8X || CFG_TUSB_MCU == OPT_MCU_LPC40XX +// LPC 17xx and 40xx endpoint type (bulk/interrupt/iso) are fixed by its number +// 0 control, 1 In, 2 Bulk, 3 Iso, 4 In etc ... +#define EPNUM_AUDIO 0x03 + +#elif TU_CHECK_MCU(OPT_MCU_NRF5X) +// nRF5x ISO can only be endpoint 8 +#define EPNUM_AUDIO 0x08 + +#else +#define EPNUM_AUDIO 0x01 +#endif + +uint8_t const desc_configuration[] = { + // Config number, interface count, string index, total length, attribute, + // power in mA + TUD_CONFIG_DESCRIPTOR(1, ITF_NUM_TOTAL, 0, CONFIG_TOTAL_LEN, 0x00, 100), + + // Interface number, string index, EP Out & EP In address, EP size + TUD_AUDIO_MIC_ONE_CH_DESCRIPTOR( + /*_itfnum*/ ITF_NUM_AUDIO_CONTROL, /*_stridx*/ 0, + /*_nBytesPerSample*/ CFG_TUD_AUDIO_FUNC_1_N_BYTES_PER_SAMPLE_TX, + /*_nBitsUsedPerSample*/ CFG_TUD_AUDIO_FUNC_1_N_BYTES_PER_SAMPLE_TX * 8, + /*_epin*/ 0x80 | EPNUM_AUDIO, /*_epsize*/ CFG_TUD_AUDIO_EP_SZ_IN)}; + +// Invoked when received GET CONFIGURATION DESCRIPTOR +// Application return pointer to descriptor +// Descriptor contents must exist long enough for transfer to complete +uint8_t const *tud_descriptor_configuration_cb(uint8_t index) { + (void)index; // for multiple configurations + return desc_configuration; +} + +//--------------------------------------------------------------------+ +// String Descriptors +//--------------------------------------------------------------------+ + +// array of pointer to string descriptors +char const *string_desc_arr[] = { + (const char[]){0x09, 0x04}, // 0: is supported language is English (0x0409) + "PaniRCorp", // 1: Manufacturer + "MicNode", // 2: Product + "123456", // 3: Serials, should use chip ID + "UAC2", // 4: Audio Interface +}; + +static uint16_t _desc_str[32]; + +// Invoked when received GET STRING DESCRIPTOR request +// Application return pointer to descriptor, whose contents must exist long +// enough for transfer to complete +uint16_t const *tud_descriptor_string_cb(uint8_t index, uint16_t langid) { + (void)langid; + + uint8_t chr_count; + + if (index == 0) { + memcpy(&_desc_str[1], string_desc_arr[0], 2); + chr_count = 1; + } else { + // Convert ASCII string into UTF-16 + + if (!(index < sizeof(string_desc_arr) / sizeof(string_desc_arr[0]))) + return NULL; + + const char *str = string_desc_arr[index]; + + // Cap at max char + chr_count = (uint8_t)strlen(str); + if (chr_count > 31) + chr_count = 31; + + for (uint8_t i = 0; i < chr_count; i++) { + _desc_str[1 + i] = str[i]; + } + } + + // first byte is length (including header), second byte is string type + _desc_str[0] = (uint16_t)((TUSB_DESC_STRING << 8) | (2 * chr_count + 2)); + + return _desc_str; +} + +/* Blink pattern + * - 250 ms : device not mounted + * - 1000 ms : device mounted + * - 2500 ms : device is suspended + */ +enum { + BLINK_NOT_MOUNTED = 250, + BLINK_MOUNTED = 1000, + BLINK_SUSPENDED = 2500, +}; + +static uint32_t blink_interval_ms = BLINK_NOT_MOUNTED; +static int channels = 2; +static uint8_t bytesPerSample; +static Stream *p_in = nullptr; +static Print *p_out = nullptr; + +// Audio controls +// Current states +bool mute[CFG_TUD_AUDIO_FUNC_1_N_CHANNELS_TX + 1]; // +1 for master channel 0 +uint16_t + volume[CFG_TUD_AUDIO_FUNC_1_N_CHANNELS_TX + 1]; // +1 for master channel 0 +uint32_t sampFreq; +uint8_t clkValid; + +// Range states +audio_control_range_2_n_t( + 1) volumeRng[CFG_TUD_AUDIO_FUNC_1_N_CHANNELS_TX + 1]; // Volume range state +audio_control_range_4_n_t(1) sampleFreqRng; // Sample frequency range state + +// Audio test data +uint16_t test_buffer_audio[(CFG_TUD_AUDIO_EP_SZ_IN - 2) / 2]; +uint16_t startVal = 0; + +void led_blinking_task(void); +void audio_task(void); + +/*------------- MAIN -------------*/ +class AudioUSB { +public: + AudioUSB(Stream &in) { + p_in = ∈ + p_out = ∈ + } + AudioUSB(Print &out) { p_out = &out; } + void begin(AudioInfo cfg) { + this->info = cfg; + channels = cfg.channels; + // board_init(); + + // init device stack on configured roothub port + tud_init(BOARD_TUD_RHPORT); + + // Init values + sampFreq = cfg.sample_rate; + clkValid = 1; + + sampleFreqRng.wNumSubRanges = 1; + sampleFreqRng.subrange[0].bMin = cfg.sample_rate; + sampleFreqRng.subrange[0].bMax = cfg.sample_rate; + sampleFreqRng.subrange[0].bRes = 0; + } + + void copy() { + tud_task(); // tinyusb device task + led_blinking_task(); + audio_task(); + } + +protected: + AudioInfo info; +}; + +//--------------------------------------------------------------------+ +// Device callbacks +//--------------------------------------------------------------------+ + +// Invoked when device is mounted +void tud_mount_cb(void) { blink_interval_ms = BLINK_MOUNTED; } + +// Invoked when device is unmounted +void tud_umount_cb(void) { blink_interval_ms = BLINK_NOT_MOUNTED; } + +// Invoked when usb bus is suspended +// remote_wakeup_en : if host allow us to perform remote wakeup +// Within 7ms, device must draw an average of current less than 2.5 mA from +// bus +void tud_suspend_cb(bool remote_wakeup_en) { + (void)remote_wakeup_en; + blink_interval_ms = BLINK_SUSPENDED; +} + +// Invoked when usb bus is resumed +void tud_resume_cb(void) { blink_interval_ms = BLINK_MOUNTED; } + +//--------------------------------------------------------------------+ +// AUDIO Task +//--------------------------------------------------------------------+ + +void audio_task(void) { + // Yet to be filled - e.g. put meas data into TX FIFOs etc. + // asm("nop"); +} + +//--------------------------------------------------------------------+ +// Application Callback API Implementations +//--------------------------------------------------------------------+ + +// Invoked when audio class specific set request received for an EP +bool tud_audio_set_req_ep_cb(uint8_t rhport, + tusb_control_request_t const *p_request, + uint8_t *pBuff) { + (void)rhport; + (void)pBuff; + + // We do not support any set range requests here, only current value + // requests + TU_VERIFY(p_request->bRequest == AUDIO_CS_REQ_CUR); + + // Page 91 in UAC2 specification + uint8_t channelNum = TU_U16_LOW(p_request->wValue); + uint8_t ctrlSel = TU_U16_HIGH(p_request->wValue); + uint8_t ep = TU_U16_LOW(p_request->wIndex); + + (void)channelNum; + (void)ctrlSel; + (void)ep; + + return false; // Yet not implemented +} + +// Invoked when audio class specific set request received for an interface +bool tud_audio_set_req_itf_cb(uint8_t rhport, + tusb_control_request_t const *p_request, + uint8_t *pBuff) { + (void)rhport; + (void)pBuff; + + // We do not support any set range requests here, only current value + // requests + TU_VERIFY(p_request->bRequest == AUDIO_CS_REQ_CUR); + + // Page 91 in UAC2 specification + uint8_t channelNum = TU_U16_LOW(p_request->wValue); + uint8_t ctrlSel = TU_U16_HIGH(p_request->wValue); + uint8_t itf = TU_U16_LOW(p_request->wIndex); + + (void)channelNum; + (void)ctrlSel; + (void)itf; + + return false; // Yet not implemented +} + +// Invoked when audio class specific set request received for an entity +bool tud_audio_set_req_entity_cb(uint8_t rhport, + tusb_control_request_t const *p_request, + uint8_t *pBuff) { + (void)rhport; + + // Page 91 in UAC2 specification + uint8_t channelNum = TU_U16_LOW(p_request->wValue); + uint8_t ctrlSel = TU_U16_HIGH(p_request->wValue); + uint8_t itf = TU_U16_LOW(p_request->wIndex); + uint8_t entityID = TU_U16_HIGH(p_request->wIndex); + + (void)itf; + + // We do not support any set range requests here, only current value + // requests + TU_VERIFY(p_request->bRequest == AUDIO_CS_REQ_CUR); + + // If request is for our feature unit + if (entityID == 2) { + switch (ctrlSel) { + case AUDIO_FU_CTRL_MUTE: + // Request uses format layout 1 + TU_VERIFY(p_request->wLength == sizeof(audio_control_cur_1_t)); + + mute[channelNum] = ((audio_control_cur_1_t *)pBuff)->bCur; + + TU_LOG2(" Set Mute: %d of channel: %u\r\n", mute[channelNum], + channelNum); + return true; + + case AUDIO_FU_CTRL_VOLUME: + // Request uses format layout 2 + TU_VERIFY(p_request->wLength == sizeof(audio_control_cur_2_t)); + + volume[channelNum] = (uint16_t)((audio_control_cur_2_t *)pBuff)->bCur; + + TU_LOG2(" Set Volume: %d dB of channel: %u\r\n", volume[channelNum], + channelNum); + return true; + + // Unknown/Unsupported control + default: + TU_BREAKPOINT(); + return false; + } + } + return false; // Yet not implemented +} + +// Invoked when audio class specific get request received for an EP +bool tud_audio_get_req_ep_cb(uint8_t rhport, + tusb_control_request_t const *p_request) { + (void)rhport; + + // Page 91 in UAC2 specification + uint8_t channelNum = TU_U16_LOW(p_request->wValue); + uint8_t ctrlSel = TU_U16_HIGH(p_request->wValue); + uint8_t ep = TU_U16_LOW(p_request->wIndex); + + (void)channelNum; + (void)ctrlSel; + (void)ep; + + // return tud_control_xfer(rhport, p_request, &tmp, 1); + + return false; // Yet not implemented +} + +// Invoked when audio class specific get request received for an interface +bool tud_audio_get_req_itf_cb(uint8_t rhport, + tusb_control_request_t const *p_request) { + (void)rhport; + + // Page 91 in UAC2 specification + uint8_t channelNum = TU_U16_LOW(p_request->wValue); + uint8_t ctrlSel = TU_U16_HIGH(p_request->wValue); + uint8_t itf = TU_U16_LOW(p_request->wIndex); + + (void)channelNum; + (void)ctrlSel; + (void)itf; + + return false; // Yet not implemented +} + +// Invoked when audio class specific get request received for an entity +bool tud_audio_get_req_entity_cb(uint8_t rhport, + tusb_control_request_t const *p_request) { + (void)rhport; + + // Page 91 in UAC2 specification + uint8_t channelNum = TU_U16_LOW(p_request->wValue); + uint8_t ctrlSel = TU_U16_HIGH(p_request->wValue); + // uint8_t itf = TU_U16_LOW(p_request->wIndex); // Since + // we have only one audio function implemented, we do not need the itf value + uint8_t entityID = TU_U16_HIGH(p_request->wIndex); + + // Input terminal (Microphone input) + if (entityID == 1) { + switch (ctrlSel) { + case AUDIO_TE_CTRL_CONNECTOR: { + // The terminal connector control only has a get request with only the + // CUR attribute. + audio_desc_channel_cluster_t ret; + + // Those are dummy values for now + ret.bNrChannels = channels; + ret.bmChannelConfig = (audio_channel_config_t)0; + ret.iChannelNames = 0; + + TU_LOG2(" Get terminal connector\r\n"); + + return tud_audio_buffer_and_schedule_control_xfer( + rhport, p_request, (void *)&ret, sizeof(ret)); + } break; + + // Unknown/Unsupported control selector + default: + TU_BREAKPOINT(); + return false; + } + } + + // Feature unit + if (entityID == 2) { + switch (ctrlSel) { + case AUDIO_FU_CTRL_MUTE: + // Audio control mute cur parameter block consists of only one byte - we + // thus can send it right away There does not exist a range parameter + // block for mute + TU_LOG2(" Get Mute of channel: %u\r\n", channelNum); + return tud_control_xfer(rhport, p_request, &mute[channelNum], 1); + + case AUDIO_FU_CTRL_VOLUME: + switch (p_request->bRequest) { + case AUDIO_CS_REQ_CUR: + TU_LOG2(" Get Volume of channel: %u\r\n", channelNum); + return tud_control_xfer(rhport, p_request, &volume[channelNum], + sizeof(volume[channelNum])); + + case AUDIO_CS_REQ_RANGE: + TU_LOG2(" Get Volume range of channel: %u\r\n", channelNum); + + // Copy values - only for testing - better is version below + audio_control_range_2_n_t(1) ret; + + ret.wNumSubRanges = 1; + ret.subrange[0].bMin = -90; // -90 dB + ret.subrange[0].bMax = 90; // +90 dB + ret.subrange[0].bRes = 1; // 1 dB steps + + return tud_audio_buffer_and_schedule_control_xfer( + rhport, p_request, (void *)&ret, sizeof(ret)); + + // Unknown/Unsupported control + default: + TU_BREAKPOINT(); + return false; + } + break; + + // Unknown/Unsupported control + default: + TU_BREAKPOINT(); + return false; + } + } + + // Clock Source unit + if (entityID == 4) { + switch (ctrlSel) { + case AUDIO_CS_CTRL_SAM_FREQ: + // channelNum is always zero in this case + switch (p_request->bRequest) { + case AUDIO_CS_REQ_CUR: + TU_LOG2(" Get Sample Freq.\r\n"); + return tud_control_xfer(rhport, p_request, &sampFreq, sizeof(sampFreq)); + + case AUDIO_CS_REQ_RANGE: + TU_LOG2(" Get Sample Freq. range\r\n"); + return tud_control_xfer(rhport, p_request, &sampleFreqRng, + sizeof(sampleFreqRng)); + + // Unknown/Unsupported control + default: + TU_BREAKPOINT(); + return false; + } + break; + + case AUDIO_CS_CTRL_CLK_VALID: + // Only cur attribute exists for this request + TU_LOG2(" Get Sample Freq. valid\r\n"); + return tud_control_xfer(rhport, p_request, &clkValid, sizeof(clkValid)); + + // Unknown/Unsupported control + default: + TU_BREAKPOINT(); + return false; + } + } + + TU_LOG2(" Unsupported entity: %d\r\n", entityID); + return false; // Yet not implemented +} + +bool tud_audio_tx_done_pre_load_cb(uint8_t rhport, uint8_t itf, uint8_t ep_in, + uint8_t cur_alt_setting) { + (void)rhport; + (void)itf; + (void)ep_in; + (void)cur_alt_setting; + + tud_audio_write((uint8_t *)test_buffer_audio, CFG_TUD_AUDIO_EP_SZ_IN - 2); + + return true; +} + +bool tud_audio_tx_done_post_load_cb(uint8_t rhport, uint16_t n_bytes_copied, + uint8_t itf, uint8_t ep_in, + uint8_t cur_alt_setting) { + (void)rhport; + (void)n_bytes_copied; + (void)itf; + (void)ep_in; + (void)cur_alt_setting; + + // for (size_t cnt = 0; cnt < (CFG_TUD_AUDIO_EP_SZ_IN - 2) / 2; cnt++) { + // test_buffer_audio[cnt] = startVal++; + // } + if (p_in != nullptr) { + p_in->readBytes((uint8_t *)test_buffer_audio, CFG_TUD_AUDIO_EP_SZ_IN - 2); + } else { + memset(test_buffer_audio, 0, CFG_TUD_AUDIO_EP_SZ_IN - 2); + } + + return true; +} + +bool tud_audio_rx_done_pre_read_cb(uint8_t rhport, uint16_t n_bytes_received, uint8_t func_id, uint8_t ep_out, uint8_t cur_alt_setting) +{ + (void)rhport; + (void)func_id; + (void)ep_out; + (void)cur_alt_setting; + + //free-running, adjust myself to match pace + bytes_read = tud_audio_read(test_buffer_audio, CFG_TUD_AUDIO_EP_SZ_IN - 2); //read from USB, write to buffer + if (p_print!=nullptr){ + p_print->write(usb_spk_buf, bytes_read); + } + + return true; +} + + +bool tud_audio_set_itf_close_EP_cb(uint8_t rhport, + tusb_control_request_t const *p_request) { + (void)rhport; + (void)p_request; + startVal = 0; + + return true; +} + +//--------------------------------------------------------------------+ +// BLINKING TASK +//--------------------------------------------------------------------+ +void led_blinking_task(void) { +#ifdef PIN_LED + static uint32_t start_ms = 0; + static bool led_state = false; + + // Blink every interval ms + if (millis() - start_ms < blink_interval_ms) + return; // not enough time + start_ms += blink_interval_ms; + + digitalWrite(PIN_LED, led_state); + led_state = 1 - led_state; // toggle +#endif +} + +} // namespace audio_tools \ No newline at end of file diff --git a/src/AudioTools/Sandbox/CodecGGWave.h b/src/Experiments/CodecGGWave.h similarity index 94% rename from src/AudioTools/Sandbox/CodecGGWave.h rename to src/Experiments/CodecGGWave.h index 396a2a1c3c..7d34d30022 100644 --- a/src/AudioTools/Sandbox/CodecGGWave.h +++ b/src/Experiments/CodecGGWave.h @@ -10,9 +10,9 @@ */ #pragma once -#include "AudioTools/AudioCodecs/AudioEncoded.h" -#include "AudioTools/CoreAudio/Buffers.h" -#include "AudioTools/CoreAudio/AudioEffects/SoundGenerator.h" +#include "AudioCodecs/AudioEncoded.h" +#include "AudioTools/Buffers.h" +#include "AudioEffects/SoundGenerator.h" #include #define GGWAVE_DEFAULT_SAMPLE_RATE 48000 @@ -120,20 +120,22 @@ class GGWaveDecoder : public AudioDecoder { active = false; } - size_t write(const uint8_t *data, size_t len) { + size_t write(const void *sampleBufferRaw, size_t bytes_read) { if (!active) return 0; - uint8_t *p_byte = (uint8_t *)data; - for (int j=0;j 0) { + result = readBytes(tmp, 512); + size_t len_write = dec_stream.write(tmp, result); + LOGI("copy %d -> %d", result, len_write); + delay(5); + + } else { + LOGI("copy %d", result); + delay(10); + } + return result; + } + + int available() override { + Stream &urlStream = getURLStream(); + int result = urlStream.available(); + if (result == 0) { + if (!parser.nextStream()) { + // we consumed all segments so we get new ones + begin(); + } + result = urlStream.available(); + } + return result; + } + + size_t readBytes(uint8_t *data, size_t len) override { + Stream &urlStream = getURLStream(); + size_t result = 0; + if (urlStream.available() > 0) { + result = urlStream.readBytes(data, len); + } + return result; + } + + protected: + /** + * An individual decoder with it's name + */ + struct CodecEntry { + const char *name; + AudioDecoder *p_decoder; + CodecEntry() = default; + CodecEntry(CodecEntry &e) = default; + CodecEntry(const char *name, AudioDecoder &codec) { + this->name = name; + this->p_decoder = &codec; + } + CodecEntry(const char *name) { + this->name = name; + this->p_decoder = nullptr; + } + }; + + /** + * Codec Management: We collect the decoders in a list and retrieve them by + * the name that is used in the codec specification of the hls + */ + class HLSCodecManagement { + public: + void addDecoder(const char *name, AudioDecoder &decoder) { + CodecEntry entry{name, decoder}; + codecs.push_back(entry); + } + void addDecoder(const char *name) { + CodecEntry entry{name}; + codecs.push_back(entry); + } + + bool isValid(const char *name) { + if (name == nullptr) return false; + + for (auto it = codecs.begin(); it != codecs.end(); ++it) { + if (Str(name).startsWith(it->name)) { + return true; + } + } + return false; + } + + AudioDecoder *create(const char *name) { + if (name == nullptr) return nullptr; + for (auto it = codecs.begin(); it != codecs.end(); ++it) { + if (Str(name).startsWith(it->name)) { + LOGI("Using codec: %s", it->name); + p_current_codec = it->p_decoder; + return p_current_codec; + } + } + return nullptr; + } + + void end() { + if (p_current_codec) { + p_current_codec->end(); + } + } + + protected: + List codecs; + AudioDecoder *p_current_codec = nullptr; + }; + + /** + * Simple Parser for HLS data. We select the entry with min bandwidth + */ + class HLSParser { + public: + bool begin(const char *urlStr) { + index_url_str = urlStr; + segments_url_str = ""; + bandwidth = 0; + LOGI("-------------------"); + LOGI("Loading index: %s", index_url_str); + url_stream.setTimeout(1000); + url_stream.setConnectionClose(false); + // we only update the content length + url_stream.httpRequest().reply().put(CONTENT_LENGTH, 0); + url_stream.setAutoCreateLines(false); + + bool rc = url_stream.begin(index_url_str); + if (rc) rc = parse(true); + if (rc) rc = codecSetup(); + return rc; + } + + bool begin() { + segments_url_str = ""; + bandwidth = 0; + LOGI("-------------------"); + LOGI("Loading index: %s", index_url_str); + bool rc = url_stream.begin(index_url_str); + if (rc) rc = parse(true); + return rc; + } + + // parse the index file and the segments + bool parse(bool process_index) { + LOGI("parsing %s", process_index ? "Index" : "Segements") + char tmp[MAX_HLS_LINE]; + bool result = true; + is_extm3u = false; + + // parse lines + memset(tmp, 0, MAX_HLS_LINE); + while (url_stream.available()) { + memset(tmp, 0, MAX_HLS_LINE); + url_stream.httpRequest().readBytesUntil('\n', tmp, MAX_HLS_LINE); + Str str(tmp); + + if (!str.startsWith("#EXTM3U")) { + is_extm3u = true; + } + + if (process_index) { + parseIndex(str); + } else { + parseSegments(str); + } + } + + // load segments + if (process_index && !segments_url_str.isEmpty()) { + LOGI("-------------------"); + endUrlStream(); + LOGI("Load segments from: %s", segments_url_str.c_str()); + if (url_stream.begin(segments_url_str.c_str())) { + result = parse(false); + } + } + return result; + } + + Queue &getSegments() { return segments; } + + URLStream &getURLStream() { return url_stream; } + + void end() { + TRACED(); + segments.clear(); + codec.clear(); + segments_url_str.clear(); + endUrlStream(); + // dec_stream.end(); + codec_mgmt.end(); + } + + // EncodedAudioStream &decodedStream() { return dec_stream; } + + /// Get the decoded data from the next segment + bool nextStream() { + TRACEI(); + bool result = false; + StrExt url1; + URLStream &url_stream = getURLStream(); + if (getSegments().dequeue(url1)) { + endUrlStream(); + StrExt tmp; + // if the segment is a complete http url we use it + if (url1.startsWith("http")) { + tmp.set(url1.c_str()); + } else { + // we create the complete url + tmp.set(segments_url_str.c_str()); + tmp.add("/"); + tmp.add(url1.c_str()); + } + LOGI("-------------------"); + LOGI("playing %s", tmp.c_str()); + endUrlStream(); + result = url_stream.begin(tmp.c_str(), "audio/mp4a", GET); + } else { + LOGW("No more segments"); + } + return result; + } + + void endUrlStream() { + TRACED(); + url_stream.end(); + TRACED(); + } + + void addDecoder(const char *name, AudioDecoder &codec) { + codec_mgmt.addDecoder(name, codec); + } + + void addDecoder(const char *name) { codec_mgmt.addDecoder(name); } + // Determine the decoder + bool codecSetup() { + codec_mgmt.end(); + decoder = codec_mgmt.create(codec.c_str()); + return decoder != nullptr; + } + + bool codecIsValid(const char *name) { return codec_mgmt.isValid(name); } + + bool codecCreate(const char *name) { return codec_mgmt.create(name); } + + void codecDelete() { codec_mgmt.end(); } + + AudioDecoder *getDecoder() { return decoder; } + + const char *getCodecString() { return codec.c_str(); } + + protected: + int bandwidth = 0; + bool url_active = false; + bool is_extm3u = false; + StrExt codec; + StrExt segments_url_str; + const char *index_url_str = nullptr; + Queue segments; + URLStream url_stream; + AudioDecoder *decoder = nullptr; + HLSCodecManagement codec_mgmt; + + // Add all segments to queue + void parseSegments(Str str) { + TRACED(); + LOGI("> %s", str.c_str()); + + if (!str.startsWith("#")) { + LOGI("-> Segment: %s", str.c_str()); + StrExt ts = str; + segments.enqueue(ts); + } + } + + // Determine codec for min badnwidth + void parseIndex(Str str) { + TRACED(); + LOGI("> %s", str.c_str()); + int tmp_bandwidth; + if (str.indexOf("EXT-X-STREAM-INF") >= 0) { + // determine min bandwidth + int pos = str.indexOf("BANDWIDTH="); + if (pos > 0) { + Str num(str.c_str() + pos + 10); + tmp_bandwidth = num.toInt(); + url_active = (tmp_bandwidth < bandwidth || bandwidth == 0); + if (url_active) { + bandwidth = tmp_bandwidth; + LOGD("-> bandwith: %d", bandwidth); + } + } + + pos = str.indexOf("CODECS="); + if (pos > 0) { + int start = pos + 8; + int end = str.indexOf('"', pos + 10); + codec.substring(str, start, end); + LOGI("-> codec: %s", codec.c_str()); + } + } + + if (url_active && str.startsWith("http")) { + if (str.startsWith("http")) { + segments_url_str.set(str); + if (codecIsValid(codec.c_str())) { + LOGI("-> url: %s", segments_url_str.c_str()); + } else { + LOGW("Url ignored because there is no codec for %s", codec.c_str()); + } + } + } + } + + } parser; + EncodedAudioStream dec_stream; + AudioStream *p_out = nullptr; + + Stream &getURLStream() { return parser.getURLStream(); } + + bool beginEncodedAudioStream() { + if (p_out == nullptr) return false; + if (parser.getDecoder() == nullptr) return false; + dec_stream.setStream(p_out); + dec_stream.setDecoder(parser.getDecoder()); + dec_stream.setNotifyAudioChange(*p_out); + return dec_stream.begin(); + } +}; +} // namespace audio_tools + +#endif \ No newline at end of file diff --git a/src/AudioTools/Sandbox/I2SBitBang.h b/src/Experiments/I2SBitBang.h similarity index 92% rename from src/AudioTools/Sandbox/I2SBitBang.h rename to src/Experiments/I2SBitBang.h index 71e64a9e11..90374a287a 100644 --- a/src/AudioTools/Sandbox/I2SBitBang.h +++ b/src/Experiments/I2SBitBang.h @@ -1,11 +1,11 @@ #pragma once -#include "AudioToolsConfig.h" +#include "AudioConfig.h" #ifndef USE_I2S # define USE_I2S # define CREATE_I2S #endif -#include "AudioTools/CoreAudio/AudioStreams.h" -#include "AudioTools/CoreAudio/AudioI2S/I2SConfig.h" +#include "AudioTools/AudioStreams.h" +#include "AudioI2S/I2SConfig.h" #include "SPI.h" namespace audio_tools { diff --git a/src/Experiments/PDMStream.h b/src/Experiments/PDMStream.h new file mode 100644 index 0000000000..9c97025ca1 --- /dev/null +++ b/src/Experiments/PDMStream.h @@ -0,0 +1,152 @@ +#include "AudioConfig.h" +#include "Stream.h" + +namespace audio_tools { + +/** + * @brief Deciates the samples of a sample stream by the indicated factor. + * Please note that this is a simplified version which assumes that the factor + * is specified as multiple of the bits_per_sample, so that we can avoid any bit + * operations. It is also assumed that we have only one channel in the data + * stream. + * @author Phil Schatzmann + * @ingroup io + * @copyright GPLv3 + */ +template +class DecimationStream { + public: + DecimationStream(Stream &in) { p_in = ∈ } + DecimationStream(Stream *in) { p_in = in; } + + // defines the decimation factor: as multiple of bits_per sample + void setFactor(int factor) { dec_factor = factor; } + + size_t readBytes(uint8_t *buffer, size_t byteCount) { + // make sure that we read full samples + int samples = byteCount / sizeof(T); + int result_bytes = samples * sizeof(T); + + // read subsequent samples + T *data = (T *)&buffer; + for (int idx = 0; idx < samples; idx++) { + // decmate subsequent samples + for (int dec = 0; dec < dec_factor; dec++) { + if (p_in->readBytes(data + idx, sizeof(T)) != sizeof(T)) { + LOGE("readBytes failed"); + } + } + } + + return result_bytes; + } + + int available() { return p_in->available() / dec_factor; } + + protected: + Stream *p_in = nullptr; + int dec_factor = 1; +}; + + +/** + * @brief Applies low pass filter to a decimated pdm signal to convert it + * to pcm + * @author Phil Schatzmann + * @ingroup io + * @copyright GPLv3 + */ + +template +class PDMStreamT { + public: + PDMStreamT(Stream &in) { + p_in = ∈ + cfg.sample_rate = 44100; + cfg.channels = 1; + cfg.bits_per_sample = bitsPerSample(); + } + + // provides the audio information of the PCM stream + AudioInfo audioInfo() { return cfg; } + + // provides the audio info of the PDM stream (with the much higher sample + // rate) + AudioInfo audioInfoPDM() { + AudioInfo result = audioInfo(); + // sample_rate_pcm = sample_rate_pdm / decimation + result.sample_rate = cfg.sample_rate * decimation(); + return result; + } + + // provides the decimation factor that was used in the processing + int decimation() { return decimation_factor; } + + // defines the decimation factor: must be multiple of bits_per sample + void setDecimation(int factor) { + if (factor % bitsPerSample() != 0) { + LOGW("decimation factor must be multiple of %d", bitsPerSample()); + } + decimation_factor = factor; + } + + bool begin(AudioInfo info) { + if (cfg.channels != 1) { + LOGE("channels must be 1"); + } + // set the factor for the decimator + decimation_stream.setFactor(decimation_factor / sizeof(T) * 8); + cfg = info; + return begin(); + } + + bool begin() { + bool rc = in_filtered.begin(cfg); + in_filtered.setFilter(0, fir); + return rc; + } + + size_t readBytes(uint8_t *buffer, size_t byteCount) { + // make sure that we read full samples + int samples = byteCount / sizeof(T); + int result_bytes = samples * sizeof(T); + + if (in_filtered.readBytes(buffer, result_bytes) != result_bytes) { + LOGE("readBytes failed"); + } + + return result_bytes; + } + + int available() { return in_filtered.available(); } + + template + void setFilterValues(const T (&b)[B]) { + fir.setValues(b); + } + + protected: + AudioInfo cfg; + Stream *p_in = nullptr; + int decimation_factor = sizeof(T) * 8; + // 44100 hz FIR: cut off: 18040, transition bandwidth: 7380 + float coef[] = { + -0.000704420658475743, -0.000537879918926308, 0.004114637509913062, + -0.012685775806621488, 0.027889173789107543, -0.049285026985058301, + 0.074005079283040689, -0.097330704866957815, 0.114052040962871595, + 0.880965753382213723, 0.114052040962871595, -0.097330704866957843, + 0.074005079283040717, -0.049285026985058301, 0.027889173789107550, + -0.012685775806621504, 0.004114637509913064, -0.000537879918926308, + -0.000704420658475743, + }; + DecimationStream decimation_stream{p_in}; + FilteredStream in_filtered{decimation_stream, 1}; + FIR fir{coef}; + + constexpr int8_t bitsPerSample() { return sizeof(T) * 8; } +}; + +// Define PDMStream +using PDMStream = PDMStreamT; + +} // namespace audio_tools \ No newline at end of file diff --git a/src/AudioTools/Sandbox/README.md b/src/Experiments/README.md similarity index 100% rename from src/AudioTools/Sandbox/README.md rename to src/Experiments/README.md diff --git a/src/AudioTools/Video/JpegOpenCV.h b/src/Video/JpegOpenCV.h similarity index 89% rename from src/AudioTools/Video/JpegOpenCV.h rename to src/Video/JpegOpenCV.h index ce82124598..59cc765b6a 100644 --- a/src/AudioTools/Video/JpegOpenCV.h +++ b/src/Video/JpegOpenCV.h @@ -44,11 +44,11 @@ class JpegOpenCV : public VideoOutput { } // Add some more data to the image vector - size_t write(const uint8_t *data, size_t len) override { - memcpy(&img_vector[pos], data, len); - pos += len; - open -= len; - return len; + size_t write(const uint8_t *buffer, size_t byteCount) override { + memcpy(&img_vector[pos], buffer, byteCount); + pos += byteCount; + open -= byteCount; + return byteCount; } protected: diff --git a/src/AudioTools/Video/JpegTFT.h b/src/Video/JpegTFT.h similarity index 95% rename from src/AudioTools/Video/JpegTFT.h rename to src/Video/JpegTFT.h index 33f15aa067..f5ed95d4bc 100644 --- a/src/AudioTools/Video/JpegTFT.h +++ b/src/Video/JpegTFT.h @@ -1,5 +1,5 @@ #pragma once -#include "AudioTools/CoreAudio/AudioBasic/Collections/Vector.h" +#include "AudioBasic/Collections/Vector.h" #include "JPEGDecoder.h" // https://github.com/Bodmer/JPEGDecoder #include "TFT_eSPI.h" // https://github.com/Bodmer/TFT_eSPI #include "Video/Video.h" @@ -42,11 +42,11 @@ class JpegTFT : public VideoOutput { } // Add some more data to the image vector - size_t write(const uint8_t *data, size_t len) override { - memcpy(&img_vector[pos], data, len); - pos += len; - open -= len; - return len; + size_t write(const uint8_t *buffer, size_t byteCount) override { + memcpy(&img_vector[pos], buffer, byteCount); + pos += byteCount; + open -= byteCount; + return byteCount; } protected: diff --git a/src/AudioTools/Video/README.md b/src/Video/README.md similarity index 100% rename from src/AudioTools/Video/README.md rename to src/Video/README.md diff --git a/src/AudioTools/Video/Video.h b/src/Video/Video.h similarity index 95% rename from src/AudioTools/Video/Video.h rename to src/Video/Video.h index 8d38fc991d..5361f2ef8e 100644 --- a/src/AudioTools/Video/Video.h +++ b/src/Video/Video.h @@ -1,6 +1,6 @@ #pragma once -#include "AudioTools/CoreAudio/AudioOutput.h" -#include "AudioTools/CoreAudio/Buffers.h" +#include "AudioTools/AudioOutput.h" +#include "AudioTools/Buffers.h" #include "stdint.h" /** @@ -21,7 +21,7 @@ namespace audio_tools { class VideoOutput { public: virtual void beginFrame(size_t size) = 0; - virtual size_t write(const uint8_t *data, size_t len) = 0; + virtual size_t write(const uint8_t *data, size_t byteCount) = 0; virtual uint32_t endFrame() = 0; }; diff --git a/tests-cmake/CMakeLists.txt b/tests-cmake/CMakeLists.txt index 8f494359e1..6fe6be3b8d 100644 --- a/tests-cmake/CMakeLists.txt +++ b/tests-cmake/CMakeLists.txt @@ -7,7 +7,10 @@ set(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} -O0") project(tests) set (CMAKE_CXX_STANDARD 11) set (DCMAKE_CXX_FLAGS "-Werror" ) -# add_compile_options(-Wstack-usage=1024) +if (CMAKE_CXX_COMPILER_ID STREQUAL "Clang") + set (CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -fno-omit-frame-pointer -fsanitize=address") + set (CMAKE_LINKER_FLAGS_DEBUG "${CMAKE_LINKER_FLAGS_DEBUG} -fno-omit-frame-pointer -fsanitize=address") +endif() include(FetchContent) @@ -20,11 +23,7 @@ endif() add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/resample ${CMAKE_CURRENT_BINARY_DIR}/resample) add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/generator ${CMAKE_CURRENT_BINARY_DIR}/generator) add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/effects ${CMAKE_CURRENT_BINARY_DIR}/effects) -add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/fft ${CMAKE_CURRENT_BINARY_DIR}/fft) -add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/ifft ${CMAKE_CURRENT_BINARY_DIR}/ifft) -add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/fft-effect ${CMAKE_CURRENT_BINARY_DIR}/fft-effect) add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/filter ${CMAKE_CURRENT_BINARY_DIR}/filter) add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/filter-wav ${CMAKE_CURRENT_BINARY_DIR}/filter-wav) add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/url-test ${CMAKE_CURRENT_BINARY_DIR}/url-test) add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/codec) -add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/pipeline) diff --git a/tests-cmake/codec/CMakeLists.txt b/tests-cmake/codec/CMakeLists.txt index 7e44e3f15e..842f50fde4 100644 --- a/tests-cmake/codec/CMakeLists.txt +++ b/tests-cmake/codec/CMakeLists.txt @@ -4,7 +4,10 @@ cmake_minimum_required(VERSION 3.20) project(tests-codec) set (CMAKE_CXX_STANDARD 11) set (DCMAKE_CXX_FLAGS "-Werror" ) -# add_compile_options(-Wstack-usage=1024) +if (CMAKE_CXX_COMPILER_ID STREQUAL "Clang") + set (CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -fno-omit-frame-pointer -fsanitize=address") + set (CMAKE_LINKER_FLAGS_DEBUG "${CMAKE_LINKER_FLAGS_DEBUG} -fno-omit-frame-pointer -fsanitize=address") +endif() include(FetchContent) @@ -15,7 +18,6 @@ if(CMAKE_SOURCE_DIR STREQUAL CMAKE_CURRENT_SOURCE_DIR) endif() -add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/aac-faad ${CMAKE_CURRENT_BINARY_DIR}/aac-faad) add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/aac-fdk ${CMAKE_CURRENT_BINARY_DIR}/aac-fdk) add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/aac-fdk-encode ${CMAKE_CURRENT_BINARY_DIR}/aac-fdk-encode) add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/mp3-helix ${CMAKE_CURRENT_BINARY_DIR}/mp3-helix) @@ -26,8 +28,6 @@ add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/mp3-metadata ${CMAKE_CURRENT_BINARY add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/opus ${CMAKE_CURRENT_BINARY_DIR}/opus) add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/opusogg ${CMAKE_CURRENT_BINARY_DIR}/opusogg) add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/container-avi ${CMAKE_CURRENT_BINARY_DIR}/container-avi) -add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/hls ${CMAKE_CURRENT_BINARY_DIR}/hls) -#add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/container-avi-movie ${CMAKE_CURRENT_BINARY_DIR}/container-avi-movie) -#add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/container-m4a ${CMAKE_CURRENT_BINARY_DIR}/container-m4a) +add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/container-avi-movie ${CMAKE_CURRENT_BINARY_DIR}/container-avi-movie) diff --git a/tests-cmake/codec/aac-faad/CMakeLists.txt b/tests-cmake/codec/aac-faad/CMakeLists.txt deleted file mode 100644 index 50a3db1146..0000000000 --- a/tests-cmake/codec/aac-faad/CMakeLists.txt +++ /dev/null @@ -1,35 +0,0 @@ -cmake_minimum_required(VERSION 2.34) - -# set the project name -project(aac-faad) -set (CMAKE_CXX_STANDARD 11) -set (DCMAKE_CXX_FLAGS -Werror ) -# add_compile_options(-Wstack-usage=1024) - -include(FetchContent) - -# Build with arduino-audio-tools -if(CMAKE_SOURCE_DIR STREQUAL CMAKE_CURRENT_SOURCE_DIR) - add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/../../.. ${CMAKE_CURRENT_BINARY_DIR}/arduino-audio-tools ) -endif() - -# Build with libfaad -FetchContent_Declare(libfaad GIT_REPOSITORY https://github.com/pschatzmann/arduino-libfaad.git GIT_TAG main ) -FetchContent_GetProperties(libfaad) -if(NOT libfaad_POPULATED) - FetchContent_Populate(libfaad) - add_subdirectory(${libfaad_SOURCE_DIR} ${CMAKE_CURRENT_BINARY_DIR}/libfaad) -endif() - -# build sketch as executable -add_executable (aac-faad aac-faad.cpp) - -# set preprocessor defines -target_compile_definitions(aac-faad PUBLIC -DARDUINO -DIS_DESKTOP -DANALYSIS) - -# OS/X might need this setting for core audio -#target_compile_definitions(portaudio PUBLIC -DPA_USE_COREAUDIO=1) - -# specify libraries -target_link_libraries(aac-faad arduino_emulator libfaad arduino-audio-tools) - diff --git a/tests-cmake/codec/aac-faad/aac-faad.cpp b/tests-cmake/codec/aac-faad/aac-faad.cpp deleted file mode 100644 index e2882f1472..0000000000 --- a/tests-cmake/codec/aac-faad/aac-faad.cpp +++ /dev/null @@ -1,34 +0,0 @@ -#include "Arduino.h" -#include "AudioTools.h" -#include "AudioTools/AudioCodecs/CodecAACFAAD.h" -//#include "AudioTools/AudioLibs/PortAudioStream.h" -#include "audio.h" - -using namespace audio_tools; - -MemoryStream aac(gs_16b_2c_44100hz_aac, gs_16b_2c_44100hz_aac_len); -//PortAudioStream out; // Output of sound on desktop -CsvOutput out(Serial, 2); -EncodedAudioStream dec(&out, new AACDecoderFAAD()); // aac data source -StreamCopy copier(dec, aac); // copy in to out - -void setup(){ - Serial.begin(115200); - AudioToolsLogger.begin(Serial, AudioToolsLogLevel::Info); - - dec.begin(); - - out.begin(); -} - -void loop(){ - if (aac) { - copier.copy(); - } else { - auto info = dec.decoder().audioInfo(); - LOGI("The audio rate from the aac file is %d", info.sample_rate); - LOGI("The channels from the aac file is %d", info.channels); - exit(0); - } -} - diff --git a/tests-cmake/codec/aac-faad/audio.h b/tests-cmake/codec/aac-faad/audio.h deleted file mode 100644 index 0363c2a644..0000000000 --- a/tests-cmake/codec/aac-faad/audio.h +++ /dev/null @@ -1,20136 +0,0 @@ -const unsigned char gs_16b_2c_44100hz_aac[] = { - 0xff, 0xf1, 0x50, 0x80, 0x0b, 0xbf, 0xfc, 0xde, 0x02, 0x00, 0x4c, 0x61, - 0x76, 0x63, 0x35, 0x38, 0x2e, 0x33, 0x34, 0x2e, 0x31, 0x30, 0x30, 0x00, - 0x42, 0x35, 0x90, 0x00, 0x20, 0x00, 0x00, 0x1e, 0xe4, 0x0e, 0x10, 0x82, - 0x75, 0xe2, 0x43, 0xc7, 0xbc, 0xe7, 0x2b, 0x28, 0x9a, 0xcd, 0x6f, 0xf6, - 0x00, 0x19, 0x80, 0x00, 0x33, 0x1a, 0x66, 0x25, 0xda, 0x99, 0xdc, 0x80, - 0x82, 0x10, 0x48, 0x84, 0x08, 0x21, 0x02, 0x6b, 0xc4, 0x81, 0xe5, 0xbd, - 0xf3, 0xcc, 0x56, 0x24, 0xeb, 0x39, 0xfd, 0x80, 0xb1, 0x50, 0x00, 0x05, - 0xaf, 0xf2, 0x0c, 0xe6, 0x7d, 0xa7, 0xe5, 0x18, 0xf0, 0xff, 0xf1, 0x50, - 0x80, 0x10, 0x3f, 0xfc, 0x21, 0x1a, 0xc8, 0x9d, 0xfa, 0xc0, 0x3f, 0x1f, - 0x72, 0x01, 0x88, 0x40, 0x42, 0xf0, 0x1e, 0x94, 0x4d, 0xa3, 0x12, 0x80, - 0x00, 0x3c, 0xfc, 0x66, 0x67, 0xcd, 0x28, 0xdf, 0x19, 0x80, 0x04, 0x22, - 0x04, 0x80, 0x0c, 0x01, 0x30, 0x00, 0x00, 0x8a, 0x40, 0x26, 0x00, 0x00, - 0x00, 0x05, 0x01, 0x69, 0x72, 0x6d, 0xaa, 0xa7, 0x1a, 0xf9, 0x6d, 0x08, - 0xbf, 0x49, 0xb6, 0x63, 0xcf, 0xe3, 0x72, 0x01, 0x88, 0x40, 0x22, 0x40, - 0x08, 0xb0, 0x05, 0xa3, 0x14, 0x68, 0xc4, 0xa0, 0x00, 0x07, 0xa6, 0xfb, - 0xf7, 0xa5, 0x28, 0xf1, 0xa9, 0x80, 0x16, 0x00, 0xa1, 0x18, 0x2a, 0x00, - 0x05, 0x80, 0x64, 0x08, 0x0d, 0xe5, 0x00, 0x0a, 0x20, 0x00, 0x00, 0x24, - 0x00, 0x00, 0x00, 0x04, 0xa0, 0x5f, 0xf3, 0xcf, 0x09, 0x30, 0x2f, 0x7e, - 0xf1, 0x6e, 0xd0, 0xfd, 0x3d, 0x3c, 0xff, 0xf1, 0x50, 0x80, 0x12, 0xff, - 0xfc, 0x21, 0x1a, 0xc8, 0x03, 0xf3, 0xc0, 0xff, 0x9e, 0x72, 0x01, 0x08, - 0x40, 0xa2, 0x70, 0x10, 0x90, 0x07, 0xa3, 0x14, 0xe8, 0x84, 0xa0, 0x00, - 0xf2, 0xef, 0xc7, 0x8a, 0x52, 0x85, 0x56, 0x2a, 0x82, 0xb0, 0x5c, 0xa0, - 0x00, 0xa0, 0x00, 0x2c, 0x08, 0x04, 0xe1, 0x32, 0x62, 0x00, 0xa2, 0x72, - 0x0f, 0x78, 0x5a, 0xd0, 0xa5, 0x00, 0xa8, 0x00, 0x00, 0x08, 0x41, 0x55, - 0x1c, 0xa5, 0x6f, 0x55, 0x64, 0x85, 0xd7, 0xfe, 0xf5, 0xaf, 0x2f, 0x2f, - 0xc7, 0x29, 0xe6, 0xfe, 0xae, 0x40, 0x91, 0x86, 0x88, 0x53, 0xa2, 0x12, - 0x80, 0x00, 0x3e, 0x73, 0xc5, 0x29, 0x43, 0x6c, 0x55, 0x00, 0xc6, 0x16, - 0x17, 0x09, 0x2c, 0x01, 0x40, 0x00, 0x2a, 0x0b, 0x80, 0x4e, 0xc6, 0x0e, - 0x40, 0xe2, 0x0a, 0x46, 0x46, 0x89, 0x90, 0x65, 0x58, 0xa2, 0x62, 0x68, - 0x84, 0x08, 0x84, 0x40, 0xae, 0x13, 0x24, 0xd7, 0x2f, 0x9e, 0x44, 0xb3, - 0x89, 0x4e, 0x57, 0x95, 0xa5, 0x5b, 0xf2, 0xf5, 0xc3, 0x66, 0x0d, 0x15, - 0xe0, 0xff, 0xf1, 0x50, 0x80, 0x11, 0xbf, 0xfc, 0x21, 0x1a, 0xc9, 0x09, - 0xf5, 0xe0, 0x04, 0x1f, 0x72, 0x01, 0x08, 0x40, 0x82, 0x10, 0x10, 0xb0, - 0x05, 0xa8, 0x10, 0xe9, 0x44, 0x88, 0x10, 0x06, 0xf4, 0x1e, 0x7c, 0xf3, - 0x99, 0xba, 0x7e, 0x6a, 0xde, 0x7b, 0xaa, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x02, 0x09, 0x73, 0x41, 0xe3, 0x87, 0xba, 0xb1, 0x46, 0xf9, - 0x2c, 0x08, 0x7b, 0xfc, 0xb6, 0xb8, 0xe2, 0x8e, 0x40, 0x22, 0x10, 0x04, - 0x42, 0x01, 0x10, 0x80, 0x45, 0xa0, 0x2d, 0x18, 0xa7, 0x44, 0x25, 0x00, - 0x00, 0x3c, 0xbb, 0xe7, 0xde, 0x94, 0xa1, 0xba, 0xaa, 0x00, 0x8a, 0xae, - 0xe0, 0x22, 0x14, 0x00, 0x00, 0x05, 0x00, 0xa7, 0xbd, 0x2b, 0x4e, 0xd7, - 0x4d, 0x62, 0x42, 0x64, 0xd7, 0x58, 0x13, 0x2b, 0x48, 0x4e, 0x45, 0x08, - 0xd0, 0x02, 0x20, 0x05, 0x84, 0x42, 0xf6, 0x2f, 0xcd, 0xa4, 0xbe, 0xff, - 0xac, 0x5b, 0xf5, 0x62, 0x46, 0x3f, 0x1f, 0x95, 0xf5, 0xf0, 0xff, 0xf1, - 0x50, 0x80, 0x15, 0x7f, 0xfc, 0x21, 0x1a, 0xc8, 0x80, 0xdf, 0xe0, 0x00, - 0x1b, 0x72, 0x01, 0x88, 0xc0, 0xa2, 0xb0, 0x16, 0x9c, 0x43, 0xa2, 0x10, - 0xe8, 0xc4, 0xa0, 0x00, 0x3d, 0x39, 0xde, 0x77, 0x53, 0xf6, 0xa2, 0x71, - 0x9a, 0x6c, 0x08, 0xe5, 0x54, 0x1a, 0x80, 0x12, 0x20, 0x00, 0x24, 0x26, - 0x00, 0x16, 0x04, 0x08, 0xc8, 0x71, 0xcf, 0x2e, 0x19, 0x61, 0x99, 0xd9, - 0xce, 0x56, 0xe1, 0xbf, 0xba, 0x11, 0xcf, 0xe1, 0x72, 0x01, 0x88, 0x40, - 0x44, 0x10, 0x20, 0xac, 0x05, 0xa1, 0x17, 0x90, 0xc0, 0x00, 0xf3, 0xdd, - 0x55, 0x29, 0x42, 0x94, 0xac, 0x03, 0xa4, 0x81, 0x70, 0x28, 0x00, 0x01, - 0x01, 0x6c, 0x92, 0x5a, 0x04, 0x1b, 0x82, 0xf2, 0x9d, 0x14, 0xcf, 0x1c, - 0x44, 0xf1, 0xc3, 0xef, 0xe0, 0xb6, 0x07, 0xa3, 0x57, 0xdf, 0x72, 0x5f, - 0x5f, 0xd1, 0x09, 0xd6, 0x75, 0x9c, 0x7b, 0x52, 0xb8, 0xbe, 0x14, 0xcb, - 0x85, 0x8e, 0x31, 0xb5, 0x27, 0x5e, 0x28, 0xcd, 0x30, 0x58, 0x04, 0xc5, - 0x02, 0x91, 0xb8, 0xe3, 0x0b, 0x75, 0xac, 0xdf, 0x87, 0x81, 0x19, 0x50, - 0x75, 0x86, 0xd4, 0xb4, 0xd4, 0x1e, 0xf4, 0x9d, 0x4d, 0xfa, 0x5a, 0xcb, - 0xb8, 0xff, 0xf1, 0x50, 0x80, 0x17, 0x9f, 0xfc, 0x21, 0x1a, 0xc8, 0x21, - 0x3e, 0xc0, 0x7f, 0x8f, 0x72, 0x01, 0x88, 0x40, 0x22, 0x10, 0x20, 0xac, - 0x07, 0xa2, 0x15, 0x68, 0x88, 0xa0, 0x00, 0x7a, 0xf3, 0xcd, 0x29, 0x45, - 0x3b, 0xaa, 0xaa, 0x60, 0xa8, 0x00, 0x15, 0x44, 0x00, 0x0b, 0xf6, 0x0a, - 0x02, 0x55, 0x9d, 0x00, 0x46, 0xbf, 0xc9, 0x0c, 0x65, 0x76, 0x54, 0x42, - 0xd1, 0xf1, 0x7e, 0xd6, 0x31, 0xdf, 0x0f, 0x7b, 0x0d, 0xa9, 0xa1, 0x4b, - 0x42, 0xfd, 0xe2, 0x80, 0x00, 0x12, 0x96, 0xb0, 0x94, 0x28, 0xc4, 0x6a, - 0xad, 0x37, 0x49, 0xed, 0x38, 0x77, 0x87, 0xf9, 0xf7, 0x05, 0x1d, 0x79, - 0xb6, 0xa6, 0x3f, 0xe3, 0x08, 0x56, 0xf3, 0xce, 0x40, 0x41, 0x10, 0x08, - 0x82, 0x01, 0x15, 0x80, 0xf4, 0x82, 0x8d, 0x19, 0x10, 0x00, 0x07, 0xa6, - 0x78, 0x77, 0x4a, 0x29, 0xb6, 0x65, 0x30, 0x49, 0x0d, 0xcb, 0xba, 0x0a, - 0x02, 0x82, 0x94, 0xa0, 0x46, 0x2b, 0x37, 0xa4, 0x2f, 0xbe, 0xeb, 0xdc, - 0x87, 0xa1, 0xbe, 0xea, 0x7a, 0x68, 0x49, 0x1b, 0x63, 0xbe, 0xd9, 0x94, - 0x43, 0xd5, 0x29, 0x6d, 0xc6, 0xe7, 0x0b, 0x45, 0x22, 0x89, 0x00, 0x25, - 0x52, 0x01, 0xd6, 0x4f, 0x9a, 0xab, 0x48, 0x2b, 0xef, 0x4b, 0x47, 0xd4, - 0xab, 0x65, 0x4f, 0xca, 0x4b, 0x51, 0x6b, 0xd0, 0xf0, 0xff, 0xf1, 0x50, - 0x80, 0x16, 0x7f, 0xfc, 0x21, 0x1a, 0xc8, 0x00, 0xaf, 0xc1, 0xff, 0xbf, - 0x72, 0x01, 0x08, 0x40, 0xa4, 0x10, 0x10, 0x84, 0x02, 0x2c, 0xd0, 0x8a, - 0xb4, 0x22, 0x42, 0x15, 0x40, 0x00, 0x3d, 0xf9, 0xaa, 0x52, 0x87, 0x8a, - 0xa5, 0x55, 0x09, 0x90, 0x02, 0xf1, 0x4c, 0x00, 0x00, 0x00, 0xb0, 0x48, - 0x9a, 0x35, 0x04, 0x06, 0x8b, 0xc6, 0x95, 0x92, 0xaa, 0xa6, 0xbc, 0x5f, - 0x1b, 0x0a, 0xc4, 0xe4, 0x4f, 0x34, 0x6f, 0x57, 0xa0, 0xa2, 0x50, 0xb2, - 0x44, 0x00, 0x00, 0x00, 0x2c, 0xf1, 0xa4, 0xb6, 0xff, 0x3a, 0xb8, 0x5c, - 0xa3, 0x3f, 0xa4, 0x0b, 0x5f, 0xe5, 0xce, 0xd4, 0xf8, 0x4e, 0x94, 0xf9, - 0x9d, 0xde, 0xa4, 0xb9, 0x02, 0x45, 0xa0, 0x2d, 0x08, 0xc8, 0x00, 0x0f, - 0x2d, 0xd5, 0x52, 0x94, 0x29, 0x4a, 0xaa, 0x15, 0x84, 0x0c, 0xb0, 0x39, - 0xa0, 0x00, 0xb0, 0x15, 0x03, 0x88, 0x01, 0x13, 0xa8, 0x16, 0xba, 0x77, - 0x62, 0x0e, 0x1c, 0xa9, 0xe7, 0x51, 0x18, 0x35, 0x4e, 0x37, 0xb2, 0xb5, - 0xbe, 0xe6, 0xc0, 0xb3, 0x95, 0x02, 0x40, 0x01, 0x04, 0xc0, 0x00, 0x01, - 0x7b, 0x5f, 0x6b, 0x01, 0xda, 0x55, 0xbf, 0x39, 0xf1, 0xf7, 0x0e, 0x32, - 0x9c, 0x67, 0xba, 0x1f, 0x6d, 0x90, 0xe9, 0x7e, 0xff, 0xf1, 0x50, 0x80, - 0x19, 0x1f, 0xfc, 0x21, 0x1a, 0xc8, 0x09, 0xe7, 0xc6, 0xbe, 0x9f, 0x72, - 0x01, 0x88, 0x40, 0x22, 0x10, 0x08, 0x84, 0x04, 0x30, 0xd0, 0x8a, 0x74, - 0x42, 0x24, 0x18, 0x00, 0x01, 0xb7, 0xe6, 0x64, 0x51, 0x93, 0x32, 0xa9, - 0x58, 0x29, 0x71, 0x00, 0x95, 0xc2, 0x41, 0x40, 0x00, 0x00, 0x00, 0x00, - 0x04, 0x2a, 0x7a, 0xc0, 0xba, 0xd6, 0x9d, 0xa8, 0x2c, 0xac, 0x1e, 0x37, - 0x85, 0x7b, 0xd3, 0x27, 0x6e, 0x72, 0xbd, 0x76, 0x5f, 0xeb, 0x44, 0xe0, - 0x94, 0xc0, 0x00, 0x42, 0x96, 0x82, 0x49, 0x7a, 0xd4, 0xbb, 0x75, 0x1e, - 0x6f, 0x2f, 0x3a, 0x04, 0xec, 0x4c, 0x06, 0x3d, 0x58, 0x36, 0xea, 0x66, - 0x39, 0xb1, 0xca, 0x7b, 0x1c, 0x80, 0x82, 0x10, 0x08, 0x88, 0x04, 0x24, - 0x01, 0x0a, 0xb4, 0x22, 0xe4, 0x10, 0x00, 0x06, 0xdf, 0x58, 0xa5, 0x0a, - 0x2a, 0x95, 0x82, 0xf7, 0x13, 0xa8, 0x98, 0xb8, 0x00, 0x00, 0x00, 0xa8, - 0x02, 0xa8, 0x94, 0xcb, 0x4c, 0xdc, 0x7b, 0xa4, 0x22, 0x5b, 0x66, 0x9b, - 0xd2, 0x15, 0xc6, 0xa6, 0x1b, 0xfd, 0xcf, 0x40, 0x42, 0xbc, 0xe5, 0xcb, - 0xa2, 0xcc, 0x9a, 0x10, 0xe3, 0xb6, 0xbe, 0xb4, 0x52, 0x48, 0x14, 0x00, - 0x14, 0x22, 0x20, 0x4a, 0xf4, 0x64, 0xe3, 0xe0, 0x46, 0xee, 0xbc, 0xf6, - 0xd9, 0x4a, 0x63, 0x05, 0x10, 0xc7, 0x27, 0x92, 0x5b, 0x7b, 0x08, 0x2d, - 0x2c, 0x4a, 0xae, 0x5c, 0xff, 0xf1, 0x50, 0x80, 0x13, 0xff, 0xfc, 0x21, - 0x1a, 0xc8, 0x0b, 0x6b, 0xa0, 0x3f, 0x1f, 0x72, 0x01, 0x88, 0x40, 0x62, - 0x10, 0x08, 0xb4, 0x05, 0xa5, 0x13, 0x68, 0xc4, 0xa0, 0x00, 0x1e, 0x1e, - 0xfb, 0xe5, 0xea, 0x94, 0x6f, 0x55, 0x8a, 0x50, 0x88, 0x09, 0x9d, 0xc0, - 0x06, 0x90, 0x04, 0xd1, 0x52, 0xd5, 0x82, 0x80, 0xa2, 0x55, 0x4e, 0xe1, - 0x19, 0xaf, 0x40, 0xd8, 0x01, 0x52, 0xc0, 0x28, 0x09, 0x62, 0x1e, 0x2b, - 0xa9, 0xc1, 0xf1, 0x4b, 0xc5, 0x92, 0xcf, 0xd9, 0x83, 0x1d, 0xb8, 0xeb, - 0xfc, 0x0e, 0x40, 0x41, 0x08, 0x04, 0x67, 0xa1, 0x15, 0x68, 0x84, 0xa0, - 0x00, 0x00, 0xf7, 0xf0, 0xa5, 0x28, 0x7b, 0xe6, 0x29, 0x42, 0x5b, 0x40, - 0x00, 0x80, 0x24, 0x00, 0x00, 0x01, 0x10, 0x82, 0x62, 0xc1, 0x84, 0xd7, - 0xec, 0x05, 0x90, 0xb4, 0x57, 0x71, 0xbd, 0x2e, 0x7b, 0x2d, 0x25, 0x81, - 0x55, 0xee, 0xbe, 0xd2, 0x21, 0x51, 0x62, 0xab, 0x00, 0x00, 0xcd, 0x5b, - 0x53, 0x15, 0x91, 0x53, 0x24, 0xa4, 0x94, 0x29, 0xcf, 0xc5, 0xb1, 0x8f, - 0xc7, 0x5f, 0x3d, 0xff, 0xfe, 0xcc, 0xdc, 0xff, 0xf1, 0x50, 0x80, 0x11, - 0x1f, 0xfc, 0x21, 0x1a, 0xc8, 0x23, 0x7f, 0x80, 0xff, 0x9f, 0x72, 0x02, - 0x09, 0x00, 0x22, 0xb0, 0x26, 0x8c, 0x53, 0xa1, 0x13, 0x00, 0x00, 0x79, - 0xf7, 0x5e, 0xa9, 0x45, 0x1d, 0xd2, 0xa8, 0x17, 0x42, 0xc9, 0x11, 0x11, - 0x00, 0x00, 0x00, 0x00, 0x15, 0x9e, 0x61, 0x50, 0x0c, 0xc9, 0x8a, 0xc3, - 0x71, 0x70, 0x05, 0x17, 0x00, 0x00, 0x2a, 0x17, 0x8a, 0x01, 0x52, 0x65, - 0x2c, 0x20, 0x77, 0x9d, 0xe9, 0x0d, 0x31, 0x46, 0x77, 0xef, 0x83, 0xf7, - 0x72, 0x02, 0x88, 0x40, 0x62, 0x20, 0x08, 0xa0, 0x09, 0xa3, 0x14, 0xe8, - 0x84, 0xa0, 0x01, 0xe7, 0xe2, 0xbd, 0x52, 0x8a, 0x3b, 0x55, 0x50, 0x00, - 0x05, 0x00, 0x00, 0x50, 0x04, 0x80, 0x04, 0x0b, 0xd4, 0x56, 0x31, 0x05, - 0x0a, 0xc4, 0x51, 0x00, 0x00, 0x00, 0x26, 0x00, 0x22, 0x98, 0x84, 0xb0, - 0x30, 0x60, 0xc5, 0x2f, 0x6a, 0xba, 0x3e, 0x19, 0x17, 0x92, 0xdc, 0xff, - 0xf1, 0x50, 0x80, 0x10, 0xdf, 0xfc, 0x21, 0x1a, 0xc8, 0x23, 0xd6, 0x80, - 0x0f, 0x1f, 0x72, 0x02, 0x88, 0x40, 0x22, 0xe0, 0x1e, 0x90, 0x51, 0xa2, - 0x12, 0x80, 0x00, 0x79, 0xf7, 0xcd, 0x7b, 0xd2, 0x85, 0x55, 0x52, 0x80, - 0xac, 0xc0, 0x2f, 0x32, 0xa0, 0x44, 0x13, 0xb0, 0x37, 0xe9, 0xb8, 0x55, - 0x82, 0x27, 0x0c, 0xb0, 0xae, 0x60, 0x09, 0x28, 0x00, 0x9d, 0x32, 0xae, - 0x4c, 0x00, 0xa0, 0x00, 0x01, 0xb6, 0xc1, 0x9d, 0xb6, 0x58, 0xb3, 0xd4, - 0xac, 0x29, 0x47, 0xfd, 0xff, 0xf8, 0xdc, 0x80, 0xa2, 0x10, 0x18, 0xac, - 0x09, 0xa7, 0x12, 0x68, 0xc4, 0xa0, 0x07, 0x3c, 0x3d, 0x73, 0xc3, 0x37, - 0x9f, 0x61, 0xc5, 0x5e, 0x28, 0x11, 0x50, 0x00, 0x00, 0x00, 0x94, 0x26, - 0x05, 0x2c, 0x48, 0x28, 0x00, 0x0a, 0x80, 0x21, 0x42, 0x8a, 0xf2, 0x57, - 0xbf, 0xe7, 0x82, 0x0c, 0x74, 0x84, 0xde, 0x3e, 0x3a, 0x7a, 0x65, 0xf4, - 0xf0, 0xff, 0xf1, 0x50, 0x80, 0x12, 0xff, 0xfc, 0x21, 0x1a, 0xc8, 0x01, - 0x8c, 0x80, 0x7d, 0x8f, 0x72, 0x01, 0x90, 0x40, 0xa2, 0xb0, 0x26, 0x8c, - 0x5c, 0x42, 0x00, 0x07, 0x8f, 0x8c, 0xf1, 0x4a, 0x50, 0xa5, 0x2a, 0x80, - 0xa2, 0x00, 0x02, 0x80, 0x22, 0x44, 0xa0, 0x01, 0x37, 0x5a, 0x5c, 0x21, - 0x62, 0x90, 0x15, 0x80, 0x2e, 0x4b, 0x77, 0xfc, 0x09, 0x00, 0x08, 0x24, - 0x26, 0x00, 0x04, 0x0a, 0xa0, 0x41, 0x65, 0x93, 0x61, 0x95, 0xbd, 0xf3, - 0x5a, 0x61, 0xec, 0xd5, 0x47, 0x7e, 0x86, 0x7a, 0xb3, 0x3d, 0xd8, 0x0b, - 0x7d, 0xdc, 0x80, 0xa2, 0x10, 0x20, 0xa8, 0x09, 0xa4, 0x14, 0x68, 0xc4, - 0x80, 0x1d, 0xf4, 0x7a, 0x73, 0x9c, 0xfa, 0x52, 0xb2, 0xd3, 0x7b, 0xca, - 0xa0, 0xa0, 0xb0, 0x00, 0x00, 0xd2, 0x0c, 0xd1, 0x02, 0x44, 0xb4, 0x21, - 0x30, 0x6e, 0xfc, 0x13, 0xb9, 0x6a, 0xd0, 0x2e, 0x13, 0xa8, 0x00, 0x09, - 0x78, 0x2f, 0x31, 0xa7, 0x5e, 0x8b, 0x90, 0xc9, 0x48, 0xc2, 0xef, 0x9e, - 0x2b, 0xf4, 0xe4, 0xeb, 0x5d, 0xec, 0xf2, 0xe0, 0xff, 0xf1, 0x50, 0x80, - 0x13, 0xdf, 0xfc, 0x21, 0x1a, 0xc8, 0x0b, 0xff, 0xc0, 0xf9, 0xdf, 0x72, - 0x03, 0x88, 0x40, 0x22, 0xc0, 0x1e, 0x84, 0x64, 0x00, 0x07, 0x9f, 0x35, - 0x4a, 0x50, 0xa2, 0xa9, 0x80, 0x01, 0x55, 0x80, 0x00, 0x01, 0x20, 0x84, - 0xe2, 0x04, 0xc4, 0xda, 0xce, 0x96, 0x56, 0x2f, 0xa9, 0x52, 0x41, 0x5b, - 0xc0, 0x42, 0x7f, 0x56, 0x57, 0x48, 0x47, 0xe4, 0x94, 0x3b, 0xd6, 0x52, - 0x23, 0x10, 0x00, 0x00, 0x02, 0x40, 0x0a, 0x81, 0x04, 0x29, 0xaa, 0x5f, - 0xbc, 0xa1, 0x8e, 0x67, 0x5e, 0xd5, 0x71, 0xd9, 0x9b, 0x5d, 0xe9, 0x77, - 0x20, 0x09, 0x04, 0x0a, 0x2e, 0x01, 0xe8, 0xc5, 0x5a, 0x11, 0x28, 0x00, - 0x07, 0x97, 0x7c, 0xf8, 0x52, 0xaa, 0xae, 0x9e, 0x2a, 0x98, 0x13, 0x2e, - 0x00, 0x80, 0x00, 0x00, 0x09, 0xd0, 0x2d, 0x10, 0x0b, 0x4a, 0x49, 0x51, - 0xa1, 0x32, 0x8b, 0x0c, 0x12, 0xcd, 0x6a, 0xcc, 0xa2, 0xb5, 0x05, 0x5a, - 0x40, 0x00, 0x01, 0x4b, 0x2e, 0x26, 0x36, 0x4e, 0x49, 0xd2, 0x51, 0x3c, - 0x4d, 0xb8, 0x36, 0x71, 0xe1, 0x61, 0x09, 0x5a, 0x3e, 0x8e, 0xff, 0xf1, - 0x50, 0x80, 0x11, 0xff, 0xfc, 0x21, 0x1a, 0xc8, 0x07, 0xfb, 0xc0, 0x7f, - 0x0f, 0x72, 0x03, 0x8b, 0x80, 0x7a, 0x41, 0x3e, 0x90, 0x48, 0x00, 0x03, - 0xd3, 0x9c, 0xdf, 0xaa, 0x50, 0xaa, 0x73, 0x95, 0x41, 0x30, 0x0d, 0x81, - 0x70, 0x00, 0x00, 0x17, 0x09, 0xac, 0x58, 0x24, 0xcb, 0x05, 0xa7, 0x6d, - 0x19, 0x80, 0x88, 0x48, 0x19, 0x86, 0xd1, 0x20, 0xaa, 0x3d, 0x90, 0xd8, - 0xad, 0x72, 0xf9, 0x52, 0x52, 0xfa, 0x5f, 0xc3, 0x66, 0xff, 0x1b, 0x90, - 0x20, 0x63, 0xa1, 0x15, 0x68, 0x84, 0x44, 0x30, 0x00, 0x03, 0xe3, 0xc2, - 0xa9, 0x41, 0xe2, 0xb1, 0x54, 0x04, 0x44, 0x00, 0x58, 0x00, 0x00, 0x00, - 0x0b, 0xd9, 0x60, 0x42, 0x68, 0x81, 0x59, 0x08, 0x82, 0x22, 0xc0, 0xaa, - 0xb1, 0x41, 0xca, 0xc2, 0xa2, 0xeb, 0x88, 0x05, 0xcd, 0x82, 0xe1, 0x53, - 0x6d, 0x42, 0xf6, 0xa5, 0xca, 0x62, 0xeb, 0x25, 0x31, 0x00, 0x1d, 0xce, - 0x85, 0x79, 0x4b, 0xaa, 0xea, 0x71, 0x0b, 0x2f, 0x2e, 0xff, 0xf1, 0x50, - 0x80, 0x16, 0x7f, 0xfc, 0x21, 0x1a, 0xc8, 0x80, 0xea, 0xc1, 0xff, 0xbf, - 0x72, 0x01, 0x88, 0x40, 0xe2, 0xb0, 0x16, 0x84, 0x5e, 0x43, 0x00, 0x07, - 0x96, 0xf1, 0x4a, 0x50, 0xa5, 0x2a, 0xa8, 0x16, 0x13, 0xa8, 0x09, 0x00, - 0x17, 0x02, 0x20, 0x05, 0xcb, 0x13, 0xaa, 0x39, 0xd3, 0xec, 0x2d, 0x23, - 0x20, 0xa3, 0x1a, 0xf6, 0x8a, 0x19, 0xd5, 0x24, 0xd3, 0x5a, 0x8b, 0x22, - 0xb8, 0x22, 0x88, 0x28, 0x54, 0x00, 0x12, 0x16, 0x4e, 0x32, 0x77, 0x9c, - 0x69, 0xab, 0xa1, 0x67, 0x3b, 0x4a, 0x95, 0x73, 0xb9, 0x79, 0xfa, 0x59, - 0x52, 0x9f, 0x89, 0xbc, 0x47, 0x4c, 0xa7, 0xeb, 0x72, 0x00, 0x88, 0x40, - 0x24, 0x10, 0x28, 0xbf, 0x44, 0x2a, 0xd0, 0x88, 0x88, 0x80, 0x00, 0x07, - 0x9e, 0xfb, 0x52, 0x94, 0x3d, 0xea, 0x95, 0x54, 0x24, 0x54, 0x17, 0x17, - 0x02, 0x4a, 0x00, 0x02, 0xa0, 0x00, 0x01, 0x20, 0x00, 0xf9, 0x03, 0x14, - 0x19, 0x57, 0x7c, 0x95, 0x52, 0x5d, 0xed, 0xa3, 0x98, 0x23, 0xcb, 0xb4, - 0x58, 0xa9, 0x61, 0x9d, 0x65, 0xc2, 0x40, 0x00, 0x0b, 0x04, 0xa9, 0x19, - 0x7f, 0x5e, 0x2b, 0xa2, 0xb3, 0x1d, 0x96, 0x05, 0x31, 0x39, 0xad, 0x24, - 0x48, 0x4f, 0xab, 0xa0, 0xc9, 0x67, 0x92, 0xf8, 0xff, 0xf1, 0x50, 0x80, - 0x16, 0x1f, 0xfc, 0x21, 0x1a, 0xc8, 0x11, 0xff, 0xc0, 0x3b, 0x9f, 0x72, - 0x03, 0x08, 0x40, 0x22, 0x10, 0x08, 0xac, 0x07, 0xa3, 0x18, 0x00, 0x01, - 0xe9, 0xbd, 0xf7, 0x4a, 0x28, 0x52, 0xab, 0x20, 0x48, 0x00, 0x08, 0x80, - 0x0b, 0xa1, 0x74, 0xf0, 0xc3, 0xaa, 0x1b, 0x31, 0xd1, 0x1f, 0x92, 0xa9, - 0xc2, 0xb7, 0x40, 0x86, 0xf9, 0xc3, 0xe0, 0x6b, 0xa9, 0xc1, 0xfd, 0xd6, - 0x96, 0x7c, 0x82, 0xca, 0xa7, 0x58, 0x09, 0x10, 0x00, 0x5c, 0x10, 0x08, - 0x0a, 0xdd, 0x34, 0x4b, 0xc7, 0x7c, 0xfa, 0x8d, 0xf7, 0x86, 0xfe, 0xe8, - 0x51, 0x9f, 0x26, 0xd5, 0x8d, 0xdf, 0x58, 0xfc, 0xef, 0xf9, 0xb9, 0x00, - 0x84, 0x20, 0x31, 0x08, 0x08, 0x58, 0x03, 0xd2, 0x89, 0xf4, 0x42, 0x22, - 0x18, 0x00, 0x0f, 0x2f, 0x19, 0x9b, 0xf1, 0x4c, 0x85, 0x2a, 0xab, 0x20, - 0x9a, 0x62, 0xe0, 0x00, 0x02, 0x83, 0xbc, 0xe2, 0x92, 0xb7, 0xad, 0x39, - 0x0d, 0xa5, 0xf8, 0x22, 0x25, 0x4b, 0x43, 0x02, 0x27, 0x29, 0xcc, 0x32, - 0x3a, 0x00, 0x40, 0x13, 0xb1, 0x2a, 0x23, 0x49, 0xc2, 0x40, 0x9e, 0x9e, - 0xff, 0x75, 0xb3, 0xb1, 0xc0, 0x2d, 0x5b, 0xfe, 0xca, 0x75, 0x4e, 0x65, - 0x67, 0xf3, 0xf5, 0x1f, 0xff, 0xf1, 0x50, 0x80, 0x13, 0x7f, 0xfc, 0x21, - 0x1a, 0xc8, 0x9b, 0xff, 0x00, 0x7f, 0x1f, 0x72, 0x00, 0x90, 0x40, 0x22, - 0x20, 0x08, 0x88, 0x02, 0x2a, 0x02, 0x88, 0x74, 0x62, 0x8d, 0x10, 0x94, - 0x00, 0x01, 0xf3, 0x5c, 0xfa, 0xa5, 0x14, 0xde, 0xaa, 0x94, 0x04, 0xc4, - 0x88, 0x40, 0x02, 0x10, 0x54, 0x54, 0x22, 0x00, 0x54, 0x13, 0x11, 0x00, - 0x3b, 0x6b, 0x01, 0x10, 0x84, 0x80, 0xab, 0xb0, 0x00, 0x04, 0x00, 0x6d, - 0x42, 0x7b, 0x30, 0x6e, 0xab, 0xa5, 0x36, 0x34, 0x5b, 0x54, 0xa1, 0xaa, - 0xd8, 0x7c, 0xfe, 0xd7, 0x20, 0x10, 0x88, 0x04, 0x30, 0x01, 0x68, 0x84, - 0x3a, 0x11, 0x3e, 0x8c, 0x46, 0x42, 0x00, 0x00, 0x1e, 0x5e, 0x3d, 0x78, - 0x52, 0x8a, 0xdd, 0xaa, 0x94, 0x13, 0x17, 0x82, 0xe0, 0x11, 0x00, 0x00, - 0xb8, 0x13, 0x15, 0x58, 0xe0, 0x0b, 0xa4, 0x28, 0x6b, 0x59, 0x40, 0x92, - 0xeb, 0x27, 0x90, 0xca, 0x81, 0x10, 0x5c, 0x08, 0x82, 0x82, 0x29, 0x94, - 0xdd, 0x75, 0x56, 0x8d, 0x67, 0xc1, 0x78, 0xaf, 0x82, 0xde, 0xf5, 0xea, - 0xb2, 0x6b, 0xb8, 0xff, 0xf1, 0x50, 0x80, 0x10, 0x7f, 0xfc, 0x21, 0x1a, - 0xc8, 0x21, 0xfd, 0x60, 0x3a, 0x0f, 0x72, 0x00, 0x88, 0x80, 0x42, 0x10, - 0x09, 0x04, 0x02, 0x2d, 0x01, 0x68, 0xc4, 0xfa, 0x51, 0x25, 0x20, 0x00, - 0x0f, 0x3f, 0x55, 0xf2, 0xa5, 0x1e, 0xa5, 0xb9, 0xc0, 0x29, 0x31, 0x4c, - 0x00, 0x46, 0x2a, 0x00, 0x00, 0x05, 0x40, 0x4c, 0x00, 0x26, 0x00, 0x1c, - 0xd4, 0x01, 0x70, 0x02, 0x61, 0x61, 0x32, 0x1c, 0x6f, 0xf2, 0x94, 0x63, - 0x8b, 0x67, 0x67, 0x6e, 0xbf, 0x07, 0x20, 0x28, 0x84, 0x08, 0x2c, 0x01, - 0x69, 0x44, 0x7a, 0x11, 0x0e, 0x8c, 0x4c, 0x00, 0x65, 0x9e, 0x9c, 0xef, - 0x37, 0xf6, 0x56, 0xd1, 0xc6, 0x00, 0x20, 0x5c, 0xb9, 0x11, 0x40, 0x00, - 0xa8, 0x05, 0xc0, 0x00, 0x00, 0x00, 0x15, 0x12, 0x00, 0x95, 0xa4, 0x30, - 0x04, 0x7f, 0x7b, 0x6f, 0x70, 0x43, 0xdb, 0xd3, 0x1e, 0x1f, 0xd9, 0xce, - 0x75, 0xe0, 0xff, 0xf1, 0x50, 0x80, 0x15, 0xdf, 0xfc, 0x21, 0x1a, 0xc8, - 0x89, 0xff, 0x60, 0xff, 0x9f, 0x72, 0x01, 0x88, 0x40, 0x62, 0x10, 0x10, - 0xbb, 0x42, 0x32, 0x00, 0x00, 0x79, 0xef, 0x14, 0xa5, 0x0a, 0x52, 0xb0, - 0x26, 0x2c, 0x00, 0x01, 0x20, 0x44, 0x2e, 0x00, 0x08, 0x85, 0x48, 0x02, - 0xe4, 0xc6, 0x92, 0xa6, 0x7d, 0xcb, 0xeb, 0xcc, 0x44, 0xba, 0xb2, 0xf3, - 0x53, 0x48, 0x56, 0xe3, 0x2a, 0x10, 0x8e, 0x62, 0xc0, 0x54, 0x50, 0x24, - 0x00, 0x0a, 0x01, 0x4d, 0x73, 0xc4, 0xd6, 0x87, 0x03, 0x0e, 0xd8, 0x74, - 0xe7, 0x53, 0x9b, 0xd9, 0xcb, 0xac, 0xf9, 0xe3, 0x7b, 0xbc, 0xdf, 0xf6, - 0xbf, 0x78, 0x39, 0x00, 0xc4, 0x20, 0x31, 0x10, 0x04, 0x58, 0x02, 0xd1, - 0x8a, 0x74, 0x46, 0x50, 0x00, 0x0f, 0x4c, 0xef, 0xba, 0x52, 0x86, 0xeb, - 0x15, 0x80, 0x65, 0x00, 0x05, 0x8a, 0x00, 0x00, 0x2a, 0x91, 0x00, 0x46, - 0x57, 0xf6, 0x21, 0xdc, 0x41, 0x0b, 0xad, 0x11, 0x76, 0xba, 0xa9, 0x30, - 0x2b, 0x21, 0x39, 0x63, 0x90, 0xad, 0x02, 0xc0, 0x2d, 0x42, 0x72, 0x39, - 0x9c, 0x8d, 0x3e, 0x25, 0xd4, 0xaf, 0x23, 0x65, 0x2b, 0xe9, 0xd1, 0x39, - 0x64, 0x9c, 0x9f, 0x6d, 0xc7, 0x6e, 0x4c, 0x78, 0xff, 0xf1, 0x50, 0x80, - 0x14, 0x7f, 0xfc, 0x21, 0x1a, 0xc9, 0x01, 0xf7, 0xc0, 0x34, 0x1f, 0x72, - 0x00, 0x88, 0x80, 0x42, 0x10, 0x11, 0x04, 0x02, 0x2f, 0xd2, 0x09, 0x34, - 0xa2, 0x5a, 0x80, 0x00, 0x07, 0xbf, 0x3b, 0xcf, 0x55, 0xbd, 0x6d, 0x35, - 0x59, 0x55, 0x41, 0x64, 0xa0, 0x17, 0x09, 0x5a, 0x62, 0x80, 0xb0, 0x00, - 0x00, 0x20, 0x11, 0x1b, 0xc2, 0x51, 0x99, 0x67, 0x4a, 0x14, 0xda, 0x5e, - 0x80, 0x01, 0x77, 0x38, 0x76, 0x15, 0x47, 0x67, 0x8f, 0xa4, 0xaa, 0xc0, - 0x2c, 0xc9, 0xa3, 0x5d, 0x3d, 0xb5, 0xeb, 0xff, 0x5c, 0x80, 0x44, 0x20, - 0x28, 0xb0, 0x07, 0xa3, 0x15, 0x68, 0x44, 0xa0, 0x00, 0x3c, 0xbc, 0x67, - 0x8a, 0x51, 0x43, 0xba, 0xaa, 0xa0, 0x59, 0x40, 0x20, 0x50, 0x00, 0x2e, - 0x02, 0x51, 0x14, 0x01, 0x49, 0xd6, 0x9d, 0x13, 0x04, 0x63, 0x0f, 0x6a, - 0x51, 0x6d, 0xd9, 0x8b, 0x2d, 0x43, 0x7b, 0x75, 0x0e, 0x57, 0xb3, 0x18, - 0x4d, 0x20, 0x17, 0x90, 0x00, 0x99, 0xb4, 0xf1, 0x55, 0x3b, 0xea, 0xda, - 0xaa, 0xc7, 0x4b, 0x90, 0xe1, 0xf9, 0xfa, 0x5a, 0x6d, 0x9f, 0x6b, 0xd9, - 0xff, 0xfc, 0xdc, 0xff, 0xf1, 0x50, 0x80, 0x14, 0xff, 0xfc, 0x21, 0x1a, - 0xc8, 0x01, 0x5f, 0x40, 0xbf, 0x8f, 0x72, 0x04, 0x8b, 0x00, 0x7a, 0x31, - 0x4e, 0x84, 0x48, 0x82, 0x00, 0x07, 0x9f, 0x8a, 0xee, 0xa9, 0x42, 0xbb, - 0x52, 0xb0, 0x28, 0x40, 0x17, 0x04, 0x40, 0x00, 0x50, 0xb2, 0xc4, 0xc2, - 0xa7, 0xa1, 0x1b, 0xad, 0x34, 0xc8, 0xc6, 0x55, 0x0a, 0xdd, 0x49, 0x0c, - 0xb6, 0xce, 0x5a, 0x82, 0x65, 0x00, 0x00, 0x64, 0x9c, 0x55, 0xa2, 0xae, - 0x85, 0xef, 0x1a, 0xcb, 0x4c, 0xb0, 0x31, 0x6e, 0x4e, 0x56, 0x5c, 0xa4, - 0xa3, 0x2a, 0xa9, 0x21, 0x6a, 0x0e, 0x5e, 0x38, 0x36, 0x8a, 0x2e, 0x40, - 0x41, 0x08, 0x08, 0x44, 0x01, 0x17, 0xe8, 0x85, 0x3a, 0x31, 0x20, 0x00, - 0x00, 0xf8, 0xdf, 0x7c, 0xda, 0x85, 0x6f, 0x32, 0xab, 0x02, 0x34, 0xa0, - 0x02, 0xe0, 0x00, 0x00, 0x00, 0x09, 0x10, 0x12, 0x0a, 0x80, 0xb4, 0x5a, - 0x52, 0x25, 0x11, 0x2b, 0x46, 0x2b, 0xa4, 0xbd, 0xf8, 0xb3, 0xd2, 0x04, - 0x4a, 0x25, 0x4b, 0xd0, 0x0b, 0x03, 0x81, 0x77, 0xa7, 0x2c, 0xba, 0x62, - 0xf0, 0x51, 0x29, 0x63, 0x46, 0x6d, 0x93, 0xf1, 0x3f, 0x7d, 0x1a, 0x2d, - 0x65, 0x38, 0xff, 0xf1, 0x50, 0x80, 0x14, 0x1f, 0xfc, 0x21, 0x2a, 0xc8, - 0x11, 0xf7, 0x40, 0x3e, 0x0f, 0x72, 0x01, 0x88, 0x40, 0x42, 0x30, 0x08, - 0x94, 0x02, 0x25, 0x01, 0xe9, 0x44, 0xba, 0x51, 0x20, 0x00, 0x0f, 0x2e, - 0xf9, 0xde, 0xfd, 0x52, 0x99, 0x38, 0xce, 0x72, 0xa8, 0x24, 0x85, 0xc2, - 0x40, 0x00, 0x00, 0xa8, 0xbf, 0x32, 0x05, 0x2d, 0x11, 0x44, 0x62, 0x20, - 0x42, 0x64, 0x62, 0xa0, 0x55, 0x55, 0x45, 0xb4, 0xcf, 0x6d, 0xe9, 0x7b, - 0x38, 0xeb, 0xe3, 0xfd, 0x29, 0xc5, 0x1f, 0x54, 0xef, 0xf4, 0xe1, 0xea, - 0x72, 0x00, 0x88, 0x40, 0x82, 0x10, 0x10, 0xb0, 0x07, 0xa3, 0x14, 0xe8, - 0x84, 0x64, 0x20, 0x00, 0x1e, 0x9c, 0xbc, 0x52, 0x94, 0x3b, 0xcc, 0x55, - 0x05, 0x69, 0x20, 0x28, 0x26, 0x00, 0xd7, 0x00, 0x50, 0x98, 0x36, 0xbe, - 0x17, 0x2e, 0xa4, 0x00, 0x4c, 0x38, 0x61, 0x6e, 0x86, 0x32, 0x55, 0x2e, - 0x82, 0x72, 0xb5, 0x8c, 0x00, 0xdd, 0x5b, 0x09, 0x02, 0xe0, 0xb0, 0x82, - 0x26, 0x2d, 0x52, 0xcf, 0x7b, 0x9b, 0x38, 0x04, 0xdf, 0xfc, 0xfb, 0xa9, - 0x73, 0x27, 0xc3, 0x53, 0x2f, 0x80, 0xff, 0xf1, 0x50, 0x80, 0x4e, 0xff, - 0xfc, 0x21, 0x4c, 0xcc, 0x80, 0x0a, 0xff, 0xfe, 0xf8, 0xbf, 0xb9, 0x07, - 0x82, 0x6e, 0x34, 0x29, 0x7c, 0xd1, 0x82, 0xca, 0xfa, 0xa7, 0xfb, 0x04, - 0xa4, 0xae, 0x25, 0x72, 0x00, 0x00, 0x01, 0xe7, 0xfe, 0x92, 0x38, 0x00, - 0x00, 0x0f, 0xcb, 0xd1, 0x61, 0xc8, 0x07, 0xb1, 0x0d, 0xf0, 0xf1, 0xfe, - 0x7f, 0x8f, 0xe4, 0xea, 0x3e, 0xcf, 0x1e, 0x92, 0xdd, 0x3d, 0xd9, 0x20, - 0x18, 0xc0, 0xb6, 0xf6, 0x7c, 0xfa, 0x4b, 0xa8, 0x99, 0xbd, 0x84, 0x34, - 0x53, 0xc8, 0x4d, 0xc2, 0x10, 0x3a, 0xc9, 0x51, 0x87, 0x2c, 0xf7, 0x82, - 0x70, 0xed, 0xd4, 0x58, 0xe2, 0x38, 0x98, 0x99, 0x0e, 0x21, 0x3a, 0x7a, - 0x3a, 0x8f, 0x70, 0x43, 0xa0, 0x4e, 0xad, 0x0b, 0xf2, 0xb5, 0x9d, 0x8b, - 0x1e, 0xb9, 0x3b, 0xd8, 0x52, 0x59, 0x31, 0x74, 0x67, 0xee, 0x88, 0x44, - 0x82, 0x4e, 0x19, 0xab, 0x4a, 0x58, 0x00, 0x73, 0x45, 0xca, 0x4c, 0x01, - 0x93, 0x43, 0x3e, 0x07, 0xd2, 0xec, 0xf2, 0x4c, 0x5a, 0x4c, 0xed, 0xb8, - 0x6c, 0xa8, 0x9e, 0xc0, 0xb9, 0x70, 0x37, 0x60, 0xc7, 0xca, 0x3c, 0xd9, - 0x11, 0x87, 0x3f, 0x60, 0xb5, 0x7e, 0x21, 0x1a, 0x3f, 0x1f, 0xfd, 0x75, - 0x17, 0x60, 0x8e, 0x14, 0x23, 0x06, 0x0d, 0x24, 0xb1, 0x46, 0xe9, 0x5d, - 0x62, 0xd1, 0xd0, 0xde, 0xd8, 0xa6, 0x3c, 0x27, 0x2e, 0x03, 0x23, 0x60, - 0x70, 0xb1, 0xd6, 0x9b, 0x96, 0xdc, 0xab, 0x21, 0x11, 0x18, 0x22, 0x4c, - 0x17, 0x3a, 0xea, 0xb2, 0xd5, 0x38, 0x6f, 0x11, 0xd1, 0xd6, 0x57, 0xb5, - 0x19, 0xa7, 0x55, 0x1b, 0xf7, 0xf5, 0xc2, 0x6a, 0xbf, 0xfe, 0x45, 0xd5, - 0xe1, 0xa8, 0x56, 0x20, 0x91, 0x03, 0xa8, 0xe9, 0xa8, 0xc6, 0x89, 0xb1, - 0x1e, 0xfd, 0x3f, 0xb5, 0x1f, 0x8d, 0xa4, 0xfc, 0x67, 0xa5, 0x21, 0x4a, - 0x31, 0x37, 0xee, 0x49, 0xf7, 0x8c, 0xf5, 0x6f, 0x08, 0x96, 0x6a, 0xd1, - 0x2b, 0x84, 0x21, 0x52, 0x45, 0x6b, 0x0a, 0x87, 0x61, 0x25, 0x08, 0x80, - 0x24, 0x4b, 0x11, 0x3e, 0xed, 0xce, 0x96, 0x62, 0x35, 0x43, 0x87, 0x45, - 0xb9, 0xbc, 0x0f, 0xab, 0x84, 0x9d, 0xc5, 0x39, 0x2d, 0x73, 0x6e, 0x33, - 0x50, 0x7a, 0x73, 0xac, 0xc6, 0x3e, 0x95, 0x53, 0x2c, 0x2b, 0xf9, 0x41, - 0x30, 0x74, 0xe4, 0x44, 0xb4, 0x2e, 0x41, 0xe0, 0x9b, 0x8d, 0xcd, 0xf3, - 0x12, 0xf9, 0x89, 0xfb, 0x04, 0xa4, 0xae, 0x25, 0x72, 0x00, 0x00, 0x00, - 0x00, 0x07, 0x4c, 0xe3, 0xf2, 0x6c, 0x4a, 0xb1, 0x46, 0x27, 0xa7, 0xe6, - 0xbf, 0x0a, 0x93, 0x31, 0x03, 0xf9, 0x7f, 0x79, 0xca, 0x59, 0x77, 0xf9, - 0x72, 0x48, 0x43, 0x55, 0xb3, 0x3c, 0xf3, 0xcd, 0x46, 0x22, 0xec, 0xb2, - 0xca, 0x09, 0x0d, 0x49, 0x44, 0x07, 0x16, 0x10, 0xd5, 0x1a, 0xcf, 0x49, - 0x39, 0xb5, 0x88, 0xba, 0xc4, 0xa0, 0x3b, 0x46, 0x5d, 0xd8, 0x62, 0x5b, - 0x44, 0xd7, 0x57, 0x48, 0x71, 0x9c, 0x85, 0xdc, 0x2f, 0xf2, 0x5a, 0xf1, - 0x88, 0x86, 0x51, 0x04, 0x5f, 0x26, 0x89, 0x75, 0xcc, 0x1a, 0x1c, 0xc4, - 0x89, 0x24, 0x90, 0xc5, 0x3f, 0x6a, 0xac, 0x55, 0xfd, 0xab, 0x64, 0xd4, - 0xad, 0x90, 0x89, 0x43, 0x25, 0xd8, 0x1e, 0x32, 0xcb, 0x8d, 0xd8, 0x94, - 0xba, 0xbd, 0x15, 0xfc, 0xb0, 0x1a, 0xc6, 0x2d, 0xd0, 0x0b, 0x2f, 0xa9, - 0x22, 0x7d, 0xe3, 0x98, 0x9c, 0x1e, 0x8d, 0x26, 0x83, 0x69, 0xb8, 0xbc, - 0x52, 0xa4, 0x81, 0xd2, 0x1f, 0x79, 0x7e, 0x41, 0x69, 0x87, 0x4e, 0x20, - 0xd9, 0xdf, 0xf3, 0x77, 0x1f, 0xc4, 0x7a, 0x12, 0x72, 0x87, 0xa8, 0xc1, - 0x74, 0x8a, 0xe6, 0x63, 0xe8, 0x6f, 0x37, 0xbf, 0xa1, 0xe2, 0x79, 0xf4, - 0xf4, 0xf8, 0x11, 0xaf, 0x29, 0xea, 0x68, 0xf8, 0xea, 0xc6, 0xdd, 0xc3, - 0x1a, 0x76, 0x83, 0x43, 0x9b, 0x4d, 0x8b, 0xd7, 0x8d, 0x87, 0x59, 0xc3, - 0x08, 0x44, 0x1d, 0xff, 0x01, 0xab, 0x84, 0x80, 0xe1, 0x89, 0xf1, 0xbe, - 0xd0, 0xba, 0x0e, 0x91, 0x25, 0x4e, 0x08, 0x19, 0x51, 0x2e, 0x6d, 0xbd, - 0x20, 0x49, 0xf8, 0xcf, 0x96, 0xe9, 0x62, 0x2b, 0xea, 0xe4, 0xa7, 0x59, - 0x27, 0x33, 0x73, 0x2c, 0x6c, 0x48, 0x43, 0xc0, 0xdd, 0xb8, 0x02, 0x54, - 0x31, 0x35, 0x1d, 0xf2, 0x74, 0x59, 0x27, 0x33, 0x1f, 0x0b, 0x9f, 0x6f, - 0x92, 0x67, 0x4f, 0x17, 0xbe, 0xbe, 0xe3, 0x57, 0xfc, 0xef, 0x18, 0x53, - 0xff, 0x7b, 0x0b, 0x94, 0x76, 0x3c, 0xee, 0xcf, 0x91, 0xde, 0x97, 0xdb, - 0xeb, 0x3d, 0xa5, 0x9c, 0x99, 0x1c, 0x63, 0x05, 0x80, 0x21, 0x8f, 0x7b, - 0x80, 0xff, 0xf1, 0x50, 0x80, 0xa5, 0xdf, 0xfc, 0x21, 0x4c, 0xda, 0xff, - 0xfd, 0x63, 0xd5, 0xc4, 0x35, 0xf1, 0x56, 0xfd, 0xbf, 0x40, 0x00, 0x0f, - 0x88, 0xfa, 0x8f, 0xf5, 0x07, 0xde, 0x67, 0x5d, 0x01, 0x2c, 0x36, 0x30, - 0x90, 0xa1, 0x13, 0xde, 0x42, 0x27, 0x16, 0xb1, 0x0e, 0x8f, 0x97, 0x23, - 0x64, 0x44, 0x4f, 0x64, 0x92, 0x37, 0x44, 0x25, 0xcf, 0x26, 0x0c, 0x75, - 0x41, 0x18, 0x9f, 0x08, 0x7c, 0xb3, 0x02, 0x89, 0x86, 0x42, 0x59, 0xc8, - 0xb4, 0xd6, 0x2c, 0x02, 0x14, 0xd2, 0x4c, 0x67, 0x22, 0x81, 0x91, 0x2c, - 0x89, 0x4c, 0x84, 0x60, 0x08, 0x93, 0x62, 0x49, 0xa2, 0xb7, 0xcd, 0x0a, - 0xa8, 0xd1, 0x72, 0x51, 0x01, 0xb5, 0xcf, 0x85, 0x7a, 0x07, 0xec, 0x90, - 0xa6, 0x18, 0x1c, 0xb0, 0x3b, 0xeb, 0x1e, 0x40, 0xd5, 0x3c, 0x69, 0x63, - 0xae, 0x46, 0x4d, 0x54, 0x4e, 0x61, 0x4e, 0xfe, 0xa7, 0x9e, 0xd6, 0x7b, - 0x27, 0x2a, 0x11, 0x5a, 0xcb, 0x0f, 0x3c, 0x47, 0x8a, 0xa2, 0x6b, 0x56, - 0xaf, 0xe0, 0x72, 0xaa, 0x5b, 0xda, 0xc1, 0x83, 0x16, 0xef, 0x5e, 0xc8, - 0xae, 0xb6, 0xb4, 0xc0, 0xd4, 0x9e, 0x52, 0x55, 0xa7, 0xa3, 0x52, 0x2d, - 0x14, 0xd0, 0x49, 0x03, 0xf0, 0xa0, 0x86, 0x4e, 0x53, 0xfe, 0xd1, 0xe2, - 0x9f, 0xf1, 0x74, 0x06, 0x1e, 0x11, 0x4e, 0xac, 0xb7, 0x78, 0x37, 0xd7, - 0xdc, 0xd2, 0x88, 0xba, 0x85, 0x36, 0x1b, 0x2a, 0x8e, 0x30, 0x62, 0x41, - 0x41, 0x2c, 0xe5, 0xb3, 0x8e, 0x10, 0x5a, 0x4a, 0xad, 0x2c, 0x65, 0x06, - 0x8d, 0x35, 0x5e, 0xa4, 0xf5, 0x62, 0x7e, 0x08, 0x8e, 0x17, 0x99, 0x12, - 0xcf, 0x5a, 0x25, 0xe1, 0x1f, 0x3f, 0xf1, 0xc4, 0x5a, 0xbe, 0x04, 0x23, - 0x7f, 0x42, 0x42, 0xd3, 0x48, 0xf3, 0x1b, 0x64, 0x10, 0x32, 0x7c, 0x4f, - 0x07, 0x75, 0xe1, 0x08, 0x68, 0x38, 0x66, 0x00, 0x29, 0x62, 0xe1, 0x21, - 0x4e, 0x22, 0x38, 0x44, 0x93, 0x40, 0x9d, 0x03, 0x10, 0x93, 0x85, 0x95, - 0x97, 0x3b, 0x63, 0x08, 0x24, 0xb6, 0x3b, 0xea, 0x4a, 0x72, 0x7a, 0x32, - 0x02, 0x65, 0x93, 0xf6, 0x51, 0x21, 0x0f, 0x70, 0x91, 0x14, 0x0a, 0xed, - 0x1e, 0xeb, 0x4a, 0x60, 0x50, 0x39, 0x8f, 0xb7, 0x2f, 0x2f, 0xce, 0x5b, - 0xa2, 0x77, 0xd3, 0xfa, 0xbe, 0x8b, 0x65, 0x4e, 0x0b, 0xb0, 0x5d, 0x0b, - 0x76, 0x48, 0xf8, 0x19, 0x64, 0x5f, 0xd6, 0xfc, 0x6b, 0x70, 0x92, 0xe3, - 0x6e, 0x97, 0xc6, 0x64, 0x56, 0x47, 0x5f, 0x14, 0x7d, 0xe8, 0x94, 0x28, - 0x1c, 0xfd, 0x3b, 0x3a, 0x56, 0x0d, 0x4a, 0xee, 0x38, 0xa9, 0x56, 0x5d, - 0xb8, 0x9b, 0x18, 0x0c, 0x31, 0x9c, 0xcc, 0x0e, 0x12, 0x3c, 0xa0, 0x9a, - 0x72, 0x95, 0x7c, 0x6b, 0x42, 0x61, 0x24, 0xe8, 0x4a, 0x90, 0x7a, 0xe8, - 0xa8, 0x5e, 0x77, 0x8e, 0x46, 0xf0, 0x60, 0xde, 0x9f, 0x2e, 0x00, 0x8c, - 0x98, 0x04, 0x80, 0x2a, 0x21, 0xd8, 0xfc, 0xd1, 0x12, 0x2f, 0xa1, 0x28, - 0xab, 0x27, 0x1e, 0xc4, 0x90, 0x8a, 0xb6, 0x2e, 0x04, 0xa2, 0x66, 0x3f, - 0x4d, 0x93, 0x69, 0x3c, 0x66, 0xba, 0x84, 0x49, 0x81, 0x20, 0x70, 0xe4, - 0x20, 0x6c, 0xaf, 0x89, 0xc2, 0x7a, 0x70, 0x88, 0xa0, 0xe4, 0xc4, 0x90, - 0x9c, 0xae, 0x84, 0x95, 0x9b, 0x77, 0xa2, 0xc6, 0x65, 0x06, 0x42, 0x44, - 0x09, 0x34, 0xc3, 0x20, 0x15, 0x56, 0x0a, 0xec, 0xb2, 0x03, 0x21, 0x32, - 0xab, 0xcf, 0x3d, 0xef, 0xf5, 0x7c, 0x0e, 0xa5, 0x05, 0x48, 0x3f, 0x40, - 0xce, 0xcf, 0xde, 0xba, 0x4a, 0xca, 0xeb, 0x7f, 0x26, 0xf1, 0x67, 0xd6, - 0x41, 0x21, 0x19, 0xfd, 0xec, 0x96, 0x0e, 0xe9, 0x0c, 0xef, 0x18, 0x24, - 0x1b, 0xc4, 0xc3, 0x44, 0x8d, 0xad, 0xc1, 0x01, 0x44, 0x25, 0xc6, 0x74, - 0xda, 0x38, 0x95, 0x71, 0xcf, 0xf4, 0x25, 0x67, 0x5a, 0x38, 0xfa, 0x26, - 0x19, 0x15, 0x4a, 0xc7, 0xf0, 0xa6, 0x56, 0x65, 0xba, 0x88, 0xfa, 0x26, - 0x65, 0xb2, 0x44, 0xf2, 0x3e, 0xab, 0x94, 0x28, 0x33, 0x71, 0x46, 0xff, - 0xb1, 0xd1, 0x93, 0x29, 0xd9, 0xc5, 0xc7, 0xe3, 0xee, 0x19, 0x75, 0x14, - 0x14, 0x6c, 0x81, 0x0e, 0xe5, 0xb7, 0x60, 0x5d, 0xa3, 0x24, 0x21, 0x10, - 0x18, 0x48, 0x96, 0x25, 0x8d, 0x12, 0x5c, 0x89, 0x50, 0x32, 0x8a, 0x81, - 0x20, 0xd2, 0x7d, 0x37, 0x69, 0x17, 0xe8, 0xc8, 0x08, 0x04, 0xe2, 0x84, - 0x82, 0xd7, 0xf5, 0x59, 0x7d, 0x38, 0x24, 0x1c, 0x9d, 0x1e, 0x53, 0x81, - 0x93, 0xaa, 0x91, 0x28, 0xc8, 0xad, 0x75, 0x89, 0xc9, 0xc0, 0x1e, 0xc4, - 0xa9, 0x45, 0x41, 0x08, 0x9c, 0x85, 0x5d, 0xef, 0x95, 0x64, 0x5d, 0x4c, - 0xbb, 0x41, 0x62, 0xbc, 0x81, 0xa0, 0x63, 0xe9, 0x04, 0xc6, 0xe9, 0x9c, - 0x53, 0xe2, 0x89, 0x82, 0x29, 0x28, 0x8f, 0x97, 0x6a, 0x90, 0x50, 0x49, - 0x8d, 0xa4, 0xe6, 0xe0, 0xa5, 0x50, 0xde, 0x39, 0x36, 0x17, 0x1f, 0x5d, - 0x41, 0xad, 0x19, 0x5c, 0xc0, 0xca, 0xc3, 0x20, 0x74, 0xd6, 0xed, 0x20, - 0xd2, 0x5b, 0x38, 0x19, 0x21, 0xaf, 0x8a, 0x4c, 0xef, 0xdb, 0xf4, 0x00, - 0x00, 0xd7, 0xfa, 0x73, 0xa8, 0xff, 0x52, 0x53, 0xef, 0x31, 0x7c, 0x20, - 0x39, 0xe9, 0xd8, 0x93, 0x44, 0xe4, 0x43, 0x24, 0x63, 0x60, 0xcb, 0x22, - 0x69, 0x59, 0xd4, 0x9f, 0x05, 0xf9, 0xc2, 0x11, 0xf0, 0x19, 0x38, 0x84, - 0x6b, 0xe5, 0x89, 0xa0, 0xa4, 0xaa, 0x45, 0xb4, 0x54, 0x4a, 0x22, 0x27, - 0xd9, 0xb2, 0xe8, 0x72, 0x7b, 0x88, 0x90, 0xb5, 0xdc, 0xae, 0x50, 0x26, - 0xab, 0x7e, 0xcf, 0x9d, 0xc9, 0x2d, 0x8b, 0x21, 0x48, 0xf5, 0x79, 0x36, - 0x4c, 0xda, 0x40, 0x2c, 0xa0, 0xc7, 0x2f, 0x13, 0x83, 0x5c, 0x7b, 0xd2, - 0xa2, 0x17, 0x48, 0x7d, 0x23, 0x3d, 0xdd, 0xb0, 0x18, 0x8a, 0xb5, 0x41, - 0x99, 0x76, 0xb6, 0xa6, 0x62, 0x77, 0xe9, 0xd6, 0x5b, 0xbb, 0x28, 0xdb, - 0xc1, 0x77, 0x2c, 0xe1, 0x8a, 0xb7, 0x05, 0x7c, 0xf9, 0x89, 0x63, 0xa1, - 0x93, 0xd0, 0xb1, 0x74, 0xfb, 0xb9, 0xb3, 0xcf, 0x47, 0x46, 0xd6, 0x58, - 0xe3, 0x08, 0x60, 0x65, 0x4f, 0x11, 0x3a, 0x9f, 0x93, 0x94, 0xdb, 0x69, - 0xdf, 0x24, 0xf2, 0xca, 0x95, 0xf8, 0xe3, 0x1d, 0xf3, 0xc9, 0x2e, 0xd7, - 0x8a, 0x36, 0xef, 0xde, 0xb0, 0x59, 0x76, 0xe6, 0xc5, 0x32, 0x4d, 0x7d, - 0x66, 0xe7, 0x56, 0x4e, 0x37, 0x56, 0x49, 0x2a, 0x69, 0x56, 0x21, 0x5a, - 0x02, 0xe1, 0x08, 0xd2, 0xc9, 0xd3, 0xa6, 0x4c, 0xd2, 0x49, 0x3a, 0x15, - 0x8a, 0x52, 0x16, 0xaf, 0x91, 0x99, 0x8f, 0x20, 0xeb, 0x24, 0xdd, 0x80, - 0xaf, 0x88, 0x40, 0xc3, 0x76, 0x86, 0x52, 0x24, 0x51, 0x90, 0x86, 0x8b, - 0x40, 0x75, 0x22, 0x28, 0x28, 0x75, 0xb8, 0xf1, 0xfa, 0x70, 0x42, 0x44, - 0x1c, 0x59, 0x3d, 0x79, 0xe3, 0x04, 0x72, 0xa9, 0x32, 0xaf, 0x20, 0x81, - 0xd6, 0xfa, 0xfb, 0xe4, 0x1a, 0x8b, 0x56, 0xff, 0xd3, 0x39, 0x77, 0xf6, - 0x2f, 0x9f, 0x20, 0x41, 0x62, 0x07, 0xf7, 0xf8, 0x10, 0x14, 0x49, 0x08, - 0x24, 0x80, 0x37, 0x07, 0x48, 0x74, 0x27, 0xc6, 0x79, 0x23, 0x7a, 0xb8, - 0x2c, 0x9f, 0x08, 0x91, 0xa2, 0x12, 0x92, 0xa2, 0x23, 0x7d, 0x8c, 0xdb, - 0x71, 0xf6, 0xed, 0x8a, 0x85, 0x04, 0x94, 0x82, 0x73, 0x66, 0xcf, 0xd8, - 0x3a, 0xe4, 0x16, 0xf5, 0xb9, 0x39, 0x7e, 0x31, 0xe5, 0x92, 0xbc, 0x12, - 0x05, 0x09, 0x33, 0x4c, 0xb8, 0xed, 0x30, 0xe4, 0xe3, 0x10, 0x6a, 0x48, - 0xa4, 0xe4, 0x61, 0xc0, 0xd8, 0x53, 0xe8, 0xad, 0xa2, 0x04, 0x0d, 0x9a, - 0x0e, 0xed, 0xac, 0x15, 0xa6, 0xb0, 0x03, 0x93, 0x08, 0xa5, 0x84, 0xfc, - 0xbf, 0xa2, 0x7b, 0xc2, 0x6b, 0x41, 0x15, 0xc6, 0xbd, 0x72, 0x0a, 0xad, - 0xc1, 0xfe, 0x87, 0xf4, 0xee, 0xf3, 0x92, 0x8a, 0x29, 0xb0, 0x9c, 0x04, - 0x13, 0x0b, 0xf9, 0x4e, 0xa0, 0x0d, 0x9a, 0x79, 0x69, 0x3d, 0x2f, 0x32, - 0x37, 0x4c, 0x48, 0x78, 0xb4, 0xf8, 0x5b, 0xb0, 0x24, 0xd6, 0x6c, 0xdb, - 0x77, 0x8c, 0x8a, 0x48, 0x48, 0x42, 0x26, 0x61, 0xe3, 0xe4, 0xe1, 0x12, - 0xf8, 0xab, 0x20, 0x66, 0xf9, 0x6e, 0x3d, 0xc7, 0x27, 0x8f, 0x9f, 0x79, - 0x1f, 0xc2, 0xca, 0x64, 0xc9, 0xe4, 0xa9, 0x87, 0xde, 0x1a, 0x4a, 0xdd, - 0x19, 0x00, 0x27, 0xcb, 0x77, 0x54, 0x7d, 0x2c, 0x40, 0xce, 0x83, 0x96, - 0xd6, 0x49, 0x70, 0x09, 0x86, 0xe1, 0x1d, 0x2a, 0xc9, 0xc4, 0x95, 0x2f, - 0xdb, 0x9f, 0x94, 0x4c, 0xe6, 0xe8, 0x02, 0x6a, 0x4e, 0x4f, 0x75, 0xd6, - 0x2e, 0x7a, 0xf4, 0x72, 0x06, 0x15, 0x2a, 0x4d, 0xe3, 0xe6, 0x6e, 0xc4, - 0xff, 0xf7, 0x29, 0xe2, 0x94, 0x11, 0x48, 0x0c, 0x24, 0xd4, 0xdc, 0xec, - 0xcf, 0x91, 0x45, 0xbd, 0xea, 0x53, 0x92, 0x82, 0xd2, 0x33, 0x81, 0x2b, - 0x92, 0x65, 0x56, 0xbe, 0xfc, 0x2d, 0x67, 0x03, 0x1d, 0x5d, 0x6d, 0xb7, - 0xe2, 0x90, 0x49, 0x88, 0xb8, 0x94, 0x5b, 0x2c, 0xae, 0xa9, 0xcb, 0x79, - 0xbf, 0x26, 0x06, 0xce, 0x87, 0x6a, 0x42, 0x9d, 0xcf, 0x3e, 0x2a, 0x59, - 0x71, 0x31, 0xa7, 0xba, 0xa7, 0xe2, 0x60, 0x2c, 0xc7, 0xf0, 0xb8, 0xb7, - 0xf0, 0x64, 0xc1, 0x48, 0x91, 0xa8, 0x11, 0x86, 0x32, 0x76, 0x0a, 0x42, - 0x4c, 0x72, 0x6f, 0xad, 0x5c, 0x23, 0x01, 0x37, 0x6f, 0x40, 0xc8, 0x2a, - 0x0f, 0xe4, 0xc9, 0x1f, 0x0c, 0x46, 0xec, 0x82, 0x78, 0x63, 0x10, 0xc1, - 0x55, 0x27, 0x22, 0xfd, 0xa1, 0x77, 0xc7, 0xec, 0xf6, 0x10, 0x6c, 0x1c, - 0x4e, 0x75, 0x85, 0x2f, 0x06, 0xa3, 0x25, 0x02, 0x12, 0x37, 0x72, 0x64, - 0xb2, 0xb0, 0x08, 0x69, 0x2e, 0x93, 0xc6, 0xe5, 0xf2, 0x66, 0x44, 0x8d, - 0xc9, 0xe4, 0xf1, 0x45, 0x21, 0x56, 0x2f, 0xff, 0xf1, 0x50, 0x80, 0xbb, - 0xff, 0xfc, 0x21, 0x4c, 0x6c, 0xf8, 0x17, 0x30, 0x7f, 0xfd, 0x25, 0x44, - 0xdf, 0xb7, 0xed, 0xfb, 0x7e, 0x80, 0x00, 0x0d, 0x6d, 0xc6, 0xbd, 0x00, - 0x1c, 0x72, 0x3f, 0x7f, 0xcf, 0xb6, 0xa5, 0x7c, 0x68, 0x13, 0xdb, 0x50, - 0xc8, 0x77, 0x6e, 0xf2, 0x91, 0x82, 0xf2, 0x6d, 0x87, 0x5a, 0xa7, 0x02, - 0x51, 0x38, 0x73, 0xb2, 0xa9, 0xb0, 0x36, 0x4a, 0xf0, 0xf9, 0xde, 0x55, - 0x4c, 0xcc, 0xaf, 0xe0, 0x71, 0x9d, 0x64, 0x0b, 0x82, 0x55, 0x0e, 0xc7, - 0xf0, 0x69, 0x1f, 0x38, 0x74, 0xc6, 0x00, 0x2c, 0x8f, 0x60, 0x7c, 0x1f, - 0xc2, 0x58, 0x1b, 0xd3, 0x3b, 0x87, 0xc8, 0xb3, 0x4e, 0x08, 0xac, 0x82, - 0x4f, 0xaf, 0x79, 0x3d, 0x91, 0xcd, 0x52, 0xd9, 0x3a, 0x33, 0xec, 0xef, - 0x36, 0x1b, 0xaa, 0x34, 0x34, 0x9f, 0x05, 0xe4, 0x04, 0x6a, 0xe1, 0x08, - 0x61, 0xb9, 0x81, 0x2b, 0x44, 0x23, 0xda, 0x78, 0x6d, 0x11, 0x60, 0x9b, - 0x66, 0x12, 0xcb, 0xa6, 0xdc, 0x61, 0x26, 0xe0, 0xac, 0xf1, 0x11, 0x0c, - 0xf2, 0x72, 0x47, 0x8f, 0x6b, 0xef, 0x52, 0x10, 0x4e, 0x42, 0x9b, 0xf3, - 0xac, 0xf2, 0x02, 0xa1, 0x1b, 0x90, 0x74, 0x3a, 0xd4, 0x64, 0x00, 0x0c, - 0xe8, 0x39, 0x38, 0x84, 0x60, 0xdd, 0xa9, 0x82, 0x42, 0x0d, 0x32, 0x6c, - 0x19, 0x25, 0x47, 0x23, 0x85, 0x8a, 0x42, 0x11, 0xe5, 0xa0, 0x65, 0x74, - 0xd4, 0x72, 0xa0, 0x84, 0x91, 0x70, 0x84, 0xeb, 0xb6, 0xf4, 0x52, 0x18, - 0xb8, 0x24, 0x9f, 0x03, 0xe0, 0xea, 0x68, 0xa4, 0x86, 0x6e, 0x55, 0xeb, - 0xad, 0x7f, 0xad, 0xff, 0x23, 0xa9, 0x88, 0x21, 0xf7, 0x40, 0x26, 0x50, - 0xcb, 0x4a, 0x94, 0xc3, 0x5c, 0x23, 0xec, 0xee, 0xdb, 0x97, 0x32, 0xe0, - 0xf0, 0x31, 0xfa, 0xbc, 0x8a, 0xce, 0x2e, 0x43, 0x05, 0x62, 0x8f, 0x4e, - 0xef, 0x9e, 0x3d, 0xe7, 0x5b, 0x6e, 0x5b, 0x29, 0x00, 0x2e, 0xa4, 0x46, - 0x4f, 0x3d, 0x06, 0x93, 0xfc, 0x32, 0xeb, 0x07, 0xeb, 0xee, 0xa4, 0x5d, - 0xcb, 0x22, 0x60, 0x7d, 0x22, 0x0b, 0x15, 0xf1, 0xf2, 0x45, 0x25, 0xa2, - 0x0c, 0x91, 0x07, 0xe2, 0x8f, 0x83, 0xc2, 0xba, 0x13, 0x14, 0xe2, 0x36, - 0x80, 0xfe, 0x03, 0x2a, 0x0b, 0xf6, 0xb9, 0xcb, 0xf8, 0xd9, 0xd4, 0x9d, - 0x83, 0xc6, 0xc7, 0x95, 0xb4, 0xa9, 0x37, 0x12, 0x87, 0x07, 0xdf, 0x36, - 0x27, 0xfa, 0x08, 0x05, 0xb6, 0x61, 0x3e, 0xef, 0xbf, 0xed, 0x11, 0xd8, - 0x82, 0xfb, 0xdd, 0x31, 0x33, 0x1b, 0x96, 0xbc, 0xcb, 0x32, 0x72, 0xae, - 0xd6, 0xad, 0x43, 0x29, 0x98, 0x99, 0x41, 0x2f, 0x1e, 0x7c, 0x0e, 0x3d, - 0x09, 0x30, 0x3c, 0x98, 0x03, 0x33, 0x30, 0x90, 0x4a, 0x49, 0xc0, 0x22, - 0x06, 0x11, 0x58, 0xf2, 0xed, 0x42, 0x5f, 0xa4, 0x93, 0xf0, 0x3f, 0x6d, - 0x27, 0x33, 0x08, 0x4b, 0x07, 0x38, 0x82, 0x65, 0x90, 0xdf, 0x78, 0xb6, - 0xc5, 0x99, 0x26, 0x14, 0x84, 0x0a, 0x55, 0x20, 0x08, 0xaa, 0x81, 0x3a, - 0x12, 0x09, 0x9b, 0x18, 0x4a, 0x83, 0xe5, 0x96, 0x13, 0xcb, 0xe4, 0x89, - 0xe2, 0x9a, 0x4b, 0x26, 0x4b, 0x16, 0x75, 0xa7, 0x23, 0x2a, 0x84, 0x84, - 0x7a, 0x64, 0x6a, 0xc6, 0xce, 0xb0, 0x68, 0x52, 0x92, 0xbf, 0x48, 0x9a, - 0xe2, 0x92, 0x96, 0x99, 0x95, 0x44, 0xa0, 0x0a, 0xed, 0xa6, 0x4a, 0x44, - 0xe2, 0x54, 0x09, 0x3a, 0x47, 0xc8, 0x0d, 0xed, 0x2a, 0x2b, 0x07, 0x6e, - 0x1a, 0xa4, 0x6f, 0xcf, 0x11, 0x3b, 0x33, 0xab, 0x6e, 0x86, 0x6d, 0xc2, - 0x63, 0x31, 0x11, 0x2f, 0x57, 0x11, 0x5b, 0x6a, 0x51, 0x11, 0x14, 0x62, - 0x2c, 0x55, 0x15, 0x1e, 0xd1, 0x41, 0x11, 0x9f, 0xb2, 0x49, 0x24, 0x53, - 0x3b, 0x48, 0xb6, 0x8e, 0xff, 0xac, 0x9b, 0x28, 0x92, 0x80, 0x3e, 0x3d, - 0x83, 0x52, 0x13, 0xa6, 0xb0, 0x21, 0x3b, 0x24, 0xd1, 0xfc, 0xfd, 0x44, - 0x9b, 0x38, 0xd3, 0xe8, 0xed, 0x05, 0x4a, 0x00, 0xfc, 0x56, 0x04, 0x48, - 0x17, 0x30, 0x59, 0xc2, 0xe3, 0xa2, 0xf6, 0xaf, 0xb2, 0xe6, 0xeb, 0x99, - 0xcf, 0xf7, 0x1f, 0xcb, 0x6c, 0xff, 0xd1, 0x82, 0x49, 0xa1, 0x95, 0xc2, - 0xae, 0x73, 0x87, 0xfb, 0x2f, 0x26, 0x6e, 0x4b, 0x7c, 0xda, 0xe3, 0xca, - 0xf3, 0x8b, 0xab, 0xef, 0xb4, 0x5a, 0x29, 0xcb, 0xf7, 0x60, 0x34, 0xff, - 0xf0, 0x91, 0x8b, 0x67, 0x03, 0x9a, 0xa2, 0x71, 0x3b, 0x0d, 0xa9, 0xaa, - 0x25, 0xe0, 0x23, 0xd8, 0xa1, 0xc7, 0x81, 0x20, 0x10, 0xdd, 0x84, 0xd2, - 0xdd, 0xca, 0x48, 0x02, 0xeb, 0x7b, 0x6a, 0x7a, 0xba, 0x81, 0x6b, 0x82, - 0x34, 0xd5, 0x3b, 0xb3, 0xeb, 0xbe, 0x32, 0xf0, 0xe9, 0xcb, 0x39, 0x27, - 0x4c, 0x2a, 0xea, 0x72, 0x3e, 0x7b, 0xf1, 0x39, 0x25, 0xe0, 0x08, 0x0c, - 0x19, 0x5a, 0x49, 0x3a, 0xf1, 0x31, 0x42, 0x08, 0x98, 0x48, 0xd3, 0xc9, - 0x4e, 0xc1, 0x12, 0xc0, 0xdd, 0x24, 0xb5, 0x12, 0x7c, 0x3b, 0xba, 0x3d, - 0x68, 0xb2, 0x4e, 0x4c, 0xb9, 0x10, 0x9d, 0x69, 0x38, 0x0c, 0xba, 0xc4, - 0x64, 0x6f, 0x61, 0xdc, 0xa4, 0x20, 0x4e, 0x26, 0xdb, 0x84, 0x48, 0x12, - 0x31, 0x9a, 0x47, 0x05, 0x3d, 0x68, 0x89, 0xd1, 0x74, 0x34, 0x83, 0xa1, - 0x13, 0xc1, 0x34, 0x88, 0xa9, 0x10, 0xa6, 0x42, 0x43, 0x41, 0x0c, 0x60, - 0x49, 0x62, 0x42, 0x46, 0x71, 0x49, 0x06, 0x9f, 0x08, 0x32, 0x6f, 0xa3, - 0x80, 0x69, 0xc9, 0x42, 0xa2, 0x4b, 0x33, 0x88, 0x25, 0x03, 0x4a, 0x4a, - 0xd4, 0x32, 0x72, 0xf1, 0xc4, 0x2a, 0x56, 0x27, 0x9b, 0x9b, 0x13, 0x7e, - 0xdf, 0xb7, 0xed, 0xfa, 0x1b, 0x06, 0xb7, 0xa3, 0x6d, 0x36, 0x4a, 0xd0, - 0x01, 0xc7, 0x37, 0x9f, 0x5e, 0xf2, 0xbe, 0x38, 0xf9, 0xff, 0x4a, 0x04, - 0x00, 0x5a, 0x85, 0x44, 0x0f, 0x36, 0x55, 0xa9, 0x2c, 0x97, 0x9a, 0x88, - 0x00, 0x92, 0x63, 0x49, 0xb0, 0x2e, 0x5f, 0x46, 0x20, 0x55, 0x10, 0x49, - 0x08, 0x3e, 0x25, 0x08, 0xb2, 0x6c, 0x5e, 0x40, 0x4f, 0xd8, 0xf0, 0x00, - 0x7f, 0xae, 0xa0, 0x2e, 0x3c, 0x41, 0x30, 0x03, 0x26, 0x06, 0xab, 0xc9, - 0x81, 0xe2, 0x3d, 0x5d, 0xc2, 0x07, 0xf7, 0xe4, 0xc6, 0xab, 0x78, 0x1a, - 0x11, 0xd0, 0x4d, 0x30, 0xb0, 0x63, 0x60, 0x01, 0xa2, 0x11, 0x59, 0x00, - 0x89, 0x85, 0x66, 0x41, 0x99, 0xc7, 0xfc, 0x4b, 0x96, 0xe8, 0x37, 0xae, - 0xe4, 0xa4, 0x18, 0x03, 0xb2, 0x0a, 0x2e, 0xb2, 0x58, 0xc0, 0x95, 0xa9, - 0x4c, 0x24, 0x81, 0x56, 0x5b, 0x9f, 0x28, 0x88, 0x85, 0x83, 0x4b, 0x45, - 0x22, 0x27, 0x4b, 0x4a, 0x24, 0x31, 0x56, 0xe5, 0xc7, 0xa2, 0x20, 0x21, - 0x90, 0x8a, 0x72, 0x73, 0x1b, 0x76, 0xd0, 0x21, 0x00, 0xe4, 0xc5, 0x10, - 0x8a, 0x61, 0x4e, 0x90, 0xb9, 0x84, 0x8d, 0xba, 0xb3, 0x34, 0xea, 0x0e, - 0x41, 0x18, 0xcb, 0x25, 0x81, 0x95, 0x26, 0x38, 0x9a, 0xcd, 0x44, 0xc5, - 0xa9, 0x1e, 0x42, 0x63, 0x31, 0xf0, 0x09, 0xa7, 0x1e, 0x4e, 0xf5, 0x69, - 0x72, 0x69, 0x25, 0x41, 0x22, 0xab, 0x84, 0x5f, 0x06, 0x64, 0x9f, 0x68, - 0xc4, 0x20, 0x60, 0xe1, 0x59, 0x04, 0x1d, 0x21, 0x5a, 0x27, 0x1e, 0x3a, - 0xc6, 0x01, 0x06, 0x1a, 0xdf, 0x65, 0xba, 0x3e, 0xab, 0xd1, 0x75, 0x6e, - 0x9a, 0x20, 0x07, 0x90, 0x50, 0xed, 0x61, 0xff, 0xff, 0x59, 0xb3, 0xe9, - 0x52, 0x74, 0x42, 0x41, 0x75, 0x0e, 0x1f, 0x06, 0xff, 0xcf, 0xcf, 0x67, - 0xd0, 0xe7, 0x12, 0x0a, 0x75, 0x08, 0xbc, 0x87, 0x0c, 0x9a, 0x81, 0xa1, - 0xe5, 0x96, 0xfd, 0x28, 0x91, 0x19, 0x2a, 0x2b, 0x2b, 0x0f, 0x3d, 0xcc, - 0x82, 0xe3, 0x5c, 0x91, 0xfd, 0xd2, 0x2e, 0x07, 0xe4, 0xb0, 0x10, 0x69, - 0x4c, 0x1c, 0x3a, 0x4a, 0x65, 0x0f, 0x65, 0xd4, 0xe0, 0xf7, 0x6b, 0xb4, - 0x52, 0x88, 0xfd, 0x2f, 0x87, 0x75, 0xc6, 0x3d, 0x31, 0x21, 0x8f, 0x9a, - 0xa4, 0x63, 0xb9, 0x62, 0x08, 0x41, 0x43, 0xff, 0xd7, 0xfa, 0xf2, 0xf9, - 0x31, 0x9e, 0xbb, 0x41, 0x31, 0x12, 0xbe, 0xec, 0x09, 0x30, 0x7b, 0xf7, - 0x14, 0xfb, 0x33, 0x81, 0xfe, 0xf1, 0x0f, 0x16, 0xfb, 0xad, 0x09, 0x80, - 0x03, 0xe6, 0x39, 0xb6, 0x0e, 0x40, 0x88, 0xc0, 0x03, 0xee, 0xf5, 0x89, - 0xf3, 0xa8, 0x3c, 0xb7, 0xf5, 0xd2, 0x46, 0xa9, 0xe6, 0x6c, 0x28, 0x84, - 0x61, 0xe4, 0x18, 0x95, 0x94, 0x22, 0x01, 0x19, 0x0a, 0xd8, 0x52, 0x14, - 0x50, 0x45, 0x91, 0x65, 0x29, 0x38, 0x3c, 0xa2, 0x62, 0x0e, 0x4c, 0x8a, - 0x4a, 0x2a, 0xe6, 0x1c, 0xad, 0x7a, 0xb5, 0x9b, 0x6f, 0xbc, 0x9c, 0x48, - 0x35, 0x0d, 0x2b, 0x58, 0x95, 0x14, 0x82, 0x36, 0x11, 0x93, 0x65, 0x10, - 0x86, 0xfa, 0x1e, 0x21, 0x18, 0xd0, 0x2b, 0xa2, 0x12, 0x02, 0xc8, 0xc3, - 0x88, 0x44, 0xca, 0xf8, 0xa2, 0x71, 0xa0, 0x91, 0x3c, 0x12, 0x34, 0x27, - 0x91, 0x88, 0xdc, 0xaa, 0xf2, 0x27, 0x89, 0x29, 0xe1, 0x48, 0x4e, 0x15, - 0xd4, 0x22, 0x44, 0x6c, 0xbb, 0x07, 0xed, 0xb4, 0x5c, 0x39, 0x44, 0x3c, - 0x47, 0x27, 0x8b, 0xe8, 0xee, 0x87, 0xfa, 0x7d, 0x68, 0x92, 0x26, 0x26, - 0xd6, 0xe4, 0x99, 0x84, 0x9b, 0xc1, 0x67, 0x0f, 0x02, 0x55, 0x60, 0x19, - 0x39, 0x55, 0x31, 0xb1, 0xfa, 0xba, 0xca, 0x46, 0xf8, 0x6b, 0xe7, 0xb1, - 0x7a, 0xdb, 0x19, 0x52, 0xa3, 0x92, 0x7f, 0xa2, 0x4d, 0xc2, 0xbb, 0x05, - 0x49, 0xfa, 0x75, 0xa2, 0x9a, 0x14, 0x19, 0x26, 0xb4, 0x0c, 0x47, 0xc9, - 0xb4, 0xab, 0xb7, 0x98, 0x24, 0xe1, 0x13, 0x39, 0x79, 0x2e, 0x7f, 0x26, - 0x2f, 0xdd, 0xb4, 0x20, 0xf6, 0x36, 0x76, 0x80, 0x4a, 0x0a, 0xb0, 0x21, - 0x62, 0x5b, 0xef, 0xdb, 0x33, 0x7e, 0x00, 0x36, 0xd4, 0xf3, 0x33, 0x16, - 0x80, 0x87, 0x6c, 0x7f, 0xc3, 0xaf, 0x71, 0x66, 0x15, 0xe7, 0xd6, 0x70, - 0xf8, 0xbf, 0xb4, 0x27, 0xe1, 0xfd, 0x82, 0x4a, 0xa7, 0x15, 0xf5, 0xc4, - 0x22, 0xba, 0x8d, 0x86, 0x46, 0xf5, 0xb0, 0x88, 0x25, 0xf7, 0x29, 0x18, - 0x20, 0x98, 0xba, 0xa7, 0xa4, 0x88, 0x88, 0x3e, 0x2d, 0x82, 0x8c, 0x88, - 0xc3, 0xe9, 0xbf, 0xba, 0xbb, 0x8c, 0xe1, 0xbc, 0xb7, 0xe6, 0xba, 0xe4, - 0xbe, 0xb6, 0xe8, 0x12, 0x37, 0x72, 0x9e, 0x7c, 0x4e, 0xdc, 0x7b, 0x3e, - 0x2f, 0xe4, 0x88, 0xb8, 0x04, 0x72, 0xb9, 0x42, 0x75, 0x48, 0x46, 0x45, - 0x52, 0x67, 0x6d, 0xa2, 0xa9, 0x94, 0x54, 0x5b, 0xa6, 0x72, 0x93, 0xb4, - 0x5f, 0xb1, 0x11, 0x32, 0xa8, 0x86, 0x11, 0xcc, 0xc1, 0xa2, 0xe2, 0x10, - 0x9c, 0x72, 0x57, 0x98, 0x42, 0xe9, 0xe8, 0x9c, 0x29, 0x39, 0x44, 0xb1, - 0xe5, 0xe0, 0x30, 0x08, 0xb2, 0x69, 0x16, 0x44, 0x22, 0x6a, 0x64, 0x1b, - 0x2c, 0x94, 0x68, 0x35, 0x31, 0xb3, 0x71, 0x2a, 0x54, 0x08, 0xc6, 0xe2, - 0x64, 0x81, 0x8e, 0x27, 0x06, 0x89, 0x19, 0xa9, 0x25, 0x83, 0xcc, 0x93, - 0x36, 0x28, 0x8d, 0x4c, 0xf9, 0x1d, 0x5d, 0x62, 0x33, 0x34, 0x44, 0x76, - 0xd8, 0x92, 0x1a, 0x09, 0xc4, 0xe5, 0x45, 0x21, 0x7c, 0x7c, 0xff, 0xf1, - 0x50, 0x80, 0x3b, 0x1f, 0xfc, 0x21, 0x7a, 0xcf, 0xff, 0xd5, 0xd5, 0x90, - 0x80, 0xa3, 0xb0, 0xd2, 0xd8, 0xa8, 0x96, 0x5c, 0x18, 0x01, 0x78, 0xbe, - 0xf5, 0xcf, 0x1c, 0xf0, 0x9d, 0xf1, 0xbe, 0x34, 0xd7, 0x19, 0x3a, 0xe7, - 0x9f, 0x3d, 0xf9, 0xe3, 0x75, 0xc0, 0x16, 0x08, 0x07, 0x11, 0x4b, 0x39, - 0x6d, 0xb5, 0xe9, 0x3b, 0xcc, 0x77, 0x3d, 0x3c, 0x5d, 0x5a, 0xd4, 0x2c, - 0x7a, 0xd6, 0x22, 0x99, 0x9a, 0x61, 0xee, 0x1c, 0x74, 0x95, 0x2d, 0x21, - 0x6e, 0xb3, 0x16, 0x78, 0x0c, 0x1b, 0x07, 0x3c, 0x89, 0x5b, 0x2c, 0x06, - 0x13, 0x60, 0x00, 0xf4, 0x21, 0xe3, 0x47, 0x37, 0x7d, 0x4b, 0x4f, 0xd9, - 0x9a, 0x4a, 0xa6, 0x98, 0x6b, 0x71, 0x06, 0x89, 0x3e, 0x10, 0x34, 0x52, - 0xb2, 0x56, 0xc2, 0xb5, 0xd0, 0xf8, 0x36, 0xaa, 0x1d, 0x9e, 0x2d, 0x6c, - 0x95, 0xc8, 0x08, 0x0a, 0x50, 0x6d, 0x5e, 0x03, 0x23, 0xc9, 0x53, 0x34, - 0x58, 0x17, 0x91, 0xa7, 0x75, 0xa3, 0x95, 0x53, 0x4d, 0x43, 0x77, 0x0d, - 0xd2, 0x4a, 0x50, 0xcf, 0x5c, 0x83, 0x4c, 0xf5, 0xd7, 0x64, 0x18, 0xd2, - 0xc6, 0xec, 0x0d, 0xe6, 0x9d, 0xf5, 0x6b, 0x98, 0xab, 0x8a, 0x62, 0x8d, - 0xf7, 0x7f, 0xfe, 0xd8, 0xdb, 0xe6, 0xb7, 0x0d, 0x73, 0x06, 0x02, 0xe7, - 0x4e, 0xea, 0x9c, 0x2e, 0x00, 0xd7, 0xfb, 0xb6, 0x12, 0x0b, 0x53, 0x8c, - 0xc8, 0x4e, 0xf2, 0x0a, 0x03, 0x95, 0x78, 0x59, 0x8b, 0x5b, 0x56, 0x33, - 0x77, 0x6a, 0xbe, 0x8f, 0x23, 0x12, 0xe3, 0x8a, 0x57, 0x83, 0x8a, 0xd3, - 0x30, 0x03, 0xd5, 0x59, 0xd3, 0xd7, 0xff, 0x71, 0xc3, 0xb8, 0x71, 0x66, - 0x98, 0x5a, 0xb9, 0x99, 0xa8, 0x93, 0x06, 0x36, 0x12, 0x65, 0x4e, 0xbf, - 0x73, 0x9a, 0xa7, 0xf2, 0x43, 0x29, 0x6b, 0x0a, 0xee, 0x6d, 0x5c, 0xd3, - 0x53, 0xa1, 0x25, 0x1a, 0x31, 0x52, 0x8c, 0xe1, 0x05, 0x19, 0xa1, 0x75, - 0x29, 0xd4, 0x9f, 0x39, 0x2e, 0x0d, 0x10, 0x52, 0xb6, 0x05, 0xd3, 0x4b, - 0x0e, 0x4d, 0x65, 0x74, 0xaa, 0x24, 0x65, 0x61, 0xa9, 0x9a, 0x04, 0x81, - 0x4a, 0x70, 0xc2, 0x56, 0x4c, 0xda, 0x5b, 0x61, 0x92, 0xc8, 0x51, 0xc5, - 0x52, 0x45, 0x52, 0x4e, 0xa4, 0x96, 0x68, 0xec, 0x30, 0x66, 0x5a, 0x1d, - 0x8f, 0xeb, 0x00, 0x5e, 0x2d, 0x80, 0x94, 0xd6, 0x35, 0xa9, 0xcf, 0x98, - 0xaf, 0x3d, 0xde, 0xbb, 0xdf, 0x08, 0xd3, 0x0d, 0xde, 0x38, 0x06, 0x1b, - 0xe4, 0x01, 0xc8, 0x64, 0xc0, 0x75, 0x1a, 0xb8, 0x68, 0xb5, 0xb5, 0x82, - 0x10, 0x97, 0xab, 0x9d, 0x4d, 0xa8, 0x12, 0x0f, 0xd2, 0x11, 0xd3, 0x98, - 0x83, 0x96, 0x36, 0xf7, 0x28, 0x13, 0x06, 0xe8, 0x5a, 0x8e, 0xf0, 0x6a, - 0xa4, 0x78, 0xee, 0xfe, 0x7e, 0xdd, 0x9b, 0x57, 0x54, 0xe8, 0xf6, 0xb0, - 0xc1, 0xfd, 0xe9, 0x1f, 0x82, 0xe6, 0x9e, 0x1c, 0x4e, 0xf2, 0xff, 0xf3, - 0x7d, 0x88, 0x37, 0x95, 0x0d, 0x55, 0x13, 0x40, 0x21, 0x32, 0x21, 0xdf, - 0xd6, 0x90, 0x58, 0x60, 0x2c, 0x6c, 0x86, 0xc0, 0x44, 0x86, 0x0d, 0x89, - 0xb5, 0xaf, 0x5b, 0xb8, 0x3e, 0x2d, 0xda, 0xbd, 0x7f, 0x0c, 0x41, 0x22, - 0x6c, 0x66, 0x36, 0x99, 0xaa, 0x0a, 0x66, 0x79, 0x2f, 0xc6, 0x8c, 0x24, - 0x14, 0x6c, 0x67, 0xee, 0xed, 0x91, 0x80, 0x81, 0x1a, 0xda, 0x82, 0x43, - 0xf2, 0x79, 0x35, 0xec, 0xc2, 0xbd, 0x7e, 0x1c, 0xbb, 0xb5, 0x6b, 0xe3, - 0x5e, 0x1c, 0xff, 0xf1, 0x50, 0x80, 0x3c, 0xdf, 0xfc, 0x21, 0x1a, 0xcf, - 0xd8, 0x97, 0xff, 0xc8, 0x00, 0x9d, 0xb6, 0x42, 0x58, 0x48, 0x56, 0x44, - 0x1c, 0x15, 0x14, 0x4a, 0x4a, 0x80, 0x09, 0x3a, 0x75, 0x2a, 0xcd, 0x6a, - 0x25, 0x41, 0x6f, 0x30, 0x9d, 0x4c, 0x85, 0x00, 0xd9, 0x48, 0xd4, 0x96, - 0xdc, 0xee, 0x69, 0xf9, 0x79, 0x7a, 0x62, 0xc7, 0x2b, 0x51, 0x7f, 0xca, - 0xf8, 0x0f, 0x1c, 0x15, 0x50, 0xe4, 0x62, 0x2c, 0x1a, 0x45, 0x75, 0x4d, - 0x5f, 0x3c, 0xf1, 0xf5, 0xfe, 0xee, 0x7d, 0x77, 0x98, 0x71, 0x3a, 0xd5, - 0x0e, 0x23, 0xca, 0x83, 0x2b, 0x71, 0x79, 0xb4, 0x5f, 0xa2, 0x03, 0x9c, - 0x15, 0x64, 0x7e, 0x24, 0x95, 0x24, 0xa4, 0x45, 0x85, 0x07, 0x31, 0x2e, - 0x26, 0x97, 0x28, 0xd2, 0xe1, 0x0e, 0x38, 0xd6, 0x56, 0x40, 0x09, 0xea, - 0x95, 0xcd, 0x66, 0xcf, 0xe0, 0xb7, 0xb8, 0x6c, 0x4c, 0xfe, 0x32, 0xf0, - 0x67, 0x1d, 0xd3, 0xc7, 0x3e, 0x4c, 0x12, 0x61, 0x69, 0x81, 0x6c, 0x87, - 0x0a, 0x74, 0x55, 0x62, 0x6f, 0x5d, 0xe7, 0x46, 0x18, 0xa0, 0x31, 0x38, - 0x6a, 0xc9, 0x6b, 0xb4, 0x1a, 0x90, 0x33, 0xc2, 0x52, 0x26, 0x59, 0xec, - 0x0c, 0xfb, 0x94, 0x74, 0xd4, 0x9a, 0x38, 0xc1, 0xc0, 0xf7, 0xad, 0x19, - 0x43, 0xe0, 0x94, 0xa5, 0x4c, 0x8c, 0x7e, 0x6a, 0x26, 0xc6, 0x7c, 0xf4, - 0x21, 0x49, 0xcc, 0xea, 0x20, 0xe4, 0x4b, 0x26, 0xe9, 0x3d, 0xd7, 0xf4, - 0xde, 0x58, 0x03, 0xd7, 0x43, 0xf8, 0x1e, 0xbb, 0xb9, 0xdf, 0x44, 0xa3, - 0x7c, 0xf3, 0x4c, 0x9c, 0xb7, 0xb7, 0xa0, 0x7a, 0x9f, 0x0b, 0x6f, 0xa9, - 0xe6, 0xfb, 0x4b, 0xec, 0x98, 0x54, 0x6c, 0xe0, 0x44, 0xbb, 0x58, 0x52, - 0x85, 0x94, 0x2a, 0xe9, 0x54, 0xc3, 0x36, 0x54, 0x78, 0xd7, 0x79, 0xbb, - 0x24, 0xb2, 0x98, 0xd4, 0xcd, 0x05, 0xbd, 0xf3, 0x71, 0x4b, 0x2e, 0x46, - 0x3d, 0x7a, 0x4d, 0x4e, 0xc6, 0xb8, 0xad, 0xdc, 0x88, 0x00, 0xe1, 0x93, - 0xf8, 0xf1, 0x5d, 0x62, 0xf3, 0xaf, 0x6c, 0x38, 0x74, 0xf1, 0xed, 0xe3, - 0x9c, 0x70, 0x23, 0xc4, 0x34, 0x62, 0x9b, 0x4f, 0x98, 0xc6, 0xc9, 0x95, - 0xcd, 0xd1, 0xed, 0x6a, 0xb2, 0x57, 0xda, 0xce, 0xef, 0x56, 0x54, 0x56, - 0xb5, 0x35, 0x1e, 0xa5, 0x7c, 0xc4, 0x89, 0xda, 0x0c, 0xa6, 0xea, 0x3d, - 0xb0, 0xdf, 0xa3, 0xcf, 0xb8, 0xd3, 0xea, 0xbb, 0x89, 0x32, 0xe0, 0x04, - 0x13, 0xb7, 0x06, 0x2a, 0x25, 0x85, 0xeb, 0x00, 0x71, 0xc8, 0x00, 0x09, - 0x3a, 0xef, 0x8f, 0x68, 0xaa, 0xd4, 0xaa, 0x64, 0x19, 0x9a, 0xb9, 0x91, - 0xcd, 0xbc, 0x8b, 0x0a, 0x4d, 0x04, 0x84, 0x3d, 0x38, 0xaf, 0x58, 0x0a, - 0x6d, 0xc1, 0x96, 0x39, 0xc6, 0x40, 0x31, 0xa1, 0x1a, 0x8d, 0x2b, 0x3a, - 0x87, 0x69, 0x1a, 0x96, 0x48, 0xc8, 0xaa, 0xe5, 0xfd, 0x1a, 0xcb, 0xdb, - 0x4a, 0x08, 0xf5, 0x12, 0xd0, 0x26, 0xbe, 0x6f, 0x63, 0xc6, 0xe1, 0x4d, - 0x69, 0x39, 0xd7, 0x56, 0xea, 0x7a, 0x5d, 0x68, 0x14, 0xcf, 0x8b, 0x5c, - 0x45, 0x4e, 0xb6, 0x0b, 0x6e, 0x4a, 0xa9, 0x82, 0xb8, 0xb8, 0x16, 0xa0, - 0x8a, 0xf3, 0xd1, 0xc8, 0x29, 0x09, 0xb7, 0x7a, 0x5e, 0x1b, 0xe4, 0x99, - 0x78, 0xd4, 0xa1, 0x0a, 0x8c, 0x5f, 0x26, 0xec, 0xba, 0xb1, 0xcc, 0x3a, - 0xc4, 0xfb, 0x12, 0x00, 0x22, 0x5c, 0xcc, 0xe9, 0xbd, 0xcb, 0x13, 0xc7, - 0x4e, 0x9f, 0x29, 0x18, 0xd1, 0x84, 0xdc, 0x18, 0xcd, 0x41, 0xed, 0x69, - 0x9a, 0x97, 0xa6, 0xb7, 0x2c, 0xd5, 0xe0, 0x78, 0xff, 0xf1, 0x50, 0x80, - 0x35, 0x3f, 0xfc, 0x21, 0x1a, 0xcb, 0xc7, 0xff, 0xff, 0xd0, 0x00, 0x9f, - 0xb6, 0x41, 0x18, 0xc8, 0x76, 0x14, 0x05, 0x89, 0x07, 0x00, 0x00, 0x00, - 0x89, 0x38, 0x79, 0xea, 0xaa, 0xb9, 0xf6, 0xd4, 0xa9, 0x90, 0x57, 0x60, - 0x21, 0x03, 0x13, 0x9a, 0xbb, 0x45, 0xc3, 0x54, 0x61, 0x5c, 0x5f, 0x15, - 0xcf, 0xae, 0xb8, 0x4b, 0x6b, 0xa9, 0xb7, 0x65, 0x9a, 0x23, 0xa7, 0x50, - 0x9e, 0x2d, 0x3f, 0x21, 0x50, 0x2c, 0x56, 0x5d, 0x9f, 0x94, 0xdf, 0x42, - 0xf9, 0xd3, 0x29, 0xd0, 0x0d, 0xca, 0x5f, 0x4a, 0xbd, 0x98, 0x5d, 0x13, - 0x13, 0x80, 0x0d, 0xdc, 0x10, 0x72, 0x68, 0x13, 0x36, 0x81, 0x89, 0x35, - 0x28, 0x75, 0x6c, 0xa1, 0xd0, 0x08, 0xd3, 0x7d, 0xe9, 0x4e, 0xec, 0x10, - 0xd6, 0x43, 0x0d, 0xa4, 0x83, 0xd6, 0xa7, 0x36, 0x7a, 0xdf, 0x7d, 0x78, - 0xd3, 0xcd, 0xd7, 0xef, 0x16, 0xab, 0x57, 0x27, 0x29, 0xfb, 0xcc, 0x6f, - 0xde, 0xb6, 0x82, 0x60, 0xcd, 0x42, 0x8e, 0x4e, 0xc8, 0xac, 0x89, 0xa1, - 0x14, 0x88, 0xa1, 0x89, 0x16, 0x23, 0x13, 0x63, 0xa0, 0xa2, 0xc4, 0x8c, - 0xc0, 0xaa, 0x80, 0x5c, 0x9d, 0xf9, 0xb1, 0xd1, 0xc7, 0x78, 0x08, 0xc1, - 0x5e, 0x19, 0x97, 0x9f, 0x9e, 0xac, 0x1f, 0xd6, 0x6d, 0xe0, 0x2b, 0x75, - 0xe5, 0xba, 0xb4, 0xe9, 0x5f, 0xd0, 0x35, 0x57, 0x3a, 0xd4, 0xec, 0xb7, - 0x5d, 0xa7, 0x66, 0xd9, 0x2c, 0xa0, 0xb3, 0x73, 0xd5, 0x72, 0x48, 0xcb, - 0xef, 0x9d, 0x5c, 0x48, 0x98, 0x8e, 0x9a, 0x22, 0x40, 0xdb, 0xbb, 0x9c, - 0x4d, 0x58, 0x6f, 0x47, 0xd5, 0x20, 0x73, 0x85, 0x36, 0xec, 0x43, 0x6b, - 0x5e, 0xd5, 0x9e, 0x73, 0x2f, 0xce, 0x66, 0xa5, 0xe6, 0x77, 0x93, 0x8c, - 0x8c, 0x24, 0x76, 0xab, 0x27, 0x05, 0x4e, 0x30, 0xee, 0x21, 0xaf, 0x6c, - 0x70, 0xea, 0xb6, 0x70, 0xd5, 0xb2, 0x08, 0x13, 0x76, 0x4c, 0xf6, 0x2a, - 0x1e, 0xdd, 0xb5, 0x2c, 0x64, 0xd0, 0x6f, 0x69, 0x43, 0x54, 0xca, 0xc4, - 0x0b, 0x0c, 0x89, 0x28, 0x44, 0x0c, 0x5b, 0x46, 0x67, 0xd9, 0x96, 0x39, - 0x55, 0x4c, 0x5f, 0xa0, 0x5c, 0x76, 0xe0, 0xcc, 0x3d, 0xbe, 0x81, 0x52, - 0x24, 0xd3, 0xac, 0xaf, 0xb6, 0x9f, 0x3f, 0x6d, 0x55, 0x89, 0x90, 0x3e, - 0xc0, 0x00, 0x00, 0x08, 0x93, 0x8e, 0x7e, 0xec, 0x32, 0x4b, 0xa6, 0x06, - 0xd5, 0x21, 0x0e, 0xc2, 0x59, 0x04, 0xc7, 0x87, 0x83, 0x10, 0x94, 0x3b, - 0x80, 0x7f, 0xa0, 0x91, 0x6d, 0x57, 0x8e, 0x69, 0x43, 0x94, 0x24, 0x94, - 0xf9, 0x14, 0xd1, 0xa6, 0x36, 0xa3, 0xfa, 0xfc, 0xef, 0x14, 0x6d, 0xca, - 0x59, 0xeb, 0xe1, 0xed, 0x77, 0xc6, 0x63, 0x74, 0xb9, 0xd4, 0x7d, 0xbf, - 0x7e, 0xb3, 0x08, 0xa2, 0xf2, 0x3a, 0x3a, 0x38, 0x2d, 0x85, 0xae, 0x29, - 0xcf, 0xa6, 0x58, 0x44, 0x9d, 0xd4, 0xf3, 0x7d, 0xac, 0x12, 0x21, 0xa0, - 0x85, 0xae, 0x31, 0x62, 0xec, 0xd3, 0x2d, 0x58, 0xbb, 0xf5, 0xe0, 0x92, - 0x58, 0x3e, 0x1b, 0x2f, 0xa6, 0x45, 0x7f, 0x72, 0x2f, 0x8d, 0x83, 0xce, - 0xc7, 0xff, 0xf1, 0x50, 0x80, 0x37, 0x9f, 0xfc, 0x21, 0x1a, 0xcf, 0xfc, - 0x3a, 0xfe, 0xb1, 0x40, 0x9c, 0xb6, 0xc1, 0x68, 0x28, 0x28, 0x0b, 0x09, - 0x0c, 0xc8, 0x82, 0x80, 0x3f, 0xf2, 0xff, 0xc0, 0x12, 0x81, 0x78, 0x79, - 0xdf, 0xc6, 0xef, 0x8f, 0x1d, 0x5c, 0xb2, 0xac, 0x4f, 0x64, 0xe4, 0x3f, - 0x01, 0x9f, 0x93, 0x57, 0xe1, 0xdc, 0x55, 0xa1, 0x73, 0xa8, 0xb5, 0x36, - 0x58, 0x7d, 0xb9, 0xc7, 0x6b, 0x24, 0x24, 0x93, 0x40, 0x1a, 0xdb, 0x5a, - 0x53, 0x31, 0xa8, 0x82, 0x29, 0xb4, 0xeb, 0x1f, 0x45, 0xfd, 0x5b, 0x3c, - 0xef, 0x6e, 0x9c, 0x5e, 0x59, 0x4c, 0x14, 0xb6, 0xc9, 0x17, 0xa9, 0xe9, - 0x34, 0x61, 0x5e, 0xfc, 0x50, 0x2c, 0xce, 0x18, 0x61, 0x79, 0x5c, 0x6b, - 0x75, 0xc1, 0x3a, 0xd3, 0xa4, 0xad, 0x08, 0x6f, 0x59, 0xf6, 0x07, 0x00, - 0xc9, 0x70, 0xcc, 0xf6, 0x01, 0xb4, 0xd6, 0x7e, 0x72, 0x24, 0x32, 0x18, - 0xf0, 0x10, 0x3b, 0xdf, 0x4d, 0x85, 0x9e, 0xfe, 0x78, 0x0d, 0xf8, 0x44, - 0xaf, 0x72, 0xd7, 0x8b, 0x55, 0xb7, 0x8a, 0xd7, 0x6c, 0x5c, 0x77, 0x91, - 0x8c, 0x0e, 0x40, 0xc5, 0x6e, 0xa9, 0xb4, 0x8e, 0xee, 0x54, 0x6a, 0x88, - 0xde, 0x01, 0xc0, 0xcc, 0x41, 0x17, 0xd6, 0x14, 0x43, 0xc2, 0x02, 0x28, - 0x04, 0xe4, 0x5b, 0x73, 0x14, 0x58, 0x16, 0x68, 0x23, 0x33, 0xea, 0xec, - 0x51, 0x13, 0x2e, 0xd1, 0x6a, 0x33, 0x9b, 0xae, 0x6e, 0xc9, 0xeb, 0x5a, - 0x9e, 0xb2, 0x17, 0x30, 0x46, 0xf3, 0xcc, 0x62, 0xa0, 0xa2, 0x92, 0xdf, - 0xd7, 0x95, 0xbe, 0x66, 0xab, 0x84, 0x3a, 0xdb, 0x42, 0x49, 0x9e, 0xc4, - 0x3e, 0xef, 0x95, 0x92, 0x83, 0xef, 0x36, 0x19, 0x7b, 0xae, 0xfb, 0x78, - 0x7a, 0xd9, 0x2b, 0xc9, 0xe7, 0xdc, 0x9a, 0x98, 0xfa, 0xec, 0xf1, 0x9a, - 0x82, 0x99, 0x02, 0xc7, 0xe2, 0x46, 0x20, 0x4f, 0x1a, 0x22, 0xd1, 0x1c, - 0x13, 0x67, 0xcd, 0x8a, 0x68, 0xcf, 0x6b, 0x82, 0x55, 0x0b, 0x2a, 0xdb, - 0x6a, 0xab, 0xe9, 0xa0, 0xcd, 0x7c, 0x79, 0x6b, 0x3a, 0xe0, 0x97, 0x69, - 0x12, 0x94, 0x50, 0xb6, 0x5d, 0x14, 0x62, 0x0d, 0xba, 0xc9, 0x71, 0x63, - 0x2a, 0x36, 0xf4, 0x3b, 0xf3, 0xe7, 0x36, 0xc6, 0x89, 0x80, 0x5c, 0xbd, - 0x60, 0x8e, 0x86, 0x9c, 0xe5, 0xa2, 0x90, 0xa7, 0x22, 0x18, 0xd0, 0x3e, - 0xc0, 0x06, 0xc6, 0x8d, 0xf1, 0xce, 0x82, 0xf2, 0xa4, 0xbf, 0x8e, 0xe5, - 0xe5, 0xe3, 0x8a, 0x54, 0x02, 0xd4, 0x26, 0xd6, 0xb4, 0x62, 0x68, 0x4b, - 0xad, 0x23, 0xcc, 0x29, 0x23, 0x80, 0x6b, 0x95, 0xab, 0xea, 0xcf, 0x40, - 0x06, 0xe9, 0x10, 0x70, 0xb7, 0xb0, 0x51, 0x83, 0xb3, 0x5f, 0xea, 0x7f, - 0x15, 0x78, 0x40, 0x78, 0x9c, 0x06, 0x70, 0x44, 0x54, 0x9f, 0x4d, 0xd5, - 0x27, 0x8e, 0x51, 0x6b, 0xde, 0x3e, 0x4f, 0xed, 0xe5, 0xf6, 0x55, 0x4c, - 0x46, 0x11, 0x1f, 0x2c, 0x3b, 0xff, 0xe6, 0x13, 0x16, 0xb6, 0xf2, 0x3b, - 0xbd, 0xd1, 0x92, 0xbb, 0x5a, 0xde, 0xed, 0xc7, 0xce, 0xe0, 0x0b, 0xea, - 0x69, 0x19, 0x2b, 0x78, 0x36, 0x90, 0x9d, 0xe9, 0xb4, 0x6a, 0x4b, 0xd1, - 0x7a, 0xb1, 0x43, 0x74, 0xd2, 0x5b, 0x09, 0x44, 0x9c, 0x4a, 0x8d, 0x30, - 0x6e, 0xff, 0xf1, 0x50, 0x80, 0x33, 0xff, 0xfc, 0x21, 0x2a, 0xce, 0xff, - 0x71, 0xde, 0x00, 0x00, 0x9f, 0xb6, 0x90, 0xa0, 0x4a, 0xe8, 0x13, 0x16, - 0x0a, 0x54, 0x01, 0x51, 0x7b, 0x27, 0x59, 0xcf, 0x1b, 0xeb, 0xbe, 0x3b, - 0x5f, 0x9f, 0xab, 0xf5, 0xc5, 0xc8, 0x99, 0xa0, 0xd3, 0xe4, 0xd2, 0x77, - 0xa4, 0x69, 0xf8, 0xfb, 0x6a, 0x4e, 0x5a, 0x56, 0x87, 0xac, 0xaf, 0x4f, - 0x1d, 0xce, 0xa1, 0xfd, 0x33, 0x91, 0xc0, 0x81, 0x71, 0x42, 0x14, 0xfe, - 0x02, 0xa1, 0x3c, 0xa0, 0xc3, 0x72, 0x47, 0x25, 0x45, 0xeb, 0x3c, 0xda, - 0xa1, 0x9a, 0x5f, 0x7e, 0xc2, 0xbc, 0xbc, 0xe4, 0xd3, 0xe3, 0x11, 0xdb, - 0xf4, 0x7a, 0xbc, 0xd9, 0x17, 0x7d, 0x38, 0xbb, 0xee, 0xea, 0x31, 0x0b, - 0x96, 0x62, 0xa7, 0xaa, 0x7b, 0x24, 0x1d, 0xf6, 0x8c, 0xc6, 0x7e, 0x1a, - 0x88, 0xd6, 0x6e, 0x41, 0x77, 0xee, 0xaa, 0xe5, 0xda, 0xaa, 0x13, 0x2c, - 0x7b, 0xfa, 0x40, 0x05, 0xf7, 0xec, 0x00, 0x31, 0xbe, 0x88, 0x00, 0x0f, - 0x4e, 0x2d, 0x10, 0xaa, 0x16, 0x9e, 0x14, 0xc6, 0x07, 0x16, 0x13, 0x8c, - 0xa4, 0x86, 0x59, 0x21, 0xe2, 0x44, 0x6b, 0x18, 0x14, 0xb0, 0x0e, 0x18, - 0x43, 0x87, 0x8b, 0xc7, 0x3c, 0x24, 0x60, 0x30, 0x04, 0x27, 0x30, 0x77, - 0xe2, 0xa2, 0xef, 0x90, 0xe7, 0x69, 0xb8, 0xcc, 0x9e, 0x35, 0xe8, 0x17, - 0x2a, 0x83, 0x1b, 0xda, 0x99, 0xf5, 0xdd, 0xb4, 0x3d, 0x3a, 0xd5, 0xa5, - 0x99, 0xef, 0xb6, 0xd8, 0x3f, 0xdb, 0xfb, 0xe1, 0x65, 0x90, 0x8b, 0x3a, - 0x8f, 0x2e, 0xe9, 0x53, 0xd7, 0xe9, 0x6e, 0xe1, 0xa3, 0x96, 0x4f, 0xa4, - 0x25, 0x16, 0xb7, 0x87, 0x57, 0x1e, 0x68, 0x21, 0x26, 0x25, 0x1b, 0x97, - 0xa6, 0x94, 0xf4, 0xf6, 0xd3, 0x4c, 0xba, 0x71, 0x23, 0x5d, 0xbd, 0x32, - 0x49, 0xab, 0xaf, 0x25, 0x4d, 0x16, 0x92, 0x85, 0x08, 0xc2, 0x73, 0x84, - 0x51, 0x32, 0x6c, 0x07, 0x2a, 0x35, 0xb1, 0x83, 0x4d, 0x82, 0x65, 0xc0, - 0x18, 0x67, 0x9e, 0x85, 0x61, 0xa5, 0x84, 0xa1, 0x28, 0x53, 0xb2, 0xb1, - 0x85, 0x4e, 0x2c, 0xfd, 0xb5, 0x04, 0xe1, 0x61, 0xa0, 0xcc, 0x88, 0x26, - 0x17, 0xb4, 0x00, 0x0b, 0xcb, 0xf6, 0xf9, 0xeb, 0x25, 0x6f, 0xae, 0xfd, - 0x9a, 0x9b, 0xbb, 0xef, 0x4a, 0x95, 0x17, 0x80, 0x1c, 0x87, 0xd5, 0xf9, - 0x49, 0xfc, 0xac, 0x78, 0xe3, 0xda, 0x04, 0x62, 0x37, 0x35, 0x7f, 0x98, - 0x8a, 0x05, 0x16, 0x59, 0x43, 0x2a, 0x9f, 0x2d, 0xbf, 0xa0, 0x18, 0xd3, - 0x9a, 0x92, 0x10, 0xa3, 0xc1, 0x05, 0x3d, 0xbd, 0x74, 0xee, 0x02, 0xcb, - 0xce, 0x6c, 0xad, 0x33, 0x3e, 0xe9, 0x55, 0x06, 0x14, 0xad, 0xdf, 0xa2, - 0xd4, 0x26, 0x68, 0x02, 0x5e, 0xd8, 0x1f, 0x84, 0x0d, 0x0a, 0x61, 0x48, - 0x26, 0x37, 0xee, 0x60, 0xc2, 0x5f, 0xa2, 0x7d, 0x61, 0x09, 0x0b, 0x16, - 0x8e, 0xfe, 0xfb, 0x02, 0x9b, 0x86, 0x08, 0x58, 0xd1, 0x9a, 0x9f, 0x0a, - 0xa9, 0x67, 0x43, 0x06, 0x23, 0x49, 0xc8, 0xf8, 0xff, 0xf1, 0x50, 0x80, - 0x40, 0x7f, 0xfc, 0x21, 0x4c, 0xcc, 0xfc, 0xa4, 0x78, 0x4c, 0x04, 0xc1, - 0xcb, 0x5b, 0x68, 0x30, 0x0d, 0xa6, 0xee, 0xcd, 0x26, 0xa6, 0x59, 0xd7, - 0xa0, 0xdb, 0x5b, 0x3a, 0xec, 0xd0, 0xd9, 0xed, 0xce, 0xda, 0xe3, 0xc5, - 0x75, 0xdf, 0xc5, 0xf1, 0xf9, 0xdb, 0x5f, 0xcf, 0x3f, 0x6f, 0xc7, 0x15, - 0xf1, 0x13, 0xbe, 0xa1, 0xfe, 0xfc, 0x0c, 0xa1, 0x5a, 0x93, 0xb6, 0x3e, - 0x07, 0xfe, 0xdc, 0xea, 0xdd, 0xbe, 0xf6, 0x9b, 0x9b, 0x94, 0xb7, 0xe2, - 0xb3, 0x71, 0x5f, 0x41, 0x35, 0xc2, 0xcf, 0x69, 0x5b, 0x2c, 0x1a, 0x88, - 0x5c, 0x32, 0x90, 0x0c, 0xa0, 0x93, 0xf8, 0xca, 0x04, 0x13, 0x88, 0x24, - 0x88, 0x61, 0x4a, 0xc6, 0x6f, 0x7f, 0x9f, 0xfc, 0x02, 0x91, 0xf9, 0xca, - 0xc4, 0x0f, 0x97, 0x1f, 0xc8, 0x0f, 0xf1, 0xf3, 0xcd, 0x68, 0x9f, 0x12, - 0xbf, 0xe6, 0xae, 0x5e, 0xee, 0xab, 0xfe, 0x95, 0xf8, 0x74, 0x53, 0xb1, - 0x37, 0xf6, 0x57, 0xf2, 0xdf, 0xf2, 0x7e, 0x1b, 0x24, 0xe3, 0x6e, 0xc7, - 0x13, 0xef, 0xd7, 0x6e, 0xd7, 0x19, 0xf3, 0xe3, 0xa6, 0x77, 0x9a, 0x44, - 0x1a, 0xd0, 0xf3, 0xa0, 0x6d, 0xf2, 0x7d, 0x04, 0xbd, 0x07, 0xfe, 0xda, - 0x3c, 0x6a, 0xab, 0x79, 0x78, 0x49, 0x12, 0x26, 0x90, 0x1d, 0x96, 0xf5, - 0xec, 0xf2, 0x85, 0x2e, 0xba, 0x82, 0x2c, 0x58, 0xbd, 0x9e, 0x04, 0x09, - 0x00, 0x0d, 0xf0, 0x2b, 0x43, 0x99, 0xd6, 0x90, 0x2b, 0x42, 0x54, 0x20, - 0xcc, 0x12, 0x70, 0x1f, 0x96, 0xc5, 0x5d, 0x2a, 0x60, 0x41, 0x4a, 0xc9, - 0xc2, 0xaa, 0xfb, 0x9c, 0x59, 0x11, 0x0d, 0x99, 0x96, 0xce, 0x54, 0xec, - 0x90, 0xb1, 0xee, 0x54, 0xaf, 0x5a, 0xc0, 0x00, 0x46, 0xde, 0xfd, 0xd3, - 0x64, 0x60, 0xdf, 0xf7, 0x05, 0x6c, 0x5d, 0xc2, 0x9a, 0xa1, 0x00, 0xc4, - 0x3d, 0xc3, 0x8c, 0x4c, 0x14, 0x66, 0x85, 0xbe, 0xd5, 0x79, 0xe7, 0xf2, - 0xab, 0x89, 0x88, 0x3d, 0x89, 0xef, 0x40, 0x62, 0xe5, 0xc8, 0x6d, 0x92, - 0xdb, 0x1d, 0x91, 0x31, 0x97, 0x55, 0x14, 0xcf, 0x45, 0x9a, 0xe1, 0xb0, - 0x42, 0xe6, 0x58, 0x3d, 0xba, 0xb1, 0xdc, 0x6a, 0x4f, 0x59, 0x88, 0x0a, - 0x25, 0xa9, 0xb2, 0x47, 0xf2, 0xe3, 0x4f, 0x96, 0xda, 0xf9, 0x63, 0xea, - 0xb9, 0xf3, 0x4c, 0x01, 0xe1, 0x77, 0x8f, 0xbf, 0xd8, 0x79, 0xdc, 0xd0, - 0x71, 0xcd, 0xf1, 0xdf, 0x12, 0xfe, 0xc3, 0xf9, 0xf7, 0xd7, 0xc6, 0xfc, - 0xd4, 0x41, 0xdf, 0x7f, 0x40, 0x0e, 0x1f, 0xf9, 0xe8, 0xc9, 0xc7, 0xd6, - 0x12, 0x9f, 0xeb, 0x3b, 0x3d, 0x2c, 0xe9, 0x8a, 0x80, 0x4a, 0xea, 0xca, - 0xd6, 0x1f, 0x44, 0x2b, 0x1a, 0x15, 0x59, 0x0a, 0x5d, 0xc5, 0xeb, 0x28, - 0x37, 0x3c, 0xea, 0xb1, 0x54, 0xb5, 0xae, 0xec, 0x57, 0x6d, 0x6e, 0x7b, - 0xf7, 0x3f, 0x83, 0x82, 0xbc, 0x46, 0xed, 0x3f, 0xb2, 0x51, 0xd2, 0xed, - 0xaf, 0x10, 0xb9, 0xa6, 0x73, 0xb8, 0xb2, 0xde, 0x37, 0xdf, 0xa5, 0xaf, - 0x75, 0x3f, 0xa2, 0xb4, 0x66, 0x4d, 0x4b, 0x85, 0x4d, 0x63, 0x59, 0x09, - 0x03, 0xd2, 0x17, 0xbe, 0xa7, 0x39, 0x6f, 0xe1, 0xf0, 0x91, 0xf8, 0xa5, - 0xe6, 0x0d, 0x48, 0x08, 0x9a, 0x85, 0xa2, 0xaf, 0xb8, 0x28, 0x42, 0x42, - 0x01, 0x9e, 0xef, 0xb5, 0x45, 0x2f, 0x80, 0xce, 0x89, 0xb3, 0x1a, 0x3c, - 0x2e, 0xb7, 0x0d, 0x58, 0xe5, 0x83, 0x27, 0x64, 0x5f, 0x12, 0x48, 0x23, - 0xec, 0x19, 0x6b, 0x09, 0x61, 0x03, 0x62, 0xd3, 0x19, 0x02, 0x70, 0xea, - 0xe1, 0xa4, 0x85, 0x42, 0x5c, 0x6e, 0x17, 0x3d, 0x53, 0xac, 0xed, 0x1b, - 0x6e, 0xad, 0x7a, 0xd7, 0xdf, 0xeb, 0x76, 0xed, 0x93, 0x1a, 0xa7, 0x11, - 0xe5, 0x21, 0xd2, 0x55, 0xe1, 0xb6, 0x9e, 0xff, 0xf1, 0x50, 0x80, 0x32, - 0x9f, 0xfc, 0x21, 0x7a, 0xcf, 0x9d, 0x49, 0x3b, 0x40, 0x20, 0xa0, 0xb6, - 0xb2, 0x15, 0x2c, 0xd0, 0xe6, 0x68, 0xf6, 0xf5, 0xcf, 0x1f, 0x5e, 0xb3, - 0x9f, 0xd7, 0xdf, 0xf5, 0xf5, 0xbe, 0xbb, 0xfd, 0x7f, 0x8f, 0xd7, 0xfa, - 0xfe, 0xfe, 0x3e, 0xfd, 0xf5, 0x39, 0xb4, 0xad, 0x2a, 0xc6, 0x9f, 0x28, - 0x8e, 0x93, 0xd5, 0xd9, 0x75, 0x64, 0xed, 0x7a, 0xf2, 0xea, 0x14, 0x63, - 0x13, 0x7f, 0xdb, 0xf2, 0xea, 0x62, 0xa5, 0x61, 0x28, 0xf8, 0x83, 0x80, - 0x04, 0x6e, 0x5b, 0x67, 0x37, 0x2e, 0xe6, 0xf5, 0xe6, 0x06, 0x78, 0xeb, - 0x08, 0x84, 0xcc, 0x87, 0x3c, 0x16, 0xba, 0xed, 0xd4, 0x80, 0x0b, 0x37, - 0xa7, 0xb0, 0xe9, 0x92, 0x66, 0x29, 0xc9, 0xe9, 0xd5, 0x61, 0x6b, 0x62, - 0xb6, 0x66, 0x3d, 0xb8, 0xdd, 0x28, 0xc1, 0xdd, 0xef, 0xe7, 0xd7, 0x8f, - 0x4f, 0xf1, 0x8e, 0xee, 0xd0, 0x08, 0x71, 0x8f, 0x9d, 0x6b, 0xa9, 0x9d, - 0xee, 0xf5, 0xae, 0xf6, 0xf8, 0x00, 0x0c, 0xeb, 0xbf, 0x16, 0x99, 0xac, - 0xb3, 0x7a, 0x6f, 0x7f, 0x15, 0x14, 0xa5, 0x50, 0x3c, 0xc1, 0xd8, 0x2d, - 0xc3, 0x39, 0xc6, 0xbd, 0x5a, 0xef, 0x13, 0xba, 0x1a, 0xaa, 0x94, 0xdc, - 0x8e, 0xe6, 0x16, 0xb2, 0xd5, 0xc1, 0x2d, 0x85, 0xd7, 0x86, 0xba, 0x32, - 0xee, 0x6a, 0x1c, 0xab, 0xae, 0xff, 0x6c, 0x61, 0x7f, 0x39, 0x6c, 0xad, - 0x6b, 0x96, 0xc9, 0xe2, 0x81, 0x58, 0x01, 0x0d, 0x17, 0xbf, 0x39, 0x8d, - 0xba, 0xe0, 0xe4, 0xa6, 0xd8, 0x20, 0xba, 0x8c, 0x6a, 0xc5, 0x92, 0xc5, - 0x9a, 0xf1, 0x81, 0x99, 0xa4, 0xfa, 0xb1, 0x82, 0x57, 0x75, 0xa1, 0x34, - 0x78, 0x56, 0x32, 0x63, 0x98, 0xd8, 0x14, 0xd4, 0x17, 0x65, 0x39, 0xcb, - 0x79, 0x4c, 0xe5, 0x4d, 0x90, 0x1b, 0xf6, 0x27, 0xa5, 0x8d, 0xeb, 0x5e, - 0x91, 0x1c, 0x67, 0xe7, 0x3c, 0xfe, 0x5b, 0x92, 0x6a, 0xcf, 0xcb, 0x65, - 0xe2, 0xd7, 0xe5, 0x86, 0x3b, 0x6f, 0xba, 0xeb, 0x26, 0xe7, 0x7a, 0xd4, - 0xb5, 0xe5, 0x74, 0xf8, 0xd8, 0x98, 0x17, 0x0a, 0xea, 0xf5, 0x4b, 0xa8, - 0x57, 0x31, 0xb0, 0x78, 0xd0, 0x53, 0x58, 0x6a, 0xdf, 0x70, 0x00, 0x38, - 0xe3, 0xdd, 0x79, 0xd7, 0x77, 0xd7, 0x73, 0x7f, 0xaf, 0xae, 0xbb, 0xd6, - 0xee, 0xaa, 0xf1, 0x2f, 0x9d, 0x68, 0x21, 0x84, 0x7b, 0x32, 0x78, 0x67, - 0x01, 0x77, 0x3d, 0x13, 0xf1, 0xd3, 0x64, 0x7a, 0xf1, 0x41, 0x12, 0x4c, - 0x51, 0x02, 0x78, 0x40, 0x26, 0x8f, 0x77, 0xce, 0x73, 0x82, 0x9d, 0xd2, - 0x2f, 0x86, 0xa7, 0x2d, 0x55, 0xbf, 0xfb, 0xe1, 0x49, 0x5c, 0xf1, 0x2b, - 0x17, 0xf0, 0x8e, 0x53, 0xbb, 0x1e, 0x80, 0xd7, 0x68, 0x97, 0x56, 0x14, - 0x9a, 0xa6, 0xc2, 0xe8, 0x91, 0x35, 0xca, 0x40, 0x01, 0x1c, 0x33, 0x11, - 0x9c, 0xce, 0x6f, 0xb3, 0x1f, 0x38, 0xf9, 0x49, 0x04, 0x83, 0x2f, 0x90, - 0xb2, 0x30, 0x47, 0xff, 0xf1, 0x50, 0x80, 0x32, 0x7f, 0xfc, 0x21, 0x1a, - 0xcd, 0xf6, 0x6b, 0x1e, 0x41, 0x10, 0xa4, 0x84, 0x32, 0x96, 0x08, 0x56, - 0x23, 0x9e, 0x0b, 0x7b, 0xfd, 0x7c, 0x67, 0xe6, 0xfe, 0x3d, 0xbf, 0x6d, - 0xff, 0x9f, 0xeb, 0xf7, 0x9f, 0x3f, 0x5c, 0xf9, 0xf7, 0xfb, 0xfe, 0xdf, - 0xbf, 0xcf, 0x9e, 0x75, 0x26, 0xf3, 0x53, 0x59, 0x61, 0x27, 0x4f, 0x46, - 0x5d, 0x66, 0x00, 0x50, 0x16, 0xfa, 0x24, 0xba, 0xc2, 0x0e, 0xdb, 0x40, - 0x75, 0x5c, 0x6b, 0x9b, 0x00, 0x72, 0xda, 0x5e, 0xcf, 0xcc, 0x14, 0xb0, - 0x1c, 0xf9, 0x75, 0x47, 0x31, 0xb5, 0xb7, 0xd2, 0x01, 0x2a, 0x0b, 0x53, - 0x97, 0x62, 0xcd, 0x74, 0x78, 0xec, 0xab, 0xe7, 0x9c, 0x5f, 0xc6, 0x23, - 0x80, 0x11, 0x2c, 0x56, 0xfb, 0xf3, 0xda, 0xb9, 0x24, 0x8b, 0xe5, 0xcb, - 0x59, 0xe9, 0x99, 0xc8, 0x01, 0xc6, 0x3a, 0xb7, 0xb9, 0xe6, 0x76, 0x6f, - 0xab, 0xaf, 0x93, 0xba, 0x00, 0x07, 0x64, 0x74, 0xc3, 0x85, 0x5d, 0x33, - 0x38, 0xb8, 0xa7, 0x77, 0x20, 0x10, 0x93, 0x35, 0x3b, 0xd3, 0x87, 0x3d, - 0xf5, 0x57, 0x19, 0xcb, 0x7d, 0xd8, 0xde, 0x44, 0xdd, 0xd3, 0x4b, 0xa4, - 0xd7, 0x26, 0xe3, 0x33, 0xcf, 0x55, 0x9c, 0x62, 0xa8, 0xab, 0xec, 0xbc, - 0x3a, 0x79, 0x66, 0x7d, 0x53, 0xb6, 0x8e, 0xf7, 0x78, 0xbf, 0x8d, 0xca, - 0xff, 0x8d, 0x43, 0x95, 0xff, 0x7f, 0x20, 0xa5, 0xdd, 0xec, 0x43, 0xc8, - 0xd1, 0x63, 0x5d, 0xce, 0x11, 0x4d, 0x5c, 0xb8, 0x0c, 0x0a, 0xfa, 0xb1, - 0x4f, 0xa1, 0xdf, 0xf3, 0x9d, 0x69, 0x4d, 0x8b, 0xd7, 0xe4, 0x33, 0x23, - 0x89, 0xaa, 0xba, 0x08, 0x08, 0xcf, 0xdc, 0x3a, 0x00, 0xc9, 0xa0, 0x9a, - 0x42, 0x9c, 0xef, 0xb2, 0x70, 0x61, 0x29, 0x2d, 0x84, 0xcb, 0x02, 0xbc, - 0x01, 0xde, 0xca, 0x67, 0xbe, 0x75, 0x92, 0x70, 0x39, 0x89, 0x1d, 0x1e, - 0x1e, 0x91, 0x4f, 0x61, 0x72, 0xac, 0x7c, 0xcc, 0x1f, 0x30, 0x2d, 0x9a, - 0xf8, 0xf9, 0xc7, 0xc4, 0xf4, 0xfe, 0x7f, 0xaf, 0xef, 0xfa, 0x7d, 0xdc, - 0xf9, 0xf7, 0xfe, 0x7d, 0xf8, 0xd7, 0x34, 0xa9, 0x5c, 0x5d, 0x24, 0xa1, - 0x51, 0xfd, 0x3d, 0xd3, 0xa6, 0x58, 0x8e, 0x76, 0x5f, 0x2d, 0x0f, 0x04, - 0xe4, 0x8c, 0xf4, 0x29, 0x7d, 0x75, 0xe8, 0xf7, 0xf0, 0x44, 0xd7, 0x0d, - 0xf5, 0xf5, 0x49, 0xac, 0x97, 0xde, 0xf8, 0x73, 0xc4, 0xa8, 0xed, 0xdf, - 0xd3, 0x9d, 0x5c, 0x2f, 0xb0, 0x70, 0xcf, 0xc3, 0x0d, 0x62, 0x71, 0xcc, - 0x6e, 0x7a, 0x73, 0xa0, 0x49, 0x11, 0x2b, 0xf5, 0xf7, 0x4d, 0x80, 0x04, - 0x7c, 0x5d, 0xd5, 0xcb, 0x1d, 0xbe, 0x7d, 0xdc, 0x3b, 0xa7, 0x87, 0x94, - 0xe8, 0x5d, 0xaa, 0xf1, 0x17, 0xaf, 0x9f, 0xb1, 0x04, 0xd8, 0xae, 0xd9, - 0xcf, 0x96, 0xea, 0xaa, 0xaf, 0x93, 0xa7, 0x8f, 0x2e, 0x85, 0x5b, 0x36, - 0x77, 0x4b, 0xb7, 0x4c, 0xb7, 0xd2, 0x01, 0x9e, 0x1d, 0x7c, 0xff, 0xf1, - 0x50, 0x80, 0x33, 0xff, 0xfc, 0x21, 0x1a, 0xce, 0xec, 0xd9, 0xf2, 0x0a, - 0x2a, 0xa4, 0xb4, 0x42, 0x95, 0x0c, 0x34, 0x63, 0x08, 0x92, 0xaf, 0x2b, - 0x5f, 0xe3, 0xed, 0xea, 0xbf, 0xd3, 0x5f, 0xdb, 0x3f, 0xdf, 0xfb, 0x7b, - 0x4d, 0xf9, 0xf7, 0xfb, 0xf8, 0xf8, 0xf9, 0xe3, 0xbe, 0x2f, 0x6c, 0x92, - 0xf2, 0x3c, 0xd0, 0x4f, 0xdf, 0x69, 0x64, 0xb3, 0xad, 0xf1, 0x5e, 0x4a, - 0x72, 0x02, 0x91, 0x6b, 0x67, 0x3d, 0x81, 0x85, 0xe3, 0xec, 0x1c, 0x9c, - 0xa5, 0xb9, 0xc6, 0xef, 0x00, 0xad, 0x22, 0xa8, 0xda, 0xb1, 0x0a, 0x76, - 0x19, 0x56, 0xc9, 0x3c, 0xe0, 0x50, 0x94, 0x67, 0x4b, 0x89, 0xb7, 0xa3, - 0x4a, 0x47, 0x78, 0x74, 0xfc, 0x22, 0xae, 0x29, 0x8f, 0x6a, 0xf7, 0x73, - 0xed, 0xbd, 0x29, 0x7d, 0x5c, 0xab, 0x86, 0x59, 0x76, 0x33, 0x26, 0x4e, - 0x3f, 0x3f, 0xaf, 0x71, 0xae, 0xfc, 0x73, 0xeb, 0x88, 0x59, 0x62, 0xfd, - 0x93, 0x77, 0x58, 0xab, 0xae, 0xeb, 0x4f, 0x6d, 0xc6, 0x4b, 0xa0, 0x80, - 0xc4, 0x00, 0xdc, 0x4c, 0xd6, 0xa3, 0xd8, 0x6b, 0x28, 0xd0, 0x24, 0x83, - 0x12, 0x69, 0x3d, 0x8a, 0xd7, 0x77, 0xc4, 0xc1, 0x68, 0xf4, 0xbf, 0xc0, - 0x3d, 0x72, 0x5c, 0xd3, 0xff, 0xab, 0x9f, 0xef, 0x91, 0xbf, 0x2b, 0xc6, - 0x0b, 0x3a, 0xdb, 0xc0, 0x1c, 0xe7, 0xd7, 0x20, 0x7f, 0xe8, 0x01, 0xa1, - 0xaf, 0xd9, 0xa6, 0xbd, 0xbd, 0x54, 0xc4, 0x03, 0x20, 0x61, 0x85, 0x50, - 0xdd, 0x1d, 0xcc, 0x1e, 0x02, 0x7e, 0x2f, 0x37, 0x58, 0x3d, 0x53, 0xf6, - 0x77, 0xfe, 0xb1, 0x74, 0x0c, 0x7f, 0x20, 0x34, 0xd8, 0xff, 0xe3, 0x77, - 0x9a, 0x00, 0x2c, 0x18, 0x12, 0xb4, 0xf2, 0xe8, 0x04, 0x3e, 0x7f, 0xf7, - 0x2b, 0xdb, 0x7b, 0xbc, 0x69, 0x7a, 0x42, 0x68, 0x68, 0xf2, 0x5d, 0x12, - 0xf3, 0x5b, 0x50, 0x79, 0x9c, 0xf4, 0x85, 0x00, 0x63, 0xb9, 0x53, 0xa3, - 0x34, 0x5c, 0x25, 0x38, 0x82, 0x06, 0x5d, 0xf1, 0x49, 0x08, 0x59, 0x30, - 0x94, 0x3c, 0x56, 0x17, 0x1c, 0xb5, 0x5e, 0x56, 0xbd, 0xbd, 0x65, 0x7d, - 0xfe, 0xbf, 0xb3, 0xf7, 0xfd, 0xbd, 0xa6, 0xfc, 0xbb, 0x9f, 0xcf, 0x8e, - 0x7d, 0x69, 0xba, 0xf6, 0x9a, 0x51, 0xc0, 0x2e, 0xae, 0x60, 0xd1, 0x96, - 0x78, 0x5d, 0x00, 0x1e, 0x74, 0x6d, 0x55, 0xcd, 0xbd, 0x12, 0xe7, 0xeb, - 0xe4, 0x57, 0xd3, 0x5f, 0xdf, 0xeb, 0xe0, 0xf9, 0x46, 0x27, 0x33, 0xf8, - 0x58, 0xfd, 0xb9, 0x94, 0xdc, 0xeb, 0xef, 0xed, 0x29, 0x1d, 0xdc, 0x64, - 0x1d, 0xdd, 0x43, 0x38, 0xdd, 0x85, 0xdc, 0x56, 0xb5, 0xd3, 0xd2, 0xf9, - 0x67, 0x57, 0x57, 0xbd, 0x01, 0x9e, 0x88, 0x00, 0x06, 0x43, 0xfb, 0x6b, - 0xba, 0x33, 0x57, 0x6f, 0x3e, 0xae, 0x43, 0xd9, 0xc0, 0xd0, 0x16, 0xf5, - 0x35, 0x1a, 0x54, 0xe3, 0xe2, 0x49, 0x76, 0x2d, 0xd1, 0x83, 0xc8, 0x6d, - 0x3e, 0x16, 0x06, 0x08, 0x56, 0x30, 0x95, 0x97, 0xf6, 0xc4, 0xcf, 0xbf, - 0x1b, 0x2c, 0xb3, 0x17, 0x0e, 0xff, 0xf1, 0x50, 0x80, 0x31, 0x7f, 0xfc, - 0x21, 0x1a, 0xcc, 0xbe, 0xa1, 0x7a, 0x20, 0x04, 0xa8, 0xa4, 0x32, 0x91, - 0x0c, 0x24, 0x73, 0x05, 0x5a, 0xbc, 0xd3, 0xbf, 0x6f, 0xac, 0xf9, 0xcf, - 0xf2, 0xfe, 0x39, 0xff, 0x9f, 0xed, 0x7e, 0xde, 0x3e, 0x1d, 0xdf, 0xb7, - 0x86, 0xe5, 0x4a, 0xa5, 0xba, 0xee, 0x5f, 0x01, 0xef, 0xc4, 0xda, 0x85, - 0x5c, 0x70, 0x69, 0x4f, 0x56, 0x72, 0x20, 0xa9, 0xc1, 0x67, 0x38, 0x25, - 0x9c, 0x1f, 0x5d, 0x33, 0x80, 0x5f, 0xbd, 0x7c, 0x2f, 0x1a, 0xd2, 0xbd, - 0x3d, 0x25, 0xd7, 0xea, 0x0a, 0x8d, 0x29, 0xb2, 0xa0, 0xed, 0xd0, 0x5d, - 0x5b, 0xbc, 0x1a, 0x8f, 0x4d, 0x75, 0x36, 0xbe, 0x4b, 0x9e, 0x1d, 0xd1, - 0xde, 0xd3, 0x87, 0xb5, 0xe7, 0x1a, 0xb2, 0x0f, 0x79, 0x38, 0xb3, 0x2f, - 0xb2, 0x0d, 0x4f, 0x16, 0x55, 0x66, 0xfd, 0x92, 0x8b, 0x26, 0x34, 0x76, - 0x2d, 0x1d, 0xd8, 0xc0, 0x5f, 0xd8, 0xa2, 0x62, 0x40, 0x60, 0x76, 0x8d, - 0x1b, 0xb0, 0x7f, 0x04, 0xc6, 0x3e, 0x8d, 0xb1, 0xed, 0x58, 0x47, 0x7b, - 0x07, 0x57, 0x09, 0x29, 0xaa, 0x79, 0xd1, 0xc1, 0xa8, 0xee, 0x51, 0x95, - 0x21, 0xfb, 0x33, 0x39, 0xcd, 0x62, 0xcd, 0xb8, 0x41, 0x0b, 0xb8, 0xeb, - 0xe3, 0xb1, 0x02, 0xcf, 0xb9, 0xcd, 0xa5, 0xdf, 0xc4, 0xea, 0xd6, 0xfa, - 0x93, 0x16, 0x6d, 0x50, 0x34, 0x21, 0x8a, 0x4e, 0x58, 0x67, 0xaa, 0x55, - 0x97, 0x76, 0x1f, 0xd1, 0x36, 0x5b, 0x64, 0x83, 0x40, 0xd1, 0xc7, 0xbb, - 0xfb, 0x35, 0x05, 0xfb, 0xec, 0xa6, 0xe6, 0x50, 0xd2, 0xb7, 0xb6, 0x69, - 0xb2, 0x67, 0xdb, 0x88, 0x33, 0x8c, 0xfa, 0xc4, 0xee, 0x2c, 0xc5, 0x7a, - 0x9d, 0x32, 0x5a, 0x25, 0x56, 0xc2, 0x3b, 0xda, 0xb0, 0xa0, 0x0e, 0xa1, - 0xf4, 0x82, 0xf1, 0xa0, 0x7c, 0x28, 0x3f, 0x0a, 0x07, 0xca, 0x70, 0x47, - 0x7e, 0xdf, 0x5f, 0x97, 0x7f, 0xf3, 0x5f, 0xb4, 0xfc, 0x7e, 0xdf, 0x13, - 0xc7, 0xc3, 0xbb, 0xfa, 0xe7, 0x9a, 0x5d, 0x2b, 0x13, 0x5b, 0x9a, 0x06, - 0xaf, 0xb2, 0xd7, 0xeb, 0xb5, 0xd1, 0x3c, 0x36, 0x92, 0xa7, 0xcf, 0x28, - 0xab, 0xcb, 0xa6, 0x4a, 0xeb, 0xdc, 0x5f, 0x4f, 0x8e, 0x9d, 0xea, 0xd3, - 0x46, 0xfa, 0x32, 0xd8, 0xd7, 0x9d, 0x08, 0x39, 0xf0, 0x6a, 0xe4, 0x0d, - 0x7d, 0x9b, 0x82, 0x9c, 0x21, 0x0a, 0x02, 0xf2, 0x85, 0x63, 0x37, 0xd6, - 0xcc, 0x91, 0x8d, 0x16, 0x5c, 0x6a, 0x28, 0xb2, 0x99, 0x0c, 0x4b, 0xe7, - 0x7d, 0xa7, 0x18, 0xd6, 0x20, 0x1d, 0xe7, 0x5a, 0x3d, 0x57, 0x32, 0xa1, - 0xe2, 0x6f, 0xc0, 0x75, 0x8b, 0x1c, 0xea, 0xbb, 0x80, 0x89, 0xa1, 0x83, - 0x4a, 0x52, 0xe4, 0x5b, 0x70, 0x5c, 0x5b, 0x15, 0x11, 0x20, 0x35, 0xea, - 0xe3, 0x1a, 0x4a, 0xe0, 0x74, 0x7d, 0x60, 0xd2, 0xc3, 0x4b, 0xa2, 0x77, - 0x64, 0x40, 0x63, 0xe0, 0xff, 0xf1, 0x50, 0x80, 0x40, 0xbf, 0xfc, 0x20, - 0xa8, 0x2a, 0xd4, 0x28, 0x6b, 0x35, 0x16, 0xc4, 0x35, 0x75, 0x79, 0xcf, - 0x9d, 0x3d, 0x3f, 0xce, 0xbe, 0x7e, 0x7f, 0xc7, 0xf5, 0xbf, 0xaf, 0x9f, - 0xdf, 0xed, 0xa9, 0xac, 0x52, 0x8a, 0xd1, 0x35, 0xcf, 0x1d, 0x05, 0xb6, - 0x8e, 0x6e, 0xea, 0xdd, 0xf3, 0xef, 0x1a, 0xa4, 0xb4, 0x47, 0x49, 0x2e, - 0xd9, 0x84, 0x26, 0x19, 0xc4, 0xac, 0xb7, 0x1c, 0x73, 0x94, 0xde, 0x2b, - 0xb1, 0x6f, 0xf1, 0x00, 0x16, 0xb1, 0x78, 0x6b, 0x84, 0xf9, 0x5c, 0xba, - 0x47, 0xfa, 0x91, 0x4c, 0xfc, 0x84, 0x00, 0x43, 0xdd, 0x92, 0x20, 0x00, - 0x45, 0xda, 0xe6, 0xa1, 0x80, 0xf7, 0x22, 0x20, 0x05, 0x11, 0xea, 0x3c, - 0xac, 0xd9, 0x84, 0x62, 0xaa, 0x08, 0x08, 0x64, 0xb0, 0x26, 0x47, 0x84, - 0x23, 0x91, 0xaa, 0xb1, 0x9b, 0x31, 0xae, 0xe2, 0xb2, 0x73, 0x8d, 0xc0, - 0x49, 0x4b, 0x99, 0xae, 0xe8, 0x23, 0x84, 0x40, 0x46, 0x04, 0xcb, 0x8f, - 0xfd, 0x7e, 0xb1, 0xc0, 0xc0, 0x09, 0xf9, 0xc6, 0xee, 0x3c, 0xba, 0x02, - 0xd5, 0x81, 0xea, 0x01, 0x6b, 0x3e, 0xe6, 0xfc, 0x59, 0x9f, 0xe8, 0x23, - 0xea, 0x4f, 0x95, 0x8e, 0x11, 0x7f, 0x9b, 0xf4, 0x73, 0x11, 0x5a, 0xa6, - 0xc7, 0x20, 0x59, 0x84, 0xf7, 0xe8, 0x52, 0x43, 0x00, 0xd3, 0xb0, 0xd5, - 0x29, 0x97, 0xf3, 0xb9, 0x95, 0x0b, 0x4b, 0xc9, 0x0c, 0xfd, 0xcd, 0x3e, - 0xac, 0x14, 0x36, 0xd1, 0x3c, 0x83, 0x98, 0x8a, 0x70, 0xd0, 0x72, 0x7d, - 0x4f, 0x50, 0xe9, 0xf7, 0x11, 0xff, 0x4e, 0x33, 0xc2, 0x30, 0x06, 0x55, - 0x68, 0x81, 0xa7, 0x6d, 0x15, 0x5a, 0xba, 0x80, 0x04, 0x1e, 0x24, 0xa6, - 0x55, 0x47, 0x0a, 0x99, 0x44, 0x86, 0x49, 0xb0, 0x8e, 0x7d, 0x48, 0xc0, - 0x51, 0x09, 0x11, 0x11, 0xdd, 0x4f, 0x66, 0x5a, 0xb6, 0x6b, 0xac, 0x40, - 0xb3, 0xd8, 0x75, 0x03, 0x5a, 0x90, 0xca, 0x55, 0xb0, 0xd1, 0x4c, 0x13, - 0x5a, 0xc9, 0x3c, 0x7b, 0x75, 0xe1, 0xdf, 0xf3, 0x7f, 0x97, 0xf9, 0xfe, - 0xfe, 0x7a, 0xf7, 0xff, 0x1d, 0xf8, 0xcf, 0x6a, 0xcf, 0x39, 0x79, 0x5a, - 0xdf, 0x0d, 0xab, 0x5a, 0x09, 0x6f, 0x1a, 0xd2, 0x42, 0xcc, 0x0c, 0xe0, - 0xe6, 0x21, 0xae, 0x8a, 0x64, 0x97, 0x67, 0x9c, 0x25, 0x9c, 0x1e, 0xd0, - 0x34, 0x39, 0x8d, 0xfb, 0x28, 0x4e, 0x2a, 0x36, 0x4b, 0x65, 0x67, 0x50, - 0x5b, 0xdc, 0x60, 0x1b, 0x29, 0xec, 0x3d, 0xed, 0x72, 0xb9, 0xf5, 0xf5, - 0x49, 0x57, 0x39, 0xf1, 0xbd, 0x5b, 0x1d, 0xe0, 0xeb, 0x70, 0xf3, 0xf8, - 0xf6, 0xff, 0x8e, 0xdf, 0xa3, 0xaa, 0x56, 0xa8, 0xe2, 0x56, 0xef, 0x59, - 0xd6, 0x14, 0xa9, 0xa2, 0xe2, 0x11, 0xdf, 0x8e, 0x8d, 0x4e, 0xfa, 0x6b, - 0x15, 0xbd, 0xd4, 0xe3, 0xc3, 0x4a, 0x08, 0xa5, 0x6e, 0x9e, 0xc0, 0x92, - 0xee, 0xcd, 0xac, 0xbd, 0xd1, 0x51, 0x89, 0x4e, 0xae, 0x76, 0x24, 0x39, - 0xd7, 0x10, 0x93, 0x8d, 0x01, 0x09, 0x61, 0x34, 0xcb, 0x9e, 0x0e, 0x2a, - 0x23, 0x04, 0x93, 0xa8, 0xdd, 0xd1, 0xfc, 0x30, 0xf7, 0xa7, 0x7c, 0xf6, - 0xa2, 0x0f, 0x98, 0x3d, 0x22, 0xb8, 0x83, 0x07, 0xb8, 0x87, 0x37, 0xdf, - 0x02, 0xf9, 0x20, 0x02, 0x54, 0x6f, 0x87, 0x99, 0xa0, 0x1a, 0x7e, 0x57, - 0xdd, 0xfd, 0x34, 0xfd, 0x4e, 0x51, 0xce, 0x3e, 0xbf, 0x6f, 0xdf, 0xfe, - 0x61, 0xcf, 0x3f, 0x04, 0x9b, 0xf4, 0xe3, 0x7f, 0xa8, 0xf6, 0xc0, 0x10, - 0x7a, 0x09, 0x45, 0xfb, 0xa4, 0x13, 0x83, 0x33, 0xb2, 0x69, 0xb1, 0x53, - 0xa3, 0x0c, 0xb8, 0x69, 0x73, 0xfe, 0xf9, 0x36, 0x7c, 0xdf, 0x9f, 0x22, - 0x35, 0xb0, 0x43, 0xf7, 0x9c, 0x41, 0xe8, 0xe7, 0x39, 0x4c, 0x52, 0xc8, - 0xf5, 0x00, 0xd1, 0x7c, 0x17, 0xff, 0xf1, 0x50, 0x80, 0x41, 0x5f, 0xfc, - 0x20, 0xa3, 0x4c, 0x37, 0x04, 0x6e, 0xe6, 0xd0, 0x52, 0xb2, 0x4c, 0x29, - 0x0b, 0x18, 0x2d, 0xd4, 0x20, 0x1d, 0xd4, 0x92, 0x9d, 0x75, 0xdd, 0x6b, - 0xfc, 0x7e, 0x7e, 0xd5, 0xf8, 0xdf, 0xb9, 0x3d, 0xb3, 0xe3, 0x7e, 0xab, - 0xdb, 0xdb, 0xfb, 0x1e, 0xdf, 0x5b, 0xbc, 0xea, 0xba, 0x0d, 0x13, 0x7b, - 0xa5, 0x41, 0x36, 0xea, 0x29, 0x9a, 0x53, 0x90, 0xae, 0x9d, 0x86, 0x1a, - 0x6b, 0x4d, 0xbd, 0xa8, 0x35, 0x75, 0x47, 0x30, 0x1f, 0x03, 0xec, 0x05, - 0xbe, 0x22, 0x5a, 0x8a, 0x46, 0x2a, 0xc3, 0xdb, 0xc3, 0xa8, 0xe8, 0xbe, - 0x82, 0xa2, 0xe4, 0x4a, 0x7b, 0xd6, 0xd9, 0xf2, 0xbb, 0x9d, 0xbd, 0x52, - 0xd5, 0x30, 0xc1, 0xfd, 0x33, 0x81, 0x5e, 0x6f, 0xc2, 0x8d, 0x52, 0xdf, - 0x64, 0x95, 0x53, 0xd5, 0xe4, 0x04, 0x3a, 0x60, 0x98, 0x1c, 0x15, 0xfd, - 0x34, 0xb6, 0xe5, 0x00, 0xb6, 0xc8, 0x5b, 0xfe, 0x9d, 0xe4, 0x18, 0xbd, - 0x7a, 0x00, 0x97, 0xb8, 0x2b, 0x4c, 0xc6, 0x1e, 0xa0, 0x4b, 0x04, 0x5f, - 0x14, 0x4d, 0xe9, 0xaa, 0x06, 0xe1, 0x7a, 0x40, 0x28, 0x98, 0x5a, 0xd4, - 0x83, 0xd9, 0xd8, 0xdc, 0xab, 0xd0, 0xa3, 0x4a, 0xc4, 0xc1, 0x5c, 0x2b, - 0x24, 0x4c, 0x44, 0x93, 0x3c, 0x65, 0x42, 0x02, 0xda, 0xa3, 0x4d, 0x2a, - 0x42, 0x13, 0x96, 0x7e, 0x1d, 0xf3, 0x81, 0xf2, 0xb6, 0x26, 0x23, 0xe4, - 0xb6, 0x79, 0x24, 0xf8, 0x8d, 0x61, 0x58, 0xee, 0x97, 0x8b, 0xcc, 0x4b, - 0xad, 0x55, 0x29, 0xa8, 0x91, 0xd6, 0x36, 0xca, 0x98, 0xf1, 0x54, 0x0d, - 0x00, 0x40, 0x46, 0x49, 0x12, 0xf2, 0x0f, 0xa8, 0xf0, 0x55, 0x55, 0xb0, - 0x9a, 0x05, 0x93, 0xaa, 0xbd, 0x42, 0x8c, 0xeb, 0xf6, 0x32, 0xb4, 0x44, - 0x25, 0xaa, 0x54, 0x35, 0x63, 0x59, 0x9e, 0x87, 0x6a, 0xa6, 0xb5, 0x9a, - 0xaf, 0x5f, 0x1d, 0x4e, 0x3f, 0xa7, 0xf3, 0xc7, 0xed, 0x38, 0xf1, 0xc7, - 0x9c, 0xd7, 0x1e, 0xb5, 0xbf, 0x63, 0x1c, 0xeb, 0x73, 0x57, 0xe2, 0xee, - 0xa3, 0x4c, 0x33, 0xb9, 0xe3, 0x98, 0x6f, 0x9c, 0x08, 0x59, 0xf7, 0xd1, - 0x7e, 0x95, 0x3e, 0x3c, 0x35, 0xba, 0x8d, 0x35, 0xfc, 0xac, 0x5e, 0x09, - 0x4d, 0xc5, 0x20, 0x4f, 0x6f, 0x40, 0x0f, 0x80, 0xc8, 0x33, 0x65, 0x8e, - 0x55, 0xf8, 0x71, 0xe8, 0x79, 0x32, 0xa2, 0x57, 0x16, 0x26, 0xfd, 0x03, - 0x6c, 0xa6, 0xfa, 0xb3, 0x67, 0x1c, 0x13, 0xa8, 0x8a, 0xa9, 0x2b, 0x75, - 0x64, 0xb6, 0x9e, 0x2b, 0x05, 0x48, 0x91, 0x3d, 0x8c, 0x00, 0x0c, 0x15, - 0xa2, 0x20, 0x5e, 0xd4, 0xc3, 0xc3, 0x45, 0x47, 0x8c, 0xe4, 0x42, 0x40, - 0x00, 0x20, 0x23, 0x5b, 0xea, 0x98, 0x52, 0xf6, 0x66, 0x43, 0x32, 0xaf, - 0x01, 0x4b, 0xdd, 0xa8, 0xb2, 0xe4, 0x56, 0x38, 0x36, 0xa0, 0xa5, 0xeb, - 0xc6, 0xe7, 0xbe, 0x81, 0x92, 0xda, 0x59, 0xd7, 0x16, 0x89, 0x01, 0x9e, - 0xd9, 0x04, 0x2b, 0xc2, 0x9a, 0x2d, 0xed, 0xa8, 0x9e, 0x43, 0x07, 0x23, - 0x99, 0xc4, 0x00, 0x9c, 0x03, 0x07, 0x68, 0x71, 0x66, 0x70, 0x69, 0x82, - 0x47, 0x36, 0xba, 0x49, 0x62, 0x08, 0x6f, 0xeb, 0x40, 0x39, 0x99, 0xdc, - 0xd8, 0x9c, 0x19, 0x24, 0x13, 0x6a, 0xdc, 0x29, 0x67, 0x1a, 0x71, 0x89, - 0x0e, 0x62, 0xed, 0x90, 0x82, 0xba, 0x9a, 0x8b, 0x70, 0x51, 0x44, 0x32, - 0x36, 0x4a, 0xdc, 0xe9, 0x81, 0x2d, 0xad, 0xdc, 0x2d, 0xd7, 0x12, 0x34, - 0xd1, 0x22, 0xa3, 0x5b, 0x21, 0xd2, 0x58, 0x0d, 0x33, 0x61, 0x23, 0x20, - 0x88, 0x30, 0x14, 0x97, 0xce, 0xef, 0x7b, 0x40, 0x95, 0x26, 0x95, 0xd7, - 0x65, 0x15, 0xbd, 0x9e, 0x01, 0x5d, 0x54, 0xa0, 0x96, 0x33, 0x03, 0xac, - 0xce, 0x22, 0xa7, 0x2b, 0xa9, 0xb4, 0x20, 0xa0, 0x2d, 0x85, 0xc0, 0xff, - 0xf1, 0x50, 0x80, 0x35, 0xdf, 0xfc, 0x20, 0xad, 0x7a, 0xcd, 0x35, 0x42, - 0x0c, 0x94, 0x83, 0x3c, 0xd5, 0x71, 0x5e, 0xb8, 0xd7, 0x9f, 0x55, 0xd7, - 0xeb, 0xf9, 0xf5, 0xfe, 0x3b, 0xf9, 0xfd, 0x7e, 0x7f, 0x7f, 0xb3, 0xdb, - 0xc7, 0x5b, 0x99, 0xbf, 0x39, 0x55, 0xa8, 0xad, 0x20, 0xbf, 0x1e, 0x04, - 0xee, 0xf5, 0xa5, 0xc0, 0xec, 0x5a, 0xfb, 0xab, 0x77, 0x79, 0x11, 0x40, - 0x02, 0xf7, 0x43, 0x92, 0x47, 0x10, 0x1d, 0x44, 0x94, 0xda, 0x26, 0xd7, - 0xd6, 0xe0, 0x35, 0x56, 0xea, 0xd5, 0xb9, 0x90, 0x8f, 0x5c, 0x84, 0x63, - 0xd0, 0x28, 0x98, 0x1a, 0xea, 0xd4, 0xfa, 0x63, 0xe5, 0x48, 0xd4, 0xaf, - 0x71, 0x44, 0xc1, 0x6d, 0xb3, 0xab, 0xd7, 0x6e, 0xe2, 0xae, 0x93, 0x87, - 0x08, 0x00, 0x22, 0x82, 0xd2, 0x03, 0xe3, 0x4d, 0x7e, 0x87, 0xa1, 0x59, - 0x4a, 0xcb, 0x13, 0xfe, 0xd0, 0x19, 0x6f, 0xdd, 0xc4, 0x3b, 0x24, 0x09, - 0x16, 0xe2, 0x64, 0xf6, 0x90, 0x60, 0x13, 0x99, 0x7e, 0xe8, 0xd2, 0x51, - 0x0c, 0x16, 0xf3, 0xfd, 0x26, 0xa9, 0x82, 0x87, 0xf8, 0xea, 0x3a, 0x82, - 0x8a, 0xa5, 0x83, 0xc5, 0x1b, 0x21, 0x9c, 0xc4, 0x65, 0x43, 0xc2, 0x0a, - 0x30, 0x1f, 0x7b, 0x96, 0xfb, 0x7e, 0xea, 0xd4, 0x7a, 0xdc, 0xa8, 0x22, - 0x5d, 0x88, 0x9e, 0x99, 0xcf, 0x74, 0x9d, 0x69, 0x5c, 0x2f, 0xa7, 0xa4, - 0xba, 0x11, 0xd6, 0x9b, 0x1c, 0x15, 0xe1, 0xc5, 0xeb, 0xb6, 0xf0, 0x15, - 0x50, 0xd6, 0xa4, 0x2d, 0x51, 0xc7, 0x9a, 0xad, 0x3d, 0x7b, 0x7c, 0x78, - 0x7c, 0xff, 0xb6, 0xbf, 0x67, 0x5e, 0xa7, 0xb5, 0x7b, 0x7d, 0xb8, 0xe3, - 0xe7, 0xf5, 0x95, 0x7d, 0xdc, 0xef, 0x55, 0x26, 0xf3, 0x3a, 0x13, 0x28, - 0x94, 0xa5, 0x36, 0x12, 0x29, 0x4e, 0x4e, 0x71, 0x90, 0x87, 0x31, 0xac, - 0xf6, 0xbb, 0xf6, 0xcb, 0x56, 0xae, 0x4b, 0xfd, 0x7a, 0xe7, 0xc7, 0xae, - 0x79, 0x63, 0xe5, 0xe5, 0xce, 0x69, 0xae, 0xc2, 0xb3, 0x8c, 0x57, 0x44, - 0x07, 0xc3, 0xa4, 0x11, 0x7d, 0x8b, 0xb9, 0xb8, 0xd6, 0x4b, 0x9e, 0x5d, - 0x8d, 0x74, 0x77, 0x75, 0xcd, 0xf6, 0x6c, 0x02, 0x26, 0x5c, 0xb7, 0xa6, - 0x27, 0x2c, 0xd4, 0x4d, 0xdf, 0x2d, 0xf1, 0x00, 0x11, 0x9d, 0x7d, 0x30, - 0xa5, 0xdc, 0xa2, 0x70, 0xc6, 0x7f, 0x7e, 0xd5, 0x75, 0x7d, 0x58, 0x8e, - 0x0f, 0xde, 0xe2, 0xfd, 0x0f, 0x0d, 0x09, 0x69, 0xf4, 0xfb, 0xff, 0xe5, - 0x6a, 0x6d, 0x1f, 0xd3, 0x6c, 0x0d, 0x86, 0xb2, 0xa9, 0x8e, 0xbc, 0x5c, - 0x8c, 0xbe, 0x54, 0x89, 0xc9, 0x9a, 0x02, 0x2b, 0xd6, 0xd9, 0xc4, 0x97, - 0xf9, 0xd3, 0x17, 0x90, 0xc0, 0x5c, 0xf8, 0x1d, 0x87, 0x4c, 0xe9, 0x62, - 0x28, 0xe8, 0x79, 0x7d, 0xe8, 0xec, 0x68, 0x59, 0x80, 0x5d, 0x21, 0x35, - 0x96, 0x95, 0xb1, 0x01, 0x74, 0x73, 0xe4, 0xd6, 0x0b, 0x09, 0x34, 0x72, - 0xaf, 0x78, 0x00, 0xde, 0x88, 0x6c, 0xef, 0x6c, 0x96, 0xe5, 0x9d, 0x9a, - 0x4b, 0xd8, 0x87, 0x73, 0x55, 0x56, 0x76, 0x72, 0x90, 0x46, 0x4f, 0x94, - 0xf4, 0x43, 0x54, 0xd7, 0x3f, 0xab, 0x64, 0x00, 0x7c, 0xff, 0xf1, 0x50, - 0x80, 0x2b, 0x3f, 0xfc, 0x21, 0x1a, 0xca, 0xec, 0x4a, 0x58, 0x88, 0x72, - 0xaa, 0x85, 0x28, 0xd8, 0xaa, 0xf2, 0x22, 0x19, 0x3e, 0x37, 0x75, 0x9d, - 0xfb, 0x7e, 0xff, 0xb6, 0xf3, 0xfe, 0xaf, 0xdb, 0xe7, 0xfc, 0xfc, 0xfa, - 0xff, 0x3f, 0xb7, 0xf8, 0xfd, 0xba, 0xd7, 0xcf, 0xe3, 0xb9, 0x31, 0x54, - 0xbb, 0xa5, 0xcd, 0x08, 0x36, 0x11, 0x40, 0x71, 0xaa, 0x42, 0xa8, 0x3e, - 0xd3, 0xac, 0xa5, 0x7b, 0xf1, 0xcf, 0x22, 0x8a, 0x6a, 0x0a, 0xf4, 0x1f, - 0x18, 0xf3, 0x4f, 0xa7, 0x5f, 0xd2, 0xd1, 0x96, 0xb0, 0x73, 0x3e, 0xb9, - 0x41, 0xa7, 0x3a, 0x1e, 0xda, 0x54, 0x6b, 0x5e, 0xb9, 0x0a, 0xf9, 0xf5, - 0xcd, 0x48, 0xe2, 0x1d, 0x5b, 0xe0, 0xee, 0xf8, 0x6b, 0xbc, 0xc7, 0x48, - 0x16, 0x8d, 0xee, 0x7a, 0xf3, 0x3b, 0xec, 0xeb, 0xc2, 0x65, 0x79, 0x89, - 0xc4, 0xf5, 0x80, 0x15, 0x8e, 0x19, 0xeb, 0x5e, 0x71, 0x35, 0x51, 0xc2, - 0x20, 0xbd, 0xea, 0xea, 0xae, 0xb7, 0x56, 0x37, 0x15, 0x32, 0xc6, 0x37, - 0x19, 0x9a, 0x6a, 0xef, 0x9c, 0xdb, 0x33, 0x05, 0x31, 0x73, 0x15, 0x7d, - 0x63, 0x05, 0x0a, 0x60, 0x72, 0x22, 0x1b, 0x25, 0xe8, 0x4c, 0xf9, 0x8d, - 0x01, 0x21, 0x28, 0x49, 0xce, 0xb6, 0xb4, 0xdd, 0xf0, 0x19, 0xd3, 0xf2, - 0x6b, 0x63, 0x49, 0x2e, 0xde, 0x49, 0x26, 0x2e, 0xd6, 0xe2, 0x1d, 0xb3, - 0x85, 0x80, 0xf3, 0xfa, 0x0e, 0xf7, 0x16, 0x55, 0x2d, 0x7a, 0xb3, 0x59, - 0xc5, 0x91, 0xa6, 0xcc, 0xfe, 0x77, 0xbd, 0xc7, 0xb8, 0x63, 0xb7, 0xb8, - 0xfe, 0x20, 0xf6, 0xaa, 0xfe, 0x7a, 0xcf, 0xe8, 0x84, 0xf9, 0x65, 0xed, - 0x59, 0x86, 0xc7, 0xe5, 0xd5, 0x2b, 0x51, 0xbc, 0x24, 0x17, 0xb5, 0x3c, - 0xd5, 0xd3, 0xd7, 0xb7, 0x5b, 0xaf, 0x1f, 0xe3, 0x5f, 0x6a, 0xfa, 0xf9, - 0x9f, 0x5f, 0x3f, 0x8f, 0xdb, 0xeb, 0xcf, 0x73, 0x22, 0xaa, 0x08, 0xba, - 0xbd, 0x0e, 0x3f, 0xdd, 0x7d, 0x9f, 0x28, 0xcc, 0xed, 0x4f, 0x3e, 0x7d, - 0xd9, 0x70, 0xb1, 0xc0, 0xae, 0x86, 0x7f, 0xd2, 0xfe, 0x93, 0xdd, 0x7d, - 0xb7, 0x46, 0xd7, 0x7e, 0xd9, 0xc2, 0x2e, 0xca, 0xf7, 0x3b, 0xde, 0x11, - 0xdf, 0x0f, 0xba, 0x6a, 0x85, 0x18, 0x5b, 0x93, 0xdb, 0x0e, 0x91, 0x7e, - 0xe1, 0xdd, 0xe4, 0xc1, 0xd3, 0x6c, 0xc7, 0xb7, 0x6e, 0xf7, 0xfa, 0x35, - 0x10, 0x2c, 0x35, 0xb7, 0xf8, 0x6a, 0x80, 0x70, 0x83, 0xad, 0xd6, 0xc4, - 0x7f, 0xf3, 0x9b, 0x39, 0x66, 0xe0, 0xff, 0xf1, 0x50, 0x80, 0x2e, 0xdf, - 0xfc, 0x21, 0x1a, 0xcc, 0x62, 0x09, 0x3d, 0x42, 0x90, 0xa1, 0xb9, 0x48, - 0x98, 0x26, 0x82, 0x1a, 0x2c, 0x02, 0x7e, 0xff, 0x3d, 0xbf, 0xe7, 0x5f, - 0xf8, 0x7e, 0xff, 0x9c, 0xff, 0x4f, 0xe9, 0xf7, 0xfb, 0x79, 0xd7, 0xaf, - 0x3c, 0xd1, 0x75, 0x2e, 0xf9, 0x92, 0xb4, 0x09, 0xa7, 0xe4, 0xcb, 0x22, - 0x7c, 0xb6, 0xc0, 0xc9, 0xaa, 0xeb, 0xf9, 0xc6, 0x92, 0xdc, 0xfd, 0xf3, - 0xe2, 0x48, 0x8a, 0x52, 0x79, 0x30, 0x54, 0x02, 0x00, 0x23, 0x6a, 0x09, - 0xcf, 0xe6, 0x32, 0x8e, 0x07, 0x53, 0x88, 0xd5, 0xac, 0xdb, 0xb2, 0x39, - 0xa0, 0x0c, 0xcf, 0x72, 0x3a, 0xc9, 0x43, 0x9e, 0x52, 0x9c, 0x8a, 0x78, - 0xa5, 0xef, 0xfb, 0x0e, 0xd9, 0x05, 0x26, 0xef, 0xb2, 0xfd, 0x5a, 0x91, - 0x7d, 0x59, 0xa9, 0xce, 0x7f, 0x96, 0x25, 0xad, 0xab, 0xb8, 0x87, 0xbc, - 0xfa, 0x07, 0xa4, 0xcb, 0xf7, 0xb6, 0xaf, 0x00, 0x8f, 0x63, 0xfc, 0xae, - 0xae, 0x79, 0xf7, 0x49, 0xbc, 0x6e, 0x62, 0x1d, 0xf0, 0xf3, 0xf9, 0xd2, - 0x7a, 0x89, 0x69, 0x46, 0xb8, 0x58, 0x5d, 0x57, 0xfb, 0xcc, 0x76, 0xd1, - 0x18, 0x13, 0x6f, 0xbd, 0x2a, 0xf0, 0x45, 0x94, 0xa0, 0x15, 0xcf, 0x65, - 0x6e, 0x32, 0x5c, 0x50, 0xcb, 0x24, 0xa7, 0x05, 0xf0, 0xb9, 0xa8, 0x65, - 0x8a, 0x6e, 0x8b, 0x69, 0x32, 0x36, 0x56, 0xfb, 0xff, 0x5b, 0xba, 0x8d, - 0xc4, 0x44, 0x2f, 0x87, 0x84, 0x48, 0x80, 0x30, 0x32, 0xec, 0xa4, 0x90, - 0xb5, 0x5c, 0xcc, 0x20, 0x33, 0x65, 0x7a, 0x67, 0x5b, 0xc7, 0x3c, 0xc2, - 0xfd, 0xc8, 0x23, 0x2d, 0x22, 0xef, 0x6b, 0x78, 0x72, 0xf7, 0x64, 0xfe, - 0xae, 0xe6, 0x30, 0x13, 0x6f, 0x9b, 0x43, 0x2c, 0x81, 0xab, 0x3c, 0x48, - 0x3f, 0x5b, 0xbb, 0xce, 0x9f, 0xed, 0xff, 0x8b, 0xff, 0x3f, 0xd3, 0xaf, - 0x7f, 0xf7, 0xaf, 0xdb, 0x7f, 0x7e, 0xf9, 0xfe, 0x7f, 0x4f, 0xbf, 0xdb, - 0xcf, 0xd7, 0xed, 0xba, 0xfb, 0xca, 0xcd, 0x53, 0x5b, 0x6a, 0xb5, 0x41, - 0xe1, 0xbe, 0x9b, 0x53, 0x4e, 0x1c, 0x68, 0x94, 0xf9, 0xae, 0xed, 0xdb, - 0xff, 0xa2, 0xd2, 0x79, 0x12, 0xef, 0x6c, 0x9b, 0x9c, 0x2f, 0x6c, 0x22, - 0x9d, 0x41, 0x18, 0x8f, 0x92, 0x79, 0x88, 0x67, 0xba, 0x4d, 0x2d, 0xd6, - 0x2a, 0xd7, 0x21, 0x0f, 0x9f, 0x8d, 0x60, 0x71, 0x91, 0xcf, 0x9e, 0x63, - 0x73, 0x80, 0xe8, 0xb0, 0x33, 0x75, 0xb4, 0x5e, 0x9d, 0x35, 0xc7, 0x7a, - 0xe7, 0x59, 0xf9, 0x47, 0x6f, 0x73, 0xbb, 0x90, 0x00, 0x56, 0x37, 0x75, - 0xc9, 0x5c, 0xd6, 0x68, 0xb7, 0x85, 0xf7, 0x93, 0x55, 0x3b, 0x6a, 0x07, - 0x3e, 0xe6, 0xbb, 0x4c, 0xb3, 0x8d, 0x03, 0x3c, 0xff, 0xf1, 0x50, 0x80, - 0x2f, 0x9f, 0xfc, 0x21, 0x1a, 0xcf, 0xed, 0x5a, 0x54, 0x21, 0x30, 0xa6, - 0xa5, 0x18, 0xe0, 0xae, 0x25, 0x42, 0x21, 0x8e, 0x71, 0x96, 0xad, 0xfb, - 0x7f, 0xa7, 0xf7, 0xae, 0x7f, 0xf2, 0xf5, 0xef, 0xef, 0xfe, 0xdf, 0xb7, - 0x7f, 0xe9, 0xfa, 0x7f, 0x8f, 0xe9, 0xf7, 0xdf, 0x8f, 0xd7, 0xbe, 0x78, - 0xae, 0x78, 0x6a, 0xa5, 0x49, 0x74, 0x03, 0x6d, 0xe7, 0x26, 0x08, 0x74, - 0x1c, 0x08, 0xed, 0x5c, 0xaa, 0x3e, 0x6a, 0x5e, 0x41, 0x28, 0x1a, 0x8d, - 0x72, 0xf5, 0x7f, 0x13, 0x2f, 0x8b, 0x3b, 0xc7, 0xa8, 0x52, 0x65, 0x40, - 0xf2, 0xc4, 0x68, 0x43, 0x6b, 0x7d, 0xcc, 0xc2, 0xe6, 0x93, 0x94, 0x15, - 0xc5, 0xee, 0x76, 0x87, 0xda, 0xe8, 0xbc, 0x71, 0x1a, 0xe0, 0x86, 0xb8, - 0xfc, 0x7b, 0x7b, 0x2e, 0xbd, 0x09, 0xd7, 0x1a, 0x01, 0x69, 0xce, 0x67, - 0x8e, 0xf8, 0xf2, 0x39, 0xf0, 0x95, 0xa3, 0x6d, 0x72, 0x00, 0x57, 0x4f, - 0x7b, 0xe0, 0xa6, 0x7a, 0xfa, 0x73, 0x88, 0x8b, 0x04, 0x3e, 0xa7, 0x32, - 0x06, 0xa9, 0x53, 0xa0, 0xb2, 0x18, 0xf0, 0xbf, 0x66, 0x7c, 0x96, 0x16, - 0x6a, 0x5e, 0xab, 0xa7, 0x93, 0x33, 0x2e, 0x00, 0xaf, 0x36, 0x51, 0xb7, - 0x82, 0x98, 0x7b, 0x69, 0x81, 0x27, 0x5c, 0xf9, 0x55, 0x07, 0x4b, 0x7b, - 0x0e, 0xcf, 0x1b, 0x20, 0xa0, 0x74, 0x97, 0x9e, 0xfa, 0xa3, 0x96, 0x61, - 0x68, 0x40, 0x81, 0x92, 0x19, 0x29, 0x5c, 0x66, 0xf6, 0xc9, 0x01, 0x72, - 0x11, 0x91, 0x22, 0x39, 0x39, 0x92, 0x28, 0x90, 0xb3, 0x03, 0x89, 0xd4, - 0x41, 0x62, 0x13, 0x24, 0x4b, 0x22, 0xba, 0x82, 0x85, 0x08, 0x8f, 0x02, - 0xc0, 0x42, 0xb4, 0xac, 0x50, 0x19, 0xa1, 0xdd, 0x20, 0x02, 0x19, 0x55, - 0x71, 0x82, 0x5c, 0xe7, 0x82, 0x8b, 0x0d, 0x0e, 0x0e, 0xef, 0x70, 0xd1, - 0x0d, 0x7d, 0x33, 0xb5, 0x5d, 0xe3, 0x40, 0xf0, 0x50, 0x3e, 0xa3, 0x8c, - 0xb7, 0x9f, 0xb7, 0xb7, 0xb7, 0xcd, 0xfa, 0xff, 0x47, 0xbf, 0x3f, 0xcf, - 0xce, 0xfe, 0xff, 0x6f, 0xdf, 0xf6, 0xf6, 0xf3, 0xf6, 0xde, 0x57, 0xdd, - 0x97, 0x56, 0x9b, 0xbb, 0x03, 0x5f, 0x9b, 0x0d, 0xa8, 0x8a, 0xc5, 0x13, - 0xfc, 0x7a, 0x9a, 0x3b, 0x2b, 0xac, 0xa3, 0x76, 0xcc, 0xd9, 0xe0, 0xaf, - 0x4f, 0x7d, 0x78, 0xf7, 0x64, 0xce, 0xeb, 0x85, 0x39, 0xf9, 0x4b, 0x97, - 0xbe, 0x5a, 0xe5, 0x69, 0xb9, 0x9e, 0xae, 0x41, 0xbe, 0xfb, 0xa4, 0x4d, - 0xdf, 0xae, 0x85, 0xee, 0xba, 0x7a, 0x19, 0xf6, 0x0f, 0x00, 0x0b, 0xa3, - 0x4e, 0x97, 0x1e, 0xb4, 0x32, 0xcc, 0xf3, 0xed, 0xe2, 0xbe, 0xc1, 0x40, - 0xa7, 0xd6, 0xb5, 0xe7, 0x8f, 0xa9, 0xd1, 0x0d, 0xe4, 0x4b, 0xef, 0x24, - 0x0d, 0xa5, 0x55, 0x2e, 0xff, 0xf1, 0x50, 0x80, 0x37, 0x1f, 0xfc, 0x20, - 0xa9, 0x1a, 0xd0, 0xa1, 0x13, 0x18, 0xda, 0x48, 0x42, 0x1f, 0xaf, 0xda, - 0xb5, 0x5b, 0xe3, 0xfd, 0xbf, 0xb5, 0xfa, 0xff, 0x6e, 0xbf, 0x8f, 0x9f, - 0xfb, 0xfe, 0x9f, 0x6f, 0xdf, 0xdf, 0xfc, 0x7f, 0x6f, 0xae, 0x3f, 0x4f, - 0xbb, 0xe6, 0xe5, 0xdd, 0x4b, 0x55, 0xef, 0x40, 0xbd, 0x4f, 0xfa, 0x41, - 0x4f, 0x19, 0x03, 0xa4, 0x00, 0xb9, 0x0d, 0xec, 0xd7, 0x9c, 0xd5, 0xab, - 0x83, 0xcc, 0x16, 0xeb, 0x3a, 0x38, 0xa4, 0xb7, 0xc7, 0xef, 0x68, 0xcc, - 0x40, 0x1d, 0x66, 0x1e, 0x12, 0x70, 0x32, 0xea, 0x3c, 0x0f, 0xd5, 0x9d, - 0x4f, 0x27, 0xa7, 0x1d, 0xd7, 0x3f, 0xf3, 0x1f, 0x19, 0xff, 0xf7, 0xaf, - 0x39, 0x1a, 0x5a, 0x5e, 0xcf, 0xb1, 0xc7, 0xef, 0x3e, 0xbb, 0x0f, 0x4e, - 0xde, 0xdf, 0xbe, 0x0d, 0xbd, 0x6f, 0xea, 0xf3, 0xdf, 0x15, 0xf5, 0x32, - 0x5f, 0x95, 0x4e, 0x8b, 0x61, 0xa0, 0x88, 0x84, 0xeb, 0x45, 0x64, 0x51, - 0x9c, 0x4e, 0x75, 0x27, 0x6c, 0x5c, 0x8e, 0x22, 0x04, 0xff, 0x6c, 0x50, - 0x25, 0x4a, 0x1d, 0xb5, 0x9f, 0xe4, 0xf8, 0x06, 0x67, 0xa2, 0xe0, 0xb6, - 0xa7, 0x66, 0xc2, 0xcf, 0x92, 0x78, 0x34, 0x37, 0xd8, 0x38, 0x75, 0x05, - 0x36, 0x78, 0x15, 0x97, 0xcd, 0x43, 0x51, 0x8f, 0x99, 0xc7, 0x01, 0x11, - 0x56, 0xf5, 0x17, 0xc0, 0xf2, 0xdc, 0x44, 0xe6, 0xa8, 0xe3, 0x69, 0x24, - 0x29, 0x8a, 0x98, 0xc2, 0x43, 0x79, 0x8e, 0x46, 0x1f, 0x85, 0x41, 0x56, - 0x81, 0x34, 0x55, 0x4c, 0x24, 0x61, 0xe7, 0x16, 0xf1, 0xed, 0x79, 0x3d, - 0xfe, 0xff, 0x5e, 0xbd, 0x7e, 0x39, 0xef, 0xf7, 0xfd, 0xbe, 0xff, 0x9d, - 0x7d, 0xfd, 0xfe, 0x37, 0xed, 0x59, 0xb9, 0x5b, 0xeb, 0x15, 0x37, 0x61, - 0x3d, 0x4f, 0x45, 0xd6, 0x2c, 0xbd, 0x27, 0xd6, 0xba, 0x83, 0x50, 0x76, - 0xc8, 0x95, 0x08, 0x28, 0xce, 0x00, 0x55, 0x05, 0xfe, 0xf8, 0xca, 0x78, - 0x8e, 0xa8, 0x9a, 0xd1, 0xb1, 0x4c, 0x74, 0xa0, 0x31, 0x65, 0x04, 0x46, - 0xa6, 0x82, 0xbd, 0xe0, 0x9d, 0x47, 0xa3, 0xa3, 0x88, 0x8e, 0x49, 0x67, - 0x7b, 0x98, 0x8e, 0xaf, 0x92, 0xfa, 0xba, 0xc0, 0x04, 0xd3, 0x17, 0xa8, - 0x8c, 0x6b, 0x51, 0xd7, 0xbc, 0x6f, 0x53, 0xd7, 0x00, 0x04, 0xd4, 0xf4, - 0xfa, 0x34, 0x8d, 0x27, 0x8c, 0x50, 0x9c, 0xfa, 0x2c, 0xb1, 0x04, 0xee, - 0x55, 0x37, 0x68, 0xa3, 0x15, 0x70, 0x94, 0xd3, 0x84, 0x86, 0xf3, 0x22, - 0x2d, 0x93, 0xde, 0x8e, 0xe7, 0x68, 0x56, 0x94, 0xb7, 0x4e, 0xaf, 0xaf, - 0xf9, 0x10, 0x2f, 0xb3, 0xe0, 0x71, 0xbd, 0x6b, 0x0c, 0xdf, 0xb3, 0x57, - 0xf8, 0xfc, 0x05, 0x4c, 0xf6, 0xb2, 0xed, 0xea, 0x8e, 0x2b, 0x08, 0xb5, - 0x8c, 0xa2, 0xa9, 0x19, 0x7c, 0xb8, 0x17, 0x30, 0x11, 0xd4, 0x72, 0x1d, - 0x14, 0x0c, 0x6b, 0x46, 0x36, 0x1c, 0x5c, 0xd2, 0x3c, 0xba, 0xc9, 0xb2, - 0x1b, 0x98, 0x0a, 0x53, 0x56, 0xfb, 0x98, 0x1c, 0x25, 0xd7, 0xa5, 0x0e, - 0x83, 0x1b, 0x2e, 0xdb, 0xb7, 0xb7, 0x0f, 0xf9, 0x80, 0x33, 0x42, 0xb9, - 0x24, 0x04, 0x65, 0x03, 0x44, 0xfb, 0x1c, 0x01, 0xd2, 0x4f, 0x4b, 0x80, - 0xff, 0xf1, 0x50, 0x80, 0x35, 0xdf, 0xfc, 0x20, 0xae, 0x1a, 0xcd, 0x34, - 0x50, 0x8d, 0x16, 0x7f, 0x9f, 0xce, 0x79, 0xef, 0xbf, 0xdf, 0xe3, 0xc7, - 0x9f, 0xf8, 0xfd, 0x6f, 0xcf, 0xfd, 0x3f, 0xe6, 0x7f, 0xaf, 0xf3, 0xde, - 0xbd, 0x7b, 0x6b, 0xf3, 0xf7, 0xcf, 0x12, 0xbe, 0xeb, 0xa9, 0x56, 0x65, - 0x82, 0xca, 0xa1, 0xdb, 0x49, 0x20, 0xc4, 0xc2, 0x0d, 0x7c, 0x64, 0x1e, - 0x1b, 0xc5, 0x69, 0x2c, 0x1f, 0x3d, 0xa0, 0x72, 0x05, 0x53, 0x20, 0x1e, - 0x06, 0x7c, 0xc2, 0xe7, 0x0d, 0xc8, 0x0e, 0xc0, 0xfa, 0x98, 0xef, 0xc9, - 0xc5, 0xb7, 0x17, 0x76, 0xb4, 0xaf, 0x05, 0x4d, 0x0e, 0xca, 0xf9, 0x9d, - 0x26, 0x1c, 0xe2, 0xdd, 0xdc, 0x2c, 0xd4, 0xf7, 0x48, 0x25, 0xe4, 0x48, - 0x71, 0x0e, 0x09, 0x81, 0xdd, 0xdd, 0xcd, 0x6e, 0xbc, 0xc5, 0x98, 0xa6, - 0x73, 0xb6, 0x92, 0x00, 0xa6, 0xf1, 0x14, 0xe9, 0x92, 0xb2, 0xc1, 0x89, - 0x2a, 0x30, 0x4e, 0x3e, 0x2c, 0x4e, 0xf4, 0x41, 0x68, 0x2f, 0xd2, 0x84, - 0xe1, 0x5e, 0x95, 0xfb, 0xc1, 0x7a, 0x17, 0x5e, 0x60, 0x7b, 0x1c, 0x74, - 0xe7, 0xa4, 0x1a, 0x55, 0x17, 0x1f, 0xe8, 0x2c, 0x6d, 0x9e, 0x88, 0x86, - 0xfd, 0xfd, 0x8d, 0xea, 0xe9, 0x3d, 0x0b, 0x54, 0x4d, 0xf0, 0x8e, 0x52, - 0x81, 0x35, 0xf2, 0x5a, 0xa0, 0x15, 0x15, 0x87, 0x49, 0x33, 0xf9, 0xde, - 0x2b, 0x58, 0x3c, 0x08, 0x0a, 0x0e, 0x8e, 0x6b, 0x51, 0xb5, 0x0f, 0xe4, - 0xbb, 0x2d, 0x4b, 0xcb, 0xa6, 0xbe, 0x8a, 0xdb, 0xe3, 0x9c, 0xd6, 0x27, - 0xf6, 0x21, 0x5c, 0xb4, 0xd6, 0x9a, 0x5f, 0x5b, 0x48, 0x2f, 0xe6, 0xbc, - 0x15, 0x2a, 0x54, 0xc3, 0x67, 0xb7, 0x37, 0x09, 0x57, 0x22, 0x14, 0x89, - 0x8a, 0x78, 0x03, 0xa7, 0x05, 0x38, 0xdf, 0xef, 0xcb, 0xf1, 0xf3, 0xcf, - 0x17, 0xe3, 0xdc, 0xf6, 0xbe, 0x7e, 0x29, 0x5e, 0xd3, 0xfd, 0x4f, 0x8e, - 0xbc, 0x71, 0xec, 0xd4, 0x3d, 0xf4, 0xfb, 0xfa, 0x00, 0x1d, 0xc9, 0x44, - 0x20, 0x17, 0x58, 0x95, 0x97, 0x2b, 0x12, 0x33, 0xc6, 0x3a, 0x27, 0xd6, - 0xc7, 0xa2, 0xac, 0x55, 0x10, 0x6c, 0x54, 0x2f, 0x77, 0xb5, 0x49, 0x43, - 0x8e, 0x6f, 0x6f, 0x54, 0xb5, 0x1c, 0xe3, 0x12, 0x54, 0x18, 0x54, 0x91, - 0x89, 0xd1, 0x22, 0xc5, 0x11, 0x1f, 0xdc, 0xa9, 0x4a, 0xf0, 0x58, 0x94, - 0x1a, 0xcb, 0x66, 0xbc, 0x27, 0xcd, 0xa1, 0x2b, 0x6e, 0xbe, 0x3f, 0xcc, - 0x0a, 0x7a, 0x14, 0xbc, 0x80, 0xa9, 0x2b, 0x30, 0x87, 0xeb, 0xa6, 0x54, - 0x0d, 0x27, 0x40, 0x2d, 0x54, 0x22, 0x8c, 0xce, 0xbe, 0xa3, 0x63, 0xf2, - 0xbb, 0xec, 0x69, 0xd0, 0x52, 0xf2, 0xc7, 0x44, 0x4f, 0x0c, 0x2f, 0x3d, - 0xb5, 0x9c, 0xc3, 0xbb, 0x9d, 0xc3, 0xb6, 0x37, 0x3e, 0xd1, 0xe4, 0xf8, - 0xf8, 0xe4, 0x23, 0x95, 0x53, 0x34, 0x8e, 0x86, 0xc1, 0xc6, 0xeb, 0xe9, - 0xbe, 0x83, 0xbd, 0x6a, 0x31, 0xc8, 0x3a, 0x27, 0xc4, 0xd0, 0xc5, 0xc7, - 0xbf, 0xb3, 0xc2, 0xfc, 0x5e, 0x39, 0x38, 0x80, 0x17, 0x9c, 0xc7, 0x20, - 0xfa, 0xae, 0x97, 0xf9, 0x5d, 0x1f, 0xc8, 0x7d, 0xe2, 0xff, 0xff, 0xf1, - 0x50, 0x80, 0x37, 0xff, 0xfc, 0x20, 0xa4, 0x1a, 0xd6, 0xe7, 0x52, 0x08, - 0xc4, 0x8f, 0x2f, 0x17, 0x7c, 0xeb, 0xfe, 0x7f, 0xac, 0xf9, 0xff, 0x7f, - 0x99, 0xf9, 0xfd, 0xfd, 0x78, 0xfa, 0xf4, 0xcf, 0xbc, 0xf9, 0xbe, 0x94, - 0x6b, 0x76, 0xd6, 0xee, 0x9c, 0x05, 0x19, 0x4c, 0x8e, 0x17, 0x39, 0xe1, - 0x4b, 0x72, 0x8e, 0xc0, 0x32, 0x11, 0x59, 0x9f, 0xdf, 0x4b, 0xca, 0x39, - 0x4f, 0x32, 0x35, 0x19, 0xa5, 0x94, 0xc9, 0x7b, 0x24, 0xd9, 0xc8, 0xe4, - 0x29, 0x58, 0xe8, 0x93, 0x15, 0xc6, 0xe4, 0x43, 0x5a, 0xfd, 0x27, 0xdd, - 0x9a, 0xa8, 0xe3, 0xe9, 0xec, 0xa2, 0x31, 0xdd, 0xfa, 0xdc, 0x10, 0x9c, - 0xb9, 0x5b, 0x58, 0x8a, 0xd3, 0x93, 0x09, 0x2f, 0x7e, 0x41, 0xd5, 0x60, - 0x0b, 0xe2, 0x44, 0x51, 0xd5, 0xfc, 0xf9, 0x57, 0x54, 0x19, 0xbf, 0x66, - 0xf1, 0x40, 0xeb, 0xc1, 0xed, 0x8f, 0x9b, 0xb7, 0x4b, 0x2b, 0x61, 0x58, - 0xc4, 0x81, 0xb1, 0xe1, 0x84, 0xe5, 0xc1, 0x9b, 0x35, 0x75, 0x8c, 0x85, - 0x58, 0xde, 0x33, 0xd8, 0xd3, 0x17, 0xe8, 0xcc, 0xf5, 0x2c, 0x35, 0x19, - 0xab, 0xda, 0x05, 0x00, 0x67, 0x61, 0x7e, 0x5b, 0xf5, 0x31, 0xe2, 0xd6, - 0x59, 0x6d, 0xf2, 0x4b, 0x84, 0x12, 0x04, 0xac, 0xa1, 0xe0, 0xcb, 0x58, - 0x1c, 0x00, 0x41, 0x9d, 0x01, 0xb5, 0xc7, 0x72, 0xe4, 0x42, 0x69, 0x58, - 0xb6, 0x33, 0x20, 0x87, 0xba, 0x3c, 0x83, 0xa9, 0xd8, 0x9a, 0xe0, 0x3c, - 0x8f, 0x6d, 0xe0, 0x53, 0x9d, 0x0d, 0x2d, 0x3a, 0xd8, 0xea, 0x3b, 0xac, - 0xd5, 0x01, 0xac, 0xff, 0xd5, 0x5a, 0x0a, 0xfd, 0x2e, 0xe9, 0xf4, 0xeb, - 0xbc, 0x95, 0x1a, 0x7a, 0x77, 0xad, 0x42, 0x66, 0x2d, 0xd8, 0x68, 0x33, - 0x5b, 0xd7, 0x1b, 0xe7, 0x5e, 0x79, 0xfd, 0x7f, 0xb7, 0xf8, 0xf1, 0xde, - 0xf8, 0x57, 0xfb, 0x7f, 0x65, 0x6b, 0x1f, 0x1e, 0x3c, 0xd5, 0x6a, 0x73, - 0xc6, 0xfe, 0xb9, 0x95, 0xbb, 0x00, 0xf3, 0xb2, 0x19, 0xcd, 0x5a, 0x72, - 0x2c, 0xd8, 0x92, 0xe8, 0xf8, 0xbd, 0x55, 0xb8, 0x0a, 0xd1, 0x1a, 0x93, - 0x61, 0x9f, 0xff, 0x8a, 0xc6, 0xda, 0xe1, 0x3d, 0x7d, 0x01, 0xeb, 0x94, - 0x9c, 0xa2, 0x8b, 0xeb, 0x4d, 0xf4, 0x44, 0x1d, 0xdd, 0xdf, 0x57, 0xd9, - 0xdd, 0x47, 0x1a, 0xb1, 0xb1, 0x7c, 0xea, 0xa9, 0x9e, 0xa0, 0x38, 0x00, - 0xc4, 0x52, 0x01, 0x32, 0x31, 0x00, 0x90, 0x99, 0xce, 0x7b, 0xee, 0xea, - 0xa0, 0xa9, 0x04, 0x03, 0x11, 0x0b, 0x4c, 0x3a, 0xf6, 0xcd, 0xa6, 0x73, - 0x9d, 0x17, 0x77, 0x5a, 0xa1, 0xd4, 0x0d, 0x74, 0x61, 0x8b, 0x5c, 0x21, - 0x55, 0x69, 0xee, 0x9d, 0xdc, 0xda, 0x6d, 0xb3, 0x27, 0x67, 0x8e, 0x91, - 0x8b, 0x95, 0x44, 0xdb, 0xb1, 0x29, 0xaa, 0xc0, 0xa5, 0x56, 0xf3, 0xab, - 0x9d, 0x16, 0x88, 0xe8, 0x99, 0x51, 0xd8, 0x8c, 0x8e, 0x39, 0xa0, 0xcf, - 0x35, 0xcb, 0x39, 0xa0, 0xc8, 0x51, 0x5a, 0xf8, 0x40, 0xb8, 0xdd, 0xc2, - 0x61, 0x98, 0x05, 0xad, 0x52, 0x32, 0x46, 0x57, 0xda, 0x46, 0x82, 0x15, - 0x24, 0x0e, 0xa5, 0x68, 0xd2, 0x5b, 0xfb, 0x14, 0xc2, 0x73, 0x67, 0xad, - 0x44, 0x30, 0x55, 0xfc, 0x8d, 0x72, 0x86, 0x2f, 0x57, 0x0c, 0x1f, 0xda, - 0xf0, 0xff, 0xf1, 0x50, 0x80, 0x25, 0x1f, 0xfc, 0x21, 0x1a, 0xcf, 0xf7, - 0xc1, 0x54, 0x37, 0x87, 0xa7, 0xa3, 0x2b, 0xcd, 0xa4, 0x64, 0x19, 0x75, - 0xc4, 0xaf, 0xd3, 0xef, 0xf8, 0xf9, 0xfb, 0xff, 0x5f, 0xe7, 0x7c, 0x7f, - 0xd3, 0xfe, 0xbf, 0x3f, 0xb7, 0xfe, 0x5f, 0xd7, 0xcf, 0xbf, 0xd6, 0xb9, - 0xcc, 0xf6, 0xcf, 0x3b, 0xc5, 0x93, 0x35, 0x74, 0x04, 0xe6, 0x30, 0xf2, - 0x97, 0xa0, 0x7b, 0x02, 0x8b, 0x04, 0x67, 0x27, 0xf5, 0xf8, 0xf6, 0xe7, - 0x92, 0x11, 0x2d, 0x46, 0xae, 0x63, 0xe1, 0x57, 0x75, 0x2e, 0x35, 0xc9, - 0xc6, 0x9d, 0x0c, 0xd4, 0xfc, 0x63, 0x25, 0x5a, 0xe3, 0xa0, 0x0f, 0xab, - 0x48, 0x53, 0x96, 0x8b, 0x2e, 0x34, 0xbb, 0x85, 0x06, 0xa7, 0xd6, 0x4f, - 0x15, 0x38, 0xf6, 0x9e, 0x50, 0x09, 0xe0, 0x02, 0x46, 0x16, 0x8e, 0xe5, - 0xaf, 0xb1, 0xd4, 0xb3, 0x63, 0x62, 0xd4, 0xbc, 0xf0, 0x2f, 0xe3, 0xf5, - 0x11, 0xed, 0x12, 0xd1, 0xab, 0x3d, 0x76, 0x1d, 0x99, 0x1e, 0x43, 0x8e, - 0x17, 0x10, 0xb9, 0x8d, 0xe4, 0x0a, 0x64, 0x03, 0x6e, 0x52, 0x7c, 0x80, - 0xd6, 0xc3, 0xea, 0x20, 0x1b, 0x79, 0x4b, 0xe8, 0x24, 0x76, 0xf1, 0x59, - 0xbc, 0x82, 0xd7, 0x3d, 0x55, 0xae, 0xc6, 0x80, 0xa0, 0x4c, 0x09, 0x48, - 0xd7, 0x10, 0xa7, 0xc7, 0xd5, 0x52, 0xa0, 0xa2, 0xab, 0xb3, 0xc5, 0x92, - 0x55, 0x66, 0x84, 0xf5, 0x3b, 0xd8, 0x49, 0xc1, 0x40, 0xf2, 0x50, 0x3c, - 0x52, 0xeb, 0x89, 0x5d, 0xeb, 0xf1, 0xf3, 0xf7, 0xfe, 0xbf, 0xef, 0xaf, - 0xef, 0xbe, 0x9b, 0xfd, 0xff, 0x37, 0x57, 0x99, 0xf1, 0x7c, 0xce, 0x37, - 0x50, 0xf8, 0xca, 0xbb, 0xa0, 0xe4, 0xce, 0x70, 0x4b, 0x35, 0x1b, 0xff, - 0xc1, 0x8b, 0x9b, 0x13, 0x7c, 0x42, 0xb9, 0x67, 0xd8, 0xe7, 0xd4, 0x75, - 0xd9, 0x5f, 0x37, 0x51, 0xb3, 0x89, 0xc3, 0x91, 0xc5, 0xda, 0x1c, 0xa8, - 0x0c, 0x8b, 0xed, 0x82, 0x66, 0x57, 0x62, 0xb6, 0x48, 0x26, 0xfc, 0x62, - 0x5c, 0x6f, 0xd7, 0x15, 0x30, 0x2c, 0xbd, 0xed, 0x6b, 0x60, 0xc9, 0x42, - 0x7c, 0xf1, 0xbe, 0x72, 0x00, 0x02, 0x9f, 0xd6, 0xb8, 0xff, 0xf1, 0x50, - 0x80, 0x26, 0xbf, 0xfc, 0x21, 0x1a, 0xcf, 0xbd, 0x60, 0x54, 0x1f, 0x3b, - 0xa7, 0xa4, 0x33, 0x54, 0x66, 0x64, 0x4b, 0x10, 0xbc, 0x71, 0x5b, 0xd7, - 0xf9, 0xfe, 0xbf, 0x8f, 0xe9, 0xfa, 0xf5, 0xbf, 0xed, 0xfe, 0xf5, 0xfd, - 0xff, 0xeb, 0xfd, 0xfc, 0xfa, 0xfd, 0x79, 0xe3, 0xdd, 0xed, 0x71, 0x92, - 0xf7, 0xc5, 0xdd, 0x67, 0x01, 0xd3, 0xbb, 0xd1, 0xa2, 0x82, 0x89, 0xc9, - 0xc4, 0xc4, 0x49, 0xe3, 0x54, 0x37, 0x52, 0x8f, 0xc7, 0x50, 0x21, 0x84, - 0xf1, 0x74, 0xe2, 0x15, 0x22, 0x11, 0x82, 0x1c, 0x9c, 0xf6, 0x0c, 0x8c, - 0x15, 0xfc, 0x01, 0x89, 0x38, 0xbd, 0xb2, 0x83, 0x2b, 0xe3, 0x6f, 0x4a, - 0x00, 0x00, 0xa9, 0xe7, 0x40, 0xda, 0x2d, 0xe8, 0x30, 0x17, 0x90, 0xcf, - 0xc8, 0x18, 0x48, 0xe7, 0xee, 0x0b, 0x3b, 0xfe, 0xd4, 0xe3, 0xe3, 0x18, - 0x40, 0x78, 0x8a, 0x54, 0xd2, 0xde, 0x68, 0x56, 0x30, 0xbd, 0xcb, 0x37, - 0x38, 0x91, 0xba, 0x2c, 0x7e, 0xdd, 0x9c, 0x86, 0x6c, 0xcf, 0xd8, 0x7c, - 0x86, 0x47, 0xfc, 0xd5, 0x59, 0x32, 0xc9, 0x69, 0x9a, 0x6a, 0xca, 0xb5, - 0xe6, 0xed, 0xe9, 0x57, 0x46, 0xa6, 0x37, 0x1a, 0xfc, 0x0e, 0x62, 0x9c, - 0x0c, 0x8a, 0xdc, 0x15, 0xb7, 0xc4, 0xc6, 0x2b, 0xf1, 0x50, 0x25, 0x02, - 0xba, 0xc8, 0x5d, 0x3a, 0xda, 0x65, 0x8f, 0x4b, 0xe3, 0x5e, 0xe9, 0x8b, - 0x12, 0xfe, 0x67, 0x7d, 0x68, 0xcf, 0x08, 0x26, 0x64, 0xc4, 0x95, 0x8d, - 0xb5, 0x00, 0xb0, 0xc4, 0x0a, 0xa0, 0x3a, 0x5c, 0x0e, 0x34, 0x38, 0x23, - 0x9c, 0x30, 0x45, 0xf3, 0xd7, 0x48, 0x19, 0x22, 0x4d, 0x63, 0x55, 0x03, - 0x4e, 0xb4, 0x33, 0x78, 0x4c, 0x3c, 0xf2, 0xf2, 0xee, 0xb7, 0xaf, 0xbf, - 0xbf, 0x9f, 0xcf, 0xeb, 0xdf, 0x24, 0xcf, 0xf4, 0xfe, 0xba, 0xcf, 0x6f, - 0x13, 0xcf, 0x2b, 0x99, 0xbd, 0x4c, 0xcb, 0xbc, 0x01, 0x18, 0x8b, 0x5f, - 0x4f, 0x6a, 0xc8, 0xff, 0x7b, 0xdf, 0xc3, 0x47, 0x34, 0xb9, 0xe0, 0xfb, - 0x7c, 0x3c, 0x10, 0x1c, 0xf8, 0x41, 0xab, 0xc8, 0x73, 0x07, 0x01, 0xdb, - 0xa1, 0xaf, 0xcd, 0x25, 0x88, 0x4a, 0xad, 0x7f, 0xd0, 0x1d, 0xa0, 0xd5, - 0xd5, 0x25, 0x10, 0xee, 0x55, 0x3e, 0xff, 0xf1, 0x50, 0x80, 0x3a, 0x5f, - 0xfc, 0x20, 0xa0, 0x1a, 0xd7, 0x19, 0x39, 0x99, 0x0c, 0xa7, 0x03, 0x5b, - 0x7d, 0x78, 0xdb, 0xdb, 0xfd, 0x3f, 0xe9, 0x9c, 0x72, 0xfd, 0x7f, 0x4b, - 0xc4, 0xf3, 0xe8, 0xfb, 0xd5, 0x54, 0xbf, 0x7f, 0xbe, 0x5e, 0x4b, 0x0b, - 0x5c, 0xeb, 0x6b, 0x93, 0x33, 0x1f, 0x34, 0x5b, 0x04, 0xfb, 0x50, 0x8b, - 0x45, 0xa3, 0xd1, 0x64, 0x92, 0x36, 0x23, 0xc7, 0xde, 0x4d, 0x29, 0x0a, - 0xf0, 0x03, 0x55, 0xd3, 0x23, 0xe3, 0x80, 0x56, 0x3c, 0x51, 0xd0, 0x81, - 0x3b, 0x1a, 0x26, 0xc3, 0xe3, 0x16, 0x5b, 0x38, 0x5f, 0xa0, 0x09, 0x78, - 0x4e, 0xaa, 0x85, 0xb7, 0xeb, 0xff, 0x86, 0xee, 0x14, 0x4e, 0xb6, 0xa7, - 0x0e, 0x32, 0x11, 0xcf, 0xfa, 0x59, 0x55, 0x21, 0x7b, 0x43, 0x0f, 0xcb, - 0x34, 0x80, 0x3f, 0x1f, 0xf0, 0x22, 0x1b, 0x1f, 0xe9, 0xb6, 0xf4, 0x3b, - 0x2a, 0x4c, 0xf2, 0x22, 0xa7, 0xbc, 0xfe, 0x48, 0x81, 0xde, 0xb8, 0x79, - 0x28, 0x94, 0xa3, 0xc3, 0x43, 0x4d, 0x2a, 0x58, 0x82, 0xf9, 0x08, 0x47, - 0x20, 0x2c, 0x64, 0x40, 0x3e, 0xb2, 0x58, 0xcc, 0x81, 0x4c, 0xde, 0x87, - 0xd3, 0x95, 0x54, 0x12, 0xd2, 0xc3, 0xd9, 0xe8, 0xda, 0x96, 0x65, 0xdc, - 0x27, 0xca, 0x4c, 0x03, 0x0d, 0xf4, 0x16, 0x0e, 0x21, 0x60, 0xca, 0xbe, - 0xea, 0xeb, 0xe0, 0x8a, 0xa8, 0x62, 0xb2, 0xce, 0x1e, 0xde, 0x6e, 0xaa, - 0x11, 0x59, 0xec, 0x9e, 0x35, 0xac, 0x72, 0xc8, 0xed, 0xed, 0xef, 0xad, - 0x5e, 0xa6, 0x3d, 0x9b, 0xce, 0x65, 0x51, 0x9b, 0xda, 0x23, 0xbb, 0x7c, - 0x31, 0xc2, 0xf3, 0xa8, 0xdf, 0x73, 0x18, 0xbf, 0x4f, 0x2e, 0xf8, 0x39, - 0x7c, 0x1a, 0x87, 0x7d, 0x14, 0x65, 0x5a, 0xd1, 0x05, 0x5c, 0x31, 0x89, - 0x56, 0x64, 0xeb, 0x9b, 0xe7, 0xac, 0x99, 0x1b, 0xfe, 0x7f, 0x49, 0x9e, - 0xdc, 0xb5, 0xbb, 0xb9, 0xd2, 0x56, 0xfd, 0xb8, 0xac, 0x68, 0x28, 0x5e, - 0xbb, 0xdd, 0x32, 0x9a, 0x71, 0x64, 0x93, 0xef, 0x23, 0xab, 0x8e, 0x9d, - 0x6a, 0xd6, 0x22, 0xb8, 0xc6, 0x58, 0xdc, 0x92, 0xce, 0x31, 0x96, 0xf8, - 0xff, 0x3c, 0x4f, 0xb3, 0xae, 0x46, 0xbc, 0xbe, 0x11, 0xba, 0x9e, 0x9f, - 0x98, 0x75, 0xc0, 0x72, 0x38, 0xfe, 0xc8, 0x1f, 0x1d, 0xf1, 0xae, 0x9c, - 0xaa, 0x77, 0xf0, 0xe8, 0xb8, 0xa8, 0x1c, 0xf4, 0x15, 0xe4, 0x0c, 0xd1, - 0x37, 0x51, 0xdd, 0x89, 0x48, 0x38, 0x20, 0x39, 0x04, 0x05, 0x87, 0x74, - 0xc4, 0x8a, 0xc0, 0x20, 0x13, 0x01, 0x45, 0x38, 0x81, 0x19, 0x04, 0xd4, - 0x8b, 0xe5, 0x32, 0x3a, 0x53, 0x5c, 0xd5, 0x08, 0x44, 0x75, 0x54, 0x5e, - 0x77, 0x3c, 0x21, 0x8c, 0xe5, 0xad, 0x45, 0x4d, 0x1e, 0x18, 0x6b, 0xb7, - 0x14, 0xbe, 0x39, 0xe2, 0x67, 0xbb, 0x91, 0x33, 0x82, 0xea, 0xa2, 0xe2, - 0xfd, 0x93, 0xb4, 0x66, 0x3e, 0x04, 0xd0, 0xdc, 0x11, 0x7e, 0x75, 0x41, - 0x15, 0x47, 0x60, 0x51, 0x45, 0x07, 0x5d, 0xf3, 0x36, 0xba, 0x26, 0x06, - 0x8e, 0x1c, 0xba, 0x9e, 0x57, 0x19, 0x0b, 0x18, 0x5e, 0x05, 0x4d, 0x52, - 0x59, 0xb7, 0x19, 0xeb, 0x80, 0x6c, 0x26, 0x7d, 0xa0, 0x14, 0x54, 0x2b, - 0x58, 0xa7, 0x2a, 0xc6, 0x2a, 0xba, 0x60, 0x39, 0x77, 0x59, 0x5d, 0x69, - 0x2a, 0x0e, 0x16, 0x80, 0x7b, 0x32, 0xf5, 0xa4, 0x8a, 0x6a, 0xb3, 0xcb, - 0x02, 0xba, 0xa3, 0x80, 0xff, 0xf1, 0x50, 0x80, 0x38, 0x3f, 0xfc, 0x20, - 0xa9, 0x1a, 0xd4, 0x86, 0x6a, 0x15, 0x42, 0x68, 0x42, 0xb0, 0xc2, 0xfd, - 0xb8, 0xfd, 0xb8, 0xfe, 0x5e, 0xff, 0x6f, 0x8e, 0x3a, 0xfe, 0xdf, 0x1a, - 0xfc, 0xfe, 0xff, 0xb5, 0xe7, 0x9f, 0xae, 0xf7, 0x5d, 0xdd, 0xdd, 0xed, - 0xc6, 0xa5, 0x79, 0xae, 0x82, 0x3b, 0xa3, 0x61, 0x08, 0x3a, 0x21, 0x22, - 0x15, 0x12, 0x50, 0x85, 0x17, 0x90, 0x54, 0x1c, 0xff, 0xb1, 0xf4, 0xea, - 0xe2, 0xaa, 0xf7, 0x90, 0xde, 0x31, 0x11, 0x09, 0x66, 0x72, 0xe7, 0x2c, - 0xbd, 0x40, 0x9e, 0xd1, 0x95, 0x48, 0x22, 0x22, 0x22, 0x06, 0xcb, 0x37, - 0x10, 0x00, 0x0a, 0x22, 0x47, 0x7b, 0xe7, 0xa5, 0x84, 0x00, 0x18, 0x74, - 0xe1, 0xfe, 0xca, 0xce, 0xe7, 0xdb, 0xb4, 0xbc, 0x3f, 0x78, 0xda, 0x07, - 0x9f, 0xd6, 0x1f, 0x10, 0xde, 0xfc, 0xa8, 0x00, 0x77, 0x40, 0xef, 0x45, - 0xbf, 0xd3, 0x09, 0x74, 0x06, 0x30, 0x74, 0x0e, 0x40, 0xdf, 0xf4, 0xfe, - 0x78, 0xdc, 0x74, 0x0e, 0x43, 0x19, 0x3d, 0x3b, 0x76, 0x1c, 0x83, 0x7f, - 0xd3, 0xf1, 0xca, 0x03, 0x51, 0x56, 0x8f, 0x3e, 0x21, 0x87, 0x7b, 0x35, - 0x80, 0x2e, 0x78, 0x3a, 0x73, 0xf0, 0x00, 0x20, 0x00, 0x61, 0x1f, 0xd3, - 0xcf, 0x80, 0x01, 0xfc, 0xff, 0x47, 0x84, 0x10, 0x6c, 0x43, 0x87, 0xe6, - 0xe3, 0xcf, 0xfe, 0x03, 0x40, 0x0c, 0x91, 0x94, 0xec, 0x0a, 0x57, 0x77, - 0xce, 0xc2, 0xa8, 0x0b, 0x7a, 0xd5, 0x9e, 0x75, 0x3e, 0x53, 0xaa, 0x81, - 0x05, 0x9a, 0x7a, 0x5e, 0x75, 0xc2, 0x09, 0x4b, 0x66, 0x7d, 0x33, 0x19, - 0xb4, 0xc9, 0xb4, 0x99, 0x9d, 0xd2, 0x06, 0x9a, 0x73, 0x46, 0xd3, 0x22, - 0x43, 0xad, 0x37, 0x60, 0x03, 0xb4, 0x0b, 0xbe, 0x53, 0xfe, 0xff, 0xf8, - 0x01, 0xc2, 0xef, 0xdc, 0x04, 0x54, 0x1d, 0xdf, 0xcd, 0xf3, 0x48, 0x3f, - 0x0f, 0x03, 0xdd, 0xa5, 0x65, 0x4f, 0xac, 0x8e, 0x85, 0xe4, 0x34, 0xa5, - 0x7d, 0x43, 0xda, 0xdf, 0xa1, 0x89, 0x25, 0x75, 0xc0, 0x0f, 0xf4, 0xc1, - 0x73, 0x68, 0x05, 0xe0, 0xeb, 0xcd, 0x71, 0xe0, 0x2c, 0x05, 0x2d, 0xb1, - 0x4d, 0x34, 0xd7, 0xb5, 0x64, 0xd0, 0x9c, 0x7e, 0xdb, 0xc3, 0x69, 0x7f, - 0xb5, 0xf3, 0x6a, 0xbf, 0xd0, 0xc8, 0x1d, 0xe0, 0xcd, 0xbf, 0x2a, 0xeb, - 0x2e, 0x8a, 0xd0, 0x74, 0xe3, 0xcc, 0x57, 0xe4, 0xf0, 0xfc, 0x33, 0xe1, - 0x48, 0x4d, 0x18, 0x1d, 0xf8, 0xb5, 0xf6, 0xaa, 0xbc, 0xfb, 0x69, 0x4e, - 0x76, 0xe4, 0xad, 0x2b, 0x8e, 0x1d, 0xbd, 0xb3, 0xb3, 0x5d, 0xbf, 0xf7, - 0xfb, 0xe2, 0xf0, 0x92, 0xd3, 0xd7, 0xe7, 0x5d, 0xe5, 0xef, 0xff, 0x56, - 0x26, 0x0f, 0x6c, 0x26, 0xd7, 0x94, 0x64, 0xba, 0x06, 0x79, 0xb1, 0xcf, - 0xa8, 0x1e, 0xf9, 0x6f, 0xef, 0xb9, 0xb2, 0x36, 0x79, 0x9b, 0x15, 0xe5, - 0xdf, 0x46, 0x57, 0x27, 0x0b, 0xa8, 0xb2, 0x5b, 0x7c, 0x1a, 0x5b, 0x53, - 0x2d, 0xf1, 0x8f, 0x86, 0xcf, 0x2f, 0xc7, 0x7f, 0x97, 0xab, 0xe7, 0x34, - 0xb7, 0x78, 0x46, 0x13, 0xe5, 0x39, 0x6d, 0x1f, 0x6e, 0xef, 0xb0, 0x7d, - 0xfc, 0x33, 0x20, 0x84, 0x22, 0xc7, 0x1e, 0xbf, 0xf2, 0x90, 0xb2, 0xab, - 0x0a, 0xee, 0xa9, 0xe2, 0xab, 0x6c, 0xb8, 0xab, 0x3e, 0xff, 0xf1, 0x50, - 0x80, 0x35, 0x3f, 0xfc, 0x20, 0xa8, 0x1a, 0xd6, 0x28, 0x33, 0x25, 0xc6, - 0x83, 0x26, 0x21, 0x98, 0x40, 0x1c, 0x19, 0x9c, 0x67, 0x5e, 0xad, 0x9c, - 0x72, 0x71, 0x99, 0xe7, 0x29, 0x7b, 0xd5, 0x7c, 0x57, 0x1c, 0xdd, 0xfc, - 0x6c, 0x30, 0x67, 0xf9, 0x58, 0x11, 0x18, 0x8a, 0xab, 0x9e, 0xba, 0x43, - 0x80, 0x22, 0xf8, 0x37, 0x2f, 0x7c, 0xa4, 0x03, 0x39, 0xc0, 0xb8, 0xc6, - 0xdf, 0x38, 0xd8, 0x00, 0x26, 0x66, 0x96, 0xc5, 0x44, 0x7f, 0x3a, 0x8d, - 0x71, 0x11, 0x22, 0x72, 0xae, 0xc3, 0xbe, 0xca, 0xd8, 0xdb, 0x47, 0x6a, - 0x94, 0x1f, 0xef, 0x55, 0xc2, 0xb3, 0x42, 0x30, 0x32, 0x06, 0x0e, 0xe6, - 0x5a, 0x88, 0xaa, 0x94, 0x60, 0x2b, 0xbe, 0xd6, 0x20, 0x09, 0xfc, 0x3a, - 0xb8, 0xbb, 0x28, 0xc8, 0x21, 0x68, 0xf0, 0x29, 0xdc, 0xc4, 0x7c, 0xc7, - 0x49, 0x2d, 0x21, 0x80, 0x7c, 0x1b, 0xb7, 0xb5, 0x2e, 0x07, 0xdb, 0x01, - 0xdd, 0x01, 0xa5, 0x9f, 0xe0, 0xd0, 0xbe, 0xde, 0x72, 0x03, 0x40, 0xf8, - 0x30, 0x60, 0x03, 0x50, 0x98, 0x5a, 0x2f, 0xa5, 0x2c, 0x6f, 0xba, 0x6b, - 0x55, 0x82, 0xaa, 0x26, 0xa2, 0x8b, 0x4d, 0x6c, 0xba, 0xd7, 0x62, 0xe5, - 0x3a, 0x97, 0x42, 0xa8, 0x29, 0x63, 0x7f, 0x04, 0xb0, 0x50, 0x5f, 0x8a, - 0xb8, 0xa5, 0xbe, 0xba, 0xd7, 0x20, 0xbf, 0xca, 0xfe, 0xaa, 0x28, 0x53, - 0x58, 0xa6, 0x6b, 0xbb, 0x77, 0xda, 0xfa, 0x69, 0xbf, 0x2e, 0xe9, 0x1a, - 0xee, 0xdd, 0x58, 0x3b, 0x63, 0x8a, 0x0d, 0x6a, 0xcd, 0x8f, 0x5f, 0x5c, - 0xc0, 0x18, 0x55, 0x3d, 0x6a, 0x42, 0x20, 0xa0, 0x81, 0x23, 0x31, 0x0f, - 0x6f, 0x4e, 0x37, 0x4e, 0xb9, 0xeb, 0xc7, 0xb7, 0xae, 0xb6, 0x9b, 0xd6, - 0xef, 0x0e, 0x1b, 0xae, 0x38, 0xe3, 0xc7, 0x8f, 0x6d, 0xfb, 0x57, 0x5a, - 0xf1, 0xf8, 0x02, 0x71, 0x7f, 0xb6, 0xae, 0x9a, 0x01, 0xb9, 0x68, 0x14, - 0xed, 0x00, 0x42, 0x49, 0xa0, 0x9a, 0xde, 0xd2, 0xc0, 0x35, 0x15, 0xfd, - 0xdf, 0x9c, 0xf1, 0xf2, 0x0d, 0xf5, 0x74, 0x6b, 0x4b, 0x27, 0xcb, 0xf5, - 0x47, 0x4f, 0xa6, 0x2d, 0xb9, 0x23, 0xee, 0x21, 0xcf, 0x43, 0xd9, 0xa5, - 0xd2, 0xdb, 0xc4, 0x2d, 0x38, 0xe6, 0x0b, 0x7d, 0x91, 0x18, 0x07, 0xe4, - 0x5f, 0xcf, 0x68, 0x7d, 0x7d, 0x4a, 0xc5, 0xf7, 0xd3, 0x30, 0xcc, 0x12, - 0x0e, 0x60, 0x70, 0xf8, 0x3f, 0xc2, 0x49, 0x38, 0x83, 0x99, 0xbe, 0x09, - 0x4f, 0xbc, 0xb9, 0x11, 0x40, 0x57, 0xde, 0x93, 0x52, 0x2b, 0x0b, 0xb6, - 0xb7, 0xf8, 0x7c, 0x3b, 0x09, 0xc0, 0x3e, 0x64, 0x90, 0x4c, 0x39, 0xe2, - 0x50, 0xcb, 0x41, 0xcf, 0x07, 0x36, 0x6c, 0xe4, 0x00, 0xcf, 0x93, 0x77, - 0xba, 0x33, 0xf3, 0x7c, 0x7a, 0xb3, 0xe3, 0x3f, 0xcb, 0x34, 0x28, 0x6a, - 0xd4, 0x10, 0xd3, 0x4b, 0x20, 0x05, 0x28, 0x76, 0xc8, 0x78, 0xc0, 0x33, - 0x4b, 0x4d, 0x16, 0x62, 0xc4, 0xac, 0x6c, 0xc1, 0x5b, 0xb3, 0x23, 0xb9, - 0xb2, 0x16, 0xae, 0xb2, 0x7f, 0x25, 0xf7, 0x77, 0x0d, 0x93, 0x6c, 0x79, - 0xa8, 0x2e, 0xff, 0xf1, 0x50, 0x80, 0x24, 0x9f, 0xfc, 0x21, 0x1a, 0xc9, - 0x73, 0x4e, 0xfe, 0x16, 0x02, 0xa4, 0xb4, 0x33, 0x10, 0xa2, 0x84, 0x33, - 0x10, 0x00, 0x4a, 0x00, 0x00, 0xe3, 0x34, 0xd6, 0xae, 0x8f, 0x37, 0x02, - 0xf8, 0xfe, 0x7f, 0x4a, 0xd7, 0x4f, 0xb8, 0x13, 0x14, 0x45, 0x7b, 0x8c, - 0xc3, 0xef, 0x48, 0xb9, 0xeb, 0xc5, 0xe8, 0x20, 0x06, 0x47, 0x83, 0x00, - 0x71, 0xbd, 0xf6, 0x25, 0x26, 0x10, 0x66, 0x04, 0xae, 0xef, 0xfe, 0x28, - 0x89, 0xb0, 0x92, 0x1b, 0xc5, 0xe1, 0x1e, 0xaf, 0xbb, 0x00, 0x9b, 0x55, - 0xc5, 0xe5, 0xc7, 0x38, 0xee, 0x38, 0x32, 0x64, 0x5a, 0x42, 0x8e, 0x6c, - 0xba, 0xad, 0x34, 0x4c, 0xf4, 0xb7, 0xfe, 0x55, 0x5b, 0x46, 0x8c, 0x7b, - 0x60, 0x9d, 0x08, 0x1a, 0xda, 0x1d, 0x0d, 0x37, 0x97, 0x1b, 0xc6, 0xd1, - 0xd3, 0xed, 0x94, 0x85, 0xbb, 0x4d, 0x97, 0xf0, 0xae, 0xad, 0x6f, 0x56, - 0xf4, 0x3c, 0x73, 0x55, 0xf5, 0x62, 0xd2, 0x57, 0xa2, 0xf7, 0x81, 0x44, - 0xf4, 0x03, 0xb6, 0x10, 0xaf, 0x5c, 0x87, 0xae, 0x06, 0xe5, 0xa3, 0xce, - 0x8a, 0xe0, 0x56, 0x8e, 0xe9, 0xe9, 0x0c, 0x0e, 0xd8, 0x71, 0xc6, 0x8f, - 0x31, 0x48, 0x17, 0x21, 0xc3, 0xdd, 0x97, 0x60, 0x2d, 0x8c, 0x9e, 0xc1, - 0x69, 0x0a, 0xaa, 0xc0, 0xd2, 0x97, 0xb1, 0x36, 0x4b, 0x57, 0x37, 0x25, - 0x01, 0xac, 0xea, 0xec, 0xb5, 0xa5, 0x8b, 0xd1, 0x1e, 0x7e, 0x1b, 0xf1, - 0xae, 0xf9, 0x20, 0x27, 0x59, 0xcb, 0x4a, 0x03, 0xa5, 0x7e, 0x0a, 0x06, - 0x0b, 0x24, 0x8c, 0x14, 0x96, 0x28, 0x5a, 0x9c, 0x4d, 0xf1, 0x00, 0x00, - 0x00, 0x24, 0xcb, 0xdc, 0xcd, 0x1b, 0x95, 0x2a, 0x58, 0xcb, 0x1f, 0xf7, - 0xa7, 0xbc, 0xa2, 0xd0, 0x2f, 0x75, 0x46, 0x71, 0x08, 0x00, 0xff, 0x9f, - 0xcc, 0x03, 0x48, 0xcb, 0x72, 0x85, 0x02, 0xdc, 0x88, 0x37, 0xbc, 0x31, - 0xc5, 0x65, 0xcd, 0x14, 0xac, 0x39, 0xff, 0xf6, 0x45, 0xd4, 0x46, 0x64, - 0xb7, 0x0c, 0x81, 0xab, 0x85, 0xbe, 0x2d, 0xee, 0x92, 0xed, 0x81, 0xae, - 0x99, 0xe7, 0x1d, 0xa1, 0xd2, 0xae, 0xff, 0xf1, 0x50, 0x80, 0x26, 0x1f, - 0xfc, 0x21, 0x1a, 0xcd, 0xda, 0xf3, 0xfe, 0xef, 0xff, 0xa2, 0xb4, 0xa0, - 0xe5, 0x04, 0x33, 0x5a, 0x11, 0x8a, 0xa8, 0x4a, 0x54, 0xa8, 0x29, 0x00, - 0x2a, 0x6b, 0x5a, 0xd5, 0xfa, 0xf8, 0xe7, 0x3c, 0xf5, 0x2a, 0x03, 0x6b, - 0x8d, 0xf0, 0x8d, 0x2d, 0xae, 0xac, 0xb5, 0x8c, 0xc0, 0x59, 0xc3, 0x4a, - 0x10, 0xb7, 0x25, 0x0c, 0x07, 0x59, 0x40, 0xd2, 0xdc, 0x11, 0xc7, 0xe7, - 0x5b, 0xed, 0xd9, 0xed, 0xb9, 0xca, 0x58, 0xe4, 0xca, 0x5b, 0x3f, 0x28, - 0xe9, 0x62, 0xb1, 0xc0, 0x83, 0x2b, 0x63, 0xf1, 0x4b, 0xc6, 0x2b, 0x06, - 0x87, 0x42, 0x8f, 0xf0, 0xdd, 0x79, 0x0c, 0xfe, 0x87, 0xf3, 0x78, 0xb2, - 0xcf, 0x97, 0x0d, 0x4a, 0xd3, 0x5e, 0xf1, 0x3e, 0x52, 0x02, 0x2c, 0xb2, - 0xc0, 0x19, 0xb9, 0xb2, 0xb9, 0x7b, 0x4f, 0x9b, 0x89, 0x0b, 0x32, 0xf8, - 0x35, 0xca, 0xe0, 0xd6, 0xde, 0xf9, 0xd6, 0xea, 0x5a, 0xdd, 0x0f, 0x42, - 0x35, 0xfc, 0xad, 0xd5, 0xb2, 0xe1, 0xd2, 0x3c, 0x96, 0xe8, 0x27, 0xe1, - 0x6e, 0xe5, 0x1a, 0x49, 0x78, 0x2a, 0x9f, 0x33, 0x53, 0xb1, 0x6e, 0x11, - 0x09, 0xde, 0xd6, 0xd5, 0x8e, 0xf4, 0xab, 0x0e, 0x9f, 0x0b, 0x02, 0xb6, - 0x5d, 0x6b, 0xa0, 0xf2, 0x49, 0x92, 0xa0, 0x59, 0xa6, 0x61, 0xb8, 0x5f, - 0x2a, 0xcd, 0xe4, 0x59, 0xad, 0x64, 0x90, 0x77, 0xdd, 0x5e, 0x32, 0xbc, - 0x14, 0xc7, 0x67, 0x21, 0xaa, 0x6b, 0xf1, 0xeb, 0x92, 0x1a, 0x70, 0x1e, - 0x5b, 0xcf, 0x46, 0x16, 0xa9, 0x04, 0x54, 0xb3, 0x71, 0xbc, 0x1e, 0x6e, - 0x65, 0x79, 0xcd, 0x4c, 0x93, 0x45, 0x2b, 0x45, 0x15, 0x92, 0x60, 0x66, - 0xe8, 0x80, 0x00, 0x00, 0x15, 0x5c, 0x6e, 0xb5, 0xae, 0xf5, 0xce, 0x4d, - 0x5f, 0x32, 0xc1, 0x8e, 0x0e, 0xa1, 0xe3, 0xe9, 0xab, 0x60, 0xe0, 0xd5, - 0x57, 0xa1, 0xb3, 0x0c, 0xfd, 0x4f, 0x85, 0x5a, 0x31, 0xb3, 0x7c, 0xe8, - 0x8d, 0x37, 0x2f, 0xef, 0xd9, 0x4c, 0x5a, 0x65, 0x9e, 0x53, 0x59, 0xf1, - 0xae, 0x33, 0x93, 0x26, 0x52, 0x0e, 0x82, 0x22, 0x88, 0xd0, 0xcd, 0xd1, - 0xa7, 0x44, 0x10, 0xf4, 0x50, 0x24, 0x66, 0xb9, 0x41, 0xc0, 0xff, 0xf1, - 0x50, 0x80, 0x29, 0x5f, 0xfc, 0x21, 0x1a, 0xcf, 0x67, 0xfb, 0x3d, 0x58, - 0x00, 0xa2, 0xb4, 0x90, 0xe5, 0x06, 0xa5, 0x3c, 0x05, 0x8a, 0x00, 0x0b, - 0xba, 0xd8, 0xf8, 0xf7, 0xbc, 0x7e, 0xbf, 0xa5, 0xf1, 0x9c, 0x79, 0xca, - 0x9c, 0x7a, 0xfd, 0x40, 0x05, 0xc1, 0x43, 0x21, 0x6b, 0xed, 0x64, 0x85, - 0x62, 0x7b, 0x78, 0x44, 0x81, 0x5c, 0xe0, 0xbc, 0x87, 0xbe, 0x12, 0x0b, - 0x17, 0xcd, 0xa9, 0xb9, 0x97, 0xda, 0x68, 0x32, 0xbc, 0x59, 0x63, 0x59, - 0x27, 0x0d, 0x6f, 0xf1, 0x5e, 0xd1, 0xa1, 0x36, 0xaa, 0xb2, 0xaa, 0xeb, - 0xee, 0xdf, 0x54, 0xc2, 0x0f, 0x0e, 0x3c, 0x89, 0x4f, 0xe0, 0x4b, 0x47, - 0xa5, 0x69, 0x1e, 0xf3, 0xba, 0x48, 0xbd, 0x3b, 0x5f, 0xa5, 0xf1, 0x74, - 0xae, 0x95, 0xbf, 0xd7, 0xc4, 0x06, 0xac, 0x52, 0x1e, 0x67, 0xa6, 0x01, - 0xbd, 0xd0, 0x80, 0x99, 0x38, 0xf1, 0xdc, 0xa4, 0xa3, 0x74, 0xd7, 0x7f, - 0x4f, 0x4f, 0xb3, 0x6b, 0x5e, 0x72, 0xba, 0x9f, 0x9d, 0xbe, 0x4c, 0xc6, - 0x43, 0x3a, 0xab, 0xe7, 0xd3, 0xd6, 0x98, 0x90, 0xa8, 0x99, 0xdf, 0x77, - 0x3e, 0x8c, 0xf4, 0x11, 0x52, 0x33, 0xee, 0xaf, 0x77, 0x55, 0xda, 0x33, - 0x3b, 0x57, 0xc2, 0x22, 0xa3, 0x1f, 0x2d, 0x40, 0x02, 0x92, 0x98, 0x41, - 0xf0, 0x90, 0x42, 0x06, 0x18, 0x1f, 0x92, 0x0c, 0xa6, 0xed, 0xb3, 0x30, - 0xb1, 0x13, 0x4b, 0x3d, 0x9b, 0xd0, 0xa6, 0x6d, 0xff, 0x3a, 0x10, 0x25, - 0x68, 0x86, 0xb5, 0xe5, 0xba, 0xbc, 0xf3, 0xf4, 0xdf, 0x50, 0xa6, 0xba, - 0x94, 0x0d, 0xe8, 0xa7, 0xab, 0x6c, 0xbd, 0x25, 0x38, 0xc8, 0x47, 0x7d, - 0xb6, 0x3d, 0x88, 0xd6, 0xef, 0x40, 0xa9, 0x91, 0xea, 0x5d, 0x51, 0x34, - 0x89, 0x9d, 0x14, 0x20, 0x89, 0x61, 0x55, 0x98, 0xfd, 0x2c, 0x1f, 0x30, - 0x00, 0x2e, 0xeb, 0x63, 0x5b, 0xbc, 0x6b, 0x78, 0xa4, 0x29, 0xfe, 0xf7, - 0x2b, 0x24, 0x0a, 0xb2, 0xbe, 0x85, 0x59, 0x2f, 0x6e, 0xce, 0x30, 0x85, - 0xe2, 0x69, 0xfe, 0xef, 0x62, 0x69, 0xf9, 0x3d, 0x41, 0x2e, 0xa1, 0x7d, - 0x88, 0xdc, 0x65, 0x14, 0xd7, 0x1f, 0xe7, 0xda, 0xa0, 0x03, 0x3f, 0x57, - 0xcf, 0x24, 0x6e, 0xe0, 0xc0, 0xe7, 0x12, 0x25, 0x60, 0x6b, 0xd5, 0xd0, - 0x00, 0x82, 0x17, 0x51, 0xe0, 0xf2, 0x01, 0x91, 0x44, 0x2c, 0x88, 0xd6, - 0x26, 0x80, 0xf5, 0x87, 0xff, 0xf1, 0x50, 0x80, 0x2d, 0x7f, 0xfc, 0x21, - 0x1a, 0xcf, 0x84, 0xf9, 0x7f, 0x74, 0x12, 0xa0, 0xb4, 0x21, 0x20, 0x72, - 0x55, 0x8b, 0x18, 0x0b, 0xcb, 0xa8, 0xc9, 0xd6, 0x73, 0xf1, 0xde, 0xfc, - 0xf8, 0xf8, 0xf7, 0xbf, 0x3d, 0xe4, 0xf8, 0xf1, 0xf0, 0xf5, 0x3d, 0xbe, - 0x7f, 0xdf, 0xe7, 0x49, 0xd0, 0x6f, 0xfd, 0x89, 0x3d, 0xf3, 0xb1, 0x67, - 0x85, 0x78, 0x8b, 0xfd, 0xc2, 0x99, 0x00, 0xab, 0xb8, 0x61, 0x12, 0x93, - 0x66, 0xe7, 0xe0, 0x15, 0xc0, 0x2e, 0x1d, 0x03, 0x7e, 0x0f, 0x0e, 0x13, - 0x24, 0x20, 0xb1, 0x9c, 0xd4, 0xe1, 0xe6, 0x3f, 0x53, 0xe3, 0xe2, 0xcf, - 0x47, 0x8f, 0x84, 0x93, 0x78, 0x79, 0xcf, 0xed, 0x7e, 0xc1, 0x81, 0x75, - 0xc6, 0x27, 0x16, 0x5c, 0x7f, 0x59, 0xbd, 0xeb, 0x3b, 0xd2, 0xb8, 0x27, - 0x3a, 0xf8, 0x4a, 0x02, 0x45, 0x63, 0x3b, 0xe9, 0xb9, 0xab, 0x81, 0xbc, - 0x6a, 0xd3, 0x32, 0x02, 0x17, 0x6b, 0xf9, 0xf4, 0x00, 0xb0, 0x9e, 0xfe, - 0xb9, 0x08, 0x40, 0x7b, 0xeb, 0xa8, 0x2e, 0xc0, 0xa7, 0x66, 0x00, 0x9b, - 0xdc, 0x3b, 0xda, 0xfa, 0xf3, 0x76, 0x59, 0x92, 0xa7, 0x9f, 0xb7, 0xa2, - 0x12, 0xc2, 0x40, 0xcf, 0x56, 0xa0, 0x01, 0x4c, 0x7c, 0xe0, 0x50, 0x1a, - 0xae, 0xfd, 0x7e, 0x31, 0xbb, 0x8a, 0x8b, 0x02, 0x7a, 0xe0, 0x01, 0xa3, - 0xc0, 0xf5, 0xff, 0xdd, 0xbe, 0x20, 0xc9, 0x96, 0x34, 0xee, 0x0d, 0x80, - 0x25, 0x6e, 0xac, 0x90, 0x1a, 0xd0, 0x7a, 0x80, 0xc1, 0x0b, 0x97, 0x8f, - 0x54, 0x2d, 0x89, 0x28, 0x4a, 0x2c, 0xf3, 0x49, 0x36, 0xbf, 0x9c, 0x10, - 0x2c, 0xc0, 0xc3, 0x0c, 0xe2, 0x0b, 0xbb, 0x9d, 0x51, 0x08, 0xf1, 0x5a, - 0xeb, 0x03, 0x97, 0xd3, 0xff, 0x34, 0x77, 0x49, 0xd6, 0xcb, 0xad, 0x77, - 0xf6, 0x34, 0xcd, 0x35, 0x03, 0x1e, 0xc2, 0xc4, 0xb0, 0xba, 0xcc, 0x7e, - 0x96, 0x0f, 0x98, 0x12, 0xf7, 0x6b, 0xcc, 0x9d, 0x67, 0x3a, 0x6f, 0xcf, - 0x8f, 0x8f, 0x7b, 0xf3, 0xdf, 0xa7, 0xd7, 0x8b, 0xbc, 0xdd, 0xfe, 0xfd, - 0x7a, 0x8b, 0xb0, 0x9e, 0x6d, 0x3a, 0xed, 0x42, 0x6d, 0x9c, 0x3c, 0x2f, - 0x19, 0x5a, 0x2d, 0x40, 0x77, 0x88, 0xe0, 0x26, 0x56, 0xa3, 0x40, 0x83, - 0x17, 0x93, 0x24, 0xaa, 0x15, 0x59, 0x98, 0xe5, 0x9f, 0xc6, 0xfd, 0xf9, - 0x56, 0x18, 0x67, 0x8e, 0x19, 0x2e, 0xaa, 0x3e, 0xfb, 0x90, 0x84, 0xe4, - 0x84, 0x88, 0xd0, 0xf4, 0x10, 0xc9, 0x95, 0x44, 0x45, 0x98, 0x4f, 0xa6, - 0xd0, 0x00, 0xf3, 0x5e, 0x25, 0x97, 0x1f, 0xd9, 0x41, 0xaf, 0x9c, 0xec, - 0xdd, 0x3c, 0x01, 0x3c, 0xc4, 0x00, 0xfc, 0xff, 0xf1, 0x50, 0x80, 0x2d, - 0x9f, 0xfc, 0x21, 0x1a, 0xcd, 0x7f, 0xfb, 0xff, 0x01, 0x00, 0x9b, 0xb8, - 0x90, 0xac, 0x2e, 0x25, 0x7c, 0x11, 0x83, 0x41, 0x54, 0xa8, 0x00, 0x95, - 0x5a, 0xc7, 0x1c, 0xf9, 0xf0, 0x5e, 0xd7, 0x3e, 0xbd, 0x77, 0xe7, 0x5f, - 0x3f, 0xe9, 0xbd, 0x4e, 0x7e, 0xe2, 0x2a, 0x4c, 0x23, 0xa0, 0x53, 0xeb, - 0x6e, 0x6e, 0xe8, 0x6e, 0xf1, 0xf3, 0x15, 0x3c, 0x99, 0xc6, 0x91, 0x83, - 0x49, 0x60, 0x52, 0xde, 0x11, 0x31, 0x0c, 0x05, 0x23, 0x34, 0x40, 0x11, - 0x98, 0xd8, 0xcf, 0x47, 0xd4, 0xa7, 0x0f, 0x88, 0x50, 0xe6, 0x82, 0x36, - 0x63, 0x1d, 0x04, 0x53, 0x19, 0x21, 0x8d, 0x43, 0x25, 0xf6, 0xe1, 0x3f, - 0xc3, 0x9f, 0x82, 0xef, 0x85, 0x53, 0x4b, 0xa6, 0xa6, 0x77, 0x0b, 0xaf, - 0x2d, 0xff, 0x97, 0x1c, 0x39, 0xe7, 0x96, 0xd3, 0xae, 0x08, 0x63, 0xeb, - 0xfc, 0xca, 0x0a, 0x6b, 0x12, 0xe8, 0xe8, 0x54, 0x00, 0x33, 0xcf, 0xca, - 0x69, 0x2a, 0x92, 0xe0, 0x4f, 0xb0, 0x04, 0x89, 0x9c, 0x79, 0x78, 0x80, - 0xa0, 0xb9, 0xe8, 0x00, 0x85, 0xac, 0xe0, 0x2d, 0x57, 0x13, 0x33, 0x8d, - 0xfe, 0x1d, 0x7b, 0x2f, 0x72, 0x02, 0x3b, 0xf8, 0x00, 0x02, 0xfb, 0x00, - 0x29, 0x25, 0xfd, 0x70, 0x92, 0x4d, 0xce, 0xd2, 0xe8, 0xfb, 0x25, 0x78, - 0x85, 0x6a, 0xc5, 0x47, 0x58, 0x01, 0x19, 0xf1, 0x84, 0x5b, 0xb1, 0x5f, - 0xa2, 0x24, 0x0c, 0xd8, 0xe4, 0x08, 0xea, 0x1b, 0x95, 0xa8, 0x10, 0xd4, - 0x68, 0x41, 0x02, 0x68, 0x19, 0xa4, 0x90, 0x41, 0x1e, 0xfc, 0x70, 0x0d, - 0x26, 0xdb, 0x25, 0x9c, 0x28, 0x24, 0x0c, 0x26, 0xad, 0x69, 0x2a, 0x0c, - 0x0a, 0x04, 0x53, 0xaf, 0x39, 0x4b, 0x4c, 0xb8, 0x70, 0x24, 0x73, 0xae, - 0x19, 0x9a, 0xd1, 0x5c, 0x6a, 0x8e, 0x32, 0xa0, 0xc0, 0x6e, 0xf7, 0x26, - 0xf8, 0x86, 0x2c, 0xa2, 0xc5, 0x4f, 0x37, 0x6d, 0x97, 0x7c, 0x00, 0x00, - 0x94, 0xbc, 0x71, 0xcf, 0x9f, 0x05, 0xef, 0x9f, 0xae, 0x78, 0xae, 0x4e, - 0x7a, 0xcd, 0x32, 0xc1, 0x51, 0xc1, 0x11, 0x8b, 0xea, 0xea, 0x87, 0xa0, - 0x3d, 0x0c, 0x22, 0x90, 0xae, 0x21, 0xd0, 0x44, 0x89, 0x00, 0xb3, 0x80, - 0x30, 0xf9, 0xe0, 0x30, 0xf9, 0xa3, 0x0a, 0x3a, 0x14, 0x15, 0xd1, 0x68, - 0x50, 0x70, 0x2d, 0x19, 0xe4, 0xce, 0xd5, 0x3b, 0x3f, 0x68, 0xfd, 0x83, - 0x2c, 0xa1, 0x18, 0xe1, 0x24, 0x5b, 0x7f, 0x6b, 0xeb, 0x88, 0x4c, 0x0c, - 0xc6, 0xbf, 0x86, 0xf9, 0x4d, 0xaa, 0x2a, 0x98, 0x0a, 0xef, 0x35, 0x7e, - 0x0f, 0xe9, 0xa0, 0x00, 0x35, 0x64, 0x00, 0x19, 0x72, 0x24, 0x07, 0xff, - 0xf1, 0x50, 0x80, 0x2f, 0xbf, 0xfc, 0x21, 0x1a, 0xcf, 0xfb, 0xfc, 0x7f, - 0x44, 0x00, 0x9c, 0xb4, 0x42, 0x2c, 0x2c, 0x2b, 0x0c, 0x89, 0x5f, 0x06, - 0x00, 0x6b, 0x63, 0xf7, 0xfd, 0x97, 0xc7, 0x89, 0x5c, 0x72, 0xe3, 0x74, - 0xba, 0x94, 0x9c, 0x77, 0xfe, 0x9a, 0xe7, 0xd7, 0x5f, 0x42, 0x44, 0xc8, - 0x64, 0x99, 0x03, 0xcc, 0x32, 0xd8, 0x2a, 0xe7, 0xbd, 0xc6, 0x8f, 0x4f, - 0x63, 0x4a, 0x84, 0xcc, 0x36, 0xec, 0x5c, 0x5b, 0xc9, 0xa0, 0x9c, 0x10, - 0xa8, 0x24, 0x00, 0x63, 0x87, 0x1b, 0xdc, 0x32, 0xdc, 0xeb, 0x7b, 0x85, - 0x51, 0x70, 0x06, 0x81, 0x8e, 0xcf, 0x31, 0x6c, 0x89, 0xa4, 0xc2, 0x7a, - 0x8c, 0x88, 0x49, 0x0c, 0xa6, 0x68, 0xec, 0x0b, 0x32, 0xaf, 0x3b, 0xac, - 0x65, 0xaf, 0xa9, 0xd7, 0x7a, 0x9c, 0xd6, 0xe8, 0x8d, 0xee, 0x5a, 0x75, - 0xfe, 0xf9, 0x21, 0x20, 0x56, 0xb9, 0xe6, 0x26, 0xe6, 0x0a, 0x8d, 0x6c, - 0xd6, 0x00, 0x22, 0xa2, 0xb5, 0xae, 0xaf, 0x8c, 0x82, 0x82, 0xb6, 0xfe, - 0x04, 0x46, 0x94, 0x45, 0xc4, 0xe9, 0x95, 0xa6, 0xa4, 0xd2, 0x4b, 0xe1, - 0x11, 0xbb, 0x89, 0x85, 0xd2, 0xb7, 0x58, 0xc5, 0x4e, 0xaa, 0xe0, 0x1a, - 0xbd, 0x70, 0x80, 0x00, 0x73, 0xed, 0x49, 0x00, 0xa5, 0x6f, 0x5b, 0xb8, - 0xbd, 0x09, 0x8d, 0xca, 0x73, 0xdb, 0xa6, 0x97, 0xa6, 0x14, 0x6b, 0xba, - 0xe4, 0x0d, 0x84, 0xf3, 0x68, 0x35, 0x6b, 0x66, 0x5b, 0x05, 0xca, 0x0a, - 0x44, 0x45, 0x2d, 0x3d, 0x7a, 0x59, 0x75, 0x49, 0xdd, 0x94, 0x7b, 0x2b, - 0x80, 0x4a, 0xa8, 0x4e, 0xe1, 0x34, 0x9e, 0x4f, 0x41, 0x08, 0xa6, 0x12, - 0x91, 0x4b, 0x7d, 0xb9, 0x63, 0x72, 0x9c, 0xc1, 0x4a, 0x08, 0x0c, 0x23, - 0x08, 0xe3, 0x0b, 0xa0, 0x21, 0xa1, 0xbd, 0x12, 0x04, 0x58, 0x21, 0xb0, - 0x92, 0x13, 0x13, 0x77, 0xd9, 0x62, 0xc7, 0xa3, 0x97, 0x51, 0x91, 0x05, - 0x0e, 0x4e, 0x44, 0x3b, 0xbe, 0x6c, 0xf9, 0xcb, 0x49, 0x0e, 0xca, 0x42, - 0x94, 0x7c, 0x00, 0x37, 0xe7, 0x63, 0xf7, 0xfd, 0x98, 0xae, 0x2b, 0x8e, - 0x5c, 0x6f, 0x9a, 0xf8, 0xe6, 0xeb, 0x38, 0x7a, 0xf8, 0x6f, 0x77, 0x60, - 0x7f, 0x47, 0xdb, 0x12, 0x34, 0x1d, 0x40, 0x8c, 0xb4, 0x65, 0xae, 0x04, - 0x5a, 0xc2, 0xe9, 0x0b, 0x41, 0xb6, 0xa7, 0x37, 0x5c, 0x06, 0xa0, 0x9f, - 0x83, 0x42, 0x81, 0xe0, 0xc0, 0x1c, 0xa4, 0xeb, 0x0c, 0x85, 0x18, 0x07, - 0x35, 0xd3, 0xce, 0x07, 0x76, 0x4d, 0xb4, 0x6a, 0xee, 0x9f, 0x73, 0xd1, - 0xa8, 0x9b, 0xcd, 0x2a, 0x8a, 0xc3, 0x93, 0xcb, 0xfa, 0xf4, 0xa6, 0xa2, - 0x70, 0x22, 0xeb, 0x5b, 0x8b, 0xf9, 0x1d, 0xe0, 0x16, 0x17, 0x72, 0x90, - 0xba, 0xc5, 0x31, 0xaf, 0xd3, 0x44, 0x29, 0x27, 0xff, 0xf1, 0x50, 0x80, - 0x2f, 0x7f, 0xfc, 0x21, 0x1a, 0xcf, 0xc8, 0xec, 0x5f, 0x42, 0x08, 0x9a, - 0xb6, 0xa0, 0xec, 0x28, 0x2b, 0x1c, 0x89, 0x4a, 0x68, 0xa0, 0xb1, 0x20, - 0x20, 0x0e, 0xbb, 0x12, 0x9a, 0xda, 0xf0, 0x00, 0x1c, 0x73, 0xff, 0x97, - 0xe9, 0x92, 0xfe, 0x86, 0xaf, 0x26, 0xc5, 0x65, 0xfb, 0x40, 0x74, 0x10, - 0x72, 0x53, 0x4d, 0xe3, 0xb6, 0xba, 0x86, 0x40, 0x71, 0x1d, 0x70, 0x09, - 0x3d, 0x2e, 0x50, 0x59, 0x28, 0xe9, 0xab, 0xcd, 0x28, 0x71, 0xd3, 0x25, - 0x6b, 0xb4, 0xd2, 0xf9, 0x40, 0x11, 0x0e, 0xcb, 0x7f, 0x34, 0x1c, 0xfd, - 0xe3, 0xaa, 0xbc, 0x16, 0x3d, 0xcb, 0xd7, 0xe7, 0x3d, 0xa7, 0x26, 0x2b, - 0x55, 0x1e, 0x18, 0x00, 0x01, 0x7a, 0xf3, 0x06, 0x2d, 0x8d, 0x4f, 0xa7, - 0xe1, 0x05, 0x19, 0x9a, 0xd8, 0x5f, 0x0d, 0x35, 0xa9, 0xf8, 0x0f, 0x72, - 0xc2, 0x99, 0xe4, 0x12, 0x89, 0xd6, 0x1a, 0xdd, 0x88, 0x90, 0xeb, 0xff, - 0xe7, 0x18, 0x80, 0x54, 0xa5, 0x9e, 0xaf, 0x6e, 0x30, 0xb8, 0x5d, 0xc1, - 0x11, 0xfc, 0x7e, 0xa5, 0xee, 0x33, 0x89, 0xb9, 0x6f, 0xa3, 0x72, 0xcd, - 0xb6, 0xcf, 0xbf, 0xf8, 0xc2, 0x65, 0x30, 0xfa, 0xfa, 0x01, 0xaf, 0xf8, - 0xbe, 0xbb, 0x6d, 0x88, 0x6e, 0x3c, 0x62, 0x04, 0x3a, 0xe8, 0x96, 0x31, - 0xf3, 0xfc, 0x8f, 0x35, 0xd7, 0x4e, 0x33, 0xde, 0x61, 0x48, 0x9c, 0x90, - 0x0b, 0x3f, 0x81, 0x93, 0xbc, 0x37, 0x48, 0x1a, 0xbb, 0x64, 0x9d, 0xc8, - 0x8b, 0x3d, 0x9d, 0x20, 0x82, 0xb7, 0xa8, 0x01, 0x6e, 0x61, 0x6d, 0x29, - 0x46, 0xa7, 0x01, 0x04, 0x72, 0x20, 0x08, 0x68, 0xcf, 0x2e, 0xd7, 0xb4, - 0x90, 0x84, 0x18, 0x4d, 0x18, 0x3a, 0xe4, 0x81, 0xc1, 0x6f, 0x13, 0xbe, - 0x2f, 0x8b, 0x90, 0x3d, 0x7d, 0xcd, 0x95, 0xbe, 0x1b, 0x33, 0xd9, 0x51, - 0x0a, 0x05, 0xcf, 0xa8, 0x1f, 0x5e, 0x6a, 0xda, 0xc3, 0xb1, 0x4a, 0x7e, - 0x00, 0xa4, 0xa2, 0x15, 0x14, 0x6b, 0x70, 0x37, 0x5e, 0xdc, 0xca, 0x47, - 0x3e, 0xd9, 0xb2, 0xc4, 0xe7, 0xe8, 0x62, 0x7c, 0x7b, 0x39, 0x51, 0x46, - 0xb2, 0x5a, 0xc3, 0xdc, 0x21, 0x04, 0x29, 0xf7, 0x67, 0x3e, 0xc1, 0x58, - 0x57, 0x4a, 0xe5, 0x43, 0x23, 0xb6, 0x4b, 0x3c, 0x39, 0x63, 0xb5, 0x93, - 0x11, 0xbc, 0x56, 0x44, 0x41, 0x68, 0x3e, 0x6a, 0x28, 0x05, 0x79, 0xa6, - 0xc9, 0x38, 0x84, 0x51, 0xdc, 0x6f, 0x05, 0x5c, 0x3a, 0x7c, 0xaa, 0x3f, - 0x94, 0xeb, 0xab, 0x22, 0xf4, 0x34, 0xa4, 0x5f, 0x0f, 0xe0, 0x7f, 0x96, - 0xc1, 0x76, 0x95, 0x68, 0xc9, 0x5d, 0xc3, 0xe5, 0xdd, 0x74, 0xd2, 0xe3, - 0x2a, 0x09, 0x8d, 0x4e, 0x6a, 0x46, 0x75, 0x99, 0x4d, 0xde, 0x8b, 0x86, - 0x22, 0xe8, 0xe0, 0xff, 0xf1, 0x50, 0x80, 0x2d, 0xdf, 0xfc, 0x21, 0x1a, - 0xcf, 0xec, 0x71, 0x5d, 0x2c, 0x00, 0x9c, 0xb5, 0x4c, 0x4c, 0xec, 0x23, - 0x0b, 0x16, 0x02, 0x05, 0x47, 0xc7, 0x8c, 0x79, 0xdf, 0x1f, 0x9f, 0xf7, - 0xfe, 0xbc, 0xfc, 0x7b, 0xf0, 0x7e, 0xbf, 0xa0, 0x9a, 0xef, 0x5f, 0xaf, - 0x8f, 0x1f, 0xbc, 0xa7, 0x01, 0x0d, 0xa2, 0x55, 0x94, 0x3c, 0x93, 0x0b, - 0x29, 0x24, 0x48, 0xc8, 0x7a, 0x84, 0x8a, 0xb2, 0xf7, 0x4b, 0xcb, 0xcd, - 0x7d, 0x2a, 0x1c, 0x63, 0xcf, 0x1c, 0x72, 0x1a, 0x1d, 0x57, 0xc8, 0xfb, - 0xc7, 0x45, 0x7b, 0xf4, 0x72, 0xca, 0x9c, 0xee, 0x05, 0x65, 0x3d, 0x57, - 0xea, 0x7d, 0x9b, 0x8f, 0xb3, 0x1e, 0xcf, 0x87, 0xd2, 0xdc, 0x5e, 0xa2, - 0xff, 0xab, 0xfe, 0xc3, 0xb0, 0x59, 0xb6, 0x43, 0x8b, 0xe8, 0x78, 0x24, - 0x48, 0x25, 0x5d, 0xa5, 0x71, 0x34, 0x45, 0xc8, 0x2b, 0x53, 0x83, 0xc1, - 0xe4, 0x5e, 0x83, 0x3c, 0xb4, 0x6b, 0x66, 0x96, 0x5c, 0x4b, 0xeb, 0xe7, - 0x00, 0x0b, 0xcb, 0xfa, 0x8d, 0xf4, 0x48, 0x3d, 0x33, 0x95, 0x89, 0x42, - 0xbe, 0x78, 0x81, 0x90, 0xfd, 0x8e, 0xb4, 0x2d, 0xe4, 0xf1, 0x6d, 0xdc, - 0xb6, 0x4f, 0x87, 0xc4, 0x0f, 0x6f, 0x50, 0x14, 0x75, 0x49, 0x63, 0xe3, - 0x24, 0xd0, 0xa4, 0x7d, 0xce, 0x07, 0x56, 0x01, 0x39, 0x38, 0x18, 0x19, - 0x90, 0x0e, 0x7f, 0x73, 0x73, 0x6a, 0x13, 0x99, 0x2c, 0xc1, 0xd4, 0x21, - 0x49, 0x80, 0xb6, 0xee, 0xc6, 0x81, 0x34, 0xdd, 0x53, 0xae, 0x59, 0x25, - 0x5c, 0xfd, 0x7c, 0x36, 0x4c, 0x0d, 0xd7, 0x39, 0xda, 0x0c, 0x07, 0x57, - 0x42, 0xd5, 0xbb, 0x81, 0x35, 0xe4, 0xcd, 0x72, 0x12, 0x75, 0x76, 0x68, - 0x38, 0xf6, 0x49, 0xdd, 0x3b, 0xcf, 0xa8, 0x4d, 0xe2, 0x3f, 0xcf, 0x29, - 0x56, 0x5a, 0x0e, 0xcb, 0x8b, 0x48, 0xeb, 0x01, 0xa0, 0x2a, 0x18, 0x74, - 0x17, 0xe7, 0x20, 0xf6, 0x67, 0x77, 0xa5, 0x03, 0xe6, 0x00, 0x4a, 0x3c, - 0xef, 0x61, 0xc7, 0x20, 0xa8, 0xa9, 0xb7, 0x5e, 0x3e, 0xa5, 0xe5, 0x71, - 0x41, 0x7f, 0x0f, 0xa5, 0x1e, 0x3b, 0x40, 0x11, 0x8c, 0x11, 0x32, 0x5f, - 0xf3, 0xb0, 0x02, 0xb2, 0x02, 0xcb, 0x16, 0x49, 0x34, 0x53, 0x80, 0x29, - 0x5a, 0x57, 0xe4, 0x16, 0x34, 0x6f, 0x06, 0x4b, 0xd5, 0xeb, 0xd4, 0x7f, - 0x47, 0x17, 0x12, 0x3e, 0xf7, 0x30, 0xd7, 0x5f, 0xbf, 0xee, 0xf4, 0x10, - 0xb0, 0x1a, 0x9a, 0xbd, 0x6f, 0xbd, 0xb5, 0x03, 0x70, 0xcf, 0xc6, 0xd2, - 0x95, 0x10, 0x81, 0x6c, 0x32, 0x96, 0xc0, 0xbd, 0x90, 0xca, 0xf5, 0xb4, - 0x90, 0x00, 0x84, 0x3a, 0x0b, 0x17, 0x85, 0x58, 0xf0, 0xff, 0xf1, 0x50, - 0x80, 0x32, 0x7f, 0xfc, 0x21, 0x1a, 0xcf, 0xb2, 0xd9, 0xd3, 0x08, 0x00, - 0x97, 0xb8, 0xb0, 0xac, 0x2e, 0x75, 0x5c, 0x05, 0x85, 0x02, 0x01, 0xe3, - 0xa8, 0x2a, 0xeb, 0xc7, 0xc6, 0x1e, 0x3c, 0xf8, 0x9e, 0x6b, 0x2d, 0x2b, - 0x27, 0xb5, 0xfc, 0xf9, 0xf6, 0xee, 0xbe, 0xac, 0x4a, 0x04, 0x27, 0x1c, - 0x19, 0x52, 0x26, 0x76, 0x3c, 0xa8, 0xeb, 0x93, 0x1d, 0xc6, 0x88, 0x4e, - 0x48, 0x59, 0xaa, 0x73, 0x47, 0xf1, 0xd2, 0xfc, 0x12, 0x65, 0x56, 0x07, - 0x91, 0x92, 0x33, 0x2c, 0x17, 0x1a, 0x8e, 0x26, 0xf8, 0xa0, 0x57, 0x20, - 0x36, 0x4a, 0x34, 0x07, 0x01, 0x10, 0x4d, 0x4f, 0x88, 0x63, 0xdf, 0x9f, - 0x57, 0x82, 0x28, 0xad, 0x97, 0x8a, 0x81, 0xee, 0x27, 0xee, 0x49, 0x1d, - 0xe4, 0x70, 0x32, 0xce, 0x79, 0x1a, 0x9f, 0xa9, 0xf7, 0x7a, 0xa8, 0x71, - 0xb0, 0x92, 0xb4, 0x8a, 0xf5, 0xbb, 0xdd, 0x0b, 0x00, 0x30, 0xe3, 0x7b, - 0x58, 0x73, 0xd6, 0x88, 0xc6, 0xb4, 0x71, 0x68, 0xf9, 0x7f, 0xd5, 0x39, - 0xc8, 0x99, 0xb8, 0x47, 0x0e, 0xbf, 0xcf, 0x12, 0x20, 0xab, 0x0f, 0x75, - 0xb1, 0x06, 0xa7, 0x6d, 0x5f, 0x15, 0x7d, 0x19, 0xad, 0x81, 0xcc, 0x77, - 0x4e, 0xdc, 0x60, 0xb0, 0xa5, 0x78, 0x52, 0x65, 0x04, 0x41, 0x9f, 0xdb, - 0x88, 0x00, 0x0e, 0x7e, 0xc1, 0x40, 0x0f, 0x2e, 0xbb, 0xe5, 0x3d, 0xfc, - 0xd4, 0x8d, 0x4d, 0x6f, 0x7e, 0xbf, 0x8e, 0x7a, 0x1c, 0xe1, 0xc7, 0x86, - 0x2b, 0x9e, 0x3a, 0x3e, 0xee, 0x99, 0x2c, 0x15, 0x43, 0xd1, 0xa5, 0xc1, - 0x25, 0x51, 0xae, 0xc4, 0xda, 0x01, 0x80, 0xf1, 0x95, 0x7c, 0x95, 0x16, - 0x30, 0xe8, 0x24, 0x31, 0x94, 0x03, 0x03, 0x0a, 0xc3, 0x73, 0xf2, 0xdb, - 0x0c, 0x63, 0x04, 0x62, 0x08, 0x48, 0xfd, 0xac, 0xe1, 0xbf, 0xa4, 0x88, - 0x84, 0xe3, 0x36, 0x39, 0x54, 0xae, 0xdb, 0x26, 0x62, 0x54, 0x6c, 0xee, - 0xf3, 0x4a, 0x22, 0x2b, 0x38, 0xe9, 0x07, 0x4f, 0x9a, 0xc5, 0x11, 0xe4, - 0x4b, 0xdb, 0xa5, 0x36, 0x19, 0x1f, 0xc0, 0x00, 0x09, 0x40, 0x09, 0x5d, - 0xfb, 0x73, 0x75, 0x92, 0x3c, 0x6b, 0x5b, 0x6b, 0x61, 0x27, 0xe9, 0x3a, - 0xda, 0x19, 0x49, 0x3c, 0x8f, 0x8d, 0x77, 0xec, 0xa3, 0x55, 0x0d, 0x16, - 0x4f, 0x83, 0x77, 0x4d, 0xcd, 0x58, 0xa9, 0xe2, 0x87, 0x55, 0xb8, 0x5b, - 0x42, 0x8c, 0x0b, 0x8a, 0xe1, 0x98, 0x0c, 0x2e, 0xf5, 0x1c, 0x55, 0x71, - 0xaa, 0xf0, 0x8f, 0x00, 0x61, 0xdb, 0x7e, 0xaf, 0xb7, 0x4b, 0x0a, 0xaf, - 0xcf, 0xb7, 0x54, 0xb3, 0xea, 0xab, 0x43, 0xff, 0x43, 0xe7, 0xdb, 0x19, - 0x8a, 0x15, 0x5e, 0x73, 0xa0, 0xff, 0x8d, 0xa7, 0x33, 0x9c, 0x40, 0x9d, - 0x80, 0xf5, 0x3a, 0x0e, 0x0a, 0xc9, 0x54, 0xa1, 0x4b, 0x0e, 0x02, 0x4c, - 0x30, 0xf4, 0xe8, 0xbc, 0xf5, 0x68, 0x4d, 0xe9, 0x4b, 0x37, 0xb0, 0x65, - 0x78, 0x21, 0x63, 0x80, 0xff, 0xf1, 0x50, 0x80, 0x2a, 0x5f, 0xfc, 0x21, - 0x2a, 0xcf, 0xff, 0xfb, 0xfe, 0x7a, 0x40, 0x9f, 0xb4, 0x52, 0x64, 0xe6, - 0xa4, 0x13, 0x16, 0x04, 0x00, 0x01, 0x9c, 0x63, 0xeb, 0xe4, 0x6b, 0xb5, - 0xf1, 0x92, 0x57, 0x57, 0x5e, 0x75, 0xbe, 0x24, 0x18, 0xbf, 0xd6, 0x64, - 0x0a, 0x67, 0x66, 0x39, 0x1b, 0x0a, 0x00, 0xb0, 0x2a, 0xca, 0x76, 0xff, - 0x42, 0xd8, 0x82, 0xe0, 0x23, 0x14, 0x18, 0x10, 0xc2, 0x81, 0x18, 0x30, - 0x9c, 0x9c, 0x7c, 0x35, 0xd8, 0xc1, 0x00, 0x08, 0xa2, 0x40, 0x40, 0x4d, - 0xc7, 0xc7, 0xab, 0x36, 0x6f, 0xd4, 0xef, 0x24, 0x56, 0x0e, 0x3e, 0x86, - 0x08, 0x6e, 0xc3, 0x0f, 0xbd, 0x78, 0xde, 0x15, 0x15, 0xb2, 0xf4, 0xe7, - 0x06, 0x1b, 0xb9, 0xdd, 0xdf, 0x4d, 0x9e, 0x77, 0x26, 0x15, 0x07, 0x45, - 0xdd, 0xf1, 0x90, 0x5e, 0x5f, 0xd0, 0x6b, 0xf1, 0x50, 0xae, 0xbf, 0x7f, - 0xf4, 0x10, 0x1a, 0x17, 0xe6, 0x01, 0x3e, 0xac, 0x17, 0xff, 0xbe, 0xda, - 0x19, 0xd3, 0xec, 0x40, 0xdf, 0x69, 0xae, 0x25, 0xf5, 0x5e, 0x9f, 0xcf, - 0x67, 0x37, 0x7d, 0x1c, 0x91, 0x90, 0x0e, 0x8e, 0x30, 0x3e, 0xa4, 0x6f, - 0xcb, 0xbc, 0x08, 0x34, 0x0b, 0x64, 0x5e, 0x39, 0x1a, 0xe3, 0x68, 0x40, - 0x02, 0x63, 0x35, 0x23, 0xb1, 0xfb, 0xf2, 0x48, 0x4c, 0xe5, 0x29, 0xe1, - 0xab, 0xb4, 0x7e, 0x51, 0xdf, 0x19, 0x0e, 0x6f, 0x14, 0x5e, 0xe9, 0x47, - 0x1b, 0xb4, 0x88, 0x4f, 0x38, 0x58, 0x45, 0x16, 0x93, 0x1d, 0x74, 0xf5, - 0x73, 0x7b, 0xba, 0x93, 0x55, 0xc1, 0x2a, 0xa5, 0xf9, 0x9f, 0x90, 0x74, - 0xe5, 0xc7, 0x54, 0x49, 0x0a, 0x3a, 0x8c, 0x99, 0x2c, 0x77, 0x9f, 0x34, - 0x41, 0xc7, 0x2a, 0x6c, 0x2f, 0xa9, 0x99, 0xe8, 0xc5, 0xa8, 0x4a, 0x5c, - 0xfb, 0x1e, 0xc2, 0xa4, 0xa0, 0xb1, 0x5d, 0x3f, 0x00, 0x00, 0x05, 0xe3, - 0xeb, 0xe4, 0x6b, 0x79, 0x19, 0x2f, 0x2e, 0xaa, 0x29, 0x56, 0x1b, 0xd6, - 0x9e, 0xe3, 0x71, 0xb0, 0xc1, 0x51, 0x6c, 0x91, 0x39, 0x47, 0xca, 0xf5, - 0x03, 0x17, 0xb0, 0x36, 0x5f, 0x5f, 0x48, 0xec, 0xd3, 0x3c, 0x01, 0x21, - 0xbc, 0x44, 0x26, 0x5d, 0x00, 0x4d, 0x65, 0x40, 0x30, 0x8c, 0x3a, 0x8f, - 0xb5, 0xea, 0x73, 0x06, 0x87, 0x2e, 0x6c, 0x76, 0x57, 0x9d, 0xae, 0x42, - 0xcb, 0xe3, 0xe5, 0xa1, 0x86, 0x14, 0x60, 0x03, 0x46, 0x51, 0x10, 0x02, - 0x71, 0xd9, 0xcf, 0x32, 0x03, 0x80, 0xff, 0xf1, 0x50, 0x80, 0x3c, 0x7f, - 0xfc, 0x20, 0xa6, 0x4c, 0xd9, 0x08, 0x60, 0x6a, 0x28, 0xd9, 0xab, 0x82, - 0xa6, 0xed, 0x71, 0x7e, 0x80, 0x27, 0xd5, 0x74, 0xcb, 0xfa, 0xe7, 0xfd, - 0x6e, 0xfa, 0xf8, 0xae, 0x3b, 0xf3, 0x9a, 0xcf, 0xf8, 0x5f, 0xe3, 0x5b, - 0xf3, 0x3c, 0x75, 0x78, 0x38, 0x62, 0xcd, 0x5e, 0x86, 0x5b, 0xc1, 0x02, - 0xcc, 0x87, 0x4f, 0xda, 0x98, 0xa5, 0xb4, 0x59, 0x55, 0x95, 0x33, 0xf5, - 0x36, 0x21, 0x29, 0x41, 0x41, 0xed, 0xc9, 0x0a, 0x2f, 0x6c, 0x15, 0x5c, - 0x5d, 0x99, 0x20, 0x62, 0x00, 0x32, 0x08, 0x22, 0xd0, 0x87, 0xc8, 0x46, - 0xef, 0x50, 0x64, 0x9d, 0xb1, 0x30, 0x96, 0xbf, 0x8c, 0x39, 0xf4, 0xf4, - 0x54, 0x14, 0xe1, 0xf6, 0xf5, 0x71, 0xe9, 0xea, 0xbc, 0xe5, 0x40, 0x3b, - 0xbf, 0xef, 0xaf, 0x77, 0x18, 0xe8, 0x97, 0x79, 0xe0, 0x89, 0xc1, 0x39, - 0xce, 0x02, 0x0e, 0x9e, 0x12, 0x8f, 0xa9, 0x25, 0x1c, 0x89, 0x43, 0xaa, - 0x3a, 0x2b, 0xa4, 0xb8, 0xfa, 0xf1, 0x8c, 0x25, 0x92, 0x15, 0x01, 0x84, - 0x71, 0x91, 0x39, 0xb9, 0x9f, 0x27, 0x8b, 0xa3, 0xc0, 0x58, 0x01, 0x80, - 0xf5, 0x0f, 0x3c, 0xd7, 0x0e, 0x45, 0x9c, 0x7c, 0x3e, 0x71, 0x70, 0x79, - 0x8b, 0xb0, 0x75, 0xc3, 0x06, 0xeb, 0xf8, 0xf4, 0xd7, 0x8c, 0x5d, 0x6e, - 0xcf, 0x63, 0x2c, 0x51, 0xcb, 0x31, 0x20, 0xb2, 0x26, 0x97, 0x19, 0xd1, - 0x90, 0xba, 0xd5, 0x8e, 0x4e, 0xa2, 0xca, 0xa0, 0xe9, 0x2f, 0x86, 0xec, - 0x28, 0x2b, 0x16, 0x92, 0xb0, 0xde, 0x79, 0xd6, 0x98, 0xf3, 0x57, 0x8d, - 0xc9, 0x61, 0x18, 0xe3, 0x76, 0x22, 0x6b, 0xac, 0x43, 0xc6, 0x3c, 0x06, - 0x8f, 0xd0, 0x38, 0xcb, 0xe5, 0x75, 0xe4, 0x7d, 0x3d, 0xe8, 0xa8, 0xd7, - 0xaf, 0xe8, 0xcd, 0xb7, 0x90, 0x44, 0xc6, 0x48, 0xfe, 0x9f, 0x4c, 0xc7, - 0x69, 0x08, 0xc0, 0xb6, 0x9b, 0xbb, 0x4d, 0x6a, 0x59, 0xb3, 0x15, 0x1b, - 0x15, 0x40, 0x03, 0xe3, 0xdf, 0xef, 0xd5, 0xd7, 0x97, 0xfd, 0x87, 0x5d, - 0xf5, 0xe6, 0x3c, 0xf1, 0xff, 0x60, 0x00, 0x3d, 0x1d, 0xbf, 0xe2, 0xb1, - 0xcf, 0x2a, 0xc9, 0x6a, 0x87, 0x53, 0x22, 0x6d, 0xc4, 0x0f, 0x96, 0xe5, - 0x3c, 0xe5, 0x3b, 0x62, 0xcb, 0x89, 0x96, 0xbf, 0xbd, 0x8b, 0x0e, 0x3f, - 0x50, 0xd0, 0x5c, 0x3c, 0x0e, 0x00, 0xd8, 0x50, 0x0a, 0x14, 0x8d, 0xe6, - 0xb9, 0x56, 0x61, 0x86, 0x1a, 0x4a, 0xe8, 0x58, 0xec, 0x0b, 0xe7, 0x47, - 0xeb, 0xaa, 0xe3, 0xc1, 0x26, 0x6a, 0x1c, 0x33, 0x0e, 0x82, 0xc6, 0x14, - 0xad, 0x9e, 0x8f, 0x71, 0x29, 0x47, 0xcb, 0xd2, 0xf7, 0xde, 0x4a, 0xe6, - 0x7c, 0xdf, 0x55, 0xcc, 0xa1, 0x5e, 0x12, 0xbf, 0x7d, 0xe7, 0x33, 0x8e, - 0x83, 0xab, 0xd8, 0x16, 0x82, 0x8a, 0xb7, 0x8b, 0x27, 0xe7, 0x2b, 0xd8, - 0xc3, 0xdb, 0xec, 0x70, 0x3d, 0x2c, 0x5c, 0x89, 0xbd, 0xe4, 0xb0, 0x2c, - 0xc1, 0x3b, 0x8b, 0x08, 0x9d, 0x06, 0x83, 0x6b, 0xda, 0x44, 0x55, 0x8b, - 0x63, 0xcb, 0x69, 0x50, 0x38, 0x0b, 0xb4, 0x79, 0x4e, 0x23, 0xa4, 0xf4, - 0xe6, 0xda, 0x33, 0x06, 0x58, 0x82, 0x5a, 0x2c, 0xa2, 0x34, 0x0f, 0x6f, - 0xe0, 0xb0, 0x28, 0x67, 0x3a, 0x9e, 0x9f, 0x12, 0x89, 0x4a, 0x19, 0xb0, - 0x7e, 0x01, 0xe8, 0xd9, 0x7e, 0x28, 0x88, 0x2a, 0xa2, 0x05, 0x14, 0xf9, - 0xab, 0x36, 0xeb, 0x5e, 0x72, 0x41, 0x5b, 0xdf, 0xa3, 0x1d, 0xbb, 0xd5, - 0x56, 0x6a, 0x32, 0x2a, 0x40, 0x5f, 0x28, 0x94, 0xc7, 0xff, 0xf1, 0x50, - 0x80, 0x28, 0x5f, 0xfc, 0x21, 0x7a, 0xcf, 0xe3, 0xff, 0xfd, 0x12, 0x64, - 0xa7, 0xb4, 0xb2, 0x11, 0x64, 0x74, 0x1b, 0x14, 0x00, 0x02, 0xf0, 0x07, - 0x55, 0xc7, 0x3a, 0xe6, 0xf7, 0x9e, 0x7c, 0xf8, 0xe3, 0xce, 0x5d, 0x7b, - 0x08, 0x5e, 0xcb, 0xa1, 0x85, 0xd1, 0x9a, 0x2b, 0x5b, 0x62, 0x54, 0xf3, - 0x69, 0xe6, 0x05, 0x34, 0x44, 0x78, 0x24, 0x64, 0x7b, 0xba, 0x82, 0x64, - 0x9e, 0x96, 0xa7, 0x8f, 0x9b, 0x16, 0x93, 0x63, 0x37, 0x4b, 0xfe, 0x89, - 0x4d, 0x2c, 0xd5, 0x40, 0x6d, 0xef, 0x2b, 0x96, 0x7d, 0x2b, 0x45, 0x6e, - 0xad, 0x76, 0xf7, 0x3b, 0xe4, 0xdb, 0xbb, 0xd5, 0xbf, 0xc4, 0xc9, 0x3a, - 0x9c, 0x9d, 0x5c, 0xb8, 0x37, 0xad, 0x66, 0x4a, 0x76, 0xfd, 0x48, 0x41, - 0xa9, 0x64, 0x88, 0x75, 0x25, 0xbb, 0xab, 0x0f, 0x36, 0xb0, 0x31, 0xca, - 0x57, 0x10, 0x1c, 0x18, 0x35, 0xbb, 0xe8, 0xa3, 0xfa, 0xbb, 0x33, 0xf9, - 0x0c, 0x1a, 0xcf, 0xa0, 0x26, 0x9e, 0xaa, 0x77, 0x22, 0x56, 0x78, 0x93, - 0xf3, 0x18, 0xf5, 0x26, 0x76, 0x23, 0xbd, 0x8d, 0xb1, 0xb4, 0xb3, 0xe3, - 0xad, 0xef, 0x04, 0x00, 0xb0, 0x10, 0xff, 0xca, 0x90, 0x1b, 0x54, 0x18, - 0x71, 0xeb, 0x07, 0x5e, 0xb3, 0x86, 0x62, 0xc8, 0x9f, 0x9a, 0xd2, 0x94, - 0x51, 0x2e, 0x7a, 0x55, 0x57, 0x86, 0x00, 0xd1, 0x6f, 0x76, 0x0a, 0x08, - 0x24, 0xcb, 0x02, 0x08, 0xb8, 0xe2, 0x80, 0x48, 0xf5, 0xea, 0xf2, 0x09, - 0xe3, 0xd7, 0x8c, 0xce, 0xe9, 0x4c, 0xe6, 0x00, 0x73, 0x46, 0x2d, 0xad, - 0x0d, 0x96, 0x63, 0xa6, 0x99, 0x9d, 0xf3, 0xfa, 0x2f, 0x70, 0xe2, 0x11, - 0x56, 0xe1, 0xed, 0xaf, 0x0c, 0x24, 0xa7, 0xa4, 0xba, 0x10, 0x6a, 0x42, - 0x1f, 0xc0, 0x00, 0x02, 0xf0, 0x03, 0x8b, 0xad, 0x65, 0x41, 0x49, 0x98, - 0xb1, 0x10, 0xd2, 0x35, 0xf5, 0xc7, 0x8c, 0x5c, 0x41, 0x04, 0x0b, 0x27, - 0xe9, 0x2d, 0x2f, 0x01, 0xd8, 0xf2, 0xcb, 0x24, 0x4d, 0x4c, 0x97, 0xab, - 0xc3, 0xcb, 0x8d, 0xf2, 0x66, 0xb5, 0xb6, 0xcc, 0xd9, 0x15, 0xd3, 0x64, - 0xc4, 0x9f, 0xaa, 0x5a, 0xee, 0xb5, 0xd1, 0x42, 0x58, 0x48, 0x30, 0xbb, - 0xb6, 0x02, 0xa5, 0x36, 0xf3, 0x98, 0x94, 0x64, 0x69, 0xb6, 0x0c, 0x7a, - 0x54, 0x44, 0xae, 0x90, 0x65, 0xcd, 0x27, 0xff, 0xf1, 0x50, 0x80, 0x33, - 0x1f, 0xfc, 0x21, 0x1a, 0xcb, 0xf1, 0x86, 0x6f, 0x00, 0x00, 0x9d, 0xba, - 0x41, 0x15, 0xac, 0x60, 0x00, 0x00, 0x00, 0x02, 0x54, 0xfc, 0x4a, 0xcb, - 0xfc, 0x0f, 0x4d, 0x22, 0x2a, 0x04, 0x80, 0x7c, 0x9f, 0x24, 0x8c, 0xf1, - 0x4c, 0xb0, 0xb8, 0xc7, 0xce, 0xf0, 0x02, 0x75, 0xbf, 0xe0, 0xa6, 0x50, - 0x44, 0x36, 0x73, 0xd1, 0x31, 0x12, 0x4d, 0x45, 0xd0, 0x75, 0x28, 0xe8, - 0x48, 0x0d, 0xf2, 0xef, 0x8a, 0x3c, 0xe6, 0x88, 0x7e, 0x7e, 0x1f, 0x47, - 0x76, 0x74, 0x6f, 0x65, 0x50, 0x4c, 0xa4, 0x35, 0xa2, 0x41, 0x14, 0x90, - 0x01, 0x86, 0x32, 0x56, 0xb0, 0x0b, 0x28, 0x29, 0x3b, 0xe9, 0xb0, 0x8c, - 0x51, 0x40, 0xf3, 0x25, 0x17, 0x3f, 0x53, 0x98, 0x80, 0x44, 0xeb, 0x4d, - 0x86, 0x82, 0x65, 0x16, 0xe8, 0x6b, 0xf9, 0x4e, 0x70, 0x18, 0x09, 0x9f, - 0x22, 0xa5, 0xca, 0x29, 0x4a, 0xc8, 0xd1, 0x08, 0xd8, 0x54, 0xd7, 0xec, - 0xe4, 0xed, 0xc7, 0x75, 0xd7, 0x0e, 0xf8, 0x19, 0x12, 0xc2, 0xf7, 0xc2, - 0x62, 0xee, 0x2c, 0xd6, 0x15, 0x99, 0xfa, 0x61, 0x73, 0x7b, 0x0e, 0xe6, - 0x73, 0xdd, 0xdb, 0x89, 0xcc, 0x38, 0xc1, 0x82, 0x32, 0x92, 0x69, 0x9c, - 0xc4, 0x4a, 0xd7, 0x32, 0xb9, 0x26, 0x60, 0x4e, 0x31, 0x22, 0x90, 0x94, - 0x56, 0x72, 0x8c, 0xd6, 0x72, 0x62, 0xb5, 0x42, 0x38, 0x76, 0xd1, 0x36, - 0x24, 0xc3, 0x00, 0xac, 0xa7, 0x30, 0x10, 0x0a, 0xca, 0x2e, 0x86, 0xe4, - 0x8a, 0x0a, 0xe4, 0x21, 0x7c, 0x0d, 0x06, 0xa7, 0x53, 0x10, 0x3a, 0x5e, - 0x96, 0x23, 0xbc, 0x58, 0xb8, 0x8d, 0xa9, 0x7d, 0x75, 0x62, 0xc0, 0x4a, - 0xf0, 0xa0, 0x15, 0xb9, 0x81, 0x28, 0x02, 0xed, 0xa0, 0x4c, 0x05, 0x4c, - 0x88, 0x20, 0x54, 0xcf, 0x8a, 0x9c, 0xc7, 0x31, 0xca, 0xd5, 0x38, 0x63, - 0x6f, 0x0f, 0x3d, 0x7f, 0x1f, 0xdb, 0xd7, 0x04, 0xf1, 0xd5, 0xd2, 0x6f, - 0x74, 0xf5, 0xca, 0x0c, 0xc3, 0xf8, 0x20, 0x28, 0x8a, 0x11, 0x48, 0xa8, - 0x12, 0xf9, 0xe3, 0x72, 0xe6, 0xfc, 0xf3, 0x3b, 0xe3, 0x63, 0x70, 0x91, - 0x05, 0x07, 0xe6, 0x74, 0x0e, 0x5c, 0x6e, 0x3a, 0x9c, 0x0c, 0x0f, 0x84, - 0x4a, 0xe5, 0x2b, 0xcd, 0x1c, 0x5e, 0xdf, 0x77, 0x41, 0xc2, 0xa7, 0xa3, - 0x0c, 0x5a, 0x35, 0x9b, 0x23, 0xa5, 0xdc, 0xd3, 0x8e, 0xee, 0xcb, 0x8d, - 0x6d, 0xc2, 0x20, 0x1c, 0x8f, 0xad, 0x0e, 0xaa, 0xe3, 0x12, 0xa4, 0x64, - 0x58, 0xcd, 0xfb, 0x89, 0x10, 0xcd, 0xc5, 0x8b, 0x81, 0x69, 0x94, 0x53, - 0xb5, 0xb2, 0x17, 0xfa, 0xc1, 0xa7, 0xad, 0x59, 0x15, 0x2e, 0x34, 0xbb, - 0x53, 0x39, 0x27, 0x97, 0x24, 0x1b, 0x05, 0x1e, 0x00, 0x27, 0x09, 0x4a, - 0xeb, 0xf1, 0xa4, 0xb8, 0x5d, 0xfa, 0x4d, 0x32, 0xa0, 0x28, 0x23, 0xfc, - 0x54, 0xa7, 0x16, 0x68, 0x3e, 0xb9, 0x00, 0x22, 0xb4, 0x40, 0x01, 0x66, - 0xbe, 0xf1, 0x67, 0x56, 0x1b, 0xc3, 0x80, 0xff, 0xf1, 0x50, 0x80, 0x2b, - 0x7f, 0xfc, 0x21, 0x1a, 0xcd, 0xfb, 0xf5, 0x7d, 0x10, 0x00, 0xa2, 0xb4, - 0xc1, 0xa4, 0xe6, 0xb6, 0x50, 0x00, 0x00, 0x03, 0xe3, 0xdd, 0x3d, 0xaa, - 0x3a, 0x4b, 0xea, 0x65, 0x70, 0x0a, 0x64, 0xc4, 0x0c, 0x78, 0xff, 0x58, - 0xa2, 0xcb, 0x9f, 0x69, 0x65, 0x96, 0xfd, 0x27, 0x61, 0xb4, 0x20, 0x7b, - 0x92, 0xf0, 0xa4, 0x92, 0xa9, 0x22, 0x42, 0x00, 0x06, 0x7b, 0xd2, 0xb7, - 0xa3, 0xb7, 0x13, 0xb8, 0x90, 0x20, 0xf0, 0x12, 0xdb, 0xac, 0x70, 0x23, - 0x45, 0x1d, 0x47, 0xc4, 0x42, 0xf4, 0xb8, 0x7d, 0xf5, 0xa5, 0x8d, 0x6d, - 0xea, 0x3f, 0x81, 0xc2, 0xca, 0x30, 0x47, 0x14, 0x25, 0xbb, 0x43, 0xa5, - 0x23, 0x1c, 0x1c, 0xd7, 0xa4, 0x69, 0xaa, 0xe7, 0x5e, 0x6f, 0xb2, 0x41, - 0xda, 0x5f, 0xae, 0x50, 0x07, 0x4a, 0x54, 0x5b, 0xa0, 0x69, 0x1a, 0x3b, - 0x14, 0xec, 0xf7, 0x1c, 0xbb, 0xe6, 0x85, 0x61, 0x3d, 0x6f, 0x52, 0x9b, - 0x87, 0x4b, 0x27, 0xfb, 0x1f, 0x66, 0x61, 0x95, 0x32, 0x6f, 0xfd, 0x0f, - 0x02, 0xec, 0x60, 0xee, 0x53, 0x8b, 0xd5, 0x0a, 0x13, 0x98, 0x93, 0xb8, - 0x83, 0x76, 0x98, 0x0a, 0x15, 0x02, 0x41, 0x49, 0x84, 0x8c, 0x38, 0x58, - 0xc4, 0xa4, 0x68, 0xc2, 0x65, 0x6d, 0x91, 0xb8, 0x4d, 0x00, 0xb8, 0x4a, - 0xa0, 0x74, 0xc6, 0x4f, 0x9f, 0x81, 0xcb, 0x24, 0x97, 0x0e, 0x37, 0x57, - 0xb9, 0x8e, 0x9e, 0xee, 0xab, 0xff, 0x1f, 0x6a, 0xa4, 0x14, 0x55, 0x52, - 0x54, 0xe7, 0xf9, 0xe1, 0x54, 0xd6, 0x26, 0x53, 0xdd, 0x3c, 0x27, 0x3e, - 0x8d, 0xad, 0x21, 0xa7, 0xa8, 0x36, 0x31, 0x5a, 0xdd, 0xf5, 0x7e, 0xb0, - 0xec, 0xf3, 0x5d, 0x2e, 0x3a, 0xee, 0x16, 0xc0, 0xa2, 0xf6, 0x47, 0xa5, - 0xf7, 0x69, 0xcd, 0xaf, 0xa8, 0x03, 0xcf, 0xdf, 0xfd, 0x9d, 0x15, 0xa6, - 0x52, 0xa9, 0xf8, 0x2a, 0x02, 0xa0, 0x2a, 0x54, 0xa8, 0x15, 0x32, 0x71, - 0xcc, 0x92, 0x95, 0x26, 0xd7, 0x40, 0x27, 0x37, 0x63, 0x3c, 0x11, 0xd2, - 0xe4, 0x11, 0x13, 0xcc, 0xd0, 0xa5, 0xc2, 0x96, 0xf7, 0x10, 0xcb, 0x3f, - 0x84, 0xd3, 0x08, 0xc1, 0x0b, 0xdd, 0xeb, 0x3d, 0x74, 0xf1, 0x3f, 0xfa, - 0x6e, 0x11, 0x7c, 0x2d, 0x62, 0xb5, 0x3c, 0x09, 0xd4, 0xfa, 0x1e, 0x5b, - 0x5f, 0x05, 0x5f, 0x1a, 0xb6, 0x19, 0x3a, 0xb6, 0x54, 0xaf, 0xaf, 0x52, - 0x04, 0x74, 0x4a, 0x55, 0xc3, 0x92, 0xa9, 0x6f, 0x6c, 0x89, 0x37, 0xa8, - 0x18, 0x09, 0xa7, 0x5e, 0x02, 0xc7, 0xff, 0xf1, 0x50, 0x80, 0x28, 0xbf, - 0xfc, 0x21, 0x1a, 0xce, 0xf1, 0xd9, 0xf5, 0x00, 0x40, 0xa1, 0xb7, 0x3a, - 0x4d, 0x68, 0x66, 0x19, 0x96, 0x30, 0xb3, 0x2d, 0x8b, 0x0c, 0xbf, 0x3e, - 0x3a, 0x8d, 0x57, 0x8e, 0xbe, 0x37, 0x9f, 0x5b, 0xcf, 0x20, 0x9a, 0x4b, - 0x20, 0xe1, 0x23, 0xe5, 0xdc, 0x84, 0x1e, 0xaa, 0xb6, 0x5c, 0x99, 0x67, - 0x39, 0x47, 0x2c, 0x20, 0xd9, 0x6e, 0xf0, 0x0a, 0xf2, 0xe7, 0x14, 0xa2, - 0xcb, 0x40, 0x8c, 0x51, 0x15, 0x9f, 0x8e, 0x93, 0xd9, 0x2d, 0x80, 0xb3, - 0xfe, 0x66, 0x87, 0xd2, 0x0d, 0x2d, 0xd6, 0xcf, 0xf3, 0xf8, 0xa6, 0xf7, - 0x4e, 0xc8, 0x92, 0xb3, 0xd6, 0xf7, 0x1a, 0x78, 0x66, 0xb3, 0x97, 0x20, - 0xd4, 0xe1, 0x48, 0xba, 0x45, 0xea, 0x1b, 0xac, 0x22, 0xb6, 0xcf, 0x1f, - 0xa5, 0x27, 0x55, 0x3c, 0x38, 0x04, 0xde, 0xf0, 0xd1, 0x1c, 0xbd, 0x7a, - 0x5e, 0x59, 0x6c, 0xeb, 0x78, 0x30, 0xa4, 0xd4, 0xcf, 0xd5, 0x73, 0xad, - 0xef, 0xf8, 0xe1, 0x6f, 0xbf, 0xba, 0xcd, 0x70, 0xbe, 0x88, 0x25, 0xd0, - 0x3d, 0x94, 0x40, 0xff, 0xe8, 0xfb, 0x1e, 0x45, 0x77, 0xa9, 0x54, 0x45, - 0xf9, 0xa9, 0xbf, 0x8a, 0xab, 0x2a, 0x25, 0x38, 0xc3, 0x46, 0x5a, 0x17, - 0x05, 0x5b, 0x38, 0x5f, 0xdb, 0xff, 0x81, 0xd7, 0xfb, 0x6d, 0x1b, 0xf2, - 0x4f, 0x82, 0xc2, 0xce, 0x04, 0x87, 0x9f, 0xd5, 0x8d, 0x8c, 0xe8, 0xd4, - 0x1b, 0x2e, 0x9f, 0xfb, 0xc1, 0x7d, 0x47, 0xa9, 0x2e, 0x22, 0x9f, 0x7c, - 0xf4, 0x4f, 0x13, 0x2b, 0xde, 0x88, 0xcb, 0x7b, 0x02, 0x8d, 0xfa, 0xf8, - 0x36, 0xf7, 0x0b, 0xe5, 0x56, 0x9e, 0x3a, 0x29, 0xd4, 0x32, 0xd1, 0x43, - 0x6a, 0x11, 0x58, 0x9c, 0xc6, 0x7f, 0x80, 0x00, 0x00, 0x00, 0xd6, 0x5f, - 0x35, 0x97, 0xad, 0xef, 0xaa, 0xdf, 0x4c, 0x06, 0x3d, 0x98, 0x97, 0x21, - 0x1f, 0xfa, 0x42, 0x23, 0x9c, 0x36, 0x41, 0x79, 0x46, 0x49, 0x3a, 0x12, - 0x38, 0xfd, 0xab, 0xf4, 0xe2, 0xf5, 0xd8, 0x09, 0xf3, 0x72, 0x43, 0xab, - 0x1f, 0x1a, 0xd6, 0xc1, 0x58, 0xdc, 0x1c, 0x8e, 0x95, 0xfb, 0x9f, 0xa5, - 0xab, 0xcf, 0x18, 0x06, 0xce, 0xe3, 0xdf, 0xc2, 0x2e, 0x7e, 0x48, 0xc9, - 0xe8, 0xc8, 0xcf, 0xd5, 0x9b, 0xbf, 0xa1, 0x1d, 0xef, 0x67, 0x29, 0xf3, - 0xf7, 0xb1, 0x41, 0xbb, 0xbf, 0x45, 0x0e, 0xff, 0xf1, 0x50, 0x80, 0x2f, - 0x3f, 0xfc, 0x21, 0x1a, 0xcf, 0xf5, 0x88, 0x7d, 0x80, 0x00, 0x9e, 0xba, - 0x29, 0xcc, 0xe8, 0x26, 0x15, 0x05, 0x85, 0x02, 0x00, 0x00, 0x00, 0x4a, - 0xd3, 0xe3, 0xc5, 0xbc, 0x7c, 0x2f, 0xeb, 0x5e, 0xfc, 0xfb, 0x74, 0x33, - 0x36, 0x56, 0x98, 0x46, 0x39, 0x7d, 0x70, 0x8c, 0x32, 0x73, 0x7f, 0x19, - 0x73, 0xde, 0xb3, 0xb6, 0x9e, 0x36, 0xf2, 0x68, 0xe3, 0xc0, 0x28, 0x13, - 0x42, 0x25, 0xf1, 0xaf, 0x3c, 0x08, 0x56, 0xcd, 0x10, 0xce, 0x42, 0xe9, - 0x68, 0xda, 0xfc, 0x58, 0xe4, 0x7c, 0xe1, 0xb5, 0x2a, 0xb6, 0x95, 0xc2, - 0x29, 0x40, 0x81, 0x92, 0xb9, 0x6d, 0xde, 0x34, 0x85, 0x08, 0x0a, 0x6a, - 0x91, 0x13, 0x4c, 0x18, 0x42, 0x8d, 0x28, 0xe5, 0xee, 0xdb, 0x55, 0x7b, - 0x28, 0x16, 0xc8, 0x3e, 0x1d, 0x98, 0x0e, 0x48, 0x54, 0x9f, 0xa6, 0xb5, - 0x04, 0x18, 0xeb, 0x86, 0x6f, 0xf0, 0x86, 0xa8, 0x01, 0x8f, 0xc3, 0xc2, - 0xc0, 0x0e, 0xfe, 0xe0, 0x0f, 0x4e, 0x92, 0x0f, 0x0b, 0xd5, 0xb4, 0xb0, - 0xff, 0x2a, 0xbc, 0xce, 0xf1, 0xa1, 0xa2, 0xe0, 0x97, 0x69, 0x09, 0xfc, - 0x43, 0x5a, 0x42, 0x7e, 0x1e, 0x3c, 0xb4, 0x44, 0xe6, 0x01, 0xca, 0x45, - 0x05, 0x63, 0x42, 0xfd, 0xc7, 0x02, 0xbb, 0x56, 0x48, 0xe3, 0x45, 0xc5, - 0xaa, 0x41, 0x14, 0x01, 0x40, 0x73, 0x42, 0x0b, 0xb4, 0xfd, 0x55, 0x37, - 0xf7, 0x6e, 0x96, 0xf2, 0xdb, 0xf3, 0xab, 0xf3, 0x7f, 0xfb, 0x70, 0x42, - 0xb4, 0x8c, 0x36, 0x0b, 0x68, 0x43, 0x28, 0x33, 0x8c, 0x97, 0xd1, 0x52, - 0xa0, 0xa9, 0x68, 0xb9, 0xa8, 0x13, 0x07, 0x77, 0xc9, 0xa6, 0xc0, 0x48, - 0xc9, 0xc2, 0x09, 0x60, 0x36, 0xc0, 0x10, 0xe0, 0x6d, 0x44, 0x91, 0x86, - 0x7d, 0x79, 0x43, 0x26, 0x7a, 0x9f, 0x45, 0xa2, 0x9b, 0x4e, 0xd0, 0xbb, - 0x5a, 0xef, 0x8b, 0x1a, 0xb7, 0x2d, 0x0d, 0x75, 0xce, 0x89, 0x39, 0xe8, - 0x35, 0xa2, 0x0a, 0xe9, 0xf7, 0x80, 0xa9, 0x51, 0x44, 0xa1, 0x02, 0xa6, - 0xa6, 0xbc, 0x5d, 0x64, 0xa4, 0xbd, 0xae, 0x00, 0x95, 0xdc, 0x43, 0xd4, - 0xa6, 0x6c, 0x4b, 0x89, 0xa0, 0x88, 0x07, 0x90, 0x70, 0x07, 0x2c, 0x01, - 0xf6, 0x58, 0x02, 0x01, 0x8d, 0x3a, 0x59, 0x70, 0x06, 0xe3, 0xf9, 0xf5, - 0x34, 0x4d, 0x81, 0x05, 0xd1, 0xd8, 0x85, 0x99, 0x31, 0xdc, 0x43, 0x95, - 0x3a, 0xd9, 0x4b, 0xea, 0xb5, 0x82, 0x34, 0xa0, 0x1d, 0xc8, 0xc3, 0x13, - 0xaa, 0x88, 0x9a, 0x1c, 0x5c, 0x0b, 0x35, 0xb2, 0xc6, 0x45, 0x31, 0xbe, - 0x4c, 0xa3, 0x5b, 0x64, 0x40, 0x32, 0xfa, 0x52, 0x46, 0xfe, 0x75, 0x6a, - 0xc8, 0xa1, 0x9e, 0xde, 0x95, 0x5c, 0x80, 0x42, 0x26, 0xe1, 0x40, 0xe0, - 0xff, 0xf1, 0x50, 0x80, 0x2d, 0x9f, 0xfc, 0x21, 0x1a, 0xcb, 0xbd, 0xef, - 0x5f, 0x80, 0x00, 0xa1, 0xb4, 0xc2, 0x1c, 0x6a, 0x94, 0x4c, 0x05, 0x87, - 0x01, 0x00, 0x00, 0x0b, 0xc1, 0xac, 0xd6, 0x55, 0x7e, 0x3e, 0x7c, 0xde, - 0xb1, 0xf5, 0xcd, 0x67, 0xdc, 0x42, 0x2a, 0x01, 0x73, 0x7f, 0x18, 0x42, - 0xf8, 0xb6, 0x19, 0xef, 0x22, 0x26, 0xc2, 0x9f, 0x88, 0xdb, 0xf9, 0x93, - 0x34, 0xe4, 0x7c, 0xf4, 0xe1, 0xb8, 0x16, 0x71, 0xd4, 0x6b, 0x9d, 0x12, - 0x61, 0x8a, 0xdd, 0xe0, 0xa4, 0x9e, 0xb8, 0x48, 0x22, 0xb5, 0x08, 0x6d, - 0xed, 0xe6, 0xaf, 0x3e, 0x9e, 0x06, 0x12, 0x85, 0x60, 0xc7, 0xec, 0x7d, - 0xfc, 0x5c, 0x97, 0x8d, 0xcd, 0xea, 0xa7, 0x5f, 0x67, 0xab, 0x5a, 0xde, - 0xed, 0x33, 0xa6, 0xeb, 0x8d, 0xc4, 0x00, 0x0e, 0xfe, 0x33, 0x70, 0x90, - 0x84, 0xdf, 0x3f, 0xb4, 0x0a, 0x04, 0x5e, 0xe4, 0x01, 0x70, 0x48, 0xdc, - 0xdc, 0x6e, 0x31, 0x03, 0xea, 0x0f, 0x30, 0xdb, 0x14, 0x88, 0xd2, 0x61, - 0xdd, 0xdd, 0xb2, 0x11, 0x69, 0x7e, 0x20, 0xc0, 0xf2, 0xca, 0xc2, 0x3a, - 0x4a, 0x77, 0x70, 0xfb, 0xa6, 0x1e, 0xa5, 0x7c, 0x8b, 0x72, 0xb4, 0x91, - 0xcd, 0x95, 0x13, 0xeb, 0x4f, 0xa3, 0x8c, 0x7c, 0x7f, 0x50, 0x9f, 0xe9, - 0x21, 0xd3, 0x76, 0xb6, 0x07, 0xb7, 0x11, 0xd1, 0x9c, 0xc7, 0x93, 0x98, - 0xf8, 0x79, 0x66, 0x2b, 0x87, 0x80, 0x4b, 0xaf, 0xa0, 0x8e, 0xcd, 0x2c, - 0x46, 0x09, 0x9e, 0xda, 0x4c, 0x20, 0xa0, 0xce, 0x62, 0x51, 0x91, 0xe7, - 0x66, 0x72, 0x8f, 0x7e, 0x48, 0x51, 0x64, 0xb7, 0x34, 0xbb, 0xe1, 0x46, - 0x1b, 0x99, 0x26, 0x63, 0x03, 0x13, 0x9e, 0x6f, 0x73, 0x66, 0x30, 0x20, - 0x34, 0xee, 0x7f, 0x3d, 0xdd, 0x0b, 0x3b, 0x58, 0xc9, 0x44, 0x74, 0x37, - 0x89, 0xa4, 0x5d, 0x15, 0x86, 0x93, 0x2b, 0x43, 0xfb, 0xd0, 0xa8, 0x25, - 0x0a, 0x81, 0x6c, 0x35, 0x9a, 0xaa, 0x75, 0x85, 0xb7, 0xa9, 0xb1, 0xa6, - 0x19, 0x5f, 0xf0, 0x0c, 0x36, 0x48, 0xfc, 0x1b, 0x93, 0xe1, 0x21, 0x14, - 0x3c, 0xc0, 0x14, 0x9b, 0x9c, 0xc6, 0x16, 0x3a, 0x11, 0x0d, 0xbd, 0xb2, - 0x9d, 0x54, 0x7a, 0xb7, 0x42, 0xe1, 0x86, 0x1f, 0x9c, 0xe5, 0x86, 0x7f, - 0x8a, 0xd6, 0xee, 0x59, 0x2f, 0x87, 0x96, 0xcc, 0x72, 0xed, 0xf3, 0xb9, - 0xdc, 0x8c, 0xb0, 0xbc, 0xc6, 0xde, 0xf5, 0x80, 0x13, 0x17, 0x86, 0xbf, - 0xe0, 0x46, 0x40, 0x8d, 0x22, 0x39, 0xaf, 0xde, 0x77, 0xf6, 0x9c, 0xc1, - 0xfa, 0x7c, 0x64, 0xaf, 0x93, 0xd5, 0xe1, 0x16, 0xb6, 0x33, 0x77, 0xb6, - 0x68, 0xcb, 0x65, 0x67, 0xff, 0xf1, 0x50, 0x80, 0x2e, 0x3f, 0xfc, 0x21, - 0x1a, 0xcf, 0xd6, 0xf5, 0xff, 0xbd, 0xff, 0xa3, 0xb1, 0x51, 0xd9, 0x52, - 0x84, 0x53, 0x18, 0x1a, 0xac, 0x7e, 0x3d, 0xf2, 0xa5, 0x75, 0xcd, 0xde, - 0x4a, 0x98, 0xe3, 0x2d, 0xad, 0xea, 0xb3, 0xc9, 0xc7, 0x55, 0x7b, 0xd0, - 0x52, 0xd7, 0x52, 0xb4, 0x09, 0x6c, 0x3b, 0x55, 0x62, 0x7d, 0x95, 0x97, - 0xc2, 0x1d, 0x93, 0x88, 0x45, 0xde, 0xc4, 0x97, 0x90, 0xba, 0xba, 0x86, - 0xc4, 0x6f, 0x1e, 0x7c, 0x28, 0xeb, 0xee, 0x77, 0xe1, 0x57, 0xb4, 0xa8, - 0x53, 0x8d, 0x07, 0x1e, 0x4e, 0xb3, 0x61, 0xe1, 0x70, 0x40, 0x8b, 0x91, - 0xf7, 0xdf, 0xb5, 0x73, 0x20, 0x31, 0x8a, 0x5c, 0xb5, 0x74, 0x38, 0xf7, - 0x55, 0xb8, 0x6f, 0xe5, 0xe5, 0x0d, 0xd3, 0xa5, 0x81, 0x6a, 0x58, 0xbc, - 0xd7, 0xa6, 0x28, 0x81, 0x08, 0xe3, 0x6c, 0x14, 0x4b, 0x07, 0x06, 0x9a, - 0xdc, 0x65, 0xb2, 0xb1, 0x2f, 0x7f, 0x17, 0x88, 0x2e, 0xf0, 0x41, 0x29, - 0xb3, 0xb2, 0x35, 0xd8, 0xe3, 0xd8, 0xc5, 0xf1, 0x55, 0xdc, 0x92, 0x60, - 0x54, 0xe5, 0xa6, 0x5b, 0x2c, 0x13, 0x5d, 0xba, 0xb6, 0x30, 0xee, 0x0e, - 0xf6, 0xd0, 0x6f, 0x8f, 0x05, 0x5e, 0xe4, 0x3a, 0x59, 0xa0, 0xac, 0x3e, - 0xca, 0x20, 0x24, 0x39, 0x77, 0x98, 0xd6, 0x98, 0xec, 0x75, 0xf5, 0x19, - 0x11, 0x60, 0x3b, 0x65, 0x15, 0x65, 0x54, 0x1c, 0x66, 0x97, 0x83, 0xda, - 0x03, 0x1d, 0x28, 0xdb, 0x97, 0x09, 0x35, 0xf7, 0xed, 0xbb, 0x6d, 0xb3, - 0xb9, 0x6a, 0xa1, 0xe3, 0xb3, 0x28, 0xb7, 0x3c, 0x23, 0x4f, 0x99, 0xb2, - 0x57, 0xe5, 0xdb, 0xd6, 0x75, 0x76, 0xfa, 0xbf, 0x17, 0x1a, 0x26, 0x98, - 0xa5, 0xf2, 0xab, 0x8d, 0xb3, 0xd7, 0x58, 0xdd, 0x48, 0x59, 0xe0, 0x12, - 0x53, 0xd7, 0x86, 0xaf, 0x50, 0x96, 0x74, 0x7d, 0x83, 0x6d, 0xb6, 0x41, - 0xcf, 0x46, 0xcb, 0x74, 0xaa, 0x39, 0xe0, 0xd5, 0x7a, 0xeb, 0xf1, 0xf3, - 0x55, 0x2b, 0x59, 0x5d, 0x64, 0xa9, 0x8e, 0x32, 0xe5, 0xee, 0xeb, 0x3c, - 0xd2, 0x91, 0x96, 0x03, 0x6f, 0x56, 0xd6, 0x39, 0x5c, 0xfd, 0x73, 0xf5, - 0x00, 0x6b, 0xff, 0x4f, 0xd5, 0xc3, 0xdb, 0x55, 0x8f, 0xb1, 0x58, 0xc2, - 0xbd, 0x37, 0x22, 0x31, 0xf6, 0x30, 0x68, 0x67, 0xcd, 0xbf, 0x42, 0x23, - 0xcb, 0xe1, 0x33, 0xe9, 0xf5, 0x17, 0x7c, 0x58, 0x92, 0xf7, 0x63, 0xec, - 0xb0, 0xc6, 0x4d, 0xe3, 0xb3, 0x15, 0x4a, 0xd7, 0xd3, 0x11, 0x72, 0x89, - 0x56, 0x74, 0x5f, 0xcf, 0xb2, 0x82, 0xef, 0xd4, 0x2b, 0x0a, 0xd6, 0x8d, - 0x4c, 0x40, 0x89, 0xee, 0xf7, 0xc8, 0xb5, 0x08, 0x91, 0x14, 0x99, 0x98, - 0x9e, 0xff, 0xf1, 0x50, 0x80, 0x2f, 0xff, 0xfc, 0x21, 0x1a, 0xcf, 0x73, - 0xe2, 0x5f, 0x10, 0x08, 0xa1, 0xb6, 0xc9, 0xd5, 0xec, 0x80, 0x5d, 0x64, - 0xf8, 0xef, 0x64, 0xeb, 0xd7, 0x1a, 0xf5, 0xc6, 0xf8, 0xde, 0xa3, 0x59, - 0xad, 0xf1, 0xcb, 0x5d, 0x67, 0x9a, 0xab, 0xcf, 0x21, 0x8f, 0xd4, 0xe5, - 0xe5, 0x64, 0x5a, 0x00, 0x3c, 0xd5, 0x1c, 0x2e, 0x65, 0xd0, 0x31, 0x17, - 0x0a, 0x9b, 0x99, 0x76, 0xd1, 0x56, 0x8f, 0x53, 0x2b, 0x94, 0x10, 0x05, - 0x60, 0x08, 0xea, 0x68, 0xde, 0x12, 0x6c, 0x86, 0xed, 0xcb, 0x27, 0xaf, - 0xa2, 0xf8, 0x1a, 0xb4, 0x72, 0x3f, 0x13, 0xa7, 0x39, 0xfd, 0x37, 0x74, - 0x9d, 0xd9, 0x23, 0x8b, 0xbf, 0x43, 0x97, 0xfd, 0x2f, 0x13, 0x50, 0x3a, - 0xa4, 0x2f, 0x1e, 0xef, 0xfe, 0xed, 0xdd, 0x5a, 0xbb, 0xfc, 0x2a, 0x07, - 0x67, 0x7f, 0x20, 0xe5, 0x55, 0x11, 0x0c, 0xf2, 0xea, 0xf4, 0xd7, 0x00, - 0x70, 0x26, 0x31, 0xf3, 0x89, 0x04, 0xae, 0xa5, 0x35, 0x3c, 0xa6, 0x2c, - 0x8d, 0xc8, 0x9c, 0x4f, 0xbb, 0x57, 0x16, 0xac, 0xed, 0x6c, 0xc6, 0x99, - 0xdc, 0x5b, 0x13, 0xc6, 0x96, 0x8a, 0xcf, 0x1c, 0x09, 0x90, 0xa6, 0x39, - 0xfb, 0x65, 0x5a, 0x68, 0xa2, 0x15, 0x5e, 0xf9, 0x48, 0x64, 0x0c, 0x7c, - 0x64, 0x0b, 0x4a, 0xe6, 0x73, 0xf2, 0xe0, 0x5a, 0x0c, 0x96, 0x50, 0x16, - 0xf2, 0x45, 0x1b, 0x61, 0x0a, 0x49, 0x89, 0x98, 0xc8, 0x30, 0x30, 0x12, - 0xe4, 0xe6, 0x2c, 0xb8, 0xe5, 0xb7, 0xc8, 0x9d, 0xc1, 0x25, 0x99, 0xb4, - 0xc8, 0x0d, 0x3e, 0x48, 0x45, 0x81, 0x6d, 0x55, 0xcd, 0x9e, 0xac, 0x91, - 0x01, 0xdc, 0x2f, 0xf9, 0x4c, 0xc9, 0x46, 0x68, 0x8e, 0x6b, 0x62, 0x96, - 0x3e, 0x2e, 0xcf, 0x65, 0xd2, 0xd9, 0xa5, 0x55, 0xc4, 0x9f, 0xc6, 0x5b, - 0x40, 0x1e, 0x0e, 0xb8, 0x14, 0x92, 0xdf, 0x02, 0xfe, 0xfe, 0x43, 0x71, - 0xd9, 0xfa, 0xaa, 0x94, 0xd7, 0xca, 0x86, 0x92, 0x83, 0x83, 0xaa, 0x7d, - 0xe0, 0xba, 0xa7, 0xc7, 0x7b, 0x16, 0x95, 0x57, 0x57, 0xbd, 0x45, 0x73, - 0xd5, 0x4a, 0xc7, 0x55, 0x99, 0x1b, 0xe8, 0x20, 0x82, 0x45, 0xef, 0xf5, - 0x3d, 0x52, 0x45, 0x0b, 0xd2, 0x0a, 0xd1, 0x0d, 0x0e, 0x95, 0x9b, 0x73, - 0xfc, 0x7d, 0x7d, 0xd2, 0x93, 0x9d, 0x50, 0x79, 0x8f, 0x0a, 0xdc, 0x9e, - 0x98, 0x63, 0x60, 0x85, 0x16, 0x14, 0xa9, 0x74, 0x4e, 0xeb, 0x0e, 0xff, - 0x8d, 0xc8, 0xbe, 0x65, 0x12, 0x34, 0xa7, 0xd7, 0xca, 0x76, 0x6b, 0xfc, - 0x8b, 0x6f, 0xbb, 0x1d, 0x85, 0x94, 0xae, 0x80, 0x9f, 0xd7, 0xd2, 0x19, - 0xf3, 0xf1, 0x27, 0x3a, 0xa9, 0x9b, 0xf1, 0x92, 0xe7, 0x92, 0xa2, 0xaf, - 0xf9, 0x7d, 0x52, 0x59, 0x08, 0xd4, 0x1c, 0xf1, 0x33, 0x3b, 0x2f, 0x80, - 0xff, 0xf1, 0x50, 0x80, 0x2a, 0xdf, 0xfc, 0x21, 0x1a, 0xcd, 0x91, 0xf0, - 0xdd, 0xbf, 0xff, 0xa4, 0xb1, 0xc1, 0x99, 0x4e, 0x45, 0x6b, 0x1d, 0xcf, - 0x06, 0x9e, 0x38, 0xf8, 0xae, 0x79, 0xd3, 0xaf, 0xb7, 0xc5, 0xf7, 0xc7, - 0x8f, 0x3b, 0xb9, 0x2e, 0x8f, 0x6f, 0x79, 0x3a, 0x92, 0x2b, 0x34, 0x21, - 0x7e, 0x53, 0x02, 0x0d, 0x27, 0x01, 0xd6, 0xd0, 0xa5, 0x5a, 0x46, 0x76, - 0x1b, 0xb3, 0xe7, 0x5c, 0x33, 0x45, 0x44, 0x5e, 0x0c, 0x05, 0xfb, 0x15, - 0xc1, 0xb3, 0x84, 0xe7, 0x73, 0xff, 0x60, 0x97, 0xf0, 0xae, 0x50, 0x00, - 0xff, 0x0d, 0xbf, 0x52, 0x5e, 0xca, 0x42, 0x0c, 0x38, 0x6f, 0xf2, 0xf9, - 0x90, 0x5e, 0xb0, 0x2f, 0x09, 0xf8, 0xa6, 0xf9, 0x96, 0x7e, 0xbe, 0x90, - 0x5d, 0x72, 0x08, 0x41, 0x33, 0x3d, 0xf3, 0xe1, 0x3a, 0x1a, 0x10, 0xb9, - 0xe3, 0x5c, 0xa8, 0x04, 0x4a, 0xae, 0xba, 0xad, 0x41, 0x02, 0x6d, 0xcf, - 0x27, 0x1a, 0x56, 0x4c, 0x0c, 0xfe, 0xb9, 0x1b, 0x8c, 0x96, 0x5c, 0x6f, - 0x85, 0x14, 0x9a, 0xad, 0x17, 0xcf, 0xa7, 0xe1, 0x85, 0xe2, 0xf4, 0x10, - 0x87, 0x5f, 0xa0, 0x01, 0x29, 0x3b, 0x3b, 0x40, 0x03, 0x1d, 0xfd, 0xc0, - 0x0c, 0xaa, 0xa7, 0xc3, 0x90, 0xa9, 0x82, 0xab, 0xba, 0x9a, 0xc8, 0xb2, - 0x8a, 0x50, 0x50, 0x20, 0xb9, 0x56, 0x60, 0xa9, 0x42, 0x54, 0x45, 0x4e, - 0x67, 0x4b, 0x15, 0x0a, 0xf2, 0x18, 0xd1, 0x32, 0x8a, 0x0d, 0xa1, 0x2d, - 0x46, 0x13, 0xe0, 0x76, 0x38, 0xc1, 0xca, 0x45, 0x07, 0x50, 0x63, 0xb0, - 0x6a, 0x70, 0x02, 0x42, 0x01, 0x53, 0x40, 0xb2, 0x82, 0x57, 0x40, 0x76, - 0x40, 0x61, 0x56, 0x79, 0x88, 0xd0, 0x20, 0x28, 0x9e, 0xbb, 0x42, 0x92, - 0x8c, 0xf6, 0xe7, 0x81, 0xad, 0xbf, 0x1b, 0xe7, 0x9d, 0x0d, 0xf1, 0x5c, - 0x6f, 0x5b, 0xba, 0xd5, 0xd6, 0xfa, 0x9d, 0xdd, 0x71, 0x98, 0x99, 0x56, - 0x22, 0x0f, 0xb8, 0x37, 0x58, 0x71, 0xe1, 0xe1, 0xb3, 0xd7, 0x10, 0xf7, - 0x2f, 0x87, 0x5f, 0x97, 0x5c, 0x83, 0x0c, 0x86, 0x4a, 0xf2, 0x7a, 0x1d, - 0x91, 0xda, 0x88, 0xbe, 0x1c, 0x73, 0x45, 0xf4, 0x78, 0x05, 0xf9, 0xb3, - 0xc6, 0xb5, 0x49, 0xc7, 0x83, 0x7d, 0x5e, 0xca, 0x0f, 0x34, 0x16, 0xac, - 0x34, 0xb4, 0x0a, 0x11, 0xa9, 0x01, 0x1a, 0xfc, 0xc1, 0x15, 0xc3, 0xb4, - 0xcc, 0xcb, 0x5e, 0x34, 0x86, 0x17, 0x91, 0x15, 0x7c, 0x5c, 0x33, 0x18, - 0x0a, 0x5a, 0xaa, 0x59, 0x22, 0x4e, 0xff, 0xf1, 0x50, 0x80, 0x2f, 0x9f, - 0xfc, 0x21, 0x1a, 0xcf, 0xb5, 0xc1, 0x07, 0x80, 0x24, 0xa3, 0xb2, 0x43, - 0x1d, 0x88, 0xf0, 0x9a, 0xeb, 0xf4, 0xd7, 0xd4, 0xf5, 0xbf, 0x65, 0xfe, - 0x7e, 0xb5, 0xf9, 0xfb, 0xfa, 0xf6, 0xef, 0xa9, 0x96, 0x25, 0x1c, 0x64, - 0xeb, 0x9a, 0x80, 0xc3, 0xcf, 0xbe, 0xc9, 0x1a, 0x45, 0x9d, 0xb7, 0x92, - 0x6f, 0x21, 0x1b, 0x8c, 0x4d, 0x8e, 0x91, 0x0e, 0x8a, 0xed, 0xb1, 0xca, - 0x08, 0x3a, 0xf5, 0x6e, 0x77, 0xaa, 0x93, 0xc3, 0x5b, 0x0f, 0x8a, 0xfd, - 0x85, 0xee, 0x27, 0xd2, 0x64, 0x37, 0x8c, 0xe2, 0x7b, 0x08, 0xc3, 0xba, - 0xc7, 0x04, 0xea, 0x64, 0x8d, 0x6e, 0x3e, 0x3e, 0xef, 0x4e, 0xe1, 0x3a, - 0x9d, 0x89, 0x86, 0x0b, 0x9e, 0x1f, 0xd2, 0x96, 0xcd, 0x3d, 0x48, 0xbf, - 0x2f, 0x2b, 0x31, 0xc4, 0xf4, 0xb8, 0x86, 0x0d, 0x55, 0x27, 0x57, 0x47, - 0x1c, 0xb1, 0xe0, 0x29, 0x93, 0x9a, 0x30, 0x2f, 0xe3, 0xe9, 0x8b, 0xa0, - 0xab, 0x2f, 0x3e, 0xb3, 0x26, 0xf3, 0x36, 0x9e, 0xf5, 0x1e, 0x49, 0x6c, - 0x78, 0xb3, 0xef, 0x33, 0xc9, 0xf6, 0x87, 0x8a, 0x1c, 0xe5, 0x1b, 0xf8, - 0x27, 0xc9, 0x4e, 0x7d, 0xef, 0x7d, 0x57, 0x39, 0x29, 0xb1, 0x43, 0x01, - 0x1f, 0xad, 0x54, 0xc5, 0xb9, 0xc4, 0x2b, 0x58, 0xf4, 0xb4, 0xa8, 0xe1, - 0x06, 0x71, 0xec, 0x3b, 0x1d, 0xe0, 0xf7, 0x71, 0x93, 0xfe, 0x1c, 0xda, - 0x4a, 0x81, 0x08, 0xea, 0xdc, 0x34, 0x82, 0x2a, 0x4a, 0x23, 0x88, 0x75, - 0xd4, 0x7c, 0xde, 0x51, 0x34, 0xcf, 0xf1, 0x9c, 0x3b, 0xce, 0x74, 0x01, - 0xaa, 0x74, 0xc7, 0xf8, 0xfd, 0x5b, 0x2a, 0xcb, 0xd4, 0xa9, 0x7e, 0xcf, - 0xb2, 0x82, 0xd6, 0x5a, 0xde, 0xf7, 0x45, 0xda, 0x75, 0x3e, 0x21, 0x46, - 0x61, 0xdd, 0xb9, 0x46, 0x1f, 0xbc, 0xbd, 0x9a, 0xe6, 0xa3, 0xa5, 0x3c, - 0xbd, 0xe0, 0xe3, 0xaf, 0x97, 0xdd, 0xeb, 0x7c, 0x6f, 0x5a, 0xef, 0xe3, - 0x94, 0xcb, 0xb9, 0x97, 0x49, 0x8a, 0x5d, 0x6f, 0x58, 0x50, 0x89, 0x42, - 0xf7, 0x28, 0xa4, 0x0c, 0x5d, 0xf2, 0x74, 0xf4, 0xaa, 0x66, 0x07, 0x41, - 0x31, 0xef, 0x78, 0x8a, 0xab, 0xd6, 0xe4, 0x21, 0x07, 0xc2, 0xeb, 0x74, - 0xa3, 0xe4, 0x57, 0xe3, 0xe9, 0xcb, 0xdb, 0xdd, 0xc9, 0x61, 0xd9, 0xf5, - 0xbb, 0x1b, 0xbb, 0xde, 0xa7, 0x6e, 0x7a, 0xf1, 0x19, 0xde, 0xdd, 0xfe, - 0xff, 0x57, 0x5c, 0x63, 0xf2, 0x26, 0x1d, 0x35, 0x5b, 0xb2, 0xf1, 0xf3, - 0x63, 0xb7, 0x05, 0xfa, 0xfd, 0x64, 0x98, 0x70, 0xbd, 0x68, 0x5e, 0x57, - 0x9e, 0xfd, 0x2d, 0xa6, 0x38, 0x46, 0x5a, 0xb3, 0xe7, 0x99, 0xae, 0x4d, - 0x76, 0xe4, 0x33, 0xdf, 0xe8, 0x85, 0x5a, 0x06, 0x45, 0x4e, 0x8e, 0x33, - 0xac, 0x5f, 0xff, 0xf1, 0x50, 0x80, 0x30, 0x1f, 0xfc, 0x21, 0x1a, 0xce, - 0x3e, 0xa9, 0x3f, 0xa6, 0x80, 0xa6, 0xb1, 0x53, 0xd8, 0xca, 0x64, 0x43, - 0x18, 0x99, 0xc7, 0x17, 0xf3, 0xd7, 0x17, 0xdf, 0x8f, 0x69, 0xaf, 0x9f, - 0x6b, 0xfb, 0x7d, 0xf9, 0xe3, 0x9d, 0x5e, 0x75, 0xb9, 0x97, 0x2a, 0x4a, - 0xe1, 0x4d, 0x04, 0x89, 0xee, 0x94, 0x00, 0xdf, 0xba, 0xdd, 0x14, 0x2c, - 0x9d, 0x58, 0xd0, 0x13, 0xf8, 0x8f, 0x9c, 0x69, 0x9d, 0xb7, 0xb2, 0x7a, - 0xdb, 0x81, 0xe6, 0x59, 0x1d, 0x04, 0x62, 0x70, 0x00, 0x80, 0xa8, 0x84, - 0x09, 0x6d, 0xc5, 0xdd, 0x4f, 0x04, 0xa4, 0xc8, 0x10, 0x7a, 0x2a, 0xe9, - 0x4e, 0x5e, 0x7d, 0x48, 0x62, 0xa3, 0xb6, 0x49, 0xa7, 0xf9, 0xff, 0xd6, - 0x20, 0x03, 0x1f, 0x03, 0x13, 0xb8, 0x44, 0x3f, 0x9e, 0x60, 0xa6, 0xd2, - 0x1d, 0x7e, 0x8f, 0x13, 0x55, 0xfe, 0xee, 0x2b, 0x1a, 0xda, 0x9b, 0x8c, - 0xf7, 0xf5, 0x6d, 0x81, 0x8a, 0xc1, 0xab, 0xfd, 0x7b, 0xc8, 0xa1, 0x1b, - 0x54, 0x5b, 0xaf, 0x5c, 0x9b, 0x8c, 0xb7, 0x72, 0x57, 0x5f, 0x65, 0xc5, - 0xd6, 0xcc, 0x5e, 0xc9, 0x5b, 0x17, 0xa0, 0xab, 0x7b, 0x6a, 0x93, 0x75, - 0xfd, 0xc5, 0xe9, 0x6e, 0xa8, 0xcc, 0x7b, 0x49, 0x97, 0x40, 0xa4, 0x39, - 0x7a, 0x74, 0xba, 0x43, 0xe2, 0x28, 0x96, 0x50, 0x4b, 0xd9, 0x79, 0x8e, - 0xde, 0xbd, 0x09, 0x89, 0xf3, 0xa6, 0x07, 0x65, 0xd3, 0xfd, 0xe6, 0xe3, - 0xaa, 0x8b, 0x32, 0x31, 0x44, 0x45, 0x31, 0x9c, 0x52, 0xd2, 0xf4, 0x8b, - 0x86, 0x52, 0x23, 0x86, 0x00, 0x47, 0x30, 0xdc, 0x82, 0x05, 0x25, 0x11, - 0x63, 0xb0, 0xd8, 0x32, 0xdc, 0x4e, 0x96, 0x23, 0xe7, 0x50, 0xd2, 0xee, - 0xb5, 0x10, 0x54, 0x45, 0x49, 0xde, 0x88, 0x56, 0x32, 0xa2, 0x98, 0x43, - 0xcd, 0x4c, 0xb8, 0x49, 0x59, 0x43, 0x42, 0x82, 0x56, 0x69, 0x55, 0xd2, - 0x9f, 0x47, 0x56, 0x0a, 0xdc, 0x02, 0x74, 0xa6, 0x85, 0xb9, 0x18, 0xca, - 0x7f, 0x78, 0x3d, 0xaf, 0xe7, 0xae, 0x39, 0x99, 0xaf, 0x1f, 0x7e, 0x79, - 0xf8, 0xe6, 0x57, 0x1c, 0xea, 0xf3, 0xaa, 0x2a, 0xf2, 0xea, 0xa5, 0x54, - 0x03, 0x8f, 0x12, 0x1e, 0x7f, 0x5a, 0x2f, 0x0f, 0xba, 0x7d, 0x14, 0x84, - 0xfc, 0x11, 0xcb, 0xd7, 0xf2, 0x8c, 0xb5, 0x54, 0x14, 0xa3, 0xaa, 0xc6, - 0x3c, 0x45, 0xf5, 0x1e, 0xe3, 0xdd, 0x42, 0xf0, 0x7b, 0x09, 0x53, 0xf0, - 0x21, 0xb7, 0xf1, 0xb5, 0x82, 0x4a, 0x01, 0x9e, 0x4c, 0xbe, 0xac, 0x28, - 0x4d, 0x4f, 0x7a, 0x04, 0x66, 0x6f, 0xaf, 0xf2, 0xed, 0x13, 0x10, 0xe3, - 0xe8, 0xc8, 0xf3, 0x9c, 0x85, 0x3e, 0xca, 0x40, 0x9f, 0x86, 0x3b, 0xa1, - 0x0b, 0xa8, 0x99, 0x7a, 0xf9, 0x02, 0x10, 0xbc, 0xf4, 0xd6, 0x84, 0x57, - 0x50, 0xe0, 0xff, 0xf1, 0x50, 0x80, 0x29, 0x3f, 0xfc, 0x21, 0x1a, 0xcd, - 0xfc, 0xed, 0xfe, 0x80, 0x00, 0xa5, 0xb1, 0xc3, 0x14, 0xa9, 0x36, 0x20, - 0x00, 0x0d, 0x6e, 0xd9, 0x75, 0x32, 0x5e, 0xf4, 0x4a, 0xbc, 0x75, 0xdf, - 0x1d, 0x5d, 0x57, 0xdc, 0x47, 0x55, 0xb1, 0x27, 0xd5, 0x38, 0xac, 0x52, - 0x31, 0x72, 0x3b, 0x6d, 0x57, 0x3f, 0x2d, 0x26, 0xbe, 0xff, 0x6b, 0xed, - 0xb1, 0xad, 0x00, 0x0c, 0xec, 0x0e, 0x4a, 0x9b, 0x4f, 0x85, 0xd5, 0xe2, - 0x50, 0x22, 0x17, 0xed, 0x1d, 0x4e, 0xd5, 0x54, 0xb9, 0x74, 0x6e, 0xaa, - 0x0a, 0xce, 0xf7, 0x5b, 0xeb, 0xf6, 0xea, 0x4b, 0x0f, 0x0f, 0xd7, 0xfd, - 0x7f, 0xb0, 0x9d, 0x9f, 0xc8, 0x48, 0x63, 0x40, 0x5b, 0xf0, 0xf4, 0xe1, - 0xef, 0x60, 0x01, 0x05, 0x52, 0x00, 0xaf, 0x00, 0x81, 0x0c, 0x40, 0xee, - 0x1e, 0xee, 0x00, 0x62, 0x30, 0xf0, 0xf5, 0x88, 0x30, 0xa5, 0x9f, 0xb0, - 0x00, 0xc2, 0x80, 0x31, 0xcd, 0xea, 0x57, 0x77, 0x73, 0xe6, 0x1b, 0x56, - 0x30, 0x63, 0x05, 0x16, 0xcc, 0x08, 0x77, 0x7a, 0x82, 0x28, 0x65, 0x00, - 0xf5, 0xbe, 0x03, 0xdb, 0x1e, 0x3d, 0x39, 0x77, 0x9d, 0xc7, 0x87, 0xb6, - 0x81, 0x8b, 0xf3, 0xc4, 0x24, 0x75, 0xd1, 0xe1, 0xef, 0x69, 0xf7, 0xc5, - 0xc7, 0xb8, 0x7e, 0x61, 0xeb, 0x8e, 0xc8, 0xe0, 0x00, 0xc8, 0x1f, 0x96, - 0x6d, 0x27, 0xee, 0x3d, 0x4a, 0x47, 0x94, 0xe6, 0x4e, 0xf1, 0x61, 0xdb, - 0x92, 0x5c, 0xd0, 0xd9, 0xb8, 0x00, 0x5b, 0x13, 0xb3, 0x82, 0xac, 0x5c, - 0xce, 0xa6, 0x44, 0x20, 0x51, 0x62, 0xad, 0x99, 0xfd, 0x65, 0xf7, 0xf5, - 0x07, 0x42, 0x16, 0x98, 0xad, 0x00, 0xa5, 0xb1, 0x3b, 0x18, 0x8a, 0xaf, - 0x7b, 0xbe, 0x80, 0xe6, 0xec, 0xef, 0xe3, 0x7b, 0xeb, 0x2e, 0xa6, 0x4b, - 0xcb, 0x8d, 0x4c, 0xaa, 0xbd, 0x26, 0xea, 0xac, 0x02, 0x87, 0x7d, 0xad, - 0x6a, 0x4d, 0xe3, 0xab, 0xcd, 0x16, 0x67, 0xe7, 0xcf, 0x1f, 0x6e, 0xb0, - 0x71, 0x97, 0x29, 0x6f, 0xf8, 0xd9, 0x15, 0xaf, 0xdb, 0x3a, 0x14, 0x4e, - 0x46, 0xbc, 0x02, 0x16, 0xf1, 0x65, 0x5e, 0x22, 0x2f, 0xb6, 0xe4, 0x54, - 0xe2, 0xe1, 0x7a, 0x77, 0x63, 0x40, 0x35, 0x02, 0x6e, 0xc8, 0x86, 0xeb, - 0x09, 0x9a, 0x1a, 0xd1, 0xa1, 0x37, 0x0d, 0x56, 0xef, 0xab, 0xa1, 0x3a, - 0xe8, 0xd0, 0xbb, 0x56, 0xd6, 0x17, 0xc0, 0xff, 0xf1, 0x50, 0x80, 0x32, - 0x7f, 0xfc, 0x21, 0x1a, 0xcf, 0xb7, 0xdd, 0xfb, 0x90, 0x00, 0xa8, 0xb2, - 0x42, 0x19, 0xc8, 0x42, 0x12, 0x15, 0x8c, 0x1e, 0x5e, 0x34, 0xe2, 0x66, - 0x6b, 0xd7, 0xc6, 0xf8, 0xcc, 0xba, 0x95, 0x5c, 0x55, 0x5a, 0xea, 0x54, - 0x4d, 0x75, 0x5d, 0xbe, 0x9a, 0x61, 0x9a, 0x78, 0x00, 0xc3, 0x63, 0x80, - 0x01, 0x07, 0x63, 0xdd, 0xca, 0xd0, 0xfd, 0x84, 0xb1, 0x70, 0x33, 0xc4, - 0xc1, 0xdf, 0x33, 0xdd, 0x7d, 0x4f, 0x1d, 0x0e, 0xcf, 0xa8, 0xd9, 0xa6, - 0xd0, 0x46, 0x7e, 0xd9, 0x23, 0x62, 0x9c, 0x22, 0xcd, 0x11, 0xb5, 0x66, - 0x04, 0xc6, 0x4a, 0x22, 0xae, 0x3d, 0x29, 0xe3, 0x1e, 0x7e, 0x18, 0x69, - 0x1a, 0xed, 0xaf, 0x57, 0x5f, 0xba, 0x52, 0x15, 0x79, 0x9e, 0x73, 0xc5, - 0x14, 0x3b, 0x92, 0x5b, 0xa8, 0x94, 0x74, 0x69, 0x79, 0xa9, 0x7a, 0x69, - 0xca, 0x6a, 0x51, 0x70, 0xaa, 0x41, 0x29, 0x2b, 0x7a, 0xca, 0xfa, 0x7c, - 0xcd, 0x14, 0x72, 0xb4, 0x62, 0xab, 0x75, 0x65, 0x28, 0x70, 0x9b, 0x37, - 0x3d, 0x2b, 0x5a, 0xf6, 0xba, 0x0a, 0x3a, 0x38, 0xf1, 0xe0, 0x5f, 0x04, - 0xda, 0x6d, 0x2c, 0xfb, 0x90, 0xdc, 0x79, 0x76, 0x9a, 0x8f, 0x3b, 0x9a, - 0x68, 0x2a, 0x16, 0xbf, 0x2b, 0x8e, 0x10, 0x97, 0x8f, 0x47, 0xdf, 0xb8, - 0x28, 0xbc, 0x99, 0x2c, 0x3a, 0xe7, 0x56, 0x26, 0xc8, 0x91, 0x5f, 0x40, - 0x34, 0x14, 0x15, 0xeb, 0x21, 0x1a, 0x0a, 0x0a, 0x4d, 0x4a, 0x42, 0x0a, - 0x0a, 0x0a, 0x0b, 0x85, 0xa4, 0x8e, 0xc2, 0x0b, 0x59, 0x0a, 0xeb, 0x28, - 0x5b, 0x6b, 0xe1, 0x26, 0x01, 0x25, 0x75, 0x31, 0x0b, 0xf6, 0xee, 0x7d, - 0x58, 0xd9, 0xca, 0xf8, 0xd6, 0x98, 0xcc, 0xdc, 0xc5, 0x85, 0xaa, 0xa6, - 0x6d, 0x7d, 0x74, 0x68, 0x59, 0x92, 0x6a, 0x16, 0x80, 0x74, 0x3b, 0x8c, - 0x12, 0x7a, 0xb0, 0xc1, 0xe4, 0xae, 0xbc, 0x30, 0xc3, 0xba, 0x49, 0x2b, - 0x9c, 0x59, 0xde, 0x90, 0x0a, 0xb0, 0xeb, 0x3b, 0x6b, 0x8d, 0x55, 0xd3, - 0x50, 0x91, 0x15, 0xb5, 0x10, 0xf6, 0x32, 0x23, 0xdc, 0x13, 0xaf, 0x0d, - 0x71, 0x33, 0x33, 0x3e, 0x37, 0x9c, 0x65, 0xd4, 0xa9, 0x75, 0x58, 0x96, - 0x9e, 0x79, 0xcc, 0x93, 0xbc, 0xb0, 0xec, 0x1b, 0xb6, 0x0b, 0x0b, 0x8d, - 0x32, 0xf8, 0x04, 0xa5, 0x43, 0x56, 0x79, 0x5d, 0xa9, 0xd3, 0x49, 0x45, - 0x18, 0x2e, 0x5b, 0x83, 0xa0, 0x02, 0x9a, 0x8e, 0x56, 0x80, 0xce, 0x54, - 0x51, 0x32, 0x36, 0x8a, 0x8b, 0xc0, 0x9d, 0xc8, 0x60, 0x48, 0x16, 0x9d, - 0x75, 0x00, 0x01, 0xb5, 0xf3, 0xbb, 0x9d, 0x51, 0xbf, 0x72, 0x90, 0x9b, - 0x20, 0xb7, 0x16, 0x73, 0x0c, 0x90, 0xaf, 0x92, 0x56, 0x17, 0xf8, 0xd0, - 0xbf, 0x92, 0xcc, 0x05, 0xc7, 0x42, 0x38, 0x9f, 0x4d, 0x4b, 0x07, 0xaa, - 0xf5, 0x6e, 0x05, 0x3d, 0x91, 0x68, 0x56, 0x69, 0x9a, 0xfe, 0x57, 0x6b, - 0x35, 0xc0, 0xff, 0xf1, 0x50, 0x80, 0x2e, 0x1f, 0xfc, 0x21, 0x1a, 0xcd, - 0xf5, 0xfb, 0xff, 0x90, 0x00, 0xa2, 0xb6, 0x33, 0xd1, 0x6c, 0x56, 0x59, - 0x2b, 0x2f, 0x38, 0xc1, 0x37, 0xc3, 0x2f, 0x2e, 0xa4, 0x44, 0x79, 0xe7, - 0x5d, 0x73, 0xa4, 0x35, 0xa0, 0xe7, 0xb7, 0xe0, 0x60, 0x84, 0x51, 0xc1, - 0x45, 0x3d, 0xf6, 0x96, 0xa0, 0xd3, 0xd7, 0xb0, 0xa4, 0x5f, 0x6c, 0x2b, - 0x6a, 0x5b, 0xd6, 0x1d, 0x54, 0xa1, 0x11, 0x81, 0x68, 0xf3, 0x37, 0xe1, - 0x41, 0x2f, 0x57, 0x96, 0x43, 0xd9, 0xe9, 0x9b, 0xbc, 0x56, 0xc5, 0x19, - 0xf7, 0xc7, 0xe7, 0xc6, 0x54, 0xcf, 0x13, 0xc8, 0x69, 0x63, 0x7d, 0x17, - 0xe8, 0xf2, 0x72, 0x0a, 0xc2, 0xe4, 0x25, 0x39, 0x0d, 0xca, 0xce, 0x54, - 0x31, 0x1b, 0xc4, 0x89, 0x53, 0x29, 0xc3, 0xbe, 0x9a, 0xd4, 0x88, 0x4d, - 0x94, 0x0e, 0x46, 0x50, 0xc2, 0x86, 0x9e, 0x42, 0x36, 0x32, 0x38, 0x21, - 0x5d, 0xa4, 0x22, 0xcd, 0x04, 0x60, 0x2b, 0xbd, 0xc6, 0x92, 0x8d, 0xf0, - 0x41, 0xb6, 0xe3, 0x6d, 0xf8, 0x8b, 0x82, 0xa6, 0xa6, 0x28, 0x02, 0x58, - 0x4d, 0x5a, 0xf9, 0x69, 0x53, 0xd8, 0xd1, 0x43, 0xbe, 0x3a, 0x7f, 0xec, - 0x1e, 0xe6, 0x4d, 0x3e, 0x78, 0x8a, 0xe8, 0x67, 0xaf, 0x55, 0xce, 0xc5, - 0x9c, 0x27, 0xb9, 0x61, 0x0c, 0x82, 0xa0, 0x04, 0xd0, 0x1f, 0x5a, 0xd3, - 0x45, 0x8f, 0xa5, 0xed, 0x7b, 0x15, 0x8a, 0x76, 0x73, 0xe2, 0xb8, 0x7c, - 0x58, 0x2a, 0xe8, 0xb5, 0xfe, 0xd7, 0xf5, 0x5e, 0x67, 0x4e, 0x7a, 0x2d, - 0x92, 0x16, 0x4c, 0x68, 0xf5, 0x60, 0xee, 0xd5, 0xc2, 0xd1, 0x7c, 0xc3, - 0xb2, 0xb8, 0x61, 0x81, 0x2a, 0xd0, 0xd8, 0x5e, 0x14, 0xf5, 0x4c, 0xd2, - 0x00, 0xc8, 0x26, 0xd2, 0x5c, 0x67, 0x32, 0x3e, 0x49, 0x39, 0xd8, 0x53, - 0x8d, 0xb6, 0x36, 0x78, 0x73, 0xff, 0x03, 0x55, 0xd4, 0x59, 0x28, 0xd4, - 0x26, 0x24, 0x0d, 0x45, 0x6a, 0x62, 0x58, 0x55, 0xbe, 0xf0, 0x4a, 0x17, - 0x82, 0x55, 0xb2, 0xf2, 0xea, 0x44, 0xbc, 0xab, 0xc9, 0xc6, 0xee, 0x6f, - 0x16, 0x06, 0xc7, 0x64, 0x14, 0x6d, 0xb1, 0x68, 0xc6, 0xfc, 0xd0, 0xe1, - 0x80, 0x50, 0x82, 0xbf, 0xcf, 0x1e, 0x81, 0x75, 0x03, 0x66, 0xd1, 0xa8, - 0x90, 0x45, 0x50, 0xfc, 0x34, 0x9a, 0x51, 0xaf, 0x9b, 0xd2, 0x8e, 0x09, - 0x93, 0x7d, 0x7e, 0xad, 0xcd, 0xe2, 0x7a, 0xbc, 0x78, 0x51, 0x75, 0xfe, - 0xb0, 0x89, 0xc5, 0x4e, 0x25, 0xc6, 0x17, 0x30, 0x8d, 0x95, 0x37, 0x33, - 0x28, 0xcf, 0xa8, 0xbb, 0x21, 0x8d, 0xb0, 0xcc, 0x6a, 0x94, 0xb5, 0xc0, - 0x63, 0xe3, 0x9d, 0x68, 0x92, 0x06, 0xd5, 0x25, 0x2d, 0x7c, 0xff, 0xf1, - 0x50, 0x80, 0x30, 0x5f, 0xfc, 0x21, 0x1a, 0xcf, 0xd7, 0xff, 0x5f, 0x91, - 0x01, 0xa6, 0xb2, 0x44, 0x98, 0xe8, 0x35, 0x1b, 0x20, 0x3c, 0xf9, 0xfb, - 0x6e, 0x3c, 0xce, 0x67, 0x3f, 0x5c, 0xea, 0xbd, 0x7d, 0x77, 0xe6, 0xa5, - 0x5c, 0x1f, 0x5f, 0x2d, 0x70, 0xb6, 0x24, 0x08, 0x78, 0xd6, 0xb1, 0x0b, - 0xa7, 0x8e, 0x01, 0x14, 0x6f, 0x7a, 0x33, 0xa2, 0x05, 0xe2, 0xb5, 0xd0, - 0x6e, 0xed, 0x78, 0x12, 0x43, 0x7a, 0x76, 0xf1, 0x69, 0x4d, 0x05, 0xa8, - 0xb0, 0xb6, 0x50, 0x0e, 0xd8, 0x4c, 0xe0, 0x9a, 0x12, 0x95, 0x24, 0x78, - 0x34, 0xa8, 0x94, 0x5a, 0x0d, 0xcd, 0x35, 0x5a, 0x8c, 0x70, 0x85, 0x0d, - 0x90, 0x16, 0xe7, 0x52, 0x18, 0x91, 0x64, 0x49, 0x01, 0xd0, 0x95, 0xa3, - 0x39, 0xfa, 0x8e, 0x98, 0x69, 0xe1, 0x99, 0xd6, 0x49, 0xb4, 0x99, 0xd2, - 0x95, 0xe0, 0xf2, 0x0b, 0xc9, 0x5a, 0xdf, 0x90, 0xdf, 0x8c, 0x52, 0xcc, - 0x62, 0x84, 0x80, 0x74, 0x84, 0x28, 0x91, 0x20, 0x11, 0x81, 0x81, 0xd3, - 0x71, 0x08, 0x39, 0x39, 0x20, 0x4c, 0x4e, 0xe3, 0x65, 0xee, 0x88, 0x73, - 0x0f, 0x9e, 0xaf, 0x79, 0x46, 0x7f, 0x7a, 0x35, 0x29, 0xfe, 0x8a, 0x79, - 0xee, 0x7e, 0x87, 0x52, 0x43, 0xfb, 0xee, 0x7c, 0x8f, 0x52, 0xbd, 0xe1, - 0x72, 0xb8, 0xeb, 0x91, 0x95, 0x01, 0xbf, 0x7f, 0x3d, 0x58, 0x15, 0x51, - 0xbe, 0x18, 0xe3, 0xca, 0xa5, 0x15, 0x16, 0xb6, 0x9d, 0x97, 0x13, 0xef, - 0xa0, 0x02, 0x18, 0xd6, 0x12, 0x49, 0x1e, 0x2a, 0xa4, 0xe0, 0xd8, 0x18, - 0x10, 0x26, 0xd1, 0x60, 0xc3, 0xa9, 0x04, 0x2d, 0x28, 0x4b, 0x20, 0x5a, - 0x40, 0xd5, 0xd6, 0xb3, 0x1e, 0x2d, 0x79, 0x4b, 0x48, 0x0c, 0x35, 0x35, - 0x11, 0x62, 0x69, 0x6b, 0xb8, 0x58, 0xd3, 0x4d, 0x34, 0xe0, 0x46, 0x80, - 0xc4, 0x31, 0x06, 0x22, 0x78, 0xe7, 0x41, 0x05, 0x20, 0x45, 0x30, 0x3a, - 0xcc, 0x2b, 0x3d, 0xd4, 0xb0, 0x21, 0x35, 0x6a, 0x00, 0x14, 0xce, 0xf6, - 0x42, 0x97, 0xde, 0x1e, 0x6f, 0xc4, 0xdb, 0xcc, 0xe6, 0x73, 0xf5, 0xce, - 0xab, 0x7d, 0x66, 0xaa, 0x55, 0xcd, 0xab, 0xae, 0x6a, 0xee, 0x92, 0xaa, - 0xa0, 0xbf, 0x5f, 0xcf, 0x9c, 0xc6, 0x6b, 0xcb, 0xd0, 0xf5, 0x2c, 0xb0, - 0xf8, 0x29, 0xfc, 0x39, 0x86, 0x70, 0xcf, 0x0b, 0x67, 0xd9, 0xf3, 0x23, - 0x93, 0xd8, 0xfa, 0x4a, 0x15, 0x91, 0x03, 0x3f, 0xb4, 0x08, 0x18, 0x11, - 0xf9, 0x95, 0x08, 0x1c, 0xfd, 0x8d, 0x70, 0x18, 0xb1, 0x83, 0x8e, 0xd4, - 0x92, 0x6b, 0x38, 0xbc, 0x1a, 0x19, 0x8b, 0x0b, 0xa2, 0xbd, 0xbf, 0x77, - 0xe5, 0xb8, 0x5d, 0xce, 0x45, 0x56, 0x9a, 0xd8, 0x49, 0x0c, 0x23, 0xbb, - 0xf9, 0x6e, 0xe9, 0x73, 0x9c, 0xd2, 0xab, 0x2a, 0xe1, 0x78, 0x52, 0x78, - 0xff, 0xf1, 0x50, 0x80, 0x31, 0x9f, 0xfc, 0x21, 0x1a, 0xcb, 0x76, 0xdf, - 0xff, 0x30, 0x80, 0xa3, 0xb2, 0x52, 0x99, 0x8e, 0xa4, 0x0b, 0x19, 0x15, - 0xd7, 0x5f, 0x32, 0x9e, 0xdb, 0xe6, 0x57, 0xd7, 0xbf, 0xb6, 0x73, 0xf1, - 0xeb, 0xcf, 0x7e, 0x71, 0x74, 0x97, 0x59, 0x73, 0x5e, 0x69, 0x59, 0xc0, - 0x37, 0x25, 0xdd, 0xed, 0x7d, 0x64, 0x33, 0x3f, 0x7e, 0xb6, 0xb3, 0x42, - 0xdf, 0x7a, 0xc9, 0x91, 0xe8, 0x5e, 0x71, 0x95, 0x83, 0x19, 0xf8, 0xe1, - 0x8c, 0xda, 0x46, 0x01, 0xfc, 0x27, 0xa3, 0x80, 0x8d, 0x96, 0x64, 0x15, - 0x55, 0x09, 0x0c, 0x47, 0x96, 0xe9, 0xaf, 0x06, 0xfd, 0xc5, 0x64, 0xa3, - 0xd5, 0x8d, 0xff, 0xde, 0x38, 0x10, 0x96, 0x3f, 0xaf, 0xb3, 0xb8, 0x0a, - 0x9d, 0x34, 0x63, 0x39, 0xa5, 0x94, 0xed, 0x64, 0xc6, 0x36, 0x24, 0xc6, - 0xe0, 0x06, 0x93, 0x05, 0x46, 0x28, 0x27, 0x2a, 0xdb, 0x5b, 0x0a, 0x64, - 0xdb, 0x71, 0x33, 0x67, 0x1b, 0xd0, 0xd5, 0x32, 0x6a, 0x72, 0x91, 0x9a, - 0xaa, 0x6e, 0x17, 0xa9, 0x29, 0xe7, 0xdb, 0x49, 0x74, 0x5d, 0xc6, 0x2a, - 0xce, 0x3b, 0x2d, 0x18, 0xbb, 0x8a, 0x09, 0x66, 0xcb, 0xcf, 0x13, 0x46, - 0x35, 0x37, 0x0c, 0xe5, 0xdc, 0x69, 0x59, 0x58, 0x99, 0x32, 0x0e, 0x08, - 0x02, 0xe1, 0x8c, 0x71, 0xbc, 0xd8, 0x02, 0x64, 0x17, 0xf2, 0xb0, 0x12, - 0x29, 0x43, 0x3e, 0x64, 0x46, 0x24, 0xda, 0x05, 0xf1, 0xaa, 0x4c, 0xb2, - 0x14, 0x2f, 0x5a, 0xfc, 0x92, 0x83, 0x18, 0x60, 0xf2, 0x34, 0x27, 0x25, - 0xe7, 0x89, 0xdb, 0x85, 0xf7, 0x3d, 0xae, 0xf3, 0x35, 0xde, 0x46, 0xe4, - 0xc1, 0x3d, 0x4e, 0xd0, 0xa3, 0x4a, 0x54, 0xdf, 0xb0, 0x4a, 0x0f, 0x48, - 0x19, 0x83, 0xdc, 0x29, 0x4f, 0x2e, 0x26, 0x08, 0x56, 0x80, 0x56, 0x35, - 0x23, 0x35, 0x3d, 0xe4, 0x02, 0xe4, 0x94, 0x2b, 0xaa, 0x55, 0x9b, 0x06, - 0x8d, 0x82, 0x7a, 0xe2, 0x9a, 0xc8, 0x00, 0x28, 0xac, 0x90, 0x79, 0x5a, - 0x9b, 0xde, 0x1c, 0x75, 0xf3, 0xc7, 0x2e, 0x1c, 0xde, 0x7d, 0x7b, 0xfb, - 0x67, 0x3f, 0x1e, 0xbc, 0xf7, 0xe7, 0x15, 0xce, 0x57, 0xb6, 0x73, 0xac, - 0x9a, 0xa5, 0x20, 0x2f, 0xfd, 0x56, 0x51, 0xe7, 0x08, 0xfd, 0x4f, 0x7e, - 0x8e, 0xa0, 0xc0, 0x21, 0x47, 0x48, 0x7e, 0xbf, 0x09, 0xcb, 0x7e, 0x52, - 0x39, 0x11, 0x37, 0xe2, 0x74, 0x1b, 0x34, 0x3b, 0xdf, 0xd9, 0x77, 0xde, - 0x5a, 0x7b, 0x71, 0xd0, 0x71, 0xfb, 0x95, 0x6b, 0x67, 0x96, 0x8b, 0x1f, - 0x67, 0x76, 0x6b, 0x35, 0xb6, 0x60, 0x4a, 0xea, 0xf3, 0xe5, 0xc2, 0xb5, - 0x7c, 0x85, 0x21, 0x2c, 0xe7, 0x17, 0x71, 0xdd, 0xdb, 0xee, 0xf1, 0x94, - 0x35, 0x8a, 0xa8, 0xab, 0x6f, 0xbb, 0xe3, 0x71, 0x2b, 0xba, 0xc4, 0xa0, - 0x9c, 0xfa, 0x93, 0x09, 0xbb, 0xc5, 0x44, 0xaa, 0xba, 0x21, 0x2a, 0x70, - 0xff, 0xf1, 0x50, 0x80, 0x32, 0x3f, 0xfc, 0x21, 0x2a, 0xcf, 0xf9, 0xb7, - 0x7f, 0x8e, 0x7f, 0x9e, 0xb4, 0xc2, 0xd8, 0x6a, 0xe6, 0x2c, 0x04, 0x03, - 0x1a, 0x9c, 0xdb, 0x8e, 0x7a, 0xe7, 0x7f, 0x1e, 0x3a, 0xef, 0xa9, 0x97, - 0x91, 0xf7, 0xf9, 0xcf, 0x68, 0xfa, 0xda, 0x2c, 0x7d, 0xac, 0x88, 0x6e, - 0x92, 0x87, 0x13, 0xdb, 0x08, 0x2c, 0x5d, 0x5f, 0x5a, 0x92, 0xdb, 0xa3, - 0x62, 0x48, 0x1f, 0x41, 0x4c, 0x3e, 0xba, 0x7f, 0x6c, 0xdd, 0x7f, 0xf0, - 0x8d, 0xf3, 0x65, 0x43, 0x0e, 0x32, 0x99, 0x6c, 0xbf, 0xae, 0x50, 0x1c, - 0x89, 0xfa, 0x44, 0x00, 0xd3, 0x10, 0x61, 0x85, 0x31, 0xda, 0xad, 0x44, - 0xa0, 0xe1, 0xb5, 0xc6, 0xf3, 0x9c, 0xe1, 0xb7, 0xdd, 0x73, 0x18, 0x00, - 0x1d, 0xd6, 0x1c, 0x1c, 0x0f, 0x58, 0x56, 0x90, 0x2d, 0x8a, 0x5b, 0x5e, - 0x66, 0x6f, 0xa3, 0xef, 0xc2, 0xe0, 0x18, 0x8d, 0xe2, 0x3b, 0x3e, 0x9c, - 0x73, 0xc6, 0x69, 0x5a, 0x55, 0x67, 0x1a, 0x8b, 0x02, 0x66, 0x5a, 0x8a, - 0x9f, 0x54, 0x4d, 0x67, 0x06, 0x62, 0x08, 0xbf, 0xa3, 0xb7, 0x0a, 0x59, - 0x99, 0x42, 0xfa, 0xbd, 0x49, 0x16, 0x80, 0x88, 0xfe, 0x7d, 0x34, 0x15, - 0x15, 0xaa, 0xdf, 0x0e, 0xbe, 0xbf, 0xc6, 0x3a, 0x11, 0x38, 0x48, 0x2b, - 0xab, 0x00, 0x01, 0x7e, 0xbf, 0x6c, 0x81, 0x48, 0x6e, 0x77, 0xf4, 0xf7, - 0xdc, 0x2d, 0x86, 0x59, 0x92, 0x3f, 0x54, 0xa6, 0x53, 0x4b, 0xbb, 0x47, - 0x5f, 0xbb, 0xc4, 0x52, 0x15, 0x69, 0xf3, 0x4d, 0x61, 0xf0, 0xf5, 0xf8, - 0x4b, 0x56, 0x67, 0xa3, 0x57, 0x04, 0x89, 0xee, 0xba, 0x59, 0x7c, 0x92, - 0xf6, 0xe1, 0xa5, 0x6e, 0x0a, 0x69, 0xac, 0xf3, 0xed, 0xf0, 0x9f, 0x29, - 0xe4, 0xad, 0x48, 0x1d, 0xc2, 0xed, 0xfd, 0xfe, 0xec, 0x56, 0xac, 0xe4, - 0xef, 0x9b, 0x05, 0x32, 0x99, 0x5b, 0xeb, 0xe0, 0x74, 0x36, 0x14, 0x0b, - 0x38, 0xd1, 0x83, 0x9f, 0xae, 0xc6, 0xb7, 0x2e, 0x06, 0x20, 0x72, 0xf2, - 0x94, 0xe3, 0x2a, 0x7a, 0x0f, 0x68, 0x82, 0x30, 0xd4, 0xdc, 0xf0, 0x02, - 0x4c, 0x38, 0xe7, 0xae, 0x5d, 0x78, 0xeb, 0xbe, 0xa6, 0x5f, 0x2d, 0xfd, - 0x6f, 0x96, 0xb7, 0xc6, 0xe4, 0x40, 0xdc, 0xba, 0x53, 0xf1, 0xb4, 0x32, - 0x8a, 0x78, 0x01, 0x12, 0xe5, 0xb9, 0xf7, 0x7c, 0x81, 0x57, 0x4b, 0x69, - 0x90, 0xd2, 0x2c, 0x1c, 0x00, 0x38, 0xbc, 0xf8, 0x8d, 0xce, 0x44, 0xa4, - 0x8a, 0x7b, 0x09, 0xee, 0xc6, 0x0b, 0x90, 0xe1, 0x84, 0x96, 0xe7, 0x8d, - 0x13, 0xbc, 0xe0, 0x80, 0x07, 0x24, 0x3b, 0x02, 0x9c, 0x25, 0xad, 0x3d, - 0xd5, 0x2b, 0x03, 0x35, 0xe8, 0xf5, 0x3a, 0x05, 0x96, 0x7f, 0xe2, 0x8e, - 0x7a, 0xd2, 0xf2, 0xb8, 0x67, 0xe7, 0xfa, 0xe4, 0x0b, 0x8a, 0x2a, 0xf8, - 0xe7, 0xa2, 0x2a, 0xaa, 0xf0, 0x5c, 0x46, 0xb5, 0xf2, 0xe4, 0xb0, 0x4d, - 0x40, 0xae, 0x4b, 0x37, 0x7c, 0xff, 0xf1, 0x50, 0x80, 0x3d, 0x5f, 0xfc, - 0x21, 0x4c, 0xcc, 0xca, 0x20, 0x10, 0xc0, 0x44, 0x51, 0x4b, 0xd9, 0x88, - 0x48, 0x2d, 0xf7, 0x0a, 0xcc, 0x36, 0x48, 0x8d, 0x41, 0x53, 0x66, 0x3c, - 0x76, 0xeb, 0xbe, 0xbb, 0xf8, 0xf7, 0x0f, 0x6f, 0xc7, 0xdb, 0xf1, 0x6f, - 0xe9, 0xd8, 0x3d, 0xbd, 0x3f, 0xc7, 0x3e, 0x3b, 0x3d, 0xbc, 0x7d, 0xde, - 0xd5, 0xe7, 0x07, 0x2d, 0x3b, 0x6b, 0x19, 0x63, 0x49, 0xc7, 0xcf, 0x13, - 0x2c, 0x67, 0xc4, 0xff, 0x76, 0x15, 0x2d, 0x7b, 0x58, 0x74, 0xa8, 0xa3, - 0xd7, 0x0b, 0xcd, 0x6a, 0x96, 0x91, 0x63, 0x30, 0x70, 0x2c, 0x45, 0xe6, - 0x5a, 0x64, 0x0f, 0xae, 0xa5, 0x2f, 0xe4, 0x18, 0x31, 0x92, 0x42, 0x81, - 0x68, 0xc5, 0x50, 0x48, 0x30, 0x02, 0x36, 0x6c, 0x6d, 0x7f, 0xd3, 0xfc, - 0x1e, 0xcf, 0xa7, 0xc1, 0x9b, 0x1a, 0x76, 0xb8, 0x39, 0x33, 0x14, 0x99, - 0x49, 0x81, 0x8b, 0xf8, 0xff, 0x9f, 0xe6, 0xfe, 0x98, 0x38, 0x7f, 0xcf, - 0xc3, 0xcb, 0xc4, 0x56, 0x27, 0x03, 0x30, 0x60, 0x15, 0x06, 0x9f, 0x64, - 0xb4, 0xcf, 0x9b, 0x09, 0x94, 0x3a, 0xab, 0xc1, 0x67, 0x0d, 0x8b, 0x52, - 0x78, 0x26, 0xc4, 0xb1, 0xe1, 0xb3, 0x41, 0x94, 0x37, 0x87, 0x4f, 0xdb, - 0xe0, 0xd2, 0xab, 0x72, 0xfe, 0x34, 0xe0, 0x88, 0xe2, 0x2c, 0x01, 0xc5, - 0x41, 0x73, 0x78, 0x1f, 0xfa, 0xa7, 0x3c, 0xc3, 0xb8, 0x96, 0x4e, 0xcf, - 0xa2, 0xfd, 0xca, 0x07, 0x42, 0xe9, 0x5d, 0x28, 0x0f, 0xdd, 0x54, 0x56, - 0xdb, 0xc2, 0xe3, 0x33, 0x86, 0x53, 0x86, 0x35, 0x24, 0x01, 0xab, 0xbb, - 0xe1, 0xf5, 0x76, 0x63, 0x8f, 0x44, 0x49, 0x8d, 0xcb, 0x02, 0x2b, 0x55, - 0x24, 0x82, 0xce, 0x7b, 0xfb, 0x8c, 0x50, 0xde, 0x59, 0x23, 0xb9, 0x3b, - 0x2e, 0x70, 0xab, 0x46, 0x8c, 0x13, 0xa3, 0x5e, 0xe0, 0xe9, 0xad, 0x9a, - 0x97, 0x47, 0x27, 0x8e, 0x35, 0x60, 0xee, 0x64, 0x76, 0x57, 0x89, 0x2a, - 0xc6, 0x45, 0x5f, 0x02, 0xc7, 0x20, 0x74, 0x23, 0xcb, 0xda, 0xa4, 0xaf, - 0x98, 0x1f, 0x36, 0xda, 0x79, 0xa5, 0x6a, 0xdc, 0x34, 0x3f, 0x6a, 0x33, - 0xa3, 0xe6, 0x18, 0x0c, 0xa8, 0x02, 0xe7, 0xb6, 0x7c, 0x4f, 0xe8, 0x06, - 0x4e, 0x2a, 0x05, 0xe7, 0x9f, 0xdf, 0xbd, 0x73, 0xc0, 0x69, 0x88, 0xb4, - 0x37, 0x57, 0xc9, 0x3d, 0xc5, 0x25, 0x4b, 0x01, 0x9e, 0x38, 0xc4, 0x38, - 0x67, 0x7d, 0x54, 0xed, 0xc2, 0x60, 0x0f, 0x29, 0x56, 0x33, 0x65, 0x38, - 0x75, 0x71, 0xfb, 0x23, 0xa5, 0x53, 0xec, 0x1e, 0xd4, 0xa2, 0xa4, 0x43, - 0x47, 0xe2, 0x73, 0xe7, 0xe3, 0xc7, 0x7a, 0x25, 0x51, 0x6c, 0x97, 0x45, - 0xb7, 0x75, 0x64, 0xbc, 0x02, 0x31, 0x7d, 0x5f, 0xad, 0xcf, 0xb3, 0xda, - 0xe3, 0xd5, 0x84, 0xc1, 0xa5, 0x93, 0xff, 0x0f, 0xbe, 0x7e, 0x43, 0xed, - 0x9f, 0x7c, 0xfc, 0x87, 0xaf, 0xfa, 0xdf, 0xe0, 0xfe, 0xff, 0xeb, 0xfc, - 0x5f, 0x31, 0xad, 0xcc, 0x72, 0x26, 0x2a, 0xdc, 0x3a, 0x9b, 0xae, 0x2c, - 0x19, 0x54, 0x2b, 0x3a, 0xde, 0x8e, 0xd2, 0xd1, 0x1c, 0xfe, 0x55, 0xb0, - 0x7f, 0x83, 0x63, 0xa9, 0x41, 0x1a, 0xef, 0xb3, 0xa5, 0x2a, 0x03, 0x35, - 0x5b, 0xa9, 0x60, 0x2f, 0x96, 0xe5, 0xaa, 0x00, 0xa5, 0xa5, 0x64, 0xa3, - 0x9c, 0x4f, 0x44, 0x27, 0xed, 0x80, 0xd7, 0xce, 0x98, 0xcd, 0xb3, 0x4c, - 0x00, 0x9a, 0xd4, 0x7c, 0x1c, 0x35, 0x13, 0xcf, 0xaf, 0xb6, 0xe3, 0xab, - 0xb8, 0xaa, 0xf7, 0x2a, 0xad, 0x54, 0xc8, 0x61, 0xca, 0x06, 0x8f, 0x26, - 0x36, 0x96, 0x4f, 0xff, 0xf1, 0x50, 0x80, 0x30, 0xbf, 0xfc, 0x21, 0x7a, - 0xca, 0x34, 0x00, 0x13, 0x88, 0x00, 0x9e, 0xb8, 0xb0, 0xa4, 0x50, 0x16, - 0x11, 0x98, 0x4c, 0xc5, 0x81, 0x00, 0x00, 0x00, 0x0b, 0xaa, 0x1a, 0x8d, - 0x7b, 0x56, 0xb9, 0xfc, 0x7c, 0x88, 0xef, 0xb4, 0x08, 0xc7, 0x5c, 0xaa, - 0x7a, 0x26, 0x66, 0x6f, 0xae, 0x08, 0xfa, 0x84, 0x6e, 0xd8, 0x64, 0x92, - 0xc3, 0x7c, 0xc0, 0xe8, 0x41, 0x52, 0x2f, 0x9a, 0x04, 0x4b, 0x2c, 0xd2, - 0xbd, 0x2a, 0x94, 0xb6, 0x79, 0x91, 0xae, 0xbe, 0x72, 0x62, 0x3c, 0xad, - 0x12, 0xa7, 0xb0, 0x30, 0xb9, 0x4a, 0x14, 0x52, 0xc6, 0x44, 0x7b, 0x65, - 0xb7, 0x52, 0xbb, 0xba, 0x84, 0xba, 0x68, 0x46, 0x6b, 0x3d, 0xb3, 0xed, - 0x52, 0xa9, 0xc9, 0x59, 0x56, 0x3a, 0xa2, 0x1e, 0xde, 0xbf, 0x11, 0x25, - 0x5a, 0x94, 0xdc, 0x66, 0x92, 0x8b, 0x59, 0xf0, 0xc4, 0x80, 0x10, 0x39, - 0xfb, 0xeb, 0x4f, 0x41, 0x7d, 0x58, 0xf3, 0xef, 0xf4, 0xed, 0xf4, 0x24, - 0x4a, 0x16, 0xd3, 0x34, 0xfa, 0x8e, 0x17, 0x69, 0x84, 0xe6, 0x76, 0xad, - 0xce, 0xcd, 0xa8, 0x54, 0xd5, 0x31, 0x0d, 0xd4, 0x87, 0x08, 0x69, 0xb7, - 0xa7, 0x66, 0xc0, 0xd6, 0x8e, 0xb3, 0xcf, 0x6e, 0x34, 0x9c, 0x36, 0x57, - 0xbc, 0xf7, 0xca, 0x6a, 0x4a, 0x38, 0x9e, 0x14, 0x05, 0x0a, 0x20, 0xa2, - 0xe4, 0x33, 0x00, 0x96, 0xf7, 0xd2, 0xa5, 0xa5, 0xab, 0x02, 0x00, 0x72, - 0x35, 0x75, 0x35, 0xb3, 0xb3, 0x31, 0x57, 0x27, 0x0b, 0xae, 0x9a, 0xd3, - 0x19, 0x97, 0x3a, 0x1b, 0xa6, 0xd4, 0xf6, 0xd7, 0x3c, 0xa5, 0xa0, 0x61, - 0x91, 0xec, 0x16, 0x55, 0x19, 0x23, 0x7f, 0x26, 0x48, 0xe0, 0xaa, 0xc8, - 0xe4, 0x73, 0xfd, 0x1b, 0xd3, 0xd8, 0x6b, 0x7b, 0x8d, 0x4b, 0xa6, 0xdb, - 0x6b, 0x38, 0xdf, 0x40, 0x6f, 0x90, 0x07, 0x11, 0x4d, 0x6a, 0x82, 0xb2, - 0x50, 0x9e, 0xd6, 0x0f, 0x85, 0xf1, 0xee, 0x7c, 0x3d, 0xfe, 0x3d, 0xf5, - 0x7b, 0x9b, 0xfe, 0x7d, 0xf6, 0x94, 0xeb, 0xbb, 0xd2, 0xe4, 0xac, 0xbe, - 0x2a, 0x66, 0x95, 0xbf, 0xbd, 0x05, 0x35, 0xb4, 0xa9, 0xbd, 0x95, 0x6e, - 0x8c, 0x57, 0x31, 0xb0, 0x7d, 0x79, 0x85, 0xa8, 0xbf, 0x14, 0x3a, 0x5b, - 0x2e, 0x19, 0x1d, 0x70, 0x2c, 0x95, 0x9c, 0xc6, 0x15, 0x13, 0xc2, 0xb7, - 0x89, 0xd6, 0x1c, 0x62, 0x4f, 0xa1, 0xcd, 0x4d, 0xe8, 0x08, 0x45, 0x3c, - 0x59, 0xd7, 0x51, 0x88, 0x06, 0x66, 0x68, 0x26, 0x85, 0x99, 0x13, 0xce, - 0x55, 0xa5, 0x83, 0x4a, 0x98, 0x15, 0xe2, 0x44, 0xd0, 0xcb, 0xd4, 0x60, - 0x40, 0x90, 0x21, 0x08, 0x5c, 0x58, 0xc0, 0x62, 0x07, 0x30, 0x92, 0x2e, - 0x63, 0x65, 0x77, 0x71, 0xed, 0xd7, 0x5b, 0x0d, 0xc1, 0x93, 0xf7, 0xce, - 0x39, 0x33, 0xaf, 0x62, 0x96, 0x72, 0xc2, 0xf0, 0xff, 0xf1, 0x50, 0x80, - 0x27, 0xbf, 0xfc, 0x21, 0x1a, 0xcf, 0xda, 0xfd, 0x69, 0x00, 0x08, 0xa3, - 0xb5, 0x31, 0x95, 0x42, 0x44, 0x3b, 0x18, 0x0b, 0xc1, 0x78, 0x4a, 0xd6, - 0xd2, 0xb5, 0xb9, 0xf1, 0xf3, 0xed, 0x3c, 0x7d, 0x77, 0x5c, 0x6b, 0x6f, - 0x3d, 0x66, 0xfd, 0xb6, 0x1c, 0x33, 0xb0, 0xe8, 0x74, 0xfd, 0x4f, 0xfc, - 0xd4, 0xe6, 0xae, 0xab, 0x24, 0xa7, 0xe8, 0xa9, 0x8c, 0x72, 0x37, 0xaf, - 0x57, 0x88, 0x04, 0x8a, 0x50, 0x85, 0xe2, 0x53, 0x38, 0x58, 0xfb, 0x59, - 0x0e, 0xb7, 0xbd, 0x91, 0x16, 0x4a, 0x01, 0xe3, 0x17, 0x55, 0x52, 0xcf, - 0x39, 0x69, 0x3f, 0x57, 0x4c, 0xc8, 0x66, 0x56, 0x38, 0xde, 0xa5, 0x44, - 0xd5, 0x10, 0xaf, 0x44, 0x16, 0xb6, 0x2c, 0x55, 0xcd, 0xef, 0x96, 0xaa, - 0x2f, 0x4a, 0x43, 0xcb, 0x30, 0xe1, 0x7f, 0xe5, 0xd6, 0x78, 0x65, 0xf9, - 0x6a, 0x5f, 0x0b, 0x0a, 0x29, 0xae, 0x23, 0x13, 0x47, 0x5a, 0xc8, 0xee, - 0xd2, 0xf2, 0x2b, 0x49, 0xf2, 0xcb, 0x1c, 0x54, 0xa4, 0x58, 0xab, 0x27, - 0x4d, 0x8c, 0xae, 0x84, 0x9b, 0x28, 0x90, 0xb3, 0x54, 0x13, 0xce, 0xa4, - 0x33, 0x3c, 0x25, 0xcc, 0xd3, 0xe0, 0x0a, 0xc9, 0xdc, 0x2c, 0xad, 0x60, - 0xd6, 0x70, 0x79, 0x08, 0x9e, 0xa6, 0x62, 0x92, 0xf3, 0x02, 0x4a, 0x61, - 0xaf, 0x36, 0x67, 0x92, 0x40, 0x8a, 0x97, 0xae, 0x79, 0x81, 0xa1, 0xd6, - 0x9a, 0x41, 0xea, 0x13, 0x0e, 0xe9, 0x2c, 0x14, 0x5b, 0x20, 0xa4, 0x08, - 0x07, 0x1d, 0x82, 0x0d, 0x8c, 0xc1, 0x20, 0x46, 0xa9, 0x30, 0xe0, 0xaf, - 0x68, 0xb4, 0xc7, 0x4c, 0xd6, 0x35, 0x6f, 0x85, 0xac, 0xf2, 0x39, 0x20, - 0x51, 0xc2, 0x65, 0xa4, 0x5f, 0x80, 0x17, 0x92, 0x97, 0x84, 0xa1, 0x28, - 0x1d, 0x4f, 0x1c, 0x64, 0x95, 0x78, 0xac, 0x92, 0x81, 0xf1, 0xb8, 0xbb, - 0x4a, 0x67, 0x18, 0x42, 0xf9, 0x33, 0x9c, 0x86, 0x09, 0xa8, 0x8f, 0xe4, - 0xd2, 0xcf, 0x91, 0xa2, 0x91, 0xab, 0xe0, 0x0b, 0x75, 0x3e, 0x13, 0x6a, - 0x34, 0xf2, 0x83, 0x83, 0x55, 0x71, 0xa3, 0xc4, 0x97, 0x44, 0x8c, 0x2a, - 0x75, 0xb2, 0xad, 0x90, 0x86, 0x12, 0x21, 0xa9, 0x56, 0x72, 0x2e, 0x11, - 0x04, 0x83, 0x91, 0x14, 0x48, 0xe2, 0x8b, 0x42, 0x65, 0x75, 0xd5, 0x00, - 0x07, 0xff, 0xf1, 0x50, 0x80, 0x33, 0xbf, 0xfc, 0x21, 0x1a, 0xcf, 0x53, - 0xf0, 0xbe, 0x00, 0x00, 0x9b, 0xb5, 0x51, 0x64, 0xec, 0x25, 0x62, 0x0a, - 0x83, 0x04, 0x1b, 0xd6, 0xf5, 0xb6, 0x81, 0xb1, 0xad, 0xe8, 0x36, 0xd6, - 0xf8, 0xd6, 0xe6, 0xae, 0xdf, 0x8f, 0x52, 0x70, 0x30, 0x47, 0x13, 0x7b, - 0x48, 0x41, 0x95, 0x80, 0x3a, 0xb5, 0xa1, 0x63, 0x83, 0x71, 0xf2, 0x65, - 0x40, 0x1c, 0x35, 0x74, 0x82, 0x23, 0xab, 0x6b, 0x58, 0xe3, 0x16, 0x6d, - 0xf0, 0x68, 0xd8, 0x24, 0x24, 0xaf, 0x30, 0x2c, 0x28, 0x2f, 0x6a, 0xda, - 0x72, 0x95, 0x68, 0x4b, 0xd2, 0x90, 0x84, 0x0c, 0x72, 0xee, 0x38, 0x4d, - 0xdf, 0xcf, 0x79, 0x9b, 0xb3, 0xa7, 0x88, 0xa8, 0xa8, 0x3a, 0x9f, 0xe1, - 0x3c, 0x86, 0x4a, 0x56, 0xfd, 0x98, 0xae, 0x6b, 0xa6, 0xf0, 0x59, 0xb2, - 0xe3, 0x2e, 0xa7, 0x8d, 0x7f, 0x53, 0x21, 0xe5, 0x37, 0xfb, 0x05, 0x5b, - 0x1f, 0x7c, 0xc8, 0x9b, 0xc7, 0x8c, 0xdc, 0x05, 0xf2, 0x5f, 0x44, 0x77, - 0x7f, 0x1d, 0x05, 0x80, 0xc4, 0x6c, 0x06, 0xb2, 0x19, 0x91, 0x16, 0x02, - 0xb3, 0xac, 0x60, 0x1a, 0xee, 0x11, 0x5a, 0x80, 0x02, 0xf7, 0x9b, 0xe3, - 0xc9, 0x52, 0x18, 0xd8, 0x55, 0x4a, 0x85, 0x90, 0x73, 0xd3, 0x54, 0xb9, - 0x54, 0xc5, 0x23, 0x3c, 0x0e, 0x8d, 0x26, 0xd6, 0xa9, 0x44, 0xf4, 0x6e, - 0xa1, 0x4e, 0xee, 0x1c, 0x03, 0x7a, 0xa6, 0x0d, 0xd5, 0x7c, 0xe2, 0x50, - 0x6f, 0xa8, 0x6e, 0x5a, 0xde, 0x06, 0xb4, 0x17, 0x53, 0x4e, 0x06, 0x71, - 0x78, 0xa1, 0x05, 0x21, 0x2f, 0xc6, 0xf4, 0x15, 0x0a, 0xab, 0x87, 0x06, - 0x29, 0x51, 0x60, 0x17, 0x9f, 0x5c, 0x8a, 0x0f, 0x00, 0xc6, 0x19, 0x18, - 0x0b, 0x20, 0x29, 0x86, 0x3d, 0x31, 0xa5, 0x14, 0x74, 0x98, 0x89, 0x24, - 0xb6, 0x76, 0xd1, 0x30, 0x84, 0x70, 0x93, 0xbe, 0x9b, 0xa0, 0xb1, 0x80, - 0xa5, 0x9a, 0x9e, 0xde, 0xdd, 0x2a, 0xb6, 0xe9, 0xd5, 0xfc, 0x31, 0x97, - 0x96, 0x57, 0x90, 0x79, 0xf3, 0x76, 0xe8, 0x4b, 0x97, 0xde, 0x00, 0x00, - 0x00, 0x1c, 0x78, 0xeb, 0x25, 0x4a, 0x9c, 0xf1, 0xb6, 0x68, 0x28, 0xbe, - 0x29, 0x5e, 0xe7, 0xd8, 0x10, 0x1c, 0x77, 0x66, 0x02, 0x49, 0xa4, 0x9a, - 0x78, 0x09, 0x6d, 0xad, 0xad, 0x64, 0x57, 0xf9, 0xb3, 0x39, 0x08, 0x80, - 0xd5, 0x15, 0xe3, 0x9c, 0x42, 0x29, 0x65, 0xf6, 0xfa, 0xe6, 0x54, 0xa4, - 0xb2, 0x04, 0x3d, 0xab, 0xa2, 0x33, 0xe6, 0x6c, 0x6a, 0x9f, 0x5d, 0x55, - 0xed, 0x54, 0x61, 0x83, 0xb7, 0x56, 0xcb, 0x68, 0x82, 0x1b, 0x21, 0x3c, - 0x9e, 0xe2, 0x2a, 0x42, 0x70, 0xb0, 0xd4, 0x8c, 0x26, 0x50, 0x51, 0xd4, - 0x71, 0xa0, 0x77, 0x26, 0xb5, 0x28, 0xd0, 0x84, 0x69, 0xcb, 0xbb, 0xed, - 0xe5, 0xad, 0xa2, 0xd4, 0xdb, 0xac, 0x45, 0xeb, 0xe1, 0xe7, 0xca, 0xb3, - 0x95, 0xef, 0xcb, 0x69, 0x13, 0x1e, 0x3e, 0xcc, 0xc6, 0xb0, 0xad, 0xbd, - 0xbb, 0x16, 0x11, 0xa2, 0xcd, 0x6e, 0xff, 0xf1, 0x50, 0x80, 0x31, 0x5f, - 0xfc, 0x21, 0x1a, 0xcf, 0xe1, 0x8b, 0x07, 0x95, 0x00, 0x9c, 0xb3, 0x42, - 0x9c, 0x8a, 0xf6, 0x40, 0x1d, 0xf3, 0x75, 0xf0, 0xf9, 0x75, 0xe7, 0xf3, - 0xf4, 0xf9, 0xf6, 0xef, 0x8d, 0xf9, 0x95, 0x77, 0xc7, 0xbe, 0xb6, 0xd7, - 0xdf, 0xed, 0xf8, 0x73, 0x7b, 0xe8, 0x49, 0xc5, 0xac, 0x0f, 0x80, 0xe1, - 0x63, 0x8b, 0x4a, 0x68, 0xf5, 0x19, 0xb2, 0x8f, 0x3e, 0x7a, 0x1b, 0x8f, - 0x61, 0xba, 0x4c, 0x3f, 0xf2, 0x47, 0x06, 0xa1, 0xd2, 0xde, 0x6f, 0x42, - 0xee, 0x86, 0x08, 0x02, 0xbb, 0x18, 0xee, 0x16, 0x8c, 0xfc, 0x62, 0x0a, - 0xeb, 0x6a, 0x58, 0x0b, 0x2f, 0x47, 0x26, 0x3d, 0x6e, 0xde, 0xaf, 0xa5, - 0x3a, 0xa9, 0xaf, 0xb3, 0xb1, 0x6f, 0x3c, 0x03, 0x4f, 0xe8, 0x5d, 0x5a, - 0xb8, 0xf8, 0x8b, 0x9f, 0xf1, 0x01, 0x13, 0x9a, 0x4d, 0xe3, 0x7c, 0xb9, - 0x3a, 0x64, 0x6f, 0x0b, 0xad, 0xe2, 0x73, 0x00, 0x11, 0x45, 0x5c, 0xe4, - 0x37, 0xdd, 0x50, 0xac, 0x66, 0xb7, 0xba, 0x4f, 0x0d, 0x6b, 0x13, 0xc6, - 0xe3, 0x9f, 0x3e, 0x8a, 0x88, 0x8e, 0x7b, 0x77, 0x16, 0x56, 0x12, 0x03, - 0x71, 0x99, 0x99, 0xbe, 0x9d, 0xcd, 0xe8, 0x5a, 0x44, 0xf4, 0xc8, 0x61, - 0x20, 0xc7, 0x19, 0x4d, 0x68, 0xbe, 0x15, 0xc5, 0xb5, 0xed, 0x16, 0x32, - 0x1b, 0x2b, 0x26, 0x84, 0xbc, 0xa7, 0xdf, 0x1f, 0x2a, 0xa5, 0x9c, 0x04, - 0x8c, 0xee, 0x23, 0x25, 0x04, 0x06, 0x01, 0xb0, 0xbd, 0x3f, 0xc5, 0xda, - 0xf5, 0x25, 0x57, 0xe9, 0x56, 0x51, 0x8d, 0xe3, 0xbb, 0xac, 0x9b, 0x4d, - 0x96, 0x87, 0x4e, 0x22, 0x1a, 0xf9, 0x92, 0x37, 0x55, 0xe2, 0x10, 0xda, - 0x68, 0x92, 0xdb, 0xdc, 0x25, 0x9b, 0x0c, 0x7b, 0x06, 0xdf, 0x0b, 0x6d, - 0x41, 0xa0, 0x62, 0xb0, 0x7a, 0xe7, 0xb5, 0xde, 0x8d, 0xa0, 0x66, 0xd6, - 0x2c, 0xfc, 0x69, 0x09, 0xea, 0xf4, 0xc9, 0xf1, 0x1c, 0xeb, 0xd1, 0x37, - 0x06, 0xe3, 0x39, 0x6e, 0x81, 0xb9, 0x90, 0x4a, 0x22, 0x0f, 0xbc, 0x00, - 0x05, 0xe0, 0x04, 0xab, 0xe3, 0x2b, 0x75, 0x25, 0xf7, 0xa6, 0xef, 0x00, - 0x7e, 0xff, 0xe4, 0xe9, 0xe5, 0x13, 0x04, 0xe6, 0x15, 0xc8, 0x5e, 0x8b, - 0x40, 0x21, 0xc7, 0x37, 0x3b, 0x1b, 0xad, 0x76, 0x42, 0xd0, 0x83, 0x9a, - 0xae, 0x61, 0xa5, 0x0c, 0xb2, 0x7b, 0xbb, 0xa9, 0x13, 0x5d, 0xb5, 0x81, - 0x44, 0x47, 0x1e, 0x0d, 0x54, 0x66, 0xa3, 0x9c, 0x3b, 0xda, 0xf6, 0x91, - 0x97, 0x50, 0xe8, 0x94, 0x72, 0x91, 0xad, 0xfd, 0x78, 0xee, 0x89, 0x3c, - 0x10, 0x8e, 0x8f, 0x07, 0x5f, 0xae, 0x5f, 0x67, 0x3d, 0x7e, 0xfc, 0x3b, - 0x8e, 0x2c, 0x51, 0xd6, 0xdf, 0x67, 0xef, 0x6e, 0xea, 0x52, 0xe3, 0x2c, - 0xe2, 0xc7, 0x84, 0x18, 0x8c, 0xaa, 0xd7, 0xd5, 0xf1, 0xd8, 0xa1, 0x7d, - 0x48, 0xa0, 0x9a, 0x4f, 0xff, 0xf1, 0x50, 0x80, 0x2a, 0xff, 0xfc, 0x21, - 0x2a, 0xcf, 0x7f, 0xe0, 0x8d, 0x01, 0x00, 0xa5, 0xa3, 0xb3, 0x8c, 0x88, - 0x85, 0x13, 0x20, 0x6f, 0x57, 0x7c, 0x9c, 0x56, 0xdd, 0x5f, 0x8e, 0x38, - 0xf5, 0xfa, 0xfe, 0x9c, 0x6f, 0x5e, 0x7b, 0xe3, 0x38, 0xf1, 0xf7, 0xef, - 0x7d, 0x27, 0x5d, 0xeb, 0x25, 0x8b, 0x1f, 0xd9, 0xfc, 0x1e, 0x23, 0x59, - 0xca, 0x5e, 0x5f, 0xb1, 0x19, 0x7c, 0x98, 0x06, 0xa4, 0x2e, 0x7b, 0xf7, - 0x7b, 0x62, 0xa4, 0xb4, 0x6c, 0x3a, 0xe5, 0xbc, 0x14, 0x4d, 0x4a, 0x26, - 0x60, 0x7d, 0x18, 0x3d, 0xb0, 0x1f, 0x73, 0x55, 0x0a, 0x14, 0x56, 0x74, - 0x86, 0x22, 0x93, 0x42, 0x00, 0x4e, 0x77, 0xdb, 0x9b, 0x27, 0xff, 0x9d, - 0x4c, 0x11, 0xfd, 0x8c, 0x26, 0x21, 0xa5, 0x4a, 0x75, 0x5d, 0x3d, 0x13, - 0xe3, 0xa9, 0x87, 0xda, 0x14, 0xef, 0x9e, 0x59, 0x34, 0xde, 0x8c, 0x25, - 0x6d, 0xf5, 0xe2, 0x3d, 0xfb, 0xab, 0x9c, 0x1c, 0x78, 0x01, 0x86, 0xc7, - 0xd4, 0x49, 0x04, 0x0b, 0x7b, 0x75, 0xcc, 0xef, 0x6d, 0xff, 0x38, 0xb9, - 0xff, 0xfd, 0xb9, 0x8c, 0xcc, 0x00, 0xdb, 0x5d, 0x7d, 0x32, 0x03, 0x17, - 0x92, 0x4e, 0x15, 0xc8, 0x17, 0x6e, 0xa8, 0x4c, 0xd7, 0x7d, 0xeb, 0xa2, - 0x8c, 0x5a, 0xa1, 0x45, 0x03, 0x73, 0xdb, 0x10, 0x14, 0x5a, 0x08, 0xa6, - 0x49, 0x17, 0x4b, 0x30, 0x44, 0xec, 0x48, 0x0b, 0x65, 0x52, 0x70, 0x8d, - 0x69, 0x3a, 0x45, 0x32, 0x62, 0x91, 0x5a, 0x90, 0x03, 0x4c, 0x2a, 0x8b, - 0x8a, 0xb0, 0xbc, 0x2d, 0x5a, 0x55, 0x9c, 0x21, 0x54, 0x0e, 0xd2, 0x7c, - 0xd2, 0x1a, 0xf4, 0x96, 0x90, 0x94, 0x29, 0x27, 0x5a, 0x21, 0x19, 0x22, - 0xe8, 0xca, 0x7a, 0x12, 0x20, 0xa2, 0x12, 0x7c, 0x32, 0x39, 0xe8, 0x94, - 0xad, 0x19, 0x62, 0xd5, 0x11, 0xa5, 0x76, 0x31, 0x8d, 0x5e, 0xf0, 0x5d, - 0xf2, 0x71, 0x5b, 0x44, 0xa8, 0x93, 0x77, 0xad, 0xf1, 0x2a, 0x6f, 0x55, - 0x9a, 0xa4, 0xdd, 0xd4, 0xa0, 0xea, 0x7a, 0x2b, 0x17, 0x73, 0xe1, 0x6f, - 0xd0, 0x8b, 0x61, 0x5d, 0x9c, 0x72, 0xf4, 0x49, 0x6d, 0x48, 0x63, 0x27, - 0xd2, 0x37, 0xfc, 0x8d, 0x8e, 0x45, 0xd8, 0xf2, 0x00, 0x5a, 0xa8, 0x2a, - 0xb2, 0x40, 0x78, 0xda, 0x71, 0xd2, 0xee, 0x9f, 0x26, 0x6e, 0x76, 0xdb, - 0x34, 0xfd, 0x8d, 0xfb, 0x5b, 0xfd, 0xe4, 0x94, 0x5b, 0xa9, 0xb5, 0x6a, - 0x46, 0xcf, 0xb7, 0x82, 0x8b, 0x7a, 0x19, 0x6b, 0xb1, 0x4b, 0x80, 0xff, - 0xf1, 0x50, 0x80, 0x33, 0x7f, 0xfc, 0x21, 0x4c, 0x6c, 0xf8, 0x7b, 0xe0, - 0x34, 0x07, 0x22, 0xd0, 0xd1, 0x68, 0x9c, 0x59, 0x66, 0x50, 0x65, 0x96, - 0x49, 0x1a, 0x41, 0xb2, 0xdc, 0x54, 0x00, 0x00, 0x3a, 0xe6, 0x9d, 0x4f, - 0x00, 0xbf, 0x39, 0x38, 0xeb, 0xfa, 0x1e, 0x78, 0xef, 0xe3, 0x3d, 0xab, - 0xa8, 0x3a, 0x41, 0x43, 0x16, 0x2c, 0xa7, 0x57, 0x97, 0xef, 0x31, 0xe4, - 0x46, 0xed, 0xd1, 0x60, 0x34, 0xce, 0x5d, 0x4b, 0xa3, 0x0f, 0x24, 0x2d, - 0x9e, 0x6f, 0xf5, 0xcb, 0xd7, 0xe4, 0x55, 0x28, 0xa3, 0xa8, 0x4d, 0x09, - 0x34, 0x8a, 0x97, 0x09, 0x4c, 0x35, 0xc8, 0x84, 0x41, 0xa2, 0x28, 0xe7, - 0xed, 0x12, 0x77, 0x5b, 0xc8, 0xe4, 0xe4, 0xea, 0x96, 0xf2, 0x20, 0x9f, - 0x95, 0x3e, 0x16, 0xcc, 0x6a, 0x6f, 0x81, 0x40, 0xf1, 0xdb, 0xfa, 0xe3, - 0x9c, 0xfe, 0x51, 0xdb, 0xa6, 0xd0, 0xaf, 0x54, 0xde, 0x9a, 0xe9, 0x4c, - 0x01, 0xd4, 0x30, 0xe7, 0x5b, 0xa9, 0xdf, 0x05, 0x96, 0xf6, 0x80, 0x4d, - 0xc8, 0x8b, 0x3d, 0x76, 0x04, 0x1f, 0xe2, 0x85, 0x24, 0xf0, 0xd7, 0x79, - 0xea, 0x2e, 0xac, 0xb9, 0xe6, 0xcc, 0xd1, 0x5c, 0xf1, 0x08, 0xde, 0x1a, - 0xc1, 0x10, 0xc0, 0xae, 0xbb, 0x62, 0xc8, 0x17, 0xec, 0x46, 0xad, 0x96, - 0x50, 0x76, 0x20, 0x19, 0xc0, 0x98, 0x8e, 0x68, 0x00, 0xa4, 0xde, 0xb8, - 0xbe, 0x59, 0x48, 0x66, 0xdd, 0x54, 0x55, 0x6b, 0x3b, 0xc9, 0x98, 0xcd, - 0x4e, 0x58, 0xe8, 0x60, 0xaf, 0xd6, 0xe0, 0xae, 0x65, 0xe5, 0x13, 0x14, - 0xd8, 0x5c, 0x43, 0xbd, 0xad, 0xb4, 0xb3, 0x18, 0xc9, 0x56, 0x88, 0xec, - 0x8a, 0x8a, 0x9a, 0x72, 0xcc, 0x5e, 0x9d, 0x2b, 0xad, 0x5b, 0xed, 0x5e, - 0x1d, 0xfa, 0x3d, 0x49, 0x71, 0xb3, 0x53, 0xeb, 0x98, 0x78, 0x5b, 0x2e, - 0xed, 0x48, 0x38, 0x4d, 0xd8, 0x87, 0x1e, 0x58, 0x94, 0x2d, 0xb4, 0xf5, - 0xb2, 0xc9, 0x27, 0xda, 0x2d, 0xbe, 0xb6, 0xa5, 0xe6, 0x8f, 0x98, 0x26, - 0xb7, 0x47, 0xa6, 0xd7, 0xb8, 0x81, 0x5e, 0x73, 0x53, 0x11, 0x52, 0xbd, - 0xae, 0xa4, 0xaf, 0x05, 0x4e, 0xab, 0xf1, 0x96, 0xf2, 0x83, 0xbe, 0xae, - 0x79, 0x1c, 0x0a, 0xa5, 0xfc, 0x15, 0xd9, 0x85, 0x0c, 0x8f, 0xb8, 0x2f, - 0xdf, 0x0a, 0x55, 0x64, 0x30, 0x6a, 0x54, 0x01, 0xf4, 0x57, 0x65, 0x95, - 0x59, 0xa9, 0x0c, 0xd1, 0xd7, 0x22, 0x81, 0xd9, 0x29, 0x54, 0x03, 0x80, - 0xa1, 0x40, 0x0f, 0x27, 0x0b, 0xb9, 0x8c, 0x53, 0xdb, 0x61, 0x85, 0x5c, - 0x70, 0xd7, 0x19, 0x5c, 0x78, 0xdb, 0x28, 0xcd, 0x1a, 0x69, 0x9d, 0x89, - 0x4d, 0x6b, 0x10, 0x4d, 0x65, 0x5c, 0xc9, 0x21, 0x3c, 0x2f, 0x7e, 0xb9, - 0x40, 0x4b, 0xd7, 0x9c, 0xd2, 0x4f, 0x9d, 0x86, 0xe3, 0x6d, 0x37, 0x12, - 0x52, 0x56, 0xe2, 0x84, 0x16, 0xe0, 0xfb, 0x9b, 0x3e, 0x37, 0xd6, 0x29, - 0x70, 0x81, 0xee, 0xc4, 0x3f, 0x7a, 0x75, 0x96, 0x9a, 0x2e, 0xcf, 0x22, - 0xbd, 0xae, 0xff, 0xf1, 0x50, 0x80, 0x2d, 0x7f, 0xfc, 0x21, 0x7a, 0xcf, - 0xd0, 0x30, 0x4f, 0x00, 0x00, 0xa3, 0xb1, 0xd2, 0xa0, 0x8c, 0x74, 0x7b, - 0x0e, 0x82, 0x00, 0x00, 0x4a, 0x01, 0xf1, 0xbd, 0xe0, 0x4d, 0x5d, 0xea, - 0xdf, 0x43, 0xf6, 0x72, 0x1b, 0x08, 0x41, 0x24, 0xf8, 0x99, 0x9d, 0x4e, - 0xaa, 0x91, 0xa9, 0xb1, 0x98, 0x03, 0xcd, 0xb8, 0xa9, 0x70, 0xb2, 0x26, - 0xa1, 0x8f, 0x41, 0x17, 0x2b, 0x45, 0x58, 0x9a, 0x12, 0xbd, 0x16, 0xf9, - 0x50, 0x80, 0xcc, 0x29, 0x34, 0x04, 0x6a, 0x73, 0x16, 0xb0, 0x76, 0x10, - 0x98, 0xec, 0xc1, 0x96, 0xd7, 0x37, 0x04, 0x22, 0xf8, 0x12, 0xa4, 0x08, - 0x62, 0x00, 0x64, 0xa3, 0x49, 0xfd, 0x80, 0x2e, 0x95, 0x42, 0xab, 0xf4, - 0x85, 0x6f, 0x81, 0x90, 0x1b, 0x5d, 0x50, 0x1a, 0x5a, 0x3b, 0x96, 0x78, - 0x3d, 0x8a, 0x7f, 0x7f, 0x02, 0x26, 0x9f, 0x05, 0x5d, 0x21, 0xe5, 0x68, - 0x11, 0xdc, 0x2c, 0xfc, 0x79, 0x76, 0xdc, 0x6a, 0xcc, 0x7b, 0xdc, 0x70, - 0xc5, 0xf2, 0xcd, 0xd5, 0x7b, 0xaf, 0xb1, 0x05, 0x5c, 0x0e, 0x90, 0x09, - 0x35, 0x88, 0xa0, 0x5a, 0xee, 0x93, 0x8e, 0xb3, 0x90, 0xa1, 0x85, 0x58, - 0x61, 0x77, 0x9d, 0xde, 0x18, 0x22, 0xc1, 0xe5, 0x77, 0xc4, 0x91, 0x58, - 0xac, 0x18, 0xcb, 0x90, 0xc8, 0xcb, 0x29, 0xf2, 0xe9, 0xc1, 0x56, 0xaa, - 0x43, 0x46, 0xf2, 0xb0, 0xa2, 0xaa, 0x83, 0x54, 0x85, 0x37, 0xc5, 0x78, - 0x6b, 0x21, 0x31, 0xb0, 0x5a, 0xf6, 0x14, 0xee, 0x30, 0x91, 0x18, 0x9e, - 0x4a, 0xcc, 0xb5, 0xfc, 0x19, 0xe8, 0x9a, 0x1e, 0xc6, 0xde, 0x15, 0x2b, - 0x3a, 0xac, 0xbe, 0x6e, 0xa7, 0x9d, 0xe4, 0xa6, 0x29, 0x4e, 0x35, 0xa4, - 0xbb, 0x35, 0x57, 0x51, 0x49, 0x39, 0xd3, 0x46, 0x19, 0x50, 0xa3, 0x5f, - 0x95, 0x47, 0x42, 0x88, 0x32, 0x08, 0x5e, 0xf0, 0x6f, 0x5b, 0x06, 0x9b, - 0x0d, 0x6d, 0xa1, 0xbf, 0x66, 0x73, 0x5d, 0x55, 0xf7, 0x6a, 0xa8, 0x08, - 0xbd, 0xe5, 0xde, 0x0e, 0xf3, 0x20, 0x03, 0x0f, 0xd8, 0x09, 0x42, 0x54, - 0xc0, 0x6d, 0xe4, 0x52, 0x2e, 0x6c, 0xe8, 0xac, 0xa4, 0x29, 0x95, 0x4b, - 0x59, 0x07, 0x1f, 0x9c, 0xa6, 0xcc, 0x89, 0xf6, 0x4f, 0x0c, 0xaa, 0x29, - 0x2d, 0x95, 0xc9, 0xaf, 0x2c, 0x78, 0xba, 0x10, 0x48, 0x08, 0xe9, 0x11, - 0x00, 0x12, 0xda, 0xe2, 0x00, 0xc6, 0x88, 0xe4, 0x2c, 0xa2, 0x65, 0x49, - 0xc0, 0x50, 0xdd, 0xf9, 0xcc, 0x8e, 0x10, 0xc2, 0x4e, 0xc1, 0x7c, 0xc6, - 0x43, 0x33, 0x8a, 0x13, 0x02, 0x10, 0xe9, 0x47, 0x8e, 0x6c, 0x88, 0x37, - 0x35, 0x09, 0x60, 0x67, 0x97, 0xff, 0xf1, 0x50, 0x80, 0x32, 0x3f, 0xfc, - 0x21, 0x1a, 0xcd, 0x36, 0xe0, 0x3b, 0x08, 0x00, 0xa1, 0xb1, 0xc2, 0xd6, - 0xcc, 0x58, 0x13, 0xd7, 0xb7, 0x2e, 0x1e, 0x3c, 0xaa, 0x1b, 0x56, 0xba, - 0xf7, 0xbb, 0x33, 0x55, 0xfb, 0xfe, 0x72, 0xa1, 0xf5, 0xe3, 0xe2, 0x4b, - 0xe7, 0x88, 0x31, 0x12, 0x21, 0x15, 0x33, 0x46, 0x90, 0x38, 0x9b, 0x3c, - 0x6e, 0xc1, 0xa1, 0x95, 0x21, 0x09, 0x58, 0x23, 0xea, 0xf4, 0x59, 0x85, - 0xa8, 0xcd, 0xbc, 0xe3, 0xef, 0x64, 0xe9, 0x45, 0xd7, 0xbd, 0x15, 0x45, - 0xc3, 0xe7, 0x5a, 0x7a, 0xfd, 0x5a, 0x3f, 0xc8, 0xcc, 0xfc, 0xef, 0x7d, - 0xf2, 0xed, 0xe7, 0xd3, 0xeb, 0xe9, 0x8e, 0xb8, 0x1e, 0x9e, 0x84, 0x5c, - 0xd7, 0xc7, 0xec, 0xfb, 0x0e, 0xf6, 0xb3, 0xd1, 0xc7, 0xd3, 0xbb, 0xc7, - 0x3f, 0x84, 0x95, 0xce, 0xb1, 0xd7, 0xd4, 0x14, 0x1c, 0x02, 0xa1, 0x7f, - 0x17, 0x32, 0x45, 0x0c, 0xf2, 0xf1, 0x7a, 0xfe, 0x3d, 0xee, 0x88, 0xb7, - 0x57, 0xef, 0xa8, 0x0e, 0x52, 0x5a, 0xaa, 0xb8, 0xc2, 0x99, 0x0c, 0xa9, - 0x53, 0x06, 0x20, 0xce, 0x64, 0x4b, 0x05, 0xdc, 0xab, 0x80, 0xbe, 0xc5, - 0x66, 0xa0, 0x8e, 0xe0, 0x09, 0xb9, 0xa8, 0xa0, 0x48, 0x73, 0xe5, 0x38, - 0xc5, 0xe2, 0x62, 0x77, 0x37, 0x2a, 0xfa, 0xc9, 0x1a, 0xe8, 0x92, 0x3e, - 0x4c, 0x2f, 0x30, 0x1e, 0xc1, 0x58, 0x55, 0x8b, 0xb8, 0xdc, 0x76, 0x15, - 0x74, 0x95, 0x43, 0x77, 0x58, 0x03, 0xac, 0x97, 0x51, 0xfa, 0xc7, 0x6b, - 0x2b, 0x5c, 0x3e, 0xd6, 0x02, 0x90, 0xda, 0xce, 0xf9, 0xc9, 0x40, 0xbb, - 0x3b, 0xbb, 0xbd, 0xa9, 0x6f, 0x40, 0xd3, 0x4b, 0x4a, 0xd4, 0xc4, 0x84, - 0x71, 0x29, 0x52, 0x56, 0x2f, 0x84, 0x95, 0xa5, 0x38, 0x1a, 0x6a, 0xd0, - 0x58, 0x95, 0x00, 0x81, 0x7c, 0x4e, 0x46, 0x5f, 0x79, 0x6b, 0x0f, 0x11, - 0x56, 0x14, 0x62, 0x09, 0x77, 0x42, 0x10, 0xe8, 0x6c, 0xb2, 0xa8, 0x22, - 0xa7, 0xd9, 0x01, 0xf1, 0x00, 0x00, 0x07, 0x1c, 0x89, 0x4f, 0x38, 0xe6, - 0xd7, 0x32, 0xa6, 0x7f, 0xdc, 0x06, 0xc4, 0x65, 0x93, 0x64, 0x35, 0x46, - 0xea, 0x17, 0x9c, 0x1a, 0x21, 0xfe, 0x19, 0x17, 0xe7, 0x39, 0xf5, 0xe5, - 0x3e, 0x26, 0x39, 0x5d, 0xc6, 0xfc, 0xac, 0x99, 0x37, 0x78, 0xfc, 0x30, - 0xd5, 0xfc, 0x44, 0x63, 0xbb, 0xf0, 0xa3, 0xe0, 0x8b, 0xf8, 0x99, 0xde, - 0x4c, 0x78, 0x47, 0xa5, 0xf7, 0xb4, 0x82, 0xa5, 0x38, 0x42, 0xba, 0x45, - 0xad, 0x62, 0xb8, 0x44, 0x67, 0x4e, 0x56, 0xb3, 0xbb, 0xe7, 0x01, 0x3b, - 0xe5, 0x1e, 0x4a, 0x8b, 0x8f, 0x84, 0x15, 0x1e, 0x8d, 0x44, 0xb5, 0xa9, - 0xe7, 0xd1, 0x7d, 0x7d, 0xa2, 0xb1, 0xb6, 0xfe, 0x48, 0xed, 0x40, 0xb5, - 0x1d, 0xf9, 0x39, 0xca, 0xe2, 0xa5, 0xdf, 0xb0, 0x4b, 0xdc, 0x2f, 0x8e, - 0x3a, 0x65, 0x67, 0x38, 0xb2, 0xf6, 0x36, 0x91, 0xfb, 0x4e, 0xff, 0xf1, - 0x50, 0x80, 0x2f, 0xdf, 0xfc, 0x21, 0x1a, 0xcf, 0xa1, 0xae, 0x99, 0x00, - 0x00, 0xa1, 0xb4, 0xb2, 0x56, 0x70, 0x60, 0xcb, 0x9b, 0x59, 0x97, 0x96, - 0x4a, 0x65, 0xaa, 0xdc, 0x75, 0xdf, 0x73, 0x8b, 0xd7, 0x3f, 0x5c, 0x2a, - 0x01, 0xb9, 0xf8, 0xac, 0x1c, 0x3f, 0x21, 0xbf, 0xe2, 0x0e, 0x86, 0x71, - 0xa2, 0x57, 0xb7, 0xea, 0x04, 0x61, 0x92, 0xcd, 0xdb, 0x2c, 0x46, 0xc7, - 0xd1, 0xda, 0x35, 0x77, 0xe8, 0xd3, 0x1e, 0x68, 0xd2, 0x1d, 0x69, 0xdd, - 0x73, 0x15, 0x9f, 0xce, 0x80, 0x0d, 0x3c, 0x4d, 0x09, 0xbc, 0x04, 0xc4, - 0x4f, 0x6f, 0xcf, 0x31, 0x90, 0xe8, 0xe0, 0x5c, 0x57, 0xbf, 0x96, 0xa5, - 0xb5, 0x5e, 0x7b, 0xab, 0x91, 0xe5, 0x58, 0x2a, 0x19, 0xea, 0xa1, 0x99, - 0x1c, 0x78, 0x00, 0x67, 0x1b, 0xd4, 0x85, 0x00, 0xdd, 0x6d, 0x24, 0x3a, - 0xb8, 0xe9, 0x9b, 0xbe, 0x8d, 0x5f, 0x76, 0x7a, 0x73, 0x4e, 0xe9, 0x18, - 0x2d, 0x22, 0xb0, 0x2a, 0x15, 0xae, 0xf0, 0x86, 0x21, 0x42, 0xfa, 0x40, - 0x72, 0xd9, 0x97, 0x74, 0x81, 0x87, 0x6c, 0x39, 0xb3, 0x77, 0x26, 0xea, - 0x2f, 0x97, 0x43, 0x75, 0x46, 0x33, 0x89, 0xb9, 0x89, 0xf4, 0xc1, 0x50, - 0xbe, 0xd8, 0x1c, 0xb3, 0x27, 0xab, 0x1b, 0xf7, 0xf2, 0x4c, 0x2b, 0x9f, - 0xee, 0x8e, 0xef, 0xbe, 0xf4, 0x82, 0xe8, 0xbc, 0x88, 0xf6, 0x4c, 0xca, - 0x46, 0x66, 0x02, 0xcb, 0x2d, 0xd4, 0x46, 0xe0, 0xa2, 0x46, 0x98, 0xab, - 0x67, 0x84, 0xd9, 0xd2, 0xd2, 0x79, 0xc5, 0x08, 0xcb, 0x75, 0x30, 0xc4, - 0x00, 0xd8, 0x29, 0xc8, 0x02, 0x5b, 0xaa, 0xa7, 0x3a, 0x1b, 0x47, 0x5a, - 0x47, 0x34, 0xdc, 0x08, 0x21, 0x94, 0xdb, 0x2d, 0xa6, 0x81, 0x31, 0x5a, - 0xe4, 0x94, 0x22, 0x2b, 0xde, 0x14, 0x2b, 0xe6, 0x8c, 0x91, 0xd1, 0x01, - 0xdb, 0x8a, 0x61, 0x9e, 0x23, 0x4a, 0xa1, 0xa5, 0x28, 0xa0, 0xee, 0x25, - 0x47, 0xb8, 0x00, 0x00, 0x00, 0x94, 0xf6, 0xe6, 0xa3, 0x3c, 0xef, 0x9e, - 0x1c, 0xdc, 0x11, 0x28, 0xcd, 0x35, 0x2d, 0x20, 0x70, 0x5c, 0x72, 0x8d, - 0xe4, 0x24, 0x5d, 0x2f, 0x61, 0x0f, 0xab, 0xfa, 0x85, 0x66, 0xee, 0xdb, - 0xf6, 0x09, 0xa8, 0xc1, 0x5b, 0x2c, 0x58, 0x3f, 0x52, 0x07, 0xeb, 0x69, - 0xc5, 0xa7, 0xc0, 0x17, 0x03, 0x30, 0xcb, 0x32, 0xa1, 0x20, 0xa4, 0xe6, - 0xad, 0x5b, 0x18, 0x62, 0x63, 0x9d, 0x44, 0x15, 0xdd, 0xf5, 0xf2, 0x8e, - 0x55, 0xbf, 0xa3, 0xe8, 0x94, 0x6d, 0x5a, 0x77, 0xf0, 0x45, 0xfc, 0xb2, - 0x5e, 0xf3, 0xea, 0xeb, 0xc1, 0x7c, 0xbd, 0x1a, 0xdb, 0xdd, 0xf1, 0x06, - 0x55, 0xf5, 0xd7, 0x2d, 0xc0, 0x5a, 0xc8, 0xc4, 0xae, 0x59, 0x25, 0x8f, - 0x46, 0xc7, 0x1e, 0x78, 0x39, 0xd5, 0x6f, 0x80, 0xff, 0xf1, 0x50, 0x80, - 0x2a, 0xff, 0xfc, 0x21, 0x1a, 0xca, 0xa5, 0xfe, 0xed, 0x04, 0x00, 0xa1, - 0xb4, 0xc1, 0xdc, 0xc6, 0xf8, 0x32, 0x54, 0xa9, 0x49, 0x50, 0x05, 0x42, - 0xa2, 0xa5, 0xd3, 0x2f, 0x3c, 0xf5, 0xef, 0xf5, 0xed, 0x33, 0x86, 0xc4, - 0x91, 0xb2, 0x65, 0x68, 0x3d, 0x5d, 0xc5, 0x78, 0xed, 0xd3, 0x22, 0x47, - 0x10, 0xf5, 0x17, 0xf3, 0xd0, 0xb0, 0xbb, 0x0a, 0x71, 0xbb, 0x62, 0xb9, - 0xcd, 0xd8, 0x8c, 0x4b, 0xb9, 0xc8, 0xaa, 0x27, 0x65, 0x52, 0x10, 0x5a, - 0xba, 0xa8, 0x5d, 0x27, 0x21, 0xb9, 0x02, 0xab, 0xed, 0x06, 0x87, 0x83, - 0x7a, 0x62, 0x13, 0xd8, 0x73, 0xdd, 0x41, 0x7c, 0x9f, 0x6e, 0x56, 0x57, - 0x6b, 0x67, 0x33, 0x87, 0x8b, 0x49, 0xfc, 0x54, 0xa3, 0xd3, 0x4d, 0x6d, - 0x04, 0x45, 0x9d, 0xc3, 0x19, 0x1e, 0x9c, 0x32, 0x8c, 0x4a, 0xa6, 0x16, - 0x0a, 0x31, 0x8a, 0x5a, 0xb2, 0x34, 0x9f, 0xba, 0x5c, 0x52, 0x93, 0xed, - 0x26, 0x43, 0x2f, 0x76, 0xf0, 0xe2, 0x1f, 0x94, 0x24, 0x62, 0x87, 0x53, - 0x3e, 0xb3, 0xf6, 0x1b, 0xdf, 0xdf, 0xf3, 0x31, 0xc2, 0xa3, 0x42, 0x6b, - 0x95, 0x85, 0x33, 0x0d, 0xca, 0x4f, 0x26, 0xca, 0xf8, 0x8c, 0x03, 0x01, - 0x08, 0xc1, 0x72, 0x05, 0xad, 0x0b, 0x0d, 0x1b, 0x40, 0x18, 0x97, 0x09, - 0x0f, 0x09, 0xf0, 0x00, 0xeb, 0x07, 0x7e, 0x19, 0x24, 0x3b, 0x0c, 0xa6, - 0x2d, 0xd2, 0x55, 0x4a, 0x11, 0x13, 0xa0, 0x8b, 0x11, 0x93, 0x91, 0x58, - 0xcc, 0x6a, 0x98, 0xef, 0x08, 0x2c, 0xd7, 0xa3, 0xbd, 0x8a, 0xa5, 0x92, - 0x41, 0xce, 0xb0, 0xb6, 0xfb, 0x89, 0x2c, 0x74, 0x16, 0xa4, 0x15, 0x1d, - 0x53, 0xef, 0x00, 0x00, 0x00, 0x0f, 0x69, 0xbd, 0xd5, 0xea, 0x6f, 0x34, - 0xe6, 0xe8, 0x47, 0x1f, 0x12, 0xae, 0xd9, 0xa6, 0xfa, 0x09, 0x39, 0x7e, - 0x69, 0x85, 0x80, 0x8f, 0x4a, 0x23, 0x6a, 0xb8, 0x44, 0x66, 0xe7, 0x56, - 0xad, 0xe8, 0xba, 0x74, 0x11, 0xd1, 0x14, 0x09, 0xbd, 0xe8, 0xf5, 0x42, - 0xfd, 0x21, 0x68, 0xd2, 0x16, 0x78, 0x5b, 0xf5, 0x52, 0x28, 0x9d, 0xdd, - 0xb9, 0x89, 0x81, 0x88, 0x91, 0xc3, 0x08, 0x77, 0x18, 0xad, 0x48, 0xfb, - 0xfc, 0x49, 0xe0, 0xcf, 0x6e, 0x0e, 0xed, 0xf6, 0xdf, 0x27, 0xf1, 0xf6, - 0xd2, 0xfb, 0x7a, 0x0b, 0x99, 0xe5, 0xd7, 0x25, 0xd4, 0x69, 0x11, 0xda, - 0x17, 0xd2, 0xbf, 0xf3, 0x39, 0xa0, 0x6e, 0x32, 0xbd, 0x97, 0xa9, 0x9c, - 0x30, 0xae, 0x3c, 0xff, 0xf1, 0x50, 0x80, 0x31, 0x5f, 0xfc, 0x21, 0x2a, - 0xcf, 0xfc, 0xdd, 0xfd, 0x00, 0x00, 0x9e, 0xb2, 0xd4, 0x15, 0xec, 0x58, - 0x10, 0x03, 0x16, 0xcb, 0x32, 0xc0, 0x95, 0xd7, 0x1d, 0xab, 0x7e, 0xd7, - 0x9e, 0x7c, 0xf7, 0x77, 0x70, 0x45, 0xae, 0x92, 0x54, 0xd4, 0xa9, 0x3f, - 0x6e, 0xa6, 0x5d, 0x59, 0x1a, 0x96, 0xe0, 0x74, 0x55, 0x57, 0xa5, 0x17, - 0xf2, 0x1f, 0x6a, 0xa2, 0xec, 0xb0, 0xd1, 0x9a, 0xe4, 0x03, 0x15, 0xc9, - 0x1f, 0x43, 0x17, 0x39, 0xb6, 0xc4, 0x8d, 0xa0, 0x8b, 0x2d, 0x4e, 0x09, - 0xd0, 0xd7, 0x18, 0x43, 0x06, 0x36, 0x48, 0x32, 0xc3, 0x4e, 0xe5, 0x92, - 0x05, 0x96, 0x74, 0x57, 0xee, 0xcb, 0x37, 0xc8, 0x88, 0x38, 0xe4, 0x0e, - 0x38, 0x8a, 0xb1, 0xf1, 0x73, 0x8b, 0xaf, 0xd1, 0xea, 0x95, 0xef, 0x49, - 0x9f, 0xe3, 0x05, 0xe3, 0xba, 0x48, 0xbc, 0xf7, 0x6c, 0x66, 0x62, 0x37, - 0xd7, 0x60, 0x2b, 0x4d, 0xe4, 0x12, 0x82, 0xe9, 0x8e, 0xb9, 0xbc, 0x5a, - 0x8f, 0x01, 0x1d, 0x5d, 0x7d, 0x12, 0xe6, 0x75, 0x4e, 0xca, 0x89, 0xad, - 0x2b, 0x4b, 0x66, 0xfa, 0x25, 0x12, 0x9d, 0x40, 0xa0, 0xad, 0x4c, 0x39, - 0x48, 0xb2, 0xf2, 0x0b, 0xeb, 0x90, 0x18, 0x0e, 0x55, 0xb1, 0x72, 0xaf, - 0x4c, 0x42, 0xe7, 0xbe, 0x3a, 0xc6, 0xd0, 0xf5, 0xc8, 0xbb, 0xbe, 0xac, - 0x0e, 0xfd, 0x61, 0x82, 0xcb, 0x40, 0xce, 0x57, 0x39, 0x85, 0xbe, 0x08, - 0xd4, 0x1e, 0x57, 0xfe, 0x12, 0x5b, 0x7f, 0x89, 0x04, 0x65, 0x30, 0x2f, - 0xe9, 0x27, 0x49, 0xc1, 0x96, 0xfd, 0x20, 0x40, 0x0a, 0xde, 0xcf, 0x5c, - 0x3e, 0x49, 0x5d, 0xb4, 0xea, 0x18, 0x95, 0x10, 0x23, 0x58, 0x72, 0x96, - 0x59, 0x19, 0x3e, 0xdb, 0xaa, 0xdb, 0xe1, 0x6b, 0x84, 0xab, 0x38, 0xef, - 0x29, 0x0b, 0x59, 0x92, 0x2d, 0xc4, 0xe5, 0x5e, 0x1b, 0xfb, 0x0a, 0x98, - 0x6f, 0x05, 0xe4, 0x6c, 0x23, 0x53, 0x88, 0x94, 0x77, 0x32, 0x29, 0xeb, - 0x93, 0x15, 0x4b, 0xef, 0x00, 0x00, 0x00, 0x0f, 0x6d, 0xce, 0x6b, 0xac, - 0xe3, 0xbb, 0x8e, 0xf5, 0x41, 0x83, 0x3c, 0x71, 0x7e, 0x1f, 0x36, 0x9e, - 0x53, 0x6f, 0xc8, 0xf1, 0xa2, 0x40, 0x10, 0x48, 0x87, 0x54, 0x8f, 0xbe, - 0xf1, 0xd8, 0xcb, 0x8f, 0xe3, 0x40, 0xd2, 0xcd, 0x18, 0xc4, 0x17, 0x11, - 0xc4, 0xee, 0xef, 0x67, 0x67, 0x7e, 0x92, 0x24, 0x56, 0xfd, 0x7a, 0xc0, - 0x78, 0xc1, 0x1b, 0xb0, 0xd1, 0xcc, 0x85, 0x64, 0x93, 0xe9, 0x2d, 0x99, - 0x52, 0x3c, 0xd0, 0x97, 0x7a, 0x95, 0x80, 0x0a, 0x2e, 0xd2, 0xa7, 0x65, - 0x08, 0x80, 0xaa, 0x03, 0x0b, 0x6d, 0x37, 0xd8, 0x05, 0xf8, 0xfa, 0x04, - 0xd6, 0xb5, 0xd0, 0xaa, 0xb5, 0xf2, 0xd4, 0x70, 0x83, 0x5b, 0xc3, 0x9f, - 0xf5, 0x6b, 0x3c, 0x85, 0x43, 0x0a, 0x91, 0x7c, 0x9c, 0x37, 0xb8, 0x67, - 0x80, 0xff, 0xf1, 0x50, 0x80, 0x31, 0x9f, 0xfc, 0x21, 0x4c, 0x36, 0xe2, - 0x06, 0x20, 0x73, 0x4e, 0xb0, 0x4e, 0xc4, 0x25, 0xba, 0x31, 0x6b, 0x30, - 0x93, 0x9a, 0x34, 0x93, 0x96, 0x46, 0xcd, 0x3c, 0xf8, 0xe7, 0x8b, 0xc3, - 0x66, 0xb6, 0x1f, 0x5e, 0x0d, 0xb5, 0xc5, 0xe3, 0xcf, 0xef, 0xff, 0x86, - 0xf5, 0xbd, 0x7b, 0x75, 0xcb, 0xf0, 0x43, 0xbe, 0x9a, 0x00, 0xfc, 0x3f, - 0x7f, 0xe2, 0xf9, 0x42, 0x60, 0xc8, 0xc8, 0x98, 0x7f, 0x64, 0xe5, 0x08, - 0x06, 0x22, 0x42, 0x69, 0x13, 0x7d, 0x81, 0xdd, 0xdd, 0x4c, 0x51, 0x77, - 0x7e, 0xf4, 0xbc, 0x9b, 0xff, 0x77, 0xeb, 0x83, 0x5e, 0xbd, 0x48, 0xf1, - 0x94, 0x59, 0x50, 0x40, 0x78, 0x9c, 0xf9, 0x02, 0xb4, 0x9b, 0x4a, 0x7c, - 0x71, 0xad, 0x5c, 0x17, 0x2d, 0x31, 0x10, 0x1e, 0xf9, 0xcf, 0xc9, 0xe2, - 0xee, 0x8d, 0x74, 0x59, 0x7c, 0x5c, 0x16, 0x90, 0x88, 0xa8, 0xd5, 0x0a, - 0x5f, 0xfc, 0xa0, 0x57, 0x3d, 0x0f, 0x6a, 0x96, 0x07, 0x0a, 0xad, 0x22, - 0xcc, 0x1c, 0x4b, 0xf9, 0x56, 0x50, 0xd5, 0xc8, 0x80, 0x70, 0xf1, 0x15, - 0xcb, 0x0e, 0x6d, 0x6a, 0x82, 0x9d, 0xa5, 0xde, 0xac, 0xa0, 0x9f, 0x11, - 0x45, 0xa2, 0x8a, 0xe7, 0x06, 0xdc, 0x20, 0x08, 0x51, 0x79, 0x80, 0x18, - 0xb5, 0xf7, 0x0f, 0x1e, 0x15, 0xd3, 0xb8, 0x8b, 0x3f, 0x7e, 0xd8, 0xef, - 0xa6, 0x47, 0xd8, 0xaa, 0xe8, 0xcb, 0xab, 0x2d, 0x95, 0xf6, 0x41, 0x6b, - 0xcb, 0x93, 0x98, 0xd1, 0x2b, 0xfe, 0x41, 0xff, 0xfc, 0xc0, 0x01, 0x54, - 0xce, 0xe0, 0xf2, 0xd5, 0x73, 0x62, 0x46, 0x45, 0x81, 0x28, 0xd0, 0xde, - 0xaa, 0xd7, 0x13, 0x95, 0xf7, 0xec, 0xb2, 0xd9, 0x26, 0x4c, 0xd6, 0x5b, - 0xd7, 0x17, 0xbd, 0xd1, 0x7b, 0x41, 0xe5, 0x21, 0x2a, 0x58, 0x25, 0x00, - 0x68, 0xa5, 0xce, 0x75, 0x98, 0xed, 0x6d, 0xc7, 0xd8, 0x84, 0xfe, 0x51, - 0x86, 0x93, 0xe8, 0x01, 0xeb, 0xe6, 0x82, 0x57, 0x0c, 0xe3, 0x38, 0xe4, - 0x1c, 0x67, 0x71, 0xfe, 0xff, 0xf8, 0x12, 0xbd, 0xbc, 0xe5, 0x50, 0x75, - 0xd9, 0xc6, 0xf9, 0x97, 0x97, 0x04, 0xed, 0x6b, 0x13, 0x85, 0x76, 0x67, - 0x1f, 0x5f, 0xa6, 0x54, 0x5b, 0x3d, 0xb3, 0x88, 0x3d, 0xcb, 0x19, 0x12, - 0x8b, 0x48, 0x54, 0xad, 0x25, 0xe6, 0x85, 0x95, 0x13, 0x34, 0x97, 0x55, - 0x14, 0x61, 0x44, 0x7a, 0x71, 0xbf, 0xf5, 0xf7, 0x73, 0x95, 0xf6, 0xd5, - 0x12, 0x17, 0xf5, 0x3f, 0x6a, 0x2b, 0x39, 0x4f, 0x56, 0x9e, 0xb3, 0x75, - 0x5e, 0xa5, 0xd3, 0x9c, 0x3f, 0x07, 0x28, 0xd0, 0xc8, 0x8a, 0x51, 0xcb, - 0x96, 0x84, 0x92, 0x0c, 0x06, 0xc5, 0x37, 0x09, 0x14, 0x5f, 0x50, 0xa0, - 0x14, 0x16, 0x30, 0x5b, 0x2f, 0x55, 0x17, 0xbc, 0x85, 0x28, 0x40, 0xe2, - 0x18, 0x67, 0xe7, 0x05, 0x89, 0xc2, 0xe3, 0xf7, 0x41, 0x9e, 0xf6, 0xff, - 0xc0, 0xff, 0xf1, 0x50, 0x80, 0x29, 0xdf, 0xfc, 0x21, 0x7a, 0xce, 0xfb, - 0xff, 0xdc, 0x80, 0x00, 0xa1, 0xb2, 0xc3, 0x54, 0xa6, 0xd6, 0x39, 0xbd, - 0x06, 0xf4, 0x01, 0x29, 0xbd, 0x17, 0x25, 0xf5, 0xef, 0xe7, 0xdf, 0x5e, - 0x5f, 0x40, 0x05, 0x28, 0xda, 0xb0, 0x22, 0xdc, 0x7e, 0xa4, 0xe5, 0x55, - 0x92, 0xf1, 0xd4, 0xfd, 0xa5, 0x29, 0xa8, 0xfa, 0x1e, 0x3c, 0xa8, 0x8e, - 0x08, 0xb0, 0xdf, 0x13, 0x8b, 0xc1, 0x19, 0xeb, 0x90, 0x3e, 0x28, 0xa6, - 0xac, 0x6a, 0x13, 0x35, 0x34, 0x95, 0x85, 0x59, 0x82, 0x12, 0x43, 0x2c, - 0xcb, 0xed, 0x5f, 0x6d, 0x9e, 0xdf, 0x08, 0x0f, 0x96, 0x55, 0x46, 0x73, - 0xf2, 0xb6, 0x71, 0xba, 0xdf, 0x18, 0xe8, 0x5f, 0x6c, 0xe9, 0xd4, 0x35, - 0xb8, 0xa2, 0xa3, 0x26, 0x8e, 0x7e, 0x50, 0x63, 0xcd, 0xc9, 0x82, 0x54, - 0x4b, 0xb2, 0xe6, 0x5e, 0x1f, 0xe9, 0xeb, 0xb8, 0x63, 0x97, 0x4f, 0x9e, - 0x7a, 0xa5, 0x61, 0x8b, 0xc2, 0xdb, 0xd5, 0xe9, 0x5a, 0xce, 0x30, 0xee, - 0x08, 0x29, 0x9b, 0x0d, 0x96, 0xe8, 0xb1, 0xdd, 0xfe, 0x26, 0xba, 0xfa, - 0x3f, 0x66, 0xab, 0x53, 0xce, 0xcf, 0xb9, 0x4c, 0x93, 0x06, 0xd3, 0x9b, - 0x8d, 0x97, 0x1e, 0xc0, 0xa2, 0x45, 0x5b, 0xe9, 0xc0, 0xae, 0x7a, 0x64, - 0xb7, 0x88, 0xa9, 0x34, 0x94, 0xac, 0x5c, 0x6e, 0x77, 0x4e, 0x40, 0xc2, - 0xf3, 0xf3, 0x53, 0x89, 0x6f, 0x2e, 0x3d, 0x59, 0xd6, 0x05, 0x5b, 0x5a, - 0x00, 0x16, 0x67, 0x6f, 0x50, 0x00, 0x01, 0x7e, 0x27, 0x85, 0x1b, 0x2a, - 0xd7, 0x84, 0xe7, 0x45, 0x09, 0x5c, 0x25, 0xe4, 0x7b, 0xc2, 0x03, 0x3c, - 0x80, 0x0a, 0xce, 0x92, 0xd8, 0xa7, 0x29, 0xd0, 0xda, 0x84, 0x50, 0x65, - 0x57, 0xbc, 0x00, 0x00, 0x94, 0x03, 0x8b, 0xe2, 0xb9, 0x95, 0x2e, 0x55, - 0x4e, 0x56, 0x0c, 0x75, 0x55, 0x13, 0x8e, 0x6a, 0xf0, 0x82, 0x1f, 0x40, - 0xf9, 0x15, 0x08, 0xab, 0x9e, 0x23, 0x54, 0x1d, 0x47, 0x35, 0x2e, 0x7a, - 0xe0, 0x49, 0x4d, 0xe6, 0x86, 0x4f, 0x6c, 0x6b, 0xc1, 0xaa, 0x2b, 0x52, - 0x01, 0x19, 0xdd, 0x78, 0x86, 0x32, 0x35, 0x95, 0x07, 0x2b, 0x82, 0xbc, - 0x13, 0x57, 0x99, 0x17, 0x59, 0xcc, 0x4c, 0xde, 0x7b, 0x3b, 0x42, 0xd0, - 0x99, 0x33, 0xbf, 0x18, 0x0e, 0x49, 0x2d, 0x10, 0xd7, 0x7e, 0xc5, 0xa2, - 0xb3, 0xd1, 0x8b, 0x8d, 0x85, 0xcc, 0x8d, 0x88, 0xd4, 0x10, 0x70, 0xff, - 0xf1, 0x50, 0x80, 0x31, 0x5f, 0xfc, 0x21, 0x1a, 0xcf, 0xbd, 0xff, 0xef, - 0x80, 0x00, 0x9c, 0xb4, 0xd2, 0x64, 0xc6, 0xb6, 0x34, 0x08, 0x2a, 0x0a, - 0x80, 0x02, 0xa0, 0xe3, 0x93, 0x5c, 0xf9, 0xf1, 0xfb, 0xd7, 0x9a, 0xcb, - 0xbe, 0x04, 0x43, 0x5d, 0x4a, 0x92, 0xf9, 0xf7, 0xb4, 0xf7, 0x5e, 0xcc, - 0xab, 0xe7, 0x50, 0x31, 0x39, 0x71, 0x34, 0xd8, 0xe3, 0x4e, 0xe3, 0x3f, - 0xfa, 0xf4, 0xa3, 0x87, 0x00, 0x9f, 0x75, 0x7a, 0x08, 0xf3, 0x3e, 0x5b, - 0x9c, 0x97, 0x05, 0xbf, 0x14, 0x36, 0x8c, 0x6b, 0x98, 0x77, 0x60, 0x17, - 0xe3, 0x38, 0x8a, 0x22, 0xb3, 0xd3, 0x08, 0x8d, 0x4f, 0x61, 0x36, 0x9b, - 0x51, 0x97, 0x1d, 0xb3, 0x97, 0xf2, 0xac, 0x83, 0x7e, 0x82, 0x5c, 0xee, - 0x66, 0x3a, 0xfb, 0x7e, 0xa5, 0x2d, 0x5b, 0xd8, 0xae, 0xab, 0x68, 0xd4, - 0x69, 0x48, 0xdf, 0xf1, 0xbc, 0x72, 0xe4, 0x5b, 0xbf, 0xe2, 0x26, 0xdf, - 0x5b, 0xc9, 0x94, 0x8c, 0xda, 0x9d, 0xed, 0x2e, 0x5e, 0x78, 0x25, 0xaf, - 0xa5, 0xa3, 0x1d, 0xf2, 0xcd, 0x85, 0x5d, 0x42, 0x93, 0xa8, 0x86, 0xbe, - 0x29, 0xe0, 0x1c, 0x06, 0xaf, 0x70, 0xd7, 0xe5, 0xb4, 0x3d, 0xfc, 0x34, - 0x5a, 0xe4, 0xf6, 0xc6, 0x57, 0x64, 0x3b, 0x29, 0x9b, 0xa0, 0x80, 0xad, - 0xca, 0x35, 0x21, 0x32, 0xcc, 0xfd, 0xae, 0x04, 0x96, 0x3a, 0x22, 0x6f, - 0x6f, 0xff, 0x3d, 0x05, 0x76, 0x79, 0xf6, 0x3a, 0x73, 0x7e, 0xe1, 0xde, - 0x04, 0x0b, 0xd2, 0xfe, 0x58, 0xd0, 0x00, 0x61, 0x2f, 0xff, 0x43, 0xef, - 0xf2, 0x52, 0x3b, 0x70, 0xb8, 0x68, 0x2b, 0x1a, 0x90, 0xca, 0x6c, 0xbe, - 0xd2, 0xf0, 0x9d, 0x06, 0xca, 0xbf, 0x52, 0xcd, 0xa8, 0xcc, 0x4c, 0x33, - 0x65, 0x53, 0xaa, 0x47, 0x2b, 0x1a, 0xa1, 0x28, 0xaf, 0xc2, 0x5d, 0xb9, - 0x0a, 0xf9, 0x3b, 0x09, 0x08, 0x4c, 0xe0, 0xda, 0xdb, 0x47, 0xd7, 0xca, - 0xdb, 0xae, 0x72, 0xe5, 0x05, 0x52, 0xfb, 0xc0, 0x00, 0x00, 0x07, 0x17, - 0xa6, 0xe9, 0x5d, 0x73, 0x35, 0xce, 0xef, 0x40, 0x48, 0xad, 0xf2, 0xe8, - 0xcb, 0x0a, 0x8b, 0x4d, 0x89, 0xd4, 0xfd, 0x84, 0x52, 0x22, 0xd1, 0xa6, - 0xfe, 0x58, 0xe2, 0x81, 0x25, 0xe2, 0xaa, 0x58, 0xab, 0xc4, 0x34, 0x42, - 0xd1, 0xa3, 0x7b, 0xdd, 0x70, 0xd0, 0x61, 0xf1, 0xc3, 0x21, 0xef, 0x6a, - 0x86, 0xdc, 0x2b, 0xc6, 0x01, 0x71, 0x06, 0xcb, 0x33, 0x03, 0x36, 0x55, - 0xea, 0x8a, 0xa1, 0x70, 0x50, 0x50, 0x4b, 0xee, 0xa0, 0x19, 0xdc, 0xe0, - 0x4d, 0x38, 0x0f, 0x2d, 0xcb, 0x5a, 0x35, 0x47, 0x76, 0x10, 0xf1, 0x00, - 0x8f, 0x83, 0xfb, 0x46, 0x2b, 0xed, 0xf8, 0x15, 0x3d, 0x7d, 0x5f, 0x4f, - 0xb6, 0x5a, 0xba, 0x55, 0x71, 0xfa, 0x05, 0x6e, 0x5c, 0x7b, 0x65, 0x3d, - 0x81, 0x17, 0xc8, 0x69, 0x4e, 0x17, 0x4c, 0x9c, 0x78, 0xff, 0xf1, 0x50, - 0x80, 0x30, 0x5f, 0xfc, 0x21, 0x1a, 0xcd, 0x69, 0x6d, 0xff, 0x80, 0x00, - 0x9c, 0xb8, 0x30, 0xe5, 0x22, 0x54, 0x24, 0x18, 0x01, 0xfa, 0xfe, 0x80, - 0x00, 0x0f, 0x3e, 0x12, 0xba, 0x9a, 0x9d, 0x7c, 0x78, 0xab, 0x82, 0x44, - 0xa8, 0xa2, 0xdd, 0xb1, 0x78, 0x8f, 0xe5, 0x3d, 0x66, 0x6f, 0x6c, 0xf1, - 0xee, 0x8c, 0x54, 0xf0, 0x93, 0x57, 0x4e, 0x47, 0xf6, 0x68, 0xcb, 0xfb, - 0x23, 0xb8, 0xc0, 0x69, 0x21, 0xfc, 0x02, 0xb7, 0x33, 0x5e, 0x44, 0x31, - 0x35, 0x2c, 0xd9, 0x15, 0x61, 0x0e, 0xc4, 0x03, 0x85, 0x2d, 0xd2, 0x2b, - 0x80, 0xbe, 0x7c, 0x90, 0xff, 0x5d, 0xbd, 0x26, 0xcb, 0x21, 0x7d, 0xa6, - 0xfd, 0x9f, 0xb4, 0xc6, 0xe1, 0x5e, 0x17, 0x6e, 0x33, 0x3c, 0xcc, 0x7b, - 0x3d, 0xb8, 0x1a, 0x95, 0x8d, 0x61, 0xe7, 0x38, 0x5b, 0x0a, 0xf0, 0x5b, - 0x61, 0x67, 0x33, 0x70, 0x4c, 0xe7, 0x58, 0xdf, 0x30, 0x26, 0x0d, 0x95, - 0xa9, 0xc6, 0x98, 0x13, 0x78, 0x08, 0x61, 0x5b, 0x12, 0x17, 0x75, 0x3f, - 0x04, 0xfd, 0xbb, 0x96, 0x17, 0x6b, 0x01, 0x17, 0x9f, 0x8d, 0xc5, 0xe5, - 0xa1, 0x80, 0xeb, 0x3e, 0x07, 0xc6, 0xbd, 0xaf, 0x83, 0x82, 0x35, 0xde, - 0xad, 0x4f, 0x3d, 0x2e, 0x65, 0x44, 0x06, 0xd7, 0xee, 0x58, 0xfb, 0xc7, - 0x67, 0xc8, 0xa3, 0xc5, 0x3b, 0x3d, 0xb4, 0xd2, 0xa5, 0x3c, 0xe2, 0x14, - 0x38, 0x06, 0xf3, 0xf0, 0x0c, 0xba, 0x79, 0x74, 0xd6, 0x98, 0x63, 0xb5, - 0x4a, 0x15, 0x24, 0x3a, 0x9a, 0x73, 0xff, 0x93, 0x84, 0xaa, 0x0a, 0x0a, - 0x36, 0xb1, 0xdb, 0x6c, 0x04, 0x35, 0x85, 0x96, 0xc8, 0x8b, 0x4c, 0x92, - 0x8c, 0x08, 0x09, 0xa2, 0x91, 0xa0, 0x58, 0xe6, 0x31, 0x25, 0xa6, 0xde, - 0x81, 0xa9, 0xec, 0x3d, 0x2a, 0xad, 0xb1, 0x21, 0x75, 0xaa, 0x88, 0x20, - 0x91, 0x03, 0x77, 0x0a, 0x41, 0x0b, 0x30, 0x02, 0x19, 0xcb, 0x93, 0xab, - 0xde, 0x00, 0x00, 0x00, 0x1e, 0xd9, 0x3b, 0xbc, 0x8a, 0xae, 0xb7, 0xcd, - 0xc0, 0x5e, 0x27, 0x66, 0xc4, 0x95, 0x43, 0x37, 0xc4, 0x29, 0x7e, 0x45, - 0xd2, 0xc9, 0xcc, 0x4a, 0xae, 0x92, 0x4b, 0x8b, 0x79, 0xef, 0xe1, 0x3a, - 0xac, 0xf9, 0x42, 0xb3, 0x28, 0xc3, 0xb0, 0x0d, 0x1e, 0xb9, 0x47, 0x1c, - 0x16, 0x7a, 0x4a, 0x8e, 0xd6, 0x46, 0x53, 0x87, 0x19, 0x99, 0x56, 0x8b, - 0x02, 0xf2, 0xfa, 0xa9, 0xd1, 0x60, 0x3d, 0x45, 0xe7, 0x4b, 0x13, 0xce, - 0xe5, 0x71, 0x59, 0xf1, 0xff, 0x67, 0x39, 0x92, 0x7c, 0xec, 0xef, 0xcb, - 0x37, 0xd9, 0xf5, 0xda, 0x22, 0xe6, 0xe3, 0xa7, 0x1d, 0xa6, 0xea, 0xd1, - 0x85, 0x33, 0xeb, 0xbc, 0x2c, 0xe1, 0x51, 0xb2, 0x95, 0x97, 0x08, 0x30, - 0xb5, 0x73, 0xb0, 0xd3, 0x04, 0x56, 0x62, 0x58, 0x25, 0x01, 0x5c, 0xff, - 0xf1, 0x50, 0x80, 0x3a, 0x3f, 0xfc, 0x21, 0x1a, 0xc9, 0x31, 0x01, 0x06, - 0x00, 0x00, 0xa1, 0xb5, 0xc2, 0x16, 0x0c, 0x83, 0xe3, 0xdd, 0xf0, 0xf7, - 0x7b, 0x4f, 0x74, 0xa2, 0xfc, 0xfb, 0xf1, 0xbd, 0x6f, 0xd9, 0xcc, 0xfa, - 0xde, 0x9c, 0xf9, 0xcf, 0x3e, 0x3c, 0xaf, 0x7c, 0x50, 0x84, 0x7e, 0x5f, - 0xc5, 0x5c, 0xc4, 0xc6, 0x6f, 0xaa, 0x45, 0x84, 0x71, 0xde, 0x98, 0x67, - 0x62, 0xd7, 0xfb, 0x77, 0x90, 0xe6, 0x07, 0xa8, 0xf9, 0x41, 0x18, 0xec, - 0x5a, 0x5a, 0x6a, 0x67, 0x0f, 0x1a, 0x7b, 0x1d, 0xcb, 0xf7, 0x95, 0x1e, - 0x0b, 0x30, 0xbc, 0xbd, 0x59, 0x0e, 0x58, 0x7e, 0xc1, 0x4e, 0x74, 0xa1, - 0xbe, 0x10, 0x54, 0x89, 0x5a, 0xb7, 0x2f, 0xf7, 0xfc, 0xa2, 0x8b, 0x4f, - 0xb2, 0x1c, 0xe5, 0xee, 0x8b, 0x21, 0x19, 0x9e, 0x38, 0x0f, 0xc7, 0x02, - 0x5f, 0x2f, 0x5c, 0x42, 0xe2, 0x7c, 0x3e, 0x58, 0xe9, 0xed, 0x1b, 0xe1, - 0xc9, 0xc7, 0x9d, 0x7c, 0x96, 0x05, 0x03, 0xa2, 0x40, 0x5f, 0x08, 0x2f, - 0x87, 0x0b, 0x95, 0x5e, 0xf8, 0x57, 0x50, 0x9d, 0x4d, 0x60, 0xbd, 0xcb, - 0x5d, 0xb9, 0x86, 0xbb, 0xb1, 0x78, 0x2b, 0x08, 0xf6, 0xdb, 0x15, 0xdd, - 0x15, 0x9c, 0x8b, 0xdd, 0x41, 0x7b, 0x31, 0xb2, 0x5a, 0xd6, 0x3e, 0x8a, - 0x76, 0x67, 0x35, 0xb1, 0x7d, 0x0d, 0x77, 0x1d, 0x8a, 0x8c, 0xf9, 0xc7, - 0x6d, 0xa0, 0xc4, 0xde, 0xc4, 0x31, 0x52, 0xdb, 0x50, 0xdd, 0x5d, 0x4a, - 0x12, 0xeb, 0x73, 0x71, 0x08, 0x60, 0x52, 0xad, 0x0c, 0x21, 0x67, 0xe0, - 0x35, 0x3c, 0xef, 0x2c, 0x8b, 0x64, 0xeb, 0x12, 0x8a, 0x4c, 0xcb, 0x6d, - 0xb3, 0xfe, 0x06, 0xe0, 0x82, 0xd3, 0x4d, 0xa1, 0x30, 0xf7, 0xcf, 0x77, - 0x8e, 0xcb, 0xad, 0x4d, 0xb6, 0x67, 0x63, 0x1c, 0xed, 0x46, 0x62, 0x15, - 0x6f, 0x66, 0xee, 0xa8, 0x9b, 0x2e, 0x40, 0xf9, 0xc4, 0xdb, 0x2b, 0x94, - 0x03, 0x3a, 0x3b, 0x31, 0xda, 0x15, 0x50, 0xe2, 0x09, 0xee, 0xf8, 0xda, - 0x4b, 0xc4, 0x46, 0x66, 0xe5, 0x45, 0x72, 0xf8, 0x9c, 0x3e, 0x6a, 0x0f, - 0x94, 0x00, 0x00, 0x00, 0x3d, 0x8e, 0x72, 0xea, 0xfb, 0xff, 0xcb, 0x8d, - 0x90, 0x57, 0x00, 0x26, 0xb4, 0x91, 0x4a, 0x6b, 0x63, 0x7d, 0x31, 0xd7, - 0xfd, 0x2d, 0x11, 0x83, 0x86, 0xa4, 0x84, 0xbd, 0xe4, 0xd5, 0x0b, 0x87, - 0xf1, 0x11, 0x8c, 0x56, 0x8a, 0x13, 0xc8, 0x14, 0x84, 0x10, 0x1e, 0x3f, - 0xe9, 0x5b, 0xfe, 0xe5, 0x9e, 0x34, 0x3e, 0x49, 0x70, 0x66, 0xd2, 0x96, - 0xf1, 0xab, 0x09, 0x74, 0x57, 0x50, 0xaa, 0x16, 0xa5, 0x31, 0xbf, 0x9c, - 0xac, 0x7b, 0x5f, 0x6f, 0x77, 0x7b, 0x97, 0x5f, 0x2b, 0x04, 0x34, 0xc1, - 0x95, 0x83, 0xb2, 0x84, 0x21, 0x27, 0xc4, 0xe7, 0xa3, 0x2a, 0x36, 0xe6, - 0xa5, 0x69, 0x74, 0x88, 0x56, 0x90, 0x10, 0x5e, 0x22, 0xf4, 0x90, 0xfe, - 0xbb, 0x17, 0xa2, 0x8c, 0x7b, 0x9f, 0xe2, 0xf4, 0x66, 0xf9, 0xd6, 0xed, - 0x3a, 0xdf, 0x3e, 0x11, 0x53, 0x93, 0x65, 0x70, 0xb5, 0x14, 0xd7, 0xd9, - 0xce, 0xd7, 0xe3, 0x5f, 0xb5, 0xd7, 0xf5, 0xb2, 0x21, 0xd4, 0x9b, 0xae, - 0x66, 0xb4, 0x6f, 0x91, 0x13, 0x0c, 0x31, 0xf7, 0x19, 0x4e, 0x77, 0x85, - 0x5e, 0x34, 0x07, 0x12, 0x6d, 0x4d, 0x3f, 0xe5, 0x51, 0xf6, 0x37, 0x99, - 0x3a, 0x8e, 0xf3, 0x60, 0x99, 0x6a, 0xaa, 0xf0, 0xff, 0xf1, 0x50, 0x80, - 0x28, 0xbf, 0xfc, 0x21, 0x2a, 0xcf, 0xfe, 0xa2, 0xa5, 0x00, 0x00, 0xa2, - 0xa4, 0x20, 0xe0, 0xcb, 0x14, 0x1b, 0x18, 0x01, 0x38, 0xec, 0x03, 0xcf, - 0x89, 0x53, 0xae, 0xf5, 0xf8, 0xf7, 0xb6, 0x79, 0xce, 0x37, 0xf1, 0x75, - 0x9c, 0x03, 0x9e, 0x3b, 0x5a, 0xb3, 0x09, 0x5a, 0x6b, 0x07, 0x03, 0xcb, - 0x47, 0x98, 0x3b, 0x86, 0x74, 0x7c, 0x29, 0x08, 0x05, 0x81, 0xcb, 0xad, - 0x66, 0x7f, 0xa6, 0x8b, 0xce, 0x2d, 0x75, 0x4a, 0x09, 0xa5, 0xbc, 0xe1, - 0x8f, 0xbf, 0x58, 0x0a, 0x9b, 0xe9, 0x8e, 0xfd, 0x95, 0x35, 0xf6, 0xf0, - 0xe1, 0xa2, 0xef, 0xa2, 0x95, 0x58, 0x22, 0x0b, 0xb8, 0xa8, 0xa8, 0x17, - 0xd8, 0x05, 0x50, 0x04, 0x56, 0x02, 0x44, 0xaa, 0x40, 0x09, 0x84, 0xc1, - 0x60, 0xe1, 0x5b, 0xa7, 0x67, 0x0b, 0xd4, 0x57, 0x1d, 0xf3, 0xbc, 0x96, - 0x9b, 0x82, 0xb7, 0x23, 0x9d, 0x9a, 0xd4, 0x0d, 0x1b, 0x64, 0x6b, 0xbf, - 0x35, 0xb9, 0x59, 0xb4, 0x0b, 0xb1, 0x70, 0x95, 0xeb, 0xb3, 0x7a, 0xea, - 0x8a, 0xdd, 0x17, 0x51, 0x4d, 0x5b, 0xc9, 0x31, 0x14, 0x8e, 0x68, 0xf9, - 0xf1, 0x9d, 0x1c, 0x01, 0xc4, 0xe9, 0x31, 0x4d, 0x27, 0x9f, 0x91, 0x81, - 0xe7, 0xc0, 0x0f, 0xbb, 0x0b, 0xec, 0xea, 0xb9, 0x16, 0x0a, 0x2c, 0x92, - 0x68, 0xc9, 0x2c, 0xde, 0xcb, 0x2d, 0xfb, 0xee, 0xe1, 0x2c, 0x80, 0xd1, - 0xa8, 0x5f, 0x7a, 0x54, 0xf7, 0x59, 0x3d, 0x37, 0xd5, 0x2e, 0x7b, 0xd6, - 0xc9, 0x5c, 0x93, 0xb2, 0x59, 0x45, 0xaf, 0xcd, 0xc0, 0x6d, 0x19, 0xa8, - 0x9c, 0x33, 0xa9, 0x82, 0x8e, 0xc9, 0x2e, 0x2c, 0x3c, 0x7e, 0xbe, 0x19, - 0xca, 0xb2, 0x5a, 0x7c, 0xc2, 0x82, 0x75, 0x94, 0x28, 0xa1, 0x2a, 0x44, - 0x31, 0xa3, 0xc2, 0x21, 0xf7, 0x00, 0x26, 0x4a, 0x94, 0xba, 0xa8, 0x52, - 0xf2, 0xef, 0x35, 0x39, 0x86, 0xb6, 0xaa, 0x80, 0x03, 0x29, 0xaf, 0x3e, - 0x14, 0x41, 0x07, 0x18, 0xcc, 0xe7, 0x1b, 0xdc, 0x22, 0x56, 0x18, 0x9e, - 0x38, 0x1a, 0xf6, 0xe6, 0x6e, 0x89, 0x85, 0x80, 0xfe, 0x05, 0x43, 0x0a, - 0x22, 0x0b, 0x45, 0x56, 0x94, 0x5f, 0xcc, 0xea, 0x61, 0x6b, 0x64, 0xee, - 0xab, 0x1e, 0xbb, 0xf9, 0x73, 0xff, 0x8d, 0x43, 0x1c, 0x12, 0xdb, 0xd0, - 0x8a, 0xf1, 0xcd, 0x2c, 0x42, 0x04, 0xa8, 0xb6, 0xbe, 0xff, 0xf1, 0x50, - 0x80, 0x35, 0x1f, 0xfc, 0x21, 0x4c, 0x6c, 0xd3, 0x34, 0xb0, 0x3a, 0x05, - 0x31, 0x4e, 0xc5, 0x4a, 0x2a, 0x93, 0x44, 0x62, 0xe9, 0x34, 0x84, 0x49, - 0x94, 0x44, 0x71, 0x40, 0x00, 0x00, 0xe1, 0xc8, 0x38, 0xbe, 0x67, 0xef, - 0xc7, 0xfc, 0x0e, 0xbb, 0xf6, 0xf6, 0xe7, 0xcf, 0x98, 0x9d, 0xf6, 0x8e, - 0x48, 0xe3, 0xbe, 0xfc, 0x37, 0x1c, 0xcb, 0xfd, 0x56, 0x8b, 0xa1, 0x51, - 0x72, 0xa6, 0x2a, 0x55, 0xbe, 0x5b, 0x0a, 0x2e, 0x92, 0x1f, 0x1a, 0xd4, - 0x99, 0xe7, 0xf2, 0x88, 0xbc, 0x27, 0xec, 0xb0, 0xbf, 0xfd, 0x9a, 0x72, - 0x90, 0x7b, 0x65, 0xda, 0x68, 0x50, 0xd7, 0xf5, 0x87, 0xac, 0x86, 0x5b, - 0xc0, 0x10, 0x7a, 0x2b, 0x10, 0xc2, 0x02, 0x47, 0x43, 0x8e, 0xaf, 0x10, - 0xdf, 0xda, 0xb2, 0xe0, 0x0b, 0x78, 0x41, 0x99, 0x05, 0x23, 0x16, 0x13, - 0xa7, 0x00, 0xe6, 0x86, 0x81, 0x5b, 0xb6, 0xfa, 0x9f, 0xda, 0x8a, 0xc7, - 0xf3, 0xaf, 0x83, 0x17, 0xf3, 0xfe, 0x38, 0x43, 0x07, 0xfd, 0x84, 0x77, - 0x79, 0x80, 0xe0, 0x8d, 0xe3, 0x40, 0xd6, 0x28, 0x21, 0xb4, 0xeb, 0xe4, - 0xc8, 0x85, 0x1a, 0xe7, 0xce, 0x7c, 0xe3, 0x7d, 0x35, 0xcd, 0x33, 0xe0, - 0x89, 0x24, 0x59, 0x33, 0x5d, 0x8a, 0xf5, 0xc8, 0x20, 0xcf, 0x0b, 0x82, - 0x46, 0x91, 0xb8, 0x58, 0x30, 0xaf, 0xc9, 0x8c, 0x7b, 0x80, 0x08, 0x05, - 0x8a, 0x65, 0xf0, 0x9b, 0xe8, 0xbc, 0xfa, 0xd7, 0x47, 0xd0, 0xe6, 0x3f, - 0xc8, 0x00, 0x19, 0x32, 0xb8, 0x97, 0x25, 0x78, 0xb5, 0x9c, 0x0a, 0xab, - 0xc3, 0xac, 0x89, 0xbd, 0x7c, 0x01, 0x10, 0x04, 0x2e, 0x90, 0x96, 0xd0, - 0x23, 0x9e, 0xd0, 0x05, 0x94, 0x27, 0xff, 0x3f, 0x5a, 0x34, 0x0d, 0x10, - 0x4e, 0xff, 0x85, 0x2e, 0x7d, 0xfe, 0x7a, 0xaa, 0xa8, 0xf0, 0x88, 0x07, - 0x13, 0x27, 0x11, 0x87, 0x16, 0x68, 0xea, 0x48, 0x49, 0x2b, 0x8d, 0x29, - 0xc3, 0x47, 0xa5, 0x02, 0x2e, 0x7e, 0x75, 0xa1, 0x3f, 0xb0, 0xd2, 0x7d, - 0xa4, 0xcd, 0x17, 0xd8, 0xac, 0xbd, 0xd2, 0x54, 0xa4, 0x53, 0x9e, 0x34, - 0xa4, 0xa8, 0xa9, 0x3a, 0xca, 0x85, 0x4a, 0xf5, 0xab, 0x49, 0x52, 0x79, - 0xf5, 0xed, 0xba, 0x9f, 0xe2, 0x97, 0x7d, 0x34, 0x0e, 0x4f, 0x7d, 0xa4, - 0x90, 0x00, 0x0d, 0x8f, 0xd3, 0x6c, 0x16, 0xb7, 0x30, 0xd2, 0x8d, 0x2d, - 0x43, 0x53, 0x62, 0x3d, 0x2f, 0xe3, 0x07, 0x4e, 0x0e, 0x9e, 0xfd, 0xa9, - 0xc9, 0x3b, 0x08, 0x17, 0xc2, 0xbe, 0xcd, 0xb9, 0x6a, 0x23, 0xfd, 0xe7, - 0xc1, 0x78, 0xd3, 0x17, 0xc3, 0xce, 0x1d, 0xbc, 0xf9, 0xea, 0x9b, 0x36, - 0x17, 0x72, 0xb2, 0x6e, 0x47, 0xb2, 0x87, 0x0e, 0xaf, 0x03, 0x84, 0x4a, - 0x15, 0x4d, 0x9c, 0x91, 0x78, 0xcd, 0x5c, 0x93, 0x5d, 0x20, 0x06, 0x52, - 0x9d, 0x6a, 0xc2, 0x4a, 0xfb, 0xac, 0x3a, 0x60, 0x35, 0xb1, 0xf3, 0x75, - 0x09, 0xa2, 0x73, 0xea, 0x62, 0x34, 0x00, 0x49, 0xf7, 0x15, 0xd9, 0x81, - 0xb7, 0xc5, 0x6f, 0xd3, 0x57, 0xa9, 0x72, 0xe7, 0x06, 0xd9, 0xb9, 0x77, - 0xc0, 0xff, 0xf1, 0x50, 0x80, 0x27, 0xbf, 0xfc, 0x21, 0x7a, 0xcf, 0xd5, - 0xfb, 0xff, 0x7f, 0xff, 0xa3, 0xb1, 0x52, 0xd9, 0x48, 0x51, 0x4a, 0x05, - 0x8a, 0x0c, 0xbc, 0xbc, 0xb0, 0x65, 0x80, 0x30, 0xeb, 0x15, 0xd5, 0x67, - 0x9f, 0x3d, 0x66, 0x81, 0xcb, 0xcf, 0x7f, 0xae, 0xa8, 0x03, 0x8a, 0xe6, - 0x17, 0x8b, 0x31, 0x43, 0x14, 0xae, 0xb8, 0xd9, 0x57, 0xf2, 0xdf, 0xbe, - 0x38, 0x38, 0x83, 0x44, 0x3a, 0x20, 0x34, 0x13, 0x24, 0x0b, 0x36, 0x33, - 0x5e, 0x71, 0x15, 0x1c, 0xf2, 0x39, 0x29, 0x1e, 0x7a, 0x81, 0xc6, 0xa9, - 0xc8, 0x85, 0x15, 0xea, 0xa3, 0x7e, 0xda, 0x6d, 0xa2, 0x89, 0x01, 0xd0, - 0xee, 0x96, 0x56, 0x39, 0x24, 0xb1, 0x2e, 0x1a, 0xa8, 0x5e, 0xe6, 0x6f, - 0xc1, 0x5f, 0x45, 0x4e, 0x6e, 0x0e, 0x41, 0x85, 0x0c, 0xd5, 0x80, 0x35, - 0x99, 0x55, 0xcf, 0xa9, 0xdf, 0x33, 0xab, 0x66, 0x8a, 0x74, 0xa4, 0xf2, - 0x26, 0xf6, 0x95, 0x85, 0x23, 0x24, 0x09, 0xb5, 0x30, 0xe0, 0xe7, 0xea, - 0x57, 0xcf, 0xc4, 0x40, 0x8d, 0x25, 0x77, 0x6c, 0x4e, 0xb6, 0x5e, 0x23, - 0x26, 0x08, 0xbc, 0x93, 0x6c, 0xde, 0xc7, 0xe2, 0xc8, 0x92, 0x3f, 0xc6, - 0x7e, 0x52, 0xca, 0x43, 0x5e, 0xf2, 0x63, 0xad, 0x39, 0x49, 0x21, 0x70, - 0x19, 0x80, 0xd8, 0x18, 0x09, 0x1d, 0xd7, 0x0c, 0x34, 0x1d, 0x89, 0xaa, - 0xf9, 0xd4, 0xc9, 0xdf, 0x52, 0x93, 0xc9, 0xe0, 0xac, 0x74, 0x91, 0x22, - 0x49, 0xdd, 0x86, 0xaa, 0x78, 0xc8, 0xee, 0xa9, 0x14, 0xdd, 0x85, 0xbd, - 0xa2, 0x15, 0x53, 0xc7, 0x18, 0xec, 0xea, 0x63, 0x11, 0x5e, 0x57, 0xb5, - 0x1c, 0x1e, 0xc3, 0x05, 0x63, 0xa1, 0xfa, 0x00, 0x00, 0x00, 0x02, 0x42, - 0x89, 0x55, 0x2e, 0x73, 0xad, 0x85, 0xef, 0xf3, 0xf4, 0x76, 0x46, 0x33, - 0xa0, 0x28, 0xa8, 0xf4, 0x43, 0x99, 0xab, 0x70, 0x08, 0x0c, 0xd1, 0x38, - 0x1c, 0x30, 0xb8, 0x63, 0x30, 0x7b, 0xde, 0x74, 0xd4, 0x50, 0x73, 0xba, - 0xa2, 0x3b, 0x3b, 0x4c, 0xee, 0x66, 0x96, 0x31, 0x82, 0x03, 0xba, 0xbf, - 0xca, 0x7b, 0xd9, 0x63, 0x9e, 0xd7, 0xc7, 0xbc, 0x1a, 0xec, 0x12, 0xd1, - 0xa5, 0x4d, 0x78, 0xf8, 0xd8, 0xf3, 0x53, 0x6a, 0x64, 0xb1, 0xb8, 0xc7, - 0x7b, 0x68, 0x32, 0x35, 0x4f, 0xc0, 0xff, 0xf1, 0x50, 0x80, 0x2c, 0xff, - 0xfc, 0x21, 0x1a, 0xcf, 0xa5, 0x63, 0x7e, 0x00, 0x00, 0xa0, 0xb4, 0x42, - 0xe8, 0x2c, 0x62, 0x22, 0x19, 0x8e, 0x00, 0x00, 0x00, 0x0d, 0x4e, 0x2b, - 0x7d, 0x3a, 0xd5, 0x6a, 0x40, 0xa3, 0x2c, 0x51, 0xf6, 0x4b, 0xae, 0x7c, - 0x0e, 0x1a, 0x6b, 0xa4, 0x68, 0xf4, 0x23, 0x40, 0x63, 0x69, 0x96, 0x01, - 0x8c, 0x92, 0x06, 0x56, 0x16, 0xab, 0x00, 0xa9, 0xb3, 0x29, 0x45, 0xb7, - 0x4d, 0x23, 0xa6, 0x68, 0x84, 0xb2, 0xdc, 0x60, 0x4b, 0x41, 0xec, 0x6f, - 0xb6, 0xa4, 0x54, 0x45, 0x21, 0x89, 0x71, 0x6a, 0x40, 0x2c, 0xa0, 0x3a, - 0x92, 0xd7, 0x4e, 0xc4, 0xe7, 0x6d, 0x73, 0x5b, 0x25, 0xf1, 0x7b, 0x36, - 0xe9, 0x27, 0x53, 0x6c, 0x55, 0x52, 0xb5, 0xa1, 0x81, 0x32, 0xad, 0xcc, - 0x94, 0x26, 0x13, 0x47, 0x76, 0x69, 0x8a, 0x21, 0xdc, 0x0a, 0xd4, 0x1a, - 0xbf, 0x3f, 0x05, 0x61, 0x7a, 0x99, 0xa3, 0xd2, 0x92, 0x09, 0x24, 0xc9, - 0x1a, 0xe1, 0x44, 0x89, 0x23, 0xad, 0xee, 0x6a, 0x64, 0xb3, 0xab, 0x81, - 0x47, 0x68, 0x6c, 0xb0, 0x42, 0x5e, 0x10, 0x90, 0x29, 0x2d, 0xb0, 0xc7, - 0x5b, 0x88, 0x52, 0x74, 0x59, 0xbc, 0x25, 0x14, 0xd3, 0x95, 0x0a, 0x2f, - 0x67, 0x62, 0x98, 0x15, 0xd3, 0xa2, 0xa7, 0x8f, 0x85, 0xef, 0xd2, 0x6a, - 0xb2, 0x02, 0x8a, 0x56, 0xeb, 0x46, 0x61, 0x58, 0x67, 0xed, 0xeb, 0x12, - 0x57, 0x4b, 0x6b, 0x9c, 0x59, 0x69, 0xef, 0x16, 0xc4, 0x01, 0x3a, 0xea, - 0x03, 0x80, 0xce, 0x04, 0xa4, 0xa2, 0xb2, 0x2f, 0xe7, 0xb5, 0xe5, 0xb7, - 0x65, 0x3d, 0xa1, 0x51, 0x24, 0x6b, 0xf8, 0x73, 0x9d, 0xab, 0xb2, 0x6d, - 0xb6, 0x6d, 0x7d, 0xf8, 0x35, 0x22, 0x3a, 0x3f, 0xd5, 0xbc, 0x72, 0x8b, - 0x2f, 0x87, 0x9f, 0x14, 0xd7, 0x3e, 0x14, 0xd2, 0x6b, 0x48, 0xc9, 0xd6, - 0x9b, 0xb0, 0xce, 0x82, 0x92, 0x83, 0xb0, 0xa2, 0x4c, 0xde, 0xf0, 0xe7, - 0x8e, 0x5c, 0x73, 0xc7, 0x3c, 0x39, 0x38, 0x73, 0xc0, 0x39, 0xbb, 0x26, - 0xd7, 0x15, 0x97, 0x52, 0x08, 0xb9, 0x8b, 0xc6, 0x13, 0xd7, 0xda, 0x4e, - 0x88, 0x48, 0x40, 0x5c, 0x39, 0x41, 0xdf, 0x6a, 0x1d, 0x6f, 0x4c, 0x00, - 0x9b, 0x4f, 0x20, 0x1d, 0x27, 0x7c, 0x5f, 0xf9, 0x4e, 0x85, 0xd4, 0xa7, - 0xfc, 0xda, 0x7a, 0x8b, 0xe1, 0xc8, 0x52, 0x82, 0x6a, 0x34, 0xff, 0xb3, - 0x83, 0x8b, 0x14, 0x7b, 0x47, 0x5d, 0x25, 0xac, 0x4f, 0xdb, 0x2d, 0x4d, - 0x0f, 0x99, 0x32, 0x69, 0xf6, 0xb6, 0xd1, 0x23, 0x9b, 0x15, 0xe7, 0x44, - 0xe8, 0x1c, 0x0e, 0x4b, 0x80, 0xff, 0xf1, 0x50, 0x80, 0x2d, 0x9f, 0xfc, - 0x21, 0x1a, 0xcf, 0xf3, 0x9f, 0x7f, 0x91, 0x80, 0xa0, 0xb4, 0x41, 0xdc, - 0xe8, 0xa5, 0x24, 0x1c, 0x00, 0x4a, 0x25, 0x5d, 0x54, 0xab, 0xcb, 0x92, - 0x49, 0x5e, 0x5e, 0xbe, 0x2b, 0x5e, 0xd2, 0xa3, 0x62, 0x2d, 0x75, 0xa3, - 0x53, 0xf3, 0x75, 0xcf, 0xca, 0x27, 0xdc, 0xac, 0xa3, 0x06, 0x27, 0x79, - 0xf7, 0xb6, 0xbe, 0x73, 0xa2, 0x4a, 0xa7, 0x0a, 0x02, 0x58, 0x49, 0x67, - 0x14, 0xaa, 0x5e, 0xf5, 0xe1, 0xbd, 0x5e, 0x5b, 0xcc, 0x10, 0xed, 0x06, - 0x30, 0xe9, 0xf2, 0x4e, 0xa4, 0x31, 0xab, 0xd9, 0x0c, 0xf6, 0x5e, 0xee, - 0xeb, 0xcc, 0xb9, 0xc7, 0x0c, 0x95, 0xa1, 0x82, 0x98, 0xf0, 0x92, 0x15, - 0x56, 0x9d, 0xe9, 0xbb, 0xcc, 0x86, 0xbb, 0x27, 0x44, 0xe2, 0x85, 0x14, - 0x0d, 0x61, 0x8a, 0xed, 0xbe, 0xb7, 0x7b, 0xb5, 0x13, 0x50, 0x0b, 0xca, - 0x6c, 0x6d, 0xb6, 0xf1, 0x4b, 0xd4, 0x8e, 0xb4, 0xd5, 0x0b, 0xcb, 0xd9, - 0xad, 0x56, 0x78, 0x32, 0xfd, 0xb5, 0x2a, 0x51, 0xd8, 0x8f, 0x42, 0xf1, - 0xea, 0xdb, 0xd6, 0x17, 0xaa, 0x2f, 0x31, 0xce, 0x6b, 0xc6, 0xab, 0x58, - 0x64, 0x03, 0x9f, 0x25, 0x02, 0xea, 0x1a, 0x6f, 0xf7, 0xe3, 0x13, 0xac, - 0x10, 0x33, 0x1b, 0xdf, 0xb2, 0xa6, 0x27, 0x4d, 0xd9, 0x4a, 0x2c, 0x29, - 0x8e, 0xfa, 0x84, 0x51, 0x2a, 0x13, 0xa1, 0x00, 0x63, 0x06, 0x9a, 0x54, - 0xab, 0xa9, 0x09, 0x02, 0xd0, 0x2a, 0x23, 0xc8, 0x94, 0x2a, 0xb1, 0x5c, - 0x51, 0x3d, 0xc9, 0x34, 0x6f, 0xc5, 0xa6, 0xa0, 0xa3, 0x91, 0x0a, 0x38, - 0x67, 0x45, 0x0c, 0x28, 0x52, 0x20, 0x72, 0x4a, 0x40, 0xbc, 0xf4, 0x06, - 0x96, 0x08, 0xf1, 0x2a, 0x22, 0x94, 0x09, 0x27, 0x71, 0xa3, 0x81, 0x89, - 0xc4, 0xbe, 0xdd, 0x23, 0x31, 0xa1, 0x84, 0x32, 0xef, 0xd1, 0x97, 0x86, - 0x89, 0x61, 0x38, 0xb0, 0x8c, 0x50, 0x86, 0xa9, 0xe8, 0xc9, 0xd1, 0xa0, - 0x63, 0xd8, 0x90, 0x74, 0x16, 0x39, 0xa3, 0xde, 0x00, 0x25, 0x12, 0xae, - 0xaa, 0x55, 0xe5, 0xc9, 0x25, 0x67, 0x9f, 0x19, 0xc6, 0x4b, 0x15, 0x82, - 0xd6, 0xec, 0xed, 0xf2, 0x59, 0x9c, 0x8f, 0x32, 0x53, 0x30, 0x17, 0xc7, - 0xb2, 0x48, 0xbd, 0xd2, 0x85, 0x58, 0x9d, 0xc8, 0xde, 0xea, 0xff, 0x3f, - 0xbf, 0xc0, 0x2d, 0x9d, 0x99, 0x81, 0x11, 0x30, 0xf1, 0x08, 0x03, 0x62, - 0xa6, 0x18, 0x85, 0xcf, 0x5e, 0xbb, 0x18, 0x0b, 0x85, 0xd4, 0xba, 0xdf, - 0xbd, 0x94, 0xda, 0xa4, 0xd6, 0xfd, 0x0a, 0xc9, 0xae, 0xea, 0x4c, 0xdc, - 0x56, 0x75, 0xea, 0xa7, 0xad, 0x76, 0x7a, 0xcd, 0xc0, 0xff, 0xf1, 0x50, - 0x80, 0x29, 0xff, 0xfc, 0x21, 0x2a, 0xcf, 0xd1, 0x9f, 0x35, 0x80, 0x00, - 0xa2, 0xb4, 0x32, 0x20, 0x88, 0x42, 0x32, 0x1d, 0x8c, 0x05, 0xe4, 0xa9, - 0xae, 0x53, 0x5c, 0xf0, 0xe7, 0x55, 0x7c, 0xf1, 0xc7, 0x33, 0x52, 0x5e, - 0x4e, 0x25, 0xf1, 0x92, 0x73, 0xc0, 0xb6, 0x74, 0x1f, 0x67, 0xd9, 0xb7, - 0x92, 0x47, 0x52, 0xd3, 0x99, 0x84, 0xc5, 0xca, 0xec, 0x07, 0x3b, 0x62, - 0x64, 0xa9, 0x96, 0xf0, 0x69, 0xce, 0xb4, 0x86, 0xdf, 0x59, 0xcc, 0xc1, - 0x41, 0x00, 0x23, 0x25, 0x63, 0x1f, 0x78, 0xc8, 0x29, 0x45, 0x18, 0x60, - 0x08, 0xdc, 0x28, 0x10, 0xf1, 0xad, 0x67, 0x3a, 0x7b, 0x59, 0x22, 0x9f, - 0x4e, 0x0a, 0xcd, 0xdc, 0x6e, 0x3e, 0xc9, 0x14, 0xe1, 0x48, 0x15, 0xa4, - 0xd4, 0xfe, 0x51, 0x5c, 0xb5, 0x00, 0x16, 0x22, 0x66, 0x60, 0x15, 0xed, - 0x4a, 0x6f, 0x7b, 0x28, 0xb6, 0x7b, 0x61, 0x70, 0x25, 0xb5, 0x53, 0x8a, - 0x4b, 0xee, 0xdc, 0x55, 0xa9, 0xd9, 0xd0, 0x95, 0x7b, 0x0a, 0xb6, 0x39, - 0x00, 0x51, 0xae, 0xef, 0x36, 0x55, 0xc1, 0x65, 0xad, 0x8a, 0xa9, 0xdf, - 0xeb, 0xd1, 0xa1, 0x2e, 0xdf, 0x70, 0x68, 0xbd, 0xcd, 0x1a, 0x63, 0x92, - 0x85, 0xe1, 0xd0, 0xed, 0xd5, 0x3d, 0x6e, 0xad, 0x3a, 0x82, 0x19, 0x6c, - 0xcd, 0x26, 0x75, 0xb8, 0x6d, 0x43, 0xaa, 0xe8, 0x1d, 0x12, 0xae, 0x37, - 0x10, 0xa3, 0xde, 0xce, 0x68, 0xa7, 0x45, 0xfb, 0x94, 0xcf, 0xaf, 0x04, - 0x38, 0xab, 0x12, 0x42, 0x31, 0xc6, 0x76, 0xc1, 0xbc, 0x2d, 0x5a, 0xd0, - 0x16, 0xc9, 0xce, 0x0a, 0x78, 0x49, 0xe8, 0x8c, 0xa0, 0xa7, 0x00, 0x6a, - 0xac, 0x9a, 0x73, 0xbe, 0x68, 0xce, 0x6a, 0x02, 0xa7, 0xdd, 0x9d, 0x15, - 0x2a, 0x50, 0x89, 0x10, 0xfb, 0xc1, 0x6c, 0x26, 0xb9, 0xba, 0xd7, 0x3c, - 0x2a, 0x65, 0x84, 0x99, 0x79, 0x13, 0x34, 0x53, 0x76, 0x04, 0x94, 0x9e, - 0xaf, 0x34, 0xe0, 0xcf, 0x38, 0x22, 0x0c, 0x04, 0x00, 0xfb, 0x23, 0xb8, - 0xc5, 0x82, 0x18, 0xda, 0xef, 0x3d, 0x1d, 0x4f, 0x94, 0x69, 0xef, 0xb8, - 0xe8, 0x30, 0xd9, 0x8c, 0x67, 0x6b, 0x65, 0xd2, 0xc2, 0x14, 0xbb, 0x27, - 0x10, 0x47, 0x60, 0x15, 0x0c, 0xfc, 0x13, 0xdf, 0xe4, 0xaf, 0x9c, 0xb6, - 0xd8, 0xb5, 0xb0, 0x68, 0x33, 0x35, 0xb2, 0xba, 0xf9, 0xd8, 0xe8, 0x6d, - 0x1d, 0x4b, 0x76, 0x5d, 0xe7, 0xa2, 0x36, 0x0e, 0xff, 0xf1, 0x50, 0x80, - 0x44, 0x5f, 0xfc, 0x20, 0x9c, 0x4c, 0xd9, 0x69, 0x99, 0xd0, 0x54, 0x96, - 0xb4, 0x73, 0x69, 0xb0, 0x9a, 0x75, 0xe5, 0x90, 0x00, 0x17, 0xd7, 0xb4, - 0xe3, 0x8c, 0xff, 0xa1, 0xc2, 0xfc, 0x2f, 0xcf, 0xd7, 0x9f, 0xfc, 0x0b, - 0xc7, 0x17, 0xe7, 0x5d, 0x3d, 0xf5, 0x13, 0xc0, 0x3d, 0xf6, 0xf2, 0x07, - 0x00, 0x28, 0x61, 0x52, 0xdb, 0x89, 0x74, 0xb2, 0x05, 0x17, 0xca, 0x9e, - 0x80, 0x93, 0x6c, 0x7a, 0x9f, 0x7e, 0x2e, 0xc6, 0x3f, 0xa3, 0x9c, 0x30, - 0x95, 0xe4, 0x42, 0xaa, 0x76, 0xe0, 0xeb, 0x4d, 0x37, 0x65, 0x1b, 0x35, - 0xf6, 0xec, 0x04, 0x40, 0x13, 0x80, 0xe8, 0xb2, 0x79, 0x5e, 0xe7, 0x95, - 0x6e, 0xb7, 0x90, 0x96, 0xa7, 0x72, 0xc7, 0xd5, 0xa9, 0x98, 0xe2, 0xee, - 0x5b, 0x6c, 0xa5, 0x9d, 0x41, 0x72, 0x32, 0x55, 0xd1, 0xc1, 0x1a, 0x8d, - 0x45, 0x30, 0x1c, 0x58, 0x0c, 0x8c, 0xba, 0xd1, 0x42, 0x97, 0x1b, 0xd2, - 0x66, 0x77, 0x4d, 0xab, 0x5a, 0x9b, 0x7c, 0x34, 0x95, 0xd9, 0xd1, 0x84, - 0xc3, 0x07, 0x5e, 0x2d, 0xaa, 0x46, 0x90, 0x72, 0x4b, 0xfb, 0x57, 0xf1, - 0xba, 0x72, 0xcf, 0xf5, 0xdb, 0x2d, 0x1f, 0xaf, 0x7f, 0x0f, 0x4f, 0xc9, - 0x8c, 0x6f, 0x4c, 0xc3, 0x4f, 0x0d, 0x0c, 0xe8, 0xea, 0x3a, 0x9e, 0x4e, - 0x0b, 0x4f, 0x72, 0x48, 0xaf, 0xb1, 0x66, 0x79, 0xee, 0x7a, 0x94, 0xb2, - 0x45, 0x1b, 0x92, 0xc1, 0xb9, 0x3e, 0xea, 0x88, 0xa1, 0x9a, 0xb4, 0x8a, - 0x28, 0xf8, 0xdb, 0xfd, 0x06, 0x32, 0xff, 0xff, 0x3b, 0x17, 0xb4, 0x75, - 0xcd, 0xcf, 0xfa, 0x75, 0xee, 0xb1, 0xf7, 0x69, 0xea, 0xfa, 0xa3, 0x3b, - 0xf4, 0x81, 0x9e, 0x6a, 0x23, 0x25, 0xcc, 0x72, 0xd5, 0x63, 0x27, 0xed, - 0xd6, 0x93, 0x14, 0xff, 0xa4, 0x94, 0x29, 0xec, 0x3b, 0x56, 0xc3, 0xf2, - 0xbd, 0xeb, 0xd8, 0xb8, 0x1e, 0xbb, 0x64, 0xcc, 0xb9, 0x57, 0x03, 0xd6, - 0x6e, 0x4a, 0xc8, 0x58, 0x85, 0x7b, 0xf7, 0x1a, 0x5b, 0xbc, 0x94, 0x67, - 0x13, 0x1b, 0x5b, 0x2d, 0xae, 0x9a, 0x53, 0x50, 0xa9, 0xa4, 0x36, 0xa6, - 0xcd, 0x96, 0xd5, 0x40, 0x0f, 0x5e, 0x82, 0x2a, 0xee, 0x3f, 0x5f, 0x3f, - 0xf8, 0x1e, 0x79, 0xd3, 0x6f, 0x3f, 0xaf, 0xfc, 0x13, 0x5c, 0xcb, 0xf3, - 0x75, 0xd0, 0xe1, 0x42, 0xac, 0xf9, 0x3c, 0xf0, 0x18, 0xa7, 0xe7, 0xf3, - 0xac, 0x82, 0x8e, 0x07, 0x70, 0x4b, 0xd6, 0x6a, 0x34, 0x92, 0x75, 0x55, - 0x6a, 0x68, 0xc5, 0xa8, 0xd0, 0x37, 0x3d, 0xd0, 0x61, 0x6c, 0x5e, 0x34, - 0x7c, 0x5f, 0x32, 0xe4, 0xf9, 0xf0, 0xc4, 0xf9, 0xe3, 0xd9, 0x93, 0x5e, - 0x2a, 0xc3, 0x49, 0xf3, 0xac, 0x95, 0x6e, 0x7c, 0xde, 0x8b, 0xd1, 0x4d, - 0x02, 0x9f, 0x82, 0xf3, 0x5e, 0x7b, 0x2a, 0x05, 0x81, 0x73, 0x59, 0x95, - 0x54, 0x06, 0xdc, 0x60, 0x82, 0x3a, 0x94, 0x25, 0x0a, 0x33, 0x55, 0x7b, - 0x5e, 0x2a, 0xab, 0x7d, 0x6a, 0xa8, 0x82, 0x33, 0x6e, 0x4d, 0xc4, 0x40, - 0x26, 0x92, 0x88, 0xe7, 0x57, 0x70, 0xa7, 0x64, 0x7c, 0xb2, 0x75, 0x5a, - 0x4b, 0xd6, 0xa9, 0x56, 0xa9, 0x55, 0x82, 0x09, 0xda, 0x80, 0x6a, 0x41, - 0xa3, 0xfa, 0x0f, 0x95, 0xe9, 0x0c, 0xc3, 0x1a, 0x79, 0x1f, 0xb7, 0x26, - 0xb1, 0xd0, 0x1a, 0x7f, 0x28, 0xfa, 0x47, 0xff, 0xd6, 0x07, 0xdf, 0x9d, - 0x0f, 0xa7, 0x01, 0xe4, 0x3f, 0x9f, 0xee, 0x00, 0x42, 0xc9, 0xfb, 0xfa, - 0x3a, 0xfe, 0x38, 0x34, 0xf9, 0xd2, 0xcc, 0x2c, 0x01, 0x4c, 0xd4, 0x45, - 0x80, 0x00, 0x7c, 0x9e, 0x9b, 0xed, 0xcf, 0x60, 0xaf, 0x7b, 0x5f, 0x41, - 0x74, 0x76, 0x1a, 0x6d, 0x93, 0x70, 0x19, 0x74, 0x0f, 0x37, 0x91, 0xa9, - 0x07, 0xc0, 0x1b, 0x0a, 0x95, 0x6e, 0xfd, 0xa1, 0x71, 0x3a, 0x40, 0x7b, - 0xf6, 0x95, 0xf4, 0x3a, 0x8e, 0x65, 0xd1, 0x8b, 0xce, 0x6f, 0x3c, 0x15, - 0x5c, 0x1c, 0xe7, 0x3c, 0xff, 0x37, 0x1a, 0xb2, 0xff, 0x87, 0x11, 0xfb, - 0x65, 0x3c, 0xff, 0xf1, 0x50, 0x80, 0x2d, 0x7f, 0xfc, 0x21, 0x7a, 0xc8, - 0x5d, 0x01, 0x6c, 0x00, 0x00, 0xa6, 0xa1, 0x42, 0xd8, 0x88, 0xc2, 0x22, - 0x15, 0x89, 0x01, 0x04, 0x53, 0xac, 0xd7, 0x8e, 0xb3, 0xf5, 0xfe, 0xbc, - 0x4e, 0x7c, 0xfa, 0xb2, 0xa7, 0x9d, 0xee, 0xe6, 0xfa, 0xe3, 0x7e, 0x57, - 0x77, 0x5c, 0x08, 0x0b, 0x37, 0x49, 0xed, 0x1e, 0x2d, 0xd0, 0x23, 0x52, - 0x9e, 0x54, 0x0e, 0x22, 0x8f, 0xcc, 0x93, 0x65, 0x09, 0x22, 0x96, 0xe0, - 0xc0, 0x25, 0x29, 0xfc, 0xe2, 0x55, 0x2c, 0x99, 0x90, 0xd7, 0xc5, 0x9f, - 0xde, 0x5d, 0x88, 0x02, 0x80, 0x2e, 0x55, 0xe9, 0xf9, 0x07, 0xb9, 0x8d, - 0x5a, 0xbb, 0xde, 0x84, 0xb2, 0xfe, 0x97, 0x10, 0x7e, 0xbc, 0x6b, 0x54, - 0x59, 0xb0, 0x58, 0xf3, 0x6e, 0xab, 0x55, 0x5d, 0xe5, 0x22, 0x96, 0x2d, - 0xa5, 0x9a, 0x19, 0xcd, 0x66, 0xf9, 0x37, 0x78, 0x1e, 0x5b, 0x13, 0x0f, - 0x94, 0x6b, 0x10, 0x3c, 0xcc, 0xee, 0x1f, 0xc4, 0xaa, 0xfd, 0x62, 0x43, - 0x91, 0x0b, 0xa4, 0xf2, 0xbf, 0xb1, 0x16, 0x1c, 0x0f, 0x0d, 0x28, 0xec, - 0x92, 0x34, 0xc0, 0x63, 0xa0, 0x07, 0xcd, 0x9b, 0xbf, 0x1b, 0x41, 0xb9, - 0xdc, 0x1e, 0x9f, 0x9e, 0xcf, 0xa4, 0xdf, 0xb6, 0xef, 0x38, 0xa3, 0x9e, - 0x57, 0x2d, 0xe9, 0xfb, 0x26, 0x11, 0x72, 0xab, 0xdf, 0xd1, 0xfe, 0x3f, - 0xfb, 0xfe, 0xce, 0x36, 0x00, 0xa5, 0x2d, 0x72, 0xcf, 0x2e, 0x43, 0x63, - 0xae, 0x52, 0xce, 0x53, 0xc6, 0x76, 0x9a, 0xe9, 0xba, 0xdb, 0x2b, 0xb9, - 0x70, 0xf8, 0x81, 0xef, 0x35, 0xdd, 0xd4, 0x73, 0x77, 0xeb, 0xa8, 0x28, - 0xe9, 0x65, 0x76, 0x25, 0xba, 0x1e, 0x7b, 0x69, 0xdf, 0x33, 0x69, 0x69, - 0x39, 0xee, 0xe7, 0x21, 0xe3, 0x92, 0x54, 0xc5, 0x3d, 0x40, 0xc4, 0x94, - 0x4e, 0x3c, 0x34, 0x99, 0xab, 0xaa, 0x88, 0x23, 0x3d, 0xe3, 0x53, 0xeb, - 0xde, 0x95, 0xc6, 0xf9, 0xff, 0x7f, 0xf5, 0xfd, 0x7e, 0xd9, 0xf5, 0xef, - 0xf7, 0xf5, 0x5a, 0x78, 0x93, 0xe3, 0xb8, 0xad, 0x32, 0xe6, 0x25, 0x50, - 0x40, 0xbd, 0x8f, 0xc9, 0x42, 0xa8, 0xa7, 0xc6, 0xeb, 0x0a, 0x4b, 0x58, - 0x22, 0x38, 0x05, 0x42, 0x15, 0x8c, 0x46, 0x48, 0xc3, 0x59, 0xf5, 0x6b, - 0xba, 0xbb, 0xc4, 0xa0, 0x73, 0x80, 0xd1, 0x4c, 0xb9, 0xe5, 0x53, 0x97, - 0x0f, 0x58, 0x47, 0x20, 0x9c, 0x45, 0x32, 0xfa, 0x58, 0xb0, 0x7d, 0x38, - 0xe8, 0xae, 0xd0, 0xeb, 0x97, 0x9e, 0x3b, 0x4b, 0xb6, 0x65, 0x46, 0x0c, - 0x18, 0xa2, 0xe3, 0xa0, 0xd7, 0x9d, 0x26, 0x4f, 0x90, 0xea, 0x17, 0x83, - 0xd7, 0x6c, 0x6a, 0x53, 0x1c, 0xff, 0xf1, 0x50, 0x80, 0x2a, 0x5f, 0xfc, - 0x21, 0x1a, 0xcf, 0xa6, 0x35, 0xcd, 0x80, 0x00, 0xa5, 0xb1, 0xb4, 0x8c, - 0xe4, 0x74, 0x1b, 0x14, 0x4e, 0xfa, 0xe6, 0xb8, 0xe2, 0xfd, 0x6f, 0xa9, - 0xf5, 0xf9, 0xd7, 0x1e, 0xbe, 0x3f, 0x4b, 0xb5, 0x4b, 0xbc, 0x8b, 0x5e, - 0xae, 0xbd, 0x95, 0x61, 0x00, 0xd0, 0xe7, 0xba, 0xd5, 0x6d, 0xdf, 0x55, - 0x89, 0xc6, 0x73, 0x2b, 0x97, 0xc8, 0xf7, 0x6b, 0x52, 0x6b, 0x40, 0x00, - 0x27, 0x32, 0xdc, 0x48, 0xa0, 0x57, 0x9c, 0x57, 0x8b, 0x24, 0xb5, 0x73, - 0xa0, 0x9c, 0xd5, 0xc4, 0x1c, 0x4e, 0x55, 0x16, 0x98, 0x09, 0x05, 0x0d, - 0x71, 0x09, 0x16, 0xec, 0xb4, 0xb0, 0x6d, 0xce, 0x2c, 0xa9, 0x7e, 0x0a, - 0x51, 0xf2, 0xae, 0x25, 0xce, 0x54, 0x53, 0xb1, 0xaf, 0xab, 0x48, 0x58, - 0x38, 0x32, 0x56, 0x5d, 0x00, 0xda, 0x03, 0x12, 0x39, 0x8f, 0xce, 0xb0, - 0x22, 0x52, 0xd8, 0xda, 0x54, 0xc6, 0x02, 0xf4, 0xf7, 0x1e, 0x28, 0x30, - 0xf7, 0x87, 0x43, 0x5c, 0xd1, 0x7b, 0x73, 0xd2, 0xbf, 0x78, 0x40, 0x47, - 0x8a, 0x03, 0xdf, 0xe6, 0x77, 0xe8, 0x52, 0x18, 0x76, 0xee, 0x87, 0x4a, - 0x2a, 0x9f, 0x7c, 0x2b, 0x55, 0x05, 0x43, 0xe1, 0x6d, 0x7e, 0x6b, 0xdc, - 0xa8, 0xa4, 0xe2, 0xbf, 0x9a, 0xf1, 0x6c, 0x68, 0x9a, 0x2b, 0x7a, 0xc7, - 0x9b, 0xd2, 0x97, 0xd1, 0xaf, 0x1c, 0x45, 0xf5, 0x0e, 0xa6, 0x89, 0x9d, - 0x46, 0xc7, 0x29, 0x6b, 0x1a, 0x29, 0xa7, 0xac, 0xed, 0xb3, 0x94, 0xbc, - 0xa5, 0x70, 0x97, 0x29, 0x18, 0x64, 0xc8, 0x76, 0xb8, 0x18, 0xed, 0xa5, - 0xab, 0xea, 0xb3, 0x15, 0xda, 0x18, 0x52, 0xda, 0x1e, 0x9e, 0xf1, 0x2a, - 0x51, 0xc7, 0x24, 0x98, 0x7b, 0x7a, 0xf8, 0xf7, 0x05, 0xf9, 0xaa, 0xad, - 0x56, 0x2f, 0x9b, 0xa3, 0x60, 0x27, 0x0c, 0x6e, 0xa1, 0x04, 0x18, 0xa2, - 0xaa, 0xe3, 0x01, 0x9b, 0x1a, 0xc7, 0x26, 0x35, 0xb3, 0x44, 0xef, 0xc9, - 0x39, 0xf9, 0xf5, 0xb1, 0xf9, 0x3e, 0xde, 0x2b, 0x11, 0x97, 0x20, 0xac, - 0xb3, 0x89, 0xf0, 0xb6, 0xc7, 0x59, 0x22, 0xf2, 0xdb, 0x55, 0xa3, 0x28, - 0x30, 0xe1, 0xe3, 0x35, 0x02, 0x35, 0x36, 0xf3, 0x6a, 0x55, 0x53, 0xa3, - 0x90, 0x83, 0x3c, 0xe7, 0x16, 0xb4, 0xae, 0xdb, 0xf6, 0x30, 0xc6, 0xd7, - 0x9e, 0x86, 0x15, 0x96, 0x7e, 0x8b, 0x85, 0xed, 0x42, 0xf1, 0xa4, 0x86, - 0x55, 0x8b, 0x1c, 0x8a, 0xca, 0x05, 0x38, 0xff, 0xf1, 0x50, 0x80, 0x2c, - 0xbf, 0xfc, 0x21, 0x1a, 0xcc, 0xd0, 0xa1, 0xe1, 0x00, 0xb2, 0xa4, 0xb2, - 0xb3, 0x12, 0xac, 0x57, 0xbe, 0xba, 0x7b, 0x78, 0xf9, 0xfc, 0x75, 0xde, - 0xf9, 0xfc, 0x6f, 0x5f, 0x3c, 0x79, 0xf7, 0xeb, 0xbe, 0x32, 0x1c, 0x4c, - 0xa5, 0xce, 0x0b, 0x95, 0x52, 0x08, 0x57, 0xab, 0xa6, 0x71, 0xa1, 0x48, - 0xa4, 0xa4, 0x9e, 0xae, 0x3e, 0xd0, 0xaa, 0x3e, 0x93, 0xa7, 0x4a, 0xc4, - 0x16, 0x81, 0x0b, 0x6f, 0x20, 0xe8, 0x88, 0xba, 0x0e, 0xb8, 0x38, 0xc4, - 0x50, 0xe0, 0x36, 0x12, 0x6f, 0x3b, 0x17, 0xd1, 0x5d, 0x0e, 0xbc, 0x98, - 0x3f, 0xf6, 0xdd, 0x55, 0x40, 0x78, 0xf7, 0x1e, 0xad, 0x6c, 0x90, 0x3e, - 0x58, 0x9e, 0xc0, 0xcc, 0x4b, 0x66, 0x12, 0xb5, 0xae, 0x56, 0x4d, 0x8c, - 0x59, 0x56, 0x77, 0xee, 0x0a, 0x50, 0xd1, 0xb2, 0x3b, 0x85, 0x63, 0x85, - 0x0d, 0x93, 0xb7, 0xd5, 0x09, 0x2f, 0xe1, 0x87, 0x12, 0x60, 0x2e, 0xc5, - 0xd4, 0xc3, 0xda, 0x6d, 0xed, 0xa2, 0xcd, 0xed, 0x46, 0xa9, 0x73, 0x55, - 0x9a, 0xdb, 0x2a, 0x19, 0x1d, 0xc3, 0x1e, 0x99, 0x15, 0xed, 0x38, 0x90, - 0x66, 0xb5, 0xbe, 0x99, 0x31, 0x87, 0xd5, 0xbe, 0xef, 0xf4, 0x71, 0xa1, - 0xdd, 0x68, 0x1f, 0xe7, 0x87, 0x69, 0xb8, 0x34, 0xac, 0x1b, 0xd8, 0xf0, - 0x8f, 0x8a, 0x06, 0x80, 0xd6, 0xcd, 0x26, 0xa6, 0xd3, 0x5f, 0xeb, 0x49, - 0x55, 0xf5, 0x56, 0xc6, 0x25, 0x8c, 0x2a, 0x49, 0x86, 0xd8, 0x80, 0x7b, - 0xed, 0x4b, 0x1e, 0x25, 0x6c, 0x11, 0xaa, 0xa5, 0x89, 0x85, 0x4f, 0x3b, - 0x24, 0xa2, 0x10, 0xc9, 0xe9, 0xb1, 0x0c, 0xc4, 0x2f, 0xbd, 0x5f, 0x26, - 0x10, 0x98, 0x66, 0xdd, 0x5b, 0x12, 0xdd, 0x0d, 0x38, 0x62, 0xa4, 0x87, - 0x3e, 0x57, 0x45, 0x25, 0xa2, 0x69, 0xef, 0x02, 0x50, 0x3d, 0xbd, 0x0f, - 0x8f, 0x7e, 0xbb, 0x0d, 0x6f, 0xce, 0x6f, 0x7a, 0xa9, 0x46, 0x4a, 0xde, - 0x81, 0x8e, 0x71, 0x68, 0x72, 0x9a, 0xe0, 0x01, 0x41, 0x55, 0xe4, 0x50, - 0x2e, 0x25, 0xf7, 0xbe, 0x3d, 0xf5, 0x97, 0x37, 0x79, 0x95, 0x9e, 0x75, - 0xf8, 0xe6, 0xb5, 0xde, 0x4c, 0x67, 0x1c, 0x7a, 0x42, 0xb8, 0x75, 0xcd, - 0xed, 0x38, 0x51, 0x86, 0x10, 0xc0, 0xcb, 0x86, 0xcf, 0xc7, 0x61, 0x61, - 0x35, 0x37, 0x6c, 0xe2, 0xa2, 0xda, 0xf1, 0xaf, 0x6e, 0x57, 0x1e, 0x18, - 0xa6, 0xb1, 0x61, 0xc4, 0xbc, 0x1a, 0x66, 0xcd, 0x29, 0x9c, 0x62, 0xf6, - 0x4d, 0x2f, 0x5f, 0x0c, 0xfd, 0x04, 0x46, 0xa4, 0xcd, 0xd5, 0x10, 0x18, - 0xc4, 0x15, 0x20, 0xe0, 0xff, 0xf1, 0x50, 0x80, 0x33, 0x3f, 0xfc, 0x21, - 0x1a, 0xcf, 0x50, 0x50, 0xb8, 0x01, 0x04, 0xa3, 0xb1, 0x42, 0xd9, 0xe8, - 0xd6, 0x11, 0xab, 0xef, 0xef, 0xe3, 0xd7, 0xb4, 0x9b, 0xdf, 0xdf, 0xbe, - 0xbe, 0x7a, 0xe3, 0xdf, 0xe3, 0xe7, 0x48, 0xbb, 0xca, 0x4c, 0x92, 0xe0, - 0xde, 0x81, 0x42, 0xe9, 0x16, 0x6b, 0xf4, 0x8a, 0x35, 0x33, 0x9e, 0xf5, - 0x73, 0xa5, 0xdb, 0x0f, 0x6d, 0x63, 0xb8, 0xbf, 0x69, 0x78, 0xcb, 0x85, - 0x87, 0xdc, 0xe3, 0x68, 0x1e, 0x3f, 0x3a, 0xb1, 0x64, 0xe9, 0x46, 0x81, - 0x03, 0x97, 0x82, 0x77, 0x2f, 0x9f, 0xee, 0xba, 0x02, 0xb3, 0x82, 0xb0, - 0x8a, 0x5f, 0xa5, 0x24, 0x55, 0x08, 0x54, 0x1a, 0xaf, 0xd6, 0x12, 0x80, - 0x1d, 0x8d, 0x3a, 0x70, 0xee, 0x9f, 0xb1, 0x82, 0xfa, 0x00, 0xd0, 0x84, - 0xc4, 0x2c, 0xb5, 0x84, 0x54, 0x02, 0xd0, 0x9a, 0x16, 0x7b, 0x58, 0x1e, - 0x0c, 0x27, 0x3a, 0x0c, 0x67, 0xca, 0xb8, 0x78, 0x11, 0x00, 0x1b, 0x5e, - 0xc5, 0x01, 0xa3, 0x50, 0x8a, 0x05, 0x49, 0x9b, 0xe6, 0xbf, 0xed, 0x3e, - 0xe5, 0xa4, 0xec, 0xb2, 0x71, 0x70, 0xf1, 0x65, 0xf4, 0x34, 0x36, 0xf7, - 0xa9, 0x73, 0xac, 0xa3, 0x00, 0xfa, 0x25, 0x2e, 0x1c, 0x75, 0x24, 0xc9, - 0x4b, 0xbd, 0xfd, 0x5e, 0xdd, 0x86, 0xc1, 0x5c, 0xbd, 0xfe, 0x92, 0x87, - 0x27, 0x7b, 0x9f, 0x23, 0xa8, 0x05, 0xe5, 0x15, 0xc3, 0x99, 0x9d, 0x4f, - 0x53, 0xb8, 0x01, 0x21, 0xe7, 0xf8, 0x86, 0xfe, 0xa0, 0x7b, 0xf5, 0xac, - 0xd8, 0xcc, 0x38, 0xb0, 0xd2, 0xdb, 0x7d, 0x0c, 0xfe, 0xb9, 0x2d, 0xed, - 0x6b, 0xf7, 0x6a, 0x1b, 0x0d, 0x6e, 0x83, 0xa6, 0xb4, 0x07, 0xa3, 0xa7, - 0xba, 0xff, 0xf6, 0x7e, 0x3e, 0xb5, 0x7e, 0x1b, 0x5f, 0xda, 0x5e, 0xd7, - 0x06, 0xe2, 0x06, 0x22, 0x70, 0x80, 0x2b, 0x09, 0x24, 0x96, 0xa4, 0xf5, - 0x0a, 0xad, 0xf2, 0x8d, 0x8a, 0xc4, 0x48, 0x4e, 0xb4, 0x76, 0x97, 0x63, - 0x14, 0x85, 0xe8, 0x60, 0xf9, 0x60, 0x26, 0xb6, 0x71, 0xc8, 0x27, 0x1d, - 0x8b, 0xa6, 0x9c, 0x8e, 0xb9, 0x9e, 0x35, 0x5b, 0x9f, 0xee, 0x13, 0x40, - 0xd7, 0x2b, 0xeb, 0x39, 0x81, 0xf5, 0x20, 0x64, 0xf0, 0xac, 0x58, 0x0b, - 0xf9, 0xbf, 0x44, 0xb4, 0xc1, 0xc9, 0x8d, 0x97, 0x86, 0xbc, 0xa3, 0xbd, - 0x3e, 0xc3, 0x6b, 0x0c, 0xc4, 0xf4, 0xf0, 0x21, 0x7b, 0xb7, 0x71, 0xbe, - 0x77, 0x6a, 0xc3, 0xcb, 0x98, 0x6e, 0xc6, 0x71, 0xf1, 0x71, 0x24, 0xca, - 0x54, 0xe1, 0x77, 0x43, 0x9c, 0x8d, 0x0f, 0x22, 0x07, 0x72, 0x01, 0x08, - 0x98, 0x46, 0xbe, 0x84, 0x0d, 0xc5, 0x02, 0x91, 0x56, 0x80, 0xbf, 0x50, - 0x30, 0xb1, 0x7e, 0x2d, 0xf4, 0x86, 0x01, 0xc2, 0xf1, 0x8d, 0x9e, 0x3e, - 0x47, 0x09, 0x0b, 0xd9, 0x77, 0x78, 0xeb, 0x9f, 0x29, 0x26, 0xa7, 0xc9, - 0x36, 0x65, 0x65, 0xde, 0x1b, 0x16, 0x77, 0x9a, 0x6a, 0x5c, 0xf9, 0x45, - 0xbc, 0xeb, 0x79, 0xf2, 0x38, 0xff, 0xf1, 0x50, 0x80, 0x3b, 0x3f, 0xfc, - 0x20, 0xa9, 0x2a, 0xd0, 0x36, 0xaa, 0x14, 0x8a, 0x89, 0x57, 0x9d, 0x73, - 0xd6, 0xf7, 0xfe, 0x77, 0xcf, 0x8a, 0xd1, 0xcf, 0x97, 0x7a, 0xdc, 0x79, - 0xe7, 0x57, 0x59, 0x89, 0x5c, 0x54, 0x92, 0x8a, 0x10, 0x5c, 0x60, 0xb9, - 0x76, 0xba, 0x49, 0x79, 0x67, 0xc3, 0xa2, 0xee, 0x7f, 0xc6, 0x3e, 0x9f, - 0x7b, 0x43, 0x73, 0x00, 0x3b, 0x39, 0x31, 0x3f, 0xa6, 0x55, 0x80, 0xe1, - 0x00, 0x4b, 0x28, 0x9b, 0x9f, 0xb4, 0xe0, 0x42, 0x54, 0x01, 0x01, 0xae, - 0x04, 0x3d, 0x00, 0x66, 0x21, 0x17, 0x22, 0x34, 0x25, 0xb5, 0xc4, 0xc5, - 0x55, 0x8c, 0x22, 0x6b, 0x38, 0x10, 0x10, 0xef, 0x10, 0x36, 0x18, 0x2f, - 0xe9, 0xdf, 0x1e, 0xd7, 0x36, 0xf9, 0x88, 0xa6, 0x09, 0xc4, 0x32, 0xc2, - 0x99, 0x24, 0xd9, 0x01, 0x49, 0xb2, 0x9c, 0x5b, 0x46, 0xef, 0x6b, 0x46, - 0x7a, 0xd9, 0xfa, 0x95, 0xaf, 0x73, 0x27, 0x43, 0x8a, 0x8d, 0x98, 0xfc, - 0x02, 0x64, 0x74, 0x28, 0x74, 0x21, 0x48, 0x7a, 0x9a, 0xf8, 0x8b, 0xf9, - 0xec, 0xd0, 0x0f, 0xd7, 0x0a, 0x02, 0xef, 0x5e, 0xe6, 0xe3, 0x68, 0x16, - 0x2d, 0x41, 0x01, 0xec, 0x9d, 0x88, 0xcc, 0x82, 0xf8, 0x54, 0xa9, 0x72, - 0x0a, 0x50, 0xcd, 0x76, 0xa5, 0xdc, 0x58, 0x48, 0x5f, 0xd2, 0x3d, 0x67, - 0x6c, 0xc5, 0x34, 0x56, 0x52, 0x9a, 0x79, 0xe8, 0x96, 0x1b, 0x46, 0x1a, - 0xd4, 0x13, 0xd5, 0x97, 0xdc, 0x6b, 0x5b, 0x2d, 0x51, 0x86, 0xe7, 0xa6, - 0xa6, 0xd1, 0xd3, 0xa0, 0xab, 0x2d, 0x2f, 0x50, 0x06, 0x78, 0x54, 0x0d, - 0x6a, 0x4a, 0xd9, 0x98, 0x4b, 0xde, 0xb2, 0xb5, 0x52, 0xb3, 0xac, 0xd7, - 0x6d, 0x6f, 0xf1, 0xf2, 0x55, 0x7b, 0x35, 0x7c, 0xfe, 0x3e, 0x7d, 0xba, - 0xcb, 0x55, 0x5e, 0xe0, 0x75, 0x7c, 0x93, 0x73, 0x71, 0x24, 0x81, 0x3a, - 0x70, 0x08, 0x3a, 0x29, 0xa6, 0x6a, 0xa4, 0x01, 0xe8, 0x8e, 0x0a, 0x43, - 0x8f, 0x56, 0x6f, 0xfb, 0x41, 0xc1, 0xb8, 0x71, 0xe5, 0xc8, 0x9d, 0x76, - 0xfc, 0xbc, 0x53, 0xd7, 0x01, 0xbb, 0xdb, 0xc8, 0x4c, 0xdf, 0xce, 0xf7, - 0xd6, 0x54, 0x46, 0xb3, 0x3d, 0x6b, 0x95, 0xf4, 0x12, 0xba, 0x1d, 0x28, - 0xbe, 0x0a, 0xb9, 0xda, 0x14, 0x5a, 0x6b, 0x1c, 0xf9, 0x1d, 0x96, 0x46, - 0x46, 0x52, 0x9a, 0xe0, 0x25, 0x91, 0xad, 0x89, 0xc3, 0x71, 0x35, 0xc2, - 0x1f, 0x6c, 0x2b, 0x31, 0x15, 0x88, 0x5c, 0x97, 0xf1, 0xab, 0x2a, 0xe2, - 0x30, 0x26, 0xc9, 0x25, 0x2f, 0x44, 0x85, 0x1a, 0xde, 0x97, 0x02, 0x06, - 0x06, 0x41, 0x12, 0x37, 0x33, 0x82, 0x02, 0xa4, 0x2f, 0x16, 0x01, 0x87, - 0xbb, 0x49, 0x50, 0xab, 0x09, 0x6e, 0x49, 0xfe, 0x93, 0x26, 0x3b, 0xa6, - 0xaf, 0xd3, 0xcb, 0x42, 0xb9, 0xea, 0x68, 0x9b, 0x37, 0xc9, 0xb6, 0xe5, - 0x53, 0x3b, 0x4c, 0x41, 0xe9, 0xcf, 0x75, 0x95, 0x81, 0xf5, 0x44, 0x0d, - 0x59, 0x68, 0x16, 0xbb, 0xec, 0xf8, 0x67, 0xa8, 0x30, 0x3d, 0x27, 0xdb, - 0xb3, 0x9b, 0xf9, 0xaa, 0xbe, 0x5a, 0x28, 0x39, 0xfa, 0x0c, 0xd7, 0x27, - 0x96, 0x99, 0x94, 0xca, 0xf9, 0x45, 0x64, 0xf4, 0xec, 0xae, 0x4b, 0xde, - 0x49, 0x7d, 0x23, 0x5f, 0x1e, 0xa5, 0x1d, 0x75, 0xd9, 0xf2, 0x07, 0x49, - 0x4a, 0x66, 0xd1, 0xf5, 0x94, 0x33, 0xb2, 0x22, 0x48, 0x42, 0x21, 0xac, - 0xfe, 0x76, 0x92, 0xe0, 0xb1, 0x74, 0x1f, 0x37, 0xb2, 0xde, 0xff, 0xf1, - 0x50, 0x80, 0x38, 0x3f, 0xfc, 0x20, 0xa1, 0x4c, 0xd3, 0x05, 0x08, 0xc0, - 0x5a, 0x41, 0xb7, 0x10, 0xad, 0x51, 0x8f, 0x69, 0x33, 0x3e, 0x00, 0x78, - 0x8e, 0x37, 0xc6, 0xbe, 0xec, 0xf2, 0xff, 0xb1, 0x79, 0xc7, 0x5d, 0xcd, - 0xf9, 0xd7, 0xe4, 0x00, 0x09, 0x20, 0xe2, 0x06, 0x70, 0x71, 0x43, 0xad, - 0xe9, 0xd6, 0x4e, 0x72, 0xb5, 0x6e, 0x1d, 0xd4, 0xdd, 0xcb, 0xa8, 0x85, - 0x0d, 0x06, 0xf5, 0x87, 0x4e, 0xc0, 0x72, 0x9a, 0xe4, 0x74, 0xec, 0x91, - 0x84, 0x19, 0xa2, 0xca, 0xad, 0x5b, 0x98, 0x05, 0x95, 0x7f, 0x06, 0x3f, - 0xef, 0xd5, 0x43, 0x0e, 0xd0, 0x02, 0x2f, 0x87, 0xfb, 0xf8, 0x5e, 0xc2, - 0x2a, 0xeb, 0x4c, 0xd7, 0x25, 0x39, 0x46, 0x73, 0x66, 0x5b, 0xbc, 0xf1, - 0x08, 0xd6, 0x1d, 0x0e, 0x56, 0x9a, 0x3a, 0x5c, 0xeb, 0x15, 0xd0, 0x33, - 0xc5, 0x2f, 0x5e, 0xb6, 0xda, 0xd6, 0x78, 0x63, 0x43, 0xbb, 0x21, 0xd5, - 0x07, 0x17, 0xbd, 0x55, 0x23, 0xfc, 0xae, 0x41, 0xd3, 0x49, 0xbe, 0x81, - 0x32, 0xe7, 0xd7, 0x7e, 0x15, 0xc4, 0xec, 0xee, 0x9e, 0xd7, 0xc5, 0xaa, - 0xd0, 0x1d, 0xed, 0xbe, 0xd3, 0x1e, 0xb0, 0xba, 0x47, 0xa9, 0x8c, 0x1c, - 0x5c, 0xac, 0x2b, 0x98, 0x56, 0xce, 0x42, 0x3a, 0x6f, 0xae, 0x65, 0xbd, - 0x74, 0x41, 0x3f, 0x1f, 0xd1, 0xb1, 0x26, 0x28, 0x21, 0xab, 0x52, 0xce, - 0xfb, 0xfa, 0xe9, 0x3c, 0x2b, 0x25, 0x2c, 0x09, 0x67, 0xc8, 0xd3, 0xe5, - 0xd6, 0x06, 0x7d, 0x23, 0x49, 0x39, 0x9e, 0x4b, 0x95, 0x4a, 0x92, 0x88, - 0x22, 0x05, 0x68, 0xd6, 0x65, 0xa6, 0x98, 0xc0, 0x1d, 0x35, 0xbe, 0xfe, - 0xbe, 0x78, 0xe7, 0xab, 0xf4, 0xe2, 0x5e, 0x67, 0xd2, 0xf9, 0xa8, 0xac, - 0xd6, 0x79, 0x87, 0x1c, 0x03, 0xe9, 0xfd, 0xfd, 0xfb, 0xff, 0x64, 0xce, - 0xb7, 0x86, 0x5c, 0xf3, 0xba, 0x89, 0xe8, 0x09, 0x55, 0xa5, 0x8f, 0x97, - 0x3f, 0xa3, 0xf3, 0xfd, 0xfd, 0x20, 0x00, 0xf3, 0x0d, 0xf9, 0x33, 0x45, - 0xb4, 0xff, 0x7e, 0x21, 0x30, 0x16, 0x22, 0x91, 0x18, 0x8b, 0x11, 0x98, - 0x98, 0x79, 0x14, 0xa8, 0x91, 0xd2, 0x6d, 0x83, 0x91, 0xc6, 0x19, 0x93, - 0xd8, 0xb7, 0xa5, 0x7d, 0x5d, 0x47, 0xce, 0x39, 0x90, 0x05, 0x74, 0x40, - 0x02, 0x05, 0xf0, 0x21, 0x9c, 0xaf, 0xbc, 0xa3, 0x0a, 0x4f, 0x9f, 0xfc, - 0xe0, 0x03, 0xc5, 0xa9, 0x5d, 0x8c, 0xaa, 0x73, 0xed, 0x06, 0x10, 0xd9, - 0x12, 0xd5, 0xc4, 0xf4, 0xee, 0xc3, 0xe1, 0x99, 0x10, 0x82, 0xc8, 0xf7, - 0x04, 0x83, 0xde, 0x3a, 0x85, 0xb9, 0x5f, 0x98, 0x7c, 0x8d, 0xd0, 0xc8, - 0xc8, 0x84, 0x16, 0x47, 0x50, 0xb5, 0x78, 0xdb, 0x1f, 0x50, 0x38, 0x1f, - 0x8c, 0x52, 0x45, 0x2b, 0x17, 0x52, 0x4b, 0x6d, 0xa9, 0x2b, 0x91, 0x08, - 0xdf, 0x4b, 0xcf, 0x60, 0xc1, 0xdc, 0x82, 0x00, 0x6a, 0xf0, 0xae, 0xcb, - 0x73, 0x4a, 0x54, 0x0d, 0xd4, 0x2d, 0xd3, 0x4b, 0x73, 0x4b, 0x75, 0xef, - 0x06, 0x48, 0xa4, 0x75, 0x41, 0xc9, 0x3d, 0x92, 0x4e, 0xb7, 0x35, 0x84, - 0x00, 0x40, 0x12, 0x41, 0xd4, 0x56, 0x61, 0x03, 0x6c, 0xd2, 0xdb, 0xc7, - 0x3e, 0xfe, 0x37, 0xfe, 0xc9, 0x3c, 0xff, 0xef, 0x54, 0xb6, 0xa4, 0xbc, - 0xb7, 0x30, 0xf0, 0xff, 0xf1, 0x50, 0x80, 0x39, 0x5f, 0xfc, 0x20, 0xaa, - 0x7a, 0xd6, 0x38, 0x4a, 0x6d, 0x88, 0x00, 0x07, 0x5b, 0xce, 0xb8, 0xf5, - 0x8f, 0xae, 0x7b, 0xfd, 0x6b, 0x55, 0xeb, 0x57, 0x92, 0xe2, 0xb8, 0xba, - 0x9d, 0x07, 0x5c, 0xfe, 0x18, 0xfb, 0x48, 0xfd, 0x67, 0x5d, 0x71, 0x63, - 0x77, 0x77, 0x36, 0x49, 0x23, 0xa4, 0xbb, 0x28, 0x24, 0x18, 0x4e, 0x5c, - 0x0a, 0x4e, 0x4e, 0x64, 0x36, 0x6c, 0x55, 0x66, 0xd7, 0x4a, 0x4b, 0x9a, - 0x27, 0xa6, 0x42, 0x8f, 0x88, 0x93, 0xc1, 0xaa, 0xf6, 0xea, 0x2c, 0x3f, - 0x07, 0xea, 0x76, 0x6b, 0x12, 0xdc, 0x2d, 0x38, 0xf0, 0xe3, 0x1e, 0x50, - 0xb4, 0xeb, 0x78, 0x38, 0x3a, 0x5a, 0x34, 0x51, 0x94, 0x9a, 0x15, 0xdb, - 0x14, 0x8b, 0xf7, 0xbc, 0xd0, 0xe8, 0x0a, 0x20, 0xb8, 0xa8, 0x3e, 0xb5, - 0xa1, 0xf8, 0x23, 0xa6, 0x9d, 0xe8, 0xb9, 0x4b, 0xd6, 0xca, 0x4e, 0x2a, - 0x43, 0x3a, 0x9a, 0x79, 0x48, 0x46, 0x51, 0x80, 0x6a, 0x84, 0xac, 0x89, - 0x8a, 0x80, 0xe2, 0xca, 0xd1, 0x60, 0xb6, 0x88, 0xfa, 0x35, 0xb5, 0x52, - 0xc6, 0x93, 0xe7, 0x34, 0x09, 0x0a, 0xc5, 0x29, 0x0d, 0x34, 0xa4, 0xad, - 0x23, 0x61, 0x45, 0x03, 0x76, 0x2c, 0xaf, 0x4d, 0x43, 0xd2, 0xe7, 0xbd, - 0x29, 0x5e, 0x33, 0x0c, 0xcc, 0x60, 0xbd, 0xb5, 0xba, 0x7f, 0x32, 0xfa, - 0x2a, 0x2e, 0x79, 0x2e, 0x10, 0xb2, 0xcc, 0x98, 0x32, 0x39, 0xeb, 0x68, - 0x69, 0xa0, 0x60, 0xc4, 0x66, 0xca, 0xcc, 0x52, 0x44, 0x50, 0x43, 0x27, - 0x5b, 0x6f, 0x69, 0xf0, 0xbd, 0xdc, 0xe8, 0x1b, 0x26, 0xcb, 0x77, 0x77, - 0x69, 0xd5, 0x66, 0xca, 0x5c, 0x74, 0xf1, 0x34, 0xa8, 0x1a, 0xd6, 0x3a, - 0x4b, 0x29, 0x22, 0xc3, 0x80, 0x80, 0x01, 0x78, 0x00, 0x71, 0xbb, 0xdf, - 0x55, 0x7c, 0xf5, 0xbc, 0xb9, 0xf7, 0xcd, 0x7b, 0x08, 0xbd, 0x46, 0x5d, - 0x97, 0xf3, 0x35, 0xd8, 0xff, 0x63, 0x41, 0x9e, 0xe1, 0xe4, 0x6d, 0x89, - 0xaf, 0xf6, 0x46, 0xef, 0x51, 0x34, 0x32, 0x11, 0x31, 0x1a, 0x18, 0x76, - 0x35, 0x82, 0x52, 0xfa, 0x64, 0x8e, 0xa9, 0xd5, 0x0e, 0xf0, 0xa0, 0x8a, - 0x48, 0x16, 0x86, 0xb3, 0x28, 0x22, 0xa1, 0xd8, 0x60, 0x81, 0x1d, 0xcc, - 0x27, 0x20, 0x1c, 0x7c, 0xdd, 0x04, 0x92, 0x12, 0x71, 0xc2, 0xd1, 0xe7, - 0x25, 0xb6, 0xce, 0x12, 0x3e, 0x87, 0xf1, 0x00, 0x13, 0x30, 0xe0, 0x19, - 0x19, 0x92, 0xa1, 0xa0, 0xe6, 0xad, 0x96, 0xf5, 0xac, 0xd4, 0xc2, 0x6b, - 0x30, 0x4f, 0x5d, 0x0d, 0x8f, 0x63, 0x18, 0x3a, 0xb2, 0x6d, 0x64, 0xad, - 0xc8, 0x33, 0xce, 0x1e, 0x52, 0xf6, 0x4f, 0x85, 0x13, 0x49, 0x71, 0x77, - 0x52, 0xd6, 0x15, 0x52, 0xcc, 0x25, 0x18, 0x55, 0x4c, 0xce, 0x4a, 0x15, - 0x3b, 0xe3, 0xa5, 0xac, 0x82, 0xbe, 0x7a, 0xa5, 0x91, 0x94, 0xc0, 0xb3, - 0xc5, 0x56, 0xe5, 0x71, 0xdd, 0xa7, 0xb6, 0xa8, 0x21, 0x52, 0x47, 0xa2, - 0x95, 0x6e, 0xef, 0x92, 0x92, 0xe9, 0x2a, 0xd9, 0x56, 0x9b, 0x07, 0x48, - 0x3c, 0x70, 0x91, 0xa3, 0x73, 0xac, 0x8e, 0x28, 0xf5, 0xde, 0x83, 0xd2, - 0x1c, 0x68, 0x77, 0x8a, 0x8e, 0xea, 0x12, 0x59, 0x9b, 0x7b, 0x4c, 0x4d, - 0xa2, 0xa4, 0x2c, 0x5a, 0xcc, 0x05, 0x34, 0x09, 0xbe, 0x38, 0xcc, 0x12, - 0x28, 0xf3, 0x59, 0x47, 0x80, 0xff, 0xf1, 0x50, 0x80, 0x22, 0x5f, 0xfc, - 0x21, 0x1a, 0xcf, 0x7f, 0xfc, 0xfc, 0x02, 0x00, 0xa8, 0xb1, 0xc2, 0x55, - 0x46, 0x62, 0x4b, 0x0d, 0x06, 0x2a, 0x00, 0x06, 0xa7, 0x26, 0xb6, 0xfb, - 0xf7, 0xe6, 0xe7, 0x2a, 0xd5, 0xe3, 0xdb, 0x54, 0xaa, 0x14, 0xaf, 0xde, - 0x36, 0x96, 0xce, 0x80, 0xd0, 0x0d, 0xc0, 0xd5, 0x63, 0xff, 0x75, 0x89, - 0xde, 0xb5, 0x4d, 0xe8, 0x11, 0x38, 0x9f, 0x72, 0x23, 0x6a, 0x45, 0xad, - 0x0c, 0xc7, 0x18, 0xbe, 0xe8, 0x5e, 0xbe, 0xe5, 0xf0, 0x45, 0x62, 0x92, - 0xd5, 0x89, 0x84, 0x85, 0xd4, 0x59, 0x38, 0xf5, 0xf8, 0x95, 0x69, 0x25, - 0x18, 0x28, 0xa9, 0xec, 0x87, 0x6b, 0xd9, 0x99, 0xb3, 0x5a, 0x41, 0xd5, - 0x72, 0x22, 0x66, 0xe3, 0x4e, 0x6f, 0x79, 0xac, 0xc5, 0xad, 0xea, 0xf6, - 0x6b, 0xee, 0xaf, 0x1f, 0x3c, 0xe8, 0xea, 0x5a, 0xc0, 0x0f, 0x9f, 0xa4, - 0x42, 0x85, 0x07, 0x04, 0x27, 0x09, 0x70, 0x09, 0x32, 0x79, 0x1c, 0x25, - 0x89, 0x7f, 0x3f, 0xbf, 0xc6, 0x6d, 0x0c, 0x3b, 0x13, 0x9f, 0x43, 0xc5, - 0x09, 0x38, 0xab, 0xa2, 0x3b, 0x66, 0x01, 0x17, 0xaf, 0x01, 0x33, 0x64, - 0x22, 0x21, 0x04, 0xa5, 0xd2, 0xcc, 0x4d, 0xa2, 0xb7, 0xa9, 0x18, 0x19, - 0x1d, 0x62, 0x58, 0x69, 0x0a, 0x92, 0x30, 0x91, 0xd8, 0x83, 0x94, 0xfd, - 0xaa, 0xaf, 0xeb, 0x5e, 0x31, 0xf1, 0x4d, 0x90, 0xe9, 0xc1, 0x93, 0xfc, - 0x2b, 0x71, 0x38, 0xae, 0x4a, 0x4c, 0x4d, 0xc0, 0xa8, 0xaa, 0x20, 0x88, - 0xf1, 0x3f, 0xc4, 0x00, 0x00, 0x0d, 0x6d, 0x77, 0x72, 0x73, 0x59, 0x26, - 0xab, 0x38, 0xda, 0xf9, 0x0b, 0x62, 0xda, 0xb5, 0x12, 0xa5, 0x55, 0x0b, - 0xb9, 0xec, 0x04, 0xb6, 0x8b, 0x0b, 0x7e, 0x82, 0xfd, 0x38, 0xba, 0x32, - 0x33, 0x5b, 0x0a, 0x75, 0xb8, 0xee, 0x14, 0xfe, 0xd7, 0x68, 0x8a, 0xaf, - 0xeb, 0xbf, 0xae, 0x3e, 0x84, 0x57, 0x44, 0x8d, 0x4d, 0x57, 0x4c, 0xfa, - 0x60, 0x5b, 0x80, 0xff, 0xf1, 0x50, 0x80, 0x2a, 0xbf, 0xfc, 0x21, 0x1a, - 0xcf, 0xfd, 0xc4, 0x7f, 0x00, 0x00, 0xa2, 0xb4, 0x4b, 0x8c, 0xc8, 0x96, - 0x30, 0x03, 0x7a, 0x36, 0x6a, 0xb1, 0xa0, 0xf6, 0xf5, 0xe7, 0xa2, 0xe9, - 0x7a, 0xe7, 0xab, 0xc4, 0x06, 0x02, 0x47, 0x87, 0x2c, 0x03, 0x68, 0x5a, - 0x0c, 0x23, 0x19, 0x3d, 0x80, 0xd7, 0xfd, 0x64, 0x1d, 0xb6, 0x99, 0xc5, - 0x99, 0xa3, 0xcb, 0xd2, 0xfd, 0xfe, 0x5d, 0x6d, 0xf4, 0xd7, 0xeb, 0x3c, - 0xad, 0x9e, 0x2e, 0x63, 0xd5, 0xbb, 0x8e, 0xf5, 0x4a, 0x34, 0x17, 0x4a, - 0xc7, 0x91, 0x9b, 0x04, 0xc1, 0x61, 0x3b, 0xec, 0x46, 0xcb, 0xaa, 0xd7, - 0xfd, 0xdc, 0x1b, 0xb1, 0xd4, 0xcd, 0x3e, 0x21, 0xab, 0xb1, 0xec, 0xb8, - 0x9b, 0xac, 0xe8, 0xce, 0x68, 0xa9, 0x70, 0x47, 0xed, 0x79, 0x6a, 0x24, - 0xef, 0x82, 0x4d, 0x14, 0x5c, 0x3f, 0xb3, 0xba, 0x42, 0xb3, 0x3a, 0x17, - 0x0a, 0xe7, 0x56, 0x37, 0x8d, 0x2b, 0x7e, 0xa7, 0x6f, 0x32, 0x04, 0xe3, - 0xab, 0x3b, 0xb8, 0xe1, 0x4e, 0xbd, 0x73, 0xdc, 0x27, 0xb9, 0x1a, 0xa6, - 0xa7, 0x4f, 0x47, 0xda, 0x64, 0xcd, 0x54, 0x2c, 0xfd, 0x97, 0xf4, 0x22, - 0x50, 0x93, 0x52, 0xbb, 0xa0, 0xb1, 0xff, 0xa1, 0xf9, 0x08, 0xce, 0x6d, - 0xb5, 0x6a, 0xbf, 0x50, 0x63, 0x5c, 0x87, 0x1b, 0x66, 0x67, 0x5e, 0x92, - 0xd4, 0x63, 0x30, 0x84, 0x4f, 0x15, 0xa2, 0x11, 0x16, 0xc1, 0x98, 0x71, - 0xbe, 0x3b, 0x68, 0x88, 0x45, 0x0a, 0x56, 0x8a, 0xd6, 0x0d, 0xc6, 0xa9, - 0x6c, 0xa6, 0xea, 0x9d, 0xd6, 0xa2, 0x85, 0x94, 0x6d, 0xcd, 0x4e, 0x9c, - 0xe3, 0xc2, 0xfb, 0x10, 0x2e, 0x54, 0xaa, 0x87, 0x57, 0xa1, 0x70, 0xbc, - 0xaf, 0xba, 0x8b, 0x9a, 0x5a, 0xf7, 0x83, 0x48, 0x21, 0x45, 0x0d, 0xa1, - 0xb1, 0x94, 0xbf, 0x00, 0x00, 0x00, 0x07, 0xb7, 0xae, 0x14, 0x55, 0x49, - 0x95, 0xae, 0x6d, 0xb0, 0xbc, 0x69, 0xbd, 0xd9, 0x53, 0x06, 0x42, 0x62, - 0x8b, 0x03, 0xa9, 0x93, 0x85, 0xc6, 0x68, 0xd1, 0xcc, 0x08, 0x16, 0xb7, - 0xfc, 0xf7, 0x43, 0x41, 0x68, 0xce, 0x8c, 0x99, 0xcb, 0x8c, 0xf6, 0x20, - 0xad, 0x41, 0xac, 0xb2, 0x96, 0x2a, 0x51, 0x6f, 0x5d, 0xba, 0x7e, 0x90, - 0x07, 0x38, 0x18, 0xd7, 0x48, 0x77, 0x34, 0xa8, 0xdb, 0x76, 0x21, 0xd0, - 0xa4, 0xa2, 0xde, 0xae, 0x40, 0x69, 0x59, 0x9b, 0xb7, 0xbc, 0xe4, 0xac, - 0x17, 0xa6, 0x1b, 0x2f, 0x05, 0x36, 0x08, 0x38, 0xff, 0xf1, 0x50, 0x80, - 0x29, 0x1f, 0xfc, 0x21, 0x1a, 0xcc, 0xfc, 0xf8, 0xe7, 0x80, 0x00, 0xa7, - 0xb1, 0xc1, 0x96, 0x68, 0x66, 0x48, 0x03, 0xae, 0xc6, 0xb6, 0xfc, 0x78, - 0xec, 0xfc, 0x77, 0xe3, 0xf9, 0xc9, 0xc6, 0x56, 0xb7, 0x35, 0xc7, 0x33, - 0x2c, 0xd0, 0x2f, 0xe8, 0xfa, 0xe3, 0x90, 0x4a, 0x43, 0xee, 0xbc, 0x04, - 0xf2, 0x65, 0xe4, 0x21, 0x1e, 0x34, 0x4a, 0xb9, 0x2c, 0x66, 0xf4, 0xed, - 0x8f, 0x46, 0xef, 0xde, 0x9f, 0x2a, 0xbb, 0x9a, 0xde, 0x7a, 0x97, 0x28, - 0xb8, 0x16, 0xec, 0xe9, 0xe7, 0x6c, 0x2a, 0xd2, 0xb3, 0x22, 0x3e, 0xbf, - 0x08, 0x2d, 0x0b, 0xef, 0xe3, 0x11, 0x80, 0x44, 0x4d, 0xe6, 0x6c, 0x00, - 0x2e, 0x76, 0xb3, 0x5b, 0xba, 0x5c, 0xd3, 0x05, 0x2f, 0x0e, 0xe1, 0x2a, - 0x03, 0x16, 0x50, 0xe2, 0x85, 0x3e, 0x45, 0xcc, 0xeb, 0x16, 0xc8, 0x6f, - 0xfa, 0xe0, 0x7a, 0x6c, 0x83, 0x57, 0x0d, 0x49, 0x8c, 0x11, 0xea, 0x9f, - 0xb4, 0xe9, 0x6f, 0xd1, 0x19, 0x55, 0xbb, 0x97, 0x87, 0x51, 0x07, 0x0f, - 0x55, 0x61, 0xf4, 0xed, 0x0f, 0xd7, 0xe0, 0x2d, 0x7a, 0x87, 0x02, 0xb1, - 0x5c, 0x56, 0x28, 0x95, 0xc9, 0x58, 0x2d, 0x85, 0x39, 0xa0, 0xe6, 0xfc, - 0x33, 0xac, 0x68, 0xa1, 0xe7, 0x0b, 0xdd, 0x62, 0x47, 0x9e, 0x08, 0x82, - 0x5b, 0x2f, 0x48, 0x58, 0x9e, 0x03, 0x16, 0x55, 0x4b, 0x9c, 0xef, 0x23, - 0x1b, 0x82, 0xc0, 0x73, 0xba, 0x04, 0xe4, 0x50, 0x40, 0xb0, 0x6c, 0xb9, - 0x94, 0x6f, 0x50, 0x8a, 0x45, 0xae, 0x13, 0x71, 0x05, 0xc4, 0xa1, 0x24, - 0x62, 0xc9, 0x46, 0xd4, 0x16, 0x18, 0x61, 0xb2, 0x59, 0x4d, 0xe5, 0x34, - 0xb5, 0x25, 0x0a, 0x1e, 0x44, 0x4b, 0xae, 0x3a, 0x2a, 0xb7, 0xa8, 0xd4, - 0x29, 0xec, 0x6b, 0x9f, 0x78, 0x00, 0x1a, 0xd8, 0x1a, 0xca, 0xfe, 0x72, - 0x5e, 0xea, 0xf0, 0xe3, 0x73, 0x72, 0x48, 0x01, 0x9f, 0x14, 0x29, 0x49, - 0xc4, 0xef, 0x3d, 0x27, 0x25, 0xf2, 0xe8, 0xee, 0x67, 0xa5, 0xf4, 0x5f, - 0x2d, 0x0f, 0xb0, 0xa9, 0x6e, 0x2c, 0xd9, 0x29, 0xf8, 0x6b, 0x35, 0x9a, - 0xc7, 0x52, 0xbe, 0x10, 0x54, 0xb6, 0xa2, 0x90, 0x9e, 0x22, 0x01, 0x18, - 0x82, 0x83, 0x25, 0xcd, 0xd9, 0x80, 0x42, 0xbe, 0x31, 0x95, 0x54, 0xee, - 0xe2, 0x66, 0x3a, 0x8a, 0xab, 0xa5, 0xb4, 0xbe, 0x19, 0x49, 0x41, 0xc0, - 0xff, 0xf1, 0x50, 0x80, 0x3e, 0x7f, 0xfc, 0x20, 0xac, 0x1a, 0xd4, 0x96, - 0x52, 0x99, 0x88, 0x85, 0x64, 0xa9, 0xed, 0xbb, 0x55, 0xfd, 0xfe, 0x73, - 0x56, 0x57, 0x97, 0x7b, 0xbf, 0xaf, 0x15, 0xe7, 0x89, 0xbc, 0xac, 0xd3, - 0x53, 0x17, 0xce, 0xa6, 0x00, 0x27, 0x6a, 0xcb, 0x66, 0x73, 0x7b, 0xd4, - 0xa9, 0x04, 0xd3, 0xa8, 0xc3, 0xbd, 0x80, 0xf8, 0x85, 0x57, 0x5b, 0xaa, - 0x75, 0x5a, 0x6d, 0x7f, 0xe4, 0x83, 0xba, 0x0f, 0x00, 0x32, 0x19, 0x8a, - 0xed, 0x21, 0x0d, 0x6f, 0xac, 0x2f, 0x3c, 0xeb, 0x12, 0x94, 0x67, 0x81, - 0xa6, 0xbe, 0x5c, 0x8a, 0xc5, 0xca, 0x7f, 0x67, 0xb1, 0x13, 0xce, 0x22, - 0xf3, 0xdf, 0xf1, 0xf5, 0xdc, 0x83, 0xaa, 0xc5, 0xa9, 0xf0, 0x29, 0x77, - 0x0b, 0x94, 0x44, 0x44, 0x0a, 0x20, 0x0d, 0x0a, 0x9e, 0x48, 0x7b, 0x71, - 0x3b, 0xc5, 0x04, 0x2c, 0x9f, 0x94, 0xed, 0x53, 0x95, 0x39, 0x95, 0x28, - 0xab, 0x78, 0x8e, 0x52, 0x89, 0xd7, 0x16, 0xce, 0x3e, 0x90, 0x24, 0x80, - 0xeb, 0xaf, 0xce, 0x7c, 0x47, 0x1d, 0x6f, 0x1e, 0x6f, 0xa3, 0x10, 0xa9, - 0x0d, 0x58, 0x98, 0xdc, 0x2f, 0x2b, 0xbb, 0x96, 0x0d, 0x5e, 0xc7, 0xae, - 0xa1, 0x88, 0xe0, 0x5e, 0x99, 0x5f, 0xda, 0x39, 0xcd, 0x2a, 0xe8, 0xa2, - 0xf7, 0x3b, 0x15, 0x48, 0xd2, 0x36, 0x28, 0x3c, 0xdb, 0x16, 0x09, 0xb4, - 0x90, 0xc8, 0x86, 0xc3, 0x89, 0x2c, 0x15, 0xc8, 0xeb, 0x9c, 0x42, 0xaa, - 0x00, 0x2d, 0x32, 0x12, 0x58, 0x2f, 0x75, 0x47, 0x08, 0x4a, 0x12, 0xa9, - 0x5c, 0xa6, 0x2e, 0xb5, 0xbd, 0xf6, 0xce, 0x59, 0x32, 0x9d, 0xc8, 0x42, - 0x05, 0x0a, 0x15, 0x18, 0x16, 0x63, 0x38, 0x89, 0x04, 0x3b, 0xcb, 0x3f, - 0x11, 0x05, 0x30, 0xb4, 0x57, 0x2a, 0x6b, 0x28, 0xa2, 0x27, 0x8b, 0xc1, - 0x6d, 0x07, 0xc8, 0x98, 0x55, 0x6a, 0xcd, 0x50, 0x82, 0xa4, 0xab, 0x59, - 0x19, 0xaa, 0xb6, 0x32, 0x24, 0x06, 0xaa, 0xf7, 0x5c, 0x2a, 0xe8, 0x9b, - 0x79, 0x67, 0xb5, 0xb5, 0xc3, 0x35, 0xbc, 0x99, 0xac, 0x6e, 0xe8, 0x34, - 0x59, 0x39, 0xca, 0x21, 0x1c, 0x78, 0xfb, 0xec, 0xdf, 0xea, 0x6e, 0xeb, - 0xb6, 0x7b, 0xf0, 0xb1, 0xbb, 0x64, 0xb7, 0x36, 0xc8, 0x8c, 0xc3, 0x82, - 0xac, 0x88, 0x96, 0xc5, 0x20, 0x18, 0xab, 0xaa, 0xc9, 0x23, 0xd4, 0xd9, - 0x5f, 0x59, 0xc1, 0x64, 0x33, 0x17, 0x85, 0x4f, 0x36, 0xfe, 0x3e, 0xc8, - 0x5d, 0xa5, 0x3d, 0x95, 0x77, 0x20, 0xaa, 0x8e, 0x73, 0x5b, 0x8b, 0xed, - 0x05, 0xb9, 0xee, 0x36, 0x75, 0x8a, 0x4d, 0x6b, 0xa3, 0x71, 0x59, 0xbc, - 0x5d, 0x6a, 0xbe, 0xac, 0x5a, 0xa9, 0xb0, 0x17, 0x13, 0x01, 0xbf, 0x16, - 0x61, 0xc5, 0xa7, 0x3c, 0x15, 0xe8, 0x92, 0x71, 0xdd, 0x7e, 0x10, 0xaa, - 0x96, 0xd3, 0x78, 0x2b, 0x2e, 0x6a, 0x33, 0xa4, 0xa1, 0x60, 0xf4, 0x77, - 0x92, 0xd3, 0x9c, 0x1d, 0xe7, 0x48, 0x9d, 0x6e, 0x8a, 0x16, 0xf5, 0x0c, - 0x8b, 0x83, 0x59, 0x28, 0xb8, 0x3f, 0x1b, 0x20, 0xd0, 0x01, 0xb0, 0xb9, - 0xdc, 0x4e, 0x9e, 0xc5, 0x0b, 0x9b, 0x8d, 0xf0, 0x8c, 0x6f, 0x79, 0x79, - 0xfc, 0xa2, 0xd9, 0x2f, 0xff, 0xf9, 0xd8, 0x26, 0xbf, 0x40, 0xde, 0xd0, - 0x01, 0x7f, 0x8b, 0xdb, 0x52, 0xff, 0xbd, 0xc7, 0xd3, 0x6c, 0x5a, 0x98, - 0x97, 0x36, 0x13, 0xf9, 0x74, 0xe7, 0xb4, 0x67, 0x1a, 0x80, 0x1b, 0xe6, - 0x9a, 0xfd, 0x53, 0x69, 0x02, 0x23, 0x93, 0x63, 0x28, 0x9c, 0xb1, 0xb3, - 0xf3, 0x7a, 0x3c, 0x8d, 0xa5, 0xb8, 0x71, 0xdb, 0x1f, 0x21, 0x2a, 0xde, - 0x16, 0xf2, 0xf3, 0x86, 0x13, 0x86, 0xf0, 0xff, 0xf1, 0x50, 0x80, 0x3a, - 0x1f, 0xfc, 0x20, 0xb1, 0x1a, 0xd0, 0xa4, 0x83, 0x05, 0x14, 0xc6, 0x5e, - 0x79, 0x56, 0xb3, 0x5f, 0xaf, 0xcf, 0x8d, 0x38, 0xe5, 0xed, 0x5b, 0xdc, - 0xf6, 0xef, 0xaa, 0xf6, 0xcc, 0xcb, 0xac, 0xdc, 0xe9, 0x2b, 0x8d, 0xb4, - 0xd3, 0x0c, 0xaf, 0xb9, 0x26, 0x1b, 0x03, 0x80, 0x0a, 0xe4, 0x44, 0x48, - 0x3d, 0x29, 0xe0, 0x9e, 0x0e, 0x35, 0x94, 0x49, 0x45, 0xa8, 0x76, 0x49, - 0x53, 0xda, 0x19, 0xfd, 0x35, 0x3a, 0xa2, 0xa3, 0x49, 0xa0, 0xf4, 0xe2, - 0xa0, 0x60, 0x21, 0x1e, 0xff, 0xc6, 0x07, 0xa4, 0xf6, 0xc0, 0x3c, 0x43, - 0x62, 0x95, 0x1a, 0x45, 0xf6, 0xbc, 0x53, 0xcf, 0x99, 0xf9, 0x46, 0x31, - 0xe1, 0x1d, 0x68, 0xd7, 0xc9, 0xea, 0x23, 0x48, 0x0f, 0x70, 0x3f, 0xa3, - 0x8c, 0xea, 0xf1, 0x2b, 0x89, 0x21, 0xed, 0x3e, 0x15, 0x1f, 0x9f, 0xfb, - 0x6d, 0xec, 0xf9, 0x64, 0x74, 0xbc, 0xe3, 0x1b, 0xbd, 0x42, 0xd4, 0x69, - 0x77, 0x0e, 0x1f, 0x67, 0x20, 0x3f, 0x1e, 0xaf, 0xa4, 0x77, 0x22, 0x21, - 0xb6, 0x84, 0x71, 0xf0, 0x8a, 0xa1, 0x86, 0x1b, 0x51, 0x90, 0x39, 0xab, - 0x80, 0x87, 0x51, 0x11, 0x8b, 0xd9, 0x6c, 0x03, 0xc4, 0x57, 0xbb, 0x49, - 0x1e, 0x19, 0xaa, 0x90, 0xbd, 0x18, 0x0e, 0x23, 0xa7, 0x17, 0xcb, 0x08, - 0xdd, 0xdb, 0x84, 0x41, 0x00, 0xcb, 0x8e, 0x37, 0x53, 0x22, 0x67, 0x72, - 0x1a, 0x96, 0x56, 0x3c, 0xf6, 0x74, 0xba, 0x65, 0x13, 0x97, 0x26, 0x18, - 0x00, 0x02, 0xa0, 0x08, 0xc2, 0x7c, 0xf7, 0xd3, 0xd2, 0x01, 0x69, 0x93, - 0xaa, 0xf9, 0x50, 0x18, 0xc5, 0x67, 0x0b, 0x2a, 0xc9, 0xb3, 0x5b, 0x98, - 0x5e, 0x7b, 0xc6, 0x7b, 0x1d, 0x15, 0x6e, 0xa7, 0x49, 0x91, 0x1c, 0x85, - 0x74, 0x40, 0xab, 0x4e, 0xbb, 0xd2, 0x75, 0x33, 0x4d, 0x96, 0x82, 0x82, - 0x09, 0xb4, 0xcd, 0x23, 0x28, 0x36, 0x9a, 0xd4, 0xb3, 0x65, 0xa0, 0xf0, - 0x00, 0x09, 0xed, 0xe7, 0x9b, 0xbf, 0x3f, 0xf0, 0x5b, 0x3c, 0xfd, 0xf9, - 0xf6, 0xdf, 0x9f, 0xfb, 0x00, 0x01, 0x5b, 0x0e, 0x25, 0x2f, 0x8d, 0xc1, - 0x44, 0x89, 0xbf, 0xe5, 0x9b, 0xf5, 0xf2, 0xc7, 0x0a, 0xd3, 0x30, 0x52, - 0xb4, 0x2c, 0x86, 0x1d, 0x69, 0x4c, 0x7e, 0x82, 0xb1, 0x89, 0x30, 0xaa, - 0x00, 0x5e, 0x80, 0x80, 0x05, 0x65, 0x93, 0x20, 0xf1, 0x22, 0x8f, 0x8d, - 0x0d, 0x36, 0xac, 0xe6, 0x1a, 0xe3, 0x98, 0xac, 0xaa, 0x91, 0x9c, 0x9e, - 0x79, 0x57, 0xbc, 0x19, 0xf2, 0xfd, 0xf8, 0x09, 0x30, 0x12, 0xa3, 0xa9, - 0x6b, 0xbd, 0x64, 0xab, 0x67, 0x7a, 0x17, 0x39, 0x5d, 0xb1, 0xec, 0xe5, - 0x24, 0x99, 0xec, 0xc3, 0x45, 0x4b, 0x2b, 0xfc, 0xc5, 0x27, 0x16, 0x0b, - 0x1d, 0x9e, 0xb7, 0xe5, 0xf9, 0x25, 0xee, 0x99, 0xf0, 0x6a, 0xba, 0xfc, - 0x9e, 0x89, 0xf2, 0xc9, 0xaa, 0x31, 0x83, 0x13, 0x52, 0xe7, 0xb4, 0x9c, - 0xae, 0x42, 0x0e, 0xd7, 0x20, 0x65, 0x22, 0x8f, 0xfa, 0xfd, 0xeb, 0xe6, - 0x71, 0x7c, 0xa3, 0x0f, 0x1d, 0x9c, 0x37, 0xac, 0x54, 0xf4, 0xaf, 0xd4, - 0x5d, 0x61, 0xc4, 0xb3, 0x86, 0xe7, 0xda, 0x8c, 0x7c, 0x4a, 0xb4, 0x24, - 0x34, 0x89, 0x08, 0x8b, 0xaf, 0xc6, 0x0a, 0xaa, 0xe9, 0x1b, 0x28, 0x47, - 0x1d, 0x51, 0x55, 0xbc, 0xc4, 0x05, 0x6c, 0x00, 0x48, 0x2b, 0x18, 0x9f, - 0x1e, 0xe0, 0x70, 0xff, 0xf1, 0x50, 0x80, 0x37, 0xff, 0xfc, 0x20, 0xb1, - 0x1a, 0xd0, 0xa5, 0x3a, 0x69, 0x2b, 0xda, 0xa2, 0xe7, 0xe3, 0xed, 0x93, - 0x8f, 0x3f, 0x6a, 0xfa, 0xde, 0x6b, 0xbf, 0x3e, 0x3e, 0x26, 0x9c, 0xb2, - 0x89, 0xc6, 0xe5, 0x4b, 0xdc, 0x07, 0xfc, 0xdd, 0x0a, 0xcb, 0x36, 0xa5, - 0x93, 0x35, 0x3a, 0x0b, 0x3e, 0xf3, 0x8a, 0x39, 0x54, 0x87, 0x4e, 0x75, - 0x08, 0xf9, 0x9d, 0x57, 0x3c, 0x22, 0x7e, 0x6f, 0x9e, 0x31, 0xca, 0x4f, - 0x7f, 0x25, 0x2f, 0xd6, 0xcf, 0x78, 0xc8, 0x81, 0x40, 0x56, 0x94, 0x1a, - 0x5e, 0x2a, 0xab, 0x58, 0x51, 0xcf, 0xc5, 0x5b, 0xe3, 0x5d, 0xef, 0xdd, - 0xe2, 0xdb, 0xb9, 0x72, 0x70, 0xb6, 0x02, 0xbf, 0x88, 0x93, 0x7a, 0x1f, - 0x7b, 0xab, 0xe6, 0x5d, 0x57, 0xe5, 0x35, 0x10, 0x18, 0xad, 0x17, 0xc3, - 0x78, 0xa3, 0x74, 0xa8, 0x77, 0x8d, 0xe4, 0xe2, 0xd4, 0xc4, 0x97, 0x72, - 0xd3, 0x49, 0x76, 0xbf, 0x11, 0xf5, 0x24, 0xfb, 0x8c, 0x2d, 0x56, 0x46, - 0x0b, 0x1e, 0x09, 0x27, 0x39, 0x06, 0x77, 0x7b, 0x87, 0x82, 0xf4, 0xb4, - 0x73, 0x27, 0x59, 0x13, 0x39, 0xaa, 0xb6, 0x27, 0xed, 0x76, 0x70, 0x6b, - 0xd6, 0x59, 0x2f, 0x93, 0xe3, 0x24, 0x45, 0x5c, 0xb7, 0x7b, 0xab, 0x8f, - 0xca, 0xfa, 0xae, 0x24, 0x6a, 0xa4, 0xc8, 0xee, 0xd7, 0x7d, 0xb9, 0x65, - 0x1a, 0xd7, 0xd8, 0x3d, 0x25, 0x46, 0x41, 0x3d, 0x67, 0x6d, 0xe9, 0x49, - 0xa1, 0x39, 0x65, 0xe8, 0x82, 0xa3, 0xe1, 0x38, 0xd3, 0x3d, 0x60, 0x2c, - 0xb5, 0x94, 0x11, 0x00, 0x31, 0xe5, 0x34, 0xaa, 0x59, 0xff, 0xb6, 0xea, - 0x1c, 0xb3, 0xaa, 0x7a, 0xd6, 0x38, 0x2a, 0x45, 0x86, 0x8f, 0x12, 0x87, - 0xc6, 0xf9, 0x92, 0x6b, 0x60, 0x7d, 0x5b, 0x8e, 0x37, 0xb9, 0x52, 0xb6, - 0x95, 0x32, 0xa6, 0xb0, 0x17, 0xe8, 0xdb, 0xe7, 0xde, 0x7f, 0x3c, 0xf4, - 0xfb, 0xd3, 0x4b, 0xa8, 0x19, 0x54, 0x98, 0x67, 0xba, 0x2b, 0xcd, 0x3b, - 0xb2, 0xda, 0xd8, 0x57, 0x37, 0xc4, 0xd1, 0xf0, 0xc6, 0xe5, 0xf6, 0x61, - 0x99, 0x25, 0xf7, 0x0e, 0xe6, 0xdd, 0x6b, 0xce, 0x16, 0x68, 0xb0, 0xe9, - 0xc3, 0xbd, 0xa5, 0x80, 0x98, 0xe6, 0xe9, 0xf8, 0xd7, 0xb5, 0xbe, 0x99, - 0x9e, 0x32, 0x85, 0x40, 0xdd, 0x41, 0x46, 0x54, 0x58, 0xe8, 0x78, 0x9f, - 0x6e, 0xe7, 0xb5, 0xc9, 0x64, 0x59, 0x0d, 0x9a, 0x64, 0x6b, 0xca, 0x69, - 0x2d, 0xb8, 0xb5, 0x1f, 0x6e, 0x8a, 0x4d, 0x53, 0x44, 0x01, 0x7f, 0xeb, - 0xd3, 0x44, 0x63, 0xd0, 0xe5, 0x96, 0x71, 0x8e, 0x2c, 0xcd, 0x02, 0x7b, - 0xd8, 0x99, 0x41, 0x39, 0x28, 0x5e, 0x83, 0xd5, 0x25, 0x8d, 0xeb, 0x38, - 0x01, 0x84, 0x7c, 0xf5, 0x60, 0xfe, 0xfe, 0x81, 0xc1, 0xb3, 0x3a, 0x7d, - 0x61, 0x97, 0x5d, 0x25, 0x82, 0xb1, 0xc0, 0x3b, 0xa1, 0xb4, 0xb4, 0x74, - 0x0e, 0x26, 0x7e, 0xc4, 0xd6, 0xcf, 0x1b, 0x30, 0x3a, 0x6b, 0x89, 0x2b, - 0x9b, 0x5c, 0x24, 0x98, 0xbb, 0xc5, 0x42, 0x62, 0xbc, 0x0d, 0xcd, 0x4a, - 0x62, 0x5d, 0x62, 0x49, 0x4f, 0x65, 0x8a, 0x45, 0x31, 0x59, 0x4f, 0x19, - 0xc1, 0xd1, 0x74, 0x5a, 0x79, 0x16, 0xea, 0x61, 0x08, 0x81, 0x03, 0x79, - 0x35, 0x74, 0x33, 0xaf, 0xcf, 0x80, 0xff, 0xf1, 0x50, 0x80, 0x26, 0x3f, - 0xfc, 0x21, 0x1a, 0xce, 0xe7, 0x1d, 0xe6, 0x04, 0xc0, 0xac, 0xa3, 0x2c, - 0x91, 0x44, 0x64, 0x19, 0x9d, 0x62, 0xdd, 0x79, 0xf5, 0xb9, 0xf7, 0xe3, - 0xed, 0x9f, 0xbf, 0xe6, 0xbe, 0x3d, 0x7e, 0x3f, 0x6f, 0xaf, 0x6d, 0xef, - 0x5e, 0x23, 0x59, 0x55, 0x25, 0x6a, 0xf2, 0xc0, 0xff, 0x9f, 0x7d, 0x9f, - 0xa8, 0xd6, 0x18, 0x9c, 0x98, 0x45, 0x2e, 0xde, 0x1f, 0x3f, 0x4c, 0xdf, - 0xd5, 0xae, 0xdb, 0xee, 0x52, 0xfb, 0x0e, 0xb6, 0x38, 0xee, 0xbd, 0x8e, - 0x5f, 0x37, 0x21, 0xaf, 0x53, 0x58, 0xbe, 0xe8, 0xfb, 0xf9, 0x82, 0xfb, - 0x02, 0xef, 0xde, 0xac, 0x8a, 0xea, 0x19, 0xeb, 0xf9, 0x73, 0xaa, 0xbc, - 0x0e, 0x50, 0x0c, 0x9c, 0x6b, 0x37, 0x78, 0x9d, 0xa0, 0xdb, 0x9e, 0x02, - 0xd1, 0xfc, 0x6b, 0x28, 0x50, 0xd5, 0x6c, 0xbf, 0x6e, 0x4d, 0xb6, 0xce, - 0xef, 0xc7, 0x5a, 0x11, 0x59, 0x3f, 0x5d, 0x71, 0xf1, 0xb7, 0xb0, 0x92, - 0x9a, 0x84, 0x88, 0xbc, 0xf5, 0xa9, 0xc6, 0x46, 0x18, 0xa8, 0xc0, 0x34, - 0x9b, 0x23, 0x45, 0x60, 0x39, 0xab, 0x6e, 0x34, 0x34, 0x71, 0xd8, 0x66, - 0x98, 0x94, 0x78, 0x7b, 0xed, 0x7e, 0x13, 0xbe, 0xb7, 0x76, 0xe0, 0xd1, - 0xd2, 0x83, 0xae, 0xc1, 0x68, 0x1f, 0xe7, 0xfd, 0x2c, 0xfe, 0x72, 0xd1, - 0xb3, 0xa9, 0x28, 0x6b, 0xa3, 0x5d, 0x0c, 0xde, 0xc8, 0xcb, 0xa3, 0xd4, - 0x7c, 0x40, 0x8c, 0xcb, 0xab, 0xd8, 0xf1, 0x9e, 0x28, 0x75, 0xde, 0x3e, - 0xe4, 0x1f, 0x15, 0x5b, 0x5b, 0x2b, 0xc0, 0xbf, 0x14, 0x0a, 0xa8, 0xc9, - 0x11, 0x75, 0x93, 0x11, 0x2a, 0x13, 0xe2, 0x35, 0x8d, 0x38, 0xf3, 0xea, - 0x6f, 0xcf, 0xc7, 0xdb, 0x3c, 0xf3, 0x5f, 0x1e, 0xb5, 0xd5, 0x7b, 0x65, - 0x4a, 0x49, 0x32, 0xae, 0x64, 0x88, 0x1c, 0x9c, 0x63, 0xdd, 0x7e, 0xb8, - 0x93, 0x03, 0x97, 0x1d, 0x34, 0x55, 0xd6, 0xd1, 0x95, 0x5e, 0xa9, 0xd0, - 0x4d, 0xb2, 0xc2, 0x2a, 0x70, 0xdd, 0x03, 0x2d, 0x89, 0xfc, 0x3d, 0xfe, - 0x72, 0xa4, 0xf8, 0xf4, 0x0b, 0x5e, 0x4f, 0x52, 0xff, 0xd1, 0x9c, 0x1b, - 0x11, 0xd4, 0xaf, 0x8d, 0x82, 0x95, 0xad, 0xb8, 0x22, 0xe4, 0x38, 0xff, - 0xf1, 0x50, 0x80, 0x2a, 0x1f, 0xfc, 0x21, 0x1a, 0xcf, 0xe7, 0x57, 0xe0, - 0x2b, 0xff, 0xa7, 0xb3, 0x3c, 0x98, 0xa8, 0xe0, 0x32, 0xbe, 0x3a, 0xf1, - 0xde, 0xbe, 0xff, 0x1f, 0xdd, 0xfe, 0x9f, 0xb3, 0xae, 0xfe, 0xbf, 0x3f, - 0xe7, 0x09, 0xcd, 0x51, 0x4d, 0x78, 0xe2, 0xfa, 0xde, 0x04, 0xbc, 0xa1, - 0x55, 0xeb, 0x43, 0x28, 0x52, 0xba, 0x4b, 0x81, 0x8d, 0x1b, 0xa3, 0x77, - 0x35, 0xea, 0x77, 0xce, 0x1b, 0x74, 0xda, 0x28, 0x47, 0x98, 0xeb, 0xf6, - 0x63, 0xa1, 0xaf, 0x3e, 0xb6, 0x5a, 0x6d, 0xda, 0x25, 0xf6, 0x7c, 0xcc, - 0xf2, 0x8d, 0x7d, 0x1c, 0xf1, 0x90, 0xdf, 0xa4, 0x05, 0x80, 0xd4, 0x14, - 0xe7, 0xdf, 0x0b, 0xa2, 0xbc, 0x94, 0x06, 0x80, 0x16, 0x57, 0x9f, 0xab, - 0x54, 0x95, 0x3a, 0x42, 0x31, 0xb5, 0xa0, 0x31, 0xa7, 0x63, 0x04, 0x3b, - 0x29, 0x91, 0x81, 0x99, 0x1f, 0xbe, 0x67, 0x0b, 0x10, 0x00, 0x94, 0x1c, - 0x18, 0x75, 0x68, 0x9d, 0x9d, 0x1e, 0x87, 0xc8, 0x2b, 0x9c, 0x73, 0x2a, - 0xd5, 0x31, 0x27, 0x96, 0x12, 0x6b, 0xe5, 0x7b, 0x94, 0xcf, 0x06, 0xee, - 0x15, 0xd8, 0x81, 0x2a, 0x7d, 0xf7, 0x0b, 0x38, 0xf9, 0x31, 0xef, 0x59, - 0x2b, 0x35, 0xc0, 0x85, 0x5b, 0x2e, 0x57, 0xb8, 0x33, 0x76, 0x5e, 0xa1, - 0x8b, 0x8b, 0x7d, 0xb4, 0x9c, 0x68, 0x6e, 0x1c, 0x8e, 0x4a, 0x34, 0x79, - 0x67, 0x46, 0x09, 0x3f, 0x35, 0xe2, 0xe2, 0xf0, 0xc2, 0x59, 0x9f, 0x8a, - 0x85, 0xaf, 0x08, 0x47, 0x4d, 0xa6, 0x3a, 0x61, 0x6f, 0xed, 0x44, 0xaf, - 0x73, 0xe1, 0x7a, 0x2b, 0x47, 0xf8, 0x7e, 0x93, 0x59, 0x0f, 0x1b, 0xaa, - 0xb9, 0x56, 0xdd, 0x96, 0xbd, 0xe2, 0xfd, 0x4d, 0x97, 0xa4, 0x7d, 0x7b, - 0xd2, 0x86, 0xb7, 0x4e, 0xbb, 0xe8, 0x01, 0x26, 0xba, 0xf1, 0x55, 0xd7, - 0xef, 0xfd, 0x33, 0xfd, 0x3f, 0x67, 0x5d, 0xfc, 0x73, 0x9f, 0xe3, 0xed, - 0xcf, 0x1b, 0x99, 0x5c, 0x63, 0x5c, 0xcb, 0xd6, 0xf0, 0x31, 0xd5, 0xc6, - 0x7c, 0x29, 0xba, 0xc2, 0xbe, 0x9e, 0x8e, 0xa9, 0xe9, 0xec, 0x9d, 0x89, - 0xae, 0x8e, 0x26, 0x23, 0x13, 0xbc, 0x73, 0x67, 0xde, 0xf1, 0x1c, 0xe5, - 0xcf, 0x71, 0x9f, 0x3f, 0xab, 0xcc, 0x0e, 0x80, 0x57, 0x2b, 0x80, 0x72, - 0x82, 0x39, 0xd4, 0x4e, 0x73, 0x81, 0xa8, 0x29, 0x72, 0x24, 0x71, 0x75, - 0xf6, 0x66, 0x6e, 0xf5, 0xd1, 0xaf, 0xb9, 0xc4, 0xf5, 0x50, 0x07, 0xff, - 0xf1, 0x50, 0x80, 0x2b, 0xff, 0xfc, 0x21, 0x1a, 0xcf, 0xff, 0x8c, 0x22, - 0x50, 0x00, 0xa6, 0xb1, 0xc1, 0xd6, 0x10, 0x15, 0x33, 0x05, 0x12, 0x01, - 0xaf, 0xbf, 0xcf, 0x3a, 0xf8, 0x7d, 0xab, 0xff, 0x2f, 0xf5, 0xaf, 0xaf, - 0x7f, 0xc7, 0xed, 0xfb, 0xf5, 0x3d, 0x75, 0xe3, 0x5d, 0xf5, 0xc7, 0x6b, - 0x53, 0x54, 0x06, 0x5a, 0x43, 0x8d, 0x2e, 0x7e, 0x58, 0xf8, 0xdc, 0x58, - 0xf9, 0x12, 0xec, 0x93, 0x07, 0x21, 0x28, 0xeb, 0x20, 0x0d, 0xf4, 0xb8, - 0x17, 0xdd, 0xd5, 0xa7, 0xce, 0x35, 0x7f, 0x05, 0x9e, 0x7c, 0x7b, 0x4b, - 0xfc, 0x0f, 0x6e, 0x3d, 0x5e, 0xfe, 0xbf, 0x67, 0x64, 0x87, 0x3e, 0x21, - 0x8a, 0xec, 0x28, 0x3a, 0x05, 0xdf, 0xb3, 0x57, 0x35, 0x05, 0x77, 0x72, - 0x06, 0x17, 0x86, 0x63, 0x9f, 0x2d, 0xf0, 0xf4, 0x62, 0xb0, 0xa4, 0x44, - 0x5a, 0xb5, 0x29, 0x08, 0x93, 0x0c, 0x2b, 0x53, 0x85, 0x0b, 0xbd, 0x72, - 0xf4, 0x2d, 0xb1, 0x9c, 0xac, 0xe3, 0xfb, 0xb2, 0x4e, 0x6c, 0x58, 0xcf, - 0x65, 0x16, 0x2c, 0xd6, 0x23, 0x7d, 0x78, 0xe8, 0xc2, 0x26, 0x78, 0xa2, - 0x09, 0xe6, 0x16, 0x9c, 0xc5, 0x65, 0x55, 0x8f, 0x8e, 0x1c, 0x37, 0x19, - 0x42, 0xd8, 0xa9, 0x4f, 0x2b, 0xc1, 0x04, 0xac, 0x21, 0x99, 0xad, 0x10, - 0x84, 0x5d, 0x80, 0x34, 0x10, 0x74, 0x55, 0xf6, 0x61, 0xc4, 0x54, 0x94, - 0x56, 0x21, 0x8d, 0x6c, 0x1b, 0xf0, 0x07, 0xd6, 0x81, 0x22, 0xfe, 0x8d, - 0x31, 0xe2, 0xf9, 0x89, 0x55, 0x32, 0x48, 0x74, 0x3a, 0x20, 0xbc, 0xb4, - 0x2c, 0x9b, 0xed, 0x5a, 0xa4, 0x74, 0x3f, 0x29, 0xc0, 0x53, 0xfe, 0x0a, - 0x1e, 0x68, 0xc6, 0x9b, 0xfe, 0xd7, 0x05, 0xaa, 0xb3, 0x35, 0xae, 0x3d, - 0x1e, 0xf7, 0x94, 0x62, 0x2b, 0xe3, 0x43, 0x75, 0xb6, 0xaa, 0xfc, 0x0a, - 0x0a, 0x67, 0x63, 0x0d, 0x59, 0xe4, 0x60, 0xfa, 0xc0, 0x35, 0xf7, 0xf9, - 0xe5, 0xfe, 0x3e, 0x3f, 0xed, 0x5f, 0x7f, 0xb2, 0xef, 0xcf, 0xcc, 0xff, - 0x6f, 0xb7, 0x8f, 0x3e, 0x27, 0xeb, 0x35, 0x95, 0xa6, 0xd7, 0x30, 0x27, - 0x1d, 0xfc, 0x7e, 0x55, 0x54, 0xd6, 0xd3, 0xac, 0xeb, 0xbf, 0x4e, 0x6f, - 0x0a, 0x90, 0x38, 0x60, 0x3e, 0x49, 0xee, 0x12, 0x79, 0x3c, 0x33, 0xe9, - 0xc6, 0x05, 0xca, 0x57, 0x55, 0xb9, 0x80, 0x7a, 0x80, 0x90, 0x1c, 0x03, - 0x9c, 0xa4, 0x0d, 0x80, 0x01, 0x51, 0xc7, 0xdd, 0x9e, 0xcb, 0xbb, 0x56, - 0x50, 0x9e, 0xa0, 0x05, 0xa4, 0xdc, 0x87, 0x8b, 0xd8, 0x0c, 0xe0, 0x6a, - 0x25, 0xc0, 0xff, 0xf1, 0x50, 0x80, 0x2a, 0x7f, 0xfc, 0x21, 0x2a, 0xcf, - 0xfd, 0x44, 0xdf, 0x04, 0x00, 0xa2, 0xb3, 0x43, 0x1c, 0x4a, 0x84, 0x4b, - 0x18, 0x01, 0xed, 0xe8, 0x00, 0x7d, 0xfe, 0xda, 0xe9, 0xbf, 0xae, 0xf8, - 0xef, 0xc7, 0x53, 0xcd, 0xf5, 0x9b, 0xf6, 0xc0, 0x75, 0x93, 0x01, 0xa5, - 0xd0, 0x54, 0x03, 0x93, 0x19, 0xfe, 0x78, 0x3d, 0xe7, 0x97, 0xda, 0x75, - 0x4b, 0x9c, 0xde, 0x77, 0x69, 0x49, 0x0b, 0x52, 0x4a, 0x51, 0x80, 0x31, - 0xa7, 0x67, 0xf9, 0x80, 0xef, 0xf8, 0xe3, 0x81, 0xe0, 0x29, 0x40, 0x43, - 0x50, 0xb3, 0xb9, 0x74, 0x44, 0x98, 0x98, 0x01, 0x80, 0x6e, 0x90, 0x6a, - 0x7d, 0x1d, 0x79, 0x67, 0xa8, 0x01, 0xcf, 0x19, 0x1e, 0xbc, 0xe3, 0x54, - 0x2e, 0xa3, 0x90, 0x94, 0xec, 0x04, 0x5a, 0xf5, 0x56, 0x90, 0x5e, 0xee, - 0xa7, 0x45, 0x34, 0xb1, 0x50, 0x12, 0xaf, 0xdd, 0xcf, 0x47, 0x8e, 0x1f, - 0x1e, 0x1e, 0xda, 0x3a, 0x83, 0xf1, 0xc3, 0x17, 0x8e, 0xd2, 0xdc, 0x9e, - 0x71, 0x08, 0xc7, 0x88, 0x76, 0x24, 0x6d, 0x66, 0x17, 0x6a, 0xc4, 0x3b, - 0xbb, 0xda, 0xab, 0x52, 0x41, 0x5d, 0xc8, 0xac, 0x46, 0x75, 0xd8, 0x8d, - 0x25, 0x95, 0xd8, 0xd4, 0x3b, 0x70, 0x05, 0xb7, 0x98, 0x0f, 0xe8, 0x9a, - 0x57, 0xec, 0xea, 0x01, 0xfc, 0x74, 0x11, 0xc0, 0x9e, 0x9b, 0x92, 0x83, - 0x3a, 0xd2, 0x79, 0xf3, 0xe1, 0x7a, 0x49, 0x86, 0xe2, 0x35, 0x64, 0x9d, - 0x99, 0x56, 0x02, 0xc4, 0x31, 0x07, 0xb0, 0x62, 0x28, 0x83, 0x50, 0x6f, - 0x5d, 0x95, 0x44, 0x20, 0x1e, 0x79, 0xf2, 0xd7, 0x43, 0x24, 0x1c, 0xa3, - 0x3c, 0xaa, 0x33, 0x6f, 0x2d, 0x72, 0xdf, 0x73, 0xe1, 0x5b, 0x73, 0xdf, - 0x92, 0x2d, 0xd7, 0x10, 0x9f, 0x64, 0xa5, 0x14, 0x50, 0x79, 0x61, 0xa3, - 0xe0, 0x00, 0xcb, 0xde, 0xb8, 0xff, 0x1f, 0xf6, 0xd7, 0xb7, 0xdb, 0x4f, - 0x6d, 0x7d, 0xb5, 0xd1, 0xc6, 0xd3, 0x3d, 0xa6, 0x56, 0x64, 0x94, 0x00, - 0xd2, 0x82, 0xdb, 0x7e, 0x64, 0x00, 0x18, 0xe0, 0x50, 0xfc, 0xae, 0xeb, - 0xe9, 0xdb, 0xab, 0x3c, 0x92, 0x2e, 0xbc, 0x9e, 0x18, 0x72, 0x6e, 0x6b, - 0x76, 0x1c, 0x6e, 0xa2, 0xfb, 0x6a, 0xd1, 0x1a, 0xba, 0x17, 0xbb, 0x65, - 0xcd, 0xd7, 0x2a, 0xa1, 0x51, 0xbc, 0xe7, 0xe7, 0x8c, 0x15, 0x9f, 0xfd, - 0x16, 0x9f, 0x3f, 0xbb, 0xbf, 0x3b, 0xf8, 0x4a, 0x0d, 0x2c, 0x18, 0x2b, - 0xad, 0xbf, 0x64, 0xa8, 0x9f, 0xff, 0xf1, 0x50, 0x80, 0x31, 0x7f, 0xfc, - 0x21, 0x4c, 0x36, 0x80, 0x03, 0xb0, 0x04, 0x00, 0x20, 0x50, 0x52, 0x39, - 0xba, 0x2a, 0x68, 0x36, 0x63, 0x86, 0x24, 0x58, 0x91, 0xb3, 0x11, 0x19, - 0x40, 0x1d, 0xf2, 0x25, 0x09, 0xf7, 0xfc, 0x9c, 0x57, 0xc7, 0xeb, 0xff, - 0x80, 0x59, 0x9a, 0x6c, 0x01, 0xae, 0x11, 0xdf, 0x40, 0x5c, 0x5c, 0x0d, - 0xd2, 0xfc, 0x35, 0xbe, 0x27, 0xc1, 0x1e, 0xad, 0x41, 0x52, 0xc8, 0x3b, - 0xa1, 0x3d, 0x45, 0x76, 0x1f, 0xe7, 0xfd, 0x38, 0x86, 0x07, 0xeb, 0xfd, - 0x1e, 0xb0, 0x0f, 0xc4, 0xea, 0xf1, 0x1b, 0x8b, 0x3a, 0x53, 0x25, 0x95, - 0x79, 0xfd, 0x83, 0x00, 0xeb, 0xaa, 0x37, 0xe2, 0xe9, 0x00, 0x98, 0x6d, - 0xc1, 0x54, 0x75, 0x96, 0x92, 0x57, 0xca, 0x09, 0xde, 0xf0, 0xfb, 0xb4, - 0x0a, 0x53, 0xa4, 0x65, 0xe2, 0xb4, 0x25, 0xd5, 0xd7, 0x35, 0x15, 0x53, - 0xec, 0x23, 0xba, 0x5c, 0xd9, 0x1e, 0x60, 0x23, 0x69, 0xd8, 0x62, 0x29, - 0x13, 0xaa, 0x9f, 0x2a, 0x16, 0x9a, 0x26, 0x98, 0x27, 0xdc, 0x50, 0x77, - 0xb3, 0x27, 0x89, 0x96, 0xfc, 0xa8, 0xb2, 0xab, 0x6a, 0x99, 0xbb, 0xf0, - 0x1e, 0x1e, 0xd5, 0x61, 0xbb, 0xe5, 0x84, 0xd5, 0x50, 0xe9, 0x44, 0xd8, - 0x66, 0x63, 0x5b, 0xa3, 0x25, 0x5d, 0xdd, 0x20, 0x71, 0xf9, 0xf8, 0x31, - 0x7e, 0xb9, 0xbd, 0xc8, 0x08, 0x4d, 0x1e, 0xb3, 0x21, 0x5a, 0xc4, 0x74, - 0xd4, 0xd0, 0xe0, 0xca, 0x58, 0x38, 0x5f, 0xb8, 0x94, 0x66, 0x51, 0xd7, - 0x57, 0xb0, 0x13, 0x3b, 0x0d, 0x16, 0x4e, 0xf5, 0xfd, 0x2b, 0xdc, 0x7c, - 0xfb, 0xb3, 0xd5, 0xfb, 0x7c, 0x7a, 0x6f, 0xab, 0x71, 0x26, 0xa1, 0x60, - 0x97, 0x68, 0xc6, 0x7e, 0x2b, 0xeb, 0x9a, 0x80, 0xf3, 0x06, 0xc0, 0x30, - 0x3f, 0x8a, 0x13, 0x67, 0x49, 0xd7, 0x98, 0x36, 0x60, 0x35, 0x31, 0xb6, - 0x3e, 0xa0, 0x5a, 0xd7, 0xb9, 0x16, 0x23, 0xb7, 0x32, 0x2b, 0xf4, 0xa5, - 0x3f, 0x30, 0x7d, 0xa5, 0x65, 0xf4, 0x06, 0xe5, 0x9d, 0x57, 0x5f, 0xaa, - 0x51, 0xdf, 0x5c, 0xeb, 0xcf, 0xf6, 0xf3, 0xe7, 0x3d, 0xba, 0xbe, 0xd6, - 0xcf, 0xfe, 0x3c, 0xf1, 0x1e, 0x78, 0xd2, 0xd8, 0x26, 0x05, 0x67, 0x13, - 0x21, 0x93, 0xa6, 0x81, 0x21, 0x37, 0x1a, 0x17, 0x90, 0xd9, 0xa9, 0x91, - 0xdf, 0xe8, 0x65, 0x9c, 0x8a, 0xb2, 0x2c, 0xbb, 0x20, 0xf9, 0xc0, 0x69, - 0xe1, 0xf2, 0xb6, 0x93, 0xd5, 0xda, 0xe6, 0x0d, 0x36, 0x52, 0x6a, 0xa2, - 0x53, 0x21, 0x75, 0xa7, 0x4c, 0x6c, 0x94, 0xda, 0x26, 0xca, 0x5e, 0x52, - 0x0c, 0x26, 0xb1, 0xd6, 0x5b, 0x6d, 0xf5, 0x90, 0x5c, 0x31, 0xfd, 0xca, - 0xeb, 0x04, 0xbc, 0xfe, 0xfb, 0x2c, 0xea, 0x5a, 0xe6, 0xc9, 0xe6, 0xad, - 0xa0, 0x50, 0xc4, 0xea, 0xc8, 0x01, 0xe1, 0xdf, 0xec, 0x6a, 0x3b, 0x79, - 0x94, 0xe4, 0x74, 0xf0, 0xff, 0xf1, 0x50, 0x80, 0x2a, 0x7f, 0xfc, 0x21, - 0x7a, 0xce, 0xcd, 0x46, 0x64, 0x80, 0x00, 0xa5, 0xb3, 0xb4, 0x10, 0xa4, - 0x54, 0x2b, 0x14, 0x02, 0x50, 0x00, 0x01, 0x6c, 0xeb, 0x35, 0x57, 0x7a, - 0x9a, 0x95, 0xec, 0x35, 0xe5, 0x99, 0x1e, 0x66, 0x5d, 0x64, 0xed, 0x41, - 0x0a, 0x4c, 0xe2, 0x83, 0x14, 0xe6, 0x5b, 0x69, 0xca, 0x13, 0x05, 0x00, - 0xd7, 0x6a, 0x6f, 0x32, 0x63, 0x36, 0xde, 0xfc, 0x35, 0x60, 0xad, 0xf5, - 0x96, 0x75, 0xa1, 0x9b, 0x19, 0x48, 0x56, 0xf8, 0xa6, 0x81, 0x1a, 0xe9, - 0xc2, 0x6a, 0xe8, 0x94, 0x24, 0x9b, 0xbe, 0xa4, 0x3b, 0x50, 0xd0, 0x6e, - 0x6b, 0xab, 0xb2, 0x25, 0x4b, 0x2e, 0xbd, 0x9b, 0x69, 0x0c, 0xf9, 0xc5, - 0x1d, 0xd4, 0xdc, 0x89, 0x94, 0x8c, 0xd5, 0xd6, 0x86, 0x38, 0xf6, 0xb9, - 0x26, 0xfb, 0x5d, 0x92, 0x12, 0x40, 0x70, 0xa7, 0x14, 0xa0, 0x89, 0xec, - 0x90, 0xe0, 0x46, 0x2b, 0xfd, 0x0b, 0x2e, 0x1e, 0x8c, 0x0b, 0x52, 0xce, - 0x6b, 0x49, 0x01, 0x03, 0xf7, 0x7d, 0x2a, 0x71, 0x06, 0xce, 0x04, 0x26, - 0xe7, 0x6b, 0x41, 0xa4, 0x92, 0x24, 0xc5, 0x61, 0x86, 0xd5, 0x05, 0x1d, - 0xad, 0x82, 0xb1, 0xc6, 0xc6, 0xaf, 0xe3, 0x45, 0x41, 0xb4, 0x13, 0x5e, - 0xe6, 0x90, 0xe7, 0x7a, 0x12, 0x74, 0x1a, 0x3b, 0x43, 0xd7, 0xfb, 0xef, - 0x81, 0x17, 0x53, 0x48, 0x06, 0xa5, 0x31, 0x69, 0x94, 0x84, 0x86, 0x46, - 0x96, 0x7a, 0x52, 0xf6, 0xc5, 0x2e, 0x92, 0xb7, 0x92, 0x24, 0x1b, 0x9d, - 0x8a, 0x91, 0x79, 0x21, 0xff, 0x2e, 0x7a, 0x1e, 0xb5, 0xad, 0xfb, 0x41, - 0x68, 0xa6, 0x6c, 0x46, 0xe2, 0x3d, 0x78, 0xf9, 0xbc, 0xde, 0x6c, 0x69, - 0xf0, 0xd0, 0xd4, 0x61, 0xd7, 0x74, 0xad, 0x4b, 0x43, 0x64, 0xa2, 0x09, - 0x1e, 0xf3, 0xbe, 0x9d, 0xba, 0x8a, 0xef, 0xaa, 0xe5, 0xd3, 0xb7, 0x4e, - 0xe7, 0x1c, 0xd7, 0x5d, 0x4d, 0xeb, 0x9e, 0xb6, 0xbc, 0xcb, 0xc7, 0x00, - 0xc7, 0xc4, 0x2a, 0xd7, 0x32, 0xd8, 0xe1, 0x58, 0x36, 0xa0, 0xb5, 0xe3, - 0x95, 0xad, 0x3c, 0xc8, 0x90, 0x28, 0x72, 0xfb, 0x6b, 0x9c, 0x0c, 0xe5, - 0x68, 0x8a, 0x69, 0xcc, 0x0a, 0x27, 0x61, 0xc2, 0x98, 0x59, 0x4f, 0x07, - 0xa1, 0xc2, 0x83, 0x48, 0xb3, 0xc2, 0xa6, 0x7d, 0x7a, 0xab, 0x85, 0x2e, - 0x79, 0x4b, 0xac, 0xfb, 0x91, 0xbd, 0x0f, 0x66, 0xb2, 0x74, 0x13, 0xe9, - 0xed, 0x50, 0xef, 0x09, 0x0a, 0x03, 0x80, 0xff, 0xf1, 0x50, 0x80, 0x2d, - 0x3f, 0xfc, 0x21, 0x1a, 0xcf, 0x9f, 0xff, 0xff, 0x80, 0x00, 0xa1, 0xb1, - 0x53, 0x99, 0x28, 0x31, 0x2a, 0x19, 0x87, 0x01, 0x00, 0x00, 0x00, 0x01, - 0xa8, 0xd6, 0x9d, 0x4b, 0xe7, 0x8e, 0x85, 0xbc, 0x02, 0x53, 0xa8, 0xc7, - 0x95, 0x2c, 0x0f, 0xab, 0xe4, 0x3b, 0xe4, 0xd5, 0x2a, 0x9f, 0xcf, 0x7b, - 0xeb, 0x6e, 0x8d, 0xdb, 0x67, 0x21, 0x92, 0x26, 0x1c, 0x63, 0x8d, 0x50, - 0xbd, 0x79, 0x32, 0x98, 0xb5, 0xab, 0x09, 0x50, 0xa4, 0x74, 0x83, 0xe1, - 0xc3, 0x45, 0x55, 0x04, 0xfe, 0xa2, 0xad, 0x88, 0x7a, 0xe2, 0x6d, 0x19, - 0x60, 0x4a, 0x53, 0x4b, 0xf3, 0xc7, 0x11, 0xc3, 0xb4, 0x89, 0x8b, 0xf7, - 0x9f, 0x51, 0x0d, 0x5d, 0x76, 0x26, 0x0f, 0x41, 0xdf, 0xe5, 0x76, 0xc5, - 0xc8, 0xe5, 0x53, 0xae, 0x5a, 0x69, 0x34, 0x5c, 0x8a, 0x0c, 0x7c, 0x2c, - 0x82, 0x9e, 0x93, 0xac, 0xc8, 0xd4, 0x15, 0x61, 0xe6, 0x47, 0x57, 0x78, - 0x01, 0x15, 0x42, 0xa4, 0xbb, 0x25, 0x4c, 0x8a, 0x79, 0x70, 0x4a, 0x12, - 0x89, 0x25, 0x1e, 0xd6, 0x95, 0xeb, 0xd9, 0xcb, 0x0e, 0x77, 0x6b, 0x7d, - 0xa5, 0xb1, 0xf4, 0xba, 0x9b, 0xaa, 0xd4, 0x38, 0x21, 0xc2, 0xad, 0xd2, - 0x01, 0x4e, 0xe3, 0x41, 0x03, 0x51, 0x69, 0x50, 0x6a, 0xb3, 0x96, 0xc3, - 0x51, 0x8b, 0x77, 0x53, 0x3d, 0xd5, 0xde, 0xcd, 0x6f, 0xf0, 0xbe, 0x77, - 0xa0, 0x3e, 0x65, 0x8f, 0x42, 0x8f, 0xe0, 0xbf, 0x63, 0xfc, 0x3e, 0xf2, - 0x65, 0xf4, 0x00, 0xdf, 0x08, 0xd7, 0xcf, 0xb4, 0xc6, 0xe9, 0xf0, 0x42, - 0x9a, 0xba, 0x1a, 0x08, 0x49, 0x56, 0x2c, 0x03, 0x2a, 0xb4, 0x80, 0x13, - 0x72, 0xf0, 0x08, 0xac, 0x6f, 0xdf, 0x75, 0x6f, 0xb3, 0x16, 0x14, 0x92, - 0xb9, 0x48, 0xeb, 0x30, 0x65, 0x35, 0x75, 0x59, 0x43, 0x62, 0x85, 0x32, - 0x10, 0xca, 0x2f, 0x78, 0x52, 0x00, 0x00, 0x03, 0x83, 0x13, 0x7a, 0x9b, - 0xba, 0xdd, 0x84, 0x8d, 0xf6, 0x69, 0x87, 0xe9, 0xd5, 0x16, 0x31, 0x92, - 0x96, 0x3d, 0x1b, 0x87, 0xfe, 0x5c, 0x51, 0x91, 0x40, 0xe1, 0x85, 0xcd, - 0xb8, 0x64, 0x69, 0x7c, 0x82, 0x0a, 0x84, 0x3d, 0x9c, 0x1d, 0xa8, 0xbd, - 0x3d, 0x84, 0x31, 0x51, 0xdd, 0xb9, 0x1e, 0x9c, 0x92, 0xfe, 0x65, 0x39, - 0x09, 0x8f, 0x5c, 0x95, 0xbb, 0x9e, 0xd0, 0xfd, 0xa8, 0x71, 0xad, 0xf5, - 0xba, 0x34, 0x35, 0x1b, 0x23, 0x2f, 0xe4, 0x73, 0x1c, 0xe8, 0x75, 0x05, - 0xfe, 0x72, 0x13, 0xa9, 0x1b, 0x87, 0x94, 0xdf, 0x1b, 0xcf, 0x3c, 0x3c, - 0xfb, 0xf0, 0x62, 0xae, 0xaf, 0x28, 0x2a, 0x1c, 0xff, 0xf1, 0x50, 0x80, - 0x2e, 0xdf, 0xfc, 0x21, 0x1a, 0xcf, 0x0f, 0xff, 0xf7, 0x80, 0x02, 0xa0, - 0xb6, 0x41, 0x98, 0xa8, 0x52, 0x22, 0x04, 0x42, 0xc1, 0x41, 0x31, 0x85, - 0x08, 0x00, 0x02, 0xa0, 0x95, 0x7d, 0x6b, 0xed, 0xfa, 0xed, 0xed, 0xbf, - 0x6c, 0xcf, 0x22, 0x45, 0xc0, 0x45, 0xdc, 0xd5, 0x91, 0xf5, 0x7f, 0x08, - 0x36, 0x05, 0x27, 0x71, 0xad, 0xa4, 0x68, 0x7c, 0x95, 0x3a, 0x01, 0xe9, - 0x05, 0x56, 0x07, 0xc3, 0x18, 0xaa, 0x4b, 0x8e, 0xc9, 0x77, 0xc4, 0x13, - 0x00, 0xc4, 0x36, 0x55, 0x29, 0x2b, 0xbb, 0x49, 0x72, 0xef, 0x04, 0x89, - 0x53, 0xab, 0x48, 0x2c, 0xc7, 0xfc, 0x51, 0x0b, 0xe3, 0xc0, 0x06, 0xd5, - 0xeb, 0x1a, 0x69, 0x88, 0xab, 0xf2, 0xc9, 0xbc, 0x86, 0xe5, 0x3c, 0xae, - 0x1a, 0xc4, 0x89, 0xfc, 0xc6, 0xe0, 0x69, 0x6b, 0xb4, 0x86, 0xd2, 0xbf, - 0xdc, 0xcc, 0xa5, 0x8c, 0x0f, 0x55, 0x9d, 0xb9, 0xdf, 0x32, 0xcc, 0x19, - 0x5d, 0x1c, 0xf4, 0x45, 0xda, 0x96, 0x3e, 0x73, 0xc8, 0xa8, 0xd7, 0x2a, - 0x8e, 0x3d, 0xc2, 0x00, 0x7c, 0x10, 0xf5, 0x20, 0xbd, 0x61, 0xf7, 0x22, - 0x5d, 0xd4, 0x32, 0x60, 0x10, 0xe9, 0xf0, 0xaf, 0xda, 0xe6, 0x20, 0x4b, - 0x2e, 0x42, 0x88, 0x51, 0x9b, 0xa9, 0x88, 0xaa, 0x9d, 0xff, 0xac, 0x60, - 0xe1, 0xb9, 0x2b, 0x23, 0x86, 0x13, 0x92, 0xe6, 0x13, 0xc6, 0xc7, 0xde, - 0x12, 0x52, 0x2e, 0x31, 0x6f, 0x1a, 0xa4, 0x4b, 0x40, 0x34, 0x6e, 0xa4, - 0x05, 0x99, 0xab, 0x1b, 0xca, 0x40, 0xb6, 0x30, 0x93, 0xe6, 0xa5, 0x10, - 0xc1, 0x8a, 0xf0, 0xed, 0xb6, 0xc4, 0x84, 0x09, 0xb6, 0x45, 0x5f, 0x92, - 0x1a, 0xe5, 0x12, 0x97, 0x4f, 0x9e, 0x54, 0xa0, 0x70, 0xfe, 0x88, 0x12, - 0xa7, 0x6a, 0x49, 0xa8, 0x0b, 0xef, 0xe8, 0x69, 0x1c, 0xfc, 0x3f, 0xdf, - 0xf5, 0x2c, 0xd3, 0x7e, 0x9c, 0x28, 0x28, 0xf0, 0xa7, 0x0b, 0x21, 0x05, - 0xef, 0x00, 0x00, 0x00, 0x09, 0xa7, 0x2c, 0xd4, 0x9b, 0x9c, 0x77, 0x50, - 0x4d, 0xbe, 0x57, 0xfe, 0x16, 0xfe, 0x77, 0xa6, 0xb3, 0x49, 0x17, 0x04, - 0xe2, 0x07, 0xa6, 0x4d, 0xd0, 0x61, 0x79, 0xca, 0xae, 0x33, 0x82, 0x72, - 0xe4, 0x7d, 0xd7, 0x0a, 0x90, 0x1d, 0x21, 0x90, 0x85, 0x80, 0x87, 0xc4, - 0x43, 0x8f, 0x19, 0x01, 0xaa, 0x6d, 0xdf, 0x2b, 0x66, 0xed, 0x0d, 0x68, - 0x05, 0xd5, 0x90, 0xa5, 0xe9, 0xc6, 0x1f, 0x69, 0x18, 0x34, 0x10, 0x38, - 0xbb, 0x9d, 0xf8, 0x81, 0x9c, 0x05, 0x5b, 0x30, 0x0b, 0xe6, 0xb6, 0x00, - 0x0e, 0x86, 0x43, 0x28, 0x63, 0xdc, 0xad, 0x4a, 0x00, 0x3a, 0xd7, 0x7b, - 0x1b, 0xf7, 0xb9, 0x0b, 0x56, 0x49, 0x41, 0xa5, 0xbd, 0xce, 0xff, 0xf1, - 0x50, 0x80, 0x2f, 0xdf, 0xfc, 0x21, 0x1a, 0xce, 0xdd, 0x70, 0xbf, 0x80, - 0x00, 0x9e, 0xb4, 0xd2, 0x58, 0xca, 0xc4, 0x1b, 0x10, 0xe7, 0x87, 0x3c, - 0x1c, 0xf1, 0xcf, 0x07, 0x23, 0x8e, 0x78, 0x03, 0x5b, 0xea, 0xbe, 0xff, - 0x3e, 0xbe, 0x2e, 0xf8, 0x99, 0xab, 0x1c, 0xc9, 0x3f, 0x3e, 0xd7, 0x66, - 0x9b, 0xd2, 0xa3, 0xd5, 0x67, 0x24, 0x16, 0xea, 0xc2, 0xc0, 0x74, 0xb3, - 0xd0, 0xae, 0x4e, 0x4d, 0x04, 0xb9, 0xf0, 0x01, 0x4c, 0x00, 0x69, 0x35, - 0x92, 0x01, 0xb5, 0xc8, 0x80, 0xca, 0xd6, 0x59, 0x9d, 0xf0, 0xf3, 0xc2, - 0x24, 0xe4, 0x58, 0xbb, 0x26, 0x24, 0x0f, 0x17, 0x78, 0x39, 0x03, 0x81, - 0x33, 0x00, 0xb3, 0xa1, 0xae, 0x55, 0xbf, 0xca, 0x01, 0xe7, 0x08, 0x5d, - 0x75, 0x39, 0xc2, 0xb4, 0x77, 0x7f, 0x08, 0xf4, 0x28, 0x8d, 0x8a, 0x2c, - 0x53, 0x22, 0xeb, 0xea, 0xfc, 0xed, 0x59, 0xbd, 0x6f, 0x0c, 0x5c, 0x2a, - 0x39, 0xfe, 0xe5, 0xc6, 0xe2, 0x40, 0x98, 0x3b, 0x67, 0x2b, 0x60, 0xc6, - 0x6e, 0x62, 0x10, 0x85, 0xb5, 0x44, 0x1a, 0x19, 0x90, 0x61, 0x0c, 0xc5, - 0x56, 0x0b, 0x99, 0xef, 0x22, 0x28, 0x54, 0x44, 0x38, 0x9d, 0x17, 0xdd, - 0xd5, 0x12, 0x7c, 0xda, 0xd7, 0x57, 0x77, 0xb7, 0xb1, 0x52, 0x77, 0x5e, - 0xc0, 0x8e, 0x3c, 0x1a, 0x00, 0x06, 0xfb, 0x57, 0x6e, 0x82, 0x81, 0x38, - 0x90, 0xf7, 0x4c, 0x5a, 0xcd, 0x5d, 0x51, 0xab, 0x3b, 0xe4, 0xea, 0x09, - 0xba, 0xb3, 0x7e, 0xbf, 0x67, 0xe3, 0xf9, 0xfe, 0x3e, 0x1f, 0x15, 0xe9, - 0x39, 0x7d, 0x6a, 0xa4, 0x18, 0x90, 0x9a, 0x98, 0x80, 0x24, 0x60, 0xae, - 0xab, 0xd2, 0xee, 0x97, 0xdf, 0x08, 0x6c, 0x02, 0xbb, 0x2d, 0x2b, 0x2b, - 0x8b, 0x56, 0xa8, 0x02, 0x9f, 0x8d, 0x1c, 0xda, 0xe0, 0x84, 0x00, 0xe4, - 0x9c, 0x29, 0x9c, 0xf9, 0x8c, 0xf5, 0xc2, 0x0a, 0x84, 0x51, 0xfb, 0xda, - 0xd8, 0x00, 0x00, 0x03, 0x4b, 0xf1, 0xaa, 0xe7, 0x57, 0xce, 0xb5, 0xcd, - 0xd0, 0x41, 0x7a, 0xf2, 0x97, 0xdd, 0x38, 0x2d, 0x02, 0x13, 0xab, 0x49, - 0xde, 0xfd, 0xe3, 0x91, 0x76, 0x8c, 0x87, 0x37, 0xdb, 0xf3, 0x47, 0xb3, - 0xd1, 0xe0, 0x1e, 0x11, 0x35, 0x87, 0x88, 0xd0, 0x85, 0x5e, 0x63, 0x74, - 0xa1, 0x0c, 0x9a, 0xa2, 0x32, 0x0e, 0x28, 0x52, 0xb9, 0x28, 0xc3, 0xed, - 0xd5, 0xd6, 0x0b, 0x32, 0x70, 0x79, 0x8c, 0xc1, 0x1e, 0xea, 0xed, 0x23, - 0x2a, 0x13, 0x38, 0xcb, 0x77, 0x13, 0xf7, 0x05, 0x0f, 0x6b, 0xa1, 0xbf, - 0x99, 0xde, 0x80, 0x05, 0x41, 0x57, 0xa5, 0xb1, 0x36, 0x8e, 0x7e, 0xb8, - 0x52, 0xb4, 0xb5, 0x21, 0x31, 0x5e, 0xb7, 0x35, 0x2d, 0xce, 0xf8, 0x44, - 0xe2, 0xf8, 0xaa, 0xb6, 0x13, 0x69, 0x02, 0x9c, 0xff, 0xf1, 0x50, 0x80, - 0x2d, 0x3f, 0xfc, 0x21, 0x1a, 0xcc, 0xde, 0xdf, 0x7b, 0x80, 0x20, 0xa1, - 0xb1, 0x44, 0x11, 0xcc, 0x23, 0x0b, 0x21, 0x8b, 0x4a, 0x2f, 0x0c, 0xb9, - 0x48, 0xab, 0x0a, 0xea, 0xf4, 0xaf, 0x6d, 0xfa, 0xfa, 0xad, 0x66, 0xa5, - 0x41, 0xc7, 0x97, 0x97, 0x57, 0xc7, 0x8f, 0x5f, 0xe5, 0xa3, 0x22, 0xc8, - 0xe5, 0x4c, 0x47, 0x34, 0x1e, 0xa6, 0x28, 0x65, 0xb4, 0x88, 0x45, 0x15, - 0x8d, 0x56, 0xcc, 0xc0, 0xad, 0x13, 0xb0, 0xca, 0x1b, 0xbc, 0xb0, 0x9e, - 0x0b, 0xe7, 0x32, 0xa9, 0xde, 0x58, 0x0c, 0x2e, 0x19, 0x96, 0x63, 0x45, - 0x1f, 0xd1, 0x32, 0x12, 0x68, 0x32, 0x94, 0xf5, 0xc1, 0x2f, 0x3e, 0x04, - 0x7a, 0xb9, 0x96, 0x69, 0x85, 0x11, 0x40, 0x10, 0x43, 0x16, 0xae, 0x5a, - 0xf7, 0xed, 0xe9, 0x90, 0xe2, 0xf9, 0x3a, 0xf2, 0xe2, 0x6e, 0xaa, 0x58, - 0x71, 0x5c, 0x5d, 0x22, 0x2a, 0x37, 0xc9, 0xd9, 0x57, 0xab, 0xbc, 0x9e, - 0x8d, 0x88, 0x1e, 0xb4, 0xfd, 0x4a, 0xcd, 0xf8, 0x62, 0xf2, 0x13, 0xc7, - 0xa9, 0x59, 0x17, 0x4b, 0x45, 0x12, 0x12, 0x0e, 0xda, 0xd8, 0x49, 0x2a, - 0xdb, 0x69, 0x9b, 0x4e, 0x23, 0x4c, 0xec, 0x26, 0xc3, 0x6c, 0xe1, 0xcb, - 0xf4, 0xba, 0xb5, 0x9c, 0x4c, 0x6d, 0xf4, 0x16, 0x64, 0x5c, 0x8d, 0x2f, - 0x34, 0x09, 0x98, 0xb0, 0xc8, 0x71, 0xbc, 0x81, 0x98, 0x18, 0x04, 0xd0, - 0x9f, 0xa5, 0xce, 0x2b, 0x6f, 0xd8, 0x00, 0x33, 0x41, 0xc6, 0x68, 0x84, - 0x14, 0x76, 0x5c, 0x25, 0x1e, 0x4f, 0x5f, 0x0a, 0x3e, 0xd8, 0xa8, 0x25, - 0x94, 0x3a, 0x85, 0x72, 0x45, 0x20, 0x5a, 0xc3, 0xaf, 0xc3, 0x72, 0xbd, - 0xb7, 0xd1, 0x00, 0x01, 0x8b, 0xe1, 0x45, 0x43, 0x66, 0xc8, 0xbb, 0xf4, - 0x81, 0x6c, 0xdc, 0x08, 0xb1, 0xb3, 0x90, 0x4b, 0x10, 0x33, 0x59, 0x47, - 0x5c, 0xfa, 0x7a, 0xa8, 0x6c, 0xec, 0xc8, 0x0a, 0x18, 0x45, 0xef, 0x00, - 0x5e, 0x04, 0xa4, 0xa5, 0x99, 0x75, 0xd4, 0xeb, 0x39, 0xae, 0x37, 0x73, - 0x97, 0x1b, 0xa0, 0x1b, 0x5d, 0x72, 0x9d, 0x10, 0x1b, 0xe2, 0x7b, 0x40, - 0xbc, 0x43, 0x62, 0xe8, 0x10, 0x43, 0x9f, 0x11, 0xa8, 0x1b, 0xfa, 0x23, - 0x20, 0x4a, 0x7e, 0xe6, 0x48, 0x0c, 0x18, 0x90, 0xe6, 0xc6, 0x9f, 0x67, - 0xbd, 0x6f, 0x8b, 0x18, 0x2f, 0xde, 0x45, 0x53, 0xf0, 0x05, 0x04, 0xb5, - 0x68, 0xa8, 0xc7, 0x20, 0x7a, 0xd4, 0x77, 0x32, 0x69, 0xe8, 0x1d, 0xc2, - 0x28, 0xf2, 0xb1, 0x29, 0xb7, 0xfb, 0x36, 0x36, 0x52, 0x6e, 0xe2, 0xe2, - 0x09, 0x8d, 0x2c, 0x3f, 0x76, 0xcf, 0x0c, 0x19, 0x38, 0xff, 0xf1, 0x50, - 0x80, 0x2f, 0xff, 0xfc, 0x21, 0x1a, 0xcc, 0xfd, 0x1f, 0xfe, 0x0c, 0x24, - 0xa5, 0xa3, 0xb2, 0xd1, 0xec, 0xa6, 0x79, 0xa9, 0x37, 0x2b, 0xaa, 0xa9, - 0x95, 0xc6, 0xf3, 0x85, 0x48, 0xa7, 0x59, 0xe7, 0xd3, 0x57, 0xc5, 0x5e, - 0xeb, 0x3a, 0xae, 0x0d, 0x30, 0xc8, 0xa0, 0xf0, 0x61, 0xb2, 0x00, 0x01, - 0xe2, 0x3f, 0x8d, 0x7a, 0x0e, 0x64, 0xb0, 0x21, 0xd7, 0x41, 0xd3, 0x1a, - 0x03, 0x47, 0xda, 0x7f, 0xa9, 0xf8, 0xad, 0xd8, 0x4b, 0x75, 0x57, 0x5f, - 0x71, 0x96, 0x37, 0x9b, 0xfb, 0x40, 0xbf, 0xe2, 0x05, 0x9c, 0x48, 0x4f, - 0xcf, 0x2d, 0x60, 0x41, 0xff, 0x18, 0x3a, 0x7a, 0x7e, 0x2f, 0xff, 0x85, - 0xcc, 0x4f, 0x73, 0xe8, 0x60, 0xdc, 0x6b, 0xc6, 0x9a, 0x1f, 0x6e, 0xba, - 0x4c, 0x3b, 0x30, 0xc3, 0x69, 0x03, 0x2b, 0x7c, 0x31, 0x2f, 0x21, 0x03, - 0xca, 0xab, 0xdb, 0xe5, 0xff, 0xd0, 0x6e, 0xb5, 0x4e, 0xe6, 0xe8, 0x46, - 0x3c, 0x3c, 0x4a, 0x40, 0xfb, 0xa8, 0x7e, 0x3f, 0x45, 0xdb, 0x7b, 0x3d, - 0x91, 0x77, 0x44, 0x95, 0xc5, 0x00, 0x37, 0x14, 0x19, 0x32, 0xfd, 0xac, - 0x62, 0xe3, 0x3a, 0x68, 0x81, 0xf0, 0xe8, 0x96, 0xdc, 0x37, 0x14, 0x13, - 0x24, 0x74, 0x7d, 0x0f, 0xa1, 0xdb, 0x06, 0x67, 0x2b, 0x4d, 0x7d, 0x12, - 0x4e, 0xf4, 0xa4, 0x92, 0x4b, 0x00, 0x06, 0x34, 0xda, 0xc0, 0x3b, 0x0a, - 0x64, 0xb2, 0x51, 0x9b, 0xf0, 0xd8, 0x30, 0xb5, 0x6a, 0x16, 0x95, 0xc9, - 0x02, 0x66, 0xb5, 0x9c, 0x0c, 0x58, 0x8a, 0x73, 0xcb, 0x0f, 0x44, 0x94, - 0xd7, 0x49, 0x5c, 0x16, 0xd3, 0x59, 0x85, 0x12, 0xcd, 0x26, 0xc9, 0x61, - 0xe1, 0xd4, 0x96, 0x15, 0x35, 0x71, 0x76, 0xcb, 0xb2, 0x1f, 0x8f, 0xe2, - 0x67, 0x9c, 0x62, 0xc1, 0x65, 0x39, 0xf8, 0x71, 0xc3, 0xae, 0xbd, 0xb7, - 0x67, 0x38, 0x95, 0x74, 0xb2, 0xca, 0x48, 0x3e, 0xee, 0xed, 0xbd, 0x5d, - 0x27, 0xd3, 0xe0, 0xf8, 0xe9, 0xea, 0x96, 0x2f, 0x51, 0x38, 0x14, 0x20, - 0x0a, 0x5a, 0x1b, 0x45, 0x11, 0xef, 0x1a, 0x93, 0x72, 0xb5, 0x3c, 0x4e, - 0xb7, 0xc6, 0xf3, 0x85, 0x48, 0xa4, 0xea, 0x77, 0xed, 0xde, 0x75, 0x51, - 0xcd, 0x5e, 0xe7, 0x42, 0x24, 0xf8, 0xac, 0x5d, 0x4a, 0xaf, 0xad, 0x4e, - 0xfe, 0xd0, 0x88, 0x7e, 0x0c, 0x1d, 0xf7, 0xd8, 0x06, 0xc1, 0x39, 0x7c, - 0x1a, 0x8f, 0x32, 0xad, 0xd6, 0x1a, 0x6c, 0x13, 0x9a, 0xa4, 0x0d, 0x8b, - 0x43, 0x18, 0xa6, 0x26, 0x17, 0xa9, 0x00, 0x01, 0xef, 0x11, 0x31, 0x5d, - 0x0e, 0xd8, 0x70, 0xb3, 0x96, 0xbe, 0x51, 0x86, 0x66, 0x30, 0xd3, 0x9b, - 0x2c, 0xf7, 0x6d, 0xb6, 0xdc, 0x4d, 0xb1, 0x66, 0xf6, 0xb0, 0x64, 0x0e, - 0x18, 0xef, 0xe9, 0x69, 0xe3, 0x02, 0x1c, 0x4f, 0xff, 0xf1, 0x50, 0x80, - 0x30, 0x3f, 0xfc, 0x21, 0x1a, 0xc9, 0x85, 0xbf, 0xf9, 0x81, 0xf7, 0xa9, - 0xa4, 0xc2, 0x95, 0xc8, 0x86, 0x11, 0xf7, 0x4d, 0x7b, 0xfb, 0x6f, 0x8c, - 0xcd, 0x56, 0x6b, 0x2b, 0x53, 0x9e, 0x13, 0x12, 0x79, 0xf1, 0xc7, 0x3f, - 0x1e, 0x2f, 0x77, 0xb9, 0x77, 0xad, 0x8c, 0x01, 0xf5, 0xb0, 0x2c, 0xb0, - 0x56, 0xca, 0xb1, 0x94, 0xe2, 0xca, 0x06, 0xca, 0xa8, 0x19, 0x2d, 0x4e, - 0x91, 0x16, 0xc2, 0x27, 0x7c, 0x44, 0x4b, 0xd4, 0x20, 0xa9, 0xa1, 0x08, - 0x9b, 0x29, 0x5d, 0xd8, 0x5d, 0x71, 0x7c, 0x4e, 0x6d, 0x97, 0x90, 0x38, - 0x88, 0xfc, 0xe1, 0x70, 0x1f, 0x8c, 0x8a, 0x89, 0xeb, 0xfd, 0xd6, 0xc5, - 0xfc, 0x2b, 0xf6, 0xed, 0x71, 0x2d, 0xf7, 0xda, 0xd8, 0xca, 0xba, 0xbd, - 0x71, 0x9b, 0xda, 0xae, 0xf7, 0x6e, 0x5a, 0xab, 0xc7, 0xaa, 0xf1, 0x52, - 0xd5, 0xc5, 0x64, 0xdc, 0x5f, 0xbb, 0xa7, 0x13, 0x7b, 0x01, 0x24, 0xf7, - 0xf5, 0x98, 0x48, 0xd7, 0x2e, 0xf5, 0xe5, 0xe5, 0xf0, 0x8a, 0x47, 0x39, - 0x9b, 0xdd, 0xcb, 0x55, 0x33, 0xdd, 0x31, 0x48, 0x33, 0x9a, 0xd7, 0x7f, - 0x53, 0x59, 0xe4, 0xdf, 0x20, 0xa1, 0xc7, 0x79, 0x98, 0x74, 0xd9, 0x38, - 0xed, 0xeb, 0xf9, 0xe3, 0xa6, 0x22, 0x13, 0xfb, 0x38, 0x77, 0xcf, 0x60, - 0xc5, 0xf4, 0x66, 0x28, 0xfc, 0x5a, 0xed, 0x42, 0xc3, 0xad, 0xf6, 0x90, - 0xe5, 0x62, 0x90, 0x1e, 0x17, 0x60, 0xea, 0x20, 0x42, 0x0c, 0x1b, 0x31, - 0xd9, 0xf2, 0x5d, 0xb1, 0xd7, 0x74, 0x3c, 0x30, 0x64, 0x6f, 0x74, 0xec, - 0x63, 0x22, 0x13, 0x69, 0x70, 0x33, 0x1f, 0x20, 0x04, 0xa8, 0xea, 0x0e, - 0xaf, 0xf9, 0x1b, 0xe2, 0xfd, 0x36, 0xf8, 0xc6, 0x18, 0x34, 0xae, 0xc4, - 0xa7, 0x6e, 0xfc, 0xa8, 0x67, 0x39, 0x64, 0x29, 0xc0, 0x72, 0xc9, 0x4e, - 0xd4, 0x24, 0x28, 0x89, 0xfb, 0x15, 0x2d, 0x8e, 0xa8, 0xf1, 0xb0, 0x79, - 0x60, 0x0e, 0xb7, 0x8b, 0xcd, 0x65, 0x6a, 0x73, 0xc2, 0x76, 0xe2, 0x5e, - 0xf8, 0xfd, 0x79, 0x8c, 0x64, 0xad, 0x56, 0xc7, 0x1c, 0x29, 0x33, 0x17, - 0xe9, 0x7d, 0xf4, 0x4e, 0x88, 0x29, 0x43, 0x54, 0xdf, 0x16, 0x38, 0xe7, - 0xc9, 0xb0, 0x9f, 0x76, 0x81, 0x92, 0x50, 0x06, 0x23, 0x8a, 0x98, 0xbd, - 0xd1, 0xdd, 0x25, 0xd9, 0x53, 0x3a, 0x06, 0xa4, 0x0b, 0x85, 0x5f, 0xf1, - 0x09, 0x90, 0x81, 0x60, 0x40, 0xca, 0x8c, 0xe0, 0x52, 0xa1, 0xd8, 0xb2, - 0xc9, 0x9d, 0x95, 0xa9, 0xd0, 0xb6, 0x3d, 0x39, 0xd5, 0xf5, 0xce, 0xe2, - 0x91, 0xc7, 0x7c, 0xd0, 0xfa, 0x62, 0x58, 0x10, 0xef, 0x91, 0x7b, 0xe7, - 0xc8, 0x5e, 0xca, 0x83, 0x58, 0x10, 0xb4, 0x80, 0xd7, 0x41, 0xee, 0xef, - 0x93, 0xda, 0xb4, 0x87, 0x96, 0x6f, 0x2c, 0xb7, 0xf0, 0xff, 0xf1, 0x50, - 0x80, 0x2d, 0x9f, 0xfc, 0x21, 0x1a, 0xcd, 0x49, 0x9a, 0x7f, 0xb9, 0x8e, - 0xa5, 0xb0, 0xc4, 0x58, 0x68, 0x57, 0x12, 0x3d, 0xaa, 0xf6, 0x3c, 0x79, - 0xef, 0xef, 0xe3, 0x7a, 0xc9, 0xaa, 0xe7, 0xab, 0xe6, 0xeb, 0xae, 0x65, - 0x45, 0x4a, 0xe3, 0x60, 0x71, 0x50, 0x65, 0x8f, 0xac, 0x75, 0xa2, 0x6d, - 0x6d, 0x05, 0x59, 0x65, 0x13, 0xbd, 0x79, 0x5d, 0x48, 0x36, 0xd2, 0x75, - 0xd5, 0xc6, 0xd6, 0x2c, 0x69, 0x89, 0xb0, 0x73, 0x50, 0x0e, 0x01, 0x38, - 0x58, 0x86, 0x0f, 0x60, 0x75, 0x60, 0x18, 0xd4, 0xd2, 0x6a, 0x84, 0xea, - 0x57, 0xc8, 0xa1, 0x6e, 0x83, 0x17, 0x44, 0xca, 0x06, 0xc9, 0x30, 0x0f, - 0xda, 0x42, 0x2d, 0xd4, 0xf5, 0xca, 0x96, 0xae, 0xee, 0xcd, 0xe5, 0x5e, - 0xde, 0xcd, 0x73, 0x04, 0x01, 0xdf, 0x2c, 0xc9, 0x56, 0x46, 0x1c, 0x71, - 0xcd, 0x77, 0x98, 0x44, 0xce, 0x48, 0x2d, 0x66, 0x18, 0x72, 0x64, 0x09, - 0xa9, 0x35, 0x50, 0x5d, 0xa0, 0xdf, 0x1f, 0x79, 0x2b, 0xc6, 0x15, 0xcb, - 0x6f, 0x4f, 0xef, 0x24, 0x31, 0xdb, 0x7c, 0x27, 0x57, 0xd3, 0x8a, 0x3b, - 0x8a, 0x10, 0x1d, 0x43, 0x0d, 0x05, 0xfd, 0x00, 0xe6, 0x9d, 0xdf, 0xae, - 0x3a, 0xaf, 0xc7, 0x98, 0x07, 0x00, 0xdc, 0x97, 0x39, 0x80, 0x78, 0xa4, - 0xd0, 0x7c, 0x43, 0x63, 0xcb, 0x1e, 0x51, 0x6c, 0xe9, 0xe3, 0x69, 0x35, - 0x18, 0x43, 0x77, 0xc1, 0xb0, 0xb6, 0x17, 0x91, 0x1d, 0x10, 0xa3, 0xfe, - 0xf6, 0x7a, 0x03, 0x0b, 0xf8, 0xd7, 0xbb, 0xdf, 0xc8, 0x12, 0x93, 0x26, - 0x75, 0x8e, 0x0d, 0x86, 0xa5, 0xe1, 0x35, 0x69, 0xcf, 0xc1, 0x7f, 0x97, - 0x99, 0xf6, 0xb8, 0x71, 0x0d, 0x21, 0x6f, 0xe4, 0xf4, 0xdf, 0xc3, 0xfb, - 0x98, 0x22, 0xdb, 0xfa, 0x6c, 0xb5, 0x5f, 0xd8, 0x30, 0x8e, 0x95, 0x2c, - 0x45, 0x57, 0xcf, 0x2d, 0xed, 0xce, 0xa3, 0xcf, 0xad, 0xd7, 0xb7, 0x3a, - 0xed, 0xaa, 0x97, 0x5d, 0x73, 0x2a, 0x72, 0xbd, 0xd4, 0x92, 0xaa, 0x2e, - 0xac, 0x0b, 0xe6, 0xdb, 0x73, 0x60, 0x27, 0xf4, 0x3a, 0x02, 0xe6, 0x91, - 0x7a, 0xa0, 0x00, 0x23, 0x19, 0x84, 0x6d, 0xd6, 0x65, 0x98, 0x2e, 0x7c, - 0x0b, 0xd4, 0x14, 0x14, 0xda, 0x25, 0x51, 0xdc, 0x68, 0x44, 0x61, 0x56, - 0x8c, 0x8f, 0x10, 0xf4, 0x9c, 0xf5, 0xf9, 0xdf, 0xd2, 0xaf, 0xf4, 0x95, - 0xef, 0x57, 0xd1, 0x29, 0xd4, 0x5f, 0xa2, 0xd5, 0x2b, 0x85, 0x7f, 0x39, - 0x3d, 0x71, 0xe5, 0xd9, 0xdb, 0x2f, 0x09, 0xd2, 0xf5, 0xf3, 0xec, 0xa6, - 0xe0, 0x47, 0x78, 0x38, 0x57, 0x26, 0x11, 0x84, 0x84, 0x4a, 0x85, 0x82, - 0x38, 0xff, 0xf1, 0x50, 0x80, 0x2f, 0xbf, 0xfc, 0x21, 0x1a, 0xcf, 0xa0, - 0xdb, 0x6b, 0x91, 0xda, 0xa9, 0xa2, 0x43, 0x99, 0x29, 0x07, 0x17, 0xd1, - 0xcd, 0xef, 0xf7, 0xef, 0x9b, 0xe7, 0x53, 0x8f, 0x9f, 0x3f, 0x5f, 0x9e, - 0x39, 0xe3, 0x7c, 0x52, 0xab, 0x26, 0xa7, 0x2b, 0xc4, 0x8d, 0x08, 0x2a, - 0x6f, 0x1a, 0x04, 0xcd, 0x56, 0x9f, 0xc2, 0x96, 0x94, 0xda, 0x67, 0xcf, - 0xac, 0x93, 0x86, 0xd2, 0xd6, 0xca, 0x59, 0x0b, 0x11, 0xc8, 0xc1, 0x56, - 0xa2, 0xaf, 0xbc, 0xec, 0x19, 0x2b, 0x17, 0x8c, 0x2a, 0xc0, 0xfb, 0x85, - 0x76, 0xb5, 0x9c, 0x0e, 0x17, 0x0c, 0x2e, 0xec, 0x01, 0x65, 0x2c, 0x3b, - 0xab, 0xcb, 0xa7, 0xdc, 0xcb, 0x82, 0x05, 0xa8, 0x08, 0xb2, 0x85, 0x30, - 0x20, 0x30, 0x1d, 0x6a, 0x20, 0x84, 0x4d, 0x3a, 0xc2, 0x03, 0xa3, 0x25, - 0x80, 0x6c, 0x42, 0x65, 0x07, 0x5c, 0x43, 0x3b, 0x49, 0xb7, 0x78, 0x0c, - 0x0a, 0x81, 0x14, 0x40, 0xbc, 0x7f, 0x82, 0x22, 0xb5, 0x52, 0xc9, 0x30, - 0x78, 0xbf, 0xcd, 0xac, 0x2c, 0xd0, 0x25, 0x96, 0x85, 0xf1, 0x19, 0x9f, - 0x87, 0x2c, 0xf3, 0x1c, 0xbf, 0xb3, 0x14, 0xde, 0x30, 0xa3, 0x01, 0x3c, - 0xcc, 0x93, 0x6e, 0xff, 0x64, 0x9f, 0x4b, 0xf9, 0xe1, 0x1e, 0x2f, 0xb3, - 0x20, 0x39, 0x7e, 0xdd, 0x60, 0x76, 0x9e, 0x65, 0x3a, 0xcf, 0xf0, 0x40, - 0x87, 0xde, 0x9a, 0xbf, 0x39, 0x76, 0xf2, 0x07, 0x35, 0xf2, 0x3e, 0x80, - 0xb1, 0x16, 0x32, 0xbb, 0x71, 0x0e, 0x2c, 0x9c, 0x37, 0xec, 0xf9, 0x89, - 0x03, 0x4d, 0xac, 0x4e, 0x50, 0xdc, 0xd2, 0x69, 0xb5, 0x0b, 0x42, 0x00, - 0x1c, 0x41, 0x3f, 0x27, 0xf5, 0xf0, 0xb4, 0xc7, 0xe6, 0xd8, 0x32, 0x71, - 0xe4, 0x8d, 0xf4, 0xd3, 0x06, 0x62, 0x01, 0xde, 0x76, 0x95, 0xb7, 0x7a, - 0x7f, 0x6c, 0xdb, 0x78, 0x2a, 0x98, 0x62, 0xc3, 0xc6, 0xc1, 0xe5, 0xb8, - 0xbe, 0xa5, 0x56, 0xab, 0x5e, 0xae, 0xef, 0x73, 0x37, 0xe6, 0x6f, 0x83, - 0x35, 0x4e, 0xd5, 0xf1, 0x13, 0x35, 0xb6, 0xaa, 0xe0, 0x74, 0x31, 0x62, - 0x20, 0x3d, 0x29, 0x71, 0x3a, 0x8d, 0x38, 0x8c, 0xa8, 0x88, 0xa1, 0xc4, - 0xed, 0x96, 0xdc, 0x27, 0x49, 0xc5, 0xc2, 0x96, 0xe5, 0xac, 0x7c, 0x3d, - 0x0b, 0xfb, 0x65, 0x70, 0xbf, 0x0f, 0x5f, 0x2f, 0xdb, 0xc1, 0x5e, 0x45, - 0xf5, 0x52, 0x23, 0x9f, 0xa7, 0xb2, 0x2f, 0x94, 0xc7, 0xf6, 0xe8, 0x57, - 0xca, 0x75, 0x14, 0xa9, 0x9a, 0xb3, 0x8f, 0x84, 0x3a, 0xef, 0x6b, 0xac, - 0x76, 0xd4, 0x18, 0xe7, 0xea, 0x45, 0x6b, 0x37, 0x59, 0xa8, 0xd2, 0x6b, - 0x1d, 0xda, 0xb9, 0xb2, 0xad, 0x11, 0x74, 0x5d, 0x6c, 0xc8, 0x89, 0x9f, - 0x51, 0xca, 0xe5, 0x77, 0xb4, 0x14, 0xd5, 0x2c, 0x64, 0xe0, 0xff, 0xf1, - 0x50, 0x80, 0x34, 0x7f, 0xfc, 0x21, 0x1a, 0xcf, 0xfb, 0x45, 0x6d, 0x83, - 0x88, 0xab, 0x89, 0x31, 0xd4, 0x68, 0xf7, 0xc4, 0x9a, 0xac, 0xe3, 0xd7, - 0xdf, 0x93, 0x9e, 0x3d, 0xbc, 0x78, 0xfa, 0xd7, 0xcf, 0x9e, 0xfc, 0xcf, - 0x1a, 0x9b, 0xaa, 0x97, 0x54, 0x0d, 0x50, 0x21, 0xf0, 0xdc, 0xd5, 0x69, - 0x31, 0x74, 0xf6, 0x97, 0xae, 0xb3, 0xd3, 0x0e, 0xc6, 0x9f, 0xbc, 0x87, - 0x44, 0x2e, 0xd2, 0x28, 0x8c, 0x17, 0xcb, 0x3a, 0x0e, 0x9b, 0x84, 0x62, - 0x3f, 0x4c, 0x50, 0xb4, 0xe7, 0x50, 0x1c, 0xf9, 0xac, 0x04, 0x02, 0x3a, - 0x8d, 0xc0, 0x13, 0xae, 0xa5, 0xec, 0x8a, 0xa8, 0xdb, 0xdd, 0x0d, 0x70, - 0x01, 0x00, 0x41, 0xd6, 0xc7, 0x10, 0xc0, 0x6e, 0x66, 0xda, 0x2a, 0x21, - 0x54, 0x36, 0x48, 0x21, 0x67, 0x21, 0x96, 0x5e, 0xd2, 0x34, 0x48, 0x75, - 0xce, 0xaa, 0xa6, 0x93, 0xaf, 0xdc, 0x36, 0x44, 0xdb, 0x59, 0x95, 0xcf, - 0xb6, 0x28, 0x8d, 0x54, 0xc6, 0x87, 0xee, 0x9e, 0x70, 0xb3, 0xf3, 0x6c, - 0x57, 0x51, 0x64, 0x0a, 0x7b, 0x45, 0x81, 0xbd, 0xfe, 0xe1, 0x67, 0xe5, - 0xcb, 0x8f, 0x4b, 0x0d, 0x50, 0x20, 0xb1, 0xf9, 0x0a, 0xf7, 0xcf, 0xf5, - 0xcc, 0x43, 0xc9, 0xec, 0x23, 0xf0, 0x6f, 0x69, 0x70, 0x4a, 0x59, 0x56, - 0xb3, 0x6d, 0x4a, 0x40, 0x15, 0x8a, 0x08, 0x72, 0xf6, 0x9b, 0x5e, 0x58, - 0x8c, 0x18, 0x7a, 0xfb, 0x61, 0x20, 0x26, 0xfa, 0xe0, 0xa6, 0x77, 0x3b, - 0x90, 0x99, 0x9a, 0x98, 0xc3, 0x45, 0x3c, 0x9a, 0xcb, 0xd1, 0xc0, 0x9c, - 0x41, 0x4e, 0x06, 0x65, 0x0f, 0xb1, 0x76, 0x67, 0xd9, 0xfc, 0xb1, 0x05, - 0x7b, 0x36, 0x07, 0xf7, 0xda, 0x55, 0x19, 0x6a, 0x7c, 0x31, 0x5b, 0xfa, - 0x01, 0xe1, 0x9e, 0xf1, 0xe3, 0xb7, 0x09, 0x55, 0xc4, 0x99, 0x0a, 0x2f, - 0x0b, 0x0f, 0x88, 0x82, 0xf2, 0xbe, 0x24, 0xd5, 0x67, 0x19, 0xd7, 0x3c, - 0xf1, 0x39, 0xe1, 0xe3, 0xeb, 0x39, 0xf2, 0x93, 0x5e, 0x27, 0x7d, 0x13, - 0x22, 0xa2, 0x5d, 0x58, 0x74, 0x22, 0x6c, 0x45, 0x01, 0xf0, 0x22, 0x67, - 0x2e, 0x64, 0x05, 0x6a, 0x24, 0xc4, 0xc1, 0x4c, 0x03, 0x44, 0xf1, 0x97, - 0x9c, 0x23, 0x30, 0x62, 0x9b, 0xd1, 0x15, 0x0e, 0xa9, 0x4a, 0x90, 0xc0, - 0xc2, 0x21, 0x3a, 0xf1, 0x26, 0x76, 0xb0, 0x4e, 0xe7, 0x50, 0x07, 0x5c, - 0x17, 0x5c, 0x8d, 0x15, 0x00, 0x7f, 0x54, 0x19, 0xc5, 0xce, 0xcc, 0xc0, - 0xe4, 0xe2, 0x63, 0x93, 0x20, 0xc2, 0x4e, 0x81, 0x05, 0x7b, 0x50, 0x17, - 0x06, 0x8f, 0xa4, 0x03, 0x94, 0x2d, 0x55, 0x20, 0x25, 0x76, 0x3e, 0x16, - 0xf0, 0x54, 0xc9, 0x12, 0x6a, 0x12, 0x93, 0x62, 0x2a, 0x51, 0xe7, 0x17, - 0x04, 0xa2, 0x1c, 0x39, 0xd0, 0x92, 0x94, 0x24, 0x3c, 0x4a, 0xe6, 0x2a, - 0x81, 0x80, 0x59, 0x21, 0x89, 0xde, 0x23, 0x68, 0x15, 0x94, 0xca, 0x86, - 0x16, 0x72, 0xd3, 0xd9, 0xf8, 0x7f, 0x2a, 0xe3, 0x3e, 0x87, 0x81, 0xab, - 0x78, 0x66, 0xbe, 0xa4, 0xdf, 0xc0, 0x24, 0x13, 0xc0, 0xff, 0xf1, 0x50, - 0x80, 0x3d, 0x7f, 0xfc, 0x20, 0xa6, 0x2a, 0xd0, 0xc6, 0x62, 0x2d, 0x82, - 0x84, 0x61, 0x92, 0x6b, 0x32, 0x67, 0xfb, 0xff, 0x06, 0x6a, 0x57, 0x7f, - 0x7e, 0x3f, 0x3c, 0x67, 0x9e, 0x72, 0x57, 0x15, 0xcc, 0x71, 0x5d, 0x7b, - 0xb4, 0xe2, 0xf6, 0x34, 0x83, 0xf0, 0xe9, 0xf7, 0x7f, 0xcf, 0xc3, 0x4f, - 0x6f, 0xdd, 0xc7, 0x6c, 0xbc, 0xad, 0xc6, 0x25, 0xc5, 0x1d, 0x77, 0xd5, - 0x2e, 0x5a, 0x9c, 0xc4, 0xc7, 0xbd, 0x3c, 0x06, 0xce, 0xc0, 0xbb, 0xa1, - 0x57, 0x2b, 0xdd, 0xfe, 0xfe, 0xc7, 0x65, 0xc4, 0x08, 0x40, 0x9f, 0xed, - 0x42, 0xf7, 0xdf, 0x2f, 0x7c, 0x77, 0x12, 0xe5, 0xbf, 0xc4, 0x0b, 0x5f, - 0x00, 0x33, 0x21, 0x14, 0xce, 0xd2, 0x63, 0x29, 0xe0, 0x94, 0x5e, 0x5c, - 0x2a, 0x08, 0xbb, 0x85, 0x0e, 0x0e, 0x6b, 0xee, 0x39, 0x8e, 0x31, 0xb7, - 0x5b, 0x7a, 0x87, 0x7f, 0xf6, 0x84, 0xe8, 0xdc, 0x32, 0x98, 0x8a, 0xfe, - 0x75, 0x51, 0x36, 0xe0, 0x32, 0xd2, 0x83, 0xb4, 0xa7, 0xd2, 0x80, 0x5f, - 0x14, 0xf3, 0x9e, 0xd7, 0x1b, 0x31, 0x6b, 0x11, 0xc5, 0x45, 0xfb, 0xfa, - 0xa9, 0x6e, 0x51, 0xa1, 0x75, 0x2c, 0xf7, 0x5e, 0xcf, 0x66, 0x51, 0x59, - 0x11, 0x1e, 0x83, 0xaf, 0xbe, 0xdc, 0x4c, 0x25, 0x28, 0x3e, 0xb4, 0x20, - 0x0f, 0x36, 0x06, 0x2e, 0xb2, 0x3f, 0x98, 0xe9, 0x64, 0xbe, 0x9a, 0x28, - 0x18, 0x29, 0x9e, 0xf7, 0x20, 0xce, 0xee, 0x14, 0xda, 0x26, 0x8c, 0x9d, - 0x4c, 0x69, 0x81, 0x0a, 0xaf, 0x72, 0xda, 0x20, 0x68, 0x8c, 0xcf, 0x60, - 0xfe, 0x76, 0x5b, 0xbc, 0x24, 0x9b, 0x86, 0xd7, 0xcf, 0x55, 0xd1, 0x6b, - 0x4a, 0x96, 0x5a, 0x5e, 0x22, 0xe3, 0xa0, 0x41, 0xa4, 0x3d, 0x74, 0xbc, - 0xdc, 0xe2, 0x69, 0x12, 0x5d, 0x97, 0x83, 0xdb, 0xc9, 0xea, 0x5c, 0xcd, - 0x08, 0xa5, 0x21, 0x0a, 0xc1, 0xad, 0x09, 0x66, 0x25, 0x9d, 0x26, 0xaa, - 0x1c, 0x78, 0xae, 0x15, 0x75, 0xcf, 0x5a, 0xef, 0xaa, 0xbe, 0x75, 0xe7, - 0xd7, 0x52, 0xb5, 0xbd, 0x55, 0x24, 0xca, 0xde, 0x80, 0xb1, 0xfe, 0xda, - 0x74, 0xf6, 0xf1, 0xeb, 0x61, 0xcb, 0xae, 0x3a, 0xee, 0xb1, 0x31, 0x2e, - 0x1b, 0xa6, 0xcc, 0xb7, 0x23, 0xd5, 0x2d, 0x5f, 0xee, 0xf8, 0x96, 0x93, - 0x8e, 0x6f, 0x5f, 0x42, 0xb7, 0xaa, 0x5c, 0xec, 0xae, 0x03, 0x72, 0x02, - 0x7c, 0x44, 0x2f, 0x94, 0x0e, 0x19, 0xed, 0x27, 0x27, 0x06, 0xd5, 0x41, - 0x16, 0x89, 0x47, 0x94, 0xe4, 0x8f, 0xf7, 0xd4, 0xc2, 0x2d, 0xcc, 0xfd, - 0xd7, 0xb3, 0xec, 0x87, 0xdf, 0x60, 0x81, 0x58, 0x67, 0x8e, 0xa3, 0x97, - 0xc3, 0x22, 0x27, 0x8e, 0x24, 0xaf, 0xd0, 0xb1, 0xb5, 0xde, 0x64, 0x67, - 0x8b, 0x73, 0x6f, 0xb9, 0xc5, 0xa4, 0xc2, 0xc6, 0xd4, 0x87, 0x96, 0x62, - 0x18, 0x1d, 0x1a, 0x3d, 0x07, 0x47, 0xc0, 0x9a, 0x95, 0x03, 0xc9, 0x3f, - 0x65, 0xa9, 0xcf, 0x81, 0xf2, 0xc2, 0xaa, 0xe2, 0x23, 0x01, 0xb8, 0xdb, - 0x02, 0x76, 0x42, 0x4d, 0x01, 0xcd, 0xa2, 0x3f, 0x2e, 0x47, 0xa9, 0x0c, - 0x57, 0x30, 0x2d, 0x76, 0x3d, 0x61, 0xce, 0xba, 0x92, 0x6c, 0x0a, 0xe6, - 0x26, 0xee, 0x9c, 0xce, 0x72, 0xa1, 0x8f, 0xfe, 0x0f, 0x27, 0x50, 0x54, - 0xba, 0xac, 0x74, 0x70, 0x7f, 0x75, 0x88, 0x7d, 0x5e, 0x4d, 0xb5, 0x46, - 0x14, 0x05, 0xe6, 0x4d, 0xcb, 0x78, 0xb5, 0x11, 0xab, 0xf2, 0x41, 0xae, - 0xdd, 0x5c, 0xdd, 0x00, 0xd0, 0x78, 0x02, 0x3c, 0xe6, 0xa2, 0xec, 0x54, - 0x01, 0x36, 0xc7, 0xa0, 0x64, 0x4c, 0xac, 0x70, 0xff, 0xf1, 0x50, 0x80, - 0x3c, 0x1f, 0xfc, 0x20, 0x9b, 0x4c, 0x9b, 0x66, 0x21, 0x53, 0x56, 0x6b, - 0x34, 0xb3, 0x66, 0xa3, 0x15, 0xa6, 0x6d, 0xb2, 0x2e, 0x00, 0x00, 0x00, - 0x7f, 0xdf, 0xfe, 0xc0, 0x00, 0x0d, 0x31, 0x78, 0x09, 0x18, 0x99, 0xca, - 0x88, 0x3e, 0x22, 0x3b, 0xde, 0x97, 0x1b, 0x69, 0x63, 0xc6, 0xb4, 0x54, - 0xf3, 0x61, 0x3b, 0x06, 0x53, 0xde, 0x05, 0xe0, 0xbf, 0x0b, 0xc5, 0x44, - 0x21, 0xcc, 0x70, 0x74, 0x06, 0x46, 0x8e, 0x82, 0x9a, 0xa9, 0x95, 0x54, - 0x53, 0x05, 0x74, 0xf7, 0xc9, 0xd8, 0x0d, 0xb2, 0xb0, 0x71, 0x7e, 0xe7, - 0x45, 0xd5, 0x34, 0x51, 0xe3, 0x4e, 0x2d, 0x2c, 0x49, 0x60, 0x0b, 0x26, - 0xcc, 0xb2, 0xa3, 0xb2, 0x5c, 0xa6, 0xcf, 0x49, 0x43, 0x58, 0xa4, 0x4c, - 0xcc, 0xf3, 0xa2, 0x03, 0x07, 0x10, 0xc4, 0x3e, 0xc9, 0x39, 0xa1, 0xc9, - 0x6e, 0x40, 0xf0, 0xfc, 0xee, 0x7d, 0xc5, 0x77, 0xc2, 0xe2, 0xa8, 0x06, - 0x77, 0x81, 0xb5, 0xd7, 0x0b, 0x0c, 0xa2, 0xb6, 0x60, 0x53, 0x65, 0xbd, - 0x04, 0xd5, 0x1d, 0x97, 0xa0, 0x84, 0xa4, 0x70, 0xbe, 0xab, 0x3f, 0xf0, - 0xf5, 0xa8, 0x11, 0x98, 0xf2, 0xce, 0xaf, 0x6a, 0x54, 0x95, 0x59, 0x41, - 0x96, 0x22, 0x75, 0xdf, 0x7e, 0x98, 0xf9, 0xa2, 0x6e, 0x2c, 0x8a, 0x5f, - 0x9f, 0xb3, 0x7c, 0xff, 0x84, 0xae, 0xd1, 0x0b, 0x9a, 0xbb, 0xec, 0xba, - 0xd6, 0x33, 0x4a, 0x09, 0xb2, 0x3d, 0x92, 0xcb, 0x03, 0x6c, 0x41, 0x92, - 0x27, 0x92, 0x27, 0x72, 0x74, 0x90, 0x4c, 0x94, 0xd3, 0x0c, 0x66, 0xcd, - 0x1f, 0x80, 0x6a, 0x8b, 0x1e, 0x77, 0xcd, 0x3f, 0xd1, 0x1a, 0xb0, 0x5e, - 0xd3, 0x66, 0xe7, 0xa3, 0xba, 0x09, 0x29, 0x9b, 0x94, 0x63, 0x4d, 0xc4, - 0x56, 0xa6, 0x40, 0x90, 0x19, 0x41, 0x2f, 0x9c, 0x1e, 0xb8, 0x6b, 0x50, - 0xd8, 0xea, 0xb4, 0x2b, 0x11, 0x1a, 0x11, 0x29, 0xf7, 0xe7, 0xd7, 0x12, - 0x73, 0xe7, 0xd7, 0x9f, 0xdf, 0xfb, 0xfd, 0xe4, 0x97, 0x7c, 0x73, 0xc6, - 0xf3, 0x9c, 0xce, 0xab, 0xa5, 0x6a, 0xa0, 0x03, 0xaa, 0x48, 0x69, 0x78, - 0x3c, 0xf9, 0x1f, 0x8d, 0xbc, 0x49, 0xb6, 0xc0, 0x9d, 0x16, 0x47, 0x52, - 0xac, 0xbd, 0xae, 0xaa, 0x93, 0x15, 0xa6, 0x2b, 0x12, 0x84, 0x07, 0x23, - 0x33, 0x62, 0x7e, 0xdd, 0xe7, 0x52, 0x71, 0x19, 0x03, 0x0d, 0x39, 0x29, - 0xa4, 0xa9, 0x89, 0xdd, 0x32, 0x08, 0x1b, 0x3f, 0xf0, 0xbc, 0x03, 0x8c, - 0x74, 0x1c, 0x6c, 0xe3, 0xda, 0xe4, 0x0b, 0xb4, 0x1a, 0xb6, 0x47, 0x04, - 0x25, 0x22, 0x46, 0x81, 0xf6, 0x80, 0x2a, 0x21, 0x51, 0xb9, 0x8a, 0x10, - 0x03, 0x30, 0xe0, 0xd0, 0x28, 0x95, 0x33, 0xaa, 0x9c, 0xb4, 0xba, 0xc5, - 0xd5, 0x4d, 0x3d, 0xff, 0xd6, 0x4e, 0x8e, 0x9b, 0x0a, 0x5f, 0x49, 0xd5, - 0x12, 0xbc, 0x56, 0x5b, 0xb8, 0xec, 0x61, 0xb7, 0x65, 0x59, 0xc4, 0x55, - 0x8a, 0xd8, 0x08, 0x96, 0xab, 0x30, 0x8b, 0x06, 0xc0, 0xac, 0x55, 0xa9, - 0x99, 0xbc, 0xb7, 0x25, 0x09, 0x9b, 0xc9, 0x4c, 0x92, 0x87, 0x18, 0x8f, - 0x65, 0x42, 0xb1, 0x54, 0x40, 0x9d, 0x15, 0x16, 0x94, 0x1a, 0x66, 0xf2, - 0x5f, 0x25, 0x11, 0xa8, 0xf8, 0x6f, 0x1a, 0x99, 0xdd, 0x29, 0x2e, 0x91, - 0x88, 0xf3, 0x66, 0x73, 0x63, 0xf3, 0xdc, 0xab, 0x8a, 0x08, 0x08, 0xf1, - 0x25, 0x06, 0x9b, 0x89, 0x7e, 0xd7, 0xb9, 0x97, 0x81, 0x33, 0xc3, 0xfe, - 0x17, 0xfb, 0x3d, 0x0a, 0xb6, 0x8f, 0xae, 0xbe, 0xff, 0xf1, 0x50, 0x80, - 0x38, 0x7f, 0xfc, 0x20, 0xaf, 0x7a, 0xd4, 0x36, 0x72, 0x68, 0x79, 0xbe, - 0x77, 0xed, 0xcf, 0x1a, 0xf1, 0xc5, 0x75, 0xdf, 0xc7, 0xcf, 0xdf, 0xaf, - 0xe3, 0xf1, 0xeb, 0xaf, 0x5d, 0x6f, 0xce, 0xb9, 0x99, 0x24, 0xab, 0x25, - 0x56, 0x82, 0x19, 0x76, 0x91, 0x0b, 0x43, 0xb6, 0x66, 0x25, 0x3f, 0x62, - 0x1f, 0xb2, 0xcc, 0xad, 0xd1, 0x4d, 0x02, 0xc8, 0x88, 0x13, 0xf8, 0x09, - 0xd0, 0x44, 0x58, 0xa2, 0x31, 0x19, 0x68, 0x60, 0x94, 0xa9, 0x67, 0xf3, - 0x95, 0xd4, 0xf4, 0xc9, 0x74, 0xef, 0xee, 0xcf, 0x5b, 0xb9, 0xb8, 0x41, - 0x42, 0xdf, 0x1a, 0x89, 0xbb, 0x29, 0x56, 0x4f, 0x28, 0x28, 0xa7, 0x71, - 0xce, 0xbf, 0x46, 0x89, 0x05, 0x0e, 0xb9, 0xa3, 0x5d, 0xed, 0x77, 0x17, - 0x27, 0x04, 0x81, 0xba, 0x50, 0x81, 0xf1, 0x2a, 0x8c, 0xf2, 0x42, 0x3c, - 0x47, 0x34, 0x36, 0x72, 0x09, 0xac, 0xbc, 0x7b, 0xb9, 0x1d, 0x8d, 0xcc, - 0x0b, 0xe2, 0xe7, 0x95, 0x9c, 0x77, 0x71, 0x6d, 0xce, 0xb3, 0x33, 0xae, - 0x9f, 0x40, 0x81, 0xa8, 0xc2, 0x6a, 0x94, 0x43, 0x06, 0x1f, 0xdf, 0xce, - 0xac, 0x0b, 0x95, 0x69, 0xeb, 0x32, 0x85, 0x54, 0x20, 0xc0, 0x7f, 0x03, - 0x74, 0x15, 0xbc, 0x43, 0x8f, 0xf5, 0x65, 0xc2, 0xd5, 0x4a, 0xeb, 0x41, - 0x9c, 0x6f, 0x93, 0x32, 0x8d, 0x28, 0x80, 0xd1, 0xd8, 0x95, 0x09, 0xa0, - 0x88, 0xc6, 0xf5, 0xd5, 0xa4, 0x5b, 0x1c, 0xe7, 0x96, 0x24, 0x6d, 0xf8, - 0xaa, 0xf6, 0x82, 0x98, 0x59, 0x17, 0x75, 0x17, 0x28, 0xc9, 0x63, 0xbd, - 0x7c, 0x44, 0xca, 0xab, 0x46, 0xb5, 0x0d, 0x92, 0xad, 0x61, 0x20, 0xd8, - 0x88, 0x91, 0x77, 0xba, 0xea, 0xb5, 0xe7, 0xed, 0xc1, 0x97, 0x97, 0xe7, - 0xd7, 0xb7, 0x7c, 0x73, 0xfa, 0xb3, 0x5b, 0xe1, 0x57, 0xce, 0x48, 0xcd, - 0x56, 0x80, 0xdd, 0xbb, 0x18, 0x46, 0x07, 0xcc, 0x38, 0x49, 0x5d, 0xdd, - 0x01, 0x7f, 0x4b, 0x39, 0x78, 0x53, 0x0a, 0xd1, 0x08, 0x73, 0x65, 0x82, - 0xce, 0xc8, 0x71, 0xbd, 0xe7, 0xd1, 0x97, 0x4c, 0x4f, 0x12, 0x3b, 0x25, - 0x1c, 0x83, 0x11, 0xd2, 0x8c, 0xaf, 0xe5, 0x2b, 0xb4, 0x56, 0x67, 0x1b, - 0x32, 0xaa, 0xb2, 0xd2, 0x1d, 0xf2, 0x15, 0xa0, 0x4a, 0x90, 0x8e, 0xd1, - 0x68, 0xac, 0x1b, 0x8a, 0x01, 0xe2, 0x4d, 0x03, 0x0e, 0xfd, 0x25, 0xdf, - 0x38, 0x62, 0x62, 0x19, 0x51, 0xe9, 0x79, 0xdd, 0xdd, 0xd3, 0xc8, 0xe3, - 0xde, 0x7c, 0x54, 0x96, 0xd0, 0x39, 0x27, 0x2d, 0x53, 0x95, 0xb5, 0x08, - 0xc4, 0x18, 0xa8, 0xb2, 0x2c, 0x4a, 0x57, 0xb1, 0x02, 0xca, 0xc2, 0x6a, - 0x28, 0x21, 0x02, 0xa8, 0x84, 0xe8, 0x05, 0x01, 0xb2, 0xbb, 0xc8, 0x5a, - 0x01, 0x05, 0x83, 0xba, 0x12, 0xa5, 0x1d, 0x4b, 0x7e, 0x99, 0x76, 0x52, - 0x52, 0xe7, 0x43, 0x86, 0xf3, 0xe8, 0x31, 0x36, 0x00, 0x99, 0x76, 0x76, - 0x29, 0x61, 0x08, 0x25, 0x5e, 0xc6, 0x16, 0xa5, 0x56, 0xe4, 0xbb, 0x4c, - 0x2d, 0xf8, 0x08, 0xb5, 0xc0, 0xc3, 0x66, 0x19, 0xad, 0x51, 0x74, 0xa9, - 0x9d, 0x1c, 0xca, 0x3e, 0x73, 0x15, 0xa3, 0xbf, 0xba, 0x1f, 0x84, 0x7c, - 0xc4, 0xcd, 0x1d, 0x25, 0x94, 0x9f, 0xa4, 0xba, 0xbe, 0xdf, 0x65, 0x1f, - 0xa0, 0x17, 0xc0, 0xff, 0xf1, 0x50, 0x80, 0x22, 0xff, 0xfc, 0x21, 0x1a, - 0xcf, 0x7f, 0xf6, 0x30, 0xf6, 0xb3, 0xac, 0xb1, 0xb2, 0x15, 0xc9, 0x21, - 0x77, 0x99, 0xc6, 0x75, 0x3c, 0x7d, 0x78, 0xd5, 0x5f, 0x7f, 0x7e, 0xbf, - 0x6f, 0x8d, 0xeb, 0xc7, 0xd7, 0x3c, 0x56, 0x10, 0xa4, 0xd6, 0xe6, 0x80, - 0x4e, 0x3d, 0x85, 0xa7, 0x6e, 0x72, 0x8f, 0x90, 0x7f, 0xcd, 0xbc, 0x89, - 0x3c, 0x3d, 0xab, 0x6e, 0x77, 0xcc, 0x70, 0xc4, 0xb8, 0xf1, 0x3b, 0x72, - 0x9e, 0x50, 0xed, 0x5d, 0xe4, 0xd5, 0xa8, 0x7b, 0xb4, 0xee, 0x92, 0x62, - 0xbc, 0x6f, 0xe1, 0xd9, 0x83, 0xae, 0x0c, 0xde, 0xb7, 0x93, 0x6b, 0x27, - 0xce, 0x11, 0x51, 0x7b, 0x6a, 0xbb, 0x05, 0x2f, 0x62, 0x42, 0x75, 0xb5, - 0xb1, 0x10, 0xc4, 0x54, 0x9e, 0x6e, 0x0b, 0xc4, 0xf9, 0x8d, 0x23, 0x1c, - 0xe8, 0x99, 0x9e, 0x18, 0x92, 0xf8, 0xcd, 0xb2, 0x26, 0x72, 0x98, 0xc7, - 0x2a, 0x17, 0x7a, 0xc5, 0x4e, 0xfb, 0xe6, 0x6e, 0xb8, 0x36, 0x40, 0x8c, - 0xbb, 0xe1, 0xd9, 0xe2, 0x21, 0x22, 0xd1, 0xb1, 0x27, 0x38, 0xca, 0xf5, - 0x12, 0x72, 0xd5, 0x4f, 0x4f, 0x53, 0xd8, 0x42, 0x55, 0x2e, 0xb8, 0xa2, - 0xce, 0xf3, 0x2f, 0x83, 0x58, 0xec, 0x56, 0x0f, 0x7a, 0x63, 0xaf, 0x3d, - 0x85, 0x16, 0xf8, 0x84, 0x0f, 0x89, 0xdf, 0x54, 0xb7, 0xa9, 0x62, 0x40, - 0xf5, 0x5c, 0x6b, 0x8c, 0xf5, 0x7b, 0xb6, 0x3d, 0xdb, 0x4c, 0x3b, 0xf7, - 0xbd, 0xf6, 0xfa, 0xe2, 0xe1, 0x22, 0x02, 0x0e, 0x28, 0x1c, 0x42, 0xc6, - 0x5b, 0xf6, 0x97, 0x7a, 0xc3, 0xd7, 0x44, 0x38, 0xcc, 0xe3, 0x3a, 0x9e, - 0x3e, 0xbc, 0x6b, 0xd7, 0xc7, 0x79, 0xae, 0x7f, 0x7d, 0xef, 0x8f, 0xac, - 0xbe, 0xef, 0x77, 0x2a, 0xf2, 0xca, 0xae, 0x03, 0xbf, 0xbf, 0x7f, 0x75, - 0xac, 0x33, 0x53, 0x7c, 0x89, 0xe0, 0xd7, 0x0b, 0x0c, 0x9a, 0x64, 0xc5, - 0x49, 0x8e, 0x59, 0xc5, 0x9a, 0xe8, 0xcc, 0x1c, 0x93, 0x14, 0x61, 0xb1, - 0xb3, 0xe3, 0x5b, 0xbf, 0xf8, 0x4e, 0xff, 0xf1, 0x50, 0x80, 0x27, 0xbf, - 0xfc, 0x21, 0x1a, 0xcf, 0x4f, 0xfe, 0x75, 0x34, 0x08, 0xa9, 0xa1, 0xb2, - 0x55, 0xe9, 0x01, 0xc6, 0x67, 0x3f, 0x5d, 0xf9, 0x73, 0xed, 0xde, 0xab, - 0xaf, 0x9f, 0xe7, 0x5f, 0xc7, 0xeb, 0xf6, 0xf8, 0xf7, 0xe3, 0x88, 0xef, - 0xae, 0x75, 0x99, 0x7a, 0xdd, 0xcc, 0xe2, 0x80, 0xdf, 0x51, 0x56, 0xa8, - 0x7b, 0x36, 0x47, 0x78, 0x79, 0xe3, 0xd5, 0x72, 0x7b, 0xfb, 0x55, 0x7f, - 0x16, 0x51, 0xac, 0x2a, 0x51, 0x26, 0xea, 0xb8, 0xee, 0x1f, 0x25, 0xfc, - 0x0f, 0x2a, 0x87, 0x1d, 0x4c, 0x0f, 0xd3, 0x4f, 0xbf, 0x71, 0xba, 0xae, - 0x98, 0xf9, 0xe3, 0xd5, 0xcd, 0x5c, 0x60, 0xcf, 0x0b, 0xeb, 0xe9, 0xec, - 0xe5, 0xde, 0x15, 0xc6, 0x1a, 0xae, 0x74, 0xee, 0xba, 0xe6, 0x0d, 0x74, - 0x81, 0x33, 0x0c, 0xaf, 0x15, 0x5e, 0xeb, 0xa2, 0x22, 0x98, 0x02, 0x5b, - 0xff, 0x10, 0x84, 0x37, 0x28, 0xb5, 0xd7, 0xb2, 0x3f, 0x43, 0x93, 0x00, - 0x4b, 0x6e, 0x18, 0x9b, 0xb2, 0x86, 0x6f, 0xd4, 0x6f, 0x40, 0x1a, 0x1d, - 0x55, 0x12, 0x59, 0xa5, 0x90, 0x4c, 0x76, 0x93, 0xb1, 0x09, 0xb6, 0xc3, - 0x85, 0xd1, 0x08, 0xec, 0x2b, 0xb4, 0xd4, 0xc5, 0x10, 0x8c, 0xc7, 0xc4, - 0xc0, 0xc2, 0xdd, 0x78, 0xc3, 0x29, 0x37, 0x33, 0xba, 0xaa, 0x9c, 0xfb, - 0x9a, 0x71, 0x0f, 0x0f, 0x9f, 0xf9, 0x7d, 0xa8, 0x4a, 0x30, 0xa5, 0x9c, - 0x98, 0x7a, 0xdd, 0xc0, 0xda, 0xe7, 0xe3, 0xe3, 0xd4, 0x99, 0xb9, 0x8f, - 0xe7, 0xb1, 0x0c, 0xe9, 0x9f, 0x97, 0x88, 0x5a, 0x5b, 0x6e, 0xf0, 0xce, - 0xf1, 0xf3, 0xfe, 0xbd, 0xfa, 0x20, 0xef, 0xcb, 0x6f, 0x2c, 0xb8, 0x2b, - 0xbb, 0xbd, 0x4b, 0x9d, 0x69, 0xf0, 0x1a, 0xac, 0x75, 0xdf, 0x97, 0x3e, - 0xdd, 0xea, 0xba, 0xf9, 0xd7, 0xc7, 0xbf, 0xeb, 0xf6, 0xeb, 0xbf, 0xbe, - 0x56, 0x64, 0xce, 0x2a, 0x12, 0xae, 0xaf, 0x03, 0x01, 0x7c, 0xbc, 0x4f, - 0x45, 0x9b, 0xbb, 0xe0, 0xbd, 0xcb, 0x0b, 0xb1, 0xf0, 0xa5, 0x41, 0x92, - 0xc8, 0x15, 0x4d, 0x8c, 0x1d, 0xb0, 0x1a, 0x9e, 0x0b, 0xd9, 0x52, 0x45, - 0x33, 0x5a, 0x9c, 0x03, 0x10, 0x41, 0x66, 0xba, 0x24, 0x18, 0x0c, 0xb7, - 0x5d, 0xf7, 0x9a, 0x89, 0xc7, 0x9d, 0x59, 0x53, 0x14, 0xb3, 0x80, 0xff, - 0xf1, 0x50, 0x80, 0x26, 0x3f, 0xfc, 0x21, 0x1a, 0xcf, 0x7d, 0xbb, 0x5d, - 0x01, 0x40, 0xab, 0xa4, 0xac, 0xd1, 0xe3, 0x89, 0x17, 0x97, 0x32, 0xd7, - 0x5e, 0xdf, 0xa7, 0x5f, 0x7f, 0xcf, 0x9e, 0x7c, 0xf7, 0x75, 0xc5, 0x65, - 0xea, 0xb3, 0x76, 0xd7, 0x8e, 0x24, 0xb0, 0x3f, 0xb3, 0x6c, 0x13, 0xfa, - 0xb5, 0xce, 0x0c, 0xb9, 0x04, 0x63, 0x05, 0x22, 0xaa, 0xfc, 0xa7, 0x4c, - 0xc7, 0x71, 0x87, 0x0e, 0x3a, 0xe7, 0x7f, 0x18, 0x98, 0x97, 0x3f, 0x4d, - 0x3e, 0x76, 0xf2, 0xd9, 0xce, 0x63, 0x88, 0xff, 0xdc, 0x2a, 0x03, 0xbb, - 0xc7, 0x3d, 0x38, 0xd1, 0xdd, 0x2b, 0x77, 0x42, 0x2f, 0x72, 0x4a, 0xb8, - 0x8c, 0x2a, 0x07, 0x4c, 0x55, 0x8e, 0x52, 0x1d, 0xad, 0xd5, 0x8c, 0x4b, - 0xd8, 0x2c, 0x02, 0xd6, 0xf9, 0xcc, 0x08, 0x5b, 0x39, 0x1c, 0x4b, 0xb8, - 0x4e, 0x19, 0x58, 0xb3, 0xa9, 0xfd, 0xbb, 0x05, 0xb9, 0xcf, 0x5a, 0xcb, - 0xa3, 0xbb, 0x96, 0xb5, 0x37, 0x23, 0x24, 0x4a, 0xfc, 0x30, 0x0e, 0x59, - 0x69, 0x77, 0x37, 0xb5, 0xad, 0x48, 0x86, 0xab, 0x9c, 0x09, 0x11, 0x19, - 0xe6, 0x93, 0xe7, 0x8b, 0xe7, 0x7e, 0x02, 0xb6, 0x37, 0x16, 0x30, 0x69, - 0x5b, 0x23, 0x7c, 0x6e, 0xf5, 0x38, 0xe0, 0x09, 0x57, 0xcf, 0x91, 0x60, - 0x45, 0xfd, 0x3c, 0x49, 0x0e, 0xc1, 0xdd, 0xde, 0x1a, 0x16, 0x50, 0xcd, - 0x75, 0x72, 0xa1, 0x1d, 0x5b, 0x43, 0x9b, 0xb2, 0x9e, 0xf2, 0xbc, 0x29, - 0xcb, 0x80, 0xea, 0x3c, 0x5c, 0xd3, 0xbd, 0x7a, 0xff, 0x87, 0x4e, 0xf5, - 0x6e, 0x45, 0xbf, 0xc0, 0x79, 0xa8, 0xbc, 0xfb, 0xfa, 0xce, 0xb7, 0x75, - 0x3c, 0x75, 0x7b, 0xfc, 0x7c, 0xf9, 0xef, 0xef, 0xf3, 0x95, 0x2a, 0x4c, - 0xbd, 0xcd, 0x6f, 0x8c, 0x80, 0x54, 0xa7, 0xbb, 0xdf, 0x17, 0x9b, 0x7c, - 0x7d, 0x8a, 0xf6, 0xf7, 0x76, 0x70, 0x45, 0x95, 0xf4, 0xe7, 0x1a, 0xda, - 0xbc, 0x4a, 0x2f, 0xd4, 0x74, 0xcb, 0xc8, 0x51, 0xee, 0x03, 0xb9, 0x1a, - 0x93, 0xef, 0x93, 0xb6, 0x33, 0xf3, 0x66, 0xbb, 0xc9, 0x39, 0xc1, 0x0c, - 0xa9, 0x9f, 0x80, 0x57, 0x76, 0x03, 0x7d, 0x9f, 0x0e, 0xd9, 0x9c, 0x63, - 0x0b, 0xb8, 0x00, 0x1c, 0xff, 0xf1, 0x50, 0x80, 0x3e, 0x9f, 0xfc, 0x20, - 0xaa, 0x1a, 0xd4, 0x96, 0x72, 0x49, 0x84, 0x34, 0x4b, 0xdf, 0xb3, 0x9f, - 0xbf, 0xcf, 0x5d, 0xff, 0xa7, 0xf7, 0xeb, 0xcf, 0xcf, 0x15, 0xd7, 0x7a, - 0xdf, 0x9e, 0x5a, 0x54, 0xc9, 0x5a, 0xbe, 0x75, 0xc0, 0x5f, 0xfe, 0xad, - 0xf7, 0x60, 0xd3, 0x79, 0x33, 0x77, 0x71, 0xe2, 0x46, 0xcd, 0x02, 0x48, - 0xa5, 0x53, 0x1f, 0xe6, 0x9e, 0xeb, 0xaf, 0x6a, 0x0a, 0x3e, 0x93, 0x6b, - 0x4c, 0xc2, 0xfe, 0xe0, 0x0d, 0x01, 0x0f, 0xf8, 0x10, 0xfa, 0xd1, 0x8f, - 0xdc, 0x01, 0xff, 0xb6, 0x0a, 0xa6, 0x01, 0x30, 0x5a, 0x04, 0x2e, 0xea, - 0x48, 0xf7, 0xa1, 0xaf, 0x8f, 0x8c, 0xaf, 0x77, 0x97, 0xaa, 0x78, 0x03, - 0x55, 0xdb, 0x43, 0x05, 0x6f, 0x61, 0x7e, 0xbe, 0x7e, 0xf1, 0x6e, 0x94, - 0x14, 0x97, 0x5f, 0xc6, 0x3c, 0xfd, 0xbf, 0x10, 0x00, 0x77, 0xaf, 0x88, - 0x7d, 0x3e, 0xf8, 0x42, 0x0d, 0x81, 0x03, 0x87, 0x87, 0xad, 0x01, 0x83, - 0x04, 0xd3, 0xe7, 0x70, 0x82, 0x35, 0x47, 0xa7, 0xe9, 0x92, 0x31, 0xd6, - 0x60, 0x98, 0x6c, 0xca, 0x00, 0xa8, 0xa8, 0xef, 0xfe, 0x3c, 0x84, 0x86, - 0x07, 0x9f, 0x0f, 0x4f, 0xe1, 0x33, 0x4b, 0xc0, 0x09, 0xdb, 0xeb, 0xdc, - 0x49, 0xd2, 0xd0, 0x65, 0x56, 0x65, 0x8c, 0x7c, 0xef, 0x32, 0x22, 0x47, - 0x64, 0xb1, 0xc0, 0x02, 0x1a, 0x61, 0x29, 0xa1, 0x19, 0x76, 0xd6, 0xeb, - 0xfa, 0x5e, 0x91, 0xdf, 0xb5, 0xd7, 0xd5, 0xc0, 0x7f, 0x81, 0x9b, 0xcc, - 0x04, 0x43, 0x65, 0x13, 0x24, 0xbb, 0xfb, 0x04, 0x2c, 0xf8, 0x83, 0x7d, - 0x47, 0x48, 0xd7, 0x73, 0x16, 0x9d, 0x30, 0x04, 0x4a, 0x42, 0x8b, 0x6a, - 0xbb, 0x94, 0x3e, 0xe0, 0x96, 0x61, 0x28, 0x80, 0xc2, 0x65, 0x73, 0xc4, - 0x0e, 0xea, 0x72, 0xad, 0x63, 0x84, 0x33, 0x14, 0xac, 0x54, 0x50, 0x2f, - 0x8a, 0xdf, 0xd7, 0x79, 0xe7, 0xbe, 0x32, 0xfd, 0x7d, 0xdd, 0xfb, 0x77, - 0xac, 0xf2, 0xdf, 0x13, 0x8d, 0xce, 0x79, 0xe3, 0x77, 0x8d, 0x02, 0x47, - 0xb7, 0xc1, 0x1a, 0xf1, 0x86, 0x64, 0x97, 0xc1, 0xb3, 0xaf, 0xcf, 0x62, - 0xe6, 0x59, 0x38, 0xed, 0x63, 0x41, 0xdf, 0x2a, 0xdf, 0x3c, 0x29, 0x09, - 0x00, 0x7f, 0x9c, 0xa9, 0x27, 0x00, 0xfb, 0x04, 0x79, 0x8c, 0xdf, 0xd4, - 0x4d, 0x39, 0x2e, 0xe3, 0x17, 0xfe, 0xe8, 0x09, 0xaa, 0xa8, 0x01, 0x2e, - 0xb9, 0xca, 0x24, 0x48, 0xfa, 0x69, 0x10, 0x94, 0x6c, 0x15, 0x32, 0x44, - 0x86, 0x13, 0xfd, 0x48, 0x16, 0x8b, 0x4d, 0x57, 0xdd, 0xb9, 0x1b, 0xfd, - 0x52, 0x37, 0xb4, 0xca, 0x59, 0xde, 0xbd, 0x7f, 0x66, 0x31, 0xae, 0xad, - 0xc6, 0xe6, 0x8e, 0x56, 0x6b, 0xe1, 0x3b, 0x52, 0x4a, 0x4d, 0xc0, 0x9c, - 0x5f, 0xc2, 0x59, 0x0e, 0x00, 0x32, 0x4b, 0x98, 0x98, 0x28, 0x51, 0xc2, - 0x8b, 0x59, 0x2d, 0x47, 0x79, 0x15, 0x6f, 0xcf, 0x4a, 0xf3, 0x91, 0xa8, - 0xd8, 0xc0, 0x34, 0xd0, 0x08, 0x52, 0x2c, 0x9b, 0x9f, 0x10, 0x88, 0x03, - 0xc0, 0x1a, 0x70, 0xd6, 0xd4, 0x36, 0x9d, 0x4c, 0x60, 0xd8, 0x80, 0x93, - 0xc3, 0xb6, 0x00, 0xd7, 0xbb, 0x0c, 0xc8, 0x29, 0x39, 0xb6, 0x57, 0xca, - 0x2f, 0x7d, 0x12, 0x61, 0xb8, 0x0d, 0x42, 0x5c, 0x66, 0x90, 0xb2, 0x07, - 0xc6, 0x8c, 0x5f, 0x2b, 0x7c, 0x70, 0xa9, 0xe7, 0x93, 0x0d, 0xb4, 0x64, - 0xa4, 0x20, 0xa5, 0x83, 0x25, 0x18, 0xa6, 0x2c, 0x5d, 0xc3, 0xf3, 0x95, - 0xec, 0x7a, 0x4e, 0xef, 0x76, 0x01, 0x86, 0x6b, 0x9e, 0xd8, 0x7a, 0x3c, - 0x22, 0x35, 0x3f, 0x29, 0x3e, 0xe2, 0x76, 0xca, 0xff, 0xde, 0xef, 0x38, - 0xff, 0xf1, 0x50, 0x80, 0x36, 0xff, 0xfc, 0x20, 0xb0, 0x1a, 0xd0, 0xa4, - 0x2a, 0x8c, 0x92, 0x81, 0x13, 0x22, 0x47, 0x53, 0x3c, 0xf1, 0xf6, 0xfd, - 0xfb, 0xef, 0xae, 0xff, 0xc7, 0xf1, 0xfc, 0xff, 0x6f, 0x8d, 0xf2, 0xbf, - 0x3c, 0xf1, 0x5f, 0x5f, 0x6d, 0xde, 0x3e, 0x37, 0x7c, 0x49, 0x50, 0x69, - 0x86, 0x52, 0xe4, 0x83, 0x0d, 0x93, 0xf9, 0x03, 0x6c, 0x63, 0xc9, 0x47, - 0x44, 0x27, 0xde, 0x3b, 0xe7, 0x04, 0x26, 0x45, 0x77, 0xc7, 0x2f, 0xa5, - 0x67, 0x01, 0x86, 0x52, 0xe3, 0xb6, 0xfc, 0x37, 0xe8, 0xf5, 0x27, 0x0c, - 0x7d, 0x70, 0x3a, 0x0c, 0x9e, 0x77, 0x87, 0x7d, 0x07, 0xfa, 0xdd, 0xd9, - 0xe1, 0x45, 0xad, 0xcd, 0xd8, 0xf2, 0x93, 0xd5, 0xd8, 0x63, 0xb0, 0xca, - 0xbf, 0xb1, 0x31, 0xa3, 0xa6, 0x6c, 0x55, 0x51, 0x18, 0x7e, 0xca, 0x72, - 0x72, 0x83, 0x7e, 0x42, 0x87, 0xd7, 0x8f, 0x1d, 0xbd, 0xec, 0x46, 0xb2, - 0xd1, 0x28, 0xa7, 0x19, 0xed, 0xf9, 0x5a, 0xb3, 0x49, 0x9f, 0xbf, 0x2f, - 0xf0, 0x0a, 0x4f, 0xfe, 0xff, 0x6f, 0x64, 0x15, 0xd7, 0x43, 0xb8, 0x67, - 0x1b, 0x61, 0xee, 0x7c, 0xf6, 0x69, 0xbb, 0x78, 0x87, 0xdc, 0x3f, 0xa0, - 0x03, 0x0f, 0xc8, 0x1d, 0xa8, 0x29, 0x54, 0xe5, 0x94, 0xfe, 0x3e, 0x16, - 0x95, 0xaf, 0x5d, 0x72, 0xd5, 0x80, 0x37, 0x0e, 0xf0, 0xc7, 0x01, 0xdf, - 0xff, 0xcf, 0xff, 0x9f, 0xfd, 0x7c, 0x3f, 0x5f, 0xee, 0xf5, 0xcd, 0xfe, - 0x0c, 0x07, 0x48, 0x89, 0x6c, 0x0c, 0x3d, 0x1f, 0xd9, 0xfe, 0x1e, 0x5f, - 0xf5, 0xfb, 0xed, 0xff, 0x3e, 0x5f, 0xec, 0xe6, 0xf5, 0x70, 0x55, 0x67, - 0x4e, 0x6a, 0xa1, 0x31, 0xb1, 0xed, 0xce, 0x53, 0xc8, 0x25, 0xda, 0x16, - 0xf4, 0x07, 0x8f, 0x15, 0xb9, 0xa9, 0x3e, 0xbc, 0x5d, 0xf9, 0x66, 0x4f, - 0x6f, 0xf5, 0x7d, 0x78, 0xd6, 0x70, 0x35, 0xaf, 0xe3, 0xab, 0x9b, 0xfd, - 0x7b, 0x4c, 0xf3, 0xd2, 0x07, 0x7d, 0xd8, 0x10, 0x49, 0xfa, 0x12, 0x21, - 0xb3, 0xef, 0x71, 0xfc, 0xe6, 0x0a, 0x41, 0xb6, 0xe8, 0x32, 0xfd, 0x61, - 0x71, 0x25, 0x59, 0xbb, 0x9e, 0x79, 0xcc, 0x57, 0x93, 0xe9, 0x01, 0x1c, - 0xa7, 0x8d, 0x11, 0xf7, 0xf5, 0x05, 0x7f, 0x9f, 0x7d, 0xb8, 0x49, 0xc6, - 0xe0, 0xd5, 0x98, 0x22, 0xb3, 0x94, 0x00, 0x40, 0x7d, 0x19, 0x85, 0xbe, - 0x43, 0x00, 0x42, 0x73, 0xae, 0xa6, 0x9f, 0x1d, 0xba, 0x54, 0x7f, 0x4c, - 0xf8, 0xac, 0xf7, 0xa6, 0x36, 0xb6, 0x74, 0x4f, 0xcd, 0xbf, 0xa1, 0xe3, - 0x3c, 0xda, 0x13, 0x62, 0x05, 0x35, 0xd1, 0xe5, 0xcb, 0xb2, 0xa3, 0x68, - 0x8f, 0x1f, 0xc8, 0x52, 0xcf, 0xfb, 0x83, 0x03, 0x69, 0x73, 0xb8, 0x90, - 0x56, 0x60, 0xac, 0xa0, 0x87, 0x67, 0x4c, 0x9f, 0xad, 0xdd, 0xe0, 0x0a, - 0xff, 0xbb, 0x7e, 0x4a, 0x71, 0xd1, 0x7f, 0x57, 0x2f, 0xfb, 0x46, 0x70, - 0x94, 0xf6, 0xa8, 0x38, 0x88, 0x1d, 0xd3, 0x8d, 0x52, 0x29, 0xfb, 0x25, - 0x29, 0x12, 0xce, 0x6f, 0x6e, 0xef, 0xcc, 0x46, 0xdb, 0x2f, 0x51, 0xb2, - 0xc9, 0x5a, 0xf5, 0x11, 0xb2, 0xa0, 0x65, 0xb1, 0x6c, 0x5e, 0xcd, 0x31, - 0x37, 0xf7, 0x2d, 0xdd, 0xe4, 0xf3, 0x97, 0xff, 0xf1, 0x50, 0x80, 0x36, - 0xdf, 0xfc, 0x20, 0xab, 0x1a, 0xd6, 0x36, 0x3a, 0x28, 0xd4, 0x44, 0x44, - 0x30, 0x41, 0x27, 0xdf, 0xf4, 0xff, 0xbf, 0xf5, 0xf5, 0xfe, 0x3f, 0x4f, - 0x8f, 0x9f, 0xaf, 0xcf, 0xb4, 0xf7, 0x7c, 0x78, 0xd6, 0xa4, 0xac, 0x9e, - 0x7d, 0x6a, 0x66, 0xb5, 0xac, 0xbc, 0xf2, 0x14, 0x38, 0xf1, 0x33, 0x56, - 0x45, 0x83, 0x5f, 0x43, 0x3d, 0xe5, 0x57, 0x64, 0xb9, 0xda, 0xde, 0x6f, - 0xbd, 0xc5, 0xb8, 0xfd, 0x1f, 0xb8, 0xbd, 0x1e, 0x8d, 0x25, 0x47, 0x00, - 0xff, 0x73, 0xfb, 0xce, 0x72, 0x23, 0x82, 0x67, 0xbc, 0xf6, 0x08, 0xee, - 0xe1, 0xa3, 0x79, 0x78, 0x5b, 0x22, 0xf4, 0xd9, 0x68, 0xc6, 0x35, 0xcf, - 0xa2, 0xe1, 0x97, 0xab, 0xc3, 0x94, 0xd1, 0x68, 0x7d, 0xbd, 0xf2, 0xd6, - 0xbd, 0x7a, 0xf4, 0xcc, 0xd4, 0xdd, 0x3b, 0x47, 0x35, 0x25, 0x30, 0x58, - 0x5f, 0x9a, 0xf3, 0xaa, 0x29, 0xe4, 0x34, 0xad, 0xc1, 0xc0, 0x1c, 0xc7, - 0x03, 0x38, 0xd0, 0x5e, 0x90, 0x83, 0x85, 0xec, 0x67, 0x66, 0xc6, 0x2c, - 0xde, 0xda, 0x2a, 0x79, 0x24, 0x34, 0x35, 0xd4, 0x0b, 0x21, 0xfc, 0xb1, - 0xb6, 0x5d, 0xe4, 0x1f, 0x36, 0xf9, 0x2d, 0x61, 0xb9, 0x1c, 0xe5, 0xf3, - 0xdf, 0xd5, 0xc3, 0x36, 0x3a, 0x1c, 0xd0, 0x1b, 0x4f, 0x4f, 0x89, 0xa4, - 0xb8, 0x13, 0x09, 0x4f, 0x50, 0x03, 0x94, 0xf1, 0xab, 0x2e, 0xb3, 0xba, - 0xc3, 0x96, 0x95, 0x0c, 0xa6, 0x7b, 0x6b, 0x71, 0x79, 0x1c, 0xa2, 0xd3, - 0x9e, 0xf4, 0x0a, 0xb7, 0xad, 0x4a, 0x63, 0x25, 0x98, 0xa0, 0x8f, 0xc7, - 0xdb, 0xf9, 0xfd, 0xb8, 0x8a, 0xe3, 0xf3, 0x3c, 0xdf, 0xba, 0x29, 0x75, - 0xaa, 0xad, 0x57, 0x12, 0x46, 0xba, 0xcd, 0x40, 0x27, 0xd0, 0x26, 0x07, - 0xed, 0xe4, 0x93, 0x2e, 0x35, 0x10, 0xc1, 0x20, 0x34, 0x2b, 0xa7, 0x60, - 0x21, 0xa6, 0x90, 0x00, 0x1c, 0x6e, 0x34, 0x2d, 0x62, 0x16, 0xd8, 0x41, - 0x8b, 0xbd, 0x6a, 0xdd, 0x7b, 0x7d, 0x80, 0xbe, 0x13, 0xee, 0x77, 0x44, - 0x7f, 0x8c, 0x9f, 0x3e, 0xb7, 0x51, 0x60, 0xd5, 0x75, 0x78, 0x69, 0x9e, - 0xd0, 0x49, 0x5d, 0xc1, 0x25, 0xa4, 0xf7, 0x5e, 0x47, 0xc0, 0x63, 0xf7, - 0xd8, 0x56, 0x29, 0x55, 0xcd, 0x3b, 0xca, 0x75, 0xf1, 0xb1, 0x40, 0xb6, - 0x67, 0x54, 0xa4, 0x51, 0x60, 0x76, 0xc7, 0x77, 0xb4, 0x90, 0xc8, 0x82, - 0x9d, 0xdd, 0x6f, 0x2e, 0x0e, 0xab, 0x61, 0xad, 0xad, 0x41, 0x41, 0x50, - 0xb2, 0xee, 0x75, 0xc3, 0x4d, 0x56, 0x34, 0x4d, 0xe5, 0x7f, 0x51, 0x3e, - 0x76, 0x0b, 0x7c, 0xd0, 0x2d, 0x89, 0x41, 0x54, 0x15, 0x3d, 0x9a, 0xa8, - 0x6f, 0x57, 0x11, 0xda, 0x80, 0x99, 0x6b, 0xad, 0x1d, 0xf2, 0xad, 0xb1, - 0xa4, 0x51, 0x0a, 0xbb, 0xea, 0xaf, 0x00, 0x05, 0x41, 0xbf, 0xae, 0xb4, - 0x46, 0x34, 0x3d, 0xfb, 0xc7, 0xe1, 0x44, 0x92, 0x70, 0x5d, 0x4d, 0x6d, - 0xb8, 0xcc, 0xc8, 0xd4, 0xfb, 0x89, 0xb1, 0x6c, 0x40, 0xb5, 0xce, 0x55, - 0xaa, 0x3e, 0x3e, 0x16, 0xe1, 0xdd, 0x5d, 0x72, 0x6e, 0xd5, 0x64, 0xd1, - 0x91, 0xd2, 0xfe, 0x67, 0xd7, 0xc0, 0x69, 0xef, 0xdb, 0xe1, 0x89, 0x59, - 0x1c, 0xff, 0xf1, 0x50, 0x80, 0x26, 0x5f, 0xfc, 0x21, 0x1a, 0xcf, 0x99, - 0x6b, 0x24, 0x04, 0x06, 0xab, 0xa1, 0xb3, 0x0d, 0x88, 0xd5, 0x18, 0x91, - 0x5f, 0x1e, 0xff, 0x8c, 0xf7, 0xf3, 0xbf, 0xbf, 0xcb, 0x77, 0xd7, 0xa9, - 0xc6, 0x57, 0x5c, 0xf9, 0x92, 0xb5, 0x53, 0x7d, 0x6f, 0x5b, 0xf6, 0x50, - 0x0d, 0xad, 0x5c, 0x1e, 0xf5, 0x5d, 0xe3, 0xb1, 0x37, 0x06, 0xa2, 0x1c, - 0xbf, 0x15, 0xdb, 0xa7, 0x34, 0xe2, 0xe6, 0x60, 0x16, 0xd8, 0xb2, 0xba, - 0x81, 0xda, 0x09, 0x84, 0xed, 0xe8, 0x27, 0xc1, 0x28, 0x7a, 0xe8, 0x27, - 0xd5, 0x71, 0xf4, 0x47, 0xb2, 0x73, 0x91, 0xf6, 0xf3, 0x9e, 0x7e, 0x5c, - 0x5a, 0x59, 0x31, 0xa3, 0x49, 0xc2, 0xb7, 0xd5, 0x9c, 0xfa, 0xf8, 0xde, - 0xe2, 0xd0, 0xb4, 0x8f, 0x85, 0x95, 0xb0, 0x14, 0x0f, 0x5b, 0xad, 0xa9, - 0x7b, 0x0e, 0x81, 0x62, 0xe8, 0xe3, 0x42, 0x9f, 0x29, 0xaf, 0x39, 0x70, - 0x9c, 0x70, 0x5d, 0x50, 0x24, 0x5a, 0x98, 0xeb, 0xf7, 0xa4, 0xb0, 0x84, - 0xd2, 0xbb, 0xd9, 0x6c, 0x3f, 0x95, 0x86, 0x5e, 0xe4, 0xf4, 0xf6, 0x2e, - 0xae, 0xcb, 0x58, 0xbc, 0xe8, 0xac, 0xf4, 0xba, 0x71, 0x95, 0x6f, 0x34, - 0x27, 0xf6, 0xfa, 0xaa, 0xf3, 0xf8, 0x0e, 0xf7, 0xf3, 0xbe, 0xd9, 0xff, - 0xe2, 0xed, 0xed, 0x18, 0x31, 0x8d, 0x03, 0xb7, 0x7c, 0xe9, 0xed, 0xcd, - 0x3f, 0xa1, 0xca, 0xf4, 0xd7, 0xfa, 0xf8, 0xeb, 0x1e, 0xc8, 0x5b, 0x07, - 0x1d, 0x75, 0x6e, 0x35, 0xfc, 0x25, 0x5c, 0xe2, 0x65, 0xbc, 0x45, 0x7b, - 0x65, 0xba, 0xf5, 0xe2, 0x31, 0xda, 0xaf, 0xe8, 0xb8, 0xeb, 0xac, 0x1a, - 0xab, 0x71, 0x2e, 0x3e, 0x22, 0x3e, 0x3d, 0xfe, 0x3b, 0xf6, 0xae, 0x6f, - 0x77, 0x5c, 0xcb, 0xac, 0xbc, 0x97, 0x92, 0xeb, 0x8a, 0xe6, 0xb5, 0xcf, - 0x0d, 0xdd, 0x4a, 0x80, 0xd4, 0xef, 0x6a, 0x13, 0xe4, 0xef, 0xb6, 0x7a, - 0x6d, 0xdb, 0x5f, 0x57, 0x24, 0xdc, 0xe3, 0xaa, 0x8a, 0x1c, 0x0f, 0x99, - 0x92, 0xba, 0xea, 0x34, 0x34, 0x75, 0x48, 0xab, 0xdc, 0x5a, 0x66, 0xfa, - 0x25, 0x78, 0x71, 0x82, 0x87, 0x23, 0x9c, 0xcd, 0x2a, 0x21, 0x58, 0xc9, - 0x72, 0x42, 0xa0, 0x26, 0xca, 0x71, 0x38, 0xff, 0xf1, 0x50, 0x80, 0x29, - 0xbf, 0xfc, 0x21, 0x1a, 0xcf, 0xf9, 0xe0, 0x7a, 0x00, 0x80, 0xa7, 0xb1, - 0xc2, 0x56, 0x88, 0x45, 0x0b, 0x18, 0x12, 0xa6, 0xfe, 0xb7, 0xce, 0xaf, - 0xaf, 0x9f, 0x8f, 0x9d, 0x71, 0xea, 0xe1, 0x35, 0xae, 0xb9, 0xbb, 0x56, - 0xe3, 0xd9, 0xbe, 0x39, 0xe0, 0x3d, 0xcf, 0xa3, 0xa5, 0xe4, 0x63, 0xb6, - 0xa8, 0x34, 0x84, 0xd8, 0x08, 0xb5, 0x93, 0x93, 0x1c, 0x50, 0x56, 0x90, - 0x71, 0x18, 0xca, 0x72, 0x81, 0xa1, 0xeb, 0xd3, 0x0e, 0xaf, 0xba, 0x9d, - 0x9d, 0x39, 0xe3, 0xa2, 0x38, 0xbb, 0x25, 0x3e, 0x30, 0x98, 0x20, 0xba, - 0x39, 0x2f, 0xe7, 0x0b, 0x94, 0xd4, 0x2f, 0x2b, 0xd6, 0x1e, 0x44, 0x8a, - 0x56, 0xad, 0x55, 0x6a, 0xe8, 0x02, 0xd6, 0x14, 0xbe, 0xba, 0x10, 0xb0, - 0x62, 0x2e, 0x7a, 0xae, 0xca, 0x6a, 0x28, 0xb7, 0xb2, 0x2b, 0x79, 0xb9, - 0xf6, 0x99, 0xdf, 0x7a, 0x11, 0x1d, 0xee, 0xa4, 0x08, 0xc2, 0x4f, 0x8f, - 0x1d, 0x0c, 0x72, 0x77, 0x53, 0x57, 0xdb, 0xce, 0x78, 0xc7, 0x38, 0x99, - 0xf5, 0x6f, 0xe1, 0x5e, 0xe2, 0x18, 0x03, 0xbf, 0x07, 0x2e, 0x63, 0xf6, - 0xc4, 0x16, 0xcf, 0x6c, 0x9b, 0xc6, 0xd1, 0x74, 0x08, 0x32, 0xc7, 0x55, - 0x3b, 0x3e, 0xda, 0x02, 0x0b, 0x16, 0x59, 0xf2, 0xef, 0x17, 0x28, 0xad, - 0xd7, 0x26, 0x0b, 0x98, 0x63, 0x1b, 0x12, 0xc9, 0x04, 0x49, 0x2a, 0x89, - 0xef, 0x3c, 0x4e, 0x18, 0x08, 0x80, 0x4f, 0xe0, 0xc0, 0x00, 0x2c, 0x44, - 0x93, 0xd2, 0x35, 0xdc, 0xe5, 0x16, 0x54, 0xe6, 0x68, 0xa0, 0xa6, 0xa6, - 0xa0, 0x8c, 0x35, 0xcd, 0xf4, 0xf2, 0x04, 0x94, 0xdd, 0x08, 0x07, 0x15, - 0x94, 0x01, 0x34, 0x84, 0x01, 0x7d, 0x3f, 0xb4, 0xdb, 0x37, 0xca, 0x14, - 0x29, 0x19, 0x53, 0xc0, 0x97, 0x3f, 0x00, 0x4a, 0x93, 0xad, 0xf3, 0xab, - 0xdc, 0xda, 0x52, 0xe3, 0x7c, 0x5d, 0xdd, 0x5f, 0x85, 0xf7, 0xc6, 0xb7, - 0xed, 0xf6, 0xbb, 0xb0, 0xec, 0x31, 0xb5, 0xf7, 0xc3, 0x53, 0x15, 0x8b, - 0x7e, 0xbd, 0x47, 0x1b, 0x7a, 0xfb, 0xe0, 0x2b, 0xbc, 0x82, 0xf2, 0x77, - 0x4e, 0x73, 0x2e, 0x3f, 0x34, 0x77, 0xe9, 0x5f, 0x9a, 0xf3, 0x30, 0xbb, - 0x71, 0xa5, 0x4a, 0xb7, 0x05, 0x38, 0xe8, 0xa8, 0x24, 0xa8, 0x66, 0x44, - 0xbb, 0x76, 0xbb, 0xec, 0xac, 0x96, 0x82, 0xab, 0x21, 0xcd, 0x7b, 0xba, - 0x5e, 0xf0, 0x88, 0x87, 0xff, 0xf1, 0x50, 0x80, 0x3d, 0x3f, 0xfc, 0x20, - 0xab, 0x1a, 0xd4, 0xa5, 0xa3, 0x0d, 0x0e, 0xc3, 0x17, 0x2f, 0xbe, 0x33, - 0xfe, 0x7f, 0xaf, 0xaf, 0x6d, 0xfc, 0x7a, 0xf8, 0xfb, 0x6b, 0xe3, 0xed, - 0x75, 0xc6, 0x7b, 0x78, 0xf6, 0xf6, 0xf7, 0xf2, 0xbe, 0xea, 0x6a, 0xf6, - 0xd0, 0x00, 0xe8, 0x0f, 0xfa, 0x46, 0x9b, 0x2d, 0x46, 0x52, 0x74, 0x4d, - 0x22, 0x75, 0x01, 0x44, 0x4c, 0x77, 0x04, 0xdf, 0x71, 0x03, 0x3f, 0xd3, - 0x39, 0x9d, 0x31, 0xe2, 0x9e, 0xdd, 0x6f, 0xce, 0x09, 0xa9, 0xf2, 0x23, - 0xee, 0x97, 0x11, 0x2b, 0xcf, 0x6b, 0xaf, 0x91, 0xe9, 0x09, 0xa8, 0x2e, - 0x22, 0xf2, 0x77, 0xf0, 0x60, 0x1c, 0x59, 0x92, 0x0e, 0x10, 0x86, 0xf3, - 0xdc, 0x89, 0xcc, 0xb4, 0x76, 0xd8, 0x00, 0x98, 0x3d, 0x31, 0x40, 0x6a, - 0xd9, 0xbf, 0xbb, 0xb3, 0x5b, 0xed, 0xe8, 0xbc, 0x7b, 0xd7, 0x23, 0x06, - 0x2f, 0x9f, 0x08, 0x20, 0xa2, 0x66, 0xa4, 0x19, 0x4e, 0xea, 0x59, 0xf3, - 0x82, 0x03, 0x07, 0x51, 0x00, 0xb8, 0x05, 0xe5, 0xd5, 0x50, 0x8b, 0x46, - 0x8a, 0xb1, 0x8a, 0xa6, 0x54, 0x30, 0xba, 0x22, 0x16, 0x34, 0x5f, 0x78, - 0x60, 0x5d, 0x7f, 0x8c, 0x0d, 0x62, 0x9d, 0xdb, 0x14, 0xfd, 0xaf, 0x51, - 0x53, 0xd5, 0x85, 0xd0, 0x70, 0x9e, 0x73, 0xf0, 0xb0, 0xaa, 0x6a, 0xe3, - 0xff, 0xb9, 0x28, 0x15, 0xc5, 0x75, 0x56, 0xfd, 0xd9, 0x1a, 0xff, 0x6d, - 0xdf, 0x35, 0x26, 0xa5, 0x3f, 0xb4, 0x8a, 0xd1, 0xac, 0xf9, 0x1b, 0xaa, - 0x3a, 0x5c, 0xd2, 0x6d, 0x36, 0xbb, 0xf5, 0xd5, 0x13, 0xaa, 0x51, 0xa8, - 0x99, 0x24, 0x49, 0x1e, 0x43, 0x36, 0xeb, 0x32, 0x24, 0xd6, 0x45, 0x9b, - 0x08, 0xdb, 0x48, 0x5a, 0x64, 0xaf, 0x6e, 0x02, 0x56, 0xb1, 0xd3, 0x52, - 0x55, 0xa8, 0x6c, 0x95, 0x83, 0x29, 0x42, 0xc4, 0x00, 0x9e, 0x7c, 0x55, - 0xcc, 0x53, 0x8d, 0x95, 0x0f, 0x3c, 0x7b, 0x77, 0x3e, 0xb7, 0x5a, 0xed, - 0x7e, 0xbf, 0x1c, 0xea, 0x0b, 0x07, 0xac, 0x56, 0xb5, 0xda, 0x2e, 0x16, - 0x6a, 0xae, 0x79, 0x44, 0x7d, 0xca, 0x2b, 0x41, 0xd9, 0x84, 0x40, 0x08, - 0x59, 0x5c, 0x44, 0x41, 0xd2, 0x60, 0xd6, 0xce, 0xca, 0x6f, 0x84, 0x47, - 0x84, 0xce, 0xe6, 0x43, 0x9c, 0xa8, 0xbd, 0xc2, 0xc6, 0x25, 0xcc, 0xb9, - 0x2d, 0x50, 0x06, 0x66, 0x17, 0x4a, 0xb5, 0xcc, 0xd8, 0x05, 0x12, 0xb6, - 0xf1, 0x95, 0xa8, 0xcc, 0xdf, 0x7e, 0x56, 0x56, 0x74, 0x4e, 0x21, 0x79, - 0xcc, 0x94, 0xcd, 0x28, 0x53, 0x3c, 0x81, 0x53, 0x50, 0x26, 0x24, 0xea, - 0x0a, 0x8e, 0x08, 0x55, 0x42, 0x9b, 0xde, 0x8a, 0xc2, 0x2e, 0xc0, 0xd6, - 0x20, 0xb5, 0x52, 0xfe, 0x05, 0x78, 0x3e, 0xeb, 0xd7, 0x7d, 0x6f, 0x7d, - 0xd5, 0x4c, 0x05, 0x39, 0xea, 0xb0, 0xc3, 0x34, 0xdd, 0x65, 0x26, 0xec, - 0x7c, 0x1a, 0x63, 0x1b, 0x63, 0xdd, 0x17, 0xf7, 0x81, 0xdb, 0x14, 0xe8, - 0x91, 0xb5, 0x61, 0xd5, 0x3d, 0xba, 0xad, 0x06, 0xb1, 0x59, 0x44, 0x0c, - 0x9c, 0x4a, 0xa9, 0x78, 0xee, 0x41, 0xe2, 0xf6, 0x33, 0x82, 0x0c, 0x04, - 0xa6, 0x64, 0x41, 0x1d, 0xd2, 0x2a, 0x3e, 0x58, 0xcc, 0x86, 0x46, 0x05, - 0xef, 0xea, 0xcd, 0x64, 0x3a, 0x8f, 0x3d, 0x94, 0xcb, 0xac, 0xe5, 0x97, - 0x8f, 0x60, 0x5d, 0xa8, 0xb1, 0x49, 0x61, 0xd6, 0x52, 0xae, 0x29, 0x6c, - 0x12, 0xf9, 0x73, 0x4e, 0xbc, 0x1d, 0xcc, 0x99, 0x2e, 0xb3, 0xa0, 0xf7, - 0xe1, 0x5e, 0x01, 0xce, 0xe7, 0xd7, 0x59, 0xe8, 0xb8, 0xbc, 0xec, 0x78, - 0xf0, 0xff, 0xf1, 0x50, 0x80, 0x38, 0xdf, 0xfc, 0x20, 0xa7, 0x1a, 0xd6, - 0x96, 0x4a, 0x59, 0x86, 0x00, 0xf3, 0x7e, 0xe7, 0xb7, 0xaf, 0x8f, 0x7b, - 0xf6, 0xf9, 0x03, 0xdb, 0xeb, 0xe5, 0x27, 0x72, 0x5f, 0x1a, 0xa9, 0xed, - 0x41, 0x15, 0xd4, 0x0c, 0xc1, 0xc9, 0x77, 0x14, 0x4f, 0x1a, 0xaa, 0xb0, - 0x31, 0xd1, 0xc2, 0x42, 0x9a, 0x16, 0x89, 0xf7, 0x39, 0xb8, 0xe6, 0xa2, - 0x1c, 0xbf, 0x76, 0x08, 0x40, 0x3c, 0x8a, 0x28, 0x43, 0xbd, 0x0d, 0x73, - 0x35, 0x10, 0x05, 0xfe, 0x40, 0xe4, 0x30, 0x23, 0xcc, 0xf9, 0xc1, 0x1b, - 0x77, 0xd1, 0xe1, 0xfa, 0x7c, 0xbe, 0xee, 0xeb, 0x31, 0x4b, 0x50, 0x50, - 0x10, 0xf9, 0xc6, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x5e, - 0xc6, 0xdc, 0x00, 0x00, 0x02, 0x40, 0x00, 0x00, 0x17, 0x7b, 0x4f, 0xfe, - 0x3c, 0x57, 0xdb, 0xdb, 0x0e, 0xfb, 0x17, 0xff, 0x85, 0x20, 0x00, 0xa0, - 0x00, 0x00, 0xf6, 0x87, 0x9f, 0x5a, 0xdf, 0x04, 0x84, 0x0c, 0x50, 0x46, - 0x21, 0x42, 0x10, 0x6c, 0xe4, 0x4f, 0xa3, 0x7c, 0x98, 0x31, 0xa4, 0x00, - 0x00, 0x14, 0xee, 0xee, 0xfb, 0x9d, 0xae, 0xf6, 0x5c, 0x62, 0xcb, 0x6d, - 0x6c, 0x7d, 0xad, 0xb9, 0x84, 0x06, 0x00, 0x04, 0x3b, 0x9f, 0xcf, 0x0a, - 0xec, 0x70, 0x1e, 0xcf, 0xb4, 0x00, 0x50, 0xeb, 0x02, 0x02, 0xee, 0xf0, - 0x1f, 0x30, 0xda, 0xbe, 0x01, 0x67, 0x27, 0x68, 0xf7, 0xf3, 0xeb, 0x48, - 0x9e, 0xfb, 0x21, 0x8b, 0x24, 0x91, 0x56, 0xa8, 0xb8, 0x5c, 0x6a, 0xb7, - 0x21, 0x03, 0xb0, 0x74, 0xd9, 0x9f, 0x66, 0xe7, 0xa7, 0xc7, 0xbb, 0x51, - 0x12, 0xa9, 0xe4, 0xb5, 0xea, 0xef, 0xe6, 0x0f, 0x3d, 0x1a, 0x66, 0x9a, - 0x28, 0xc5, 0x85, 0x82, 0xda, 0x2d, 0x96, 0xf1, 0xb2, 0x45, 0x40, 0x0f, - 0x8f, 0x7f, 0x8d, 0xfd, 0x7c, 0x32, 0xeb, 0xfe, 0x8f, 0x35, 0x39, 0xfa, - 0xa9, 0xde, 0x9f, 0x29, 0x57, 0x79, 0xc6, 0xef, 0xae, 0x07, 0x97, 0x8b, - 0x26, 0x3b, 0x07, 0x5d, 0x1b, 0xc2, 0xfb, 0xde, 0x14, 0xa1, 0x56, 0xdc, - 0x47, 0x5d, 0xf3, 0x13, 0xc2, 0x44, 0x1a, 0x07, 0xbf, 0x8a, 0x72, 0x59, - 0x2b, 0xd9, 0xd6, 0xd4, 0x40, 0x9b, 0x4e, 0xb8, 0x63, 0x71, 0x20, 0x09, - 0x5a, 0x3d, 0x8c, 0x49, 0xe3, 0x6d, 0xc7, 0xcf, 0x8a, 0x49, 0x12, 0xe3, - 0x5e, 0xc7, 0x66, 0x68, 0x4a, 0x22, 0x2d, 0xd2, 0x2f, 0xd9, 0x4c, 0xdb, - 0x88, 0x93, 0xc0, 0x00, 0x5c, 0x41, 0x45, 0xb1, 0x6e, 0x27, 0x16, 0xd9, - 0x25, 0xce, 0x5d, 0x54, 0x4c, 0xb1, 0x64, 0x28, 0x0f, 0xdf, 0x71, 0x89, - 0x15, 0x3e, 0x3c, 0x46, 0xd2, 0xa8, 0x96, 0x1d, 0x9c, 0xca, 0xe9, 0xab, - 0xc6, 0xbb, 0xac, 0xec, 0xcd, 0xd0, 0x8b, 0x0f, 0x0b, 0x88, 0xf2, 0xe9, - 0xaf, 0x51, 0x9d, 0xab, 0x39, 0x1b, 0xd2, 0x52, 0x63, 0x5c, 0xad, 0x18, - 0x5f, 0x95, 0x53, 0x56, 0x2d, 0xd5, 0xd3, 0xdb, 0xc8, 0xf5, 0x26, 0x55, - 0x1b, 0x4b, 0x30, 0x39, 0x5c, 0x08, 0xc8, 0xee, 0x59, 0xdd, 0x4e, 0x66, - 0xdc, 0x39, 0x1d, 0x4d, 0x94, 0xa7, 0xd6, 0x29, 0xbb, 0xf9, 0x12, 0xea, - 0x1b, 0xea, 0x56, 0xdc, 0x1e, 0x38, 0x13, 0xf6, 0x77, 0xd6, 0x35, 0xb9, - 0x77, 0x67, 0x7a, 0xd3, 0xcb, 0xc3, 0xd5, 0xdf, 0x5d, 0x29, 0xe0, 0xff, - 0xf1, 0x50, 0x80, 0x33, 0x3f, 0xfc, 0x20, 0xaa, 0x1a, 0xd4, 0x28, 0x4a, - 0xa8, 0x9a, 0x83, 0x63, 0x00, 0xfb, 0xfd, 0xbf, 0xdf, 0x9f, 0xeb, 0xad, - 0xfb, 0x7a, 0xfb, 0xfd, 0xb5, 0xc7, 0xa0, 0x5f, 0x52, 0x54, 0xbe, 0xfc, - 0xc7, 0xc6, 0x33, 0xa8, 0x03, 0xa8, 0xf7, 0xce, 0x81, 0x8d, 0x6c, 0x15, - 0x14, 0x21, 0xac, 0xdc, 0x98, 0x6f, 0x43, 0x1a, 0xce, 0x99, 0x51, 0xd1, - 0x10, 0xf6, 0xd6, 0xea, 0x2a, 0xe9, 0x3b, 0xdd, 0x71, 0x1e, 0xc9, 0x40, - 0xf8, 0x4a, 0xee, 0xee, 0xdd, 0xe6, 0x15, 0x0e, 0x64, 0xb4, 0xbe, 0xdc, - 0x5a, 0xc6, 0x53, 0x1e, 0x9c, 0xd9, 0xbc, 0x43, 0x1f, 0xb4, 0x0b, 0x0a, - 0x1b, 0x1d, 0xdd, 0x09, 0x07, 0x54, 0x5a, 0xf2, 0x75, 0x00, 0x05, 0xf6, - 0x2b, 0xd3, 0xb6, 0x5f, 0x71, 0x10, 0xe1, 0x4f, 0xc1, 0x80, 0x09, 0xed, - 0x77, 0x46, 0x8f, 0x43, 0x3e, 0xd7, 0x8e, 0xc7, 0xad, 0xf3, 0x9d, 0x4d, - 0x63, 0x0e, 0x74, 0x80, 0x0f, 0xf1, 0xc0, 0x23, 0xa6, 0xa2, 0x69, 0xd1, - 0x43, 0x89, 0x4b, 0x9d, 0x19, 0xce, 0xdb, 0xdd, 0x28, 0xbf, 0x4b, 0x94, - 0x45, 0x38, 0x92, 0xf2, 0x99, 0xc8, 0xc6, 0x7a, 0xb0, 0x6e, 0x29, 0xd7, - 0x94, 0x60, 0x18, 0xd2, 0x6f, 0x26, 0xe9, 0x2d, 0x77, 0x44, 0x79, 0x8d, - 0x31, 0xa5, 0x13, 0x1a, 0x69, 0x73, 0x06, 0x33, 0x0b, 0x64, 0x93, 0x07, - 0x2a, 0xeb, 0x78, 0x66, 0x67, 0xb4, 0x54, 0x8e, 0xcc, 0x64, 0xc3, 0x49, - 0xb6, 0x3d, 0x78, 0x98, 0xab, 0x2d, 0x39, 0x7c, 0xe4, 0xc3, 0x0e, 0x41, - 0x04, 0x20, 0xd6, 0xd4, 0x56, 0x54, 0xbd, 0x6b, 0x1b, 0x25, 0x16, 0x2e, - 0x63, 0x03, 0x5b, 0x3f, 0xd3, 0xfa, 0x1e, 0x77, 0xb0, 0x03, 0x8c, 0xcb, - 0x8c, 0xbd, 0xf5, 0x3f, 0x5e, 0xf5, 0x9d, 0x70, 0x0b, 0x65, 0xe9, 0x1d, - 0x19, 0x31, 0x64, 0xbb, 0x8e, 0x6a, 0x49, 0xea, 0xd5, 0x30, 0x56, 0x73, - 0x78, 0xd4, 0x84, 0xe0, 0x1b, 0xbe, 0x57, 0x05, 0x7a, 0x35, 0x4c, 0x5d, - 0x0c, 0xb1, 0x75, 0x5a, 0xfd, 0x32, 0x95, 0x64, 0x69, 0xef, 0x63, 0x3f, - 0xe7, 0x61, 0x09, 0xc1, 0x5d, 0x2e, 0x71, 0xd1, 0x3b, 0xc2, 0x78, 0xd7, - 0xc1, 0xfc, 0x3d, 0x37, 0xa3, 0x94, 0xf0, 0xe2, 0xbe, 0xc4, 0xdf, 0xc2, - 0x3c, 0x6e, 0xc6, 0x22, 0x26, 0xf1, 0x79, 0x81, 0x64, 0x7e, 0x07, 0x8d, - 0x42, 0xb8, 0xb6, 0x15, 0xb8, 0x8e, 0x24, 0xd8, 0xb0, 0xe3, 0x18, 0xf7, - 0xc6, 0x92, 0x80, 0x45, 0x91, 0x81, 0xe6, 0xff, 0x4c, 0x66, 0x5f, 0x24, - 0x71, 0xc8, 0xe1, 0x15, 0xbc, 0x93, 0x44, 0x8e, 0x94, 0xe3, 0x8b, 0x39, - 0xd0, 0xe0, 0xc8, 0x4e, 0x48, 0x0d, 0x4b, 0x04, 0x31, 0xbd, 0x6c, 0x90, - 0xe6, 0x85, 0x25, 0x38, 0xab, 0x35, 0x62, 0x60, 0x02, 0x2c, 0x6d, 0x88, - 0xd0, 0xa3, 0x81, 0x48, 0x2f, 0x86, 0x12, 0x2d, 0x2d, 0x09, 0x89, 0x4d, - 0x44, 0xbf, 0x6e, 0xed, 0xd5, 0xd6, 0x8d, 0x0b, 0x6d, 0x94, 0x5f, 0xc0, - 0xff, 0xf1, 0x50, 0x80, 0x25, 0xbf, 0xfc, 0x21, 0x1a, 0xcd, 0xfd, 0xff, - 0xfe, 0x6c, 0x23, 0xa5, 0xb1, 0x42, 0x98, 0xc8, 0x91, 0x32, 0x11, 0x88, - 0xe2, 0x54, 0x35, 0xb2, 0xa0, 0x00, 0x0a, 0x2e, 0xda, 0xf3, 0xdf, 0x9c, - 0xf3, 0x4d, 0x34, 0x14, 0xe0, 0xbe, 0x1b, 0x4b, 0xd2, 0xf8, 0xa6, 0x55, - 0x84, 0x82, 0xa0, 0x06, 0xa1, 0xa0, 0x75, 0xd8, 0x02, 0x65, 0x3c, 0x15, - 0x2c, 0xe9, 0x6b, 0x42, 0x4b, 0x46, 0xf5, 0xed, 0x88, 0x1b, 0xc2, 0xe5, - 0x24, 0x51, 0x5a, 0xd0, 0x03, 0xf8, 0x2b, 0x40, 0x06, 0xd3, 0xf6, 0x0b, - 0x1a, 0x1b, 0xe1, 0xb7, 0xcc, 0xf8, 0x21, 0x3a, 0x61, 0x36, 0xbe, 0x36, - 0x45, 0x1f, 0x21, 0xdf, 0x07, 0x3e, 0x30, 0x9e, 0x7a, 0x41, 0x95, 0xe9, - 0x46, 0xf7, 0xcc, 0x0c, 0x82, 0xf0, 0x35, 0x19, 0x75, 0xb6, 0xcd, 0xfb, - 0xeb, 0x28, 0xd1, 0x9c, 0xec, 0xb3, 0x9d, 0x60, 0x27, 0x25, 0x3f, 0xad, - 0x75, 0x47, 0x55, 0xa6, 0x47, 0xd9, 0xf7, 0xaf, 0x36, 0x06, 0xae, 0xe7, - 0x9f, 0xf0, 0x10, 0x3d, 0x54, 0x68, 0x74, 0x23, 0xe4, 0xf1, 0x3d, 0xb6, - 0x72, 0x7d, 0x64, 0x64, 0x59, 0x51, 0xdd, 0x71, 0x59, 0x3e, 0x50, 0xcf, - 0xa6, 0xa0, 0xcb, 0x8f, 0x9b, 0x78, 0x62, 0x00, 0x50, 0xd1, 0x2d, 0xc3, - 0x2a, 0x19, 0xbc, 0xf2, 0x45, 0x24, 0x01, 0xdd, 0x03, 0x65, 0x44, 0xa8, - 0x68, 0x00, 0x26, 0xd8, 0xd1, 0x5c, 0x48, 0xf2, 0x06, 0x6a, 0xcc, 0x2e, - 0x8c, 0xfd, 0x73, 0x4d, 0x8a, 0x07, 0xc3, 0x45, 0x92, 0x2e, 0x68, 0xb7, - 0x47, 0x27, 0xb6, 0xca, 0xfd, 0x5f, 0x46, 0x0a, 0xdd, 0xa0, 0xa8, 0x99, - 0x9e, 0x04, 0x52, 0xd8, 0xd6, 0x44, 0x33, 0x17, 0xc4, 0x1a, 0xd8, 0x00, - 0x00, 0xca, 0x5e, 0x49, 0x37, 0xd6, 0xd7, 0x53, 0x00, 0xa6, 0x0c, 0xd3, - 0x6b, 0x0d, 0x92, 0xce, 0xf7, 0x9b, 0x8a, 0xdf, 0xe7, 0xe8, 0x7a, 0xfb, - 0xaa, 0xf9, 0x2a, 0xa6, 0xe3, 0xfc, 0x97, 0x46, 0x2d, 0x76, 0xf3, 0xd3, - 0x7c, 0x88, 0x55, 0x43, 0x38, 0x12, 0x46, 0xec, 0x3a, 0xca, 0xab, 0x22, - 0x56, 0x99, 0xf6, 0x79, 0xf7, 0x7f, 0xd7, 0xbc, 0x90, 0xf3, 0x02, 0x08, - 0x1c, 0xff, 0xf1, 0x50, 0x80, 0x27, 0x1f, 0xfc, 0x21, 0x1a, 0xcf, 0x7d, - 0xef, 0xff, 0x40, 0x00, 0xa3, 0xb1, 0x51, 0xd8, 0xce, 0x35, 0x5b, 0x15, - 0x46, 0xc5, 0x50, 0x80, 0x00, 0x00, 0x2f, 0x35, 0xe6, 0xee, 0x70, 0xfc, - 0xe9, 0xf8, 0xdd, 0xc0, 0x0b, 0x47, 0x60, 0xe0, 0xfb, 0x24, 0x87, 0x98, - 0xbf, 0x2a, 0x54, 0xbc, 0x7a, 0xec, 0xa3, 0x23, 0x92, 0x4e, 0xa3, 0x9d, - 0x0a, 0xd0, 0x28, 0x14, 0xfa, 0xe0, 0x0f, 0x3b, 0xf9, 0xf1, 0x05, 0x14, - 0x3b, 0x4e, 0x71, 0x19, 0x17, 0x7f, 0xcd, 0xa8, 0x81, 0xa8, 0xd5, 0xc5, - 0x31, 0x55, 0xe9, 0x5e, 0x26, 0xe7, 0x04, 0x84, 0xe2, 0xa6, 0xb3, 0xa3, - 0x01, 0xb5, 0x97, 0x08, 0x82, 0xa6, 0xe0, 0xa4, 0x27, 0x02, 0x68, 0xbc, - 0x4d, 0x83, 0xa0, 0x02, 0xe3, 0x5c, 0xdd, 0xcd, 0xd9, 0x8d, 0x95, 0xa0, - 0x48, 0x68, 0x88, 0xb8, 0xd6, 0x21, 0x00, 0x61, 0xcd, 0x12, 0x83, 0x01, - 0x82, 0x3d, 0x26, 0xe4, 0x35, 0x12, 0xb0, 0xca, 0xe6, 0xe0, 0x7b, 0xe9, - 0x1a, 0xab, 0x91, 0x2c, 0xac, 0x6b, 0x1e, 0x73, 0x05, 0x55, 0x3f, 0x17, - 0xb2, 0x2b, 0xed, 0xec, 0x81, 0x35, 0x60, 0x24, 0x9e, 0x8e, 0x18, 0x20, - 0x00, 0xb7, 0xba, 0x08, 0x10, 0x15, 0x25, 0xf8, 0xce, 0x68, 0x02, 0x14, - 0x8e, 0x4f, 0x0e, 0x0b, 0x65, 0x3e, 0x14, 0x6a, 0x3d, 0x8e, 0x61, 0x52, - 0x16, 0xaf, 0xed, 0x40, 0x26, 0x22, 0x66, 0x4b, 0x87, 0xc2, 0x61, 0x24, - 0x93, 0xaa, 0xd6, 0xd1, 0x36, 0xd1, 0xc5, 0xb9, 0x48, 0x66, 0xd4, 0xd3, - 0xf8, 0x74, 0x60, 0x96, 0xae, 0x59, 0x8b, 0xca, 0xc7, 0xe1, 0xe7, 0xee, - 0xac, 0x2c, 0x32, 0x3e, 0xbf, 0x37, 0xfd, 0x03, 0x18, 0x96, 0x88, 0x45, - 0x1d, 0x11, 0xd2, 0x86, 0x34, 0x7c, 0x05, 0x40, 0xf3, 0xe0, 0x54, 0x00, - 0x5e, 0x4a, 0xbc, 0xcd, 0x67, 0x37, 0x9e, 0x7b, 0xe2, 0x90, 0x1e, 0xfe, - 0xb7, 0xec, 0x9d, 0x1b, 0x7a, 0xa6, 0x67, 0x1a, 0xb8, 0x75, 0x55, 0x18, - 0xfb, 0x5b, 0x2b, 0x4e, 0xc1, 0xc0, 0x55, 0x16, 0xdd, 0xd3, 0x3b, 0xad, - 0x2d, 0x96, 0xd6, 0x64, 0x64, 0x86, 0x6b, 0xd0, 0x33, 0x7f, 0xb4, 0x76, - 0xb7, 0x8c, 0xa9, 0x2f, 0x78, 0xa6, 0xc0, 0x3d, 0xb4, 0x0b, 0x85, 0x41, - 0xc0, 0xff, 0xf1, 0x50, 0x80, 0x2d, 0xff, 0xfc, 0x21, 0x1a, 0xca, 0x30, - 0x4b, 0x7e, 0x00, 0x00, 0x9e, 0xb4, 0xc1, 0x58, 0x76, 0x14, 0x14, 0x05, - 0x86, 0xe1, 0x53, 0xb0, 0x8c, 0x6c, 0x13, 0x0b, 0x0e, 0x02, 0x94, 0x42, - 0x84, 0xa4, 0xa8, 0xa8, 0x0a, 0x79, 0x6b, 0x7f, 0x53, 0xfa, 0x6a, 0xff, - 0x5f, 0x7f, 0x32, 0xfa, 0x0e, 0x4c, 0x10, 0x44, 0x96, 0x1c, 0x15, 0x5b, - 0xb3, 0x62, 0xab, 0xc7, 0xae, 0x53, 0xb0, 0xf9, 0xe1, 0xc7, 0x4f, 0x18, - 0xcb, 0x58, 0x76, 0x0c, 0x52, 0xdf, 0x25, 0xc9, 0x19, 0xca, 0x7f, 0x7d, - 0x0a, 0x44, 0x0e, 0xcf, 0xac, 0x28, 0x99, 0xd9, 0xf2, 0x27, 0x15, 0x2a, - 0x0b, 0x29, 0x07, 0x4e, 0xa3, 0x2f, 0xf3, 0xe2, 0x67, 0xc7, 0x59, 0x0c, - 0x20, 0x7d, 0xf8, 0xae, 0x5b, 0x50, 0xee, 0x03, 0x0c, 0xc8, 0xa7, 0x37, - 0xbb, 0xd2, 0x2c, 0x09, 0x0b, 0x40, 0xd4, 0x41, 0x37, 0x0e, 0xe7, 0x51, - 0x18, 0xde, 0x9a, 0x26, 0xd7, 0x79, 0xcf, 0xd0, 0x26, 0xac, 0x95, 0x84, - 0xfd, 0x80, 0x81, 0x9b, 0xb6, 0x4e, 0x8e, 0xd6, 0x04, 0xe7, 0x78, 0x2c, - 0xae, 0x25, 0xa2, 0xaa, 0x51, 0xb8, 0x6d, 0x7c, 0xf9, 0x32, 0x01, 0x50, - 0x5f, 0x3e, 0x49, 0x10, 0xaa, 0x00, 0x07, 0xac, 0x59, 0xc0, 0xb7, 0x75, - 0x94, 0x59, 0x88, 0x53, 0xc6, 0x45, 0x79, 0x06, 0x54, 0xc6, 0x5e, 0xe0, - 0x0f, 0x00, 0x05, 0xfc, 0xb0, 0x1d, 0xbb, 0xb7, 0xd6, 0x90, 0xb1, 0xa1, - 0x48, 0x80, 0x35, 0x57, 0x09, 0x7e, 0xb2, 0xbd, 0xa0, 0xaf, 0x3c, 0xc4, - 0xac, 0x9a, 0x87, 0x64, 0xbc, 0xe8, 0x0a, 0x21, 0x6c, 0x82, 0x46, 0xd8, - 0x86, 0xbe, 0x5b, 0xea, 0x72, 0x12, 0x22, 0x83, 0x78, 0xaf, 0x7f, 0x92, - 0x7d, 0x90, 0xee, 0x13, 0x19, 0x02, 0x0d, 0x4f, 0x3e, 0x46, 0xc1, 0xc2, - 0x35, 0x02, 0x1d, 0x3b, 0x63, 0xa4, 0xb2, 0x24, 0xfe, 0x75, 0x0f, 0xa0, - 0x00, 0x00, 0x00, 0x67, 0x5b, 0xc7, 0x59, 0xf5, 0xbd, 0xf5, 0xcd, 0xc9, - 0x43, 0x22, 0x5d, 0x11, 0x46, 0xfa, 0x87, 0x72, 0xd1, 0x23, 0xe6, 0x54, - 0xb5, 0x9b, 0x2d, 0x28, 0x81, 0x60, 0xca, 0x5f, 0x24, 0xdc, 0x17, 0xee, - 0x86, 0x8a, 0xb8, 0x20, 0xc9, 0xa3, 0xb3, 0x0a, 0x42, 0xf1, 0xcb, 0xd8, - 0x14, 0x6b, 0x0e, 0x26, 0x60, 0x5e, 0x89, 0x23, 0x13, 0x76, 0xd8, 0x0f, - 0x5d, 0x44, 0xc8, 0xa3, 0xef, 0x73, 0xac, 0x6b, 0x5f, 0xe0, 0xf1, 0x9d, - 0xa8, 0xcb, 0x5a, 0x98, 0x63, 0x76, 0x3a, 0xad, 0x97, 0x73, 0x14, 0x9b, - 0x11, 0x52, 0xae, 0xfa, 0x00, 0xa4, 0xd8, 0x86, 0xfc, 0x68, 0xc9, 0x2b, - 0x3d, 0xb7, 0x30, 0x9a, 0x44, 0x08, 0x8a, 0xe0, 0xff, 0xf1, 0x50, 0x80, - 0x3c, 0xbf, 0xfc, 0x20, 0xa2, 0x2a, 0xd6, 0x38, 0x3b, 0x1d, 0xc8, 0x85, - 0x31, 0x20, 0x95, 0x30, 0x40, 0x01, 0xd7, 0x63, 0xcf, 0x81, 0xc7, 0x3e, - 0x7c, 0x6b, 0x6f, 0xbd, 0x73, 0x7d, 0x5e, 0x67, 0x7f, 0xe9, 0xeb, 0xe9, - 0x96, 0x2c, 0xbd, 0x7b, 0xd5, 0x5c, 0x9d, 0x20, 0x5e, 0xed, 0x92, 0x12, - 0xc1, 0x4e, 0xfa, 0x65, 0x1f, 0xe2, 0xfb, 0xf3, 0xdb, 0x82, 0xb5, 0xcd, - 0x4a, 0x87, 0x86, 0x44, 0x6f, 0xe6, 0x08, 0xa1, 0x12, 0x78, 0x01, 0x09, - 0x6e, 0xbb, 0xd7, 0xfb, 0x2d, 0x4c, 0x31, 0x89, 0x99, 0x84, 0x11, 0x84, - 0x6c, 0xed, 0x74, 0xf3, 0x4f, 0x3e, 0x7e, 0xfc, 0x63, 0xfe, 0x7f, 0xd4, - 0x31, 0x90, 0x0f, 0x19, 0x8d, 0xdf, 0x84, 0x00, 0x30, 0xab, 0xf3, 0xaf, - 0x08, 0x7b, 0xa1, 0x14, 0xb7, 0x37, 0x17, 0x47, 0xe9, 0xf4, 0xf8, 0x21, - 0xb5, 0x75, 0xa8, 0x8f, 0x0f, 0x8c, 0x00, 0xc0, 0x2b, 0x7b, 0x92, 0xf9, - 0xc0, 0x10, 0x28, 0x3a, 0xb7, 0x3b, 0xde, 0xd2, 0xc3, 0x5a, 0xd6, 0xad, - 0x7f, 0x66, 0xef, 0x1a, 0xbd, 0x37, 0xb9, 0xb5, 0x57, 0x5f, 0xa7, 0xe8, - 0xb6, 0xb5, 0xa8, 0xad, 0xd4, 0xc4, 0x5a, 0xab, 0xc3, 0x71, 0xad, 0x42, - 0xaa, 0x4b, 0x1b, 0xe9, 0x88, 0x20, 0x03, 0xa9, 0xfe, 0x3f, 0xe7, 0xab, - 0x3d, 0x51, 0xf7, 0xb9, 0x71, 0xdc, 0xa9, 0xc2, 0xb3, 0xdd, 0xea, 0x52, - 0xf7, 0x71, 0x13, 0x20, 0x49, 0x06, 0xdf, 0x0e, 0xbe, 0x49, 0x37, 0x8c, - 0x54, 0x36, 0x10, 0xa3, 0x39, 0x82, 0xea, 0x11, 0x4e, 0xcc, 0xb6, 0xb8, - 0x92, 0x50, 0x19, 0x1a, 0x60, 0x7a, 0x60, 0x42, 0x34, 0xda, 0xb7, 0x11, - 0x89, 0x48, 0x27, 0xa6, 0xe4, 0xe4, 0x4c, 0x64, 0x32, 0xe9, 0xa7, 0x6e, - 0x81, 0xe8, 0x4c, 0x84, 0x9a, 0x11, 0xad, 0x63, 0x87, 0xaa, 0x0c, 0xac, - 0xc0, 0x00, 0x1a, 0xd8, 0x4a, 0xbc, 0xe3, 0x9b, 0xbb, 0x7b, 0x38, 0xae, - 0x6f, 0xd8, 0x02, 0xff, 0x72, 0xf5, 0xfb, 0xb8, 0x38, 0x64, 0x4e, 0x3d, - 0x6b, 0xcc, 0xcc, 0xdf, 0xd1, 0xbe, 0x37, 0xdf, 0xd1, 0x24, 0x9d, 0xc1, - 0x91, 0x2c, 0x50, 0x4b, 0xe2, 0x24, 0x69, 0x2c, 0x62, 0x92, 0xe7, 0x36, - 0x99, 0xce, 0xe8, 0x4f, 0x39, 0x87, 0x01, 0x66, 0x5f, 0xad, 0x00, 0x7b, - 0xaa, 0xa6, 0x61, 0xd6, 0x3a, 0x6e, 0xff, 0xd3, 0xb4, 0x23, 0x63, 0x34, - 0x1d, 0x58, 0x20, 0xbc, 0x34, 0x82, 0x5e, 0x59, 0x6e, 0x53, 0x37, 0x7d, - 0x45, 0x36, 0x78, 0xe0, 0x3d, 0xe8, 0x33, 0xf1, 0xbe, 0x0a, 0xb1, 0x37, - 0xfc, 0xfe, 0xbb, 0x60, 0xac, 0xba, 0x04, 0x37, 0xf8, 0xe4, 0x2a, 0x57, - 0x44, 0x02, 0xd8, 0xaa, 0x91, 0x75, 0x4a, 0xaa, 0xa0, 0x00, 0x60, 0x29, - 0x01, 0x05, 0x6f, 0x09, 0x5e, 0x22, 0x88, 0xb3, 0x3c, 0xe5, 0x18, 0x01, - 0x1e, 0xce, 0xbc, 0xd6, 0xd9, 0xe2, 0x00, 0xea, 0x18, 0x9c, 0xd6, 0xc5, - 0xc8, 0x62, 0x20, 0x01, 0x46, 0x51, 0x02, 0x20, 0x55, 0xd2, 0x11, 0x04, - 0x47, 0x7a, 0xad, 0xe0, 0x51, 0x91, 0xe9, 0xd3, 0xf6, 0x12, 0x48, 0x65, - 0x2a, 0xae, 0x8e, 0xfd, 0xf9, 0x0d, 0x39, 0xe9, 0xa3, 0x6c, 0x5d, 0xdb, - 0x8f, 0x3b, 0x63, 0x1a, 0x4f, 0x27, 0x43, 0x9f, 0x09, 0x56, 0x49, 0x72, - 0x4c, 0x2d, 0xa7, 0xf7, 0xc6, 0x76, 0x4b, 0xa7, 0xba, 0x45, 0xc3, 0x64, - 0x99, 0x45, 0x48, 0xaf, 0x68, 0x75, 0x65, 0xbc, 0x2a, 0xe3, 0x48, 0xff, - 0xa7, 0x17, 0x5f, 0x2b, 0x95, 0xd7, 0x2d, 0x9d, 0x89, 0xd9, 0x2c, 0x71, - 0xe0, 0xff, 0xf1, 0x50, 0x80, 0x35, 0xbf, 0xfc, 0x20, 0x9f, 0x4c, 0xd9, - 0x45, 0x9a, 0xb0, 0xb6, 0x58, 0xa7, 0x76, 0x5b, 0xcb, 0x2d, 0xd5, 0x11, - 0x20, 0x00, 0x7e, 0xf7, 0x8f, 0xaf, 0xfb, 0x05, 0xf1, 0xfc, 0xfa, 0xbb, - 0xbf, 0xf5, 0x07, 0x95, 0xe7, 0xb7, 0xd8, 0x5c, 0x0c, 0x75, 0x88, 0x1f, - 0xfa, 0xf4, 0xbf, 0x89, 0xec, 0xea, 0xb2, 0x11, 0x6f, 0x75, 0x07, 0x97, - 0x19, 0xe4, 0x5c, 0xcf, 0x35, 0x39, 0xad, 0xb8, 0xb5, 0x0b, 0x22, 0xc8, - 0xb1, 0x88, 0x43, 0x96, 0x62, 0x53, 0xb8, 0x1c, 0x5c, 0x23, 0xeb, 0x74, - 0xaa, 0x3e, 0x9a, 0x2d, 0x1a, 0xc3, 0x6d, 0xd1, 0x68, 0xf6, 0x26, 0xf2, - 0xcb, 0x2c, 0xf0, 0xcd, 0xf3, 0x70, 0x18, 0xee, 0xc1, 0x5b, 0xbd, 0xb9, - 0x1d, 0x1e, 0xff, 0xbd, 0x5c, 0xcd, 0x37, 0x6b, 0xae, 0x9d, 0xb4, 0xd2, - 0x91, 0x02, 0xf5, 0xa9, 0x96, 0x16, 0xa6, 0x68, 0x38, 0x32, 0x7d, 0x7b, - 0xc8, 0x7a, 0xdf, 0x4f, 0xb7, 0x28, 0x05, 0xf5, 0x8f, 0x92, 0x6a, 0x39, - 0xf8, 0x43, 0x71, 0x15, 0x35, 0x8b, 0x42, 0xf1, 0x31, 0x21, 0x04, 0xdd, - 0x56, 0xff, 0xd1, 0x52, 0xf4, 0xad, 0xd1, 0x1d, 0x04, 0xa3, 0x41, 0x51, - 0x2d, 0x71, 0x44, 0xfd, 0x0f, 0xb2, 0x57, 0xb6, 0xed, 0x06, 0x81, 0xf1, - 0xcc, 0x2e, 0x75, 0xbc, 0xae, 0x01, 0x09, 0x27, 0xf1, 0xef, 0x62, 0x32, - 0x67, 0x31, 0x53, 0x74, 0x96, 0x79, 0x72, 0xae, 0x41, 0xc0, 0x41, 0xdb, - 0x28, 0x7f, 0x96, 0xc2, 0x94, 0x6b, 0x59, 0x61, 0xa9, 0x56, 0x20, 0x00, - 0x00, 0xeb, 0xbe, 0x39, 0xf3, 0xe3, 0xe3, 0x33, 0xc8, 0xad, 0xe7, 0x5e, - 0x66, 0x71, 0xbd, 0x74, 0x29, 0x72, 0xb1, 0xe0, 0x35, 0x44, 0xea, 0x1c, - 0xa5, 0x00, 0x90, 0x6f, 0xa8, 0x6e, 0x6f, 0xac, 0xb6, 0xae, 0x52, 0x4e, - 0x30, 0xd6, 0x9a, 0x3d, 0x7e, 0xfb, 0x8e, 0xe7, 0x98, 0x3a, 0x91, 0x01, - 0xda, 0x03, 0x80, 0xa5, 0x0c, 0x90, 0xb8, 0x02, 0x88, 0x60, 0x02, 0x0d, - 0x48, 0xdc, 0x51, 0x5f, 0x05, 0x02, 0x70, 0x95, 0x75, 0x36, 0xb6, 0x87, - 0x19, 0xc0, 0x08, 0xfb, 0x5d, 0x19, 0x67, 0xb2, 0x07, 0x7f, 0x75, 0x4e, - 0x9d, 0x59, 0x88, 0x21, 0x1e, 0xdb, 0x1b, 0xd8, 0xc3, 0xa9, 0xfc, 0xeb, - 0x5f, 0x17, 0xa4, 0x21, 0x87, 0xd9, 0x1e, 0xa4, 0x99, 0x98, 0xaf, 0x1f, - 0x3f, 0xa0, 0xc5, 0xc4, 0x3c, 0x5c, 0xfb, 0xe1, 0x99, 0x8b, 0x1c, 0x78, - 0x7c, 0xf6, 0xc1, 0xb0, 0x3b, 0xb8, 0x7b, 0xbc, 0x1a, 0x42, 0xc6, 0x03, - 0xdb, 0xb6, 0xd5, 0x80, 0x00, 0x3c, 0x96, 0x9f, 0x77, 0x70, 0x07, 0xc5, - 0xed, 0x0f, 0x58, 0x07, 0xf4, 0x0f, 0xb0, 0x44, 0xae, 0x4e, 0xec, 0x24, - 0xfd, 0x40, 0x7d, 0x2e, 0x57, 0xfa, 0xfb, 0x68, 0x70, 0x7a, 0xe1, 0xf3, - 0x87, 0xc6, 0x93, 0xfe, 0x07, 0x49, 0x41, 0x15, 0x5e, 0xee, 0xf8, 0x12, - 0x02, 0xb6, 0x0a, 0x22, 0x7c, 0xa2, 0x2e, 0x9d, 0x0c, 0xc0, 0xd5, 0x64, - 0x77, 0xce, 0x5a, 0xae, 0xef, 0x84, 0x28, 0x13, 0xb3, 0x3f, 0xea, 0x59, - 0x18, 0xe8, 0xc6, 0x8c, 0xb0, 0x93, 0x7a, 0xc5, 0x9a, 0x78, 0xff, 0xf1, - 0x50, 0x80, 0x34, 0x3f, 0xfc, 0x20, 0xaa, 0x7a, 0xd6, 0x36, 0x42, 0x18, - 0xd6, 0x46, 0x50, 0xb2, 0x00, 0x1e, 0xde, 0x92, 0xbd, 0xbd, 0xbf, 0x86, - 0xbc, 0xfc, 0xf1, 0xcf, 0x9d, 0xfc, 0x78, 0xea, 0xaa, 0x66, 0x79, 0xbf, - 0x6a, 0xb5, 0xb7, 0xa1, 0x39, 0xf5, 0x0a, 0x89, 0x9d, 0x19, 0x35, 0x7c, - 0xd3, 0x47, 0x70, 0xaf, 0x7c, 0x46, 0x6f, 0xb9, 0xad, 0x50, 0x04, 0xfc, - 0x48, 0x24, 0x8a, 0x94, 0xd5, 0x6e, 0x2b, 0xfb, 0x7d, 0xd8, 0x69, 0xd7, - 0xbe, 0x5b, 0x57, 0x63, 0x51, 0xe1, 0x53, 0xcb, 0x2b, 0xb1, 0xd5, 0x8e, - 0xfc, 0x76, 0xf9, 0x13, 0xc6, 0xe2, 0x58, 0xe7, 0xcb, 0x1e, 0xa7, 0x59, - 0xfb, 0x1d, 0xc8, 0x41, 0x43, 0x71, 0xe7, 0x39, 0xb4, 0x35, 0x76, 0xef, - 0xbd, 0x31, 0xba, 0x6f, 0xd6, 0x00, 0x94, 0x3f, 0x9b, 0xd6, 0x94, 0x2d, - 0x9f, 0x5f, 0x03, 0xac, 0x01, 0xbc, 0x76, 0x7c, 0xc8, 0x55, 0xb6, 0xe0, - 0xc6, 0x0f, 0x15, 0x39, 0x43, 0xe8, 0x5d, 0x3e, 0x5a, 0x54, 0xc0, 0x2e, - 0x8a, 0x3c, 0x65, 0x21, 0x85, 0x95, 0x65, 0x95, 0x06, 0x5b, 0xae, 0x81, - 0x99, 0xeb, 0xb4, 0x65, 0x4a, 0xad, 0x32, 0x62, 0x8b, 0xe8, 0xd7, 0xa1, - 0x92, 0x97, 0xa1, 0xe9, 0x96, 0x1d, 0xe8, 0x76, 0x59, 0x79, 0xbc, 0xf2, - 0xcb, 0xb6, 0xa6, 0x00, 0xb0, 0xea, 0xad, 0xde, 0x99, 0x08, 0x3c, 0x94, - 0xc1, 0x9d, 0x96, 0x62, 0x9d, 0xe7, 0x36, 0xbb, 0x67, 0xcc, 0x2c, 0x0b, - 0x30, 0xbc, 0xb1, 0x54, 0x54, 0x6b, 0xab, 0x7b, 0xce, 0x72, 0x2c, 0x4c, - 0x35, 0x79, 0x68, 0xb7, 0xa6, 0x12, 0xcf, 0x39, 0x4c, 0xb0, 0x73, 0x8d, - 0x80, 0xd6, 0xa2, 0x24, 0x05, 0xa8, 0xa1, 0xfe, 0x9f, 0xd1, 0xd7, 0x7c, - 0x75, 0xf3, 0xae, 0x2f, 0xdb, 0x7f, 0xb3, 0xaf, 0x6f, 0xb6, 0xab, 0xdb, - 0x94, 0x7b, 0x7a, 0x54, 0xf3, 0x75, 0x9c, 0x67, 0x92, 0x07, 0x78, 0x4f, - 0x9e, 0x7c, 0x28, 0x92, 0x4b, 0x35, 0xdf, 0x97, 0x60, 0xd0, 0x2b, 0xf7, - 0x58, 0x5d, 0x96, 0x56, 0x57, 0xae, 0x74, 0xff, 0x4c, 0x25, 0xd4, 0xf6, - 0x39, 0xf7, 0x9f, 0xbb, 0x8f, 0x24, 0xd2, 0xa2, 0x11, 0x82, 0xb4, 0xcd, - 0x2e, 0x3f, 0x94, 0xf4, 0x5f, 0x41, 0x9d, 0x56, 0x8c, 0xfd, 0xe3, 0x28, - 0xa3, 0x9f, 0xa6, 0x85, 0xdf, 0xd3, 0xe5, 0x87, 0xee, 0xae, 0x58, 0xf3, - 0x56, 0x4e, 0x57, 0xa7, 0x38, 0x37, 0x4d, 0x34, 0x4e, 0x9a, 0x17, 0x6d, - 0x85, 0x50, 0x55, 0x4d, 0x57, 0x2b, 0x04, 0xd4, 0xf0, 0x0c, 0xcb, 0x46, - 0x45, 0x14, 0x86, 0xd3, 0x9a, 0xb9, 0x31, 0xb9, 0xcb, 0x9f, 0xa1, 0x2a, - 0x88, 0x23, 0x8e, 0xe8, 0x91, 0x1c, 0x70, 0xb5, 0x2b, 0xd8, 0xd7, 0x00, - 0xb0, 0x7e, 0x36, 0xc7, 0x1d, 0x35, 0xac, 0x38, 0x70, 0x76, 0x62, 0xbc, - 0x9f, 0xc3, 0xc7, 0xf7, 0x81, 0x3f, 0xbe, 0x1a, 0x8b, 0x9f, 0xd7, 0xb9, - 0xc7, 0xbf, 0xdc, 0x93, 0xb0, 0x2d, 0xb9, 0x6c, 0x27, 0xc6, 0x22, 0xe4, - 0xf0, 0x6a, 0x0e, 0x2e, 0x5f, 0xb8, 0xe0, 0xff, 0xf1, 0x50, 0x80, 0x29, - 0x5f, 0xfc, 0x21, 0x1a, 0xcb, 0xb8, 0xe1, 0xf8, 0x00, 0x01, 0xa6, 0xb1, - 0x42, 0x59, 0x46, 0xc4, 0x23, 0x18, 0x0e, 0x2b, 0x7a, 0xdc, 0xbc, 0xfc, - 0x67, 0xd8, 0xd6, 0xc0, 0xd6, 0xb9, 0x9b, 0xbe, 0xab, 0xae, 0x7f, 0xd3, - 0xe7, 0xab, 0xef, 0x81, 0x08, 0xad, 0x45, 0x43, 0x92, 0x59, 0x83, 0xee, - 0x73, 0xca, 0x1b, 0x20, 0x05, 0x42, 0x6c, 0xb6, 0x8e, 0x1d, 0x01, 0x20, - 0x53, 0x85, 0x9a, 0x06, 0x75, 0x94, 0x52, 0x27, 0x57, 0x68, 0x3f, 0x5d, - 0xb4, 0x8e, 0xc8, 0xb0, 0xad, 0x94, 0x41, 0x03, 0xd0, 0x40, 0xe8, 0xf0, - 0x55, 0x04, 0xaa, 0x66, 0x20, 0xfc, 0x8a, 0x74, 0xc7, 0xe0, 0xeb, 0xfb, - 0x2c, 0x3c, 0xa9, 0xf5, 0xeb, 0xab, 0xd1, 0x67, 0xda, 0xee, 0x2e, 0x50, - 0x9c, 0xb1, 0xfb, 0x32, 0xf3, 0xa7, 0x96, 0x9d, 0x13, 0xa6, 0x94, 0xaf, - 0x2c, 0x3b, 0xad, 0xe1, 0xda, 0xe0, 0x94, 0x79, 0x6d, 0x8c, 0xba, 0x95, - 0xd1, 0x85, 0xe3, 0xa4, 0x8e, 0xf2, 0xe7, 0x5d, 0xbd, 0xad, 0x14, 0x0f, - 0x13, 0x55, 0xac, 0x1a, 0x3b, 0x65, 0x02, 0xa1, 0x10, 0xdc, 0x9d, 0xdc, - 0x26, 0x93, 0x4f, 0xe2, 0x72, 0xfd, 0xa7, 0x2b, 0x62, 0xac, 0x85, 0x15, - 0x6a, 0xc2, 0x8d, 0xfe, 0xde, 0x02, 0xae, 0x71, 0x24, 0xc1, 0xf9, 0xfb, - 0x15, 0xb5, 0x10, 0x00, 0x22, 0xb1, 0xb1, 0xa6, 0xb4, 0x44, 0xe3, 0x4a, - 0x20, 0xb6, 0x35, 0xc9, 0x7d, 0x21, 0x82, 0x76, 0xee, 0x91, 0xde, 0xf9, - 0xa3, 0xb4, 0xf4, 0x7e, 0xbd, 0x4c, 0x4e, 0xe0, 0x91, 0x5b, 0xb9, 0xf5, - 0xd0, 0xee, 0x01, 0x4d, 0x0e, 0x35, 0xdc, 0x5e, 0xef, 0xf3, 0x77, 0x03, - 0xbc, 0x20, 0x49, 0xc6, 0x4a, 0x7b, 0x0c, 0x29, 0x4e, 0x88, 0xf8, 0xa1, - 0x53, 0x8a, 0xe7, 0xcf, 0x8b, 0xbc, 0xb9, 0xda, 0x6b, 0x62, 0x9e, 0x6d, - 0xcc, 0xdd, 0xcd, 0xdc, 0x8d, 0xf1, 0xce, 0xc4, 0x23, 0xb4, 0x6b, 0xb8, - 0xaf, 0x32, 0xe3, 0x51, 0x18, 0x51, 0x58, 0x61, 0x30, 0xde, 0xbd, 0x03, - 0xa1, 0xfd, 0x6d, 0x2c, 0x18, 0x86, 0xfb, 0x53, 0xd7, 0xc6, 0x71, 0x0e, - 0x73, 0xc7, 0x1e, 0x73, 0xda, 0xfd, 0x77, 0xb4, 0xa1, 0x17, 0x45, 0x67, - 0x5e, 0x87, 0x1b, 0xe9, 0x14, 0x41, 0x58, 0xbd, 0x58, 0x7a, 0xa7, 0x7c, - 0xcc, 0xf2, 0xa9, 0xea, 0xfc, 0x16, 0xf3, 0xc3, 0x59, 0x6c, 0x79, 0x13, - 0xf0, 0xff, 0xf1, 0x50, 0x80, 0x27, 0x5f, 0xfc, 0x21, 0x1a, 0xce, 0x6e, - 0xee, 0x7c, 0x04, 0x00, 0xa4, 0xb1, 0xc1, 0x98, 0xae, 0x56, 0x21, 0x94, - 0x4a, 0xa4, 0x63, 0x00, 0x01, 0xc6, 0x60, 0x04, 0xa7, 0x9e, 0xfa, 0xad, - 0x5f, 0x5c, 0xd7, 0xeb, 0xbd, 0x66, 0xb0, 0x27, 0xc8, 0x48, 0xad, 0xe8, - 0x4b, 0xb0, 0x79, 0x95, 0xa2, 0xc0, 0x70, 0x28, 0x9c, 0xf8, 0x8e, 0x21, - 0xf8, 0x3b, 0x5a, 0xc9, 0x00, 0xff, 0x15, 0xab, 0x6c, 0x13, 0xad, 0x2f, - 0x1d, 0x07, 0x18, 0xc1, 0x28, 0x9d, 0xcc, 0xa1, 0x7f, 0x03, 0x29, 0x53, - 0x75, 0xe6, 0x66, 0xe6, 0x62, 0x69, 0x9c, 0x4c, 0x70, 0x05, 0x49, 0x93, - 0x89, 0xa1, 0xb0, 0x6d, 0xd8, 0x1e, 0xbd, 0x90, 0xfa, 0x54, 0xa1, 0xbc, - 0x99, 0x32, 0x79, 0xfb, 0x5f, 0x6c, 0x58, 0x86, 0xc4, 0x00, 0xee, 0x9f, - 0xe8, 0x42, 0x0a, 0x4e, 0xd8, 0xb0, 0xdb, 0x8e, 0x78, 0xe3, 0x4d, 0x7b, - 0x4f, 0x63, 0x37, 0x85, 0x66, 0xa7, 0xaf, 0xae, 0xe2, 0xe6, 0x61, 0x92, - 0x30, 0xc4, 0x57, 0xba, 0xd1, 0x38, 0x5d, 0x4e, 0x4e, 0xd9, 0x47, 0x9c, - 0x00, 0xba, 0x30, 0x6f, 0xaa, 0x11, 0x59, 0x4e, 0xe0, 0x3b, 0xb0, 0xaa, - 0x59, 0x6a, 0x0d, 0x97, 0x54, 0x6a, 0x77, 0xe5, 0x9d, 0x64, 0x05, 0x76, - 0x3e, 0x15, 0x44, 0x08, 0x52, 0x48, 0x1e, 0x12, 0xd8, 0x9a, 0xb7, 0x69, - 0x96, 0x4c, 0xa1, 0xef, 0xed, 0x80, 0xae, 0xc5, 0x84, 0xa9, 0x25, 0x6d, - 0x7d, 0x73, 0x00, 0x5a, 0xa7, 0x15, 0x0c, 0x83, 0xe9, 0xb3, 0xe6, 0xea, - 0x86, 0xa7, 0xed, 0x9c, 0x76, 0x35, 0x55, 0x05, 0xd4, 0x74, 0xd6, 0xf7, - 0x66, 0xa1, 0x49, 0x44, 0x97, 0x30, 0xd0, 0xbf, 0x10, 0x00, 0x17, 0x80, - 0x0e, 0xb3, 0x55, 0x33, 0x76, 0xc9, 0x5c, 0x7a, 0xd3, 0x01, 0xd6, 0xc6, - 0x26, 0xfc, 0x4b, 0x46, 0x05, 0xdd, 0x4e, 0xb6, 0x84, 0x2e, 0xb9, 0x84, - 0xef, 0xf6, 0x7f, 0x49, 0x24, 0xd3, 0x05, 0xe1, 0xb5, 0x1b, 0xf1, 0x09, - 0x72, 0xfa, 0xc8, 0xc8, 0xc5, 0x17, 0xcd, 0xb4, 0x2a, 0xac, 0x1a, 0xa6, - 0xb2, 0xa8, 0x3d, 0xa9, 0xc0, 0x28, 0x05, 0xad, 0xbc, 0x8b, 0xfb, 0x6e, - 0x54, 0xb1, 0xda, 0xe0, 0x7d, 0xbd, 0x27, 0x8b, 0x03, 0xa8, 0x2b, 0x0a, - 0x48, 0x37, 0x7e, 0xff, 0xf1, 0x50, 0x80, 0x28, 0x9f, 0xfc, 0x21, 0x1a, - 0xcf, 0xde, 0xff, 0xff, 0x00, 0x00, 0xa4, 0xb1, 0xb3, 0x9c, 0x28, 0x53, - 0x2a, 0x91, 0x84, 0xa3, 0x63, 0x00, 0x3a, 0xec, 0x6b, 0x5e, 0x09, 0x40, - 0xba, 0xfe, 0x7d, 0xd7, 0xe6, 0xfd, 0x5e, 0x7e, 0x33, 0x55, 0x78, 0x20, - 0x44, 0x48, 0x62, 0x20, 0x87, 0x60, 0x8f, 0x48, 0x73, 0xc1, 0xec, 0x9f, - 0xff, 0xb5, 0xf7, 0x2e, 0xbf, 0x2f, 0x20, 0x10, 0x4b, 0x5c, 0x9e, 0x95, - 0xaa, 0xc0, 0xb2, 0xe3, 0x00, 0xc4, 0x41, 0x4c, 0x2e, 0x1b, 0xca, 0xe8, - 0xbc, 0xbe, 0x1e, 0x26, 0xa6, 0x4c, 0x20, 0x3e, 0x4b, 0xdf, 0xac, 0x94, - 0x6a, 0x11, 0xfa, 0x53, 0x2a, 0xde, 0xce, 0xac, 0x68, 0x7b, 0x6b, 0x57, - 0xdb, 0x49, 0x59, 0x92, 0x9c, 0x77, 0x20, 0xc5, 0xb9, 0x80, 0x8a, 0x2a, - 0x2f, 0x7c, 0x3b, 0x7b, 0x71, 0xcb, 0xd7, 0x38, 0x15, 0x61, 0xcf, 0x12, - 0xd1, 0x93, 0x94, 0x8d, 0x47, 0x18, 0x10, 0x48, 0xd4, 0xe2, 0x6b, 0x8f, - 0x20, 0x5a, 0xe4, 0xc5, 0x68, 0xab, 0xab, 0x3a, 0xd3, 0x83, 0xa0, 0x5b, - 0x44, 0xfb, 0xaa, 0xa4, 0xab, 0x54, 0x16, 0xd9, 0xec, 0x30, 0x0a, 0xc9, - 0x65, 0x7d, 0x21, 0x71, 0x91, 0x75, 0x03, 0xd2, 0x10, 0x66, 0x43, 0x4b, - 0x8f, 0x7e, 0x93, 0x8b, 0x7b, 0x6c, 0x38, 0xae, 0x47, 0x53, 0x6a, 0xa3, - 0xba, 0x7f, 0x25, 0x4d, 0xd1, 0x89, 0x18, 0xe8, 0x4c, 0x3c, 0x82, 0x9d, - 0xb2, 0x50, 0xcf, 0x83, 0xf8, 0x7b, 0x8b, 0x8c, 0x51, 0x53, 0x37, 0xe2, - 0x6f, 0x20, 0x95, 0xc5, 0xa4, 0x77, 0x27, 0x5d, 0x22, 0x54, 0xf2, 0xb7, - 0xcd, 0x8e, 0x3c, 0x6e, 0xdb, 0xd2, 0xdb, 0x64, 0xa0, 0xf0, 0x1e, 0x16, - 0x77, 0xc4, 0xcf, 0xb3, 0xb5, 0x3a, 0x82, 0x92, 0x16, 0xb0, 0xf8, 0x00, - 0x3a, 0xec, 0x6b, 0x5e, 0x09, 0x40, 0xbb, 0xeb, 0x2b, 0x2a, 0xfb, 0x98, - 0xd6, 0xe5, 0xe0, 0x53, 0xf4, 0x32, 0x12, 0x11, 0xdc, 0x71, 0xb3, 0x98, - 0xb7, 0x2d, 0x50, 0x2c, 0x07, 0x0a, 0xd1, 0xe8, 0xd9, 0x60, 0xe5, 0xd7, - 0x4b, 0x2a, 0x1b, 0x79, 0x78, 0xf1, 0x8d, 0xa0, 0xc2, 0x6e, 0x25, 0x9a, - 0xab, 0xba, 0xbf, 0x93, 0xc5, 0x2a, 0x83, 0x41, 0x96, 0xf9, 0x5d, 0x02, - 0xe9, 0x95, 0x7b, 0x6f, 0x8c, 0x55, 0xa6, 0x80, 0xb5, 0xaf, 0x37, 0x7d, - 0xd0, 0x19, 0x38, 0xff, 0xf1, 0x50, 0x80, 0x3c, 0x5f, 0xfc, 0x20, 0xa5, - 0x1a, 0xd6, 0x28, 0x42, 0xc9, 0x0a, 0xc3, 0x30, 0xb1, 0x80, 0x1d, 0x76, - 0x2f, 0x8f, 0x06, 0xb6, 0x6b, 0x7c, 0x54, 0xf8, 0xa9, 0x5a, 0x95, 0xc7, - 0xbf, 0xc5, 0x71, 0x75, 0x06, 0xeb, 0x96, 0xc9, 0x3a, 0x9b, 0x7b, 0xa4, - 0xe6, 0x1b, 0x61, 0x27, 0xf2, 0x90, 0x58, 0x81, 0x06, 0x89, 0xa1, 0xc3, - 0x9f, 0xe3, 0x50, 0xed, 0xef, 0xcf, 0x44, 0xdf, 0x67, 0x6b, 0x1b, 0x97, - 0x1c, 0x4a, 0x79, 0xe2, 0xa1, 0x7f, 0xf3, 0x90, 0xd5, 0xee, 0xb7, 0x77, - 0x32, 0xbf, 0xaf, 0x32, 0x98, 0xa9, 0x85, 0x63, 0x01, 0xdd, 0x25, 0x88, - 0xb6, 0xae, 0xfb, 0xfb, 0xb3, 0x1c, 0x99, 0x90, 0x02, 0x9c, 0xfa, 0x49, - 0x01, 0x66, 0x4e, 0x00, 0x42, 0xf1, 0xad, 0xd3, 0xb5, 0xd1, 0x80, 0xec, - 0xdf, 0x9c, 0x76, 0x75, 0xb9, 0x61, 0x5a, 0xb4, 0x6b, 0xd4, 0xc9, 0x3c, - 0xbf, 0x2d, 0x71, 0x6b, 0xc1, 0xf7, 0x4c, 0xa2, 0xea, 0x02, 0x05, 0xfd, - 0x64, 0x30, 0x34, 0x7d, 0x51, 0x94, 0x98, 0x80, 0x82, 0xad, 0x48, 0x6e, - 0x63, 0x1c, 0xb3, 0x43, 0xb2, 0xa9, 0x40, 0x44, 0x18, 0x08, 0x05, 0x28, - 0xd1, 0x7b, 0x95, 0xa9, 0x85, 0x24, 0xe1, 0x25, 0x06, 0x32, 0xfe, 0xfb, - 0x3e, 0xda, 0xc5, 0xdd, 0xa2, 0xa9, 0x62, 0x97, 0x9a, 0xab, 0x15, 0xa6, - 0xbe, 0xe0, 0x90, 0x29, 0xaa, 0x66, 0x19, 0x9d, 0xca, 0x55, 0x56, 0x48, - 0xba, 0xc6, 0x4a, 0x23, 0x6f, 0x66, 0xd5, 0xef, 0xd8, 0xb2, 0xf8, 0x35, - 0xf1, 0xee, 0x69, 0xea, 0x8d, 0xbc, 0x58, 0x9e, 0xdf, 0xb3, 0x68, 0xce, - 0xde, 0xbb, 0xb0, 0xdb, 0x5a, 0x0e, 0x11, 0x4f, 0x6c, 0x2d, 0x56, 0xdb, - 0x80, 0x99, 0xcf, 0xec, 0xb1, 0x32, 0xd5, 0xc7, 0x55, 0x19, 0x56, 0xb1, - 0x44, 0xd4, 0x6c, 0x13, 0x43, 0x28, 0x06, 0xb6, 0x00, 0x00, 0x2e, 0x9d, - 0x79, 0xb9, 0x1d, 0x25, 0x03, 0x2b, 0x86, 0x5a, 0x55, 0x6a, 0xba, 0x08, - 0x94, 0x23, 0xbf, 0x56, 0x4b, 0x47, 0xfc, 0x55, 0xab, 0x51, 0x06, 0x68, - 0x03, 0x40, 0xe8, 0x45, 0x51, 0x4e, 0x9b, 0x24, 0x45, 0x05, 0x32, 0x92, - 0x51, 0x31, 0x00, 0x00, 0x18, 0x2e, 0x15, 0x90, 0x12, 0xad, 0x7a, 0x1e, - 0x40, 0x0c, 0xb0, 0xb0, 0x4c, 0x94, 0x8d, 0xe2, 0x30, 0xd0, 0x03, 0x95, - 0x4d, 0x36, 0x10, 0xda, 0xad, 0xdf, 0xe9, 0xd2, 0xa5, 0x6e, 0x95, 0xf4, - 0x1a, 0x44, 0xc7, 0x75, 0x3a, 0xf0, 0x7e, 0x43, 0x26, 0x94, 0x6c, 0x51, - 0xd0, 0x75, 0xf8, 0x23, 0xf4, 0xba, 0xc2, 0xbb, 0x8d, 0x3f, 0xb7, 0xea, - 0xba, 0xf0, 0x61, 0x29, 0x3b, 0x09, 0x29, 0x90, 0xb4, 0xaf, 0x01, 0xa3, - 0x4b, 0x51, 0xde, 0x87, 0x26, 0x7a, 0x31, 0xd1, 0x86, 0x9f, 0xd2, 0x4c, - 0x62, 0x9a, 0x50, 0xd8, 0x45, 0x68, 0xa1, 0x05, 0xfb, 0x55, 0x6c, 0x83, - 0x44, 0x38, 0x10, 0x30, 0x25, 0xa6, 0x60, 0x84, 0x44, 0x2a, 0x81, 0x1e, - 0x0a, 0xac, 0x01, 0xf9, 0x1e, 0x48, 0x95, 0xa5, 0xb7, 0x01, 0x00, 0x85, - 0xf5, 0x51, 0xba, 0x6f, 0x23, 0x50, 0x5d, 0x95, 0x92, 0xa9, 0xac, 0xa9, - 0xce, 0x49, 0x72, 0x35, 0xba, 0x90, 0xbb, 0xd0, 0xf4, 0x18, 0x8c, 0x58, - 0x13, 0xeb, 0x63, 0x2b, 0xee, 0xd3, 0xb0, 0x73, 0x98, 0xc9, 0x6d, 0xdd, - 0x64, 0xf2, 0x89, 0x26, 0x13, 0xdd, 0xe2, 0xfb, 0xe5, 0xf3, 0x85, 0x83, - 0x3e, 0x63, 0x11, 0x96, 0x7c, 0xe5, 0x26, 0xcf, 0x70, 0x82, 0x26, 0x1e, - 0x65, 0xef, 0x3d, 0x6a, 0x3c, 0xff, 0xf1, 0x50, 0x80, 0x38, 0xbf, 0xfc, - 0x20, 0xab, 0x1a, 0xd4, 0x36, 0x7a, 0x38, 0x8e, 0xc4, 0x03, 0x5f, 0x5f, - 0xa7, 0xb7, 0xab, 0x9b, 0x01, 0xed, 0xeb, 0xae, 0xf8, 0xd7, 0xb7, 0x7c, - 0xd5, 0xd2, 0x87, 0x1f, 0x76, 0xfd, 0x81, 0x9f, 0x89, 0xf6, 0x5d, 0x0a, - 0x40, 0x4e, 0x97, 0xca, 0xad, 0x5c, 0x51, 0xb3, 0x17, 0xa2, 0x9c, 0xd4, - 0x0a, 0x78, 0x88, 0x05, 0x11, 0x10, 0x89, 0xe1, 0x0d, 0x6c, 0x95, 0x4a, - 0x32, 0xb9, 0x15, 0x44, 0x3b, 0xe1, 0x04, 0x18, 0x8c, 0x3d, 0x20, 0x00, - 0xf0, 0xa7, 0xe9, 0x11, 0x1b, 0xa5, 0x00, 0xfa, 0x64, 0x45, 0xe2, 0x82, - 0xd9, 0xae, 0xa3, 0xc2, 0xd6, 0xc0, 0x23, 0x0f, 0x77, 0xc3, 0x60, 0xc5, - 0x41, 0xe7, 0xec, 0x67, 0xe6, 0x9f, 0xe0, 0x30, 0x1f, 0xa6, 0x3a, 0x48, - 0xc4, 0x6d, 0x38, 0x98, 0x0f, 0xe5, 0x86, 0x2d, 0x6e, 0x07, 0x0f, 0x10, - 0x06, 0x7c, 0xc4, 0x6d, 0x98, 0x8f, 0x37, 0x3e, 0xe7, 0xf2, 0x31, 0x78, - 0x03, 0x95, 0xd7, 0xf6, 0x06, 0xf0, 0x4f, 0x58, 0xba, 0xa2, 0xec, 0x8d, - 0x95, 0x61, 0xb6, 0x00, 0x14, 0x6f, 0x02, 0xf9, 0xf4, 0xdd, 0x18, 0x93, - 0xa8, 0x0b, 0xc4, 0x83, 0xd9, 0x31, 0xf8, 0x27, 0xe6, 0xb0, 0x80, 0x36, - 0xfc, 0xc6, 0xd9, 0x11, 0xa8, 0xb8, 0xef, 0x3a, 0x24, 0xe6, 0x59, 0xc1, - 0x23, 0x0f, 0x65, 0xaf, 0x45, 0xd7, 0x90, 0x84, 0x2e, 0x7d, 0x1f, 0xaa, - 0xfc, 0xc4, 0x61, 0x33, 0x5b, 0xa7, 0x6c, 0xf3, 0x18, 0x80, 0x55, 0x0c, - 0xb2, 0x84, 0x55, 0x96, 0x8e, 0x5a, 0x2d, 0xec, 0xa7, 0xb6, 0x9c, 0xa7, - 0xd3, 0x26, 0xd9, 0x4c, 0x13, 0x4d, 0x18, 0xee, 0xcd, 0x96, 0xa9, 0x4c, - 0xd9, 0x86, 0xcd, 0x20, 0x90, 0x0e, 0xb8, 0xb8, 0xe9, 0x79, 0x53, 0xf7, - 0xff, 0xb0, 0x09, 0x49, 0x40, 0x00, 0x7e, 0xb1, 0xb3, 0x52, 0x84, 0xf7, - 0xf5, 0x9e, 0x31, 0x57, 0x9d, 0xed, 0x7a, 0xed, 0x5a, 0xa0, 0xbe, 0x63, - 0x4d, 0xc5, 0x8e, 0x0a, 0xe1, 0x80, 0x84, 0xcc, 0x03, 0x43, 0x79, 0x6d, - 0x6d, 0x1e, 0xfb, 0xfd, 0x38, 0x12, 0x1c, 0x4e, 0x46, 0xd3, 0x22, 0x20, - 0xce, 0x81, 0x0c, 0x4c, 0x20, 0x06, 0x52, 0x19, 0x1b, 0x4d, 0xa4, 0xc4, - 0x2a, 0x36, 0x84, 0x0e, 0x6b, 0xb0, 0x7a, 0xd1, 0x74, 0xf1, 0x10, 0x8c, - 0x27, 0x8b, 0x61, 0x87, 0xed, 0x6c, 0x5f, 0x52, 0x4b, 0x6d, 0x95, 0xbe, - 0xe9, 0xb5, 0xd8, 0x57, 0x14, 0x25, 0xde, 0x29, 0x91, 0x75, 0xe8, 0xaf, - 0xbb, 0x6d, 0x54, 0xf7, 0x69, 0xac, 0x45, 0x2c, 0xbe, 0x9d, 0x36, 0xfc, - 0xd7, 0xd0, 0x06, 0x7f, 0x6c, 0xf4, 0x93, 0x15, 0xd1, 0x12, 0xb1, 0xf2, - 0x60, 0x2a, 0xe6, 0xe2, 0x53, 0x43, 0x55, 0xb1, 0x81, 0x81, 0xfa, 0xd8, - 0xcf, 0x58, 0x71, 0x74, 0xdd, 0x4f, 0xc0, 0x9c, 0xcc, 0x59, 0x77, 0x0d, - 0xe7, 0xec, 0xa0, 0xcd, 0xbd, 0x09, 0x92, 0xc9, 0x90, 0x46, 0x0f, 0x94, - 0xdf, 0x7a, 0x35, 0x2a, 0x16, 0xde, 0x2a, 0x0d, 0x3f, 0x81, 0x6b, 0xb0, - 0x4a, 0xbb, 0xc2, 0x2b, 0xbb, 0xc6, 0x2f, 0x57, 0x2d, 0x28, 0x1a, 0x4c, - 0xf8, 0x09, 0x9e, 0x90, 0xae, 0x1f, 0xa8, 0x45, 0xc4, 0x02, 0x34, 0x54, - 0x86, 0xb9, 0x5f, 0x94, 0x9b, 0x67, 0x7e, 0xa0, 0xe2, 0x8b, 0xb5, 0x6c, - 0xab, 0x80, 0xff, 0xf1, 0x50, 0x80, 0x36, 0xff, 0xfc, 0x20, 0xa3, 0x1a, - 0xd6, 0x38, 0x7b, 0x1c, 0x9c, 0xc4, 0x00, 0x4a, 0x00, 0x00, 0x00, 0x0f, - 0xad, 0x67, 0xea, 0xd3, 0x0c, 0xd0, 0x00, 0x06, 0x1b, 0x00, 0x00, 0x4d, - 0xa2, 0x4e, 0x34, 0xca, 0x4a, 0xce, 0x16, 0xe1, 0xd4, 0xa9, 0xca, 0xca, - 0xbe, 0x57, 0xad, 0x29, 0x35, 0xc9, 0xec, 0x76, 0x84, 0x28, 0xe2, 0x67, - 0xd3, 0x5c, 0x34, 0x66, 0x92, 0x41, 0x68, 0x63, 0x5b, 0xa4, 0x18, 0x94, - 0x62, 0x85, 0x4d, 0x55, 0x26, 0x3d, 0x58, 0x42, 0x13, 0xa5, 0xd4, 0x72, - 0x38, 0x0d, 0x34, 0xb0, 0x1d, 0x7d, 0xd8, 0x1f, 0x5f, 0xee, 0xdb, 0x30, - 0x93, 0xdf, 0x26, 0x04, 0xee, 0x2e, 0x04, 0x81, 0x6d, 0xdb, 0xb2, 0x73, - 0x03, 0x74, 0x7a, 0x66, 0x69, 0xaf, 0xef, 0x02, 0xc1, 0xc6, 0xf2, 0x8c, - 0x72, 0xb6, 0xc8, 0x3d, 0x8b, 0xde, 0x53, 0xf9, 0x9c, 0x5f, 0x29, 0x04, - 0x92, 0x66, 0x41, 0x01, 0x28, 0x67, 0xc2, 0x9b, 0xf5, 0xb1, 0x1e, 0x98, - 0x1a, 0x01, 0x4e, 0xbf, 0x84, 0x78, 0xb6, 0xd3, 0x80, 0xee, 0xfb, 0xa4, - 0x3b, 0xa3, 0xd3, 0xdf, 0x41, 0x24, 0xe1, 0xc3, 0xb6, 0xfe, 0x67, 0xd6, - 0xc5, 0x00, 0xfa, 0x47, 0xc3, 0x68, 0x62, 0x48, 0x0c, 0x80, 0x83, 0x8e, - 0x9f, 0xe3, 0x3f, 0xbc, 0xc0, 0x2b, 0xe3, 0x30, 0x7f, 0x30, 0x0f, 0xcd, - 0x34, 0x1b, 0x84, 0x1a, 0x01, 0x1e, 0x38, 0xa3, 0xa1, 0x3b, 0xba, 0x93, - 0xdf, 0x4d, 0xcf, 0x80, 0xd3, 0xd7, 0x34, 0xc1, 0xd7, 0xa0, 0x00, 0x0d, - 0x90, 0x9d, 0xc1, 0xbc, 0x13, 0x0f, 0x26, 0x03, 0x8e, 0x29, 0xdb, 0xdd, - 0x82, 0xb3, 0x35, 0x23, 0xd6, 0xb1, 0xc4, 0x98, 0xe8, 0x91, 0x2b, 0x04, - 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xc8, 0x27, 0xb1, 0x8d, 0x58, 0x0e, - 0xd7, 0x6c, 0x63, 0x9b, 0x68, 0xc8, 0x16, 0x5c, 0x7e, 0xd8, 0xc7, 0x32, - 0x74, 0x78, 0xc9, 0x42, 0xc4, 0x7d, 0x86, 0x58, 0x61, 0x45, 0xf1, 0x92, - 0x37, 0x8b, 0xcf, 0x2d, 0x1a, 0x80, 0x45, 0xae, 0xbe, 0x24, 0x92, 0x06, - 0xca, 0xf1, 0x43, 0x91, 0x3f, 0xa9, 0x02, 0x45, 0x04, 0x93, 0x05, 0x4e, - 0x10, 0x5c, 0xa0, 0x6a, 0xf0, 0xa0, 0x10, 0x53, 0x70, 0x6a, 0x0e, 0x9b, - 0x42, 0x29, 0xb4, 0xa5, 0xa3, 0x5d, 0x12, 0x56, 0x70, 0x6f, 0x5c, 0xc9, - 0x2e, 0xfd, 0xb4, 0x55, 0xdd, 0x29, 0xd6, 0x8a, 0x78, 0xe2, 0x62, 0x10, - 0x03, 0x62, 0x51, 0x20, 0x85, 0xc9, 0x34, 0x13, 0x84, 0x3b, 0xbf, 0x24, - 0x41, 0x53, 0x40, 0x07, 0xa4, 0x2d, 0xdd, 0x4e, 0x0a, 0xa4, 0x66, 0xdf, - 0xfa, 0xbe, 0xf7, 0xd7, 0x28, 0x69, 0x91, 0x51, 0x5b, 0x27, 0x02, 0xfa, - 0xd7, 0x42, 0xd1, 0x55, 0xf7, 0x0a, 0x2b, 0xc6, 0x62, 0x15, 0x93, 0xbd, - 0x97, 0x2e, 0x26, 0xbd, 0x86, 0x6f, 0x45, 0x0d, 0xb4, 0x2a, 0x8a, 0x0a, - 0xd7, 0x5b, 0x29, 0xd3, 0x8d, 0x44, 0xef, 0x46, 0x41, 0xac, 0xea, 0x5d, - 0x4b, 0xf7, 0xd0, 0x53, 0x4e, 0xe3, 0x49, 0xd6, 0xb5, 0x3f, 0xe2, 0xf8, - 0x5b, 0x5b, 0x2f, 0x88, 0x02, 0x20, 0x09, 0x08, 0x65, 0x80, 0x34, 0x18, - 0xb3, 0xd3, 0x5e, 0x23, 0x3d, 0x21, 0x33, 0x33, 0x70, 0xff, 0xf1, 0x50, - 0x80, 0x2d, 0x7f, 0xfc, 0x21, 0x1a, 0xca, 0xc0, 0xde, 0x5e, 0x00, 0x00, - 0x9e, 0xb5, 0xc0, 0xe4, 0xec, 0x43, 0x62, 0x0d, 0x84, 0xe1, 0x00, 0x00, - 0x00, 0x0b, 0xc3, 0x57, 0xbb, 0x71, 0x35, 0xed, 0x76, 0x35, 0xe9, 0x08, - 0x8c, 0xa8, 0x30, 0x44, 0xe5, 0x8a, 0xbd, 0xd6, 0x82, 0x5e, 0x92, 0x1c, - 0x1d, 0x7f, 0x2c, 0x82, 0x04, 0xcd, 0x6d, 0x14, 0x08, 0x16, 0x4e, 0xa0, - 0x35, 0x53, 0xab, 0xe3, 0x4c, 0x1d, 0x22, 0x9e, 0xff, 0xa4, 0xb7, 0xe7, - 0x2e, 0xa6, 0xf3, 0xfa, 0xd5, 0x46, 0x38, 0x5e, 0x59, 0xf7, 0xd8, 0xef, - 0xd7, 0xcb, 0xdb, 0x7a, 0x52, 0xb3, 0xe6, 0x57, 0x1e, 0xda, 0x52, 0xc3, - 0x81, 0x1f, 0x31, 0xd4, 0xe2, 0xfc, 0x1c, 0xe4, 0xfc, 0x8d, 0x90, 0x08, - 0x5f, 0xc9, 0x3d, 0x4c, 0xc9, 0x8d, 0x4a, 0x0c, 0x1c, 0x43, 0x91, 0x2d, - 0x47, 0x1d, 0xa2, 0x66, 0xed, 0xc7, 0x63, 0xce, 0x97, 0x05, 0xbc, 0xc7, - 0x8d, 0x49, 0xd5, 0xec, 0xb9, 0xbf, 0xa6, 0x88, 0x27, 0x8b, 0x35, 0xe1, - 0x2d, 0x01, 0x72, 0xf7, 0x8a, 0x08, 0xc1, 0x17, 0xd9, 0xaf, 0x18, 0xcf, - 0x87, 0x2d, 0xa7, 0x4a, 0x53, 0x9f, 0x6d, 0x6a, 0x65, 0xcb, 0x0a, 0xf6, - 0x99, 0x60, 0x09, 0x5e, 0x59, 0x9c, 0x5d, 0xa6, 0xd3, 0xd0, 0xdc, 0xc5, - 0x18, 0xa2, 0x19, 0x62, 0x57, 0x2c, 0x0f, 0x2a, 0x44, 0xc5, 0x52, 0xd3, - 0x85, 0xa7, 0xa2, 0x87, 0x95, 0x52, 0x29, 0x43, 0x6d, 0x77, 0x08, 0xd1, - 0x53, 0xe1, 0xd7, 0x9d, 0xd3, 0xf6, 0xa0, 0x0b, 0x76, 0x9f, 0xf8, 0xdd, - 0xdd, 0xd6, 0xde, 0x82, 0x86, 0x64, 0x01, 0x2a, 0x1c, 0x8d, 0x0d, 0x6f, - 0x71, 0xc0, 0xe2, 0x72, 0x30, 0x8a, 0x9f, 0xb8, 0xc1, 0x18, 0xbe, 0xcd, - 0x0f, 0x88, 0x43, 0xe1, 0x4a, 0x8a, 0x04, 0x54, 0x14, 0x95, 0x07, 0xb7, - 0x3d, 0x33, 0x6b, 0xc0, 0xf7, 0x49, 0xe0, 0x65, 0x12, 0x0b, 0x4f, 0x60, - 0x73, 0x57, 0x1e, 0xf4, 0x01, 0xe3, 0x89, 0xdd, 0x0f, 0xcf, 0x41, 0x6d, - 0xa8, 0x8c, 0xfb, 0x8e, 0x61, 0x4c, 0xd9, 0xe1, 0x4a, 0x1f, 0x58, 0xc7, - 0x60, 0x7c, 0xc4, 0xe7, 0x40, 0x43, 0x2b, 0xd4, 0xc8, 0x60, 0x63, 0x95, - 0x30, 0x9a, 0x31, 0x65, 0xc0, 0x13, 0xe9, 0x5b, 0xf9, 0x62, 0x91, 0x05, - 0x78, 0x0d, 0x11, 0x52, 0x8c, 0x86, 0xc5, 0x70, 0x86, 0x1a, 0x97, 0x70, - 0xf0, 0xde, 0x3b, 0xf8, 0x30, 0x7c, 0x2a, 0x1a, 0x3c, 0x14, 0xe6, 0x6e, - 0x6c, 0x1e, 0x2c, 0x1b, 0x99, 0x90, 0x8e, 0xa0, 0xb0, 0xe6, 0xbc, 0xb7, - 0x58, 0x8d, 0x06, 0x89, 0x74, 0x21, 0xad, 0x09, 0x58, 0x1f, 0x84, 0x07, - 0xff, 0xf1, 0x50, 0x80, 0x28, 0xbf, 0xfc, 0x21, 0x1a, 0xcf, 0xfe, 0xdf, - 0x74, 0x00, 0x00, 0xa1, 0xb1, 0x53, 0x55, 0x82, 0x54, 0x33, 0x11, 0x42, - 0x00, 0x00, 0x3a, 0xec, 0x3a, 0xed, 0xe6, 0x4a, 0xfb, 0xef, 0x24, 0xd5, - 0xe6, 0xb4, 0x1a, 0x58, 0x98, 0x17, 0x93, 0x27, 0xfe, 0x03, 0xf1, 0xaa, - 0x59, 0x0b, 0x2d, 0x76, 0x5b, 0x7e, 0xc4, 0x21, 0xa2, 0x45, 0x0a, 0x4d, - 0x78, 0x00, 0x9c, 0xf1, 0xcd, 0xca, 0x34, 0x71, 0x26, 0xc1, 0x60, 0x54, - 0x1d, 0xba, 0xba, 0x1a, 0x02, 0xbe, 0x21, 0x76, 0xee, 0xdf, 0x57, 0xe7, - 0x8e, 0x68, 0xfd, 0x39, 0xab, 0x47, 0x27, 0x9e, 0x77, 0x93, 0x11, 0x7c, - 0xfa, 0xf5, 0xf5, 0xfa, 0x20, 0xab, 0xe4, 0xb9, 0x75, 0xe7, 0xd3, 0xd2, - 0xba, 0x0a, 0x85, 0x43, 0x47, 0x20, 0x4d, 0xd0, 0x0d, 0x76, 0x19, 0xda, - 0x01, 0x0d, 0x36, 0x01, 0x2b, 0x5f, 0x6d, 0xed, 0x97, 0xdf, 0x69, 0xac, - 0xb5, 0xae, 0xf1, 0x54, 0xb1, 0xa6, 0xb4, 0xd5, 0x6e, 0xc4, 0xd1, 0x9a, - 0xdb, 0xc1, 0x5e, 0x0f, 0x65, 0x18, 0xe8, 0x6a, 0xc4, 0xf4, 0xf6, 0x3a, - 0x26, 0x9a, 0xc3, 0x0a, 0x6e, 0x08, 0x09, 0x5d, 0x9a, 0x54, 0x6c, 0xb7, - 0x75, 0xb7, 0x84, 0x4c, 0x52, 0xad, 0xcc, 0xd9, 0xe3, 0x6b, 0xbf, 0xc7, - 0x42, 0xe5, 0xeb, 0x97, 0x16, 0x6b, 0x8c, 0x78, 0x58, 0x34, 0xcc, 0xc0, - 0x84, 0x40, 0x94, 0xa2, 0x29, 0x0b, 0x4a, 0x49, 0xa0, 0xc3, 0x29, 0xc4, - 0x2d, 0x40, 0xe7, 0x6d, 0x93, 0xb1, 0x88, 0x12, 0xca, 0x17, 0xe7, 0x17, - 0xeb, 0xd3, 0x8e, 0xaa, 0x85, 0x05, 0x66, 0x86, 0x7e, 0x94, 0x7f, 0x2e, - 0xff, 0x4f, 0xd0, 0x28, 0x6d, 0x66, 0x28, 0x23, 0x11, 0x4b, 0xf1, 0x00, - 0x00, 0x00, 0x2f, 0x51, 0x5b, 0xba, 0xa4, 0xa9, 0xb8, 0xa0, 0x5d, 0x37, - 0x9a, 0x9b, 0xec, 0x22, 0x96, 0x33, 0x21, 0x4f, 0x52, 0x03, 0x1a, 0x60, - 0xe9, 0xaf, 0x73, 0x42, 0x41, 0x65, 0xf5, 0x88, 0xde, 0xde, 0x9c, 0xb4, - 0x9f, 0x41, 0x8d, 0x48, 0xc5, 0x7d, 0xc0, 0x39, 0xcb, 0x5d, 0xcf, 0x4c, - 0x54, 0xb3, 0xb8, 0x93, 0x03, 0xec, 0x0a, 0xc6, 0xf1, 0x2e, 0x0d, 0xc0, - 0xd2, 0x55, 0xa6, 0x4d, 0x90, 0xb3, 0x33, 0xe9, 0xe7, 0x77, 0xe2, 0xe3, - 0x2b, 0xe1, 0x5f, 0x1e, 0x55, 0xd4, 0xb9, 0x5c, 0x11, 0x94, 0xd6, 0x8b, - 0xb7, 0xff, 0xf1, 0x50, 0x80, 0x2a, 0x1f, 0xfc, 0x21, 0x1a, 0xcf, 0x7e, - 0xf6, 0x54, 0x00, 0x08, 0xa1, 0xb1, 0xc2, 0xdd, 0x22, 0x65, 0x23, 0x28, - 0x65, 0x83, 0x2c, 0x65, 0xb3, 0xcf, 0x39, 0x79, 0x7e, 0x7d, 0xcf, 0xf4, - 0xc6, 0xb5, 0x78, 0x88, 0xa8, 0x12, 0xdb, 0x87, 0x24, 0x98, 0x12, 0xaa, - 0x69, 0x5c, 0x8f, 0x2b, 0x5e, 0x22, 0x27, 0x7f, 0xe8, 0xd3, 0x3b, 0x3a, - 0x7e, 0xfc, 0x73, 0x88, 0x57, 0x1f, 0xed, 0xf0, 0xac, 0xb5, 0x2a, 0xe9, - 0x41, 0xef, 0x16, 0x8c, 0xd7, 0x26, 0x92, 0xdf, 0x8e, 0xfe, 0x0c, 0x5a, - 0xb9, 0x2a, 0x95, 0x99, 0xb3, 0x31, 0x63, 0x87, 0xa7, 0xd1, 0xcb, 0x89, - 0x24, 0x80, 0xd6, 0xc3, 0xc9, 0xc1, 0x8b, 0x13, 0x94, 0x27, 0xf0, 0x25, - 0xfe, 0xb1, 0x45, 0x96, 0x16, 0x05, 0xeb, 0x10, 0x25, 0x73, 0x84, 0x67, - 0x55, 0x14, 0x66, 0x6e, 0xe2, 0x33, 0x37, 0x31, 0x5a, 0xc2, 0xd7, 0x37, - 0xd3, 0x2a, 0xdd, 0x2a, 0x91, 0x3c, 0x23, 0x2a, 0xcd, 0x05, 0x46, 0x72, - 0xae, 0x92, 0xef, 0xd3, 0x32, 0x01, 0x81, 0x29, 0x5c, 0x85, 0xa8, 0xa1, - 0x96, 0x2b, 0x5a, 0x21, 0x2e, 0x44, 0x9c, 0xa5, 0x96, 0x25, 0x02, 0xd6, - 0x2e, 0x32, 0xb5, 0x8f, 0x7a, 0xb3, 0xd4, 0x8c, 0x47, 0xec, 0x80, 0xae, - 0x50, 0x47, 0x43, 0x03, 0x52, 0x57, 0xb2, 0xba, 0xed, 0x4b, 0xd4, 0xd5, - 0x8c, 0xdf, 0xad, 0x65, 0xa9, 0x0e, 0xab, 0xee, 0x7a, 0x8a, 0x24, 0x4a, - 0x58, 0x42, 0xc7, 0x03, 0x9a, 0x25, 0xd2, 0x84, 0x20, 0xd7, 0xde, 0x75, - 0x4a, 0xb4, 0xd0, 0xc3, 0x5c, 0x51, 0x3d, 0xf2, 0x49, 0x22, 0x00, 0x6b, - 0x03, 0x3d, 0xd5, 0x2a, 0x80, 0x00, 0x15, 0xd3, 0xa6, 0x48, 0x60, 0x0f, - 0x29, 0x4f, 0x64, 0xa1, 0x43, 0x6b, 0x77, 0xfc, 0x40, 0x00, 0x00, 0x09, - 0x7c, 0xfc, 0x66, 0x54, 0xcc, 0xba, 0x56, 0xb7, 0x00, 0xaf, 0xd9, 0x4c, - 0x45, 0x20, 0x3d, 0x8c, 0xa4, 0xd0, 0x21, 0x45, 0x1f, 0xae, 0x28, 0xd3, - 0x28, 0xfc, 0xd4, 0x00, 0xfa, 0x52, 0x9d, 0x03, 0x55, 0xc9, 0xa3, 0x23, - 0x7e, 0xb7, 0xca, 0xd6, 0x5d, 0x36, 0xab, 0x97, 0x9f, 0x63, 0xf4, 0xb2, - 0xa3, 0x4e, 0x2e, 0x6f, 0xc7, 0xc5, 0xe5, 0xb6, 0x37, 0xc8, 0x72, 0xb5, - 0xfc, 0xb8, 0xec, 0x76, 0xf2, 0xc1, 0x95, 0x86, 0x71, 0xd1, 0x92, 0xb3, - 0x51, 0x1b, 0x67, 0xae, 0x9b, 0xca, 0x85, 0x98, 0x9b, 0xe2, 0xe0, 0x5b, - 0x80, 0xff, 0xf1, 0x50, 0x80, 0x3b, 0x7f, 0xfc, 0x20, 0xa1, 0x1a, 0xd6, - 0xb8, 0x1b, 0xa5, 0x52, 0xc2, 0x50, 0xb2, 0x00, 0x00, 0x07, 0xdf, 0xec, - 0x01, 0xfc, 0xe6, 0x7d, 0x4f, 0x5e, 0xdc, 0xa7, 0x59, 0xa0, 0xda, 0x9f, - 0x55, 0xd6, 0xb2, 0xe1, 0xaf, 0x89, 0xac, 0x1e, 0x36, 0x81, 0xb4, 0x8b, - 0x47, 0x32, 0x11, 0x03, 0x3e, 0x8b, 0xf9, 0x85, 0x40, 0x28, 0x28, 0xcc, - 0xde, 0x71, 0x2d, 0xe1, 0x1a, 0xee, 0x14, 0x35, 0xb9, 0x84, 0xd6, 0x5b, - 0x67, 0x3a, 0x46, 0x7d, 0x8f, 0x0a, 0x54, 0xdc, 0x15, 0x9c, 0x46, 0x78, - 0x61, 0x1b, 0x2d, 0x5f, 0xe0, 0xd2, 0x8e, 0x34, 0x10, 0x16, 0x6f, 0x8f, - 0x8d, 0xf8, 0x11, 0x40, 0x89, 0x6c, 0xf6, 0xa8, 0x00, 0x03, 0x8f, 0x89, - 0x20, 0x05, 0x9b, 0x00, 0x01, 0x7b, 0x06, 0xd3, 0x3d, 0x6a, 0xaa, 0xa7, - 0xc7, 0xb1, 0xb9, 0x2d, 0x4b, 0xc6, 0x18, 0x9e, 0x1d, 0xa5, 0x82, 0x2a, - 0x83, 0x7f, 0x25, 0x57, 0x2b, 0xd5, 0x6d, 0x17, 0xbe, 0xf1, 0x45, 0x0a, - 0x75, 0x74, 0x9e, 0x6b, 0x66, 0x93, 0x2c, 0x1a, 0x63, 0x1a, 0x8e, 0x92, - 0xf0, 0xa3, 0x68, 0x04, 0x88, 0xef, 0xe8, 0x88, 0x19, 0xa5, 0xe4, 0x0a, - 0xfc, 0xab, 0x32, 0x1e, 0x65, 0x69, 0xa5, 0x68, 0x6c, 0x0e, 0x0e, 0x26, - 0x83, 0xdf, 0x28, 0x12, 0xa9, 0x1b, 0x85, 0x8b, 0x24, 0xb4, 0x32, 0xf3, - 0xa8, 0x42, 0xe2, 0x46, 0x9d, 0xe0, 0x39, 0xdd, 0xdb, 0x4d, 0xb5, 0xf9, - 0x55, 0x6e, 0xf1, 0xb5, 0x8e, 0xf5, 0x61, 0xb3, 0x24, 0x7e, 0xde, 0x7d, - 0xa5, 0xd6, 0x44, 0x20, 0xdf, 0xbd, 0xa0, 0x14, 0xef, 0x3c, 0x73, 0xad, - 0x33, 0x80, 0xe5, 0x87, 0x49, 0xf6, 0xcb, 0x9e, 0x10, 0x1d, 0x55, 0xf9, - 0x2b, 0xb1, 0xad, 0x2a, 0x12, 0xad, 0x6b, 0x84, 0x29, 0x8d, 0x0c, 0xa0, - 0x00, 0x00, 0x00, 0xb3, 0x5f, 0x1e, 0x7c, 0x7c, 0x73, 0xa8, 0x03, 0x56, - 0x64, 0x11, 0x7f, 0xce, 0x84, 0x89, 0xf5, 0x45, 0x38, 0x7c, 0x7d, 0x47, - 0xbf, 0x5b, 0x5e, 0x2d, 0x04, 0x58, 0x60, 0x1a, 0x15, 0x79, 0xae, 0x12, - 0x18, 0xe4, 0x93, 0x03, 0xaa, 0xed, 0xdb, 0x97, 0x78, 0x46, 0xb3, 0x0c, - 0x8d, 0x5f, 0x3c, 0x98, 0xd0, 0x57, 0x97, 0x19, 0xce, 0x0b, 0x77, 0xfb, - 0xc0, 0x09, 0x67, 0xaa, 0x88, 0x07, 0xcd, 0x72, 0xf8, 0xe5, 0x32, 0x56, - 0xfb, 0xbd, 0xd7, 0x75, 0xfb, 0x66, 0x1a, 0xb7, 0x01, 0xae, 0x8c, 0x7d, - 0xf0, 0xac, 0xa8, 0xeb, 0x4f, 0xd7, 0x8a, 0xe8, 0x47, 0x4c, 0xd4, 0x2b, - 0xb9, 0x0b, 0xf9, 0xa8, 0xc4, 0x97, 0x27, 0x5b, 0x82, 0x01, 0x6b, 0x60, - 0xa2, 0x37, 0xd0, 0x09, 0x97, 0x2b, 0x50, 0x72, 0x62, 0xee, 0xf3, 0xeb, - 0xae, 0xba, 0xdf, 0xe4, 0xbe, 0xc7, 0xad, 0x82, 0x35, 0x4a, 0x8b, 0x30, - 0x55, 0xda, 0x5a, 0x10, 0x02, 0x80, 0xbc, 0x10, 0x80, 0x40, 0x0c, 0x83, - 0x44, 0xa6, 0xba, 0xb2, 0xa7, 0x83, 0x92, 0xef, 0x63, 0x62, 0x0c, 0x5d, - 0xb3, 0x1a, 0x44, 0x08, 0x9c, 0x9d, 0xce, 0x87, 0x8a, 0xa9, 0xaa, 0xc0, - 0xe7, 0x84, 0x83, 0x66, 0x6e, 0x3c, 0x5a, 0xc9, 0x64, 0xb2, 0x94, 0x55, - 0xc5, 0xaa, 0x40, 0x90, 0x96, 0xd0, 0x5b, 0x94, 0x9b, 0x50, 0xd9, 0x28, - 0x16, 0x74, 0x18, 0xb4, 0x8f, 0x35, 0x8b, 0x5e, 0x5a, 0x66, 0x37, 0x4a, - 0x40, 0x4e, 0xe3, 0xc4, 0xa1, 0x14, 0xa3, 0x5d, 0x33, 0xa5, 0x8b, 0x65, - 0x71, 0x13, 0x9e, 0xa7, 0xee, 0xfc, 0x40, 0x70, 0xff, 0xf1, 0x50, 0x80, - 0x35, 0x5f, 0xfc, 0x20, 0xa8, 0x1a, 0xd4, 0xa5, 0x69, 0xa5, 0x0a, 0xc6, - 0x03, 0xfd, 0x3f, 0xad, 0x01, 0x29, 0x38, 0xec, 0x35, 0x6d, 0x5f, 0xbd, - 0xfb, 0x57, 0x95, 0xde, 0xfc, 0xbd, 0x58, 0x7b, 0xf3, 0x2a, 0xdf, 0x21, - 0x6f, 0x2c, 0x13, 0x07, 0x23, 0x12, 0x54, 0xc6, 0x91, 0x08, 0x8e, 0xe8, - 0x40, 0x0c, 0xf9, 0xf8, 0x45, 0xb1, 0x29, 0x71, 0xaf, 0xd5, 0x36, 0xa9, - 0xd3, 0x74, 0xb8, 0x0f, 0x96, 0x9b, 0x42, 0x97, 0x7b, 0xbd, 0x54, 0xbf, - 0x97, 0x4b, 0x5b, 0x9b, 0x82, 0x00, 0xfb, 0xfb, 0x41, 0xa6, 0x88, 0x59, - 0xc3, 0xc5, 0x9b, 0x1c, 0xb8, 0x6c, 0xa3, 0x67, 0x77, 0x51, 0x9e, 0xd7, - 0xd4, 0xf8, 0xf9, 0xd0, 0x03, 0x3f, 0x59, 0xcb, 0x20, 0x10, 0x30, 0x71, - 0x67, 0xb0, 0xf9, 0xd1, 0x48, 0x3e, 0xa3, 0x77, 0x44, 0x57, 0x22, 0x5a, - 0x3a, 0x8d, 0x98, 0xe4, 0xf1, 0x80, 0x0f, 0xef, 0xf6, 0xe4, 0x74, 0xaf, - 0x11, 0x2d, 0xd2, 0x16, 0x1e, 0x03, 0xf8, 0xef, 0xa9, 0xfd, 0x70, 0x7d, - 0x41, 0x80, 0xe0, 0x15, 0xc6, 0x21, 0xde, 0x7b, 0x1f, 0x2b, 0xe5, 0xba, - 0x65, 0xba, 0x63, 0x18, 0x6b, 0x09, 0x16, 0x58, 0x45, 0x24, 0x30, 0x82, - 0x15, 0xae, 0xe9, 0x4c, 0x77, 0xc7, 0x4d, 0x58, 0xb5, 0xb0, 0xaf, 0x75, - 0x8a, 0x53, 0xf4, 0xba, 0x7c, 0xee, 0xa3, 0xc6, 0xe4, 0x52, 0x83, 0x18, - 0x69, 0x6e, 0x45, 0x24, 0x52, 0x43, 0x92, 0x0e, 0xa9, 0xce, 0xa8, 0x65, - 0x28, 0x51, 0x9e, 0x4c, 0xd3, 0x66, 0x12, 0x31, 0x21, 0x34, 0xd9, 0x84, - 0x91, 0xad, 0x24, 0xee, 0xda, 0x70, 0xc8, 0x03, 0xa0, 0x0f, 0x6f, 0x8d, - 0x68, 0xff, 0xa0, 0x2f, 0x8f, 0x17, 0x7e, 0x7e, 0xc2, 0xf3, 0xe3, 0xae, - 0xf3, 0xeb, 0x81, 0x99, 0x63, 0xce, 0x14, 0x6e, 0xfe, 0xf0, 0x9a, 0x7e, - 0x64, 0x75, 0x58, 0x82, 0xd0, 0xb1, 0x2c, 0x61, 0xd1, 0x20, 0x0e, 0x98, - 0xf4, 0x9b, 0xc0, 0x46, 0xe6, 0xf9, 0x67, 0xe2, 0x93, 0x4d, 0x33, 0xb9, - 0x5c, 0x7c, 0xdd, 0x25, 0x76, 0x31, 0x79, 0x2b, 0x31, 0xf3, 0x65, 0xb3, - 0xf3, 0x4d, 0x2a, 0x34, 0xf3, 0x32, 0xcf, 0x69, 0x38, 0x87, 0x1e, 0xc9, - 0x08, 0x6f, 0x9a, 0xb3, 0x3b, 0x40, 0x9d, 0xb6, 0x1f, 0x89, 0xd7, 0xd3, - 0x0a, 0x80, 0xc2, 0xbd, 0xba, 0x54, 0xac, 0xd5, 0x77, 0x12, 0x90, 0x3a, - 0x9a, 0xd9, 0xbb, 0xfa, 0x8a, 0x83, 0x10, 0x24, 0xa2, 0x0a, 0x5f, 0x19, - 0x4d, 0x3a, 0xbe, 0x15, 0x8f, 0x3f, 0x0f, 0xf1, 0xf6, 0xf8, 0xd5, 0x25, - 0x9e, 0x6c, 0x48, 0x4d, 0x32, 0x22, 0x6e, 0x8b, 0x22, 0x66, 0x59, 0x41, - 0xb4, 0xb1, 0xbb, 0xdc, 0x0e, 0x1d, 0x2b, 0x06, 0x1a, 0x23, 0xef, 0x4e, - 0x56, 0x62, 0x7a, 0x9a, 0x32, 0xe0, 0x40, 0x46, 0x53, 0x71, 0x5d, 0x6e, - 0x5d, 0x1a, 0xa1, 0xab, 0x80, 0x60, 0x5f, 0x07, 0xd1, 0x93, 0x01, 0x5a, - 0x14, 0x4c, 0x95, 0xcc, 0x6e, 0xa5, 0x02, 0xb5, 0xce, 0xe1, 0x59, 0xa6, - 0x26, 0xce, 0x2a, 0xa2, 0xeb, 0x1b, 0x58, 0x2b, 0x87, 0x85, 0xa3, 0x43, - 0x41, 0xf0, 0xff, 0xf1, 0x50, 0x80, 0x37, 0xff, 0xfc, 0x20, 0xa2, 0x1a, - 0xd6, 0xb6, 0x14, 0xb8, 0xc6, 0x84, 0x52, 0x31, 0x40, 0x00, 0x05, 0xe1, - 0x79, 0xd7, 0x6b, 0xf3, 0x57, 0x74, 0xf3, 0x5c, 0xfb, 0x3a, 0xc8, 0x23, - 0x12, 0x2a, 0x0e, 0x0e, 0xcd, 0xd0, 0x92, 0x92, 0x54, 0x7c, 0x44, 0x96, - 0x47, 0x9b, 0x99, 0x28, 0x8e, 0xa1, 0x28, 0x06, 0xa5, 0x13, 0x65, 0x70, - 0x83, 0x49, 0x15, 0x7a, 0x29, 0xcd, 0x29, 0xf3, 0x7d, 0x6f, 0x05, 0xfa, - 0x1c, 0xe7, 0x18, 0xa4, 0xbb, 0xe4, 0xf8, 0x2c, 0x8b, 0xfb, 0x46, 0x50, - 0xc9, 0x8e, 0x38, 0x4e, 0x59, 0x49, 0x57, 0xf4, 0x1e, 0x3c, 0xef, 0x92, - 0x15, 0x08, 0x85, 0x6f, 0xfa, 0x0f, 0x4f, 0x21, 0x28, 0x31, 0x5e, 0xa1, - 0x01, 0x55, 0x14, 0xba, 0xc8, 0x57, 0x36, 0x40, 0x03, 0xcf, 0xa2, 0x03, - 0xdc, 0x34, 0x62, 0x3e, 0x87, 0x1e, 0x6b, 0x82, 0xa8, 0x16, 0x57, 0xbf, - 0x56, 0xca, 0xa3, 0xad, 0xa5, 0x8a, 0xba, 0xb0, 0x17, 0xb2, 0xe5, 0xb2, - 0xbe, 0xb1, 0xc8, 0x99, 0x98, 0x41, 0x5c, 0x14, 0x0f, 0xc1, 0x22, 0x73, - 0x10, 0x8b, 0x1a, 0xfc, 0x2e, 0x2e, 0x59, 0x8c, 0x61, 0xa8, 0xd6, 0x2b, - 0x5f, 0x5c, 0x77, 0x56, 0x26, 0xa2, 0xe6, 0x17, 0x83, 0x1f, 0x3e, 0xc8, - 0xa1, 0x10, 0x2d, 0x61, 0x03, 0xdd, 0x25, 0x2f, 0x65, 0x33, 0xb2, 0x40, - 0x22, 0x11, 0x18, 0xd5, 0xfc, 0x91, 0x42, 0x89, 0x5e, 0x11, 0xe8, 0x6c, - 0x77, 0xea, 0xdb, 0xa3, 0x2c, 0x2f, 0xd0, 0xa1, 0x51, 0xb5, 0xca, 0x2f, - 0xf3, 0xed, 0x8b, 0x71, 0x99, 0xdc, 0x3b, 0xa6, 0x4c, 0x84, 0xc3, 0x77, - 0x91, 0xd6, 0x88, 0xaf, 0xab, 0x8d, 0xb5, 0xe9, 0xa3, 0x7a, 0xd6, 0xb7, - 0x33, 0x18, 0x52, 0x86, 0x62, 0x80, 0x00, 0x00, 0x25, 0xef, 0x55, 0x75, - 0xed, 0x0e, 0xfd, 0xae, 0xef, 0x54, 0x1a, 0x6c, 0xd2, 0x56, 0xf0, 0xb7, - 0x34, 0x03, 0x1d, 0x08, 0xb1, 0x12, 0x8d, 0x44, 0xf7, 0xaa, 0xa9, 0x4a, - 0x78, 0x85, 0xcf, 0x46, 0x0c, 0xa0, 0xc5, 0xe6, 0xd1, 0x7c, 0x61, 0xbb, - 0x0a, 0xe8, 0xac, 0xbf, 0xc5, 0xfd, 0x78, 0x6a, 0xc7, 0x05, 0x06, 0x73, - 0xa7, 0xde, 0xc0, 0x93, 0xc9, 0x32, 0x23, 0x6b, 0x94, 0x71, 0x69, 0x89, - 0x6f, 0x69, 0xe4, 0xaa, 0x62, 0xe5, 0x78, 0x80, 0x09, 0xb8, 0x83, 0x82, - 0x84, 0xbb, 0x29, 0xc3, 0x89, 0x93, 0x3f, 0x3e, 0x7e, 0xad, 0xdd, 0x5a, - 0x08, 0xcb, 0xa7, 0xa4, 0xb2, 0x96, 0xa9, 0x35, 0x70, 0x3c, 0xdf, 0x52, - 0x81, 0xfd, 0x8a, 0xde, 0x0f, 0xe6, 0xa4, 0x15, 0x8d, 0x44, 0x72, 0x50, - 0x51, 0x30, 0xac, 0xe8, 0x07, 0x7e, 0xe9, 0x7c, 0x6d, 0x4e, 0xed, 0x45, - 0x49, 0xe2, 0x4b, 0xe8, 0x60, 0x37, 0x15, 0xa9, 0x75, 0x85, 0x1e, 0xd6, - 0x10, 0xd0, 0xc9, 0x9a, 0xa6, 0xad, 0x78, 0xbf, 0x4a, 0x54, 0xf3, 0x36, - 0x82, 0x4f, 0x60, 0x08, 0xa0, 0x0b, 0xe2, 0xf8, 0x54, 0xd0, 0x4b, 0x32, - 0x38, 0x30, 0xe1, 0xd8, 0x2e, 0x8a, 0xc8, 0x0f, 0x4f, 0x83, 0xda, 0xd8, - 0xdb, 0x81, 0xcc, 0x92, 0x02, 0x88, 0x14, 0xa4, 0xfe, 0x4b, 0xe9, 0x23, - 0x10, 0x4a, 0x17, 0x95, 0x75, 0x3d, 0x97, 0x5d, 0xdd, 0xab, 0x8c, 0xf8, - 0x0b, 0x50, 0x4e, 0x0b, 0xc0, 0xff, 0xf1, 0x50, 0x80, 0x26, 0x7f, 0xfc, - 0x21, 0x1a, 0xcf, 0xd8, 0xfa, 0x5c, 0xff, 0x7f, 0xa3, 0xb5, 0xbb, 0x04, - 0xea, 0x66, 0x38, 0x00, 0x0e, 0x39, 0x5f, 0x5e, 0xaf, 0x3a, 0xec, 0x4e, - 0x3f, 0x5f, 0x9c, 0xfc, 0x73, 0xae, 0x37, 0x2e, 0xea, 0x03, 0xf3, 0x31, - 0x24, 0xd0, 0xf3, 0xbe, 0x3b, 0x51, 0x84, 0x28, 0xb3, 0x69, 0x35, 0x09, - 0xc2, 0x84, 0x37, 0xca, 0xac, 0xf0, 0x2d, 0x80, 0xe2, 0x37, 0x9b, 0x53, - 0x24, 0x61, 0xcb, 0xd9, 0xaf, 0xdb, 0xfd, 0x96, 0xec, 0x32, 0xcd, 0x66, - 0x84, 0xe1, 0xa4, 0xa3, 0x4a, 0x2e, 0x17, 0x2c, 0xee, 0x0c, 0xf1, 0xc2, - 0x7b, 0x8d, 0x29, 0x8c, 0xc2, 0x40, 0xbd, 0x1f, 0x6e, 0x69, 0x56, 0x9a, - 0x34, 0x4e, 0x85, 0x0c, 0x12, 0xfb, 0x72, 0x20, 0x36, 0x80, 0x5c, 0x90, - 0x79, 0x22, 0x8a, 0x89, 0xee, 0x98, 0xcb, 0xb7, 0x28, 0xec, 0xe6, 0x10, - 0x2e, 0x14, 0x5f, 0x61, 0x00, 0xa0, 0x1c, 0x31, 0x48, 0xe3, 0x53, 0x86, - 0xd3, 0xa9, 0xdf, 0x97, 0x2a, 0xd4, 0xd9, 0x51, 0x75, 0xb1, 0x0e, 0xed, - 0x5b, 0x7b, 0xb5, 0x33, 0x3d, 0xbb, 0xd4, 0x1e, 0xb9, 0x69, 0x10, 0x9a, - 0x01, 0x6f, 0x43, 0x46, 0x27, 0x15, 0xea, 0x84, 0x06, 0x22, 0x29, 0x04, - 0xc0, 0x94, 0x4e, 0xfb, 0x91, 0x06, 0x00, 0x89, 0xe6, 0xe3, 0x2d, 0x88, - 0x13, 0x1f, 0x24, 0x76, 0x7b, 0x11, 0x4e, 0xfa, 0x07, 0x1e, 0xec, 0x24, - 0x1e, 0x56, 0xa0, 0x19, 0x8b, 0x09, 0xb5, 0x10, 0x60, 0x1b, 0xf0, 0xf2, - 0x8b, 0xa5, 0x73, 0x4b, 0x56, 0xaa, 0xef, 0x41, 0x92, 0x01, 0xc8, 0xe8, - 0xe2, 0x12, 0x51, 0xc1, 0xa5, 0x0a, 0xbe, 0x90, 0x00, 0x0e, 0x39, 0x03, - 0xae, 0xeb, 0x77, 0x2f, 0x59, 0xcc, 0x4e, 0x39, 0xa2, 0x40, 0xb9, 0xdd, - 0x9e, 0x81, 0xe4, 0x84, 0x63, 0x3f, 0x93, 0x5b, 0x65, 0x7b, 0x3f, 0xb4, - 0x69, 0xd6, 0x7a, 0x9f, 0x8f, 0x78, 0x21, 0x50, 0x37, 0x6a, 0xfa, 0xe6, - 0xf8, 0x6d, 0x10, 0xe1, 0x1e, 0x5d, 0x50, 0x67, 0xf8, 0xca, 0x5f, 0x2c, - 0x63, 0x9b, 0x79, 0x54, 0xf5, 0x4f, 0x2e, 0x0a, 0xe8, 0x94, 0xdd, 0x44, - 0x09, 0xbf, 0xdf, 0xf8, 0x98, 0x60, 0xe2, 0xcc, 0x1d, 0xa4, 0xb5, 0x2e, - 0xff, 0xf1, 0x50, 0x80, 0x25, 0x9f, 0xfc, 0x21, 0x1a, 0xcf, 0xe5, 0xf6, - 0x56, 0x02, 0x00, 0xa2, 0xb4, 0xc1, 0x5d, 0xa6, 0x64, 0x23, 0x18, 0x00, - 0x07, 0xb7, 0xa3, 0xae, 0xf5, 0xbf, 0x6f, 0x5e, 0xde, 0x9a, 0x79, 0xef, - 0xae, 0xb3, 0xaf, 0x1f, 0x15, 0x9a, 0xd4, 0x0e, 0xde, 0x21, 0x9d, 0x4f, - 0x2d, 0x96, 0x22, 0xc0, 0xe6, 0x8f, 0xe3, 0x87, 0xb3, 0x12, 0xaf, 0x40, - 0x58, 0xec, 0x7c, 0xa5, 0xa2, 0x38, 0x8b, 0xf1, 0x47, 0x71, 0x86, 0x51, - 0x66, 0x65, 0xd0, 0xcc, 0xfe, 0xa5, 0xdb, 0x26, 0x73, 0x3a, 0xcb, 0xca, - 0x46, 0x10, 0x06, 0xcc, 0x24, 0xc6, 0xb3, 0xc3, 0xba, 0xd1, 0xac, 0xa6, - 0xae, 0x09, 0x01, 0xdf, 0x4e, 0x09, 0x4d, 0x54, 0x2c, 0xa2, 0x40, 0xc1, - 0x72, 0xb2, 0x63, 0xa9, 0xd3, 0x5c, 0x00, 0x33, 0x40, 0x07, 0x64, 0xb8, - 0x34, 0x62, 0x70, 0xe5, 0x67, 0x4b, 0xba, 0xbc, 0x11, 0x4f, 0x97, 0x60, - 0x4b, 0xd3, 0x7f, 0x1f, 0x83, 0x2d, 0x65, 0x23, 0xe3, 0xcf, 0x42, 0xc2, - 0xea, 0xe7, 0x8d, 0x1f, 0x6a, 0x4f, 0xc7, 0x72, 0x72, 0x37, 0x26, 0xbc, - 0xcd, 0xcd, 0x34, 0xce, 0x51, 0x64, 0xee, 0x67, 0x3e, 0xfa, 0x7b, 0x85, - 0x98, 0x14, 0x0c, 0xd1, 0x2d, 0x58, 0xd9, 0x49, 0x9c, 0x8e, 0xa8, 0x88, - 0xd0, 0xcc, 0xb5, 0x00, 0x03, 0xd3, 0x53, 0xd2, 0x80, 0x62, 0x68, 0x11, - 0x85, 0xc5, 0x78, 0x75, 0xad, 0x77, 0x38, 0x12, 0x01, 0x81, 0x38, 0xbb, - 0xeb, 0xee, 0xb1, 0x9a, 0x3b, 0x76, 0xf5, 0x5f, 0x9d, 0xfd, 0x8a, 0x69, - 0x11, 0x75, 0x73, 0x81, 0x95, 0x15, 0xa5, 0xe3, 0xf1, 0x01, 0xad, 0x80, - 0x17, 0x84, 0xaf, 0x6f, 0x7e, 0xa2, 0x09, 0xc7, 0x72, 0xa6, 0x48, 0x0a, - 0x74, 0x65, 0x03, 0xa7, 0x80, 0xa0, 0x51, 0x1c, 0x43, 0x86, 0x28, 0x00, - 0xcd, 0x73, 0x31, 0x37, 0x21, 0xdc, 0xe0, 0xa4, 0x0a, 0xae, 0xbb, 0xf8, - 0xee, 0x50, 0xb9, 0x65, 0x5b, 0xb4, 0x45, 0xe8, 0x4a, 0x9c, 0x58, 0xbe, - 0x32, 0xcb, 0x8d, 0x09, 0xfa, 0x9b, 0x61, 0xaf, 0x10, 0x5d, 0x02, 0xfb, - 0x1f, 0x54, 0x16, 0x32, 0x42, 0xb4, 0x82, 0x06, 0x2a, 0x2a, 0x8a, 0xbe, - 0xff, 0xf1, 0x50, 0x80, 0x38, 0xff, 0xfc, 0x20, 0xa2, 0x1a, 0xd6, 0xc9, - 0x58, 0x99, 0x0e, 0xc7, 0x00, 0x00, 0x12, 0xa5, 0x5d, 0xf2, 0x3f, 0x59, - 0x37, 0x7a, 0x75, 0xdd, 0xf1, 0x57, 0x81, 0xcd, 0x90, 0xd7, 0x3b, 0x07, - 0xcb, 0xaa, 0xe1, 0xd8, 0xe2, 0xcd, 0xa1, 0x96, 0x66, 0xa7, 0xf2, 0xc3, - 0xc2, 0xee, 0x89, 0xdc, 0x0a, 0xc0, 0x29, 0x48, 0x55, 0x87, 0x44, 0x91, - 0xf3, 0xcd, 0x65, 0x59, 0xea, 0xfc, 0xd7, 0x0b, 0x6a, 0x55, 0xd2, 0x44, - 0xc6, 0xe9, 0x67, 0x1a, 0xfc, 0xd8, 0x52, 0x49, 0xc4, 0x86, 0x6d, 0x6f, - 0xb9, 0x70, 0x42, 0x2c, 0x92, 0x33, 0xc3, 0x7e, 0xaf, 0x4c, 0x4e, 0xb9, - 0x63, 0x97, 0x5e, 0x11, 0x12, 0x94, 0xf2, 0x44, 0x12, 0xf9, 0x80, 0xae, - 0xe0, 0x2a, 0xdb, 0x99, 0xa5, 0x8f, 0xa2, 0xd7, 0x62, 0x65, 0xc4, 0x07, - 0xd0, 0x3d, 0x00, 0xa3, 0xf8, 0x4e, 0x28, 0x20, 0x96, 0xfa, 0x7b, 0x37, - 0xc9, 0xeb, 0x60, 0x15, 0xe5, 0xf4, 0x8b, 0xbb, 0xd5, 0xab, 0xb4, 0x35, - 0xe4, 0xfe, 0x4b, 0x7f, 0x02, 0x3a, 0x72, 0x24, 0x34, 0xf3, 0xfe, 0x74, - 0x72, 0x10, 0x66, 0x8f, 0xc9, 0x67, 0x98, 0x97, 0x63, 0xa0, 0xa8, 0x8c, - 0xeb, 0x96, 0xd8, 0xbd, 0x9c, 0x0e, 0x66, 0x58, 0x81, 0x22, 0x45, 0x50, - 0x6b, 0x24, 0x9e, 0x63, 0x35, 0x3a, 0xae, 0x85, 0x0c, 0x3b, 0xef, 0x98, - 0x1e, 0xe9, 0x9e, 0x47, 0x92, 0x47, 0x24, 0x95, 0x27, 0x4a, 0xc4, 0xfe, - 0x7a, 0xc2, 0xca, 0x80, 0xe8, 0x5c, 0xba, 0xad, 0xb2, 0xc1, 0x90, 0xb9, - 0xc8, 0x52, 0x59, 0x44, 0x6b, 0x2a, 0xa5, 0x10, 0xcd, 0x57, 0xb2, 0x33, - 0x90, 0xa8, 0x5d, 0xab, 0x44, 0x55, 0xac, 0x74, 0xb5, 0x49, 0xad, 0x92, - 0x00, 0x00, 0x00, 0x79, 0xf1, 0xed, 0xc3, 0xd7, 0xd3, 0xe1, 0x26, 0xef, - 0x7c, 0x72, 0x30, 0xca, 0xec, 0xbf, 0xc0, 0x95, 0x87, 0xd6, 0xf4, 0x9e, - 0x3e, 0xe5, 0xa9, 0xaa, 0xba, 0x50, 0x59, 0xa5, 0x84, 0x7a, 0x0e, 0xfa, - 0x01, 0xc4, 0x2c, 0xdc, 0x95, 0x87, 0x14, 0x53, 0x81, 0x1c, 0x10, 0x42, - 0x87, 0xa1, 0x23, 0xd3, 0x37, 0x7d, 0x5d, 0x93, 0x2f, 0x2a, 0x95, 0x6f, - 0xda, 0xb9, 0xe5, 0x9e, 0x37, 0xcf, 0x78, 0x17, 0x73, 0xeb, 0xc0, 0x98, - 0x01, 0xbd, 0xbc, 0xd0, 0x93, 0x8a, 0x0b, 0xf2, 0x86, 0x80, 0xd7, 0xe9, - 0x10, 0xe8, 0x30, 0xb8, 0xca, 0x12, 0x6a, 0xe4, 0xeb, 0x55, 0xf3, 0x7c, - 0x51, 0x94, 0xe5, 0xce, 0xe2, 0xcd, 0x4e, 0xbe, 0x59, 0x2d, 0xbd, 0xee, - 0x82, 0xed, 0x1e, 0xde, 0x09, 0x4f, 0x24, 0x7f, 0xa5, 0xfd, 0x52, 0xf1, - 0x86, 0x87, 0x12, 0x6b, 0x73, 0xd4, 0x75, 0x90, 0xcf, 0x3a, 0x83, 0xc8, - 0x72, 0xc5, 0xad, 0xa8, 0xb9, 0xa0, 0x2a, 0x55, 0x7a, 0x9f, 0x16, 0xb1, - 0x6e, 0xdf, 0x4d, 0xb6, 0x15, 0xd4, 0x45, 0x8b, 0x40, 0xd5, 0x9d, 0xc8, - 0xbc, 0xe7, 0x7e, 0x45, 0x34, 0xdb, 0xca, 0x63, 0xb6, 0x9c, 0x8a, 0x49, - 0xe2, 0x29, 0xbc, 0x2e, 0x06, 0xb9, 0x4a, 0xcd, 0x2e, 0x51, 0x84, 0x94, - 0x99, 0xb1, 0xa0, 0xb4, 0xeb, 0x04, 0x76, 0xd3, 0x63, 0x55, 0x0c, 0x65, - 0x23, 0xd1, 0x2b, 0x5c, 0x00, 0x80, 0x52, 0x76, 0xdf, 0x6d, 0x71, 0xcb, - 0xa8, 0x65, 0x12, 0x5c, 0x0a, 0xeb, 0x6c, 0x5b, 0x16, 0x2e, 0x4e, 0xff, - 0xf1, 0x50, 0x80, 0x32, 0xdf, 0xfc, 0x20, 0xa9, 0x1a, 0xcc, 0x8a, 0x09, - 0xc9, 0x10, 0x23, 0x42, 0x80, 0x1e, 0x7c, 0x7c, 0x7b, 0xfd, 0x7c, 0xeb, - 0x7e, 0xcf, 0x5f, 0x7f, 0xb7, 0x99, 0xbf, 0x37, 0xc5, 0xfa, 0x38, 0x9e, - 0x3c, 0x6b, 0xeb, 0x59, 0xad, 0x0b, 0x7e, 0x5f, 0xbf, 0x9b, 0x34, 0x7a, - 0x88, 0x5c, 0x4e, 0xe1, 0xb8, 0x0c, 0xf2, 0x1c, 0x19, 0xa7, 0xf9, 0xae, - 0x93, 0x9d, 0xd1, 0xfd, 0x47, 0x3e, 0xf5, 0x2e, 0xaa, 0xb3, 0x2d, 0x96, - 0xf9, 0xb6, 0x1f, 0x38, 0xe0, 0x87, 0x17, 0xd6, 0x0e, 0x7e, 0x49, 0xed, - 0xe8, 0x4d, 0xc8, 0xb1, 0x28, 0x5c, 0x6a, 0x60, 0x1a, 0x18, 0x36, 0x39, - 0x40, 0x0f, 0x05, 0x4c, 0x4b, 0x03, 0xf1, 0xa0, 0x36, 0x9e, 0x33, 0xd9, - 0x1f, 0x4a, 0xcf, 0xcf, 0xcf, 0xbc, 0x14, 0x5d, 0xc7, 0x9f, 0x63, 0x0b, - 0x28, 0xda, 0xe7, 0xdf, 0x00, 0xec, 0x6e, 0xe1, 0x9b, 0xd4, 0xdd, 0xe4, - 0x23, 0x5e, 0xc8, 0xda, 0x6c, 0x8b, 0x6e, 0x66, 0x38, 0x07, 0xd2, 0xf9, - 0xe7, 0x8f, 0x09, 0x36, 0xc0, 0x09, 0x47, 0xfa, 0x48, 0xb8, 0x93, 0x4f, - 0x9f, 0xc5, 0xf1, 0x70, 0x0b, 0xbd, 0x78, 0x04, 0x74, 0x8c, 0x6b, 0x00, - 0x08, 0xcf, 0xc9, 0x98, 0x70, 0x02, 0x33, 0xe2, 0x79, 0x58, 0x7e, 0xbc, - 0x7b, 0x00, 0xf4, 0x69, 0xbf, 0x89, 0xb3, 0xa0, 0x3c, 0x5f, 0x5f, 0xe9, - 0x34, 0xbd, 0x0a, 0x66, 0xc8, 0x48, 0xc4, 0x86, 0x84, 0xbd, 0xb6, 0xa9, - 0x69, 0xb4, 0x60, 0x00, 0xfb, 0xf5, 0xf3, 0x7f, 0xab, 0xfe, 0x0e, 0xa7, - 0x8f, 0xdf, 0xdb, 0xc4, 0xf6, 0x9f, 0xf8, 0x3c, 0xf9, 0xfc, 0xfe, 0xba, - 0xed, 0xf1, 0xc0, 0xf4, 0x81, 0xa1, 0x62, 0x48, 0xd0, 0x1d, 0xf4, 0x2f, - 0x84, 0xd7, 0x44, 0x68, 0xf5, 0xb5, 0xd8, 0x5a, 0x8b, 0xa0, 0xf1, 0x6f, - 0xd1, 0x86, 0x51, 0x43, 0x11, 0x20, 0x09, 0x7b, 0xbc, 0x46, 0x54, 0x88, - 0xa4, 0x2d, 0x4d, 0x8a, 0xd9, 0x62, 0xa5, 0x15, 0x3a, 0x8e, 0xea, 0x8c, - 0xb7, 0x11, 0xd7, 0xe3, 0x5a, 0xd6, 0x31, 0xe0, 0x01, 0xb2, 0xe7, 0x28, - 0xc6, 0x00, 0xd2, 0xa8, 0xcf, 0x74, 0x30, 0x51, 0x51, 0x53, 0x9d, 0xc6, - 0x5e, 0x5f, 0x4f, 0xb6, 0x12, 0xa8, 0x42, 0x23, 0x11, 0x71, 0x1f, 0x1c, - 0x74, 0x6e, 0x3a, 0xfd, 0xde, 0xbf, 0xd7, 0x3b, 0xce, 0x78, 0xa8, 0x0a, - 0xa2, 0x96, 0xd2, 0x40, 0x87, 0x30, 0x41, 0x39, 0x8f, 0x23, 0xd7, 0x15, - 0x88, 0xec, 0x66, 0x73, 0x0f, 0x30, 0x2b, 0x01, 0x5f, 0xda, 0x4c, 0x7f, - 0xbc, 0xe7, 0x39, 0x90, 0xb4, 0x58, 0x26, 0x1e, 0x26, 0xde, 0x98, 0x70, - 0xad, 0x4b, 0x49, 0xce, 0x5b, 0x47, 0xf2, 0x98, 0xad, 0x19, 0x93, 0xd9, - 0x89, 0xb0, 0x90, 0xe2, 0x34, 0xc4, 0xda, 0x3a, 0x58, 0x10, 0x80, 0x0d, - 0x39, 0xd0, 0x01, 0x44, 0x80, 0x3f, 0xe3, 0xf8, 0x47, 0x43, 0x49, 0xa1, - 0xf6, 0x7b, 0x1d, 0x9f, 0xf7, 0x40, 0x70, 0x07, 0x80, 0xff, 0xf1, 0x50, - 0x80, 0x37, 0xbf, 0xfc, 0x20, 0xaf, 0x1a, 0xd0, 0xb4, 0x48, 0x99, 0x06, - 0x23, 0x43, 0xb1, 0x07, 0xd7, 0x5f, 0x1f, 0xd7, 0xd3, 0xf7, 0x67, 0x8e, - 0x7d, 0xba, 0x7b, 0xfb, 0x67, 0xbb, 0x1a, 0xbf, 0xbe, 0x4f, 0x9d, 0xdf, - 0x99, 0x57, 0x5c, 0x71, 0xae, 0x78, 0x05, 0xa3, 0x6f, 0x3d, 0xc0, 0x71, - 0x37, 0xa8, 0xeb, 0x62, 0x02, 0x01, 0x95, 0x05, 0xdb, 0x2d, 0xc8, 0x0a, - 0x6e, 0xe7, 0xdc, 0xb4, 0x8d, 0x7b, 0xd4, 0xb4, 0x00, 0xff, 0x37, 0xa3, - 0xee, 0x1f, 0xc1, 0x7b, 0x95, 0x2f, 0x5d, 0xd6, 0x27, 0x07, 0x74, 0x35, - 0xae, 0x5b, 0xfa, 0xd7, 0x5b, 0x46, 0x35, 0xca, 0x5c, 0xe7, 0xf0, 0xb6, - 0x20, 0x1d, 0x7b, 0xc9, 0xf7, 0x0c, 0xea, 0xc4, 0xe4, 0x7c, 0x20, 0xea, - 0xd4, 0xe9, 0x5e, 0x95, 0x49, 0x3c, 0xbb, 0xbc, 0xe1, 0x2d, 0x63, 0x5f, - 0x3d, 0xe1, 0x28, 0x50, 0x85, 0x12, 0x9c, 0xc2, 0x5a, 0x97, 0x36, 0x2f, - 0x63, 0x0d, 0x95, 0x64, 0x99, 0x76, 0x61, 0xd5, 0x5a, 0x67, 0x12, 0x59, - 0x95, 0xb4, 0x18, 0xa8, 0xd0, 0x22, 0xc3, 0x62, 0x88, 0x16, 0x84, 0xec, - 0xbe, 0x4b, 0x67, 0x2d, 0xb4, 0x11, 0x9f, 0x72, 0xfd, 0xea, 0x91, 0xf7, - 0xc3, 0x6b, 0x7f, 0xfe, 0xe7, 0xa0, 0xd7, 0x60, 0x9e, 0xf9, 0x78, 0x8c, - 0x87, 0x2f, 0x16, 0x92, 0x82, 0x69, 0xf0, 0xac, 0x15, 0xa1, 0xd8, 0xac, - 0x9c, 0xde, 0xb1, 0x08, 0xbe, 0xd2, 0x5e, 0xfa, 0x58, 0x58, 0x94, 0xa2, - 0x55, 0xca, 0x5d, 0x92, 0xd5, 0x38, 0x01, 0x93, 0x1d, 0xac, 0xbd, 0xcd, - 0x4c, 0x98, 0xd9, 0x98, 0xed, 0x22, 0x74, 0xb7, 0xac, 0x8b, 0x21, 0x17, - 0x05, 0xbc, 0x00, 0x3d, 0x16, 0x0b, 0xe3, 0x7d, 0x54, 0x9f, 0x20, 0xd4, - 0x54, 0xe3, 0xdc, 0xf8, 0xe3, 0xe7, 0xe2, 0x6f, 0x8c, 0xfc, 0x2e, 0x7f, - 0xb9, 0xdf, 0x80, 0x1b, 0x2c, 0x62, 0xa0, 0x2e, 0xcf, 0xfe, 0xe0, 0x4a, - 0x22, 0xef, 0xec, 0xfb, 0x83, 0x42, 0x39, 0xad, 0xf5, 0x22, 0x83, 0xc2, - 0x1a, 0x08, 0x28, 0x11, 0x91, 0x93, 0xbb, 0x88, 0x4d, 0x16, 0x6d, 0xe8, - 0x16, 0x9e, 0x35, 0x60, 0x25, 0x20, 0x00, 0x45, 0x6c, 0x86, 0xc8, 0xe7, - 0x67, 0x67, 0x8b, 0xb9, 0xc2, 0x8e, 0x14, 0x53, 0x8f, 0x0f, 0xdb, 0x63, - 0x01, 0x79, 0x30, 0x5a, 0x3a, 0x47, 0xd9, 0x7e, 0x3e, 0x4d, 0x7f, 0xbe, - 0x8a, 0x64, 0xbb, 0xcb, 0xb3, 0xba, 0x7e, 0x0c, 0x35, 0xf7, 0xff, 0xfc, - 0xfc, 0x11, 0x35, 0xe7, 0x50, 0x40, 0xf7, 0x60, 0x08, 0x81, 0x85, 0x9f, - 0x0f, 0xce, 0x99, 0x80, 0xd5, 0x4c, 0x01, 0x11, 0x41, 0x18, 0x02, 0x0c, - 0x60, 0xba, 0xed, 0xf2, 0x5b, 0x55, 0xc4, 0x42, 0x1b, 0xf0, 0x61, 0xfa, - 0x0b, 0xf1, 0x5b, 0xba, 0x90, 0x7a, 0xd6, 0x38, 0x0f, 0x64, 0xdc, 0x26, - 0x98, 0x34, 0x61, 0x4b, 0x97, 0xd0, 0x71, 0xc0, 0x26, 0xeb, 0x1d, 0xb7, - 0xf9, 0xbb, 0xb5, 0x06, 0x12, 0x10, 0x46, 0x8f, 0xbd, 0x6b, 0xd3, 0x15, - 0x27, 0x52, 0xd1, 0xd7, 0x42, 0x94, 0xd2, 0x1a, 0x29, 0xcc, 0x51, 0x50, - 0x27, 0x73, 0xec, 0xe7, 0x24, 0x8c, 0x11, 0x3a, 0x28, 0xc5, 0xca, 0xae, - 0xa6, 0xc1, 0x59, 0x82, 0x76, 0x4a, 0x65, 0x31, 0x72, 0xe0, 0xff, 0xf1, - 0x50, 0x80, 0x35, 0x5f, 0xfc, 0x20, 0xa8, 0x1a, 0xd0, 0xa6, 0x22, 0xb0, - 0x8a, 0x87, 0x62, 0x82, 0x50, 0x03, 0x5b, 0xbf, 0x6f, 0x9b, 0xc3, 0x8d, - 0x51, 0x35, 0x91, 0x7a, 0xdf, 0x9e, 0x37, 0x56, 0x2e, 0xfc, 0xbd, 0x62, - 0xbb, 0xdc, 0x46, 0xc2, 0x32, 0xc7, 0x18, 0x29, 0xea, 0x7b, 0x43, 0xf6, - 0xff, 0x8b, 0x8d, 0x11, 0x22, 0x21, 0x64, 0x2d, 0xa2, 0x35, 0x55, 0xe4, - 0xe8, 0x6a, 0xf5, 0x50, 0x69, 0xa5, 0x95, 0xe2, 0xbb, 0x6e, 0x71, 0xcd, - 0x1c, 0xa2, 0xda, 0x9b, 0x84, 0x66, 0xb7, 0x45, 0xc1, 0x1b, 0xdc, 0xe2, - 0xed, 0x30, 0xaa, 0x19, 0x2e, 0xe8, 0x5c, 0x8b, 0x17, 0xc2, 0xb3, 0x9a, - 0xc8, 0xf0, 0xfb, 0x10, 0x02, 0xb0, 0x11, 0xe0, 0xa4, 0x56, 0xc2, 0x1f, - 0x65, 0x26, 0x78, 0x05, 0x3f, 0xfa, 0xa9, 0x48, 0xa7, 0x32, 0x27, 0xb8, - 0xe8, 0xb7, 0x5c, 0x2e, 0x21, 0x4b, 0x88, 0xcc, 0xa1, 0xcb, 0x1a, 0x4c, - 0x5c, 0xaf, 0x08, 0xa3, 0xb3, 0x77, 0xe4, 0x62, 0xba, 0x33, 0x0b, 0x2d, - 0xad, 0xed, 0x90, 0xfe, 0xe1, 0x99, 0x8c, 0xc8, 0x96, 0xf3, 0xc2, 0x2b, - 0x81, 0xfa, 0xf6, 0x19, 0x95, 0x86, 0x36, 0xe7, 0xa9, 0x57, 0x55, 0xd7, - 0x9c, 0xf4, 0x58, 0x6d, 0x30, 0x14, 0x2a, 0x52, 0xf5, 0x5d, 0x29, 0xb8, - 0x24, 0xaa, 0x37, 0x5b, 0x15, 0x58, 0xb2, 0xdd, 0x3d, 0x06, 0xc6, 0x85, - 0x46, 0x89, 0x42, 0xa7, 0xb9, 0x96, 0x57, 0x74, 0xc8, 0xe5, 0x74, 0x40, - 0x4b, 0x33, 0x0a, 0xda, 0x74, 0xb3, 0x32, 0x86, 0x47, 0x95, 0xeb, 0xa9, - 0x7a, 0xd0, 0xe4, 0x49, 0xa1, 0x0e, 0xc5, 0x00, 0x6b, 0x61, 0x28, 0x4b, - 0xd8, 0xbe, 0xbd, 0x6b, 0xaa, 0x5d, 0x4d, 0x6f, 0xef, 0xc7, 0x33, 0x62, - 0x5f, 0xd7, 0xd0, 0x7b, 0xf0, 0xac, 0x56, 0x14, 0x5b, 0x1b, 0x41, 0xdf, - 0xea, 0xf5, 0x8a, 0xe1, 0x00, 0xa5, 0x0d, 0xa0, 0xb8, 0x6b, 0x60, 0x92, - 0xe3, 0x44, 0x03, 0xd1, 0x70, 0x6c, 0xa4, 0xd0, 0xb5, 0x84, 0xeb, 0xe9, - 0xed, 0x5c, 0x10, 0x5f, 0xba, 0xa8, 0xa6, 0xa3, 0x2b, 0x57, 0x17, 0x9a, - 0xd2, 0xed, 0x0d, 0xbf, 0x2d, 0x2a, 0x29, 0x8b, 0x2a, 0x38, 0xd1, 0x79, - 0x9a, 0xe0, 0x9d, 0xf4, 0x5e, 0x04, 0xf1, 0x57, 0x0c, 0x67, 0x63, 0x66, - 0x75, 0xbc, 0xb0, 0xb8, 0x74, 0xb6, 0x21, 0x3e, 0x83, 0x1e, 0x9b, 0x0c, - 0xe1, 0x79, 0xe7, 0x7f, 0xad, 0x15, 0x31, 0xdb, 0x71, 0xa1, 0x4f, 0x2a, - 0xa6, 0x81, 0x51, 0x5b, 0x7a, 0x1e, 0xdc, 0x9e, 0x38, 0x4a, 0xa4, 0x2a, - 0x86, 0x8e, 0x6a, 0x98, 0xdb, 0xe3, 0x45, 0x95, 0x4c, 0xc7, 0x1c, 0x05, - 0xe3, 0x4b, 0xb9, 0xa3, 0xe8, 0xde, 0x65, 0xce, 0xb9, 0x4d, 0xd6, 0xc2, - 0x9e, 0xf9, 0x2e, 0x01, 0xed, 0xaf, 0xb6, 0xa2, 0x35, 0x66, 0xda, 0x5d, - 0xf6, 0x69, 0x82, 0xce, 0xc5, 0x6d, 0x9e, 0xab, 0x6c, 0x1a, 0x55, 0xfb, - 0xc8, 0xfe, 0xc2, 0xee, 0x1a, 0x54, 0xd4, 0x88, 0xec, 0x52, 0xc3, 0xf3, - 0x75, 0xf2, 0x84, 0x5d, 0xa9, 0x0e, 0xa9, 0x29, 0x99, 0xa6, 0xbd, 0x4a, - 0x0e, 0x0d, 0x8e, 0x6e, 0xff, 0xf1, 0x50, 0x80, 0x21, 0xdf, 0xfc, 0x21, - 0x1a, 0xce, 0xfd, 0xca, 0x5d, 0x13, 0x1b, 0xa9, 0xa4, 0xaa, 0x45, 0x28, - 0x21, 0x1a, 0x19, 0x82, 0x84, 0x2a, 0x02, 0xa1, 0x49, 0x53, 0xcd, 0x77, - 0xf1, 0xee, 0x79, 0xaf, 0xad, 0xe3, 0x4e, 0xb9, 0xf3, 0xbb, 0xe3, 0x98, - 0xd0, 0x33, 0xed, 0x1f, 0xf6, 0x77, 0x1e, 0x41, 0xcc, 0xeb, 0x34, 0x1a, - 0x25, 0x48, 0x18, 0x77, 0x83, 0x80, 0xd0, 0x45, 0xc1, 0x78, 0x1c, 0x74, - 0xef, 0xe3, 0x50, 0xdf, 0x7c, 0x5e, 0x1f, 0xb6, 0x86, 0x70, 0x95, 0x97, - 0x3c, 0x2e, 0xef, 0x93, 0xc9, 0x8f, 0xb8, 0xdb, 0xb2, 0x92, 0xf1, 0xdc, - 0xbd, 0x06, 0x14, 0xd2, 0x13, 0x43, 0xd2, 0xe3, 0x74, 0xd0, 0x9d, 0x52, - 0xc0, 0x78, 0xf3, 0x38, 0xbc, 0x0b, 0x7d, 0xcc, 0x5f, 0x78, 0x0b, 0x25, - 0x28, 0x1b, 0xa2, 0x79, 0xa9, 0xec, 0xa5, 0xad, 0x92, 0x06, 0x96, 0x34, - 0xaa, 0x26, 0x98, 0xc1, 0x9b, 0x0e, 0xa1, 0x08, 0xd1, 0x9b, 0xec, 0x91, - 0x0c, 0x64, 0x23, 0xde, 0x88, 0xf3, 0xff, 0x88, 0x37, 0x95, 0xea, 0x79, - 0x87, 0x33, 0x9e, 0x50, 0x9e, 0x53, 0x8a, 0xd1, 0xad, 0xe1, 0xba, 0xa2, - 0xe6, 0xba, 0x55, 0xad, 0xac, 0x65, 0x07, 0x39, 0xa1, 0x8a, 0x6f, 0xa2, - 0x84, 0x01, 0x1c, 0xc5, 0x99, 0xad, 0xab, 0x39, 0xac, 0x2f, 0x9b, 0x5e, - 0xc0, 0x5d, 0xf6, 0xe7, 0x1a, 0x48, 0x3b, 0xf1, 0xef, 0x9b, 0x0e, 0x53, - 0xe3, 0x00, 0x2d, 0xf2, 0x8f, 0x88, 0x9f, 0xf8, 0x7f, 0xe1, 0xfd, 0x77, - 0xaa, 0x66, 0x42, 0x7f, 0x90, 0x00, 0x00, 0x38, 0xe4, 0xf6, 0xf1, 0xa5, - 0x60, 0xa9, 0x26, 0x55, 0x01, 0xc5, 0x65, 0xf5, 0x4f, 0xd8, 0xe9, 0x91, - 0x91, 0x5d, 0x7d, 0xf9, 0xe6, 0x98, 0xc2, 0x40, 0xe3, 0x62, 0x8e, 0x16, - 0xf8, 0xc2, 0x68, 0x95, 0x4c, 0x95, 0xb2, 0x19, 0xf2, 0xce, 0xc5, 0x9c, - 0xc1, 0xb7, 0xf4, 0xb4, 0xc4, 0xa0, 0x4d, 0x74, 0x56, 0x70, 0xff, 0xf1, - 0x50, 0x80, 0x23, 0x1f, 0xfc, 0x21, 0x1a, 0xcf, 0xbf, 0xf8, 0xec, 0xed, - 0xfe, 0xa9, 0xb4, 0x9c, 0xd4, 0xcc, 0x64, 0x18, 0x54, 0x00, 0x26, 0x4b, - 0xce, 0x35, 0xe9, 0xed, 0x9e, 0x7c, 0xe5, 0x75, 0xb9, 0xa0, 0x00, 0x8b, - 0xa3, 0xf7, 0xae, 0xfd, 0x13, 0x0e, 0xa4, 0xe1, 0xeb, 0x22, 0x82, 0xd8, - 0x89, 0x13, 0x5e, 0x60, 0xd1, 0x7f, 0x0f, 0xfc, 0x5f, 0xe2, 0xcd, 0xf1, - 0xdf, 0x89, 0x57, 0xb1, 0xaa, 0xdd, 0xc9, 0x9c, 0x1e, 0xdb, 0xba, 0x4e, - 0x71, 0x61, 0xbe, 0xc0, 0xbc, 0x25, 0xcb, 0xf0, 0xee, 0x15, 0x48, 0xf1, - 0x6b, 0x44, 0xdb, 0x1d, 0x58, 0xcf, 0x39, 0x23, 0xef, 0xd5, 0xc9, 0x35, - 0x5c, 0x26, 0x2f, 0x9f, 0x0e, 0xe3, 0x94, 0x13, 0x25, 0x67, 0x9c, 0x4c, - 0xf3, 0xd6, 0x38, 0x41, 0x74, 0x85, 0xa0, 0xc4, 0x76, 0xf7, 0xfc, 0xf2, - 0xab, 0x57, 0xca, 0xeb, 0x17, 0x5d, 0xd0, 0x5d, 0x76, 0x9b, 0x9d, 0xc4, - 0xa9, 0xbd, 0x39, 0x29, 0x1c, 0x37, 0xd9, 0x71, 0x50, 0x49, 0x42, 0xb2, - 0x3d, 0x35, 0xda, 0x33, 0x08, 0x00, 0x01, 0x19, 0xdb, 0x59, 0xd0, 0xa3, - 0x6a, 0x34, 0xa0, 0x86, 0xb0, 0xbd, 0x08, 0x90, 0x0c, 0xe6, 0x8a, 0xa8, - 0xa5, 0x0e, 0x67, 0x8b, 0x0a, 0x11, 0x92, 0xf9, 0xaf, 0x4b, 0x88, 0xc2, - 0x54, 0x4a, 0xd8, 0xe1, 0x2d, 0x74, 0xc2, 0xf6, 0x81, 0x55, 0x02, 0x35, - 0xb9, 0xde, 0x17, 0x2c, 0x38, 0x65, 0x76, 0x14, 0x74, 0x40, 0xf6, 0x3d, - 0x49, 0x81, 0xda, 0xe4, 0x5d, 0x38, 0xef, 0x8c, 0x67, 0xfe, 0x3e, 0x94, - 0x86, 0x8e, 0x1e, 0xc7, 0xbd, 0x52, 0xf2, 0x14, 0x74, 0x40, 0x00, 0x35, - 0xb5, 0xe7, 0x1c, 0x8c, 0xbe, 0xb2, 0xb1, 0x5a, 0x4d, 0xcc, 0x01, 0xb7, - 0x0d, 0x3b, 0x1e, 0x09, 0x89, 0xbf, 0x53, 0x0f, 0xe4, 0xd8, 0x2a, 0x44, - 0x1c, 0x5b, 0x4c, 0xe5, 0xb0, 0x5c, 0xc9, 0x19, 0xd3, 0x57, 0x49, 0x3c, - 0x68, 0x59, 0x51, 0xa2, 0xb0, 0xac, 0xc8, 0x53, 0x44, 0x81, 0x80, 0x22, - 0x29, 0xc0, 0xff, 0xf1, 0x50, 0x80, 0x26, 0x3f, 0xfc, 0x21, 0x1a, 0xcf, - 0xfb, 0xe4, 0x8d, 0x04, 0x43, 0xa7, 0xb2, 0x42, 0x95, 0xc8, 0x66, 0x0a, - 0x0d, 0x8a, 0x01, 0xbd, 0x09, 0xbe, 0x27, 0x8f, 0x69, 0xdf, 0xef, 0xcf, - 0xbc, 0xeb, 0x9e, 0x3c, 0xb2, 0x49, 0xa6, 0x57, 0x96, 0x67, 0x01, 0x0f, - 0x18, 0xfb, 0x2c, 0xf3, 0x7b, 0xe9, 0x86, 0x0d, 0x12, 0xdc, 0xc1, 0xd6, - 0x1a, 0xef, 0xc7, 0xf1, 0x6f, 0x5d, 0x56, 0x90, 0xb4, 0x9c, 0x4a, 0xef, - 0x02, 0xf2, 0x31, 0xcb, 0x40, 0x95, 0x2d, 0x2f, 0x50, 0xc5, 0x72, 0x2a, - 0x02, 0xab, 0xf4, 0xe4, 0xd8, 0xa4, 0xf0, 0xa0, 0x77, 0xe2, 0xb3, 0xae, - 0x8e, 0x08, 0xce, 0xf0, 0x9e, 0xe6, 0x1d, 0x3c, 0x00, 0x1d, 0x41, 0x64, - 0x44, 0x91, 0x49, 0x5e, 0x41, 0xb8, 0x02, 0xa8, 0xa8, 0x5d, 0xa8, 0x63, - 0x00, 0xd3, 0x0b, 0xd2, 0xde, 0x2c, 0x6e, 0x6a, 0xeb, 0xf4, 0x62, 0xbe, - 0xd0, 0x3d, 0x05, 0x92, 0xad, 0x77, 0xa5, 0x6e, 0x5d, 0xdd, 0x91, 0x73, - 0xa8, 0x1c, 0x35, 0x0a, 0xf8, 0xbc, 0xb9, 0x18, 0x52, 0xdd, 0x1d, 0xb8, - 0xff, 0x13, 0x85, 0x21, 0x68, 0xbb, 0xac, 0xe5, 0x68, 0x31, 0x5b, 0x70, - 0x0a, 0xb8, 0x37, 0x27, 0xea, 0x08, 0xfa, 0x1a, 0xca, 0x9c, 0x1c, 0x47, - 0xe9, 0x83, 0xf3, 0xce, 0xed, 0x5f, 0x0a, 0x7e, 0xe5, 0x88, 0xc7, 0x1d, - 0xb9, 0x64, 0xf8, 0x5d, 0x64, 0x4b, 0x5a, 0x21, 0xcb, 0x35, 0x4c, 0x94, - 0xd8, 0x64, 0x43, 0x8d, 0x57, 0x46, 0x28, 0x61, 0x63, 0x84, 0xc1, 0x14, - 0x06, 0x22, 0x2c, 0xd5, 0x0c, 0x62, 0x08, 0x77, 0x4c, 0x20, 0x00, 0xd9, - 0x80, 0x94, 0xb6, 0x20, 0x18, 0xc8, 0x56, 0x9e, 0x56, 0x51, 0xe4, 0x48, - 0x00, 0x0a, 0x75, 0x99, 0xa3, 0xe0, 0x00, 0x09, 0x4d, 0x6d, 0xd7, 0x64, - 0x99, 0xac, 0xe2, 0x9b, 0xa9, 0x13, 0x7a, 0x95, 0xbe, 0x83, 0xfd, 0xbb, - 0xba, 0xa2, 0x8d, 0xc2, 0xa6, 0x77, 0xf4, 0xc7, 0x4f, 0x88, 0xa8, 0x03, - 0xed, 0xcc, 0x27, 0x59, 0x15, 0xd7, 0x8b, 0x73, 0x1e, 0x55, 0xe9, 0x7d, - 0x1e, 0x26, 0x49, 0xc8, 0xf5, 0x66, 0xc5, 0x8a, 0x18, 0xe9, 0x11, 0x2b, - 0xb2, 0x11, 0x21, 0xe6, 0x51, 0xc8, 0x38, 0xff, 0xf1, 0x50, 0x80, 0x36, - 0x7f, 0xfc, 0x20, 0xa9, 0x1a, 0xd4, 0xa5, 0x49, 0xa5, 0x14, 0xc2, 0x41, - 0x84, 0x98, 0x35, 0x39, 0xe3, 0x7d, 0x5f, 0xbf, 0x99, 0xbe, 0x1d, 0xea, - 0xf3, 0xad, 0x3c, 0x7b, 0x4d, 0xf9, 0xa9, 0x9e, 0x73, 0x27, 0x20, 0xfc, - 0xef, 0xcf, 0x38, 0x8b, 0x95, 0x3d, 0xd6, 0xc1, 0x2c, 0x33, 0xa5, 0x82, - 0x90, 0xc5, 0xc5, 0x1c, 0x55, 0x54, 0xe9, 0xe5, 0x5d, 0x0c, 0x57, 0xcb, - 0x8e, 0x7a, 0xa6, 0x5b, 0xef, 0xf6, 0x8b, 0xde, 0x1a, 0xa8, 0xb1, 0x13, - 0xef, 0xfd, 0x44, 0x4e, 0xed, 0xe9, 0x13, 0x6c, 0x77, 0xfb, 0xa2, 0xc8, - 0xe2, 0xef, 0x7a, 0xbd, 0x06, 0x8c, 0xb7, 0x6e, 0x28, 0x2b, 0x1b, 0x21, - 0xde, 0x04, 0xa2, 0x97, 0x71, 0xe5, 0xb2, 0x56, 0x39, 0xbc, 0x66, 0xaa, - 0x03, 0xac, 0xb5, 0xdb, 0x18, 0xca, 0x93, 0x38, 0xad, 0xdd, 0xd7, 0x74, - 0x73, 0x38, 0x2a, 0xee, 0xb8, 0xbc, 0xc7, 0x20, 0xb5, 0x76, 0x74, 0x87, - 0x94, 0x94, 0x83, 0xd3, 0x32, 0x4e, 0xce, 0x3c, 0x47, 0xd7, 0x10, 0xe2, - 0x1d, 0xf0, 0x25, 0x3b, 0xc6, 0xbf, 0x30, 0xba, 0x37, 0xc3, 0x68, 0x79, - 0xfc, 0x09, 0x10, 0x5d, 0x7e, 0x2b, 0x02, 0x62, 0x69, 0x7b, 0xb6, 0x04, - 0x0d, 0x03, 0x69, 0xdb, 0xb4, 0x17, 0x74, 0x00, 0x1a, 0xef, 0x3d, 0x71, - 0x45, 0xb7, 0xcb, 0xa8, 0x49, 0x51, 0xc1, 0xcd, 0x9a, 0x6d, 0xd4, 0x1b, - 0xf2, 0x68, 0x13, 0x10, 0x3f, 0x17, 0x62, 0x4d, 0x00, 0x77, 0xc3, 0x5f, - 0xeb, 0x1e, 0xa6, 0x00, 0x22, 0x30, 0x32, 0xd5, 0x3d, 0x21, 0x56, 0xb4, - 0x42, 0x68, 0x28, 0x63, 0x3a, 0x14, 0x42, 0xc6, 0x00, 0x00, 0xbc, 0xb6, - 0x5b, 0x12, 0x5b, 0x5b, 0xf6, 0x9b, 0xd4, 0xf1, 0xf7, 0xeb, 0xb5, 0x03, - 0xaf, 0xe8, 0xec, 0x41, 0x60, 0x80, 0xd6, 0xdb, 0x0f, 0x50, 0x7b, 0xa4, - 0x86, 0xa8, 0x91, 0xa8, 0xe5, 0xb0, 0xd1, 0x2a, 0x8c, 0xda, 0xd9, 0xa4, - 0x9a, 0x2a, 0xcc, 0x81, 0x28, 0x43, 0x60, 0x97, 0x6b, 0x6e, 0x80, 0x95, - 0x42, 0x98, 0xdc, 0xe5, 0x31, 0x7d, 0xc7, 0x11, 0x63, 0x46, 0x6a, 0xc4, - 0xe2, 0xf5, 0x19, 0xb9, 0xdd, 0x96, 0xf7, 0xdd, 0x9d, 0xbc, 0xc8, 0x7e, - 0xe1, 0x51, 0xb7, 0x38, 0xf7, 0x79, 0x17, 0xe4, 0x8f, 0x9b, 0xb7, 0x0d, - 0x8f, 0xd1, 0xb7, 0xd3, 0x45, 0x78, 0xfa, 0xf7, 0xed, 0x33, 0x31, 0x18, - 0x4a, 0x52, 0x23, 0x49, 0xbe, 0xd1, 0xdb, 0x09, 0x38, 0xf8, 0x61, 0x05, - 0x08, 0x3e, 0x63, 0x16, 0xdf, 0xb6, 0xb2, 0xae, 0x2f, 0x34, 0xdb, 0x42, - 0xbd, 0x72, 0xad, 0xfb, 0x5b, 0x73, 0xb5, 0x6f, 0x61, 0x2c, 0x95, 0x8c, - 0x9d, 0x6c, 0xc3, 0xe7, 0xab, 0x69, 0x72, 0x5c, 0xf6, 0x56, 0x5c, 0x54, - 0x48, 0x82, 0xad, 0xee, 0x14, 0xb0, 0x26, 0x69, 0xd4, 0x4d, 0xfd, 0xfa, - 0xa2, 0xa9, 0x2b, 0xea, 0x7a, 0xdb, 0x9d, 0x0a, 0xc6, 0xf7, 0x85, 0xcc, - 0xea, 0x8c, 0x0b, 0x11, 0x8b, 0x54, 0x68, 0xb3, 0x62, 0x0d, 0x7c, 0xa6, - 0x09, 0x81, 0xa8, 0x30, 0x90, 0x90, 0x2c, 0x5e, 0xe6, 0xa5, 0x6d, 0xb0, - 0x4e, 0x9b, 0x00, 0xd8, 0x69, 0x53, 0x37, 0xb9, 0x40, 0xf8, 0xff, 0xf1, - 0x50, 0x80, 0x3b, 0x3f, 0xfc, 0x20, 0xa6, 0x1a, 0xd4, 0xa8, 0x32, 0x1d, - 0x48, 0x47, 0x43, 0xc0, 0x80, 0x00, 0x25, 0x45, 0x3c, 0xf8, 0x5e, 0x75, - 0xb5, 0x46, 0xbc, 0xaf, 0xba, 0xb7, 0xb7, 0xb0, 0x81, 0xff, 0xdf, 0xde, - 0x7b, 0xa5, 0x53, 0x6b, 0x75, 0x97, 0x56, 0x1a, 0x9c, 0xe0, 0x26, 0x73, - 0x3d, 0x2c, 0x7d, 0xc3, 0xe9, 0x38, 0x51, 0x70, 0x88, 0x43, 0x0c, 0xe6, - 0x9e, 0xa8, 0x7c, 0x97, 0x3f, 0x87, 0x30, 0x4c, 0xca, 0x6d, 0xe6, 0x97, - 0xbd, 0x1e, 0xb4, 0x37, 0xca, 0x36, 0xa7, 0xa1, 0x78, 0xf4, 0xc1, 0x34, - 0x46, 0x25, 0x69, 0xcf, 0x45, 0x7e, 0xa5, 0x57, 0xa3, 0x31, 0x39, 0xb9, - 0x8b, 0xaa, 0x9a, 0x62, 0x88, 0xa4, 0x93, 0x18, 0x45, 0x90, 0x71, 0xd0, - 0x24, 0xca, 0xd7, 0x3e, 0x56, 0xbf, 0x73, 0x2c, 0x1f, 0x73, 0x9c, 0xd0, - 0x72, 0xfc, 0x7d, 0x32, 0xe8, 0xe7, 0x89, 0x6e, 0x73, 0x3a, 0xe7, 0x36, - 0xe7, 0x39, 0xa8, 0x42, 0x84, 0xcb, 0x61, 0xf7, 0x31, 0x50, 0x38, 0xa0, - 0x5d, 0x1c, 0xe4, 0xbc, 0xb3, 0xfd, 0x3e, 0x06, 0x0d, 0xef, 0x1f, 0xe8, - 0x30, 0xed, 0xb2, 0x83, 0x4a, 0x3b, 0x4e, 0x6b, 0x9e, 0xfb, 0xa0, 0xab, - 0x43, 0x17, 0xf0, 0x79, 0x57, 0x3f, 0xe3, 0x3b, 0x56, 0xff, 0x42, 0x1a, - 0xa4, 0x83, 0x5a, 0x24, 0x97, 0x13, 0x45, 0x55, 0x5a, 0x9a, 0x21, 0xba, - 0xac, 0x18, 0xbd, 0x5a, 0x14, 0xc5, 0x56, 0x0b, 0x06, 0x0d, 0xbb, 0x2e, - 0x01, 0x76, 0x16, 0xca, 0xa3, 0x85, 0xca, 0x15, 0xe1, 0x4b, 0x40, 0x50, - 0xba, 0x76, 0x97, 0x6b, 0xed, 0x27, 0x59, 0x1f, 0xc2, 0x8b, 0x46, 0x0a, - 0x6c, 0xe4, 0x63, 0x54, 0xcc, 0xa2, 0x4c, 0x37, 0x09, 0x26, 0xe7, 0xd1, - 0x19, 0x44, 0x48, 0xae, 0x0d, 0x25, 0xcd, 0x00, 0x79, 0x75, 0x57, 0xad, - 0x87, 0xd6, 0x75, 0xf1, 0x6f, 0x5c, 0xfe, 0x44, 0xbe, 0x32, 0xd5, 0xf7, - 0xfe, 0x82, 0x3a, 0xf8, 0xea, 0xd0, 0x50, 0xd8, 0x13, 0x48, 0x57, 0x53, - 0x9b, 0xc9, 0xf2, 0x36, 0x70, 0x5c, 0xd5, 0xac, 0x6a, 0x46, 0x94, 0x04, - 0xdb, 0xca, 0x56, 0xa9, 0x6f, 0x91, 0x81, 0x86, 0x24, 0x4d, 0xa6, 0x5b, - 0x77, 0xe8, 0x30, 0x2f, 0x10, 0x7d, 0x96, 0x28, 0x84, 0x44, 0x27, 0xa4, - 0xed, 0x55, 0xce, 0xe7, 0xb1, 0xac, 0xbf, 0x7a, 0x49, 0x08, 0xbc, 0xd8, - 0x9f, 0x1a, 0x42, 0x0d, 0x17, 0x20, 0x21, 0x29, 0x9a, 0xfb, 0x25, 0x40, - 0xac, 0x6b, 0xe6, 0x99, 0xca, 0x3d, 0xcb, 0x04, 0xf5, 0x55, 0x5e, 0xd5, - 0x70, 0xe5, 0xd1, 0xf1, 0xfb, 0xfa, 0xe8, 0xcc, 0x24, 0x1d, 0xfc, 0x7b, - 0xfb, 0xb1, 0xd8, 0x24, 0x03, 0x1a, 0x9c, 0xd7, 0x03, 0x00, 0x0b, 0x4e, - 0x5e, 0x87, 0xb3, 0xda, 0x5d, 0xea, 0x91, 0x24, 0xc8, 0x2d, 0xcb, 0x45, - 0x47, 0x33, 0x84, 0x17, 0x04, 0xfe, 0x15, 0x97, 0xb7, 0xbe, 0xd5, 0x9f, - 0x40, 0x74, 0xbc, 0x87, 0xf2, 0xb4, 0x63, 0xa5, 0xd8, 0x89, 0xed, 0xad, - 0x04, 0xde, 0xca, 0x26, 0x92, 0xa0, 0xa5, 0x54, 0x91, 0xd5, 0x82, 0x22, - 0xa2, 0xf8, 0x2a, 0x59, 0x8f, 0xac, 0xc6, 0xf2, 0x91, 0x40, 0x52, 0x48, - 0x53, 0xdf, 0x13, 0xd3, 0xbd, 0x96, 0x18, 0x8a, 0x72, 0x1a, 0x46, 0xdb, - 0xba, 0x85, 0x26, 0x99, 0x6c, 0x91, 0x9e, 0x82, 0x9d, 0x42, 0x46, 0x51, - 0x01, 0x31, 0x96, 0xa3, 0xc9, 0xd6, 0xd6, 0xa8, 0x68, 0x90, 0xd6, 0xcb, - 0x92, 0x07, 0x80, 0xff, 0xf1, 0x50, 0x80, 0x39, 0x5f, 0xfc, 0x20, 0xae, - 0x1a, 0xd0, 0xb7, 0x82, 0x41, 0x23, 0xdb, 0x55, 0xeb, 0xaf, 0x1e, 0xd5, - 0x57, 0x9d, 0x65, 0x4b, 0x65, 0xef, 0x5a, 0xe7, 0xef, 0x3b, 0xba, 0xa6, - 0x97, 0xbd, 0x55, 0x58, 0x3b, 0xd7, 0xb9, 0xc1, 0xc4, 0xf1, 0xee, 0x5d, - 0xbc, 0xa8, 0xe4, 0xa3, 0x86, 0xca, 0x06, 0x2b, 0x4c, 0xb2, 0x93, 0x20, - 0xc7, 0xb4, 0xde, 0xe1, 0x63, 0x86, 0x11, 0xc2, 0xf4, 0x30, 0xb9, 0xd0, - 0x95, 0x71, 0x24, 0xb9, 0xc2, 0xf8, 0x9e, 0xae, 0x6a, 0xaa, 0x67, 0x6b, - 0x08, 0x19, 0x6c, 0x6b, 0xf0, 0x02, 0x8c, 0x50, 0x5e, 0x8c, 0x4b, 0x85, - 0x56, 0x20, 0xad, 0x78, 0xa5, 0x62, 0x9c, 0x32, 0xd0, 0x18, 0x49, 0x14, - 0x50, 0xe7, 0x06, 0x2b, 0xd6, 0x75, 0x75, 0xbf, 0x40, 0xfe, 0xef, 0xd8, - 0x6c, 0xd2, 0x7c, 0x07, 0x29, 0xad, 0xcd, 0xa5, 0x64, 0x32, 0xc9, 0x6a, - 0xd4, 0xae, 0x0b, 0x1f, 0xf7, 0xaf, 0x85, 0x66, 0xbb, 0x57, 0x5e, 0xdd, - 0xc3, 0x03, 0xc3, 0x2a, 0xd5, 0xb2, 0x10, 0x26, 0x0c, 0xba, 0xb2, 0xd1, - 0x16, 0xd2, 0xc9, 0x9b, 0xb6, 0xcc, 0x1b, 0x67, 0xa3, 0x56, 0xb6, 0xd1, - 0x84, 0x21, 0xbb, 0x38, 0x70, 0xe1, 0x21, 0x2d, 0xbf, 0x0b, 0xc5, 0xbd, - 0x5e, 0xdb, 0x5b, 0x37, 0x7d, 0x58, 0x17, 0x20, 0x2b, 0x0c, 0x3b, 0x24, - 0x06, 0x00, 0x3e, 0x29, 0xbe, 0xa4, 0x21, 0xcc, 0xca, 0xf3, 0x47, 0xb3, - 0x76, 0x7e, 0x8e, 0x87, 0xf4, 0xd6, 0xaa, 0xac, 0xf1, 0x59, 0xa2, 0x20, - 0x5f, 0x9f, 0xd2, 0xea, 0xbd, 0x04, 0xfa, 0x53, 0xde, 0xdc, 0x02, 0xb8, - 0x5d, 0xd9, 0xf7, 0x5a, 0x67, 0xad, 0x69, 0x74, 0x30, 0x8d, 0x4c, 0xe0, - 0x00, 0x01, 0x6a, 0x98, 0x75, 0xc6, 0x7e, 0x39, 0xb8, 0x5c, 0x56, 0x93, - 0x60, 0x49, 0xfc, 0x17, 0x0c, 0x8d, 0xa9, 0x29, 0x17, 0xf6, 0xd5, 0x64, - 0xf0, 0x52, 0x9a, 0x55, 0x9e, 0x43, 0xd5, 0x4b, 0xe3, 0x6e, 0xaa, 0x6f, - 0x4b, 0x8f, 0x81, 0x8e, 0x86, 0xf7, 0xeb, 0xf4, 0xc3, 0x7d, 0xeb, 0xad, - 0x58, 0xd1, 0x52, 0x6e, 0x7a, 0xac, 0x25, 0x99, 0x98, 0x0c, 0x67, 0x28, - 0x39, 0x59, 0xb6, 0x05, 0x52, 0xac, 0xfd, 0x57, 0x12, 0xac, 0x4c, 0xe8, - 0x05, 0xa5, 0xce, 0x8d, 0x68, 0x42, 0x70, 0x86, 0x5e, 0xe8, 0xc6, 0x79, - 0x14, 0xb1, 0x99, 0x30, 0x9c, 0x58, 0x56, 0x83, 0x72, 0x89, 0x2a, 0x98, - 0xd0, 0xd8, 0xd0, 0x00, 0x58, 0xea, 0x01, 0x60, 0x0b, 0x4b, 0x6d, 0x46, - 0x55, 0x99, 0xc0, 0x0c, 0x22, 0x01, 0xa2, 0x2f, 0x55, 0xc8, 0xe0, 0x4a, - 0x06, 0xaf, 0x68, 0xe9, 0x17, 0x68, 0x96, 0x06, 0x09, 0x5d, 0xf8, 0x18, - 0x3b, 0xa9, 0xd4, 0xcb, 0x2b, 0x18, 0x50, 0x74, 0x2a, 0x09, 0xa6, 0x35, - 0x8c, 0x4b, 0xcc, 0x85, 0xd6, 0x49, 0xbb, 0xae, 0x10, 0xa6, 0xc4, 0x64, - 0xa1, 0x88, 0x5a, 0x74, 0xa3, 0x94, 0x99, 0xb9, 0xe1, 0x28, 0x0c, 0x1a, - 0x48, 0x34, 0x53, 0x48, 0x4a, 0x81, 0x78, 0x81, 0xd1, 0x16, 0xd5, 0xb0, - 0x2a, 0xa9, 0x2f, 0x49, 0xd2, 0x9c, 0xb8, 0x07, 0x96, 0x6b, 0x3a, 0x64, - 0x6b, 0x7c, 0xf7, 0x8c, 0xda, 0xa4, 0xb8, 0x5e, 0xba, 0x40, 0x01, 0xfb, - 0xbb, 0xa9, 0xd7, 0x0b, 0xe4, 0x74, 0x80, 0x5a, 0x26, 0x89, 0x64, 0xef, - 0x31, 0x95, 0xc4, 0x50, 0x38, 0xff, 0xf1, 0x50, 0x80, 0x26, 0x1f, 0xfc, - 0x21, 0x1a, 0xcf, 0xe1, 0xf1, 0x6c, 0x0d, 0x65, 0xa6, 0xb1, 0x42, 0xd1, - 0x46, 0x54, 0x78, 0x17, 0xea, 0x38, 0x0f, 0x8e, 0x7b, 0x9f, 0x7f, 0x5b, - 0xfe, 0x7f, 0x8f, 0x8c, 0xe6, 0xff, 0x5f, 0x0c, 0xd6, 0x97, 0x59, 0x62, - 0xc0, 0xfd, 0x3b, 0x31, 0xe6, 0x3e, 0x67, 0x71, 0x07, 0xf4, 0xb6, 0xa6, - 0x14, 0x0c, 0xf1, 0xb4, 0x20, 0x2a, 0x2b, 0x96, 0xf4, 0x26, 0x71, 0xa0, - 0x45, 0x03, 0x44, 0x4e, 0x6b, 0xbe, 0xb7, 0xf3, 0x5a, 0xd6, 0x4f, 0x6d, - 0x92, 0xee, 0x8f, 0x8f, 0xfa, 0xae, 0xef, 0x76, 0x9c, 0x81, 0x97, 0x77, - 0xfd, 0xc0, 0x17, 0xd9, 0x80, 0x67, 0xdc, 0x13, 0xab, 0x7d, 0x3f, 0x90, - 0xc0, 0xaa, 0xc8, 0x4f, 0x00, 0x07, 0xb0, 0xc1, 0xba, 0xfb, 0x08, 0xae, - 0x37, 0xfa, 0xcb, 0x7d, 0xc5, 0x87, 0x64, 0xa9, 0x76, 0xed, 0x03, 0x18, - 0xe0, 0x79, 0xbc, 0xf7, 0x27, 0x96, 0x00, 0xfc, 0x2b, 0xe3, 0x98, 0x65, - 0x54, 0xaf, 0xfd, 0x9c, 0x30, 0x79, 0xfd, 0x6f, 0xe2, 0xeb, 0x9f, 0x08, - 0x84, 0x30, 0x3e, 0x5d, 0x0a, 0x8f, 0x9b, 0xe7, 0x54, 0xbc, 0x20, 0x5f, - 0xf8, 0xb5, 0xa7, 0xbf, 0x5f, 0x8a, 0x17, 0xaf, 0xc7, 0x22, 0x7f, 0xf5, - 0xd9, 0xc4, 0xfc, 0xce, 0xb6, 0x5b, 0xc8, 0xf2, 0xad, 0x94, 0x01, 0xbb, - 0x22, 0x4a, 0x54, 0x39, 0xcd, 0xce, 0x8c, 0x17, 0x8b, 0x27, 0xb7, 0xdf, - 0x7f, 0x43, 0xed, 0xa9, 0xea, 0xa6, 0x00, 0xef, 0xa1, 0xe9, 0xbc, 0x0e, - 0x44, 0xae, 0xf7, 0x0f, 0x4c, 0xa6, 0xab, 0xa5, 0xaa, 0xbe, 0x37, 0xe0, - 0x78, 0x18, 0x5a, 0x4a, 0x99, 0x8f, 0x62, 0x55, 0x99, 0xb8, 0x28, 0x1f, - 0x78, 0x1f, 0xbf, 0xec, 0x00, 0x1a, 0xde, 0xb7, 0xf1, 0xef, 0xbf, 0xc7, - 0xb7, 0xbd, 0x1a, 0xde, 0xab, 0x7a, 0x50, 0x0b, 0x3b, 0x9e, 0x41, 0x46, - 0x22, 0x07, 0xe0, 0x4e, 0x5e, 0xe4, 0x4a, 0x87, 0xfa, 0xc1, 0x52, 0x20, - 0xff, 0x4c, 0x49, 0xbe, 0x62, 0xa6, 0x4f, 0x49, 0x5e, 0xfc, 0x1f, 0x2f, - 0x60, 0x14, 0xcc, 0x98, 0x10, 0x04, 0xa8, 0xd7, 0x12, 0xbb, 0xd2, 0xef, - 0xb4, 0x23, 0x7c, 0x17, 0x6a, 0x8f, 0x63, 0xe8, 0xf0, 0xff, 0xf1, 0x50, - 0x80, 0x29, 0xbf, 0xfc, 0x21, 0x1a, 0xcf, 0xd9, 0xa4, 0xf9, 0x00, 0xbd, - 0xa6, 0xa4, 0xad, 0x10, 0xcc, 0x80, 0x71, 0x99, 0xbe, 0xa6, 0x65, 0xe7, - 0xef, 0xf3, 0x5d, 0xfe, 0x3b, 0xaf, 0x6f, 0x5c, 0x75, 0xbe, 0xff, 0x1b, - 0xba, 0xdd, 0xce, 0x1d, 0xf5, 0x15, 0x00, 0xfb, 0xce, 0xf5, 0xfa, 0xf7, - 0x76, 0xb2, 0x31, 0x4d, 0x02, 0x29, 0x18, 0x8a, 0xc0, 0x6e, 0x6e, 0xd5, - 0x75, 0x45, 0x73, 0xf2, 0x84, 0xe7, 0xc2, 0x27, 0xce, 0x7a, 0x2e, 0x3b, - 0xfe, 0xe0, 0xf6, 0xfa, 0xa1, 0xdd, 0x21, 0xa9, 0xeb, 0xfe, 0x1a, 0x0a, - 0xc5, 0xdc, 0xec, 0xed, 0xb4, 0x66, 0x40, 0xca, 0x0a, 0xef, 0xc1, 0xdd, - 0x10, 0x24, 0x77, 0x0b, 0xa5, 0x73, 0xed, 0xde, 0xe9, 0xb1, 0xc6, 0xc9, - 0xa5, 0x71, 0xbb, 0x00, 0x01, 0x9e, 0x1d, 0x28, 0x54, 0x4d, 0x5c, 0x6d, - 0xeb, 0xc0, 0xde, 0x08, 0xd3, 0x8c, 0x65, 0x64, 0x6f, 0xe1, 0xae, 0x53, - 0x26, 0x07, 0x63, 0xb8, 0x49, 0xaa, 0x21, 0xf6, 0x2e, 0xf2, 0xf2, 0xa4, - 0xb7, 0x74, 0x3e, 0xac, 0x9e, 0xe0, 0x86, 0x62, 0x04, 0x87, 0x53, 0x44, - 0x19, 0xdb, 0x15, 0x4a, 0x6e, 0x50, 0xcb, 0xd7, 0x52, 0xb9, 0x88, 0xda, - 0x84, 0x6f, 0x45, 0xce, 0x21, 0x24, 0xeb, 0x79, 0x54, 0x23, 0x5b, 0x8d, - 0x66, 0x08, 0xc6, 0x34, 0x92, 0x22, 0xc2, 0x0a, 0x90, 0x98, 0x0b, 0x0d, - 0xab, 0x55, 0x8a, 0xca, 0x9b, 0x60, 0x98, 0xc4, 0x11, 0xa9, 0xa1, 0x34, - 0x30, 0x41, 0x4c, 0x34, 0xaa, 0x54, 0x10, 0x66, 0xf5, 0x4b, 0x82, 0x9d, - 0x53, 0x9b, 0x08, 0x0d, 0x16, 0x57, 0x4a, 0xe2, 0xc6, 0x80, 0x74, 0xa2, - 0xd6, 0x42, 0x42, 0xb6, 0x5d, 0x49, 0x01, 0x04, 0xf9, 0x05, 0x32, 0x19, - 0xda, 0x69, 0xf2, 0xb0, 0x79, 0x20, 0x5e, 0x1a, 0xca, 0x94, 0xd6, 0xdf, - 0x1d, 0xd7, 0xb7, 0xac, 0x7c, 0x4d, 0x7a, 0xba, 0xaf, 0xe7, 0x77, 0x32, - 0x45, 0x07, 0x6b, 0xf4, 0x74, 0xbe, 0x58, 0xce, 0x5a, 0xae, 0x3f, 0xe7, - 0x6a, 0xf5, 0x9f, 0xd9, 0x46, 0xeb, 0x11, 0xb5, 0xdf, 0x70, 0x08, 0x9e, - 0x64, 0x30, 0x8d, 0x84, 0x49, 0xbb, 0x4d, 0x7a, 0xbb, 0xc8, 0x4e, 0x4c, - 0xfb, 0x4c, 0x79, 0xfc, 0x75, 0x44, 0xe0, 0xbd, 0x44, 0x16, 0x72, 0x82, - 0x52, 0x8a, 0xe2, 0x25, 0x33, 0x7a, 0x55, 0x52, 0x08, 0xcd, 0xea, 0x77, - 0x8b, 0xac, 0xae, 0xfc, 0x21, 0xf8, 0xff, 0xf1, 0x50, 0x80, 0x28, 0x5f, - 0xfc, 0x21, 0x1a, 0xcf, 0xfb, 0xf2, 0x6c, 0x21, 0x2c, 0xa5, 0xb4, 0x3a, - 0xcd, 0x48, 0x96, 0x28, 0x2e, 0xab, 0x7c, 0x15, 0x2b, 0x8a, 0xcf, 0x5f, - 0xbf, 0x6f, 0xaf, 0x7e, 0x37, 0xf1, 0xf3, 0xed, 0x2e, 0xaa, 0x6a, 0x66, - 0x70, 0xa8, 0x09, 0xdd, 0x3d, 0x05, 0x9e, 0x72, 0x5b, 0x19, 0x57, 0x09, - 0x57, 0xf3, 0x58, 0x8a, 0x51, 0x77, 0xf6, 0x75, 0xfa, 0x1e, 0x3c, 0xe7, - 0x3d, 0x34, 0x8d, 0xfa, 0x9a, 0xb3, 0xdc, 0xd6, 0xd6, 0x8e, 0xfe, 0x48, - 0xbd, 0x90, 0x6b, 0x41, 0x5a, 0x66, 0xa7, 0x75, 0x80, 0x7f, 0xd8, 0xce, - 0x2e, 0x7c, 0xff, 0xf6, 0x2e, 0xff, 0xcd, 0x96, 0x6f, 0xba, 0xa5, 0xec, - 0x52, 0x47, 0x41, 0xbb, 0x7b, 0xe8, 0x0b, 0xe8, 0xf8, 0xf2, 0xfe, 0x83, - 0xa6, 0xec, 0x01, 0xdf, 0xf1, 0x16, 0x29, 0xe4, 0xfb, 0xdb, 0x84, 0x7d, - 0xc7, 0x60, 0x4e, 0x5d, 0xb4, 0xfc, 0xcc, 0x8b, 0x1d, 0x47, 0x13, 0x0b, - 0x0b, 0xcb, 0xba, 0x9f, 0x76, 0x1b, 0x50, 0xe5, 0x13, 0xc5, 0x5f, 0x1f, - 0xb2, 0x51, 0xc6, 0x48, 0x75, 0x6f, 0x84, 0xad, 0x81, 0x43, 0x35, 0x97, - 0x77, 0xa0, 0xad, 0x15, 0xcc, 0xb8, 0x7f, 0x68, 0x34, 0x1f, 0xfd, 0x3f, - 0x0d, 0x4d, 0x9f, 0xf2, 0x68, 0xee, 0x2b, 0xfd, 0x3a, 0xe1, 0xd7, 0xba, - 0xd4, 0x4c, 0x99, 0xea, 0xd5, 0xde, 0xf3, 0xba, 0x39, 0x25, 0xf2, 0x4c, - 0xf3, 0x72, 0x40, 0x19, 0x58, 0x1a, 0xd6, 0x34, 0x8a, 0x62, 0xb0, 0x4b, - 0x6f, 0x11, 0x0a, 0xad, 0x45, 0x13, 0x80, 0x54, 0xa7, 0x1b, 0x5c, 0xe9, - 0x36, 0x03, 0x03, 0xbc, 0x6e, 0x25, 0xce, 0x0a, 0x02, 0x78, 0x3a, 0x62, - 0xd3, 0x62, 0x42, 0x55, 0x9d, 0xeb, 0x4a, 0x82, 0x95, 0x0f, 0x43, 0x59, - 0x7b, 0xc1, 0x75, 0x48, 0x54, 0xa2, 0x54, 0x88, 0xe3, 0xd7, 0x9e, 0x2f, - 0xc4, 0x99, 0xbb, 0x95, 0x97, 0x57, 0x50, 0x78, 0x7c, 0x22, 0x68, 0x26, - 0x43, 0x01, 0x3c, 0x80, 0xd0, 0x15, 0xa3, 0xd1, 0x51, 0x60, 0xff, 0x8e, - 0x56, 0x8d, 0xfe, 0xa8, 0x37, 0xb9, 0x3a, 0xf9, 0x0f, 0x19, 0x67, 0xae, - 0x1a, 0xba, 0x53, 0x01, 0x95, 0xe2, 0xe8, 0xa1, 0x52, 0x2b, 0x43, 0x59, - 0xa0, 0x2f, 0xa8, 0x36, 0x31, 0x00, 0x92, 0x49, 0xa1, 0x9d, 0x83, 0x60, - 0xc3, 0x39, 0xae, 0x7c, 0xff, 0xf1, 0x50, 0x80, 0x2a, 0x3f, 0xfc, 0x21, - 0x2a, 0xcf, 0xb5, 0xfa, 0xeb, 0x1c, 0x00, 0xa4, 0xa6, 0xb1, 0x5d, 0x68, - 0x46, 0x50, 0x24, 0xf1, 0xc5, 0x40, 0xd5, 0x67, 0x3f, 0x15, 0x5f, 0x7f, - 0x99, 0x5a, 0xdc, 0xf6, 0x24, 0x71, 0x36, 0x9a, 0xaa, 0x10, 0x9d, 0xa3, - 0x52, 0xe0, 0x39, 0x1d, 0x95, 0x5d, 0x90, 0xd8, 0xa1, 0xb1, 0x0c, 0x60, - 0x75, 0x07, 0x88, 0xd5, 0x51, 0x22, 0x42, 0x52, 0x00, 0x20, 0x05, 0x22, - 0x05, 0x5c, 0x6c, 0x09, 0xc7, 0xd1, 0xad, 0x4d, 0x4f, 0x38, 0x03, 0xda, - 0xe0, 0x03, 0xc4, 0xa2, 0x7f, 0x7f, 0xde, 0x61, 0x10, 0x8c, 0xaa, 0xe6, - 0x57, 0xad, 0xb6, 0x77, 0xe3, 0x05, 0x59, 0xd4, 0xef, 0x82, 0xa7, 0x13, - 0x6e, 0xd0, 0x0d, 0x50, 0xce, 0x80, 0x00, 0x59, 0x4c, 0xa0, 0x00, 0x09, - 0xbe, 0x3e, 0x80, 0x00, 0xa9, 0x63, 0xc8, 0xe6, 0xe9, 0xdb, 0x3c, 0xd8, - 0x82, 0xc4, 0x21, 0xe7, 0x70, 0xd4, 0xbb, 0xa4, 0x72, 0xb7, 0x3b, 0x3e, - 0x25, 0x92, 0xff, 0xb5, 0xe9, 0x01, 0x55, 0x8b, 0xd5, 0x28, 0x2c, 0x0c, - 0x80, 0x17, 0xa3, 0x00, 0x0d, 0x20, 0xda, 0x89, 0x8a, 0x61, 0x03, 0x06, - 0x12, 0x44, 0x09, 0x22, 0xec, 0x54, 0xc6, 0x66, 0xaf, 0x03, 0xa1, 0x1e, - 0x34, 0x9d, 0x15, 0xcb, 0x9e, 0x38, 0xe4, 0x22, 0x29, 0x3d, 0x29, 0x80, - 0x8b, 0xd4, 0x36, 0x26, 0x27, 0x03, 0xa8, 0x1d, 0xa6, 0x41, 0x94, 0x26, - 0x89, 0x22, 0xb6, 0x50, 0x5a, 0x23, 0x30, 0xb8, 0x29, 0x83, 0x64, 0xb2, - 0x77, 0x95, 0xe2, 0x5b, 0xaf, 0xa6, 0xbc, 0x09, 0x12, 0xf8, 0x70, 0x0a, - 0xa7, 0x1c, 0xe3, 0x47, 0x58, 0xa1, 0xd7, 0x85, 0xf5, 0x87, 0x58, 0x59, - 0x82, 0x54, 0xcb, 0x89, 0x1a, 0x56, 0x4f, 0x83, 0x4f, 0x50, 0x22, 0x4a, - 0x34, 0x90, 0x7b, 0x1b, 0x95, 0x88, 0x69, 0xf7, 0x82, 0x4c, 0x01, 0xad, - 0x9c, 0x55, 0x46, 0xf5, 0xae, 0x1b, 0x55, 0x63, 0x53, 0x6b, 0xbc, 0x00, - 0x54, 0xcb, 0x48, 0xe2, 0x5b, 0x58, 0xf9, 0xee, 0x12, 0x00, 0xda, 0xff, - 0xd8, 0x1e, 0x13, 0x9b, 0xc6, 0x28, 0x32, 0x87, 0xb9, 0xdf, 0x39, 0xe3, - 0xb7, 0xee, 0xc4, 0x59, 0x04, 0xab, 0x13, 0x69, 0x58, 0x8e, 0x0c, 0x56, - 0xd9, 0x50, 0xa9, 0x0d, 0xf2, 0x1d, 0xc9, 0xfb, 0x24, 0x5d, 0x9c, 0x11, - 0x79, 0xe1, 0x07, 0x39, 0x99, 0xef, 0x70, 0x89, 0x4e, 0x89, 0x1d, 0x86, - 0x35, 0xfb, 0xba, 0xfb, 0xfe, 0xff, 0xf1, 0x50, 0x80, 0x3a, 0x7f, 0xfc, - 0x21, 0x4c, 0x36, 0xf4, 0x03, 0x00, 0xf2, 0x27, 0x30, 0x4e, 0x5c, 0x19, - 0xb2, 0xab, 0x71, 0x66, 0xa2, 0xeb, 0x36, 0x69, 0x91, 0x54, 0xc5, 0x13, - 0x20, 0x07, 0x9f, 0x12, 0xc0, 0x07, 0xf3, 0xfc, 0x00, 0x95, 0x3f, 0xef, - 0xff, 0x80, 0x6b, 0xac, 0xd7, 0xb1, 0x1d, 0xf6, 0xee, 0x78, 0x27, 0x7d, - 0xa0, 0xf0, 0x79, 0xf8, 0xa9, 0x0a, 0x3d, 0x6a, 0x7c, 0xa7, 0xd2, 0xa6, - 0xac, 0x4a, 0x0e, 0x4a, 0x17, 0x3d, 0x00, 0x48, 0x9f, 0x4d, 0x51, 0xe5, - 0x7c, 0x6c, 0xb7, 0x2b, 0x3c, 0x7a, 0x0d, 0xb9, 0x65, 0x08, 0x7d, 0x99, - 0x96, 0x4d, 0xe3, 0x55, 0xc8, 0xa5, 0x08, 0xa3, 0x12, 0x75, 0x18, 0xc5, - 0x32, 0x93, 0x6b, 0x56, 0xca, 0xa0, 0xe1, 0xbe, 0x65, 0xfc, 0xeb, 0x64, - 0xd1, 0x88, 0xc3, 0x09, 0xa6, 0x20, 0x86, 0x4e, 0x3e, 0xa9, 0xaf, 0x75, - 0x28, 0xf7, 0xaa, 0xa4, 0xf2, 0x2d, 0xa5, 0x87, 0x14, 0x3b, 0xb3, 0xca, - 0x67, 0x2d, 0x43, 0xbf, 0xb5, 0xdc, 0xee, 0x82, 0xd7, 0xf0, 0xec, 0xc4, - 0xf6, 0x19, 0x20, 0xbc, 0x7b, 0xe3, 0x18, 0xab, 0x90, 0x0e, 0xe7, 0x77, - 0x77, 0x87, 0x79, 0x9b, 0xab, 0x50, 0x77, 0xdf, 0xd5, 0xae, 0xfe, 0xdc, - 0x18, 0x60, 0x0c, 0xd9, 0x1b, 0x47, 0x35, 0x6e, 0x2d, 0xc3, 0xba, 0xb5, - 0xbb, 0x2e, 0x8e, 0x47, 0x8d, 0x67, 0xa0, 0xdc, 0xbb, 0x45, 0xce, 0x85, - 0x78, 0xa6, 0x01, 0x60, 0x64, 0xf4, 0xed, 0xb2, 0x56, 0x2d, 0x50, 0x51, - 0x97, 0xb8, 0x2f, 0x26, 0xa3, 0x97, 0x3d, 0x98, 0x04, 0x92, 0x2a, 0x87, - 0xe1, 0xe1, 0x7a, 0x8d, 0x22, 0x9d, 0x1a, 0x3e, 0x9c, 0x6e, 0x91, 0x38, - 0xa3, 0x82, 0xac, 0xbb, 0xc3, 0x36, 0x57, 0xa4, 0xc4, 0x7c, 0x97, 0xe3, - 0x04, 0xd5, 0x35, 0x59, 0xa4, 0x78, 0xbf, 0xc3, 0x23, 0x71, 0xa7, 0xa9, - 0x96, 0x86, 0xef, 0x7f, 0x55, 0x86, 0xd7, 0x62, 0x44, 0x22, 0x07, 0x92, - 0x4e, 0xca, 0x16, 0xdc, 0x03, 0xbe, 0x67, 0xba, 0x9a, 0xcd, 0x54, 0x24, - 0xbf, 0xc2, 0xb3, 0xd1, 0xba, 0x4a, 0x12, 0x4b, 0x11, 0xac, 0xbe, 0x72, - 0x20, 0xad, 0xd6, 0x9b, 0x74, 0xa8, 0x8f, 0xda, 0x9a, 0x31, 0x09, 0xfa, - 0x63, 0xe8, 0x54, 0xa7, 0xa5, 0xea, 0xf5, 0xcb, 0xdb, 0xc5, 0x13, 0x8f, - 0x17, 0x7c, 0x72, 0x52, 0x79, 0xfb, 0x71, 0xdd, 0xff, 0x3f, 0xf6, 0x29, - 0x35, 0xc3, 0xf5, 0xe3, 0x03, 0xfb, 0x08, 0xe4, 0x39, 0x08, 0xe1, 0x52, - 0x53, 0x4d, 0x5b, 0xde, 0xde, 0xfb, 0xd5, 0x97, 0xb4, 0x67, 0x9e, 0xc9, - 0x61, 0x15, 0x40, 0xc6, 0xe8, 0x55, 0xf0, 0x10, 0x8f, 0x10, 0x35, 0x29, - 0x90, 0x5a, 0x80, 0xf7, 0x1a, 0x9d, 0x28, 0xb3, 0x00, 0x9a, 0x80, 0x2a, - 0x8d, 0x04, 0xa8, 0xed, 0x77, 0x57, 0x77, 0xf9, 0x49, 0x04, 0x68, 0xa2, - 0xb7, 0xdc, 0x50, 0x25, 0x9d, 0x5d, 0xb8, 0xed, 0xed, 0xee, 0x84, 0x96, - 0xa4, 0x8a, 0xb7, 0xbc, 0x6b, 0x29, 0x3f, 0xad, 0x59, 0x94, 0x7c, 0x93, - 0x14, 0x40, 0x81, 0x04, 0xe2, 0x03, 0x60, 0x31, 0xca, 0x83, 0x40, 0x6b, - 0xe9, 0xa3, 0xfa, 0xaf, 0xb1, 0x1c, 0x33, 0x24, 0x5a, 0xea, 0xa9, 0x0e, - 0xad, 0x72, 0x14, 0x02, 0x51, 0x1a, 0x67, 0xb3, 0xf4, 0xb5, 0xb3, 0x4b, - 0xea, 0x2f, 0x20, 0x1f, 0xda, 0x1c, 0xc9, 0xd6, 0x90, 0x78, 0xb5, 0xcd, - 0x4c, 0xdf, 0xcb, 0x37, 0xff, 0xf1, 0x50, 0x80, 0x29, 0x1f, 0xfc, 0x21, - 0x7a, 0xcf, 0xb4, 0xc4, 0xfc, 0x00, 0x00, 0xa0, 0xb7, 0xb0, 0xec, 0x2a, - 0x53, 0x52, 0x0d, 0x42, 0x83, 0x81, 0x00, 0x00, 0x0d, 0x6c, 0x1e, 0x7c, - 0x7b, 0x78, 0xe1, 0xdf, 0x1e, 0xd9, 0xac, 0x9a, 0x9e, 0xd4, 0x32, 0x31, - 0x20, 0x97, 0xad, 0x35, 0xd7, 0x70, 0xc7, 0x90, 0xde, 0xd1, 0x77, 0xd1, - 0xe0, 0xf8, 0x5e, 0xb3, 0x7d, 0xd0, 0x45, 0x6f, 0xc4, 0xa3, 0x10, 0x44, - 0xb6, 0x00, 0x52, 0x18, 0x0c, 0xe0, 0xa1, 0x2a, 0xd7, 0x1b, 0xc1, 0x4a, - 0x5e, 0x13, 0x5f, 0xb0, 0x8a, 0x74, 0x95, 0x0c, 0xed, 0xa8, 0x09, 0x60, - 0x43, 0xd1, 0xf6, 0xe6, 0xa4, 0xc7, 0x86, 0x2f, 0x4a, 0x6a, 0x66, 0xb4, - 0x3b, 0x64, 0x41, 0x04, 0x09, 0xbc, 0x7e, 0x7f, 0xb8, 0x66, 0x29, 0x1d, - 0x14, 0x3e, 0xcb, 0x00, 0x87, 0x1b, 0xda, 0xaf, 0x94, 0x47, 0x53, 0xd8, - 0xea, 0x68, 0xca, 0xfa, 0xfc, 0xae, 0x0d, 0x59, 0x82, 0x64, 0xa7, 0xd9, - 0x23, 0x0c, 0x3a, 0x30, 0xb6, 0xc8, 0x09, 0x0b, 0x49, 0x0c, 0xd1, 0x85, - 0xed, 0x89, 0xa6, 0xa6, 0x19, 0xac, 0x3b, 0x76, 0x45, 0xaa, 0x20, 0xa1, - 0x43, 0xa7, 0xd1, 0x4b, 0xc2, 0x1a, 0x17, 0xc1, 0x54, 0xd7, 0x62, 0xd0, - 0x04, 0xce, 0xeb, 0x5f, 0xd4, 0x4f, 0x98, 0xa6, 0x90, 0x96, 0x68, 0x3b, - 0x57, 0x66, 0x0b, 0xf7, 0xce, 0x13, 0x55, 0xf4, 0x2e, 0x57, 0xcc, 0xfd, - 0xea, 0x1f, 0x65, 0xb7, 0x17, 0x03, 0x52, 0x80, 0x69, 0xb8, 0x29, 0x25, - 0x53, 0x86, 0xb4, 0x63, 0x96, 0x3a, 0xe1, 0x63, 0x4e, 0xbe, 0xb0, 0x85, - 0xcb, 0x45, 0x78, 0x94, 0xd1, 0xa0, 0x83, 0xd8, 0xa0, 0xc8, 0x55, 0x3f, - 0xc0, 0x39, 0xe1, 0xcf, 0x1c, 0xb8, 0x72, 0x71, 0xcb, 0xdb, 0x61, 0xbd, - 0x57, 0xb6, 0xef, 0x79, 0xc0, 0xee, 0xe4, 0xa0, 0x75, 0xdf, 0x52, 0xfe, - 0x66, 0xac, 0x6c, 0x6f, 0xa6, 0x16, 0xd9, 0x93, 0x06, 0x99, 0xc8, 0xbf, - 0xeb, 0x1e, 0x02, 0x92, 0x7d, 0x66, 0x38, 0x8e, 0x64, 0x9a, 0xfe, 0x71, - 0x4c, 0x28, 0xeb, 0xae, 0x73, 0x97, 0xb6, 0xf5, 0x69, 0x95, 0xdb, 0x94, - 0xad, 0xaa, 0xa7, 0x9b, 0x86, 0x6b, 0x16, 0x36, 0x5f, 0x5e, 0x68, 0xc5, - 0x8b, 0xc6, 0x19, 0x28, 0x55, 0x36, 0x7a, 0x71, 0x8b, 0xc1, 0x93, 0x95, - 0xa4, 0x15, 0x03, 0x33, 0x15, 0x89, 0x27, 0x80, 0xff, 0xf1, 0x50, 0x80, - 0x2d, 0xff, 0xfc, 0x21, 0x1a, 0xce, 0x7b, 0xfb, 0x2e, 0x00, 0x00, 0x9f, - 0xb4, 0xc2, 0x6c, 0x2e, 0x55, 0x4b, 0x0a, 0x02, 0xc4, 0x81, 0x80, 0x00, - 0x0e, 0x1c, 0xfb, 0x7a, 0x25, 0x7c, 0x7a, 0xfd, 0xfb, 0xf1, 0xf7, 0xaf, - 0x8f, 0x15, 0x5d, 0x75, 0x96, 0x2e, 0x0e, 0x2c, 0xfe, 0x4b, 0xcf, 0xff, - 0x4d, 0xa9, 0xa7, 0x68, 0x1f, 0xcd, 0x0b, 0x4f, 0xe0, 0x71, 0x57, 0x20, - 0xee, 0xe5, 0x8f, 0xde, 0x6b, 0x48, 0x4e, 0xd8, 0x45, 0xad, 0xc1, 0xf4, - 0x4e, 0xaf, 0xf7, 0x66, 0xe5, 0x91, 0x97, 0xb6, 0x14, 0x2b, 0x3c, 0x25, - 0x39, 0xd2, 0xde, 0x52, 0xa1, 0x20, 0x2a, 0xa1, 0xce, 0x00, 0x50, 0x80, - 0x5e, 0xac, 0xaa, 0x99, 0x32, 0xc2, 0xf0, 0xf0, 0xb9, 0xa0, 0x93, 0x19, - 0x9b, 0xac, 0xb4, 0x71, 0x8c, 0xbe, 0xcb, 0x00, 0x0a, 0x80, 0x4f, 0x96, - 0xc0, 0x10, 0xa0, 0xe3, 0xa0, 0x00, 0x0a, 0xfd, 0x1a, 0xec, 0x9c, 0xef, - 0x36, 0x2e, 0x37, 0x6a, 0x8e, 0x00, 0x01, 0xc3, 0x79, 0xa7, 0xcb, 0x34, - 0xfa, 0xe1, 0x9a, 0xea, 0x4b, 0x05, 0xcf, 0x67, 0x4e, 0xac, 0x84, 0x89, - 0x04, 0x72, 0x00, 0xa2, 0x0b, 0x45, 0x0b, 0x06, 0x36, 0xce, 0x38, 0x2a, - 0x20, 0xc0, 0x90, 0xbd, 0x40, 0x46, 0x1c, 0xf2, 0x28, 0x17, 0x98, 0xd8, - 0x09, 0xbc, 0x03, 0x47, 0x35, 0x75, 0x4b, 0x16, 0x34, 0xfb, 0x2a, 0x79, - 0x26, 0x01, 0x02, 0x40, 0x14, 0x49, 0xe9, 0x27, 0xec, 0xb2, 0x08, 0x00, - 0x40, 0x44, 0x14, 0x4c, 0x5c, 0x87, 0x70, 0x18, 0x90, 0x90, 0x10, 0x9b, - 0x89, 0x9e, 0xbc, 0x47, 0xaf, 0x62, 0x0d, 0x20, 0xfd, 0x92, 0xc5, 0x13, - 0xc0, 0xc2, 0xc7, 0x36, 0x35, 0xbf, 0x45, 0x76, 0x05, 0x0a, 0x7e, 0x3c, - 0x23, 0x33, 0xf1, 0x13, 0x89, 0xbe, 0xf1, 0xa4, 0x0b, 0x57, 0x70, 0xf3, - 0xc3, 0x1e, 0x38, 0xf6, 0xf3, 0xf2, 0xeb, 0xd8, 0x79, 0x12, 0xc9, 0xb8, - 0x80, 0x53, 0xf6, 0xa9, 0x97, 0xbc, 0xa4, 0x15, 0x00, 0xa8, 0xa4, 0xa8, - 0x52, 0x57, 0x5c, 0xe6, 0xaa, 0x6e, 0x6f, 0x85, 0x01, 0x99, 0x16, 0x3f, - 0xa5, 0x7a, 0x82, 0xfb, 0x55, 0xed, 0xc4, 0x0c, 0x86, 0x5c, 0x59, 0x48, - 0x39, 0x25, 0x7a, 0xaf, 0x8f, 0xb6, 0xee, 0x30, 0x9b, 0x25, 0xcf, 0xf4, - 0x89, 0x87, 0x0f, 0xa3, 0xfd, 0xca, 0x6a, 0x9b, 0x99, 0xed, 0xe1, 0x4e, - 0x38, 0x73, 0xb8, 0x79, 0x46, 0xcf, 0xdf, 0x73, 0x59, 0x6b, 0xc1, 0x64, - 0xcd, 0xd7, 0x87, 0xcd, 0x4c, 0x81, 0x71, 0x91, 0x51, 0xce, 0xd3, 0x08, - 0x37, 0x66, 0x11, 0x8c, 0xd2, 0x00, 0x36, 0x21, 0x30, 0xa4, 0x8a, 0xda, - 0x28, 0x04, 0xf0, 0xff, 0xf1, 0x50, 0x80, 0x2d, 0xff, 0xfc, 0x21, 0x1a, - 0xce, 0xfd, 0xfc, 0xff, 0xa0, 0x00, 0x9d, 0xb5, 0x41, 0x24, 0x96, 0x15, - 0x84, 0x05, 0x85, 0x05, 0x32, 0xc1, 0x96, 0x06, 0x35, 0x4d, 0x6c, 0x15, - 0xf5, 0xbe, 0xff, 0x9a, 0xf6, 0xe7, 0xc7, 0xc6, 0xfa, 0x6c, 0x4f, 0x53, - 0xf1, 0xaa, 0x53, 0xda, 0x27, 0x5b, 0xca, 0x2f, 0xbd, 0xf7, 0x56, 0x3e, - 0x9f, 0x2f, 0x92, 0xe6, 0xdf, 0x3d, 0xe1, 0x09, 0x60, 0x0b, 0xce, 0xc6, - 0x49, 0x99, 0x0d, 0xf1, 0xaa, 0x0d, 0xdc, 0x36, 0x7d, 0x49, 0xb7, 0xcf, - 0x4e, 0xd7, 0xf2, 0x9e, 0x1e, 0x95, 0xc7, 0x3f, 0x81, 0x0e, 0x94, 0x27, - 0x7d, 0x7b, 0x71, 0x21, 0x07, 0xb3, 0x0e, 0x01, 0xa6, 0x75, 0xe1, 0x52, - 0x3b, 0xcd, 0x7f, 0x6f, 0x19, 0x28, 0x5d, 0x1c, 0x7e, 0x2a, 0x8f, 0x7f, - 0xea, 0x91, 0x30, 0x71, 0x01, 0xdf, 0xf1, 0xe5, 0x25, 0x8b, 0xd8, 0x15, - 0xd0, 0x16, 0x00, 0xb5, 0xf7, 0xfe, 0x88, 0x08, 0x00, 0x95, 0xf4, 0xcc, - 0x80, 0x20, 0x6e, 0xbb, 0xa5, 0xdd, 0x03, 0xb8, 0x01, 0xdf, 0x40, 0x00, - 0x18, 0xdc, 0x89, 0x86, 0x53, 0xce, 0x6b, 0xba, 0x6b, 0xe9, 0xfd, 0x15, - 0x70, 0xc4, 0xd5, 0x33, 0xac, 0xab, 0x31, 0x3f, 0xe5, 0x13, 0x3d, 0x08, - 0xd3, 0xd0, 0x5e, 0x46, 0x55, 0xfc, 0xeb, 0xea, 0x93, 0x61, 0xe4, 0xec, - 0x78, 0xb0, 0x25, 0x50, 0xdc, 0x68, 0x73, 0x00, 0x19, 0x5f, 0x6d, 0x50, - 0x20, 0x67, 0x0e, 0x0c, 0x0e, 0xe1, 0x9c, 0x6c, 0xe2, 0xea, 0x9c, 0x4c, - 0x20, 0x21, 0x4c, 0x30, 0x72, 0x99, 0x20, 0xce, 0x49, 0x6c, 0xc4, 0x3a, - 0x20, 0x00, 0x8b, 0x5c, 0xe7, 0x4f, 0x7a, 0xdf, 0x3a, 0xca, 0x24, 0x71, - 0x82, 0x7b, 0xe1, 0x25, 0x1b, 0x24, 0x9e, 0xa1, 0x42, 0x5c, 0x53, 0x39, - 0x52, 0x81, 0x74, 0xfd, 0xc1, 0x4d, 0x05, 0x14, 0xc4, 0x02, 0xc5, 0x45, - 0x3a, 0x05, 0x96, 0x09, 0x26, 0x9d, 0xb8, 0x3a, 0x84, 0x5e, 0xf0, 0x00, - 0x01, 0x29, 0xad, 0x87, 0xad, 0x7b, 0x76, 0xd6, 0x4a, 0xc3, 0x52, 0x83, - 0x39, 0x19, 0xa5, 0x2c, 0x1f, 0x91, 0x53, 0xb0, 0x94, 0xff, 0x05, 0xe5, - 0x32, 0x77, 0x2c, 0x72, 0x0e, 0xa7, 0xfb, 0x8c, 0x6a, 0x59, 0x8a, 0x0b, - 0xcd, 0x14, 0x0e, 0x03, 0x06, 0x5d, 0x46, 0x21, 0xa7, 0x18, 0x66, 0x41, - 0x1e, 0x28, 0xc3, 0xc8, 0x15, 0xc6, 0x70, 0xb4, 0x0e, 0x9f, 0xb9, 0x39, - 0x76, 0x5d, 0x8f, 0x35, 0x67, 0x53, 0x46, 0x50, 0x35, 0xb1, 0x47, 0x69, - 0xed, 0xa5, 0x60, 0x1b, 0xc4, 0x54, 0x15, 0x06, 0x7d, 0xf4, 0x24, 0x2f, - 0x9f, 0x60, 0xa1, 0x59, 0x91, 0xe3, 0x37, 0x8c, 0x26, 0xe0, 0xff, 0xf1, - 0x50, 0x80, 0x2e, 0x9f, 0xfc, 0x21, 0x1a, 0xcb, 0xfd, 0xb0, 0xaf, 0x00, - 0x00, 0xa2, 0xb9, 0xba, 0x18, 0x2a, 0x38, 0x09, 0x85, 0x94, 0xe0, 0x2f, - 0x19, 0x6c, 0xb6, 0x8f, 0x5f, 0x0e, 0x7a, 0xee, 0xf3, 0x5f, 0xe9, 0xfd, - 0xb8, 0xf6, 0xe3, 0xe7, 0xcf, 0x5b, 0x9c, 0xf5, 0xdf, 0x13, 0x04, 0x2d, - 0xff, 0x2a, 0x9b, 0xd1, 0x67, 0xb8, 0x21, 0xff, 0x6d, 0x9a, 0xe8, 0xd5, - 0x77, 0x92, 0xdc, 0xc5, 0x74, 0x09, 0xce, 0x82, 0x73, 0x0a, 0x0d, 0x94, - 0x23, 0x1a, 0x0c, 0xe6, 0x22, 0xd1, 0x4e, 0x44, 0x56, 0x04, 0x8a, 0xdb, - 0xf9, 0xc0, 0x51, 0xa8, 0x94, 0x50, 0x18, 0x0a, 0x67, 0x40, 0x0d, 0x30, - 0x41, 0x98, 0xf0, 0xfe, 0xf4, 0x78, 0x34, 0x03, 0x29, 0x5c, 0x41, 0xa9, - 0x9b, 0x66, 0x87, 0x7a, 0xa9, 0xc4, 0x5e, 0xc3, 0x58, 0x5d, 0x72, 0xb9, - 0x80, 0x5c, 0x00, 0xd6, 0xe0, 0x80, 0x40, 0xb5, 0x36, 0xe8, 0x0c, 0xf6, - 0x5e, 0xfb, 0x92, 0xc9, 0xb1, 0x2c, 0x23, 0x9f, 0x3e, 0xff, 0x44, 0x08, - 0x14, 0x18, 0x86, 0xfa, 0x80, 0x0e, 0x04, 0xe7, 0x39, 0x3b, 0xee, 0x66, - 0xfd, 0xa5, 0xba, 0x13, 0x0d, 0x10, 0x4a, 0x69, 0xa4, 0xca, 0x32, 0xd4, - 0x00, 0x70, 0x7e, 0x8c, 0xf6, 0x80, 0x44, 0x23, 0x0a, 0xd6, 0x4d, 0x57, - 0x68, 0x1a, 0xed, 0x39, 0x28, 0xcb, 0xd1, 0x79, 0x01, 0xfb, 0x64, 0xd2, - 0xbe, 0x8a, 0x15, 0x08, 0x52, 0x70, 0x03, 0x15, 0x02, 0x45, 0x6c, 0x7e, - 0x98, 0x34, 0xfb, 0x1d, 0x68, 0x31, 0x00, 0x00, 0x43, 0xbc, 0x6c, 0xc8, - 0xc0, 0x0a, 0xc4, 0x88, 0x11, 0x48, 0x3c, 0x9a, 0x9f, 0xbe, 0x55, 0x9f, - 0x27, 0x6a, 0x95, 0xd0, 0x89, 0x02, 0x00, 0x47, 0x5a, 0x98, 0x11, 0x81, - 0x14, 0x16, 0xe9, 0x7c, 0x9e, 0xb2, 0x60, 0x60, 0x35, 0x0a, 0x3d, 0x74, - 0x6a, 0x12, 0x02, 0x04, 0xeb, 0xb1, 0x43, 0xcb, 0x3c, 0x98, 0x1c, 0xae, - 0x55, 0xdc, 0x8c, 0x32, 0x99, 0x84, 0xf5, 0xbd, 0xdb, 0xef, 0x02, 0xf0, - 0x00, 0x71, 0xca, 0x55, 0xe6, 0xb3, 0x2f, 0x58, 0xe7, 0x46, 0x24, 0x98, - 0x35, 0x9f, 0xcb, 0x43, 0x1b, 0x8e, 0x23, 0xeb, 0x0d, 0xc3, 0x45, 0x2c, - 0x42, 0xd3, 0xa9, 0x75, 0xe3, 0x76, 0xd9, 0x5d, 0x6f, 0x62, 0x14, 0xc8, - 0xb3, 0x40, 0x41, 0xcc, 0x39, 0xd6, 0xae, 0x43, 0x4c, 0x34, 0xff, 0x02, - 0xab, 0x00, 0x36, 0x4f, 0x79, 0x0f, 0x3c, 0xcd, 0x57, 0x6b, 0xb1, 0xd5, - 0xf5, 0x1a, 0x28, 0x4e, 0x70, 0xb8, 0x2f, 0x4e, 0x11, 0xad, 0x33, 0x6a, - 0x08, 0x27, 0x6c, 0x23, 0x1e, 0x5c, 0x15, 0x0b, 0xe9, 0x80, 0xa2, 0x1d, - 0xa0, 0x04, 0x8a, 0x95, 0x65, 0x88, 0xbd, 0x80, 0x05, 0x5f, 0xff, 0xf1, - 0x50, 0x80, 0x2e, 0x9f, 0xfc, 0x21, 0x2a, 0xcf, 0xfb, 0x0a, 0xcf, 0x08, - 0x00, 0x9d, 0xb7, 0xca, 0xcc, 0x90, 0x15, 0x13, 0x28, 0x03, 0x7a, 0x6c, - 0x6a, 0x56, 0xf4, 0xdf, 0x58, 0x4e, 0xbc, 0x7d, 0xfe, 0xff, 0xa4, 0xe3, - 0xcc, 0xf7, 0xcf, 0x8e, 0xf5, 0x90, 0x43, 0x5f, 0x74, 0x01, 0xbf, 0x21, - 0x45, 0x07, 0x9f, 0x96, 0x27, 0xc0, 0x4c, 0x5c, 0x39, 0x82, 0x18, 0x81, - 0xd9, 0xba, 0x2e, 0xb0, 0xdf, 0x70, 0xaf, 0xe0, 0x2d, 0x28, 0x25, 0x14, - 0xa3, 0x40, 0xd8, 0x61, 0x43, 0x42, 0x1e, 0x8d, 0x1c, 0xfc, 0x93, 0xc7, - 0xaf, 0x95, 0xc6, 0xe8, 0x57, 0x33, 0x06, 0x3b, 0x0a, 0xd9, 0xa9, 0xa1, - 0xc1, 0xf9, 0xef, 0xf6, 0x9d, 0x1a, 0x42, 0x00, 0x8c, 0xf3, 0xe3, 0xf4, - 0xf3, 0x24, 0x84, 0x15, 0x12, 0xcf, 0x1e, 0x4f, 0x5a, 0x28, 0xac, 0x42, - 0x6c, 0xd3, 0xf2, 0xda, 0x0b, 0x0a, 0xb1, 0x23, 0x7d, 0x7d, 0x41, 0xeb, - 0xff, 0x6e, 0x22, 0x3a, 0xed, 0x0f, 0xf4, 0x18, 0xab, 0xa5, 0x8f, 0x52, - 0x72, 0x94, 0x5d, 0x11, 0x8a, 0x54, 0x7d, 0x34, 0xe7, 0xd8, 0x23, 0x0a, - 0x32, 0x54, 0xaa, 0x6b, 0x8f, 0x80, 0x0a, 0x24, 0x53, 0x3e, 0x6a, 0xb4, - 0x07, 0x5b, 0x84, 0x04, 0x90, 0x90, 0x1a, 0x7c, 0xcf, 0xbb, 0xfc, 0xe6, - 0x3d, 0x40, 0xa8, 0x3d, 0xf8, 0x05, 0xdd, 0x99, 0xf6, 0x6e, 0xff, 0xd5, - 0x50, 0xb1, 0xa5, 0xbc, 0xcb, 0xf1, 0x1b, 0xdb, 0x37, 0x52, 0x8c, 0xc5, - 0x98, 0x6b, 0x05, 0xb5, 0x80, 0x10, 0x10, 0x82, 0x3a, 0xc1, 0x44, 0x59, - 0xc4, 0x04, 0x7b, 0xf2, 0x9c, 0x8d, 0x17, 0xb9, 0x63, 0xbf, 0x54, 0xf6, - 0xb4, 0x09, 0x2d, 0xee, 0x16, 0x54, 0xd6, 0x5d, 0x2a, 0x18, 0x65, 0x28, - 0x56, 0x96, 0xca, 0xa1, 0xa1, 0x40, 0xec, 0xe2, 0xb5, 0xe5, 0x09, 0x6a, - 0xad, 0xaf, 0x34, 0x15, 0x64, 0x69, 0x3b, 0x63, 0x72, 0x14, 0xc1, 0x3d, - 0x6a, 0xf1, 0x20, 0x04, 0xec, 0x22, 0xce, 0xed, 0xf7, 0x80, 0x00, 0x00, - 0x11, 0x9a, 0xd7, 0xcf, 0x9d, 0xde, 0x42, 0x54, 0xa1, 0x3f, 0xf7, 0x62, - 0x72, 0x33, 0x53, 0x44, 0xb8, 0x6a, 0x88, 0x33, 0x8c, 0x56, 0x3e, 0x93, - 0x9f, 0xc3, 0xc8, 0xa9, 0x4b, 0x2d, 0x90, 0xe0, 0x8d, 0x4e, 0xac, 0x53, - 0x83, 0xd0, 0x40, 0xcf, 0x8d, 0x84, 0x85, 0x29, 0xf9, 0x24, 0x68, 0xa8, - 0x26, 0x97, 0x17, 0x6d, 0xc7, 0xe6, 0xcc, 0xb5, 0x72, 0xc2, 0xb2, 0xef, - 0x74, 0xa3, 0x28, 0xab, 0x31, 0x0c, 0x8d, 0x4f, 0xe3, 0xfd, 0x8d, 0x08, - 0xdd, 0x80, 0x09, 0xd5, 0x2e, 0x71, 0xa1, 0x6b, 0xbf, 0x40, 0x10, 0x56, - 0x5e, 0x1c, 0x01, 0x74, 0x28, 0x37, 0x0a, 0xc4, 0x00, 0x4f, 0xff, 0xf1, - 0x50, 0x80, 0x36, 0x7f, 0xfc, 0x21, 0x4c, 0x36, 0xd6, 0x00, 0x90, 0x5b, - 0x2f, 0x31, 0x4e, 0xb4, 0x19, 0xa4, 0x56, 0x9a, 0x8d, 0xcd, 0x16, 0x69, - 0x9d, 0x54, 0x5a, 0xcc, 0x01, 0xe8, 0x58, 0x00, 0xd6, 0xc3, 0x5c, 0x7a, - 0xf8, 0xab, 0xfe, 0x7f, 0xf0, 0x1a, 0xe3, 0xc7, 0xc7, 0x5c, 0x58, 0xe9, - 0xfd, 0xe8, 0x1b, 0x4f, 0x9f, 0x0c, 0x8d, 0x7d, 0x90, 0x89, 0x54, 0x5c, - 0xaf, 0x05, 0x56, 0x15, 0x52, 0x2d, 0xc4, 0xe9, 0x2d, 0xb6, 0x35, 0x53, - 0xa0, 0x2e, 0xca, 0x85, 0x2d, 0xa6, 0x5a, 0x1d, 0x81, 0xd9, 0x2e, 0xec, - 0xc8, 0x6c, 0x82, 0x04, 0x50, 0x93, 0x17, 0x54, 0xe0, 0xb3, 0x6e, 0x5b, - 0xb2, 0x6f, 0x78, 0x46, 0xc2, 0x74, 0x6d, 0xb9, 0x3c, 0xaa, 0xe9, 0xef, - 0x7e, 0x53, 0xbf, 0x1f, 0xcc, 0x54, 0xee, 0x69, 0x7d, 0x2a, 0x2c, 0x66, - 0xb7, 0xfa, 0x4c, 0xa6, 0xb0, 0xce, 0x81, 0x41, 0x4c, 0x3b, 0x2e, 0x09, - 0xfc, 0xd0, 0x5b, 0x54, 0xe6, 0xca, 0xcc, 0x7b, 0xe1, 0x87, 0x2f, 0xa9, - 0x76, 0xe3, 0x83, 0x93, 0xc6, 0xea, 0xa7, 0x12, 0xaa, 0x45, 0xc6, 0x73, - 0xb3, 0x5f, 0xb1, 0xe4, 0x7f, 0x37, 0x2f, 0x8d, 0xe1, 0x65, 0xdf, 0x46, - 0x31, 0x25, 0xfd, 0xee, 0xcc, 0x82, 0x12, 0x52, 0xeb, 0x6f, 0xa4, 0xe6, - 0x74, 0x86, 0xfc, 0x27, 0x73, 0xa6, 0x65, 0x22, 0x79, 0xc8, 0xa0, 0xf1, - 0x0d, 0xeb, 0x82, 0xb7, 0x42, 0x57, 0x07, 0x08, 0x84, 0x2a, 0x76, 0x2d, - 0x85, 0x4a, 0x5b, 0xb1, 0xce, 0x04, 0x28, 0x4a, 0x64, 0x65, 0x5b, 0x3a, - 0x7e, 0x38, 0xc1, 0xc1, 0xd1, 0x48, 0x34, 0x99, 0xd4, 0x40, 0xcc, 0x03, - 0x5d, 0xad, 0x48, 0x6d, 0xa0, 0x40, 0x47, 0x77, 0x55, 0x05, 0x29, 0x50, - 0x59, 0xf3, 0x72, 0x01, 0x00, 0x00, 0xab, 0xa4, 0x5b, 0x09, 0x09, 0xe6, - 0xc8, 0x08, 0xfc, 0xce, 0x13, 0xaf, 0xb7, 0x43, 0x29, 0xf6, 0x93, 0x35, - 0x7e, 0xe0, 0xc3, 0x49, 0xea, 0x17, 0x95, 0x4a, 0x87, 0xa5, 0xd3, 0x8a, - 0xa2, 0x52, 0x6f, 0x72, 0xad, 0x51, 0xd6, 0x71, 0xd1, 0x9f, 0x8f, 0xfc, - 0x15, 0x77, 0xc7, 0x1c, 0x7b, 0x71, 0x63, 0x9b, 0x57, 0x7f, 0x73, 0xd6, - 0x26, 0x12, 0x23, 0xe7, 0xf5, 0xb9, 0xeb, 0x81, 0x3c, 0x36, 0xfc, 0xba, - 0x2a, 0x98, 0xb4, 0x08, 0x08, 0xda, 0x30, 0x4f, 0x1a, 0x85, 0x14, 0x68, - 0xde, 0x48, 0xf3, 0x07, 0xcc, 0x32, 0xab, 0xd0, 0x1b, 0xa8, 0x5a, 0xac, - 0x16, 0x2d, 0x6f, 0x39, 0x26, 0xd1, 0x0e, 0x0c, 0x15, 0x74, 0x83, 0xa9, - 0xd9, 0x9f, 0xb2, 0xf4, 0xfa, 0x73, 0x53, 0x43, 0xe7, 0xa9, 0x63, 0xe6, - 0xa7, 0x5c, 0x8f, 0x63, 0x80, 0xcb, 0x34, 0xa7, 0xdc, 0x5c, 0xd3, 0x69, - 0x0c, 0xe7, 0x3a, 0x92, 0x0c, 0xca, 0x41, 0xae, 0x2d, 0xe5, 0xca, 0x9c, - 0xdd, 0x81, 0xe5, 0xbb, 0x40, 0x66, 0x97, 0xa8, 0x72, 0xf1, 0x34, 0x9b, - 0xd0, 0xc0, 0xbb, 0xa8, 0xcd, 0x92, 0x5f, 0xe2, 0xb7, 0xdc, 0xa3, 0xb8, - 0xce, 0x02, 0x80, 0x60, 0x19, 0x1c, 0x6c, 0xad, 0xb6, 0xba, 0xd1, 0x46, - 0xa1, 0x7d, 0xc2, 0x2c, 0x0c, 0x48, 0xa0, 0x06, 0xec, 0x94, 0x86, 0x5c, - 0xbe, 0xff, 0xf1, 0x50, 0x80, 0x30, 0x9f, 0xfc, 0x21, 0x7a, 0xcf, 0xa0, - 0x00, 0x00, 0x00, 0x00, 0x9d, 0xb9, 0xba, 0x51, 0xac, 0x1a, 0x08, 0x00, - 0x0b, 0xc0, 0x01, 0xd7, 0x61, 0x2b, 0xeb, 0x96, 0xa5, 0xfe, 0xa2, 0xbc, - 0x24, 0x52, 0xdd, 0xae, 0xba, 0x95, 0xc5, 0x9e, 0x22, 0x9f, 0x58, 0xb6, - 0xf9, 0x91, 0xdc, 0xc2, 0xa6, 0xa1, 0x20, 0x4c, 0x90, 0xd9, 0x5a, 0x6b, - 0xfe, 0xb3, 0x12, 0xf5, 0xa4, 0x0a, 0xe3, 0x1e, 0x67, 0x8e, 0xe1, 0x67, - 0x72, 0x4d, 0x24, 0xd0, 0x9a, 0x32, 0xe5, 0x8f, 0x07, 0x76, 0xae, 0x14, - 0xa3, 0xa5, 0x18, 0x63, 0x22, 0x80, 0x0b, 0x40, 0x9a, 0x05, 0xff, 0xce, - 0x3a, 0x4f, 0x05, 0x8a, 0xb8, 0x55, 0x54, 0x63, 0x87, 0x15, 0x96, 0x57, - 0x1d, 0x45, 0xd3, 0xdd, 0x78, 0x3c, 0x4c, 0x74, 0x6f, 0x29, 0xeb, 0xe2, - 0xf2, 0xc7, 0x1d, 0x49, 0x8e, 0xcf, 0xfa, 0xb5, 0xaa, 0x6b, 0x26, 0x5b, - 0xb5, 0x74, 0xc5, 0x53, 0x1d, 0xd9, 0xd4, 0x99, 0x5c, 0x28, 0xb5, 0xc7, - 0x23, 0xe4, 0x6b, 0x2c, 0xbd, 0x4c, 0xcc, 0x67, 0x24, 0xe5, 0x77, 0xc1, - 0xc4, 0x72, 0xe0, 0x3b, 0x9c, 0x42, 0x38, 0x2a, 0x97, 0x06, 0xfd, 0x84, - 0x51, 0x48, 0x5c, 0x0a, 0xcb, 0xcf, 0x6a, 0xf0, 0x94, 0x28, 0x56, 0xf2, - 0x38, 0xeb, 0xd7, 0x78, 0xd2, 0x87, 0x69, 0xbc, 0x6c, 0x55, 0x58, 0x54, - 0xf6, 0xae, 0x1c, 0x8e, 0xda, 0xf5, 0x1d, 0xdd, 0xec, 0xca, 0xd5, 0x63, - 0x11, 0x32, 0x5b, 0xfc, 0x97, 0x9f, 0xef, 0x3d, 0x5c, 0x87, 0x4e, 0xab, - 0x86, 0x58, 0xa5, 0xdb, 0x3d, 0x2b, 0x6e, 0x85, 0x05, 0x1a, 0x4d, 0xf1, - 0xad, 0x2a, 0x80, 0xaa, 0x17, 0xbf, 0x10, 0x51, 0x54, 0x34, 0x36, 0x51, - 0xa0, 0xa9, 0x98, 0x1e, 0xd5, 0x1b, 0x19, 0x8a, 0xb9, 0x7b, 0x5c, 0xdb, - 0x77, 0xdc, 0xa1, 0x5d, 0x58, 0x84, 0x87, 0x2a, 0x8f, 0x2b, 0x86, 0x56, - 0xb5, 0x8c, 0x38, 0x73, 0xb6, 0xd6, 0x3a, 0x1f, 0xdf, 0x01, 0x0f, 0x5e, - 0xde, 0x30, 0xbc, 0x1e, 0xdd, 0xd5, 0x7e, 0xbf, 0x6c, 0xd6, 0xf5, 0x57, - 0x7d, 0x7a, 0xeb, 0x9b, 0x86, 0xb3, 0x15, 0x7f, 0xe4, 0x0d, 0xbb, 0xf7, - 0xaf, 0x6f, 0xac, 0xb4, 0x0c, 0x2a, 0xf7, 0xa3, 0x04, 0x42, 0x71, 0xdb, - 0xf1, 0x88, 0xb1, 0x91, 0xb9, 0x65, 0x1a, 0x29, 0xe1, 0x8b, 0x05, 0x71, - 0xc3, 0x33, 0x09, 0x50, 0xbe, 0xee, 0x5c, 0x74, 0x34, 0xa5, 0x5b, 0x93, - 0x54, 0x20, 0x03, 0x6a, 0x53, 0xf1, 0x90, 0x04, 0x06, 0xcc, 0x69, 0x79, - 0x89, 0xfe, 0xd9, 0x6e, 0xc8, 0x69, 0x83, 0xa9, 0x22, 0xb0, 0x75, 0xba, - 0xa5, 0x4e, 0xb2, 0x4c, 0x3d, 0x1c, 0x45, 0x29, 0xdf, 0xd6, 0x30, 0x0b, - 0xa5, 0x76, 0x28, 0x3e, 0x9b, 0xe3, 0xad, 0xa1, 0x6c, 0xd1, 0x47, 0x3b, - 0xe2, 0x73, 0x96, 0x77, 0x70, 0xff, 0xf1, 0x50, 0x80, 0x30, 0x5f, 0xfc, - 0x21, 0x1a, 0xcb, 0xea, 0xa0, 0xf9, 0x00, 0x00, 0xa0, 0xb4, 0xc1, 0xe5, - 0x66, 0x75, 0x12, 0x06, 0x0c, 0xd0, 0x6f, 0x5b, 0xd6, 0xf5, 0xbd, 0x6f, - 0xf5, 0xfc, 0xd3, 0x41, 0xb6, 0xb7, 0xfb, 0xfe, 0xda, 0xac, 0xf3, 0xd5, - 0xbc, 0xee, 0x6a, 0x58, 0x4f, 0x29, 0x92, 0x58, 0x2d, 0x46, 0x68, 0x8e, - 0x90, 0x8d, 0xb9, 0xf1, 0x5f, 0xd9, 0xd6, 0x61, 0xc6, 0x2e, 0x94, 0x0d, - 0x74, 0xaa, 0x3b, 0xb4, 0xcc, 0x22, 0xd6, 0x65, 0x09, 0xca, 0x93, 0xa6, - 0x20, 0x5b, 0x29, 0x24, 0xa9, 0xdd, 0x48, 0x82, 0x33, 0x90, 0x28, 0xa9, - 0x6d, 0x68, 0xa2, 0x1c, 0xef, 0xd4, 0x3f, 0x4e, 0x25, 0xb5, 0xb4, 0x92, - 0x38, 0xb8, 0xb1, 0xf1, 0xfa, 0x11, 0x53, 0x81, 0xd0, 0xcc, 0xc9, 0x53, - 0x7e, 0x7f, 0xd2, 0xe6, 0x2e, 0x4b, 0xc0, 0x0b, 0xcb, 0x2b, 0x16, 0x12, - 0xac, 0x8e, 0xab, 0x83, 0x74, 0x2d, 0x14, 0x63, 0xaf, 0xd1, 0x3c, 0xeb, - 0x8e, 0x7f, 0xaf, 0x79, 0x3f, 0x90, 0xf3, 0x65, 0x5a, 0x35, 0x90, 0x97, - 0x52, 0x9e, 0x05, 0xf0, 0xb6, 0x34, 0xc1, 0xd9, 0x62, 0x92, 0x91, 0x69, - 0xb5, 0x7a, 0xc8, 0xce, 0xde, 0x22, 0x6b, 0xbb, 0x9b, 0x77, 0x60, 0xc0, - 0xbc, 0x4a, 0x3b, 0xff, 0x85, 0x51, 0x50, 0xe2, 0xb3, 0x31, 0xf3, 0xd2, - 0xfd, 0x00, 0xa0, 0x55, 0x09, 0x07, 0x34, 0xf2, 0xf2, 0x0b, 0x1b, 0x9e, - 0x87, 0x7c, 0x12, 0x59, 0x5b, 0x9d, 0x0d, 0x14, 0x0c, 0xb7, 0x3e, 0x35, - 0xc4, 0xb5, 0x96, 0x84, 0x2c, 0xbc, 0xa7, 0x42, 0x13, 0x81, 0x15, 0xa4, - 0xb6, 0xcb, 0xbb, 0xa2, 0x93, 0xf3, 0x48, 0xa9, 0x9d, 0x40, 0xce, 0x0d, - 0x43, 0x80, 0x69, 0x54, 0xf4, 0xb3, 0x0b, 0x61, 0x80, 0xd5, 0x0e, 0x3d, - 0x41, 0x0c, 0xc9, 0xa5, 0x13, 0x49, 0xba, 0x9f, 0xb2, 0xb4, 0xb1, 0xd0, - 0x15, 0x56, 0xf6, 0x11, 0x2a, 0x76, 0xde, 0xec, 0xf4, 0xa8, 0x7c, 0xc0, - 0x00, 0x00, 0x03, 0x5a, 0xf1, 0xd7, 0x32, 0x59, 0x8d, 0xaf, 0x03, 0x8a, - 0x4f, 0x0c, 0x68, 0xa4, 0xb9, 0x5d, 0x73, 0x21, 0x96, 0x57, 0x00, 0xc8, - 0x64, 0x7a, 0x28, 0x0b, 0x3d, 0x81, 0xed, 0x0a, 0xa6, 0x98, 0xca, 0xfe, - 0x00, 0x0b, 0x95, 0x5e, 0x70, 0x73, 0xe8, 0x90, 0x29, 0x43, 0xd5, 0x2b, - 0x41, 0x0c, 0xb4, 0x1e, 0x69, 0xca, 0xef, 0xe3, 0xf0, 0x46, 0x1f, 0xa9, - 0x3f, 0xed, 0xfe, 0x3f, 0x46, 0xeb, 0xf7, 0x6b, 0xdc, 0xae, 0x07, 0xa2, - 0x9a, 0xe2, 0x7e, 0x73, 0x4b, 0x47, 0x01, 0x01, 0x79, 0x6c, 0x6f, 0xd9, - 0xc1, 0xc4, 0xa8, 0x8c, 0xf5, 0xb5, 0x4a, 0xd3, 0xe1, 0x67, 0x38, 0xf1, - 0xb1, 0x85, 0x6e, 0xd7, 0xed, 0x98, 0x79, 0x97, 0xa3, 0x6e, 0x36, 0x38, - 0x58, 0x28, 0x80, 0xaf, 0x7d, 0x33, 0x3c, 0xff, 0xf1, 0x50, 0x80, 0x29, - 0xbf, 0xfc, 0x21, 0x1a, 0xcc, 0xec, 0xf0, 0x75, 0x00, 0x00, 0x9f, 0xb4, - 0xcc, 0x8d, 0x0c, 0x86, 0xda, 0x32, 0xdb, 0x96, 0x3c, 0xfc, 0x92, 0xcd, - 0xeb, 0x2f, 0x3f, 0x9f, 0xe3, 0x89, 0xdf, 0xb7, 0x5d, 0x73, 0xd7, 0x3e, - 0xde, 0x35, 0xa1, 0x47, 0xc9, 0xe9, 0x6d, 0x37, 0x4b, 0x73, 0x2a, 0x52, - 0x71, 0x8b, 0xe5, 0x5d, 0xb2, 0x03, 0xcc, 0x52, 0xd4, 0x09, 0xaf, 0x9e, - 0xfa, 0xe5, 0xa2, 0x94, 0xd4, 0xfa, 0x6d, 0x6d, 0x1f, 0x8c, 0x72, 0xf4, - 0xee, 0xb5, 0xbe, 0x67, 0xc1, 0x56, 0x13, 0xa7, 0xb1, 0x58, 0xc2, 0x95, - 0xbb, 0xf3, 0xef, 0x55, 0xda, 0x09, 0x58, 0x71, 0x8a, 0xe2, 0xe6, 0x65, - 0x66, 0xa2, 0x6c, 0xd6, 0x87, 0x26, 0x80, 0x64, 0x09, 0x9a, 0xb9, 0xa8, - 0x00, 0x90, 0x67, 0xdc, 0x20, 0x26, 0x13, 0x8f, 0x72, 0x99, 0xfe, 0xd3, - 0x3f, 0xc6, 0xed, 0x94, 0x04, 0xd0, 0x66, 0x20, 0xbe, 0x2a, 0x34, 0xc1, - 0xa8, 0x99, 0x01, 0x2f, 0xe9, 0x0e, 0xbb, 0xbb, 0x5b, 0xfd, 0x7e, 0x6e, - 0x4e, 0x9d, 0x2c, 0x73, 0x18, 0x9a, 0x12, 0x25, 0x33, 0xeb, 0x93, 0x50, - 0x00, 0x9a, 0x01, 0x82, 0x20, 0x65, 0xc0, 0x99, 0xb5, 0x0e, 0xe9, 0xe7, - 0x49, 0x89, 0xb6, 0xd0, 0x85, 0x4d, 0x86, 0xec, 0x94, 0x02, 0x04, 0x85, - 0x08, 0x4a, 0x01, 0x5a, 0x86, 0xda, 0x7c, 0xa9, 0xcb, 0x61, 0x77, 0xe7, - 0xdd, 0x38, 0x54, 0xe8, 0x2c, 0xc1, 0x4c, 0x20, 0xda, 0xcd, 0x52, 0x5b, - 0x7d, 0xa4, 0x5b, 0x19, 0x08, 0xb3, 0x97, 0x03, 0xeb, 0x8b, 0x29, 0xac, - 0xc2, 0xcf, 0xf2, 0x4f, 0x5e, 0xc9, 0x5e, 0xc8, 0x27, 0xee, 0x92, 0x7e, - 0xd5, 0x31, 0xf8, 0x01, 0xf1, 0xee, 0x00, 0x2f, 0x00, 0xbd, 0x6e, 0xf2, - 0x55, 0xf3, 0xd6, 0xe9, 0xce, 0xa0, 0x37, 0x4a, 0xbd, 0x88, 0xab, 0x94, - 0x8b, 0x2d, 0x05, 0xf8, 0x0c, 0x48, 0xba, 0x49, 0xa4, 0x24, 0x99, 0x44, - 0x3c, 0x54, 0x79, 0x87, 0x23, 0x48, 0x4e, 0x7c, 0xef, 0xc1, 0xe8, 0xd3, - 0x77, 0xde, 0xab, 0x85, 0x8d, 0xe8, 0xdd, 0x30, 0xf1, 0xb9, 0x97, 0x5d, - 0xcf, 0x47, 0xc3, 0xfb, 0xf7, 0x09, 0xa0, 0xdb, 0x31, 0x3b, 0x92, 0x6c, - 0x39, 0xd1, 0xa5, 0x7b, 0x20, 0x52, 0x06, 0xfb, 0xbe, 0x4e, 0x5c, 0xcc, - 0x0b, 0x95, 0xde, 0xc1, 0x16, 0xaa, 0x5e, 0x66, 0xbf, 0x3f, 0x74, 0x6e, - 0xc4, 0x0d, 0x08, 0x0e, 0xff, 0xf1, 0x50, 0x80, 0x2c, 0x5f, 0xfc, 0x21, - 0x1a, 0xcf, 0xe8, 0xf7, 0x7d, 0xbf, 0x80, 0xa1, 0xb4, 0xca, 0x8d, 0xec, - 0x28, 0x0b, 0x12, 0x04, 0x07, 0xef, 0xfb, 0x1a, 0xd8, 0xff, 0xcb, 0xfb, - 0xf6, 0xd3, 0x77, 0x9a, 0x64, 0xf8, 0xe6, 0xf3, 0x77, 0xf1, 0xae, 0x39, - 0xc9, 0x7d, 0x50, 0x4b, 0x98, 0x79, 0x39, 0x2c, 0x6d, 0x13, 0x55, 0x9b, - 0x62, 0x76, 0xd1, 0xc5, 0x74, 0x2e, 0x5c, 0xcb, 0x9e, 0x91, 0xc9, 0xa2, - 0xf2, 0xea, 0xcf, 0x74, 0x1a, 0xdd, 0x1f, 0x3f, 0x9d, 0xd5, 0xe3, 0xb4, - 0xbe, 0x76, 0xc5, 0x7c, 0xab, 0xc4, 0xfa, 0x46, 0x8b, 0x95, 0x90, 0x8d, - 0x5c, 0xf9, 0x7f, 0xaf, 0x71, 0x12, 0xaf, 0x89, 0x4c, 0xf2, 0x2f, 0x8e, - 0x2c, 0xda, 0x39, 0xd3, 0xfe, 0xee, 0x46, 0x06, 0xc4, 0xfe, 0x99, 0xd8, - 0x23, 0x1d, 0x81, 0xe4, 0x8b, 0xc0, 0x53, 0xf8, 0xfe, 0x43, 0x53, 0x99, - 0xf5, 0x1c, 0x2e, 0xd7, 0xc2, 0xe1, 0xbc, 0x17, 0xb4, 0xf2, 0x38, 0xf1, - 0xfd, 0x86, 0xf2, 0xac, 0x18, 0xf5, 0xef, 0xa6, 0x09, 0x7c, 0xef, 0xdc, - 0x05, 0x85, 0xf3, 0x74, 0x02, 0x17, 0x04, 0x11, 0x34, 0x2c, 0xdd, 0x49, - 0x04, 0xaf, 0x01, 0x8a, 0x06, 0xd9, 0x91, 0x68, 0x21, 0xf6, 0x44, 0xcb, - 0xa8, 0x02, 0xb8, 0x10, 0x60, 0x73, 0x29, 0xaa, 0x90, 0xc0, 0xb5, 0x21, - 0x59, 0x25, 0x11, 0x96, 0x0d, 0x60, 0x3e, 0x74, 0xbc, 0x14, 0xe8, 0x77, - 0xdd, 0x0d, 0xc6, 0xd3, 0x50, 0x40, 0x09, 0x8c, 0x8a, 0x79, 0x53, 0x40, - 0x08, 0x21, 0x51, 0x28, 0xdd, 0xa4, 0x93, 0x8d, 0x33, 0xee, 0x9e, 0xdb, - 0xef, 0xaa, 0x06, 0x62, 0x68, 0xd5, 0x0e, 0x31, 0xd7, 0x52, 0x87, 0x15, - 0x9e, 0xfb, 0x34, 0x6e, 0xa9, 0xdd, 0x25, 0x42, 0xb9, 0xa9, 0xf7, 0xec, - 0x78, 0x88, 0x05, 0x05, 0x1d, 0xd0, 0x50, 0xc2, 0x66, 0x7c, 0x83, 0x0f, - 0x98, 0x0d, 0x6c, 0x07, 0x4e, 0xdf, 0x5e, 0x37, 0x79, 0xad, 0xc9, 0x5c, - 0x2b, 0x25, 0xa6, 0xf2, 0x56, 0xa8, 0x2e, 0x6d, 0x7a, 0x0b, 0x40, 0x4e, - 0x80, 0xe6, 0x30, 0x7c, 0x5a, 0xbc, 0x7a, 0x56, 0xff, 0xe0, 0xe6, 0x9b, - 0xa4, 0x2f, 0x7f, 0x76, 0x0e, 0x57, 0x97, 0xc9, 0x78, 0xe3, 0xb1, 0x1a, - 0x3b, 0x0a, 0x9e, 0x36, 0x1d, 0x97, 0xa9, 0xc3, 0x03, 0x42, 0xe2, 0xf6, - 0x42, 0xb3, 0x35, 0xb3, 0xbd, 0xf6, 0x19, 0x82, 0x25, 0x6d, 0x01, 0x50, - 0xaa, 0xd1, 0x19, 0xc9, 0xab, 0x9c, 0xc9, 0x85, 0xf3, 0x0d, 0xa0, 0xac, - 0xe0, 0x11, 0x25, 0x84, 0x1b, 0x4c, 0x67, 0xbf, 0x57, 0x80, 0xff, 0xf1, - 0x50, 0x80, 0x2d, 0xff, 0xfc, 0x21, 0x1a, 0xcf, 0xbf, 0xf4, 0xfd, 0x80, - 0x00, 0xa4, 0xa6, 0xb9, 0x6c, 0x32, 0xa3, 0x2b, 0x24, 0xd5, 0x67, 0x5c, - 0x7c, 0xca, 0xf8, 0xee, 0xba, 0xf5, 0xfe, 0x39, 0xf1, 0xdf, 0xeb, 0xcf, - 0x3a, 0xdf, 0x59, 0x79, 0x75, 0x78, 0xd7, 0x5a, 0x9e, 0xba, 0xe9, 0xba, - 0x0e, 0xad, 0x9a, 0x9c, 0x13, 0x1a, 0xe9, 0x76, 0xc8, 0xac, 0xa9, 0xda, - 0x3a, 0xc1, 0x93, 0x1a, 0xef, 0xb9, 0xdb, 0x88, 0x40, 0xb1, 0x8e, 0x8a, - 0x24, 0xef, 0xc2, 0x07, 0x62, 0x13, 0x85, 0x53, 0xc6, 0xfc, 0xdb, 0x39, - 0xb1, 0xe2, 0x43, 0x38, 0xcd, 0x4c, 0x5e, 0x0f, 0x1e, 0xa0, 0x28, 0x00, - 0x35, 0xb4, 0x18, 0x8a, 0xd0, 0x99, 0xbd, 0x7e, 0x65, 0x92, 0x72, 0x6c, - 0x35, 0x4b, 0x9e, 0xc3, 0x40, 0x2b, 0x3c, 0xc5, 0x2c, 0x8d, 0xbf, 0x8b, - 0xd3, 0x94, 0x4e, 0x54, 0xc4, 0x81, 0x7a, 0xb6, 0x15, 0x60, 0x95, 0xaf, - 0x57, 0xd6, 0x78, 0x72, 0x22, 0xc0, 0xcb, 0x3f, 0xa5, 0xab, 0x73, 0x3b, - 0x16, 0x97, 0xae, 0xd9, 0x8b, 0x4b, 0xd0, 0x4d, 0xd1, 0xa6, 0x1a, 0x9d, - 0xde, 0x6c, 0x2f, 0xa9, 0x91, 0x0c, 0x0f, 0x4a, 0xe7, 0x32, 0x68, 0x04, - 0x35, 0x56, 0xc1, 0x10, 0xcc, 0x44, 0xfa, 0x4a, 0x67, 0x11, 0x42, 0x69, - 0x51, 0xa7, 0x19, 0x14, 0x13, 0xc3, 0x95, 0x54, 0x2b, 0xa0, 0x51, 0xe4, - 0x98, 0x6d, 0xc8, 0x9d, 0x99, 0xaf, 0x95, 0x51, 0x00, 0xd2, 0x14, 0x85, - 0x4d, 0x28, 0x8f, 0x3c, 0xae, 0x37, 0x32, 0x28, 0x4d, 0xed, 0x07, 0x9d, - 0xfc, 0xb9, 0xcd, 0xa7, 0x5e, 0x9b, 0x4e, 0xf9, 0xde, 0xb8, 0xff, 0x7c, - 0x48, 0x1f, 0x74, 0xc5, 0x47, 0x87, 0x2e, 0x57, 0x7d, 0xf9, 0x3c, 0xfe, - 0x0d, 0x72, 0x2e, 0x77, 0x63, 0x42, 0x4f, 0xd8, 0x58, 0xa6, 0x45, 0x60, - 0x8c, 0xf0, 0x58, 0xf8, 0x8d, 0xc9, 0x6d, 0x23, 0x1d, 0xeb, 0xef, 0x35, - 0x59, 0xd7, 0x1f, 0x32, 0xbe, 0x3b, 0xae, 0xbd, 0x7c, 0x6b, 0xe6, 0x7d, - 0x73, 0xce, 0xb7, 0xd7, 0x7d, 0x66, 0x59, 0x39, 0xeb, 0x7a, 0xf5, 0xc5, - 0xd6, 0x40, 0x17, 0xf1, 0x63, 0x61, 0xbf, 0x97, 0xf3, 0x0c, 0xa3, 0xdd, - 0xf1, 0x2b, 0x47, 0xd8, 0xae, 0xae, 0x04, 0x56, 0x3d, 0xe4, 0x43, 0x3b, - 0xe0, 0x6d, 0x34, 0xe7, 0x63, 0x2d, 0xdb, 0x33, 0x34, 0x3b, 0x09, 0xf2, - 0xf8, 0x3c, 0xd1, 0x48, 0xce, 0x2f, 0x58, 0x35, 0xa5, 0x5c, 0x4e, 0x06, - 0xfb, 0x91, 0x00, 0xa2, 0xaa, 0x60, 0xa3, 0x1d, 0xdb, 0x4b, 0xc2, 0x0b, - 0xec, 0xf4, 0x84, 0xea, 0x6a, 0xaf, 0x48, 0x94, 0xde, 0xd9, 0xd1, 0x15, - 0x78, 0x16, 0x14, 0x9b, 0xe0, 0xff, 0xf1, 0x50, 0x80, 0x33, 0xff, 0xfc, - 0x21, 0x1a, 0xce, 0xd3, 0xac, 0xf9, 0x83, 0x40, 0x9f, 0xb5, 0x31, 0x1d, - 0x88, 0xd8, 0x21, 0xcf, 0x0e, 0x78, 0xe5, 0xc1, 0xcf, 0x1c, 0xf0, 0xe5, - 0xc0, 0x66, 0x70, 0xdd, 0xc9, 0x2b, 0x8c, 0xdd, 0xfc, 0x75, 0x79, 0xa6, - 0x98, 0x65, 0x62, 0x40, 0x30, 0xd9, 0x23, 0x80, 0x4d, 0x5a, 0x07, 0xa0, - 0xe4, 0x34, 0x49, 0x1f, 0x77, 0xbb, 0xf7, 0x98, 0x14, 0x14, 0xe2, 0xe6, - 0x21, 0x48, 0x92, 0x6c, 0x97, 0x4e, 0xfd, 0x30, 0x6b, 0x3a, 0x49, 0xbb, - 0xea, 0xd2, 0xf6, 0xae, 0x63, 0x27, 0x9b, 0xd3, 0x75, 0xf2, 0xc1, 0x17, - 0xa9, 0x8e, 0xf9, 0xa4, 0xe7, 0x7d, 0x87, 0xd8, 0xfe, 0xc4, 0x23, 0x0d, - 0x7d, 0x3b, 0xcd, 0x7c, 0xbd, 0x3d, 0xb2, 0xd9, 0xd5, 0xfc, 0x0c, 0x58, - 0xe7, 0x55, 0xc8, 0xcd, 0xba, 0x3a, 0x8d, 0x9c, 0xb4, 0x65, 0xd9, 0x78, - 0x25, 0xe7, 0xcb, 0x9d, 0xcb, 0xac, 0xeb, 0x7e, 0x4b, 0xd4, 0xfe, 0x06, - 0x18, 0x60, 0xa6, 0x11, 0xdf, 0xec, 0xdc, 0x05, 0xbf, 0x84, 0x60, 0x64, - 0xe0, 0x1f, 0x68, 0x73, 0x00, 0x09, 0xb0, 0x77, 0x7c, 0x32, 0x80, 0xc5, - 0xab, 0x75, 0xbb, 0x88, 0x9a, 0x07, 0x82, 0xa5, 0xb6, 0x5a, 0xdd, 0x3b, - 0x4b, 0xee, 0xe6, 0x0f, 0xee, 0x0b, 0xa5, 0xc0, 0x27, 0xa5, 0x95, 0x2b, - 0x4b, 0x49, 0x05, 0x76, 0x36, 0x79, 0x2f, 0x72, 0x21, 0x92, 0x56, 0xe3, - 0xf0, 0x7c, 0x3e, 0xd1, 0x90, 0x66, 0x19, 0x5d, 0xb6, 0xc4, 0x3b, 0x6e, - 0x32, 0xbb, 0xbb, 0x0f, 0x53, 0xc0, 0x09, 0x39, 0xc7, 0x36, 0xff, 0xf3, - 0x7f, 0xfd, 0xff, 0xf9, 0xe8, 0xa0, 0xfd, 0x43, 0x87, 0x9a, 0x80, 0xc2, - 0x39, 0x41, 0x73, 0x86, 0x95, 0x0b, 0x0f, 0xc1, 0xb2, 0xdd, 0x88, 0x8e, - 0xbe, 0x36, 0x25, 0x07, 0xad, 0xe4, 0xd5, 0xd8, 0x4c, 0x78, 0xf9, 0x15, - 0xb5, 0x5b, 0xf7, 0xc3, 0x01, 0x3a, 0xee, 0xf5, 0x39, 0x11, 0xc9, 0x14, - 0xc8, 0xcc, 0xed, 0xa2, 0xe3, 0xc7, 0x9c, 0x29, 0xa0, 0xef, 0x4d, 0x99, - 0x3b, 0x05, 0xd4, 0x08, 0xcf, 0xc1, 0xec, 0x64, 0x2b, 0x3b, 0x1a, 0x82, - 0xa2, 0xf7, 0x80, 0x00, 0x00, 0x05, 0xd3, 0x9b, 0xa9, 0x37, 0x5c, 0x27, - 0x31, 0xa6, 0x19, 0x43, 0xfe, 0x0c, 0x36, 0x00, 0x00, 0x15, 0xc8, 0xb6, - 0x56, 0x35, 0x27, 0x9f, 0x91, 0x1a, 0x84, 0x33, 0xc1, 0x37, 0x50, 0xe4, - 0x36, 0x61, 0xcb, 0xe6, 0xde, 0xc4, 0xd2, 0x79, 0xb1, 0xd9, 0x90, 0x82, - 0xc7, 0x28, 0x2a, 0x10, 0xd2, 0x66, 0x08, 0x74, 0x71, 0x07, 0x10, 0x75, - 0xc6, 0x00, 0x87, 0x60, 0xef, 0x72, 0x5e, 0xfc, 0x81, 0xaf, 0x11, 0x21, - 0xa6, 0xa3, 0x10, 0x80, 0x6f, 0x08, 0x77, 0xa5, 0x74, 0x21, 0x82, 0x2a, - 0x58, 0xb6, 0xa3, 0x28, 0x84, 0x1f, 0x00, 0x83, 0x51, 0x3b, 0x3a, 0x09, - 0x13, 0x35, 0x82, 0x08, 0x39, 0xe0, 0x68, 0x18, 0x20, 0xf7, 0xf2, 0xc0, - 0x51, 0x1b, 0xdc, 0xf4, 0x22, 0xb5, 0x3a, 0x37, 0xc8, 0x2c, 0x4a, 0xf8, - 0xff, 0xf1, 0x50, 0x80, 0x2c, 0x7f, 0xfc, 0x21, 0x1a, 0xcf, 0xb7, 0x47, - 0x3b, 0x00, 0x00, 0xa1, 0xb7, 0xa0, 0xe0, 0x2c, 0x47, 0x1a, 0x8c, 0x4a, - 0x85, 0x82, 0x00, 0x00, 0x00, 0x00, 0x9a, 0xad, 0x5c, 0xdf, 0x9e, 0x3a, - 0xb0, 0x2e, 0x74, 0x15, 0x19, 0xda, 0x94, 0xae, 0x88, 0x8b, 0x5c, 0xaf, - 0x38, 0x79, 0xd6, 0x0a, 0xfa, 0xfe, 0x35, 0x3e, 0xf3, 0x16, 0xeb, 0x33, - 0x50, 0x82, 0x73, 0xc3, 0x14, 0x58, 0xec, 0x8f, 0x55, 0xc0, 0x84, 0xc4, - 0xcb, 0xb8, 0xf1, 0x50, 0x41, 0x7b, 0x3b, 0xe4, 0xc9, 0x87, 0xc0, 0x67, - 0xfe, 0x63, 0x80, 0x6e, 0x20, 0xcd, 0x9e, 0x49, 0xa9, 0x3a, 0x28, 0x3f, - 0xd6, 0x8f, 0x54, 0x88, 0x4a, 0x85, 0x4a, 0x52, 0xb1, 0x68, 0xdf, 0xad, - 0x9d, 0x77, 0x1c, 0xf9, 0xc5, 0xc4, 0xb1, 0xc5, 0x51, 0x8c, 0xc5, 0xb5, - 0xbd, 0x9e, 0x33, 0x05, 0x2e, 0xe2, 0xab, 0x95, 0x44, 0x56, 0x30, 0xa1, - 0x8a, 0xcb, 0x0c, 0xaf, 0x13, 0x3e, 0xd6, 0x68, 0xc9, 0x0a, 0x4a, 0xfc, - 0xf2, 0xe0, 0xd9, 0x5b, 0x5c, 0x77, 0x1e, 0x2c, 0x56, 0x0c, 0xf7, 0x7d, - 0x35, 0x29, 0xc3, 0x6f, 0x2f, 0x31, 0x1b, 0x01, 0x56, 0xd0, 0x51, 0x55, - 0xd5, 0xcc, 0x2d, 0x0d, 0x84, 0xeb, 0x55, 0xa0, 0xe3, 0x53, 0xef, 0x22, - 0xa9, 0xc0, 0x49, 0xab, 0x6d, 0x88, 0xf1, 0x4b, 0x64, 0x67, 0xb1, 0x80, - 0x56, 0xeb, 0xf1, 0xd1, 0x42, 0xf6, 0xa6, 0x40, 0xa7, 0xc6, 0xf0, 0x47, - 0x0b, 0x12, 0x29, 0xa2, 0x81, 0x4a, 0xb9, 0xd0, 0x5f, 0x55, 0x91, 0x53, - 0xea, 0x9d, 0xb5, 0x13, 0x33, 0xb6, 0x68, 0x4c, 0xbf, 0x38, 0x1f, 0xa7, - 0x4d, 0x9b, 0x9b, 0x4c, 0x1c, 0xbc, 0xc9, 0xe4, 0xa5, 0xc9, 0x49, 0x1d, - 0x6d, 0x09, 0xd5, 0xcf, 0x6e, 0x64, 0xeb, 0x6b, 0x60, 0x8d, 0x5f, 0x9e, - 0x0d, 0x65, 0x0c, 0x35, 0xcc, 0xa9, 0xf7, 0x86, 0xf4, 0xde, 0x8d, 0xeb, - 0x66, 0x8d, 0xb4, 0x6f, 0x4d, 0xab, 0x59, 0xac, 0x93, 0x31, 0x22, 0x05, - 0x59, 0x55, 0xf7, 0x7a, 0xba, 0xfa, 0x20, 0x87, 0x7c, 0x2a, 0x4c, 0x20, - 0x50, 0x39, 0x0c, 0x9d, 0x45, 0x24, 0x71, 0x00, 0xe1, 0x70, 0x3f, 0xa2, - 0x17, 0x15, 0xbb, 0xfc, 0x1d, 0xb2, 0xb2, 0xcf, 0x28, 0xcf, 0x34, 0xe7, - 0xb3, 0x48, 0xec, 0xbf, 0xdd, 0xfb, 0x38, 0xd0, 0x63, 0xe7, 0x31, 0x48, - 0xe0, 0x77, 0xfb, 0x2a, 0xb6, 0xde, 0x0d, 0x54, 0x4f, 0x6d, 0x55, 0x3a, - 0xf0, 0x4a, 0x8d, 0x67, 0x2b, 0x0d, 0xfd, 0xfb, 0x2e, 0xae, 0xa0, 0x44, - 0x28, 0xa1, 0x92, 0x43, 0x24, 0xdb, 0x80, 0xff, 0xf1, 0x50, 0x80, 0x33, - 0x1f, 0xfc, 0x21, 0x1a, 0xcf, 0xb2, 0xce, 0xff, 0xa0, 0x00, 0x9d, 0xb9, - 0xb1, 0x1c, 0xa6, 0x54, 0x1b, 0x05, 0x05, 0x04, 0x0c, 0xb6, 0x2f, 0x2d, - 0x8b, 0x32, 0xc0, 0x32, 0x7b, 0x77, 0x5f, 0x19, 0x3a, 0xf4, 0xfa, 0xe2, - 0xf6, 0x1a, 0xe4, 0xc2, 0x54, 0xae, 0xad, 0xc3, 0xf4, 0x77, 0x2e, 0x99, - 0xff, 0x21, 0x98, 0x44, 0x29, 0xd6, 0xa2, 0xbf, 0x75, 0xd4, 0xc1, 0x9c, - 0x48, 0xdf, 0xf8, 0x49, 0x06, 0x8a, 0x21, 0x6d, 0x43, 0x96, 0x07, 0x8a, - 0xeb, 0x5a, 0x48, 0x64, 0x06, 0xb9, 0x2d, 0x30, 0x2c, 0x9a, 0x95, 0x11, - 0xa6, 0x2f, 0x2d, 0x1b, 0x60, 0xd4, 0x43, 0x16, 0x62, 0x5f, 0x0b, 0x83, - 0x0b, 0x68, 0x2e, 0x42, 0x70, 0x1b, 0xb2, 0x63, 0x41, 0x9c, 0x0d, 0x0c, - 0x7a, 0x21, 0xb2, 0x02, 0x3e, 0x0e, 0x68, 0x0e, 0x45, 0x19, 0x7f, 0x8f, - 0xf4, 0xa2, 0x32, 0x35, 0x71, 0xc6, 0x2a, 0x58, 0x67, 0x15, 0xe0, 0x72, - 0x71, 0x80, 0x9c, 0x44, 0x63, 0x13, 0x8c, 0x6b, 0xef, 0xb0, 0x49, 0x6a, - 0x1a, 0xfc, 0x2e, 0x5a, 0x41, 0x19, 0x72, 0xc1, 0xb5, 0xa3, 0x97, 0xfc, - 0x3a, 0xdb, 0x9d, 0x7d, 0x1b, 0x17, 0xe8, 0x47, 0x68, 0x1f, 0xd3, 0xe2, - 0x13, 0x68, 0x56, 0x53, 0x1a, 0xbd, 0xa1, 0x3c, 0x0c, 0x39, 0x9b, 0x33, - 0xb4, 0xf4, 0x35, 0x17, 0x4c, 0x68, 0x52, 0x53, 0x03, 0x11, 0x74, 0x0e, - 0xca, 0xf5, 0x17, 0x84, 0xf7, 0x0d, 0x00, 0xf6, 0x2c, 0xc0, 0xc8, 0x38, - 0xd8, 0x58, 0x4a, 0xa8, 0xe0, 0x95, 0x8f, 0x3f, 0xaa, 0xc9, 0xbb, 0x21, - 0x8e, 0x9c, 0x13, 0xee, 0x94, 0x03, 0x2a, 0x58, 0x52, 0x8c, 0xd3, 0x90, - 0x06, 0x49, 0x29, 0xbf, 0x5d, 0x0e, 0x46, 0x2a, 0x15, 0x44, 0x86, 0xb0, - 0xe6, 0xb2, 0x02, 0x84, 0xbe, 0xaa, 0x15, 0x3d, 0xa3, 0x27, 0x8d, 0x55, - 0xa2, 0xcc, 0xc7, 0xb9, 0x4c, 0x02, 0x24, 0x66, 0x58, 0x42, 0x73, 0xee, - 0xad, 0xd8, 0xdb, 0x57, 0x4e, 0xdb, 0xd0, 0x74, 0x15, 0x4f, 0xa5, 0x85, - 0xe4, 0x00, 0x00, 0x00, 0x06, 0x42, 0x2a, 0xff, 0xd1, 0x92, 0xb0, 0x16, - 0xc5, 0x7a, 0x52, 0x1a, 0x5e, 0xff, 0x0f, 0x8b, 0x3a, 0xb3, 0x81, 0x63, - 0x36, 0x13, 0x52, 0xf3, 0x3a, 0x59, 0xf4, 0x5d, 0x96, 0x31, 0xf5, 0x19, - 0xa3, 0x7d, 0x19, 0xd1, 0x8b, 0x3a, 0x7f, 0xe5, 0xe3, 0xd5, 0x63, 0x4a, - 0x9a, 0xd3, 0x46, 0x0c, 0x9f, 0xab, 0xe7, 0x9b, 0xfe, 0xf2, 0x13, 0x40, - 0xee, 0x9f, 0x94, 0x3a, 0x40, 0xc0, 0xdb, 0xac, 0x66, 0x6c, 0xba, 0x3f, - 0x4f, 0xff, 0xf8, 0x73, 0xd4, 0x94, 0x5e, 0xd7, 0x88, 0xd7, 0xf1, 0xef, - 0xbb, 0x95, 0x19, 0xa4, 0x98, 0x2f, 0xfe, 0xbe, 0xec, 0xc1, 0x50, 0x5d, - 0xce, 0x72, 0x6a, 0x36, 0x12, 0x28, 0x0d, 0x70, 0xb9, 0x78, 0x1d, 0x19, - 0xfd, 0x27, 0xb7, 0x74, 0x4b, 0x65, 0x12, 0x39, 0xbc, 0xca, 0x83, 0x25, - 0xdc, 0x37, 0x52, 0x7f, 0xae, 0xb7, 0x5e, 0xff, 0xf1, 0x50, 0x80, 0x2e, - 0xff, 0xfc, 0x21, 0x1a, 0xcf, 0x3b, 0x9e, 0xef, 0xa0, 0x00, 0x9e, 0xb5, - 0x40, 0xd8, 0xb6, 0x17, 0x41, 0x8d, 0x0c, 0xc3, 0x82, 0x0c, 0x59, 0x96, - 0x62, 0xe5, 0x32, 0xcd, 0x6c, 0x38, 0xdf, 0xd6, 0xdb, 0xbf, 0x37, 0xcd, - 0xfb, 0x48, 0x18, 0xdf, 0xb9, 0x70, 0x85, 0xe2, 0xdb, 0xac, 0x9e, 0xc2, - 0xee, 0x9b, 0xde, 0x2a, 0xb7, 0xc4, 0x21, 0xe5, 0x63, 0x5c, 0x90, 0x40, - 0x1d, 0x9e, 0x51, 0xfb, 0x96, 0xc9, 0x80, 0x71, 0x16, 0xb8, 0x48, 0x71, - 0xfc, 0x82, 0x90, 0x17, 0x87, 0x9b, 0xe8, 0x84, 0x51, 0x10, 0x5e, 0xf9, - 0x00, 0xd8, 0xa0, 0x3f, 0x39, 0x34, 0x12, 0x0a, 0xe0, 0x34, 0xaa, 0xce, - 0xd4, 0xaa, 0x27, 0x97, 0xa9, 0xef, 0xd8, 0x88, 0xc7, 0x2c, 0x2e, 0x15, - 0x95, 0xff, 0x5f, 0xe7, 0x69, 0x40, 0x11, 0x00, 0xad, 0xd9, 0xd6, 0x40, - 0x00, 0xb9, 0xeb, 0x32, 0x48, 0x0d, 0xc7, 0xa9, 0x7d, 0x7e, 0xee, 0xd3, - 0xe1, 0xb5, 0xa5, 0xd1, 0x76, 0x89, 0x97, 0x35, 0xbf, 0x46, 0x9f, 0xb0, - 0x15, 0x55, 0x6f, 0x8f, 0x10, 0xde, 0x51, 0x3e, 0x6f, 0x32, 0x9a, 0x7d, - 0xeb, 0x0c, 0xae, 0x04, 0x3c, 0x39, 0xd9, 0xd2, 0xdf, 0xab, 0x0d, 0x8e, - 0xd3, 0xf7, 0x81, 0xa6, 0xbf, 0x2c, 0x0b, 0x01, 0x23, 0x5c, 0x87, 0xd7, - 0xfe, 0xf0, 0x91, 0x83, 0x40, 0xaa, 0x33, 0x3b, 0xb5, 0xcb, 0xe4, 0xa7, - 0x16, 0x37, 0x62, 0x40, 0x41, 0x44, 0x17, 0x73, 0x97, 0xb4, 0x4a, 0x11, - 0x44, 0x98, 0x41, 0x51, 0x0e, 0x19, 0xe9, 0xf5, 0x93, 0x48, 0x0a, 0xac, - 0x68, 0x7c, 0x7c, 0x70, 0x97, 0x61, 0x32, 0xfd, 0xc2, 0xa6, 0xe8, 0xc3, - 0x5b, 0x03, 0x30, 0xcd, 0x52, 0x52, 0xc2, 0xe4, 0xb2, 0x71, 0x2a, 0xf4, - 0x03, 0x5e, 0x08, 0x81, 0xda, 0xd0, 0xe1, 0xef, 0x5c, 0xf6, 0x38, 0x84, - 0xd3, 0xbe, 0x84, 0x43, 0x18, 0x9b, 0x4d, 0x30, 0x8f, 0x2a, 0x7a, 0x94, - 0x82, 0xb1, 0xbb, 0x7c, 0xcc, 0x1f, 0x40, 0x00, 0x09, 0x40, 0x01, 0xcf, - 0x53, 0x7b, 0xff, 0x48, 0xa5, 0x90, 0x03, 0x01, 0x96, 0x26, 0xa9, 0x62, - 0x82, 0xd0, 0xeb, 0x15, 0xb2, 0x0e, 0x70, 0x0b, 0x4f, 0x49, 0x68, 0x07, - 0x8e, 0x87, 0x79, 0x41, 0x44, 0x05, 0x71, 0x12, 0x08, 0xaf, 0x06, 0x46, - 0x1a, 0xa7, 0xd6, 0x46, 0x14, 0x31, 0x95, 0xe3, 0x51, 0x78, 0xa2, 0x6b, - 0xd2, 0x77, 0xbf, 0xb3, 0xb2, 0xf1, 0xc6, 0xb4, 0x45, 0x48, 0x71, 0xfe, - 0x1f, 0xbe, 0xd2, 0x98, 0x50, 0xc5, 0x52, 0x3b, 0x3c, 0xeb, 0x48, 0x80, - 0x2c, 0x5e, 0x69, 0x90, 0x00, 0x2b, 0x9a, 0x00, 0x00, 0x5e, 0x09, 0x54, - 0x4d, 0xd4, 0x56, 0x7b, 0xe5, 0xe9, 0x5e, 0x75, 0x51, 0xc0, 0xff, 0xf1, - 0x50, 0x80, 0x2b, 0x9f, 0xfc, 0x21, 0x2a, 0xce, 0xff, 0xff, 0x34, 0xa4, - 0x00, 0x9e, 0xb5, 0x4c, 0x55, 0x0c, 0x68, 0x11, 0x50, 0x04, 0xa0, 0x15, - 0x2f, 0x72, 0x9c, 0x75, 0xbc, 0xf3, 0x59, 0xad, 0x4f, 0x8f, 0x52, 0x71, - 0x07, 0x89, 0xe5, 0x70, 0x4a, 0x82, 0xd8, 0x1a, 0xd1, 0x33, 0xd7, 0x18, - 0xb1, 0xe5, 0xd5, 0xb1, 0x85, 0x37, 0xc0, 0x88, 0x88, 0x66, 0xca, 0x40, - 0xde, 0x8a, 0xf2, 0xa8, 0xc9, 0x7b, 0x63, 0x53, 0xf9, 0xde, 0xe3, 0x73, - 0x97, 0x3f, 0xf2, 0xec, 0x2a, 0xb2, 0x46, 0x11, 0xd0, 0x8a, 0x23, 0xb6, - 0xf8, 0x4d, 0xd5, 0x15, 0x5b, 0xa5, 0x16, 0x0b, 0xf7, 0x5c, 0xba, 0x0c, - 0x18, 0x09, 0x5d, 0xab, 0x08, 0x8f, 0xb6, 0xfa, 0x9e, 0xbd, 0xe7, 0x99, - 0x20, 0x0c, 0xb4, 0x30, 0x90, 0x00, 0xce, 0xfc, 0xff, 0xad, 0x80, 0x00, - 0x1d, 0x8a, 0xea, 0xee, 0xca, 0xf2, 0xc6, 0x30, 0x0e, 0x84, 0x2e, 0x57, - 0x60, 0x13, 0x8e, 0x00, 0x03, 0x4d, 0x2b, 0xe8, 0xe5, 0xb8, 0xb2, 0xc4, - 0x24, 0x3e, 0xc8, 0x02, 0x2e, 0xa4, 0x15, 0xdf, 0x77, 0x45, 0xd2, 0xea, - 0xe6, 0x48, 0x7d, 0x16, 0x13, 0x04, 0x62, 0x60, 0x47, 0x7c, 0xd0, 0x2a, - 0xbb, 0x39, 0x55, 0xdc, 0x5c, 0xe6, 0x62, 0x43, 0x00, 0xbf, 0xd5, 0xaf, - 0xd7, 0x66, 0xae, 0x35, 0xd3, 0x7f, 0x76, 0xec, 0xff, 0x9d, 0x1f, 0x5d, - 0x97, 0xed, 0xdc, 0x0d, 0x4a, 0x5a, 0x72, 0x64, 0x90, 0xc5, 0x74, 0x42, - 0x7a, 0x2c, 0x19, 0x2c, 0x97, 0x3a, 0x9e, 0xf5, 0xb4, 0x12, 0xf9, 0x5b, - 0xa5, 0xff, 0xe8, 0x48, 0x48, 0x66, 0x6b, 0x7a, 0x80, 0x0e, 0xd9, 0x4b, - 0x25, 0xa2, 0x0c, 0x7b, 0x50, 0xac, 0xfc, 0xaa, 0x23, 0x25, 0xab, 0x1f, - 0x1b, 0x5a, 0x83, 0x31, 0x14, 0x5c, 0x71, 0x0e, 0x79, 0x13, 0xd6, 0xb9, - 0x8f, 0xbc, 0x00, 0x4a, 0x02, 0x52, 0xf0, 0x6b, 0x0e, 0xb7, 0xbb, 0xca, - 0x84, 0xa8, 0x06, 0x9e, 0xea, 0xd2, 0xbb, 0x92, 0x46, 0x54, 0x68, 0x05, - 0x94, 0x6a, 0x49, 0x12, 0x5c, 0x15, 0x54, 0xf1, 0x5a, 0x0f, 0xf0, 0xf3, - 0xdb, 0x5a, 0x33, 0xe3, 0xdd, 0xe5, 0x19, 0xe5, 0xff, 0x95, 0x55, 0x18, - 0x78, 0x4f, 0xa9, 0xc5, 0xdc, 0xad, 0x7a, 0xfa, 0x0b, 0x63, 0x9e, 0x7e, - 0x4b, 0x43, 0x8d, 0x43, 0x1a, 0x12, 0xcc, 0xcf, 0x8f, 0xcb, 0xe0, 0x4b, - 0x68, 0x2d, 0x8a, 0x22, 0x3c, 0xcf, 0xc4, 0xe7, 0x88, 0x00, 0x2f, 0x9f, - 0x0d, 0xa0, 0x00, 0x72, 0x66, 0x40, 0x00, 0xc3, 0x1c, 0xdc, 0xff, 0xf1, - 0x50, 0x80, 0x33, 0x5f, 0xfc, 0x21, 0x4c, 0xd8, 0xff, 0x1f, 0xf0, 0x57, - 0x2d, 0xa0, 0x4e, 0x44, 0x38, 0x12, 0x22, 0xc2, 0x2c, 0xeb, 0xdb, 0x31, - 0xcd, 0xb0, 0x15, 0x06, 0xb7, 0x35, 0xc7, 0xd4, 0xe3, 0xfe, 0xd5, 0x2a, - 0x4e, 0xbc, 0x7f, 0x31, 0xd7, 0xfc, 0x54, 0x5d, 0x67, 0xe3, 0xbf, 0x6d, - 0x58, 0xe4, 0x79, 0x5f, 0xb7, 0x3a, 0xa9, 0xbc, 0x73, 0x7a, 0x4e, 0xc4, - 0x58, 0x89, 0x71, 0x92, 0x01, 0x6f, 0x49, 0x70, 0x1f, 0xf6, 0xd6, 0x7d, - 0x3a, 0x7e, 0x79, 0x2f, 0x39, 0xcf, 0x64, 0x97, 0x04, 0x5f, 0x99, 0xef, - 0xb8, 0x46, 0xd6, 0x3f, 0xa2, 0x85, 0xb8, 0xab, 0x8e, 0x30, 0xd8, 0x05, - 0x37, 0x60, 0x51, 0x66, 0xc2, 0x73, 0x55, 0x6a, 0xf4, 0x37, 0x7d, 0x8d, - 0x5f, 0x4a, 0x56, 0x28, 0x52, 0xe0, 0x74, 0x25, 0x0d, 0x21, 0x52, 0x0c, - 0x84, 0x42, 0xe5, 0x42, 0x01, 0x01, 0x13, 0x65, 0x44, 0x87, 0x80, 0x99, - 0x6b, 0x90, 0x5e, 0x93, 0xb2, 0xba, 0x14, 0x4e, 0x31, 0x44, 0xc5, 0x8b, - 0xea, 0xfa, 0xfa, 0x7a, 0x11, 0x11, 0x8b, 0x75, 0x16, 0xb9, 0x71, 0x04, - 0xf1, 0xd0, 0x69, 0x6d, 0x84, 0x14, 0x4e, 0x2a, 0x80, 0x4e, 0x18, 0x78, - 0x28, 0x80, 0x04, 0x58, 0x60, 0x46, 0x8d, 0x23, 0x14, 0x18, 0x00, 0x00, - 0x04, 0x05, 0x02, 0x13, 0xb6, 0xf4, 0x4f, 0x80, 0xf8, 0x0e, 0x81, 0xd6, - 0x3a, 0xc6, 0xd1, 0x94, 0x34, 0x68, 0x3b, 0x3e, 0x39, 0xb4, 0x70, 0x1d, - 0x53, 0x8c, 0x58, 0x2c, 0x18, 0x8a, 0xcc, 0x9e, 0xf0, 0x75, 0x31, 0xe5, - 0xed, 0x15, 0x56, 0x41, 0x51, 0x87, 0x35, 0xc9, 0x92, 0xec, 0x5d, 0xae, - 0xe7, 0x26, 0xf0, 0x56, 0x89, 0xe5, 0x75, 0xdf, 0x3c, 0x39, 0xe1, 0x2e, - 0x1c, 0x26, 0x39, 0xdf, 0xa9, 0xa3, 0xf8, 0x8e, 0xa5, 0xf0, 0x5c, 0x2f, - 0xbe, 0x33, 0x7e, 0xb1, 0xd2, 0x38, 0xbc, 0x69, 0x38, 0xc6, 0x8b, 0x21, - 0x9f, 0x21, 0x33, 0x57, 0xf1, 0xb8, 0xf3, 0x0e, 0xcc, 0x26, 0xbf, 0x40, - 0x2a, 0xa5, 0x06, 0xb7, 0x3f, 0x59, 0x32, 0x7e, 0xc4, 0xa9, 0xd5, 0x57, - 0xdf, 0xdf, 0xf7, 0xff, 0x80, 0x9c, 0x71, 0x8b, 0xc0, 0xec, 0x17, 0x44, - 0x9a, 0x51, 0x72, 0x03, 0xea, 0x2e, 0xc6, 0x87, 0x0b, 0x92, 0xb4, 0x54, - 0xe6, 0x35, 0xaa, 0xb4, 0xc9, 0x2b, 0x52, 0x4a, 0x28, 0x00, 0x66, 0x26, - 0xc4, 0x8f, 0x1d, 0x37, 0x7a, 0x47, 0xa0, 0xa1, 0x69, 0xcc, 0x42, 0xe9, - 0x37, 0x22, 0xd0, 0xc3, 0x79, 0x2a, 0x21, 0x62, 0x92, 0x2d, 0x4b, 0x14, - 0x75, 0xd6, 0x6f, 0x05, 0xf8, 0xb1, 0x99, 0xd7, 0x58, 0x20, 0x6a, 0xbc, - 0x10, 0x01, 0x6c, 0xcf, 0x6c, 0xb0, 0x85, 0x0a, 0x86, 0x18, 0xa1, 0xbb, - 0xdd, 0xed, 0xf6, 0xf3, 0xdf, 0x98, 0xc3, 0x14, 0x2a, 0x2a, 0x3f, 0x27, - 0xfa, 0xff, 0xe3, 0xfe, 0x7f, 0xef, 0xfc, 0xdf, 0x17, 0x5b, 0xb7, 0xb0, - 0xf4, 0x11, 0x90, 0xb9, 0x87, 0x29, 0x31, 0xc6, 0x60, 0x1c, 0x0a, 0xbe, - 0xff, 0xf1, 0x50, 0x80, 0x3d, 0x9f, 0xfc, 0x21, 0x4c, 0xda, 0xda, 0x04, - 0xf0, 0x24, 0x04, 0xf5, 0x96, 0x83, 0x80, 0xd9, 0x88, 0x8e, 0x0d, 0xbb, - 0x89, 0x49, 0x80, 0x00, 0xe3, 0x9d, 0x5f, 0x5f, 0x0e, 0xbf, 0xf0, 0x00, - 0x10, 0x39, 0xeb, 0xfb, 0x82, 0x4b, 0xb0, 0xd7, 0x8f, 0x95, 0x97, 0x53, - 0x2d, 0x5b, 0xcc, 0xa2, 0x2f, 0x42, 0xbf, 0x81, 0x6a, 0x26, 0xe1, 0xe8, - 0x1f, 0x3d, 0x01, 0x75, 0xc1, 0x6c, 0xf9, 0x31, 0x70, 0x9e, 0x17, 0x48, - 0xa2, 0xa4, 0x21, 0xa9, 0x48, 0x16, 0x99, 0x5d, 0xff, 0x98, 0x04, 0x80, - 0x8a, 0xb9, 0x91, 0x52, 0x85, 0x81, 0x20, 0x64, 0x10, 0xa8, 0x48, 0x3c, - 0x44, 0x5b, 0x82, 0x73, 0x75, 0x21, 0xa4, 0x04, 0x23, 0xd8, 0x19, 0xa3, - 0x43, 0x14, 0x23, 0x87, 0x41, 0x50, 0x5f, 0x1e, 0xb3, 0x3e, 0x21, 0x9a, - 0x49, 0xed, 0x48, 0x18, 0xd8, 0x49, 0xc0, 0x3e, 0x93, 0xa1, 0xdd, 0xe2, - 0xea, 0x6c, 0xc8, 0xb8, 0x18, 0x66, 0x23, 0xf5, 0xe3, 0x83, 0xc3, 0xaa, - 0xa5, 0x53, 0x8d, 0xa9, 0x73, 0x47, 0x40, 0xdf, 0x2f, 0xa6, 0xaf, 0xa5, - 0xdd, 0xe1, 0x7d, 0x49, 0x2d, 0xda, 0x0c, 0x34, 0x76, 0x72, 0xc5, 0xea, - 0xc3, 0xdd, 0x67, 0xd2, 0xf8, 0x6e, 0x1b, 0x48, 0xa9, 0xc6, 0x84, 0xc6, - 0x4f, 0x8b, 0x98, 0x98, 0x23, 0x08, 0x87, 0x6a, 0xc6, 0x2a, 0xf1, 0xa5, - 0x21, 0xce, 0x9c, 0xa5, 0x73, 0x4f, 0xa0, 0xe2, 0x2d, 0x34, 0xcc, 0x6b, - 0x64, 0x9e, 0x8e, 0xb3, 0x36, 0x66, 0xf3, 0x3c, 0xee, 0x15, 0x05, 0x4d, - 0x40, 0xeb, 0x87, 0x4f, 0x56, 0x90, 0x21, 0x14, 0xca, 0xa5, 0xaf, 0x0d, - 0xff, 0x45, 0x29, 0x4c, 0xe3, 0x15, 0xf8, 0xe3, 0x94, 0x1b, 0x81, 0x1d, - 0xaa, 0xd1, 0xa3, 0xf9, 0x7d, 0xaf, 0x26, 0xd2, 0x29, 0xf5, 0xa7, 0xe3, - 0x91, 0xad, 0x81, 0xdf, 0x11, 0x49, 0xcc, 0xb4, 0x67, 0xc4, 0x7e, 0xb9, - 0x0f, 0x51, 0x96, 0xdf, 0x9f, 0xcf, 0xe0, 0xd3, 0xf4, 0xed, 0x9b, 0xdd, - 0x45, 0xf3, 0xf0, 0x90, 0x09, 0x9b, 0x13, 0xd4, 0x59, 0x8b, 0xfb, 0x45, - 0xb5, 0x9f, 0x36, 0xed, 0x25, 0xfa, 0xde, 0x9b, 0xd6, 0xcf, 0x8e, 0x6b, - 0x5b, 0x6b, 0x8e, 0x75, 0xf8, 0x65, 0x7b, 0x7f, 0xd3, 0x5b, 0x6b, 0x73, - 0xbd, 0x70, 0x20, 0x73, 0xf6, 0x38, 0x1c, 0x10, 0x80, 0x55, 0x46, 0xee, - 0xe3, 0x39, 0xec, 0x3a, 0x63, 0x5d, 0x70, 0x0b, 0x79, 0x0f, 0x22, 0x91, - 0x3b, 0xbf, 0x3c, 0xb4, 0xbd, 0x7e, 0x42, 0x4c, 0x5c, 0xf4, 0xc1, 0xeb, - 0x26, 0x5f, 0x41, 0xdb, 0x01, 0x27, 0xad, 0xfb, 0x10, 0x80, 0x13, 0x34, - 0xd7, 0x51, 0xe9, 0x01, 0x2e, 0xc5, 0x82, 0x88, 0xa8, 0x50, 0xcc, 0x1c, - 0xb2, 0x64, 0x08, 0x86, 0x00, 0xd5, 0x0b, 0x76, 0x9b, 0xb6, 0xb5, 0xb5, - 0x19, 0xb3, 0x97, 0x3e, 0xe1, 0x8d, 0x9b, 0x7c, 0x47, 0xf1, 0x66, 0x28, - 0x1d, 0x1e, 0x8a, 0x4a, 0xe4, 0xbe, 0x56, 0xd2, 0x4f, 0xb8, 0xc3, 0x66, - 0x50, 0x00, 0xf4, 0x59, 0xe6, 0x91, 0xca, 0x38, 0x4e, 0xc8, 0xcc, 0x53, - 0x04, 0x60, 0xf5, 0x0f, 0x2f, 0xf5, 0x9a, 0xa2, 0x9d, 0xc5, 0xd5, 0x8a, - 0xcb, 0xcc, 0x11, 0x92, 0xbb, 0x65, 0xd7, 0x6c, 0xce, 0x44, 0x89, 0x6c, - 0x56, 0xd9, 0xa6, 0xdc, 0xbc, 0xc7, 0x88, 0x50, 0x00, 0xe4, 0x9a, 0x73, - 0x47, 0x9d, 0xca, 0x5a, 0x33, 0xf4, 0xb9, 0x86, 0x6f, 0xc9, 0x71, 0xe8, - 0xb2, 0xb0, 0xde, 0x7a, 0x0c, 0x65, 0x71, 0xc6, 0x66, 0x17, 0xe7, 0xd1, - 0xad, 0x70, 0x13, 0x4e, 0x00, 0x5b, 0xfb, 0xd0, 0x77, 0x87, 0xf8, 0xfc, - 0xff, 0xf1, 0x50, 0x80, 0x28, 0x5f, 0xfc, 0x21, 0x7a, 0xc8, 0xb7, 0xf1, - 0x6f, 0x00, 0x00, 0xa5, 0xb1, 0x42, 0x19, 0x2a, 0xc2, 0x10, 0x8d, 0x0c, - 0xc1, 0x00, 0x00, 0xf6, 0xf4, 0x94, 0x05, 0xe0, 0x4a, 0xd7, 0x9e, 0xf8, - 0xeb, 0x7e, 0x3f, 0xd8, 0x7b, 0xfb, 0x46, 0x76, 0xb5, 0xac, 0x83, 0x83, - 0x7e, 0xfd, 0xe6, 0xf6, 0x09, 0x4d, 0x3c, 0x02, 0x24, 0xfc, 0xae, 0xb9, - 0x24, 0xfb, 0x60, 0x19, 0xad, 0x13, 0x9e, 0x54, 0x12, 0xbb, 0x5a, 0xab, - 0x0b, 0xb0, 0x1d, 0x76, 0x90, 0x3a, 0x13, 0xc9, 0x67, 0x5b, 0x41, 0xcf, - 0x50, 0x9c, 0x5b, 0x36, 0x96, 0xfb, 0xb0, 0xaf, 0x81, 0xf3, 0xf6, 0x52, - 0xd0, 0xcc, 0xc7, 0xa7, 0xe0, 0x8a, 0xcc, 0x55, 0x2a, 0x33, 0x19, 0x2f, - 0xaf, 0xd7, 0x13, 0x9c, 0xb4, 0xb5, 0xab, 0xb2, 0x65, 0x28, 0x08, 0xb5, - 0x42, 0x16, 0x63, 0x70, 0xb4, 0xd4, 0x41, 0x54, 0xb6, 0x62, 0xea, 0x2e, - 0x69, 0x9a, 0xd6, 0x02, 0xf6, 0xa3, 0x02, 0xa1, 0x91, 0x66, 0xc2, 0xde, - 0x43, 0xef, 0xdb, 0x90, 0xf9, 0xcb, 0x20, 0x0a, 0x5a, 0x00, 0x78, 0xa9, - 0x87, 0x26, 0x04, 0x72, 0x42, 0xe5, 0xf9, 0x63, 0x95, 0x8d, 0x71, 0x5c, - 0xf6, 0x39, 0xe1, 0x24, 0x14, 0xf0, 0xa9, 0xdd, 0x0d, 0xf8, 0xad, 0x65, - 0x90, 0x5c, 0x4a, 0x82, 0xea, 0x3f, 0x92, 0x43, 0xb5, 0xdf, 0x05, 0x36, - 0x0b, 0x77, 0x62, 0x37, 0x5b, 0x00, 0x93, 0x2f, 0x0d, 0x63, 0xa3, 0x71, - 0x7c, 0xfc, 0xd2, 0xd2, 0x7d, 0xe0, 0x71, 0x34, 0x59, 0x50, 0xd1, 0xd7, - 0x4d, 0x63, 0x65, 0xc1, 0x18, 0xa8, 0x4f, 0x80, 0x95, 0x15, 0x00, 0xa2, - 0x54, 0x54, 0x1e, 0x77, 0x5a, 0xdc, 0xd6, 0x69, 0x52, 0xaa, 0xa8, 0x42, - 0xe4, 0xd8, 0x1d, 0x93, 0xff, 0x93, 0x82, 0x12, 0x65, 0xbf, 0xe4, 0x84, - 0x01, 0xd7, 0xb9, 0xae, 0xd5, 0x0a, 0x96, 0x99, 0x05, 0xa2, 0x63, 0x9a, - 0x29, 0xaa, 0x69, 0xd1, 0x4c, 0xd1, 0x93, 0x48, 0xa7, 0xc2, 0x30, 0x0b, - 0xd4, 0x2d, 0x08, 0xd5, 0x79, 0x68, 0x9a, 0x92, 0x31, 0x72, 0x06, 0x42, - 0x3c, 0x45, 0x2d, 0x16, 0x85, 0x32, 0x02, 0xae, 0x64, 0x22, 0x1f, 0x88, - 0x32, 0x6f, 0xaf, 0x07, 0xe8, 0x3e, 0xba, 0x64, 0x7d, 0x55, 0xfe, 0x74, - 0x5d, 0x2b, 0x35, 0x72, 0x97, 0x85, 0x5e, 0x3c, 0x96, 0xde, 0xff, 0xf1, - 0x50, 0x80, 0x34, 0xbf, 0xfc, 0x21, 0x1a, 0xcd, 0x3c, 0x24, 0x40, 0x00, - 0x00, 0x9d, 0xbd, 0x38, 0x55, 0xb0, 0x30, 0x00, 0x00, 0x00, 0x04, 0xd7, - 0x3d, 0x27, 0x57, 0xbf, 0xdc, 0x73, 0xe9, 0x18, 0xd7, 0x09, 0x51, 0x6d, - 0xd7, 0x1b, 0xda, 0x7e, 0xc1, 0x16, 0xde, 0x53, 0x4d, 0x2a, 0x3f, 0x15, - 0x08, 0xe2, 0x1b, 0xde, 0xa3, 0x76, 0xfc, 0x92, 0xc0, 0xa2, 0x37, 0x80, - 0x02, 0x23, 0xca, 0x4e, 0xb7, 0x04, 0x05, 0x23, 0x13, 0x1a, 0x2f, 0x0b, - 0xf4, 0x33, 0x1a, 0x67, 0xa3, 0xec, 0x22, 0x1f, 0x00, 0x57, 0x30, 0xa0, - 0x3c, 0x29, 0x85, 0xba, 0xfb, 0x90, 0xea, 0x53, 0x53, 0x32, 0x48, 0x07, - 0x80, 0x50, 0x01, 0x17, 0x8e, 0xdd, 0x5f, 0xe8, 0xf2, 0x28, 0xaa, 0x8d, - 0xd3, 0xc3, 0x49, 0x0f, 0xe5, 0xe6, 0xaf, 0x64, 0xda, 0x1d, 0x08, 0x82, - 0xe5, 0x0b, 0x6c, 0x01, 0x90, 0xe2, 0x2a, 0x0e, 0xd4, 0x58, 0x0c, 0xae, - 0x59, 0x46, 0xa3, 0xaf, 0xfe, 0xcc, 0x98, 0xb0, 0x44, 0x50, 0x86, 0x18, - 0x3c, 0xc6, 0x86, 0x12, 0x61, 0xf0, 0x38, 0x46, 0x26, 0x81, 0x77, 0x85, - 0x5d, 0xf0, 0xb9, 0x24, 0x33, 0xdd, 0xd2, 0x17, 0x32, 0x55, 0x4a, 0x96, - 0xdc, 0x46, 0xa5, 0x9c, 0xf6, 0x41, 0x70, 0x2a, 0x6d, 0x9a, 0x8a, 0x88, - 0x07, 0x1a, 0x0c, 0xfe, 0xa9, 0x6b, 0x32, 0xcd, 0x0e, 0x3d, 0x11, 0x9a, - 0x85, 0x16, 0x2a, 0xb3, 0x24, 0xf5, 0xc3, 0xb0, 0xbd, 0xc8, 0x4d, 0x06, - 0x6b, 0xa2, 0xf1, 0x2b, 0xe6, 0x67, 0xb9, 0x78, 0xed, 0x6b, 0xb1, 0x53, - 0x17, 0x5c, 0x3b, 0x2a, 0x33, 0xdd, 0x43, 0x8f, 0xca, 0xa2, 0xfb, 0x51, - 0x56, 0x2e, 0x6f, 0xda, 0x5c, 0x77, 0xe3, 0xbe, 0xdf, 0x7f, 0xc2, 0x36, - 0x2d, 0x06, 0x94, 0x96, 0xfc, 0x47, 0x1c, 0xa7, 0x1d, 0x70, 0x92, 0xb8, - 0x06, 0xb6, 0x94, 0x61, 0x84, 0x6e, 0x33, 0xa2, 0xbe, 0x75, 0x6c, 0x72, - 0x70, 0x3d, 0x7f, 0xa0, 0xe7, 0xb1, 0xf0, 0x6a, 0xdd, 0x71, 0x87, 0x7b, - 0x55, 0x3b, 0x62, 0xa8, 0xc1, 0x58, 0x9e, 0xf7, 0x3c, 0x72, 0xe0, 0x72, - 0x38, 0xe5, 0xc7, 0x27, 0x1c, 0x81, 0xe6, 0x2b, 0x9e, 0x32, 0xd8, 0xcd, - 0x57, 0x21, 0x2f, 0x54, 0xfe, 0x9a, 0x75, 0x8f, 0x76, 0xde, 0x99, 0xb4, - 0x8e, 0xba, 0x0a, 0x11, 0x23, 0x34, 0x44, 0x6f, 0x5f, 0xba, 0x0c, 0xb3, - 0x55, 0x07, 0x07, 0x45, 0x37, 0xa7, 0x14, 0x0d, 0x56, 0xf9, 0xc3, 0x85, - 0x6e, 0xbc, 0x9d, 0xc2, 0x05, 0x3d, 0x68, 0x3b, 0xa4, 0x03, 0xa2, 0x58, - 0x08, 0x18, 0x27, 0xfc, 0x30, 0xba, 0xbe, 0x61, 0xa8, 0xba, 0xbe, 0x34, - 0x22, 0xa5, 0x1a, 0x63, 0x96, 0xb0, 0x92, 0x71, 0x3a, 0xde, 0x9c, 0x22, - 0x7b, 0x56, 0xb7, 0x00, 0xe2, 0x4e, 0x0b, 0x1e, 0xd8, 0xa1, 0x5e, 0x78, - 0x8d, 0xd5, 0x69, 0x98, 0xb6, 0x23, 0x93, 0xca, 0x25, 0x0a, 0x68, 0xe1, - 0x1f, 0x78, 0x00, 0x78, 0xe2, 0x4f, 0x2a, 0xb7, 0x96, 0x79, 0x01, 0x8e, - 0x71, 0x00, 0x6a, 0x38, 0x0b, 0x52, 0x81, 0xc1, 0x75, 0x9b, 0x8f, 0xff, - 0xf1, 0x50, 0x80, 0x2f, 0x5f, 0xfc, 0x21, 0x1a, 0xcb, 0x38, 0xc4, 0x6f, - 0x00, 0x00, 0x9c, 0xb9, 0xb0, 0xa0, 0x32, 0x45, 0x39, 0xa5, 0x82, 0x00, - 0x00, 0x00, 0x00, 0x71, 0xc9, 0x7f, 0x19, 0x3f, 0x70, 0xb4, 0x40, 0xcc, - 0xa2, 0xaa, 0xe6, 0x4a, 0x99, 0x93, 0xf0, 0xb3, 0x67, 0xcf, 0xba, 0x3c, - 0xb0, 0x4e, 0x4d, 0x5e, 0xc3, 0x5f, 0xd3, 0x42, 0xe3, 0x9a, 0x04, 0x0e, - 0x13, 0x4e, 0x37, 0x9e, 0x00, 0xc9, 0xe3, 0x5e, 0x42, 0x4a, 0x56, 0x81, - 0x14, 0x5b, 0x82, 0x30, 0xce, 0x81, 0x6d, 0x1c, 0x12, 0x37, 0x3b, 0x1e, - 0x08, 0x5b, 0x90, 0xf2, 0x10, 0x66, 0x63, 0x23, 0x1b, 0xff, 0x53, 0x97, - 0x40, 0xaa, 0x98, 0x75, 0xb6, 0x87, 0xbc, 0x54, 0x00, 0x58, 0xf7, 0x52, - 0x7d, 0xb1, 0x23, 0xeb, 0x09, 0x5d, 0x53, 0x48, 0xc7, 0xb9, 0x6c, 0xcf, - 0x83, 0xbf, 0xf0, 0x5a, 0xfc, 0xc7, 0x2b, 0xb9, 0xf1, 0x7b, 0x2c, 0x34, - 0x21, 0x8e, 0xc8, 0x27, 0xfc, 0x9f, 0x61, 0x82, 0xb1, 0xe9, 0x1a, 0x97, - 0x5a, 0xdf, 0x57, 0x28, 0x3a, 0x24, 0xba, 0xd1, 0x50, 0xcd, 0xea, 0x62, - 0x7a, 0x78, 0x2a, 0x16, 0x0c, 0xf8, 0xa2, 0x57, 0xc8, 0x10, 0x30, 0xcc, - 0xe3, 0x01, 0x20, 0x0b, 0xbe, 0x0a, 0x54, 0x64, 0x1d, 0x52, 0x2b, 0xcf, - 0x9b, 0x73, 0xdf, 0x1d, 0x74, 0xa7, 0xe3, 0x89, 0x0d, 0x0a, 0xb3, 0xda, - 0x0a, 0x9a, 0x05, 0x3d, 0x55, 0x28, 0x92, 0x86, 0xf1, 0xa3, 0x24, 0xf1, - 0x19, 0xca, 0x40, 0xcb, 0xe3, 0x19, 0x3d, 0xef, 0x24, 0xab, 0xbf, 0xe2, - 0x45, 0xb4, 0xa7, 0x67, 0x49, 0xe2, 0x7c, 0x6a, 0xd7, 0x49, 0x56, 0x1c, - 0x8a, 0x57, 0xb5, 0xeb, 0xc2, 0x4a, 0xaa, 0x59, 0xeb, 0x9c, 0x23, 0xe0, - 0xb6, 0x2c, 0xc2, 0xd8, 0x5e, 0x16, 0xcb, 0x1f, 0x1b, 0x5f, 0x35, 0xc4, - 0x99, 0xb3, 0x35, 0xc8, 0x73, 0xf9, 0x0f, 0xf8, 0xea, 0xfc, 0xdb, 0x1d, - 0xd2, 0x1c, 0xf2, 0xa5, 0xe5, 0x8b, 0xd9, 0x80, 0x8c, 0x1f, 0xa1, 0x25, - 0x5e, 0x92, 0xa1, 0xcf, 0xa8, 0xad, 0x40, 0xaf, 0xc0, 0x60, 0xc7, 0xcd, - 0x14, 0x1b, 0xa5, 0x32, 0x76, 0xe5, 0x9b, 0x04, 0xfe, 0xa7, 0x92, 0xeb, - 0xe7, 0x25, 0x52, 0x94, 0x2c, 0x39, 0xa3, 0x21, 0x34, 0xa6, 0x73, 0xc4, - 0xdf, 0x3d, 0x13, 0x9e, 0x22, 0x22, 0x31, 0xa8, 0x30, 0x8a, 0x6a, 0x57, - 0xc9, 0x72, 0x94, 0xd2, 0x96, 0x79, 0x1d, 0x94, 0xf2, 0x25, 0x55, 0xb8, - 0xee, 0x09, 0x80, 0x1e, 0x4d, 0x53, 0xc8, 0xbf, 0x82, 0xdf, 0xbd, 0xdf, - 0x14, 0x9e, 0xf0, 0xae, 0xf7, 0xda, 0x33, 0x8e, 0xd2, 0x8e, 0xe4, 0x3b, - 0x16, 0x4e, 0xa3, 0x49, 0x16, 0x3b, 0x93, 0x2a, 0x16, 0x1b, 0xb0, 0x7c, - 0x2e, 0x51, 0x47, 0x73, 0xb8, 0xff, 0xf1, 0x50, 0x80, 0x3f, 0x7f, 0xfc, - 0x20, 0xa0, 0x1a, 0xd7, 0x37, 0x29, 0xa5, 0x06, 0xc3, 0x82, 0x01, 0xe7, - 0xc0, 0x00, 0xd7, 0x1e, 0x80, 0x8f, 0x6c, 0xef, 0xce, 0xb7, 0xf1, 0xe3, - 0x5f, 0x59, 0xcf, 0xc0, 0x60, 0xae, 0x15, 0xe2, 0xd6, 0x04, 0x4f, 0x87, - 0xbf, 0x6c, 0x87, 0x54, 0x6e, 0x92, 0xaf, 0x05, 0x21, 0x9d, 0x26, 0x29, - 0xea, 0x31, 0xc0, 0x0c, 0x38, 0x84, 0x69, 0x2a, 0x1a, 0x88, 0xe3, 0x41, - 0x85, 0x18, 0xbf, 0x22, 0x40, 0xe6, 0x87, 0x59, 0x9e, 0x11, 0xe0, 0x43, - 0x44, 0x5c, 0x00, 0x29, 0x40, 0x24, 0x42, 0xd4, 0x01, 0xd0, 0x42, 0xcc, - 0xc6, 0xe6, 0x38, 0x61, 0x78, 0x72, 0x35, 0x5b, 0x3c, 0x0e, 0x7e, 0x2a, - 0xfc, 0x3b, 0xbd, 0x7b, 0x2e, 0xf3, 0x8b, 0xe3, 0xfa, 0xbe, 0x0e, 0x12, - 0x46, 0x5c, 0x3e, 0xa4, 0xc3, 0x2f, 0xde, 0xee, 0xa8, 0xaa, 0x5c, 0x3d, - 0x3a, 0x84, 0x4a, 0xd7, 0xf9, 0xcf, 0xdc, 0x40, 0xbb, 0x5b, 0x9a, 0x48, - 0xeb, 0x77, 0xb2, 0xe9, 0x1f, 0x67, 0xc8, 0x25, 0xbb, 0xfb, 0xbf, 0x0e, - 0xf0, 0xb1, 0x2e, 0x8d, 0x2e, 0x6a, 0xb5, 0xba, 0xd1, 0xab, 0x28, 0xe8, - 0x4c, 0xea, 0x96, 0xca, 0x9d, 0xef, 0x27, 0x56, 0x73, 0xce, 0xbd, 0x8b, - 0x2b, 0x2a, 0x7e, 0x4c, 0xaa, 0x1b, 0x5e, 0x0c, 0xcf, 0x59, 0x88, 0xae, - 0x9b, 0x19, 0xae, 0xb2, 0x24, 0x30, 0x83, 0x02, 0x11, 0x43, 0x22, 0x5a, - 0xee, 0xd7, 0x0e, 0xea, 0xe8, 0xa0, 0x4c, 0x26, 0xb8, 0x5c, 0x3b, 0xbd, - 0x12, 0x0c, 0x57, 0x08, 0x82, 0x81, 0x01, 0x66, 0x13, 0x80, 0xe7, 0x23, - 0x63, 0x12, 0xb6, 0xfa, 0x27, 0x20, 0x27, 0x72, 0xd0, 0x0e, 0x14, 0x4c, - 0x03, 0x80, 0x01, 0x40, 0xc5, 0x04, 0x2f, 0x9b, 0x1e, 0xc2, 0x31, 0x37, - 0x0f, 0x09, 0x80, 0x72, 0xe3, 0x90, 0xe5, 0xd0, 0x15, 0x6b, 0x4d, 0x29, - 0x89, 0x03, 0x56, 0xc0, 0x98, 0x40, 0x79, 0xf0, 0x00, 0x00, 0x00, 0x5e, - 0x6b, 0x8a, 0xfd, 0xf5, 0xf6, 0xfc, 0x09, 0x82, 0xa0, 0x03, 0xba, 0x9c, - 0xb9, 0xfe, 0x97, 0x48, 0xd3, 0x13, 0xd4, 0x25, 0x42, 0x43, 0x3a, 0xba, - 0x73, 0xc5, 0x96, 0x07, 0x2d, 0x75, 0x1b, 0x9b, 0x35, 0xe5, 0x3e, 0x9c, - 0x87, 0xa0, 0xc4, 0x9c, 0x8a, 0xc5, 0x46, 0x41, 0xbb, 0xa9, 0xbc, 0x1b, - 0x80, 0x17, 0x5c, 0xae, 0x1b, 0x72, 0x12, 0xa1, 0x0c, 0x24, 0xd9, 0x34, - 0xbc, 0xd5, 0x7d, 0x74, 0x6a, 0x72, 0xd0, 0x2f, 0x93, 0x89, 0x8b, 0x40, - 0xa8, 0x1d, 0x6a, 0x6b, 0x9f, 0xdf, 0x1a, 0x23, 0xa9, 0xca, 0xd7, 0x16, - 0xcb, 0x93, 0xc8, 0x32, 0x6a, 0xf6, 0xbe, 0x33, 0x09, 0x0a, 0xb7, 0x59, - 0xa0, 0xf9, 0xd6, 0xb6, 0x86, 0xb7, 0x37, 0x8b, 0xa1, 0x09, 0xc0, 0xb4, - 0x24, 0x99, 0x22, 0xea, 0xf1, 0xee, 0x63, 0x1c, 0x39, 0x2e, 0xf5, 0x7e, - 0x98, 0xdf, 0x5d, 0x3a, 0x63, 0x94, 0x56, 0xbb, 0x13, 0x51, 0xae, 0xf1, - 0x6e, 0xaf, 0x19, 0x96, 0xf7, 0xda, 0x21, 0x80, 0x75, 0x4a, 0x91, 0x89, - 0xd0, 0x20, 0x27, 0x5a, 0x82, 0x64, 0x16, 0x20, 0xf0, 0xec, 0xb9, 0x86, - 0x54, 0x4e, 0xfe, 0x60, 0xa8, 0xb0, 0x4a, 0x04, 0xc2, 0x0b, 0xc8, 0x2a, - 0x43, 0x3d, 0xf9, 0x92, 0xe2, 0xd9, 0x55, 0xab, 0x46, 0x0b, 0xe1, 0x7b, - 0x92, 0xe7, 0x32, 0xa6, 0xb8, 0x8a, 0x9b, 0xa6, 0x91, 0x31, 0x3a, 0x73, - 0x16, 0x37, 0x5b, 0xbe, 0x4a, 0xc0, 0xa5, 0x02, 0x03, 0xae, 0xbc, 0xb0, - 0x01, 0x72, 0x55, 0x26, 0x2e, 0x71, 0x50, 0x25, 0x2a, 0xf0, 0xc2, 0xd3, - 0xda, 0xcc, 0x64, 0x26, 0x04, 0x40, 0x23, 0xc7, 0x3e, 0x9e, 0x7e, 0xda, - 0x00, 0x4f, 0x3a, 0xfe, 0x3c, 0x56, 0x55, 0xe0, 0xff, 0xf1, 0x50, 0x80, - 0x36, 0xff, 0xfc, 0x20, 0xaa, 0x1a, 0xd0, 0x94, 0x42, 0x9d, 0x1e, 0xc4, - 0x03, 0xfe, 0x7f, 0xd6, 0x9f, 0x1e, 0xe4, 0xe3, 0xe3, 0xf6, 0x7e, 0xbf, - 0x98, 0xce, 0xb8, 0xce, 0x2f, 0x77, 0xe3, 0x77, 0xd6, 0x4d, 0xfd, 0x5f, - 0x0b, 0x0a, 0xb6, 0x6b, 0x79, 0x02, 0x4e, 0xd0, 0x02, 0x32, 0x25, 0x69, - 0x5c, 0xfb, 0xa5, 0x9d, 0xa9, 0xe0, 0x38, 0x74, 0x03, 0x0c, 0x79, 0x4d, - 0xe6, 0x8c, 0x3f, 0xe8, 0x3f, 0x57, 0x07, 0xbf, 0x90, 0x4d, 0x7c, 0x6e, - 0xe3, 0x9a, 0x87, 0x5e, 0xec, 0xb5, 0x26, 0x93, 0x4c, 0x18, 0x24, 0x48, - 0x3d, 0x59, 0x52, 0xb0, 0xae, 0xf0, 0x3d, 0x6a, 0xc1, 0x85, 0x00, 0xfa, - 0xdd, 0xdb, 0x76, 0xdb, 0x23, 0xc3, 0xc9, 0xe2, 0xf2, 0x8c, 0x18, 0x36, - 0x7b, 0xc1, 0x36, 0x90, 0x5d, 0x07, 0xa9, 0x5f, 0x32, 0xb4, 0x99, 0xed, - 0x5b, 0x0c, 0x1f, 0xe7, 0xc0, 0xe7, 0xed, 0x5b, 0x78, 0xd9, 0x05, 0x78, - 0x2f, 0x2d, 0xf0, 0x6f, 0x2a, 0x10, 0xe4, 0xc3, 0x6f, 0x95, 0xea, 0x12, - 0x74, 0xf1, 0xc3, 0xbe, 0xdc, 0xcd, 0x2c, 0xef, 0x35, 0x84, 0xa9, 0xac, - 0x7b, 0xb7, 0x52, 0x5a, 0xc1, 0x32, 0x8c, 0x09, 0xc7, 0x35, 0xbb, 0xfc, - 0xe1, 0x1a, 0xc0, 0x07, 0xcd, 0xd6, 0x7d, 0xff, 0x23, 0xb0, 0x55, 0x02, - 0x5b, 0x01, 0xe8, 0xb8, 0x99, 0x64, 0xea, 0xef, 0xb2, 0xf9, 0x14, 0xae, - 0x86, 0x59, 0x40, 0x9a, 0xeb, 0x32, 0x88, 0x4c, 0xec, 0x5d, 0x3b, 0x27, - 0xcd, 0x1f, 0x6d, 0x7e, 0x5c, 0xd1, 0xf3, 0xf2, 0xd6, 0xb3, 0x70, 0x49, - 0xa4, 0x41, 0x9f, 0x4c, 0x9b, 0x05, 0xa8, 0xdd, 0x2d, 0x55, 0xdc, 0x88, - 0x9c, 0x09, 0x8c, 0x94, 0x48, 0x00, 0x75, 0x00, 0xf3, 0xc7, 0xaf, 0x8f, - 0xc7, 0xfd, 0x80, 0x0b, 0xc0, 0x02, 0xc7, 0xa4, 0xad, 0x85, 0x9e, 0xc9, - 0xcd, 0xe4, 0x02, 0x1f, 0xdc, 0x1a, 0xb3, 0xaa, 0xf0, 0x21, 0xa0, 0xc4, - 0x5e, 0x27, 0x4f, 0x3e, 0x0e, 0xae, 0xe2, 0xe5, 0xc9, 0x5d, 0x40, 0x3e, - 0x1b, 0xbe, 0xc4, 0xee, 0xf7, 0xd9, 0x3e, 0xf7, 0x87, 0x5a, 0xe1, 0xe3, - 0x93, 0x28, 0xa3, 0x4d, 0x3f, 0x19, 0xb7, 0x9a, 0x0e, 0x00, 0xae, 0xf6, - 0xf5, 0xe1, 0x4a, 0x66, 0x5d, 0xde, 0xe0, 0x77, 0xcc, 0xe1, 0xff, 0x25, - 0xc3, 0x4d, 0xcf, 0x72, 0x37, 0x6a, 0x2a, 0xd1, 0x45, 0x6e, 0xf6, 0x39, - 0xb6, 0xaf, 0x71, 0x97, 0x5c, 0x95, 0x22, 0x12, 0x4a, 0x2b, 0x14, 0x43, - 0x5c, 0x33, 0x12, 0xd7, 0xf6, 0x45, 0xdc, 0xc3, 0xfb, 0x69, 0x22, 0xed, - 0xed, 0x68, 0x80, 0x41, 0xf2, 0xe5, 0xa5, 0x59, 0x3b, 0x0c, 0x49, 0x6b, - 0x6d, 0xbe, 0x68, 0xd0, 0xd0, 0x78, 0xb8, 0xac, 0xe1, 0xf0, 0x0d, 0x02, - 0xe9, 0x89, 0x1d, 0x74, 0x67, 0xdc, 0x16, 0x76, 0x00, 0xfc, 0xc4, 0x9f, - 0xff, 0x16, 0xc9, 0x06, 0x2f, 0x17, 0xb6, 0xbd, 0x2e, 0x22, 0x04, 0x14, - 0xf4, 0x30, 0x80, 0x26, 0x82, 0x08, 0x61, 0x4a, 0x6c, 0x75, 0xc0, 0x5b, - 0xe6, 0xc6, 0x2e, 0x1a, 0x9f, 0x2e, 0xc5, 0x9d, 0xba, 0x90, 0x70, 0xbf, - 0x3f, 0x2b, 0xc0, 0xda, 0x37, 0x51, 0xca, 0xc4, 0xca, 0xf3, 0x53, 0x77, - 0x99, 0x75, 0xdc, 0xff, 0xf1, 0x50, 0x80, 0x30, 0x7f, 0xfc, 0x20, 0xac, - 0x1a, 0xd4, 0x34, 0x70, 0xc9, 0x06, 0xc5, 0x09, 0x5d, 0x76, 0x7e, 0x3f, - 0x27, 0x9e, 0x59, 0xc5, 0x6f, 0xcf, 0x38, 0xfa, 0xe6, 0xf7, 0xc5, 0x49, - 0xb9, 0xd5, 0xef, 0xab, 0xcb, 0xe1, 0xa6, 0x19, 0xd8, 0x73, 0xcc, 0x36, - 0x00, 0x1c, 0x92, 0x76, 0x0f, 0xe1, 0x87, 0x48, 0x77, 0xe8, 0xef, 0x44, - 0x03, 0xe6, 0x7b, 0x57, 0xa8, 0xc8, 0xda, 0xca, 0x50, 0x7b, 0xa2, 0x99, - 0x32, 0xcd, 0x9e, 0x5d, 0x09, 0x33, 0xe4, 0xe9, 0x0c, 0xb6, 0xac, 0x9a, - 0xfd, 0xd5, 0x3c, 0x64, 0xe8, 0xf6, 0x62, 0x53, 0x42, 0x6d, 0x78, 0x1a, - 0xf2, 0x8c, 0x71, 0x0c, 0x91, 0x96, 0x49, 0xe5, 0x9d, 0x9a, 0xcc, 0x16, - 0x97, 0x83, 0xbd, 0x68, 0xaf, 0x3b, 0xe2, 0xdf, 0x19, 0x5e, 0x59, 0xb1, - 0x74, 0x5b, 0xd7, 0x85, 0x29, 0x6a, 0xcb, 0x85, 0x79, 0x41, 0xc9, 0x33, - 0x0a, 0xd8, 0x62, 0xe2, 0xa1, 0x48, 0x9a, 0x9d, 0x14, 0x59, 0xa5, 0xc9, - 0xa7, 0x2a, 0x86, 0x5b, 0x46, 0x8a, 0x69, 0x0d, 0x58, 0x41, 0x75, 0x52, - 0x3b, 0x81, 0xd3, 0x35, 0xb8, 0x0d, 0x33, 0x01, 0x60, 0xf6, 0x79, 0xa6, - 0xaf, 0xc9, 0x25, 0x38, 0xa1, 0x90, 0xb3, 0x13, 0x93, 0x98, 0xd3, 0x01, - 0x80, 0xd3, 0x8a, 0x3d, 0x6a, 0x18, 0xd7, 0x84, 0x87, 0x33, 0x4c, 0x98, - 0x28, 0x33, 0x3e, 0x08, 0x47, 0x34, 0x61, 0x81, 0xe2, 0x98, 0x54, 0x68, - 0x07, 0x4e, 0xf5, 0xac, 0x50, 0x76, 0x81, 0x0d, 0x02, 0x47, 0x33, 0x20, - 0x40, 0x00, 0x00, 0x00, 0x00, 0xbf, 0xc7, 0xa7, 0xfa, 0x05, 0x0e, 0x34, - 0xba, 0xc9, 0xc5, 0xfb, 0x08, 0xa7, 0xee, 0xe5, 0x80, 0x68, 0x08, 0x62, - 0x3a, 0xfa, 0x95, 0xab, 0x1b, 0xb8, 0x46, 0x14, 0xa5, 0x78, 0x72, 0xa6, - 0x83, 0x0c, 0xaa, 0x99, 0x6e, 0xee, 0x94, 0x23, 0xae, 0x47, 0x2f, 0x0c, - 0xb9, 0xdf, 0xee, 0xe0, 0x4c, 0xe0, 0xee, 0xa6, 0xa6, 0xec, 0xf0, 0x6d, - 0x6d, 0x6e, 0x6c, 0x00, 0x60, 0x70, 0x0f, 0x8a, 0x2c, 0x7b, 0x90, 0x6d, - 0xf7, 0x38, 0x3d, 0x93, 0xaf, 0x7f, 0x40, 0x25, 0x7c, 0x41, 0xac, 0x32, - 0xdb, 0xd9, 0xac, 0xe6, 0x6c, 0x96, 0x48, 0xca, 0x08, 0x0f, 0x58, 0x97, - 0x5b, 0x03, 0x9d, 0x14, 0x32, 0x02, 0x0c, 0x4b, 0x69, 0x01, 0x70, 0xbe, - 0xa7, 0x01, 0x8c, 0x45, 0x8c, 0xc6, 0x1c, 0xcf, 0xce, 0xfa, 0xa2, 0x43, - 0x66, 0xf8, 0x08, 0xca, 0x03, 0xf6, 0x11, 0x41, 0x8b, 0xcb, 0x68, 0x1b, - 0x75, 0x3d, 0x00, 0xd5, 0xc7, 0x9a, 0xb5, 0x32, 0xea, 0x44, 0xb6, 0xf8, - 0x12, 0x8d, 0x83, 0x7b, 0x2c, 0x52, 0x7c, 0x52, 0x3f, 0x7b, 0x69, 0x24, - 0xbe, 0x93, 0x8b, 0x6e, 0x94, 0xb6, 0xf0, 0xda, 0x67, 0xf5, 0xaf, 0x82, - 0xff, 0xd5, 0xc2, 0xb2, 0x7d, 0xbe, 0xff, 0xf1, 0x50, 0x80, 0x29, 0x1f, - 0xfc, 0x21, 0x1a, 0xcf, 0xb7, 0xe9, 0x7c, 0x00, 0x00, 0xa1, 0xb2, 0x41, - 0x58, 0xee, 0x76, 0x09, 0x90, 0x8c, 0x84, 0x62, 0x28, 0x42, 0xa2, 0xa0, - 0x2a, 0x52, 0x5e, 0x07, 0x1d, 0xbc, 0xe5, 0xea, 0xa6, 0xa5, 0xad, 0x7c, - 0x78, 0x0d, 0xb2, 0x50, 0xe5, 0x60, 0x01, 0xf2, 0xbe, 0x37, 0xdf, 0xcd, - 0xcd, 0x69, 0x5f, 0xec, 0xa5, 0xcb, 0x0a, 0x12, 0xb8, 0x6b, 0xe0, 0x48, - 0x2a, 0x8c, 0x81, 0x55, 0x82, 0x54, 0x7d, 0xeb, 0xf0, 0x68, 0x8f, 0xf8, - 0xa0, 0x0d, 0x2f, 0xb4, 0xee, 0xa0, 0xc7, 0x62, 0xfd, 0xcf, 0xc4, 0xd9, - 0x70, 0x8b, 0xc8, 0x60, 0x73, 0x63, 0x8d, 0xcd, 0x15, 0x88, 0x0a, 0xa3, - 0x5b, 0xa4, 0xd2, 0x41, 0x2b, 0x11, 0xe4, 0x95, 0x9f, 0x2f, 0x93, 0x18, - 0xd3, 0x40, 0xa4, 0x66, 0x05, 0xbe, 0xa1, 0x99, 0xd0, 0x82, 0x49, 0x06, - 0x87, 0xde, 0x21, 0x02, 0xc7, 0xca, 0xdc, 0xf0, 0x84, 0x0e, 0x93, 0x5a, - 0x50, 0x9f, 0x65, 0x01, 0x3a, 0x13, 0xb4, 0xce, 0xba, 0x66, 0x3b, 0xdb, - 0x3b, 0xb3, 0x49, 0xa7, 0x70, 0x84, 0x56, 0x60, 0xfd, 0x36, 0x63, 0xbb, - 0xac, 0x46, 0xc6, 0x88, 0x4e, 0xf6, 0x8f, 0xf5, 0xae, 0x69, 0xae, 0x8f, - 0x2f, 0x73, 0xc3, 0xa2, 0x33, 0xae, 0xb8, 0x55, 0x56, 0x3b, 0xd3, 0x5a, - 0x55, 0xd2, 0xb1, 0x4c, 0xce, 0x02, 0x57, 0xda, 0xb4, 0x59, 0x42, 0xda, - 0x34, 0x80, 0x5f, 0x6a, 0x50, 0x14, 0x01, 0x55, 0x12, 0x3a, 0x61, 0x55, - 0xec, 0xcb, 0xd6, 0x49, 0x55, 0x56, 0x8c, 0x47, 0xf1, 0x3e, 0x01, 0xdd, - 0x84, 0x88, 0x79, 0xb8, 0xb7, 0x1a, 0xe8, 0xfa, 0x6f, 0xbc, 0x1d, 0x8e, - 0xda, 0x1b, 0x51, 0x12, 0x06, 0xc3, 0x53, 0x7c, 0x40, 0x00, 0x00, 0x09, - 0xdf, 0xdf, 0xbb, 0xac, 0xb6, 0x99, 0x54, 0xe3, 0xdc, 0x31, 0x7d, 0xa9, - 0x05, 0x94, 0xb0, 0xf2, 0x53, 0x14, 0x1f, 0x52, 0x8a, 0x80, 0x00, 0xba, - 0xbd, 0x0f, 0x21, 0x75, 0x2f, 0x8e, 0x2e, 0xf4, 0x35, 0x93, 0xdf, 0x40, - 0xd1, 0xe2, 0x34, 0x29, 0xce, 0x1b, 0xbe, 0x10, 0xb9, 0xf0, 0xbd, 0xd3, - 0x2d, 0x62, 0x68, 0x64, 0x7b, 0xf0, 0xa1, 0xd6, 0x9d, 0x5b, 0x35, 0x89, - 0x6a, 0x44, 0x8b, 0x99, 0xc4, 0xe3, 0x5f, 0xbc, 0x74, 0x72, 0xdc, 0x7b, - 0xfb, 0x0a, 0x95, 0x75, 0xae, 0x65, 0x51, 0xa5, 0xe7, 0x80, 0xff, 0xf1, - 0x50, 0x80, 0x26, 0xff, 0xfc, 0x21, 0x1a, 0xc9, 0xfe, 0xf4, 0x6e, 0x00, - 0x00, 0xa1, 0xb0, 0xd2, 0x1d, 0x46, 0xa4, 0x2b, 0x24, 0x00, 0x00, 0x6b, - 0x60, 0xea, 0xb8, 0xea, 0xb5, 0xeb, 0xcd, 0xcd, 0x65, 0x5c, 0xd6, 0xc3, - 0xa6, 0x7d, 0x88, 0x8c, 0x66, 0xbd, 0x8f, 0xbc, 0xf8, 0x29, 0x0e, 0x10, - 0x9c, 0x10, 0x01, 0x4d, 0xaf, 0x4c, 0xa0, 0xfe, 0x31, 0x5a, 0x4a, 0xe8, - 0xe7, 0x6d, 0xe5, 0xf0, 0x79, 0xf0, 0xcb, 0x53, 0xe3, 0xe5, 0x6b, 0xd0, - 0xe6, 0x35, 0x06, 0x58, 0x91, 0xfe, 0xde, 0x58, 0x9c, 0xb3, 0xe1, 0x94, - 0x67, 0xe4, 0xd4, 0x4e, 0x96, 0xb5, 0x3c, 0xc7, 0x99, 0x60, 0x69, 0x8f, - 0x02, 0x02, 0xc7, 0x36, 0x5c, 0x95, 0x81, 0xbb, 0xe4, 0x65, 0x32, 0xd4, - 0x13, 0x78, 0xf9, 0xdb, 0x66, 0x42, 0x80, 0x15, 0x44, 0x36, 0xd8, 0x27, - 0x6e, 0xf3, 0xe8, 0x69, 0x6c, 0xfd, 0x5a, 0x13, 0x64, 0x1f, 0x27, 0x71, - 0x53, 0x3b, 0xda, 0xc3, 0x05, 0x74, 0x21, 0xde, 0xb7, 0xf0, 0x4c, 0x0c, - 0x2e, 0x60, 0x4e, 0x02, 0x81, 0x8c, 0xd3, 0x4b, 0x23, 0xa9, 0xd9, 0x6a, - 0xcb, 0x30, 0xad, 0xc8, 0x33, 0x73, 0xc8, 0x11, 0x2e, 0x94, 0x15, 0xba, - 0xde, 0x0a, 0x64, 0x56, 0x45, 0x97, 0xb6, 0x67, 0xa0, 0x06, 0x05, 0xca, - 0xa2, 0xb6, 0xb4, 0xac, 0x9a, 0xc0, 0x00, 0x34, 0x79, 0x6d, 0x5b, 0xea, - 0x0a, 0x2f, 0x46, 0x1e, 0xf1, 0x0c, 0x20, 0x08, 0x59, 0x8f, 0x9e, 0x2b, - 0x03, 0xbc, 0x8f, 0x43, 0x4e, 0x25, 0x1f, 0x76, 0x33, 0xe8, 0xc2, 0x36, - 0x0f, 0x3c, 0xaa, 0x74, 0x89, 0xed, 0xbf, 0xf5, 0x0c, 0x04, 0x76, 0x28, - 0x92, 0xd1, 0x58, 0x99, 0x90, 0x42, 0x11, 0x9b, 0xe2, 0x40, 0x15, 0x05, - 0x4a, 0x22, 0xa1, 0xd7, 0x76, 0xa5, 0x2a, 0xe4, 0xc1, 0x81, 0xe2, 0xb9, - 0x3e, 0x8e, 0xe5, 0x8c, 0x9e, 0x8f, 0x45, 0x7e, 0xa6, 0x70, 0x0b, 0xf8, - 0xdb, 0x5a, 0xa0, 0x27, 0x3a, 0x01, 0x05, 0xc4, 0x2b, 0x87, 0x64, 0x4d, - 0xda, 0x27, 0x5c, 0x8a, 0x08, 0xac, 0xed, 0x99, 0xca, 0x19, 0x0a, 0x0d, - 0x8e, 0xe5, 0x3b, 0x85, 0x1e, 0xea, 0x4e, 0xe9, 0x18, 0x23, 0x4a, 0xbc, - 0x9c, 0xfc, 0x77, 0x88, 0x3f, 0x15, 0x02, 0x9a, 0x3c, 0xff, 0xf1, 0x50, - 0x80, 0x27, 0x9f, 0xfc, 0x21, 0x1a, 0xcf, 0xca, 0xea, 0x7e, 0x04, 0x01, - 0xa2, 0xb2, 0x41, 0x95, 0x46, 0x96, 0x6a, 0x84, 0x00, 0xeb, 0xb0, 0xd6, - 0xdf, 0x1c, 0xf2, 0x1f, 0x1e, 0xdf, 0x6f, 0xe6, 0xdc, 0xf0, 0x01, 0x41, - 0xeb, 0x1e, 0x01, 0xcf, 0x71, 0x35, 0xeb, 0x94, 0xfb, 0x5f, 0x07, 0x62, - 0x6b, 0x95, 0xac, 0x1d, 0x10, 0x1f, 0x5c, 0x64, 0xd6, 0xd8, 0xd7, 0xc7, - 0xba, 0x3b, 0xeb, 0xd8, 0xc7, 0x5f, 0x2f, 0x03, 0x3f, 0x1e, 0x82, 0x64, - 0x49, 0xba, 0xfb, 0x81, 0x97, 0x54, 0xd2, 0x2e, 0xa1, 0x75, 0x20, 0xe6, - 0x68, 0x72, 0x76, 0x1a, 0x0d, 0x31, 0x9b, 0x08, 0x1a, 0x5a, 0x5e, 0x85, - 0xbb, 0xcd, 0x64, 0xff, 0xf8, 0xbf, 0xfb, 0x9f, 0x24, 0x03, 0x68, 0xb4, - 0x6e, 0xd6, 0x80, 0x4e, 0x4e, 0x41, 0x36, 0x8a, 0x2e, 0x92, 0x96, 0x81, - 0xd4, 0xde, 0xa2, 0x87, 0xa5, 0xd9, 0xa1, 0x76, 0xad, 0x18, 0xdf, 0x17, - 0xce, 0x9d, 0xce, 0xe8, 0x44, 0xc0, 0x0c, 0x8e, 0x00, 0x09, 0xf1, 0x00, - 0x43, 0x43, 0x64, 0x5c, 0xa4, 0x4b, 0x60, 0x61, 0xfd, 0xe7, 0x55, 0x93, - 0x5a, 0x45, 0x69, 0xad, 0x79, 0x50, 0xc2, 0xc5, 0xba, 0x5a, 0xaf, 0x74, - 0x90, 0xc0, 0x0c, 0xe0, 0xe9, 0x31, 0x0e, 0x5d, 0xd3, 0x05, 0xa2, 0x8a, - 0xf5, 0xc0, 0x55, 0x14, 0x89, 0x0d, 0x3e, 0x6e, 0xd0, 0x8a, 0x84, 0xa0, - 0x6a, 0x52, 0x65, 0xd9, 0x45, 0xc6, 0x10, 0x05, 0x6e, 0x70, 0xb6, 0x1d, - 0x92, 0xcf, 0x4c, 0xb3, 0x42, 0x59, 0x4c, 0xb3, 0x16, 0x23, 0x82, 0x01, - 0xce, 0x13, 0x6f, 0xa9, 0x46, 0x88, 0x40, 0x0b, 0x67, 0x18, 0xa6, 0x23, - 0x75, 0x15, 0xc8, 0x32, 0x6f, 0xb9, 0xeb, 0x2e, 0x3f, 0x5f, 0xab, 0xa3, - 0xf8, 0x00, 0xba, 0x28, 0x32, 0xb0, 0xd3, 0xf0, 0x06, 0x2f, 0x2f, 0x2c, - 0xcb, 0xcb, 0xc7, 0x9e, 0x43, 0x19, 0xc2, 0xb1, 0x0c, 0xbc, 0xd5, 0x60, - 0x74, 0xcd, 0x35, 0xe4, 0x94, 0x72, 0x13, 0x8c, 0x0b, 0x9b, 0xbd, 0x66, - 0xbf, 0x74, 0x95, 0x50, 0x8a, 0x4a, 0xae, 0xe6, 0x5e, 0x84, 0x57, 0x7d, - 0xf8, 0x9e, 0x82, 0x2a, 0x0c, 0xfd, 0xd0, 0xbe, 0x79, 0x1b, 0x7c, 0x7a, - 0x80, 0x69, 0xd7, 0x57, 0xdb, 0xcf, 0x1c, 0xbc, 0x40, 0xee, 0x00, 0x6d, - 0xb8, 0xff, 0xf1, 0x50, 0x80, 0x29, 0x3f, 0xfc, 0x21, 0x2a, 0xcf, 0xfb, - 0xf1, 0x66, 0x04, 0x02, 0xa1, 0xb5, 0x31, 0x4d, 0x6a, 0x16, 0x80, 0x05, - 0x40, 0x51, 0x38, 0xcc, 0x99, 0x1f, 0xbf, 0x9f, 0x1e, 0xdb, 0x9c, 0xfb, - 0x1b, 0xd4, 0xde, 0xb2, 0xc3, 0x8c, 0x90, 0x4d, 0xf8, 0x0c, 0xb1, 0xd6, - 0x1b, 0x11, 0xab, 0x62, 0x4f, 0x4b, 0xd6, 0xe3, 0x21, 0x0e, 0x4e, 0x18, - 0xc0, 0xfe, 0x80, 0x29, 0x0c, 0x4a, 0xa0, 0x5f, 0x68, 0x0e, 0x7c, 0xe8, - 0x86, 0x8d, 0x6a, 0xa3, 0xff, 0x12, 0x37, 0x84, 0xa2, 0xff, 0x13, 0x5a, - 0xd3, 0x9b, 0x46, 0xd9, 0x93, 0x20, 0xab, 0x5f, 0x97, 0x33, 0xca, 0x89, - 0x77, 0x41, 0xb9, 0x6d, 0x4f, 0x3e, 0x00, 0xab, 0x40, 0x4c, 0x20, 0x54, - 0xd2, 0xd8, 0xf8, 0x5d, 0x53, 0xa0, 0xa0, 0x18, 0x01, 0x8a, 0x29, 0xdd, - 0x19, 0x65, 0xb9, 0xc0, 0x5c, 0x49, 0x02, 0x73, 0x13, 0x68, 0x21, 0x36, - 0x69, 0x21, 0x1a, 0x8b, 0x84, 0x81, 0xc9, 0x44, 0x1a, 0x68, 0xc4, 0x95, - 0x92, 0x80, 0x02, 0x02, 0x10, 0x11, 0x30, 0x1d, 0x40, 0xe1, 0x9a, 0x89, - 0x43, 0x65, 0x32, 0xf8, 0x12, 0x76, 0x89, 0x55, 0x74, 0x53, 0x06, 0x55, - 0xcd, 0x6b, 0x5f, 0x12, 0x41, 0x6b, 0xb3, 0xc8, 0x20, 0x11, 0x19, 0x5c, - 0x51, 0x07, 0x2c, 0xa1, 0x14, 0xc5, 0x85, 0x10, 0x16, 0xaa, 0x8c, 0x88, - 0x91, 0x5d, 0x06, 0xb5, 0xde, 0x33, 0x1d, 0xb2, 0xca, 0xa3, 0xca, 0x97, - 0xba, 0x61, 0xb9, 0x04, 0x9c, 0x16, 0x5d, 0x37, 0x69, 0xbc, 0x31, 0x70, - 0xad, 0xc1, 0xda, 0xb6, 0xed, 0xac, 0x04, 0x71, 0x60, 0x00, 0xb6, 0xa9, - 0x97, 0xcc, 0x4b, 0xa6, 0xfc, 0xd4, 0x24, 0xa0, 0x00, 0x00, 0x00, 0x4a, - 0x2f, 0xf2, 0x50, 0x06, 0x72, 0x49, 0x92, 0x59, 0x2e, 0xfd, 0x93, 0xd6, - 0x54, 0xd0, 0x14, 0x30, 0x92, 0x2b, 0x90, 0xd3, 0xf0, 0x00, 0x00, 0x0b, - 0xc0, 0xd6, 0xb7, 0x7c, 0xde, 0xe4, 0x24, 0xa0, 0x1b, 0xd1, 0x5c, 0xcc, - 0x7d, 0x4e, 0x69, 0x16, 0x00, 0x4b, 0x9c, 0xb4, 0x29, 0xdb, 0x71, 0x71, - 0x35, 0x6c, 0x16, 0x0f, 0xef, 0x85, 0x92, 0xfa, 0x38, 0x1d, 0xb2, 0x3d, - 0x46, 0x9e, 0x79, 0xf1, 0xe0, 0xe3, 0x6b, 0x34, 0x32, 0x4f, 0x61, 0x06, - 0x8a, 0xc9, 0x2b, 0xce, 0xf6, 0x1e, 0x0f, 0xcd, 0x4b, 0x43, 0xbd, 0xe3, - 0x89, 0x54, 0x29, 0xa8, 0x61, 0x70, 0xff, 0xf1, 0x50, 0x80, 0x32, 0xdf, - 0xfc, 0x21, 0x4c, 0xd8, 0xbe, 0x72, 0x28, 0x6a, 0x06, 0xa0, 0x4c, 0xd9, - 0x84, 0x8f, 0x48, 0xa0, 0x76, 0x61, 0x23, 0x8a, 0xa6, 0x2b, 0x64, 0x36, - 0x5b, 0xc4, 0x07, 0x85, 0x12, 0x9e, 0x7b, 0xff, 0xad, 0xef, 0x2f, 0x3f, - 0xba, 0xa5, 0x4a, 0xbc, 0x11, 0x52, 0xa5, 0x0e, 0xa8, 0x4a, 0x85, 0x7c, - 0x6c, 0xf7, 0x55, 0xae, 0x45, 0x4b, 0xb4, 0x27, 0xa5, 0xc5, 0x3e, 0x88, - 0xa8, 0x51, 0x38, 0x90, 0xee, 0xfc, 0x72, 0xc1, 0x77, 0xc6, 0xd0, 0xfa, - 0x41, 0x25, 0x28, 0x00, 0xce, 0xb9, 0xcf, 0x51, 0xed, 0xaa, 0x0e, 0x80, - 0xb9, 0x70, 0x9c, 0xdb, 0x28, 0x15, 0x3a, 0xaa, 0x84, 0x27, 0x7c, 0xcf, - 0xb3, 0xd7, 0xcb, 0xfc, 0x96, 0x8b, 0x0f, 0x89, 0x73, 0xa8, 0x6f, 0x4c, - 0xde, 0xbf, 0x3b, 0x6f, 0xb7, 0xd4, 0x24, 0x82, 0x14, 0x09, 0xb8, 0x24, - 0x99, 0x77, 0x74, 0x8f, 0x09, 0x11, 0xa9, 0xe8, 0xd5, 0x7c, 0xc5, 0x97, - 0x32, 0x99, 0x75, 0x37, 0x63, 0xa8, 0x29, 0x6d, 0x13, 0x61, 0x73, 0x5d, - 0x46, 0xf9, 0x31, 0x55, 0x05, 0x28, 0xc7, 0xbb, 0x09, 0x9b, 0x9e, 0x11, - 0xd8, 0x4f, 0xd3, 0x4d, 0x31, 0xb2, 0xea, 0xe0, 0x2c, 0x63, 0xa3, 0x81, - 0x0a, 0xea, 0x75, 0x72, 0x23, 0x6d, 0xfa, 0xbb, 0x52, 0x4e, 0x6d, 0x36, - 0xaf, 0xeb, 0x7d, 0x1f, 0xdf, 0xec, 0xdc, 0x7d, 0x01, 0xb5, 0x37, 0x02, - 0x25, 0x3a, 0xfb, 0x7a, 0xb9, 0xf1, 0xd9, 0x26, 0x53, 0x78, 0xe9, 0x29, - 0xd4, 0x6e, 0xc9, 0x01, 0x0d, 0x6b, 0xca, 0xfe, 0x95, 0xe4, 0x79, 0x8e, - 0x53, 0x99, 0x10, 0xf1, 0x0f, 0x8f, 0x9b, 0x22, 0x9e, 0xef, 0xff, 0x99, - 0x6a, 0xce, 0xb0, 0x6c, 0x71, 0x7a, 0xa6, 0x2c, 0x05, 0x90, 0x9c, 0x70, - 0x16, 0x98, 0x1c, 0x0c, 0x9d, 0x69, 0x98, 0x9c, 0x29, 0x21, 0xac, 0x00, - 0xa6, 0x2c, 0xa5, 0x13, 0xf6, 0xc9, 0x98, 0x4a, 0xa1, 0x7c, 0x6e, 0x3e, - 0xc7, 0x1f, 0x40, 0x52, 0xf2, 0x01, 0xfe, 0x3e, 0x19, 0xc6, 0xff, 0xf0, - 0x3e, 0xff, 0x67, 0x15, 0xb4, 0x0f, 0x6f, 0x5c, 0x73, 0x2e, 0xf1, 0x3b, - 0xee, 0xc0, 0x12, 0x00, 0xa0, 0x04, 0xa1, 0x43, 0x87, 0x5b, 0x77, 0x35, - 0xc8, 0x03, 0x9e, 0xdd, 0xf8, 0x5d, 0x79, 0xe5, 0x6c, 0xfa, 0x3b, 0x43, - 0xb7, 0xcc, 0x72, 0xb3, 0x6c, 0x1a, 0xcf, 0x0b, 0xd7, 0x55, 0x24, 0xda, - 0x43, 0x14, 0xec, 0xce, 0xe4, 0x77, 0xf6, 0x0f, 0xe9, 0x35, 0xd3, 0x3b, - 0x6c, 0xb6, 0xea, 0x7b, 0xd1, 0xa1, 0xa9, 0x19, 0x2e, 0xce, 0xed, 0x9b, - 0x3b, 0xcb, 0xe2, 0xac, 0xf3, 0x0e, 0xf1, 0x00, 0x55, 0xb8, 0xd4, 0x8b, - 0x8b, 0x38, 0xb0, 0xe3, 0xdc, 0x88, 0x8e, 0xf4, 0xc9, 0xec, 0xe0, 0xaf, - 0xdf, 0xd7, 0xeb, 0x9b, 0xb1, 0x6b, 0x83, 0xf9, 0x98, 0x1c, 0xf9, 0x34, - 0xa5, 0x5f, 0xa6, 0xae, 0x5e, 0x4d, 0x38, 0x04, 0x94, 0xa8, 0x29, 0x36, - 0x72, 0xd9, 0x31, 0xf8, 0xff, 0xf1, 0x50, 0x80, 0x3e, 0x1f, 0xfc, 0x20, - 0x9e, 0x4c, 0x9b, 0x65, 0x2e, 0x91, 0x40, 0xec, 0xa5, 0xad, 0x65, 0x09, - 0x4a, 0x30, 0xd9, 0x59, 0x4e, 0x99, 0x00, 0xec, 0x04, 0xf8, 0xe7, 0xf5, - 0xc7, 0x55, 0xc7, 0xfe, 0x06, 0xb3, 0xfd, 0x2f, 0x79, 0xa7, 0xf6, 0x3c, - 0xee, 0xbf, 0x5e, 0x6f, 0xcf, 0x01, 0xe6, 0x22, 0x7e, 0x6b, 0x2d, 0x79, - 0x5a, 0x14, 0x35, 0xb3, 0xb3, 0x38, 0xb2, 0xb6, 0x70, 0x98, 0x28, 0x61, - 0x2b, 0x68, 0x60, 0x0a, 0xff, 0x2a, 0x0f, 0x61, 0xea, 0x90, 0x19, 0xc7, - 0xc5, 0x00, 0xc4, 0x60, 0x40, 0xc2, 0x40, 0x4e, 0x77, 0xe7, 0xf4, 0x76, - 0x7e, 0x0c, 0xf3, 0x56, 0x48, 0xc6, 0x06, 0x18, 0x67, 0x17, 0x88, 0xf1, - 0x96, 0x02, 0x1c, 0x59, 0xe9, 0xe2, 0xc5, 0x3a, 0x1d, 0x0a, 0xda, 0xad, - 0x77, 0x97, 0x37, 0xa7, 0x12, 0x84, 0xf5, 0x62, 0xdd, 0x79, 0x9e, 0x23, - 0x1b, 0x15, 0x7e, 0xd3, 0x3e, 0x89, 0xb2, 0x69, 0x67, 0x18, 0x01, 0xd5, - 0xbf, 0xd9, 0xfe, 0x39, 0x88, 0xc2, 0xe5, 0xd5, 0x40, 0xce, 0x8f, 0xde, - 0xf0, 0x64, 0xeb, 0x63, 0x72, 0x46, 0x32, 0x42, 0xb1, 0x01, 0x4b, 0x5a, - 0x30, 0x70, 0x43, 0x6c, 0x50, 0xad, 0x94, 0x25, 0x0e, 0x18, 0x0b, 0x09, - 0x84, 0xa5, 0x7e, 0xf8, 0xf9, 0x75, 0x68, 0xf6, 0x07, 0xaf, 0xcd, 0xa7, - 0x42, 0x24, 0x2a, 0x0d, 0xbc, 0xb0, 0xfc, 0xed, 0x9f, 0x53, 0x04, 0xbd, - 0xcd, 0x2e, 0x41, 0x52, 0x2a, 0x11, 0xa4, 0x18, 0x48, 0x65, 0xcb, 0xea, - 0x57, 0x54, 0x00, 0xd8, 0xe3, 0x54, 0x16, 0x3b, 0x54, 0xe7, 0x34, 0x60, - 0x25, 0x32, 0xf1, 0x7b, 0xca, 0x8c, 0x12, 0x79, 0xf0, 0xac, 0xd0, 0x51, - 0xcf, 0x82, 0x78, 0x0c, 0x4f, 0x5c, 0x93, 0xa6, 0x77, 0xd5, 0x81, 0x35, - 0x2d, 0x64, 0x3d, 0x34, 0x8b, 0x04, 0xf2, 0xef, 0x25, 0x98, 0x02, 0x8d, - 0xeb, 0x59, 0x99, 0x89, 0x46, 0x28, 0x1c, 0x72, 0x89, 0x2a, 0x85, 0x85, - 0x2e, 0x4e, 0x3c, 0x71, 0xc6, 0x4b, 0xef, 0x7d, 0x79, 0xde, 0xb5, 0xa1, - 0xab, 0xc9, 0x94, 0xf8, 0x08, 0xbe, 0x96, 0xf5, 0x85, 0xbe, 0x35, 0x98, - 0xed, 0xa2, 0x9f, 0x77, 0xd7, 0xdd, 0xff, 0x31, 0xec, 0xf5, 0x02, 0x3d, - 0xfd, 0xaa, 0xe3, 0x8d, 0xf3, 0x45, 0x32, 0x3d, 0xf1, 0xd6, 0xc6, 0x63, - 0xdb, 0x4a, 0x0f, 0xa5, 0x90, 0xab, 0xa9, 0x17, 0x06, 0x2e, 0x71, 0x1e, - 0x74, 0x80, 0xed, 0xed, 0xd9, 0xca, 0xa3, 0xbb, 0x76, 0x1d, 0x37, 0x06, - 0x80, 0x3c, 0x6b, 0x53, 0x52, 0x67, 0xca, 0xd8, 0xb3, 0xce, 0xb8, 0x3a, - 0xdc, 0x89, 0x6c, 0xe7, 0xd0, 0x4a, 0xbb, 0x64, 0x8a, 0xa9, 0x16, 0xf0, - 0xe7, 0xed, 0xe6, 0xba, 0x32, 0xfd, 0x41, 0xfe, 0x85, 0xdf, 0x3d, 0x50, - 0xbe, 0x18, 0xbb, 0xa1, 0x5d, 0xf6, 0x3f, 0xd8, 0x6a, 0xdb, 0x0f, 0x7e, - 0xef, 0x7d, 0x2a, 0x49, 0x86, 0x0d, 0x25, 0x82, 0xcf, 0xb3, 0xc6, 0x01, - 0x68, 0xfd, 0xf3, 0xdf, 0xd0, 0x22, 0x72, 0x0e, 0xf7, 0xe0, 0x4a, 0x0a, - 0x09, 0xcc, 0xb4, 0xf6, 0x84, 0x69, 0x08, 0x99, 0x9b, 0xb2, 0x56, 0x3d, - 0x57, 0x70, 0x0c, 0xbd, 0x5d, 0x1d, 0xb8, 0x06, 0x0e, 0x33, 0x46, 0xfc, - 0x3b, 0xe6, 0xa6, 0x91, 0x0e, 0x58, 0x1d, 0xc7, 0xd8, 0x48, 0x37, 0x8c, - 0x5f, 0x59, 0x55, 0x00, 0xeb, 0x55, 0xcc, 0x48, 0xf3, 0xaa, 0x0d, 0xcd, - 0xc2, 0x85, 0x8c, 0xed, 0x09, 0x27, 0x94, 0x96, 0x0d, 0xf6, 0x6a, 0xea, - 0xb3, 0xb6, 0x71, 0x29, 0x62, 0xd6, 0x37, 0xa3, 0xc7, 0x7d, 0x1d, 0x37, - 0x4c, 0x33, 0x49, 0x8c, 0xcf, 0x8d, 0x21, 0x47, 0xff, 0xf1, 0x50, 0x80, - 0x3a, 0x5f, 0xfc, 0x20, 0x9e, 0x7a, 0xd6, 0x48, 0x62, 0x21, 0xc4, 0x83, - 0x11, 0x21, 0xd8, 0xa0, 0x00, 0x00, 0x0b, 0xfa, 0xe6, 0xfe, 0x75, 0xf1, - 0xdc, 0xe2, 0xf7, 0xad, 0x66, 0x9e, 0x45, 0xba, 0xc2, 0x14, 0xa2, 0xcf, - 0xd2, 0xe6, 0x60, 0x60, 0xc0, 0xe6, 0xfb, 0x2b, 0xa1, 0x33, 0xbd, 0x2a, - 0x9f, 0xb0, 0x91, 0x99, 0xca, 0x54, 0xa4, 0x8e, 0xa1, 0x14, 0x4b, 0x8d, - 0x65, 0x9a, 0x99, 0x86, 0x3e, 0x27, 0x12, 0x6c, 0x40, 0x39, 0x1b, 0x11, - 0x94, 0x2e, 0x03, 0x62, 0xa2, 0x9e, 0xaa, 0x73, 0x57, 0xb8, 0x2c, 0x55, - 0x11, 0x15, 0x3c, 0xb1, 0x2b, 0xad, 0x7b, 0x0d, 0xe7, 0x98, 0x4f, 0x93, - 0x56, 0x9d, 0xe9, 0x59, 0x90, 0xce, 0x4b, 0xa8, 0x4a, 0x3e, 0x2d, 0xd3, - 0x5f, 0x3a, 0x74, 0xcf, 0x9b, 0x90, 0x54, 0x2e, 0xf7, 0xff, 0xcd, 0x8a, - 0xea, 0x39, 0x5b, 0x01, 0xa8, 0xce, 0x4b, 0xa2, 0x59, 0xb1, 0xca, 0xe9, - 0x20, 0xba, 0x9b, 0x44, 0xd2, 0x26, 0x0a, 0x9a, 0xcc, 0x2d, 0xcc, 0xee, - 0x0a, 0x79, 0x07, 0x35, 0x58, 0x84, 0xb2, 0x4b, 0x6d, 0xfd, 0x20, 0x1d, - 0x01, 0x8e, 0x5c, 0x7f, 0x92, 0x1b, 0xf4, 0x00, 0xc8, 0x2f, 0x92, 0x29, - 0x60, 0x96, 0x1d, 0xd5, 0xfb, 0x4c, 0xe9, 0xa1, 0x4f, 0xe0, 0x83, 0xdd, - 0x7d, 0xe2, 0xf3, 0xdb, 0x11, 0x2b, 0xc8, 0xc7, 0x7c, 0x57, 0x81, 0xa1, - 0x1d, 0x72, 0x01, 0x9b, 0x90, 0x3b, 0x45, 0x22, 0x52, 0x2a, 0x45, 0xb5, - 0x8b, 0xad, 0xbb, 0x2a, 0x1a, 0xba, 0x64, 0xc0, 0x05, 0x4d, 0x81, 0x24, - 0x80, 0x00, 0x25, 0x76, 0x5c, 0x6a, 0xdb, 0xfd, 0xfc, 0xfa, 0x90, 0x42, - 0x27, 0xbe, 0x74, 0x6b, 0x5a, 0x21, 0x4c, 0x68, 0x09, 0xa5, 0x82, 0x84, - 0x62, 0x00, 0x00, 0x00, 0x1e, 0x7c, 0x0b, 0xab, 0xfc, 0x78, 0xef, 0xcc, - 0x79, 0xaf, 0x81, 0x74, 0x98, 0x84, 0xc0, 0x10, 0x1a, 0xb8, 0xb3, 0x78, - 0x4a, 0x20, 0x2f, 0xe5, 0x32, 0xfe, 0x9a, 0x17, 0x28, 0x26, 0x39, 0x00, - 0xd8, 0xa6, 0xd0, 0x59, 0x57, 0xa4, 0xe8, 0x6b, 0x54, 0x96, 0x9b, 0xdd, - 0x84, 0xc8, 0xc3, 0x78, 0xf8, 0x48, 0xfb, 0x29, 0xb0, 0x84, 0xdf, 0x07, - 0x30, 0x0f, 0xf9, 0xc7, 0x09, 0x92, 0xf3, 0x34, 0xaa, 0xd0, 0x79, 0x40, - 0xc9, 0x3d, 0x57, 0xef, 0x6a, 0x40, 0x08, 0xbc, 0x2a, 0x12, 0x39, 0xd5, - 0x09, 0x40, 0x2d, 0x02, 0x54, 0x3e, 0xc1, 0x27, 0x47, 0x7d, 0x01, 0xcf, - 0xb6, 0x80, 0xc1, 0xe1, 0x88, 0x66, 0x12, 0xa0, 0x3e, 0xf7, 0x03, 0xf6, - 0x79, 0x17, 0x9e, 0x0f, 0x81, 0x3e, 0x89, 0x5f, 0x4e, 0xaa, 0x0c, 0x12, - 0xfb, 0x64, 0x35, 0xa1, 0xce, 0xc1, 0x65, 0x0f, 0x9d, 0x33, 0x29, 0x2e, - 0x10, 0xbb, 0x7b, 0x0a, 0x19, 0x4a, 0x44, 0xa7, 0x71, 0x98, 0xfd, 0xa1, - 0xe9, 0xb9, 0xda, 0xd3, 0x07, 0x65, 0x56, 0xd0, 0x96, 0xa1, 0x7c, 0x08, - 0x74, 0xd7, 0x82, 0x59, 0x71, 0x59, 0x3b, 0xdb, 0x9e, 0x34, 0x1b, 0xa2, - 0x0b, 0x5c, 0x32, 0x99, 0xd1, 0x2c, 0xf2, 0xaf, 0x4c, 0xe5, 0x8a, 0xd3, - 0x50, 0x80, 0x06, 0x5a, 0xe0, 0x1d, 0x10, 0x5c, 0xdd, 0x70, 0x05, 0x98, - 0x8e, 0xfa, 0x58, 0x8e, 0x2f, 0xd3, 0x45, 0x27, 0x64, 0x64, 0x01, 0x6f, - 0x1b, 0xe2, 0xd9, 0x00, 0x0b, 0x1f, 0xd9, 0xfd, 0x9d, 0x1f, 0xe7, 0xb3, - 0xbf, 0x8b, 0xa1, 0x5d, 0xd5, 0x7f, 0xff, 0xf1, 0x50, 0x80, 0x39, 0xbf, - 0xfc, 0x20, 0xa2, 0x2a, 0xd6, 0x46, 0x32, 0xd5, 0x04, 0xca, 0x02, 0x52, - 0x28, 0x11, 0xc6, 0xea, 0xaf, 0x3f, 0x5f, 0xc7, 0xcf, 0x1d, 0xb8, 0xce, - 0x26, 0x4b, 0xdf, 0x55, 0xb0, 0xf9, 0xca, 0xd6, 0x67, 0x35, 0xdf, 0x31, - 0xda, 0xad, 0x27, 0x1b, 0xf2, 0xd4, 0xde, 0x51, 0x15, 0xc0, 0xaa, 0x91, - 0x11, 0xa9, 0x44, 0x5e, 0x0e, 0xa9, 0x66, 0xbe, 0xfc, 0x35, 0xdf, 0x12, - 0x8f, 0x87, 0x24, 0xdd, 0x4a, 0xa6, 0x04, 0xd4, 0xae, 0x69, 0x55, 0x00, - 0x86, 0x0a, 0x04, 0x89, 0x9a, 0x95, 0xd5, 0x38, 0xd8, 0x0b, 0xe7, 0xf1, - 0xaa, 0x2a, 0x54, 0xf5, 0xfc, 0xbb, 0xb7, 0xec, 0x9d, 0xe7, 0xef, 0xdf, - 0x0c, 0xd3, 0xdb, 0x90, 0x95, 0x0f, 0xb2, 0x2e, 0xda, 0xe7, 0xdd, 0x8a, - 0xd6, 0xe4, 0xc4, 0x49, 0x65, 0xf3, 0xec, 0x14, 0xeb, 0x2c, 0xd2, 0xa0, - 0xa9, 0x9b, 0x78, 0x2f, 0x8c, 0x37, 0x74, 0xa5, 0x12, 0xfc, 0x4a, 0xa5, - 0x01, 0x0d, 0x27, 0xb2, 0x8f, 0x08, 0x01, 0x84, 0x4a, 0x89, 0x0a, 0xe9, - 0x01, 0xde, 0x1f, 0x19, 0x5e, 0x83, 0x83, 0xd9, 0x7b, 0x9b, 0x18, 0x81, - 0x8c, 0x92, 0x25, 0xd3, 0x02, 0x13, 0xb9, 0xd6, 0x44, 0x91, 0x5c, 0xb0, - 0x31, 0x53, 0xca, 0x01, 0x51, 0x9e, 0x6b, 0xe0, 0x4a, 0xa0, 0x3b, 0xeb, - 0xba, 0x52, 0x12, 0xae, 0x88, 0x12, 0x48, 0x40, 0xf3, 0x32, 0xa9, 0x98, - 0x14, 0x0c, 0xba, 0x61, 0xeb, 0x46, 0xdb, 0x42, 0xd6, 0xc5, 0x2c, 0xa9, - 0xbf, 0x35, 0x5a, 0x49, 0x4c, 0x40, 0x4b, 0x5f, 0x65, 0x8f, 0xce, 0xc0, - 0x00, 0x3a, 0x84, 0x19, 0x94, 0x62, 0xe2, 0x72, 0xbe, 0xc6, 0x5a, 0x62, - 0xa3, 0x18, 0xb5, 0xa8, 0x46, 0xb5, 0xa1, 0x96, 0xa6, 0x34, 0x32, 0x80, - 0xd6, 0xc0, 0x00, 0x0f, 0x6e, 0xdf, 0x8f, 0xcf, 0xc3, 0xea, 0xb8, 0xf5, - 0x7a, 0xb9, 0x81, 0x99, 0xad, 0x39, 0x39, 0x4f, 0x9e, 0xf2, 0x3c, 0x6b, - 0x1b, 0x69, 0x28, 0x8b, 0x80, 0x68, 0x98, 0xa2, 0xa2, 0x8f, 0xa9, 0x97, - 0x97, 0x13, 0x8b, 0xaa, 0x04, 0x9a, 0xe7, 0x00, 0x80, 0x03, 0x3f, 0xa1, - 0x55, 0x7d, 0x7a, 0x78, 0x00, 0xfb, 0x68, 0x01, 0xe7, 0xab, 0x36, 0xd7, - 0x9a, 0x03, 0x3a, 0xb8, 0x92, 0x3a, 0x44, 0xc5, 0x1a, 0x37, 0x11, 0x0e, - 0x3d, 0xb2, 0x26, 0xa1, 0x53, 0x0b, 0xf6, 0x64, 0x90, 0xc2, 0x61, 0x35, - 0xe0, 0xc0, 0xb4, 0x50, 0x45, 0x88, 0x0f, 0x3a, 0x97, 0xda, 0xdd, 0xff, - 0x1a, 0x5a, 0x99, 0x49, 0x48, 0x5b, 0x37, 0xb9, 0x38, 0x7d, 0xa9, 0xe1, - 0x46, 0xf3, 0x8c, 0x9e, 0x8f, 0x8a, 0x80, 0x06, 0x24, 0x4a, 0x39, 0xe3, - 0xa6, 0x16, 0x60, 0x2c, 0xc1, 0xc4, 0xd2, 0xbb, 0xe4, 0x36, 0x87, 0xee, - 0xe1, 0xd6, 0xda, 0x48, 0x03, 0x83, 0xcd, 0x3d, 0xba, 0xef, 0x66, 0x64, - 0x31, 0xac, 0xc8, 0x00, 0x0b, 0x95, 0x00, 0x57, 0x98, 0x77, 0x20, 0x82, - 0x30, 0xd0, 0x00, 0x40, 0x15, 0xbf, 0x11, 0x06, 0x4c, 0x6e, 0xec, 0x99, - 0x54, 0x4b, 0x4c, 0xb4, 0x23, 0x71, 0x8a, 0x3f, 0x70, 0x60, 0x96, 0x35, - 0xbc, 0x6c, 0x9c, 0x7e, 0x53, 0x86, 0x7d, 0xdd, 0x84, 0x08, 0x50, 0x1a, - 0xab, 0x6b, 0x49, 0xf4, 0x4c, 0xbb, 0x71, 0x15, 0xc3, 0x44, 0x15, 0xdb, - 0x43, 0x0a, 0x5e, 0x91, 0xbb, 0xc2, 0x8e, 0x24, 0x8b, 0x25, 0x3c, 0xff, - 0xf1, 0x50, 0x80, 0x2f, 0x7f, 0xfc, 0x20, 0x9e, 0x4c, 0xd9, 0x64, 0x68, - 0x5d, 0x34, 0x4a, 0x1f, 0x15, 0xd5, 0xb6, 0xf0, 0x88, 0x0e, 0x91, 0xa6, - 0xff, 0x7e, 0x75, 0x73, 0x5f, 0xdf, 0x5b, 0xfa, 0xf9, 0xd7, 0xdf, 0xc7, - 0x5c, 0x5f, 0x1f, 0xf6, 0x06, 0x95, 0xe5, 0x43, 0xcb, 0x90, 0x36, 0x8e, - 0xf2, 0x68, 0xd6, 0xf8, 0x3a, 0xfb, 0x3c, 0xb3, 0x46, 0x80, 0x27, 0xe7, - 0x28, 0xc7, 0xcb, 0xe6, 0xe8, 0xf6, 0xf7, 0x4d, 0x7d, 0x99, 0xa2, 0xc5, - 0x35, 0xa9, 0xe8, 0xd5, 0x47, 0x67, 0x0e, 0x1a, 0x6a, 0x7f, 0x68, 0xff, - 0xec, 0xd3, 0x3b, 0x6e, 0xc6, 0x0b, 0xec, 0x1f, 0x76, 0x7d, 0xc7, 0xc6, - 0x1a, 0xef, 0xc3, 0x7e, 0x06, 0xc4, 0x2a, 0xbe, 0xe3, 0xcb, 0xc0, 0xcf, - 0xd3, 0xe0, 0x94, 0x3f, 0xf0, 0x3f, 0x8f, 0xd0, 0x3c, 0xe3, 0xc3, 0x0c, - 0xf2, 0x43, 0x08, 0xc5, 0x22, 0x0c, 0xc3, 0x96, 0x29, 0x8c, 0xea, 0xc9, - 0x68, 0xf4, 0xd7, 0xfa, 0xba, 0xc1, 0xd2, 0x69, 0x74, 0x90, 0x88, 0x13, - 0x73, 0xee, 0xa8, 0xd6, 0x45, 0xd7, 0xdb, 0x27, 0x69, 0xe8, 0xfe, 0xe4, - 0xf1, 0x7b, 0xe1, 0xf9, 0x1a, 0x7b, 0xdf, 0x61, 0xa6, 0xa3, 0x77, 0xe4, - 0x4c, 0x7f, 0xfb, 0x5f, 0xc4, 0x56, 0xfa, 0x24, 0xee, 0x1a, 0xb3, 0x95, - 0x66, 0xd9, 0xe0, 0x5b, 0x32, 0x06, 0x56, 0x23, 0x70, 0x79, 0x16, 0xe2, - 0x4b, 0x77, 0x04, 0x09, 0x19, 0xea, 0x95, 0x48, 0x35, 0xa8, 0x8c, 0xf2, - 0x29, 0xa8, 0x88, 0xc5, 0x00, 0x0b, 0xc0, 0x00, 0x1f, 0x1e, 0xfd, 0x6b, - 0x7e, 0xdd, 0xf5, 0xaf, 0x33, 0x2c, 0x2f, 0xfd, 0xd2, 0xf4, 0x0a, 0x6b, - 0x31, 0x7c, 0xfd, 0x7c, 0x2d, 0xbd, 0x23, 0x7f, 0x82, 0x6c, 0xf8, 0xd1, - 0x11, 0x00, 0xa7, 0x59, 0x88, 0x19, 0x02, 0xb9, 0x54, 0x80, 0x73, 0xca, - 0x0a, 0xba, 0xd4, 0x42, 0x98, 0xa0, 0xa7, 0x94, 0x56, 0x55, 0x74, 0x83, - 0xfd, 0xbc, 0x8c, 0x6f, 0x0d, 0xb6, 0xb3, 0xc2, 0x6f, 0x45, 0x33, 0xda, - 0x64, 0x4f, 0xd4, 0x02, 0x26, 0x4e, 0x80, 0x0e, 0x78, 0x37, 0xfc, 0xfa, - 0x91, 0x0d, 0xfc, 0x53, 0xc6, 0x48, 0x07, 0x22, 0xb1, 0x42, 0xe3, 0x3f, - 0x4f, 0xed, 0xe8, 0x40, 0x25, 0x4d, 0xfa, 0xef, 0x99, 0xc2, 0x4f, 0xb7, - 0xb0, 0xe4, 0xac, 0x2e, 0x50, 0x11, 0xfd, 0xac, 0x8c, 0x2f, 0x1d, 0xfe, - 0x1c, 0x69, 0xc7, 0xe6, 0xbe, 0xef, 0x23, 0xbd, 0x5e, 0x74, 0x5a, 0x88, - 0x49, 0x62, 0x82, 0xa2, 0x50, 0xbe, 0x06, 0xcd, 0x70, 0xd7, 0x1c, 0xad, - 0x15, 0xd8, 0x2f, 0x08, 0x76, 0x0d, 0xa8, 0xca, 0x48, 0xc1, 0x2c, 0xd3, - 0xdc, 0x72, 0xa2, 0x14, 0xc2, 0xf0, 0x6b, 0x55, 0x93, 0x0c, 0x76, 0x08, - 0xc2, 0xd1, 0x0f, 0x2a, 0x9a, 0x27, 0xff, 0xf1, 0x50, 0x80, 0x35, 0x1f, - 0xfc, 0x20, 0xa7, 0x7a, 0xd4, 0x36, 0x42, 0x3c, 0x4a, 0x87, 0x62, 0x87, - 0xd7, 0xca, 0x26, 0x00, 0x00, 0x12, 0x70, 0xea, 0xfb, 0xd4, 0xf3, 0x75, - 0xa0, 0x80, 0xfc, 0xc9, 0x25, 0xe9, 0xb9, 0x64, 0xf0, 0xda, 0x3b, 0x7e, - 0xb8, 0xa3, 0x84, 0x0a, 0x42, 0x76, 0x67, 0x89, 0xba, 0x20, 0x12, 0xe9, - 0xa3, 0xa0, 0x1c, 0x8c, 0xb7, 0xc6, 0xb3, 0x05, 0x07, 0xe7, 0xec, 0x87, - 0xf1, 0xdf, 0x60, 0x1f, 0xbb, 0xc1, 0xd1, 0xe6, 0x7b, 0xce, 0xb5, 0xf7, - 0x0a, 0x8b, 0x06, 0x67, 0xc6, 0x0b, 0x43, 0x74, 0x16, 0x81, 0xe1, 0xc1, - 0x28, 0x83, 0xb3, 0xc2, 0x80, 0x91, 0x53, 0x8d, 0x49, 0xd7, 0x2d, 0xa0, - 0xcc, 0xc0, 0xdf, 0x14, 0x7b, 0x4f, 0xec, 0x93, 0x2c, 0x27, 0x48, 0xb1, - 0xd5, 0x5a, 0xc4, 0x8e, 0x9b, 0xa6, 0x69, 0x36, 0x23, 0x41, 0x41, 0x5c, - 0xd5, 0xb8, 0x3d, 0x65, 0x64, 0x40, 0xa2, 0xe7, 0x4d, 0x1c, 0x55, 0x7f, - 0x41, 0xd2, 0x3c, 0x2f, 0xdb, 0xe7, 0x85, 0x49, 0x0d, 0x12, 0x91, 0x85, - 0x00, 0xe1, 0xa0, 0x61, 0x4c, 0x9a, 0xe1, 0xda, 0x5e, 0xce, 0x0b, 0xc4, - 0xb9, 0x73, 0x17, 0x61, 0xb7, 0x26, 0x6a, 0x6f, 0xa5, 0x92, 0x89, 0xda, - 0x6c, 0x7c, 0x39, 0xa3, 0x28, 0xb3, 0x3c, 0xc5, 0x35, 0xd9, 0x4f, 0x10, - 0x2f, 0x55, 0x7b, 0xf2, 0x1a, 0xb3, 0xe0, 0x2f, 0x54, 0x9a, 0x73, 0xbb, - 0xcb, 0x3d, 0x0c, 0x53, 0xdd, 0x97, 0x94, 0xd6, 0x52, 0x7b, 0xf9, 0xf6, - 0x74, 0xb0, 0xd9, 0x6c, 0xb1, 0x42, 0xe6, 0x2a, 0x61, 0xad, 0x4b, 0x63, - 0xa2, 0x84, 0x88, 0x76, 0x20, 0x00, 0x00, 0x00, 0x3a, 0xae, 0x35, 0xcf, - 0x1d, 0xf1, 0xab, 0xe3, 0x8c, 0xb1, 0x33, 0x94, 0xe8, 0x94, 0x54, 0x39, - 0x62, 0xb1, 0xc3, 0x08, 0x98, 0xc2, 0xa2, 0x04, 0x80, 0xdc, 0x14, 0x7d, - 0x7e, 0x3c, 0x60, 0xb3, 0xae, 0x99, 0x10, 0x6f, 0xb0, 0xd4, 0x8a, 0x56, - 0xbf, 0xb3, 0xfa, 0xf1, 0x53, 0x3b, 0x16, 0xa9, 0x6f, 0x21, 0xaa, 0x7f, - 0x80, 0x0a, 0xc2, 0xaf, 0x5f, 0x5d, 0xad, 0xd8, 0x88, 0xd3, 0x35, 0x32, - 0x45, 0xd8, 0x76, 0xd6, 0x9d, 0x17, 0xba, 0xb2, 0x92, 0x85, 0x30, 0x62, - 0xc5, 0xf0, 0x02, 0x4b, 0xd6, 0x2f, 0x05, 0x27, 0x05, 0x70, 0x5b, 0x4d, - 0x58, 0xaa, 0x68, 0x9a, 0xbd, 0x1a, 0xc6, 0x29, 0x89, 0xd3, 0xe1, 0xb6, - 0x8e, 0x3e, 0x48, 0x03, 0xd3, 0xcb, 0x2e, 0xfb, 0x2f, 0x93, 0xa0, 0x9b, - 0x69, 0xd6, 0xf5, 0x64, 0xca, 0xa1, 0x07, 0x93, 0x25, 0xbe, 0x4d, 0x50, - 0x60, 0x01, 0x76, 0x48, 0xac, 0x9f, 0x09, 0x18, 0x51, 0x0c, 0x8b, 0x2d, - 0x21, 0xaa, 0x15, 0x10, 0x2a, 0xa9, 0x12, 0x94, 0xd8, 0x7d, 0x1e, 0x5c, - 0x7a, 0x7e, 0xbd, 0xc8, 0x3a, 0x0c, 0xf4, 0xc1, 0xca, 0xea, 0xb5, 0xa0, - 0xa1, 0x9b, 0xc6, 0x24, 0xd2, 0xf7, 0x77, 0xc2, 0xa1, 0xbb, 0x2a, 0xf6, - 0xf8, 0xca, 0xfd, 0x1d, 0x1d, 0xc4, 0x6e, 0xca, 0x05, 0x73, 0x92, 0x7a, - 0xfb, 0xe9, 0xcd, 0x66, 0x24, 0x0c, 0xca, 0x32, 0x6b, 0x78, 0xff, 0xf1, - 0x50, 0x80, 0x25, 0x3f, 0xfc, 0x21, 0x1a, 0xcf, 0xff, 0xd7, 0xfe, 0xd7, - 0xff, 0xb0, 0x82, 0x23, 0x4d, 0x48, 0xc6, 0x20, 0x38, 0x70, 0x7b, 0xf5, - 0xdf, 0x5e, 0xdf, 0xa7, 0xc7, 0x37, 0x8a, 0xdf, 0x5b, 0xf3, 0xbf, 0xa9, - 0xbf, 0x1d, 0x6b, 0xbb, 0xd6, 0xa6, 0x7b, 0x57, 0x30, 0x27, 0xc2, 0xe5, - 0xe5, 0xb4, 0x76, 0x41, 0xcc, 0x35, 0x2f, 0x3c, 0xd8, 0xba, 0x18, 0x8e, - 0x4a, 0xf3, 0x83, 0xc7, 0x14, 0xd1, 0xed, 0xfa, 0x63, 0x4b, 0xb9, 0x00, - 0x16, 0x12, 0x7b, 0xdd, 0x55, 0x15, 0xd4, 0xc2, 0x3c, 0x0e, 0xe5, 0x51, - 0xc4, 0x6e, 0xd2, 0x1b, 0x6d, 0x5d, 0x09, 0x1b, 0x8a, 0xae, 0xee, 0x8d, - 0x15, 0x2d, 0x00, 0x72, 0x21, 0x64, 0x2c, 0x33, 0xc6, 0x6e, 0x57, 0xcd, - 0x18, 0x86, 0x28, 0xbe, 0x20, 0x73, 0x59, 0x49, 0xc0, 0xd6, 0xee, 0xbb, - 0x5a, 0x7f, 0x8f, 0x3e, 0x55, 0x14, 0xe8, 0xf0, 0x39, 0x4a, 0x5f, 0xd8, - 0xe4, 0x91, 0xd1, 0xa4, 0x8f, 0x28, 0x37, 0x7a, 0x15, 0xe0, 0xaf, 0x2c, - 0xb6, 0xc3, 0xbe, 0x60, 0x06, 0xd5, 0xb8, 0xab, 0x95, 0xfe, 0x4f, 0x70, - 0x37, 0x90, 0xf6, 0xc7, 0x01, 0xfa, 0x5d, 0x11, 0xbe, 0x03, 0x4c, 0xef, - 0x83, 0x80, 0xf1, 0xa9, 0x5c, 0x1a, 0x11, 0x1e, 0x34, 0xf2, 0x0f, 0x1b, - 0x56, 0xfb, 0x4a, 0x9a, 0xcd, 0x72, 0xe9, 0x07, 0x6f, 0x3a, 0xd6, 0x8b, - 0x2b, 0x98, 0x91, 0x67, 0x5b, 0xde, 0x17, 0x25, 0x40, 0xa9, 0xa1, 0xe6, - 0x17, 0x6b, 0x69, 0x96, 0x22, 0xfa, 0xda, 0x49, 0xe5, 0x85, 0x65, 0x42, - 0x4b, 0x03, 0x6b, 0x0d, 0x08, 0x26, 0xe8, 0x83, 0x87, 0x07, 0xbf, 0x5d, - 0xfe, 0x27, 0xf7, 0xfe, 0x79, 0xbc, 0x56, 0xfa, 0xdf, 0x99, 0x77, 0x9d, - 0xf1, 0x75, 0x97, 0x5a, 0xad, 0x37, 0x4d, 0x30, 0xca, 0xc0, 0x72, 0x61, - 0xbe, 0x38, 0x01, 0x9f, 0x58, 0x50, 0x8f, 0xf9, 0xfc, 0xc5, 0x36, 0xd6, - 0xdf, 0x05, 0x06, 0x59, 0x25, 0x3f, 0x50, 0x06, 0xd3, 0xd9, 0x3a, 0xab, - 0x1b, 0xb7, 0x74, 0x2f, 0x96, 0xca, 0x20, 0xbd, 0x73, 0xe6, 0x21, 0x76, - 0x92, 0xb3, 0x32, 0xdf, 0x25, 0x90, 0xe0, 0xff, 0xf1, 0x50, 0x80, 0x23, - 0x3f, 0xfc, 0x21, 0x1a, 0xcd, 0x7d, 0xa7, 0xfe, 0x86, 0x00, 0xa8, 0xb2, - 0xb2, 0x65, 0x26, 0x41, 0x4a, 0x11, 0x86, 0x02, 0x50, 0x00, 0x01, 0x38, - 0xda, 0x26, 0xaa, 0xe7, 0xc6, 0x75, 0x61, 0x94, 0x26, 0x54, 0xf7, 0x37, - 0x44, 0x78, 0x7e, 0xc1, 0x8d, 0xaa, 0xf7, 0xc0, 0x51, 0x62, 0xef, 0x7a, - 0x42, 0x93, 0x5d, 0xcc, 0xb5, 0x25, 0x8c, 0x15, 0x51, 0x24, 0x56, 0x04, - 0xb7, 0x09, 0xaf, 0x93, 0xca, 0x10, 0xa8, 0xd2, 0x29, 0x22, 0xa5, 0x56, - 0x11, 0x39, 0xd7, 0x2b, 0x4b, 0x09, 0x45, 0xab, 0xd3, 0xbc, 0x7e, 0xbd, - 0x8b, 0xeb, 0x74, 0xdd, 0x2e, 0x5e, 0xf9, 0x4a, 0x58, 0x17, 0xbf, 0x9a, - 0xe6, 0x09, 0xc2, 0x44, 0x50, 0xb6, 0xba, 0x5a, 0x72, 0x21, 0x71, 0x91, - 0x62, 0xa8, 0xfd, 0x07, 0x5f, 0x7a, 0x8a, 0x3c, 0x91, 0x65, 0xc0, 0xf8, - 0x42, 0x91, 0x6a, 0x8c, 0xfd, 0xfb, 0xaf, 0x62, 0xb4, 0xb4, 0x61, 0x2b, - 0x3c, 0xdb, 0xac, 0xab, 0x83, 0xc9, 0x80, 0x2a, 0x1a, 0x77, 0xc1, 0x5e, - 0xb4, 0x4b, 0x7d, 0x63, 0xcc, 0xfe, 0xc4, 0x4e, 0x87, 0xa7, 0xc4, 0xce, - 0x8c, 0x4a, 0x4c, 0x1a, 0xa0, 0x4e, 0xcc, 0xe6, 0x76, 0xd6, 0x20, 0x87, - 0x3b, 0xd4, 0x95, 0xf7, 0x2b, 0x4d, 0x30, 0x79, 0x24, 0x03, 0x9a, 0x40, - 0x42, 0x08, 0x9a, 0x49, 0xf6, 0xef, 0xe0, 0x01, 0x64, 0x03, 0x29, 0xb5, - 0x1b, 0xa5, 0xa8, 0xb2, 0x1a, 0x18, 0x76, 0x14, 0x2c, 0x88, 0x47, 0xf1, - 0x01, 0x28, 0x00, 0x01, 0xa5, 0xd7, 0x3c, 0x6e, 0x4a, 0x8b, 0xca, 0xbc, - 0x01, 0x56, 0x6a, 0x2a, 0xd0, 0x4d, 0x22, 0x55, 0x59, 0x28, 0xd4, 0xcc, - 0xfd, 0x67, 0x3b, 0x37, 0x31, 0xab, 0xc1, 0xfb, 0xa4, 0x80, 0x49, 0xe9, - 0xe5, 0xaf, 0x96, 0xab, 0x17, 0x75, 0xe1, 0xc1, 0x5c, 0xd9, 0xbb, 0xa7, - 0x94, 0xc6, 0x9a, 0xec, 0x96, 0xce, 0x23, 0xea, 0xd5, 0x4c, 0x99, 0x63, - 0xea, 0x3a, 0x0c, 0x8c, 0x31, 0xf4, 0x30, 0x9b, 0xf4, 0x30, 0x93, 0x38, - 0xff, 0xf1, 0x50, 0x80, 0x2b, 0x5f, 0xfc, 0x21, 0x1a, 0xcf, 0xff, 0x77, - 0xff, 0x04, 0x00, 0xa2, 0xb2, 0x42, 0xec, 0x2c, 0x6b, 0x0b, 0x04, 0xd8, - 0x42, 0x62, 0x80, 0x00, 0x00, 0x04, 0xa3, 0x8d, 0x7a, 0x9d, 0x4f, 0xaf, - 0x1f, 0x1c, 0x41, 0xd0, 0x64, 0xa9, 0x58, 0x20, 0x40, 0x92, 0x33, 0x69, - 0xa8, 0x6c, 0x83, 0xea, 0xf9, 0x7f, 0xa5, 0x2b, 0xb2, 0x75, 0x41, 0xac, - 0x7a, 0xa5, 0x75, 0x97, 0xb9, 0x89, 0x19, 0xa6, 0x5b, 0x27, 0x6d, 0x32, - 0x96, 0x45, 0xca, 0x09, 0x34, 0x82, 0x86, 0x0a, 0x36, 0x5a, 0x2a, 0x61, - 0x0e, 0xf9, 0x05, 0xc6, 0x41, 0x13, 0x14, 0x4c, 0x4d, 0xa4, 0x20, 0x84, - 0x62, 0x13, 0x42, 0xf6, 0xca, 0xd2, 0x08, 0x8c, 0xec, 0x37, 0xe6, 0x4b, - 0xe4, 0x5b, 0x30, 0xa1, 0x8d, 0x79, 0xd9, 0x77, 0x20, 0x1d, 0xe9, 0x1a, - 0x34, 0xad, 0x68, 0x69, 0xe3, 0xd4, 0x9f, 0x53, 0xff, 0x13, 0xcc, 0xe8, - 0x61, 0xd9, 0x8e, 0x19, 0xc2, 0x3c, 0xa2, 0xb9, 0x23, 0x03, 0x8d, 0xfb, - 0xdc, 0xda, 0xa5, 0xa3, 0x3b, 0xb4, 0x33, 0x18, 0x89, 0xde, 0x49, 0x8d, - 0xd8, 0xd2, 0xda, 0xe0, 0xfa, 0xde, 0xcb, 0x6f, 0xb3, 0xcc, 0x9d, 0x4a, - 0xd4, 0x9e, 0xa0, 0x8e, 0xad, 0xec, 0x9f, 0x3f, 0xd9, 0x2c, 0xc8, 0x3e, - 0xaa, 0x31, 0x70, 0x2d, 0x69, 0x12, 0x8e, 0xef, 0x48, 0xb4, 0x08, 0x99, - 0x01, 0x90, 0xba, 0xa0, 0x52, 0x80, 0x18, 0x80, 0x11, 0x39, 0xbd, 0xd6, - 0x5c, 0x74, 0x9f, 0x59, 0xd0, 0x16, 0x5a, 0xcb, 0x45, 0xd6, 0x10, 0x4d, - 0x7f, 0xbb, 0xae, 0xae, 0x2f, 0x7f, 0x8a, 0x57, 0x56, 0x8c, 0x3c, 0xde, - 0xec, 0x74, 0x52, 0x98, 0x49, 0xdd, 0xdb, 0x7c, 0xf6, 0x63, 0x14, 0x56, - 0x26, 0x6d, 0x85, 0x8d, 0x25, 0xf8, 0x00, 0x00, 0x00, 0x06, 0xb7, 0x2a, - 0x4e, 0xf4, 0x71, 0xea, 0x6a, 0x82, 0xdf, 0x73, 0xfe, 0x37, 0xdf, 0x77, - 0x6c, 0x5e, 0xb6, 0x99, 0xe7, 0x72, 0x25, 0x20, 0x4e, 0x36, 0xdc, 0x4c, - 0xbe, 0xd2, 0xc2, 0x12, 0xcf, 0x20, 0xcc, 0x56, 0x9c, 0xfa, 0x79, 0x54, - 0x51, 0xc8, 0xb4, 0x9b, 0x24, 0xca, 0xf7, 0x4c, 0x88, 0x0c, 0x45, 0x69, - 0x69, 0x17, 0x22, 0xe1, 0x59, 0x9c, 0x8a, 0x46, 0x5c, 0x0c, 0x21, 0x0d, - 0x29, 0x6b, 0x46, 0xfe, 0x81, 0xfd, 0xd6, 0x22, 0xb1, 0xc3, 0x76, 0xc9, - 0x9e, 0x82, 0x72, 0xbc, 0x74, 0x22, 0x71, 0x52, 0xed, 0x30, 0x85, 0x6c, - 0xc1, 0x62, 0x72, 0xcb, 0xe2, 0x66, 0x6d, 0xad, 0x75, 0x7c, 0xff, 0xf1, - 0x50, 0x80, 0x2c, 0x3f, 0xfc, 0x21, 0x1a, 0xc9, 0xff, 0xbf, 0xff, 0x61, - 0xf7, 0xa1, 0xb8, 0x31, 0xac, 0x2a, 0x43, 0x53, 0x18, 0x00, 0x00, 0x00, - 0x09, 0x5e, 0xd5, 0xce, 0x6b, 0xe3, 0x5e, 0x3e, 0x3a, 0xb1, 0x9f, 0x48, - 0xce, 0xc5, 0xf9, 0x4f, 0x3d, 0xf0, 0x84, 0xf4, 0x66, 0xee, 0xcc, 0xc7, - 0x54, 0xfb, 0x86, 0x68, 0xbd, 0xbe, 0x24, 0x72, 0xbc, 0x43, 0x8e, 0x95, - 0xb3, 0xaf, 0x4b, 0x68, 0x9c, 0x6a, 0x62, 0xed, 0x73, 0x94, 0x64, 0x62, - 0xe5, 0x64, 0x06, 0xb2, 0x30, 0x45, 0x2f, 0xad, 0x7b, 0xd1, 0x45, 0x9d, - 0x1e, 0xd0, 0x11, 0x46, 0xb8, 0x80, 0x11, 0x40, 0x34, 0xa2, 0xc0, 0x82, - 0x1d, 0x6c, 0x94, 0x5f, 0x21, 0xf3, 0x08, 0x71, 0xeb, 0xf9, 0x58, 0x8d, - 0xb1, 0x13, 0x2f, 0x0e, 0xbe, 0x19, 0x8e, 0xdf, 0x46, 0x5c, 0x6a, 0xd5, - 0x37, 0x8e, 0xf1, 0x26, 0x69, 0x78, 0x5b, 0x33, 0x6f, 0x1e, 0x73, 0x36, - 0x1f, 0xde, 0x2f, 0xc4, 0x8b, 0x59, 0xaf, 0x22, 0xea, 0x77, 0xfb, 0x00, - 0x28, 0xb6, 0xe0, 0xd1, 0xd9, 0x06, 0x46, 0xde, 0x8f, 0x9e, 0x7e, 0xfd, - 0x9d, 0x76, 0x6b, 0xef, 0x76, 0x6b, 0xbd, 0xd1, 0xe1, 0x89, 0x4e, 0xc2, - 0xe4, 0x20, 0xb5, 0xe5, 0x34, 0xb1, 0x6e, 0x8b, 0xb5, 0x36, 0xca, 0xc2, - 0x26, 0x4b, 0xdc, 0x5c, 0xcc, 0xdd, 0x5d, 0x9a, 0xe1, 0xd4, 0x28, 0x28, - 0x2e, 0x64, 0x01, 0x7b, 0xa1, 0x00, 0x80, 0x15, 0xc0, 0x42, 0x64, 0x80, - 0xaa, 0xc8, 0x33, 0x0b, 0x4b, 0x8b, 0x3c, 0x5b, 0x65, 0x9b, 0xa7, 0xae, - 0xba, 0xa6, 0xc7, 0xc2, 0x89, 0x39, 0x61, 0x3a, 0xce, 0x9b, 0xbe, 0x7a, - 0x07, 0x30, 0x7f, 0x6c, 0x5f, 0xbf, 0x2c, 0xc9, 0x3b, 0xa8, 0x6c, 0x6c, - 0x9b, 0x23, 0x1a, 0x4b, 0xd0, 0x00, 0x00, 0x00, 0x14, 0x97, 0x94, 0xc9, - 0x9c, 0x3b, 0xb6, 0x85, 0x1a, 0x45, 0x96, 0xfd, 0xd6, 0x4d, 0x14, 0x64, - 0xe7, 0xd1, 0xbe, 0x2d, 0x89, 0x62, 0x50, 0xe0, 0x55, 0x6e, 0x0a, 0xeb, - 0xbb, 0x88, 0x9b, 0xbf, 0x45, 0xaa, 0x2a, 0x4b, 0x02, 0x2c, 0xf9, 0xa0, - 0x39, 0xac, 0xe1, 0x35, 0xca, 0x3a, 0xc8, 0xb3, 0xca, 0x4c, 0x69, 0x4c, - 0x10, 0x2c, 0xd2, 0x08, 0xe8, 0xae, 0x3e, 0x1a, 0x4a, 0x47, 0x0a, 0xca, - 0x4a, 0x2b, 0x8a, 0xd2, 0x05, 0x88, 0x32, 0xf8, 0x2f, 0xf2, 0xfb, 0x74, - 0xb1, 0xd1, 0x6d, 0xd9, 0x1a, 0xbd, 0x45, 0x42, 0xee, 0xb2, 0x99, 0xc1, - 0xba, 0x55, 0x37, 0x13, 0x9e, 0x55, 0x30, 0x17, 0xde, 0xf7, 0x6d, 0x49, - 0xa4, 0xc1, 0xc0, 0xff, 0xf1, 0x50, 0x80, 0x36, 0x7f, 0xfc, 0x21, 0x1a, - 0xcf, 0xff, 0xdf, 0xff, 0x7b, 0xd0, 0x9b, 0xb8, 0x41, 0xac, 0x4c, 0x37, - 0x1a, 0xb2, 0x02, 0x00, 0x00, 0x00, 0x00, 0x07, 0x1c, 0xcf, 0xc7, 0xd0, - 0xad, 0xe1, 0x12, 0xc2, 0x6a, 0xbb, 0xec, 0x98, 0x9f, 0x42, 0xbb, 0x49, - 0xe2, 0x59, 0x58, 0xdc, 0xa9, 0xce, 0x14, 0x94, 0xc4, 0x9e, 0x8c, 0x8c, - 0xfe, 0xe4, 0x2b, 0xd5, 0x7d, 0x54, 0xe2, 0x25, 0xcd, 0x22, 0xd0, 0xbb, - 0xfd, 0x1e, 0x8f, 0x66, 0x64, 0x69, 0xf2, 0x15, 0x02, 0x77, 0x79, 0x6c, - 0x4d, 0xc6, 0xf2, 0x32, 0x9c, 0x96, 0xe3, 0x25, 0xb4, 0xcc, 0x66, 0x1e, - 0xec, 0x21, 0x7a, 0xe0, 0xf1, 0x13, 0x14, 0xf7, 0x1e, 0x6b, 0xc8, 0xd5, - 0x6a, 0x89, 0x72, 0xa8, 0xc2, 0x40, 0x39, 0x37, 0x1a, 0x9f, 0x59, 0xbb, - 0x50, 0x58, 0x65, 0x5f, 0x4a, 0x73, 0x28, 0xbc, 0x90, 0x42, 0xf2, 0xf8, - 0x6d, 0x3e, 0x84, 0x50, 0x3c, 0xc2, 0x92, 0x15, 0x49, 0x00, 0xcb, 0x4f, - 0x0e, 0x5b, 0xd6, 0xbb, 0xad, 0x82, 0x6c, 0x4c, 0x12, 0xcb, 0xd0, 0x89, - 0xc1, 0x6c, 0xa7, 0xdd, 0xe8, 0xb6, 0x01, 0xe0, 0x81, 0x3a, 0x9e, 0xb5, - 0x13, 0x11, 0x37, 0x35, 0x02, 0x2f, 0x36, 0x55, 0x73, 0x9b, 0x3c, 0x62, - 0x72, 0x8c, 0x3d, 0x4e, 0x38, 0x17, 0x72, 0x46, 0x6a, 0x98, 0xb4, 0xee, - 0x77, 0x8a, 0x8a, 0xdc, 0x42, 0x17, 0x73, 0x4d, 0xde, 0xdf, 0xea, 0xb8, - 0x0c, 0x91, 0x00, 0x29, 0x37, 0x72, 0x02, 0x41, 0x75, 0xdf, 0x39, 0x40, - 0x25, 0x73, 0x5b, 0xa5, 0xec, 0xd6, 0x61, 0x55, 0x04, 0xdd, 0x65, 0x23, - 0x15, 0x54, 0x9b, 0x80, 0x40, 0x1a, 0xab, 0xab, 0x80, 0x5d, 0xae, 0x41, - 0x7c, 0x49, 0x02, 0x02, 0x40, 0x2b, 0xdb, 0x9e, 0x88, 0x8a, 0xae, 0x3d, - 0x28, 0xb6, 0x06, 0xeb, 0xc5, 0xaa, 0x19, 0x55, 0xf7, 0x6c, 0xca, 0x27, - 0x9f, 0x58, 0x8b, 0x31, 0xa6, 0xec, 0x30, 0xbb, 0x5b, 0x0e, 0x43, 0xcc, - 0x41, 0x78, 0x50, 0x20, 0x00, 0xcb, 0x00, 0x0c, 0xd6, 0x6b, 0xc7, 0x0d, - 0x92, 0xfd, 0xbd, 0xb3, 0x90, 0xeb, 0x24, 0x90, 0xc3, 0xad, 0xfb, 0xb6, - 0x24, 0x64, 0x67, 0x5c, 0x43, 0x94, 0x2a, 0xe7, 0x6b, 0xe0, 0x7f, 0x92, - 0x5f, 0x26, 0xd3, 0x93, 0xce, 0x11, 0xb2, 0x1a, 0x6d, 0xab, 0xbe, 0x65, - 0xf5, 0x6e, 0x17, 0x3b, 0x85, 0x51, 0x99, 0xab, 0xf5, 0x9c, 0xa7, 0x53, - 0xcd, 0x95, 0x0e, 0x0e, 0x71, 0x00, 0x2d, 0xa7, 0x59, 0x57, 0x31, 0x34, - 0xe5, 0x8c, 0xeb, 0xb8, 0x61, 0x46, 0xcd, 0x8f, 0xd0, 0x46, 0xff, 0x38, - 0xc4, 0xbd, 0x6a, 0x1e, 0xd1, 0x11, 0x60, 0x56, 0x31, 0x11, 0x69, 0x4c, - 0x67, 0x3c, 0x4a, 0xcc, 0x7f, 0x3b, 0x8e, 0x2a, 0x9d, 0x8f, 0xb5, 0x41, - 0xe1, 0x83, 0x97, 0x72, 0x4e, 0xbe, 0x33, 0x50, 0x3e, 0xc0, 0xbd, 0xfb, - 0x55, 0xc7, 0xbc, 0x6f, 0xa1, 0x99, 0x0b, 0x6b, 0xd3, 0x7f, 0x94, 0xd2, - 0xcd, 0xad, 0xb5, 0x95, 0xaf, 0x31, 0x78, 0xf7, 0x4f, 0x9d, 0x7f, 0xde, - 0x7e, 0x42, 0x86, 0xa2, 0xfe, 0x97, 0xab, 0x3d, 0xd2, 0xde, 0x46, 0xad, - 0x8c, 0x83, 0x74, 0x98, 0xbd, 0xc0, 0xff, 0xf1, 0x50, 0x80, 0x2e, 0xbf, - 0xfc, 0x21, 0x1a, 0xce, 0xdd, 0x37, 0x7f, 0x49, 0x48, 0x9e, 0xb8, 0x30, - 0xe0, 0x76, 0x13, 0x4a, 0x08, 0x46, 0xc2, 0x50, 0xc0, 0xc0, 0x00, 0x00, - 0x04, 0xa1, 0xfa, 0xef, 0xe6, 0x57, 0xc7, 0xb7, 0xcf, 0xdf, 0xa0, 0xe2, - 0xc2, 0x26, 0xc4, 0xff, 0xf6, 0x53, 0x27, 0xe8, 0x68, 0x97, 0xa9, 0xdc, - 0x33, 0xcd, 0xcc, 0xe0, 0x61, 0x5f, 0x0b, 0x78, 0xe2, 0x2a, 0x28, 0x9a, - 0x97, 0x08, 0x7c, 0x5b, 0xf2, 0x14, 0x8c, 0xdb, 0x58, 0x9c, 0x71, 0x3b, - 0x34, 0xe1, 0xd7, 0xd2, 0x30, 0x1d, 0x71, 0x6d, 0xe9, 0x34, 0xba, 0x49, - 0xe4, 0xca, 0x37, 0x21, 0xb5, 0x12, 0x01, 0x93, 0xa0, 0x50, 0x8e, 0xd7, - 0x52, 0xd0, 0xea, 0x50, 0x8e, 0xb2, 0xd0, 0x80, 0x48, 0x5e, 0x14, 0x8a, - 0xa1, 0x99, 0xbf, 0x5c, 0x8e, 0xf0, 0x38, 0xa0, 0x3a, 0xb0, 0x73, 0x3a, - 0x2f, 0xaf, 0x97, 0xcb, 0x76, 0x5e, 0xed, 0x3e, 0x49, 0xa4, 0xda, 0xbb, - 0x5a, 0x9e, 0xd7, 0x1e, 0x46, 0x3e, 0x66, 0x7d, 0x26, 0x29, 0x40, 0x96, - 0xdb, 0x56, 0x2e, 0xf7, 0xf2, 0xe6, 0x04, 0x9b, 0xad, 0x16, 0x25, 0x44, - 0x7d, 0x77, 0x14, 0xe4, 0x42, 0x3e, 0xb5, 0xf2, 0xff, 0x50, 0xf8, 0x93, - 0x60, 0x62, 0x7e, 0x60, 0x6a, 0xcb, 0x0e, 0xf8, 0x40, 0xcd, 0x6a, 0x98, - 0x9b, 0x69, 0x06, 0x74, 0x89, 0xc8, 0xd1, 0xe9, 0x81, 0x76, 0x04, 0x34, - 0x0b, 0x27, 0xba, 0xad, 0xf7, 0xac, 0xc7, 0x34, 0x94, 0x26, 0x74, 0x22, - 0x56, 0x2b, 0xbc, 0xd3, 0x20, 0xc0, 0x8c, 0x5a, 0xb6, 0xd4, 0x5e, 0x17, - 0x94, 0x17, 0x8c, 0xd2, 0x5a, 0x3d, 0x76, 0x04, 0x2a, 0x61, 0x2a, 0x39, - 0x23, 0xb5, 0xe3, 0xa9, 0xc0, 0xa6, 0x69, 0xb1, 0x69, 0x53, 0x4e, 0x33, - 0x04, 0xec, 0x23, 0x3e, 0x8e, 0x0a, 0xfe, 0x35, 0xcf, 0x58, 0xe1, 0x36, - 0x49, 0x5f, 0xc0, 0xcb, 0x65, 0x99, 0x78, 0xb6, 0x59, 0x96, 0x01, 0x57, - 0xcf, 0x5b, 0xca, 0x89, 0xb9, 0xaa, 0x0b, 0xbf, 0xca, 0xd7, 0x39, 0xfc, - 0x6e, 0x85, 0x4e, 0x73, 0xd1, 0xc2, 0xf0, 0x48, 0x46, 0x83, 0x95, 0xd6, - 0x45, 0x85, 0x12, 0xfc, 0x90, 0x6c, 0x6d, 0xf0, 0xb4, 0x9b, 0xea, 0x64, - 0x54, 0x18, 0xa5, 0x41, 0x50, 0x16, 0x1c, 0x86, 0xa0, 0x87, 0x2f, 0x9b, - 0x41, 0x36, 0x2a, 0x6d, 0x8b, 0x1b, 0xe5, 0x08, 0xe7, 0xe8, 0x3c, 0xf6, - 0xb4, 0xa7, 0x36, 0xae, 0x3c, 0xad, 0x24, 0xcc, 0xe7, 0x9c, 0xe0, 0xbe, - 0x4f, 0xf2, 0xd9, 0xe5, 0x85, 0xb5, 0x70, 0xcd, 0xd4, 0xcc, 0x65, 0xa9, - 0x33, 0x3b, 0xb0, 0xd7, 0xac, 0xb1, 0xa4, 0xdc, 0x63, 0x8c, 0xe4, 0xa2, - 0xe5, 0x87, 0xb4, 0x6a, 0x12, 0x5b, 0x80, 0xff, 0xf1, 0x50, 0x80, 0x2d, - 0xff, 0xfc, 0x21, 0x1a, 0xcf, 0xdd, 0xf2, 0xff, 0x77, 0xf7, 0x9e, 0xb8, - 0x31, 0xac, 0x2e, 0x51, 0x22, 0x15, 0x8c, 0x00, 0x01, 0x28, 0x01, 0x14, - 0xd7, 0xde, 0x78, 0x99, 0xf5, 0x3c, 0x79, 0xbe, 0x06, 0xff, 0x24, 0xda, - 0x24, 0x82, 0x5d, 0x6d, 0x64, 0x45, 0x0f, 0xea, 0x8a, 0xbe, 0xe1, 0x1d, - 0x01, 0x73, 0x84, 0xdb, 0xb8, 0xd4, 0x0f, 0x47, 0x50, 0x16, 0x7c, 0xd6, - 0x61, 0x22, 0x20, 0xcd, 0x3a, 0x0f, 0x37, 0xd0, 0x89, 0x68, 0x2c, 0x61, - 0x4d, 0x5e, 0x91, 0xbd, 0x26, 0x81, 0xd8, 0x7c, 0xc0, 0x79, 0x84, 0x06, - 0x00, 0xa7, 0xb6, 0xe7, 0x45, 0x6b, 0xf5, 0xba, 0x18, 0x21, 0xbb, 0x35, - 0x7c, 0xf2, 0xa8, 0x33, 0xd0, 0x02, 0xdd, 0x9d, 0x43, 0x8e, 0x0e, 0x29, - 0x21, 0x3c, 0x66, 0xe8, 0x9c, 0x3d, 0x25, 0x2f, 0xa4, 0x44, 0xe5, 0x8e, - 0x58, 0x04, 0xad, 0x53, 0x80, 0x2b, 0xa3, 0x19, 0x05, 0xeb, 0xa8, 0x12, - 0x04, 0xe4, 0xc1, 0xbd, 0x3d, 0xb8, 0x40, 0x19, 0x4c, 0x08, 0x49, 0x77, - 0xf3, 0x5e, 0x3b, 0xfa, 0x50, 0x5d, 0xc3, 0x79, 0x0a, 0x09, 0xfe, 0xab, - 0x74, 0x6f, 0xda, 0x92, 0x7c, 0x67, 0x91, 0x04, 0xbd, 0x9c, 0x47, 0xa3, - 0xaa, 0xf2, 0xf0, 0x04, 0x80, 0x19, 0x78, 0x22, 0xa3, 0xa5, 0x1e, 0xda, - 0xd4, 0xb0, 0x69, 0xfe, 0xf5, 0x08, 0x4f, 0x85, 0x9d, 0x54, 0x8d, 0x60, - 0xee, 0x74, 0x0a, 0xdf, 0x2e, 0x34, 0x67, 0x0e, 0x17, 0x37, 0xe0, 0xd8, - 0x01, 0x80, 0xc8, 0x45, 0xb9, 0xcd, 0x0f, 0x7a, 0x00, 0x86, 0x33, 0x50, - 0x5a, 0xeb, 0xf7, 0x33, 0xb0, 0xca, 0x76, 0x09, 0x4f, 0xbb, 0x55, 0x67, - 0xac, 0x57, 0x5e, 0x26, 0x38, 0xdd, 0x5d, 0xb6, 0x78, 0x7e, 0xb6, 0xae, - 0x53, 0x7f, 0x9a, 0x36, 0x5f, 0xfa, 0xc6, 0xb8, 0xba, 0x7a, 0xc3, 0x0b, - 0xb2, 0x30, 0xe0, 0x28, 0x28, 0x0a, 0x93, 0xa0, 0x0d, 0xe8, 0xde, 0x83, - 0x6d, 0x6f, 0x40, 0x2a, 0x6f, 0x55, 0xc6, 0xea, 0x89, 0x92, 0xe0, 0x4f, - 0xe2, 0xd4, 0xab, 0x0a, 0x68, 0x52, 0x53, 0x9a, 0x03, 0xa8, 0x6a, 0x92, - 0xb5, 0x6f, 0x73, 0xf5, 0x2f, 0xa1, 0x5e, 0xa1, 0xd3, 0x73, 0x15, 0x19, - 0x5a, 0x41, 0xd4, 0x4a, 0xc7, 0x13, 0x96, 0x69, 0x43, 0xc9, 0x32, 0x72, - 0x25, 0x71, 0x55, 0xc3, 0x16, 0xfa, 0x10, 0x72, 0x40, 0x3a, 0xa5, 0x19, - 0xe2, 0x05, 0xaa, 0x5e, 0x81, 0x8c, 0xb5, 0x78, 0x12, 0x72, 0x6c, 0xcf, - 0xc8, 0x78, 0xba, 0xf1, 0xa2, 0xc0, 0xd7, 0xcc, 0x66, 0x21, 0xc7, 0x2d, - 0xf3, 0xac, 0x3c, 0xac, 0xd8, 0x9a, 0x46, 0x8b, 0x97, 0xd5, 0xf4, 0xf2, - 0x50, 0x38, 0xff, 0xf1, 0x50, 0x80, 0x2b, 0xdf, 0xfc, 0x21, 0x1a, 0xcf, - 0xdd, 0x35, 0xfb, 0x08, 0x02, 0xa1, 0xb6, 0xba, 0xcc, 0xea, 0x16, 0x58, - 0x2f, 0x12, 0x80, 0x85, 0x07, 0x59, 0xbf, 0xdf, 0x7c, 0xf7, 0xf8, 0xe3, - 0x8f, 0x7f, 0x3b, 0xf6, 0xab, 0xe6, 0xc1, 0xc8, 0x45, 0x10, 0xe8, 0x72, - 0x44, 0x2c, 0x9c, 0x39, 0x04, 0xf4, 0x64, 0x20, 0x73, 0x4b, 0x46, 0x2b, - 0xf0, 0x05, 0x44, 0x59, 0x86, 0xa3, 0x48, 0xcc, 0xc4, 0xc7, 0x22, 0x4a, - 0xff, 0xdb, 0xb5, 0x41, 0xa5, 0x51, 0x56, 0xc7, 0x53, 0xe0, 0xe8, 0xed, - 0x65, 0x7b, 0x48, 0xb4, 0x93, 0x8e, 0x84, 0xb3, 0xdb, 0x09, 0x91, 0x84, - 0x28, 0x56, 0xd3, 0x7f, 0xf4, 0xf7, 0x91, 0x58, 0x95, 0x0c, 0xbc, 0x3d, - 0x12, 0x27, 0x8f, 0x93, 0x2d, 0x31, 0x50, 0xf4, 0x90, 0xfc, 0xd6, 0xce, - 0xeb, 0x57, 0xc8, 0xec, 0xe4, 0x3c, 0x0e, 0x38, 0xed, 0x70, 0x25, 0xdc, - 0x00, 0x50, 0xa5, 0xce, 0xac, 0xbc, 0x07, 0x77, 0x35, 0xb5, 0x28, 0x61, - 0xcd, 0x22, 0x54, 0x63, 0x79, 0xc8, 0x2a, 0x92, 0xb4, 0xb3, 0x21, 0x19, - 0x24, 0x8a, 0xe5, 0x96, 0xaa, 0x50, 0x48, 0x2d, 0x3c, 0xc1, 0x4c, 0x90, - 0x09, 0x43, 0x94, 0x2d, 0xd2, 0x4d, 0x7b, 0xb0, 0x84, 0x8c, 0xf2, 0x48, - 0x8e, 0x80, 0xf9, 0xdb, 0x88, 0x1a, 0x89, 0x00, 0x45, 0xa6, 0x5f, 0x9b, - 0x4a, 0xac, 0x24, 0x7f, 0x40, 0x9b, 0xd3, 0x4a, 0x77, 0x85, 0x98, 0x0f, - 0x71, 0xac, 0xa9, 0x4c, 0x12, 0x82, 0x54, 0xb2, 0xd7, 0x7b, 0x4d, 0xba, - 0x0a, 0xcc, 0xfd, 0xfa, 0xf6, 0x0d, 0xd3, 0xc5, 0xca, 0x95, 0x10, 0x0c, - 0xc9, 0x96, 0x72, 0xdd, 0x90, 0x00, 0x4f, 0xd8, 0x84, 0x57, 0x76, 0xb7, - 0x69, 0xb7, 0x7d, 0xe5, 0x64, 0x98, 0x9f, 0x15, 0x88, 0xd8, 0xaa, 0x75, - 0x50, 0xd0, 0x61, 0x76, 0x49, 0x5f, 0xc0, 0x02, 0x50, 0x12, 0xa5, 0x03, - 0x5b, 0xb6, 0x33, 0x8c, 0x31, 0x26, 0x5e, 0x00, 0x3a, 0x2f, 0xe3, 0xd4, - 0x54, 0x24, 0x20, 0x74, 0x86, 0xcb, 0x80, 0x72, 0x9c, 0xa8, 0x3a, 0x44, - 0x47, 0x42, 0xfc, 0x91, 0x63, 0x5e, 0x91, 0xa3, 0xb9, 0x4e, 0x4f, 0x13, - 0x3a, 0x74, 0xc2, 0xcb, 0x02, 0xd9, 0xf2, 0x34, 0xce, 0x97, 0xf0, 0xd1, - 0xba, 0x15, 0xca, 0xd9, 0x31, 0xc5, 0x45, 0x6b, 0xc1, 0xe9, 0xb8, 0xe3, - 0x31, 0x0a, 0xdf, 0x6c, 0x64, 0xb4, 0x1b, 0xbc, 0x16, 0x77, 0x84, 0xac, - 0xbe, 0x7c, 0xca, 0xa1, 0x54, 0xbb, 0xcf, 0x12, 0xca, 0xa5, 0x4c, 0x18, - 0x95, 0xb1, 0x00, 0x70, 0xff, 0xf1, 0x50, 0x80, 0x2a, 0xdf, 0xfc, 0x21, - 0x1a, 0xce, 0xd1, 0x83, 0xff, 0x40, 0x00, 0xa0, 0xb6, 0xba, 0xcd, 0x28, - 0x11, 0x1b, 0x18, 0xa8, 0xa9, 0x5f, 0x7f, 0xb4, 0x50, 0x40, 0x1c, 0x73, - 0xf1, 0xf3, 0x79, 0xf8, 0xcf, 0x6e, 0x6f, 0xf8, 0xff, 0x4e, 0xb9, 0xea, - 0xb9, 0x19, 0x4c, 0x90, 0x67, 0x76, 0xd6, 0x3b, 0xd4, 0x8e, 0xd9, 0xa4, - 0x92, 0x76, 0x7b, 0x3a, 0x31, 0x82, 0x0c, 0xd6, 0x78, 0xf3, 0x21, 0x97, - 0x98, 0xf6, 0xaa, 0x84, 0x32, 0x39, 0x6e, 0x59, 0x2e, 0x51, 0x81, 0x65, - 0xf4, 0xb3, 0xc5, 0x2c, 0xb9, 0x3a, 0x6a, 0x6e, 0x19, 0xe3, 0x11, 0x95, - 0x2b, 0x15, 0x63, 0xda, 0xca, 0xc5, 0x22, 0x67, 0x64, 0xb7, 0x6d, 0x85, - 0xdf, 0x7b, 0x17, 0xc9, 0x84, 0xc5, 0x57, 0x6b, 0x26, 0xe5, 0x2c, 0xa8, - 0xd3, 0xa8, 0xf3, 0xea, 0x0d, 0xff, 0x6b, 0xa3, 0x2d, 0xa5, 0x04, 0xe2, - 0x11, 0x00, 0x7a, 0x01, 0x06, 0xe3, 0x86, 0x7d, 0x5f, 0xb7, 0xd8, 0x59, - 0xe1, 0xd6, 0x2a, 0x7e, 0x83, 0xa4, 0x2e, 0x76, 0x10, 0xd0, 0xb2, 0xf4, - 0x84, 0x36, 0x40, 0x96, 0x6f, 0xbf, 0xe2, 0xfd, 0xe1, 0x18, 0x1c, 0x00, - 0xd6, 0xb2, 0x40, 0x05, 0x04, 0xeb, 0x24, 0xe8, 0xe3, 0x9d, 0x57, 0x4f, - 0xeb, 0xa2, 0xdb, 0xa0, 0x82, 0xfc, 0xce, 0x57, 0x46, 0x49, 0xcd, 0x5d, - 0x06, 0xe8, 0x90, 0xc2, 0x50, 0xb6, 0x35, 0xc9, 0x25, 0x75, 0xb5, 0xd7, - 0x92, 0x5b, 0xdb, 0xbe, 0x63, 0x59, 0xb8, 0x66, 0x83, 0x6c, 0xe5, 0x83, - 0x69, 0xaa, 0x4b, 0x89, 0x45, 0xc6, 0xd9, 0x82, 0x12, 0x56, 0x90, 0x02, - 0xfa, 0x0b, 0x8c, 0x95, 0x84, 0x63, 0xf1, 0x18, 0x7c, 0xc2, 0x1f, 0x30, - 0x05, 0xe0, 0x00, 0x0e, 0x39, 0xbd, 0xfd, 0x4c, 0xf3, 0xce, 0xb9, 0xd9, - 0x5a, 0xca, 0x03, 0x74, 0x92, 0xf9, 0xb9, 0xf0, 0x84, 0x77, 0x91, 0x14, - 0xca, 0x5a, 0x2c, 0x13, 0x43, 0xc0, 0x73, 0xb7, 0xf1, 0x4f, 0x4e, 0x0f, - 0x96, 0x62, 0x81, 0x51, 0xcb, 0xc5, 0xda, 0xca, 0x1e, 0x14, 0x7c, 0x86, - 0x6a, 0x39, 0x68, 0x8f, 0xa6, 0x9e, 0x25, 0xb8, 0x0d, 0xa3, 0x10, 0xa1, - 0x79, 0x31, 0x5a, 0x48, 0xbc, 0x58, 0xe5, 0x15, 0x8f, 0xf8, 0xcd, 0x6a, - 0x2e, 0x5a, 0xf5, 0x9d, 0x49, 0x76, 0x16, 0xfc, 0x60, 0x8d, 0x09, 0xd0, - 0xd7, 0xeb, 0x30, 0x0c, 0x13, 0x97, 0xa3, 0x44, 0x5c, 0x4a, 0x1f, 0xf0, - 0xea, 0x19, 0x3d, 0xbb, 0x31, 0x65, 0x74, 0xca, 0x23, 0x80, 0xff, 0xf1, - 0x50, 0x80, 0x2f, 0xdf, 0xfc, 0x21, 0x1a, 0xce, 0xf8, 0xa0, 0x97, 0x3b, - 0xfb, 0xa0, 0xb6, 0xb1, 0x9d, 0x0c, 0x13, 0x2b, 0x0c, 0xc2, 0xc4, 0x81, - 0x01, 0xad, 0xf1, 0xba, 0x17, 0x9c, 0x33, 0x2e, 0x6d, 0xa9, 0xcf, 0xeb, - 0xe1, 0xdf, 0xe3, 0x7d, 0x77, 0xd7, 0xdb, 0xef, 0x2a, 0xfd, 0xbd, 0xc3, - 0x80, 0x88, 0x03, 0xd8, 0x7b, 0xce, 0x29, 0x01, 0x57, 0x60, 0x2b, 0x40, - 0x5e, 0xd2, 0x99, 0xec, 0x1d, 0x86, 0x1c, 0x64, 0xae, 0xf6, 0x74, 0xb1, - 0x80, 0xd7, 0xeb, 0x6e, 0x8c, 0x58, 0xa1, 0x09, 0xc2, 0x9a, 0xea, 0xf3, - 0x01, 0x47, 0xbf, 0x7e, 0xc1, 0x16, 0xbe, 0xa1, 0x64, 0x01, 0x46, 0x74, - 0x02, 0xa5, 0xb9, 0x7f, 0xbd, 0xb2, 0x2b, 0x1a, 0xd7, 0xcf, 0x3c, 0x18, - 0x4a, 0x5a, 0xf1, 0x8d, 0xd7, 0x33, 0x7e, 0x30, 0xdf, 0x35, 0xd9, 0x7e, - 0xfe, 0xd4, 0x86, 0x70, 0x9a, 0x14, 0x5d, 0x29, 0x36, 0x95, 0xef, 0x90, - 0x05, 0x92, 0xe5, 0x26, 0xbd, 0x49, 0xd1, 0xa4, 0xa6, 0xa9, 0x7e, 0x62, - 0x26, 0x86, 0x7c, 0x5c, 0x28, 0x76, 0xd9, 0xc9, 0x3e, 0x14, 0x8f, 0x49, - 0xe9, 0x1d, 0x0e, 0xbe, 0xa7, 0xf4, 0x4f, 0x38, 0x6b, 0x25, 0xf9, 0x97, - 0x59, 0x3e, 0xbe, 0x89, 0x54, 0x1c, 0x22, 0xe6, 0x9d, 0x96, 0xa6, 0xab, - 0x69, 0x02, 0x08, 0x46, 0x6e, 0x0e, 0x84, 0x06, 0x06, 0x18, 0x91, 0x59, - 0x0a, 0xf6, 0x51, 0x60, 0x0e, 0x72, 0x01, 0xe4, 0x6b, 0xc6, 0x03, 0x62, - 0xa3, 0x8d, 0x76, 0x5c, 0xce, 0x93, 0x85, 0xd3, 0x49, 0x64, 0x9b, 0x52, - 0x71, 0xac, 0x25, 0x46, 0x61, 0xb4, 0xa8, 0x40, 0x67, 0x64, 0x5d, 0xb2, - 0xd8, 0x97, 0x3d, 0x95, 0xdb, 0x73, 0xf7, 0xe5, 0xbe, 0x80, 0xbd, 0xe6, - 0x32, 0xad, 0x11, 0x42, 0x6a, 0xda, 0x2e, 0xc3, 0x41, 0xbf, 0x90, 0xf0, - 0x55, 0xa3, 0xbc, 0x1c, 0x56, 0x9d, 0x02, 0xaf, 0x17, 0x0b, 0x9e, 0x10, - 0x91, 0x19, 0xdd, 0x05, 0x04, 0x11, 0xd1, 0x64, 0x96, 0x73, 0xcd, 0xe8, - 0xd6, 0xfe, 0xbf, 0x46, 0xb7, 0xad, 0x92, 0xb5, 0x95, 0xad, 0xf9, 0x9c, - 0xfb, 0x64, 0xaa, 0xe2, 0xaa, 0xaa, 0x54, 0xb9, 0x40, 0x1b, 0x8c, 0x4d, - 0x15, 0xb7, 0x8d, 0x77, 0x2d, 0x09, 0xd2, 0x53, 0x6e, 0x96, 0x8e, 0xaf, - 0x7e, 0xd1, 0xfb, 0x75, 0x4d, 0xa8, 0xe2, 0x48, 0xad, 0x23, 0xe2, 0xc6, - 0x2f, 0x2c, 0xc3, 0x11, 0x9e, 0x18, 0xa9, 0x58, 0x40, 0x65, 0x61, 0xb6, - 0x57, 0x73, 0x79, 0xa7, 0x0d, 0x63, 0x2f, 0xf5, 0x78, 0xa6, 0xad, 0x59, - 0xc5, 0xd6, 0x05, 0x48, 0xe4, 0xf1, 0x31, 0x4c, 0xca, 0x1b, 0xf1, 0x19, - 0x42, 0x35, 0x2a, 0xe6, 0x40, 0x5e, 0x39, 0x49, 0x38, 0x95, 0xd7, 0x48, - 0x0d, 0x2b, 0x5a, 0x29, 0x8a, 0xaa, 0xeb, 0x80, 0xff, 0xf1, 0x50, 0x80, - 0x2f, 0x7f, 0xfc, 0x21, 0x1a, 0xca, 0xfd, 0xc8, 0xdb, 0x80, 0x00, 0xa6, - 0xb8, 0x20, 0xdc, 0x2a, 0xd6, 0x09, 0x05, 0x90, 0xd6, 0xf8, 0xab, 0xe3, - 0xbf, 0x6e, 0x5c, 0xb5, 0xc7, 0x8e, 0xa5, 0x78, 0xf2, 0xf0, 0xf8, 0x9e, - 0x3f, 0x1e, 0xa7, 0x3f, 0x4e, 0x3d, 0xfd, 0xbc, 0x79, 0x4c, 0x74, 0x1d, - 0xff, 0xb6, 0xb1, 0xc1, 0xdb, 0xf8, 0xbb, 0x8e, 0x37, 0x75, 0xa3, 0x7d, - 0x2d, 0xbe, 0x99, 0xad, 0x9f, 0x77, 0x9f, 0x14, 0xc9, 0xa8, 0x3c, 0x72, - 0x11, 0xc1, 0x95, 0x78, 0x33, 0x5a, 0x2c, 0x9c, 0x4e, 0x54, 0x38, 0x5a, - 0x14, 0x27, 0x86, 0x01, 0x52, 0xd5, 0xc6, 0x87, 0x38, 0x05, 0xa0, 0x24, - 0x68, 0x07, 0x61, 0x1c, 0x7c, 0x9c, 0xc6, 0xa5, 0xa6, 0xb8, 0x8a, 0xef, - 0x83, 0xab, 0xa5, 0x77, 0x21, 0x7f, 0x74, 0x17, 0x34, 0xbe, 0xf8, 0xbb, - 0x22, 0xd6, 0x48, 0xb8, 0x1c, 0xb8, 0x40, 0x0e, 0xac, 0x5c, 0x30, 0x9d, - 0xb1, 0xd4, 0xb9, 0xad, 0xb3, 0x7a, 0x8a, 0x52, 0x51, 0x69, 0x9a, 0xdc, - 0xa4, 0x99, 0xc5, 0x09, 0x1c, 0xfe, 0x23, 0x23, 0x4f, 0x9f, 0x43, 0x18, - 0xeb, 0xd5, 0x4d, 0xd2, 0xb1, 0x13, 0x32, 0xcf, 0x7f, 0x54, 0xf2, 0xde, - 0x44, 0xda, 0x39, 0x1a, 0x9d, 0x4f, 0x6e, 0x52, 0x1a, 0xe4, 0x60, 0x2c, - 0xb3, 0x9d, 0x62, 0xb8, 0xaf, 0x56, 0x2c, 0xec, 0xb5, 0x55, 0xc6, 0x0a, - 0x52, 0x01, 0x34, 0xbe, 0xd3, 0x53, 0x24, 0x43, 0x33, 0x10, 0x38, 0x44, - 0xb2, 0x6a, 0x92, 0x79, 0x8e, 0x85, 0x7b, 0xea, 0x35, 0xb3, 0xa4, 0x4f, - 0x7e, 0x62, 0xc1, 0x11, 0x08, 0x33, 0x18, 0x86, 0x58, 0xd6, 0x75, 0x56, - 0x27, 0x40, 0x51, 0x54, 0x03, 0xe5, 0x02, 0x8a, 0x66, 0xb6, 0x35, 0xf2, - 0xc8, 0x0a, 0x24, 0x88, 0xa5, 0x74, 0x4e, 0x53, 0x60, 0x08, 0x47, 0xf1, - 0x97, 0x86, 0x6d, 0xce, 0xd0, 0x22, 0x96, 0x6f, 0x35, 0x74, 0x76, 0x44, - 0x2c, 0x0e, 0xc2, 0xef, 0xf7, 0x89, 0x57, 0xc7, 0x7e, 0xde, 0xfc, 0x72, - 0x25, 0x4a, 0x79, 0xe7, 0x1a, 0xf3, 0xe3, 0xf1, 0xcc, 0x57, 0x9d, 0xd2, - 0xaf, 0x2e, 0xea, 0xc4, 0xdf, 0x8f, 0x84, 0x91, 0xbf, 0xe0, 0xf5, 0x59, - 0x8d, 0xca, 0xbc, 0xa5, 0x2c, 0x01, 0x3b, 0xf6, 0xcc, 0x57, 0x75, 0x8c, - 0xa0, 0xe1, 0x58, 0x8c, 0x1f, 0xfe, 0x6d, 0x2e, 0xd7, 0x2d, 0x78, 0xdb, - 0x78, 0x57, 0x57, 0xde, 0xc1, 0xc4, 0x9c, 0xaa, 0xaa, 0x1a, 0xb9, 0xce, - 0x5b, 0x61, 0xab, 0xfd, 0x1b, 0x86, 0x29, 0xeb, 0x18, 0x6e, 0xa8, 0xa4, - 0x8a, 0xed, 0x49, 0x92, 0x66, 0xbb, 0x60, 0x11, 0x2c, 0x28, 0x4a, 0xa7, - 0x57, 0x6b, 0x29, 0x8c, 0xb2, 0x46, 0xa5, 0x00, 0x4e, 0xbc, 0x67, 0x13, - 0x8c, 0x6f, 0x07, 0xff, 0xf1, 0x50, 0x80, 0x2e, 0x5f, 0xfc, 0x21, 0x1a, - 0xcf, 0xfc, 0x81, 0xec, 0x37, 0xbf, 0xa4, 0xb6, 0xb1, 0x95, 0x88, 0x46, - 0x08, 0x85, 0x8c, 0x1a, 0x4e, 0x7d, 0xb2, 0xb7, 0x5c, 0x6b, 0xd7, 0xc2, - 0xbd, 0x7b, 0x4f, 0x4f, 0x3d, 0x7b, 0xfd, 0x78, 0xbd, 0xfb, 0x26, 0x7e, - 0x3f, 0x8e, 0x38, 0xaa, 0x70, 0x2e, 0x32, 0x23, 0x6c, 0xb4, 0x8a, 0x9d, - 0xea, 0x1e, 0xfa, 0x33, 0x6e, 0xb8, 0x96, 0x56, 0xd8, 0x53, 0xb6, 0x13, - 0xb5, 0x29, 0xd2, 0x47, 0x56, 0x94, 0x8e, 0x8e, 0x69, 0x60, 0xda, 0x9a, - 0x2a, 0xaf, 0xb1, 0xc1, 0xde, 0x9c, 0x0a, 0x84, 0x3c, 0xa2, 0x3d, 0xec, - 0x20, 0xd3, 0xca, 0x02, 0xc6, 0xae, 0x48, 0x45, 0x28, 0x8d, 0x7e, 0xe0, - 0xac, 0xdd, 0x55, 0xb5, 0x63, 0x23, 0xf5, 0x1a, 0x07, 0x7f, 0xb0, 0x5a, - 0x48, 0xb9, 0xbc, 0x13, 0xaa, 0x47, 0x5b, 0x59, 0xd7, 0x0d, 0x1f, 0x57, - 0x20, 0x0d, 0x73, 0x8e, 0xaa, 0x9a, 0x8e, 0xba, 0x8c, 0xa5, 0x76, 0x2f, - 0xa4, 0x15, 0x9c, 0x17, 0x7b, 0x88, 0x54, 0x6e, 0xc0, 0x1d, 0x5e, 0x32, - 0x45, 0x1d, 0x10, 0xaa, 0x1a, 0x67, 0x5c, 0x90, 0x3c, 0x60, 0xa6, 0xfd, - 0x10, 0xaa, 0x5d, 0xa6, 0x14, 0xdf, 0xf1, 0xf5, 0x73, 0xd0, 0x1c, 0x70, - 0x36, 0x6a, 0x52, 0x81, 0x06, 0xcb, 0x3e, 0x78, 0xf5, 0xe5, 0xe3, 0xd1, - 0xbf, 0x3b, 0x23, 0xf8, 0xb3, 0x66, 0x98, 0x3c, 0x5f, 0xf7, 0x0b, 0xe9, - 0xd5, 0x52, 0x03, 0xaa, 0x94, 0x04, 0x0a, 0xc4, 0x60, 0xd5, 0xc4, 0x22, - 0xe2, 0x7a, 0x57, 0x89, 0x40, 0xd7, 0xc4, 0xab, 0x40, 0xb9, 0xd9, 0x00, - 0xe0, 0xa7, 0xbc, 0xce, 0xfb, 0x4d, 0x95, 0x2b, 0xac, 0x60, 0x4e, 0x64, - 0x03, 0x5b, 0x02, 0x62, 0xba, 0xa2, 0x80, 0xae, 0x2c, 0x07, 0x00, 0x10, - 0x0f, 0x3c, 0x8f, 0x6c, 0xe9, 0x93, 0x6f, 0xd8, 0x50, 0x01, 0x48, 0xcc, - 0xb0, 0xb1, 0x9c, 0x2a, 0x8e, 0x78, 0x69, 0x39, 0xf6, 0xf9, 0xae, 0x39, - 0xbb, 0xe7, 0x57, 0xd7, 0xaf, 0x69, 0xf9, 0xe3, 0xcd, 0xfc, 0xff, 0x9e, - 0xe7, 0x1b, 0xe3, 0x73, 0x5c, 0xc4, 0x40, 0x21, 0xdf, 0xe7, 0x59, 0xc2, - 0x1c, 0xc2, 0x68, 0x33, 0x0b, 0x1f, 0x71, 0x94, 0x9b, 0xb8, 0x80, 0x4a, - 0x89, 0x45, 0xa8, 0x4c, 0xef, 0x58, 0xc5, 0x18, 0x5c, 0xfd, 0x9f, 0x4a, - 0x01, 0x29, 0x00, 0x32, 0x23, 0x35, 0xb1, 0x1a, 0x12, 0xdd, 0xdf, 0x06, - 0xe0, 0x57, 0x5f, 0x6f, 0x2a, 0xd7, 0x4d, 0x45, 0xf2, 0x17, 0xfd, 0x64, - 0x04, 0xfa, 0x01, 0xbb, 0xc1, 0x58, 0xd2, 0xe1, 0x17, 0xa6, 0xea, 0x41, - 0xd7, 0xc0, 0x03, 0xb3, 0xdb, 0x99, 0x97, 0x2a, 0xe8, 0xe5, 0xb6, 0x2b, - 0x80, 0xff, 0xf1, 0x50, 0x80, 0x2c, 0x9f, 0xfc, 0x21, 0x1a, 0xce, 0x34, - 0x41, 0xe5, 0x10, 0x00, 0xac, 0xb1, 0x51, 0x9a, 0x08, 0xf6, 0x21, 0x7e, - 0x61, 0xbf, 0x33, 0x79, 0xef, 0xfb, 0xce, 0xfc, 0xe5, 0x66, 0x99, 0x9c, - 0x41, 0xd7, 0x89, 0xac, 0xe2, 0x73, 0x6e, 0xb9, 0xb8, 0x1b, 0xee, 0xef, - 0x12, 0xff, 0x15, 0x0c, 0x59, 0x88, 0x5b, 0x02, 0xf4, 0x60, 0xb0, 0xbe, - 0xe6, 0x95, 0xc5, 0x4b, 0xec, 0x81, 0x28, 0x2f, 0x7b, 0xb0, 0xf0, 0xa0, - 0x86, 0xaa, 0xe0, 0xeb, 0x6e, 0xeb, 0xdc, 0x01, 0x73, 0x92, 0x7c, 0xc9, - 0x5e, 0x85, 0x43, 0xb9, 0x44, 0xab, 0x07, 0x00, 0x20, 0x25, 0x32, 0x12, - 0x03, 0x10, 0x32, 0x3b, 0xde, 0x58, 0x67, 0x61, 0x45, 0xbb, 0x43, 0x98, - 0xc0, 0x24, 0xbc, 0xa4, 0x77, 0x52, 0xf4, 0x4a, 0x4e, 0x7d, 0x87, 0xcc, - 0xfe, 0x58, 0x96, 0x31, 0xe7, 0xe0, 0xf6, 0xe2, 0x22, 0x3e, 0xf2, 0x31, - 0x0b, 0xa4, 0xf5, 0xf4, 0xf0, 0x33, 0x04, 0x74, 0x7e, 0x41, 0x09, 0x24, - 0x94, 0xe5, 0x94, 0x9f, 0x77, 0xfc, 0x49, 0xfc, 0xc4, 0x4b, 0xcb, 0x11, - 0xcf, 0xa0, 0x4b, 0x9c, 0x8e, 0x95, 0xee, 0x1e, 0xf3, 0x16, 0xae, 0x01, - 0xe0, 0x04, 0x43, 0xb6, 0x80, 0x30, 0xa2, 0x81, 0xf8, 0xa0, 0xd7, 0xff, - 0x0f, 0x30, 0x7e, 0x60, 0xe0, 0xcb, 0xbf, 0x00, 0xc0, 0x33, 0xbb, 0xa8, - 0xa1, 0xcc, 0x58, 0x09, 0x0d, 0xa4, 0x52, 0x37, 0xd1, 0xe3, 0x31, 0x1a, - 0x4c, 0x61, 0x6c, 0x33, 0x9f, 0x3b, 0x0f, 0x56, 0xb9, 0x2f, 0x71, 0x81, - 0x15, 0x6b, 0xf3, 0x84, 0xb1, 0xca, 0x04, 0x0c, 0x98, 0x15, 0xe1, 0x49, - 0x4e, 0x7c, 0xc2, 0x56, 0x14, 0xb5, 0x16, 0xdd, 0x02, 0x00, 0x16, 0x0d, - 0xab, 0x25, 0x96, 0xc8, 0xba, 0x40, 0x22, 0xb1, 0x8f, 0x44, 0x71, 0x32, - 0x14, 0xfe, 0xf1, 0xd7, 0x54, 0xef, 0xe3, 0x7b, 0xce, 0x7e, 0xa7, 0xdb, - 0xee, 0xbc, 0xd3, 0xe7, 0xe2, 0x79, 0xef, 0xcf, 0x3c, 0x6d, 0x24, 0xbd, - 0x9a, 0xaa, 0xa0, 0x06, 0x61, 0xd3, 0x75, 0x7d, 0x63, 0xc1, 0xc5, 0x04, - 0x4d, 0xe0, 0xb3, 0xe6, 0x90, 0x22, 0x24, 0xcb, 0x4f, 0x49, 0x7e, 0xbf, - 0x07, 0x60, 0xc8, 0x07, 0x4c, 0x5f, 0xb7, 0xd6, 0x80, 0x94, 0xac, 0x1b, - 0xd2, 0x41, 0x78, 0x28, 0xa3, 0x3c, 0xdf, 0x0a, 0x0c, 0x00, 0x8e, 0xc9, - 0xd4, 0x33, 0xd3, 0xa5, 0xb7, 0x6a, 0xfd, 0x40, 0xc0, 0xa2, 0x50, 0xd9, - 0xa8, 0xaa, 0x95, 0x6f, 0xe5, 0x55, 0xc9, 0x52, 0x65, 0x8b, 0xe4, 0x24, - 0xb5, 0xeb, 0x87, 0x5e, 0x11, 0xbe, 0x44, 0x62, 0x38, 0xff, 0xf1, 0x50, - 0x80, 0x2e, 0x3f, 0xfc, 0x21, 0x1a, 0xce, 0xfb, 0x25, 0xef, 0x85, 0x90, - 0xa9, 0xb1, 0x44, 0x14, 0xa9, 0x43, 0xcc, 0xeb, 0x97, 0xbf, 0xe2, 0xf1, - 0xf6, 0xfd, 0x67, 0x3a, 0xe3, 0x7d, 0xf5, 0x55, 0x69, 0x53, 0x23, 0x5c, - 0xc3, 0x8c, 0x32, 0xc2, 0x13, 0x92, 0x33, 0xb1, 0x78, 0x50, 0x76, 0x8b, - 0xd4, 0xf6, 0xcd, 0xe6, 0xe8, 0xed, 0x11, 0xd5, 0x58, 0x79, 0x7b, 0x34, - 0xb5, 0x34, 0x25, 0x4e, 0x49, 0xa7, 0x2b, 0x8a, 0xfb, 0x8d, 0xd4, 0x13, - 0xe6, 0xe3, 0xa4, 0x27, 0x89, 0x0e, 0xfb, 0x0b, 0x73, 0xac, 0xad, 0x1c, - 0xa6, 0x4e, 0x29, 0x21, 0x4c, 0x75, 0x43, 0x9f, 0x9c, 0x85, 0xce, 0x6e, - 0xaa, 0xe9, 0x13, 0xc1, 0x9f, 0x7f, 0x4a, 0xba, 0x31, 0x2c, 0xff, 0xd3, - 0xb5, 0x8a, 0x08, 0xac, 0xba, 0xd4, 0x50, 0x05, 0x39, 0xec, 0x52, 0x29, - 0x7d, 0xdd, 0x8c, 0x81, 0xa1, 0xfb, 0xf1, 0x69, 0xe1, 0x0e, 0xc3, 0xc5, - 0xd1, 0x3a, 0x59, 0xe1, 0xea, 0x9d, 0x61, 0x00, 0x33, 0x02, 0x15, 0xf1, - 0xe8, 0xbe, 0x79, 0x0b, 0xd6, 0xce, 0x7a, 0x94, 0x9f, 0x38, 0x5d, 0x1f, - 0x3b, 0x5a, 0x26, 0x3a, 0x64, 0x14, 0x5a, 0x00, 0xed, 0x82, 0x85, 0x16, - 0x10, 0x77, 0xc2, 0x49, 0x4d, 0x04, 0x6a, 0xab, 0x47, 0xe2, 0x04, 0x5f, - 0x12, 0x0d, 0xb3, 0x7b, 0x99, 0x4d, 0x8d, 0x40, 0xa4, 0x9e, 0x9f, 0xbd, - 0x77, 0xc9, 0xcb, 0xc2, 0x52, 0xf2, 0x09, 0x0a, 0x06, 0xd5, 0x3b, 0xaf, - 0x25, 0xb7, 0xcb, 0x27, 0x21, 0x8d, 0xba, 0xe4, 0xfa, 0x63, 0x9f, 0x94, - 0x67, 0xd6, 0x17, 0xcf, 0x94, 0x93, 0x00, 0xe2, 0xd7, 0xd3, 0x4f, 0x58, - 0x66, 0x9d, 0xb7, 0x62, 0x12, 0x5c, 0xa6, 0x64, 0xf2, 0xc7, 0xac, 0xf3, - 0xa4, 0x00, 0x9b, 0xbd, 0xa9, 0x86, 0x51, 0x15, 0x08, 0x4f, 0x79, 0x3c, - 0xf5, 0xcb, 0xdf, 0xef, 0xac, 0xe7, 0xd7, 0xef, 0x5f, 0xa7, 0xef, 0x57, - 0xdf, 0x55, 0xe3, 0xd9, 0x2a, 0x6e, 0x6f, 0x5b, 0xd5, 0x35, 0xb9, 0xac, - 0x98, 0x16, 0xa6, 0x81, 0x99, 0x09, 0x44, 0x16, 0x43, 0x8a, 0x73, 0x93, - 0xb6, 0xd3, 0x9f, 0x05, 0xbc, 0x04, 0xa2, 0xf0, 0x8a, 0xb4, 0xe3, 0x89, - 0x0d, 0xf9, 0xc2, 0x23, 0x12, 0x09, 0x40, 0xae, 0x5c, 0x36, 0xa6, 0xe6, - 0x2d, 0xe3, 0xe5, 0x9e, 0xaa, 0xfc, 0xd7, 0x7d, 0x35, 0x3a, 0xdb, 0x1f, - 0x92, 0xcb, 0x4f, 0x47, 0x2e, 0x5c, 0xe9, 0xae, 0xbd, 0x8f, 0xd9, 0xfa, - 0xad, 0x93, 0x3a, 0x8f, 0xae, 0xc9, 0xad, 0xc1, 0xc8, 0xa8, 0x6c, 0x02, - 0xbd, 0x70, 0x4e, 0x73, 0xaa, 0xec, 0x27, 0xe7, 0x35, 0x32, 0x96, 0x4e, - 0xbf, 0xb2, 0xc5, 0xca, 0x3e, 0x5c, 0xff, 0xf1, 0x50, 0x80, 0x30, 0x7f, - 0xfc, 0x21, 0x1a, 0xcc, 0x94, 0xc7, 0x6a, 0x20, 0x10, 0xa8, 0xb1, 0x44, - 0x5c, 0x89, 0x46, 0x7c, 0x35, 0x9a, 0xfd, 0x3f, 0x79, 0x3b, 0xf7, 0xfd, - 0xfe, 0x3f, 0x33, 0xe3, 0x9f, 0x7f, 0x6a, 0xcd, 0x56, 0xb9, 0xba, 0x97, - 0xb2, 0xa2, 0x27, 0x1d, 0xe8, 0x39, 0xfa, 0x6b, 0x21, 0x07, 0x39, 0xf1, - 0x59, 0x68, 0xae, 0x2a, 0x6c, 0x38, 0xed, 0x9e, 0xd7, 0x61, 0xee, 0xb9, - 0xd0, 0x53, 0x6a, 0x30, 0xc0, 0x01, 0x25, 0xf1, 0x01, 0xce, 0xd9, 0xae, - 0x3b, 0xca, 0x75, 0x19, 0xd1, 0x5e, 0xb1, 0x89, 0x7c, 0x4e, 0x4a, 0xb0, - 0xc3, 0x71, 0x57, 0x81, 0x52, 0x50, 0x94, 0x92, 0x27, 0x9f, 0x87, 0x88, - 0x6e, 0xc2, 0x79, 0x38, 0x5e, 0x86, 0x51, 0x18, 0x0e, 0x57, 0x15, 0x95, - 0x63, 0x08, 0xa1, 0xd7, 0xd1, 0x98, 0xdf, 0xcb, 0xed, 0xb9, 0x95, 0xb2, - 0xac, 0x40, 0x34, 0x30, 0xf7, 0x7e, 0x0c, 0x6a, 0xe5, 0x22, 0x80, 0x78, - 0x52, 0x18, 0x5b, 0x6f, 0xbb, 0xd4, 0xec, 0x60, 0xc0, 0x99, 0x52, 0xee, - 0x01, 0xf4, 0x83, 0x31, 0x61, 0xd5, 0xb1, 0x8e, 0x14, 0x75, 0x0c, 0x1f, - 0xc2, 0x03, 0xa6, 0x52, 0x8e, 0x2f, 0x05, 0xa5, 0x88, 0xf1, 0x9b, 0xf3, - 0xa3, 0xd3, 0xcb, 0x27, 0x89, 0x2e, 0x87, 0xef, 0x95, 0xc8, 0x26, 0x0a, - 0xa4, 0x85, 0x52, 0xba, 0x1a, 0xaf, 0x2f, 0xb2, 0xb3, 0x96, 0x55, 0x01, - 0xd9, 0xb0, 0xa2, 0x2c, 0x00, 0xcb, 0x07, 0xfd, 0x3a, 0x90, 0xc5, 0x7c, - 0x2a, 0xba, 0xa9, 0x3d, 0x0e, 0x52, 0xac, 0xd9, 0xba, 0xf1, 0x73, 0x60, - 0xf8, 0x3a, 0x49, 0x3a, 0x5c, 0x03, 0x53, 0xe5, 0x9f, 0xd5, 0xa6, 0xcf, - 0xfe, 0x8f, 0x1b, 0xb1, 0x47, 0xd9, 0xa7, 0x15, 0x22, 0xaf, 0xac, 0xa6, - 0xa9, 0xd4, 0xa1, 0x7e, 0xe6, 0x45, 0x20, 0xf9, 0xeb, 0xd4, 0x5a, 0xe4, - 0xac, 0x37, 0x22, 0x0d, 0x45, 0xef, 0x27, 0x5e, 0x7c, 0x71, 0xf6, 0xfc, - 0x73, 0x75, 0xef, 0xf8, 0xbf, 0xdb, 0xf1, 0xf1, 0xcf, 0x8e, 0xab, 0xdf, - 0xea, 0x4c, 0xf6, 0xf5, 0xc5, 0xf3, 0x57, 0xbd, 0x25, 0x6a, 0xf2, 0x03, - 0x50, 0x64, 0xe3, 0x71, 0x5d, 0xf8, 0x41, 0x06, 0x72, 0xbd, 0x36, 0x9f, - 0x40, 0x52, 0x7e, 0x56, 0x38, 0x6b, 0x37, 0xc3, 0xd8, 0xcd, 0xe6, 0xdc, - 0x35, 0x65, 0x57, 0x2d, 0x6e, 0xfb, 0x46, 0xf1, 0xc3, 0xa3, 0xef, 0x7f, - 0x0f, 0x68, 0x96, 0x01, 0xd4, 0x79, 0x04, 0xd2, 0xc5, 0x98, 0x1b, 0xea, - 0xfb, 0x4b, 0xab, 0x63, 0x71, 0x17, 0x85, 0x43, 0x8f, 0x08, 0xaf, 0x5b, - 0xab, 0x24, 0xee, 0x10, 0xff, 0xca, 0xe7, 0x78, 0x8e, 0xd8, 0xd4, 0x47, - 0xc2, 0xf0, 0xba, 0xe3, 0x44, 0x41, 0xad, 0xa4, 0xfe, 0x76, 0x84, 0xa2, - 0x7a, 0xf5, 0x5c, 0x65, 0x1b, 0x3e, 0x51, 0x1f, 0x0e, 0xff, 0xf1, 0x50, - 0x80, 0x2f, 0xff, 0xfc, 0x21, 0x1a, 0xce, 0xfc, 0xb1, 0xec, 0x34, 0xff, - 0xa7, 0xb4, 0x42, 0x90, 0x8e, 0x14, 0x1a, 0x85, 0x10, 0xc1, 0x43, 0x9c, - 0x3a, 0xad, 0x7e, 0x7e, 0xad, 0x9e, 0xff, 0xcd, 0x77, 0xaf, 0x8f, 0x1d, - 0xfb, 0x73, 0xbe, 0x14, 0x97, 0xb2, 0x85, 0xd2, 0xd8, 0x14, 0x7b, 0x70, - 0x90, 0x0f, 0xb1, 0xef, 0xf0, 0x22, 0xe9, 0x6a, 0xb1, 0x45, 0x6e, 0x18, - 0xa8, 0xb4, 0xc3, 0xc9, 0x1b, 0x82, 0x1b, 0x89, 0x60, 0x11, 0x18, 0x46, - 0xcf, 0xd0, 0xb8, 0xdc, 0xce, 0x30, 0xd3, 0xa6, 0x9a, 0x07, 0x4e, 0xce, - 0xcc, 0x98, 0xe7, 0x5b, 0xf1, 0x5c, 0xc6, 0xf2, 0x89, 0x86, 0xf7, 0x47, - 0x1b, 0xdb, 0x48, 0xd2, 0xb2, 0xf9, 0x1f, 0xb8, 0x39, 0xaf, 0x84, 0x63, - 0x18, 0x7b, 0x77, 0x59, 0x28, 0xa6, 0x1b, 0x3d, 0x4d, 0xfe, 0x9a, 0xc8, - 0xaf, 0x40, 0xf2, 0xca, 0xe7, 0xb1, 0xa1, 0xbf, 0x3c, 0x38, 0xc5, 0xcc, - 0x4e, 0x85, 0xb2, 0xd6, 0xd8, 0x59, 0xfa, 0xcd, 0xbc, 0xef, 0xf7, 0x4a, - 0xc3, 0x94, 0x73, 0xb1, 0x8a, 0xbd, 0x71, 0x56, 0xf0, 0x1d, 0x24, 0x58, - 0x9b, 0x08, 0xf7, 0xfb, 0xbd, 0x23, 0xc6, 0x79, 0x98, 0x8f, 0x74, 0x04, - 0x48, 0x5b, 0x3e, 0x6f, 0x13, 0xe8, 0x71, 0x29, 0x5b, 0x57, 0x82, 0xd7, - 0x19, 0x42, 0x5a, 0x7b, 0x80, 0xf2, 0xf1, 0x10, 0xde, 0x88, 0x99, 0x33, - 0xd0, 0x02, 0xaf, 0x00, 0x9b, 0x00, 0xb8, 0x22, 0x55, 0xb1, 0x29, 0x85, - 0xc8, 0x91, 0xb7, 0xdf, 0x03, 0xfb, 0xa7, 0x70, 0x6d, 0x5f, 0x5b, 0xdf, - 0xc8, 0x47, 0x91, 0x60, 0xf5, 0xe4, 0x15, 0x5a, 0xd3, 0x81, 0x06, 0x0f, - 0xc3, 0x7e, 0x1a, 0x5a, 0x0b, 0xd5, 0xa9, 0xaa, 0x78, 0x87, 0x9c, 0xed, - 0x22, 0xf2, 0x02, 0x3d, 0x02, 0xbb, 0xa4, 0x77, 0xac, 0x17, 0xc7, 0x36, - 0x7b, 0x74, 0xf3, 0xe5, 0x4f, 0x10, 0x63, 0x38, 0x50, 0x66, 0x16, 0x0f, - 0x3c, 0xf3, 0x9d, 0x56, 0xbf, 0x3f, 0x5b, 0x97, 0xf3, 0xfe, 0x2b, 0xf8, - 0xfa, 0xbd, 0x77, 0xed, 0xcf, 0xdb, 0xf0, 0xd6, 0x7c, 0x76, 0xa4, 0x2a, - 0xea, 0xee, 0xb0, 0x16, 0xd8, 0x9a, 0xa7, 0xa6, 0x45, 0x11, 0x9d, 0x47, - 0x30, 0x60, 0xb9, 0x4d, 0xc7, 0xef, 0x49, 0x4b, 0x79, 0xf7, 0x39, 0x83, - 0xb8, 0x3b, 0x43, 0x14, 0xab, 0xc0, 0x73, 0x9a, 0x43, 0x4b, 0x6c, 0x27, - 0x25, 0xdf, 0x81, 0x4e, 0xf9, 0x10, 0x75, 0x9c, 0x91, 0xe0, 0x13, 0x3f, - 0xc9, 0x80, 0x12, 0xca, 0xc3, 0x82, 0xad, 0xb6, 0x19, 0xe6, 0x6b, 0x93, - 0xa3, 0xbf, 0x90, 0x0c, 0xbd, 0xa2, 0x82, 0x05, 0xa7, 0xeb, 0xe8, 0xde, - 0xf5, 0x06, 0xda, 0x5e, 0xf0, 0x4d, 0x0a, 0x7f, 0xe0, 0xd4, 0x13, 0x61, - 0xb2, 0x12, 0x48, 0x71, 0xa4, 0x6b, 0xbb, 0x80, 0xff, 0xf1, 0x50, 0x80, - 0x30, 0x1f, 0xfc, 0x21, 0x1a, 0xcf, 0xc4, 0xc4, 0xa8, 0x0c, 0x16, 0xa8, - 0xb1, 0xc2, 0x99, 0x49, 0x43, 0xcf, 0x4c, 0xfb, 0xff, 0xd3, 0xf7, 0xb9, - 0xe3, 0xc7, 0xf3, 0x3c, 0x4f, 0x6c, 0xf7, 0xf3, 0x95, 0x75, 0x2a, 0x5d, - 0x6d, 0x28, 0x09, 0x56, 0x21, 0x32, 0x57, 0xf7, 0x9e, 0xf3, 0x71, 0xe2, - 0x73, 0xed, 0x64, 0x4e, 0xcb, 0xc5, 0x0e, 0xf6, 0xaf, 0xc6, 0x11, 0xde, - 0x94, 0xb3, 0x99, 0x41, 0xf8, 0x46, 0x11, 0x68, 0xdd, 0x3c, 0x9c, 0xc6, - 0x88, 0xf8, 0x05, 0x7b, 0xf4, 0x20, 0x6f, 0xd2, 0x32, 0xb4, 0x45, 0x39, - 0x38, 0x1b, 0xd7, 0xe5, 0x3b, 0xd4, 0x81, 0x89, 0x15, 0xd7, 0x6c, 0x80, - 0x68, 0x22, 0x47, 0x1b, 0x6f, 0x65, 0x52, 0x30, 0xcc, 0x7a, 0x4c, 0x69, - 0xeb, 0x1f, 0x84, 0x46, 0x0a, 0x5f, 0x2e, 0x44, 0x32, 0x20, 0x2f, 0x3e, - 0x13, 0xbe, 0x0c, 0x3e, 0xa2, 0xdc, 0xeb, 0x94, 0xc8, 0x53, 0xbb, 0x64, - 0xee, 0x18, 0xf5, 0x37, 0x7d, 0xff, 0x94, 0x6c, 0x78, 0xb1, 0x38, 0x7f, - 0x21, 0x0c, 0xfb, 0x17, 0x14, 0xa8, 0x8f, 0xc7, 0x67, 0xba, 0xc7, 0x06, - 0x25, 0x1c, 0x3f, 0x01, 0x3d, 0x0d, 0x3a, 0x33, 0x29, 0x47, 0x9a, 0x05, - 0xe6, 0xd5, 0x6a, 0x3d, 0x20, 0xf6, 0x7d, 0x87, 0x43, 0xff, 0x43, 0xa9, - 0x00, 0xf9, 0x17, 0x93, 0xd8, 0x5d, 0xa7, 0x43, 0xba, 0x09, 0xfd, 0xae, - 0xbf, 0x80, 0x98, 0x99, 0x25, 0xcf, 0x52, 0x77, 0x7a, 0x6f, 0x3b, 0xa3, - 0xbb, 0x84, 0xef, 0xae, 0xb7, 0x1a, 0xed, 0x33, 0xcf, 0xb3, 0xb7, 0x41, - 0x6e, 0x83, 0x81, 0xb3, 0xa7, 0xb5, 0x55, 0xd7, 0xb7, 0xfb, 0x8e, 0x78, - 0x0d, 0x4b, 0x78, 0xb7, 0x8a, 0xdb, 0x34, 0x8d, 0xe6, 0xdc, 0x35, 0x1a, - 0xe7, 0x64, 0xc2, 0xc2, 0x8f, 0x86, 0xa3, 0xbc, 0x86, 0x50, 0xea, 0x8a, - 0x5b, 0xb9, 0x87, 0xef, 0x3c, 0xf4, 0xdf, 0x5f, 0xb7, 0xe3, 0x15, 0xe3, - 0xee, 0xf1, 0xe6, 0x39, 0xe1, 0xeb, 0xe3, 0x5a, 0xae, 0x32, 0xb2, 0x4a, - 0xc9, 0x13, 0x38, 0xdf, 0x01, 0x12, 0x0d, 0x2f, 0x20, 0x01, 0x9e, 0x10, - 0x39, 0x74, 0x99, 0x06, 0x6d, 0x9e, 0x8a, 0xb8, 0xf4, 0xcf, 0xa9, 0x57, - 0x3c, 0x44, 0x6e, 0x83, 0xd6, 0xc3, 0x0d, 0x4e, 0xa3, 0x08, 0xcb, 0x4b, - 0xcb, 0xee, 0x73, 0xd0, 0x4f, 0x13, 0x24, 0x75, 0x3a, 0x91, 0x3a, 0x7b, - 0x27, 0x0e, 0x24, 0xed, 0xcb, 0xe1, 0xe7, 0x03, 0x3d, 0x5e, 0x4f, 0x2f, - 0x2c, 0x31, 0x4f, 0x2e, 0x14, 0xaf, 0x65, 0x34, 0x94, 0x22, 0xef, 0x14, - 0xeb, 0x59, 0xa0, 0xd4, 0xab, 0xd7, 0xdd, 0x3c, 0x2e, 0x86, 0x3c, 0x46, - 0x73, 0x26, 0xbd, 0x0d, 0xa0, 0x2e, 0xc3, 0x72, 0x81, 0xb2, 0x1c, 0xb2, - 0x40, 0x55, 0x57, 0x93, 0x2a, 0xc4, 0xe5, 0xe0, 0xff, 0xf1, 0x50, 0x80, - 0x2e, 0x1f, 0xfc, 0x21, 0x2a, 0xca, 0xf8, 0x80, 0xea, 0xb7, 0x10, 0xab, - 0x88, 0xa6, 0x8f, 0x3e, 0x63, 0x5f, 0xd7, 0xf1, 0xc7, 0x9f, 0xb7, 0x3f, - 0xcb, 0xde, 0x71, 0xbe, 0x75, 0x79, 0x79, 0xc3, 0x5d, 0xb0, 0x12, 0xa2, - 0xa0, 0x13, 0xca, 0xff, 0x22, 0x34, 0x73, 0xec, 0xe8, 0x68, 0xf8, 0xe1, - 0xb8, 0xa5, 0xca, 0x95, 0xdb, 0x73, 0x8b, 0xc2, 0xb4, 0xe6, 0xbc, 0x42, - 0x0c, 0x6e, 0x23, 0x20, 0x6e, 0x6a, 0xd1, 0x66, 0x4a, 0x64, 0xcb, 0x98, - 0x92, 0xea, 0xca, 0x39, 0x93, 0x8a, 0xe1, 0x7a, 0x53, 0xe6, 0xb5, 0x83, - 0xf4, 0x5f, 0xf8, 0xdd, 0x31, 0x89, 0x1b, 0xf8, 0x55, 0xbf, 0x1a, 0xa1, - 0xc3, 0x73, 0x30, 0x0a, 0x0d, 0x58, 0x79, 0xd1, 0xf2, 0x4f, 0x8d, 0x94, - 0xe5, 0x0d, 0xa6, 0x62, 0xda, 0x32, 0x09, 0xff, 0x2f, 0xfa, 0x71, 0x90, - 0xbe, 0xf7, 0xcd, 0xbe, 0xef, 0x96, 0xc2, 0x15, 0xfe, 0x51, 0xf3, 0x7b, - 0x57, 0xcb, 0x15, 0x41, 0xab, 0xbc, 0x4f, 0xa7, 0x07, 0xf0, 0x2a, 0x0d, - 0x07, 0xcb, 0xb1, 0x67, 0x88, 0x25, 0x53, 0x35, 0xab, 0x65, 0xee, 0xa1, - 0x09, 0xa3, 0xcc, 0x29, 0x8c, 0xcb, 0xaa, 0x09, 0x63, 0xcf, 0xbf, 0x18, - 0xba, 0x06, 0xc4, 0x16, 0xcb, 0xb9, 0xf7, 0x77, 0x7f, 0x20, 0x2e, 0xdd, - 0x67, 0xc4, 0xec, 0x00, 0xce, 0xef, 0x4e, 0x9a, 0xc1, 0xb8, 0x98, 0x1b, - 0x58, 0xdd, 0x44, 0x67, 0x92, 0xb5, 0x85, 0x04, 0x41, 0x22, 0xb8, 0x4d, - 0xab, 0xdb, 0x9a, 0x6b, 0x18, 0x98, 0xbd, 0xb4, 0x68, 0xbd, 0xee, 0x0b, - 0x03, 0x67, 0x2f, 0xec, 0x16, 0xd8, 0x13, 0xe4, 0x74, 0x33, 0x82, 0x65, - 0x36, 0x61, 0xbb, 0x37, 0x38, 0x49, 0x7a, 0xed, 0x46, 0x94, 0xd2, 0xc1, - 0xea, 0xd5, 0xc0, 0x5a, 0x4e, 0x44, 0x2f, 0x1d, 0x03, 0xe7, 0x35, 0xf5, - 0x1a, 0xfe, 0xbf, 0x75, 0x5f, 0xe9, 0xf8, 0x9e, 0xfe, 0xd2, 0xf3, 0x57, - 0xf3, 0xf5, 0xd6, 0xf4, 0xe6, 0x65, 0x49, 0x8d, 0x65, 0xc5, 0xe0, 0x0e, - 0x9a, 0x68, 0xfa, 0x4e, 0x3c, 0x74, 0x98, 0xc9, 0x2f, 0x07, 0xab, 0xdc, - 0x97, 0xea, 0x16, 0xe7, 0x51, 0x05, 0xce, 0x61, 0xe3, 0x46, 0xc8, 0x88, - 0x4b, 0x4b, 0x2f, 0x7b, 0xca, 0x23, 0xb8, 0x55, 0x6f, 0x9c, 0x05, 0xe0, - 0xce, 0xe1, 0x4d, 0xfd, 0xc5, 0x60, 0x61, 0x0c, 0xb1, 0x8b, 0x2e, 0x2e, - 0x0b, 0xed, 0x94, 0x16, 0x33, 0xdd, 0x8e, 0xd5, 0xfd, 0x98, 0xe7, 0xdd, - 0x78, 0x20, 0xfa, 0x1f, 0x37, 0xc3, 0x07, 0xda, 0x38, 0xc6, 0xca, 0x24, - 0x8d, 0xad, 0xfa, 0xe8, 0xd0, 0xc5, 0x94, 0xe4, 0x8c, 0x7f, 0x08, 0x96, - 0x77, 0xbf, 0xb4, 0xbe, 0xff, 0xf1, 0x50, 0x80, 0x34, 0x1f, 0xfc, 0x21, - 0x4c, 0x6c, 0xe0, 0x02, 0x04, 0xec, 0x01, 0x30, 0x4c, 0xd9, 0x8c, 0x96, - 0xd3, 0xd3, 0xb6, 0x92, 0xb1, 0x49, 0x1c, 0xb4, 0x39, 0x00, 0x38, 0xe4, - 0x2e, 0x6d, 0x34, 0xe7, 0x8b, 0xf1, 0xf1, 0xf5, 0xee, 0xae, 0x3f, 0x4b, - 0xbf, 0x3f, 0xaf, 0xe7, 0xfd, 0x3d, 0x78, 0x9c, 0xd8, 0x95, 0xc3, 0x6d, - 0xce, 0xf2, 0x97, 0x4e, 0x7a, 0x1b, 0xfb, 0xb1, 0x19, 0xd6, 0x7b, 0xbc, - 0x13, 0x06, 0x72, 0xa1, 0xee, 0x7c, 0x0d, 0x2f, 0xf5, 0x30, 0x94, 0x9c, - 0xb2, 0xbd, 0x00, 0x3b, 0x8a, 0xff, 0xa3, 0x83, 0xec, 0xb0, 0xce, 0xba, - 0x40, 0xf2, 0x2b, 0x6a, 0xe1, 0x53, 0xa5, 0xff, 0x17, 0x1b, 0xc3, 0x36, - 0xb7, 0x1a, 0x72, 0x67, 0xa7, 0xf6, 0x39, 0x41, 0xc0, 0xae, 0x57, 0x84, - 0x4e, 0x33, 0x16, 0xd7, 0xe0, 0xd4, 0x94, 0x4c, 0xba, 0x2c, 0xc8, 0x3a, - 0x54, 0x34, 0x44, 0x4e, 0x3e, 0x95, 0x8d, 0xbd, 0x8c, 0x4d, 0xa8, 0xf6, - 0xb2, 0x59, 0xc2, 0xe0, 0xe9, 0x5d, 0x6d, 0x00, 0xa2, 0x8e, 0xcc, 0x1e, - 0x39, 0x1a, 0x6e, 0x56, 0x83, 0x12, 0x2c, 0x40, 0x1c, 0x16, 0x16, 0xc8, - 0x70, 0xcd, 0x8d, 0xe3, 0x08, 0x88, 0xb1, 0x5a, 0x1c, 0x71, 0x7d, 0x30, - 0x4f, 0x8c, 0x90, 0xcb, 0xa5, 0xd0, 0x06, 0xda, 0xea, 0x02, 0xbb, 0xe7, - 0xe5, 0xea, 0xa9, 0xaa, 0x19, 0x2f, 0xa6, 0x62, 0x1b, 0x17, 0xae, 0xa3, - 0x73, 0x32, 0xfd, 0x38, 0x9d, 0x29, 0xe5, 0x1d, 0x3b, 0xcc, 0xb4, 0x1a, - 0xa9, 0x5b, 0xd5, 0x72, 0xd3, 0x86, 0xf5, 0xdb, 0x00, 0xbb, 0xf7, 0x18, - 0xcf, 0xa8, 0xef, 0x78, 0xd2, 0x2e, 0x94, 0x93, 0x02, 0xb5, 0x46, 0x93, - 0xd2, 0xd3, 0x34, 0x17, 0x2f, 0x1a, 0x37, 0xea, 0x2c, 0x97, 0xa2, 0x6c, - 0xf8, 0xd8, 0x12, 0x4f, 0x37, 0x79, 0x60, 0x37, 0xdc, 0xb7, 0xb4, 0xf2, - 0x88, 0x20, 0x34, 0xfa, 0xd3, 0x8c, 0xcc, 0x71, 0xf6, 0xcc, 0x26, 0x48, - 0xbe, 0xc3, 0x59, 0xf6, 0x38, 0xf2, 0xc7, 0xcc, 0x1f, 0x20, 0x24, 0x99, - 0x40, 0x4c, 0xd7, 0x71, 0x42, 0xf3, 0xea, 0xf6, 0xae, 0xbe, 0x46, 0xb8, - 0xff, 0x47, 0x8c, 0x90, 0x71, 0xc8, 0x2e, 0x6d, 0xa7, 0xa1, 0xa4, 0xdc, - 0x15, 0x10, 0x74, 0x2c, 0x78, 0xe2, 0x4f, 0x42, 0xee, 0xd0, 0x3a, 0x8e, - 0x0a, 0x27, 0xc6, 0x04, 0x35, 0x2a, 0x08, 0x99, 0x2b, 0x4d, 0xef, 0x47, - 0x41, 0xa0, 0x74, 0xe8, 0xa9, 0x89, 0x6e, 0x55, 0x7e, 0x68, 0xd3, 0x06, - 0xb2, 0xea, 0x9b, 0x4b, 0xeb, 0xab, 0x75, 0x0a, 0x4b, 0x69, 0x3c, 0xfc, - 0x33, 0xf5, 0x7d, 0xae, 0x9e, 0x23, 0x88, 0x86, 0x4d, 0xce, 0x71, 0x35, - 0xdc, 0x68, 0xd7, 0x3b, 0xeb, 0xad, 0x17, 0x3a, 0x8d, 0xef, 0xa5, 0x7a, - 0xc1, 0x8f, 0x42, 0x58, 0x98, 0x86, 0x97, 0xd5, 0x9b, 0x77, 0x59, 0x9e, - 0x86, 0xa0, 0xab, 0xea, 0x80, 0xc8, 0xa6, 0x10, 0x1b, 0xcf, 0x95, 0x77, - 0xd9, 0x0a, 0xf4, 0xfe, 0xb7, 0xec, 0x82, 0xc2, 0x24, 0x34, 0xe5, 0x70, - 0xff, 0xf1, 0x50, 0x80, 0x2d, 0xdf, 0xfc, 0x21, 0x7a, 0xcf, 0x24, 0xc4, - 0xbe, 0x00, 0x80, 0xa4, 0x86, 0xb2, 0x94, 0x29, 0x35, 0x4f, 0x3a, 0xab, - 0xf9, 0xfa, 0x99, 0xcb, 0x8b, 0xde, 0xb8, 0x78, 0xf3, 0xcd, 0x26, 0xb7, - 0xa9, 0xc9, 0x40, 0x2e, 0xa0, 0x86, 0x77, 0x6d, 0x57, 0xe6, 0x10, 0xed, - 0x2a, 0x37, 0x17, 0x3b, 0x6b, 0xd2, 0xc9, 0xcc, 0x31, 0x25, 0xc5, 0xdf, - 0x8a, 0xb7, 0x2d, 0xdc, 0x3a, 0xd3, 0x2b, 0xe1, 0xc5, 0x19, 0x3f, 0x11, - 0x51, 0xdb, 0xbb, 0x96, 0xc9, 0x59, 0xf7, 0xb8, 0xc6, 0xa9, 0xc5, 0x29, - 0xb0, 0x52, 0xcd, 0x55, 0xbf, 0xde, 0x40, 0x62, 0x10, 0x96, 0x4d, 0x83, - 0x9a, 0x02, 0x33, 0xa9, 0x05, 0x75, 0xa5, 0xaa, 0x83, 0x08, 0xf3, 0xbd, - 0x10, 0x47, 0x81, 0x0c, 0xf0, 0xbf, 0x4d, 0x28, 0x4d, 0x7b, 0x9e, 0x68, - 0x52, 0xf2, 0xd2, 0x45, 0x29, 0x76, 0xc3, 0x4f, 0x7b, 0xdf, 0x9b, 0xa8, - 0xdf, 0x74, 0x73, 0x42, 0xfe, 0x1f, 0xdd, 0x20, 0x70, 0xa3, 0xd8, 0xc9, - 0xde, 0x78, 0x56, 0xcc, 0x1a, 0xbd, 0xd4, 0x24, 0xb7, 0x77, 0x92, 0x14, - 0x81, 0xc9, 0x1e, 0xee, 0xb1, 0x7f, 0x73, 0x61, 0xac, 0xb5, 0xe8, 0x57, - 0xe6, 0xe1, 0xe8, 0x99, 0x57, 0x3f, 0xfc, 0x79, 0xfa, 0xe6, 0x28, 0xe7, - 0xdc, 0x0b, 0x44, 0x47, 0x89, 0x6e, 0xc0, 0xc9, 0x96, 0x1c, 0xd6, 0x1b, - 0xfb, 0xb7, 0xf4, 0x4a, 0x49, 0xbd, 0x61, 0x17, 0x0e, 0xf2, 0x8b, 0xd5, - 0x80, 0xd0, 0xf8, 0x61, 0xbd, 0x58, 0x1d, 0x16, 0x6f, 0x02, 0xf4, 0xde, - 0x39, 0x6d, 0x27, 0x4a, 0x96, 0x3a, 0xad, 0x6f, 0xda, 0x84, 0x2b, 0xda, - 0x15, 0xe0, 0xe6, 0x93, 0x09, 0x8a, 0x77, 0x08, 0x19, 0x4a, 0x6b, 0xcc, - 0xcc, 0x73, 0x63, 0x46, 0xae, 0x92, 0x28, 0xc8, 0xf7, 0xaa, 0x3c, 0xdd, - 0x7c, 0xfe, 0xbd, 0x89, 0x7e, 0xbe, 0xf5, 0x45, 0xd2, 0x6b, 0x5c, 0x6e, - 0xa4, 0xdc, 0x94, 0x46, 0x94, 0x1d, 0xa4, 0x7a, 0x21, 0x4b, 0x0e, 0xae, - 0xd5, 0x87, 0x45, 0x05, 0x56, 0x0d, 0x6d, 0x26, 0xa8, 0x4d, 0x92, 0x11, - 0x45, 0x53, 0xa0, 0x72, 0xc2, 0xc4, 0xe9, 0xa1, 0x9a, 0x88, 0x40, 0xd8, - 0x1d, 0xa0, 0x24, 0xc2, 0x13, 0xc7, 0x74, 0x95, 0x1c, 0x45, 0x12, 0xe5, - 0x53, 0x32, 0x91, 0xd9, 0x1d, 0x4e, 0x52, 0x09, 0xec, 0x75, 0x81, 0x71, - 0x08, 0x32, 0x4a, 0x7d, 0x40, 0x0c, 0x8a, 0xc7, 0x35, 0xe8, 0x82, 0x60, - 0x8b, 0x44, 0x29, 0x09, 0x08, 0x19, 0xb1, 0x9d, 0x15, 0x9a, 0xaa, 0x32, - 0x5e, 0xdd, 0x8c, 0x77, 0xc1, 0x1a, 0xb5, 0xb6, 0x15, 0x77, 0xd8, 0x5a, - 0x97, 0xc3, 0x6f, 0x23, 0x99, 0xb8, 0xff, 0xf1, 0x50, 0x80, 0x30, 0x1f, - 0xfc, 0x21, 0x1a, 0xcd, 0x64, 0xec, 0xfa, 0x21, 0x82, 0xa0, 0x86, 0x33, - 0x92, 0x2f, 0x1e, 0x78, 0xd7, 0x7f, 0x8f, 0xdb, 0xcc, 0xae, 0xf8, 0xbd, - 0x7a, 0xe3, 0xa9, 0xeb, 0x55, 0xbe, 0x2b, 0xcf, 0x89, 0x79, 0x91, 0x92, - 0x88, 0x34, 0x18, 0xb3, 0xc6, 0x9b, 0xd0, 0x49, 0x8b, 0x8b, 0x64, 0xdd, - 0xf8, 0x43, 0x69, 0x2b, 0x6e, 0xdc, 0x02, 0x99, 0x35, 0x63, 0x72, 0xc3, - 0xc9, 0x2f, 0x22, 0x1f, 0xa4, 0x07, 0xd0, 0xa9, 0xa6, 0xfc, 0xae, 0x93, - 0x50, 0x16, 0xe4, 0x0b, 0x7f, 0x45, 0x13, 0x5d, 0x84, 0xe9, 0xdd, 0x85, - 0x47, 0xef, 0x80, 0x03, 0x53, 0x22, 0xb6, 0xf4, 0xa5, 0x04, 0x5d, 0x08, - 0x0b, 0xe8, 0x64, 0x00, 0x08, 0x65, 0xae, 0xf0, 0x66, 0xac, 0x0d, 0x0e, - 0xd1, 0x42, 0xa1, 0x8a, 0x26, 0xbe, 0x93, 0x0b, 0xc2, 0x10, 0x78, 0xe9, - 0xed, 0x8b, 0x2c, 0x5e, 0xb0, 0xde, 0x40, 0x67, 0x11, 0xf5, 0x27, 0xfa, - 0x8f, 0x4f, 0xec, 0x73, 0x58, 0x7d, 0xca, 0x85, 0x6e, 0x2e, 0x97, 0x66, - 0xaf, 0x7e, 0xa1, 0x62, 0x38, 0xbe, 0x5f, 0x97, 0x67, 0x63, 0x6b, 0x04, - 0xb8, 0x0a, 0xae, 0x06, 0x6a, 0xd9, 0x47, 0x39, 0x81, 0xe0, 0x0f, 0x5b, - 0xb6, 0xcb, 0xab, 0xc2, 0xb9, 0xf1, 0x84, 0xb1, 0xb0, 0x6a, 0x70, 0x10, - 0x08, 0x28, 0x73, 0x30, 0x55, 0x34, 0x1e, 0xfe, 0xb2, 0x82, 0x8c, 0xf3, - 0x57, 0xa6, 0x98, 0x47, 0x18, 0x55, 0x1e, 0xf4, 0x17, 0x00, 0xec, 0xc5, - 0xc3, 0x29, 0xbb, 0xf7, 0xbc, 0xdb, 0xf8, 0xf2, 0x3c, 0x15, 0x88, 0x30, - 0x25, 0x7b, 0xda, 0x58, 0xe5, 0x77, 0xe4, 0xee, 0x09, 0x70, 0xf3, 0x96, - 0x45, 0x2f, 0x70, 0xa9, 0x78, 0xe2, 0xb5, 0x3f, 0x5c, 0xef, 0x52, 0x82, - 0xd4, 0xd2, 0xe1, 0x30, 0x7d, 0x81, 0xed, 0xdf, 0x5e, 0xbc, 0xee, 0x6f, - 0x8b, 0xcc, 0xe2, 0xaf, 0x35, 0x5b, 0xe3, 0xcd, 0x49, 0xe2, 0xee, 0xea, - 0xaf, 0x75, 0x76, 0xaa, 0xe0, 0x19, 0x4f, 0x69, 0xbf, 0x83, 0x1c, 0x43, - 0xb3, 0x03, 0xc0, 0xe0, 0x7d, 0x0b, 0x72, 0x8e, 0xe2, 0x77, 0xe3, 0x61, - 0x02, 0xdd, 0xb3, 0xbf, 0xf0, 0x08, 0xc2, 0x21, 0xeb, 0xe0, 0x21, 0x68, - 0x1c, 0x7a, 0xa8, 0xee, 0xf8, 0x4c, 0x02, 0xd5, 0x01, 0x9b, 0x0a, 0xfe, - 0x65, 0x23, 0xe1, 0x41, 0x1f, 0xff, 0x81, 0x21, 0x75, 0x09, 0x45, 0x9c, - 0x88, 0xcd, 0x12, 0x02, 0x51, 0x00, 0x40, 0x0a, 0x6f, 0x26, 0xcc, 0x4d, - 0x84, 0x15, 0xd7, 0x5d, 0xb9, 0xd6, 0x2e, 0x2d, 0x49, 0xb0, 0x2e, 0x31, - 0xa1, 0xe9, 0x9a, 0x45, 0xeb, 0x27, 0x1a, 0x51, 0x18, 0x0e, 0x71, 0x8b, - 0x0e, 0x13, 0x09, 0x3a, 0xda, 0xb3, 0xb5, 0xa9, 0xde, 0x43, 0x70, 0xdf, - 0x5b, 0x3c, 0xe6, 0xd9, 0x6a, 0xe0, 0xff, 0xf1, 0x50, 0x80, 0x2f, 0x1f, - 0xfc, 0x21, 0x1a, 0xce, 0x60, 0xe9, 0x39, 0x08, 0xf4, 0xa1, 0xa6, 0xba, - 0x91, 0x8c, 0x14, 0x3b, 0x38, 0xe2, 0xfc, 0x7b, 0x6f, 0xcf, 0x73, 0x9b, - 0xbe, 0x3d, 0xfa, 0xd6, 0x78, 0xf8, 0xcd, 0xea, 0xa4, 0x4d, 0xcc, 0x97, - 0x95, 0x29, 0x12, 0x08, 0x2a, 0xaf, 0x42, 0x4a, 0x6f, 0x16, 0xd5, 0xff, - 0xc6, 0x80, 0x97, 0x6b, 0x8a, 0x05, 0x35, 0xf3, 0x27, 0x0e, 0x94, 0x11, - 0x2c, 0x10, 0x41, 0x8a, 0x72, 0x18, 0xc9, 0x51, 0xb9, 0x7e, 0x77, 0xe8, - 0x2d, 0x3a, 0xfd, 0x4f, 0xaf, 0xe1, 0x62, 0x76, 0x30, 0xc7, 0x4f, 0x81, - 0x2b, 0xd2, 0xca, 0x75, 0x95, 0x7f, 0x9d, 0x43, 0x63, 0x53, 0x5f, 0x6f, - 0x3c, 0xd2, 0x72, 0x5c, 0xbc, 0x7e, 0x43, 0x19, 0x97, 0x83, 0xfa, 0x09, - 0x04, 0x2b, 0xaa, 0xf1, 0x6e, 0xfc, 0xd6, 0xf7, 0xd6, 0xf7, 0xe8, 0x4f, - 0x5a, 0xdb, 0x21, 0x6c, 0xba, 0x5c, 0x31, 0x6e, 0xaf, 0xcd, 0x85, 0x8e, - 0x0d, 0x4c, 0xc7, 0x26, 0x43, 0x5e, 0x1d, 0x67, 0x85, 0x98, 0xf2, 0xdf, - 0x8e, 0xd1, 0x1c, 0xe1, 0x79, 0x23, 0x83, 0xef, 0x04, 0x2e, 0xe5, 0x85, - 0x3e, 0x9b, 0x85, 0xf7, 0x61, 0x6e, 0xcb, 0x87, 0x17, 0x80, 0xd8, 0x8b, - 0x79, 0x74, 0xba, 0x1e, 0x9f, 0xe5, 0xbe, 0xe8, 0x35, 0x2b, 0x77, 0xce, - 0x4e, 0x42, 0xe4, 0x4f, 0xf6, 0x29, 0xef, 0x82, 0x85, 0xd2, 0xac, 0x17, - 0xa4, 0x00, 0x50, 0x6d, 0xcf, 0x8f, 0x7d, 0x7f, 0x8d, 0xf6, 0x97, 0x6d, - 0x28, 0xd3, 0x5e, 0x2f, 0x73, 0xdb, 0xc5, 0x9e, 0xf4, 0x7e, 0xbd, 0x66, - 0x02, 0xd1, 0xde, 0x5d, 0xd7, 0xe2, 0x81, 0x6f, 0x36, 0x39, 0x80, 0x5a, - 0x0f, 0x95, 0xa4, 0x0f, 0x3c, 0x4c, 0x95, 0xe7, 0x34, 0xc7, 0xe8, 0x8e, - 0x25, 0xd4, 0xe4, 0xa7, 0x10, 0x7a, 0x86, 0x9a, 0xcf, 0xf7, 0xb2, 0xeb, - 0xeb, 0xc7, 0x1e, 0xff, 0x18, 0x5d, 0xd6, 0xfa, 0x4d, 0xcb, 0xde, 0xba, - 0xbf, 0x1f, 0x5c, 0xd5, 0xee, 0xf5, 0x5c, 0xc4, 0x92, 0x00, 0x00, 0x5f, - 0xe5, 0x8f, 0xea, 0xe2, 0x9e, 0x87, 0x8a, 0x20, 0x0e, 0x7b, 0x7b, 0xe8, - 0xcf, 0xc0, 0x69, 0x91, 0x14, 0xa4, 0xf2, 0xa2, 0x39, 0xd9, 0xc8, 0x01, - 0xbe, 0x96, 0x8f, 0xe0, 0x25, 0x77, 0x60, 0xb3, 0xa8, 0x1d, 0x5b, 0x27, - 0x4b, 0x91, 0xdb, 0x68, 0xaa, 0xd4, 0x4c, 0x5d, 0xa8, 0x62, 0xc0, 0xaf, - 0xa9, 0x11, 0x4e, 0x0d, 0x68, 0x04, 0x3b, 0xea, 0x05, 0x30, 0x04, 0x62, - 0xb2, 0xa1, 0x40, 0x10, 0xa9, 0x6b, 0x57, 0xa8, 0x0a, 0x51, 0xcc, 0xc1, - 0xaf, 0xa2, 0x2e, 0xdb, 0x06, 0x80, 0x47, 0x53, 0x88, 0x00, 0x38, 0x8d, - 0xbc, 0x1e, 0xd3, 0xc2, 0xfa, 0xa1, 0xf7, 0x4f, 0x80, 0xf0, 0xff, 0xf1, - 0x50, 0x80, 0x3c, 0xdf, 0xfc, 0x20, 0xa4, 0x2a, 0xd6, 0x18, 0x63, 0x39, - 0x20, 0xe7, 0xe2, 0xf8, 0xef, 0xad, 0xf1, 0x5f, 0x5f, 0xb7, 0x7f, 0x53, - 0xbf, 0xab, 0x9f, 0x9f, 0xbf, 0x8a, 0x47, 0x9d, 0xd4, 0xdb, 0x5b, 0x0a, - 0x8b, 0xd0, 0x0f, 0xd8, 0x6e, 0xb1, 0x8c, 0xc6, 0xb1, 0x4b, 0xa4, 0x7d, - 0x02, 0x78, 0x30, 0x6e, 0xdb, 0xa6, 0x95, 0xeb, 0x06, 0xa4, 0x92, 0xe1, - 0x69, 0x80, 0xa5, 0x6a, 0x2f, 0xcc, 0x78, 0x43, 0xdb, 0x3e, 0xff, 0x7f, - 0x60, 0x00, 0xe1, 0xb1, 0x13, 0x74, 0x63, 0x46, 0xd9, 0x2f, 0x0d, 0x53, - 0x76, 0x0f, 0x92, 0x00, 0x12, 0xdc, 0xb4, 0x4e, 0x37, 0xc2, 0x99, 0x9b, - 0x20, 0x0b, 0xe0, 0x66, 0xd0, 0x4b, 0x53, 0xeb, 0xb4, 0x96, 0x14, 0x00, - 0x89, 0xde, 0xf8, 0x37, 0x13, 0x27, 0x83, 0x45, 0x9d, 0x50, 0xa1, 0x5b, - 0x44, 0xcd, 0x69, 0x15, 0x83, 0x6b, 0x6a, 0xa4, 0x20, 0x47, 0xee, 0xf8, - 0x10, 0xee, 0xc4, 0x72, 0x58, 0x30, 0xd3, 0xe4, 0x64, 0x46, 0x17, 0x66, - 0x82, 0xad, 0x82, 0xc0, 0x5c, 0xac, 0x5c, 0xf2, 0xe3, 0x36, 0x1d, 0xbb, - 0xa7, 0x79, 0x99, 0x73, 0x37, 0x6c, 0xea, 0xed, 0xae, 0x57, 0x5e, 0x7a, - 0x12, 0xe2, 0x3a, 0xcc, 0x00, 0xab, 0xd6, 0xd6, 0xfe, 0x84, 0x6b, 0x21, - 0xd8, 0xf1, 0x51, 0x68, 0xa2, 0xa5, 0xcb, 0x86, 0x0e, 0xbc, 0x36, 0xf5, - 0xab, 0xc9, 0xc7, 0x11, 0x00, 0x3b, 0x4d, 0x37, 0x00, 0x19, 0x3a, 0x6c, - 0x2c, 0x1c, 0x05, 0x68, 0x54, 0x95, 0xa9, 0xce, 0x47, 0xbf, 0x9b, 0xa2, - 0x85, 0xc5, 0xca, 0x84, 0xa4, 0x3c, 0x8d, 0x8d, 0x77, 0xd5, 0xcf, 0x9e, - 0x78, 0x5e, 0xcb, 0x1f, 0x7b, 0xdc, 0x03, 0xa1, 0x2b, 0x1a, 0xaf, 0xb0, - 0x0b, 0x07, 0xf3, 0x28, 0xc6, 0xb5, 0x8a, 0x1a, 0xce, 0x47, 0x1a, 0xdf, - 0xd7, 0xaf, 0xdf, 0xfa, 0xfd, 0x77, 0xae, 0x6b, 0xda, 0xb7, 0xe7, 0x2f, - 0xe3, 0xde, 0xfb, 0xf3, 0xed, 0xcd, 0x79, 0x77, 0x93, 0x78, 0x92, 0xf9, - 0x95, 0x28, 0x1b, 0xb9, 0x3b, 0x89, 0x4b, 0x45, 0x84, 0xbd, 0x4d, 0x99, - 0x6b, 0xd6, 0x43, 0x47, 0xab, 0xe0, 0x54, 0x02, 0xc0, 0x60, 0x42, 0x5c, - 0x45, 0x8b, 0x54, 0x05, 0x2b, 0xe8, 0x71, 0x0e, 0xb7, 0x72, 0x81, 0xde, - 0x08, 0xfb, 0x27, 0x45, 0xe7, 0x40, 0xa5, 0x73, 0xa1, 0x4d, 0x51, 0x8c, - 0xaf, 0x05, 0x47, 0x3e, 0xd6, 0xa9, 0x0c, 0xe5, 0x0a, 0xed, 0x20, 0x23, - 0x95, 0x60, 0x3c, 0xe0, 0x84, 0x88, 0x08, 0x90, 0xd3, 0x29, 0x82, 0xa6, - 0x21, 0xe5, 0x0e, 0xc0, 0x9e, 0x51, 0x94, 0x26, 0x34, 0x87, 0xc0, 0x89, - 0x6a, 0x40, 0x2b, 0x71, 0x35, 0x27, 0x50, 0x02, 0xe2, 0x0a, 0xab, 0x0b, - 0xdd, 0x5d, 0x57, 0xd9, 0xc5, 0x32, 0x9a, 0xf2, 0xa9, 0xe1, 0xee, 0xf3, - 0x35, 0x76, 0xa7, 0x8a, 0x2f, 0x9a, 0xc9, 0x1b, 0xfe, 0xfe, 0x6a, 0xcc, - 0x37, 0xda, 0x03, 0x62, 0x62, 0x2b, 0xa8, 0x6a, 0xfe, 0x68, 0xba, 0x1f, - 0x4c, 0x4d, 0xb6, 0x5a, 0xaa, 0xde, 0xe7, 0x2a, 0xac, 0x15, 0x84, 0xe7, - 0x80, 0x52, 0x03, 0xb8, 0x08, 0x62, 0xde, 0xc7, 0x2d, 0x86, 0xce, 0x00, - 0x09, 0x45, 0x70, 0x57, 0x28, 0xef, 0x29, 0x83, 0x83, 0xc5, 0x0e, 0x02, - 0x0e, 0x90, 0x25, 0x75, 0xde, 0x3c, 0xbc, 0x0b, 0x72, 0x24, 0x78, 0x27, - 0x01, 0xe0, 0xea, 0xb1, 0x27, 0x6b, 0x61, 0xb6, 0xb3, 0x11, 0x22, 0xa1, - 0x0b, 0x9e, 0xdb, 0x01, 0x72, 0x85, 0x2c, 0xde, 0x1d, 0x77, 0xd2, 0x45, - 0x5f, 0x30, 0xb6, 0x38, 0xff, 0xf1, 0x50, 0x80, 0x38, 0xbf, 0xfc, 0x20, - 0xa0, 0x4c, 0xd9, 0x04, 0x90, 0x71, 0xa4, 0x0b, 0x51, 0x4c, 0x52, 0x47, - 0x2d, 0x1a, 0x5c, 0xa8, 0x1d, 0xe3, 0xda, 0x7a, 0xf3, 0xf1, 0x97, 0xcc, - 0xfd, 0x35, 0x3e, 0xee, 0x7a, 0xeb, 0xbe, 0x5c, 0x7f, 0x5b, 0xbf, 0x3f, - 0xaf, 0xe7, 0xf1, 0x9e, 0x27, 0x36, 0x9d, 0xf4, 0xea, 0x07, 0xe0, 0x5a, - 0x9f, 0xbf, 0x03, 0x4e, 0xae, 0xd4, 0xae, 0x52, 0xe0, 0xda, 0x0e, 0xf4, - 0x02, 0x10, 0xa9, 0x58, 0x91, 0x89, 0xaa, 0xd6, 0x02, 0x27, 0xcf, 0xc2, - 0xe0, 0xaf, 0x8b, 0x8d, 0x14, 0x87, 0xfc, 0xb6, 0xa4, 0xad, 0x7b, 0xc9, - 0x67, 0x8a, 0xb8, 0x09, 0xc8, 0xd7, 0xf8, 0x2b, 0x91, 0x69, 0xee, 0x5a, - 0x1f, 0x8a, 0x73, 0x9b, 0xa1, 0x8e, 0x8d, 0xc7, 0x43, 0xc3, 0xb7, 0x7d, - 0xa3, 0x99, 0xe2, 0x06, 0x12, 0xa7, 0x20, 0x8e, 0x1e, 0x9f, 0xa7, 0x81, - 0x4b, 0x79, 0x70, 0xa4, 0xd0, 0xbb, 0x8c, 0xbc, 0x01, 0x26, 0xb4, 0xf2, - 0xbc, 0xc6, 0x6f, 0xa6, 0x80, 0x27, 0x2d, 0x0b, 0x9f, 0xaf, 0xb7, 0xcc, - 0x9b, 0xbf, 0x5f, 0x60, 0xc5, 0xb7, 0x45, 0x9d, 0x1f, 0x28, 0x1b, 0x25, - 0x7b, 0xa1, 0x87, 0xb3, 0x4d, 0xb7, 0x12, 0x5c, 0x2d, 0x73, 0xee, 0xd7, - 0xdd, 0x15, 0x1c, 0x49, 0x81, 0xc4, 0xc4, 0x79, 0xa7, 0x3b, 0x07, 0x5b, - 0xe0, 0x21, 0xdb, 0x9d, 0xad, 0x25, 0xd9, 0x35, 0x09, 0x24, 0xaa, 0x61, - 0x0b, 0x9a, 0xf1, 0xda, 0xb3, 0x1e, 0x82, 0x32, 0x06, 0x82, 0x4f, 0x0a, - 0x7a, 0x61, 0x52, 0x2e, 0x52, 0xb4, 0x42, 0xaa, 0x06, 0xb3, 0x45, 0x1c, - 0xc1, 0x44, 0x30, 0xcd, 0x66, 0xb7, 0xd7, 0x8f, 0xf1, 0xfa, 0x6b, 0x7b, - 0xf2, 0xdf, 0xc7, 0x68, 0xaa, 0x92, 0xf5, 0x9a, 0x6f, 0xeb, 0xc5, 0xca, - 0x39, 0xbe, 0xba, 0xe4, 0x1f, 0x8f, 0x84, 0xcf, 0x7c, 0x67, 0x30, 0xd1, - 0x72, 0x54, 0x1f, 0x69, 0xbb, 0xa9, 0x43, 0x1b, 0x44, 0x91, 0x04, 0x03, - 0x11, 0x48, 0xbc, 0xa2, 0x97, 0x83, 0xd2, 0x80, 0xd9, 0xc3, 0x30, 0x52, - 0xfa, 0x75, 0xb0, 0x0d, 0xf0, 0x65, 0x0e, 0xf4, 0x54, 0x7a, 0xd6, 0x33, - 0x86, 0x0a, 0xe9, 0x33, 0x6c, 0x04, 0xfb, 0xd9, 0xfd, 0xbd, 0x00, 0x18, - 0x5b, 0xc8, 0x55, 0x33, 0x2c, 0x37, 0x91, 0x0d, 0x01, 0xfa, 0x37, 0x0c, - 0xb3, 0x4b, 0x92, 0xac, 0xde, 0x56, 0xf8, 0xa2, 0x11, 0x9f, 0xa0, 0x19, - 0x06, 0x83, 0x2e, 0x70, 0xcf, 0x14, 0x50, 0x8e, 0xf3, 0x0e, 0x1d, 0x79, - 0x63, 0x19, 0xf7, 0xb3, 0xc5, 0x49, 0xe8, 0x5c, 0xc9, 0x0c, 0xc0, 0x93, - 0x80, 0x82, 0x29, 0xc7, 0xa4, 0x00, 0x62, 0xff, 0xdb, 0xf1, 0xf1, 0x01, - 0xfa, 0xf9, 0xb7, 0x0b, 0xff, 0xff, 0xd7, 0xfa, 0x0e, 0x62, 0x3d, 0xbe, - 0xe0, 0x94, 0x7a, 0x3e, 0xcb, 0xa9, 0xff, 0x9f, 0x27, 0x53, 0x48, 0x5b, - 0x7c, 0x3b, 0xe8, 0x1d, 0x66, 0xbf, 0xae, 0x39, 0xc3, 0xdb, 0xf9, 0x80, - 0x05, 0xd0, 0xf7, 0x00, 0x00, 0xa2, 0x3e, 0xc0, 0x06, 0x38, 0xbe, 0x7a, - 0x40, 0x02, 0x1a, 0x7d, 0xa5, 0x06, 0xc9, 0x28, 0x05, 0xce, 0x48, 0x89, - 0xe7, 0x40, 0x12, 0x9f, 0x5d, 0x91, 0x02, 0xf7, 0xd6, 0x9b, 0xf2, 0xff, - 0x79, 0xd9, 0x2c, 0xb2, 0xa4, 0x54, 0x59, 0x35, 0xe7, 0x29, 0x9d, 0xd3, - 0xa7, 0xff, 0xf1, 0x50, 0x80, 0x37, 0x7f, 0xfc, 0x20, 0xa6, 0x7a, 0xd0, - 0xa4, 0x39, 0x35, 0x10, 0xc5, 0x0f, 0x8f, 0x73, 0x8e, 0x3b, 0xcb, 0xd9, - 0xc7, 0x26, 0xb6, 0x71, 0x5b, 0xd5, 0xf1, 0x32, 0x17, 0x73, 0x8e, 0x5c, - 0x74, 0x15, 0xd8, 0x78, 0x2c, 0xd2, 0x9c, 0xf8, 0x53, 0x26, 0x0c, 0x84, - 0xc5, 0x0c, 0xc4, 0xc4, 0x39, 0x0d, 0xde, 0xa4, 0x09, 0x53, 0xdd, 0x93, - 0x1a, 0x14, 0xb8, 0x47, 0xbb, 0xef, 0x1f, 0xbd, 0xd5, 0x9b, 0x14, 0xcc, - 0x0d, 0x9e, 0xfd, 0xc9, 0x5c, 0x4e, 0x5c, 0x76, 0x7d, 0x1b, 0x82, 0x09, - 0x13, 0xfb, 0x93, 0x82, 0x10, 0x72, 0x79, 0x79, 0x41, 0x4e, 0x70, 0x02, - 0x31, 0x28, 0x93, 0x0f, 0x16, 0xdd, 0x91, 0xe6, 0xd3, 0xb9, 0x7e, 0xf0, - 0xcb, 0x2d, 0x2e, 0x45, 0x08, 0x28, 0x2f, 0x5a, 0x8c, 0x18, 0xc5, 0xfb, - 0x05, 0x44, 0xc2, 0xc0, 0x0c, 0x08, 0x11, 0x70, 0x82, 0x44, 0x79, 0xe7, - 0x65, 0x7b, 0x6d, 0xf2, 0xda, 0xdf, 0xdb, 0x28, 0x1d, 0x41, 0x19, 0xde, - 0x99, 0x76, 0x72, 0xc5, 0x0a, 0xb8, 0x64, 0x40, 0x41, 0x0e, 0x2b, 0xd0, - 0x53, 0x76, 0x59, 0x76, 0xe4, 0x36, 0xad, 0x67, 0xfa, 0x69, 0xb6, 0x50, - 0xc5, 0x74, 0x3a, 0x68, 0xb3, 0x70, 0xb1, 0xe1, 0xe4, 0x9f, 0x34, 0x90, - 0xa5, 0x46, 0xc1, 0x64, 0x02, 0x90, 0xec, 0x03, 0x54, 0x59, 0xe0, 0x30, - 0x72, 0x51, 0x26, 0x8a, 0xed, 0x73, 0x13, 0x5e, 0x58, 0x76, 0xdb, 0xa6, - 0xeb, 0x62, 0xb4, 0x12, 0x80, 0x10, 0xd8, 0xff, 0x9d, 0x5e, 0x3d, 0xc5, - 0x84, 0x11, 0xc7, 0x73, 0x5d, 0xdd, 0x56, 0x35, 0x99, 0x69, 0xb6, 0x2a, - 0x71, 0x27, 0x3f, 0x7f, 0x9f, 0xae, 0x76, 0xdf, 0xf3, 0xf3, 0xcf, 0x9c, - 0xbd, 0xf5, 0xe3, 0x55, 0xc4, 0xcc, 0xce, 0x3d, 0xb3, 0xd6, 0xb4, 0xc9, - 0xe6, 0x65, 0x80, 0x73, 0xab, 0xbf, 0x1f, 0xf2, 0x68, 0x29, 0x4f, 0x9c, - 0x71, 0x6a, 0x37, 0xb5, 0xf4, 0x42, 0xe1, 0x0e, 0x0b, 0x87, 0xd9, 0xbf, - 0x33, 0xf1, 0xf2, 0x01, 0x1c, 0x5d, 0x78, 0xe8, 0x3a, 0x10, 0xfb, 0xd7, - 0x28, 0xea, 0xb8, 0xe0, 0x5f, 0x45, 0xd9, 0xaf, 0x54, 0x7b, 0xd1, 0xda, - 0x84, 0x75, 0x47, 0x76, 0xb9, 0x7d, 0x35, 0x9d, 0x06, 0x08, 0x19, 0xa5, - 0xc9, 0x50, 0x03, 0x01, 0xc7, 0x33, 0x30, 0x44, 0x03, 0x53, 0x27, 0x82, - 0xcb, 0xd2, 0xd7, 0x47, 0x1d, 0xb6, 0xdb, 0x33, 0x61, 0x87, 0xeb, 0xaa, - 0x44, 0x00, 0x0c, 0x7b, 0x8b, 0x87, 0xa7, 0xd1, 0xde, 0x19, 0xaf, 0xaa, - 0xba, 0x64, 0x9c, 0xfa, 0xf0, 0xed, 0xa2, 0x55, 0x3a, 0xe9, 0xb6, 0x7e, - 0x70, 0xf1, 0xf3, 0xd7, 0x0f, 0x8e, 0xda, 0x76, 0xfb, 0xda, 0x62, 0xf2, - 0x07, 0x00, 0x50, 0xbd, 0xd0, 0x31, 0x4d, 0x97, 0x2e, 0x5f, 0xaf, 0x6c, - 0xb1, 0xba, 0x24, 0x7d, 0xcc, 0x26, 0x49, 0xe5, 0x45, 0x45, 0x1e, 0x2b, - 0x9a, 0x72, 0xb6, 0x22, 0x66, 0x98, 0xdc, 0x65, 0xa0, 0xc5, 0x48, 0x71, - 0x47, 0xa9, 0x60, 0xe4, 0x4e, 0x96, 0xa2, 0x9b, 0x12, 0x89, 0x13, 0xc4, - 0x66, 0xc6, 0x2e, 0xf9, 0xad, 0x88, 0xc7, 0x9a, 0x73, 0x59, 0xac, 0xb9, - 0x48, 0x2d, 0x39, 0x7c, 0x03, 0x65, 0x33, 0xb0, 0x33, 0x32, 0x34, 0x3f, - 0xff, 0xf1, 0x50, 0x80, 0x26, 0x5f, 0xfc, 0x21, 0x1a, 0xcd, 0xe9, 0xbd, - 0xfc, 0x00, 0x00, 0xa5, 0xa1, 0x33, 0xd3, 0x0e, 0x20, 0xbc, 0x95, 0x7e, - 0xde, 0xf2, 0xb8, 0xf3, 0xf3, 0xc4, 0xcd, 0xdd, 0x54, 0x9d, 0x54, 0xbd, - 0xd7, 0x1b, 0xd6, 0x49, 0xaa, 0x69, 0xec, 0x0e, 0xfd, 0x1e, 0xf1, 0x44, - 0x59, 0x6e, 0xda, 0x76, 0xc1, 0xb5, 0x25, 0xc5, 0x27, 0xda, 0x24, 0x8a, - 0x84, 0x42, 0x70, 0x75, 0xc2, 0xd0, 0x63, 0x7d, 0x1a, 0x71, 0x7e, 0x24, - 0x65, 0x5a, 0xc9, 0x00, 0x5f, 0xec, 0xf2, 0x0c, 0x3b, 0xd7, 0x53, 0x72, - 0x6a, 0xb8, 0x13, 0x9a, 0x9d, 0x68, 0x65, 0x8a, 0xe6, 0x69, 0x6a, 0xe6, - 0xe7, 0xa9, 0xd9, 0x9e, 0x5c, 0x7b, 0x22, 0x00, 0x63, 0x4a, 0xd2, 0x9b, - 0x4a, 0xc2, 0xa2, 0x4f, 0x75, 0x93, 0xc1, 0x1d, 0x75, 0x5d, 0x6c, 0x18, - 0x35, 0xc7, 0xb3, 0x58, 0x4e, 0x56, 0x56, 0x4a, 0x76, 0x2b, 0x1a, 0x3b, - 0x14, 0x4f, 0x34, 0xe2, 0xa8, 0x36, 0x94, 0xa9, 0x83, 0x21, 0x41, 0x4b, - 0x3a, 0x74, 0x50, 0x13, 0x57, 0x68, 0xcb, 0x9b, 0xf9, 0xbe, 0xd4, 0x7b, - 0x11, 0xaa, 0xd1, 0xa9, 0x46, 0xb0, 0xc9, 0x91, 0x98, 0x15, 0x15, 0x11, - 0x6b, 0xbc, 0xfc, 0xd5, 0xb9, 0xe1, 0xa1, 0x30, 0x96, 0xce, 0x51, 0x7a, - 0xb1, 0x7e, 0x9b, 0xe4, 0x5e, 0x30, 0x3d, 0xbd, 0x47, 0xe5, 0x64, 0xed, - 0xc3, 0x40, 0xef, 0x8f, 0xf3, 0xb9, 0x37, 0x44, 0x71, 0x58, 0xde, 0x13, - 0xaf, 0x95, 0x66, 0x99, 0xfc, 0x08, 0x6e, 0x70, 0x83, 0x8d, 0x79, 0xdc, - 0xcd, 0xe8, 0x73, 0xce, 0x5e, 0x07, 0x89, 0xf7, 0x9a, 0x14, 0xcb, 0x29, - 0xca, 0x6a, 0x63, 0x6d, 0x2d, 0x89, 0x5e, 0x29, 0xf8, 0x85, 0xe7, 0xc7, - 0xbd, 0xe4, 0x99, 0xd7, 0x9f, 0x9e, 0x3b, 0xe3, 0x77, 0x55, 0x25, 0x58, - 0xdf, 0x19, 0xc7, 0x24, 0xc4, 0xdd, 0x82, 0xc5, 0xfb, 0xe7, 0x3f, 0x73, - 0x88, 0x0c, 0xeb, 0xe1, 0x9d, 0x70, 0xd5, 0x70, 0xa2, 0x55, 0x59, 0xba, - 0xfc, 0x31, 0x57, 0xa9, 0xa7, 0x6a, 0xbf, 0x31, 0x5a, 0x70, 0x9e, 0x74, - 0xff, 0x32, 0x95, 0xe1, 0x1c, 0x84, 0xb9, 0xf7, 0x14, 0x8b, 0xa2, 0x92, - 0xb5, 0x7d, 0x76, 0x9e, 0x29, 0xb8, 0xff, 0xf1, 0x50, 0x80, 0x28, 0x1f, - 0xfc, 0x21, 0x1a, 0xce, 0xfd, 0x19, 0xf9, 0x00, 0x10, 0xa8, 0xb1, 0x34, - 0xd4, 0x49, 0x36, 0x09, 0xc4, 0xd4, 0xcd, 0xf5, 0x7b, 0xbd, 0xfc, 0x78, - 0xaf, 0xbf, 0x8d, 0xd7, 0x0d, 0xf1, 0x76, 0xd7, 0x89, 0x37, 0xd6, 0xd5, - 0x33, 0x52, 0x4e, 0x02, 0x0e, 0xa7, 0xd4, 0x8f, 0x1d, 0x28, 0x6d, 0x55, - 0x66, 0x54, 0x4d, 0xd5, 0xbb, 0x9d, 0x8e, 0xb4, 0xa0, 0x58, 0x88, 0x04, - 0xa0, 0x3c, 0x22, 0xa5, 0x5e, 0x42, 0x52, 0x34, 0x5e, 0x00, 0x3d, 0x76, - 0xde, 0x84, 0x15, 0x1d, 0x24, 0x64, 0x8e, 0x21, 0x31, 0x01, 0x0b, 0x39, - 0x35, 0x8c, 0xc5, 0xa9, 0x9d, 0xab, 0xdb, 0x8a, 0x81, 0xa9, 0x83, 0x4e, - 0x66, 0xd3, 0x39, 0x1e, 0xd2, 0x96, 0xf3, 0x29, 0x34, 0x2e, 0xe6, 0x2e, - 0x19, 0x3d, 0xf5, 0x19, 0xc7, 0x48, 0x7c, 0x8b, 0x60, 0xfb, 0x69, 0x72, - 0xb7, 0x24, 0xbc, 0x9c, 0x2a, 0x6b, 0x62, 0xb0, 0x19, 0xfc, 0xe6, 0x18, - 0x94, 0x6e, 0x70, 0x52, 0xc3, 0x40, 0xae, 0x82, 0x26, 0xc1, 0x33, 0x87, - 0x30, 0x37, 0x88, 0x57, 0xb4, 0x0d, 0x9b, 0x3a, 0xd2, 0x0e, 0x89, 0x06, - 0xc1, 0x8d, 0xa5, 0xb2, 0x39, 0x4a, 0x04, 0x37, 0x2a, 0x7b, 0x81, 0x55, - 0x6a, 0x57, 0xc7, 0xc6, 0xc3, 0xb2, 0xa4, 0xe4, 0xec, 0xe3, 0x6c, 0x55, - 0xbb, 0x65, 0x1a, 0xb9, 0xa9, 0x93, 0x72, 0x8a, 0x33, 0x44, 0x58, 0x04, - 0x16, 0x59, 0xd9, 0xfd, 0x9b, 0x8b, 0x97, 0x4b, 0x9f, 0xb6, 0x3e, 0x6b, - 0xd2, 0x67, 0xcc, 0xb7, 0xfc, 0x34, 0xee, 0x78, 0xec, 0x98, 0x3b, 0xdb, - 0xd6, 0x1b, 0xff, 0xd3, 0x47, 0x84, 0xd8, 0x77, 0x44, 0x51, 0x75, 0x05, - 0x08, 0x00, 0x63, 0x50, 0xc7, 0x5a, 0x7c, 0x0d, 0x5e, 0xa6, 0x6f, 0xae, - 0x75, 0x9d, 0xfb, 0x4a, 0x66, 0xab, 0x86, 0xf8, 0xe6, 0xfc, 0xdf, 0x77, - 0x59, 0xd6, 0x28, 0x2e, 0x40, 0x59, 0xec, 0x39, 0x6e, 0x41, 0x78, 0x1a, - 0xc9, 0xa9, 0x0a, 0xec, 0xd2, 0xee, 0x2f, 0xbe, 0xcf, 0x76, 0x93, 0x77, - 0x9b, 0x38, 0xfc, 0xe9, 0x73, 0x35, 0x17, 0x9a, 0xa8, 0x80, 0xb9, 0x88, - 0x06, 0x13, 0x7a, 0x86, 0x62, 0xe4, 0x74, 0x02, 0x2a, 0x11, 0x98, 0x4a, - 0xd1, 0x49, 0x4d, 0xce, 0x45, 0x16, 0x5c, 0xf1, 0x60, 0xcd, 0xd5, 0x4e, - 0x6b, 0x80, 0xff, 0xf1, 0x50, 0x80, 0x29, 0x1f, 0xfc, 0x21, 0x1a, 0xcf, - 0xc8, 0xb3, 0xb3, 0x7d, 0x7b, 0xa6, 0x86, 0xb1, 0xd2, 0xed, 0xf9, 0xbe, - 0xb7, 0x3b, 0xeb, 0x4c, 0xef, 0xa9, 0x38, 0xda, 0xdb, 0xdf, 0x15, 0xd5, - 0x2a, 0x5f, 0x31, 0x99, 0xc6, 0xf8, 0x9c, 0xf4, 0x16, 0x6f, 0x66, 0x50, - 0xeb, 0x15, 0x16, 0x5c, 0x2f, 0xb4, 0xa2, 0x82, 0x0c, 0xd6, 0x60, 0x94, - 0xa2, 0x10, 0x32, 0x8e, 0x77, 0x8e, 0xa5, 0x69, 0x88, 0x81, 0x99, 0x82, - 0x47, 0x5c, 0x5c, 0x43, 0x6a, 0x2e, 0x50, 0x52, 0x92, 0x1c, 0x88, 0x21, - 0x20, 0x9e, 0xf9, 0x04, 0x07, 0x95, 0x20, 0xda, 0x60, 0x8a, 0x1b, 0xfa, - 0xcb, 0xa5, 0x77, 0x43, 0x38, 0xe3, 0x57, 0xcf, 0x93, 0xa7, 0xee, 0xda, - 0xaa, 0x59, 0xf9, 0xfc, 0x53, 0x0f, 0x5f, 0x42, 0xc2, 0xa4, 0x39, 0xa1, - 0xb3, 0xaa, 0xd4, 0x89, 0xc6, 0xf1, 0x29, 0xa9, 0x67, 0x7e, 0x9f, 0x09, - 0xbb, 0xea, 0x17, 0x84, 0x58, 0x26, 0xda, 0xd3, 0x56, 0x4d, 0xcc, 0xd1, - 0x76, 0x96, 0x65, 0xc7, 0x5b, 0x70, 0x8b, 0xe4, 0x2b, 0x8b, 0xf7, 0xc5, - 0xcb, 0x19, 0x8a, 0x90, 0x1c, 0x59, 0x9c, 0x4a, 0x07, 0x66, 0xde, 0x96, - 0x46, 0x3d, 0xf1, 0xa3, 0x90, 0x9a, 0xc1, 0x95, 0x81, 0x6a, 0x39, 0x8a, - 0x39, 0x61, 0x57, 0x01, 0x17, 0xbe, 0x53, 0x4c, 0x5d, 0xd3, 0xae, 0x72, - 0xd9, 0xc1, 0x06, 0xc9, 0x68, 0x3d, 0x5b, 0xaf, 0xfe, 0x8e, 0x14, 0xe3, - 0x1c, 0x24, 0x74, 0xc3, 0x7f, 0x1b, 0x99, 0xc3, 0x55, 0xa9, 0xdf, 0xf2, - 0xc1, 0x6c, 0x0c, 0xea, 0xbe, 0xbf, 0xcd, 0xd3, 0x63, 0x90, 0x6d, 0x0c, - 0x3a, 0xa5, 0x87, 0x7d, 0xd6, 0x53, 0xfd, 0x2d, 0x33, 0x2d, 0xca, 0x8b, - 0xe8, 0x37, 0xe6, 0xfa, 0xcb, 0xf5, 0xe6, 0x93, 0xe7, 0xea, 0x7a, 0xf8, - 0x8b, 0xe6, 0x6f, 0xcc, 0x95, 0x99, 0x25, 0x45, 0x65, 0xd5, 0xca, 0xb8, - 0x0f, 0xdd, 0x3c, 0x34, 0x00, 0x02, 0xcd, 0x9e, 0x33, 0x19, 0x6a, 0x50, - 0xd0, 0x6b, 0x4d, 0x4c, 0x6e, 0x55, 0xe9, 0x32, 0x4d, 0xae, 0xb1, 0x8e, - 0x3f, 0xa8, 0x8a, 0xe7, 0x17, 0x2e, 0x0a, 0xb0, 0x2e, 0xfb, 0xf9, 0x16, - 0xbd, 0xee, 0x24, 0xdc, 0x77, 0x92, 0xaa, 0x1d, 0x9d, 0xdf, 0xdb, 0x7c, - 0x0e, 0xe9, 0xb0, 0x2e, 0xef, 0x57, 0x59, 0x4c, 0x79, 0xa4, 0x4c, 0x72, - 0xcb, 0x5a, 0x69, 0x92, 0x19, 0x3f, 0xff, 0xf1, 0x50, 0x80, 0x3b, 0x5f, - 0xfc, 0x20, 0x9f, 0x2a, 0xd0, 0xb6, 0x52, 0x1d, 0x82, 0x8e, 0x7a, 0xf2, - 0xea, 0xa7, 0x7e, 0x54, 0xef, 0xcd, 0xd7, 0xb7, 0x75, 0x9c, 0x72, 0x9c, - 0x55, 0x4e, 0x39, 0x65, 0xde, 0xf7, 0xaa, 0x94, 0xd5, 0x8b, 0x5a, 0xbc, - 0xc6, 0x22, 0xeb, 0x36, 0x94, 0x32, 0x1e, 0x1d, 0xd4, 0xc1, 0xbb, 0xbe, - 0xc9, 0x28, 0x37, 0x60, 0xea, 0x50, 0x39, 0x7d, 0xdc, 0xdb, 0xe7, 0xaa, - 0x4a, 0xaa, 0x59, 0x1e, 0xdb, 0x8a, 0x59, 0xdf, 0x65, 0x1e, 0xba, 0x65, - 0xc9, 0x5d, 0xef, 0x5e, 0x2b, 0x00, 0xc9, 0xee, 0x03, 0x51, 0xcd, 0xa6, - 0x8b, 0x02, 0x28, 0xc4, 0x77, 0x5c, 0xa3, 0x47, 0xe7, 0x43, 0xad, 0x0b, - 0x6c, 0xec, 0x3a, 0x57, 0xf6, 0xe0, 0x11, 0x6f, 0x3c, 0xa6, 0x23, 0xa8, - 0x02, 0xfb, 0x2d, 0x5e, 0x7f, 0x4b, 0x5b, 0x41, 0xed, 0x34, 0x73, 0x79, - 0x12, 0x66, 0x18, 0x94, 0x78, 0x1a, 0x80, 0xa9, 0xcf, 0x9d, 0xa4, 0x1d, - 0xbc, 0xab, 0xb6, 0x4d, 0xeb, 0x64, 0x6e, 0xb4, 0x66, 0xad, 0xa6, 0x2f, - 0xd0, 0xdd, 0x6b, 0x76, 0xf4, 0x70, 0xcb, 0x30, 0x70, 0x1d, 0xf5, 0x8b, - 0x42, 0xc7, 0x9c, 0xc4, 0xce, 0xab, 0xd8, 0xc0, 0x92, 0x9d, 0x47, 0xd0, - 0x83, 0xed, 0xa1, 0x16, 0xeb, 0xd3, 0x02, 0xba, 0xdd, 0x13, 0xf9, 0x35, - 0xaa, 0xe6, 0x1a, 0xe8, 0xb8, 0x91, 0xcc, 0x15, 0x7a, 0xf6, 0xbb, 0x16, - 0x8a, 0xbc, 0xb1, 0xe5, 0x56, 0x02, 0x9b, 0x89, 0x9b, 0x56, 0x57, 0x21, - 0x45, 0xaf, 0x67, 0x8e, 0x4c, 0xf6, 0x14, 0xbf, 0xc2, 0xc6, 0x22, 0xa9, - 0xdd, 0x42, 0x6c, 0x07, 0x9c, 0xf3, 0x8e, 0x45, 0x8b, 0xf7, 0xdd, 0x2f, - 0x9f, 0xd7, 0x7e, 0xff, 0x7b, 0xef, 0xaa, 0x8f, 0xa6, 0x1a, 0xd4, 0x86, - 0x3b, 0x85, 0x0c, 0xa1, 0x4a, 0x17, 0xc7, 0x55, 0x7e, 0xbe, 0x3b, 0xba, - 0xf9, 0xfc, 0x4f, 0xcf, 0xeb, 0x50, 0x9e, 0x3c, 0xcf, 0x3d, 0xf5, 0x6c, - 0xca, 0xab, 0xc9, 0x8c, 0xe2, 0xac, 0x13, 0xd2, 0xf5, 0x86, 0x10, 0xab, - 0x14, 0x30, 0x58, 0x61, 0x50, 0xc9, 0x03, 0x02, 0x8e, 0x79, 0x53, 0xe8, - 0xcf, 0xd6, 0xf6, 0x9e, 0x3a, 0x8d, 0xbf, 0x7e, 0x18, 0x45, 0x62, 0xf0, - 0xf5, 0xef, 0xf6, 0x8d, 0x69, 0xec, 0x03, 0xd9, 0x6b, 0x79, 0xe4, 0x01, - 0xfb, 0x25, 0x7c, 0x49, 0x8f, 0x9d, 0x04, 0xf5, 0x1c, 0xa6, 0x48, 0x5e, - 0x7e, 0xd0, 0x47, 0x4d, 0x11, 0xb4, 0x8d, 0x17, 0xf3, 0x29, 0x39, 0x1d, - 0xaf, 0xb9, 0xfd, 0xf7, 0x41, 0x37, 0xce, 0xbc, 0x11, 0x48, 0xa0, 0xc6, - 0x87, 0x28, 0x29, 0xac, 0xbe, 0xb3, 0xd1, 0xbb, 0x7c, 0x74, 0xd8, 0xcf, - 0xbd, 0xe9, 0x9f, 0xb2, 0xe9, 0x6a, 0x9a, 0x1f, 0x42, 0x74, 0x7f, 0xdf, - 0xf3, 0x8e, 0x58, 0x12, 0x0f, 0x9f, 0xef, 0xb6, 0xc0, 0x33, 0x43, 0x47, - 0x0c, 0xbf, 0x6e, 0xd5, 0x56, 0xc4, 0x7c, 0xa5, 0x8f, 0x18, 0xb8, 0x13, - 0xa3, 0x69, 0x2c, 0xe5, 0x64, 0xc7, 0x8c, 0xcc, 0x29, 0xa8, 0x42, 0x7b, - 0xeb, 0xe4, 0x6f, 0xb6, 0x05, 0x64, 0x28, 0x2b, 0x33, 0x88, 0x68, 0xcc, - 0x63, 0xe5, 0x3b, 0x94, 0x39, 0x3d, 0x2c, 0x2f, 0xa3, 0x90, 0xa2, 0x5b, - 0xd3, 0x42, 0xc6, 0x47, 0xa0, 0x42, 0x46, 0x45, 0xdb, 0x29, 0xa6, 0x65, - 0xa5, 0x63, 0x2d, 0xf1, 0xda, 0x18, 0x85, 0x46, 0x0e, 0x84, 0x32, 0xfa, - 0x53, 0x7d, 0xca, 0x9f, 0x78, 0x83, 0xc4, 0xe5, 0xe8, 0xec, 0x32, 0xfc, - 0xff, 0xf1, 0x50, 0x80, 0x3a, 0xff, 0xfc, 0x20, 0x95, 0x4c, 0xd9, 0x65, - 0x98, 0xf0, 0x06, 0xcb, 0x14, 0xad, 0x6c, 0xc7, 0x95, 0x9b, 0x00, 0x00, - 0xeb, 0xbf, 0x8f, 0xae, 0x6a, 0x4f, 0xe1, 0xd7, 0x9b, 0xf1, 0xfc, 0xbb, - 0xbd, 0xf3, 0xf9, 0x00, 0x02, 0xb5, 0x0c, 0x27, 0xf7, 0xc5, 0xfb, 0x5b, - 0xf2, 0x6b, 0xba, 0x93, 0xd7, 0xd9, 0x03, 0xb2, 0xa6, 0x10, 0x2b, 0x48, - 0xce, 0xf1, 0x8c, 0x69, 0x57, 0xd5, 0x70, 0x9c, 0x81, 0x10, 0x06, 0x40, - 0xa0, 0x59, 0x6b, 0x0c, 0xb0, 0xc1, 0x2c, 0x95, 0xa3, 0x30, 0xb2, 0xc8, - 0xe9, 0xa2, 0x4d, 0xc6, 0x6a, 0xc6, 0x7c, 0x4f, 0xb3, 0xa0, 0x52, 0xd5, - 0x76, 0x65, 0xd9, 0xbe, 0x6f, 0xc6, 0x0a, 0x73, 0x65, 0x9a, 0x25, 0x57, - 0x73, 0x5a, 0xda, 0xfa, 0x5c, 0x4d, 0x75, 0xe6, 0x52, 0x4e, 0x85, 0x80, - 0x85, 0x54, 0xdc, 0xb6, 0xbb, 0x8a, 0x70, 0x25, 0x11, 0x95, 0x29, 0x65, - 0x25, 0xa1, 0x59, 0xed, 0x00, 0x79, 0x2c, 0x97, 0x59, 0xd1, 0x45, 0x20, - 0x8e, 0x31, 0x98, 0x25, 0x21, 0x55, 0x38, 0x52, 0x8d, 0x53, 0x11, 0x3c, - 0x39, 0x84, 0x83, 0x49, 0x0c, 0x35, 0x06, 0x5b, 0xf9, 0x12, 0x65, 0x89, - 0x3a, 0xfd, 0x09, 0xef, 0x0d, 0xc3, 0xa9, 0xb9, 0xd3, 0xe0, 0xc8, 0x6c, - 0x7f, 0x0a, 0x69, 0x86, 0x6d, 0x1c, 0xbb, 0x32, 0x63, 0x55, 0xd3, 0x62, - 0x18, 0x22, 0x62, 0x48, 0x72, 0x70, 0xb6, 0x5b, 0x65, 0x8c, 0xf8, 0x27, - 0xf6, 0xaf, 0xca, 0x30, 0xf9, 0x5d, 0x8d, 0x80, 0x59, 0x07, 0xd2, 0x98, - 0xfa, 0xe5, 0x9d, 0x57, 0x97, 0xae, 0x56, 0xe9, 0x97, 0xf5, 0x51, 0x8c, - 0x2c, 0xb1, 0x65, 0x78, 0xcb, 0x38, 0x44, 0x2d, 0x73, 0xcb, 0x0a, 0x54, - 0xc3, 0x5a, 0x86, 0xc9, 0x72, 0x31, 0x12, 0xac, 0x20, 0xbf, 0xd7, 0xc7, - 0xbf, 0xb7, 0x7c, 0x3c, 0x75, 0xe7, 0xf3, 0xed, 0xe7, 0x58, 0xdf, 0xdb, - 0xe3, 0x3f, 0x5e, 0x65, 0xd3, 0xd4, 0x95, 0x59, 0xc5, 0xea, 0xba, 0xb0, - 0x1f, 0xa7, 0xa8, 0x16, 0x95, 0x48, 0x5c, 0x49, 0x4f, 0x09, 0x60, 0xb1, - 0x67, 0x24, 0x60, 0x82, 0x86, 0xb8, 0xce, 0xef, 0x47, 0x52, 0x83, 0x4f, - 0x0a, 0xe6, 0x0e, 0xc5, 0x01, 0x1c, 0x95, 0x2e, 0x2b, 0x9b, 0x64, 0xa3, - 0x65, 0x95, 0x4a, 0xab, 0x8f, 0x3f, 0xdd, 0xf3, 0x00, 0xd0, 0x00, 0x16, - 0x36, 0xee, 0x5d, 0xc0, 0x0c, 0x2a, 0x31, 0xe1, 0xe1, 0xee, 0x18, 0x8b, - 0x62, 0xe8, 0x7f, 0xf3, 0xf7, 0x7b, 0x38, 0x80, 0x00, 0x05, 0x70, 0xff, - 0xf9, 0xe1, 0xf9, 0x88, 0x00, 0x00, 0x93, 0x5f, 0xe8, 0xf3, 0xf7, 0xc3, - 0xc7, 0xfd, 0x3f, 0xe5, 0xeb, 0x80, 0x03, 0x50, 0x30, 0x3f, 0x5f, 0xff, - 0xcf, 0x3e, 0xe0, 0x1f, 0x40, 0xda, 0x2d, 0xb5, 0xec, 0x00, 0x00, 0x55, - 0xa1, 0xe1, 0xe1, 0xee, 0x20, 0x01, 0x87, 0x0f, 0x3e, 0xdc, 0x00, 0x00, - 0x0c, 0x07, 0x87, 0x87, 0xb8, 0x00, 0x00, 0x03, 0xda, 0x1e, 0x1e, 0xf0, - 0x00, 0x18, 0x50, 0x78, 0x7b, 0xb8, 0x80, 0x03, 0x0a, 0xbe, 0x1e, 0xb7, - 0x00, 0x40, 0xa8, 0xc3, 0xc3, 0xdb, 0x83, 0x48, 0x00, 0x54, 0x50, 0x78, - 0x7d, 0x9f, 0x40, 0x00, 0x55, 0xa3, 0xfe, 0xbf, 0xbb, 0x94, 0xd6, 0x1f, - 0xa8, 0x18, 0xb5, 0xef, 0x87, 0x87, 0x80, 0x55, 0xa6, 0x55, 0x8b, 0xb2, - 0x8b, 0x91, 0xc8, 0x28, 0x7c, 0x74, 0xf4, 0x92, 0x90, 0xba, 0xd9, 0xe1, - 0x18, 0xca, 0x9e, 0xff, 0xf1, 0x50, 0x80, 0x35, 0xff, 0xfc, 0x20, 0xa6, - 0x7a, 0xd0, 0x26, 0x62, 0x65, 0x88, 0x75, 0x55, 0xc4, 0xe7, 0xef, 0x5d, - 0xee, 0xbe, 0xfb, 0xce, 0xee, 0xb8, 0xdb, 0x4f, 0x3d, 0xfc, 0x77, 0x2b, - 0x5a, 0xde, 0xea, 0xf8, 0x95, 0xd6, 0x66, 0x86, 0x1f, 0x9f, 0xc8, 0x6e, - 0x48, 0xad, 0x66, 0x4a, 0xa1, 0x64, 0xb6, 0xef, 0x06, 0x9f, 0x11, 0x73, - 0x11, 0x1a, 0xd0, 0x82, 0x70, 0x4e, 0x24, 0xa2, 0xb6, 0xdf, 0x78, 0x4d, - 0x81, 0x47, 0x45, 0x3d, 0xa0, 0xb4, 0x41, 0x2a, 0xb3, 0x68, 0x2d, 0x10, - 0x56, 0x56, 0x35, 0x95, 0xd6, 0xc9, 0x62, 0x9a, 0x5b, 0xc3, 0xf2, 0x56, - 0x4e, 0xc4, 0xf0, 0xc8, 0x34, 0xd3, 0x9d, 0xe1, 0x45, 0x72, 0x2f, 0xd3, - 0x76, 0x59, 0xc4, 0x14, 0xee, 0xe1, 0x41, 0x41, 0x42, 0xad, 0xb0, 0x28, - 0xa2, 0xda, 0x34, 0x0a, 0x35, 0x95, 0xe1, 0x8e, 0x95, 0xb5, 0x51, 0x05, - 0x38, 0x2f, 0xc1, 0x41, 0x64, 0x05, 0xf9, 0xae, 0x85, 0x24, 0x54, 0x17, - 0x1b, 0x6a, 0x44, 0x14, 0x14, 0x76, 0x2d, 0x55, 0x13, 0x4f, 0x63, 0x56, - 0xb2, 0x34, 0xd2, 0xa1, 0x1a, 0x0a, 0x69, 0x50, 0x82, 0xb8, 0x2b, 0x2b, - 0x35, 0x61, 0xd0, 0x64, 0xbc, 0xa4, 0xd0, 0x2a, 0x3e, 0x97, 0x1e, 0x68, - 0x2b, 0xf9, 0xad, 0xf9, 0x33, 0x47, 0x3e, 0x6a, 0x0a, 0xa4, 0xc7, 0x78, - 0x91, 0x43, 0x02, 0x15, 0x7b, 0x63, 0x03, 0x25, 0xc7, 0x1d, 0x14, 0xb5, - 0x28, 0x90, 0x41, 0x4e, 0x34, 0x3d, 0x75, 0x13, 0x43, 0xb8, 0x83, 0x8e, - 0x0e, 0xa0, 0x23, 0x20, 0x93, 0xc3, 0xc4, 0x95, 0x6a, 0xae, 0xba, 0xc9, - 0xdd, 0x89, 0x68, 0xa3, 0x1a, 0xd4, 0x86, 0x41, 0x19, 0x42, 0x45, 0x51, - 0x20, 0x49, 0x08, 0x36, 0x08, 0x3e, 0x3d, 0x56, 0xb6, 0x12, 0x96, 0xf6, - 0xdc, 0xf5, 0xed, 0xcf, 0x98, 0xeb, 0xbe, 0xbe, 0x7a, 0xf5, 0x35, 0x97, - 0xaf, 0x6f, 0x1c, 0x79, 0x18, 0x3f, 0xd9, 0xf4, 0x8e, 0x57, 0x28, 0xa3, - 0xe2, 0x2a, 0x22, 0x07, 0x00, 0xe0, 0x6f, 0x2a, 0x56, 0xb0, 0x15, 0xc2, - 0x2f, 0x5a, 0x13, 0xfd, 0xb8, 0xc2, 0x1a, 0x08, 0x31, 0xeb, 0xc1, 0xc2, - 0x12, 0x36, 0x66, 0x19, 0x22, 0x9f, 0x8a, 0xfe, 0x2d, 0x81, 0x3e, 0x9e, - 0xaf, 0xab, 0xc2, 0x03, 0x3e, 0x88, 0x1f, 0xf8, 0x33, 0x60, 0x2f, 0xfe, - 0x7f, 0x88, 0x1e, 0x84, 0x6b, 0xfe, 0xe6, 0xac, 0x7a, 0x7a, 0x7b, 0x93, - 0x03, 0x32, 0x8c, 0xd5, 0xb3, 0x9c, 0xf9, 0xc8, 0x2e, 0x6f, 0x03, 0x39, - 0xcc, 0x0f, 0x7f, 0x52, 0x9a, 0x0a, 0x0a, 0x28, 0x74, 0x00, 0xff, 0xc7, - 0xc6, 0x61, 0x9f, 0xdf, 0xdc, 0x18, 0xc1, 0xd0, 0x03, 0x1c, 0xcc, 0x02, - 0x96, 0x0f, 0xf1, 0xf0, 0x07, 0x6e, 0x81, 0x93, 0xe0, 0x2d, 0x49, 0xf7, - 0x26, 0x70, 0x00, 0x2c, 0x17, 0xc7, 0xc3, 0x33, 0xaa, 0x17, 0x2d, 0x7f, - 0xe3, 0xe1, 0xdf, 0x7f, 0xb5, 0x0a, 0xd7, 0xc7, 0x7b, 0x23, 0xc3, 0xb8, - 0xaa, 0xac, 0x0a, 0x0a, 0x0a, 0x58, 0xd1, 0xba, 0x56, 0xe1, 0xcb, 0xc6, - 0x61, 0xd4, 0xa0, 0xb5, 0x3d, 0xcd, 0x4c, 0xdc, 0x74, 0x53, 0x69, 0x80, - 0xb9, 0x0f, 0xff, 0xf1, 0x50, 0x80, 0x27, 0x3f, 0xfc, 0x21, 0x1a, 0xce, - 0xec, 0x10, 0xe8, 0x00, 0x00, 0xa1, 0xb1, 0x33, 0x50, 0xc6, 0x34, 0x2a, - 0x89, 0x18, 0x49, 0xc7, 0x55, 0xf3, 0xe6, 0x56, 0xf3, 0xcc, 0xdf, 0xd7, - 0x8e, 0x39, 0xb9, 0xb9, 0xd3, 0x87, 0x1d, 0xea, 0xf3, 0x7a, 0xde, 0x6b, - 0x2e, 0xa7, 0x21, 0xd1, 0x93, 0x41, 0x9e, 0xfa, 0x47, 0xe1, 0x8a, 0xf3, - 0xdb, 0x79, 0xb3, 0x95, 0xf3, 0xb4, 0xcd, 0x3b, 0x4d, 0x02, 0x23, 0xb5, - 0x80, 0x35, 0x4e, 0x67, 0x51, 0x20, 0x69, 0x0f, 0x36, 0x4f, 0x29, 0x3d, - 0xb1, 0xc4, 0xc1, 0x2a, 0x49, 0xdd, 0xca, 0xa4, 0xa6, 0xc5, 0xec, 0x92, - 0xfd, 0xf1, 0x38, 0x11, 0x6c, 0xea, 0x15, 0x69, 0xca, 0x6a, 0xfd, 0x2b, - 0x16, 0xb8, 0x9d, 0x82, 0xc9, 0x6c, 0xd5, 0x56, 0x98, 0x7d, 0x92, 0xa5, - 0x7d, 0x34, 0xad, 0x16, 0x2a, 0xe7, 0xde, 0xeb, 0x4c, 0x23, 0x8f, 0x24, - 0xca, 0x0e, 0x76, 0x64, 0xad, 0x72, 0xe7, 0xba, 0xbb, 0x9d, 0x78, 0x48, - 0x8f, 0x1b, 0xd0, 0x97, 0x6d, 0xee, 0xbe, 0x99, 0xa2, 0x85, 0x67, 0xf6, - 0x5b, 0xf7, 0x7e, 0xfa, 0x37, 0x99, 0x8b, 0x24, 0xdb, 0x78, 0x06, 0x3a, - 0x03, 0x5f, 0x05, 0x21, 0x78, 0xbd, 0xa8, 0x40, 0x70, 0xfc, 0x95, 0xcf, - 0x29, 0xee, 0xdb, 0x2e, 0x19, 0xbc, 0x4e, 0x08, 0xce, 0x73, 0x3f, 0x97, - 0xcd, 0xe2, 0xeb, 0xdf, 0xde, 0x79, 0xaf, 0x04, 0xfa, 0xec, 0x4b, 0x44, - 0xc4, 0x11, 0xc0, 0xde, 0xb7, 0x61, 0x87, 0x01, 0xea, 0x13, 0x3c, 0x66, - 0xb8, 0xb5, 0x7c, 0xc0, 0x69, 0x79, 0xd3, 0x05, 0xfb, 0xb5, 0x73, 0x89, - 0xba, 0xb6, 0xd7, 0xbf, 0x43, 0x08, 0x79, 0x7c, 0x4a, 0xd7, 0x1d, 0x78, - 0xe7, 0xcf, 0x34, 0xdf, 0x9e, 0x3e, 0xde, 0x5a, 0x97, 0x3f, 0x3f, 0x4e, - 0x75, 0x1b, 0xb6, 0x4d, 0x72, 0x99, 0xaa, 0xaa, 0x0e, 0x29, 0x9d, 0xb7, - 0x49, 0xa3, 0x9c, 0x61, 0x00, 0x09, 0x56, 0xfe, 0xf6, 0x0e, 0x9c, 0xae, - 0xfa, 0x62, 0x9c, 0x49, 0x57, 0xeb, 0x56, 0x15, 0x6a, 0x67, 0x35, 0xdb, - 0x8b, 0xd8, 0x54, 0xa7, 0x76, 0x4b, 0x49, 0x88, 0x26, 0x22, 0x0c, 0xca, - 0x51, 0x7c, 0x29, 0x08, 0x27, 0x76, 0x01, 0x6d, 0x53, 0x29, 0x44, 0x58, - 0x0d, 0x11, 0xc0, 0xff, 0xf1, 0x50, 0x80, 0x27, 0xdf, 0xfc, 0x21, 0x1a, - 0xcf, 0xbc, 0x01, 0xcd, 0x00, 0x00, 0x9f, 0xb1, 0xb1, 0x1c, 0xca, 0xe6, - 0x0a, 0x15, 0x82, 0x89, 0x12, 0xf8, 0xce, 0xfe, 0x2b, 0x39, 0xae, 0xaf, - 0x3f, 0x7f, 0x4f, 0x1d, 0x66, 0xf5, 0xc7, 0xc7, 0xaf, 0x89, 0xbb, 0xaa, - 0x64, 0xab, 0xdd, 0x65, 0xd0, 0xd7, 0x19, 0x08, 0x9d, 0x64, 0xe0, 0xe8, - 0x25, 0xbe, 0x11, 0x22, 0xb7, 0x18, 0xce, 0x7c, 0xec, 0x5b, 0x72, 0x67, - 0x26, 0xb6, 0x14, 0xf4, 0xd5, 0x73, 0x98, 0xc6, 0x2d, 0xa9, 0x97, 0x7f, - 0x97, 0xc5, 0x51, 0x58, 0xbe, 0xce, 0x99, 0x62, 0xae, 0xec, 0x25, 0x5b, - 0x4e, 0xe8, 0xd4, 0x87, 0xa1, 0x40, 0xbb, 0xd8, 0x15, 0x50, 0x84, 0x9d, - 0xbc, 0x33, 0xbe, 0x19, 0x89, 0xef, 0xbb, 0xd6, 0x11, 0xc3, 0x20, 0x3a, - 0xa4, 0x02, 0xaa, 0xa9, 0xe2, 0xa2, 0x26, 0x75, 0x37, 0xdc, 0x0e, 0xe5, - 0xfc, 0x2f, 0xde, 0x9a, 0xb6, 0x3f, 0xd3, 0xbe, 0x52, 0x85, 0xdb, 0x33, - 0xe1, 0x7e, 0xaf, 0xee, 0x1f, 0xf5, 0x7d, 0xc0, 0xfc, 0x9f, 0x28, 0xf0, - 0xe0, 0xd3, 0x7f, 0xea, 0xa2, 0x94, 0x16, 0xf7, 0x8e, 0x1f, 0xa9, 0x71, - 0x85, 0x05, 0x7c, 0x04, 0x4e, 0xfa, 0x29, 0x12, 0x31, 0x9b, 0x58, 0x1c, - 0x74, 0x9e, 0x06, 0x3e, 0xa6, 0x4a, 0xfb, 0xda, 0xae, 0x0b, 0x3c, 0x6f, - 0x70, 0x6e, 0x60, 0xd7, 0xf9, 0x37, 0x1d, 0x0b, 0xcf, 0x70, 0x7d, 0x5b, - 0xac, 0x21, 0xe9, 0x9f, 0x74, 0x31, 0x6b, 0xd8, 0xfd, 0xff, 0x71, 0x56, - 0xb3, 0x4c, 0x06, 0xb1, 0x02, 0xa1, 0xea, 0x62, 0x82, 0x05, 0x2d, 0xe5, - 0x32, 0xfe, 0x39, 0x8b, 0x3d, 0x99, 0x19, 0x1c, 0xb1, 0x14, 0xaf, 0xb4, - 0x93, 0x29, 0xf9, 0x81, 0xaf, 0xe0, 0x26, 0x7c, 0x67, 0x7f, 0x1f, 0x2a, - 0xce, 0x9d, 0xfc, 0x67, 0xd7, 0x8e, 0xbc, 0x77, 0xf5, 0xc7, 0xaf, 0x8b, - 0xbb, 0xde, 0x15, 0x74, 0xc9, 0x48, 0x30, 0xf1, 0x98, 0x71, 0xfd, 0x02, - 0x63, 0x1c, 0x72, 0xd3, 0x30, 0xf8, 0xed, 0xb7, 0xaf, 0x1d, 0x58, 0x32, - 0xd9, 0x49, 0xe6, 0x64, 0xaa, 0x8a, 0xc5, 0xbf, 0x84, 0x6c, 0x70, 0x4a, - 0x97, 0xd5, 0xbc, 0x34, 0xc6, 0xef, 0x06, 0xe9, 0x57, 0x7f, 0x10, 0x61, - 0x2d, 0xe8, 0x46, 0x22, 0xf1, 0x4b, 0x77, 0xdc, 0x1c, 0xff, 0xf1, 0x50, - 0x80, 0x28, 0x1f, 0xfc, 0x21, 0x1a, 0xcd, 0x00, 0xc0, 0x38, 0x00, 0x01, - 0x9f, 0xb1, 0xb2, 0xd5, 0x29, 0x31, 0x08, 0xea, 0x47, 0x7f, 0x8f, 0x15, - 0xdd, 0x7c, 0x4d, 0xfe, 0xbe, 0xab, 0x7c, 0x6e, 0xbe, 0xaa, 0x3c, 0xb9, - 0xd3, 0x14, 0xcb, 0xdb, 0x35, 0xb1, 0xa5, 0xad, 0x00, 0x66, 0xcc, 0xf9, - 0x92, 0x94, 0x62, 0xba, 0x06, 0x2a, 0xbc, 0x71, 0xd0, 0xd4, 0x46, 0x33, - 0x34, 0x01, 0x07, 0xa0, 0x90, 0xbd, 0x15, 0x0a, 0x29, 0x4c, 0x65, 0xe5, - 0xc8, 0x7b, 0xbc, 0x2a, 0xad, 0x1a, 0xe7, 0x70, 0xa6, 0xe7, 0xe2, 0x06, - 0x7a, 0x22, 0x6a, 0xce, 0x72, 0x39, 0xf6, 0x28, 0x07, 0x81, 0xfc, 0x22, - 0x2c, 0xaa, 0x73, 0x42, 0x6e, 0xad, 0x15, 0xdf, 0x51, 0xa6, 0x72, 0xbc, - 0x93, 0x74, 0xf7, 0x7a, 0xc3, 0xff, 0x56, 0x73, 0x37, 0x40, 0xc2, 0x4b, - 0x85, 0x8d, 0x8b, 0x61, 0xef, 0xfe, 0xfd, 0x5b, 0x24, 0x7f, 0xea, 0xb2, - 0xe5, 0x5f, 0x41, 0x7d, 0x06, 0xfe, 0x94, 0x2b, 0xd0, 0x40, 0x31, 0xd5, - 0x1b, 0x1c, 0x4b, 0xd5, 0x35, 0x39, 0x2f, 0x4f, 0xcb, 0xfa, 0x3b, 0xea, - 0x20, 0xd8, 0xd9, 0xc7, 0xf6, 0x3a, 0x52, 0xbe, 0x41, 0x54, 0x3d, 0x33, - 0x56, 0x55, 0x23, 0xac, 0xaf, 0xe7, 0xe2, 0x04, 0x28, 0xa7, 0x5a, 0x8d, - 0xfe, 0xb4, 0x12, 0xee, 0xcf, 0x73, 0xb2, 0x01, 0x52, 0xaf, 0x05, 0xd9, - 0x2d, 0xe9, 0xae, 0x80, 0x00, 0x54, 0x52, 0x44, 0xae, 0x21, 0x76, 0x32, - 0x2c, 0xa2, 0x5c, 0x0e, 0xb5, 0x64, 0x27, 0x96, 0x33, 0x39, 0x2c, 0x50, - 0xc9, 0xca, 0xad, 0xc2, 0xc6, 0xd6, 0x16, 0x1d, 0x92, 0xbe, 0xd9, 0xdc, - 0x4f, 0xd8, 0x99, 0x2a, 0xa3, 0x37, 0xc1, 0x97, 0x26, 0xb5, 0xf6, 0xf8, - 0xee, 0xf2, 0xb4, 0xef, 0xcb, 0xeb, 0x35, 0xe2, 0xbe, 0xbd, 0x6f, 0xcd, - 0xc9, 0xcd, 0xe5, 0xd5, 0x23, 0x1a, 0xe4, 0x16, 0xfb, 0x6c, 0x76, 0x86, - 0xa1, 0x2d, 0xd7, 0x73, 0x4a, 0xd6, 0x39, 0x7f, 0x61, 0x3f, 0x29, 0x6c, - 0xb6, 0xd4, 0x28, 0x38, 0x7c, 0xd6, 0x57, 0x47, 0x57, 0xeb, 0xfb, 0x64, - 0xf4, 0xc9, 0x50, 0xbe, 0x7c, 0x19, 0xac, 0xce, 0xbb, 0xe3, 0x2c, 0xca, - 0xb9, 0xf4, 0xae, 0x24, 0x1e, 0x9b, 0xd5, 0xab, 0x7f, 0xa9, 0x8c, 0x22, - 0x06, 0xb7, 0x93, 0x49, 0xc0, 0xff, 0xf1, 0x50, 0x80, 0x3b, 0x9f, 0xfc, - 0x20, 0xa1, 0x2a, 0xd6, 0x96, 0x52, 0x49, 0x8c, 0x06, 0xb6, 0x00, 0x00, - 0x0d, 0x35, 0x38, 0xe7, 0x1e, 0xde, 0xcb, 0x58, 0x26, 0x41, 0xe4, 0x30, - 0x52, 0xfe, 0x01, 0xda, 0x9c, 0xd1, 0x4c, 0x53, 0x91, 0x46, 0x81, 0x53, - 0x8e, 0x32, 0x48, 0x9c, 0xc6, 0xb7, 0x94, 0x45, 0xbe, 0x5a, 0x08, 0x1d, - 0x95, 0x7f, 0xe4, 0xb0, 0x89, 0x0d, 0x2d, 0xdd, 0x53, 0xe7, 0x29, 0xa8, - 0xc4, 0x14, 0x02, 0xf6, 0x7f, 0x72, 0x51, 0xd9, 0x6e, 0x53, 0xdd, 0xfe, - 0x7c, 0x47, 0xf2, 0x0c, 0x40, 0x47, 0x7c, 0x47, 0xaf, 0xc2, 0x36, 0x02, - 0xc0, 0x04, 0x60, 0x96, 0xdf, 0x03, 0xe3, 0xde, 0x47, 0x53, 0xce, 0x76, - 0x67, 0xb3, 0x7b, 0xa6, 0xdd, 0x84, 0x5c, 0x3c, 0x3d, 0x25, 0x71, 0xf6, - 0xb6, 0xed, 0xc7, 0x1d, 0xd1, 0x15, 0xdd, 0xdd, 0x8a, 0x8d, 0xe4, 0xe8, - 0xf2, 0x79, 0x9b, 0x73, 0xef, 0x6a, 0x45, 0x1f, 0x53, 0x1a, 0x81, 0xb3, - 0xf5, 0xc8, 0xd3, 0x6c, 0x3d, 0xcc, 0x1f, 0xa2, 0x23, 0x33, 0x22, 0x0c, - 0x2c, 0x78, 0x7f, 0x2c, 0xfa, 0x98, 0x45, 0x7c, 0x88, 0xed, 0xd6, 0x23, - 0x30, 0x23, 0x11, 0x16, 0x23, 0x5b, 0xb1, 0x48, 0x7a, 0xe3, 0x78, 0xe9, - 0xb6, 0x00, 0x5d, 0xf4, 0x28, 0x2d, 0xb3, 0x82, 0x18, 0x82, 0xf5, 0x51, - 0x84, 0xf3, 0xcf, 0xfd, 0x69, 0x49, 0xa7, 0x14, 0x8b, 0x81, 0x51, 0x68, - 0xbe, 0x4d, 0x96, 0x55, 0x96, 0x46, 0x59, 0x7f, 0x5a, 0x6c, 0xaf, 0x2f, - 0xad, 0xcd, 0x77, 0xc5, 0xad, 0xb4, 0xa6, 0x7f, 0x9e, 0xce, 0xff, 0x5d, - 0x31, 0x88, 0xbd, 0xab, 0x66, 0x9f, 0x51, 0xdd, 0xe1, 0xd3, 0xff, 0x4b, - 0xc7, 0x1f, 0x95, 0xcd, 0xd1, 0xbc, 0x76, 0xc9, 0xb3, 0x2c, 0x86, 0x90, - 0x6b, 0x52, 0x19, 0x09, 0x26, 0x0a, 0x0d, 0x8a, 0x0f, 0x6d, 0xf3, 0xaa, - 0x9b, 0x6b, 0x61, 0x78, 0x0e, 0x35, 0xbe, 0xa7, 0x3e, 0xdb, 0xe3, 0xbb, - 0xd4, 0xea, 0xae, 0xc4, 0x7c, 0x1f, 0x4b, 0xea, 0xd9, 0x46, 0x30, 0x87, - 0x85, 0x07, 0x0e, 0x1c, 0x46, 0xe6, 0x55, 0x2b, 0x42, 0xa8, 0x90, 0x5a, - 0x52, 0x18, 0x83, 0x1d, 0x2f, 0x42, 0x00, 0x40, 0x45, 0xda, 0x5c, 0x07, - 0x6c, 0x16, 0x00, 0x0f, 0xdb, 0x9c, 0x3e, 0x39, 0x9b, 0x80, 0x1f, 0xef, - 0x01, 0xed, 0xc0, 0xab, 0x4f, 0xb8, 0x7b, 0x6f, 0x6b, 0x8f, 0x08, 0x1e, - 0x47, 0xa7, 0x00, 0xae, 0x60, 0xfb, 0xd9, 0x24, 0xac, 0x58, 0x60, 0xc3, - 0x75, 0xd1, 0x9c, 0x3f, 0x4a, 0xba, 0x37, 0xc0, 0x39, 0x4f, 0x89, 0x12, - 0xbb, 0x75, 0xfa, 0x08, 0xee, 0xaf, 0x03, 0xe0, 0xe1, 0x16, 0xdd, 0x2c, - 0xc9, 0xf7, 0x73, 0x07, 0xe9, 0x47, 0x51, 0xef, 0x29, 0xbd, 0x43, 0xff, - 0xbf, 0xd4, 0xd2, 0x09, 0x9c, 0x00, 0x7c, 0x08, 0xd2, 0x1b, 0x31, 0x93, - 0x14, 0x19, 0x1c, 0xa4, 0xbb, 0xd1, 0x6e, 0x08, 0x7a, 0x9b, 0xb8, 0xfa, - 0x91, 0xbb, 0x6b, 0x30, 0x1d, 0x41, 0x1d, 0x4e, 0x20, 0xa0, 0x6d, 0x00, - 0x27, 0xc0, 0xb0, 0xca, 0x3d, 0x30, 0x48, 0x0d, 0x99, 0x64, 0x8a, 0x30, - 0xd2, 0x83, 0xb6, 0x6a, 0xd5, 0xd1, 0x68, 0x07, 0x2c, 0x70, 0xa0, 0xea, - 0xaf, 0xe7, 0x4d, 0x55, 0xd1, 0x6a, 0x42, 0xa5, 0x7b, 0x70, 0xa2, 0x77, - 0xbc, 0x92, 0x7b, 0x66, 0x96, 0xaf, 0xf5, 0x4f, 0x96, 0x57, 0xe9, 0x99, - 0xe2, 0x3a, 0x59, 0xbb, 0xab, 0x5f, 0x4e, 0xa4, 0xb2, 0x4c, 0x8d, 0x28, - 0xe0, 0xff, 0xf1, 0x50, 0x80, 0x3a, 0x7f, 0xfc, 0x20, 0xa7, 0x4c, 0xd9, - 0x45, 0x18, 0x90, 0xba, 0x40, 0xd5, 0x8a, 0xc5, 0x0c, 0x7b, 0x4c, 0xba, - 0x03, 0xa9, 0xf4, 0xbe, 0x2b, 0xad, 0x07, 0xfd, 0xa7, 0xb4, 0xd7, 0xcf, - 0x16, 0xb3, 0xf3, 0x28, 0x09, 0x22, 0xef, 0xaf, 0xe0, 0x07, 0x1d, 0xf6, - 0x90, 0x39, 0x23, 0x3c, 0x3b, 0x41, 0x0b, 0xc3, 0x0b, 0x5f, 0x07, 0x57, - 0xcd, 0x9d, 0x86, 0xc1, 0x07, 0x8b, 0xcc, 0x01, 0xba, 0x1d, 0x51, 0xc2, - 0xed, 0xad, 0xb7, 0x2b, 0xa2, 0xe0, 0xf1, 0x85, 0x48, 0x05, 0x50, 0x27, - 0x9c, 0xb9, 0x69, 0x7b, 0x75, 0x3c, 0xb8, 0xc3, 0xf5, 0x4b, 0x25, 0x8e, - 0x11, 0x6d, 0x18, 0x96, 0xa1, 0x31, 0x34, 0x86, 0x92, 0xa9, 0x93, 0x0a, - 0x8f, 0x35, 0xb2, 0xf0, 0xcd, 0x0c, 0x99, 0xa2, 0xa7, 0x0c, 0x9a, 0xbe, - 0xcf, 0x26, 0x61, 0x84, 0xc2, 0xe9, 0x58, 0xe4, 0xe5, 0x63, 0xa0, 0xde, - 0x39, 0x93, 0xf2, 0x8b, 0xdc, 0xf4, 0x7c, 0x7f, 0xfb, 0x6e, 0x68, 0x15, - 0x34, 0x9b, 0xb3, 0x09, 0x89, 0x41, 0xad, 0xff, 0xdf, 0xfd, 0xed, 0x71, - 0x6e, 0x5e, 0x27, 0x71, 0x60, 0x16, 0x82, 0x5e, 0xdf, 0xd6, 0x89, 0xa3, - 0x06, 0xb2, 0x89, 0x6e, 0x38, 0x11, 0x80, 0x5f, 0xfb, 0x76, 0x2a, 0xdf, - 0x94, 0x52, 0x0c, 0x96, 0x65, 0x40, 0xea, 0xd2, 0x60, 0x87, 0x2b, 0x69, - 0x50, 0x65, 0x30, 0x49, 0x77, 0x13, 0x76, 0xf6, 0x59, 0x99, 0x77, 0xe2, - 0x55, 0xa5, 0x26, 0xde, 0xe2, 0x2b, 0xf5, 0x0f, 0x48, 0xc4, 0xa6, 0xd5, - 0xa6, 0x5d, 0xfa, 0x84, 0x67, 0xfc, 0x93, 0xf0, 0x86, 0x5d, 0x99, 0xf6, - 0x0f, 0xee, 0x70, 0x13, 0x46, 0x39, 0x80, 0xce, 0xe8, 0x42, 0x79, 0x42, - 0x2c, 0x06, 0xb5, 0x0d, 0x8e, 0xaa, 0x49, 0x31, 0x42, 0xf5, 0xf1, 0xfb, - 0x7b, 0x7e, 0xbf, 0xea, 0x71, 0xcf, 0x9f, 0x17, 0x27, 0x0f, 0x3c, 0xdc, - 0xba, 0xc9, 0x4a, 0xcd, 0x6a, 0x55, 0xc3, 0xa0, 0x9f, 0xcd, 0xf9, 0x9d, - 0xca, 0x10, 0x95, 0xbc, 0x62, 0x88, 0xbb, 0x69, 0xd2, 0x32, 0xf2, 0xfd, - 0xad, 0x7f, 0xcf, 0xf2, 0xdd, 0x0c, 0xfe, 0xae, 0x15, 0xb2, 0x12, 0xe9, - 0xdc, 0x9c, 0x64, 0x2b, 0x58, 0x80, 0x37, 0x2a, 0x50, 0x7e, 0xaf, 0x5c, - 0xf7, 0xd3, 0x5b, 0xc6, 0xf7, 0x6d, 0x48, 0x26, 0x25, 0x30, 0x40, 0xd0, - 0x8d, 0xf3, 0x40, 0x8f, 0xd4, 0xc5, 0x79, 0x7b, 0xe1, 0xe5, 0x23, 0x63, - 0xea, 0x3b, 0x9e, 0x84, 0xa8, 0x95, 0x1f, 0x7e, 0x9b, 0x57, 0x72, 0x6b, - 0x51, 0xee, 0x10, 0x1b, 0x47, 0x52, 0xc1, 0x8c, 0x74, 0x30, 0xf4, 0x95, - 0xc1, 0xd3, 0xb6, 0xf6, 0xb6, 0x00, 0x47, 0x3b, 0x8a, 0xc4, 0xfc, 0x01, - 0x77, 0xf7, 0xfa, 0x0f, 0x68, 0x04, 0x38, 0xc1, 0x63, 0x6c, 0x5b, 0x5e, - 0x93, 0x22, 0xf0, 0xa4, 0x6d, 0x80, 0x92, 0xca, 0xc5, 0x46, 0x03, 0xcd, - 0x89, 0x25, 0xbf, 0xcd, 0xe9, 0xd6, 0x0b, 0xfa, 0x78, 0x56, 0x0c, 0x71, - 0x8d, 0x3c, 0x3f, 0xe0, 0x04, 0xeb, 0xb7, 0xd2, 0x27, 0x18, 0xd5, 0x71, - 0x78, 0x45, 0x43, 0x49, 0x56, 0x0c, 0x75, 0x28, 0x45, 0xc1, 0x7a, 0xa9, - 0x5c, 0xca, 0xb2, 0xc6, 0x42, 0xb4, 0xdb, 0x6c, 0xe8, 0x27, 0x15, 0xd2, - 0x36, 0x82, 0x11, 0xe6, 0x20, 0x13, 0xa0, 0xab, 0x65, 0x6b, 0x53, 0x50, - 0xc7, 0x97, 0x3d, 0x0f, 0x87, 0x81, 0x38, 0xd1, 0x45, 0xca, 0x64, 0x5c, - 0xff, 0xf1, 0x50, 0x80, 0x38, 0x5f, 0xfc, 0x20, 0xa7, 0x7a, 0xd6, 0xd5, - 0x49, 0x31, 0x0e, 0xc2, 0x06, 0xb6, 0x79, 0xf0, 0x5e, 0x04, 0xa7, 0x1b, - 0x5c, 0xae, 0xbb, 0x99, 0xd5, 0x79, 0xf1, 0xd6, 0xfc, 0xb7, 0xc7, 0xb0, - 0xfe, 0x7b, 0xa5, 0x1d, 0x97, 0x59, 0x3b, 0x6f, 0x45, 0xa1, 0xd3, 0x73, - 0x60, 0xb2, 0xce, 0x0a, 0x80, 0xfb, 0x88, 0xe8, 0x8c, 0x93, 0x0c, 0x30, - 0xdb, 0xca, 0xfc, 0x22, 0x20, 0x64, 0x99, 0x33, 0xff, 0x95, 0x6d, 0x57, - 0xfd, 0xbf, 0xd7, 0x18, 0xc6, 0x2a, 0xeb, 0x08, 0xcd, 0x77, 0x57, 0xcf, - 0xe9, 0xe4, 0x30, 0xbd, 0xc1, 0x2c, 0x3a, 0x57, 0x49, 0xbe, 0xb1, 0xf3, - 0x70, 0xcf, 0x08, 0xe3, 0x02, 0x74, 0x20, 0xa3, 0x7d, 0x2e, 0x62, 0xaf, - 0x46, 0x85, 0x84, 0xb4, 0x46, 0xe9, 0xb9, 0xd2, 0x74, 0x06, 0x4d, 0xcd, - 0xf9, 0xf7, 0x6b, 0x28, 0xf7, 0x0e, 0x94, 0x2d, 0xfa, 0x01, 0xa0, 0x98, - 0x8e, 0xde, 0x6a, 0xd2, 0x43, 0x37, 0xb8, 0x6e, 0x57, 0x48, 0x76, 0xfa, - 0x3f, 0xce, 0xb6, 0x7b, 0xe8, 0x86, 0x5b, 0x08, 0x70, 0x50, 0x24, 0xfa, - 0xd8, 0x58, 0x5c, 0xb6, 0x2c, 0x43, 0xd5, 0x4a, 0x74, 0x96, 0xb9, 0x2c, - 0xa0, 0x58, 0xa2, 0x13, 0xf3, 0x20, 0x95, 0x1d, 0x70, 0x50, 0x42, 0x98, - 0xfd, 0xe8, 0x56, 0xc8, 0x74, 0x51, 0x4e, 0x32, 0xee, 0x05, 0xc2, 0xe9, - 0xa3, 0x5c, 0xaf, 0xec, 0x57, 0xb9, 0x69, 0xa1, 0xaa, 0x08, 0x66, 0x22, - 0xa6, 0xd5, 0xd6, 0xe0, 0xc5, 0x5d, 0x40, 0x58, 0x61, 0x4c, 0x79, 0x3a, - 0x5d, 0xc1, 0xa4, 0xe5, 0x44, 0xa3, 0x6a, 0xce, 0x4f, 0xae, 0xac, 0x6b, - 0x58, 0x99, 0x16, 0x25, 0x2a, 0x55, 0x8a, 0x0e, 0x2b, 0x7a, 0xbe, 0xc3, - 0xdb, 0xd4, 0xa1, 0xed, 0xeb, 0xa4, 0xf6, 0xe7, 0x26, 0x8b, 0x94, 0xd6, - 0xb9, 0xe3, 0x43, 0x4f, 0x9f, 0x43, 0x70, 0xf6, 0x7c, 0x52, 0xdd, 0x72, - 0xa1, 0x58, 0xf5, 0x15, 0xd2, 0x0b, 0xce, 0x02, 0xb3, 0xb4, 0xc1, 0x48, - 0x88, 0x88, 0x6c, 0xe6, 0xbb, 0xf2, 0x52, 0x97, 0xf3, 0xb8, 0xab, 0x6b, - 0xee, 0x31, 0x3d, 0x03, 0xac, 0xbb, 0x95, 0x52, 0xe1, 0xfc, 0x26, 0x2b, - 0xbe, 0xc8, 0xf4, 0x30, 0x72, 0x4c, 0x1f, 0x31, 0x78, 0xd6, 0x0d, 0xc7, - 0x08, 0xb1, 0x19, 0x14, 0x33, 0x2a, 0x13, 0x59, 0xe5, 0x2a, 0xff, 0x5a, - 0xb2, 0x55, 0x02, 0x85, 0x21, 0xaf, 0xca, 0xa6, 0x0b, 0x35, 0x71, 0x36, - 0x39, 0x05, 0x5b, 0x2e, 0x6e, 0xac, 0x1d, 0xf8, 0x54, 0x55, 0x41, 0x54, - 0x34, 0x59, 0xa4, 0xb2, 0x03, 0x0b, 0x33, 0x0c, 0x88, 0xb2, 0x2b, 0xd6, - 0xe0, 0x51, 0x40, 0xd9, 0xf2, 0x90, 0xc3, 0xae, 0x6b, 0x9e, 0xa0, 0x4d, - 0x41, 0xf6, 0x53, 0x77, 0x62, 0x3d, 0xe7, 0x80, 0xdf, 0xab, 0xca, 0x3a, - 0xba, 0xa4, 0x12, 0x7d, 0x86, 0x8b, 0xf9, 0x5a, 0xf7, 0x43, 0x0c, 0xb7, - 0x7a, 0xa4, 0xc0, 0x19, 0x57, 0x2a, 0xc9, 0x85, 0xf0, 0x77, 0xc2, 0xb2, - 0x90, 0xf1, 0x4a, 0xe1, 0xc6, 0x10, 0xe6, 0x25, 0xeb, 0x4e, 0xd7, 0x10, - 0x33, 0xb8, 0x19, 0x9d, 0xb8, 0xba, 0x19, 0x56, 0xac, 0x1a, 0xf1, 0xb6, - 0x4c, 0x04, 0xd8, 0x0a, 0xfc, 0x62, 0xcf, 0x84, 0xeb, 0x7e, 0xba, 0x19, - 0x91, 0x6e, 0x6b, 0x42, 0xf6, 0xe0, 0xff, 0xf1, 0x50, 0x80, 0x26, 0xdf, - 0xfc, 0x21, 0x1a, 0xcf, 0x3a, 0x70, 0x2a, 0x00, 0xc0, 0xa2, 0xb6, 0xb1, - 0xdc, 0x46, 0x62, 0x53, 0x14, 0x0e, 0x39, 0x00, 0x00, 0xeb, 0xb3, 0xac, - 0xae, 0x25, 0x5e, 0x4e, 0x3a, 0xbe, 0x9b, 0x13, 0xf9, 0x2d, 0xf9, 0xde, - 0xa7, 0x43, 0xd0, 0xfe, 0x53, 0x86, 0xb7, 0xea, 0x80, 0x50, 0x6f, 0x9d, - 0x11, 0x82, 0x91, 0x28, 0xe9, 0x87, 0x87, 0x35, 0x3e, 0x0e, 0x0b, 0xc4, - 0x44, 0x42, 0x14, 0x3c, 0x76, 0x8a, 0x1a, 0x64, 0x01, 0xb2, 0x78, 0x51, - 0x01, 0x5a, 0x3d, 0x68, 0x03, 0x1b, 0x10, 0xd2, 0xf0, 0x48, 0x83, 0x37, - 0x17, 0xab, 0x99, 0xe8, 0xef, 0x76, 0x54, 0xb2, 0x55, 0xa7, 0x6e, 0x7c, - 0xe0, 0xfb, 0x47, 0x0a, 0x55, 0xd2, 0x2c, 0x9d, 0x70, 0xce, 0xf8, 0xea, - 0x99, 0x69, 0x12, 0x26, 0x98, 0x39, 0x3c, 0x95, 0x27, 0x1c, 0xc0, 0xc0, - 0xf0, 0x42, 0x80, 0x1c, 0x0a, 0xb1, 0x87, 0x49, 0x31, 0x50, 0x4a, 0xdc, - 0x43, 0x9f, 0xc4, 0x2f, 0x61, 0xa2, 0x51, 0x0a, 0x46, 0x30, 0x3a, 0x83, - 0xca, 0x98, 0x40, 0x52, 0xbd, 0x4e, 0x81, 0xd1, 0xdc, 0x47, 0xcc, 0xec, - 0xaa, 0x3d, 0x10, 0x7e, 0xb1, 0x3f, 0xc4, 0xeb, 0x58, 0xe6, 0x6a, 0x28, - 0xc6, 0xec, 0x0e, 0xa0, 0x20, 0xc5, 0x39, 0xc1, 0xdf, 0x4a, 0xbd, 0x9a, - 0x98, 0xd3, 0x5a, 0xd9, 0xba, 0x2b, 0x1f, 0x1a, 0x38, 0xc3, 0xca, 0xde, - 0x6f, 0x43, 0x58, 0xea, 0x34, 0x5c, 0x72, 0x55, 0x2f, 0x97, 0xdc, 0x11, - 0xc0, 0x8e, 0x8b, 0x2e, 0x97, 0x50, 0x9c, 0x4b, 0x4c, 0xce, 0xf4, 0x50, - 0x52, 0x25, 0x8a, 0x5f, 0xf1, 0x1c, 0xb8, 0xe3, 0x9e, 0x78, 0xe5, 0xc1, - 0xc8, 0x38, 0xe7, 0x8e, 0x78, 0xc9, 0x72, 0x95, 0x31, 0x2d, 0x35, 0xba, - 0x09, 0x8c, 0xbc, 0xba, 0xd8, 0x49, 0xbb, 0xcc, 0x36, 0xca, 0x5a, 0xa0, - 0x10, 0x98, 0x5a, 0xce, 0x02, 0x4f, 0xcc, 0xee, 0xa2, 0x6b, 0x47, 0x83, - 0x55, 0x77, 0x8e, 0xec, 0x38, 0xf7, 0xb6, 0x69, 0x9f, 0x03, 0xef, 0x90, - 0xda, 0xe2, 0x65, 0xaf, 0x40, 0x6e, 0xd2, 0x51, 0x4c, 0x3c, 0xac, 0xdc, - 0x36, 0xdc, 0xd4, 0x20, 0xce, 0x57, 0x14, 0x34, 0x73, 0xbb, 0xb0, 0x94, - 0x8a, 0x12, 0x23, 0x17, 0xff, 0xf1, 0x50, 0x80, 0x2a, 0x3f, 0xfc, 0x21, - 0x1a, 0xcf, 0xf0, 0x20, 0x00, 0xde, 0xfe, 0x9c, 0xb6, 0xc1, 0xe8, 0x2c, - 0x27, 0x28, 0xad, 0x06, 0xc1, 0x00, 0x00, 0x00, 0x01, 0x35, 0xbb, 0xdc, - 0xe2, 0xf8, 0xbd, 0x7b, 0x09, 0xde, 0x51, 0x16, 0xe5, 0xa5, 0xab, 0xb2, - 0xbe, 0x7a, 0x87, 0x49, 0xdd, 0x8b, 0x51, 0x97, 0x5a, 0x1c, 0x55, 0xcc, - 0x4b, 0x09, 0x91, 0x4b, 0x46, 0x8f, 0x44, 0x44, 0xb4, 0x78, 0xb6, 0xc1, - 0x17, 0x12, 0x5f, 0x09, 0xa7, 0xd6, 0x59, 0xa9, 0xf0, 0xdb, 0xf1, 0xd7, - 0x68, 0x25, 0xdd, 0x48, 0xe7, 0x25, 0x68, 0xe5, 0x82, 0x0a, 0x78, 0x04, - 0x75, 0x27, 0xf6, 0x5e, 0xfb, 0x89, 0x86, 0x94, 0x71, 0x0a, 0xac, 0x66, - 0x40, 0x47, 0x12, 0xb0, 0x8c, 0x36, 0xa3, 0x26, 0xbc, 0x8e, 0x54, 0xb4, - 0xcc, 0xdc, 0x44, 0xe9, 0x24, 0x5e, 0xe5, 0xf7, 0xdb, 0x2d, 0x9e, 0xec, - 0x26, 0x93, 0x26, 0x39, 0x4e, 0x34, 0x9b, 0xcb, 0x76, 0xd6, 0x8a, 0xa4, - 0x89, 0x06, 0x13, 0x91, 0x6b, 0xd4, 0xa1, 0xaa, 0x51, 0x14, 0xcb, 0x0e, - 0xec, 0x02, 0x72, 0xc1, 0x46, 0x5a, 0x9e, 0x1b, 0xd6, 0x62, 0x58, 0xe5, - 0x49, 0xd2, 0x2a, 0x45, 0x43, 0xd7, 0x08, 0x6e, 0x85, 0x3e, 0xd3, 0xe8, - 0x4f, 0xe9, 0x4c, 0x4e, 0xd6, 0xa2, 0x77, 0x68, 0x42, 0x92, 0x97, 0xd2, - 0x10, 0xd4, 0xb6, 0x3a, 0x77, 0xe0, 0x4f, 0x8f, 0x1b, 0xac, 0x77, 0x12, - 0x55, 0x81, 0x22, 0x0e, 0xba, 0x07, 0xfc, 0xd1, 0x62, 0x24, 0x68, 0xd5, - 0x4c, 0xd9, 0x6f, 0xf4, 0xfd, 0x50, 0x13, 0xc9, 0xd9, 0x37, 0x61, 0x79, - 0x17, 0x22, 0xab, 0x74, 0x38, 0xe3, 0x44, 0xe5, 0xb5, 0x85, 0x2b, 0xe8, - 0x30, 0x40, 0x7a, 0x07, 0xb7, 0xa0, 0x06, 0xb1, 0x9a, 0xcd, 0x64, 0xbc, - 0xa9, 0x75, 0xa9, 0xbf, 0xfa, 0x84, 0x2f, 0x1c, 0x5f, 0xf4, 0xb9, 0xa2, - 0x9a, 0xb4, 0x49, 0x90, 0xa5, 0x25, 0x03, 0x11, 0xe6, 0xf6, 0x62, 0x23, - 0x18, 0xc3, 0x15, 0xf3, 0xb2, 0xe9, 0x61, 0xc0, 0xa2, 0xc4, 0x8a, 0x29, - 0xce, 0xdc, 0x48, 0xda, 0x8b, 0xaf, 0x6e, 0x9b, 0x1d, 0x7f, 0x44, 0xfe, - 0x3d, 0x7b, 0x6b, 0x4b, 0x5e, 0xa5, 0x84, 0xc3, 0x76, 0x06, 0x45, 0x5e, - 0xdf, 0x60, 0x89, 0x9b, 0xb4, 0x66, 0x44, 0xc8, 0x80, 0x6f, 0x15, 0x85, - 0xd2, 0xa1, 0x03, 0x09, 0x58, 0x54, 0xe2, 0x49, 0xcb, 0x49, 0x3c, 0x65, - 0x47, 0x8c, 0xa2, 0x01, 0x1c, 0xff, 0xf1, 0x50, 0x80, 0x2a, 0x5f, 0xfc, - 0x21, 0x1a, 0xcf, 0xe0, 0x92, 0xeb, 0x20, 0x00, 0xa1, 0xb6, 0xaa, 0x84, - 0x68, 0x66, 0x0a, 0x11, 0x8c, 0x07, 0xe3, 0xf2, 0x00, 0x07, 0xb7, 0xa9, - 0x5f, 0x5b, 0xeb, 0x34, 0xeb, 0xbb, 0xdc, 0xf6, 0xdd, 0xaf, 0x42, 0x4f, - 0x24, 0x9a, 0x9b, 0x3a, 0x3c, 0xbf, 0x43, 0x43, 0x8e, 0xa9, 0x91, 0xd8, - 0x15, 0x0e, 0x3a, 0x45, 0x56, 0x5d, 0x9c, 0x4f, 0x80, 0x2f, 0x00, 0x77, - 0xe4, 0x22, 0x38, 0xcb, 0x53, 0x34, 0x40, 0x4b, 0x7f, 0xe8, 0x73, 0x93, - 0x3b, 0x8d, 0xc1, 0xbf, 0xc3, 0xa6, 0x2e, 0x23, 0x5d, 0xb9, 0x98, 0xdc, - 0x2a, 0x17, 0x56, 0x47, 0x1f, 0xdf, 0xdb, 0x8c, 0x82, 0x6e, 0x18, 0x87, - 0x1c, 0x8d, 0x4e, 0xbf, 0xbe, 0xf0, 0x29, 0x16, 0xab, 0x6d, 0x6c, 0x63, - 0x83, 0xac, 0x4b, 0xdd, 0x7b, 0xe0, 0x94, 0x9b, 0xa7, 0x6f, 0x67, 0x9c, - 0x82, 0xbc, 0xeb, 0x27, 0x8e, 0x44, 0xa9, 0x85, 0x41, 0x6b, 0x10, 0x6f, - 0xd5, 0x0a, 0x47, 0x93, 0x33, 0xf2, 0xd8, 0xf6, 0x39, 0xa2, 0x8a, 0x70, - 0x57, 0x3a, 0x10, 0x98, 0x10, 0x68, 0xb7, 0xa4, 0x54, 0x1a, 0x21, 0xb8, - 0x1a, 0x34, 0x98, 0xda, 0x30, 0x20, 0xdf, 0x83, 0x7b, 0x2d, 0x97, 0x61, - 0xa2, 0xd9, 0xa6, 0x48, 0x89, 0xc8, 0x23, 0x21, 0x3e, 0x30, 0x9f, 0x34, - 0x70, 0xce, 0xf5, 0x3c, 0x29, 0x40, 0xc6, 0xe6, 0xa7, 0x26, 0x6d, 0x73, - 0xc0, 0x5b, 0x43, 0xa1, 0x44, 0x11, 0x02, 0x23, 0x9e, 0xf1, 0x8b, 0x06, - 0xdb, 0xd2, 0x31, 0x14, 0x04, 0xac, 0x18, 0x80, 0x92, 0xae, 0xf5, 0xb8, - 0x44, 0xef, 0x78, 0x3f, 0xee, 0x09, 0xec, 0x83, 0xb5, 0xeb, 0x79, 0xe8, - 0x17, 0x89, 0x45, 0x55, 0xfc, 0xfa, 0x3d, 0xd2, 0x2d, 0x50, 0x14, 0x2e, - 0xab, 0x0c, 0x9c, 0xd3, 0xf0, 0x02, 0xfc, 0x4a, 0x71, 0xcb, 0x8e, 0x5c, - 0x73, 0xc1, 0x2b, 0xae, 0xfe, 0x97, 0x96, 0x55, 0x4a, 0xe3, 0x22, 0xa0, - 0x47, 0xbb, 0xff, 0x3e, 0x6c, 0xb5, 0xd3, 0x1b, 0x5a, 0x7d, 0x3f, 0xe0, - 0x9c, 0x77, 0x80, 0x1c, 0x92, 0x8a, 0x7e, 0xd1, 0x71, 0x51, 0x3e, 0xe5, - 0xad, 0x35, 0x2b, 0x6c, 0xd0, 0xce, 0x72, 0x2f, 0xd5, 0x3e, 0xad, 0xa2, - 0x8c, 0x71, 0xe4, 0x7f, 0x5b, 0x35, 0xb7, 0x9c, 0xd6, 0x7f, 0xaa, 0x6a, - 0xba, 0xc1, 0x5e, 0x55, 0x99, 0xfb, 0x8c, 0x6e, 0x9b, 0xd4, 0x9a, 0xa6, - 0x66, 0x56, 0xeb, 0x71, 0x8b, 0x7d, 0xc0, 0xff, 0xf1, 0x50, 0x80, 0x29, - 0xff, 0xfc, 0x21, 0x2a, 0xcf, 0x34, 0xb3, 0x4c, 0x00, 0x00, 0xa2, 0xb1, - 0xc2, 0x95, 0xe8, 0x36, 0x0a, 0x15, 0x84, 0x84, 0x1c, 0xb8, 0x73, 0xc5, - 0x6f, 0x8e, 0x78, 0x71, 0xef, 0xc3, 0x8e, 0x78, 0xf3, 0xbc, 0x9d, 0x4b, - 0x69, 0x7b, 0xbd, 0xdd, 0xb2, 0xf2, 0xc7, 0x24, 0xd0, 0x02, 0x24, 0xf2, - 0x59, 0xf2, 0xbe, 0xd7, 0xb1, 0xe3, 0xdd, 0xb6, 0x36, 0xf3, 0x94, 0xa8, - 0x10, 0x40, 0xc6, 0x3b, 0x97, 0x87, 0x4b, 0x54, 0x06, 0x5f, 0x38, 0x08, - 0x28, 0xdc, 0x2f, 0xcf, 0x11, 0x30, 0x31, 0x21, 0x8a, 0xa9, 0x09, 0x2f, - 0x72, 0x5f, 0x1f, 0x80, 0xc9, 0x2c, 0xf6, 0xb1, 0x31, 0x78, 0xa0, 0x71, - 0x88, 0xad, 0x65, 0x65, 0x80, 0xa9, 0x18, 0x9d, 0xba, 0x16, 0xa2, 0xab, - 0x39, 0xa7, 0x70, 0xe6, 0x82, 0x2a, 0xde, 0x10, 0x89, 0xdc, 0x17, 0x98, - 0x31, 0xbd, 0xd9, 0xfd, 0xca, 0x6c, 0xd2, 0x55, 0xc5, 0x72, 0x8f, 0xc7, - 0xd6, 0xf3, 0xd4, 0x6a, 0xfa, 0x9d, 0x5a, 0x19, 0xca, 0x09, 0xc9, 0x20, - 0x82, 0x78, 0x7e, 0x0a, 0x00, 0x42, 0xe7, 0xf5, 0x32, 0x5c, 0x60, 0x37, - 0x76, 0x37, 0x73, 0x46, 0x2b, 0x95, 0x6f, 0xbd, 0x3b, 0x05, 0xfa, 0x1f, - 0x71, 0xfb, 0x86, 0x22, 0xa5, 0x3f, 0xc9, 0x54, 0x36, 0x31, 0xec, 0x98, - 0xf2, 0x76, 0x5b, 0x92, 0xfd, 0x99, 0x5f, 0x30, 0x41, 0xb0, 0x70, 0x6a, - 0x50, 0xd6, 0xb1, 0xc6, 0xa9, 0xde, 0xa9, 0xac, 0xa0, 0x08, 0x64, 0x67, - 0x62, 0x59, 0x26, 0x6b, 0xbb, 0x65, 0x9c, 0xce, 0xbc, 0x1d, 0x7f, 0x19, - 0xa8, 0xac, 0x10, 0xff, 0xc2, 0xfb, 0x87, 0xfc, 0x00, 0x2d, 0xe6, 0x67, - 0xa9, 0x6f, 0x49, 0xcb, 0x77, 0x5f, 0xe2, 0xfe, 0x6f, 0xb0, 0xf8, 0x60, - 0xfd, 0x08, 0x79, 0xe8, 0x9d, 0x36, 0x45, 0x77, 0xc0, 0x00, 0x00, 0x1d, - 0x67, 0x19, 0xc4, 0xe2, 0xaa, 0xb7, 0xab, 0xdc, 0xab, 0x1c, 0xf0, 0x2d, - 0xc9, 0xf3, 0xf0, 0xe7, 0x87, 0xfe, 0x3e, 0x3e, 0x68, 0x35, 0x3d, 0x48, - 0xcd, 0x01, 0x45, 0xa8, 0xda, 0x4b, 0xbc, 0x4d, 0x30, 0xd4, 0xc2, 0x40, - 0x5a, 0xb8, 0x22, 0x5b, 0xcd, 0xf3, 0x9a, 0xaa, 0x9f, 0xf3, 0xfa, 0x41, - 0x76, 0x80, 0xa7, 0x00, 0x5a, 0x6f, 0x48, 0xbb, 0xaa, 0xad, 0xc5, 0x93, - 0x21, 0x7d, 0xf9, 0x1a, 0x99, 0x04, 0xc3, 0x79, 0xe4, 0x84, 0xe7, 0x4a, - 0x8a, 0xcd, 0x13, 0x31, 0x11, 0x3c, 0xff, 0xf1, 0x50, 0x80, 0x35, 0x1f, - 0xfc, 0x21, 0x4c, 0xd2, 0xc1, 0x06, 0x10, 0x46, 0x04, 0x27, 0xcb, 0xda, - 0x46, 0x28, 0x2d, 0xa8, 0xee, 0xe3, 0x73, 0x71, 0x9a, 0xd0, 0x03, 0xae, - 0x2f, 0xaa, 0x8d, 0xfd, 0x7f, 0xd8, 0x12, 0x87, 0x1c, 0x80, 0x1c, 0x20, - 0x77, 0xdb, 0xc7, 0xe0, 0x04, 0xb0, 0xcb, 0x9a, 0x90, 0xa0, 0x9c, 0xd2, - 0x46, 0x2b, 0x8e, 0x5b, 0x2e, 0x68, 0xe4, 0xe5, 0xf3, 0x73, 0xea, 0x0d, - 0x41, 0xc9, 0x2f, 0x89, 0x20, 0x87, 0xba, 0x27, 0x49, 0x52, 0x5e, 0x0b, - 0x01, 0x53, 0x23, 0x49, 0xf7, 0x8c, 0x87, 0x96, 0x63, 0xcc, 0xb7, 0x48, - 0xa6, 0x2b, 0x71, 0x90, 0x43, 0x21, 0x33, 0xf2, 0xa7, 0x0a, 0x8a, 0x64, - 0x23, 0x15, 0x65, 0x82, 0x17, 0x0a, 0x34, 0x03, 0xaf, 0x6e, 0x2a, 0x31, - 0x6e, 0x2c, 0xa2, 0x43, 0xbe, 0xd2, 0x33, 0xe7, 0x01, 0x59, 0x47, 0x2e, - 0x83, 0xc8, 0x71, 0xcb, 0x1c, 0x80, 0x36, 0x37, 0xad, 0x0e, 0xfc, 0x06, - 0x96, 0x9c, 0x73, 0x4e, 0x14, 0x45, 0x92, 0xd8, 0xc4, 0x38, 0xd5, 0x3a, - 0x23, 0x3a, 0xbb, 0x6f, 0x12, 0x96, 0xca, 0xb9, 0xb3, 0xd0, 0xca, 0xb9, - 0xe3, 0x3d, 0x0c, 0xf3, 0x03, 0x9b, 0xdf, 0x6a, 0x81, 0x60, 0x85, 0xc8, - 0xa3, 0x95, 0x1d, 0x99, 0xdb, 0x90, 0x16, 0x53, 0xd9, 0x2d, 0xb3, 0xdf, - 0x2d, 0xd9, 0x4e, 0x10, 0x92, 0xdc, 0x72, 0x94, 0xea, 0x04, 0x23, 0x9f, - 0x53, 0x91, 0x54, 0x39, 0xb9, 0x9f, 0x5d, 0x2d, 0x52, 0x59, 0x81, 0xce, - 0x91, 0x5b, 0x11, 0x80, 0x1e, 0x36, 0xe1, 0x1b, 0x95, 0xd8, 0xc4, 0xc8, - 0xfe, 0x10, 0x34, 0x9c, 0xc0, 0x1e, 0xee, 0xde, 0x55, 0x42, 0x3b, 0xa3, - 0xbb, 0xe1, 0xf3, 0x04, 0x63, 0x8e, 0xa6, 0x7b, 0x80, 0xf8, 0x37, 0xd8, - 0x91, 0x93, 0xbb, 0xa3, 0xa7, 0x55, 0x9c, 0x98, 0x9a, 0x59, 0x2f, 0x49, - 0x10, 0xbe, 0x20, 0x7d, 0xda, 0x48, 0xc7, 0xec, 0x6b, 0xbe, 0x62, 0x33, - 0xea, 0xf4, 0x1e, 0xd3, 0x77, 0xc3, 0x5f, 0x67, 0xb5, 0xff, 0x3e, 0x2d, - 0x9d, 0x7f, 0x4f, 0x47, 0xb3, 0xf7, 0x64, 0x8f, 0xdb, 0xd3, 0xeb, 0xbd, - 0x7b, 0x78, 0x97, 0xcf, 0x03, 0x8a, 0x5f, 0xcb, 0x62, 0x56, 0x5b, 0x0a, - 0xa9, 0xaf, 0x1a, 0x2a, 0xa8, 0x66, 0xfb, 0xe5, 0xc8, 0x68, 0xde, 0x1e, - 0x16, 0xa0, 0x2c, 0x04, 0x07, 0xa0, 0x47, 0x50, 0x7c, 0x7c, 0x61, 0xd5, - 0xfe, 0xdc, 0xf1, 0x6b, 0xba, 0xee, 0xfc, 0xbb, 0xa5, 0xf0, 0xfa, 0x71, - 0x95, 0xf6, 0xf4, 0xf6, 0x3c, 0xd5, 0x78, 0xe2, 0x05, 0xf7, 0xc8, 0x91, - 0x69, 0x08, 0xaa, 0x22, 0x76, 0xe4, 0x47, 0xd7, 0xf9, 0x12, 0xcc, 0x07, - 0x05, 0xe2, 0x7e, 0x99, 0x4e, 0xfd, 0x4e, 0x62, 0xaa, 0x3c, 0x4f, 0x7b, - 0xe0, 0x81, 0xea, 0x0f, 0xd5, 0xed, 0x2f, 0xb8, 0x6b, 0x1b, 0xbc, 0x39, - 0x9b, 0xe9, 0x9b, 0xff, 0x51, 0xf7, 0x05, 0x74, 0x6d, 0xcd, 0x2f, 0x5e, - 0x47, 0x2c, 0x2c, 0x80, 0x85, 0x70, 0xa4, 0x84, 0xb5, 0xae, 0xf3, 0x62, - 0x16, 0x06, 0x00, 0xfb, 0x2f, 0x2f, 0xe7, 0x62, 0x44, 0x70, 0xff, 0xf1, - 0x50, 0x80, 0x27, 0x9f, 0xfc, 0x21, 0x7a, 0xcf, 0xc4, 0xe9, 0x3f, 0x00, - 0x80, 0xa5, 0xb1, 0x42, 0x91, 0x62, 0x34, 0x63, 0x09, 0x06, 0x09, 0xd6, - 0xf5, 0x5e, 0x25, 0xea, 0xb5, 0x4a, 0x92, 0xfc, 0xfa, 0xe3, 0x3c, 0xdc, - 0xde, 0x93, 0x77, 0x92, 0x5d, 0x24, 0x1e, 0x16, 0x74, 0x55, 0xdd, 0x07, - 0x67, 0xf4, 0xe7, 0x52, 0xb0, 0xaa, 0x69, 0xd1, 0xc4, 0x52, 0x9a, 0x38, - 0xd1, 0x53, 0x9c, 0x6f, 0x52, 0x30, 0x9a, 0x7b, 0xc4, 0xf2, 0x59, 0xc9, - 0xf7, 0xba, 0x54, 0x0a, 0x07, 0x52, 0x70, 0xeb, 0xd6, 0x3d, 0x88, 0x9b, - 0xc4, 0xd0, 0xb0, 0x6c, 0xd9, 0xcc, 0x1c, 0x99, 0x3a, 0x29, 0x17, 0x18, - 0x6b, 0xca, 0xf2, 0x93, 0x2c, 0x83, 0xcd, 0x2d, 0x1d, 0xfe, 0x18, 0x3b, - 0xb6, 0x3f, 0x44, 0xa0, 0xec, 0xbd, 0x5c, 0xa0, 0xdf, 0x3b, 0xfb, 0x0d, - 0x54, 0x97, 0x0e, 0x26, 0x23, 0x4a, 0x7c, 0x12, 0xa2, 0x92, 0x8f, 0xee, - 0x61, 0xe6, 0x6b, 0x29, 0xc7, 0x40, 0x8a, 0xc6, 0x5b, 0xcf, 0x35, 0xa3, - 0x6c, 0xed, 0x79, 0xc7, 0x5a, 0x05, 0xbe, 0x60, 0x32, 0x82, 0x47, 0xe2, - 0xac, 0x42, 0x1a, 0x0a, 0x6b, 0x8a, 0xc6, 0x07, 0x5a, 0x4a, 0x1b, 0x2d, - 0xc4, 0x65, 0x2f, 0x3c, 0xe0, 0x61, 0x95, 0x6a, 0xc4, 0x7c, 0x68, 0xe5, - 0x8d, 0x07, 0xc2, 0x3f, 0x92, 0xd5, 0xbf, 0x70, 0xc5, 0xd4, 0x4a, 0x51, - 0xe3, 0x16, 0xc4, 0x38, 0x18, 0xed, 0x35, 0x10, 0x1a, 0xa0, 0x4d, 0x8e, - 0x80, 0x13, 0xbf, 0xe3, 0xf5, 0xf1, 0xfa, 0x14, 0x6b, 0x54, 0x4f, 0x8e, - 0x30, 0x7d, 0x85, 0x8d, 0x34, 0x53, 0xbe, 0xfe, 0x66, 0xb2, 0x88, 0x1b, - 0x00, 0x7a, 0x55, 0xdf, 0xc0, 0x0b, 0xcf, 0x3e, 0x0b, 0xd7, 0x7e, 0xde, - 0xaf, 0x5c, 0xbe, 0x3c, 0xfa, 0xe3, 0x3c, 0xc9, 0xcd, 0xcc, 0x5e, 0x2f, - 0x20, 0x17, 0x5f, 0x47, 0xd4, 0x22, 0x23, 0xb3, 0xd0, 0xbe, 0xfe, 0xee, - 0x53, 0xcf, 0xc6, 0x71, 0xf5, 0xff, 0x6e, 0x44, 0x1e, 0xbe, 0xbd, 0xc4, - 0x15, 0xd5, 0x96, 0xb3, 0x15, 0x51, 0xce, 0xd3, 0x76, 0x8e, 0x40, 0xac, - 0x40, 0x19, 0x9c, 0x82, 0x51, 0x8e, 0x53, 0xbc, 0x05, 0xe1, 0x68, 0x35, - 0x30, 0x45, 0x54, 0x60, 0x12, 0x95, 0x4c, 0xf7, 0x46, 0x2f, 0x18, 0x98, - 0x17, 0xc0, 0xff, 0xf1, 0x50, 0x80, 0x29, 0xff, 0xfc, 0x21, 0x1a, 0xcf, - 0x38, 0xb3, 0x3a, 0x08, 0x00, 0xa6, 0xb1, 0x42, 0x59, 0x2a, 0x74, 0x80, - 0xf8, 0xd7, 0x7e, 0xde, 0xba, 0xaf, 0x55, 0xe6, 0xf3, 0x8a, 0xc5, 0xca, - 0xba, 0xd4, 0xe1, 0x33, 0x5c, 0xd5, 0x4d, 0x22, 0x9a, 0x0c, 0x6c, 0x9c, - 0x5b, 0xa8, 0xd9, 0xaa, 0xba, 0xf9, 0x54, 0x64, 0x1a, 0x1d, 0xa0, 0x88, - 0x85, 0xd7, 0x8f, 0x84, 0x0e, 0x70, 0xbb, 0xe2, 0x1b, 0xea, 0xba, 0x35, - 0xd4, 0x29, 0x5a, 0x03, 0x71, 0x58, 0x3a, 0x81, 0x2f, 0xab, 0x60, 0x10, - 0x86, 0xe9, 0x32, 0x83, 0x20, 0x43, 0xae, 0x73, 0x64, 0x51, 0xf1, 0xa9, - 0x25, 0x1b, 0x5e, 0x97, 0x35, 0x98, 0x84, 0xdd, 0xdd, 0x68, 0xdf, 0x35, - 0x65, 0xab, 0xa0, 0x84, 0x44, 0x86, 0xb8, 0xc7, 0x5e, 0x9b, 0x8b, 0xf0, - 0x9b, 0x04, 0x25, 0x50, 0x3b, 0xee, 0x0d, 0x8d, 0x3f, 0xf4, 0xbc, 0xb1, - 0xbb, 0xb1, 0x26, 0xd4, 0xee, 0x7c, 0xa5, 0xc0, 0x23, 0x98, 0x0e, 0xd7, - 0x07, 0xe0, 0x29, 0x77, 0x81, 0xea, 0xb0, 0x4d, 0x52, 0xff, 0xb8, 0x5e, - 0x33, 0x36, 0x96, 0x92, 0x5d, 0x9a, 0x8a, 0x9b, 0x1b, 0x46, 0x9d, 0x98, - 0xb4, 0x48, 0x65, 0x82, 0x46, 0x46, 0x80, 0x7a, 0x65, 0x2b, 0x5b, 0xeb, - 0xc6, 0x4e, 0x84, 0xf9, 0xb7, 0x41, 0x83, 0xbc, 0x90, 0xd6, 0xfd, 0x3e, - 0xbe, 0xe9, 0xa2, 0x44, 0x0e, 0xff, 0xa1, 0x28, 0x5e, 0x85, 0x03, 0x96, - 0xa6, 0xb1, 0xf4, 0xfb, 0xeb, 0x8c, 0x1a, 0x2d, 0xbc, 0x10, 0xf7, 0xec, - 0xdc, 0x1d, 0xc0, 0xb3, 0x57, 0xd9, 0x2c, 0x0b, 0x1b, 0xa0, 0xc7, 0x75, - 0xd3, 0xed, 0x7e, 0xc9, 0xfb, 0xcd, 0xc4, 0x46, 0xe4, 0xfd, 0x67, 0xc1, - 0x74, 0xe5, 0x94, 0xcc, 0xa5, 0x59, 0x19, 0x43, 0xef, 0x13, 0xa7, 0xb7, - 0xab, 0xef, 0x77, 0x5f, 0x59, 0x5c, 0xf9, 0x63, 0x8b, 0xac, 0xf3, 0x90, - 0x95, 0x59, 0x2e, 0xa2, 0xb4, 0xc0, 0x47, 0x64, 0xe2, 0x0d, 0xbf, 0x48, - 0x84, 0x21, 0x37, 0xc7, 0x04, 0x2f, 0xc1, 0xbf, 0x3f, 0x6a, 0x96, 0xf8, - 0x7a, 0x39, 0x36, 0xbd, 0xfc, 0x7d, 0x1c, 0xf8, 0xa8, 0xf7, 0x7c, 0x15, - 0x0e, 0x77, 0xdf, 0x32, 0x81, 0x7e, 0xc8, 0x15, 0x5d, 0x9c, 0xa4, 0x07, - 0x88, 0x10, 0x8b, 0x10, 0x96, 0xef, 0x7d, 0x11, 0x50, 0xdd, 0x2e, 0x6e, - 0xf4, 0xc6, 0x72, 0x9f, 0xaf, 0xe1, 0xe0, 0xb1, 0xdc, 0xb8, 0xa9, 0x63, - 0x7c, 0xff, 0xf1, 0x50, 0x80, 0x2f, 0x9f, 0xfc, 0xde, 0x02, 0x00, 0x4c, - 0x61, 0x76, 0x63, 0x35, 0x38, 0x2e, 0x33, 0x34, 0x2e, 0x31, 0x30, 0x30, - 0x00, 0x42, 0x35, 0x9f, 0x73, 0x02, 0x6a, 0x08, 0x01, 0x47, 0x62, 0x86, - 0x2a, 0x51, 0xac, 0x14, 0x1b, 0x0c, 0x71, 0xed, 0xeb, 0xeb, 0xdf, 0xdb, - 0x7f, 0x3a, 0xbf, 0x3c, 0xf0, 0x6f, 0x59, 0xa1, 0x1c, 0x6f, 0xaa, 0x73, - 0x6c, 0xf6, 0xe5, 0x9e, 0x77, 0x04, 0x46, 0xea, 0x82, 0x48, 0x0c, 0xe8, - 0xac, 0x88, 0x67, 0xff, 0xec, 0xc3, 0x2b, 0x66, 0x18, 0x41, 0x59, 0x35, - 0x72, 0xc8, 0x73, 0xa0, 0x6e, 0x20, 0x76, 0x31, 0xd8, 0x15, 0x9b, 0x89, - 0x13, 0x46, 0x67, 0x5f, 0x63, 0xd3, 0xdb, 0x5b, 0xb9, 0xf4, 0x36, 0xcf, - 0x2e, 0xce, 0x59, 0xa5, 0x33, 0x5c, 0xb5, 0xc6, 0x04, 0x7f, 0x1c, 0x26, - 0x10, 0xbe, 0x71, 0x1e, 0x29, 0xbf, 0x84, 0x5b, 0xd0, 0x38, 0x8a, 0xb0, - 0x38, 0xb0, 0x2c, 0x8f, 0xa0, 0x3e, 0xcc, 0x84, 0x15, 0x7f, 0x3c, 0x90, - 0xd4, 0xb6, 0x86, 0xf9, 0xba, 0xd4, 0xd6, 0xa0, 0x4a, 0x39, 0x4a, 0x72, - 0xfe, 0x76, 0x59, 0x7f, 0xfa, 0x29, 0xde, 0xd7, 0xac, 0xad, 0x15, 0xe5, - 0x75, 0x20, 0xde, 0xa1, 0x3a, 0x38, 0xd8, 0x4d, 0x0a, 0x60, 0xb6, 0x37, - 0xf6, 0x10, 0x90, 0x8f, 0xad, 0xe7, 0xb2, 0x5c, 0x5b, 0xbe, 0x44, 0xaf, - 0x09, 0x26, 0x83, 0x93, 0x09, 0x45, 0x9d, 0xd1, 0x5b, 0x1a, 0x71, 0x11, - 0x5f, 0x19, 0x08, 0x19, 0x13, 0x97, 0x69, 0xde, 0x0f, 0x55, 0xcd, 0x5f, - 0xaa, 0x01, 0x6f, 0xa5, 0x04, 0x9a, 0xeb, 0xbc, 0x21, 0x10, 0xe6, 0x29, - 0xa2, 0xd2, 0x82, 0x91, 0x90, 0x45, 0x53, 0x63, 0x2b, 0x5c, 0xe7, 0xbc, - 0xc4, 0x0c, 0x56, 0xea, 0xf3, 0x7b, 0x94, 0x11, 0x05, 0x59, 0x00, 0x3a, - 0xb4, 0x58, 0x35, 0xac, 0x81, 0x6a, 0xd6, 0x74, 0x85, 0xc8, 0x00, 0x26, - 0x6c, 0x36, 0x25, 0x4e, 0x06, 0xcd, 0x13, 0xcd, 0x3d, 0xf4, 0x70, 0xb6, - 0x52, 0x1f, 0xde, 0x38, 0xbc, 0xfa, 0xf7, 0x9d, 0xd6, 0xb7, 0xed, 0xce, - 0xb7, 0xd1, 0x5d, 0x32, 0xf3, 0x57, 0x97, 0x9a, 0xe6, 0xf1, 0xa6, 0x2c, - 0x0b, 0xde, 0xf8, 0x29, 0x89, 0xc3, 0xba, 0xb7, 0x53, 0x94, 0xa7, 0xa1, - 0x26, 0xd5, 0x4a, 0xe2, 0x73, 0xa1, 0xfa, 0x7e, 0x29, 0x48, 0xa8, 0x7e, - 0x7e, 0x9f, 0x04, 0x30, 0x02, 0xb3, 0xb5, 0xfb, 0x28, 0x80, 0x2b, 0x7b, - 0x62, 0xb0, 0x37, 0xe7, 0xc9, 0x18, 0x8d, 0x02, 0xba, 0xe6, 0x42, 0x00, - 0x40, 0x8d, 0x2e, 0xb0, 0xc0, 0x81, 0x85, 0xec, 0x36, 0x59, 0x56, 0xec, - 0x91, 0x0a, 0x4d, 0x83, 0x80, 0xb8, 0xdb, 0x5c, 0xa3, 0x6c, 0xa2, 0xcc, - 0xfb, 0x0f, 0x93, 0x7d, 0xa5, 0xe5, 0x5e, 0xc3, 0x80, 0xff, 0xf1, 0x50, - 0x80, 0x3e, 0xbf, 0xfc, 0x20, 0xa3, 0x1a, 0xd6, 0x38, 0x63, 0x19, 0x28, - 0xc2, 0x1a, 0xf6, 0xef, 0xdb, 0xd7, 0xf8, 0xfd, 0x3d, 0xef, 0xae, 0x3b, - 0xf6, 0xd9, 0x22, 0x25, 0x35, 0x4f, 0x6f, 0x15, 0x4a, 0xb9, 0x79, 0x5e, - 0xdc, 0x87, 0x06, 0x4e, 0x8b, 0x3f, 0x3f, 0x9a, 0x34, 0x23, 0xe3, 0x2d, - 0x84, 0x6c, 0x51, 0xd4, 0x41, 0x4a, 0x46, 0x4d, 0x8c, 0x18, 0x78, 0x9c, - 0xe7, 0x7d, 0x0d, 0x2a, 0xe1, 0xf9, 0xe6, 0x62, 0x91, 0x26, 0x24, 0x6e, - 0x12, 0xb3, 0x57, 0x31, 0x02, 0x8c, 0x3e, 0x82, 0xc0, 0x9d, 0xe1, 0xd4, - 0xcf, 0x08, 0x07, 0x95, 0x20, 0x12, 0x0b, 0x26, 0x15, 0xa7, 0x36, 0x9b, - 0x68, 0x1f, 0xce, 0x28, 0x08, 0xf0, 0x61, 0x8c, 0x18, 0x1e, 0x64, 0x7e, - 0x38, 0x62, 0x43, 0x33, 0x64, 0x24, 0x66, 0x9c, 0x25, 0x17, 0x5a, 0x56, - 0xec, 0x2d, 0xa4, 0x3e, 0xce, 0xd3, 0x5a, 0x2d, 0x60, 0xd3, 0x09, 0xbb, - 0x76, 0xea, 0x9c, 0x49, 0x80, 0x26, 0x77, 0x78, 0x89, 0x5b, 0x4c, 0xf2, - 0x6f, 0x37, 0x88, 0xfe, 0x2f, 0x51, 0x86, 0xfd, 0x00, 0x57, 0x02, 0x28, - 0x88, 0x38, 0x55, 0xd8, 0xea, 0x00, 0x82, 0x14, 0xb0, 0xcb, 0xcd, 0x46, - 0xd2, 0xf6, 0x55, 0xc0, 0xbd, 0x22, 0x03, 0x50, 0xd8, 0xa4, 0x12, 0xd0, - 0xbe, 0x07, 0xd4, 0xb7, 0x6d, 0x45, 0xe1, 0x75, 0x5c, 0xbf, 0xc3, 0x33, - 0x5a, 0x9d, 0x97, 0xba, 0x05, 0x6a, 0xc2, 0x58, 0x87, 0x07, 0xfd, 0x37, - 0x27, 0xe4, 0x50, 0x70, 0xc6, 0xc6, 0xb8, 0x29, 0xd4, 0xf9, 0x6d, 0x26, - 0x13, 0xda, 0x30, 0x7d, 0x5b, 0xd2, 0x97, 0xb0, 0x69, 0xe0, 0x55, 0x24, - 0xed, 0x73, 0xf2, 0x74, 0x3a, 0x84, 0x0e, 0xe3, 0x57, 0x88, 0x24, 0x00, - 0x92, 0xe7, 0x2a, 0x42, 0xad, 0x62, 0xa6, 0x31, 0x55, 0x0c, 0xb4, 0x28, - 0x7b, 0x73, 0xed, 0xdc, 0xce, 0x57, 0xc5, 0x5f, 0x3c, 0x44, 0x93, 0x2f, - 0x8d, 0xfc, 0x6a, 0x95, 0x48, 0x52, 0x6e, 0xe8, 0x66, 0x8b, 0xa9, 0xde, - 0xa5, 0x0a, 0xd6, 0x21, 0x0b, 0x90, 0x64, 0x48, 0x37, 0xcf, 0x15, 0x06, - 0x1c, 0xad, 0x9b, 0xb0, 0x9f, 0x62, 0x3c, 0x6e, 0x57, 0x54, 0x01, 0xe1, - 0xcb, 0x92, 0x1a, 0x0f, 0x5b, 0x0c, 0x70, 0xc1, 0xfb, 0x5a, 0xfd, 0x34, - 0x41, 0x15, 0xbe, 0x19, 0xed, 0x04, 0x9e, 0xda, 0xec, 0x38, 0x72, 0x3d, - 0x74, 0xb6, 0x2b, 0x5e, 0xcd, 0x63, 0x0a, 0x6b, 0xa2, 0x22, 0xb9, 0x6d, - 0x6d, 0xe6, 0x84, 0xfd, 0x77, 0xac, 0xb4, 0xba, 0x9b, 0xc3, 0x5b, 0xab, - 0x6e, 0x1c, 0xee, 0x37, 0x88, 0x43, 0x0c, 0x22, 0x41, 0x9a, 0x90, 0x46, - 0x91, 0x56, 0xd9, 0x10, 0x0c, 0x22, 0x81, 0x0c, 0x6e, 0x86, 0x1a, 0xa8, - 0xbf, 0x01, 0xa2, 0x8b, 0x6d, 0xde, 0x26, 0x50, 0x8a, 0x70, 0x96, 0xb9, - 0x1b, 0x92, 0x32, 0x8e, 0x12, 0xb8, 0xda, 0x92, 0x3b, 0xd9, 0x04, 0x4c, - 0x76, 0xc2, 0x3b, 0xd7, 0x75, 0xc9, 0x14, 0x08, 0xa3, 0xcc, 0xb7, 0x99, - 0x39, 0xbd, 0xc8, 0x78, 0xca, 0x54, 0x09, 0x8a, 0x20, 0xa5, 0xb5, 0x1d, - 0x73, 0x94, 0x18, 0xc2, 0xbe, 0x68, 0x22, 0x2a, 0x37, 0xc1, 0xa8, 0xa8, - 0x04, 0xf8, 0xa9, 0x82, 0x45, 0xd6, 0xdb, 0x00, 0x2e, 0x0a, 0xaa, 0x09, - 0x6c, 0xcd, 0x71, 0x02, 0xaa, 0xa4, 0xaf, 0x49, 0x98, 0xae, 0x99, 0x4a, - 0xbd, 0xbb, 0xb0, 0x8d, 0x53, 0xd3, 0xf6, 0x7a, 0x5a, 0x7e, 0x07, 0xa7, - 0xcf, 0xe1, 0x97, 0x36, 0xfe, 0x7a, 0x86, 0x03, 0x1c, 0xfe, 0x4d, 0x22, - 0x03, 0x88, 0xff, 0xa3, 0xd0, 0x0f, 0xd5, 0xad, 0xa2, 0x50, 0xf9, 0x4c, - 0x47, 0xb6, 0x11, 0xd9, 0xf9, 0x9f, 0xff, 0xf1, 0x50, 0x80, 0x3a, 0xff, - 0xfc, 0x20, 0xa5, 0x1a, 0xd6, 0x36, 0x62, 0x5d, 0x8a, 0x38, 0x99, 0xfa, - 0xfe, 0xde, 0x79, 0xdb, 0x5f, 0x1f, 0x3e, 0x6b, 0x6e, 0x0c, 0x3a, 0xc9, - 0xc7, 0xbc, 0xe2, 0x13, 0x9f, 0x3e, 0xdf, 0xb7, 0xd5, 0x74, 0x1c, 0x99, - 0x58, 0xfe, 0x79, 0xd6, 0xbd, 0x79, 0xa3, 0x05, 0x9e, 0x03, 0x70, 0xfd, - 0x43, 0x63, 0xd8, 0xb5, 0x00, 0x06, 0xca, 0x83, 0x11, 0x74, 0xba, 0x57, - 0x8d, 0x86, 0xaf, 0x75, 0x1e, 0x40, 0x01, 0x8e, 0x12, 0xa5, 0xbc, 0x9f, - 0x87, 0x5c, 0x7a, 0xb2, 0x85, 0xa1, 0x80, 0x1e, 0x7a, 0x7a, 0x27, 0xc7, - 0xd2, 0x60, 0x3f, 0x67, 0x10, 0xa0, 0xf0, 0xf5, 0x80, 0x18, 0x00, 0x00, - 0x00, 0xd8, 0x2d, 0xab, 0x1c, 0x1e, 0x26, 0x58, 0xd9, 0x00, 0x00, 0x01, - 0x10, 0xf8, 0x00, 0x00, 0x2c, 0x41, 0x70, 0x03, 0x68, 0xd0, 0x08, 0xa8, - 0x0f, 0x8c, 0x3d, 0xb7, 0xcd, 0xf3, 0xc0, 0x40, 0x01, 0x02, 0x2e, 0xee, - 0xe0, 0x0c, 0x58, 0xc5, 0xee, 0x21, 0x21, 0xc3, 0x04, 0x2d, 0x0f, 0x4f, - 0x68, 0x78, 0x78, 0x7a, 0xca, 0xd3, 0x4e, 0x1e, 0x9c, 0x3b, 0xcf, 0x47, - 0x66, 0x60, 0x1c, 0xb1, 0x9a, 0x73, 0x24, 0xb9, 0x6d, 0xa5, 0x00, 0x00, - 0x08, 0x00, 0x50, 0x40, 0x80, 0xc3, 0xbb, 0x63, 0x14, 0x0d, 0x73, 0xc0, - 0xd2, 0x1d, 0x1b, 0xb2, 0x7c, 0x12, 0xe8, 0xb6, 0x7a, 0x0f, 0x6d, 0x95, - 0x34, 0xd6, 0x81, 0xaa, 0x9a, 0xaa, 0x9a, 0x82, 0x80, 0x08, 0x4d, 0x22, - 0x5b, 0x6d, 0x30, 0xc0, 0x11, 0x04, 0x4c, 0xb9, 0xcb, 0x2a, 0x02, 0x44, - 0xa6, 0xb7, 0xd8, 0x6c, 0x66, 0x32, 0x3e, 0xfc, 0x6f, 0x96, 0xcd, 0x54, - 0xe7, 0x4b, 0x71, 0x9d, 0xa6, 0x93, 0x36, 0x5a, 0x64, 0x28, 0x16, 0x90, - 0x76, 0x99, 0x2c, 0x44, 0xc5, 0x0d, 0x56, 0xe2, 0xcd, 0x78, 0x03, 0xb4, - 0x23, 0xea, 0xf2, 0x4a, 0xfc, 0x8f, 0xf6, 0xfd, 0xbd, 0xbc, 0xba, 0xaf, - 0x5f, 0xd4, 0x3a, 0x9c, 0x26, 0xf8, 0x04, 0xc4, 0x61, 0x89, 0x41, 0x1b, - 0x17, 0xb4, 0xc3, 0x29, 0x77, 0xf4, 0xdf, 0x12, 0xd3, 0xd4, 0x68, 0xb6, - 0xde, 0x4f, 0xca, 0xd2, 0xa1, 0xe6, 0x5b, 0xc4, 0x50, 0xaf, 0x5c, 0x35, - 0x4b, 0xa3, 0x6e, 0x78, 0x7b, 0x6a, 0xc3, 0xab, 0xe4, 0x57, 0xd4, 0x62, - 0xfb, 0x38, 0xbf, 0x58, 0x7b, 0xa7, 0x66, 0x69, 0x87, 0x6a, 0x26, 0x61, - 0x28, 0x5c, 0x21, 0xc8, 0xab, 0x57, 0x4e, 0xb1, 0xbb, 0xba, 0xaf, 0xe3, - 0xdb, 0x98, 0xe0, 0xa3, 0xfd, 0xdc, 0x58, 0xdb, 0x20, 0xc3, 0xde, 0x77, - 0xe7, 0x1f, 0x63, 0xff, 0x52, 0x2d, 0xf5, 0x0e, 0x27, 0xdf, 0x96, 0x87, - 0xc4, 0x69, 0x02, 0x78, 0x14, 0x00, 0x63, 0x83, 0x3e, 0x3b, 0x52, 0x17, - 0xc8, 0x8e, 0x3d, 0x42, 0x09, 0x9d, 0x1f, 0x47, 0xab, 0x91, 0xcd, 0x83, - 0x74, 0x2c, 0x55, 0x53, 0x66, 0x51, 0x19, 0x3c, 0xf4, 0x69, 0xbb, 0x1f, - 0x2e, 0x13, 0xdc, 0x53, 0x75, 0x6f, 0xf1, 0xf5, 0x4d, 0x67, 0xb2, 0xee, - 0x9f, 0x09, 0xb3, 0xf3, 0x1d, 0x73, 0x6d, 0x07, 0x1b, 0xff, 0xa8, 0xb6, - 0xb2, 0x98, 0x15, 0xe4, 0x44, 0x9c, 0xb0, 0x55, 0xa4, 0x1e, 0x3c, 0x4b, - 0xfa, 0x7f, 0xa0, 0xe8, 0x89, 0xf3, 0x4d, 0x27, 0xf3, 0x7a, 0x52, 0xca, - 0x3c, 0x7d, 0x05, 0x82, 0x70, 0x8b, 0x26, 0x07, 0x16, 0xbd, 0x99, 0x62, - 0x10, 0x6c, 0x07, 0x79, 0xdb, 0xfa, 0x7a, 0xd9, 0xbe, 0xff, 0xf1, 0x50, - 0x80, 0x37, 0x1f, 0xfc, 0x20, 0xaa, 0x1a, 0xd4, 0x44, 0x89, 0x21, 0x0e, - 0xc7, 0x02, 0x75, 0xe3, 0x8e, 0x6e, 0xbc, 0xf7, 0x5d, 0x65, 0xf8, 0xf8, - 0xf7, 0x38, 0xae, 0x35, 0x9c, 0x19, 0xa8, 0xea, 0xaa, 0xf8, 0xe7, 0x80, - 0x27, 0xd1, 0x7e, 0xd3, 0xe1, 0x49, 0x78, 0x71, 0x33, 0xfb, 0x66, 0xd2, - 0x79, 0xb3, 0x82, 0x3f, 0x7b, 0x6e, 0x16, 0x06, 0xdd, 0xd4, 0x14, 0xf3, - 0x59, 0xc3, 0x80, 0x90, 0xdb, 0x46, 0xa2, 0x70, 0x78, 0xab, 0xe1, 0xaa, - 0x86, 0x8c, 0x09, 0x44, 0xe1, 0x58, 0xa2, 0x45, 0x2d, 0x08, 0x73, 0x98, - 0x22, 0x42, 0x50, 0x59, 0xaf, 0xfd, 0xfa, 0xa6, 0x3e, 0x5d, 0x9e, 0x9f, - 0x6b, 0x7c, 0x2c, 0x9e, 0xf7, 0xd8, 0x83, 0xf7, 0xb2, 0x21, 0x85, 0x8a, - 0xae, 0x48, 0x68, 0x66, 0xbc, 0xdf, 0x0d, 0x3c, 0x76, 0x50, 0xaa, 0x70, - 0x8a, 0xf8, 0xae, 0x0b, 0x9b, 0x9a, 0xdc, 0x17, 0x95, 0xca, 0x53, 0x55, - 0x44, 0x19, 0x4e, 0xb7, 0x2d, 0xcf, 0xa5, 0x90, 0x5e, 0x82, 0x47, 0xf8, - 0xdd, 0x2d, 0x83, 0x4a, 0x98, 0x2f, 0x49, 0x02, 0xdb, 0x42, 0xf6, 0x6e, - 0xf4, 0x6d, 0x56, 0x9c, 0x39, 0xc1, 0x34, 0x5b, 0x33, 0x99, 0x4a, 0xd6, - 0xc5, 0x0a, 0x69, 0x4c, 0x4d, 0x69, 0xa5, 0x98, 0xbd, 0x30, 0x84, 0x2c, - 0xec, 0x02, 0xc4, 0xb4, 0xdb, 0x7b, 0x1e, 0xb9, 0xb0, 0xa6, 0x05, 0x84, - 0xed, 0xcd, 0x35, 0x67, 0x62, 0xc2, 0xf7, 0x49, 0x37, 0x72, 0xc3, 0x9d, - 0x17, 0xba, 0xc4, 0x8e, 0xf2, 0x5f, 0x74, 0x87, 0x6c, 0x8e, 0x15, 0xeb, - 0xfc, 0xea, 0xc5, 0x36, 0xb6, 0x2b, 0x8e, 0x35, 0x19, 0x4c, 0x36, 0x4f, - 0x5a, 0x14, 0x8c, 0x25, 0xa1, 0xd8, 0x67, 0xb5, 0x6a, 0x5e, 0x6b, 0xef, - 0xfc, 0x4a, 0xd6, 0x7e, 0x3e, 0x59, 0x35, 0x5b, 0x93, 0x1a, 0xd2, 0x92, - 0xe6, 0x75, 0xcc, 0xae, 0x20, 0x2a, 0xeb, 0x59, 0x6a, 0x16, 0x09, 0x90, - 0x9b, 0xef, 0x1c, 0xa0, 0x38, 0x94, 0x8a, 0x91, 0x52, 0x4f, 0x95, 0x34, - 0x7a, 0xe7, 0x45, 0xf1, 0x44, 0x85, 0xb1, 0xfa, 0xdf, 0xe9, 0xb2, 0xf0, - 0xdb, 0x3c, 0x38, 0x12, 0x9e, 0x22, 0xab, 0x8c, 0xdd, 0x50, 0x84, 0xf2, - 0x25, 0x34, 0x48, 0x3b, 0xee, 0x81, 0x9c, 0xbe, 0xa8, 0xbe, 0x7e, 0x56, - 0xf9, 0x45, 0x3f, 0x79, 0xce, 0xdb, 0x69, 0x1c, 0x1b, 0x19, 0xd9, 0x99, - 0xe8, 0x61, 0x6f, 0xdf, 0x43, 0xea, 0x75, 0xc9, 0x52, 0xcf, 0x5f, 0xab, - 0xfe, 0x39, 0x1f, 0xf1, 0xb5, 0x4b, 0x3e, 0x4f, 0x83, 0xfc, 0x66, 0x32, - 0x08, 0x66, 0xd0, 0xeb, 0xe5, 0x42, 0x4b, 0x46, 0x72, 0x24, 0x81, 0x3a, - 0xe4, 0x52, 0x56, 0x72, 0x45, 0xb8, 0x21, 0x91, 0xfa, 0x86, 0xf3, 0x92, - 0x5c, 0x91, 0xa2, 0x99, 0xb4, 0xd6, 0x5c, 0x92, 0xfb, 0x82, 0x73, 0xcf, - 0x8d, 0xef, 0x1a, 0x4b, 0x20, 0x4f, 0x4a, 0x74, 0x3e, 0xf5, 0x54, 0x3b, - 0x90, 0x28, 0x29, 0xa8, 0x68, 0x92, 0x4b, 0x74, 0x18, 0x34, 0xab, 0x39, - 0xdd, 0x65, 0xdd, 0x00, 0xf1, 0x99, 0x91, 0xb1, 0x06, 0x41, 0x74, 0x25, - 0x67, 0x34, 0xad, 0x5e, 0xb1, 0x64, 0xeb, 0xc5, 0xb1, 0xc5, 0x92, 0x1d, - 0xc9, 0xdc, 0x49, 0xc0, 0x38, 0xff, 0xf1, 0x50, 0x80, 0x22, 0x9f, 0xfc, - 0x21, 0x1a, 0xcf, 0xfd, 0xef, 0xbc, 0x20, 0x00, 0xa4, 0xb2, 0x32, 0x4a, - 0x28, 0x86, 0x24, 0x04, 0x02, 0xf0, 0x35, 0xb0, 0xe3, 0x73, 0x1d, 0x13, - 0x4d, 0x65, 0xd6, 0xb5, 0x97, 0x29, 0xe4, 0x20, 0xfe, 0xf1, 0x16, 0x07, - 0x1e, 0x8d, 0xbc, 0xd8, 0x5c, 0xfb, 0xd4, 0xd2, 0x76, 0x8c, 0xba, 0xcc, - 0xda, 0xe5, 0x34, 0x28, 0x9c, 0x65, 0x43, 0x0b, 0x23, 0x97, 0xca, 0x1e, - 0xdc, 0x0f, 0x17, 0xc8, 0x2a, 0xbd, 0x69, 0xe2, 0x39, 0xc4, 0x07, 0x61, - 0x03, 0xaf, 0x15, 0x89, 0x9c, 0xb0, 0x00, 0x83, 0x1c, 0x29, 0xf0, 0xb9, - 0xbb, 0x03, 0xa9, 0x92, 0xc4, 0x41, 0x66, 0x04, 0x0f, 0x28, 0x4c, 0xa9, - 0xf4, 0x1e, 0x91, 0xca, 0x13, 0x9c, 0x20, 0xf4, 0x55, 0x5a, 0x64, 0xd1, - 0xd2, 0x70, 0x0d, 0x51, 0x41, 0x8c, 0xaa, 0x55, 0x4f, 0x35, 0xfe, 0x2b, - 0x9c, 0x8b, 0x35, 0x95, 0xb1, 0x38, 0xd4, 0x70, 0x9a, 0x38, 0x0f, 0x61, - 0xc8, 0x64, 0x35, 0x5a, 0x45, 0x05, 0x71, 0xc0, 0x53, 0x68, 0xae, 0x0f, - 0xfa, 0xd6, 0x96, 0x7f, 0x85, 0x6e, 0x3d, 0x56, 0x8b, 0xf5, 0x6c, 0xbe, - 0xcd, 0x15, 0x7a, 0xf6, 0xc8, 0x05, 0x9f, 0x89, 0x42, 0xd8, 0xf5, 0x4c, - 0x36, 0x00, 0x91, 0xc8, 0x49, 0x24, 0xa4, 0x0e, 0x33, 0x33, 0xca, 0x16, - 0x2e, 0xa9, 0x2f, 0x01, 0xaa, 0x4c, 0x95, 0x90, 0xa7, 0xa1, 0xd4, 0xe0, - 0x20, 0xea, 0x06, 0xb2, 0x10, 0xe2, 0xa8, 0x96, 0x9a, 0xe4, 0x3c, 0x76, - 0x56, 0x31, 0xa2, 0xda, 0x6f, 0x9c, 0xab, 0xca, 0x73, 0x81, 0x49, 0x04, - 0x53, 0x8b, 0xfe, 0x20, 0x17, 0xb8, 0x00, 0xab, 0x98, 0x89, 0x97, 0x57, - 0xce, 0x97, 0x53, 0x76, 0xc0, 0x04, 0x76, 0xfb, 0x42, 0x4c, 0xb2, 0x61, - 0x7d, 0xf8, 0x5f, 0x28, 0xba, 0x2a, 0x7f, 0xe6, 0x76, 0x7c, 0xe2, 0x4a, - 0xb0, 0x97, 0x8e, 0x0c, 0x48, 0x5a, 0xd2, 0x81, 0x8c, 0x24, 0x72, 0x2b, - 0x10, 0x16, 0xb6, 0xe3, 0x80, 0xff, 0xf1, 0x50, 0x80, 0x27, 0xbf, 0xfc, - 0x21, 0x1a, 0xcf, 0x7e, 0xf2, 0xdf, 0x0c, 0x98, 0xa3, 0xb1, 0xb2, 0x50, - 0x84, 0xc6, 0x22, 0x11, 0x8e, 0x37, 0xa7, 0xc7, 0xba, 0xfb, 0xd1, 0x77, - 0x95, 0xac, 0xce, 0xb7, 0x9a, 0xb9, 0x38, 0xd5, 0xb7, 0x35, 0xcf, 0x13, - 0x2a, 0x71, 0x42, 0x30, 0xeb, 0xb9, 0x94, 0xd6, 0x29, 0xb9, 0x4c, 0xe1, - 0xd6, 0x94, 0xcf, 0xb4, 0x2f, 0x4d, 0x91, 0x69, 0x90, 0x80, 0x53, 0xa2, - 0xab, 0xd6, 0xcc, 0x4e, 0xe0, 0xfb, 0x99, 0x2f, 0x1a, 0x73, 0x1e, 0xaf, - 0xc7, 0x30, 0xeb, 0x75, 0xf5, 0x1b, 0x8d, 0xfa, 0x7e, 0x27, 0xcc, 0x7a, - 0xf9, 0xc3, 0x67, 0x20, 0x3e, 0x98, 0x81, 0x40, 0x7e, 0x97, 0xc2, 0x7e, - 0x4d, 0x74, 0x07, 0xde, 0x86, 0x1e, 0x62, 0xf7, 0x69, 0xe5, 0x36, 0x86, - 0x53, 0x27, 0x23, 0x62, 0x0a, 0xe9, 0x60, 0x68, 0x0a, 0x68, 0x08, 0xa5, - 0x10, 0xac, 0x63, 0x69, 0xe4, 0xc2, 0x54, 0xa8, 0x8d, 0xab, 0x77, 0x76, - 0x91, 0xf0, 0x91, 0x00, 0xc2, 0x5c, 0x94, 0x03, 0x13, 0x0b, 0xd1, 0x8e, - 0xa0, 0xb7, 0xe2, 0xd9, 0x12, 0x8b, 0xfc, 0xe0, 0x3a, 0xce, 0xed, 0xbb, - 0x27, 0xd8, 0xbf, 0xd7, 0xf7, 0x34, 0x85, 0x05, 0x6b, 0xe3, 0xb0, 0x6b, - 0x77, 0x0f, 0xb6, 0xc6, 0xbb, 0x04, 0x96, 0xf4, 0x9d, 0xa4, 0xb8, 0x4a, - 0xb6, 0xa8, 0xf5, 0x1b, 0xba, 0x73, 0x79, 0x31, 0x59, 0xeb, 0x60, 0x1a, - 0xe4, 0x9d, 0xfc, 0x10, 0x8f, 0x10, 0x64, 0x32, 0x4b, 0x63, 0x21, 0x2b, - 0x20, 0x2b, 0x45, 0x64, 0x10, 0x29, 0x68, 0x41, 0x38, 0x98, 0xaa, 0x02, - 0x9d, 0x00, 0x00, 0xee, 0xb6, 0xfb, 0xe2, 0x1f, 0x76, 0xca, 0x18, 0x36, - 0xcd, 0x17, 0x50, 0x1d, 0x6e, 0xfd, 0x3a, 0xa0, 0x30, 0xda, 0x84, 0x44, - 0xaa, 0x4d, 0x47, 0x31, 0x15, 0x7c, 0x00, 0xf8, 0xf7, 0x5e, 0x0b, 0xc4, - 0xab, 0xc9, 0x33, 0x57, 0x2c, 0x72, 0xb5, 0x26, 0x24, 0xd8, 0xad, 0x3f, - 0x9f, 0x74, 0x9d, 0xf4, 0x3e, 0x63, 0x10, 0xae, 0x3c, 0xe1, 0x6a, 0xce, - 0x30, 0x9d, 0x9f, 0xca, 0x73, 0x2c, 0x35, 0xf0, 0x21, 0x44, 0x4e, 0x19, - 0x4d, 0xce, 0x8f, 0x01, 0x4a, 0x78, 0xb2, 0x0c, 0xd3, 0x57, 0x29, 0x58, - 0x40, 0x05, 0xca, 0xdb, 0xde, 0xb5, 0x04, 0x51, 0x5b, 0x80, 0xff, 0xf1, - 0x50, 0x80, 0x25, 0x1f, 0xfc, 0x21, 0x1a, 0xcf, 0x20, 0xeb, 0xbe, 0x88, - 0x01, 0xa5, 0xb1, 0xa3, 0xc9, 0x06, 0x22, 0x12, 0x34, 0x6a, 0xea, 0x73, - 0xad, 0x66, 0x48, 0xa4, 0x5c, 0xa4, 0xce, 0xba, 0xf1, 0xad, 0x54, 0xa4, - 0xd1, 0xbb, 0x4c, 0x17, 0xc7, 0x25, 0xfd, 0x37, 0xf3, 0x97, 0xb3, 0x8f, - 0xfe, 0x81, 0x3e, 0x56, 0x99, 0xfe, 0x58, 0x2b, 0x70, 0x87, 0x83, 0xda, - 0x77, 0x6b, 0xde, 0xe5, 0xeb, 0xa9, 0x6f, 0x1a, 0xc3, 0x31, 0x5e, 0xca, - 0x76, 0xd2, 0xee, 0x8a, 0x9b, 0xbf, 0xf8, 0x96, 0x92, 0xc6, 0x37, 0xd4, - 0xd9, 0xa4, 0xa0, 0xa2, 0x09, 0x57, 0x16, 0x02, 0x81, 0x4a, 0x10, 0x1f, - 0x7b, 0xd4, 0xd3, 0xa2, 0x7b, 0x04, 0xe1, 0xe9, 0x30, 0x1e, 0xe5, 0x5c, - 0x37, 0xb5, 0x04, 0xb9, 0x37, 0x64, 0xad, 0xf8, 0x1c, 0x74, 0x37, 0x53, - 0xa0, 0xfc, 0x1c, 0xb0, 0x55, 0xb4, 0xce, 0x51, 0x5d, 0x03, 0xe4, 0x9a, - 0x46, 0xbd, 0xb5, 0x1a, 0x4a, 0x85, 0x48, 0xd4, 0x91, 0x4e, 0x1d, 0x59, - 0x81, 0xac, 0x91, 0x2c, 0xf6, 0x0d, 0x26, 0x67, 0xd1, 0xdc, 0xc6, 0x7e, - 0xca, 0x42, 0x3d, 0xf0, 0x74, 0x61, 0x84, 0x59, 0x45, 0xa3, 0xbe, 0x55, - 0xdb, 0x5a, 0xb1, 0x56, 0x01, 0xe7, 0xb2, 0x53, 0x64, 0x09, 0xfe, 0x80, - 0xa8, 0x6d, 0x23, 0xbd, 0x74, 0xc9, 0x74, 0x0a, 0xc1, 0x17, 0x97, 0x4a, - 0xfc, 0x2e, 0xaa, 0x77, 0xfc, 0xa5, 0x9b, 0x25, 0x50, 0x3d, 0x8f, 0xcb, - 0x2c, 0xfe, 0xdf, 0x3e, 0xbd, 0x41, 0x99, 0x23, 0xbf, 0x4b, 0x0b, 0x38, - 0xfb, 0xc2, 0xef, 0x8f, 0x7f, 0xbf, 0xbe, 0x6a, 0x4d, 0xde, 0xf8, 0x5e, - 0xf4, 0x99, 0xd2, 0xa5, 0x65, 0x24, 0xbc, 0x94, 0xa9, 0x41, 0x48, 0xe3, - 0xe4, 0xcd, 0x30, 0x34, 0x90, 0x05, 0x4e, 0x80, 0xc8, 0xc0, 0x49, 0xe0, - 0x2d, 0x6e, 0x17, 0xb5, 0x8f, 0x4d, 0xca, 0xd1, 0x85, 0xfc, 0xfb, 0x32, - 0x3f, 0xe4, 0xce, 0x0b, 0xdd, 0xc7, 0xc1, 0x4a, 0xba, 0x1c, 0x2d, 0xa4, - 0x44, 0xa5, 0x5b, 0xb6, 0xab, 0x69, 0x6b, 0xdd, 0x5c, 0x5b, 0xed, 0x3f, - 0x3d, 0x87, 0x67, 0x99, 0x98, 0x0e, 0xff, 0xf1, 0x50, 0x80, 0x2d, 0xbf, - 0xfc, 0x21, 0x1a, 0xcd, 0x3c, 0xe3, 0xbb, 0x00, 0x40, 0xa1, 0xb1, 0x42, - 0x99, 0x29, 0x16, 0x0a, 0x11, 0xcf, 0x1c, 0x6a, 0x57, 0x3c, 0x5e, 0xea, - 0xb4, 0xba, 0x55, 0xd2, 0x0d, 0x4a, 0x49, 0x59, 0xd3, 0x9b, 0xbd, 0xf9, - 0xdd, 0x8d, 0xa8, 0x4c, 0x6c, 0xfd, 0x2f, 0x48, 0x64, 0x68, 0x5a, 0xb3, - 0xbd, 0x82, 0x78, 0xab, 0xcb, 0x7f, 0xdd, 0x89, 0x29, 0x51, 0x04, 0xf1, - 0xb8, 0x08, 0xc3, 0x93, 0x1c, 0xe3, 0xbf, 0x42, 0xe9, 0xca, 0x67, 0x19, - 0xba, 0x3d, 0x43, 0x66, 0x17, 0xcf, 0x3a, 0x6b, 0x02, 0x26, 0xa0, 0xa8, - 0x20, 0x5b, 0xd9, 0xd5, 0xe6, 0x79, 0x98, 0x39, 0x0d, 0x0e, 0x98, 0xd7, - 0x34, 0xed, 0x64, 0x23, 0xe2, 0xdc, 0xed, 0x6e, 0x0f, 0x02, 0x28, 0x07, - 0x81, 0xdd, 0x6a, 0xaf, 0x36, 0x93, 0x77, 0x06, 0xcf, 0x4a, 0xb1, 0x37, - 0x00, 0x38, 0xe5, 0xc7, 0x9e, 0xe3, 0x89, 0xf2, 0xc3, 0x4a, 0xb7, 0x70, - 0x26, 0xaf, 0x21, 0x8e, 0x13, 0x44, 0xe8, 0xdd, 0xbd, 0x39, 0x30, 0x92, - 0x60, 0xe4, 0xee, 0xf9, 0xbe, 0x37, 0xe0, 0xff, 0x3d, 0x1d, 0xb4, 0x0c, - 0x01, 0x1e, 0x14, 0x26, 0x21, 0x95, 0xeb, 0x7e, 0x16, 0xa1, 0x94, 0x01, - 0xb9, 0x38, 0xe1, 0xe2, 0x5a, 0xfc, 0x50, 0xae, 0x53, 0xf7, 0x67, 0x7f, - 0xf6, 0xdf, 0xf4, 0xaf, 0xed, 0xbd, 0x2f, 0x0d, 0xc0, 0xdc, 0x2c, 0x19, - 0x7c, 0xf6, 0x42, 0xc5, 0xc5, 0xc9, 0x3b, 0xca, 0x01, 0x59, 0x55, 0xda, - 0xe2, 0x82, 0x09, 0x20, 0xc9, 0x35, 0x3c, 0x38, 0x90, 0x58, 0x77, 0xa1, - 0xdc, 0x17, 0x3b, 0xf9, 0xb5, 0xf1, 0x8f, 0x56, 0xdf, 0xb3, 0x50, 0xfc, - 0x30, 0xb0, 0x9b, 0x7c, 0x56, 0xf5, 0x3f, 0xd1, 0x9e, 0xdd, 0x0d, 0xd6, - 0xff, 0xfa, 0xf8, 0x9e, 0x7b, 0x2b, 0xd0, 0xda, 0xd9, 0x0e, 0x22, 0x1a, - 0x93, 0xde, 0x13, 0xda, 0xb9, 0xe3, 0xbc, 0xea, 0xb5, 0x4f, 0x1f, 0x15, - 0x7d, 0xf1, 0x1b, 0xd6, 0xb2, 0xab, 0x11, 0x75, 0x90, 0xe3, 0x76, 0x0c, - 0x7a, 0x22, 0xa0, 0x49, 0x8a, 0x3e, 0x59, 0x93, 0x1b, 0xe5, 0x12, 0x04, - 0x0f, 0x26, 0x9a, 0xd4, 0x32, 0xf1, 0x1b, 0x65, 0x94, 0x2e, 0x89, 0x94, - 0x14, 0xd4, 0x29, 0x6c, 0x8d, 0xc8, 0xa4, 0xc9, 0x9b, 0x8d, 0x94, 0x89, - 0xf2, 0xf0, 0x77, 0x1b, 0x99, 0x35, 0x9a, 0x43, 0x9d, 0xe8, 0xef, 0x5b, - 0x04, 0xce, 0x1d, 0xf4, 0xc8, 0xa4, 0xeb, 0x99, 0x95, 0x58, 0x99, 0xbe, - 0x0f, 0x89, 0x98, 0xf3, 0x8c, 0x9c, 0x0b, 0x22, 0x65, 0x52, 0x8a, 0xbd, - 0xea, 0x38, 0xee, 0xaa, 0xd9, 0xce, 0x61, 0x88, 0x5a, 0xa4, 0xe0, 0xff, - 0xf1, 0x50, 0x80, 0x2e, 0x3f, 0xfc, 0x21, 0x2a, 0xcc, 0x50, 0xf9, 0xfb, - 0x10, 0x08, 0xa4, 0xb1, 0xc2, 0xd8, 0xce, 0x14, 0x63, 0x09, 0x08, 0xc4, - 0x66, 0xba, 0xf3, 0x55, 0x5d, 0x56, 0x73, 0x7a, 0xce, 0x2b, 0x5b, 0x17, - 0x42, 0xf3, 0x49, 0x9d, 0x55, 0x6e, 0x9d, 0x55, 0x83, 0x5d, 0xa6, 0x6e, - 0x91, 0xa3, 0xb3, 0x64, 0x69, 0x51, 0xec, 0x49, 0x9b, 0x89, 0xf4, 0x5b, - 0x5d, 0x48, 0xdf, 0xbe, 0x94, 0xe9, 0xe1, 0x6a, 0x92, 0x84, 0x2b, 0x01, - 0x40, 0x0d, 0x66, 0xb9, 0x28, 0xa4, 0xcd, 0x24, 0x91, 0xde, 0xea, 0x8a, - 0x14, 0x3c, 0x22, 0xe9, 0x40, 0xf3, 0xad, 0xc9, 0x0a, 0xb1, 0xbe, 0x91, - 0x54, 0xdc, 0xc2, 0x1d, 0xfe, 0xdf, 0x4f, 0x92, 0x9a, 0x40, 0x7e, 0x9b, - 0x5c, 0xfc, 0xbf, 0x28, 0x2e, 0xe2, 0xe4, 0xa4, 0x11, 0xcd, 0x00, 0x9a, - 0x2c, 0x3f, 0x39, 0x1e, 0x6c, 0xac, 0x28, 0xbd, 0xdb, 0x96, 0x1a, 0xbb, - 0x8c, 0xa4, 0xed, 0xd4, 0x44, 0x32, 0xa1, 0x96, 0x36, 0xd1, 0xaf, 0x57, - 0xd8, 0x7a, 0x85, 0x2d, 0x55, 0xb6, 0x3d, 0x60, 0x52, 0xd6, 0xa0, 0x0f, - 0xaf, 0x15, 0xb6, 0x09, 0xd3, 0xff, 0xe0, 0x93, 0x36, 0x82, 0x80, 0x19, - 0x98, 0x03, 0x3a, 0x04, 0x06, 0x79, 0x90, 0x42, 0x26, 0x58, 0xca, 0xb1, - 0x72, 0x0a, 0xa6, 0x98, 0x26, 0x25, 0x48, 0x24, 0x6f, 0xf4, 0x53, 0x1f, - 0xcf, 0xbc, 0xde, 0x61, 0x07, 0x2b, 0x1e, 0x6c, 0x0f, 0xd4, 0xab, 0x6a, - 0xe3, 0x7d, 0x76, 0xd6, 0x00, 0x15, 0x6d, 0x79, 0x17, 0x44, 0x16, 0xad, - 0xfb, 0xa0, 0xe7, 0x93, 0x58, 0x33, 0x05, 0x20, 0x4a, 0x88, 0x60, 0x4c, - 0xf3, 0x01, 0x8c, 0x8d, 0x5c, 0xc0, 0x6e, 0x74, 0x37, 0x16, 0x9a, 0xc5, - 0x00, 0x09, 0x22, 0xa4, 0xa6, 0xb0, 0x05, 0x57, 0x88, 0x0d, 0x20, 0xf4, - 0x4b, 0xb0, 0x8a, 0x22, 0x92, 0xc8, 0xd0, 0x70, 0xa1, 0xfd, 0xe1, 0xfa, - 0xf3, 0x7e, 0x3d, 0xbc, 0x49, 0x7a, 0xce, 0x3c, 0x5e, 0x85, 0xd6, 0xf4, - 0xf3, 0x95, 0x2a, 0xb8, 0xac, 0x2a, 0xd6, 0x08, 0x18, 0x3e, 0xd3, 0xb8, - 0xba, 0x32, 0x40, 0x58, 0x7e, 0xf6, 0x0e, 0x1f, 0xf0, 0xd2, 0x9f, 0x8f, - 0x82, 0x27, 0xd2, 0x71, 0xa3, 0x87, 0xb4, 0x60, 0x4c, 0xe3, 0xdb, 0xe8, - 0x06, 0x98, 0x00, 0xbf, 0x05, 0x9a, 0x8b, 0xdb, 0xba, 0xc5, 0xbc, 0x81, - 0x30, 0x22, 0x91, 0x48, 0xd1, 0xa0, 0x18, 0xca, 0xcb, 0x87, 0xf7, 0x3d, - 0x41, 0x6d, 0x9a, 0x7b, 0xaa, 0xa5, 0x0d, 0xb3, 0x6a, 0x0b, 0xa3, 0xd8, - 0xdf, 0x1e, 0xaa, 0xac, 0x19, 0x46, 0x2b, 0x30, 0xb6, 0xf1, 0xec, 0x4e, - 0x9c, 0xb0, 0x86, 0xb9, 0x84, 0x1c, 0x2b, 0x80, 0xff, 0xf1, 0x50, 0x80, - 0x3b, 0xff, 0xfc, 0x21, 0x4c, 0x9a, 0x82, 0x0e, 0x78, 0xca, 0x81, 0xa0, - 0x4b, 0x5a, 0x39, 0xa8, 0x65, 0x9a, 0xcc, 0xcc, 0xd9, 0xb3, 0x0e, 0x73, - 0x1a, 0x36, 0x93, 0x34, 0x01, 0xe7, 0x00, 0x2e, 0xae, 0x72, 0x7f, 0xe5, - 0xff, 0x80, 0x38, 0x27, 0xb7, 0xe8, 0x47, 0x1b, 0xd6, 0xab, 0xad, 0x8a, - 0x14, 0x31, 0xe5, 0x0a, 0x34, 0x8e, 0x23, 0x8c, 0xd4, 0xbe, 0xb2, 0x14, - 0xd1, 0x15, 0x84, 0x00, 0x2a, 0x00, 0x00, 0x26, 0xe0, 0x72, 0x06, 0xb5, - 0x53, 0x86, 0x61, 0x88, 0x59, 0x5f, 0x9f, 0x6f, 0x3f, 0x3f, 0x1e, 0xde, - 0x7e, 0x3f, 0xcb, 0xe5, 0xca, 0x22, 0xe8, 0xb3, 0x39, 0x42, 0xcf, 0x1c, - 0xf3, 0xcf, 0xfb, 0x81, 0xa3, 0xec, 0xb9, 0x1f, 0xf3, 0xe4, 0xc4, 0x0f, - 0x9b, 0xd6, 0xee, 0x22, 0x50, 0x4a, 0xe3, 0xa2, 0x20, 0xaa, 0xd0, 0x8b, - 0x71, 0x7f, 0x8e, 0x2b, 0x24, 0xbf, 0x0e, 0xdf, 0xe8, 0xa7, 0x11, 0x01, - 0xe0, 0x16, 0x01, 0x34, 0x3f, 0xfc, 0x9f, 0xe5, 0x63, 0xbb, 0xee, 0x77, - 0x99, 0xe1, 0xe3, 0x96, 0xd9, 0x19, 0xab, 0xc5, 0x74, 0x38, 0xf7, 0x2c, - 0xd8, 0xe5, 0xc6, 0xc2, 0x99, 0x61, 0xbf, 0x8b, 0x64, 0x4d, 0x9e, 0xf3, - 0xf6, 0x5e, 0xed, 0xf5, 0x2f, 0x1b, 0xfa, 0x4f, 0x13, 0x38, 0xcf, 0x2a, - 0x90, 0x9f, 0x96, 0x7f, 0xae, 0xfb, 0x57, 0x7d, 0xe9, 0x5a, 0xfc, 0x1d, - 0xf1, 0x59, 0x65, 0x71, 0xb2, 0x17, 0xbf, 0x4b, 0x62, 0x9e, 0xa3, 0x11, - 0x8b, 0xa4, 0x45, 0x64, 0x70, 0xda, 0xa9, 0xaa, 0x3c, 0xc6, 0x97, 0x39, - 0x90, 0xb0, 0x92, 0x50, 0x45, 0x12, 0x59, 0xc1, 0x24, 0x2d, 0x2a, 0x29, - 0x8a, 0x44, 0xf9, 0xf4, 0x0e, 0xaa, 0x09, 0xeb, 0x2d, 0x2e, 0x45, 0x14, - 0xfe, 0xfc, 0xa1, 0x13, 0x74, 0x53, 0x27, 0x09, 0x05, 0x1f, 0xd9, 0x88, - 0xb0, 0xef, 0xfd, 0x66, 0xae, 0x19, 0xd3, 0x07, 0x7a, 0x28, 0xa0, 0x1c, - 0xc2, 0xb0, 0xa5, 0x7b, 0x0b, 0x23, 0x45, 0x25, 0x9b, 0xe1, 0x85, 0x15, - 0x9c, 0xc8, 0x92, 0xcc, 0xc9, 0x16, 0xaa, 0x18, 0x2a, 0x3d, 0xe6, 0x13, - 0x56, 0x8e, 0xe9, 0xf2, 0x07, 0x94, 0x36, 0xbe, 0xec, 0xd9, 0x8e, 0x3e, - 0x5a, 0x6d, 0xe6, 0x0f, 0xa0, 0x71, 0xcb, 0xa7, 0x72, 0x70, 0xb9, 0x55, - 0x72, 0xef, 0x7f, 0xef, 0xff, 0x8e, 0x5c, 0x65, 0xf5, 0x5b, 0xe7, 0xdb, - 0x3d, 0xdc, 0x4a, 0xeb, 0xaf, 0x69, 0x93, 0x07, 0x8e, 0x9d, 0xe5, 0xc6, - 0x4f, 0x21, 0x39, 0x1a, 0x1e, 0xa7, 0xb9, 0x5f, 0x64, 0x03, 0xc5, 0x14, - 0x02, 0xe6, 0x07, 0x09, 0xb8, 0x5d, 0x34, 0xb7, 0x64, 0x8a, 0x27, 0x62, - 0xae, 0x64, 0x0c, 0x52, 0x0d, 0x51, 0x08, 0x70, 0x48, 0x41, 0x51, 0x02, - 0x10, 0x80, 0x8c, 0x40, 0x80, 0xd8, 0x1f, 0x69, 0x37, 0xff, 0x6d, 0xa7, - 0xc0, 0xce, 0xd4, 0xd8, 0xb7, 0x84, 0x78, 0x0c, 0xd1, 0x8b, 0x18, 0x31, - 0x54, 0x32, 0x4e, 0xd2, 0x4a, 0x32, 0xcd, 0x71, 0xc9, 0xf0, 0x50, 0x32, - 0x8c, 0x67, 0x3e, 0x25, 0x6a, 0xa3, 0xb2, 0xc8, 0x4c, 0x8a, 0xc8, 0x2c, - 0x2a, 0xb6, 0x95, 0xc2, 0xc6, 0x30, 0x14, 0xb0, 0xac, 0x00, 0xaf, 0xc2, - 0x66, 0x2b, 0x4b, 0xb5, 0xd5, 0x03, 0x71, 0x54, 0xa3, 0x84, 0xa8, 0xa1, - 0xde, 0xb7, 0x43, 0x81, 0x51, 0x04, 0xad, 0xf6, 0xa0, 0xd7, 0x3a, 0xc4, - 0xbf, 0x9e, 0x3d, 0xbe, 0xda, 0xbd, 0x39, 0xcd, 0x42, 0x0f, 0xbd, 0x3d, - 0x53, 0xd3, 0x40, 0xc4, 0x02, 0xe3, 0x80, 0xff, 0xf1, 0x50, 0x80, 0x2c, - 0xff, 0xfc, 0x21, 0x7a, 0xce, 0xf2, 0x33, 0xae, 0x09, 0x00, 0xa3, 0xb4, - 0xb3, 0x12, 0x6a, 0x32, 0x5f, 0x0e, 0xbb, 0xfd, 0xfe, 0xcd, 0xdd, 0xe6, - 0x95, 0x57, 0x52, 0x9c, 0x37, 0x24, 0xbe, 0xf5, 0x95, 0xaa, 0x27, 0x9f, - 0x7f, 0xa1, 0x46, 0x64, 0x10, 0x69, 0xbd, 0x66, 0xd4, 0xda, 0x2e, 0x8f, - 0x6b, 0x42, 0xc2, 0xd0, 0x6a, 0xe3, 0x5f, 0x70, 0x54, 0x01, 0xf4, 0x23, - 0xbb, 0xb1, 0xcf, 0xba, 0x88, 0x3d, 0xd8, 0xd8, 0x22, 0xc8, 0x37, 0x50, - 0x35, 0x2d, 0x68, 0x77, 0x59, 0x34, 0xc2, 0xc6, 0x75, 0x89, 0xc8, 0x89, - 0x0f, 0x8c, 0x02, 0x13, 0x88, 0x6f, 0x6a, 0x82, 0x97, 0x47, 0x59, 0x91, - 0xfa, 0x20, 0x66, 0x77, 0xce, 0xe5, 0x61, 0x9c, 0x26, 0xbf, 0x7a, 0x9c, - 0x57, 0xf5, 0x64, 0x36, 0xd8, 0x74, 0x87, 0x8d, 0x60, 0xac, 0x68, 0x80, - 0xf9, 0x6a, 0xa2, 0x45, 0x0e, 0xc6, 0xf4, 0xbc, 0xda, 0x28, 0xe9, 0x4c, - 0x0b, 0x67, 0xb4, 0x19, 0x2d, 0x4d, 0x53, 0xce, 0xbc, 0x49, 0x2c, 0x3b, - 0xac, 0x7d, 0xf7, 0xa7, 0x83, 0x41, 0x58, 0x98, 0x8b, 0xfc, 0x05, 0xac, - 0x3b, 0x89, 0x4b, 0xe5, 0x6a, 0xd4, 0xbb, 0x9a, 0x70, 0x61, 0xd4, 0x99, - 0x7c, 0xa3, 0x08, 0x49, 0x8f, 0x52, 0x6d, 0xcc, 0x8c, 0xed, 0x51, 0x67, - 0xf7, 0x68, 0x7b, 0xe4, 0x55, 0xf3, 0x0c, 0xec, 0x08, 0xbd, 0x9d, 0x4d, - 0x64, 0x92, 0xa7, 0xbd, 0xbb, 0xb6, 0x0f, 0xe8, 0x99, 0x7a, 0xeb, 0x66, - 0xf5, 0x4d, 0x30, 0x93, 0xa0, 0x2c, 0x47, 0x9d, 0xf5, 0x72, 0x77, 0x72, - 0xc6, 0xb9, 0xf9, 0x41, 0x5f, 0xae, 0xeb, 0x5b, 0xd4, 0xc2, 0xf3, 0x71, - 0x32, 0x7a, 0xfb, 0x71, 0xdf, 0xf6, 0x67, 0xea, 0xc0, 0x05, 0x1c, 0x0d, - 0x9c, 0x8b, 0xf7, 0x99, 0xd7, 0x0e, 0xbe, 0xde, 0x6f, 0xf4, 0x9e, 0x7c, - 0xe6, 0xeb, 0xcd, 0x5f, 0xaf, 0x6f, 0x6f, 0x7e, 0x1c, 0x57, 0x36, 0xcb, - 0x64, 0xa9, 0x6d, 0x73, 0x61, 0x5a, 0x8d, 0x5d, 0x96, 0x34, 0x60, 0xec, - 0xf0, 0x34, 0x2c, 0x66, 0xaf, 0xc7, 0x3a, 0xf5, 0xf0, 0x02, 0xaf, 0xeb, - 0xea, 0x62, 0x44, 0x6c, 0xb2, 0x41, 0xb5, 0x41, 0x12, 0x7e, 0x53, 0x1a, - 0x31, 0x8f, 0x98, 0x63, 0x5e, 0x0e, 0xcd, 0x43, 0x34, 0xd7, 0xaf, 0x3a, - 0x5f, 0xf8, 0xdf, 0x15, 0x65, 0x59, 0x4f, 0xa8, 0x28, 0xfd, 0xca, 0x8a, - 0xaf, 0x0e, 0x11, 0xa0, 0x36, 0xbf, 0xb7, 0x61, 0xdd, 0x3a, 0xb8, 0x62, - 0xdc, 0x2c, 0x9b, 0x5e, 0xa7, 0xe5, 0x1a, 0xef, 0xef, 0x2b, 0xfc, 0xda, - 0x40, 0x9d, 0x30, 0x0b, 0x83, 0x5c, 0xff, 0xf1, 0x50, 0x80, 0x29, 0xdf, - 0xfc, 0x21, 0x1a, 0xcf, 0x74, 0xb3, 0x66, 0x9d, 0x12, 0xa5, 0xa5, 0xb1, - 0x90, 0xcc, 0x14, 0x99, 0x3c, 0xb5, 0xad, 0xf1, 0x59, 0x97, 0x6b, 0xeb, - 0xd4, 0xf3, 0xea, 0x38, 0xdd, 0xef, 0xdb, 0x74, 0xaa, 0x98, 0x95, 0x57, - 0x2b, 0x81, 0x82, 0xdc, 0x74, 0x61, 0xde, 0xc3, 0x97, 0x01, 0x4e, 0x50, - 0x37, 0xe5, 0xa5, 0x66, 0x65, 0x62, 0x87, 0x47, 0x84, 0x10, 0xf6, 0xf5, - 0x5f, 0x94, 0xca, 0x1a, 0x13, 0x2a, 0x0c, 0x4c, 0x8a, 0x89, 0xa5, 0xa8, - 0x6a, 0xcc, 0xf5, 0x67, 0x5b, 0x58, 0xc7, 0x32, 0xfe, 0x8c, 0x2b, 0xeb, - 0x0e, 0xff, 0x62, 0xa0, 0xf6, 0xb5, 0xca, 0xd9, 0x2e, 0x80, 0x49, 0x61, - 0xc6, 0xd8, 0x3e, 0x59, 0x0c, 0x38, 0x62, 0xbb, 0x0e, 0xe7, 0xda, 0x2b, - 0x60, 0x3c, 0xb5, 0x2f, 0x18, 0x01, 0xcd, 0x17, 0x92, 0x92, 0x1b, 0x48, - 0x53, 0x36, 0x84, 0xf5, 0x7e, 0x31, 0x33, 0x39, 0x09, 0xe9, 0x97, 0x7f, - 0x8e, 0x14, 0x04, 0x3a, 0x06, 0xe0, 0x2d, 0x4d, 0x72, 0xd7, 0xcc, 0x76, - 0xbd, 0xa0, 0x00, 0xf5, 0x40, 0x66, 0xca, 0xe6, 0x3c, 0xbe, 0x3b, 0x48, - 0xd4, 0x75, 0x0d, 0xbc, 0xf5, 0xe2, 0x2b, 0xca, 0x1d, 0x48, 0xc4, 0x35, - 0x55, 0xf5, 0x2b, 0xe0, 0xac, 0x99, 0x9a, 0xf5, 0x29, 0xde, 0x5c, 0x5d, - 0x4d, 0xdf, 0x81, 0xc3, 0xe3, 0x21, 0x85, 0x46, 0x4e, 0xd9, 0x92, 0xf5, - 0x35, 0xa5, 0x01, 0x30, 0x59, 0xd9, 0xad, 0xb3, 0xa2, 0x91, 0xc8, 0xfb, - 0xe7, 0x8b, 0x99, 0x77, 0xe8, 0xc7, 0xa0, 0x66, 0x0f, 0x24, 0x27, 0xed, - 0xe7, 0xde, 0x96, 0x14, 0xe7, 0x44, 0xb0, 0xbd, 0xe4, 0xe2, 0xf5, 0xaf, - 0x5f, 0x1c, 0xfc, 0xfc, 0x38, 0x6d, 0xa8, 0xa9, 0xed, 0xea, 0x5f, 0x9c, - 0xc5, 0x17, 0x80, 0xb9, 0x41, 0x5f, 0xa5, 0x18, 0xef, 0x28, 0xbd, 0x60, - 0x56, 0xb3, 0x89, 0x42, 0xd4, 0xdb, 0x0a, 0x4d, 0x71, 0x9f, 0xa6, 0xdf, - 0x33, 0x18, 0x38, 0xbe, 0x8b, 0xb4, 0x29, 0xa1, 0xa3, 0xa1, 0xe5, 0xea, - 0x11, 0x0e, 0xee, 0xd7, 0x3f, 0xbf, 0xbd, 0x9b, 0x72, 0x1c, 0xda, 0xcb, - 0x97, 0xb8, 0x6b, 0x93, 0xbd, 0x6d, 0x43, 0xfe, 0x3f, 0x9e, 0x83, 0xd6, - 0x84, 0x9e, 0xad, 0x42, 0xac, 0x1b, 0x43, 0x25, 0x0c, 0x64, 0xaf, 0x70, - 0x6f, 0x06, 0x07, 0xb9, 0x6d, 0x13, 0x8a, 0x59, 0x64, 0x06, 0x40, 0x96, - 0x10, 0xc6, 0x00, 0xf8, 0xff, 0xf1, 0x50, 0x80, 0x2c, 0xdf, 0xfc, 0x21, - 0x2a, 0xc9, 0xfc, 0xb3, 0xff, 0x20, 0xa0, 0xa3, 0x84, 0x33, 0x92, 0xad, - 0xf5, 0x77, 0xa9, 0xbd, 0x4a, 0xdd, 0xc8, 0x95, 0x15, 0x6a, 0xd5, 0x5e, - 0x5e, 0x67, 0x0c, 0xdc, 0xbc, 0xae, 0x99, 0x63, 0x1b, 0x16, 0x96, 0xdb, - 0x3e, 0xa9, 0x8f, 0x6b, 0xd0, 0x32, 0x1b, 0xcc, 0x5a, 0x70, 0x3e, 0xc7, - 0xd5, 0x73, 0x60, 0xbf, 0xbe, 0x41, 0x8f, 0x85, 0x5c, 0xf9, 0x79, 0x36, - 0x68, 0xa2, 0x12, 0x0a, 0xa8, 0x33, 0xb7, 0x5d, 0xbd, 0x97, 0x78, 0xd8, - 0x63, 0x1a, 0xea, 0x05, 0x97, 0xa9, 0x20, 0xad, 0x9c, 0x8c, 0x6e, 0x44, - 0x87, 0x80, 0x10, 0x39, 0xfe, 0x58, 0x0c, 0xd8, 0x87, 0x87, 0xfd, 0xbb, - 0x27, 0xb2, 0x0f, 0x59, 0x6a, 0xd4, 0x72, 0xa2, 0xcd, 0x89, 0x6e, 0x81, - 0xab, 0xe3, 0xfd, 0x0f, 0x71, 0xed, 0x96, 0xbf, 0xb8, 0xd2, 0xa1, 0x5c, - 0x4b, 0x02, 0x53, 0x23, 0xef, 0x35, 0x3f, 0xe2, 0x4f, 0xfa, 0xc8, 0x11, - 0x3a, 0x32, 0xe4, 0xa8, 0xb3, 0x07, 0xca, 0x3b, 0x25, 0x72, 0x7e, 0x88, - 0x3f, 0x2f, 0xe7, 0x29, 0xb5, 0xbf, 0x45, 0xca, 0xbb, 0xa0, 0xf5, 0x67, - 0xb8, 0xc3, 0xaa, 0xb0, 0x0e, 0xfb, 0x26, 0x2b, 0x8f, 0x42, 0x3a, 0xec, - 0x0e, 0x9a, 0xfd, 0xec, 0x88, 0x1e, 0xba, 0x9b, 0x25, 0xdb, 0x4a, 0x6a, - 0x34, 0x6e, 0x15, 0x37, 0xcc, 0x81, 0xc3, 0xbd, 0x77, 0x96, 0x2e, 0x9a, - 0x3b, 0x08, 0x44, 0xe9, 0x29, 0xa1, 0x41, 0x5a, 0x09, 0x58, 0x7a, 0x1d, - 0x13, 0x59, 0x86, 0x86, 0x3f, 0x0a, 0xf5, 0x10, 0x7e, 0xcf, 0xfd, 0xfa, - 0x5b, 0x8b, 0x63, 0xcd, 0x1d, 0x85, 0xdb, 0xcb, 0x62, 0xdf, 0x34, 0x8a, - 0x0f, 0xcf, 0xee, 0x9d, 0x33, 0xd2, 0xc2, 0xd9, 0x68, 0x56, 0x0f, 0xbc, - 0xe2, 0xef, 0x53, 0x7a, 0xaf, 0x7f, 0x2d, 0x4d, 0xd7, 0x11, 0x56, 0xad, - 0x55, 0xeb, 0x74, 0x82, 0xa5, 0x38, 0xac, 0x82, 0xff, 0x93, 0xf6, 0xb9, - 0x41, 0x71, 0xb2, 0x63, 0x9c, 0xe1, 0x39, 0x10, 0xb6, 0x33, 0xdc, 0xd2, - 0x52, 0x8d, 0xcb, 0xea, 0x47, 0x8d, 0x72, 0x27, 0xf3, 0x85, 0xe6, 0xb2, - 0xb0, 0x46, 0xef, 0xa9, 0x92, 0x37, 0x9f, 0xa0, 0x76, 0x56, 0xde, 0x5d, - 0x38, 0xfa, 0x6c, 0xa8, 0xc1, 0x49, 0x12, 0xf5, 0xb1, 0x4d, 0xfc, 0x2a, - 0x76, 0x10, 0xbf, 0xbe, 0xf3, 0x61, 0x39, 0x6e, 0x93, 0xc3, 0xb4, 0xb7, - 0xbe, 0xdf, 0xd7, 0x52, 0x6b, 0x20, 0x51, 0x5c, 0x84, 0x49, 0x0b, 0xd6, - 0xd4, 0xa4, 0x4e, 0x36, 0xb3, 0xae, 0x6d, 0xdf, 0x40, 0xa2, 0x0c, 0x32, - 0x40, 0xb7, 0xff, 0xf1, 0x50, 0x80, 0x36, 0xdf, 0xfc, 0x21, 0x4c, 0x9a, - 0xe7, 0x89, 0xf8, 0xcf, 0x84, 0xf8, 0x49, 0xda, 0x64, 0x3c, 0xdb, 0x6c, - 0x8a, 0xcd, 0xba, 0x6c, 0xd5, 0xdb, 0x66, 0x8c, 0x98, 0x00, 0x00, 0x0f, - 0xfb, 0xff, 0xd8, 0x00, 0x00, 0x94, 0xd4, 0x18, 0xef, 0xb2, 0x2a, 0x49, - 0x11, 0xa6, 0xa0, 0x1b, 0xf9, 0x2a, 0xa6, 0xbb, 0x48, 0xdb, 0xb3, 0x6f, - 0xab, 0x0e, 0xff, 0x1b, 0xa5, 0x78, 0x6b, 0x54, 0x3b, 0x15, 0x9d, 0x2f, - 0xb3, 0xf6, 0xe6, 0x6e, 0x19, 0x11, 0x4e, 0x31, 0x66, 0x59, 0xa1, 0x2b, - 0xb9, 0x34, 0xe9, 0xe1, 0x52, 0x59, 0x8a, 0x78, 0x31, 0xca, 0x72, 0xcb, - 0xcb, 0xe5, 0xf2, 0xf9, 0x7a, 0xa8, 0x5c, 0xf3, 0x55, 0xae, 0x21, 0x5f, - 0xcf, 0xfd, 0xbd, 0xf5, 0x2c, 0x97, 0xea, 0x76, 0xf8, 0x49, 0x80, 0x44, - 0xd0, 0x0d, 0x95, 0xd0, 0x58, 0x19, 0xa7, 0x71, 0x56, 0x68, 0xba, 0xd2, - 0x4f, 0x14, 0xb2, 0x6a, 0x77, 0xde, 0x2f, 0x9e, 0x29, 0xa3, 0x31, 0x7a, - 0xb2, 0xe2, 0xce, 0x3c, 0xcd, 0xa9, 0xf7, 0x17, 0x57, 0xb7, 0x97, 0x56, - 0x5c, 0x81, 0xee, 0x57, 0xab, 0x3c, 0xb4, 0x57, 0x9a, 0xe7, 0x6b, 0x10, - 0x39, 0xe6, 0xaa, 0x08, 0x51, 0x20, 0xb5, 0x6a, 0x76, 0x11, 0x4a, 0x1e, - 0x85, 0xbf, 0xdf, 0x6e, 0x01, 0x30, 0x83, 0x84, 0x18, 0xea, 0xaf, 0xb4, - 0x7c, 0xa6, 0x60, 0x80, 0x2f, 0x94, 0xe0, 0xa5, 0x31, 0xfb, 0x78, 0x56, - 0x6f, 0x13, 0x44, 0x51, 0x75, 0xd5, 0xbc, 0xe6, 0x14, 0x0c, 0x64, 0x5e, - 0x7b, 0xbb, 0xa2, 0xb2, 0x52, 0x4b, 0x01, 0x74, 0x21, 0x1a, 0x35, 0x42, - 0xa2, 0x3a, 0xb2, 0xe0, 0x0c, 0x97, 0xcf, 0x9a, 0x25, 0xb4, 0xea, 0xec, - 0xa5, 0xfa, 0x32, 0x44, 0x65, 0x94, 0xd5, 0xb6, 0x15, 0xc8, 0x6b, 0x01, - 0xa1, 0x88, 0x9f, 0x8f, 0x04, 0x1b, 0x10, 0x07, 0x64, 0x17, 0xf8, 0x98, - 0x35, 0x2b, 0x96, 0x60, 0x8d, 0xb8, 0xba, 0x71, 0x26, 0x43, 0x23, 0x5e, - 0x19, 0x9a, 0x40, 0xcf, 0x8a, 0xdb, 0x93, 0xa6, 0xc8, 0xdf, 0x90, 0x3c, - 0xad, 0x32, 0x19, 0xee, 0x8d, 0xb6, 0x62, 0xbe, 0x5c, 0x59, 0xf0, 0x03, - 0x10, 0x02, 0x4a, 0xff, 0xbf, 0xfd, 0x80, 0x10, 0x02, 0x56, 0x42, 0x0e, - 0xfa, 0xb1, 0x03, 0x91, 0x60, 0x9e, 0xd6, 0xf6, 0x6a, 0x61, 0x6a, 0x4b, - 0x66, 0xc1, 0x89, 0x47, 0xd1, 0x30, 0x9e, 0x73, 0x88, 0xfb, 0x9a, 0xdc, - 0x09, 0x38, 0x46, 0xd8, 0x8b, 0x09, 0x3e, 0x87, 0x68, 0xcc, 0x46, 0xd9, - 0xc4, 0x8b, 0x9c, 0xb6, 0x3d, 0xd6, 0xee, 0xbb, 0x9a, 0xe0, 0x76, 0xf6, - 0x1e, 0xc1, 0xb5, 0xb5, 0x21, 0xee, 0x25, 0xc6, 0xf3, 0x06, 0x67, 0xf0, - 0xfd, 0xd5, 0x2b, 0x97, 0x3d, 0x6e, 0xaa, 0xa7, 0xd6, 0xb9, 0x2f, 0x3f, - 0x73, 0x9a, 0x37, 0xd4, 0x20, 0xe7, 0xa1, 0xf2, 0x6a, 0xdd, 0x2f, 0x51, - 0x24, 0x36, 0xbc, 0xcb, 0x67, 0xfb, 0x2b, 0xfc, 0xd1, 0x8a, 0x52, 0xe3, - 0x2f, 0xac, 0xde, 0x12, 0x68, 0xa8, 0x76, 0xc6, 0xd4, 0x12, 0xfd, 0x0e, - 0x21, 0xde, 0x50, 0x0c, 0xfc, 0x75, 0x03, 0x52, 0x7d, 0x3a, 0x75, 0xc5, - 0xc5, 0x30, 0xa5, 0x04, 0x16, 0x4c, 0xc5, 0xf0, 0xff, 0xf1, 0x50, 0x80, - 0x3b, 0xff, 0xfc, 0x20, 0x9c, 0x4c, 0xdb, 0x66, 0x11, 0x20, 0x7a, 0x80, - 0xe9, 0xb4, 0xdd, 0xda, 0x6e, 0xc0, 0xef, 0xb0, 0x79, 0xf3, 0x9f, 0x72, - 0xaf, 0xfe, 0x87, 0x5d, 0x4f, 0xa9, 0xba, 0xe8, 0x09, 0x80, 0xb9, 0x88, - 0x92, 0x05, 0x99, 0x3e, 0x17, 0x9e, 0x2d, 0xea, 0xeb, 0xe8, 0xce, 0x59, - 0x10, 0x0e, 0x1b, 0xe4, 0xb2, 0x7f, 0xc8, 0x52, 0xe4, 0x4e, 0x6e, 0xc0, - 0x64, 0x58, 0x44, 0xe3, 0x1b, 0x8c, 0x8e, 0xbd, 0x1d, 0x6e, 0x6e, 0xd7, - 0x15, 0x1b, 0x41, 0x25, 0xb8, 0xc0, 0x41, 0xe1, 0x10, 0xa0, 0x99, 0xad, - 0x95, 0x56, 0x48, 0xac, 0x41, 0xc0, 0x64, 0x64, 0x2a, 0x4c, 0x06, 0x91, - 0x0d, 0xcd, 0x9d, 0x3e, 0x1c, 0x6b, 0x39, 0x69, 0xb9, 0x5f, 0xaf, 0x1a, - 0x23, 0x4e, 0x20, 0x35, 0xe7, 0x22, 0x31, 0x82, 0x9d, 0xc3, 0xa2, 0x98, - 0x2e, 0xe9, 0x25, 0xc2, 0x7a, 0x04, 0xc2, 0x30, 0x3d, 0xf2, 0xcd, 0x3a, - 0x59, 0x54, 0xda, 0xe2, 0x6a, 0x55, 0x89, 0xdd, 0xb5, 0xd5, 0xeb, 0xdb, - 0x22, 0x00, 0x1f, 0x86, 0xf5, 0xa9, 0x80, 0x21, 0x6d, 0x99, 0x88, 0x90, - 0x94, 0x61, 0x72, 0x1a, 0x22, 0x77, 0x3e, 0xe7, 0x16, 0x38, 0xd2, 0x03, - 0xc2, 0xb4, 0xb2, 0x61, 0x98, 0xee, 0xb9, 0x4e, 0x60, 0x87, 0x69, 0x82, - 0xef, 0xa6, 0xa6, 0x96, 0x69, 0xfe, 0x9d, 0x5f, 0xa3, 0x97, 0xf6, 0xcb, - 0xa3, 0x6d, 0x92, 0x9d, 0x2c, 0xf3, 0x4e, 0xf5, 0x48, 0x55, 0x4e, 0x39, - 0xdb, 0x7d, 0x6b, 0xb6, 0x4b, 0x82, 0xfb, 0x79, 0xf2, 0x5e, 0xd9, 0x07, - 0xfc, 0x99, 0x5a, 0x77, 0xcd, 0x2c, 0xf3, 0x87, 0x43, 0xe5, 0xa2, 0xef, - 0xda, 0xdd, 0x75, 0x8c, 0xfb, 0xd6, 0xb6, 0xb0, 0xe8, 0x2c, 0x34, 0xa3, - 0x0c, 0x00, 0x00, 0x03, 0xaa, 0xd2, 0x4c, 0xbc, 0xd7, 0x1e, 0xf3, 0xcf, - 0x77, 0xc5, 0x7d, 0x0c, 0x74, 0x48, 0xd8, 0x42, 0x17, 0xf1, 0x24, 0x5f, - 0x0a, 0x59, 0x66, 0x02, 0x5c, 0xc5, 0x82, 0x8f, 0xee, 0x59, 0x27, 0x67, - 0x6d, 0xbb, 0xab, 0x45, 0xed, 0x29, 0x9e, 0x53, 0x1d, 0x53, 0x0b, 0x76, - 0x24, 0x48, 0xa1, 0x4e, 0x3f, 0x91, 0x40, 0x0f, 0x0f, 0x18, 0xfb, 0x0f, - 0xbd, 0xd0, 0xe4, 0x96, 0xb7, 0x14, 0x29, 0xc5, 0x4a, 0x57, 0xd9, 0x01, - 0x73, 0x14, 0x15, 0x71, 0x37, 0x16, 0xbf, 0x51, 0x09, 0x17, 0x40, 0xb7, - 0x8e, 0xdc, 0x4f, 0xa2, 0x2d, 0x3f, 0x65, 0xac, 0xbd, 0x91, 0xde, 0x66, - 0xa2, 0x73, 0x37, 0xb1, 0xbd, 0x48, 0xc0, 0x7d, 0x2a, 0x3c, 0x7e, 0x0b, - 0xb8, 0x0f, 0x60, 0x74, 0xd8, 0x19, 0xa4, 0x4e, 0x00, 0x36, 0xe2, 0x12, - 0xb8, 0xd0, 0x5f, 0xcd, 0x24, 0x7f, 0x6e, 0x00, 0x55, 0x0c, 0x49, 0x86, - 0x03, 0x84, 0x13, 0xc6, 0xc2, 0x33, 0xe7, 0x77, 0x57, 0x89, 0xbe, 0x8e, - 0x97, 0x92, 0xda, 0xa9, 0x10, 0xa3, 0x07, 0x31, 0x0e, 0x61, 0x1b, 0x75, - 0x23, 0xbf, 0xb8, 0x87, 0x7e, 0x93, 0x41, 0x84, 0x0f, 0xfc, 0x85, 0x77, - 0x78, 0xc4, 0x28, 0x7c, 0x1c, 0xac, 0x30, 0xc0, 0x7b, 0x90, 0x3f, 0x50, - 0x3d, 0x0b, 0x83, 0x08, 0x08, 0xd6, 0x7f, 0x6f, 0xff, 0xc0, 0xfc, 0x06, - 0x07, 0xa4, 0x23, 0x4a, 0x02, 0x3a, 0xf8, 0xc3, 0x76, 0x18, 0x0f, 0xa9, - 0x50, 0x14, 0x16, 0x68, 0x76, 0x85, 0xb2, 0x03, 0xdd, 0x3c, 0x96, 0x10, - 0xb4, 0x2d, 0xe7, 0x90, 0x28, 0xca, 0x86, 0xdb, 0x7c, 0x7e, 0xb8, 0x54, - 0xb9, 0xf4, 0x7b, 0x96, 0x59, 0x46, 0xee, 0xff, 0xf1, 0x50, 0x80, 0x35, - 0x7f, 0xfc, 0x20, 0xa7, 0x4c, 0x9b, 0x04, 0x90, 0x75, 0x05, 0xbc, 0xa2, - 0x91, 0xb2, 0x1a, 0x23, 0x21, 0xb1, 0x46, 0x07, 0x15, 0xc2, 0xe4, 0x8f, - 0xd0, 0xbd, 0x7b, 0x6f, 0xa9, 0x0f, 0xe8, 0x7b, 0x71, 0x3e, 0x26, 0x67, - 0x01, 0xa9, 0x4f, 0x1b, 0x0e, 0x3d, 0x22, 0x0f, 0x84, 0x73, 0x1b, 0xa0, - 0x44, 0xa9, 0x53, 0x7c, 0xe5, 0xc6, 0xec, 0x13, 0x1b, 0x95, 0x76, 0x0d, - 0x63, 0x8e, 0xfd, 0x57, 0xc6, 0x52, 0xcf, 0x35, 0xd5, 0x1b, 0x27, 0x0c, - 0xf1, 0xaf, 0x85, 0x29, 0x23, 0xa3, 0xa9, 0x4a, 0xe8, 0xe6, 0xbd, 0x74, - 0xed, 0xa6, 0xcd, 0xb5, 0x09, 0x3f, 0xf3, 0xc7, 0x9f, 0xec, 0xd3, 0x42, - 0xba, 0x09, 0x20, 0xbb, 0x09, 0x91, 0x09, 0x91, 0x92, 0x09, 0xa2, 0xe4, - 0xec, 0xe0, 0x4a, 0x49, 0xbb, 0xc6, 0x89, 0x7c, 0xd5, 0xbe, 0x23, 0x2d, - 0x07, 0x0e, 0xa6, 0x09, 0x7d, 0xf2, 0x57, 0xc3, 0x98, 0xc9, 0x58, 0x25, - 0xe2, 0x4e, 0xc8, 0x88, 0xfc, 0xfc, 0x28, 0x39, 0x72, 0x95, 0x0c, 0x9d, - 0x90, 0x22, 0x58, 0x5a, 0xe2, 0xc9, 0x57, 0x59, 0xb7, 0x6f, 0x0c, 0x65, - 0xcd, 0x0c, 0x7a, 0xc9, 0x1a, 0xc8, 0xec, 0x96, 0x51, 0x06, 0x38, 0x02, - 0x61, 0x15, 0x03, 0x57, 0xa4, 0xd7, 0x05, 0xe7, 0x97, 0x3f, 0x5f, 0x8f, - 0x0a, 0x29, 0x44, 0x7a, 0xb4, 0x89, 0xa3, 0x10, 0x9b, 0x8a, 0x5b, 0x86, - 0x13, 0x67, 0x8d, 0x4f, 0x84, 0xf8, 0x03, 0x64, 0xeb, 0xbd, 0xda, 0x04, - 0x3e, 0x33, 0x0a, 0xea, 0xb3, 0x52, 0x35, 0xad, 0x08, 0x47, 0x2a, 0x18, - 0x90, 0x88, 0x62, 0x00, 0x00, 0xbc, 0xbd, 0x72, 0x9e, 0xdf, 0x37, 0x29, - 0x52, 0x71, 0x5a, 0xde, 0xdc, 0x4f, 0x6b, 0xdf, 0xd0, 0x41, 0xf9, 0x4f, - 0xc0, 0x74, 0x9c, 0x2e, 0x04, 0xea, 0x91, 0x54, 0x90, 0x28, 0x9e, 0x3c, - 0x34, 0x8b, 0x88, 0x11, 0xdc, 0x87, 0x76, 0xc7, 0xb9, 0xad, 0xbf, 0xbb, - 0x2d, 0x93, 0x13, 0x8e, 0xdb, 0xac, 0x13, 0x8e, 0x59, 0xf9, 0x99, 0x57, - 0x4f, 0x53, 0xa2, 0x47, 0x1e, 0x34, 0x02, 0x6c, 0x76, 0xa1, 0xc4, 0xd4, - 0x41, 0xdf, 0x7d, 0x2a, 0x1f, 0x53, 0x3b, 0xbe, 0x9e, 0x0b, 0x40, 0xc0, - 0xa4, 0x5b, 0x1a, 0x8c, 0xf3, 0x55, 0xb0, 0x45, 0x15, 0x49, 0x70, 0xbb, - 0xc6, 0xb1, 0x01, 0xe6, 0x67, 0x61, 0xba, 0x57, 0x52, 0x40, 0x89, 0x28, - 0x0f, 0x30, 0xc7, 0x1a, 0x56, 0xc7, 0xa1, 0xd5, 0x31, 0x6d, 0x53, 0x4d, - 0x42, 0x57, 0x3a, 0xcb, 0xd3, 0x9a, 0x48, 0x6c, 0x16, 0x60, 0xf7, 0xb3, - 0x85, 0x66, 0xe3, 0xa0, 0xb4, 0x78, 0x4c, 0xac, 0x5e, 0xcb, 0xb8, 0x26, - 0xad, 0x7a, 0x5b, 0xaf, 0x36, 0x59, 0x96, 0x47, 0xb4, 0xb4, 0x96, 0xcb, - 0x49, 0x34, 0x2e, 0x13, 0xa0, 0x81, 0x38, 0xc8, 0x43, 0x4d, 0xb8, 0x5e, - 0x95, 0x83, 0x24, 0x86, 0x84, 0x34, 0x3d, 0x58, 0xda, 0x76, 0xcf, 0xae, - 0xd3, 0x0b, 0xaa, 0xa2, 0x69, 0x00, 0xda, 0xa6, 0x3b, 0x54, 0xa9, 0xac, - 0x2c, 0x8c, 0xe3, 0x65, 0x3e, 0x9f, 0xf6, 0x4d, 0x69, 0xdd, 0x32, 0x0b, - 0x24, 0xbc, 0xff, 0xf1, 0x50, 0x80, 0x3b, 0x7f, 0xfc, 0x20, 0x98, 0x4c, - 0xd3, 0x66, 0x9a, 0x11, 0xa0, 0x74, 0xdb, 0x4a, 0x49, 0x95, 0xb4, 0xea, - 0xdb, 0x23, 0xe0, 0x00, 0x75, 0x81, 0xf1, 0xae, 0xf5, 0xe7, 0xdb, 0xfe, - 0xc0, 0x6b, 0x60, 0x00, 0xd6, 0xc1, 0x3a, 0x70, 0x7e, 0xdc, 0x4a, 0x4c, - 0x26, 0x56, 0x6d, 0x2b, 0x9f, 0xa6, 0xea, 0x97, 0x8c, 0xca, 0xd0, 0x71, - 0x4c, 0x94, 0x41, 0xda, 0x5a, 0xa8, 0x64, 0x02, 0xf1, 0xc5, 0x27, 0xa5, - 0x2b, 0xbc, 0xb0, 0xcd, 0xfc, 0x19, 0xa7, 0x1e, 0x20, 0xec, 0x4e, 0x0c, - 0xfc, 0x2b, 0x04, 0xd4, 0x10, 0x2a, 0x4c, 0xaa, 0xf8, 0xb2, 0x11, 0x57, - 0xcc, 0xf5, 0x65, 0xf7, 0x20, 0x69, 0x75, 0x95, 0x05, 0x78, 0x56, 0xbc, - 0x02, 0xa2, 0x8a, 0x85, 0x51, 0x78, 0x68, 0x1a, 0x0b, 0x1a, 0x5c, 0x47, - 0x15, 0xfa, 0xea, 0x1f, 0x7b, 0x0a, 0xcb, 0x45, 0xa8, 0xbd, 0x5f, 0xf4, - 0x64, 0x5b, 0x57, 0x5a, 0x11, 0xb4, 0x82, 0x72, 0x22, 0xb5, 0x61, 0xcb, - 0xe3, 0x2e, 0x63, 0x3e, 0x48, 0x4d, 0xf3, 0x33, 0x1a, 0xa2, 0xc3, 0xed, - 0xac, 0x17, 0x95, 0x96, 0x50, 0x8a, 0xf9, 0x57, 0x31, 0xab, 0xea, 0x69, - 0xff, 0xc0, 0x23, 0x7b, 0xdb, 0x81, 0xf0, 0x07, 0x72, 0x58, 0x28, 0x99, - 0x4c, 0x86, 0x00, 0x7a, 0x66, 0x2d, 0x91, 0x31, 0xd6, 0x44, 0x75, 0x42, - 0xda, 0x0e, 0x5d, 0x65, 0x18, 0xd3, 0x0a, 0xdc, 0xb7, 0x1b, 0xb0, 0x65, - 0xe9, 0x3d, 0x9c, 0xca, 0xfe, 0x4c, 0x9b, 0x39, 0x7b, 0x5d, 0xd7, 0x95, - 0x28, 0xdd, 0x76, 0x93, 0x52, 0x70, 0x98, 0x28, 0xc6, 0xb5, 0xa2, 0x12, - 0x86, 0x23, 0xa1, 0xd8, 0xc0, 0x00, 0xe3, 0x90, 0x2f, 0xe3, 0xe6, 0x67, - 0x0c, 0xaf, 0x8a, 0x5d, 0xef, 0x5a, 0xbe, 0x32, 0xcd, 0x30, 0xcd, 0x03, - 0x8e, 0x61, 0xb2, 0x40, 0xfc, 0xdb, 0xc1, 0xa6, 0xce, 0x94, 0x66, 0x56, - 0x73, 0xbe, 0x48, 0x58, 0xb2, 0x60, 0xc7, 0x9a, 0x9c, 0x48, 0xce, 0x64, - 0x4b, 0xa4, 0x0f, 0x05, 0x4c, 0x84, 0x6b, 0xa3, 0x0a, 0x8b, 0x31, 0xa1, - 0xf7, 0xfc, 0xd0, 0x2c, 0x64, 0xb9, 0xc1, 0x08, 0x54, 0x27, 0x06, 0x47, - 0x9a, 0x49, 0x83, 0xb5, 0xb3, 0xd6, 0xea, 0xe0, 0x32, 0x99, 0xc4, 0xf3, - 0x3c, 0xd9, 0xf1, 0xa7, 0xd5, 0x1c, 0x57, 0xde, 0xe5, 0xf4, 0xa5, 0x55, - 0xb8, 0x17, 0x99, 0xbe, 0x64, 0xa1, 0xca, 0x3b, 0x73, 0x5c, 0xc3, 0xe6, - 0xeb, 0x17, 0xc4, 0x6b, 0x08, 0x3e, 0x27, 0xf1, 0x36, 0x23, 0x21, 0x9a, - 0x94, 0x25, 0xa8, 0x56, 0x4c, 0x80, 0x76, 0x3c, 0xf1, 0x8c, 0x10, 0xfb, - 0xe1, 0x06, 0xc2, 0x86, 0x11, 0x2c, 0x07, 0xa6, 0x0d, 0xd4, 0xf1, 0xd8, - 0xa4, 0x65, 0x60, 0xa6, 0x0f, 0x98, 0x3e, 0x31, 0xa5, 0x8a, 0xbd, 0xb5, - 0x9c, 0xf9, 0xbe, 0xb4, 0x57, 0x0c, 0x50, 0x6e, 0x41, 0xe9, 0x34, 0x2a, - 0xc0, 0x03, 0xf8, 0x0f, 0xb3, 0x85, 0x74, 0xfe, 0xaa, 0x6e, 0x3e, 0x6b, - 0xb0, 0x0c, 0x17, 0x1a, 0xe4, 0xa6, 0x6a, 0xfb, 0x64, 0xc6, 0x0b, 0xb3, - 0x41, 0xd3, 0xaf, 0x76, 0x16, 0xdf, 0x4c, 0xd4, 0xcc, 0xfb, 0xa5, 0xac, - 0x94, 0x63, 0x64, 0xcf, 0x84, 0x96, 0x6b, 0xa2, 0x1f, 0x08, 0xc8, 0xfa, - 0xe6, 0x8f, 0xcc, 0x83, 0x58, 0xcb, 0xd7, 0x3f, 0x76, 0xac, 0x26, 0x8d, - 0x54, 0xf8, 0x53, 0x6d, 0x76, 0x67, 0x6f, 0x9b, 0x65, 0xde, 0x07, 0x8d, - 0xd5, 0x75, 0xc4, 0xdf, 0x79, 0xa1, 0x15, 0xdb, 0x80, 0xff, 0xf1, 0x50, - 0x80, 0x3b, 0x5f, 0xfc, 0x20, 0x98, 0x7a, 0xd7, 0x48, 0x13, 0x09, 0x08, - 0x4a, 0x42, 0x40, 0x40, 0x00, 0x00, 0x00, 0x02, 0x4e, 0x2f, 0x7c, 0x7f, - 0x23, 0x02, 0x19, 0x28, 0xd8, 0x52, 0x1c, 0x23, 0x42, 0x47, 0x85, 0xd4, - 0xc1, 0x65, 0x11, 0x72, 0xb2, 0xa9, 0x48, 0xc5, 0x14, 0x1f, 0xfb, 0x27, - 0x75, 0xf6, 0x3a, 0x89, 0xf1, 0x4c, 0xcc, 0x8e, 0xc2, 0xdb, 0x51, 0x8c, - 0x80, 0x47, 0xdb, 0xea, 0x61, 0x2e, 0xe1, 0x83, 0x4f, 0x54, 0x96, 0x3f, - 0x74, 0xca, 0xac, 0x26, 0x79, 0x70, 0xba, 0x57, 0x32, 0x52, 0x3c, 0x79, - 0xed, 0x49, 0x89, 0x16, 0x44, 0xdf, 0x66, 0x98, 0x88, 0x56, 0x6b, 0x9c, - 0xc7, 0x06, 0x14, 0xe8, 0x88, 0x9c, 0x9a, 0xf5, 0x10, 0xe0, 0x78, 0x40, - 0x1b, 0xcb, 0x08, 0x91, 0x48, 0xde, 0xbf, 0x13, 0x8c, 0xda, 0x04, 0xa4, - 0x20, 0x7a, 0xe7, 0x4b, 0x3a, 0xd0, 0x1b, 0x98, 0xf5, 0x2a, 0x41, 0xdf, - 0x1e, 0x3b, 0x85, 0xaf, 0x0b, 0xd2, 0xe5, 0xb8, 0x86, 0x22, 0xb0, 0x2e, - 0x80, 0xaf, 0x5e, 0x58, 0xe4, 0x88, 0xc7, 0x96, 0x57, 0xcc, 0x1a, 0x0a, - 0xd6, 0x33, 0x15, 0x99, 0x11, 0xd2, 0x0e, 0x6d, 0x1d, 0xb2, 0xeb, 0x09, - 0x3a, 0x58, 0xb0, 0x83, 0x7a, 0x42, 0x60, 0x67, 0xc5, 0x76, 0x98, 0x6c, - 0xa4, 0xde, 0xe4, 0x38, 0x30, 0x13, 0x11, 0x09, 0x07, 0xdf, 0x0c, 0x8e, - 0xe1, 0x9e, 0xe5, 0x39, 0x49, 0x88, 0x78, 0xb6, 0x16, 0x9a, 0xc4, 0x82, - 0xa1, 0x5b, 0x2a, 0x9e, 0xec, 0x4a, 0xb7, 0x94, 0x27, 0xcc, 0x5f, 0x64, - 0x2b, 0x36, 0x54, 0xfd, 0x2b, 0xf9, 0xce, 0x20, 0x0d, 0xa1, 0x3e, 0x13, - 0x45, 0xa0, 0xd3, 0xe0, 0x53, 0x86, 0x48, 0x5c, 0xb1, 0xc9, 0x9a, 0x1a, - 0xd7, 0x18, 0x2b, 0x91, 0x0c, 0x48, 0x41, 0x38, 0x40, 0x00, 0x00, 0x00, - 0x71, 0xc8, 0x6b, 0x55, 0xd3, 0xf7, 0x1d, 0xf7, 0x75, 0x5b, 0x21, 0x93, - 0xc6, 0x90, 0xca, 0xb2, 0x8b, 0x1f, 0x6e, 0xf6, 0xd5, 0x0a, 0x2c, 0xb5, - 0x4e, 0xc9, 0x36, 0xd4, 0x7c, 0x71, 0xcc, 0x4a, 0x90, 0x39, 0xba, 0x72, - 0x75, 0x54, 0x43, 0xac, 0x7d, 0x30, 0xf4, 0x1a, 0xe4, 0xfa, 0x3b, 0x86, - 0x7d, 0xaf, 0xe9, 0x63, 0x01, 0x48, 0xd0, 0x28, 0x28, 0xac, 0x25, 0x31, - 0x71, 0xaf, 0x52, 0x90, 0xb5, 0x69, 0x45, 0xac, 0x10, 0x0b, 0xb2, 0x60, - 0x02, 0xb8, 0x9c, 0xfa, 0x7f, 0xed, 0x12, 0x42, 0xb0, 0x42, 0xac, 0xf4, - 0x5f, 0x39, 0x94, 0x8e, 0x16, 0x9f, 0x39, 0x72, 0xc6, 0x27, 0x5f, 0x99, - 0xbf, 0x83, 0xb5, 0x5d, 0xc6, 0x4b, 0x63, 0x19, 0xe5, 0x13, 0x9d, 0x52, - 0x3a, 0x1a, 0xf1, 0x53, 0x79, 0x7d, 0x91, 0xf5, 0x29, 0x33, 0x72, 0x9e, - 0x12, 0xaa, 0x44, 0x47, 0xba, 0x1c, 0xd8, 0xbf, 0x5b, 0xc9, 0x50, 0xa0, - 0xb6, 0x25, 0x1b, 0x9c, 0xaa, 0x7b, 0xd9, 0x56, 0xcd, 0x54, 0x9d, 0xe2, - 0x55, 0xaa, 0xe7, 0x88, 0x9e, 0xcb, 0xa3, 0x6d, 0x26, 0x9a, 0x06, 0x29, - 0xb7, 0xe0, 0xa6, 0xa8, 0x2e, 0x80, 0x63, 0x6c, 0xca, 0x62, 0xd4, 0x31, - 0x30, 0x83, 0x00, 0x16, 0x0d, 0x74, 0x67, 0x26, 0xc9, 0x31, 0x08, 0x07, - 0x68, 0x5e, 0xfc, 0xcb, 0xe1, 0xa9, 0x3b, 0xd2, 0xba, 0x3c, 0xbd, 0x5c, - 0xbb, 0xa4, 0x36, 0xb5, 0x91, 0xc9, 0x77, 0x09, 0x2c, 0x1a, 0xca, 0x3d, - 0x88, 0x55, 0xdf, 0xda, 0xce, 0x09, 0xc5, 0x7e, 0x2f, 0x03, 0x4f, 0x56, - 0xf2, 0xd7, 0xe0, 0xff, 0xf1, 0x50, 0x80, 0x27, 0xff, 0xfc, 0x21, 0x1a, - 0xce, 0x7b, 0x81, 0xf8, 0x00, 0x00, 0x9e, 0xb6, 0xa9, 0x0d, 0x48, 0x56, - 0x0a, 0x25, 0x82, 0x00, 0x09, 0x5f, 0xbf, 0xed, 0x14, 0x79, 0xf1, 0xc7, - 0xd7, 0xbf, 0x9e, 0xb7, 0x57, 0xe7, 0x9c, 0xdc, 0xd4, 0xc9, 0x3d, 0x87, - 0x69, 0xe7, 0x46, 0x10, 0xbf, 0x5e, 0x54, 0xad, 0xad, 0xe1, 0x93, 0x7f, - 0x6c, 0x63, 0xb5, 0x55, 0x16, 0xc2, 0xb2, 0x3f, 0x7f, 0xf0, 0x08, 0x17, - 0x8a, 0x70, 0xa3, 0x54, 0xd4, 0x45, 0x29, 0xa1, 0x58, 0x0e, 0xe0, 0xa1, - 0x7c, 0xee, 0x41, 0xd5, 0x8e, 0x1f, 0x0b, 0xbe, 0x3f, 0x54, 0x19, 0xe1, - 0xbe, 0x78, 0x42, 0xa3, 0xe2, 0x8d, 0x38, 0xb8, 0x6a, 0xa3, 0x99, 0xb7, - 0x0b, 0xb4, 0x7e, 0x25, 0x6e, 0xab, 0x6b, 0x9d, 0x3b, 0x69, 0x56, 0x42, - 0x7b, 0x97, 0x61, 0x44, 0x12, 0x0b, 0xe5, 0x72, 0x59, 0x08, 0xeb, 0x5a, - 0x83, 0x7b, 0x67, 0xce, 0xac, 0x35, 0x49, 0x81, 0x5e, 0x63, 0x1c, 0x47, - 0x1d, 0x49, 0x10, 0x4d, 0x5c, 0x40, 0x8a, 0xa0, 0xa8, 0x7e, 0x8a, 0x89, - 0xd4, 0x04, 0x73, 0x6c, 0x2d, 0x7e, 0xe6, 0x01, 0x93, 0x0b, 0xa0, 0x74, - 0xf4, 0x5f, 0xda, 0x7a, 0x7b, 0x17, 0x4d, 0x4c, 0x8e, 0x62, 0x72, 0xb8, - 0xca, 0x25, 0xdb, 0x7b, 0x32, 0xb5, 0x68, 0x19, 0x9e, 0xe9, 0x41, 0xd8, - 0xd4, 0xd2, 0x44, 0xfb, 0xa6, 0xfa, 0x4c, 0xf3, 0x7f, 0xf1, 0x02, 0xe7, - 0x77, 0xb9, 0xf6, 0x33, 0xe4, 0xcf, 0x8a, 0xeb, 0x6f, 0x1a, 0x88, 0x4f, - 0xf2, 0x15, 0x5e, 0x0a, 0x3d, 0x3b, 0xfb, 0x24, 0xd9, 0x61, 0xe0, 0xb8, - 0xf4, 0x1c, 0x95, 0x82, 0xdf, 0xf5, 0x11, 0x6d, 0x43, 0x00, 0xdb, 0x29, - 0xf6, 0x4f, 0x5a, 0x50, 0x76, 0x17, 0x40, 0x97, 0xe2, 0x62, 0xf5, 0xaf, - 0x19, 0x66, 0x03, 0x54, 0x79, 0xf1, 0x5a, 0xb8, 0xc5, 0x6a, 0x98, 0x81, - 0x03, 0x3d, 0x2b, 0xda, 0x13, 0xcc, 0x0e, 0x30, 0x7f, 0xf1, 0x4a, 0x63, - 0x17, 0x08, 0x8d, 0x93, 0x16, 0xcf, 0x6e, 0x1b, 0x5f, 0x1f, 0xd0, 0x1d, - 0x3c, 0x3b, 0x0f, 0xd9, 0x38, 0xaa, 0x58, 0x0d, 0x2c, 0x27, 0x18, 0x3c, - 0x2e, 0xfc, 0xb1, 0xcb, 0x92, 0x8a, 0xe1, 0x01, 0x5b, 0x03, 0xc4, 0xa2, - 0xec, 0x74, 0xd8, 0xba, 0xe5, 0xe3, 0x2d, 0x96, 0x5b, 0x80, 0xff, 0xf1, - 0x50, 0x80, 0x22, 0xff, 0xfc, 0x21, 0x2a, 0xcf, 0xc3, 0xb4, 0x00, 0x00, - 0x00, 0xa2, 0xb2, 0xba, 0x0d, 0x89, 0x02, 0x10, 0x00, 0x2f, 0x8c, 0xe7, - 0x57, 0x7a, 0xfb, 0x7d, 0xfe, 0x7e, 0x3e, 0xf5, 0xa7, 0x8e, 0x2e, 0x6e, - 0xb2, 0x57, 0x1c, 0xf9, 0xf1, 0x05, 0xd6, 0x0f, 0xa9, 0x90, 0x7b, 0x65, - 0x48, 0x6f, 0xcc, 0xa0, 0xb3, 0xcd, 0x8e, 0x66, 0x8c, 0x21, 0x8e, 0x1f, - 0xb2, 0x9c, 0xe0, 0xdf, 0xc8, 0xa7, 0x1b, 0x0a, 0x9e, 0xaa, 0x77, 0x04, - 0xa8, 0xf4, 0xfe, 0x44, 0xe0, 0xac, 0xd6, 0xd8, 0x9b, 0xa1, 0xc4, 0xd2, - 0x72, 0xdc, 0xb1, 0xb4, 0xae, 0x15, 0x83, 0x49, 0x7c, 0x50, 0x83, 0xb5, - 0x7f, 0xb5, 0x78, 0xdd, 0x3b, 0xea, 0x88, 0xe0, 0x10, 0xae, 0xff, 0x91, - 0xe4, 0x0e, 0xfa, 0xeb, 0xa4, 0xb2, 0xe7, 0x54, 0xf5, 0x07, 0xa0, 0xbf, - 0x93, 0x18, 0xec, 0x88, 0x1e, 0x2a, 0x27, 0xb1, 0x72, 0xd3, 0xed, 0xd7, - 0x68, 0x4e, 0x77, 0x0e, 0xd3, 0x4c, 0xb1, 0x80, 0x9f, 0x11, 0xf6, 0xca, - 0x05, 0xf2, 0xb1, 0x9e, 0x26, 0x32, 0xee, 0x4a, 0xe4, 0xd4, 0xc0, 0x95, - 0x69, 0x0a, 0x48, 0x0f, 0xad, 0x8c, 0xcf, 0xe2, 0x0c, 0xa5, 0x2a, 0x9a, - 0xee, 0xe0, 0x69, 0xcd, 0x80, 0xaa, 0x55, 0x0c, 0xd9, 0xc3, 0xe3, 0x00, - 0x46, 0xd9, 0x3a, 0xf7, 0xb1, 0x25, 0x70, 0x9d, 0x66, 0x1d, 0xba, 0x1a, - 0x96, 0x94, 0x68, 0x9a, 0x74, 0xb5, 0x85, 0x6f, 0xe3, 0xec, 0xd8, 0xc8, - 0x10, 0x63, 0x74, 0x22, 0x8a, 0x06, 0x84, 0xa1, 0x3a, 0x4d, 0x1f, 0x10, - 0x00, 0x5f, 0x1e, 0x01, 0xed, 0xe7, 0xf6, 0xf6, 0xbe, 0x1b, 0x13, 0x12, - 0x85, 0xd5, 0x04, 0xf7, 0xfd, 0x49, 0x8f, 0xb6, 0x38, 0x86, 0xc1, 0x25, - 0x07, 0x29, 0x04, 0xdc, 0x0a, 0xdb, 0x0b, 0x1f, 0xe6, 0xf3, 0x58, 0x52, - 0x65, 0x16, 0xdd, 0x89, 0x4a, 0x8d, 0x18, 0xe5, 0x8e, 0xd8, 0x5f, 0x42, - 0x75, 0x69, 0xc7, 0x95, 0xc6, 0x51, 0xce, 0xa6, 0x12, 0x55, 0x7d, 0xb8, - 0xe0, 0xff, 0xf1, 0x50, 0x80, 0x34, 0x1f, 0xfc, 0x21, 0x4c, 0xd8, 0x80, - 0x05, 0x40, 0x40, 0x04, 0x0d, 0x4a, 0x59, 0x84, 0x88, 0x50, 0x4d, 0x36, - 0x99, 0x0a, 0x66, 0x8e, 0xcc, 0x74, 0x8a, 0xf4, 0x60, 0x0f, 0x20, 0xd3, - 0xea, 0x6b, 0xc5, 0xe7, 0xd8, 0xe3, 0xcf, 0x8f, 0xf6, 0x9e, 0xba, 0xfd, - 0x27, 0xd8, 0x0f, 0x3b, 0xdf, 0x58, 0x08, 0xe0, 0x66, 0x69, 0x09, 0x7a, - 0x26, 0x6a, 0xea, 0x07, 0x19, 0x0c, 0xff, 0xae, 0x3e, 0x87, 0x10, 0x70, - 0x91, 0x09, 0x09, 0x4b, 0x02, 0x41, 0x34, 0x89, 0xe5, 0x12, 0x81, 0xb0, - 0x51, 0x6c, 0x9e, 0xc8, 0x49, 0x93, 0x9a, 0x48, 0xc7, 0x9d, 0x1e, 0x46, - 0x8e, 0x11, 0x0a, 0x88, 0xaa, 0x68, 0x9f, 0x1e, 0xee, 0x0a, 0xb0, 0x64, - 0x54, 0xb1, 0x70, 0xe0, 0xa3, 0xae, 0x09, 0x8e, 0xd0, 0x4b, 0x48, 0x91, - 0x9d, 0x5a, 0x18, 0x6c, 0x11, 0x28, 0xba, 0x42, 0x4c, 0x49, 0x5c, 0xda, - 0xd5, 0x96, 0x86, 0x1d, 0x04, 0xc7, 0x24, 0x8d, 0x19, 0x3b, 0x69, 0xca, - 0xd4, 0xba, 0x95, 0x67, 0x5c, 0xd7, 0x12, 0xe1, 0x3c, 0x25, 0x21, 0xa3, - 0x31, 0x58, 0x43, 0x89, 0xe3, 0x99, 0xf6, 0xf2, 0xd8, 0x89, 0x6b, 0x09, - 0xa7, 0xef, 0xd9, 0xc3, 0x28, 0x25, 0xa1, 0x48, 0x69, 0x0d, 0x5f, 0x81, - 0xb4, 0x31, 0x7d, 0xd4, 0x72, 0x8c, 0x06, 0xae, 0xd4, 0x8a, 0x4b, 0x42, - 0x6c, 0x57, 0xb9, 0x5a, 0x83, 0xbc, 0x21, 0x80, 0x06, 0x90, 0x00, 0x21, - 0x0e, 0x5d, 0x8a, 0x2d, 0x44, 0x8d, 0xf6, 0x3b, 0x16, 0x29, 0x22, 0xfe, - 0x9f, 0x65, 0xa0, 0x60, 0x74, 0x10, 0xc2, 0x31, 0xe6, 0x1f, 0x0e, 0xed, - 0xcf, 0xe4, 0x59, 0xd9, 0x5a, 0x9e, 0x18, 0xfb, 0x0a, 0xdc, 0x76, 0x77, - 0xb1, 0x73, 0xe6, 0x37, 0x14, 0x6c, 0x42, 0x91, 0x2d, 0xc1, 0x33, 0x69, - 0x90, 0xbf, 0x80, 0x69, 0x32, 0x37, 0xf6, 0x92, 0x58, 0x3e, 0x50, 0xfa, - 0x6d, 0x58, 0x07, 0x5c, 0xd3, 0xae, 0xfa, 0xef, 0xe3, 0xdf, 0xef, 0x6d, - 0xdb, 0xbe, 0xcd, 0x5f, 0xc7, 0xdd, 0xf9, 0xd5, 0x7a, 0xec, 0xbc, 0xe3, - 0xdb, 0x76, 0xd0, 0x23, 0x04, 0xb8, 0x85, 0x0e, 0x69, 0x1b, 0x54, 0x32, - 0x57, 0x9c, 0xae, 0x77, 0xbe, 0x52, 0xea, 0xdc, 0xf7, 0xcf, 0xad, 0x92, - 0x86, 0xac, 0x22, 0xcd, 0x25, 0xa1, 0x48, 0x13, 0x81, 0xb7, 0x22, 0x60, - 0x1f, 0x2c, 0x35, 0x29, 0xc4, 0xa8, 0x27, 0x98, 0x0d, 0x3c, 0xc5, 0x92, - 0xd9, 0x46, 0x95, 0x9e, 0x29, 0x8f, 0xb3, 0x42, 0x4b, 0xbd, 0x45, 0x5b, - 0xf4, 0x6b, 0xf6, 0x76, 0x06, 0xd7, 0xfe, 0xe8, 0xdb, 0x76, 0x75, 0x5f, - 0x2b, 0xc5, 0x76, 0xf5, 0x73, 0xc0, 0x20, 0xf9, 0x06, 0x25, 0xa3, 0xff, - 0x5e, 0x3e, 0x2b, 0x7e, 0xfd, 0x1d, 0x32, 0x67, 0x65, 0x1c, 0x25, 0xb2, - 0x76, 0xf5, 0x54, 0xb1, 0xbc, 0x41, 0xbd, 0x4a, 0x97, 0x3d, 0xa0, 0xf5, - 0xfd, 0xb3, 0x8b, 0xd9, 0x35, 0x6a, 0xcd, 0x34, 0x2d, 0xce, 0xc9, 0xc4, - 0x79, 0xbe, 0xe5, 0xb4, 0xbf, 0x52, 0xaf, 0x27, 0x80, 0xff, 0xf1, 0x50, - 0x80, 0x27, 0x9f, 0xfc, 0x21, 0x7a, 0xcf, 0xf0, 0x98, 0x0e, 0x50, 0xc2, - 0xa4, 0xb2, 0xa3, 0x49, 0x68, 0x26, 0x11, 0x11, 0x02, 0xc5, 0x04, 0x55, - 0xfd, 0x7c, 0xe3, 0x5a, 0xcd, 0xff, 0xa7, 0xe9, 0xd8, 0x97, 0xe5, 0x52, - 0xf8, 0xf3, 0xcf, 0xaa, 0xef, 0xda, 0xfc, 0xe6, 0x75, 0xa1, 0x37, 0xd4, - 0x61, 0xc7, 0x82, 0x99, 0x19, 0x9a, 0x5d, 0xb1, 0x1b, 0xf3, 0xa5, 0x29, - 0x3d, 0x6b, 0xcb, 0x93, 0x7e, 0x77, 0x30, 0x7a, 0x60, 0xca, 0x23, 0x0a, - 0xa1, 0x4a, 0x14, 0xef, 0x27, 0xcd, 0x35, 0xef, 0xe0, 0xc2, 0x3e, 0x32, - 0x7c, 0x7c, 0x7c, 0x1f, 0xe3, 0x33, 0xed, 0xe8, 0x1e, 0xe5, 0x9e, 0x10, - 0x01, 0x1c, 0xc7, 0x0b, 0x1e, 0x2d, 0x93, 0xef, 0xb6, 0x7c, 0x9e, 0x3f, - 0x07, 0x00, 0x3a, 0x49, 0xf7, 0xc4, 0x0d, 0x19, 0xf2, 0xfc, 0x05, 0x1d, - 0x14, 0x28, 0x28, 0xeb, 0xd9, 0xf1, 0xaf, 0xc1, 0x2e, 0xc6, 0xac, 0x4c, - 0x56, 0xb0, 0xbf, 0x16, 0x4b, 0xdb, 0xaf, 0xc2, 0x66, 0xa5, 0x9a, 0xb9, - 0x2b, 0x79, 0x05, 0x98, 0x90, 0x99, 0x99, 0x90, 0xdf, 0xcd, 0x6e, 0x40, - 0x21, 0xd0, 0x3d, 0xcb, 0x3f, 0xa4, 0x23, 0x37, 0xc0, 0x19, 0x83, 0x60, - 0x3b, 0xef, 0xa1, 0x21, 0xfc, 0xc8, 0xdd, 0x4a, 0x1d, 0x50, 0xab, 0x51, - 0x16, 0x8c, 0x30, 0x92, 0x9c, 0x4a, 0xa3, 0x40, 0x55, 0x70, 0x47, 0x56, - 0x29, 0x24, 0x73, 0x47, 0x54, 0x02, 0xaf, 0x07, 0x77, 0x7c, 0x1d, 0x91, - 0x58, 0x19, 0x91, 0x12, 0xb8, 0xb7, 0xb7, 0x81, 0x95, 0x72, 0x54, 0xa2, - 0xe4, 0x01, 0x84, 0x80, 0x00, 0xf5, 0xe1, 0x21, 0xcc, 0x05, 0xdd, 0xda, - 0xf4, 0x8b, 0xa4, 0x0f, 0xc0, 0x11, 0xdd, 0x97, 0xcf, 0x1e, 0x73, 0xb9, - 0x3d, 0xbf, 0x1f, 0xc4, 0xee, 0xfc, 0xfd, 0x73, 0x5c, 0x67, 0xb5, 0x7a, - 0x8a, 0x9d, 0x6d, 0x70, 0x5f, 0x7f, 0xbf, 0xb3, 0xb5, 0x7d, 0xdc, 0x8a, - 0x57, 0x57, 0x84, 0x67, 0xba, 0xc9, 0xa2, 0x92, 0xef, 0x8c, 0xe4, 0xc2, - 0xaa, 0x59, 0x83, 0x78, 0x06, 0xa4, 0xc1, 0x57, 0x35, 0x13, 0xdd, 0x7d, - 0xff, 0x7f, 0x66, 0xd3, 0xb4, 0x63, 0x0a, 0x63, 0x18, 0x30, 0x03, 0x18, - 0x0c, 0x01, 0x8c, 0x49, 0x59, 0x06, 0xcf, 0xfc, 0xd5, 0xeb, 0xca, 0x05, - 0xe0, 0xff, 0xf1, 0x50, 0x80, 0x2b, 0xff, 0xfc, 0x21, 0x1a, 0xcf, 0xf4, - 0x58, 0xb9, 0x00, 0x00, 0xa0, 0xb1, 0xc2, 0x95, 0xa8, 0x85, 0x12, 0x1c, - 0x27, 0x53, 0xbd, 0x5d, 0x66, 0x7b, 0x54, 0xcd, 0x4e, 0xfa, 0xde, 0xb3, - 0xcf, 0xb6, 0x4a, 0xab, 0xba, 0xe5, 0xaa, 0xca, 0xcb, 0xcb, 0x12, 0x78, - 0xe7, 0x79, 0x34, 0x02, 0x88, 0x4c, 0x17, 0x26, 0xb8, 0xba, 0xaf, 0x4c, - 0x22, 0xbe, 0x52, 0x8b, 0x5d, 0x8a, 0x2c, 0x2e, 0xf1, 0x9d, 0x22, 0x38, - 0x15, 0x40, 0xa7, 0xcd, 0xd6, 0x14, 0x6f, 0xca, 0xef, 0x53, 0xc9, 0x9c, - 0x65, 0xdd, 0xe2, 0xae, 0x33, 0x89, 0x6a, 0x6b, 0x96, 0x4c, 0x26, 0xb1, - 0x35, 0x9c, 0x57, 0x5d, 0x4c, 0x36, 0x4d, 0x41, 0x9d, 0x31, 0x50, 0x21, - 0x3e, 0xf9, 0x27, 0x9f, 0x7d, 0xe7, 0x54, 0xea, 0xc7, 0x2e, 0xbf, 0x2d, - 0x67, 0x9f, 0xcb, 0x91, 0x8e, 0xad, 0x03, 0x87, 0x88, 0xca, 0x13, 0xe0, - 0x93, 0xf0, 0x7b, 0x66, 0x3c, 0xc1, 0x7e, 0x8f, 0xfe, 0x5e, 0x4d, 0xff, - 0xc1, 0x4c, 0xfa, 0xc3, 0xeb, 0x69, 0x1b, 0xb7, 0x92, 0x2a, 0xda, 0x3d, - 0x6f, 0xc9, 0xfe, 0x05, 0x9f, 0x19, 0x79, 0x37, 0x8f, 0xc1, 0x7c, 0x6f, - 0xf1, 0x7b, 0x98, 0xbf, 0x90, 0x43, 0xf1, 0x16, 0xb7, 0x6e, 0x7b, 0xfa, - 0x2d, 0x79, 0xfb, 0xee, 0xc5, 0x10, 0xbb, 0x97, 0xbc, 0xe7, 0x04, 0xd3, - 0xa1, 0xdd, 0x01, 0xd9, 0xae, 0x2b, 0xce, 0x7e, 0x3e, 0xa2, 0xab, 0x15, - 0x15, 0x91, 0xf7, 0xfe, 0x32, 0xfe, 0x7f, 0x1b, 0x23, 0xf6, 0xd7, 0x8f, - 0xa5, 0x79, 0xf0, 0x85, 0x47, 0x49, 0x55, 0xb7, 0x1a, 0x9e, 0x29, 0x2f, - 0xd2, 0xac, 0xfb, 0xd8, 0x43, 0x44, 0xda, 0x79, 0xc6, 0xf7, 0x01, 0x12, - 0xf5, 0x4c, 0x1e, 0x83, 0x0b, 0x39, 0x3a, 0x9a, 0x5b, 0x77, 0x40, 0x04, - 0x77, 0x9d, 0x23, 0xbb, 0x6f, 0x41, 0x48, 0x59, 0x20, 0x7e, 0x01, 0x3a, - 0x9f, 0x3e, 0xdb, 0xe6, 0xef, 0x5a, 0x9e, 0xf2, 0xee, 0xda, 0xcb, 0xcf, - 0xc1, 0x11, 0x54, 0xbd, 0xde, 0x6b, 0x01, 0xdb, 0xce, 0x28, 0x94, 0x0b, - 0x76, 0x6c, 0x94, 0x0e, 0x44, 0xe2, 0xa0, 0x7a, 0x06, 0xa3, 0xd3, 0x67, - 0x1f, 0xa3, 0x15, 0xf6, 0xf0, 0x3d, 0x7d, 0xba, 0x74, 0x42, 0x7a, 0x37, - 0x8d, 0x29, 0x9e, 0x9e, 0x85, 0xad, 0xee, 0xe8, 0x3d, 0x35, 0x3b, 0xf9, - 0xd9, 0x13, 0x5a, 0x8a, 0x5c, 0x2b, 0x8c, 0x1b, 0x37, 0x86, 0x2a, 0x24, - 0xa1, 0xcc, 0x74, 0x63, 0x6b, 0xb2, 0x07, 0x74, 0x84, 0xb4, 0xea, 0x80, - 0x4f, 0x03, 0x78, 0xfc, 0xff, 0xf1, 0x50, 0x80, 0x2e, 0xbf, 0xfc, 0x21, - 0x1a, 0xcf, 0xea, 0xe0, 0x9c, 0x80, 0x00, 0x9f, 0xb2, 0x42, 0x18, 0xea, - 0x64, 0x3b, 0x05, 0x10, 0x42, 0x0f, 0x6c, 0xf3, 0xf6, 0xf8, 0xac, 0xaa, - 0xf6, 0xe3, 0xc2, 0xdb, 0xeb, 0x77, 0x9f, 0x0f, 0x1f, 0x45, 0x64, 0xca, - 0xb9, 0xbc, 0xeb, 0x9d, 0x84, 0x9e, 0x79, 0xde, 0x97, 0xe1, 0x71, 0xe4, - 0xc9, 0x60, 0xb4, 0x95, 0x2b, 0xfb, 0x2a, 0x3d, 0xb6, 0xd4, 0xb8, 0x12, - 0xee, 0x70, 0xde, 0xd2, 0x42, 0xa0, 0x6a, 0x0d, 0x1d, 0x5c, 0x28, 0x92, - 0x1d, 0x32, 0x4a, 0x1b, 0x45, 0x26, 0xa9, 0x7a, 0xe9, 0x03, 0x1d, 0xb2, - 0x18, 0xc4, 0xa4, 0xad, 0x5c, 0x44, 0xd0, 0x95, 0x49, 0x47, 0x55, 0x5d, - 0xd6, 0xeb, 0x47, 0xbd, 0x6d, 0xc4, 0xab, 0x68, 0xa4, 0x4d, 0x2d, 0xae, - 0x5d, 0xaf, 0x28, 0xca, 0x66, 0x5c, 0xb0, 0x99, 0x81, 0xda, 0x33, 0xf0, - 0x97, 0xaf, 0x29, 0x1d, 0x4c, 0xaa, 0xd2, 0xaf, 0x28, 0x17, 0xa3, 0x90, - 0x76, 0xf6, 0x37, 0xa6, 0xfa, 0xaf, 0x83, 0xbb, 0xab, 0x6c, 0xd3, 0x81, - 0xba, 0xdb, 0x54, 0x6b, 0x6d, 0xdf, 0xef, 0xf4, 0x7b, 0x1f, 0x11, 0x5c, - 0xc7, 0xd2, 0xe5, 0xdc, 0x5b, 0x59, 0x29, 0x59, 0xf7, 0xe7, 0xc3, 0x76, - 0x15, 0xab, 0x59, 0x32, 0x41, 0x40, 0x0b, 0x80, 0x68, 0xf5, 0x95, 0xcc, - 0xf8, 0xcc, 0xb4, 0x66, 0x5b, 0x4b, 0x75, 0xd5, 0x0a, 0x6e, 0xc7, 0xf4, - 0xfd, 0x8c, 0xd9, 0xb8, 0x81, 0x10, 0xed, 0xc6, 0x6a, 0x7c, 0x3f, 0x83, - 0x4e, 0xbb, 0x4b, 0xb9, 0x81, 0x16, 0x5b, 0x7f, 0x67, 0x77, 0xe6, 0x51, - 0x75, 0x78, 0xdb, 0x97, 0x24, 0x1f, 0xc4, 0xa8, 0x5c, 0xe8, 0x7e, 0xe1, - 0x87, 0x23, 0x74, 0x89, 0x0a, 0xfe, 0x8b, 0x34, 0xf6, 0xca, 0x30, 0x56, - 0x79, 0x21, 0xbc, 0xf8, 0xb8, 0xd3, 0x82, 0x7e, 0x94, 0xea, 0x43, 0x30, - 0xbd, 0xe1, 0xed, 0x97, 0xe2, 0xfa, 0xf9, 0xf3, 0x5e, 0xdd, 0xdf, 0x35, - 0x7a, 0x38, 0xcf, 0x8f, 0x5e, 0xda, 0x4c, 0x48, 0xa9, 0x48, 0x50, 0x45, - 0x8f, 0x57, 0xd4, 0x4a, 0x76, 0x3c, 0x4f, 0xc1, 0x38, 0x39, 0xcc, 0xe5, - 0x4f, 0xd1, 0x9a, 0xc2, 0x41, 0xca, 0x61, 0xd1, 0xc1, 0xd8, 0xd5, 0x9a, - 0xbe, 0xe7, 0x1d, 0x3c, 0xb8, 0x66, 0xee, 0x0d, 0xeb, 0x71, 0x33, 0xc9, - 0xaf, 0xc9, 0xe2, 0x13, 0x4f, 0x13, 0x29, 0xaf, 0x1e, 0xd3, 0xd7, 0x42, - 0x61, 0x7f, 0xaa, 0x6f, 0x1a, 0xa5, 0x81, 0x8a, 0xa2, 0xac, 0xf3, 0xb7, - 0x09, 0xa6, 0xe3, 0x65, 0x38, 0x3d, 0xdb, 0xab, 0xf2, 0xb1, 0x1b, 0xbb, - 0x2f, 0x1b, 0x39, 0xba, 0xc1, 0x8b, 0x28, 0x22, 0x18, 0x48, 0x40, 0xe4, - 0x36, 0x04, 0xa6, 0x30, 0xbc, 0xff, 0xf1, 0x50, 0x80, 0x2f, 0x1f, 0xfc, - 0x21, 0x1a, 0xcf, 0xf0, 0xe8, 0xf0, 0x81, 0x20, 0x9f, 0xb4, 0x31, 0x91, - 0x4c, 0x84, 0x58, 0x71, 0x9e, 0x7d, 0xfa, 0x63, 0x3f, 0x5e, 0xf2, 0xb8, - 0xda, 0x48, 0xfc, 0x7b, 0xef, 0xf5, 0x9e, 0x3c, 0x79, 0xde, 0x55, 0xf9, - 0xfb, 0x54, 0xbe, 0x3b, 0x12, 0x99, 0xae, 0x9a, 0x76, 0xfa, 0x65, 0x58, - 0x92, 0xa1, 0xa3, 0xb9, 0x17, 0x70, 0xd5, 0x4f, 0x47, 0x98, 0x8c, 0x29, - 0x75, 0xe8, 0x35, 0x5e, 0xe0, 0x14, 0xde, 0xc5, 0xf3, 0x92, 0xf0, 0xf3, - 0x55, 0xbc, 0xab, 0x2b, 0x78, 0x3d, 0x18, 0xbb, 0x9b, 0x3c, 0x21, 0x87, - 0x85, 0x51, 0xba, 0x2d, 0xf1, 0xc3, 0x06, 0xaa, 0xe0, 0x22, 0x60, 0x32, - 0x3c, 0x0e, 0x1b, 0x6a, 0x2a, 0x37, 0x5f, 0x1b, 0x1b, 0xd3, 0xdf, 0x65, - 0x92, 0x5f, 0x42, 0x92, 0x29, 0x1d, 0x77, 0x81, 0x08, 0xc2, 0xa2, 0x88, - 0xb1, 0x20, 0xa0, 0x0d, 0x88, 0xf9, 0x88, 0xc0, 0xba, 0xc1, 0x27, 0x59, - 0xd7, 0xb3, 0x06, 0x79, 0x97, 0x64, 0x9e, 0x5d, 0xde, 0x6a, 0xa0, 0xed, - 0x5a, 0x06, 0x79, 0x83, 0x2a, 0x90, 0x90, 0x04, 0xc5, 0x0c, 0x0c, 0x59, - 0xd0, 0x1e, 0x14, 0x16, 0xc7, 0x78, 0x38, 0x07, 0x88, 0x53, 0x49, 0x77, - 0x70, 0x18, 0xd1, 0x45, 0xd4, 0xa6, 0xd8, 0x33, 0xb0, 0x9d, 0x4b, 0x6e, - 0x8b, 0xc0, 0xc3, 0x79, 0xa3, 0x44, 0x02, 0xee, 0xff, 0xe0, 0x1d, 0x1d, - 0xeb, 0x6e, 0xd6, 0xaf, 0x73, 0xde, 0xfe, 0xb7, 0xc5, 0xbe, 0xd5, 0xad, - 0x2b, 0x7f, 0x51, 0xc2, 0xf5, 0x8c, 0xdb, 0x45, 0xbc, 0x9d, 0xc8, 0xdd, - 0x6b, 0x16, 0xb2, 0xbc, 0x6d, 0x48, 0x81, 0xd8, 0x7c, 0xa0, 0x6d, 0x25, - 0xb3, 0xbd, 0xa7, 0xd2, 0xe9, 0xe7, 0xb3, 0xa4, 0x37, 0xed, 0x46, 0xf4, - 0x60, 0xb0, 0x63, 0xd7, 0x06, 0x9f, 0xb1, 0x42, 0x55, 0xcc, 0x3f, 0x13, - 0x07, 0xd8, 0x1c, 0x67, 0x9f, 0xb7, 0x0c, 0xd6, 0x7e, 0xbe, 0x9b, 0x8b, - 0x92, 0x66, 0xbc, 0xde, 0xb2, 0x82, 0xae, 0xfb, 0x8b, 0xbc, 0x08, 0xd8, - 0xf8, 0xfe, 0x08, 0x92, 0x05, 0xcf, 0x50, 0x14, 0xb5, 0xde, 0xb2, 0xef, - 0xb5, 0x16, 0x81, 0x6e, 0x04, 0xaa, 0x12, 0xeb, 0x8b, 0xbe, 0xf8, 0xee, - 0xbe, 0x47, 0x1d, 0x75, 0x22, 0x49, 0x7b, 0x67, 0x8a, 0xe3, 0xf7, 0x74, - 0xe1, 0xe7, 0x94, 0xf6, 0xc1, 0x2c, 0xf5, 0x45, 0xec, 0x29, 0x31, 0x3a, - 0xab, 0xce, 0x33, 0x8b, 0x2a, 0xaa, 0x23, 0xc8, 0xbd, 0xea, 0x75, 0x93, - 0x42, 0x06, 0xb5, 0xb9, 0x36, 0x22, 0x27, 0xad, 0xda, 0x87, 0x63, 0x28, - 0x28, 0x03, 0xa6, 0xb3, 0xcd, 0xd7, 0x62, 0xda, 0x3b, 0xce, 0xb7, 0x2c, - 0x2e, 0x20, 0xa8, 0x98, 0x71, 0x09, 0x6c, 0xbe, 0x8e, 0xff, 0xf1, 0x50, - 0x80, 0x2e, 0xdf, 0xfc, 0x21, 0x2a, 0xcf, 0xfc, 0x60, 0x9c, 0x10, 0x00, - 0x9d, 0xb4, 0x41, 0x59, 0x28, 0x46, 0x1a, 0x38, 0x27, 0x55, 0xdf, 0x57, - 0xcc, 0xdf, 0xb5, 0xce, 0xfe, 0xff, 0x2e, 0x31, 0x9d, 0x73, 0xe6, 0x64, - 0xf5, 0xc4, 0xcc, 0x94, 0x2b, 0xcd, 0x02, 0x66, 0x8d, 0x3f, 0xdf, 0x26, - 0x99, 0x64, 0x64, 0x12, 0xcc, 0x57, 0x3a, 0x3c, 0x61, 0x8e, 0xa4, 0x4c, - 0x9f, 0x2c, 0x63, 0x97, 0x9c, 0xd2, 0x26, 0x92, 0xdc, 0x72, 0xb6, 0xaf, - 0x1a, 0x8a, 0xa1, 0x37, 0xc2, 0x39, 0xf2, 0x64, 0x03, 0xd5, 0x37, 0x1b, - 0x20, 0x4c, 0x38, 0xb5, 0xbb, 0xbe, 0x3a, 0x0f, 0x09, 0xc5, 0x50, 0x08, - 0x8c, 0x80, 0x00, 0x7a, 0x9e, 0x92, 0x18, 0x87, 0x51, 0x92, 0x23, 0xc5, - 0x82, 0xb1, 0xaf, 0x71, 0xeb, 0x39, 0xb3, 0x79, 0x01, 0x24, 0x2f, 0x1a, - 0xc4, 0xd8, 0x23, 0x48, 0x22, 0x74, 0xb8, 0xa4, 0x9e, 0x84, 0x75, 0xad, - 0x6c, 0x91, 0x28, 0x93, 0x3c, 0x4a, 0xfc, 0x88, 0x9b, 0xa0, 0x54, 0xaa, - 0xa2, 0x68, 0x76, 0x0c, 0x3f, 0xef, 0x71, 0xb8, 0xcf, 0xe4, 0x7f, 0x7f, - 0xa5, 0x74, 0x7e, 0x39, 0xbb, 0x94, 0x0c, 0xec, 0x1b, 0xc3, 0x29, 0x73, - 0xb0, 0x33, 0x77, 0xff, 0x19, 0x2f, 0x26, 0x2f, 0x10, 0x7a, 0x44, 0x60, - 0x02, 0xeb, 0x4c, 0x8f, 0xc2, 0x36, 0xc9, 0xcd, 0x21, 0x69, 0x7c, 0x6d, - 0xa4, 0x0f, 0x39, 0xfb, 0xd9, 0x62, 0x53, 0x36, 0x7a, 0x57, 0x4e, 0x16, - 0xbf, 0x7c, 0x21, 0xcb, 0x17, 0x4d, 0x76, 0x65, 0x65, 0x37, 0xbf, 0x53, - 0x87, 0x94, 0x6b, 0x70, 0x4b, 0x52, 0xfc, 0xa0, 0x31, 0xe7, 0xc9, 0xd8, - 0x88, 0x4b, 0x10, 0x96, 0xd6, 0x0a, 0x74, 0x82, 0x08, 0x72, 0x4a, 0x07, - 0x32, 0xa9, 0xf2, 0xf1, 0x5f, 0xd0, 0x0f, 0x04, 0x86, 0xd7, 0xfb, 0x82, - 0xe7, 0x68, 0x8c, 0x87, 0x32, 0x1d, 0x87, 0xef, 0x09, 0xd5, 0x77, 0xd7, - 0x64, 0xf6, 0xee, 0xfb, 0x9c, 0x5e, 0x38, 0xce, 0xb8, 0xd7, 0x1d, 0xeb, - 0x7a, 0x4c, 0xbd, 0xc8, 0x6a, 0x84, 0xde, 0xf3, 0xb8, 0xf3, 0x26, 0x8a, - 0x22, 0xe7, 0xe1, 0xe3, 0x8d, 0x0f, 0x4f, 0xbf, 0xf2, 0xb6, 0x7a, 0xe2, - 0xef, 0x2f, 0xf7, 0xa5, 0x7b, 0x90, 0xe2, 0x6d, 0x96, 0x9c, 0x2f, 0x9d, - 0x5a, 0x1c, 0x2a, 0xae, 0xc1, 0xcb, 0x48, 0xec, 0x79, 0x3f, 0xe5, 0xd9, - 0xe8, 0xc4, 0x15, 0x80, 0x5e, 0x9e, 0x9f, 0x3f, 0x79, 0xef, 0xd2, 0x07, - 0x67, 0x0b, 0xa3, 0xe3, 0xca, 0x44, 0x74, 0x60, 0x47, 0x48, 0x52, 0x2b, - 0x3b, 0x5b, 0x28, 0xc4, 0xec, 0x41, 0x28, 0xdf, 0x5c, 0xd3, 0xf0, 0xd8, - 0x9d, 0xb4, 0xd4, 0xf7, 0xeb, 0xca, 0xfc, 0xa2, 0x32, 0x7f, 0x9f, 0xff, - 0xf1, 0x50, 0x80, 0x30, 0x3f, 0xfc, 0x21, 0x4c, 0xd8, 0xfa, 0x07, 0xf0, - 0x36, 0x05, 0x91, 0x4f, 0xd9, 0x44, 0x70, 0x2d, 0x78, 0x86, 0xbb, 0x25, - 0x65, 0xb1, 0xd0, 0xde, 0xb7, 0xa5, 0xf5, 0xd7, 0x3e, 0x56, 0xd6, 0xff, - 0xd7, 0x5a, 0xf8, 0xe7, 0x59, 0xf8, 0x3d, 0xe7, 0xe9, 0xbd, 0x36, 0xf3, - 0xce, 0xb9, 0x96, 0x9d, 0xf5, 0x10, 0x3f, 0x00, 0x72, 0x82, 0x5b, 0x24, - 0x33, 0x83, 0x2d, 0xf1, 0xbd, 0x1c, 0xb5, 0xd1, 0x3e, 0x60, 0x99, 0x48, - 0xc7, 0x70, 0x10, 0x6d, 0xf0, 0x1d, 0x68, 0x2f, 0x88, 0x48, 0x24, 0x42, - 0x38, 0x36, 0x02, 0x27, 0x2b, 0x74, 0xe9, 0x13, 0x06, 0xac, 0x3a, 0x4e, - 0x44, 0xce, 0xa0, 0x67, 0x25, 0x5f, 0xe0, 0xfe, 0x35, 0x7a, 0xa8, 0x07, - 0xb5, 0x22, 0xb4, 0xad, 0xaf, 0x0d, 0x7c, 0x7c, 0x8d, 0x47, 0x75, 0xa3, - 0x05, 0x5e, 0x81, 0x9b, 0x7b, 0xbf, 0x1e, 0x8c, 0xf1, 0x19, 0x8b, 0x59, - 0x19, 0x1c, 0xe6, 0xfd, 0x41, 0xcc, 0xb9, 0x9f, 0x2b, 0xb5, 0xe1, 0x5a, - 0x9b, 0x5c, 0x13, 0xab, 0x05, 0x0e, 0xc6, 0x54, 0x82, 0x5f, 0x28, 0xb1, - 0xed, 0xed, 0x08, 0xec, 0x65, 0xad, 0x76, 0x78, 0x51, 0xf8, 0x35, 0xee, - 0xaa, 0x52, 0x71, 0x62, 0xbd, 0x23, 0xd7, 0xdc, 0xba, 0xa5, 0x0e, 0x3c, - 0xa6, 0x93, 0x5d, 0x27, 0xaa, 0x25, 0xd3, 0xa6, 0x59, 0x0f, 0xcd, 0xf5, - 0xc8, 0xf9, 0x57, 0x91, 0xe6, 0x0c, 0x44, 0x5f, 0x94, 0x17, 0xab, 0xca, - 0x2c, 0xe8, 0x62, 0x93, 0xe6, 0xa2, 0x84, 0x72, 0x73, 0xab, 0x7d, 0xaa, - 0xd4, 0x9b, 0x4b, 0xc3, 0x4f, 0xb5, 0x4b, 0x3b, 0x79, 0x03, 0x8f, 0x7a, - 0x56, 0x1f, 0xd1, 0x5e, 0x33, 0xae, 0x77, 0x51, 0x69, 0x8a, 0x91, 0xc1, - 0x5a, 0xac, 0x2e, 0xa3, 0xbd, 0xf9, 0xef, 0x54, 0xb1, 0xaa, 0x5f, 0x1c, - 0xd6, 0xa3, 0x72, 0xbd, 0x4d, 0x10, 0x10, 0x74, 0x51, 0x8d, 0x9f, 0x3e, - 0xfa, 0x06, 0x6e, 0x81, 0xbf, 0x91, 0x7d, 0xc4, 0x04, 0xfa, 0x11, 0xde, - 0x41, 0x69, 0xe2, 0x6d, 0x9c, 0x1f, 0xb4, 0x5a, 0xd1, 0xfa, 0xdc, 0x7d, - 0x01, 0x8e, 0xaa, 0x2f, 0xae, 0xb9, 0xf2, 0x55, 0xcf, 0xe1, 0xaa, 0xeb, - 0x5a, 0x95, 0x53, 0x3f, 0x60, 0x6b, 0xaa, 0xbf, 0x16, 0x81, 0xdf, 0x6f, - 0x1c, 0x00, 0x2f, 0xa9, 0x0e, 0x00, 0xa2, 0x91, 0x08, 0xed, 0x33, 0x2d, - 0x6e, 0xc3, 0x6f, 0xc8, 0xa3, 0x08, 0xa8, 0x0b, 0xc9, 0x6d, 0x93, 0xf1, - 0x5c, 0x95, 0xd7, 0xe7, 0x0a, 0x0d, 0xd4, 0x97, 0x20, 0x6c, 0x72, 0x6e, - 0x8e, 0x19, 0x60, 0x84, 0x25, 0x55, 0xca, 0x83, 0x91, 0xbe, 0x99, 0x64, - 0x2a, 0x59, 0x63, 0x1e, 0xff, 0x0d, 0xf7, 0xe2, 0x78, 0xef, 0x2d, 0x1b, - 0x10, 0xaf, 0x99, 0xca, 0xab, 0x32, 0xd9, 0x2c, 0x64, 0x6d, 0x77, 0x57, - 0xff, 0xf1, 0x50, 0x80, 0x38, 0xdf, 0xfc, 0x20, 0xa2, 0x4c, 0xdb, 0x45, - 0xa1, 0x33, 0x94, 0x53, 0x96, 0x72, 0x53, 0x35, 0x00, 0x01, 0x2f, 0x7d, - 0x71, 0x3d, 0x9d, 0x7f, 0xda, 0x56, 0xa5, 0x70, 0xeb, 0x8e, 0xb6, 0x3b, - 0x40, 0xfe, 0x46, 0x17, 0x7b, 0x83, 0x91, 0x25, 0x61, 0x52, 0x7b, 0x56, - 0x02, 0xca, 0x60, 0xcc, 0x7a, 0xe8, 0x32, 0x00, 0x2d, 0xc0, 0xa0, 0x37, - 0xc1, 0x90, 0xd8, 0x43, 0xb3, 0x1c, 0x31, 0xce, 0x6f, 0x2a, 0x82, 0x46, - 0x22, 0x86, 0xca, 0xe0, 0x0b, 0xde, 0x20, 0x0e, 0x29, 0xdf, 0x9c, 0x8b, - 0x3e, 0xbd, 0x3f, 0xca, 0xe0, 0x8e, 0xf4, 0xe7, 0xe8, 0xe6, 0x10, 0x88, - 0x9c, 0x79, 0x58, 0xcd, 0x35, 0x28, 0x4a, 0x22, 0x49, 0x70, 0x3c, 0xcf, - 0x74, 0x5a, 0xb7, 0x15, 0x0a, 0x2d, 0xcd, 0x73, 0x80, 0xd7, 0x52, 0x94, - 0x17, 0x9e, 0x73, 0x5f, 0x4d, 0x3a, 0x27, 0x5d, 0xa0, 0xd3, 0xb9, 0xd5, - 0x49, 0xd8, 0x6f, 0x2f, 0x85, 0x1e, 0xb3, 0x9a, 0xdb, 0x5e, 0x60, 0x86, - 0xb4, 0x7c, 0x84, 0x85, 0x4c, 0xcd, 0xdb, 0xb3, 0x9d, 0x48, 0x75, 0x4c, - 0x96, 0xd1, 0x9c, 0x27, 0x7d, 0x83, 0xe3, 0x9f, 0x4b, 0x87, 0xf1, 0x71, - 0x06, 0xa5, 0xda, 0x23, 0xe8, 0xbd, 0x7c, 0x62, 0xc0, 0x44, 0x62, 0xfb, - 0x1e, 0xa3, 0x60, 0x44, 0xb1, 0x62, 0x1c, 0x50, 0xdc, 0x29, 0xbb, 0x26, - 0xb2, 0xd3, 0x28, 0x02, 0x7c, 0x7c, 0x6b, 0xcb, 0x4e, 0x45, 0xe8, 0xb3, - 0x6f, 0x4f, 0x37, 0xc3, 0xc6, 0xf3, 0xd8, 0x1d, 0xb9, 0x0d, 0xe2, 0x64, - 0xbf, 0xfa, 0x89, 0xec, 0x29, 0xce, 0xaa, 0x57, 0xad, 0x68, 0x69, 0x22, - 0x08, 0xcc, 0x1a, 0x10, 0x00, 0x00, 0x00, 0x00, 0x26, 0x9f, 0x1f, 0x8e, - 0x47, 0x43, 0xf9, 0xfe, 0x98, 0xa2, 0xcd, 0x1f, 0x62, 0x55, 0xf4, 0x64, - 0xa2, 0x91, 0xa9, 0xc6, 0x95, 0x21, 0x91, 0x1e, 0xd1, 0x9a, 0xb3, 0xee, - 0x8b, 0x69, 0xa7, 0xfd, 0x96, 0x5c, 0xa8, 0xbb, 0x11, 0xb7, 0x63, 0x48, - 0x52, 0xb2, 0x99, 0x5a, 0x3c, 0x65, 0x7e, 0xe5, 0xd3, 0xe1, 0x3f, 0x81, - 0x23, 0xa4, 0x06, 0xca, 0x71, 0xb7, 0x76, 0x74, 0xb3, 0x36, 0x37, 0x41, - 0x1c, 0x28, 0x2e, 0xc6, 0x5a, 0x66, 0xa6, 0x66, 0xa6, 0x14, 0xc4, 0xc9, - 0xcf, 0x25, 0xc5, 0x91, 0xca, 0xb1, 0x77, 0x7a, 0xab, 0xa8, 0xd0, 0x20, - 0x30, 0x39, 0xbf, 0xba, 0x37, 0x3e, 0xe8, 0x7d, 0x13, 0x34, 0xa1, 0xf3, - 0x62, 0x1d, 0x37, 0x2a, 0xce, 0xe9, 0x19, 0x0d, 0xf7, 0xb4, 0x39, 0x22, - 0x50, 0x48, 0x50, 0x92, 0x64, 0x14, 0xd1, 0xab, 0x5d, 0x78, 0x92, 0x5e, - 0x31, 0x05, 0x0a, 0xb3, 0x55, 0x2e, 0xdb, 0x33, 0x6c, 0x6e, 0x33, 0x0a, - 0xc0, 0xa5, 0x8d, 0x6a, 0xa1, 0x94, 0xf1, 0xcf, 0x38, 0xf3, 0x38, 0x64, - 0x30, 0x3e, 0xae, 0x76, 0x35, 0x9d, 0x25, 0x06, 0x5c, 0xec, 0x99, 0x43, - 0x14, 0x94, 0x24, 0xb0, 0x87, 0xdb, 0xcc, 0xe5, 0x39, 0xb2, 0x3f, 0xf0, - 0xde, 0x49, 0xf9, 0xd9, 0x08, 0x18, 0x66, 0x62, 0x7a, 0xdd, 0xeb, 0x7a, - 0x63, 0xbb, 0x54, 0xe0, 0x38, 0xd4, 0x96, 0x27, 0xab, 0x13, 0x7e, 0x24, - 0xc5, 0xc5, 0xd8, 0x7c, 0x0b, 0x5f, 0x42, 0x29, 0xb5, 0xab, 0x1e, 0x8d, - 0x3d, 0x58, 0x9f, 0x92, 0xb2, 0x83, 0x9d, 0x00, 0x60, 0x38, 0xff, 0xf1, - 0x50, 0x80, 0x39, 0xdf, 0xfc, 0x20, 0xaa, 0x7a, 0xd4, 0x36, 0x7a, 0x55, - 0x88, 0x38, 0xb2, 0xa4, 0x52, 0x5e, 0x50, 0x25, 0x24, 0x5e, 0x4e, 0xb7, - 0xae, 0x33, 0x25, 0x75, 0x6b, 0x18, 0x3c, 0xd3, 0xfb, 0xe2, 0xc7, 0x95, - 0xa2, 0x7d, 0x22, 0xbb, 0x72, 0xa8, 0x76, 0x49, 0x85, 0xec, 0x13, 0x5a, - 0x55, 0xbf, 0x25, 0x48, 0xbe, 0x64, 0xad, 0x2c, 0xb6, 0x83, 0x0b, 0xd8, - 0xd0, 0x26, 0x9a, 0x66, 0xa5, 0x1a, 0x95, 0xec, 0xb8, 0x75, 0x6b, 0x43, - 0xb7, 0x3a, 0xb0, 0x16, 0x62, 0xe8, 0x97, 0x2d, 0x94, 0x62, 0x6a, 0xc0, - 0xa7, 0x46, 0xfd, 0x14, 0x18, 0x14, 0x16, 0xc3, 0xa0, 0xcb, 0xc7, 0x1d, - 0xcb, 0x10, 0xa7, 0x35, 0x88, 0x2a, 0x0a, 0x64, 0xde, 0x92, 0x5a, 0x34, - 0x1c, 0x69, 0x94, 0x20, 0xc5, 0x1b, 0x14, 0xd2, 0x60, 0xa3, 0xab, 0x15, - 0x95, 0x81, 0x4b, 0x28, 0x14, 0xf8, 0xe4, 0xb0, 0xa2, 0xfd, 0x40, 0xd8, - 0x70, 0xd4, 0xa4, 0xc1, 0x55, 0x92, 0x85, 0x02, 0xd0, 0xc3, 0xb0, 0xaa, - 0x68, 0x09, 0x2d, 0x73, 0xa9, 0x53, 0x71, 0x5e, 0x51, 0xbf, 0xec, 0x19, - 0xeb, 0x4f, 0xa2, 0x9b, 0xd3, 0x5d, 0xf7, 0xe3, 0x3c, 0x52, 0x8d, 0x03, - 0x69, 0xa8, 0x95, 0x28, 0x56, 0x40, 0x41, 0x5a, 0xe3, 0x2e, 0x5d, 0xc1, - 0x15, 0xa1, 0xf6, 0xd5, 0xa2, 0xd3, 0x4e, 0x2a, 0xd2, 0x73, 0xa5, 0x8a, - 0xa9, 0xd6, 0x30, 0x58, 0x54, 0x62, 0x16, 0xdb, 0x11, 0x91, 0x49, 0x25, - 0xa7, 0x92, 0x9b, 0x4c, 0x7a, 0xa6, 0x69, 0x8c, 0x1e, 0xff, 0xdd, 0x34, - 0xdb, 0x4a, 0x40, 0x3a, 0x49, 0x2b, 0xcb, 0xbf, 0x88, 0xe0, 0xdd, 0xc1, - 0xce, 0xc4, 0x4c, 0x05, 0xea, 0xc6, 0xb5, 0x0d, 0x3c, 0xca, 0x1d, 0x55, - 0x71, 0xcd, 0x94, 0x97, 0x95, 0x2a, 0x0e, 0xbb, 0xeb, 0x59, 0xaa, 0x9d, - 0x64, 0xba, 0xd6, 0xe5, 0x4b, 0x0c, 0x1e, 0x13, 0xd2, 0xca, 0x8f, 0x27, - 0x5f, 0xee, 0x5d, 0xf7, 0x3e, 0xff, 0x19, 0x3f, 0x7c, 0x52, 0x27, 0xd4, - 0xaf, 0xe0, 0xa7, 0x49, 0x2e, 0xf4, 0xc0, 0xa0, 0xa4, 0xc2, 0xed, 0xaa, - 0x15, 0xa6, 0x96, 0xba, 0xac, 0x4f, 0x82, 0x99, 0x39, 0x35, 0xf7, 0xc0, - 0x15, 0xe9, 0xdd, 0x80, 0x1d, 0x4c, 0x66, 0x30, 0x52, 0xd9, 0xc5, 0x9a, - 0xd4, 0x72, 0x80, 0x45, 0x22, 0xbc, 0x30, 0xd2, 0x64, 0xee, 0xc2, 0x65, - 0xd9, 0xc1, 0xc4, 0xfc, 0xe8, 0xee, 0xfd, 0x05, 0x3f, 0x36, 0x41, 0x27, - 0x1b, 0xa8, 0xb8, 0x9d, 0x36, 0xcb, 0x17, 0x72, 0x96, 0xb7, 0x74, 0x37, - 0x42, 0xff, 0x2b, 0x28, 0x50, 0x72, 0x72, 0x9e, 0x77, 0x35, 0x51, 0x12, - 0x7c, 0xcf, 0x4a, 0x90, 0x13, 0x89, 0x48, 0x07, 0xa0, 0x29, 0x86, 0x21, - 0x78, 0x89, 0x02, 0x45, 0x7a, 0x85, 0x9a, 0x9a, 0x5b, 0x1a, 0x0e, 0x88, - 0x14, 0x90, 0x9a, 0xf5, 0xc4, 0x12, 0x70, 0x86, 0x86, 0xc6, 0x5a, 0x25, - 0x97, 0x12, 0xbf, 0x1a, 0x09, 0x65, 0x83, 0x6b, 0x42, 0xc6, 0x60, 0x43, - 0xc7, 0x14, 0x60, 0x8a, 0xe8, 0xa7, 0x1c, 0x70, 0x50, 0xbe, 0x43, 0x45, - 0x45, 0x16, 0xa9, 0xe9, 0xa1, 0x02, 0x2b, 0xc1, 0x9e, 0x21, 0xe5, 0xe3, - 0x15, 0x95, 0x84, 0x07, 0x51, 0x5c, 0x91, 0x44, 0x11, 0xe1, 0x74, 0xe2, - 0xaa, 0x66, 0x07, 0x34, 0xd9, 0xb5, 0xb7, 0x57, 0x3c, 0xef, 0x54, 0x15, - 0x92, 0x87, 0x13, 0x3e, 0xff, 0xf1, 0x50, 0x80, 0x25, 0x7f, 0xfc, 0x21, - 0x1a, 0xcd, 0x4e, 0x3b, 0xff, 0x14, 0x09, 0xaa, 0xa1, 0xb3, 0x13, 0x8c, - 0xe3, 0x5c, 0x64, 0xc9, 0x2e, 0xbd, 0xbd, 0x6b, 0x77, 0x4a, 0x99, 0x12, - 0x4d, 0x49, 0x4b, 0xa6, 0xb7, 0x79, 0x2d, 0x42, 0xff, 0x72, 0x7f, 0x2c, - 0xbb, 0xbb, 0xfb, 0xca, 0xc1, 0xd9, 0x17, 0x57, 0xae, 0x54, 0xba, 0x8a, - 0xc0, 0x14, 0x8c, 0xd1, 0xd2, 0x3b, 0x10, 0x1f, 0x07, 0x62, 0xa7, 0x43, - 0x0f, 0x2d, 0xc2, 0xa5, 0x51, 0xb2, 0x24, 0xd5, 0x6f, 0x7d, 0x78, 0x2e, - 0xf5, 0x8c, 0xcb, 0x15, 0x53, 0x79, 0x1d, 0x86, 0x6a, 0xb5, 0xac, 0xba, - 0xa3, 0x33, 0x64, 0xf9, 0x01, 0xb1, 0x9d, 0x5c, 0x65, 0x68, 0x51, 0x97, - 0x51, 0xf0, 0x6f, 0x70, 0xec, 0x68, 0x56, 0xe6, 0xe9, 0xa2, 0xfe, 0x10, - 0x68, 0x42, 0x81, 0x76, 0x48, 0x88, 0x0d, 0xec, 0xf7, 0x13, 0xc7, 0x3d, - 0x50, 0x35, 0xb6, 0x0a, 0xe3, 0x53, 0x84, 0xcb, 0xdb, 0x51, 0xda, 0x0d, - 0x24, 0x5e, 0x86, 0xff, 0x61, 0xef, 0x74, 0x34, 0x33, 0xcd, 0x5d, 0x4b, - 0x9c, 0xf3, 0x93, 0x06, 0x94, 0x49, 0xe5, 0xd5, 0x04, 0x1e, 0x74, 0xc7, - 0x84, 0x57, 0xdc, 0xde, 0x3b, 0xcd, 0x57, 0xe7, 0xdb, 0x47, 0x98, 0x66, - 0xb2, 0x7f, 0xea, 0x2e, 0x56, 0xd4, 0x65, 0x5a, 0x80, 0xaf, 0xe7, 0x80, - 0x39, 0x57, 0xda, 0x96, 0x28, 0x1f, 0x88, 0xf5, 0xdc, 0xd4, 0x83, 0xb4, - 0x63, 0x6a, 0xfe, 0xc7, 0x41, 0xb2, 0x2b, 0xbd, 0xb1, 0xbf, 0xfb, 0x2a, - 0x3f, 0x1c, 0xf0, 0xbc, 0xc7, 0xc7, 0xb7, 0x1a, 0x81, 0x8f, 0xeb, 0xff, - 0x93, 0xd5, 0xaa, 0xee, 0x7c, 0xef, 0xb0, 0xb8, 0xb7, 0xaa, 0x62, 0x22, - 0xc5, 0x9f, 0x02, 0xef, 0xad, 0x92, 0x65, 0x38, 0xd6, 0xf7, 0xc2, 0xa6, - 0x44, 0x93, 0x2e, 0x94, 0x89, 0x4a, 0xae, 0x28, 0x12, 0xef, 0x5e, 0xeb, - 0x11, 0xbe, 0x64, 0x3a, 0x3a, 0x36, 0x28, 0x15, 0xc4, 0x94, 0x80, 0x6b, - 0xe6, 0xc6, 0xea, 0xdc, 0x24, 0xbe, 0x2e, 0xd6, 0xc3, 0x4c, 0xf5, 0xc4, - 0xf1, 0x3b, 0x23, 0x04, 0xc1, 0x08, 0x8b, 0xc0, 0x0f, 0x82, 0x99, 0xe3, - 0x77, 0x5e, 0xbc, 0xff, 0xf1, 0x50, 0x80, 0x28, 0x3f, 0xfc, 0x21, 0x1a, - 0xcd, 0xf5, 0x7e, 0x6f, 0x44, 0x88, 0xa7, 0x82, 0x32, 0x93, 0x0c, 0x15, - 0x23, 0x5b, 0xeb, 0x53, 0x2b, 0xcf, 0x35, 0xc6, 0x2b, 0x59, 0x5c, 0x6e, - 0xf7, 0xc6, 0xf5, 0x0b, 0xcb, 0xaa, 0xbc, 0xb4, 0xf3, 0xe2, 0xa7, 0x91, - 0x17, 0xb2, 0xbb, 0x3e, 0x50, 0xb2, 0xa8, 0x4c, 0x05, 0xe1, 0x4d, 0x91, - 0xdb, 0xb4, 0xa5, 0xdc, 0x3a, 0xe0, 0x5d, 0x31, 0x1a, 0xd4, 0xa5, 0x79, - 0x5a, 0xd4, 0x54, 0xbd, 0xff, 0x6f, 0x69, 0x6d, 0xb2, 0x72, 0xdc, 0xbd, - 0x7a, 0xdb, 0x63, 0xaf, 0x60, 0xbe, 0x5e, 0xd3, 0x77, 0x29, 0xd9, 0xae, - 0xf2, 0xf2, 0x5e, 0xf6, 0x10, 0x64, 0x97, 0x9e, 0x83, 0xcc, 0xe1, 0x8d, - 0x05, 0x20, 0xaa, 0x98, 0x1c, 0x98, 0x1f, 0x5a, 0x3c, 0x4e, 0xa4, 0x5e, - 0x99, 0x03, 0x3d, 0x15, 0x03, 0x49, 0xf7, 0x10, 0x8b, 0xfd, 0x06, 0x57, - 0xbf, 0x93, 0x74, 0xfb, 0xbe, 0x85, 0x0b, 0x8e, 0xbc, 0x13, 0xd9, 0xea, - 0x63, 0x55, 0x05, 0x66, 0x88, 0x7b, 0xfb, 0x98, 0xaf, 0x4a, 0xba, 0x1e, - 0xfb, 0xb8, 0xc0, 0x7a, 0x86, 0x31, 0x58, 0x1e, 0x3a, 0x3e, 0xc0, 0xc2, - 0x3d, 0x81, 0x25, 0xbe, 0x2c, 0x9e, 0xbe, 0xad, 0x46, 0xee, 0xf6, 0x69, - 0x8d, 0x51, 0x44, 0xdf, 0xbb, 0xc1, 0xa6, 0xc3, 0xc4, 0x12, 0xb2, 0x60, - 0x93, 0xfb, 0xb6, 0x92, 0x6a, 0x11, 0x19, 0xb0, 0xa5, 0x25, 0x9c, 0xac, - 0xc8, 0xa7, 0x63, 0xe5, 0x82, 0xb1, 0xdd, 0x55, 0x5b, 0xe7, 0xc9, 0xbf, - 0x9d, 0xd5, 0x63, 0xbe, 0xc1, 0x35, 0xab, 0x82, 0xfb, 0xb6, 0x57, 0x7d, - 0xc7, 0x6b, 0x5e, 0xde, 0x1e, 0x01, 0x59, 0xce, 0x73, 0x93, 0xe7, 0x4f, - 0x0a, 0x45, 0x11, 0xfe, 0x0a, 0x9d, 0x6a, 0x67, 0x7f, 0x7b, 0xf4, 0xb5, - 0x6b, 0x29, 0x9d, 0x6e, 0x35, 0x15, 0x54, 0x95, 0x26, 0xed, 0xd7, 0x69, - 0x02, 0xde, 0x26, 0xa1, 0x4e, 0x09, 0x71, 0x06, 0xe0, 0x9d, 0xca, 0xd0, - 0x4f, 0x7e, 0x59, 0x72, 0x05, 0xb3, 0xf9, 0x24, 0x7f, 0x18, 0x4d, 0x99, - 0x97, 0x80, 0xc8, 0x2c, 0x29, 0x1b, 0x8c, 0x1a, 0x96, 0xd7, 0xbc, 0x74, - 0xd4, 0xa1, 0xe6, 0x3f, 0xbb, 0xf2, 0x17, 0x39, 0xfc, 0xc4, 0x21, 0x59, - 0x5e, 0x33, 0x05, 0x5e, 0x6a, 0x33, 0x44, 0x14, 0x2f, 0xaf, 0x00, 0xce, - 0xff, 0xf1, 0x50, 0x80, 0x2b, 0xff, 0xfc, 0x21, 0x2a, 0xce, 0xf3, 0xff, - 0x7f, 0x84, 0x00, 0xa3, 0xa3, 0xb2, 0xd1, 0x4c, 0x14, 0x43, 0x05, 0x08, - 0xc1, 0x1e, 0x50, 0x93, 0x26, 0x6a, 0x97, 0x85, 0xe4, 0xa8, 0xbb, 0xbc, - 0x2a, 0x65, 0x5f, 0x1c, 0xc6, 0x7e, 0x05, 0xbb, 0xf0, 0xb2, 0xd6, 0x95, - 0x7a, 0x4b, 0xa7, 0x55, 0x87, 0x52, 0x16, 0x12, 0xa4, 0xb4, 0x67, 0xd5, - 0x7f, 0x75, 0x73, 0x49, 0x75, 0x5c, 0x66, 0x3d, 0x21, 0x76, 0xed, 0x42, - 0x18, 0x2b, 0xc9, 0x29, 0x05, 0x38, 0x83, 0x6d, 0x2b, 0x4f, 0x7d, 0x52, - 0x28, 0x40, 0x8f, 0xb2, 0xe4, 0xb6, 0xf2, 0xee, 0x45, 0xc0, 0x01, 0xf4, - 0x15, 0x25, 0x05, 0x2a, 0x0c, 0x15, 0x77, 0xb6, 0xdb, 0x15, 0x89, 0xe7, - 0xa1, 0xba, 0x97, 0xf2, 0x22, 0x7a, 0x9d, 0xb1, 0x98, 0xf1, 0x51, 0x28, - 0x46, 0xd4, 0x03, 0x43, 0xd8, 0x35, 0x7c, 0x1d, 0xa4, 0x50, 0xa8, 0xe5, - 0x8a, 0xa8, 0x91, 0x26, 0x7b, 0x40, 0x4b, 0x8f, 0xf7, 0xfc, 0x09, 0x03, - 0x6d, 0xe8, 0xe9, 0xe4, 0x43, 0x47, 0x4f, 0x50, 0xe6, 0xf8, 0x24, 0xbc, - 0x4f, 0x95, 0x70, 0xe0, 0x7f, 0x6a, 0xd9, 0xbc, 0x3e, 0x5d, 0xc6, 0x52, - 0xbc, 0x51, 0x74, 0x48, 0x40, 0x76, 0xf4, 0x57, 0x47, 0xc7, 0x88, 0xda, - 0xdd, 0xbb, 0x68, 0x79, 0x18, 0xfd, 0x1f, 0x62, 0x03, 0x05, 0x6e, 0x4c, - 0x16, 0x15, 0x65, 0xd4, 0x3c, 0x24, 0xa4, 0xad, 0x59, 0xec, 0xbe, 0x91, - 0x2b, 0x84, 0x95, 0x02, 0x69, 0x33, 0x35, 0xcb, 0x2e, 0x75, 0x15, 0xd2, - 0x8b, 0x29, 0xe8, 0x55, 0xb3, 0xb0, 0x7f, 0xa2, 0xef, 0x0a, 0x3d, 0xc9, - 0x38, 0x1d, 0xe9, 0xed, 0xf2, 0xbb, 0x8e, 0xa6, 0x3c, 0x7a, 0xc7, 0x5e, - 0x70, 0xef, 0xfb, 0x84, 0xe9, 0x67, 0x41, 0xb1, 0xd0, 0x22, 0x2f, 0x3a, - 0x38, 0x43, 0x34, 0x8c, 0x81, 0xf7, 0x95, 0xec, 0x8f, 0x7f, 0x3c, 0x64, - 0xcd, 0x52, 0xf1, 0x5a, 0xc9, 0x51, 0x77, 0x5b, 0xba, 0x4a, 0x42, 0x4c, - 0xb0, 0x3a, 0x63, 0xa7, 0x1b, 0x42, 0x6a, 0x25, 0x8c, 0x3b, 0xf0, 0xc3, - 0xbd, 0x77, 0xbf, 0x26, 0x10, 0x9a, 0x6b, 0x80, 0x5a, 0x87, 0x27, 0x2b, - 0x6a, 0x46, 0x45, 0xa2, 0x86, 0x47, 0x03, 0xdf, 0x88, 0x2f, 0x5a, 0xce, - 0x2e, 0xb3, 0x04, 0x21, 0x51, 0xac, 0x40, 0x88, 0x8a, 0x44, 0x51, 0xfb, - 0x7a, 0xaa, 0x9b, 0x1b, 0xba, 0x3b, 0x3b, 0xe5, 0x06, 0xbc, 0xfc, 0x12, - 0x48, 0x08, 0x97, 0x2a, 0x7b, 0x10, 0x41, 0x3b, 0x01, 0x03, 0x71, 0xe3, - 0xd9, 0xe6, 0xfc, 0xff, 0xf1, 0x50, 0x80, 0x31, 0x9f, 0xfc, 0x21, 0x4c, - 0xd8, 0x9f, 0x35, 0x80, 0x1a, 0x95, 0x30, 0x4c, 0x52, 0x3e, 0xec, 0xa7, - 0x96, 0x63, 0xca, 0xe9, 0x98, 0x00, 0x0b, 0x95, 0xd7, 0x1d, 0xeb, 0x2b, - 0xc7, 0xc6, 0x9e, 0x3f, 0x9f, 0x5f, 0x7e, 0x63, 0x9f, 0xeb, 0xc7, 0x20, - 0x25, 0x0e, 0xcb, 0x49, 0x63, 0xbb, 0xfe, 0x53, 0x04, 0x44, 0xb5, 0xc3, - 0x84, 0x2b, 0xb9, 0x9c, 0x21, 0xe3, 0x60, 0xaf, 0x19, 0x58, 0x40, 0x06, - 0x50, 0x00, 0xa8, 0x2c, 0xa1, 0x58, 0xd8, 0x67, 0x2e, 0x02, 0x15, 0x28, - 0x40, 0xae, 0xf6, 0x93, 0xd7, 0x2b, 0xaf, 0x4b, 0xa1, 0xde, 0xc8, 0x02, - 0x8f, 0xd9, 0x91, 0x9b, 0x67, 0x0d, 0x41, 0x04, 0xf6, 0x8f, 0xb4, 0xef, - 0x5b, 0x7a, 0xfb, 0x56, 0x0d, 0x0d, 0xd6, 0x1f, 0x24, 0x33, 0xd0, 0xf8, - 0x99, 0xda, 0xd1, 0x14, 0xca, 0x75, 0xe4, 0x2b, 0xf8, 0x91, 0xf0, 0xc0, - 0x76, 0x58, 0x26, 0x8d, 0xfc, 0x85, 0x9d, 0xac, 0x4b, 0xd8, 0xe4, 0xe8, - 0xa5, 0xc7, 0x16, 0xe9, 0x82, 0x51, 0xb2, 0x05, 0x2a, 0x9d, 0x04, 0x35, - 0xa6, 0xe5, 0x3c, 0x00, 0xe5, 0x12, 0xad, 0x4e, 0x10, 0xcc, 0x14, 0x83, - 0x4e, 0xfb, 0xf8, 0xea, 0x49, 0x18, 0xb9, 0x6d, 0xc9, 0xa8, 0xb6, 0xd8, - 0x81, 0x07, 0xe9, 0xc8, 0xbd, 0x90, 0x78, 0xd0, 0x1e, 0x16, 0xd1, 0xbf, - 0x7a, 0x45, 0x0f, 0x72, 0x6a, 0x3b, 0x90, 0x5b, 0xe7, 0x7a, 0x3f, 0x68, - 0xfe, 0xa6, 0xd8, 0x4a, 0xb0, 0x9d, 0xb7, 0x29, 0xa1, 0x25, 0xa0, 0x3d, - 0x8c, 0xd4, 0xe2, 0xc3, 0x0a, 0x2e, 0xea, 0x67, 0xca, 0xfc, 0xc9, 0x2c, - 0x7c, 0x99, 0x49, 0x33, 0x14, 0x59, 0xba, 0x43, 0x7a, 0x69, 0x84, 0x49, - 0x9c, 0x2f, 0x76, 0x6b, 0x8f, 0x30, 0xbc, 0xc1, 0x6d, 0xf4, 0xd0, 0x6c, - 0x3c, 0x9b, 0xd5, 0xf5, 0x7d, 0x75, 0x57, 0x4f, 0xfa, 0x3f, 0x93, 0xe2, - 0xb8, 0xd7, 0x77, 0x3f, 0xd7, 0x2f, 0x6d, 0x2b, 0x38, 0xb0, 0xda, 0x54, - 0x68, 0x74, 0x59, 0xf3, 0xc4, 0xb3, 0x91, 0x20, 0x80, 0x39, 0xfa, 0x1d, - 0xbb, 0x54, 0x9d, 0xc0, 0x5c, 0x5f, 0xdf, 0x60, 0x3a, 0x37, 0x89, 0x51, - 0x2a, 0xad, 0x3b, 0xb8, 0x56, 0x93, 0x7d, 0x5e, 0xe3, 0x4b, 0xba, 0x1c, - 0xf7, 0x47, 0x38, 0xc8, 0xe9, 0x68, 0x3e, 0x95, 0x7d, 0x9a, 0x6b, 0x59, - 0xbc, 0x97, 0xdb, 0x75, 0xcc, 0xe6, 0x2c, 0x2b, 0x62, 0xee, 0x9e, 0xaa, - 0x86, 0x79, 0xc1, 0x53, 0x1e, 0x71, 0x16, 0x41, 0x97, 0x64, 0x41, 0xa1, - 0x65, 0xcf, 0x56, 0x95, 0x05, 0xae, 0x06, 0xb3, 0x2e, 0x4d, 0x35, 0x32, - 0x2a, 0x98, 0xd5, 0xd5, 0x87, 0xac, 0x86, 0x56, 0xd2, 0xa8, 0x35, 0x82, - 0x72, 0x76, 0xc6, 0x84, 0xad, 0x3e, 0xee, 0xdd, 0xd8, 0xf0, 0xd2, 0xd4, - 0x42, 0xce, 0x1d, 0x84, 0xe7, 0x66, 0x9a, 0x65, 0x8d, 0xaf, 0xcf, 0xc5, - 0xf2, 0x42, 0x8e, 0xff, 0xf1, 0x50, 0x80, 0x3b, 0x1f, 0xfc, 0x20, 0x9c, - 0x4c, 0xdb, 0x65, 0x95, 0x28, 0x2a, 0x8e, 0x59, 0x4e, 0xd4, 0xd9, 0x6f, - 0x00, 0x00, 0x6a, 0x4b, 0xf3, 0x55, 0xaf, 0x6f, 0xfa, 0x47, 0xd6, 0xef, - 0x8e, 0x97, 0x65, 0xcf, 0x6f, 0x3c, 0x80, 0x03, 0x71, 0x3b, 0x2d, 0x45, - 0x52, 0x24, 0xce, 0x27, 0xf2, 0xdd, 0x49, 0x3a, 0x79, 0xb4, 0xe1, 0x31, - 0x1b, 0xcd, 0x70, 0x82, 0x9c, 0x07, 0xc3, 0x4f, 0x04, 0x78, 0x67, 0x6e, - 0x9a, 0x24, 0x21, 0x78, 0x4a, 0x02, 0x80, 0x7e, 0x54, 0x02, 0x65, 0xbf, - 0x87, 0xdd, 0xc0, 0x04, 0xf0, 0xf9, 0x2f, 0xa5, 0x65, 0xe0, 0x88, 0xe9, - 0x53, 0xe5, 0x01, 0xba, 0xa8, 0x06, 0xa5, 0x40, 0x04, 0xa1, 0x11, 0x4e, - 0xc4, 0xa3, 0x54, 0xe0, 0xa5, 0x83, 0x00, 0xec, 0x9a, 0x1a, 0xb6, 0x56, - 0x50, 0xc8, 0x97, 0x1a, 0x14, 0xce, 0xa5, 0x14, 0xd2, 0x3c, 0xb2, 0xee, - 0xec, 0x68, 0x8b, 0xe3, 0xf0, 0x50, 0x6a, 0x7b, 0x4d, 0x71, 0x42, 0x90, - 0x80, 0x64, 0xf2, 0xe7, 0x7a, 0x5e, 0x5b, 0x40, 0xe9, 0x97, 0xa4, 0x72, - 0xc7, 0xff, 0x6f, 0x1d, 0xea, 0xe2, 0x8d, 0x2d, 0x9c, 0xa5, 0xfb, 0x6b, - 0xf9, 0xff, 0x98, 0xf9, 0xd9, 0x20, 0xdb, 0x62, 0x17, 0x2f, 0x49, 0x6d, - 0x64, 0xc1, 0x41, 0x64, 0x6d, 0x04, 0xe8, 0x85, 0x51, 0xd0, 0x2f, 0x39, - 0x24, 0x80, 0x8c, 0x90, 0xaf, 0x39, 0x8a, 0xd9, 0x74, 0x07, 0x8d, 0x2c, - 0xa9, 0xf1, 0x5a, 0x81, 0x26, 0x56, 0x5e, 0xbb, 0xa9, 0x1e, 0x2f, 0x5d, - 0xd2, 0xad, 0xdc, 0xa9, 0x5d, 0x93, 0x2e, 0x7a, 0xfd, 0x45, 0x76, 0x19, - 0x4b, 0x3b, 0xf6, 0x68, 0x39, 0xa5, 0x00, 0x88, 0x0d, 0x7f, 0x69, 0x10, - 0xb9, 0x48, 0x96, 0xd4, 0x3d, 0x6b, 0x1b, 0x49, 0x24, 0xc4, 0x1a, 0xe1, - 0x7d, 0xf1, 0xad, 0xeb, 0x99, 0x29, 0x4d, 0x7d, 0x7b, 0xd5, 0x38, 0xdd, - 0xf9, 0xfc, 0xeb, 0xeb, 0xdf, 0x3c, 0xf7, 0xed, 0x3b, 0xf6, 0xce, 0x7e, - 0x80, 0xbc, 0xce, 0x40, 0x26, 0x71, 0xc0, 0x0c, 0x4f, 0x66, 0xfc, 0x35, - 0x4c, 0x58, 0x6f, 0xce, 0x99, 0xed, 0xa9, 0xc2, 0x9b, 0xcc, 0xe6, 0x78, - 0x09, 0x6a, 0xb6, 0xb1, 0x99, 0x2a, 0x78, 0x91, 0xd5, 0x90, 0x9c, 0xab, - 0xee, 0x70, 0x02, 0x6c, 0x59, 0x19, 0x0a, 0xd1, 0x17, 0x79, 0x55, 0xbf, - 0x2a, 0x94, 0x4f, 0x69, 0x12, 0x6a, 0x3a, 0x74, 0x09, 0x31, 0x40, 0xab, - 0xd9, 0x13, 0xce, 0xfc, 0x26, 0x86, 0xef, 0x20, 0xbc, 0x5d, 0xd4, 0xf0, - 0xd1, 0x34, 0xc8, 0x28, 0x2e, 0xb5, 0x54, 0x36, 0x2c, 0xa7, 0xf3, 0x55, - 0x4e, 0x53, 0xff, 0x0e, 0x95, 0x26, 0xef, 0x90, 0x50, 0x50, 0x50, 0x50, - 0x50, 0x53, 0x4b, 0x77, 0x27, 0x85, 0x10, 0x5f, 0xd2, 0xa0, 0x19, 0xff, - 0x6e, 0x86, 0x5e, 0xb5, 0xa2, 0xab, 0xe0, 0xa0, 0xaf, 0x61, 0x4a, 0x41, - 0x47, 0x42, 0x8e, 0xe4, 0xee, 0x6b, 0xa5, 0xa2, 0xa7, 0xa4, 0x53, 0x5d, - 0x3b, 0x8d, 0x05, 0x05, 0x35, 0x0a, 0x10, 0x52, 0xd4, 0xb8, 0x96, 0xaa, - 0x0a, 0xa6, 0x0a, 0x0b, 0xf0, 0x50, 0x54, 0x36, 0x9a, 0x15, 0xc8, 0x82, - 0x82, 0x93, 0x05, 0x05, 0x05, 0x64, 0xee, 0xf5, 0xe0, 0x32, 0x39, 0x11, - 0xb3, 0x1e, 0x38, 0xcc, 0xc8, 0xcc, 0xcc, 0x04, 0xef, 0x5e, 0x18, 0x3b, - 0xb9, 0x11, 0x03, 0x1b, 0x33, 0x33, 0x33, 0x33, 0x03, 0x32, 0x3e, 0x0e, - 0xf8, 0x61, 0x85, 0x78, 0x5d, 0x5b, 0x9f, 0xff, 0xf1, 0x50, 0x80, 0x3b, - 0x5f, 0xfc, 0x20, 0xa0, 0x7a, 0xd6, 0x84, 0xeb, 0x18, 0x0e, 0x39, 0xd5, - 0xd5, 0x24, 0x54, 0xbc, 0xd6, 0xda, 0xcd, 0x3d, 0xb6, 0x53, 0x9e, 0x26, - 0xa5, 0xef, 0xdb, 0xe7, 0xc8, 0x3c, 0x4c, 0x40, 0x26, 0x2b, 0x18, 0x08, - 0xfc, 0xe7, 0x9b, 0x6d, 0x99, 0xe6, 0x31, 0x78, 0x3a, 0xa4, 0x84, 0xe1, - 0x4d, 0xcf, 0xa5, 0x69, 0xbf, 0x2b, 0xb3, 0xf0, 0xbb, 0xbf, 0x47, 0xf3, - 0x9e, 0x31, 0xb8, 0xb7, 0x85, 0xe9, 0x6e, 0xfd, 0x64, 0xef, 0x02, 0xe2, - 0x42, 0xf5, 0xe1, 0xd3, 0x90, 0x34, 0x3a, 0x2b, 0x3d, 0xe3, 0x4e, 0x9b, - 0x33, 0xf1, 0x50, 0x5c, 0xe7, 0xb0, 0x76, 0x8f, 0xd9, 0x78, 0x2a, 0x81, - 0xd0, 0x9a, 0x74, 0xc4, 0x34, 0x52, 0x67, 0xd9, 0x78, 0xf6, 0xd5, 0xff, - 0x6c, 0xbc, 0x8c, 0x4e, 0xda, 0x5b, 0xb5, 0x31, 0x54, 0x78, 0x8e, 0x5a, - 0xc0, 0x90, 0x1a, 0x22, 0x6d, 0xc9, 0x79, 0x55, 0x03, 0xaa, 0x84, 0x27, - 0xac, 0xc6, 0xab, 0xe2, 0xcf, 0xa2, 0x88, 0x1f, 0x19, 0x45, 0x4b, 0x44, - 0x1a, 0x53, 0x72, 0xa3, 0xc5, 0x1d, 0x0a, 0x36, 0x4a, 0xe8, 0xf2, 0x17, - 0x66, 0xf7, 0x28, 0x3f, 0xab, 0x43, 0xe3, 0x47, 0xbe, 0x95, 0x4a, 0xc8, - 0xde, 0x1f, 0xb2, 0xb9, 0xd0, 0xd1, 0x01, 0x21, 0xea, 0x00, 0x25, 0xa2, - 0x28, 0x6a, 0x5c, 0xca, 0x39, 0xc5, 0xb2, 0xaa, 0x92, 0x02, 0xd7, 0x20, - 0x9a, 0x9a, 0x0c, 0xf2, 0xd6, 0xc0, 0x00, 0x0c, 0x00, 0x05, 0x64, 0xe7, - 0xb7, 0xa5, 0x9d, 0x0f, 0x30, 0xba, 0x80, 0xce, 0xa0, 0x80, 0x79, 0x96, - 0xf3, 0x15, 0x91, 0x10, 0x40, 0x18, 0x00, 0x02, 0x11, 0x44, 0xa0, 0x87, - 0x0f, 0x0e, 0xe9, 0xe7, 0x9f, 0x1a, 0xd6, 0x38, 0x52, 0x41, 0x42, 0x87, - 0x63, 0x01, 0x79, 0xed, 0xde, 0x4a, 0xb9, 0x7c, 0xba, 0xe6, 0xe6, 0xb7, - 0xd7, 0xaf, 0x63, 0x2e, 0x79, 0xfb, 0x6a, 0xee, 0xb5, 0x3a, 0xee, 0xc5, - 0x7b, 0x95, 0xd7, 0x90, 0xeb, 0x56, 0x25, 0xc7, 0x37, 0xe5, 0xee, 0xf2, - 0x54, 0x09, 0xcc, 0xad, 0x7d, 0x03, 0x94, 0x88, 0x56, 0x27, 0xed, 0xc0, - 0x4c, 0x92, 0xd4, 0x7c, 0xab, 0xc9, 0x3d, 0xdb, 0x9a, 0x22, 0xaa, 0x2f, - 0x66, 0xf1, 0xed, 0xec, 0xd0, 0xe6, 0x8b, 0x45, 0x15, 0x9e, 0x22, 0x17, - 0xe2, 0xcb, 0x3f, 0x01, 0x40, 0x13, 0xea, 0x52, 0x39, 0x5e, 0xa4, 0x39, - 0xed, 0xd2, 0xe1, 0x4d, 0xf3, 0x76, 0x9d, 0xeb, 0xb0, 0xf4, 0xea, 0xf5, - 0xf4, 0x93, 0x03, 0xec, 0x53, 0x60, 0xac, 0x80, 0xce, 0xd8, 0x15, 0x9d, - 0x83, 0x4d, 0x1b, 0x17, 0xeb, 0x04, 0xcd, 0x60, 0xa5, 0x54, 0x85, 0x09, - 0x30, 0x25, 0xf6, 0xa6, 0x49, 0x50, 0xd5, 0x01, 0xcf, 0x9f, 0x79, 0xd4, - 0xff, 0x4a, 0xd7, 0x7a, 0xea, 0x86, 0xba, 0xe4, 0xdf, 0x7e, 0xad, 0x9e, - 0x2f, 0xc4, 0x56, 0xea, 0x8b, 0xe5, 0x8a, 0x45, 0x6e, 0x18, 0xb2, 0xea, - 0xe4, 0x81, 0xcf, 0xe0, 0x7a, 0x01, 0x8b, 0x3c, 0x98, 0x0c, 0xd0, 0x47, - 0xfc, 0xaa, 0x5b, 0xd5, 0xc0, 0x00, 0x50, 0x00, 0x68, 0x25, 0x18, 0x6a, - 0x17, 0x6d, 0x09, 0x08, 0x0f, 0x6d, 0x42, 0xf0, 0x08, 0x36, 0x53, 0x65, - 0x41, 0x32, 0x2d, 0x26, 0x40, 0xfb, 0x52, 0x36, 0xf8, 0xd9, 0xe2, 0xa6, - 0x17, 0x64, 0x59, 0xd4, 0x6b, 0x16, 0xfd, 0x1a, 0xd0, 0x00, 0xc5, 0xdd, - 0x21, 0x2a, 0x10, 0x00, 0x84, 0x51, 0x28, 0xdc, 0x2f, 0xe1, 0xf9, 0x88, - 0xe0, 0xff, 0xf1, 0x50, 0x80, 0x29, 0x7f, 0xfc, 0x21, 0x1a, 0xcf, 0x2f, - 0xa9, 0x74, 0x00, 0x04, 0xa0, 0xb1, 0x33, 0xd1, 0x8a, 0x34, 0x52, 0x84, - 0x97, 0x32, 0xf9, 0xe8, 0xa9, 0x38, 0xdd, 0x4b, 0x55, 0x69, 0x75, 0x7a, - 0xd6, 0xe7, 0x1c, 0x89, 0x97, 0x95, 0x9f, 0x02, 0x97, 0xc1, 0x4a, 0x48, - 0xe8, 0xbc, 0xfe, 0xe2, 0xb9, 0xf8, 0x4e, 0xbb, 0xfb, 0x65, 0xa7, 0xca, - 0x3a, 0xbb, 0xa5, 0xae, 0xf7, 0xd4, 0xc5, 0xa7, 0x8a, 0xa6, 0x08, 0x41, - 0x4a, 0xb4, 0x48, 0xc4, 0x8a, 0xed, 0xbd, 0x02, 0xd8, 0x92, 0x08, 0xd6, - 0x58, 0xf6, 0xf6, 0x9f, 0x05, 0x05, 0xb9, 0x4d, 0xd2, 0x65, 0x19, 0xb2, - 0x60, 0xe2, 0x52, 0x98, 0x7a, 0x61, 0xb9, 0x3d, 0xeb, 0xbe, 0x5d, 0x4b, - 0x30, 0xe9, 0x6e, 0xb5, 0x02, 0x7e, 0xf7, 0x2b, 0xa2, 0xe4, 0x93, 0x61, - 0x13, 0x90, 0x64, 0x5c, 0x57, 0xfb, 0x99, 0xf8, 0xdc, 0x83, 0x4c, 0x08, - 0xe7, 0xf1, 0x42, 0x94, 0x80, 0xd2, 0x68, 0x5c, 0x15, 0xe8, 0x9c, 0xe7, - 0x80, 0xe1, 0xf9, 0xd6, 0x9d, 0x53, 0xc2, 0xbb, 0xfe, 0x2d, 0x0a, 0x6c, - 0xc3, 0xb4, 0xa9, 0x64, 0x7b, 0x06, 0x95, 0x33, 0x0f, 0xb3, 0x66, 0x0f, - 0xcc, 0x2f, 0xf1, 0x79, 0x3a, 0xdc, 0x91, 0xbe, 0x0a, 0x10, 0xc9, 0x73, - 0x2d, 0xc6, 0xbf, 0xc5, 0xa6, 0x6a, 0x71, 0x4f, 0xba, 0xdc, 0x1a, 0xf4, - 0x12, 0x73, 0xcd, 0x32, 0x2f, 0x47, 0x24, 0x47, 0x8a, 0xb5, 0xe3, 0xfc, - 0xaf, 0x5c, 0xf2, 0x32, 0x2e, 0xae, 0xf6, 0xec, 0x3c, 0xb7, 0x42, 0x95, - 0x06, 0xd2, 0x6f, 0x1f, 0x24, 0xa2, 0xef, 0xac, 0x6a, 0x15, 0x73, 0xdc, - 0xcf, 0x26, 0x8f, 0x0e, 0x7d, 0x3d, 0x1d, 0xbc, 0xfb, 0x93, 0xef, 0x39, - 0xf1, 0xa0, 0xb2, 0xa1, 0xdd, 0xff, 0x06, 0xb2, 0xa4, 0x9b, 0xe8, 0xae, - 0x7c, 0xd5, 0xf7, 0x5f, 0x1d, 0xf1, 0x5a, 0xdf, 0x5e, 0xfd, 0x4b, 0xd6, - 0x39, 0x9c, 0x73, 0x7b, 0xd3, 0x75, 0x01, 0x95, 0xee, 0x0c, 0x1d, 0x9c, - 0x50, 0x1c, 0x74, 0xab, 0x07, 0x7b, 0xc9, 0x46, 0xcf, 0xe0, 0xaf, 0xe8, - 0x54, 0x4f, 0x55, 0xf7, 0x78, 0xd6, 0x57, 0x6c, 0xf5, 0x09, 0x98, 0x9c, - 0xb9, 0xa1, 0x8e, 0xd3, 0x71, 0x32, 0x96, 0xa0, 0x59, 0x4a, 0x60, 0x8a, - 0x92, 0x53, 0x53, 0x76, 0x88, 0xbc, 0x34, 0x41, 0x75, 0x74, 0x89, 0x2a, - 0x00, 0xcb, 0x10, 0xa3, 0x40, 0xb4, 0xaf, 0x80, 0xff, 0xf1, 0x50, 0x80, - 0x29, 0xdf, 0xfc, 0x21, 0x1a, 0xcf, 0xe5, 0xac, 0xed, 0x00, 0x00, 0xa0, - 0xb1, 0x42, 0x92, 0x0c, 0x14, 0x71, 0x25, 0xcb, 0xf5, 0xd4, 0x6f, 0x89, - 0x57, 0xbb, 0x4b, 0xe7, 0xce, 0xfa, 0xdf, 0x13, 0x8e, 0x63, 0xad, 0xcc, - 0x56, 0x08, 0x2e, 0x1f, 0x80, 0xc9, 0xe3, 0x9b, 0x69, 0xc6, 0x6b, 0x64, - 0xc0, 0xe3, 0xc0, 0xa6, 0xc3, 0x55, 0x7c, 0x28, 0xa4, 0xa3, 0x0d, 0xce, - 0x16, 0xa8, 0x81, 0x45, 0x3d, 0xed, 0x8e, 0xc0, 0xea, 0x1e, 0xaa, 0x64, - 0x6f, 0x8c, 0x0a, 0x90, 0x95, 0x7f, 0xf1, 0x2a, 0x37, 0xe5, 0x0e, 0xc5, - 0x3a, 0xca, 0x97, 0x8b, 0x5d, 0xef, 0x70, 0x9b, 0xed, 0x96, 0x9b, 0x34, - 0x5a, 0xe1, 0x4c, 0xa6, 0x9c, 0xaa, 0xf6, 0xfb, 0x1e, 0x99, 0xea, 0x41, - 0x22, 0xb9, 0x2d, 0xa4, 0x99, 0xc7, 0x5e, 0x2c, 0x8c, 0xd1, 0x00, 0xa0, - 0x87, 0xf5, 0x28, 0x93, 0x8a, 0xfb, 0xdf, 0xdf, 0x19, 0x6e, 0x68, 0xa5, - 0x8a, 0xc5, 0x4a, 0xf6, 0xff, 0x73, 0x2a, 0xb2, 0xfd, 0x8a, 0x8a, 0xa5, - 0xda, 0x36, 0x23, 0x9e, 0x8e, 0x9a, 0xfa, 0x17, 0xf0, 0x8c, 0xf3, 0x2f, - 0xec, 0x1b, 0x57, 0x93, 0xc4, 0x82, 0x88, 0xd3, 0xd4, 0xba, 0x7b, 0xa3, - 0x70, 0x4c, 0x6f, 0xfa, 0x85, 0x8d, 0x11, 0xcc, 0xb7, 0x23, 0x7c, 0xe0, - 0x78, 0x62, 0x6c, 0xf5, 0xcc, 0x92, 0x6c, 0x7f, 0x8a, 0xeb, 0x6c, 0xd4, - 0xdb, 0xc3, 0x5e, 0x08, 0xfd, 0x28, 0x78, 0x1d, 0xef, 0x00, 0x5e, 0xe2, - 0x28, 0x3d, 0x1b, 0x24, 0x1b, 0x72, 0x34, 0x65, 0xfb, 0x8e, 0x28, 0x21, - 0xe3, 0xea, 0x24, 0x6c, 0xee, 0xad, 0xfb, 0x74, 0x2b, 0xeb, 0x69, 0xb6, - 0xa1, 0xf8, 0xba, 0xb9, 0x47, 0xc4, 0x5b, 0x77, 0xa0, 0x70, 0xb2, 0x96, - 0x1e, 0x25, 0x0f, 0xb4, 0x92, 0xe0, 0xcd, 0x6f, 0x3d, 0xae, 0xb7, 0x57, - 0x77, 0xcb, 0x5d, 0x7b, 0xfd, 0x4e, 0x3e, 0xd9, 0x55, 0x6a, 0xac, 0xb2, - 0x81, 0xe9, 0x36, 0x4c, 0x73, 0x85, 0x6e, 0x85, 0x44, 0xd3, 0xae, 0x26, - 0x15, 0xf0, 0x46, 0xbd, 0x53, 0xdf, 0x07, 0xb7, 0xd9, 0x0e, 0xeb, 0xbe, - 0xbe, 0x50, 0xbc, 0xe5, 0x7c, 0x25, 0x5e, 0x14, 0xf9, 0x76, 0xe2, 0x8c, - 0xdd, 0x8a, 0x54, 0xeb, 0x04, 0x4a, 0xa0, 0x95, 0xb8, 0xe4, 0xa1, 0x8b, - 0x2d, 0x5a, 0xcd, 0x15, 0x55, 0xce, 0xc0, 0x8e, 0x01, 0x86, 0x3a, 0xe7, - 0x48, 0xb8, 0xc0, 0x3c, 0x40, 0x0e, 0xff, 0xf1, 0x50, 0x80, 0x38, 0x5f, - 0xfc, 0x20, 0xa6, 0x1a, 0xd4, 0x26, 0x72, 0x35, 0x90, 0x84, 0x61, 0x1e, - 0x52, 0xb5, 0xbe, 0x35, 0x9d, 0xeb, 0xcd, 0xb9, 0xce, 0xb7, 0x7c, 0xc5, - 0xf3, 0xc7, 0x4d, 0x73, 0x75, 0xc7, 0x77, 0xae, 0xdb, 0xe9, 0x60, 0xef, - 0xa2, 0xe1, 0x80, 0xab, 0x8c, 0x6f, 0xb6, 0x29, 0x91, 0xd8, 0xa5, 0x95, - 0x2b, 0x1a, 0xc0, 0x2b, 0x30, 0x9e, 0xa2, 0x09, 0x01, 0x1e, 0xe8, 0x5b, - 0xb4, 0x13, 0x53, 0x25, 0x1e, 0x58, 0x45, 0xab, 0x0f, 0xf9, 0xe7, 0x30, - 0x33, 0xc2, 0xec, 0xf6, 0xe3, 0xc7, 0xf5, 0xf9, 0x73, 0x99, 0xed, 0x21, - 0x99, 0x80, 0x84, 0x70, 0xf5, 0x33, 0x6e, 0x30, 0xbe, 0x6c, 0xeb, 0x98, - 0x7d, 0x1c, 0x20, 0x03, 0xa8, 0xda, 0x3a, 0x61, 0x19, 0x57, 0x13, 0x66, - 0x80, 0x8e, 0x3d, 0x1e, 0x06, 0x47, 0xf1, 0xd6, 0x09, 0x8f, 0xdf, 0xda, - 0x2b, 0xa2, 0xca, 0xa3, 0x88, 0xcc, 0xaa, 0x4a, 0x20, 0xaa, 0x46, 0x50, - 0xd5, 0x19, 0x44, 0x29, 0x41, 0x94, 0x4f, 0x9c, 0x11, 0x91, 0xc4, 0x30, - 0x1b, 0xaa, 0x80, 0x16, 0x62, 0x56, 0x3a, 0x0d, 0xd3, 0x39, 0x8c, 0x00, - 0x54, 0xa3, 0x63, 0xe6, 0x12, 0x53, 0xa6, 0xcb, 0x40, 0xe5, 0x45, 0x20, - 0x2c, 0x8c, 0x15, 0x5d, 0x08, 0xc6, 0xc1, 0x0b, 0xa8, 0x29, 0xd9, 0x54, - 0xe5, 0x28, 0x28, 0x3b, 0x34, 0xc4, 0x42, 0x2a, 0xac, 0xaa, 0x42, 0x15, - 0xc0, 0x64, 0x00, 0x77, 0x99, 0xf9, 0x85, 0x7c, 0x18, 0x46, 0x01, 0xbb, - 0x81, 0xcd, 0x28, 0xbf, 0x61, 0x16, 0xc8, 0x88, 0x49, 0x6c, 0xc9, 0xc1, - 0xb7, 0x02, 0xff, 0x67, 0xc1, 0x37, 0x1b, 0x61, 0x40, 0x32, 0x30, 0x7d, - 0x0b, 0x7d, 0xd7, 0x8a, 0x67, 0x41, 0xde, 0x0a, 0x53, 0x2b, 0xcf, 0x91, - 0x5a, 0x11, 0x4a, 0x55, 0xac, 0x4c, 0xb4, 0x79, 0x89, 0x1a, 0x71, 0xcf, - 0x94, 0xae, 0xbc, 0x4e, 0x67, 0xb5, 0x49, 0xcd, 0xea, 0xb8, 0xcd, 0xc9, - 0xd7, 0x5e, 0xb3, 0xe2, 0xa5, 0x2e, 0xb7, 0x52, 0x54, 0x07, 0x7b, 0x4a, - 0x92, 0x11, 0x27, 0x0d, 0xf8, 0x45, 0xe5, 0x74, 0x5b, 0xc4, 0x28, 0x2e, - 0x00, 0xef, 0x82, 0x26, 0x55, 0x0a, 0xf3, 0x5c, 0x12, 0x51, 0x9e, 0x05, - 0xba, 0x62, 0x13, 0x42, 0xb8, 0x88, 0x21, 0x92, 0xa0, 0x7e, 0x36, 0xb8, - 0xbd, 0x5b, 0x5d, 0xe3, 0xdf, 0x71, 0x5d, 0x8c, 0xab, 0x38, 0x08, 0x30, - 0xf6, 0xb4, 0x63, 0x1b, 0x58, 0x79, 0x60, 0xfa, 0x84, 0x96, 0x9f, 0xf8, - 0xc7, 0xe7, 0x01, 0x74, 0xf0, 0x74, 0x95, 0x26, 0x2f, 0xf3, 0x83, 0x72, - 0x0a, 0xba, 0xf0, 0x7e, 0x80, 0xc8, 0x8a, 0x60, 0xf1, 0xda, 0xbe, 0x6f, - 0xf0, 0xca, 0x41, 0xda, 0xd4, 0x28, 0x09, 0x00, 0xfb, 0x60, 0x31, 0x69, - 0xdb, 0xe7, 0xe9, 0x00, 0x6f, 0x18, 0xa1, 0xdf, 0xb3, 0x3e, 0x6f, 0x5d, - 0xe0, 0x8d, 0xb6, 0xe5, 0x33, 0xaa, 0x14, 0x44, 0x3f, 0x93, 0xcf, 0xd4, - 0x3e, 0x9f, 0x99, 0xe5, 0xe7, 0x80, 0x57, 0x80, 0x2a, 0x9f, 0xbb, 0x7d, - 0xb7, 0x31, 0x5e, 0xa5, 0x80, 0x88, 0x71, 0xd3, 0xeb, 0x6f, 0x2f, 0x72, - 0xe4, 0x8c, 0x17, 0x2f, 0x94, 0x3e, 0x65, 0xe1, 0x17, 0xce, 0x1b, 0x8d, - 0x79, 0xf1, 0xb4, 0x3e, 0xa4, 0x11, 0x4a, 0x24, 0xb9, 0x48, 0xa3, 0xe0, - 0xff, 0xf1, 0x50, 0x80, 0x38, 0x1f, 0xfc, 0x20, 0xa5, 0x1a, 0xd4, 0x86, - 0x3c, 0x05, 0x1a, 0x27, 0x63, 0x80, 0x3f, 0x7f, 0xce, 0x3f, 0x5f, 0x5f, - 0xed, 0xff, 0x69, 0x5f, 0x8f, 0xb5, 0xe9, 0xc3, 0xbf, 0x37, 0xfb, 0x5d, - 0x49, 0x77, 0xf7, 0xee, 0x79, 0xaa, 0xa1, 0xf1, 0x9f, 0xd4, 0xf4, 0x3b, - 0x7e, 0xc1, 0x0a, 0xe6, 0x72, 0x6a, 0xb0, 0x03, 0x88, 0x02, 0x2c, 0xf3, - 0x9f, 0xd5, 0x85, 0x2b, 0x44, 0x57, 0x61, 0x56, 0x57, 0xaa, 0xee, 0x86, - 0x8e, 0xd9, 0x28, 0xe3, 0x6a, 0x10, 0x30, 0xe8, 0xe4, 0x12, 0x61, 0xfe, - 0xe1, 0xfd, 0x43, 0x76, 0xba, 0x23, 0x0a, 0x59, 0xd7, 0xc2, 0xa6, 0x1c, - 0x00, 0x00, 0x8b, 0x01, 0xe9, 0xaf, 0x66, 0xd4, 0x00, 0xb4, 0x35, 0xcc, - 0x7a, 0x3f, 0x9b, 0x17, 0x21, 0x00, 0x01, 0x87, 0x67, 0x20, 0x08, 0x31, - 0x39, 0xac, 0xb5, 0xea, 0x6d, 0x5f, 0xc9, 0xba, 0x70, 0x4e, 0x89, 0xc8, - 0x7a, 0x16, 0x99, 0x11, 0x53, 0x6f, 0xf4, 0x84, 0xbf, 0xc5, 0xe2, 0x9c, - 0x00, 0xfe, 0x9f, 0x79, 0x72, 0x53, 0xfa, 0x40, 0x41, 0xfe, 0x1b, 0x7e, - 0xb9, 0xbe, 0x5f, 0xe3, 0x42, 0xc5, 0x3f, 0xa7, 0xde, 0xaf, 0xaf, 0xdf, - 0x6c, 0x0a, 0x8a, 0xc5, 0xa4, 0x09, 0x74, 0xd0, 0x50, 0x13, 0x5b, 0xbe, - 0x00, 0xf3, 0x75, 0x33, 0x72, 0x44, 0x82, 0x3b, 0xc2, 0xa5, 0x07, 0xba, - 0x88, 0x88, 0xa3, 0x4a, 0x98, 0x04, 0xf9, 0x6d, 0xe9, 0xc2, 0x0e, 0xfb, - 0x76, 0x25, 0x53, 0x4f, 0xa6, 0xd7, 0xbe, 0xaa, 0x91, 0x1f, 0xcb, 0x3c, - 0x05, 0x37, 0xae, 0xdc, 0xe5, 0xbe, 0xca, 0xa3, 0x2d, 0x92, 0x46, 0x59, - 0x65, 0x55, 0x86, 0x59, 0x4d, 0xd9, 0xa8, 0x42, 0x12, 0x2d, 0x84, 0x88, - 0x72, 0xa1, 0x4c, 0xd8, 0xc4, 0x60, 0x6d, 0x34, 0x51, 0xb7, 0x37, 0xe3, - 0x1b, 0x29, 0x8a, 0x81, 0xd4, 0x4d, 0x79, 0xbf, 0x3c, 0x2a, 0x6f, 0xfd, - 0x5f, 0xce, 0xf5, 0xf1, 0x51, 0x97, 0xcf, 0xf7, 0x00, 0x20, 0x24, 0xd9, - 0x0d, 0x0f, 0x4f, 0xe5, 0x0e, 0xb1, 0x26, 0x5c, 0x10, 0xe9, 0xcd, 0xfa, - 0x31, 0xb8, 0x03, 0xba, 0x7c, 0xb2, 0x35, 0x99, 0xc3, 0x46, 0xbe, 0xf8, - 0xd6, 0xce, 0xc0, 0xd4, 0xde, 0x24, 0x43, 0x81, 0x68, 0xbf, 0x8b, 0xa1, - 0x8c, 0xc3, 0x38, 0x3f, 0xbb, 0xaa, 0x62, 0xa3, 0xb1, 0x2b, 0x8e, 0x2a, - 0x51, 0x6e, 0x77, 0x1d, 0xd6, 0x63, 0xed, 0x3c, 0xa3, 0xdf, 0x38, 0x51, - 0x53, 0x35, 0xfb, 0x8e, 0x1a, 0xcb, 0x4a, 0x90, 0x8c, 0x16, 0xb0, 0x50, - 0x1b, 0x91, 0x27, 0xb2, 0xfa, 0xab, 0xb9, 0x93, 0x86, 0x86, 0x85, 0xa9, - 0x49, 0xa6, 0x09, 0x90, 0xa0, 0xad, 0x8a, 0x56, 0x51, 0x50, 0x15, 0x61, - 0x23, 0xea, 0xc2, 0xa6, 0xa8, 0x67, 0x28, 0x18, 0x12, 0x38, 0x29, 0xcf, - 0xce, 0x75, 0x47, 0xd4, 0x37, 0x06, 0xc4, 0x61, 0x19, 0x4f, 0x57, 0xff, - 0x4f, 0xb0, 0xe3, 0x2d, 0x19, 0x36, 0x69, 0x0b, 0x27, 0x67, 0xaf, 0x6e, - 0x49, 0x3b, 0x1f, 0x17, 0x52, 0x61, 0x7d, 0xe5, 0x0e, 0x21, 0x13, 0x57, - 0x8c, 0x3a, 0x3f, 0xfa, 0x5b, 0x09, 0x24, 0xe5, 0x18, 0x13, 0x7e, 0xcb, - 0xc9, 0xa5, 0x97, 0x6b, 0xc3, 0x8f, 0x2a, 0x37, 0x31, 0xdb, 0xa0, 0x7d, - 0xa6, 0xd6, 0x90, 0x38, 0xff, 0xf1, 0x50, 0x80, 0x36, 0x7f, 0xfc, 0x20, - 0xaa, 0x1a, 0xd6, 0x86, 0x5a, 0x24, 0x8c, 0x84, 0x62, 0x82, 0x2b, 0xf5, - 0xdf, 0x3c, 0xd7, 0xb7, 0x9a, 0xfb, 0x7b, 0x7c, 0x7d, 0xba, 0xde, 0xf7, - 0xc4, 0xe3, 0xaf, 0x79, 0xdf, 0x15, 0x73, 0x1e, 0xde, 0xdc, 0xe4, 0xe8, - 0x47, 0x55, 0x01, 0x1c, 0x3f, 0xef, 0x35, 0x68, 0x18, 0xe2, 0x24, 0x67, - 0x05, 0x03, 0x21, 0x73, 0x16, 0xd3, 0xc0, 0x19, 0x01, 0x81, 0x09, 0x49, - 0x3b, 0xc9, 0x5d, 0x5d, 0xed, 0xa5, 0x6a, 0x9a, 0x08, 0x2f, 0xa8, 0x19, - 0x89, 0xc6, 0xda, 0xc4, 0xec, 0x9b, 0x1e, 0xf3, 0x09, 0x44, 0x04, 0xf9, - 0x06, 0xed, 0x00, 0x47, 0x8d, 0x9e, 0xbc, 0xf3, 0x3b, 0x5f, 0x0c, 0xe4, - 0x89, 0x79, 0x58, 0x98, 0x73, 0xe0, 0xbb, 0x32, 0xb9, 0xf0, 0xd5, 0xa3, - 0xc0, 0xf8, 0xa0, 0x00, 0x6d, 0x07, 0xaa, 0x39, 0x2a, 0xb1, 0xc6, 0x8e, - 0x13, 0x0d, 0xb5, 0xda, 0xfa, 0x88, 0xfa, 0xf3, 0x28, 0x50, 0xea, 0x71, - 0x06, 0x4c, 0x2e, 0xac, 0x8b, 0xc5, 0x80, 0x34, 0xda, 0x53, 0xa2, 0x43, - 0xdb, 0xda, 0x31, 0x3b, 0xeb, 0x1e, 0x26, 0xcf, 0x4d, 0x50, 0xe5, 0x11, - 0x94, 0x95, 0xf3, 0xba, 0x29, 0xb8, 0xcc, 0x4d, 0x5c, 0x8a, 0x48, 0xed, - 0xde, 0x46, 0x1f, 0xd4, 0x7c, 0xe2, 0xb4, 0xb0, 0x38, 0xa5, 0x64, 0x6d, - 0x8d, 0x41, 0xc8, 0xfb, 0x78, 0xd6, 0xa3, 0x28, 0x0a, 0xdd, 0x69, 0xb5, - 0x29, 0x23, 0xc1, 0xc8, 0xc8, 0x68, 0xc5, 0x9d, 0xa1, 0x52, 0x56, 0x6c, - 0x85, 0x22, 0x32, 0xa2, 0xb5, 0x05, 0x53, 0xd8, 0x4a, 0xc1, 0x47, 0x7c, - 0xb5, 0x4a, 0x34, 0x36, 0xc3, 0xa4, 0xe9, 0xc6, 0x6b, 0xea, 0xde, 0xb5, - 0x8d, 0x09, 0x43, 0x56, 0x92, 0x10, 0x42, 0x45, 0x0b, 0x14, 0x1a, 0xdb, - 0xaf, 0x3f, 0x38, 0xf3, 0xce, 0xbb, 0x49, 0xd4, 0xf7, 0x45, 0x6a, 0xf3, - 0x5d, 0x73, 0x59, 0x8f, 0x3f, 0x4c, 0xd7, 0x01, 0xcd, 0x50, 0x02, 0xe5, - 0xde, 0xed, 0xa6, 0x8d, 0x30, 0x2f, 0x68, 0x14, 0x6f, 0x5b, 0x14, 0xf2, - 0x56, 0x55, 0xcc, 0x22, 0xea, 0xf3, 0xf5, 0xfa, 0x37, 0x1d, 0x63, 0x73, - 0xaa, 0xce, 0x5a, 0xe4, 0xbc, 0x0c, 0x44, 0x28, 0x51, 0x44, 0xbb, 0xb7, - 0x4d, 0x26, 0xbb, 0x31, 0x01, 0x92, 0xa8, 0x41, 0x9e, 0x9d, 0x45, 0x49, - 0x25, 0x74, 0x21, 0x24, 0xb5, 0x08, 0xa2, 0x58, 0x0b, 0x46, 0x2b, 0xad, - 0x0e, 0x30, 0xc6, 0xaa, 0x05, 0xe0, 0x4b, 0x9d, 0x00, 0xf9, 0xd0, 0x58, - 0x51, 0x07, 0x7b, 0xe0, 0x58, 0xac, 0x38, 0xec, 0xa8, 0x2b, 0xdd, 0x89, - 0xe9, 0x34, 0x2d, 0x36, 0x32, 0x76, 0x47, 0x3b, 0x15, 0xa2, 0x77, 0xeb, - 0x6d, 0x9b, 0xfa, 0xa9, 0xaa, 0x2a, 0x27, 0xbf, 0x3e, 0xaa, 0x20, 0x73, - 0x2e, 0xa7, 0x55, 0x0a, 0x81, 0x09, 0x20, 0x81, 0x13, 0x15, 0x75, 0x5f, - 0x8d, 0xb2, 0x35, 0x25, 0x75, 0x26, 0xfb, 0x66, 0x21, 0x75, 0x17, 0x50, - 0x60, 0xb5, 0xc6, 0x85, 0x46, 0x5b, 0x8c, 0xee, 0x71, 0x9d, 0x5d, 0xec, - 0x80, 0x96, 0x05, 0x27, 0xa6, 0x70, 0xb2, 0x27, 0xdd, 0x56, 0x87, 0x92, - 0x69, 0xdd, 0x71, 0x9a, 0x75, 0xc5, 0xb8, 0xff, 0xf1, 0x50, 0x80, 0x25, - 0x1f, 0xfc, 0x21, 0x1a, 0xcf, 0xb7, 0xc0, 0xb8, 0x21, 0x00, 0xa9, 0xb1, - 0x41, 0xd5, 0xa4, 0x54, 0x80, 0x92, 0xb5, 0xeb, 0xe0, 0xae, 0x7c, 0xf5, - 0xd7, 0xf5, 0xe9, 0x5f, 0x5e, 0xea, 0xf2, 0xd5, 0xce, 0x6e, 0xf7, 0xaf, - 0x13, 0x8a, 0x92, 0xa6, 0x83, 0x14, 0xf8, 0x3f, 0x56, 0xe7, 0x69, 0xd3, - 0x61, 0x25, 0x52, 0xe2, 0x1d, 0x25, 0x07, 0xcc, 0x40, 0x1d, 0x6c, 0xc6, - 0x72, 0xc7, 0x7a, 0x9f, 0xf3, 0xd9, 0x0b, 0x44, 0xf7, 0xd6, 0xb1, 0xe1, - 0xcd, 0xc7, 0xb5, 0x69, 0x31, 0x10, 0xcc, 0x9a, 0x16, 0xce, 0x73, 0xd8, - 0xbd, 0x00, 0xd2, 0x57, 0x02, 0x98, 0xa0, 0x81, 0x76, 0x39, 0x72, 0x64, - 0x33, 0x95, 0x74, 0x13, 0x79, 0x33, 0x64, 0xd6, 0x86, 0x80, 0xa9, 0x52, - 0x2a, 0xae, 0xf0, 0xa6, 0xaa, 0x76, 0x84, 0xe8, 0x56, 0x74, 0xda, 0xd5, - 0x62, 0x20, 0x56, 0x64, 0x43, 0x3d, 0x35, 0x14, 0xaa, 0x1b, 0x08, 0x0a, - 0x61, 0x7c, 0x9c, 0xf6, 0x24, 0x37, 0xec, 0xb9, 0x10, 0xa5, 0xcb, 0x8e, - 0x0e, 0xdd, 0x2e, 0xe8, 0xaf, 0x24, 0xf6, 0xcc, 0xf5, 0x65, 0x8a, 0xb6, - 0x51, 0x53, 0xaf, 0x66, 0x2f, 0x09, 0x35, 0xd0, 0x68, 0x56, 0xaa, 0x5c, - 0x62, 0x55, 0x7c, 0xce, 0x78, 0x71, 0xda, 0xd9, 0x86, 0xe1, 0x52, 0xac, - 0x50, 0xa0, 0x9f, 0xd1, 0xc7, 0x5f, 0x35, 0x6a, 0x8b, 0xe3, 0x6f, 0x42, - 0x02, 0xb0, 0x5f, 0x19, 0xf7, 0x27, 0xa8, 0x0c, 0xda, 0xa8, 0x63, 0xb5, - 0xda, 0x2f, 0x49, 0x12, 0x0b, 0xf9, 0xec, 0xc4, 0x72, 0x7e, 0xda, 0x2a, - 0x97, 0x98, 0x94, 0x85, 0xf1, 0x10, 0xd7, 0x3d, 0x15, 0xcf, 0xd7, 0xab, - 0xe6, 0x38, 0xd6, 0x2b, 0x57, 0xed, 0x4e, 0xee, 0xb2, 0xf3, 0x52, 0xbb, - 0x8d, 0x60, 0xaa, 0xbe, 0x49, 0x35, 0x5b, 0xc5, 0x77, 0x3f, 0xfe, 0x63, - 0x36, 0xb8, 0xc3, 0x10, 0x5f, 0xb9, 0xd3, 0x65, 0xaf, 0x2a, 0xde, 0x9b, - 0xca, 0x61, 0xda, 0x45, 0x61, 0x26, 0xe9, 0x35, 0xd7, 0x94, 0xbf, 0xce, - 0x7f, 0x37, 0x00, 0x55, 0xe0, 0x70, 0x83, 0xbb, 0xd6, 0xf6, 0x8d, 0xf0, - 0xd8, 0x6e, 0xce, 0xff, 0xf1, 0x50, 0x80, 0x27, 0x7f, 0xfc, 0x21, 0x1a, - 0xcf, 0xf2, 0xe0, 0x10, 0x00, 0x20, 0xa8, 0xb1, 0x41, 0xd5, 0xe9, 0x33, - 0x49, 0x5d, 0x7a, 0xea, 0x79, 0xf1, 0xeb, 0xe3, 0xee, 0xfd, 0x35, 0x59, - 0xf1, 0xdd, 0x3a, 0xe2, 0xa5, 0xbb, 0x5d, 0x6a, 0x65, 0x6a, 0xaa, 0xc1, - 0x14, 0xf8, 0x2a, 0x80, 0x53, 0x14, 0xd2, 0x77, 0xef, 0x2a, 0x5c, 0x59, - 0x50, 0x27, 0xbc, 0x8f, 0x79, 0xde, 0x6a, 0xe1, 0xdc, 0x97, 0x5f, 0x44, - 0x57, 0xf2, 0x53, 0x8b, 0x97, 0x9f, 0x53, 0x86, 0x3d, 0x9b, 0xae, 0xee, - 0x07, 0x35, 0x2a, 0x23, 0x1d, 0xb6, 0x7d, 0x50, 0x2a, 0xfb, 0xf1, 0x8d, - 0xda, 0x0e, 0xd5, 0xf3, 0xa5, 0x90, 0xa4, 0x91, 0x4e, 0x27, 0x39, 0x89, - 0x3d, 0x33, 0xd8, 0xd5, 0xf0, 0xf2, 0x83, 0x90, 0x60, 0x34, 0x88, 0x2f, - 0x37, 0x4f, 0x45, 0x92, 0x8c, 0xcb, 0x87, 0x41, 0xe9, 0x74, 0x99, 0x39, - 0x2f, 0xf7, 0x5a, 0x6f, 0x4e, 0x68, 0x5b, 0x80, 0x9a, 0x72, 0xca, 0xa0, - 0x38, 0xda, 0xcd, 0xc9, 0x8e, 0xb1, 0x75, 0xaa, 0x18, 0xba, 0xe8, 0xc2, - 0x78, 0x0a, 0xf5, 0x50, 0x53, 0x07, 0x44, 0x2d, 0xf1, 0x86, 0xff, 0xbc, - 0xf6, 0x82, 0xa6, 0x2a, 0xf9, 0x7f, 0x69, 0x1c, 0x7c, 0x74, 0x50, 0x2d, - 0x14, 0x64, 0xca, 0x14, 0x0d, 0x1c, 0x73, 0x25, 0x52, 0xf3, 0xed, 0xa6, - 0xcb, 0x77, 0xdf, 0x9d, 0xd0, 0x85, 0x31, 0xac, 0xea, 0x7f, 0xfa, 0xf5, - 0xf3, 0x12, 0xe0, 0x58, 0x44, 0x03, 0x66, 0x1a, 0xf3, 0xbd, 0xf8, 0x24, - 0xda, 0x33, 0x23, 0xaf, 0x83, 0xe9, 0xc0, 0xd8, 0xfb, 0x20, 0xc5, 0x9b, - 0xe3, 0xe2, 0x34, 0xc2, 0x70, 0x3e, 0xa7, 0xf4, 0x99, 0x3d, 0xfa, 0x87, - 0xc9, 0x0b, 0xe0, 0x69, 0x2b, 0xaf, 0x77, 0xb7, 0x3c, 0x7a, 0xf8, 0xf7, - 0xf3, 0xe0, 0xfa, 0xe7, 0x3a, 0xf1, 0xc7, 0x9d, 0xf1, 0x93, 0xc3, 0x5b, - 0x92, 0x9a, 0xdb, 0x41, 0x96, 0xe6, 0xba, 0x9b, 0x70, 0x0a, 0xc7, 0xf8, - 0x78, 0x31, 0x70, 0x55, 0x90, 0x5f, 0x51, 0xd7, 0x64, 0x91, 0xa4, 0x6f, - 0xa1, 0xd6, 0xc3, 0x74, 0x99, 0x41, 0xc8, 0x1b, 0xcb, 0x99, 0xa9, 0xce, - 0x09, 0x90, 0x06, 0x62, 0x06, 0x23, 0x70, 0xa5, 0xa8, 0x4f, 0x06, 0xe7, - 0x67, 0xec, 0x30, 0x70, 0xcd, 0x8e, 0xff, 0xf1, 0x50, 0x80, 0x29, 0x5f, - 0xfc, 0x21, 0x1a, 0xcf, 0x7a, 0x24, 0x12, 0x05, 0x83, 0xa5, 0xb1, 0x32, - 0xd5, 0x28, 0x46, 0x0a, 0x35, 0x46, 0x1a, 0x5f, 0x7f, 0x7d, 0xfd, 0x7e, - 0x7b, 0xf8, 0xfa, 0x9f, 0x95, 0x5f, 0x5e, 0x0d, 0x79, 0x38, 0xce, 0x65, - 0x10, 0x4e, 0x00, 0xb5, 0x68, 0x0f, 0x3a, 0x18, 0xdf, 0xb5, 0x9b, 0xb6, - 0x74, 0xe5, 0xf8, 0x27, 0xdc, 0x9f, 0xed, 0xab, 0xae, 0x1e, 0x7c, 0xd4, - 0x03, 0x5a, 0xba, 0x77, 0xd5, 0x2e, 0xf3, 0x8f, 0xee, 0xaf, 0xc7, 0xca, - 0xa5, 0xa8, 0x27, 0xb2, 0x2f, 0x30, 0x7c, 0x24, 0xb2, 0xf7, 0x17, 0xbf, - 0x54, 0x5f, 0x3a, 0x38, 0xc8, 0x3b, 0xdb, 0xb1, 0x2d, 0xb5, 0x77, 0x2f, - 0x31, 0x59, 0x48, 0xd7, 0xa8, 0x8d, 0x9e, 0x66, 0x89, 0x72, 0x72, 0x58, - 0x1a, 0x0c, 0x9c, 0x21, 0xc0, 0xef, 0x34, 0xad, 0x9f, 0x83, 0x8f, 0x73, - 0xce, 0xff, 0x9d, 0xa1, 0xd8, 0x36, 0x05, 0xe5, 0x6b, 0xfe, 0x96, 0x56, - 0xba, 0x37, 0x5d, 0x53, 0x4c, 0xeb, 0x6a, 0xc6, 0xf3, 0xc5, 0x70, 0x06, - 0x26, 0xa2, 0xcc, 0xe1, 0x60, 0xce, 0x9a, 0xcb, 0x7c, 0xb3, 0x17, 0xc4, - 0x5c, 0xce, 0xb3, 0x55, 0x18, 0xbe, 0xf5, 0xfb, 0x36, 0x56, 0xd3, 0x26, - 0x57, 0x3f, 0x11, 0xab, 0x27, 0x5f, 0x57, 0xb6, 0x9d, 0x11, 0x46, 0x4e, - 0xcd, 0xab, 0x3d, 0xb8, 0xf2, 0x90, 0x54, 0x49, 0x84, 0xf6, 0xe4, 0x73, - 0xbf, 0xf5, 0x53, 0x7a, 0xf8, 0xd5, 0x6f, 0x65, 0xdf, 0x55, 0x9a, 0x6d, - 0x8c, 0x78, 0x82, 0x40, 0x56, 0xdf, 0x18, 0xf7, 0x75, 0x2e, 0x27, 0xa0, - 0x93, 0xe3, 0xed, 0x42, 0x17, 0xf1, 0xf0, 0xbb, 0xea, 0xf1, 0xd2, 0xf4, - 0xed, 0xf5, 0xdb, 0x72, 0x31, 0x27, 0x3e, 0xea, 0x57, 0x91, 0xa7, 0xe0, - 0x1a, 0x5f, 0x7d, 0x5f, 0x77, 0xcf, 0xc7, 0x8d, 0x5f, 0x79, 0xc4, 0xce, - 0x3b, 0xeb, 0xeb, 0xde, 0xf2, 0xfc, 0x25, 0x12, 0xea, 0xea, 0xc2, 0xb9, - 0x0c, 0xe7, 0x2f, 0x2f, 0x5c, 0x08, 0xe8, 0xeb, 0x5b, 0x2e, 0x15, 0xa9, - 0xa1, 0x4d, 0x6a, 0xe4, 0xe7, 0xdc, 0xc4, 0x20, 0xdf, 0xa8, 0x8d, 0xe5, - 0x63, 0xa8, 0x65, 0x06, 0xf8, 0x7e, 0x6e, 0x1e, 0x9c, 0xf7, 0xd7, 0xf3, - 0xf6, 0xfa, 0x36, 0x1d, 0x39, 0xe5, 0x1a, 0x2f, 0x28, 0xae, 0x1d, 0xa7, - 0x16, 0xa7, 0xf3, 0x9f, 0x43, 0xbe, 0xf7, 0x9c, 0x97, 0x26, 0x1a, 0x4e, - 0xff, 0xf1, 0x50, 0x80, 0x3e, 0x3f, 0xfc, 0x20, 0xa6, 0x2a, 0xd6, 0x36, - 0x92, 0x4d, 0x86, 0x35, 0x77, 0x7e, 0xff, 0x52, 0x6f, 0xbf, 0x6f, 0x6e, - 0xbf, 0x88, 0xbf, 0xaf, 0xcd, 0x2f, 0x8d, 0x4c, 0x65, 0xe5, 0xeb, 0x73, - 0x2b, 0xa9, 0xbe, 0x84, 0x1e, 0x7c, 0x11, 0x01, 0x0a, 0x99, 0x7c, 0x28, - 0x7d, 0x77, 0xf8, 0xf7, 0x69, 0xe3, 0x5f, 0x7d, 0xdf, 0xe7, 0x8c, 0xbb, - 0x49, 0x4e, 0xca, 0x8a, 0x32, 0x92, 0x90, 0xee, 0xa8, 0x43, 0x10, 0x0a, - 0xe7, 0xd4, 0xda, 0xf9, 0x72, 0x22, 0xe0, 0xa1, 0xcc, 0x04, 0xdd, 0x04, - 0x70, 0x44, 0x04, 0xa0, 0x0e, 0xa2, 0x3a, 0x03, 0xef, 0x5f, 0x16, 0x7f, - 0xbc, 0xab, 0x73, 0x87, 0x63, 0x5e, 0xa6, 0xb3, 0x54, 0x3f, 0x5b, 0x65, - 0xd5, 0x8a, 0xa9, 0xd5, 0x9b, 0xe3, 0x0a, 0x53, 0x75, 0xb8, 0xef, 0x5f, - 0xe8, 0x9f, 0x0e, 0xe3, 0x40, 0xb5, 0xb9, 0x6d, 0x55, 0xd1, 0xb2, 0x00, - 0xb3, 0x7d, 0x97, 0x24, 0x16, 0x85, 0x8f, 0x31, 0x00, 0x16, 0x62, 0xb8, - 0xf8, 0xaa, 0x04, 0x8a, 0xd5, 0x4a, 0x0d, 0x49, 0x69, 0x71, 0xa0, 0x27, - 0x6c, 0xe2, 0x63, 0xdb, 0x19, 0x4b, 0x9f, 0x55, 0xbe, 0x66, 0xd1, 0x00, - 0x55, 0x39, 0xb8, 0xb4, 0x19, 0xb2, 0x8f, 0x6d, 0xe4, 0xbb, 0x2f, 0x7f, - 0xfb, 0x83, 0xa0, 0x56, 0xc2, 0x70, 0x99, 0xb4, 0xa2, 0x7a, 0xdd, 0xf6, - 0x81, 0xf4, 0x84, 0xa1, 0x13, 0xdf, 0x5e, 0xbe, 0x9f, 0xbc, 0x17, 0x08, - 0x5b, 0xb5, 0x5e, 0x50, 0xeb, 0x82, 0x69, 0x1f, 0x49, 0xa1, 0xf5, 0x20, - 0x4f, 0x42, 0x02, 0x69, 0xe4, 0xad, 0x50, 0x0f, 0x7a, 0xc8, 0x42, 0x42, - 0x68, 0xf1, 0x75, 0x86, 0x2e, 0xa8, 0xd0, 0x66, 0x80, 0x52, 0x01, 0x5f, - 0x09, 0x26, 0x17, 0x4a, 0xe4, 0x0a, 0xbb, 0xf9, 0xd2, 0x8d, 0x6b, 0x14, - 0x21, 0xce, 0xab, 0x67, 0x86, 0xa6, 0xbd, 0x7b, 0x56, 0x71, 0xe3, 0xeb, - 0xdf, 0xac, 0x6e, 0xf5, 0x8a, 0xd7, 0x1c, 0xfc, 0x75, 0x79, 0x7b, 0x73, - 0xa3, 0x35, 0x79, 0x03, 0x1d, 0xb8, 0x22, 0x04, 0x1e, 0xcd, 0xd0, 0xe0, - 0x0e, 0x9e, 0x26, 0x17, 0xa3, 0xb3, 0x0d, 0x8f, 0xb1, 0x20, 0x62, 0x8e, - 0x9e, 0x37, 0x07, 0x62, 0xb4, 0x38, 0xd8, 0x73, 0x4b, 0x0d, 0xd2, 0x76, - 0xad, 0x9a, 0xa5, 0xfa, 0x7a, 0x39, 0xc1, 0xdb, 0x0b, 0xff, 0x19, 0x3b, - 0x47, 0x8c, 0xbb, 0xfa, 0x61, 0xcf, 0xe1, 0x24, 0x96, 0x5f, 0xd3, 0x8a, - 0x7c, 0xa5, 0x75, 0x07, 0xdf, 0xc8, 0xda, 0x98, 0x19, 0x37, 0xd6, 0x2e, - 0x69, 0x91, 0x4c, 0x90, 0x56, 0xb3, 0x25, 0xb1, 0x46, 0x7d, 0x00, 0x4d, - 0x63, 0xa9, 0x2d, 0x8d, 0x3d, 0x93, 0xd7, 0x26, 0x10, 0xcf, 0x54, 0xc3, - 0x56, 0x16, 0xc2, 0x4a, 0xe5, 0x2e, 0xf7, 0xc2, 0x04, 0x10, 0x02, 0xd4, - 0x10, 0x76, 0x08, 0xac, 0x9d, 0x32, 0x16, 0x4f, 0x02, 0x69, 0x96, 0xc0, - 0x42, 0x3b, 0x82, 0x8b, 0xec, 0x53, 0xcc, 0x5a, 0xd0, 0xa8, 0xa9, 0x5b, - 0x06, 0x53, 0x5c, 0x81, 0xa8, 0x25, 0x54, 0x27, 0x46, 0x0a, 0x45, 0x0c, - 0xc6, 0xa5, 0x42, 0x81, 0x14, 0x0c, 0x5e, 0xb5, 0xb9, 0x65, 0xb6, 0x04, - 0x06, 0xba, 0x0d, 0x56, 0x1a, 0x09, 0x9d, 0x28, 0xa8, 0x06, 0x42, 0x08, - 0x61, 0x91, 0xc5, 0x43, 0x82, 0x08, 0x10, 0x12, 0x89, 0x85, 0x26, 0x05, - 0x13, 0xcc, 0xa3, 0x71, 0x92, 0xb6, 0x21, 0x40, 0xb8, 0xd6, 0xa0, 0x01, - 0x35, 0x2b, 0x44, 0x15, 0x41, 0x5a, 0xd1, 0x7a, 0x44, 0xf2, 0xd9, 0x52, - 0x14, 0x15, 0x4f, 0x4c, 0xce, 0x4a, 0x10, 0xae, 0xf5, 0x88, 0xcd, 0x84, - 0x54, 0x24, 0x40, 0x69, 0x87, 0xff, 0xf1, 0x50, 0x80, 0x38, 0x5f, 0xfc, - 0x20, 0x97, 0x4c, 0xd3, 0x65, 0x98, 0xf3, 0x69, 0x29, 0x45, 0x36, 0x59, - 0xd7, 0x16, 0x6c, 0xc3, 0x6e, 0x40, 0x04, 0xa0, 0x7c, 0x66, 0x1e, 0x7e, - 0xc1, 0x7c, 0x42, 0xff, 0x7f, 0xfb, 0x01, 0x2e, 0xae, 0xd0, 0x3b, 0xed, - 0xe3, 0x80, 0x42, 0x31, 0x14, 0xf7, 0x59, 0x2f, 0x13, 0x9b, 0xe3, 0xbc, - 0x30, 0x7e, 0x7e, 0xb4, 0x78, 0x22, 0x63, 0x0c, 0x61, 0xa1, 0xf3, 0xfb, - 0x1f, 0x9d, 0xd9, 0x77, 0xe7, 0x3b, 0x17, 0x15, 0xa2, 0x93, 0x28, 0x93, - 0x72, 0x16, 0x91, 0x1c, 0xc1, 0xa2, 0x13, 0xaa, 0xc0, 0x10, 0x18, 0xf9, - 0x10, 0x79, 0x72, 0x64, 0x31, 0xf4, 0xa8, 0xda, 0x5b, 0x50, 0x39, 0x8c, - 0x84, 0xc8, 0x03, 0x14, 0x0a, 0x08, 0xcb, 0xd7, 0x9a, 0x34, 0x1d, 0x66, - 0xed, 0x4b, 0xfd, 0xa6, 0x1a, 0x9a, 0x34, 0xac, 0x59, 0xc6, 0x56, 0x47, - 0xdd, 0xdf, 0x98, 0x0f, 0x47, 0x2e, 0x96, 0xb5, 0xad, 0x13, 0x2b, 0x6d, - 0xa8, 0xde, 0xc2, 0xd6, 0x8d, 0x39, 0x46, 0x6a, 0x10, 0xf6, 0x6d, 0x9f, - 0x71, 0x74, 0x10, 0x2d, 0xa3, 0x01, 0x43, 0x4d, 0xa2, 0xe3, 0x7a, 0xa5, - 0x76, 0xd5, 0xb6, 0x0e, 0x7a, 0xe8, 0x07, 0xb9, 0xe9, 0xdd, 0x6e, 0x43, - 0x3e, 0xf3, 0xc7, 0x46, 0x7c, 0x87, 0x7c, 0xff, 0x4b, 0xb9, 0x68, 0x61, - 0x92, 0xc1, 0x1d, 0x31, 0x0b, 0x63, 0xd4, 0x28, 0x19, 0xa5, 0xe8, 0x94, - 0x55, 0x4f, 0x5b, 0xef, 0x20, 0xaf, 0xc9, 0x33, 0x69, 0xfd, 0x7c, 0x2d, - 0xa2, 0x02, 0x30, 0xed, 0x50, 0x21, 0x00, 0xae, 0x40, 0xa4, 0xe2, 0x12, - 0x6b, 0xbc, 0x99, 0xc5, 0x86, 0x40, 0x25, 0x4f, 0x99, 0x08, 0x58, 0x11, - 0x04, 0xc2, 0xb8, 0xed, 0x16, 0x03, 0xa9, 0x82, 0x11, 0x67, 0x91, 0xfe, - 0xfe, 0x6b, 0x4d, 0x06, 0xd1, 0xd6, 0x26, 0xa9, 0x58, 0x35, 0xa8, 0x6b, - 0x54, 0x99, 0x35, 0x1f, 0xe7, 0xfe, 0xdf, 0x7d, 0xe7, 0x5e, 0x3f, 0x1e, - 0xbd, 0xbe, 0xd2, 0xa7, 0xb7, 0xa9, 0x5e, 0xda, 0x92, 0x64, 0xa9, 0xb9, - 0x6c, 0x99, 0xc6, 0x74, 0x07, 0x31, 0xf5, 0x19, 0x69, 0x05, 0xb7, 0x18, - 0x63, 0xd9, 0xf6, 0xd7, 0x6e, 0x21, 0x05, 0x49, 0xd5, 0x76, 0xc8, 0xf9, - 0x15, 0x87, 0x3e, 0xaa, 0xc3, 0x98, 0xd8, 0xc8, 0xd0, 0xa5, 0xd6, 0x2d, - 0xbe, 0x95, 0xdc, 0x17, 0x20, 0xac, 0x59, 0x62, 0x85, 0x8f, 0xda, 0x51, - 0x18, 0x80, 0xe2, 0x3d, 0x3e, 0x2a, 0x2d, 0x0c, 0x0b, 0xd4, 0xe5, 0x2b, - 0xd4, 0x2c, 0x8d, 0x25, 0x72, 0x25, 0xa3, 0x80, 0x71, 0x14, 0x83, 0xb5, - 0x0f, 0x3e, 0x22, 0x20, 0xde, 0x4b, 0xcc, 0x8c, 0xc8, 0xc4, 0x66, 0x45, - 0x25, 0x73, 0x05, 0x88, 0xaf, 0x12, 0x25, 0xa5, 0x98, 0xfa, 0x96, 0x3d, - 0xaa, 0x5a, 0x5f, 0x90, 0x1f, 0x75, 0xa3, 0xc9, 0xb0, 0x34, 0x83, 0xc4, - 0x37, 0x47, 0x0b, 0x11, 0xa4, 0x1f, 0x53, 0x67, 0x01, 0x9a, 0xbd, 0xd9, - 0xa3, 0xa8, 0x98, 0x8a, 0xf3, 0x22, 0x5a, 0x88, 0x46, 0x2c, 0x0c, 0xc7, - 0x03, 0xe2, 0xe6, 0xae, 0x46, 0x6c, 0xc8, 0x36, 0x45, 0x26, 0x6f, 0x02, - 0xa0, 0xae, 0x44, 0x22, 0x5a, 0x34, 0x98, 0x49, 0x5c, 0x89, 0x69, 0x48, - 0xb6, 0xa8, 0x07, 0xfd, 0xe0, 0x38, 0x1d, 0x21, 0x80, 0xf7, 0x7e, 0xff, - 0xf1, 0x50, 0x80, 0x39, 0x9f, 0xfc, 0x20, 0xab, 0x7a, 0xd6, 0x26, 0x72, - 0x21, 0x82, 0x92, 0x2f, 0x5a, 0xb6, 0xff, 0x7e, 0xef, 0x7e, 0x3d, 0x9d, - 0x7c, 0xf5, 0xba, 0xfa, 0xf7, 0xbe, 0xff, 0x1b, 0xd4, 0xe7, 0x36, 0xd5, - 0xd5, 0x72, 0xd4, 0x8d, 0x01, 0xe5, 0x00, 0xf5, 0x66, 0xdd, 0xd5, 0x87, - 0x92, 0xaa, 0xe4, 0xa1, 0x99, 0xb9, 0xd7, 0x87, 0x7a, 0xe5, 0x25, 0x65, - 0x53, 0x05, 0x74, 0x76, 0x19, 0xa2, 0x27, 0x14, 0x4a, 0xea, 0x04, 0x1e, - 0xee, 0x15, 0x7d, 0x5e, 0xd4, 0xe8, 0xaf, 0xe9, 0x5b, 0x04, 0xab, 0x30, - 0xcf, 0xe5, 0xb5, 0xf1, 0x3b, 0xc2, 0x14, 0x8d, 0xff, 0xca, 0x98, 0x2b, - 0x13, 0x6f, 0x92, 0x54, 0x60, 0x7b, 0x16, 0xa1, 0xe1, 0xe9, 0x31, 0x7f, - 0xeb, 0x82, 0x15, 0xb9, 0xfa, 0xc5, 0xec, 0x8c, 0xff, 0x42, 0xed, 0xf4, - 0xe2, 0x40, 0x2e, 0xcf, 0xa3, 0x95, 0x2f, 0xe2, 0x5a, 0x8d, 0xd9, 0xd3, - 0x2d, 0xc0, 0xe1, 0xe8, 0x92, 0x20, 0x39, 0x82, 0xc9, 0x49, 0x26, 0x32, - 0xdd, 0x30, 0xa8, 0xed, 0xbb, 0x4a, 0xb5, 0x20, 0x2a, 0x74, 0xe2, 0xa4, - 0x6d, 0x2d, 0x5c, 0x48, 0xe7, 0x66, 0xab, 0x30, 0x10, 0x74, 0x0e, 0x8e, - 0xc0, 0x55, 0x98, 0x92, 0x51, 0xa0, 0x99, 0x75, 0x33, 0x01, 0x24, 0x8d, - 0xdd, 0x79, 0x39, 0x1a, 0x70, 0x41, 0x33, 0x39, 0x37, 0x1b, 0xd8, 0xbb, - 0xab, 0x10, 0x22, 0xa6, 0x46, 0x10, 0xcf, 0x71, 0x36, 0x51, 0xd3, 0x0b, - 0xe4, 0x6d, 0xc7, 0x74, 0x2a, 0x4c, 0x74, 0xad, 0x1e, 0x02, 0x2d, 0x07, - 0x2d, 0xd9, 0xab, 0xd8, 0x1e, 0x9d, 0x0f, 0x1b, 0xae, 0xfc, 0x56, 0xa0, - 0x6b, 0x58, 0x98, 0xe9, 0x06, 0x2a, 0x34, 0x6a, 0xf8, 0xe3, 0xf4, 0xf8, - 0xdd, 0x4d, 0xf1, 0x9e, 0xde, 0xaa, 0xde, 0x7c, 0x49, 0xed, 0xc4, 0xd6, - 0xef, 0x9b, 0xce, 0x68, 0xba, 0x2a, 0xc4, 0xe6, 0xb4, 0x24, 0xc8, 0x2e, - 0xb5, 0xeb, 0x99, 0x35, 0xdb, 0x5c, 0x4f, 0x8c, 0xdc, 0xf6, 0x5d, 0x47, - 0x0d, 0x6e, 0xec, 0xfa, 0x9f, 0xed, 0x06, 0x2d, 0xeb, 0x04, 0x7a, 0xff, - 0x87, 0xd3, 0x79, 0xbe, 0xbf, 0x65, 0x08, 0xfd, 0x5e, 0x62, 0xc6, 0x65, - 0xc1, 0x17, 0xee, 0x72, 0x1e, 0xe0, 0x8f, 0x6d, 0x80, 0xef, 0x55, 0xcf, - 0x5a, 0x66, 0x9e, 0x83, 0x6f, 0xaa, 0xd6, 0xf1, 0x5b, 0x67, 0x42, 0xd1, - 0x74, 0x1c, 0x84, 0x0e, 0x87, 0x6e, 0x9e, 0x73, 0x4e, 0x8e, 0x56, 0xbb, - 0x70, 0x43, 0x06, 0xe9, 0x25, 0x1b, 0xae, 0x96, 0x0f, 0xb2, 0x44, 0x53, - 0x11, 0xb0, 0x1a, 0x89, 0xa5, 0xba, 0x52, 0xb6, 0xbb, 0xa4, 0x71, 0x83, - 0x8b, 0x53, 0xab, 0x86, 0xe0, 0xda, 0x6f, 0xdb, 0xbf, 0x6b, 0x36, 0xed, - 0x3d, 0xa2, 0x66, 0xfe, 0x37, 0x77, 0xa4, 0x03, 0xb2, 0x09, 0x0f, 0x33, - 0xcd, 0x46, 0x63, 0xf8, 0x79, 0xb0, 0xbe, 0xae, 0x92, 0x7a, 0xfd, 0xa3, - 0x06, 0x04, 0x11, 0x30, 0x46, 0xc8, 0xcf, 0xa6, 0x63, 0x03, 0x1b, 0xbb, - 0xcf, 0x6e, 0x67, 0x98, 0x46, 0x96, 0x9c, 0x7e, 0x2a, 0xfc, 0xce, 0x96, - 0xf9, 0x88, 0x04, 0x23, 0x2a, 0x5e, 0x3f, 0x6d, 0x62, 0x0e, 0x99, 0x34, - 0x74, 0xc9, 0x6b, 0xe5, 0x7b, 0x79, 0x73, 0x71, 0x8d, 0x6a, 0x42, 0xec, - 0x3f, 0x01, 0x53, 0x78, 0x86, 0xdc, 0x36, 0x5b, 0x29, 0xf7, 0x4b, 0x0b, - 0xae, 0xe3, 0x78, 0xff, 0xf1, 0x50, 0x80, 0x26, 0x9f, 0xfc, 0x21, 0x1a, - 0xcf, 0x70, 0x18, 0x2a, 0x00, 0x5c, 0xa0, 0xb4, 0xb3, 0x12, 0x02, 0x34, - 0x18, 0x1e, 0xde, 0xbf, 0xf2, 0xfe, 0xff, 0x1e, 0xbc, 0x5e, 0xb5, 0xeb, - 0x57, 0x5e, 0xdf, 0xa7, 0x9d, 0xde, 0xaf, 0x7b, 0x73, 0x72, 0xea, 0x77, - 0x25, 0x7b, 0x2c, 0x6e, 0x39, 0x68, 0x35, 0x85, 0x6e, 0x12, 0x1c, 0x8c, - 0xcd, 0x99, 0x6b, 0x68, 0x47, 0x48, 0xae, 0x2a, 0x2d, 0xb5, 0xd7, 0x99, - 0x1b, 0x45, 0xb9, 0x2f, 0xa4, 0xc3, 0x3a, 0x3c, 0xaf, 0xca, 0x10, 0xa5, - 0x33, 0x44, 0x59, 0x12, 0x14, 0x1a, 0xa0, 0x2b, 0x10, 0x56, 0xc7, 0x4a, - 0xac, 0x61, 0x5c, 0x45, 0x49, 0x67, 0x9f, 0x0d, 0x54, 0xca, 0xeb, 0xe3, - 0x96, 0x7d, 0x87, 0xb6, 0x63, 0x13, 0xf7, 0xcd, 0xbf, 0xb8, 0x03, 0xdf, - 0x5e, 0xa6, 0x6c, 0x55, 0xa0, 0xf5, 0xea, 0x5d, 0xac, 0x77, 0xfb, 0xfe, - 0x8f, 0x36, 0x26, 0x11, 0xbd, 0xb1, 0x29, 0x7d, 0xec, 0x3a, 0xd6, 0xcc, - 0x0f, 0x11, 0x6c, 0xd9, 0x9e, 0xa8, 0x7e, 0x85, 0x4a, 0x96, 0x51, 0x61, - 0xee, 0x3b, 0xb9, 0xf5, 0x27, 0xa9, 0x9c, 0x18, 0xa4, 0x77, 0xc8, 0x9a, - 0xac, 0x22, 0x7e, 0x07, 0x71, 0xd8, 0xa7, 0x88, 0xce, 0x20, 0xf7, 0x29, - 0xf9, 0x53, 0x65, 0x89, 0x32, 0x45, 0x14, 0x49, 0x25, 0x27, 0x82, 0xae, - 0x0b, 0xc7, 0x18, 0x6e, 0x01, 0xd6, 0x53, 0x49, 0xe7, 0x0c, 0x30, 0x84, - 0xa1, 0xcf, 0xfe, 0xc8, 0xac, 0xdf, 0x8e, 0xac, 0x36, 0x2b, 0x2b, 0xb8, - 0xdb, 0x64, 0xd2, 0xec, 0x7b, 0xfe, 0x0b, 0x8c, 0x7e, 0x46, 0x01, 0xfd, - 0xeb, 0x5a, 0x7d, 0x0b, 0x41, 0x42, 0x58, 0x09, 0x90, 0x5f, 0x11, 0xe3, - 0xcb, 0xdb, 0xfb, 0x7b, 0x54, 0xae, 0x7a, 0xf8, 0xd7, 0xe8, 0x86, 0xab, - 0x8f, 0x6c, 0x66, 0x45, 0x5e, 0xaa, 0xf9, 0x84, 0x94, 0x03, 0xaf, 0xf8, - 0x51, 0xbe, 0xdb, 0xd7, 0x3d, 0x17, 0x6b, 0xfc, 0x7a, 0x1c, 0xe0, 0xe6, - 0x37, 0x1a, 0x7d, 0xf2, 0x77, 0x9b, 0xed, 0x98, 0x8f, 0x6c, 0xba, 0xe0, - 0xd4, 0x98, 0x62, 0x59, 0xed, 0x67, 0x18, 0xd5, 0xa6, 0xa8, 0x98, 0xd7, - 0x4d, 0xbb, 0x33, 0xa9, 0x6a, 0xc5, 0x0d, 0x0d, 0x47, 0x30, 0x9e, 0xff, - 0xf1, 0x50, 0x80, 0x29, 0xbf, 0xfc, 0x21, 0x1a, 0xcf, 0x30, 0xdc, 0xf3, - 0x10, 0x21, 0xa2, 0xb1, 0x34, 0x50, 0xe4, 0xc4, 0x23, 0x04, 0x53, 0x8f, - 0xaf, 0xdb, 0xeb, 0x9f, 0x6f, 0xcb, 0xe3, 0xde, 0x57, 0xeb, 0xbc, 0xce, - 0xfc, 0xcc, 0x9c, 0xf1, 0xeb, 0xad, 0xde, 0x4d, 0xf5, 0x33, 0xe3, 0xd7, - 0xb7, 0x3f, 0xcb, 0x4c, 0x33, 0xf9, 0xc4, 0x18, 0x6f, 0x9e, 0x00, 0x7a, - 0x9d, 0x6b, 0x26, 0x55, 0x4c, 0xb8, 0x9f, 0x0a, 0x83, 0x9d, 0xc5, 0x4c, - 0xfe, 0xdf, 0x25, 0xa7, 0xe1, 0x20, 0x71, 0xde, 0xfc, 0x77, 0x80, 0x73, - 0x5d, 0x41, 0xe9, 0x4b, 0x1a, 0xe2, 0x07, 0x68, 0x95, 0x06, 0x03, 0x58, - 0x85, 0x33, 0x0d, 0x7a, 0x60, 0x6a, 0xa2, 0xd1, 0x9c, 0x59, 0xd3, 0x43, - 0x8d, 0x79, 0xce, 0xd1, 0xe8, 0xba, 0x4e, 0xc8, 0x4f, 0x73, 0xd8, 0x75, - 0x47, 0x4c, 0x06, 0xc8, 0x2c, 0x93, 0x1e, 0xc1, 0xb3, 0xaa, 0xce, 0x7f, - 0xf5, 0x6e, 0x7d, 0x0c, 0xd0, 0xe3, 0x59, 0xbd, 0xfe, 0x14, 0x5b, 0xbf, - 0xce, 0xe5, 0xdd, 0x97, 0xef, 0xcb, 0x39, 0x25, 0x6f, 0xd9, 0xf9, 0xd1, - 0xf9, 0x21, 0x4a, 0x66, 0x76, 0x33, 0x6e, 0x43, 0xa7, 0x1d, 0xc6, 0x58, - 0x01, 0xaa, 0xf3, 0x9e, 0x0e, 0x6a, 0x39, 0xa0, 0x09, 0xd0, 0xf4, 0xdf, - 0x99, 0xb3, 0x1c, 0x4a, 0x67, 0xfe, 0x23, 0x19, 0x39, 0x9b, 0x89, 0x65, - 0x4c, 0x28, 0x97, 0xe5, 0xe6, 0x43, 0xcd, 0xf0, 0x88, 0x26, 0x0d, 0xa0, - 0x37, 0xb9, 0xe2, 0xbf, 0xd4, 0x83, 0x75, 0x96, 0xb9, 0x21, 0x3a, 0xea, - 0xc2, 0xc0, 0x4f, 0xf0, 0x3f, 0xfe, 0xfc, 0xc8, 0x68, 0x5e, 0xed, 0x76, - 0xbc, 0x7b, 0x57, 0xde, 0xe9, 0xe6, 0x05, 0x0e, 0x3d, 0xac, 0x88, 0x90, - 0x0a, 0x98, 0x48, 0x08, 0x95, 0xfe, 0xa8, 0xa6, 0xa2, 0x36, 0x08, 0x8b, - 0xe0, 0x0d, 0x7d, 0x7e, 0x92, 0x81, 0x29, 0x7d, 0x67, 0x7e, 0x79, 0xfa, - 0xef, 0x9a, 0x4a, 0x95, 0x32, 0xe6, 0xf5, 0x95, 0x20, 0xd7, 0xfa, 0x0f, - 0x49, 0xd6, 0x91, 0xdd, 0x38, 0xf9, 0x93, 0xbf, 0xb5, 0xfe, 0xc1, 0xbb, - 0x66, 0xfd, 0x24, 0x6f, 0xcc, 0xb9, 0x57, 0x45, 0x06, 0x91, 0x40, 0x37, - 0xc1, 0xaa, 0x29, 0x4d, 0xf2, 0xc3, 0x42, 0x6a, 0xb6, 0xdc, 0xd6, 0xc8, - 0x03, 0x3f, 0xfa, 0x79, 0xf6, 0x67, 0x6c, 0x27, 0xb1, 0xa4, 0xa6, 0x87, - 0x2e, 0x79, 0x35, 0xed, 0xe3, 0x2c, 0x64, 0x87, 0xff, 0xf1, 0x50, 0x80, - 0x26, 0xff, 0xfc, 0x21, 0x2a, 0xcf, 0xc1, 0xd8, 0xff, 0x2c, 0x00, 0xa9, - 0xa1, 0x34, 0x91, 0x04, 0x34, 0x43, 0x10, 0xb9, 0x3c, 0xf5, 0xf9, 0xff, - 0x4f, 0x53, 0xd7, 0x3f, 0x7f, 0x7a, 0xf8, 0xd7, 0xb7, 0xbf, 0x3d, 0xf1, - 0x9f, 0x15, 0xcc, 0xf5, 0x75, 0xd4, 0xc5, 0xeb, 0x2f, 0x84, 0xe0, 0x6e, - 0xfe, 0xd3, 0x03, 0xbc, 0x55, 0x2a, 0xd3, 0x21, 0x59, 0x2e, 0xad, 0xb6, - 0xaf, 0xd7, 0xf3, 0xdd, 0x38, 0xa9, 0xd6, 0xd1, 0x93, 0xb0, 0x4c, 0x77, - 0x49, 0x8a, 0x83, 0x12, 0xa0, 0xa0, 0xd3, 0x85, 0x67, 0x5d, 0xf7, 0x55, - 0x05, 0x87, 0x77, 0x1b, 0xa5, 0xf8, 0x0d, 0xab, 0x34, 0x55, 0x07, 0x54, - 0x4a, 0x39, 0x6b, 0x12, 0x4a, 0xd1, 0xe1, 0x52, 0x05, 0xd0, 0x5e, 0x65, - 0x78, 0x9f, 0xe8, 0x83, 0x53, 0x0d, 0xaa, 0x69, 0x61, 0xbc, 0x13, 0x2f, - 0x4d, 0x12, 0x54, 0x5c, 0x28, 0xa1, 0xb5, 0x49, 0x4d, 0x30, 0x49, 0xf7, - 0x3e, 0xd3, 0x81, 0x0d, 0x52, 0x27, 0x49, 0x34, 0xfa, 0xd8, 0x9c, 0xa0, - 0xaa, 0xe1, 0x16, 0x79, 0xbe, 0xd8, 0x75, 0x17, 0x7a, 0xaa, 0x39, 0x49, - 0x15, 0x56, 0x99, 0x2b, 0x41, 0xac, 0xed, 0x72, 0xa4, 0x8a, 0xca, 0xb7, - 0x76, 0x71, 0xac, 0x2d, 0xea, 0xee, 0x41, 0x66, 0xa9, 0x40, 0xb3, 0xd4, - 0x2d, 0xa1, 0xae, 0x62, 0x1b, 0x70, 0xeb, 0xd4, 0x49, 0xd3, 0x67, 0x95, - 0x6e, 0x68, 0x08, 0x54, 0x91, 0x51, 0x96, 0x6a, 0xab, 0xc2, 0x69, 0xc8, - 0x4e, 0x47, 0x10, 0x03, 0xa0, 0x4d, 0x0a, 0xf4, 0x4a, 0xea, 0x64, 0xbd, - 0x2d, 0x5e, 0xe6, 0xa5, 0x05, 0x89, 0xab, 0x55, 0x3a, 0x9e, 0x12, 0x08, - 0x19, 0x1c, 0x18, 0xb7, 0x7d, 0x43, 0xf2, 0x81, 0x52, 0xb4, 0x13, 0xfc, - 0x0b, 0x92, 0xde, 0x3a, 0xaf, 0xbf, 0xae, 0x6d, 0x59, 0xe6, 0xf8, 0xe7, - 0xbe, 0x33, 0xe2, 0xb9, 0xcd, 0x6e, 0x26, 0x5e, 0xee, 0x57, 0x1b, 0xa8, - 0x2f, 0xb7, 0xb3, 0x92, 0x58, 0x37, 0xbe, 0x9e, 0xbe, 0xbf, 0xe1, 0x77, - 0xcc, 0x47, 0xa8, 0xdc, 0x17, 0x29, 0xe4, 0x20, 0x15, 0xc7, 0x07, 0x19, - 0x5d, 0xb4, 0xee, 0x94, 0x6b, 0x71, 0x79, 0x9f, 0x8c, 0x0e, 0xe5, 0xc2, - 0xcb, 0x5d, 0x77, 0x3c, 0x70, 0x01, 0xc0, 0xff, 0xf1, 0x50, 0x80, 0x3e, - 0x9f, 0xfc, 0x20, 0x9a, 0x4c, 0xd9, 0x65, 0x91, 0xf5, 0x65, 0x92, 0x75, - 0x66, 0x3c, 0xb3, 0x5c, 0xb3, 0x00, 0x01, 0xc4, 0xe7, 0xea, 0xea, 0xaa, - 0x7d, 0x97, 0xfc, 0xe7, 0xe7, 0xad, 0x7b, 0x71, 0xbe, 0x3f, 0xf0, 0x25, - 0x79, 0xac, 0xce, 0x3f, 0xc0, 0xa2, 0x4a, 0xd3, 0x21, 0xa7, 0xa6, 0x1c, - 0xb2, 0xf7, 0x48, 0xd4, 0x21, 0x08, 0x89, 0x2b, 0x03, 0x2e, 0xb6, 0xd9, - 0xd0, 0x30, 0x7d, 0x7e, 0xa8, 0xcf, 0xbd, 0xf1, 0x62, 0xe9, 0xf7, 0xd3, - 0xde, 0x0a, 0x9d, 0x2b, 0x16, 0xe8, 0x4b, 0xa0, 0x72, 0x3a, 0x57, 0x3a, - 0xe2, 0x18, 0x21, 0x85, 0x94, 0x02, 0xa3, 0xb0, 0x47, 0xcb, 0xc9, 0x65, - 0x91, 0x2e, 0x49, 0x66, 0xd9, 0xb1, 0xeb, 0x93, 0x07, 0x33, 0x4a, 0xa1, - 0x73, 0x57, 0x03, 0x86, 0xe0, 0xa7, 0xcf, 0x0f, 0x9e, 0xf0, 0x69, 0xe9, - 0xcc, 0x8e, 0xdd, 0x49, 0xae, 0x31, 0xf1, 0xeb, 0x5f, 0x15, 0xd7, 0x4c, - 0x00, 0xc2, 0x51, 0xa1, 0xbe, 0xb3, 0xc7, 0x4a, 0x90, 0xce, 0xc3, 0xea, - 0x0d, 0x6b, 0x4a, 0x9e, 0x96, 0xb3, 0xa7, 0xb4, 0xb5, 0xab, 0xc3, 0x73, - 0xc0, 0x73, 0xbf, 0x76, 0x51, 0x9b, 0xbd, 0x4e, 0xe1, 0xa0, 0x61, 0x68, - 0x0b, 0x26, 0x80, 0x0e, 0x38, 0x46, 0x84, 0x2b, 0xa6, 0x58, 0xce, 0x40, - 0xae, 0x30, 0x34, 0x0a, 0x68, 0x4e, 0xd2, 0xdc, 0x65, 0x37, 0x44, 0xa8, - 0xd2, 0xad, 0x60, 0x2f, 0xbf, 0xc1, 0xc2, 0x66, 0x4d, 0x8f, 0x90, 0xac, - 0xa9, 0xc3, 0x33, 0xd2, 0x48, 0x62, 0x33, 0x8c, 0xa3, 0x8e, 0x6d, 0x19, - 0x44, 0x6f, 0xe5, 0xeb, 0x79, 0x0d, 0x02, 0xce, 0xfd, 0x88, 0xbf, 0x96, - 0xd1, 0x39, 0x4e, 0xda, 0x5b, 0x98, 0x40, 0x14, 0x98, 0x06, 0xef, 0xeb, - 0x27, 0xd1, 0xba, 0xc4, 0x9b, 0x4c, 0x37, 0x68, 0xee, 0xd8, 0xf6, 0x8b, - 0x45, 0xd0, 0x6c, 0xb3, 0x76, 0x6d, 0x8c, 0x10, 0x0e, 0xfd, 0xdd, 0x41, - 0xf7, 0xd7, 0x7f, 0x8f, 0xd3, 0xdb, 0xe7, 0xe3, 0xaf, 0xee, 0x3c, 0xf8, - 0xbf, 0x6f, 0x17, 0xc7, 0xd7, 0xf7, 0x00, 0xeb, 0xf1, 0xe4, 0x4c, 0xa1, - 0x82, 0x24, 0x76, 0xf9, 0x6d, 0x8c, 0xf4, 0x41, 0xb1, 0xba, 0x20, 0xe7, - 0x86, 0xa8, 0xbf, 0x19, 0x48, 0x4c, 0x61, 0xda, 0xe1, 0x9b, 0xcb, 0x4c, - 0xb4, 0xc7, 0x77, 0x96, 0xdb, 0x94, 0x17, 0x01, 0x82, 0x00, 0xa0, 0x50, - 0xa8, 0x44, 0x94, 0x6a, 0x10, 0xb7, 0xb5, 0xb8, 0xa1, 0x01, 0x51, 0x1d, - 0xbc, 0xbb, 0x7f, 0x79, 0x96, 0x6d, 0x5e, 0x21, 0xb0, 0xe5, 0x7c, 0xdc, - 0xa8, 0x42, 0x0e, 0x03, 0x3c, 0xd1, 0x81, 0xae, 0x62, 0xad, 0x63, 0x25, - 0xe3, 0xb6, 0x56, 0x58, 0xed, 0xf0, 0xc7, 0xc6, 0xb4, 0xdd, 0x45, 0xde, - 0x73, 0xd9, 0x9c, 0xe4, 0x00, 0x3e, 0x1d, 0xdd, 0x13, 0x3a, 0x2e, 0xc2, - 0x06, 0x31, 0xd3, 0x1d, 0xff, 0x4e, 0x11, 0x91, 0x12, 0x85, 0x9e, 0xb6, - 0x68, 0xd6, 0x4d, 0x4b, 0x09, 0xb7, 0x51, 0x19, 0x87, 0x87, 0x1a, 0xe4, - 0x48, 0xcb, 0x6c, 0x2e, 0x0f, 0x46, 0xce, 0x6a, 0x71, 0x3c, 0xd6, 0x42, - 0x3d, 0x58, 0x5c, 0xbe, 0x1f, 0x31, 0xba, 0xa3, 0xa3, 0x9a, 0x3d, 0x00, - 0x77, 0xd2, 0xde, 0x07, 0x50, 0x13, 0x55, 0x3e, 0x07, 0xc4, 0xfb, 0xcf, - 0x40, 0x01, 0x8f, 0x87, 0xb7, 0x50, 0xab, 0xff, 0xb3, 0xfb, 0x20, 0x1a, - 0x7d, 0x8f, 0x18, 0x41, 0xf4, 0x8f, 0x8f, 0x72, 0x0e, 0xde, 0x72, 0xaa, - 0xd2, 0xc3, 0x0a, 0xbd, 0xfd, 0x9c, 0xaf, 0x61, 0xe0, 0xc6, 0xab, 0x45, - 0x09, 0xe9, 0x14, 0x74, 0xd4, 0x15, 0xbc, 0xe1, 0xcd, 0x05, 0x92, 0x22, - 0x85, 0x01, 0xf0, 0xff, 0xf1, 0x50, 0x80, 0x3a, 0x3f, 0xfc, 0x20, 0xa7, - 0x4c, 0xd3, 0x04, 0xb0, 0x56, 0x75, 0x62, 0x47, 0xb4, 0xd2, 0x0d, 0x00, - 0xd5, 0x7b, 0x79, 0xab, 0xf2, 0xc9, 0x3f, 0xed, 0x34, 0xb5, 0xdd, 0xd4, - 0x9f, 0xb6, 0x98, 0x9c, 0x4d, 0x49, 0x68, 0xe7, 0xaf, 0xf1, 0xc0, 0x06, - 0x80, 0xcc, 0x9e, 0x01, 0x0f, 0x98, 0x89, 0xe5, 0xfd, 0x61, 0xd0, 0x5e, - 0x3e, 0x5e, 0x1d, 0x53, 0xc6, 0xca, 0x09, 0xa4, 0x35, 0x9c, 0x24, 0xda, - 0xca, 0xb2, 0x4b, 0x81, 0x48, 0xd2, 0xc1, 0xe7, 0x35, 0x6f, 0x4e, 0xe1, - 0x34, 0xbd, 0x8d, 0x84, 0x65, 0x73, 0x80, 0xcb, 0xf9, 0x3d, 0xf8, 0xd2, - 0x96, 0x9b, 0x1e, 0xb7, 0xb2, 0x27, 0xe0, 0xcd, 0x57, 0x3f, 0xfc, 0x5b, - 0x8b, 0xd1, 0x70, 0x9c, 0xa0, 0xeb, 0x0a, 0x7b, 0x76, 0xc2, 0xaf, 0xe7, - 0x1f, 0x61, 0xe4, 0x7c, 0x93, 0x16, 0xe5, 0x88, 0x93, 0xec, 0xfe, 0xde, - 0x7e, 0x27, 0xf0, 0xee, 0x00, 0x00, 0x20, 0x13, 0x87, 0x67, 0xaf, 0x8f, - 0xb0, 0xf6, 0x1c, 0x37, 0x00, 0x6e, 0x51, 0xab, 0x13, 0xd9, 0xf7, 0x71, - 0xb2, 0x31, 0x1e, 0xf1, 0x88, 0x45, 0xc6, 0xae, 0x95, 0x31, 0x9c, 0xa6, - 0x11, 0xaa, 0x8b, 0x3e, 0xd5, 0x26, 0xaa, 0x9f, 0x0d, 0x16, 0x6e, 0x9a, - 0x53, 0xa8, 0xb2, 0x4d, 0x28, 0xc9, 0x92, 0x16, 0x45, 0x2a, 0x72, 0x6c, - 0x92, 0x8c, 0x0d, 0x08, 0x64, 0xee, 0x80, 0x61, 0x5c, 0x2e, 0x7a, 0x4d, - 0x7c, 0xa9, 0x10, 0x08, 0x30, 0x26, 0xf8, 0x0e, 0x56, 0x5c, 0x7e, 0x1f, - 0x0c, 0x16, 0x77, 0x65, 0xf7, 0xf8, 0x78, 0x24, 0xad, 0x9c, 0x9d, 0x32, - 0x33, 0x14, 0x92, 0x4e, 0x57, 0x54, 0x26, 0x6d, 0x62, 0x38, 0x2b, 0x4a, - 0xb0, 0xe1, 0xa7, 0x40, 0x0d, 0x71, 0xe6, 0xb5, 0xed, 0x15, 0x3f, 0xf1, - 0x24, 0xe3, 0xbf, 0x6e, 0x26, 0xaf, 0x84, 0x73, 0xd7, 0xf8, 0xe0, 0x39, - 0xee, 0xdf, 0x90, 0x86, 0x90, 0x91, 0x2c, 0x6f, 0xc6, 0x5e, 0x0a, 0xf4, - 0x8d, 0x11, 0xeb, 0x86, 0xf6, 0xd0, 0xd0, 0x92, 0xeb, 0x14, 0xcd, 0x61, - 0x7c, 0xcd, 0x42, 0xc5, 0xb3, 0x6c, 0xd5, 0x33, 0x1d, 0xb2, 0x5d, 0xed, - 0xe2, 0x1d, 0x30, 0x15, 0xca, 0xd4, 0x1c, 0xde, 0x84, 0xad, 0xe2, 0x32, - 0x40, 0xb3, 0x54, 0x01, 0x61, 0x30, 0x75, 0x6f, 0x1a, 0x11, 0x45, 0x9e, - 0x9c, 0x87, 0x53, 0xe4, 0xdc, 0xea, 0xb9, 0x2a, 0xb6, 0x23, 0xf2, 0x28, - 0x71, 0xf6, 0x08, 0x8a, 0xa7, 0xfd, 0x97, 0xc7, 0xfc, 0xaf, 0x45, 0x80, - 0x0d, 0x5a, 0x20, 0x1e, 0xc2, 0xc1, 0x49, 0x90, 0x30, 0xaf, 0x7a, 0xe0, - 0x31, 0x5d, 0x1f, 0xa2, 0xf5, 0x52, 0x46, 0x68, 0x1b, 0x43, 0x2e, 0xaf, - 0x06, 0xd0, 0x8a, 0x7e, 0x8f, 0xf6, 0xfe, 0x5e, 0x77, 0xf1, 0xfd, 0xa9, - 0x8c, 0xb3, 0x89, 0xc9, 0x96, 0x79, 0x13, 0x1e, 0xdc, 0xf7, 0x5f, 0x6b, - 0x2b, 0x53, 0xdc, 0xb9, 0xca, 0x73, 0x8c, 0xa3, 0xa3, 0x97, 0x3c, 0x48, - 0x10, 0x61, 0xfd, 0x9e, 0x87, 0xf2, 0xf6, 0xb5, 0xdb, 0x1f, 0x06, 0x6f, - 0xa8, 0x0f, 0x25, 0xec, 0x12, 0x24, 0x5e, 0x88, 0x68, 0x8a, 0xd4, 0xa2, - 0xff, 0x2a, 0x7a, 0x2b, 0x04, 0x28, 0x9b, 0xfa, 0x0b, 0x43, 0x99, 0xbc, - 0x70, 0xec, 0x69, 0x41, 0x4a, 0x1e, 0x37, 0x00, 0xf4, 0xd7, 0xe5, 0x44, - 0xab, 0xf6, 0x30, 0x7e, 0xff, 0x6e, 0x8b, 0xdd, 0xbe, 0x3d, 0x4c, 0x70, - 0xff, 0xf1, 0x50, 0x80, 0x34, 0x9f, 0xfc, 0x20, 0xa9, 0x4c, 0xdb, 0x49, - 0x10, 0xa0, 0xd4, 0x91, 0x15, 0x47, 0x25, 0x34, 0x3a, 0x40, 0x01, 0xe7, - 0x4e, 0x2f, 0x5f, 0xdc, 0x35, 0xc7, 0xc7, 0x3d, 0x6b, 0x81, 0x97, 0x1e, - 0xf4, 0xe3, 0x90, 0x1c, 0xe9, 0x93, 0xa1, 0x90, 0x0d, 0x8d, 0xe7, 0xd4, - 0x5b, 0xc7, 0x36, 0xdd, 0xaa, 0xaa, 0xdc, 0x7d, 0x42, 0xd6, 0x07, 0x86, - 0x80, 0x8e, 0xdd, 0x25, 0x18, 0x5f, 0x57, 0x8c, 0x97, 0xdc, 0xe4, 0x58, - 0x63, 0x61, 0x94, 0x0c, 0x08, 0x6d, 0x98, 0x94, 0xc1, 0x60, 0xce, 0x96, - 0x8b, 0xb0, 0x07, 0x49, 0x29, 0x85, 0x36, 0xfd, 0x8f, 0xb1, 0x05, 0xe9, - 0x90, 0xe7, 0x95, 0xde, 0x6f, 0xf6, 0xf8, 0xf4, 0x11, 0x2d, 0x92, 0xe3, - 0xb0, 0x1e, 0x47, 0xe2, 0x7c, 0x36, 0xb4, 0xfe, 0xf7, 0x3a, 0x09, 0xb1, - 0x78, 0x3f, 0x4d, 0x1f, 0x58, 0x72, 0x1e, 0x03, 0x64, 0x39, 0x98, 0xf7, - 0x17, 0x7d, 0xd7, 0x79, 0x09, 0x3e, 0xd9, 0xb6, 0xe5, 0x8c, 0x15, 0x7d, - 0xea, 0x30, 0x30, 0x05, 0xa6, 0x59, 0xdb, 0xa8, 0x7d, 0x16, 0x97, 0x01, - 0x94, 0x6a, 0x6d, 0xb1, 0xde, 0xf1, 0x71, 0x82, 0xa9, 0x39, 0x6f, 0x13, - 0x8b, 0x11, 0xc2, 0xe8, 0x35, 0x6b, 0x48, 0xd8, 0x6a, 0x3d, 0xae, 0xe6, - 0x7a, 0x12, 0x10, 0x8f, 0x7c, 0x3f, 0x01, 0x63, 0xdb, 0x77, 0x9e, 0x95, - 0x9c, 0xc9, 0x53, 0xa6, 0x36, 0xa8, 0x2f, 0x48, 0xad, 0x54, 0x99, 0x0c, - 0x63, 0x09, 0x8d, 0x25, 0x63, 0xc5, 0xa4, 0xd5, 0x00, 0x3c, 0x4d, 0xf4, - 0x01, 0xf1, 0x6e, 0x3f, 0x80, 0x3a, 0xa6, 0xaf, 0xe4, 0x17, 0xd7, 0x1c, - 0x71, 0x07, 0x66, 0x0d, 0xd4, 0x8a, 0xc0, 0x1d, 0x21, 0x2e, 0xca, 0x28, - 0x1f, 0x7f, 0x40, 0x4f, 0xf2, 0x10, 0xc4, 0x04, 0x2c, 0xa3, 0xd8, 0x6f, - 0xea, 0xa4, 0x34, 0x59, 0xa0, 0xe9, 0x58, 0x1b, 0x5f, 0x66, 0xfc, 0x7d, - 0xf1, 0x4e, 0x4f, 0x53, 0x25, 0xd4, 0xbc, 0x04, 0xa3, 0x8c, 0xd8, 0x0b, - 0xee, 0x32, 0x80, 0x03, 0x70, 0x26, 0x56, 0x62, 0x31, 0x3c, 0x2f, 0xc7, - 0xc9, 0x33, 0x82, 0xb3, 0x01, 0xd5, 0x6f, 0xa0, 0x13, 0xf4, 0x4a, 0x66, - 0xca, 0xfa, 0x23, 0x10, 0x4e, 0x73, 0xcf, 0x2c, 0xe0, 0x33, 0xba, 0xe7, - 0xf6, 0xe9, 0x2b, 0x9a, 0x89, 0x11, 0xc4, 0x04, 0x42, 0xe9, 0xa5, 0x10, - 0x22, 0x4d, 0x19, 0x8e, 0x9e, 0x86, 0x54, 0x0d, 0xfc, 0x66, 0x9d, 0xe2, - 0x05, 0xd7, 0xa2, 0xf5, 0xdb, 0x2e, 0xfe, 0x1b, 0x8e, 0x2b, 0xe7, 0x05, - 0x6b, 0x87, 0x39, 0x6c, 0x42, 0xe1, 0x93, 0xe9, 0x1e, 0xbf, 0x8e, 0xfb, - 0x40, 0x43, 0x34, 0xb4, 0x22, 0x39, 0x95, 0xeb, 0x83, 0x65, 0x17, 0xaf, - 0xcd, 0xe2, 0x52, 0x79, 0x07, 0xcb, 0x45, 0x3a, 0xef, 0x81, 0x21, 0x27, - 0xbe, 0xa3, 0xe2, 0x8b, 0x5d, 0x96, 0x67, 0xc7, 0xb3, 0x4f, 0x20, 0xe4, - 0xcb, 0x64, 0x56, 0x55, 0x28, 0xa5, 0xa4, 0x8e, 0xd8, 0x7a, 0x0d, 0x5b, - 0x41, 0x49, 0xc1, 0x4f, 0xc8, 0x2b, 0xc6, 0xe8, 0x3b, 0xf4, 0xec, 0x97, - 0xff, 0xf1, 0x50, 0x80, 0x37, 0x5f, 0xfc, 0x20, 0x9f, 0x4c, 0xdb, 0x66, - 0x9b, 0x23, 0xa0, 0x6d, 0x32, 0x4c, 0xca, 0xd2, 0x70, 0xcc, 0x00, 0x01, - 0x3d, 0x9c, 0x5f, 0x5f, 0xea, 0x0d, 0x7b, 0x67, 0x97, 0xc0, 0xc0, 0x52, - 0xa9, 0x45, 0x99, 0xb7, 0x45, 0x36, 0x1e, 0xb3, 0x7c, 0x9b, 0x9c, 0x31, - 0xac, 0x2f, 0x4b, 0x65, 0x2f, 0x44, 0x11, 0xbb, 0x93, 0xc1, 0x75, 0x22, - 0xa7, 0xba, 0x2e, 0xb6, 0x9e, 0x00, 0x92, 0x32, 0xbd, 0xe8, 0x8e, 0x5d, - 0x7f, 0x95, 0xed, 0xd0, 0xbb, 0xa9, 0xa2, 0x2d, 0x2e, 0xd1, 0x50, 0x41, - 0x62, 0x22, 0x9c, 0xa0, 0x1d, 0xf8, 0xed, 0x97, 0x1a, 0xd3, 0x58, 0x81, - 0x04, 0x4e, 0x1b, 0x23, 0xba, 0x6b, 0xd5, 0x1e, 0x96, 0xd6, 0xf2, 0x3e, - 0x52, 0xef, 0x89, 0x58, 0x0b, 0x71, 0x13, 0x1b, 0xf3, 0x17, 0xa5, 0x3e, - 0x2e, 0x79, 0xdd, 0xbd, 0x64, 0x60, 0xd4, 0xaf, 0x34, 0x7b, 0x5b, 0x39, - 0x59, 0x68, 0xc5, 0x2a, 0x5d, 0x80, 0x3f, 0x98, 0x17, 0x88, 0x50, 0x27, - 0xac, 0x95, 0xeb, 0x28, 0x06, 0x96, 0xe3, 0x49, 0x00, 0x31, 0x02, 0x6e, - 0x0e, 0x60, 0x69, 0x3b, 0x93, 0x4f, 0x7b, 0x54, 0x22, 0xb8, 0xd4, 0x44, - 0x25, 0x45, 0xa5, 0xaa, 0x56, 0x51, 0xcd, 0x3d, 0xae, 0xbd, 0x6b, 0x34, - 0xdd, 0x2d, 0x72, 0x36, 0xb5, 0x8d, 0x42, 0xf9, 0x11, 0xb7, 0x2a, 0x86, - 0xfe, 0xef, 0x5b, 0x9d, 0xe5, 0x51, 0x74, 0xd5, 0xac, 0x1d, 0x51, 0xef, - 0x61, 0x0a, 0xea, 0x2e, 0x0c, 0x02, 0x94, 0x38, 0x89, 0xc5, 0x9b, 0x29, - 0x45, 0x6b, 0xe4, 0xea, 0xcf, 0x2b, 0xf5, 0x52, 0x49, 0x50, 0xf5, 0xad, - 0x90, 0x56, 0x21, 0x35, 0x08, 0xc5, 0x03, 0xdb, 0xd0, 0x00, 0x04, 0xaf, - 0x6f, 0x5e, 0x72, 0xe3, 0x57, 0x29, 0xd4, 0xeb, 0x20, 0x8f, 0xae, 0xf3, - 0xfd, 0x37, 0x6e, 0xe2, 0xb1, 0xab, 0x94, 0xa4, 0xa7, 0x44, 0x64, 0x0b, - 0xcb, 0x5c, 0x58, 0xa6, 0xac, 0xa1, 0x61, 0x7c, 0xcb, 0x22, 0xd3, 0x4e, - 0xa3, 0xcc, 0x8a, 0x14, 0xbd, 0x4c, 0xd2, 0x42, 0xe5, 0x14, 0x59, 0x94, - 0xe1, 0xb8, 0x24, 0xc9, 0xf7, 0x19, 0xb1, 0x65, 0xc7, 0x96, 0x0e, 0x64, - 0xcd, 0x30, 0xc8, 0x0c, 0x42, 0x03, 0xe5, 0x97, 0x85, 0xaf, 0xb5, 0xa2, - 0x55, 0x82, 0x08, 0x8e, 0x77, 0x2d, 0x3a, 0xc2, 0x44, 0xed, 0x1c, 0x23, - 0x25, 0x78, 0x31, 0xcf, 0x13, 0x00, 0x00, 0x46, 0xe0, 0xea, 0x49, 0x2d, - 0x08, 0x61, 0x58, 0x56, 0x72, 0x01, 0x5e, 0xf0, 0xba, 0x0e, 0x8f, 0x86, - 0xd8, 0xcc, 0x86, 0x04, 0x6c, 0xc2, 0xd1, 0xdb, 0xda, 0xd6, 0x7b, 0xb1, - 0x48, 0x34, 0x46, 0x89, 0xf4, 0xab, 0x02, 0xab, 0x80, 0xa3, 0xd5, 0x85, - 0x56, 0x62, 0xb8, 0x56, 0x7d, 0xca, 0xc9, 0x07, 0x05, 0x54, 0x1b, 0x25, - 0xfd, 0xae, 0x8f, 0x0a, 0xce, 0x26, 0xa4, 0x02, 0xbe, 0x24, 0xae, 0x5c, - 0x61, 0xd5, 0x02, 0x64, 0xc7, 0x8a, 0xd7, 0x55, 0xa2, 0x6c, 0x90, 0x36, - 0xab, 0x25, 0x7c, 0x6e, 0xbe, 0x4a, 0xb8, 0xf1, 0xc5, 0x25, 0xab, 0x53, - 0x10, 0xd7, 0xce, 0x6a, 0x4c, 0x2d, 0xc9, 0x01, 0x9e, 0xc7, 0x2d, 0x67, - 0x46, 0x33, 0x42, 0xf9, 0xb1, 0x99, 0x9a, 0x20, 0xc5, 0xf8, 0xff, 0xf1, - 0x50, 0x80, 0x33, 0x3f, 0xfc, 0x20, 0xa8, 0x7a, 0xd6, 0x44, 0x34, 0x0c, - 0x8e, 0x6c, 0x43, 0x31, 0x40, 0x1c, 0x72, 0x16, 0xc0, 0x2f, 0x3c, 0xef, - 0xce, 0xb9, 0xe2, 0xaf, 0xcf, 0x7a, 0x69, 0x70, 0x34, 0x0b, 0x11, 0xfc, - 0xdd, 0x57, 0x45, 0x8e, 0x03, 0x1f, 0xf9, 0xbe, 0xe0, 0xcd, 0x33, 0x63, - 0xd6, 0x89, 0x3e, 0xe5, 0xb0, 0x8e, 0xe5, 0xaf, 0x7e, 0xab, 0x36, 0x64, - 0x44, 0x6f, 0x25, 0xa0, 0xc0, 0xed, 0x63, 0xcf, 0x7a, 0xd2, 0xa7, 0xa1, - 0xbc, 0xe5, 0xb5, 0xd1, 0x99, 0x60, 0xab, 0x08, 0x43, 0x4e, 0xc2, 0x6c, - 0x51, 0xb5, 0xed, 0x1d, 0x61, 0xb0, 0x3a, 0xd9, 0xc6, 0xd7, 0x8f, 0x42, - 0xaa, 0x18, 0x34, 0x25, 0x28, 0xa9, 0x0e, 0xd6, 0x17, 0x97, 0xaa, 0x4d, - 0x2c, 0xd5, 0xed, 0x14, 0xf3, 0x76, 0x69, 0x92, 0x9b, 0x25, 0x65, 0x3a, - 0xeb, 0x00, 0x43, 0x73, 0x18, 0xff, 0xc9, 0xf8, 0x7d, 0x8b, 0xe9, 0x74, - 0xca, 0xba, 0xc0, 0x2e, 0xe5, 0x8e, 0xdb, 0x54, 0x61, 0xc2, 0x78, 0x50, - 0xfd, 0x2a, 0x85, 0x8e, 0x0b, 0xd6, 0x50, 0x9b, 0x98, 0x0a, 0xea, 0x9b, - 0x14, 0xb3, 0x1d, 0x6a, 0x65, 0x70, 0xe3, 0x12, 0x2d, 0x0d, 0x22, 0xd3, - 0x18, 0x52, 0x86, 0xec, 0xcc, 0x17, 0xf8, 0x5a, 0xa3, 0x63, 0x48, 0xad, - 0x69, 0x24, 0xf0, 0xa2, 0x10, 0x22, 0xdb, 0x1e, 0xa9, 0xad, 0xad, 0xe9, - 0x87, 0x1c, 0xe9, 0x90, 0xda, 0x22, 0x12, 0xc0, 0x95, 0x66, 0x2a, 0x51, - 0xad, 0x6d, 0x41, 0xd0, 0x50, 0x4e, 0x31, 0x39, 0x19, 0x0a, 0xc3, 0x00, - 0x00, 0x00, 0x25, 0x79, 0xeb, 0xd7, 0xb5, 0x26, 0x9c, 0xf9, 0xce, 0x1d, - 0x0c, 0x93, 0x2e, 0x49, 0x90, 0x9c, 0xf0, 0x68, 0xf1, 0x1c, 0x76, 0xf2, - 0xaa, 0x80, 0xe8, 0xe6, 0xaf, 0x90, 0x0a, 0x3c, 0x60, 0xa6, 0x85, 0xfc, - 0x92, 0x45, 0x0c, 0x34, 0xa1, 0x8d, 0x2d, 0xee, 0x92, 0x02, 0xa7, 0x2f, - 0x23, 0x2c, 0x1e, 0x8c, 0xc7, 0xae, 0xda, 0x64, 0xbf, 0x3f, 0xf8, 0x9d, - 0x12, 0xbb, 0x93, 0x8d, 0x8d, 0x6d, 0xff, 0x8b, 0x9d, 0xd0, 0x36, 0x7c, - 0xb7, 0x6e, 0xd6, 0xc1, 0xfe, 0x1f, 0x4a, 0xaa, 0xdf, 0x65, 0x55, 0x4e, - 0x2a, 0x5d, 0xbc, 0x08, 0xdd, 0x1c, 0x90, 0x52, 0xdd, 0x14, 0xe9, 0xbe, - 0x97, 0xc0, 0x61, 0xcc, 0x1c, 0x3d, 0x1c, 0xef, 0x86, 0xda, 0xb4, 0xf0, - 0x65, 0x60, 0x37, 0x35, 0x08, 0xdc, 0xc3, 0xb7, 0x93, 0x22, 0xcd, 0xe9, - 0x0a, 0xf3, 0x68, 0x77, 0x20, 0xb0, 0x4e, 0x23, 0x0a, 0x94, 0xdc, 0xd0, - 0x9b, 0xb2, 0xc1, 0x2f, 0xca, 0xb5, 0xa2, 0x53, 0x49, 0x68, 0x33, 0x9d, - 0x69, 0xde, 0xf0, 0x4f, 0xcd, 0xae, 0x81, 0x00, 0xf0, 0xd6, 0x81, 0x0d, - 0x44, 0x34, 0x50, 0xd0, 0xa7, 0x79, 0xed, 0x74, 0xa5, 0x35, 0x52, 0x90, - 0xa8, 0x06, 0xef, 0x7e, 0x2a, 0x37, 0x88, 0xd4, 0xc4, 0xe0, 0x09, 0x27, - 0x62, 0xb7, 0x2b, 0xd9, 0xdd, 0xe4, 0xc6, 0x30, 0xa1, 0x5f, 0x80, 0xff, - 0xf1, 0x50, 0x80, 0x21, 0x7f, 0xfc, 0x21, 0x1a, 0xcf, 0xfe, 0xbf, 0xec, - 0x10, 0x22, 0xa7, 0xb1, 0x42, 0xcd, 0xc4, 0x54, 0x58, 0x03, 0xaf, 0x12, - 0xb8, 0xdf, 0xdf, 0xec, 0x09, 0x5e, 0x77, 0x3c, 0xcb, 0xe7, 0x5e, 0x6a, - 0xb1, 0x3a, 0x93, 0x9d, 0x08, 0xda, 0xa6, 0x77, 0x82, 0x31, 0xf8, 0x5a, - 0x25, 0x70, 0x1a, 0x28, 0xcf, 0xa2, 0x01, 0xfe, 0x21, 0x7c, 0xc6, 0x5b, - 0xe2, 0xd1, 0x16, 0xe7, 0x09, 0xcb, 0xb1, 0x89, 0xac, 0xe0, 0x4b, 0x52, - 0x7e, 0xc1, 0x7b, 0xfa, 0xb0, 0x89, 0x3f, 0x45, 0x35, 0x2a, 0x00, 0xb4, - 0x65, 0x0c, 0x82, 0x15, 0xaf, 0x06, 0x20, 0x74, 0xe7, 0x4f, 0xa8, 0x32, - 0x67, 0x7b, 0xbd, 0xca, 0x64, 0x46, 0xae, 0xfe, 0xa8, 0x31, 0x3a, 0x00, - 0x14, 0xa9, 0x46, 0x55, 0x94, 0x20, 0xd7, 0x51, 0x90, 0x79, 0x1c, 0xca, - 0x89, 0x8d, 0x83, 0x78, 0x1a, 0x0b, 0x46, 0x9d, 0x85, 0x80, 0x77, 0x44, - 0x76, 0xc9, 0x83, 0xe4, 0xff, 0xc0, 0xe5, 0xa4, 0xd4, 0xe6, 0x2d, 0x2d, - 0xc8, 0x1a, 0xc3, 0xa2, 0xa5, 0xc2, 0xc5, 0xd9, 0x60, 0x3b, 0x8f, 0xcb, - 0x42, 0x74, 0xae, 0xf9, 0xfb, 0xe7, 0x5b, 0xa3, 0xf5, 0x7a, 0xff, 0x0b, - 0xea, 0xbf, 0x82, 0x3f, 0x9d, 0x72, 0x87, 0x4d, 0xa7, 0xc8, 0xfe, 0xdb, - 0xef, 0x1d, 0xc0, 0x51, 0x01, 0x1d, 0x29, 0x8f, 0xc0, 0x5f, 0xe5, 0xa9, - 0xad, 0x5d, 0xa6, 0x7c, 0x9e, 0x81, 0xb4, 0x7a, 0x1d, 0x62, 0x5a, 0x7e, - 0x2d, 0x3c, 0x0d, 0x4f, 0x61, 0x51, 0x1b, 0x3e, 0x40, 0x0e, 0xbb, 0x35, - 0xbf, 0xbf, 0xd8, 0x12, 0xbc, 0xe4, 0x8c, 0xbc, 0xba, 0x95, 0x9c, 0x65, - 0xd5, 0x00, 0xb7, 0x6f, 0x20, 0xd0, 0x68, 0x16, 0x7d, 0xa9, 0xb9, 0x1c, - 0xf3, 0xa0, 0x88, 0x81, 0xf6, 0x0a, 0x3c, 0x34, 0x4d, 0x8e, 0x7f, 0x43, - 0x97, 0xfb, 0xb2, 0x5f, 0xca, 0x15, 0x10, 0x0d, 0x14, 0x04, 0xa0, 0xe3, - 0x58, 0xe0, 0xff, 0xf1, 0x50, 0x80, 0x20, 0xdf, 0xfc, 0x21, 0x1a, 0xcf, - 0x9e, 0xf3, 0x7e, 0xbf, 0xbf, 0xa7, 0xb4, 0x30, 0xcd, 0xe4, 0x54, 0x5a, - 0x84, 0x01, 0x28, 0x3d, 0xbd, 0x5e, 0x1a, 0xeb, 0x76, 0x9a, 0x6f, 0x57, - 0xac, 0xad, 0x55, 0x3a, 0xab, 0xb1, 0x46, 0x91, 0x38, 0x1a, 0x9c, 0x11, - 0xfb, 0xc3, 0xc3, 0x52, 0xa9, 0xa6, 0x2e, 0x2d, 0x99, 0x57, 0x80, 0x2a, - 0x89, 0x4a, 0x3a, 0x70, 0x2f, 0xff, 0x47, 0xd6, 0xdc, 0xd2, 0xae, 0xb6, - 0x8a, 0xaf, 0x14, 0x9f, 0x54, 0x20, 0xa4, 0xd3, 0x9c, 0xe0, 0x04, 0xe7, - 0x28, 0xa3, 0x32, 0x4e, 0x50, 0x16, 0x53, 0x54, 0x5c, 0x5e, 0x8e, 0x0c, - 0x66, 0xb4, 0xbc, 0x23, 0x3b, 0x14, 0x29, 0x45, 0x13, 0xa8, 0x15, 0x44, - 0x87, 0x99, 0x68, 0xce, 0xc1, 0x0a, 0x33, 0x39, 0x9f, 0x7b, 0xd7, 0x9b, - 0x47, 0x01, 0x1b, 0x88, 0x30, 0xae, 0x15, 0xd8, 0x3a, 0x5e, 0x25, 0xe5, - 0x2a, 0x94, 0x33, 0x09, 0x69, 0x77, 0xd8, 0x87, 0x57, 0xa7, 0x5f, 0x19, - 0xf3, 0x81, 0x62, 0xef, 0x03, 0x89, 0xef, 0x6c, 0xb0, 0xfc, 0xc6, 0x6b, - 0xbc, 0xcd, 0x5e, 0x5a, 0xf1, 0x69, 0x03, 0x3f, 0x43, 0x36, 0x0b, 0x7e, - 0x4e, 0x3e, 0x85, 0x05, 0xfe, 0xbc, 0x15, 0x7a, 0x08, 0x3c, 0x3b, 0x77, - 0xd1, 0x9d, 0x5c, 0xb6, 0x69, 0xb0, 0x9e, 0xad, 0x37, 0x6f, 0xb3, 0x5c, - 0x26, 0xe2, 0xf0, 0x9d, 0xd9, 0x14, 0xec, 0x69, 0x58, 0xa7, 0xa2, 0x18, - 0xb2, 0x6e, 0xc6, 0xbb, 0xe3, 0x32, 0xf5, 0xd6, 0xed, 0x97, 0x99, 0x75, - 0x73, 0x9d, 0x4a, 0xab, 0x90, 0x1f, 0xc6, 0x5d, 0x64, 0xa0, 0x3a, 0xa0, - 0xdc, 0xaf, 0x1f, 0x98, 0xbc, 0x34, 0xbf, 0xff, 0x5b, 0xc1, 0xcb, 0xd1, - 0x80, 0xa9, 0x59, 0x91, 0xc9, 0xe0, 0x25, 0x9f, 0x05, 0x88, 0x0b, 0xc1, - 0x38, 0xdb, 0x5e, 0xa2, 0xf9, 0xf7, 0xa3, 0x32, 0xb9, 0x67, 0x41, 0xc0, - 0xff, 0xf1, 0x50, 0x80, 0x2b, 0xbf, 0xfc, 0x21, 0x1a, 0xc9, 0xf5, 0x57, - 0xec, 0x04, 0x81, 0xb0, 0x85, 0xaa, 0x89, 0x88, 0xa6, 0xbc, 0xd7, 0x9a, - 0xaf, 0xb7, 0xeb, 0x3d, 0x5e, 0x6b, 0xae, 0x77, 0xe6, 0xaf, 0x77, 0xbe, - 0x27, 0x73, 0xa8, 0xc9, 0x73, 0x9c, 0xf6, 0xcc, 0xe2, 0xa0, 0x3a, 0xbd, - 0x3c, 0x13, 0x59, 0x87, 0xae, 0x8d, 0x02, 0xb8, 0xd7, 0x48, 0xf1, 0xcb, - 0x2c, 0xb1, 0x29, 0xe4, 0x3e, 0xce, 0xa0, 0x3f, 0x4f, 0x08, 0x95, 0xf7, - 0x54, 0xc4, 0xeb, 0x7a, 0xaa, 0xf5, 0xe2, 0x62, 0x71, 0x5c, 0xfa, 0xa1, - 0x92, 0x65, 0x79, 0x1a, 0xe3, 0xb9, 0xae, 0x85, 0xb5, 0x1c, 0xe7, 0xea, - 0x7e, 0xaf, 0xe2, 0x50, 0x4e, 0x02, 0xff, 0xad, 0xef, 0xa2, 0xef, 0x37, - 0xd3, 0x5d, 0x0b, 0x49, 0xaf, 0xbd, 0x2f, 0x7d, 0x37, 0x7d, 0x3e, 0x9b, - 0xb7, 0x63, 0xff, 0x7a, 0xbe, 0xf7, 0x6f, 0x78, 0x51, 0xfa, 0x9f, 0x78, - 0x3f, 0xaf, 0xac, 0xea, 0x36, 0xce, 0x66, 0x97, 0x5a, 0xaa, 0xae, 0xbe, - 0x8e, 0xaf, 0x59, 0xc2, 0xfa, 0x1a, 0xa9, 0x98, 0x6e, 0x25, 0xc2, 0x74, - 0xc6, 0x15, 0x29, 0x9c, 0x9a, 0xed, 0xd2, 0xb6, 0x99, 0xab, 0x8e, 0x58, - 0xba, 0xb5, 0x1f, 0x53, 0x8a, 0x3b, 0x80, 0x56, 0x2a, 0x59, 0x44, 0xe6, - 0x19, 0x6c, 0x02, 0x72, 0x01, 0xb3, 0x0b, 0xb7, 0x42, 0x63, 0xa2, 0x06, - 0x8b, 0x69, 0x08, 0xa7, 0xae, 0xee, 0xbe, 0x1d, 0xb3, 0x75, 0xcd, 0xe6, - 0xe4, 0x86, 0x16, 0xbc, 0x08, 0x18, 0x5a, 0xf5, 0x6e, 0x6d, 0xab, 0xa8, - 0x57, 0x83, 0x37, 0xa8, 0x99, 0x91, 0xd8, 0x5e, 0xa6, 0x0f, 0x90, 0x95, - 0xe6, 0xab, 0x9f, 0x37, 0xef, 0x2b, 0xea, 0xba, 0xfd, 0x3c, 0xd5, 0xee, - 0xee, 0xa3, 0xdb, 0xdb, 0x69, 0x51, 0x59, 0xc5, 0x65, 0xe6, 0xb9, 0x69, - 0x86, 0x45, 0x04, 0x73, 0x0d, 0x81, 0xc0, 0x2b, 0xfb, 0xf7, 0xaf, 0xf8, - 0xc8, 0xa4, 0xc6, 0x4c, 0x97, 0xd5, 0x7e, 0xcf, 0xc4, 0x6b, 0x64, 0x65, - 0xf1, 0xae, 0x94, 0x1c, 0x59, 0x67, 0x5a, 0xb0, 0xc3, 0x2d, 0x7d, 0x7c, - 0x26, 0x9d, 0x54, 0xe9, 0x42, 0xea, 0x34, 0x29, 0x71, 0x3f, 0xee, 0xfb, - 0xd9, 0xdf, 0x99, 0x89, 0x61, 0xb4, 0x21, 0x88, 0x9e, 0x75, 0x73, 0x55, - 0xf2, 0x38, 0x4c, 0xdd, 0xfd, 0xec, 0x20, 0x2a, 0x41, 0x82, 0x9e, 0x45, - 0x58, 0x45, 0x92, 0x4f, 0x44, 0xc3, 0xde, 0xdb, 0xe4, 0xab, 0xa6, 0xf5, - 0xc2, 0x7d, 0x37, 0x74, 0x40, 0x9e, 0xaa, 0x06, 0x31, 0x93, 0xaa, 0xc1, - 0xe0, 0xff, 0xf1, 0x50, 0x80, 0x2d, 0x3f, 0xfc, 0x21, 0x1a, 0xcf, 0x9d, - 0x9d, 0xb9, 0x0c, 0x01, 0xa9, 0xa1, 0x43, 0xd8, 0xa9, 0x45, 0x09, 0xa7, - 0x1d, 0x67, 0xbf, 0xc7, 0xd7, 0xf7, 0xfb, 0xf8, 0xf6, 0x4d, 0xf5, 0xcc, - 0x5c, 0xac, 0x9d, 0x72, 0x99, 0xa6, 0xe5, 0x25, 0x71, 0xbd, 0x74, 0x02, - 0xff, 0x0e, 0xa6, 0xe7, 0x12, 0x80, 0xb6, 0x9e, 0x94, 0x79, 0xb1, 0x41, - 0x93, 0x67, 0xe5, 0x67, 0xce, 0xac, 0x21, 0x47, 0x4b, 0x1b, 0x54, 0xfb, - 0x66, 0x31, 0xf6, 0x57, 0xa2, 0x84, 0xe1, 0x24, 0xe7, 0x80, 0x25, 0x89, - 0x35, 0x25, 0x4b, 0x44, 0x2f, 0x58, 0xe6, 0x39, 0xce, 0x20, 0x80, 0x5a, - 0x10, 0x85, 0x06, 0xa1, 0xe7, 0x50, 0x48, 0x44, 0x2b, 0x26, 0x60, 0x94, - 0x2b, 0xbe, 0x1a, 0x06, 0x62, 0x2b, 0xb9, 0x73, 0x72, 0x54, 0x74, 0xa4, - 0x7e, 0xf4, 0xf9, 0xf5, 0xf1, 0x36, 0x27, 0x6e, 0xec, 0x24, 0x0d, 0x9b, - 0x94, 0xaa, 0xdb, 0x94, 0xe9, 0x5b, 0x3a, 0xaa, 0xa6, 0x5a, 0x21, 0x74, - 0xbb, 0xfd, 0x74, 0x50, 0x04, 0xe5, 0x30, 0x70, 0x7a, 0x95, 0x5a, 0x2b, - 0x16, 0xef, 0xe1, 0x02, 0x00, 0x99, 0x8f, 0xd5, 0xde, 0x5b, 0xc0, 0xc5, - 0x87, 0xc1, 0xf6, 0x59, 0x1c, 0xa7, 0x34, 0xbb, 0x13, 0x04, 0x46, 0xb7, - 0x93, 0x69, 0x95, 0x11, 0x4b, 0xf2, 0x25, 0x85, 0x76, 0x4d, 0x38, 0x8b, - 0xcc, 0xc4, 0xa8, 0xa4, 0x63, 0x34, 0xdd, 0x4a, 0xe6, 0x98, 0xc5, 0x75, - 0x7b, 0x71, 0x86, 0x4a, 0x66, 0x04, 0x01, 0x20, 0x88, 0xfd, 0xf7, 0xea, - 0x2a, 0x58, 0x7b, 0xc5, 0xd8, 0xee, 0xe9, 0xd5, 0x5a, 0xaa, 0xea, 0x10, - 0xb1, 0xf5, 0x25, 0xe5, 0x5f, 0x14, 0x3f, 0x91, 0xee, 0xff, 0xee, 0x56, - 0x30, 0xa5, 0x7c, 0x6a, 0x54, 0x91, 0x14, 0x37, 0x99, 0x83, 0xe9, 0x34, - 0xeb, 0x8c, 0xf7, 0xf8, 0xbf, 0xcf, 0xdf, 0xf3, 0xfb, 0xd4, 0xdd, 0xd4, - 0xdf, 0x52, 0xb5, 0x9a, 0xdd, 0x54, 0x93, 0x8c, 0x85, 0x6b, 0x35, 0x61, - 0xe4, 0xe8, 0x5a, 0xfd, 0xe8, 0xd1, 0x58, 0x5b, 0xc0, 0xed, 0x49, 0x26, - 0x83, 0x9c, 0xe8, 0x68, 0x40, 0x73, 0x85, 0xcb, 0xd3, 0xc4, 0x01, 0x65, - 0x12, 0x33, 0x5e, 0x3c, 0x2e, 0x3b, 0x9c, 0x12, 0xe2, 0x1c, 0x80, 0x08, - 0x28, 0xe8, 0xd4, 0xe1, 0x18, 0x42, 0x84, 0x0b, 0x85, 0x4e, 0x30, 0x6e, - 0xa1, 0x7a, 0x44, 0x30, 0x21, 0x4a, 0x91, 0xec, 0x41, 0x3f, 0x71, 0xb4, - 0x20, 0x62, 0xe1, 0xcb, 0xfd, 0xa8, 0x4d, 0x0a, 0xc5, 0xe8, 0xd2, 0xad, - 0x81, 0xb9, 0xd7, 0x9a, 0x6a, 0x73, 0x05, 0xd4, 0x7a, 0x09, 0xcd, 0xe8, - 0x85, 0xe0, 0xff, 0xf1, 0x50, 0x80, 0x2a, 0x7f, 0xfc, 0x21, 0x1a, 0xce, - 0xf5, 0xbb, 0xdf, 0x80, 0x89, 0xa8, 0x86, 0xb2, 0x12, 0xcd, 0xfd, 0xf7, - 0x7c, 0x4e, 0x6f, 0x8e, 0x6a, 0x69, 0x7c, 0xeb, 0x76, 0x45, 0xe4, 0xe3, - 0x2a, 0xaf, 0x62, 0x26, 0x5c, 0x82, 0x9e, 0x83, 0xce, 0x9f, 0xf8, 0x53, - 0x7b, 0x75, 0x99, 0x6b, 0xdf, 0x48, 0xf5, 0xdb, 0xf8, 0x9d, 0xaf, 0x32, - 0x45, 0x4a, 0x05, 0x0f, 0x9b, 0x01, 0x91, 0x39, 0x86, 0x8f, 0x7b, 0x55, - 0x02, 0xcf, 0x79, 0x59, 0x16, 0xc5, 0x89, 0xef, 0xd7, 0xb9, 0x81, 0xde, - 0x9a, 0xc6, 0x83, 0x01, 0x47, 0x73, 0xa9, 0x51, 0x04, 0xf3, 0xe4, 0x36, - 0x8f, 0x19, 0xf6, 0x69, 0x36, 0xc2, 0x17, 0xe0, 0x29, 0x4b, 0x79, 0x2d, - 0x49, 0x8b, 0x5b, 0x8b, 0xa9, 0x24, 0xb9, 0x6a, 0xa5, 0x52, 0x58, 0xae, - 0x63, 0x22, 0x7a, 0x39, 0xfb, 0x23, 0x8f, 0x62, 0xbd, 0x57, 0xb2, 0x6b, - 0x87, 0xe7, 0x88, 0x80, 0xad, 0x67, 0xc2, 0x64, 0x00, 0x0c, 0xe8, 0x4d, - 0x4e, 0xd3, 0x2b, 0xa9, 0x5d, 0x36, 0x91, 0x96, 0x24, 0x95, 0xc4, 0x12, - 0xd5, 0xf4, 0x06, 0x71, 0xda, 0xd8, 0xcf, 0xca, 0x6c, 0x02, 0x57, 0xc2, - 0x2f, 0x8f, 0xce, 0xe8, 0x2f, 0x33, 0xdc, 0xeb, 0xda, 0x65, 0xd3, 0x9b, - 0x96, 0xbf, 0xee, 0x89, 0x42, 0xc0, 0xbb, 0x1b, 0x57, 0x2c, 0x3d, 0x4d, - 0x74, 0xfa, 0x59, 0x00, 0x27, 0xc6, 0xfb, 0x23, 0xf8, 0x66, 0x3c, 0x30, - 0x3a, 0x20, 0x9c, 0x08, 0x6c, 0x5c, 0xca, 0xa9, 0x88, 0xd1, 0xd9, 0x3d, - 0xa3, 0x43, 0xcd, 0xa3, 0xe7, 0x51, 0xa6, 0x3a, 0xb1, 0xc6, 0x7a, 0x72, - 0xb5, 0x4c, 0xf7, 0xd4, 0xba, 0x05, 0x16, 0xdb, 0x61, 0x8f, 0x3a, 0xa1, - 0xda, 0xc6, 0x42, 0x11, 0x7d, 0xed, 0xf5, 0x77, 0xc4, 0xfe, 0x3f, 0x5d, - 0x6e, 0xbe, 0xdf, 0xca, 0xfe, 0xdf, 0x5b, 0xb2, 0x2f, 0x27, 0x19, 0xb9, - 0x7b, 0x85, 0xd2, 0xae, 0x03, 0x1a, 0xcf, 0xb4, 0xd3, 0xcc, 0xa9, 0xe3, - 0x34, 0xf8, 0xbd, 0xa4, 0xcf, 0x22, 0xce, 0xe7, 0xbe, 0xef, 0x54, 0x3c, - 0x0d, 0xf7, 0x92, 0x19, 0x5f, 0x84, 0xe1, 0x40, 0xa2, 0xdb, 0x7f, 0xd1, - 0x01, 0x0d, 0x63, 0xf8, 0xa1, 0x09, 0x00, 0xb6, 0x54, 0x4f, 0x0f, 0xd9, - 0x69, 0xae, 0x8e, 0xa9, 0x4f, 0x87, 0x90, 0xd1, 0x5f, 0xe5, 0xa8, 0xa8, - 0x67, 0x5e, 0xe3, 0xe8, 0xaf, 0x30, 0x61, 0x91, 0x02, 0x2e, 0x12, 0x23, - 0xcd, 0x35, 0x92, 0x11, 0xc0, 0xff, 0xf1, 0x50, 0x80, 0x2f, 0x7f, 0xfc, - 0x21, 0x1a, 0xce, 0xf9, 0x9e, 0xfc, 0x80, 0x92, 0xa4, 0xa8, 0xb2, 0x51, - 0x2c, 0x14, 0x3b, 0xc7, 0xc6, 0xfd, 0xae, 0xfd, 0xfc, 0xce, 0xee, 0xbc, - 0xd4, 0xcb, 0xe7, 0xaa, 0x59, 0x93, 0x8e, 0x68, 0x2f, 0x04, 0x94, 0x1c, - 0xfd, 0x92, 0xf4, 0x96, 0xe6, 0xb8, 0x1d, 0x55, 0x3e, 0x29, 0xc5, 0x33, - 0x4b, 0x82, 0x4d, 0xa6, 0xe9, 0xab, 0xaa, 0x70, 0x0f, 0x4f, 0x0e, 0x64, - 0x83, 0x01, 0x44, 0x8e, 0xff, 0xa2, 0x98, 0x78, 0xa8, 0x2e, 0x45, 0x91, - 0x26, 0xd2, 0x9a, 0x08, 0xa3, 0x38, 0x03, 0xfa, 0x91, 0x71, 0x93, 0xe5, - 0xf2, 0xf5, 0xb4, 0x20, 0x51, 0x8a, 0xd0, 0xc2, 0x68, 0x34, 0x60, 0x60, - 0x73, 0x05, 0xb7, 0xe0, 0xd7, 0x28, 0x1c, 0x0d, 0x65, 0x28, 0xb9, 0xda, - 0xa4, 0x13, 0x0d, 0x40, 0x77, 0xaa, 0x2f, 0x36, 0x1b, 0x8a, 0xe0, 0x62, - 0x2a, 0x28, 0x4a, 0xa7, 0x44, 0x89, 0x38, 0xa0, 0x9f, 0x98, 0xff, 0x14, - 0xc7, 0x6b, 0xc0, 0xfb, 0xdd, 0x91, 0xff, 0x08, 0x57, 0x30, 0x00, 0xf4, - 0xed, 0x87, 0x9b, 0x9d, 0xbc, 0xd3, 0x1f, 0x3d, 0x95, 0x33, 0x5b, 0x89, - 0x6d, 0x3b, 0x90, 0x40, 0x68, 0x76, 0x97, 0x80, 0x9f, 0x15, 0x00, 0x6a, - 0x9e, 0xbe, 0x01, 0xac, 0xba, 0xfe, 0x85, 0x2d, 0x2c, 0x3a, 0x3b, 0xa4, - 0xcc, 0xc2, 0xda, 0x43, 0xb3, 0xb3, 0xfd, 0x92, 0xa2, 0xaa, 0x5d, 0x37, - 0xce, 0xe2, 0x87, 0x83, 0x79, 0x02, 0xa1, 0x1a, 0x02, 0x0c, 0x64, 0xcd, - 0x63, 0x11, 0x51, 0xe7, 0x54, 0x26, 0x2b, 0xf9, 0xab, 0xa4, 0x4b, 0x6e, - 0x3f, 0x05, 0xe9, 0x5f, 0x0c, 0x9f, 0x1a, 0x6d, 0x62, 0x84, 0x75, 0x12, - 0x19, 0xeb, 0x83, 0x40, 0x7d, 0x2a, 0x12, 0x05, 0x9a, 0xc8, 0xc3, 0xe6, - 0xb2, 0x7d, 0xa4, 0xda, 0x3e, 0xc4, 0xb4, 0x1c, 0xe4, 0xd5, 0xb4, 0x0d, - 0x24, 0xb9, 0x09, 0x03, 0x43, 0xfb, 0xde, 0x3f, 0x1e, 0x3d, 0xae, 0xfb, - 0xf6, 0xaf, 0x57, 0xf6, 0xfe, 0x53, 0x2f, 0x35, 0x4b, 0x25, 0xb3, 0x9b, - 0x55, 0xeb, 0xbb, 0xa9, 0x2e, 0xa8, 0x3b, 0x3a, 0x7b, 0xdf, 0x8f, 0x95, - 0x69, 0xe8, 0x6a, 0xde, 0x0f, 0xa2, 0x57, 0x63, 0xbb, 0xe9, 0xd1, 0x87, - 0xfa, 0x9f, 0x37, 0x36, 0x57, 0x86, 0xe1, 0xfb, 0xd6, 0x0c, 0x60, 0xd3, - 0x46, 0xb6, 0x28, 0xbf, 0xf6, 0xfa, 0x80, 0x8e, 0x03, 0x48, 0x63, 0x9c, - 0xa2, 0x0e, 0x64, 0xd4, 0x70, 0x38, 0x47, 0x0d, 0x08, 0xf4, 0x0a, 0xe3, - 0xfd, 0x37, 0x88, 0x73, 0xb4, 0x38, 0x81, 0x1d, 0xed, 0x00, 0xc2, 0xe8, - 0xdc, 0xc7, 0x74, 0xfb, 0xc0, 0x6a, 0x3e, 0x43, 0x56, 0x41, 0x3c, 0x8a, - 0xbe, 0x3a, 0xe0, 0x30, 0x26, 0xec, 0x56, 0x98, 0x2c, 0x15, 0x7f, 0x80, - 0xff, 0xf1, 0x50, 0x80, 0x2b, 0x5f, 0xfc, 0x21, 0x1a, 0xc8, 0xdf, 0xfb, - 0xfb, 0x00, 0x22, 0xa1, 0x85, 0x33, 0x10, 0x8c, 0x14, 0x83, 0xed, 0xd7, - 0x17, 0xad, 0x7e, 0x7f, 0x0f, 0xcf, 0xb6, 0x7b, 0x1b, 0xbd, 0xf5, 0x52, - 0x56, 0xfc, 0xdf, 0x6c, 0x04, 0xe3, 0x9b, 0xaa, 0x94, 0x22, 0x5a, 0xe8, - 0xd5, 0xdf, 0x78, 0xfd, 0xc6, 0x8b, 0xe9, 0xc3, 0x50, 0xd6, 0x38, 0xba, - 0x58, 0x20, 0x93, 0x8c, 0x68, 0x4f, 0xbc, 0x7a, 0xe5, 0x51, 0xdd, 0xea, - 0xf2, 0xdc, 0x6b, 0xaa, 0x8f, 0x1e, 0x5c, 0x6d, 0xe0, 0x17, 0xbe, 0x3b, - 0x67, 0x41, 0x67, 0xcb, 0xe0, 0xa0, 0x24, 0x02, 0xf4, 0xc2, 0x48, 0x10, - 0xd7, 0xfb, 0x52, 0x21, 0x01, 0xc3, 0x45, 0xf4, 0x4d, 0x0a, 0x6d, 0x09, - 0xfa, 0x1c, 0x8a, 0xd7, 0x94, 0x2b, 0x5f, 0xde, 0x40, 0x57, 0xae, 0xba, - 0x68, 0xfb, 0xc7, 0x60, 0xbd, 0x83, 0x02, 0x87, 0x1d, 0x0a, 0xda, 0x6f, - 0x05, 0x7a, 0xcf, 0xa4, 0x65, 0x87, 0x36, 0x13, 0x4b, 0x56, 0xcc, 0x28, - 0xd5, 0x51, 0xaa, 0x11, 0xd2, 0x96, 0x9e, 0x17, 0x2b, 0x9a, 0x9f, 0x5e, - 0xe0, 0xea, 0xc9, 0x18, 0xeb, 0x6d, 0x67, 0x91, 0x0c, 0x3f, 0x20, 0xcc, - 0xe9, 0x56, 0x68, 0x93, 0xd7, 0xa6, 0xaf, 0x1d, 0xf0, 0x3d, 0x55, 0xde, - 0xd4, 0x52, 0xa3, 0xf0, 0x5e, 0x74, 0xb7, 0xab, 0xe1, 0xc9, 0x21, 0x27, - 0x54, 0x1b, 0xa3, 0x1c, 0xb2, 0xe4, 0x0e, 0xf3, 0x02, 0x11, 0x3b, 0x40, - 0x35, 0x56, 0xe6, 0x75, 0xeb, 0xd1, 0x7a, 0x3e, 0x4a, 0x9a, 0x77, 0x78, - 0x68, 0x21, 0x0d, 0x7b, 0x58, 0x77, 0xb7, 0x6e, 0x1e, 0x76, 0xc2, 0xea, - 0x77, 0x37, 0x4f, 0x09, 0x75, 0x31, 0xd0, 0x5e, 0xf5, 0x79, 0x5e, 0xb5, - 0xf9, 0xfc, 0x3f, 0x3e, 0xd9, 0xec, 0x78, 0xeb, 0x7d, 0x54, 0x95, 0x2e, - 0xea, 0xb6, 0x22, 0xf5, 0xdd, 0xa9, 0x03, 0x1a, 0x8e, 0xfa, 0xff, 0xde, - 0xb4, 0xce, 0x31, 0x2c, 0xf4, 0x0e, 0xb5, 0x4b, 0x64, 0x9c, 0x68, 0x7c, - 0xee, 0x56, 0xd4, 0xaf, 0x93, 0xa3, 0xa3, 0x55, 0x93, 0x0c, 0xba, 0x9d, - 0xb8, 0x32, 0xca, 0x39, 0x1e, 0xcb, 0x01, 0x35, 0xe9, 0x24, 0xce, 0x55, - 0x04, 0xa1, 0xc1, 0x1c, 0xbc, 0x04, 0x60, 0xee, 0x01, 0xbe, 0x6a, 0xab, - 0x98, 0x96, 0x72, 0x72, 0x72, 0x01, 0x6a, 0x01, 0x4d, 0xc8, 0x64, 0x9d, - 0x5e, 0x22, 0x9d, 0x4a, 0x84, 0x90, 0x70, 0xf9, 0x85, 0x05, 0xb7, 0x0c, - 0x11, 0xe3, 0x5f, 0xca, 0x5c, 0x0d, 0x0d, 0xaf, 0xa2, 0x38, 0xff, 0xf1, - 0x50, 0x80, 0x2e, 0x5f, 0xfc, 0x21, 0x2a, 0xcf, 0xdf, 0xb8, 0xfd, 0xbf, - 0xfc, 0xa2, 0xb0, 0xc3, 0x19, 0xa8, 0x85, 0x0a, 0x21, 0xef, 0xaf, 0x69, - 0x7a, 0xf5, 0xf1, 0x7e, 0xf7, 0x9e, 0x61, 0x55, 0xaa, 0x48, 0xcd, 0x30, - 0xa4, 0x54, 0x71, 0xca, 0x50, 0xcd, 0x76, 0xf0, 0xfc, 0x3a, 0xfc, 0xf8, - 0xd4, 0xbb, 0x26, 0xfd, 0x6d, 0x51, 0x5c, 0x0f, 0x20, 0x9d, 0x65, 0x96, - 0xae, 0x4e, 0x6a, 0x90, 0x23, 0x97, 0x76, 0x9e, 0x9d, 0xe3, 0xba, 0xef, - 0xbf, 0xb3, 0x64, 0xcd, 0xbf, 0x1d, 0xf2, 0x60, 0xfa, 0xb4, 0xb2, 0xe3, - 0xfa, 0x17, 0x0c, 0x33, 0xab, 0xdb, 0xbe, 0xe0, 0x38, 0xb2, 0xcc, 0x98, - 0x04, 0x1a, 0x0b, 0xdc, 0xe6, 0xeb, 0x54, 0x07, 0x8b, 0x8f, 0x36, 0x7c, - 0x15, 0x84, 0x1d, 0x04, 0x5a, 0x77, 0x62, 0xa2, 0xeb, 0x8e, 0xfa, 0xe8, - 0x43, 0x64, 0xc8, 0x1e, 0xd5, 0x68, 0x1c, 0x84, 0xfa, 0xde, 0x6a, 0x9b, - 0x24, 0x2b, 0x98, 0x3d, 0x8d, 0x23, 0x4f, 0x07, 0x92, 0xbd, 0x82, 0x83, - 0x10, 0x0e, 0xba, 0x11, 0x68, 0xfd, 0x53, 0x8a, 0xfb, 0x79, 0xfa, 0x88, - 0xbe, 0x48, 0x0e, 0xcb, 0x1b, 0xd6, 0x5d, 0xc0, 0x9a, 0x7c, 0x60, 0x13, - 0x21, 0x17, 0x29, 0x3e, 0x1d, 0x03, 0xbd, 0xd5, 0x1d, 0x45, 0x5a, 0x71, - 0x51, 0xe8, 0x20, 0xbd, 0xa7, 0x81, 0x78, 0xc5, 0xd6, 0x7a, 0x2f, 0x5e, - 0x64, 0x22, 0x53, 0x85, 0x05, 0x49, 0xf9, 0x37, 0xce, 0x87, 0x51, 0xff, - 0x4c, 0xd3, 0xe1, 0x95, 0x0b, 0x6f, 0x37, 0xb9, 0x7b, 0x6b, 0xf2, 0x0e, - 0x60, 0x63, 0xc7, 0x0f, 0x4d, 0x48, 0x80, 0xbd, 0x5e, 0xbc, 0xaf, 0x13, - 0x85, 0x63, 0x6b, 0x9b, 0xf5, 0xeb, 0x4e, 0xa5, 0x45, 0x44, 0x54, 0x1d, - 0xd0, 0x5d, 0xdd, 0xdf, 0x58, 0x9c, 0xab, 0xe5, 0x73, 0x6e, 0x73, 0x51, - 0xdd, 0xe8, 0xa1, 0xac, 0x94, 0x37, 0x3d, 0xef, 0xaf, 0x69, 0x7a, 0xf5, - 0xf1, 0x7e, 0xf6, 0xe2, 0x37, 0xac, 0xd5, 0x24, 0x43, 0x73, 0x25, 0x49, - 0x57, 0x5a, 0xdd, 0xd0, 0x11, 0x8a, 0xf8, 0x8c, 0xb5, 0x8d, 0x36, 0xbb, - 0x06, 0x96, 0xfa, 0x75, 0x59, 0x01, 0x21, 0xe7, 0x2d, 0x35, 0x74, 0x00, - 0xa7, 0x01, 0x8a, 0x39, 0x32, 0x83, 0x20, 0x93, 0x75, 0xa5, 0x68, 0x6b, - 0xcf, 0xa7, 0xde, 0x7c, 0xa8, 0xc2, 0xdd, 0xd8, 0x7a, 0xe0, 0x9c, 0x24, - 0x06, 0xb2, 0xa5, 0x3b, 0xa7, 0x7b, 0x94, 0x11, 0x56, 0xa5, 0xc9, 0x44, - 0xef, 0x20, 0xbc, 0x71, 0xa3, 0x7f, 0xb7, 0x62, 0x56, 0x80, 0x1a, 0xa8, - 0x82, 0xff, 0x95, 0xcf, 0xc8, 0x14, 0xee, 0xf3, 0xaa, 0xc7, 0x30, 0xc1, - 0x5a, 0xff, 0xa1, 0xae, 0x00, 0xd5, 0x50, 0xe0, 0xff, 0xf1, 0x50, 0x80, - 0x33, 0x9f, 0xfc, 0x21, 0x4c, 0x6c, 0xd4, 0x07, 0x81, 0xe4, 0x02, 0x60, - 0x4f, 0xd1, 0x4a, 0x57, 0x4d, 0x95, 0xed, 0x8d, 0x96, 0xf2, 0xcc, 0x78, - 0x00, 0x1e, 0x63, 0xad, 0xd7, 0xc3, 0x35, 0x3e, 0x3f, 0xd5, 0xc7, 0x5b, - 0xeb, 0x4e, 0x66, 0xb8, 0xfe, 0xef, 0xdf, 0xcf, 0x33, 0xcf, 0x3c, 0xdf, - 0x90, 0xf4, 0x85, 0xa6, 0x96, 0x07, 0x6f, 0x45, 0xff, 0x31, 0xaa, 0x3f, - 0x19, 0x77, 0x7d, 0x2d, 0x9d, 0x41, 0x02, 0xd5, 0x22, 0x1b, 0x1c, 0x2c, - 0x5d, 0x84, 0xbe, 0xb3, 0x5f, 0x0a, 0x88, 0x4a, 0x52, 0x96, 0x0c, 0x89, - 0xb5, 0x67, 0x70, 0x07, 0xbe, 0xc8, 0xad, 0x20, 0x57, 0x64, 0x73, 0xd5, - 0x6c, 0x1d, 0xf5, 0x77, 0x7b, 0x2e, 0x6c, 0xc0, 0x42, 0x6b, 0xaa, 0x76, - 0x8e, 0xdc, 0xe6, 0xd4, 0x38, 0xf5, 0x4f, 0x63, 0x5a, 0xcd, 0xec, 0x16, - 0xb5, 0x63, 0x6c, 0x79, 0x62, 0xce, 0x86, 0x3d, 0xda, 0x90, 0x2d, 0xbf, - 0x3d, 0xe0, 0x05, 0x44, 0x76, 0x57, 0xd2, 0x1f, 0x15, 0x28, 0xd2, 0x59, - 0x12, 0xc6, 0x90, 0x50, 0x32, 0x30, 0x59, 0x8c, 0xac, 0x02, 0x44, 0xbc, - 0x4c, 0xd4, 0x16, 0x06, 0xfb, 0x91, 0xd1, 0xd0, 0x2f, 0x1c, 0xb0, 0x2d, - 0x4a, 0xc0, 0xb9, 0x65, 0x39, 0x16, 0x2d, 0x59, 0xb9, 0x30, 0x29, 0xaa, - 0xae, 0x12, 0x3b, 0xe2, 0x60, 0x68, 0xe0, 0x62, 0x60, 0x07, 0x56, 0x2c, - 0x6b, 0x5c, 0x80, 0x24, 0xec, 0x42, 0x48, 0x6b, 0x64, 0x86, 0xd8, 0xe2, - 0x90, 0x44, 0x20, 0x19, 0xf8, 0xf8, 0xe7, 0x9e, 0x72, 0xcb, 0x00, 0x55, - 0xb2, 0x52, 0x28, 0xaa, 0xe6, 0xa0, 0x95, 0x9b, 0x9a, 0xaf, 0x2e, 0x8b, - 0xfa, 0x5b, 0x69, 0x31, 0xe3, 0xa5, 0x73, 0x83, 0x1a, 0xab, 0x99, 0x42, - 0x64, 0xc2, 0x6e, 0x02, 0x71, 0x20, 0xc5, 0x06, 0x2a, 0x2b, 0x08, 0x08, - 0x6b, 0xa7, 0xe5, 0xd7, 0xcf, 0xc0, 0xcc, 0xf1, 0x64, 0x64, 0x18, 0x90, - 0x4f, 0xb2, 0x19, 0xb9, 0xb8, 0xe2, 0xc7, 0x9b, 0x24, 0xbf, 0x6c, 0xb7, - 0x1e, 0xd8, 0xaa, 0x54, 0xa7, 0xa3, 0x26, 0xa4, 0x3a, 0xfb, 0xef, 0xef, - 0xbe, 0xce, 0x3f, 0xb2, 0x9e, 0xc9, 0x79, 0x34, 0xf7, 0x9e, 0x75, 0xe6, - 0x6f, 0xf6, 0xf1, 0x6f, 0xf4, 0x1b, 0x2d, 0x16, 0x91, 0x35, 0x8f, 0xda, - 0x50, 0x91, 0x54, 0x4b, 0x72, 0xa0, 0x20, 0x20, 0x10, 0xdc, 0x50, 0x8b, - 0x5f, 0xe7, 0x9f, 0x69, 0xa2, 0xee, 0x4b, 0x80, 0x44, 0x8a, 0xd7, 0xeb, - 0xf6, 0x6d, 0xf7, 0x2b, 0xfa, 0xfd, 0x2f, 0xfe, 0x8f, 0x70, 0xf7, 0x0d, - 0xdb, 0xbe, 0xf4, 0xc9, 0x24, 0xd0, 0x84, 0x26, 0x24, 0xf3, 0xb5, 0x97, - 0xea, 0xbe, 0xe3, 0xb9, 0x2d, 0x4e, 0x57, 0x0c, 0x6a, 0x6d, 0xbb, 0x81, - 0x8d, 0x80, 0x00, 0x15, 0x03, 0x9d, 0x0b, 0x01, 0xec, 0xfe, 0x9b, 0x02, - 0xd3, 0x3a, 0x07, 0x79, 0xad, 0xa6, 0x6f, 0x0e, 0x35, 0xd4, 0x17, 0xfa, - 0xec, 0xc3, 0xd6, 0x6a, 0xaa, 0x3d, 0x9d, 0x1c, 0x32, 0xef, 0xca, 0x38, - 0xff, 0xf1, 0x50, 0x80, 0x2e, 0x9f, 0xfc, 0x21, 0x7a, 0xcd, 0xf8, 0x10, - 0x20, 0x00, 0x00, 0xa8, 0xb1, 0x42, 0xd9, 0x89, 0x16, 0x08, 0xf3, 0x7a, - 0xaf, 0xb7, 0xe3, 0xdb, 0xf4, 0xd6, 0xfd, 0xac, 0x4d, 0xdd, 0x12, 0x15, - 0x50, 0x55, 0xa1, 0x53, 0xc8, 0xd6, 0x7e, 0xa3, 0xa5, 0x26, 0x65, 0xf0, - 0xb3, 0xbb, 0xf4, 0xda, 0xcb, 0xd0, 0xd6, 0x11, 0xa9, 0x91, 0xb9, 0xbb, - 0xa9, 0x92, 0x27, 0x2d, 0xdb, 0x09, 0xcf, 0xdd, 0x38, 0x27, 0xab, 0xd6, - 0x76, 0x47, 0x83, 0x46, 0xdc, 0xa5, 0xf0, 0xc2, 0xa8, 0x0b, 0x7e, 0x15, - 0x11, 0xc0, 0x8f, 0x91, 0x24, 0xae, 0xb3, 0xb1, 0xfd, 0xfc, 0x44, 0x89, - 0x27, 0x75, 0x6c, 0xf5, 0x35, 0x4a, 0x45, 0x82, 0x62, 0x7b, 0x0a, 0xbb, - 0x81, 0x9e, 0x1a, 0xb3, 0xb1, 0xea, 0x2d, 0xb7, 0x4c, 0x6c, 0xa9, 0xb8, - 0xc8, 0x3c, 0x8d, 0x9f, 0x42, 0xa7, 0x1a, 0x6f, 0x45, 0x04, 0x3d, 0x3c, - 0x8e, 0x89, 0xcc, 0x2e, 0x81, 0x74, 0x27, 0xc9, 0xe0, 0x8b, 0xa8, 0xb2, - 0xae, 0x80, 0x49, 0xee, 0x01, 0x72, 0x44, 0x62, 0x13, 0x48, 0x7b, 0xff, - 0x1d, 0x8b, 0xe0, 0x86, 0x60, 0xdd, 0x12, 0xc9, 0xcb, 0xca, 0x43, 0xbc, - 0x5e, 0x1b, 0xc1, 0x7e, 0x96, 0xff, 0x16, 0xdf, 0xf7, 0xcb, 0x2c, 0xdb, - 0x78, 0xe8, 0x15, 0xe2, 0xa9, 0xbd, 0xfc, 0x3d, 0x7a, 0xa6, 0x81, 0x6d, - 0x56, 0x9b, 0xcf, 0x0a, 0xab, 0x6e, 0x75, 0xfa, 0x70, 0xc0, 0xff, 0xc5, - 0xe1, 0x4c, 0x5a, 0xa1, 0x42, 0x7a, 0x56, 0x06, 0x02, 0xdf, 0xbd, 0x69, - 0x49, 0x66, 0xc0, 0x08, 0x02, 0x6b, 0xb8, 0xd2, 0x13, 0xee, 0x01, 0x62, - 0x80, 0x10, 0x89, 0x8a, 0xd5, 0x38, 0x65, 0x83, 0x05, 0x25, 0xc7, 0xd7, - 0xb3, 0x78, 0x31, 0x5d, 0x8a, 0x51, 0x99, 0x2d, 0x0d, 0x62, 0x03, 0x01, - 0x69, 0x42, 0x62, 0x60, 0xa8, 0xb1, 0x35, 0x10, 0xde, 0xf6, 0x5f, 0x9b, - 0xd5, 0x7d, 0xbc, 0xeb, 0xe7, 0xcf, 0xaf, 0x8f, 0x8f, 0x5b, 0xba, 0xb9, - 0xd7, 0x89, 0x50, 0xe2, 0xb3, 0x38, 0x94, 0xa8, 0xac, 0xb8, 0x00, 0xed, - 0x39, 0x2b, 0x0d, 0x02, 0x6d, 0x7a, 0xc3, 0x42, 0xb7, 0x10, 0xf8, 0xfd, - 0x21, 0x34, 0x30, 0x53, 0xfb, 0x5b, 0x6e, 0x32, 0x02, 0x7b, 0xf8, 0xce, - 0x97, 0xd2, 0x38, 0x6a, 0x35, 0x3a, 0xe4, 0x1b, 0x67, 0xd0, 0x97, 0x28, - 0x1f, 0x3e, 0xc4, 0x0b, 0x01, 0x8e, 0xe5, 0x10, 0xbc, 0x0c, 0x46, 0x7a, - 0xa8, 0x02, 0x2a, 0xeb, 0xeb, 0xa4, 0x8e, 0xcd, 0x7e, 0x53, 0xd9, 0x59, - 0x6a, 0x1b, 0x9f, 0x9a, 0xbd, 0x30, 0xf9, 0x6b, 0x74, 0xd7, 0x1a, 0xc4, - 0x8d, 0x92, 0x35, 0xc6, 0xf1, 0xb2, 0x40, 0x14, 0x14, 0x72, 0x8b, 0x80, - 0xff, 0xf1, 0x50, 0x80, 0x2b, 0x3f, 0xfc, 0x21, 0x1a, 0xce, 0xf4, 0xac, - 0xf9, 0x04, 0x00, 0xa9, 0xb1, 0x42, 0xd9, 0x48, 0xf2, 0x29, 0xed, 0x57, - 0x27, 0xe9, 0xfa, 0xfd, 0x67, 0x8f, 0x5f, 0x7b, 0x65, 0xd6, 0x5d, 0x5a, - 0x4a, 0xb6, 0xe0, 0x10, 0xa5, 0x8c, 0x8f, 0xd1, 0x3e, 0x9d, 0x2f, 0x17, - 0x5e, 0x2b, 0xbd, 0x0b, 0xfd, 0x5b, 0xdb, 0x11, 0x72, 0x47, 0x95, 0xa4, - 0x7d, 0x02, 0xc2, 0xa9, 0x4e, 0x7c, 0x7a, 0x79, 0xae, 0x5e, 0xaf, 0x89, - 0xef, 0xf2, 0x48, 0x4b, 0xad, 0x76, 0xb3, 0x29, 0x3e, 0xdf, 0xec, 0x41, - 0x64, 0x3c, 0xbd, 0xe3, 0x0d, 0x08, 0x48, 0x81, 0x14, 0xd4, 0x09, 0x4e, - 0xf9, 0x04, 0x9c, 0x2d, 0x26, 0x00, 0x3f, 0x07, 0x00, 0x51, 0x22, 0x37, - 0x40, 0x76, 0x60, 0xec, 0x06, 0x41, 0xd0, 0x8f, 0x5d, 0x80, 0x10, 0x03, - 0x1c, 0x8f, 0x38, 0xd1, 0x18, 0xc5, 0x2c, 0x92, 0x62, 0x65, 0xaa, 0x41, - 0x56, 0x15, 0xa5, 0x8e, 0x52, 0x38, 0x50, 0x5d, 0x29, 0xea, 0x2c, 0xda, - 0xb2, 0x65, 0xc2, 0x9e, 0xa9, 0xcb, 0xa1, 0xa9, 0x86, 0xf7, 0x17, 0x6a, - 0xcd, 0x6e, 0x54, 0xec, 0x3b, 0xcb, 0x45, 0x06, 0x2c, 0x41, 0x8b, 0x12, - 0x76, 0x70, 0xe3, 0x4e, 0x54, 0x87, 0x40, 0xc5, 0xd1, 0x3c, 0x64, 0xcd, - 0xaf, 0x7c, 0x99, 0x04, 0x82, 0x85, 0x3f, 0x30, 0x77, 0xbf, 0x47, 0x38, - 0x4d, 0x56, 0x8e, 0xf1, 0xb5, 0xf5, 0xe7, 0x5c, 0x3e, 0x58, 0xd2, 0x62, - 0x0e, 0xae, 0x2c, 0x6b, 0xde, 0x58, 0x82, 0x75, 0x32, 0xef, 0xf6, 0xef, - 0xf3, 0xc0, 0x75, 0xb2, 0xaa, 0x64, 0xf2, 0x32, 0x97, 0x68, 0xa9, 0x96, - 0xc1, 0x19, 0x08, 0x3f, 0x79, 0xd4, 0xb9, 0x3e, 0xdf, 0x7e, 0x3f, 0x6f, - 0xbf, 0xcf, 0xeb, 0x5a, 0xcc, 0x8b, 0xab, 0x54, 0x95, 0xc6, 0xe7, 0x8b, - 0xba, 0x56, 0x8c, 0x02, 0xb4, 0xf7, 0xf9, 0x1a, 0xcb, 0x8f, 0x18, 0xe5, - 0x17, 0x9e, 0x9f, 0xb5, 0x65, 0x7a, 0xdd, 0xf9, 0x5e, 0xa9, 0xd6, 0xaa, - 0x4e, 0x67, 0x83, 0x25, 0xf4, 0x04, 0x69, 0x16, 0xe0, 0x10, 0x54, 0x92, - 0xe0, 0xd6, 0xcb, 0x94, 0x04, 0xe3, 0xd7, 0x86, 0x16, 0xf5, 0x3b, 0x9c, - 0x9c, 0x50, 0xc9, 0x02, 0xe6, 0x30, 0x00, 0x00, 0x1e, 0xd2, 0x36, 0x4a, - 0x8c, 0x2e, 0x65, 0x03, 0x32, 0x9d, 0x5c, 0x0c, 0x2e, 0xd5, 0x79, 0x11, - 0xd6, 0xa6, 0xf3, 0xcb, 0xfd, 0xa4, 0x75, 0xb9, 0x46, 0x02, 0xea, 0xd2, - 0x19, 0x3e, 0x16, 0x05, 0xd8, 0x15, 0xe7, 0x73, 0xce, 0xff, 0xf1, 0x50, - 0x80, 0x2d, 0xff, 0xfc, 0x21, 0x1a, 0xcf, 0x7e, 0xfc, 0xff, 0x80, 0x00, - 0xa9, 0xb2, 0xc3, 0x18, 0xc9, 0x43, 0xeb, 0x8a, 0xb7, 0xf1, 0xfe, 0x9a, - 0xaf, 0x1e, 0xbf, 0x59, 0x39, 0xe3, 0xbb, 0xb8, 0x10, 0x50, 0x22, 0xa0, - 0x1c, 0x87, 0xdf, 0xfd, 0xba, 0x57, 0x0c, 0x1e, 0x27, 0x0f, 0x39, 0x92, - 0xe0, 0x7d, 0x06, 0x2d, 0xfb, 0x93, 0x79, 0xab, 0x13, 0xb4, 0x72, 0x62, - 0x17, 0xe7, 0xd6, 0xbb, 0xb1, 0x7d, 0xce, 0x62, 0xfc, 0xe0, 0xd7, 0x4b, - 0x75, 0x3c, 0xf4, 0x22, 0xb2, 0x33, 0xac, 0x5c, 0x1c, 0xd0, 0xc8, 0xf6, - 0x58, 0xbf, 0xf2, 0x65, 0xb5, 0x8c, 0xfc, 0x93, 0x89, 0xc5, 0x4c, 0x38, - 0xef, 0x2b, 0xe5, 0x84, 0x2e, 0x0e, 0x9b, 0x7d, 0xd3, 0xa3, 0xa5, 0x40, - 0x31, 0xff, 0xcc, 0x87, 0xc4, 0x35, 0x5b, 0x74, 0x55, 0xea, 0x8c, 0x5b, - 0x59, 0x94, 0x42, 0x89, 0xae, 0xf7, 0x23, 0x93, 0x4d, 0xb1, 0x7e, 0xa1, - 0x28, 0x64, 0x3d, 0x43, 0xa2, 0x41, 0x66, 0xbc, 0xca, 0xfc, 0xa8, 0x02, - 0x8c, 0xf9, 0xc3, 0x5b, 0xe3, 0x40, 0x47, 0x30, 0xf8, 0x7e, 0x31, 0x1b, - 0x62, 0x8a, 0x40, 0x44, 0xfa, 0x90, 0x2c, 0x99, 0x2c, 0xa5, 0xa9, 0xaf, - 0x7b, 0xcd, 0x14, 0x8a, 0x9c, 0xf4, 0xf9, 0x14, 0x5d, 0x13, 0xae, 0x33, - 0xf8, 0x01, 0x52, 0x20, 0xd0, 0x01, 0xe8, 0xa7, 0xe4, 0xed, 0x89, 0x9e, - 0x01, 0xb3, 0x9b, 0xa1, 0xef, 0x53, 0x19, 0xa2, 0x2d, 0xc3, 0x95, 0x75, - 0x08, 0xcf, 0x9a, 0xc6, 0x60, 0xb7, 0x37, 0x5b, 0xe8, 0x83, 0xb9, 0xb2, - 0x05, 0x2a, 0x2e, 0xd9, 0xfa, 0x3e, 0x55, 0x7a, 0xc0, 0x4e, 0xfa, 0x01, - 0xc8, 0x68, 0x74, 0x10, 0x84, 0x1e, 0xe0, 0xec, 0x66, 0x68, 0xd0, 0x4a, - 0x21, 0x4c, 0xaf, 0x97, 0xe5, 0x61, 0x91, 0xf5, 0x31, 0x64, 0x37, 0xbc, - 0xfa, 0xb9, 0x6f, 0xe3, 0xfd, 0x35, 0xfd, 0x7e, 0x3d, 0x7e, 0xb2, 0x73, - 0x25, 0x5c, 0x08, 0xa6, 0xc2, 0xe1, 0x32, 0xc1, 0x70, 0x3e, 0xd7, 0x38, - 0xad, 0x3f, 0x6c, 0x59, 0x8e, 0x17, 0x09, 0x59, 0x7e, 0x19, 0x76, 0x51, - 0x5c, 0x67, 0xfa, 0x7c, 0x63, 0x39, 0x9c, 0xbe, 0xbf, 0x15, 0xac, 0x12, - 0x5c, 0x18, 0x09, 0xa2, 0x77, 0x96, 0x13, 0x1e, 0xfe, 0x31, 0xac, 0x82, - 0xb0, 0xf7, 0x3a, 0x41, 0x0f, 0xa2, 0xb3, 0x2b, 0x4c, 0x21, 0x8a, 0x43, - 0x82, 0x1a, 0x99, 0xae, 0x00, 0x0e, 0x5b, 0x58, 0x5b, 0x57, 0x3b, 0x1f, - 0xe6, 0xe7, 0xd8, 0x45, 0xa9, 0xb6, 0x5b, 0xd4, 0xbf, 0x34, 0xdb, 0x1d, - 0x28, 0x8d, 0x28, 0x57, 0x39, 0xa3, 0xf5, 0xda, 0xee, 0xf8, 0x66, 0xeb, - 0x82, 0x5a, 0x41, 0xdc, 0xff, 0xf1, 0x50, 0x80, 0x2f, 0xff, 0xfc, 0x21, - 0x1a, 0xcf, 0x78, 0xb9, 0xbf, 0xaf, 0x9f, 0xa8, 0xb1, 0xc4, 0x58, 0xa9, - 0x12, 0x09, 0xd7, 0x9d, 0xf9, 0xaf, 0xdb, 0xfc, 0xb7, 0x93, 0x89, 0x39, - 0xd7, 0x3a, 0xb5, 0x49, 0x13, 0x61, 0x50, 0x00, 0x49, 0x7b, 0x57, 0x3b, - 0x02, 0x57, 0x05, 0xee, 0xcf, 0xdf, 0xf9, 0x3d, 0xfa, 0x7c, 0xc9, 0xb5, - 0x7b, 0x49, 0x51, 0xb6, 0x84, 0x21, 0xa3, 0x13, 0x87, 0xb0, 0x70, 0x3c, - 0x8b, 0xe2, 0x0e, 0x85, 0x8a, 0xf8, 0x74, 0x09, 0x0e, 0x88, 0x40, 0xca, - 0x43, 0x44, 0x07, 0xdd, 0x12, 0xe4, 0x5f, 0x52, 0xf0, 0x67, 0x19, 0xd1, - 0xd4, 0x73, 0x52, 0xaf, 0x7c, 0x56, 0x27, 0x7a, 0x5b, 0xd7, 0x00, 0x28, - 0x94, 0x4a, 0xe1, 0x4f, 0x34, 0x53, 0x8a, 0x81, 0xc4, 0x87, 0x95, 0xa1, - 0x4d, 0x04, 0x0b, 0xa4, 0xe2, 0xb1, 0x54, 0x7d, 0x4f, 0xc6, 0xe9, 0xb5, - 0x43, 0x97, 0xec, 0x0a, 0xf7, 0x49, 0x8c, 0x7c, 0x60, 0x59, 0xda, 0x89, - 0x15, 0x5f, 0x8a, 0x30, 0x32, 0x73, 0x4a, 0x66, 0xa9, 0x93, 0x21, 0x1a, - 0xa8, 0x09, 0x5f, 0x01, 0xb1, 0x16, 0xe9, 0x2f, 0xf7, 0xca, 0xb0, 0x83, - 0x81, 0x39, 0x32, 0xb5, 0xa0, 0x1d, 0xf2, 0x0c, 0x60, 0x6f, 0x40, 0xa3, - 0x0f, 0xb7, 0xf2, 0x1a, 0x7f, 0x3d, 0x4e, 0x9b, 0x54, 0x11, 0xd7, 0x54, - 0x50, 0xff, 0x6f, 0x58, 0x6c, 0x62, 0xf4, 0xaf, 0x20, 0x0d, 0xe4, 0xdf, - 0xc4, 0x56, 0xb8, 0x21, 0xc4, 0xfc, 0xfc, 0x35, 0x28, 0x0f, 0x2b, 0xc5, - 0x88, 0x8c, 0x64, 0xac, 0x3f, 0xc3, 0xb5, 0x68, 0x1b, 0x78, 0x49, 0xde, - 0x4f, 0xae, 0x6e, 0x51, 0xab, 0xd3, 0xae, 0xff, 0x7d, 0x0e, 0x7b, 0x39, - 0xd3, 0x26, 0x05, 0x03, 0xc1, 0xc9, 0x4a, 0xaa, 0x63, 0xcf, 0xc3, 0x3b, - 0xbf, 0x7c, 0x60, 0x93, 0x91, 0x71, 0x8a, 0x67, 0xa8, 0x84, 0x58, 0x61, - 0x8c, 0x34, 0x27, 0x3c, 0xea, 0xe7, 0x9a, 0xfd, 0xbf, 0x79, 0xf6, 0xf8, - 0xfd, 0x3f, 0xcc, 0x9e, 0xb5, 0x5a, 0xce, 0x2a, 0x48, 0x9c, 0xcc, 0x93, - 0x2e, 0x52, 0x2c, 0x0e, 0x71, 0xe3, 0x02, 0x0c, 0x3a, 0xd0, 0x08, 0x47, - 0x38, 0xe1, 0xb2, 0xec, 0xd9, 0xa6, 0xa7, 0x19, 0xcb, 0xc3, 0x9a, 0x03, - 0xa6, 0xb7, 0x3a, 0xea, 0x31, 0x06, 0xa4, 0xb1, 0x47, 0x48, 0x90, 0x0c, - 0x83, 0x06, 0x47, 0x7f, 0x46, 0xe6, 0xd1, 0x38, 0x39, 0x14, 0x08, 0x31, - 0x02, 0x58, 0xd4, 0xa1, 0x9e, 0x91, 0x6b, 0x19, 0xf6, 0x0e, 0x66, 0x04, - 0x2f, 0xbd, 0xf5, 0x5a, 0xe4, 0x26, 0x0a, 0x0a, 0x44, 0x9c, 0xe6, 0xbd, - 0x6b, 0x9f, 0x8d, 0xf0, 0x3b, 0x86, 0xa0, 0x70, 0xad, 0x2d, 0x6f, 0x7b, - 0x47, 0x9e, 0xc6, 0x4a, 0x98, 0xdc, 0x2b, 0x86, 0x82, 0xf2, 0xf9, 0xca, - 0x19, 0x1d, 0xc0, 0xff, 0xf1, 0x50, 0x80, 0x30, 0x7f, 0xfc, 0x21, 0x2a, - 0xcf, 0xfc, 0xf9, 0xbf, 0x86, 0x10, 0xa6, 0xb2, 0x42, 0xd9, 0x68, 0xe5, - 0x18, 0xe3, 0x3f, 0x19, 0xf3, 0xf8, 0x99, 0x8f, 0x35, 0xad, 0xeb, 0xc7, - 0x12, 0xa2, 0x24, 0xaa, 0xa4, 0xc8, 0x95, 0x2a, 0x6a, 0x84, 0x83, 0xec, - 0x04, 0x86, 0x8a, 0x0c, 0x3b, 0x0d, 0xd7, 0x85, 0xc1, 0xb3, 0xdb, 0x5f, - 0x6c, 0xbe, 0x44, 0xad, 0xbf, 0xb3, 0x53, 0xbf, 0xa3, 0x4d, 0xb2, 0xf5, - 0xea, 0x3f, 0x95, 0x31, 0x86, 0x3b, 0x8c, 0xf6, 0xef, 0xdb, 0xa2, 0x5e, - 0xac, 0x80, 0x2c, 0xb1, 0x4d, 0x56, 0xf8, 0xb8, 0x0b, 0xa3, 0x75, 0xff, - 0x24, 0x12, 0x22, 0x3d, 0x97, 0xa9, 0x53, 0x6d, 0xb3, 0x46, 0xaa, 0xa2, - 0xd4, 0x14, 0xfe, 0xa5, 0x8b, 0x35, 0xe9, 0x45, 0x10, 0x11, 0x61, 0x00, - 0x09, 0xa8, 0xae, 0xa8, 0x5a, 0xde, 0x54, 0xa4, 0x40, 0x3a, 0xa8, 0x26, - 0x13, 0x62, 0x83, 0x38, 0x64, 0x0b, 0xf1, 0x12, 0x5d, 0xc4, 0x16, 0xa7, - 0xba, 0x51, 0xbf, 0x80, 0x67, 0xe0, 0x5e, 0x09, 0x7e, 0xcf, 0xfd, 0x91, - 0x4a, 0xc7, 0x4d, 0xd0, 0x95, 0xbf, 0x81, 0xd5, 0x19, 0x3f, 0x75, 0xd6, - 0xef, 0xdc, 0xca, 0x70, 0xc3, 0xb3, 0x71, 0x68, 0x7d, 0xde, 0x33, 0xea, - 0x88, 0xfc, 0xb3, 0x73, 0x6c, 0x7d, 0xff, 0xcb, 0xb1, 0x6f, 0x23, 0xa5, - 0x17, 0x59, 0xac, 0xb8, 0x6a, 0x5a, 0x30, 0x56, 0xf1, 0xc9, 0x8d, 0x07, - 0x08, 0xe8, 0x7e, 0x3e, 0x93, 0xf4, 0xfa, 0x17, 0x78, 0xdf, 0xec, 0xb8, - 0xc9, 0x6d, 0x7a, 0xfb, 0xa1, 0xc7, 0xdb, 0x80, 0xb8, 0xe7, 0xac, 0x41, - 0x1d, 0x60, 0x11, 0xa6, 0xb7, 0x4f, 0xc6, 0xb1, 0xe7, 0x99, 0x61, 0xe0, - 0x3c, 0x80, 0x79, 0xf8, 0x06, 0x11, 0xef, 0xf4, 0x44, 0xb9, 0x72, 0xa8, - 0xde, 0x7b, 0x7a, 0x60, 0x5b, 0x12, 0x5d, 0x80, 0x9f, 0x97, 0x88, 0x9e, - 0xf3, 0xae, 0x1a, 0xd3, 0xbb, 0x17, 0x4d, 0x11, 0x64, 0xa0, 0xbd, 0xe3, - 0x8c, 0xfc, 0x67, 0xcf, 0xe3, 0x75, 0x1e, 0x6b, 0x5e, 0xb8, 0xdf, 0x15, - 0x22, 0x24, 0xcd, 0xc4, 0xab, 0xa9, 0x4b, 0x4d, 0x05, 0xe0, 0x15, 0x79, - 0x9c, 0xa9, 0x6e, 0x58, 0xd2, 0x52, 0x81, 0x46, 0x5c, 0x6f, 0xd6, 0x43, - 0x93, 0xd5, 0xc5, 0x09, 0xcc, 0x77, 0x1e, 0x94, 0x74, 0x08, 0x8a, 0xb3, - 0x9d, 0x02, 0x42, 0x08, 0x86, 0x54, 0x8d, 0x92, 0x15, 0xd7, 0x7a, 0x97, - 0xfd, 0xff, 0xc2, 0x14, 0x08, 0x2f, 0x24, 0x90, 0xbd, 0x13, 0x05, 0x5e, - 0x7d, 0xdb, 0x05, 0x4c, 0x86, 0xcf, 0xab, 0x05, 0xc7, 0x3d, 0x8e, 0x21, - 0x78, 0x01, 0x99, 0xb9, 0x59, 0x2a, 0x05, 0xd1, 0x28, 0xda, 0xe9, 0x2e, - 0xc4, 0x09, 0x80, 0x10, 0xcc, 0xd5, 0x4a, 0x65, 0xc6, 0x65, 0x16, 0xc0, - 0x45, 0xcb, 0xe5, 0x27, 0xa8, 0xae, 0xff, 0xf1, 0x50, 0x80, 0x33, 0xff, - 0xfc, 0x21, 0x4c, 0x36, 0xe6, 0x03, 0x30, 0x20, 0x04, 0x60, 0x4f, 0xd9, - 0x1b, 0xb4, 0x0d, 0xd6, 0xe2, 0xca, 0x66, 0x28, 0x99, 0x51, 0x23, 0x05, - 0xba, 0x29, 0x0f, 0x6b, 0x75, 0x8f, 0x8f, 0x7f, 0x6c, 0xed, 0xc5, 0x4f, - 0x8b, 0xfe, 0xb3, 0xda, 0x6f, 0x38, 0xf8, 0xdd, 0x6b, 0xcf, 0xfc, 0x57, - 0xfb, 0xf8, 0xae, 0x27, 0x1d, 0x94, 0x26, 0x64, 0xaf, 0xfd, 0x81, 0xf8, - 0x87, 0x8a, 0xa9, 0x68, 0xa3, 0x54, 0x91, 0x61, 0x13, 0x18, 0x2c, 0x3d, - 0xf0, 0xe7, 0x33, 0xd7, 0xd7, 0x5d, 0x97, 0x4b, 0x89, 0x3b, 0x40, 0xc0, - 0xe8, 0x4c, 0x17, 0x9c, 0x79, 0x1a, 0x8b, 0xa5, 0x00, 0x0a, 0x24, 0x28, - 0x36, 0x9b, 0x54, 0xd8, 0x83, 0x7c, 0xb4, 0x3f, 0x80, 0xe9, 0x3a, 0xae, - 0x59, 0xc5, 0x8d, 0xfc, 0x6c, 0x5b, 0xc5, 0xb2, 0x01, 0x60, 0xf8, 0xfc, - 0xe8, 0xa1, 0x52, 0x42, 0xf1, 0x98, 0xe4, 0xaa, 0x24, 0x2d, 0xe7, 0xc7, - 0x94, 0xdc, 0x4b, 0xe6, 0x39, 0x05, 0xc8, 0x88, 0x46, 0x16, 0xfe, 0x47, - 0x72, 0xfa, 0xf2, 0x94, 0xf0, 0x28, 0x1c, 0x24, 0x62, 0xa7, 0x19, 0x91, - 0xdd, 0xd5, 0x52, 0x62, 0x4b, 0xb2, 0x97, 0x77, 0xab, 0xba, 0x4b, 0x42, - 0xbe, 0xe3, 0x70, 0x2f, 0xec, 0x24, 0xec, 0xf8, 0x81, 0x46, 0x03, 0x9b, - 0x8d, 0xd8, 0x06, 0xf7, 0x51, 0xb5, 0x17, 0x1f, 0x18, 0x31, 0x91, 0x22, - 0x43, 0xcf, 0xad, 0xda, 0x56, 0x12, 0x05, 0x99, 0x1a, 0x2e, 0x4b, 0x40, - 0xea, 0x14, 0x8d, 0x1d, 0x72, 0x57, 0x3a, 0xce, 0x92, 0xc0, 0x0e, 0xaa, - 0xbe, 0x1d, 0x96, 0x61, 0xee, 0xac, 0x69, 0x92, 0x5b, 0x05, 0xc6, 0x6b, - 0x4a, 0x25, 0xba, 0xe1, 0x3b, 0x0e, 0xa9, 0x66, 0x48, 0x13, 0xbc, 0xec, - 0x4b, 0xa9, 0x5b, 0xc2, 0x5a, 0xd3, 0x3a, 0x1a, 0x59, 0x2d, 0xa7, 0x39, - 0xa0, 0xde, 0x9a, 0xe4, 0x79, 0x53, 0x20, 0x5e, 0x40, 0x92, 0x80, 0x06, - 0x60, 0x4d, 0x05, 0x44, 0xa4, 0xce, 0x90, 0x68, 0x33, 0xe7, 0xb7, 0x5a, - 0x4b, 0xf6, 0xca, 0x56, 0x5f, 0x12, 0x33, 0xf2, 0xc7, 0xd8, 0x0f, 0x4b, - 0xdd, 0xce, 0x9f, 0x1e, 0xe1, 0xde, 0xb5, 0x78, 0x2f, 0xda, 0x7b, 0x5f, - 0x69, 0x7f, 0xea, 0x5f, 0xd7, 0x1c, 0x65, 0x81, 0xf7, 0x77, 0x6b, 0x72, - 0x0f, 0x94, 0x59, 0x64, 0xc3, 0x94, 0xbe, 0x01, 0xae, 0xce, 0x57, 0xac, - 0x44, 0xbb, 0xac, 0xd3, 0x49, 0x09, 0x14, 0xc7, 0x61, 0x46, 0xda, 0x60, - 0xb9, 0xb4, 0x2b, 0x62, 0xf1, 0xa4, 0xd8, 0xae, 0xa8, 0x60, 0x37, 0x27, - 0x14, 0xd4, 0xb4, 0x59, 0xbe, 0x59, 0x9e, 0x47, 0xfc, 0xd5, 0x1d, 0xdc, - 0x2d, 0x95, 0x90, 0x77, 0xff, 0xd8, 0xfb, 0x77, 0x6e, 0xbf, 0xef, 0xc9, - 0x0a, 0x10, 0x69, 0x79, 0xed, 0x5a, 0x86, 0xe3, 0xb2, 0x32, 0xca, 0x42, - 0x20, 0x20, 0xb5, 0x30, 0x66, 0x39, 0xe9, 0x91, 0xae, 0xb8, 0x3f, 0xa7, - 0xa3, 0x3d, 0x7d, 0x24, 0x49, 0xa8, 0x96, 0xca, 0x94, 0xe6, 0x96, 0x59, - 0x38, 0xff, 0xf1, 0x50, 0x80, 0x2d, 0x9f, 0xfc, 0x21, 0x7a, 0xcf, 0x0c, - 0x11, 0xdf, 0xa8, 0xc0, 0xa9, 0xb1, 0x42, 0xd8, 0xc9, 0x83, 0xef, 0x79, - 0x7f, 0x8f, 0xdb, 0x89, 0x55, 0x9e, 0x57, 0x8a, 0x8a, 0x91, 0x5c, 0x65, - 0x55, 0x01, 0x29, 0x00, 0xdd, 0xd5, 0x9f, 0xf2, 0x3f, 0x83, 0x3e, 0xbe, - 0xa2, 0xca, 0xbe, 0xab, 0x8c, 0xf4, 0xe0, 0x97, 0xb5, 0xc5, 0xb9, 0x09, - 0x91, 0x27, 0x68, 0xa5, 0xe4, 0xc7, 0x11, 0xfc, 0x75, 0xe4, 0xdd, 0xa1, - 0x25, 0x9c, 0x8d, 0xac, 0xc8, 0x12, 0xea, 0x38, 0xde, 0x08, 0x6d, 0x8f, - 0xa0, 0x02, 0xb4, 0x0f, 0xa4, 0x4b, 0xed, 0x36, 0x47, 0xe5, 0x71, 0x72, - 0xce, 0x5f, 0xd2, 0xeb, 0xd9, 0x4a, 0xf6, 0x38, 0x3e, 0x56, 0x85, 0xc4, - 0x15, 0xa9, 0x68, 0x45, 0x0e, 0x79, 0xc8, 0x19, 0x4b, 0x8b, 0x0a, 0xed, - 0x27, 0x2e, 0x30, 0x74, 0x0e, 0x4f, 0x0c, 0x85, 0x41, 0x52, 0x3d, 0x2c, - 0x63, 0x97, 0x94, 0xc3, 0x0f, 0x0e, 0xaf, 0xe1, 0x32, 0x3b, 0x44, 0x2b, - 0x47, 0x52, 0x07, 0x26, 0x9f, 0x7f, 0x62, 0xad, 0xa8, 0x7a, 0xab, 0x7b, - 0x8e, 0x8b, 0x47, 0x9a, 0x7f, 0x84, 0x9e, 0x54, 0x83, 0x97, 0xeb, 0x64, - 0x72, 0xcb, 0xd5, 0xa7, 0xf9, 0x5f, 0xbd, 0x82, 0x80, 0x93, 0xb4, 0x0b, - 0xa0, 0xc3, 0xac, 0x3e, 0x00, 0x6f, 0xee, 0x47, 0x81, 0xc3, 0x2a, 0x71, - 0xea, 0xbf, 0x11, 0xd7, 0x1a, 0x7a, 0xf8, 0x2d, 0x16, 0x47, 0xd4, 0xc8, - 0x5f, 0x49, 0xcc, 0xff, 0x86, 0x0c, 0x19, 0xd4, 0x7e, 0x5a, 0x0e, 0xa3, - 0xfb, 0x7e, 0xdb, 0xde, 0x4c, 0xf8, 0x0e, 0x6f, 0xdf, 0xef, 0xfd, 0x3e, - 0xd0, 0x89, 0x30, 0x03, 0xb6, 0x9f, 0x00, 0xda, 0x2d, 0x81, 0x8c, 0xb2, - 0x1b, 0xfd, 0xf3, 0xb7, 0x78, 0x9c, 0x3d, 0xfc, 0x51, 0x65, 0x53, 0x12, - 0x62, 0x21, 0xbd, 0xe7, 0xde, 0x9a, 0x9b, 0xe3, 0x7c, 0xde, 0xfe, 0xae, - 0xb6, 0xd4, 0x4a, 0x8a, 0xe3, 0x37, 0x59, 0x20, 0xa2, 0xd7, 0x03, 0x96, - 0x27, 0x46, 0x28, 0xfd, 0x0c, 0xab, 0x6c, 0xad, 0x1b, 0x59, 0x48, 0xfe, - 0x02, 0x4a, 0xe7, 0x88, 0x28, 0xd4, 0xe4, 0x67, 0x14, 0x4c, 0x61, 0x0a, - 0xca, 0xb6, 0xa4, 0xd6, 0x61, 0x10, 0xc8, 0x10, 0x43, 0x6c, 0xf8, 0x9b, - 0x8b, 0x8e, 0x10, 0x85, 0xc6, 0xd8, 0x79, 0x5c, 0x10, 0x40, 0xb1, 0x17, - 0x8a, 0x6d, 0x02, 0xa9, 0xe1, 0x89, 0xcc, 0xb4, 0x38, 0x07, 0x12, 0x6c, - 0x59, 0x8b, 0x8d, 0xff, 0x9e, 0x76, 0x95, 0x28, 0x81, 0xca, 0xf2, 0x1a, - 0x1c, 0x6e, 0x91, 0x2d, 0xeb, 0x52, 0x70, 0x42, 0xf8, 0xf4, 0xf5, 0x3c, - 0xe0, 0xb7, 0xa8, 0x2f, 0x80, 0xff, 0xf1, 0x50, 0x80, 0x2e, 0x3f, 0xfc, - 0x21, 0x1a, 0xcf, 0xfc, 0xe9, 0xf2, 0x88, 0x88, 0xab, 0xb1, 0xc4, 0x13, - 0x07, 0xeb, 0x1f, 0x4f, 0x7f, 0x35, 0x99, 0x5e, 0xd9, 0x2a, 0x56, 0xb2, - 0xdc, 0xcb, 0xcd, 0x6d, 0x52, 0x85, 0x45, 0x48, 0x01, 0xc1, 0xbc, 0xe9, - 0x56, 0xa8, 0x0e, 0x1f, 0xda, 0xe6, 0x06, 0xde, 0xe2, 0x95, 0xf8, 0x08, - 0x8d, 0x53, 0x24, 0x0a, 0x58, 0xac, 0xc8, 0x17, 0x20, 0xa5, 0x3b, 0x2d, - 0x62, 0x75, 0x25, 0x2e, 0x08, 0xa4, 0x9c, 0x15, 0xce, 0x71, 0x0f, 0xbe, - 0x35, 0x16, 0x11, 0xa3, 0xa4, 0x28, 0x49, 0x1d, 0xdc, 0x10, 0x3e, 0x91, - 0x9e, 0x8f, 0xea, 0xdc, 0x5f, 0xb0, 0xd4, 0x3a, 0x7b, 0xa0, 0x8c, 0x0d, - 0x56, 0xdf, 0xea, 0xb3, 0x50, 0xec, 0x07, 0x98, 0xeb, 0xbe, 0xda, 0xa8, - 0x09, 0xe8, 0x76, 0xb6, 0x11, 0x5b, 0xc6, 0x15, 0x82, 0x77, 0x7a, 0xc7, - 0x6f, 0x07, 0x81, 0xc9, 0xb1, 0x02, 0x65, 0x50, 0x06, 0xff, 0x7c, 0xca, - 0x04, 0x75, 0x12, 0x0b, 0xdc, 0x3d, 0xd3, 0x08, 0xd6, 0x47, 0xce, 0xa0, - 0x0b, 0xa6, 0x43, 0x94, 0xaa, 0x9a, 0x77, 0x52, 0x90, 0x0d, 0xcf, 0xce, - 0x98, 0x57, 0x35, 0xe4, 0xda, 0x95, 0x29, 0x5d, 0x29, 0xcd, 0xeb, 0x25, - 0xba, 0xb6, 0xb6, 0x9b, 0x87, 0x78, 0x9d, 0x02, 0x7d, 0x13, 0xb1, 0xee, - 0x6e, 0x5a, 0xe1, 0x21, 0x5a, 0x96, 0x3f, 0xda, 0xff, 0x7b, 0xb3, 0xb3, - 0x3f, 0x9c, 0xc2, 0x42, 0x89, 0x49, 0x6a, 0x9d, 0x70, 0x73, 0x8d, 0xff, - 0x96, 0x1d, 0x61, 0xa2, 0x07, 0x37, 0xc1, 0x3f, 0x32, 0x0c, 0xc4, 0x27, - 0xf1, 0xd7, 0x02, 0x99, 0xe7, 0x1f, 0x6c, 0x80, 0x0d, 0x10, 0x1a, 0x44, - 0xb4, 0xe8, 0x01, 0xdb, 0x58, 0x2f, 0xa7, 0x16, 0x65, 0xab, 0x2a, 0x66, - 0x63, 0x73, 0x21, 0x1d, 0x5c, 0x49, 0x8c, 0x84, 0xf7, 0x9f, 0xac, 0x7d, - 0x3d, 0xfc, 0xf3, 0xdf, 0x15, 0xec, 0xaf, 0x17, 0xad, 0x65, 0x8a, 0x8d, - 0xdf, 0x37, 0xbd, 0x2b, 0x2e, 0xaa, 0xf4, 0x0e, 0x49, 0x40, 0x8c, 0x91, - 0x3a, 0x51, 0xaa, 0xf8, 0x82, 0x82, 0x5d, 0xde, 0x38, 0x37, 0xf9, 0x8a, - 0x73, 0xbe, 0x61, 0x85, 0x66, 0x73, 0xab, 0x67, 0x0d, 0x4d, 0xa0, 0x31, - 0xd3, 0xaa, 0x02, 0x4f, 0x08, 0xf2, 0x62, 0xfe, 0x27, 0xa6, 0x23, 0xbb, - 0x75, 0x87, 0x13, 0x24, 0x20, 0x1e, 0x17, 0x4b, 0x12, 0x30, 0xe9, 0xf0, - 0x78, 0xb4, 0xe0, 0xf4, 0xaa, 0x76, 0xa2, 0x19, 0x30, 0x3d, 0x81, 0x4a, - 0xf5, 0x0d, 0x70, 0x0f, 0x2a, 0x8c, 0x8a, 0x36, 0x18, 0x6f, 0x6b, 0x7d, - 0xfa, 0xa9, 0x07, 0xfc, 0x7d, 0x1b, 0x98, 0xb7, 0x4a, 0x1b, 0xb0, 0xd2, - 0xcf, 0x80, 0xff, 0xf1, 0x50, 0x80, 0x2c, 0x5f, 0xfc, 0x21, 0x1a, 0xcf, - 0xfc, 0xcd, 0x40, 0x84, 0x03, 0xa9, 0xb1, 0x43, 0xd0, 0x8a, 0x14, 0xa9, - 0xf1, 0x3d, 0xb4, 0xfb, 0x7c, 0x37, 0x33, 0xef, 0xef, 0xf1, 0xe9, 0x2b, - 0x5c, 0xca, 0x95, 0x38, 0xde, 0x4a, 0x0a, 0x42, 0x02, 0x3e, 0x96, 0xf3, - 0xab, 0xd7, 0x59, 0xaa, 0x97, 0xf5, 0xdc, 0xef, 0xd7, 0x72, 0xe3, 0x42, - 0x3b, 0x7a, 0xf1, 0xc2, 0x05, 0xb9, 0x55, 0xab, 0x16, 0xda, 0xb1, 0x6b, - 0xf1, 0x88, 0xb3, 0x00, 0x97, 0x09, 0x91, 0x96, 0x9f, 0x7c, 0x4b, 0x49, - 0x1c, 0xec, 0x44, 0x69, 0x02, 0xfd, 0xfd, 0x79, 0xf4, 0x8d, 0x0a, 0xb2, - 0x6c, 0xad, 0xf2, 0xbf, 0x4c, 0xe4, 0x70, 0x89, 0x99, 0xdf, 0x42, 0xd9, - 0xd9, 0x74, 0xb2, 0x5d, 0xbd, 0x4c, 0x7e, 0xe8, 0x63, 0x54, 0x71, 0x63, - 0x75, 0x2f, 0x9a, 0x7c, 0x23, 0xac, 0xba, 0xea, 0x21, 0xde, 0x43, 0x80, - 0x15, 0xfc, 0xf2, 0xa0, 0x76, 0x8c, 0x59, 0x82, 0x5e, 0x3a, 0x79, 0xab, - 0xaf, 0x4f, 0xc7, 0x27, 0x69, 0x91, 0x39, 0x40, 0xbc, 0x13, 0x73, 0x06, - 0xbb, 0xda, 0xe9, 0x0e, 0xb8, 0x3d, 0x2a, 0xb4, 0x11, 0x03, 0x39, 0x23, - 0xdb, 0xa8, 0xf2, 0xdc, 0xea, 0xcf, 0x7e, 0x82, 0x00, 0x71, 0x27, 0x0e, - 0x52, 0x60, 0xd8, 0x00, 0x33, 0x29, 0x89, 0x32, 0x20, 0xd8, 0x46, 0x24, - 0x9a, 0x44, 0xa2, 0x8d, 0x15, 0xf7, 0xcc, 0x5c, 0x17, 0xdb, 0x00, 0xa6, - 0x08, 0x62, 0xe8, 0x22, 0x20, 0x08, 0x67, 0xb8, 0xe0, 0x7f, 0x3a, 0xa4, - 0x46, 0x2b, 0x4c, 0xc8, 0xd3, 0x89, 0xc2, 0xd5, 0x62, 0x12, 0xfe, 0x33, - 0xde, 0x8b, 0xb0, 0x87, 0xa4, 0xf0, 0x5d, 0xea, 0x62, 0x4c, 0x67, 0x0a, - 0x0f, 0xde, 0x7c, 0x4f, 0x6d, 0x3e, 0xdf, 0x1b, 0xf1, 0xed, 0x9e, 0xcd, - 0x7a, 0xab, 0xb4, 0x03, 0x73, 0x20, 0xa5, 0x45, 0xde, 0x07, 0x2b, 0x03, - 0x0f, 0x2c, 0x1e, 0x29, 0x0e, 0x19, 0x4b, 0x94, 0x6f, 0xc9, 0x90, 0x68, - 0x7c, 0xc7, 0x39, 0xde, 0x5b, 0x4e, 0x59, 0xdc, 0xad, 0xe9, 0xe0, 0x06, - 0x49, 0xcc, 0x32, 0xa5, 0x10, 0xe7, 0x99, 0x51, 0x85, 0x6a, 0x22, 0x6f, - 0x9d, 0xdc, 0x9f, 0xf1, 0x8e, 0xc5, 0x08, 0xdb, 0x35, 0xc1, 0xb5, 0x91, - 0xf1, 0xb4, 0x4f, 0x60, 0x22, 0x26, 0xa1, 0x2c, 0xf1, 0x2a, 0xc8, 0x56, - 0x87, 0x21, 0x96, 0xdb, 0xd0, 0x25, 0xe2, 0xce, 0x8b, 0xd2, 0x46, 0xe8, - 0xdd, 0x49, 0x9e, 0x3f, 0x9c, 0xfc, 0x5c, 0xf6, 0x85, 0x31, 0xd6, 0x65, - 0x0d, 0x77, 0x96, 0xa3, 0xa0, 0x01, 0x87, 0x80, 0xff, 0xf1, 0x50, 0x80, - 0x30, 0x7f, 0xfc, 0x21, 0x1a, 0xcf, 0xa1, 0xfd, 0xc4, 0x01, 0x02, 0xaa, - 0xb1, 0x42, 0xd9, 0x28, 0xa6, 0x0a, 0x28, 0xfa, 0x9e, 0x6f, 0x5f, 0x9f, - 0xbd, 0x72, 0x9d, 0x26, 0xd5, 0xa6, 0x13, 0x11, 0x42, 0xa5, 0x4a, 0x89, - 0x50, 0x33, 0xe9, 0x6b, 0x9b, 0x52, 0x48, 0xca, 0xac, 0xe4, 0x68, 0xe6, - 0xc7, 0xd3, 0x14, 0xf2, 0x1b, 0x18, 0x6f, 0x61, 0x88, 0x13, 0x23, 0xd9, - 0x14, 0x1e, 0xb9, 0x26, 0x77, 0x26, 0xb2, 0x03, 0x62, 0x81, 0x15, 0x28, - 0xfc, 0x40, 0xca, 0x86, 0x4b, 0x9d, 0x65, 0x1e, 0xb1, 0x00, 0x58, 0x5e, - 0xd8, 0x29, 0x9e, 0x53, 0x90, 0xb5, 0x8d, 0xaf, 0x44, 0x5d, 0x25, 0xd1, - 0xd3, 0xfe, 0x47, 0xc3, 0x2a, 0x92, 0x4c, 0x3f, 0x2d, 0xfa, 0xc4, 0x86, - 0x77, 0x62, 0xd1, 0xf7, 0x3d, 0xee, 0x80, 0xed, 0x4a, 0xf2, 0x9f, 0xa4, - 0x41, 0xa2, 0xee, 0x64, 0x4f, 0x8e, 0xd1, 0xff, 0xa2, 0x34, 0x9f, 0xe9, - 0x48, 0x16, 0x2f, 0xc5, 0xf2, 0xb9, 0x16, 0xa2, 0x4c, 0x6b, 0xcf, 0x87, - 0x1d, 0xeb, 0x3f, 0x9e, 0xcb, 0x27, 0x3e, 0x42, 0xfa, 0x8a, 0x9e, 0x02, - 0xfb, 0x08, 0x88, 0xad, 0x3f, 0x1c, 0x9d, 0x28, 0xa5, 0x64, 0xc3, 0x31, - 0x3a, 0x10, 0xbc, 0x45, 0x97, 0xbc, 0x30, 0x2d, 0xb1, 0x3c, 0x62, 0xbe, - 0xf9, 0xa0, 0xb9, 0x27, 0xb4, 0xa5, 0x23, 0x7a, 0xd3, 0x95, 0x44, 0x1f, - 0xc7, 0x1a, 0xff, 0x1f, 0xc0, 0x83, 0x3b, 0xdf, 0x14, 0xfb, 0x40, 0xfd, - 0x99, 0xbe, 0xd7, 0xac, 0xf9, 0x2d, 0x2d, 0x79, 0xd1, 0x3d, 0x02, 0x3f, - 0xa7, 0xf2, 0xb9, 0x90, 0xe6, 0xc7, 0xa1, 0x8b, 0x71, 0xfb, 0x91, 0x0f, - 0x89, 0x78, 0x9f, 0x7e, 0x35, 0x78, 0x08, 0x10, 0xdd, 0xbc, 0xaf, 0xd4, - 0x5f, 0xa5, 0xb5, 0x1f, 0xf6, 0xf4, 0xe0, 0x0e, 0xd0, 0x87, 0x47, 0x48, - 0x45, 0xe3, 0x01, 0x19, 0xc7, 0x93, 0x39, 0xba, 0xa9, 0x64, 0x1d, 0x8e, - 0x82, 0xf7, 0x9f, 0x53, 0xad, 0x5f, 0xf5, 0xfd, 0x70, 0x9d, 0x26, 0xf2, - 0x68, 0xb6, 0xee, 0x3b, 0xe3, 0x93, 0x38, 0xc9, 0x96, 0xd6, 0x02, 0xb5, - 0xb6, 0x77, 0xdb, 0xeb, 0xbb, 0xfc, 0x9b, 0x8b, 0x2e, 0xbb, 0xcc, 0xef, - 0xf4, 0x8f, 0x8d, 0x55, 0x7c, 0x45, 0x26, 0xf9, 0x8a, 0x39, 0x5f, 0x47, - 0x48, 0xa3, 0x32, 0xb5, 0xaa, 0xfa, 0x64, 0x40, 0x77, 0x0e, 0xc2, 0x28, - 0x05, 0xbc, 0x77, 0x27, 0x91, 0xa5, 0x7d, 0x52, 0xc2, 0xb0, 0x81, 0x89, - 0xb4, 0xde, 0x1c, 0xa8, 0x9f, 0x01, 0xee, 0x40, 0xb0, 0xb4, 0x23, 0xcb, - 0x48, 0x4e, 0xca, 0x64, 0x94, 0x4f, 0x3a, 0x54, 0xe8, 0x36, 0xbb, 0x90, - 0xcc, 0x97, 0xa4, 0x32, 0xda, 0x17, 0x9b, 0xd9, 0xe0, 0x50, 0xf9, 0x61, - 0xf0, 0x53, 0x8e, 0x34, 0xcc, 0x78, 0xd1, 0xe7, 0x7b, 0xf2, 0x4e, 0xff, - 0xf1, 0x50, 0x80, 0x3d, 0x3f, 0xfc, 0x20, 0xad, 0x1a, 0xd4, 0x28, 0x8d, - 0x15, 0x84, 0x91, 0x3d, 0xb8, 0xeb, 0x7e, 0x7d, 0xfa, 0xac, 0x95, 0xa6, - 0xb9, 0x54, 0x54, 0x29, 0x35, 0x58, 0xa0, 0xd4, 0xaf, 0x35, 0x00, 0x3d, - 0x29, 0xd6, 0x79, 0x98, 0xcc, 0x13, 0x9e, 0x85, 0x4c, 0x5e, 0x6c, 0x74, - 0xa8, 0x12, 0xda, 0xdc, 0x03, 0x51, 0x5b, 0x89, 0x18, 0xd5, 0xd4, 0x78, - 0x6a, 0x03, 0x71, 0xee, 0x35, 0x86, 0x4c, 0x65, 0xc0, 0x66, 0xdf, 0xc8, - 0xb4, 0x86, 0x02, 0x13, 0x4a, 0xe1, 0xe6, 0xcb, 0x51, 0x0e, 0xdb, 0x8c, - 0x62, 0x8e, 0xc7, 0x01, 0x60, 0x9a, 0xff, 0x84, 0xd7, 0x2d, 0xfb, 0x7f, - 0x3d, 0xd0, 0x1e, 0x78, 0xdb, 0x2c, 0x41, 0x9b, 0x3c, 0xbb, 0x23, 0x5d, - 0xad, 0xb6, 0xd0, 0xf3, 0xc6, 0xd9, 0x58, 0x09, 0x6e, 0xbd, 0x24, 0xa9, - 0x39, 0xba, 0x6f, 0xea, 0xda, 0xa8, 0x8d, 0xe3, 0x26, 0x4d, 0xd0, 0x80, - 0x2c, 0x18, 0x35, 0xe5, 0x7a, 0xbb, 0x40, 0xb6, 0xaa, 0xff, 0x11, 0xfa, - 0xe1, 0x11, 0xe9, 0x02, 0x24, 0x1e, 0x92, 0xbd, 0x6c, 0x88, 0xe9, 0xc0, - 0x73, 0xc4, 0x46, 0x46, 0xf1, 0xbd, 0x27, 0xae, 0x00, 0xb0, 0x10, 0xf7, - 0xae, 0x44, 0xb3, 0x07, 0xdc, 0x30, 0xc4, 0xe6, 0x0f, 0xc9, 0x1c, 0x14, - 0x6c, 0xb5, 0x26, 0x6c, 0x47, 0x85, 0xb9, 0x2e, 0xdb, 0x1e, 0xbb, 0x14, - 0xa5, 0x2c, 0xa2, 0xea, 0x71, 0xfc, 0x07, 0x2b, 0x49, 0x2c, 0x03, 0xdc, - 0x7c, 0xd6, 0x80, 0x3d, 0x6c, 0x58, 0x6e, 0xe5, 0xad, 0xb1, 0x8d, 0xde, - 0x59, 0x2a, 0xf2, 0x44, 0xe4, 0xc0, 0x70, 0xc1, 0xbc, 0xbf, 0xab, 0x65, - 0xab, 0xbf, 0xb8, 0xb9, 0x2b, 0xf2, 0x03, 0x55, 0x53, 0xfd, 0x73, 0x41, - 0x9e, 0x1e, 0x20, 0x3f, 0xbc, 0x80, 0x66, 0x8f, 0x10, 0x3d, 0x25, 0x59, - 0x56, 0xa0, 0xc5, 0xd8, 0x88, 0xf3, 0xe3, 0x3a, 0xb9, 0xce, 0x99, 0x2b, - 0x83, 0x2a, 0xb8, 0xa9, 0x19, 0x21, 0x50, 0x29, 0x29, 0x7d, 0x72, 0x1c, - 0x74, 0xa8, 0x3d, 0xcc, 0x40, 0xb1, 0x55, 0x68, 0x19, 0x18, 0x91, 0x45, - 0x05, 0x8c, 0x47, 0x8b, 0x64, 0x04, 0x30, 0x12, 0xcc, 0xcb, 0x65, 0xa1, - 0x12, 0x2a, 0x94, 0x45, 0x38, 0xf7, 0x16, 0x94, 0xcb, 0x0b, 0x62, 0x41, - 0x1b, 0xe5, 0x18, 0xb4, 0x18, 0x5c, 0x64, 0x56, 0xa6, 0x11, 0x6d, 0x20, - 0xd3, 0x82, 0x66, 0x1a, 0xb2, 0xd4, 0x04, 0x1c, 0x9b, 0x65, 0xa9, 0x0a, - 0x0f, 0x65, 0x95, 0x32, 0x9d, 0x2c, 0x4b, 0x3e, 0x3d, 0xa1, 0x2f, 0x90, - 0xdc, 0x13, 0x13, 0xc1, 0x59, 0xb3, 0xe3, 0xd9, 0x50, 0x3e, 0x48, 0x33, - 0xf3, 0xe9, 0xbe, 0x9f, 0x17, 0x85, 0x11, 0x0c, 0xde, 0x73, 0x29, 0xcc, - 0xf3, 0xae, 0x7f, 0xeb, 0x0e, 0xab, 0x2b, 0xea, 0xb0, 0x90, 0x6e, 0x9c, - 0x00, 0xd6, 0x58, 0x80, 0x4b, 0xb7, 0xde, 0x68, 0x3f, 0x9c, 0x57, 0xb6, - 0xe9, 0xfb, 0x5b, 0x21, 0x18, 0x81, 0xe8, 0x1f, 0x20, 0xca, 0x27, 0xc8, - 0xb7, 0x1c, 0xcc, 0x91, 0x5d, 0x25, 0xbe, 0x03, 0xc4, 0x1b, 0x80, 0x78, - 0x43, 0x80, 0xea, 0xea, 0x89, 0xad, 0x10, 0x8e, 0x92, 0x84, 0x62, 0xf7, - 0xde, 0x91, 0x16, 0xed, 0x10, 0x8c, 0x46, 0x97, 0x48, 0x3f, 0x2a, 0xf5, - 0x2f, 0x4a, 0xf4, 0x4b, 0x67, 0x73, 0x11, 0x07, 0xf0, 0xae, 0x62, 0xc0, - 0x39, 0xa3, 0x81, 0x18, 0x44, 0xbc, 0xd0, 0x78, 0x0e, 0x72, 0x1e, 0xba, - 0x32, 0x38, 0x10, 0x31, 0x1e, 0x15, 0x7d, 0x09, 0x23, 0xe5, 0x8c, 0x08, - 0x91, 0xee, 0xe3, 0x18, 0x39, 0x62, 0x2d, 0xf0, 0xff, 0xf1, 0x50, 0x80, - 0x36, 0x3f, 0xfc, 0x20, 0xad, 0x1a, 0xd0, 0x86, 0x4a, 0x24, 0x98, 0x85, - 0x38, 0xf8, 0xba, 0x9c, 0xfb, 0x72, 0x55, 0x98, 0x95, 0x31, 0x64, 0x94, - 0x0d, 0x6f, 0x56, 0x57, 0x14, 0x17, 0xf3, 0x76, 0x7a, 0x1c, 0xc8, 0x60, - 0x4b, 0x73, 0x36, 0x5c, 0x3a, 0x72, 0x28, 0xd8, 0xdd, 0x4f, 0x11, 0x44, - 0xb6, 0xea, 0x04, 0xb8, 0xbd, 0x39, 0xb4, 0xad, 0x14, 0xd0, 0x12, 0x44, - 0xa1, 0x55, 0xf7, 0xcc, 0x36, 0x22, 0xd1, 0xd2, 0xf0, 0x24, 0x91, 0x15, - 0xf9, 0x55, 0xfd, 0xec, 0x6f, 0xec, 0xb5, 0xdf, 0xd5, 0x3e, 0x56, 0x4a, - 0xa8, 0x26, 0xab, 0xe1, 0x55, 0xa9, 0x3d, 0xc4, 0xf8, 0x97, 0x3a, 0x7e, - 0xed, 0x1f, 0xf4, 0xc8, 0xec, 0xd3, 0x35, 0xd2, 0xe9, 0x69, 0x1b, 0x83, - 0x9e, 0x3c, 0xe7, 0x07, 0x31, 0x8d, 0xcc, 0xf4, 0xd0, 0x7c, 0xbb, 0xcb, - 0xee, 0x26, 0xb3, 0x5f, 0x45, 0xbc, 0x62, 0xdc, 0xfe, 0xee, 0x5f, 0x2c, - 0x0e, 0xd3, 0x27, 0xe9, 0xe7, 0xca, 0x69, 0x97, 0xf8, 0x37, 0x1f, 0xce, - 0x8d, 0x73, 0x0e, 0x23, 0xda, 0x84, 0x1e, 0x36, 0xd3, 0x74, 0x6e, 0x83, - 0xbc, 0x41, 0xa3, 0x89, 0x93, 0xca, 0x54, 0x75, 0xa9, 0x7d, 0x2b, 0xcf, - 0x13, 0x22, 0xfc, 0x21, 0xea, 0x9c, 0xa2, 0x3a, 0xbe, 0x1d, 0xa8, 0x41, - 0x6a, 0xe2, 0x9e, 0x7b, 0xd5, 0x69, 0xba, 0xba, 0xf1, 0xd4, 0x7d, 0x55, - 0xde, 0xe3, 0xcb, 0x0f, 0xb0, 0xf3, 0x14, 0x79, 0xc8, 0x4c, 0xde, 0x13, - 0x36, 0xbd, 0x2a, 0x61, 0xb7, 0xdb, 0xa4, 0xaf, 0x90, 0x4b, 0x21, 0x16, - 0xc6, 0x0a, 0x48, 0x54, 0x99, 0x21, 0x01, 0xdd, 0xc4, 0xad, 0x43, 0x8e, - 0xaf, 0xf3, 0xed, 0xe3, 0x57, 0x3f, 0x1f, 0xdd, 0x35, 0xc6, 0xed, 0x57, - 0x7f, 0x8f, 0xfa, 0x26, 0xb8, 0xf8, 0xd6, 0xab, 0x90, 0x7c, 0x30, 0xa9, - 0xa0, 0x3e, 0xbc, 0xda, 0xdd, 0x11, 0x1c, 0x85, 0xe8, 0xbb, 0xd0, 0x1b, - 0xdc, 0xc8, 0xde, 0xcb, 0xd3, 0xe5, 0xae, 0x2d, 0xcd, 0x46, 0x60, 0x7a, - 0x26, 0xcb, 0x3b, 0xc7, 0x60, 0x3a, 0xe0, 0x26, 0xe4, 0xb2, 0x59, 0x5a, - 0x49, 0x95, 0x23, 0x2b, 0x2d, 0xd6, 0xf6, 0x56, 0xa0, 0xa8, 0x79, 0x63, - 0x1f, 0xc6, 0x9f, 0xe6, 0x28, 0x16, 0xc3, 0xbb, 0xf8, 0x2e, 0xeb, 0x17, - 0xbf, 0x27, 0x54, 0x51, 0x35, 0x4d, 0x2e, 0xca, 0xe7, 0x41, 0x9a, 0xe5, - 0x84, 0x9e, 0x40, 0x97, 0x3e, 0xf0, 0xb4, 0xf2, 0x0d, 0x22, 0xf6, 0x26, - 0xf4, 0x8f, 0x82, 0x49, 0xb1, 0x53, 0xb5, 0x80, 0x9d, 0x9b, 0x1b, 0xcf, - 0xa9, 0x19, 0xd8, 0x20, 0x7e, 0xa8, 0x94, 0xe2, 0xbe, 0xf5, 0xd6, 0x5d, - 0x68, 0xd0, 0x52, 0x17, 0xdb, 0xed, 0xdd, 0xf9, 0x36, 0x19, 0x2a, 0xd2, - 0xe9, 0x36, 0xe0, 0x50, 0xb5, 0x75, 0x8e, 0x6e, 0xa4, 0x57, 0xa2, 0x0c, - 0x99, 0x93, 0x4e, 0x9d, 0x83, 0x25, 0x77, 0x1a, 0x26, 0x77, 0x53, 0xd1, - 0x4a, 0x88, 0x02, 0xdd, 0x19, 0xad, 0x1f, 0x9d, 0x5f, 0x09, 0xed, 0x59, - 0x1e, 0x46, 0x05, 0x88, 0x17, 0xc9, 0x3a, 0xd5, 0xb7, 0x0c, 0xc5, 0x17, - 0x63, 0x4b, 0x03, 0xc5, 0xf4, 0x4c, 0x98, 0xb5, 0xc0, 0xff, 0xf1, 0x50, - 0x80, 0x3b, 0x7f, 0xfc, 0x20, 0xa3, 0x1a, 0xd6, 0x28, 0x43, 0x1d, 0x2e, - 0xc2, 0x80, 0x92, 0x23, 0x7d, 0x7c, 0x7d, 0xa6, 0xf5, 0x75, 0x56, 0xc9, - 0x28, 0x45, 0xd4, 0xf3, 0xcb, 0x9d, 0x54, 0x93, 0xad, 0x68, 0x42, 0x7b, - 0x2b, 0xe9, 0x7e, 0x5b, 0x34, 0x17, 0x11, 0xed, 0x5b, 0xaa, 0x13, 0x70, - 0xd9, 0xec, 0x2b, 0x91, 0xba, 0x78, 0x47, 0x3e, 0xc1, 0x73, 0xd8, 0x81, - 0xaf, 0x4f, 0x34, 0x51, 0x0b, 0x22, 0x77, 0x99, 0x57, 0x09, 0x1c, 0x4b, - 0xf0, 0xa1, 0xfa, 0x74, 0xe2, 0x6f, 0x1b, 0xd0, 0xa8, 0x97, 0xa6, 0xfa, - 0xb9, 0x9e, 0x66, 0xdc, 0x36, 0xd7, 0xe3, 0xaa, 0xbe, 0x73, 0x74, 0x36, - 0x8d, 0x17, 0x28, 0x2c, 0x98, 0x2e, 0x1b, 0x31, 0x96, 0x23, 0xc9, 0xc3, - 0xe7, 0x54, 0x24, 0x44, 0xd5, 0x95, 0xe6, 0x4b, 0x50, 0x0a, 0x8e, 0x23, - 0x16, 0xd6, 0x45, 0x6e, 0xca, 0xf5, 0x83, 0x1e, 0xa5, 0xca, 0x6c, 0xa7, - 0x26, 0xc1, 0x4b, 0x35, 0xce, 0x80, 0xfc, 0x1f, 0x1a, 0x27, 0x43, 0xc7, - 0xf4, 0xe7, 0xc1, 0x92, 0xc1, 0x9d, 0x1a, 0xb0, 0x06, 0xb1, 0x53, 0x57, - 0xac, 0xad, 0x12, 0xda, 0xac, 0x76, 0xb0, 0xac, 0x30, 0x7a, 0x55, 0x70, - 0x59, 0x23, 0x70, 0x9a, 0x2b, 0x41, 0x36, 0x16, 0xc8, 0xb8, 0x68, 0x34, - 0x1a, 0x44, 0xb4, 0x8c, 0x4b, 0x35, 0x7a, 0xbc, 0x8a, 0x9a, 0x2c, 0x55, - 0xd0, 0x1e, 0xb5, 0x20, 0xed, 0x2c, 0xea, 0x67, 0xac, 0xfa, 0xa4, 0x9d, - 0x66, 0x22, 0xe6, 0x4c, 0x6e, 0x95, 0x56, 0xe9, 0xab, 0xab, 0x47, 0x71, - 0xfe, 0x71, 0x59, 0xf3, 0x26, 0xa3, 0xe3, 0x26, 0x27, 0x13, 0xab, 0x07, - 0x7c, 0x67, 0xc2, 0xc0, 0x9e, 0xa1, 0x7a, 0xd6, 0x88, 0x2a, 0x6a, 0x08, - 0x00, 0x06, 0xb6, 0x43, 0x8d, 0xd5, 0x47, 0x1b, 0xeb, 0x9a, 0xeb, 0xc5, - 0xf1, 0xbe, 0xa7, 0xb7, 0x3d, 0x74, 0x3f, 0x67, 0x9b, 0xe4, 0xf0, 0xed, - 0x97, 0xd3, 0xba, 0x71, 0xa0, 0xd2, 0x2c, 0x65, 0xaa, 0x23, 0x51, 0xd2, - 0xfd, 0x95, 0x80, 0x56, 0x4f, 0x0d, 0x6c, 0x79, 0x23, 0x09, 0x0f, 0x89, - 0xdd, 0x99, 0x1f, 0x65, 0x87, 0x42, 0x9c, 0x25, 0xdf, 0x61, 0xfd, 0xab, - 0xbd, 0x6a, 0x7f, 0xa2, 0xd0, 0xc2, 0xfa, 0x06, 0xaf, 0x89, 0xda, 0x7c, - 0xcf, 0x4a, 0x5f, 0x55, 0xdb, 0xfc, 0x15, 0xa4, 0xe6, 0xa8, 0x6e, 0x8f, - 0xb0, 0x12, 0xf2, 0xc2, 0xeb, 0x59, 0x15, 0xd2, 0x41, 0x43, 0x67, 0xbe, - 0x89, 0xe2, 0x09, 0x15, 0xe4, 0xd7, 0x6f, 0x0c, 0xae, 0xd6, 0x6b, 0x08, - 0x97, 0x42, 0x9d, 0x59, 0x49, 0x16, 0x9b, 0xa3, 0xe8, 0x48, 0x97, 0x85, - 0x5a, 0xd5, 0x5e, 0xc6, 0xac, 0x6a, 0xc2, 0xac, 0xc5, 0x9e, 0xda, 0x8c, - 0xeb, 0xb3, 0x56, 0x91, 0x37, 0x78, 0xad, 0x8a, 0xd5, 0x42, 0x66, 0x99, - 0x25, 0xb2, 0x16, 0xa5, 0x8c, 0x0e, 0x26, 0x2d, 0xab, 0x13, 0x5f, 0x95, - 0x2d, 0x94, 0xe5, 0x75, 0x35, 0xb9, 0x6d, 0x80, 0x94, 0xb8, 0x39, 0x0e, - 0x12, 0xfa, 0x16, 0xb1, 0xdc, 0x46, 0x82, 0x1e, 0xd2, 0x42, 0x5f, 0x1c, - 0xa6, 0x56, 0xfd, 0x8a, 0x9f, 0x9c, 0xdd, 0xca, 0xaa, 0x9a, 0xa0, 0x14, - 0x8a, 0x0b, 0xeb, 0x48, 0x97, 0x38, 0xe2, 0x92, 0xcb, 0x09, 0xdc, 0x13, - 0x22, 0x62, 0xe3, 0x71, 0x71, 0x55, 0x23, 0x65, 0x35, 0x30, 0x27, 0x1c, - 0xdf, 0x47, 0xcb, 0x75, 0x7a, 0x76, 0x77, 0x77, 0xe0, 0x74, 0xab, 0x1d, - 0xbc, 0x93, 0x81, 0xc0, 0xff, 0xf1, 0x50, 0x80, 0x27, 0x9f, 0xfc, 0x21, - 0x1a, 0xcb, 0x30, 0xa0, 0x1f, 0x4c, 0x40, 0xa8, 0xa1, 0xb3, 0xd2, 0xcc, - 0x33, 0xae, 0x19, 0xac, 0xe1, 0xca, 0x44, 0x53, 0xae, 0xee, 0x97, 0x9d, - 0x65, 0x75, 0xb9, 0x13, 0x21, 0x3a, 0xdd, 0x81, 0x79, 0x3a, 0x9d, 0x86, - 0xe6, 0xd6, 0xce, 0x5b, 0x1d, 0xfd, 0x03, 0xab, 0xdf, 0x2c, 0xb5, 0xdf, - 0x2b, 0x8a, 0x6c, 0xbe, 0x8a, 0x67, 0x5a, 0xee, 0xed, 0x49, 0x82, 0x80, - 0xbe, 0xc8, 0x6b, 0xd1, 0x2c, 0x49, 0xcc, 0x42, 0xa7, 0x4e, 0xee, 0x62, - 0x9b, 0x87, 0xe3, 0x6b, 0xec, 0xb2, 0x05, 0xfc, 0x68, 0xa8, 0x2f, 0xea, - 0x2b, 0x53, 0x02, 0xd5, 0x7f, 0x6c, 0xae, 0xa8, 0x0e, 0xd6, 0x80, 0x2a, - 0x9b, 0xb0, 0xa9, 0x71, 0xe7, 0xc7, 0xbd, 0x4a, 0x82, 0xbf, 0x8d, 0x4c, - 0x9d, 0x25, 0xd8, 0x5a, 0xef, 0xb8, 0x89, 0xdd, 0x0d, 0x2f, 0x76, 0x8b, - 0x02, 0xd1, 0x1a, 0x15, 0xde, 0x59, 0x51, 0xab, 0x4a, 0x5c, 0x2b, 0xe8, - 0xd9, 0xd9, 0xd5, 0x76, 0x75, 0x3a, 0xe6, 0x3a, 0x04, 0x93, 0xb8, 0x61, - 0x7e, 0x30, 0x98, 0x78, 0xda, 0x76, 0x31, 0x1a, 0x83, 0x04, 0xd7, 0xf7, - 0xdc, 0x49, 0xfb, 0x2f, 0x68, 0x2f, 0x26, 0x03, 0x0b, 0x68, 0x31, 0xca, - 0x09, 0xc5, 0x11, 0x3a, 0x81, 0x16, 0x0f, 0x05, 0x53, 0xab, 0x5b, 0xa7, - 0x14, 0x10, 0x75, 0x5a, 0x94, 0x23, 0x2a, 0x47, 0xf8, 0xcf, 0x0d, 0x86, - 0x1f, 0xdc, 0xcf, 0xde, 0x2b, 0x52, 0x12, 0x6d, 0xe9, 0x17, 0x18, 0x5c, - 0xcc, 0x8d, 0x4d, 0x11, 0x55, 0xc2, 0x02, 0x06, 0xa0, 0x00, 0x15, 0x86, - 0x1a, 0x08, 0x58, 0x84, 0xb0, 0x71, 0x61, 0x39, 0x9c, 0x2a, 0x20, 0x4a, - 0xb2, 0x77, 0xc0, 0xeb, 0xbe, 0xaf, 0x5f, 0x6e, 0xb3, 0x7f, 0x1e, 0xbe, - 0xb9, 0xbc, 0xbe, 0x4d, 0x70, 0xbc, 0xeb, 0x2b, 0x5c, 0xdd, 0x48, 0xa9, - 0x9a, 0xbc, 0x0a, 0x3b, 0x36, 0xe8, 0x76, 0xa7, 0xdd, 0xa9, 0x96, 0x2b, - 0xbf, 0xb7, 0x0e, 0x33, 0x72, 0xfa, 0xb0, 0x17, 0xdf, 0xa7, 0x24, 0xfc, - 0xdc, 0xf9, 0x8d, 0xbc, 0xfb, 0x6f, 0xf3, 0x38, 0x2a, 0xf8, 0x31, 0xa6, - 0xc5, 0x97, 0x32, 0xb0, 0x90, 0x65, 0xa8, 0x03, 0x10, 0xe7, 0xe3, 0xa2, - 0x76, 0xcc, 0x56, 0x7b, 0x93, 0xb0, 0xd9, 0x5f, 0xff, 0xf1, 0x50, 0x80, - 0x24, 0xdf, 0xfc, 0x21, 0x1a, 0xcb, 0x78, 0xff, 0x7d, 0x09, 0x08, 0xa3, - 0x81, 0xb3, 0x52, 0x24, 0x74, 0x1b, 0xbe, 0xb8, 0xd7, 0x31, 0xed, 0xde, - 0xef, 0x7c, 0x25, 0x49, 0x6d, 0xae, 0x57, 0x5d, 0xad, 0x5c, 0x73, 0x53, - 0x7a, 0x5c, 0x80, 0x44, 0x99, 0x3a, 0x0d, 0x73, 0x79, 0xb2, 0xdb, 0x27, - 0x4d, 0x09, 0x1f, 0x6a, 0x73, 0x77, 0x18, 0x19, 0x09, 0x32, 0x01, 0x15, - 0xd0, 0x75, 0x14, 0x84, 0xcb, 0x4d, 0x04, 0x54, 0xdd, 0xb3, 0xd5, 0x40, - 0x28, 0xf7, 0x1c, 0x8f, 0x75, 0x1d, 0x41, 0x05, 0x3e, 0xc4, 0x76, 0x4b, - 0x42, 0xa5, 0x65, 0x0e, 0xeb, 0x6b, 0x28, 0x8e, 0xd0, 0x15, 0x20, 0x65, - 0xfa, 0x12, 0x60, 0x00, 0x9a, 0xa5, 0xe0, 0xdb, 0xc2, 0xdd, 0x72, 0x9b, - 0x20, 0xb3, 0x16, 0x51, 0xab, 0x23, 0x1d, 0xa8, 0x61, 0x98, 0xb7, 0x18, - 0x71, 0xb2, 0xfa, 0xb1, 0x86, 0x9b, 0x11, 0x47, 0xa6, 0x6e, 0x10, 0xba, - 0x19, 0x60, 0xe8, 0x32, 0xa0, 0x32, 0xd7, 0xef, 0x6f, 0x9c, 0x38, 0x16, - 0x67, 0x4c, 0x7c, 0x14, 0xea, 0x9c, 0xf1, 0x22, 0x75, 0x7e, 0xf5, 0xa0, - 0x02, 0xe5, 0xfc, 0x76, 0xd7, 0xb1, 0x52, 0x92, 0x7a, 0xc4, 0x4c, 0x58, - 0xd4, 0x10, 0xb5, 0xd5, 0xe7, 0x91, 0x50, 0xcf, 0xb7, 0xa2, 0xe4, 0xb5, - 0xcf, 0xb4, 0x1e, 0x38, 0x4b, 0x51, 0x00, 0x39, 0x96, 0x89, 0xb7, 0xdc, - 0x76, 0x2e, 0x2d, 0xb9, 0x50, 0xbe, 0xdb, 0xbf, 0x0f, 0x62, 0x9d, 0xe3, - 0xd5, 0xef, 0x50, 0xcb, 0x48, 0x7c, 0x0e, 0x9e, 0xdc, 0xc4, 0xce, 0x7c, - 0xef, 0x84, 0xa9, 0x17, 0xb5, 0xce, 0x1d, 0xc9, 0x52, 0x15, 0x0b, 0x8a, - 0x01, 0xfb, 0x77, 0xbf, 0xe0, 0x57, 0xd8, 0x8a, 0x00, 0x27, 0x4a, 0xfd, - 0x23, 0xf4, 0xe4, 0x07, 0xf9, 0x62, 0xa3, 0xf2, 0x52, 0xc9, 0x86, 0xe4, - 0x2a, 0xec, 0x00, 0x34, 0xd8, 0xc6, 0x57, 0x5f, 0x71, 0x3e, 0x06, 0x9e, - 0x9b, 0xdf, 0xc1, 0xb7, 0x7c, 0xf0, 0xc6, 0xb3, 0xd7, 0x5e, 0x76, 0xad, - 0xd1, 0xf3, 0x0b, 0xa5, 0x34, 0x20, 0x60, 0x41, 0xe0, 0xf0, 0xf5, 0xee, - 0x67, 0x38, 0xff, 0xf1, 0x50, 0x80, 0x2d, 0x9f, 0xfc, 0x21, 0x2a, 0xcb, - 0x3c, 0xbb, 0x6f, 0x13, 0x20, 0xa1, 0xa3, 0x33, 0x92, 0xac, 0x26, 0xef, - 0xe1, 0x97, 0xcf, 0x54, 0x54, 0x9a, 0xd7, 0x6f, 0x3d, 0xcd, 0xde, 0x6a, - 0xa5, 0x49, 0xcc, 0xa7, 0x9e, 0xd5, 0x7a, 0xd7, 0x03, 0xdb, 0x3b, 0xc3, - 0xf6, 0xc1, 0xc9, 0xbf, 0x9a, 0x1c, 0x07, 0x83, 0x15, 0x1d, 0x3b, 0x83, - 0x5c, 0x75, 0xe3, 0xc1, 0x8d, 0xbf, 0x58, 0xe7, 0xdb, 0x65, 0x94, 0xf2, - 0xe3, 0x66, 0x01, 0x33, 0x0e, 0xca, 0x6e, 0xb1, 0xe2, 0xc9, 0x1f, 0xaa, - 0x47, 0x01, 0x3a, 0xeb, 0x69, 0x02, 0xbb, 0x2d, 0x93, 0xe7, 0x44, 0xe3, - 0xc9, 0x6e, 0x06, 0xdf, 0xad, 0xbe, 0x6a, 0xdf, 0xcb, 0xb1, 0x72, 0x40, - 0x73, 0x07, 0x5e, 0xbd, 0x2b, 0xbb, 0xb6, 0x6d, 0x90, 0xf5, 0xf1, 0x2a, - 0x57, 0x06, 0x16, 0xbd, 0x92, 0x73, 0x80, 0xc2, 0x75, 0xd5, 0xde, 0x28, - 0x0b, 0xa9, 0x52, 0x7a, 0x55, 0xf9, 0x73, 0x42, 0x13, 0x35, 0x2d, 0x5e, - 0xe3, 0x5e, 0x0e, 0xaa, 0x97, 0x36, 0x79, 0xe2, 0x3c, 0x82, 0xb9, 0xfc, - 0x7b, 0x05, 0x7c, 0xba, 0x66, 0x2e, 0x3c, 0x61, 0xd2, 0xde, 0xe0, 0x4b, - 0xe9, 0x87, 0x44, 0x05, 0x80, 0x90, 0x2d, 0x60, 0xde, 0x10, 0xb4, 0x87, - 0xc6, 0x76, 0x19, 0x21, 0x29, 0x3c, 0xb6, 0x70, 0x0f, 0xd3, 0x04, 0xff, - 0xab, 0xa6, 0xff, 0x34, 0x3e, 0x1c, 0xc4, 0x55, 0x4c, 0xc9, 0x2a, 0xd8, - 0x8a, 0x63, 0x85, 0x66, 0xe2, 0x0e, 0x40, 0xf8, 0xbe, 0xff, 0x8d, 0x3b, - 0xc5, 0x48, 0x68, 0xd8, 0xce, 0x94, 0x9d, 0xea, 0x8e, 0x37, 0xbe, 0x6a, - 0x1e, 0xf7, 0xa9, 0x38, 0x97, 0x5b, 0x5b, 0xb9, 0xd9, 0x66, 0xed, 0x61, - 0x6d, 0x00, 0x1e, 0xcf, 0x5b, 0x6d, 0xe2, 0x20, 0x00, 0x04, 0x5d, 0x3e, - 0x80, 0x14, 0xb0, 0xb6, 0x32, 0x29, 0x83, 0xef, 0x4b, 0xe3, 0x55, 0x7c, - 0xf5, 0xde, 0xfd, 0xbd, 0x7b, 0x4d, 0x77, 0xa4, 0x45, 0x66, 0xab, 0xce, - 0xd5, 0x52, 0x5c, 0xc2, 0x5a, 0x87, 0x03, 0xa3, 0xe3, 0xa2, 0xf3, 0x8a, - 0xd7, 0xf9, 0xb1, 0x0c, 0x71, 0x96, 0x3d, 0xbc, 0x59, 0x3e, 0x24, 0xc6, - 0x32, 0xfa, 0xd5, 0xc2, 0x0a, 0x27, 0x97, 0xeb, 0x73, 0x90, 0xa0, 0x54, - 0x88, 0xc1, 0x7d, 0x9e, 0x7b, 0xa6, 0x4a, 0x0c, 0x1f, 0xd4, 0xd5, 0xbf, - 0xfa, 0xa7, 0xbb, 0xe1, 0x7b, 0xc4, 0xaa, 0x80, 0x81, 0xdb, 0x32, 0x1f, - 0xb1, 0x37, 0xd6, 0xe2, 0x4b, 0x03, 0x6f, 0xee, 0x49, 0xd2, 0x14, 0xcc, - 0x56, 0x6a, 0xa7, 0x95, 0x98, 0x60, 0x50, 0x7c, 0x4c, 0x96, 0xc4, 0x13, - 0x24, 0x54, 0x4a, 0x22, 0xc1, 0xe0, 0xff, 0xf1, 0x50, 0x80, 0x40, 0x1f, - 0xfc, 0x20, 0x9e, 0x4c, 0xd8, 0xc4, 0x48, 0x4d, 0x60, 0xe0, 0xb6, 0x86, - 0xca, 0xf9, 0xa9, 0x5e, 0x8e, 0xfb, 0xc7, 0x9f, 0x6d, 0x6b, 0xe6, 0xfa, - 0xd9, 0x5f, 0xb7, 0xef, 0xd7, 0x75, 0xf0, 0xd7, 0x8a, 0xef, 0x8f, 0xeb, - 0xfc, 0xfd, 0x65, 0xf7, 0xc4, 0xf1, 0x79, 0xe4, 0x64, 0x9f, 0x07, 0xf5, - 0x9a, 0x96, 0x12, 0xb9, 0xef, 0xd8, 0x0a, 0x10, 0x02, 0xbb, 0x73, 0x2d, - 0x2b, 0x47, 0x54, 0x4b, 0xda, 0x5e, 0x3f, 0x8d, 0xa3, 0xe4, 0xf4, 0x7e, - 0xff, 0xe1, 0xb5, 0xc4, 0x04, 0xc1, 0xb7, 0xd3, 0x5f, 0x7a, 0x00, 0x18, - 0x8b, 0x2f, 0xfb, 0xb1, 0x95, 0x97, 0x38, 0x27, 0x2c, 0x30, 0x62, 0x4d, - 0x2b, 0xb8, 0x50, 0x0d, 0x08, 0x9f, 0x3b, 0x8c, 0x70, 0x1a, 0xa1, 0x19, - 0xfc, 0x3e, 0x19, 0x62, 0x39, 0x23, 0x33, 0x94, 0xe8, 0x50, 0x70, 0x36, - 0x1c, 0x09, 0x86, 0x2d, 0xbb, 0x50, 0xed, 0x33, 0x44, 0x72, 0x7a, 0xfb, - 0xff, 0x66, 0x56, 0xf1, 0x20, 0x40, 0x30, 0x00, 0xbc, 0x50, 0x15, 0x25, - 0x0e, 0x9d, 0x2b, 0xf2, 0xe5, 0x90, 0xae, 0x34, 0x41, 0xe0, 0x1c, 0x3f, - 0x8e, 0x16, 0x9b, 0x8f, 0x5f, 0x4b, 0x12, 0xf0, 0x7a, 0x7c, 0x89, 0x03, - 0xa1, 0x75, 0xb6, 0xb2, 0xf1, 0x6c, 0x2a, 0xc3, 0xfd, 0x6c, 0xfc, 0x07, - 0xf5, 0xbf, 0xcc, 0x7a, 0x0d, 0x22, 0x4f, 0xff, 0xba, 0x37, 0xb7, 0xfb, - 0x6c, 0x0e, 0x52, 0x88, 0xb3, 0x06, 0x19, 0x51, 0xa0, 0x6c, 0x3d, 0x04, - 0xc9, 0xa7, 0xa7, 0x49, 0xbb, 0xaa, 0xd8, 0x47, 0xeb, 0xde, 0x73, 0xe1, - 0x3f, 0x6c, 0x58, 0x7f, 0x03, 0x5d, 0x85, 0x84, 0x91, 0xc8, 0x1a, 0x8f, - 0xe4, 0x8e, 0xec, 0x38, 0x37, 0xd5, 0xc9, 0x46, 0x66, 0x4c, 0x71, 0xb6, - 0x26, 0x55, 0xb7, 0x40, 0xd4, 0x05, 0xf9, 0x37, 0xd2, 0x77, 0xcd, 0xa1, - 0xc9, 0xb4, 0x8c, 0xfb, 0x32, 0x99, 0xa6, 0x91, 0xcd, 0x20, 0x85, 0xd2, - 0x69, 0x4a, 0x25, 0x70, 0xd6, 0xcb, 0x65, 0x90, 0x40, 0x78, 0x83, 0xda, - 0x7b, 0x7a, 0xfd, 0x75, 0xe3, 0x39, 0xe3, 0xfa, 0xbd, 0x95, 0xe3, 0xf9, - 0xf5, 0xeb, 0xce, 0x75, 0xf2, 0xf6, 0xf5, 0xe7, 0x9e, 0xb7, 0xcc, 0xbf, - 0x61, 0xea, 0xed, 0xb4, 0xc0, 0xfc, 0x19, 0x43, 0xbc, 0xc6, 0xb5, 0xb2, - 0x99, 0xe4, 0x7d, 0xd5, 0xdd, 0x54, 0x51, 0x08, 0x6c, 0xb9, 0xa0, 0x5c, - 0x00, 0x02, 0xf4, 0x07, 0x2e, 0x37, 0x4f, 0x63, 0x4a, 0x54, 0x69, 0xac, - 0x06, 0x5d, 0xbd, 0x7c, 0xea, 0x9f, 0xe4, 0x5f, 0xfa, 0xc4, 0x1b, 0xf4, - 0x81, 0x6f, 0x73, 0x03, 0x04, 0x56, 0xcc, 0x2c, 0x9b, 0xec, 0x1d, 0x77, - 0x0b, 0x58, 0x59, 0x72, 0xbe, 0x55, 0x1b, 0x7e, 0x09, 0x8e, 0x4e, 0x8a, - 0x35, 0x74, 0x92, 0x8b, 0x4d, 0x35, 0x0a, 0xba, 0x52, 0x29, 0x4b, 0x7e, - 0x05, 0xbc, 0x84, 0x34, 0x91, 0x41, 0xa3, 0x39, 0x11, 0x0b, 0x35, 0xf7, - 0x3a, 0x49, 0x67, 0x5a, 0x46, 0x8e, 0x78, 0x86, 0x31, 0xb7, 0x85, 0xee, - 0xdc, 0xa8, 0x4d, 0x02, 0x58, 0xec, 0xc3, 0x0c, 0x72, 0xdb, 0xc6, 0x34, - 0x50, 0xb9, 0xe7, 0x9a, 0xaa, 0x9a, 0x86, 0xfd, 0xfd, 0xfe, 0x5f, 0xfd, - 0xfe, 0xf5, 0x65, 0x94, 0x4e, 0x17, 0xaf, 0x8a, 0xcb, 0x61, 0xbe, 0x6b, - 0x72, 0xd0, 0x03, 0x70, 0xd3, 0xd4, 0xf3, 0x2f, 0x6c, 0xf7, 0xea, 0x11, - 0x79, 0x3b, 0x49, 0x0e, 0x61, 0xa4, 0x00, 0xd3, 0xfe, 0xfc, 0x35, 0xd1, - 0x7b, 0x18, 0x77, 0x76, 0x3b, 0x86, 0xad, 0x8b, 0xef, 0xf9, 0x8a, 0x3e, - 0x78, 0xef, 0xef, 0xde, 0x1b, 0x0e, 0xee, 0x1f, 0x0e, 0xef, 0xe5, 0xf0, - 0x16, 0xac, 0xaf, 0xb4, 0xfa, 0x55, 0x46, 0x80, 0x40, 0x5b, 0x8c, 0x77, - 0x09, 0xee, 0xff, 0xf1, 0x50, 0x80, 0x29, 0xdf, 0xfc, 0x21, 0x7a, 0xcd, - 0x70, 0xec, 0xeb, 0x72, 0xce, 0xa8, 0xa4, 0x33, 0x52, 0xc5, 0xfd, 0x33, - 0xcf, 0xc7, 0xad, 0xdf, 0x37, 0x3d, 0xbd, 0xdc, 0x39, 0xf6, 0xe7, 0xbd, - 0x56, 0x92, 0xa7, 0x37, 0x36, 0xa8, 0x5d, 0xd7, 0x00, 0x2d, 0xd3, 0x83, - 0x4b, 0x1a, 0xb0, 0x59, 0x92, 0xaf, 0xdc, 0xb1, 0x34, 0x59, 0xaa, 0xc7, - 0x75, 0xf4, 0xca, 0xa7, 0x75, 0x26, 0xdc, 0xad, 0x86, 0xbc, 0x4a, 0x82, - 0xaf, 0x2c, 0x3f, 0x99, 0x40, 0x1d, 0xc0, 0x2a, 0x97, 0x1c, 0x82, 0xe5, - 0x39, 0x65, 0x0b, 0x11, 0xd6, 0x64, 0x01, 0x01, 0x7e, 0xc9, 0xeb, 0x06, - 0x2b, 0x68, 0xfb, 0xf0, 0x82, 0xe9, 0x20, 0xba, 0xc3, 0x68, 0x67, 0x73, - 0x22, 0x8f, 0x8f, 0x8f, 0x9d, 0x2a, 0x18, 0xdf, 0x6e, 0xa5, 0xae, 0xe3, - 0xee, 0xf1, 0x9a, 0x3e, 0x97, 0x07, 0x84, 0x54, 0x8a, 0xc1, 0xef, 0xed, - 0x14, 0x3c, 0x24, 0xdb, 0x8f, 0xbd, 0xa5, 0x90, 0x88, 0x8c, 0x68, 0xba, - 0x74, 0x3a, 0xb8, 0x23, 0x1b, 0x53, 0x14, 0x5d, 0x55, 0xf0, 0x01, 0xb4, - 0x0b, 0x13, 0xa4, 0x34, 0x08, 0xe6, 0x2d, 0xef, 0xda, 0xf8, 0x9a, 0x2c, - 0x0f, 0x59, 0x7a, 0x63, 0x34, 0x20, 0xb0, 0xe6, 0x53, 0x76, 0x05, 0x6f, - 0xa9, 0xf3, 0x2a, 0x08, 0x52, 0x9d, 0x7a, 0x26, 0xa9, 0x23, 0x37, 0x7c, - 0xd2, 0x6a, 0xd7, 0x29, 0x27, 0x42, 0xd1, 0x58, 0x05, 0x65, 0x85, 0x50, - 0xd0, 0xcb, 0x5d, 0x31, 0xa8, 0xb4, 0x8b, 0x6b, 0x6c, 0x24, 0xbf, 0xdd, - 0x37, 0x1e, 0x4d, 0x52, 0xda, 0xec, 0x6e, 0xc2, 0x36, 0x0b, 0xf6, 0xcc, - 0x33, 0xfc, 0xff, 0x8a, 0x17, 0xff, 0xb7, 0x3c, 0x1a, 0x41, 0xea, 0x1d, - 0x69, 0x0e, 0x82, 0xb5, 0xaf, 0x39, 0xe7, 0xed, 0xe7, 0x7b, 0xf3, 0x72, - 0x8a, 0xab, 0xf6, 0xe7, 0xcf, 0xbe, 0xfa, 0x39, 0x95, 0x52, 0x33, 0x55, - 0x97, 0x14, 0x1b, 0xbe, 0x5f, 0x62, 0xac, 0x7b, 0x2d, 0x21, 0x37, 0x5f, - 0x13, 0x0d, 0x4e, 0x36, 0x99, 0xaf, 0xd8, 0x0c, 0x51, 0x50, 0x50, 0x3d, - 0x6d, 0x36, 0xf2, 0x13, 0x9d, 0xda, 0xe0, 0x94, 0x29, 0x7c, 0x00, 0x86, - 0x47, 0x59, 0xcf, 0x88, 0x85, 0xec, 0x59, 0x1a, 0x98, 0x9f, 0xd2, 0xbf, - 0x23, 0x53, 0xe2, 0x87, 0xaf, 0x48, 0x7a, 0xee, 0x2f, 0xba, 0x13, 0x23, - 0xd4, 0x4f, 0x04, 0x48, 0x71, 0xf1, 0x2f, 0x87, 0x5b, 0xef, 0x45, 0xc0, - 0xff, 0xf1, 0x50, 0x80, 0x2d, 0xff, 0xfc, 0x21, 0x1a, 0xcf, 0xb4, 0xe0, - 0x49, 0x99, 0x50, 0xa3, 0xb0, 0xc4, 0x19, 0x09, 0x23, 0xda, 0x4a, 0xfb, - 0xfb, 0xeb, 0x2f, 0xb9, 0xc3, 0xeb, 0x7c, 0xdf, 0x5d, 0xef, 0x73, 0x8e, - 0xf4, 0x64, 0xda, 0xd8, 0x2e, 0x48, 0x14, 0xfe, 0xed, 0xf9, 0xa5, 0xea, - 0xed, 0x20, 0xee, 0xe7, 0xc9, 0xbf, 0x5d, 0x75, 0xcd, 0x5e, 0x39, 0x99, - 0x15, 0x12, 0xa4, 0x0c, 0x92, 0x5d, 0x12, 0x3a, 0x88, 0xbb, 0x88, 0xc8, - 0x70, 0x14, 0xa7, 0x88, 0x20, 0xb5, 0x58, 0xf7, 0xb2, 0xa2, 0x12, 0x9e, - 0xc0, 0xaa, 0x4a, 0xee, 0xf8, 0xd4, 0x37, 0xc2, 0xcc, 0xbf, 0x3e, 0xcc, - 0xb0, 0xbe, 0x69, 0x95, 0x70, 0x19, 0xd9, 0xf0, 0x43, 0xaa, 0xd5, 0x05, - 0x30, 0x2b, 0x0d, 0x58, 0x65, 0x00, 0x20, 0x00, 0xbd, 0xa9, 0xe5, 0x42, - 0xb2, 0xc8, 0x64, 0x7a, 0x15, 0xf3, 0xf4, 0x61, 0xaf, 0xee, 0x79, 0x22, - 0x2d, 0x16, 0x4a, 0xe6, 0x58, 0xb9, 0xde, 0x60, 0x93, 0x40, 0x65, 0xd5, - 0x82, 0x6d, 0x51, 0x21, 0xe8, 0x80, 0xab, 0x0a, 0x7b, 0xbe, 0x55, 0xc2, - 0x9e, 0x7e, 0x77, 0x4e, 0xb0, 0xf4, 0x28, 0xf7, 0xfe, 0xae, 0x4a, 0xba, - 0xe1, 0x2c, 0xe3, 0xe4, 0xd0, 0xc6, 0x84, 0x42, 0x90, 0xf3, 0x40, 0xb4, - 0x54, 0x33, 0x83, 0x2e, 0xe0, 0xdc, 0x2d, 0xda, 0xf9, 0x85, 0xa0, 0x4b, - 0x6c, 0xf3, 0xf2, 0xca, 0x55, 0x49, 0x59, 0xa3, 0x26, 0x51, 0x0b, 0x2a, - 0xbd, 0xc0, 0xb4, 0x16, 0x90, 0x94, 0x58, 0x41, 0x72, 0x62, 0xe9, 0xf5, - 0x1b, 0xe7, 0x5a, 0xed, 0xf9, 0xea, 0xe5, 0xd7, 0x72, 0x90, 0x3f, 0x9d, - 0x5b, 0x4c, 0xc0, 0xce, 0x9b, 0xd1, 0xee, 0xb8, 0x98, 0x0f, 0x59, 0x8d, - 0xa2, 0xad, 0xf8, 0x2f, 0x9d, 0xa6, 0x49, 0xa4, 0x03, 0x5d, 0x1c, 0x29, - 0xc8, 0xc9, 0x52, 0xfb, 0xcf, 0x69, 0x79, 0xf7, 0xfe, 0xdf, 0xaf, 0x8d, - 0xf1, 0x38, 0xeb, 0xd6, 0xe4, 0xd7, 0x1e, 0xbd, 0xbb, 0xaa, 0xe1, 0xcd, - 0xee, 0x57, 0x17, 0xdc, 0x6e, 0x5c, 0x05, 0x78, 0x97, 0x38, 0x2c, 0x9a, - 0x83, 0x32, 0x5c, 0xe5, 0x5e, 0x95, 0xb3, 0x40, 0x18, 0x2b, 0xd7, 0xe5, - 0xf4, 0xb9, 0x40, 0xae, 0xe2, 0x2f, 0x84, 0xbe, 0xc5, 0xc3, 0x00, 0x9a, - 0x4b, 0x43, 0x1d, 0x57, 0x0d, 0xce, 0x01, 0xe8, 0xaf, 0x50, 0xa2, 0x09, - 0xea, 0x70, 0x08, 0x63, 0x1a, 0x36, 0x3b, 0x00, 0x16, 0xe9, 0x90, 0x92, - 0xf2, 0xae, 0x70, 0x29, 0x22, 0x62, 0x75, 0x2b, 0x17, 0xdb, 0x7d, 0x5b, - 0x4e, 0x71, 0x43, 0x2a, 0xe8, 0xf6, 0x0c, 0x5d, 0xdf, 0x6c, 0x2e, 0xa5, - 0x7d, 0xf8, 0xc5, 0x4c, 0xa3, 0x40, 0xe0, 0xff, 0xf1, 0x50, 0x80, 0x2e, - 0xbf, 0xfc, 0x21, 0x1a, 0xc9, 0xf0, 0xf1, 0xad, 0x90, 0x80, 0x9f, 0xb1, - 0x44, 0xd4, 0x28, 0x56, 0x11, 0x85, 0x16, 0xc2, 0x38, 0x71, 0xbe, 0x3b, - 0xe9, 0xc7, 0xbb, 0x8b, 0xfa, 0xfc, 0xb5, 0x7f, 0x1f, 0xc4, 0x4d, 0x38, - 0x78, 0xa9, 0x9a, 0xdc, 0xe3, 0xbb, 0x5d, 0xfb, 0x07, 0x2a, 0x8c, 0xca, - 0x27, 0xec, 0x6b, 0x74, 0x6a, 0x39, 0xe4, 0xc1, 0x31, 0x6d, 0x31, 0xe0, - 0x31, 0x79, 0x06, 0x5e, 0x24, 0x54, 0xcb, 0x4b, 0x54, 0x25, 0x92, 0xf4, - 0x42, 0x02, 0x8b, 0xcb, 0xb2, 0x6c, 0xc8, 0xc8, 0xe0, 0x88, 0xea, 0xbe, - 0xa0, 0x99, 0x6a, 0x52, 0x21, 0x70, 0x5e, 0x39, 0x91, 0x60, 0xa2, 0x93, - 0x54, 0xb9, 0x78, 0x74, 0xf9, 0x26, 0xc5, 0x2e, 0x7c, 0x1c, 0x76, 0x41, - 0xb6, 0xff, 0x65, 0x59, 0xf4, 0xe4, 0xb1, 0x7c, 0xb3, 0x18, 0x36, 0x9d, - 0x64, 0x80, 0xbb, 0xab, 0x4a, 0xa8, 0x4a, 0x1a, 0x0f, 0x71, 0x8a, 0x43, - 0x8b, 0x68, 0x51, 0x41, 0x03, 0xb8, 0xaa, 0xb4, 0x4f, 0x19, 0xdf, 0x59, - 0xd4, 0x1a, 0x35, 0xf6, 0x88, 0x00, 0x45, 0xbb, 0x38, 0x66, 0x33, 0x53, - 0x56, 0x49, 0x57, 0xec, 0xd8, 0xdb, 0x45, 0xd2, 0x82, 0xda, 0x60, 0x53, - 0xa1, 0x55, 0xb5, 0xae, 0x37, 0xfc, 0xad, 0x97, 0x6a, 0xb3, 0xf0, 0x29, - 0xab, 0x3d, 0xb4, 0xa7, 0x15, 0x9b, 0x2d, 0x8d, 0x7e, 0x2b, 0xd4, 0x97, - 0x1a, 0xc6, 0xb6, 0x22, 0x87, 0x49, 0x04, 0xe1, 0x0d, 0x5b, 0xcb, 0x64, - 0x17, 0x2a, 0x5d, 0xad, 0x1b, 0xfb, 0xcc, 0x55, 0x6c, 0x49, 0xcf, 0x4d, - 0x07, 0x53, 0xca, 0xeb, 0x05, 0xcf, 0x67, 0xb6, 0x83, 0x94, 0xb8, 0x1c, - 0xb4, 0xcc, 0x06, 0x6c, 0xb5, 0xb9, 0x60, 0x35, 0x83, 0x3b, 0x9e, 0xd9, - 0x73, 0xf4, 0xf2, 0xff, 0xd7, 0x7a, 0x22, 0x7d, 0x08, 0x14, 0x16, 0x28, - 0x43, 0x2d, 0x4a, 0x82, 0xf7, 0x9f, 0x1b, 0xe3, 0x7c, 0x7d, 0xbe, 0x33, - 0x9e, 0x1c, 0x5f, 0xc7, 0xf5, 0xd7, 0x57, 0xdd, 0x75, 0xcc, 0xe7, 0xdb, - 0x87, 0x1e, 0xb5, 0xdf, 0x17, 0xcf, 0x1b, 0xcb, 0x92, 0x84, 0x07, 0x0d, - 0xe4, 0xde, 0x75, 0xbc, 0x94, 0x2f, 0xd6, 0x18, 0xce, 0x44, 0x30, 0xec, - 0xbd, 0xe5, 0x41, 0xc6, 0x41, 0xef, 0xec, 0xa9, 0xc8, 0x01, 0xba, 0xe4, - 0x30, 0x30, 0x1d, 0xee, 0x7b, 0x3c, 0xd5, 0xf6, 0x8e, 0x58, 0x62, 0x10, - 0x20, 0x3c, 0x2a, 0x40, 0x84, 0x68, 0x22, 0x13, 0x74, 0x12, 0x69, 0xa4, - 0x08, 0x93, 0xb2, 0x63, 0x1e, 0x39, 0xb6, 0x29, 0x58, 0x80, 0xba, 0x9c, - 0xf1, 0x45, 0x62, 0xf0, 0x06, 0xbd, 0x55, 0x4a, 0x21, 0x61, 0x44, 0xc1, - 0x9f, 0x41, 0x15, 0x61, 0x13, 0x08, 0x81, 0xb8, 0xff, 0xf1, 0x50, 0x80, - 0x2d, 0xdf, 0xfc, 0x21, 0x1a, 0xcd, 0xa4, 0xf1, 0xe8, 0x80, 0x04, 0x99, - 0xb6, 0xb1, 0x5c, 0x69, 0x67, 0xaf, 0x67, 0x1c, 0xdf, 0xcf, 0xdc, 0xca, - 0xe2, 0x71, 0xe3, 0xe3, 0xdf, 0x57, 0xbf, 0x3c, 0xc7, 0xc3, 0x79, 0xd6, - 0x66, 0xbc, 0x55, 0xf3, 0xf1, 0x3e, 0x78, 0xc1, 0x93, 0x0b, 0x90, 0xc5, - 0xbd, 0x16, 0x33, 0x55, 0x7e, 0x9f, 0x41, 0x51, 0x35, 0x6d, 0xc6, 0x75, - 0x44, 0x67, 0x1a, 0x98, 0x8f, 0x4c, 0x80, 0x61, 0xb2, 0xf1, 0xa2, 0xbf, - 0xde, 0x00, 0x3c, 0x23, 0x66, 0x1e, 0x62, 0x77, 0xd8, 0x71, 0x8a, 0x18, - 0x1f, 0x82, 0x48, 0xb2, 0x9d, 0x0d, 0xe9, 0xb6, 0x56, 0x59, 0x4c, 0x4c, - 0x55, 0x31, 0x4c, 0x74, 0xac, 0x7f, 0x85, 0x17, 0xd6, 0x3f, 0x49, 0x02, - 0x62, 0xd5, 0x7b, 0xb8, 0x2b, 0x16, 0xbb, 0x90, 0xb1, 0xa8, 0x70, 0xcf, - 0x2d, 0x6b, 0x77, 0x90, 0xfe, 0x2b, 0xef, 0xdb, 0xc6, 0xad, 0x0e, 0xd0, - 0x48, 0xfc, 0x8f, 0xf0, 0xf4, 0xb9, 0xfc, 0xf3, 0x94, 0xe9, 0x5d, 0x66, - 0x42, 0x16, 0xa2, 0x29, 0x07, 0xf5, 0xbd, 0x8b, 0x15, 0xca, 0xfc, 0xdb, - 0x8b, 0x71, 0x9f, 0xdc, 0xed, 0x60, 0x93, 0x7d, 0xc2, 0xfd, 0x0b, 0x7f, - 0x55, 0x75, 0x90, 0x84, 0x6a, 0xac, 0x88, 0xcd, 0xbe, 0x5e, 0x92, 0x42, - 0xbb, 0x62, 0xd7, 0xa8, 0x8e, 0x7e, 0xc0, 0xa6, 0xcb, 0x5b, 0x16, 0xaf, - 0xe7, 0x1d, 0x8d, 0x8a, 0x2f, 0x8e, 0x43, 0x16, 0x76, 0x8c, 0xef, 0x20, - 0x65, 0x31, 0xb7, 0xf2, 0xb2, 0xb7, 0x1e, 0xc8, 0x51, 0x2f, 0xd4, 0x02, - 0xbb, 0x91, 0xf8, 0xc8, 0x33, 0x40, 0x9e, 0xc7, 0x5d, 0x68, 0x42, 0x14, - 0x35, 0x2f, 0xe3, 0xdd, 0xde, 0xf8, 0x11, 0x7c, 0x9e, 0x0e, 0xf2, 0x94, - 0x94, 0x96, 0xe7, 0xb0, 0xbc, 0x55, 0x23, 0x15, 0x25, 0xc2, 0x66, 0xd1, - 0x05, 0x57, 0xfb, 0xc2, 0xf2, 0x56, 0xb6, 0x95, 0xc4, 0xae, 0x6e, 0x6a, - 0xf7, 0x6e, 0x5c, 0x7d, 0x35, 0x5a, 0xef, 0x87, 0x7a, 0xca, 0xba, 0x40, - 0xa1, 0x3e, 0x0e, 0x64, 0x1d, 0x88, 0x72, 0x8e, 0xa1, 0x78, 0x5e, 0x7c, - 0x01, 0x73, 0xd9, 0xb7, 0xa2, 0x2a, 0x4b, 0xf1, 0x38, 0xe6, 0xfe, 0x20, - 0x39, 0x07, 0x73, 0xb9, 0x71, 0x39, 0x35, 0x31, 0x99, 0x67, 0xce, 0x3a, - 0xe7, 0xbf, 0x1f, 0x93, 0xbe, 0x95, 0xdd, 0xf6, 0x64, 0x23, 0xaf, 0x55, - 0x9b, 0x37, 0x5d, 0xdb, 0x0b, 0xb3, 0x76, 0xdf, 0x76, 0x73, 0x29, 0x77, - 0x33, 0x6d, 0x4c, 0x4c, 0xba, 0xe1, 0x2c, 0xd4, 0x2c, 0x66, 0xf1, 0x2b, - 0x46, 0xfb, 0xb3, 0x36, 0x93, 0xf1, 0x9a, 0x5f, 0x7d, 0xc7, 0xd5, 0xad, - 0xc4, 0x57, 0xff, 0xf1, 0x50, 0x80, 0x2c, 0x1f, 0xfc, 0x21, 0x2a, 0xcf, - 0x3d, 0x61, 0x9b, 0x80, 0x08, 0xa0, 0xa4, 0x39, 0x59, 0x88, 0x76, 0x0a, - 0x18, 0xc8, 0x6b, 0xe9, 0xbb, 0xf5, 0xf5, 0x99, 0x53, 0xcc, 0x8c, 0xe7, - 0x8e, 0x32, 0xd7, 0xdf, 0x40, 0x36, 0x66, 0xab, 0x72, 0x0d, 0xbb, 0xa3, - 0x72, 0x0d, 0x83, 0x45, 0x60, 0x33, 0x39, 0x82, 0x82, 0x16, 0xc0, 0x91, - 0x70, 0x4f, 0x9b, 0x1f, 0x13, 0x36, 0x1a, 0x1b, 0xa1, 0xf3, 0x53, 0xb7, - 0xf1, 0xbb, 0x5d, 0x29, 0x51, 0x44, 0xf2, 0x94, 0xd5, 0x4e, 0xe3, 0x1a, - 0x19, 0x12, 0xb8, 0xbb, 0xaf, 0x7d, 0x4a, 0x44, 0x01, 0x07, 0x02, 0x4c, - 0xd7, 0x4f, 0x42, 0x81, 0x3d, 0x85, 0x45, 0xc0, 0xb0, 0x28, 0x54, 0x45, - 0xea, 0xa4, 0x6b, 0x59, 0x41, 0x89, 0xe4, 0xe5, 0x1d, 0x2a, 0x28, 0x94, - 0x9e, 0x76, 0x27, 0xac, 0xad, 0x78, 0x3f, 0xaf, 0x22, 0x91, 0x67, 0x05, - 0xe1, 0x90, 0xe0, 0xa7, 0xe8, 0x0f, 0xfa, 0xe8, 0x02, 0xb3, 0xe3, 0x1d, - 0xeb, 0xf7, 0x31, 0x94, 0xdb, 0xe8, 0x68, 0x6f, 0x57, 0xe0, 0xce, 0x17, - 0x84, 0xbe, 0x92, 0x29, 0xd9, 0xc0, 0x5e, 0x85, 0xba, 0xe3, 0x94, 0x56, - 0x20, 0x10, 0x08, 0x86, 0x75, 0x11, 0x59, 0x9a, 0xcd, 0xb2, 0xe5, 0x42, - 0xab, 0xf9, 0xe2, 0x30, 0x91, 0x8d, 0xb9, 0xbd, 0x2e, 0x60, 0x2c, 0xcf, - 0xe7, 0x09, 0x7b, 0x6c, 0xf7, 0xe9, 0x82, 0xd4, 0x63, 0xb3, 0xb8, 0xf4, - 0xf1, 0xf8, 0x9d, 0x83, 0x29, 0x12, 0x22, 0xc4, 0x1e, 0x89, 0x46, 0x98, - 0x0d, 0xef, 0x39, 0xc5, 0xbf, 0x20, 0x26, 0x25, 0xe4, 0x8f, 0x0b, 0x6a, - 0xdb, 0x59, 0x3f, 0x67, 0xa5, 0x6d, 0x3f, 0x53, 0x9b, 0xad, 0x0b, 0x7b, - 0x27, 0x7f, 0xeb, 0x35, 0xb4, 0x9a, 0xb4, 0x10, 0xb6, 0x32, 0x2f, 0xde, - 0x6a, 0x71, 0x2f, 0xd7, 0xd7, 0xdb, 0xcf, 0x8f, 0x3e, 0x7b, 0xbe, 0x74, - 0xe3, 0x77, 0xd3, 0xdf, 0xae, 0x95, 0x74, 0xba, 0xac, 0xb1, 0x97, 0x42, - 0x0d, 0x37, 0x31, 0xb8, 0xe5, 0x3b, 0x36, 0xe9, 0xc6, 0x86, 0x1a, 0x72, - 0xe5, 0x4a, 0x88, 0x1a, 0xe3, 0x39, 0x7b, 0x10, 0x1f, 0x38, 0x01, 0x7e, - 0x5d, 0x86, 0x92, 0x8c, 0x9a, 0x45, 0x53, 0x33, 0x3e, 0xce, 0x5c, 0xfd, - 0xd0, 0xec, 0xce, 0xf9, 0x1b, 0x66, 0x52, 0x3b, 0x88, 0x7d, 0xb6, 0x4e, - 0x22, 0xd4, 0x64, 0x2e, 0x87, 0x6e, 0x96, 0xe1, 0x7d, 0xf7, 0x4c, 0x1e, - 0xcc, 0xea, 0x03, 0x94, 0xa6, 0x50, 0x03, 0x71, 0xa7, 0x90, 0xfe, 0xec, - 0x0c, 0x28, 0x81, 0xf6, 0x43, 0x78, 0xff, 0xf1, 0x50, 0x80, 0x35, 0xbf, - 0xfc, 0x21, 0x4c, 0xd8, 0xf0, 0x04, 0xe8, 0x30, 0x15, 0x80, 0x4d, 0xcc, - 0x0d, 0xa5, 0x4d, 0x14, 0xcd, 0x63, 0xdb, 0xd5, 0x81, 0xd5, 0xff, 0x7f, - 0xfb, 0x2f, 0xcc, 0xd3, 0xef, 0xea, 0xf8, 0xcf, 0xf5, 0x9e, 0x7d, 0x00, - 0x1f, 0x59, 0x9d, 0xda, 0xbc, 0xfa, 0xd2, 0x1d, 0xf4, 0xe2, 0x8f, 0xe1, - 0xfd, 0x07, 0xa8, 0xd7, 0xda, 0xf4, 0xf0, 0xad, 0x91, 0x00, 0x02, 0x90, - 0x00, 0x4e, 0xa0, 0xfd, 0x03, 0xfa, 0x8f, 0x4a, 0x2d, 0xa0, 0x26, 0x43, - 0xed, 0x2a, 0x17, 0x92, 0x50, 0x15, 0xe9, 0x7c, 0x2d, 0xd0, 0x5f, 0xb1, - 0x7b, 0x9a, 0x85, 0x7f, 0x0f, 0xf8, 0x06, 0xa1, 0x6c, 0x18, 0xf0, 0x79, - 0x32, 0x09, 0xd9, 0xb9, 0x5f, 0xfb, 0x7f, 0xab, 0xf2, 0xe8, 0xe0, 0x00, - 0x00, 0xb3, 0xba, 0x85, 0x99, 0x64, 0x16, 0x67, 0x2b, 0x82, 0x89, 0x46, - 0xff, 0xeb, 0xbc, 0xc9, 0x9e, 0xee, 0xab, 0x06, 0xd0, 0x20, 0x78, 0x6b, - 0x7c, 0x04, 0x94, 0x3d, 0xf4, 0x4b, 0x66, 0x1f, 0xf9, 0x40, 0xc1, 0xd4, - 0x02, 0x8a, 0x98, 0xc9, 0x60, 0xf1, 0x2b, 0xac, 0xf1, 0x96, 0xef, 0xf7, - 0x51, 0x91, 0x98, 0x85, 0x87, 0xdc, 0x30, 0x25, 0xed, 0xe7, 0x8e, 0x9e, - 0x0c, 0x49, 0x31, 0xd0, 0x20, 0x6a, 0x61, 0x80, 0x30, 0xc1, 0x9c, 0x85, - 0x09, 0x88, 0x50, 0xa8, 0x30, 0xce, 0x86, 0x46, 0xe3, 0xb8, 0xdc, 0x4c, - 0x5a, 0x82, 0x98, 0xfd, 0xdb, 0xc8, 0x69, 0xbd, 0xa1, 0xe4, 0xd4, 0x7f, - 0xb6, 0x6b, 0xfe, 0x81, 0xc8, 0x9a, 0xcb, 0x63, 0xd1, 0xde, 0x75, 0xce, - 0xfa, 0x2e, 0xec, 0xbf, 0x69, 0xda, 0x60, 0xf6, 0x9d, 0xa7, 0xbe, 0xdc, - 0x79, 0x4f, 0x59, 0xd7, 0x53, 0xbb, 0xfe, 0xae, 0xf1, 0x72, 0x93, 0x6a, - 0x45, 0x6b, 0x8a, 0xeb, 0x52, 0x5c, 0x7b, 0x9b, 0x68, 0xa1, 0x65, 0xe9, - 0xd6, 0xd5, 0xa6, 0xd8, 0x8a, 0x09, 0xe7, 0x48, 0x2f, 0x2d, 0xa1, 0x7c, - 0x82, 0xdc, 0x7c, 0xbe, 0xbc, 0xda, 0x11, 0xcf, 0x8c, 0xfa, 0x93, 0x5f, - 0x53, 0x55, 0x6f, 0xfe, 0x35, 0xe7, 0xe7, 0xad, 0xf1, 0xd7, 0x8a, 0xee, - 0x7c, 0xe7, 0xe2, 0x7b, 0x78, 0xe3, 0x24, 0xd4, 0x41, 0xdf, 0x57, 0x1c, - 0x7e, 0x1c, 0xb2, 0x2c, 0x4a, 0xf5, 0x0e, 0x0f, 0x81, 0xe4, 0x75, 0x0d, - 0x75, 0xda, 0x4a, 0x97, 0x56, 0xe5, 0x39, 0x4b, 0x31, 0x42, 0xc6, 0x7c, - 0xb2, 0x51, 0x95, 0x06, 0x91, 0x81, 0x39, 0x77, 0xd1, 0x99, 0x6e, 0x50, - 0x63, 0x05, 0xbb, 0xf4, 0x5a, 0x47, 0x66, 0x13, 0x00, 0xd4, 0x89, 0xbf, - 0x69, 0x10, 0xf5, 0x35, 0xfa, 0xc7, 0xb3, 0x14, 0x53, 0xb2, 0x4b, 0xa0, - 0x0a, 0x22, 0xe8, 0x66, 0xef, 0x9e, 0x86, 0x07, 0x40, 0xce, 0x0c, 0x2a, - 0x6f, 0xff, 0xc3, 0x8b, 0x68, 0x80, 0xdb, 0x8f, 0xf5, 0xee, 0xba, 0xc6, - 0x3a, 0xb6, 0x34, 0x41, 0x4b, 0x31, 0xfa, 0xef, 0x70, 0xc3, 0x38, 0xd7, - 0x0c, 0xc3, 0x75, 0xec, 0x7b, 0xbf, 0xaa, 0xef, 0xb1, 0x51, 0xd6, 0x52, - 0x87, 0xfc, 0xb0, 0x22, 0x7d, 0x0b, 0x86, 0x96, 0x8e, 0x92, 0xf3, 0x0c, - 0xc1, 0x73, 0xf0, 0xff, 0xf1, 0x50, 0x80, 0x3e, 0x7f, 0xfc, 0x20, 0x9f, - 0x4c, 0xdb, 0x45, 0x1a, 0xa1, 0xd0, 0x9a, 0xd9, 0xa7, 0x06, 0xcb, 0x21, - 0xc8, 0xc0, 0x01, 0x6b, 0xd7, 0x53, 0xeb, 0xc5, 0xea, 0xbf, 0xe0, 0xe3, - 0x97, 0x12, 0xb8, 0xeb, 0x48, 0x39, 0xe9, 0xdb, 0x80, 0x46, 0xfa, 0xb6, - 0x39, 0xae, 0xda, 0x97, 0xb7, 0xd6, 0xdf, 0xd3, 0x75, 0xbb, 0x4b, 0xf2, - 0x41, 0xea, 0xb7, 0x91, 0xdd, 0xf7, 0x73, 0xdc, 0x41, 0x96, 0xa6, 0x22, - 0x2c, 0x32, 0x9a, 0x74, 0x47, 0xfe, 0xeb, 0xe8, 0xf3, 0x43, 0x67, 0xaf, - 0xc9, 0xea, 0x81, 0xe8, 0xa9, 0xd2, 0x1b, 0xbd, 0x76, 0x27, 0x2c, 0x40, - 0x07, 0x49, 0x7b, 0xb2, 0x3f, 0x96, 0xd2, 0xf8, 0xf3, 0xe8, 0x57, 0xea, - 0x6d, 0x67, 0x08, 0x76, 0x60, 0x49, 0x43, 0x2f, 0xc8, 0xcb, 0x48, 0x79, - 0x7d, 0x3e, 0x4f, 0xe7, 0x19, 0xa4, 0xe9, 0x13, 0x07, 0x64, 0xfd, 0xfa, - 0x3d, 0xe6, 0xd9, 0xe1, 0xe8, 0xff, 0x7c, 0x7f, 0xbf, 0x20, 0xda, 0x62, - 0xb8, 0x1d, 0xe9, 0xf3, 0x9f, 0x74, 0xf4, 0x5d, 0x4e, 0x5c, 0x80, 0x4e, - 0x69, 0x90, 0x90, 0xd5, 0x2a, 0xcf, 0x26, 0x9f, 0x25, 0xa8, 0x20, 0x42, - 0xae, 0x6f, 0x4d, 0x6c, 0x78, 0x48, 0xd9, 0x32, 0xeb, 0xd1, 0x97, 0x10, - 0x2b, 0x0f, 0xd1, 0xe3, 0xb5, 0x8e, 0xef, 0xfc, 0x45, 0x96, 0xdf, 0x86, - 0xaa, 0xeb, 0xc5, 0x2c, 0x83, 0x12, 0x56, 0x87, 0x23, 0x23, 0x18, 0x53, - 0xff, 0x9f, 0xb6, 0xc2, 0x55, 0x73, 0xf2, 0x24, 0xcf, 0xd1, 0xe7, 0xcc, - 0x90, 0x53, 0xdd, 0xea, 0x32, 0x86, 0x4a, 0x99, 0x36, 0xbd, 0x08, 0x89, - 0xb8, 0x0e, 0x76, 0xf8, 0xd3, 0xbc, 0xc6, 0x96, 0x65, 0xf4, 0xd3, 0x3d, - 0x50, 0x7c, 0xbe, 0x71, 0xda, 0x67, 0x8d, 0x61, 0xf2, 0x65, 0xc0, 0xa4, - 0xb3, 0x15, 0x83, 0x37, 0x92, 0xa4, 0xb5, 0xc4, 0x8c, 0x35, 0x87, 0x9d, - 0xda, 0xfd, 0xbe, 0x81, 0x07, 0x0f, 0x2a, 0x79, 0xe8, 0x5e, 0xb5, 0xa2, - 0x14, 0xca, 0x45, 0x08, 0xf4, 0x30, 0x10, 0x00, 0x00, 0x00, 0x00, 0x03, - 0xdf, 0xff, 0x21, 0xdf, 0x96, 0x20, 0xf8, 0xda, 0x54, 0x1f, 0xde, 0x1c, - 0x94, 0x9a, 0x1c, 0xdb, 0x01, 0x3c, 0x81, 0x54, 0x63, 0xbf, 0xb4, 0xb4, - 0x61, 0x94, 0x15, 0xe2, 0xd0, 0x19, 0x73, 0xf8, 0x5b, 0x9f, 0x2f, 0xc0, - 0xe0, 0x68, 0x5b, 0x44, 0x65, 0xa4, 0x75, 0x4f, 0x98, 0x89, 0x77, 0x98, - 0xc7, 0x9e, 0x8a, 0x54, 0xa2, 0xa6, 0x0b, 0x6d, 0x45, 0xcf, 0x22, 0xc4, - 0xb6, 0x57, 0x2c, 0x81, 0x42, 0x16, 0x60, 0xb2, 0x1c, 0xaa, 0x75, 0x4c, - 0x78, 0xc2, 0xdd, 0x4c, 0x56, 0xc6, 0xd1, 0x51, 0x00, 0x2f, 0xed, 0xd8, - 0x2b, 0xb2, 0x53, 0xaa, 0x97, 0xb4, 0x0c, 0xa9, 0xb5, 0x50, 0x67, 0x25, - 0x04, 0xb1, 0xc1, 0xb4, 0xcf, 0x7f, 0x4b, 0x85, 0xba, 0x13, 0x58, 0x16, - 0x31, 0x3a, 0xc8, 0xcf, 0x7c, 0x1c, 0x1f, 0x0b, 0x68, 0xa9, 0x71, 0x5d, - 0x33, 0xe9, 0xf3, 0x5d, 0x6f, 0x05, 0xfb, 0x01, 0xba, 0x93, 0x26, 0x88, - 0x74, 0x4f, 0xd2, 0x6b, 0xc2, 0xc6, 0xa9, 0xd5, 0xd2, 0x50, 0xf4, 0x5a, - 0x7a, 0xa4, 0x4a, 0x85, 0x25, 0xa1, 0x86, 0x2d, 0x1a, 0xb0, 0x2d, 0xab, - 0x64, 0x5e, 0x69, 0xb0, 0x24, 0x92, 0x9c, 0x30, 0x34, 0x70, 0x4b, 0x3c, - 0x89, 0x44, 0x08, 0x81, 0x7a, 0xaf, 0x45, 0x06, 0x66, 0xa7, 0x78, 0xa1, - 0xcd, 0x13, 0x5e, 0x69, 0xa4, 0xde, 0xbf, 0x7a, 0x2b, 0x73, 0xdb, 0x33, - 0x8c, 0x6e, 0x62, 0xf5, 0xac, 0x1c, 0x6c, 0xfc, 0xb8, 0x8d, 0xfa, 0xde, - 0x75, 0xc7, 0x85, 0x73, 0xb9, 0xde, 0x6e, 0x6c, 0x0d, 0x70, 0xff, 0xf1, - 0x50, 0x80, 0x34, 0x1f, 0xfc, 0x20, 0x9e, 0x7a, 0xd6, 0xd8, 0x23, 0x28, - 0x86, 0x2a, 0x41, 0x30, 0x40, 0x00, 0x00, 0x00, 0x04, 0xab, 0xaf, 0x3a, - 0xe3, 0xe0, 0x4a, 0x05, 0x20, 0x1c, 0xb9, 0x0b, 0xb0, 0x89, 0xdd, 0x41, - 0x10, 0x83, 0xff, 0x70, 0x3e, 0x58, 0xdb, 0xb1, 0x26, 0x08, 0x84, 0xc5, - 0x1c, 0x5a, 0x46, 0xca, 0xef, 0x91, 0x78, 0xb6, 0x61, 0x86, 0xac, 0x9d, - 0x60, 0x5c, 0x0c, 0xf2, 0xdb, 0x67, 0x5e, 0xa6, 0xdb, 0xdd, 0x78, 0x22, - 0x8c, 0x31, 0x2c, 0x80, 0x34, 0x91, 0x42, 0xc1, 0xb2, 0xff, 0x44, 0x8a, - 0xd1, 0x3b, 0xd8, 0x51, 0x34, 0x93, 0x22, 0xdc, 0x4f, 0x20, 0x02, 0x37, - 0xa8, 0xac, 0x0b, 0x5a, 0x41, 0x60, 0x77, 0x7a, 0xdd, 0x1f, 0xbb, 0xb7, - 0x0b, 0x3c, 0x31, 0x9c, 0xad, 0x37, 0x98, 0x92, 0xb7, 0x10, 0xa9, 0x19, - 0x00, 0x0c, 0x80, 0x01, 0xf0, 0xc3, 0xb6, 0x4a, 0xe4, 0x46, 0x66, 0x40, - 0x33, 0x10, 0xf8, 0xf8, 0x4c, 0xaa, 0x01, 0x33, 0x39, 0xad, 0xf3, 0x36, - 0x60, 0xef, 0xbc, 0x21, 0xdc, 0xff, 0xc3, 0x01, 0xe1, 0x17, 0x41, 0x6c, - 0xae, 0xd6, 0xf3, 0x32, 0x16, 0x94, 0x32, 0x25, 0x02, 0xe7, 0x40, 0x98, - 0xd8, 0x43, 0x21, 0xb8, 0x02, 0x56, 0xe2, 0x18, 0x67, 0xb3, 0x11, 0x8a, - 0x35, 0x5a, 0x88, 0x8e, 0x1b, 0x59, 0xed, 0x41, 0x5a, 0xfb, 0xf3, 0xab, - 0x79, 0x16, 0x8a, 0xf5, 0xaa, 0x15, 0x57, 0xef, 0x9e, 0xce, 0x82, 0x22, - 0x01, 0x19, 0x15, 0xd3, 0xe3, 0x5a, 0xe4, 0xc7, 0x42, 0x11, 0x84, 0xea, - 0x10, 0x00, 0x00, 0x00, 0x00, 0x1c, 0x4a, 0xff, 0x61, 0x3a, 0x13, 0x20, - 0x63, 0x88, 0x3a, 0x41, 0x3b, 0x72, 0x3f, 0xb3, 0x2f, 0x85, 0xdb, 0xd2, - 0x16, 0xcc, 0x29, 0x0e, 0x80, 0xbe, 0x3c, 0x94, 0x82, 0x45, 0xa6, 0x58, - 0x98, 0x5e, 0x16, 0x52, 0x32, 0x89, 0x39, 0x07, 0x67, 0xcf, 0x6b, 0x3b, - 0xf5, 0x6a, 0xdf, 0x0b, 0x5a, 0xf1, 0x1c, 0xc5, 0x08, 0x89, 0xa6, 0x02, - 0x02, 0x2b, 0x94, 0x87, 0x33, 0xa8, 0x0a, 0xbd, 0x4d, 0x29, 0xd1, 0xf2, - 0x2b, 0x8e, 0xdf, 0xb0, 0x15, 0xb6, 0x73, 0x91, 0x66, 0x25, 0xe7, 0x38, - 0x48, 0xe4, 0x72, 0x32, 0xce, 0x56, 0x32, 0x15, 0x2c, 0x83, 0xce, 0xb7, - 0x09, 0xdc, 0x47, 0xdf, 0x59, 0xbf, 0x27, 0xc0, 0x37, 0xc8, 0xa9, 0x43, - 0x38, 0x02, 0xc1, 0x7f, 0x0b, 0xd2, 0xbf, 0x46, 0xdd, 0x79, 0xb4, 0x5d, - 0xa2, 0x6f, 0x9d, 0x16, 0xb0, 0x50, 0x55, 0x9b, 0xad, 0x8d, 0x5c, 0xa7, - 0x8f, 0x07, 0xce, 0xad, 0x0e, 0x05, 0xa7, 0x45, 0x7f, 0x19, 0x32, 0x50, - 0x64, 0x37, 0xcd, 0x98, 0xa0, 0x26, 0x3e, 0x6c, 0x8d, 0x80, 0xdd, 0x21, - 0x99, 0x2b, 0xcc, 0x18, 0x33, 0xc2, 0x9d, 0x03, 0x38, 0xd1, 0x24, 0xa0, - 0x3c, 0x2e, 0x4c, 0x65, 0x41, 0x55, 0x49, 0x40, 0xca, 0x42, 0x58, 0x93, - 0x1e, 0x9b, 0x44, 0x4a, 0x84, 0xa1, 0x09, 0xaf, 0x71, 0x5d, 0x7d, 0x3e, - 0x7f, 0x61, 0x54, 0xad, 0xaf, 0x80, 0xff, 0xf1, 0x50, 0x80, 0x29, 0x7f, - 0xfc, 0x21, 0x1a, 0xcd, 0xfb, 0x6c, 0xbb, 0x04, 0x00, 0xa1, 0xb4, 0xca, - 0xc4, 0xe8, 0xb6, 0x2a, 0xa0, 0x2a, 0x2a, 0x2a, 0x29, 0x38, 0xe4, 0xd4, - 0xae, 0x38, 0xd6, 0xfa, 0xef, 0xe3, 0xd4, 0xea, 0xb8, 0xa9, 0xbe, 0x04, - 0xfc, 0x1c, 0x99, 0x28, 0x81, 0x69, 0x4b, 0x4b, 0xbd, 0xef, 0x94, 0x31, - 0x6d, 0x42, 0x98, 0x42, 0x8a, 0x66, 0xb5, 0x45, 0x2d, 0xcc, 0xce, 0xa0, - 0xca, 0x2d, 0xe3, 0x79, 0xbe, 0x99, 0x5f, 0xdd, 0x7e, 0x26, 0x63, 0x46, - 0x15, 0x0a, 0x49, 0xcb, 0xf2, 0x90, 0xa1, 0xbc, 0x65, 0x44, 0x6e, 0x85, - 0x7a, 0xfe, 0xd8, 0xf2, 0x60, 0xd8, 0x3c, 0x6b, 0x34, 0x3c, 0xf5, 0x6b, - 0xc3, 0x0b, 0xc2, 0xca, 0x2e, 0xaa, 0xc1, 0x70, 0x8e, 0x9f, 0x07, 0xc3, - 0x14, 0x64, 0x95, 0x8d, 0xd4, 0x33, 0xb9, 0xda, 0xe0, 0x5e, 0xb4, 0x9d, - 0x4d, 0xf0, 0xab, 0x0e, 0xc8, 0xab, 0x29, 0xd0, 0xdc, 0xfb, 0xa4, 0x5f, - 0x9d, 0x25, 0x3a, 0x17, 0x77, 0x7a, 0xb4, 0xbb, 0xd0, 0x9f, 0xdd, 0x7e, - 0x17, 0xfa, 0xd3, 0x5b, 0x6d, 0xd4, 0x5c, 0x4b, 0x15, 0x63, 0xb6, 0x64, - 0x9b, 0x0c, 0x40, 0x3a, 0x68, 0x3a, 0x82, 0xac, 0xda, 0x7b, 0xc6, 0xdf, - 0xbd, 0x45, 0x69, 0x68, 0x94, 0xaa, 0xa1, 0xd4, 0x47, 0xaa, 0xf9, 0xdf, - 0x30, 0xc3, 0xb2, 0xdb, 0x32, 0xd9, 0x45, 0x89, 0x32, 0x4c, 0xd7, 0xd9, - 0x60, 0x81, 0x8f, 0x83, 0x5a, 0x64, 0x08, 0x89, 0x09, 0xb0, 0x48, 0x4a, - 0xa1, 0x69, 0xec, 0xec, 0xe8, 0x59, 0x44, 0x22, 0xde, 0x24, 0x12, 0xcf, - 0x53, 0x89, 0x8a, 0xf0, 0x95, 0x54, 0x08, 0xa3, 0x29, 0xfd, 0xdd, 0xea, - 0x60, 0x02, 0xb6, 0x55, 0x6d, 0x0d, 0xa4, 0xc5, 0x42, 0x73, 0x1a, 0x3e, - 0x00, 0x35, 0xb0, 0x00, 0x0e, 0x2b, 0x52, 0xb7, 0xad, 0xeb, 0x96, 0xb2, - 0xa5, 0x2c, 0x1a, 0x8c, 0x80, 0x2a, 0x60, 0x48, 0x78, 0x50, 0x2d, 0x29, - 0x45, 0x0a, 0x20, 0x1b, 0xae, 0x7f, 0x29, 0x38, 0x51, 0x4b, 0xd8, 0x92, - 0x0d, 0x86, 0x1c, 0x73, 0xfb, 0x1d, 0x2d, 0xb2, 0x56, 0xb7, 0x6f, 0xbe, - 0x6a, 0x1b, 0xbb, 0xad, 0x73, 0x1d, 0xb9, 0x47, 0x03, 0x9e, 0xe7, 0xdb, - 0x86, 0xfb, 0x4d, 0xd0, 0xe9, 0xdb, 0x14, 0x61, 0x55, 0x59, 0x5e, 0x5e, - 0xc3, 0x4b, 0x97, 0x4c, 0x35, 0xd9, 0xdb, 0xdd, 0x27, 0x0a, 0x39, 0xca, - 0x38, 0xff, 0xf1, 0x50, 0x80, 0x27, 0xdf, 0xfc, 0x21, 0x1a, 0xcf, 0xe1, - 0x97, 0x7a, 0x00, 0x00, 0xa2, 0xb4, 0x3b, 0x04, 0xc8, 0x56, 0x0a, 0x29, - 0x82, 0x00, 0xf8, 0xf7, 0x09, 0x42, 0xf2, 0xf3, 0xdb, 0x51, 0xc4, 0xcf, - 0x3c, 0xd5, 0xd4, 0xd6, 0x71, 0xcd, 0xe8, 0x41, 0xb2, 0x65, 0x02, 0x03, - 0x9d, 0xd5, 0xf0, 0xa5, 0x57, 0x5e, 0x5b, 0xdb, 0x0f, 0x16, 0x21, 0x48, - 0x5d, 0xb4, 0x28, 0xf0, 0x56, 0xac, 0xaf, 0x45, 0x1e, 0x93, 0x85, 0xf0, - 0xf0, 0xff, 0x47, 0xb2, 0x45, 0x97, 0x37, 0x92, 0x4a, 0xfe, 0xc8, 0x54, - 0xdb, 0x70, 0xd1, 0x1c, 0x90, 0x64, 0x27, 0x64, 0x5b, 0x81, 0xe0, 0xe0, - 0x9c, 0x3f, 0x0d, 0xf2, 0x70, 0x60, 0x98, 0x6b, 0x35, 0x00, 0x2b, 0x8a, - 0xca, 0x16, 0xa1, 0x3e, 0xf2, 0x25, 0xa2, 0x72, 0xa8, 0x77, 0xdb, 0x3a, - 0xf9, 0x6e, 0xd2, 0xd5, 0xa4, 0x97, 0x6f, 0x88, 0x60, 0x45, 0x16, 0xdc, - 0xf5, 0x8a, 0x2a, 0x4a, 0xef, 0x45, 0xc1, 0xdc, 0x82, 0x89, 0x77, 0x19, - 0x54, 0xba, 0xe4, 0x26, 0xc0, 0xd2, 0xbd, 0x14, 0x76, 0x57, 0xca, 0x42, - 0x15, 0x7b, 0x60, 0xeb, 0xb5, 0xb1, 0xda, 0x5b, 0xd8, 0xaa, 0x1b, 0x46, - 0xff, 0x3c, 0x08, 0x3a, 0xe3, 0x45, 0x86, 0x99, 0x4b, 0xee, 0x9d, 0x19, - 0x55, 0x68, 0x05, 0x20, 0xf1, 0x11, 0xf0, 0x5f, 0x51, 0xd0, 0xfa, 0x96, - 0xdb, 0x59, 0x02, 0xe2, 0xbc, 0xd7, 0x62, 0xab, 0xad, 0x7b, 0xb3, 0x7b, - 0x77, 0xc6, 0xfa, 0x1e, 0xb1, 0x13, 0xc5, 0x86, 0xf0, 0xa5, 0x0f, 0x7f, - 0x97, 0x74, 0x49, 0x78, 0x9e, 0x3f, 0xf6, 0xf8, 0x2d, 0xe9, 0x0c, 0x74, - 0x09, 0x4c, 0xc2, 0x04, 0x76, 0xad, 0x9b, 0xf3, 0x8a, 0x2b, 0x49, 0x8e, - 0xc2, 0xe5, 0x34, 0xfc, 0x00, 0xf3, 0xe1, 0x78, 0x00, 0xbc, 0xbc, 0x7d, - 0xfe, 0xd2, 0xa6, 0x1b, 0xd4, 0xa8, 0x64, 0x80, 0x05, 0x98, 0x34, 0xf3, - 0xca, 0x4b, 0x84, 0x00, 0x2c, 0x71, 0x45, 0xb8, 0x7f, 0xb0, 0xc4, 0xbf, - 0x69, 0xf1, 0xa7, 0xbb, 0x3d, 0xa8, 0x7a, 0xb3, 0x46, 0x0d, 0x32, 0xb4, - 0x6a, 0x37, 0xed, 0x57, 0x13, 0xd5, 0x97, 0x19, 0x5f, 0x15, 0xbb, 0x74, - 0xef, 0x61, 0x66, 0xa0, 0xd0, 0x1a, 0x6a, 0xb0, 0x36, 0x9e, 0xe1, 0x6b, - 0x64, 0xad, 0xe7, 0xad, 0x85, 0xa2, 0x38, 0xff, 0xf1, 0x50, 0x80, 0x2b, - 0xdf, 0xfc, 0x21, 0x1a, 0xcf, 0xfc, 0x82, 0x40, 0x80, 0x00, 0xa2, 0xb4, - 0x3b, 0x04, 0xa8, 0xc6, 0x30, 0x03, 0xef, 0xe3, 0x9d, 0xfd, 0x73, 0xd7, - 0xdb, 0x5f, 0x7f, 0xdb, 0xe3, 0xd7, 0xd7, 0x37, 0xf5, 0xe3, 0x8e, 0x23, - 0x55, 0x55, 0x37, 0x69, 0x52, 0xda, 0x61, 0x92, 0x47, 0xd8, 0xc3, 0x7f, - 0x70, 0x41, 0xaa, 0xd0, 0x86, 0x41, 0xd1, 0xb0, 0xd7, 0x64, 0x70, 0x8a, - 0x8d, 0x70, 0x0e, 0x30, 0xa2, 0x8d, 0x47, 0x67, 0x9e, 0x0c, 0xc7, 0x29, - 0x13, 0x81, 0xe3, 0x73, 0x75, 0x9a, 0x5f, 0xd5, 0x37, 0x3a, 0x42, 0x59, - 0x56, 0xd3, 0x5b, 0x32, 0x86, 0x7a, 0xd5, 0x55, 0x59, 0x35, 0x82, 0x59, - 0x22, 0x64, 0x73, 0x60, 0x85, 0xd0, 0x70, 0x82, 0xfb, 0x35, 0xe8, 0x65, - 0x88, 0xf1, 0x82, 0xab, 0x60, 0x1b, 0x8b, 0xbd, 0x6c, 0x25, 0x75, 0x65, - 0x94, 0x36, 0x85, 0xdc, 0x83, 0xe2, 0xdc, 0xf1, 0xad, 0x4d, 0x3a, 0x01, - 0x4d, 0xc2, 0x4b, 0xc3, 0xd2, 0x7f, 0x53, 0xba, 0x6c, 0xc9, 0x09, 0xfa, - 0x5c, 0x9b, 0xe2, 0x7f, 0xb1, 0x03, 0xfe, 0x91, 0xa8, 0xb1, 0xdb, 0xa0, - 0x39, 0xf7, 0x6d, 0xf2, 0xa7, 0x74, 0x51, 0x11, 0x78, 0x77, 0x80, 0xbb, - 0x82, 0x80, 0xf6, 0xe5, 0x2b, 0xd3, 0x68, 0x41, 0xf1, 0xa4, 0x05, 0xea, - 0x4e, 0x38, 0x94, 0x57, 0xc9, 0x50, 0x27, 0x4b, 0x52, 0xdf, 0x02, 0x82, - 0xb0, 0x55, 0xb7, 0x29, 0xcf, 0x3e, 0x85, 0x1b, 0xd3, 0xa7, 0x49, 0x3b, - 0xf2, 0x83, 0xcf, 0x4c, 0x99, 0x51, 0x93, 0xd8, 0x75, 0xe4, 0x96, 0x76, - 0x66, 0x5d, 0x5d, 0x54, 0x6d, 0xf1, 0x93, 0x24, 0x53, 0xb3, 0x3b, 0x7b, - 0xf2, 0xa3, 0xc6, 0x59, 0x6e, 0xc9, 0x97, 0xb2, 0x50, 0x89, 0xce, 0xcc, - 0xc6, 0xee, 0xf8, 0xb3, 0x3a, 0xb0, 0xba, 0x74, 0x97, 0x48, 0xc4, 0xe8, - 0x6a, 0x27, 0x6d, 0x32, 0x5f, 0x24, 0xe9, 0x17, 0xd1, 0x41, 0x26, 0x06, - 0x8f, 0x78, 0x02, 0x50, 0x03, 0xae, 0xc7, 0xc7, 0x7f, 0x54, 0xa8, 0x15, - 0xcd, 0xe4, 0x9d, 0x05, 0xd9, 0xf6, 0x99, 0x65, 0x2d, 0xe2, 0xe0, 0x23, - 0x77, 0xd1, 0xb2, 0x8b, 0x93, 0x1d, 0xd6, 0x89, 0xf5, 0xce, 0x9b, 0xf1, - 0x7f, 0x55, 0x86, 0x05, 0xe8, 0x5c, 0x5e, 0x47, 0x1f, 0xeb, 0xbb, 0x16, - 0x35, 0x36, 0x2a, 0xa0, 0xd1, 0xd6, 0x8a, 0xb9, 0x67, 0x82, 0xb5, 0x55, - 0xb1, 0x0d, 0x05, 0xdf, 0x68, 0x8d, 0x69, 0x9b, 0x69, 0xd7, 0xea, 0xd6, - 0x3a, 0x2a, 0x28, 0xe5, 0xc5, 0xcb, 0xae, 0x3c, 0xdc, 0xff, 0xf1, 0x50, - 0x80, 0x2a, 0x7f, 0xfc, 0x21, 0x1a, 0xcf, 0xae, 0x45, 0xff, 0x80, 0x00, - 0xa4, 0xb1, 0x42, 0xd5, 0x68, 0xe6, 0x29, 0xad, 0xdc, 0xd7, 0x8e, 0x3c, - 0xef, 0xc5, 0x7b, 0x47, 0x77, 0x2a, 0x5e, 0x57, 0x1e, 0x4e, 0x6e, 0xb8, - 0x9b, 0x49, 0xad, 0x40, 0x1d, 0xf3, 0xe1, 0x88, 0x29, 0x9f, 0x51, 0x40, - 0x80, 0x6a, 0x58, 0x4d, 0x26, 0xce, 0x07, 0xfd, 0x36, 0xc4, 0xb2, 0x9c, - 0xa5, 0xbc, 0xe7, 0x53, 0x35, 0x71, 0x3a, 0xb1, 0xa1, 0xc7, 0x1d, 0x10, - 0xcc, 0xf6, 0x61, 0xe5, 0xc1, 0x44, 0x4c, 0x69, 0x5d, 0x41, 0xc6, 0x51, - 0x13, 0x5a, 0xa1, 0xb8, 0x50, 0x2b, 0x9a, 0x56, 0x90, 0x44, 0x81, 0xbc, - 0xae, 0xa2, 0x3c, 0x71, 0x56, 0x6f, 0x5f, 0x73, 0xa1, 0x7b, 0x74, 0x4b, - 0xae, 0x4e, 0x63, 0xd4, 0x83, 0x0c, 0x9a, 0x61, 0x77, 0x40, 0xcc, 0xcb, - 0x65, 0xe7, 0x49, 0x84, 0xc5, 0x42, 0x56, 0x2d, 0xf3, 0xbd, 0x84, 0xd7, - 0x6e, 0xdd, 0x32, 0x0d, 0xa9, 0x89, 0x24, 0x39, 0x33, 0xde, 0xbc, 0x30, - 0xb5, 0xf7, 0x47, 0xa5, 0xbf, 0xa3, 0x6a, 0x66, 0x70, 0x5d, 0xb9, 0x5e, - 0x06, 0xd4, 0xde, 0x36, 0x61, 0x25, 0x72, 0x78, 0x8d, 0xa8, 0x49, 0x5f, - 0xf4, 0xad, 0x13, 0xe1, 0x1d, 0xcb, 0xc8, 0x91, 0x7f, 0xaa, 0xb0, 0x4f, - 0xd6, 0x7e, 0x86, 0xc7, 0xa3, 0xbd, 0xa4, 0x3d, 0x37, 0x6d, 0x35, 0xd3, - 0x15, 0x4f, 0x29, 0xdb, 0x4b, 0x80, 0x05, 0x35, 0xb5, 0x6b, 0x38, 0xe3, - 0x6e, 0x2f, 0x2e, 0x55, 0x51, 0xa6, 0x77, 0xb2, 0xb3, 0x22, 0xd7, 0xbb, - 0x1c, 0x2c, 0x22, 0x6a, 0xf5, 0xdb, 0x42, 0x8e, 0x3a, 0xa8, 0xcd, 0x6d, - 0x4b, 0x1e, 0x69, 0x05, 0x12, 0xd6, 0x97, 0xbf, 0xc3, 0x4d, 0x3b, 0x81, - 0xcc, 0x8e, 0x92, 0x10, 0xb0, 0x42, 0x7b, 0xcd, 0x6e, 0x5f, 0xc7, 0xe7, - 0x8f, 0x3e, 0xf8, 0xe3, 0x2e, 0xb3, 0xaa, 0x97, 0x95, 0xc7, 0x93, 0x17, - 0x76, 0xc2, 0x4b, 0xaa, 0xc0, 0xb6, 0x76, 0x0d, 0x0c, 0x2b, 0x46, 0x60, - 0xce, 0xb4, 0xde, 0x82, 0x69, 0xb8, 0x4d, 0x7a, 0xcf, 0x5c, 0xe1, 0xdd, - 0x36, 0xc4, 0xb7, 0xed, 0xbe, 0x56, 0x8c, 0xf1, 0xd9, 0x4b, 0x7d, 0x7d, - 0xb9, 0xc7, 0x11, 0xdb, 0x68, 0xc1, 0xa9, 0x28, 0x56, 0x00, 0xde, 0xa2, - 0xb1, 0x00, 0xd4, 0xb3, 0xb9, 0x66, 0xc7, 0x90, 0x98, 0xcb, 0xc6, 0xf8, - 0xe7, 0x85, 0x74, 0x92, 0xb7, 0x5e, 0xed, 0x41, 0x4c, 0xde, 0xf4, 0xb8, - 0xff, 0xf1, 0x50, 0x80, 0x30, 0x5f, 0xfc, 0x21, 0x1a, 0xcd, 0xfc, 0xa0, - 0x20, 0x00, 0x00, 0xa2, 0xb1, 0xc2, 0x91, 0xac, 0x44, 0x33, 0x1c, 0xbc, - 0xbb, 0xbf, 0x1e, 0xda, 0xaf, 0x7a, 0xf8, 0x7c, 0x7d, 0xb5, 0x49, 0x91, - 0x3a, 0xf2, 0xd6, 0x73, 0x59, 0xcf, 0x1c, 0x4e, 0x1b, 0xd3, 0x91, 0x4b, - 0xf4, 0x41, 0x09, 0x91, 0x6c, 0x53, 0xe7, 0x37, 0xec, 0x6d, 0xcc, 0xe4, - 0xc5, 0x70, 0x82, 0x3e, 0x3f, 0x21, 0xf5, 0x1c, 0xa3, 0xb8, 0x64, 0x39, - 0x74, 0x5b, 0xda, 0x8e, 0x69, 0x4f, 0xd3, 0xe7, 0xca, 0xf5, 0xc7, 0xeb, - 0xdf, 0x45, 0x31, 0x8f, 0x7a, 0x37, 0x3e, 0x11, 0xee, 0xf5, 0xc3, 0x24, - 0xe8, 0x19, 0xe1, 0x35, 0x6c, 0xa9, 0x48, 0x1f, 0x2e, 0x96, 0xb2, 0xb5, - 0x3c, 0xf1, 0xce, 0x6e, 0xe6, 0xb6, 0x8d, 0xbc, 0xa8, 0xc6, 0x16, 0xdf, - 0x2c, 0xc5, 0x7d, 0x16, 0x99, 0xab, 0x9e, 0x31, 0x61, 0x4a, 0x8c, 0x33, - 0x93, 0x10, 0xc5, 0x6d, 0x48, 0x15, 0xb7, 0xb9, 0xe6, 0xbe, 0xd9, 0x9e, - 0xf2, 0x95, 0x2c, 0xae, 0x7a, 0xc1, 0x09, 0x9a, 0xa9, 0x86, 0x21, 0xee, - 0x90, 0x33, 0xaa, 0xed, 0x7a, 0x3a, 0x35, 0x6b, 0x61, 0x9e, 0x14, 0x6a, - 0xbd, 0x35, 0x8f, 0x6b, 0x61, 0x48, 0xb9, 0xd5, 0x69, 0x8a, 0xdd, 0xb1, - 0xdd, 0xe9, 0x4b, 0x40, 0xf0, 0x5d, 0x03, 0x53, 0xed, 0x16, 0x4d, 0x85, - 0xb5, 0x75, 0x37, 0xc7, 0x21, 0x5e, 0x54, 0x7b, 0xd6, 0x48, 0x85, 0x47, - 0x98, 0x38, 0x51, 0x0d, 0x69, 0x00, 0x37, 0x6a, 0x3e, 0x6a, 0x97, 0xe3, - 0x5c, 0xba, 0x2b, 0xe2, 0x45, 0x24, 0xd5, 0xbb, 0x21, 0x10, 0x01, 0x18, - 0xc2, 0xcd, 0x0e, 0x78, 0xc9, 0x7e, 0x07, 0x71, 0xc6, 0x32, 0x0b, 0x9a, - 0x49, 0x4f, 0x19, 0x78, 0xc9, 0x7a, 0x22, 0x00, 0x4f, 0x2d, 0xd6, 0x35, - 0x53, 0x48, 0xd3, 0xc8, 0x74, 0x44, 0x5a, 0x68, 0x08, 0xb9, 0xba, 0x80, - 0x05, 0x15, 0x89, 0xcc, 0xb4, 0xf4, 0x90, 0x7c, 0xac, 0xe3, 0x2e, 0xef, - 0xc7, 0xb7, 0x3c, 0xde, 0xfe, 0xfe, 0x2b, 0x72, 0x49, 0xd7, 0x35, 0x3a, - 0xcf, 0x8e, 0x15, 0x92, 0x6f, 0xaa, 0x52, 0xef, 0xb6, 0x98, 0x64, 0x52, - 0x38, 0x30, 0xd8, 0x00, 0x00, 0x04, 0x24, 0x20, 0x1e, 0xa5, 0x26, 0x8d, - 0x5e, 0xe9, 0x9a, 0x4b, 0x5b, 0xd9, 0xd4, 0x6b, 0x7a, 0x57, 0xa4, 0x3f, - 0xdb, 0x5f, 0xe3, 0xf7, 0x4b, 0xc4, 0xe3, 0xa8, 0xf1, 0xd6, 0xb6, 0xf7, - 0x7b, 0x21, 0x4a, 0x7a, 0x62, 0xaf, 0xb8, 0x4e, 0x71, 0x79, 0xa9, 0xcf, - 0x46, 0x15, 0x11, 0x57, 0x22, 0x95, 0xd4, 0x97, 0x39, 0x32, 0x5f, 0x31, - 0xa5, 0x5e, 0x05, 0xa3, 0x71, 0x56, 0xa5, 0x28, 0x69, 0x49, 0x1a, 0xe5, - 0x18, 0xad, 0x63, 0x3a, 0x8d, 0x08, 0xfe, 0x59, 0xfe, 0x9c, 0xbe, 0x5f, - 0x26, 0xb8, 0xff, 0xf1, 0x50, 0x80, 0x30, 0x9f, 0xfc, 0x21, 0x1a, 0xcf, - 0xf8, 0x00, 0x73, 0x00, 0x00, 0x9e, 0xb6, 0xb1, 0xd0, 0xec, 0xb5, 0x1a, - 0x08, 0x13, 0xdb, 0xdf, 0xcc, 0x7b, 0xd7, 0xc4, 0x9b, 0xb6, 0x4a, 0xb9, - 0xac, 0xe3, 0xd9, 0x9b, 0xf3, 0x98, 0xa8, 0xae, 0x7e, 0xbd, 0x50, 0xf9, - 0xfe, 0xfd, 0x21, 0x5a, 0x8c, 0xb3, 0x4b, 0xb6, 0xf2, 0x82, 0xde, 0xe0, - 0x8f, 0x87, 0x65, 0x8d, 0x4e, 0x62, 0x4d, 0xba, 0xc2, 0x06, 0xab, 0x0e, - 0x05, 0x27, 0x00, 0x72, 0x3c, 0xdf, 0x48, 0x16, 0x84, 0x00, 0xf8, 0x89, - 0x2f, 0x0d, 0xcb, 0x62, 0x01, 0x5d, 0xf8, 0x10, 0xae, 0x7a, 0x56, 0x56, - 0x38, 0xa1, 0xc0, 0xba, 0xef, 0x53, 0x25, 0x70, 0x0f, 0xef, 0x6d, 0x3b, - 0xb6, 0x68, 0x68, 0x78, 0x36, 0x81, 0xd2, 0x1d, 0x17, 0x19, 0xbc, 0x64, - 0x6a, 0x19, 0x67, 0x9d, 0xf3, 0xad, 0x61, 0xeb, 0xe6, 0x61, 0x86, 0xda, - 0x67, 0x99, 0xce, 0x35, 0x6a, 0xce, 0xc0, 0x04, 0xa0, 0x83, 0x29, 0x0d, - 0x98, 0x88, 0x7d, 0xd0, 0xa4, 0x60, 0x65, 0xca, 0x93, 0x00, 0xc6, 0x90, - 0x0a, 0xdd, 0xc6, 0x00, 0xc9, 0xc0, 0x40, 0xc4, 0x8c, 0xb5, 0xb3, 0x05, - 0xb0, 0x31, 0xf3, 0xa8, 0x24, 0x13, 0x14, 0x1c, 0x0a, 0xbc, 0x50, 0x6c, - 0x52, 0x53, 0x90, 0x2a, 0xa7, 0x13, 0xb6, 0x66, 0x81, 0x89, 0x85, 0x27, - 0x16, 0x0b, 0x4d, 0x51, 0x39, 0x84, 0xe2, 0x08, 0x06, 0xb4, 0xa5, 0x75, - 0x14, 0x40, 0xbd, 0xc0, 0x70, 0x14, 0x92, 0x25, 0x20, 0x11, 0x75, 0x00, - 0x26, 0x7b, 0xe9, 0x63, 0x82, 0x51, 0x0d, 0xd6, 0xb0, 0x0d, 0x07, 0x05, - 0x12, 0x9c, 0x12, 0x04, 0x07, 0xd7, 0xed, 0x05, 0xe4, 0xb1, 0xd0, 0xdf, - 0x62, 0x91, 0xcf, 0x07, 0x2e, 0xcf, 0x65, 0xc9, 0xd7, 0xd2, 0xda, 0xa1, - 0xf5, 0x63, 0x6c, 0x3a, 0x94, 0x3b, 0xed, 0xb3, 0x39, 0xda, 0x0b, 0x06, - 0xa3, 0x46, 0xb1, 0xb9, 0xe8, 0x65, 0x86, 0x08, 0xab, 0xf0, 0x98, 0x7d, - 0xa0, 0x9e, 0xde, 0xfa, 0x9c, 0x9a, 0xdd, 0xe1, 0x7c, 0x55, 0xf2, 0xf3, - 0xc4, 0xf3, 0xe3, 0x6c, 0xb9, 0xce, 0xab, 0x69, 0x01, 0x36, 0x7d, 0x6e, - 0xf2, 0x30, 0xb3, 0x2c, 0x6e, 0x12, 0x96, 0x68, 0x50, 0xcd, 0xad, 0x84, - 0x87, 0x3b, 0x05, 0x58, 0x4f, 0x71, 0xc7, 0xc8, 0xcc, 0xb5, 0xf2, 0xc0, - 0xa1, 0xc0, 0x07, 0x1e, 0xb8, 0xf1, 0x51, 0x11, 0xd7, 0xa5, 0xce, 0x13, - 0xcf, 0xa6, 0xdf, 0xbb, 0xc4, 0xf7, 0x4c, 0x2b, 0xbb, 0x18, 0xe4, 0x7a, - 0x70, 0xba, 0x8b, 0x58, 0xb1, 0x57, 0x4c, 0x1b, 0x49, 0xf3, 0xe1, 0x53, - 0xf1, 0xe1, 0x0a, 0x8b, 0xad, 0xa7, 0x74, 0xce, 0x3a, 0x68, 0xe9, 0xee, - 0xa8, 0xef, 0xb9, 0xec, 0xca, 0x50, 0xcd, 0xd4, 0x76, 0xdf, 0x9f, 0x67, - 0x76, 0x43, 0xe9, 0x71, 0x24, 0xe0, 0xff, 0xf1, 0x50, 0x80, 0x2d, 0xdf, - 0xfc, 0x21, 0x1a, 0xcf, 0xf0, 0xc0, 0xf3, 0x80, 0x00, 0xa0, 0xb1, 0x52, - 0xdc, 0x8a, 0xe4, 0x60, 0x75, 0xbf, 0xaf, 0xcf, 0xdd, 0x5e, 0x37, 0xf5, - 0x38, 0xf7, 0xe1, 0x55, 0x24, 0xd6, 0xef, 0xc9, 0x84, 0xa9, 0x4b, 0xef, - 0x52, 0x85, 0x46, 0x29, 0xdd, 0xe4, 0x23, 0xa3, 0x1e, 0x4a, 0xb1, 0xd9, - 0xa4, 0x47, 0x5c, 0x6d, 0x1a, 0xf0, 0x89, 0x86, 0xab, 0x8a, 0x44, 0xd0, - 0x8f, 0x41, 0x22, 0xc3, 0xd0, 0xc1, 0x01, 0xcc, 0xc5, 0x81, 0x26, 0xf7, - 0xf1, 0x0e, 0x8b, 0xd7, 0xbe, 0x21, 0x53, 0xed, 0x1f, 0x6f, 0x67, 0x7d, - 0xe6, 0xd7, 0xed, 0x85, 0x7d, 0x35, 0x7b, 0xef, 0x1e, 0xa5, 0xd7, 0x6f, - 0x09, 0xe7, 0xb8, 0x5d, 0xb7, 0xe9, 0x88, 0x9a, 0xd5, 0xef, 0x70, 0x71, - 0x61, 0x9c, 0x5f, 0x08, 0x99, 0xe5, 0xba, 0x41, 0x71, 0x13, 0x92, 0x25, - 0xcc, 0xc2, 0x4e, 0xd6, 0x5a, 0xa6, 0x7d, 0x7a, 0x94, 0xe5, 0xab, 0xe8, - 0x4e, 0xaf, 0x4f, 0x7e, 0x19, 0xba, 0xa9, 0xef, 0xf6, 0x44, 0xed, 0x76, - 0xea, 0xb9, 0x69, 0x6e, 0x32, 0xa2, 0x27, 0x3f, 0x5c, 0x5d, 0xbb, 0x4f, - 0x99, 0x05, 0x0c, 0xd3, 0xa9, 0xf9, 0xe8, 0xf0, 0x7e, 0xd5, 0x1f, 0x3c, - 0x8a, 0x3d, 0xd8, 0x70, 0x7f, 0x69, 0x81, 0x43, 0x3a, 0xa1, 0x7f, 0x38, - 0x5d, 0x4d, 0x49, 0xa2, 0xc7, 0xc7, 0xf8, 0xbb, 0xe6, 0x84, 0x08, 0xfe, - 0x35, 0x87, 0xba, 0x28, 0x71, 0x8e, 0x5c, 0x25, 0xc8, 0x28, 0x93, 0x7f, - 0x33, 0xc4, 0x77, 0xae, 0x9f, 0xa6, 0x63, 0x13, 0x3c, 0x00, 0x0f, 0x2f, - 0xf8, 0xe7, 0x39, 0x5b, 0x2b, 0x4f, 0x9b, 0x1d, 0xd0, 0x80, 0x8b, 0x78, - 0x0f, 0x23, 0xa7, 0xc8, 0xbb, 0x63, 0x39, 0x23, 0xf5, 0xdc, 0x9d, 0xbc, - 0xfe, 0x29, 0xed, 0x5d, 0x9c, 0xc8, 0x99, 0xad, 0xda, 0xad, 0x04, 0x29, - 0xd0, 0xaa, 0xf7, 0x87, 0x5b, 0xfa, 0xfc, 0xf1, 0x7e, 0x35, 0xbe, 0x17, - 0xdd, 0x75, 0xc5, 0x49, 0xdd, 0xf1, 0x7e, 0x6e, 0xf3, 0x75, 0xac, 0x91, - 0x95, 0x70, 0x13, 0x66, 0x66, 0xa6, 0x1c, 0x2b, 0xa1, 0xa3, 0x53, 0x96, - 0x60, 0xf8, 0x93, 0x4a, 0x2a, 0x31, 0x15, 0xbb, 0xb4, 0x33, 0x6a, 0x4e, - 0x5b, 0x6e, 0xbb, 0x5a, 0x6a, 0xf2, 0xb5, 0xb8, 0x3a, 0xb3, 0x17, 0xc5, - 0xf3, 0xc3, 0x97, 0xb2, 0x9d, 0xcf, 0x05, 0x7d, 0x99, 0x30, 0xab, 0xec, - 0x9a, 0xf3, 0xfe, 0x92, 0x46, 0x48, 0xaa, 0xa1, 0x99, 0x57, 0x38, 0x56, - 0x87, 0xa6, 0x2a, 0x3b, 0x27, 0x0a, 0x54, 0x77, 0xc8, 0xa4, 0xdc, 0xe5, - 0xc6, 0x11, 0xdc, 0x85, 0x8c, 0xa8, 0xce, 0x23, 0xdf, 0x8c, 0xc3, 0x3c, - 0xff, 0xf1, 0x50, 0x80, 0x2b, 0xff, 0xfc, 0x21, 0x1a, 0xc9, 0xe4, 0x74, - 0xff, 0x04, 0x7f, 0xa5, 0xb1, 0xc1, 0x9c, 0x8a, 0xe4, 0x81, 0xc3, 0xcf, - 0x7f, 0xe9, 0xfb, 0x71, 0xc7, 0x3d, 0xef, 0xda, 0x5f, 0x77, 0x2a, 0xea, - 0x55, 0xd7, 0x57, 0x5c, 0xe9, 0x4c, 0xe2, 0xe7, 0x26, 0xa0, 0xec, 0x79, - 0xd4, 0xff, 0x4c, 0xac, 0x0b, 0xb0, 0x5c, 0xf9, 0x35, 0x33, 0x8e, 0xaa, - 0x99, 0xf2, 0xd7, 0x21, 0x26, 0x7a, 0x47, 0x1a, 0x4d, 0x3f, 0x37, 0xf0, - 0x6d, 0xc5, 0x55, 0xf2, 0x31, 0xe3, 0xbd, 0xf5, 0xa9, 0x8e, 0xc3, 0xce, - 0x6a, 0xd0, 0x77, 0x6c, 0xe1, 0x50, 0x2a, 0xb4, 0xb4, 0xe1, 0x7b, 0xd9, - 0x6a, 0xad, 0xee, 0xda, 0xd2, 0x97, 0x19, 0x94, 0xf7, 0xa8, 0x21, 0x26, - 0x8d, 0xca, 0xed, 0xb9, 0x6c, 0x5d, 0x9d, 0x8a, 0xc8, 0x78, 0xfc, 0xcd, - 0xa4, 0x6d, 0xf8, 0x88, 0xde, 0xde, 0x40, 0xc3, 0xd0, 0x40, 0x06, 0xef, - 0x83, 0x6b, 0x2b, 0x63, 0x14, 0xd2, 0x37, 0xa2, 0xc4, 0xc3, 0x42, 0x50, - 0x7a, 0x4e, 0xbc, 0x82, 0x93, 0x5f, 0xb2, 0xc2, 0x73, 0xe6, 0x7f, 0x09, - 0x55, 0x8f, 0x37, 0xbd, 0x05, 0xcc, 0x05, 0xc1, 0x64, 0xa2, 0xe6, 0xcd, - 0x36, 0xa0, 0xcb, 0x39, 0x02, 0x3e, 0xdd, 0xb0, 0xa3, 0xbf, 0x5b, 0x19, - 0x73, 0xca, 0x50, 0xe1, 0x57, 0xd7, 0xc9, 0xf5, 0xbd, 0x45, 0xa3, 0xa9, - 0xe0, 0x38, 0x1c, 0x34, 0x8f, 0x13, 0xca, 0x17, 0x64, 0x57, 0xb8, 0xfb, - 0x4b, 0xad, 0x7e, 0x7b, 0x57, 0x7f, 0x2f, 0xc3, 0x21, 0xbb, 0x82, 0x27, - 0xe7, 0xce, 0x9f, 0x44, 0x07, 0xf2, 0x94, 0x15, 0x28, 0x6d, 0x0c, 0xa5, - 0x57, 0x99, 0x83, 0xc8, 0x0f, 0x3d, 0xfb, 0x7b, 0xfc, 0x37, 0xcd, 0x7b, - 0x46, 0xf9, 0xe2, 0xee, 0xa5, 0x5d, 0x75, 0xed, 0x54, 0xca, 0xd3, 0x2e, - 0xe9, 0x92, 0xc4, 0xa6, 0x39, 0x36, 0x2c, 0xa8, 0x72, 0x0d, 0x02, 0xf0, - 0x2d, 0xa8, 0xa3, 0x00, 0xb4, 0x68, 0xd8, 0xf7, 0xd9, 0x28, 0xea, 0x23, - 0xeb, 0xb4, 0x57, 0x1f, 0xc4, 0xe3, 0x62, 0x00, 0xf0, 0x08, 0xa7, 0xd6, - 0x5a, 0x89, 0x00, 0x27, 0xf6, 0x88, 0x6e, 0x42, 0x1e, 0xcf, 0x0d, 0x3f, - 0xa4, 0x94, 0x9b, 0x93, 0x5e, 0x11, 0x68, 0x87, 0x18, 0x54, 0x97, 0x9d, - 0xcb, 0x37, 0xa5, 0x6d, 0xae, 0x7a, 0x57, 0x18, 0xb6, 0xf4, 0xbc, 0x8d, - 0x2a, 0x69, 0x6a, 0xd9, 0xde, 0xd9, 0xa3, 0xee, 0xef, 0xe1, 0x15, 0xdf, - 0x4f, 0x7f, 0x66, 0xea, 0x82, 0xfd, 0xf2, 0x14, 0xa8, 0x01, 0x0d, 0x4b, - 0x15, 0x0b, 0xc0, 0xff, 0xf1, 0x50, 0x80, 0x40, 0x7f, 0xfc, 0x20, 0xa4, - 0x2a, 0xd6, 0x58, 0x43, 0x29, 0x18, 0xc1, 0x43, 0x8f, 0x6d, 0x73, 0xf5, - 0xeb, 0x8b, 0x7a, 0xdf, 0xc5, 0xdf, 0x31, 0x4d, 0x4d, 0xdc, 0x35, 0x95, - 0x15, 0x5f, 0x1d, 0xd1, 0x93, 0x81, 0x6f, 0x8a, 0x4f, 0x80, 0xd9, 0xa2, - 0x0f, 0xd6, 0x0e, 0x8b, 0xf3, 0xd6, 0xa8, 0x82, 0x0f, 0x75, 0xf6, 0x28, - 0x50, 0xea, 0xf8, 0x79, 0x79, 0x6d, 0x8c, 0x63, 0x65, 0x6b, 0xeb, 0xbb, - 0xa7, 0x10, 0x26, 0x86, 0xf5, 0x94, 0x5d, 0x4d, 0x7c, 0x2c, 0x14, 0x04, - 0xfb, 0x3c, 0x4d, 0xa5, 0xc1, 0x4c, 0x5f, 0x06, 0x40, 0xee, 0x84, 0x28, - 0x88, 0xe5, 0xe0, 0xd0, 0xd6, 0xcc, 0x07, 0x02, 0xa6, 0xab, 0x4d, 0x15, - 0x15, 0x3f, 0x6c, 0x6a, 0x96, 0x5e, 0x38, 0x35, 0x10, 0xcf, 0xec, 0xf6, - 0xa6, 0xc2, 0xa4, 0xcf, 0x98, 0x80, 0xf5, 0xa1, 0xdd, 0xae, 0xa5, 0xa3, - 0xd7, 0xde, 0x01, 0x1d, 0x54, 0x07, 0x11, 0x52, 0xa5, 0xe6, 0x86, 0x50, - 0xfd, 0xef, 0xef, 0xf4, 0xff, 0xb6, 0x05, 0x60, 0x30, 0x6a, 0xc3, 0x8b, - 0x1b, 0x79, 0x42, 0x65, 0xe3, 0xe6, 0xd1, 0x05, 0x13, 0x33, 0x8f, 0xc1, - 0xd8, 0x2f, 0xb3, 0x46, 0x27, 0x00, 0xfd, 0x1f, 0x4d, 0x07, 0x61, 0x4f, - 0xdc, 0x30, 0xee, 0xc0, 0x05, 0xfb, 0x23, 0x03, 0xf1, 0x92, 0x0f, 0xb0, - 0x95, 0x2f, 0xa2, 0xee, 0x13, 0xdf, 0x75, 0x94, 0x01, 0x9c, 0x0a, 0xe4, - 0x94, 0x52, 0x72, 0xc2, 0x2d, 0xfe, 0xbb, 0x13, 0x1c, 0xdd, 0x27, 0xac, - 0xcf, 0x99, 0x4d, 0xae, 0x7c, 0x99, 0x10, 0x94, 0x9f, 0xc0, 0xa5, 0xd5, - 0xa0, 0x89, 0xbc, 0x60, 0xb3, 0x93, 0x96, 0xa0, 0x60, 0x0d, 0xb4, 0x5e, - 0x20, 0x67, 0x58, 0x43, 0xac, 0x67, 0xdc, 0x16, 0xc0, 0x7e, 0x9a, 0xb3, - 0xe1, 0xa1, 0x3f, 0xa6, 0xe3, 0x75, 0x3e, 0x1b, 0x46, 0x35, 0xac, 0x70, - 0xd5, 0x8a, 0x11, 0x82, 0x85, 0x17, 0xed, 0xeb, 0xef, 0xfa, 0x7d, 0xe7, - 0x7c, 0xe7, 0xd4, 0xd7, 0xbb, 0x57, 0x25, 0x2e, 0xba, 0xe3, 0x76, 0xb7, - 0x2b, 0xcd, 0x73, 0x76, 0xc1, 0xdf, 0x75, 0xcc, 0x1d, 0x9b, 0x2c, 0x07, - 0xc8, 0x52, 0xdd, 0xd2, 0xa8, 0x30, 0xf4, 0x42, 0x97, 0xa9, 0xe0, 0x63, - 0x47, 0x1d, 0xe3, 0x05, 0x3f, 0x30, 0x4b, 0xd3, 0x9b, 0x46, 0xf4, 0x2e, - 0x3a, 0x32, 0x80, 0x78, 0x26, 0xca, 0x64, 0x88, 0x54, 0xfd, 0x9e, 0x06, - 0xf8, 0x6f, 0x3f, 0xf1, 0xfd, 0x55, 0xea, 0x83, 0xae, 0xbd, 0x15, 0xc2, - 0x35, 0xc7, 0xfd, 0xce, 0x31, 0x9c, 0x73, 0xda, 0x08, 0xaf, 0x64, 0x55, - 0x6f, 0x82, 0xa9, 0x4f, 0x5f, 0x24, 0xdc, 0x66, 0x71, 0xb3, 0xa3, 0x56, - 0xd9, 0x30, 0x26, 0x26, 0x7c, 0xa7, 0x66, 0xb4, 0xe5, 0xd6, 0xb9, 0xac, - 0x67, 0xbb, 0xd5, 0x8b, 0xce, 0xe6, 0x33, 0xca, 0xc4, 0x38, 0xf6, 0xcf, - 0x57, 0x54, 0x15, 0xed, 0xb5, 0xea, 0xab, 0x79, 0xa9, 0xd7, 0x45, 0xe2, - 0x4a, 0xaa, 0xbc, 0xb9, 0x7b, 0xb5, 0x99, 0xee, 0x45, 0xf0, 0xda, 0x7b, - 0xf8, 0xcf, 0x17, 0x6e, 0x38, 0x62, 0x4e, 0xfe, 0xd9, 0xb9, 0x8e, 0xaf, - 0x5d, 0xc6, 0xa4, 0xc0, 0xcd, 0xce, 0x48, 0x63, 0x23, 0x18, 0xe7, 0xf0, - 0x0e, 0x02, 0x3d, 0x9e, 0xe0, 0x03, 0x1d, 0x99, 0xcd, 0xe0, 0xe5, 0xef, - 0xc4, 0x47, 0xd8, 0x0e, 0xd2, 0x14, 0x01, 0xd3, 0x90, 0xb2, 0x93, 0x37, - 0x92, 0xf7, 0xd8, 0xb0, 0xe2, 0x62, 0xa2, 0x2a, 0x0d, 0x77, 0x5e, 0x27, - 0x36, 0xbd, 0xd4, 0x07, 0x3d, 0x95, 0xff, 0xc3, 0x66, 0xdd, 0x36, 0xea, - 0x1f, 0xc0, 0xfd, 0xcc, 0x37, 0x7c, 0xd8, 0xad, 0xdd, 0xcf, 0x8c, 0xf9, - 0xe7, 0xd1, 0x05, 0x07, 0xf6, 0x97, 0x7a, 0x5d, 0x05, 0xb1, 0x79, 0x80, - 0x76, 0x70, 0xff, 0xf1, 0x50, 0x80, 0x3b, 0x1f, 0xfc, 0x20, 0x9d, 0x4c, - 0xc7, 0x65, 0x50, 0x73, 0xa6, 0xcc, 0x79, 0x7c, 0x45, 0xb2, 0xd1, 0x33, - 0x10, 0xef, 0x95, 0xfe, 0x9f, 0xc6, 0x71, 0x3c, 0xdf, 0xd6, 0xef, 0xfe, - 0x8d, 0x7d, 0x7a, 0x9d, 0x6d, 0xf8, 0xf7, 0xeb, 0xfa, 0x1e, 0x7b, 0x93, - 0x8d, 0xd7, 0x9e, 0x10, 0xef, 0xb7, 0x9e, 0x78, 0x09, 0x74, 0x6d, 0x8b, - 0xc5, 0x36, 0x71, 0x7a, 0xe1, 0xe2, 0xee, 0x9e, 0xa8, 0x48, 0xec, 0xf4, - 0x4f, 0x48, 0xe2, 0xdb, 0xa9, 0x87, 0x93, 0x2d, 0x63, 0x3b, 0x00, 0x4d, - 0xbc, 0x77, 0xd2, 0x0c, 0xb2, 0xdb, 0x13, 0x13, 0x26, 0x3e, 0xdd, 0x9e, - 0xef, 0xbe, 0x7e, 0x1d, 0x55, 0x46, 0x21, 0x0d, 0x94, 0xc7, 0x7a, 0xd2, - 0x2e, 0xc2, 0xe3, 0x92, 0x8b, 0x38, 0xdf, 0x2d, 0x57, 0x98, 0xba, 0x8b, - 0xd3, 0x73, 0x67, 0x31, 0x3e, 0xdc, 0x33, 0x76, 0xa2, 0xfb, 0x0f, 0xf5, - 0x73, 0x27, 0xff, 0x6b, 0x83, 0x46, 0xd4, 0xa5, 0x63, 0x3f, 0x73, 0xe8, - 0xed, 0x8a, 0xf8, 0xe6, 0x60, 0x2d, 0xda, 0x6a, 0xea, 0x7c, 0x1d, 0x66, - 0xcb, 0xf9, 0x58, 0xc5, 0x84, 0x93, 0x50, 0x09, 0x2b, 0x27, 0x85, 0xf5, - 0x8f, 0x92, 0xe7, 0x3b, 0xf5, 0x53, 0x56, 0xeb, 0x48, 0x88, 0x58, 0x87, - 0x14, 0x69, 0xe6, 0xbe, 0xc8, 0x74, 0xcd, 0x7f, 0xe5, 0xb4, 0x93, 0x41, - 0x89, 0x47, 0xe7, 0x79, 0x77, 0x40, 0xbd, 0x64, 0x47, 0x8f, 0xed, 0xab, - 0x2e, 0xa0, 0x0d, 0x1a, 0xf6, 0x65, 0xb3, 0x16, 0x70, 0xa7, 0x85, 0x4e, - 0xfd, 0x11, 0x1f, 0x28, 0xd6, 0x34, 0x8a, 0x18, 0xb9, 0x86, 0xe2, 0xe4, - 0x90, 0xc4, 0x2b, 0x28, 0x61, 0x7e, 0x0c, 0x6c, 0x86, 0x95, 0x62, 0x81, - 0x4a, 0x35, 0xad, 0x4c, 0x64, 0xc3, 0x0c, 0x3e, 0xff, 0x3f, 0x5f, 0x6f, - 0xaf, 0xe7, 0xfe, 0x36, 0xf8, 0xcf, 0x3f, 0x9a, 0xd6, 0x9b, 0xbd, 0xdd, - 0xdf, 0x35, 0xd7, 0x7e, 0x5e, 0xb5, 0x93, 0x8d, 0xfb, 0x4e, 0x2c, 0x70, - 0x79, 0x35, 0x5e, 0xb7, 0xc9, 0x9a, 0x61, 0x11, 0xc9, 0xcb, 0x5a, 0x21, - 0x4e, 0xb5, 0xa3, 0x07, 0x0a, 0xc9, 0x6d, 0xac, 0x50, 0x6e, 0x80, 0x05, - 0xc2, 0x7f, 0xe2, 0x76, 0x09, 0x4a, 0x5e, 0x13, 0xd8, 0x81, 0xf1, 0x79, - 0xc3, 0x9c, 0x1e, 0x7c, 0x3b, 0xdc, 0x0f, 0x86, 0x74, 0x48, 0xb5, 0xe7, - 0x32, 0xb3, 0x9f, 0x23, 0x2c, 0xa5, 0xb8, 0x78, 0x40, 0x3d, 0x23, 0x0b, - 0x7c, 0xd5, 0x43, 0x50, 0x00, 0x2a, 0x87, 0xe0, 0x28, 0x88, 0x97, 0x25, - 0x38, 0xfe, 0x5f, 0xf5, 0xce, 0xa6, 0x37, 0x3e, 0xc3, 0x68, 0xf2, 0x0f, - 0x77, 0xaf, 0x69, 0xf1, 0xe7, 0xc3, 0x87, 0xee, 0xe3, 0xa8, 0x0f, 0xbd, - 0x7a, 0x58, 0x2b, 0xbd, 0x20, 0x7c, 0x8b, 0x6d, 0x29, 0x0b, 0x33, 0xaf, - 0x06, 0x66, 0x19, 0x7a, 0x02, 0xd6, 0x61, 0xfe, 0x0a, 0x00, 0x03, 0xf0, - 0x77, 0x78, 0x15, 0x86, 0x22, 0x04, 0x28, 0x40, 0x83, 0x15, 0x20, 0x7a, - 0x89, 0x15, 0x50, 0xf9, 0x12, 0xca, 0xc0, 0xfb, 0x14, 0x91, 0xbc, 0x6b, - 0x1e, 0x24, 0xb7, 0xd4, 0xd2, 0x62, 0x44, 0xb2, 0x31, 0x11, 0xd4, 0x1f, - 0xbe, 0x71, 0x76, 0xf5, 0xe7, 0xc1, 0xc4, 0x6a, 0xe6, 0x20, 0x08, 0x06, - 0x13, 0x6e, 0x13, 0xac, 0xa5, 0x30, 0x84, 0x76, 0x61, 0xb2, 0xf7, 0x9b, - 0x3b, 0x76, 0x18, 0x5d, 0x0f, 0xd9, 0xef, 0xc6, 0xef, 0x66, 0x19, 0x65, - 0x7c, 0xa9, 0x0c, 0xed, 0x5c, 0xfc, 0xff, 0xf1, 0x50, 0x80, 0x38, 0xff, - 0xfc, 0x20, 0xa8, 0x7a, 0xd6, 0x26, 0x3a, 0x79, 0x88, 0x27, 0x19, 0xaf, - 0x1d, 0x57, 0x5d, 0xbd, 0xbc, 0x75, 0xbf, 0x3e, 0x2b, 0x09, 0xac, 0xf3, - 0xde, 0xa4, 0xc6, 0xb2, 0xe6, 0xb3, 0x47, 0x42, 0xbf, 0xf3, 0xdd, 0xa5, - 0x05, 0xc2, 0x61, 0xd6, 0xef, 0x4c, 0xb6, 0xda, 0x5a, 0x46, 0x6f, 0x13, - 0xa3, 0xb5, 0x27, 0x59, 0xa2, 0x98, 0x47, 0xf6, 0x69, 0xbb, 0x8f, 0xb5, - 0x0f, 0x08, 0xe2, 0xfb, 0xff, 0xb7, 0xe9, 0xf8, 0x00, 0xe4, 0x80, 0x79, - 0x64, 0x6b, 0x03, 0x6c, 0xc9, 0xfc, 0xe4, 0x61, 0xf3, 0xae, 0x0d, 0x8b, - 0xba, 0x6a, 0x1e, 0x59, 0x2a, 0xb7, 0x61, 0xcc, 0xb1, 0x0a, 0xec, 0x01, - 0xdb, 0x37, 0xd3, 0xfa, 0x78, 0x33, 0x10, 0x8d, 0xdc, 0xfd, 0xdc, 0x87, - 0x84, 0x4b, 0x30, 0x72, 0xec, 0x31, 0x25, 0x4e, 0xbe, 0x45, 0x65, 0xdd, - 0x4d, 0x79, 0x3d, 0xfc, 0x2f, 0x38, 0x64, 0x69, 0x11, 0xb3, 0x47, 0x1b, - 0x5a, 0x35, 0x7e, 0x51, 0xa9, 0xcd, 0x17, 0x41, 0xf3, 0x70, 0x8a, 0xe7, - 0x19, 0x86, 0x34, 0x0b, 0xce, 0x46, 0x40, 0x29, 0xfb, 0xbd, 0x07, 0xc8, - 0x04, 0xa6, 0x4a, 0x1b, 0x5b, 0x17, 0xa1, 0xf4, 0x55, 0x41, 0x38, 0xec, - 0xfe, 0x37, 0x1c, 0xfc, 0xfa, 0x23, 0xbf, 0xa3, 0xe7, 0x4b, 0x44, 0x72, - 0xae, 0xdf, 0x0f, 0xb0, 0x37, 0x74, 0xb8, 0xee, 0xb9, 0x92, 0xe2, 0x08, - 0x59, 0xa7, 0x96, 0xc8, 0x74, 0xd2, 0x94, 0x22, 0x4f, 0x22, 0xc5, 0x48, - 0x83, 0x45, 0x75, 0x4e, 0x44, 0xa0, 0x23, 0x79, 0xe1, 0x7d, 0x4e, 0xb6, - 0x61, 0xfd, 0x4f, 0x0a, 0x76, 0x3b, 0x0a, 0x76, 0x4b, 0xbc, 0xa9, 0x06, - 0xb5, 0x91, 0x9a, 0x96, 0x62, 0x01, 0xf5, 0xf3, 0x14, 0x01, 0x2a, 0x50, - 0xe3, 0x7d, 0x53, 0x8d, 0xf7, 0x7d, 0x55, 0x3d, 0xa7, 0x5c, 0x0e, 0x7b, - 0xba, 0x4f, 0xb1, 0xa7, 0x50, 0xcf, 0x2b, 0x49, 0x66, 0xea, 0x64, 0x6a, - 0x7c, 0x26, 0xa7, 0xd3, 0x5a, 0x59, 0xc4, 0x22, 0xc9, 0x1c, 0x76, 0xb4, - 0xcc, 0xf0, 0x4c, 0xd0, 0x8f, 0x28, 0x11, 0x15, 0xce, 0xbd, 0x97, 0xf2, - 0xf0, 0x8b, 0x7e, 0xb0, 0xc5, 0x80, 0x51, 0x96, 0x36, 0x15, 0x54, 0xb3, - 0x59, 0x9b, 0x90, 0x0c, 0x2b, 0x3c, 0xc1, 0xf1, 0xa0, 0x5d, 0x1d, 0x23, - 0xa5, 0x5e, 0xe8, 0x97, 0xb2, 0x94, 0x54, 0xbd, 0x12, 0x38, 0x1c, 0xa7, - 0x9d, 0x82, 0x25, 0x5b, 0xcf, 0xcd, 0x18, 0x9c, 0xe1, 0x24, 0x31, 0x5a, - 0x38, 0x95, 0xa4, 0x46, 0xbe, 0x42, 0x6c, 0x1a, 0xe6, 0x90, 0xc9, 0x26, - 0xa5, 0xa9, 0x7e, 0xf7, 0xbf, 0xa0, 0x46, 0x71, 0xe8, 0xa5, 0x98, 0x52, - 0x40, 0xb0, 0x6a, 0x77, 0x2e, 0x3a, 0x27, 0x3b, 0x65, 0x34, 0x58, 0x15, - 0xb1, 0xf1, 0xb7, 0x1a, 0x84, 0xa1, 0xd3, 0x7e, 0xc8, 0x4c, 0x87, 0x95, - 0xbd, 0x87, 0xd8, 0x59, 0x38, 0x21, 0xac, 0x6a, 0xd9, 0x4c, 0x56, 0x67, - 0x00, 0x86, 0xd5, 0x34, 0x12, 0x48, 0xf6, 0x2a, 0x77, 0xa9, 0xf7, 0x48, - 0x25, 0x02, 0xe4, 0xf2, 0x10, 0xb9, 0x3c, 0x82, 0xe1, 0x9e, 0x24, 0xa6, - 0x9b, 0xc9, 0xf9, 0xfb, 0x26, 0x6d, 0xdd, 0xeb, 0x12, 0x85, 0x2f, 0xe5, - 0x5f, 0x3d, 0x1e, 0xef, 0x97, 0x3d, 0x7f, 0xe7, 0x69, 0x9e, 0x99, 0x75, - 0xd5, 0xd9, 0xd3, 0xb2, 0x8e, 0xff, 0xf1, 0x50, 0x80, 0x23, 0x5f, 0xfc, - 0x21, 0x1a, 0xce, 0xf2, 0xb3, 0xde, 0x4b, 0x30, 0xa8, 0xa1, 0x32, 0x11, - 0x44, 0x84, 0x78, 0xbb, 0xcb, 0xe7, 0xf1, 0xeb, 0x8e, 0x7a, 0xe6, 0xed, - 0xca, 0xae, 0xa5, 0x1c, 0x38, 0xdf, 0x17, 0x7b, 0xd6, 0x4c, 0xa9, 0x75, - 0x63, 0x07, 0xed, 0xbd, 0x4d, 0x4d, 0xb5, 0xd9, 0x8b, 0x5f, 0x32, 0xdc, - 0x21, 0xd5, 0x74, 0xa7, 0xa0, 0xe8, 0x92, 0x7a, 0xa4, 0x1f, 0x59, 0x63, - 0xac, 0x93, 0x2a, 0x96, 0x9c, 0xd6, 0x00, 0xe0, 0xaa, 0xc8, 0x9e, 0x0c, - 0xf0, 0xa6, 0xb0, 0x62, 0xf4, 0xff, 0xa7, 0x39, 0x81, 0xe6, 0x49, 0x18, - 0xe1, 0x21, 0xa5, 0xa1, 0x89, 0x9c, 0xca, 0x52, 0xad, 0x7b, 0x44, 0xa1, - 0xc1, 0x9f, 0x2b, 0x67, 0xf8, 0x0f, 0x16, 0xa5, 0xcb, 0x44, 0x34, 0xd8, - 0xf5, 0x26, 0xce, 0x22, 0xb1, 0xd8, 0x30, 0x26, 0xb2, 0x05, 0x1b, 0xb7, - 0x8d, 0x68, 0xc3, 0x2b, 0xf4, 0x28, 0x49, 0xbf, 0xf2, 0xde, 0xab, 0x4a, - 0x31, 0x67, 0xf1, 0x50, 0x68, 0xde, 0xd7, 0x91, 0xa5, 0xad, 0xcf, 0xaa, - 0x43, 0x65, 0x5d, 0x29, 0xf5, 0xc2, 0x1a, 0x26, 0x05, 0x20, 0xe9, 0x0d, - 0x07, 0xdc, 0xd1, 0x24, 0xc9, 0xf1, 0xfc, 0x72, 0xea, 0x48, 0xef, 0x9c, - 0x97, 0x6a, 0xee, 0x63, 0x41, 0x03, 0x4b, 0xd7, 0x74, 0x6b, 0xee, 0x49, - 0xc0, 0x04, 0x77, 0x04, 0x2d, 0x0b, 0x3e, 0x5e, 0x8b, 0x18, 0x26, 0x0b, - 0x6b, 0x73, 0xed, 0xee, 0x6b, 0xde, 0xe2, 0x8d, 0x8b, 0xd2, 0xb5, 0x05, - 0x2a, 0x0b, 0x3e, 0x33, 0xd8, 0x85, 0xe8, 0x5c, 0xf4, 0x87, 0x15, 0xdb, - 0xdf, 0x41, 0x99, 0xa8, 0x61, 0x9d, 0xfe, 0x22, 0xef, 0x2f, 0x9b, 0xbd, - 0xeb, 0x35, 0x7c, 0xef, 0xa5, 0x5f, 0x3c, 0x51, 0x36, 0xa4, 0x89, 0x93, - 0x72, 0x97, 0x40, 0xad, 0xb5, 0xfd, 0x72, 0x46, 0xca, 0xf9, 0xde, 0x97, - 0xad, 0x1e, 0x63, 0x4d, 0xd7, 0x68, 0xb2, 0xc2, 0xd9, 0x5f, 0x02, 0xc9, - 0xa8, 0xa9, 0xdb, 0xa9, 0x98, 0x22, 0xc0, 0xd8, 0x00, 0x4c, 0x38, 0xff, - 0xf1, 0x50, 0x80, 0x2a, 0x3f, 0xfc, 0x21, 0x1a, 0xce, 0xf4, 0xeb, 0xab, - 0x4f, 0x1f, 0xa7, 0xa1, 0xb3, 0x12, 0x0c, 0x14, 0x3a, 0x90, 0xea, 0xea, - 0x4e, 0xfe, 0x39, 0x94, 0x9f, 0x7f, 0x7c, 0x94, 0xeb, 0x95, 0xa4, 0x5c, - 0xcf, 0x3d, 0xdd, 0x73, 0x23, 0x5a, 0xef, 0xa1, 0x81, 0xe7, 0x76, 0x77, - 0xd5, 0xcd, 0x66, 0x4b, 0x24, 0x96, 0xed, 0x61, 0xbb, 0x9b, 0x53, 0x7e, - 0xc6, 0x46, 0xbd, 0x63, 0xbc, 0x4a, 0x30, 0x87, 0x96, 0x47, 0x9e, 0xbf, - 0x3c, 0xe2, 0xd5, 0xe3, 0x53, 0xf7, 0xd0, 0x95, 0x9a, 0xf2, 0x10, 0x12, - 0xb6, 0xec, 0x1b, 0x9d, 0x7e, 0xf9, 0x00, 0x1b, 0xbb, 0x5c, 0xf2, 0xb7, - 0x7d, 0xcd, 0x82, 0x8e, 0xe5, 0x67, 0x33, 0x96, 0xcc, 0x72, 0x48, 0x51, - 0x0e, 0x16, 0x86, 0xd9, 0xd3, 0x3d, 0x10, 0x90, 0xdd, 0x19, 0x8e, 0x60, - 0x57, 0x70, 0x04, 0x39, 0xa6, 0x8f, 0x5d, 0xf7, 0x08, 0x73, 0x02, 0x5d, - 0x08, 0x17, 0x98, 0xd8, 0x7e, 0x63, 0xef, 0xe6, 0x24, 0xbd, 0xa3, 0x8b, - 0x30, 0x78, 0x3a, 0xf2, 0x40, 0xa6, 0xe9, 0xaa, 0x15, 0x8b, 0xe0, 0x22, - 0xe7, 0x73, 0xf4, 0x43, 0x17, 0x9d, 0x74, 0x39, 0x01, 0x0c, 0x49, 0x68, - 0xaf, 0x27, 0x55, 0x50, 0x09, 0xe8, 0xc3, 0x7c, 0x69, 0xbb, 0x2e, 0x06, - 0xa0, 0xf3, 0x56, 0x34, 0x13, 0x8a, 0x5a, 0x03, 0xd9, 0xcc, 0x71, 0x6e, - 0x5d, 0x26, 0x35, 0xbb, 0x5e, 0x74, 0x15, 0x6b, 0x8e, 0x31, 0x7a, 0x5f, - 0x4d, 0x3e, 0xd2, 0x4e, 0x7e, 0xea, 0xc3, 0x53, 0xcc, 0x86, 0x3b, 0x13, - 0xdf, 0xcf, 0xd1, 0xda, 0x63, 0xa7, 0xd3, 0x33, 0xd9, 0xc0, 0xad, 0x56, - 0x30, 0x1d, 0x9b, 0xcd, 0xbb, 0xb3, 0x40, 0xaf, 0x1e, 0x9e, 0xfe, 0xd8, - 0x7b, 0xab, 0x39, 0x53, 0xb2, 0x91, 0x62, 0x6e, 0x42, 0x07, 0x8e, 0x3d, - 0xaa, 0x4f, 0x5f, 0x7f, 0xb4, 0xe9, 0x2d, 0xe3, 0xaa, 0x47, 0x3e, 0x7c, - 0x7b, 0x4c, 0x56, 0xf8, 0xdf, 0x19, 0xf1, 0x59, 0x2f, 0x8e, 0x60, 0x0d, - 0xfd, 0xcf, 0xe5, 0xee, 0x48, 0x55, 0x03, 0x10, 0xb7, 0x15, 0x52, 0xe2, - 0xbe, 0x8b, 0x81, 0x97, 0x5e, 0x35, 0x4f, 0x53, 0x10, 0x4d, 0xf4, 0x28, - 0x22, 0xec, 0x1b, 0x65, 0x3a, 0x0e, 0xa5, 0x0b, 0xec, 0x24, 0xe8, 0x47, - 0xb4, 0x77, 0xe8, 0x38, 0x2f, 0xa4, 0xb5, 0xf5, 0xce, 0xd9, 0xf7, 0x13, - 0xfc, 0xdc, 0xf7, 0x97, 0xf6, 0x67, 0x30, 0xda, 0x3c, 0x4e, 0x9b, 0x80, - 0xff, 0xf1, 0x50, 0x80, 0x2a, 0x5f, 0xfc, 0x21, 0x1a, 0xcf, 0xc8, 0x39, - 0xab, 0x60, 0x12, 0xa5, 0xa4, 0x32, 0x11, 0x8c, 0x14, 0x52, 0x90, 0xbe, - 0x24, 0xce, 0x7d, 0xaa, 0xf2, 0x2f, 0x03, 0xae, 0x75, 0xbf, 0x3e, 0x3d, - 0xb7, 0xe7, 0xbd, 0xf5, 0x53, 0x77, 0x75, 0x49, 0x9d, 0x03, 0x3a, 0xbf, - 0x5f, 0xa2, 0x2a, 0xa4, 0xa8, 0xb2, 0x4e, 0x60, 0x72, 0x85, 0x80, 0x55, - 0x6b, 0x92, 0xcc, 0x6e, 0x08, 0x78, 0x2c, 0x19, 0x06, 0x9a, 0x20, 0xa6, - 0x25, 0x35, 0x04, 0xcf, 0x45, 0xbd, 0xe3, 0x39, 0x5c, 0xe2, 0xf4, 0xc7, - 0xab, 0x29, 0x31, 0xc3, 0x6e, 0xd7, 0x7a, 0xed, 0x49, 0x6c, 0xa5, 0x9e, - 0xba, 0xe8, 0x89, 0xeb, 0xdd, 0x56, 0x5e, 0x0e, 0x8f, 0xe3, 0x1f, 0xc0, - 0x8f, 0x04, 0x47, 0x42, 0x65, 0xb0, 0xdf, 0xdf, 0x65, 0x2b, 0xda, 0xde, - 0x4b, 0xc4, 0xc2, 0xac, 0x89, 0x67, 0x70, 0x13, 0x92, 0xcb, 0xd4, 0xd9, - 0xbc, 0x11, 0x18, 0xf5, 0x9b, 0xa6, 0xdb, 0x83, 0x21, 0x20, 0xb5, 0x54, - 0x0b, 0x9e, 0x76, 0x0d, 0xc8, 0x21, 0x33, 0x7c, 0xf8, 0xff, 0x23, 0xd4, - 0xa5, 0x00, 0x8e, 0x86, 0x14, 0x2f, 0xcc, 0xac, 0xdb, 0xb7, 0xd6, 0x74, - 0x29, 0xc6, 0x88, 0xec, 0x3f, 0xc7, 0xe3, 0x2b, 0x24, 0x60, 0xea, 0xac, - 0xad, 0x86, 0x84, 0xbd, 0x2d, 0x94, 0xb0, 0xe9, 0xbf, 0x51, 0x36, 0x6b, - 0x39, 0xf0, 0xee, 0xbf, 0xc4, 0xd8, 0x30, 0x9e, 0xeb, 0x0d, 0x29, 0x2d, - 0x5d, 0x4c, 0x0b, 0xaa, 0x93, 0x5e, 0x47, 0x09, 0xe8, 0x91, 0x29, 0x13, - 0x8f, 0x9f, 0x4c, 0xc5, 0xd1, 0xcf, 0x6b, 0xee, 0xca, 0xab, 0xb7, 0x80, - 0x70, 0xc1, 0x9d, 0xef, 0xb2, 0x6f, 0x73, 0xc7, 0xa4, 0xaf, 0x7f, 0xf1, - 0x00, 0x52, 0xb2, 0x96, 0x3c, 0x36, 0x0f, 0xb0, 0xbe, 0x26, 0xd9, 0xc7, - 0x72, 0x4e, 0xaf, 0x3b, 0x74, 0xcd, 0x73, 0xc5, 0x4f, 0x6a, 0x6f, 0xa9, - 0x57, 0x97, 0x52, 0xaf, 0x77, 0x40, 0xab, 0xfc, 0x92, 0x3b, 0xee, 0x23, - 0x5c, 0xec, 0xcc, 0x22, 0x48, 0xe5, 0x35, 0xbd, 0x63, 0xe7, 0x17, 0x5d, - 0x7c, 0x2f, 0x69, 0xae, 0xe8, 0x9a, 0xcc, 0xcb, 0xbb, 0x07, 0x54, 0x8d, - 0xd6, 0x3a, 0xd7, 0xa8, 0x11, 0xa3, 0x98, 0xdc, 0x4a, 0xd1, 0x52, 0xa8, - 0x70, 0x59, 0xa2, 0x29, 0x08, 0xcc, 0x22, 0x6a, 0x24, 0x24, 0x20, 0x95, - 0xc6, 0x25, 0x26, 0x85, 0xca, 0xf6, 0x90, 0x99, 0xd5, 0x85, 0x00, 0x7b, - 0xac, 0xe0, 0xff, 0xf1, 0x50, 0x80, 0x2a, 0x7f, 0xfc, 0x21, 0x2a, 0xcd, - 0xb8, 0xc1, 0x5d, 0x80, 0x00, 0xa2, 0xa2, 0x32, 0x91, 0xec, 0x24, 0x5a, - 0x85, 0x53, 0x5a, 0x4e, 0xf8, 0xab, 0xc9, 0x21, 0x52, 0x54, 0xc8, 0xf2, - 0x4e, 0x2f, 0x95, 0x2a, 0xd7, 0x49, 0x63, 0x60, 0x95, 0xd0, 0xd7, 0x59, - 0x6b, 0x54, 0x88, 0xf8, 0x06, 0x3c, 0xb9, 0xcd, 0x7f, 0xfb, 0xca, 0xa7, - 0xe2, 0x20, 0x58, 0xc3, 0x7e, 0x93, 0x01, 0xdd, 0x53, 0x58, 0x58, 0x8f, - 0xe5, 0x3b, 0x93, 0x5c, 0xeb, 0xa6, 0xa6, 0xaf, 0xc2, 0x96, 0x0e, 0xdf, - 0x42, 0x53, 0x1c, 0xdc, 0x89, 0x6c, 0xb7, 0x45, 0x42, 0x7f, 0x9e, 0x05, - 0x59, 0xeb, 0x9e, 0x9d, 0x07, 0x6e, 0x94, 0xed, 0x2a, 0xc8, 0xf8, 0xc8, - 0x4d, 0x4e, 0x36, 0x16, 0x8c, 0x6c, 0x5d, 0xfd, 0x0c, 0xdf, 0x3c, 0x41, - 0x54, 0xd1, 0xae, 0x83, 0xcc, 0xab, 0x52, 0x24, 0xd0, 0x50, 0x47, 0x04, - 0x01, 0xc6, 0x71, 0xb9, 0xed, 0x60, 0x49, 0xb5, 0xd2, 0x36, 0x22, 0xde, - 0xcc, 0x15, 0xa2, 0xdf, 0x61, 0x04, 0xe9, 0x2e, 0x1c, 0xc8, 0x5e, 0x02, - 0x96, 0x7b, 0xc3, 0xd0, 0x82, 0xb9, 0x77, 0x33, 0xe2, 0xd5, 0xb6, 0xb6, - 0x06, 0xde, 0x7f, 0xa5, 0x34, 0x12, 0xfe, 0x0c, 0xa1, 0xc1, 0x5b, 0xa5, - 0x99, 0x70, 0xa7, 0x6c, 0x84, 0x0d, 0x41, 0x34, 0xcc, 0x85, 0xa1, 0xaf, - 0xfc, 0x3e, 0x74, 0x96, 0x29, 0xf4, 0xef, 0x31, 0x0b, 0xb3, 0x43, 0x17, - 0xe1, 0x01, 0x79, 0x80, 0x66, 0x43, 0xf7, 0xcf, 0xed, 0x01, 0xdd, 0xd5, - 0x22, 0x01, 0x00, 0x7d, 0xee, 0x97, 0xaf, 0x0a, 0xad, 0xd2, 0xef, 0xbd, - 0xb6, 0xc1, 0x4f, 0xe7, 0xd5, 0xb6, 0xcd, 0x72, 0x1e, 0x1e, 0x15, 0xda, - 0x07, 0xf4, 0x7d, 0x32, 0x57, 0x7e, 0x45, 0x6f, 0x7b, 0xdd, 0x15, 0x89, - 0x92, 0x8b, 0x23, 0x7b, 0xc6, 0x9a, 0x9d, 0xb1, 0xd4, 0xdd, 0xb1, 0xd6, - 0xf5, 0xce, 0xa3, 0x3e, 0x15, 0x32, 0x8b, 0xdc, 0xba, 0xba, 0xb0, 0x1e, - 0x47, 0xe5, 0xe5, 0xb8, 0xb6, 0x0e, 0xac, 0xdd, 0x00, 0x8a, 0x9e, 0xfa, - 0xfd, 0xec, 0xe3, 0xba, 0x6b, 0x4f, 0x31, 0xff, 0x9f, 0x12, 0xd7, 0xd3, - 0xe8, 0x54, 0x77, 0xc2, 0xc2, 0x53, 0xa1, 0xd2, 0x93, 0x24, 0x92, 0xe3, - 0x6c, 0x95, 0x65, 0x5c, 0x56, 0xbd, 0xb9, 0x80, 0x72, 0x86, 0xd7, 0x74, - 0x64, 0x33, 0x14, 0xa1, 0xdb, 0x08, 0xa4, 0x09, 0x5f, 0xc9, 0x40, 0x95, - 0x33, 0xa1, 0xb4, 0xd8, 0xbe, 0xff, 0xf1, 0x50, 0x80, 0x36, 0xff, 0xfc, - 0x21, 0x4c, 0xcc, 0xb6, 0x10, 0x42, 0x61, 0x0c, 0x46, 0x4b, 0x52, 0x58, - 0x0b, 0x48, 0x3b, 0x2d, 0xcd, 0x96, 0x56, 0x6a, 0x41, 0x54, 0x8c, 0xcd, - 0x9a, 0xd1, 0x40, 0x76, 0xfd, 0x7c, 0xde, 0x79, 0xf1, 0xd7, 0x7d, 0x7e, - 0xbf, 0x67, 0xa0, 0x6b, 0x65, 0xb3, 0xf5, 0xf9, 0xbb, 0xd7, 0xeb, 0xf3, - 0xf1, 0x56, 0x1d, 0x38, 0x3b, 0x00, 0x3e, 0xc0, 0xe9, 0x17, 0x26, 0xae, - 0xea, 0x42, 0x5b, 0xe5, 0xaf, 0xae, 0x58, 0xca, 0x0a, 0xd6, 0xf3, 0x21, - 0x13, 0xa6, 0x2c, 0xb7, 0x88, 0x40, 0x04, 0x0e, 0xfd, 0x9e, 0xcf, 0xf9, - 0xc1, 0x3b, 0x1f, 0xaa, 0x6c, 0xf3, 0x55, 0xef, 0xca, 0xe9, 0xe3, 0x2c, - 0xa2, 0x22, 0xe7, 0x55, 0x91, 0xc8, 0x7e, 0xdf, 0xec, 0x7e, 0x13, 0x3a, - 0xb2, 0x9e, 0x2c, 0xcf, 0xc6, 0x51, 0x29, 0xcf, 0x3b, 0x3c, 0x31, 0xb6, - 0x40, 0x03, 0x8a, 0xd3, 0x33, 0x8c, 0xf3, 0x2a, 0x1f, 0x6b, 0x24, 0xb0, - 0xc1, 0x23, 0x40, 0x6c, 0x64, 0xf6, 0xda, 0xd8, 0x46, 0x9d, 0xf6, 0x0f, - 0x72, 0x30, 0xec, 0x48, 0xb0, 0x6a, 0xbb, 0x67, 0xc6, 0xf9, 0xcf, 0x0a, - 0x0b, 0x06, 0x38, 0xe6, 0x66, 0x17, 0x36, 0x70, 0xcf, 0xc0, 0x9b, 0xba, - 0x23, 0xa0, 0xdc, 0xf8, 0xcc, 0x12, 0xdd, 0x17, 0x8b, 0xfc, 0x56, 0x76, - 0x55, 0xb6, 0xe1, 0x0b, 0xe1, 0x77, 0x74, 0xf0, 0xa8, 0xe4, 0xb1, 0x4b, - 0xa3, 0xe2, 0xfc, 0x5b, 0x7b, 0x86, 0x62, 0x09, 0x09, 0x42, 0x13, 0xae, - 0xbf, 0xa6, 0x52, 0xe0, 0x27, 0xd4, 0x83, 0x91, 0x22, 0xac, 0x06, 0xde, - 0x49, 0xc5, 0xb2, 0x6c, 0xd5, 0xeb, 0x64, 0xf2, 0x8c, 0x05, 0xa3, 0xa1, - 0x9d, 0x8f, 0x64, 0x2a, 0xaf, 0x35, 0x9d, 0xde, 0x73, 0xb8, 0x3e, 0x0d, - 0xca, 0xd1, 0xcd, 0xb1, 0xf8, 0x39, 0x53, 0x16, 0x59, 0x0c, 0xe2, 0x07, - 0xdd, 0x6b, 0xd0, 0xd2, 0x7d, 0x8e, 0xfd, 0x2d, 0x96, 0xcd, 0x65, 0x49, - 0x5a, 0x9c, 0x67, 0x5f, 0x8f, 0x1a, 0x99, 0xfd, 0x18, 0x3a, 0xdc, 0x97, - 0xde, 0xf3, 0xaa, 0xeb, 0xbf, 0xf1, 0x35, 0x56, 0x1d, 0x56, 0xc5, 0x1d, - 0x42, 0x39, 0x42, 0x05, 0x32, 0xd6, 0x3e, 0xf4, 0x03, 0x50, 0xbe, 0x37, - 0x79, 0xb4, 0xe4, 0x48, 0xbc, 0xc8, 0x83, 0x3e, 0xd9, 0x23, 0x0f, 0x74, - 0x09, 0x46, 0xf6, 0x3c, 0x7d, 0xa1, 0xdc, 0x79, 0xac, 0xf9, 0x54, 0x47, - 0x78, 0xbf, 0x39, 0x63, 0xc0, 0xf8, 0xc9, 0x35, 0x37, 0xc3, 0xf8, 0x67, - 0xc4, 0x46, 0x3f, 0x57, 0xfc, 0x5f, 0x4f, 0x93, 0x00, 0x3a, 0xee, 0x38, - 0x8f, 0x1b, 0xf7, 0x60, 0x34, 0x6e, 0x4f, 0x07, 0x29, 0xee, 0x0f, 0xc8, - 0x4d, 0x35, 0xfe, 0x87, 0xca, 0x18, 0xe2, 0x47, 0xdc, 0x7a, 0xaf, 0x66, - 0x9e, 0x92, 0x75, 0x9d, 0x51, 0x87, 0x6c, 0x2d, 0x32, 0x9d, 0x39, 0x34, - 0x1f, 0x4b, 0xed, 0xa7, 0xd4, 0xf9, 0xfb, 0xdf, 0x4a, 0xd9, 0x56, 0x21, - 0x97, 0x4d, 0x50, 0xd5, 0x3c, 0x46, 0xfe, 0xeb, 0x24, 0xc4, 0x41, 0xcb, - 0x53, 0xa3, 0x12, 0xb0, 0x9a, 0x6c, 0xdd, 0x40, 0xed, 0x2a, 0x92, 0x9e, - 0x4e, 0xda, 0x55, 0xef, 0xbb, 0x87, 0x8a, 0xaa, 0x30, 0x6f, 0xab, 0x80, - 0xff, 0xf1, 0x50, 0x80, 0x39, 0x9f, 0xfc, 0x21, 0x4c, 0x9a, 0xa8, 0x00, - 0x20, 0x00, 0x00, 0x40, 0x49, 0xda, 0x48, 0x37, 0x4d, 0xfb, 0x69, 0xb9, - 0x15, 0x66, 0x23, 0x54, 0x40, 0x01, 0xe4, 0x07, 0x9e, 0x2d, 0xef, 0xfe, - 0xdf, 0xf6, 0x00, 0x25, 0x00, 0x16, 0x8e, 0x7b, 0x40, 0xdc, 0x70, 0x5d, - 0x98, 0x8e, 0x23, 0x28, 0xc3, 0xe2, 0xa8, 0x61, 0xd5, 0xb7, 0x7f, 0xa1, - 0xa5, 0xe5, 0xd0, 0x9b, 0x2c, 0xfd, 0xa3, 0x61, 0xd9, 0x0a, 0x15, 0x98, - 0x02, 0x84, 0x02, 0x44, 0xa2, 0x2e, 0x37, 0x62, 0x50, 0x40, 0xb9, 0x10, - 0x86, 0x34, 0x5b, 0x0f, 0x97, 0x7a, 0xdb, 0x5a, 0x35, 0x68, 0x86, 0xa8, - 0xe4, 0x3c, 0xd7, 0x1b, 0x76, 0xe6, 0xc8, 0xd1, 0x7b, 0x3d, 0xb4, 0x3a, - 0x0a, 0x73, 0x10, 0x9e, 0x2f, 0xca, 0x68, 0xc7, 0x46, 0x38, 0x15, 0xb1, - 0xf2, 0xb9, 0x7e, 0x3e, 0x01, 0x1e, 0x7e, 0x9f, 0xea, 0x79, 0x8c, 0x89, - 0x40, 0xc3, 0xc5, 0x89, 0xa5, 0x73, 0xbe, 0x56, 0x22, 0xa8, 0xec, 0x26, - 0xa8, 0x0f, 0xa9, 0xe4, 0x2e, 0xc7, 0x28, 0x15, 0x49, 0x33, 0x5d, 0x09, - 0x5f, 0xed, 0xfc, 0x9f, 0x83, 0x59, 0xa2, 0xf9, 0x33, 0xd1, 0x6e, 0xfa, - 0x1a, 0x4a, 0xab, 0x43, 0x6e, 0x1e, 0x1d, 0xc0, 0x08, 0x6c, 0x20, 0x67, - 0x76, 0x7c, 0x7d, 0xb7, 0xcc, 0xcc, 0x86, 0x0a, 0x40, 0x01, 0xa1, 0x6b, - 0xbe, 0x87, 0xba, 0xaa, 0x66, 0x19, 0x10, 0x00, 0x30, 0x09, 0xb0, 0xa7, - 0x6d, 0xa5, 0xe2, 0x29, 0x17, 0xf2, 0x8b, 0x1b, 0x4a, 0x6b, 0x96, 0xf5, - 0x26, 0x70, 0x59, 0x35, 0x83, 0x24, 0x09, 0x1d, 0x20, 0xf2, 0xcb, 0x42, - 0x29, 0xc6, 0x28, 0x4b, 0x5e, 0x9a, 0xcf, 0xd7, 0x01, 0x9b, 0x6c, 0x0c, - 0x5b, 0x6a, 0xa9, 0x91, 0x81, 0x0a, 0x48, 0xb0, 0x4a, 0xc0, 0xa8, 0x57, - 0x9d, 0x1c, 0xe2, 0x0d, 0x80, 0x9e, 0x2c, 0x85, 0x55, 0xec, 0x47, 0xb0, - 0xcb, 0xd2, 0x26, 0xbb, 0xb1, 0x60, 0xfb, 0x01, 0x83, 0x0f, 0x33, 0x68, - 0xe6, 0x9f, 0x9a, 0x6d, 0x34, 0xbd, 0x69, 0xaf, 0xdb, 0x2d, 0xbd, 0xc1, - 0xf2, 0xf3, 0xe3, 0xcf, 0x83, 0xc1, 0xcf, 0x15, 0xd0, 0x3c, 0xdf, 0x9c, - 0x71, 0xe7, 0xfe, 0xc0, 0x7c, 0x6e, 0xb4, 0x0f, 0x3d, 0xdb, 0x73, 0xa8, - 0x25, 0x85, 0x48, 0xba, 0xbe, 0x63, 0x79, 0x36, 0xad, 0x78, 0xec, 0x7b, - 0x2f, 0xee, 0x8b, 0xe9, 0x94, 0x71, 0xf2, 0x8c, 0x59, 0x00, 0xa5, 0x0b, - 0x77, 0x3f, 0x2c, 0xd9, 0xbb, 0x01, 0xd9, 0xbe, 0x43, 0x92, 0xad, 0x8c, - 0xf9, 0xd9, 0x0d, 0x9b, 0x9f, 0x47, 0xbf, 0xca, 0x08, 0x86, 0x29, 0x24, - 0xe6, 0xb6, 0xd9, 0x86, 0x6b, 0xcf, 0x5c, 0x90, 0x12, 0x5b, 0x1f, 0x84, - 0x55, 0x8f, 0xb5, 0xe5, 0xe1, 0x9e, 0xe3, 0xee, 0xe0, 0xfc, 0xa9, 0x9a, - 0x3a, 0x24, 0xfe, 0x8c, 0x10, 0xc5, 0x6c, 0xb6, 0x2c, 0x62, 0xcc, 0xf3, - 0xc9, 0x39, 0xdb, 0xdd, 0x93, 0x25, 0x3b, 0x65, 0x94, 0x44, 0x37, 0xd5, - 0x3d, 0xea, 0x49, 0x7b, 0x59, 0x4c, 0x52, 0x03, 0x48, 0x22, 0x90, 0xb4, - 0x96, 0x2f, 0x2b, 0xd3, 0x12, 0xe0, 0xf1, 0x4a, 0xdb, 0xa5, 0x69, 0xcf, - 0xa7, 0x56, 0xf5, 0x93, 0x29, 0x4a, 0x6c, 0xd7, 0x72, 0xee, 0x81, 0x0e, - 0x6a, 0x97, 0x62, 0xc0, 0x98, 0x1a, 0x5d, 0x74, 0x57, 0x0e, 0xcf, 0xb3, - 0xae, 0x9c, 0xc3, 0x1c, 0xff, 0xf1, 0x50, 0x80, 0x2d, 0x1f, 0xfc, 0x21, - 0x7a, 0xc8, 0x00, 0xe0, 0x60, 0x00, 0x00, 0xa0, 0xb1, 0x42, 0xd0, 0xea, - 0x64, 0x53, 0x1a, 0x02, 0x12, 0xbe, 0xff, 0x6f, 0x6f, 0x5a, 0xdb, 0x8d, - 0xdd, 0xe7, 0x5d, 0xec, 0x4a, 0xf8, 0xcb, 0xad, 0xcd, 0xea, 0xf3, 0xef, - 0xeb, 0xeb, 0xac, 0xe7, 0xd8, 0x44, 0xbe, 0xdf, 0xb6, 0x6a, 0xdf, 0x8f, - 0x80, 0x0d, 0x88, 0xd6, 0x3d, 0xeb, 0x53, 0x1e, 0x7e, 0x12, 0x61, 0x29, - 0xc0, 0x03, 0xee, 0x74, 0xf0, 0x78, 0xcb, 0x78, 0x1f, 0x5a, 0xea, 0xaa, - 0xef, 0xfc, 0x8a, 0xf6, 0xb8, 0xa0, 0xba, 0xbd, 0xe6, 0xc9, 0xa6, 0x77, - 0x2f, 0x78, 0xdd, 0x42, 0xf8, 0x0c, 0xf7, 0xeb, 0xfb, 0x7e, 0xe8, 0x84, - 0x16, 0x82, 0xe2, 0xb5, 0x26, 0x0c, 0x5c, 0x4c, 0x60, 0xbe, 0xba, 0xa0, - 0x68, 0x16, 0x15, 0xc9, 0x7a, 0x29, 0xa7, 0x5b, 0x33, 0xaf, 0x71, 0x89, - 0xe5, 0xee, 0x9f, 0x07, 0x1c, 0xf1, 0x65, 0x7d, 0x51, 0x37, 0xed, 0xa6, - 0xab, 0xaa, 0x75, 0x3b, 0xe8, 0xd5, 0x42, 0xe3, 0x72, 0xac, 0x28, 0x30, - 0x2a, 0xf6, 0x54, 0x6e, 0x60, 0xd3, 0xce, 0x52, 0xc2, 0x8f, 0x88, 0x2b, - 0x9a, 0x73, 0x4e, 0xc8, 0x50, 0x2c, 0x8e, 0x29, 0xa8, 0x97, 0x56, 0xb0, - 0xa5, 0x36, 0x66, 0x95, 0x97, 0x16, 0x01, 0xbf, 0x19, 0x31, 0xb4, 0x41, - 0xdc, 0x41, 0xdc, 0xd8, 0x41, 0x00, 0x31, 0xa5, 0x39, 0xce, 0xf8, 0x61, - 0xaa, 0xa9, 0x2b, 0x16, 0x86, 0xf2, 0x02, 0xfd, 0x7f, 0xd8, 0xb2, 0x66, - 0xa6, 0x9d, 0x65, 0x40, 0xf0, 0xd9, 0xfe, 0x5e, 0xae, 0xf4, 0x46, 0x18, - 0x60, 0xbe, 0x42, 0xca, 0xea, 0x4d, 0x2f, 0xa7, 0xef, 0x44, 0xc4, 0xed, - 0x30, 0x1b, 0x22, 0xc2, 0xe3, 0xb9, 0x22, 0xa4, 0xda, 0xdd, 0x5b, 0x5c, - 0xc5, 0x68, 0xa3, 0xd2, 0xd1, 0x18, 0xeb, 0x0f, 0x49, 0x07, 0xc6, 0x81, - 0xf1, 0x01, 0xf1, 0xee, 0x3a, 0x71, 0xf2, 0x25, 0x74, 0xcd, 0x9a, 0xf8, - 0xa9, 0xcd, 0x56, 0xa9, 0xd6, 0xb9, 0xbf, 0x34, 0x09, 0x9f, 0x92, 0x7c, - 0xed, 0x45, 0xf0, 0x2b, 0x6d, 0x0e, 0x34, 0xa6, 0xbd, 0x7f, 0x37, 0x7d, - 0xf2, 0xbb, 0x57, 0xc4, 0xdb, 0x6e, 0xb5, 0xc6, 0x0c, 0xc7, 0x1e, 0x55, - 0x13, 0x7d, 0xfd, 0x31, 0x76, 0x99, 0xf3, 0x9c, 0xb6, 0x5c, 0x5e, 0x73, - 0x32, 0xbe, 0x91, 0x79, 0x54, 0xa6, 0xf4, 0x9d, 0xe3, 0x2a, 0x4d, 0xc1, - 0x1a, 0x84, 0x72, 0xcd, 0xa0, 0x6a, 0xc0, 0xd8, 0x0a, 0x89, 0x88, 0x86, - 0x4c, 0xfc, 0x1d, 0xca, 0x4e, 0x8f, 0xb8, 0x91, 0x9a, 0x6a, 0xb7, 0xd2, - 0xbf, 0x6a, 0x14, 0xbe, 0xff, 0xf1, 0x50, 0x80, 0x2e, 0xdf, 0xfc, 0x21, - 0x1a, 0xc9, 0x7a, 0x48, 0x5e, 0x00, 0x00, 0x9e, 0xb1, 0xc1, 0xe4, 0x6b, - 0x26, 0x60, 0x4a, 0xeb, 0xbe, 0x39, 0x1a, 0xc5, 0x49, 0xae, 0xc8, 0x75, - 0xcf, 0x9b, 0xe7, 0xf5, 0xee, 0xae, 0xdd, 0xfd, 0x6b, 0xc5, 0xf0, 0x3a, - 0x6f, 0x32, 0xc9, 0x32, 0x60, 0x69, 0xad, 0x8d, 0x13, 0xe0, 0x94, 0x25, - 0xd3, 0x24, 0x91, 0xec, 0x81, 0x0f, 0x8f, 0x40, 0x6e, 0xda, 0x76, 0xb9, - 0xfd, 0xc3, 0x4a, 0xef, 0x1d, 0xf5, 0x2b, 0xdf, 0x3f, 0xce, 0xbc, 0x0e, - 0x25, 0x12, 0xee, 0xc5, 0xb8, 0x15, 0x73, 0xc2, 0xac, 0xe7, 0xf6, 0x4a, - 0xef, 0xe3, 0x1e, 0xef, 0xdd, 0x0b, 0xe0, 0x6e, 0xe6, 0x3a, 0xb1, 0x2d, - 0x09, 0xe8, 0x8a, 0xfa, 0xab, 0x37, 0x89, 0x3a, 0xc0, 0xb8, 0x2b, 0x05, - 0xf1, 0x24, 0x2c, 0x06, 0x64, 0x2a, 0x39, 0xcb, 0x2a, 0x39, 0x8b, 0xf9, - 0x60, 0xc6, 0xae, 0xf1, 0x93, 0x79, 0xd0, 0x8b, 0xc0, 0xc9, 0x8c, 0x76, - 0xc4, 0x92, 0x75, 0xa0, 0x86, 0x16, 0xbd, 0x56, 0x8a, 0x04, 0x9a, 0xe5, - 0x91, 0x51, 0xc1, 0xed, 0x91, 0xce, 0x83, 0x66, 0x3b, 0xb6, 0xa4, 0x14, - 0x18, 0x4c, 0x93, 0x01, 0x19, 0x9d, 0x34, 0xd3, 0x80, 0x00, 0x12, 0x89, - 0xb4, 0xe2, 0x15, 0xae, 0x02, 0x27, 0x51, 0xc5, 0x55, 0x89, 0xb9, 0xf5, - 0x21, 0xc4, 0x09, 0xdf, 0x6f, 0xcb, 0x8d, 0x42, 0x24, 0xa6, 0x7a, 0xd1, - 0xc0, 0x20, 0x80, 0xdc, 0x00, 0xc0, 0x0d, 0x27, 0x7c, 0x3d, 0x40, 0x0c, - 0x37, 0xf8, 0xac, 0xdd, 0x73, 0x6e, 0xe0, 0xde, 0x1f, 0x7b, 0xef, 0x2c, - 0x55, 0xa3, 0x0d, 0x3f, 0xea, 0xe4, 0xc5, 0x77, 0x74, 0x88, 0x96, 0x43, - 0x53, 0xc5, 0x05, 0x1d, 0x3b, 0x8e, 0x43, 0x9f, 0x2d, 0x53, 0xd0, 0xe8, - 0x96, 0x6c, 0x7b, 0x24, 0x8b, 0x8a, 0x27, 0xb6, 0x8f, 0x67, 0xf7, 0xc0, - 0xb2, 0x4b, 0x27, 0xac, 0x90, 0x87, 0x7f, 0xa8, 0xc3, 0xe5, 0x09, 0x5d, - 0x76, 0x02, 0x50, 0x10, 0xae, 0xb5, 0x55, 0xb2, 0x2a, 0xe9, 0x30, 0x38, - 0x7d, 0x44, 0x2d, 0xfb, 0x82, 0x44, 0x43, 0x39, 0x74, 0x49, 0x80, 0x51, - 0xc0, 0x13, 0x1d, 0x1d, 0x0c, 0x69, 0xf8, 0x3a, 0x64, 0x8a, 0xe3, 0xbe, - 0x27, 0x72, 0x4b, 0xcd, 0x4b, 0x8e, 0x01, 0x7d, 0x47, 0xad, 0xc2, 0xbd, - 0x8b, 0xec, 0xfb, 0x48, 0xbe, 0x2e, 0xdd, 0x39, 0xfa, 0x7d, 0xeb, 0x77, - 0x67, 0x9b, 0x1e, 0xf3, 0x1d, 0x2d, 0xd9, 0x95, 0xa2, 0x5f, 0x1f, 0xa8, - 0x9f, 0x73, 0xec, 0xa6, 0xf0, 0x01, 0x13, 0x35, 0x05, 0x6b, 0x61, 0x8d, - 0x6a, 0xb5, 0x63, 0x29, 0x38, 0xf6, 0x51, 0x58, 0x17, 0x4c, 0xa7, 0x7e, - 0x78, 0x6d, 0x49, 0xe4, 0x82, 0xa7, 0xff, 0xf1, 0x50, 0x80, 0x3d, 0x1f, - 0xfc, 0x20, 0xa2, 0x2a, 0xd0, 0x55, 0xd3, 0x30, 0x49, 0x27, 0x3c, 0x64, - 0xdb, 0xce, 0x2b, 0x5a, 0xce, 0xf3, 0x57, 0x5c, 0x75, 0xbf, 0x33, 0xbf, - 0x8b, 0xef, 0xa5, 0x67, 0x15, 0x4b, 0x1b, 0x9d, 0xc2, 0x1a, 0x22, 0x7a, - 0xe4, 0x26, 0x41, 0x9c, 0x6b, 0x95, 0x7c, 0xf9, 0xeb, 0x9d, 0x4c, 0xf1, - 0x1f, 0x1e, 0x8f, 0xa3, 0x94, 0xab, 0x78, 0xb3, 0x1c, 0xf9, 0x5e, 0x4e, - 0x32, 0x66, 0x75, 0x37, 0xb5, 0xf2, 0x8b, 0x69, 0x98, 0xe0, 0x57, 0x68, - 0x69, 0xcf, 0xb0, 0xdf, 0x08, 0x2c, 0xdc, 0x48, 0x33, 0x6a, 0xb2, 0xa4, - 0x65, 0x54, 0xb4, 0x45, 0x2f, 0x03, 0x9c, 0xbb, 0x52, 0x00, 0x8e, 0x8e, - 0x52, 0x37, 0x78, 0xf6, 0x76, 0x6c, 0x2a, 0x61, 0x5d, 0x5b, 0x5f, 0x7e, - 0x6d, 0x78, 0x5f, 0x94, 0x50, 0x9d, 0x98, 0x05, 0x76, 0x6d, 0x62, 0xf7, - 0x63, 0x8c, 0x62, 0x10, 0xcd, 0x07, 0x7f, 0x05, 0x92, 0xaa, 0x06, 0x5b, - 0xf7, 0x5c, 0x65, 0x5c, 0xd2, 0x24, 0xe0, 0x4d, 0x79, 0xc3, 0x01, 0x98, - 0x2d, 0x50, 0x92, 0x25, 0xda, 0xad, 0x88, 0xa6, 0xa2, 0x9e, 0x56, 0x37, - 0x55, 0xcc, 0x2e, 0x14, 0x02, 0x92, 0xd9, 0xe3, 0xaa, 0x9b, 0x27, 0x78, - 0xb2, 0x75, 0x75, 0x7a, 0xd7, 0x24, 0xb6, 0x47, 0x4b, 0xf6, 0x09, 0x4f, - 0x69, 0x50, 0x31, 0x6a, 0x41, 0xab, 0x58, 0x3c, 0xa3, 0x93, 0x9b, 0x94, - 0xf9, 0xd7, 0x38, 0x49, 0x71, 0x51, 0x47, 0x5a, 0x70, 0xbc, 0xd9, 0x9a, - 0x6a, 0x8a, 0xe9, 0xe5, 0xb5, 0x68, 0xba, 0x49, 0x8a, 0xd0, 0xa5, 0x96, - 0xb2, 0x61, 0xcb, 0x20, 0x8a, 0x5e, 0xf5, 0x8b, 0xed, 0x19, 0xca, 0x2b, - 0xef, 0xc4, 0xd0, 0x54, 0x89, 0xd5, 0x6c, 0x96, 0x5d, 0x13, 0xc3, 0x5a, - 0xd5, 0x07, 0x92, 0xab, 0x58, 0x46, 0x18, 0x28, 0x1f, 0xaf, 0xe8, 0x00, - 0x00, 0x03, 0xf1, 0xaf, 0x13, 0xcd, 0x7e, 0x9f, 0xf5, 0xaa, 0x72, 0x31, - 0x1f, 0xa0, 0xe8, 0xdb, 0xef, 0x95, 0x77, 0x9c, 0xd3, 0x5b, 0xd5, 0x85, - 0x02, 0xa3, 0xd1, 0xc6, 0x69, 0xd1, 0x96, 0xa7, 0xa9, 0x09, 0x36, 0xaa, - 0xb8, 0x4a, 0xf2, 0x38, 0xfe, 0xfd, 0xea, 0x81, 0x29, 0x4d, 0xb9, 0x48, - 0x56, 0xf8, 0x99, 0x09, 0x47, 0x5f, 0x8f, 0x40, 0x6b, 0xc3, 0x6e, 0xcb, - 0xfa, 0x0e, 0x75, 0x7d, 0x14, 0xab, 0xa3, 0xd5, 0xc9, 0xcd, 0x31, 0xf4, - 0x05, 0xea, 0xf4, 0xf7, 0xec, 0xfc, 0x94, 0xb9, 0x70, 0x33, 0x32, 0xf9, - 0xdd, 0xc8, 0x7a, 0x72, 0xc5, 0xcd, 0xf6, 0xe9, 0x6f, 0x5e, 0x04, 0x5e, - 0x3a, 0x4c, 0xc0, 0xa8, 0xae, 0x01, 0x79, 0x12, 0x7a, 0xe0, 0x32, 0xb8, - 0x90, 0x36, 0x2c, 0x54, 0x01, 0xc4, 0x09, 0x00, 0x77, 0x39, 0xac, 0x6b, - 0xa4, 0x92, 0x5d, 0xd5, 0x2c, 0xba, 0xf6, 0xec, 0xc4, 0x4f, 0x3e, 0x8c, - 0xfc, 0xdd, 0xa6, 0xae, 0x31, 0x6c, 0x48, 0x9d, 0x33, 0xc2, 0x84, 0x8b, - 0x90, 0x18, 0x5d, 0xf5, 0x05, 0x82, 0x23, 0x3a, 0x26, 0x26, 0x88, 0xb0, - 0x49, 0x46, 0x8a, 0x8a, 0x2d, 0x42, 0x15, 0xf6, 0xc1, 0x88, 0x11, 0xe7, - 0x4e, 0x7f, 0xc3, 0x1a, 0x8f, 0x23, 0x22, 0x72, 0x87, 0x3a, 0xe3, 0x06, - 0xa1, 0xe5, 0xe9, 0x5f, 0x93, 0x87, 0x78, 0x24, 0xed, 0xce, 0x28, 0x63, - 0x17, 0x22, 0x40, 0x70, 0xe1, 0x3c, 0xeb, 0x53, 0xf8, 0xd8, 0x02, 0x03, - 0x93, 0x13, 0x01, 0x06, 0x80, 0x38, 0x02, 0xc7, 0x15, 0xf7, 0x8d, 0x3d, - 0x1b, 0x16, 0xc6, 0x40, 0xdc, 0x64, 0xa6, 0x14, 0x91, 0x66, 0x03, 0x2f, - 0x9f, 0x80, 0xff, 0xf1, 0x50, 0x80, 0x34, 0xff, 0xfc, 0x20, 0x9d, 0x4c, - 0xd9, 0x04, 0xe8, 0x68, 0x29, 0xe3, 0x79, 0x46, 0xb9, 0x45, 0x08, 0x02, - 0x5e, 0xfc, 0xfb, 0x79, 0xe7, 0x5b, 0xd7, 0xf7, 0x7d, 0x3e, 0x7d, 0xbc, - 0xd4, 0xef, 0x8f, 0xbf, 0xfd, 0x97, 0x7c, 0xf5, 0xec, 0xfc, 0xd7, 0xdf, - 0x91, 0x76, 0x7f, 0x48, 0xae, 0x9b, 0xa1, 0xe9, 0xde, 0x0a, 0xc8, 0x23, - 0xd6, 0x5e, 0x71, 0x05, 0x69, 0xfa, 0x02, 0xac, 0x3f, 0xe1, 0x9f, 0x50, - 0x59, 0x84, 0x90, 0x5e, 0xe4, 0xc2, 0x1e, 0xdc, 0xd7, 0x45, 0xf1, 0xcc, - 0xe4, 0xd5, 0xba, 0x33, 0x7d, 0x70, 0x17, 0xf0, 0x75, 0xfa, 0x5f, 0x9b, - 0x00, 0x50, 0xc8, 0x4d, 0x4b, 0x12, 0xd4, 0xda, 0xef, 0xef, 0xe6, 0xc2, - 0x29, 0xe8, 0x6e, 0x35, 0xdb, 0x6a, 0x24, 0x16, 0x5f, 0xcb, 0xb8, 0x5d, - 0xcb, 0xda, 0x0c, 0x63, 0xd5, 0x25, 0x9d, 0x80, 0x49, 0x66, 0xcf, 0xc9, - 0xe5, 0x92, 0x7e, 0xd0, 0x73, 0xd0, 0x7a, 0xed, 0x35, 0x81, 0x84, 0xb8, - 0x65, 0x4b, 0x68, 0x87, 0x99, 0xcc, 0x91, 0x48, 0xdc, 0x22, 0x06, 0x76, - 0x7c, 0x5d, 0x7d, 0x81, 0xfb, 0x13, 0x6a, 0xce, 0xd3, 0xa7, 0x2d, 0x70, - 0x24, 0x22, 0x24, 0xe5, 0xbd, 0x78, 0xa5, 0x5b, 0xf1, 0x66, 0xb7, 0x9d, - 0xab, 0x29, 0xd9, 0xb8, 0x17, 0xae, 0xd7, 0x5e, 0x8f, 0xe8, 0x7c, 0xc7, - 0xf1, 0x2c, 0x2a, 0x76, 0xb1, 0xaa, 0xdb, 0x68, 0x8a, 0x4d, 0x81, 0xbc, - 0xf8, 0x05, 0x4e, 0x55, 0xa1, 0x29, 0xe6, 0x20, 0x27, 0xfb, 0xff, 0x6f, - 0xaf, 0xd0, 0x7c, 0x7b, 0xfd, 0x7c, 0xba, 0xe5, 0x5f, 0x8f, 0x7f, 0x6e, - 0xf4, 0xa6, 0xba, 0xef, 0x2f, 0x35, 0x97, 0xf8, 0xfc, 0xfc, 0x41, 0x07, - 0xa5, 0x8a, 0x45, 0xe9, 0xa5, 0xdc, 0x96, 0x31, 0x9f, 0x5f, 0xbc, 0xb7, - 0xd3, 0x8b, 0xef, 0x3f, 0xc7, 0x95, 0xaa, 0x3a, 0x53, 0x82, 0x7e, 0x30, - 0xfc, 0xb0, 0x9f, 0xf2, 0x16, 0xa9, 0xd7, 0xa4, 0x3d, 0x72, 0x28, 0x3a, - 0xf5, 0x8c, 0x11, 0x5b, 0xc7, 0xc9, 0x9f, 0x82, 0x21, 0xa4, 0x19, 0xc9, - 0x62, 0x18, 0x11, 0xb0, 0x1c, 0x25, 0x2e, 0x1a, 0x81, 0x87, 0x8d, 0xb7, - 0x00, 0x00, 0x31, 0x55, 0x38, 0x7e, 0x6e, 0x22, 0x31, 0xe1, 0xf3, 0x88, - 0x20, 0xee, 0x1e, 0xdb, 0xd8, 0x00, 0x61, 0xde, 0xfb, 0x98, 0x75, 0x6b, - 0xf3, 0xf1, 0xb8, 0x04, 0x94, 0x43, 0xff, 0x5f, 0x80, 0x00, 0x01, 0x5a, - 0xfc, 0x3d, 0xb8, 0x00, 0x00, 0x18, 0x70, 0xf6, 0xe0, 0x00, 0x77, 0x0f, - 0x0f, 0x77, 0x00, 0x00, 0x50, 0x78, 0x79, 0xf8, 0x00, 0x18, 0xb4, 0x3d, - 0xbb, 0x60, 0x00, 0x61, 0xe3, 0x0f, 0x10, 0xc5, 0x86, 0x2d, 0x0f, 0x6d, - 0xc0, 0x0c, 0x57, 0x5c, 0x65, 0x80, 0x28, 0xae, 0x3f, 0x87, 0xae, 0x5b, - 0x7c, 0xb3, 0xb0, 0x14, 0x4f, 0x32, 0xbd, 0xde, 0x58, 0xcd, 0x54, 0x15, - 0x46, 0x32, 0x84, 0x00, 0x03, 0x55, 0x96, 0xe9, 0xe9, 0x96, 0x51, 0x05, - 0x94, 0x83, 0xb3, 0x3c, 0xd6, 0x2e, 0x9f, 0xcb, 0xd5, 0x16, 0x66, 0x28, - 0x96, 0x55, 0x76, 0xc4, 0xe0, 0xff, 0xf1, 0x50, 0x80, 0x35, 0xff, 0xfc, - 0x21, 0x4c, 0x36, 0x82, 0x00, 0x40, 0x44, 0x06, 0x00, 0x51, 0x41, 0x1d, - 0xa4, 0x4e, 0xb9, 0xd5, 0xac, 0x8b, 0x6b, 0x05, 0x95, 0x96, 0x42, 0x90, - 0x93, 0x36, 0x01, 0xde, 0xcb, 0x87, 0x1f, 0x15, 0xbf, 0x3e, 0xd1, 0x4f, - 0xfa, 0x23, 0x57, 0x72, 0xee, 0xa7, 0xec, 0xbc, 0x79, 0xcb, 0xa9, 0x74, - 0x8e, 0x7e, 0x45, 0x1f, 0x10, 0x7a, 0xcb, 0x7f, 0x5c, 0xa8, 0x99, 0x14, - 0x7f, 0x9d, 0x39, 0xe7, 0x50, 0xd2, 0xbe, 0x53, 0x5f, 0x01, 0x82, 0x41, - 0x67, 0x01, 0xd5, 0x14, 0x4d, 0xc3, 0xf5, 0x7d, 0x6f, 0x6b, 0xb5, 0xe7, - 0x51, 0x84, 0x08, 0xf1, 0xe5, 0x1e, 0x89, 0xae, 0x08, 0x01, 0x56, 0xb3, - 0x52, 0x7d, 0x5f, 0x5f, 0x5c, 0x2c, 0x42, 0x18, 0xf0, 0xf3, 0xdd, 0xc4, - 0x5f, 0x08, 0x13, 0x9c, 0xe5, 0xd8, 0xe7, 0x69, 0x82, 0xe5, 0x00, 0xa2, - 0xa8, 0xbe, 0x25, 0xa1, 0xe2, 0xab, 0xd1, 0xd8, 0x9d, 0x90, 0xdd, 0x06, - 0xc9, 0x56, 0x98, 0xab, 0x01, 0x51, 0x9d, 0x4a, 0x2e, 0xff, 0xff, 0xcf, - 0x5a, 0x3c, 0x58, 0x7d, 0xd4, 0x58, 0xb9, 0x1a, 0x0a, 0xb9, 0x41, 0xc9, - 0x7a, 0x36, 0x70, 0x95, 0x01, 0x8e, 0xde, 0x11, 0x68, 0x00, 0x5c, 0xff, - 0xc8, 0xf2, 0xed, 0x6b, 0x9a, 0x51, 0x1b, 0x00, 0xb2, 0x9c, 0xce, 0x5f, - 0xe3, 0x3e, 0xbd, 0x6f, 0x4d, 0x17, 0x9d, 0x12, 0x68, 0x11, 0x70, 0x63, - 0xec, 0xd4, 0x61, 0x6d, 0xd6, 0x0a, 0x6b, 0x58, 0x0b, 0x5a, 0x68, 0xf4, - 0x89, 0x65, 0x72, 0xe3, 0x6d, 0x16, 0xb5, 0xc2, 0x44, 0xba, 0x7f, 0xd7, - 0x7e, 0x13, 0xd0, 0x72, 0x3b, 0x41, 0x00, 0xe3, 0x0e, 0x69, 0x23, 0x62, - 0xf6, 0xea, 0x24, 0x35, 0x07, 0x27, 0x86, 0x97, 0x2a, 0x0a, 0xd7, 0x69, - 0x87, 0x7b, 0x43, 0xb5, 0xc7, 0xc1, 0x1e, 0x9d, 0xbd, 0x94, 0x41, 0xa6, - 0x2a, 0x4f, 0x6e, 0x6e, 0x52, 0x27, 0x10, 0x7d, 0x31, 0xb2, 0xcd, 0x27, - 0xd0, 0x7b, 0x3a, 0xb5, 0xbe, 0x32, 0x7a, 0xd7, 0xeb, 0xc5, 0x6f, 0x26, - 0x6f, 0x55, 0xcf, 0x33, 0xeb, 0x3c, 0x4f, 0xae, 0x19, 0xf7, 0xfe, 0x8a, - 0xab, 0xad, 0x6b, 0x9b, 0x94, 0x3c, 0x8d, 0x42, 0xbc, 0x01, 0x23, 0x19, - 0xff, 0xd4, 0xa4, 0x81, 0x8f, 0xb2, 0x53, 0x5c, 0xe5, 0x24, 0xcf, 0x7b, - 0xe9, 0xe8, 0xb8, 0xf0, 0xf5, 0xf7, 0x9b, 0x4a, 0x40, 0x07, 0x3f, 0xd3, - 0xed, 0xbd, 0x8b, 0xd3, 0x88, 0x2b, 0x20, 0x7d, 0x0d, 0xdd, 0x72, 0x47, - 0xad, 0x52, 0xbf, 0x05, 0x42, 0xfb, 0xac, 0xde, 0x4c, 0xb5, 0x6d, 0x3c, - 0x38, 0xb4, 0xaf, 0xa4, 0xe8, 0x12, 0x95, 0xbf, 0x3d, 0x7d, 0x71, 0xcf, - 0xc3, 0x77, 0xb5, 0xe0, 0x02, 0xd0, 0x81, 0x61, 0x0e, 0xfe, 0xd2, 0xf2, - 0x28, 0x5e, 0x1a, 0xf9, 0xec, 0x97, 0xed, 0x54, 0xfd, 0x60, 0x56, 0x98, - 0x5a, 0x24, 0x25, 0xb9, 0xa3, 0x55, 0x81, 0x3c, 0x37, 0xd6, 0xcf, 0xb9, - 0x1d, 0x12, 0x04, 0xe0, 0xc8, 0x8f, 0x72, 0x00, 0x06, 0x50, 0x77, 0x74, - 0x45, 0x31, 0x00, 0x11, 0x18, 0x9d, 0xc2, 0xd2, 0x68, 0xf1, 0xde, 0xf8, - 0xea, 0xf2, 0xbc, 0xcf, 0xff, 0xf1, 0x50, 0x80, 0x28, 0x1f, 0xfc, 0x21, - 0x7a, 0xcf, 0x64, 0xf8, 0x70, 0x04, 0x00, 0xa4, 0xb4, 0xb1, 0xd1, 0x82, - 0x64, 0x09, 0x11, 0x88, 0x00, 0x00, 0xd7, 0x1e, 0x15, 0xad, 0xb8, 0x9e, - 0x7d, 0x37, 0xac, 0xea, 0xfb, 0xbd, 0x4f, 0xac, 0xe2, 0xc7, 0x3f, 0xe4, - 0x18, 0x99, 0x5e, 0x14, 0xb5, 0x1a, 0x85, 0x17, 0xf3, 0x1b, 0x89, 0xa4, - 0x7e, 0x38, 0x55, 0x9b, 0xd7, 0xe6, 0xef, 0xc3, 0xba, 0xdb, 0x06, 0xe0, - 0xa1, 0x75, 0x55, 0xfe, 0x68, 0x2e, 0xdf, 0xbe, 0x95, 0x91, 0x5d, 0xc9, - 0xf0, 0x77, 0xed, 0xaf, 0x5a, 0x9f, 0x04, 0x95, 0x5e, 0x71, 0xda, 0xce, - 0x68, 0x29, 0xf0, 0xd5, 0x51, 0x51, 0x67, 0x1c, 0xea, 0x82, 0xe0, 0xe1, - 0x72, 0xc4, 0xf5, 0xec, 0x9b, 0x3b, 0x0e, 0xe1, 0x40, 0x26, 0x9b, 0x81, - 0x45, 0x7d, 0xd2, 0xb9, 0x6d, 0x48, 0x51, 0x2b, 0xfd, 0x25, 0x7c, 0xa4, - 0xc2, 0xff, 0x90, 0xf4, 0x29, 0x18, 0x79, 0x04, 0xbc, 0x42, 0xd6, 0xd9, - 0x93, 0xcd, 0x97, 0x24, 0xe7, 0x13, 0x1e, 0x4a, 0x21, 0x8f, 0xf5, 0xf2, - 0x64, 0x4e, 0x4c, 0x31, 0x7b, 0x94, 0x1b, 0x44, 0x5c, 0x93, 0x40, 0xcb, - 0x0d, 0xfb, 0xbf, 0xd7, 0xf2, 0x33, 0x46, 0x6c, 0x3a, 0x4f, 0x7c, 0xb3, - 0x76, 0x96, 0x72, 0xc7, 0xb7, 0x15, 0xb2, 0x5e, 0x86, 0x66, 0x36, 0xeb, - 0x69, 0xb1, 0xa5, 0xb1, 0xd1, 0xa2, 0x63, 0x43, 0x86, 0xf9, 0xd1, 0x12, - 0x5e, 0xab, 0x9c, 0xe0, 0x6e, 0x82, 0x92, 0x5c, 0x40, 0x3e, 0x8b, 0xc8, - 0x8a, 0x5c, 0x70, 0xf5, 0x57, 0xf2, 0xb7, 0xa6, 0x3f, 0x8e, 0xab, 0x78, - 0xd5, 0x2f, 0x96, 0x0b, 0x9e, 0x09, 0x7d, 0x24, 0x21, 0x54, 0x48, 0xf0, - 0x90, 0x7d, 0xe3, 0x9e, 0x1c, 0xb8, 0xe5, 0xc0, 0x72, 0xba, 0xe2, 0xb5, - 0xce, 0xf5, 0xe6, 0xa5, 0x73, 0x53, 0x42, 0xf2, 0x54, 0xa0, 0xe0, 0x6b, - 0xfe, 0x5d, 0x11, 0xc8, 0xcf, 0x2c, 0x00, 0x3a, 0x4f, 0x03, 0xe1, 0xc2, - 0xb4, 0xad, 0x7c, 0xe3, 0x31, 0xb3, 0xae, 0xd5, 0x92, 0xa1, 0x8b, 0xce, - 0x67, 0xd3, 0xf9, 0xe2, 0xa2, 0x4e, 0xee, 0xbf, 0xf1, 0x77, 0xff, 0xa4, - 0x1c, 0xca, 0xce, 0x18, 0xb4, 0xad, 0x0c, 0xb5, 0x10, 0x91, 0xb6, 0xa2, - 0xe0, 0xa3, 0x1d, 0xdc, 0x88, 0x59, 0xd1, 0xef, 0x11, 0xdd, 0x9b, 0x80, - 0xff, 0xf1, 0x50, 0x80, 0x27, 0x1f, 0xfc, 0x21, 0x1a, 0xcd, 0xf6, 0xd5, - 0x9a, 0x04, 0x00, 0xa8, 0xb1, 0x52, 0x16, 0x09, 0x15, 0x43, 0x5b, 0xa9, - 0x7c, 0x77, 0x5c, 0x7c, 0x75, 0xfd, 0xbe, 0xab, 0xbf, 0xaf, 0x7f, 0x39, - 0x7a, 0xaf, 0x8e, 0xf4, 0xaa, 0xaa, 0xd5, 0xc6, 0x38, 0x0a, 0x3f, 0x3e, - 0xf7, 0x95, 0x06, 0x54, 0xa4, 0xc2, 0xe1, 0xc9, 0x20, 0x53, 0x90, 0x2c, - 0x7d, 0x90, 0x47, 0x09, 0x9c, 0xd4, 0x6c, 0x76, 0xc3, 0x5a, 0xfc, 0xd0, - 0x89, 0xea, 0x6f, 0xab, 0xab, 0x18, 0x3a, 0x17, 0x48, 0xba, 0x46, 0x62, - 0xe9, 0x7c, 0x8b, 0xa9, 0xdc, 0xeb, 0xac, 0x13, 0x09, 0x4b, 0x85, 0x64, - 0x21, 0x64, 0x45, 0x93, 0x34, 0xad, 0x2c, 0x64, 0x52, 0x6b, 0x37, 0x44, - 0xb3, 0x59, 0x86, 0xee, 0xc9, 0x74, 0xb7, 0x53, 0xe4, 0x1d, 0x98, 0xb8, - 0xf6, 0x6f, 0x1b, 0x58, 0xae, 0x24, 0x54, 0xe7, 0x63, 0x9e, 0x64, 0xd9, - 0x7d, 0x85, 0x5c, 0x6b, 0x4e, 0x5c, 0x0d, 0xf5, 0x51, 0x1a, 0xc5, 0xa6, - 0x4c, 0x7a, 0xf7, 0x95, 0xdf, 0xbf, 0x7d, 0xea, 0xda, 0x2e, 0xe3, 0x40, - 0x9b, 0x72, 0xf0, 0xaa, 0x30, 0x26, 0xf3, 0x93, 0x61, 0x58, 0x9b, 0xf0, - 0x5a, 0xa2, 0x84, 0xc4, 0x4f, 0x2f, 0x86, 0x7a, 0x95, 0xb4, 0x4e, 0xef, - 0xff, 0x66, 0x3e, 0x4c, 0x53, 0x3d, 0xb8, 0x18, 0x68, 0x56, 0xa5, 0x90, - 0x46, 0xaf, 0x27, 0x9f, 0xe2, 0x82, 0x79, 0x5b, 0xa6, 0xd6, 0x50, 0xdd, - 0x40, 0xc4, 0x34, 0x9b, 0xfd, 0xe5, 0xa1, 0xfa, 0xdb, 0xf5, 0x81, 0xba, - 0x2b, 0x4a, 0x65, 0x5f, 0x71, 0xfd, 0x87, 0x61, 0xd1, 0x36, 0x9d, 0x98, - 0xaf, 0xe7, 0x0f, 0xdb, 0x51, 0x62, 0x97, 0x92, 0x7e, 0x20, 0xd6, 0xcb, - 0xc9, 0x4f, 0xd7, 0x9f, 0x79, 0xed, 0xde, 0xb9, 0xf8, 0xce, 0x7e, 0xbc, - 0xb3, 0x5b, 0xc4, 0xa4, 0xb7, 0x37, 0x80, 0x0c, 0xb6, 0x76, 0x3f, 0x2e, - 0x95, 0xcc, 0x4b, 0x12, 0xfb, 0xbf, 0xfe, 0x76, 0x9a, 0xe1, 0x7a, 0xb0, - 0xbb, 0x1d, 0x86, 0x9c, 0x4c, 0x8c, 0xa5, 0x5a, 0xe6, 0x3b, 0x92, 0x11, - 0xdb, 0x5a, 0x50, 0x11, 0xe3, 0xda, 0xcb, 0x8b, 0x03, 0x18, 0x5d, 0x12, - 0x19, 0x4d, 0x6b, 0xbc, 0xeb, 0x17, 0x69, 0x28, 0x62, 0xed, 0x6f, 0x80, - 0xff, 0xf1, 0x50, 0x80, 0x28, 0x9f, 0xfc, 0x21, 0x1a, 0xcd, 0xff, 0xad, - 0xfb, 0x11, 0x00, 0xa8, 0xb1, 0xb1, 0xd3, 0xe2, 0x54, 0x5e, 0xa7, 0x8b, - 0xd5, 0xf2, 0xbf, 0x39, 0xcf, 0xdf, 0xbd, 0xf5, 0x5e, 0xd5, 0x4e, 0x29, - 0x3c, 0x3c, 0xef, 0x2f, 0x59, 0x4b, 0xd6, 0xc5, 0x7f, 0xff, 0xdf, 0xcf, - 0x4b, 0x62, 0xaa, 0xda, 0xe5, 0xf4, 0x46, 0x9b, 0xb8, 0x56, 0x3b, 0xbf, - 0x9e, 0x03, 0x34, 0xd8, 0xf5, 0x96, 0x75, 0xd1, 0x68, 0xbd, 0x29, 0x52, - 0x14, 0x70, 0xf6, 0x70, 0xbf, 0x98, 0x01, 0xc5, 0xb2, 0x34, 0xf0, 0xf4, - 0xae, 0xef, 0xc4, 0x83, 0x73, 0x1c, 0xfc, 0x77, 0xbd, 0x57, 0x15, 0x62, - 0xa0, 0xa2, 0xb5, 0x53, 0x78, 0xe4, 0xd3, 0xcc, 0xc0, 0x6d, 0xcc, 0x70, - 0xad, 0xdc, 0x07, 0xa0, 0x64, 0x92, 0x7c, 0x34, 0xa9, 0x89, 0xae, 0x68, - 0x82, 0x9c, 0x77, 0x55, 0x61, 0x59, 0x0d, 0xc5, 0x93, 0x31, 0xf3, 0xa3, - 0xb4, 0x8d, 0x1a, 0xf3, 0x71, 0xca, 0x0c, 0x1e, 0xe0, 0xde, 0x96, 0xf3, - 0x28, 0x31, 0xa1, 0x2e, 0x22, 0x91, 0xd8, 0x73, 0xa8, 0x36, 0x27, 0x5b, - 0x73, 0xad, 0x15, 0x76, 0xd1, 0x92, 0x5b, 0x31, 0x8c, 0x88, 0x8a, 0x9a, - 0xaa, 0x5b, 0x34, 0xd2, 0x10, 0x5e, 0x95, 0x98, 0x72, 0xc1, 0x98, 0xa6, - 0x90, 0xfa, 0x10, 0x5d, 0x8b, 0xbd, 0x34, 0x35, 0x29, 0xba, 0x95, 0x6e, - 0x50, 0x28, 0x15, 0x9b, 0xb4, 0x3c, 0x16, 0xf8, 0xdb, 0x71, 0xb5, 0x90, - 0x5e, 0x47, 0x73, 0x16, 0xec, 0x0a, 0x24, 0x9f, 0x56, 0x77, 0x5a, 0x17, - 0x56, 0x59, 0xf1, 0x7f, 0xbd, 0x60, 0x68, 0x27, 0xc6, 0x0c, 0x0a, 0x9f, - 0x46, 0x43, 0x2b, 0xd4, 0x58, 0x93, 0x3f, 0x00, 0xbd, 0x4f, 0x17, 0xab, - 0xe7, 0x7d, 0x6a, 0x73, 0xab, 0xdf, 0x55, 0xed, 0x5f, 0x6f, 0xae, 0x35, - 0x1b, 0xe6, 0xe4, 0xb2, 0xa8, 0xdb, 0x4c, 0x33, 0x40, 0x00, 0x18, 0x6c, - 0x0e, 0x00, 0x00, 0x7d, 0xba, 0x86, 0x0d, 0x8b, 0x78, 0x6c, 0xd5, 0x4b, - 0xcd, 0xf4, 0x73, 0xcb, 0x6f, 0xf5, 0x51, 0xcf, 0x0d, 0xf1, 0xca, 0xb7, - 0xa8, 0x42, 0x6a, 0x91, 0x4c, 0xf3, 0xc1, 0x4f, 0x7c, 0xd5, 0x35, 0x3d, - 0x58, 0x8a, 0xa0, 0x63, 0x9f, 0x52, 0x4c, 0x15, 0x8d, 0x1a, 0xce, 0xeb, - 0xb5, 0xa9, 0x9c, 0xe3, 0x69, 0x33, 0xa7, 0x7f, 0xbe, 0xdd, 0x6d, 0xe0, - 0xff, 0xf1, 0x50, 0x80, 0x2d, 0x3f, 0xfc, 0x21, 0x1a, 0xcf, 0xfe, 0x48, - 0xe8, 0x00, 0x00, 0xa6, 0xb1, 0xb4, 0xd0, 0xac, 0x24, 0x70, 0x7c, 0x5e, - 0xf9, 0xe3, 0xe3, 0x9a, 0xef, 0x8f, 0x3c, 0xd7, 0x5d, 0x7c, 0xfd, 0xf7, - 0x8a, 0xdf, 0x1c, 0x33, 0x37, 0x13, 0xae, 0x7a, 0xf1, 0x2f, 0x7a, 0x1a, - 0x5f, 0x15, 0x24, 0x70, 0x50, 0xc8, 0xa4, 0xd0, 0xfe, 0xbb, 0xfb, 0xfe, - 0xbc, 0x70, 0x9f, 0xba, 0x8c, 0x35, 0x97, 0x5a, 0x02, 0xdb, 0x4c, 0x22, - 0x48, 0x43, 0xe4, 0xf5, 0x46, 0xe1, 0x46, 0xd1, 0x0e, 0xe5, 0xa0, 0x8a, - 0xbe, 0x40, 0x54, 0x14, 0xf2, 0xc0, 0x4d, 0xc2, 0x59, 0x31, 0x04, 0x94, - 0x01, 0x1a, 0x55, 0x9d, 0x31, 0x47, 0x0a, 0x0b, 0x4a, 0x06, 0x5e, 0xf7, - 0x10, 0x0d, 0x48, 0x30, 0x3e, 0x9e, 0x68, 0xd3, 0xf4, 0xcd, 0xc1, 0x50, - 0x4b, 0x40, 0x67, 0xd0, 0x91, 0x9b, 0xb3, 0xf3, 0x83, 0xe8, 0x53, 0x5a, - 0x8b, 0x52, 0x4b, 0x2c, 0x22, 0xbd, 0xb4, 0x04, 0x92, 0xce, 0xc1, 0x4a, - 0x29, 0x48, 0xb2, 0xe5, 0x98, 0x35, 0x34, 0x3a, 0x43, 0xab, 0xdc, 0xfa, - 0xd2, 0x44, 0xcb, 0xe4, 0x1a, 0x7d, 0xf2, 0x54, 0xe5, 0x4c, 0x57, 0x9a, - 0x7b, 0x11, 0xf1, 0x46, 0x06, 0x10, 0xa4, 0x93, 0x9e, 0x91, 0x0e, 0x89, - 0x8f, 0x72, 0xb8, 0xba, 0xb4, 0xef, 0xf3, 0xfd, 0x19, 0x41, 0x36, 0x43, - 0x02, 0x2f, 0xea, 0x5d, 0x09, 0xc8, 0xee, 0x73, 0x58, 0x7b, 0x41, 0xcb, - 0x97, 0xb2, 0xa5, 0xd8, 0xfa, 0xa8, 0xab, 0x8b, 0xff, 0xec, 0x23, 0xd1, - 0xe6, 0x6d, 0xe2, 0xfb, 0x5f, 0xa9, 0xfb, 0xe2, 0xb5, 0x7a, 0xf8, 0x2c, - 0x4f, 0xc5, 0x26, 0x1b, 0xa4, 0xa1, 0xf7, 0xd9, 0x2b, 0xc3, 0x57, 0xcd, - 0x3e, 0xef, 0x01, 0xeb, 0x77, 0xaa, 0xf7, 0x78, 0xcf, 0xe3, 0xf6, 0x82, - 0x3e, 0xf7, 0x16, 0x77, 0xa6, 0x6a, 0xa1, 0x18, 0x5f, 0x00, 0xf8, 0xbd, - 0xf3, 0xc7, 0xc7, 0xbc, 0xdf, 0x55, 0x7d, 0xf1, 0xc6, 0xfe, 0xfb, 0x9b, - 0xef, 0xef, 0x36, 0x64, 0x2e, 0xf3, 0x5b, 0x8a, 0xb0, 0x59, 0xec, 0xdf, - 0xc5, 0x99, 0xf2, 0x56, 0x63, 0x20, 0xf1, 0xd2, 0x17, 0x98, 0x59, 0xd7, - 0x07, 0x37, 0x27, 0x00, 0xe5, 0x14, 0x50, 0x53, 0x07, 0x85, 0x14, 0xa8, - 0x39, 0x83, 0x7b, 0x4a, 0xc3, 0x00, 0x0b, 0x54, 0x6f, 0x14, 0x30, 0x69, - 0x0d, 0x86, 0x70, 0x44, 0xe2, 0x05, 0x4d, 0xf9, 0xb4, 0x8f, 0x72, 0x9d, - 0x2b, 0x7e, 0xf5, 0xbe, 0x9d, 0xfb, 0x9d, 0xd3, 0xda, 0xab, 0x2a, 0x06, - 0x93, 0x94, 0xc1, 0x39, 0x21, 0xc1, 0x3b, 0x0c, 0xb8, 0xc3, 0x2a, 0x1c, - 0xbc, 0xff, 0xf1, 0x50, 0x80, 0x2e, 0xbf, 0xfc, 0x21, 0x1a, 0xcf, 0xd6, - 0xf7, 0xd5, 0xa1, 0xed, 0xa5, 0xb4, 0x32, 0x12, 0xac, 0x60, 0xf3, 0x75, - 0xce, 0xba, 0x9b, 0xe7, 0x89, 0x32, 0xf5, 0xce, 0xb7, 0xed, 0xeb, 0xad, - 0xeb, 0x8d, 0xd6, 0xf1, 0x6a, 0xeb, 0x2a, 0x48, 0x69, 0x86, 0x51, 0x20, - 0x83, 0x0d, 0x80, 0x00, 0x0d, 0xae, 0xda, 0xc0, 0x93, 0x41, 0xa2, 0x93, - 0x73, 0x42, 0x1e, 0xf1, 0xd9, 0xc7, 0xa7, 0x0a, 0xd2, 0x53, 0x5f, 0x7b, - 0x67, 0xd7, 0x93, 0x5c, 0x52, 0x27, 0x7f, 0xe4, 0x16, 0x3f, 0x46, 0x57, - 0xdf, 0x53, 0xe6, 0x63, 0x23, 0xd0, 0x71, 0xf5, 0x29, 0x4f, 0xaf, 0x6f, - 0x0f, 0xc7, 0x96, 0xa0, 0xb0, 0x86, 0x90, 0xd0, 0x1a, 0x00, 0x03, 0xff, - 0x06, 0x64, 0xcc, 0x79, 0x5a, 0xda, 0x7d, 0x38, 0x66, 0x1d, 0x40, 0x81, - 0x49, 0xc5, 0x95, 0xa8, 0x2e, 0x5e, 0x6d, 0x3d, 0xc9, 0xf1, 0x7c, 0x2b, - 0xf3, 0x7e, 0x51, 0xa8, 0xb4, 0x9d, 0x38, 0x4b, 0x52, 0x01, 0xf6, 0x20, - 0x67, 0xf6, 0xfe, 0x1a, 0x48, 0x02, 0x7c, 0x83, 0x21, 0xc4, 0x68, 0xbe, - 0x4e, 0x4c, 0xbf, 0x2f, 0x3e, 0x9c, 0xfc, 0x75, 0xcf, 0x9f, 0x4e, 0xf6, - 0x41, 0x25, 0x92, 0x08, 0xa8, 0xa5, 0x35, 0xb9, 0xe3, 0x68, 0x79, 0xf6, - 0xd6, 0x9b, 0x37, 0xbc, 0xf6, 0x79, 0x1d, 0x41, 0x8c, 0x48, 0xe7, 0x4e, - 0x72, 0xf9, 0x87, 0x9f, 0xb7, 0x76, 0x4a, 0x91, 0x8d, 0x43, 0x9e, 0x79, - 0xe7, 0x9c, 0xa2, 0xb9, 0xe7, 0x9d, 0x43, 0x11, 0x17, 0x44, 0x14, 0xe8, - 0x82, 0x26, 0x81, 0x2d, 0x92, 0xe7, 0x99, 0xca, 0x27, 0x2e, 0x79, 0x8e, - 0x6a, 0x22, 0x8a, 0x2a, 0xab, 0x67, 0x60, 0xc5, 0xd7, 0x34, 0xf3, 0xc5, - 0xd1, 0x5c, 0x40, 0x74, 0xcf, 0x6e, 0x6a, 0xae, 0x26, 0x26, 0xaa, 0x57, - 0x88, 0x8a, 0xd2, 0x45, 0x6d, 0x49, 0x11, 0x11, 0x4b, 0x08, 0x69, 0x20, - 0xb9, 0xe1, 0xe6, 0x3d, 0x79, 0xeb, 0x9b, 0xe7, 0x89, 0x92, 0xf5, 0xce, - 0xae, 0xf5, 0xdf, 0xc7, 0xbf, 0x19, 0x7c, 0xd5, 0xd4, 0xae, 0xbe, 0x64, - 0x92, 0xc2, 0xc4, 0x7b, 0x62, 0x8e, 0x20, 0x3e, 0xe8, 0x38, 0x8e, 0x28, - 0x79, 0x5d, 0xa0, 0xc2, 0x6f, 0x97, 0x40, 0x08, 0x24, 0x41, 0x98, 0xe8, - 0x73, 0x39, 0xc1, 0x5b, 0x92, 0xb8, 0x0f, 0xa8, 0x63, 0x28, 0x20, 0x05, - 0x26, 0x25, 0x24, 0xa0, 0x14, 0xc4, 0x58, 0x8c, 0x40, 0x4c, 0x69, 0x3b, - 0x48, 0x2c, 0x50, 0x51, 0x45, 0x68, 0xaf, 0x52, 0x56, 0x2c, 0x63, 0x3d, - 0x2e, 0x60, 0x17, 0x99, 0xa0, 0x20, 0xc9, 0xd5, 0xbe, 0xbb, 0x6d, 0x14, - 0x3b, 0xdc, 0x3c, 0x3c, 0x3d, 0xb1, 0xe3, 0x6c, 0xab, 0xc5, 0x6a, 0x69, - 0x00, 0x2e, 0xff, 0xf1, 0x50, 0x80, 0x2a, 0x7f, 0xfc, 0x21, 0x1a, 0xcb, - 0xee, 0xb7, 0xf1, 0x82, 0x82, 0xa9, 0xb1, 0x41, 0x92, 0xe4, 0x64, 0x09, - 0x11, 0x82, 0x89, 0xc7, 0x13, 0xbf, 0x32, 0x6e, 0xad, 0x74, 0xbc, 0xf8, - 0xef, 0x8f, 0x78, 0xeb, 0x35, 0x59, 0xb4, 0xc5, 0xeb, 0x9d, 0x27, 0x91, - 0x8d, 0x81, 0xe0, 0x42, 0xf3, 0x3e, 0x9c, 0x6b, 0x83, 0x77, 0xd6, 0x08, - 0xef, 0xa4, 0x3b, 0x5e, 0x33, 0xc7, 0x41, 0xba, 0xfd, 0x33, 0xda, 0xed, - 0x9f, 0xe6, 0xfb, 0x22, 0x6e, 0x17, 0x0e, 0xf4, 0x05, 0x90, 0xeb, 0x2c, - 0xd7, 0x1e, 0x16, 0x30, 0xf2, 0x95, 0x61, 0xa9, 0x3a, 0xef, 0xc2, 0x63, - 0x90, 0x63, 0x7f, 0xf7, 0x5e, 0xf7, 0x8b, 0xbb, 0x07, 0x67, 0x5f, 0x41, - 0x91, 0xc1, 0x4d, 0x29, 0xc6, 0x65, 0x8b, 0x4c, 0x9b, 0xaa, 0x16, 0x56, - 0x8c, 0xe2, 0xe4, 0x25, 0x83, 0x97, 0x05, 0x46, 0x8d, 0x92, 0x5b, 0x04, - 0xd4, 0x27, 0xe6, 0x98, 0x91, 0xf4, 0x51, 0x99, 0x9d, 0xc0, 0x41, 0x70, - 0x77, 0xd4, 0x27, 0x33, 0x6b, 0x6b, 0x9d, 0x63, 0xb7, 0xd5, 0x50, 0x84, - 0xae, 0x32, 0xab, 0x15, 0xaf, 0x18, 0x71, 0x13, 0xce, 0x04, 0xe1, 0x9a, - 0x47, 0x8a, 0xab, 0x2b, 0x98, 0xc8, 0xac, 0x4a, 0xb4, 0x7e, 0x34, 0x33, - 0x28, 0xa0, 0x24, 0xdc, 0xa8, 0xa3, 0xf2, 0xa0, 0x37, 0x89, 0x97, 0xa7, - 0x8b, 0xc7, 0xf1, 0x82, 0x93, 0x30, 0xf9, 0x9b, 0xc7, 0xa6, 0x1e, 0xba, - 0x2a, 0x1f, 0xc9, 0xbd, 0x18, 0xa6, 0xfe, 0x0b, 0x12, 0x30, 0xb8, 0xdf, - 0x78, 0xb9, 0x50, 0x87, 0x51, 0x01, 0x6c, 0x20, 0xfd, 0x4c, 0x1f, 0x20, - 0x9c, 0x71, 0xaf, 0x5e, 0x67, 0x37, 0xcf, 0x4a, 0xb5, 0xe7, 0xc7, 0x7c, - 0x54, 0xe6, 0xf1, 0x1b, 0xab, 0xaa, 0xb7, 0xc5, 0x2c, 0x35, 0x37, 0x78, - 0xe8, 0x07, 0x45, 0x80, 0x13, 0x0f, 0x02, 0x69, 0x84, 0x08, 0x93, 0x97, - 0x00, 0xa1, 0xda, 0x85, 0xe5, 0x94, 0x02, 0x23, 0x9d, 0x67, 0xa2, 0xb1, - 0x07, 0xb0, 0x9c, 0x01, 0x91, 0xae, 0x37, 0x17, 0x02, 0x00, 0x10, 0x22, - 0x91, 0x6b, 0x4a, 0xf8, 0x02, 0x92, 0x5a, 0xe7, 0x06, 0xc8, 0x94, 0x18, - 0x5f, 0x55, 0xab, 0x15, 0xb3, 0xb4, 0x0a, 0x47, 0x55, 0x26, 0x28, 0x88, - 0x89, 0x39, 0xf7, 0xe6, 0xa8, 0xd5, 0xb2, 0x4a, 0x3a, 0x3b, 0x17, 0x35, - 0x52, 0x1d, 0x2a, 0x69, 0xf2, 0xc4, 0xc3, 0x69, 0x94, 0xf2, 0x9b, 0x55, - 0x68, 0x61, 0xbd, 0x9f, 0x80, 0xff, 0xf1, 0x50, 0x80, 0x2e, 0xdf, 0xfc, - 0x21, 0x1a, 0xcd, 0xf4, 0xd4, 0x95, 0x00, 0x45, 0xa6, 0xb1, 0x43, 0x18, - 0xe9, 0x67, 0x3a, 0xe3, 0xe2, 0x4c, 0xd5, 0x75, 0xde, 0xfd, 0xb7, 0xaa, - 0xab, 0x99, 0xbf, 0x3e, 0x3a, 0xef, 0x8e, 0x52, 0x54, 0xc0, 0x9a, 0xca, - 0xd0, 0xd3, 0xdd, 0xdf, 0x05, 0xa8, 0xbd, 0x3c, 0xbe, 0xa4, 0x8d, 0xa4, - 0xca, 0xde, 0x00, 0x74, 0x92, 0xb2, 0xaa, 0x4a, 0xc3, 0x04, 0x34, 0x70, - 0xb0, 0x09, 0x15, 0x61, 0x92, 0x6c, 0x8e, 0x34, 0x66, 0xb1, 0x09, 0x4e, - 0xf1, 0xea, 0x05, 0xdb, 0x41, 0xf5, 0x83, 0xb6, 0x66, 0x9c, 0x1b, 0x42, - 0xf4, 0x29, 0x9b, 0xcc, 0x04, 0x01, 0x66, 0x91, 0x8a, 0x37, 0xd9, 0xef, - 0x7d, 0xfa, 0x0a, 0x5e, 0x3f, 0x1e, 0xa4, 0xbf, 0x67, 0x71, 0xfd, 0xd3, - 0xd3, 0xb5, 0xa7, 0xf7, 0x57, 0x4c, 0x80, 0xbf, 0x31, 0x0f, 0xff, 0x29, - 0x80, 0xad, 0xb1, 0x38, 0x6a, 0x42, 0x5f, 0x96, 0x53, 0x15, 0x53, 0x88, - 0x5a, 0x7d, 0x5f, 0xd1, 0x2a, 0x15, 0xb4, 0xa4, 0x09, 0x11, 0xd7, 0xf6, - 0x24, 0x37, 0x5f, 0x64, 0xe8, 0x68, 0x5b, 0xb5, 0xfa, 0xfb, 0xf8, 0x68, - 0x13, 0xf8, 0x82, 0x94, 0x38, 0x1f, 0xd2, 0xda, 0x93, 0x94, 0x76, 0x56, - 0x94, 0x04, 0x19, 0xa0, 0x30, 0x22, 0xbb, 0x1a, 0x23, 0xf9, 0xb2, 0x35, - 0x8f, 0x20, 0xbf, 0x6f, 0x12, 0x04, 0xa5, 0x30, 0xe9, 0x2d, 0xeb, 0x5a, - 0xcc, 0x11, 0x27, 0x99, 0xe7, 0xe5, 0x28, 0x0c, 0x50, 0x83, 0xbe, 0x1a, - 0x78, 0xbd, 0x7b, 0x7a, 0xa3, 0x85, 0x01, 0xab, 0x8e, 0x0f, 0x1f, 0xa7, - 0xfa, 0x75, 0xf2, 0xbf, 0xb4, 0x37, 0x05, 0xcf, 0xb6, 0xc1, 0xe8, 0x5b, - 0x83, 0x8d, 0x2e, 0xf4, 0x03, 0xd5, 0xe1, 0xa3, 0xf8, 0xcf, 0xa9, 0xf9, - 0x90, 0x01, 0xc7, 0x1f, 0x4d, 0x62, 0x84, 0xb4, 0x7d, 0xec, 0x9c, 0x7c, - 0x49, 0xe3, 0xd9, 0x93, 0x7a, 0x8b, 0xdc, 0xe2, 0xab, 0x5b, 0xbc, 0x9c, - 0x65, 0x4a, 0xa5, 0xe6, 0x4d, 0x4c, 0x58, 0x34, 0x5f, 0xe1, 0x6a, 0x20, - 0x28, 0xe4, 0x16, 0x30, 0x90, 0x77, 0x60, 0xe7, 0xa0, 0x3b, 0xb4, 0x96, - 0xfc, 0xbe, 0xf1, 0x47, 0x60, 0x80, 0x57, 0xe9, 0xf5, 0x26, 0xe1, 0x25, - 0xd8, 0x5b, 0xed, 0x6d, 0x01, 0xb7, 0x8b, 0x85, 0xd0, 0x4d, 0xeb, 0xf4, - 0x84, 0x59, 0x28, 0xbf, 0x07, 0x93, 0x95, 0xf0, 0x54, 0xd0, 0x25, 0x68, - 0x58, 0x37, 0x12, 0x0c, 0xf7, 0x5a, 0xcb, 0x23, 0xca, 0x42, 0xa9, 0x52, - 0x33, 0x1c, 0xd6, 0xb4, 0x05, 0xc3, 0x3d, 0x05, 0x1a, 0x88, 0x0d, 0x9a, - 0x90, 0x91, 0xc5, 0x2c, 0x94, 0x09, 0x58, 0xd1, 0x09, 0x0f, 0x6c, 0xd7, - 0x08, 0xa3, 0xcc, 0xef, 0x3d, 0x11, 0x77, 0xff, 0xf1, 0x50, 0x80, 0x30, - 0x1f, 0xfc, 0x21, 0x1a, 0xcf, 0xfd, 0x47, 0x7c, 0x10, 0xc0, 0xa5, 0xb1, - 0x43, 0x98, 0xc8, 0x45, 0x0a, 0x39, 0x84, 0x47, 0xb3, 0x5b, 0xbf, 0xbf, - 0xbd, 0x67, 0x4f, 0xaf, 0xcd, 0x24, 0xa8, 0xbe, 0x78, 0xe4, 0xd6, 0x5d, - 0xf3, 0x52, 0xae, 0xab, 0xce, 0xc6, 0x9f, 0xf9, 0x84, 0x80, 0x7f, 0xc5, - 0x74, 0xa5, 0xf0, 0xa4, 0x76, 0xb4, 0x22, 0x63, 0xd6, 0xb0, 0x3e, 0x8a, - 0x66, 0x9f, 0xd1, 0x45, 0x6f, 0xcd, 0x84, 0xe9, 0xd1, 0x53, 0x58, 0x58, - 0x93, 0x80, 0xc2, 0x60, 0x69, 0x44, 0xcd, 0x85, 0x2d, 0xb9, 0x67, 0xc6, - 0xb8, 0x08, 0x0a, 0x7c, 0x81, 0x65, 0x93, 0x88, 0x9d, 0xf3, 0x27, 0xc0, - 0x2f, 0xcc, 0xc2, 0x41, 0x85, 0x70, 0xdf, 0x99, 0x09, 0x37, 0x51, 0xcc, - 0xfe, 0xf0, 0x7a, 0x83, 0xf7, 0x23, 0x24, 0x95, 0x90, 0x6a, 0x1d, 0x62, - 0xfe, 0xd2, 0x5f, 0x9f, 0x2d, 0xbd, 0x08, 0xca, 0xf5, 0xd0, 0xfd, 0xd8, - 0xad, 0xa3, 0xf6, 0x3c, 0x8a, 0x72, 0x95, 0x80, 0x39, 0x21, 0x6e, 0x22, - 0x01, 0xc9, 0x07, 0x7a, 0x4d, 0xe1, 0x3c, 0xc0, 0x5c, 0xbf, 0xd8, 0x7f, - 0xca, 0xc4, 0x33, 0xd9, 0xd3, 0xaa, 0x9f, 0x5c, 0x0e, 0xcf, 0x2a, 0x67, - 0x93, 0xa6, 0xaf, 0xc5, 0xa0, 0xfa, 0x69, 0x0e, 0x87, 0xd9, 0xf5, 0xfd, - 0x12, 0xae, 0xe7, 0xd1, 0xb5, 0x67, 0xcb, 0x6f, 0x8f, 0x38, 0x5b, 0x13, - 0x8f, 0xc0, 0x15, 0x31, 0x40, 0x97, 0x3d, 0xad, 0x46, 0xde, 0xbe, 0xc4, - 0xe2, 0xe3, 0x23, 0x53, 0x79, 0x1b, 0x53, 0x55, 0x30, 0xd7, 0x2b, 0x9c, - 0x17, 0xa5, 0xad, 0xfb, 0xd9, 0x34, 0x38, 0xb2, 0x76, 0xdd, 0xe6, 0xe5, - 0xec, 0xb7, 0xc3, 0xc9, 0xbe, 0x2d, 0xc5, 0x3a, 0xdc, 0x37, 0xb0, 0x86, - 0x6a, 0xf1, 0x72, 0xbb, 0xb4, 0xca, 0x50, 0xb0, 0x5a, 0x4a, 0x1e, 0x70, - 0x01, 0x94, 0x0a, 0xb0, 0x58, 0x36, 0xb6, 0x0c, 0x89, 0x29, 0x61, 0x2d, - 0x3f, 0x79, 0x1e, 0xcd, 0x6e, 0xe4, 0xe6, 0x45, 0xa8, 0xba, 0x8b, 0xcf, - 0xad, 0xf8, 0x9a, 0xdc, 0xba, 0xcb, 0xde, 0xa9, 0x60, 0xb5, 0x47, 0x4c, - 0x49, 0x29, 0x08, 0x02, 0x63, 0x1c, 0x79, 0x17, 0x1c, 0xc7, 0xfb, 0x85, - 0x97, 0xeb, 0xf0, 0xf1, 0x8d, 0x4c, 0x47, 0x47, 0x74, 0xd4, 0x4d, 0x09, - 0x2f, 0x6b, 0xad, 0xc3, 0x2b, 0xf6, 0x76, 0xf1, 0x52, 0xd9, 0x14, 0x74, - 0x1b, 0x0c, 0xe1, 0xc3, 0x06, 0x50, 0xcb, 0x0a, 0x2c, 0x92, 0xa0, 0x33, - 0x01, 0x38, 0x92, 0xe6, 0xb4, 0xc6, 0x7b, 0xe1, 0xc6, 0x86, 0x5b, 0x86, - 0x10, 0x58, 0xef, 0x23, 0x5c, 0x1c, 0xdd, 0x49, 0x09, 0xc0, 0x69, 0x31, - 0x02, 0x19, 0xf4, 0xf3, 0x17, 0xa3, 0xf8, 0x25, 0x51, 0x2d, 0x43, 0x71, - 0x2b, 0xdf, 0x17, 0xc0, 0xad, 0xe5, 0xc0, 0xff, 0xf1, 0x50, 0x80, 0x30, - 0x5f, 0xfc, 0x21, 0x1a, 0xcf, 0xf0, 0x77, 0xfa, 0x00, 0x00, 0xa6, 0xb4, - 0x42, 0x50, 0xec, 0x14, 0x33, 0x05, 0x16, 0x6a, 0x71, 0x27, 0x8f, 0x6f, - 0xd7, 0xf3, 0xaf, 0x7f, 0x29, 0xdc, 0xa8, 0x4a, 0xfa, 0xf7, 0x54, 0xa9, - 0xc7, 0x3d, 0x72, 0xad, 0x6e, 0x55, 0x84, 0xe7, 0x87, 0x64, 0xe1, 0xf7, - 0x54, 0x12, 0x71, 0x19, 0x16, 0xd8, 0x11, 0x65, 0x20, 0x8f, 0x5b, 0x93, - 0xa8, 0x67, 0xab, 0xad, 0x76, 0x15, 0x50, 0x01, 0x0b, 0xa8, 0x84, 0xf2, - 0x34, 0x0b, 0x51, 0x88, 0x30, 0x53, 0xd1, 0x1d, 0x31, 0x92, 0xf0, 0x26, - 0x36, 0x74, 0xb6, 0x41, 0xe5, 0x14, 0xc8, 0x85, 0x1b, 0x06, 0x46, 0xd9, - 0x7c, 0x86, 0xd3, 0xce, 0x2b, 0x96, 0x62, 0x61, 0x23, 0x9e, 0xa7, 0x63, - 0x4f, 0xe1, 0x53, 0x95, 0x96, 0xd2, 0xcf, 0x39, 0x58, 0xa5, 0xa4, 0x61, - 0x7f, 0xc6, 0x93, 0xc0, 0xba, 0x92, 0xd0, 0x28, 0x6f, 0xda, 0xa7, 0x0f, - 0xd3, 0xe1, 0x21, 0x59, 0xe6, 0x55, 0xeb, 0xa2, 0xe1, 0x90, 0xa2, 0xe0, - 0xe7, 0xeb, 0xf9, 0x27, 0x76, 0x36, 0xc2, 0x2d, 0x40, 0x0a, 0x2d, 0x4c, - 0x32, 0x13, 0xde, 0x35, 0x8a, 0x0f, 0x43, 0x1b, 0x18, 0xf5, 0x59, 0x3e, - 0xce, 0x3f, 0x99, 0x7c, 0x33, 0x7b, 0xc8, 0x78, 0x55, 0x65, 0x0d, 0x10, - 0x3f, 0x5d, 0x17, 0x85, 0x3e, 0x42, 0x82, 0x47, 0x49, 0x10, 0x34, 0x19, - 0xb5, 0xdf, 0x1e, 0xaa, 0x9e, 0xdd, 0xd4, 0x94, 0xc5, 0x8c, 0xd1, 0xf9, - 0x2b, 0xae, 0xb9, 0x00, 0xbe, 0xb1, 0x60, 0xc0, 0xd3, 0xd3, 0x1e, 0xbf, - 0x0b, 0x09, 0x89, 0xd1, 0x48, 0xed, 0x1c, 0x7e, 0x5b, 0x8f, 0x40, 0x9e, - 0x82, 0x7b, 0x46, 0x06, 0x5b, 0x9d, 0x6f, 0x26, 0x2d, 0x1d, 0x0b, 0xdb, - 0x4c, 0xc7, 0xb0, 0xc2, 0x91, 0x9c, 0x24, 0x0f, 0x99, 0x03, 0xe3, 0x35, - 0x38, 0x93, 0xdf, 0x8f, 0xc7, 0xe5, 0x3c, 0xb3, 0x25, 0x42, 0x55, 0xd6, - 0xb8, 0x57, 0x8d, 0x14, 0x4a, 0x2f, 0x01, 0x58, 0xc5, 0xa1, 0x05, 0x64, - 0x80, 0x63, 0x48, 0x05, 0x60, 0x09, 0x86, 0x6c, 0x93, 0x06, 0x59, 0x82, - 0x6c, 0x06, 0x5c, 0xbf, 0xef, 0x1d, 0xb4, 0x84, 0xa1, 0x0e, 0xf0, 0x4e, - 0xb0, 0x89, 0x3d, 0x9c, 0x73, 0x18, 0x0b, 0x79, 0xf9, 0x64, 0xb7, 0x9b, - 0xf7, 0xaf, 0xd7, 0x67, 0xc5, 0xd9, 0xeb, 0xdb, 0xa7, 0x81, 0x3d, 0x2c, - 0x75, 0xff, 0x1f, 0x47, 0x52, 0x38, 0xf4, 0x26, 0x70, 0x0e, 0xa4, 0xe5, - 0xaf, 0xa0, 0x95, 0xea, 0x91, 0xa7, 0xeb, 0x7e, 0x6c, 0xf6, 0xee, 0x15, - 0xb7, 0xce, 0xf2, 0x92, 0x59, 0x03, 0x93, 0xb6, 0x2f, 0xef, 0x0e, 0x1c, - 0xc8, 0x08, 0xc3, 0x5c, 0x60, 0x82, 0xd7, 0xd6, 0xc3, 0x24, 0x32, 0x44, - 0x1d, 0x8e, 0xeb, 0x66, 0xfe, 0xed, 0x67, 0xe8, 0xf0, 0xff, 0xf1, 0x50, - 0x80, 0x3e, 0x1f, 0xfc, 0x20, 0xaa, 0x1a, 0xd4, 0x88, 0x4b, 0x15, 0x22, - 0xa1, 0x41, 0x9e, 0xd7, 0xab, 0x7f, 0x1f, 0xe3, 0xe3, 0xde, 0xfe, 0x7f, - 0x1c, 0xf9, 0xfc, 0xf4, 0xa8, 0x2d, 0xae, 0x6d, 0x8a, 0x97, 0x9c, 0x4e, - 0x75, 0xcf, 0xb7, 0x60, 0x5c, 0xcb, 0xdb, 0x95, 0x5c, 0x0a, 0x49, 0x51, - 0xd1, 0x12, 0x03, 0x73, 0xa0, 0x93, 0x74, 0x7d, 0x98, 0xd7, 0xae, 0x33, - 0x8a, 0x9a, 0x64, 0x9f, 0x44, 0xca, 0xf2, 0x2f, 0xa6, 0x8c, 0x41, 0x88, - 0x64, 0x12, 0x09, 0xd4, 0xa1, 0x33, 0xbc, 0xf3, 0xab, 0x03, 0x47, 0x5e, - 0xb2, 0x47, 0xad, 0x2b, 0xe9, 0x3c, 0xd3, 0x94, 0x41, 0xec, 0x0b, 0x0a, - 0x43, 0x5d, 0x63, 0x01, 0xb8, 0x23, 0xa1, 0xb5, 0xff, 0x2f, 0x21, 0x4f, - 0x0f, 0x29, 0x82, 0xbc, 0xcd, 0x2d, 0xcf, 0x80, 0xe6, 0xff, 0x84, 0x1f, - 0x46, 0xf7, 0xf6, 0x7f, 0xcc, 0x30, 0x2f, 0x8b, 0xf8, 0x9b, 0x0d, 0x3c, - 0x67, 0xbc, 0xed, 0xb3, 0xdb, 0x94, 0xd2, 0x43, 0x38, 0x13, 0x24, 0xc3, - 0x75, 0xa8, 0xf4, 0x9b, 0x11, 0xa1, 0x15, 0x59, 0x5a, 0x3e, 0x90, 0x36, - 0xf6, 0x37, 0x84, 0xea, 0x59, 0x10, 0xef, 0x85, 0x0a, 0xfd, 0xf8, 0xf8, - 0x35, 0xfc, 0x15, 0x60, 0x25, 0xdb, 0xb7, 0x15, 0xbd, 0xf3, 0xcf, 0x6a, - 0x70, 0x8f, 0x4c, 0xb9, 0xa3, 0x71, 0x90, 0xec, 0x38, 0xfd, 0x6c, 0x7d, - 0xfa, 0x31, 0x76, 0x0f, 0x00, 0x7d, 0x7a, 0xbc, 0xb7, 0x97, 0x74, 0x6e, - 0x90, 0x6f, 0x0c, 0x8f, 0xec, 0x61, 0x5a, 0x8e, 0x95, 0xa0, 0xb4, 0xa8, - 0xe7, 0xc7, 0x99, 0xe5, 0xcb, 0x98, 0x2e, 0x61, 0x18, 0x88, 0x6e, 0x7e, - 0xa3, 0x6f, 0x8b, 0xf2, 0x13, 0x69, 0x6a, 0x07, 0x7c, 0x8f, 0xfb, 0xba, - 0x6b, 0xaa, 0xd4, 0x1e, 0x3f, 0xd0, 0x57, 0xa4, 0xa8, 0x7b, 0x78, 0x13, - 0xd2, 0xbd, 0x59, 0x56, 0xa4, 0xc2, 0x18, 0x69, 0x73, 0xad, 0x70, 0x9f, - 0xb7, 0xe3, 0x8e, 0xef, 0xdf, 0xe9, 0x7e, 0xba, 0x85, 0x75, 0xcc, 0xea, - 0xad, 0xcf, 0x4d, 0xe5, 0x71, 0xdc, 0xea, 0x6e, 0xf0, 0x38, 0xa9, 0x67, - 0x55, 0x7a, 0xa1, 0x4e, 0xa0, 0xd6, 0x18, 0x60, 0x05, 0x93, 0xc0, 0x54, - 0x30, 0x7c, 0x7e, 0x7f, 0xdb, 0x9e, 0x37, 0x58, 0x80, 0x67, 0x6c, 0x2e, - 0x7f, 0xca, 0xac, 0xc4, 0x61, 0x4a, 0x09, 0x24, 0x38, 0x07, 0x62, 0x84, - 0x1e, 0xa5, 0x32, 0xee, 0xba, 0x08, 0x2f, 0x50, 0xa4, 0x44, 0x9b, 0xb8, - 0x00, 0xa3, 0x82, 0xe4, 0x32, 0x3b, 0x5f, 0x0c, 0xee, 0x03, 0xfe, 0x38, - 0xc7, 0x3b, 0x2d, 0x40, 0x0e, 0x62, 0x15, 0x53, 0x80, 0x47, 0x37, 0x5b, - 0xbe, 0x99, 0x4d, 0xd2, 0xf1, 0x5e, 0x8e, 0x20, 0x15, 0xa0, 0x0a, 0x1a, - 0xa8, 0x03, 0xc0, 0xd0, 0x1d, 0xa5, 0xf4, 0x9f, 0xd4, 0x23, 0xdb, 0xef, - 0x69, 0x0b, 0xb5, 0x87, 0xc0, 0x8e, 0xb9, 0x83, 0xf7, 0x11, 0x9e, 0xda, - 0x62, 0x6b, 0xcf, 0x21, 0x1f, 0x61, 0xf1, 0x10, 0xfd, 0x23, 0x54, 0x65, - 0x06, 0x02, 0xe0, 0x8f, 0xed, 0xe7, 0x23, 0xed, 0x0c, 0xde, 0x13, 0x93, - 0x07, 0xd6, 0x8c, 0x3e, 0x02, 0x2d, 0xd4, 0xbf, 0x03, 0xbf, 0x47, 0xfa, - 0xc6, 0xc1, 0x67, 0x52, 0xfe, 0x54, 0x29, 0xc7, 0xbd, 0xad, 0x80, 0x03, - 0xd8, 0x60, 0xfc, 0x2d, 0x89, 0xbe, 0x91, 0x86, 0xc8, 0x0f, 0xa9, 0xa8, - 0x79, 0x37, 0x4e, 0x36, 0xcf, 0x68, 0x72, 0xba, 0xf6, 0x8c, 0x6b, 0x60, - 0x7c, 0xae, 0xe1, 0xc2, 0x8a, 0x00, 0x3e, 0x9a, 0xa7, 0x84, 0x19, 0xc9, - 0x66, 0xd4, 0x33, 0xf4, 0x11, 0xfc, 0xa3, 0x68, 0x2c, 0x50, 0xf4, 0xcf, - 0xc0, 0xff, 0xf1, 0x50, 0x80, 0x37, 0xbf, 0xfc, 0x20, 0xae, 0x1a, 0xd4, - 0x64, 0x93, 0x05, 0x20, 0xc2, 0x12, 0x7f, 0xcf, 0xfa, 0xef, 0x5f, 0x1d, - 0xe7, 0x3c, 0x71, 0xcd, 0x2f, 0x16, 0xaf, 0xd7, 0x9d, 0xe6, 0xb5, 0x9e, - 0xb8, 0xb4, 0x9a, 0xbd, 0xea, 0xbd, 0x82, 0x18, 0x0d, 0xf2, 0xe0, 0x64, - 0xd8, 0x47, 0x22, 0x5e, 0x48, 0xc0, 0x8f, 0xbf, 0x76, 0x7f, 0x8e, 0x83, - 0x69, 0x2d, 0xc5, 0xcb, 0x6a, 0x00, 0xf2, 0xc6, 0xf7, 0x00, 0xf4, 0x91, - 0x87, 0xd2, 0x22, 0xd9, 0x80, 0xfa, 0x44, 0x69, 0x57, 0x30, 0x58, 0x0d, - 0x24, 0x73, 0x1f, 0x47, 0x53, 0x32, 0x29, 0x23, 0x38, 0xee, 0xfb, 0x6d, - 0xc4, 0x40, 0xa0, 0x65, 0xb5, 0xbf, 0x0c, 0x00, 0x32, 0x35, 0x96, 0x46, - 0x93, 0x1c, 0x12, 0x23, 0x83, 0x32, 0x29, 0x2b, 0x98, 0xaf, 0xb4, 0xa4, - 0xae, 0xc2, 0xf8, 0xbd, 0x4b, 0x36, 0x95, 0x8f, 0x62, 0x96, 0x4b, 0x54, - 0x0c, 0x24, 0xe5, 0x75, 0x1d, 0xdf, 0x35, 0x16, 0x47, 0x96, 0x6a, 0x82, - 0xbe, 0x45, 0x91, 0x88, 0xe4, 0xcc, 0x2e, 0x31, 0x27, 0xc0, 0xe0, 0x47, - 0x51, 0x6e, 0x8e, 0x02, 0x44, 0x52, 0x50, 0x7c, 0x40, 0xe0, 0x6c, 0x8a, - 0x63, 0x68, 0x03, 0x8d, 0x86, 0x0f, 0x13, 0xbe, 0x23, 0x00, 0xd5, 0xc4, - 0x74, 0xe7, 0x7e, 0xc7, 0xed, 0xbe, 0xa7, 0x30, 0x79, 0x81, 0x7b, 0x8f, - 0xd0, 0x6c, 0xae, 0x02, 0x19, 0xf2, 0x7a, 0x87, 0xfa, 0x9f, 0xa6, 0x23, - 0xf0, 0x37, 0xcf, 0xcd, 0x80, 0x1e, 0x22, 0x86, 0xe5, 0x7b, 0xc5, 0xf2, - 0x64, 0xab, 0x72, 0x2c, 0x14, 0x8d, 0x25, 0xd9, 0xdb, 0x46, 0x7d, 0x4f, - 0xd3, 0x8b, 0x6a, 0xba, 0x7d, 0x32, 0x6d, 0x32, 0x61, 0xa1, 0xa7, 0x36, - 0x61, 0xb7, 0x24, 0x6c, 0xc3, 0x4c, 0x68, 0x00, 0x01, 0xff, 0x7f, 0xfb, - 0x00, 0x00, 0x00, 0x5d, 0xf4, 0xfd, 0xff, 0xe0, 0x3a, 0xfe, 0xad, 0x99, - 0x73, 0xda, 0x37, 0x65, 0x4d, 0x70, 0x98, 0xe4, 0x4f, 0xdc, 0x5d, 0x20, - 0x5c, 0x6d, 0x6f, 0x88, 0x07, 0x2a, 0x88, 0x22, 0x4e, 0x56, 0x56, 0xc4, - 0x67, 0x80, 0x33, 0x3e, 0xef, 0xe4, 0x7f, 0x40, 0x63, 0x59, 0xcf, 0xde, - 0x78, 0x38, 0xe7, 0xa7, 0xae, 0x37, 0xef, 0x0c, 0x51, 0x1c, 0xe4, 0xb5, - 0xa8, 0x63, 0xe3, 0x93, 0x52, 0x4c, 0xbc, 0x5c, 0xe2, 0xaa, 0xed, 0xc5, - 0x0d, 0x8f, 0xb8, 0x27, 0x7e, 0x5c, 0xcf, 0x73, 0x5f, 0xe7, 0x22, 0x27, - 0xa0, 0x5e, 0xc4, 0x51, 0x93, 0xab, 0x23, 0x94, 0xa0, 0x05, 0x26, 0x97, - 0x2a, 0x61, 0x41, 0x02, 0x88, 0x2b, 0xdf, 0x93, 0x1b, 0x13, 0x3a, 0x21, - 0x99, 0x8a, 0x45, 0x97, 0x58, 0x04, 0x86, 0x64, 0x34, 0xcc, 0x00, 0x6a, - 0xf6, 0xf6, 0x5d, 0x4e, 0xeb, 0xee, 0x9a, 0x43, 0x01, 0x83, 0x51, 0x04, - 0xdc, 0x30, 0x10, 0x7e, 0x4d, 0xa0, 0x13, 0x40, 0x73, 0x46, 0xb3, 0x78, - 0xd4, 0x0e, 0xed, 0x48, 0xd4, 0xbd, 0x46, 0x85, 0x6f, 0xb2, 0xac, 0xb5, - 0x0c, 0xa9, 0x10, 0xe3, 0xf2, 0x4a, 0x7e, 0xc8, 0x03, 0x3e, 0xaa, 0x97, - 0x40, 0x6e, 0x06, 0x16, 0x11, 0xc8, 0xaf, 0xf6, 0x3e, 0xf6, 0x46, 0xf3, - 0xa3, 0x0f, 0x50, 0xdf, 0xf8, 0x76, 0x6c, 0x12, 0xbf, 0x8a, 0xe4, 0xc0, - 0x42, 0xa7, 0xff, 0xf1, 0x50, 0x80, 0x38, 0x3f, 0xfc, 0x20, 0xa5, 0x1a, - 0xd6, 0x2a, 0x5b, 0x31, 0x08, 0x26, 0x21, 0xb1, 0x40, 0x1e, 0xde, 0x80, - 0x00, 0x01, 0xd7, 0x6e, 0xaf, 0x5d, 0x5f, 0x8d, 0x7d, 0x0c, 0xb0, 0x40, - 0x76, 0xb8, 0x17, 0x70, 0x61, 0x33, 0xa0, 0x47, 0x9c, 0x74, 0xf2, 0x28, - 0xa4, 0xa4, 0x28, 0xa4, 0x8e, 0x1d, 0xa2, 0x73, 0xb8, 0x87, 0x09, 0x41, - 0x7c, 0x62, 0x5f, 0x87, 0x18, 0x2b, 0x54, 0x97, 0x90, 0x59, 0x15, 0xcb, - 0xd8, 0x09, 0x32, 0x56, 0xc0, 0x71, 0x23, 0xa0, 0x56, 0x80, 0xdc, 0x66, - 0x73, 0x67, 0x19, 0x89, 0x58, 0xb0, 0x6a, 0xed, 0x6b, 0xdd, 0x02, 0x2a, - 0x63, 0x90, 0x4c, 0x64, 0xd3, 0x41, 0x0c, 0xd5, 0xd9, 0x08, 0xeb, 0xb2, - 0xb3, 0x63, 0x92, 0xae, 0xe5, 0xa3, 0x6c, 0xd3, 0x36, 0x06, 0xed, 0xad, - 0xdf, 0x7a, 0xa3, 0xda, 0x66, 0x33, 0x38, 0x45, 0x5b, 0x23, 0x4b, 0x64, - 0xed, 0xef, 0x57, 0x3a, 0xf3, 0xf3, 0x62, 0x91, 0x68, 0x7f, 0xc6, 0x34, - 0x72, 0x63, 0x4c, 0xc2, 0xe4, 0xc1, 0xf0, 0x84, 0xa1, 0x09, 0xce, 0x93, - 0xc4, 0x02, 0xc7, 0x15, 0x3e, 0x7c, 0x2d, 0x9e, 0xf8, 0xb3, 0xed, 0xed, - 0xb3, 0xa1, 0x31, 0x89, 0x95, 0x7f, 0xba, 0x34, 0x5b, 0x1e, 0x49, 0xc1, - 0xe1, 0x12, 0xa5, 0x89, 0xe1, 0x1a, 0x68, 0xb5, 0x86, 0x59, 0x46, 0xd2, - 0xd1, 0x17, 0x14, 0x3b, 0xda, 0xf6, 0x91, 0x39, 0xd0, 0x26, 0xaa, 0xc8, - 0x06, 0xc5, 0x21, 0x42, 0x31, 0x57, 0x13, 0x43, 0xa8, 0x5d, 0x14, 0xb5, - 0x5c, 0x0f, 0xc2, 0xfe, 0xcf, 0x15, 0x95, 0x6f, 0x97, 0x37, 0xa6, 0x7a, - 0xd6, 0x36, 0x4d, 0x11, 0x96, 0xa3, 0x14, 0x20, 0x58, 0x80, 0x00, 0x00, - 0x00, 0x22, 0xb5, 0xb9, 0xae, 0xbf, 0x7f, 0xce, 0xbe, 0x85, 0xc0, 0x40, - 0x32, 0xfb, 0xdb, 0x48, 0x6a, 0x16, 0xf6, 0x20, 0x1a, 0xed, 0x9e, 0x03, - 0xe5, 0xc9, 0x25, 0x2a, 0xde, 0x37, 0xc9, 0x6c, 0xff, 0xd2, 0x70, 0x16, - 0x2f, 0xfa, 0xdc, 0x82, 0x42, 0x27, 0x41, 0x1e, 0x78, 0x0b, 0xd2, 0x52, - 0x90, 0x1e, 0x83, 0x6b, 0xab, 0x7b, 0x8e, 0xe7, 0x73, 0x3a, 0x6e, 0xdf, - 0xf0, 0xae, 0x8b, 0xd6, 0x25, 0x42, 0x43, 0xdf, 0xa9, 0x4c, 0x82, 0xf7, - 0x32, 0x9c, 0x16, 0x82, 0x53, 0x13, 0xc0, 0xd1, 0x19, 0xe6, 0x01, 0x15, - 0xa2, 0x9b, 0x8a, 0xad, 0x10, 0x80, 0x09, 0x23, 0xac, 0x08, 0x28, 0xbd, - 0xed, 0x31, 0xa0, 0x5f, 0xba, 0xa0, 0x76, 0xe1, 0x3c, 0x62, 0xe7, 0x1b, - 0xa6, 0x6e, 0x4b, 0xc1, 0x75, 0x9f, 0x5e, 0x05, 0x25, 0x71, 0x9a, 0xac, - 0xc2, 0xb8, 0x5a, 0x95, 0xf0, 0xd1, 0x7c, 0x5b, 0x8c, 0x2c, 0x74, 0xc3, - 0x47, 0x38, 0x60, 0x2d, 0x81, 0x92, 0xfb, 0x7c, 0x36, 0x36, 0x49, 0xc6, - 0x7d, 0x27, 0x32, 0x11, 0x8c, 0xfc, 0x98, 0xa9, 0x86, 0xf9, 0x85, 0x75, - 0xff, 0x31, 0xf9, 0x67, 0xf8, 0x00, 0xa5, 0xd8, 0xeb, 0x1a, 0xcc, 0xf3, - 0x0f, 0xd7, 0x4b, 0xdd, 0x74, 0x53, 0x45, 0x1a, 0x3c, 0x34, 0x78, 0x4e, - 0xd3, 0x20, 0xd7, 0x50, 0x19, 0x80, 0x80, 0x00, 0xca, 0x53, 0xa3, 0x23, - 0xbc, 0x4f, 0x04, 0x65, 0x9a, 0x6f, 0x97, 0xb2, 0xfa, 0xe4, 0x5e, 0xcc, - 0xff, 0x3f, 0x09, 0x2c, 0xb2, 0x59, 0x78, 0xff, 0xf1, 0x50, 0x80, 0x27, - 0x7f, 0xfc, 0x21, 0x1a, 0xcf, 0x35, 0xff, 0xfe, 0x10, 0x44, 0xa5, 0xb1, - 0xd2, 0x99, 0x48, 0x41, 0x39, 0x05, 0x44, 0xc6, 0x00, 0x00, 0x00, 0x01, - 0xe6, 0xea, 0x5d, 0xf9, 0x5d, 0x71, 0x81, 0xa8, 0x92, 0xe4, 0x50, 0x2f, - 0xfc, 0x8e, 0xb1, 0xf4, 0xeb, 0x2d, 0xa2, 0xd0, 0xfc, 0x83, 0xcb, 0xa8, - 0x2b, 0xc1, 0xea, 0x83, 0x32, 0x31, 0xe2, 0xbb, 0xba, 0x6a, 0xaa, 0x48, - 0x1b, 0x15, 0x2f, 0xd6, 0x0f, 0xba, 0xaa, 0x2e, 0x97, 0xa9, 0x03, 0x46, - 0xbd, 0x42, 0x5f, 0x3a, 0x8a, 0xb9, 0x40, 0x48, 0x5e, 0xc7, 0x65, 0x7e, - 0x5d, 0x2b, 0x40, 0x4b, 0x48, 0x60, 0xda, 0x4a, 0x06, 0xc6, 0x56, 0x52, - 0xc2, 0xe1, 0x77, 0x41, 0x05, 0x00, 0x4b, 0x42, 0xa5, 0xec, 0xf3, 0x47, - 0xe2, 0xca, 0x3c, 0x6c, 0x83, 0x46, 0xc3, 0xd2, 0x21, 0x67, 0xbd, 0x87, - 0xae, 0x5c, 0x13, 0x58, 0xc1, 0x9f, 0xc4, 0x61, 0xac, 0x12, 0x06, 0x33, - 0x29, 0xcf, 0xf3, 0x62, 0xe4, 0x6f, 0x8c, 0xef, 0x57, 0xab, 0x0e, 0xbc, - 0x75, 0x25, 0xe2, 0x2e, 0xd3, 0xbb, 0xbd, 0xc9, 0x05, 0x3f, 0x34, 0x61, - 0x39, 0xce, 0x60, 0x14, 0xcc, 0x74, 0xec, 0xeb, 0x90, 0x2d, 0xb1, 0x66, - 0x95, 0x98, 0xd9, 0xf0, 0x43, 0x62, 0x2f, 0x46, 0x93, 0x00, 0x07, 0x77, - 0x10, 0x12, 0x03, 0x36, 0x27, 0xa4, 0x26, 0x81, 0xba, 0x40, 0x2e, 0xee, - 0xdc, 0x1f, 0x1d, 0x00, 0x0f, 0xf8, 0x21, 0x03, 0x07, 0x20, 0x32, 0xc7, - 0x5f, 0xaa, 0x66, 0x9a, 0x96, 0x68, 0x1c, 0x04, 0xf0, 0x93, 0x0f, 0x21, - 0x60, 0x3d, 0xd2, 0x60, 0xf6, 0xc8, 0xc4, 0xa8, 0xd4, 0xb4, 0x84, 0x25, - 0x05, 0x5b, 0xf1, 0x00, 0x00, 0x00, 0x2d, 0x53, 0x35, 0x87, 0x1c, 0xf1, - 0x7f, 0x37, 0xd7, 0x60, 0x38, 0x6f, 0x39, 0xcd, 0x11, 0x0e, 0xc3, 0x64, - 0xd7, 0x15, 0x1c, 0x48, 0x47, 0xe9, 0x17, 0x32, 0xda, 0x11, 0x8e, 0x8f, - 0x90, 0x38, 0xa7, 0xef, 0xe2, 0xa6, 0xe6, 0xf9, 0x2b, 0xb7, 0xdd, 0x9e, - 0x5b, 0xf3, 0x85, 0x74, 0x2a, 0x62, 0x91, 0x3a, 0xce, 0x7d, 0x19, 0xda, - 0x44, 0x19, 0x62, 0x78, 0x46, 0x72, 0xa5, 0xde, 0xe6, 0xf6, 0xbf, 0x4e, - 0x2c, 0x8a, 0x2c, 0x98, 0xc5, 0xc8, 0xad, 0xc1, 0x92, 0x78, 0xff, 0xf1, - 0x50, 0x80, 0x2b, 0xdf, 0xfc, 0x21, 0x1a, 0xce, 0xfc, 0xef, 0xff, 0x00, - 0x00, 0xa0, 0xb8, 0xc1, 0xd0, 0x66, 0xc6, 0x20, 0x00, 0x00, 0x00, 0x38, - 0xe7, 0x8a, 0xae, 0xab, 0x7e, 0xd9, 0xf5, 0xbf, 0x3a, 0x1a, 0xbc, 0x9c, - 0x0c, 0xf1, 0x1a, 0x4d, 0xfb, 0xc6, 0x8b, 0x22, 0x64, 0x5c, 0xde, 0x2d, - 0x84, 0xc1, 0x58, 0x20, 0x10, 0x1d, 0x73, 0x4a, 0xf2, 0x04, 0x0a, 0x02, - 0x3a, 0x57, 0xb9, 0x96, 0x1d, 0xba, 0x7a, 0xa4, 0x66, 0xb1, 0xf3, 0xb9, - 0xbf, 0x1f, 0x02, 0x35, 0x48, 0x58, 0xc5, 0x2d, 0x0c, 0x5b, 0x25, 0xb8, - 0xe8, 0xd0, 0x43, 0x70, 0xad, 0x0e, 0x88, 0x99, 0x25, 0x78, 0x1c, 0x81, - 0x46, 0x5b, 0x4b, 0xe6, 0xd1, 0xae, 0x1a, 0x00, 0x49, 0x02, 0xd7, 0x05, - 0x84, 0x17, 0xae, 0x85, 0x05, 0x22, 0x4d, 0x63, 0xc6, 0xa9, 0xef, 0x9c, - 0x50, 0x9c, 0x55, 0x42, 0x81, 0x45, 0x34, 0x2e, 0x93, 0xb0, 0x63, 0x71, - 0x0b, 0x3a, 0xbd, 0x4a, 0xfe, 0x2a, 0x16, 0x3e, 0xdf, 0x82, 0x20, 0x99, - 0xa9, 0x86, 0xfb, 0x54, 0x19, 0xb5, 0xb6, 0x97, 0x4d, 0xa4, 0xb4, 0x2c, - 0x65, 0x9f, 0x9f, 0xf0, 0x09, 0x4f, 0x4e, 0x22, 0x76, 0x4d, 0xa4, 0xd4, - 0x8a, 0xa9, 0xec, 0x85, 0xfd, 0x91, 0x23, 0xdc, 0x00, 0xcf, 0xa7, 0x68, - 0x0b, 0x75, 0xfb, 0x53, 0xa7, 0xc8, 0x6a, 0xef, 0xfe, 0x2f, 0x0b, 0xd4, - 0x16, 0xeb, 0x64, 0x35, 0x6c, 0xa2, 0x2f, 0x0b, 0x56, 0x55, 0xe4, 0x09, - 0x04, 0x62, 0x0f, 0x08, 0xe2, 0x88, 0x01, 0xee, 0xb1, 0x5c, 0x5c, 0x9c, - 0x84, 0xc9, 0x2d, 0x96, 0x8a, 0x6d, 0xed, 0xba, 0x7a, 0xd6, 0xba, 0x79, - 0x4b, 0x1c, 0xce, 0xba, 0x0b, 0x6c, 0x11, 0x8a, 0xa4, 0x60, 0xfc, 0x0a, - 0x80, 0xa4, 0x2a, 0x00, 0x0d, 0x4d, 0x78, 0xd6, 0xee, 0x5f, 0x8d, 0x4e, - 0xde, 0x79, 0x0d, 0xfa, 0xdc, 0xdb, 0x05, 0x27, 0x87, 0x91, 0x11, 0xc6, - 0x4e, 0x81, 0x20, 0xec, 0x05, 0x20, 0x50, 0x46, 0x6a, 0xcb, 0x71, 0xa5, - 0x1b, 0xd3, 0x72, 0xf1, 0x1c, 0xd6, 0x02, 0xdc, 0xcb, 0xb2, 0x00, 0x1c, - 0x4f, 0x1d, 0xf1, 0x8a, 0xa8, 0x35, 0x33, 0x75, 0x3c, 0x5c, 0x4a, 0xff, - 0x83, 0x13, 0x28, 0x02, 0xdd, 0x2c, 0xf7, 0xce, 0xe9, 0x5f, 0xc2, 0x9d, - 0x55, 0x05, 0xa9, 0x72, 0x90, 0xf2, 0x26, 0xd6, 0x86, 0x62, 0xa3, 0xb6, - 0x62, 0xf7, 0xc9, 0xd5, 0xed, 0xbd, 0xaf, 0xe6, 0xbb, 0x3b, 0x8d, 0x72, - 0xa2, 0xbb, 0x96, 0xe6, 0x56, 0x4b, 0x81, 0x3a, 0x40, 0x95, 0x22, 0x3c, - 0xff, 0xf1, 0x50, 0x80, 0x2e, 0x9f, 0xfc, 0x21, 0x1a, 0xcf, 0xff, 0xf6, - 0xff, 0x00, 0x40, 0x9e, 0xb6, 0xc2, 0xd5, 0xec, 0x40, 0x00, 0x00, 0x00, - 0x5e, 0x6a, 0xeb, 0x55, 0xbf, 0x69, 0xf1, 0xcd, 0xf9, 0x1d, 0xda, 0x46, - 0xb5, 0x3a, 0xca, 0x2f, 0xe0, 0xb9, 0x9a, 0xeb, 0x34, 0x77, 0x3e, 0xb6, - 0xa9, 0x80, 0x38, 0x5a, 0x53, 0xe5, 0xa6, 0x1d, 0x1a, 0x7d, 0x6d, 0xcc, - 0x9d, 0x25, 0x22, 0xa5, 0x30, 0xb9, 0x63, 0xcd, 0xe2, 0xab, 0x26, 0x1e, - 0x11, 0x9d, 0x48, 0xed, 0x4d, 0x69, 0xc3, 0x23, 0xa5, 0x46, 0x9d, 0x75, - 0x92, 0x71, 0x11, 0xf5, 0x6d, 0x8c, 0x79, 0xa5, 0xc2, 0x76, 0xc4, 0x65, - 0x18, 0xc6, 0x57, 0x9b, 0xa5, 0xcf, 0x5a, 0x23, 0x50, 0x65, 0x39, 0x73, - 0x9e, 0xba, 0x4e, 0x6c, 0x4b, 0x28, 0x81, 0xc3, 0xa2, 0x19, 0x11, 0xcc, - 0x84, 0xa3, 0x86, 0x79, 0xcc, 0x01, 0x08, 0x99, 0xba, 0xc4, 0xc7, 0x5c, - 0xf9, 0xbe, 0x35, 0x2e, 0xd8, 0xac, 0x5c, 0x93, 0x8f, 0x4b, 0x53, 0x10, - 0xe7, 0x08, 0x90, 0xef, 0x02, 0xc5, 0x45, 0x02, 0xa0, 0xb0, 0x90, 0xb6, - 0x34, 0x52, 0x87, 0xd9, 0x5a, 0x06, 0x05, 0xb9, 0x2a, 0xcb, 0x42, 0xd3, - 0x21, 0x15, 0x15, 0x51, 0xb1, 0x69, 0x0e, 0x20, 0xa2, 0xce, 0x9d, 0x55, - 0x52, 0xa5, 0x13, 0x1e, 0xb0, 0x00, 0x03, 0x82, 0x17, 0x80, 0x19, 0xca, - 0xfd, 0xc4, 0xca, 0xee, 0x22, 0x2b, 0xed, 0xe8, 0x94, 0x84, 0xc0, 0x01, - 0x4e, 0x57, 0x6a, 0xe7, 0x1e, 0xcc, 0xb4, 0x8d, 0xbe, 0x57, 0xc5, 0xd6, - 0x44, 0xbd, 0x60, 0x22, 0x0c, 0x03, 0x2c, 0xb2, 0xb7, 0x4c, 0xb6, 0x66, - 0xa9, 0x73, 0xb3, 0xac, 0x18, 0xf3, 0xcf, 0x75, 0xdd, 0x37, 0x5d, 0xeb, - 0xf1, 0xe3, 0x67, 0x48, 0x49, 0x86, 0x7a, 0xc5, 0x0a, 0xb2, 0xc1, 0x18, - 0x52, 0x4f, 0x80, 0x00, 0x0a, 0x8a, 0x80, 0x3c, 0xe3, 0x9b, 0x5e, 0xfa, - 0xef, 0x57, 0xdd, 0x5d, 0x05, 0x9b, 0x14, 0x3e, 0xe5, 0x92, 0x3b, 0x55, - 0x30, 0xd6, 0x8d, 0x21, 0x53, 0x3d, 0xf5, 0x52, 0x74, 0x59, 0x2a, 0x09, - 0x41, 0xe7, 0x96, 0xb8, 0xcf, 0x37, 0x4f, 0x13, 0x20, 0x9d, 0xa3, 0x8a, - 0xf0, 0x6a, 0x8a, 0x36, 0x4d, 0xc7, 0x53, 0xbb, 0x25, 0xbf, 0x99, 0x64, - 0x46, 0x60, 0x94, 0x66, 0x4e, 0xb9, 0x15, 0x27, 0x0c, 0x57, 0x3b, 0x0b, - 0xf2, 0xb4, 0x26, 0x12, 0x8e, 0xbd, 0x0a, 0x83, 0x83, 0x5d, 0xcc, 0x81, - 0x90, 0xd9, 0x4e, 0x24, 0x60, 0xe2, 0xec, 0xcf, 0xd3, 0xfa, 0x3c, 0xf3, - 0xcd, 0x7b, 0x6f, 0x1d, 0xb2, 0xd5, 0xd5, 0xe7, 0xec, 0xd7, 0xc4, 0xdd, - 0xb2, 0xe6, 0xa5, 0x1a, 0xf3, 0xd7, 0x30, 0xce, 0xe2, 0x37, 0x43, 0x80, - 0xff, 0xf1, 0x50, 0x80, 0x2e, 0xff, 0xfc, 0x21, 0x1a, 0xcf, 0x79, 0xf1, - 0x7f, 0x44, 0x00, 0x9f, 0xb7, 0xb1, 0x20, 0x2c, 0x45, 0x6b, 0x18, 0x00, - 0x00, 0x00, 0x71, 0xcf, 0x9f, 0x1e, 0xce, 0x37, 0xd7, 0xcf, 0xeb, 0xbf, - 0x8f, 0x1d, 0x41, 0xd3, 0x75, 0xd6, 0x27, 0xfe, 0x59, 0x8e, 0xac, 0x9f, - 0x9b, 0x55, 0x7f, 0xa3, 0xa0, 0xb1, 0xa9, 0xb1, 0xda, 0x4b, 0xe4, 0x55, - 0xe2, 0xae, 0x34, 0xea, 0x28, 0xfd, 0x34, 0x7a, 0xaa, 0xd3, 0x20, 0x68, - 0x3f, 0x6f, 0xc7, 0x35, 0xb1, 0x18, 0x07, 0x5e, 0x0c, 0xb1, 0xaf, 0xa5, - 0xb6, 0xee, 0x82, 0xd9, 0x6a, 0xe7, 0x49, 0x40, 0xc0, 0x91, 0x0c, 0x3a, - 0x21, 0xc1, 0xd0, 0x64, 0xc7, 0x34, 0x80, 0x7f, 0x57, 0x0f, 0x7c, 0xb8, - 0x5a, 0x35, 0x5f, 0xc1, 0x6d, 0xf5, 0x1e, 0x0c, 0x8b, 0x8a, 0x87, 0xf1, - 0xe5, 0x57, 0x4b, 0x5a, 0x49, 0x45, 0x4d, 0x46, 0x66, 0x5d, 0x70, 0x48, - 0xa9, 0xf6, 0xc9, 0x71, 0x10, 0x58, 0x37, 0x10, 0x00, 0xb9, 0xe6, 0xa1, - 0x07, 0x7a, 0x58, 0x1b, 0xab, 0xab, 0x80, 0x92, 0x3d, 0x0a, 0x00, 0xab, - 0xee, 0xef, 0x99, 0x89, 0x05, 0xc8, 0x9e, 0x7d, 0x25, 0x16, 0x5a, 0xab, - 0x0b, 0xb7, 0xc3, 0xc6, 0x62, 0x23, 0x60, 0xfa, 0x8b, 0x06, 0x4f, 0x6f, - 0x09, 0x4c, 0x59, 0x88, 0x14, 0x2f, 0x94, 0x9b, 0xd5, 0x42, 0x85, 0x37, - 0x49, 0x6d, 0x59, 0x19, 0x1c, 0x34, 0xe3, 0x54, 0x47, 0x2c, 0x11, 0x50, - 0xcd, 0x15, 0x80, 0x00, 0xc0, 0xe4, 0xce, 0x75, 0xd7, 0x6c, 0xa5, 0xaa, - 0xb5, 0x4d, 0x7d, 0x5e, 0x5a, 0xa4, 0xed, 0x77, 0x54, 0x41, 0xbd, 0x5a, - 0x66, 0x44, 0x0a, 0xff, 0x35, 0xb2, 0xeb, 0x98, 0x34, 0xf0, 0xbc, 0x45, - 0x37, 0x89, 0xed, 0x6c, 0x0c, 0x1e, 0xb9, 0xea, 0xe2, 0x75, 0x71, 0xce, - 0x27, 0xee, 0x88, 0x36, 0x0a, 0x0f, 0xd6, 0x61, 0xf2, 0x00, 0x00, 0x00, - 0x0f, 0x3e, 0xb8, 0xe7, 0x8d, 0xf5, 0x97, 0xe3, 0x5a, 0xdd, 0x71, 0x81, - 0x42, 0x76, 0x2b, 0xbe, 0xa9, 0x86, 0x35, 0xea, 0x7f, 0xee, 0x1d, 0x42, - 0x61, 0xc1, 0x9a, 0x17, 0xc5, 0xde, 0xda, 0x18, 0x95, 0x87, 0x10, 0xad, - 0x04, 0x73, 0xca, 0x67, 0x41, 0xa3, 0x00, 0x35, 0x24, 0xc8, 0xb3, 0xe0, - 0x33, 0x62, 0x68, 0x91, 0xf8, 0xc3, 0x21, 0xad, 0x1b, 0xb9, 0x53, 0x33, - 0x81, 0xb8, 0x17, 0xb9, 0x8a, 0x6e, 0xb2, 0x5c, 0x00, 0x03, 0xdc, 0x1c, - 0x86, 0x17, 0x35, 0x3c, 0x6c, 0xdf, 0x91, 0xa1, 0x54, 0x52, 0x9d, 0x4a, - 0xba, 0xaa, 0xc0, 0xbf, 0xbd, 0x93, 0xb1, 0x1d, 0x7e, 0xbf, 0x85, 0x61, - 0xb9, 0xd6, 0x07, 0xcb, 0xff, 0x6b, 0xcb, 0x7d, 0x5e, 0xc6, 0x78, 0x40, - 0xaf, 0x70, 0x07, 0xff, 0xf1, 0x50, 0x80, 0x2c, 0x5f, 0xfc, 0x21, 0x1a, - 0xcf, 0x93, 0xf7, 0x7f, 0x24, 0x40, 0x9f, 0xb6, 0xc1, 0xd8, 0x8a, 0xd6, - 0x30, 0x00, 0x00, 0x02, 0x55, 0xe5, 0xe7, 0xe3, 0x7a, 0xdd, 0x6f, 0xf7, - 0xc9, 0x27, 0x02, 0x7f, 0x09, 0x39, 0x70, 0xc8, 0xb0, 0x3e, 0xae, 0xa9, - 0xc3, 0xad, 0x8f, 0xf5, 0x55, 0x0e, 0x85, 0xac, 0x40, 0xaa, 0x39, 0x2d, - 0xc8, 0xc8, 0x54, 0x0c, 0x68, 0x67, 0x09, 0xb2, 0x41, 0xc4, 0x34, 0x70, - 0xbd, 0x83, 0x57, 0xcf, 0xea, 0x9b, 0xd2, 0x62, 0x20, 0x7a, 0xf1, 0x92, - 0x64, 0x50, 0xd3, 0x73, 0xa5, 0x9d, 0x0d, 0xf1, 0xbc, 0x13, 0x00, 0x47, - 0x59, 0x0f, 0xab, 0xe0, 0xda, 0xb2, 0x03, 0x39, 0x91, 0xab, 0x50, 0x97, - 0x2b, 0xde, 0x97, 0x3e, 0x0a, 0x5c, 0x53, 0x20, 0x78, 0xa1, 0xef, 0xf9, - 0x6c, 0x68, 0x0b, 0x6b, 0xbb, 0xbc, 0x2f, 0x4a, 0xbb, 0x95, 0x71, 0x82, - 0x17, 0x4d, 0xfd, 0x10, 0x58, 0x29, 0x72, 0x71, 0xa9, 0x00, 0x5d, 0xd8, - 0x05, 0x41, 0x80, 0x14, 0x00, 0xfb, 0x2b, 0x00, 0x29, 0x5d, 0x9e, 0x11, - 0x11, 0x42, 0x61, 0x72, 0x39, 0xf6, 0x85, 0x5e, 0x90, 0x4e, 0x8b, 0xbf, - 0x5f, 0x2b, 0x59, 0x62, 0xbc, 0x81, 0x01, 0x5d, 0xd5, 0x05, 0x5e, 0x42, - 0xad, 0x03, 0xb6, 0x8b, 0x80, 0x25, 0x69, 0x2c, 0xfb, 0x31, 0xa1, 0x40, - 0x81, 0xc8, 0xc2, 0x3e, 0xfc, 0x20, 0x1c, 0x48, 0xdc, 0x00, 0x10, 0xc9, - 0x32, 0xe7, 0x23, 0xc2, 0x00, 0x22, 0xd2, 0x34, 0x32, 0xaf, 0xac, 0x32, - 0x1a, 0x64, 0xbd, 0x67, 0xa6, 0xde, 0x00, 0x0a, 0x77, 0xee, 0x71, 0x40, - 0xc4, 0xe6, 0x1c, 0x49, 0x6a, 0xe5, 0xbb, 0x87, 0x1d, 0x5d, 0xcf, 0xba, - 0x3f, 0x34, 0xa4, 0x42, 0xc1, 0x94, 0xfd, 0xb5, 0xcd, 0x61, 0x93, 0xfc, - 0x00, 0x00, 0x00, 0x12, 0xba, 0xde, 0x67, 0x9e, 0x78, 0xa7, 0x7e, 0x67, - 0x89, 0x40, 0xbf, 0xf4, 0xfb, 0xf6, 0x1a, 0x2c, 0xe5, 0xe1, 0x3e, 0xed, - 0x14, 0xf6, 0xb4, 0x2c, 0xf1, 0x89, 0xa6, 0x8f, 0xd1, 0x4a, 0x2c, 0x72, - 0xa3, 0xe0, 0x2c, 0xa1, 0x99, 0x3a, 0xeb, 0x25, 0xf4, 0x62, 0x6a, 0x78, - 0x9b, 0x3a, 0x6f, 0xf0, 0x7b, 0x9f, 0x4e, 0xaf, 0x75, 0xcd, 0x86, 0xed, - 0x7c, 0xaa, 0x74, 0xef, 0x63, 0x32, 0x8e, 0xe5, 0x38, 0x1b, 0x10, 0xc5, - 0xdd, 0x57, 0x34, 0x44, 0x4e, 0x24, 0xda, 0x2b, 0x09, 0x56, 0x27, 0xcd, - 0x72, 0x27, 0x44, 0xc9, 0x55, 0x35, 0xbb, 0x0c, 0xf7, 0xc2, 0xe3, 0x1d, - 0xf8, 0xda, 0x06, 0x39, 0x70, 0x57, 0x34, 0x21, 0xc0, 0xff, 0xf1, 0x50, - 0x80, 0x2e, 0x3f, 0xfc, 0x21, 0x1a, 0xce, 0xff, 0xe7, 0xff, 0x44, 0x40, - 0x9d, 0xb6, 0xc1, 0xac, 0x2c, 0x38, 0x0a, 0xbd, 0x88, 0x00, 0x00, 0x00, - 0x38, 0xe6, 0xf3, 0xcd, 0x6a, 0xa7, 0x8f, 0xc5, 0x4a, 0xeb, 0xc8, 0x94, - 0x40, 0x46, 0x5e, 0x52, 0x8a, 0x8f, 0x69, 0x93, 0x12, 0x9d, 0xd7, 0xa1, - 0x6a, 0x33, 0xbe, 0x5b, 0x8b, 0x5b, 0x4d, 0x3b, 0xb4, 0x4d, 0xfa, 0xcc, - 0xfa, 0xca, 0x77, 0xaa, 0xa4, 0xbf, 0xf6, 0x38, 0x89, 0x69, 0xc1, 0x3f, - 0x0e, 0x98, 0x20, 0x98, 0xbb, 0x38, 0x40, 0x8e, 0xf5, 0x99, 0xcf, 0x66, - 0x69, 0x37, 0x64, 0x2f, 0xaa, 0xa2, 0xe4, 0x92, 0x79, 0x4b, 0xd5, 0x30, - 0xe7, 0x24, 0xb5, 0xad, 0xd0, 0xe0, 0x63, 0x5e, 0x28, 0x5c, 0x0b, 0xb0, - 0x43, 0xf4, 0xce, 0xb4, 0xab, 0x69, 0x77, 0xfa, 0x5b, 0x59, 0xa5, 0xc2, - 0x39, 0xf7, 0x4f, 0x16, 0x77, 0x49, 0x2b, 0x56, 0x8a, 0xeb, 0xea, 0x16, - 0x87, 0x12, 0x36, 0x23, 0x9f, 0xf3, 0xa3, 0x53, 0x31, 0x0a, 0x48, 0xd4, - 0x80, 0x2b, 0x09, 0xe0, 0x00, 0x5c, 0x82, 0x1c, 0x56, 0xa0, 0xcf, 0xf1, - 0x9a, 0x10, 0x11, 0xdd, 0x8a, 0xba, 0x6a, 0xa8, 0x2c, 0x4d, 0xf2, 0x48, - 0xdc, 0x40, 0xc8, 0x97, 0x7c, 0x20, 0xb0, 0x7a, 0x54, 0x2c, 0x1e, 0x52, - 0x02, 0xc0, 0x2b, 0x11, 0x05, 0x80, 0x6e, 0x2f, 0xe1, 0xcb, 0xbd, 0xd5, - 0x30, 0xbb, 0xdd, 0xf5, 0x7d, 0x9d, 0x31, 0x92, 0x51, 0x0b, 0x42, 0x53, - 0x94, 0xd5, 0x66, 0x04, 0x6b, 0x50, 0x25, 0x56, 0xda, 0xad, 0x08, 0x92, - 0x41, 0x08, 0xbb, 0x90, 0x9b, 0xd7, 0xdf, 0x70, 0x0e, 0x60, 0xc3, 0x3d, - 0xb0, 0x59, 0x35, 0xbe, 0x9e, 0xec, 0xfc, 0xfc, 0x42, 0x7b, 0x2d, 0x6d, - 0x37, 0xd8, 0xff, 0xec, 0xed, 0xd1, 0x06, 0xc2, 0x71, 0x7c, 0x0a, 0x80, - 0x05, 0x20, 0x01, 0xe7, 0xdf, 0x8d, 0xdd, 0x75, 0x4e, 0xf5, 0x3b, 0x97, - 0x02, 0x89, 0x10, 0x83, 0xe5, 0xe3, 0x98, 0xca, 0xd3, 0x64, 0xd5, 0xe1, - 0xbe, 0x7c, 0xe1, 0xd1, 0x9a, 0x1d, 0x9e, 0xf1, 0x3a, 0xd1, 0x6c, 0x85, - 0x31, 0x41, 0xf4, 0x78, 0x74, 0xc1, 0x8d, 0x7c, 0x11, 0x6c, 0x7e, 0xbb, - 0x03, 0x85, 0x07, 0xbf, 0xb2, 0x2a, 0xc3, 0x23, 0x5a, 0xfe, 0x2a, 0x51, - 0x92, 0x33, 0xe4, 0xa4, 0x0f, 0x25, 0xa3, 0x1f, 0x1a, 0xdf, 0x3a, 0x02, - 0x96, 0x65, 0xc1, 0x64, 0xa0, 0x3a, 0x59, 0x66, 0x47, 0x5a, 0x19, 0xe5, - 0xb2, 0x66, 0xda, 0x1b, 0xaa, 0x03, 0x51, 0xff, 0xdf, 0x55, 0x9d, 0x2f, - 0x30, 0x35, 0x10, 0x65, 0x62, 0x14, 0xc3, 0x7c, 0xa3, 0x38, 0x23, 0x2e, - 0xfd, 0x13, 0xa9, 0x9a, 0x21, 0xc0, 0xff, 0xf1, 0x50, 0x80, 0x2d, 0x7f, - 0xfc, 0x21, 0x1a, 0xcf, 0xd5, 0xc7, 0xff, 0x04, 0x40, 0x9f, 0xb6, 0xcb, - 0x8d, 0x4c, 0x60, 0x54, 0xa9, 0x50, 0xa2, 0x00, 0x4a, 0xe9, 0x7e, 0xff, - 0xaf, 0x8f, 0xc7, 0x73, 0xed, 0xfe, 0x92, 0x56, 0x74, 0x35, 0xee, 0x09, - 0x88, 0x24, 0xe5, 0x74, 0x86, 0x3a, 0xc4, 0x7f, 0x47, 0x38, 0x4f, 0x71, - 0xd9, 0x75, 0x42, 0xd3, 0x13, 0x6b, 0x81, 0x08, 0x70, 0xb1, 0x5e, 0x93, - 0x9f, 0x8f, 0x44, 0x11, 0xbe, 0xf1, 0xf4, 0x70, 0x7c, 0x0d, 0xbb, 0x45, - 0x85, 0x33, 0x86, 0xfc, 0x37, 0xeb, 0xec, 0x6a, 0xce, 0xfd, 0x7e, 0x73, - 0x3d, 0x9d, 0xdf, 0x08, 0x5b, 0x7f, 0xa6, 0xfe, 0x5d, 0x38, 0xa2, 0x26, - 0x35, 0x2e, 0x67, 0x6c, 0xd5, 0x67, 0x7b, 0x2b, 0x53, 0x9b, 0x7c, 0x58, - 0xca, 0xf5, 0x26, 0xfa, 0xba, 0xcc, 0x8d, 0x79, 0x25, 0x41, 0x72, 0x4c, - 0x5b, 0x28, 0x11, 0xc0, 0x82, 0xe0, 0x2b, 0x49, 0xfa, 0x99, 0x33, 0xf6, - 0x36, 0x5b, 0x0e, 0xb7, 0xf0, 0x5a, 0x06, 0x9e, 0x7d, 0x81, 0xe9, 0x90, - 0x0b, 0xff, 0xb2, 0xe8, 0x92, 0x9c, 0xeb, 0x8d, 0x3f, 0x10, 0xa3, 0x6f, - 0xaa, 0x8e, 0x6f, 0xf9, 0x2d, 0x1e, 0x29, 0x5b, 0x75, 0xf7, 0x6e, 0x6a, - 0x63, 0xbb, 0xfb, 0x96, 0x97, 0x5f, 0xc7, 0xc3, 0x4d, 0xc9, 0xbb, 0x31, - 0x01, 0xba, 0x2d, 0xf4, 0x52, 0x32, 0xa4, 0x30, 0x40, 0x01, 0x7b, 0x23, - 0x51, 0xd2, 0x57, 0xb5, 0x02, 0xb7, 0x45, 0x53, 0x3a, 0xa4, 0xfb, 0xcc, - 0x34, 0x69, 0x45, 0x97, 0x67, 0x21, 0xe9, 0xb7, 0x07, 0xba, 0x4d, 0xe4, - 0xb6, 0x59, 0x3e, 0x57, 0x2a, 0x4a, 0x24, 0xab, 0xe0, 0x67, 0x89, 0xc4, - 0xb1, 0x4d, 0x69, 0x85, 0x1e, 0xc7, 0xba, 0x55, 0xdb, 0xe6, 0xc4, 0x62, - 0x6c, 0x52, 0xec, 0xeb, 0x9f, 0xb6, 0xc1, 0x9d, 0x1e, 0xb5, 0x0f, 0x90, - 0x00, 0x00, 0x01, 0x2b, 0x8a, 0xcc, 0xe3, 0x7e, 0x76, 0xf1, 0xed, 0xed, - 0xc8, 0x08, 0x24, 0xc2, 0xd2, 0x4b, 0xdc, 0x4c, 0x4a, 0x4e, 0x66, 0x23, - 0xac, 0xf1, 0xc1, 0x67, 0xe4, 0xbd, 0x6c, 0xa6, 0x60, 0x8e, 0xd1, 0x0a, - 0x50, 0xbf, 0x9f, 0x34, 0xa2, 0xd8, 0x17, 0xd6, 0xd1, 0xe9, 0xc2, 0x8c, - 0xc2, 0xf5, 0x7e, 0x85, 0x3b, 0x2c, 0xdd, 0x4e, 0x68, 0x0c, 0x95, 0xe0, - 0x48, 0x84, 0x60, 0x7d, 0x84, 0xc6, 0x31, 0xba, 0x0f, 0x4d, 0xf2, 0xf2, - 0xa4, 0xcc, 0x5d, 0xa7, 0x18, 0xc6, 0xc0, 0x2a, 0x4c, 0xe1, 0xf6, 0x3c, - 0x3c, 0x72, 0x56, 0x04, 0x98, 0xc8, 0xac, 0x4c, 0x66, 0xb3, 0xd5, 0x95, - 0x37, 0xeb, 0x0d, 0xd2, 0xb3, 0xca, 0x40, 0x00, 0xe0, 0xff, 0xf1, 0x50, - 0x80, 0x2e, 0xbf, 0xfc, 0x21, 0x1a, 0xce, 0xf8, 0xed, 0xa7, 0x44, 0x40, - 0x9e, 0xb6, 0xb1, 0x64, 0xca, 0xd6, 0x24, 0x08, 0x00, 0x00, 0x01, 0x2b, - 0xdb, 0xc7, 0xb7, 0xbf, 0xf3, 0xf3, 0xed, 0xcf, 0x9f, 0x9f, 0xc5, 0x5b, - 0xce, 0x0e, 0x8a, 0x27, 0x14, 0xfb, 0x87, 0x26, 0x09, 0x2c, 0x9c, 0x9e, - 0x41, 0xe4, 0xd2, 0x44, 0x60, 0x8a, 0xcb, 0x3a, 0x96, 0x79, 0xf2, 0xc4, - 0x0c, 0x65, 0xaa, 0x94, 0xf4, 0x59, 0xe5, 0xeb, 0x30, 0x12, 0xbc, 0x11, - 0x31, 0x18, 0x49, 0xe5, 0x3e, 0x43, 0x69, 0x55, 0xc0, 0x5f, 0xb7, 0xce, - 0x62, 0x0a, 0x03, 0x33, 0x13, 0x44, 0xbc, 0x33, 0x74, 0x5f, 0xa2, 0xfe, - 0x31, 0xb2, 0xc1, 0x75, 0x8e, 0xae, 0x58, 0x56, 0x19, 0xe7, 0xb1, 0xc8, - 0xdb, 0x39, 0x96, 0x8c, 0x3f, 0x78, 0xf6, 0x39, 0xc6, 0xb2, 0x55, 0x85, - 0x2a, 0x6a, 0x13, 0x78, 0x62, 0xc3, 0x8f, 0xaf, 0x47, 0x30, 0x6d, 0x72, - 0x7b, 0xd9, 0xd8, 0xb0, 0xce, 0xf8, 0xc8, 0x13, 0xce, 0x66, 0x74, 0x2e, - 0xed, 0xdf, 0x00, 0x23, 0xd7, 0x60, 0x15, 0xae, 0x7e, 0x9f, 0xd5, 0x3a, - 0x9a, 0x10, 0xc8, 0x1c, 0x81, 0x52, 0x1a, 0x0a, 0xfd, 0xdd, 0x32, 0x52, - 0xd4, 0xbf, 0x76, 0x8a, 0x5c, 0x8b, 0xff, 0x7d, 0xdd, 0x19, 0x8c, 0x95, - 0x8c, 0xee, 0x11, 0x7d, 0x99, 0x80, 0x05, 0x07, 0x6c, 0x57, 0x2d, 0x17, - 0xba, 0x41, 0xd4, 0x37, 0xb2, 0x55, 0xd4, 0xe2, 0x06, 0x8f, 0x2b, 0x01, - 0x2d, 0x16, 0x1d, 0x1e, 0x0f, 0x58, 0xd7, 0x8d, 0x88, 0x79, 0x15, 0x61, - 0x05, 0xe1, 0x7c, 0xcd, 0x63, 0x4b, 0xbc, 0x5a, 0x94, 0x37, 0xab, 0x91, - 0x4d, 0xc2, 0xdb, 0xc9, 0xb5, 0x71, 0x0a, 0xa3, 0x95, 0x42, 0xfa, 0x79, - 0xe5, 0xa1, 0x6d, 0x62, 0x60, 0x3c, 0x3b, 0xc3, 0x95, 0x05, 0x42, 0x47, - 0x04, 0xf5, 0xb6, 0x0c, 0xe8, 0xf8, 0x15, 0x02, 0x88, 0x54, 0x54, 0x54, - 0xa4, 0x95, 0xe7, 0xd5, 0xb8, 0xf1, 0xd6, 0x4e, 0x65, 0xe5, 0x6b, 0x02, - 0x2c, 0xbc, 0x2e, 0x31, 0xf3, 0x26, 0x25, 0xe2, 0x06, 0xe5, 0x44, 0x06, - 0x01, 0xd7, 0x98, 0xb2, 0x93, 0x6d, 0x94, 0x14, 0x53, 0x23, 0xbd, 0xb0, - 0x71, 0x86, 0x19, 0x7a, 0xd9, 0x32, 0x49, 0xd2, 0x2b, 0x39, 0x0a, 0x47, - 0xbd, 0x9d, 0xaa, 0x95, 0x81, 0x30, 0x39, 0xd5, 0xa6, 0xd5, 0xe9, 0xc6, - 0x89, 0x3d, 0x89, 0x0f, 0xad, 0x13, 0xaa, 0xff, 0x2b, 0x6c, 0x19, 0x4f, - 0x61, 0xcd, 0x57, 0x56, 0xa9, 0xa3, 0x2e, 0x27, 0x0e, 0x74, 0xf9, 0xa2, - 0x3a, 0x30, 0xbf, 0x77, 0x85, 0x4b, 0x77, 0x28, 0x82, 0xf3, 0x5b, 0x3c, - 0x66, 0xaa, 0x59, 0x54, 0xaa, 0x2b, 0xcc, 0x37, 0x03, 0x80, 0xff, 0xf1, - 0x50, 0x80, 0x2d, 0x5f, 0xfc, 0x21, 0x1a, 0xcf, 0xf9, 0xee, 0xe5, 0x04, - 0x40, 0xa1, 0xb6, 0xca, 0xd5, 0xac, 0x60, 0x25, 0x25, 0x11, 0x46, 0xb7, - 0x0a, 0xbe, 0xbd, 0x7e, 0xff, 0x3f, 0x8f, 0xe3, 0xf7, 0x93, 0xbe, 0xbe, - 0xdf, 0xe9, 0x97, 0xbb, 0xb1, 0x18, 0x58, 0x83, 0xcd, 0x7a, 0x2b, 0x24, - 0x41, 0xfd, 0xe4, 0xdf, 0x0d, 0x75, 0x9d, 0x7c, 0x1e, 0x64, 0x89, 0xac, - 0xe9, 0x8c, 0x8e, 0x8c, 0xf0, 0xf6, 0xa8, 0x69, 0xd5, 0xcf, 0x3b, 0x39, - 0x63, 0x2b, 0x41, 0x98, 0x30, 0x9d, 0x66, 0x51, 0x8a, 0xf9, 0x79, 0x5a, - 0xa5, 0xbf, 0x09, 0xce, 0x39, 0x11, 0x95, 0x4a, 0xf0, 0xf5, 0x1d, 0x09, - 0x2d, 0x96, 0x9a, 0xb1, 0xdb, 0x37, 0x53, 0xbe, 0x2b, 0x74, 0xa4, 0xbc, - 0x5b, 0xbd, 0x39, 0x41, 0x2a, 0x2e, 0xa5, 0x2e, 0x73, 0x1c, 0x70, 0xde, - 0x57, 0xb8, 0xbe, 0xe1, 0x60, 0xde, 0xb9, 0x7c, 0xa2, 0xbe, 0x1c, 0x65, - 0x88, 0xa8, 0x06, 0x2b, 0x80, 0x1a, 0xf5, 0xd4, 0xb2, 0xab, 0x87, 0x8c, - 0x77, 0x4e, 0xc0, 0x75, 0x80, 0x0f, 0x77, 0xa7, 0x19, 0xab, 0xea, 0x45, - 0x4a, 0xfb, 0xd7, 0x8c, 0xfb, 0xfe, 0x9b, 0xc4, 0x31, 0x8d, 0xe2, 0x62, - 0xab, 0x38, 0xb6, 0xbf, 0xb6, 0xab, 0x07, 0x76, 0x05, 0x7d, 0xb9, 0x92, - 0xc2, 0x5f, 0xeb, 0xe9, 0xdd, 0x5b, 0x18, 0xab, 0xa9, 0x89, 0xbc, 0xa2, - 0xfa, 0x41, 0x40, 0xbf, 0x68, 0x16, 0x79, 0x59, 0x9e, 0x57, 0x61, 0x7e, - 0x1c, 0x6d, 0x8e, 0x9f, 0x7d, 0xf4, 0x89, 0x4d, 0x49, 0xd1, 0x3f, 0x78, - 0x82, 0x3b, 0xd9, 0x1d, 0x3a, 0x1d, 0xb6, 0x36, 0x52, 0xd3, 0xa2, 0xc4, - 0x1f, 0x27, 0xa4, 0x19, 0x8e, 0xe8, 0xf0, 0xa0, 0x88, 0x52, 0x5b, 0xa8, - 0x8e, 0xea, 0xe9, 0x1e, 0xa5, 0xef, 0xbb, 0x09, 0x60, 0x30, 0xb6, 0xe3, - 0xae, 0x5b, 0xcd, 0xbe, 0xd6, 0x57, 0xa7, 0x3f, 0x45, 0x91, 0xf1, 0x5a, - 0x1a, 0x0b, 0x2e, 0xc6, 0xad, 0xf7, 0x81, 0x28, 0x12, 0x81, 0x14, 0xbc, - 0x6b, 0x3a, 0xc9, 0x9c, 0x6d, 0xbd, 0x4c, 0xc9, 0x60, 0xee, 0x90, 0xbd, - 0x6f, 0xdc, 0x92, 0x63, 0x00, 0x46, 0x43, 0xca, 0xaf, 0x0e, 0xbf, 0x38, - 0x9d, 0xf4, 0x00, 0x30, 0x0a, 0xab, 0x66, 0x1b, 0x4d, 0x0c, 0x62, 0x33, - 0x47, 0x7c, 0x2c, 0x37, 0x37, 0xd7, 0xd4, 0x3c, 0x67, 0x3c, 0xd5, 0x7f, - 0x2e, 0x13, 0xbf, 0x5d, 0xb9, 0xf8, 0x6a, 0x4a, 0x73, 0xa2, 0xb0, 0xa1, - 0x0e, 0x2b, 0x72, 0x55, 0xcc, 0xfb, 0xbc, 0x2c, 0xe3, 0x60, 0xc6, 0x98, - 0x52, 0x56, 0xa8, 0x16, 0xcf, 0xa0, 0x50, 0x21, 0x63, 0xa9, 0x60, 0xe0, - 0xff, 0xf1, 0x50, 0x80, 0x2f, 0x5f, 0xfc, 0x21, 0x1a, 0xcf, 0xbf, 0xf4, - 0x7d, 0x84, 0x03, 0x9e, 0xb6, 0xad, 0x90, 0x4c, 0x60, 0xef, 0xa0, 0x06, - 0xf5, 0x5d, 0x7a, 0xd0, 0x9f, 0x1f, 0x3d, 0x75, 0x7f, 0xb7, 0xe2, 0xbc, - 0xfb, 0xf1, 0xcf, 0xdc, 0x54, 0x19, 0x68, 0x93, 0xc5, 0xaa, 0x3d, 0x7f, - 0xa3, 0x67, 0xf2, 0x37, 0xf6, 0x68, 0xf3, 0x45, 0x68, 0xfc, 0xe9, 0xe6, - 0x3b, 0xd8, 0x1b, 0x0a, 0x0c, 0x6d, 0xe7, 0xb9, 0xcb, 0xc2, 0x35, 0x8a, - 0x30, 0x2d, 0x10, 0x58, 0x64, 0xcd, 0x5b, 0x46, 0x9d, 0xb3, 0xfa, 0x7f, - 0x3b, 0xf7, 0xde, 0x3f, 0x44, 0x37, 0xc2, 0xb9, 0xf7, 0x13, 0xae, 0x37, - 0x11, 0x19, 0xf3, 0xf7, 0x58, 0xa8, 0xea, 0xf0, 0xcc, 0x56, 0x26, 0x5c, - 0x39, 0xf2, 0xdf, 0x9c, 0xbb, 0xfa, 0x55, 0x85, 0x7f, 0x3d, 0xcc, 0xab, - 0x10, 0x17, 0x89, 0xb9, 0x99, 0xeb, 0xcd, 0xaa, 0xbb, 0xf1, 0xbe, 0xe3, - 0xf7, 0xd9, 0x6a, 0x04, 0xc2, 0x7b, 0x74, 0x06, 0x75, 0x96, 0xfb, 0xb5, - 0xd9, 0x5a, 0x5b, 0xa7, 0x87, 0xae, 0x6f, 0x3d, 0x5a, 0xa9, 0xdb, 0xbe, - 0x78, 0x32, 0x03, 0xb3, 0xc4, 0x05, 0x2f, 0xcb, 0xe3, 0xc0, 0xad, 0x4d, - 0x4d, 0x5a, 0xe4, 0x3d, 0x97, 0x22, 0x64, 0x75, 0xfc, 0x37, 0x71, 0x15, - 0xfd, 0xe3, 0xb3, 0x57, 0x1c, 0x7a, 0x09, 0x79, 0xfc, 0x81, 0x4b, 0x3f, - 0xe4, 0x97, 0x05, 0x1b, 0xa7, 0x5f, 0x97, 0x4a, 0xfa, 0xcb, 0xb1, 0xcb, - 0x35, 0xbe, 0x7d, 0x15, 0xc7, 0x7d, 0xb8, 0x77, 0x53, 0x64, 0x4f, 0x53, - 0x84, 0xdb, 0x24, 0xd3, 0x35, 0xef, 0x82, 0x56, 0x73, 0x04, 0xb6, 0x6d, - 0x55, 0xa2, 0xb1, 0x68, 0x96, 0xc3, 0x29, 0xe6, 0xef, 0xbe, 0xeb, 0x35, - 0xfa, 0x9a, 0x68, 0xb6, 0xac, 0x40, 0x9e, 0x97, 0x31, 0x88, 0x27, 0x8e, - 0x16, 0xee, 0x22, 0x2b, 0x78, 0x08, 0xd9, 0xe8, 0xef, 0xed, 0x8b, 0xe7, - 0xed, 0x71, 0x50, 0x18, 0x2e, 0x85, 0x6c, 0xf4, 0x32, 0xc3, 0x47, 0x74, - 0x7b, 0xc0, 0x00, 0x00, 0x25, 0x3a, 0x8a, 0x95, 0x25, 0x64, 0x9c, 0xc4, - 0x09, 0x76, 0xde, 0xc4, 0x7b, 0xdc, 0x82, 0xba, 0xd9, 0x5d, 0xab, 0xa0, - 0x74, 0x01, 0xba, 0x48, 0x94, 0x58, 0x0b, 0xb7, 0x96, 0xe3, 0x53, 0x33, - 0x71, 0xd4, 0x49, 0x88, 0xf3, 0x07, 0x08, 0x8c, 0x82, 0x37, 0x6b, 0x9d, - 0xda, 0xf9, 0xdc, 0x82, 0xf8, 0x8f, 0x94, 0x74, 0xda, 0x60, 0x05, 0x3f, - 0x15, 0x55, 0x89, 0x03, 0x05, 0x21, 0xb2, 0x77, 0x31, 0x8a, 0x0c, 0xe9, - 0xb8, 0xe0, 0x0a, 0x4a, 0x09, 0x15, 0xa4, 0xba, 0x57, 0x55, 0xf4, 0x62, - 0x56, 0x9a, 0x82, 0x30, 0x26, 0xb0, 0x2a, 0x40, 0xa7, 0x02, 0x65, 0x92, - 0xc6, 0x4d, 0xc3, 0x38, 0xa0, 0x70, 0xff, 0xf1, 0x50, 0x80, 0x2e, 0x3f, - 0xfc, 0x21, 0x1a, 0xcf, 0xbd, 0xf3, 0x73, 0x9f, 0xbf, 0x9f, 0xb6, 0xc1, - 0x56, 0x6c, 0x37, 0x0b, 0x08, 0x39, 0xe0, 0x4a, 0x25, 0x73, 0xa9, 0x3b, - 0xe0, 0xdf, 0xc3, 0xbf, 0x39, 0xf5, 0xfb, 0x7e, 0xad, 0x77, 0xc7, 0xcf, - 0xe2, 0x40, 0x2d, 0x00, 0xd4, 0x91, 0x27, 0xaa, 0xd4, 0x6b, 0x3d, 0xa9, - 0x9c, 0xb5, 0xd1, 0x8e, 0xa5, 0x48, 0xf4, 0xf2, 0x15, 0x18, 0x62, 0x26, - 0x33, 0x17, 0x9e, 0xbf, 0x94, 0x29, 0xac, 0x9d, 0xcd, 0x8f, 0x3c, 0xb2, - 0x08, 0x47, 0x5d, 0x0c, 0x32, 0x80, 0x52, 0x90, 0x2c, 0xa3, 0x3a, 0x1b, - 0xe7, 0x12, 0x4f, 0x38, 0x34, 0xd6, 0xb5, 0xea, 0xe5, 0x7d, 0x92, 0xaf, - 0xd1, 0x10, 0x43, 0xbf, 0x67, 0x3d, 0xa9, 0x1c, 0xce, 0xe9, 0xaf, 0x29, - 0x54, 0x9c, 0x3d, 0xa1, 0x52, 0x27, 0xa7, 0x12, 0x8d, 0x36, 0x10, 0x07, - 0xbc, 0x5a, 0x45, 0x56, 0xf2, 0x77, 0x55, 0x00, 0x33, 0x4e, 0x81, 0x64, - 0x7a, 0x66, 0x23, 0x68, 0x1b, 0x9f, 0x48, 0x05, 0xf5, 0x4c, 0x81, 0x18, - 0xc6, 0xfe, 0x9e, 0x8a, 0x5f, 0x7a, 0x91, 0x53, 0x28, 0xaa, 0xeb, 0xf7, - 0x63, 0xa5, 0x0d, 0x38, 0x2f, 0x5d, 0x5c, 0xab, 0x2d, 0xf6, 0x4e, 0xa7, - 0x7d, 0xde, 0x8f, 0x04, 0xa7, 0xd3, 0xc2, 0x28, 0x40, 0x79, 0x7d, 0xd5, - 0xcf, 0x7b, 0xad, 0x55, 0x35, 0xbd, 0xf5, 0x68, 0x77, 0xe6, 0x8b, 0xd0, - 0x2f, 0x09, 0x27, 0x16, 0xdd, 0x11, 0x2a, 0x65, 0x25, 0x64, 0x45, 0xdd, - 0xec, 0xb7, 0x51, 0x0c, 0x4d, 0x06, 0x60, 0x11, 0x3c, 0xcb, 0xa2, 0x22, - 0x47, 0x46, 0x7b, 0x7b, 0xf4, 0x6f, 0x9e, 0x9c, 0xfb, 0x0d, 0x3b, 0xe2, - 0x6b, 0xe1, 0x7a, 0x38, 0x43, 0x66, 0xba, 0x93, 0x73, 0x57, 0x9e, 0x1f, - 0xac, 0xa6, 0x7b, 0x1c, 0x07, 0xfc, 0x9b, 0x19, 0x9a, 0x5a, 0x52, 0xe9, - 0x72, 0x8a, 0x61, 0xf5, 0xdd, 0x3f, 0x65, 0x63, 0xd8, 0x5c, 0xd6, 0x17, - 0x47, 0x3c, 0x00, 0x12, 0x84, 0xa0, 0xbd, 0x77, 0xe7, 0x7d, 0x31, 0x2f, - 0x9c, 0x97, 0x55, 0x00, 0x4e, 0x9c, 0x76, 0x2c, 0x98, 0xac, 0x71, 0x8e, - 0x43, 0x83, 0x04, 0x21, 0x5c, 0xca, 0x92, 0xfc, 0x54, 0x29, 0x22, 0x27, - 0xa1, 0x8d, 0x86, 0xb5, 0x87, 0x1d, 0x98, 0xb7, 0xce, 0x15, 0xe7, 0xfa, - 0xfe, 0xf3, 0xbf, 0xcb, 0x09, 0x9a, 0xca, 0x2a, 0xbb, 0x0d, 0x2a, 0x68, - 0x55, 0x2b, 0xf2, 0xf2, 0x66, 0xd9, 0x60, 0x6e, 0xd1, 0x88, 0x89, 0x42, - 0x00, 0xc3, 0x6a, 0xea, 0x2b, 0xd6, 0xe1, 0xac, 0xd1, 0x88, 0x17, 0x9d, - 0xdc, 0x52, 0x22, 0xe1, 0x84, 0x2c, 0x9e, 0x14, 0x14, 0x0b, 0x50, 0xd5, - 0x9c, 0xc1, 0xc0, 0xff, 0xf1, 0x50, 0x80, 0x2d, 0x7f, 0xfc, 0x21, 0x1a, - 0xcb, 0xfb, 0xbe, 0x79, 0x3f, 0x3f, 0xa0, 0xb6, 0xb9, 0xd6, 0x2c, 0x60, - 0x05, 0xe1, 0x29, 0x1a, 0xe6, 0x15, 0x7c, 0x78, 0xfa, 0xf5, 0xfa, 0xfe, - 0xdf, 0xcd, 0xcf, 0xb7, 0x5e, 0xbf, 0x75, 0xde, 0xf4, 0x3b, 0x28, 0x93, - 0x5f, 0x32, 0x0b, 0xea, 0x3d, 0x07, 0xba, 0x96, 0x33, 0xfb, 0xe2, 0x95, - 0x46, 0xfa, 0x5c, 0xf2, 0x71, 0x55, 0x9b, 0xe8, 0xb7, 0x18, 0xe8, 0x99, - 0x1f, 0x04, 0xf1, 0xda, 0x1d, 0x26, 0xe3, 0x24, 0xd8, 0x18, 0x83, 0x1d, - 0x95, 0xa5, 0xa7, 0xa1, 0x7b, 0x7d, 0x7e, 0xa3, 0x9d, 0xa9, 0x17, 0x5b, - 0xb2, 0xbc, 0xb9, 0xb3, 0x55, 0xcd, 0xc7, 0x7f, 0x23, 0x18, 0xdf, 0x29, - 0x5d, 0xe6, 0x6e, 0x39, 0xc2, 0x39, 0x3e, 0x52, 0xa8, 0x35, 0x81, 0x71, - 0x22, 0x78, 0xc0, 0xbe, 0xa2, 0xcd, 0x5a, 0xc7, 0x88, 0xb0, 0x5e, 0xbb, - 0x26, 0x19, 0xac, 0x22, 0x40, 0x9a, 0xdc, 0x01, 0xae, 0x71, 0x3d, 0x2a, - 0xb8, 0x7a, 0xf1, 0x5c, 0x63, 0x60, 0xbe, 0x7d, 0x30, 0x02, 0x2f, 0x5f, - 0x7f, 0xd3, 0xb2, 0xba, 0x51, 0x38, 0xc5, 0xef, 0x53, 0x55, 0xfd, 0xfc, - 0x22, 0x0c, 0x22, 0x71, 0x7f, 0x3f, 0x15, 0x6e, 0x2b, 0xfd, 0x2e, 0x7b, - 0x73, 0x3c, 0x6c, 0x47, 0xb0, 0x00, 0x7a, 0xfa, 0x6d, 0x73, 0xbd, 0x93, - 0xad, 0x6e, 0x69, 0xbe, 0xbc, 0xc0, 0xaa, 0x04, 0x9f, 0x82, 0xe6, 0x72, - 0x59, 0x45, 0xd1, 0x4c, 0xa8, 0x78, 0x34, 0xbf, 0xe4, 0xda, 0xee, 0x41, - 0x85, 0x51, 0xb0, 0xbb, 0x50, 0xe6, 0x9a, 0x73, 0xa0, 0xef, 0xe5, 0x94, - 0xfa, 0xf5, 0x53, 0x7d, 0x3a, 0xfa, 0xa7, 0xf4, 0x7b, 0x6b, 0xd1, 0x34, - 0x51, 0x81, 0x3d, 0x31, 0xb7, 0x3b, 0xae, 0x3d, 0x96, 0x52, 0xbd, 0x2b, - 0x6b, 0x69, 0xce, 0xae, 0xea, 0xee, 0x7b, 0xaa, 0x79, 0x20, 0x7b, 0x3d, - 0x9b, 0x67, 0xf8, 0x51, 0x4a, 0xd0, 0x58, 0x99, 0x56, 0x19, 0x7f, 0x3c, - 0x01, 0x78, 0x4a, 0x45, 0x21, 0x5e, 0xde, 0x8c, 0xd6, 0xf8, 0xb6, 0x6c, - 0x19, 0x63, 0x63, 0x12, 0x33, 0x31, 0x16, 0x9c, 0xbc, 0xb5, 0x76, 0xe5, - 0x5a, 0x29, 0x03, 0xdb, 0xcb, 0xe0, 0x92, 0xb8, 0x20, 0x91, 0xed, 0x6e, - 0xcb, 0xbf, 0xd1, 0x72, 0xa8, 0x41, 0xcb, 0x2c, 0xee, 0xae, 0xb1, 0xaf, - 0x27, 0x0c, 0xf6, 0x30, 0xe7, 0xdd, 0x47, 0x0b, 0x85, 0x31, 0xc0, 0x6f, - 0xfb, 0xdf, 0x82, 0x9e, 0x1c, 0xc1, 0x36, 0x9a, 0x58, 0x03, 0x0c, 0x88, - 0x61, 0xd0, 0xc8, 0xab, 0x55, 0x64, 0xce, 0x72, 0x92, 0x4a, 0x28, 0x2f, - 0x9e, 0x14, 0x0b, 0x50, 0xb0, 0x1c, 0xff, 0xf1, 0x50, 0x80, 0x31, 0x1f, - 0xfc, 0x21, 0x1a, 0xcd, 0xf7, 0xfc, 0x75, 0x84, 0x80, 0x9d, 0xb6, 0xc1, - 0x10, 0x4e, 0x55, 0x6b, 0x19, 0xcf, 0x00, 0x00, 0x4a, 0xe7, 0xaa, 0x73, - 0xed, 0xaf, 0x1f, 0x1e, 0xbf, 0x1f, 0xa7, 0xf9, 0xf1, 0xd7, 0xbf, 0x9f, - 0x1f, 0x79, 0x50, 0x12, 0x78, 0x88, 0x8e, 0x1c, 0xca, 0x3d, 0xf9, 0x2d, - 0x03, 0xee, 0x79, 0xb7, 0x9d, 0x6f, 0x3e, 0x3c, 0x7a, 0xb2, 0x59, 0xaa, - 0x1a, 0xab, 0x03, 0x76, 0x23, 0x7d, 0x9e, 0x9a, 0x28, 0x3e, 0xcb, 0x53, - 0xca, 0x2f, 0xbe, 0x51, 0xbe, 0x48, 0xc5, 0x66, 0x97, 0x65, 0xc7, 0x5c, - 0x09, 0x23, 0x08, 0xfa, 0xfe, 0xc3, 0xe2, 0xde, 0x09, 0x82, 0x13, 0xa2, - 0x66, 0x2e, 0x21, 0xf3, 0x34, 0xae, 0x4a, 0x9c, 0xb4, 0xf1, 0x56, 0x50, - 0xb9, 0xdf, 0xb5, 0xaf, 0xb3, 0x12, 0x28, 0xcf, 0xf3, 0xa9, 0x24, 0xc2, - 0x4c, 0xd0, 0xe7, 0x30, 0x45, 0xf6, 0x2a, 0x76, 0xfd, 0x73, 0x25, 0x82, - 0x71, 0x6a, 0x76, 0xf0, 0x50, 0x02, 0xbb, 0x28, 0x0b, 0xe7, 0x9c, 0xf6, - 0xe0, 0x73, 0xee, 0x94, 0x72, 0xd8, 0x1e, 0xda, 0x00, 0x77, 0xfd, 0xbc, - 0x3a, 0x7f, 0x2a, 0xae, 0x78, 0xd5, 0x5a, 0x73, 0x75, 0x8d, 0xff, 0x6a, - 0x91, 0x6a, 0x1f, 0xbf, 0x97, 0x0b, 0x8a, 0xcb, 0xf2, 0xf6, 0x73, 0xba, - 0xb7, 0x1a, 0xbb, 0xf8, 0x10, 0x11, 0x44, 0x7a, 0x5d, 0x97, 0x8e, 0xf8, - 0xbd, 0xeb, 0x6c, 0x74, 0xd5, 0x47, 0xc0, 0x98, 0x9c, 0xed, 0x64, 0x4c, - 0x5a, 0x71, 0xa2, 0xe9, 0x77, 0x2f, 0x69, 0xf8, 0x32, 0x35, 0x92, 0x7e, - 0xe7, 0x96, 0x75, 0x4f, 0x37, 0x70, 0xd1, 0x5d, 0x97, 0xd2, 0x21, 0x5e, - 0xee, 0xc6, 0x34, 0xb3, 0x29, 0x46, 0x1d, 0x47, 0xc9, 0x5c, 0xef, 0xd5, - 0x45, 0xd3, 0x5e, 0xb8, 0x4e, 0xbb, 0x31, 0xac, 0xf8, 0xc9, 0x56, 0x0c, - 0xb2, 0x54, 0xdd, 0x94, 0xdf, 0xea, 0xea, 0x37, 0x1a, 0x30, 0xa1, 0xeb, - 0xc1, 0x5b, 0x1d, 0xfe, 0x68, 0xae, 0xcb, 0xed, 0x9d, 0xba, 0x2a, 0x3d, - 0xe0, 0x00, 0x01, 0x28, 0xd6, 0xdf, 0x1c, 0xfb, 0x73, 0xc7, 0x3c, 0x73, - 0x0b, 0xef, 0x48, 0x10, 0xe4, 0x59, 0x50, 0x79, 0xd0, 0x6a, 0xd8, 0xe5, - 0xaf, 0x4e, 0x58, 0x6f, 0x18, 0xcc, 0x01, 0x6b, 0x47, 0x88, 0x5b, 0x27, - 0x85, 0xc0, 0xa6, 0x50, 0x18, 0xc4, 0xa8, 0xc5, 0x47, 0x7e, 0xdf, 0x40, - 0x10, 0x0a, 0x29, 0x2a, 0xf3, 0xcf, 0x46, 0x95, 0x1f, 0x16, 0x6b, 0x35, - 0xc0, 0x96, 0x36, 0x55, 0x40, 0xb5, 0xb5, 0x94, 0x0b, 0x06, 0xd2, 0xe4, - 0x3e, 0x33, 0xcb, 0xca, 0x4d, 0xa2, 0xc6, 0x2e, 0x85, 0x85, 0x45, 0x95, - 0x9d, 0x1f, 0x2b, 0xf2, 0xf6, 0xf2, 0xd1, 0xce, 0x54, 0x22, 0xd6, 0x9b, - 0x12, 0xed, 0x17, 0xbf, 0xe1, 0x42, 0xa4, 0x35, 0x30, 0x47, 0x46, 0x36, - 0x17, 0xc0, 0xff, 0xf1, 0x50, 0x80, 0x2d, 0x1f, 0xfc, 0x21, 0x1a, 0xcd, - 0x7f, 0xd4, 0xbd, 0x00, 0x40, 0x9f, 0xb1, 0x52, 0xdc, 0xea, 0xb6, 0x12, - 0x91, 0x8c, 0xa9, 0x50, 0x02, 0x55, 0x48, 0xae, 0xb9, 0xa7, 0xc6, 0xfb, - 0xff, 0x1d, 0xfc, 0x7f, 0x4f, 0xf6, 0x95, 0xe3, 0xcf, 0xaf, 0xaa, 0x8a, - 0x83, 0xf9, 0xfd, 0x6f, 0x62, 0x4a, 0x43, 0xba, 0xea, 0xb6, 0xaf, 0x6e, - 0xa4, 0xe5, 0x66, 0x6b, 0x40, 0x2b, 0x7b, 0xc5, 0x01, 0x55, 0x9a, 0x50, - 0xc3, 0xba, 0x29, 0x07, 0x5b, 0xa4, 0x37, 0xee, 0xb7, 0x0c, 0xeb, 0x15, - 0x91, 0x75, 0xa7, 0xcf, 0x79, 0xf4, 0xc4, 0xb5, 0xac, 0x86, 0x2c, 0xf1, - 0x33, 0x65, 0xf9, 0xde, 0xa3, 0x0d, 0xa0, 0xe9, 0xc4, 0x69, 0x0b, 0x8a, - 0xe0, 0xe7, 0x29, 0x2a, 0x1d, 0x38, 0x81, 0x51, 0x0a, 0x5e, 0x72, 0xb5, - 0xa1, 0x50, 0xd8, 0xa7, 0xb0, 0x2c, 0x1d, 0x39, 0xec, 0x2b, 0xa7, 0xb3, - 0x55, 0x44, 0xd8, 0xe5, 0xdd, 0x58, 0x12, 0x8c, 0x79, 0x57, 0x46, 0xfa, - 0x2d, 0x70, 0x3c, 0xa7, 0xb4, 0x1d, 0xf2, 0x00, 0xa7, 0x29, 0x2b, 0xcf, - 0xb5, 0x20, 0x94, 0x88, 0x5c, 0xe6, 0xb0, 0xdf, 0x84, 0xdd, 0x75, 0xaa, - 0x04, 0x8f, 0x48, 0xce, 0xea, 0xb4, 0x73, 0xdd, 0x7f, 0x3d, 0xc7, 0x5a, - 0x99, 0xaf, 0x4c, 0x50, 0x26, 0x50, 0xce, 0x7f, 0x9f, 0x47, 0x2f, 0x92, - 0x1c, 0x71, 0x75, 0xd7, 0xc3, 0x7d, 0x3d, 0x5e, 0x11, 0x80, 0xc0, 0xc6, - 0x57, 0x01, 0xbe, 0xfe, 0x84, 0x5d, 0x28, 0x7a, 0x0c, 0x8b, 0xb6, 0x77, - 0xf7, 0x90, 0xee, 0x76, 0x8e, 0x2d, 0x43, 0x56, 0x21, 0x35, 0x48, 0x81, - 0x31, 0xa8, 0x78, 0x13, 0x49, 0xd7, 0xba, 0xee, 0x69, 0x7b, 0x0b, 0x53, - 0x01, 0x6f, 0x2d, 0x7f, 0x69, 0x7b, 0x46, 0xe3, 0x61, 0xa5, 0x2c, 0x69, - 0xa6, 0x22, 0x97, 0x58, 0x76, 0xe9, 0xa8, 0xa8, 0x08, 0x44, 0x25, 0xfe, - 0xe4, 0xdf, 0x11, 0xd0, 0x1a, 0x7e, 0xc9, 0x30, 0x34, 0x7b, 0xc0, 0x00, - 0x01, 0x29, 0xf1, 0xee, 0xd5, 0x75, 0xbb, 0xae, 0x3b, 0x9c, 0xea, 0x97, - 0xb8, 0x0d, 0x91, 0xda, 0x6a, 0x01, 0x9e, 0x77, 0x2b, 0xf9, 0xf5, 0xf3, - 0xf1, 0xc0, 0xd6, 0xf3, 0xfc, 0x67, 0x74, 0xc6, 0x3c, 0xf5, 0xba, 0xbc, - 0x75, 0x7a, 0xbf, 0xbd, 0x6c, 0xe3, 0x17, 0xab, 0x86, 0x86, 0xe9, 0xcf, - 0xe3, 0xb0, 0xb8, 0x3a, 0x38, 0xac, 0xfc, 0xdf, 0x02, 0x31, 0xe8, 0x67, - 0x3e, 0xaf, 0xa3, 0xdf, 0x06, 0x97, 0xcf, 0xd4, 0x0d, 0x14, 0x3f, 0xf8, - 0x0b, 0x70, 0x62, 0xe0, 0xaa, 0x8d, 0x2d, 0xd9, 0xf6, 0xb5, 0x50, 0xa9, - 0xe5, 0x07, 0xff, 0xf1, 0x50, 0x80, 0x2f, 0xff, 0xfc, 0x21, 0x1a, 0xcf, - 0xb5, 0xec, 0x74, 0x00, 0x80, 0x9a, 0xb6, 0xcb, 0x8c, 0x8c, 0x25, 0x1b, - 0x0a, 0x02, 0xc4, 0x00, 0x00, 0x00, 0x5e, 0x7c, 0x7b, 0xfc, 0x7b, 0xff, - 0x84, 0xe7, 0xd7, 0xe2, 0xbe, 0x3d, 0xd9, 0xc0, 0xe3, 0x5f, 0xc8, 0xec, - 0x1b, 0xa8, 0x33, 0x21, 0xeb, 0x42, 0x4d, 0x72, 0x1a, 0xa4, 0x1b, 0x30, - 0x6e, 0x2d, 0x6a, 0x7d, 0xa1, 0x87, 0x94, 0x1b, 0x93, 0x98, 0x4a, 0x82, - 0xb5, 0x5d, 0x25, 0x1c, 0x64, 0xee, 0xf6, 0xeb, 0xd3, 0x8e, 0xa7, 0x87, - 0xd9, 0xca, 0xb6, 0x75, 0xd1, 0xbe, 0xf8, 0x3c, 0x05, 0x6f, 0xfd, 0xe7, - 0x41, 0xca, 0x98, 0xbe, 0x25, 0x2b, 0x47, 0x29, 0x6c, 0xbd, 0x5f, 0xc2, - 0xe5, 0xe3, 0x34, 0xc2, 0x71, 0xee, 0x0b, 0xd5, 0x6c, 0x5b, 0x53, 0x6c, - 0x73, 0xcd, 0xb0, 0xa9, 0xcb, 0xe2, 0x7b, 0xa6, 0x78, 0x8d, 0x00, 0x33, - 0x5a, 0x85, 0x2a, 0xc5, 0x75, 0x57, 0x12, 0x20, 0x34, 0x7b, 0x17, 0xa2, - 0xaa, 0x66, 0xd5, 0x07, 0x7f, 0x1f, 0x02, 0xef, 0x03, 0x31, 0xed, 0x7a, - 0xf0, 0x3a, 0x66, 0xae, 0xce, 0xa7, 0x2a, 0xcc, 0x1c, 0x98, 0x2f, 0xc5, - 0xe6, 0x54, 0x4d, 0x0a, 0x86, 0xe7, 0x0a, 0x14, 0xac, 0xb6, 0xaf, 0xfd, - 0x3b, 0x6f, 0x78, 0xbb, 0xae, 0x3e, 0x11, 0x6a, 0xc2, 0x4b, 0xc5, 0x4a, - 0x80, 0x00, 0xee, 0xc2, 0x44, 0xd0, 0xd2, 0xbc, 0x53, 0x58, 0x3c, 0x98, - 0xb5, 0xfb, 0xde, 0xe7, 0x58, 0x7a, 0xa4, 0xbe, 0xca, 0x98, 0xd2, 0x37, - 0xed, 0x84, 0x23, 0xd1, 0xf0, 0xa8, 0xed, 0xb0, 0xcf, 0x1d, 0x51, 0x24, - 0x12, 0x28, 0x15, 0x83, 0x0e, 0x99, 0xe0, 0x6c, 0xc6, 0xe4, 0x11, 0x3a, - 0x02, 0xdd, 0x71, 0x75, 0xec, 0xb6, 0x88, 0x52, 0x35, 0x62, 0x80, 0x53, - 0x00, 0x3b, 0x00, 0x40, 0xca, 0xf5, 0xf1, 0xd8, 0xb2, 0x98, 0x1c, 0x55, - 0x52, 0x5f, 0xa1, 0xc2, 0x6a, 0xe8, 0xa8, 0xf7, 0x86, 0x5b, 0x2f, 0x2c, - 0x65, 0xb0, 0xb3, 0x2f, 0x0c, 0xe3, 0x9e, 0x33, 0x8d, 0xd4, 0xad, 0x73, - 0x10, 0x1d, 0xcb, 0xbc, 0xfd, 0x8c, 0x5f, 0x06, 0xab, 0x72, 0x4d, 0xfb, - 0xac, 0x0f, 0x3d, 0x88, 0x5b, 0xe2, 0x65, 0x02, 0xf5, 0xf5, 0xfc, 0x80, - 0x21, 0xf8, 0x4a, 0xb8, 0xe6, 0xc1, 0x5b, 0x70, 0x8e, 0x84, 0x22, 0xd4, - 0xc3, 0xca, 0x34, 0xb2, 0xd1, 0x43, 0xdb, 0xd3, 0x94, 0xf0, 0x98, 0x52, - 0xb8, 0x32, 0x78, 0x06, 0x4a, 0xe5, 0xa8, 0x1b, 0xb9, 0x58, 0xca, 0x19, - 0xe8, 0xb2, 0xb9, 0x97, 0x60, 0xa4, 0x3c, 0x05, 0x43, 0x8f, 0xde, 0x50, - 0x12, 0xa5, 0x6f, 0x81, 0xd8, 0xf3, 0x76, 0x0e, 0x32, 0xa9, 0x55, 0x52, - 0x97, 0x26, 0x21, 0x90, 0x9c, 0xf6, 0x8e, 0x70, 0x18, 0x07, 0x39, 0x03, - 0x80, 0xff, 0xf1, 0x50, 0x80, 0x30, 0x3f, 0xfc, 0x21, 0x1a, 0xcf, 0x76, - 0x85, 0x3c, 0x3f, 0xff, 0x9a, 0xb6, 0xba, 0xcc, 0xaa, 0x86, 0x24, 0x08, - 0x00, 0x00, 0x02, 0xf8, 0xf1, 0xf1, 0xef, 0xfa, 0xfc, 0xfe, 0xfc, 0xd6, - 0x75, 0x9a, 0xeb, 0xbb, 0xf3, 0xd8, 0x8e, 0x09, 0xbe, 0x3e, 0x41, 0x19, - 0x30, 0x32, 0x5d, 0x27, 0x12, 0xbc, 0x6b, 0x01, 0x71, 0x5c, 0xbc, 0x38, - 0xf7, 0x47, 0x24, 0x15, 0xa1, 0x0a, 0xfb, 0xfb, 0xde, 0x49, 0x53, 0x87, - 0x42, 0x93, 0x8a, 0xc7, 0x5b, 0x83, 0x36, 0xe4, 0x00, 0xdf, 0x5e, 0x13, - 0x16, 0xd2, 0xa8, 0xcf, 0x42, 0x75, 0x77, 0xed, 0xdf, 0xf2, 0x55, 0xd8, - 0x67, 0x59, 0xed, 0x8c, 0x67, 0x8d, 0xce, 0x71, 0xe6, 0xbd, 0x15, 0x44, - 0xac, 0xdd, 0xd7, 0x8c, 0x33, 0x63, 0x87, 0x44, 0xcf, 0x17, 0x6d, 0xeb, - 0x61, 0x0c, 0xe3, 0x8b, 0xeb, 0x68, 0x40, 0x2b, 0x28, 0xba, 0x43, 0x91, - 0x7d, 0x3d, 0xe7, 0xf9, 0x74, 0x68, 0x1d, 0x57, 0x40, 0x7c, 0x72, 0x15, - 0x1d, 0x4d, 0xc6, 0xb9, 0xf6, 0x69, 0x0c, 0x2a, 0x21, 0x9c, 0x01, 0x51, - 0x98, 0xc0, 0x04, 0x66, 0x3f, 0x18, 0x9e, 0xfb, 0x3b, 0x66, 0x71, 0x17, - 0xbe, 0xee, 0x12, 0x62, 0xee, 0x75, 0x77, 0xab, 0x9a, 0xc5, 0xd1, 0xdb, - 0x10, 0x6e, 0xb8, 0xb9, 0xee, 0x35, 0x09, 0x0b, 0x9a, 0x7a, 0x6b, 0x2c, - 0x0e, 0x15, 0x4c, 0xf7, 0x6f, 0xb3, 0xb2, 0xaf, 0xd5, 0x4b, 0xd6, 0x8c, - 0xc0, 0x78, 0x14, 0xba, 0x30, 0x90, 0x0e, 0x73, 0xcc, 0xa0, 0x9a, 0x3b, - 0xe7, 0xbb, 0xe3, 0x33, 0x79, 0xf7, 0x4b, 0x78, 0x91, 0xfa, 0xd1, 0x02, - 0x2a, 0x5a, 0xf0, 0xb4, 0xc7, 0x82, 0xd0, 0xe6, 0x12, 0x91, 0x45, 0xdd, - 0x78, 0xae, 0xf9, 0x01, 0xc0, 0xfa, 0xaa, 0x34, 0x65, 0xde, 0x64, 0x2f, - 0xc0, 0x67, 0xb2, 0xf4, 0x70, 0xea, 0xe7, 0xd6, 0xa2, 0x41, 0xd2, 0x9d, - 0x52, 0xdb, 0x94, 0x62, 0xdf, 0x9c, 0x21, 0x8e, 0x6a, 0xdb, 0x06, 0x92, - 0x99, 0x39, 0xe3, 0x7a, 0x37, 0xa6, 0xf5, 0xb1, 0xad, 0xeb, 0x6d, 0x17, - 0x5c, 0xdd, 0x71, 0x93, 0x7a, 0xdd, 0xe4, 0x94, 0xab, 0x0c, 0x79, 0x5c, - 0x6a, 0xc6, 0xac, 0xc4, 0x6a, 0xcf, 0x69, 0x73, 0x5f, 0xce, 0xa8, 0x03, - 0x0d, 0x78, 0xe7, 0x14, 0xf0, 0x93, 0x53, 0xe9, 0xa4, 0xa7, 0xc8, 0xe2, - 0x5c, 0x07, 0xbe, 0xb8, 0xa1, 0x80, 0xc8, 0x8f, 0x9c, 0x73, 0xad, 0x14, - 0x34, 0x28, 0xb1, 0x23, 0x25, 0xe5, 0x15, 0xc4, 0x62, 0x53, 0x7d, 0x22, - 0x3b, 0x89, 0x80, 0x5c, 0xa0, 0x1f, 0xf5, 0x0f, 0x88, 0xe2, 0x29, 0x31, - 0x87, 0x7d, 0x17, 0x01, 0x25, 0xd2, 0xb3, 0xcd, 0x51, 0x7c, 0x5e, 0xed, - 0x62, 0xcb, 0x68, 0xe2, 0xa4, 0xd7, 0x9c, 0x1e, 0x17, 0x8e, 0xf6, 0x60, - 0xd9, 0x1c, 0xff, 0xf1, 0x50, 0x80, 0x2e, 0x9f, 0xfc, 0x21, 0x1a, 0xcf, - 0xb9, 0xfd, 0x3f, 0x80, 0x00, 0x9b, 0xb6, 0xb1, 0x64, 0xc6, 0x55, 0x43, - 0x0a, 0x02, 0xc2, 0x80, 0x80, 0x00, 0x00, 0x69, 0xbf, 0x6f, 0x5f, 0xe9, - 0xfd, 0x3f, 0x5b, 0xe3, 0xfb, 0x7e, 0x39, 0xf8, 0x9d, 0x7a, 0x7b, 0x0c, - 0xf0, 0x44, 0x31, 0xe8, 0xb0, 0xda, 0x6f, 0xc7, 0x72, 0x88, 0x95, 0xee, - 0xa3, 0xeb, 0x4c, 0x7a, 0x17, 0x1e, 0x8d, 0x4e, 0xac, 0xfb, 0xf7, 0xa8, - 0xd0, 0x08, 0xb2, 0xc6, 0xcb, 0x4e, 0x34, 0x1a, 0x7c, 0x67, 0x37, 0xf8, - 0xfa, 0x19, 0x79, 0xd8, 0xdb, 0x88, 0x42, 0xbb, 0xf7, 0x96, 0x9b, 0x39, - 0x01, 0xfe, 0xa7, 0x31, 0x80, 0x56, 0x4e, 0x61, 0xd0, 0x4e, 0x5a, 0x1f, - 0x8c, 0x60, 0x52, 0xef, 0xae, 0xc9, 0x7b, 0x0b, 0x8d, 0xd9, 0xb1, 0xdb, - 0x7c, 0x0d, 0x02, 0xd7, 0xd3, 0xf8, 0x08, 0x2e, 0x21, 0x75, 0x12, 0x8d, - 0x05, 0xbc, 0xa2, 0xa5, 0x74, 0x1e, 0x88, 0x0d, 0x81, 0x37, 0xd8, 0xde, - 0xd3, 0x5b, 0xd7, 0xee, 0xe5, 0xdf, 0xba, 0x01, 0xb8, 0x80, 0x16, 0xdf, - 0xae, 0x15, 0x8d, 0x54, 0x5a, 0xf1, 0x9d, 0xf5, 0x95, 0xf4, 0xe9, 0xbc, - 0xd6, 0x1d, 0x3a, 0xb9, 0xc7, 0x66, 0xf3, 0x88, 0xfe, 0x7d, 0x34, 0xbc, - 0xbd, 0x40, 0xd0, 0x01, 0xa8, 0x89, 0x6d, 0xbc, 0xcc, 0xa9, 0xae, 0x83, - 0x3c, 0x7a, 0xd4, 0x9c, 0x92, 0x55, 0x86, 0x63, 0x41, 0xd0, 0x36, 0xd8, - 0x2d, 0x95, 0xd4, 0xf3, 0x67, 0xf1, 0xec, 0xd8, 0xd2, 0x41, 0x80, 0x48, - 0x05, 0x7a, 0x4f, 0xaf, 0xc3, 0x55, 0x86, 0xa9, 0x67, 0x66, 0x09, 0x6a, - 0x61, 0xed, 0x9d, 0xfd, 0xac, 0xce, 0x03, 0x64, 0x80, 0x8b, 0x0b, 0x20, - 0x5d, 0x0c, 0xf3, 0xd4, 0x6a, 0x75, 0xa1, 0x3c, 0xa0, 0x1d, 0x28, 0x50, - 0xd5, 0x3e, 0x20, 0x92, 0x5c, 0xae, 0x02, 0xa7, 0x2c, 0x29, 0xca, 0x9b, - 0xb6, 0xb1, 0x24, 0xe6, 0x4f, 0x78, 0x65, 0x98, 0xb0, 0x32, 0xf1, 0x66, - 0x9b, 0xf6, 0xce, 0xb9, 0x93, 0x5d, 0xa6, 0x42, 0xac, 0x30, 0xfd, 0x97, - 0x69, 0x2d, 0xa0, 0x54, 0x3d, 0x7d, 0xd2, 0x9d, 0x92, 0x7d, 0x19, 0x68, - 0x96, 0x6a, 0xd3, 0xc6, 0xb2, 0x8f, 0x45, 0x29, 0x0d, 0xfc, 0x13, 0x35, - 0x4d, 0x15, 0x54, 0x48, 0x81, 0x5b, 0x2e, 0x70, 0x42, 0xcb, 0x2d, 0x71, - 0xb2, 0x30, 0x32, 0x8f, 0x4b, 0xee, 0xef, 0x2d, 0xa1, 0xf7, 0x6e, 0x6c, - 0x3b, 0xdc, 0x71, 0xf9, 0x57, 0xca, 0xe7, 0x14, 0xe3, 0x8e, 0xed, 0x78, - 0x24, 0x49, 0x23, 0x6c, 0xaa, 0x98, 0xfa, 0x5e, 0x52, 0x33, 0x46, 0x7c, - 0xc7, 0x48, 0xc3, 0x8f, 0x29, 0xa7, 0xea, 0xcd, 0x02, 0x9c, 0xe7, 0x93, - 0xc0, 0x70, 0xff, 0xf1, 0x50, 0x80, 0x32, 0x5f, 0xfc, 0x21, 0x1a, 0xcf, - 0xf9, 0xe8, 0x7e, 0xbf, 0xff, 0x99, 0xb6, 0xb1, 0x65, 0x26, 0x25, 0x43, - 0x0a, 0x08, 0x01, 0x9c, 0x60, 0xcb, 0xc1, 0x60, 0xce, 0x39, 0xfd, 0xff, - 0x6f, 0xbe, 0xa7, 0xed, 0xfc, 0xff, 0x1f, 0xcb, 0xdb, 0xc5, 0xf0, 0x2f, - 0xaa, 0x81, 0x64, 0x94, 0x6e, 0x35, 0xa1, 0x93, 0xf6, 0xce, 0x77, 0xce, - 0xef, 0xe3, 0x5c, 0x19, 0x2d, 0x1d, 0x84, 0x81, 0xc2, 0x93, 0x64, 0x22, - 0x7c, 0x2e, 0x29, 0xd7, 0x34, 0x92, 0x87, 0x07, 0x65, 0xb8, 0x0c, 0x95, - 0x9d, 0x41, 0x30, 0x10, 0xaa, 0x9d, 0x1d, 0x99, 0x83, 0x1d, 0x5c, 0x2e, - 0xd3, 0xcf, 0xe2, 0xa1, 0x4d, 0x42, 0x66, 0x2c, 0x9b, 0xe4, 0xdc, 0xae, - 0x9b, 0xf6, 0x7c, 0x62, 0xaf, 0x74, 0x6b, 0xf1, 0x8c, 0x74, 0x35, 0x63, - 0x17, 0x3b, 0x0c, 0x30, 0xe6, 0xee, 0xe4, 0xec, 0x4a, 0x63, 0xf3, 0x7f, - 0x7c, 0x85, 0xe3, 0x9c, 0x86, 0x10, 0x2a, 0x64, 0x54, 0x58, 0x6f, 0xf1, - 0x60, 0x80, 0xd0, 0x63, 0x03, 0xe6, 0x04, 0x75, 0x78, 0x5f, 0x1a, 0xda, - 0x2f, 0x7d, 0xbd, 0x0c, 0xd2, 0x82, 0xba, 0xfa, 0x00, 0x66, 0xaf, 0xab, - 0xe1, 0x54, 0xbb, 0xdc, 0x5c, 0xc5, 0xc5, 0xf3, 0xd2, 0x3f, 0x9f, 0x2a, - 0x52, 0xf0, 0x5c, 0x5f, 0x64, 0x46, 0xb1, 0xfa, 0xfa, 0x75, 0x57, 0x78, - 0xe0, 0x1a, 0xe2, 0x00, 0x7f, 0x7c, 0x74, 0x74, 0xca, 0xed, 0xc6, 0x31, - 0x7a, 0xcd, 0x0a, 0xe4, 0xa0, 0x06, 0xc7, 0x16, 0xa5, 0xe3, 0x04, 0xa4, - 0xe2, 0x14, 0xb3, 0xb8, 0x7a, 0xb8, 0x09, 0x41, 0x4c, 0x54, 0xd8, 0x74, - 0x9a, 0x64, 0x49, 0xa7, 0x50, 0x0a, 0x23, 0x02, 0x06, 0xd8, 0x44, 0xd4, - 0x7e, 0xa3, 0x3a, 0x59, 0x6e, 0x72, 0x4a, 0x41, 0x01, 0x8d, 0x4e, 0x5e, - 0x05, 0x96, 0x1c, 0x31, 0x60, 0x2c, 0x94, 0xce, 0x70, 0x45, 0x3b, 0xf3, - 0x05, 0xb0, 0xc9, 0xae, 0x0e, 0xdf, 0x46, 0x98, 0x9a, 0xb8, 0xef, 0xad, - 0xb8, 0xc4, 0xbd, 0x59, 0x33, 0x74, 0x60, 0xa0, 0xa4, 0xbc, 0xf0, 0x00, - 0x00, 0x01, 0x7d, 0xfb, 0x55, 0x6b, 0x32, 0x1b, 0xb5, 0x40, 0xde, 0xd2, - 0xde, 0x64, 0x30, 0xee, 0xa8, 0xf2, 0xf0, 0x90, 0xac, 0x21, 0x14, 0x0a, - 0x35, 0x5a, 0x18, 0xe2, 0x2e, 0x38, 0xab, 0x57, 0x14, 0xba, 0x3e, 0x40, - 0x8f, 0x33, 0x8a, 0xee, 0xb0, 0x32, 0x00, 0x25, 0xa3, 0xcd, 0x45, 0x5c, - 0x85, 0xe6, 0x1f, 0xf8, 0xec, 0x4a, 0xc5, 0xbb, 0x57, 0x22, 0xa3, 0x29, - 0x33, 0xc6, 0xed, 0xab, 0x8f, 0xac, 0xf4, 0xfe, 0xd8, 0x3a, 0x17, 0xfa, - 0xaa, 0x34, 0x36, 0xb3, 0xe6, 0xa9, 0x0a, 0x31, 0xd7, 0x74, 0xa2, 0x5a, - 0x5d, 0xf1, 0x4f, 0x42, 0x8a, 0x34, 0x93, 0x9e, 0xbf, 0xf6, 0xfe, 0x9a, - 0x62, 0xab, 0x0c, 0xe0, 0x34, 0x41, 0x98, 0x99, 0x0d, 0xd9, 0xf8, 0x3d, - 0x13, 0x9f, 0x49, 0x2c, 0x83, 0x75, 0x50, 0x38, 0xff, 0xf1, 0x50, 0x80, - 0x32, 0x3f, 0xfc, 0x21, 0x1a, 0xcf, 0xf2, 0x72, 0xd6, 0xbf, 0xbf, 0x98, - 0xb6, 0xc1, 0xac, 0x2c, 0x3b, 0x0b, 0xb5, 0x8b, 0x41, 0x00, 0x00, 0x00, - 0x07, 0xb7, 0xa9, 0xd5, 0xfb, 0xf1, 0xcf, 0xd4, 0xbf, 0x1f, 0x5f, 0x03, - 0x23, 0x76, 0xc1, 0x23, 0x94, 0x81, 0x63, 0x50, 0x46, 0xc8, 0x46, 0xe2, - 0x15, 0x83, 0x26, 0x60, 0x49, 0xee, 0x84, 0xf7, 0x8a, 0x76, 0xbc, 0xc9, - 0xe8, 0xf0, 0xa6, 0xec, 0x95, 0x34, 0x2d, 0x76, 0x9c, 0x79, 0xbf, 0x08, - 0xa8, 0x83, 0x0c, 0xad, 0x91, 0xb0, 0x89, 0x60, 0x4b, 0x42, 0xb1, 0xe3, - 0x47, 0x83, 0x15, 0xb1, 0x24, 0xf3, 0xfa, 0x75, 0x10, 0x91, 0x21, 0x0d, - 0xc9, 0x69, 0x93, 0xec, 0x8d, 0xb5, 0x61, 0x7d, 0x1f, 0x37, 0xf5, 0xa7, - 0x92, 0x8a, 0x2d, 0x8e, 0xbd, 0x20, 0xe1, 0x83, 0x29, 0x19, 0xea, 0x3d, - 0xb3, 0x1f, 0xfa, 0xe0, 0x23, 0x0e, 0xce, 0xe9, 0x29, 0xf1, 0xcc, 0x34, - 0xb6, 0x55, 0x55, 0x0c, 0xb3, 0x0b, 0x91, 0x94, 0x99, 0xab, 0x8b, 0xff, - 0xc6, 0x95, 0x98, 0x64, 0x24, 0x82, 0x14, 0x04, 0x5d, 0xc5, 0x74, 0x62, - 0x16, 0x9c, 0x65, 0x79, 0xc2, 0xa6, 0x36, 0xcd, 0x41, 0x49, 0x33, 0xf9, - 0xc0, 0x62, 0x17, 0x84, 0x0c, 0xe0, 0x92, 0x97, 0x47, 0x06, 0x09, 0x16, - 0xc6, 0x22, 0xec, 0xc3, 0x93, 0x17, 0x95, 0x5d, 0xeb, 0x0b, 0xe3, 0xc0, - 0x00, 0xdb, 0x86, 0x55, 0x30, 0xab, 0x81, 0x30, 0x47, 0x04, 0x2e, 0x54, - 0x56, 0x44, 0x72, 0x48, 0xd5, 0x0c, 0x00, 0xa2, 0x30, 0x80, 0x97, 0x2b, - 0xd0, 0x98, 0xd5, 0x95, 0x0d, 0x4e, 0x70, 0x48, 0x28, 0xd0, 0xc3, 0x5c, - 0x2d, 0x25, 0x47, 0x81, 0xdb, 0x62, 0x00, 0x1b, 0x83, 0x90, 0x80, 0x10, - 0x82, 0x1d, 0xb4, 0xea, 0x43, 0x9a, 0xc0, 0xb7, 0x08, 0xd7, 0x4f, 0xca, - 0x79, 0x1c, 0x65, 0xb7, 0x34, 0xcd, 0xde, 0x13, 0x2e, 0x1b, 0x00, 0x24, - 0xe3, 0xe9, 0x34, 0xb4, 0xd3, 0x16, 0xd8, 0x34, 0xa7, 0x9e, 0x03, 0x6d, - 0x6d, 0xa3, 0x6d, 0x6f, 0x4d, 0xeb, 0x7a, 0x6f, 0x5e, 0x3e, 0xbb, 0xbb, - 0x66, 0x56, 0x69, 0x9a, 0x80, 0xfd, 0xcd, 0xec, 0x6f, 0xe4, 0xf0, 0xb3, - 0xbb, 0x89, 0xc3, 0x99, 0x4d, 0x7b, 0x1c, 0xc6, 0x9d, 0xa4, 0xd0, 0xac, - 0xef, 0x10, 0xf9, 0xc4, 0xef, 0x86, 0x98, 0xcb, 0xe8, 0xaa, 0x8d, 0x3f, - 0x8c, 0xc9, 0x26, 0x5f, 0x24, 0x38, 0x67, 0x61, 0x3e, 0xd2, 0xc6, 0xc5, - 0x66, 0xc6, 0xaf, 0x16, 0xa3, 0xbe, 0xa8, 0xeb, 0x56, 0xa8, 0xaf, 0x9b, - 0xf1, 0x28, 0xd8, 0xa7, 0x87, 0xe6, 0xb5, 0xf4, 0x35, 0xa9, 0x51, 0x5c, - 0x5c, 0x30, 0xcb, 0x93, 0x57, 0x9c, 0x65, 0x70, 0x34, 0x74, 0xb6, 0xe5, - 0x0d, 0x7f, 0xb7, 0xf9, 0x12, 0xf1, 0xac, 0xaa, 0x0a, 0xc6, 0x45, 0x64, - 0xb5, 0xe1, 0x42, 0xb0, 0xf0, 0x32, 0xbd, 0xf3, 0x20, 0xb1, 0x9a, 0x41, - 0xc0, 0xff, 0xf1, 0x50, 0x80, 0x29, 0xdf, 0xfc, 0x21, 0x1a, 0xcb, 0x62, - 0x31, 0xfc, 0x80, 0x00, 0x9d, 0xb6, 0xa1, 0xa5, 0x06, 0xa6, 0x30, 0x00, - 0x05, 0xe0, 0x4a, 0x7c, 0x7b, 0xf9, 0xf1, 0xfb, 0xfe, 0xdf, 0x8e, 0x7c, - 0xf3, 0xc7, 0xaf, 0xd6, 0xf1, 0x76, 0x1d, 0xd4, 0x00, 0x6d, 0x70, 0x4e, - 0x83, 0xbd, 0x3b, 0xfd, 0xfb, 0xf5, 0x6d, 0x03, 0xdb, 0x1a, 0xa6, 0x23, - 0x8f, 0x46, 0xbb, 0x93, 0x46, 0xec, 0x43, 0x0e, 0x79, 0xe8, 0xa4, 0x29, - 0xe9, 0xf7, 0xce, 0xe7, 0xc5, 0x48, 0xb6, 0x1b, 0x76, 0x8d, 0x5f, 0x8e, - 0x76, 0x14, 0x4d, 0xfb, 0x29, 0xa0, 0xa5, 0x02, 0xee, 0x79, 0x7e, 0x9f, - 0xdc, 0x2c, 0x56, 0x31, 0x80, 0xc8, 0x05, 0x08, 0x11, 0x8f, 0x49, 0x42, - 0xac, 0x91, 0x21, 0x90, 0x8c, 0xad, 0x17, 0xa6, 0xcf, 0xd0, 0x02, 0x83, - 0x94, 0xa4, 0x01, 0xe8, 0xc7, 0x33, 0xfa, 0xee, 0x8f, 0xd9, 0xf0, 0xeb, - 0x71, 0xfd, 0xfe, 0x3f, 0x3a, 0x17, 0xf6, 0xda, 0xab, 0x69, 0x5b, 0x5e, - 0x8e, 0xfc, 0x12, 0x36, 0x29, 0xb3, 0xd7, 0x7a, 0x08, 0x78, 0x7a, 0x2f, - 0x7f, 0xe8, 0xb1, 0x30, 0x9f, 0xde, 0x3e, 0xbb, 0x83, 0x6e, 0xfc, 0x5b, - 0xa8, 0xeb, 0xe2, 0x11, 0x59, 0x1a, 0x67, 0x09, 0xab, 0xa5, 0xae, 0x61, - 0x75, 0x5a, 0xeb, 0xb3, 0x46, 0x34, 0xc3, 0xd8, 0x70, 0x35, 0x43, 0x97, - 0x6c, 0x18, 0x4a, 0x01, 0x0f, 0x0f, 0x24, 0x5e, 0x87, 0xe5, 0xd2, 0x44, - 0x94, 0x81, 0xa1, 0xce, 0x84, 0xa5, 0x38, 0xd4, 0xf5, 0x13, 0x4a, 0xb7, - 0xd9, 0x95, 0x85, 0xb5, 0x67, 0x36, 0x93, 0x28, 0xb3, 0xcd, 0x76, 0x39, - 0x89, 0x94, 0x34, 0xcb, 0x3b, 0x6d, 0x98, 0x7b, 0x80, 0x00, 0x00, 0x94, - 0x3c, 0xf1, 0x31, 0xbd, 0x5e, 0xcb, 0xaa, 0xd5, 0x07, 0x16, 0xc9, 0x75, - 0xdf, 0xc1, 0x76, 0x49, 0xba, 0x88, 0x53, 0x90, 0x0c, 0x29, 0xe9, 0xbf, - 0xa9, 0x4d, 0x7f, 0x1a, 0x75, 0x78, 0xf0, 0xb6, 0xc1, 0x4d, 0x7b, 0xce, - 0x35, 0xea, 0x7e, 0x0b, 0x29, 0x86, 0xaf, 0x85, 0xb9, 0x58, 0xf3, 0x73, - 0xc6, 0xba, 0x8d, 0xad, 0x6e, 0x16, 0x5d, 0x17, 0x79, 0x1b, 0xf7, 0x7a, - 0x66, 0x12, 0xd9, 0x2d, 0x6b, 0x99, 0x24, 0x09, 0x0a, 0x03, 0x20, 0x7a, - 0x56, 0x08, 0x20, 0x14, 0x08, 0x06, 0x50, 0x35, 0x3a, 0x8c, 0x45, 0xc8, - 0x2e, 0x06, 0x39, 0xc0, 0x12, 0x55, 0xeb, 0x66, 0xba, 0x83, 0x80, 0xff, - 0xf1, 0x50, 0x80, 0x31, 0x3f, 0xfc, 0x21, 0x1a, 0xc8, 0xb3, 0xfd, 0xbc, - 0x00, 0x40, 0x9c, 0xb6, 0xb1, 0xa4, 0x2a, 0xd6, 0x09, 0x85, 0x82, 0x61, - 0x83, 0x09, 0x51, 0x48, 0x02, 0xa3, 0x8e, 0x4b, 0xeb, 0xdf, 0xef, 0xf6, - 0xfe, 0x7f, 0x4f, 0xf4, 0xf7, 0xfa, 0xfb, 0x7b, 0x7a, 0xfd, 0x66, 0xf8, - 0x04, 0x97, 0x28, 0x4a, 0x73, 0xf9, 0x15, 0x0a, 0x0e, 0x69, 0xd0, 0xf5, - 0x1a, 0xdc, 0xff, 0xf9, 0xc8, 0x34, 0xc8, 0x25, 0x3e, 0xe0, 0x72, 0xba, - 0x9c, 0xd7, 0xf1, 0xc7, 0x85, 0x83, 0x24, 0xbb, 0xc5, 0xb7, 0xf5, 0x0a, - 0x38, 0x80, 0x97, 0x49, 0x10, 0x57, 0x74, 0x2b, 0xad, 0x5d, 0x9f, 0x2a, - 0x11, 0x67, 0x38, 0x32, 0x05, 0x8e, 0x02, 0x15, 0x95, 0xbf, 0xb0, 0xe1, - 0x10, 0x77, 0xb3, 0x5b, 0xbb, 0xb2, 0xd7, 0x35, 0x52, 0xc5, 0x07, 0x77, - 0xcf, 0x91, 0x14, 0x95, 0x99, 0x81, 0x18, 0x99, 0xb9, 0x74, 0x5d, 0xcc, - 0xcf, 0xf6, 0xf1, 0x90, 0x91, 0x7d, 0x57, 0x26, 0x6d, 0x94, 0x55, 0x85, - 0x6e, 0x00, 0x62, 0xbb, 0x38, 0x66, 0x8a, 0xce, 0xa6, 0x77, 0xcb, 0xe2, - 0x0b, 0x99, 0x80, 0x19, 0xfb, 0x7d, 0x7f, 0x3e, 0xef, 0x41, 0xbb, 0xe1, - 0x9d, 0xaa, 0xab, 0x51, 0xaf, 0x7e, 0xa0, 0x94, 0x03, 0x4b, 0x5c, 0x8d, - 0x2d, 0x67, 0xeb, 0xaa, 0x23, 0xbe, 0x54, 0x0d, 0xc7, 0xfd, 0x17, 0x30, - 0xba, 0xa3, 0x24, 0xcb, 0x78, 0x3a, 0x5b, 0x7e, 0x2b, 0x73, 0x59, 0x1f, - 0xd9, 0x8c, 0x94, 0xbd, 0x9c, 0x46, 0x59, 0x69, 0xac, 0x10, 0xc5, 0x26, - 0x58, 0xa7, 0x0c, 0x9c, 0x11, 0x46, 0x94, 0x08, 0x4c, 0x96, 0x78, 0xa4, - 0x4e, 0x97, 0x0a, 0xc0, 0x92, 0x41, 0x0a, 0xa2, 0xc0, 0x75, 0xaa, 0x05, - 0x88, 0x56, 0x17, 0xd7, 0x09, 0x22, 0x03, 0x4d, 0x55, 0x0b, 0x7d, 0x16, - 0x06, 0x5a, 0x29, 0x47, 0x65, 0xa1, 0xd2, 0x4d, 0x34, 0x4b, 0x95, 0x26, - 0x44, 0x0d, 0xc8, 0x8c, 0x28, 0x22, 0xe1, 0xc1, 0xbf, 0x18, 0x05, 0xb8, - 0x20, 0x9e, 0x5d, 0x29, 0x2c, 0x4d, 0xdb, 0x5e, 0x1e, 0xe0, 0x00, 0xd6, - 0xc0, 0x0b, 0x65, 0xfd, 0x4a, 0xbe, 0xf8, 0xaa, 0x8a, 0x5e, 0x58, 0xe0, - 0xf5, 0xa3, 0xbe, 0x97, 0x21, 0x4b, 0x22, 0xfc, 0x0b, 0x28, 0xa4, 0x57, - 0xec, 0xe5, 0x0a, 0x0c, 0xa9, 0x6b, 0x06, 0x3b, 0x83, 0x1d, 0x5c, 0x2a, - 0x0d, 0x38, 0x17, 0x2f, 0x4c, 0x71, 0xb7, 0xc3, 0xac, 0xf8, 0x82, 0x98, - 0x47, 0x23, 0x7e, 0xb6, 0x75, 0xaf, 0xda, 0xe0, 0xca, 0x1d, 0x4f, 0xa7, - 0xc3, 0x57, 0x4b, 0xee, 0xf0, 0xa6, 0x72, 0x87, 0x4f, 0xe1, 0xca, 0x93, - 0x86, 0xa7, 0x0f, 0x29, 0xc7, 0x20, 0x03, 0x29, 0x17, 0xc7, 0xfb, 0xde, - 0x08, 0x58, 0x54, 0xc0, 0xb0, 0x5c, 0x06, 0x5c, 0x3d, 0xa2, 0xe4, 0x00, - 0xde, 0x01, 0x14, 0xd1, 0xe2, 0xe1, 0x01, 0x0e, 0xff, 0xf1, 0x50, 0x80, - 0x2f, 0x7f, 0xfc, 0x21, 0x1a, 0xcf, 0xf6, 0xed, 0x6b, 0x00, 0x00, 0x9c, - 0xb6, 0xb9, 0xcd, 0x2a, 0x46, 0x0a, 0x0d, 0x8c, 0x04, 0xa0, 0x08, 0xa5, - 0xd6, 0xb9, 0xfb, 0xcf, 0xcf, 0xf3, 0xfb, 0x7f, 0x9f, 0xeb, 0xfc, 0xfa, - 0xfd, 0x7d, 0xed, 0xc6, 0xab, 0x2e, 0x07, 0x8a, 0xc8, 0x1f, 0x7d, 0xad, - 0xc5, 0xa9, 0x27, 0xc0, 0x31, 0x5d, 0x80, 0xbd, 0xbc, 0xc3, 0x41, 0x9e, - 0x84, 0x57, 0xc6, 0x4d, 0xad, 0xf9, 0x00, 0xc7, 0xf0, 0xeb, 0x80, 0x0c, - 0x41, 0x72, 0x47, 0xdb, 0x4b, 0xbb, 0x3d, 0xbe, 0x90, 0xa6, 0x96, 0x38, - 0xe8, 0xec, 0x57, 0xad, 0x8e, 0x73, 0x5c, 0xec, 0xcb, 0xc7, 0x56, 0xcd, - 0x46, 0xff, 0x71, 0xe4, 0x15, 0xa5, 0xbe, 0x70, 0x34, 0xaa, 0xea, 0x07, - 0xc7, 0xe0, 0x7e, 0x16, 0xdf, 0x33, 0x8e, 0x58, 0xe1, 0xbb, 0xe9, 0x6e, - 0x0f, 0xf0, 0x95, 0x3d, 0x7f, 0x2c, 0xf3, 0x5a, 0x3a, 0xff, 0x38, 0x7b, - 0x81, 0x8f, 0x7f, 0xdd, 0xc1, 0xb8, 0x77, 0x4b, 0x96, 0xfb, 0xba, 0x38, - 0x03, 0x92, 0x00, 0x33, 0xf7, 0x23, 0x5f, 0x07, 0x29, 0xbe, 0xc5, 0xe6, - 0xe8, 0x7b, 0x6f, 0x13, 0x2c, 0x82, 0xf6, 0xb6, 0xe6, 0x49, 0xb7, 0x63, - 0xe4, 0x9a, 0xbb, 0x5a, 0x1e, 0x7e, 0x8c, 0x65, 0x9c, 0x69, 0x93, 0x77, - 0xa2, 0x2f, 0x47, 0x4c, 0xf4, 0x4a, 0x7f, 0xc1, 0x1b, 0x7b, 0xe9, 0xbd, - 0x8e, 0xed, 0xce, 0x91, 0x9b, 0x7b, 0xa4, 0xa2, 0x00, 0x00, 0x2e, 0xda, - 0xf8, 0x47, 0xd6, 0xfd, 0xca, 0xe5, 0xdf, 0xe3, 0xb1, 0xea, 0x83, 0xcf, - 0x8b, 0x89, 0xfc, 0x8a, 0xa9, 0x04, 0xf8, 0xc0, 0x34, 0x7b, 0xab, 0x98, - 0x0e, 0x19, 0xb6, 0x3c, 0xa8, 0x76, 0x5d, 0x6d, 0xf3, 0x9d, 0xde, 0xe0, - 0xa2, 0x6c, 0x36, 0x71, 0x4c, 0x70, 0xb7, 0x5c, 0xb3, 0xec, 0x75, 0xf0, - 0x5f, 0x98, 0x97, 0xd7, 0x12, 0x2b, 0x47, 0x32, 0xee, 0x8c, 0xa7, 0x2d, - 0xb2, 0xb3, 0x2f, 0xb8, 0x09, 0x40, 0x12, 0x8b, 0xaa, 0x6b, 0xe3, 0xf3, - 0xed, 0xe6, 0xf7, 0x3b, 0xf6, 0x56, 0xec, 0xab, 0x80, 0x6b, 0x2b, 0x30, - 0x33, 0x5e, 0xbc, 0xec, 0x64, 0xa8, 0xf8, 0x5e, 0x05, 0x9a, 0xa1, 0xd0, - 0x65, 0x16, 0x52, 0x54, 0xe6, 0xe9, 0x5b, 0xf8, 0xc3, 0xa9, 0x9a, 0xa3, - 0xc7, 0xf3, 0x39, 0xa9, 0x39, 0x5d, 0x5e, 0x5d, 0x77, 0x3a, 0x37, 0xf1, - 0x36, 0x2b, 0x65, 0xcf, 0x4b, 0xa7, 0x86, 0x9f, 0xb6, 0xd5, 0x37, 0xaf, - 0xd3, 0xbd, 0x46, 0x30, 0x0c, 0x34, 0x60, 0xb0, 0x15, 0x0d, 0x38, 0x11, - 0xd0, 0xf8, 0xb0, 0xb1, 0xef, 0x8f, 0x27, 0xc7, 0xce, 0xf0, 0x1b, 0xae, - 0x66, 0x36, 0x81, 0x86, 0x8f, 0x51, 0xa9, 0x0c, 0xdf, 0xdb, 0xfe, 0x61, - 0xc7, 0xca, 0xe0, 0xff, 0xf1, 0x50, 0x80, 0x30, 0xff, 0xfc, 0x21, 0x1a, - 0xcf, 0xf6, 0xa0, 0x9e, 0x00, 0x00, 0x97, 0xb8, 0x30, 0xe4, 0x2b, 0x16, - 0x30, 0x00, 0x00, 0x03, 0xae, 0xfd, 0xbd, 0x7e, 0x3f, 0x3f, 0xbe, 0xfc, - 0xf8, 0xce, 0x38, 0xf3, 0xea, 0xb5, 0xa1, 0xfa, 0x8a, 0xd8, 0x57, 0x6a, - 0xe7, 0xd7, 0xcb, 0xe1, 0xac, 0x8b, 0xd6, 0x84, 0x9a, 0xd9, 0x70, 0x38, - 0xf9, 0x9a, 0x63, 0x98, 0x4f, 0xc5, 0x1f, 0x9f, 0xbe, 0x92, 0xbb, 0x36, - 0x03, 0x65, 0x0e, 0xaa, 0xf6, 0x33, 0xb9, 0xbd, 0x51, 0x06, 0xac, 0xc7, - 0xa0, 0xae, 0xd0, 0x48, 0x69, 0xe0, 0x6d, 0x60, 0xf8, 0x7b, 0x7a, 0x58, - 0x14, 0xc5, 0x4c, 0xc9, 0x64, 0x08, 0xbe, 0xcf, 0x5d, 0xb7, 0xdf, 0x55, - 0x63, 0x66, 0x22, 0x28, 0x4d, 0xf9, 0xaf, 0x9e, 0xe6, 0xb8, 0x8c, 0xf9, - 0xf6, 0xed, 0x99, 0xa5, 0xcd, 0xc5, 0xf3, 0x9e, 0xaf, 0xba, 0x56, 0xbf, - 0x8f, 0x00, 0x00, 0x81, 0x21, 0x0c, 0xc1, 0x9e, 0xcf, 0xd5, 0x22, 0xe4, - 0x26, 0x38, 0x51, 0xc6, 0xc0, 0x0e, 0xee, 0xa8, 0x00, 0xe3, 0x15, 0xdd, - 0x8c, 0xdb, 0x11, 0xba, 0xbe, 0x72, 0x19, 0xc6, 0xa0, 0x03, 0x18, 0xe5, - 0x5d, 0x59, 0x4c, 0x5e, 0x75, 0x73, 0xdf, 0x2d, 0xd7, 0x0a, 0xdf, 0x2a, - 0x4a, 0x94, 0xab, 0xd5, 0x5d, 0xce, 0x1f, 0xb2, 0xad, 0x9b, 0xc7, 0x7c, - 0xfc, 0xed, 0x32, 0xc1, 0x48, 0x85, 0xe9, 0x7b, 0x42, 0x99, 0xb9, 0x46, - 0x3b, 0xba, 0xab, 0xa2, 0x29, 0x35, 0x46, 0x73, 0x17, 0x9b, 0xa2, 0xf3, - 0x44, 0x66, 0x2a, 0xc0, 0x96, 0xeb, 0xce, 0xcc, 0x9a, 0x7e, 0x5a, 0x42, - 0xda, 0xc8, 0x64, 0x31, 0xf0, 0xc4, 0x85, 0x1c, 0x59, 0x50, 0xc2, 0xb9, - 0x3b, 0x41, 0x0a, 0xf4, 0xc0, 0x40, 0xd9, 0x2b, 0x13, 0x36, 0x25, 0x98, - 0x7a, 0xd0, 0xe8, 0x50, 0x04, 0xee, 0x95, 0x2b, 0x42, 0x13, 0x99, 0xa9, - 0x9b, 0x01, 0x43, 0xb7, 0x0b, 0xe8, 0xc3, 0x5f, 0x19, 0x7b, 0x6c, 0x19, - 0xca, 0x64, 0x40, 0xfb, 0x80, 0x77, 0xd3, 0xbe, 0xbb, 0xeb, 0xb0, 0xeb, - 0xb7, 0x47, 0x5f, 0x6e, 0x3f, 0x13, 0x7a, 0xad, 0x57, 0x36, 0xbd, 0xd6, - 0xa8, 0x1d, 0xc2, 0x39, 0xa3, 0xa3, 0x55, 0xef, 0x91, 0xfc, 0xdd, 0x1e, - 0x5e, 0x54, 0x10, 0x52, 0xe0, 0xb1, 0x03, 0xab, 0x4a, 0x0b, 0x23, 0x9d, - 0xb8, 0x75, 0x3a, 0xe5, 0xc9, 0x55, 0xb4, 0x8d, 0x44, 0x92, 0x67, 0x8b, - 0xf6, 0xb9, 0x81, 0x68, 0x49, 0xa3, 0x29, 0x45, 0x6a, 0xba, 0x55, 0xce, - 0x5d, 0xf5, 0x4e, 0x90, 0x78, 0x0f, 0x89, 0x5c, 0x77, 0x53, 0x0e, 0x0d, - 0x5c, 0x33, 0x94, 0xd2, 0xf9, 0x04, 0xc5, 0x0b, 0x91, 0x57, 0x22, 0x7e, - 0x17, 0x07, 0x94, 0x29, 0x25, 0x22, 0xe9, 0x0a, 0xcd, 0xa1, 0xf6, 0x95, - 0x69, 0x83, 0x10, 0x4b, 0x8e, 0xbb, 0x6e, 0x28, 0x26, 0xe0, 0xff, 0xf1, - 0x50, 0x80, 0x35, 0x9f, 0xfc, 0x21, 0x1a, 0xcf, 0xed, 0xb2, 0xab, 0x94, - 0x40, 0x93, 0xba, 0x31, 0x1c, 0xa6, 0x62, 0x0a, 0x96, 0x04, 0x05, 0x45, - 0x45, 0x45, 0x25, 0x4a, 0x95, 0x2a, 0x26, 0x75, 0xdf, 0xdf, 0xbe, 0x3e, - 0x7e, 0x3d, 0xff, 0x59, 0x75, 0xf4, 0x13, 0xf2, 0x49, 0x3a, 0x95, 0xd9, - 0x7b, 0x3a, 0x2a, 0xb3, 0x93, 0x5b, 0xdd, 0xa8, 0x41, 0x6e, 0xe4, 0x39, - 0xec, 0x8c, 0x59, 0x5f, 0x2f, 0x93, 0x85, 0x0c, 0x6b, 0xd1, 0x7d, 0x7b, - 0xa5, 0x3b, 0xfe, 0x17, 0x0e, 0x4a, 0xcf, 0xbb, 0x68, 0x72, 0x4d, 0xf0, - 0x9f, 0xcf, 0xf6, 0xcd, 0xa4, 0xfe, 0x85, 0x96, 0x21, 0x4c, 0xe0, 0x6a, - 0x79, 0x8b, 0x95, 0x56, 0xde, 0x0f, 0xd5, 0x24, 0x9c, 0x70, 0x09, 0x92, - 0x86, 0x33, 0x41, 0x09, 0xb5, 0xba, 0xba, 0x48, 0x45, 0xe4, 0xcb, 0x28, - 0x4d, 0x3a, 0x38, 0x42, 0xa8, 0x62, 0x3e, 0x90, 0x9a, 0x6e, 0xe0, 0xfb, - 0xff, 0xa8, 0x91, 0x5c, 0xc2, 0x42, 0x53, 0x86, 0xe5, 0xb4, 0xcc, 0x08, - 0x57, 0xb3, 0xfe, 0xf4, 0x51, 0x02, 0x7c, 0xe5, 0x85, 0x61, 0xce, 0x15, - 0x84, 0x25, 0x61, 0x3d, 0x1f, 0xeb, 0xe1, 0x9b, 0xe6, 0x41, 0x8c, 0x9b, - 0xee, 0x81, 0x65, 0xe3, 0x3f, 0x03, 0x4e, 0x4a, 0xc1, 0xc1, 0xa5, 0xd0, - 0x6b, 0x5c, 0x15, 0xb0, 0xfa, 0xed, 0x80, 0xeb, 0xbe, 0xc0, 0x94, 0x99, - 0xcc, 0xfe, 0x20, 0x4f, 0x16, 0xf1, 0xf7, 0x6c, 0x18, 0x5a, 0xfb, 0x10, - 0x0f, 0x65, 0xe3, 0x9b, 0x4e, 0xd4, 0x7b, 0x15, 0xd1, 0xa1, 0x20, 0x37, - 0x33, 0x77, 0xbe, 0x73, 0xaa, 0xdf, 0x59, 0x39, 0xcf, 0xbf, 0xe3, 0xd3, - 0xc7, 0x6b, 0xa9, 0xc1, 0xa3, 0x9e, 0x9d, 0xb6, 0x9d, 0x62, 0x27, 0x78, - 0xab, 0xfc, 0x3f, 0xc7, 0xc6, 0x66, 0x69, 0x05, 0xe6, 0xe5, 0x03, 0x9f, - 0x0d, 0x18, 0xd3, 0x22, 0x50, 0x08, 0x5a, 0x90, 0xd5, 0xdc, 0x50, 0x65, - 0x53, 0xb1, 0x3b, 0xe1, 0x8e, 0xf5, 0xe3, 0x48, 0x32, 0x68, 0x10, 0x29, - 0x3b, 0xac, 0x9f, 0xc8, 0x21, 0xf5, 0x00, 0x00, 0x00, 0x09, 0xeb, 0xe3, - 0x2f, 0x55, 0x89, 0x37, 0xbe, 0xa6, 0x06, 0x53, 0x38, 0x28, 0x14, 0xca, - 0x81, 0x5f, 0x9e, 0x13, 0x7f, 0xc3, 0x25, 0x7e, 0xa5, 0x6d, 0x2d, 0x30, - 0xfb, 0xc4, 0x71, 0x38, 0x5b, 0x7b, 0x96, 0x77, 0x32, 0x71, 0xb4, 0x95, - 0x08, 0x24, 0x2e, 0x0c, 0x64, 0xd6, 0xd2, 0x97, 0xc6, 0x75, 0x17, 0x34, - 0x94, 0xa5, 0x53, 0x69, 0x1d, 0x27, 0x1d, 0x51, 0xfd, 0xec, 0x56, 0xa7, - 0xe4, 0x6b, 0x1d, 0x35, 0x53, 0x1d, 0x7b, 0xa8, 0x98, 0x9b, 0x4b, 0xc4, - 0xd3, 0xcf, 0xf2, 0x2d, 0x30, 0x70, 0x23, 0x42, 0xde, 0x74, 0x8f, 0x41, - 0x02, 0x62, 0xc6, 0x42, 0xa8, 0xa1, 0xa9, 0x55, 0x11, 0x44, 0xc0, 0x45, - 0x76, 0xbc, 0x5e, 0xa7, 0xea, 0x1e, 0x81, 0x9e, 0xda, 0xf4, 0x8f, 0xc1, - 0xe7, 0x35, 0x86, 0xed, 0x2d, 0xda, 0x9b, 0x97, 0xaf, 0x50, 0x5c, 0xc5, - 0x17, 0x94, 0x97, 0xaf, 0xbb, 0xe4, 0xb0, 0xad, 0x6d, 0x94, 0x29, 0x80, - 0xdd, 0x56, 0x17, 0x53, 0xee, 0x1c, 0xff, 0xf1, 0x50, 0x80, 0x31, 0x7f, - 0xfc, 0x21, 0x1a, 0xcf, 0xfe, 0xf4, 0xff, 0x40, 0x44, 0x95, 0xba, 0xb0, - 0xa5, 0x06, 0x66, 0x19, 0x86, 0x04, 0x00, 0x65, 0x8c, 0xbc, 0x58, 0x09, - 0xbf, 0x3e, 0x3c, 0xe6, 0xbd, 0x7b, 0x7a, 0xfd, 0x6b, 0x5d, 0xfd, 0xf4, - 0x3d, 0x2e, 0xb5, 0xbc, 0x42, 0x4a, 0xab, 0x89, 0x99, 0x4a, 0xcc, 0x5c, - 0xb4, 0x52, 0x10, 0xe6, 0xcb, 0xc5, 0x24, 0x19, 0x1a, 0xf3, 0xf8, 0x2e, - 0xf6, 0xde, 0x8e, 0xcb, 0xb0, 0x2e, 0xf7, 0xd3, 0xe0, 0x8c, 0x65, 0x57, - 0xea, 0xf4, 0xba, 0x19, 0xd4, 0x4e, 0xbc, 0x86, 0xaf, 0xca, 0x4f, 0xa1, - 0xb1, 0x54, 0xf9, 0x1d, 0xb8, 0x99, 0x4d, 0x2a, 0xd8, 0xe1, 0xb5, 0xa4, - 0x33, 0xa5, 0xb0, 0xe8, 0x27, 0xe4, 0xb6, 0x32, 0xce, 0xaf, 0x34, 0xd1, - 0x81, 0x89, 0x71, 0xbe, 0xa7, 0x2b, 0x04, 0xf8, 0x16, 0x59, 0x48, 0x10, - 0x43, 0xd7, 0x0f, 0xce, 0x52, 0x07, 0x15, 0xa5, 0xeb, 0x05, 0xf2, 0x4b, - 0x01, 0x0e, 0x36, 0x1f, 0x38, 0x88, 0x2e, 0xee, 0x4c, 0x73, 0x80, 0x82, - 0x92, 0x17, 0x8f, 0xe4, 0x30, 0x37, 0xce, 0x0b, 0x21, 0xb6, 0x58, 0x2c, - 0x17, 0x44, 0xb2, 0xf5, 0xbb, 0x99, 0x26, 0x2b, 0x2b, 0x6e, 0x82, 0x27, - 0x28, 0xa4, 0xaa, 0x05, 0xf4, 0x7a, 0x14, 0x28, 0x78, 0x1c, 0xea, 0x53, - 0x81, 0x41, 0xee, 0x86, 0xcf, 0x3a, 0xf9, 0xfe, 0x8f, 0x6a, 0x74, 0xe3, - 0xce, 0xc1, 0xfb, 0x50, 0x96, 0xfe, 0x4d, 0x6c, 0x16, 0xcb, 0xdd, 0x5c, - 0x48, 0xb0, 0x33, 0x10, 0xa2, 0x80, 0x84, 0x0d, 0x8e, 0x96, 0x6e, 0x45, - 0x24, 0x08, 0x84, 0x07, 0x28, 0x0b, 0xaf, 0x95, 0x46, 0x32, 0x12, 0xa4, - 0x0e, 0x0c, 0xe9, 0xfd, 0x47, 0x50, 0xc7, 0x10, 0x50, 0x4c, 0x25, 0x28, - 0x25, 0x2e, 0x15, 0x23, 0x40, 0x99, 0xde, 0x29, 0x88, 0x16, 0x83, 0x15, - 0x8a, 0x45, 0x7c, 0x65, 0x27, 0x54, 0x01, 0x8e, 0x56, 0xeb, 0x26, 0xf8, - 0x00, 0x00, 0x00, 0x0b, 0xfc, 0xfd, 0x4c, 0xbc, 0x8d, 0xcd, 0x92, 0x06, - 0xfd, 0xbe, 0xaf, 0xe2, 0xc4, 0x15, 0x24, 0x63, 0x9f, 0x9e, 0xa4, 0x38, - 0xf9, 0x03, 0x11, 0x8a, 0x5a, 0x7e, 0x90, 0x66, 0xe0, 0x2e, 0x43, 0x9b, - 0x66, 0xb3, 0x0f, 0x0a, 0xb6, 0xe9, 0xac, 0x8d, 0x88, 0x54, 0x00, 0x21, - 0x7e, 0x05, 0x15, 0x37, 0x41, 0x07, 0x91, 0x8f, 0x4d, 0x54, 0xa7, 0x17, - 0xbe, 0x9d, 0x81, 0xa2, 0xf6, 0x12, 0x4a, 0x78, 0x15, 0xc9, 0x02, 0x60, - 0x77, 0x2a, 0x7a, 0x7e, 0x49, 0x2c, 0x77, 0x58, 0xad, 0xda, 0x73, 0x4c, - 0x1e, 0x1c, 0x00, 0x71, 0xd9, 0xad, 0x1e, 0x37, 0x5f, 0x70, 0x36, 0xe8, - 0x2e, 0xeb, 0x8d, 0x8d, 0xb2, 0xe3, 0xff, 0x17, 0xf8, 0x86, 0x39, 0xb0, - 0xd9, 0x56, 0x6e, 0x90, 0xad, 0x90, 0x56, 0xc8, 0x1a, 0xba, 0x5f, 0x1a, - 0xe0, 0x1a, 0xda, 0x35, 0x07, 0xff, 0xf1, 0x50, 0x80, 0x2f, 0x5f, 0xfc, - 0x21, 0x1a, 0xcd, 0xfc, 0xd7, 0xff, 0x40, 0x20, 0x99, 0xb6, 0xc1, 0xac, - 0x28, 0x37, 0x0a, 0xa1, 0x04, 0x63, 0x61, 0xa8, 0x60, 0x4a, 0x80, 0x52, - 0x2a, 0x54, 0x00, 0x95, 0xc7, 0x7f, 0x1e, 0xfe, 0x7c, 0xd7, 0xe7, 0xe3, - 0xdf, 0xf5, 0xbf, 0x6f, 0xd3, 0xf5, 0x09, 0xdc, 0x17, 0x49, 0xe3, 0xaf, - 0x1a, 0x99, 0x01, 0x27, 0x32, 0x01, 0x67, 0x27, 0xcb, 0x2b, 0xb4, 0xe9, - 0xfb, 0x89, 0x23, 0x86, 0x09, 0x1f, 0xa8, 0xc6, 0xc9, 0xd5, 0x10, 0x8b, - 0xa7, 0xab, 0x8e, 0xf2, 0xe1, 0x7d, 0xf1, 0x0f, 0x83, 0x24, 0xe1, 0x51, - 0x05, 0x88, 0x37, 0x9c, 0x93, 0xe8, 0x0c, 0x5a, 0xac, 0xf9, 0x81, 0x5f, - 0x1c, 0x04, 0xdf, 0x58, 0x83, 0x93, 0x1c, 0xce, 0x64, 0x68, 0xb4, 0xd6, - 0x02, 0x10, 0xd0, 0x29, 0xf8, 0x73, 0x3a, 0xea, 0xab, 0xeb, 0x3c, 0xe9, - 0x6e, 0xed, 0xa2, 0xfb, 0x8f, 0xc0, 0x85, 0x2a, 0x9a, 0x44, 0xd0, 0x6c, - 0x2f, 0x75, 0x37, 0x6b, 0xff, 0x7e, 0x45, 0xc4, 0x29, 0xbb, 0xbe, 0x04, - 0x58, 0x40, 0x0f, 0x2f, 0x7d, 0x03, 0x11, 0x19, 0xea, 0xe8, 0xc2, 0xd3, - 0xdb, 0x80, 0xa9, 0x17, 0xfa, 0xe4, 0x20, 0x1a, 0xec, 0x94, 0xab, 0x63, - 0x40, 0xea, 0x59, 0x7c, 0x4b, 0x2c, 0x48, 0x0e, 0x4f, 0xf7, 0xcb, 0x01, - 0xbe, 0x25, 0x58, 0x76, 0x91, 0x3f, 0x9f, 0x44, 0x3e, 0xff, 0x86, 0x8f, - 0xad, 0x9e, 0x99, 0x36, 0x1d, 0xf8, 0x84, 0x3b, 0x52, 0x28, 0x02, 0x91, - 0x84, 0x27, 0x05, 0x41, 0x16, 0xab, 0xd8, 0x26, 0x8d, 0x0e, 0x06, 0x0a, - 0xaa, 0xfd, 0x96, 0x49, 0xe1, 0xb5, 0xbb, 0xc6, 0x5f, 0x0f, 0x09, 0x5f, - 0xdd, 0xa7, 0xc1, 0xe0, 0x9a, 0x68, 0x81, 0x81, 0x07, 0xa4, 0xb6, 0xbb, - 0x31, 0xc8, 0x39, 0x8a, 0x24, 0x66, 0x7e, 0xc3, 0x42, 0xcd, 0x3a, 0x65, - 0x05, 0xa1, 0x38, 0x93, 0x40, 0x41, 0xb0, 0x0e, 0x66, 0xe0, 0xc3, 0x94, - 0x7c, 0x00, 0x00, 0x00, 0x12, 0xab, 0x7a, 0xbc, 0xe3, 0x9a, 0x94, 0xa9, - 0x96, 0x0a, 0x3d, 0x3b, 0x51, 0x06, 0x52, 0x02, 0xac, 0x2d, 0x0e, 0xb1, - 0x47, 0x47, 0x8a, 0xaa, 0x29, 0x6c, 0x4e, 0xd1, 0x15, 0x45, 0xeb, 0xcb, - 0x23, 0x2c, 0x4b, 0x40, 0x4a, 0x88, 0xe1, 0xae, 0x6e, 0x5a, 0x24, 0x2b, - 0x34, 0x40, 0x0c, 0x02, 0x8d, 0x78, 0x10, 0xa6, 0x4e, 0x46, 0xff, 0x29, - 0xb5, 0x27, 0xbb, 0x50, 0xf2, 0x3e, 0x2a, 0x9f, 0xf6, 0x53, 0x58, 0x74, - 0xeb, 0xbf, 0x43, 0x5a, 0xb1, 0xce, 0xf0, 0xc9, 0x8c, 0x44, 0x15, 0x8e, - 0x52, 0x68, 0x65, 0x6b, 0x72, 0xfe, 0xf7, 0xf0, 0x9a, 0x19, 0x99, 0x30, - 0xb9, 0xbb, 0xc8, 0x2a, 0x02, 0x41, 0x3e, 0x17, 0x32, 0xa0, 0x38, 0xff, - 0xf1, 0x50, 0x80, 0x2f, 0x3f, 0xfc, 0x21, 0x1a, 0xcb, 0xef, 0xda, 0xff, - 0x47, 0xbf, 0x98, 0xba, 0x3b, 0x48, 0x2a, 0x34, 0x13, 0x04, 0xc2, 0xc2, - 0x00, 0x00, 0x00, 0x00, 0xf8, 0xaf, 0x5a, 0xdf, 0xe1, 0xaf, 0x7f, 0xe7, - 0x43, 0x7e, 0x93, 0x1c, 0x0f, 0x63, 0x94, 0xc7, 0xe6, 0x24, 0xc2, 0x09, - 0x94, 0x39, 0xd6, 0x37, 0x1b, 0xe5, 0x43, 0xcd, 0xdd, 0xaa, 0x9d, 0xe6, - 0x0b, 0x09, 0x8e, 0xdc, 0x4e, 0x6a, 0x45, 0xa4, 0xb6, 0x1e, 0x4f, 0x92, - 0x8c, 0xd5, 0x8e, 0x2e, 0xc2, 0xf1, 0x42, 0x32, 0xeb, 0x7d, 0x7e, 0x34, - 0x11, 0x98, 0xd3, 0x93, 0x97, 0xe2, 0x97, 0x30, 0xe3, 0xb2, 0x8e, 0x1c, - 0x3c, 0x3a, 0x00, 0x1d, 0x72, 0xe9, 0x20, 0x44, 0x63, 0x29, 0x0e, 0x27, - 0x42, 0x7e, 0xd5, 0x28, 0xe0, 0x55, 0x5e, 0xae, 0x15, 0x55, 0xc7, 0xbb, - 0x84, 0xee, 0x9a, 0x94, 0xa5, 0x77, 0xcb, 0xfd, 0xff, 0xc1, 0x84, 0xa6, - 0x20, 0x4e, 0x83, 0x05, 0x24, 0x12, 0x2b, 0x1f, 0x61, 0x99, 0x79, 0x81, - 0xb8, 0xac, 0x90, 0x08, 0x16, 0xd7, 0xef, 0x20, 0xbc, 0x6d, 0x72, 0x40, - 0x99, 0xc9, 0x42, 0x82, 0xbe, 0xdf, 0x34, 0x4d, 0xc6, 0x30, 0x1b, 0xe0, - 0xa5, 0x48, 0x33, 0x5d, 0x35, 0x39, 0x32, 0x17, 0x20, 0xf7, 0x4c, 0x1a, - 0xee, 0x27, 0x95, 0x5b, 0xf9, 0xe5, 0x20, 0x1c, 0xe0, 0xa5, 0x58, 0x4d, - 0xc9, 0x1a, 0xfb, 0xaa, 0x17, 0x59, 0x16, 0xcf, 0xd9, 0x76, 0xb1, 0x62, - 0xed, 0x42, 0x78, 0x7e, 0x8f, 0x67, 0xa6, 0x9f, 0x2b, 0x7a, 0x19, 0xb1, - 0x98, 0x71, 0x43, 0x9c, 0x62, 0xc8, 0x27, 0x73, 0xbb, 0xc8, 0xd1, 0x80, - 0xd1, 0x51, 0x2f, 0x1a, 0x66, 0xb1, 0xb5, 0x11, 0xcc, 0xd0, 0x9e, 0xaf, - 0xe3, 0xfd, 0xa7, 0x7e, 0x88, 0xb7, 0x7c, 0xd0, 0xd9, 0x77, 0x75, 0x57, - 0xd7, 0xd7, 0x33, 0x6d, 0x41, 0x51, 0x50, 0x76, 0x17, 0x1f, 0x41, 0x02, - 0xa0, 0x2a, 0x2a, 0x54, 0x01, 0x5d, 0xea, 0xf3, 0xe3, 0x9c, 0xac, 0xd6, - 0x4c, 0xd5, 0x8c, 0x44, 0x98, 0x02, 0xb5, 0x97, 0xcd, 0x6c, 0x9e, 0xcf, - 0x87, 0xa9, 0xc3, 0xcd, 0xa4, 0x6a, 0xce, 0x41, 0xa7, 0x0c, 0x2a, 0x23, - 0x1e, 0x47, 0x47, 0xe4, 0x86, 0x05, 0x63, 0x4a, 0xc6, 0xf1, 0x79, 0x39, - 0xd8, 0x1a, 0x94, 0xb5, 0x88, 0x6c, 0xdd, 0x91, 0x58, 0x24, 0x5c, 0x02, - 0xa7, 0x9b, 0x9b, 0x80, 0x6c, 0x73, 0xa0, 0x9e, 0xe5, 0x84, 0x83, 0xd1, - 0x87, 0x8d, 0x57, 0x86, 0x34, 0x74, 0x51, 0x2e, 0x9b, 0x66, 0x9a, 0xc2, - 0x2c, 0x73, 0xf6, 0x1d, 0x64, 0x6b, 0x7f, 0x8e, 0x81, 0x51, 0x70, 0x1a, - 0x68, 0xbb, 0x32, 0xc0, 0xdf, 0x10, 0x30, 0x91, 0x74, 0x19, 0xeb, 0xfc, - 0x29, 0x11, 0x90, 0xe0, 0xff, 0xf1, 0x50, 0x80, 0x2e, 0x1f, 0xfc, 0x21, - 0x1a, 0xcf, 0x73, 0xff, 0xff, 0x40, 0x40, 0x98, 0xb6, 0xc1, 0xac, 0x32, - 0xb4, 0x19, 0x8d, 0x82, 0xa2, 0x61, 0x80, 0x00, 0x00, 0x09, 0x5d, 0x77, - 0xd4, 0xbf, 0x7f, 0x6f, 0x5f, 0xcb, 0xd7, 0xdf, 0x8b, 0x1c, 0x33, 0x2a, - 0xc4, 0x26, 0x93, 0x4f, 0xab, 0xd1, 0xf7, 0x4b, 0x75, 0x1e, 0x40, 0x8b, - 0xcd, 0x44, 0xc4, 0x19, 0x87, 0xba, 0x99, 0x39, 0x20, 0xf7, 0x33, 0x94, - 0xb8, 0x53, 0x95, 0xda, 0xad, 0x04, 0xdf, 0x4f, 0xe7, 0x8d, 0x5d, 0xa0, - 0x78, 0x8e, 0xc6, 0x24, 0xf1, 0x54, 0xc2, 0x5b, 0x80, 0xdf, 0x1b, 0x93, - 0x84, 0x3a, 0x9f, 0x0e, 0xb4, 0x32, 0x34, 0x40, 0x8e, 0x59, 0x91, 0x0e, - 0x71, 0x2b, 0x5c, 0x93, 0x58, 0x57, 0x40, 0x70, 0x00, 0x00, 0xb7, 0xb2, - 0xb7, 0xaf, 0x1d, 0x29, 0x21, 0x0d, 0xc6, 0x86, 0x4c, 0x95, 0xc5, 0xfd, - 0x03, 0x19, 0x4a, 0xa8, 0x4e, 0x62, 0xd4, 0x21, 0x21, 0x9e, 0xdf, 0x41, - 0x32, 0xb0, 0x32, 0x95, 0x2a, 0xa6, 0x02, 0x02, 0x3d, 0x06, 0x98, 0x0c, - 0x2d, 0x9c, 0x40, 0xa2, 0x94, 0xa1, 0x3e, 0x95, 0x77, 0x16, 0xb8, 0x18, - 0x44, 0xb2, 0x77, 0xed, 0xb7, 0x0a, 0xd8, 0x70, 0x6c, 0x9c, 0xce, 0xcc, - 0x7f, 0x4c, 0x29, 0x32, 0x4c, 0xd1, 0xc6, 0xcb, 0xc2, 0x88, 0x4f, 0x53, - 0x5f, 0x7f, 0x87, 0x66, 0xbb, 0x3b, 0xef, 0x4f, 0xe4, 0x82, 0x14, 0xce, - 0x74, 0x73, 0x6b, 0xc2, 0x94, 0x76, 0x56, 0xbc, 0x7e, 0xb5, 0x47, 0x07, - 0x65, 0x26, 0xe9, 0xe3, 0x8e, 0x94, 0xa4, 0xb5, 0xad, 0x48, 0xed, 0xde, - 0xbd, 0x41, 0x6d, 0x37, 0x13, 0xae, 0x9b, 0xaa, 0x52, 0xbf, 0x47, 0xc2, - 0x49, 0xbb, 0x68, 0x34, 0xc6, 0xc5, 0xbd, 0x3b, 0xb7, 0x53, 0x94, 0xd5, - 0x7a, 0xfb, 0x3e, 0x1f, 0x26, 0xec, 0x6c, 0xed, 0xcd, 0x25, 0xb6, 0x62, - 0xda, 0x82, 0x81, 0xca, 0x7e, 0x03, 0x2c, 0xc5, 0x80, 0x00, 0x27, 0x8e, - 0x37, 0xc5, 0xec, 0xe7, 0xaa, 0xef, 0x86, 0x83, 0x3e, 0xc5, 0xcd, 0x50, - 0xf3, 0xd0, 0xa9, 0x0c, 0x80, 0x14, 0x92, 0x43, 0x3b, 0x34, 0x28, 0x67, - 0xfc, 0x02, 0x3b, 0x74, 0x2c, 0x88, 0x3b, 0x3c, 0x8f, 0x56, 0xb7, 0x0c, - 0x6f, 0xaf, 0x32, 0xb5, 0x31, 0x09, 0x13, 0x39, 0xda, 0x7e, 0xcb, 0x10, - 0xa8, 0x2c, 0xa3, 0x96, 0x73, 0xf4, 0x55, 0x48, 0x87, 0x20, 0x83, 0x3b, - 0x63, 0x3d, 0xcc, 0x7e, 0xf3, 0xec, 0x72, 0x85, 0xce, 0x75, 0x9a, 0xe0, - 0x0a, 0x93, 0x4b, 0x2a, 0x33, 0xf4, 0x7f, 0xed, 0xda, 0x16, 0x24, 0x45, - 0x6a, 0xe2, 0x84, 0xc9, 0x6a, 0x80, 0xd7, 0xf0, 0xf0, 0x95, 0x60, 0x1c, - 0xff, 0xf1, 0x50, 0x80, 0x2e, 0xdf, 0xfc, 0x21, 0x1a, 0xc8, 0xfe, 0xf3, - 0xff, 0x40, 0x00, 0x98, 0xb6, 0xc1, 0x65, 0xa8, 0x33, 0x1b, 0x18, 0x00, - 0x00, 0x03, 0x55, 0x97, 0x9e, 0xde, 0xbe, 0xab, 0xdb, 0xfa, 0xfd, 0x7c, - 0xfe, 0xfc, 0x7a, 0xfa, 0x9a, 0x19, 0x68, 0x80, 0xc9, 0x2b, 0xbd, 0x86, - 0x5a, 0x0e, 0x74, 0x34, 0xb4, 0x1a, 0x2d, 0xb6, 0xa8, 0x28, 0x01, 0xc3, - 0xb9, 0xa4, 0xd6, 0xfa, 0xb4, 0x77, 0x09, 0xcd, 0x90, 0x07, 0x5b, 0x95, - 0x13, 0xb3, 0x6d, 0x51, 0x13, 0x3f, 0xd5, 0xbc, 0x89, 0x5b, 0x98, 0x92, - 0x0d, 0x4c, 0xc7, 0x52, 0xe4, 0xd0, 0x97, 0x6c, 0x30, 0xe1, 0x6d, 0x43, - 0xa6, 0x66, 0xc8, 0x44, 0xe3, 0x57, 0x8f, 0x13, 0x09, 0xdb, 0xfa, 0x17, - 0xda, 0x6e, 0x57, 0x0c, 0xf6, 0xaa, 0xe5, 0x10, 0xac, 0xd5, 0x15, 0x16, - 0x2f, 0x85, 0xf5, 0x1b, 0x94, 0x8a, 0xa5, 0xcd, 0x8b, 0xb8, 0x2a, 0x45, - 0x31, 0xeb, 0x70, 0x09, 0x06, 0x49, 0x32, 0x94, 0x00, 0x57, 0x57, 0x86, - 0x40, 0x6d, 0xac, 0x71, 0xa0, 0x88, 0x17, 0xa6, 0x0e, 0x7e, 0x37, 0x04, - 0x81, 0x4e, 0x9b, 0x34, 0x39, 0x6d, 0x96, 0x71, 0x6b, 0x7a, 0x8d, 0xb4, - 0x54, 0x37, 0x9f, 0x0e, 0x95, 0xb0, 0xb4, 0x2a, 0xdc, 0xba, 0x9f, 0xe9, - 0x2c, 0x31, 0x0a, 0x23, 0x0c, 0xdd, 0x0b, 0xa5, 0x76, 0xbb, 0x8c, 0x25, - 0x4a, 0x28, 0x6a, 0x31, 0x86, 0x80, 0x12, 0x00, 0x10, 0xdb, 0x16, 0xe9, - 0x79, 0x9f, 0x8d, 0x29, 0x48, 0x93, 0xd8, 0x0d, 0x7c, 0x09, 0x23, 0x80, - 0x80, 0x80, 0x10, 0x76, 0x91, 0x4f, 0x25, 0xf6, 0x07, 0x9b, 0x3d, 0x0d, - 0xa1, 0x8a, 0x68, 0x33, 0x3f, 0x6d, 0x6e, 0xf4, 0x69, 0x9f, 0x68, 0x8f, - 0xee, 0x44, 0x38, 0x4d, 0x27, 0xab, 0x5e, 0xce, 0xba, 0xb6, 0x69, 0x89, - 0x2e, 0x2f, 0x40, 0xe1, 0x31, 0x6d, 0x82, 0xc9, 0x8c, 0x7e, 0x95, 0x0f, - 0x98, 0x00, 0x00, 0x03, 0x55, 0x8d, 0xea, 0xa5, 0xd6, 0xeb, 0xe2, 0x4d, - 0xdd, 0x68, 0x67, 0xaf, 0x84, 0x26, 0x30, 0xb9, 0x65, 0xe0, 0x10, 0x09, - 0xe6, 0x9a, 0xb6, 0x73, 0x1a, 0xaf, 0x57, 0x34, 0xc4, 0xd2, 0x08, 0xef, - 0xe4, 0xaf, 0x0d, 0x2d, 0x5e, 0x79, 0xce, 0x6a, 0x6b, 0x92, 0x2b, 0x20, - 0xad, 0x0a, 0x46, 0x30, 0xb7, 0x6a, 0x8c, 0x60, 0x49, 0x62, 0x29, 0x95, - 0xd5, 0xc5, 0xe8, 0x39, 0xdf, 0xee, 0xdc, 0x90, 0x10, 0x25, 0x58, 0x4b, - 0x2b, 0x5f, 0x51, 0xd1, 0x4b, 0x12, 0x71, 0xd0, 0xa2, 0x82, 0xed, 0x2b, - 0x8c, 0x57, 0x7a, 0xff, 0x63, 0xf6, 0x0f, 0x40, 0x11, 0x9d, 0xa8, 0x72, - 0xb8, 0x57, 0x4f, 0xc9, 0x78, 0x8d, 0x03, 0xe1, 0x48, 0xbb, 0xb6, 0x13, - 0x83, 0x2e, 0xff, 0xf1, 0x50, 0x80, 0x30, 0x1f, 0xfc, 0x21, 0x1a, 0xce, - 0xe7, 0x39, 0x7d, 0x3f, 0x7f, 0x96, 0xb6, 0xc1, 0x65, 0x66, 0x74, 0x0b, - 0x18, 0x00, 0x00, 0x01, 0x79, 0xad, 0xf9, 0xf1, 0xf5, 0x9d, 0x78, 0xdf, - 0xb7, 0x5d, 0x77, 0xc7, 0x1c, 0x8f, 0x3b, 0x94, 0x0d, 0x2b, 0x17, 0x06, - 0x7f, 0xb5, 0x12, 0x23, 0xe8, 0x42, 0x11, 0x6c, 0x6a, 0xcc, 0x14, 0x12, - 0x34, 0x0f, 0x8a, 0x11, 0x0c, 0xf1, 0x1d, 0x40, 0x3a, 0x85, 0x25, 0xe6, - 0xc2, 0xd5, 0x3a, 0x03, 0xde, 0xc2, 0xdb, 0x7c, 0x7b, 0x1f, 0xf6, 0xbd, - 0xa0, 0x8b, 0xb6, 0xf4, 0x5a, 0xd0, 0xe6, 0xc3, 0x69, 0x87, 0xb4, 0x5c, - 0x5d, 0x4c, 0x36, 0x87, 0xb1, 0x35, 0x19, 0x28, 0xb9, 0x93, 0x11, 0xcd, - 0xa6, 0x77, 0xa3, 0xff, 0xcb, 0xf4, 0xf5, 0x46, 0x8c, 0x6f, 0xc2, 0x54, - 0xb6, 0x70, 0xc3, 0x2a, 0x5e, 0x96, 0x2b, 0x98, 0xf9, 0x6f, 0x93, 0xe1, - 0x00, 0x4c, 0x03, 0x09, 0x85, 0x95, 0x6a, 0x71, 0xf1, 0xa8, 0x2e, 0x41, - 0x9c, 0xc9, 0xad, 0x14, 0x00, 0xd4, 0xe1, 0xcc, 0x01, 0xd8, 0x7a, 0xad, - 0x11, 0xd8, 0xe2, 0x25, 0xff, 0x1a, 0x7a, 0x43, 0xd7, 0x69, 0x79, 0x4f, - 0xb7, 0xea, 0xe9, 0xda, 0x63, 0xf9, 0xb7, 0x5f, 0x72, 0x74, 0xc7, 0xbd, - 0xfb, 0x0f, 0x96, 0xfa, 0x16, 0xef, 0x27, 0xce, 0xf2, 0xb6, 0x65, 0x14, - 0xc1, 0x28, 0x6e, 0xbc, 0xd4, 0x82, 0x44, 0x4b, 0x57, 0x74, 0x1a, 0xca, - 0x85, 0x21, 0x39, 0xbc, 0x58, 0x68, 0xa9, 0x4d, 0x57, 0xc2, 0x49, 0xb8, - 0xb6, 0xa7, 0x99, 0x6b, 0xce, 0x0e, 0xd1, 0xe0, 0x0b, 0x7b, 0x1a, 0x10, - 0xa0, 0x26, 0x5f, 0x60, 0x94, 0xaf, 0xab, 0xd5, 0xae, 0xb8, 0xe1, 0xb8, - 0xc2, 0x83, 0x0b, 0xe6, 0x6c, 0xfe, 0x65, 0xc0, 0xfe, 0x57, 0xdc, 0xda, - 0x97, 0x4c, 0x53, 0x4e, 0x9a, 0xca, 0x5a, 0x3e, 0xeb, 0x4d, 0x56, 0xc3, - 0xdb, 0x99, 0x35, 0xf2, 0xd6, 0xd8, 0x35, 0x86, 0x4f, 0xd0, 0x6b, 0xbb, - 0x31, 0x66, 0x2c, 0xc5, 0xe5, 0x86, 0x5b, 0xbd, 0x5c, 0x99, 0x5b, 0xe2, - 0xa6, 0x49, 0x41, 0x4b, 0x3d, 0x7e, 0xfb, 0x5a, 0x71, 0x22, 0x40, 0x68, - 0xbd, 0xf9, 0x3d, 0x66, 0x23, 0x8d, 0x09, 0x19, 0x61, 0x3b, 0x31, 0xcf, - 0x06, 0x29, 0xf8, 0x52, 0x1e, 0xc7, 0xbf, 0x31, 0x9f, 0x93, 0x41, 0x1b, - 0xfd, 0x8e, 0xdd, 0x9e, 0x42, 0x4a, 0x1d, 0xcb, 0x01, 0x70, 0x2f, 0x05, - 0xbb, 0x8a, 0x4b, 0x79, 0xf7, 0x50, 0xf3, 0xf5, 0x31, 0x35, 0x02, 0x44, - 0xaa, 0xd2, 0x4e, 0x3c, 0x0e, 0x3f, 0xae, 0x4b, 0x60, 0x5e, 0x30, 0x17, - 0x4c, 0x39, 0x82, 0x01, 0x44, 0xb1, 0xc6, 0x17, 0xa3, 0xf8, 0x8f, 0xa9, - 0xf8, 0x12, 0x2a, 0x02, 0xb2, 0x0b, 0x03, 0x31, 0x7a, 0xbb, 0x78, 0x61, - 0x01, 0xc0, 0xff, 0xf1, 0x50, 0x80, 0x2f, 0x1f, 0xfc, 0x21, 0x1a, 0xcf, - 0xf4, 0xfa, 0xff, 0x40, 0x00, 0x96, 0xb6, 0xc0, 0xe5, 0x66, 0x44, 0x21, - 0x09, 0x8c, 0x02, 0xa5, 0x20, 0x54, 0xa8, 0x16, 0xcf, 0x3e, 0xbf, 0x1f, - 0x9f, 0xad, 0x6b, 0xdf, 0x5c, 0xfd, 0x75, 0xbb, 0xd0, 0x70, 0xd2, 0x06, - 0x80, 0x48, 0xa0, 0x24, 0x45, 0xf4, 0x69, 0x31, 0x43, 0x96, 0x41, 0x6e, - 0x51, 0xe7, 0xec, 0x7a, 0x37, 0x5f, 0x34, 0x9f, 0x50, 0x7e, 0xc3, 0xf5, - 0xc5, 0xca, 0x17, 0xf4, 0x2a, 0xd0, 0xa0, 0x80, 0x2b, 0x77, 0x46, 0xa7, - 0xf6, 0x1f, 0x23, 0x96, 0x24, 0x75, 0x0e, 0x3a, 0x63, 0x1d, 0xe9, 0x1b, - 0x93, 0x71, 0x23, 0x11, 0x86, 0xf7, 0x23, 0x97, 0xd7, 0x45, 0x6b, 0x7b, - 0x28, 0xb6, 0xac, 0xe5, 0xfe, 0x0f, 0xec, 0xdc, 0x29, 0x2e, 0xb1, 0xe2, - 0x62, 0x41, 0x50, 0x88, 0x45, 0xe1, 0x08, 0x4f, 0x86, 0xd0, 0xe3, 0x51, - 0x52, 0x5a, 0x99, 0x8a, 0xa0, 0x55, 0x91, 0xc9, 0xbd, 0x5b, 0x2e, 0x43, - 0x87, 0x70, 0xf6, 0x12, 0xbe, 0xe4, 0xf6, 0x3f, 0x77, 0x8e, 0xdb, 0xf0, - 0x08, 0x6d, 0xb3, 0x50, 0xf9, 0x4d, 0xe5, 0x2c, 0x78, 0xd4, 0x56, 0x7a, - 0x52, 0x98, 0x13, 0xe2, 0x1d, 0xdf, 0x4b, 0xa3, 0xa2, 0x7a, 0xb6, 0x52, - 0x3e, 0xdf, 0xb9, 0x5b, 0xe0, 0xdf, 0xcc, 0x78, 0xfa, 0x67, 0x10, 0xac, - 0x1d, 0x76, 0xbf, 0xd9, 0x16, 0x35, 0xb1, 0xbd, 0xc9, 0x2b, 0xd0, 0x4e, - 0x41, 0x0d, 0xdc, 0x59, 0xde, 0xa7, 0xa2, 0xf1, 0x21, 0x03, 0x01, 0x61, - 0xe5, 0x5c, 0xb1, 0xb2, 0x99, 0x2d, 0xa1, 0xe8, 0x3c, 0x69, 0x38, 0x01, - 0x0b, 0xc2, 0x81, 0x10, 0xaf, 0x97, 0x01, 0x20, 0xe1, 0xca, 0xb5, 0x19, - 0x86, 0xba, 0xa5, 0x88, 0x16, 0x94, 0xb7, 0x58, 0x9e, 0xbc, 0x70, 0x45, - 0xb2, 0x96, 0xa8, 0x49, 0xa5, 0xaa, 0xed, 0xb6, 0x6c, 0x4e, 0xdd, 0xcc, - 0x73, 0x5e, 0x37, 0x0c, 0x92, 0xd6, 0xd8, 0x34, 0xa3, 0xe0, 0x00, 0x00, - 0x01, 0x6c, 0xd7, 0xcf, 0xc7, 0x32, 0xea, 0x95, 0x26, 0x44, 0x08, 0x37, - 0xe6, 0xab, 0xbc, 0x0e, 0xb4, 0x4d, 0x77, 0x74, 0x57, 0x0d, 0x1d, 0x8c, - 0x61, 0x59, 0x73, 0x6b, 0x9d, 0x62, 0x49, 0xdd, 0xeb, 0x2e, 0xe9, 0xd9, - 0x0d, 0xb3, 0x83, 0xc5, 0xd5, 0x6f, 0xa9, 0x42, 0x26, 0x3f, 0x97, 0xe1, - 0xf9, 0xea, 0x21, 0x43, 0x6f, 0x39, 0xd7, 0xc7, 0x34, 0xf0, 0x72, 0x3c, - 0xfd, 0x45, 0xf2, 0x5f, 0x19, 0x3a, 0xb8, 0x09, 0x04, 0xc1, 0x52, 0xf3, - 0x1f, 0x98, 0x73, 0x30, 0x29, 0xba, 0xca, 0xc4, 0x2c, 0x4b, 0x35, 0xb0, - 0xe7, 0x7a, 0xf7, 0xc2, 0x7a, 0xc6, 0x81, 0x2b, 0x14, 0x06, 0x0a, 0x16, - 0x15, 0xd1, 0xed, 0x15, 0x01, 0xc0, 0xff, 0xf1, 0x50, 0x80, 0x2f, 0xdf, - 0xfc, 0x21, 0x1a, 0xcf, 0xa4, 0x6a, 0xff, 0x40, 0x00, 0x96, 0xb6, 0xb1, - 0xac, 0x28, 0x39, 0x42, 0x0c, 0xc8, 0xc5, 0x00, 0x00, 0x00, 0x5e, 0x1d, - 0x77, 0xd7, 0x4f, 0x9e, 0xbc, 0x79, 0xf6, 0xe3, 0xc7, 0xb7, 0x43, 0xdb, - 0x09, 0x0e, 0xae, 0x08, 0x2f, 0xe3, 0xcb, 0xa6, 0x22, 0x76, 0x4c, 0xe7, - 0x27, 0x01, 0xbf, 0x4d, 0xca, 0xc8, 0x68, 0xe2, 0xe5, 0x87, 0x34, 0x6e, - 0xe1, 0xca, 0x5d, 0x6e, 0xa7, 0x1b, 0x28, 0xb6, 0x96, 0x8e, 0x78, 0x46, - 0x09, 0xd8, 0x07, 0x3d, 0x1b, 0xbf, 0x08, 0x24, 0x50, 0x6e, 0xc4, 0x00, - 0x01, 0xd0, 0x72, 0xd9, 0xe1, 0x50, 0x92, 0xfb, 0xbd, 0x9b, 0xc1, 0x53, - 0xf6, 0x41, 0x89, 0x80, 0xbc, 0xd7, 0x32, 0x2a, 0x0e, 0x30, 0x46, 0x01, - 0xa0, 0x0c, 0x37, 0x81, 0x78, 0xec, 0xd8, 0xac, 0x67, 0xbb, 0xf6, 0x4e, - 0x64, 0x2e, 0x58, 0x0a, 0x48, 0x30, 0xaa, 0xc2, 0x22, 0x85, 0xea, 0x77, - 0xdb, 0x4b, 0x9a, 0x82, 0x59, 0x82, 0xb0, 0x00, 0x5f, 0x2f, 0x70, 0x26, - 0xaa, 0x13, 0x22, 0xe3, 0x08, 0x86, 0x41, 0x51, 0x3e, 0x23, 0x80, 0x12, - 0x5b, 0x4f, 0x67, 0xb2, 0xfd, 0xc7, 0xa2, 0x9a, 0xd1, 0x5a, 0x21, 0xea, - 0x37, 0x24, 0xdc, 0x6d, 0x3d, 0x2d, 0xde, 0xae, 0x7e, 0x1a, 0x7f, 0x28, - 0x0f, 0xbc, 0xfa, 0x65, 0x5b, 0xcc, 0xbc, 0xec, 0x2f, 0xda, 0xee, 0x45, - 0x08, 0xf0, 0xfd, 0xdd, 0x35, 0x38, 0x29, 0xaa, 0x08, 0xce, 0xaa, 0xe1, - 0x82, 0xb4, 0x01, 0x63, 0xae, 0x73, 0x7e, 0x29, 0x5d, 0x4c, 0x88, 0x75, - 0xdd, 0xd5, 0x30, 0xdd, 0xbe, 0x50, 0x42, 0x17, 0x43, 0x02, 0x21, 0x84, - 0x0d, 0xf5, 0x2c, 0xf9, 0x2a, 0x8c, 0xca, 0xf9, 0x65, 0x44, 0xbb, 0x37, - 0xf9, 0x57, 0x4f, 0xab, 0xe3, 0x9f, 0x4e, 0xa9, 0x96, 0x40, 0xd3, 0x57, - 0xaa, 0x5a, 0xdb, 0x06, 0x91, 0x10, 0xac, 0x2a, 0x3f, 0x80, 0x65, 0xe2, - 0xf0, 0xb6, 0x5e, 0x5e, 0x58, 0x2f, 0x32, 0xb8, 0xe7, 0x57, 0x93, 0x7c, - 0xcd, 0x52, 0x40, 0xcb, 0xd5, 0xb7, 0x8a, 0xca, 0xe8, 0xe9, 0x57, 0xa4, - 0x7e, 0x37, 0xe1, 0x86, 0x50, 0x30, 0x7b, 0x2b, 0xb0, 0xcd, 0x8f, 0x23, - 0xb6, 0xdb, 0x85, 0x8b, 0xd5, 0xa3, 0xc5, 0x23, 0xb2, 0x90, 0xdd, 0x0d, - 0xfb, 0x28, 0x9b, 0x49, 0x2d, 0xae, 0x3a, 0xe1, 0x70, 0xe9, 0x7a, 0x72, - 0x93, 0x55, 0xa0, 0x15, 0xaa, 0x8d, 0x3a, 0x2c, 0xe8, 0x65, 0x85, 0x8a, - 0x93, 0x8e, 0x73, 0xd0, 0xfe, 0xa1, 0xfc, 0x4e, 0x65, 0xd4, 0x6e, 0xd3, - 0x94, 0xec, 0x29, 0xae, 0xec, 0xb4, 0xe6, 0x1a, 0x8e, 0x6e, 0xe0, 0x0a, - 0x80, 0x21, 0x46, 0xd6, 0xc7, 0x38, 0xb1, 0x74, 0x42, 0x20, 0x39, 0xff, - 0xfb, 0x65, 0x48, 0x70, 0xff, 0xf1, 0x50, 0x80, 0x2e, 0xdf, 0xfc, 0x21, - 0x1a, 0xcf, 0xda, 0xbf, 0xff, 0x58, 0x40, 0x98, 0xb6, 0xb1, 0xac, 0x28, - 0x29, 0x3a, 0x05, 0xc2, 0x66, 0x61, 0xa8, 0x60, 0x40, 0x00, 0x00, 0x00, - 0xfa, 0xf9, 0xbe, 0x35, 0xf3, 0xc7, 0x3f, 0x7d, 0x5f, 0x7f, 0x8b, 0x1e, - 0x6d, 0xfd, 0xea, 0x10, 0x56, 0xe9, 0xdc, 0x18, 0x23, 0xe6, 0x22, 0x29, - 0x0e, 0xfa, 0xf5, 0x56, 0x9c, 0xc2, 0x8d, 0xba, 0xa7, 0xc5, 0xc3, 0xee, - 0xe8, 0x1a, 0x04, 0x6b, 0xd1, 0xa4, 0x2e, 0x69, 0xa1, 0xf5, 0x2f, 0x05, - 0xae, 0xc2, 0x48, 0x39, 0x12, 0x10, 0x41, 0xda, 0x65, 0x79, 0xf7, 0xfd, - 0xf6, 0xd7, 0x1e, 0x12, 0x81, 0x97, 0x2a, 0x73, 0x73, 0x5e, 0xe8, 0x30, - 0xc1, 0x09, 0xea, 0x64, 0x53, 0x60, 0xc2, 0xc7, 0x1f, 0xf2, 0x9f, 0xb1, - 0xf3, 0x87, 0x9a, 0xf5, 0x95, 0x74, 0xa6, 0x19, 0xfe, 0xa1, 0xe5, 0x31, - 0x5a, 0xe5, 0x59, 0xcc, 0x6c, 0x0a, 0xc0, 0x85, 0xa0, 0xbd, 0x3f, 0x9f, - 0x68, 0x0c, 0x89, 0x44, 0xdc, 0x16, 0xcc, 0x81, 0x45, 0x2f, 0xc0, 0x40, - 0x09, 0x70, 0xb5, 0x12, 0x51, 0xe4, 0x61, 0x9a, 0x78, 0x81, 0xa9, 0xee, - 0x34, 0x18, 0x17, 0x30, 0x97, 0x71, 0x92, 0x5a, 0x59, 0xf5, 0x12, 0xee, - 0x4e, 0x72, 0x9f, 0x2f, 0x47, 0x53, 0x6f, 0xc9, 0xc7, 0x57, 0xbc, 0x79, - 0xe4, 0x03, 0xc0, 0x8b, 0xa8, 0x9c, 0xab, 0x07, 0x87, 0x02, 0x16, 0x59, - 0x6a, 0x3c, 0x84, 0x08, 0x36, 0x03, 0xdd, 0x02, 0x75, 0x07, 0x28, 0x17, - 0x68, 0x95, 0x82, 0x00, 0xc6, 0x8a, 0xe8, 0xb8, 0xab, 0xa0, 0x86, 0xfe, - 0x50, 0xa1, 0x41, 0x63, 0xcf, 0xc5, 0x53, 0x53, 0x64, 0x99, 0x42, 0x2d, - 0xa2, 0x90, 0x25, 0xe0, 0x28, 0x86, 0x2f, 0x80, 0x65, 0x0b, 0xb5, 0xdb, - 0xd2, 0x4e, 0x24, 0xdb, 0x78, 0xe8, 0x85, 0xa2, 0xe4, 0x0d, 0xbb, 0xe3, - 0x98, 0xb6, 0xb1, 0xac, 0x28, 0x3b, 0x0a, 0x8f, 0xe0, 0x00, 0x00, 0x00, - 0x33, 0x7e, 0xdd, 0xce, 0x37, 0x7c, 0xc8, 0xda, 0xc0, 0xc6, 0xd6, 0xcd, - 0x5b, 0x35, 0x8c, 0xe6, 0x58, 0x6d, 0xc0, 0xd8, 0x6d, 0x92, 0xca, 0x77, - 0x42, 0x5c, 0xfd, 0x5c, 0x89, 0x69, 0x85, 0xe7, 0x75, 0x6a, 0x55, 0x24, - 0x0e, 0xc6, 0xdf, 0x7d, 0xea, 0x32, 0x74, 0x23, 0xcb, 0xac, 0x51, 0x47, - 0x9c, 0xfe, 0xdd, 0xf6, 0x6d, 0xd3, 0xa8, 0x39, 0x19, 0xfb, 0x09, 0xae, - 0xdb, 0xb6, 0x11, 0x58, 0x11, 0x3d, 0x0e, 0x37, 0x90, 0x02, 0x9b, 0xd5, - 0xda, 0x84, 0x1f, 0x37, 0x8c, 0x39, 0x9f, 0x52, 0x80, 0xb4, 0xb7, 0xfe, - 0x70, 0x55, 0x30, 0x42, 0xca, 0xe6, 0xab, 0x63, 0xac, 0x13, 0x36, 0x62, - 0xc5, 0x47, 0xfe, 0xe0, 0x4c, 0x8e, 0xff, 0xf1, 0x50, 0x80, 0x2f, 0xdf, - 0xfc, 0x21, 0x1a, 0xcf, 0xfc, 0x6f, 0x7f, 0x40, 0x00, 0x98, 0xb6, 0xc2, - 0x25, 0x4a, 0x66, 0x1a, 0x86, 0x04, 0x00, 0x00, 0x00, 0x0f, 0x3e, 0x2f, - 0x89, 0xeb, 0x5b, 0xfc, 0x57, 0x1d, 0xfb, 0x7b, 0x06, 0xaf, 0x6c, 0xa0, - 0x49, 0x62, 0x87, 0xdc, 0xb0, 0x22, 0x4b, 0x80, 0xb7, 0xa1, 0xb4, 0x75, - 0x22, 0x6b, 0x69, 0x11, 0x73, 0x0e, 0x6d, 0xc8, 0xb0, 0x64, 0xe8, 0x21, - 0x69, 0xdb, 0xaa, 0x94, 0x7a, 0xcf, 0x2c, 0x89, 0xcc, 0x08, 0x6a, 0x1c, - 0xce, 0x06, 0x10, 0x01, 0x31, 0xe6, 0x71, 0x84, 0x0f, 0xae, 0x39, 0x06, - 0x59, 0x99, 0x9e, 0x60, 0x16, 0x84, 0x88, 0x13, 0x38, 0xdc, 0xc7, 0xf8, - 0x7f, 0x55, 0xa8, 0x0b, 0x84, 0x69, 0xbf, 0x18, 0xf8, 0xf1, 0xe1, 0x58, - 0x46, 0xaa, 0x29, 0x39, 0x5c, 0xa7, 0x89, 0xf7, 0xaf, 0x69, 0x84, 0x22, - 0x89, 0xce, 0xb3, 0x0a, 0x90, 0xb0, 0x8d, 0x9f, 0x45, 0x92, 0xf3, 0xca, - 0xae, 0xd7, 0xbd, 0x02, 0xe9, 0x69, 0x40, 0xc3, 0xa0, 0xe9, 0x41, 0x96, - 0xdc, 0x30, 0xa8, 0xca, 0x05, 0xeb, 0x66, 0xa8, 0xc3, 0x15, 0x2f, 0x5f, - 0xd2, 0xfa, 0xd0, 0x48, 0x57, 0x20, 0x48, 0xa3, 0x70, 0x8d, 0x7a, 0x25, - 0x19, 0xa9, 0x07, 0x18, 0x86, 0x5b, 0xcd, 0x56, 0x23, 0x5d, 0x22, 0xba, - 0xfd, 0xa0, 0x05, 0x73, 0x98, 0xc4, 0xd1, 0xbd, 0x5a, 0xe2, 0x47, 0x94, - 0xaf, 0x48, 0xd0, 0x34, 0xc9, 0x52, 0x45, 0xa4, 0x95, 0x51, 0x72, 0xa1, - 0x56, 0x8f, 0x76, 0x88, 0x60, 0x30, 0x41, 0x40, 0x62, 0xbd, 0x1a, 0x9a, - 0x28, 0xd6, 0x92, 0x9d, 0xca, 0x72, 0xd4, 0xb8, 0x77, 0xf4, 0x7d, 0x21, - 0x4a, 0x8b, 0x4f, 0x68, 0x15, 0x05, 0x93, 0x18, 0x80, 0x05, 0x01, 0x8d, - 0x27, 0xcb, 0xd4, 0x67, 0x93, 0x78, 0xba, 0xfb, 0xc8, 0x6d, 0xe4, 0x47, - 0x91, 0x23, 0x6e, 0x8a, 0x49, 0x4c, 0x52, 0xec, 0x50, 0x36, 0x1d, 0x85, - 0x07, 0x61, 0x71, 0xfc, 0x00, 0x14, 0x45, 0x41, 0x50, 0x0a, 0xce, 0xaa, - 0xb5, 0xcc, 0xae, 0x6f, 0x35, 0x8b, 0x11, 0x0d, 0x9e, 0x43, 0x86, 0xc5, - 0xce, 0x87, 0x78, 0x9c, 0x3c, 0x22, 0x07, 0x4d, 0x57, 0x4c, 0x2a, 0xbc, - 0xb6, 0x96, 0x5c, 0x97, 0x85, 0x11, 0x63, 0x94, 0x44, 0x9d, 0x8f, 0xce, - 0xc9, 0xcb, 0x7c, 0xaa, 0xf8, 0xa9, 0x80, 0x09, 0x53, 0xcc, 0xb2, 0x88, - 0x95, 0xb5, 0x6a, 0x11, 0xa7, 0xeb, 0x4c, 0x5a, 0x95, 0xab, 0xab, 0x91, - 0x7d, 0x62, 0x53, 0x1a, 0x53, 0x17, 0x17, 0x54, 0x08, 0x14, 0x93, 0x66, - 0xfb, 0xc9, 0xe0, 0x33, 0xd2, 0x02, 0x15, 0x3e, 0x1f, 0xd5, 0xa7, 0x4d, - 0x30, 0xc3, 0x2a, 0x09, 0x68, 0xcc, 0x95, 0x8c, 0x41, 0x62, 0xf2, 0x8f, - 0xcc, 0xca, 0x20, 0x0e, 0xff, 0xf1, 0x50, 0x80, 0x30, 0xbf, 0xfc, 0x21, - 0x1a, 0xce, 0xfa, 0xf5, 0x7f, 0x00, 0x00, 0x97, 0xb6, 0xc1, 0x65, 0xe4, - 0x13, 0x12, 0x85, 0x86, 0x61, 0x81, 0x15, 0x02, 0xa5, 0x41, 0x52, 0xa5, - 0x40, 0x29, 0xd7, 0x6b, 0xeb, 0xd6, 0xb7, 0xfc, 0xd5, 0x78, 0xfa, 0xf8, - 0x1c, 0xce, 0x4c, 0x80, 0xbb, 0x5b, 0x93, 0x05, 0xca, 0x75, 0x1c, 0x0a, - 0x54, 0x98, 0xe0, 0xf3, 0xaf, 0xc1, 0xcd, 0x34, 0x6a, 0x17, 0x60, 0xcd, - 0x2d, 0xdc, 0x90, 0x82, 0x91, 0x1d, 0xe7, 0x1a, 0x2c, 0x45, 0x3e, 0xd0, - 0xf2, 0x3e, 0x57, 0xf7, 0x52, 0x0b, 0xca, 0x59, 0xcc, 0xaf, 0x73, 0xea, - 0xb9, 0x8d, 0x08, 0x9d, 0x77, 0xe7, 0x12, 0x0c, 0x27, 0x63, 0x96, 0xf0, - 0x40, 0x04, 0x39, 0xe6, 0xfc, 0xf9, 0xdd, 0x97, 0xd6, 0xa3, 0x7e, 0x2d, - 0x7e, 0x3e, 0x5d, 0x36, 0x59, 0xdf, 0x00, 0x96, 0x7a, 0x42, 0xb4, 0x28, - 0x67, 0xd7, 0xff, 0xe7, 0xf9, 0x5b, 0xba, 0x6e, 0x12, 0xc3, 0x68, 0x56, - 0x61, 0x8e, 0x59, 0xe1, 0x1a, 0xba, 0x5e, 0x5b, 0x22, 0x73, 0xa9, 0x99, - 0x8c, 0x34, 0x41, 0x54, 0x91, 0x46, 0x1a, 0x9d, 0x46, 0x9c, 0xc4, 0x62, - 0x33, 0xc6, 0x22, 0x06, 0x77, 0x16, 0xba, 0xc9, 0x83, 0x2d, 0xda, 0x1e, - 0xa1, 0xa7, 0x51, 0x31, 0x68, 0x33, 0xdb, 0x01, 0x65, 0x88, 0x2e, 0xf7, - 0xe4, 0x2d, 0x42, 0xb6, 0xef, 0xa6, 0x7b, 0x64, 0xf1, 0x3c, 0xb0, 0x39, - 0x22, 0x8d, 0x2a, 0x17, 0xf0, 0xa5, 0xb5, 0x20, 0xc5, 0xac, 0x36, 0x8c, - 0x89, 0x15, 0x46, 0x18, 0x5b, 0x65, 0xe6, 0xbd, 0xcc, 0x48, 0x60, 0xf2, - 0xa4, 0x0a, 0x52, 0xec, 0x14, 0x3b, 0xea, 0x08, 0x20, 0xbe, 0xb7, 0x50, - 0xc0, 0xbd, 0x4e, 0xb2, 0xd2, 0x5f, 0x12, 0x13, 0x16, 0xb0, 0x18, 0x2e, - 0x2c, 0x67, 0x79, 0x34, 0x61, 0xad, 0x1f, 0x11, 0x6c, 0xc1, 0x67, 0x87, - 0x11, 0x82, 0x24, 0x67, 0x19, 0x93, 0x3a, 0x5e, 0xdb, 0x06, 0xb0, 0xa0, - 0xec, 0x2a, 0x3f, 0x80, 0x00, 0x00, 0x00, 0xca, 0xe3, 0x99, 0xac, 0x95, - 0xcf, 0x0a, 0xaa, 0xb0, 0x8e, 0x9b, 0xd9, 0x89, 0xba, 0x8e, 0xdf, 0x8e, - 0x1a, 0xf6, 0x37, 0x6d, 0xae, 0x14, 0x04, 0x70, 0xb1, 0x5c, 0x3c, 0x01, - 0x65, 0xc4, 0xaa, 0x79, 0xaf, 0xa0, 0x18, 0xc7, 0x6b, 0x54, 0x68, 0xfd, - 0xe1, 0x1c, 0x24, 0xa9, 0x9c, 0x60, 0x00, 0x5d, 0x77, 0x0c, 0x60, 0xeb, - 0xf9, 0x3b, 0x9a, 0xb0, 0x9c, 0x03, 0x36, 0xbe, 0xf8, 0x4a, 0x9e, 0x51, - 0x80, 0x25, 0x87, 0xcd, 0xa6, 0x67, 0x3a, 0x85, 0x01, 0xa5, 0xf8, 0xda, - 0x1e, 0x3e, 0xb7, 0xe2, 0x39, 0xab, 0xfb, 0x3c, 0x31, 0x86, 0xb3, 0xa8, - 0x46, 0x61, 0xc5, 0xe8, 0xcb, 0x1b, 0x17, 0x4d, 0xf9, 0xc0, 0xaa, 0xed, - 0x0a, 0x82, 0x77, 0xfb, 0x3d, 0x72, 0x26, 0x08, 0xe0, 0xff, 0xf1, 0x50, - 0x80, 0x30, 0x3f, 0xfc, 0x21, 0x1a, 0xcb, 0xfd, 0xeb, 0x7f, 0x40, 0x00, - 0x96, 0xb6, 0xc1, 0xd0, 0x72, 0x85, 0x32, 0x08, 0xc2, 0xc3, 0x00, 0x00, - 0xeb, 0xb0, 0x01, 0x79, 0x79, 0xd7, 0x77, 0x9f, 0xab, 0xd7, 0xde, 0xfc, - 0x89, 0x44, 0x53, 0xbd, 0x2b, 0x34, 0x97, 0x53, 0x34, 0x25, 0x02, 0x9b, - 0x5c, 0x96, 0xa3, 0xad, 0x71, 0xf8, 0xa3, 0x6f, 0x67, 0x0c, 0xc7, 0x92, - 0xfa, 0x3d, 0x83, 0xac, 0x5b, 0x0e, 0x12, 0xd7, 0xd2, 0xef, 0x85, 0x07, - 0x74, 0xec, 0x68, 0x27, 0x2e, 0xce, 0x88, 0x22, 0x80, 0x4b, 0xa4, 0x81, - 0x64, 0x15, 0x6e, 0xfe, 0x03, 0xb7, 0xab, 0xe6, 0x3e, 0x90, 0xd7, 0x0c, - 0x0a, 0x1a, 0x6b, 0xb9, 0x3a, 0x84, 0x83, 0xb7, 0xbd, 0x6a, 0xb1, 0x20, - 0x12, 0x50, 0xfe, 0xaf, 0xd3, 0x7f, 0x22, 0xab, 0xa6, 0xf7, 0x89, 0xd1, - 0xe1, 0x9f, 0x2f, 0xfc, 0x9f, 0x82, 0xdb, 0x35, 0x38, 0xb1, 0x45, 0x5e, - 0x52, 0xb6, 0x57, 0x22, 0x83, 0x0c, 0x7d, 0xc3, 0xb9, 0x0b, 0xa2, 0x57, - 0x49, 0x2e, 0xea, 0x2a, 0x16, 0x19, 0xeb, 0xf9, 0x98, 0x16, 0x56, 0x51, - 0x00, 0xb8, 0xb4, 0x4e, 0x35, 0x2a, 0xae, 0x47, 0x85, 0xc2, 0x95, 0x4c, - 0x85, 0x63, 0x34, 0x22, 0x45, 0xc0, 0xb5, 0x64, 0x5a, 0xc1, 0x9b, 0x85, - 0x66, 0x73, 0x78, 0x98, 0xa4, 0xaa, 0xbd, 0x94, 0x00, 0x26, 0x62, 0x6e, - 0x8c, 0xd5, 0x6b, 0x71, 0x07, 0xbe, 0x26, 0x50, 0x09, 0xa4, 0xef, 0xbc, - 0x96, 0xb5, 0x0e, 0x30, 0x8f, 0x4e, 0x77, 0x9d, 0xd6, 0xa3, 0xf3, 0x83, - 0xcb, 0x20, 0xfe, 0x16, 0x8a, 0x8d, 0x11, 0x34, 0x3f, 0x92, 0x29, 0x4a, - 0xc9, 0x01, 0xda, 0x74, 0xcb, 0xa0, 0x01, 0x9a, 0x63, 0xba, 0x2d, 0x4c, - 0x80, 0xe4, 0xb2, 0x9e, 0x9f, 0x41, 0x39, 0x3d, 0xdd, 0x1e, 0xef, 0x4e, - 0x8f, 0x46, 0x7f, 0x1b, 0x76, 0xca, 0xec, 0xd2, 0xd6, 0xd8, 0x35, 0x85, - 0x07, 0x61, 0x71, 0xfc, 0x00, 0x00, 0x00, 0x05, 0x67, 0x0c, 0x99, 0x7b, - 0xce, 0x2b, 0x12, 0xc4, 0xea, 0x1a, 0x8e, 0x25, 0x18, 0xb0, 0x53, 0x52, - 0xa7, 0x22, 0x2a, 0x36, 0x4f, 0x96, 0x99, 0x13, 0x7d, 0x18, 0xd1, 0xb5, - 0x29, 0xb7, 0x4f, 0x8e, 0xb1, 0x57, 0x24, 0x56, 0xb8, 0x4f, 0xc1, 0xb3, - 0xb1, 0x2b, 0x3c, 0x3c, 0xbc, 0x18, 0xc6, 0x38, 0x4a, 0xb6, 0xf3, 0xf7, - 0x83, 0x27, 0xdf, 0x8a, 0xbc, 0x98, 0xf3, 0xb1, 0x69, 0x91, 0x00, 0xf0, - 0x28, 0xa7, 0x26, 0x36, 0xeb, 0x45, 0x50, 0x68, 0x57, 0x28, 0x05, 0xd7, - 0x5f, 0xd2, 0x9a, 0xbd, 0x9d, 0x0f, 0xc9, 0xe5, 0x47, 0xe7, 0x29, 0x0d, - 0x4f, 0x23, 0xe0, 0x44, 0x79, 0x28, 0xb2, 0xcd, 0x2e, 0x08, 0xbd, 0xb0, - 0x10, 0x29, 0x19, 0x0c, 0xbe, 0x16, 0xae, 0x40, 0xa9, 0x70, 0xff, 0xf1, - 0x50, 0x80, 0x2d, 0x7f, 0xfc, 0x21, 0x1a, 0xcf, 0xfe, 0xbf, 0xff, 0x48, - 0x80, 0x95, 0xb8, 0x30, 0xec, 0x32, 0xb3, 0x32, 0x85, 0x89, 0x01, 0x00, - 0x00, 0x00, 0x06, 0xb7, 0x75, 0xe7, 0xd1, 0xf7, 0xeb, 0xc7, 0x9e, 0x38, - 0x16, 0x4d, 0x77, 0x06, 0x8c, 0x95, 0x91, 0x40, 0x12, 0x50, 0xa3, 0xab, - 0x89, 0x22, 0x47, 0xb2, 0x7b, 0x95, 0xfb, 0x5a, 0xa3, 0x46, 0xe9, 0xcd, - 0x17, 0x14, 0x97, 0x46, 0x4f, 0x29, 0xdb, 0x70, 0x97, 0x13, 0xb1, 0x8b, - 0x93, 0xf9, 0x33, 0xe9, 0x58, 0x7d, 0x9f, 0x4f, 0x39, 0x9d, 0x31, 0x0a, - 0x57, 0x21, 0xf4, 0x08, 0xcf, 0xa3, 0xce, 0x09, 0x61, 0xe8, 0xaf, 0xf9, - 0xc5, 0x1d, 0xd4, 0x18, 0xa7, 0x2d, 0x9d, 0x1f, 0xd9, 0xca, 0xc0, 0x59, - 0x8b, 0x67, 0x34, 0xd0, 0x19, 0xee, 0x62, 0x54, 0xe4, 0x34, 0x7f, 0x03, - 0xe5, 0x53, 0x9c, 0xa2, 0x46, 0x38, 0x66, 0xb2, 0xd2, 0x24, 0x36, 0xf7, - 0x1c, 0xc5, 0xcc, 0xc0, 0xc6, 0xc5, 0x48, 0x24, 0x35, 0x7c, 0xce, 0x3a, - 0x20, 0x22, 0xa0, 0x94, 0xc2, 0x66, 0x31, 0xcc, 0xca, 0x74, 0xfd, 0x7a, - 0xf3, 0x8b, 0xc2, 0x43, 0xfa, 0x02, 0x69, 0x42, 0x5c, 0x19, 0xe4, 0x22, - 0x43, 0x87, 0x63, 0xa9, 0xe8, 0x3f, 0xd5, 0x70, 0x7a, 0x47, 0xaa, 0x80, - 0x20, 0xd2, 0x76, 0x7e, 0x2d, 0xe0, 0x5a, 0x14, 0xde, 0x6c, 0x41, 0x4b, - 0xce, 0xfe, 0x99, 0x2b, 0x11, 0x53, 0x36, 0xa8, 0x6d, 0xb8, 0x4f, 0x12, - 0x36, 0x03, 0x01, 0x33, 0x60, 0x40, 0x7b, 0x9c, 0x1d, 0x56, 0xbd, 0x2e, - 0xb0, 0x91, 0xc1, 0x18, 0x28, 0x65, 0x13, 0x9e, 0xf4, 0xa5, 0x7b, 0x97, - 0xb5, 0x30, 0xaa, 0xbc, 0x6b, 0x15, 0xa8, 0x0a, 0xfd, 0x1a, 0x62, 0x07, - 0x8d, 0x24, 0xcf, 0x8d, 0xb4, 0x65, 0x6e, 0x0c, 0x39, 0x47, 0xc0, 0x00, - 0xa9, 0x50, 0x00, 0x1b, 0xe7, 0xaa, 0x97, 0xbb, 0xad, 0xdd, 0xe5, 0x6a, - 0x06, 0x7c, 0x86, 0xaa, 0x8f, 0x45, 0x89, 0x9b, 0xb3, 0xd2, 0x41, 0x84, - 0x5c, 0x20, 0xef, 0xb6, 0xf2, 0x9c, 0x85, 0x0b, 0x40, 0xae, 0xe3, 0xc8, - 0xf9, 0xb1, 0xd4, 0x65, 0x38, 0xfa, 0xa8, 0x8b, 0x28, 0x55, 0xb4, 0xf1, - 0x5e, 0x06, 0x28, 0xa7, 0x0a, 0x59, 0x48, 0xe1, 0x28, 0x79, 0x26, 0xf6, - 0x1d, 0xac, 0x90, 0xfc, 0xdb, 0x9e, 0x91, 0xb6, 0xaa, 0x09, 0x52, 0xaf, - 0xfc, 0x4f, 0xdf, 0x74, 0x49, 0xc7, 0x43, 0x7e, 0xb6, 0x19, 0xb3, 0x15, - 0x9e, 0x11, 0x0a, 0xc2, 0x09, 0xe2, 0x7f, 0xa0, 0xff, 0x1d, 0xc0, 0xc1, - 0x70, 0x91, 0x72, 0x12, 0x80, 0x92, 0xef, 0x97, 0xa1, 0xa5, 0x81, 0x10, - 0x1c, 0xff, 0xf1, 0x50, 0x80, 0x30, 0x7f, 0xfc, 0x21, 0x1a, 0xcf, 0xbe, - 0x67, 0x7b, 0x40, 0x00, 0x93, 0xb6, 0xd2, 0x08, 0x52, 0x83, 0x2a, 0x11, - 0x86, 0x00, 0x00, 0x00, 0x0e, 0x39, 0xd6, 0xfe, 0xff, 0x6b, 0xd7, 0x9e, - 0xbd, 0x7c, 0x4e, 0x06, 0xb2, 0x94, 0xed, 0x63, 0xe6, 0x13, 0x24, 0x6e, - 0x22, 0x4e, 0x20, 0x26, 0x58, 0x38, 0x3d, 0xbe, 0xc5, 0x22, 0x20, 0x55, - 0xfc, 0x54, 0xb0, 0xf8, 0xfc, 0x6f, 0xc6, 0xc6, 0x6f, 0xbc, 0xcd, 0x9a, - 0xd8, 0xdf, 0x49, 0x5f, 0x57, 0x34, 0xf3, 0x5d, 0xa6, 0x79, 0x93, 0xc9, - 0xd4, 0x84, 0xab, 0x4a, 0xa2, 0x90, 0xa9, 0x16, 0xce, 0x4a, 0x50, 0xdc, - 0x54, 0x75, 0x7f, 0x7c, 0x0c, 0xf7, 0x61, 0x24, 0x6c, 0x9a, 0x20, 0xca, - 0x7e, 0x20, 0x13, 0x89, 0x41, 0xe7, 0x75, 0xd0, 0x08, 0x72, 0x0c, 0x45, - 0x1e, 0x7c, 0xc0, 0xa2, 0xc5, 0x75, 0xfc, 0xb8, 0x77, 0x78, 0xea, 0xa8, - 0xfd, 0x4b, 0xe3, 0x5b, 0x02, 0x2a, 0xd8, 0xc6, 0x52, 0x33, 0xba, 0x53, - 0x19, 0x2e, 0xfd, 0x1e, 0xf6, 0xd0, 0xce, 0x12, 0xca, 0x03, 0x28, 0x0b, - 0x06, 0xbf, 0x32, 0xe2, 0x58, 0x17, 0x08, 0x54, 0x93, 0x05, 0x19, 0x2c, - 0xed, 0x7a, 0x0c, 0xb4, 0xee, 0xaa, 0x54, 0x9f, 0x3d, 0x5a, 0xd5, 0xa8, - 0xde, 0x52, 0x81, 0xbe, 0xbe, 0x69, 0xe0, 0xf8, 0x9f, 0xd8, 0xfe, 0x4f, - 0xce, 0xab, 0x5b, 0x12, 0x09, 0xe5, 0x3c, 0x12, 0x54, 0x95, 0x9b, 0x25, - 0xf3, 0x3d, 0x3a, 0xca, 0xe8, 0xbc, 0x5f, 0x8b, 0x45, 0xa9, 0xf8, 0x3e, - 0x0b, 0xdf, 0x3b, 0xfa, 0xf9, 0xcd, 0x3b, 0x8a, 0xd6, 0x74, 0x50, 0x67, - 0x56, 0xf2, 0x62, 0x57, 0xd4, 0x74, 0xf4, 0x5b, 0x15, 0x4d, 0xc4, 0x83, - 0xb5, 0xbf, 0x35, 0x64, 0xc9, 0x65, 0x75, 0x72, 0x58, 0x83, 0x2c, 0x3d, - 0x1c, 0xb6, 0x63, 0xbd, 0xe6, 0x19, 0xee, 0xde, 0xe1, 0xba, 0x4e, 0xe8, - 0x83, 0xb0, 0xa8, 0xfe, 0x01, 0x50, 0x29, 0x14, 0x85, 0x41, 0x51, 0x59, - 0xc6, 0xf5, 0x36, 0xaa, 0xb6, 0xed, 0x02, 0x3f, 0x35, 0xf6, 0x3a, 0xa4, - 0xfc, 0x55, 0x5f, 0xd7, 0x63, 0x5e, 0xe5, 0x1d, 0x5c, 0x54, 0x2b, 0x02, - 0xd2, 0xaa, 0x51, 0x39, 0x02, 0x1f, 0x1b, 0xf2, 0xde, 0x48, 0x73, 0x33, - 0x9f, 0x60, 0xc7, 0x76, 0x0c, 0x9f, 0x6e, 0x56, 0x7c, 0xba, 0x65, 0x41, - 0x8b, 0x65, 0x46, 0xb4, 0x47, 0xad, 0x6d, 0x57, 0x14, 0xca, 0x07, 0x80, - 0xc6, 0x2a, 0xa1, 0x29, 0x85, 0x78, 0xaa, 0xad, 0x95, 0x71, 0x41, 0x78, - 0xcc, 0x44, 0x40, 0x5a, 0x58, 0xf0, 0xe6, 0x8e, 0x37, 0xe1, 0xb0, 0xd8, - 0xf6, 0xb1, 0x40, 0x2a, 0x9c, 0x46, 0x22, 0xf9, 0x65, 0x0b, 0xd5, 0x70, - 0xa0, 0x50, 0xaa, 0xb5, 0xe2, 0xe4, 0xc5, 0x48, 0x50, 0xbf, 0xed, 0xd9, - 0xde, 0x2a, 0x43, 0x80, 0xff, 0xf1, 0x50, 0x80, 0x2f, 0x9f, 0xfc, 0x21, - 0x1a, 0xcf, 0x92, 0xf4, 0xbf, 0x10, 0x00, 0x94, 0xb8, 0xba, 0x88, 0x6a, - 0x54, 0x13, 0x16, 0x02, 0x14, 0x94, 0x95, 0x05, 0x4a, 0x4a, 0x82, 0xf2, - 0xaf, 0xcf, 0xbf, 0xd7, 0x8f, 0x8f, 0x9f, 0x6c, 0xf6, 0xeb, 0x7d, 0x48, - 0x24, 0x8c, 0x7a, 0xb2, 0x52, 0x83, 0x74, 0xb3, 0xf0, 0xf7, 0x4d, 0x5d, - 0x26, 0x42, 0x0a, 0xa5, 0x60, 0x7e, 0xa9, 0x62, 0xac, 0x5f, 0x0e, 0x47, - 0x7d, 0xd1, 0xef, 0x50, 0x2d, 0x96, 0x9d, 0xe5, 0x41, 0xe5, 0x4e, 0x0b, - 0xd5, 0xbc, 0xe5, 0xfd, 0xc9, 0xfc, 0x84, 0xa7, 0xc0, 0xa2, 0x07, 0x20, - 0x2e, 0x5c, 0x5a, 0x8a, 0xe5, 0xb6, 0x60, 0xa3, 0x9b, 0x51, 0x2f, 0x7b, - 0x79, 0x6f, 0x69, 0x45, 0xec, 0x85, 0x44, 0xea, 0x65, 0x9f, 0xaa, 0xfc, - 0x0c, 0x27, 0x3c, 0xab, 0x1a, 0xd9, 0x6a, 0x81, 0x5a, 0xc9, 0x22, 0x01, - 0xd5, 0x7e, 0x87, 0xc8, 0xd0, 0x12, 0x05, 0x8b, 0x98, 0x06, 0x65, 0x75, - 0x3d, 0x47, 0x81, 0xa0, 0x00, 0xf0, 0x19, 0xcc, 0x02, 0x95, 0x27, 0x70, - 0x70, 0x73, 0x71, 0xd2, 0x49, 0x7d, 0xcf, 0xf8, 0x57, 0x57, 0xab, 0xfa, - 0xd0, 0x41, 0x7d, 0xda, 0xd6, 0xaf, 0x25, 0x66, 0xaf, 0x7a, 0xa4, 0x44, - 0x60, 0xb8, 0xa0, 0x2e, 0x44, 0x44, 0x45, 0x67, 0x3a, 0xe5, 0x72, 0xb9, - 0x34, 0x00, 0x18, 0xa3, 0x9c, 0xa6, 0x6e, 0xb4, 0x57, 0x28, 0x5a, 0x96, - 0xd9, 0x30, 0x08, 0x8a, 0x8c, 0x20, 0x63, 0x10, 0x62, 0x44, 0x7c, 0x45, - 0xd2, 0xb3, 0xe9, 0xa2, 0xc8, 0x03, 0xdc, 0xa3, 0x6d, 0xbb, 0xdf, 0x22, - 0x29, 0x0a, 0xd5, 0x80, 0x90, 0x25, 0x99, 0x54, 0x68, 0x96, 0xeb, 0x2b, - 0xf8, 0x6f, 0x3e, 0xea, 0x2d, 0x64, 0x12, 0x99, 0x9b, 0x6e, 0x8d, 0x4b, - 0x9e, 0x91, 0x7f, 0x2e, 0xae, 0x12, 0xab, 0x31, 0x0b, 0x5e, 0x88, 0xa9, - 0xf4, 0x8b, 0x7c, 0x2a, 0x56, 0x04, 0x48, 0x94, 0xb9, 0x4a, 0x7e, 0x00, - 0x00, 0x00, 0x0b, 0xcc, 0xd5, 0x73, 0xd4, 0xe6, 0x06, 0xed, 0x60, 0xaf, - 0x79, 0xf9, 0x33, 0xa3, 0x5c, 0x08, 0xbc, 0xaa, 0xb3, 0x0f, 0xaf, 0x36, - 0x21, 0xa6, 0x07, 0x5f, 0xb6, 0xd7, 0xe9, 0xa1, 0x34, 0x0b, 0xfb, 0xff, - 0xa5, 0xe9, 0x15, 0x34, 0x11, 0x9c, 0x6b, 0x96, 0xe7, 0xd6, 0x4c, 0x99, - 0x5e, 0x74, 0x6c, 0xa7, 0x19, 0x40, 0x46, 0x36, 0x21, 0x98, 0xbc, 0x0b, - 0xf6, 0xa8, 0xd0, 0x42, 0xaf, 0xc5, 0x3c, 0xc2, 0xd7, 0x1d, 0x3f, 0xe1, - 0x78, 0xa7, 0x35, 0xd1, 0xf7, 0x4f, 0xc5, 0xe6, 0x30, 0xb8, 0x8b, 0x89, - 0x90, 0xb8, 0x91, 0x94, 0x15, 0xab, 0xfb, 0x0f, 0xf2, 0x9f, 0x09, 0xa1, - 0x05, 0x08, 0xab, 0x99, 0x13, 0x60, 0x25, 0xab, 0xd1, 0x55, 0x08, 0x0e, - 0xff, 0xf1, 0x50, 0x80, 0x2e, 0x7f, 0xfc, 0x21, 0x1a, 0xcf, 0xf9, 0xdb, - 0xf5, 0x44, 0x00, 0x96, 0xb8, 0x30, 0xec, 0x24, 0x3b, 0x0b, 0x8d, 0x54, - 0xc3, 0x50, 0xc0, 0x80, 0x00, 0x00, 0x1a, 0xde, 0xb7, 0xad, 0xf5, 0xcf, - 0xc7, 0xcf, 0xb7, 0xaf, 0xbf, 0x5a, 0xf7, 0xfc, 0x58, 0xbf, 0xdb, 0x83, - 0x99, 0x9b, 0x62, 0x03, 0xa8, 0x88, 0x18, 0x92, 0xb1, 0xc9, 0x28, 0xbe, - 0x91, 0xb9, 0x4d, 0x18, 0xd5, 0xd2, 0x1d, 0x3c, 0xcf, 0x52, 0x2a, 0x48, - 0x6f, 0x2a, 0xee, 0x76, 0xc3, 0x75, 0x27, 0x1c, 0x1c, 0x1b, 0xa0, 0xfa, - 0x46, 0xb7, 0x34, 0xb0, 0x5e, 0xbc, 0x32, 0x34, 0xe2, 0x8e, 0x33, 0x36, - 0x52, 0x54, 0xf2, 0xc1, 0x07, 0x25, 0xde, 0x77, 0x44, 0x4e, 0x89, 0x10, - 0xe1, 0x2c, 0xd2, 0xa4, 0x1a, 0xc0, 0x18, 0x27, 0x87, 0x68, 0x39, 0x04, - 0xf6, 0x3f, 0x61, 0x8a, 0x0f, 0x0b, 0xbd, 0xa6, 0x42, 0x36, 0x0d, 0x02, - 0xc6, 0xa0, 0x60, 0x13, 0x96, 0x65, 0x28, 0xac, 0x27, 0xe1, 0xef, 0x13, - 0x24, 0xd5, 0xf3, 0x86, 0xc9, 0xa3, 0x64, 0x8b, 0xd7, 0xc4, 0x09, 0x98, - 0x28, 0x2b, 0xa7, 0x65, 0xc4, 0x60, 0x89, 0xff, 0xde, 0x40, 0x25, 0x3a, - 0xca, 0x97, 0x96, 0x96, 0xb0, 0x3a, 0x00, 0x15, 0x85, 0x3b, 0xe3, 0x1c, - 0x6b, 0x3b, 0x44, 0xf4, 0x85, 0xf0, 0x00, 0x1f, 0x24, 0xee, 0x63, 0xa7, - 0x09, 0x6d, 0xb4, 0x5c, 0x73, 0x89, 0xad, 0x80, 0x40, 0xac, 0xae, 0xaf, - 0x08, 0x49, 0x74, 0x01, 0x84, 0xf6, 0xf6, 0x2b, 0xc6, 0x91, 0xb1, 0x50, - 0x22, 0xb8, 0x88, 0x54, 0xce, 0x95, 0x19, 0xe4, 0xb8, 0xfe, 0x5d, 0xf9, - 0x5b, 0x4c, 0xfa, 0x34, 0x9d, 0xde, 0x8a, 0x9a, 0xd0, 0x95, 0x41, 0x75, - 0x18, 0x90, 0x4c, 0x65, 0xb2, 0xc3, 0x41, 0x45, 0x49, 0x83, 0x13, 0x33, - 0x0b, 0x3a, 0x94, 0xc0, 0x62, 0x01, 0x80, 0x8a, 0x00, 0x7c, 0x72, 0xd4, - 0xcb, 0x0c, 0x11, 0x05, 0x61, 0x93, 0xfc, 0x00, 0x00, 0x00, 0x06, 0x65, - 0xeb, 0x2f, 0x9b, 0x6e, 0xd3, 0x2c, 0x10, 0xcd, 0xb3, 0xba, 0x68, 0x95, - 0x2d, 0xf2, 0xbb, 0x2c, 0x1a, 0x26, 0xe2, 0x18, 0xb2, 0xe6, 0xb2, 0x9b, - 0xd3, 0xe5, 0x21, 0xb8, 0x06, 0x3b, 0x9c, 0x28, 0x6a, 0x92, 0x60, 0x08, - 0x26, 0xad, 0x60, 0xdb, 0x30, 0xbf, 0x86, 0x02, 0x22, 0xa9, 0x1d, 0x5d, - 0x85, 0xe5, 0xa3, 0xf8, 0x97, 0xb2, 0xdb, 0xa7, 0xfd, 0x41, 0xc8, 0xe4, - 0x79, 0x7c, 0x0a, 0xe0, 0x58, 0x63, 0x75, 0xa9, 0x50, 0xb4, 0x11, 0x18, - 0x32, 0x45, 0x62, 0xbb, 0xe1, 0xff, 0xdc, 0xe1, 0xcd, 0x61, 0x0a, 0x85, - 0x15, 0x01, 0x0b, 0x22, 0x8c, 0x30, 0xcb, 0xb1, 0xce, 0x48, 0x0e, 0xff, - 0xf1, 0x50, 0x80, 0x30, 0xdf, 0xfc, 0x21, 0x1a, 0xcf, 0xc4, 0xf6, 0x76, - 0x60, 0x00, 0x98, 0xb6, 0xb1, 0x64, 0x48, 0x3b, 0x0a, 0xb5, 0x89, 0x02, - 0x05, 0xf6, 0xd6, 0xda, 0x1b, 0xd3, 0x6d, 0x1b, 0xd7, 0x1e, 0xbc, 0xf7, - 0xed, 0xef, 0xed, 0x5f, 0x1f, 0x9e, 0xbb, 0xfd, 0xe7, 0x1d, 0xf4, 0x0c, - 0xee, 0x80, 0xcc, 0xe2, 0xa8, 0x09, 0x23, 0x5d, 0x4d, 0xce, 0x15, 0x0a, - 0xf3, 0x3e, 0x18, 0x79, 0x79, 0x4a, 0x22, 0xaa, 0x1c, 0x74, 0x9a, 0xae, - 0x52, 0xf4, 0x47, 0x59, 0x29, 0x59, 0x59, 0x23, 0x15, 0x9e, 0xf3, 0xbe, - 0x16, 0xcf, 0x43, 0x0b, 0x05, 0xd2, 0xf4, 0xa8, 0x18, 0xbe, 0xab, 0x9b, - 0x28, 0xf8, 0x0d, 0x87, 0xcf, 0x1d, 0x6b, 0x1e, 0x13, 0xeb, 0xa9, 0xc1, - 0xf4, 0x34, 0x5a, 0x23, 0x97, 0xab, 0x2f, 0xc0, 0x53, 0x61, 0x67, 0xcb, - 0x1c, 0xed, 0x3f, 0xee, 0x07, 0x4d, 0x01, 0xc1, 0x08, 0x55, 0xae, 0xd6, - 0xb4, 0x44, 0x6a, 0x6b, 0x75, 0x5f, 0xd7, 0xc2, 0x04, 0xc1, 0xb7, 0x3c, - 0xc4, 0xa6, 0xa2, 0x94, 0x02, 0xb9, 0x01, 0xab, 0xb9, 0xae, 0x71, 0x71, - 0xbb, 0xe3, 0x01, 0x41, 0xaf, 0xd7, 0xca, 0x40, 0x59, 0xea, 0x2e, 0xef, - 0x2b, 0x29, 0x13, 0x48, 0xf5, 0xcd, 0xa4, 0x17, 0xbc, 0x70, 0xeb, 0xf8, - 0xf2, 0xae, 0xbe, 0x09, 0xce, 0xf8, 0xfd, 0x02, 0x5d, 0x20, 0x03, 0xe7, - 0xd1, 0x8d, 0x2f, 0x28, 0x88, 0x54, 0xc0, 0xea, 0xcd, 0x4a, 0xc0, 0x66, - 0x78, 0x5d, 0x9b, 0xec, 0x30, 0x71, 0xbe, 0xfb, 0xb2, 0x5e, 0xfc, 0xb8, - 0xe9, 0xd7, 0xc1, 0xd4, 0xab, 0x58, 0xb4, 0x17, 0xc2, 0xee, 0xb9, 0x37, - 0x87, 0x91, 0xf0, 0x6d, 0x90, 0xfe, 0xf9, 0xbe, 0xbe, 0x4f, 0xdb, 0xd8, - 0x84, 0x46, 0x28, 0x61, 0x5b, 0x95, 0x41, 0x08, 0x19, 0x34, 0x0c, 0x1c, - 0x2f, 0x00, 0xd0, 0x36, 0x25, 0x11, 0xa8, 0xda, 0xb6, 0x36, 0x4b, 0x41, - 0x6f, 0xa8, 0xe6, 0x53, 0x61, 0x44, 0x73, 0x14, 0xcb, 0x0c, 0x0d, 0x87, - 0x61, 0x41, 0xd8, 0x5c, 0x7f, 0x00, 0x00, 0x00, 0x07, 0x1c, 0xee, 0xf2, - 0xea, 0x55, 0xd5, 0x2a, 0xf7, 0x78, 0x10, 0xc2, 0x26, 0xab, 0x83, 0x53, - 0x07, 0x41, 0x6c, 0xc0, 0x26, 0xe4, 0x0b, 0xa2, 0xd2, 0x66, 0x13, 0x47, - 0x0d, 0x24, 0x7e, 0xca, 0x65, 0x41, 0x66, 0x0b, 0x5f, 0x9b, 0x89, 0xd0, - 0xa5, 0x40, 0xad, 0x93, 0xbe, 0x99, 0x40, 0x84, 0xbc, 0x4e, 0xa7, 0xaf, - 0xbc, 0x09, 0xa8, 0xb7, 0x3a, 0xdc, 0x1b, 0x95, 0x17, 0x69, 0xf3, 0x94, - 0x09, 0x3a, 0x99, 0x8c, 0x40, 0x37, 0x00, 0x9d, 0xdc, 0x35, 0x3e, 0x71, - 0x39, 0x4a, 0x54, 0xc4, 0x31, 0x53, 0xa9, 0x18, 0xcb, 0x30, 0x61, 0x04, - 0x88, 0x5c, 0xb6, 0x49, 0x37, 0x95, 0x2a, 0x22, 0xe6, 0x73, 0xb9, 0xfb, - 0x1d, 0x3c, 0x10, 0x81, 0xc0, 0xff, 0xf1, 0x50, 0x80, 0x2f, 0xdf, 0xfc, - 0x21, 0x1a, 0xcf, 0xf5, 0x67, 0x5f, 0x44, 0x10, 0x97, 0xb6, 0xc1, 0xa4, - 0x96, 0x17, 0x63, 0x10, 0xc3, 0x02, 0x00, 0x00, 0x00, 0x4a, 0x03, 0xcf, - 0x89, 0x5f, 0xac, 0x9e, 0xff, 0x5e, 0xc0, 0x49, 0x3a, 0x26, 0xeb, 0xed, - 0xed, 0xa2, 0x4c, 0x27, 0xa1, 0x42, 0x40, 0x28, 0xe3, 0x6c, 0xc6, 0xb3, - 0x04, 0x3c, 0xa5, 0x1a, 0x6f, 0x57, 0xa6, 0xfb, 0x4b, 0xcd, 0xfd, 0xc7, - 0x5f, 0xac, 0x47, 0x48, 0x79, 0xfd, 0x57, 0x9f, 0xb3, 0x89, 0x0a, 0x79, - 0xdb, 0xb9, 0xb8, 0xe7, 0x40, 0x5b, 0x02, 0x0e, 0x9c, 0x09, 0x67, 0xf7, - 0x9e, 0x40, 0xaa, 0x57, 0x0d, 0xb8, 0x2b, 0xa6, 0x27, 0x82, 0xb0, 0x25, - 0xe8, 0x9f, 0x99, 0xf1, 0x9a, 0x37, 0xad, 0x7c, 0xbc, 0x31, 0xc6, 0x74, - 0x26, 0x88, 0xea, 0xe8, 0xba, 0xb4, 0x01, 0xa7, 0xbd, 0x1f, 0xa3, 0x45, - 0x00, 0x29, 0x9d, 0x4c, 0x26, 0xb6, 0xac, 0xb0, 0xa5, 0xdd, 0xaf, 0x3f, - 0xd2, 0xa8, 0x19, 0xdc, 0xd9, 0x8e, 0x70, 0xbb, 0x98, 0x4d, 0x40, 0x2b, - 0xa3, 0x10, 0x98, 0xa2, 0x2c, 0x11, 0xb2, 0x54, 0x8a, 0xc8, 0xc3, 0x91, - 0xfe, 0x9e, 0x70, 0xd4, 0x94, 0x9b, 0xb1, 0x28, 0x94, 0x14, 0x0c, 0xf9, - 0xd0, 0x54, 0x05, 0x68, 0xcd, 0x25, 0xa6, 0x5e, 0x2b, 0xcc, 0xbb, 0xe5, - 0xec, 0x0b, 0x03, 0x4a, 0x69, 0x9d, 0xc5, 0x20, 0x90, 0x41, 0xf6, 0xa2, - 0x0c, 0x24, 0xc2, 0xce, 0x40, 0x4f, 0x60, 0x50, 0x16, 0x45, 0x63, 0x79, - 0xce, 0xad, 0x1c, 0xe8, 0xe5, 0xea, 0x8b, 0x0c, 0x16, 0x73, 0x34, 0x55, - 0x15, 0x40, 0x02, 0xd4, 0xaf, 0x3d, 0xf5, 0x1d, 0x11, 0x4f, 0x66, 0x1f, - 0xea, 0xa6, 0xd9, 0x4a, 0x83, 0x26, 0x75, 0x39, 0x69, 0x38, 0x03, 0x3a, - 0x30, 0xa1, 0x7b, 0x17, 0x8e, 0x49, 0x32, 0x49, 0xd6, 0x27, 0x18, 0xf0, - 0xe3, 0x58, 0x16, 0xa3, 0x06, 0x5e, 0x97, 0x65, 0x61, 0xd8, 0x58, 0x76, - 0x15, 0x1f, 0xc0, 0x00, 0x00, 0x01, 0x2b, 0x25, 0x2a, 0x2f, 0x13, 0x0a, - 0xb0, 0x87, 0xf6, 0x5b, 0x0b, 0x3a, 0x52, 0xef, 0x14, 0x3a, 0xb0, 0x2f, - 0xd0, 0x2e, 0x66, 0x25, 0x24, 0x54, 0x7a, 0x63, 0x9d, 0x60, 0x9f, 0x4f, - 0xb9, 0x7c, 0xa5, 0xd2, 0xb1, 0xce, 0x84, 0x6e, 0x45, 0x95, 0xde, 0x78, - 0x42, 0x8d, 0x62, 0x7c, 0x36, 0x5d, 0x8f, 0x10, 0x8d, 0x8e, 0x2a, 0x26, - 0xa3, 0x1f, 0x53, 0xd7, 0x3a, 0x64, 0x11, 0x79, 0x85, 0xcd, 0x41, 0x46, - 0x6a, 0x1c, 0x42, 0xe0, 0xef, 0x9d, 0x4d, 0x7b, 0x49, 0xc5, 0xe2, 0x74, - 0x59, 0x1d, 0x1d, 0x10, 0x05, 0x53, 0xe3, 0xa7, 0x8f, 0x44, 0x30, 0x0b, - 0x17, 0x85, 0xd1, 0xd7, 0x01, 0x79, 0x2a, 0x12, 0x1d, 0x7f, 0xfb, 0x8a, - 0xc1, 0x63, 0x80, 0xff, 0xf1, 0x50, 0x80, 0x30, 0xdf, 0xfc, 0x21, 0x1a, - 0xcf, 0xee, 0xe5, 0xff, 0x60, 0x50, 0x95, 0xb6, 0xc1, 0xac, 0x2c, 0x3b, - 0x0b, 0x9d, 0x52, 0xc3, 0x02, 0xa1, 0x50, 0xa4, 0xa8, 0x01, 0x40, 0xe3, - 0x9e, 0x39, 0xfd, 0x77, 0xcf, 0xde, 0xfc, 0x8b, 0xe3, 0x2b, 0x3a, 0xed, - 0x36, 0x4f, 0x27, 0xb4, 0x91, 0x2c, 0x59, 0x9c, 0x73, 0x33, 0x39, 0x77, - 0xac, 0x1c, 0x54, 0xf9, 0xd7, 0x24, 0x85, 0xb0, 0xcf, 0x5e, 0x70, 0x99, - 0x81, 0x1b, 0xcc, 0x77, 0x1a, 0xb9, 0xe0, 0xba, 0x47, 0x53, 0xe5, 0xba, - 0xe2, 0x29, 0x37, 0x52, 0xa0, 0x5d, 0x48, 0x87, 0x0a, 0x81, 0x30, 0xac, - 0x52, 0xfd, 0x7c, 0x53, 0x9c, 0x67, 0xca, 0x50, 0x1e, 0x4e, 0x54, 0x28, - 0xb8, 0x91, 0x36, 0x8b, 0xab, 0x9e, 0x4d, 0x14, 0x0e, 0x49, 0x2e, 0xa2, - 0x98, 0xa1, 0xd5, 0xa2, 0xda, 0x02, 0x82, 0x53, 0x36, 0x22, 0x94, 0xa2, - 0x93, 0x29, 0x93, 0x94, 0x02, 0x9a, 0x08, 0xb8, 0xaa, 0xd2, 0x44, 0xb0, - 0x29, 0x33, 0x59, 0xb2, 0xdf, 0xdc, 0xfa, 0xad, 0x99, 0x4c, 0x19, 0x09, - 0xca, 0x45, 0xdc, 0x02, 0x96, 0xd3, 0xe1, 0x6c, 0xb0, 0xa8, 0x2d, 0x88, - 0x4c, 0x06, 0x37, 0x85, 0x47, 0x51, 0xf5, 0x3b, 0xfc, 0x91, 0x32, 0x84, - 0x56, 0xee, 0x45, 0x40, 0xa2, 0xea, 0x4e, 0x98, 0xa4, 0x94, 0x27, 0x8a, - 0x68, 0xab, 0x2e, 0xb5, 0x91, 0xae, 0x73, 0x8a, 0x00, 0x77, 0x41, 0x1a, - 0x24, 0x9d, 0x46, 0x66, 0x9e, 0x20, 0x5a, 0x54, 0x63, 0xbd, 0xd7, 0x79, - 0xe8, 0xe5, 0x9e, 0xfa, 0xbc, 0x4d, 0x5f, 0x4f, 0xb0, 0xb8, 0x50, 0x11, - 0x00, 0x08, 0x2e, 0xef, 0xe0, 0x73, 0xc8, 0x7b, 0x5a, 0x86, 0xaa, 0x2e, - 0x99, 0xc0, 0x11, 0x82, 0x49, 0x78, 0xf7, 0xf0, 0x35, 0x1c, 0x83, 0xa9, - 0x9f, 0x47, 0xf7, 0xf4, 0xf1, 0xc7, 0xd3, 0x85, 0xc7, 0xa2, 0x6f, 0xe5, - 0xea, 0x9a, 0xce, 0x12, 0xb4, 0xab, 0x1c, 0x1a, 0xc2, 0xc3, 0xb0, 0xb8, - 0xfe, 0x00, 0x00, 0x00, 0x03, 0x0a, 0xbc, 0x9c, 0x56, 0xe5, 0x54, 0xa8, - 0x1d, 0x67, 0x5c, 0x05, 0x2a, 0x1a, 0xb1, 0xff, 0xaf, 0xb4, 0x2d, 0x2e, - 0xf8, 0x55, 0xab, 0x18, 0xa4, 0x54, 0x25, 0x3b, 0x54, 0xc7, 0xdf, 0x6f, - 0xb2, 0xda, 0x04, 0x54, 0x63, 0xb3, 0xc6, 0x4b, 0x2b, 0x4b, 0x87, 0x77, - 0xe1, 0x1a, 0x55, 0x9a, 0x0d, 0x17, 0x75, 0xc1, 0x99, 0x46, 0xdc, 0x02, - 0xbc, 0x40, 0xac, 0x29, 0xa5, 0xa0, 0xf6, 0x62, 0x04, 0xbc, 0x23, 0x3e, - 0x7e, 0xa1, 0x02, 0x50, 0x00, 0xf5, 0x86, 0xbc, 0x58, 0x95, 0x41, 0x08, - 0x16, 0x6b, 0xd0, 0x19, 0x2c, 0x21, 0x19, 0x13, 0x84, 0x80, 0x46, 0x30, - 0x14, 0x60, 0x83, 0xd6, 0x58, 0xc6, 0x5c, 0x4c, 0xf1, 0x31, 0xa5, 0xcc, - 0x15, 0x14, 0xc3, 0xa8, 0xf6, 0xe9, 0x6a, 0x49, 0xc0, 0xff, 0xf1, 0x50, - 0x80, 0x2f, 0x5f, 0xfc, 0x21, 0x1a, 0xcb, 0xfd, 0xbd, 0xb7, 0x04, 0x08, - 0x95, 0xb6, 0xc0, 0xd8, 0x76, 0x15, 0x1d, 0x85, 0xd2, 0x24, 0x61, 0x18, - 0x58, 0x50, 0x14, 0x01, 0x51, 0x50, 0x54, 0x54, 0x54, 0x2f, 0x64, 0xae, - 0x29, 0xbf, 0xc7, 0x5f, 0x6f, 0xbb, 0xd8, 0x66, 0x49, 0xf9, 0x34, 0x24, - 0x49, 0x3d, 0x97, 0x91, 0x02, 0xc8, 0x97, 0x84, 0x44, 0x71, 0x24, 0x3e, - 0xce, 0x7e, 0xeb, 0xc3, 0x1c, 0xf1, 0x7d, 0x6d, 0x0b, 0xa4, 0x9b, 0xef, - 0xc3, 0x1d, 0x7f, 0xf7, 0x8d, 0xd0, 0xbb, 0x27, 0xc0, 0x46, 0xcf, 0x9b, - 0x4e, 0x35, 0xe3, 0x23, 0xcd, 0x19, 0x52, 0xba, 0x53, 0x54, 0x22, 0x5e, - 0xf2, 0x06, 0xcf, 0x3e, 0x7c, 0xb4, 0xb9, 0x5e, 0xfe, 0x11, 0x10, 0x28, - 0x6f, 0x99, 0x1f, 0x01, 0x06, 0xef, 0x8d, 0x7f, 0x97, 0x30, 0xdd, 0x72, - 0xca, 0x6b, 0x31, 0xa5, 0x08, 0xe4, 0x94, 0x8d, 0xf2, 0xb7, 0xa8, 0x1a, - 0x21, 0x46, 0x41, 0x15, 0xa6, 0x14, 0x89, 0x54, 0xd4, 0xad, 0xcb, 0xd2, - 0xde, 0x64, 0x98, 0x2a, 0xe2, 0xf2, 0x84, 0xe1, 0x89, 0x31, 0x43, 0x17, - 0x97, 0x46, 0x18, 0xa6, 0xc2, 0x41, 0x12, 0x9b, 0x4d, 0x98, 0x6a, 0x7b, - 0x8b, 0xc6, 0x2e, 0x41, 0x69, 0x41, 0x52, 0x81, 0x61, 0x1c, 0x90, 0x01, - 0x91, 0xc5, 0x8a, 0x58, 0xed, 0x4f, 0x00, 0x18, 0x1f, 0xce, 0xf0, 0xb5, - 0xb6, 0xe3, 0xeb, 0xba, 0x21, 0x22, 0x9c, 0xe4, 0x67, 0x0c, 0xc9, 0x42, - 0x2b, 0x1c, 0xf5, 0x58, 0x3c, 0x96, 0x61, 0xc1, 0x8e, 0x2c, 0x0c, 0x0e, - 0x13, 0x23, 0x0f, 0x79, 0xd2, 0xa6, 0x3c, 0x65, 0xae, 0x8a, 0xea, 0x62, - 0x81, 0x64, 0x6c, 0x14, 0xe8, 0x95, 0xd4, 0x9c, 0xc7, 0xb8, 0xbb, 0x48, - 0x9a, 0x01, 0x4e, 0x5c, 0x50, 0x36, 0x9f, 0x06, 0x89, 0x0b, 0x59, 0x24, - 0xce, 0x94, 0xba, 0x20, 0xec, 0x2a, 0x3f, 0x80, 0x00, 0x00, 0x00, 0xca, - 0x86, 0x4e, 0xb7, 0xac, 0xad, 0xda, 0x0d, 0x61, 0x3f, 0x22, 0x3c, 0xa4, - 0x0d, 0x87, 0xdf, 0xb7, 0xa6, 0xa3, 0x71, 0x20, 0x80, 0xaf, 0x31, 0xf7, - 0x0f, 0x44, 0xda, 0x6e, 0xf2, 0xe2, 0xb0, 0x8a, 0x5b, 0x64, 0x62, 0x8f, - 0x1a, 0x3d, 0xc8, 0x49, 0x61, 0x27, 0xc0, 0xa7, 0xf3, 0x9a, 0x12, 0xc4, - 0x2a, 0x00, 0x31, 0xd3, 0xa2, 0x31, 0xd8, 0x22, 0xae, 0x07, 0x83, 0xcb, - 0x2e, 0x66, 0x48, 0xd6, 0x73, 0x20, 0xc7, 0x0b, 0x40, 0x50, 0xfc, 0xff, - 0xd6, 0xa7, 0x4c, 0x10, 0x3f, 0x38, 0x13, 0xaa, 0xfb, 0xf9, 0x17, 0xfb, - 0x05, 0x1c, 0xa3, 0x08, 0x20, 0xa5, 0x26, 0x64, 0xf5, 0xf1, 0xc4, 0x2c, - 0xf8, 0xf8, 0x86, 0xf8, 0xeb, 0x41, 0x3b, 0x05, 0x0c, 0xfd, 0xdf, 0xb8, - 0xd3, 0x01, 0xc0, 0xff, 0xf1, 0x50, 0x80, 0x2e, 0xbf, 0xfc, 0x21, 0x1a, - 0xc9, 0xec, 0x5f, 0xbf, 0x40, 0x80, 0x93, 0xb6, 0xc1, 0xa5, 0x86, 0x66, - 0x11, 0x85, 0x86, 0x00, 0x00, 0x00, 0x2f, 0x0f, 0x8f, 0x7f, 0x6d, 0xf3, - 0xaf, 0x3f, 0x5f, 0xa7, 0xe1, 0xd0, 0x71, 0x63, 0xf5, 0xdd, 0x93, 0xe5, - 0xc1, 0x4b, 0xc8, 0x24, 0xd8, 0xd4, 0x02, 0xb2, 0x64, 0xbf, 0x06, 0xfb, - 0xcd, 0xff, 0x57, 0xa3, 0x63, 0xbd, 0x65, 0x40, 0xc9, 0x2e, 0xc7, 0x9c, - 0x90, 0xa0, 0xeb, 0xd6, 0x2b, 0xe1, 0x30, 0x6f, 0x1a, 0x8f, 0x39, 0xbe, - 0xe9, 0xb6, 0x40, 0x75, 0xf6, 0x30, 0xa1, 0x0a, 0x64, 0xb1, 0xa1, 0x31, - 0x3b, 0xc4, 0x45, 0x18, 0x2f, 0xe0, 0xb1, 0x44, 0xee, 0x3d, 0x75, 0xe1, - 0x29, 0xe2, 0xec, 0xff, 0xc7, 0xfe, 0xd1, 0x35, 0x53, 0xbe, 0x30, 0xd6, - 0xa5, 0x0c, 0xeb, 0x4a, 0x65, 0x53, 0x94, 0xc5, 0x65, 0xb7, 0xfb, 0xff, - 0xa5, 0x6a, 0x51, 0x34, 0x32, 0xa9, 0x28, 0x16, 0x44, 0x31, 0xbe, 0x4c, - 0xea, 0xed, 0x80, 0x19, 0x67, 0x32, 0x5d, 0xd1, 0x00, 0xad, 0xdb, 0x67, - 0x10, 0x10, 0x50, 0xac, 0xc8, 0x94, 0x95, 0x5d, 0xe7, 0x63, 0x79, 0x42, - 0x24, 0x3c, 0x2d, 0xcd, 0xb7, 0x6e, 0xdf, 0x82, 0x0f, 0x6a, 0x90, 0x32, - 0x45, 0x5c, 0xd9, 0x7c, 0xeb, 0xec, 0xfa, 0x35, 0x51, 0x20, 0x2a, 0xf0, - 0x78, 0x67, 0xd9, 0x24, 0xc4, 0xb4, 0x8c, 0xc0, 0x80, 0x8c, 0x4a, 0x05, - 0x66, 0x31, 0xf0, 0x9d, 0x6c, 0xb9, 0xe8, 0x8a, 0x34, 0x4e, 0x6e, 0xb8, - 0xd5, 0x5d, 0xb7, 0x85, 0xe4, 0xb4, 0x8f, 0x3b, 0x93, 0x1a, 0x35, 0x3e, - 0x84, 0x9d, 0x0e, 0xf8, 0x43, 0xd7, 0x40, 0xee, 0xd4, 0x22, 0x6f, 0x2b, - 0x29, 0xdf, 0x4f, 0xdb, 0xc3, 0x65, 0xbb, 0x71, 0x91, 0xe6, 0x1b, 0x1e, - 0x8c, 0x0e, 0x89, 0x3b, 0x7b, 0x12, 0xc4, 0x42, 0xb0, 0xa8, 0xfe, 0x00, - 0x00, 0x00, 0x03, 0x75, 0x77, 0x37, 0x5d, 0x56, 0x64, 0xdd, 0xa0, 0x77, - 0x4c, 0xe4, 0x21, 0x1c, 0xfd, 0x1a, 0x56, 0x65, 0x5f, 0xf4, 0xac, 0xd8, - 0xed, 0x1e, 0x32, 0x73, 0xa8, 0x2f, 0x88, 0x33, 0x71, 0xcd, 0x9d, 0xb2, - 0xde, 0x9a, 0xd8, 0x3d, 0xed, 0x5f, 0xb1, 0x7b, 0x76, 0xa4, 0x70, 0x49, - 0xc5, 0x99, 0x6b, 0x4b, 0x89, 0x6d, 0xa5, 0x50, 0x84, 0xa6, 0x3e, 0xfb, - 0x19, 0x39, 0x7f, 0x84, 0x6f, 0xa5, 0x46, 0xf0, 0xbe, 0x5f, 0x27, 0x16, - 0x69, 0xcb, 0x0f, 0x18, 0xd8, 0x63, 0x98, 0x60, 0x17, 0x34, 0x48, 0xb4, - 0x59, 0x6b, 0x30, 0xdd, 0xa0, 0xf9, 0xb5, 0x9a, 0x6d, 0xbe, 0x1d, 0xff, - 0x1e, 0x96, 0x50, 0xc6, 0x04, 0xc3, 0x35, 0xbc, 0x8b, 0x28, 0x41, 0x50, - 0x7b, 0x74, 0xb2, 0x47, 0xff, 0xf1, 0x50, 0x80, 0x2d, 0x3f, 0xfc, 0x21, - 0x1a, 0xcf, 0xfb, 0xbb, 0xbf, 0x40, 0x00, 0x94, 0xb7, 0xcb, 0x04, 0x6a, - 0x41, 0x1a, 0x05, 0x89, 0x01, 0x00, 0x00, 0x00, 0x1a, 0xdf, 0x1c, 0xfc, - 0x2f, 0x73, 0xd7, 0xd7, 0xe3, 0xdf, 0xa9, 0xc0, 0xb8, 0xe7, 0x58, 0x64, - 0x12, 0x1c, 0x80, 0xbe, 0x2b, 0x26, 0x09, 0xbe, 0x0a, 0x4d, 0x21, 0x95, - 0x45, 0xf4, 0xa5, 0x4c, 0xc8, 0x89, 0xc9, 0x1c, 0x6b, 0x99, 0x27, 0x24, - 0x51, 0x95, 0x6a, 0x25, 0xa7, 0x45, 0xec, 0x33, 0x5e, 0x8b, 0xd4, 0x9d, - 0xa3, 0x8b, 0x11, 0xc9, 0x43, 0x99, 0x43, 0x8e, 0x04, 0x46, 0x8d, 0x02, - 0x05, 0x33, 0xdb, 0xba, 0xa2, 0x53, 0xa7, 0xc9, 0x99, 0xce, 0x3c, 0xee, - 0x9a, 0x35, 0xa3, 0x1f, 0xf0, 0x3f, 0x59, 0xc4, 0xbd, 0x19, 0xcf, 0x0c, - 0x95, 0x61, 0x84, 0x4e, 0x58, 0xdc, 0x5a, 0x5a, 0xbf, 0xe2, 0x7e, 0x67, - 0xaf, 0x33, 0x0b, 0xa0, 0xc2, 0x40, 0x00, 0x71, 0x3a, 0x7c, 0xa4, 0x89, - 0x0c, 0x77, 0x6a, 0x5f, 0x89, 0xfc, 0x75, 0x14, 0xe9, 0x75, 0x3e, 0xd0, - 0x78, 0x97, 0x11, 0xfa, 0x7e, 0x8e, 0x8e, 0x45, 0x01, 0xdb, 0x7a, 0x46, - 0xe3, 0x7b, 0x4c, 0x42, 0x93, 0xcf, 0xb9, 0xd1, 0x71, 0x20, 0x37, 0x3b, - 0xdb, 0x55, 0x58, 0xd4, 0x1d, 0xab, 0xc9, 0x7c, 0x02, 0x85, 0xf8, 0xbd, - 0xde, 0xbc, 0x7f, 0x49, 0x7d, 0x40, 0x80, 0x4c, 0xe4, 0xac, 0x43, 0xe4, - 0x16, 0xc7, 0x3e, 0x0d, 0x3d, 0xd9, 0x4b, 0x54, 0xb5, 0xa9, 0xad, 0x49, - 0x00, 0xca, 0xb5, 0x81, 0x18, 0x06, 0x32, 0x04, 0x30, 0xe9, 0xda, 0x73, - 0x32, 0x55, 0x61, 0xc4, 0x5c, 0x60, 0xb5, 0x52, 0x54, 0x1d, 0x97, 0x83, - 0xed, 0xdc, 0x4c, 0xbc, 0xec, 0xe2, 0x2c, 0x92, 0x82, 0x4a, 0x8a, 0x5b, - 0x10, 0x45, 0x7a, 0x79, 0x73, 0xa5, 0x2d, 0xb0, 0x57, 0x31, 0x8f, 0xe0, - 0x00, 0x00, 0x00, 0x30, 0xad, 0x6b, 0xd7, 0x53, 0x9a, 0xe3, 0xb8, 0xb0, - 0xcf, 0xc0, 0x74, 0x49, 0xf5, 0x48, 0xc1, 0xb7, 0xe7, 0xef, 0x30, 0xa3, - 0x90, 0xd1, 0x57, 0x43, 0x24, 0x11, 0x78, 0xa1, 0xbe, 0x78, 0x19, 0xc7, - 0x6f, 0xd5, 0x07, 0xa7, 0xa4, 0x76, 0xe8, 0xab, 0x9c, 0x59, 0x2b, 0x36, - 0x88, 0x30, 0x39, 0x6d, 0x89, 0x51, 0x5b, 0x28, 0xc1, 0x93, 0xd0, 0x32, - 0xa0, 0xaf, 0x6c, 0x61, 0x32, 0x0a, 0xd5, 0x5e, 0x6c, 0x67, 0x2e, 0xff, - 0xf2, 0xb9, 0xe6, 0x6e, 0xf4, 0x36, 0xe9, 0x44, 0x65, 0x41, 0x54, 0xb8, - 0x49, 0x0f, 0x2f, 0xe0, 0xfd, 0xbd, 0x35, 0xcd, 0x32, 0xac, 0x0b, 0x42, - 0xff, 0x25, 0xfd, 0x7a, 0xc7, 0xff, 0xf1, 0x50, 0x80, 0x30, 0x9f, 0xfc, - 0x21, 0x1a, 0xcf, 0xff, 0x7f, 0xff, 0x04, 0x04, 0x93, 0xb7, 0x41, 0x6c, - 0x28, 0x39, 0x41, 0x91, 0x44, 0xc5, 0x80, 0x80, 0x00, 0x00, 0x09, 0x5a, - 0xdd, 0xe7, 0xb7, 0xa9, 0x5f, 0x7a, 0xe3, 0x3a, 0xf2, 0x3a, 0x2e, 0xa3, - 0x95, 0xb8, 0x2a, 0x03, 0xda, 0xa3, 0x22, 0xf4, 0xcc, 0xe9, 0x22, 0x95, - 0xcb, 0x42, 0xa8, 0x44, 0x8e, 0xe3, 0x4a, 0x7e, 0xf6, 0xb5, 0x81, 0x02, - 0xfd, 0x0c, 0xc8, 0x3b, 0x89, 0xb5, 0xae, 0x29, 0x28, 0x53, 0x4f, 0x1e, - 0xab, 0xf2, 0xc5, 0xd2, 0x49, 0x90, 0xf3, 0x24, 0xab, 0xd4, 0xe2, 0xb2, - 0xe0, 0x5b, 0xf3, 0x8e, 0xde, 0xa9, 0xa7, 0x8a, 0x18, 0x91, 0x40, 0xa3, - 0x84, 0xf9, 0x11, 0x9c, 0xa8, 0x5c, 0x87, 0x7d, 0x6e, 0xc3, 0x82, 0x0e, - 0x5c, 0x3b, 0xd9, 0xf3, 0xe9, 0x6e, 0xf0, 0x78, 0x5a, 0x21, 0x77, 0x97, - 0x84, 0xf8, 0xe7, 0xd5, 0xf4, 0xe2, 0x6e, 0xb0, 0x2f, 0x0a, 0xe1, 0x89, - 0x49, 0x05, 0x45, 0xd6, 0x11, 0xf3, 0xcc, 0x05, 0x90, 0xb6, 0x13, 0x80, - 0xc7, 0x3b, 0x54, 0xe7, 0x38, 0x44, 0x61, 0xab, 0xb5, 0x69, 0xca, 0xa4, - 0xbc, 0xe6, 0x05, 0xee, 0xa3, 0x5e, 0xaa, 0x70, 0xa9, 0xaf, 0xba, 0xf7, - 0x39, 0x31, 0xa8, 0x3c, 0x3e, 0xee, 0xae, 0x72, 0x37, 0x3f, 0x4e, 0x1c, - 0x5e, 0xa9, 0xd0, 0xf5, 0x7e, 0xf0, 0x7d, 0x92, 0xe7, 0xcb, 0x81, 0x99, - 0xab, 0x84, 0xc9, 0xb6, 0xa0, 0x57, 0xd1, 0x05, 0xcc, 0xaa, 0x0b, 0x94, - 0xe0, 0x02, 0x61, 0xac, 0x8e, 0x2d, 0x28, 0x31, 0x89, 0xb8, 0x42, 0x09, - 0x93, 0x11, 0x01, 0x20, 0xc2, 0xd3, 0x99, 0x8a, 0xa4, 0x46, 0x50, 0xc1, - 0x1c, 0x64, 0xf9, 0xc4, 0x18, 0x80, 0x88, 0x40, 0x81, 0x00, 0x1c, 0x2c, - 0x8b, 0x15, 0x47, 0x11, 0x7b, 0x36, 0x9c, 0xfa, 0xff, 0x6d, 0x59, 0x41, - 0xbd, 0xba, 0x88, 0xb2, 0x93, 0x90, 0x24, 0x51, 0x58, 0x8b, 0x93, 0xb6, - 0xc1, 0xac, 0x28, 0x3b, 0x0b, 0x8f, 0xe0, 0x00, 0x32, 0xc0, 0x00, 0xcd, - 0xe9, 0x52, 0xe7, 0x32, 0xf7, 0x57, 0x9c, 0x05, 0x3f, 0x7f, 0x11, 0xb2, - 0xc2, 0x96, 0x70, 0x77, 0x84, 0x1e, 0x3b, 0x36, 0x1c, 0x5b, 0xd1, 0x70, - 0xf6, 0xf9, 0xb0, 0x94, 0x8a, 0x85, 0x5c, 0x67, 0x04, 0x9c, 0xff, 0xd6, - 0x6b, 0x38, 0x93, 0xa8, 0x12, 0x2d, 0xdc, 0xac, 0x0d, 0x22, 0xc2, 0x3b, - 0xb2, 0xbb, 0xd0, 0x10, 0xc7, 0xec, 0x6c, 0x02, 0x0a, 0x88, 0x51, 0x65, - 0x82, 0x7d, 0x64, 0xa5, 0x40, 0xe0, 0xee, 0x5e, 0xb2, 0x03, 0x47, 0x1e, - 0xca, 0x36, 0x23, 0x1d, 0x79, 0xc6, 0x3e, 0xb0, 0x51, 0x4c, 0x9d, 0x95, - 0xa1, 0x99, 0x14, 0x00, 0x51, 0xb1, 0x19, 0xb0, 0xd0, 0xc5, 0x65, 0x67, - 0x77, 0x2b, 0x0c, 0x2f, 0xf1, 0xb4, 0x0b, 0x24, 0xe0, 0xff, 0xf1, 0x50, - 0x80, 0x2f, 0x3f, 0xfc, 0x21, 0x1a, 0xcb, 0xad, 0xff, 0x7f, 0x44, 0x00, - 0x96, 0xb6, 0xc0, 0xd8, 0x72, 0xc3, 0x33, 0x12, 0x04, 0x04, 0xa0, 0x00, - 0x01, 0x79, 0xe7, 0xc6, 0xb7, 0xed, 0xcc, 0xe7, 0xf0, 0x9b, 0xfa, 0xe8, - 0x57, 0xb2, 0x8c, 0x6b, 0x1c, 0x1c, 0xff, 0xca, 0x16, 0x84, 0x3d, 0x1d, - 0x27, 0x32, 0x60, 0xe4, 0x44, 0x50, 0x04, 0xc8, 0xd8, 0x68, 0xc9, 0x22, - 0x73, 0x38, 0x5a, 0x46, 0x7b, 0x4b, 0x24, 0x46, 0x92, 0x19, 0x1b, 0x5c, - 0xff, 0x46, 0x4e, 0x36, 0x4b, 0x3b, 0x82, 0xac, 0xee, 0x49, 0x8b, 0x74, - 0x00, 0x26, 0xb9, 0xd5, 0x85, 0x6a, 0x3f, 0xb0, 0x9c, 0x5b, 0xee, 0x65, - 0x14, 0x95, 0xfa, 0x5f, 0xb3, 0xf0, 0xf7, 0xe3, 0x2e, 0x2c, 0xce, 0x8a, - 0x6b, 0x0a, 0x85, 0x6c, 0x91, 0x9a, 0x66, 0x70, 0xf3, 0xef, 0x90, 0xfc, - 0x6f, 0x69, 0x9e, 0x78, 0xcd, 0xdc, 0x5e, 0x54, 0xbc, 0xf3, 0x92, 0x14, - 0xc5, 0x53, 0x7e, 0x6f, 0x60, 0xca, 0x08, 0x35, 0xa8, 0xc1, 0x54, 0x48, - 0x0d, 0x7d, 0x20, 0x63, 0xb2, 0xa9, 0x9e, 0xa8, 0x61, 0xab, 0x71, 0x9e, - 0x36, 0x98, 0xa8, 0x77, 0x4f, 0x43, 0xa6, 0x94, 0xde, 0x7b, 0x5e, 0x0f, - 0xc4, 0x13, 0x85, 0xa4, 0x3d, 0x00, 0x6e, 0x77, 0x98, 0x7e, 0x0f, 0x3b, - 0xb7, 0xe2, 0xfb, 0x99, 0x3f, 0xfb, 0xc2, 0x96, 0x79, 0xec, 0xda, 0xa8, - 0xf9, 0x9d, 0x77, 0xed, 0xc6, 0xb2, 0xd3, 0x3d, 0x22, 0x2a, 0x24, 0xed, - 0x7a, 0xb2, 0x53, 0x50, 0xba, 0x97, 0x50, 0xa0, 0x4e, 0x12, 0xc3, 0x00, - 0x8b, 0x58, 0xd0, 0xf3, 0xb1, 0x23, 0x95, 0x91, 0x50, 0x45, 0x9c, 0x23, - 0xd2, 0x40, 0x2a, 0xa8, 0x04, 0x10, 0xce, 0x40, 0x90, 0xa8, 0x22, 0xd4, - 0x57, 0xd0, 0x21, 0x34, 0x00, 0x80, 0x8f, 0xc0, 0xe9, 0x80, 0x95, 0x4d, - 0xcb, 0x21, 0xba, 0xfb, 0xbb, 0x62, 0x71, 0x84, 0xd1, 0x12, 0xd6, 0xd8, - 0x35, 0x84, 0x87, 0x61, 0x71, 0xfc, 0x00, 0x00, 0x00, 0x06, 0x5d, 0x4c, - 0xbc, 0xe3, 0xbb, 0xac, 0x24, 0x12, 0x4d, 0x76, 0xf6, 0x27, 0x1a, 0xf8, - 0x1a, 0xec, 0x5a, 0x23, 0xb5, 0x0c, 0x55, 0x31, 0x8d, 0xc6, 0xc4, 0xcd, - 0x74, 0x0b, 0x25, 0x88, 0x76, 0x39, 0x3e, 0xbd, 0x20, 0x1d, 0x1f, 0xba, - 0xc9, 0x62, 0x51, 0x2c, 0x42, 0xad, 0x50, 0x02, 0x8d, 0x41, 0x80, 0x4a, - 0x1c, 0x06, 0xc6, 0xe1, 0x40, 0xc3, 0xea, 0x4c, 0x3b, 0xed, 0x84, 0xb3, - 0x85, 0xcb, 0x44, 0xe0, 0x06, 0x1e, 0x59, 0x65, 0x4d, 0xe5, 0x99, 0x26, - 0x36, 0xa1, 0x1e, 0xc6, 0x87, 0x1a, 0x3a, 0x9d, 0x07, 0x7a, 0x88, 0x50, - 0x03, 0x36, 0x6c, 0xe1, 0xb5, 0x14, 0x4a, 0xc6, 0x25, 0x4b, 0xdd, 0xe6, - 0x09, 0x38, 0xff, 0xf1, 0x50, 0x80, 0x32, 0x7f, 0xfc, 0x21, 0x1a, 0xcf, - 0x4f, 0x6b, 0x5f, 0x44, 0x60, 0x95, 0xb6, 0xc1, 0xac, 0x4a, 0x2b, 0x0b, - 0x9d, 0x55, 0x02, 0x19, 0x78, 0xb1, 0x96, 0xcb, 0xd6, 0xd9, 0x79, 0x61, - 0x8e, 0x39, 0x95, 0xed, 0xeb, 0x5b, 0xf6, 0xbb, 0xf5, 0xf8, 0xf6, 0x0b, - 0x39, 0x34, 0x18, 0xfd, 0x52, 0x9a, 0x24, 0x3a, 0x08, 0xfd, 0xeb, 0x83, - 0x9e, 0xc8, 0xdf, 0xe8, 0x62, 0xea, 0xa7, 0x5f, 0x9b, 0x06, 0x71, 0x7b, - 0xc2, 0x33, 0x98, 0x30, 0x5f, 0x55, 0x64, 0x2c, 0xbc, 0x88, 0x93, 0x14, - 0x20, 0x33, 0x90, 0xd7, 0x5d, 0xac, 0xab, 0xff, 0xb1, 0x13, 0x0b, 0x75, - 0x81, 0x56, 0xb0, 0x5f, 0xfd, 0x79, 0x41, 0x6c, 0x09, 0x56, 0x22, 0x23, - 0xd5, 0xcd, 0x32, 0x82, 0x0c, 0x34, 0xbf, 0x5b, 0x26, 0x18, 0x52, 0xce, - 0x3c, 0x30, 0xa4, 0x59, 0x33, 0xb2, 0x94, 0x3d, 0x8b, 0xc1, 0x39, 0xd1, - 0x82, 0x22, 0xf9, 0xcd, 0xf6, 0x78, 0xc0, 0x35, 0x0e, 0x57, 0x7a, 0xca, - 0xf3, 0x60, 0xa6, 0x7a, 0x19, 0x6a, 0x5a, 0x92, 0x86, 0xa7, 0xfa, 0x7b, - 0x76, 0x11, 0x8d, 0xa2, 0xe1, 0xba, 0x05, 0x54, 0x29, 0x01, 0x6d, 0xfd, - 0x60, 0x31, 0x2a, 0x5b, 0x81, 0xba, 0x17, 0x73, 0x12, 0x61, 0xa9, 0xf6, - 0x1c, 0x30, 0xb9, 0x5d, 0x33, 0xc8, 0x1b, 0x26, 0x42, 0x11, 0xd0, 0xa0, - 0x13, 0x7f, 0x25, 0x18, 0xea, 0x95, 0x35, 0x5b, 0x40, 0xf1, 0x00, 0x2f, - 0xba, 0x27, 0x29, 0xa5, 0x8c, 0x41, 0x5b, 0xfd, 0x91, 0x6b, 0x80, 0x53, - 0xdb, 0x53, 0x35, 0x8d, 0xc0, 0xa4, 0xae, 0xb7, 0xed, 0xec, 0xce, 0x3b, - 0x26, 0xcc, 0x9b, 0x9e, 0xde, 0xcd, 0x6f, 0xa3, 0x96, 0xaa, 0xa3, 0xd3, - 0xbc, 0x54, 0x7a, 0xb8, 0xf6, 0x82, 0x08, 0x5d, 0x08, 0x09, 0x16, 0x80, - 0x8a, 0x8c, 0xf9, 0x6a, 0x19, 0x29, 0x44, 0xca, 0xcd, 0x0a, 0x04, 0x40, - 0x79, 0x63, 0xea, 0x88, 0xd9, 0x69, 0xe0, 0x8d, 0x4e, 0x56, 0xdb, 0x06, - 0xb0, 0xb0, 0xec, 0x32, 0x3f, 0x80, 0x00, 0x00, 0x00, 0xca, 0xb9, 0x97, - 0x27, 0x31, 0x95, 0x50, 0x02, 0x69, 0xa4, 0x93, 0x03, 0xb6, 0xd4, 0x21, - 0x30, 0x03, 0x1b, 0x13, 0x16, 0x47, 0x9a, 0x30, 0x34, 0x47, 0xa6, 0xb8, - 0x15, 0x56, 0x16, 0x12, 0xbb, 0x6a, 0xf7, 0xc3, 0xbb, 0xa9, 0x11, 0x37, - 0xea, 0x13, 0x91, 0x76, 0x9b, 0x1f, 0xa9, 0xb0, 0x1c, 0x5e, 0xab, 0x5e, - 0xee, 0xa7, 0x9f, 0x71, 0xec, 0x71, 0xa6, 0xd9, 0xe5, 0x72, 0x2e, 0xd8, - 0x96, 0x72, 0x1b, 0xbe, 0x52, 0x46, 0x04, 0xee, 0x6c, 0x9c, 0xf7, 0x31, - 0x63, 0x80, 0x24, 0xb1, 0xb5, 0x93, 0xe3, 0x29, 0x50, 0xa6, 0x4e, 0xf9, - 0xd2, 0x76, 0x22, 0x9a, 0x64, 0x03, 0x8e, 0x17, 0xa8, 0x57, 0x00, 0xc0, - 0x3f, 0x36, 0x73, 0xc9, 0xab, 0xd2, 0x5d, 0x2a, 0xf7, 0x5d, 0xec, 0x8a, - 0x8b, 0x11, 0xf7, 0x0d, 0x2c, 0xe7, 0x15, 0xca, 0xf8, 0xff, 0xf1, 0x50, - 0x80, 0x2f, 0xdf, 0xfc, 0x21, 0x1a, 0xcf, 0xdc, 0xdf, 0xff, 0x77, 0xdb, - 0x96, 0xb6, 0xc1, 0xac, 0x48, 0x2b, 0x0c, 0x9d, 0x54, 0xc2, 0x05, 0x42, - 0x91, 0x50, 0x00, 0x29, 0xc7, 0x37, 0x9e, 0xde, 0xa5, 0x7c, 0x4a, 0xaf, - 0x8f, 0x61, 0x8c, 0xbd, 0x28, 0xb2, 0xdc, 0x7c, 0xbf, 0x5d, 0xc2, 0x9b, - 0x65, 0x51, 0x6d, 0xfd, 0x18, 0x7a, 0x28, 0x5a, 0x25, 0xac, 0x76, 0xc7, - 0x92, 0x8c, 0x7d, 0x0a, 0x6b, 0x79, 0x60, 0x45, 0x69, 0x8a, 0x91, 0xa6, - 0xac, 0x62, 0x12, 0xd3, 0x58, 0xa9, 0x21, 0xd7, 0x1d, 0xcc, 0xc1, 0x58, - 0xd8, 0x4f, 0x73, 0xc2, 0xad, 0x25, 0x12, 0x85, 0x62, 0xe0, 0x28, 0xd4, - 0x4e, 0x70, 0x82, 0x1c, 0x3d, 0xf3, 0x42, 0x1d, 0x09, 0x82, 0x0c, 0x0a, - 0x05, 0x0c, 0x7c, 0x38, 0x0a, 0x4b, 0xe4, 0xb7, 0xb8, 0xcb, 0x33, 0x81, - 0x4a, 0x53, 0xf0, 0x91, 0x2f, 0x2c, 0x05, 0x13, 0x1b, 0x2b, 0x91, 0x89, - 0x2c, 0xf0, 0x28, 0xa1, 0x86, 0x97, 0xc7, 0xaa, 0xa6, 0xf3, 0x12, 0x6b, - 0xc0, 0x45, 0x88, 0x64, 0x2b, 0x53, 0x15, 0x15, 0x38, 0x91, 0x5b, 0xd4, - 0x9b, 0xd9, 0x16, 0xbc, 0x30, 0x19, 0xf0, 0xfe, 0x97, 0x41, 0x37, 0x68, - 0x2a, 0xba, 0xf4, 0x11, 0x40, 0x8d, 0x97, 0xd5, 0x25, 0xa2, 0x84, 0xef, - 0xb7, 0x35, 0x55, 0x8a, 0xc3, 0x2c, 0x57, 0x70, 0x7a, 0xe4, 0x00, 0x8e, - 0x4a, 0x5d, 0xa6, 0xd1, 0x04, 0x68, 0xfd, 0xd5, 0x5a, 0xb8, 0x02, 0x3b, - 0x67, 0x37, 0xd7, 0xd2, 0xbc, 0xaa, 0xb2, 0x2e, 0x30, 0xab, 0x21, 0x57, - 0x5b, 0xf7, 0x6a, 0x17, 0x73, 0x32, 0x88, 0xa8, 0xc5, 0x71, 0xfb, 0x7e, - 0xb6, 0x2f, 0x62, 0x05, 0xc8, 0x92, 0x57, 0x38, 0x12, 0x32, 0xe5, 0x52, - 0x25, 0xbe, 0x5d, 0x56, 0xff, 0x13, 0xa7, 0x1d, 0x94, 0x51, 0xab, 0x75, - 0x3e, 0xdf, 0x5c, 0xb7, 0x78, 0x4b, 0x53, 0x2c, 0x30, 0x6b, 0x0a, 0x0e, - 0xc2, 0xe3, 0xe8, 0x00, 0x00, 0x00, 0x0a, 0xba, 0xcb, 0xab, 0x73, 0x6c, - 0xdf, 0x19, 0x00, 0x57, 0xdb, 0x53, 0x00, 0x28, 0x02, 0xe3, 0x8f, 0x4d, - 0x02, 0xc4, 0x14, 0x73, 0x41, 0x5d, 0x2c, 0x63, 0xe3, 0x9c, 0x2c, 0xf7, - 0x96, 0x3b, 0x40, 0xdc, 0xb0, 0xd4, 0x24, 0x28, 0xe6, 0xac, 0xe3, 0xe6, - 0x2d, 0x17, 0x62, 0x4b, 0x22, 0x95, 0x2e, 0x56, 0x92, 0xb9, 0xc6, 0x09, - 0xfb, 0xf5, 0x0d, 0x05, 0xf8, 0x00, 0xa3, 0x05, 0xe3, 0xb2, 0x97, 0x88, - 0xb4, 0x60, 0x1b, 0xad, 0xf4, 0x09, 0xaf, 0xfb, 0xe4, 0xa1, 0xa4, 0x4f, - 0xc2, 0x33, 0xa6, 0x14, 0x79, 0xef, 0xcb, 0xfc, 0x84, 0xb4, 0x62, 0x88, - 0x8a, 0x2f, 0x2c, 0x71, 0xc3, 0xd0, 0xe2, 0x5e, 0xf5, 0xe3, 0x6c, 0xf2, - 0x2e, 0xba, 0x3d, 0x78, 0x62, 0x4a, 0x9c, 0xff, 0xf1, 0x50, 0x80, 0x2e, - 0xff, 0xfc, 0x21, 0x1a, 0xcb, 0x77, 0xff, 0xff, 0x04, 0x10, 0x97, 0xb6, - 0xc0, 0xd8, 0x76, 0x19, 0x5a, 0xa5, 0x85, 0x01, 0x03, 0xcf, 0x80, 0x00, - 0x01, 0xad, 0xf5, 0xdf, 0x5d, 0xfc, 0x7b, 0xeb, 0x7f, 0x7a, 0xcf, 0xa7, - 0x91, 0x8c, 0xc8, 0x04, 0x99, 0x81, 0xea, 0x55, 0x55, 0x00, 0x0f, 0x1e, - 0xc9, 0xc4, 0xc4, 0x34, 0x3a, 0xac, 0xf6, 0x5b, 0x38, 0xbe, 0x86, 0x81, - 0xb5, 0x37, 0x5a, 0x86, 0x5e, 0x9f, 0x24, 0x0c, 0x87, 0x79, 0xae, 0xa5, - 0x7f, 0xd2, 0x46, 0xda, 0x28, 0x12, 0x32, 0xa0, 0xa8, 0x33, 0x4a, 0x74, - 0x7b, 0xbd, 0xdd, 0xe7, 0x04, 0x22, 0xb0, 0xcc, 0xf6, 0xa2, 0x58, 0xc9, - 0xbb, 0xa5, 0xfa, 0xf0, 0xa5, 0x1a, 0x26, 0x78, 0xc6, 0xcd, 0xca, 0xc4, - 0x42, 0x26, 0xe0, 0xab, 0x9a, 0xd4, 0xf9, 0x87, 0xff, 0x4f, 0x0f, 0x2c, - 0x26, 0x6b, 0x62, 0x27, 0x2e, 0x35, 0xca, 0xd0, 0xb9, 0xca, 0x6f, 0x12, - 0xb0, 0xe9, 0x38, 0x45, 0x22, 0x14, 0xc7, 0x18, 0x54, 0x5e, 0x19, 0x2d, - 0x74, 0x51, 0xa9, 0xc7, 0x98, 0x32, 0x17, 0x8d, 0x50, 0xba, 0xc6, 0x33, - 0xb4, 0xe3, 0x0a, 0xad, 0x0f, 0x03, 0xa7, 0x9d, 0x5a, 0x28, 0x88, 0xe7, - 0xca, 0x4d, 0x4c, 0x2c, 0x40, 0x5e, 0x68, 0x01, 0x5a, 0xd9, 0xad, 0x62, - 0x6f, 0x35, 0x18, 0x9e, 0xf2, 0xf7, 0x00, 0x00, 0x62, 0x09, 0x17, 0xbc, - 0x55, 0x6f, 0xa7, 0xee, 0xcc, 0xd8, 0x0b, 0xa7, 0x66, 0x2e, 0xa2, 0xba, - 0xb7, 0x9c, 0xe6, 0x93, 0x9d, 0xfd, 0x39, 0xa2, 0x30, 0x5c, 0xe5, 0x89, - 0x00, 0x88, 0x95, 0x66, 0xba, 0xfb, 0x38, 0xd2, 0x57, 0x2e, 0xb4, 0x24, - 0x8b, 0xba, 0xc6, 0x86, 0xef, 0x61, 0xb2, 0x16, 0x64, 0xa2, 0xc4, 0x77, - 0xbb, 0xec, 0x08, 0x48, 0xf8, 0xe8, 0xa0, 0x94, 0x38, 0x0d, 0xab, 0xe2, - 0x21, 0x16, 0x4e, 0x39, 0x7b, 0x6c, 0x1a, 0xc2, 0x83, 0xb0, 0xa8, 0xfe, - 0x00, 0x79, 0xf0, 0x6b, 0x60, 0x00, 0x2a, 0x37, 0xaa, 0x93, 0x65, 0x2a, - 0x40, 0xde, 0xfc, 0xa5, 0xfd, 0x51, 0x72, 0x6a, 0x18, 0x15, 0x10, 0x92, - 0xbc, 0x81, 0xaa, 0x8c, 0x49, 0xda, 0x9e, 0x81, 0x48, 0xb7, 0xb1, 0x23, - 0x8a, 0xbe, 0x70, 0x5e, 0x30, 0x47, 0x25, 0x74, 0xd2, 0x64, 0xa7, 0xb3, - 0x50, 0x33, 0xe8, 0x40, 0xa8, 0x80, 0x23, 0xc5, 0xf3, 0x04, 0xe0, 0x1a, - 0x13, 0x61, 0x59, 0xcb, 0x6c, 0x80, 0xa8, 0x07, 0x63, 0x62, 0x78, 0xf9, - 0xf1, 0x0d, 0x0f, 0x24, 0x38, 0xb8, 0xc7, 0xc7, 0x90, 0x25, 0xca, 0x29, - 0x40, 0xa8, 0x6c, 0xb2, 0x77, 0xf9, 0x60, 0x50, 0x15, 0xb8, 0x46, 0xba, - 0xb9, 0x0b, 0xda, 0x53, 0x76, 0x15, 0xfa, 0x78, 0x40, 0x07, 0xff, 0xf1, - 0x50, 0x80, 0x2f, 0x5f, 0xfc, 0x21, 0x1a, 0xce, 0xf6, 0x8f, 0xff, 0x10, - 0x40, 0x94, 0xb6, 0xc1, 0xa8, 0x28, 0x3b, 0x0a, 0x9c, 0xcc, 0x41, 0x62, - 0x80, 0x00, 0x00, 0x0b, 0xc3, 0x5b, 0xeb, 0xb9, 0x5e, 0xde, 0x79, 0xfc, - 0x77, 0xf0, 0x1d, 0xd5, 0xd4, 0xab, 0xa8, 0x58, 0x32, 0x3b, 0x02, 0xde, - 0x8f, 0x97, 0x2d, 0xf6, 0xe5, 0x9e, 0xa1, 0x6d, 0xf1, 0xa2, 0x04, 0xae, - 0xdf, 0x14, 0xaf, 0x64, 0x0a, 0xf5, 0xd0, 0xa4, 0x5a, 0xb6, 0x48, 0x7e, - 0x09, 0xc6, 0xba, 0x9f, 0xbe, 0x27, 0xe9, 0xc4, 0xe9, 0x8e, 0x5e, 0x24, - 0x06, 0x90, 0x94, 0x7b, 0xd7, 0x87, 0x1f, 0xe3, 0xad, 0x2c, 0x63, 0x10, - 0xe6, 0x8d, 0xc5, 0x39, 0xa7, 0x88, 0x39, 0x9e, 0x27, 0xe2, 0x3e, 0x11, - 0xc2, 0x08, 0x87, 0xc1, 0xe8, 0x67, 0xf8, 0x45, 0x27, 0x9e, 0xcb, 0xe8, - 0x59, 0x32, 0x71, 0x29, 0x0b, 0x17, 0x7d, 0x96, 0x00, 0x39, 0xba, 0x67, - 0x1c, 0xfa, 0x68, 0x9a, 0xdd, 0xd5, 0xca, 0x65, 0x55, 0xfa, 0x7b, 0xd3, - 0x40, 0x12, 0x36, 0xd5, 0xa9, 0x60, 0x7d, 0x5a, 0x89, 0x24, 0x2e, 0xc1, - 0x09, 0x2f, 0x05, 0x1f, 0xcf, 0x79, 0x96, 0x21, 0x27, 0x0e, 0x74, 0xf6, - 0x22, 0xa2, 0x7d, 0xad, 0xc3, 0x8c, 0xb6, 0xeb, 0x2e, 0x2a, 0xc3, 0xc5, - 0x2e, 0x35, 0x80, 0xdf, 0xff, 0xbe, 0xeb, 0x23, 0x53, 0x0a, 0xad, 0xc2, - 0xdf, 0xeb, 0xc6, 0xe8, 0xb4, 0x6c, 0x66, 0x92, 0xb8, 0xaa, 0xc6, 0x99, - 0x2a, 0x84, 0x17, 0x20, 0x00, 0x75, 0x03, 0xa0, 0x47, 0x07, 0x0b, 0x66, - 0xe6, 0xf8, 0x69, 0x76, 0xbb, 0xae, 0x5d, 0xd3, 0xbe, 0xd9, 0x13, 0x13, - 0x25, 0xbc, 0xd4, 0x55, 0x3b, 0x48, 0x99, 0xfc, 0x9f, 0x8d, 0x56, 0x5f, - 0xbd, 0xb9, 0x87, 0x74, 0xd8, 0xe7, 0x4f, 0x5b, 0xca, 0x5b, 0x60, 0xd6, - 0x16, 0x1d, 0x85, 0x47, 0xf0, 0x2a, 0x0a, 0x8a, 0x94, 0x40, 0x01, 0x55, - 0x32, 0xf2, 0x71, 0xdc, 0xc9, 0x9a, 0xad, 0x06, 0x5b, 0x5a, 0xbf, 0x69, - 0xd1, 0x96, 0x94, 0x74, 0xbd, 0x32, 0x71, 0xc2, 0x06, 0x8a, 0xce, 0xad, - 0xd4, 0x8a, 0x0d, 0xb7, 0x73, 0xee, 0x0c, 0xc7, 0xc8, 0xfb, 0xd2, 0x6a, - 0x54, 0x1a, 0x69, 0xf4, 0x19, 0x52, 0xb1, 0x1c, 0xee, 0x23, 0xef, 0x71, - 0x84, 0x66, 0x75, 0x74, 0xe8, 0x21, 0x7b, 0x49, 0xf3, 0xa6, 0x73, 0x05, - 0xaa, 0x52, 0x82, 0xa7, 0xea, 0x5f, 0x8a, 0xe7, 0xc1, 0x7f, 0xfd, 0x5d, - 0x6c, 0xae, 0x60, 0xe0, 0x18, 0x05, 0xc3, 0x1b, 0x9c, 0x20, 0x12, 0x05, - 0x5a, 0x1c, 0x64, 0x34, 0x30, 0x39, 0x8b, 0x97, 0x6f, 0x75, 0xe3, 0x02, - 0x11, 0x70, 0xa7, 0x7b, 0x24, 0x5e, 0x22, 0x24, 0x91, 0xbb, 0xf1, 0xf6, - 0x4a, 0x6e, 0x60, 0x70, 0xff, 0xf1, 0x50, 0x80, 0x2d, 0xbf, 0xfc, 0x21, - 0x1a, 0xcb, 0xb7, 0x8d, 0xbf, 0x00, 0x00, 0x94, 0xb6, 0xc1, 0xac, 0x24, - 0x39, 0x41, 0x99, 0x04, 0xa1, 0x61, 0xa1, 0x51, 0x50, 0x51, 0x15, 0x15, - 0x01, 0x4d, 0x6f, 0xcf, 0x8e, 0xbb, 0x3e, 0xfd, 0x73, 0xed, 0x9e, 0x43, - 0x92, 0xb6, 0x83, 0xfa, 0x4c, 0x4e, 0x5d, 0x09, 0x34, 0xa2, 0x5d, 0x4e, - 0x00, 0x8e, 0x5e, 0xfd, 0xd3, 0x47, 0xc3, 0xbd, 0xbd, 0x53, 0x1b, 0x35, - 0x87, 0x95, 0x24, 0x5c, 0x70, 0x79, 0x4f, 0x91, 0xea, 0xd1, 0x9e, 0xad, - 0xc1, 0x59, 0x59, 0x7f, 0x8f, 0xac, 0x7c, 0xc6, 0x72, 0x3a, 0x8e, 0xfd, - 0x4a, 0xe3, 0x89, 0xef, 0xfb, 0x10, 0x02, 0x96, 0xe0, 0xa4, 0xe2, 0x50, - 0x34, 0x24, 0x88, 0x22, 0x16, 0xab, 0xb4, 0xb9, 0xb5, 0x28, 0x01, 0x4f, - 0x49, 0xaf, 0xf9, 0x28, 0x84, 0x71, 0x6b, 0x7d, 0x6d, 0xff, 0xd7, 0xef, - 0x3a, 0xe9, 0xb5, 0x4e, 0x43, 0x7d, 0x31, 0x31, 0x17, 0x0a, 0x91, 0xd3, - 0x68, 0x71, 0xca, 0xb9, 0xa1, 0x40, 0xa2, 0x40, 0x5f, 0x3f, 0x15, 0x00, - 0x5c, 0xa8, 0x24, 0x26, 0x29, 0x9f, 0x6b, 0xc1, 0x45, 0x29, 0x65, 0xef, - 0x8a, 0xea, 0x46, 0x70, 0x66, 0x8b, 0x5c, 0x4a, 0xcd, 0x6d, 0x1e, 0x67, - 0x67, 0xa1, 0xe9, 0x30, 0xa8, 0x00, 0x97, 0x99, 0x6d, 0x08, 0x48, 0x6a, - 0x0c, 0x73, 0x36, 0x74, 0xc2, 0xf8, 0xef, 0x84, 0xcc, 0x5a, 0xf4, 0x2b, - 0x4b, 0x84, 0x28, 0x6e, 0x62, 0xae, 0x7a, 0xf9, 0x21, 0x24, 0x93, 0x34, - 0x8e, 0x01, 0xa9, 0x04, 0x80, 0x62, 0xf1, 0x41, 0x1c, 0x28, 0x2c, 0x01, - 0x9c, 0x18, 0x4d, 0xa8, 0xbe, 0xba, 0xf1, 0xb0, 0x92, 0x5c, 0x20, 0xf6, - 0xd8, 0x37, 0x49, 0xdd, 0x10, 0x76, 0x17, 0x1f, 0xc1, 0xad, 0x80, 0x00, - 0x00, 0x56, 0x6b, 0x7d, 0x78, 0xd5, 0x5e, 0x5e, 0x2f, 0x7c, 0x06, 0x09, - 0x4a, 0x0f, 0xd2, 0x72, 0x38, 0xee, 0x15, 0x6e, 0xfc, 0x77, 0x77, 0x32, - 0xe6, 0x43, 0x33, 0x39, 0x4e, 0xb3, 0x8d, 0xd8, 0x9f, 0xe5, 0xa2, 0xfc, - 0xc3, 0xca, 0x69, 0x68, 0xf5, 0x5f, 0x82, 0xb4, 0xb2, 0xa4, 0xa3, 0xdd, - 0xc8, 0x03, 0x55, 0x29, 0x0b, 0x7f, 0x40, 0x87, 0x38, 0x5a, 0x51, 0x97, - 0x49, 0x4d, 0x4b, 0x18, 0xa5, 0xd1, 0x8b, 0x95, 0x79, 0xa8, 0x73, 0x39, - 0xa3, 0x98, 0x50, 0x1d, 0xc1, 0x48, 0xfa, 0x57, 0x18, 0x0c, 0xfd, 0x9f, - 0x7d, 0x9e, 0x86, 0x5c, 0x3b, 0xc9, 0x3a, 0x3d, 0x6d, 0x12, 0x8e, 0xfb, - 0x6f, 0xd1, 0x55, 0xc7, 0x80, 0x0c, 0x16, 0x54, 0xc7, 0x2b, 0x18, 0xcc, - 0x81, 0x07, 0xc3, 0xd0, 0xe0, 0xc4, 0x22, 0x43, 0x80, 0xff, 0xf1, 0x50, - 0x80, 0x2e, 0x9f, 0xfc, 0x21, 0x1a, 0xcf, 0xf7, 0x99, 0xef, 0x10, 0x00, - 0x93, 0xb6, 0xc0, 0xd8, 0x72, 0x85, 0x32, 0x04, 0xc6, 0xc6, 0x00, 0x00, - 0x00, 0x1e, 0xde, 0xbc, 0xf8, 0xf3, 0xe3, 0xf1, 0xf9, 0x3f, 0x1e, 0x77, - 0x7d, 0x41, 0x4a, 0x64, 0x27, 0x50, 0xa7, 0xc9, 0xac, 0xdc, 0x24, 0x43, - 0x26, 0x67, 0x01, 0x23, 0x0f, 0x08, 0xfe, 0x2c, 0x4f, 0xdd, 0x13, 0x26, - 0x7f, 0x72, 0xfc, 0x95, 0xd0, 0x2d, 0x9d, 0x48, 0x32, 0x9f, 0x45, 0xaa, - 0xe3, 0x45, 0xfb, 0x16, 0xd0, 0xd9, 0x19, 0x06, 0x01, 0x29, 0xc7, 0xea, - 0xaa, 0x0c, 0x09, 0x36, 0x0f, 0x6c, 0xa8, 0x7c, 0x79, 0x93, 0x10, 0x81, - 0x7a, 0xf3, 0x08, 0xa9, 0x76, 0x41, 0x15, 0xfa, 0xfe, 0x03, 0xc9, 0xc4, - 0x4c, 0xcd, 0x68, 0xd6, 0x35, 0xb8, 0x15, 0x57, 0x53, 0x85, 0xd6, 0x66, - 0xff, 0x51, 0xea, 0x78, 0x73, 0x72, 0xa9, 0x0c, 0xac, 0x28, 0x25, 0x73, - 0x58, 0x76, 0xbd, 0xd3, 0xc6, 0x71, 0x21, 0x25, 0x15, 0xd1, 0xd1, 0x6c, - 0xb7, 0x59, 0x62, 0x02, 0x7b, 0xfa, 0x7a, 0x7a, 0x64, 0x1a, 0x8b, 0xdc, - 0xcb, 0x15, 0x55, 0x69, 0x60, 0x23, 0xf8, 0x74, 0x7d, 0x1a, 0x88, 0x03, - 0x96, 0x9a, 0x86, 0xd1, 0xa9, 0xdd, 0xd0, 0xbd, 0xb5, 0xa0, 0x01, 0x00, - 0xec, 0xa9, 0xa0, 0x21, 0x5b, 0x9e, 0x66, 0x67, 0x04, 0xc8, 0x34, 0x9f, - 0xd3, 0x73, 0xc3, 0x58, 0x64, 0x57, 0x1a, 0x28, 0x20, 0x90, 0xd0, 0x04, - 0x91, 0x93, 0x86, 0xaa, 0x10, 0x47, 0x29, 0xc0, 0xd8, 0x62, 0x94, 0x20, - 0x41, 0x44, 0x51, 0x8a, 0x71, 0xac, 0xaf, 0xb3, 0xd3, 0x2d, 0x50, 0xc0, - 0xeb, 0x17, 0x8b, 0x80, 0x8c, 0xe7, 0x2e, 0xfe, 0xbd, 0x7c, 0xab, 0xeb, - 0x6c, 0x39, 0xb4, 0xab, 0xa7, 0x24, 0xd5, 0xee, 0x9f, 0xd0, 0x08, 0xd9, - 0x49, 0x82, 0x4c, 0xd7, 0x04, 0x9d, 0xb9, 0x8a, 0xe4, 0xb0, 0xa8, 0xfe, - 0x00, 0x32, 0xc6, 0x2d, 0x8b, 0x19, 0x62, 0x6e, 0xf6, 0x6b, 0x9b, 0xbd, - 0xca, 0x4b, 0x00, 0xe6, 0xd0, 0xae, 0x26, 0xd5, 0x7c, 0x9b, 0x37, 0xe6, - 0xd8, 0x09, 0x6d, 0xd3, 0x04, 0xa4, 0xb6, 0xd6, 0xfa, 0xb2, 0x37, 0xd1, - 0x86, 0x69, 0x64, 0xa2, 0xa3, 0x76, 0xad, 0x6e, 0x09, 0xd2, 0xd1, 0x38, - 0xb6, 0xf2, 0x18, 0xb0, 0xb0, 0x30, 0xef, 0x8e, 0x1e, 0x90, 0x42, 0xff, - 0x74, 0x0d, 0x71, 0x7f, 0x86, 0xb6, 0xb5, 0xff, 0x9c, 0xcd, 0x18, 0x0f, - 0xf6, 0xfe, 0xd5, 0x86, 0x44, 0x4d, 0x58, 0xac, 0xed, 0x34, 0x43, 0x10, - 0x1f, 0xcf, 0xac, 0x03, 0x00, 0x00, 0xd3, 0x30, 0x55, 0x20, 0x44, 0xcc, - 0x84, 0x22, 0x7f, 0x87, 0xc3, 0xbe, 0x14, 0xa0, 0xe0, 0xff, 0xf1, 0x50, - 0x80, 0x30, 0xff, 0xfc, 0x21, 0x1a, 0xce, 0x15, 0xff, 0xff, 0x40, 0x40, - 0x94, 0xb6, 0xc1, 0xa4, 0x96, 0x17, 0x3a, 0x99, 0x8c, 0x00, 0x00, 0x00, - 0x0f, 0x3e, 0x35, 0xbf, 0xaf, 0x99, 0x5f, 0x5c, 0x6b, 0x7d, 0x74, 0x27, - 0x13, 0x38, 0x30, 0x15, 0xe0, 0x22, 0xe0, 0x44, 0x02, 0xcf, 0x1e, 0xb4, - 0x60, 0x51, 0xf6, 0xf0, 0x60, 0x1b, 0xcd, 0x0a, 0x8c, 0xf7, 0x9f, 0xbd, - 0xf3, 0x09, 0xe7, 0xca, 0x64, 0xf9, 0x95, 0xe5, 0x9a, 0x3b, 0x4c, 0x14, - 0x8e, 0x52, 0xee, 0xb2, 0x73, 0x64, 0xd1, 0x70, 0xbb, 0x75, 0x90, 0x10, - 0xc5, 0x00, 0xd8, 0x27, 0x99, 0xd0, 0x84, 0x60, 0x15, 0x9b, 0x6e, 0x18, - 0xac, 0x10, 0x00, 0x00, 0xe2, 0xbf, 0xd8, 0xff, 0xff, 0xf0, 0xf1, 0xcd, - 0x86, 0xed, 0x4c, 0x4d, 0x9c, 0x6d, 0x0b, 0xcd, 0xb9, 0x81, 0x2b, 0x40, - 0x32, 0x8e, 0x3a, 0x70, 0xf1, 0x40, 0x59, 0x42, 0x52, 0xcc, 0xea, 0x85, - 0x66, 0x91, 0x81, 0x33, 0xaf, 0xff, 0x3d, 0xf3, 0x55, 0x73, 0x32, 0xa5, - 0x66, 0x12, 0x05, 0x13, 0x51, 0xda, 0xc0, 0x61, 0x44, 0xce, 0x79, 0x29, - 0x96, 0x85, 0x21, 0x76, 0x2e, 0xbb, 0x8c, 0x30, 0x18, 0x4a, 0x6d, 0xcf, - 0x81, 0x6a, 0x00, 0x1c, 0xfb, 0x8a, 0x02, 0xd5, 0x1d, 0x5a, 0x8d, 0x5a, - 0xf6, 0xeb, 0xad, 0x9a, 0x4f, 0xa2, 0x00, 0x41, 0xbd, 0x80, 0x33, 0xb9, - 0xdd, 0x6d, 0xe3, 0x17, 0x76, 0xa1, 0x0b, 0xaa, 0xf6, 0x21, 0x74, 0x76, - 0x13, 0x53, 0x16, 0x32, 0x2b, 0x90, 0x05, 0x90, 0xd7, 0x1c, 0xea, 0x55, - 0x9d, 0x25, 0x65, 0x60, 0xa6, 0x25, 0xae, 0x59, 0xf4, 0x4a, 0x1a, 0x71, - 0xe1, 0x55, 0x05, 0x39, 0x91, 0x20, 0x85, 0xed, 0x69, 0xf3, 0xa7, 0x05, - 0xdf, 0x75, 0x17, 0x71, 0xc3, 0x25, 0x95, 0x5b, 0x52, 0x71, 0x7d, 0xdf, - 0xda, 0xff, 0xea, 0x39, 0x77, 0xfd, 0x2a, 0x55, 0xb0, 0x65, 0x2d, 0xb0, - 0x6b, 0x0a, 0x0e, 0xc2, 0xe3, 0xf2, 0x88, 0x7d, 0x40, 0x00, 0x00, 0x01, - 0x89, 0x9d, 0x5c, 0xca, 0x95, 0x75, 0x50, 0x16, 0xef, 0xd5, 0x10, 0x6d, - 0xa4, 0x40, 0x8d, 0x97, 0x87, 0x3b, 0x97, 0x8b, 0x7d, 0x69, 0xc9, 0xe6, - 0xeb, 0x99, 0x20, 0xc6, 0x4e, 0x57, 0x47, 0x59, 0x24, 0xa1, 0x21, 0x49, - 0x4a, 0x45, 0x5e, 0x22, 0x51, 0x93, 0x4e, 0x45, 0x4b, 0x99, 0x56, 0x10, - 0x38, 0xb9, 0xe5, 0xa2, 0x64, 0xdb, 0x8d, 0xcf, 0x49, 0x40, 0x72, 0x44, - 0x1d, 0x40, 0xd4, 0xb8, 0xb3, 0x92, 0x35, 0x38, 0x2c, 0x46, 0xc3, 0x0a, - 0x01, 0xcf, 0x7b, 0xb4, 0xfb, 0xf2, 0xd8, 0xfe, 0x10, 0x44, 0xbc, 0xbd, - 0x20, 0x0d, 0x25, 0x4e, 0x87, 0xbd, 0x0e, 0x01, 0xa5, 0xb2, 0x42, 0xea, - 0x74, 0xa8, 0x6a, 0x6c, 0x24, 0x51, 0x7a, 0xde, 0xf7, 0x68, 0xa4, 0x1c, - 0x62, 0xb9, 0x48, 0x70, 0xff, 0xf1, 0x50, 0x80, 0x31, 0x9f, 0xfc, 0x21, - 0x1a, 0xcf, 0xef, 0x93, 0x77, 0x10, 0x10, 0x95, 0xb6, 0xc1, 0xac, 0x4a, - 0x2b, 0x09, 0x0a, 0x56, 0x81, 0x61, 0x28, 0x60, 0x40, 0x00, 0x00, 0x00, - 0xbc, 0xbc, 0xe3, 0x93, 0x5e, 0xd3, 0x9f, 0xaf, 0x60, 0x2c, 0xa6, 0x5b, - 0x34, 0xbf, 0x48, 0xe7, 0x62, 0x41, 0x57, 0x98, 0x65, 0x41, 0x6c, 0xee, - 0x7d, 0x6e, 0xeb, 0x21, 0x99, 0xe8, 0x1a, 0x96, 0x11, 0x9c, 0xe4, 0x64, - 0xaa, 0x05, 0x47, 0x3a, 0x0b, 0x51, 0x9e, 0x33, 0x49, 0xc0, 0x48, 0x0e, - 0x11, 0x1d, 0x8a, 0x66, 0x7b, 0x3e, 0x78, 0x9c, 0xad, 0x50, 0x22, 0x9c, - 0xca, 0xf2, 0x0e, 0xa1, 0x99, 0x37, 0xb8, 0xe1, 0x18, 0x8b, 0x40, 0x62, - 0x0c, 0x37, 0x7a, 0xf8, 0x9b, 0x86, 0x9c, 0xa1, 0x85, 0x19, 0xac, 0xd5, - 0xea, 0xa8, 0x31, 0xf8, 0x5d, 0x8e, 0x3b, 0xdc, 0x5d, 0xc4, 0x80, 0x68, - 0x95, 0x70, 0xea, 0x14, 0x8b, 0x8a, 0x02, 0x8c, 0x94, 0x8f, 0x31, 0x1d, - 0xb2, 0x8f, 0x58, 0x8e, 0x83, 0xf4, 0x4e, 0xba, 0x58, 0x56, 0x12, 0x1c, - 0xe8, 0x0c, 0x52, 0x12, 0x1b, 0xb8, 0xf3, 0x06, 0x44, 0x0d, 0x06, 0x10, - 0x66, 0xc3, 0x0b, 0x67, 0x04, 0x61, 0xe8, 0x98, 0x05, 0x42, 0x0d, 0xf8, - 0x14, 0xc2, 0x33, 0x52, 0xe0, 0x5e, 0x1e, 0x37, 0x30, 0x89, 0x18, 0x56, - 0x38, 0xd2, 0xd3, 0x55, 0x15, 0xad, 0x15, 0x6c, 0xf5, 0xfa, 0xe1, 0x69, - 0xa5, 0x56, 0x19, 0xc6, 0x14, 0x9c, 0x90, 0xce, 0xac, 0x99, 0xd4, 0xec, - 0x22, 0x61, 0x68, 0x98, 0x61, 0x87, 0x49, 0x51, 0x85, 0xe0, 0x58, 0x91, - 0x88, 0x9f, 0xc2, 0x0d, 0x4a, 0x10, 0x8e, 0xd8, 0x69, 0xcc, 0xdc, 0x00, - 0x1d, 0x8e, 0xe9, 0xab, 0x99, 0xab, 0xde, 0x8f, 0x51, 0x28, 0xa3, 0x4b, - 0xe3, 0xb0, 0x24, 0xd7, 0x84, 0x25, 0x60, 0x20, 0xe1, 0x80, 0xa8, 0x81, - 0x6d, 0x6f, 0x5b, 0x9f, 0x59, 0xc8, 0xef, 0x23, 0x3b, 0x22, 0x3b, 0x65, - 0xa0, 0xf1, 0x40, 0x95, 0xa6, 0x5a, 0x18, 0x72, 0x4f, 0x80, 0x17, 0x81, - 0x49, 0x48, 0xa8, 0x54, 0x19, 0x1b, 0xce, 0x29, 0x4d, 0x65, 0x4b, 0x01, - 0xf3, 0xfb, 0x03, 0x27, 0x15, 0x10, 0x39, 0xc1, 0x9a, 0x9a, 0x26, 0x48, - 0x72, 0x52, 0x80, 0x6e, 0x94, 0xba, 0x24, 0x2d, 0x32, 0xd6, 0x07, 0xc9, - 0x3e, 0x49, 0x7e, 0x58, 0x42, 0xd6, 0xba, 0x4c, 0x0b, 0xc0, 0x08, 0xa0, - 0xb2, 0xac, 0x9c, 0x56, 0x11, 0x98, 0xb3, 0x62, 0x43, 0x82, 0x8f, 0x0b, - 0x78, 0x18, 0xd3, 0xa7, 0x53, 0x8c, 0x0b, 0x8b, 0x52, 0xb4, 0xd6, 0x4a, - 0x38, 0x9a, 0x86, 0x30, 0xd3, 0x90, 0x6b, 0x36, 0x93, 0x4b, 0x18, 0x02, - 0x64, 0xe2, 0x84, 0x96, 0xb0, 0x33, 0xf4, 0xaf, 0x8a, 0xff, 0x47, 0x6a, - 0xaa, 0x36, 0x2d, 0x5b, 0xf8, 0x01, 0x59, 0x92, 0x58, 0xad, 0xbf, 0x72, - 0xd0, 0x4a, 0xea, 0x0e, 0xff, 0xf1, 0x50, 0x80, 0x2d, 0x9f, 0xfc, 0x21, - 0x1a, 0xcf, 0xfe, 0xf7, 0xff, 0x06, 0x20, 0x97, 0xb6, 0xc1, 0xac, 0x2c, - 0x29, 0x59, 0x8d, 0x08, 0xc3, 0x00, 0x00, 0x00, 0x07, 0x5d, 0xde, 0x4a, - 0x3a, 0xea, 0x57, 0xb7, 0x41, 0xbf, 0x27, 0x97, 0x60, 0xfd, 0xd9, 0x66, - 0xeb, 0x3e, 0x39, 0xe8, 0xec, 0xbb, 0xac, 0x14, 0xae, 0x10, 0x52, 0x07, - 0x3a, 0x9d, 0x5d, 0x08, 0x4e, 0x98, 0xe8, 0x88, 0xd5, 0xc4, 0xe3, 0x07, - 0xd3, 0x73, 0x1f, 0x90, 0x96, 0x7e, 0x75, 0x61, 0x39, 0x82, 0xca, 0x15, - 0x1b, 0x62, 0xc6, 0x17, 0x76, 0x02, 0x24, 0x21, 0x56, 0xa0, 0x6e, 0x31, - 0xe1, 0x18, 0x86, 0xe5, 0xea, 0xeb, 0xb4, 0x5b, 0x81, 0x48, 0x84, 0xd2, - 0xcf, 0x2a, 0x5e, 0x62, 0x6f, 0x16, 0x04, 0x01, 0xe1, 0x38, 0x31, 0x65, - 0x03, 0x97, 0xfd, 0x9b, 0xf8, 0x0a, 0xaa, 0xbc, 0x82, 0x81, 0x72, 0x0c, - 0xe2, 0x23, 0x3d, 0x3f, 0xe8, 0xf3, 0x05, 0x44, 0xc9, 0x7b, 0xa1, 0x05, - 0x00, 0x17, 0xbb, 0x6e, 0x56, 0x17, 0x17, 0x9d, 0xe9, 0x2c, 0x4e, 0x30, - 0x9c, 0xb7, 0xc0, 0xbf, 0x3f, 0x8c, 0x06, 0x01, 0x79, 0xec, 0x48, 0x84, - 0xc2, 0xf3, 0x88, 0xb9, 0xe3, 0xf9, 0x6a, 0x15, 0x01, 0xec, 0x7a, 0xf0, - 0xd1, 0x43, 0xf5, 0x7a, 0xf1, 0x9d, 0x6b, 0xf7, 0xf6, 0x93, 0x49, 0x69, - 0xd4, 0x3f, 0xa3, 0xb0, 0xb9, 0x9d, 0xec, 0xda, 0xd4, 0xf4, 0x14, 0xbc, - 0xeb, 0xa8, 0x9e, 0x8f, 0x13, 0x5b, 0xb7, 0x93, 0xdc, 0x83, 0x94, 0x9a, - 0xf1, 0x3d, 0xf6, 0xda, 0xfc, 0x3f, 0x78, 0x80, 0x94, 0x39, 0x90, 0x01, - 0x88, 0x9d, 0x49, 0x67, 0x39, 0x16, 0x7b, 0x7a, 0xeb, 0x4b, 0xeb, 0xc5, - 0xaf, 0x0f, 0x0e, 0xa2, 0xd3, 0xba, 0xbd, 0x1c, 0xfd, 0xd8, 0xac, 0x4f, - 0x34, 0xbb, 0xa5, 0xe1, 0x96, 0x18, 0x35, 0x85, 0x07, 0x61, 0x20, 0xb8, - 0xbe, 0x00, 0x05, 0x41, 0x50, 0x00, 0xbd, 0xcc, 0x78, 0xe3, 0x59, 0x56, - 0x54, 0xd0, 0x1e, 0xae, 0xc9, 0x97, 0x88, 0x7f, 0x5f, 0x51, 0x40, 0xe9, - 0x18, 0x2c, 0x05, 0x57, 0x99, 0x0d, 0x7d, 0x6e, 0x1e, 0x94, 0x0f, 0x78, - 0xd1, 0x73, 0x74, 0xc6, 0x74, 0xcb, 0x7c, 0x13, 0xea, 0x02, 0xad, 0xde, - 0x82, 0x02, 0x03, 0xfa, 0x79, 0xaf, 0x8d, 0x41, 0xb1, 0x7c, 0x23, 0x45, - 0xa4, 0x13, 0x0a, 0xc0, 0x2b, 0xea, 0x5f, 0x52, 0xb0, 0x18, 0x15, 0x3e, - 0x39, 0xc7, 0xb8, 0xb5, 0x5a, 0x3a, 0xd0, 0x63, 0x22, 0xd4, 0x38, 0x0f, - 0xa8, 0xb9, 0x34, 0xa3, 0x4d, 0x35, 0x9c, 0x79, 0xfa, 0x74, 0x15, 0x33, - 0xa1, 0x41, 0x86, 0xa7, 0xea, 0x83, 0x01, 0xc0, 0xff, 0xf1, 0x50, 0x80, - 0x2f, 0x1f, 0xfc, 0x21, 0x1a, 0xcf, 0xef, 0xbf, 0x7f, 0x59, 0x90, 0x96, - 0xb6, 0xc1, 0xac, 0x2c, 0x39, 0x42, 0xa1, 0x04, 0xc2, 0x02, 0xa0, 0x54, - 0x05, 0x40, 0x29, 0x79, 0x79, 0xc7, 0x27, 0xde, 0x55, 0xf9, 0xe0, 0x69, - 0xf3, 0xea, 0x69, 0xee, 0x72, 0xe8, 0xd2, 0x21, 0x07, 0x03, 0xb7, 0x41, - 0x16, 0xdf, 0xec, 0x36, 0x11, 0x48, 0x1f, 0x6f, 0xed, 0x3e, 0x24, 0x91, - 0xc6, 0xa4, 0x53, 0x26, 0xda, 0x8a, 0xfe, 0x66, 0x86, 0x3d, 0xff, 0x10, - 0x8d, 0xbd, 0xdd, 0x6a, 0x6b, 0xc4, 0x02, 0x4e, 0xde, 0xd8, 0xe9, 0x38, - 0xad, 0x37, 0xa2, 0x24, 0x54, 0x6c, 0xe2, 0x19, 0x38, 0x40, 0xa7, 0x82, - 0xb4, 0xfa, 0xf3, 0xfb, 0xa9, 0xc1, 0x4b, 0x02, 0xfb, 0x25, 0xe5, 0x21, - 0xd8, 0xde, 0x55, 0x21, 0x4b, 0xf6, 0x30, 0x13, 0x40, 0x0c, 0x37, 0xe1, - 0xff, 0x6f, 0xe1, 0x70, 0x5d, 0xc2, 0xe0, 0x24, 0x5a, 0xc5, 0xee, 0xce, - 0x73, 0xe8, 0xff, 0xab, 0xf1, 0xd5, 0x77, 0x8e, 0x14, 0x67, 0x2d, 0x78, - 0x14, 0x05, 0x83, 0x56, 0x40, 0x11, 0x79, 0x0a, 0xbb, 0x56, 0x26, 0x06, - 0x77, 0xd9, 0xf9, 0x08, 0x18, 0x51, 0xa2, 0xca, 0x54, 0xdd, 0x26, 0x51, - 0x6d, 0xfc, 0x7f, 0x2f, 0xd0, 0x96, 0x33, 0x01, 0x8c, 0x5b, 0x4b, 0x90, - 0x8a, 0x15, 0xd7, 0xc1, 0x39, 0x2e, 0x49, 0x9d, 0xc4, 0x8b, 0x06, 0x62, - 0xab, 0x7b, 0xc7, 0xc1, 0x26, 0x42, 0xa1, 0xac, 0x57, 0x3d, 0x5d, 0x53, - 0x17, 0xae, 0xa9, 0xb9, 0xe3, 0xf4, 0xab, 0x30, 0x11, 0x37, 0x30, 0x52, - 0xa4, 0x0a, 0x5f, 0x1c, 0x15, 0xe4, 0xf1, 0x2e, 0x68, 0x5e, 0xe0, 0xd6, - 0x76, 0x23, 0x94, 0x5a, 0xf4, 0xf6, 0xd3, 0x35, 0x15, 0xeb, 0x92, 0x4a, - 0x6f, 0x8a, 0xf0, 0xdb, 0xdd, 0x7f, 0x87, 0x5b, 0x09, 0x6f, 0x3d, 0xb9, - 0xf8, 0xbd, 0x12, 0xd0, 0xcb, 0x0c, 0x1a, 0xc2, 0x83, 0xb0, 0xc8, 0xfe, - 0x00, 0x00, 0x00, 0x00, 0xaa, 0xaf, 0x1f, 0x19, 0x52, 0x45, 0x40, 0x1f, - 0x27, 0x47, 0xb5, 0x55, 0xd1, 0xbb, 0x9c, 0xba, 0x6e, 0x78, 0x95, 0xc3, - 0xc7, 0x81, 0xe3, 0xbd, 0x14, 0xf2, 0xa6, 0xe1, 0x22, 0x68, 0xa4, 0x82, - 0x10, 0xc2, 0x47, 0x8f, 0x2a, 0xa7, 0x7d, 0x60, 0xd6, 0x36, 0x22, 0x12, - 0x34, 0x68, 0x69, 0x34, 0x41, 0x19, 0x09, 0x70, 0x0f, 0x02, 0x94, 0x5b, - 0xeb, 0x78, 0x01, 0x6e, 0xa3, 0x34, 0xab, 0x1e, 0xa9, 0x5e, 0x52, 0x00, - 0x80, 0x2f, 0x0f, 0xa0, 0xe4, 0x27, 0xfe, 0x9f, 0x7a, 0x5e, 0x77, 0xcd, - 0x46, 0x1d, 0x7c, 0x9a, 0x17, 0x48, 0x80, 0x30, 0xe6, 0x52, 0x95, 0xbb, - 0x3c, 0x14, 0xd4, 0xca, 0x82, 0x4b, 0xcd, 0xf5, 0x6c, 0x8b, 0x90, 0xe0, - 0xff, 0xf1, 0x50, 0x80, 0x2f, 0x7f, 0xfc, 0x21, 0x1a, 0xce, 0x79, 0xe7, - 0xff, 0x28, 0xc0, 0x96, 0xb6, 0xc1, 0xac, 0x2c, 0x29, 0x2a, 0xa9, 0x89, - 0x02, 0x00, 0x00, 0x00, 0x06, 0xb7, 0x79, 0xd7, 0x67, 0xeb, 0x39, 0xf6, - 0xf6, 0x06, 0xef, 0xba, 0xd4, 0xc0, 0xad, 0xc0, 0xfc, 0xa8, 0x19, 0x37, - 0x4a, 0x06, 0x63, 0xd8, 0x89, 0xa9, 0xd1, 0xd8, 0xd0, 0x38, 0x05, 0x58, - 0x1d, 0xb6, 0x59, 0x88, 0x26, 0xa9, 0x84, 0xd7, 0xf4, 0x8a, 0x12, 0x47, - 0x29, 0x2d, 0x20, 0x2c, 0x62, 0x57, 0x04, 0x69, 0xeb, 0x46, 0x65, 0x38, - 0xff, 0x8d, 0xa9, 0x94, 0x02, 0x39, 0xa7, 0x52, 0x9d, 0x29, 0xcf, 0x28, - 0x01, 0xd4, 0xdd, 0x2f, 0x86, 0x3c, 0x14, 0x39, 0x6d, 0x73, 0x5b, 0x0a, - 0x5a, 0x11, 0x81, 0x6c, 0x9a, 0x70, 0x88, 0x22, 0x17, 0xd4, 0xfc, 0xcb, - 0x38, 0x2f, 0x0a, 0x08, 0x48, 0xc8, 0x22, 0xec, 0xc3, 0x81, 0xf8, 0xce, - 0x61, 0x74, 0x33, 0xdd, 0x22, 0x35, 0x36, 0x02, 0xb7, 0xe9, 0xe1, 0x21, - 0x6b, 0x45, 0xe6, 0x28, 0xac, 0xac, 0x48, 0xbf, 0x2f, 0xf5, 0xc1, 0x35, - 0x61, 0x19, 0x84, 0xca, 0xc1, 0x42, 0xf5, 0xf7, 0x62, 0x16, 0xa0, 0x63, - 0xb6, 0x63, 0x51, 0x8a, 0xa4, 0x5a, 0x22, 0xa7, 0x7d, 0x7e, 0x0a, 0x86, - 0x0d, 0xa3, 0x70, 0x25, 0x37, 0x08, 0x64, 0x6f, 0x7d, 0x52, 0x14, 0x13, - 0x65, 0xce, 0x4f, 0x6c, 0xa2, 0x74, 0x00, 0xa1, 0xcc, 0x50, 0x28, 0xeb, - 0x33, 0xc6, 0x4b, 0x83, 0x9a, 0x23, 0x99, 0x81, 0x02, 0xb3, 0x9a, 0x02, - 0xa1, 0xdf, 0x32, 0x60, 0x33, 0x3c, 0x3b, 0xb9, 0xd1, 0x5d, 0x59, 0xcd, - 0x88, 0x61, 0xad, 0x8c, 0x5b, 0x24, 0x59, 0xc0, 0xc3, 0x75, 0xa4, 0xdd, - 0x34, 0xd2, 0x7b, 0x0c, 0x6a, 0x56, 0x63, 0x40, 0x8b, 0xf4, 0x4c, 0x94, - 0x6c, 0x10, 0x8f, 0x0c, 0xb5, 0xd1, 0x87, 0x24, 0xe2, 0x88, 0x7d, 0x40, - 0x00, 0x00, 0x03, 0x23, 0x6e, 0x25, 0x5a, 0x62, 0xd4, 0x0d, 0x43, 0x3d, - 0xac, 0xd5, 0x16, 0xe3, 0xb0, 0xae, 0x58, 0x6e, 0x2c, 0x16, 0x59, 0x60, - 0xb6, 0xc1, 0x24, 0xd5, 0x10, 0x57, 0x12, 0x43, 0x46, 0x6c, 0xbc, 0x0d, - 0x8c, 0x4f, 0x03, 0x9f, 0x63, 0x90, 0x8b, 0x83, 0xcf, 0xc3, 0x96, 0x05, - 0x3c, 0xb6, 0xc1, 0xc4, 0x7c, 0xdf, 0xfb, 0xb4, 0x80, 0x01, 0xe9, 0x0c, - 0xe3, 0x34, 0x65, 0x33, 0x1c, 0x70, 0x2c, 0x44, 0x9b, 0x73, 0x85, 0x2b, - 0xc8, 0x08, 0x5d, 0x6a, 0x83, 0x0d, 0x4f, 0x54, 0x10, 0x85, 0xf0, 0x42, - 0x35, 0x1b, 0x33, 0x65, 0xa7, 0xe1, 0xff, 0xf7, 0xb8, 0x52, 0xa9, 0xc1, - 0x13, 0x96, 0x52, 0x15, 0x9d, 0xa0, 0x2c, 0xbf, 0x46, 0xf6, 0x39, 0x18, - 0xa2, 0x91, 0xcb, 0xe2, 0x9d, 0xcb, 0x70, 0xff, 0xf1, 0x50, 0x80, 0x31, - 0x9f, 0xfc, 0x21, 0x1a, 0xcf, 0xf8, 0x18, 0x9f, 0x10, 0x00, 0x92, 0xba, - 0x30, 0xe5, 0x28, 0x66, 0x24, 0x04, 0x00, 0x00, 0x00, 0x01, 0xd7, 0x67, - 0x5f, 0x5d, 0xf1, 0xc7, 0x90, 0xc1, 0xc4, 0x70, 0x28, 0x19, 0x34, 0xf6, - 0xb8, 0x2c, 0xe7, 0x50, 0x88, 0x26, 0x10, 0xf5, 0xc5, 0x66, 0x07, 0x4e, - 0xc6, 0x19, 0xc9, 0x78, 0x48, 0xa7, 0x1f, 0x0c, 0x6f, 0xa5, 0xc5, 0x37, - 0x95, 0x0d, 0x3f, 0x3e, 0x8a, 0x43, 0xe9, 0xf9, 0x4f, 0x3e, 0x41, 0xd0, - 0xa8, 0x94, 0xa1, 0x3e, 0x0c, 0x26, 0x2d, 0x38, 0x55, 0x46, 0x88, 0xec, - 0x45, 0xe7, 0x47, 0x54, 0x1d, 0xfe, 0x68, 0xa2, 0x24, 0x66, 0xcc, 0xd2, - 0x86, 0x3d, 0x4f, 0xca, 0x14, 0x3c, 0x81, 0x64, 0xd0, 0x16, 0x9a, 0x41, - 0x32, 0xc3, 0x9e, 0xb4, 0x20, 0x0f, 0x46, 0x91, 0x26, 0x62, 0x41, 0x46, - 0xff, 0x44, 0xfd, 0xe3, 0xbe, 0x5e, 0x34, 0x2d, 0x85, 0x4c, 0x09, 0x88, - 0x22, 0x91, 0x5a, 0xdf, 0x9b, 0x76, 0x3c, 0x5c, 0xea, 0x2a, 0xd5, 0x4d, - 0x29, 0x02, 0x16, 0xb8, 0x54, 0xa7, 0xd7, 0xa9, 0x8a, 0xf2, 0x90, 0xa0, - 0xa9, 0x93, 0x0a, 0xcb, 0x14, 0xb1, 0xf6, 0x0d, 0x99, 0x42, 0xa3, 0x3b, - 0xa5, 0xe0, 0x2a, 0x66, 0xc0, 0x3e, 0x94, 0xbc, 0x76, 0x07, 0x78, 0x2c, - 0xc4, 0x4d, 0x11, 0x99, 0xa1, 0x45, 0xf8, 0x2b, 0xa7, 0x43, 0x9e, 0xb5, - 0x2c, 0x9e, 0xda, 0x38, 0x61, 0x75, 0x5c, 0xa9, 0x73, 0xa2, 0x89, 0xc9, - 0x2d, 0x64, 0xd5, 0x6b, 0x97, 0x2c, 0xed, 0x34, 0xd3, 0xae, 0x53, 0xd7, - 0x6c, 0xd1, 0x51, 0xc0, 0x4e, 0x46, 0x00, 0x04, 0x0c, 0x46, 0xc3, 0x4e, - 0xe7, 0x13, 0x45, 0x48, 0x72, 0xaa, 0x7e, 0x52, 0x09, 0x93, 0x30, 0x4d, - 0x79, 0x66, 0x57, 0xcd, 0x4c, 0x07, 0x83, 0xbe, 0x69, 0xbe, 0xb7, 0x6c, - 0x31, 0x25, 0x50, 0x9b, 0xc4, 0xa0, 0x57, 0x8e, 0x21, 0x07, 0x14, 0x95, - 0xd1, 0x07, 0x24, 0xf8, 0x0d, 0x6c, 0x6c, 0x1a, 0x6c, 0xd6, 0xda, 0x06, - 0xdc, 0xea, 0x5e, 0x2e, 0xa2, 0xa4, 0x03, 0x1c, 0xdc, 0x6e, 0x65, 0x7a, - 0x56, 0x1f, 0xf7, 0xf8, 0x4b, 0x6d, 0x72, 0xc8, 0x28, 0xce, 0x2a, 0xd1, - 0x35, 0x25, 0x2a, 0xee, 0x75, 0x0c, 0xe4, 0x8f, 0xb4, 0xf2, 0xbc, 0x67, - 0x6d, 0xa4, 0xed, 0xfc, 0x09, 0x2d, 0x2e, 0x83, 0xc9, 0x15, 0x4a, 0x3f, - 0x8d, 0x76, 0x9c, 0x7d, 0x36, 0xd6, 0x0f, 0x69, 0x75, 0x92, 0x85, 0x2e, - 0x14, 0xff, 0x4a, 0x33, 0x21, 0x0f, 0xae, 0x01, 0xc6, 0x0a, 0x56, 0x5f, - 0x1f, 0xdf, 0x95, 0x8d, 0x28, 0xde, 0x43, 0xfd, 0x5f, 0xde, 0xf9, 0x1a, - 0x80, 0xb7, 0xdb, 0xa1, 0x4c, 0x93, 0xcf, 0x7f, 0xac, 0xfb, 0x77, 0x49, - 0x59, 0x65, 0x81, 0x08, 0xc2, 0xae, 0x4a, 0xca, 0xc1, 0x89, 0xbf, 0xc5, - 0xf9, 0x5f, 0x58, 0xdb, 0x6c, 0xec, 0x38, 0xff, 0xf1, 0x50, 0x80, 0x2c, - 0xdf, 0xfc, 0x21, 0x1a, 0xcf, 0xf2, 0xed, 0x3f, 0x54, 0x80, 0x92, 0xb6, - 0xc1, 0xac, 0x28, 0x37, 0x41, 0x99, 0x02, 0xc5, 0x01, 0x8b, 0xcb, 0x32, - 0xd9, 0x78, 0xb0, 0x32, 0x56, 0xb7, 0xed, 0xe9, 0x3f, 0x1f, 0x1e, 0xff, - 0x12, 0x09, 0x23, 0x20, 0x2b, 0x85, 0x76, 0x00, 0x5a, 0x0c, 0x33, 0xbd, - 0x2c, 0xb3, 0x52, 0x93, 0xc3, 0xe8, 0x40, 0xb6, 0xb3, 0x49, 0x6a, 0x0c, - 0xef, 0x74, 0x43, 0x58, 0x30, 0x86, 0x40, 0x59, 0xc3, 0xcc, 0x7f, 0x01, - 0xbd, 0xa5, 0x10, 0x91, 0x26, 0x20, 0x8e, 0xaa, 0x77, 0xc4, 0xf1, 0x1c, - 0x12, 0xcc, 0x41, 0x47, 0x03, 0x1d, 0x17, 0x96, 0xa0, 0x92, 0x27, 0xdc, - 0x65, 0xb9, 0xa7, 0xc0, 0x98, 0x7f, 0x4f, 0x41, 0xca, 0x26, 0x81, 0x4c, - 0xc1, 0x6e, 0x93, 0xfd, 0x71, 0xee, 0xee, 0x30, 0xba, 0x75, 0x5e, 0xee, - 0xe7, 0xd1, 0x79, 0xb0, 0x83, 0x3a, 0x13, 0x08, 0x28, 0xaa, 0x28, 0x57, - 0xa7, 0xd5, 0xe3, 0x11, 0x8a, 0x46, 0x37, 0x22, 0x28, 0x01, 0x39, 0x6d, - 0x00, 0x10, 0x09, 0x82, 0x40, 0xcb, 0x81, 0x5a, 0xf2, 0x03, 0xd6, 0x29, - 0x10, 0xf3, 0xcd, 0x80, 0x56, 0xf9, 0xa3, 0x29, 0x53, 0x0e, 0x97, 0x02, - 0x60, 0x4f, 0x45, 0x0a, 0x6d, 0x63, 0xaa, 0xc1, 0x42, 0xa8, 0xfa, 0x70, - 0x41, 0x0e, 0xc4, 0x3c, 0x86, 0xb9, 0xf8, 0x95, 0x4b, 0x31, 0x52, 0xfa, - 0x2b, 0x23, 0x31, 0x03, 0x70, 0x23, 0x85, 0x99, 0x81, 0x0e, 0xf9, 0x4f, - 0x3e, 0x77, 0xc4, 0xb4, 0x0a, 0x6a, 0x4b, 0xc8, 0xee, 0xda, 0x5e, 0x0d, - 0x44, 0xc1, 0x1b, 0xa5, 0x97, 0x39, 0xf5, 0xaa, 0xd8, 0xb7, 0xe4, 0x12, - 0xe3, 0x69, 0xc5, 0x9d, 0x2c, 0xa1, 0xa4, 0xad, 0xf2, 0xcf, 0x80, 0xd6, - 0xc0, 0x00, 0x00, 0xca, 0x6f, 0xac, 0x94, 0x4b, 0xcd, 0x6e, 0xc2, 0x0c, - 0x31, 0xbf, 0xad, 0x92, 0x58, 0x0d, 0xbf, 0xb2, 0xa0, 0x59, 0x5e, 0xbc, - 0xe1, 0x5a, 0x3e, 0x7a, 0xf0, 0x99, 0x75, 0x89, 0xd6, 0x88, 0xcb, 0x83, - 0x37, 0x16, 0xa9, 0x1d, 0xac, 0xd1, 0x92, 0x64, 0xb2, 0x19, 0xbb, 0xbf, - 0xa8, 0x5c, 0xcc, 0xe9, 0xa9, 0x1d, 0x8e, 0xb6, 0x17, 0x3f, 0xd8, 0xd9, - 0x5e, 0x86, 0x3f, 0x99, 0x72, 0x35, 0x33, 0x8e, 0x0f, 0x07, 0x1c, 0xb2, - 0x8e, 0xc7, 0xdd, 0xbe, 0x87, 0xb3, 0x3c, 0x5c, 0xed, 0x59, 0xcf, 0x18, - 0xba, 0x82, 0xe6, 0xd5, 0x9d, 0xc9, 0x37, 0xbb, 0xfa, 0xce, 0xdf, 0xad, - 0x2b, 0x0b, 0xaa, 0x85, 0xa6, 0xa9, 0x51, 0x22, 0x90, 0x3b, 0xaf, 0x13, - 0xaf, 0xb4, 0x63, 0x4c, 0x4e, 0xff, 0xf1, 0x50, 0x80, 0x30, 0xff, 0xfc, - 0x21, 0x1a, 0xcf, 0xea, 0x67, 0x7f, 0x04, 0x40, 0x92, 0xb6, 0xc1, 0xac, - 0x2c, 0x19, 0x1a, 0xad, 0x06, 0xc4, 0x80, 0x80, 0x00, 0x00, 0x45, 0x4a, - 0xeb, 0xbe, 0x39, 0xfc, 0x7e, 0x75, 0xbf, 0xbf, 0x12, 0x5f, 0x42, 0x4a, - 0x94, 0x21, 0x7f, 0xe7, 0x6e, 0x36, 0x78, 0xbb, 0x1d, 0x2f, 0x2b, 0x06, - 0x4e, 0xb4, 0x95, 0x82, 0xd8, 0x98, 0x8a, 0x9a, 0xe4, 0xa7, 0x60, 0xd0, - 0xe8, 0x8e, 0x28, 0xf4, 0x6c, 0x0c, 0x64, 0x09, 0x25, 0x20, 0xae, 0x69, - 0xc9, 0x64, 0x2e, 0x0e, 0x78, 0x7a, 0x3b, 0x6b, 0xb4, 0x09, 0x0e, 0x03, - 0x3d, 0xe6, 0x00, 0x22, 0x70, 0xe6, 0x09, 0x47, 0x06, 0x01, 0x01, 0x42, - 0x4b, 0x49, 0xed, 0xda, 0xc8, 0xc1, 0xc0, 0x47, 0xe7, 0x62, 0x34, 0x5f, - 0x36, 0xcb, 0x17, 0x16, 0xb4, 0x66, 0xec, 0x3e, 0x3f, 0xd9, 0xe9, 0xe4, - 0x98, 0x90, 0xe9, 0xa1, 0x9d, 0x86, 0x57, 0xbb, 0x77, 0xfb, 0x7e, 0x9d, - 0x44, 0x28, 0x39, 0xec, 0x5d, 0xca, 0xe8, 0x05, 0xf2, 0x8d, 0x42, 0x5a, - 0x9b, 0x23, 0x1b, 0x92, 0xae, 0xf9, 0xd5, 0x4a, 0x37, 0x59, 0xe8, 0xdf, - 0xa7, 0x84, 0x01, 0x59, 0x8d, 0x6a, 0x28, 0x9b, 0xa2, 0x48, 0x77, 0x77, - 0xf5, 0x74, 0x40, 0x06, 0x73, 0x86, 0xad, 0x8c, 0x44, 0x74, 0x6f, 0x5d, - 0x5f, 0x19, 0xd7, 0x3d, 0x2e, 0xea, 0x8a, 0x36, 0x9e, 0xda, 0x37, 0xa5, - 0xc2, 0x14, 0xba, 0xac, 0x3e, 0x3a, 0x8a, 0xcb, 0x37, 0x41, 0x4c, 0xea, - 0x6c, 0xf5, 0xa7, 0x1b, 0xf3, 0x62, 0x79, 0xe0, 0xd7, 0x38, 0xa2, 0x50, - 0x3a, 0x46, 0xb2, 0x92, 0x07, 0x24, 0x9c, 0xa5, 0x6b, 0xde, 0x79, 0xbe, - 0xd1, 0x3d, 0x7c, 0xa6, 0xfa, 0xd4, 0x2a, 0x0a, 0xd2, 0xad, 0x26, 0xd0, - 0x58, 0x5d, 0xbe, 0xcd, 0x17, 0x61, 0xb6, 0xc6, 0xbe, 0x47, 0x29, 0xa7, - 0xdf, 0xd6, 0x23, 0xa3, 0x8b, 0x6c, 0x42, 0x9a, 0x12, 0x30, 0x3f, 0x1e, - 0x09, 0x2b, 0x83, 0x0e, 0xc2, 0xa3, 0x80, 0xb8, 0xfe, 0x00, 0x65, 0xe5, - 0xe2, 0xd8, 0xb3, 0x2c, 0x45, 0x6e, 0xf3, 0x2e, 0xb8, 0xcc, 0x95, 0x64, - 0x81, 0x96, 0xd6, 0xc3, 0x35, 0x3a, 0xa5, 0x3c, 0xe0, 0xe4, 0x67, 0x13, - 0xb8, 0xa5, 0xc0, 0xd7, 0x0b, 0x87, 0x72, 0x08, 0x79, 0x70, 0xf5, 0x65, - 0xd2, 0x45, 0x96, 0x20, 0xb5, 0xb0, 0x2a, 0x8a, 0xfa, 0xd2, 0xba, 0x4e, - 0xce, 0x70, 0x98, 0x15, 0xaa, 0xc7, 0x40, 0xb8, 0x76, 0x14, 0x42, 0xb6, - 0x71, 0x90, 0xe7, 0x82, 0xb4, 0x17, 0xbe, 0x78, 0x33, 0xae, 0xc8, 0x02, - 0x8a, 0x2b, 0x42, 0x35, 0x2c, 0x14, 0x50, 0x2e, 0xe7, 0x47, 0x78, 0x61, - 0x85, 0x33, 0x86, 0xc2, 0x3f, 0xfe, 0xda, 0xdb, 0x8c, 0x6a, 0x00, 0x52, - 0x5e, 0x08, 0x2d, 0x42, 0xc9, 0x67, 0x9e, 0x1f, 0x97, 0xa7, 0x20, 0x38, - 0xff, 0xf1, 0x50, 0x80, 0x2f, 0xbf, 0xfc, 0x21, 0x1a, 0xcf, 0xfe, 0xab, - 0x2f, 0x4d, 0x40, 0x94, 0xba, 0x49, 0xd5, 0x6c, 0x48, 0x08, 0x00, 0x00, - 0x03, 0x5b, 0x3d, 0xbd, 0x1e, 0x7c, 0x1f, 0x7d, 0x6b, 0x9f, 0x3d, 0x08, - 0x47, 0x1c, 0x47, 0xd2, 0x7f, 0x25, 0xd6, 0x96, 0xf4, 0x5c, 0x52, 0xb7, - 0x13, 0x1f, 0x60, 0xad, 0xba, 0x51, 0xa8, 0xc6, 0x0a, 0x26, 0xc6, 0xcb, - 0x71, 0xa2, 0x33, 0x58, 0x4f, 0x08, 0x83, 0x17, 0xa4, 0x63, 0xec, 0x81, - 0x04, 0x84, 0x1d, 0x25, 0xa4, 0x5c, 0x70, 0x32, 0xb1, 0x70, 0xb5, 0xb6, - 0x6c, 0xd5, 0x5b, 0xc5, 0x46, 0x8b, 0x84, 0x05, 0x03, 0xca, 0x80, 0x00, - 0x5a, 0x04, 0x20, 0x50, 0xee, 0x6d, 0x11, 0xc2, 0x39, 0x60, 0x41, 0x47, - 0x74, 0x73, 0x33, 0x58, 0xf4, 0x86, 0x6c, 0x3a, 0x0c, 0x74, 0xe9, 0x86, - 0xae, 0x9a, 0x2e, 0x78, 0x3f, 0x27, 0xe6, 0x8a, 0xb2, 0x55, 0x98, 0xba, - 0xa9, 0x22, 0x23, 0x3b, 0x63, 0xd9, 0x7d, 0x17, 0x86, 0x12, 0x15, 0xd7, - 0xa0, 0xa2, 0x6c, 0x19, 0x99, 0xe8, 0x42, 0x57, 0xd3, 0xa6, 0xaa, 0xa5, - 0x53, 0xbd, 0xf6, 0x44, 0xae, 0x12, 0xdb, 0xff, 0x62, 0x05, 0x03, 0x10, - 0x17, 0xd0, 0x0d, 0xa2, 0x1c, 0xfd, 0x3b, 0x98, 0x01, 0x5a, 0xbc, 0xd6, - 0xe7, 0xaa, 0x2b, 0x14, 0x8e, 0x90, 0xe7, 0x02, 0x00, 0xbc, 0xed, 0x64, - 0x25, 0x2c, 0x40, 0xaf, 0x8e, 0xec, 0x5d, 0x06, 0xeb, 0xa5, 0xd4, 0xba, - 0xe7, 0xc2, 0xf5, 0x8e, 0x8d, 0x12, 0xa5, 0x4a, 0xe4, 0xb4, 0x67, 0x2b, - 0x2b, 0x4b, 0xa6, 0x0d, 0x6d, 0x98, 0x5b, 0xbb, 0x63, 0xca, 0xf5, 0x63, - 0x62, 0x72, 0xf3, 0x81, 0x08, 0x9b, 0xa8, 0x08, 0x3c, 0x1a, 0x2e, 0xd9, - 0x23, 0x7c, 0xde, 0x3d, 0x2d, 0x91, 0x7e, 0x47, 0x3a, 0xe0, 0x25, 0xaa, - 0xf5, 0xce, 0x3c, 0xbd, 0x7e, 0xdd, 0xe3, 0xaa, 0x16, 0x8d, 0x4f, 0x04, - 0xa5, 0xb6, 0x0b, 0x29, 0xf8, 0x00, 0x1b, 0xd6, 0xf5, 0xbd, 0x6f, 0x4d, - 0xb5, 0xbd, 0x35, 0xbd, 0xea, 0x57, 0x39, 0xc5, 0xf3, 0x24, 0xc9, 0x5c, - 0x00, 0x92, 0xac, 0x0c, 0x6b, 0x3a, 0x3d, 0x2e, 0x53, 0x90, 0x58, 0x40, - 0x63, 0x01, 0x5d, 0xb0, 0x85, 0xb0, 0x5b, 0xc9, 0x65, 0x42, 0x1f, 0x68, - 0x4f, 0xb0, 0x08, 0xbf, 0x99, 0x67, 0x69, 0x35, 0x85, 0x3d, 0x13, 0x9e, - 0x86, 0xb4, 0x24, 0x21, 0x1a, 0x2f, 0x02, 0xf9, 0x10, 0x18, 0x15, 0x0f, - 0xcb, 0x48, 0xce, 0x46, 0x1a, 0x95, 0x87, 0xfe, 0x57, 0xea, 0xbc, 0x71, - 0x9e, 0x96, 0x18, 0xe6, 0x8c, 0xa1, 0x05, 0x58, 0xbc, 0xe9, 0x95, 0xf2, - 0xff, 0xee, 0x7e, 0xbf, 0xcd, 0xb9, 0xab, 0xc2, 0xa2, 0x0c, 0x41, 0x10, - 0x4a, 0x21, 0x7a, 0x93, 0xf7, 0x4c, 0x28, 0x66, 0x38, 0xff, 0xf1, 0x50, - 0x80, 0x2f, 0x7f, 0xfc, 0x21, 0x1a, 0xce, 0xc6, 0xaf, 0xef, 0x18, 0x00, - 0x96, 0xb6, 0xc1, 0xac, 0x2c, 0x39, 0x6a, 0x09, 0x89, 0x01, 0x00, 0x00, - 0x00, 0x0b, 0xf3, 0xee, 0x5e, 0x1f, 0x13, 0x59, 0xf1, 0xd0, 0x41, 0xee, - 0xd3, 0x28, 0x3e, 0xaf, 0x30, 0xe4, 0xf1, 0x73, 0x8f, 0x5c, 0x35, 0xe6, - 0xa4, 0x8b, 0xce, 0x15, 0x7e, 0x57, 0x51, 0x56, 0x73, 0x43, 0x04, 0x01, - 0x26, 0xaa, 0x0c, 0xf5, 0xc8, 0xf3, 0x69, 0x33, 0x42, 0x25, 0xb6, 0x24, - 0xb1, 0x6d, 0x95, 0x92, 0xab, 0xa2, 0x1f, 0x19, 0x34, 0x1f, 0xf4, 0x5e, - 0x48, 0x43, 0xfd, 0x5c, 0x89, 0x26, 0x73, 0x78, 0x0e, 0x07, 0x43, 0x94, - 0xb0, 0x38, 0x0d, 0xb0, 0x01, 0x4b, 0x2a, 0xd6, 0xf4, 0x8c, 0x45, 0xd1, - 0xa3, 0xbf, 0x60, 0x40, 0x24, 0xe7, 0x79, 0xd5, 0xfb, 0xcf, 0xdf, 0xf4, - 0x98, 0x31, 0x92, 0x97, 0x32, 0x02, 0x14, 0x9c, 0xee, 0xe3, 0xf5, 0x2d, - 0x04, 0xcc, 0x61, 0x6c, 0x15, 0xa9, 0x21, 0x20, 0x4a, 0x15, 0x56, 0xac, - 0xed, 0x71, 0x5b, 0xe9, 0xbb, 0x0c, 0xef, 0x1c, 0xb5, 0xd9, 0x63, 0x11, - 0x50, 0x46, 0xbf, 0xca, 0xf9, 0x96, 0x14, 0x18, 0x6c, 0x0b, 0x90, 0xb9, - 0x42, 0xab, 0xb1, 0x50, 0x91, 0x77, 0x9a, 0x56, 0x12, 0x42, 0x05, 0x77, - 0xcb, 0x9a, 0x0c, 0x0c, 0xe6, 0x04, 0x00, 0x2b, 0x89, 0x4a, 0x71, 0x54, - 0x2d, 0xb2, 0xc0, 0x1c, 0xc4, 0x0d, 0x6b, 0x9a, 0x4a, 0x62, 0xe0, 0x44, - 0x02, 0x93, 0xbb, 0x55, 0x4b, 0x82, 0x29, 0x1d, 0xe7, 0x7b, 0x74, 0x32, - 0x0b, 0x46, 0x2a, 0xdc, 0x31, 0x3a, 0xa2, 0x0a, 0x38, 0x91, 0x89, 0x06, - 0x13, 0xae, 0xd7, 0x8b, 0x3c, 0xb2, 0xa5, 0xa7, 0xcf, 0x39, 0x1a, 0xfb, - 0x64, 0x6f, 0x75, 0x6a, 0x27, 0x03, 0x01, 0xa4, 0x50, 0x53, 0xec, 0x41, - 0x2d, 0x63, 0x84, 0xd8, 0x60, 0x6c, 0x3b, 0x0a, 0x8e, 0xc3, 0x23, 0xf2, - 0x98, 0x7d, 0x46, 0x5b, 0x0b, 0x65, 0xe5, 0xe5, 0x81, 0x96, 0x2b, 0x4f, - 0x13, 0x50, 0xcb, 0x95, 0x16, 0x01, 0x8a, 0x20, 0xfd, 0x90, 0x7e, 0xfe, - 0x5e, 0x6d, 0xb4, 0xa5, 0x47, 0x40, 0x14, 0x67, 0x3a, 0xb4, 0x94, 0xce, - 0xc7, 0x52, 0x99, 0x2d, 0x60, 0x0c, 0x07, 0xc5, 0xc9, 0xc4, 0xdd, 0xe4, - 0x32, 0x56, 0x08, 0x68, 0xeb, 0xd8, 0x1d, 0x83, 0xe3, 0xe0, 0x47, 0x21, - 0x54, 0xfd, 0x4a, 0xb6, 0x25, 0xf7, 0x90, 0x93, 0x73, 0x9d, 0x83, 0x52, - 0x94, 0x50, 0x01, 0x63, 0x59, 0xe1, 0x13, 0x7e, 0xb9, 0x15, 0x75, 0x80, - 0xdd, 0x94, 0x07, 0x38, 0xf3, 0x4d, 0x96, 0x5a, 0xf0, 0x35, 0x8d, 0xa6, - 0xf5, 0xa0, 0x50, 0x14, 0xb6, 0x5c, 0x7f, 0xc5, 0xf0, 0x42, 0x46, 0x39, - 0xbd, 0xc5, 0x2c, 0xe0, 0xff, 0xf1, 0x50, 0x80, 0x2f, 0x1f, 0xfc, 0x21, - 0x1a, 0xcd, 0xe7, 0xff, 0xff, 0x1f, 0x40, 0x96, 0xba, 0x30, 0xa5, 0x2a, - 0x64, 0x13, 0x0e, 0x02, 0x00, 0x00, 0x00, 0x0d, 0x6c, 0x0f, 0x6d, 0x69, - 0xc7, 0x90, 0x25, 0x27, 0x2a, 0x0f, 0xcf, 0x62, 0xf8, 0x2a, 0x78, 0xaf, - 0x9a, 0xa3, 0x8d, 0x7a, 0xcc, 0xc3, 0xea, 0x4e, 0x75, 0x04, 0x25, 0x44, - 0x12, 0x25, 0x49, 0x24, 0x4d, 0x5e, 0xd0, 0xbe, 0x1f, 0xdc, 0x80, 0x82, - 0x81, 0x58, 0xf0, 0x39, 0x6a, 0x9b, 0xcb, 0x02, 0x8a, 0x68, 0xf1, 0xb0, - 0xac, 0x91, 0x3c, 0xe0, 0xbe, 0xbc, 0x8c, 0xc4, 0x21, 0x11, 0x5e, 0xbc, - 0x40, 0x52, 0x3a, 0xb2, 0x8d, 0x01, 0xaa, 0x75, 0xc9, 0x30, 0x23, 0xc6, - 0xc7, 0x4b, 0x2a, 0xd8, 0x68, 0x53, 0x7f, 0xca, 0x0c, 0x0f, 0x26, 0xc9, - 0x31, 0x9e, 0xfa, 0x64, 0xee, 0x1f, 0xe9, 0x3e, 0x1a, 0x50, 0x80, 0x5e, - 0x19, 0xd1, 0x85, 0x26, 0xa6, 0xb2, 0x8c, 0xf0, 0x7a, 0x47, 0xed, 0xbc, - 0x14, 0x4d, 0x23, 0x28, 0x57, 0x2a, 0x10, 0xab, 0x00, 0x2b, 0x50, 0x02, - 0x2c, 0x8b, 0x4a, 0xc8, 0x44, 0xdc, 0x2a, 0x34, 0x3d, 0x84, 0x5a, 0x22, - 0x45, 0xf6, 0xd8, 0xc4, 0x09, 0x99, 0x54, 0xce, 0xff, 0xce, 0x4a, 0x01, - 0xbb, 0x31, 0x40, 0xa0, 0xcb, 0xf7, 0xf2, 0xc4, 0xd2, 0x70, 0xd8, 0xcd, - 0x22, 0xaa, 0x4c, 0xea, 0x05, 0x6f, 0xaf, 0xdb, 0x8c, 0x5a, 0xa2, 0x56, - 0xac, 0xb1, 0x5f, 0x9a, 0xdb, 0x40, 0x90, 0x0d, 0x67, 0x18, 0x82, 0x1d, - 0xd4, 0x37, 0x4b, 0x1d, 0x12, 0xcc, 0xf3, 0x61, 0x14, 0xe7, 0x01, 0x72, - 0x4b, 0x2f, 0xe2, 0x41, 0x65, 0x07, 0x54, 0x05, 0x5a, 0x1c, 0x5c, 0xbb, - 0x16, 0x8e, 0x2a, 0x91, 0xd6, 0x07, 0x58, 0xf7, 0x77, 0x81, 0x12, 0xde, - 0xc5, 0x6b, 0x16, 0x66, 0x07, 0xa1, 0xab, 0x26, 0x29, 0x31, 0xe9, 0xcb, - 0x58, 0xa1, 0x56, 0x18, 0x1b, 0x0e, 0xc2, 0x83, 0xb0, 0xc8, 0xfe, 0x0a, - 0x82, 0x90, 0x00, 0x00, 0x71, 0xbd, 0xef, 0x35, 0x6a, 0x5c, 0xa5, 0x80, - 0x63, 0xf6, 0x4f, 0x9f, 0x7f, 0xf4, 0x17, 0xad, 0x36, 0x5a, 0xdd, 0x0a, - 0x58, 0xfc, 0xc8, 0x2f, 0x32, 0x17, 0xd7, 0xd5, 0x4b, 0x79, 0x85, 0x9b, - 0x4f, 0x51, 0xc9, 0xbb, 0xdc, 0x82, 0x6b, 0x55, 0x89, 0x0a, 0x69, 0x9b, - 0x0d, 0xe8, 0x34, 0x8d, 0xb3, 0x80, 0xcf, 0xa9, 0xeb, 0x0b, 0x5c, 0xdc, - 0xd7, 0x6c, 0xe8, 0x2f, 0x63, 0x5e, 0x50, 0x0d, 0xac, 0xbc, 0xbd, 0x9f, - 0xe6, 0x19, 0x01, 0x63, 0x4a, 0x95, 0xf4, 0xa3, 0x9c, 0x80, 0x49, 0xc4, - 0x38, 0x58, 0xc0, 0x68, 0x9b, 0x95, 0x9b, 0x7e, 0x10, 0x28, 0x49, 0x64, - 0xe3, 0xde, 0xfd, 0x71, 0x75, 0x04, 0x23, 0x80, 0xff, 0xf1, 0x50, 0x80, - 0x2f, 0x9f, 0xfc, 0x21, 0x1a, 0xcf, 0x6d, 0xff, 0x6b, 0x54, 0xc0, 0x95, - 0xba, 0x28, 0xec, 0x32, 0x91, 0x1a, 0x11, 0x85, 0x01, 0x03, 0xeb, 0xe4, - 0x00, 0x00, 0x17, 0x80, 0x7d, 0x1d, 0x5f, 0xb0, 0x67, 0xc7, 0xff, 0xf8, - 0x67, 0x0e, 0x2a, 0xc9, 0x8b, 0xda, 0x55, 0xd9, 0x32, 0x3e, 0xba, 0x50, - 0x59, 0x48, 0x86, 0x0d, 0x0d, 0x07, 0x85, 0x83, 0x40, 0x4f, 0x08, 0xb2, - 0x95, 0x07, 0xe6, 0x1f, 0xca, 0x74, 0x14, 0x02, 0x5c, 0x53, 0x01, 0x66, - 0x29, 0x7b, 0xd9, 0x73, 0x85, 0xb5, 0xc9, 0x14, 0x90, 0x3d, 0x4d, 0xed, - 0x93, 0x59, 0x0a, 0x82, 0x38, 0x57, 0x00, 0x03, 0xec, 0x45, 0x96, 0x0c, - 0xe4, 0x7d, 0xaf, 0xe8, 0x47, 0x97, 0x16, 0xca, 0xfc, 0x3b, 0xf5, 0xd3, - 0xda, 0xf6, 0x09, 0x7b, 0x67, 0x51, 0x53, 0xb8, 0x83, 0x47, 0x8b, 0x7d, - 0xec, 0x06, 0x03, 0x0c, 0x08, 0xc4, 0xbe, 0x4e, 0x88, 0x94, 0x45, 0x97, - 0x19, 0xb5, 0x3b, 0x0f, 0xd5, 0x3d, 0x57, 0x3c, 0x6d, 0x94, 0xdb, 0x39, - 0xad, 0x5c, 0x45, 0xe4, 0x32, 0x06, 0x1c, 0x4e, 0xeb, 0x98, 0x50, 0x5e, - 0x50, 0x10, 0x63, 0x8c, 0xe2, 0x2a, 0xbc, 0xfb, 0xd2, 0x71, 0xb6, 0x4d, - 0x18, 0x5b, 0x7e, 0x20, 0x90, 0x28, 0xcf, 0x4f, 0xbb, 0x6d, 0x04, 0x0d, - 0x30, 0x50, 0xa5, 0x25, 0xdf, 0xee, 0x28, 0xf6, 0x4a, 0x52, 0x5d, 0xf3, - 0xd6, 0x1a, 0xb5, 0xde, 0xb3, 0xbd, 0x61, 0xd2, 0xf3, 0xce, 0x3d, 0x85, - 0x19, 0x92, 0x87, 0x02, 0xd0, 0x80, 0xf2, 0x5e, 0xda, 0x7c, 0x70, 0xb3, - 0x96, 0xee, 0x56, 0x20, 0xe7, 0x3a, 0x8e, 0x66, 0xb5, 0xe4, 0x56, 0x31, - 0xac, 0x61, 0x0e, 0x78, 0xac, 0xc5, 0xd5, 0xf0, 0x35, 0x0b, 0x77, 0xd4, - 0x0f, 0x56, 0x40, 0x87, 0x55, 0x4f, 0x9e, 0x6c, 0xb2, 0x20, 0x39, 0xe5, - 0x31, 0x2b, 0x6d, 0x83, 0x58, 0x50, 0x76, 0x19, 0x1f, 0xc0, 0x65, 0xb2, - 0xd9, 0x60, 0x19, 0x6c, 0xbc, 0xb6, 0x6a, 0xb7, 0x55, 0x3d, 0xbb, 0x85, - 0x45, 0x83, 0x64, 0xb8, 0xe5, 0xd8, 0x0f, 0xbd, 0xa5, 0x86, 0xb0, 0x28, - 0x00, 0xc6, 0xb0, 0xa7, 0x64, 0xa0, 0x33, 0xb1, 0xb6, 0x39, 0x92, 0x4b, - 0x68, 0xf8, 0xed, 0xd1, 0xf1, 0x9e, 0xc2, 0x42, 0xc6, 0xac, 0x93, 0x41, - 0xa3, 0x12, 0x32, 0x75, 0xc9, 0x27, 0x72, 0x18, 0x3c, 0x4a, 0x51, 0x22, - 0x35, 0xc9, 0x40, 0x45, 0x85, 0x99, 0xe0, 0xa9, 0x0b, 0x2a, 0xed, 0xae, - 0x24, 0x20, 0x30, 0x7e, 0x4f, 0x00, 0x8f, 0xea, 0x21, 0xb1, 0xc5, 0x5f, - 0x0e, 0xdd, 0x42, 0x32, 0x81, 0x5a, 0x65, 0x69, 0x28, 0xa3, 0x46, 0x15, - 0x4a, 0xaa, 0xcc, 0x2a, 0xc9, 0x32, 0x8c, 0xe7, 0x3f, 0x4b, 0xf9, 0xc6, - 0x25, 0x5c, 0x41, 0xc0, 0xff, 0xf1, 0x50, 0x80, 0x31, 0xff, 0xfc, 0x21, - 0x1a, 0xce, 0xe5, 0xfb, 0xff, 0x45, 0x40, 0x93, 0xba, 0x20, 0xec, 0x24, - 0x29, 0x1a, 0xa1, 0x02, 0xc4, 0x80, 0x95, 0x0a, 0x4a, 0x80, 0xa8, 0x00, - 0xa6, 0xb7, 0x2b, 0x8e, 0x4f, 0x6f, 0xad, 0xf1, 0x7c, 0x08, 0x1d, 0x46, - 0xbf, 0x74, 0xc9, 0x88, 0x9e, 0x89, 0x0c, 0xb2, 0xa1, 0x30, 0x40, 0xee, - 0xaf, 0xe7, 0x53, 0x71, 0x14, 0xdd, 0x88, 0x4c, 0x69, 0x8c, 0x31, 0x6c, - 0xf2, 0xec, 0xcd, 0x56, 0xc2, 0x14, 0xad, 0xaa, 0xb2, 0xad, 0x22, 0x8b, - 0x04, 0x36, 0xe7, 0xa2, 0x42, 0x84, 0xf2, 0x43, 0xf6, 0xd0, 0xef, 0x07, - 0x47, 0x79, 0xc8, 0xe2, 0x1a, 0xc8, 0xa3, 0xf0, 0x32, 0xcf, 0x0c, 0xcd, - 0xed, 0x21, 0xe1, 0x86, 0x40, 0xfd, 0xa9, 0x49, 0xb3, 0x84, 0x10, 0xd3, - 0x32, 0xe4, 0x61, 0x5c, 0x9d, 0xf0, 0xd4, 0xf6, 0x91, 0xf3, 0x9c, 0x76, - 0x3f, 0x2b, 0xf8, 0xa5, 0xa2, 0x80, 0xc3, 0x0d, 0xe6, 0x5b, 0x5e, 0x83, - 0x29, 0x2d, 0x3e, 0x33, 0x7b, 0x2f, 0xe9, 0xfb, 0x82, 0x53, 0x81, 0x92, - 0x32, 0xa1, 0x35, 0x92, 0xc8, 0x0c, 0xfc, 0xfb, 0x1c, 0xae, 0xa0, 0x21, - 0x73, 0x63, 0x37, 0x89, 0x8c, 0x14, 0xd7, 0x57, 0xe1, 0xfe, 0xbd, 0x31, - 0x15, 0x49, 0x0e, 0xdb, 0x29, 0x29, 0x5d, 0xd2, 0x2e, 0x3a, 0xbe, 0x30, - 0x92, 0x02, 0x3b, 0x62, 0xcd, 0xe3, 0x05, 0x89, 0x5e, 0xbe, 0x5c, 0x80, - 0x1b, 0xa4, 0x58, 0x05, 0x2a, 0xab, 0x7e, 0x9f, 0x02, 0xda, 0x10, 0x91, - 0x0a, 0x41, 0xa2, 0x83, 0xde, 0x88, 0x67, 0xac, 0xab, 0x6a, 0x1c, 0x56, - 0xad, 0x98, 0xe1, 0x88, 0xd6, 0xce, 0x75, 0x32, 0x14, 0xd0, 0xe4, 0x59, - 0x5b, 0xe6, 0x74, 0xb1, 0x57, 0x09, 0xfb, 0x0c, 0xd2, 0xce, 0xf5, 0x1b, - 0x6a, 0xee, 0xf0, 0x8d, 0x34, 0x56, 0x33, 0xea, 0xbb, 0x5d, 0x35, 0x79, - 0xad, 0x37, 0x8e, 0xc6, 0x2b, 0x55, 0xa9, 0x3c, 0xb6, 0x33, 0xad, 0x59, - 0x68, 0x74, 0x52, 0x93, 0xb6, 0xc1, 0xac, 0x2c, 0x3b, 0x09, 0x0a, 0x43, - 0xf0, 0x00, 0x00, 0x00, 0x15, 0x97, 0xcc, 0xa9, 0x61, 0x31, 0x00, 0xbe, - 0x16, 0xe1, 0x8d, 0xb9, 0xb8, 0x91, 0xe1, 0x1f, 0xbc, 0x26, 0x37, 0x39, - 0x46, 0xa4, 0x7b, 0xaa, 0x39, 0x5d, 0xe9, 0xb5, 0x20, 0x6a, 0xa0, 0x2e, - 0x03, 0x57, 0xc4, 0x4a, 0x4a, 0xbf, 0x2c, 0x5b, 0x96, 0x2b, 0x7c, 0xed, - 0x9a, 0x1a, 0x80, 0x0f, 0x7e, 0x22, 0x51, 0x9f, 0x05, 0x86, 0xeb, 0x42, - 0x21, 0x5e, 0x47, 0x50, 0x16, 0x53, 0x21, 0x4a, 0x51, 0x84, 0xfb, 0x57, - 0x51, 0xb8, 0xdd, 0x18, 0x00, 0x32, 0xd2, 0x20, 0xf4, 0xf8, 0x00, 0xba, - 0x39, 0x9a, 0x30, 0x24, 0x09, 0x98, 0x99, 0xbb, 0x2f, 0x26, 0x36, 0x28, - 0x61, 0xcb, 0x17, 0xb9, 0x7a, 0x00, 0xf3, 0x24, 0x58, 0xad, 0x5d, 0xd3, - 0xfb, 0x8f, 0xc6, 0x52, 0xbc, 0xac, 0x70, 0xff, 0xf1, 0x50, 0x80, 0x2e, - 0xdf, 0xfc, 0x21, 0x1a, 0xca, 0xec, 0x77, 0xff, 0x15, 0x80, 0x93, 0xb6, - 0xb1, 0xac, 0x28, 0x39, 0x52, 0x04, 0xc6, 0x81, 0x61, 0x40, 0xc0, 0x94, - 0x00, 0x00, 0x2f, 0x38, 0xe6, 0xf3, 0xe3, 0xdd, 0x7f, 0x7e, 0xb3, 0xdb, - 0x2c, 0x72, 0x1a, 0x85, 0xde, 0x67, 0xea, 0x1d, 0x9b, 0x75, 0xc7, 0xe6, - 0xdf, 0x1f, 0xc4, 0x78, 0x63, 0xd5, 0xf8, 0xb4, 0x59, 0x46, 0x6e, 0x19, - 0x68, 0xc7, 0xda, 0x9b, 0x28, 0x52, 0xa2, 0x8a, 0x7d, 0x5e, 0xb3, 0x5f, - 0x64, 0xcb, 0x84, 0x77, 0x98, 0x0a, 0xcc, 0x16, 0x25, 0xab, 0x34, 0xd9, - 0x21, 0x5d, 0xf2, 0x31, 0x67, 0x7c, 0x78, 0x8a, 0x9a, 0x9a, 0x00, 0x0f, - 0x9b, 0x1f, 0x16, 0x54, 0x1c, 0xd0, 0x53, 0x05, 0xfd, 0x10, 0xe1, 0xc3, - 0xac, 0xb4, 0x53, 0x54, 0x0c, 0xda, 0xc7, 0xfd, 0xe7, 0xd7, 0x71, 0x52, - 0x32, 0x11, 0x35, 0x80, 0xc3, 0x12, 0x2a, 0xa6, 0xca, 0xf8, 0x2f, 0xb7, - 0x54, 0xd0, 0xc4, 0x8c, 0xe0, 0x22, 0x2c, 0x02, 0xbb, 0xce, 0x94, 0xa0, - 0x4c, 0xdc, 0x05, 0xc3, 0x05, 0xd4, 0x86, 0xfe, 0xb3, 0x93, 0x94, 0x94, - 0x0c, 0x68, 0x30, 0x28, 0x80, 0x8e, 0xe2, 0xb8, 0x00, 0x91, 0xcb, 0x3b, - 0x43, 0x88, 0xda, 0x7c, 0x8f, 0xfa, 0x08, 0x91, 0x95, 0xe0, 0x4a, 0x63, - 0xcd, 0x43, 0xb0, 0x91, 0xd5, 0xd1, 0x7a, 0x65, 0x57, 0x36, 0x80, 0x7f, - 0x53, 0x1b, 0x99, 0x9e, 0xf9, 0x80, 0x8b, 0x45, 0x15, 0xcb, 0x32, 0x03, - 0x8a, 0x01, 0x32, 0x05, 0x91, 0xbf, 0xa0, 0xeb, 0x0a, 0x13, 0xec, 0x23, - 0x51, 0xa9, 0x61, 0xb5, 0xd4, 0x49, 0x78, 0xed, 0x92, 0xfd, 0x16, 0x78, - 0xda, 0x86, 0x91, 0x90, 0x60, 0xba, 0xa6, 0x93, 0x0a, 0x83, 0x36, 0xec, - 0x44, 0x27, 0x2a, 0x99, 0x32, 0x80, 0x99, 0xc9, 0x91, 0x0c, 0x12, 0x77, - 0x06, 0x1c, 0x84, 0x87, 0x61, 0x71, 0xfc, 0x06, 0xb6, 0x00, 0x00, 0x06, - 0x5b, 0x1d, 0xf4, 0xc9, 0xaa, 0x94, 0xa1, 0x5e, 0xca, 0x27, 0x53, 0xbc, - 0xf6, 0xe9, 0x30, 0xcb, 0xa4, 0xbd, 0x1a, 0x78, 0x48, 0x3a, 0xf3, 0x31, - 0x8b, 0x0b, 0x66, 0xbf, 0xa0, 0xba, 0xd9, 0x6d, 0x8a, 0xe6, 0xef, 0x58, - 0xe5, 0x1f, 0x4f, 0xc3, 0x6f, 0xaa, 0x8a, 0x19, 0x2c, 0x3d, 0x7d, 0xe0, - 0xd2, 0x50, 0xc8, 0x5d, 0x2d, 0x28, 0x97, 0x22, 0xe4, 0x3b, 0x98, 0xf0, - 0x99, 0x2f, 0x9e, 0x44, 0x45, 0x4a, 0xcd, 0xc1, 0x76, 0xff, 0xe8, 0xff, - 0x7e, 0xd2, 0xb1, 0x38, 0x8f, 0xa1, 0x0f, 0xaf, 0x5c, 0xd7, 0xf1, 0x18, - 0xbe, 0x38, 0x2c, 0xe1, 0x7f, 0x00, 0x01, 0x40, 0x4e, 0x6b, 0x17, 0x8c, - 0x0a, 0x81, 0x97, 0xaa, 0xfd, 0x1e, 0x30, 0x04, 0x70, 0xff, 0xf1, 0x50, - 0x80, 0x30, 0x1f, 0xfc, 0x21, 0x1a, 0xca, 0xe0, 0xca, 0xbb, 0x00, 0x00, - 0x92, 0xb7, 0x41, 0x6c, 0x28, 0x39, 0x41, 0x99, 0x8c, 0x00, 0x00, 0x04, - 0xae, 0x39, 0x4a, 0xd6, 0xf5, 0xbf, 0x8f, 0x75, 0xfd, 0x79, 0xdf, 0x13, - 0x81, 0x09, 0xb4, 0x4a, 0x4c, 0xe4, 0xfc, 0xbc, 0xba, 0x1b, 0x35, 0x92, - 0xfa, 0x72, 0x08, 0xa2, 0x5c, 0xc6, 0x96, 0x93, 0x1e, 0x15, 0x73, 0xed, - 0x08, 0x4c, 0x19, 0x73, 0x89, 0x4c, 0x47, 0x33, 0xf4, 0xe2, 0x8f, 0x32, - 0xd9, 0x6d, 0x44, 0x9d, 0x78, 0x96, 0x5e, 0x47, 0x60, 0x6c, 0xb1, 0x51, - 0x2e, 0xbf, 0x3d, 0x4a, 0x25, 0x99, 0x68, 0xad, 0x03, 0x11, 0xa6, 0x2d, - 0x4a, 0x30, 0x89, 0x52, 0x03, 0x47, 0x0f, 0x36, 0xcb, 0xa3, 0x8d, 0x00, - 0xb2, 0xc9, 0xfb, 0x81, 0x75, 0xad, 0x66, 0x46, 0x10, 0xda, 0xdd, 0x75, - 0x5f, 0x1f, 0xe4, 0xa8, 0xcb, 0x2b, 0x94, 0x67, 0xbc, 0x42, 0x42, 0x92, - 0xcb, 0x77, 0x4f, 0xf4, 0x4e, 0xbb, 0x40, 0x95, 0x04, 0x20, 0x30, 0x00, - 0x74, 0x5d, 0x0e, 0x79, 0x80, 0xb4, 0x05, 0xc0, 0x46, 0x17, 0x39, 0x47, - 0x27, 0xa0, 0xd0, 0xe4, 0x60, 0x03, 0x77, 0xb8, 0xc1, 0x60, 0x6e, 0xf4, - 0x76, 0x44, 0xed, 0xd7, 0x71, 0x64, 0x74, 0x4e, 0x91, 0xf7, 0x57, 0xbd, - 0xc9, 0xd0, 0x10, 0x20, 0x46, 0xd3, 0x57, 0xbf, 0x81, 0xbd, 0xd9, 0xa8, - 0x75, 0x13, 0x33, 0x82, 0x9c, 0x19, 0x1c, 0xb3, 0x09, 0xee, 0x9c, 0x4e, - 0x55, 0x01, 0xea, 0x4e, 0xc6, 0x5a, 0x5e, 0x67, 0x10, 0x07, 0x98, 0xcf, - 0x17, 0xb7, 0x4b, 0x94, 0x5d, 0x9d, 0x11, 0x6b, 0x7a, 0x68, 0x92, 0x56, - 0x20, 0xc0, 0x4a, 0x90, 0xd1, 0x5c, 0xf4, 0xea, 0x8c, 0xa1, 0x70, 0x4d, - 0xdc, 0xba, 0x1f, 0xf9, 0xf3, 0x68, 0xc7, 0xd9, 0x57, 0x1c, 0xae, 0x89, - 0xbe, 0xd6, 0x84, 0x9d, 0xb6, 0x0d, 0x21, 0x41, 0xd8, 0x5c, 0x7f, 0x04, - 0xae, 0x36, 0xa0, 0x8a, 0x4a, 0x95, 0x2a, 0x4a, 0xa8, 0x99, 0x55, 0x32, - 0xea, 0x4a, 0x99, 0x2a, 0x05, 0x19, 0xf4, 0x37, 0x35, 0x3f, 0x2a, 0x84, - 0x8b, 0x4d, 0x48, 0x49, 0xe2, 0x83, 0xab, 0x82, 0xa0, 0xa8, 0xa9, 0x3f, - 0x20, 0x48, 0xab, 0x22, 0xe7, 0xa7, 0x68, 0xb0, 0xfe, 0xf4, 0x18, 0x66, - 0x23, 0x90, 0x8f, 0x10, 0x27, 0x91, 0xc9, 0x93, 0x31, 0x23, 0xd3, 0x2f, - 0x06, 0x9a, 0x7e, 0x8a, 0xe4, 0xcd, 0xf1, 0xbe, 0x11, 0x26, 0x85, 0xdb, - 0x9c, 0x07, 0x32, 0x4c, 0x0b, 0x12, 0x7b, 0x1f, 0xfb, 0x5d, 0xef, 0x1c, - 0x74, 0x0d, 0x02, 0x8b, 0xe8, 0xb6, 0x38, 0xdb, 0x69, 0xe1, 0x28, 0x37, - 0x3c, 0xfe, 0x4b, 0x8d, 0xd7, 0x88, 0x02, 0x15, 0x25, 0x28, 0x54, 0x48, - 0x49, 0x37, 0x8f, 0x83, 0xc9, 0xf6, 0x30, 0x2c, 0x38, 0xff, 0xf1, 0x50, - 0x80, 0x2a, 0xbf, 0xfc, 0x21, 0x1a, 0xcf, 0xfe, 0x9e, 0x5f, 0x01, 0x00, - 0x93, 0xb6, 0xb1, 0x65, 0xa6, 0x66, 0x30, 0x01, 0x97, 0x8b, 0x18, 0xbc, - 0xb0, 0xaa, 0x94, 0x7b, 0x7a, 0xd3, 0xf5, 0xd6, 0x69, 0xa0, 0x83, 0x38, - 0x93, 0x18, 0xad, 0xf4, 0x44, 0xa4, 0xc9, 0xb7, 0xd5, 0x70, 0x58, 0xff, - 0x46, 0x1e, 0xaa, 0x0c, 0x51, 0xc3, 0xb9, 0x09, 0xae, 0x8a, 0x36, 0x70, - 0x38, 0xa5, 0xbc, 0x0a, 0x93, 0x74, 0x68, 0x30, 0x2c, 0x14, 0x04, 0x26, - 0xe1, 0x7f, 0xd5, 0xd8, 0x0a, 0x7d, 0x6d, 0x28, 0x50, 0x37, 0x62, 0xe6, - 0x47, 0xe8, 0xa0, 0xc8, 0xed, 0xc1, 0x53, 0x2f, 0xea, 0x34, 0x38, 0x2c, - 0x58, 0x61, 0xc7, 0xdf, 0x56, 0xe2, 0x05, 0x67, 0x2b, 0x44, 0xb0, 0x5f, - 0x57, 0xfa, 0x9f, 0x93, 0xcc, 0x94, 0xca, 0x0c, 0xe6, 0x8b, 0x51, 0x6c, - 0x2c, 0xac, 0xbd, 0x2f, 0x4c, 0x31, 0x08, 0xa0, 0x4d, 0x90, 0x06, 0x7c, - 0x80, 0x00, 0xc8, 0x2a, 0x09, 0x30, 0x07, 0x9b, 0xce, 0x40, 0x4c, 0x90, - 0x3c, 0x3d, 0x04, 0xce, 0x3c, 0x76, 0xad, 0x65, 0x93, 0x64, 0xfd, 0x31, - 0x54, 0x34, 0x1b, 0x1c, 0xb5, 0x35, 0x12, 0xd4, 0xd4, 0xed, 0x8b, 0xac, - 0x10, 0x0c, 0x60, 0xee, 0x83, 0x58, 0x52, 0x02, 0x8c, 0xc2, 0xd4, 0x50, - 0x86, 0x48, 0xe6, 0x99, 0xd3, 0x32, 0x1d, 0xe6, 0x24, 0xd3, 0xad, 0xd2, - 0x89, 0x94, 0xb7, 0x61, 0x3f, 0x27, 0xa1, 0xac, 0x37, 0x61, 0xaa, 0xba, - 0x20, 0x23, 0xca, 0xd5, 0x0e, 0xb1, 0x17, 0xb6, 0x96, 0x14, 0xa2, 0xaa, - 0x4a, 0xff, 0x75, 0xf1, 0x7d, 0x73, 0xac, 0x0a, 0xab, 0x6d, 0x93, 0xb7, - 0x31, 0x6c, 0x32, 0x7f, 0x81, 0xad, 0x80, 0x00, 0x00, 0xc5, 0x26, 0xed, - 0x75, 0x6c, 0x99, 0x60, 0x1c, 0x9b, 0xa9, 0x62, 0x37, 0xf7, 0x76, 0xce, - 0x10, 0x80, 0xe9, 0x6a, 0xe2, 0x0c, 0xb6, 0xa9, 0x39, 0x40, 0xc4, 0xe7, - 0x6e, 0xa1, 0xd7, 0x7d, 0x00, 0xa4, 0x24, 0xe4, 0xae, 0x98, 0x20, 0x29, - 0xe4, 0x31, 0xf1, 0x65, 0xca, 0x79, 0xc1, 0x91, 0x4d, 0xde, 0x8a, 0x0a, - 0xaf, 0xfc, 0xe9, 0x33, 0x18, 0x55, 0x80, 0xa1, 0x5e, 0x72, 0xb4, 0x2d, - 0xa8, 0x21, 0x5d, 0xa1, 0x28, 0xa2, 0xcc, 0x61, 0x11, 0x2a, 0xd8, 0x89, - 0x69, 0xca, 0x05, 0x55, 0x65, 0xbf, 0xb5, 0xff, 0xd1, 0xfd, 0xff, 0x6d, - 0xdb, 0x0b, 0x10, 0x80, 0xb9, 0x90, 0xc9, 0x2b, 0xd3, 0xfa, 0xc6, 0xc0, - 0x81, 0xc0, 0xff, 0xf1, 0x50, 0x80, 0x2f, 0x1f, 0xfc, 0x21, 0x1a, 0xcd, - 0xe9, 0xfe, 0x7f, 0x84, 0x40, 0x95, 0xb6, 0xc1, 0xac, 0x28, 0x39, 0x62, - 0x0d, 0x89, 0x01, 0x00, 0x00, 0xbc, 0x00, 0x17, 0x81, 0xad, 0xfc, 0x6a, - 0xd3, 0xe8, 0x28, 0x4f, 0x80, 0xe0, 0x4f, 0x7c, 0x9f, 0x2b, 0x96, 0xd5, - 0x0f, 0x9d, 0xe4, 0x98, 0xfc, 0xe4, 0x3c, 0x75, 0x3b, 0xf6, 0x4a, 0x31, - 0xf2, 0x9d, 0x51, 0x54, 0x5a, 0xfd, 0x61, 0x45, 0x36, 0xba, 0x58, 0x89, - 0x93, 0x28, 0x89, 0xea, 0xf2, 0x96, 0x2b, 0x29, 0x81, 0x40, 0xdc, 0xc6, - 0x47, 0x08, 0xd5, 0xb0, 0x24, 0x90, 0x2c, 0x37, 0xaa, 0xc6, 0xb1, 0x10, - 0xab, 0x43, 0x28, 0x32, 0xb9, 0x13, 0xc0, 0xd6, 0xd9, 0x98, 0x69, 0x8d, - 0x36, 0xdf, 0x7c, 0xc2, 0x83, 0x12, 0x18, 0x3d, 0x69, 0xd0, 0xfa, 0xa7, - 0xbf, 0xed, 0x5d, 0x61, 0x81, 0x2b, 0xc9, 0x45, 0xe8, 0x5d, 0xe7, 0x2c, - 0xf1, 0x2b, 0xba, 0xfe, 0xc5, 0xd2, 0x29, 0x33, 0x2a, 0x30, 0x04, 0x48, - 0x2c, 0x65, 0x5c, 0x3b, 0x48, 0xb3, 0x06, 0x20, 0xd7, 0x95, 0x9a, 0x13, - 0x8a, 0xde, 0xab, 0xf0, 0xf2, 0x85, 0x62, 0x26, 0xf0, 0x09, 0x84, 0x8c, - 0x26, 0xee, 0xeb, 0xba, 0xce, 0x62, 0xa0, 0x5e, 0x72, 0x42, 0x60, 0x2e, - 0x45, 0xb8, 0x00, 0x10, 0xc5, 0x01, 0x0c, 0x5b, 0x85, 0x77, 0x18, 0xef, - 0xc0, 0x21, 0x75, 0xbc, 0xdf, 0xd5, 0x73, 0xf1, 0x8a, 0xe3, 0x49, 0x46, - 0x2e, 0xa2, 0x60, 0x85, 0x50, 0xba, 0x06, 0x93, 0xb6, 0xd4, 0x40, 0x94, - 0xca, 0x2f, 0x65, 0x17, 0xdb, 0x25, 0x12, 0x95, 0xde, 0xb6, 0x0b, 0xc2, - 0xd8, 0x84, 0x88, 0xb0, 0x41, 0x67, 0x45, 0x1c, 0xcf, 0x85, 0x60, 0x63, - 0x7f, 0x41, 0x8b, 0xb5, 0x8d, 0xc9, 0x4a, 0x65, 0x2d, 0xfb, 0xf2, 0xe3, - 0xc4, 0x8e, 0x4a, 0x08, 0xef, 0xca, 0xdb, 0xd8, 0x96, 0x14, 0x1c, 0x97, - 0xde, 0x00, 0x01, 0x78, 0x00, 0x65, 0x55, 0xee, 0x71, 0x8a, 0x89, 0xcf, - 0x00, 0xc9, 0x23, 0x0b, 0xd8, 0xe6, 0x4f, 0x2f, 0xb9, 0xe9, 0x12, 0xf0, - 0x38, 0xb9, 0x9a, 0x90, 0xad, 0xe3, 0x66, 0xfb, 0x60, 0xf8, 0x98, 0xc1, - 0xd5, 0xce, 0xa8, 0x8e, 0xf1, 0x68, 0xa0, 0x84, 0x4c, 0xbe, 0x3c, 0x85, - 0x1a, 0x00, 0x0c, 0xeb, 0x05, 0xa6, 0xf4, 0xb2, 0xf0, 0x98, 0xc0, 0x42, - 0xd6, 0x1d, 0xfe, 0x5c, 0x58, 0xd0, 0x6d, 0xa3, 0x37, 0x13, 0x94, 0x39, - 0x0d, 0x78, 0xc5, 0xf6, 0xc2, 0x59, 0xe2, 0xee, 0xf4, 0x1b, 0x10, 0x5a, - 0xb2, 0x3b, 0xa7, 0xf8, 0x4f, 0xcc, 0xf6, 0xc6, 0x51, 0x96, 0x02, 0x75, - 0xf6, 0xc8, 0xbc, 0xa2, 0xa2, 0x2a, 0xc8, 0xd5, 0xe3, 0x7d, 0xbf, 0x1c, - 0xc4, 0x2c, 0x54, 0x05, 0x03, 0x80, 0xff, 0xf1, 0x50, 0x80, 0x2d, 0xbf, - 0xfc, 0x21, 0x1a, 0xce, 0xff, 0xf6, 0xf7, 0x84, 0x50, 0x96, 0xb6, 0xc1, - 0xac, 0x2a, 0x3b, 0x0c, 0xa4, 0xcc, 0xc4, 0x32, 0xc0, 0x06, 0x5b, 0x2c, - 0x65, 0x8c, 0xd6, 0xc9, 0x47, 0xc3, 0xab, 0x7b, 0x05, 0x09, 0xd0, 0x5f, - 0xcd, 0xc8, 0xce, 0x39, 0xf4, 0x93, 0xde, 0x85, 0xbd, 0xaf, 0xc9, 0xe0, - 0x88, 0x19, 0x44, 0x62, 0x13, 0x85, 0x31, 0x5f, 0x05, 0x25, 0xd3, 0xce, - 0x28, 0x2d, 0xc6, 0x6f, 0x96, 0xc5, 0x98, 0x52, 0x3b, 0x99, 0xd6, 0x3d, - 0xde, 0xf9, 0x84, 0x1e, 0x9e, 0x32, 0xec, 0x57, 0x82, 0x44, 0x91, 0x00, - 0x46, 0xf1, 0x4a, 0x31, 0x86, 0x80, 0xc6, 0x17, 0xc0, 0x7e, 0x47, 0x24, - 0x30, 0xb1, 0x00, 0xc6, 0x79, 0xae, 0xf7, 0x17, 0xeb, 0x92, 0xea, 0x20, - 0xaa, 0x68, 0x06, 0x42, 0xa4, 0xc2, 0xbe, 0x97, 0x88, 0x05, 0x0f, 0x9c, - 0x4c, 0xcc, 0x70, 0x28, 0x65, 0xa1, 0x15, 0x35, 0x79, 0xe5, 0x55, 0x5d, - 0x87, 0xec, 0x3b, 0xe0, 0xbb, 0xa5, 0xc6, 0x5b, 0xac, 0x30, 0x08, 0x90, - 0xa4, 0x91, 0x2b, 0x89, 0x9b, 0x50, 0x5d, 0x52, 0x6e, 0xb2, 0xb4, 0x2b, - 0xe5, 0x9c, 0xd9, 0x22, 0x4b, 0x23, 0x60, 0x54, 0x00, 0x27, 0x3e, 0x14, - 0xb0, 0x10, 0x34, 0xea, 0xad, 0xe7, 0x3d, 0x47, 0x9c, 0xc9, 0x05, 0xba, - 0x8a, 0x06, 0x82, 0x1f, 0x9d, 0xc6, 0x6a, 0xd9, 0x3d, 0x6c, 0xfc, 0x7d, - 0x33, 0xb9, 0xd6, 0x74, 0x0d, 0x4f, 0xea, 0xd3, 0x29, 0x45, 0x53, 0x74, - 0x17, 0x94, 0x66, 0x73, 0x95, 0xd2, 0xb2, 0xd0, 0xa3, 0x60, 0x5f, 0x4c, - 0x2d, 0xe8, 0xce, 0x47, 0x2a, 0xa4, 0x1c, 0xea, 0xa6, 0x6f, 0x78, 0x53, - 0x5d, 0x4c, 0xf6, 0x17, 0xa4, 0x32, 0xef, 0x17, 0xd3, 0x33, 0x4d, 0x2e, - 0xa9, 0x6b, 0x23, 0x22, 0xc3, 0x03, 0x61, 0xd8, 0x50, 0x74, 0x12, 0x13, - 0x8b, 0xde, 0x00, 0x00, 0x00, 0x0a, 0x99, 0xcf, 0x1c, 0x73, 0x50, 0x99, - 0x60, 0x66, 0xf3, 0x5b, 0x46, 0x3b, 0x9f, 0x60, 0x8d, 0xc8, 0x7b, 0x9d, - 0xae, 0x01, 0xaa, 0x81, 0xd3, 0x55, 0xcc, 0x5f, 0xaa, 0xfd, 0x09, 0x66, - 0x85, 0x3e, 0xd2, 0x26, 0xfd, 0x86, 0x41, 0x18, 0xcc, 0xa8, 0x68, 0x5e, - 0x77, 0x29, 0x8a, 0x41, 0xe8, 0xd5, 0x00, 0x5d, 0x22, 0x8c, 0x1a, 0x46, - 0x01, 0x68, 0xf9, 0xee, 0xaf, 0x47, 0x34, 0x42, 0xcb, 0xe4, 0x82, 0xf7, - 0xda, 0x9d, 0x52, 0xb3, 0xf2, 0x7b, 0xa0, 0x11, 0xc1, 0x02, 0x07, 0x49, - 0x5e, 0x88, 0x23, 0x84, 0x1f, 0xeb, 0x23, 0xbc, 0x82, 0x76, 0xee, 0xc1, - 0xc3, 0x35, 0x3f, 0xe9, 0x21, 0x33, 0x11, 0x44, 0x01, 0x03, 0x80, 0xff, - 0xf1, 0x50, 0x80, 0x2f, 0x3f, 0xfc, 0x21, 0x1a, 0xcd, 0xfe, 0x7f, 0x7f, - 0xb8, 0x10, 0x97, 0xb6, 0xc1, 0xac, 0x33, 0x14, 0x13, 0x10, 0x0e, 0xbb, - 0x00, 0x94, 0x00, 0x09, 0x41, 0xf1, 0x38, 0xb9, 0xe4, 0x20, 0xf2, 0xac, - 0xf9, 0xe0, 0x54, 0x87, 0xf1, 0x37, 0xd7, 0x0e, 0x7e, 0xb8, 0x84, 0x6b, - 0x42, 0x22, 0xd4, 0x6a, 0x81, 0x5d, 0x55, 0xc2, 0xb8, 0x75, 0x13, 0xed, - 0xe2, 0xb9, 0x0b, 0x9d, 0xf7, 0x77, 0x90, 0x96, 0x7f, 0x6c, 0x41, 0x64, - 0x2c, 0x62, 0x23, 0x34, 0xa4, 0x02, 0x04, 0xb4, 0x51, 0x14, 0x21, 0x9d, - 0xa4, 0x46, 0xa4, 0x96, 0x17, 0x38, 0xc9, 0xea, 0xe5, 0x30, 0x48, 0x83, - 0x8a, 0xbc, 0x5c, 0x34, 0xeb, 0xba, 0xcc, 0x5f, 0x10, 0x89, 0x54, 0xc4, - 0x66, 0xa0, 0xdb, 0xfd, 0x77, 0x07, 0x39, 0x2f, 0x0a, 0xcd, 0x5a, 0xda, - 0x21, 0x58, 0xe6, 0xca, 0x23, 0x2a, 0xb6, 0x7d, 0x9f, 0xd7, 0xb2, 0xb1, - 0x8b, 0x21, 0xa5, 0x80, 0xb9, 0x80, 0x05, 0xf2, 0xe1, 0x60, 0x92, 0xb1, - 0x06, 0x36, 0x60, 0xda, 0x86, 0x7c, 0x7f, 0xa5, 0xd0, 0xb1, 0x13, 0x30, - 0x69, 0xc4, 0x04, 0x28, 0xb0, 0xcf, 0x97, 0x94, 0x48, 0xb0, 0xba, 0xac, - 0x59, 0x32, 0x05, 0xa1, 0x6d, 0x7d, 0x18, 0x95, 0xa4, 0x18, 0x58, 0x04, - 0x40, 0x67, 0x7c, 0x2a, 0xd7, 0x00, 0x99, 0xe2, 0x23, 0x02, 0xb0, 0x17, - 0x17, 0x3b, 0x0a, 0xe5, 0xc4, 0xb0, 0xac, 0xe1, 0xb6, 0x68, 0x31, 0x51, - 0x48, 0xb9, 0x60, 0x42, 0x99, 0x77, 0x35, 0xd9, 0x7d, 0xe2, 0x9a, 0xac, - 0x8a, 0xca, 0x42, 0x45, 0xc6, 0x53, 0x80, 0xf5, 0xe7, 0x71, 0xd2, 0xf6, - 0x52, 0x37, 0x4b, 0x8c, 0xfc, 0xc6, 0xce, 0xaf, 0x26, 0xce, 0xca, 0xfc, - 0xbf, 0x9b, 0x2a, 0xa6, 0xba, 0x69, 0x2b, 0x25, 0xed, 0xb0, 0x6b, 0x0c, - 0xa3, 0x8a, 0x41, 0xf4, 0x81, 0xd7, 0x60, 0x12, 0x80, 0x0c, 0xcb, 0xbe, - 0x7c, 0xf1, 0x58, 0xb4, 0xde, 0x81, 0x8f, 0x5d, 0x49, 0xd1, 0x42, 0x36, - 0x9b, 0x0e, 0xc6, 0xa1, 0x80, 0x41, 0xd1, 0x33, 0x11, 0x91, 0xc4, 0x6b, - 0xd4, 0x19, 0xc0, 0x5f, 0xce, 0x23, 0x9a, 0xe5, 0xc6, 0xdc, 0xd9, 0x04, - 0x2a, 0x58, 0xac, 0xa0, 0xe8, 0x94, 0x05, 0x4b, 0x88, 0x4a, 0x03, 0x3f, - 0x88, 0x23, 0x59, 0xd5, 0xa4, 0x51, 0x8d, 0x3e, 0x28, 0x82, 0x24, 0x1c, - 0x39, 0xdc, 0x32, 0xca, 0xda, 0x47, 0x18, 0x46, 0x3b, 0xf9, 0x59, 0xe1, - 0x5b, 0x65, 0x31, 0x78, 0xef, 0xac, 0xad, 0x94, 0x0a, 0xfe, 0xc5, 0xeb, - 0xbb, 0xd5, 0x1a, 0xb9, 0x50, 0x9e, 0x66, 0x22, 0xe2, 0x16, 0xa9, 0x43, - 0x0f, 0x11, 0xd1, 0xec, 0x85, 0xdd, 0x48, 0x6f, 0x05, 0x83, 0x0b, 0x18, - 0xa8, 0x66, 0x1a, 0xf8, 0xff, 0xf1, 0x50, 0x80, 0x30, 0xdf, 0xfc, 0x21, - 0x1a, 0xcd, 0xf9, 0xff, 0x5f, 0xbf, 0xff, 0x94, 0xb6, 0xc1, 0xac, 0x2c, - 0x19, 0x52, 0x9d, 0x07, 0x02, 0x00, 0x00, 0x00, 0x00, 0x4a, 0x35, 0xd3, - 0xab, 0xf8, 0x0a, 0x1f, 0x05, 0x42, 0x02, 0xe1, 0xeb, 0x0c, 0x9d, 0x07, - 0x9d, 0x2a, 0x30, 0xbd, 0x4f, 0x28, 0x62, 0x0b, 0xd4, 0xe3, 0xac, 0x2c, - 0xf5, 0x94, 0x79, 0xd2, 0xc2, 0x0c, 0x35, 0x53, 0x8c, 0xe6, 0x11, 0x31, - 0x4c, 0x34, 0x42, 0xc8, 0xea, 0xf7, 0xe4, 0x5c, 0x7f, 0x40, 0x08, 0xd0, - 0xfa, 0xd1, 0x88, 0xa4, 0x9b, 0xee, 0xd3, 0x11, 0xc5, 0x84, 0x92, 0x14, - 0x63, 0x61, 0x1a, 0x02, 0x38, 0x7a, 0xff, 0x9d, 0xef, 0x71, 0xc5, 0x14, - 0x56, 0xe8, 0x4f, 0x35, 0x38, 0x6c, 0x06, 0x37, 0x18, 0xc5, 0x4c, 0xcd, - 0x6c, 0xe2, 0x7f, 0xee, 0xe2, 0x85, 0xdd, 0x0c, 0xf8, 0x70, 0x2b, 0x8b, - 0x6c, 0x4c, 0xf1, 0xb3, 0x1f, 0x29, 0xf6, 0x6d, 0x69, 0xbb, 0xc4, 0x96, - 0x7b, 0xf4, 0x61, 0x4c, 0x65, 0x45, 0xd8, 0xbe, 0xaf, 0x91, 0x09, 0x4c, - 0x08, 0xa9, 0x58, 0xd8, 0x88, 0x8a, 0xd5, 0x84, 0xe1, 0x87, 0xe5, 0x9d, - 0xfe, 0x58, 0x52, 0x26, 0x95, 0x3d, 0xf1, 0x22, 0x40, 0x15, 0xbf, 0x8f, - 0x85, 0x51, 0x60, 0xbc, 0x58, 0x02, 0x67, 0x18, 0x6b, 0xa3, 0xfc, 0xd4, - 0xaa, 0xaa, 0x15, 0x8b, 0xc6, 0x6e, 0xaa, 0xab, 0x37, 0x62, 0xa9, 0xc6, - 0xbe, 0x3e, 0x1b, 0xdc, 0x26, 0x86, 0x74, 0x25, 0xb5, 0xe0, 0xdd, 0xb0, - 0x27, 0x7b, 0x12, 0x14, 0xe4, 0xa6, 0xae, 0xd6, 0xb1, 0x32, 0xd2, 0xd0, - 0x67, 0x7b, 0xd0, 0x80, 0xc5, 0x8e, 0xdf, 0xd9, 0x35, 0x6f, 0x11, 0x26, - 0x04, 0x61, 0x25, 0xf3, 0x35, 0x00, 0x94, 0x33, 0xbd, 0x19, 0x74, 0x94, - 0x09, 0x12, 0xeb, 0x45, 0x2a, 0x48, 0x27, 0x39, 0x1e, 0x2c, 0x61, 0x4a, - 0x53, 0x2c, 0x30, 0x6b, 0x0b, 0x0e, 0xc3, 0x24, 0xe7, 0xaa, 0x02, 0x90, - 0x02, 0xa5, 0x40, 0x65, 0x49, 0xbd, 0x62, 0xae, 0xea, 0x55, 0x88, 0x8d, - 0x7e, 0xa3, 0x48, 0x77, 0xe6, 0xa1, 0xdc, 0xa3, 0x45, 0x3e, 0x68, 0xc5, - 0x2e, 0x26, 0x0f, 0x42, 0xc8, 0x4a, 0x00, 0x80, 0xaa, 0xa9, 0x8d, 0x2b, - 0x7d, 0x89, 0xcb, 0x65, 0x27, 0x9e, 0x65, 0x08, 0x35, 0x26, 0x91, 0x7a, - 0x25, 0x16, 0x68, 0x4f, 0x6d, 0xa0, 0x09, 0xc2, 0x97, 0x5d, 0x17, 0xc8, - 0x86, 0x04, 0xdc, 0x75, 0xc1, 0x61, 0x3f, 0xba, 0x80, 0x3a, 0xa1, 0x66, - 0x94, 0x53, 0x16, 0xb2, 0x9f, 0x5f, 0x70, 0xb1, 0x1d, 0xb2, 0xd6, 0x6e, - 0xc6, 0x40, 0x00, 0x21, 0xcf, 0x49, 0x29, 0x10, 0xc5, 0x32, 0x35, 0x17, - 0x59, 0x72, 0xe1, 0x28, 0xd9, 0x94, 0xe4, 0xac, 0x68, 0x65, 0xfd, 0x77, - 0x67, 0x9e, 0x34, 0xc2, 0x72, 0xcc, 0xc6, 0x43, 0x0b, 0x07, 0xff, 0xf1, - 0x50, 0x80, 0x31, 0x5f, 0xfc, 0x21, 0x1a, 0xcf, 0xde, 0x32, 0x2b, 0xb8, - 0xff, 0x93, 0xb6, 0xb1, 0xac, 0x2e, 0xf2, 0x13, 0x18, 0x00, 0x02, 0xf0, - 0x00, 0x05, 0xe1, 0xf1, 0xe7, 0x3e, 0x2b, 0x40, 0xcd, 0x8f, 0x76, 0x83, - 0x20, 0x9e, 0xc1, 0x26, 0x48, 0x12, 0xa8, 0xeb, 0x00, 0x62, 0xf4, 0xf2, - 0x07, 0x79, 0x4f, 0x13, 0x0e, 0xaa, 0x85, 0xd3, 0xd0, 0x06, 0xa5, 0x53, - 0xab, 0x8f, 0x21, 0x6e, 0x78, 0x11, 0xb4, 0xd9, 0x30, 0x42, 0x25, 0x8a, - 0x8f, 0x45, 0x3e, 0x2b, 0x6f, 0x37, 0xf9, 0xb6, 0x1e, 0xba, 0xe5, 0xcd, - 0x85, 0x1b, 0xbd, 0x80, 0x11, 0x77, 0x49, 0xac, 0x41, 0xea, 0x23, 0xc9, - 0x03, 0x81, 0xa8, 0xef, 0x46, 0xb5, 0x3a, 0x71, 0x9d, 0x8a, 0x86, 0x22, - 0xb3, 0x99, 0x9c, 0x59, 0x7e, 0x7e, 0xdf, 0x47, 0x39, 0x27, 0x5b, 0x1b, - 0x89, 0xdf, 0xd5, 0x31, 0xc1, 0xba, 0xa6, 0x49, 0xaa, 0x99, 0xae, 0xeb, - 0xc7, 0xd1, 0xc4, 0x45, 0x4c, 0x53, 0x65, 0x0a, 0xc8, 0x01, 0x84, 0x7a, - 0x99, 0x81, 0x8c, 0xdd, 0x90, 0x0b, 0x19, 0x4b, 0x24, 0x27, 0xe1, 0xf0, - 0x26, 0x59, 0x48, 0x30, 0xc8, 0x4d, 0x80, 0x2b, 0x53, 0xb8, 0xe1, 0x05, - 0x01, 0xa3, 0x8c, 0x53, 0x00, 0x0a, 0xbb, 0xcf, 0xae, 0xc8, 0x2a, 0x8a, - 0xa8, 0x56, 0x2c, 0x0f, 0xcd, 0x63, 0xfd, 0x08, 0x5f, 0xf4, 0x1d, 0x01, - 0x8d, 0x86, 0x01, 0x41, 0x94, 0xca, 0x47, 0x13, 0x2a, 0x71, 0x32, 0xa6, - 0x62, 0x49, 0xed, 0x0b, 0xc9, 0x99, 0x1a, 0x77, 0x12, 0x80, 0x70, 0x37, - 0x4d, 0x4e, 0x75, 0xdb, 0xef, 0xcf, 0x40, 0xd1, 0x75, 0xd3, 0x62, 0x58, - 0xc9, 0x1a, 0x17, 0xbd, 0x60, 0x30, 0xda, 0x9b, 0x30, 0x9f, 0x53, 0xe7, - 0x67, 0x2f, 0xff, 0xa5, 0xf0, 0xb7, 0x2e, 0xde, 0xa9, 0xc0, 0xeb, 0xed, - 0xa4, 0xa6, 0x93, 0xa5, 0xda, 0x50, 0x6e, 0x5e, 0x20, 0x87, 0x94, 0x0a, - 0x82, 0x88, 0xa4, 0xa2, 0x54, 0xa8, 0x78, 0xea, 0xb5, 0xc6, 0xea, 0xf2, - 0xd2, 0xab, 0x42, 0x26, 0x7a, 0x45, 0xad, 0x76, 0xb9, 0x7b, 0xe7, 0x29, - 0x93, 0x11, 0x6f, 0x25, 0xda, 0x69, 0x69, 0x97, 0xbf, 0xc2, 0x42, 0x85, - 0x21, 0xc8, 0x98, 0x23, 0x6a, 0xe8, 0x0f, 0xcd, 0xd4, 0x49, 0x36, 0x49, - 0xf0, 0x8c, 0xb4, 0xaf, 0x11, 0x72, 0xf1, 0x4e, 0xa1, 0x2d, 0x32, 0xb3, - 0xd0, 0xb7, 0xd3, 0x88, 0x64, 0x07, 0xdc, 0x8b, 0xd0, 0x98, 0x79, 0x79, - 0xf2, 0xcd, 0x28, 0xd0, 0xd0, 0x25, 0x43, 0xcc, 0xa5, 0x7c, 0x88, 0x31, - 0xbf, 0xf0, 0x76, 0xad, 0x13, 0xc9, 0xd5, 0x3c, 0xe0, 0xcf, 0x14, 0x18, - 0xe5, 0xc2, 0xf8, 0xbc, 0x3b, 0x56, 0x75, 0x73, 0x58, 0xeb, 0xec, 0x0c, - 0xb1, 0x99, 0xc4, 0x9b, 0x5c, 0xdf, 0xa0, 0xfc, 0xec, 0x85, 0x06, 0x79, - 0x48, 0x00, 0xb4, 0xb0, 0x6e, 0x81, 0xdb, 0x80, 0xff, 0xf1, 0x50, 0x80, - 0x30, 0x3f, 0xfc, 0x21, 0x1a, 0xcf, 0xb5, 0x71, 0xbf, 0x00, 0x00, 0x91, - 0xba, 0x4b, 0x4c, 0xac, 0x48, 0x08, 0x00, 0x00, 0x00, 0x03, 0xae, 0xcf, - 0x8f, 0x39, 0xc5, 0xf4, 0x21, 0x76, 0xfa, 0xc8, 0x95, 0x59, 0xd4, 0x9e, - 0xd6, 0x45, 0x51, 0xe6, 0x74, 0x92, 0x10, 0x34, 0x86, 0xb1, 0x53, 0xb8, - 0x10, 0x20, 0xdf, 0xbe, 0x29, 0x46, 0xdc, 0x94, 0x63, 0xe9, 0xfa, 0x8a, - 0x94, 0xbd, 0x94, 0xcd, 0xeb, 0xf9, 0xc6, 0x7f, 0x05, 0x73, 0x98, 0x25, - 0xa7, 0xe0, 0x33, 0xf0, 0xda, 0x81, 0x39, 0x45, 0xbc, 0x2d, 0x35, 0x1a, - 0xac, 0xff, 0x18, 0xd2, 0xcc, 0x6f, 0x8a, 0x98, 0x62, 0x94, 0xc7, 0x54, - 0x34, 0xc1, 0x46, 0xbe, 0xae, 0xe0, 0xfc, 0x6a, 0xc9, 0x63, 0xa2, 0xc7, - 0x5e, 0x71, 0xa8, 0xe2, 0xea, 0xad, 0x09, 0xc7, 0x0b, 0x84, 0x97, 0x8f, - 0xb4, 0xfb, 0x67, 0x4d, 0x9d, 0xa3, 0x2c, 0x8b, 0xc6, 0xb1, 0x4d, 0x66, - 0x95, 0xb1, 0x8b, 0xb8, 0x74, 0x7d, 0xdf, 0x4c, 0x84, 0xd9, 0x17, 0xb2, - 0x20, 0xa5, 0x00, 0xc2, 0xba, 0xad, 0xcc, 0xe2, 0x6e, 0x40, 0x64, 0x6e, - 0x02, 0x2a, 0x13, 0xab, 0xeb, 0xda, 0x7c, 0xca, 0x61, 0x90, 0x65, 0xb4, - 0x00, 0x08, 0xae, 0x76, 0xf4, 0xe0, 0x5d, 0x83, 0x21, 0x15, 0xf7, 0x7d, - 0x07, 0xe8, 0x80, 0x42, 0xa4, 0x32, 0xef, 0x2d, 0xef, 0xa3, 0x4e, 0xc3, - 0xa4, 0x52, 0x30, 0x73, 0xb4, 0x23, 0xa5, 0xca, 0xb7, 0xa1, 0xd6, 0xa2, - 0x65, 0x9a, 0xdb, 0x96, 0x66, 0x33, 0x07, 0x7a, 0xab, 0x14, 0x6a, 0x1c, - 0x21, 0x12, 0x65, 0xb7, 0x33, 0x31, 0x63, 0x98, 0xa9, 0x1f, 0x75, 0xcd, - 0x17, 0xbc, 0xb5, 0xda, 0xda, 0xfc, 0x36, 0xbd, 0x50, 0x63, 0x39, 0x85, - 0xb0, 0x12, 0x63, 0x4b, 0x0d, 0x69, 0x97, 0x64, 0x94, 0xb5, 0x63, 0x2a, - 0x3d, 0x4a, 0x52, 0x2a, 0xa4, 0x6e, 0xa8, 0x27, 0x27, 0xc0, 0x00, 0x00, - 0x00, 0x37, 0x37, 0x33, 0xa2, 0x97, 0x59, 0x20, 0x36, 0x4b, 0xef, 0xc8, - 0x33, 0x17, 0x77, 0x43, 0x38, 0x67, 0xf6, 0x26, 0xd1, 0x18, 0x4d, 0x2a, - 0xe7, 0xea, 0x03, 0x1f, 0x71, 0x0d, 0xf6, 0xb4, 0x54, 0x77, 0x7e, 0x73, - 0x35, 0xf5, 0x5f, 0x01, 0xc3, 0x6a, 0x35, 0x91, 0x99, 0x8b, 0xb3, 0x52, - 0x71, 0x7a, 0x80, 0x8a, 0x87, 0x8e, 0x36, 0x58, 0x9c, 0xbc, 0xb8, 0x69, - 0xd1, 0x93, 0x2f, 0xcc, 0x6d, 0xf4, 0x02, 0xbc, 0x3b, 0x97, 0x33, 0x01, - 0x4c, 0x69, 0x22, 0x42, 0xec, 0xc2, 0xcc, 0x64, 0x8c, 0xe1, 0x62, 0x3f, - 0xc7, 0x0d, 0x44, 0x52, 0xba, 0x3c, 0x54, 0xd0, 0x06, 0x37, 0xee, 0x7d, - 0x6f, 0x44, 0xad, 0x7b, 0x8a, 0xc7, 0x08, 0xe2, 0x6d, 0x2d, 0xab, 0x38, - 0x13, 0x75, 0x43, 0xc5, 0xc3, 0xde, 0xd9, 0x88, 0x38, 0xff, 0xf1, 0x50, - 0x80, 0x2c, 0xdf, 0xfc, 0x21, 0x1a, 0xc8, 0xff, 0x5f, 0xee, 0x00, 0x00, - 0x94, 0xb7, 0x30, 0xa5, 0x06, 0xd6, 0x30, 0xb0, 0x19, 0x79, 0x60, 0x32, - 0xcc, 0xbc, 0xbc, 0xf6, 0xf5, 0xf8, 0xfb, 0x2b, 0xf7, 0xe1, 0x77, 0x06, - 0x36, 0x53, 0x56, 0x5d, 0xf3, 0x6d, 0x95, 0x6e, 0x3b, 0xd0, 0xa7, 0x53, - 0xee, 0xbe, 0x40, 0x82, 0x8d, 0x4e, 0xad, 0x8b, 0x62, 0x2c, 0x39, 0xc2, - 0x69, 0x79, 0xac, 0x89, 0x91, 0x1f, 0x65, 0x8b, 0xcf, 0x65, 0x6b, 0x52, - 0x21, 0xb8, 0x46, 0x5d, 0x9a, 0xc4, 0x57, 0x81, 0x80, 0x7e, 0x9e, 0xd7, - 0x15, 0xdf, 0x2f, 0xa7, 0x03, 0x3f, 0x21, 0x92, 0x9a, 0xfa, 0x30, 0xcf, - 0x76, 0xa7, 0xf8, 0x86, 0x31, 0x83, 0x1d, 0x5c, 0x32, 0x38, 0xdc, 0x4c, - 0x22, 0x16, 0x19, 0x60, 0x4e, 0x1b, 0xbb, 0xfd, 0x2a, 0xa8, 0x5e, 0xf8, - 0x29, 0x09, 0xf4, 0x73, 0x4b, 0x47, 0x87, 0xe2, 0x3f, 0x3b, 0x1d, 0x44, - 0xed, 0xac, 0xd2, 0x04, 0x73, 0xd4, 0x88, 0x45, 0xbb, 0xe4, 0x3f, 0x3d, - 0xf1, 0x0f, 0x10, 0x6f, 0xb1, 0xf1, 0xde, 0x92, 0xe2, 0xc4, 0xf3, 0xae, - 0x47, 0x20, 0x37, 0x1c, 0xec, 0x93, 0xd2, 0x68, 0x7c, 0xee, 0xae, 0xc0, - 0x00, 0x3b, 0x1c, 0x9c, 0x50, 0x26, 0x22, 0x1a, 0x19, 0x22, 0xd0, 0x66, - 0x51, 0x69, 0x8d, 0x37, 0xc4, 0xf6, 0x9e, 0x63, 0x10, 0xf6, 0x80, 0xd4, - 0x0c, 0x24, 0x62, 0xc9, 0x0f, 0x61, 0x65, 0x5d, 0xce, 0xd7, 0x64, 0x43, - 0x75, 0x69, 0xac, 0xa7, 0xc8, 0x94, 0x59, 0x8d, 0xdd, 0x69, 0x8e, 0xda, - 0xe9, 0xdb, 0xa3, 0x31, 0x49, 0xa1, 0xbb, 0xec, 0x82, 0xe1, 0x44, 0xb6, - 0x58, 0x0f, 0x2d, 0xe1, 0x12, 0x56, 0xd6, 0x35, 0x85, 0x06, 0xe4, 0xf2, - 0x88, 0x7d, 0x40, 0x00, 0x00, 0x02, 0x56, 0x66, 0xae, 0x52, 0x99, 0x75, - 0x20, 0x92, 0xe7, 0x75, 0xe4, 0x12, 0xfc, 0x17, 0xc7, 0x10, 0x15, 0x07, - 0xee, 0xdb, 0x64, 0x20, 0x2d, 0xa8, 0xc4, 0x4a, 0x6f, 0x0c, 0xdc, 0x48, - 0x1f, 0x0b, 0xcd, 0xd3, 0xce, 0x23, 0xa3, 0x6b, 0x92, 0x9e, 0x47, 0x50, - 0x00, 0x86, 0x96, 0x97, 0xa2, 0x76, 0x78, 0xf6, 0x6e, 0xb6, 0x60, 0xb9, - 0xf2, 0xe7, 0x40, 0x00, 0x1d, 0xd5, 0xb3, 0x1e, 0xd9, 0x86, 0x11, 0xba, - 0xff, 0x07, 0x9b, 0x59, 0x96, 0x51, 0xbd, 0xa6, 0xca, 0x6a, 0x2c, 0x67, - 0x79, 0xac, 0x95, 0xba, 0x7c, 0x5f, 0xbf, 0xe7, 0x55, 0xb5, 0x61, 0x55, - 0x96, 0x85, 0x41, 0x48, 0x09, 0xc8, 0xcf, 0x47, 0xf1, 0x7f, 0x33, 0x01, - 0x39, 0xa2, 0xef, 0x96, 0xb2, 0x12, 0xe0, 0xff, 0xf1, 0x50, 0x80, 0x2f, - 0x5f, 0xfc, 0x21, 0x1a, 0xce, 0xfa, 0xdf, 0xf2, 0x84, 0x00, 0x93, 0xb6, - 0xc0, 0xd0, 0x76, 0x19, 0x69, 0x91, 0x8c, 0x00, 0x00, 0x00, 0x0f, 0x3e, - 0x35, 0xbb, 0xc3, 0xf1, 0x76, 0x9e, 0x46, 0x36, 0xa1, 0x36, 0x52, 0x24, - 0x02, 0xbb, 0x2e, 0xe2, 0xcb, 0x85, 0xca, 0x81, 0xaf, 0x9d, 0xe9, 0x62, - 0x43, 0x30, 0xc2, 0x37, 0x7a, 0x28, 0xf2, 0x49, 0x7d, 0x33, 0x07, 0xa1, - 0xd3, 0x2c, 0x64, 0x8d, 0x4d, 0x0f, 0x9f, 0xd6, 0x4d, 0x75, 0xea, 0x72, - 0xd3, 0x11, 0x43, 0x9e, 0xf1, 0x9a, 0xad, 0x0c, 0x4e, 0xd3, 0xa8, 0x49, - 0xc1, 0x5b, 0xc5, 0x6b, 0x0c, 0xe5, 0x95, 0xc1, 0x28, 0x10, 0x0c, 0x70, - 0xb0, 0xa9, 0xad, 0x0c, 0xa1, 0xc3, 0x85, 0x98, 0x16, 0x61, 0x33, 0x15, - 0xfa, 0x47, 0xf3, 0x9f, 0xa2, 0x70, 0x31, 0xa6, 0xed, 0x3c, 0xee, 0xe9, - 0xc3, 0x90, 0xcc, 0xbb, 0xb9, 0x27, 0x1f, 0x1d, 0xe8, 0x34, 0xb2, 0xab, - 0x9d, 0x70, 0x65, 0x00, 0x04, 0xe0, 0x5b, 0x8f, 0xa4, 0x2f, 0x33, 0x08, - 0x9c, 0xa2, 0x0b, 0xcf, 0x48, 0xc3, 0x28, 0xd4, 0xc1, 0x93, 0x2f, 0x8b, - 0xe2, 0x13, 0x0b, 0x35, 0x24, 0x2a, 0x11, 0x14, 0x12, 0xcb, 0xcd, 0xe6, - 0x01, 0xa1, 0x89, 0x28, 0xe8, 0x56, 0x4d, 0x2f, 0x45, 0x02, 0x10, 0xe9, - 0x61, 0x5c, 0x8d, 0x4f, 0x6b, 0x4d, 0x3a, 0x1d, 0x02, 0x82, 0x24, 0x20, - 0x48, 0x35, 0xcc, 0x12, 0x41, 0xaa, 0xfb, 0x35, 0x82, 0x5f, 0x9e, 0x75, - 0xc5, 0x82, 0x24, 0x67, 0x01, 0x7d, 0xd5, 0xb4, 0x00, 0x30, 0x8d, 0xc8, - 0xbf, 0xc2, 0xd0, 0x00, 0x2b, 0x20, 0x59, 0xa5, 0x27, 0xb8, 0x57, 0xb1, - 0xcc, 0x9a, 0xb4, 0xca, 0x66, 0xbe, 0x27, 0xa7, 0x3d, 0x1a, 0x7b, 0x02, - 0x7b, 0x68, 0xae, 0xba, 0x8b, 0xf6, 0x8c, 0xbe, 0x33, 0x94, 0xf2, 0x77, - 0x06, 0x1d, 0x85, 0x07, 0x25, 0xf7, 0x99, 0x61, 0x97, 0x96, 0xcb, 0x00, - 0xc5, 0xe5, 0xf3, 0xd6, 0x4f, 0x1d, 0x71, 0xd9, 0x26, 0x4d, 0x03, 0x64, - 0x99, 0x0a, 0x3c, 0x77, 0x12, 0x28, 0xa1, 0xf7, 0xcc, 0x86, 0xcc, 0x65, - 0x91, 0x01, 0xc4, 0xce, 0x15, 0x1a, 0xb1, 0x2c, 0xaa, 0x09, 0x91, 0x93, - 0xaa, 0x23, 0x6b, 0x48, 0xd1, 0x5b, 0x40, 0xb3, 0xe4, 0x4b, 0x30, 0xa4, - 0x30, 0x11, 0x9b, 0x70, 0xa3, 0xec, 0x2d, 0xb1, 0x61, 0x00, 0xc0, 0x71, - 0xb7, 0x80, 0x0e, 0x74, 0xca, 0xe4, 0x25, 0x57, 0xa8, 0xe0, 0x07, 0x7e, - 0x14, 0x03, 0xe4, 0x4f, 0x93, 0x7b, 0x6b, 0x3f, 0x28, 0x50, 0x07, 0x40, - 0x39, 0x31, 0x9f, 0xb8, 0x68, 0xc4, 0x31, 0xcc, 0x1a, 0xf1, 0x25, 0x6e, - 0xc6, 0x6a, 0x8b, 0x43, 0x6b, 0xf3, 0x7e, 0x9e, 0x44, 0x83, 0x3b, 0x10, - 0x07, 0xff, 0xf1, 0x50, 0x80, 0x2f, 0xdf, 0xfc, 0x21, 0x1a, 0xcd, 0xbd, - 0x2f, 0xdf, 0x00, 0x00, 0x96, 0xb8, 0x20, 0xec, 0x32, 0xd4, 0x0a, 0x91, - 0x89, 0x01, 0x02, 0x50, 0x04, 0xa0, 0x07, 0x1c, 0xf1, 0xcf, 0x9f, 0x1e, - 0x7c, 0x5e, 0x7d, 0x6a, 0xe6, 0xba, 0x02, 0xe5, 0x1f, 0x96, 0xcd, 0xd8, - 0xe6, 0x53, 0x5c, 0x63, 0xbb, 0x96, 0x5a, 0x0b, 0x69, 0x3a, 0x0e, 0xd6, - 0xf6, 0x31, 0x44, 0x74, 0x47, 0xcb, 0x6f, 0x3d, 0x9a, 0x93, 0x23, 0x02, - 0x5b, 0xa0, 0xe4, 0x6d, 0xd4, 0xbb, 0xe8, 0x35, 0x16, 0x89, 0x9d, 0xf8, - 0x54, 0x46, 0xaa, 0x03, 0x66, 0x91, 0x23, 0x17, 0xc1, 0xc5, 0xd2, 0x20, - 0x71, 0x12, 0xb6, 0x3f, 0x73, 0xfc, 0x72, 0xcd, 0xc2, 0xf6, 0xc6, 0xc6, - 0x1b, 0x34, 0xa0, 0xae, 0x39, 0x96, 0x49, 0x9b, 0x35, 0xbe, 0xe5, 0xf9, - 0x27, 0x30, 0x65, 0x81, 0x2c, 0xf5, 0x26, 0xc6, 0xd9, 0x17, 0x9a, 0xcc, - 0x7f, 0x9c, 0xe8, 0x08, 0xbc, 0x21, 0x15, 0x8e, 0xbe, 0x0b, 0xc1, 0x81, - 0x19, 0x08, 0x17, 0xa0, 0x00, 0x5c, 0x96, 0x6c, 0xc6, 0x5c, 0x4c, 0xf0, - 0x88, 0x2f, 0xe4, 0x78, 0x61, 0x98, 0x31, 0xe1, 0x8c, 0x10, 0xa8, 0xa9, - 0x11, 0x37, 0xde, 0x60, 0xc6, 0x04, 0xa7, 0x9a, 0x49, 0x14, 0x62, 0xaf, - 0xf1, 0x0d, 0xba, 0xfd, 0x52, 0xca, 0xe8, 0x83, 0x71, 0x31, 0x4c, 0x18, - 0x4d, 0x4c, 0xc5, 0x3c, 0xa2, 0xc8, 0x16, 0xac, 0x70, 0xd9, 0x9a, 0xaf, - 0x8e, 0x3a, 0x97, 0x8a, 0xd9, 0x89, 0x2f, 0x11, 0xb0, 0x41, 0x15, 0xf6, - 0x78, 0x42, 0x52, 0x29, 0x03, 0xac, 0x91, 0x63, 0x2c, 0xc6, 0x36, 0x97, - 0x4f, 0x8c, 0xa1, 0x62, 0xa9, 0x18, 0x19, 0x2b, 0xc3, 0x6e, 0xe2, 0xfa, - 0xbc, 0x30, 0xf1, 0xce, 0x9c, 0x8e, 0x91, 0x98, 0xb3, 0xb7, 0x98, 0x0c, - 0x82, 0xf6, 0xe8, 0xe3, 0xd3, 0x5e, 0x5d, 0x2b, 0xcd, 0x3e, 0xa9, 0x6a, - 0x23, 0x1e, 0xc5, 0x05, 0x10, 0xd8, 0x65, 0x1e, 0xf6, 0x5b, 0x2e, 0x53, - 0x2f, 0x17, 0x97, 0x28, 0x65, 0x86, 0x28, 0xef, 0xce, 0x29, 0x13, 0x34, - 0x11, 0x4b, 0xfb, 0x2e, 0x8c, 0x46, 0x60, 0x6a, 0xd9, 0x5e, 0xd6, 0x9a, - 0x2d, 0x59, 0xad, 0x1f, 0x9c, 0xcc, 0xfb, 0xd9, 0x8d, 0x8b, 0x4a, 0x08, - 0x93, 0xae, 0x08, 0x60, 0x71, 0x9c, 0x7d, 0x5e, 0xe3, 0x25, 0x51, 0x31, - 0xf9, 0x3c, 0x62, 0xd3, 0x57, 0x98, 0x43, 0x13, 0x6f, 0x05, 0xd3, 0x51, - 0xff, 0xb6, 0x57, 0x01, 0xb3, 0x4c, 0x94, 0xa2, 0xca, 0x61, 0xab, 0x75, - 0x84, 0x73, 0xe4, 0x63, 0xba, 0xe6, 0x6b, 0x0a, 0x9c, 0x25, 0xb7, 0xcd, - 0xfd, 0x63, 0x98, 0x9a, 0xce, 0x99, 0x52, 0xf2, 0x0d, 0xd9, 0xa0, 0xac, - 0x06, 0x5a, 0x7f, 0x5f, 0xe6, 0x40, 0x90, 0xba, 0x80, 0xb0, 0x70, 0xff, - 0xf1, 0x50, 0x80, 0x2e, 0x9f, 0xfc, 0x21, 0x1a, 0xce, 0xfa, 0xfd, 0xff, - 0x91, 0x80, 0x96, 0xb6, 0xc1, 0xac, 0x2c, 0x22, 0x0d, 0x86, 0x4e, 0xa8, - 0x21, 0x30, 0x8a, 0x81, 0x52, 0xa0, 0x2a, 0x00, 0x2a, 0xf2, 0x50, 0x5f, - 0x9d, 0x6f, 0x8f, 0xb8, 0x32, 0x98, 0xfd, 0xf7, 0x80, 0xc7, 0xbf, 0xed, - 0xde, 0xfe, 0x22, 0x85, 0xac, 0xfb, 0x88, 0xfa, 0x82, 0x3d, 0xb1, 0x91, - 0xf6, 0x13, 0x87, 0x4d, 0x1c, 0x76, 0x17, 0x5b, 0xea, 0xac, 0x6f, 0x54, - 0xe0, 0x25, 0x97, 0xc9, 0xe3, 0xca, 0x35, 0x8a, 0x20, 0xd5, 0xd4, 0x5f, - 0x1a, 0x14, 0x07, 0xe6, 0x2c, 0x6a, 0x82, 0xc4, 0xe1, 0x4d, 0xc5, 0xc0, - 0x37, 0x01, 0x7a, 0xba, 0xdc, 0xf9, 0xd8, 0x8d, 0xa3, 0x44, 0x12, 0x3a, - 0xe7, 0xdf, 0x39, 0xae, 0xb1, 0x20, 0x05, 0x90, 0xb6, 0x2a, 0xd1, 0x4c, - 0x4f, 0x31, 0xef, 0x93, 0xa5, 0x81, 0xa8, 0xf1, 0x17, 0x7b, 0x17, 0x11, - 0x9c, 0xcd, 0x54, 0x2a, 0x73, 0x5e, 0xa7, 0xfc, 0x7e, 0x0e, 0x35, 0x58, - 0x63, 0x82, 0xcd, 0xf0, 0x2a, 0xaa, 0x80, 0x1b, 0xb4, 0x42, 0xac, 0xaa, - 0x44, 0x94, 0x92, 0x0a, 0x0b, 0xf3, 0xfd, 0x10, 0xc6, 0x02, 0xf8, 0x00, - 0x0a, 0x15, 0xd7, 0xf5, 0xe2, 0x62, 0xc0, 0x8b, 0x20, 0x02, 0x6c, 0x5e, - 0xbd, 0xb7, 0xb2, 0xed, 0x15, 0x29, 0x09, 0xc9, 0x16, 0xa2, 0x77, 0xbe, - 0x98, 0xa8, 0x98, 0x2a, 0xee, 0x90, 0x06, 0xc9, 0x33, 0x25, 0x62, 0x50, - 0xab, 0xab, 0xae, 0x51, 0x90, 0xe6, 0xff, 0xb9, 0x75, 0xf6, 0xf8, 0x7f, - 0x8a, 0x2e, 0x84, 0x44, 0x80, 0x14, 0x85, 0x72, 0xb5, 0xc0, 0x13, 0xa0, - 0xa8, 0x92, 0x08, 0xdb, 0xdc, 0x9e, 0x78, 0x8c, 0x69, 0x1a, 0x9f, 0x5e, - 0x77, 0xcb, 0x58, 0xe1, 0x36, 0x18, 0x35, 0x85, 0x07, 0x61, 0x92, 0x79, - 0x04, 0x3e, 0xa0, 0x00, 0x00, 0x00, 0xa5, 0xd2, 0x8c, 0x95, 0x78, 0xd0, - 0x2c, 0xe5, 0x2e, 0x8e, 0xf5, 0x95, 0x45, 0x59, 0x6e, 0x23, 0x99, 0xe4, - 0x0e, 0xca, 0xa4, 0x76, 0xe0, 0x3f, 0xa1, 0x1a, 0xc1, 0x98, 0x66, 0x54, - 0x16, 0x7a, 0x51, 0x09, 0x07, 0x68, 0xc9, 0x08, 0x2d, 0x1c, 0x6d, 0xca, - 0xbe, 0xb5, 0x06, 0x5f, 0xc5, 0x6e, 0x75, 0x1c, 0x3f, 0x45, 0x52, 0xce, - 0x99, 0xd4, 0xc2, 0xce, 0x64, 0x3c, 0x02, 0xb3, 0x30, 0xe3, 0x80, 0x0b, - 0xff, 0xf0, 0x8f, 0x8c, 0x90, 0x7e, 0x59, 0xf4, 0x8c, 0x43, 0xb4, 0x48, - 0x87, 0xe0, 0x7e, 0xef, 0xa9, 0x4e, 0x98, 0x61, 0x42, 0xa6, 0xd8, 0x63, - 0xcc, 0x82, 0xb0, 0xd2, 0x5a, 0xab, 0x2c, 0x0b, 0xe2, 0x7b, 0xee, 0xd5, - 0x11, 0x22, 0x6e, 0x42, 0x6c, 0x37, 0xc3, 0x5c, 0xe6, 0x6d, 0xe0, 0xff, - 0xf1, 0x50, 0x80, 0x2e, 0xbf, 0xfc, 0x21, 0x1a, 0xcf, 0xcf, 0xff, 0xf7, - 0x87, 0x00, 0x96, 0xba, 0x20, 0xec, 0x32, 0x95, 0x2b, 0x12, 0x02, 0x00, - 0x00, 0x00, 0x0b, 0xcb, 0xcb, 0xc3, 0xdb, 0x8a, 0xe3, 0x8e, 0x83, 0x2e, - 0x3c, 0xe4, 0xef, 0xb8, 0xc3, 0xa5, 0x25, 0x40, 0x3a, 0x92, 0x2c, 0xb0, - 0x5a, 0xaa, 0xe2, 0x9a, 0x98, 0xc1, 0xf4, 0xe5, 0x6c, 0x9a, 0x32, 0x23, - 0xab, 0x08, 0xfd, 0xf9, 0x50, 0x1e, 0xb4, 0x23, 0x4b, 0x6f, 0x28, 0x95, - 0xf2, 0x51, 0xa1, 0x5e, 0x0e, 0xc8, 0xf8, 0xab, 0xe7, 0xb7, 0xe1, 0xa6, - 0x96, 0x5c, 0x08, 0xc3, 0x4b, 0x35, 0x9b, 0xd9, 0x62, 0x16, 0x8e, 0xe3, - 0x93, 0xdd, 0xc0, 0x16, 0x4d, 0x2c, 0xbe, 0x1b, 0xe8, 0x87, 0x7f, 0x6a, - 0xab, 0x7a, 0x3e, 0x42, 0x70, 0xc0, 0xa3, 0x92, 0x05, 0xda, 0x50, 0x54, - 0x00, 0x04, 0x12, 0x19, 0x5c, 0xc1, 0x79, 0x22, 0x15, 0x9c, 0x31, 0xcf, - 0xe6, 0x7f, 0xb9, 0x69, 0x15, 0x59, 0xad, 0x0d, 0x1b, 0x0b, 0x05, 0x86, - 0x7a, 0x58, 0xca, 0x84, 0x15, 0xa9, 0x62, 0x99, 0x15, 0x5b, 0xb3, 0x52, - 0xb7, 0x7c, 0x9b, 0x40, 0x61, 0x89, 0x6b, 0xc2, 0x42, 0x02, 0x65, 0x72, - 0xcf, 0xc3, 0x58, 0x49, 0x67, 0x3c, 0x86, 0x70, 0x85, 0xdc, 0x8d, 0xf5, - 0xff, 0xe4, 0x8d, 0x14, 0xed, 0x8b, 0x60, 0x8a, 0x85, 0x25, 0x68, 0x88, - 0xee, 0xc2, 0x40, 0xdc, 0x23, 0x8a, 0x26, 0x2b, 0x7a, 0xe5, 0x5d, 0xc6, - 0x11, 0x04, 0x68, 0x90, 0xc0, 0x81, 0x8d, 0xc3, 0x74, 0x84, 0x22, 0x67, - 0x13, 0xd4, 0x89, 0x06, 0x40, 0x2f, 0xcd, 0xe9, 0xb7, 0x0e, 0xe3, 0x78, - 0x13, 0xa7, 0x28, 0x08, 0x0e, 0x4d, 0xdf, 0xca, 0x8d, 0x56, 0x2e, 0xcc, - 0xfa, 0x8a, 0x9b, 0xec, 0x08, 0xd1, 0x85, 0xe2, 0x5d, 0xd2, 0x1c, 0xd3, - 0xd7, 0x32, 0xab, 0xc0, 0x85, 0xa9, 0x68, 0x65, 0x91, 0x87, 0x61, 0x94, - 0x7b, 0xc1, 0x48, 0x00, 0x02, 0xa0, 0xc9, 0x53, 0xc7, 0x45, 0x24, 0xa5, - 0x85, 0x58, 0x6f, 0xd7, 0x17, 0xeb, 0xa3, 0x93, 0x25, 0x17, 0x5a, 0x99, - 0x12, 0xc2, 0x08, 0x87, 0x2a, 0x00, 0xa2, 0x76, 0x60, 0xc0, 0x09, 0xce, - 0x24, 0x4d, 0x54, 0xab, 0x15, 0x25, 0x03, 0x44, 0x74, 0x9e, 0x66, 0xaa, - 0xc9, 0x26, 0x64, 0x3a, 0x80, 0x6b, 0xab, 0x45, 0xf1, 0x4a, 0xeb, 0x43, - 0x61, 0xa6, 0x44, 0x95, 0x88, 0x98, 0x50, 0x2a, 0xaf, 0x77, 0x09, 0x8e, - 0x7a, 0x11, 0x75, 0x51, 0xa2, 0x30, 0x43, 0x29, 0xcf, 0xa7, 0xff, 0xcf, - 0xf0, 0x35, 0x8e, 0x59, 0xa6, 0x71, 0xb9, 0xa8, 0xb9, 0x55, 0x4a, 0x64, - 0x8a, 0x56, 0xa7, 0xba, 0x79, 0xb8, 0x41, 0x8b, 0x12, 0xe8, 0x10, 0x0e, - 0xff, 0xf1, 0x50, 0x80, 0x31, 0xdf, 0xfc, 0x21, 0x1a, 0xc9, 0xbf, 0x7b, - 0xf7, 0x80, 0x00, 0x94, 0xba, 0x30, 0xec, 0x24, 0x29, 0x3a, 0x91, 0x06, - 0xc3, 0x00, 0x00, 0x00, 0x04, 0xa3, 0x8e, 0x4e, 0xbd, 0xb9, 0xf6, 0x9e, - 0xc1, 0xb9, 0x3a, 0x1f, 0x99, 0xfb, 0x03, 0xfd, 0xec, 0x72, 0x50, 0xa0, - 0xef, 0x3b, 0xd5, 0x58, 0x1f, 0x65, 0xa7, 0x39, 0x1b, 0x37, 0xe2, 0xc5, - 0x4f, 0x2b, 0x25, 0x1e, 0x4a, 0xdc, 0x1a, 0x2b, 0x21, 0xab, 0xfe, 0x1c, - 0x94, 0x7c, 0x86, 0x05, 0x1a, 0x3f, 0x15, 0xe9, 0x12, 0xc0, 0xa2, 0xad, - 0x3a, 0xd0, 0xee, 0x91, 0xd1, 0x0a, 0x23, 0xd4, 0x62, 0x38, 0x86, 0x18, - 0x28, 0xc7, 0x99, 0x22, 0x29, 0xda, 0x44, 0x70, 0x31, 0x65, 0x40, 0x04, - 0x27, 0x43, 0x57, 0x6d, 0xeb, 0xa0, 0x00, 0xde, 0xf0, 0x31, 0x14, 0x89, - 0x85, 0x4e, 0x63, 0x38, 0xe0, 0xab, 0x24, 0xd2, 0x07, 0x00, 0x65, 0xe2, - 0x06, 0xf1, 0xed, 0x89, 0x11, 0x1a, 0x94, 0xb7, 0xd0, 0xff, 0x64, 0xf5, - 0xa9, 0x5d, 0x66, 0x46, 0x59, 0xdc, 0x04, 0x80, 0x60, 0xbe, 0xaf, 0x46, - 0xc8, 0xc9, 0x6a, 0x46, 0x21, 0x53, 0x91, 0x33, 0x32, 0x9c, 0xf8, 0xff, - 0x3e, 0xcc, 0x64, 0x0c, 0x71, 0x89, 0x28, 0x01, 0x51, 0x87, 0x85, 0xd0, - 0x8c, 0x57, 0x49, 0x2a, 0x73, 0x03, 0x6a, 0x55, 0x2f, 0x25, 0x7a, 0x59, - 0xab, 0x62, 0xad, 0x62, 0x02, 0xae, 0xe3, 0x35, 0xad, 0xea, 0x1b, 0xfa, - 0x70, 0x02, 0xe7, 0x35, 0x09, 0xab, 0x03, 0x60, 0x2d, 0xa7, 0x43, 0xed, - 0xfc, 0x9f, 0x92, 0x1b, 0xc5, 0x75, 0x51, 0xd3, 0x76, 0xdd, 0x6a, 0x67, - 0xa2, 0x0f, 0xf6, 0xa0, 0xe6, 0x29, 0x2b, 0x4a, 0xf7, 0x81, 0xc1, 0x6c, - 0xa9, 0x99, 0x38, 0xf6, 0xd2, 0x18, 0xe2, 0xf3, 0xf3, 0x73, 0x2e, 0x35, - 0x7e, 0xf9, 0xf9, 0x70, 0xaa, 0x6e, 0xdf, 0x73, 0x6f, 0xfd, 0x5b, 0x77, - 0xba, 0xeb, 0xa5, 0x2d, 0xb0, 0x6b, 0x0c, 0xa3, 0xc8, 0x21, 0xf5, 0x00, - 0x00, 0x00, 0x07, 0xae, 0xa4, 0xba, 0x55, 0x41, 0x01, 0xeb, 0x8e, 0x06, - 0xdf, 0x04, 0xa8, 0x22, 0xe3, 0xd7, 0xd5, 0x79, 0xde, 0x2b, 0xc0, 0xe1, - 0xa2, 0xad, 0x3c, 0xbd, 0x1d, 0x5b, 0x71, 0x16, 0x2a, 0x15, 0xa5, 0x67, - 0xe7, 0x5e, 0xd4, 0xf0, 0x8f, 0xc2, 0x10, 0xa2, 0xec, 0xe4, 0x43, 0x91, - 0x49, 0xab, 0xe7, 0x9b, 0x50, 0xbf, 0x3f, 0x9a, 0x59, 0xcf, 0x07, 0x6c, - 0xc3, 0x72, 0xdc, 0x60, 0x26, 0xa4, 0xa0, 0xec, 0x2d, 0xc5, 0x50, 0x65, - 0x57, 0x34, 0x53, 0x32, 0x6f, 0xc6, 0x76, 0xc7, 0x42, 0xab, 0x6b, 0x27, - 0x14, 0xb3, 0xc6, 0x35, 0xf9, 0xff, 0x9f, 0xf7, 0x6f, 0x43, 0x9d, 0x63, - 0x49, 0x65, 0x71, 0x71, 0x45, 0x44, 0x41, 0x6c, 0x59, 0xe7, 0xd3, 0x7f, - 0x62, 0xf5, 0xbc, 0xc9, 0xc6, 0x09, 0xc6, 0xc2, 0xac, 0x07, 0x1f, 0x00, - 0x4b, 0x80, 0xff, 0xf1, 0x50, 0x80, 0x30, 0x3f, 0xfc, 0x21, 0x1a, 0xcf, - 0xfa, 0xb3, 0xff, 0x2b, 0xbf, 0x92, 0xb6, 0xc1, 0xac, 0x32, 0xd4, 0x11, - 0x89, 0x8c, 0x01, 0x52, 0xa5, 0x45, 0x7c, 0x7a, 0x00, 0x6b, 0x92, 0x57, - 0xb7, 0xa3, 0xf5, 0xeb, 0xc7, 0x5e, 0x43, 0x19, 0x51, 0x27, 0x26, 0x8e, - 0x76, 0x33, 0xbf, 0x20, 0xc7, 0xe2, 0xff, 0xff, 0x76, 0xf3, 0x80, 0xd9, - 0xe0, 0xf2, 0x84, 0xf3, 0x20, 0x0a, 0xa5, 0x38, 0x98, 0x57, 0x14, 0xcd, - 0x0a, 0xf5, 0xfd, 0xf0, 0xb4, 0x34, 0x0b, 0x8e, 0x12, 0x5c, 0x6d, 0x01, - 0x9e, 0x94, 0x68, 0x23, 0x98, 0x39, 0x3f, 0xc1, 0xf2, 0xc8, 0x03, 0xf6, - 0xc5, 0xac, 0xc9, 0x18, 0x83, 0xad, 0x4a, 0xab, 0xca, 0xd1, 0xa8, 0x81, - 0x36, 0x05, 0x28, 0x3a, 0x5d, 0xed, 0x78, 0xdd, 0xca, 0x06, 0x9e, 0x30, - 0x63, 0x73, 0x83, 0x2d, 0x6f, 0xd5, 0x3f, 0x36, 0xf2, 0x10, 0xac, 0x6e, - 0x14, 0xca, 0x82, 0x91, 0x06, 0x11, 0x38, 0xc5, 0xf8, 0xff, 0xd0, 0x76, - 0x17, 0x59, 0x62, 0x31, 0xc8, 0x31, 0xba, 0x00, 0x6f, 0xd0, 0x04, 0x02, - 0x42, 0x83, 0x0b, 0x06, 0x3d, 0x66, 0xed, 0x42, 0x81, 0x59, 0x02, 0x56, - 0x05, 0x4b, 0xb6, 0xca, 0x44, 0x03, 0x2b, 0xb1, 0x4b, 0x7c, 0xdd, 0xe3, - 0xf5, 0xe2, 0xda, 0x9b, 0x18, 0xeb, 0xb5, 0x44, 0x52, 0xae, 0xc4, 0x3f, - 0x05, 0xaa, 0x9d, 0x28, 0x09, 0x48, 0x15, 0x92, 0x2a, 0xc5, 0x0c, 0x11, - 0xa1, 0x93, 0xa5, 0xaf, 0x55, 0xd8, 0x48, 0x75, 0xca, 0xa7, 0x8c, 0x56, - 0x03, 0x7b, 0x31, 0xa5, 0x62, 0xe8, 0xdd, 0xab, 0x11, 0x2f, 0x59, 0x96, - 0x81, 0x99, 0x6c, 0x99, 0x6d, 0x30, 0xa2, 0x9d, 0xcf, 0x26, 0xec, 0x8b, - 0x4f, 0xd3, 0x63, 0xef, 0xee, 0x8f, 0x3e, 0xfc, 0x19, 0xfb, 0xb3, 0x26, - 0xe7, 0xe6, 0x9f, 0xd9, 0x7a, 0x5c, 0xfe, 0x6f, 0x09, 0x2a, 0x65, 0x91, - 0x87, 0x61, 0x61, 0xc9, 0x79, 0xe0, 0x00, 0x00, 0x01, 0xcc, 0x93, 0xdf, - 0xeb, 0x77, 0x51, 0x8b, 0x04, 0x37, 0x2f, 0xb8, 0x92, 0x13, 0xf3, 0xd3, - 0x31, 0xf9, 0x17, 0x67, 0x37, 0x88, 0x55, 0x83, 0x7a, 0x1c, 0x21, 0x61, - 0x06, 0x89, 0x5a, 0x8a, 0x43, 0x1a, 0x74, 0x3c, 0x0e, 0xdd, 0xc0, 0x10, - 0xc7, 0xec, 0x71, 0xe2, 0xc7, 0x28, 0x64, 0x0e, 0x66, 0x67, 0x17, 0xaf, - 0x33, 0xe9, 0x4d, 0x53, 0xdb, 0xb7, 0x5e, 0x0a, 0x6b, 0x15, 0x0d, 0x04, - 0x67, 0xff, 0x6a, 0x72, 0xfe, 0xe9, 0xc4, 0x39, 0xa3, 0x9d, 0x90, 0x73, - 0x44, 0x6d, 0x40, 0x35, 0xae, 0xa4, 0x31, 0x16, 0x2a, 0x15, 0xcd, 0xff, - 0xca, 0xff, 0xe2, 0xf9, 0xc6, 0x8c, 0xe3, 0x8a, 0x0a, 0x6e, 0xc4, 0x4a, - 0xec, 0x45, 0xdc, 0xe3, 0xe7, 0xb9, 0x78, 0x6a, 0x08, 0xa3, 0x1d, 0x10, - 0xb8, 0x03, 0x80, 0xff, 0xf1, 0x50, 0x80, 0x2e, 0xff, 0xfc, 0x21, 0x1a, - 0xcb, 0xa2, 0xf7, 0xff, 0xab, 0xff, 0x92, 0xba, 0x20, 0xe4, 0x86, 0x25, - 0x4b, 0x14, 0x00, 0x00, 0x02, 0x52, 0xf3, 0x8e, 0x78, 0xe6, 0x2b, 0x5e, - 0x7e, 0x32, 0xef, 0x80, 0xf5, 0x6f, 0x06, 0xa5, 0x4e, 0x04, 0x3e, 0xa0, - 0x20, 0x54, 0x4a, 0xc3, 0x9f, 0x90, 0x6b, 0x12, 0x49, 0xbc, 0x44, 0x03, - 0xe3, 0x94, 0x2c, 0x10, 0x86, 0xea, 0xe2, 0x8a, 0x14, 0xd3, 0xd8, 0x56, - 0xd9, 0x18, 0xce, 0xca, 0x23, 0x96, 0xcb, 0x5d, 0xa0, 0x7c, 0x9a, 0xb8, - 0xc8, 0x8a, 0x24, 0x45, 0x83, 0xd4, 0x8e, 0x38, 0x1a, 0x06, 0xc5, 0x8d, - 0x30, 0xd4, 0x68, 0xc6, 0x5c, 0xcc, 0x61, 0x59, 0x6b, 0xdb, 0x14, 0x0d, - 0x93, 0x0a, 0x18, 0x18, 0x27, 0x34, 0x4b, 0xc7, 0x98, 0x60, 0xc8, 0xbb, - 0x1a, 0x9e, 0xdf, 0xf3, 0x1f, 0x2b, 0x14, 0x9a, 0xb1, 0x96, 0xa0, 0x01, - 0x37, 0x76, 0xc7, 0xd2, 0x78, 0xdd, 0x76, 0x16, 0xcd, 0x2b, 0x7b, 0x38, - 0xda, 0xe0, 0xfe, 0x27, 0x20, 0x29, 0xa4, 0x2a, 0xb4, 0x94, 0x40, 0x98, - 0xcf, 0xab, 0xa7, 0xf2, 0xe8, 0x01, 0x7c, 0xb7, 0x50, 0x09, 0x54, 0x16, - 0xaf, 0xaf, 0x5a, 0x12, 0x28, 0x00, 0x28, 0x46, 0xe7, 0x16, 0xb0, 0x2e, - 0xc0, 0xcd, 0x2a, 0x04, 0x63, 0x4f, 0x2d, 0xb7, 0x30, 0xc8, 0x0a, 0x26, - 0xf6, 0x94, 0x5e, 0x3a, 0xee, 0xf7, 0xab, 0x49, 0x1d, 0x83, 0x76, 0xdc, - 0x6a, 0x4c, 0x9d, 0x8e, 0xea, 0xc8, 0x22, 0x9a, 0x16, 0xe1, 0x6e, 0xd3, - 0x64, 0x36, 0x4b, 0x6c, 0x9e, 0x8a, 0xb7, 0xb6, 0xdc, 0x9c, 0x06, 0xf0, - 0x88, 0xab, 0x71, 0x1e, 0x98, 0x4a, 0x7b, 0x3b, 0x3b, 0x67, 0x81, 0xcd, - 0x83, 0x97, 0xd3, 0xd9, 0xe1, 0xb8, 0x71, 0xf3, 0x2f, 0x6d, 0x52, 0x57, - 0xbf, 0xc9, 0x27, 0x4c, 0xb4, 0x20, 0xec, 0x2a, 0x4e, 0x7a, 0x15, 0x28, - 0x95, 0x05, 0x40, 0x12, 0x97, 0xce, 0xbb, 0xe3, 0x9d, 0x5e, 0x5d, 0x54, - 0x48, 0x1d, 0x7d, 0x17, 0x30, 0xbc, 0x83, 0x95, 0xde, 0x73, 0x9d, 0xd5, - 0x9a, 0x2c, 0x0b, 0x8a, 0xd5, 0x59, 0x79, 0xde, 0x0c, 0xbc, 0x38, 0x08, - 0xaa, 0xc3, 0xb9, 0x4e, 0x17, 0xd1, 0xd7, 0x44, 0x12, 0x10, 0x0f, 0x6b, - 0xb1, 0xc6, 0x78, 0xd1, 0x14, 0x90, 0x9d, 0x04, 0x63, 0x66, 0xc5, 0xc0, - 0x60, 0x21, 0x59, 0x29, 0x98, 0xe6, 0x98, 0x31, 0x67, 0x46, 0x2a, 0x03, - 0xd4, 0x30, 0xc3, 0x40, 0x0d, 0x90, 0x71, 0x3e, 0x65, 0x23, 0xef, 0xd4, - 0x47, 0xe4, 0x1c, 0x28, 0x9c, 0x37, 0x96, 0x73, 0x69, 0xc0, 0x28, 0xb1, - 0xf5, 0x6a, 0x8c, 0xa4, 0x34, 0xb5, 0xce, 0xcb, 0xc7, 0xbf, 0xdb, 0x34, - 0x02, 0x62, 0xe0, 0xa8, 0x03, 0x80, 0xff, 0xf1, 0x50, 0x80, 0x30, 0x5f, - 0xfc, 0x21, 0x1a, 0xcb, 0xb9, 0xa7, 0xbf, 0xaf, 0xbf, 0x90, 0xb6, 0xc1, - 0xac, 0x2c, 0x39, 0x22, 0x99, 0x10, 0xc2, 0x00, 0x00, 0x00, 0x04, 0xae, - 0xbb, 0xeb, 0xb3, 0xaf, 0xac, 0x6b, 0xe0, 0x1b, 0xe9, 0x64, 0x21, 0x04, - 0x99, 0xcb, 0xdc, 0x84, 0x89, 0x2a, 0x64, 0x09, 0x30, 0x27, 0x38, 0x28, - 0xa5, 0xa5, 0x0e, 0x29, 0xdc, 0xd6, 0x58, 0xcf, 0xc7, 0xe2, 0x33, 0xa5, - 0xaf, 0x86, 0x0a, 0x8b, 0x8f, 0xe4, 0x18, 0x75, 0x4c, 0xc2, 0x5c, 0x0b, - 0x1f, 0xd1, 0x5f, 0x89, 0x63, 0x3d, 0xa4, 0xa0, 0x3c, 0x04, 0x75, 0x95, - 0x44, 0x18, 0x66, 0xc4, 0x60, 0x2e, 0x4d, 0x14, 0x4c, 0x65, 0xb1, 0xa4, - 0xc1, 0x19, 0x4e, 0x45, 0x0e, 0x08, 0x3e, 0x84, 0xc8, 0x56, 0xd0, 0xea, - 0x42, 0x40, 0x87, 0x42, 0x47, 0x59, 0x98, 0x18, 0xfc, 0xdf, 0xd1, 0xf5, - 0x45, 0xe3, 0x21, 0x82, 0x4b, 0xd8, 0xa8, 0x32, 0x8c, 0xb1, 0xad, 0x5e, - 0x8b, 0xe3, 0x1c, 0x08, 0xc7, 0x2b, 0x28, 0xef, 0xb1, 0x40, 0x02, 0xba, - 0xbd, 0x5b, 0xd8, 0x22, 0xa7, 0x14, 0x14, 0x2a, 0x30, 0xdb, 0x75, 0xef, - 0xf9, 0x7f, 0x9c, 0xae, 0xf7, 0x14, 0x57, 0x09, 0x4d, 0xdc, 0x86, 0x82, - 0xeb, 0xba, 0x66, 0xa5, 0x70, 0x00, 0x78, 0x39, 0xba, 0x4a, 0x86, 0x3d, - 0x0b, 0xee, 0xa2, 0xa8, 0x6a, 0xdc, 0x9e, 0x75, 0xbe, 0x2a, 0x78, 0x7e, - 0xa7, 0xa9, 0x18, 0x30, 0xb8, 0x32, 0x9b, 0x1c, 0xe3, 0xa5, 0x8b, 0xd4, - 0xf0, 0x6f, 0x69, 0xaf, 0xa7, 0xf0, 0x9c, 0x3f, 0xa7, 0xb8, 0x73, 0xf4, - 0x97, 0x73, 0x14, 0x8f, 0xd5, 0xf3, 0xae, 0x60, 0xae, 0xdf, 0x94, 0x5d, - 0xa5, 0xa9, 0x54, 0x34, 0x5d, 0xce, 0x0c, 0x46, 0xf7, 0xad, 0x67, 0x6b, - 0x6d, 0x6c, 0xb8, 0xdd, 0xe9, 0xf6, 0xdb, 0xd9, 0x98, 0xd5, 0xd3, 0xf6, - 0xca, 0x79, 0xed, 0xc6, 0x4a, 0xe0, 0xc3, 0xb1, 0x49, 0xf9, 0xeb, 0x6b, - 0xbb, 0x31, 0x6c, 0xbc, 0x58, 0xcb, 0x01, 0xb9, 0xae, 0xe3, 0x2d, 0x2a, - 0x34, 0x03, 0x2f, 0xb4, 0xf1, 0x4a, 0x5e, 0xa5, 0x7f, 0xd7, 0x42, 0xe6, - 0xc4, 0x25, 0xa9, 0x82, 0x66, 0xda, 0x39, 0xc6, 0xed, 0x8e, 0x26, 0x33, - 0xe6, 0xb7, 0x23, 0xf5, 0xa3, 0x1c, 0x87, 0x54, 0xb8, 0x79, 0x02, 0x0b, - 0x77, 0x2b, 0x18, 0x08, 0x19, 0x0c, 0xb2, 0xca, 0x08, 0xdc, 0xcf, 0xd9, - 0x73, 0xaa, 0x15, 0xbb, 0xae, 0x9e, 0xf1, 0x9a, 0x85, 0x42, 0x8c, 0xbe, - 0xce, 0xdc, 0x3a, 0x59, 0x40, 0xbc, 0x74, 0xca, 0x08, 0x0c, 0x66, 0x2b, - 0x57, 0x3b, 0xc7, 0x3b, 0xc1, 0x2b, 0xca, 0x7f, 0xf5, 0xf9, 0x7a, 0xf8, - 0x45, 0xc6, 0x62, 0x2a, 0x70, 0xa3, 0x39, 0xb4, 0x27, 0x01, 0xa1, 0xec, - 0xbe, 0x2f, 0x9a, 0x64, 0x51, 0x59, 0x80, 0x0e, 0xff, 0xf1, 0x50, 0x80, - 0x32, 0xff, 0xfc, 0x21, 0x1a, 0xcd, 0x39, 0xed, 0x75, 0x80, 0x00, 0x93, - 0xba, 0x30, 0xec, 0x32, 0x75, 0x3b, 0x14, 0x00, 0x00, 0x01, 0x28, 0x95, - 0x79, 0xe7, 0xc1, 0xed, 0xd4, 0xaf, 0x3c, 0x07, 0xba, 0x8c, 0x56, 0x60, - 0x6d, 0x12, 0x53, 0x79, 0xdd, 0x3c, 0xeb, 0xe0, 0xb8, 0xec, 0xa3, 0x20, - 0x29, 0xca, 0x6c, 0xbf, 0x94, 0x99, 0xaf, 0x71, 0x2a, 0x1e, 0x7b, 0x74, - 0x34, 0x28, 0xdc, 0xaf, 0xf4, 0x3d, 0x80, 0x42, 0xf4, 0x3f, 0xa6, 0x48, - 0x08, 0x8d, 0x2a, 0x37, 0x3e, 0x28, 0xe2, 0x29, 0x57, 0xd9, 0xb0, 0xa0, - 0x18, 0x57, 0x60, 0x8f, 0x08, 0x8b, 0x60, 0x38, 0x94, 0xc3, 0x47, 0x85, - 0x5b, 0x85, 0xb3, 0x54, 0x74, 0x76, 0x6c, 0xad, 0xac, 0xa6, 0x7b, 0x9b, - 0x34, 0x50, 0x1b, 0x34, 0x98, 0xba, 0x14, 0x18, 0x1a, 0x2b, 0xa6, 0xdf, - 0x4e, 0xf7, 0x6d, 0x9a, 0x59, 0xa2, 0x60, 0x84, 0xb3, 0x0b, 0x67, 0x36, - 0xce, 0x69, 0x53, 0xd3, 0xfe, 0x0f, 0x8f, 0x79, 0xb2, 0xc2, 0x2a, 0x26, - 0xf5, 0xf6, 0x84, 0x80, 0x20, 0xe1, 0xc8, 0x01, 0xae, 0x95, 0xa4, 0xab, - 0x30, 0x62, 0x8e, 0xcb, 0xc8, 0x40, 0xb1, 0x5a, 0x9d, 0x89, 0x4e, 0xf4, - 0x15, 0x27, 0x9f, 0xe5, 0x5b, 0x2c, 0x31, 0x5b, 0xba, 0xc4, 0xce, 0xe3, - 0x94, 0x35, 0x52, 0x8e, 0xc8, 0xfd, 0x9a, 0xbd, 0xae, 0x0d, 0xdd, 0x37, - 0xb9, 0x1b, 0x96, 0xa8, 0xd6, 0x2a, 0x7a, 0x38, 0x45, 0xc6, 0xf2, 0xc8, - 0x9b, 0xac, 0x6b, 0x47, 0x7f, 0x45, 0x5a, 0xf5, 0x73, 0x63, 0x3a, 0x67, - 0x62, 0xf0, 0xa0, 0x25, 0x61, 0x33, 0x84, 0x38, 0x3b, 0x18, 0x72, 0xa2, - 0xbc, 0x54, 0xce, 0x6d, 0x8d, 0xe3, 0x4c, 0x50, 0xd5, 0x01, 0x19, 0x0b, - 0x4a, 0x68, 0x25, 0xe5, 0xf6, 0x7c, 0xf1, 0x29, 0x5d, 0x6e, 0x30, 0x2a, - 0x39, 0x75, 0xfb, 0x28, 0xf9, 0xd1, 0xec, 0xdf, 0x5c, 0xde, 0x32, 0xc9, - 0x28, 0xcb, 0x9d, 0x92, 0x76, 0xd8, 0x35, 0x85, 0x86, 0xe5, 0xf2, 0x20, - 0x7d, 0x4c, 0xbc, 0x59, 0x8b, 0x19, 0x6c, 0xbc, 0xb3, 0x2f, 0x2d, 0x7b, - 0xcc, 0xf6, 0xe3, 0x22, 0x4c, 0xa9, 0xa0, 0x6c, 0x77, 0x82, 0x0f, 0xfe, - 0xa1, 0x4e, 0x70, 0x1e, 0xac, 0xdb, 0xec, 0xc1, 0x10, 0xcc, 0xd6, 0x97, - 0xc2, 0x9a, 0x53, 0x55, 0x63, 0x96, 0xf4, 0xbd, 0x38, 0xca, 0x47, 0x1b, - 0xb3, 0xe8, 0x30, 0x58, 0x1e, 0xce, 0x20, 0x9e, 0x1a, 0x73, 0xd9, 0x2a, - 0x11, 0x76, 0x1e, 0x99, 0x43, 0x79, 0x52, 0xb4, 0x12, 0xd3, 0x78, 0xe0, - 0x53, 0x8c, 0xe2, 0x3f, 0x54, 0x4c, 0x64, 0x75, 0x80, 0xe3, 0x10, 0x99, - 0x2c, 0x8d, 0xd8, 0xa4, 0x80, 0x1d, 0x8b, 0x65, 0x88, 0x45, 0x3a, 0xab, - 0x67, 0x8d, 0xf8, 0x11, 0x50, 0x55, 0xad, 0x9e, 0xd0, 0xac, 0x58, 0x58, - 0x93, 0x8d, 0xc4, 0xf4, 0x3a, 0x6b, 0xc9, 0x22, 0xd5, 0x20, 0x00, 0x77, - 0x3e, 0x0d, 0xd3, 0xef, 0x55, 0x71, 0x70, 0xff, 0xf1, 0x50, 0x80, 0x2f, - 0x1f, 0xfc, 0x21, 0x1a, 0xcf, 0xdf, 0x7f, 0xfd, 0x00, 0x00, 0x97, 0xb2, - 0x42, 0x2c, 0x30, 0x6b, 0x0b, 0x0e, 0xc3, 0x2c, 0x41, 0x30, 0xe0, 0x40, - 0x00, 0x00, 0x0d, 0x6c, 0xeb, 0xb9, 0x5d, 0x76, 0x7d, 0xf5, 0x1e, 0x6c, - 0x33, 0xef, 0xbc, 0x8d, 0xbd, 0x63, 0x7f, 0x81, 0xcd, 0xfc, 0xcd, 0xb8, - 0x10, 0x92, 0x40, 0x12, 0x48, 0x4a, 0x7a, 0xb3, 0xd2, 0x1a, 0x2c, 0x5f, - 0x89, 0x71, 0x7c, 0x5f, 0xb9, 0x93, 0xba, 0x9b, 0x55, 0x33, 0x46, 0x5b, - 0x98, 0xbe, 0x2a, 0x00, 0x81, 0x7e, 0x61, 0x0b, 0x59, 0xdb, 0xad, 0x80, - 0xea, 0x00, 0x23, 0x20, 0x07, 0x27, 0x98, 0xe8, 0x78, 0x0b, 0xcd, 0x35, - 0x1c, 0xda, 0xa6, 0x81, 0x33, 0xf0, 0x95, 0x40, 0xa2, 0x60, 0x40, 0x31, - 0x73, 0x31, 0x00, 0xff, 0x7d, 0xbe, 0x80, 0xb3, 0x4d, 0x10, 0x60, 0xba, - 0xa0, 0x84, 0xae, 0xea, 0x37, 0xe6, 0xe8, 0x3f, 0xc0, 0x70, 0x98, 0x25, - 0x57, 0x6b, 0xc7, 0x68, 0xa8, 0x00, 0x1a, 0xfd, 0x34, 0xa4, 0x51, 0x0d, - 0x41, 0x9b, 0x52, 0x6b, 0x09, 0x22, 0x17, 0x9f, 0x77, 0xeb, 0x60, 0x05, - 0x61, 0x5b, 0x09, 0x20, 0x19, 0x21, 0x7a, 0xfe, 0x0b, 0x44, 0x00, 0xad, - 0x98, 0x5c, 0x5d, 0x45, 0x54, 0x2a, 0xc6, 0x18, 0x73, 0x51, 0x0b, 0x12, - 0x9d, 0xb4, 0xa0, 0x96, 0x29, 0x0c, 0x7b, 0x72, 0x9c, 0xfd, 0x7b, 0x5a, - 0xbe, 0x5b, 0x77, 0xd2, 0xfe, 0x51, 0x0e, 0xa4, 0x41, 0xd1, 0x64, 0x1d, - 0x35, 0x68, 0xb1, 0x14, 0xde, 0xb9, 0xe5, 0x64, 0xbf, 0x28, 0xd2, 0x79, - 0x50, 0xc5, 0x8b, 0xcf, 0x7c, 0xbb, 0x74, 0x0b, 0x14, 0xec, 0x50, 0x4a, - 0x13, 0xf1, 0x6a, 0x78, 0xad, 0x61, 0x82, 0x80, 0xa5, 0x03, 0x08, 0x28, - 0x81, 0x43, 0xcb, 0x2e, 0xb0, 0xb3, 0x33, 0xc0, 0x60, 0xb4, 0x1f, 0x34, - 0x93, 0xaa, 0x12, 0x19, 0x97, 0x86, 0x58, 0x60, 0xd6, 0x14, 0x1c, 0x93, - 0xca, 0x41, 0xf5, 0x00, 0x00, 0x00, 0x09, 0xcd, 0xdf, 0x7e, 0xd7, 0x31, - 0x26, 0x5e, 0x58, 0x5e, 0xfe, 0x95, 0xf1, 0x69, 0x88, 0x9b, 0x1d, 0x65, - 0x52, 0x0a, 0xde, 0x34, 0x20, 0x7a, 0x83, 0xca, 0x40, 0x76, 0x65, 0x7e, - 0x48, 0x0d, 0xdc, 0xb9, 0x33, 0x05, 0x52, 0x11, 0xe7, 0x00, 0x86, 0x48, - 0x21, 0x31, 0x9f, 0xc5, 0x5e, 0x5a, 0xcf, 0x05, 0x85, 0xf7, 0xc0, 0xdb, - 0x94, 0x15, 0x1d, 0xd8, 0x3d, 0x5b, 0xe1, 0x50, 0x88, 0x01, 0xf4, 0xe9, - 0x8e, 0xfc, 0xee, 0x47, 0xf3, 0xe8, 0xde, 0xc0, 0xfd, 0x61, 0x3f, 0xb0, - 0xfc, 0xea, 0x62, 0xaa, 0x6c, 0xc2, 0x14, 0x0c, 0xed, 0x76, 0x81, 0x3a, - 0xff, 0x46, 0xd9, 0x61, 0x47, 0xdf, 0xa8, 0xd0, 0xd6, 0x90, 0x5c, 0xff, - 0xf1, 0x50, 0x80, 0x2d, 0xbf, 0xfc, 0x21, 0x1a, 0xcf, 0x9a, 0xdb, 0xed, - 0x01, 0x00, 0x98, 0xb2, 0x32, 0x2c, 0x30, 0x6b, 0x0c, 0xb5, 0x48, 0xc4, - 0x81, 0x02, 0x5e, 0xf8, 0xe6, 0x50, 0x75, 0xd8, 0x0e, 0x39, 0x38, 0xe7, - 0x5b, 0xf6, 0xf5, 0x2b, 0xf7, 0xb9, 0x9e, 0xd6, 0x19, 0xf6, 0x2e, 0x73, - 0xe3, 0x46, 0xae, 0xec, 0x8d, 0xb7, 0x9d, 0x6f, 0x12, 0x12, 0x4e, 0x25, - 0xe1, 0xdb, 0xdf, 0x50, 0x81, 0xd6, 0x3f, 0x7a, 0x99, 0x78, 0x61, 0x1b, - 0xb1, 0x28, 0xb4, 0x42, 0x03, 0x1f, 0xc6, 0x63, 0x1d, 0x42, 0xe3, 0xef, - 0x28, 0xe2, 0x67, 0x56, 0x00, 0xac, 0x21, 0x39, 0x26, 0x33, 0xbc, 0x75, - 0xd8, 0xfb, 0x78, 0xf0, 0xe6, 0x83, 0x64, 0xd5, 0xab, 0xa1, 0x8c, 0x08, - 0xe0, 0x4d, 0x5d, 0xc6, 0xa8, 0xad, 0x1f, 0x51, 0xf6, 0x24, 0x26, 0xc0, - 0x81, 0x48, 0x26, 0x23, 0x4e, 0xf6, 0x56, 0x1e, 0x73, 0xed, 0x78, 0xd2, - 0xef, 0x20, 0x6a, 0x48, 0x52, 0x45, 0x83, 0x7e, 0x4b, 0xa4, 0x03, 0x08, - 0xb0, 0xc6, 0x8d, 0x6c, 0x2c, 0x61, 0xa1, 0xeb, 0x32, 0x16, 0x0d, 0x2d, - 0x82, 0x6d, 0x04, 0xcc, 0x17, 0x7d, 0xbd, 0x02, 0x03, 0xaf, 0xbd, 0x76, - 0xb9, 0x8d, 0x91, 0x8c, 0x97, 0xaf, 0x0c, 0x08, 0x45, 0xd2, 0x5b, 0xd4, - 0xa2, 0xe0, 0x5a, 0x98, 0x2f, 0xd9, 0x8a, 0x81, 0x01, 0x8c, 0xab, 0xd3, - 0x63, 0x19, 0x94, 0xb7, 0x56, 0x63, 0x4b, 0xa4, 0x14, 0x12, 0xc7, 0xb2, - 0x70, 0xc3, 0x5e, 0xc5, 0x4b, 0xc1, 0xdf, 0x2d, 0x96, 0xe1, 0x70, 0xee, - 0x2e, 0xb7, 0xae, 0xab, 0x68, 0xf5, 0x48, 0x15, 0x00, 0xb6, 0x20, 0x60, - 0xef, 0x41, 0xbb, 0xae, 0xf4, 0xc4, 0x3e, 0xdc, 0x85, 0x54, 0x91, 0x7f, - 0x8c, 0x8d, 0xe7, 0xad, 0xab, 0x0a, 0xbd, 0x75, 0x33, 0x4b, 0xb5, 0xb3, - 0x35, 0x01, 0xb3, 0x41, 0x02, 0xa2, 0xcc, 0x33, 0x2c, 0x30, 0x36, 0x1c, - 0xa3, 0xe0, 0x18, 0xeb, 0x79, 0x79, 0x6c, 0xb6, 0x58, 0x54, 0x65, 0xb2, - 0x55, 0x78, 0xf8, 0xa6, 0x5a, 0x54, 0x80, 0x39, 0x49, 0xf4, 0x4f, 0x9d, - 0x5f, 0xac, 0x86, 0xbc, 0x27, 0xa4, 0x9c, 0x36, 0x05, 0xe7, 0x5e, 0xe3, - 0x9d, 0x08, 0xc5, 0xf3, 0x4c, 0x1f, 0x0f, 0xeb, 0x33, 0xd4, 0x00, 0xce, - 0x79, 0x6f, 0x1c, 0x3a, 0x47, 0xc0, 0xc8, 0xba, 0xe4, 0x5b, 0xcc, 0xdb, - 0x64, 0x28, 0x59, 0xf9, 0xe7, 0xe6, 0x79, 0x56, 0x0c, 0xea, 0xe6, 0x31, - 0x9d, 0x09, 0x13, 0xb9, 0x89, 0x56, 0xba, 0x8e, 0x47, 0xfa, 0x8f, 0x5e, - 0xcd, 0x51, 0x88, 0x67, 0x9e, 0xc0, 0x49, 0x4b, 0xc9, 0x0c, 0x38, 0x9e, - 0xd3, 0x88, 0x66, 0x38, 0xff, 0xf1, 0x50, 0x80, 0x2c, 0xbf, 0xfc, 0x21, - 0x1a, 0xce, 0xff, 0xdf, 0xff, 0x51, 0x40, 0x96, 0xb2, 0x42, 0x2c, 0x30, - 0x6b, 0x0b, 0x0e, 0x48, 0xaa, 0x41, 0xc0, 0xc0, 0xeb, 0xb0, 0x00, 0x2f, - 0x04, 0xab, 0xc2, 0x55, 0xe7, 0xe1, 0x9e, 0xdd, 0x58, 0x67, 0xc3, 0x7f, - 0x6f, 0xac, 0x9f, 0x9c, 0x5d, 0xa8, 0x39, 0x22, 0x3c, 0xe1, 0x5b, 0x3f, - 0x3e, 0x03, 0x43, 0x8d, 0x44, 0x21, 0x25, 0x1c, 0x0a, 0x6b, 0x6f, 0x5b, - 0x10, 0xb4, 0x98, 0x4a, 0x54, 0x5a, 0xc8, 0x9c, 0xe3, 0x3c, 0x8e, 0xee, - 0x62, 0x00, 0x0b, 0x3e, 0x5f, 0x44, 0x0e, 0x30, 0x62, 0x2d, 0x47, 0x29, - 0x64, 0x12, 0x48, 0x0f, 0xd7, 0x9f, 0x4e, 0xda, 0x4a, 0x68, 0xc1, 0x21, - 0x3d, 0x40, 0xd1, 0xf0, 0x41, 0x06, 0xa6, 0x44, 0x80, 0x06, 0x56, 0x34, - 0xf4, 0x5f, 0x56, 0xe3, 0x4b, 0x12, 0xaa, 0x17, 0x70, 0x15, 0x41, 0x78, - 0x30, 0xdd, 0xc8, 0xfd, 0x83, 0xdf, 0xea, 0x15, 0x80, 0x5e, 0xe4, 0x2e, - 0x24, 0x02, 0xb7, 0xf6, 0x02, 0xd0, 0x32, 0x29, 0x84, 0xee, 0x00, 0xaf, - 0xe7, 0xf2, 0xd1, 0x74, 0x2f, 0x17, 0xc8, 0x2a, 0x01, 0x72, 0x39, 0xf2, - 0xc8, 0xc0, 0x2f, 0x33, 0x72, 0x9c, 0x01, 0x22, 0x7e, 0x9e, 0xc9, 0x92, - 0x41, 0x35, 0x56, 0x56, 0x42, 0xad, 0x37, 0x5b, 0xe1, 0xb1, 0x40, 0x43, - 0xd2, 0xa2, 0x0b, 0xa6, 0xbf, 0x28, 0x96, 0xe7, 0x21, 0xf8, 0x9c, 0x9e, - 0xd8, 0xbb, 0xea, 0x6a, 0xae, 0xf5, 0x2e, 0x39, 0xcb, 0x91, 0x71, 0x99, - 0x01, 0x4c, 0x2b, 0x1a, 0x40, 0x08, 0x8f, 0x19, 0x29, 0x64, 0x96, 0x95, - 0x92, 0x13, 0xdc, 0xba, 0x22, 0xa5, 0x24, 0x05, 0x91, 0x29, 0x45, 0xb1, - 0x73, 0x69, 0xbe, 0xc6, 0x29, 0x10, 0x3e, 0xb9, 0xad, 0x86, 0xf0, 0xa8, - 0xcb, 0x58, 0xd9, 0x36, 0x18, 0x1b, 0x0e, 0xc2, 0x83, 0x92, 0x7c, 0x00, - 0xeb, 0xb0, 0x00, 0x2f, 0x05, 0x77, 0xd6, 0xe5, 0x17, 0x52, 0x55, 0x5c, - 0x00, 0x86, 0x4a, 0x6d, 0x48, 0x1f, 0x82, 0xd9, 0xfb, 0x31, 0x8d, 0x4a, - 0xb4, 0x21, 0xcb, 0x36, 0x2c, 0xd6, 0x3d, 0xc5, 0xcd, 0x13, 0xd6, 0xe5, - 0x25, 0x0a, 0xb7, 0xcb, 0x46, 0x7a, 0x26, 0xf4, 0x86, 0x64, 0x94, 0xfa, - 0xbd, 0x43, 0x3c, 0x24, 0xfb, 0xda, 0x14, 0xab, 0x0a, 0x20, 0x17, 0x6c, - 0xe8, 0x0e, 0xbe, 0x58, 0xa0, 0x3d, 0x1e, 0x8c, 0x2b, 0x9c, 0xe3, 0x81, - 0xab, 0xe2, 0x04, 0x26, 0xab, 0xed, 0xbf, 0xb3, 0xf1, 0x33, 0xba, 0xac, - 0xe4, 0x35, 0xe1, 0x2b, 0x08, 0x24, 0x6a, 0x7a, 0x77, 0xb4, 0xc8, 0xa4, - 0x9c, 0xff, 0xf1, 0x50, 0x80, 0x2f, 0xff, 0xfc, 0x21, 0x1a, 0xcf, 0x55, - 0xaf, 0xff, 0x86, 0xc0, 0x93, 0xb6, 0xc1, 0xac, 0x33, 0x16, 0x24, 0x08, - 0x00, 0x00, 0x00, 0x01, 0x28, 0xfd, 0x7a, 0xf5, 0xf5, 0xa0, 0x37, 0x36, - 0x13, 0x18, 0xab, 0x30, 0x71, 0x1a, 0xdd, 0x4e, 0x9f, 0xb7, 0x4d, 0x91, - 0x13, 0x5a, 0x4c, 0x5c, 0xe8, 0x36, 0x17, 0x0a, 0x71, 0x89, 0x35, 0x5d, - 0x0b, 0x44, 0xc2, 0x87, 0x32, 0x8d, 0x03, 0xae, 0x61, 0x5d, 0xf0, 0xb8, - 0x44, 0xc4, 0x02, 0x18, 0x72, 0x62, 0x01, 0x4d, 0x3d, 0xf9, 0x69, 0x90, - 0x21, 0x1b, 0xa6, 0xc8, 0x8c, 0x64, 0xa5, 0x32, 0x35, 0x3a, 0x2c, 0xb0, - 0x1d, 0xf8, 0xa8, 0x05, 0xb3, 0xdf, 0xad, 0xab, 0x55, 0xbf, 0x57, 0x9b, - 0x03, 0x4f, 0x3c, 0x29, 0x95, 0xe2, 0xc2, 0xf5, 0xfd, 0x17, 0xf1, 0xaa, - 0x84, 0xe0, 0x0b, 0xa5, 0xad, 0x8d, 0x08, 0x84, 0x44, 0xf8, 0x6f, 0xdc, - 0xf9, 0x93, 0x0a, 0x9c, 0x62, 0xd9, 0x4c, 0x05, 0x02, 0x41, 0xad, 0xa0, - 0xb5, 0x20, 0x4d, 0xc5, 0x8a, 0x04, 0xde, 0x70, 0xcf, 0xc5, 0xce, 0xa6, - 0x0a, 0x05, 0x5e, 0x8d, 0x8a, 0xc8, 0x12, 0x99, 0xcf, 0x7f, 0x31, 0x91, - 0x60, 0x63, 0x8e, 0x6b, 0x05, 0xad, 0x12, 0xc3, 0xb6, 0xdb, 0x51, 0x57, - 0x41, 0x82, 0x42, 0x21, 0x22, 0x26, 0x9a, 0x9d, 0x47, 0x09, 0x8c, 0x08, - 0x09, 0x15, 0xd0, 0xc0, 0xee, 0x01, 0x1a, 0xa2, 0x2a, 0xbf, 0x73, 0xdc, - 0xd8, 0xd5, 0x97, 0x06, 0x5e, 0x58, 0x8e, 0xeb, 0x4f, 0x73, 0x8b, 0x18, - 0xc1, 0xc8, 0xa2, 0x80, 0x9a, 0x12, 0x3f, 0x9d, 0x3e, 0xac, 0x1b, 0x34, - 0x79, 0x97, 0x21, 0x96, 0x6a, 0xe4, 0x90, 0x17, 0xd9, 0x34, 0x39, 0x29, - 0xae, 0x54, 0x5d, 0x61, 0x74, 0x04, 0x44, 0x9e, 0xc0, 0x62, 0x80, 0xa8, - 0x60, 0x13, 0x71, 0x15, 0x0b, 0x12, 0xc7, 0x27, 0x6d, 0x83, 0x58, 0x54, - 0x76, 0x12, 0x13, 0x8b, 0xde, 0x2a, 0x54, 0xa9, 0x51, 0x52, 0xa0, 0x00, - 0x55, 0x56, 0x4a, 0x6a, 0xae, 0x88, 0x06, 0x30, 0x73, 0x30, 0x09, 0x7c, - 0x48, 0x3f, 0xda, 0x7b, 0x66, 0xbd, 0xf2, 0x75, 0x9c, 0xd1, 0x45, 0xe9, - 0x0c, 0x46, 0x3d, 0x10, 0xcc, 0x64, 0xd1, 0x32, 0x36, 0x25, 0xd5, 0xf7, - 0xad, 0x12, 0xc7, 0xc6, 0xf0, 0x45, 0x3e, 0x61, 0xf7, 0x6a, 0x16, 0x89, - 0x6d, 0xf3, 0xeb, 0x1c, 0x75, 0xa3, 0xb4, 0x8d, 0x04, 0x84, 0x86, 0x35, - 0x2d, 0x4b, 0x8f, 0xfb, 0xae, 0xb3, 0x3e, 0x69, 0x45, 0x67, 0x4c, 0xa7, - 0x8f, 0xf2, 0xa2, 0xfa, 0xb1, 0x99, 0xda, 0x67, 0x12, 0x22, 0x92, 0x4f, - 0x33, 0x5e, 0x38, 0x00, 0xc8, 0x27, 0x41, 0xc8, 0x86, 0xec, 0x42, 0xbe, - 0xde, 0x4f, 0xa3, 0xe3, 0xdd, 0x43, 0x1b, 0x0c, 0x72, 0x09, 0x03, 0x80, - 0xff, 0xf1, 0x50, 0x80, 0x2f, 0x9f, 0xfc, 0x21, 0x1a, 0xce, 0xfe, 0x81, - 0x7f, 0x80, 0x00, 0x91, 0xb6, 0xb1, 0x6c, 0x48, 0x39, 0x32, 0xa5, 0x8a, - 0x00, 0x00, 0x00, 0x02, 0xf2, 0xf0, 0xf8, 0xf8, 0xf1, 0xd4, 0xf6, 0x0d, - 0xcb, 0xa1, 0x1e, 0x5e, 0x4c, 0x88, 0x76, 0xd4, 0xa0, 0x97, 0xc5, 0x28, - 0xb7, 0x56, 0xc4, 0x8e, 0x38, 0x97, 0x31, 0x39, 0xdf, 0xf8, 0xda, 0x61, - 0x8e, 0x34, 0x9a, 0xb8, 0x58, 0xce, 0x33, 0x51, 0xaa, 0x2f, 0x6a, 0xfe, - 0xed, 0x01, 0x1c, 0x0c, 0x9f, 0xb3, 0xe1, 0xb2, 0x2d, 0xb7, 0xda, 0xa6, - 0x3e, 0xce, 0x9a, 0x20, 0x4e, 0xeb, 0x09, 0x99, 0xc9, 0x08, 0xe3, 0x2f, - 0x45, 0x71, 0x3f, 0x68, 0x03, 0x1c, 0x18, 0xd0, 0xbf, 0xf4, 0xe6, 0xf5, - 0x53, 0xdb, 0xa1, 0x68, 0xd3, 0x5d, 0xca, 0xc3, 0xf6, 0x4f, 0xcb, 0x59, - 0xa9, 0x90, 0xc6, 0xf0, 0x81, 0x68, 0xc5, 0x39, 0x65, 0x09, 0xaf, 0x70, - 0xfd, 0xab, 0x9f, 0xb6, 0x72, 0x98, 0x5d, 0x22, 0x64, 0x58, 0x28, 0x5b, - 0xbd, 0xe0, 0xd2, 0x90, 0x12, 0xda, 0x04, 0x97, 0x2d, 0x63, 0x68, 0xf5, - 0xff, 0xd6, 0x35, 0x6a, 0x80, 0xb9, 0x24, 0xb1, 0x17, 0x4a, 0xa5, 0xf8, - 0xb3, 0x64, 0x05, 0x54, 0x58, 0x20, 0x99, 0x3a, 0xed, 0xf5, 0xf2, 0xc2, - 0x69, 0x21, 0xa0, 0x16, 0xba, 0xd2, 0x68, 0xfb, 0xaa, 0x2e, 0xa4, 0x41, - 0xb0, 0x89, 0x9c, 0xdf, 0x56, 0xb5, 0x43, 0x75, 0x42, 0x93, 0xa5, 0x27, - 0xbc, 0xaa, 0x45, 0x85, 0x44, 0x61, 0x07, 0x87, 0xbd, 0x1d, 0x80, 0xa0, - 0x60, 0xe0, 0x9d, 0x58, 0xce, 0xb5, 0xbb, 0x33, 0x90, 0xe5, 0x06, 0x09, - 0xa6, 0x45, 0x6c, 0xd6, 0xf9, 0xec, 0xb5, 0x65, 0xc0, 0x2d, 0x3f, 0x19, - 0xb9, 0xf9, 0xf6, 0xcc, 0xdd, 0xa5, 0x5f, 0x7d, 0x36, 0xc6, 0x99, 0x1b, - 0xa4, 0xa3, 0xde, 0x00, 0x00, 0x00, 0x1c, 0xea, 0x6f, 0x35, 0x8d, 0x55, - 0xd6, 0x4b, 0x06, 0x51, 0x9b, 0x3b, 0x52, 0xce, 0x48, 0x98, 0x0f, 0xcc, - 0xb5, 0x0a, 0x6e, 0x16, 0x90, 0xd0, 0x9a, 0x1c, 0x7a, 0x77, 0x8c, 0xb6, - 0x89, 0x53, 0x6a, 0xe3, 0xb5, 0x06, 0xa4, 0xea, 0xa9, 0x48, 0x10, 0xa4, - 0x6e, 0x63, 0x09, 0xa4, 0x2a, 0x2c, 0xda, 0x4e, 0xd3, 0x4e, 0x87, 0x52, - 0xf7, 0x55, 0x35, 0x90, 0x80, 0x3a, 0x89, 0x82, 0xc2, 0x71, 0xdb, 0x21, - 0xd0, 0x1e, 0x46, 0x60, 0x80, 0x60, 0xcd, 0x24, 0x94, 0x2c, 0x05, 0xd1, - 0xe8, 0x85, 0x9c, 0xdd, 0x9a, 0x76, 0x65, 0x3c, 0x58, 0x88, 0x70, 0x31, - 0x33, 0xc6, 0x51, 0x95, 0x6b, 0x7f, 0x5d, 0xfe, 0x07, 0xcb, 0x66, 0xcb, - 0x3d, 0x90, 0xa4, 0x67, 0x88, 0x21, 0x34, 0xb5, 0xab, 0xdc, 0xbd, 0x67, - 0xe7, 0x95, 0x0c, 0x56, 0x29, 0x20, 0x09, 0x70, 0xff, 0xf1, 0x50, 0x80, - 0x2c, 0xff, 0xfc, 0x21, 0x1a, 0xcd, 0x3b, 0xeb, 0x6f, 0x90, 0x80, 0x91, - 0xb6, 0xb0, 0xe5, 0xe6, 0x66, 0x33, 0x2f, 0x16, 0x65, 0x86, 0x5e, 0x5b, - 0x2d, 0x96, 0x19, 0x79, 0xe7, 0x77, 0xda, 0x7d, 0xf8, 0x71, 0x70, 0x49, - 0x13, 0xe4, 0x2f, 0x1a, 0xd5, 0xbd, 0x9d, 0x28, 0x41, 0x73, 0x7c, 0x86, - 0x15, 0x25, 0x9a, 0xd9, 0x1c, 0x79, 0x0d, 0xa8, 0xc6, 0xcb, 0x2d, 0x00, - 0xc3, 0xb2, 0x2b, 0xad, 0x51, 0x8b, 0xcd, 0x90, 0x47, 0x5f, 0x34, 0x4b, - 0x03, 0x6f, 0x99, 0xbc, 0xd6, 0x90, 0xd0, 0x22, 0xb6, 0x47, 0xe6, 0xaa, - 0xbb, 0xaf, 0xb6, 0x45, 0x61, 0x84, 0x6a, 0x4d, 0x56, 0x1d, 0x87, 0xe8, - 0xff, 0xe5, 0x78, 0x33, 0x86, 0x31, 0x96, 0x51, 0x79, 0x72, 0x75, 0xe6, - 0x4b, 0x8b, 0x4c, 0xe3, 0x79, 0xb2, 0xe7, 0x7d, 0x3f, 0xaa, 0xca, 0x15, - 0x78, 0x05, 0x40, 0x49, 0x74, 0x45, 0xe0, 0xf7, 0x2e, 0xe9, 0xf1, 0xac, - 0x97, 0x28, 0x15, 0x33, 0x22, 0x44, 0x28, 0x31, 0xc1, 0x25, 0x0c, 0xc4, - 0x82, 0xa8, 0xbc, 0x43, 0x7f, 0x02, 0xf8, 0xb9, 0x88, 0x1a, 0xb1, 0x69, - 0x6b, 0xd5, 0xca, 0xaf, 0x63, 0x29, 0x19, 0x01, 0x33, 0xee, 0xcb, 0xf6, - 0xd8, 0xa0, 0x8a, 0x22, 0xa9, 0xf3, 0x38, 0xeb, 0xd2, 0x38, 0x00, 0x80, - 0x6e, 0x02, 0x49, 0x09, 0x49, 0xb5, 0xea, 0xa5, 0x2c, 0xe2, 0x55, 0x59, - 0x5d, 0x96, 0x18, 0x88, 0xda, 0xd3, 0xb3, 0xab, 0xd8, 0x9c, 0x1e, 0x74, - 0x59, 0xd0, 0x53, 0x6e, 0x09, 0x35, 0xbd, 0xe6, 0xb2, 0xd9, 0x58, 0xb2, - 0x04, 0xa9, 0xc3, 0x22, 0xc7, 0x4e, 0x74, 0xf6, 0x34, 0xc2, 0x99, 0x53, - 0x6d, 0xc7, 0x3f, 0x7d, 0x56, 0x79, 0xed, 0xea, 0x76, 0x3b, 0x1f, 0x52, - 0xc8, 0xdb, 0x60, 0x72, 0xcf, 0x78, 0x00, 0x00, 0x00, 0x56, 0xe4, 0x93, - 0x15, 0x29, 0x75, 0x01, 0x9c, 0x82, 0x7c, 0x57, 0xda, 0xd1, 0xee, 0x7c, - 0x48, 0x80, 0x57, 0xfa, 0x68, 0x18, 0x23, 0x2d, 0x93, 0x33, 0x4d, 0xda, - 0x0e, 0x93, 0xc3, 0x98, 0xbe, 0x34, 0x35, 0x73, 0x74, 0x56, 0xec, 0xa6, - 0xa2, 0x63, 0x56, 0xcd, 0x4c, 0x0b, 0x45, 0x8a, 0x24, 0x14, 0x36, 0xbb, - 0x76, 0xad, 0xa3, 0xdb, 0xf5, 0x69, 0xa9, 0x9c, 0x63, 0x04, 0xe9, 0x79, - 0x0f, 0x03, 0xeb, 0x9b, 0x99, 0xee, 0xd3, 0x67, 0x1a, 0x79, 0xec, 0x15, - 0x9a, 0x70, 0x85, 0x61, 0x52, 0xe2, 0x7d, 0xe7, 0xe2, 0xfe, 0xbf, 0xa7, - 0x92, 0x73, 0x41, 0x95, 0x05, 0x08, 0x19, 0x5c, 0xde, 0x97, 0xd1, 0x3e, - 0x49, 0xb1, 0x24, 0x0c, 0x80, 0x01, 0xc0, 0xff, 0xf1, 0x50, 0x80, 0x31, - 0xdf, 0xfc, 0x21, 0x1a, 0xcf, 0xbd, 0xfb, 0xff, 0x90, 0x3f, 0x93, 0xb7, - 0x41, 0x6c, 0x28, 0x3b, 0x0b, 0xac, 0x84, 0xc5, 0x80, 0x80, 0x35, 0xb0, - 0x71, 0xc8, 0x0b, 0xcb, 0xcf, 0x6f, 0x52, 0xaf, 0x38, 0xe7, 0xf5, 0x91, - 0x7e, 0x44, 0x22, 0x4e, 0x55, 0x68, 0x5f, 0xaa, 0x6a, 0x2f, 0x32, 0xf2, - 0x0f, 0x32, 0xbc, 0x64, 0x41, 0x8f, 0x24, 0x51, 0x54, 0x90, 0x1b, 0x9a, - 0x02, 0x24, 0xaa, 0x22, 0x9d, 0x7f, 0xbe, 0xd1, 0x5f, 0xe9, 0x06, 0x5b, - 0x40, 0x83, 0xed, 0x60, 0x12, 0x33, 0x82, 0xe1, 0x8c, 0xd8, 0x4c, 0x0a, - 0x8c, 0xbc, 0x4a, 0x2f, 0x01, 0x10, 0x69, 0xe5, 0xad, 0x6b, 0x02, 0x65, - 0xad, 0x4a, 0x55, 0x70, 0x3a, 0x04, 0x05, 0x1c, 0x10, 0x53, 0x8e, 0x6b, - 0xf3, 0x25, 0x71, 0x8a, 0xdb, 0x14, 0xe3, 0x84, 0xe7, 0xb4, 0xdc, 0x65, - 0x80, 0x01, 0xb9, 0xd2, 0x4b, 0x20, 0xc2, 0x24, 0xac, 0x2d, 0x9e, 0x5d, - 0x5f, 0xfa, 0xf9, 0x79, 0x95, 0x79, 0xc9, 0x7a, 0xf2, 0x18, 0x00, 0x32, - 0xb8, 0xda, 0x28, 0xbc, 0xb4, 0x45, 0xd5, 0xce, 0x5a, 0x99, 0xe1, 0x9e, - 0x8c, 0x5c, 0x90, 0xfb, 0x5a, 0x02, 0x2b, 0x01, 0x1b, 0xe1, 0x02, 0x41, - 0x98, 0x3d, 0x00, 0x00, 0xdd, 0x84, 0x45, 0xc5, 0x88, 0x82, 0xd6, 0xcf, - 0xac, 0x85, 0x98, 0x26, 0x23, 0xd7, 0xfa, 0x59, 0xec, 0xc9, 0xb5, 0xd8, - 0xb7, 0x75, 0xaa, 0xf1, 0x60, 0xbe, 0x28, 0x63, 0x6a, 0x82, 0xbb, 0x78, - 0xd9, 0x4d, 0x42, 0x32, 0xf5, 0x5e, 0x46, 0xd6, 0x30, 0x54, 0xb0, 0x91, - 0x26, 0x35, 0x23, 0x19, 0xd7, 0x23, 0x6e, 0xde, 0x0d, 0x94, 0xd7, 0xcb, - 0x8f, 0x45, 0x6b, 0x24, 0xa2, 0x14, 0x8c, 0x9a, 0x55, 0x7b, 0xec, 0xd5, - 0xd5, 0x14, 0x25, 0x69, 0x59, 0xd8, 0x49, 0xcf, 0x02, 0xe5, 0x21, 0x88, - 0xce, 0xfc, 0x7a, 0x67, 0x6d, 0x28, 0xc4, 0xd5, 0xa2, 0x79, 0x3b, 0x6b, - 0x1a, 0xc2, 0x83, 0xb0, 0xa9, 0x3c, 0x88, 0x2e, 0x48, 0x00, 0x00, 0x02, - 0xf2, 0xea, 0x77, 0xae, 0xb5, 0xbd, 0x66, 0xb1, 0x2a, 0xc1, 0x98, 0xce, - 0x0a, 0x94, 0x1f, 0xad, 0xb7, 0xc7, 0x74, 0xb0, 0x03, 0x21, 0xc1, 0xe5, - 0x78, 0xef, 0x31, 0x27, 0x6a, 0xf0, 0x46, 0xd9, 0x5c, 0x88, 0x17, 0x8b, - 0xf7, 0x42, 0x37, 0x43, 0x2f, 0xa6, 0xf8, 0x24, 0xfb, 0x43, 0x42, 0xb7, - 0xa3, 0xd7, 0x4d, 0x95, 0x6d, 0xf7, 0x30, 0xb4, 0x1d, 0x2e, 0x4c, 0x63, - 0xcf, 0xbf, 0xd8, 0x7e, 0x01, 0x46, 0x60, 0x0e, 0xd7, 0x38, 0x7c, 0x28, - 0x08, 0xb2, 0x4c, 0x07, 0x1c, 0x7f, 0x35, 0xe7, 0x77, 0x90, 0x30, 0xb6, - 0x7a, 0x83, 0x3a, 0x80, 0xa4, 0xce, 0xd7, 0x72, 0x47, 0xed, 0xfa, 0x79, - 0x22, 0xab, 0x61, 0x55, 0x08, 0x58, 0x07, 0xe8, 0x8a, 0xf7, 0x56, 0xcb, - 0xcb, 0x43, 0xc7, 0x2d, 0x87, 0x56, 0x7c, 0x6d, 0xee, 0xff, 0xf1, 0x50, - 0x80, 0x2e, 0x7f, 0xfc, 0x21, 0x1a, 0xcf, 0xe8, 0xbf, 0xef, 0x81, 0x40, - 0x96, 0xb6, 0xc1, 0xac, 0x28, 0x3b, 0x0c, 0x8d, 0x56, 0xc3, 0x81, 0x03, - 0x4d, 0x80, 0x00, 0x0b, 0xce, 0xbb, 0xbc, 0xf3, 0xe2, 0x57, 0xc7, 0x4d, - 0x71, 0xa0, 0xf5, 0xd3, 0x5b, 0x33, 0xce, 0x1c, 0x3c, 0x74, 0x0c, 0x3e, - 0x32, 0xc4, 0xc6, 0x52, 0xca, 0x31, 0xd6, 0x1b, 0x28, 0x10, 0xa2, 0x18, - 0x41, 0x04, 0x4e, 0x5a, 0x7c, 0x8e, 0x29, 0x44, 0xc0, 0x12, 0x79, 0xb0, - 0xcb, 0x32, 0xee, 0xd5, 0x20, 0x12, 0xc9, 0x6d, 0x37, 0x1a, 0xea, 0x20, - 0xcb, 0x50, 0x35, 0x25, 0x24, 0xe3, 0x28, 0xc0, 0x58, 0xd7, 0x0b, 0x55, - 0x9e, 0xa7, 0x18, 0xe5, 0x09, 0xa4, 0x95, 0x7b, 0xbe, 0x53, 0x62, 0x60, - 0xb2, 0x4d, 0xa4, 0x70, 0x3b, 0xcb, 0x3b, 0xbc, 0xf9, 0x60, 0x51, 0x40, - 0xba, 0xa5, 0xaa, 0x01, 0x66, 0x15, 0xdd, 0x7f, 0xd4, 0x71, 0x20, 0xcd, - 0x34, 0x2f, 0x81, 0x68, 0xb5, 0x00, 0x57, 0x08, 0x32, 0xa9, 0xc2, 0x74, - 0x0d, 0x70, 0xbd, 0x70, 0x33, 0x10, 0x73, 0xff, 0xde, 0xd2, 0x97, 0x20, - 0xe4, 0x55, 0x21, 0x69, 0x50, 0x57, 0xdf, 0x30, 0x82, 0x43, 0xb3, 0xb5, - 0x5b, 0x61, 0x33, 0x09, 0x80, 0x8f, 0x19, 0x2d, 0x74, 0x44, 0xe4, 0x12, - 0xb1, 0x33, 0x24, 0xfa, 0x72, 0xdd, 0xc2, 0x36, 0x1c, 0x35, 0x35, 0xdb, - 0x07, 0x1b, 0xcd, 0xab, 0x70, 0xf2, 0xc4, 0xc3, 0x45, 0x80, 0x28, 0xf4, - 0x5c, 0xda, 0x74, 0xb8, 0x4e, 0x49, 0x7c, 0x89, 0xa5, 0xa9, 0xb2, 0x85, - 0xae, 0x7f, 0x36, 0xae, 0x75, 0x47, 0xe6, 0x77, 0xe0, 0xac, 0xe9, 0x73, - 0xdd, 0x34, 0x77, 0x37, 0xcb, 0x8c, 0x0a, 0xbb, 0xed, 0xaf, 0x12, 0x2c, - 0x71, 0x45, 0x3e, 0xe2, 0x8c, 0xad, 0x09, 0x13, 0x98, 0xcf, 0x66, 0x4d, - 0x92, 0x04, 0x53, 0x42, 0x5a, 0x19, 0x63, 0x62, 0x58, 0x50, 0x76, 0x17, - 0x27, 0xbc, 0x1a, 0xe6, 0x51, 0x2a, 0x00, 0x02, 0xe8, 0xae, 0x75, 0x32, - 0xf2, 0x10, 0x1c, 0x97, 0x73, 0x5c, 0x2f, 0x10, 0xae, 0xa1, 0x5b, 0x8e, - 0x9c, 0xe0, 0x1a, 0xc1, 0x9c, 0xa6, 0xd0, 0x63, 0x49, 0xc6, 0x49, 0x52, - 0x88, 0x51, 0xfa, 0x59, 0x39, 0x3b, 0x82, 0x65, 0x43, 0xf0, 0xa3, 0x53, - 0xdf, 0xcf, 0x96, 0x81, 0x51, 0xba, 0xa1, 0x1e, 0x32, 0x79, 0xc4, 0xa7, - 0x4b, 0x8c, 0x9c, 0xf9, 0x12, 0xcd, 0x01, 0xd3, 0x85, 0x86, 0x99, 0xa2, - 0x6c, 0xab, 0x14, 0x21, 0x61, 0x6e, 0xd8, 0xa5, 0xbd, 0x59, 0x3c, 0xe6, - 0xae, 0x05, 0x98, 0x66, 0xa4, 0x0b, 0xc8, 0x11, 0x00, 0x93, 0x7e, 0x7f, - 0x97, 0x8c, 0xaa, 0xe5, 0x25, 0x50, 0x2c, 0x1c, 0xff, 0xf1, 0x50, 0x80, - 0x2e, 0x7f, 0xfc, 0x21, 0x1a, 0xcd, 0xdb, 0xef, 0xff, 0x8c, 0x30, 0x97, - 0xb2, 0x32, 0x2d, 0x12, 0xb5, 0x43, 0x10, 0x1a, 0xca, 0x00, 0x03, 0x5b, - 0x4a, 0x75, 0x9b, 0x95, 0xd7, 0x77, 0x9f, 0x7a, 0xe2, 0xbc, 0xf0, 0x0d, - 0x7d, 0xf8, 0x6a, 0x4d, 0xb3, 0x24, 0x55, 0xdd, 0x09, 0xf1, 0xce, 0x34, - 0x1d, 0xf3, 0x6c, 0xe6, 0x1e, 0x34, 0x4e, 0x1f, 0xa4, 0xda, 0xb8, 0x6c, - 0x50, 0xc0, 0x05, 0x58, 0xe8, 0xa5, 0xfa, 0xd7, 0xf1, 0x0c, 0x29, 0xd8, - 0xf2, 0x02, 0x9c, 0x08, 0x51, 0xc3, 0x0c, 0x34, 0x64, 0x17, 0xf0, 0x35, - 0x99, 0x48, 0x07, 0x01, 0x8d, 0x53, 0x94, 0xc1, 0x51, 0x0d, 0x48, 0xa1, - 0xc7, 0xad, 0x28, 0x8d, 0x3c, 0x1c, 0x70, 0x6b, 0x09, 0x91, 0x75, 0x97, - 0x91, 0xfc, 0xcb, 0x21, 0x53, 0x34, 0x56, 0x89, 0x6a, 0xce, 0xd9, 0xb0, - 0xc4, 0xc2, 0xb4, 0xbf, 0xed, 0xf4, 0xa4, 0xb1, 0x91, 0x18, 0x48, 0xce, - 0xc1, 0x59, 0xaa, 0x97, 0xa5, 0x83, 0x18, 0xaa, 0xbc, 0x95, 0x14, 0x41, - 0xa1, 0x95, 0xaa, 0xab, 0x28, 0xa8, 0xbf, 0x98, 0xf8, 0x22, 0xd8, 0x48, - 0xdc, 0xc0, 0x73, 0xc8, 0x54, 0x0b, 0x9f, 0xf0, 0x0c, 0x83, 0x84, 0xb5, - 0x4e, 0x33, 0x04, 0x5c, 0x86, 0xfa, 0x00, 0x2e, 0x6b, 0x73, 0x28, 0x6a, - 0x97, 0x4d, 0x32, 0x6b, 0xe1, 0xca, 0xb3, 0x98, 0x49, 0x51, 0xaf, 0xaa, - 0xb1, 0x9c, 0x74, 0x74, 0xad, 0x0b, 0xdd, 0x77, 0xdf, 0x57, 0xee, 0xe5, - 0x42, 0xc5, 0x30, 0xba, 0x63, 0x35, 0x35, 0x5b, 0xa9, 0xc8, 0x0b, 0xb6, - 0x64, 0xcb, 0xd3, 0x3a, 0xa8, 0x5e, 0x68, 0x44, 0x16, 0x98, 0x18, 0x49, - 0x29, 0xbe, 0xd9, 0x06, 0xce, 0xb8, 0xd9, 0xed, 0xef, 0xd9, 0xe5, 0xfe, - 0xfc, 0xbf, 0x88, 0xc7, 0xed, 0xab, 0x0a, 0x64, 0x6e, 0xd0, 0xf7, 0xcb, - 0x84, 0xbd, 0x89, 0x95, 0x61, 0x81, 0xa8, 0xec, 0x28, 0x3b, 0x0c, 0x93, - 0xde, 0x0f, 0xaf, 0x7a, 0x00, 0x03, 0x5b, 0x4a, 0x6b, 0x9c, 0xaa, 0xbe, - 0xb9, 0x4c, 0x24, 0x00, 0x36, 0xe1, 0xeb, 0xc9, 0xc7, 0x87, 0xbe, 0x53, - 0xfa, 0x0a, 0x85, 0x43, 0xee, 0xca, 0xb0, 0xa4, 0x37, 0x84, 0xb6, 0xdf, - 0x2e, 0x37, 0x31, 0x93, 0x87, 0xb1, 0x94, 0x45, 0x09, 0xce, 0x3f, 0x29, - 0x47, 0xa0, 0x59, 0xa6, 0x14, 0x3d, 0x7f, 0x87, 0x0a, 0xc5, 0xba, 0xef, - 0x39, 0x6a, 0x36, 0x95, 0xce, 0x59, 0x45, 0x8b, 0xc4, 0x60, 0xd0, 0xc4, - 0x5d, 0xe7, 0x43, 0x50, 0xb2, 0x20, 0x44, 0x83, 0xbf, 0x3f, 0x94, 0xf0, - 0x01, 0x48, 0x30, 0x8a, 0x1a, 0x02, 0x85, 0x41, 0x82, 0xd1, 0xd0, 0x7c, - 0x77, 0x9a, 0x2e, 0x85, 0xaa, 0x00, 0x0e, 0xff, 0xf1, 0x50, 0x80, 0x30, - 0xbf, 0xfc, 0x21, 0x1a, 0xcf, 0x8f, 0xb7, 0x73, 0xb8, 0x10, 0x97, 0xb2, - 0x42, 0x2c, 0x30, 0x6b, 0x0a, 0x0e, 0xc3, 0x29, 0x32, 0x31, 0x60, 0x21, - 0x53, 0x5b, 0x0a, 0x8a, 0x85, 0x45, 0xed, 0x12, 0xba, 0xf0, 0x79, 0xf1, - 0xad, 0xfe, 0xba, 0xb8, 0xe8, 0x33, 0xe1, 0xff, 0x6d, 0xd2, 0xea, 0x9f, - 0xb0, 0x8b, 0x2f, 0xd6, 0x36, 0x02, 0x68, 0x99, 0xf9, 0xd8, 0x23, 0x5e, - 0x8d, 0x20, 0xe0, 0x5b, 0x44, 0x72, 0x16, 0xbf, 0xe4, 0xc9, 0x45, 0xe0, - 0xd3, 0xf1, 0xa3, 0x14, 0xd4, 0x16, 0xb3, 0x84, 0x13, 0x91, 0xd8, 0x38, - 0x97, 0x18, 0x1b, 0x20, 0xac, 0xe7, 0x12, 0xc0, 0x21, 0x2f, 0x7e, 0x77, - 0x47, 0x8a, 0x1d, 0x3a, 0xbc, 0x40, 0x30, 0x7d, 0x3d, 0x23, 0x4f, 0xc2, - 0xde, 0xa8, 0x52, 0xd1, 0x46, 0x12, 0x52, 0x2b, 0xf9, 0x78, 0x0c, 0x30, - 0xf5, 0x91, 0x1c, 0x8b, 0x0a, 0x94, 0xa6, 0xf2, 0xa5, 0xf1, 0xfe, 0x57, - 0xfb, 0xd6, 0xdb, 0x27, 0x36, 0x43, 0x74, 0x8c, 0x6a, 0xc4, 0x81, 0xad, - 0xa2, 0x02, 0xea, 0xab, 0x45, 0x6a, 0xd6, 0xc5, 0x59, 0x6a, 0xd9, 0x77, - 0x9f, 0x47, 0xef, 0x54, 0x00, 0x6a, 0x48, 0xc7, 0x00, 0x01, 0x3d, 0x6a, - 0xd7, 0x61, 0xba, 0xf1, 0x6e, 0xfd, 0xc9, 0xd7, 0xe6, 0x5d, 0x7f, 0xe9, - 0x41, 0xf1, 0x3d, 0x8d, 0xe5, 0x6b, 0x7b, 0x75, 0x75, 0xf6, 0xd3, 0x98, - 0x8b, 0x23, 0xa8, 0x60, 0xeb, 0x14, 0xcb, 0x91, 0xf3, 0x19, 0x68, 0xd3, - 0xa1, 0x1c, 0xe5, 0x6b, 0x9b, 0xbc, 0x22, 0x4d, 0x0d, 0x57, 0x0c, 0xbc, - 0xa5, 0xe4, 0x49, 0x12, 0xdd, 0xdd, 0xf1, 0x34, 0xa9, 0xe7, 0xbf, 0xc0, - 0xb7, 0xf0, 0xbe, 0xd7, 0x11, 0x6c, 0x2f, 0xc7, 0x1b, 0xbb, 0x25, 0xc8, - 0x67, 0xab, 0x5c, 0x63, 0xf5, 0xd7, 0x3f, 0x17, 0x5c, 0xe0, 0xe8, 0x03, - 0x6f, 0xdf, 0xa8, 0x41, 0xfa, 0xab, 0x96, 0x81, 0x28, 0x55, 0x52, 0xf4, - 0x66, 0x35, 0x86, 0x0d, 0x61, 0x41, 0xc9, 0x78, 0x86, 0x1f, 0x50, 0x1e, - 0x7c, 0x00, 0x01, 0x79, 0x78, 0x95, 0x27, 0x72, 0x6a, 0xb9, 0xb8, 0x54, - 0x11, 0x07, 0x84, 0x72, 0x0d, 0x45, 0x13, 0x19, 0xe3, 0x71, 0x94, 0xd9, - 0x94, 0x21, 0x2c, 0xeb, 0x72, 0x5c, 0x12, 0xc7, 0x7c, 0x61, 0xf2, 0xbb, - 0xe5, 0xae, 0x1f, 0x40, 0x52, 0xd1, 0x98, 0xc5, 0x63, 0x84, 0xef, 0xed, - 0x67, 0xef, 0x64, 0xb9, 0x6b, 0x27, 0x48, 0x75, 0x2c, 0xf1, 0xd6, 0x23, - 0x9d, 0xd6, 0x6a, 0x34, 0x0b, 0xdd, 0x26, 0x9a, 0x28, 0x0d, 0x89, 0xc7, - 0x36, 0xe0, 0x98, 0xaf, 0xb4, 0xbe, 0xc3, 0x47, 0xf4, 0x1e, 0xfe, 0x31, - 0xc5, 0x31, 0x94, 0xa5, 0x34, 0x9a, 0xa5, 0x24, 0xcc, 0x8a, 0xec, 0xbe, - 0x4f, 0x41, 0x02, 0xef, 0x3c, 0x42, 0x40, 0x96, 0xc7, 0xc3, 0x17, 0xe0, - 0xff, 0xf1, 0x50, 0x80, 0x2e, 0x3f, 0xfc, 0x21, 0x1a, 0xca, 0x6b, 0xdb, - 0xff, 0x80, 0x40, 0x96, 0xb1, 0x42, 0xad, 0x13, 0x16, 0x24, 0x08, 0x00, - 0x03, 0x5b, 0x17, 0x80, 0x75, 0xdd, 0xe5, 0xe4, 0xaf, 0xde, 0x38, 0xd5, - 0x89, 0xcc, 0xa6, 0x3f, 0x5d, 0xd7, 0xfd, 0x3f, 0x3f, 0xcd, 0xfe, 0x3d, - 0xbb, 0x02, 0xe3, 0x7e, 0x1b, 0xc0, 0x82, 0xd9, 0xb8, 0xc9, 0x0f, 0x03, - 0x34, 0x52, 0xc6, 0x88, 0xfc, 0xac, 0x4a, 0x47, 0x51, 0x43, 0x01, 0x5b, - 0x80, 0x40, 0x16, 0x1e, 0xac, 0x22, 0x23, 0x07, 0x15, 0xb0, 0x6a, 0x41, - 0x85, 0x42, 0x02, 0x00, 0x1a, 0x29, 0xc1, 0x01, 0x1c, 0x8f, 0x5b, 0xb3, - 0x4c, 0x1c, 0x34, 0xb3, 0x37, 0x61, 0x9d, 0xe7, 0xb1, 0x98, 0xbc, 0xb6, - 0x5d, 0xa9, 0x22, 0x3c, 0x47, 0xcd, 0xe2, 0x83, 0x5c, 0x27, 0x5a, 0x24, - 0x89, 0x91, 0x76, 0x55, 0xc7, 0xf5, 0xbd, 0xe5, 0x24, 0x90, 0xbd, 0x81, - 0x99, 0x60, 0x2f, 0x3d, 0x71, 0x43, 0x09, 0x54, 0xc1, 0x54, 0xb3, 0x28, - 0xd5, 0x59, 0xa1, 0xfb, 0x4e, 0xbc, 0x55, 0x12, 0x2f, 0x5e, 0x02, 0xe4, - 0x08, 0x4e, 0x7a, 0xfe, 0x67, 0x60, 0xac, 0xa8, 0x61, 0x71, 0x74, 0x5c, - 0x2c, 0x84, 0xcb, 0x1e, 0x04, 0xac, 0x20, 0x31, 0x91, 0x52, 0xb5, 0xd9, - 0x77, 0x9e, 0xbe, 0xc2, 0x6a, 0x65, 0x98, 0x8b, 0x01, 0x36, 0x3a, 0x1c, - 0x29, 0xbe, 0x05, 0x53, 0x2c, 0xad, 0x3a, 0xcd, 0x2f, 0xa8, 0x2c, 0x79, - 0x1e, 0xfb, 0x70, 0x50, 0x46, 0x9f, 0x53, 0xd3, 0x4c, 0x54, 0xd6, 0xdd, - 0x6f, 0x83, 0x6c, 0xf2, 0xef, 0x4d, 0x35, 0x5b, 0xa2, 0x92, 0xee, 0x7a, - 0x6b, 0x97, 0x4d, 0x51, 0x9a, 0xc9, 0x30, 0x48, 0x42, 0x61, 0x76, 0xcc, - 0x23, 0x8a, 0xb8, 0x49, 0x23, 0x2b, 0x59, 0x55, 0x0b, 0x09, 0x29, 0x77, - 0x6c, 0x2a, 0x55, 0xb6, 0x09, 0x6b, 0x6c, 0x1a, 0xc3, 0x28, 0xf7, 0x80, - 0x00, 0x02, 0xf0, 0x15, 0x55, 0xaf, 0x9e, 0x25, 0xe5, 0xa6, 0xee, 0x05, - 0x3a, 0xec, 0x15, 0xf2, 0xcd, 0x27, 0xb0, 0xf5, 0x0f, 0x74, 0xbc, 0x00, - 0x08, 0xa2, 0x2a, 0xf1, 0x82, 0x42, 0x3c, 0x38, 0xc3, 0x21, 0x56, 0x1d, - 0x29, 0x48, 0x19, 0x5d, 0x50, 0x89, 0xdf, 0xc4, 0x7c, 0xae, 0x68, 0x90, - 0x91, 0x28, 0xcb, 0x5a, 0x30, 0x4e, 0x24, 0x2a, 0x5e, 0x0f, 0xc4, 0x07, - 0x51, 0xc2, 0x62, 0x84, 0x07, 0x23, 0xf4, 0x99, 0x6c, 0x87, 0xca, 0x2c, - 0xdc, 0xd1, 0x56, 0x61, 0x2c, 0x99, 0xd0, 0x33, 0xaa, 0xb8, 0xe7, 0x7f, - 0xe4, 0xef, 0x83, 0x1c, 0x64, 0x4e, 0x94, 0x0a, 0x40, 0x24, 0x8c, 0xbd, - 0xd3, 0xe4, 0xf2, 0x16, 0x17, 0x90, 0x5c, 0x83, 0x80, 0xff, 0xf1, 0x50, - 0x80, 0x2e, 0xff, 0xfc, 0x21, 0x1a, 0xcf, 0xe3, 0xbb, 0xff, 0x94, 0x00, - 0x94, 0xb6, 0xb1, 0x6c, 0x4e, 0x75, 0x31, 0x04, 0xc2, 0x42, 0x61, 0xc0, - 0xc0, 0xeb, 0xb0, 0x00, 0x00, 0x79, 0xf0, 0x7b, 0x7a, 0xd6, 0x7c, 0x79, - 0xdf, 0x9a, 0xf2, 0x27, 0x32, 0x89, 0xb2, 0xc6, 0xaa, 0xce, 0x75, 0xc9, - 0x1a, 0xfd, 0x59, 0xf8, 0xb6, 0x2c, 0xd1, 0x21, 0x8d, 0x96, 0xca, 0x18, - 0xfd, 0xde, 0xc4, 0xd4, 0x23, 0xd2, 0xca, 0x67, 0xbd, 0xae, 0xab, 0x16, - 0xca, 0xd2, 0x68, 0x7a, 0x12, 0xd3, 0x68, 0x02, 0xbb, 0x19, 0xcd, 0xe8, - 0x8f, 0xea, 0xe8, 0xb8, 0x03, 0xe0, 0xa9, 0x50, 0x12, 0x11, 0x40, 0xc4, - 0x15, 0x66, 0x3c, 0x73, 0xc2, 0xe2, 0x8c, 0x2e, 0x35, 0xf1, 0x46, 0x19, - 0x42, 0x6d, 0xbe, 0x56, 0xb0, 0xaa, 0xeb, 0xbe, 0x3f, 0x6e, 0x44, 0x17, - 0x0c, 0x71, 0x00, 0x2e, 0xb2, 0xaa, 0x6e, 0xf5, 0x5a, 0x18, 0xc5, 0x5c, - 0x10, 0x76, 0x82, 0x41, 0x22, 0xef, 0x88, 0x92, 0x2b, 0x37, 0x1a, 0x91, - 0x8d, 0xdd, 0xde, 0xb1, 0xb0, 0xaf, 0xdb, 0x9f, 0x67, 0x00, 0x8a, 0x3b, - 0xc2, 0x50, 0x01, 0x0e, 0xfc, 0x68, 0xa0, 0x56, 0xdb, 0xc8, 0x3f, 0x4e, - 0x63, 0x06, 0xab, 0xf9, 0x2a, 0x22, 0x52, 0x1f, 0x4e, 0x0a, 0x80, 0x4d, - 0xdb, 0x0c, 0x55, 0x80, 0xb6, 0x81, 0x01, 0x12, 0x05, 0x17, 0x92, 0x81, - 0xaa, 0xc8, 0xcc, 0x09, 0x86, 0x3c, 0xce, 0x62, 0xee, 0x96, 0xa0, 0x04, - 0x31, 0x62, 0x14, 0x16, 0x16, 0x2b, 0x9e, 0xdd, 0x60, 0xd2, 0xb1, 0x68, - 0x24, 0xfb, 0xc2, 0x33, 0x99, 0x53, 0x25, 0x81, 0x30, 0x9d, 0xd0, 0x3c, - 0xe7, 0xca, 0x18, 0x50, 0x67, 0x18, 0x4c, 0x19, 0xda, 0xb4, 0x2f, 0x93, - 0x12, 0x76, 0x2d, 0xea, 0xe9, 0x4a, 0x76, 0x16, 0x29, 0xbf, 0x29, 0x0c, - 0xb1, 0xb1, 0x2c, 0x28, 0x27, 0x37, 0xa0, 0x83, 0xe6, 0x02, 0x88, 0x54, - 0x2a, 0x00, 0x19, 0xc7, 0xae, 0x39, 0xe3, 0xbd, 0x7b, 0x41, 0x56, 0x17, - 0xe3, 0xae, 0xf4, 0x52, 0xe5, 0x11, 0xfa, 0xc6, 0x58, 0x16, 0x52, 0x54, - 0x61, 0xb1, 0xf0, 0x92, 0x44, 0x91, 0xba, 0x3c, 0xd8, 0x29, 0x1d, 0x16, - 0x0d, 0x76, 0xe9, 0xd8, 0xff, 0xfe, 0x29, 0xab, 0xd9, 0xdf, 0x85, 0x42, - 0x88, 0x39, 0xff, 0x0d, 0x84, 0x36, 0xbe, 0xd7, 0x03, 0xa1, 0x9e, 0x88, - 0x04, 0xdf, 0x3f, 0x07, 0xed, 0x4a, 0xc0, 0x06, 0x8c, 0xfb, 0x68, 0xc3, - 0xf9, 0x38, 0x34, 0xd6, 0x64, 0xd3, 0x2a, 0xe1, 0xff, 0xb7, 0xf0, 0xd5, - 0x94, 0xd4, 0xcc, 0xc2, 0x31, 0x51, 0x31, 0x20, 0xb4, 0x56, 0x7f, 0x65, - 0x4a, 0xba, 0x0b, 0xc2, 0x40, 0x06, 0xe6, 0x86, 0x29, 0x70, 0xee, 0xf0, - 0xff, 0xf1, 0x50, 0x80, 0x2f, 0xff, 0xfc, 0x21, 0x1a, 0xce, 0x5d, 0x8d, - 0x7f, 0x90, 0x00, 0x91, 0xb6, 0xb1, 0x6c, 0x48, 0x37, 0x22, 0xad, 0x8a, - 0x00, 0x00, 0x01, 0x28, 0x5e, 0x71, 0xcf, 0xd7, 0xc9, 0xe7, 0xa7, 0x55, - 0xf0, 0x31, 0xb6, 0x8a, 0x49, 0x11, 0x96, 0x91, 0x60, 0xdd, 0x99, 0xfe, - 0x3f, 0x06, 0x81, 0xa0, 0xf3, 0xfd, 0x2a, 0x95, 0x56, 0x55, 0x7f, 0x31, - 0x21, 0x62, 0x40, 0x53, 0xaa, 0x90, 0x5e, 0xb3, 0x98, 0x63, 0xf5, 0x1c, - 0xef, 0x24, 0x96, 0x7f, 0x20, 0x40, 0x62, 0xc6, 0x2b, 0xea, 0xca, 0x52, - 0x9f, 0x68, 0x7c, 0x20, 0xe5, 0x3f, 0x38, 0x4e, 0x2f, 0xc8, 0x50, 0x0e, - 0x74, 0x11, 0x54, 0x40, 0xc7, 0xef, 0xa2, 0x46, 0xd2, 0x94, 0x63, 0x03, - 0x92, 0xd6, 0xe4, 0xeb, 0x90, 0xad, 0x13, 0x26, 0xb0, 0x4f, 0xd8, 0xfe, - 0xe7, 0xb3, 0xce, 0xa6, 0xd6, 0x54, 0x6e, 0x92, 0x91, 0x99, 0x79, 0xcc, - 0xc4, 0xbe, 0x1f, 0x67, 0xab, 0xb1, 0x55, 0x13, 0x26, 0x77, 0x4a, 0x52, - 0x42, 0x29, 0x6e, 0xfc, 0x55, 0xe5, 0x42, 0x17, 0x94, 0x08, 0xd2, 0xdb, - 0xb3, 0x27, 0x6f, 0xed, 0xfd, 0x1c, 0x00, 0x36, 0xa9, 0xaa, 0x8d, 0x53, - 0x62, 0x46, 0x61, 0xa2, 0x81, 0x79, 0xde, 0x31, 0x15, 0xd7, 0xac, 0x23, - 0x15, 0x73, 0x96, 0x63, 0x14, 0x00, 0x11, 0x7b, 0x20, 0x99, 0x33, 0x9e, - 0xa8, 0x96, 0x66, 0x18, 0xa0, 0x0b, 0x2e, 0x97, 0x11, 0x6d, 0xfc, 0x8c, - 0xc2, 0xd7, 0x36, 0x42, 0x3b, 0xa4, 0x74, 0x18, 0x48, 0x68, 0x07, 0x03, - 0x40, 0xa2, 0xfe, 0x77, 0x83, 0x27, 0x85, 0xea, 0x9b, 0xaf, 0xbd, 0xec, - 0x5d, 0x94, 0x4c, 0x46, 0x2a, 0x84, 0xe3, 0x76, 0x2b, 0x37, 0xb1, 0x99, - 0x88, 0x1a, 0x68, 0x3a, 0x38, 0x84, 0x7d, 0x3e, 0xfe, 0x6e, 0x15, 0xc9, - 0xf1, 0xea, 0xf7, 0x48, 0xdb, 0x60, 0xd6, 0x19, 0x47, 0xbc, 0xa4, 0xa8, - 0x54, 0x51, 0x15, 0x2a, 0x09, 0x42, 0xa6, 0x49, 0x7b, 0xdc, 0xab, 0x99, - 0xa0, 0x6d, 0x1b, 0xe9, 0xf9, 0x22, 0x11, 0x62, 0x06, 0xd6, 0x0e, 0x4f, - 0x14, 0xe4, 0x53, 0xd1, 0x70, 0x09, 0xa0, 0xe8, 0x1c, 0xf3, 0x1a, 0xa9, - 0x65, 0xa0, 0x75, 0xa4, 0x4c, 0xac, 0xa9, 0x24, 0x58, 0xe4, 0x25, 0xb6, - 0xe2, 0x57, 0x53, 0x63, 0x1b, 0x7a, 0x1d, 0x1e, 0xbe, 0x43, 0xeb, 0x8d, - 0xf3, 0xbe, 0x98, 0x4c, 0x8f, 0x48, 0x02, 0x31, 0xd9, 0x01, 0x8f, 0x02, - 0xd9, 0x25, 0x0d, 0xdc, 0xde, 0xcc, 0x42, 0xc6, 0xd5, 0x9d, 0x12, 0x71, - 0xc0, 0x26, 0x66, 0xeb, 0x38, 0xdb, 0x8b, 0x2d, 0x7f, 0xcd, 0x7f, 0x48, - 0xf9, 0xd6, 0x4d, 0x97, 0x53, 0x39, 0xd4, 0x6a, 0x67, 0x09, 0xb8, 0x80, - 0x41, 0x5d, 0x0e, 0x97, 0x7b, 0xa2, 0x01, 0x9c, 0x02, 0xc0, 0xe0, 0xff, - 0xf1, 0x50, 0x80, 0x30, 0x7f, 0xfc, 0x21, 0x1a, 0xcb, 0xe4, 0xfe, 0xf3, - 0x90, 0xc0, 0x92, 0xba, 0x20, 0xec, 0x2a, 0xd6, 0x2c, 0x05, 0x02, 0x92, - 0x90, 0x0a, 0x85, 0xed, 0xac, 0x35, 0xcf, 0xb6, 0xfa, 0xf9, 0xba, 0xfb, - 0xf1, 0x34, 0xe0, 0x1b, 0xcd, 0x24, 0xa0, 0x1f, 0x8f, 0x3a, 0xe2, 0xa2, - 0x81, 0x97, 0x3b, 0x36, 0xe7, 0x4e, 0x95, 0xd8, 0x7d, 0x2b, 0xe9, 0x52, - 0x8d, 0x6e, 0x0a, 0xb2, 0x60, 0x1a, 0x91, 0xd8, 0xc1, 0xd0, 0x8e, 0xa5, - 0x52, 0x24, 0x59, 0x1c, 0x0e, 0x4b, 0x3a, 0x86, 0x3d, 0x10, 0xa4, 0xec, - 0xd4, 0x0e, 0xa5, 0x78, 0x5d, 0x9a, 0x9a, 0x04, 0x14, 0xc1, 0xe1, 0xa6, - 0x8a, 0x52, 0x05, 0xe0, 0x0b, 0xcc, 0x31, 0x46, 0x4c, 0x1e, 0x24, 0xe8, - 0x10, 0xa3, 0x77, 0xc2, 0x37, 0xb7, 0x05, 0xe1, 0xb9, 0x66, 0x29, 0xee, - 0x82, 0x3c, 0x33, 0xb0, 0xa3, 0xe1, 0x84, 0x31, 0x30, 0x6c, 0xce, 0x63, - 0x42, 0xee, 0xcb, 0x9a, 0xb8, 0xb9, 0xf6, 0xff, 0x1f, 0xd5, 0x0d, 0xce, - 0xa8, 0x27, 0x21, 0x20, 0x81, 0x15, 0xd1, 0xa0, 0xa5, 0x4d, 0xe0, 0xb3, - 0x09, 0xce, 0x2e, 0x77, 0x7a, 0x33, 0xdf, 0x7e, 0xfc, 0xe4, 0x0a, 0xec, - 0xcc, 0x22, 0x67, 0x11, 0x8c, 0xaa, 0x28, 0xae, 0x6e, 0xba, 0xb0, 0x2d, - 0xc6, 0x35, 0x99, 0x54, 0x6f, 0x78, 0xef, 0xc7, 0x1e, 0x78, 0xba, 0x9b, - 0xd6, 0x81, 0x0b, 0xd8, 0x13, 0x81, 0x64, 0x3e, 0x10, 0x75, 0x14, 0x14, - 0x0a, 0x43, 0x78, 0x18, 0x28, 0x33, 0x5c, 0x7d, 0xcc, 0xd3, 0xe3, 0x36, - 0x88, 0xd7, 0x23, 0x43, 0xd9, 0x0b, 0x44, 0x5d, 0x23, 0xbd, 0x94, 0x0c, - 0x5a, 0xdd, 0xb6, 0xf3, 0x2a, 0x97, 0x46, 0x8a, 0xe6, 0x5b, 0x3e, 0x7c, - 0x8b, 0x4a, 0xc7, 0x19, 0x29, 0xf0, 0x9a, 0x8c, 0xa6, 0xa7, 0x1c, 0xed, - 0x4e, 0x93, 0x7e, 0x8b, 0x2b, 0x69, 0x7b, 0xd8, 0xe8, 0xf8, 0x60, 0x8e, - 0x58, 0x09, 0x3c, 0xe0, 0x31, 0x40, 0x2c, 0x48, 0xdb, 0x60, 0xd4, 0x14, - 0x1c, 0x97, 0xde, 0x00, 0x00, 0x05, 0xe2, 0xf1, 0x36, 0x8c, 0x82, 0x28, - 0x01, 0xaa, 0x7a, 0x7e, 0x0c, 0x6e, 0x1a, 0xdb, 0x98, 0xa0, 0x02, 0x01, - 0x4b, 0x9d, 0xb0, 0xac, 0x92, 0x89, 0x5e, 0x36, 0xd4, 0x89, 0xd8, 0xe9, - 0x7e, 0xbc, 0xbc, 0x78, 0x5a, 0xe4, 0x5a, 0x20, 0x84, 0xb8, 0x9d, 0xdd, - 0xac, 0x31, 0x9e, 0x9e, 0x6b, 0xe5, 0x4f, 0xa7, 0xa1, 0x48, 0x01, 0x08, - 0x92, 0x00, 0x4e, 0x78, 0x52, 0x8c, 0xe0, 0x03, 0xe1, 0xcd, 0xfa, 0xe1, - 0x93, 0x8e, 0x46, 0xf0, 0x24, 0x1a, 0xa2, 0x57, 0x10, 0x29, 0x48, 0x07, - 0xb0, 0xf4, 0xfe, 0x4e, 0x8a, 0x18, 0x55, 0xe3, 0x37, 0x49, 0x05, 0x50, - 0xa9, 0xb4, 0xf2, 0xb8, 0x5f, 0x67, 0xd3, 0x95, 0x2e, 0x44, 0x60, 0x2d, - 0x40, 0xe0, 0xff, 0xf1, 0x50, 0x80, 0x2e, 0xdf, 0xfc, 0x21, 0x1a, 0xcc, - 0xfa, 0xfe, 0xff, 0x84, 0x40, 0x94, 0xb2, 0x42, 0x2c, 0x8c, 0x3b, 0x0b, - 0x90, 0xd6, 0xa3, 0x61, 0xc0, 0x80, 0x00, 0x3c, 0xf8, 0x00, 0x5e, 0x71, - 0xcf, 0xc7, 0xbf, 0xc7, 0x8d, 0x77, 0xfa, 0xd7, 0x55, 0xd6, 0x83, 0x2f, - 0x10, 0xa0, 0xcf, 0x99, 0x6f, 0x5e, 0xc3, 0xfd, 0x0e, 0x7e, 0xfd, 0x0e, - 0xac, 0x25, 0x07, 0x12, 0xd6, 0x48, 0x2f, 0xd6, 0x80, 0x2b, 0x03, 0x67, - 0xe1, 0x56, 0xb7, 0xf2, 0x78, 0x89, 0xd2, 0xc0, 0x60, 0x71, 0xf1, 0xd8, - 0x0a, 0xa6, 0x20, 0xc2, 0x18, 0x69, 0x48, 0xb2, 0x43, 0x8b, 0xce, 0x7d, - 0x04, 0xdc, 0x0a, 0xec, 0xa9, 0x25, 0x93, 0x85, 0xc8, 0xa8, 0xcd, 0x28, - 0xad, 0xec, 0xd9, 0x61, 0x39, 0x60, 0x1a, 0x78, 0x46, 0x2c, 0xe8, 0x87, - 0x13, 0xfe, 0x1c, 0xe2, 0xa4, 0xcd, 0xdd, 0x4f, 0x14, 0x65, 0x0d, 0x77, - 0xdb, 0xc7, 0x4f, 0x96, 0x97, 0x94, 0x29, 0x38, 0x8f, 0x98, 0x6a, 0x2d, - 0x8a, 0xf5, 0xaf, 0x9c, 0xe0, 0xfd, 0xad, 0x16, 0xaf, 0x9c, 0x73, 0x27, - 0x78, 0x57, 0xe5, 0xbe, 0xe8, 0x87, 0xde, 0x3e, 0x9f, 0xa9, 0xac, 0x3f, - 0xb8, 0x11, 0x67, 0xf2, 0xfa, 0x5b, 0x7e, 0x8f, 0xd6, 0xd8, 0xf3, 0x83, - 0xe5, 0x81, 0x57, 0xc1, 0x77, 0xb9, 0xd9, 0x88, 0xb6, 0x07, 0xd8, 0x9a, - 0x90, 0x17, 0xf6, 0x4b, 0x33, 0xf4, 0x69, 0xae, 0x8a, 0x8a, 0x72, 0x0d, - 0xf9, 0x03, 0x80, 0x56, 0x8e, 0x8f, 0x3c, 0x4d, 0x2c, 0xeb, 0x77, 0x55, - 0xeb, 0x3d, 0xd0, 0x74, 0x63, 0xc5, 0x4a, 0xae, 0xfc, 0x98, 0x56, 0xbd, - 0x77, 0x08, 0x44, 0x71, 0x96, 0xc2, 0xd5, 0x89, 0x22, 0x6e, 0x2d, 0x0d, - 0x1a, 0x49, 0x6a, 0x3d, 0x26, 0x4a, 0xc7, 0xa5, 0xb8, 0xc4, 0xd6, 0x18, - 0x85, 0x44, 0x2d, 0xe6, 0xe6, 0x57, 0xe1, 0x9f, 0x3a, 0x52, 0xe0, 0xc3, - 0xb0, 0xa0, 0xe4, 0xbe, 0xf0, 0x00, 0x0f, 0x3e, 0x00, 0x16, 0xce, 0x2b, - 0x2b, 0xaf, 0x16, 0xdd, 0xd2, 0xc0, 0x24, 0xe1, 0x4d, 0xf6, 0x98, 0xd7, - 0xfc, 0xef, 0x31, 0xcb, 0x7c, 0x70, 0x2c, 0xe1, 0xab, 0xc4, 0x43, 0xa0, - 0x33, 0x5e, 0xa0, 0xb3, 0x25, 0xb4, 0x88, 0x91, 0x71, 0x6e, 0x48, 0xb9, - 0x27, 0x1a, 0x64, 0x82, 0xf0, 0x70, 0x04, 0x70, 0x14, 0x03, 0x98, 0x64, - 0x41, 0xd6, 0xaa, 0x72, 0x76, 0x10, 0x86, 0x6a, 0x06, 0x63, 0x1e, 0x87, - 0x0d, 0x00, 0x67, 0x02, 0xe2, 0x8a, 0x54, 0xd6, 0x77, 0xe3, 0xd6, 0x03, - 0x39, 0x10, 0xe7, 0xac, 0x39, 0xdf, 0x01, 0xa1, 0x09, 0x8b, 0x89, 0x45, - 0x61, 0xa6, 0x11, 0x25, 0x11, 0x25, 0xe5, 0xd9, 0x64, 0x45, 0xb3, 0x2b, - 0x0b, 0x06, 0x60, 0xe0, 0xff, 0xf1, 0x50, 0x80, 0x2e, 0x9f, 0xfc, 0x21, - 0x1a, 0xce, 0x53, 0xfb, 0xff, 0x84, 0x10, 0x96, 0xb8, 0x30, 0xec, 0x32, - 0xb5, 0x3b, 0x12, 0x02, 0x0e, 0xbb, 0x5e, 0x01, 0xd7, 0x60, 0x09, 0x5e, - 0x7c, 0x79, 0xf1, 0xe7, 0xb5, 0x7b, 0x71, 0x2e, 0xfd, 0x82, 0x87, 0x9e, - 0x7d, 0x5f, 0x7e, 0xb1, 0xf9, 0xec, 0x3e, 0x6f, 0x88, 0xbc, 0x0c, 0xa0, - 0x71, 0x12, 0xa3, 0x34, 0x27, 0x54, 0x9a, 0x0f, 0x88, 0xf6, 0x91, 0xda, - 0xce, 0x3b, 0x3b, 0x08, 0x20, 0x31, 0x90, 0xd1, 0xe4, 0xec, 0xe8, 0x4c, - 0x43, 0x8a, 0x5b, 0xd8, 0xa6, 0x81, 0x42, 0xce, 0x32, 0x43, 0x4c, 0x7c, - 0xfb, 0xe5, 0xa8, 0x2f, 0x24, 0x50, 0x23, 0x29, 0x26, 0xd7, 0x62, 0xf0, - 0x00, 0x98, 0xd4, 0xc6, 0x76, 0xc7, 0x3c, 0x5b, 0x64, 0xcd, 0xc2, 0x25, - 0x15, 0xca, 0xf4, 0xc8, 0x81, 0x73, 0x69, 0x8b, 0xd0, 0x56, 0x66, 0x09, - 0x99, 0xa8, 0x95, 0xd6, 0xef, 0x59, 0xe1, 0x89, 0xcc, 0x54, 0xe5, 0xa2, - 0x08, 0x0a, 0x40, 0xdd, 0x89, 0x0c, 0xd5, 0x38, 0x63, 0x30, 0x17, 0xcd, - 0x45, 0x92, 0x4b, 0x53, 0xcf, 0x36, 0x28, 0x4a, 0x6f, 0x13, 0xbb, 0x62, - 0x39, 0xf5, 0xd4, 0x6c, 0xb0, 0xd7, 0xfa, 0x58, 0x40, 0xba, 0xe9, 0x8a, - 0x69, 0xb8, 0x75, 0x5d, 0x4f, 0x50, 0x73, 0xa0, 0x01, 0x9d, 0xa4, 0x89, - 0xa4, 0x26, 0xc5, 0x7e, 0x98, 0x8d, 0x0a, 0x07, 0x8e, 0x37, 0x6c, 0x66, - 0x62, 0xe9, 0x10, 0x90, 0x2f, 0x02, 0x28, 0xa6, 0x51, 0x12, 0x39, 0xd2, - 0x70, 0x79, 0x80, 0x9c, 0xee, 0x4a, 0x9b, 0x1e, 0x16, 0x1e, 0x77, 0x69, - 0x9c, 0xce, 0x33, 0xaa, 0x86, 0x89, 0x24, 0x9e, 0x72, 0x4a, 0x91, 0x68, - 0x9f, 0x8c, 0x80, 0xb1, 0xf9, 0xca, 0x6d, 0xb9, 0xe9, 0xd6, 0x2c, 0xd9, - 0x78, 0xf6, 0x00, 0x62, 0x04, 0x45, 0xb8, 0x40, 0x96, 0xf2, 0x65, 0xac, - 0x8c, 0x8b, 0x43, 0xa3, 0xde, 0x52, 0x6b, 0x74, 0x80, 0x54, 0x00, 0x4c, - 0xd2, 0xb7, 0x3a, 0xef, 0x05, 0xe7, 0x00, 0x66, 0xe4, 0x21, 0xfe, 0x9e, - 0xa1, 0x04, 0x87, 0xaf, 0xf9, 0x9c, 0xe8, 0x4f, 0x6e, 0xc3, 0xd4, 0xf1, - 0x64, 0x58, 0x1d, 0x41, 0x78, 0xb3, 0xef, 0xac, 0x83, 0xad, 0x27, 0x18, - 0x6f, 0xf1, 0x00, 0x53, 0xc0, 0x51, 0xa6, 0xa2, 0x88, 0x3f, 0xce, 0x8e, - 0x29, 0x58, 0xc5, 0x51, 0x8a, 0x3c, 0xb8, 0x0f, 0x01, 0xa5, 0x89, 0xc7, - 0x07, 0xe7, 0xac, 0xca, 0x01, 0x2d, 0x38, 0x56, 0xfc, 0x74, 0x24, 0xa6, - 0xe8, 0xc6, 0xcc, 0x19, 0xa3, 0x5b, 0xdc, 0x76, 0xb1, 0x51, 0x79, 0xc8, - 0x6a, 0xee, 0xa1, 0x9e, 0x44, 0x1a, 0x82, 0xa3, 0xaa, 0x84, 0x18, 0xc9, - 0x4d, 0xe9, 0x16, 0x0e, 0xff, 0xf1, 0x50, 0x80, 0x2f, 0x5f, 0xfc, 0x21, - 0x1a, 0xcd, 0xdb, 0xff, 0xfb, 0x84, 0x90, 0x97, 0xb1, 0xc2, 0x6c, 0x2e, - 0x6b, 0x0c, 0xb8, 0xc6, 0xc5, 0x80, 0xaa, 0x1f, 0x5e, 0xf5, 0xe7, 0xc0, - 0x00, 0x15, 0x1d, 0x78, 0xd6, 0xfe, 0xbe, 0x7c, 0xf8, 0xeb, 0xbf, 0xbc, - 0xd5, 0xcf, 0x21, 0xeb, 0xff, 0x92, 0x2a, 0x9c, 0x6d, 0xdb, 0x9e, 0x97, - 0x36, 0xa8, 0xb5, 0x63, 0xc2, 0xa2, 0xef, 0x8a, 0x68, 0x29, 0x4c, 0x7a, - 0x5a, 0x4a, 0x9d, 0x92, 0x5b, 0x9e, 0xef, 0x39, 0x2d, 0x94, 0x7a, 0x22, - 0xa7, 0x34, 0xf2, 0x55, 0xc9, 0xc2, 0x67, 0x0f, 0xbc, 0xfc, 0x9d, 0x91, - 0x37, 0xe5, 0xba, 0x8a, 0x45, 0x8e, 0x3c, 0xee, 0x2e, 0x78, 0x44, 0x69, - 0x45, 0xe3, 0x82, 0x27, 0x1a, 0xe8, 0x25, 0x0c, 0xb3, 0x92, 0x49, 0xa4, - 0x67, 0xe6, 0xb8, 0x31, 0x02, 0x32, 0xaa, 0xc6, 0x38, 0x30, 0xc9, 0x38, - 0x04, 0xd5, 0x2f, 0x3e, 0x8f, 0xf1, 0x58, 0x67, 0x8d, 0xdc, 0x89, 0xc6, - 0x61, 0x12, 0xdf, 0x71, 0x9d, 0x48, 0x55, 0x72, 0x26, 0x68, 0x55, 0x91, - 0x9e, 0x78, 0x8d, 0xf8, 0x62, 0x65, 0x75, 0x06, 0x7c, 0x7f, 0x5f, 0xd0, - 0xb2, 0x66, 0x53, 0xbe, 0xb9, 0x58, 0xe5, 0x34, 0xc7, 0x5e, 0xe6, 0x62, - 0x82, 0xf9, 0x7c, 0x60, 0x01, 0x85, 0x2e, 0xda, 0x75, 0x14, 0x8d, 0x41, - 0xee, 0x83, 0x34, 0xb5, 0xfa, 0x8d, 0x6c, 0x9e, 0xfb, 0x2d, 0xbf, 0xee, - 0xfa, 0x38, 0x24, 0xe0, 0x66, 0x4e, 0x0e, 0x84, 0x83, 0x45, 0xa3, 0x21, - 0x6e, 0x39, 0x98, 0xd6, 0xf1, 0x00, 0x00, 0x35, 0xde, 0x52, 0xc5, 0x52, - 0xba, 0xdc, 0x97, 0x3d, 0xd9, 0xce, 0x9d, 0xf8, 0xed, 0xb9, 0x62, 0xdd, - 0x83, 0xdf, 0x89, 0x48, 0xd4, 0x4c, 0x28, 0xf9, 0xb8, 0xf9, 0xac, 0x29, - 0x2c, 0x8a, 0x9b, 0xa7, 0xb2, 0x4e, 0x1e, 0x86, 0x5a, 0x05, 0x5a, 0x54, - 0x32, 0xf1, 0xa0, 0xae, 0x6a, 0x89, 0x2f, 0x4b, 0xd8, 0x99, 0x56, 0x46, - 0x1d, 0x85, 0x06, 0xa5, 0xf7, 0x81, 0x28, 0x00, 0x02, 0xf1, 0xd7, 0x37, - 0x73, 0xbb, 0xeb, 0x9a, 0xaa, 0x84, 0x06, 0x4e, 0x3d, 0x54, 0x68, 0xde, - 0x3c, 0x6d, 0x0b, 0x8a, 0x63, 0x00, 0xb3, 0x73, 0xca, 0x51, 0x63, 0xdd, - 0x9d, 0x03, 0x38, 0xd8, 0xa4, 0x22, 0x6f, 0xb2, 0x71, 0x55, 0x81, 0x7a, - 0xe1, 0x17, 0xa1, 0x18, 0x54, 0x5a, 0x76, 0x82, 0xe4, 0x87, 0xd5, 0x22, - 0x3d, 0x4a, 0xb5, 0x01, 0x02, 0xc9, 0x14, 0x31, 0x95, 0x85, 0x14, 0x79, - 0x41, 0x80, 0xb4, 0xf2, 0xa6, 0xa9, 0x52, 0x88, 0x88, 0xec, 0xf3, 0xed, - 0xbb, 0x97, 0x4d, 0x9a, 0x67, 0xb2, 0x30, 0x5a, 0x62, 0x17, 0x05, 0xeb, - 0xab, 0xff, 0xe0, 0x8c, 0x97, 0x5a, 0x80, 0x9b, 0xa1, 0xc0, 0xff, 0xf1, - 0x50, 0x80, 0x31, 0x7f, 0xfc, 0x21, 0x1a, 0xc9, 0xd6, 0xbb, 0xfe, 0x86, - 0x00, 0x95, 0xb2, 0x31, 0xed, 0x2e, 0x6b, 0x0c, 0x91, 0x4a, 0x84, 0x61, - 0x82, 0x53, 0x5b, 0x02, 0x50, 0x00, 0x38, 0xe7, 0x8e, 0x4e, 0xb5, 0x9f, - 0x7a, 0xf2, 0x27, 0x13, 0xf1, 0x3c, 0xc3, 0x87, 0x46, 0xd8, 0x30, 0x33, - 0x86, 0xf2, 0xe5, 0xe9, 0x8a, 0xb5, 0xdb, 0xc6, 0x91, 0xe3, 0x9f, 0x78, - 0xc8, 0xec, 0xa4, 0x76, 0x10, 0xb4, 0xe9, 0x20, 0x63, 0x91, 0xca, 0xf4, - 0xa2, 0x22, 0x84, 0xf0, 0xb0, 0x94, 0x71, 0x4e, 0xa7, 0x11, 0x0a, 0x28, - 0x3f, 0xe8, 0x0e, 0x0c, 0xc4, 0x54, 0x4c, 0x3a, 0xe5, 0xe3, 0x16, 0x50, - 0xa3, 0x17, 0x25, 0xd7, 0x23, 0xfd, 0x05, 0x53, 0x0b, 0x12, 0xf5, 0xf0, - 0xac, 0xe7, 0x2e, 0xac, 0x56, 0x79, 0x65, 0x57, 0x78, 0x64, 0x2f, 0x93, - 0xe9, 0x35, 0x62, 0xa6, 0x32, 0x85, 0x4d, 0x65, 0x8d, 0x25, 0x9e, 0x88, - 0x5e, 0xfa, 0xbb, 0x6f, 0xf5, 0x3d, 0x30, 0x7c, 0xb0, 0x10, 0x52, 0x86, - 0x03, 0x57, 0x68, 0x2f, 0x20, 0x5d, 0x87, 0xa5, 0x63, 0x39, 0x30, 0xb9, - 0x29, 0x1b, 0x68, 0xbd, 0x78, 0x66, 0xc5, 0x78, 0x35, 0xfc, 0x3f, 0xf3, - 0xfe, 0xb1, 0xb7, 0x29, 0xc2, 0xf2, 0x2f, 0x33, 0xaa, 0xd9, 0x7d, 0x97, - 0x68, 0xab, 0x43, 0x7b, 0xff, 0xe7, 0x0a, 0x20, 0x19, 0xae, 0x5b, 0xdb, - 0x6a, 0x82, 0xb1, 0x2a, 0x4d, 0xfc, 0x28, 0x05, 0x1d, 0xa4, 0x90, 0x15, - 0x04, 0x65, 0xaa, 0xd6, 0xa7, 0x5a, 0xab, 0x43, 0x65, 0x0c, 0x51, 0xc5, - 0x4f, 0x50, 0x7c, 0xcb, 0x99, 0xba, 0x8c, 0x9f, 0xe0, 0x29, 0x4a, 0xf5, - 0x28, 0xb9, 0xf0, 0x8f, 0x98, 0xf3, 0xbd, 0x6a, 0xf7, 0x63, 0x75, 0xd8, - 0x6e, 0x89, 0x6a, 0x45, 0x68, 0xb1, 0x94, 0xae, 0xac, 0x75, 0x17, 0x6c, - 0xcd, 0x67, 0x9c, 0x75, 0x96, 0xe9, 0xa9, 0xab, 0xc6, 0xbe, 0x5e, 0xbb, - 0x27, 0x54, 0xeb, 0xee, 0x95, 0xb1, 0x42, 0xad, 0x0e, 0x6b, 0x08, 0x87, - 0xde, 0x01, 0xad, 0x80, 0x00, 0x18, 0x9a, 0xf1, 0xab, 0xdd, 0x15, 0xc6, - 0xec, 0x28, 0x49, 0xc0, 0xa2, 0x41, 0xf6, 0xa7, 0xea, 0x5f, 0xb9, 0x20, - 0x07, 0x24, 0x97, 0x20, 0xf0, 0xf1, 0x17, 0x4b, 0xb9, 0x81, 0xec, 0x68, - 0x2b, 0xe4, 0xb1, 0x4e, 0x4b, 0x32, 0x68, 0x5e, 0x79, 0x33, 0xda, 0x8f, - 0x07, 0x3e, 0x81, 0x10, 0xec, 0x84, 0x3c, 0x93, 0xcd, 0x0f, 0xa5, 0x5e, - 0x0a, 0xf0, 0x94, 0x27, 0x45, 0x66, 0x3b, 0x31, 0xca, 0x00, 0x2e, 0x73, - 0x33, 0x05, 0x30, 0x83, 0x86, 0x30, 0xd9, 0x8d, 0xb8, 0x05, 0x31, 0xca, - 0x73, 0x4e, 0xa6, 0x48, 0xbd, 0xfe, 0x5f, 0xfc, 0xba, 0xd6, 0x7b, 0x20, - 0x53, 0x2d, 0x7d, 0x14, 0x1b, 0xcb, 0x54, 0x11, 0x65, 0x1b, 0xbe, 0x4a, - 0x00, 0x35, 0x1a, 0x53, 0x00, 0x29, 0xc8, 0xf8, 0x1c, 0xff, 0xf1, 0x50, - 0x80, 0x2f, 0x1f, 0xfc, 0x21, 0x1a, 0xcf, 0xdd, 0xef, 0xff, 0xbb, 0xbf, - 0x94, 0xb8, 0x30, 0xe5, 0xc4, 0x15, 0x23, 0x12, 0x02, 0x0f, 0x3c, 0xe6, - 0xb6, 0x00, 0x00, 0x03, 0xae, 0xcf, 0x3d, 0x73, 0xe6, 0xfe, 0x03, 0x2f, - 0x12, 0xc7, 0x86, 0xfa, 0x93, 0xf6, 0xdf, 0x36, 0x13, 0xd3, 0xfa, 0x03, - 0x8c, 0xe1, 0xf5, 0xc2, 0xb6, 0x0a, 0x2a, 0x36, 0x56, 0x3e, 0x94, 0xd6, - 0x06, 0xa1, 0xcd, 0x81, 0x36, 0x23, 0x62, 0x4c, 0x69, 0x2d, 0xcd, 0x82, - 0x4f, 0x03, 0x68, 0x53, 0xc6, 0xb1, 0x2a, 0x16, 0x20, 0x21, 0xef, 0xb5, - 0x90, 0x2a, 0x0b, 0xaa, 0x47, 0x47, 0x27, 0x8b, 0x10, 0xfb, 0xbf, 0xf6, - 0x7f, 0x40, 0xd0, 0xa6, 0x39, 0xb0, 0xc3, 0x38, 0xac, 0x83, 0x0a, 0xca, - 0xd7, 0x94, 0x22, 0xe7, 0x8d, 0xf5, 0x1d, 0xb4, 0x98, 0xdb, 0x61, 0x8c, - 0x5d, 0x5d, 0xe1, 0x33, 0x8c, 0x53, 0x23, 0x0d, 0x0f, 0xec, 0x3e, 0xb3, - 0x8e, 0x09, 0xca, 0x68, 0x9c, 0x08, 0x22, 0x42, 0x50, 0x61, 0xd0, 0x74, - 0x38, 0x90, 0x82, 0x58, 0x64, 0x0c, 0xa9, 0x6c, 0xd1, 0x79, 0xf4, 0x7f, - 0x07, 0xf6, 0x8d, 0x29, 0x4c, 0x64, 0x19, 0xe9, 0x02, 0x80, 0x5a, 0xe7, - 0xa6, 0x66, 0xa5, 0x50, 0x74, 0xd5, 0x8c, 0x79, 0x91, 0x91, 0xa2, 0xbb, - 0x18, 0x2c, 0x08, 0x95, 0xcd, 0x02, 0xea, 0x4b, 0x9f, 0xa3, 0x96, 0xec, - 0xc1, 0x16, 0xb2, 0xe5, 0x09, 0x2e, 0x99, 0xbb, 0xbf, 0x86, 0x00, 0x4a, - 0x71, 0x51, 0x18, 0x4f, 0x5d, 0x77, 0xc9, 0x04, 0x82, 0x00, 0x8e, 0xa4, - 0x70, 0x23, 0xb5, 0x8a, 0x4a, 0x97, 0x79, 0xb5, 0x34, 0x34, 0xb8, 0x54, - 0x02, 0x24, 0x13, 0xdb, 0x12, 0x4a, 0x30, 0xa7, 0x66, 0x7d, 0xcf, 0x84, - 0x8c, 0x05, 0xaa, 0x58, 0x62, 0x97, 0x9b, 0xd1, 0xa6, 0x66, 0x09, 0x9a, - 0x81, 0x12, 0x90, 0xcb, 0x44, 0xa3, 0x9e, 0x05, 0xe6, 0xb6, 0x00, 0x00, - 0x77, 0xc3, 0x5e, 0xb8, 0x98, 0x28, 0xb0, 0xa2, 0xb7, 0xfe, 0xe2, 0xba, - 0x82, 0xd4, 0x46, 0x18, 0xdb, 0x18, 0xc4, 0xf3, 0xbb, 0xca, 0xd3, 0x9d, - 0x4b, 0x66, 0x74, 0xa0, 0xa6, 0x9f, 0x20, 0xc8, 0x21, 0x73, 0x3b, 0x67, - 0x09, 0x39, 0x62, 0x15, 0x49, 0x98, 0x6c, 0x34, 0xc7, 0x9e, 0xca, 0x8f, - 0x4b, 0x1d, 0x70, 0x42, 0x15, 0x93, 0x14, 0x2b, 0x36, 0x82, 0xd1, 0x0e, - 0x60, 0x9b, 0xa2, 0xd7, 0x02, 0x21, 0x8a, 0x1e, 0x33, 0x9c, 0xb1, 0xe3, - 0x71, 0xb8, 0x62, 0xb7, 0x65, 0x7b, 0x0d, 0x35, 0xcb, 0x5f, 0xd1, 0xff, - 0x40, 0xce, 0xaf, 0x0c, 0xb4, 0xe5, 0x33, 0x8e, 0x88, 0x5e, 0xb5, 0x63, - 0x52, 0x5a, 0xa7, 0x57, 0xcc, 0xf7, 0x2b, 0x2a, 0x68, 0x2a, 0x42, 0xa4, - 0x1c, 0xff, 0xf1, 0x50, 0x80, 0x30, 0xbf, 0xfc, 0x21, 0x1a, 0xcf, 0x9b, - 0xbb, 0xfb, 0xbf, 0xf7, 0x92, 0xb8, 0x30, 0xec, 0x53, 0x06, 0x2c, 0x04, - 0x02, 0xf0, 0x00, 0x01, 0x78, 0x1c, 0x72, 0x7e, 0x38, 0xf9, 0xf8, 0xd7, - 0x90, 0xcf, 0x9a, 0xc8, 0x21, 0x1a, 0x7f, 0x2a, 0xe3, 0xd1, 0xf6, 0xdf, - 0x58, 0x52, 0x75, 0x61, 0xf4, 0x0c, 0xde, 0x9c, 0xa3, 0x0e, 0x79, 0x3b, - 0x12, 0x71, 0x59, 0x1f, 0x40, 0xbe, 0x9f, 0x7c, 0xf1, 0xec, 0xfb, 0x80, - 0xac, 0xa0, 0x4b, 0xb1, 0xd7, 0x26, 0x16, 0xbf, 0x31, 0xec, 0x47, 0xb1, - 0x14, 0x75, 0x63, 0xfa, 0x86, 0x92, 0xd2, 0xa0, 0x12, 0x85, 0x53, 0x46, - 0x97, 0xdd, 0xe6, 0xdd, 0x5a, 0x74, 0xa3, 0x4d, 0x42, 0x75, 0x9a, 0xe5, - 0x31, 0xa3, 0x48, 0x9a, 0xda, 0xbb, 0xa9, 0xa5, 0xc6, 0x55, 0xe2, 0x7f, - 0x2e, 0xca, 0x2e, 0x75, 0xe1, 0x71, 0x8c, 0xf1, 0xa0, 0x45, 0x29, 0x29, - 0x94, 0xcf, 0xc3, 0xf8, 0x0d, 0x56, 0x28, 0x61, 0x2a, 0xa8, 0x88, 0x2e, - 0x24, 0x12, 0x57, 0x3b, 0xa5, 0x80, 0xb1, 0x71, 0x21, 0x94, 0xd0, 0xc2, - 0x2e, 0x58, 0xfb, 0x8f, 0xb4, 0xf0, 0x39, 0x8a, 0x04, 0xb6, 0x05, 0x94, - 0x05, 0x2e, 0x2f, 0x1a, 0x05, 0x84, 0x67, 0x69, 0x13, 0x8d, 0x17, 0xba, - 0xb5, 0x65, 0x6b, 0x02, 0x42, 0x95, 0x77, 0x15, 0x37, 0x6c, 0x74, 0x3b, - 0xa6, 0x96, 0x25, 0x80, 0x04, 0x00, 0xa8, 0x14, 0x9a, 0xfc, 0x05, 0xc5, - 0xea, 0xae, 0xcc, 0xa2, 0xb3, 0x69, 0xc3, 0xc2, 0xde, 0xb9, 0x22, 0xb7, - 0x60, 0x66, 0x42, 0x50, 0x66, 0x00, 0x2a, 0xea, 0x46, 0x7d, 0xf5, 0xb4, - 0xc8, 0x67, 0x86, 0x04, 0x43, 0x2b, 0xd6, 0x12, 0x2b, 0xe4, 0x5c, 0xe6, - 0xf8, 0x07, 0x77, 0x6d, 0xb5, 0xd4, 0x74, 0xa0, 0x9e, 0x01, 0xda, 0x53, - 0x24, 0x04, 0x37, 0xe0, 0xdc, 0x5e, 0x42, 0x64, 0x95, 0xb6, 0x8e, 0x83, - 0xa0, 0x90, 0xa4, 0x5c, 0xf6, 0xb6, 0x00, 0x03, 0x5b, 0x01, 0x79, 0x2a, - 0x4c, 0xd7, 0x35, 0xaa, 0xe6, 0xdc, 0x00, 0x73, 0xd4, 0x22, 0x9b, 0x1e, - 0x1c, 0x4f, 0x0e, 0xbe, 0xf6, 0xa4, 0x11, 0xac, 0x0c, 0x5a, 0xc1, 0xd7, - 0x3d, 0x94, 0x88, 0x06, 0x18, 0xc6, 0x04, 0x73, 0x44, 0x87, 0xef, 0xbb, - 0xa7, 0x14, 0x4b, 0x07, 0xcb, 0x08, 0x44, 0x51, 0x40, 0x1a, 0x7a, 0x32, - 0x90, 0x91, 0x8d, 0x11, 0xf9, 0x99, 0x15, 0xe8, 0x01, 0x36, 0x00, 0x90, - 0x10, 0x88, 0x26, 0x43, 0x22, 0x3a, 0x6c, 0xcb, 0xf2, 0xfa, 0x52, 0x08, - 0x8b, 0x16, 0x93, 0xec, 0x74, 0x3c, 0x0d, 0xd9, 0xcf, 0xd1, 0x04, 0xc7, - 0xe7, 0x1e, 0xc3, 0x9a, 0x6d, 0xcb, 0x00, 0x09, 0x07, 0x21, 0xc4, 0x34, - 0x0e, 0xdd, 0xbd, 0x7b, 0x96, 0x7e, 0xcf, 0xbd, 0xeb, 0xf5, 0xb9, 0xd5, - 0x64, 0xaa, 0x2c, 0x16, 0x90, 0x70, 0xff, 0xf1, 0x50, 0x80, 0x2f, 0xdf, - 0xfc, 0x21, 0x1a, 0xc9, 0xcf, 0xef, 0x3f, 0x90, 0x80, 0x92, 0xb6, 0xb1, - 0xac, 0x28, 0x39, 0x42, 0x0c, 0xc4, 0x41, 0x62, 0xc0, 0x41, 0x78, 0x00, - 0x02, 0x55, 0xe3, 0x5b, 0xeb, 0xbf, 0x3b, 0xf6, 0xf9, 0xae, 0x3e, 0xfc, - 0x78, 0xea, 0x74, 0x31, 0xb6, 0xfb, 0x2d, 0x40, 0x65, 0x50, 0xb4, 0x49, - 0x90, 0x29, 0xef, 0xab, 0x7b, 0xca, 0xb8, 0xa6, 0x63, 0xa2, 0x8d, 0xdb, - 0x10, 0x94, 0xcc, 0x70, 0x61, 0x8e, 0x0a, 0xa6, 0xc2, 0x25, 0xf2, 0xe3, - 0x80, 0xd6, 0x21, 0x23, 0x9f, 0xdc, 0x5d, 0x66, 0xf6, 0xe8, 0x0d, 0xce, - 0x92, 0x54, 0x9b, 0x78, 0x5e, 0x22, 0xbc, 0xbf, 0xa1, 0x33, 0x16, 0x61, - 0x00, 0x49, 0xff, 0x05, 0x32, 0x81, 0x50, 0x41, 0xcc, 0x3e, 0x53, 0x9f, - 0xb2, 0xf9, 0x19, 0x86, 0x0c, 0xd0, 0xb3, 0xfa, 0x07, 0x69, 0xc0, 0x5b, - 0x1c, 0x31, 0x2f, 0x1c, 0xb1, 0x2b, 0x20, 0x52, 0xf3, 0xb7, 0xfa, 0x5e, - 0x9f, 0x68, 0xb5, 0x89, 0xc0, 0x22, 0xec, 0x21, 0x6a, 0xbe, 0x1e, 0x62, - 0xe0, 0x95, 0x4d, 0xc1, 0x75, 0x85, 0xc5, 0xe7, 0x98, 0x65, 0xa1, 0xc2, - 0xe1, 0xc1, 0x20, 0x87, 0x6f, 0x1d, 0xc2, 0x3b, 0x9a, 0xa2, 0xbe, 0x8b, - 0x60, 0xc8, 0xdd, 0x52, 0x24, 0xf5, 0x82, 0x94, 0x7e, 0x29, 0xcb, 0xbd, - 0x7b, 0xad, 0xbc, 0x42, 0xa0, 0x1e, 0x97, 0xf6, 0xd5, 0x5f, 0xb3, 0x0e, - 0x74, 0x35, 0x02, 0xe1, 0xb0, 0xab, 0x2a, 0x47, 0x2b, 0xe8, 0x97, 0x5b, - 0x66, 0x1d, 0x3c, 0x35, 0x53, 0xdd, 0x74, 0x96, 0xd3, 0xd4, 0xd0, 0x00, - 0x84, 0x20, 0x6c, 0x93, 0x6f, 0x0e, 0xe9, 0x44, 0x82, 0x8f, 0xe7, 0xe9, - 0x3c, 0xd4, 0x53, 0x85, 0x83, 0x42, 0xd6, 0x6e, 0xe2, 0xa5, 0x88, 0x77, - 0xce, 0x98, 0xfe, 0x73, 0xd3, 0x65, 0xb9, 0x5f, 0xa2, 0xb4, 0xbd, 0x56, - 0x25, 0x10, 0x9b, 0x37, 0x05, 0x13, 0x97, 0x39, 0x1a, 0x72, 0x56, 0x56, - 0x3d, 0x92, 0x59, 0xef, 0x00, 0x00, 0x02, 0x55, 0xe3, 0x4d, 0xdd, 0x44, - 0x37, 0x7c, 0xae, 0x06, 0xed, 0xa6, 0x5b, 0x40, 0x78, 0xf4, 0xca, 0xb5, - 0xe8, 0xb0, 0x57, 0xf0, 0xa3, 0xdf, 0x5d, 0x18, 0x71, 0xd6, 0xf6, 0x3c, - 0xe4, 0x8a, 0x4f, 0xa1, 0x60, 0x1d, 0xa3, 0x5a, 0x64, 0x7f, 0x8c, 0xae, - 0x80, 0xe1, 0x4e, 0xe3, 0xc0, 0xb8, 0x43, 0x71, 0xd9, 0xec, 0xa9, 0x96, - 0xac, 0x39, 0xb1, 0x78, 0xe9, 0x22, 0xb7, 0xff, 0x86, 0xeb, 0xf0, 0x99, - 0xc7, 0x3a, 0xeb, 0x35, 0x30, 0xc6, 0x72, 0x84, 0xdd, 0x61, 0x9c, 0x08, - 0xaa, 0x8c, 0xbf, 0xdf, 0x7b, 0x87, 0x80, 0xa4, 0x5c, 0xc0, 0xa8, 0xab, - 0x65, 0x9e, 0x61, 0x22, 0x6f, 0xd0, 0x77, 0x3e, 0x16, 0xd0, 0x91, 0x75, - 0x63, 0x08, 0x03, 0x80, 0xff, 0xf1, 0x50, 0x80, 0x2f, 0x3f, 0xfc, 0x21, - 0x1a, 0xcf, 0x4b, 0xf7, 0xff, 0xaf, 0xff, 0x92, 0xb2, 0xb1, 0xac, 0x4c, - 0x59, 0x6a, 0x14, 0xc2, 0xc4, 0x81, 0x00, 0x00, 0x00, 0x0f, 0x3e, 0x3e, - 0x3d, 0xfd, 0x9d, 0x7e, 0x9e, 0xde, 0xbf, 0xc6, 0xf5, 0x7d, 0x41, 0x25, - 0xca, 0x70, 0x3e, 0x7e, 0x89, 0x26, 0xef, 0xa1, 0xc7, 0xde, 0xbf, 0x59, - 0x7f, 0x47, 0x93, 0xcc, 0x3b, 0xbd, 0xdf, 0x6d, 0xcd, 0xbd, 0x08, 0x4e, - 0x19, 0x97, 0x14, 0x71, 0x27, 0xce, 0xe3, 0x25, 0x5f, 0x4f, 0x90, 0x13, - 0xe2, 0xfc, 0xab, 0xf9, 0x08, 0xc1, 0x46, 0x14, 0x28, 0xad, 0x3b, 0xaa, - 0x31, 0xa0, 0x23, 0x3b, 0x35, 0x7f, 0xcc, 0x78, 0xdd, 0x7a, 0xd8, 0xca, - 0x2e, 0xf5, 0x73, 0xad, 0xa5, 0xb7, 0xe0, 0x18, 0xa6, 0x8f, 0x15, 0xeb, - 0x3d, 0x64, 0x04, 0x86, 0x41, 0x70, 0x90, 0xa3, 0x0e, 0xa7, 0xe8, 0x5e, - 0x33, 0xa4, 0xd0, 0x01, 0x0c, 0x49, 0xa9, 0xc9, 0x60, 0x5d, 0xf5, 0x5a, - 0x60, 0x21, 0x85, 0xdc, 0xd1, 0x8a, 0x6f, 0x2c, 0xb8, 0x81, 0x4e, 0x3e, - 0x36, 0x02, 0x2d, 0xd4, 0x9d, 0x7a, 0xf9, 0xe3, 0x52, 0xd7, 0xfa, 0x71, - 0xcb, 0x73, 0xc1, 0xe2, 0x11, 0xe5, 0xa3, 0xfb, 0xf1, 0x9d, 0x45, 0xc4, - 0x73, 0x46, 0x7d, 0x6e, 0xee, 0xf2, 0x0f, 0x0a, 0xaf, 0x58, 0x8f, 0x13, - 0xe8, 0x9f, 0xc4, 0xd8, 0x84, 0x82, 0x71, 0x77, 0xb2, 0xa1, 0x4b, 0xe6, - 0x7c, 0xd6, 0xed, 0x9d, 0xd1, 0x96, 0x54, 0x24, 0xac, 0xc2, 0x4e, 0x28, - 0x0e, 0x0c, 0x11, 0x0f, 0x75, 0x0a, 0xd6, 0xcc, 0xbd, 0xbb, 0x79, 0x5d, - 0xd5, 0x96, 0x3b, 0xa9, 0x0e, 0x26, 0x37, 0x4a, 0x14, 0x4e, 0xb1, 0x58, - 0x6a, 0x65, 0x98, 0xa2, 0xc4, 0x44, 0xf1, 0xef, 0xc9, 0xe0, 0xc7, 0xb7, - 0xc5, 0x26, 0xee, 0x0e, 0x44, 0x78, 0xc4, 0x39, 0x09, 0x82, 0xb4, 0x81, - 0x10, 0x49, 0x5b, 0x60, 0x68, 0x3b, 0x0a, 0x0e, 0xc2, 0xe4, 0xe7, 0xb5, - 0xba, 0x94, 0x95, 0x01, 0x50, 0x00, 0xf3, 0x49, 0xb9, 0x7c, 0xeb, 0x35, - 0xca, 0x40, 0x36, 0x49, 0xc9, 0x1f, 0x2e, 0x5c, 0xd3, 0xdc, 0x6b, 0x91, - 0xb4, 0x8a, 0x85, 0x40, 0xc6, 0x15, 0x71, 0xa1, 0x2b, 0x05, 0x0e, 0x59, - 0x66, 0x9d, 0x46, 0x50, 0xa9, 0x58, 0xf8, 0xe3, 0x88, 0xd5, 0x9b, 0xe0, - 0x29, 0x8d, 0x2b, 0xc4, 0x9c, 0xb5, 0x0a, 0x97, 0xcd, 0xd0, 0xbd, 0x8f, - 0xfe, 0x97, 0x49, 0xd2, 0x8e, 0xfc, 0xee, 0x23, 0x0b, 0x28, 0x60, 0x2b, - 0x80, 0x27, 0x2d, 0x53, 0xe6, 0x59, 0xd4, 0xd8, 0x0a, 0xdd, 0xfe, 0x4b, - 0xd0, 0xe0, 0x16, 0x01, 0x25, 0x2a, 0x42, 0x40, 0x15, 0xa1, 0xec, 0xb8, - 0x4a, 0x24, 0x2d, 0x79, 0xaf, 0x2c, 0xe6, 0x43, 0x80, 0xff, 0xf1, 0x50, - 0x80, 0x30, 0xbf, 0xfc, 0x21, 0x1a, 0xcd, 0x38, 0x80, 0x30, 0x84, 0x00, - 0x92, 0xb1, 0xc2, 0x6c, 0x32, 0xf3, 0x4b, 0x12, 0x04, 0xf1, 0xe7, 0xc3, - 0xd9, 0x4a, 0x4f, 0x03, 0xe3, 0xdc, 0xf6, 0xa7, 0x85, 0xe7, 0x9f, 0x1f, - 0x1e, 0xff, 0x7f, 0x5f, 0xbf, 0xf5, 0xf6, 0xcf, 0x6d, 0x71, 0xef, 0xf5, - 0x06, 0x36, 0xb5, 0x7e, 0x27, 0x00, 0xb2, 0xe7, 0xd6, 0xa7, 0xc0, 0x3b, - 0xe4, 0x13, 0x14, 0x1d, 0x06, 0xe1, 0x81, 0x6f, 0x3c, 0xa7, 0x27, 0x8c, - 0x1f, 0x88, 0x80, 0xd5, 0x94, 0x75, 0xaa, 0x3e, 0xb2, 0x61, 0x7a, 0x74, - 0x3b, 0xcd, 0x0d, 0x09, 0xad, 0xd6, 0xb6, 0x35, 0x8c, 0xdc, 0x5b, 0x67, - 0xd1, 0x30, 0xce, 0xb0, 0xdb, 0x71, 0x8e, 0xa4, 0xd7, 0x74, 0xa1, 0x1a, - 0x39, 0x88, 0xa4, 0x2b, 0x9f, 0xf1, 0xbf, 0xac, 0x6c, 0x4e, 0x69, 0xb1, - 0x78, 0x03, 0x40, 0x16, 0x35, 0x35, 0xbe, 0xf7, 0xc7, 0x91, 0x02, 0x5c, - 0xbc, 0xf3, 0x18, 0x80, 0x78, 0x04, 0xfa, 0xf7, 0x50, 0x97, 0xe2, 0x28, - 0x1d, 0xf8, 0xa4, 0x87, 0xf4, 0xf8, 0x39, 0x1d, 0x8f, 0x5f, 0xc7, 0xd6, - 0x13, 0xee, 0x87, 0xbd, 0xf8, 0x97, 0x03, 0xe2, 0x0f, 0x5b, 0xff, 0x3c, - 0xf6, 0x60, 0xf9, 0xf4, 0x33, 0x72, 0x2b, 0x9f, 0x5f, 0xa7, 0xee, 0xfa, - 0xe6, 0xa7, 0x02, 0x7c, 0x40, 0xde, 0x08, 0x08, 0x22, 0xe8, 0xa9, 0x6a, - 0xa9, 0x2b, 0xf9, 0x09, 0x42, 0x5c, 0x48, 0x86, 0x60, 0xa9, 0xcc, 0x8a, - 0x5b, 0x35, 0x85, 0x52, 0xe4, 0xa7, 0x26, 0xca, 0x3c, 0xe5, 0xb1, 0x00, - 0x90, 0x48, 0x08, 0x21, 0x21, 0x00, 0xc1, 0x72, 0x17, 0xc0, 0xb1, 0x1d, - 0x05, 0xb5, 0x69, 0x46, 0x0d, 0x37, 0x10, 0x5b, 0xc0, 0xb1, 0x0d, 0xa4, - 0x0d, 0x05, 0xd5, 0x61, 0xc2, 0x09, 0x2b, 0xa2, 0x0e, 0xc2, 0x82, 0xb0, - 0x90, 0xbd, 0xc0, 0x8a, 0x00, 0x00, 0x09, 0x9d, 0x6e, 0xb4, 0xae, 0x64, - 0x6d, 0x40, 0xd6, 0xee, 0x4f, 0x77, 0xa2, 0x36, 0xb9, 0x04, 0x0f, 0xf4, - 0x93, 0x7b, 0xf8, 0x04, 0x3e, 0xcd, 0x11, 0x4f, 0x75, 0x60, 0x97, 0x83, - 0x01, 0x70, 0x06, 0xd3, 0x40, 0xc6, 0x99, 0x0d, 0xfb, 0xc9, 0x1c, 0xa6, - 0x78, 0x8c, 0x52, 0x3f, 0x46, 0x11, 0x14, 0x51, 0x56, 0x6a, 0xde, 0x90, - 0x1a, 0xed, 0xbe, 0x81, 0x35, 0x19, 0xf3, 0x19, 0x1a, 0xc4, 0xa6, 0x78, - 0xeb, 0xaa, 0x68, 0xc8, 0xa3, 0x71, 0x99, 0x6a, 0xc4, 0x02, 0xc0, 0xe3, - 0x23, 0xff, 0x1a, 0x4d, 0x01, 0xb3, 0xf4, 0x8a, 0x70, 0xf3, 0x75, 0x43, - 0x8c, 0x11, 0x35, 0xa0, 0x36, 0xd0, 0x80, 0x2a, 0x06, 0x6d, 0x21, 0x8a, - 0x82, 0xbd, 0x8f, 0x5e, 0x9e, 0x8f, 0x89, 0x44, 0x7f, 0x6b, 0x70, 0x98, - 0x21, 0xac, 0x84, 0x30, 0x07, 0x5c, 0x40, 0x54, 0x60, 0x4f, 0x29, 0xfb, - 0x4a, 0x4e, 0xff, 0xf1, 0x50, 0x80, 0x2f, 0x3f, 0xfc, 0x21, 0x1a, 0xcd, - 0xdc, 0xf3, 0xff, 0xbb, 0xff, 0x96, 0xa4, 0xb0, 0xec, 0x30, 0x34, 0x1c, - 0xa1, 0x50, 0x42, 0x62, 0x40, 0x95, 0x0f, 0xf4, 0xfd, 0xb7, 0xc3, 0xb8, - 0x29, 0x03, 0x5b, 0x0a, 0xeb, 0xbe, 0xbb, 0xfa, 0xf9, 0xe2, 0xbf, 0x15, - 0x33, 0xdb, 0x41, 0xdf, 0xd0, 0xfb, 0x1f, 0xab, 0x3e, 0x79, 0xe6, 0xa5, - 0xa1, 0x34, 0x33, 0x3d, 0x72, 0x09, 0x94, 0xd2, 0x83, 0xaa, 0x71, 0x82, - 0xa8, 0xed, 0xef, 0x7d, 0x9a, 0x86, 0xed, 0x72, 0x73, 0xa5, 0xe8, 0xf7, - 0xd4, 0x30, 0xbf, 0x18, 0x6c, 0x88, 0x1b, 0x0b, 0x59, 0x41, 0xb6, 0x4b, - 0x29, 0x1f, 0x56, 0xb0, 0xf8, 0x0f, 0xfc, 0xdd, 0xf0, 0x56, 0xbd, 0xd6, - 0x8a, 0xf8, 0xdb, 0x16, 0x90, 0x8b, 0xc6, 0x2c, 0xd5, 0xfb, 0x7f, 0xe4, - 0x9b, 0x17, 0x18, 0x45, 0xdd, 0x16, 0xa5, 0x54, 0x0b, 0xa8, 0xca, 0xe7, - 0x2e, 0xe9, 0xfd, 0xf3, 0xd0, 0x89, 0xc3, 0x15, 0x45, 0xf5, 0x66, 0x05, - 0x82, 0x43, 0x4f, 0x6c, 0x83, 0x22, 0x33, 0x35, 0x0a, 0xe6, 0xbb, 0x8b, - 0x6c, 0xaa, 0xc7, 0xe8, 0x81, 0xaa, 0x54, 0x63, 0xa7, 0x94, 0xdc, 0xaf, - 0x43, 0x72, 0x0d, 0xf4, 0x90, 0x06, 0xeb, 0x8c, 0x96, 0xf9, 0x27, 0x79, - 0x4f, 0xc4, 0x3a, 0xa8, 0x00, 0xff, 0xc7, 0x32, 0xe6, 0x1e, 0x58, 0x16, - 0xee, 0xb7, 0xe4, 0x33, 0x0a, 0xab, 0xb8, 0x3e, 0x21, 0x3c, 0xfd, 0xf4, - 0xa5, 0x6e, 0xb6, 0x18, 0x6b, 0xfe, 0x1c, 0x49, 0xde, 0x73, 0x01, 0x39, - 0xe8, 0x5e, 0x96, 0xad, 0x85, 0x10, 0x82, 0x0f, 0xb2, 0xb8, 0xb6, 0xef, - 0x3e, 0x82, 0x16, 0x44, 0x43, 0x02, 0x05, 0x52, 0x44, 0x8a, 0x65, 0x47, - 0x42, 0xf1, 0x72, 0xd3, 0x86, 0x98, 0xe1, 0x8f, 0xe5, 0xd4, 0xd5, 0xb5, - 0x36, 0x50, 0x2e, 0x32, 0x26, 0xa5, 0x82, 0x85, 0x62, 0x29, 0xaa, 0x5a, - 0xc4, 0xca, 0xb2, 0x39, 0x10, 0x76, 0x19, 0x27, 0x3c, 0x0d, 0x6f, 0x8e, - 0x40, 0x03, 0x5b, 0x0e, 0xf8, 0xbc, 0x95, 0x78, 0xa9, 0xbb, 0xa8, 0x00, - 0x5f, 0xa5, 0xf5, 0xd5, 0x13, 0xd5, 0x25, 0xc7, 0x3a, 0x33, 0xca, 0x0f, - 0xc5, 0xe4, 0xee, 0x93, 0xc2, 0x2f, 0x95, 0x4d, 0xee, 0x7f, 0x11, 0x27, - 0x2a, 0x54, 0x7b, 0xe4, 0x06, 0x1c, 0xa4, 0x83, 0x26, 0x58, 0x61, 0x85, - 0xea, 0x5a, 0xbd, 0x49, 0xc2, 0x98, 0x67, 0xbb, 0x0e, 0xb6, 0x98, 0x2b, - 0xfe, 0x98, 0x56, 0x0f, 0x03, 0xbf, 0x85, 0x1d, 0xd9, 0x22, 0xab, 0x67, - 0x0b, 0xe4, 0x16, 0x6b, 0x7e, 0x4f, 0x0c, 0x68, 0xb8, 0xa1, 0x8d, 0x44, - 0x96, 0x48, 0xd1, 0x15, 0x5b, 0x14, 0x29, 0x17, 0x95, 0xff, 0x6e, 0xd0, - 0xa5, 0x10, 0x15, 0xb4, 0x2a, 0x43, 0x80, 0xff, 0xf1, 0x50, 0x80, 0x2f, - 0xdf, 0xfc, 0x21, 0x1a, 0xcf, 0xab, 0x44, 0x33, 0x04, 0x40, 0x96, 0xb6, - 0xb1, 0x9c, 0x96, 0x19, 0x19, 0xa9, 0x89, 0x02, 0x0c, 0xbd, 0x6f, 0xeb, - 0xde, 0xb9, 0xe3, 0x31, 0xc7, 0x23, 0x89, 0xdb, 0x87, 0x3f, 0x0f, 0x7f, - 0xaf, 0x1f, 0x5f, 0xa7, 0xc7, 0xaf, 0xf1, 0xbd, 0x67, 0x9d, 0x03, 0x24, - 0x0e, 0xb2, 0xe4, 0xf7, 0xe7, 0x53, 0x38, 0x30, 0xd4, 0x60, 0xb9, 0x65, - 0x64, 0xb9, 0xa9, 0xc3, 0xae, 0xd4, 0x09, 0x51, 0x2e, 0x31, 0x9a, 0x88, - 0xca, 0x20, 0x87, 0x9b, 0x08, 0x61, 0x0d, 0x2d, 0x2e, 0x70, 0x41, 0xc2, - 0xc7, 0x40, 0xb3, 0x5f, 0xb4, 0x89, 0x96, 0x77, 0x23, 0x08, 0x50, 0x76, - 0x43, 0xfb, 0x2f, 0xcf, 0xb5, 0xc4, 0x65, 0x8c, 0xdd, 0x5f, 0x83, 0x62, - 0xe2, 0xe7, 0x36, 0x53, 0x40, 0x10, 0x5e, 0xed, 0x1c, 0x00, 0x0d, 0x2e, - 0xcc, 0x26, 0xb3, 0x13, 0xa8, 0x14, 0xca, 0x2d, 0x1d, 0xb7, 0xf7, 0xbd, - 0x08, 0x93, 0x21, 0xa9, 0xe4, 0x53, 0xc4, 0x6f, 0xf9, 0x1a, 0xfd, 0x33, - 0x8f, 0x9f, 0xe9, 0x5a, 0x95, 0xe4, 0x1f, 0x9e, 0xc7, 0xa0, 0x3c, 0x28, - 0xb4, 0x9b, 0x7f, 0x69, 0xdc, 0xed, 0x37, 0x3a, 0xbb, 0x34, 0x3d, 0x22, - 0xa1, 0xf5, 0xfe, 0x5a, 0xcf, 0xb9, 0xb4, 0xfa, 0x7f, 0x92, 0x3d, 0x08, - 0x3e, 0x97, 0xcc, 0xb7, 0x30, 0xe0, 0xcb, 0x3b, 0xc3, 0x16, 0xe8, 0xb0, - 0x6c, 0xae, 0xd8, 0x29, 0x89, 0xb7, 0xbf, 0xa2, 0x82, 0x23, 0xb3, 0x6e, - 0x80, 0x26, 0x0a, 0x82, 0x04, 0xd6, 0xd9, 0x0a, 0x63, 0xbb, 0x29, 0x60, - 0x89, 0xc2, 0xbe, 0x51, 0x7d, 0xbd, 0xde, 0x3a, 0x02, 0x44, 0x37, 0x33, - 0x81, 0x48, 0xa8, 0x5a, 0x03, 0x12, 0x1a, 0x24, 0x28, 0x90, 0xd0, 0x45, - 0xc5, 0x61, 0xd2, 0xd8, 0x5f, 0xcb, 0xac, 0x9a, 0xcc, 0x48, 0x10, 0x84, - 0xf5, 0x59, 0x6e, 0x44, 0xf5, 0x52, 0xd0, 0xcb, 0x23, 0x0e, 0xc4, 0x42, - 0x92, 0xfb, 0xc0, 0x12, 0x80, 0x04, 0xa0, 0xc9, 0xac, 0x91, 0xbc, 0x50, - 0x07, 0x03, 0x3b, 0x58, 0x6b, 0x13, 0xca, 0xec, 0xd6, 0xe2, 0xa8, 0x2e, - 0x13, 0xe0, 0x64, 0x27, 0x24, 0x68, 0xb8, 0x8e, 0x7d, 0x8a, 0xce, 0x7f, - 0x00, 0x8c, 0xec, 0x47, 0xe0, 0xe3, 0x63, 0xad, 0x6b, 0x8e, 0xc6, 0xcb, - 0x0d, 0xf6, 0x90, 0xc3, 0x6a, 0x84, 0x09, 0x77, 0x7a, 0x0d, 0x68, 0x13, - 0x93, 0x39, 0xc5, 0xb6, 0xb9, 0xa5, 0xa4, 0xa6, 0x34, 0xc1, 0x4d, 0x52, - 0x6b, 0x03, 0xeb, 0xb6, 0xe6, 0xcd, 0xff, 0xd2, 0xa7, 0x12, 0xc7, 0x87, - 0xf5, 0x6f, 0x51, 0x27, 0x2a, 0x88, 0xc6, 0xf1, 0xcf, 0x2c, 0x97, 0x79, - 0x68, 0xc4, 0x15, 0x99, 0x38, 0x7d, 0xd3, 0xfe, 0xae, 0x8d, 0x45, 0x10, - 0x43, 0x57, 0x68, 0x6c, 0x07, 0xff, 0xf1, 0x50, 0x80, 0x2f, 0x7f, 0xfc, - 0x21, 0x1a, 0xcf, 0x79, 0xba, 0xdf, 0x84, 0x00, 0x96, 0xb2, 0xb1, 0xec, - 0x30, 0x46, 0x15, 0x86, 0x56, 0x64, 0x21, 0x31, 0x60, 0x20, 0xd6, 0xd3, - 0x5c, 0x83, 0xf5, 0xfd, 0x00, 0x07, 0x9f, 0x1e, 0xdd, 0xf9, 0xf7, 0xd6, - 0x7e, 0x2f, 0x38, 0x9e, 0x42, 0x0e, 0xec, 0xfb, 0x76, 0x7f, 0x43, 0x9f, - 0xdf, 0x31, 0xc4, 0x29, 0x0a, 0x68, 0x2b, 0x79, 0xba, 0xc8, 0xba, 0x96, - 0xab, 0xe3, 0xd9, 0x57, 0x0e, 0x2e, 0xac, 0xec, 0x12, 0x58, 0x4c, 0x0f, - 0x89, 0x24, 0x31, 0x9a, 0xec, 0x43, 0x4e, 0x0c, 0x1f, 0x08, 0x5c, 0x55, - 0x0b, 0x6e, 0x90, 0x82, 0x31, 0x10, 0x0b, 0xa4, 0x95, 0x39, 0x48, 0xb0, - 0x01, 0x75, 0x25, 0x73, 0xa1, 0xa5, 0xc2, 0x0a, 0xd6, 0x5a, 0x00, 0xea, - 0xfd, 0x32, 0x25, 0x85, 0x63, 0x31, 0x0b, 0xc2, 0x4b, 0xbc, 0xe0, 0x2f, - 0x1b, 0xac, 0x38, 0x9e, 0xf1, 0xf9, 0x36, 0x00, 0x81, 0x96, 0x90, 0x20, - 0x11, 0x59, 0x30, 0xc3, 0xa5, 0xdb, 0x0a, 0x4d, 0x4c, 0xc5, 0x6b, 0xc2, - 0xd5, 0x86, 0x95, 0x2b, 0x38, 0x92, 0x73, 0xd7, 0xd1, 0x80, 0xd8, 0x57, - 0xe9, 0xba, 0xcd, 0xd4, 0x61, 0xbb, 0x3c, 0xea, 0x0c, 0xc9, 0x7e, 0x73, - 0x9c, 0xd7, 0x9f, 0x57, 0xfd, 0x3f, 0x03, 0xfa, 0x08, 0x84, 0x92, 0xea, - 0xbd, 0x13, 0x1c, 0x36, 0x75, 0xff, 0x91, 0xdc, 0xc5, 0x87, 0x18, 0xa9, - 0x7e, 0xd6, 0x28, 0xb0, 0xe8, 0x36, 0x4a, 0x1b, 0x47, 0xe0, 0xa7, 0xac, - 0xec, 0x72, 0x1b, 0xad, 0x67, 0xa4, 0x18, 0x82, 0x60, 0x41, 0x78, 0x13, - 0x94, 0x19, 0x3e, 0xdd, 0xb8, 0xa5, 0xfc, 0xf2, 0x2e, 0x7a, 0xd0, 0xfe, - 0xc1, 0x17, 0x03, 0x71, 0x78, 0x13, 0x3d, 0x77, 0xe3, 0x44, 0x49, 0xe3, - 0xf4, 0xbb, 0x4f, 0x9b, 0x59, 0x3a, 0xda, 0x31, 0x9e, 0x35, 0x47, 0x4d, - 0xb9, 0x77, 0x94, 0x79, 0x6a, 0x23, 0x22, 0xc3, 0x03, 0x63, 0x10, 0x6c, - 0x2a, 0x2b, 0x08, 0x87, 0xde, 0x0d, 0x6c, 0x00, 0x00, 0x1e, 0xff, 0x0b, - 0xdd, 0xeb, 0xc2, 0xa3, 0x10, 0x44, 0x26, 0x4e, 0x54, 0xa3, 0x1a, 0x8f, - 0x68, 0xb9, 0x04, 0x90, 0x22, 0x5e, 0x61, 0xcf, 0x92, 0xd1, 0x59, 0xf6, - 0xfd, 0x09, 0x4d, 0xff, 0xa6, 0x61, 0x96, 0x5f, 0x7d, 0xc2, 0x1a, 0x51, - 0x41, 0x2a, 0x10, 0xeb, 0xab, 0x14, 0xcf, 0x4f, 0xcd, 0x9c, 0x19, 0x1b, - 0x83, 0x21, 0x53, 0xee, 0xd9, 0x60, 0x1b, 0xa3, 0x9d, 0x22, 0x6b, 0x30, - 0x56, 0x6c, 0xd2, 0xcd, 0x4c, 0x84, 0x06, 0xbb, 0xfe, 0xdc, 0xf4, 0x39, - 0xa2, 0x9a, 0x26, 0x93, 0x11, 0xdc, 0x99, 0x39, 0xc9, 0x0b, 0xb5, 0xd3, - 0x93, 0x96, 0x1c, 0xa5, 0x98, 0x50, 0x18, 0x00, 0x01, 0x4c, 0x89, 0x0e, - 0xff, 0xf1, 0x50, 0x80, 0x31, 0x3f, 0xfc, 0x21, 0x1a, 0xcc, 0xdf, 0xff, - 0xff, 0x80, 0x40, 0x92, 0xb6, 0xc0, 0xd8, 0x72, 0x16, 0x10, 0x86, 0xc2, - 0x82, 0xb0, 0xb9, 0x04, 0x6a, 0x66, 0x18, 0x1a, 0xd8, 0x00, 0x00, 0x2f, - 0x25, 0x71, 0xcc, 0xaf, 0x3a, 0xf5, 0xf8, 0x9e, 0xc3, 0x4e, 0x9d, 0x1f, - 0xf6, 0x6f, 0xd6, 0xc8, 0x75, 0xa8, 0x2f, 0x3d, 0x37, 0x01, 0x58, 0x19, - 0x20, 0xe6, 0x3f, 0x26, 0xaf, 0x47, 0xdb, 0xc5, 0x8a, 0xb0, 0xa0, 0x2b, - 0x58, 0xd9, 0x10, 0x6b, 0x8e, 0xd3, 0x51, 0x3b, 0x97, 0xff, 0x6a, 0xf0, - 0x97, 0xbf, 0x24, 0xe1, 0x08, 0xe5, 0x6e, 0x04, 0x8c, 0x74, 0xfa, 0x96, - 0xb2, 0x20, 0x61, 0x45, 0x4e, 0xbf, 0xa1, 0x7f, 0x07, 0x9b, 0x85, 0x28, - 0xa5, 0x9a, 0x6a, 0xb5, 0x31, 0x90, 0x0c, 0x3c, 0x79, 0x31, 0x83, 0x1e, - 0xbb, 0xe0, 0x17, 0xe1, 0x00, 0x0d, 0x41, 0x88, 0xc0, 0x17, 0xad, 0xe3, - 0x97, 0xb0, 0x4d, 0x1c, 0x60, 0xd2, 0xb0, 0x59, 0x96, 0x03, 0x0c, 0x61, - 0x80, 0x31, 0x77, 0xad, 0x36, 0x36, 0x2c, 0x65, 0xbe, 0x55, 0xa5, 0xfe, - 0x6c, 0x6f, 0x4e, 0x2e, 0xe2, 0xa5, 0x37, 0x90, 0x63, 0x46, 0x78, 0xc6, - 0x52, 0xc6, 0x39, 0x3d, 0xb7, 0x5b, 0x4c, 0x77, 0x5c, 0x5a, 0xb8, 0x07, - 0x29, 0x71, 0x4b, 0x05, 0xf4, 0x16, 0xc9, 0x39, 0xf4, 0x9c, 0xf9, 0xf1, - 0xad, 0xfe, 0x1c, 0xa0, 0x01, 0x13, 0x73, 0x73, 0x34, 0x31, 0xb8, 0x22, - 0x77, 0xd8, 0x92, 0x2f, 0x02, 0xc0, 0x9c, 0x86, 0x62, 0xd7, 0xd5, 0x3a, - 0xc7, 0x54, 0xed, 0x75, 0x8c, 0xdf, 0x7e, 0xb2, 0xee, 0xa8, 0x02, 0x8d, - 0xef, 0x14, 0xb1, 0xad, 0x9a, 0x66, 0x57, 0x28, 0x92, 0x50, 0x88, 0xa5, - 0x2b, 0x58, 0x03, 0x79, 0xda, 0xe9, 0x6e, 0xf6, 0x81, 0x99, 0xaa, 0x73, - 0x57, 0xfe, 0x2c, 0x2e, 0xd9, 0xce, 0x7b, 0xa5, 0xdf, 0x25, 0x63, 0x84, - 0xd9, 0x18, 0x76, 0x14, 0x1d, 0x85, 0x03, 0x23, 0xf7, 0x81, 0xad, 0x80, - 0x00, 0x07, 0x8f, 0x6a, 0x5e, 0xf2, 0x4a, 0xc1, 0xa0, 0x64, 0x23, 0x2b, - 0x36, 0x5d, 0x05, 0x57, 0x6e, 0x9c, 0x8a, 0xda, 0x02, 0x3e, 0x22, 0x48, - 0xa8, 0x95, 0xcd, 0x44, 0x4f, 0x43, 0x84, 0xaf, 0xf9, 0x65, 0x2d, 0x3c, - 0x8b, 0x3f, 0xca, 0xca, 0x92, 0xa5, 0x80, 0x21, 0x42, 0x52, 0x93, 0x4a, - 0xa9, 0x45, 0x9c, 0x3b, 0x87, 0xba, 0x45, 0x59, 0x7b, 0x26, 0xbe, 0x9d, - 0x17, 0xf5, 0xe8, 0x4b, 0x13, 0xd9, 0x68, 0x06, 0x91, 0x3a, 0x60, 0x82, - 0x9b, 0xf3, 0xff, 0xd7, 0xad, 0x99, 0xb3, 0xc2, 0x4d, 0x6f, 0xa6, 0x70, - 0x66, 0xef, 0xaa, 0xe7, 0xcd, 0x48, 0xb8, 0x05, 0x42, 0xbc, 0x24, 0x17, - 0x48, 0xb2, 0xed, 0x3a, 0xaa, 0x22, 0x15, 0xbb, 0xe8, 0x55, 0xf6, 0x7c, - 0xa3, 0x3b, 0x84, 0x18, 0x69, 0x40, 0xba, 0xc4, 0x1c, 0xff, 0xf1, 0x50, - 0x80, 0x2f, 0xbf, 0xfc, 0x21, 0x1a, 0xc9, 0xf9, 0xdf, 0xff, 0x40, 0x00, - 0x91, 0xb2, 0x42, 0x2c, 0x30, 0x49, 0x39, 0x09, 0xca, 0x66, 0x62, 0xc0, - 0x40, 0x01, 0x78, 0x94, 0x00, 0x25, 0x6a, 0x71, 0xee, 0xe3, 0xdb, 0x8c, - 0xe3, 0xc7, 0xea, 0x2b, 0xca, 0x8a, 0x1e, 0x8d, 0xa0, 0x07, 0x21, 0x60, - 0x82, 0xd4, 0x5d, 0xbb, 0x42, 0xbc, 0x17, 0x64, 0x60, 0xbb, 0x4a, 0x06, - 0xca, 0xbe, 0x02, 0x70, 0x5e, 0x91, 0x1b, 0x1b, 0xa3, 0x70, 0xd4, 0x34, - 0x72, 0x03, 0x25, 0xa6, 0x3c, 0x65, 0x87, 0x32, 0x23, 0xc1, 0x04, 0xff, - 0x9a, 0xb0, 0xc0, 0x18, 0x38, 0xf8, 0xd1, 0x1c, 0xc9, 0xad, 0xd1, 0x9f, - 0xb5, 0xfe, 0x03, 0x88, 0x63, 0x86, 0xd8, 0xd4, 0xbc, 0xb5, 0xa3, 0x26, - 0x57, 0x13, 0x17, 0x37, 0x64, 0xa3, 0xeb, 0x3f, 0xbd, 0xf5, 0xb5, 0x04, - 0x14, 0x3f, 0x4a, 0x31, 0x98, 0xd5, 0xde, 0x85, 0xff, 0x07, 0x1b, 0xe5, - 0xf3, 0xa2, 0x62, 0x86, 0x79, 0x2b, 0x16, 0xbe, 0x30, 0x85, 0x12, 0xbe, - 0xe6, 0x18, 0xd4, 0x4a, 0x02, 0x60, 0x54, 0xb1, 0x54, 0xce, 0x44, 0xbf, - 0x2b, 0x4a, 0x32, 0x58, 0x3e, 0xc3, 0x32, 0x09, 0x1b, 0xca, 0xf6, 0x91, - 0xb4, 0x79, 0xbf, 0xb9, 0x3f, 0xb1, 0x47, 0x99, 0x2e, 0x8d, 0xd1, 0xff, - 0x2b, 0x16, 0xd8, 0x99, 0x32, 0x30, 0xd9, 0xeb, 0xbe, 0x77, 0x53, 0xb5, - 0xd2, 0xb7, 0x0f, 0x18, 0x9c, 0x01, 0x8d, 0x19, 0xa6, 0xb4, 0x70, 0xc1, - 0x12, 0xbb, 0x91, 0xee, 0xcf, 0x1a, 0x4f, 0x78, 0xc2, 0xb3, 0x4d, 0x35, - 0xae, 0x02, 0x59, 0xa9, 0x19, 0x71, 0x11, 0xb4, 0x6b, 0xec, 0xdd, 0x22, - 0x77, 0x61, 0x4e, 0xf7, 0x79, 0x21, 0xe2, 0x11, 0x14, 0x49, 0x19, 0x92, - 0xcf, 0x04, 0x00, 0x25, 0xd9, 0x81, 0x00, 0xce, 0xd4, 0xc5, 0xb5, 0x98, - 0x8c, 0x93, 0xa9, 0xd2, 0x6a, 0x64, 0x6c, 0x50, 0xab, 0x13, 0x16, 0xc2, - 0x83, 0x92, 0x7c, 0x00, 0x00, 0x25, 0x00, 0x39, 0xae, 0x17, 0x7b, 0xc4, - 0x9b, 0x4d, 0xf0, 0x24, 0x89, 0xf9, 0xdb, 0x8b, 0x00, 0x66, 0x8f, 0x87, - 0x17, 0x65, 0x1d, 0x67, 0x00, 0x41, 0x58, 0xf8, 0xbb, 0x6a, 0xae, 0xd9, - 0x47, 0x5c, 0x79, 0x73, 0x09, 0x2e, 0x0b, 0x95, 0x46, 0x43, 0x50, 0x69, - 0x71, 0x4d, 0xb4, 0x4f, 0x38, 0xde, 0x49, 0x13, 0x16, 0xde, 0xde, 0x20, - 0x9c, 0xc3, 0x63, 0x48, 0x8c, 0xd4, 0xa5, 0x20, 0xbd, 0x7e, 0x1d, 0xa9, - 0x55, 0x40, 0x43, 0x17, 0xb1, 0xd1, 0xd6, 0xaa, 0x18, 0x02, 0xd6, 0x48, - 0xcb, 0xc6, 0xaf, 0xf7, 0x4f, 0xf1, 0xff, 0x25, 0xc5, 0x8e, 0x79, 0x44, - 0x2f, 0x5f, 0x42, 0x46, 0x4c, 0x69, 0x38, 0xc5, 0x5c, 0xd7, 0xe0, 0xbb, - 0x0f, 0x9e, 0x52, 0x6c, 0x83, 0x80, 0xff, 0xf1, 0x50, 0x80, 0x31, 0x7f, - 0xfc, 0x21, 0x1a, 0xcc, 0xea, 0xff, 0xf7, 0x00, 0x00, 0x90, 0xba, 0x90, - 0xa8, 0x28, 0x29, 0x0a, 0xa9, 0x08, 0xc2, 0x03, 0x8e, 0x40, 0x00, 0x01, - 0xad, 0xf1, 0xcf, 0x9f, 0x09, 0xed, 0xe7, 0xd6, 0xba, 0xf2, 0x31, 0x97, - 0x52, 0x08, 0xac, 0xb8, 0x20, 0x9d, 0xd8, 0x30, 0xb8, 0xa2, 0x50, 0x05, - 0xec, 0xf8, 0x47, 0x8c, 0x46, 0xbe, 0x36, 0x6c, 0x88, 0xb7, 0x1f, 0xd3, - 0x6c, 0x45, 0xa0, 0x2e, 0x0e, 0xa7, 0xae, 0xe6, 0x90, 0x1b, 0x46, 0x49, - 0x04, 0x61, 0x88, 0x2c, 0xba, 0xd1, 0x64, 0x74, 0xf0, 0x8b, 0xf3, 0xc1, - 0xe4, 0x70, 0x15, 0x36, 0xc8, 0x00, 0xe5, 0x30, 0xab, 0x0a, 0x06, 0xb3, - 0x44, 0x30, 0x80, 0x31, 0xda, 0x84, 0x12, 0xc8, 0x52, 0x80, 0x20, 0x30, - 0xf2, 0xb2, 0x31, 0x03, 0xd1, 0x14, 0x7e, 0xad, 0xe8, 0x80, 0x05, 0xab, - 0x74, 0x3d, 0x47, 0x78, 0x30, 0x74, 0x40, 0x90, 0x29, 0x41, 0x95, 0x14, - 0xb7, 0xb6, 0xa0, 0x7e, 0xb2, 0xfb, 0x77, 0xae, 0xfd, 0xcb, 0x08, 0xce, - 0x26, 0x08, 0xc5, 0x74, 0xc9, 0x88, 0x13, 0x75, 0x32, 0x7c, 0xfc, 0x7a, - 0x33, 0x2e, 0x12, 0xa2, 0x42, 0x6b, 0x5a, 0xda, 0xaf, 0x3b, 0x86, 0xfe, - 0x7f, 0xf1, 0xfe, 0x3f, 0xbf, 0xaa, 0x66, 0x68, 0x56, 0xb3, 0xb9, 0x26, - 0x20, 0xa2, 0x07, 0x6e, 0x96, 0x00, 0xb9, 0x88, 0xc5, 0x4d, 0xd4, 0x53, - 0x33, 0xd9, 0xbb, 0xe5, 0xad, 0x6a, 0x00, 0x51, 0xab, 0x40, 0x24, 0xa5, - 0xef, 0x51, 0x3b, 0xca, 0xca, 0x00, 0xf8, 0x99, 0xaa, 0x66, 0xcf, 0xe4, - 0x3b, 0x2d, 0xf3, 0x90, 0x7c, 0xf5, 0x1e, 0x99, 0x04, 0xeb, 0xf9, 0x6e, - 0x0f, 0x2d, 0xda, 0x7c, 0xb7, 0x60, 0x8f, 0xfb, 0xfb, 0x68, 0x06, 0x49, - 0x71, 0xdd, 0xc3, 0x9a, 0x87, 0x92, 0x45, 0x5e, 0xa5, 0x92, 0xfa, 0xf0, - 0xa8, 0xdf, 0x7d, 0x39, 0xa4, 0x9b, 0x32, 0xf2, 0xcf, 0x53, 0x73, 0x1b, - 0xdb, 0x7c, 0x85, 0x8e, 0x13, 0x61, 0x83, 0x58, 0x50, 0x72, 0x4f, 0x82, - 0x90, 0xa9, 0x52, 0xa0, 0x00, 0xa8, 0x3d, 0x79, 0xa7, 0x1c, 0xdd, 0x5f, - 0x26, 0x5d, 0x80, 0x91, 0xb2, 0xab, 0x6e, 0xc1, 0xd9, 0x19, 0x08, 0xd6, - 0xfd, 0x7c, 0xd1, 0x9c, 0x62, 0x4e, 0x98, 0xa5, 0xf6, 0xd1, 0x22, 0x57, - 0xb3, 0xbd, 0x99, 0x03, 0x1c, 0x00, 0xc5, 0xf2, 0x15, 0x22, 0x38, 0x0c, - 0x6f, 0x22, 0xd3, 0x8e, 0x1b, 0x07, 0x5b, 0x3e, 0xbc, 0x0d, 0x6a, 0x67, - 0x9c, 0x6a, 0x77, 0x4d, 0x93, 0xa4, 0xa5, 0x42, 0xb7, 0x40, 0x34, 0xf6, - 0x4e, 0xd1, 0x38, 0xb8, 0xd1, 0x44, 0x02, 0x65, 0xb8, 0x2e, 0xbf, 0x05, - 0x39, 0x63, 0x95, 0xea, 0xff, 0x62, 0xec, 0xb5, 0xa2, 0xd7, 0x8e, 0x11, - 0x31, 0xab, 0x32, 0x56, 0x38, 0xd8, 0xcd, 0x37, 0x94, 0x6a, 0xfe, 0xc9, - 0xf1, 0x9d, 0x12, 0x81, 0xc0, 0xff, 0xf1, 0x50, 0x80, 0x2d, 0x1f, 0xfc, - 0x21, 0x1a, 0xcd, 0xfb, 0xd3, 0x7f, 0x00, 0x20, 0x94, 0xb1, 0xc2, 0x6c, - 0x4c, 0x79, 0x43, 0x05, 0x50, 0xc2, 0x50, 0xc0, 0x95, 0x00, 0x0a, 0x95, - 0xed, 0xe1, 0x50, 0x0a, 0xeb, 0xdb, 0xf4, 0xeb, 0x7e, 0x7d, 0xfe, 0x3d, - 0xfe, 0xb8, 0xe3, 0xdf, 0xf5, 0xb0, 0x5d, 0x5f, 0x52, 0x8e, 0x9a, 0x8d, - 0x7c, 0x9f, 0x37, 0xbb, 0xe8, 0x94, 0x09, 0x25, 0xc3, 0x22, 0x15, 0x94, - 0xe2, 0xd8, 0x19, 0xef, 0x12, 0x5e, 0x2f, 0x18, 0xbd, 0xf6, 0x4e, 0x44, - 0x2a, 0x05, 0xd1, 0xa1, 0x80, 0x15, 0x6a, 0x45, 0x00, 0xfa, 0xe7, 0x4d, - 0x74, 0xa4, 0x68, 0x31, 0x13, 0x89, 0xc0, 0x04, 0xff, 0x6e, 0x36, 0xb0, - 0x80, 0x99, 0x2e, 0x47, 0x3d, 0x20, 0x2e, 0x6d, 0x9b, 0x19, 0x61, 0x75, - 0xe2, 0x3f, 0x18, 0xd9, 0x16, 0xa8, 0x86, 0x6d, 0xd8, 0x85, 0x8a, 0x85, - 0x13, 0xbb, 0x4f, 0xdc, 0xf3, 0x94, 0x24, 0xa3, 0x78, 0x20, 0x9b, 0x33, - 0xce, 0x2a, 0xed, 0x10, 0x64, 0xcd, 0x20, 0xdb, 0x24, 0x32, 0x84, 0xec, - 0x56, 0x53, 0x50, 0xad, 0x7f, 0x1e, 0x56, 0x55, 0x94, 0xbd, 0x74, 0xc8, - 0x40, 0x56, 0x82, 0x7a, 0x76, 0x00, 0x63, 0xa2, 0x8d, 0xf1, 0x9a, 0x1c, - 0x64, 0x5f, 0x94, 0x00, 0x1d, 0xfd, 0x79, 0x9c, 0xc5, 0xdd, 0x6a, 0xf1, - 0x19, 0xa9, 0x1c, 0xf4, 0x00, 0x5f, 0xa2, 0x18, 0x75, 0xcd, 0xdd, 0x02, - 0x28, 0x0f, 0x1b, 0xc5, 0xc1, 0x15, 0x54, 0x80, 0xea, 0x6f, 0x4b, 0x14, - 0x71, 0xd3, 0x59, 0xcd, 0x07, 0x2f, 0x0b, 0x29, 0x7e, 0xfe, 0x88, 0x16, - 0xd4, 0x6e, 0x35, 0x00, 0xe5, 0x54, 0x45, 0xbe, 0xc1, 0x03, 0x05, 0x15, - 0x8a, 0x9e, 0x12, 0x76, 0x51, 0x2b, 0x2b, 0x12, 0x93, 0x42, 0xf3, 0x13, - 0x6b, 0xea, 0xfc, 0x4c, 0x48, 0x5b, 0xa5, 0x2c, 0x4c, 0xab, 0x23, 0x0e, - 0xc3, 0x27, 0xf8, 0x00, 0x00, 0x00, 0x0d, 0xcb, 0x9b, 0x45, 0x6e, 0xe8, - 0x80, 0x08, 0x88, 0x89, 0x18, 0x5b, 0x7c, 0xa6, 0xcf, 0xc0, 0x01, 0x8c, - 0xa6, 0x2e, 0x40, 0x25, 0x5b, 0x1d, 0x1e, 0x82, 0x61, 0x9c, 0x49, 0x85, - 0xa8, 0x31, 0x24, 0x06, 0x06, 0xe9, 0x48, 0x04, 0x62, 0x3e, 0x03, 0xb5, - 0xa8, 0xc6, 0xd0, 0xc9, 0x0e, 0x1b, 0x09, 0x4a, 0xdc, 0x6c, 0x75, 0x12, - 0x2b, 0x63, 0x02, 0x10, 0x83, 0x84, 0x1c, 0x28, 0x2a, 0x99, 0x6c, 0x67, - 0xa5, 0x80, 0x8c, 0x33, 0x2d, 0x8e, 0xd3, 0x1e, 0x8f, 0xfa, 0x7f, 0xac, - 0x44, 0x45, 0x5e, 0x21, 0x86, 0x80, 0x5e, 0xd4, 0xd9, 0x94, 0x17, 0xdb, - 0x77, 0xba, 0x76, 0x95, 0x8e, 0xff, 0xf1, 0x50, 0x80, 0x2e, 0x3f, 0xfc, - 0x21, 0x1a, 0xce, 0xea, 0x5a, 0x7f, 0x40, 0x40, 0x97, 0xb2, 0x32, 0x2c, - 0x30, 0x34, 0x1c, 0xb9, 0x82, 0x21, 0x52, 0x40, 0x54, 0x30, 0x23, 0x2d, - 0xc5, 0xf8, 0xf3, 0xe3, 0xcf, 0xbd, 0xe5, 0xb8, 0xe7, 0x8f, 0x16, 0x12, - 0xaf, 0x9f, 0xdf, 0xf6, 0xf6, 0xdf, 0xe3, 0xfa, 0xfe, 0x3f, 0x3f, 0xbb, - 0xef, 0xf6, 0xf6, 0xd0, 0x67, 0x9e, 0xfb, 0x37, 0x5b, 0xaa, 0xc1, 0x5f, - 0x6d, 0x3f, 0x07, 0xa3, 0x1d, 0x2f, 0x24, 0xfc, 0xb5, 0x94, 0xf2, 0xa1, - 0xe6, 0xb4, 0x58, 0x81, 0x1c, 0xe7, 0x5c, 0x15, 0x9f, 0x02, 0x99, 0x8e, - 0x70, 0x42, 0xb4, 0x94, 0x69, 0x1a, 0x1d, 0x08, 0x94, 0x0e, 0x94, 0xf3, - 0xe9, 0x3f, 0x37, 0xc6, 0x7e, 0x16, 0xd2, 0x6b, 0x42, 0xf3, 0xca, 0x6f, - 0x28, 0x64, 0x54, 0x50, 0xcc, 0x5e, 0xfe, 0x37, 0xe4, 0x9b, 0x09, 0xce, - 0x90, 0x9b, 0xda, 0x17, 0x80, 0x26, 0x0a, 0xd1, 0xfd, 0x33, 0x48, 0x22, - 0x05, 0xef, 0xc2, 0x57, 0x59, 0x42, 0xc0, 0x1a, 0x00, 0x46, 0x38, 0xe5, - 0x86, 0xa3, 0x0c, 0x21, 0xd9, 0xe7, 0x72, 0x24, 0x9b, 0xed, 0xe4, 0x04, - 0xcb, 0x0e, 0x2c, 0x06, 0x98, 0x90, 0x27, 0x8e, 0x00, 0x8b, 0x0f, 0x63, - 0x93, 0x07, 0x29, 0x5c, 0x8c, 0xeb, 0xfa, 0x1e, 0x50, 0x63, 0xef, 0x8d, - 0xd2, 0xb3, 0x9a, 0x21, 0x61, 0xc6, 0x22, 0xa4, 0x03, 0xf2, 0xe3, 0x7d, - 0x7c, 0x77, 0x19, 0x9d, 0x95, 0xd9, 0xbd, 0x33, 0xfe, 0x85, 0xe9, 0xa8, - 0x8c, 0x2a, 0xb8, 0x5f, 0x3d, 0x63, 0x37, 0x25, 0xa2, 0x9d, 0x59, 0xc9, - 0xa6, 0x2d, 0xa5, 0xc6, 0x6f, 0x57, 0xdd, 0xb6, 0x8b, 0x95, 0x37, 0x88, - 0x10, 0x07, 0xe9, 0x81, 0xcb, 0x11, 0x57, 0xa3, 0x09, 0xf5, 0xc3, 0xd7, - 0xf0, 0x22, 0x33, 0x1e, 0xfc, 0x27, 0x65, 0x56, 0x46, 0x42, 0xec, 0x67, - 0x2a, 0x5e, 0xca, 0x86, 0xb2, 0xb0, 0xec, 0x32, 0x7f, 0x80, 0x38, 0xe4, - 0x00, 0x00, 0x95, 0xbc, 0xf6, 0xdc, 0xcd, 0x55, 0x33, 0x38, 0xee, 0x68, - 0x00, 0xf8, 0xc5, 0x81, 0x74, 0x8a, 0xe4, 0x49, 0x41, 0x07, 0x68, 0xc7, - 0xd9, 0xa9, 0x5b, 0xa7, 0x21, 0x08, 0x20, 0x00, 0xcf, 0x8b, 0xc9, 0xda, - 0x93, 0x44, 0xb6, 0xfe, 0x60, 0xc6, 0xa1, 0x79, 0x29, 0x28, 0x1e, 0x6f, - 0x5f, 0xea, 0xa4, 0x4b, 0x92, 0x11, 0x25, 0xc7, 0xd8, 0xc0, 0xe7, 0x5d, - 0x0e, 0xb5, 0x32, 0x99, 0x45, 0x0d, 0x6c, 0x12, 0xc7, 0x9e, 0x0d, 0x3a, - 0x93, 0x2b, 0xcb, 0x0c, 0x23, 0xa1, 0xfe, 0x7f, 0xe3, 0x9a, 0x38, 0xcc, - 0x6f, 0xda, 0x25, 0xaf, 0x62, 0xf2, 0x0b, 0xb8, 0x37, 0xe1, 0xe7, 0xd8, - 0x00, 0x70, 0xff, 0xf1, 0x50, 0x80, 0x32, 0x3f, 0xfc, 0x21, 0x1a, 0xce, - 0x9b, 0xbf, 0x5b, 0xbf, 0xdf, 0x96, 0xb2, 0x32, 0x2c, 0x30, 0x6b, 0x0b, - 0x0e, 0xc3, 0x27, 0x53, 0x30, 0xdc, 0x50, 0x12, 0xa4, 0xd7, 0x27, 0x5c, - 0xd5, 0x42, 0x52, 0xa5, 0x45, 0x44, 0xcf, 0x6f, 0x57, 0x7c, 0x7b, 0xf5, - 0xdf, 0xdd, 0xe7, 0xd7, 0x5f, 0x01, 0xef, 0xca, 0xfb, 0x53, 0x0b, 0x1a, - 0xe0, 0x5a, 0x92, 0xa4, 0x9b, 0x56, 0xa8, 0xa7, 0xbd, 0x4a, 0xdf, 0x89, - 0x39, 0x95, 0x5e, 0x6e, 0x98, 0xbb, 0x73, 0x1e, 0x04, 0x86, 0x71, 0xd3, - 0x35, 0x7c, 0x67, 0x05, 0xba, 0x58, 0xe3, 0x40, 0xb0, 0x25, 0xf8, 0x04, - 0xee, 0x43, 0x20, 0xa7, 0x8c, 0xb0, 0xab, 0x08, 0xd4, 0x42, 0x77, 0xfe, - 0xe7, 0x18, 0x51, 0xd7, 0xdf, 0x15, 0x48, 0x91, 0x3e, 0x14, 0x00, 0x00, - 0x6e, 0x14, 0x70, 0x17, 0x55, 0x30, 0xb4, 0x0e, 0x6f, 0x1d, 0x3a, 0x51, - 0x40, 0xce, 0x29, 0x11, 0x8f, 0x3b, 0x01, 0xbb, 0x15, 0x25, 0x88, 0xad, - 0xbf, 0x5e, 0xef, 0xa8, 0x5c, 0x15, 0x86, 0xfc, 0x16, 0x54, 0x50, 0x42, - 0x66, 0x39, 0x1c, 0xf8, 0x08, 0x9c, 0x2a, 0x95, 0xb1, 0x38, 0xcf, 0x12, - 0xb0, 0xc2, 0x13, 0x32, 0x6f, 0xf9, 0xbe, 0x12, 0xaa, 0xa2, 0x4b, 0x67, - 0xa0, 0x35, 0x92, 0x8a, 0x96, 0x1b, 0xf5, 0x64, 0x02, 0x6e, 0xa2, 0x32, - 0xbe, 0xa9, 0x8e, 0x89, 0xdc, 0xfc, 0x46, 0xfa, 0xfe, 0x90, 0x48, 0x4f, - 0xc6, 0x3b, 0x93, 0xb9, 0xba, 0xc4, 0x6d, 0x3a, 0x56, 0xfe, 0x3e, 0xc5, - 0xe6, 0xe0, 0x92, 0xbd, 0xb1, 0x16, 0xd8, 0xa4, 0x26, 0x4c, 0x6c, 0x20, - 0x4c, 0x7d, 0x27, 0x03, 0x28, 0x66, 0x01, 0x38, 0xbe, 0x27, 0xb1, 0xa5, - 0x93, 0xad, 0xfc, 0x9e, 0xb6, 0x6f, 0x0b, 0xef, 0xaf, 0xd7, 0xa7, 0xe8, - 0x08, 0x28, 0x83, 0x58, 0x9c, 0xb5, 0x39, 0xa6, 0x35, 0x33, 0xde, 0xca, - 0x4d, 0x3f, 0x4f, 0x68, 0x8e, 0x85, 0xeb, 0x88, 0x83, 0x92, 0x7b, 0x52, - 0x83, 0xae, 0x02, 0x97, 0x2d, 0x74, 0x71, 0x10, 0x6c, 0x2e, 0x4e, 0x78, - 0x03, 0xae, 0x68, 0x25, 0x00, 0x26, 0xf5, 0x93, 0x5b, 0x55, 0x5e, 0x4a, - 0xab, 0x06, 0xb7, 0xd8, 0xe1, 0xaf, 0xee, 0x44, 0x15, 0x9b, 0x4a, 0x31, - 0x56, 0x57, 0x94, 0xb8, 0x01, 0x55, 0x81, 0x58, 0xd2, 0x99, 0x0d, 0x00, - 0x35, 0x19, 0x70, 0x69, 0xd8, 0x73, 0xe7, 0x11, 0x20, 0x23, 0x3c, 0x9a, - 0x38, 0xc7, 0x53, 0x20, 0x06, 0xc1, 0x72, 0x54, 0x3c, 0xf8, 0xa0, 0x10, - 0x32, 0xc0, 0x54, 0xcf, 0x49, 0x12, 0x1a, 0x38, 0x86, 0x6b, 0x7b, 0x1a, - 0x5b, 0x88, 0x71, 0xce, 0x2d, 0xad, 0x72, 0xf5, 0xee, 0x31, 0x5f, 0x3f, - 0xfc, 0xb3, 0xf0, 0x02, 0x94, 0xe7, 0xf0, 0xf2, 0x61, 0xa6, 0x18, 0x50, - 0x62, 0x91, 0xc0, 0x5d, 0x1b, 0x16, 0xbc, 0xec, 0x63, 0xbf, 0xff, 0x7d, - 0x11, 0x51, 0x01, 0x8c, 0x84, 0xc0, 0x70, 0xff, 0xf1, 0x50, 0x80, 0x2d, - 0xff, 0xfc, 0x21, 0x1a, 0xcd, 0xde, 0xeb, 0xdf, 0x1f, 0xff, 0x96, 0xb2, - 0x32, 0x2c, 0x30, 0x6b, 0x0c, 0xac, 0xcc, 0xc3, 0x50, 0xc0, 0x95, 0x0f, - 0xaf, 0x58, 0x4a, 0x2a, 0x71, 0xc8, 0xa8, 0x14, 0xbc, 0xd6, 0xab, 0xbe, - 0x39, 0xf8, 0xe3, 0x33, 0xeb, 0xa0, 0xf7, 0xf5, 0x9c, 0x39, 0xfa, 0xda, - 0xb2, 0xb4, 0x08, 0x9e, 0xdd, 0xc1, 0xf1, 0x52, 0xd1, 0xc0, 0x70, 0x1b, - 0x16, 0x72, 0x4f, 0x2d, 0x5b, 0xf5, 0x4e, 0x6a, 0x61, 0x13, 0xcc, 0xc4, - 0x95, 0x61, 0x56, 0x16, 0x73, 0x69, 0x5f, 0x1b, 0x84, 0x2d, 0xb6, 0x80, - 0xa1, 0x99, 0x2a, 0x41, 0x02, 0x80, 0x32, 0xc4, 0x98, 0x09, 0xd3, 0xce, - 0xe4, 0xe9, 0xa3, 0xcc, 0xcf, 0x0b, 0xbb, 0x7e, 0x3c, 0xd8, 0xb8, 0xec, - 0x82, 0x75, 0x94, 0x35, 0xe1, 0x79, 0xf7, 0x7f, 0xfd, 0x7f, 0xc9, 0x74, - 0x48, 0xe2, 0x65, 0x59, 0x15, 0xa2, 0xb5, 0x65, 0x88, 0x84, 0x11, 0xbb, - 0x47, 0xea, 0x38, 0x97, 0x96, 0x56, 0x46, 0x1b, 0x24, 0x46, 0x20, 0xa0, - 0xd4, 0xf1, 0x1a, 0x60, 0x84, 0x0d, 0xa1, 0x59, 0x02, 0x85, 0x63, 0xef, - 0x1b, 0x64, 0x59, 0xb1, 0xb1, 0x8a, 0x59, 0x72, 0x10, 0xcd, 0xf7, 0x85, - 0x4b, 0x5f, 0x81, 0x5e, 0x3e, 0x4c, 0xf3, 0xfc, 0x26, 0xff, 0xfe, 0x66, - 0xe8, 0x1f, 0x23, 0x7b, 0x0c, 0xd2, 0x7d, 0x89, 0x5d, 0x23, 0x34, 0x13, - 0xd4, 0x4c, 0xc3, 0x10, 0xa8, 0xe0, 0x20, 0x18, 0xc8, 0x18, 0xa3, 0x92, - 0x40, 0x8a, 0x9b, 0x31, 0x11, 0x83, 0x30, 0x21, 0x0b, 0x9a, 0x0d, 0x99, - 0xfd, 0x6d, 0x95, 0x91, 0x48, 0x90, 0xa1, 0x96, 0x02, 0x44, 0xa3, 0xbd, - 0x03, 0x22, 0x5c, 0x34, 0xee, 0xaa, 0x29, 0xb4, 0x10, 0x45, 0x0e, 0xa5, - 0x9a, 0x61, 0x91, 0x7d, 0x23, 0x91, 0x2d, 0x62, 0x65, 0x58, 0x60, 0xd6, - 0x16, 0x1d, 0x85, 0x47, 0xd0, 0x02, 0xf0, 0x00, 0x00, 0x6f, 0xbf, 0x6b, - 0xad, 0x77, 0x18, 0xd6, 0xd5, 0xa0, 0x13, 0x99, 0xd8, 0x70, 0x08, 0x92, - 0x8b, 0x2e, 0x06, 0xa2, 0x43, 0x84, 0x80, 0x90, 0x29, 0xdd, 0xe7, 0x2f, - 0xde, 0x44, 0x85, 0xad, 0xfe, 0x49, 0xdb, 0x39, 0xf0, 0x5a, 0x37, 0x04, - 0x32, 0x3c, 0xec, 0x24, 0x80, 0x1f, 0x45, 0xf3, 0x9d, 0x85, 0x31, 0x38, - 0xe1, 0x6a, 0x2b, 0x46, 0x85, 0xc4, 0x48, 0xf3, 0x4b, 0x2c, 0xe8, 0xa3, - 0x03, 0x48, 0x04, 0xc9, 0x5f, 0xa5, 0x91, 0x8a, 0xef, 0xa9, 0x81, 0x20, - 0x56, 0xe2, 0x61, 0xf6, 0x39, 0x34, 0xe7, 0x97, 0x14, 0x39, 0x8a, 0x51, - 0x43, 0xab, 0x95, 0x8d, 0x72, 0x9a, 0x98, 0x06, 0xbf, 0x5f, 0xdb, 0x15, - 0x34, 0x0e, 0xff, 0xf1, 0x50, 0x80, 0x31, 0x3f, 0xfc, 0x21, 0x1a, 0xcf, - 0xd3, 0xb6, 0x3f, 0x00, 0x00, 0x93, 0xb1, 0xc2, 0x6c, 0x30, 0x6b, 0x0b, - 0x0e, 0xc2, 0x82, 0x95, 0xa1, 0x18, 0x30, 0x10, 0x15, 0xe7, 0xb2, 0xa2, - 0xa2, 0x88, 0x0a, 0x0e, 0xb7, 0x92, 0xbd, 0xb5, 0xf3, 0xe7, 0xcf, 0xdc, - 0x29, 0xcf, 0xa2, 0xbb, 0x87, 0xec, 0x02, 0xce, 0xe1, 0xa3, 0xcd, 0xec, - 0xd2, 0x31, 0x06, 0x47, 0x00, 0xda, 0x64, 0x80, 0xc4, 0x93, 0x24, 0xbb, - 0x99, 0x6c, 0xd7, 0x25, 0x97, 0x52, 0x47, 0x57, 0x95, 0x22, 0x90, 0xd4, - 0x81, 0x51, 0xae, 0x75, 0xd4, 0x9c, 0x75, 0xe3, 0x7a, 0x24, 0x2c, 0xe5, - 0x5c, 0xd2, 0x59, 0xdc, 0xa3, 0x05, 0x92, 0xdd, 0x43, 0xd8, 0x38, 0x1a, - 0xe3, 0xa7, 0x93, 0x10, 0x02, 0x56, 0x76, 0x79, 0x24, 0x01, 0x27, 0x91, - 0x9c, 0x44, 0xe4, 0x04, 0x35, 0x0e, 0x51, 0xbb, 0x04, 0xc2, 0x50, 0x18, - 0x60, 0xb5, 0x70, 0x47, 0xb1, 0x61, 0xc4, 0xd7, 0x61, 0x21, 0xa9, 0xfb, - 0xbf, 0xf6, 0x3f, 0x92, 0xcd, 0xd5, 0x56, 0x31, 0x51, 0x4e, 0x6c, 0x8c, - 0x01, 0x8c, 0xa3, 0x2d, 0x4f, 0x3c, 0xf8, 0xbd, 0x0c, 0x72, 0xb1, 0x38, - 0x05, 0x48, 0xcf, 0x56, 0xf0, 0x2e, 0x7c, 0xff, 0x83, 0xa2, 0x18, 0x95, - 0x86, 0x12, 0x15, 0x40, 0x5d, 0x53, 0x53, 0x5e, 0x50, 0xbb, 0x09, 0x08, - 0x44, 0xa1, 0x57, 0x94, 0xb0, 0x8e, 0xb6, 0x62, 0x00, 0x26, 0x2d, 0x28, - 0x5d, 0x84, 0x30, 0xad, 0x4e, 0x14, 0x89, 0x40, 0x14, 0xb8, 0xba, 0xaa, - 0x85, 0xd1, 0x4b, 0x47, 0xb1, 0x7d, 0x61, 0x42, 0xb0, 0xff, 0xda, 0xea, - 0x67, 0x77, 0x77, 0x56, 0x3b, 0xe9, 0xd4, 0x45, 0x55, 0x57, 0x4b, 0x56, - 0x73, 0x47, 0x6a, 0xde, 0x2f, 0x60, 0x27, 0x8f, 0x34, 0x25, 0x66, 0x1a, - 0x12, 0x92, 0x8a, 0x85, 0x0b, 0xda, 0xd5, 0xcd, 0xa6, 0x6a, 0x23, 0x93, - 0xa6, 0x5a, 0x18, 0x76, 0x19, 0x1f, 0xc0, 0x00, 0x00, 0x00, 0x67, 0x3a, - 0xd5, 0x79, 0xfb, 0x6a, 0x29, 0x55, 0x70, 0x23, 0x43, 0xfc, 0x30, 0xf7, - 0x7c, 0xf0, 0x46, 0x98, 0x20, 0x69, 0x68, 0x61, 0xc6, 0x16, 0x92, 0x85, - 0x49, 0xa2, 0xca, 0x38, 0x9c, 0x3c, 0xa4, 0x42, 0xc3, 0xea, 0x3c, 0x7e, - 0x56, 0xe3, 0xd6, 0x91, 0x23, 0xc4, 0x29, 0x44, 0x90, 0xe6, 0x8b, 0x86, - 0xd3, 0xf9, 0x0f, 0x9a, 0xb4, 0xa1, 0x00, 0x29, 0xf4, 0xf1, 0x38, 0x44, - 0x81, 0x7b, 0x1a, 0xa1, 0xc3, 0x16, 0xfb, 0x1e, 0x51, 0xa7, 0x2d, 0x09, - 0x54, 0x70, 0x1b, 0xc1, 0xb0, 0xf6, 0x49, 0xa6, 0xee, 0xc0, 0x66, 0x0e, - 0x4c, 0x42, 0xac, 0x99, 0xb0, 0x1a, 0xe7, 0xe3, 0xfc, 0x9c, 0x97, 0xc1, - 0xa5, 0x94, 0x22, 0x49, 0x8b, 0xc6, 0xba, 0x9d, 0x96, 0xca, 0x34, 0x64, - 0xce, 0x2e, 0x29, 0x7c, 0xbf, 0xf2, 0x73, 0x83, 0x04, 0x25, 0xc0, 0xff, - 0xf1, 0x50, 0x80, 0x2f, 0xff, 0xfc, 0x21, 0x1a, 0xcd, 0xf8, 0xbf, 0xff, - 0x00, 0x28, 0x93, 0xb8, 0x30, 0xec, 0x2c, 0x3b, 0x0c, 0x95, 0x50, 0x81, - 0x60, 0x98, 0x58, 0x60, 0xd6, 0xd7, 0x80, 0x00, 0x00, 0xd6, 0xb7, 0xbd, - 0x67, 0x5f, 0x7f, 0xd3, 0xea, 0xfd, 0x82, 0x9e, 0xe1, 0xa9, 0xcd, 0x94, - 0x34, 0x0d, 0xd9, 0xa9, 0xac, 0x29, 0x11, 0x4c, 0x55, 0xa1, 0xd0, 0x34, - 0xa2, 0x64, 0x70, 0x18, 0x55, 0xca, 0x66, 0x53, 0x98, 0x69, 0x21, 0x35, - 0x7d, 0x27, 0x57, 0x27, 0x5e, 0x4d, 0x0e, 0x74, 0x85, 0x02, 0x46, 0x83, - 0xe8, 0x00, 0x65, 0x1a, 0xb4, 0xfc, 0xa4, 0xb6, 0xcc, 0xc2, 0x8d, 0x1b, - 0xb8, 0x12, 0xaf, 0x6e, 0x08, 0x3e, 0x18, 0x51, 0x46, 0x81, 0x60, 0xf5, - 0xc9, 0x52, 0xd4, 0x00, 0x97, 0xdb, 0x73, 0x32, 0x1a, 0xc3, 0x10, 0x23, - 0xcf, 0xfc, 0xba, 0xaa, 0x58, 0x18, 0x33, 0x3c, 0x13, 0x9d, 0xea, 0x40, - 0xc2, 0x72, 0x21, 0x70, 0x6f, 0xf9, 0x1f, 0xb4, 0x79, 0x0c, 0xa5, 0x24, - 0xa1, 0xc6, 0xce, 0x55, 0x58, 0x82, 0x19, 0x2f, 0x4f, 0xeb, 0xd8, 0xe3, - 0x10, 0xcf, 0x0b, 0xb5, 0xf3, 0x21, 0x79, 0x17, 0x77, 0x9a, 0x37, 0xff, - 0x3d, 0x9d, 0xf3, 0x6c, 0x48, 0xdd, 0x76, 0x8a, 0xbc, 0x50, 0x0b, 0xc6, - 0x67, 0x52, 0x67, 0x23, 0x59, 0x82, 0xb1, 0x9c, 0x0d, 0x46, 0x51, 0x75, - 0xd7, 0xdf, 0x34, 0x50, 0x12, 0xc4, 0xa8, 0xad, 0xd4, 0xe6, 0xd6, 0xd7, - 0x65, 0x45, 0x90, 0x00, 0x2b, 0x35, 0xa6, 0x4e, 0xcb, 0x2f, 0xba, 0x92, - 0xd3, 0x34, 0xd3, 0x42, 0x8d, 0xd7, 0x4b, 0x84, 0xc9, 0x30, 0x9a, 0x7f, - 0x1e, 0xe3, 0x4b, 0x8c, 0x15, 0x98, 0x23, 0xc0, 0x20, 0x9d, 0x62, 0x97, - 0x79, 0x5c, 0xc5, 0x01, 0x32, 0xeb, 0xa4, 0x68, 0x7f, 0x44, 0x89, 0x1b, - 0x95, 0x77, 0xe7, 0x97, 0x7e, 0x99, 0xb3, 0xbb, 0xa4, 0x9d, 0xb6, 0x06, - 0xc3, 0xb0, 0xa0, 0xec, 0x32, 0x3f, 0x80, 0x35, 0xb0, 0x00, 0x00, 0xce, - 0xf8, 0xe2, 0xaf, 0xbd, 0x56, 0xbc, 0x5e, 0x25, 0x82, 0x8e, 0xd8, 0xe7, - 0xff, 0xd2, 0xb2, 0xbb, 0xc2, 0x8b, 0x18, 0x00, 0xa7, 0x8d, 0x48, 0x6b, - 0x25, 0x36, 0xe2, 0xc2, 0x70, 0x16, 0x42, 0x82, 0x9f, 0x14, 0x39, 0xa0, - 0x84, 0x4b, 0x84, 0x79, 0xf6, 0x86, 0xd1, 0x08, 0xc2, 0x24, 0x5b, 0x7c, - 0x27, 0x33, 0x1d, 0x77, 0x9d, 0x88, 0xb8, 0x54, 0xb8, 0x95, 0x8b, 0x10, - 0xa3, 0xf0, 0x95, 0x8b, 0xa3, 0xd1, 0x9a, 0x69, 0xad, 0x87, 0xb5, 0xf9, - 0x57, 0xa3, 0xb0, 0x04, 0x50, 0xe9, 0x03, 0x06, 0x7b, 0xa8, 0x43, 0xa8, - 0x37, 0x60, 0x86, 0x8a, 0x8d, 0x0e, 0x09, 0x9b, 0xdb, 0x80, 0xca, 0xf2, - 0x2a, 0xa0, 0x5f, 0x9d, 0xf5, 0x3e, 0x83, 0x6a, 0x2c, 0x1c, 0xff, 0xf1, - 0x50, 0x80, 0x31, 0x5f, 0xfc, 0x21, 0x1a, 0xcb, 0xfd, 0xa1, 0xff, 0x00, - 0x00, 0x90, 0xba, 0x20, 0xec, 0x28, 0x29, 0x29, 0x99, 0x8b, 0x01, 0x00, - 0x00, 0xd6, 0xc0, 0x00, 0xeb, 0x36, 0x7c, 0x7c, 0x7e, 0x7e, 0x35, 0xec, - 0x14, 0x3f, 0x48, 0x4d, 0x6f, 0xcf, 0x74, 0x84, 0x9c, 0x76, 0x8d, 0x5f, - 0x87, 0xb4, 0x16, 0x75, 0x13, 0xd4, 0x1f, 0x40, 0x40, 0x9d, 0xf4, 0xc0, - 0x88, 0x04, 0x7f, 0x44, 0x4a, 0x9b, 0x9d, 0x19, 0xa8, 0x98, 0x44, 0x4b, - 0x93, 0x4d, 0xf4, 0xa7, 0xd1, 0xa6, 0xb9, 0x97, 0xaa, 0x24, 0x14, 0x53, - 0x1b, 0x71, 0xf8, 0xe2, 0x21, 0x65, 0x7e, 0x78, 0x04, 0x48, 0xda, 0xd8, - 0x17, 0x0a, 0x50, 0xf9, 0x9a, 0xeb, 0x3c, 0xca, 0xdb, 0xf1, 0xa2, 0x19, - 0xa9, 0x76, 0xce, 0x61, 0x45, 0x78, 0xfd, 0x29, 0xc9, 0x1d, 0xc8, 0x54, - 0xbc, 0x53, 0xce, 0x4b, 0xca, 0x30, 0xa1, 0x0b, 0xaf, 0x98, 0xc6, 0x5e, - 0x90, 0x52, 0x85, 0xaf, 0x88, 0xff, 0x13, 0x5d, 0xf6, 0x88, 0x89, 0x44, - 0x6f, 0xc6, 0x24, 0xca, 0x85, 0xa7, 0x2a, 0x6f, 0xf9, 0x4f, 0x83, 0xcb, - 0x14, 0x92, 0xb4, 0xd5, 0x8b, 0x32, 0x18, 0x51, 0x97, 0x6d, 0xd0, 0xe5, - 0xaf, 0x8e, 0x55, 0x01, 0x96, 0xb5, 0xbd, 0x19, 0xa9, 0xa3, 0xd4, 0xb6, - 0x65, 0xcd, 0x55, 0x1d, 0x5c, 0x2d, 0xee, 0xae, 0x9f, 0xd2, 0xee, 0xa1, - 0x33, 0x35, 0xfe, 0x77, 0x6e, 0xd6, 0xeb, 0x31, 0xf0, 0xd3, 0xad, 0xbc, - 0x8b, 0x10, 0x4c, 0x0d, 0x1d, 0xd0, 0x14, 0xee, 0x81, 0x64, 0xbc, 0xac, - 0x7a, 0xf4, 0x56, 0xb8, 0x4b, 0x34, 0xd6, 0x62, 0x00, 0x80, 0x86, 0x80, - 0xc8, 0x15, 0x98, 0x90, 0x53, 0x59, 0x5b, 0xc0, 0x12, 0xbc, 0x58, 0x44, - 0x41, 0xed, 0x15, 0x9a, 0x24, 0xa5, 0x0d, 0xfb, 0xb6, 0xd5, 0x4e, 0x49, - 0x0d, 0x36, 0xd0, 0xa6, 0xc3, 0xe1, 0x71, 0x1c, 0x90, 0x0a, 0xa6, 0x36, - 0x0b, 0x29, 0x2b, 0x1b, 0x22, 0xc5, 0x06, 0xb0, 0xa0, 0xec, 0x32, 0x3f, - 0x82, 0xc0, 0x65, 0xb2, 0xf0, 0x58, 0x05, 0x73, 0xab, 0x5f, 0x7c, 0x53, - 0x19, 0xaa, 0xd0, 0x39, 0x84, 0x79, 0x56, 0xce, 0x55, 0xb8, 0x63, 0xe5, - 0xe2, 0x08, 0x4a, 0x7c, 0x26, 0x0d, 0x54, 0xae, 0x4f, 0x6f, 0xe2, 0x46, - 0x57, 0x14, 0x6a, 0x88, 0x24, 0xa0, 0x68, 0x26, 0x7e, 0x0b, 0xfb, 0x45, - 0xa9, 0x40, 0xfc, 0x1c, 0xe9, 0xd8, 0x3f, 0x4d, 0x52, 0x28, 0xa6, 0x6d, - 0xf4, 0x9a, 0x52, 0x9f, 0x31, 0x21, 0x01, 0x0b, 0xf2, 0x65, 0x54, 0xfe, - 0x19, 0x78, 0xa5, 0x17, 0xc1, 0x7e, 0x1f, 0xaa, 0xfa, 0x33, 0x53, 0x6c, - 0xa5, 0x5e, 0xf6, 0xa4, 0x11, 0x86, 0x4c, 0x0f, 0xd9, 0xf9, 0x55, 0xe0, - 0xd6, 0x65, 0x8b, 0x7b, 0xee, 0xea, 0x63, 0x04, 0x6f, 0xbc, 0x2c, 0xa8, - 0xa3, 0x1f, 0x23, 0xf5, 0xbe, 0x8a, 0x28, 0x07, 0xff, 0xf1, 0x50, 0x80, - 0x2e, 0xdf, 0xfc, 0x21, 0x1a, 0xcf, 0x5e, 0xe3, 0xff, 0x00, 0x00, 0x91, - 0xb6, 0xb1, 0xac, 0x28, 0x39, 0x42, 0x0c, 0xc6, 0xc6, 0x07, 0x9f, 0x00, - 0x0f, 0xd7, 0xf4, 0x00, 0x07, 0x57, 0x3d, 0xef, 0x3e, 0xba, 0xe7, 0x5d, - 0x74, 0x21, 0x55, 0x01, 0xf0, 0x72, 0xfe, 0xed, 0xc7, 0xec, 0x79, 0x26, - 0x19, 0x00, 0x71, 0x94, 0xae, 0x08, 0xdd, 0x4c, 0xc1, 0x5d, 0x32, 0xf1, - 0x8d, 0x3a, 0x10, 0xa3, 0x69, 0x77, 0x8b, 0x02, 0x64, 0x1c, 0xb2, 0xde, - 0xcf, 0xcb, 0x68, 0xeb, 0xf2, 0xea, 0x00, 0x07, 0xa7, 0x3e, 0xc9, 0x70, - 0x7f, 0xfe, 0x64, 0x06, 0xfb, 0x48, 0xd0, 0x1e, 0x4c, 0x17, 0x56, 0x55, - 0x0b, 0x80, 0xde, 0x48, 0x59, 0x5f, 0x90, 0xcb, 0x0e, 0xee, 0x19, 0xea, - 0x45, 0x96, 0x5f, 0x9b, 0x79, 0x8d, 0x4c, 0x0c, 0x72, 0x90, 0xa8, 0x17, - 0x6a, 0x01, 0x5d, 0x2f, 0xb1, 0x75, 0x7a, 0x35, 0x80, 0x17, 0x88, 0x5c, - 0x50, 0x03, 0xce, 0xf7, 0x4e, 0x27, 0x12, 0x12, 0xc4, 0x21, 0x65, 0x81, - 0x94, 0xe3, 0x0e, 0x77, 0x5f, 0xa1, 0xe1, 0x38, 0x9b, 0x22, 0x24, 0x6b, - 0x68, 0xd7, 0x63, 0xe7, 0x3a, 0xae, 0x23, 0xe6, 0x1b, 0x83, 0xba, 0x3c, - 0xf1, 0xb3, 0xa1, 0x4b, 0x7d, 0x0d, 0x23, 0x39, 0x65, 0xd0, 0x20, 0x17, - 0x9b, 0x46, 0x50, 0x77, 0xb7, 0xd6, 0xde, 0x42, 0xe0, 0x4a, 0x97, 0x26, - 0x28, 0x41, 0x35, 0x09, 0x84, 0xad, 0xca, 0x6a, 0x15, 0xf5, 0x2a, 0xa2, - 0x2a, 0x38, 0x89, 0x82, 0xba, 0x11, 0x3c, 0x21, 0x06, 0xa4, 0x06, 0xcf, - 0x6d, 0x4d, 0x05, 0x11, 0x70, 0x18, 0x84, 0x21, 0x1c, 0xd7, 0xdc, 0xd5, - 0x2c, 0xb8, 0x5c, 0xb5, 0x4b, 0x4e, 0x9e, 0x7e, 0x1a, 0x7a, 0x5d, 0x18, - 0x79, 0x7d, 0x7a, 0x71, 0xee, 0xdb, 0x2e, 0x4f, 0xc6, 0x46, 0xe0, 0xc3, - 0xb0, 0xa0, 0xec, 0x32, 0x3f, 0x80, 0xa9, 0x50, 0x54, 0x28, 0x80, 0x0a, - 0xdd, 0xef, 0x8d, 0x6e, 0xf0, 0xd9, 0x76, 0x04, 0xaf, 0x3b, 0xb9, 0x2b, - 0xe2, 0x09, 0xd0, 0x37, 0x82, 0xd8, 0xa0, 0x7c, 0xe8, 0xcb, 0x62, 0x32, - 0x42, 0x00, 0x94, 0xc6, 0x06, 0xd2, 0xc3, 0x36, 0x04, 0x15, 0xed, 0x73, - 0x28, 0x84, 0x13, 0xff, 0xe1, 0x97, 0x0a, 0x3e, 0x33, 0x4b, 0x81, 0xed, - 0x89, 0x34, 0xe2, 0xf2, 0x6c, 0x29, 0x55, 0x5f, 0xc1, 0xc4, 0x2c, 0x1a, - 0x9d, 0x51, 0x35, 0xfe, 0xef, 0x6f, 0xb8, 0xc7, 0x28, 0x5e, 0x08, 0x49, - 0x6e, 0xe5, 0x4f, 0xea, 0xb1, 0x35, 0x3c, 0x8c, 0x50, 0xa5, 0xf9, 0x73, - 0x54, 0x2c, 0x14, 0x58, 0xe6, 0x6d, 0x9b, 0xaa, 0xa8, 0x11, 0x78, 0xd9, - 0x29, 0x32, 0xea, 0x3f, 0x17, 0xe3, 0xf5, 0xe5, 0x88, 0x38, 0xff, 0xf1, - 0x50, 0x80, 0x2f, 0xbf, 0xfc, 0x21, 0x1a, 0xce, 0x5a, 0xce, 0x7f, 0x40, - 0x40, 0x92, 0xb1, 0xc2, 0x6c, 0x30, 0x6b, 0x0a, 0x8e, 0xc2, 0x42, 0x95, - 0xb0, 0xcc, 0x30, 0x20, 0x7b, 0x7a, 0x00, 0x00, 0x00, 0x5b, 0x25, 0x7c, - 0x47, 0x3f, 0x7f, 0x80, 0xdc, 0xae, 0x15, 0xa3, 0x3d, 0xcd, 0xff, 0xc8, - 0xb3, 0xf1, 0xa3, 0xda, 0x0a, 0x20, 0x43, 0x12, 0xf8, 0xdb, 0x16, 0x3e, - 0x3b, 0xe2, 0x2e, 0x9b, 0xda, 0x59, 0xeb, 0x52, 0xd8, 0x49, 0xe0, 0x33, - 0x24, 0xe6, 0x13, 0xed, 0xcc, 0x72, 0xec, 0x2d, 0x12, 0x72, 0xca, 0xb8, - 0xcd, 0x21, 0x4b, 0x0d, 0x29, 0x82, 0x4c, 0xe9, 0x3a, 0x98, 0xed, 0x08, - 0x37, 0x84, 0xa2, 0x48, 0x84, 0x55, 0xc5, 0x63, 0x53, 0xdd, 0xdf, 0xae, - 0x3f, 0x6c, 0x97, 0x3d, 0x7b, 0x0a, 0xbb, 0x10, 0x75, 0xae, 0xec, 0xcd, - 0x82, 0x0a, 0x69, 0x41, 0xf7, 0xa2, 0x43, 0x23, 0x7e, 0xf0, 0x14, 0xcd, - 0xfa, 0x3f, 0x9a, 0xfa, 0xfc, 0x99, 0xe6, 0x98, 0x47, 0x12, 0x03, 0x28, - 0xb0, 0xcc, 0xb8, 0xf1, 0x1e, 0xf2, 0x81, 0x9a, 0x62, 0x6f, 0x52, 0x0b, - 0x64, 0x43, 0x2d, 0x4d, 0x98, 0x55, 0x57, 0x9a, 0xf8, 0xe6, 0x82, 0x51, - 0x01, 0x7b, 0x41, 0x9d, 0x05, 0x29, 0x0d, 0x28, 0x00, 0xc1, 0x72, 0x15, - 0x0b, 0x2f, 0x35, 0x2b, 0xbc, 0xc4, 0x2c, 0x0a, 0x85, 0x2f, 0x34, 0xc2, - 0x58, 0x0c, 0xfb, 0xdd, 0x35, 0x21, 0x41, 0x54, 0x0c, 0x52, 0x42, 0x86, - 0xae, 0x42, 0xa6, 0xaf, 0x17, 0x67, 0x2a, 0xc2, 0x00, 0x89, 0x83, 0x98, - 0x41, 0xd4, 0x63, 0x7a, 0xe6, 0x24, 0xc0, 0x16, 0x11, 0x2f, 0xf8, 0x21, - 0x79, 0x63, 0x48, 0x65, 0x05, 0xcb, 0x4c, 0xe7, 0x1a, 0x60, 0x45, 0xb1, - 0x33, 0x69, 0x54, 0x54, 0xb9, 0x34, 0x8e, 0x9d, 0x30, 0x6e, 0xda, 0x63, - 0xa3, 0x0c, 0x86, 0x64, 0xac, 0x8c, 0x8b, 0x23, 0x0e, 0xc2, 0x83, 0xb0, - 0xc8, 0xfe, 0x0d, 0x76, 0xbc, 0xb6, 0x5e, 0x5b, 0x16, 0x62, 0xc0, 0xcc, - 0x8b, 0xbe, 0x74, 0xc9, 0x99, 0x5a, 0xd0, 0x45, 0x25, 0x62, 0xde, 0x92, - 0xfb, 0xee, 0xd7, 0x1c, 0xf5, 0xa8, 0x02, 0x5a, 0x2d, 0xc5, 0xf7, 0xcd, - 0x6d, 0x7b, 0xb6, 0x6d, 0x9f, 0xdd, 0xc1, 0x61, 0x56, 0x51, 0x69, 0x22, - 0x16, 0x54, 0x99, 0x92, 0xcb, 0x31, 0x95, 0x86, 0xa0, 0x3b, 0x0a, 0x45, - 0xc6, 0xd2, 0x75, 0x82, 0x1f, 0x08, 0x61, 0x03, 0x32, 0x35, 0x28, 0xc1, - 0x8e, 0x4c, 0xec, 0xc2, 0xc7, 0x34, 0x60, 0x9a, 0x60, 0x7a, 0x7d, 0x95, - 0xf9, 0xf5, 0x32, 0xd7, 0x80, 0xa7, 0x87, 0x63, 0xcd, 0x53, 0x21, 0xdb, - 0x07, 0x04, 0x04, 0x1b, 0x35, 0x86, 0x7a, 0x76, 0x2b, 0x35, 0xd9, 0x88, - 0xe7, 0x68, 0xff, 0x31, 0x90, 0x24, 0xe0, 0xff, 0xf1, 0x50, 0x80, 0x2c, - 0xff, 0xfc, 0x21, 0x1a, 0xce, 0x0a, 0xdf, 0xef, 0x40, 0x60, 0x98, 0xa1, - 0xb2, 0x6c, 0x2c, 0x6b, 0x0c, 0xb5, 0x82, 0x63, 0x71, 0x40, 0x54, 0x34, - 0x22, 0x9a, 0xfa, 0xdf, 0xae, 0x3b, 0xbc, 0x54, 0x00, 0x0e, 0xbc, 0x7d, - 0x7c, 0xf5, 0xab, 0xfb, 0x7b, 0x7a, 0xfb, 0xeb, 0xcf, 0xcf, 0xee, 0x0e, - 0xbd, 0x9b, 0xda, 0x1b, 0x14, 0xc3, 0x8d, 0xb2, 0x4d, 0xed, 0xdf, 0xe2, - 0xb5, 0xb1, 0x64, 0xd9, 0x58, 0xc1, 0x3e, 0xe6, 0xb1, 0x03, 0xea, 0x04, - 0x2e, 0x56, 0xac, 0x97, 0xf4, 0x43, 0x57, 0x96, 0x27, 0xe6, 0x1a, 0xed, - 0x83, 0x08, 0xa6, 0x53, 0x38, 0x07, 0x64, 0xa1, 0x2d, 0xd7, 0x76, 0x31, - 0x46, 0x18, 0xa2, 0xaf, 0x1c, 0x77, 0x5c, 0xaf, 0x05, 0xa3, 0x5d, 0x66, - 0x79, 0x58, 0xd5, 0xf4, 0xcf, 0x03, 0x82, 0xea, 0x32, 0x4d, 0x43, 0x00, - 0xbc, 0xe4, 0x13, 0x9d, 0xce, 0xb7, 0xdc, 0x32, 0x0b, 0x18, 0x46, 0xed, - 0x8b, 0x67, 0xa7, 0x19, 0xa8, 0x0b, 0xcf, 0xbf, 0x03, 0x0c, 0x31, 0xac, - 0xf8, 0xfb, 0x2a, 0x61, 0xc5, 0x8b, 0x15, 0x89, 0x79, 0xe9, 0xfa, 0x08, - 0x18, 0x17, 0x2c, 0x78, 0x85, 0x2e, 0x64, 0xb4, 0x42, 0x8e, 0x58, 0x17, - 0x30, 0x85, 0x13, 0x80, 0xdc, 0x96, 0x39, 0xa4, 0x1a, 0xde, 0x7c, 0xf8, - 0x3b, 0xed, 0x2f, 0x39, 0xb7, 0x13, 0x61, 0xdf, 0xc4, 0x0e, 0x1a, 0x74, - 0xef, 0x0c, 0xe0, 0xce, 0xd4, 0x67, 0x7f, 0x1e, 0x15, 0x39, 0xd4, 0xda, - 0x2b, 0x0a, 0x1a, 0x75, 0xf1, 0x92, 0x02, 0x02, 0xc2, 0x28, 0xc0, 0x02, - 0x37, 0xec, 0x2b, 0xfa, 0x49, 0xa8, 0x95, 0x8b, 0x82, 0x55, 0x71, 0x0a, - 0xe2, 0x46, 0xaf, 0x26, 0xe5, 0x9a, 0x0a, 0x5d, 0xb0, 0x31, 0xa6, 0xd0, - 0xcf, 0x44, 0xb6, 0x70, 0xe2, 0x39, 0x41, 0x81, 0x48, 0xb3, 0x01, 0x20, - 0xf3, 0x10, 0x56, 0x3d, 0x85, 0x8b, 0x22, 0x21, 0xd8, 0x54, 0x7f, 0x00, - 0x23, 0x5c, 0x80, 0x00, 0x37, 0xbe, 0x99, 0xc6, 0x4c, 0x32, 0x54, 0x07, - 0x25, 0x67, 0x6a, 0xae, 0x05, 0x76, 0xfc, 0xd2, 0xa4, 0x26, 0x35, 0x1a, - 0xc1, 0xd2, 0x66, 0xb3, 0x79, 0x4b, 0x2b, 0x1b, 0xd2, 0x84, 0x59, 0x8d, - 0xbb, 0xd5, 0xe2, 0x41, 0x64, 0xf7, 0x25, 0x9b, 0xa2, 0x3d, 0x6c, 0x25, - 0x67, 0xca, 0x33, 0x01, 0x9e, 0xef, 0x4f, 0x33, 0xe5, 0x5f, 0xf9, 0x38, - 0xc3, 0x0e, 0x5d, 0xf7, 0x67, 0x30, 0xf3, 0x70, 0x50, 0x61, 0x0d, 0x15, - 0xc7, 0xee, 0x0a, 0x82, 0x16, 0x06, 0x37, 0x25, 0xf5, 0x82, 0x20, 0x28, - 0x66, 0xff, 0xe3, 0xd4, 0xb0, 0x38, 0xff, 0xf1, 0x50, 0x80, 0x2f, 0x7f, - 0xfc, 0x21, 0x1a, 0xcf, 0xb9, 0xdb, 0xfd, 0x40, 0x04, 0x96, 0xb1, 0xb2, - 0x6c, 0x30, 0x6b, 0x0c, 0xac, 0xcc, 0xc2, 0x80, 0xa8, 0x60, 0x41, 0x3d, - 0xbd, 0x3a, 0xf9, 0xeb, 0x1b, 0xd3, 0xef, 0xf6, 0x01, 0xbd, 0x6b, 0xc6, - 0xb7, 0x7a, 0xe3, 0xe7, 0x8e, 0x7f, 0x5b, 0xeb, 0xdf, 0xe3, 0x80, 0x66, - 0x7a, 0xf8, 0x49, 0x80, 0x4b, 0x0b, 0x4f, 0x67, 0x15, 0x0f, 0xea, 0xd3, - 0xb8, 0xb0, 0xce, 0x49, 0xd2, 0xfe, 0xc9, 0x74, 0x97, 0x3a, 0x87, 0xa9, - 0xc6, 0x4b, 0x3d, 0x6a, 0xa4, 0xaa, 0x21, 0x81, 0x57, 0x40, 0x08, 0x22, - 0xab, 0xd4, 0x7e, 0xfc, 0x59, 0x4d, 0x43, 0x7a, 0x07, 0x42, 0xe0, 0x2d, - 0x10, 0xb8, 0x9e, 0x99, 0xa9, 0x1c, 0x69, 0x68, 0x0a, 0xac, 0x72, 0x9c, - 0x67, 0x77, 0x42, 0xa9, 0xa7, 0x21, 0x9a, 0xe6, 0x24, 0xc3, 0xc2, 0x7e, - 0x7d, 0xf5, 0x15, 0x30, 0xdd, 0x38, 0x54, 0xe1, 0xba, 0xe6, 0x0c, 0xf7, - 0x48, 0x55, 0x4b, 0x7f, 0x4f, 0xfe, 0x0f, 0xbe, 0x84, 0xb6, 0xc1, 0x0d, - 0xd0, 0x67, 0x19, 0xd0, 0x02, 0xfa, 0xbe, 0x40, 0x00, 0xd1, 0x90, 0xd6, - 0x14, 0xa1, 0x9e, 0x7e, 0x3f, 0x99, 0x16, 0x48, 0x97, 0x71, 0x3a, 0x09, - 0x4f, 0xc8, 0xf1, 0x21, 0x6b, 0xf4, 0x2c, 0x5a, 0xbf, 0x5d, 0x8e, 0xbe, - 0x72, 0xbf, 0xc7, 0x81, 0xe6, 0x13, 0xcc, 0x66, 0xbb, 0xbf, 0x29, 0xcc, - 0x9e, 0xa5, 0xb9, 0xd9, 0x48, 0xc4, 0x85, 0x61, 0x85, 0xce, 0xde, 0x85, - 0x6a, 0x03, 0x1a, 0x19, 0xc5, 0x3a, 0x90, 0xcc, 0x8c, 0x0f, 0xf1, 0x53, - 0x94, 0x69, 0xe5, 0x22, 0x66, 0x8f, 0x23, 0x97, 0x84, 0x4f, 0x1e, 0x1a, - 0x81, 0x0c, 0xad, 0xc8, 0x05, 0xc0, 0x80, 0xef, 0x09, 0x48, 0x24, 0x33, - 0xb7, 0xd6, 0xaa, 0xaf, 0x29, 0x2b, 0xf5, 0x13, 0x92, 0x68, 0xa1, 0x50, - 0x8c, 0xd0, 0x72, 0xa5, 0xa1, 0x96, 0x18, 0x35, 0x85, 0x07, 0x61, 0x91, - 0xfc, 0x01, 0xf1, 0xe9, 0x4b, 0xc0, 0x00, 0x13, 0xc5, 0xf1, 0x9a, 0xc5, - 0x54, 0xcb, 0xdd, 0xd0, 0x71, 0xfe, 0x94, 0x0e, 0xc9, 0xc7, 0xae, 0x73, - 0xe8, 0x14, 0xa7, 0x00, 0x97, 0xac, 0x06, 0x60, 0xa6, 0x1b, 0x9a, 0xe1, - 0x17, 0xa7, 0x6f, 0xc2, 0x06, 0xd9, 0x92, 0x48, 0xbd, 0xd9, 0x62, 0x3d, - 0x0a, 0xf7, 0xd5, 0x39, 0xcc, 0xfe, 0x20, 0x03, 0xa4, 0xd2, 0xad, 0x4c, - 0x10, 0x5f, 0x50, 0x1c, 0x63, 0xc3, 0x07, 0x99, 0x8a, 0x78, 0x00, 0x5e, - 0x8c, 0x94, 0xc6, 0xb4, 0x1b, 0x6f, 0xb2, 0xa5, 0xce, 0x59, 0xeb, 0x34, - 0xd4, 0x7d, 0x27, 0x6a, 0x8d, 0x64, 0x61, 0x8c, 0x1b, 0x46, 0x68, 0xae, - 0x0c, 0x0c, 0x31, 0x4b, 0x2a, 0x8c, 0x96, 0xd4, 0xfc, 0x0d, 0xc2, 0x56, - 0x38, 0xff, 0xf1, 0x50, 0x80, 0x30, 0xff, 0xfc, 0x21, 0x1a, 0xcd, 0x89, - 0xdf, 0xff, 0x3b, 0xa0, 0x95, 0xa2, 0xb1, 0xac, 0xac, 0x3b, 0x0b, 0x0e, - 0x50, 0x65, 0x52, 0xc0, 0x81, 0xc4, 0xef, 0xe3, 0xdc, 0x00, 0x00, 0x08, - 0xa3, 0xef, 0xe1, 0x7f, 0x8f, 0x21, 0x1a, 0xdf, 0xc2, 0xeb, 0x10, 0xbb, - 0xfc, 0x29, 0x4a, 0x46, 0x4d, 0xfd, 0x89, 0x8a, 0x0f, 0xbb, 0x0c, 0x8c, - 0x71, 0x92, 0x00, 0x3a, 0xda, 0x45, 0x9b, 0x30, 0xa4, 0x74, 0xfa, 0x69, - 0x3c, 0x4f, 0x85, 0xe3, 0x83, 0xb1, 0xb4, 0x60, 0xe6, 0xa4, 0xcc, 0x87, - 0x10, 0x41, 0xfe, 0xd4, 0xc4, 0x5b, 0x35, 0xb7, 0xb5, 0x00, 0xb0, 0x6a, - 0x04, 0xce, 0x12, 0xc7, 0x07, 0xcf, 0x8f, 0x63, 0x4c, 0x58, 0xb3, 0x08, - 0x10, 0x9a, 0x1c, 0x09, 0x99, 0xaf, 0xcd, 0xff, 0xc3, 0xee, 0x2a, 0x35, - 0xe9, 0x2a, 0xc2, 0x65, 0x8c, 0xea, 0x93, 0x89, 0x28, 0xbe, 0x5f, 0xca, - 0xfb, 0x95, 0xe3, 0x28, 0xa8, 0xb5, 0xd7, 0x14, 0xc8, 0xce, 0x85, 0x19, - 0x23, 0x89, 0xf2, 0xdb, 0x81, 0x05, 0x2b, 0x0d, 0xa5, 0xde, 0xfc, 0x03, - 0x09, 0xab, 0xcf, 0x5f, 0xc5, 0xfe, 0x99, 0xb6, 0xe5, 0x72, 0x4f, 0xdb, - 0xc3, 0x7b, 0xac, 0x0f, 0x39, 0x8d, 0xa8, 0x6a, 0x46, 0x7d, 0x9d, 0x1c, - 0x31, 0x6b, 0xfa, 0x88, 0x4f, 0xa6, 0x9a, 0x3a, 0x41, 0x5e, 0x7c, 0x21, - 0x33, 0x53, 0x56, 0xbb, 0x47, 0x6e, 0x77, 0x11, 0xa9, 0xe7, 0x8c, 0xcf, - 0x43, 0xb7, 0xa7, 0x7f, 0x38, 0x00, 0x5a, 0x66, 0x10, 0x02, 0x62, 0x33, - 0xbf, 0x8f, 0xb1, 0x2d, 0x31, 0x3a, 0x12, 0x22, 0x4a, 0x54, 0xa7, 0x3e, - 0xcb, 0x80, 0x24, 0xd7, 0x10, 0x18, 0xaa, 0xf0, 0x96, 0x0c, 0x38, 0xd5, - 0x56, 0x7c, 0x4e, 0x5d, 0xd0, 0x0f, 0x4a, 0x4a, 0x52, 0xb6, 0x56, 0x3d, - 0x86, 0x0d, 0x61, 0x41, 0xd8, 0x64, 0x7c, 0xb8, 0x0b, 0x07, 0xc6, 0x00, - 0x00, 0x00, 0x33, 0x72, 0xda, 0xdf, 0x1d, 0xd5, 0xff, 0xa6, 0x6e, 0xec, - 0x02, 0x3a, 0x73, 0xb3, 0x41, 0x40, 0xb1, 0x63, 0x14, 0xc1, 0xca, 0x62, - 0x40, 0x1e, 0xd5, 0xbb, 0x83, 0x1e, 0x3a, 0xbe, 0x17, 0xa3, 0xbe, 0x5a, - 0x01, 0x3c, 0x7e, 0x42, 0xcd, 0x73, 0x24, 0x45, 0xa3, 0x92, 0x90, 0x80, - 0x81, 0x76, 0x9e, 0x72, 0x47, 0x59, 0xff, 0xea, 0xd0, 0x53, 0x13, 0xcd, - 0x29, 0x47, 0x0d, 0x9c, 0xee, 0x4b, 0xc9, 0xe4, 0xa3, 0xe5, 0x7e, 0x0b, - 0x73, 0xe5, 0x78, 0x60, 0x76, 0x1c, 0x8a, 0xf3, 0xa3, 0xa5, 0x14, 0x96, - 0xa1, 0x3a, 0xc9, 0x45, 0x0e, 0x52, 0xe9, 0x63, 0x13, 0xc8, 0x2e, 0xae, - 0xf3, 0xa5, 0x23, 0x15, 0x32, 0xd6, 0xf9, 0x36, 0x43, 0x2b, 0x0b, 0xd5, - 0x84, 0x84, 0xe1, 0x85, 0xd0, 0x9d, 0xc7, 0x1a, 0x0b, 0x96, 0xee, 0x35, - 0x71, 0x51, 0x50, 0x10, 0x27, 0xb2, 0xfa, 0xf8, 0xff, 0xf1, 0x50, 0x80, - 0x31, 0xdf, 0xfc, 0x21, 0x1a, 0xcb, 0xcb, 0xfb, 0xff, 0x3f, 0xff, 0x94, - 0xb6, 0xc1, 0xac, 0x4a, 0x2b, 0x0c, 0x9d, 0x06, 0xa3, 0x62, 0xc0, 0x50, - 0x7b, 0x77, 0x9a, 0xd8, 0x15, 0x00, 0x14, 0x38, 0x6b, 0xd5, 0xe7, 0xc4, - 0xe7, 0xef, 0x5f, 0x41, 0xee, 0x7f, 0x05, 0xd2, 0x1e, 0x49, 0x59, 0xcf, - 0xaf, 0xb8, 0x0b, 0xb9, 0x29, 0xc6, 0xf2, 0x54, 0x2d, 0x84, 0x40, 0xb0, - 0xcd, 0x84, 0xc3, 0x4c, 0x48, 0xef, 0x12, 0x72, 0x3b, 0xe8, 0x91, 0xca, - 0x4b, 0x5a, 0x2b, 0x3a, 0x27, 0x18, 0xcf, 0x65, 0x51, 0x3c, 0x2d, 0x14, - 0x7f, 0x4f, 0x34, 0xa7, 0x09, 0x3d, 0x1a, 0x8e, 0xa0, 0x29, 0x18, 0xcf, - 0x89, 0x3c, 0x7c, 0x1d, 0x32, 0xe1, 0x9e, 0x0b, 0x11, 0x12, 0x51, 0x45, - 0x5e, 0xc9, 0x47, 0xdd, 0xc9, 0x31, 0x24, 0x51, 0xc2, 0x67, 0x26, 0xa9, - 0xf9, 0xbf, 0x62, 0x3f, 0xd7, 0x98, 0x22, 0x03, 0x36, 0xc4, 0xce, 0x59, - 0x14, 0xce, 0x8c, 0x95, 0x52, 0x6a, 0x74, 0x9f, 0x98, 0x75, 0xdb, 0x65, - 0x73, 0x17, 0x0c, 0xb7, 0x60, 0x21, 0x95, 0x95, 0x42, 0xb5, 0x3e, 0x61, - 0xe6, 0xb8, 0x63, 0x30, 0x34, 0xa0, 0x5e, 0x41, 0x85, 0x4d, 0xcd, 0x68, - 0xfd, 0x76, 0xa2, 0xae, 0x0b, 0x59, 0xb8, 0xc8, 0xa5, 0x55, 0xc9, 0xa5, - 0xdc, 0xd0, 0xd8, 0x5f, 0x14, 0xab, 0x3c, 0x13, 0x62, 0xa5, 0x12, 0x1e, - 0x14, 0x1b, 0xab, 0xea, 0xf6, 0x84, 0x81, 0x5a, 0x11, 0x89, 0x18, 0x9a, - 0x4c, 0x38, 0x49, 0x02, 0x80, 0x44, 0x56, 0x70, 0xb6, 0x0c, 0xcd, 0xa2, - 0x23, 0x1c, 0x1a, 0x04, 0xd1, 0x16, 0x08, 0x2d, 0xc1, 0xc6, 0x08, 0xc8, - 0x00, 0x44, 0x04, 0x01, 0x56, 0xca, 0x00, 0x87, 0x29, 0xec, 0x8b, 0x07, - 0x9c, 0x7a, 0x35, 0x13, 0x3a, 0xce, 0x61, 0x5e, 0x4d, 0x12, 0xaa, 0x4f, - 0x1e, 0x6b, 0x4c, 0x71, 0xec, 0x1e, 0xaf, 0xed, 0x33, 0x9b, 0x59, 0x5a, - 0x18, 0xc2, 0xaf, 0x44, 0xc1, 0x93, 0xb1, 0x42, 0xad, 0x08, 0x3b, 0x0c, - 0x8f, 0xa0, 0x07, 0xe3, 0xdf, 0x60, 0x00, 0x06, 0x73, 0xc2, 0xf5, 0xb5, - 0x55, 0x54, 0x9b, 0xd0, 0x53, 0xf5, 0x3b, 0x63, 0x25, 0x44, 0x69, 0xe8, - 0x11, 0x51, 0x34, 0xa5, 0x44, 0xbf, 0x0c, 0x04, 0xdf, 0x2c, 0xc2, 0x34, - 0xee, 0x54, 0xd9, 0x4d, 0xe3, 0x70, 0x5a, 0x44, 0x30, 0x19, 0xce, 0x7c, - 0x35, 0x91, 0x4c, 0xa1, 0xef, 0x26, 0x28, 0xac, 0xa6, 0xde, 0x46, 0x27, - 0x15, 0x39, 0xf8, 0x22, 0xf6, 0x6a, 0x4a, 0x08, 0x8d, 0x44, 0x69, 0x86, - 0x2e, 0xff, 0xc6, 0x5a, 0x4a, 0xf1, 0x06, 0x34, 0xef, 0x39, 0xd0, 0xbe, - 0x1d, 0x17, 0xa9, 0x5e, 0xf1, 0x7f, 0x51, 0x4e, 0x7a, 0x5c, 0x92, 0x84, - 0xdb, 0x00, 0x04, 0x06, 0x6b, 0xad, 0x9e, 0x71, 0x95, 0xa3, 0x49, 0x56, - 0x9a, 0xb5, 0xce, 0x7f, 0x05, 0xeb, 0xf9, 0x89, 0x91, 0xc0, 0xff, 0xf1, - 0x50, 0x80, 0x2f, 0x9f, 0xfc, 0x21, 0x1a, 0xcc, 0xb7, 0xbf, 0xfb, 0x00, - 0x00, 0x92, 0xb2, 0x32, 0x2d, 0x08, 0x3b, 0x0b, 0x9d, 0x52, 0xc2, 0x80, - 0xa9, 0x2a, 0x4c, 0x82, 0xa0, 0x01, 0x51, 0x43, 0xce, 0x79, 0xfb, 0x5e, - 0x7d, 0x7b, 0x7b, 0xfc, 0x3c, 0x89, 0xcc, 0xee, 0x8f, 0x45, 0xb9, 0xc6, - 0xc3, 0x1c, 0x50, 0x8d, 0xcd, 0x2e, 0xa7, 0xd7, 0xdb, 0x2d, 0x8e, 0x5a, - 0x4c, 0xae, 0xf2, 0x78, 0xb7, 0x66, 0xca, 0x36, 0xd1, 0x98, 0x42, 0x90, - 0xa5, 0x93, 0x9d, 0x02, 0x96, 0x21, 0x06, 0x14, 0x61, 0x45, 0xde, 0x89, - 0xcd, 0x14, 0x59, 0xcc, 0xe1, 0x5c, 0xd1, 0x11, 0x0a, 0x74, 0x70, 0x34, - 0x4b, 0xf1, 0x05, 0x0b, 0x56, 0xa1, 0x02, 0x8c, 0xd9, 0x3f, 0x6d, 0xe9, - 0x7c, 0x5e, 0xf3, 0x9c, 0x16, 0x96, 0x1b, 0x4a, 0x4b, 0xe8, 0xb8, 0x4a, - 0x62, 0xcb, 0x2d, 0x78, 0xd5, 0x37, 0x8b, 0x3b, 0x45, 0x63, 0xb8, 0xbb, - 0x5d, 0xde, 0x7b, 0xbf, 0xab, 0xfc, 0xba, 0xd2, 0x26, 0x0c, 0xa9, 0x25, - 0x65, 0x68, 0x13, 0x48, 0xcb, 0x8d, 0xdb, 0xa5, 0x8a, 0xc1, 0x90, 0x32, - 0x25, 0x8e, 0x2a, 0x47, 0x13, 0xd2, 0xec, 0xce, 0x13, 0x59, 0x8a, 0xea, - 0x26, 0xd8, 0xa8, 0x80, 0x0e, 0xcb, 0xb8, 0xb4, 0x82, 0xb7, 0x98, 0xd5, - 0xfc, 0x16, 0x96, 0x27, 0x73, 0x77, 0xe5, 0x32, 0x28, 0x17, 0x24, 0xdd, - 0xc9, 0x51, 0x98, 0x0b, 0xec, 0x8b, 0xb9, 0x01, 0x02, 0xa7, 0x2c, 0x61, - 0x13, 0x04, 0x4f, 0xb6, 0x79, 0x7c, 0x32, 0xf9, 0x7c, 0x75, 0xbc, 0xf1, - 0xf5, 0x70, 0xef, 0x98, 0x99, 0x09, 0x82, 0xda, 0xf6, 0x48, 0x2b, 0x46, - 0x55, 0x12, 0x27, 0xb9, 0x59, 0x05, 0xb8, 0xd5, 0x07, 0xe1, 0x8b, 0x34, - 0x92, 0xb6, 0x35, 0x32, 0x31, 0x84, 0x98, 0x84, 0xd5, 0xe0, 0x9c, 0xeb, - 0x76, 0x1f, 0x1a, 0x28, 0x04, 0x04, 0xc9, 0x5d, 0x10, 0x76, 0x17, 0x1f, - 0xc0, 0x09, 0x40, 0x00, 0x03, 0x33, 0x52, 0x6b, 0xbc, 0x9b, 0xb4, 0xac, - 0xe0, 0x0f, 0x39, 0xb4, 0x5a, 0x79, 0xfd, 0xe7, 0x82, 0xb3, 0xaf, 0xcc, - 0x02, 0xd7, 0x8a, 0xdf, 0x14, 0xd1, 0x55, 0xcb, 0x38, 0x60, 0x26, 0x85, - 0xac, 0x8c, 0x3e, 0xbf, 0xce, 0xf9, 0x52, 0x5a, 0x0e, 0x48, 0x40, 0x4a, - 0x19, 0x91, 0x86, 0x26, 0x19, 0x79, 0x67, 0x90, 0x7b, 0xf1, 0x4b, 0x01, - 0x4c, 0x7e, 0x98, 0xc8, 0xb3, 0x57, 0x14, 0x78, 0xd0, 0x2e, 0x7b, 0x96, - 0x8b, 0xba, 0x0f, 0x00, 0xa6, 0x80, 0x82, 0x50, 0xfb, 0xcd, 0x57, 0xbc, - 0x51, 0x65, 0x4d, 0x08, 0xe0, 0xff, 0xc3, 0xd4, 0x96, 0x10, 0x01, 0x4c, - 0xc4, 0xc6, 0x8d, 0x5a, 0xc8, 0x46, 0x52, 0xa8, 0x45, 0xcc, 0xdf, 0xeb, - 0xff, 0x67, 0x89, 0xa4, 0xaa, 0x07, 0xff, 0xf1, 0x50, 0x80, 0x2a, 0xbf, - 0xfc, 0x21, 0x1a, 0xc8, 0xfb, 0x7b, 0xff, 0x00, 0x00, 0x93, 0xb6, 0xc1, - 0xac, 0x26, 0x3b, 0x0b, 0x9c, 0xce, 0x82, 0x61, 0x81, 0x28, 0x03, 0x8e, - 0x40, 0x01, 0xec, 0xd7, 0xd9, 0xae, 0x3f, 0x1f, 0x3d, 0x75, 0xa1, 0x39, - 0x94, 0x4b, 0x35, 0xce, 0x6f, 0xaa, 0x79, 0xf0, 0x9d, 0xd8, 0x38, 0xea, - 0xe0, 0x13, 0x53, 0x4c, 0x66, 0x2a, 0xe1, 0x00, 0x71, 0x5a, 0xce, 0xc2, - 0x85, 0x88, 0x91, 0x00, 0xc8, 0xe5, 0xb1, 0x3f, 0x55, 0xac, 0x02, 0x9a, - 0xb9, 0x72, 0x93, 0x41, 0x29, 0xc6, 0x02, 0x8c, 0x2d, 0xc3, 0xce, 0x12, - 0x8c, 0x07, 0x94, 0xb5, 0x7e, 0x69, 0x98, 0x5c, 0xa6, 0xbc, 0x51, 0x8b, - 0xe2, 0xfb, 0x14, 0x7c, 0xa0, 0x21, 0x13, 0x0b, 0xe3, 0xe6, 0xa4, 0x85, - 0x01, 0xa2, 0x14, 0x18, 0xd6, 0x42, 0xa0, 0x95, 0x03, 0xab, 0xed, 0x79, - 0x58, 0x12, 0x0d, 0x60, 0x20, 0x02, 0x58, 0x7a, 0xbd, 0x39, 0x21, 0x01, - 0x18, 0xc0, 0xc8, 0x15, 0x85, 0xa7, 0xa3, 0xee, 0xf0, 0xf9, 0x62, 0x43, - 0xb2, 0xdc, 0xc3, 0x46, 0xe4, 0xf8, 0xfc, 0xf3, 0x2e, 0x8d, 0x66, 0x9c, - 0x34, 0x7e, 0x6b, 0xab, 0xe7, 0x7a, 0x4d, 0x23, 0xbc, 0x8c, 0x00, 0x16, - 0x4f, 0xbd, 0x19, 0xec, 0x08, 0xa9, 0x54, 0xab, 0x09, 0xf8, 0x9f, 0xbe, - 0xfa, 0xdc, 0x9c, 0x83, 0xf7, 0x9a, 0xbc, 0x1f, 0x8a, 0xa9, 0xdd, 0x9a, - 0x7c, 0xa4, 0x45, 0x4c, 0x1c, 0x05, 0x14, 0x13, 0x51, 0x19, 0x13, 0x8a, - 0x3a, 0xdb, 0x4b, 0x55, 0xbe, 0x8d, 0x7a, 0xe2, 0xd6, 0x47, 0x4d, 0x84, - 0x53, 0x87, 0x4c, 0x4e, 0xa5, 0xc0, 0xa5, 0xc9, 0x65, 0x94, 0xb1, 0xb2, - 0x6c, 0x2c, 0x5b, 0x11, 0x0e, 0xc2, 0xe3, 0xf8, 0x08, 0x4a, 0xa8, 0xa8, - 0x2a, 0x00, 0x33, 0x9e, 0x33, 0x8e, 0x3b, 0x9c, 0xeb, 0x9b, 0x95, 0x20, - 0x67, 0x3a, 0x83, 0x86, 0xf9, 0x91, 0xeb, 0x2a, 0x85, 0xc9, 0xc1, 0x4e, - 0xf9, 0x1e, 0xf9, 0x8c, 0x1d, 0xce, 0x7e, 0xab, 0xbf, 0x4b, 0x0a, 0xb7, - 0xa0, 0x10, 0x2c, 0xaa, 0x98, 0x1c, 0x61, 0x29, 0xf8, 0x2c, 0x2b, 0xdd, - 0xfe, 0xb2, 0x11, 0x77, 0x72, 0x25, 0x19, 0x18, 0x72, 0xc6, 0x01, 0xe8, - 0xdf, 0xec, 0xe4, 0x95, 0xc6, 0x81, 0xac, 0xe7, 0xca, 0x64, 0x13, 0x67, - 0x0e, 0x28, 0x45, 0x2e, 0xae, 0x91, 0x2a, 0xd5, 0xc3, 0x14, 0x58, 0x97, - 0x4b, 0xaa, 0x09, 0x80, 0x40, 0xbf, 0x73, 0xd6, 0x67, 0x20, 0x1c, 0xff, - 0xf1, 0x50, 0x80, 0x30, 0x1f, 0xfc, 0x21, 0x1a, 0xc9, 0xbf, 0xef, 0x5b, - 0xbf, 0xff, 0x93, 0xb1, 0xb2, 0x6c, 0x50, 0x69, 0x5a, 0x14, 0x42, 0xc6, - 0x2d, 0x97, 0xad, 0x81, 0x96, 0x32, 0xf2, 0xd9, 0x6b, 0xe7, 0xae, 0xfd, - 0xb8, 0xd7, 0xed, 0xed, 0xcf, 0xeb, 0x8b, 0xea, 0x06, 0x7d, 0xa3, 0xf7, - 0xec, 0x28, 0xff, 0x23, 0xf6, 0xfd, 0xbb, 0xd5, 0xbd, 0x25, 0xcb, 0x9a, - 0x17, 0xac, 0xd6, 0xa1, 0xac, 0xf6, 0x86, 0x5f, 0x95, 0x7f, 0xdd, 0x51, - 0x55, 0xab, 0x37, 0x6d, 0xf4, 0xe3, 0x4e, 0x34, 0xe5, 0x09, 0x67, 0x2e, - 0xa8, 0x0b, 0x08, 0x20, 0xd3, 0xac, 0x6e, 0x48, 0x61, 0x20, 0xd7, 0x8b, - 0xd5, 0xfb, 0x1c, 0x05, 0x00, 0x43, 0xc3, 0x56, 0xa6, 0xe2, 0xf4, 0x01, - 0x19, 0x52, 0x18, 0x64, 0x4f, 0xbd, 0x7f, 0x89, 0x8c, 0x73, 0xca, 0xab, - 0x43, 0x0a, 0x5a, 0xc6, 0x50, 0x0c, 0x88, 0x98, 0xea, 0x75, 0xf8, 0xeb, - 0x46, 0x21, 0x90, 0x44, 0x5d, 0x80, 0xbb, 0xdd, 0xdd, 0xf8, 0x73, 0x61, - 0x25, 0x5e, 0xdb, 0x2e, 0xa2, 0x91, 0x61, 0x7b, 0xfa, 0x0e, 0x82, 0x90, - 0x03, 0x1c, 0x27, 0x58, 0x34, 0x45, 0x07, 0xc0, 0xb8, 0xcf, 0x8c, 0xa1, - 0x8e, 0x95, 0x49, 0x35, 0xb6, 0xa9, 0xd5, 0x65, 0x43, 0xaa, 0xe5, 0xf7, - 0x7b, 0x2a, 0xb6, 0xee, 0xee, 0xe5, 0xe9, 0x40, 0x01, 0x0d, 0x15, 0x05, - 0x7e, 0x92, 0x85, 0x45, 0x2c, 0x91, 0x9b, 0x17, 0x96, 0x12, 0x7b, 0x0e, - 0x92, 0x0a, 0xf5, 0x5d, 0xbc, 0x14, 0x02, 0xd4, 0x30, 0x24, 0x51, 0x53, - 0x2b, 0x02, 0x05, 0x11, 0x51, 0x90, 0x1b, 0xc9, 0x39, 0x94, 0xd3, 0x74, - 0x94, 0x55, 0xa2, 0xf8, 0x9e, 0x73, 0x0d, 0xef, 0x21, 0x94, 0x55, 0x16, - 0xcf, 0x53, 0xfe, 0x71, 0xfa, 0xea, 0x9b, 0x94, 0xa3, 0xb7, 0x49, 0x31, - 0x72, 0xdf, 0xc4, 0x7c, 0xa0, 0x52, 0x36, 0x56, 0x3d, 0xa1, 0x84, 0x41, - 0xb0, 0xb9, 0x39, 0xe0, 0x6b, 0x60, 0x00, 0x02, 0xf9, 0xe2, 0xab, 0xaa, - 0xcd, 0xc9, 0xcd, 0xa0, 0x0b, 0xe4, 0xb5, 0x10, 0xa5, 0xa3, 0xb6, 0xa0, - 0x42, 0x0d, 0x98, 0xde, 0x06, 0x00, 0xf6, 0x41, 0xc9, 0x84, 0xb3, 0x04, - 0x07, 0x9f, 0xab, 0x5e, 0x32, 0x24, 0xf8, 0xca, 0x0a, 0x6c, 0xb2, 0x25, - 0x26, 0x42, 0x89, 0x0d, 0xd0, 0x58, 0xe7, 0x02, 0x9b, 0x91, 0xd5, 0xcf, - 0x01, 0x41, 0x02, 0x38, 0x78, 0x05, 0x29, 0x3b, 0xd0, 0xeb, 0x85, 0x65, - 0x5a, 0x87, 0x03, 0x87, 0xb0, 0xcf, 0x9a, 0x06, 0x54, 0x10, 0x9d, 0x8a, - 0xb4, 0x91, 0x2e, 0xc1, 0x83, 0xed, 0x16, 0xdf, 0xb0, 0x6a, 0x42, 0xf1, - 0x43, 0x80, 0xc3, 0x51, 0x9b, 0x9a, 0x4d, 0x4c, 0x20, 0x8c, 0x22, 0x20, - 0xa8, 0x85, 0xf4, 0xfb, 0x0f, 0xc3, 0xcc, 0x2c, 0x10, 0x05, 0x87, 0xff, - 0xf1, 0x50, 0x80, 0x31, 0xbf, 0xfc, 0x21, 0x1a, 0xcf, 0x4d, 0xc2, 0xfe, - 0x40, 0x00, 0x93, 0xb1, 0xb2, 0x6c, 0x30, 0x52, 0x0d, 0x89, 0x05, 0x61, - 0x41, 0x39, 0x50, 0x66, 0x36, 0x1a, 0x86, 0x04, 0x37, 0xaa, 0xfa, 0xf7, - 0x6f, 0x46, 0xcf, 0x8e, 0xf7, 0xa0, 0x37, 0xad, 0xfc, 0x7b, 0xfc, 0x67, - 0x5f, 0x3d, 0x7a, 0xfa, 0xe3, 0x5d, 0xfd, 0x68, 0x09, 0x1a, 0x12, 0x00, - 0xd6, 0x97, 0x48, 0x3c, 0x5b, 0xa6, 0x4d, 0x3f, 0x56, 0xae, 0x00, 0x93, - 0x87, 0x3d, 0xc5, 0x92, 0xfb, 0x9f, 0xdf, 0x52, 0x5c, 0xe8, 0x82, 0x19, - 0x89, 0x95, 0x94, 0x0f, 0xf6, 0xe3, 0x17, 0x41, 0x87, 0x86, 0x00, 0xc9, - 0xe7, 0x8a, 0x1b, 0xe4, 0x43, 0xc6, 0xf9, 0xc8, 0x75, 0xde, 0x56, 0x5b, - 0xe6, 0xc0, 0x90, 0x31, 0x63, 0x8e, 0x02, 0x29, 0xad, 0xb1, 0x8d, 0xe8, - 0xec, 0x38, 0x71, 0x64, 0x77, 0x1f, 0x13, 0x11, 0xdc, 0x7e, 0x50, 0xf4, - 0xb8, 0xb3, 0x05, 0x2c, 0x33, 0x80, 0x21, 0xdd, 0xb8, 0x89, 0xf0, 0xb2, - 0xa3, 0xe1, 0x97, 0xff, 0x1a, 0x82, 0xe5, 0x48, 0x98, 0x99, 0x4b, 0x15, - 0x58, 0x90, 0xe9, 0xeb, 0xa4, 0x16, 0x9b, 0x72, 0x26, 0x66, 0xf0, 0xbb, - 0x9a, 0xc1, 0x18, 0x11, 0x1f, 0x5b, 0xd0, 0x68, 0x0c, 0xf3, 0x59, 0x67, - 0xaa, 0xde, 0xf2, 0x00, 0x6a, 0xea, 0x55, 0x1d, 0x9e, 0x5c, 0x1e, 0x79, - 0x43, 0x19, 0x1f, 0xa8, 0x8c, 0x00, 0xe9, 0xb3, 0xdd, 0x07, 0x81, 0xcb, - 0x5b, 0x42, 0x71, 0xc0, 0xef, 0x40, 0x1a, 0x6d, 0x07, 0x33, 0x26, 0x83, - 0x73, 0x33, 0x11, 0x3b, 0x6f, 0x51, 0x73, 0x7a, 0x05, 0x27, 0x84, 0xa7, - 0x4c, 0xe3, 0x91, 0x78, 0x8a, 0xc2, 0xdd, 0x94, 0xc9, 0x9e, 0xf0, 0xfd, - 0x68, 0x37, 0x32, 0x69, 0x75, 0x44, 0x88, 0x24, 0x95, 0xc3, 0x56, 0xc1, - 0xc9, 0x2c, 0x18, 0x17, 0x23, 0x2f, 0xcb, 0x8c, 0x93, 0xcd, 0x06, 0x2b, - 0xe4, 0x16, 0x95, 0xcd, 0x71, 0xce, 0x39, 0x3a, 0x23, 0x22, 0xd0, 0xc3, - 0xb0, 0xc8, 0xfe, 0x00, 0x00, 0x00, 0x02, 0xab, 0x5d, 0xf4, 0xa9, 0xb9, - 0x93, 0x68, 0x01, 0x83, 0xda, 0xec, 0x91, 0x1b, 0xe4, 0xde, 0xa8, 0x2a, - 0x3c, 0xbb, 0x99, 0x99, 0x62, 0x6c, 0x68, 0x86, 0x2c, 0xf8, 0xd2, 0x0e, - 0xfe, 0x2a, 0x27, 0x53, 0x4c, 0x4e, 0x59, 0x71, 0x47, 0x94, 0x8e, 0x7a, - 0x6e, 0x37, 0x72, 0x44, 0x1f, 0xaa, 0x9b, 0x52, 0x0a, 0x32, 0xa3, 0x34, - 0xd1, 0xdb, 0x64, 0x53, 0x1d, 0x8c, 0x07, 0x8f, 0x20, 0x02, 0xeb, 0xfe, - 0xec, 0x9d, 0x48, 0xa6, 0x58, 0x15, 0x0b, 0x61, 0xbc, 0xe3, 0xbc, 0x04, - 0x80, 0xee, 0x47, 0x98, 0xc4, 0x25, 0x12, 0xce, 0x36, 0x70, 0x63, 0xcb, - 0x04, 0x8f, 0x0d, 0x1c, 0xa4, 0x7b, 0xe6, 0xe1, 0x5d, 0x20, 0x9d, 0xd1, - 0x09, 0xce, 0x50, 0xb7, 0xb2, 0xfd, 0x17, 0x9b, 0x9a, 0xb3, 0xa6, 0x0e, - 0xff, 0xf1, 0x50, 0x80, 0x30, 0x3f, 0xfc, 0x21, 0x1a, 0xce, 0xb1, 0xb7, - 0xff, 0x44, 0x40, 0x96, 0xa6, 0x58, 0x60, 0xd6, 0x16, 0x11, 0x06, 0xc2, - 0xe7, 0x33, 0x31, 0x20, 0x46, 0x75, 0x29, 0xaf, 0x3f, 0xc5, 0xb2, 0xd9, - 0x60, 0x06, 0x3d, 0xbd, 0x6a, 0xbd, 0xbe, 0x7a, 0xef, 0xf7, 0x75, 0xe3, - 0xf1, 0x41, 0x0d, 0xf3, 0xf7, 0xda, 0x41, 0x10, 0x01, 0x3d, 0x8e, 0xe1, - 0x58, 0x2e, 0x84, 0x6a, 0x43, 0xcd, 0x11, 0x06, 0xa5, 0x14, 0xe6, 0x29, - 0x64, 0x2d, 0x2d, 0xeb, 0x9e, 0x72, 0x76, 0xf8, 0x83, 0x8b, 0x5a, 0x75, - 0x86, 0x49, 0x04, 0x60, 0x40, 0x0c, 0xf4, 0xdf, 0x73, 0x80, 0x85, 0x7c, - 0x27, 0x40, 0x65, 0xcc, 0x72, 0x8d, 0x33, 0x46, 0xd9, 0x90, 0xce, 0x01, - 0xa0, 0x5e, 0x17, 0xbe, 0x96, 0x7e, 0x50, 0x02, 0x09, 0x59, 0x95, 0xc0, - 0x81, 0x03, 0xa3, 0x96, 0x1d, 0x20, 0x05, 0x9a, 0x3e, 0x16, 0x8c, 0x1b, - 0x42, 0xf2, 0x21, 0x52, 0x5e, 0x77, 0xfe, 0xaf, 0x34, 0x8b, 0xb9, 0xb2, - 0xa4, 0x30, 0x94, 0xa6, 0xe0, 0x36, 0x7a, 0x89, 0x09, 0xc7, 0x39, 0xa6, - 0x50, 0xa8, 0x6b, 0x67, 0x37, 0x2c, 0xe0, 0x9a, 0xe3, 0x7d, 0x10, 0xba, - 0x43, 0x37, 0x8e, 0x46, 0x95, 0x1e, 0xe0, 0x3e, 0x99, 0xb3, 0x8b, 0xe4, - 0x74, 0xbc, 0x7f, 0xfe, 0x64, 0xbe, 0xb0, 0x1d, 0xfe, 0xc7, 0x9b, 0x9e, - 0xd0, 0xe1, 0xd0, 0x7d, 0xff, 0x0d, 0x04, 0x4c, 0x9c, 0x60, 0x0c, 0x65, - 0x79, 0xeb, 0xea, 0xc0, 0x2e, 0x07, 0x3d, 0xc2, 0x30, 0x64, 0x48, 0x22, - 0x8e, 0xde, 0xde, 0xec, 0x3c, 0x26, 0xeb, 0xdd, 0xb2, 0x28, 0x96, 0x8b, - 0x29, 0xba, 0x7d, 0x43, 0xf8, 0x9d, 0x00, 0xc1, 0x80, 0x58, 0x22, 0xb4, - 0x36, 0x86, 0xea, 0x35, 0x90, 0x24, 0x09, 0xd0, 0xe7, 0xa2, 0x55, 0x4c, - 0x5e, 0xbf, 0xde, 0xb4, 0x60, 0x88, 0x56, 0xf2, 0xac, 0x2a, 0xc4, 0x1a, - 0x84, 0x50, 0x3c, 0xb4, 0x32, 0xc3, 0x06, 0xb0, 0xb0, 0xec, 0x2e, 0x3f, - 0x80, 0xd6, 0xf4, 0xdd, 0xe0, 0x00, 0x03, 0x7b, 0xf3, 0xb9, 0x1a, 0xdd, - 0x4c, 0x86, 0x07, 0x2b, 0xaf, 0x5c, 0x2f, 0x58, 0xe8, 0x47, 0x20, 0xea, - 0x78, 0xc7, 0x88, 0x8a, 0xec, 0x8a, 0xab, 0x08, 0x0b, 0xf1, 0x10, 0xa7, - 0x44, 0xea, 0x74, 0x13, 0xda, 0x41, 0x96, 0x70, 0x6d, 0x0c, 0xe3, 0x9f, - 0x14, 0x34, 0x71, 0xf8, 0xfd, 0x64, 0xdc, 0xe5, 0x61, 0xf8, 0xca, 0x51, - 0xcc, 0x40, 0xa0, 0x19, 0xd6, 0x39, 0x0a, 0x66, 0x4a, 0x62, 0xc0, 0xd3, - 0x14, 0x0c, 0x41, 0xe9, 0x14, 0x40, 0x5d, 0xa9, 0x00, 0x89, 0x3c, 0xaa, - 0x51, 0xec, 0x66, 0xea, 0x96, 0x4a, 0xf0, 0x50, 0x01, 0xb5, 0x81, 0x86, - 0xb7, 0x2e, 0x43, 0x6c, 0x44, 0x96, 0x27, 0x97, 0xfe, 0xf8, 0x60, 0x0b, - 0xe0, 0xff, 0xf1, 0x50, 0x80, 0x30, 0x1f, 0xfc, 0x21, 0x1a, 0xc9, 0xda, - 0x5f, 0xff, 0x40, 0x40, 0x97, 0xb0, 0xc2, 0xec, 0x30, 0x6b, 0x0b, 0x0a, - 0x52, 0x65, 0x52, 0xb0, 0x8b, 0x3f, 0x7f, 0xb6, 0xfe, 0xbe, 0x71, 0x79, - 0x60, 0x01, 0x8e, 0x39, 0x3c, 0xf8, 0xbc, 0xfc, 0x6e, 0xfc, 0x7d, 0x7d, - 0x06, 0x5c, 0x13, 0xf9, 0xe2, 0x9a, 0xba, 0xf3, 0x04, 0x52, 0x37, 0x4a, - 0x59, 0xaf, 0x8d, 0xeb, 0x88, 0x93, 0x9a, 0x7c, 0xc3, 0xab, 0x22, 0x82, - 0x10, 0x5f, 0xb4, 0x11, 0xab, 0xc9, 0xb2, 0x1a, 0xb4, 0x06, 0x18, 0xed, - 0xb3, 0x49, 0x10, 0xc1, 0xe4, 0x74, 0x84, 0x73, 0xc1, 0xf0, 0x3c, 0x04, - 0x54, 0x02, 0x5f, 0x1a, 0xdd, 0xc8, 0xae, 0x10, 0x44, 0x44, 0x70, 0xa9, - 0x14, 0x24, 0x7a, 0xbc, 0x3b, 0x46, 0x82, 0xf1, 0x64, 0x36, 0x8f, 0x0c, - 0x71, 0xb5, 0xd3, 0x29, 0x7b, 0x97, 0xf8, 0xaf, 0xc2, 0xb6, 0x75, 0x86, - 0xd6, 0x46, 0xb9, 0x6b, 0xdf, 0x90, 0x8c, 0xb1, 0x98, 0xc7, 0x67, 0x98, - 0xfe, 0xe3, 0x8a, 0xb2, 0xac, 0xe0, 0x86, 0xe0, 0x65, 0x8a, 0x0c, 0xac, - 0xbc, 0x79, 0x5e, 0x04, 0x30, 0x30, 0xca, 0x6b, 0x10, 0x8d, 0x83, 0x3a, - 0x98, 0x2f, 0x3f, 0x6f, 0xd1, 0xc1, 0x9c, 0xd4, 0x42, 0xd8, 0x92, 0x07, - 0xbc, 0xc6, 0xa9, 0xc5, 0xa4, 0x79, 0x3c, 0x19, 0xff, 0xad, 0x67, 0xff, - 0x7c, 0x0d, 0xf0, 0x53, 0x54, 0x4f, 0xe5, 0x08, 0xcc, 0xea, 0x54, 0x67, - 0xb2, 0xb3, 0xce, 0xb3, 0x6b, 0x8e, 0xde, 0xec, 0xf5, 0x33, 0x5c, 0x7c, - 0x2b, 0x08, 0xba, 0x2f, 0x75, 0x6d, 0xf2, 0x89, 0xed, 0x86, 0x5b, 0xf9, - 0x76, 0xa6, 0xb9, 0x73, 0xd8, 0x66, 0x43, 0x70, 0x0b, 0x29, 0x8b, 0x88, - 0xd4, 0xd7, 0xbc, 0x1d, 0x1e, 0x1f, 0x8d, 0xe3, 0x64, 0xdf, 0xd2, 0xf7, - 0x3d, 0x27, 0xf9, 0xc2, 0xfc, 0x25, 0x95, 0xb1, 0x32, 0x6d, 0x2c, 0x3b, - 0x0b, 0x8f, 0xe0, 0x06, 0xb6, 0x00, 0x00, 0x1c, 0xf9, 0xdc, 0x56, 0xb3, - 0x9b, 0xdd, 0xd5, 0x58, 0x39, 0xb9, 0x39, 0x2f, 0x7c, 0x0d, 0x8d, 0x0d, - 0x5d, 0x97, 0x35, 0x04, 0x17, 0x76, 0xef, 0x3d, 0x20, 0x56, 0xc2, 0xe7, - 0x28, 0x8a, 0x81, 0xcb, 0x44, 0x27, 0xa1, 0xad, 0x44, 0xd0, 0x31, 0xb8, - 0x99, 0x8a, 0x00, 0x78, 0x12, 0xb0, 0x23, 0xf0, 0xaa, 0xc7, 0xc0, 0x44, - 0x30, 0xd8, 0xfd, 0x19, 0xc5, 0x56, 0x49, 0x4a, 0x2c, 0x71, 0xd4, 0x67, - 0xd0, 0x71, 0x61, 0xe3, 0xc3, 0x94, 0xb2, 0xee, 0xce, 0x8b, 0xa3, 0x40, - 0x01, 0x38, 0x6f, 0x6a, 0x19, 0x8b, 0x40, 0xc2, 0x11, 0x5a, 0xf0, 0x70, - 0xd0, 0xa1, 0x38, 0x8c, 0x41, 0x14, 0x33, 0xb5, 0xe7, 0xc5, 0xe1, 0x2a, - 0x4e, 0x1a, 0x26, 0x6f, 0x09, 0x5b, 0xd7, 0xff, 0x86, 0x59, 0x18, 0x2d, - 0x3c, 0xff, 0xf1, 0x50, 0x80, 0x31, 0x7f, 0xfc, 0xde, 0x02, 0x00, 0x4c, - 0x61, 0x76, 0x63, 0x35, 0x38, 0x2e, 0x33, 0x34, 0x2e, 0x31, 0x30, 0x30, - 0x00, 0x42, 0x35, 0x9d, 0x69, 0x87, 0xfe, 0x00, 0x81, 0x2b, 0x63, 0x64, - 0xda, 0x65, 0x4a, 0x96, 0x14, 0x04, 0x4a, 0x95, 0xd6, 0x6c, 0x0b, 0xc0, - 0x01, 0x28, 0xeb, 0xb3, 0xea, 0x77, 0xf8, 0xaf, 0x80, 0xf5, 0xd1, 0x5e, - 0x6a, 0xaa, 0x3c, 0x96, 0xf4, 0x4c, 0x33, 0xeb, 0x4b, 0x7a, 0xe2, 0x0e, - 0x8f, 0x87, 0xac, 0x63, 0xba, 0xac, 0x52, 0xa7, 0x5e, 0x4f, 0x82, 0x4a, - 0xae, 0xb8, 0x93, 0x8a, 0xf2, 0x38, 0x19, 0x14, 0x61, 0x03, 0xe5, 0x29, - 0x4d, 0x95, 0x7b, 0x31, 0x8d, 0x31, 0xb6, 0x60, 0xe6, 0x81, 0xab, 0x4b, - 0xc5, 0x34, 0x23, 0x89, 0xf9, 0x7c, 0x50, 0x78, 0xd2, 0xcd, 0x40, 0x63, - 0x98, 0xf8, 0xa2, 0x6a, 0x72, 0x61, 0x6d, 0xfa, 0x1a, 0x28, 0x89, 0xca, - 0x0b, 0xbf, 0xf9, 0xbc, 0x98, 0x9b, 0xcb, 0x4a, 0xb1, 0x1a, 0xb4, 0xb5, - 0xc0, 0x86, 0x32, 0x2b, 0xe0, 0xbe, 0x17, 0x2a, 0xa5, 0x4a, 0x0d, 0x9a, - 0xb8, 0xd9, 0x6c, 0x71, 0x25, 0x10, 0xbe, 0x37, 0xe9, 0xf7, 0x94, 0x19, - 0x55, 0x2b, 0x2d, 0xd2, 0x93, 0x38, 0x0c, 0xf0, 0xc8, 0xd4, 0xf3, 0x1f, - 0x3e, 0xca, 0x19, 0xc2, 0x0a, 0x8e, 0xc0, 0xa0, 0x52, 0x03, 0xd9, 0x40, - 0x91, 0x55, 0x09, 0xc4, 0xf6, 0x52, 0x73, 0x77, 0x8b, 0x53, 0xbe, 0x24, - 0x16, 0x1d, 0xc9, 0xd1, 0x2a, 0x0b, 0x45, 0x75, 0xfb, 0xb6, 0xa6, 0x49, - 0x25, 0xbe, 0xcd, 0xdd, 0xf6, 0xda, 0x25, 0x35, 0xb9, 0xd5, 0x7c, 0x3a, - 0x91, 0x57, 0x59, 0x84, 0x6e, 0xaf, 0xaf, 0x20, 0x02, 0x6f, 0x57, 0xf0, - 0x9b, 0x7d, 0xba, 0x39, 0x04, 0xb7, 0xe5, 0x14, 0x62, 0xf3, 0x91, 0xb4, - 0xe9, 0x28, 0x0a, 0xc3, 0x4d, 0x8a, 0xf6, 0x67, 0xf4, 0x02, 0x32, 0xb3, - 0x08, 0x45, 0x0d, 0xb5, 0x0d, 0x31, 0x75, 0xec, 0xac, 0x25, 0x3d, 0x2b, - 0x0c, 0xb4, 0x28, 0xec, 0x2e, 0x3f, 0x80, 0x95, 0x29, 0xad, 0x80, 0x00, - 0x39, 0x9a, 0x56, 0x5f, 0x5e, 0x82, 0x55, 0x87, 0x59, 0x76, 0x19, 0x66, - 0xf8, 0x4c, 0x6a, 0xa3, 0x29, 0x62, 0x3c, 0x0e, 0x0a, 0x40, 0x84, 0xc8, - 0x5c, 0xab, 0xaa, 0xa2, 0x9c, 0xe4, 0x99, 0x4d, 0x8f, 0x35, 0x1e, 0x67, - 0x1f, 0xa9, 0x9e, 0x73, 0xc4, 0xa3, 0x53, 0xc8, 0x8f, 0x3c, 0xf2, 0xc3, - 0x65, 0x49, 0x7b, 0xda, 0xfa, 0xf8, 0x41, 0xc4, 0x5e, 0x53, 0x26, 0x60, - 0x39, 0xae, 0x7a, 0xdc, 0x3a, 0x90, 0xe9, 0x60, 0x1d, 0x9d, 0xd7, 0xe3, - 0x31, 0x7b, 0x44, 0x1e, 0x8c, 0xaa, 0x9d, 0x1b, 0xa3, 0x04, 0x7e, 0x54, - 0xa1, 0x6b, 0x38, 0x31, 0x86, 0x02, 0x11, 0x86, 0x1a, 0x50, 0x9b, 0xca, - 0x25, 0x57, 0x57, 0x68, 0xdf, 0xf6, 0x7f, 0xd9, 0x8c, 0x93, 0x13, 0x0e, - 0xff, 0xf1, 0x50, 0x80, 0x2f, 0x5f, 0xfc, 0x21, 0x1a, 0xcf, 0xdf, 0xfb, - 0xfd, 0x44, 0x40, 0x93, 0xb1, 0x32, 0x6d, 0x2c, 0x3b, 0x0c, 0xa0, 0x48, - 0xa1, 0x41, 0xb0, 0xc1, 0x5d, 0x72, 0x01, 0x50, 0x05, 0x4a, 0x5e, 0x75, - 0xcf, 0x1d, 0x9e, 0x7e, 0x3b, 0xfa, 0x74, 0x1e, 0xb8, 0xfb, 0xfe, 0x37, - 0xbd, 0x1e, 0xdf, 0xa6, 0xef, 0xcc, 0xcb, 0x6b, 0x55, 0xa8, 0x6f, 0x90, - 0xf5, 0x7d, 0x0a, 0x19, 0xdc, 0xc9, 0xce, 0x16, 0xc1, 0x05, 0x82, 0x59, - 0xd8, 0xca, 0xae, 0x70, 0x32, 0x15, 0x02, 0xe5, 0x42, 0xcf, 0xa3, 0x79, - 0xbd, 0x84, 0x29, 0x18, 0x1c, 0x98, 0x0d, 0x38, 0x53, 0x36, 0x67, 0x15, - 0x86, 0x29, 0x9c, 0x1e, 0x83, 0x27, 0xc2, 0xa3, 0x15, 0x70, 0x32, 0x25, - 0x86, 0xbc, 0xe6, 0x44, 0x11, 0xae, 0x10, 0x91, 0x1c, 0xc1, 0x80, 0xc2, - 0xbc, 0xb9, 0xe4, 0x73, 0x40, 0xa3, 0xf8, 0x32, 0xc7, 0x1c, 0x90, 0x56, - 0x18, 0xd8, 0x0d, 0x4f, 0x11, 0xe9, 0xbc, 0xc8, 0x30, 0xb8, 0xc6, 0x6e, - 0xaa, 0x04, 0x44, 0x84, 0x86, 0x5f, 0x2a, 0xd7, 0xc9, 0x53, 0x95, 0xa4, - 0x9c, 0x60, 0x26, 0xc4, 0x21, 0x1a, 0x9e, 0x79, 0xdc, 0xb2, 0x83, 0x02, - 0x13, 0x3c, 0x6a, 0x0b, 0x0c, 0xae, 0x1d, 0x3c, 0x20, 0xc9, 0x2e, 0x1b, - 0x8f, 0x77, 0x7b, 0xf8, 0x41, 0x50, 0xbd, 0xe5, 0xb7, 0xcf, 0xa2, 0x5a, - 0x50, 0x00, 0xa6, 0x56, 0xdb, 0x59, 0x98, 0x04, 0xe8, 0x2e, 0xa8, 0x59, - 0xfe, 0x8f, 0xcf, 0x29, 0x4c, 0x86, 0xe3, 0xba, 0xbd, 0x7a, 0x03, 0x6c, - 0x35, 0x9f, 0xb9, 0x1e, 0x41, 0xcb, 0xf3, 0x9b, 0x44, 0x5e, 0xcb, 0x34, - 0x60, 0x2f, 0x8c, 0x87, 0x13, 0x5f, 0x85, 0x8c, 0x9f, 0x6e, 0xcd, 0x53, - 0x7d, 0xe6, 0xed, 0xb2, 0x81, 0xcc, 0xbd, 0x17, 0x2c, 0x49, 0xd0, 0xa1, - 0x56, 0x85, 0x1d, 0x86, 0x47, 0xe5, 0x30, 0xfa, 0x80, 0xe3, 0x96, 0xb6, - 0x00, 0x01, 0x9c, 0xfb, 0x65, 0xb7, 0xc7, 0x34, 0x95, 0x2a, 0x08, 0xa0, - 0xb9, 0xc4, 0x24, 0x7c, 0x21, 0x12, 0x4e, 0x32, 0x37, 0x8b, 0x53, 0x96, - 0xb2, 0xf4, 0xea, 0x10, 0x28, 0xd6, 0x16, 0x20, 0x35, 0x12, 0x1c, 0x62, - 0x7a, 0xcc, 0x45, 0x7e, 0x79, 0x08, 0x28, 0x2b, 0x08, 0x51, 0x42, 0x2c, - 0x6b, 0x84, 0xcf, 0x45, 0x2b, 0xa9, 0xe4, 0x73, 0x8b, 0xc0, 0xb3, 0x88, - 0x07, 0x02, 0x90, 0xb4, 0xe0, 0xac, 0xf1, 0x45, 0x42, 0x8d, 0x2b, 0xbf, - 0x7d, 0x7c, 0xae, 0x9f, 0x58, 0x6f, 0x8e, 0x26, 0xf1, 0x39, 0x73, 0x65, - 0x0a, 0x9d, 0x05, 0x97, 0x9f, 0x45, 0x00, 0x29, 0x2b, 0xd4, 0x5d, 0x6b, - 0x69, 0xc0, 0x8d, 0xa5, 0x18, 0x62, 0x57, 0x13, 0xf4, 0x1f, 0x02, 0x62, - 0x88, 0x2d, 0x9f, 0xf9, 0x44, 0x4e, 0xff, 0xf1, 0x50, 0x80, 0x2d, 0xbf, - 0xfc, 0x21, 0x1a, 0xcf, 0xf5, 0xef, 0x7f, 0x44, 0x40, 0x92, 0xb1, 0xb2, - 0x6d, 0x08, 0x39, 0x41, 0x99, 0x8c, 0x00, 0x00, 0x00, 0x25, 0x4a, 0xf6, - 0xf5, 0xed, 0xe8, 0xfb, 0xfb, 0x78, 0xf3, 0x7c, 0x03, 0x33, 0xf4, 0xb8, - 0x4d, 0xf4, 0xb5, 0xef, 0x8f, 0xf5, 0xf7, 0x19, 0x31, 0x1b, 0xed, 0x7b, - 0xde, 0x8c, 0xf1, 0x8a, 0xf9, 0xf6, 0x9d, 0x69, 0xd1, 0x56, 0x96, 0x50, - 0x9b, 0x75, 0xbf, 0xeb, 0x4a, 0x58, 0xc0, 0x98, 0xd2, 0x80, 0xa6, 0x1a, - 0xb4, 0xb1, 0x0d, 0x12, 0x71, 0x05, 0x28, 0x52, 0x8b, 0x46, 0x7c, 0x50, - 0x03, 0x62, 0xb7, 0x60, 0xf6, 0x5a, 0x72, 0xc0, 0xbf, 0xc9, 0x74, 0x3b, - 0x76, 0xd7, 0xb7, 0x55, 0x04, 0x07, 0x59, 0x5b, 0x2c, 0xfd, 0x13, 0xc6, - 0x60, 0xcd, 0x9e, 0x24, 0xcd, 0xe7, 0x02, 0x98, 0x95, 0x30, 0xc2, 0xb7, - 0x7e, 0x3d, 0xef, 0x5a, 0x50, 0x2a, 0x61, 0x38, 0x84, 0x48, 0xc4, 0x82, - 0x1c, 0xbf, 0x41, 0xaa, 0x20, 0x08, 0x49, 0x4b, 0x17, 0x12, 0x61, 0xdb, - 0x7a, 0xbe, 0xde, 0x40, 0x84, 0x27, 0xe5, 0x2c, 0xdc, 0xf1, 0xc3, 0x36, - 0xd3, 0xe8, 0x8a, 0x48, 0x27, 0x97, 0xa3, 0x43, 0xda, 0x76, 0x93, 0xfb, - 0x0e, 0xd8, 0x66, 0x5e, 0x48, 0x13, 0xcf, 0xe2, 0xd1, 0x10, 0x01, 0xb4, - 0x25, 0x80, 0x76, 0x14, 0x26, 0x57, 0x4b, 0xde, 0x5a, 0x52, 0x79, 0x6b, - 0xab, 0x75, 0x77, 0x0f, 0x75, 0x46, 0x28, 0x00, 0x0e, 0x4a, 0x73, 0x48, - 0xda, 0x84, 0x86, 0xda, 0x2c, 0xe3, 0x98, 0x4d, 0x67, 0x18, 0x96, 0x16, - 0x05, 0xcf, 0x2c, 0xf7, 0x78, 0x4d, 0x3d, 0x99, 0xcf, 0xc3, 0x80, 0x4b, - 0x53, 0xec, 0xdc, 0xde, 0x1e, 0xa5, 0xf8, 0xc7, 0x4e, 0x1a, 0xe6, 0x2d, - 0x52, 0x0c, 0x95, 0x0a, 0x15, 0x61, 0x83, 0x58, 0x50, 0x76, 0x17, 0x1f, - 0xc0, 0x05, 0x4a, 0x82, 0xa0, 0xa8, 0x06, 0xe5, 0xd4, 0xe6, 0xd5, 0x79, - 0x2a, 0x20, 0x88, 0xaa, 0x97, 0x08, 0x72, 0x0f, 0x0a, 0x26, 0x18, 0x0e, - 0xec, 0x73, 0xae, 0x1c, 0x3a, 0x87, 0xe0, 0x11, 0xb2, 0x8f, 0x80, 0xf2, - 0x3d, 0x73, 0x3c, 0x95, 0x1c, 0x35, 0x64, 0xbb, 0xe8, 0x81, 0x58, 0xb1, - 0x55, 0x11, 0x72, 0xba, 0xfc, 0x47, 0x70, 0x43, 0xd3, 0x10, 0xc4, 0x28, - 0x13, 0x51, 0xc2, 0xbf, 0xae, 0xed, 0x81, 0x2f, 0x3e, 0x36, 0x2c, 0xd0, - 0xf8, 0xe5, 0x49, 0xff, 0x2d, 0x33, 0xef, 0xa3, 0xb1, 0x43, 0xdf, 0x16, - 0x8f, 0x81, 0x70, 0x42, 0xcb, 0x10, 0xc6, 0x61, 0x10, 0x9d, 0x4c, 0x41, - 0x59, 0x93, 0x72, 0x32, 0xf4, 0x5d, 0xee, 0x98, 0xb2, 0x93, 0xc0, 0xff, - 0xf1, 0x50, 0x80, 0x2f, 0xdf, 0xfc, 0x21, 0x1a, 0xcf, 0x6a, 0xbb, 0xef, - 0x04, 0x80, 0x91, 0xb2, 0xb1, 0xed, 0x0c, 0x37, 0x41, 0x99, 0x8c, 0x07, - 0x1c, 0xfb, 0x7a, 0x00, 0x09, 0x40, 0xbc, 0xe3, 0x9f, 0xaf, 0x93, 0xf5, - 0xf6, 0xef, 0x55, 0xec, 0x19, 0xe6, 0x32, 0x04, 0x2d, 0x94, 0x7b, 0x41, - 0xd0, 0x11, 0xc0, 0x51, 0x0e, 0x13, 0x46, 0x86, 0xbf, 0x3c, 0x08, 0x09, - 0xa5, 0xeb, 0x5b, 0x3e, 0x38, 0x64, 0xef, 0x1c, 0x97, 0x0c, 0xcf, 0x7e, - 0x0b, 0xb3, 0xd4, 0x92, 0x32, 0x61, 0xbf, 0x8c, 0x84, 0xfb, 0x90, 0xb1, - 0xcd, 0x02, 0xd1, 0x5b, 0x6b, 0x63, 0x81, 0xa2, 0x8a, 0x50, 0x33, 0x65, - 0x96, 0xb0, 0x32, 0xf5, 0x75, 0x26, 0x58, 0x82, 0xf3, 0x81, 0x11, 0x3c, - 0x0d, 0x18, 0x0a, 0xb8, 0x12, 0x06, 0x84, 0x34, 0xad, 0x4f, 0xd7, 0xf4, - 0xba, 0x34, 0x4e, 0x00, 0x02, 0x64, 0x0a, 0x65, 0xa5, 0xf1, 0x35, 0x60, - 0x89, 0x91, 0x9e, 0x13, 0x37, 0x30, 0xb0, 0x12, 0xdd, 0xf1, 0x6a, 0x31, - 0x52, 0x58, 0x18, 0xcd, 0x94, 0x0c, 0x26, 0x21, 0x97, 0x1b, 0xbe, 0xe2, - 0x75, 0xb8, 0x12, 0xac, 0x1e, 0x0f, 0x1c, 0x6f, 0xc6, 0x84, 0x9e, 0x64, - 0x0e, 0x56, 0xff, 0xe9, 0xfa, 0x96, 0xea, 0xbe, 0xbb, 0xef, 0x1f, 0xa5, - 0x70, 0xeb, 0xc8, 0x9c, 0x15, 0xac, 0xb5, 0xdc, 0x3e, 0x67, 0x6b, 0x63, - 0x92, 0xcf, 0x5a, 0xd9, 0x01, 0x5e, 0x05, 0x00, 0xd2, 0xd0, 0x3d, 0x73, - 0xbf, 0xe2, 0xf9, 0x6a, 0xfd, 0x59, 0xb8, 0x6c, 0xa9, 0xf7, 0xe1, 0x6a, - 0xb0, 0x1a, 0xc8, 0x6d, 0x24, 0x51, 0x2b, 0x3d, 0x96, 0x2c, 0x2a, 0x7a, - 0xe8, 0x4b, 0x38, 0x3d, 0x2e, 0x9a, 0x53, 0x04, 0x07, 0x9a, 0x0f, 0xba, - 0xfb, 0x27, 0x5b, 0x2e, 0xbe, 0xe3, 0x94, 0x25, 0xe3, 0x7d, 0xb5, 0xa7, - 0xcb, 0xc0, 0x3d, 0xbf, 0xd7, 0x9e, 0xbc, 0x7b, 0xe4, 0x91, 0xa2, 0x32, - 0x2c, 0x50, 0x5b, 0x0a, 0x0e, 0xc2, 0xe3, 0xf8, 0x0a, 0x8a, 0x95, 0x2a, - 0x54, 0x2a, 0x05, 0x41, 0xba, 0xe3, 0x24, 0x6e, 0x54, 0x65, 0xac, 0x44, - 0xf7, 0xb9, 0xe5, 0x10, 0x93, 0x2a, 0xb2, 0x7c, 0xd9, 0x52, 0x37, 0x4e, - 0x56, 0x7c, 0x2d, 0xbb, 0x3a, 0x3b, 0xbc, 0xc6, 0x1b, 0x3b, 0x3d, 0x4a, - 0xa2, 0xc8, 0x60, 0x59, 0x42, 0x77, 0x47, 0xb7, 0x2f, 0xc6, 0xb1, 0xcb, - 0x78, 0xd0, 0x1a, 0x1c, 0xee, 0x24, 0x4e, 0xed, 0x36, 0x00, 0x20, 0x40, - 0xca, 0x25, 0x07, 0x2e, 0x1e, 0xac, 0xaa, 0x3e, 0x55, 0x58, 0x1c, 0x0a, - 0x83, 0x6c, 0x3a, 0x8d, 0x54, 0x77, 0x0c, 0x6d, 0x73, 0xc5, 0xa8, 0xfe, - 0xa6, 0x33, 0x7b, 0x02, 0xc5, 0x2c, 0xe6, 0x4a, 0xc2, 0xb2, 0x81, 0x51, - 0x90, 0x06, 0x3c, 0x9f, 0x43, 0xde, 0x41, 0x40, 0xe0, 0xff, 0xf1, 0x50, - 0x80, 0x2d, 0xdf, 0xfc, 0x21, 0x1a, 0xcb, 0xb1, 0xf7, 0x7f, 0x44, 0x40, - 0x93, 0xb1, 0xb2, 0x6c, 0x8c, 0x3b, 0x0b, 0x0e, 0x50, 0xa7, 0x62, 0x40, - 0x41, 0xaa, 0xaa, 0x00, 0x00, 0x35, 0xbd, 0x6f, 0x5b, 0xf3, 0xe3, 0x5b, - 0xe3, 0xef, 0xbd, 0xfb, 0x7b, 0x07, 0xbf, 0x3f, 0xad, 0x01, 0x48, 0x1d, - 0xc8, 0xa5, 0xab, 0x5d, 0xb1, 0x8a, 0xdb, 0x7a, 0x9d, 0xd1, 0x75, 0x36, - 0x5b, 0x3e, 0xf6, 0xd7, 0x21, 0x76, 0xe2, 0x48, 0xe0, 0xe4, 0x56, 0x75, - 0x63, 0x64, 0xe0, 0x22, 0xb3, 0x8d, 0x78, 0x1a, 0xc7, 0x7d, 0x8c, 0x8a, - 0x41, 0xff, 0x3e, 0x10, 0x8d, 0x50, 0x8b, 0xa0, 0x49, 0x26, 0x83, 0xd3, - 0x8e, 0x08, 0x28, 0xcd, 0xb6, 0xce, 0xc3, 0x10, 0xb0, 0x50, 0xc4, 0xa6, - 0x84, 0x32, 0x3a, 0x8d, 0x53, 0xbf, 0xe5, 0x3e, 0xbf, 0xb4, 0x8c, 0xb1, - 0x49, 0x86, 0xd5, 0x19, 0x51, 0x15, 0x53, 0x05, 0xf3, 0xff, 0x3b, 0xe3, - 0xe6, 0x13, 0x51, 0x8c, 0xe7, 0x11, 0x58, 0x2d, 0x2a, 0x4e, 0x72, 0xa7, - 0x69, 0xd6, 0xc0, 0x19, 0xcc, 0xde, 0x13, 0x2b, 0x52, 0x33, 0x9c, 0xb3, - 0x88, 0x60, 0xf7, 0x3d, 0x7c, 0xc2, 0xd0, 0xbc, 0x6c, 0x31, 0x1a, 0x00, - 0xa7, 0xab, 0x72, 0x02, 0xa5, 0x71, 0xa4, 0xe6, 0x97, 0x1a, 0x9e, 0x21, - 0x8e, 0x8d, 0x00, 0x1d, 0x10, 0x45, 0x01, 0x43, 0x73, 0xf4, 0x48, 0x91, - 0x10, 0x6f, 0x7b, 0x4a, 0x54, 0xab, 0x22, 0x23, 0x95, 0x0f, 0x27, 0x65, - 0x40, 0x66, 0x40, 0x71, 0x69, 0x03, 0x09, 0x66, 0xae, 0x48, 0x26, 0xb7, - 0x64, 0xd6, 0xe4, 0xa8, 0x20, 0x8a, 0x62, 0xc0, 0x84, 0x62, 0x02, 0xbc, - 0x8a, 0x75, 0x33, 0x04, 0x68, 0x19, 0xb2, 0xf2, 0xec, 0x65, 0xca, 0x84, - 0xa7, 0xda, 0x00, 0x86, 0xf2, 0x56, 0x83, 0x3c, 0xa9, 0x4a, 0x1b, 0x91, - 0x8b, 0x68, 0x41, 0xc9, 0x3e, 0x08, 0x57, 0x15, 0xb2, 0x05, 0x42, 0xa0, - 0x15, 0x97, 0x39, 0x5f, 0x1b, 0xad, 0xf1, 0x5b, 0x96, 0x01, 0xdd, 0xf9, - 0xc9, 0xb9, 0x41, 0xb4, 0xbd, 0x62, 0x17, 0xa9, 0x57, 0x44, 0xc5, 0x2b, - 0x80, 0xaf, 0x02, 0x49, 0x2e, 0xb7, 0x98, 0x4d, 0xda, 0x49, 0x34, 0xd3, - 0xd8, 0x82, 0x3d, 0xbd, 0x8a, 0x02, 0x22, 0x36, 0x1d, 0x79, 0x88, 0x69, - 0x66, 0xc0, 0xcd, 0x34, 0x71, 0x04, 0x40, 0xb8, 0xa1, 0x87, 0x12, 0xb5, - 0x21, 0x06, 0x9f, 0x08, 0x5c, 0x69, 0x7b, 0xb0, 0x7d, 0xf9, 0xe4, 0x3e, - 0x9d, 0xd2, 0xcd, 0xd3, 0xe1, 0x87, 0x13, 0xfe, 0xe7, 0xc9, 0xe7, 0x3c, - 0xe7, 0x38, 0xcd, 0x46, 0xf9, 0xb9, 0x10, 0x92, 0xe8, 0x67, 0xf1, 0x59, - 0x2e, 0xe4, 0x70, 0xff, 0xf1, 0x50, 0x80, 0x30, 0xbf, 0xfc, 0x21, 0x1a, - 0xcf, 0xea, 0xf7, 0xff, 0x00, 0x40, 0x95, 0xa3, 0x31, 0xad, 0x12, 0xb3, - 0x22, 0x89, 0x89, 0x02, 0x0e, 0x3e, 0xbc, 0x7a, 0xeb, 0xbd, 0xe8, 0x6f, - 0x8a, 0x01, 0xad, 0xd6, 0x75, 0xde, 0xb3, 0xeb, 0xed, 0xad, 0xfe, 0xf7, - 0x7e, 0x3a, 0xf6, 0x02, 0xfe, 0x7e, 0x3b, 0x90, 0xca, 0x84, 0x96, 0xca, - 0x0c, 0x2d, 0x1d, 0x63, 0xd1, 0x8d, 0x55, 0x06, 0xbb, 0x37, 0xca, 0x00, - 0xba, 0xa7, 0xb8, 0x61, 0x17, 0x11, 0x28, 0x5c, 0x50, 0x85, 0x16, 0x47, - 0x82, 0x02, 0x83, 0x71, 0xe4, 0xf0, 0xa6, 0x84, 0x92, 0x7c, 0xf9, 0x60, - 0x65, 0x18, 0x9c, 0x63, 0x40, 0x40, 0x57, 0x30, 0xb2, 0xd1, 0x4a, 0x01, - 0xc0, 0x5c, 0x95, 0x70, 0xe3, 0x56, 0xbe, 0xeb, 0xd5, 0xd3, 0xae, 0x14, - 0x2e, 0x6b, 0xa1, 0x9b, 0x54, 0x59, 0x57, 0x7f, 0x17, 0xfa, 0xbe, 0xd6, - 0x77, 0x9c, 0xd5, 0xe7, 0x37, 0x82, 0xf3, 0x42, 0xad, 0x95, 0xda, 0xb2, - 0xbe, 0x27, 0xe2, 0x3b, 0xfc, 0xc8, 0x89, 0x26, 0x77, 0xad, 0x33, 0x19, - 0x01, 0x64, 0xef, 0xeb, 0xa5, 0x62, 0xaa, 0xa1, 0x91, 0x2b, 0xdf, 0x0b, - 0xb4, 0xc0, 0x75, 0x3d, 0xfc, 0x8b, 0x10, 0xf5, 0x09, 0x4e, 0x80, 0xf2, - 0x8a, 0x1c, 0xbe, 0xb3, 0x71, 0xd8, 0x7b, 0x6a, 0x7a, 0x47, 0xce, 0x4b, - 0x43, 0x7c, 0x07, 0x2d, 0x6f, 0x65, 0x09, 0x52, 0x19, 0xbf, 0x59, 0x32, - 0xb0, 0x2e, 0x6c, 0xa1, 0x97, 0x80, 0xab, 0x9e, 0x28, 0x6c, 0xf9, 0x4f, - 0xe8, 0xa0, 0x67, 0x25, 0x2a, 0xa7, 0x48, 0x79, 0x78, 0xde, 0xf4, 0x51, - 0xce, 0x47, 0xbc, 0x96, 0xf1, 0x7c, 0xb8, 0xfd, 0x17, 0xd2, 0x04, 0x40, - 0x82, 0x20, 0x2a, 0x70, 0x08, 0x20, 0x81, 0x6e, 0x3b, 0x66, 0x50, 0xb0, - 0xa8, 0x0a, 0x5e, 0x9f, 0x49, 0x43, 0x22, 0xa4, 0xb4, 0xef, 0x4c, 0xa2, - 0x73, 0xef, 0x5c, 0x84, 0xa7, 0x4a, 0xc3, 0x2c, 0x30, 0x6b, 0x0a, 0x0e, - 0xc2, 0x82, 0x90, 0xfc, 0x03, 0x8f, 0xaf, 0xd8, 0x01, 0x28, 0x06, 0xb7, - 0xcd, 0x6a, 0x54, 0xa8, 0xa6, 0x6b, 0x32, 0xc1, 0xf7, 0xf4, 0xbb, 0x71, - 0x85, 0xc8, 0x90, 0xe3, 0x3e, 0x64, 0x8c, 0x77, 0x16, 0xc9, 0xe4, 0x5e, - 0x41, 0x2d, 0x3a, 0xc6, 0x34, 0x04, 0x02, 0x32, 0x1b, 0x88, 0xf4, 0x14, - 0xfb, 0x63, 0xa8, 0x2c, 0x9a, 0x13, 0xe2, 0x44, 0xc4, 0xf4, 0xc9, 0x3a, - 0x6b, 0xe2, 0x8e, 0x35, 0x10, 0xb2, 0x4a, 0x02, 0x8e, 0xaf, 0xfa, 0xfe, - 0x59, 0x40, 0xa1, 0xc3, 0xc0, 0xf0, 0xbe, 0xd1, 0xf9, 0x12, 0xf9, 0x77, - 0x9d, 0x99, 0xf4, 0x25, 0x47, 0x2a, 0xbe, 0x94, 0x05, 0x79, 0xaf, 0x2c, - 0xc0, 0x17, 0x7f, 0x8f, 0xcb, 0xd9, 0x1c, 0xf5, 0x1b, 0x26, 0xa0, 0x01, - 0xb3, 0x4b, 0xf2, 0x1d, 0x26, 0x2c, 0x10, 0x38, 0xff, 0xf1, 0x50, 0x80, - 0x2f, 0x3f, 0xfc, 0x21, 0x1a, 0xcf, 0x05, 0x13, 0xff, 0x50, 0x10, 0x97, - 0xa4, 0x31, 0x2c, 0x30, 0x6b, 0x0b, 0x0e, 0x42, 0x42, 0x72, 0xa9, 0x98, - 0xb4, 0x11, 0xed, 0xc6, 0xf7, 0xd7, 0xae, 0x65, 0x12, 0x90, 0x05, 0xe5, - 0x25, 0x6b, 0x7e, 0xde, 0xb5, 0xbf, 0xbf, 0x13, 0xd7, 0xdf, 0xa0, 0x2f, - 0x39, 0xe5, 0x74, 0xa0, 0x54, 0x92, 0x25, 0xa0, 0x44, 0x66, 0xf3, 0x13, - 0x3f, 0xae, 0x86, 0xe5, 0x77, 0xaf, 0x90, 0x26, 0x9d, 0x11, 0x41, 0xf9, - 0x98, 0x64, 0xf5, 0xb8, 0xeb, 0x36, 0xb4, 0x53, 0x94, 0x56, 0xac, 0x45, - 0x34, 0xa1, 0x7c, 0x60, 0x71, 0xce, 0xd6, 0xaa, 0xb0, 0xc5, 0x28, 0xe5, - 0x3c, 0xe1, 0xac, 0xf4, 0xf5, 0x4e, 0xc2, 0xc0, 0xb0, 0xbe, 0x2b, 0xa2, - 0xc6, 0x1f, 0x41, 0x38, 0x1b, 0x75, 0xca, 0x4e, 0x24, 0x75, 0x3a, 0x39, - 0xdd, 0xf7, 0xd5, 0xe7, 0x04, 0x61, 0xfa, 0x87, 0xa6, 0x15, 0x91, 0x88, - 0xa7, 0xf8, 0xcb, 0x3f, 0xfb, 0x73, 0x51, 0x39, 0xa6, 0x97, 0xaf, 0x70, - 0xa5, 0x59, 0x60, 0xbc, 0xfc, 0xbe, 0x10, 0x5e, 0x56, 0x43, 0x32, 0xcd, - 0xd6, 0x53, 0x00, 0xbf, 0xaf, 0x81, 0x6b, 0x95, 0x9d, 0x7a, 0x27, 0x17, - 0xbb, 0x50, 0x16, 0xef, 0x00, 0x43, 0xac, 0xcd, 0x4f, 0x54, 0x17, 0x37, - 0x01, 0x7d, 0x20, 0xa0, 0xc7, 0x74, 0x24, 0xc2, 0x36, 0x6e, 0x04, 0x79, - 0x6d, 0x31, 0x1c, 0xe0, 0x1a, 0x27, 0x03, 0x25, 0x16, 0x75, 0x48, 0x17, - 0xaa, 0xd9, 0xa8, 0xf8, 0x34, 0xea, 0x91, 0x41, 0x85, 0x45, 0x10, 0x13, - 0x82, 0x9f, 0x2a, 0x82, 0x72, 0x0a, 0xef, 0x6e, 0x72, 0xff, 0x7c, 0xfb, - 0x59, 0x0c, 0x0c, 0x40, 0x50, 0x04, 0x40, 0x2e, 0xb6, 0x92, 0x53, 0x54, - 0xaa, 0xdb, 0x6f, 0xab, 0x57, 0xbf, 0xff, 0xe5, 0xeb, 0x13, 0x92, 0xb5, - 0x69, 0xb5, 0xe1, 0xb8, 0x68, 0xfd, 0x56, 0x10, 0x28, 0xa5, 0xd9, 0x96, - 0x18, 0x2a, 0x06, 0xc2, 0x83, 0xb0, 0xb8, 0xfe, 0x00, 0x94, 0xd6, 0xc0, - 0x00, 0x5e, 0x6d, 0x09, 0x5a, 0xdd, 0x4a, 0x54, 0x81, 0x2b, 0xb3, 0x65, - 0x06, 0x1b, 0xe5, 0x6b, 0xdb, 0x11, 0xd8, 0x8d, 0x4f, 0xa7, 0x13, 0xc4, - 0xef, 0xfd, 0x15, 0x4b, 0xe1, 0x6b, 0x95, 0xdd, 0x3c, 0xc7, 0x21, 0xb4, - 0x26, 0x3a, 0xeb, 0xe2, 0x85, 0x12, 0xe0, 0xd4, 0xfa, 0x4d, 0x15, 0x00, - 0xf7, 0x91, 0x10, 0xc2, 0x58, 0x74, 0x67, 0xe6, 0xce, 0xe5, 0x42, 0x28, - 0xc0, 0x23, 0x98, 0xf6, 0x68, 0x02, 0xad, 0xd3, 0xe0, 0x1b, 0x43, 0xa2, - 0x30, 0x68, 0x54, 0xe8, 0x08, 0x79, 0x83, 0x18, 0x5a, 0x28, 0x98, 0x2b, - 0x44, 0x2a, 0xcb, 0xcb, 0x0c, 0x32, 0x13, 0xfe, 0xfc, 0x73, 0xa2, 0xca, - 0xe0, 0xff, 0xf1, 0x50, 0x80, 0x2c, 0xff, 0xfc, 0x21, 0x1a, 0xcd, 0x01, - 0xc7, 0xff, 0x48, 0x10, 0x98, 0x86, 0x58, 0x60, 0xd6, 0x16, 0x1c, 0xa0, - 0xcc, 0xc6, 0x54, 0xaf, 0xae, 0xbc, 0x65, 0x77, 0x0a, 0x20, 0x01, 0xae, - 0x6f, 0x25, 0x7c, 0x7b, 0xf5, 0xdf, 0xe3, 0x5a, 0xe7, 0xcf, 0x01, 0x7a, - 0x5e, 0xec, 0xa3, 0xcf, 0x78, 0x0d, 0xb2, 0x86, 0x82, 0x4b, 0xaf, 0x88, - 0x78, 0x1f, 0x19, 0xdf, 0x1a, 0x52, 0x83, 0x03, 0xee, 0x4a, 0x04, 0x44, - 0x34, 0x55, 0x68, 0xa9, 0x4a, 0x72, 0x93, 0x20, 0x83, 0x84, 0x52, 0xf4, - 0xe9, 0x2c, 0x43, 0x54, 0xf2, 0x48, 0x71, 0x9e, 0x45, 0x37, 0x29, 0x74, - 0xfb, 0xee, 0xb2, 0x02, 0xb1, 0x46, 0x02, 0xda, 0xf1, 0x45, 0x67, 0xb5, - 0x15, 0x04, 0xe4, 0x73, 0x03, 0x27, 0x14, 0xbc, 0xaf, 0xee, 0x1e, 0x0b, - 0x38, 0xb4, 0xe5, 0x28, 0x54, 0xc2, 0x11, 0x20, 0xac, 0xf2, 0xbd, 0xda, - 0x1f, 0x0b, 0x8b, 0x16, 0x78, 0xa5, 0x58, 0xe3, 0x91, 0x29, 0xa4, 0x58, - 0x19, 0xf6, 0xf1, 0x00, 0xc4, 0x45, 0x12, 0xa8, 0x23, 0x76, 0x68, 0x27, - 0x7f, 0xa5, 0x54, 0x89, 0x91, 0xdd, 0x4c, 0x89, 0xa9, 0xf0, 0xc9, 0xde, - 0xdf, 0x0b, 0x6a, 0x7d, 0x23, 0x77, 0xf3, 0xdd, 0x72, 0xbe, 0x58, 0x1e, - 0x7b, 0x6a, 0x52, 0x95, 0x8e, 0xb6, 0xff, 0xea, 0xd5, 0xea, 0xa3, 0x42, - 0x81, 0x61, 0x95, 0xef, 0x06, 0x1a, 0x1c, 0xa4, 0x60, 0x9b, 0xde, 0xa2, - 0x42, 0x03, 0x50, 0x01, 0x0c, 0x14, 0x20, 0x4d, 0x92, 0xa8, 0x3d, 0x95, - 0x57, 0x7b, 0x97, 0x77, 0xa7, 0xe2, 0xa1, 0x00, 0xa0, 0x00, 0x85, 0x08, - 0x28, 0x16, 0x48, 0x35, 0xd8, 0xcb, 0x45, 0x80, 0xaf, 0xaf, 0xfa, 0xec, - 0xfa, 0x7f, 0x99, 0xfa, 0xfd, 0x7d, 0x3f, 0x75, 0x29, 0xa7, 0xcb, 0xfe, - 0x9c, 0x51, 0x3c, 0xc5, 0x89, 0x95, 0x64, 0x95, 0xfc, 0x01, 0x3f, 0x1f, - 0xa0, 0x00, 0x00, 0x77, 0xab, 0xcc, 0x9c, 0x65, 0x37, 0x72, 0x80, 0x0a, - 0xe1, 0xec, 0x68, 0x9c, 0x33, 0xd9, 0x5a, 0x0e, 0xfb, 0x4e, 0x98, 0xe6, - 0x74, 0xd6, 0x02, 0x37, 0xa3, 0xaa, 0x8c, 0x07, 0x05, 0xcb, 0x7a, 0xad, - 0x26, 0x20, 0x28, 0xb8, 0xd4, 0x04, 0xd3, 0x9a, 0xd5, 0x14, 0xea, 0xe4, - 0x64, 0xd6, 0xad, 0x3a, 0xe2, 0xf0, 0x31, 0x69, 0xe5, 0x3b, 0xbe, 0x81, - 0xf5, 0x3c, 0xf0, 0x43, 0x66, 0xec, 0xb1, 0x8c, 0xe8, 0x1b, 0x12, 0x45, - 0xe1, 0x8c, 0x67, 0xbf, 0xfe, 0xf7, 0x80, 0x9c, 0xa9, 0x94, 0xdc, 0xdd, - 0x56, 0x25, 0x30, 0x2c, 0x21, 0x13, 0xcb, 0xea, 0x26, 0x48, 0x99, 0x38, - 0xff, 0xf1, 0x50, 0x80, 0x30, 0xbf, 0xfc, 0x21, 0x1a, 0xcd, 0xda, 0x7b, - 0xbb, 0x00, 0x00, 0x94, 0xa1, 0xc2, 0x6d, 0x0c, 0x39, 0x51, 0x8d, 0x86, - 0x61, 0x61, 0x40, 0x41, 0xc4, 0xde, 0x00, 0xe3, 0x90, 0x00, 0x1a, 0xd9, - 0xf7, 0xd7, 0xe7, 0xf1, 0x3e, 0x82, 0x10, 0x9c, 0xdf, 0x86, 0x2f, 0x65, - 0x6c, 0xaa, 0xd2, 0xc0, 0x53, 0x97, 0x30, 0xeb, 0x86, 0x95, 0x29, 0xce, - 0x04, 0x8e, 0x74, 0xcb, 0x22, 0x6f, 0xab, 0x7a, 0xd9, 0x2d, 0x39, 0xb1, - 0xe4, 0x44, 0xe5, 0x8a, 0x2b, 0xe5, 0x51, 0x01, 0x8b, 0xd0, 0xba, 0x6a, - 0xf4, 0xe5, 0x00, 0xb6, 0x09, 0x62, 0x82, 0xf4, 0x08, 0x87, 0x04, 0x03, - 0x28, 0xe5, 0x52, 0xd0, 0xc5, 0x1c, 0xb2, 0xc1, 0xb3, 0xd1, 0xd7, 0x35, - 0x57, 0xee, 0x06, 0x03, 0xe7, 0x3c, 0x39, 0x24, 0x3c, 0x2d, 0xe1, 0xbb, - 0xf6, 0xbc, 0x2d, 0x79, 0x71, 0x78, 0xf9, 0xaa, 0x3c, 0x74, 0x59, 0x39, - 0x49, 0x04, 0x4d, 0x56, 0x7e, 0xad, 0xf1, 0xdb, 0x55, 0xc6, 0x2c, 0x53, - 0x59, 0x4d, 0x09, 0x92, 0x8a, 0xce, 0x0f, 0x51, 0xdf, 0x19, 0x65, 0x1a, - 0x49, 0x31, 0xc4, 0x43, 0x38, 0x21, 0x8a, 0xa5, 0xab, 0xf9, 0x1f, 0xa8, - 0xe3, 0x28, 0x64, 0x2f, 0x75, 0xd8, 0xac, 0x82, 0xa4, 0xc6, 0xeb, 0xa6, - 0x17, 0x0a, 0x65, 0x76, 0xe7, 0xb7, 0x3a, 0xb0, 0xf4, 0xec, 0x6f, 0x03, - 0xc6, 0x09, 0xfc, 0x0b, 0x30, 0xda, 0x63, 0xdb, 0xa9, 0x52, 0x41, 0x60, - 0x71, 0x03, 0x08, 0x7b, 0x21, 0xc6, 0x60, 0x60, 0x03, 0xa5, 0xaf, 0xb5, - 0x42, 0xcf, 0x01, 0x90, 0xe2, 0xb0, 0x0a, 0xde, 0xb1, 0x9e, 0xfb, 0x83, - 0x46, 0xd6, 0xfc, 0xad, 0x18, 0x2c, 0xad, 0x6d, 0x54, 0xcc, 0x45, 0x50, - 0x4c, 0xe0, 0x65, 0x0d, 0x51, 0xd0, 0xea, 0xb0, 0x38, 0xfe, 0xe5, 0x5f, - 0x47, 0x69, 0x1e, 0xa5, 0x9a, 0x54, 0x87, 0x03, 0xfd, 0xd2, 0x49, 0x1c, - 0xd2, 0xe5, 0x24, 0xac, 0x6b, 0x14, 0x1a, 0xc2, 0xc3, 0xb0, 0x90, 0xa4, - 0x3f, 0x06, 0x59, 0x9e, 0xdb, 0xe6, 0xf1, 0x63, 0x2c, 0xcb, 0x32, 0xdd, - 0xdc, 0x32, 0xef, 0x23, 0x79, 0xc5, 0x58, 0x9d, 0x4f, 0xbd, 0xfc, 0x9e, - 0x0d, 0x5d, 0x21, 0xd1, 0x89, 0x42, 0x84, 0xa1, 0xb9, 0x3c, 0x3c, 0x1b, - 0x1a, 0x28, 0x8a, 0xb3, 0x8a, 0xde, 0x39, 0x26, 0xe9, 0xee, 0xe4, 0xf3, - 0xa2, 0x29, 0x09, 0x67, 0x0a, 0x60, 0x8f, 0xe3, 0xbc, 0x35, 0xb8, 0xdb, - 0x38, 0xa4, 0x8e, 0x89, 0x41, 0x15, 0xf1, 0x2a, 0xca, 0x4c, 0x3c, 0x4c, - 0xf2, 0xe3, 0xc2, 0xd8, 0x8d, 0x2f, 0x83, 0xb2, 0xbb, 0x9c, 0x1d, 0x42, - 0x00, 0x10, 0xc9, 0x30, 0xe7, 0x9c, 0x95, 0xa9, 0xd0, 0x80, 0x1e, 0x34, - 0xcd, 0x04, 0x73, 0x07, 0xdf, 0x6f, 0x16, 0xf9, 0xf4, 0x7e, 0x87, 0xf0, - 0x91, 0x96, 0x51, 0x84, 0x8e, 0xff, 0xf1, 0x50, 0x80, 0x2d, 0x9f, 0xfc, - 0x21, 0x1a, 0xcf, 0xf3, 0xdb, 0xff, 0x20, 0x00, 0x91, 0xb2, 0x32, 0x2c, - 0x30, 0x52, 0x0d, 0x85, 0x84, 0xe9, 0x33, 0x31, 0x81, 0xd6, 0xf0, 0x02, - 0x50, 0x02, 0xf2, 0xf3, 0x5a, 0xe3, 0xd4, 0xe7, 0xfd, 0xa6, 0xf5, 0x76, - 0x1e, 0xba, 0x93, 0xdd, 0xbc, 0xb9, 0x16, 0x60, 0x8f, 0xa4, 0x9f, 0x57, - 0x09, 0xd4, 0x2c, 0xf6, 0x89, 0x48, 0x14, 0x74, 0x6b, 0x2b, 0x58, 0xe5, - 0xd8, 0xbf, 0xe0, 0x89, 0xe0, 0xf2, 0x78, 0xf5, 0xe1, 0xa9, 0x78, 0xf2, - 0x49, 0x78, 0xa3, 0x4e, 0x95, 0xe9, 0x28, 0x21, 0x3c, 0x4d, 0x11, 0x9d, - 0x09, 0xf2, 0x73, 0x1f, 0x4a, 0x7a, 0x33, 0x51, 0xa0, 0x0e, 0x37, 0x95, - 0x06, 0xd8, 0xb2, 0x01, 0xd1, 0xcf, 0x4a, 0x66, 0xcc, 0xd8, 0xc7, 0x17, - 0xf7, 0xe4, 0x4e, 0x18, 0x5e, 0x30, 0xc4, 0x5c, 0x5c, 0x59, 0x44, 0x2a, - 0xef, 0x81, 0x32, 0x54, 0xa9, 0x35, 0x8e, 0x40, 0xb2, 0xb2, 0x0a, 0xd6, - 0xf1, 0xb6, 0x82, 0x49, 0x67, 0xb0, 0x2e, 0x42, 0x03, 0x1f, 0x17, 0xb5, - 0x09, 0x11, 0xf0, 0xa2, 0xd1, 0x0e, 0x6f, 0x9e, 0x67, 0x80, 0xd6, 0xcc, - 0xdd, 0x74, 0xbd, 0x5f, 0xea, 0xf8, 0x6b, 0x9b, 0xba, 0x0b, 0xec, 0x6f, - 0x7a, 0x2b, 0x55, 0x0c, 0xbf, 0xb9, 0x01, 0x36, 0x1e, 0x79, 0x01, 0xab, - 0x53, 0x99, 0xaf, 0x86, 0xac, 0x8d, 0x43, 0x56, 0x71, 0xd7, 0x8d, 0xac, - 0xec, 0x7a, 0x13, 0x0e, 0xd3, 0x09, 0x98, 0x2b, 0xc7, 0x19, 0x9e, 0x41, - 0x6a, 0x6b, 0x7e, 0x1a, 0x2e, 0xbd, 0xb0, 0xb6, 0x47, 0x5a, 0x29, 0xa2, - 0xaa, 0xf0, 0x86, 0x7c, 0xe4, 0xb2, 0x35, 0x9e, 0x74, 0x57, 0xe5, 0xfe, - 0x35, 0xa7, 0xd8, 0x76, 0x57, 0x7e, 0x3a, 0xf7, 0x1f, 0xd3, 0x76, 0x84, - 0x5a, 0x3b, 0x64, 0x61, 0x96, 0x18, 0x35, 0x85, 0x47, 0x61, 0x91, 0xfc, - 0x01, 0xd6, 0xf6, 0x82, 0xa2, 0xa0, 0x02, 0xb1, 0x25, 0xf3, 0xc7, 0x32, - 0x51, 0x60, 0xab, 0x0e, 0x7f, 0x99, 0xc4, 0xa3, 0x6f, 0x12, 0xe5, 0x84, - 0xc1, 0x09, 0x62, 0x98, 0xc0, 0x24, 0xb2, 0x8d, 0xfd, 0x3f, 0x12, 0xc8, - 0x81, 0x91, 0x01, 0xc8, 0xc5, 0x97, 0x9d, 0x45, 0xd9, 0xc4, 0x8c, 0x4e, - 0x31, 0xdf, 0x38, 0x99, 0x8b, 0xc4, 0x5a, 0x9c, 0x29, 0x29, 0x8d, 0x61, - 0x1c, 0xc0, 0x8c, 0x41, 0xeb, 0x54, 0x47, 0x95, 0xc2, 0xae, 0x94, 0x05, - 0xb8, 0xbf, 0x80, 0xf5, 0xe2, 0x47, 0x07, 0x1c, 0xa6, 0xb5, 0x66, 0x94, - 0xaf, 0xb8, 0xf9, 0xc9, 0x79, 0x63, 0x14, 0x2e, 0x42, 0xee, 0x01, 0x0b, - 0x22, 0xc6, 0x3e, 0x17, 0xeb, 0xb9, 0xa1, 0x52, 0x4f, 0xff, 0xf1, 0x50, - 0x80, 0x29, 0xdf, 0xfc, 0x21, 0x1a, 0xcf, 0x78, 0xd6, 0xff, 0x10, 0x00, - 0x8f, 0xb6, 0xc1, 0xac, 0x2a, 0xb1, 0x32, 0x18, 0x00, 0x00, 0x00, 0x02, - 0xfc, 0xfb, 0xfd, 0xf3, 0xef, 0xc3, 0x3b, 0xc0, 0xdc, 0x94, 0x13, 0x6f, - 0x32, 0x2c, 0xe0, 0xf9, 0xe9, 0x02, 0x38, 0x82, 0xab, 0x16, 0xcc, 0xa6, - 0x4f, 0xf3, 0xc6, 0x24, 0x8a, 0x81, 0xd1, 0x8f, 0x0a, 0x70, 0x20, 0x2c, - 0x6b, 0xec, 0x41, 0x90, 0xd2, 0xba, 0x5f, 0xa8, 0x41, 0x8c, 0x3a, 0x82, - 0x09, 0x79, 0xd0, 0xf5, 0x31, 0x4c, 0x20, 0x8b, 0xd4, 0x28, 0xe3, 0x1d, - 0xfa, 0x6e, 0x71, 0x02, 0xfd, 0x13, 0x50, 0x90, 0x5f, 0x02, 0x9a, 0x17, - 0xc7, 0xea, 0x75, 0x73, 0xe3, 0x6d, 0x13, 0xff, 0x93, 0x33, 0x77, 0x08, - 0x8c, 0x7f, 0x8e, 0xbe, 0x5a, 0x8d, 0xc5, 0x62, 0x65, 0x5c, 0x7a, 0x02, - 0xe5, 0x00, 0xd4, 0x63, 0xe8, 0xff, 0x9e, 0x55, 0x45, 0x08, 0xa9, 0x00, - 0x00, 0xdf, 0xcb, 0x80, 0x52, 0x89, 0x58, 0x20, 0x28, 0x5d, 0xf2, 0xff, - 0x30, 0x09, 0x05, 0x02, 0x80, 0x4a, 0x95, 0xa9, 0x6d, 0x49, 0x68, 0x07, - 0x94, 0x46, 0x7c, 0x38, 0xc2, 0x3e, 0x20, 0x68, 0x05, 0x73, 0x20, 0xd0, - 0x5b, 0xc5, 0xd4, 0x0f, 0xd3, 0x73, 0x1d, 0x1d, 0xb1, 0xe9, 0x60, 0x30, - 0x7f, 0xa0, 0x9d, 0xe6, 0xe0, 0x4d, 0x7c, 0x68, 0x33, 0x84, 0xbc, 0x15, - 0xd5, 0xa8, 0x1b, 0xe4, 0x1c, 0xcd, 0x8a, 0x51, 0x3d, 0x42, 0xa2, 0x43, - 0xc2, 0x5d, 0xd0, 0xe2, 0x5b, 0x35, 0x81, 0x47, 0xd9, 0x18, 0xf6, 0x36, - 0x2d, 0x85, 0x07, 0x61, 0x51, 0xfc, 0x06, 0x58, 0xc2, 0xd9, 0x79, 0x6c, - 0xb0, 0x15, 0x79, 0x6a, 0x49, 0x2a, 0xd5, 0xb0, 0x32, 0x71, 0xd2, 0xf7, - 0x2a, 0xa9, 0xaf, 0xcc, 0xa1, 0xac, 0xc1, 0xa1, 0xaa, 0x2d, 0x75, 0xa5, - 0x1f, 0x62, 0x5d, 0x30, 0x95, 0x73, 0x10, 0x09, 0x93, 0xe4, 0x12, 0xad, - 0x96, 0x20, 0xd1, 0xbe, 0x86, 0x39, 0x8c, 0xae, 0xea, 0x2f, 0xf6, 0xe7, - 0x9f, 0xa8, 0x4c, 0xc3, 0x54, 0x5f, 0x03, 0xba, 0x12, 0x02, 0xf9, 0x64, - 0xe0, 0x4b, 0x43, 0x7d, 0x9a, 0x28, 0xbf, 0xcb, 0xfe, 0x59, 0x8f, 0xc3, - 0x52, 0xc3, 0x3e, 0xdc, 0x9d, 0x40, 0xa1, 0x45, 0x9b, 0x7b, 0xa2, 0xd6, - 0x03, 0xc8, 0x0e, 0x61, 0x9d, 0x55, 0x30, 0xdc, 0x4c, 0x41, 0x52, 0x52, - 0xc8, 0xaf, 0xbf, 0xfe, 0x20, 0xa5, 0x0e, 0xff, 0xf1, 0x50, 0x80, 0x2f, - 0xbf, 0xfc, 0x21, 0x1a, 0xcd, 0xa7, 0x8f, 0x7f, 0x3f, 0xff, 0x8d, 0xb2, - 0x42, 0x2c, 0x30, 0x6b, 0x0b, 0xac, 0xcc, 0xc6, 0x00, 0x00, 0x00, 0x00, - 0xd7, 0xd7, 0x7a, 0xef, 0xfc, 0xcd, 0xce, 0x68, 0x69, 0xd2, 0x93, 0xef, - 0x8b, 0xb4, 0x0f, 0xdb, 0xac, 0x19, 0xc6, 0xcb, 0xc1, 0x14, 0xe9, 0x0a, - 0x1c, 0xe4, 0x25, 0x41, 0x76, 0xfc, 0xd6, 0xe1, 0xd0, 0x9a, 0x95, 0x84, - 0xf2, 0xbb, 0x0c, 0x8b, 0xcb, 0x62, 0xc3, 0x88, 0xb3, 0xb5, 0x38, 0x91, - 0x84, 0x38, 0x36, 0x84, 0x38, 0x24, 0x2b, 0x65, 0x31, 0x35, 0xad, 0x09, - 0x64, 0x14, 0x64, 0xd0, 0xc8, 0x43, 0xf3, 0xd4, 0xab, 0xd7, 0x0e, 0xd5, - 0xe4, 0xce, 0xa5, 0xba, 0xa4, 0x63, 0x0b, 0xca, 0x91, 0x9c, 0xec, 0xd9, - 0x83, 0xe1, 0x7e, 0x0e, 0xba, 0xb0, 0xce, 0xb0, 0x54, 0xb1, 0x91, 0x39, - 0x16, 0x33, 0x19, 0xe3, 0xf0, 0xf5, 0x56, 0xbd, 0x59, 0x0c, 0x4b, 0x24, - 0x09, 0x2b, 0x77, 0xc3, 0xeb, 0xc5, 0x8b, 0x52, 0x81, 0x21, 0x12, 0x5c, - 0x6b, 0xfd, 0x2f, 0x36, 0x00, 0x29, 0x8d, 0x92, 0x05, 0xf6, 0xb8, 0xc1, - 0x96, 0xb7, 0xd3, 0xeb, 0xad, 0xea, 0xbe, 0x2a, 0xbf, 0x40, 0x23, 0x78, - 0x6f, 0xfa, 0x0e, 0x5d, 0x7b, 0xe0, 0xcf, 0x9f, 0x3f, 0x0d, 0xb8, 0x63, - 0x00, 0x75, 0x5b, 0x24, 0xd1, 0xc2, 0xc3, 0x77, 0xd5, 0x5c, 0x9b, 0x6e, - 0x92, 0x41, 0xa3, 0x1a, 0xf9, 0xc9, 0x42, 0x68, 0xcb, 0xe6, 0x4e, 0xf6, - 0xcb, 0x2b, 0x11, 0x1c, 0xf2, 0xb4, 0x69, 0x5b, 0xc2, 0x4e, 0x33, 0xdc, - 0x73, 0x70, 0x3a, 0x71, 0xe4, 0x22, 0x15, 0x58, 0xd3, 0xb9, 0xc5, 0x20, - 0x86, 0x8f, 0x54, 0x08, 0xcd, 0x60, 0xbd, 0x72, 0x92, 0x1d, 0x0d, 0x03, - 0x95, 0x04, 0x23, 0x1b, 0x65, 0x63, 0xd9, 0x18, 0x76, 0x15, 0x1d, 0x85, - 0xc9, 0xcf, 0x6f, 0x4d, 0xeb, 0x6d, 0x0d, 0x9a, 0x1b, 0xd0, 0x1e, 0x77, - 0x79, 0x72, 0x6b, 0x6b, 0x15, 0x01, 0x8e, 0x6b, 0x48, 0xdd, 0xe8, 0xfb, - 0x34, 0x1c, 0x34, 0x62, 0xe4, 0x74, 0xdf, 0xb6, 0xa7, 0xec, 0xd9, 0x63, - 0xf3, 0xb2, 0x8f, 0xd6, 0x8b, 0x92, 0xc3, 0x26, 0xf3, 0x92, 0xe5, 0x18, - 0xcc, 0x9c, 0x3c, 0x74, 0xaa, 0x91, 0x14, 0x3d, 0x44, 0x07, 0x5e, 0x9a, - 0x3a, 0x7e, 0xbd, 0x21, 0x7f, 0xc8, 0xb4, 0xbb, 0x73, 0x1b, 0x5e, 0x4b, - 0x5b, 0x53, 0x5a, 0x91, 0xf2, 0x40, 0xe3, 0x05, 0x77, 0x75, 0xd7, 0xb7, - 0x6e, 0x48, 0xa6, 0x67, 0x55, 0xc1, 0xd7, 0x08, 0x01, 0x36, 0xa6, 0xe6, - 0xef, 0x77, 0x91, 0xcd, 0x10, 0xcc, 0xa8, 0xac, 0xe4, 0x86, 0x44, 0xc8, - 0xca, 0x57, 0xfa, 0xfe, 0x9b, 0x61, 0x18, 0x4c, 0x95, 0xbb, 0x39, 0x8b, - 0xa6, 0x19, 0x08, 0xe0, 0xff, 0xf1, 0x50, 0x80, 0x31, 0x3f, 0xfc, 0x21, - 0x1a, 0xcf, 0x88, 0x41, 0x59, 0x10, 0x00, 0x8d, 0xb6, 0xc1, 0xd8, 0x76, - 0x17, 0x38, 0x99, 0x88, 0x82, 0x00, 0x00, 0x00, 0x01, 0xad, 0xfc, 0x57, - 0x1d, 0xff, 0x8b, 0xc7, 0x8c, 0x13, 0x8a, 0xd8, 0x9f, 0x49, 0xa2, 0x52, - 0xa7, 0x74, 0x87, 0x1d, 0xe5, 0x16, 0xfb, 0x11, 0x63, 0x9d, 0x51, 0x90, - 0x15, 0x59, 0x0f, 0x1b, 0xa9, 0x40, 0x57, 0x1e, 0x32, 0x00, 0x7e, 0x70, - 0xf5, 0x91, 0x6e, 0xd6, 0xe0, 0x3c, 0x5b, 0xcc, 0xba, 0x22, 0x1c, 0x2d, - 0xdb, 0x40, 0xa1, 0x06, 0xb8, 0x88, 0x14, 0x77, 0x7e, 0x6f, 0xf4, 0xb5, - 0x8d, 0x64, 0x36, 0xd1, 0x80, 0xf7, 0xa1, 0xd8, 0x5f, 0x1a, 0x8f, 0x0e, - 0x5f, 0x04, 0x09, 0xe7, 0x73, 0x14, 0x5f, 0x01, 0xb8, 0x98, 0xd0, 0xc4, - 0x0c, 0x0f, 0x34, 0x54, 0x34, 0xc6, 0x43, 0x8b, 0xc9, 0x82, 0xb8, 0x94, - 0x19, 0xd0, 0x29, 0x53, 0x9b, 0xf2, 0xe5, 0x4a, 0x99, 0x2a, 0x68, 0x2e, - 0xa8, 0x01, 0xab, 0xd7, 0xf0, 0x00, 0xa4, 0x2a, 0xc0, 0x2a, 0xf2, 0xaa, - 0x5c, 0xf8, 0xbf, 0x67, 0x73, 0x25, 0x8a, 0xc1, 0x30, 0x5a, 0x20, 0xb6, - 0xdf, 0x46, 0x12, 0xaf, 0x37, 0x8c, 0x1f, 0x4e, 0x9b, 0x6b, 0xf2, 0xd5, - 0xfd, 0xdf, 0x2f, 0x06, 0x61, 0x03, 0x29, 0x72, 0x09, 0x1e, 0xe8, 0xaa, - 0xb9, 0xa8, 0x19, 0x6d, 0xb8, 0xf4, 0xd7, 0x7f, 0x5b, 0x33, 0xd9, 0x28, - 0xc4, 0x57, 0x62, 0xce, 0xef, 0x7e, 0xf8, 0x4c, 0x22, 0x6e, 0x16, 0x4c, - 0x75, 0xd1, 0x45, 0xea, 0xe4, 0x64, 0x1a, 0x52, 0xd9, 0x20, 0x0a, 0x6c, - 0xcc, 0xf1, 0x01, 0xd3, 0xcd, 0x47, 0x3b, 0xcb, 0x38, 0x15, 0x7d, 0xaa, - 0xb1, 0xb4, 0x56, 0x3d, 0x86, 0x0a, 0x41, 0xb0, 0xa0, 0xec, 0x2e, 0x4f, - 0x53, 0x07, 0xc2, 0xc1, 0xf1, 0x06, 0xdc, 0x56, 0xc6, 0xb6, 0x1a, 0xde, - 0xb7, 0xa6, 0xda, 0xf6, 0x9b, 0xef, 0xca, 0x75, 0x95, 0x3f, 0x75, 0x60, - 0x1c, 0xcb, 0xcb, 0x7c, 0xba, 0xf4, 0xe8, 0x2b, 0x76, 0xfe, 0x7b, 0xbd, - 0xc9, 0x33, 0x53, 0xb2, 0x3c, 0x9c, 0x32, 0xf7, 0xc9, 0xda, 0x1f, 0x75, - 0xcc, 0x84, 0x87, 0x54, 0x9e, 0x52, 0x3e, 0x11, 0x78, 0x95, 0xcb, 0xe4, - 0x26, 0xb9, 0x07, 0x7f, 0x4c, 0xf9, 0xd1, 0x12, 0x36, 0xef, 0x3e, 0xe7, - 0xca, 0xdb, 0xc3, 0x85, 0xd0, 0x8b, 0x74, 0xee, 0x72, 0xca, 0x32, 0x3f, - 0xa8, 0xc3, 0xef, 0x3d, 0x2c, 0x8c, 0xa4, 0xb7, 0x03, 0x0d, 0xd2, 0xd3, - 0xa0, 0xff, 0x1c, 0xf0, 0x86, 0x83, 0x39, 0xc8, 0x67, 0x01, 0x44, 0xcd, - 0x8a, 0x74, 0xea, 0xfe, 0xce, 0x21, 0x03, 0x2c, 0xb2, 0xd2, 0xbb, 0x2b, - 0x24, 0x85, 0xdb, 0xa6, 0x5b, 0xb0, 0xdd, 0xba, 0x40, 0xdc, 0xf7, 0xee, - 0x08, 0xed, 0xc6, 0x6b, 0x67, 0xae, 0x2b, 0xc6, 0xda, 0x8a, 0x2d, 0xde, - 0x1c, 0xff, 0xf1, 0x50, 0x80, 0x32, 0x1f, 0xfc, 0x21, 0x1a, 0xc9, 0x80, - 0x81, 0x02, 0x80, 0x00, 0x8b, 0xbc, 0x49, 0xc4, 0x86, 0x26, 0x30, 0x00, - 0x00, 0x00, 0xbc, 0x0f, 0xae, 0xfa, 0xf1, 0xfe, 0xd3, 0x97, 0x34, 0x11, - 0x73, 0x91, 0x18, 0x30, 0x3e, 0xb9, 0x55, 0x60, 0x8b, 0xdb, 0xde, 0x69, - 0x1c, 0x29, 0x8e, 0x90, 0xf2, 0x85, 0x52, 0x7c, 0x48, 0x3b, 0x69, 0xe9, - 0x23, 0x85, 0x99, 0xc4, 0x47, 0x67, 0xb6, 0xca, 0xd1, 0x39, 0x94, 0x89, - 0x75, 0xe8, 0x92, 0xcc, 0xf3, 0xea, 0x65, 0x2a, 0x3a, 0x47, 0x10, 0x17, - 0xb3, 0xec, 0x44, 0x26, 0x16, 0x2a, 0xf9, 0x1d, 0xb2, 0x84, 0x16, 0x45, - 0x04, 0x43, 0x0e, 0x2b, 0x9b, 0x99, 0xa9, 0xf4, 0x27, 0xd0, 0x16, 0xcd, - 0x0a, 0x9b, 0xca, 0xb8, 0xc7, 0xdb, 0x20, 0x28, 0x86, 0x20, 0x8f, 0x4e, - 0x8a, 0xf4, 0x07, 0x8d, 0x28, 0xd1, 0xe7, 0x1a, 0x52, 0xa0, 0x9b, 0xe5, - 0x87, 0x0a, 0x14, 0x16, 0x34, 0xa2, 0x35, 0x99, 0x6c, 0x84, 0xcc, 0xc2, - 0x48, 0xa5, 0x5e, 0x87, 0x9b, 0xfd, 0x8b, 0x08, 0x4c, 0xef, 0xce, 0x82, - 0x45, 0x58, 0x2e, 0xad, 0x78, 0xf5, 0x9d, 0x0e, 0x20, 0x16, 0xc2, 0x81, - 0x03, 0x18, 0x5b, 0x1d, 0x7f, 0xbb, 0xec, 0x91, 0x63, 0xef, 0x05, 0x20, - 0x5d, 0xa7, 0x48, 0x69, 0xfd, 0x63, 0xfc, 0x7c, 0xfe, 0x2d, 0x66, 0xda, - 0x94, 0x93, 0xa7, 0x73, 0x25, 0x59, 0xed, 0xf8, 0x94, 0x7f, 0x4a, 0x06, - 0xfa, 0xf8, 0xcd, 0x4c, 0x88, 0xcf, 0x9e, 0xaf, 0x75, 0xd6, 0x21, 0x5d, - 0x81, 0x3d, 0x36, 0x49, 0x7b, 0xec, 0x8c, 0x89, 0xa7, 0xd1, 0x24, 0x95, - 0x44, 0x50, 0xaf, 0x52, 0xa9, 0xdd, 0xd6, 0x8b, 0x5c, 0xb1, 0x22, 0x0d, - 0x77, 0x38, 0x77, 0x9c, 0x3b, 0xd2, 0x44, 0xa2, 0xb5, 0x55, 0x34, 0x18, - 0xc2, 0x24, 0xe4, 0x63, 0x12, 0x56, 0xa8, 0x17, 0x93, 0xc1, 0x3b, 0x9c, - 0x95, 0x95, 0x8d, 0x62, 0x63, 0x58, 0x5d, 0x1e, 0x96, 0x17, 0x90, 0xf6, - 0x7a, 0x03, 0xdb, 0xd0, 0x3d, 0xbd, 0x03, 0xdb, 0xd7, 0xd7, 0x3c, 0x65, - 0x4d, 0x71, 0xcd, 0x7e, 0xed, 0x52, 0x81, 0x93, 0x5a, 0xd4, 0xfb, 0x04, - 0xf1, 0xf1, 0x70, 0xf8, 0x00, 0xe8, 0xc7, 0x7d, 0xd0, 0x5a, 0x2e, 0xa8, - 0xa3, 0x7d, 0xef, 0xe4, 0x47, 0x81, 0xe3, 0x9c, 0x89, 0x5e, 0x81, 0x6a, - 0x3b, 0xf0, 0xe6, 0x2f, 0x55, 0xfc, 0x3c, 0x03, 0xe5, 0xa4, 0x04, 0x9f, - 0xb9, 0xa8, 0x88, 0x56, 0xf3, 0x11, 0x01, 0x57, 0x00, 0x97, 0xdf, 0x7f, - 0x2c, 0x03, 0x38, 0xbb, 0xe4, 0x2a, 0xa1, 0x56, 0xc9, 0x50, 0x26, 0x58, - 0xfc, 0x4e, 0xc7, 0xda, 0x37, 0x15, 0x22, 0x32, 0xb1, 0x78, 0x30, 0x26, - 0x25, 0x39, 0xef, 0xe1, 0x66, 0x00, 0x8b, 0x81, 0x40, 0x23, 0x81, 0x13, - 0xd4, 0x56, 0xc9, 0x36, 0xe0, 0x65, 0xba, 0xfa, 0xa0, 0x8d, 0xef, 0xd2, - 0x8e, 0x62, 0x96, 0x52, 0x3c, 0xff, 0xf1, 0x50, 0x80, 0x34, 0xbf, 0xfc, - 0x21, 0x1a, 0xcf, 0x88, 0x00, 0x02, 0x10, 0x00, 0x8b, 0xba, 0x30, 0xec, - 0x2e, 0x71, 0x12, 0x11, 0x8c, 0x00, 0x00, 0x00, 0x09, 0x5d, 0x67, 0xeb, - 0xf3, 0xf1, 0xf3, 0xf7, 0xf6, 0xce, 0x6f, 0xc0, 0x6f, 0xdd, 0x64, 0x23, - 0x0e, 0x25, 0x4c, 0x29, 0x08, 0x90, 0x03, 0xfe, 0x1c, 0x5f, 0x1c, 0xb0, - 0x18, 0x9c, 0xc6, 0xf5, 0xe6, 0x78, 0xc7, 0x7b, 0x61, 0x81, 0x02, 0xc9, - 0xa7, 0x22, 0x6d, 0x30, 0x33, 0x91, 0x52, 0x32, 0xc8, 0x47, 0xc0, 0x78, - 0xeb, 0xbe, 0xfa, 0xc8, 0xc6, 0x37, 0xa8, 0xf4, 0xa7, 0x0b, 0x3e, 0xa5, - 0x25, 0x31, 0x9a, 0x39, 0x68, 0x32, 0xca, 0xba, 0xb3, 0xd2, 0x56, 0xa4, - 0x6a, 0xab, 0x48, 0x8a, 0x52, 0xfa, 0x0c, 0x78, 0xd8, 0x20, 0x14, 0x82, - 0xd6, 0xb5, 0x09, 0xa9, 0x34, 0x20, 0x3a, 0x67, 0xb7, 0x3c, 0xb2, 0x4c, - 0x01, 0x44, 0x0c, 0xc6, 0x48, 0x6d, 0x97, 0x5d, 0x1e, 0x14, 0x31, 0xa6, - 0x03, 0x91, 0xd6, 0xc3, 0x34, 0x62, 0x32, 0x5a, 0x56, 0xfd, 0x9e, 0x6c, - 0x15, 0x7a, 0x8c, 0x55, 0x15, 0x02, 0x71, 0x48, 0x4a, 0xee, 0xff, 0xbb, - 0x3a, 0x84, 0x56, 0x35, 0x66, 0x20, 0x31, 0x00, 0xcf, 0x57, 0xc9, 0xa4, - 0x02, 0xd8, 0xdc, 0xbc, 0xb4, 0xf0, 0x8b, 0x70, 0x2e, 0xd4, 0x23, 0xa5, - 0xe6, 0x57, 0xc2, 0xbb, 0x6d, 0x66, 0xf0, 0x7a, 0x6e, 0xea, 0x5b, 0xaf, - 0xb1, 0x6e, 0xfb, 0x7b, 0x07, 0x84, 0x59, 0x08, 0x15, 0xb6, 0x58, 0x0a, - 0xf8, 0xe2, 0x4a, 0x44, 0x91, 0x54, 0x5c, 0x94, 0x83, 0xa2, 0x86, 0x84, - 0x8a, 0xa3, 0x0b, 0xe1, 0x66, 0xc6, 0xda, 0x29, 0xe1, 0x66, 0xbe, 0x44, - 0x2d, 0xd5, 0x66, 0x4e, 0xd6, 0x9d, 0xb2, 0x22, 0x31, 0x54, 0x8c, 0xf2, - 0x51, 0xa4, 0x24, 0x1b, 0x22, 0x39, 0x9a, 0x56, 0x48, 0x41, 0x71, 0xbd, - 0x34, 0x1f, 0x83, 0x41, 0x25, 0xf3, 0x41, 0x82, 0x0c, 0x45, 0xcc, 0x01, - 0x17, 0x65, 0x63, 0xd8, 0x98, 0xb6, 0x14, 0x1b, 0x91, 0x03, 0xea, 0x60, - 0xf8, 0x98, 0x3e, 0x10, 0xf1, 0xe7, 0xc7, 0x9f, 0x00, 0x00, 0x35, 0xaf, - 0x1f, 0x8f, 0x9f, 0xbd, 0x66, 0xae, 0x56, 0xef, 0xf1, 0x2b, 0x9a, 0x06, - 0xbb, 0xe5, 0x03, 0xdc, 0x15, 0x63, 0x09, 0x55, 0x44, 0x76, 0x92, 0x86, - 0x88, 0xed, 0x2b, 0xa6, 0x9b, 0xb6, 0xf4, 0xac, 0xef, 0x3f, 0x39, 0x9f, - 0xfc, 0x62, 0x38, 0xac, 0x99, 0x36, 0x02, 0x1e, 0x50, 0x88, 0xbb, 0x16, - 0x9a, 0x71, 0xa7, 0x64, 0xa2, 0x44, 0x32, 0xec, 0x00, 0x72, 0x10, 0x83, - 0x0b, 0xec, 0x1a, 0x28, 0xd7, 0xf1, 0x8a, 0x0e, 0x4b, 0x1b, 0x5c, 0xf5, - 0x55, 0xfc, 0x91, 0x6b, 0x79, 0xeb, 0x2e, 0xab, 0xe1, 0x7d, 0xc8, 0x56, - 0x36, 0xa4, 0x44, 0xe2, 0x17, 0x19, 0xaa, 0xe1, 0x84, 0xd5, 0xea, 0xf6, - 0x59, 0x0c, 0x00, 0xff, 0x56, 0x2c, 0x68, 0x14, 0x96, 0xdb, 0xba, 0x6b, - 0x2b, 0xc6, 0xe4, 0xba, 0xe5, 0xbe, 0xe3, 0xbd, 0xa0, 0x99, 0xda, 0x22, - 0x2f, 0x8c, 0x02, 0xe9, 0xd4, 0xf8, 0xff, 0xf1, 0x50, 0x80, 0x31, 0xdf, - 0xfc, 0x21, 0x1a, 0xce, 0x80, 0x81, 0x00, 0x40, 0x00, 0x8a, 0xba, 0x30, - 0xec, 0x32, 0x71, 0x22, 0x0d, 0x86, 0x82, 0x00, 0x00, 0x00, 0x01, 0x35, - 0xd7, 0x7f, 0x1e, 0xbf, 0x1e, 0xdd, 0xbc, 0x60, 0xc8, 0xf6, 0xfc, 0x2a, - 0x22, 0x1d, 0x15, 0x0b, 0x4c, 0xd6, 0xf0, 0x73, 0x4f, 0x86, 0xc2, 0xa2, - 0xe9, 0x54, 0x1e, 0x27, 0x33, 0x11, 0xd4, 0x2e, 0x26, 0xca, 0x18, 0xba, - 0x77, 0x39, 0xe5, 0x95, 0x7b, 0xf4, 0x7f, 0xae, 0x89, 0x42, 0xac, 0x4b, - 0x8b, 0xee, 0x88, 0x4e, 0x56, 0x14, 0x21, 0x4b, 0x13, 0xd1, 0xf4, 0x66, - 0x23, 0x4f, 0x6d, 0x38, 0xc7, 0x34, 0x1c, 0x8d, 0x87, 0xd1, 0xc3, 0x8a, - 0xac, 0xc1, 0x18, 0xc2, 0x88, 0x35, 0x6c, 0x82, 0x27, 0x3c, 0xc2, 0x7d, - 0x4c, 0xd1, 0x8d, 0xfc, 0x7a, 0xf9, 0xcd, 0xe6, 0x03, 0x23, 0xe6, 0xdf, - 0x0b, 0xf9, 0xda, 0x95, 0xc9, 0x68, 0xea, 0x7d, 0x23, 0xf8, 0xf3, 0x2b, - 0xdf, 0x2c, 0x80, 0x73, 0x8d, 0xa8, 0xd6, 0xdb, 0x9e, 0x71, 0x35, 0xba, - 0xee, 0x60, 0x95, 0xaf, 0xec, 0x1f, 0x59, 0xc6, 0x28, 0xb8, 0x0d, 0x62, - 0xcc, 0x14, 0x02, 0xf2, 0xe7, 0xf5, 0xf2, 0x49, 0x75, 0x18, 0x48, 0x84, - 0x40, 0xa9, 0xc1, 0x38, 0xd4, 0xf6, 0x98, 0x0b, 0xa3, 0x67, 0x10, 0x12, - 0x96, 0x00, 0xe1, 0xbb, 0xdb, 0xe1, 0xff, 0x8d, 0xd5, 0x42, 0xa4, 0x60, - 0x8e, 0xee, 0x71, 0x5f, 0xa6, 0x54, 0x35, 0xee, 0xa9, 0xde, 0xdb, 0x6b, - 0x7d, 0x4f, 0xbb, 0xee, 0x79, 0x1d, 0x2a, 0x7d, 0x72, 0xf9, 0x0c, 0xb3, - 0xed, 0xea, 0xf0, 0xeb, 0xa5, 0x9b, 0x25, 0xb7, 0xb2, 0xf4, 0xb6, 0x19, - 0x29, 0x9c, 0xe2, 0xe0, 0xa1, 0xa1, 0xfb, 0xac, 0xb2, 0xc6, 0xb1, 0x6a, - 0x8e, 0x04, 0x50, 0xb7, 0xaa, 0xd3, 0x23, 0x46, 0xf8, 0x1a, 0x40, 0x1a, - 0x14, 0xea, 0x88, 0xec, 0x29, 0x53, 0x41, 0x04, 0x55, 0x91, 0x91, 0x61, - 0x62, 0xba, 0x7d, 0x4c, 0x2f, 0x21, 0xeb, 0xdb, 0xd7, 0x5d, 0x87, 0xb7, - 0xa0, 0x7b, 0x7a, 0x01, 0x91, 0xae, 0x7a, 0xcb, 0xad, 0xff, 0xb7, 0x5d, - 0x55, 0x60, 0x0f, 0x7f, 0xf5, 0xef, 0xa6, 0x9f, 0xea, 0x74, 0xd9, 0x8b, - 0xb3, 0xa9, 0xd0, 0xee, 0xb2, 0xaa, 0xec, 0x31, 0x98, 0xab, 0x6a, 0x42, - 0xda, 0xcc, 0xe3, 0x72, 0x49, 0xcf, 0x54, 0x41, 0xc6, 0x22, 0x9f, 0xce, - 0x25, 0x3d, 0xf7, 0x6c, 0x91, 0x45, 0xa9, 0xaf, 0x8a, 0xa8, 0x0f, 0x89, - 0x2d, 0x37, 0x93, 0xbb, 0xff, 0x72, 0xae, 0xb7, 0xde, 0xb6, 0x8b, 0x32, - 0x6d, 0x53, 0x9d, 0x33, 0x2e, 0x61, 0x86, 0xbf, 0xeb, 0xfa, 0x11, 0x18, - 0xa8, 0x61, 0x24, 0xa6, 0x03, 0x1c, 0x43, 0x89, 0xd4, 0xc0, 0x00, 0x9c, - 0xcb, 0x5c, 0x8e, 0x80, 0x34, 0x48, 0x64, 0x54, 0xd5, 0x52, 0xeb, 0x22, - 0x9e, 0xb5, 0x6b, 0xad, 0x79, 0xed, 0xef, 0xe0, 0xff, 0xf1, 0x50, 0x80, - 0x26, 0xbf, 0xfc, 0x21, 0x1a, 0xc9, 0x33, 0xf1, 0xff, 0x00, 0x00, 0x90, - 0xb2, 0x32, 0x2c, 0x2c, 0x57, 0x7a, 0x11, 0x8c, 0x04, 0xa0, 0x00, 0x01, - 0xad, 0xf5, 0xdf, 0xe2, 0xb5, 0x3a, 0xef, 0xe1, 0x49, 0x82, 0x73, 0x5c, - 0x8b, 0xbe, 0x30, 0xf6, 0xc6, 0xa9, 0xc9, 0x70, 0x68, 0x94, 0xed, 0x22, - 0xfa, 0xb9, 0x1b, 0x43, 0x40, 0x32, 0xcf, 0xee, 0x65, 0x3e, 0x98, 0x23, - 0x13, 0x03, 0xf6, 0x1c, 0x1c, 0xd3, 0xa4, 0x07, 0x02, 0x43, 0x9c, 0x40, - 0xcf, 0x14, 0x05, 0x26, 0x98, 0x0f, 0x0f, 0xd4, 0x63, 0x79, 0x4e, 0x32, - 0x9b, 0x44, 0x80, 0x44, 0x82, 0xf0, 0xe2, 0xf5, 0xb0, 0x00, 0x24, 0x01, - 0x41, 0x9f, 0x1b, 0x86, 0x00, 0xb2, 0x00, 0xa0, 0x42, 0xdc, 0x9d, 0xa0, - 0x20, 0x90, 0x2a, 0x40, 0x21, 0xbb, 0x60, 0x09, 0x56, 0x33, 0x31, 0xa7, - 0x9a, 0x68, 0x10, 0x2b, 0x49, 0x22, 0x41, 0x2c, 0x26, 0x6f, 0x30, 0x04, - 0x19, 0x5b, 0x8f, 0xca, 0xf0, 0xa7, 0x85, 0xac, 0xf9, 0xda, 0xe8, 0x0b, - 0xcb, 0x88, 0x07, 0x70, 0x11, 0x5b, 0x40, 0x57, 0x5d, 0x04, 0x6d, 0xbd, - 0x44, 0x92, 0x18, 0xd7, 0x6d, 0xc0, 0x05, 0x21, 0xcb, 0x75, 0x77, 0x29, - 0x5b, 0x2a, 0x92, 0xdd, 0x0d, 0x2b, 0xa2, 0x22, 0xa3, 0x1b, 0x41, 0xd1, - 0x72, 0x4a, 0x49, 0xa7, 0xb6, 0x8c, 0x8b, 0x71, 0xd8, 0x68, 0x4f, 0x31, - 0x41, 0x41, 0x32, 0xa4, 0x4f, 0x07, 0x22, 0xad, 0x48, 0x89, 0x3c, 0x05, - 0x34, 0x24, 0xa0, 0x13, 0xa0, 0xa2, 0xdf, 0x71, 0xe7, 0x25, 0x65, 0x53, - 0xd8, 0x65, 0x66, 0x3f, 0x81, 0x78, 0xb9, 0xb5, 0x83, 0x0b, 0x00, 0xac, - 0xb6, 0x9b, 0xe9, 0xbb, 0x52, 0x60, 0x67, 0xb5, 0xed, 0x00, 0xed, 0x23, - 0x51, 0x55, 0xf7, 0xd4, 0xd5, 0x71, 0x18, 0xfd, 0x58, 0xc7, 0x2c, 0x61, - 0x94, 0xd2, 0x60, 0x7e, 0x9b, 0xc9, 0xe2, 0x95, 0xcd, 0xc3, 0xcc, 0xb8, - 0x9d, 0xbc, 0x48, 0xef, 0xb9, 0x8a, 0xaa, 0xea, 0xb8, 0x65, 0x56, 0xdc, - 0x12, 0x9f, 0xd3, 0xbc, 0x36, 0xb4, 0x93, 0x77, 0x12, 0x6a, 0xa0, 0x69, - 0xa1, 0x74, 0xb2, 0x35, 0xbd, 0x0f, 0xbe, 0x61, 0x24, 0xed, 0x09, 0x5a, - 0x81, 0xbb, 0xfb, 0x80, 0x38, 0xff, 0xf1, 0x50, 0x80, 0x35, 0x5f, 0xfc, - 0x21, 0x1a, 0xcf, 0x2f, 0xb1, 0xd9, 0x80, 0x00, 0x91, 0xb1, 0xb2, 0x6c, - 0x28, 0x67, 0x0a, 0xad, 0x8e, 0x85, 0x1c, 0xce, 0xb8, 0xf1, 0xab, 0xed, - 0xcf, 0x07, 0x15, 0xb4, 0xe9, 0xe3, 0xea, 0xbd, 0xff, 0x7d, 0xcf, 0x9f, - 0xf1, 0xbd, 0x77, 0xaf, 0xcf, 0xef, 0xef, 0x1d, 0xd8, 0x67, 0xd0, 0xb6, - 0xf0, 0x71, 0x54, 0x1e, 0x50, 0x76, 0x8c, 0x49, 0x35, 0x9b, 0x6d, 0x40, - 0xe9, 0x7f, 0x56, 0x4c, 0xd3, 0x20, 0x34, 0x82, 0xf8, 0x5c, 0x12, 0x5e, - 0x66, 0xf8, 0xdb, 0xfd, 0xff, 0xa3, 0xc7, 0x5f, 0x77, 0x83, 0xef, 0x0f, - 0x49, 0x1a, 0xb0, 0xf3, 0x7a, 0x98, 0x2e, 0xe1, 0xb9, 0x3e, 0x1b, 0x8b, - 0x8d, 0x4e, 0x29, 0xcf, 0x13, 0x14, 0xdf, 0x1f, 0x7e, 0x01, 0xba, 0x6f, - 0x5f, 0x0c, 0xc7, 0x66, 0x63, 0x5f, 0x0d, 0xe2, 0xf2, 0xe5, 0x8b, 0x5b, - 0xc0, 0x01, 0x38, 0x4b, 0x75, 0x8b, 0x8b, 0xdc, 0x6e, 0xd5, 0x3b, 0xcf, - 0xe9, 0xa5, 0x51, 0x7d, 0x1b, 0xd4, 0xf1, 0xeb, 0x6d, 0xab, 0xe9, 0x8d, - 0xd5, 0x01, 0x5c, 0xc0, 0x07, 0x2a, 0xa2, 0x54, 0xc3, 0x3a, 0x54, 0x1b, - 0x2a, 0x12, 0x71, 0x03, 0x0b, 0x1d, 0x76, 0x8c, 0xf7, 0xc4, 0xe6, 0x21, - 0x3a, 0xe4, 0x01, 0x35, 0x8e, 0x00, 0x57, 0x37, 0x67, 0x9f, 0x8a, 0xbe, - 0x46, 0x19, 0x40, 0xa9, 0x22, 0xe4, 0xca, 0x54, 0x32, 0xa8, 0x5d, 0x78, - 0xdb, 0x3d, 0x95, 0xcd, 0xc1, 0xba, 0x8c, 0x61, 0x46, 0x19, 0x48, 0x04, - 0x00, 0x00, 0x2e, 0x9c, 0xf3, 0x92, 0x02, 0xa4, 0x76, 0xb3, 0x7c, 0xd2, - 0xd7, 0xaf, 0x3f, 0xfd, 0x84, 0x16, 0x47, 0x06, 0x38, 0xc1, 0x1d, 0x91, - 0x84, 0x69, 0x7f, 0xb0, 0x6a, 0xef, 0xa9, 0xfc, 0xa0, 0x79, 0x14, 0x83, - 0xf2, 0x0c, 0x77, 0x7b, 0xc8, 0xd2, 0x57, 0x23, 0xf5, 0xc4, 0x62, 0x43, - 0x11, 0xc1, 0x5e, 0x8c, 0x8d, 0x15, 0x0f, 0x61, 0x73, 0xa0, 0xdc, 0xbe, - 0x84, 0x0f, 0x89, 0x88, 0x0b, 0xc6, 0xaf, 0xb0, 0x04, 0xaf, 0x3e, 0x24, - 0xcf, 0xdf, 0xaf, 0x5e, 0x77, 0x95, 0x36, 0x6f, 0xe2, 0x4c, 0x11, 0x25, - 0xef, 0x62, 0xaf, 0x1c, 0xac, 0x9d, 0xa5, 0x5a, 0x44, 0x9d, 0x8f, 0x0d, - 0xbb, 0x4d, 0xe1, 0x18, 0x50, 0x99, 0xca, 0xce, 0x8b, 0x79, 0xec, 0x45, - 0xde, 0x0a, 0xe6, 0xc3, 0xe1, 0xf5, 0x88, 0x61, 0xdc, 0x56, 0x6a, 0x85, - 0xd9, 0x9f, 0xf7, 0xfc, 0x3c, 0xc1, 0x19, 0x12, 0x0d, 0xac, 0x40, 0x5f, - 0x07, 0x2d, 0x98, 0xa7, 0x5f, 0xdd, 0xff, 0x66, 0x36, 0xba, 0x92, 0x69, - 0x82, 0x93, 0x69, 0xcd, 0x00, 0x6c, 0xe3, 0xd8, 0x06, 0xbe, 0x5a, 0x9c, - 0xd8, 0xaa, 0x8d, 0x5a, 0xcf, 0x6d, 0x80, 0xda, 0x0f, 0xb2, 0x37, 0x1f, - 0x65, 0xd4, 0x30, 0x5a, 0x31, 0x1b, 0xa5, 0xe1, 0x5b, 0x32, 0xe4, 0x9d, - 0x8f, 0x39, 0x95, 0x1a, 0x27, 0x36, 0x2d, 0x6f, 0x4f, 0xa2, 0x8e, 0x7c, - 0xda, 0x7c, 0xf9, 0x78, 0x07, 0x5c, 0x0d, 0xd1, 0x55, 0xd9, 0xdd, 0x3e, - 0x67, 0x48, 0x0f, 0x94, 0xaa, 0xd8, 0x6f, 0x25, 0xb2, 0x76, 0x70, 0xff, - 0xf1, 0x50, 0x80, 0x2a, 0xff, 0xfc, 0x21, 0x1a, 0xcc, 0x99, 0xf2, 0xf9, - 0x00, 0x00, 0x8f, 0xb6, 0xb1, 0x5c, 0x4a, 0xc4, 0x23, 0x1c, 0x00, 0x03, - 0xae, 0xc0, 0x1e, 0x7c, 0x7d, 0x7c, 0xff, 0x86, 0x4e, 0x33, 0xcd, 0x65, - 0xd5, 0x03, 0x77, 0x4c, 0xbe, 0x3e, 0x4c, 0x8f, 0xf6, 0x1f, 0xe3, 0x46, - 0x8a, 0xcb, 0x8b, 0x3a, 0x28, 0xcf, 0xb3, 0x01, 0x72, 0x61, 0x8b, 0x18, - 0x61, 0x8e, 0x86, 0x34, 0x44, 0x33, 0x65, 0xbb, 0x0f, 0x54, 0xe0, 0x08, - 0x77, 0xb4, 0xef, 0x62, 0x86, 0x89, 0x85, 0x53, 0x64, 0x88, 0x30, 0xf3, - 0x51, 0x15, 0x3e, 0xab, 0x3c, 0x6e, 0x8b, 0xc6, 0xea, 0xb5, 0xb8, 0x17, - 0x02, 0x11, 0x06, 0xbd, 0xff, 0xf7, 0x41, 0x42, 0x5b, 0xc2, 0x17, 0x54, - 0x12, 0x18, 0xc7, 0xa8, 0x24, 0x2f, 0x16, 0x49, 0x2b, 0x96, 0x14, 0x20, - 0x02, 0x31, 0x4c, 0x88, 0x98, 0x2a, 0xb6, 0x17, 0x51, 0x88, 0x01, 0xd1, - 0xc6, 0x3a, 0xb9, 0xdd, 0xdb, 0xaf, 0x86, 0x2d, 0x52, 0x98, 0x9f, 0xcf, - 0x3f, 0x67, 0x32, 0x7c, 0xc3, 0x39, 0x2f, 0x2d, 0x2a, 0xb1, 0xd5, 0x01, - 0x92, 0x87, 0x24, 0xfb, 0xf3, 0xb4, 0xf2, 0xcf, 0xa4, 0xff, 0x35, 0xcb, - 0x8d, 0x9c, 0xfb, 0xca, 0x52, 0x80, 0xbc, 0xa4, 0x56, 0x10, 0x07, 0x09, - 0x45, 0xcd, 0x91, 0x3b, 0x6f, 0xa6, 0x91, 0x3c, 0xf3, 0x6c, 0xb1, 0xdd, - 0x2c, 0x5c, 0x9d, 0x72, 0xd5, 0x12, 0x14, 0x46, 0x13, 0xd0, 0xbd, 0x2d, - 0x76, 0xf0, 0xb4, 0x61, 0x4a, 0x06, 0x91, 0xee, 0x58, 0xd5, 0x36, 0xcc, - 0x2a, 0xd0, 0x74, 0xc1, 0x66, 0x8d, 0x36, 0x36, 0xae, 0x2c, 0x57, 0x62, - 0xf1, 0x24, 0xb0, 0xf1, 0x4e, 0x4b, 0xb5, 0xf4, 0xde, 0x8f, 0x8b, 0xdc, - 0xcc, 0xb7, 0xa5, 0xa4, 0x91, 0xf6, 0x54, 0x3d, 0x85, 0x86, 0x83, 0x94, - 0x7c, 0x18, 0xbc, 0x5b, 0x16, 0x18, 0xbc, 0xb0, 0xc5, 0xe2, 0xf3, 0x57, - 0xcf, 0x15, 0xbd, 0x55, 0x55, 0x80, 0x15, 0xf8, 0x69, 0x94, 0x07, 0xf2, - 0x59, 0x6e, 0x2b, 0x29, 0x53, 0xc0, 0x48, 0x6f, 0xfe, 0x9d, 0x37, 0x52, - 0xf1, 0x76, 0x90, 0x49, 0xc8, 0x22, 0x4a, 0xbe, 0x52, 0xe0, 0x12, 0xd4, - 0x66, 0xb5, 0x61, 0x97, 0x01, 0x12, 0xf5, 0x24, 0xf2, 0x96, 0x87, 0x95, - 0xea, 0xbf, 0x28, 0xcb, 0x22, 0xab, 0x2b, 0x98, 0x9b, 0x88, 0x11, 0x11, - 0x25, 0x0a, 0xc7, 0xf9, 0x1e, 0xb6, 0x04, 0xc0, 0x30, 0xc6, 0x0c, 0x6e, - 0x6e, 0x40, 0x98, 0xe1, 0x48, 0x07, 0xff, 0xf1, 0x50, 0x80, 0x2d, 0x7f, - 0xfc, 0x21, 0x1a, 0xcf, 0xbf, 0x73, 0xff, 0x80, 0x00, 0x8f, 0xb2, 0x32, - 0x2c, 0x2c, 0x55, 0x49, 0x0c, 0xc2, 0xcb, 0x80, 0x81, 0x28, 0x03, 0x8e, - 0x40, 0x1f, 0x7f, 0xb7, 0x5d, 0xff, 0xa7, 0x7c, 0x24, 0xe9, 0x55, 0x30, - 0x1a, 0xf9, 0x26, 0x30, 0xf8, 0x62, 0xdc, 0xa2, 0x26, 0xb9, 0xbb, 0x82, - 0x0f, 0x14, 0xb7, 0x9d, 0x57, 0x93, 0x51, 0xe1, 0x65, 0xed, 0x7b, 0xd5, - 0x07, 0xbb, 0xfc, 0x90, 0x07, 0x9c, 0xa9, 0x16, 0x79, 0x88, 0x0d, 0x28, - 0xdb, 0xc4, 0xec, 0x44, 0xee, 0x84, 0x03, 0x9e, 0x39, 0x60, 0x6f, 0xd9, - 0xac, 0xca, 0xae, 0xe9, 0xb8, 0xd4, 0xdc, 0x89, 0x00, 0x8f, 0xd7, 0xf5, - 0x7c, 0x80, 0x0a, 0xd5, 0x88, 0x41, 0x01, 0x53, 0x8f, 0x88, 0x50, 0x75, - 0x2b, 0xbf, 0xa9, 0xbc, 0x6c, 0x1b, 0x91, 0x70, 0x03, 0x81, 0x3a, 0xbb, - 0x68, 0xf3, 0x60, 0xdc, 0x44, 0x51, 0xb2, 0x47, 0x1b, 0x66, 0x3b, 0x08, - 0x2d, 0xb1, 0x6f, 0x06, 0x02, 0x03, 0xae, 0x93, 0x15, 0x53, 0x80, 0x05, - 0x9a, 0x60, 0x82, 0x80, 0x0c, 0xde, 0x01, 0x37, 0xcf, 0x11, 0x74, 0x5f, - 0x6d, 0x15, 0x90, 0x08, 0xe7, 0x63, 0x80, 0x40, 0xa2, 0xd8, 0x94, 0x56, - 0x79, 0x45, 0x17, 0x94, 0xf7, 0x45, 0xde, 0x69, 0xb7, 0x5d, 0x41, 0x5a, - 0x8e, 0x16, 0xce, 0xc7, 0x75, 0xfe, 0xf9, 0x2d, 0xce, 0xd5, 0x05, 0x60, - 0xdd, 0xb7, 0xcf, 0xab, 0x3b, 0xa2, 0xfe, 0xc8, 0x6f, 0x1f, 0x04, 0xec, - 0x56, 0xaf, 0xbf, 0xbc, 0x8a, 0x89, 0xbc, 0x32, 0xc8, 0x77, 0x51, 0x65, - 0x7d, 0x70, 0x9f, 0xbd, 0x1b, 0xe6, 0x92, 0x09, 0xe4, 0xc4, 0x25, 0xef, - 0xba, 0x87, 0x9f, 0x3d, 0xc0, 0xf0, 0xb2, 0x5d, 0xa6, 0x6f, 0x9d, 0x7d, - 0xfd, 0xe7, 0xe0, 0xf3, 0x47, 0x16, 0x3b, 0xed, 0x9c, 0xc4, 0x12, 0xfa, - 0xd2, 0x4d, 0x38, 0xfb, 0x2a, 0x1a, 0xc4, 0xc4, 0x21, 0x58, 0x48, 0x72, - 0x44, 0x0f, 0xbc, 0x2a, 0x4a, 0x15, 0x0a, 0x40, 0x07, 0xdf, 0x9b, 0xd5, - 0x64, 0xd6, 0xe5, 0xe1, 0x40, 0x63, 0x72, 0x21, 0xb2, 0x84, 0x50, 0x2e, - 0x13, 0x15, 0x0c, 0x2c, 0xe9, 0xeb, 0x14, 0xec, 0x4f, 0x31, 0x01, 0xd3, - 0xef, 0x57, 0xd1, 0x25, 0x45, 0xdc, 0x53, 0x81, 0xab, 0x4c, 0x25, 0x24, - 0x7f, 0x20, 0xb3, 0x19, 0x22, 0x27, 0x5d, 0x7e, 0x04, 0x7c, 0x05, 0xe0, - 0x0b, 0x0d, 0x7d, 0xfc, 0x56, 0x0c, 0xef, 0x35, 0xe7, 0x3d, 0x63, 0xaf, - 0xfb, 0xbf, 0x07, 0x98, 0xa0, 0x80, 0xc2, 0x2a, 0xa7, 0x20, 0xa2, 0xe3, - 0x70, 0x03, 0x87, 0xd5, 0x79, 0x16, 0x09, 0xad, 0x87, 0xff, 0xf1, 0x50, - 0x80, 0x2f, 0x9f, 0xfc, 0x21, 0x1a, 0xc9, 0xfa, 0xab, 0x9b, 0xbe, 0x00, - 0x8d, 0xb6, 0xb0, 0xe5, 0x66, 0x45, 0x23, 0x20, 0x00, 0x02, 0x50, 0x03, - 0xcf, 0x8e, 0x39, 0xfd, 0x63, 0xdf, 0xf7, 0xef, 0xda, 0xe6, 0x39, 0x0f, - 0x55, 0xa0, 0x72, 0xb8, 0xbd, 0x29, 0xf1, 0xf6, 0x8e, 0x82, 0xd6, 0x72, - 0x52, 0xa8, 0xeb, 0x06, 0x0a, 0xf8, 0x1c, 0x66, 0x68, 0xd7, 0x9a, 0x53, - 0x21, 0x11, 0x15, 0x08, 0x18, 0xaf, 0xac, 0xcc, 0xa7, 0xa0, 0x45, 0xdb, - 0x51, 0xf3, 0xed, 0xd8, 0xe0, 0x47, 0xeb, 0x9f, 0x33, 0x2d, 0x9b, 0x61, - 0x14, 0xd3, 0xc7, 0x1b, 0x9f, 0x83, 0xe3, 0x4d, 0x5d, 0xd3, 0x4f, 0x04, - 0x5c, 0x65, 0x10, 0x5c, 0x91, 0x00, 0xe9, 0x3b, 0xa7, 0x0e, 0x46, 0x02, - 0xad, 0x92, 0x62, 0x49, 0x08, 0x55, 0x57, 0x13, 0xc7, 0xc0, 0x90, 0xda, - 0xd9, 0xaf, 0x23, 0x69, 0x55, 0xb8, 0xbf, 0x4c, 0x0d, 0xe4, 0xdb, 0x7b, - 0x3a, 0xc8, 0x23, 0x11, 0xb7, 0x73, 0xb6, 0x95, 0xbe, 0xdb, 0xc4, 0xb9, - 0x5d, 0xeb, 0x8a, 0xf1, 0x3a, 0xc7, 0x1d, 0x47, 0x5d, 0x66, 0xb0, 0x9a, - 0xe0, 0xcb, 0x71, 0xc4, 0xec, 0xe3, 0xd9, 0x9c, 0xe7, 0x39, 0x8a, 0xa0, - 0x0b, 0xa5, 0x73, 0x13, 0x7c, 0x2b, 0xe9, 0x46, 0xd1, 0xaa, 0x7b, 0x86, - 0x39, 0x58, 0x2e, 0x2e, 0x8c, 0xe8, 0x04, 0x47, 0xe1, 0x42, 0x15, 0xcb, - 0xe0, 0x79, 0x5a, 0x7c, 0xfb, 0xee, 0x2e, 0xec, 0x29, 0xee, 0xac, 0x6e, - 0xb7, 0xf8, 0xad, 0x73, 0xf3, 0x48, 0x15, 0xf6, 0xdd, 0xcf, 0xf5, 0x46, - 0x7a, 0xae, 0xee, 0xea, 0xc2, 0x59, 0xf5, 0x4a, 0xd8, 0x70, 0xc0, 0x6e, - 0x7e, 0x33, 0x77, 0x70, 0x66, 0xf4, 0x2d, 0x93, 0x97, 0x1e, 0xc3, 0x99, - 0x3a, 0xc6, 0x9a, 0x34, 0xcb, 0xd7, 0x5e, 0x54, 0xba, 0x22, 0x64, 0x13, - 0x65, 0xc6, 0xf6, 0x19, 0x22, 0x97, 0xae, 0x42, 0xc8, 0xc8, 0xb0, 0xb0, - 0xe5, 0x9c, 0x64, 0x0f, 0xa0, 0xd0, 0x37, 0xad, 0xeb, 0x7a, 0xde, 0xb7, - 0xa3, 0x6d, 0x37, 0xa7, 0x9e, 0x6f, 0x55, 0x1e, 0x6f, 0x9b, 0xab, 0x55, - 0x00, 0x8c, 0x2e, 0xb5, 0x52, 0xcd, 0x81, 0x8e, 0x48, 0x9e, 0x82, 0x0a, - 0xc6, 0xe3, 0x1b, 0xe7, 0x58, 0xd0, 0x3c, 0xe1, 0x5a, 0xb8, 0x4b, 0xea, - 0x38, 0x24, 0xdf, 0xd6, 0x32, 0x30, 0x6a, 0xa1, 0xf6, 0xac, 0x8b, 0xc2, - 0x94, 0xad, 0x2c, 0xd5, 0x19, 0xd1, 0x18, 0xe9, 0xfc, 0xf7, 0x45, 0x15, - 0x94, 0x30, 0x8b, 0x68, 0x5b, 0x28, 0x46, 0x7a, 0xb2, 0x18, 0xd6, 0x71, - 0xf5, 0xdf, 0xe8, 0xe9, 0x93, 0x88, 0x22, 0x54, 0xbc, 0xb0, 0x5c, 0x81, - 0x73, 0xc5, 0x00, 0x33, 0xa9, 0xc5, 0x4b, 0xa2, 0x6b, 0x10, 0xa9, 0xce, - 0x8a, 0x6b, 0x48, 0x13, 0xe0, 0xff, 0xf1, 0x50, 0x80, 0x2c, 0x5f, 0xfc, - 0x21, 0x1a, 0xcd, 0x98, 0x80, 0x1c, 0x3f, 0xff, 0x8d, 0xb2, 0x32, 0x2c, - 0x2c, 0x37, 0x59, 0x91, 0x06, 0xc9, 0x00, 0x00, 0x00, 0x1a, 0xd9, 0xfc, - 0xee, 0xba, 0xd7, 0x3f, 0x46, 0xeb, 0x61, 0xb9, 0x26, 0x09, 0xcb, 0x33, - 0x8d, 0xf9, 0x69, 0x16, 0xc8, 0xb6, 0x39, 0x52, 0xbb, 0x03, 0x6f, 0xbd, - 0xaf, 0xcb, 0x85, 0xb6, 0x02, 0xb2, 0x0f, 0x87, 0x06, 0xf6, 0x08, 0xb2, - 0x34, 0x6b, 0x50, 0x67, 0x9a, 0xcb, 0x14, 0x5e, 0x82, 0x22, 0xad, 0x92, - 0xdd, 0xdc, 0x27, 0x1c, 0x70, 0xd4, 0x36, 0x67, 0xc5, 0xd3, 0xf1, 0x34, - 0x72, 0x67, 0x82, 0xb4, 0x74, 0x0d, 0xa1, 0x7b, 0x66, 0xd4, 0x33, 0x4e, - 0x97, 0x1b, 0x9f, 0x2a, 0xaa, 0xab, 0x50, 0x41, 0x56, 0x99, 0x91, 0x43, - 0xb3, 0xd4, 0x91, 0x01, 0x19, 0x31, 0xe9, 0x08, 0xcb, 0xd0, 0x8c, 0x49, - 0xd5, 0x6b, 0x87, 0x44, 0x88, 0x37, 0xc4, 0x39, 0xc1, 0x73, 0x17, 0x4d, - 0x80, 0x58, 0x10, 0xc4, 0x19, 0x70, 0xa2, 0x6c, 0x02, 0xa1, 0x2d, 0xfc, - 0xa1, 0xb7, 0x22, 0x00, 0x3b, 0x8e, 0x80, 0x22, 0xa0, 0xde, 0xf0, 0x20, - 0x09, 0x02, 0x66, 0xb9, 0x86, 0x3b, 0x32, 0x05, 0x59, 0x8c, 0xfb, 0x2d, - 0x22, 0x60, 0x50, 0x43, 0x31, 0x40, 0xf8, 0x04, 0x75, 0xa4, 0xfb, 0x96, - 0x7a, 0x39, 0xb6, 0x19, 0xe9, 0xba, 0x77, 0xbc, 0x60, 0x7b, 0x73, 0x97, - 0xb6, 0xde, 0xcd, 0xf9, 0x54, 0x29, 0xe9, 0x8d, 0x5d, 0x75, 0x9f, 0x7f, - 0x76, 0x9d, 0x8f, 0x5c, 0x96, 0x4d, 0x5b, 0xd0, 0xa5, 0x1b, 0xec, 0x9a, - 0x67, 0xdd, 0x54, 0x9a, 0xb1, 0x3a, 0xe6, 0xa1, 0xc2, 0x69, 0x48, 0x96, - 0xcc, 0xad, 0x7e, 0xa4, 0x70, 0x92, 0x82, 0xa0, 0x0e, 0x44, 0x56, 0xbe, - 0x7b, 0x23, 0x6c, 0xac, 0x7b, 0x0b, 0x0c, 0x47, 0x61, 0x93, 0x90, 0x79, - 0xee, 0x78, 0x72, 0xe1, 0xc9, 0xc7, 0x20, 0x0e, 0x0e, 0x5f, 0x8d, 0xce, - 0xa9, 0xcf, 0x12, 0xaf, 0x00, 0x19, 0x5f, 0x71, 0xec, 0xb4, 0x03, 0xb0, - 0x93, 0x5b, 0x72, 0x38, 0x97, 0xc1, 0x06, 0x92, 0x0e, 0x30, 0xbb, 0x16, - 0xc7, 0x15, 0x76, 0x2e, 0x4b, 0xce, 0xf5, 0x3e, 0xba, 0x86, 0x05, 0x95, - 0xf4, 0x59, 0x34, 0xd6, 0x71, 0xe5, 0x5b, 0xdd, 0x48, 0xd7, 0x91, 0xe7, - 0x02, 0x16, 0x30, 0xf6, 0x55, 0x64, 0x95, 0x5b, 0x2b, 0xad, 0x68, 0xcb, - 0x08, 0x45, 0xd7, 0x1b, 0xf6, 0x0f, 0x52, 0x95, 0x92, 0xa5, 0xa5, 0x50, - 0x91, 0x00, 0x37, 0xc8, 0x03, 0x24, 0x29, 0xb7, 0x91, 0x97, 0xc0, 0xff, - 0xf1, 0x50, 0x80, 0x2f, 0xbf, 0xfc, 0x21, 0x1a, 0xcf, 0xb0, 0x64, 0x5d, - 0x00, 0x00, 0x8c, 0xb6, 0xb1, 0xa4, 0x28, 0x25, 0x38, 0x89, 0x98, 0x00, - 0x01, 0x28, 0x01, 0x2a, 0xef, 0xf7, 0xf5, 0xc1, 0x3e, 0x2a, 0xb2, 0xb0, - 0x37, 0xa5, 0x24, 0x64, 0xc1, 0x59, 0xe7, 0x53, 0xc8, 0x62, 0xde, 0xb3, - 0x14, 0x01, 0x4c, 0xea, 0x80, 0x04, 0x6e, 0x21, 0x87, 0x99, 0xa5, 0x34, - 0xc1, 0xc4, 0x30, 0xa8, 0x1a, 0xe3, 0x25, 0x00, 0x90, 0x0d, 0x9a, 0x99, - 0x14, 0xbc, 0xb1, 0xfd, 0xe6, 0xe6, 0xcf, 0xdb, 0x34, 0x21, 0x07, 0x9b, - 0x80, 0x35, 0x4f, 0x3d, 0xe4, 0x10, 0x5f, 0x61, 0xf8, 0xc9, 0x57, 0xb3, - 0xd7, 0x9f, 0x58, 0x9f, 0x59, 0x26, 0x62, 0x46, 0x5b, 0xfe, 0xde, 0x7f, - 0xa2, 0x11, 0x48, 0x11, 0xae, 0x56, 0x6b, 0x37, 0x90, 0x55, 0x63, 0x7f, - 0x3f, 0xca, 0x02, 0x4b, 0xa5, 0x86, 0x55, 0x21, 0x09, 0xa9, 0xdc, 0x17, - 0x88, 0x2b, 0x7a, 0x6b, 0xde, 0xe2, 0x3c, 0x3c, 0x93, 0x1a, 0x7d, 0xec, - 0x52, 0x61, 0xbe, 0x9c, 0xc0, 0xeb, 0x16, 0x65, 0xa0, 0x01, 0xc1, 0xad, - 0x84, 0x07, 0x00, 0x56, 0x8d, 0x54, 0xb8, 0x30, 0x03, 0xb0, 0x90, 0x2c, - 0x05, 0xad, 0x9d, 0x62, 0x09, 0x35, 0x32, 0xb2, 0x20, 0xd7, 0x38, 0x4a, - 0x64, 0x89, 0x95, 0xc8, 0x27, 0x30, 0x2a, 0x5f, 0x65, 0xb3, 0x08, 0xdc, - 0xd2, 0x6e, 0x03, 0x36, 0x49, 0xbc, 0xef, 0xc5, 0x2e, 0xe9, 0xe1, 0x48, - 0xef, 0xcb, 0x29, 0x58, 0xea, 0xce, 0x9b, 0x20, 0xaa, 0xdb, 0xc2, 0xdf, - 0x35, 0x3c, 0xfc, 0x2a, 0xf0, 0x4c, 0x2d, 0xab, 0xe1, 0x7f, 0x84, 0x8b, - 0x8e, 0x54, 0x57, 0x14, 0xc9, 0x34, 0x15, 0xf2, 0x8d, 0xbb, 0xf1, 0xd8, - 0xcf, 0x3f, 0x37, 0xae, 0x04, 0xca, 0x9a, 0xfb, 0xaa, 0x91, 0x00, 0x64, - 0x09, 0xf6, 0x48, 0x0e, 0xc6, 0x5c, 0xa2, 0xb6, 0xbc, 0xaa, 0xbe, 0x62, - 0xa2, 0x32, 0xca, 0xc7, 0xb0, 0xb0, 0x90, 0x96, 0x19, 0x3a, 0x07, 0xde, - 0x1c, 0xf0, 0xe4, 0x38, 0x72, 0xeb, 0xb3, 0x8e, 0x78, 0x39, 0xe3, 0xef, - 0xde, 0xb8, 0x55, 0x5d, 0x57, 0x15, 0x55, 0x56, 0x0c, 0x73, 0x35, 0xf6, - 0x6a, 0xf3, 0x87, 0xe2, 0x34, 0xa2, 0x30, 0x73, 0xa7, 0x42, 0xd7, 0x46, - 0x59, 0xa9, 0x89, 0x58, 0xdb, 0xdd, 0x4b, 0xf3, 0xc4, 0xac, 0x32, 0x5c, - 0x4f, 0x09, 0x4f, 0x9f, 0x06, 0xab, 0x44, 0x21, 0xd4, 0x20, 0xf0, 0xe5, - 0x8b, 0x5b, 0x3f, 0x6b, 0x8c, 0xe4, 0xd5, 0xc0, 0x58, 0x53, 0x77, 0x05, - 0x0c, 0x48, 0x5e, 0x1b, 0x17, 0x8d, 0x4c, 0xe8, 0x98, 0x7f, 0x6e, 0xda, - 0x51, 0x32, 0x2a, 0xa0, 0x8c, 0x6e, 0x10, 0x05, 0xea, 0xe6, 0x22, 0x83, - 0x7a, 0x02, 0x4b, 0xc0, 0x77, 0x13, 0x5d, 0xe0, 0xff, 0xf1, 0x50, 0x80, - 0x2f, 0x9f, 0xfc, 0x21, 0x1a, 0xcd, 0xfe, 0xe2, 0xbf, 0x3f, 0xff, 0x8b, - 0xb7, 0xb2, 0x4c, 0xec, 0x14, 0x23, 0x1c, 0x00, 0xeb, 0xb0, 0xeb, 0xb0, - 0x00, 0x7e, 0x32, 0xf4, 0xe2, 0x6a, 0xab, 0x78, 0x1e, 0xad, 0x23, 0x90, - 0x12, 0x3b, 0x07, 0x1d, 0xe5, 0x53, 0xf1, 0x5f, 0x2c, 0xe7, 0xb4, 0x82, - 0x33, 0x2a, 0x72, 0xe4, 0x42, 0xa2, 0xae, 0x32, 0xa1, 0xf1, 0xc4, 0x30, - 0x64, 0x31, 0x17, 0x94, 0x5d, 0xa6, 0x4a, 0xfc, 0xd9, 0x92, 0x33, 0x64, - 0xd2, 0x99, 0xbe, 0x57, 0x1c, 0x34, 0xc7, 0x9b, 0x60, 0x4e, 0xb7, 0x28, - 0x99, 0x07, 0x90, 0x55, 0xc0, 0xbd, 0x9f, 0xfc, 0x21, 0x48, 0x0d, 0xd5, - 0xdc, 0x2f, 0x33, 0x40, 0x31, 0x74, 0x54, 0x32, 0x11, 0x20, 0xf6, 0x71, - 0xf8, 0x31, 0x10, 0x34, 0x76, 0x9e, 0x2e, 0x1e, 0xba, 0xa3, 0x27, 0x85, - 0xea, 0x89, 0x13, 0xaa, 0x10, 0xe0, 0x1b, 0x8f, 0xa8, 0xae, 0xa4, 0xd7, - 0xdc, 0x63, 0x9b, 0xae, 0x5b, 0xf5, 0xd1, 0xbc, 0x8e, 0x40, 0xd2, 0x3d, - 0xca, 0x26, 0x86, 0x07, 0x10, 0x5c, 0xab, 0x1f, 0x79, 0x6b, 0xc9, 0x96, - 0xd9, 0xbf, 0x15, 0x08, 0x55, 0x18, 0xf5, 0x23, 0xdf, 0xcf, 0x90, 0xeb, - 0x36, 0xf4, 0x5f, 0x37, 0xd3, 0xf6, 0xff, 0x0d, 0x05, 0xfb, 0x34, 0x87, - 0xce, 0x64, 0x73, 0x7e, 0xd9, 0xd4, 0x23, 0x2c, 0xa4, 0x1a, 0x0b, 0x37, - 0xb3, 0x28, 0xed, 0xa4, 0xf9, 0x51, 0x14, 0x65, 0x3c, 0xb2, 0x4b, 0x66, - 0xe6, 0xa3, 0xb5, 0xb7, 0xd9, 0xd7, 0xd0, 0x56, 0x59, 0xa7, 0x97, 0x2e, - 0xb9, 0xaa, 0x59, 0xe4, 0xa2, 0x6a, 0xa9, 0xfd, 0x61, 0x35, 0x3d, 0xf9, - 0xcb, 0xb6, 0x8c, 0xe5, 0x96, 0x52, 0xe3, 0xa4, 0xaf, 0x4c, 0x38, 0xe4, - 0x79, 0x89, 0xb6, 0xfa, 0xb0, 0x94, 0x89, 0x5b, 0x9c, 0x5a, 0xcf, 0x17, - 0xb5, 0x86, 0xc1, 0x99, 0xca, 0xa6, 0xe3, 0x17, 0x65, 0x63, 0xd8, 0x58, - 0xb2, 0x63, 0x27, 0x3d, 0xbd, 0x00, 0xde, 0x8d, 0x9a, 0xde, 0xb7, 0xa0, - 0x79, 0xda, 0x5c, 0x5e, 0x6b, 0x23, 0x18, 0x01, 0x8f, 0x20, 0x3f, 0x71, - 0xab, 0x47, 0x46, 0x5c, 0x10, 0x00, 0x46, 0x24, 0xf5, 0x9d, 0x29, 0x3c, - 0x4b, 0x45, 0x96, 0xbd, 0x14, 0x2b, 0x76, 0x51, 0x0d, 0x47, 0x89, 0xfb, - 0x3c, 0xb5, 0x22, 0x45, 0x76, 0xe0, 0xd7, 0x71, 0x95, 0xc1, 0xdf, 0xe7, - 0xf8, 0x40, 0x62, 0xb6, 0xda, 0xc2, 0x3f, 0xab, 0xfa, 0x36, 0x48, 0x46, - 0xdd, 0x1c, 0xa2, 0x6f, 0x49, 0xbe, 0xab, 0x3c, 0x23, 0x47, 0x5a, 0xd3, - 0x9c, 0xd5, 0x76, 0x9f, 0xab, 0x78, 0x1c, 0x32, 0xab, 0x4a, 0xb3, 0xdc, - 0xdf, 0x99, 0xa2, 0x2b, 0xf2, 0x9c, 0xd6, 0xdd, 0xda, 0x34, 0xfa, 0xa6, - 0xf2, 0xbd, 0xb6, 0x7e, 0xff, 0xf1, 0x50, 0x80, 0x2e, 0xbf, 0xfc, 0x21, - 0x1a, 0xcf, 0xca, 0xcb, 0x6c, 0x00, 0x00, 0x8d, 0xb6, 0xaa, 0xcc, 0xe8, - 0x26, 0x50, 0x3c, 0xcf, 0x40, 0x12, 0x80, 0x5e, 0x5e, 0x1f, 0xbe, 0xfe, - 0x37, 0x27, 0x93, 0xba, 0xe4, 0x09, 0xc6, 0x56, 0xe1, 0x28, 0x01, 0xc8, - 0x55, 0xa0, 0xf1, 0xd5, 0x81, 0x4a, 0x27, 0x29, 0x12, 0x11, 0xef, 0xc5, - 0xb3, 0x30, 0xe1, 0xc2, 0x8d, 0x2c, 0xb1, 0x44, 0x72, 0x2f, 0x7b, 0xc4, - 0x48, 0xc0, 0xa9, 0x44, 0x81, 0x7d, 0xb7, 0x1d, 0xb8, 0x23, 0xa7, 0xb6, - 0xa9, 0x8e, 0x76, 0x55, 0xb5, 0x78, 0xa7, 0xab, 0xa6, 0x6c, 0x98, 0x9d, - 0x94, 0x12, 0x9d, 0x46, 0x2f, 0x14, 0x19, 0xff, 0xbc, 0xc8, 0x45, 0x28, - 0x4f, 0x43, 0x15, 0x06, 0x0a, 0xa7, 0x44, 0xef, 0x52, 0x95, 0x29, 0xaa, - 0x98, 0xc8, 0xcc, 0x12, 0x7e, 0x4c, 0x45, 0x2c, 0xa5, 0x3a, 0x76, 0x85, - 0x50, 0x2a, 0xe9, 0xf0, 0xcc, 0x19, 0xab, 0xe8, 0xe4, 0x77, 0x69, 0xc2, - 0x0d, 0xe7, 0x19, 0x69, 0x16, 0x22, 0x31, 0x59, 0x24, 0x00, 0x23, 0x35, - 0x45, 0x35, 0x1a, 0xa7, 0x72, 0x69, 0xee, 0x00, 0xa0, 0x85, 0x40, 0xa7, - 0x0b, 0x69, 0x71, 0x20, 0x34, 0xb9, 0x02, 0x45, 0xaa, 0xb9, 0x1e, 0x9f, - 0x5b, 0x04, 0xbd, 0x26, 0x7d, 0xeb, 0xc4, 0xf0, 0x94, 0xfe, 0xeb, 0x77, - 0x40, 0xa4, 0x43, 0x29, 0xad, 0x89, 0xd9, 0xb7, 0x1f, 0x69, 0xa3, 0xf0, - 0x91, 0x2a, 0xae, 0x39, 0xcf, 0xc0, 0x80, 0x86, 0xf2, 0x70, 0x5a, 0x4a, - 0xa9, 0xe4, 0x19, 0xd9, 0x4e, 0x0a, 0x8b, 0xe1, 0x65, 0x69, 0x91, 0x06, - 0x51, 0x88, 0x26, 0x62, 0x72, 0x56, 0x74, 0x14, 0x28, 0xdb, 0x13, 0x2a, - 0xc4, 0x85, 0x92, 0x99, 0x3d, 0x2c, 0x3f, 0x18, 0x36, 0xd6, 0xf5, 0xbd, - 0x36, 0xd6, 0x5b, 0x7a, 0x6f, 0x4c, 0xaf, 0xc3, 0x33, 0xa4, 0x95, 0x7f, - 0x79, 0x5b, 0x00, 0xf0, 0xbf, 0x25, 0x9e, 0x83, 0x2a, 0xbf, 0xdd, 0xea, - 0x33, 0x0d, 0xf6, 0x05, 0xd2, 0xb3, 0x52, 0x97, 0x95, 0x28, 0x42, 0xa1, - 0xdb, 0x84, 0x28, 0xb6, 0x56, 0x5b, 0xa0, 0x72, 0xd9, 0xfc, 0x47, 0x43, - 0xcf, 0xd3, 0x4b, 0x43, 0xa5, 0x93, 0x13, 0xa4, 0x70, 0xf1, 0xfe, 0x60, - 0x2e, 0xa7, 0x18, 0x84, 0x28, 0x5d, 0x68, 0x4a, 0xa6, 0xa5, 0x75, 0xed, - 0xde, 0x52, 0x04, 0x55, 0xbf, 0x42, 0x77, 0xc0, 0x8c, 0xbd, 0x23, 0x18, - 0x74, 0x7a, 0xdb, 0xb5, 0x8e, 0xfb, 0xda, 0xcf, 0x4e, 0xfc, 0x79, 0x89, - 0x36, 0x27, 0x18, 0x2d, 0x16, 0x45, 0x14, 0xd7, 0x9b, 0x49, 0x46, 0xeb, - 0xbb, 0x9a, 0xcd, 0x6c, 0xb4, 0xc8, 0x95, 0x46, 0xbf, 0x0b, 0xac, 0x34, - 0xb5, 0x6b, 0x93, 0x58, 0xf0, 0xff, 0xf1, 0x50, 0x80, 0x2e, 0x1f, 0xfc, - 0x21, 0x1a, 0xcb, 0x08, 0x06, 0x34, 0x20, 0x00, 0x8b, 0xb2, 0x31, 0xec, - 0x4c, 0x6b, 0x0c, 0x9c, 0x48, 0x82, 0x65, 0x03, 0xae, 0xc0, 0x17, 0x80, - 0x0e, 0x39, 0x95, 0xfb, 0xe7, 0x9c, 0xd6, 0x79, 0x33, 0x39, 0x08, 0xfe, - 0xef, 0xf0, 0x24, 0x14, 0x06, 0xe7, 0xff, 0xde, 0x41, 0x07, 0xdf, 0xa6, - 0x87, 0x0d, 0xbd, 0xe7, 0x6a, 0xd3, 0x7d, 0x96, 0x8b, 0xb2, 0x5d, 0x02, - 0x38, 0xa1, 0x83, 0x10, 0x96, 0x3a, 0x46, 0x24, 0xd2, 0x37, 0x0d, 0x37, - 0xbc, 0x3d, 0x0d, 0x50, 0xd6, 0xa9, 0x27, 0x12, 0x06, 0x5d, 0xe6, 0x00, - 0x6d, 0xe2, 0x7c, 0x98, 0x60, 0xc0, 0x65, 0x6b, 0x30, 0x22, 0x69, 0x58, - 0xa5, 0x62, 0x6c, 0xad, 0xbe, 0xa7, 0xf9, 0x8e, 0xca, 0x24, 0x0a, 0x4d, - 0xef, 0xdb, 0x18, 0x80, 0x9c, 0xb9, 0xda, 0x79, 0x88, 0x2a, 0x63, 0xcb, - 0x07, 0xab, 0x5d, 0x20, 0x4b, 0x90, 0x1b, 0x25, 0x55, 0x85, 0x22, 0x37, - 0x5b, 0x3f, 0xc1, 0x86, 0xef, 0x30, 0x57, 0x68, 0xda, 0x72, 0x1f, 0xf2, - 0xe2, 0x24, 0x57, 0x3a, 0x1a, 0x89, 0xc5, 0xaa, 0x16, 0xb6, 0xb0, 0xb4, - 0x77, 0x51, 0x55, 0x51, 0x7e, 0xf6, 0x33, 0x60, 0x5a, 0xe5, 0x86, 0x66, - 0xae, 0xd5, 0x68, 0xa7, 0x09, 0xe7, 0xca, 0x46, 0x1a, 0x7c, 0x0b, 0x06, - 0xd9, 0x12, 0xb2, 0x58, 0x01, 0x7a, 0x28, 0xc9, 0xde, 0x59, 0xe3, 0x4a, - 0x46, 0x89, 0xfd, 0x73, 0x86, 0xf9, 0x7c, 0x26, 0xd3, 0x4f, 0x0c, 0xde, - 0x8e, 0xdc, 0x50, 0x34, 0x5b, 0x6d, 0xbd, 0x2e, 0x48, 0xd1, 0xd5, 0x30, - 0x65, 0x22, 0xc9, 0xd7, 0xb2, 0xfd, 0x9e, 0xf8, 0x2b, 0x69, 0xba, 0xbb, - 0x3b, 0x64, 0x0e, 0xb6, 0x1b, 0x2d, 0xec, 0xdb, 0x4c, 0x25, 0x42, 0xf4, - 0x53, 0xde, 0xb4, 0xbf, 0x83, 0x0c, 0xc4, 0xe5, 0x41, 0xde, 0x66, 0x06, - 0x08, 0xcd, 0x5c, 0xd2, 0x16, 0x46, 0x45, 0x85, 0x86, 0xe4, 0x41, 0x99, - 0x10, 0x3e, 0xf7, 0x4e, 0xc7, 0x5d, 0xde, 0x03, 0xdb, 0x3b, 0x3a, 0x77, - 0xd7, 0x6f, 0xf4, 0x9c, 0xf1, 0x51, 0x2f, 0x24, 0x64, 0xc0, 0x33, 0xaf, - 0x54, 0xf6, 0xd3, 0x66, 0x23, 0x56, 0x43, 0xba, 0xa5, 0x49, 0x51, 0xbe, - 0xc0, 0x55, 0x33, 0x13, 0xde, 0xe2, 0x7d, 0x75, 0x15, 0x48, 0x91, 0x7a, - 0xe8, 0x33, 0x7a, 0xe4, 0x50, 0xea, 0x17, 0x26, 0x53, 0x71, 0x86, 0xa9, - 0x76, 0xa9, 0x2f, 0x1e, 0xab, 0xd4, 0x85, 0x10, 0xa5, 0x8e, 0x54, 0x5a, - 0x6d, 0x99, 0xa9, 0x55, 0x9f, 0xdf, 0x9e, 0xaa, 0x6d, 0x91, 0xd6, 0xd0, - 0xcc, 0x1a, 0x9e, 0x30, 0x6b, 0x74, 0xdf, 0xda, 0xe2, 0x52, 0xb3, 0xa4, - 0xf8, 0xff, 0xf1, 0x50, 0x80, 0x34, 0x7f, 0xfc, 0x21, 0x1a, 0xcd, 0x0a, - 0x24, 0x02, 0x80, 0x00, 0x8a, 0xb6, 0xb1, 0x20, 0x56, 0x14, 0x1d, 0x84, - 0xca, 0x42, 0x66, 0x02, 0x50, 0x02, 0x50, 0x0d, 0x6f, 0xe3, 0xdf, 0xae, - 0xff, 0x9a, 0xd5, 0x2f, 0x82, 0xb7, 0xc8, 0xc6, 0xcf, 0xe8, 0xce, 0xab, - 0x7f, 0x73, 0xad, 0x6e, 0xdb, 0x26, 0x0a, 0xb9, 0x5c, 0xa1, 0x04, 0x4d, - 0x1f, 0x1c, 0x66, 0xaa, 0xe6, 0x3c, 0xa8, 0x7c, 0xa3, 0x51, 0x83, 0x9d, - 0x09, 0xec, 0xfa, 0x46, 0x9b, 0xea, 0x71, 0x6b, 0xb4, 0x52, 0xeb, 0x9c, - 0xc3, 0x40, 0xc1, 0x81, 0xd9, 0xa4, 0x28, 0xcb, 0x3a, 0x14, 0x21, 0x32, - 0x8f, 0xa9, 0xe5, 0x9a, 0xcc, 0x03, 0x8c, 0x50, 0x77, 0x1c, 0x70, 0x1b, - 0xf0, 0xb8, 0x3f, 0x6c, 0xb3, 0xb8, 0xd4, 0x03, 0x5b, 0xf2, 0x78, 0x73, - 0x89, 0x46, 0x01, 0x65, 0xeb, 0xc7, 0x5f, 0x14, 0xa3, 0x48, 0x59, 0x0f, - 0x5f, 0xf4, 0xa6, 0x9f, 0x36, 0x1f, 0x4b, 0x72, 0xbf, 0xaf, 0x85, 0x57, - 0xb7, 0x2c, 0x38, 0x53, 0x7c, 0x67, 0xe3, 0x8d, 0x40, 0x59, 0x51, 0x82, - 0x44, 0x51, 0x10, 0x5b, 0x6d, 0x2b, 0x5a, 0x57, 0xba, 0x05, 0x12, 0xc3, - 0x17, 0x06, 0x0c, 0x2b, 0x65, 0x85, 0x18, 0x2b, 0x9d, 0xeb, 0x96, 0x88, - 0xbe, 0xb3, 0x9e, 0xf0, 0x04, 0x9a, 0x5b, 0x18, 0x60, 0x52, 0x4b, 0xa7, - 0xba, 0xcb, 0x4e, 0xc9, 0x64, 0xb2, 0x87, 0xbd, 0x1d, 0x1a, 0x72, 0xb4, - 0x26, 0x4b, 0xa2, 0x6f, 0x26, 0x2b, 0x31, 0x8c, 0xf9, 0xd0, 0xee, 0x76, - 0x79, 0xf1, 0x9a, 0x99, 0xe1, 0xae, 0x96, 0xd9, 0x39, 0x5f, 0xa6, 0xe7, - 0x8e, 0xca, 0xf6, 0x46, 0x16, 0x37, 0x66, 0x28, 0xb6, 0x06, 0xc1, 0x99, - 0x6a, 0xde, 0x73, 0xcb, 0x3e, 0x1e, 0x07, 0x78, 0xd6, 0x1d, 0x96, 0xe6, - 0xde, 0xb8, 0x7c, 0x79, 0xe8, 0xaf, 0x19, 0x1a, 0xe9, 0xe6, 0x26, 0x8e, - 0x0e, 0xf8, 0x5e, 0x19, 0x97, 0x74, 0x1a, 0x14, 0xe8, 0x74, 0xb3, 0x45, - 0x0d, 0x8d, 0xe7, 0x71, 0x20, 0x45, 0x59, 0x19, 0x16, 0x17, 0x38, 0x99, - 0x45, 0xe9, 0x61, 0xf8, 0xde, 0x3c, 0xf8, 0x7f, 0x3f, 0xb7, 0x5e, 0x3c, - 0xf8, 0x3c, 0xf8, 0x7b, 0x7a, 0x26, 0xb9, 0x5f, 0xb7, 0xcf, 0xf3, 0x4d, - 0x65, 0xd4, 0x8e, 0x34, 0x56, 0x03, 0x21, 0x32, 0x69, 0x74, 0x21, 0xc8, - 0x83, 0xa6, 0x95, 0xfb, 0x4f, 0xe4, 0xbc, 0x22, 0xe0, 0x3d, 0x50, 0x2d, - 0x25, 0x32, 0x08, 0x72, 0x16, 0x5d, 0x0d, 0x95, 0x26, 0x77, 0x57, 0x83, - 0x59, 0xe5, 0x10, 0x98, 0x8e, 0x34, 0x5a, 0x9c, 0x80, 0x88, 0xbc, 0x17, - 0x8f, 0xda, 0xfd, 0x39, 0x09, 0xe8, 0x8f, 0x7b, 0x7b, 0xfa, 0xc1, 0xdd, - 0xfe, 0x86, 0xe6, 0x54, 0xb9, 0x77, 0xd9, 0x5c, 0x66, 0xfc, 0x40, 0x0d, - 0xea, 0xc9, 0xac, 0xdb, 0x74, 0x37, 0x3b, 0x6e, 0x4e, 0x35, 0x4f, 0x4c, - 0xee, 0xe6, 0xa8, 0x66, 0xae, 0xbd, 0xd2, 0x4d, 0x25, 0x36, 0xe3, 0xd6, - 0x92, 0x09, 0xe4, 0x40, 0xeb, 0x3a, 0x5f, 0x9a, 0x90, 0x8a, 0xe7, 0xc0, - 0xff, 0xf1, 0x50, 0x80, 0x2c, 0xff, 0xfc, 0x21, 0x1a, 0xc9, 0x91, 0x8c, - 0x1f, 0x00, 0x00, 0x8c, 0xb2, 0x32, 0x2c, 0x28, 0x49, 0x38, 0x9d, 0x06, - 0xa1, 0x62, 0xa0, 0xc1, 0xc7, 0x20, 0x0b, 0xc0, 0x38, 0xcc, 0xf3, 0xe3, - 0xae, 0x7f, 0x5c, 0xe9, 0x2b, 0x8b, 0x78, 0xca, 0x13, 0x9a, 0xe4, 0xdf, - 0x95, 0xff, 0xce, 0x91, 0xf4, 0x1f, 0x23, 0xb8, 0x34, 0x48, 0x50, 0x95, - 0xeb, 0x80, 0x93, 0x36, 0x70, 0x67, 0x02, 0xf2, 0x68, 0x65, 0xc8, 0x84, - 0x85, 0x5f, 0xe2, 0x3f, 0xa8, 0xfd, 0x8b, 0xf6, 0xb5, 0x11, 0x99, 0x2d, - 0x43, 0xbb, 0x42, 0xea, 0xe6, 0x54, 0xd5, 0xfe, 0x37, 0xfa, 0xed, 0x6c, - 0x25, 0x94, 0x44, 0x4a, 0xc0, 0xc6, 0x40, 0x95, 0x3c, 0x68, 0x2d, 0x97, - 0xbc, 0x39, 0x33, 0x13, 0xa6, 0xdf, 0xb8, 0x7b, 0x6a, 0xfe, 0x35, 0xa7, - 0xf3, 0x5d, 0xfc, 0xc5, 0xce, 0x59, 0xfc, 0xdc, 0x20, 0x3d, 0xa0, 0xb1, - 0x86, 0x62, 0xce, 0x8a, 0x6f, 0x25, 0x55, 0x02, 0x65, 0x84, 0xd4, 0x2c, - 0x14, 0x10, 0x64, 0x36, 0xcd, 0x71, 0xb9, 0x9a, 0x46, 0xb5, 0xe7, 0x81, - 0x68, 0xcb, 0x7d, 0x55, 0x85, 0x26, 0xe7, 0x61, 0x9d, 0x4a, 0x30, 0x6a, - 0xe6, 0x2f, 0x78, 0x81, 0x5b, 0x65, 0xc0, 0xa6, 0x84, 0x14, 0xb5, 0x53, - 0xb4, 0xd7, 0xa4, 0x8f, 0x24, 0xe7, 0x5b, 0x66, 0x4a, 0xf2, 0xb8, 0x01, - 0x53, 0x95, 0x36, 0x59, 0x36, 0x14, 0xad, 0xd5, 0x4d, 0xc6, 0x6e, 0x12, - 0x0f, 0x7c, 0x2e, 0x43, 0x9d, 0x14, 0x3f, 0xd9, 0xc0, 0xf2, 0x89, 0xd2, - 0xd2, 0xb3, 0xa2, 0x65, 0x5e, 0xcc, 0xf5, 0xd7, 0x18, 0xd5, 0x15, 0xa2, - 0x90, 0xe3, 0x38, 0x00, 0x22, 0x90, 0xb2, 0xb1, 0xec, 0x28, 0x67, 0x42, - 0x07, 0xd2, 0xc2, 0xf2, 0x1c, 0x39, 0x71, 0xc9, 0xc3, 0x97, 0xc7, 0x37, - 0xe3, 0xe3, 0xdc, 0xe1, 0xc6, 0x67, 0xdf, 0x26, 0xa9, 0x24, 0xdf, 0x1a, - 0xba, 0xa0, 0x67, 0xfb, 0x36, 0xe9, 0x79, 0x2c, 0xb7, 0x71, 0xc8, 0x9c, - 0x84, 0x75, 0x18, 0x0b, 0x4a, 0x5f, 0x6a, 0xcc, 0x2c, 0x29, 0x70, 0x40, - 0x98, 0xe3, 0x95, 0xe3, 0xda, 0x87, 0xf5, 0x2b, 0x8a, 0x93, 0x0a, 0x98, - 0x5c, 0x5f, 0x74, 0xb1, 0x28, 0x98, 0x8f, 0x7a, 0xff, 0xa5, 0x21, 0x55, - 0x88, 0xc4, 0x2a, 0x31, 0xca, 0x00, 0x9d, 0x1f, 0xf6, 0xe9, 0x08, 0x04, - 0x88, 0xca, 0x99, 0xc8, 0x02, 0xf8, 0x00, 0x0f, 0x4b, 0xb9, 0x82, 0x89, - 0x33, 0x2b, 0x80, 0x91, 0xaa, 0x72, 0xab, 0x26, 0x9d, 0x14, 0x2d, 0xa0, - 0xed, 0xad, 0xe5, 0x8b, 0xac, 0xa6, 0x78, 0x1a, 0xb4, 0x41, 0xf0, 0xff, - 0xf1, 0x50, 0x80, 0x2d, 0xff, 0xfc, 0x21, 0x1a, 0xce, 0x98, 0x6b, 0xff, - 0x00, 0x00, 0x8d, 0xb2, 0x32, 0x2c, 0x28, 0x59, 0x31, 0x9d, 0x0a, 0xc4, - 0x41, 0x10, 0x40, 0x00, 0x3c, 0xf8, 0x0b, 0xce, 0xb3, 0x7f, 0x1e, 0xff, - 0xaf, 0xbf, 0xe2, 0x5d, 0x5c, 0xd2, 0xb9, 0xae, 0x43, 0xdf, 0xaa, 0xc6, - 0x96, 0x78, 0xa3, 0x69, 0xd8, 0x4c, 0x4f, 0xbe, 0xda, 0x06, 0x51, 0x3e, - 0xd3, 0x37, 0x06, 0x11, 0x31, 0x04, 0xec, 0x63, 0xf0, 0x1b, 0x4e, 0xa6, - 0xe7, 0x07, 0x41, 0xf6, 0xa3, 0x97, 0x82, 0x33, 0x35, 0xf6, 0x6c, 0xea, - 0xdc, 0x2d, 0x5d, 0x07, 0xf7, 0x8d, 0x08, 0x11, 0xb9, 0x05, 0x24, 0x20, - 0x00, 0xe0, 0xe4, 0x15, 0xaa, 0x97, 0x55, 0x72, 0xa3, 0x37, 0xa3, 0x0e, - 0x2e, 0x4c, 0x6d, 0xa9, 0xd6, 0x40, 0xd3, 0xd2, 0x1c, 0x7d, 0x76, 0x3f, - 0x07, 0xb3, 0xed, 0xfa, 0x9b, 0x0e, 0x40, 0x7b, 0x81, 0xd7, 0x33, 0x47, - 0xe9, 0x0d, 0xf1, 0x7c, 0xb4, 0x9f, 0x9e, 0xa0, 0x8a, 0x75, 0xc9, 0xef, - 0x6a, 0xbd, 0x15, 0x6f, 0x96, 0xd7, 0x67, 0x51, 0x26, 0x9a, 0xd3, 0x21, - 0x97, 0xae, 0x02, 0x69, 0x2f, 0xe6, 0xd1, 0x1d, 0xd4, 0xf5, 0xd2, 0x14, - 0x04, 0x0c, 0x63, 0x07, 0x92, 0xdb, 0x5c, 0xef, 0xa2, 0x90, 0x4a, 0x6d, - 0x41, 0x4c, 0x32, 0xb2, 0x6c, 0x93, 0x30, 0x3b, 0x62, 0xdc, 0xab, 0xc3, - 0x41, 0x2c, 0xfa, 0x9a, 0x47, 0x96, 0x74, 0xe5, 0x6b, 0xf2, 0x26, 0xda, - 0x39, 0x0d, 0xa0, 0xa9, 0xc4, 0xed, 0xa5, 0x60, 0xfa, 0xe0, 0x76, 0x94, - 0x52, 0x7e, 0xcc, 0x67, 0x5c, 0x63, 0x4f, 0x75, 0x7d, 0x8d, 0xfb, 0xd3, - 0x27, 0xd1, 0x98, 0xb8, 0xff, 0x84, 0x6d, 0x95, 0x8f, 0x61, 0x42, 0xb9, - 0x84, 0x6c, 0x1f, 0x32, 0x07, 0xc2, 0xc2, 0xf2, 0x95, 0x29, 0x14, 0x22, - 0xa5, 0x40, 0xbc, 0xeb, 0x37, 0xfe, 0xdb, 0xe8, 0xab, 0xed, 0xaa, 0x0c, - 0x06, 0x09, 0x98, 0xb7, 0x62, 0xd5, 0x70, 0x33, 0xab, 0x38, 0x4b, 0xc8, - 0xf0, 0x32, 0xb0, 0x2f, 0xa1, 0xe6, 0xb1, 0x22, 0x52, 0x3f, 0x65, 0x01, - 0xa2, 0xa6, 0x75, 0xea, 0x4d, 0xd3, 0x12, 0xbf, 0x84, 0xeb, 0x85, 0x33, - 0x6d, 0x55, 0x93, 0x88, 0x4a, 0x74, 0xfe, 0x7c, 0x8b, 0xcf, 0x19, 0x90, - 0x51, 0x85, 0x42, 0xa0, 0xc8, 0xcf, 0xa7, 0x84, 0x0d, 0x09, 0x6b, 0xc9, - 0x2c, 0x13, 0x14, 0xd8, 0x18, 0x74, 0x5d, 0xf4, 0xd0, 0x8d, 0x55, 0x6a, - 0x5a, 0x14, 0x47, 0x17, 0x0b, 0x67, 0x90, 0x74, 0xf2, 0xf2, 0xe9, 0x60, - 0x4f, 0x5c, 0xe5, 0x62, 0x41, 0x38, 0x71, 0x65, 0x44, 0x64, 0xae, 0xc8, - 0xa6, 0x43, 0x15, 0x19, 0xee, 0x6e, 0xff, 0xf1, 0x50, 0x80, 0x33, 0x1f, - 0xfc, 0x21, 0x1a, 0xcd, 0x80, 0x85, 0x52, 0x80, 0x00, 0x8b, 0xb2, 0x32, - 0x2c, 0x28, 0x69, 0x29, 0x9d, 0x98, 0x00, 0x5e, 0x07, 0x1c, 0x86, 0xb7, - 0xf5, 0xdf, 0x3f, 0xbf, 0xed, 0xfa, 0xfd, 0xbf, 0x59, 0x26, 0xa2, 0x57, - 0x8b, 0xf0, 0x1e, 0xf9, 0xf7, 0x27, 0x21, 0xbd, 0xe4, 0x5e, 0x67, 0xbe, - 0x73, 0xef, 0xf9, 0x22, 0x40, 0x73, 0xe4, 0x37, 0x94, 0x52, 0xa2, 0x2a, - 0x14, 0xfd, 0xf3, 0x67, 0x3a, 0x8c, 0x80, 0x2e, 0x7e, 0x2a, 0xca, 0x1f, - 0x18, 0x3a, 0x73, 0x72, 0x05, 0x37, 0xee, 0xab, 0xd0, 0x3a, 0xaa, 0xbf, - 0x72, 0xff, 0x87, 0xb5, 0x09, 0xc6, 0xa2, 0xe6, 0x73, 0x02, 0xf2, 0x13, - 0x05, 0xef, 0xe9, 0xfa, 0xe0, 0xc3, 0xb5, 0xa7, 0xf9, 0xae, 0xb7, 0xb4, - 0xad, 0x38, 0x0f, 0x0f, 0x10, 0x7e, 0x33, 0xe8, 0x1d, 0x0b, 0x0e, 0xaf, - 0x64, 0x5e, 0xbe, 0xb0, 0xe7, 0xee, 0xa9, 0xf1, 0x2e, 0xee, 0x5d, 0x4e, - 0x40, 0xf3, 0xa0, 0x4b, 0x29, 0xa0, 0x00, 0x54, 0x62, 0x99, 0xdc, 0xd9, - 0x27, 0x5a, 0xcc, 0x86, 0xb9, 0xa7, 0x06, 0xcc, 0x14, 0xd2, 0x94, 0x31, - 0x13, 0xaa, 0x17, 0x89, 0x28, 0xe6, 0x6c, 0xb2, 0xba, 0xd8, 0xcd, 0x76, - 0x44, 0x26, 0xd9, 0x09, 0x6a, 0xf0, 0xb6, 0x27, 0x45, 0x69, 0xc5, 0x2d, - 0x5e, 0x72, 0x4d, 0x55, 0x76, 0x6e, 0xe5, 0x71, 0x79, 0x12, 0xcb, 0xcf, - 0x11, 0x2d, 0x96, 0xa1, 0x4f, 0x80, 0xc8, 0x33, 0x08, 0xfe, 0xdd, 0xbb, - 0xab, 0x7a, 0x32, 0x96, 0x89, 0x7b, 0x6f, 0x56, 0xb6, 0xa9, 0x66, 0xa7, - 0xb6, 0xab, 0x72, 0x7b, 0x2d, 0x98, 0x23, 0xa6, 0xea, 0x1a, 0x56, 0xdd, - 0x75, 0xb3, 0x0a, 0xd5, 0x16, 0xd8, 0x86, 0xa4, 0x4f, 0x48, 0x89, 0x60, - 0xf8, 0x54, 0x6f, 0x4c, 0xc1, 0x39, 0x04, 0x5c, 0x04, 0x42, 0x42, 0x90, - 0x64, 0x72, 0x42, 0x90, 0x45, 0xd9, 0x19, 0x16, 0x19, 0x39, 0x9d, 0x03, - 0xe7, 0x62, 0x79, 0x1e, 0x3c, 0xbc, 0x01, 0xe7, 0xc0, 0xfb, 0xfd, 0xbc, - 0xf8, 0xf3, 0x5c, 0x7c, 0xfd, 0xdf, 0x6f, 0xf7, 0xe7, 0xaa, 0x6a, 0xad, - 0x25, 0xd6, 0xf5, 0xb0, 0x62, 0x0a, 0xe0, 0x19, 0xbc, 0xa7, 0x0b, 0xcc, - 0x75, 0xfb, 0xba, 0x42, 0xb6, 0xc5, 0x39, 0xe1, 0x0d, 0x88, 0x19, 0x1e, - 0x19, 0xbd, 0xdb, 0x83, 0xe2, 0x5b, 0x51, 0x5a, 0x56, 0x99, 0x63, 0xa0, - 0xac, 0x31, 0xc5, 0x04, 0xcc, 0x88, 0xee, 0xdf, 0xae, 0xe8, 0x89, 0xe8, - 0xe2, 0x99, 0xd0, 0x39, 0x77, 0xb4, 0xd7, 0x81, 0xca, 0xcf, 0x7e, 0x99, - 0xd7, 0xe4, 0xd0, 0xc7, 0x44, 0x27, 0xee, 0x00, 0xee, 0xe6, 0x9d, 0xfa, - 0x44, 0x53, 0xd6, 0xf3, 0x6a, 0xb5, 0x6f, 0x77, 0x83, 0x6a, 0xc5, 0x00, - 0xb3, 0x74, 0xae, 0x8b, 0x20, 0xc4, 0xd6, 0x29, 0x71, 0xd8, 0xcd, 0x54, - 0x44, 0x14, 0xe5, 0x37, 0x32, 0x78, 0x3b, 0xeb, 0xaa, 0x9a, 0xa9, 0x34, - 0xec, 0xd8, 0x42, 0x56, 0x84, 0x9c, 0xff, 0xf1, 0x50, 0x80, 0x32, 0x9f, - 0xfc, 0x21, 0x1a, 0xcb, 0xc2, 0xa9, 0xf5, 0x80, 0x00, 0x8a, 0xb6, 0xa9, - 0xac, 0x24, 0x37, 0x28, 0x8d, 0x98, 0x07, 0x1c, 0x80, 0x7d, 0xfe, 0xc3, - 0x55, 0x9a, 0xe3, 0xd7, 0xeb, 0xf3, 0xf1, 0xf9, 0xfe, 0x64, 0x6b, 0x73, - 0xcf, 0x3b, 0xd8, 0x04, 0xc0, 0x48, 0xf1, 0x33, 0xa0, 0x69, 0x39, 0xf5, - 0x71, 0x3e, 0xc4, 0x92, 0x56, 0x81, 0x40, 0xc3, 0xd1, 0xf1, 0xa6, 0x2b, - 0xb3, 0x44, 0x31, 0x40, 0x20, 0x16, 0xef, 0x59, 0x78, 0xaa, 0x27, 0xd3, - 0xcb, 0x28, 0xe2, 0xd4, 0x6f, 0x09, 0x8b, 0xf5, 0xfa, 0xb9, 0x71, 0xbe, - 0xbc, 0x65, 0x97, 0x5d, 0xa5, 0x36, 0xdd, 0xea, 0xf3, 0xe6, 0x00, 0x58, - 0xf3, 0xff, 0x84, 0x03, 0x7d, 0x12, 0xd0, 0x35, 0x3f, 0x13, 0xd9, 0xc8, - 0xac, 0x82, 0xb2, 0x2a, 0xf4, 0x73, 0x8a, 0xc9, 0x66, 0x15, 0x85, 0xf2, - 0x40, 0x11, 0x5c, 0x86, 0x16, 0x68, 0xde, 0x53, 0x37, 0x5a, 0x35, 0xd1, - 0x46, 0xb7, 0xc3, 0x57, 0xfd, 0xfa, 0xfd, 0x47, 0xff, 0x88, 0x40, 0x05, - 0x21, 0x2b, 0x64, 0x2c, 0x58, 0x88, 0x60, 0x9e, 0x65, 0xa4, 0x51, 0xa1, - 0x6d, 0x65, 0x37, 0xa9, 0x86, 0x7b, 0x29, 0xc5, 0x85, 0x64, 0x66, 0x27, - 0x29, 0xae, 0xbe, 0xd4, 0x9c, 0x2f, 0xae, 0xa9, 0xae, 0x3a, 0x51, 0x17, - 0xb0, 0xa9, 0x8b, 0xe9, 0x14, 0x9a, 0xab, 0xb2, 0x3c, 0xb0, 0x65, 0x92, - 0xf0, 0x06, 0x15, 0x92, 0xdb, 0x06, 0x06, 0xe4, 0xd6, 0xaf, 0x0d, 0x4a, - 0x84, 0xf6, 0xab, 0xa0, 0x94, 0xb4, 0x55, 0xb3, 0x77, 0x6d, 0xd2, 0xb2, - 0xf8, 0xbc, 0x93, 0x68, 0x7d, 0xa2, 0x27, 0x4b, 0xdb, 0x4d, 0x8a, 0x3d, - 0x27, 0xd4, 0xd2, 0x8d, 0x8c, 0xa2, 0x7c, 0x5d, 0x5d, 0x54, 0x96, 0x4a, - 0x46, 0x50, 0x98, 0x9e, 0x78, 0x60, 0x28, 0x11, 0x02, 0x63, 0x09, 0xc1, - 0xc6, 0xd7, 0x98, 0xa2, 0xfb, 0x88, 0xca, 0x3a, 0xc8, 0xc8, 0xb0, 0xc9, - 0xcd, 0x1e, 0x64, 0x17, 0x89, 0x83, 0xe4, 0x70, 0x39, 0x1c, 0x73, 0xc7, - 0x3c, 0x73, 0xc5, 0x6f, 0x83, 0x55, 0xe3, 0xd9, 0xbf, 0xd6, 0xaf, 0x35, - 0x2a, 0xa4, 0x79, 0xa2, 0x81, 0xaf, 0x13, 0xb9, 0x58, 0x95, 0x2b, 0xc1, - 0xa9, 0x7f, 0x93, 0x86, 0x6f, 0x6e, 0x29, 0x1b, 0x06, 0xf4, 0x5b, 0x46, - 0xc0, 0xa4, 0xcd, 0x33, 0xe7, 0xe7, 0xbe, 0x0d, 0xc6, 0x78, 0x5e, 0x77, - 0x0d, 0x9c, 0xdd, 0x0a, 0xc9, 0x52, 0x15, 0x85, 0x23, 0x3f, 0xbb, 0xff, - 0x15, 0xb9, 0x2b, 0x70, 0x57, 0xa8, 0x8b, 0xa3, 0x5f, 0x82, 0x26, 0xeb, - 0xb8, 0x35, 0x63, 0xaf, 0xa3, 0xd7, 0x72, 0x77, 0xb7, 0xe1, 0x5b, 0x00, - 0x72, 0x7f, 0xea, 0x79, 0x92, 0xf0, 0x3d, 0x3a, 0x80, 0x1b, 0xe6, 0xe3, - 0xc1, 0x9a, 0xd3, 0x28, 0x9b, 0x08, 0xfd, 0x16, 0xf4, 0xd8, 0x57, 0xf7, - 0x00, 0x92, 0x48, 0xb3, 0x43, 0xa5, 0x1b, 0x2f, 0xc1, 0xdf, 0xb3, 0xef, - 0x7c, 0x9c, 0xff, 0xf1, 0x50, 0x80, 0x30, 0x7f, 0xfc, 0x21, 0x1a, 0xc8, - 0x30, 0xef, 0xff, 0x80, 0x00, 0x8c, 0xb2, 0x32, 0x2c, 0x2e, 0xb2, 0x11, - 0x98, 0x82, 0xc7, 0x41, 0x80, 0x00, 0x7d, 0x7c, 0x8b, 0xd7, 0x7f, 0x13, - 0xe7, 0xfd, 0xbf, 0xa7, 0xeb, 0xf3, 0xf7, 0xae, 0xb2, 0x75, 0x97, 0x9d, - 0xb6, 0x10, 0x70, 0xc9, 0x6c, 0x5e, 0xb1, 0x9b, 0x3b, 0x53, 0x53, 0x41, - 0xf5, 0xba, 0xee, 0x2a, 0xec, 0x40, 0xa2, 0xdb, 0xdc, 0x09, 0x40, 0xbc, - 0xf3, 0x99, 0xc1, 0x2d, 0x02, 0xcc, 0x1b, 0x22, 0xd1, 0x65, 0x4e, 0xfc, - 0x72, 0x8d, 0xd9, 0x5d, 0x5c, 0xa7, 0x0b, 0x0e, 0x2f, 0xf1, 0x65, 0x02, - 0xf3, 0x80, 0x04, 0xb2, 0x85, 0x44, 0xda, 0xa7, 0xba, 0xc0, 0x10, 0x0c, - 0x69, 0x31, 0xbc, 0xa5, 0x33, 0xc6, 0xbe, 0x20, 0xe8, 0x9f, 0x21, 0xf4, - 0x6e, 0x9f, 0xc3, 0xba, 0x99, 0x7d, 0xf0, 0xa6, 0xfe, 0x5e, 0xb6, 0xaf, - 0x99, 0xec, 0xbf, 0xfa, 0xa3, 0x75, 0x11, 0xa3, 0xec, 0x9a, 0x9f, 0xc9, - 0xee, 0xed, 0x6f, 0x19, 0xbb, 0xf5, 0x2f, 0xc4, 0x0e, 0xb3, 0x61, 0x0c, - 0xe8, 0x12, 0x56, 0xd3, 0x99, 0xb2, 0x4d, 0x3b, 0x54, 0x93, 0x11, 0xde, - 0x81, 0x4b, 0x13, 0x02, 0xf1, 0x50, 0x32, 0x72, 0xa6, 0xeb, 0x2b, 0xa0, - 0x90, 0xd4, 0xdf, 0x64, 0xbb, 0xe5, 0x59, 0x4f, 0x03, 0xd4, 0x72, 0x1c, - 0xd8, 0xf5, 0xcd, 0x9d, 0xb6, 0x54, 0x87, 0x7c, 0x4e, 0xb6, 0xad, 0xf6, - 0xe4, 0xe7, 0x8a, 0xce, 0x99, 0xcd, 0x15, 0x87, 0xdf, 0x1e, 0xa7, 0x38, - 0xa6, 0x57, 0xb2, 0x95, 0x6e, 0xa9, 0x2b, 0x6c, 0x5b, 0x30, 0x6f, 0xb0, - 0xcd, 0xb8, 0xeb, 0x5a, 0xba, 0x71, 0x5a, 0xa1, 0x20, 0x6d, 0xe0, 0xc3, - 0x5f, 0x9f, 0xdc, 0xf9, 0x2d, 0x0b, 0x2f, 0xe5, 0xa6, 0x46, 0x67, 0x1d, - 0x65, 0x63, 0xd8, 0x65, 0x64, 0x21, 0x0b, 0x0b, 0xce, 0xc1, 0xf0, 0xb0, - 0x7c, 0x80, 0xb6, 0x0b, 0x32, 0xfe, 0xbe, 0x45, 0xeb, 0xbf, 0x89, 0xf3, - 0xfe, 0xdb, 0x8a, 0x68, 0xbc, 0xb9, 0x40, 0x12, 0x30, 0xb3, 0x85, 0x52, - 0x06, 0x34, 0xe6, 0x8d, 0x4d, 0x49, 0xcd, 0x80, 0x46, 0x11, 0xe9, 0x4b, - 0x68, 0x7c, 0xc8, 0x2b, 0x48, 0x16, 0x73, 0xf0, 0xf2, 0x63, 0x8b, 0x5f, - 0xe6, 0xb8, 0xd6, 0xbc, 0xa6, 0xf0, 0xcb, 0x84, 0xac, 0x6b, 0x5a, 0x10, - 0x2e, 0x8b, 0xf3, 0xdf, 0xaa, 0xe8, 0x82, 0x82, 0x60, 0x2e, 0xb1, 0xbb, - 0x98, 0x50, 0xef, 0x36, 0x82, 0x15, 0x60, 0xf6, 0xd6, 0xdc, 0xe1, 0x34, - 0xe7, 0xa0, 0x25, 0x1b, 0xa0, 0xb0, 0xbc, 0x69, 0x32, 0x49, 0x15, 0x73, - 0x2b, 0x13, 0x9a, 0x11, 0x1d, 0x97, 0xbb, 0x3d, 0xb4, 0x4a, 0x59, 0xf2, - 0x16, 0x06, 0xe9, 0x4a, 0xce, 0xb5, 0x1e, 0xec, 0x31, 0xe9, 0x79, 0x5b, - 0x54, 0x93, 0x91, 0xc8, 0xfc, 0xff, 0xf1, 0x50, 0x80, 0x30, 0xbf, 0xfc, - 0x21, 0x1a, 0xcd, 0xa0, 0x00, 0x3c, 0x80, 0x00, 0x8b, 0xb4, 0x21, 0x2c, - 0x32, 0x73, 0x3a, 0x0c, 0xc2, 0x84, 0x62, 0xa0, 0xc0, 0x00, 0x38, 0xe4, - 0x9c, 0x5f, 0xae, 0xbe, 0x3f, 0x6f, 0xf4, 0xef, 0x5f, 0xc7, 0xfa, 0x55, - 0xd7, 0x4a, 0xbe, 0xea, 0xb6, 0x27, 0x1d, 0x09, 0x90, 0xc9, 0x60, 0xe9, - 0x8a, 0xd8, 0xb5, 0x45, 0x32, 0xe7, 0x30, 0x76, 0xa1, 0x44, 0x7c, 0x99, - 0xe1, 0x86, 0x55, 0x2c, 0x3b, 0xf5, 0x23, 0x73, 0x29, 0x31, 0x92, 0x5b, - 0x0f, 0x0d, 0x7b, 0xb3, 0x90, 0xc7, 0x87, 0x96, 0x4c, 0xb3, 0xc5, 0x2b, - 0xbc, 0x86, 0x1f, 0xc6, 0x7d, 0xb7, 0x11, 0x93, 0x9a, 0x53, 0x6f, 0x7b, - 0xab, 0x3e, 0xa7, 0x59, 0xbf, 0xe5, 0x11, 0x39, 0x4f, 0xfd, 0x3b, 0xde, - 0x4b, 0x43, 0x4a, 0x78, 0x41, 0x38, 0xfa, 0xff, 0x1b, 0x07, 0xaf, 0xf0, - 0x68, 0xcf, 0xc5, 0xd6, 0x2f, 0x1a, 0xa4, 0xb9, 0x1e, 0x55, 0xac, 0x21, - 0xb3, 0x19, 0x1a, 0x80, 0x58, 0xc1, 0x5f, 0x47, 0x5d, 0x73, 0xf2, 0x1a, - 0x00, 0x1f, 0xcd, 0x06, 0xd8, 0x32, 0x10, 0xd1, 0xfd, 0x25, 0xdc, 0x43, - 0xbc, 0xe9, 0x5f, 0x7a, 0xf6, 0xa1, 0x2e, 0x1b, 0xd6, 0xf0, 0x1d, 0x6d, - 0x76, 0xae, 0x49, 0x31, 0xc9, 0xf2, 0x65, 0x56, 0x69, 0x28, 0x7b, 0x73, - 0xad, 0xe9, 0x9a, 0xde, 0x61, 0x34, 0x0d, 0x15, 0xcb, 0x5a, 0x52, 0xb2, - 0xdd, 0x53, 0x53, 0x36, 0xb5, 0x2b, 0x9a, 0x16, 0x63, 0xba, 0xba, 0x99, - 0x19, 0x95, 0x8e, 0xa4, 0xcb, 0xb8, 0xa5, 0xba, 0xbb, 0x82, 0x74, 0x7b, - 0x0c, 0x49, 0xe8, 0xc8, 0xca, 0x4f, 0x5e, 0x8d, 0xa0, 0x3f, 0x4c, 0xf9, - 0xe2, 0x8e, 0x6e, 0x1c, 0x4b, 0xa1, 0xd5, 0x0b, 0x16, 0x84, 0x2c, 0xc5, - 0xd4, 0x0e, 0x70, 0x95, 0x22, 0x51, 0x76, 0x54, 0x3d, 0x85, 0xd6, 0x23, - 0x40, 0xf9, 0xd8, 0x9e, 0x47, 0x8f, 0x2f, 0x1e, 0x7c, 0x00, 0x3f, 0x9f, - 0xe3, 0xef, 0xf6, 0xbf, 0xab, 0xf5, 0xd3, 0xe7, 0xfe, 0x6e, 0xb3, 0x8c, - 0xb7, 0x32, 0x6b, 0x00, 0x09, 0xc7, 0xc9, 0xea, 0x51, 0xe4, 0x92, 0xe0, - 0x4e, 0xf1, 0x21, 0x06, 0xeb, 0x7c, 0x60, 0x4c, 0x6b, 0xaf, 0x71, 0x1a, - 0x6a, 0x4d, 0x21, 0xe5, 0x5a, 0x2a, 0xcc, 0xe9, 0xbd, 0x8e, 0x8d, 0x4d, - 0x48, 0x6d, 0xab, 0xc4, 0xc7, 0x19, 0x05, 0xd1, 0x60, 0x69, 0x26, 0xb3, - 0x45, 0x59, 0x5e, 0x9b, 0x00, 0xa8, 0x95, 0xfa, 0xf2, 0xf0, 0x56, 0x76, - 0x96, 0x59, 0x85, 0xea, 0x97, 0xaf, 0xeb, 0x21, 0x46, 0x20, 0xca, 0xe0, - 0xa0, 0x1c, 0xd4, 0x96, 0xd8, 0x28, 0x4a, 0xc2, 0x67, 0x7a, 0x01, 0x10, - 0x86, 0x20, 0x84, 0x94, 0xc9, 0xc1, 0x6e, 0xd8, 0x4e, 0x0a, 0x49, 0x13, - 0xd6, 0x97, 0xba, 0xdd, 0x6a, 0xd4, 0x67, 0x29, 0x40, 0x70, 0xff, 0xf1, - 0x50, 0x80, 0x2d, 0xbf, 0xfc, 0x21, 0x1a, 0xcf, 0x20, 0x8d, 0xbb, 0x80, - 0x00, 0x8c, 0xb2, 0x32, 0x2c, 0x32, 0x73, 0x5a, 0x15, 0x88, 0x83, 0x00, - 0x71, 0xc9, 0x15, 0xac, 0xa3, 0xae, 0x3e, 0x7f, 0x7c, 0xf1, 0xf8, 0xfd, - 0x3f, 0x9f, 0xd3, 0xe2, 0x5a, 0xb5, 0xac, 0xae, 0x6b, 0x90, 0xf5, 0xcb, - 0x15, 0xdb, 0x3c, 0x9d, 0x87, 0xe0, 0xe9, 0x16, 0xf7, 0x64, 0x2f, 0x6b, - 0x5f, 0x70, 0xda, 0x08, 0xa4, 0x84, 0x60, 0xde, 0xe5, 0x65, 0x36, 0x10, - 0x74, 0x1d, 0x55, 0x58, 0xaa, 0xb3, 0x2d, 0xaf, 0xb7, 0x33, 0x04, 0xd5, - 0x88, 0x0e, 0x9b, 0xe9, 0x41, 0xd7, 0x97, 0xa3, 0x91, 0xc1, 0xd8, 0xe3, - 0x7e, 0x23, 0x19, 0xef, 0x3a, 0x1f, 0x4e, 0x4f, 0x79, 0x07, 0x5f, 0xd1, - 0x2b, 0x7c, 0x61, 0x77, 0x85, 0xd3, 0x3a, 0x6f, 0xdc, 0xbc, 0x63, 0xdc, - 0x1e, 0xa1, 0x9b, 0xf2, 0x3c, 0x8f, 0x5f, 0xca, 0xe7, 0xe9, 0x8e, 0xba, - 0x0b, 0xa8, 0x49, 0xdf, 0x67, 0x65, 0x72, 0x71, 0x3d, 0x40, 0x3c, 0x75, - 0xa0, 0x9a, 0x6c, 0xc7, 0xb4, 0xf3, 0x10, 0x2a, 0xc2, 0x06, 0xf3, 0x6e, - 0xdb, 0x96, 0xfa, 0x95, 0xa6, 0xe2, 0x39, 0x40, 0x4e, 0x6c, 0xe5, 0xfe, - 0xaf, 0xe0, 0xdc, 0x8d, 0x8c, 0xe9, 0x4b, 0x27, 0x3d, 0xe7, 0x43, 0x19, - 0x02, 0x35, 0x64, 0x2e, 0x5a, 0xca, 0x82, 0x25, 0x8d, 0x41, 0x5d, 0xae, - 0x0d, 0x17, 0x0c, 0x93, 0x95, 0x1b, 0xdc, 0x64, 0x64, 0x83, 0x9a, 0xa8, - 0x98, 0xa2, 0x69, 0xc0, 0x0a, 0x8a, 0x9b, 0x32, 0x1d, 0x4b, 0x1f, 0x3e, - 0x86, 0xb5, 0x81, 0x61, 0xc5, 0x94, 0x60, 0xcf, 0x3a, 0x76, 0xac, 0x19, - 0x0c, 0xab, 0x05, 0x8c, 0xb2, 0x31, 0x48, 0x76, 0x17, 0x5a, 0x11, 0x83, - 0xe7, 0x61, 0x79, 0x47, 0x2e, 0x3a, 0xf9, 0x4a, 0xe3, 0x99, 0x5c, 0x6b, - 0xc6, 0x95, 0xe7, 0x8f, 0x9f, 0xab, 0xf1, 0xf8, 0x5a, 0xa2, 0xdb, 0xe3, - 0x0a, 0xa0, 0x0a, 0x5d, 0xd8, 0xae, 0xbd, 0x62, 0x3d, 0x8e, 0x3f, 0xa4, - 0x24, 0x69, 0xb8, 0x91, 0x55, 0xd2, 0x8a, 0xac, 0x85, 0xee, 0xc1, 0xa8, - 0x57, 0xd9, 0xf8, 0x95, 0xa7, 0x28, 0x9a, 0xa8, 0x2e, 0xeb, 0x29, 0x12, - 0x52, 0xbe, 0xd7, 0x5f, 0x22, 0xd5, 0x39, 0x96, 0xab, 0x44, 0x6d, 0xbb, - 0xb2, 0x85, 0x68, 0xec, 0x06, 0x81, 0x78, 0x7b, 0x21, 0x14, 0x73, 0xaf, - 0x66, 0x55, 0xe0, 0x3a, 0xee, 0xee, 0x22, 0xda, 0x86, 0x3c, 0x40, 0xb5, - 0x21, 0x5a, 0x37, 0xaf, 0x3c, 0x9d, 0x85, 0x55, 0x08, 0x00, 0x94, 0x96, - 0x43, 0xb2, 0x53, 0x07, 0x27, 0x98, 0xab, 0x14, 0xa5, 0x9b, 0x46, 0x46, - 0x93, 0x1a, 0xa7, 0xff, 0xf1, 0x50, 0x80, 0x31, 0x3f, 0xfc, 0x21, 0x1a, - 0xcf, 0x73, 0xb2, 0x1e, 0x80, 0x00, 0x8c, 0xb2, 0x32, 0x2c, 0x32, 0xb4, - 0x30, 0x85, 0x06, 0xc7, 0x41, 0x01, 0x78, 0x01, 0xfb, 0xfe, 0xde, 0xde, - 0xbc, 0xf3, 0xae, 0xff, 0x0f, 0xb7, 0xfb, 0x7d, 0xbd, 0xbf, 0x4f, 0xd6, - 0x56, 0x9c, 0x2b, 0x2b, 0xbc, 0x0f, 0x5e, 0x6d, 0xfa, 0xdf, 0xb5, 0xde, - 0x7e, 0xdb, 0x49, 0xb9, 0xfe, 0x72, 0xa7, 0xd0, 0x6e, 0xf5, 0x41, 0x0d, - 0x8a, 0x08, 0xa8, 0x85, 0x96, 0xa3, 0x6e, 0x62, 0x73, 0x09, 0xc5, 0x3b, - 0x49, 0xb9, 0x0c, 0x76, 0xed, 0xbd, 0xba, 0xb2, 0x4d, 0x27, 0x4a, 0x0c, - 0x38, 0xff, 0x9c, 0x50, 0x88, 0xb9, 0xca, 0x94, 0x0c, 0xd8, 0xe2, 0x89, - 0x49, 0x3c, 0xad, 0xa0, 0x35, 0x3f, 0x9f, 0x38, 0xfa, 0x64, 0xed, 0xac, - 0xba, 0x37, 0x96, 0xee, 0xef, 0xa3, 0xfa, 0xc6, 0x39, 0xe7, 0xf6, 0x34, - 0xd1, 0x2c, 0xb1, 0x63, 0xf3, 0x82, 0x55, 0x9f, 0xe0, 0xc1, 0xf8, 0x92, - 0x24, 0xb6, 0x80, 0x7c, 0xe4, 0x7b, 0x03, 0xae, 0x1b, 0x85, 0x5f, 0xdc, - 0xe8, 0xa7, 0x05, 0xf8, 0x7e, 0xa7, 0xd4, 0x05, 0xf8, 0x30, 0x74, 0x60, - 0xff, 0x4d, 0x88, 0x46, 0xb8, 0x7d, 0x95, 0x3f, 0x6c, 0x63, 0x5d, 0xd2, - 0x81, 0x04, 0x6d, 0xa9, 0xa7, 0x45, 0x2a, 0x54, 0x90, 0x1e, 0x8a, 0x31, - 0xb1, 0x94, 0x79, 0x28, 0x94, 0xd6, 0x6b, 0xb0, 0xb2, 0xd1, 0x27, 0x75, - 0xd8, 0x67, 0xc5, 0x8b, 0xeb, 0x5a, 0xf4, 0x06, 0xc6, 0xcc, 0x67, 0xb5, - 0x26, 0x84, 0x46, 0x96, 0x75, 0x5b, 0x9f, 0x85, 0xfa, 0x93, 0x9c, 0x37, - 0x81, 0xf5, 0x09, 0x5d, 0x71, 0xb4, 0xd2, 0xa4, 0x62, 0x2e, 0x52, 0xad, - 0xb3, 0xab, 0x4f, 0x82, 0x2a, 0x06, 0x67, 0x95, 0x68, 0x13, 0xa8, 0x4b, - 0xcf, 0x64, 0xfd, 0xde, 0x25, 0x25, 0x50, 0xc8, 0x73, 0xab, 0xcc, 0x23, - 0x28, 0xaa, 0x7b, 0x0b, 0x9c, 0xce, 0xc1, 0xf3, 0x20, 0x7c, 0x28, 0x2f, - 0x28, 0xde, 0x97, 0xdb, 0x46, 0xf4, 0xdf, 0xc7, 0x75, 0x9a, 0xfa, 0xe7, - 0xbf, 0xc3, 0xf6, 0xff, 0xae, 0x59, 0x75, 0xe2, 0xe4, 0xa9, 0x58, 0x11, - 0x17, 0xec, 0xda, 0x02, 0x0f, 0x56, 0xbb, 0x18, 0x81, 0xa5, 0x5d, 0xd0, - 0xf1, 0x84, 0x2f, 0x73, 0x15, 0x96, 0x7a, 0xe6, 0x4e, 0xa5, 0x5f, 0x42, - 0x2b, 0x38, 0x22, 0x31, 0xdb, 0x99, 0x96, 0xac, 0x80, 0x2f, 0xed, 0xfe, - 0xe4, 0x40, 0xd1, 0x87, 0xcb, 0xcb, 0xf6, 0xfa, 0x24, 0x35, 0x2b, 0x06, - 0x6e, 0xe1, 0x1d, 0x6f, 0x4d, 0xaf, 0x5b, 0x1e, 0x85, 0xd6, 0x68, 0xf5, - 0x24, 0x00, 0x96, 0x84, 0x67, 0x3c, 0xd9, 0xe9, 0xc0, 0x0d, 0xfb, 0x95, - 0x53, 0xd7, 0xa4, 0xc0, 0xb8, 0xb2, 0x51, 0xbf, 0x7e, 0xe1, 0x5b, 0xe6, - 0xc2, 0x73, 0xc0, 0xa1, 0x6d, 0xa7, 0xcc, 0x02, 0xec, 0x7e, 0xc1, 0xc0, - 0xff, 0xf1, 0x50, 0x80, 0x34, 0x9f, 0xfc, 0x21, 0x1a, 0xcb, 0xa2, 0x86, - 0xbc, 0x80, 0x00, 0x8b, 0xb6, 0xcb, 0x90, 0x66, 0x16, 0x4a, 0x0c, 0x00, - 0x94, 0x46, 0xb9, 0xd6, 0xfe, 0x3c, 0x79, 0xf7, 0xfa, 0xf6, 0xfe, 0x9f, - 0xed, 0xef, 0xd7, 0xbf, 0xc3, 0x52, 0x55, 0x71, 0xbd, 0xee, 0x83, 0x2e, - 0x1c, 0x40, 0x42, 0xfd, 0xd6, 0x9a, 0xad, 0x03, 0x31, 0x48, 0x28, 0x0f, - 0x94, 0xd8, 0x2d, 0x0a, 0x42, 0xc1, 0x71, 0xc3, 0x00, 0x01, 0x78, 0x14, - 0xdd, 0x4e, 0x6f, 0x3e, 0xf0, 0x8d, 0x96, 0xc3, 0xb4, 0xac, 0xb0, 0xb5, - 0x99, 0x4e, 0x17, 0x50, 0xa1, 0x83, 0x1b, 0xbc, 0x39, 0x5f, 0xa0, 0xe2, - 0x2e, 0x33, 0xdd, 0x85, 0xd0, 0x2e, 0xb3, 0xad, 0x3c, 0x53, 0x63, 0x57, - 0xb4, 0x90, 0x61, 0x30, 0xbb, 0xac, 0x86, 0xed, 0x3d, 0xdb, 0x2a, 0x32, - 0xe5, 0xde, 0x58, 0x18, 0x00, 0x3c, 0xaf, 0xc6, 0x71, 0xd4, 0xfc, 0xce, - 0x8f, 0x5e, 0x61, 0xd1, 0xf0, 0xb2, 0x8b, 0xb3, 0xfc, 0xf9, 0xc3, 0x16, - 0xae, 0xd7, 0x00, 0x16, 0x55, 0x3f, 0x32, 0x49, 0x22, 0xb5, 0x8a, 0x66, - 0xe1, 0x53, 0xd4, 0xf3, 0xb0, 0x3d, 0x10, 0x40, 0x0a, 0xc7, 0x50, 0xcc, - 0x04, 0x76, 0xa6, 0xd5, 0x5a, 0x5d, 0xc8, 0xc1, 0x6d, 0xc4, 0x16, 0x85, - 0x18, 0x43, 0xa2, 0xc7, 0x18, 0x1a, 0x64, 0xca, 0xfa, 0xd1, 0x6a, 0xb0, - 0x68, 0x50, 0x92, 0xda, 0x31, 0x2d, 0x17, 0x98, 0x33, 0x59, 0x29, 0x9e, - 0x48, 0x36, 0x40, 0xd8, 0x35, 0x00, 0x10, 0x45, 0xca, 0x15, 0xab, 0xab, - 0x5b, 0xd1, 0x0b, 0x93, 0x0a, 0xe5, 0x54, 0xbb, 0x7a, 0x84, 0x69, 0x8b, - 0x4d, 0x2b, 0x83, 0xc2, 0x0e, 0x5e, 0x11, 0x84, 0xb6, 0x08, 0xbf, 0xf6, - 0x63, 0xa4, 0x8b, 0x1e, 0x1b, 0x5c, 0x90, 0x03, 0xa6, 0xc4, 0xf0, 0xa5, - 0x6f, 0xa8, 0xa8, 0x85, 0xab, 0xaa, 0xf5, 0x20, 0xc6, 0x42, 0x8a, 0xa7, - 0xb0, 0xbb, 0x90, 0x3e, 0x54, 0x2b, 0x07, 0xc8, 0xe8, 0xef, 0xaf, 0xc7, - 0xf4, 0x75, 0xcf, 0x1d, 0xdc, 0xaf, 0x6e, 0x72, 0xf3, 0x8f, 0x3e, 0xff, - 0x59, 0xf6, 0xff, 0xaa, 0x65, 0xe7, 0x89, 0x39, 0xbe, 0xae, 0xa0, 0x21, - 0xb3, 0x5a, 0xfc, 0x83, 0x89, 0x37, 0x13, 0xc4, 0x39, 0x43, 0xd5, 0xe2, - 0xe3, 0xca, 0xab, 0x55, 0xd7, 0x15, 0x15, 0x66, 0xc7, 0x76, 0xfa, 0x7a, - 0xb7, 0x43, 0x02, 0xa9, 0x78, 0x46, 0x3a, 0x57, 0x32, 0x67, 0x76, 0x20, - 0xac, 0xfd, 0x27, 0xdf, 0xc8, 0xba, 0xac, 0xae, 0x19, 0x83, 0x08, 0xab, - 0x82, 0x05, 0xf1, 0x76, 0x02, 0x54, 0xba, 0xd3, 0xb1, 0x57, 0x8d, 0x46, - 0x54, 0x20, 0xd2, 0x00, 0x6a, 0xcd, 0xc0, 0x6e, 0xb2, 0xd4, 0x3d, 0xfd, - 0x95, 0x29, 0x10, 0x4e, 0x5c, 0xf7, 0xab, 0x4a, 0x01, 0xde, 0x9b, 0xd1, - 0xb2, 0xb8, 0x41, 0xf5, 0x05, 0x88, 0x3a, 0x40, 0xc7, 0xc3, 0x64, 0x98, - 0x9f, 0x4f, 0x9b, 0x6c, 0xdf, 0x2d, 0xe3, 0xaf, 0x9f, 0x63, 0x0b, 0x63, - 0xa9, 0xd7, 0x32, 0x42, 0x50, 0xd1, 0x25, 0x55, 0xcc, 0xa1, 0xac, 0xf8, - 0xff, 0xf1, 0x50, 0x80, 0x2e, 0xbf, 0xfc, 0x21, 0x1a, 0xcd, 0x10, 0x03, - 0xad, 0x00, 0x00, 0x8d, 0xb2, 0x32, 0x2c, 0x2e, 0x94, 0x5b, 0x20, 0x84, - 0x12, 0xb5, 0xbb, 0x8d, 0xb5, 0x53, 0xf5, 0xfd, 0xb8, 0xf1, 0xf7, 0xef, - 0xdb, 0xf4, 0xfc, 0x7d, 0x7f, 0x7f, 0xf4, 0xef, 0x5d, 0xfb, 0x66, 0xaf, - 0x2b, 0xa3, 0xc6, 0xe8, 0x19, 0x73, 0x7e, 0x53, 0x9f, 0x11, 0x6c, 0x6d, - 0x01, 0x4a, 0x71, 0x09, 0xc6, 0xcd, 0x42, 0x75, 0x0b, 0x5a, 0x06, 0x82, - 0x63, 0x31, 0x8e, 0x49, 0xfa, 0x16, 0xcc, 0xc2, 0x29, 0xa6, 0xb8, 0x23, - 0x28, 0x6b, 0xeb, 0x50, 0x26, 0xd0, 0x8e, 0x47, 0xe9, 0xc8, 0xc6, 0x35, - 0xd5, 0x60, 0x14, 0xc0, 0x39, 0x36, 0x2f, 0x1a, 0x9d, 0xf3, 0x7e, 0x52, - 0x42, 0xdf, 0xb3, 0x9c, 0xf1, 0x75, 0xef, 0xe7, 0xcc, 0xe5, 0x9b, 0xbb, - 0xb8, 0x5f, 0x18, 0x2c, 0x83, 0x6f, 0x30, 0xf5, 0x5f, 0x88, 0xb0, 0x26, - 0xa2, 0x88, 0x60, 0x2d, 0x29, 0x7f, 0xbb, 0x86, 0xb7, 0x52, 0xbe, 0x1d, - 0xb5, 0x4b, 0x1e, 0xcf, 0xc2, 0x65, 0x16, 0x94, 0x9e, 0x75, 0xed, 0x1f, - 0xf6, 0xd5, 0x7c, 0x4e, 0x5e, 0x3c, 0x76, 0xf5, 0x3b, 0xdd, 0x85, 0x67, - 0xb0, 0x5e, 0x87, 0x75, 0x65, 0x18, 0x31, 0xbd, 0x8e, 0xd7, 0x40, 0x1d, - 0xd8, 0xba, 0xc4, 0xe2, 0xcf, 0x10, 0x56, 0x11, 0xca, 0xce, 0x84, 0x68, - 0x60, 0xcf, 0x54, 0x52, 0x26, 0x78, 0x1b, 0xb5, 0x83, 0xa8, 0xe9, 0xae, - 0x5c, 0x0d, 0xb0, 0x95, 0xe9, 0x1b, 0xa2, 0xb7, 0xca, 0xa9, 0x86, 0x59, - 0x90, 0xea, 0x8a, 0x62, 0x6c, 0x92, 0x7a, 0x12, 0x62, 0x2a, 0x21, 0x26, - 0xf0, 0x90, 0x45, 0x9e, 0x05, 0xec, 0x06, 0x53, 0xb0, 0xa1, 0xde, 0x3b, - 0xf5, 0xf6, 0x13, 0xbd, 0x09, 0x69, 0xfe, 0xef, 0xe7, 0x6e, 0x9c, 0x63, - 0x6c, 0xaa, 0x7b, 0x0a, 0xbb, 0xce, 0x81, 0xf1, 0x30, 0xbc, 0x8e, 0x78, - 0xe4, 0xf6, 0xf4, 0x94, 0x25, 0x7e, 0xfe, 0xb8, 0xf1, 0x38, 0xeb, 0xed, - 0xf8, 0x7c, 0xeb, 0x65, 0xa7, 0xf2, 0x97, 0x55, 0xb5, 0x80, 0x9c, 0xd6, - 0x81, 0xbe, 0x97, 0x30, 0xb6, 0x21, 0x4b, 0x42, 0x3a, 0x2a, 0xee, 0x78, - 0xab, 0x25, 0x0d, 0xc0, 0xf0, 0x42, 0x21, 0x4f, 0x2b, 0xe8, 0xa8, 0x42, - 0x55, 0x5d, 0x74, 0x9c, 0xc0, 0xb0, 0xd6, 0x7d, 0x1f, 0xcc, 0x0b, 0xce, - 0x17, 0xb2, 0xcd, 0x67, 0x4d, 0x15, 0x63, 0xaa, 0x40, 0x9b, 0x39, 0xa6, - 0x5a, 0x9a, 0xef, 0x8b, 0xc6, 0xae, 0x6c, 0x70, 0x00, 0x6a, 0xb4, 0x39, - 0xbf, 0x25, 0xd5, 0x94, 0x78, 0xf1, 0x49, 0x70, 0xc6, 0x16, 0x1c, 0xc2, - 0x6d, 0x5d, 0xae, 0x6a, 0x61, 0x33, 0xc4, 0xa5, 0x7b, 0x16, 0x61, 0x04, - 0x5c, 0xff, 0xf1, 0x50, 0x80, 0x30, 0x5f, 0xfc, 0x21, 0x1a, 0xcf, 0xa9, - 0x6a, 0xdc, 0x00, 0x00, 0x8d, 0xb2, 0x32, 0x2c, 0x2a, 0xe4, 0x23, 0x21, - 0x08, 0x17, 0x2f, 0x7c, 0x65, 0xf3, 0x9c, 0x49, 0xf1, 0xee, 0xf1, 0xe7, - 0xce, 0x7b, 0xfe, 0xf7, 0xfa, 0x7e, 0xb9, 0x33, 0xa5, 0x5a, 0x5b, 0x7c, - 0xb6, 0x11, 0xb8, 0xfc, 0xf3, 0x64, 0x5e, 0xbd, 0x2e, 0xd3, 0x03, 0xd4, - 0xcf, 0xb4, 0x39, 0xfa, 0xc2, 0xc7, 0x9a, 0x87, 0x1e, 0x6e, 0x01, 0xcc, - 0x19, 0x78, 0x04, 0xa0, 0x06, 0xac, 0xd5, 0x47, 0x62, 0x8b, 0xac, 0x7a, - 0xa7, 0x57, 0x9d, 0x95, 0x77, 0x06, 0xeb, 0x3f, 0xfd, 0xa0, 0x5d, 0x26, - 0x39, 0xcc, 0xe6, 0x71, 0x96, 0xf0, 0x89, 0xcc, 0x8d, 0x02, 0xae, 0x19, - 0xaa, 0xed, 0xda, 0xb7, 0x15, 0xeb, 0x9c, 0x23, 0xad, 0x32, 0x74, 0x80, - 0x01, 0xba, 0x32, 0x76, 0x51, 0x79, 0xe8, 0x0a, 0xe3, 0x73, 0x40, 0x19, - 0x55, 0xb4, 0x3a, 0x3f, 0x2d, 0x1e, 0x1b, 0xa5, 0x6c, 0x2c, 0xf4, 0x2d, - 0xd4, 0x94, 0x33, 0x75, 0x54, 0x8b, 0x72, 0x15, 0x45, 0x68, 0xa2, 0xdb, - 0x73, 0x38, 0x9d, 0x34, 0xc0, 0x49, 0x63, 0x28, 0x4e, 0xe2, 0xa2, 0xf2, - 0xc2, 0x1e, 0x44, 0x66, 0x83, 0x23, 0x02, 0xc4, 0x12, 0xd9, 0x9a, 0x1a, - 0x28, 0x5c, 0x64, 0x34, 0x62, 0x2c, 0xd2, 0x40, 0x1a, 0xae, 0x80, 0xa4, - 0x82, 0xb7, 0xab, 0x35, 0x7c, 0xa3, 0x24, 0xb2, 0x2b, 0x15, 0x03, 0x72, - 0x86, 0xab, 0x19, 0x19, 0x55, 0xb2, 0x9b, 0xf5, 0x4c, 0x94, 0x05, 0x4f, - 0x15, 0x41, 0x64, 0x4f, 0xcb, 0x3a, 0x54, 0xad, 0xd5, 0x84, 0x54, 0x77, - 0xee, 0x9f, 0x29, 0xf9, 0x89, 0x2b, 0x4f, 0xd0, 0xef, 0x68, 0x33, 0xce, - 0xba, 0x4d, 0x42, 0x03, 0x54, 0x70, 0x70, 0x94, 0xc7, 0x2d, 0x5a, 0x24, - 0x51, 0x61, 0x46, 0x68, 0xda, 0x2a, 0x1a, 0xc5, 0x2b, 0x41, 0x18, 0x7c, - 0xe8, 0x36, 0x0f, 0x94, 0x33, 0xa5, 0x6e, 0x79, 0xf7, 0xbe, 0x33, 0x8c, - 0xb9, 0xeb, 0xce, 0x79, 0xf7, 0xfd, 0xf9, 0xf9, 0xbc, 0xad, 0x46, 0x7e, - 0x13, 0x51, 0x53, 0x04, 0x49, 0x7d, 0xe6, 0x9e, 0x28, 0x00, 0x98, 0x56, - 0x4b, 0xa6, 0x2b, 0x62, 0xfb, 0xde, 0x9a, 0x7d, 0x2c, 0x00, 0xf4, 0x00, - 0x12, 0x7c, 0xbe, 0xc1, 0xe9, 0x45, 0x60, 0x2b, 0x5f, 0x2a, 0xc6, 0x6a, - 0xa2, 0xaa, 0xe2, 0x18, 0x4d, 0x4e, 0xcf, 0x50, 0x81, 0x54, 0xdb, 0x6a, - 0xc6, 0x68, 0x4c, 0x4c, 0x42, 0x92, 0x35, 0x64, 0x1d, 0x53, 0x1a, 0x59, - 0x45, 0xf4, 0x05, 0x38, 0xa0, 0xb4, 0xab, 0xdc, 0x02, 0x04, 0x71, 0x4e, - 0xbe, 0x69, 0x44, 0x95, 0x22, 0xb6, 0x0f, 0x0d, 0xc1, 0x69, 0x6b, 0x08, - 0xb2, 0xe7, 0x74, 0x90, 0x8a, 0x3d, 0xe6, 0x0c, 0xa7, 0x05, 0x8a, 0xa8, - 0x57, 0x71, 0xbf, 0xff, 0xf1, 0x50, 0x80, 0x2e, 0x7f, 0xfc, 0x21, 0x1a, - 0xce, 0xb2, 0x0b, 0x12, 0x00, 0x00, 0x8d, 0xb2, 0x32, 0x2c, 0x28, 0x69, - 0x32, 0x1d, 0x92, 0x82, 0x06, 0xaf, 0xbe, 0x23, 0x37, 0xc4, 0xaf, 0x8f, - 0x4e, 0xff, 0x5a, 0x9f, 0xa7, 0xdf, 0xdb, 0xc7, 0xc7, 0x37, 0x9a, 0xbd, - 0xd6, 0xac, 0xa9, 0xdf, 0x21, 0x9e, 0xbb, 0xd2, 0xfe, 0x93, 0x48, 0xd5, - 0x35, 0x7a, 0xaf, 0x80, 0x9f, 0x22, 0xe5, 0xa1, 0x2f, 0x81, 0x98, 0x06, - 0xf0, 0x21, 0xa2, 0xa3, 0x15, 0xf1, 0xf0, 0xe3, 0x2d, 0xfe, 0xef, 0x03, - 0x28, 0x31, 0x5d, 0x7e, 0x1e, 0x18, 0xee, 0x27, 0x18, 0x39, 0xdf, 0x5d, - 0x81, 0x8d, 0xe3, 0xb6, 0xb2, 0xad, 0x94, 0xa3, 0x16, 0x39, 0x99, 0x28, - 0xae, 0xf7, 0x87, 0x21, 0xb4, 0x44, 0x66, 0x38, 0xde, 0xd9, 0x74, 0xa8, - 0x1e, 0xad, 0x43, 0xbb, 0xbb, 0xbe, 0x36, 0x85, 0x7e, 0xb7, 0x3a, 0x4e, - 0x60, 0x04, 0x42, 0xb5, 0xcf, 0x8c, 0xd2, 0xb4, 0x03, 0xc3, 0x3c, 0x8d, - 0x0f, 0xc3, 0x4f, 0xac, 0xc5, 0x2b, 0x55, 0xad, 0xbb, 0x1c, 0x32, 0x5f, - 0xea, 0x0c, 0x52, 0x11, 0xfd, 0xe4, 0xe4, 0xc8, 0xaa, 0x55, 0x34, 0x28, - 0xcb, 0x15, 0xb0, 0x57, 0x14, 0x9a, 0x80, 0x11, 0x4e, 0x2c, 0x4a, 0xd0, - 0x4c, 0xcd, 0x0e, 0x2a, 0x94, 0x22, 0xd0, 0x02, 0x02, 0x20, 0xa2, 0xc3, - 0x6c, 0xd3, 0xd2, 0x6b, 0x3d, 0xb0, 0x6e, 0x21, 0x76, 0x16, 0xce, 0x2b, - 0x83, 0x08, 0x38, 0xc3, 0x0d, 0xf6, 0x21, 0xd2, 0xa7, 0x20, 0x3a, 0x15, - 0x2e, 0xc5, 0x36, 0x03, 0x90, 0x15, 0xe6, 0xf3, 0xbe, 0x2e, 0x6f, 0x43, - 0xa4, 0x8a, 0x52, 0x83, 0xc5, 0x91, 0x3b, 0x43, 0xc8, 0x95, 0xbd, 0x26, - 0xb5, 0x41, 0x9c, 0xee, 0x44, 0x6f, 0x7c, 0x01, 0xd2, 0x3d, 0xa0, 0x81, - 0x1a, 0x22, 0x83, 0xce, 0xa5, 0x22, 0xca, 0x58, 0xda, 0x2a, 0x1e, 0xc2, - 0xae, 0xf4, 0xa0, 0xbc, 0xa7, 0x3c, 0x73, 0xed, 0x7d, 0xef, 0xe3, 0xbc, - 0xcd, 0x4e, 0x2e, 0x9f, 0x3e, 0x7a, 0xeb, 0xed, 0xfb, 0xdf, 0xf4, 0xbc, - 0x44, 0x56, 0xbf, 0x9b, 0xca, 0xbd, 0xc1, 0x10, 0x2d, 0x75, 0x3a, 0xad, - 0xc5, 0xc2, 0xa6, 0xc7, 0x0e, 0x15, 0xd6, 0x78, 0x30, 0xda, 0x25, 0xfe, - 0xec, 0x0a, 0xf6, 0x7a, 0xfa, 0x37, 0x94, 0x18, 0x38, 0xb1, 0xee, 0x88, - 0x8d, 0x53, 0x12, 0x80, 0x33, 0xf5, 0xfd, 0x80, 0x46, 0xca, 0xcd, 0x17, - 0xaa, 0xd2, 0xb1, 0x8b, 0x40, 0xb9, 0x05, 0xce, 0xe9, 0xc3, 0xe5, 0x9d, - 0xd4, 0x4c, 0xdf, 0x55, 0x53, 0x3d, 0x79, 0x2b, 0x00, 0x03, 0x3a, 0xc9, - 0xa3, 0xdd, 0x9b, 0x38, 0x2c, 0x32, 0xb7, 0x37, 0x29, 0xf4, 0x7b, 0x77, - 0x7a, 0xdc, 0xff, 0xf1, 0x50, 0x80, 0x2c, 0xdf, 0xfc, 0x21, 0x1a, 0xcc, - 0xb2, 0x83, 0x05, 0x00, 0x00, 0x8e, 0xb2, 0x32, 0x2c, 0x2a, 0xe4, 0x53, - 0x0d, 0x06, 0x23, 0x4c, 0xd1, 0xbc, 0xeb, 0xf5, 0xfd, 0x3f, 0x5f, 0xdb, - 0xe3, 0xf4, 0xfd, 0xe6, 0xbf, 0xb7, 0xf3, 0xaf, 0xcf, 0xf8, 0xcc, 0x6b, - 0x2b, 0x59, 0xa9, 0x37, 0x4f, 0x41, 0x9f, 0x81, 0xf2, 0x2e, 0xb1, 0x79, - 0xfc, 0xc4, 0x50, 0x2c, 0x10, 0x29, 0x37, 0xd2, 0x6d, 0x45, 0xcc, 0xc2, - 0x6e, 0x41, 0xc8, 0x81, 0x63, 0x05, 0x1c, 0x3f, 0x5b, 0x97, 0xcf, 0x1c, - 0x85, 0xc7, 0x47, 0xc3, 0x3d, 0xfc, 0xe0, 0x45, 0x66, 0xab, 0x5a, 0xfa, - 0xbe, 0x80, 0x89, 0xbe, 0x98, 0xc3, 0x53, 0xa9, 0x3e, 0x15, 0x39, 0xe3, - 0xd1, 0x8b, 0x92, 0xbd, 0x3e, 0x00, 0xe1, 0x01, 0xd9, 0xa9, 0x8e, 0xbd, - 0xf3, 0xba, 0x26, 0x33, 0x10, 0x76, 0x00, 0x3e, 0x11, 0xe3, 0x73, 0xbe, - 0x70, 0x62, 0xf9, 0x49, 0x72, 0xfc, 0xb7, 0x87, 0x4d, 0xab, 0xe8, 0x37, - 0x13, 0xe9, 0xab, 0x91, 0xce, 0xbe, 0x59, 0xdb, 0xb9, 0xfa, 0x8f, 0x45, - 0x61, 0xdf, 0x93, 0x7b, 0x88, 0x3c, 0xa2, 0x38, 0x83, 0x64, 0x6f, 0xa0, - 0xdb, 0x68, 0x7e, 0x81, 0xe4, 0x16, 0xe0, 0x9b, 0xb0, 0x51, 0x8e, 0x08, - 0x22, 0xa7, 0xe5, 0x82, 0xc7, 0x01, 0xde, 0xf1, 0xee, 0x67, 0x28, 0xe0, - 0x41, 0x1f, 0x60, 0xbb, 0xe3, 0x76, 0x13, 0x75, 0xdb, 0x4c, 0x02, 0xb6, - 0xfd, 0xc6, 0x70, 0xa8, 0x2a, 0x89, 0xa8, 0x9a, 0x62, 0x56, 0xb6, 0xd1, - 0x08, 0x5a, 0xb7, 0xaa, 0x0b, 0x20, 0xf4, 0x54, 0x61, 0x24, 0x18, 0x3b, - 0xe7, 0x8a, 0x28, 0x17, 0x19, 0xae, 0x99, 0x18, 0xac, 0xd0, 0xf1, 0x82, - 0x5c, 0x5c, 0x22, 0x62, 0x3e, 0x8e, 0x96, 0x0f, 0xfe, 0x0e, 0x07, 0xed, - 0x36, 0x7a, 0xc5, 0xc5, 0x9d, 0x78, 0xeb, 0x1a, 0xa6, 0xc2, 0xae, 0xf4, - 0x90, 0x7c, 0xce, 0x5d, 0x77, 0xf0, 0xe5, 0xd7, 0x33, 0xc7, 0x9a, 0xbf, - 0x6f, 0x1f, 0x1f, 0xd3, 0xda, 0x79, 0xf7, 0xfd, 0x6b, 0xd5, 0xb7, 0xa5, - 0x49, 0x5e, 0x64, 0xdd, 0xe5, 0x00, 0x41, 0xfb, 0x36, 0xc9, 0x7e, 0x11, - 0xfb, 0xfe, 0xb8, 0x4a, 0xfc, 0x66, 0xfb, 0x33, 0xb9, 0x67, 0x91, 0x52, - 0x6e, 0x45, 0xba, 0x45, 0x9c, 0xf9, 0xaa, 0xbb, 0x2b, 0x7b, 0xd6, 0x76, - 0x8e, 0x84, 0xdc, 0x86, 0x62, 0xa2, 0xa3, 0xcf, 0x98, 0x54, 0x26, 0x4b, - 0x06, 0x31, 0xd2, 0xc4, 0x5d, 0x8b, 0xdc, 0x82, 0xd2, 0x66, 0xf6, 0x4b, - 0x58, 0x65, 0x86, 0xc6, 0xc0, 0x0b, 0x11, 0x34, 0x69, 0xc4, 0x75, 0xf0, - 0xff, 0xf1, 0x50, 0x80, 0x2e, 0x9f, 0xfc, 0x21, 0x1a, 0xcd, 0x5d, 0xfa, - 0xed, 0x80, 0x00, 0x8e, 0xa2, 0xb1, 0xec, 0x2a, 0xe4, 0x43, 0x15, 0x06, - 0x1a, 0xd3, 0x17, 0x37, 0xcf, 0x9b, 0xcf, 0xc7, 0xe5, 0xdf, 0xd3, 0xf1, - 0xfd, 0xbf, 0xc5, 0xff, 0x1f, 0xcf, 0xad, 0x73, 0xe5, 0x52, 0xeb, 0x47, - 0xaa, 0xa0, 0x8f, 0xe7, 0x77, 0x58, 0x7f, 0x1f, 0x4b, 0xd1, 0x0b, 0xa6, - 0x38, 0x2d, 0x1d, 0xb5, 0xbd, 0xb3, 0x45, 0xe6, 0x29, 0x50, 0x84, 0x9c, - 0x41, 0xbb, 0x26, 0x3e, 0x9a, 0xec, 0x15, 0x4d, 0xc3, 0x51, 0xbe, 0xcf, - 0x46, 0x27, 0x80, 0x75, 0x10, 0xbe, 0xff, 0xa8, 0x1a, 0x8c, 0x6b, 0x6e, - 0x2b, 0x4a, 0xa7, 0xdf, 0xdb, 0xdd, 0xd9, 0x9e, 0x1c, 0xc7, 0x60, 0x17, - 0x10, 0xf7, 0x5b, 0x3a, 0xee, 0xf3, 0xbd, 0x77, 0x67, 0xaf, 0x15, 0xf2, - 0x9a, 0xeb, 0x3b, 0x40, 0x1d, 0x58, 0xd3, 0xf8, 0xd6, 0x3a, 0x53, 0x17, - 0x1b, 0xab, 0x1d, 0x4f, 0x64, 0x12, 0xf6, 0x47, 0x3a, 0xa4, 0x6c, 0xa9, - 0x75, 0x7b, 0xf0, 0x0c, 0x9d, 0x54, 0x88, 0x97, 0xec, 0x7d, 0x95, 0xe9, - 0x00, 0x76, 0x1c, 0xf7, 0xa2, 0x79, 0x70, 0xf1, 0x7c, 0x8a, 0x0f, 0xad, - 0x87, 0x34, 0xa8, 0x1b, 0x7b, 0x52, 0x72, 0xfb, 0xe8, 0x66, 0xc4, 0x5f, - 0x49, 0xbf, 0xa0, 0x19, 0x62, 0xf4, 0x58, 0xca, 0xa9, 0x78, 0xde, 0xa6, - 0x61, 0x66, 0xe0, 0x04, 0x42, 0x56, 0x13, 0x95, 0xbc, 0x1a, 0x0e, 0x69, - 0x12, 0xe1, 0xa1, 0xe1, 0xa6, 0xb2, 0x27, 0x42, 0x83, 0xd2, 0x69, 0x76, - 0x64, 0x71, 0x29, 0x5e, 0x94, 0x0c, 0x13, 0x5b, 0x3c, 0x87, 0x18, 0xd7, - 0xaa, 0xaa, 0x2a, 0x64, 0xc2, 0x01, 0xc6, 0xa2, 0xf4, 0x92, 0x85, 0xf7, - 0x4e, 0xdd, 0x50, 0x71, 0xf6, 0x88, 0xa6, 0xcd, 0xb8, 0x8b, 0x04, 0x8e, - 0xb2, 0x31, 0x88, 0x56, 0x15, 0x7f, 0xa1, 0x03, 0xe6, 0x54, 0xae, 0x2e, - 0x62, 0xe6, 0xf9, 0xf3, 0x79, 0xf1, 0xef, 0x7e, 0xbe, 0x97, 0xef, 0xfe, - 0x3b, 0xf5, 0xfc, 0xdd, 0x52, 0x48, 0xf0, 0xc9, 0x88, 0x01, 0xea, 0x4d, - 0x14, 0x6e, 0x07, 0x34, 0x08, 0x1f, 0x78, 0x07, 0x72, 0x16, 0x93, 0xbc, - 0xb2, 0xa9, 0x1c, 0x9c, 0x59, 0x9a, 0x5a, 0xb7, 0x1a, 0x1e, 0x65, 0xe6, - 0x22, 0x26, 0xfb, 0x67, 0xdb, 0xba, 0xef, 0xe3, 0x41, 0xba, 0x93, 0xd3, - 0xfa, 0xf8, 0x40, 0xad, 0xee, 0x64, 0xc2, 0x51, 0x38, 0xe8, 0x8a, 0xee, - 0xda, 0xc3, 0xed, 0x80, 0x77, 0x30, 0xeb, 0xdf, 0x86, 0xa7, 0x9e, 0xe3, - 0x9e, 0x26, 0x35, 0x42, 0x5b, 0xd0, 0x03, 0x7d, 0xfc, 0xeb, 0x17, 0x4b, - 0xee, 0xcc, 0xcf, 0x4b, 0xf2, 0x16, 0xb4, 0x63, 0xb8, 0x6e, 0x8c, 0xe0, - 0xff, 0xf1, 0x50, 0x80, 0x2c, 0x7f, 0xfc, 0x21, 0x1a, 0xcf, 0x2a, 0xab, - 0x9f, 0x00, 0x00, 0x8c, 0xb2, 0x2a, 0x2c, 0x2e, 0x82, 0x2a, 0x8d, 0x12, - 0xc2, 0x21, 0x83, 0x8c, 0xab, 0x8e, 0x5a, 0x4f, 0xc7, 0xe9, 0x39, 0xfd, - 0x7e, 0x7f, 0x1f, 0x3e, 0xda, 0xfb, 0x7e, 0xff, 0x6f, 0x8e, 0x6e, 0xb5, - 0x57, 0x35, 0x39, 0xaf, 0x14, 0x07, 0xe0, 0xad, 0xcf, 0xdf, 0x47, 0xfc, - 0xb2, 0x8d, 0x07, 0xad, 0x1d, 0xd1, 0xc7, 0xf4, 0x45, 0xc6, 0x3a, 0x64, - 0x93, 0xcc, 0x6b, 0x1d, 0x16, 0x9d, 0xd9, 0xe6, 0xb5, 0x6c, 0x92, 0xb3, - 0xb9, 0xe4, 0xb2, 0xc3, 0x74, 0x82, 0xc5, 0x79, 0x3e, 0x88, 0x65, 0x55, - 0x86, 0xc3, 0xc0, 0x1d, 0xe9, 0x79, 0xe4, 0x19, 0xcc, 0x16, 0x31, 0x3e, - 0x8f, 0x18, 0x29, 0x55, 0x95, 0x6b, 0x00, 0x0e, 0x0e, 0x79, 0x56, 0xa7, - 0xde, 0x9b, 0x9a, 0x50, 0x5c, 0xd9, 0x72, 0x92, 0xec, 0x7c, 0x7b, 0x9b, - 0x89, 0x21, 0x7d, 0x75, 0xec, 0x16, 0xf8, 0x27, 0xa8, 0x7c, 0xb3, 0x69, - 0x2a, 0xc2, 0x6a, 0xa4, 0xc6, 0xe0, 0x0c, 0x28, 0xb6, 0x33, 0x3e, 0xcc, - 0x33, 0x3d, 0x20, 0x66, 0xea, 0x60, 0x9d, 0xdd, 0x7f, 0xe3, 0xb9, 0x84, - 0x00, 0x5a, 0xa4, 0x82, 0xc8, 0x93, 0xbb, 0xc1, 0xb7, 0xcb, 0x0f, 0x5d, - 0x79, 0x51, 0xff, 0x93, 0xd0, 0xfd, 0xa0, 0x00, 0x8f, 0x1d, 0x0d, 0x56, - 0xb1, 0xd2, 0x7a, 0x50, 0x21, 0x0d, 0x1a, 0x14, 0x4c, 0x02, 0xf6, 0x26, - 0xba, 0x49, 0x21, 0x10, 0xd7, 0xaf, 0x19, 0x2e, 0xf9, 0x7c, 0xe5, 0x68, - 0xa8, 0xb5, 0x62, 0xf7, 0x85, 0xea, 0x38, 0xe6, 0x44, 0x42, 0x32, 0x68, - 0x83, 0x32, 0xfa, 0x10, 0x3e, 0x14, 0x0f, 0x90, 0x6e, 0xbe, 0x3e, 0xbf, - 0x46, 0x9c, 0xf7, 0xf1, 0x75, 0xc7, 0x73, 0x9e, 0xbc, 0xeb, 0xe7, 0xdb, - 0x5f, 0x6f, 0x8d, 0xf5, 0x96, 0x2f, 0x2b, 0xaa, 0x64, 0x0e, 0x77, 0xcb, - 0xbd, 0x43, 0x0e, 0xab, 0x5f, 0xbe, 0xf1, 0xe3, 0xa6, 0xd5, 0xc5, 0xcf, - 0xd0, 0x89, 0xac, 0x77, 0xe8, 0x52, 0xce, 0xc3, 0xda, 0x3f, 0x31, 0xe1, - 0x11, 0x8e, 0x9c, 0xe5, 0x75, 0x96, 0x1b, 0x6a, 0x75, 0x73, 0x80, 0xa0, - 0xdf, 0xe8, 0xfe, 0x32, 0x85, 0x5e, 0xee, 0xfe, 0xa5, 0x02, 0xa6, 0x4b, - 0x43, 0x58, 0x7c, 0xe3, 0x4e, 0x27, 0x16, 0xc3, 0xe1, 0xbe, 0xf4, 0xdf, - 0x38, 0x3a, 0x67, 0x3f, 0x27, 0x8c, 0x13, 0xac, 0xba, 0xef, 0xfb, 0xda, - 0xfc, 0x4e, 0xa0, 0xcb, 0xf4, 0x4a, 0x31, 0xa8, 0x25, 0x3d, 0xd4, 0x7b, - 0xfa, 0xd4, 0x8f, 0x1b, 0x67, 0x55, 0x70, 0xff, 0xf1, 0x50, 0x80, 0x30, - 0xff, 0xfc, 0x21, 0x1a, 0xcf, 0x38, 0x88, 0x19, 0x00, 0x00, 0x8c, 0xb2, - 0x32, 0x2c, 0x2a, 0xb6, 0x11, 0x85, 0x82, 0x87, 0x62, 0xa0, 0xc2, 0xf8, - 0xad, 0xf1, 0x53, 0x7b, 0xf2, 0xaf, 0xc7, 0xcd, 0x78, 0xff, 0x4f, 0xb7, - 0xd7, 0xdb, 0xf9, 0x7e, 0xdf, 0xe3, 0xe7, 0xaf, 0x1c, 0x35, 0x9c, 0x6f, - 0xce, 0xeb, 0x9c, 0x81, 0x1c, 0x16, 0xd2, 0x27, 0xe4, 0x9f, 0x98, 0x45, - 0xfd, 0x7f, 0x61, 0x07, 0x70, 0x7b, 0xe9, 0x0b, 0x8d, 0xe4, 0x24, 0x71, - 0x10, 0xf0, 0xa8, 0x16, 0x71, 0xe9, 0x2a, 0xbd, 0x55, 0x6f, 0x52, 0xc1, - 0xde, 0xf8, 0xe7, 0xdf, 0x53, 0x31, 0x68, 0x8b, 0xd5, 0x2f, 0xcb, 0xf7, - 0xe0, 0x6b, 0xbf, 0xbb, 0xb7, 0x3c, 0xdd, 0x72, 0x2b, 0x11, 0xd1, 0xce, - 0x15, 0x38, 0x27, 0x50, 0x06, 0xc6, 0x83, 0x9b, 0xc1, 0xac, 0x31, 0xb7, - 0x49, 0xd0, 0x14, 0x98, 0xd8, 0xaf, 0x4c, 0xbb, 0x80, 0x92, 0xc4, 0x99, - 0x03, 0xe3, 0x18, 0x0a, 0xa5, 0xaf, 0x15, 0xdd, 0x52, 0xdd, 0x97, 0xa6, - 0x90, 0xe0, 0x6f, 0x22, 0x17, 0xc8, 0x3f, 0x7e, 0xef, 0x9f, 0xfd, 0x4b, - 0xb3, 0x35, 0x8a, 0xd7, 0x19, 0xf9, 0xb4, 0x82, 0x72, 0x26, 0x7f, 0x0d, - 0x43, 0x16, 0x89, 0x4c, 0x33, 0xd4, 0x7e, 0x99, 0xd0, 0x5a, 0x3b, 0xe6, - 0x87, 0xd5, 0x99, 0x04, 0x06, 0x7e, 0xf9, 0xd6, 0xae, 0x39, 0x30, 0x9d, - 0x55, 0x30, 0x12, 0xf3, 0x83, 0xc4, 0xa8, 0x0b, 0x91, 0xe1, 0xda, 0x05, - 0x80, 0x24, 0x01, 0x05, 0x94, 0x6a, 0x14, 0x7c, 0xcd, 0xab, 0x94, 0xa3, - 0x7f, 0x36, 0x99, 0xeb, 0x0b, 0x42, 0xab, 0x49, 0x2a, 0x56, 0xd1, 0x35, - 0x42, 0x41, 0x9a, 0x5b, 0x41, 0x02, 0xb1, 0x55, 0x3b, 0x49, 0xbe, 0x8f, - 0x25, 0x82, 0x36, 0x93, 0xc3, 0x72, 0x66, 0x02, 0x05, 0xde, 0x1f, 0xc2, - 0xd5, 0x04, 0x51, 0xc5, 0xde, 0x16, 0x03, 0x19, 0x05, 0x43, 0xd8, 0x5d, - 0x68, 0x4f, 0x39, 0x09, 0x43, 0xe5, 0x1d, 0xbf, 0x15, 0xbd, 0xdd, 0x6b, - 0xdf, 0xab, 0xd7, 0xb7, 0xe6, 0x7a, 0xf8, 0xf3, 0xe7, 0xed, 0xed, 0x7e, - 0xfe, 0xd5, 0x7b, 0xbb, 0x4c, 0xab, 0xc2, 0xac, 0x28, 0xff, 0x5f, 0x98, - 0x66, 0xeb, 0xa9, 0x54, 0x1e, 0xc8, 0xba, 0xbe, 0x13, 0x8d, 0x57, 0xee, - 0xaa, 0x4a, 0x11, 0xd4, 0xfe, 0x2a, 0x47, 0x44, 0x44, 0xae, 0xe1, 0x8d, - 0xf3, 0x63, 0x5b, 0x81, 0x41, 0xbb, 0xa3, 0xd5, 0xc8, 0xa6, 0x35, 0x51, - 0x70, 0x98, 0x9c, 0xad, 0xa2, 0xce, 0x2f, 0x4a, 0xcb, 0xf4, 0x80, 0x68, - 0x4a, 0x76, 0x72, 0x77, 0x67, 0xce, 0x29, 0xa7, 0xa3, 0x48, 0xec, 0xbb, - 0xbb, 0x99, 0x1d, 0x56, 0x22, 0x62, 0x7d, 0x79, 0x6b, 0x0f, 0x76, 0xee, - 0x89, 0x9f, 0x66, 0x9d, 0xed, 0x7d, 0xb0, 0x64, 0xc5, 0x66, 0x44, 0x7c, - 0xc2, 0xf8, 0xff, 0xf1, 0x50, 0x80, 0x2a, 0x5f, 0xfc, 0x21, 0x1a, 0xc9, - 0xba, 0x7a, 0x1d, 0x00, 0x00, 0x8e, 0xa2, 0x32, 0x28, 0x26, 0xb6, 0x11, - 0x85, 0x1a, 0x43, 0x12, 0x70, 0xdd, 0xcd, 0x78, 0xdf, 0x51, 0xfb, 0xfe, - 0xd7, 0xef, 0xfe, 0x3d, 0x7c, 0x7e, 0xdf, 0xce, 0xbf, 0x3f, 0xbf, 0x6e, - 0xfc, 0xd0, 0xe1, 0x2b, 0xbc, 0xd8, 0x89, 0xa7, 0xf4, 0x85, 0xbc, 0x6e, - 0xcb, 0x20, 0xf2, 0x50, 0xc4, 0x47, 0xf0, 0x25, 0x28, 0xc8, 0x6c, 0xe0, - 0x01, 0xbc, 0x50, 0xa8, 0x5c, 0xfc, 0x3e, 0xec, 0xa9, 0xa9, 0x1e, 0xdb, - 0xf5, 0xde, 0x7f, 0xb5, 0xd8, 0xb0, 0xa7, 0xff, 0x18, 0xe9, 0x3a, 0xbf, - 0x8d, 0xbc, 0x17, 0xf2, 0x0d, 0x4d, 0x87, 0xe6, 0x04, 0x39, 0x30, 0xad, - 0x02, 0x0b, 0x1c, 0x19, 0x5e, 0x11, 0x28, 0x58, 0x09, 0xf7, 0x42, 0xe9, - 0xef, 0x84, 0xf2, 0xff, 0x92, 0xfb, 0x82, 0xb0, 0xaf, 0x55, 0x5d, 0x16, - 0xa6, 0x53, 0xf4, 0x95, 0x9a, 0x3b, 0x56, 0x86, 0x0a, 0xc2, 0xa0, 0xaa, - 0x0b, 0x53, 0x87, 0x3f, 0xe4, 0x94, 0x59, 0x01, 0x1c, 0xc4, 0x95, 0xb9, - 0xe8, 0x59, 0x69, 0x97, 0x7e, 0x50, 0x8e, 0xe6, 0x73, 0x18, 0x74, 0xf8, - 0x54, 0xb4, 0x14, 0xf3, 0x60, 0x5b, 0x9d, 0xad, 0x7a, 0xda, 0x30, 0xf3, - 0xff, 0xb0, 0x4f, 0x5a, 0x09, 0xcb, 0x96, 0x29, 0x7d, 0x1b, 0x65, 0xd8, - 0x82, 0x38, 0xf2, 0x86, 0x1b, 0x4b, 0xc5, 0xfc, 0x28, 0xb9, 0x48, 0xdd, - 0x4b, 0x52, 0xa2, 0x17, 0xac, 0x43, 0x96, 0x7c, 0xe1, 0x10, 0xb0, 0x96, - 0x76, 0xa1, 0xa9, 0x0e, 0x38, 0xff, 0x45, 0x4d, 0x24, 0xc5, 0x2a, 0x97, - 0x21, 0x55, 0x21, 0x62, 0x99, 0x21, 0x8c, 0x5e, 0x82, 0x0f, 0x98, 0xb9, - 0xbf, 0x86, 0xf3, 0xac, 0xdd, 0x75, 0x1a, 0xdf, 0x5f, 0xb7, 0xeb, 0xd4, - 0xfd, 0xbf, 0x9c, 0xf1, 0xe6, 0xdc, 0xdc, 0x86, 0xe5, 0x2a, 0x80, 0x1d, - 0x96, 0xf0, 0x10, 0xc6, 0xac, 0xf2, 0x3a, 0x38, 0x5b, 0x7e, 0x4a, 0xe6, - 0x5b, 0x19, 0xdf, 0x54, 0xe5, 0xe2, 0xe7, 0x7f, 0x51, 0xe4, 0x30, 0xa3, - 0x02, 0xe1, 0x7b, 0x32, 0x61, 0x5b, 0xe4, 0x33, 0x93, 0x29, 0xcf, 0xc9, - 0x48, 0x3a, 0x93, 0x3e, 0xe7, 0xa2, 0x28, 0x49, 0x61, 0x90, 0x5e, 0x7e, - 0x24, 0xa7, 0x7a, 0xe5, 0x4a, 0x32, 0x06, 0x87, 0x60, 0x4a, 0x8e, 0x94, - 0xd7, 0x80, 0x4f, 0x91, 0xe9, 0x9a, 0x3a, 0x73, 0xd0, 0x83, 0x3f, 0x83, - 0x6c, 0x39, 0xee, 0xe0, 0xff, 0xf1, 0x50, 0x80, 0x32, 0x3f, 0xfc, 0x21, - 0x1a, 0xcb, 0x48, 0x93, 0x3d, 0x80, 0x20, 0x8b, 0xb2, 0x32, 0x2c, 0x2c, - 0x84, 0x23, 0x04, 0x42, 0x8a, 0x61, 0xa0, 0xc2, 0xf4, 0xc9, 0xc3, 0xbd, - 0xfb, 0x5e, 0x7c, 0x7c, 0xf9, 0xfc, 0xff, 0x3b, 0x7c, 0xfe, 0x3d, 0xbf, - 0xaf, 0xe3, 0x7a, 0xac, 0x9a, 0xdd, 0xce, 0x2b, 0x33, 0xc5, 0x03, 0x35, - 0x64, 0xac, 0x2f, 0xea, 0xac, 0xe7, 0xf7, 0xca, 0x0f, 0x19, 0xc7, 0x00, - 0xdb, 0x69, 0x0e, 0x91, 0x95, 0x05, 0xe0, 0x0b, 0xc9, 0xc4, 0x20, 0xf6, - 0x71, 0xe3, 0xd3, 0x01, 0xb0, 0x9c, 0x4a, 0x2d, 0x05, 0xd3, 0x21, 0x66, - 0x40, 0x40, 0x03, 0x4b, 0x41, 0xb6, 0xee, 0x57, 0x10, 0x00, 0x06, 0x04, - 0xf1, 0x55, 0x29, 0xcf, 0x76, 0xbc, 0xeb, 0xa1, 0x4a, 0x6d, 0xff, 0x05, - 0xea, 0x77, 0x3a, 0x45, 0x28, 0x83, 0xa1, 0xc6, 0x6a, 0x71, 0xac, 0xf9, - 0x29, 0x46, 0x32, 0xc0, 0x07, 0x1b, 0xb3, 0x11, 0x97, 0xf1, 0x97, 0x05, - 0x79, 0xe7, 0x2f, 0x00, 0x61, 0xe7, 0xfd, 0x43, 0xcb, 0x1a, 0x9b, 0xed, - 0xcc, 0x0d, 0xac, 0x6b, 0xe2, 0x5d, 0x24, 0x23, 0x7d, 0xfc, 0xca, 0x63, - 0xbf, 0x21, 0xbb, 0xd1, 0x17, 0x6a, 0x6e, 0x5c, 0xfa, 0xb0, 0xfd, 0x1f, - 0x17, 0xb6, 0xe2, 0x9f, 0x90, 0xd1, 0x46, 0x0d, 0x31, 0xb7, 0x7a, 0x5c, - 0x49, 0x0f, 0x7e, 0x07, 0x53, 0xba, 0xa0, 0x87, 0xce, 0x63, 0x8f, 0x84, - 0x8b, 0xfb, 0xd0, 0x7f, 0x32, 0x2b, 0xd1, 0x4a, 0xe0, 0x2f, 0x56, 0xfe, - 0x7e, 0xab, 0xf0, 0x3d, 0x46, 0x35, 0x2d, 0x76, 0x5d, 0x13, 0x53, 0x70, - 0x5c, 0xa7, 0xd8, 0xf5, 0x35, 0xd2, 0x80, 0x67, 0xd4, 0x0e, 0xe4, 0xb2, - 0x45, 0xaa, 0x1c, 0xc6, 0x94, 0x94, 0xe5, 0x60, 0x8d, 0x0a, 0x50, 0xdf, - 0x81, 0x7b, 0x42, 0x8c, 0x3d, 0x76, 0x6a, 0x16, 0x5f, 0xb8, 0x66, 0xa3, - 0x9c, 0xbe, 0xce, 0x26, 0x2b, 0x48, 0x58, 0x99, 0x56, 0x14, 0x32, 0xa7, - 0xce, 0x41, 0xf0, 0xb0, 0x7c, 0xaf, 0x8d, 0xf7, 0xec, 0x8c, 0xd5, 0x3d, - 0xfe, 0x3c, 0xf8, 0xf3, 0xbf, 0x3f, 0xdb, 0xf7, 0xe1, 0xf3, 0xf8, 0xcf, - 0x1f, 0x85, 0xcd, 0xcb, 0xaf, 0x77, 0xee, 0x32, 0x03, 0x28, 0xcf, 0x45, - 0xe5, 0x70, 0xec, 0xf6, 0xe9, 0x43, 0x1c, 0x13, 0x9f, 0x01, 0xbe, 0x43, - 0xac, 0xc3, 0x04, 0x01, 0x61, 0x31, 0xb9, 0x95, 0x01, 0x21, 0x68, 0x2b, - 0x3c, 0x72, 0x98, 0xee, 0x3f, 0x27, 0x7f, 0x93, 0xbc, 0xa5, 0x1c, 0x5f, - 0xe1, 0xfe, 0x24, 0x6e, 0x37, 0x1c, 0x1c, 0xb9, 0x5a, 0xb7, 0x7d, 0xb5, - 0x3b, 0xb2, 0x70, 0x57, 0x1e, 0x40, 0x54, 0x5b, 0x87, 0x46, 0xa4, 0x97, - 0xc6, 0xeb, 0x7a, 0xdc, 0xd5, 0xaf, 0xa0, 0x01, 0xbd, 0x65, 0xd0, 0xa8, - 0xec, 0xe7, 0xbc, 0x73, 0xac, 0xdd, 0x42, 0xb4, 0x56, 0xac, 0x20, 0xb1, - 0x8c, 0x58, 0x82, 0x65, 0xef, 0x55, 0xdd, 0xa9, 0x38, 0xff, 0xf1, 0x50, - 0x80, 0x33, 0x3f, 0xfc, 0x21, 0x1a, 0xcf, 0x3e, 0x22, 0x15, 0x00, 0x00, - 0x8d, 0xb1, 0xb2, 0x25, 0x2c, 0x35, 0x0b, 0x08, 0x42, 0xc3, 0x41, 0xb0, - 0x91, 0x02, 0x6a, 0xe7, 0x3a, 0x9a, 0xf5, 0xbf, 0x6f, 0xf1, 0xfb, 0x7e, - 0xbf, 0xd6, 0x78, 0xff, 0x12, 0x7f, 0x1f, 0xe9, 0xaf, 0xe9, 0xfe, 0x9e, - 0xaf, 0x0e, 0x9c, 0xf0, 0x9b, 0xcd, 0xe0, 0x65, 0xb9, 0x3e, 0x97, 0xd5, - 0x2e, 0x3c, 0x37, 0x62, 0x6c, 0xaa, 0x34, 0x22, 0x7d, 0x1b, 0x98, 0xeb, - 0x86, 0x17, 0xa2, 0x9c, 0x89, 0x77, 0x5f, 0xd1, 0x7b, 0xee, 0x62, 0xd5, - 0xd2, 0x74, 0xba, 0xf6, 0x68, 0xe3, 0xdd, 0xf4, 0xf4, 0x92, 0x27, 0x12, - 0x2a, 0xf7, 0xf3, 0x42, 0xe9, 0x0a, 0x53, 0x2b, 0x5d, 0x5d, 0x12, 0xb9, - 0x4a, 0xed, 0xf0, 0x88, 0x8e, 0x56, 0x7c, 0x41, 0x45, 0x0a, 0xca, 0x60, - 0xc5, 0x12, 0xef, 0x5a, 0x6c, 0xbd, 0x69, 0x5b, 0x14, 0x65, 0x84, 0x22, - 0x98, 0x49, 0xcc, 0x56, 0xee, 0xe2, 0x96, 0xcb, 0x84, 0x41, 0x01, 0xe3, - 0x1c, 0x44, 0x01, 0x09, 0x46, 0x62, 0x23, 0xbd, 0xea, 0x57, 0x51, 0x03, - 0x71, 0x59, 0xa2, 0xe4, 0x32, 0x50, 0x41, 0x52, 0xc2, 0x52, 0xd7, 0xe6, - 0x3d, 0x2d, 0x4a, 0x45, 0x91, 0xf4, 0xe9, 0x13, 0xe1, 0x4c, 0x85, 0x37, - 0x08, 0xbd, 0x54, 0xc5, 0xc2, 0x2b, 0x2d, 0x56, 0x99, 0x00, 0x8a, 0x14, - 0xc7, 0x7c, 0xca, 0xed, 0x3c, 0xbb, 0xb6, 0x33, 0xe5, 0x84, 0xf9, 0xfb, - 0x67, 0x54, 0xfe, 0x4f, 0xd2, 0xc7, 0xf1, 0x7d, 0xef, 0x4f, 0x9d, 0xed, - 0x39, 0xf8, 0xd0, 0x12, 0x3f, 0xb1, 0xe6, 0xd6, 0xfc, 0x6f, 0xc5, 0xe8, - 0xac, 0x68, 0x4f, 0x47, 0x79, 0xd0, 0xbc, 0xd0, 0x18, 0x6d, 0x58, 0xe1, - 0x8f, 0x88, 0x9c, 0xd8, 0xa9, 0x25, 0x27, 0x4b, 0x4d, 0x4d, 0xbd, 0xd0, - 0x3d, 0x90, 0xb5, 0x48, 0x20, 0xfb, 0xb3, 0x05, 0xda, 0xe9, 0x70, 0x0b, - 0x8d, 0x82, 0xa9, 0xec, 0x2e, 0x74, 0x47, 0x95, 0x05, 0xe1, 0x40, 0xf9, - 0x86, 0x6b, 0xa9, 0xce, 0xa6, 0x6f, 0x9f, 0xa6, 0x7e, 0x3e, 0xde, 0xdf, - 0xd7, 0xef, 0xaf, 0x6f, 0x5f, 0x5e, 0x3d, 0x7d, 0x5d, 0x4a, 0x94, 0x94, - 0xc9, 0x80, 0x74, 0x3b, 0x4f, 0x4d, 0xf4, 0xa3, 0xd5, 0x46, 0x21, 0xc9, - 0xc7, 0x0e, 0x7b, 0xba, 0xa9, 0xc7, 0x61, 0xfb, 0x20, 0xc5, 0xbb, 0x38, - 0xbc, 0x99, 0x33, 0x57, 0xc9, 0x8c, 0x23, 0x69, 0xa7, 0xa5, 0xc6, 0xce, - 0x75, 0x01, 0xa5, 0x43, 0xa6, 0x3d, 0x4c, 0x08, 0xfd, 0xca, 0x40, 0xac, - 0xb1, 0xd1, 0x7d, 0xe9, 0x38, 0x09, 0xc7, 0x4b, 0xd2, 0xcb, 0x9f, 0xf4, - 0xd0, 0x81, 0xfc, 0x87, 0x8c, 0x71, 0x73, 0x58, 0x5e, 0xd3, 0xd4, 0x9f, - 0x61, 0x77, 0x77, 0xb9, 0xa2, 0x75, 0x6f, 0xc4, 0xe2, 0xcb, 0x74, 0x4c, - 0x99, 0x0f, 0x2e, 0xb1, 0x7b, 0xc1, 0x8c, 0x1d, 0x3d, 0x33, 0xc8, 0xbc, - 0xaf, 0xd3, 0x5e, 0x32, 0x48, 0xa2, 0xe2, 0x42, 0x5f, 0x80, 0xff, 0xf1, - 0x50, 0x80, 0x2f, 0x5f, 0xfc, 0x21, 0x1a, 0xcf, 0x32, 0x2b, 0xf2, 0x80, - 0x00, 0x8d, 0xb1, 0xb2, 0x60, 0x28, 0xa5, 0x0a, 0x39, 0x82, 0x84, 0x0d, - 0x5c, 0xcb, 0x7b, 0x7d, 0xb7, 0xed, 0xae, 0x7f, 0x1f, 0x3a, 0xf9, 0xfa, - 0xe3, 0x8f, 0xef, 0xfe, 0xd5, 0xf9, 0xfc, 0x65, 0x66, 0xaa, 0x57, 0x12, - 0xb8, 0xef, 0x9d, 0xc0, 0xa1, 0xff, 0x69, 0x44, 0x30, 0x87, 0xd5, 0x29, - 0xa1, 0xea, 0x45, 0xd4, 0x5c, 0xb1, 0x3b, 0xdc, 0xe7, 0x98, 0x41, 0x8c, - 0x35, 0x8a, 0xbc, 0x5b, 0x50, 0xe6, 0x00, 0x08, 0x00, 0x7e, 0x7e, 0x37, - 0xce, 0xb2, 0x5d, 0xa1, 0x25, 0xe0, 0xe1, 0xdd, 0xde, 0xee, 0x5d, 0xf0, - 0xf5, 0x47, 0x4a, 0x2a, 0x61, 0xa3, 0xd7, 0x20, 0xec, 0xbe, 0x57, 0xe4, - 0x75, 0x11, 0x0a, 0x70, 0x71, 0x46, 0x38, 0x96, 0x0d, 0xbb, 0xbb, 0xa8, - 0x7e, 0x70, 0x08, 0x10, 0x64, 0xd1, 0xa1, 0x1d, 0xdc, 0x25, 0x28, 0x05, - 0xac, 0xd6, 0x18, 0x27, 0x3f, 0x51, 0x8c, 0xe2, 0x0d, 0x7b, 0xaa, 0x59, - 0x5c, 0x8a, 0x59, 0xe6, 0x6e, 0xb9, 0x9f, 0xaf, 0x41, 0x60, 0xb5, 0x46, - 0x5e, 0x49, 0x73, 0x96, 0x3d, 0xfc, 0xc1, 0x45, 0x8f, 0xec, 0x26, 0xbc, - 0x2a, 0xee, 0x38, 0x57, 0xaf, 0xe9, 0x76, 0xd2, 0x08, 0x6d, 0x21, 0x34, - 0xa9, 0x75, 0x1d, 0xba, 0x0c, 0xc8, 0x70, 0x5d, 0x52, 0x1a, 0x60, 0x6d, - 0x72, 0x84, 0x03, 0xd0, 0xd4, 0xce, 0x20, 0x22, 0x34, 0xe7, 0x50, 0x84, - 0x41, 0x54, 0x24, 0x6c, 0xaa, 0xf4, 0x2c, 0xcf, 0x0c, 0x82, 0x72, 0xa2, - 0x19, 0x49, 0xec, 0x32, 0xf5, 0x68, 0x66, 0xf4, 0x73, 0x9e, 0xb6, 0x11, - 0x31, 0x9c, 0x6c, 0x89, 0x95, 0x61, 0x53, 0xa1, 0x8c, 0x5e, 0x74, 0x1b, - 0x07, 0xc8, 0x32, 0x79, 0x9b, 0xae, 0x1b, 0xef, 0xea, 0x9c, 0x66, 0xbe, - 0x7e, 0xb8, 0xe3, 0xf4, 0xfa, 0xbf, 0xd3, 0xf5, 0xd5, 0x62, 0xe5, 0x7a, - 0x6b, 0xcc, 0xee, 0xe0, 0x47, 0xc0, 0x69, 0x51, 0x76, 0x8f, 0x7f, 0xf3, - 0x00, 0x86, 0x9d, 0x16, 0xfd, 0x80, 0x24, 0xe6, 0x88, 0xbd, 0x75, 0x1a, - 0x73, 0x05, 0x5c, 0xf7, 0x57, 0x13, 0xa8, 0xcc, 0x4b, 0xbf, 0x97, 0xc9, - 0x33, 0x13, 0x81, 0x04, 0xcf, 0x3f, 0x2e, 0xe0, 0x18, 0x79, 0x9f, 0x78, - 0xcd, 0x3f, 0x33, 0xf4, 0x21, 0x28, 0x7b, 0x88, 0x78, 0xe2, 0xdf, 0x02, - 0x7f, 0x68, 0xe0, 0xb5, 0x3e, 0xd9, 0xe4, 0xe5, 0x20, 0x8d, 0x58, 0xcb, - 0xe0, 0x87, 0xe3, 0x35, 0x3d, 0x23, 0x8a, 0x7a, 0x32, 0x5f, 0x8b, 0x80, - 0x63, 0xd9, 0x76, 0x9f, 0x48, 0xcd, 0x0c, 0xcb, 0x67, 0x80, 0x35, 0x8d, - 0x81, 0xc7, 0x4a, 0x3d, 0xf7, 0x8e, 0x0b, 0x1b, 0x43, 0x19, 0xbd, 0x6f, - 0x95, 0x83, 0xb2, 0x7e, 0xff, 0xf1, 0x50, 0x80, 0x31, 0xbf, 0xfc, 0x21, - 0x1a, 0xcf, 0x1a, 0x93, 0x2b, 0x80, 0x00, 0x8e, 0xa4, 0xc9, 0x99, 0x06, - 0x16, 0x11, 0x85, 0x1a, 0x43, 0x1a, 0xbb, 0xd7, 0x37, 0x73, 0x9f, 0x5f, - 0xbd, 0x67, 0xef, 0xf6, 0xbf, 0xcf, 0xfb, 0x66, 0x7e, 0x7f, 0x7d, 0x7e, - 0xdf, 0x7a, 0xbe, 0x4a, 0x8e, 0xa5, 0x66, 0xeb, 0x91, 0x33, 0xb9, 0x7b, - 0x8f, 0xc1, 0x05, 0x66, 0x84, 0x41, 0x51, 0xc7, 0xcb, 0x00, 0xea, 0xa8, - 0x2e, 0xa5, 0xcf, 0x36, 0x67, 0x74, 0xcc, 0x76, 0x7b, 0x1a, 0x7f, 0xde, - 0xa2, 0x1b, 0xf2, 0x9a, 0x59, 0x92, 0x55, 0x77, 0x6a, 0xe7, 0x70, 0x81, - 0x81, 0xc0, 0x33, 0x94, 0xd5, 0xf8, 0x80, 0x00, 0x54, 0x7c, 0xef, 0xc8, - 0x96, 0xe0, 0xbc, 0x1b, 0x92, 0xe4, 0xb6, 0x3a, 0x31, 0xa3, 0xfd, 0x0c, - 0x43, 0x70, 0x13, 0xa6, 0xe9, 0xe0, 0xac, 0x6c, 0x8e, 0x93, 0x0d, 0xde, - 0x56, 0x7a, 0xe5, 0xe3, 0x01, 0xc0, 0x8e, 0x64, 0x5e, 0x12, 0xc3, 0xee, - 0x57, 0x2b, 0xb8, 0xec, 0x4a, 0x23, 0x1b, 0xec, 0xfd, 0x8f, 0xd5, 0x7c, - 0x5c, 0x08, 0x85, 0x64, 0x9c, 0x46, 0x0d, 0x0d, 0xbf, 0x5c, 0x2e, 0x94, - 0xa0, 0x32, 0xee, 0x51, 0xe0, 0x07, 0x72, 0xea, 0x79, 0x6b, 0x30, 0x14, - 0x7b, 0x38, 0x45, 0x06, 0x24, 0x02, 0xca, 0x80, 0x15, 0x48, 0x00, 0xd2, - 0xa5, 0x1c, 0xe6, 0x6a, 0xda, 0x23, 0x4c, 0x5f, 0xd8, 0xf9, 0x6e, 0xa3, - 0x45, 0x8f, 0x91, 0x6f, 0xf4, 0x4d, 0x76, 0x4b, 0xa4, 0xec, 0x77, 0x6e, - 0x1b, 0x83, 0xe5, 0x40, 0xb1, 0x83, 0x92, 0xe4, 0x5f, 0x0a, 0x4f, 0xae, - 0xf5, 0x10, 0x21, 0x99, 0xe9, 0x7f, 0xbf, 0xb3, 0xde, 0x44, 0xd1, 0x1f, - 0xfd, 0xad, 0xb7, 0xd9, 0x2f, 0x6f, 0xd7, 0xf1, 0x53, 0x77, 0xc8, 0xfa, - 0x8f, 0x45, 0xf1, 0x22, 0x39, 0xd9, 0x61, 0x63, 0x39, 0x58, 0x46, 0x14, - 0x0f, 0x9d, 0x07, 0xe5, 0x18, 0xf3, 0xe7, 0x9c, 0xf3, 0x95, 0xf3, 0xf1, - 0xe7, 0xc7, 0xef, 0xe2, 0xff, 0x8f, 0xe7, 0xa9, 0xfc, 0x7e, 0xbc, 0x7e, - 0xdf, 0x78, 0x54, 0x5f, 0xcf, 0x16, 0xc8, 0xc0, 0x7d, 0x97, 0xa0, 0x63, - 0xc0, 0xe4, 0x78, 0x58, 0x4f, 0x0d, 0xbe, 0x8e, 0x13, 0x47, 0x1c, 0xe7, - 0x23, 0x52, 0x4c, 0x50, 0xb4, 0x03, 0x9c, 0xb4, 0x39, 0x1e, 0x44, 0xe2, - 0xce, 0xac, 0xda, 0xb6, 0xaa, 0xd4, 0xa6, 0x4b, 0x08, 0x82, 0x12, 0x57, - 0x7f, 0x21, 0x9e, 0xa6, 0x96, 0xfc, 0x2f, 0x56, 0xf9, 0xf5, 0x8c, 0x71, - 0xd7, 0x69, 0xf0, 0xa3, 0x93, 0xb0, 0xbe, 0x2c, 0x01, 0x51, 0x7b, 0x30, - 0x5c, 0x09, 0x83, 0x8a, 0xca, 0xf0, 0x87, 0x79, 0xda, 0x37, 0xcb, 0xc4, - 0x04, 0xda, 0x1e, 0xd0, 0xcc, 0x40, 0xfb, 0x2c, 0x17, 0x9d, 0xe1, 0x4e, - 0x4d, 0xae, 0x8b, 0x54, 0x50, 0x29, 0xa5, 0x2e, 0xee, 0xc7, 0x8e, 0x45, - 0x59, 0xbb, 0xae, 0x84, 0xf0, 0xff, 0xf1, 0x50, 0x80, 0x2e, 0x1f, 0xfc, - 0x21, 0x1a, 0xcf, 0x11, 0xc9, 0x74, 0x00, 0x00, 0x8e, 0xb1, 0xb2, 0x64, - 0x2c, 0x47, 0x1a, 0x18, 0xc2, 0xc1, 0x46, 0x90, 0x87, 0x12, 0xe5, 0x48, - 0xdf, 0x8f, 0xc7, 0xdf, 0xf4, 0xfe, 0x7f, 0xaf, 0x9f, 0xb7, 0xf8, 0x3d, - 0x7d, 0xe7, 0xdb, 0xe9, 0xca, 0xab, 0x5b, 0x75, 0x5a, 0xde, 0x73, 0xc8, - 0x32, 0xc2, 0xf4, 0x8d, 0x84, 0xd9, 0x8e, 0xfe, 0xc3, 0x84, 0x06, 0x93, - 0xe7, 0x5c, 0x1c, 0xed, 0x41, 0xa2, 0x44, 0x3f, 0x75, 0x0b, 0xfc, 0x17, - 0x80, 0x16, 0x4a, 0xaa, 0x8c, 0xf6, 0x40, 0x38, 0x4b, 0x2f, 0x4e, 0xed, - 0x71, 0x28, 0x89, 0xaa, 0xdd, 0x5e, 0x08, 0x1d, 0xd9, 0x03, 0xf8, 0xb7, - 0x49, 0x97, 0xe3, 0x61, 0xe4, 0x2c, 0x5f, 0x9e, 0x66, 0x99, 0x1b, 0xd7, - 0x78, 0x1f, 0x68, 0x29, 0x8a, 0x6d, 0x4f, 0xd8, 0x6d, 0x1f, 0xfc, 0x34, - 0xff, 0xec, 0x61, 0x0a, 0xd9, 0xc1, 0x1a, 0x9e, 0xb2, 0x90, 0x49, 0x94, - 0x1c, 0x68, 0xef, 0xca, 0xff, 0x9b, 0xb4, 0xa7, 0x7f, 0x4e, 0x00, 0x1b, - 0xac, 0x79, 0xb4, 0xdd, 0xf9, 0x20, 0x4e, 0xae, 0xc9, 0x50, 0xe6, 0x9c, - 0x6a, 0x41, 0x91, 0xb3, 0x53, 0xc1, 0xb4, 0xd0, 0xc1, 0x21, 0xdb, 0x61, - 0x87, 0xbb, 0xe5, 0x00, 0x53, 0xd5, 0x54, 0xd0, 0x05, 0x30, 0x2d, 0xa4, - 0x0f, 0x50, 0xa3, 0x4a, 0x94, 0x8e, 0x2c, 0xb8, 0xa3, 0xfc, 0x61, 0x37, - 0x43, 0xf3, 0xab, 0x1f, 0xd2, 0x7d, 0x62, 0x25, 0x80, 0x71, 0x4b, 0xdf, - 0x7c, 0x51, 0x51, 0xa5, 0xf8, 0xb5, 0x3f, 0xa5, 0xff, 0xe1, 0xea, 0xea, - 0xb9, 0xf1, 0x62, 0x1c, 0xb8, 0xc0, 0xe5, 0xd5, 0x1d, 0x01, 0x05, 0x62, - 0x01, 0xf8, 0x00, 0x84, 0x36, 0x88, 0xbb, 0x23, 0x82, 0x94, 0x22, 0xc1, - 0xbc, 0xaf, 0x28, 0xe7, 0x13, 0x2a, 0xc2, 0x85, 0x51, 0xa1, 0xfd, 0x04, - 0x1f, 0x30, 0xa7, 0x1d, 0x76, 0xbe, 0x37, 0xe3, 0xab, 0xbb, 0x97, 0xfd, - 0x3f, 0xea, 0x7e, 0xdf, 0xe9, 0xbf, 0xcf, 0xd6, 0xb6, 0x2c, 0xec, 0x53, - 0x01, 0xdb, 0xfa, 0x81, 0xbe, 0x4c, 0x7b, 0xa0, 0x73, 0x01, 0xd8, 0x96, - 0xd1, 0x13, 0x21, 0x9a, 0x82, 0x30, 0x83, 0x0f, 0x5a, 0xbe, 0xfa, 0x4c, - 0x41, 0x27, 0x73, 0x1c, 0x51, 0x3f, 0xc0, 0xee, 0x57, 0x56, 0x91, 0x7e, - 0x5d, 0x81, 0x5d, 0xf7, 0xec, 0xc3, 0x94, 0x74, 0x55, 0xdf, 0xe8, 0x7c, - 0xef, 0x57, 0x48, 0xf7, 0x3d, 0x6e, 0xe1, 0x1a, 0x17, 0xa3, 0xa5, 0xf8, - 0x47, 0x6a, 0x1d, 0xaa, 0xfc, 0x10, 0xbf, 0x7e, 0x1e, 0x95, 0xdd, 0xf1, - 0xb5, 0xac, 0xb6, 0x5f, 0xbe, 0x87, 0xde, 0xe2, 0x3a, 0x49, 0x13, 0xe1, - 0x57, 0xff, 0xf1, 0x50, 0x80, 0x2f, 0x9f, 0xfc, 0x21, 0x1a, 0xcf, 0x08, - 0x92, 0x90, 0x00, 0x00, 0x8d, 0xb1, 0x42, 0x64, 0x4a, 0xc4, 0x5b, 0x14, - 0x84, 0x2f, 0x8b, 0xeb, 0xd6, 0xa7, 0x9f, 0x9f, 0x5f, 0x89, 0x9f, 0xaf, - 0xed, 0xe7, 0xf4, 0xff, 0x14, 0xf5, 0xf8, 0x73, 0xab, 0xed, 0x48, 0xb9, - 0x2f, 0x75, 0xbe, 0xc0, 0x3e, 0xbf, 0xb9, 0x1d, 0xd6, 0xee, 0x6b, 0xc7, - 0x74, 0x67, 0x4f, 0x1b, 0x5a, 0x23, 0xc3, 0x18, 0x29, 0x09, 0x4d, 0x00, - 0xec, 0x34, 0x9a, 0xdf, 0x9d, 0xe8, 0xec, 0xf8, 0x69, 0x32, 0x71, 0x9e, - 0xbf, 0x97, 0xa7, 0xd1, 0xdf, 0x14, 0x56, 0x73, 0x39, 0xd7, 0x3d, 0x75, - 0x85, 0x47, 0x4f, 0x3e, 0xe8, 0x41, 0xba, 0x9f, 0x76, 0x3b, 0x17, 0x9a, - 0x9a, 0x27, 0xfd, 0xe4, 0x0c, 0x4f, 0xcf, 0xc6, 0xfe, 0x9c, 0x40, 0x9f, - 0xef, 0xbb, 0x7e, 0xb7, 0xe4, 0x5d, 0xc7, 0xde, 0xd7, 0x85, 0xe8, 0xa8, - 0x6c, 0x5c, 0x62, 0xb4, 0xde, 0x2e, 0x9c, 0xd0, 0x1a, 0x57, 0xfa, 0x77, - 0x63, 0xa8, 0xf4, 0xb0, 0x29, 0x23, 0x03, 0xae, 0xef, 0x5c, 0x8b, 0xc3, - 0x54, 0xc0, 0x59, 0xe9, 0xfe, 0x67, 0x27, 0xb4, 0xb0, 0xb5, 0x26, 0x28, - 0xfc, 0x8c, 0xcd, 0x2e, 0x69, 0x77, 0x59, 0x22, 0xce, 0xab, 0x73, 0xd7, - 0x90, 0xa8, 0xc8, 0x34, 0x17, 0x7e, 0x35, 0xce, 0x1a, 0xab, 0xb0, 0x50, - 0x15, 0x34, 0xfa, 0xb0, 0x7a, 0xa5, 0xd5, 0x42, 0xfc, 0xe2, 0x2a, 0x8c, - 0x6d, 0xc8, 0x2f, 0x60, 0x50, 0x5b, 0xaf, 0x44, 0x38, 0xa1, 0x15, 0x06, - 0x96, 0x1b, 0xde, 0x66, 0xbb, 0x33, 0x69, 0x28, 0x12, 0x99, 0xce, 0x12, - 0x38, 0xca, 0x80, 0x96, 0xce, 0x6c, 0x06, 0x13, 0x15, 0xa2, 0xea, 0xc0, - 0x96, 0x30, 0xb8, 0xaa, 0x52, 0xf0, 0x29, 0x33, 0x78, 0xb8, 0x5c, 0xbf, - 0xd9, 0x65, 0xd6, 0x28, 0xc8, 0x19, 0x29, 0xd1, 0x1b, 0x34, 0x43, 0x10, - 0xbc, 0xe4, 0x2f, 0x30, 0xe6, 0xfa, 0xb9, 0xbb, 0x93, 0xf3, 0xf1, 0xf1, - 0xeb, 0xce, 0x6f, 0x7f, 0x0f, 0x3f, 0x9f, 0xdf, 0x7d, 0xeb, 0x5b, 0xbc, - 0x8d, 0x6e, 0x6e, 0xea, 0xa6, 0x03, 0xe5, 0x7c, 0x1d, 0x99, 0x73, 0x79, - 0xfc, 0xec, 0x2b, 0x8f, 0x7a, 0xf6, 0xdf, 0x5b, 0xe7, 0x29, 0xbd, 0xa6, - 0xf8, 0xbe, 0x7f, 0xe4, 0x7a, 0x5e, 0x29, 0x7b, 0x2f, 0x1a, 0x8c, 0xf3, - 0x56, 0x37, 0xb2, 0x24, 0x4d, 0x5a, 0x6b, 0x7f, 0x23, 0xb9, 0xc8, 0xbb, - 0xe8, 0x53, 0xe2, 0x2d, 0xc9, 0x60, 0x45, 0xdd, 0x39, 0x8b, 0x8a, 0x69, - 0xdf, 0x97, 0x7d, 0x86, 0x14, 0x9e, 0xc0, 0x5a, 0x9c, 0x11, 0xe2, 0x16, - 0xab, 0x5a, 0xd2, 0x94, 0xf2, 0x9e, 0xcf, 0xf9, 0xde, 0x5b, 0x0f, 0x3b, - 0x53, 0xa2, 0x00, 0x2e, 0x9f, 0x3b, 0x50, 0x87, 0xb8, 0xff, 0xf1, 0x50, - 0x80, 0x2b, 0xbf, 0xfc, 0x21, 0x1a, 0xce, 0x2a, 0xfb, 0x77, 0x00, 0x00, - 0x8e, 0xa1, 0xcb, 0xd0, 0xaa, 0x44, 0x68, 0x8c, 0x74, 0xbb, 0xdd, 0xaf, - 0xbe, 0xfe, 0xa4, 0xfc, 0x7d, 0xb5, 0xf6, 0xfe, 0x73, 0x8f, 0xe9, 0xfb, - 0xeb, 0xd7, 0x15, 0xae, 0xf5, 0x8e, 0x29, 0xa6, 0xfc, 0x56, 0x01, 0xf7, - 0x7f, 0x28, 0xef, 0xa7, 0xd0, 0x74, 0xce, 0x7c, 0xdf, 0x21, 0xcf, 0xe2, - 0x46, 0xfa, 0xd5, 0x88, 0x4c, 0x1c, 0xa8, 0x6c, 0xfe, 0x07, 0xa1, 0xdb, - 0x52, 0xc9, 0x14, 0x61, 0x1a, 0xdc, 0xbd, 0x1d, 0x2d, 0xd2, 0x5a, 0x84, - 0x72, 0x7e, 0x19, 0x6f, 0x27, 0x32, 0x0e, 0xe7, 0x4d, 0x2c, 0x6f, 0xfd, - 0x62, 0x91, 0x77, 0x23, 0xdd, 0xc0, 0x1b, 0x62, 0x63, 0xbf, 0x14, 0xad, - 0x15, 0x7a, 0x2a, 0xb3, 0x8a, 0x76, 0x8d, 0x82, 0x0b, 0x68, 0xfb, 0xac, - 0x2e, 0x41, 0x92, 0xcd, 0xb7, 0x53, 0xce, 0x8e, 0xd9, 0xdb, 0x30, 0x4e, - 0x98, 0xab, 0x44, 0x67, 0x1b, 0x23, 0x6a, 0xd6, 0xc5, 0x93, 0xe3, 0x6a, - 0xa1, 0x7a, 0x59, 0x52, 0xaf, 0x5d, 0x61, 0xe4, 0x16, 0x38, 0xb8, 0x56, - 0x79, 0x63, 0x44, 0x75, 0x73, 0x54, 0x6a, 0x5f, 0x1d, 0xae, 0x84, 0x7a, - 0xf2, 0xbe, 0x90, 0x52, 0xdb, 0x1c, 0x50, 0x65, 0x6b, 0x6a, 0x9b, 0x9a, - 0x2a, 0x4e, 0x96, 0xe2, 0xb5, 0xe2, 0xa8, 0xe3, 0xa6, 0x87, 0x43, 0x21, - 0xff, 0xf3, 0x7a, 0x04, 0x0f, 0x1e, 0xaf, 0x71, 0xa2, 0xb3, 0x48, 0x6f, - 0x11, 0x77, 0x68, 0xce, 0xb1, 0xd6, 0xd9, 0x48, 0xc3, 0xce, 0xb5, 0xbb, - 0xa7, 0x08, 0xe7, 0xa2, 0x23, 0xcc, 0x83, 0xf3, 0x19, 0x6f, 0x65, 0x56, - 0x9b, 0xef, 0xea, 0x4d, 0x66, 0xbf, 0xa7, 0xfc, 0xe7, 0x1f, 0x6f, 0x6d, - 0x7a, 0x92, 0x6f, 0x8d, 0xdd, 0x49, 0x15, 0x88, 0x15, 0xfa, 0xbe, 0x1e, - 0x5e, 0xe3, 0x2e, 0xa3, 0xab, 0xb6, 0x2e, 0x2e, 0xd9, 0xdf, 0xcc, 0xca, - 0x6a, 0xb3, 0x31, 0x35, 0xff, 0x4f, 0x9a, 0x11, 0x39, 0xc4, 0xde, 0x6d, - 0xb7, 0xad, 0x78, 0xea, 0xe4, 0x17, 0x31, 0x38, 0xde, 0x96, 0xaf, 0x9a, - 0x07, 0xc1, 0xcc, 0x86, 0x8f, 0x73, 0x1a, 0xe2, 0x2f, 0xd2, 0xe0, 0x18, - 0x3b, 0x5c, 0x3e, 0x36, 0x83, 0x60, 0xaa, 0x68, 0x6c, 0xfd, 0x9a, 0x4b, - 0x34, 0x7d, 0x39, 0x2e, 0x3b, 0xbe, 0x46, 0x09, 0x89, 0x94, 0xfa, 0x92, - 0x03, 0x21, 0xb0, 0x20, 0xbd, 0x3c, 0xaf, 0xd3, 0xa6, 0xb6, 0xed, 0xa9, - 0x3f, 0x13, 0x68, 0x81, 0x3a, 0xc9, 0x5a, 0xc7, 0x7e, 0xe0, 0xff, 0xf1, - 0x50, 0x80, 0x2f, 0x3f, 0xfc, 0x21, 0x1a, 0xcd, 0x8a, 0x80, 0x7f, 0x00, - 0x00, 0x8b, 0xb2, 0x32, 0x2c, 0x2c, 0x44, 0xab, 0x08, 0x86, 0x2e, 0x6b, - 0xcf, 0xcf, 0x0f, 0xbf, 0xe9, 0xdf, 0xf8, 0xe3, 0x9f, 0x6f, 0xe3, 0xcf, - 0xaf, 0x6d, 0x6b, 0xf8, 0xfd, 0xf3, 0xbf, 0x6c, 0xbe, 0xda, 0xad, 0xba, - 0x4c, 0xee, 0xb6, 0x21, 0x73, 0xb9, 0x35, 0xef, 0x58, 0xe8, 0x32, 0x14, - 0x78, 0x9b, 0xd8, 0x27, 0xca, 0xb1, 0xf6, 0xbd, 0xb6, 0x5d, 0xc2, 0xe7, - 0xea, 0x9c, 0xa3, 0xbe, 0xa0, 0x1f, 0x83, 0x22, 0x6a, 0xed, 0x8e, 0x99, - 0xc2, 0x37, 0x2b, 0xca, 0x5d, 0x3a, 0x65, 0xdc, 0x1e, 0x0d, 0xff, 0x0e, - 0x6b, 0xf6, 0x07, 0x5d, 0xfd, 0x48, 0x0a, 0x4f, 0x48, 0x6d, 0x75, 0xbe, - 0x69, 0xa0, 0xbe, 0x20, 0x1c, 0xff, 0x84, 0xf5, 0xbb, 0x87, 0x55, 0xfc, - 0xee, 0xde, 0x00, 0x0e, 0x3c, 0xcd, 0xed, 0xc3, 0x83, 0x8f, 0x37, 0x77, - 0x57, 0x27, 0x2b, 0xd0, 0xfa, 0x9f, 0xcc, 0x57, 0xfa, 0xad, 0x99, 0xee, - 0x6d, 0x6b, 0x4d, 0x37, 0xbc, 0x7f, 0x85, 0x11, 0x50, 0x8d, 0x45, 0x5e, - 0xfe, 0x8f, 0xb0, 0x66, 0x25, 0x30, 0xba, 0x7f, 0x3e, 0xfc, 0x8d, 0xfa, - 0x0f, 0xa6, 0xa9, 0x5a, 0xad, 0x31, 0xcd, 0x01, 0x19, 0xeb, 0x0c, 0x48, - 0x3f, 0xdd, 0xd9, 0x5a, 0x0a, 0x4f, 0x3d, 0x93, 0x36, 0x06, 0xf4, 0x62, - 0xa7, 0x4e, 0x44, 0x0c, 0x05, 0xb2, 0xa0, 0x8a, 0xc1, 0x57, 0xa8, 0x1c, - 0x65, 0xaf, 0xf7, 0xf0, 0x60, 0xc4, 0x3a, 0x8f, 0x5c, 0xb0, 0x7e, 0x85, - 0x23, 0xc5, 0x41, 0x29, 0xba, 0x33, 0x40, 0x35, 0xf4, 0x0d, 0x6f, 0x06, - 0x21, 0x49, 0x81, 0xb0, 0x4f, 0x33, 0xd3, 0x05, 0x6b, 0x07, 0xf4, 0x51, - 0x9e, 0x1d, 0xcc, 0x4d, 0x98, 0xec, 0x05, 0xde, 0x6f, 0x30, 0x1c, 0x8b, - 0x96, 0xb1, 0x94, 0x28, 0x8f, 0x42, 0x8b, 0xca, 0xef, 0xab, 0xf1, 0xd5, - 0xab, 0xad, 0xcf, 0xb7, 0xef, 0xce, 0xbe, 0xb7, 0xbf, 0xeb, 0xfe, 0xfa, - 0xd7, 0xf1, 0xfb, 0xe7, 0x6b, 0x98, 0xb5, 0x4e, 0x75, 0x4a, 0x94, 0x2a, - 0xba, 0x3f, 0xe0, 0x73, 0x62, 0x78, 0x3d, 0xd0, 0xd5, 0x6e, 0xc2, 0x1c, - 0xc4, 0xdc, 0x6a, 0x6d, 0xa5, 0x4b, 0x95, 0xfa, 0x16, 0x0c, 0xee, 0x8a, - 0x11, 0x08, 0x35, 0x0e, 0xbc, 0x95, 0x56, 0x49, 0x14, 0x44, 0xcc, 0x8d, - 0x6c, 0xe3, 0xed, 0x00, 0xc0, 0x1c, 0x40, 0x53, 0x60, 0x07, 0xb6, 0x70, - 0x5b, 0x7d, 0xaf, 0x07, 0xad, 0xde, 0x1d, 0x1a, 0x31, 0x36, 0xfc, 0x84, - 0x0e, 0x3a, 0xb7, 0xb0, 0x8b, 0x66, 0x77, 0x7b, 0xe1, 0x79, 0xb7, 0x3e, - 0x20, 0xe0, 0x4c, 0x21, 0x24, 0x59, 0x35, 0x65, 0xa2, 0x25, 0x3c, 0x31, - 0x17, 0x70, 0xbe, 0xff, 0xf1, 0x50, 0x80, 0x32, 0x7f, 0xfc, 0x21, 0x1a, - 0xce, 0x20, 0xed, 0x7c, 0x00, 0x00, 0x8d, 0xb1, 0x42, 0xd4, 0xac, 0x53, - 0x0b, 0x11, 0x10, 0xc4, 0x41, 0x8f, 0x84, 0xd6, 0xee, 0xaf, 0xbf, 0x5f, - 0xac, 0xe1, 0xdf, 0xb7, 0xcf, 0x9f, 0x39, 0xf9, 0xfd, 0x5d, 0xf5, 0xba, - 0x94, 0xbb, 0xc9, 0x56, 0xce, 0xf0, 0x3d, 0xd7, 0x21, 0xe9, 0xfb, 0x93, - 0x8f, 0x97, 0x45, 0x3b, 0xb5, 0x0e, 0xa9, 0x5a, 0x8c, 0x25, 0x8e, 0x22, - 0xd9, 0x44, 0xc3, 0x39, 0x97, 0x72, 0x37, 0x23, 0xf9, 0xc3, 0x66, 0x3c, - 0x2a, 0x5a, 0xb8, 0xcd, 0x6d, 0xc7, 0x87, 0x25, 0xc9, 0x29, 0xa2, 0x94, - 0xdb, 0x56, 0xc0, 0x20, 0x02, 0x49, 0x0d, 0xaf, 0x4e, 0xb9, 0x28, 0xb9, - 0xef, 0xd6, 0xb3, 0xac, 0x06, 0x3c, 0xc5, 0xf7, 0x94, 0x49, 0x00, 0x95, - 0xd9, 0x26, 0x12, 0x69, 0x09, 0xde, 0xc5, 0x15, 0x46, 0x99, 0x5f, 0x54, - 0x14, 0x01, 0x20, 0x02, 0x39, 0x57, 0x99, 0x4d, 0x02, 0x13, 0xb5, 0x31, - 0x2f, 0x13, 0x1a, 0x0e, 0x2b, 0xf7, 0xa4, 0x36, 0x41, 0xa4, 0xb3, 0x84, - 0x3b, 0xda, 0x7b, 0x35, 0x79, 0xb5, 0x39, 0xc4, 0x62, 0x53, 0x9c, 0x97, - 0x9e, 0x8f, 0x07, 0xfc, 0x31, 0x2a, 0x94, 0x63, 0x32, 0x56, 0xed, 0xb9, - 0x74, 0x72, 0xe2, 0x11, 0x0a, 0x3c, 0x1f, 0xf3, 0xbf, 0x3d, 0x86, 0x42, - 0x78, 0x7e, 0x8b, 0xcc, 0xd7, 0xa7, 0xa0, 0xdd, 0x86, 0x6b, 0xa0, 0xb0, - 0x6e, 0x63, 0xc0, 0x64, 0x36, 0x32, 0xc7, 0x06, 0x15, 0x47, 0x61, 0x81, - 0x89, 0x4a, 0xa2, 0x20, 0x20, 0x75, 0x9c, 0x21, 0xd1, 0x0e, 0x67, 0x46, - 0xb2, 0x5b, 0xd1, 0x0a, 0xa4, 0x59, 0xe6, 0x44, 0x32, 0x79, 0x62, 0x15, - 0x6c, 0x9a, 0x25, 0x24, 0xfd, 0x10, 0xdd, 0xce, 0xf1, 0x1e, 0x97, 0x6e, - 0x0d, 0x22, 0xc6, 0x63, 0x41, 0x96, 0xd2, 0x64, 0xb6, 0xd2, 0xac, 0x4a, - 0xe5, 0x30, 0x18, 0xd8, 0x6b, 0x11, 0x46, 0x88, 0xf2, 0xa0, 0xbc, 0x24, - 0x1f, 0x31, 0x52, 0x7c, 0x57, 0x33, 0x4a, 0xf5, 0xfa, 0xe7, 0xb3, 0xc7, - 0xc7, 0xf6, 0xfe, 0x7c, 0xe7, 0xe7, 0xf5, 0xe7, 0xd6, 0xad, 0x44, 0xab, - 0xc2, 0x95, 0x41, 0xca, 0xec, 0x27, 0x0f, 0xe3, 0x46, 0xc7, 0x1c, 0xce, - 0x79, 0x52, 0xa3, 0x9e, 0x08, 0xa6, 0x88, 0x63, 0x40, 0x37, 0x38, 0x46, - 0x76, 0xf7, 0xf5, 0xaf, 0xe2, 0x4e, 0x6a, 0x10, 0x25, 0x67, 0x48, 0x22, - 0xd3, 0x2f, 0x65, 0x41, 0x15, 0x35, 0xdd, 0xbb, 0xf7, 0xfd, 0xe0, 0x68, - 0x4e, 0x4d, 0x2f, 0xc2, 0xfc, 0x07, 0xc9, 0x74, 0x64, 0xfd, 0xb3, 0xe1, - 0xeb, 0x77, 0xd5, 0x3d, 0xc9, 0x99, 0x34, 0x69, 0x90, 0xdf, 0xe8, 0xa2, - 0xc1, 0xce, 0x46, 0x43, 0x0a, 0xd7, 0x97, 0xe9, 0x35, 0x92, 0x5c, 0x71, - 0x47, 0x77, 0x34, 0xb9, 0x04, 0xd0, 0xed, 0xab, 0xc7, 0x4e, 0x68, 0x4f, - 0x51, 0x35, 0x89, 0xb3, 0xf6, 0x6c, 0xea, 0x69, 0xa9, 0xb8, 0xff, 0xf1, - 0x50, 0x80, 0x3e, 0x5f, 0xfc, 0x20, 0x8d, 0x1a, 0xd6, 0x66, 0x35, 0x85, - 0x18, 0xc1, 0x45, 0xb0, 0xd0, 0x60, 0x0b, 0xc3, 0xe3, 0xc7, 0x9c, 0xcf, - 0xbf, 0xf1, 0xed, 0xc5, 0x6e, 0x7e, 0xff, 0xa7, 0xb7, 0x3c, 0x7a, 0x9c, - 0x71, 0xdf, 0x15, 0xd5, 0xe6, 0xf2, 0x86, 0x9f, 0x69, 0x86, 0xd0, 0x29, - 0x02, 0x0b, 0x4b, 0xb8, 0xb3, 0x29, 0x33, 0x26, 0x57, 0x07, 0x31, 0xc5, - 0x9b, 0x39, 0x1c, 0xfb, 0x6a, 0x18, 0x18, 0x83, 0x55, 0x55, 0x03, 0x19, - 0xf1, 0xf2, 0xd0, 0x8e, 0x1e, 0xd3, 0xe4, 0x04, 0x00, 0x47, 0x98, 0x03, - 0x89, 0xd1, 0xfd, 0xe5, 0x57, 0x73, 0x07, 0xdc, 0x4b, 0x7b, 0x83, 0x60, - 0x14, 0x70, 0x0f, 0x8e, 0x92, 0xd5, 0x1d, 0x28, 0xe0, 0x46, 0xdd, 0x52, - 0x59, 0x64, 0x7a, 0xd2, 0xf1, 0x94, 0xb0, 0xb1, 0xa7, 0x71, 0xe1, 0x1f, - 0x87, 0x98, 0x0c, 0x20, 0x1f, 0xa2, 0x07, 0x51, 0xaf, 0x27, 0x8c, 0x62, - 0x93, 0x02, 0x00, 0xce, 0xc1, 0x48, 0x03, 0xfb, 0x60, 0x0e, 0x61, 0xcc, - 0x5e, 0x23, 0x49, 0xf2, 0x6e, 0xd6, 0xc3, 0xea, 0x66, 0x3f, 0xbc, 0xf2, - 0x52, 0x51, 0xb1, 0xeb, 0x59, 0xb0, 0x04, 0x54, 0xeb, 0x7f, 0x49, 0xf4, - 0x1f, 0x08, 0xd8, 0x07, 0x1f, 0xa2, 0xdd, 0x77, 0x77, 0x4f, 0xbb, 0x0b, - 0xfc, 0x84, 0x90, 0x73, 0x33, 0x01, 0xf4, 0xb4, 0xbf, 0x99, 0xc9, 0xd8, - 0x3e, 0xab, 0x0c, 0x01, 0x7a, 0xa2, 0xf2, 0xb9, 0x0c, 0x42, 0xc9, 0x02, - 0xe5, 0x9e, 0x44, 0x9d, 0x45, 0x39, 0x4d, 0xb7, 0x6b, 0x61, 0x7d, 0x57, - 0x53, 0x85, 0x75, 0xcb, 0x01, 0x23, 0x12, 0x09, 0xf2, 0xea, 0xf8, 0x4c, - 0x87, 0x42, 0xff, 0x50, 0xe3, 0x76, 0x8a, 0xdf, 0x63, 0x08, 0x4a, 0x6f, - 0x5c, 0x4e, 0xbd, 0x2c, 0x01, 0x5c, 0x8c, 0xa9, 0xe4, 0x8a, 0xb5, 0x1d, - 0x0a, 0xe3, 0x62, 0x26, 0x01, 0x2f, 0x77, 0xd7, 0x3b, 0x75, 0x5f, 0x8d, - 0x7d, 0xbf, 0x9f, 0xfb, 0x7f, 0x37, 0x7e, 0x27, 0xb7, 0xaf, 0x3b, 0xeb, - 0xd7, 0x59, 0xad, 0xcd, 0x67, 0x15, 0xb9, 0x6d, 0x30, 0xca, 0xff, 0x80, - 0x61, 0x92, 0x00, 0xe1, 0xc1, 0xf8, 0x3e, 0x23, 0xff, 0x89, 0x10, 0xa0, - 0x7f, 0xcf, 0xa5, 0xc9, 0x9a, 0x24, 0xd8, 0xfb, 0x73, 0x15, 0x07, 0x84, - 0x18, 0xc9, 0xab, 0xf0, 0x73, 0xce, 0x4b, 0xb9, 0x5e, 0x50, 0xd2, 0xd8, - 0xf0, 0xe2, 0xaa, 0x01, 0xd9, 0x61, 0xcf, 0x75, 0x62, 0x9d, 0xcc, 0xfb, - 0xa6, 0x0c, 0x44, 0x63, 0xec, 0xff, 0xd0, 0x00, 0xd6, 0x5e, 0xd9, 0xd8, - 0x7b, 0xef, 0x88, 0x80, 0x36, 0xc0, 0x76, 0x7e, 0x4e, 0x18, 0xe5, 0x5d, - 0xbc, 0x72, 0x0f, 0x49, 0xf4, 0x7d, 0xf3, 0x2e, 0xf2, 0x8e, 0x28, 0xea, - 0x07, 0x80, 0x87, 0x11, 0x5c, 0xac, 0x8f, 0x12, 0x59, 0x1d, 0x4f, 0x87, - 0xfe, 0xa1, 0x5c, 0xc6, 0x6c, 0x16, 0xa3, 0x49, 0x57, 0x7a, 0x7b, 0xba, - 0x38, 0x0e, 0x0c, 0x43, 0xbf, 0x48, 0x8e, 0x7a, 0x2d, 0xa1, 0x5a, 0xab, - 0xd1, 0xbd, 0xc4, 0x7d, 0x7f, 0x43, 0xb3, 0x4f, 0x98, 0x0f, 0x31, 0xd7, - 0xe0, 0x93, 0x3a, 0x77, 0x84, 0x8c, 0xfb, 0x84, 0x6b, 0x8b, 0x0e, 0xdf, - 0xf3, 0x9c, 0xaf, 0x54, 0x0f, 0xf0, 0x0b, 0x7c, 0x00, 0xde, 0x1c, 0x07, - 0xeb, 0xfd, 0xc6, 0x4b, 0xc7, 0xfc, 0x7f, 0x64, 0xfb, 0x7e, 0x98, 0x28, - 0x09, 0xa4, 0x43, 0xc2, 0x39, 0xaf, 0x1c, 0xfc, 0x77, 0xc4, 0x7f, 0xd2, - 0x11, 0xa5, 0xc6, 0x5d, 0xe5, 0x9e, 0x18, 0x09, 0xfa, 0x85, 0xbe, 0x4f, - 0xfb, 0x69, 0x7b, 0x0a, 0x58, 0x91, 0x2d, 0x3f, 0x76, 0x7f, 0x46, 0x78, - 0x13, 0xc5, 0xa0, 0x78, 0xff, 0xf1, 0x50, 0x80, 0x39, 0x3f, 0xfc, 0x20, - 0x94, 0x2a, 0xd6, 0x56, 0x41, 0x20, 0xd4, 0x8c, 0x00, 0x71, 0xc8, 0x0f, - 0x8f, 0x5e, 0x7b, 0xd7, 0x3e, 0x7e, 0x3e, 0xdc, 0x77, 0xf1, 0xf3, 0xf7, - 0xe3, 0x9c, 0xf6, 0xf1, 0xd6, 0xac, 0x07, 0x7c, 0xfe, 0x2e, 0xd9, 0xca, - 0x4a, 0x0d, 0x07, 0x74, 0xc6, 0xda, 0xe9, 0xf5, 0xcc, 0xe9, 0x6a, 0xcb, - 0x91, 0xb2, 0x05, 0xfd, 0xb0, 0xa6, 0x0b, 0x69, 0x3e, 0x91, 0xdf, 0xb3, - 0x2e, 0x3d, 0xf1, 0x91, 0xc6, 0xfb, 0xae, 0x78, 0x1f, 0xb2, 0x22, 0x36, - 0xbb, 0x57, 0x65, 0x51, 0x71, 0xbc, 0xda, 0x29, 0xb2, 0x8f, 0x51, 0xff, - 0xd5, 0x1c, 0x16, 0xc5, 0xd5, 0x61, 0x5f, 0x5a, 0x7b, 0xad, 0x6d, 0xa0, - 0x52, 0xcd, 0x96, 0x8f, 0x55, 0xbc, 0x5e, 0xa0, 0xa7, 0x15, 0x6b, 0xa0, - 0xd5, 0x97, 0x31, 0x51, 0x7a, 0x88, 0x66, 0x89, 0xc6, 0xf6, 0x2a, 0x9a, - 0xed, 0x52, 0x53, 0x2e, 0xb1, 0x54, 0x46, 0x96, 0xf8, 0x64, 0xe9, 0x5a, - 0x45, 0x58, 0x24, 0xc8, 0xe8, 0x6e, 0xa2, 0x19, 0x4d, 0x15, 0x62, 0xb2, - 0xe6, 0x88, 0xf0, 0x58, 0x30, 0xda, 0xac, 0xfc, 0xa4, 0x0b, 0xad, 0x4a, - 0x66, 0x61, 0x60, 0x89, 0x42, 0xba, 0xdf, 0x94, 0xf1, 0x9e, 0x65, 0x17, - 0x1a, 0xd1, 0x7e, 0x76, 0xed, 0xfc, 0x5e, 0xd3, 0x94, 0xd9, 0xe1, 0x05, - 0xfe, 0x5b, 0xe7, 0xad, 0x72, 0xaf, 0xb7, 0x27, 0xd4, 0x1a, 0x7c, 0xcf, - 0xc3, 0x4e, 0x81, 0x5e, 0x1d, 0xc0, 0xc4, 0xa6, 0x4d, 0xb4, 0x73, 0x50, - 0x2d, 0x20, 0x6d, 0x56, 0x69, 0x66, 0x92, 0x75, 0x66, 0x33, 0x34, 0x40, - 0x3a, 0xf2, 0xdf, 0x1b, 0xbb, 0xce, 0xab, 0x72, 0xfc, 0xff, 0x60, 0xbe, - 0xab, 0xa6, 0xb3, 0xf4, 0x1f, 0xbe, 0xbd, 0xb3, 0x2a, 0xb6, 0x8e, 0xfb, - 0xb8, 0xeb, 0x90, 0x51, 0x22, 0xbd, 0xf3, 0xa8, 0x32, 0x59, 0x6b, 0x99, - 0xc2, 0x3f, 0x7f, 0xe9, 0xf3, 0x92, 0x21, 0x2f, 0xb7, 0x94, 0xf3, 0x1f, - 0x53, 0x6b, 0x82, 0x1e, 0xaa, 0x15, 0xfc, 0x67, 0xc5, 0x67, 0xa5, 0xc0, - 0x35, 0x93, 0xa2, 0x7d, 0x18, 0x7f, 0xdb, 0x6c, 0x08, 0x22, 0xd5, 0xd5, - 0x29, 0x46, 0x5d, 0x91, 0x2c, 0xeb, 0xf3, 0x6e, 0xa9, 0x67, 0xe1, 0x94, - 0x75, 0x27, 0x39, 0x4b, 0x23, 0xe7, 0x6b, 0x22, 0x62, 0x4b, 0x67, 0xa5, - 0xd9, 0xdf, 0x5b, 0xa8, 0x38, 0x97, 0x05, 0x26, 0x26, 0x79, 0x8e, 0xd5, - 0xb5, 0xff, 0xfa, 0xd9, 0x66, 0xcb, 0x17, 0xfa, 0xb7, 0xc6, 0xc8, 0xe4, - 0xec, 0xfe, 0xab, 0xfc, 0xd1, 0x17, 0x75, 0x73, 0xdf, 0xf8, 0xbf, 0xcd, - 0x0c, 0x62, 0xbd, 0x0c, 0xee, 0xfd, 0x67, 0xf7, 0x5f, 0xad, 0xc4, 0xb7, - 0xa2, 0xea, 0xff, 0x8b, 0xf5, 0x4f, 0xf9, 0x5e, 0xc9, 0xb2, 0x3f, 0x30, - 0x42, 0x56, 0x70, 0x38, 0xdb, 0xd4, 0x60, 0x5a, 0xdf, 0x1d, 0xec, 0xfe, - 0xfd, 0x34, 0x7c, 0x85, 0x29, 0xdc, 0x87, 0xcc, 0x19, 0x24, 0x84, 0x26, - 0xad, 0x70, 0x03, 0x29, 0x46, 0x86, 0xfe, 0xf8, 0x98, 0xd8, 0xe0, 0x93, - 0x6b, 0x6d, 0x68, 0x96, 0x3f, 0x7f, 0x9a, 0xe4, 0xe9, 0x66, 0xb2, 0xe8, - 0x6b, 0x3f, 0x69, 0xb9, 0x4c, 0x67, 0xc7, 0x0c, 0x56, 0xbb, 0xd9, 0xaf, - 0x7a, 0x86, 0x8b, 0x3a, 0x38, 0x07, 0xa5, 0xfe, 0x80, 0x7b, 0x85, 0xe0, - 0xd6, 0x36, 0xdb, 0x6d, 0x0e, 0xff, 0xf1, 0x50, 0x80, 0x36, 0x9f, 0xfc, - 0x20, 0x84, 0x4c, 0xc7, 0x6d, 0x08, 0xa0, 0xd6, 0x63, 0x96, 0x76, 0x9b, - 0xbb, 0x34, 0x96, 0x9c, 0x00, 0xfb, 0xfa, 0xfd, 0xd6, 0x56, 0x67, 0xec, - 0x3e, 0x3d, 0xf8, 0xbb, 0xee, 0xfe, 0x3f, 0x60, 0x71, 0x99, 0xac, 0xe0, - 0x57, 0x01, 0xe0, 0x64, 0x25, 0x82, 0x5f, 0x04, 0x45, 0xc6, 0xa4, 0xe1, - 0x85, 0x53, 0x23, 0x58, 0x07, 0xc4, 0xbf, 0x96, 0xfb, 0x7b, 0xa1, 0xbd, - 0x80, 0x76, 0xe8, 0x4d, 0x10, 0x94, 0xb5, 0x62, 0x9b, 0x1d, 0xb5, 0x76, - 0xfb, 0xc9, 0x7c, 0xb3, 0xb5, 0x9a, 0xca, 0x6d, 0x4c, 0x86, 0x11, 0x64, - 0x67, 0x01, 0x1a, 0x54, 0x0d, 0x4e, 0x6d, 0x9d, 0xb8, 0x9f, 0x5e, 0xd3, - 0x24, 0x21, 0x34, 0x96, 0xfa, 0xab, 0x49, 0x2f, 0x32, 0x40, 0x06, 0xc9, - 0x86, 0xc4, 0xd7, 0x66, 0x5b, 0xe9, 0xf6, 0x2d, 0x99, 0xe7, 0x9e, 0x6a, - 0x07, 0x2e, 0x79, 0xde, 0x97, 0x65, 0x97, 0x54, 0x44, 0x44, 0x44, 0x1a, - 0xae, 0x72, 0xc8, 0x96, 0x94, 0xf4, 0x73, 0xec, 0x21, 0x28, 0x90, 0x8a, - 0x90, 0xdf, 0xa8, 0x27, 0x83, 0xb7, 0x90, 0x03, 0xcf, 0xe4, 0x72, 0xdc, - 0x95, 0x49, 0xf1, 0x3a, 0x5a, 0xb8, 0x4d, 0x86, 0xef, 0xf1, 0xd5, 0x63, - 0x1e, 0xdf, 0xae, 0x4b, 0xfa, 0x71, 0xd3, 0xeb, 0xaa, 0xcf, 0x60, 0x6f, - 0xba, 0x3b, 0xae, 0xa9, 0x8a, 0x33, 0xf0, 0xcf, 0x71, 0x70, 0xa5, 0x7a, - 0xaf, 0x36, 0xa4, 0xc7, 0x49, 0xc5, 0xe9, 0xae, 0xd6, 0xac, 0xce, 0x8b, - 0x5d, 0xaa, 0x48, 0x83, 0x75, 0xf8, 0x3d, 0xef, 0x47, 0x8e, 0xe9, 0x0a, - 0xa5, 0xad, 0x2a, 0xf5, 0xac, 0x8c, 0x94, 0x58, 0x89, 0x1a, 0xc4, 0x00, - 0x4a, 0x2e, 0xf5, 0xdf, 0x9e, 0x66, 0xb9, 0xd4, 0xf1, 0xd7, 0x5e, 0x7b, - 0xc5, 0xdd, 0x5e, 0xeb, 0xac, 0xf8, 0xe7, 0x80, 0x89, 0xd7, 0x61, 0xe4, - 0xfe, 0x27, 0xaf, 0xfb, 0x0d, 0xdf, 0x4b, 0x2e, 0xba, 0x89, 0x79, 0xe7, - 0xd2, 0x59, 0xe2, 0x11, 0x6c, 0x20, 0x73, 0x56, 0x99, 0x78, 0x53, 0xc2, - 0x81, 0x84, 0x83, 0x2c, 0xba, 0xc8, 0xcc, 0xd1, 0xd4, 0xa5, 0x75, 0x59, - 0x6f, 0x99, 0xbb, 0xe1, 0x4a, 0xb9, 0x1c, 0x0b, 0x4b, 0x45, 0xe9, 0x0e, - 0x46, 0x2b, 0x3f, 0xc2, 0xa1, 0x15, 0xb5, 0x03, 0x0d, 0xd1, 0x38, 0x70, - 0xb6, 0xb7, 0xd0, 0x3b, 0xe7, 0x26, 0x77, 0x83, 0x34, 0x60, 0x21, 0x98, - 0x91, 0xa8, 0xdd, 0x75, 0x54, 0x49, 0x7a, 0x8d, 0xd4, 0x97, 0xb1, 0x7c, - 0x8b, 0x23, 0xbb, 0x81, 0x65, 0x31, 0x1e, 0x16, 0x66, 0x47, 0x4c, 0x66, - 0xe0, 0x69, 0x22, 0x8c, 0x4b, 0xd9, 0x5c, 0x8a, 0x49, 0x7c, 0x93, 0x85, - 0xcb, 0x1b, 0x03, 0x75, 0xd8, 0x8c, 0xe3, 0x77, 0x91, 0xd4, 0x98, 0x39, - 0x1d, 0xd4, 0x99, 0xe2, 0xfb, 0x86, 0xbc, 0x07, 0x03, 0x13, 0x49, 0x52, - 0x91, 0xd3, 0x05, 0x91, 0xe5, 0x36, 0x6e, 0x4a, 0x8b, 0xc9, 0x82, 0x7b, - 0xc5, 0x0e, 0x39, 0x52, 0xb0, 0x6b, 0x73, 0x66, 0x21, 0x08, 0x61, 0x02, - 0xf7, 0xbb, 0x2d, 0xca, 0xbb, 0xd0, 0x50, 0x9a, 0x97, 0x9a, 0xc8, 0x28, - 0xca, 0xc9, 0x2e, 0x31, 0xb2, 0xba, 0x06, 0xdb, 0xf8, 0xff, 0xf1, 0x50, - 0x80, 0x37, 0xdf, 0xfc, 0x20, 0x90, 0x7a, 0xd6, 0x86, 0x2a, 0x59, 0x42, - 0x87, 0x00, 0x03, 0xe3, 0xd5, 0x6b, 0xdb, 0xed, 0xfa, 0xef, 0xdb, 0xdf, - 0xcc, 0x9d, 0x7b, 0xf1, 0x5a, 0xbd, 0xef, 0xaa, 0xe3, 0x2b, 0x6e, 0x47, - 0x22, 0x90, 0x84, 0x2e, 0x8a, 0xa2, 0xcf, 0xdb, 0xa4, 0x82, 0xcd, 0x71, - 0xe0, 0xf5, 0x88, 0xae, 0x17, 0xc4, 0x39, 0x32, 0x1d, 0x8b, 0x4c, 0xee, - 0xb5, 0xd0, 0x6b, 0xb3, 0x09, 0x8a, 0xd7, 0xaf, 0xc3, 0x62, 0xb0, 0xf8, - 0xf5, 0xd5, 0x73, 0xb7, 0x32, 0xef, 0x9a, 0x64, 0x50, 0xd8, 0xee, 0xd1, - 0xd1, 0x4f, 0x47, 0x3a, 0x24, 0xba, 0xb0, 0x2a, 0x4e, 0x2c, 0xd3, 0xbb, - 0xca, 0xc0, 0x78, 0xd9, 0xa6, 0x44, 0x5d, 0xcd, 0x13, 0x03, 0xaf, 0xab, - 0x09, 0x64, 0x1b, 0xfd, 0x9f, 0xb0, 0xf6, 0xdb, 0xc5, 0x6a, 0x97, 0x55, - 0x85, 0x15, 0x43, 0x92, 0x55, 0x64, 0xe7, 0x7c, 0x34, 0xd5, 0xb3, 0x04, - 0xa4, 0xc5, 0x49, 0x09, 0xb3, 0xab, 0xda, 0x5e, 0x74, 0x4e, 0x80, 0x5c, - 0xe5, 0x6d, 0x0b, 0xe6, 0xbd, 0x36, 0x8f, 0xb3, 0x50, 0x6c, 0xa5, 0xa5, - 0x3d, 0x40, 0xb2, 0x4a, 0xb6, 0x18, 0xce, 0x82, 0x4a, 0x4a, 0xae, 0x8a, - 0xac, 0xe2, 0x27, 0x3e, 0x11, 0x76, 0x9c, 0x7a, 0x5d, 0xb1, 0x5c, 0x7a, - 0xfc, 0x1e, 0x87, 0x5d, 0xa2, 0xff, 0xd2, 0x03, 0xf3, 0xdf, 0x92, 0x44, - 0x6a, 0xac, 0xfb, 0xf7, 0xc7, 0x96, 0x78, 0x74, 0xa4, 0x52, 0x08, 0xd4, - 0x5b, 0xce, 0xf3, 0x42, 0xf1, 0x9c, 0x10, 0xdb, 0x87, 0x52, 0x9e, 0xa1, - 0x24, 0x46, 0x08, 0xe2, 0x39, 0x3d, 0x74, 0xb5, 0xde, 0x47, 0x14, 0x61, - 0x99, 0x31, 0xad, 0x65, 0x84, 0x27, 0x80, 0x1a, 0xdc, 0xad, 0x7d, 0x78, - 0xdf, 0x1f, 0x5d, 0xfd, 0x78, 0xf3, 0xe2, 0x5d, 0x71, 0x49, 0x53, 0x2a, - 0xe5, 0x58, 0x0a, 0xf6, 0xec, 0x2e, 0xb0, 0xf6, 0xbd, 0xc3, 0x6f, 0x06, - 0x3c, 0xb9, 0xfe, 0x56, 0x23, 0x5b, 0x39, 0x21, 0xf0, 0x66, 0x9d, 0xf1, - 0x8c, 0x28, 0x81, 0x91, 0x2d, 0x66, 0x0b, 0x5c, 0xb5, 0xd5, 0xad, 0x7c, - 0xed, 0x5a, 0xd6, 0xb9, 0x76, 0x2b, 0x43, 0xff, 0xea, 0xa1, 0xd7, 0x7e, - 0x9b, 0x70, 0xd7, 0x4b, 0xa2, 0x78, 0x17, 0xe2, 0x95, 0x6a, 0x7a, 0xdc, - 0x5a, 0xeb, 0x42, 0xf6, 0x31, 0xd9, 0xb2, 0x97, 0x0a, 0x2d, 0x99, 0xa6, - 0x35, 0x1a, 0xd9, 0x5e, 0x4b, 0x14, 0xe0, 0xbc, 0x43, 0x9b, 0xa8, 0xc9, - 0xa6, 0x75, 0x8b, 0x12, 0x5c, 0xa6, 0x8b, 0x01, 0x18, 0xec, 0x66, 0x46, - 0x38, 0x83, 0xb1, 0xaf, 0x60, 0xa0, 0x66, 0x0f, 0x5a, 0x1f, 0x1e, 0xa1, - 0x35, 0x5c, 0x86, 0xa8, 0x1a, 0x1b, 0x9a, 0x3b, 0xdf, 0xea, 0xd3, 0xd8, - 0xf5, 0x94, 0x52, 0x7e, 0x8c, 0xe2, 0xdf, 0xad, 0x12, 0x08, 0x37, 0x00, - 0xcb, 0x31, 0xe4, 0x43, 0x5a, 0xeb, 0x49, 0x82, 0x85, 0xa7, 0xbf, 0xf8, - 0x36, 0xcc, 0x78, 0x0a, 0x9e, 0x77, 0x9b, 0xfc, 0x24, 0xf2, 0x86, 0x3e, - 0xfb, 0xd6, 0xf5, 0xef, 0x45, 0x5f, 0x2e, 0xb2, 0xda, 0xc8, 0x8e, 0x93, - 0x84, 0x17, 0x9f, 0x9b, 0x7e, 0xba, 0xd3, 0x41, 0x25, 0x05, 0xeb, 0x48, - 0xdc, 0x41, 0x9c, 0x9a, 0xed, 0xc7, 0xd4, 0xff, 0x0d, 0xd5, 0xf0, 0xff, - 0xf1, 0x50, 0x80, 0x22, 0x9f, 0xfc, 0x21, 0x1a, 0xcf, 0xde, 0xfb, 0xce, - 0x00, 0x00, 0x90, 0xb4, 0x23, 0x48, 0xc6, 0x42, 0x32, 0x14, 0x42, 0x01, - 0x26, 0x5e, 0x75, 0xed, 0x5e, 0xbe, 0xaf, 0xed, 0xfc, 0xfc, 0xf5, 0x99, - 0xc6, 0x79, 0xef, 0x2b, 0x8c, 0x95, 0xae, 0x2f, 0x6d, 0xd6, 0xc6, 0xeb, - 0x25, 0x04, 0x56, 0xf4, 0x49, 0xb3, 0xa8, 0x2c, 0xd7, 0xde, 0x77, 0x0b, - 0xad, 0xdb, 0x34, 0x3a, 0x99, 0x89, 0xc8, 0xaf, 0xf4, 0x4d, 0x4c, 0xd4, - 0x5e, 0x36, 0xab, 0xbf, 0xf6, 0x50, 0xc7, 0xdc, 0x68, 0xb2, 0x12, 0xf5, - 0x82, 0x40, 0x2b, 0x08, 0x0b, 0x54, 0x6e, 0xfd, 0x80, 0x16, 0xf8, 0xc4, - 0xaa, 0x74, 0xfe, 0x2d, 0x51, 0xfa, 0xcf, 0x08, 0x30, 0x76, 0xc8, 0xfd, - 0xd7, 0x67, 0x61, 0xda, 0x11, 0xa7, 0xd8, 0x9c, 0xf9, 0xf9, 0x41, 0xfd, - 0x15, 0xe5, 0x92, 0x2c, 0x4e, 0x2d, 0x84, 0xed, 0x90, 0xec, 0x42, 0xd7, - 0x84, 0x27, 0x3a, 0xb7, 0xb3, 0x12, 0x29, 0x8b, 0xd8, 0x0e, 0xd8, 0xce, - 0x93, 0x52, 0x03, 0x7a, 0x96, 0x73, 0xd2, 0x2a, 0xe8, 0xcb, 0x10, 0x55, - 0x89, 0xf3, 0x39, 0x18, 0x3b, 0x0e, 0xb3, 0x9b, 0xd7, 0x1d, 0xc9, 0x6d, - 0x4b, 0xd4, 0x32, 0xc0, 0x59, 0x09, 0x05, 0x67, 0xa1, 0xc0, 0x73, 0x7c, - 0xca, 0x50, 0xd1, 0x4a, 0x80, 0x14, 0x1a, 0xf1, 0x9c, 0xca, 0x37, 0x36, - 0x30, 0x4a, 0x1d, 0x8e, 0x11, 0x55, 0x13, 0x4b, 0x1c, 0xfb, 0x56, 0x92, - 0x29, 0x06, 0x31, 0xb9, 0x06, 0x23, 0xf8, 0x82, 0xa2, 0x4d, 0x6f, 0x3a, - 0xf6, 0xa5, 0xdf, 0xdb, 0xf9, 0xdd, 0x66, 0x71, 0x2e, 0x95, 0x51, 0x53, - 0x55, 0x8c, 0xba, 0x10, 0x9d, 0xdd, 0x79, 0x6b, 0xcf, 0xe4, 0xe5, 0xa6, - 0xa5, 0x0f, 0xaa, 0x5c, 0xc9, 0xfe, 0xc8, 0xd4, 0x16, 0xdf, 0x68, 0x62, - 0x70, 0xf0, 0xec, 0x69, 0x18, 0x69, 0x4c, 0x93, 0xa3, 0xe1, 0x7c, 0x4b, - 0xa8, 0xf8, 0x70, 0xe6, 0x7c, 0xcf, 0x28, 0x2d, 0x16, 0x16, 0x7e, 0xff, - 0xf1, 0x50, 0x80, 0x26, 0xbf, 0xfc, 0x21, 0x1a, 0xcf, 0xe7, 0xbe, 0xfc, - 0x00, 0x00, 0x90, 0xb3, 0x31, 0xd0, 0xca, 0x14, 0xb8, 0x1a, 0xab, 0xba, - 0x9d, 0xfc, 0x4e, 0xbd, 0xf5, 0xc7, 0xaf, 0x89, 0x7e, 0xfd, 0x49, 0xae, - 0x66, 0x2c, 0x93, 0x83, 0x39, 0xd8, 0xcf, 0xa4, 0x4a, 0x4b, 0x4d, 0x32, - 0xc0, 0x78, 0x66, 0x17, 0x86, 0x40, 0x9c, 0xce, 0x5b, 0xf1, 0xeb, 0x27, - 0x7c, 0xcb, 0x48, 0xce, 0xa8, 0xcb, 0x71, 0x9a, 0x04, 0x98, 0xcb, 0xa3, - 0xda, 0xba, 0x59, 0x0a, 0xb1, 0x1a, 0x46, 0x8b, 0x90, 0x01, 0xe5, 0x58, - 0xf4, 0x40, 0x63, 0x98, 0x0a, 0x59, 0x32, 0x58, 0x74, 0x78, 0x2b, 0x15, - 0x78, 0xf1, 0x77, 0x7c, 0x69, 0x08, 0x01, 0x4e, 0x85, 0x0e, 0x71, 0x40, - 0x21, 0xac, 0xf9, 0xb0, 0x70, 0xe5, 0x14, 0xb7, 0xab, 0xa7, 0xf6, 0xed, - 0x79, 0x1a, 0xc9, 0x8d, 0xb7, 0x0d, 0x07, 0x10, 0x76, 0x15, 0x90, 0x9b, - 0x11, 0x00, 0xbc, 0x73, 0xca, 0xf4, 0xc5, 0x0e, 0x1a, 0x7b, 0x94, 0xb4, - 0xbb, 0xaa, 0x2a, 0x99, 0xc3, 0xd1, 0xce, 0x9c, 0xaa, 0x8c, 0xc6, 0xb9, - 0x4b, 0x7e, 0xa6, 0x75, 0xa7, 0x9f, 0xbc, 0xbb, 0x65, 0xb0, 0xcb, 0x00, - 0x1a, 0xaf, 0x13, 0xc3, 0xa4, 0x04, 0xf5, 0x64, 0x78, 0x48, 0xf3, 0xe0, - 0xfe, 0x3f, 0x22, 0x49, 0xcf, 0x35, 0x98, 0xb2, 0xeb, 0x07, 0x08, 0xac, - 0x0f, 0xfb, 0x4d, 0x5c, 0x5a, 0x80, 0x7c, 0xa7, 0x15, 0x01, 0x32, 0xbf, - 0xd4, 0x0f, 0xa7, 0xd1, 0x66, 0x36, 0x77, 0xe4, 0x65, 0x38, 0x7b, 0xb7, - 0x5b, 0xa4, 0x15, 0x6e, 0x11, 0x08, 0x8f, 0x05, 0x52, 0x39, 0x98, 0xfa, - 0xf2, 0x10, 0x45, 0xb7, 0xc4, 0x09, 0x9d, 0x5d, 0x4f, 0xb7, 0xef, 0x3a, - 0xf7, 0xd7, 0xc7, 0xe7, 0xe2, 0x5f, 0xbf, 0x5b, 0x97, 0x93, 0x2a, 0x4a, - 0xb9, 0x45, 0x55, 0x05, 0x92, 0xd6, 0x55, 0xb3, 0x2a, 0x2e, 0xea, 0x0f, - 0xa3, 0xe7, 0x1b, 0x50, 0xdc, 0xa6, 0xce, 0xff, 0xd2, 0xe4, 0x04, 0xea, - 0x7b, 0x30, 0x02, 0x4e, 0x7d, 0xd7, 0x9e, 0x9a, 0x74, 0x84, 0x5d, 0x2d, - 0x10, 0xbc, 0x5f, 0x5d, 0x66, 0x77, 0x7a, 0xee, 0x8a, 0x18, 0x01, 0xa2, - 0xd7, 0x78, 0x9a, 0x4d, 0xce, 0x21, 0x25, 0xf0, 0xff, 0xf1, 0x50, 0x80, - 0x28, 0x9f, 0xfc, 0x21, 0x1a, 0xcf, 0xfb, 0xf7, 0x59, 0x00, 0x00, 0x90, - 0xb2, 0xb3, 0x92, 0x4c, 0x34, 0x18, 0x95, 0xe5, 0x9a, 0x5d, 0x7b, 0xfd, - 0x6b, 0xcf, 0xbb, 0xef, 0xf6, 0xf8, 0xdd, 0xfa, 0xeb, 0xcf, 0x77, 0x93, - 0x13, 0x2f, 0x4d, 0x43, 0xde, 0x05, 0x29, 0x98, 0x3e, 0xb3, 0x39, 0xe3, - 0x5d, 0x6c, 0xaa, 0xa1, 0x1e, 0xa4, 0xc2, 0xc3, 0xb2, 0xa9, 0x2c, 0x93, - 0x43, 0x01, 0x5d, 0x85, 0x6e, 0x92, 0x85, 0xa5, 0x54, 0x91, 0x06, 0xea, - 0x43, 0x13, 0x16, 0x02, 0xad, 0x06, 0x86, 0x40, 0x94, 0x22, 0x6d, 0x67, - 0xef, 0x9e, 0xdf, 0x7f, 0xaf, 0x4b, 0x2e, 0x3c, 0x37, 0xad, 0xbe, 0xa5, - 0x2a, 0xb1, 0xac, 0xb7, 0xb5, 0xa5, 0x36, 0xbf, 0x1f, 0xfa, 0x7f, 0x16, - 0x5f, 0x41, 0x57, 0xb3, 0x0e, 0xed, 0x75, 0x16, 0xa0, 0x8b, 0xe8, 0x6e, - 0x51, 0xd2, 0x49, 0x84, 0xbe, 0x66, 0x4c, 0xda, 0x90, 0x11, 0xa8, 0x8a, - 0x06, 0xaa, 0xd0, 0x77, 0x07, 0x49, 0x96, 0x44, 0x2e, 0x0d, 0x75, 0x3d, - 0x0b, 0xb8, 0x22, 0xce, 0x22, 0x0f, 0x8c, 0x47, 0x46, 0x58, 0xe6, 0x71, - 0x58, 0xd9, 0xee, 0x58, 0xe6, 0x9f, 0x1c, 0x00, 0xee, 0x27, 0xa5, 0x5a, - 0xad, 0x4c, 0x02, 0x4c, 0x93, 0xaa, 0x11, 0xe6, 0xb7, 0x46, 0xb7, 0x38, - 0xef, 0xeb, 0x84, 0x30, 0x07, 0xbe, 0xc2, 0x42, 0xbc, 0xe2, 0xbb, 0x5c, - 0x4a, 0x51, 0x7b, 0x91, 0x68, 0x43, 0x19, 0x26, 0x7b, 0x27, 0xb4, 0xce, - 0xd2, 0x4b, 0x6f, 0x8c, 0x8d, 0xd9, 0x37, 0xde, 0xbe, 0x99, 0xd6, 0xdb, - 0x52, 0x3f, 0x8c, 0x5c, 0xa4, 0xca, 0x7c, 0x07, 0x52, 0xab, 0x8e, 0x15, - 0x3a, 0xca, 0x35, 0xe9, 0x53, 0x90, 0x82, 0xad, 0xbe, 0x02, 0x57, 0x96, - 0x5c, 0xba, 0xf7, 0xfa, 0xd5, 0xf2, 0xfb, 0xff, 0x5f, 0xde, 0x67, 0xa7, - 0x96, 0xb2, 0x55, 0x65, 0xc9, 0x53, 0x2e, 0x6e, 0x05, 0x8a, 0x66, 0x33, - 0x50, 0xbf, 0xa5, 0x28, 0x0d, 0x57, 0xc2, 0xfd, 0x53, 0xed, 0x2e, 0x78, - 0xf4, 0x4c, 0x49, 0x5e, 0xbe, 0x8d, 0x49, 0x6c, 0xb1, 0x1c, 0xe1, 0x2d, - 0x01, 0x52, 0xaa, 0x5f, 0xb0, 0x33, 0x71, 0xd9, 0x57, 0xdb, 0xb4, 0x5d, - 0xeb, 0xe3, 0x16, 0x62, 0x74, 0x1c, 0xc0, 0xa3, 0x09, 0x82, 0x6a, 0x39, - 0x4d, 0x25, 0x50, 0x1c, 0x26, 0xc8, 0x8a, 0x57, 0xff, 0xf1, 0x50, 0x80, - 0x29, 0x1f, 0xfc, 0x21, 0x1a, 0xcf, 0x77, 0xbe, 0x75, 0x00, 0x00, 0x90, - 0xa2, 0xb2, 0x51, 0xaa, 0x32, 0x2a, 0x18, 0x84, 0x13, 0xf5, 0xcd, 0xdd, - 0xd4, 0xfd, 0x3f, 0xc1, 0x9a, 0xcd, 0xfe, 0x3c, 0x6b, 0xd6, 0xbc, 0xf7, - 0xe7, 0xc4, 0xe6, 0x2a, 0xfa, 0xad, 0x31, 0xea, 0x02, 0x3a, 0x6f, 0x43, - 0x5f, 0xc7, 0x3b, 0x24, 0x90, 0x96, 0x90, 0x6f, 0xeb, 0xb2, 0x31, 0x19, - 0x37, 0x6c, 0x3a, 0xf1, 0x83, 0x88, 0xdc, 0x20, 0x5e, 0x2c, 0xf3, 0x1d, - 0xc1, 0x67, 0x69, 0x05, 0x11, 0xfe, 0xb4, 0x6a, 0xff, 0xbd, 0xce, 0x70, - 0x57, 0x08, 0x76, 0xcd, 0x65, 0xa3, 0xb8, 0xc5, 0x49, 0x7b, 0x0a, 0x41, - 0x01, 0xfb, 0xff, 0xb2, 0xce, 0xf5, 0x6f, 0x84, 0xed, 0x5e, 0xb0, 0xd9, - 0x7d, 0x39, 0xbd, 0x7f, 0x0e, 0x53, 0xb6, 0xd4, 0x9a, 0xb0, 0x09, 0xba, - 0xd4, 0x70, 0xe6, 0x8d, 0x7a, 0x1d, 0x08, 0xbc, 0x18, 0xb6, 0x20, 0xad, - 0x4c, 0xde, 0x91, 0xa6, 0x2a, 0xae, 0xa7, 0x3b, 0x0a, 0xe0, 0x47, 0xf1, - 0x5c, 0xab, 0x7c, 0x57, 0x00, 0xa3, 0x41, 0x8c, 0xf9, 0x04, 0x8c, 0x14, - 0x7d, 0x0b, 0x1d, 0x6c, 0x62, 0xc3, 0x24, 0xed, 0xe6, 0x80, 0xe7, 0xf4, - 0xff, 0xb1, 0x25, 0xc8, 0x2f, 0x41, 0x51, 0xbc, 0x53, 0x39, 0xc0, 0xd4, - 0x6c, 0x84, 0xae, 0xa6, 0xae, 0x9c, 0xb9, 0x7e, 0x38, 0x83, 0x6b, 0xaf, - 0x9e, 0x7d, 0x12, 0x6a, 0x02, 0x5b, 0x8a, 0x09, 0x7e, 0xa3, 0x2d, 0x5f, - 0x6c, 0x94, 0x67, 0x30, 0x5b, 0x98, 0x83, 0x98, 0xc7, 0x3b, 0xe5, 0x43, - 0xa0, 0xf1, 0x2a, 0x85, 0x48, 0x43, 0x50, 0xac, 0x32, 0x22, 0x0f, 0xde, - 0x13, 0xf5, 0xca, 0x5d, 0x4f, 0xdb, 0xfd, 0x0c, 0xd7, 0x1f, 0x3f, 0x8f, - 0x1a, 0xe4, 0x8d, 0x55, 0x64, 0xab, 0x92, 0x98, 0xa8, 0x1c, 0x47, 0xac, - 0xe4, 0x8a, 0xae, 0xab, 0x20, 0xe5, 0x3f, 0x82, 0xc8, 0xbd, 0xe9, 0x22, - 0x30, 0x94, 0xe8, 0x09, 0x70, 0x84, 0x67, 0x2b, 0xfc, 0x46, 0x50, 0x24, - 0xba, 0x82, 0x3d, 0xea, 0xb0, 0xec, 0x2b, 0x7a, 0x4d, 0x31, 0xe8, 0x1e, - 0x89, 0x90, 0x21, 0x07, 0x0c, 0xe7, 0x41, 0x85, 0x7e, 0x3c, 0x9b, 0x42, - 0xe4, 0x73, 0xca, 0xa4, 0xbc, 0x20, 0x64, 0x00, 0x23, 0x78, 0x83, 0x21, - 0xb5, 0x9e, 0x5c, 0x55, 0x88, 0x85, 0xce, 0x3c, 0xbe, 0x1b, 0x7d, 0x0e, - 0xff, 0xf1, 0x50, 0x80, 0x2b, 0x5f, 0xfc, 0x21, 0x1a, 0xcf, 0xa7, 0xb7, - 0xbf, 0x80, 0x40, 0x90, 0xb3, 0x33, 0x13, 0x21, 0x7f, 0x8a, 0x4b, 0xc7, - 0xcf, 0xef, 0x95, 0x1d, 0x78, 0xe2, 0x6b, 0xc2, 0x4c, 0xa0, 0x5e, 0xab, - 0x4d, 0xd6, 0xc1, 0xbd, 0xaa, 0xf1, 0x45, 0x83, 0x29, 0x56, 0xe3, 0x0c, - 0xd5, 0x57, 0xd2, 0xc5, 0xb3, 0x6d, 0x7a, 0xb3, 0xdd, 0xb7, 0x85, 0xb2, - 0x42, 0x6a, 0x09, 0x38, 0x82, 0xfc, 0x2a, 0xc6, 0x85, 0x09, 0x25, 0xa2, - 0x24, 0x74, 0x13, 0x68, 0x94, 0x6a, 0xc1, 0x4e, 0x1f, 0x88, 0xea, 0xdd, - 0xd8, 0x9d, 0xc0, 0xbe, 0x97, 0x02, 0x1d, 0x47, 0x1f, 0xcf, 0x90, 0x7d, - 0xc3, 0x69, 0xdd, 0x93, 0x6b, 0xa2, 0x85, 0x14, 0x59, 0x9b, 0x00, 0x50, - 0x20, 0x7a, 0x83, 0x57, 0xca, 0x83, 0x06, 0xe0, 0x54, 0xe4, 0x90, 0x36, - 0x26, 0x29, 0x9d, 0x05, 0x8f, 0x63, 0x42, 0xad, 0x46, 0xa3, 0x77, 0x59, - 0x0c, 0xa4, 0x20, 0x94, 0x0f, 0x7f, 0x04, 0x25, 0xeb, 0x4f, 0x6d, 0x35, - 0xba, 0x0c, 0xe8, 0x24, 0xbf, 0x51, 0x46, 0x48, 0x2c, 0x9b, 0xd2, 0x5b, - 0xdc, 0x0d, 0x37, 0x82, 0x4a, 0xcd, 0xdc, 0x46, 0x48, 0x6c, 0x72, 0x57, - 0x4f, 0xde, 0x25, 0x21, 0x4f, 0x20, 0x4e, 0x3e, 0xf2, 0xa5, 0xe7, 0x63, - 0x6f, 0xa8, 0x37, 0x5e, 0x94, 0xfe, 0x08, 0x2d, 0x18, 0xa9, 0x14, 0x6f, - 0x85, 0x3c, 0x64, 0x53, 0x7c, 0x5d, 0xcc, 0xe7, 0xdd, 0xc0, 0x8d, 0x32, - 0x5a, 0x25, 0x68, 0xf3, 0xf0, 0xb7, 0xc5, 0x9c, 0x94, 0x21, 0xa0, 0xcc, - 0x73, 0x54, 0xed, 0xb0, 0x96, 0xbe, 0x79, 0x8c, 0x90, 0x84, 0xb3, 0x10, - 0xa6, 0x2f, 0x42, 0x0b, 0xca, 0x17, 0xf8, 0x6f, 0x57, 0x8f, 0xe3, 0xfd, - 0xb3, 0xcf, 0x87, 0x5e, 0x3d, 0xb3, 0x5e, 0x12, 0x65, 0xb1, 0x4e, 0x26, - 0xb7, 0xb9, 0x4a, 0x0e, 0x6a, 0x5c, 0xd0, 0xda, 0x18, 0x5d, 0x27, 0x46, - 0x40, 0x39, 0x2a, 0xf3, 0x28, 0xc4, 0xca, 0x25, 0x0b, 0x2e, 0xb6, 0x48, - 0x10, 0xe7, 0xfd, 0x4d, 0x5d, 0xa4, 0xc6, 0x00, 0x76, 0xbf, 0xd2, 0x10, - 0x8e, 0xd7, 0x01, 0x00, 0x4a, 0x6e, 0x72, 0x1a, 0x79, 0xda, 0x80, 0x31, - 0x35, 0xa1, 0x74, 0xdd, 0x68, 0x22, 0x9e, 0x08, 0x51, 0x16, 0xe0, 0xb3, - 0xbd, 0x1b, 0xe7, 0x23, 0xa0, 0x29, 0x8b, 0x13, 0x57, 0xc4, 0x85, 0xba, - 0x9d, 0xd8, 0x91, 0x9e, 0xba, 0xd7, 0xd8, 0x00, 0x97, 0x6d, 0xec, 0x8c, - 0x23, 0xab, 0x0d, 0xf2, 0xeb, 0xbb, 0xf8, 0xd1, 0xb1, 0xc0, 0xff, 0xf1, - 0x50, 0x80, 0x2f, 0xff, 0xfc, 0x21, 0x1a, 0xcf, 0x73, 0xbb, 0xbe, 0x80, - 0x00, 0x8d, 0xb4, 0x43, 0x58, 0xc9, 0x01, 0xc5, 0x75, 0xc5, 0x5a, 0x67, - 0xdb, 0xf7, 0x4d, 0x77, 0x7b, 0xfb, 0xfb, 0xca, 0x6a, 0x6e, 0xa5, 0x2a, - 0x13, 0x89, 0xbc, 0xaa, 0x0e, 0x4f, 0x70, 0xb7, 0x87, 0xa8, 0x60, 0x1a, - 0xda, 0x0c, 0xe9, 0x50, 0x2e, 0xca, 0x44, 0xb3, 0x13, 0xe5, 0x86, 0x10, - 0x62, 0xff, 0xaf, 0xc2, 0xe8, 0x08, 0xb6, 0x31, 0x78, 0x9a, 0x50, 0x39, - 0x25, 0x90, 0xfd, 0x34, 0x1b, 0x3b, 0xd4, 0x1b, 0x02, 0x64, 0x4b, 0x73, - 0x25, 0xd6, 0xcc, 0x18, 0x8e, 0x63, 0x00, 0x4d, 0x90, 0x57, 0x8e, 0xa0, - 0xe6, 0x32, 0x90, 0x79, 0xfa, 0xd8, 0x13, 0x0a, 0x9a, 0xcc, 0xa9, 0x36, - 0x32, 0x88, 0x74, 0x54, 0x35, 0x1d, 0xf2, 0x40, 0xa9, 0xbd, 0x28, 0x6f, - 0x38, 0x05, 0x2e, 0x15, 0xc4, 0x08, 0x3c, 0x01, 0x05, 0x34, 0x3c, 0x11, - 0x04, 0xf1, 0x1b, 0x56, 0x20, 0x59, 0x54, 0x06, 0x39, 0x85, 0x0e, 0x09, - 0x71, 0x5f, 0xb7, 0x8c, 0x6b, 0xb0, 0xd2, 0x86, 0xee, 0x35, 0x47, 0x03, - 0xe8, 0xc8, 0xf8, 0x4a, 0x8a, 0xf8, 0x11, 0x96, 0x27, 0xf0, 0x42, 0xfd, - 0x9c, 0x99, 0xde, 0xe9, 0x4c, 0xe8, 0x84, 0xc3, 0xfe, 0x63, 0x98, 0x10, - 0x19, 0x4f, 0x88, 0x83, 0x96, 0x06, 0x7e, 0xb0, 0xb5, 0x1d, 0x19, 0x02, - 0x6a, 0x5b, 0x63, 0x26, 0xf6, 0x1a, 0x9c, 0xbb, 0xbe, 0xb2, 0xb0, 0x39, - 0x9e, 0x3c, 0x02, 0x7c, 0xd7, 0x89, 0x27, 0xf5, 0x13, 0x5e, 0x9f, 0x4d, - 0x90, 0xf2, 0x51, 0x7d, 0x34, 0x13, 0x9d, 0xc5, 0x20, 0x77, 0x81, 0xfa, - 0x43, 0x3f, 0xaf, 0x4f, 0xd8, 0x8b, 0xcb, 0xd9, 0xb7, 0xdf, 0xb7, 0x3d, - 0x6d, 0x3f, 0x23, 0x6c, 0x71, 0x33, 0x3c, 0x48, 0x8f, 0x8f, 0x84, 0xc9, - 0xea, 0xe7, 0xaf, 0xcd, 0xa2, 0xef, 0x04, 0x1d, 0x8d, 0x87, 0x2a, 0x90, - 0x9e, 0x94, 0x0f, 0x94, 0x4b, 0xeb, 0x8e, 0x75, 0x73, 0x3f, 0xb7, 0xfc, - 0xa6, 0x2f, 0x7a, 0xa9, 0x4d, 0x6e, 0x5c, 0xde, 0x4d, 0x65, 0x8c, 0x98, - 0x01, 0x87, 0x97, 0xcd, 0x55, 0x1a, 0x1d, 0x87, 0x9e, 0x51, 0x67, 0x64, - 0x9e, 0xd0, 0x9b, 0xac, 0xb1, 0x46, 0x68, 0x40, 0xc6, 0x19, 0x2a, 0x0b, - 0x85, 0xdb, 0x3c, 0x82, 0xe0, 0x36, 0x93, 0x9c, 0xc5, 0x3a, 0x3d, 0x39, - 0xfb, 0xd9, 0xbc, 0xc2, 0xf7, 0x36, 0x7c, 0x3d, 0x90, 0xc6, 0xe3, 0x1d, - 0xb3, 0x9e, 0xc5, 0x23, 0x5c, 0xca, 0x2f, 0x5b, 0xa1, 0x5f, 0x3e, 0x04, - 0xc9, 0x77, 0x9a, 0xba, 0x2b, 0x50, 0xf5, 0x16, 0xf4, 0x59, 0xbf, 0x2b, - 0xac, 0xb4, 0x5c, 0xff, 0x34, 0xc3, 0xc0, 0xc5, 0xda, 0x6e, 0x12, 0xe3, - 0xe3, 0xed, 0xf3, 0x01, 0x10, 0x8d, 0x0e, 0xa4, 0xe0, 0xff, 0xf1, 0x50, - 0x80, 0x2d, 0x7f, 0xfc, 0x21, 0x1a, 0xca, 0x9f, 0xbf, 0xef, 0x80, 0x00, - 0x90, 0xa2, 0xc3, 0xd8, 0x69, 0x43, 0xad, 0x5d, 0x74, 0x95, 0x1f, 0x9f, - 0xdd, 0x26, 0x4c, 0xd5, 0x5e, 0xe2, 0x50, 0x0b, 0xcd, 0x2b, 0x2f, 0x01, - 0x7d, 0x39, 0xde, 0x59, 0x37, 0x19, 0x45, 0x52, 0x7c, 0xc2, 0xf4, 0xe6, - 0x33, 0xb8, 0xb5, 0xd1, 0xec, 0x93, 0xd6, 0x9a, 0x25, 0xde, 0xdd, 0xa8, - 0x89, 0x47, 0x90, 0x64, 0xfe, 0x65, 0x4b, 0x42, 0x53, 0x01, 0x93, 0x96, - 0xd7, 0x81, 0x11, 0x75, 0xb5, 0x66, 0xab, 0x3d, 0x8b, 0x6b, 0x38, 0x56, - 0x81, 0x0b, 0x1c, 0x83, 0x4b, 0xe6, 0x05, 0xc3, 0x11, 0x1d, 0xe5, 0x4c, - 0x83, 0x7b, 0xb6, 0x25, 0x10, 0xa1, 0x12, 0x96, 0xe0, 0x5e, 0x4f, 0xa7, - 0x76, 0x11, 0x97, 0xe4, 0x8b, 0x54, 0xd4, 0x01, 0x0e, 0x33, 0xe6, 0x25, - 0xa2, 0xe1, 0xff, 0xc2, 0x33, 0xd8, 0x92, 0x36, 0x03, 0xe3, 0x0a, 0xe4, - 0xea, 0x57, 0x3c, 0xce, 0x50, 0x7e, 0xa0, 0x60, 0xa0, 0xef, 0x44, 0x3a, - 0x65, 0xdf, 0xaf, 0xf4, 0x62, 0xa5, 0xd0, 0xf3, 0x70, 0xbf, 0x77, 0x8c, - 0x24, 0xd6, 0x08, 0xb1, 0x43, 0xc8, 0x0a, 0x7e, 0xf0, 0x22, 0x8e, 0x9f, - 0xee, 0x47, 0xff, 0x8c, 0x11, 0xbd, 0x46, 0xe3, 0xce, 0xd3, 0x2b, 0xa3, - 0x3a, 0x73, 0x1e, 0x1a, 0x5e, 0xdd, 0x9d, 0x10, 0x01, 0x91, 0xe8, 0x8a, - 0x01, 0x9f, 0x0e, 0xcc, 0x3b, 0x41, 0x61, 0x91, 0x54, 0x72, 0xe3, 0x6f, - 0xe8, 0x3b, 0x0e, 0x52, 0x97, 0x86, 0x0c, 0x44, 0xac, 0x1b, 0x3d, 0x7a, - 0x58, 0xa8, 0xf4, 0xdb, 0x6e, 0x49, 0x66, 0x2d, 0xdc, 0x85, 0xf2, 0xfe, - 0x56, 0xbb, 0x9a, 0x5a, 0xa9, 0xd3, 0xb0, 0xbf, 0x9c, 0x93, 0x44, 0x6e, - 0xba, 0x0e, 0x67, 0xe8, 0x54, 0xef, 0x31, 0xdb, 0x66, 0x5e, 0xb7, 0x13, - 0xde, 0xe4, 0x68, 0x30, 0xc4, 0x32, 0x85, 0x11, 0xef, 0x47, 0xc6, 0xf8, - 0xe9, 0x2a, 0x3f, 0xa7, 0xfb, 0x24, 0xc9, 0x9a, 0xa9, 0x91, 0x2a, 0xd8, - 0xa9, 0x2f, 0x2d, 0xcc, 0x80, 0xe7, 0x52, 0xe0, 0xa9, 0x9e, 0x60, 0x09, - 0xf3, 0x55, 0xfe, 0xc0, 0x43, 0xb6, 0x46, 0x05, 0xe5, 0x96, 0x27, 0x29, - 0x94, 0x54, 0x4a, 0x9d, 0x0f, 0xea, 0xee, 0x98, 0xa1, 0x83, 0x18, 0xf2, - 0x03, 0xba, 0x84, 0x9c, 0x1d, 0xdd, 0x94, 0xea, 0x63, 0xd5, 0x36, 0x84, - 0x3e, 0xf2, 0xb0, 0x54, 0xcb, 0xf8, 0x4e, 0xd8, 0xae, 0x45, 0x54, 0x3f, - 0xa6, 0xa2, 0x1e, 0x2a, 0xec, 0xc1, 0xe6, 0x85, 0x02, 0xba, 0xba, 0xdb, - 0x21, 0xe7, 0x2e, 0x1b, 0x91, 0xd6, 0xee, 0xe5, 0x65, 0x93, 0x2f, 0x80, - 0xff, 0xf1, 0x50, 0x80, 0x2b, 0xbf, 0xfc, 0x21, 0x1a, 0xcf, 0x97, 0xbf, - 0x7f, 0x80, 0x00, 0x92, 0xa2, 0xc2, 0xd9, 0x29, 0x23, 0x8e, 0xa7, 0x9a, - 0x4b, 0xa7, 0xcf, 0xeb, 0x90, 0x95, 0x75, 0x2a, 0x50, 0x04, 0x49, 0x85, - 0x00, 0x3a, 0x45, 0x4a, 0xab, 0xa4, 0x35, 0x99, 0x90, 0xfe, 0x47, 0x59, - 0x8f, 0xd2, 0xd9, 0x0f, 0xa4, 0xc6, 0x8e, 0xdd, 0xcd, 0xa4, 0x4d, 0x7d, - 0x6a, 0x72, 0xd9, 0x74, 0x3e, 0x0b, 0x9b, 0x78, 0xb9, 0x2f, 0x3c, 0xcc, - 0xec, 0x94, 0xd1, 0x1d, 0xd8, 0x53, 0xcb, 0x19, 0x8f, 0xaa, 0x02, 0xf3, - 0x03, 0xc6, 0x1b, 0xf9, 0x58, 0x0c, 0x42, 0xd4, 0xc6, 0x60, 0xbc, 0xd5, - 0x24, 0x27, 0x46, 0x0a, 0x40, 0xa7, 0x51, 0x81, 0xa0, 0xea, 0x07, 0x08, - 0xa0, 0x89, 0x1c, 0x14, 0x79, 0x21, 0x68, 0x0c, 0x69, 0xdb, 0xc7, 0xbc, - 0xef, 0x3f, 0xb9, 0xa3, 0xa6, 0xae, 0x13, 0xbc, 0x28, 0x36, 0xf7, 0xa9, - 0x10, 0x51, 0xee, 0x49, 0xc2, 0x73, 0x6d, 0x86, 0xe7, 0x70, 0x95, 0x04, - 0x72, 0xd5, 0xdd, 0x55, 0x05, 0xf2, 0x3f, 0x88, 0xd9, 0x41, 0x74, 0xce, - 0x8a, 0xa8, 0x0a, 0xd4, 0xb9, 0x86, 0x4b, 0xe6, 0x76, 0x39, 0x12, 0x5e, - 0xee, 0x21, 0x2e, 0xe3, 0xf5, 0xbb, 0x39, 0x98, 0x26, 0xd7, 0x53, 0x6b, - 0xf8, 0x86, 0x26, 0xa2, 0x87, 0x6b, 0xd0, 0x41, 0x69, 0x93, 0x0f, 0x5c, - 0x38, 0xf8, 0x55, 0xc0, 0xcf, 0x0e, 0x12, 0x0d, 0x06, 0x59, 0x61, 0xf8, - 0x43, 0xb4, 0x0f, 0x26, 0xe6, 0x07, 0xbd, 0x14, 0x09, 0xbb, 0x6c, 0x81, - 0xcb, 0xd6, 0xba, 0x45, 0x3a, 0x4c, 0x2b, 0x1f, 0x6f, 0x13, 0x89, 0x1d, - 0x67, 0x63, 0x80, 0x7e, 0xd4, 0x8f, 0x38, 0xfb, 0x3f, 0x34, 0x2e, 0xc1, - 0xf2, 0x73, 0xac, 0xe7, 0x05, 0xe2, 0xde, 0x18, 0xc9, 0x28, 0x33, 0x20, - 0x8a, 0xc1, 0x44, 0x7b, 0xce, 0x3a, 0xe7, 0xdb, 0x55, 0x77, 0x4f, 0xeb, - 0xfe, 0xd9, 0x09, 0xcf, 0x55, 0x2a, 0x51, 0x15, 0x2a, 0x2e, 0xa6, 0x0d, - 0x87, 0x27, 0x60, 0x72, 0x9f, 0x86, 0x56, 0xb5, 0x1e, 0x8b, 0x05, 0x7f, - 0xfe, 0x66, 0xb4, 0xea, 0x10, 0x9a, 0x72, 0x6b, 0xd0, 0x2d, 0xff, 0x93, - 0x4a, 0xc8, 0x6a, 0x47, 0x83, 0x70, 0x12, 0x4f, 0xd7, 0x3d, 0x5b, 0xf2, - 0xfa, 0x99, 0x1a, 0x34, 0x4b, 0xb9, 0x16, 0x1a, 0xd1, 0x09, 0x6a, 0xde, - 0xb0, 0x70, 0xdc, 0x6a, 0xd1, 0xa4, 0xa9, 0xc5, 0x51, 0xc4, 0x44, 0x54, - 0xa2, 0x90, 0x13, 0xaa, 0xae, 0xaa, 0x99, 0xa5, 0xb7, 0x1b, 0x44, 0x3a, - 0x0e, 0xff, 0xf1, 0x50, 0x80, 0x2d, 0xff, 0xfc, 0x21, 0x2a, 0xcb, 0xbb, - 0x9f, 0x7f, 0x80, 0x00, 0x92, 0x87, 0xb4, 0xd1, 0x26, 0xbe, 0xfd, 0xfc, - 0x56, 0xa3, 0x77, 0xcf, 0x9c, 0xb0, 0xd6, 0xe5, 0x12, 0x80, 0x92, 0x89, - 0x40, 0x3f, 0xbf, 0xe2, 0x7b, 0x98, 0xb6, 0xff, 0xdc, 0xbe, 0xbe, 0x35, - 0x05, 0x64, 0x44, 0xd1, 0x07, 0x2a, 0x9e, 0x8c, 0xa0, 0xdb, 0x53, 0x9a, - 0x84, 0xdb, 0x33, 0x75, 0x85, 0x19, 0xb2, 0x63, 0x94, 0x28, 0x64, 0xea, - 0x8f, 0x7e, 0x1f, 0x0f, 0x0e, 0x84, 0x13, 0xf5, 0xce, 0xd3, 0xce, 0x4f, - 0x6b, 0x6f, 0xe9, 0x8c, 0x69, 0x71, 0x01, 0x54, 0xb5, 0x04, 0xd2, 0x27, - 0x82, 0xaf, 0x5c, 0x5b, 0x0a, 0x0a, 0xb9, 0x49, 0x45, 0x34, 0x61, 0x38, - 0x51, 0x08, 0x34, 0xc3, 0xdb, 0x7e, 0x38, 0x0e, 0xd8, 0xb4, 0x66, 0x9a, - 0x04, 0xc8, 0x11, 0xe5, 0x09, 0x14, 0x85, 0x0c, 0x44, 0x94, 0xcd, 0xe9, - 0x47, 0x1a, 0x48, 0xa4, 0x56, 0x22, 0x65, 0x98, 0x1c, 0x20, 0x27, 0xc0, - 0xa5, 0x4b, 0xab, 0x54, 0xb2, 0x42, 0x55, 0x82, 0xac, 0x01, 0x21, 0x5e, - 0x45, 0x62, 0x9c, 0x62, 0x12, 0xb7, 0x70, 0x3a, 0xdc, 0xe8, 0x72, 0x99, - 0x19, 0xc1, 0x2e, 0xad, 0xcd, 0x1d, 0xcc, 0xdb, 0x01, 0xc6, 0x80, 0x87, - 0x56, 0x69, 0x2d, 0x10, 0xe2, 0x41, 0x54, 0xa1, 0x22, 0x5a, 0x68, 0x85, - 0x86, 0x35, 0xf6, 0x2e, 0x8e, 0x96, 0x3a, 0xbe, 0x81, 0x4f, 0x7f, 0x00, - 0x6a, 0x8a, 0xbf, 0x1d, 0x9e, 0xdd, 0x60, 0xd6, 0xa5, 0xf4, 0x79, 0xee, - 0x01, 0x10, 0xd8, 0x9c, 0x14, 0x2b, 0xa2, 0xd0, 0xf1, 0x01, 0x4f, 0xa7, - 0x25, 0x35, 0x5a, 0xb0, 0xf1, 0x3d, 0xbc, 0x18, 0x57, 0xbd, 0x66, 0x81, - 0xf0, 0xd3, 0xf6, 0xdb, 0x2f, 0xc9, 0x75, 0x84, 0x88, 0xea, 0xc6, 0x3b, - 0x9d, 0xd5, 0xd4, 0xbd, 0x19, 0x2b, 0x5e, 0x40, 0x44, 0x9b, 0x55, 0xdf, - 0x5c, 0x95, 0x06, 0x1a, 0xa6, 0x44, 0x7b, 0xcd, 0x7d, 0xf3, 0xaa, 0xd6, - 0xf8, 0xdd, 0xfe, 0x9f, 0x7b, 0xb1, 0xbf, 0x3b, 0x94, 0x4a, 0x85, 0x22, - 0xf6, 0x4a, 0x03, 0x9c, 0xfa, 0xd8, 0x29, 0x81, 0x71, 0x63, 0x6a, 0x60, - 0x54, 0x0d, 0x0d, 0xa9, 0x39, 0x4a, 0x01, 0x8e, 0xa4, 0x42, 0x40, 0xa8, - 0x0d, 0xbd, 0xb4, 0x4d, 0xc4, 0xc0, 0x41, 0xaa, 0x62, 0x1c, 0x65, 0x76, - 0xeb, 0xd4, 0x54, 0x36, 0x8a, 0x3b, 0xbf, 0x87, 0x26, 0x54, 0xff, 0xce, - 0x81, 0xcd, 0x9d, 0x64, 0x7d, 0x89, 0x0c, 0x55, 0xe5, 0xf5, 0x32, 0x62, - 0x66, 0x31, 0x2c, 0x8c, 0xd0, 0x91, 0xab, 0x36, 0x8e, 0x9f, 0x73, 0x8c, - 0x6f, 0x86, 0x04, 0x9c, 0x7a, 0x4b, 0xaf, 0xc0, 0xff, 0xf1, 0x50, 0x80, - 0x33, 0x9f, 0xfc, 0x21, 0x4c, 0xd8, 0xa8, 0x00, 0xf8, 0x4f, 0x97, 0xfc, - 0x41, 0xd9, 0x56, 0x13, 0x48, 0x1d, 0x20, 0x6c, 0xc4, 0x66, 0x88, 0x55, - 0x74, 0xce, 0xfd, 0x07, 0x8d, 0xa7, 0xd7, 0x98, 0x57, 0x35, 0x99, 0xeb, - 0xf5, 0xcf, 0x30, 0x52, 0x73, 0xfb, 0x1c, 0x7e, 0xf4, 0x95, 0xcb, 0x68, - 0x1d, 0xf5, 0x51, 0xc0, 0x24, 0x19, 0x23, 0x59, 0xda, 0x10, 0x27, 0xe3, - 0xab, 0x7e, 0x4c, 0x12, 0xc2, 0x11, 0x50, 0x1c, 0xf7, 0x83, 0xbe, 0xf8, - 0xd9, 0xf4, 0x20, 0x85, 0x00, 0xa5, 0x5e, 0x0b, 0xe5, 0xdc, 0x4b, 0xae, - 0x99, 0x75, 0x54, 0xd6, 0xcd, 0x75, 0xb7, 0xea, 0xe5, 0xf2, 0x32, 0xc3, - 0x29, 0xb1, 0xfc, 0x4f, 0x4c, 0xc7, 0x52, 0xd6, 0xef, 0x0e, 0x91, 0xdf, - 0x9c, 0x75, 0xc8, 0xfa, 0x5e, 0x87, 0x96, 0xce, 0xb4, 0x79, 0xa3, 0xc4, - 0xda, 0xc9, 0x79, 0x99, 0xd7, 0x69, 0x05, 0x89, 0xb2, 0x43, 0x8d, 0xfa, - 0xf5, 0x41, 0xc7, 0xfe, 0x2c, 0x70, 0x27, 0x5d, 0xb4, 0xb4, 0xa9, 0x78, - 0x0b, 0x66, 0x94, 0xe3, 0x97, 0xba, 0xfd, 0x72, 0x12, 0xc9, 0x84, 0x9e, - 0xac, 0x31, 0x08, 0x4e, 0xa8, 0xa5, 0xfd, 0x86, 0x22, 0x77, 0x3f, 0xac, - 0x16, 0xb6, 0x3c, 0xba, 0x0c, 0xb3, 0x3b, 0xdb, 0x9f, 0x74, 0xbb, 0x34, - 0x99, 0x47, 0xb3, 0xb6, 0x8b, 0xdd, 0x12, 0xb9, 0x94, 0xb6, 0x8c, 0x87, - 0x1c, 0x66, 0x8d, 0x21, 0x97, 0x1d, 0x31, 0x7f, 0x8f, 0x26, 0x10, 0x93, - 0x19, 0xa8, 0xb0, 0xd2, 0xa4, 0xca, 0xcc, 0x7e, 0x9e, 0x44, 0x9f, 0x48, - 0x4e, 0x2c, 0x9a, 0x99, 0x84, 0x23, 0x48, 0xc7, 0x90, 0x89, 0x85, 0x52, - 0xb8, 0xbe, 0x91, 0xf8, 0x8a, 0x53, 0x2a, 0x17, 0xf7, 0x39, 0x3c, 0x1d, - 0x64, 0x40, 0x45, 0xea, 0xdf, 0x8b, 0xe7, 0x8e, 0x74, 0xfa, 0x5f, 0x73, - 0xef, 0xce, 0xf0, 0xb2, 0x0e, 0x44, 0xf6, 0x3d, 0x56, 0xa0, 0x74, 0xf3, - 0xb2, 0x12, 0x11, 0xa0, 0xbe, 0x40, 0xf9, 0x84, 0xd5, 0x0f, 0xc8, 0xeb, - 0xca, 0x1f, 0x2f, 0x2a, 0xfb, 0x4a, 0x91, 0x49, 0xfe, 0x90, 0xa2, 0x67, - 0xf4, 0xaf, 0xf6, 0xa8, 0x05, 0x7f, 0x63, 0x8f, 0xde, 0x92, 0x90, 0x28, - 0x1b, 0xb5, 0x87, 0x63, 0x18, 0xf8, 0x04, 0x93, 0x93, 0xf1, 0x63, 0xe3, - 0x8a, 0xa8, 0xdb, 0x7d, 0x19, 0x3d, 0x35, 0x6a, 0x01, 0x64, 0xe6, 0xe6, - 0x0a, 0xa3, 0x82, 0x5b, 0x96, 0x99, 0x2d, 0xfb, 0x9b, 0xfd, 0x24, 0xe5, - 0x1f, 0x18, 0x5e, 0xfc, 0x0e, 0xf6, 0x9d, 0xdd, 0x6f, 0xa5, 0x89, 0x85, - 0x55, 0xa3, 0x72, 0xdb, 0x0c, 0x38, 0x33, 0xf4, 0xbf, 0x42, 0x20, 0xf7, - 0xfe, 0x4f, 0xfc, 0x83, 0x4b, 0x08, 0xd9, 0x3a, 0xf0, 0x0e, 0x16, 0x96, - 0x9b, 0x55, 0x04, 0xd4, 0x63, 0xc2, 0xeb, 0x4d, 0x3f, 0x66, 0x55, 0x1f, - 0xf5, 0xdc, 0x56, 0x84, 0x2b, 0x38, 0xac, 0x10, 0x72, 0x36, 0x33, 0x5c, - 0x07, 0x58, 0x7c, 0x31, 0x18, 0x49, 0x63, 0x18, 0x8a, 0x42, 0x71, 0x2f, - 0xff, 0xf1, 0x50, 0x80, 0x2a, 0x1f, 0xfc, 0x21, 0x7a, 0xc9, 0x7f, 0xff, - 0xff, 0x80, 0x00, 0x92, 0xa7, 0x58, 0xa0, 0x6c, 0x68, 0x0b, 0x09, 0x14, - 0xc5, 0x3e, 0x3b, 0xea, 0xc3, 0x00, 0x1d, 0x77, 0x26, 0x22, 0x53, 0xaf, - 0x11, 0xe7, 0xc7, 0x5e, 0xcc, 0xdf, 0x40, 0x6e, 0xc6, 0xf9, 0x07, 0xde, - 0xf4, 0xb8, 0xb0, 0x3e, 0x95, 0x7c, 0x5c, 0xa9, 0xb2, 0x97, 0x84, 0x3a, - 0x53, 0x49, 0x97, 0x3b, 0xc9, 0x51, 0xc1, 0x2b, 0xb7, 0x7b, 0x4f, 0xa2, - 0xe2, 0x56, 0x3c, 0x5f, 0x2a, 0x2d, 0x71, 0xe9, 0x1b, 0x8c, 0xb9, 0x49, - 0xf1, 0xa8, 0xe0, 0xad, 0x33, 0x59, 0x64, 0xd1, 0x5e, 0x25, 0x91, 0x0c, - 0x80, 0x9a, 0x0a, 0x3e, 0x0a, 0x9b, 0x26, 0xee, 0x16, 0xd2, 0xa9, 0x51, - 0xb1, 0xd3, 0x4f, 0x84, 0xcd, 0x32, 0x5a, 0xef, 0x20, 0x82, 0x01, 0x57, - 0x23, 0xb8, 0x02, 0x23, 0x33, 0x33, 0x33, 0x63, 0x4e, 0x3c, 0x2d, 0xb5, - 0x54, 0xe6, 0x8a, 0x10, 0x9c, 0xb0, 0x84, 0x11, 0x10, 0x01, 0xdf, 0x0e, - 0xd2, 0xc2, 0x4c, 0x1d, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xc5, 0x7c, 0x6b, - 0x24, 0x1a, 0x68, 0x34, 0x9c, 0x26, 0x85, 0x05, 0x05, 0x05, 0x05, 0x3d, - 0xa0, 0xba, 0x97, 0x4a, 0x82, 0xb2, 0xf3, 0x9d, 0x2b, 0xad, 0x75, 0x28, - 0x36, 0x59, 0x3b, 0xbd, 0xb2, 0x02, 0xc5, 0xf0, 0xaf, 0xd6, 0x82, 0x8e, - 0xbe, 0x3a, 0xe8, 0x29, 0xc1, 0x7e, 0x0b, 0xf3, 0x50, 0x57, 0x29, 0x48, - 0xe9, 0x40, 0xa6, 0xa2, 0xc0, 0x98, 0x2e, 0xb5, 0x05, 0x7b, 0x02, 0x82, - 0x82, 0x82, 0x82, 0x82, 0xb4, 0x55, 0xa0, 0xa0, 0xa6, 0xad, 0x05, 0xbe, - 0xac, 0xd4, 0xeb, 0xa6, 0x96, 0xc7, 0x19, 0x90, 0x05, 0xc4, 0x95, 0x99, - 0x28, 0x05, 0x36, 0xc7, 0x1c, 0x71, 0xf0, 0xa5, 0x98, 0x18, 0xd1, 0x90, - 0x1e, 0xda, 0xeb, 0xc2, 0x4c, 0x1d, 0xdd, 0xdd, 0xeb, 0x77, 0x91, 0xe4, - 0x17, 0x22, 0xe1, 0x69, 0x31, 0x9a, 0xa1, 0x3a, 0x10, 0xc5, 0x13, 0xc5, - 0x59, 0x15, 0x22, 0xff, 0x78, 0x2c, 0x30, 0x01, 0xd7, 0x72, 0x62, 0x02, - 0x8b, 0xc6, 0xb7, 0x26, 0xc4, 0x95, 0xfc, 0x77, 0x26, 0x8d, 0x45, 0x91, - 0x10, 0xe8, 0x9d, 0xef, 0x8d, 0x2f, 0x15, 0x98, 0x0c, 0xd9, 0x57, 0x33, - 0xf6, 0x72, 0x4f, 0x1e, 0x1f, 0xff, 0xe2, 0x01, 0x52, 0x18, 0x2d, 0x40, - 0x5e, 0x00, 0x0c, 0x96, 0x88, 0x09, 0xcf, 0xc9, 0x87, 0xc3, 0x70, 0x70, - 0xff, 0xf1, 0x50, 0x80, 0x29, 0xff, 0xfc, 0x21, 0x1a, 0xcb, 0xff, 0xff, - 0xff, 0xc0, 0x00, 0x94, 0x88, 0x32, 0xd2, 0x0b, 0x9f, 0x5c, 0xfd, 0x25, - 0xbd, 0x65, 0x7c, 0x4e, 0x7a, 0x65, 0x80, 0x25, 0x2a, 0x42, 0x54, 0xa5, - 0x03, 0x6b, 0x10, 0xaf, 0xd7, 0x57, 0xd3, 0xad, 0xe9, 0x27, 0xdc, 0xeb, - 0x67, 0x5d, 0x74, 0xe8, 0x8e, 0x05, 0xc9, 0x84, 0x2e, 0xec, 0xa7, 0x25, - 0x55, 0x54, 0x25, 0xdf, 0x03, 0x76, 0xd5, 0x21, 0xa9, 0x91, 0x23, 0x45, - 0xd6, 0x8e, 0x14, 0xf6, 0x06, 0xfd, 0xf6, 0x4e, 0xd8, 0x25, 0xe1, 0x57, - 0x91, 0xbe, 0xd4, 0xf8, 0xad, 0x03, 0x3d, 0x51, 0x69, 0xdd, 0x63, 0x62, - 0x09, 0x17, 0xe1, 0x62, 0xa5, 0x65, 0xc3, 0x5d, 0xc4, 0x00, 0x65, 0x75, - 0x40, 0x6e, 0x42, 0x0e, 0x26, 0x66, 0x4f, 0xc1, 0x16, 0x26, 0x35, 0x46, - 0x79, 0xae, 0xad, 0xc8, 0x6b, 0xad, 0xd4, 0xdf, 0x1c, 0x63, 0x86, 0xb6, - 0xec, 0x93, 0x72, 0x03, 0xf0, 0xe6, 0x82, 0xeb, 0x1a, 0x37, 0x73, 0xfa, - 0xaf, 0x97, 0x51, 0x89, 0x33, 0x44, 0xd3, 0xdf, 0x66, 0x88, 0x69, 0x99, - 0x0d, 0x86, 0x82, 0xad, 0x30, 0xff, 0x4d, 0x59, 0xc0, 0xe9, 0xad, 0xe7, - 0x1e, 0xca, 0x28, 0x0c, 0xf8, 0x55, 0x29, 0xd5, 0x16, 0xd1, 0x1d, 0x28, - 0xfc, 0x0d, 0x21, 0x60, 0x23, 0x64, 0x6d, 0xea, 0x01, 0xe4, 0x7a, 0x43, - 0xac, 0x5f, 0xbd, 0x4a, 0x3e, 0x29, 0x09, 0x35, 0x5f, 0x34, 0xf6, 0x39, - 0x0c, 0x4a, 0x1a, 0x8f, 0x33, 0x6c, 0x13, 0xcf, 0x1b, 0x52, 0xda, 0x00, - 0xa8, 0xa9, 0x5c, 0x51, 0xb2, 0x69, 0xa7, 0xa9, 0x52, 0xc6, 0x07, 0xb4, - 0x07, 0xba, 0xd7, 0x85, 0xb2, 0xc7, 0x45, 0xed, 0xc4, 0x32, 0x0c, 0x1b, - 0x9c, 0xa4, 0x25, 0xe0, 0x44, 0xf7, 0x2e, 0x7d, 0x73, 0xf4, 0x96, 0xf5, - 0x95, 0xf1, 0x39, 0xe9, 0x96, 0x00, 0xbc, 0xa5, 0x48, 0x94, 0xbe, 0x60, - 0x76, 0xc1, 0xa5, 0x06, 0x6d, 0xac, 0x21, 0x0d, 0x6a, 0x26, 0x24, 0x44, - 0x02, 0xfe, 0xf2, 0x92, 0x8b, 0xce, 0xf7, 0x7a, 0x7b, 0x95, 0x22, 0xb4, - 0x39, 0x55, 0x35, 0x04, 0x86, 0x2b, 0x15, 0x94, 0xe5, 0xa1, 0x04, 0x20, - 0xc6, 0xd6, 0x86, 0x05, 0xc4, 0x5d, 0x45, 0x33, 0xb8, 0x88, 0x2a, 0x8a, - 0x4c, 0xa7, 0x53, 0x2c, 0xba, 0xf0, 0x1b, 0x4c, 0x31, 0x45, 0x9e, 0xfd, - 0x74, 0xc4, 0xec, 0x63, 0xd8, 0x06, 0xa1, 0x4f, 0xc1, 0x79, 0x97, 0xff, - 0xf1, 0x50, 0x80, 0x2d, 0xdf, 0xfc, 0x21, 0x1a, 0xcb, 0x41, 0xbf, 0xff, - 0xc0, 0x00, 0x94, 0x87, 0xb3, 0x91, 0xcb, 0xaf, 0xae, 0x27, 0x11, 0xae, - 0x77, 0x9e, 0x77, 0x77, 0x4d, 0x32, 0xd8, 0x94, 0x95, 0x29, 0x25, 0x42, - 0xaa, 0x01, 0x55, 0xc8, 0x8d, 0x50, 0xad, 0x79, 0x5c, 0x89, 0xfb, 0x0b, - 0xd7, 0x24, 0xc8, 0x97, 0x73, 0x42, 0x89, 0xe2, 0x8e, 0xc7, 0x35, 0x19, - 0x95, 0x41, 0x66, 0x4c, 0x48, 0xf3, 0x1a, 0x4a, 0x33, 0x85, 0x83, 0x70, - 0x79, 0x59, 0xe1, 0xa0, 0xb0, 0xe5, 0xea, 0xe2, 0xd1, 0x01, 0x8a, 0xe1, - 0xba, 0xcb, 0x4f, 0x39, 0x3b, 0x6a, 0x48, 0x67, 0x64, 0x99, 0x78, 0x71, - 0xae, 0x99, 0xa8, 0x18, 0x2b, 0x66, 0xac, 0x6a, 0x8c, 0x89, 0xed, 0x78, - 0x63, 0xbc, 0x6f, 0x34, 0x22, 0x7a, 0x78, 0xc5, 0xb4, 0x3b, 0x1c, 0xf2, - 0xcc, 0x8a, 0xe2, 0x05, 0x7a, 0x18, 0x3c, 0x40, 0x35, 0x34, 0x50, 0x53, - 0xa3, 0x0c, 0x5d, 0x49, 0x2d, 0x6c, 0x80, 0x88, 0x00, 0xab, 0xc8, 0x82, - 0x93, 0xa5, 0xf4, 0xba, 0x45, 0x93, 0xe9, 0x82, 0x89, 0xd8, 0xcf, 0x12, - 0x04, 0xd1, 0x56, 0xac, 0x56, 0xed, 0xf5, 0x2d, 0x46, 0x09, 0x55, 0x1d, - 0x31, 0xf1, 0x12, 0xe8, 0x29, 0xc4, 0x1c, 0x8b, 0x94, 0x22, 0xab, 0xee, - 0xa0, 0x03, 0xe3, 0x15, 0x34, 0x4e, 0xb8, 0xec, 0xbf, 0x94, 0xe1, 0xa5, - 0x07, 0x8b, 0xad, 0x19, 0x4f, 0x30, 0x7e, 0x2d, 0x16, 0xa1, 0xc0, 0x70, - 0x9b, 0x9a, 0x76, 0xbf, 0x4a, 0x9f, 0x1a, 0x27, 0x90, 0xa2, 0xb9, 0x48, - 0xf2, 0xab, 0x4d, 0xe8, 0xdc, 0xd6, 0xa0, 0xef, 0x8d, 0x0a, 0x1c, 0x96, - 0x27, 0x1c, 0x60, 0x5e, 0x1d, 0x0c, 0xf5, 0xd0, 0x96, 0xeb, 0xb9, 0x21, - 0x60, 0xfd, 0x4e, 0x6c, 0x76, 0xfc, 0x61, 0xbb, 0xe1, 0x5b, 0xc7, 0x65, - 0xe4, 0xf7, 0x6e, 0x19, 0x27, 0x0d, 0x48, 0x7b, 0x92, 0xbf, 0xcf, 0x37, - 0xe7, 0x9d, 0x4d, 0xef, 0xd7, 0xe3, 0x77, 0x74, 0xd3, 0x2d, 0x89, 0x4a, - 0xab, 0xcb, 0x4a, 0x54, 0x2c, 0x0b, 0xeb, 0x8e, 0xe7, 0x1f, 0x7e, 0x74, - 0xc5, 0xe9, 0x88, 0xad, 0xa2, 0x6f, 0xc6, 0xce, 0x66, 0x86, 0x3a, 0x9c, - 0xa2, 0xd6, 0x6d, 0xf5, 0x2c, 0x39, 0x28, 0x9c, 0x07, 0x8f, 0x57, 0x7d, - 0xe3, 0xae, 0xbc, 0x37, 0x25, 0xb1, 0x80, 0xc2, 0xff, 0x04, 0xce, 0x3f, - 0x1e, 0xd7, 0xca, 0x99, 0xac, 0x5a, 0xb2, 0x8d, 0x27, 0xf7, 0x03, 0xde, - 0x1a, 0x36, 0x87, 0xd6, 0x2a, 0xc0, 0x45, 0x5b, 0x5d, 0x7b, 0xe6, 0x42, - 0xc4, 0xfe, 0x20, 0xd3, 0xe1, 0x53, 0x74, 0x0a, 0xd1, 0x92, 0xcd, 0x6b, - 0xd6, 0xcf, 0x82, 0x93, 0x80, 0xff, 0xf1, 0x50, 0x80, 0x2e, 0x9f, 0xfc, - 0x21, 0x1a, 0xcf, 0x6e, 0xbb, 0xfd, 0xc0, 0x00, 0x92, 0xa6, 0xb5, 0x11, - 0x45, 0xfe, 0x17, 0xc4, 0x4c, 0xdf, 0x3f, 0x5d, 0xea, 0xe8, 0x95, 0x02, - 0xa0, 0x00, 0x00, 0x5d, 0x81, 0x35, 0x6b, 0x4e, 0x1d, 0xc0, 0x2a, 0xf5, - 0x1e, 0x88, 0xc0, 0xf3, 0xb5, 0x68, 0x64, 0xf0, 0xb2, 0x9d, 0x0e, 0x89, - 0xba, 0xd3, 0xb5, 0x05, 0xbf, 0x88, 0x86, 0xe4, 0x62, 0xaf, 0x54, 0x40, - 0x35, 0x25, 0x66, 0x3e, 0xae, 0x2d, 0xab, 0xb3, 0xab, 0x80, 0x46, 0xcb, - 0x47, 0x72, 0xe0, 0xcd, 0xe4, 0xfb, 0xea, 0x27, 0x0a, 0x48, 0x67, 0xa9, - 0x45, 0x12, 0xc1, 0xd3, 0x68, 0x4e, 0x69, 0x85, 0x64, 0xb2, 0x0e, 0x30, - 0x76, 0x80, 0x93, 0x0b, 0x91, 0x04, 0x30, 0xda, 0xf6, 0x62, 0x3e, 0x1a, - 0x54, 0xd6, 0xd5, 0x48, 0x51, 0x17, 0x75, 0x60, 0x0a, 0xd1, 0x6c, 0x48, - 0xf6, 0x30, 0x81, 0x42, 0x9c, 0x96, 0x4a, 0x24, 0x63, 0x02, 0x93, 0xdf, - 0x55, 0x78, 0x13, 0xd7, 0x7c, 0xd4, 0xbd, 0x55, 0xab, 0x94, 0xd8, 0xbc, - 0x4a, 0x48, 0xeb, 0x0f, 0x63, 0xb5, 0x68, 0xe0, 0xf0, 0xeb, 0x91, 0x3b, - 0x50, 0xf2, 0x33, 0x39, 0xb1, 0x48, 0x80, 0x43, 0x05, 0x63, 0x30, 0x4c, - 0x62, 0x09, 0x6e, 0xdd, 0x0d, 0x9f, 0x0a, 0x13, 0x91, 0x82, 0x7c, 0x76, - 0x4e, 0x7a, 0x12, 0x0c, 0x1f, 0xa4, 0xd3, 0xca, 0xc6, 0x79, 0xda, 0x00, - 0x58, 0x08, 0x6d, 0x57, 0xed, 0x25, 0x03, 0xc3, 0x62, 0x9f, 0x30, 0xd6, - 0x72, 0x0a, 0xad, 0x77, 0x2b, 0x26, 0xbf, 0x76, 0xf3, 0xb1, 0x9c, 0xf4, - 0xeb, 0x0f, 0x10, 0x9c, 0x9a, 0x7e, 0x34, 0x17, 0x3f, 0x14, 0xcf, 0x96, - 0xdf, 0xc8, 0x5a, 0x59, 0x0b, 0xcf, 0xfb, 0x9e, 0x90, 0xfa, 0x10, 0xae, - 0x5b, 0xf2, 0x7d, 0x15, 0xda, 0xc1, 0xe6, 0x2f, 0xce, 0x92, 0x88, 0xaa, - 0x50, 0x7e, 0xe2, 0xff, 0x5a, 0xbd, 0x5a, 0x7a, 0xbf, 0xcf, 0xfa, 0x77, - 0xa8, 0x25, 0x43, 0x7a, 0xa8, 0x51, 0x75, 0x29, 0x52, 0xa0, 0x51, 0x8b, - 0x10, 0x0f, 0xea, 0xfd, 0xba, 0xb0, 0x04, 0x6d, 0xd8, 0x62, 0x42, 0xa7, - 0x90, 0x00, 0x47, 0x0e, 0xdc, 0xce, 0xc2, 0xc9, 0x90, 0xb5, 0x7c, 0x42, - 0x20, 0xee, 0x86, 0x45, 0xb3, 0x05, 0xb8, 0xcf, 0xb1, 0x22, 0x9f, 0x2b, - 0xcb, 0x41, 0xe9, 0xef, 0xab, 0xe7, 0x6b, 0xbb, 0x13, 0x77, 0x78, 0xae, - 0x3d, 0xb8, 0x5b, 0x0c, 0x66, 0xb2, 0x9c, 0xf4, 0x4e, 0x65, 0x29, 0xde, - 0x6b, 0x78, 0x9e, 0xee, 0x97, 0xd3, 0xe7, 0x4a, 0xdc, 0x66, 0xed, 0x32, - 0xce, 0x5c, 0x94, 0x38, 0x2f, 0x1d, 0x16, 0x6f, 0x98, 0x9e, 0x1c, 0xda, - 0xcf, 0xc9, 0x68, 0x79, 0x3e, 0xff, 0xf1, 0x50, 0x80, 0x30, 0x1f, 0xfc, - 0x21, 0x1a, 0xcf, 0x3f, 0xef, 0x7f, 0xc0, 0x00, 0x92, 0x85, 0xb4, 0x91, - 0xc3, 0xf5, 0xe3, 0x51, 0xac, 0xef, 0x8f, 0xe3, 0xfd, 0x3d, 0x6a, 0xe1, - 0x79, 0x15, 0x5a, 0xda, 0xca, 0x42, 0x50, 0xa0, 0x7f, 0xf3, 0x37, 0xe3, - 0x5c, 0x2b, 0xbc, 0x69, 0xf8, 0xba, 0x54, 0x75, 0xb7, 0x37, 0x51, 0x1b, - 0x2c, 0x8f, 0x7d, 0x2a, 0x0f, 0x6b, 0x2e, 0xbb, 0xce, 0xa2, 0x73, 0x8b, - 0x4f, 0xa8, 0xc3, 0x3f, 0xce, 0x84, 0xf8, 0x33, 0x79, 0x89, 0x6a, 0xe9, - 0x57, 0x24, 0xdc, 0x08, 0xd3, 0x5f, 0xe7, 0x94, 0xbb, 0xd5, 0x25, 0xf1, - 0x89, 0xfa, 0x4e, 0x0a, 0xf6, 0xea, 0x92, 0xcb, 0x40, 0x1b, 0x79, 0xb2, - 0xdf, 0x6a, 0x37, 0x23, 0xa5, 0x50, 0x82, 0x90, 0x80, 0x4b, 0x6f, 0xca, - 0xab, 0x5f, 0x62, 0x70, 0x92, 0x26, 0x87, 0x40, 0x20, 0x52, 0x75, 0x34, - 0x2b, 0x2f, 0x80, 0x57, 0x06, 0xa8, 0x68, 0x46, 0xb0, 0x0e, 0xa8, 0x05, - 0x32, 0x31, 0x25, 0x19, 0x2c, 0xdc, 0xb3, 0x5a, 0x9a, 0x5b, 0x9b, 0x33, - 0xcf, 0xad, 0xf8, 0x2d, 0x0d, 0x17, 0xd6, 0xf4, 0x1b, 0xfe, 0xab, 0xa4, - 0x3d, 0x0e, 0x6a, 0x03, 0xd6, 0xeb, 0x2a, 0x44, 0x59, 0xd6, 0x9b, 0x9f, - 0xb8, 0xbb, 0x9d, 0x6f, 0xbd, 0x4f, 0xb8, 0xdd, 0xa8, 0x85, 0x21, 0xe4, - 0xdd, 0xed, 0x26, 0x99, 0xd6, 0xbb, 0x0d, 0x7f, 0x99, 0xef, 0xc8, 0xe4, - 0x07, 0xd8, 0x07, 0xf8, 0x5b, 0x93, 0x03, 0x09, 0xe8, 0x9d, 0x2f, 0x52, - 0xd2, 0x1e, 0xc8, 0x39, 0x33, 0x00, 0x8f, 0xba, 0xdb, 0xcd, 0xbd, 0xce, - 0xd9, 0x6c, 0x1a, 0x1b, 0xa7, 0x34, 0x0a, 0x4f, 0xd0, 0x28, 0x16, 0x94, - 0x62, 0x38, 0x7a, 0x80, 0xc9, 0x35, 0x9f, 0x29, 0x09, 0xe9, 0xc9, 0x57, - 0xa9, 0x7f, 0xf3, 0x13, 0x91, 0x28, 0x44, 0x3f, 0x42, 0x7a, 0x4d, 0x7f, - 0x52, 0xd7, 0xc6, 0x2f, 0xfc, 0x07, 0x66, 0x37, 0x25, 0x10, 0x41, 0xc0, - 0x51, 0x3e, 0xe1, 0xf1, 0xaf, 0x88, 0xd6, 0x77, 0xc7, 0xf1, 0xfe, 0x9b, - 0xab, 0x8e, 0x7c, 0xe4, 0x55, 0x6b, 0x64, 0xdc, 0x94, 0x4a, 0xbc, 0xba, - 0x0e, 0x48, 0x27, 0x70, 0x1f, 0xf1, 0xcd, 0xd5, 0xa2, 0x93, 0x49, 0x5f, - 0x50, 0xa2, 0xb0, 0x60, 0xba, 0x09, 0x70, 0x9a, 0x7b, 0x4e, 0x76, 0x94, - 0x70, 0xa2, 0x13, 0xb9, 0x9c, 0x41, 0xf1, 0xc8, 0x29, 0xdc, 0x32, 0x22, - 0xaa, 0x56, 0x23, 0xbb, 0x70, 0xf4, 0x1e, 0x50, 0x55, 0x56, 0x85, 0xc2, - 0xb8, 0x61, 0x95, 0xb8, 0xed, 0x5d, 0x42, 0x0f, 0xe5, 0x40, 0x2c, 0xa4, - 0xab, 0x9b, 0x7f, 0xa1, 0xcf, 0xff, 0x27, 0x35, 0x8c, 0x5c, 0x86, 0xb5, - 0xdb, 0xa6, 0xfa, 0x18, 0x76, 0x16, 0xd7, 0xde, 0x8c, 0xe4, 0x2c, 0xf1, - 0x44, 0x78, 0x5e, 0xac, 0xfc, 0xff, 0xf1, 0x50, 0x80, 0x30, 0x9f, 0xfc, - 0x21, 0x1a, 0xcf, 0x28, 0x3f, 0xff, 0xc0, 0x00, 0x91, 0xa7, 0xb3, 0xd1, - 0x6c, 0x23, 0xda, 0x75, 0xed, 0x93, 0x5d, 0xab, 0xdf, 0xf7, 0xdb, 0x55, - 0x2a, 0x4a, 0x00, 0x94, 0x80, 0xa0, 0x0b, 0xe0, 0x06, 0xa7, 0x6a, 0x67, - 0x04, 0x7b, 0xe6, 0x0a, 0x88, 0xc0, 0x3b, 0x8d, 0x4a, 0x1c, 0xfd, 0x62, - 0x64, 0x79, 0x95, 0xd0, 0x06, 0x05, 0x69, 0x15, 0x51, 0x08, 0xa8, 0x97, - 0x1b, 0x1a, 0x09, 0x40, 0xc8, 0xbc, 0x59, 0xb1, 0x51, 0xf0, 0xcb, 0xd5, - 0x56, 0xff, 0xfb, 0x44, 0xb7, 0xb3, 0x61, 0x47, 0x7c, 0x9d, 0xd7, 0xd1, - 0xba, 0xd9, 0x46, 0x71, 0xa2, 0xd7, 0xb2, 0xeb, 0x7b, 0xf1, 0xea, 0x73, - 0x90, 0x68, 0x93, 0xb2, 0xd7, 0x2c, 0x5f, 0x51, 0xac, 0xb5, 0xa2, 0x92, - 0x89, 0xca, 0x09, 0x30, 0x13, 0x77, 0x4b, 0xdb, 0x43, 0x00, 0x50, 0xe4, - 0x8e, 0x17, 0x2b, 0x9a, 0x09, 0xb4, 0x1d, 0x68, 0x81, 0x0f, 0x30, 0x3b, - 0x35, 0x69, 0x9c, 0x9c, 0x0a, 0x92, 0xb8, 0x02, 0xe0, 0x38, 0xd6, 0xb6, - 0x3e, 0xce, 0x16, 0x5c, 0x0a, 0x88, 0x82, 0x61, 0x9c, 0x06, 0x27, 0x85, - 0x00, 0x5a, 0x51, 0xff, 0x5b, 0xda, 0x48, 0xd5, 0x1d, 0x99, 0x7c, 0x87, - 0xf2, 0x01, 0x9a, 0x93, 0xca, 0x00, 0xc9, 0x17, 0x55, 0xdf, 0x6b, 0x3f, - 0xa3, 0x96, 0x9e, 0x5d, 0x4b, 0xe8, 0xf8, 0x5b, 0xc6, 0xdf, 0x9d, 0x0a, - 0x9a, 0x1b, 0xb4, 0xc1, 0x2f, 0x3b, 0x8b, 0x3e, 0xe4, 0x0e, 0x80, 0x25, - 0x3b, 0x62, 0x66, 0xaa, 0x5e, 0x55, 0xc5, 0x2f, 0xe0, 0x35, 0x25, 0xf3, - 0x18, 0xc2, 0x17, 0x01, 0x33, 0x89, 0x7d, 0xc9, 0x38, 0xd8, 0x8d, 0x18, - 0x14, 0x81, 0xf9, 0xe9, 0x01, 0x35, 0x75, 0xa0, 0x3c, 0x0c, 0x9d, 0x1a, - 0x6d, 0xa8, 0x16, 0x31, 0x15, 0x16, 0x24, 0xb7, 0x65, 0xec, 0x08, 0x2f, - 0x3c, 0x4c, 0xa2, 0xc7, 0x6c, 0xec, 0xd5, 0xdf, 0x23, 0x10, 0x41, 0xa9, - 0xd0, 0x7e, 0xe3, 0xda, 0xfe, 0xf1, 0x57, 0xce, 0x6b, 0xfa, 0x7f, 0xbe, - 0xda, 0xa9, 0x52, 0x50, 0x11, 0x54, 0x89, 0x8d, 0x65, 0x40, 0xa2, 0x4c, - 0x3b, 0xe4, 0xe4, 0x6a, 0x37, 0x43, 0x2e, 0x45, 0xfd, 0x2b, 0xc5, 0x76, - 0x3f, 0x62, 0x0d, 0xdb, 0xcf, 0xdc, 0x6b, 0x71, 0x68, 0x60, 0xfe, 0x6b, - 0x5e, 0x58, 0x6f, 0x90, 0xe8, 0x8b, 0xc1, 0x0b, 0x1c, 0xec, 0xa2, 0xc6, - 0x0a, 0x12, 0x29, 0x12, 0x4a, 0x7c, 0xdb, 0xc9, 0x8f, 0xf3, 0x15, 0x32, - 0x6a, 0x69, 0xbe, 0x4f, 0x57, 0x47, 0x29, 0xc9, 0x5c, 0x73, 0xae, 0xf6, - 0x75, 0x7d, 0x5c, 0xb1, 0x04, 0x45, 0x35, 0x9c, 0x55, 0xe5, 0xfe, 0x3a, - 0x99, 0xd7, 0x42, 0x89, 0x9b, 0x95, 0x29, 0x28, 0x6d, 0x27, 0x81, 0x5a, - 0xa6, 0x9d, 0xd2, 0x8f, 0x49, 0xd2, 0x16, 0x8e, 0x6e, 0xff, 0xf1, 0x50, - 0x80, 0x2c, 0xbf, 0xfc, 0x21, 0x1a, 0xcf, 0xbf, 0x2f, 0xff, 0xc0, 0x00, - 0x8f, 0xa8, 0xb3, 0x11, 0xc7, 0x9b, 0xbf, 0x68, 0x19, 0xdf, 0xb0, 0xba, - 0xad, 0x64, 0x32, 0xd8, 0x00, 0x00, 0x0f, 0xf3, 0x70, 0x15, 0x6c, 0xfd, - 0x90, 0x39, 0xb7, 0x84, 0x2d, 0xed, 0xe6, 0x3b, 0x0b, 0x49, 0x6a, 0xb9, - 0x47, 0xd2, 0x0c, 0x0a, 0xd8, 0x0d, 0xee, 0x6d, 0x0d, 0x82, 0x23, 0x29, - 0xf2, 0x50, 0x83, 0xab, 0x0c, 0x87, 0x6f, 0x51, 0x59, 0x07, 0x19, 0x29, - 0xc1, 0xd7, 0x5d, 0xd4, 0x7d, 0x3f, 0x01, 0x16, 0x61, 0xb6, 0xb9, 0x77, - 0x57, 0x46, 0xb2, 0xb1, 0x17, 0xc0, 0x2e, 0x4a, 0xc6, 0xba, 0x29, 0xba, - 0xdc, 0x98, 0xc8, 0xb1, 0x7e, 0xa2, 0x19, 0x80, 0xee, 0x9a, 0x61, 0x90, - 0xc8, 0x52, 0x2a, 0x96, 0x53, 0x93, 0x92, 0xb6, 0x5c, 0x12, 0x53, 0x03, - 0x10, 0x11, 0x03, 0xce, 0x11, 0x6f, 0x23, 0xa1, 0xaa, 0x36, 0x61, 0x94, - 0x4d, 0x19, 0x28, 0xaa, 0x66, 0x9e, 0xa6, 0x85, 0x38, 0x42, 0x26, 0xd1, - 0x2d, 0x39, 0xae, 0x82, 0x22, 0xda, 0x03, 0xaf, 0x30, 0xf8, 0x99, 0x49, - 0xcf, 0xba, 0x2c, 0x5e, 0xf9, 0x2b, 0xd7, 0xe1, 0x74, 0x75, 0xd1, 0x31, - 0xb1, 0xa5, 0x66, 0xae, 0x5b, 0x86, 0x9e, 0xbc, 0xc7, 0xb2, 0xdf, 0xbe, - 0xf1, 0x01, 0xa4, 0x06, 0x06, 0x35, 0x53, 0x0b, 0x55, 0x9a, 0xef, 0xb5, - 0x5b, 0x30, 0x6c, 0x88, 0x30, 0x47, 0x1b, 0x60, 0x53, 0x55, 0xf2, 0x43, - 0x63, 0x99, 0xa9, 0x6b, 0xc4, 0x76, 0x74, 0x18, 0xe2, 0xeb, 0xc6, 0x9b, - 0xcb, 0x25, 0x04, 0xd1, 0x89, 0x0f, 0x96, 0xff, 0x93, 0x8b, 0xee, 0xa1, - 0xe0, 0xac, 0xe3, 0x74, 0x16, 0x3d, 0x3d, 0x82, 0x7c, 0x7a, 0x4b, 0x7c, - 0x42, 0xb1, 0x03, 0xc7, 0xc4, 0x15, 0x44, 0x3f, 0x71, 0xe6, 0xfe, 0x24, - 0x0a, 0xfe, 0x3f, 0xc5, 0x45, 0xd5, 0x6b, 0x21, 0x96, 0xc4, 0xaa, 0x4a, - 0x89, 0x95, 0x70, 0x0e, 0x71, 0xd4, 0x67, 0x20, 0x86, 0xc4, 0xb4, 0xd8, - 0x51, 0x09, 0x9d, 0x43, 0x71, 0x02, 0x2c, 0x71, 0x72, 0x4d, 0x4c, 0xa7, - 0x07, 0x41, 0x7d, 0xe0, 0x45, 0x1a, 0x67, 0xc4, 0xac, 0x68, 0xba, 0x05, - 0x66, 0x77, 0xaf, 0x1e, 0xeb, 0xcc, 0x4d, 0x40, 0xd7, 0xd7, 0x95, 0x3b, - 0x55, 0xb4, 0xf5, 0xb3, 0xdf, 0xd7, 0x9d, 0x4c, 0x52, 0xef, 0xab, 0xea, - 0xa4, 0xd7, 0x6e, 0xae, 0x84, 0xaf, 0x31, 0x1b, 0xce, 0x3b, 0xeb, 0xdb, - 0x50, 0x08, 0x02, 0x2e, 0xff, 0xe6, 0x4f, 0xac, 0xec, 0x8b, 0xa3, 0x8f, - 0xe6, 0x30, 0x60, 0xb5, 0x92, 0x38, 0xff, 0xf1, 0x50, 0x80, 0x30, 0xbf, - 0xfc, 0x21, 0x1a, 0xce, 0xdf, 0xbf, 0xbf, 0xc0, 0x00, 0x91, 0xa7, 0xb4, - 0xd0, 0xcc, 0x37, 0x1e, 0xd5, 0xd4, 0xea, 0xa6, 0x55, 0xfe, 0x7f, 0x7e, - 0xf4, 0x92, 0xae, 0xa5, 0x00, 0x08, 0x0a, 0x00, 0x78, 0x86, 0x02, 0x3f, - 0xc6, 0x9b, 0xa9, 0xdc, 0xe6, 0x56, 0x8d, 0xc2, 0x55, 0x5e, 0x5b, 0x2e, - 0xab, 0xd1, 0x30, 0x18, 0xd3, 0xa4, 0xc6, 0x57, 0x58, 0xde, 0xd1, 0x43, - 0x22, 0x8a, 0xd2, 0xca, 0x16, 0x27, 0x61, 0x51, 0xf6, 0xac, 0x96, 0x90, - 0xec, 0xfc, 0xd1, 0xd9, 0x72, 0x6c, 0x39, 0x2e, 0xe0, 0x1d, 0xd4, 0x74, - 0xa7, 0x35, 0x98, 0x28, 0xb5, 0xae, 0xa7, 0x7a, 0x6b, 0x76, 0xd0, 0x15, - 0x0a, 0x54, 0xc0, 0x54, 0x69, 0xa0, 0x6e, 0x4d, 0x69, 0x83, 0x50, 0x7c, - 0xa9, 0x51, 0xef, 0xaf, 0x11, 0xba, 0xe1, 0x96, 0x80, 0x19, 0x40, 0x05, - 0x0d, 0x14, 0xa2, 0xd3, 0x81, 0x05, 0xa9, 0x9c, 0x9e, 0xd0, 0xb8, 0x0a, - 0x44, 0x8d, 0x04, 0xcf, 0x3d, 0xc2, 0xca, 0x8e, 0x12, 0x5f, 0x21, 0x4d, - 0x72, 0x4d, 0x20, 0xb3, 0xda, 0x63, 0x23, 0xef, 0x49, 0x44, 0xc0, 0xd2, - 0xf1, 0xc4, 0xed, 0x52, 0xcf, 0xae, 0x70, 0xbd, 0xc0, 0x06, 0x74, 0x17, - 0x91, 0xcd, 0xdd, 0x06, 0xca, 0x1e, 0x7a, 0xad, 0x83, 0x27, 0xc6, 0xb7, - 0x85, 0x57, 0x44, 0x2a, 0x45, 0x17, 0x21, 0x42, 0x48, 0x43, 0xdc, 0x34, - 0x73, 0x7a, 0x89, 0xbf, 0x8f, 0xcd, 0x19, 0x03, 0x16, 0x20, 0x2f, 0x01, - 0x35, 0xfb, 0x8f, 0xe0, 0x6a, 0x71, 0xdb, 0x08, 0x4f, 0xec, 0x71, 0xaa, - 0xf3, 0x38, 0xff, 0xf2, 0xb5, 0x72, 0xa8, 0x58, 0x08, 0x4d, 0x76, 0x12, - 0x6a, 0x9c, 0x37, 0x11, 0xef, 0xe3, 0x6e, 0xda, 0x9e, 0xeb, 0xeb, 0x4d, - 0x0c, 0x76, 0x92, 0x8c, 0x80, 0xac, 0x27, 0x12, 0xbd, 0x33, 0x82, 0x23, - 0x35, 0x6e, 0x2e, 0x0f, 0x89, 0xc6, 0x2d, 0x23, 0xd3, 0x12, 0x05, 0x59, - 0xe7, 0x79, 0x85, 0xaa, 0x0b, 0x23, 0x0c, 0x64, 0xa2, 0x3d, 0xce, 0x2f, - 0x8e, 0xba, 0x95, 0x32, 0xaf, 0xfd, 0x7f, 0xeb, 0xde, 0x92, 0x55, 0xd4, - 0xa0, 0x28, 0xa9, 0x4d, 0x52, 0x20, 0x59, 0x33, 0x55, 0xa2, 0x79, 0x67, - 0x0b, 0x16, 0xd3, 0x9d, 0x44, 0xa7, 0xd6, 0x92, 0xcc, 0x03, 0x06, 0xbc, - 0x06, 0xf1, 0x4c, 0x00, 0xfb, 0xaa, 0x1d, 0x16, 0x03, 0xb2, 0x72, 0xfb, - 0x4a, 0x81, 0x30, 0x54, 0x2a, 0x46, 0x00, 0x26, 0xb7, 0xcd, 0xb7, 0x71, - 0x88, 0x31, 0xcd, 0x18, 0xc1, 0xd7, 0xbb, 0xf5, 0xcd, 0x66, 0xd2, 0x5f, - 0x82, 0x84, 0xd4, 0xba, 0x15, 0x6d, 0x89, 0x2b, 0x34, 0x11, 0x4f, 0xf2, - 0x9c, 0xb6, 0xc6, 0x2f, 0x5d, 0x3d, 0x35, 0x17, 0x35, 0xdc, 0xae, 0x29, - 0x07, 0xa1, 0xe7, 0xf9, 0x43, 0x8d, 0x4c, 0xe2, 0x21, 0xe2, 0xf0, 0xff, - 0xf1, 0x50, 0x80, 0x30, 0x1f, 0xfc, 0x21, 0x1a, 0xcf, 0xff, 0x3f, 0xff, - 0xc0, 0x00, 0x91, 0xa3, 0x43, 0x1a, 0x28, 0x85, 0xfd, 0xf5, 0x9e, 0x78, - 0xe6, 0x6b, 0x6f, 0xcf, 0xef, 0xdd, 0xdd, 0x5d, 0x5d, 0x4a, 0x99, 0x74, - 0x94, 0x12, 0xa1, 0x55, 0x40, 0x6c, 0x5d, 0x45, 0x19, 0xe8, 0xda, 0x4c, - 0x4d, 0xca, 0x5e, 0xf4, 0x8d, 0x8c, 0x64, 0xfb, 0xcc, 0xda, 0xd9, 0xd1, - 0xc0, 0x22, 0x63, 0xb3, 0x64, 0xf1, 0x40, 0x5e, 0xc8, 0x52, 0x66, 0x94, - 0x67, 0x23, 0xbf, 0xaa, 0xb6, 0x02, 0x8a, 0x62, 0x75, 0x89, 0xf6, 0xd2, - 0xe7, 0x1d, 0xa1, 0x46, 0x29, 0x7d, 0x92, 0x7b, 0xbc, 0xb0, 0x90, 0x07, - 0xc2, 0xa6, 0x0a, 0x59, 0xc8, 0xac, 0x29, 0x68, 0x84, 0x46, 0x33, 0x5a, - 0xe6, 0x99, 0x15, 0x1d, 0x0a, 0xe7, 0x04, 0xb4, 0x68, 0x99, 0xf6, 0xa1, - 0x69, 0x61, 0x9c, 0xd9, 0x90, 0x00, 0x6e, 0x82, 0xac, 0x2a, 0x12, 0x68, - 0xf0, 0x01, 0x21, 0x82, 0x06, 0x67, 0xf0, 0x39, 0xd4, 0x83, 0x07, 0x62, - 0x06, 0x0a, 0xed, 0x4b, 0xca, 0x45, 0x35, 0x90, 0xc6, 0x82, 0x39, 0xd9, - 0x27, 0x75, 0x10, 0x54, 0x22, 0x81, 0x5b, 0xee, 0x8d, 0x46, 0x4a, 0x34, - 0x55, 0x48, 0x03, 0x4c, 0x6a, 0x6e, 0x72, 0x42, 0x91, 0x3a, 0x9b, 0x13, - 0x1d, 0x37, 0xce, 0xd1, 0xd5, 0x61, 0x8d, 0xc4, 0x2a, 0xea, 0x0f, 0x53, - 0x24, 0x8e, 0xad, 0x5d, 0x0b, 0x12, 0x03, 0x6c, 0x30, 0x52, 0x17, 0x66, - 0xad, 0xe4, 0xbe, 0x17, 0x58, 0x2c, 0x67, 0xc6, 0xee, 0xa7, 0x21, 0xa5, - 0x78, 0x98, 0x9d, 0x69, 0xa8, 0x21, 0xfd, 0x00, 0x2c, 0xe9, 0xb3, 0x96, - 0xb9, 0x54, 0x29, 0x98, 0x19, 0xed, 0x9b, 0x49, 0x42, 0x1a, 0xe0, 0x12, - 0x7b, 0x04, 0x7b, 0x50, 0xd5, 0xb9, 0xd5, 0x75, 0x5d, 0xef, 0x6c, 0x15, - 0xd1, 0xb8, 0x80, 0xc4, 0x7d, 0xcf, 0x85, 0xee, 0x73, 0x88, 0xd2, 0x23, - 0xfb, 0x68, 0x8e, 0x21, 0xfa, 0xe9, 0x69, 0x16, 0xa2, 0x27, 0xdc, 0xbf, - 0xbe, 0xb3, 0xcf, 0x1c, 0xcd, 0x6f, 0x9f, 0xeb, 0xff, 0x5e, 0xee, 0xea, - 0xea, 0xea, 0x54, 0xcb, 0xa6, 0xb7, 0x54, 0x6a, 0x95, 0x6c, 0x00, 0x9e, - 0xb6, 0x1f, 0xd5, 0xff, 0x74, 0xd9, 0x20, 0x06, 0x72, 0x8d, 0xd4, 0x98, - 0x31, 0x97, 0xf5, 0x81, 0x57, 0xfd, 0x01, 0xf9, 0xde, 0xc2, 0x52, 0x03, - 0x0f, 0x56, 0xdd, 0x73, 0x40, 0x0b, 0xa3, 0x12, 0x0b, 0x00, 0x08, 0x22, - 0xe1, 0x76, 0xa1, 0x20, 0xa9, 0xdf, 0x97, 0x28, 0x0b, 0x6c, 0x2a, 0xb4, - 0xdf, 0x3e, 0xfe, 0x33, 0xd9, 0x63, 0xa0, 0xca, 0x2d, 0x32, 0xdb, 0x5f, - 0x58, 0x42, 0x74, 0x16, 0x84, 0x55, 0x49, 0x73, 0xd2, 0x70, 0xd5, 0x74, - 0x78, 0xbd, 0xff, 0xe4, 0xad, 0xc6, 0x2b, 0x98, 0xb8, 0x10, 0xbe, 0xff, - 0xf1, 0x50, 0x80, 0x2e, 0xdf, 0xfc, 0x21, 0x1a, 0xcf, 0x37, 0xdf, 0xff, - 0xc0, 0x00, 0x8f, 0x85, 0xb5, 0xd1, 0x27, 0xc6, 0xa7, 0x5a, 0x95, 0x79, - 0x3f, 0x3f, 0xaf, 0x6e, 0x2a, 0x66, 0x82, 0xa2, 0xa2, 0x88, 0x14, 0xaa, - 0x06, 0xdc, 0x5d, 0x0f, 0x58, 0xf2, 0x5d, 0x7d, 0xa5, 0xce, 0xb9, 0xaa, - 0xfb, 0xe8, 0x5c, 0xcc, 0xbb, 0x8b, 0x0e, 0x09, 0xbd, 0x59, 0xd3, 0x3f, - 0x9f, 0x82, 0xa8, 0xcd, 0xa2, 0xe9, 0x14, 0xc4, 0x3e, 0xde, 0xdf, 0x4d, - 0x6f, 0xdb, 0x56, 0xee, 0xef, 0x33, 0x48, 0x6c, 0xce, 0xed, 0x5e, 0xbb, - 0xff, 0x5d, 0x42, 0xd3, 0x01, 0x93, 0x83, 0x84, 0x23, 0xd1, 0x4c, 0x0d, - 0x93, 0x99, 0xd5, 0x23, 0x25, 0xa7, 0x35, 0xb1, 0x20, 0xa9, 0x85, 0x20, - 0xb0, 0x0e, 0xd0, 0x79, 0x5d, 0x9a, 0x9c, 0xe3, 0x00, 0x80, 0x60, 0xa6, - 0x40, 0x84, 0x4e, 0x34, 0x89, 0x03, 0x5a, 0xa8, 0xcc, 0x95, 0x4c, 0x60, - 0x8c, 0x8c, 0xb0, 0xac, 0x02, 0xb8, 0xdb, 0x3d, 0x71, 0x52, 0xcc, 0x86, - 0x08, 0x48, 0xf8, 0x5f, 0x58, 0xab, 0xaa, 0x29, 0x88, 0x91, 0xdb, 0xda, - 0x72, 0x4c, 0xe4, 0x03, 0x30, 0x52, 0x2a, 0x40, 0xc3, 0x18, 0xd3, 0x72, - 0x22, 0x00, 0xda, 0xca, 0xc0, 0x6d, 0x0b, 0x31, 0xbc, 0x92, 0x66, 0xee, - 0x08, 0xb7, 0x0a, 0x68, 0x61, 0x42, 0x21, 0xc0, 0x0f, 0x46, 0x5d, 0x66, - 0xaf, 0x08, 0x8a, 0x2e, 0x54, 0xc9, 0x2f, 0x9d, 0xa3, 0x0b, 0x49, 0xf7, - 0x88, 0xd0, 0xf1, 0x3f, 0xc8, 0x7d, 0xbe, 0xfe, 0xd5, 0x25, 0x37, 0x4f, - 0x0b, 0x99, 0x34, 0x13, 0xbf, 0x43, 0x03, 0x1f, 0xb9, 0x42, 0x19, 0x15, - 0x58, 0x2c, 0xa3, 0x37, 0x49, 0x9e, 0x1f, 0x03, 0xd0, 0xc0, 0x7f, 0x8c, - 0xe8, 0x85, 0x77, 0x5d, 0x80, 0x23, 0x0a, 0xfa, 0xd1, 0xae, 0x35, 0xfe, - 0xdd, 0xf3, 0x9a, 0xfd, 0x53, 0xe3, 0x7f, 0xd7, 0xb8, 0xfd, 0xcd, 0x3b, - 0x1f, 0x10, 0x51, 0xa2, 0xbd, 0xc7, 0xc7, 0x96, 0xb5, 0x97, 0x79, 0x3f, - 0x3f, 0x58, 0xe2, 0xa6, 0x68, 0x2a, 0x2a, 0xf6, 0x52, 0x26, 0x5d, 0x40, - 0x73, 0x02, 0x51, 0x15, 0x2b, 0xcc, 0xca, 0x96, 0x22, 0x8b, 0x46, 0xe0, - 0x23, 0x6d, 0xf2, 0x94, 0x3c, 0x15, 0x2e, 0x42, 0xe3, 0x02, 0xf0, 0xf0, - 0xeb, 0x69, 0x4d, 0x72, 0xd4, 0xb1, 0x31, 0x02, 0x2d, 0x8f, 0x24, 0x43, - 0x77, 0x48, 0x26, 0xbd, 0xfc, 0xde, 0xa9, 0xc1, 0x39, 0xe3, 0xb5, 0x82, - 0x75, 0x05, 0x8e, 0xdd, 0xd6, 0xb4, 0x29, 0x98, 0x95, 0xdc, 0x01, 0x9a, - 0x49, 0x90, 0xfe, 0x67, 0x79, 0xc1, 0x16, 0x33, 0xc9, 0x51, 0x57, 0x1f, - 0x1c, 0x8d, 0xf8, 0xd7, 0x7c, 0x52, 0xc4, 0xc9, 0xc2, 0x29, 0xf3, 0x56, - 0x5c, 0xff, 0xf1, 0x50, 0x80, 0x32, 0xff, 0xfc, 0x21, 0x1a, 0xcb, 0xff, - 0x7f, 0xff, 0xc0, 0x02, 0x8c, 0xa1, 0xc4, 0x59, 0xa8, 0x76, 0x1b, 0xcd, - 0xea, 0xfc, 0xea, 0xa5, 0x65, 0x7e, 0x7f, 0xc7, 0x76, 0xba, 0x97, 0x41, - 0x2b, 0x2e, 0x80, 0x51, 0x40, 0x9b, 0x4b, 0x4f, 0x2b, 0x3c, 0x09, 0x28, - 0xbe, 0x88, 0x7a, 0xcd, 0x5e, 0x8f, 0x57, 0xa3, 0x6d, 0xfd, 0xbc, 0x45, - 0xa6, 0xc2, 0x0e, 0xd0, 0x97, 0xa1, 0x30, 0x2a, 0x8d, 0x4d, 0x98, 0x90, - 0x8a, 0x09, 0x00, 0x99, 0x24, 0x9c, 0x20, 0x45, 0xdb, 0x7b, 0x84, 0x0c, - 0x82, 0x4d, 0x26, 0xd1, 0xef, 0x24, 0x4e, 0xf6, 0x74, 0x63, 0xb1, 0x04, - 0x52, 0x90, 0x52, 0x05, 0x39, 0x31, 0x5e, 0x00, 0xc5, 0x5c, 0x8c, 0x27, - 0x1c, 0x9d, 0x54, 0x1b, 0x42, 0x32, 0xcc, 0xf4, 0x9a, 0x02, 0x42, 0xc9, - 0xb8, 0x0e, 0xe7, 0xa6, 0x60, 0x46, 0x68, 0x77, 0x10, 0xa6, 0x08, 0x45, - 0x40, 0xda, 0x46, 0x27, 0x81, 0xab, 0x78, 0x15, 0x36, 0x33, 0xb9, 0x84, - 0x03, 0x5b, 0xb9, 0xf7, 0x3c, 0xe9, 0x38, 0x1b, 0x40, 0x4f, 0x4c, 0xd4, - 0xb5, 0x24, 0xca, 0x40, 0x74, 0x0d, 0x2d, 0x6e, 0x58, 0x3b, 0x68, 0x31, - 0xa4, 0x2a, 0xae, 0xc7, 0x47, 0x87, 0x1b, 0x8a, 0x01, 0x37, 0x9c, 0x87, - 0x7d, 0xa3, 0x65, 0xf4, 0xad, 0x93, 0xdb, 0x55, 0x48, 0xf7, 0x7f, 0x18, - 0xad, 0x3d, 0x5e, 0x3d, 0xa7, 0x66, 0x47, 0x54, 0x3f, 0x42, 0xee, 0x7c, - 0x2b, 0x26, 0x11, 0xe5, 0x53, 0xa8, 0x15, 0x93, 0x43, 0xca, 0xb3, 0xa9, - 0x7a, 0xea, 0x7a, 0xd4, 0xd5, 0x16, 0x43, 0x6f, 0x43, 0x2e, 0x79, 0x59, - 0x88, 0x9b, 0xb3, 0x1e, 0x68, 0xa1, 0xd6, 0xc8, 0xeb, 0x23, 0x9e, 0x2c, - 0xb6, 0x08, 0xaf, 0x36, 0x45, 0x15, 0x55, 0x06, 0xb2, 0xbc, 0x6a, 0x47, - 0x42, 0x15, 0x0b, 0xed, 0x48, 0xaf, 0x96, 0x71, 0x7b, 0x79, 0x6e, 0x0a, - 0xa0, 0x64, 0x06, 0xab, 0xad, 0x30, 0x6b, 0x46, 0x26, 0x20, 0xca, 0x41, - 0xfb, 0x10, 0x3e, 0x16, 0xaf, 0x57, 0xe7, 0x55, 0x2b, 0x39, 0xf7, 0xff, - 0x1d, 0xda, 0xea, 0x5d, 0x04, 0xac, 0xba, 0xc4, 0xab, 0xa2, 0xea, 0xa0, - 0x4b, 0x45, 0x32, 0x6f, 0x7b, 0x05, 0xee, 0x09, 0x63, 0xc4, 0x60, 0xd6, - 0x00, 0x72, 0x49, 0xa6, 0x31, 0x82, 0x97, 0x7d, 0x19, 0x5d, 0x5d, 0xe7, - 0x1b, 0xee, 0xdf, 0x88, 0x8a, 0x4a, 0x70, 0x8f, 0xda, 0x52, 0xd9, 0x34, - 0x64, 0x43, 0xb6, 0x38, 0x5f, 0x01, 0x14, 0xb2, 0x2c, 0xde, 0xba, 0xa4, - 0x20, 0xae, 0x0e, 0x80, 0x05, 0x06, 0x58, 0xb6, 0x49, 0xcc, 0x11, 0xdc, - 0x97, 0x11, 0x15, 0x46, 0xb6, 0x41, 0x12, 0x00, 0xac, 0xeb, 0x3a, 0x54, - 0x49, 0xf5, 0x26, 0x88, 0xa8, 0x68, 0x02, 0x20, 0x34, 0x4d, 0x08, 0x7c, - 0xae, 0x37, 0x3c, 0x2a, 0x73, 0xff, 0xf0, 0xd1, 0xb9, 0x3a, 0x18, 0x53, - 0xdf, 0x1a, 0x87, 0x33, 0x3a, 0xcb, 0x86, 0xf8, 0x54, 0x05, 0xf6, 0x7e, - 0xff, 0xf1, 0x50, 0x80, 0x30, 0xbf, 0xfc, 0x21, 0x1a, 0xcb, 0x1f, 0x6f, - 0xff, 0xc0, 0x00, 0x8a, 0x89, 0xb1, 0xd1, 0xac, 0x47, 0xb4, 0xbd, 0x79, - 0xbd, 0xe9, 0x9b, 0xfd, 0x3f, 0xcd, 0x6f, 0x86, 0x48, 0x15, 0x02, 0x81, - 0x31, 0x4c, 0x01, 0xe6, 0x56, 0x83, 0xc6, 0x4f, 0x4f, 0x6d, 0xe1, 0xe9, - 0x7a, 0x0f, 0x69, 0xb7, 0x5c, 0xe6, 0xb6, 0xf0, 0x3e, 0x1b, 0xeb, 0xd5, - 0xcf, 0x1b, 0x4e, 0xf2, 0x9c, 0xb2, 0xdd, 0x65, 0x60, 0x39, 0x05, 0xc3, - 0x51, 0x95, 0xe4, 0xcb, 0x1b, 0x54, 0x42, 0x70, 0x4c, 0x69, 0xf1, 0x10, - 0xa1, 0x39, 0xf7, 0x21, 0xb3, 0xfc, 0xf8, 0x12, 0x24, 0xd4, 0xb9, 0xb3, - 0x95, 0xa4, 0x9d, 0x72, 0xdc, 0xe2, 0xc8, 0x06, 0x18, 0x5a, 0x74, 0xd0, - 0x94, 0x1b, 0x03, 0x89, 0x11, 0xb4, 0xd3, 0x41, 0x57, 0x0f, 0xe1, 0x50, - 0x0a, 0x28, 0x20, 0x09, 0x84, 0xf6, 0x17, 0xac, 0xaa, 0x1c, 0x10, 0xa4, - 0x57, 0x2f, 0xd2, 0x97, 0x46, 0xd2, 0xd3, 0x1d, 0xe6, 0xf9, 0xab, 0xa1, - 0xe4, 0x7f, 0xf8, 0x9e, 0x4a, 0x77, 0xb3, 0x8d, 0x01, 0xe2, 0x39, 0xe6, - 0x63, 0x6e, 0xf7, 0xb7, 0x28, 0x3c, 0xba, 0xa4, 0xf9, 0x97, 0x00, 0x51, - 0x17, 0x22, 0x47, 0x44, 0xc9, 0x63, 0xb2, 0xf8, 0xbf, 0x73, 0x21, 0x8d, - 0x73, 0xdf, 0x29, 0xfb, 0xd1, 0x08, 0x04, 0xf7, 0x20, 0xa3, 0x6c, 0x49, - 0x97, 0xad, 0x25, 0x39, 0x69, 0x14, 0xcb, 0xca, 0xcb, 0x81, 0x1b, 0x5e, - 0x89, 0x3b, 0xed, 0x55, 0x7b, 0xc2, 0xb4, 0x5c, 0x77, 0x18, 0xc5, 0x90, - 0x92, 0x6d, 0x1d, 0x90, 0xc8, 0x3d, 0x30, 0xb8, 0x44, 0x81, 0x73, 0x0a, - 0xad, 0x14, 0x93, 0x35, 0x14, 0x77, 0xb3, 0x6e, 0xef, 0x17, 0xf9, 0x57, - 0x5c, 0x4a, 0xe7, 0x3d, 0xa0, 0xfe, 0x42, 0x8d, 0x83, 0xf7, 0xc5, 0x73, - 0xd3, 0xdb, 0xad, 0x2c, 0xb1, 0x6a, 0xd7, 0xbb, 0xb9, 0x64, 0xd5, 0xc2, - 0x58, 0x38, 0x7b, 0x29, 0x49, 0xee, 0x27, 0x5d, 0xfd, 0x71, 0xbd, 0x33, - 0x9f, 0xcf, 0xf9, 0xf5, 0xae, 0x19, 0x20, 0x54, 0x13, 0x2e, 0xaa, 0x05, - 0x5e, 0xc5, 0x8f, 0xe9, 0xe1, 0xe8, 0x0a, 0xd2, 0xc3, 0xfe, 0xfc, 0xd0, - 0xf2, 0x2b, 0x9a, 0xd1, 0x43, 0x12, 0x2b, 0x56, 0x44, 0x80, 0x87, 0x8a, - 0x35, 0x1e, 0xad, 0xcb, 0x8f, 0xba, 0x05, 0x31, 0x14, 0x59, 0xe5, 0x2f, - 0x74, 0xd7, 0x55, 0x38, 0x49, 0x77, 0x28, 0x41, 0x99, 0xdc, 0x41, 0x0c, - 0x7d, 0xbf, 0x0d, 0x6c, 0x8f, 0x40, 0x18, 0x39, 0x01, 0x9d, 0xce, 0xd3, - 0x5c, 0xe2, 0x30, 0x63, 0x08, 0xc8, 0x22, 0x15, 0x0a, 0x10, 0x1c, 0x09, - 0xdf, 0x24, 0x09, 0xcf, 0xd9, 0xff, 0x7e, 0x17, 0xda, 0xac, 0x95, 0x93, - 0x14, 0xa2, 0x6e, 0xb5, 0x09, 0x2b, 0x2e, 0xe3, 0x9e, 0x36, 0xa8, 0xbc, - 0x63, 0x55, 0x02, 0x79, 0xc7, 0xff, 0xf1, 0x50, 0x80, 0x34, 0x1f, 0xfc, - 0x21, 0x1a, 0xcd, 0xe7, 0x6f, 0xff, 0xc0, 0x07, 0x85, 0xa8, 0x32, 0x91, - 0xac, 0x45, 0xf5, 0x9a, 0xeb, 0x5b, 0xd1, 0xcf, 0xe7, 0xfc, 0x63, 0x51, - 0xbe, 0x28, 0x15, 0x42, 0x87, 0x19, 0xb2, 0xa8, 0x04, 0x06, 0x39, 0x6d, - 0xa9, 0x92, 0x8c, 0x0b, 0x34, 0x74, 0x07, 0x6d, 0x92, 0x5a, 0x5f, 0x5e, - 0x62, 0x4c, 0x5a, 0x92, 0x54, 0xc2, 0x16, 0x7a, 0xab, 0x84, 0x40, 0xa8, - 0x08, 0xb7, 0x12, 0xcf, 0x20, 0xfc, 0xef, 0xe2, 0x92, 0x41, 0xcb, 0x2b, - 0xa4, 0xd7, 0xa8, 0xc5, 0x8a, 0x56, 0xdb, 0x3e, 0xfe, 0xc4, 0xd3, 0xbf, - 0xcc, 0x63, 0x5c, 0xb3, 0x1c, 0x82, 0x01, 0x05, 0x0f, 0x6c, 0x02, 0x00, - 0x09, 0x37, 0xa8, 0x42, 0x27, 0x2e, 0x50, 0x80, 0x22, 0x28, 0x06, 0x1a, - 0x45, 0x1a, 0xad, 0xc7, 0xce, 0x77, 0xca, 0x09, 0x04, 0x80, 0x16, 0x6b, - 0xd1, 0x08, 0x99, 0x50, 0x63, 0x77, 0x93, 0x76, 0xfc, 0xb0, 0x14, 0x22, - 0xe3, 0x96, 0xa1, 0x01, 0x06, 0x41, 0xb7, 0x94, 0xa1, 0x2f, 0x78, 0x80, - 0x06, 0x9a, 0xad, 0x86, 0x3a, 0x2b, 0x98, 0x71, 0xdc, 0x76, 0xc1, 0xd5, - 0x83, 0x80, 0xfa, 0x53, 0x68, 0x8d, 0x4d, 0xcb, 0x5a, 0xe5, 0xb7, 0xf1, - 0xb0, 0x33, 0x20, 0x3b, 0x9a, 0x8c, 0xd3, 0xef, 0xcf, 0x69, 0xe1, 0x45, - 0x60, 0xa9, 0x10, 0xac, 0xce, 0x56, 0x48, 0x21, 0xe6, 0x12, 0xd1, 0x5f, - 0x48, 0xe0, 0x4c, 0x18, 0xda, 0x81, 0x04, 0xc0, 0x14, 0xa6, 0x66, 0x5b, - 0x6b, 0x5d, 0xbd, 0x54, 0xa2, 0x90, 0x45, 0x14, 0x0d, 0xee, 0x68, 0xcb, - 0x72, 0x53, 0x00, 0x27, 0x7a, 0xcc, 0x13, 0xd6, 0xf0, 0xc6, 0xc5, 0x69, - 0x9d, 0xd2, 0xc4, 0x99, 0x19, 0x23, 0x83, 0xcb, 0x0e, 0xca, 0x11, 0x6e, - 0xf2, 0xa8, 0x2e, 0xa2, 0x6a, 0xdf, 0x18, 0x5a, 0x34, 0x39, 0x8a, 0x84, - 0xf5, 0xb0, 0xdb, 0xfb, 0xe6, 0xba, 0xb8, 0x3d, 0xfd, 0x7f, 0x8e, 0xf5, - 0xa8, 0xdf, 0x14, 0x0a, 0xab, 0xc5, 0x5b, 0x2f, 0x2e, 0xb2, 0xa8, 0x17, - 0x6a, 0x9e, 0x5d, 0x15, 0x88, 0x17, 0xcd, 0x48, 0x66, 0x82, 0x3f, 0xc3, - 0x61, 0xb3, 0x09, 0x01, 0x2c, 0x09, 0xf6, 0xee, 0x5e, 0xff, 0x51, 0xcb, - 0xd8, 0x86, 0x62, 0x57, 0x18, 0x20, 0x83, 0x49, 0xc4, 0xcb, 0xf9, 0x05, - 0x98, 0x13, 0x69, 0xb9, 0x24, 0xdd, 0xc4, 0xe8, 0x21, 0x74, 0x2d, 0xce, - 0x7f, 0x40, 0x05, 0x84, 0xd3, 0x13, 0x45, 0xf4, 0x80, 0x16, 0xb2, 0x91, - 0x21, 0xc4, 0x95, 0x2f, 0x85, 0x20, 0x0d, 0xf2, 0x19, 0x02, 0x83, 0x1b, - 0xb4, 0xf7, 0xd1, 0xdb, 0x7d, 0x3f, 0x75, 0xfe, 0x95, 0xbd, 0x48, 0xe1, - 0x54, 0x25, 0xce, 0x8d, 0xeb, 0x21, 0xb6, 0xdf, 0x9c, 0xcd, 0x37, 0xc0, - 0xe8, 0xb4, 0x51, 0x0c, 0x25, 0xcf, 0x6d, 0x18, 0x06, 0xa0, 0xce, 0x4a, - 0x98, 0x40, 0xce, 0x81, 0x8b, 0xa6, 0xa2, 0xf1, 0x7e, 0x54, 0xde, 0x35, - 0x57, 0x3b, 0x53, 0x81, 0x25, 0x0c, 0x11, 0x5e, 0x01, 0x8d, 0x3a, 0x17, - 0x3e, 0xff, 0xf1, 0x50, 0x80, 0x31, 0xbf, 0xfc, 0x21, 0x1a, 0xce, 0x5c, - 0xae, 0x7f, 0x80, 0x01, 0x83, 0xa1, 0xc3, 0x18, 0xca, 0x54, 0x6b, 0x11, - 0x75, 0xad, 0x3c, 0xa6, 0xb9, 0xcf, 0x5f, 0x7e, 0x5d, 0x0b, 0xc5, 0xe5, - 0x6b, 0x77, 0x94, 0x13, 0x4e, 0xd5, 0x50, 0x03, 0x52, 0x9d, 0x59, 0x9e, - 0x20, 0xbf, 0xa3, 0x75, 0x2b, 0xc1, 0x35, 0x8c, 0xcb, 0x66, 0x70, 0x99, - 0x93, 0xcb, 0x86, 0xa2, 0xb0, 0x46, 0x27, 0xfc, 0x41, 0x83, 0xe4, 0x03, - 0x17, 0x84, 0x4b, 0xb5, 0x1e, 0x38, 0x89, 0xf6, 0x9c, 0x83, 0x05, 0xbb, - 0x43, 0xcb, 0xd6, 0x08, 0x81, 0x5f, 0x8e, 0xe3, 0xcf, 0x73, 0xa5, 0x1d, - 0x95, 0x54, 0xc0, 0xaf, 0x75, 0xc2, 0x51, 0xa9, 0x8b, 0x6f, 0xcb, 0xa4, - 0x9e, 0xe9, 0x9d, 0xdc, 0x27, 0x46, 0x97, 0x1f, 0x1a, 0xd7, 0xbb, 0xe9, - 0x9f, 0x96, 0xd0, 0x88, 0x2a, 0xd9, 0x85, 0xa2, 0xef, 0x44, 0xd8, 0xe3, - 0x84, 0x43, 0x9e, 0x5b, 0xaa, 0xe9, 0x9a, 0xd3, 0xbb, 0xe0, 0x4f, 0x79, - 0xd3, 0x6d, 0xb5, 0xec, 0x73, 0x17, 0x78, 0xb0, 0x28, 0x61, 0x29, 0x44, - 0x02, 0x06, 0x58, 0x8b, 0x99, 0x72, 0x34, 0x85, 0xf9, 0xdc, 0xe1, 0xa3, - 0x1c, 0x90, 0xd1, 0x35, 0x8f, 0xcd, 0xc2, 0xe7, 0xb6, 0x71, 0x5e, 0x73, - 0x7e, 0x22, 0x5c, 0xe8, 0xad, 0xa4, 0x0c, 0xc5, 0x05, 0x9a, 0xfd, 0x30, - 0x0e, 0x59, 0x63, 0x97, 0xa6, 0x23, 0x9a, 0x25, 0x32, 0xbc, 0x72, 0xbc, - 0x06, 0x39, 0x10, 0x77, 0x25, 0x63, 0x9c, 0xe0, 0xbc, 0xbb, 0xdf, 0x27, - 0x2a, 0xfb, 0xd2, 0x5e, 0x6e, 0x15, 0x0d, 0x4c, 0xb5, 0x96, 0xa4, 0x0b, - 0x01, 0xd9, 0xa6, 0x14, 0xa6, 0x8c, 0x02, 0xb5, 0x45, 0x2f, 0x06, 0x5c, - 0x33, 0xa8, 0x3b, 0x74, 0xda, 0x06, 0xec, 0xb2, 0xd7, 0x31, 0xd8, 0x12, - 0x9e, 0x05, 0x6c, 0xf7, 0x3c, 0xd8, 0xb4, 0x1d, 0x0a, 0x24, 0x88, 0xf3, - 0xa0, 0x7c, 0xcc, 0x15, 0xce, 0xab, 0xc9, 0x2b, 0x73, 0xf4, 0xff, 0x1d, - 0xce, 0x9b, 0xcf, 0x6c, 0x5e, 0x56, 0xb7, 0x18, 0xd6, 0xda, 0xba, 0xcb, - 0xbc, 0xf0, 0x09, 0xfa, 0x59, 0x09, 0x9f, 0xf1, 0x9d, 0x06, 0x40, 0xab, - 0xa8, 0xce, 0x8f, 0x95, 0x9c, 0x36, 0xaf, 0x3d, 0x19, 0x12, 0x5f, 0xd6, - 0xaa, 0xb6, 0x7e, 0xc9, 0xce, 0xf2, 0xd6, 0x99, 0xaa, 0xd1, 0x02, 0xba, - 0xd0, 0x1a, 0xe7, 0x52, 0x31, 0x4a, 0x95, 0x08, 0x0c, 0xb0, 0x90, 0xa1, - 0x16, 0x13, 0x02, 0xaf, 0x4a, 0x51, 0xdc, 0x72, 0x45, 0x9f, 0xbe, 0x6f, - 0xdd, 0x2c, 0x1f, 0x8c, 0xf7, 0xf0, 0xc3, 0xe6, 0xe8, 0xb2, 0x9e, 0xc1, - 0x44, 0x22, 0xd5, 0x61, 0x2d, 0xdf, 0x77, 0xc4, 0xbd, 0x26, 0xa5, 0xe2, - 0x88, 0x14, 0x21, 0xc7, 0x09, 0xba, 0x03, 0xc8, 0xc2, 0x2e, 0xf4, 0xe9, - 0xce, 0xab, 0xd2, 0x4b, 0x22, 0x94, 0xca, 0x2c, 0xa7, 0x05, 0x4c, 0xc6, - 0xc9, 0xf8, 0xff, 0xf1, 0x50, 0x80, 0x2b, 0xdf, 0xfc, 0x21, 0x1a, 0xcf, - 0xf4, 0xfe, 0x7f, 0x80, 0x00, 0x84, 0x88, 0x30, 0xd4, 0xe8, 0xe6, 0x1a, - 0x7b, 0x49, 0x77, 0x97, 0x7c, 0xbe, 0xdf, 0xaf, 0x35, 0xe6, 0x5e, 0xf5, - 0xbd, 0x55, 0x56, 0xb3, 0x0a, 0x0e, 0x9b, 0xda, 0xa8, 0x2a, 0xe3, 0xca, - 0x62, 0x4d, 0xbf, 0x39, 0x6d, 0x96, 0x24, 0xc7, 0x6d, 0x99, 0x1e, 0x17, - 0xa1, 0x62, 0x01, 0x23, 0xfe, 0xc2, 0xc3, 0xd3, 0x06, 0xa1, 0x9c, 0x08, - 0x80, 0x27, 0x16, 0x09, 0x73, 0xb5, 0xc7, 0x0e, 0x12, 0x41, 0x66, 0xf1, - 0x4c, 0x22, 0xac, 0x87, 0x7e, 0x8f, 0xa7, 0xa5, 0x9d, 0x18, 0x55, 0x0c, - 0xf1, 0xe1, 0xad, 0xcd, 0xf2, 0xad, 0x3e, 0xf1, 0x89, 0xea, 0xdc, 0x11, - 0x22, 0xe9, 0x5a, 0xc7, 0xc2, 0x26, 0x97, 0x00, 0x52, 0x26, 0xf6, 0xd4, - 0x5d, 0xaf, 0x9d, 0x2b, 0xa2, 0xbd, 0x66, 0xd5, 0x0c, 0x5e, 0x47, 0x83, - 0x9c, 0x59, 0xe7, 0xaf, 0x89, 0x74, 0x44, 0xf9, 0x60, 0x1d, 0x4d, 0x0e, - 0x7a, 0x30, 0x08, 0x5d, 0xfa, 0xf5, 0xc0, 0x1b, 0x15, 0x06, 0x3b, 0x02, - 0xa4, 0x09, 0x6a, 0x53, 0x6e, 0x54, 0xc5, 0xf7, 0xcc, 0x8a, 0x56, 0xf2, - 0x8e, 0x90, 0xdf, 0x15, 0x21, 0x34, 0x0c, 0x90, 0xc4, 0x55, 0x87, 0x54, - 0xfa, 0xed, 0xce, 0x64, 0x9f, 0x4e, 0xa7, 0xbe, 0x56, 0x6e, 0xd2, 0x74, - 0x22, 0x04, 0xe8, 0xe7, 0x5d, 0x1a, 0x1c, 0x81, 0x3b, 0x1a, 0xc6, 0xbe, - 0x3a, 0x2a, 0x7a, 0x5a, 0x1a, 0xe7, 0x26, 0xb1, 0x12, 0x12, 0x98, 0xda, - 0x21, 0x4e, 0x4b, 0xcd, 0x66, 0x62, 0x5a, 0x33, 0x92, 0xc8, 0x99, 0x06, - 0x97, 0x05, 0x99, 0x6d, 0xfb, 0x5b, 0x3e, 0x6b, 0x46, 0x5a, 0x6c, 0xb6, - 0xd5, 0x9a, 0x78, 0x49, 0x0c, 0x31, 0x8e, 0x88, 0xf7, 0xa7, 0xb4, 0x97, - 0x79, 0x79, 0x97, 0xf6, 0xfd, 0x79, 0xaf, 0x33, 0x75, 0xe7, 0x7a, 0xaa, - 0xad, 0x61, 0x45, 0xa8, 0x98, 0xbc, 0x13, 0xea, 0x7d, 0x4e, 0x86, 0xad, - 0x92, 0x74, 0xbd, 0x1c, 0x5e, 0x82, 0x9b, 0x0d, 0xe5, 0x9d, 0x1c, 0x09, - 0x0a, 0x5a, 0xe8, 0x3e, 0xfd, 0x5f, 0x3d, 0x67, 0xf4, 0xef, 0x48, 0xfa, - 0x67, 0x0a, 0x5b, 0x62, 0x00, 0x9b, 0x5c, 0xca, 0xc2, 0xae, 0x20, 0x68, - 0xb6, 0x53, 0x6d, 0x5b, 0x20, 0x0d, 0x18, 0xa3, 0xb1, 0xc6, 0x7d, 0x88, - 0x31, 0x43, 0x4b, 0xb9, 0x7e, 0xf5, 0x99, 0xad, 0xb0, 0xbb, 0x64, 0xee, - 0x78, 0xf9, 0x4d, 0x66, 0x76, 0x88, 0x56, 0x76, 0x5d, 0x4b, 0x50, 0xc3, - 0xc4, 0xa9, 0x70, 0xe0, 0xff, 0xf1, 0x50, 0x80, 0x36, 0x9f, 0xfc, 0x21, - 0x1a, 0xcb, 0xc7, 0x67, 0x6f, 0xc4, 0x0f, 0x82, 0xa1, 0xc4, 0x50, 0xac, - 0x12, 0x1b, 0x09, 0x12, 0xc3, 0x3c, 0xc9, 0xe4, 0x4a, 0xdf, 0xcf, 0xef, - 0x5b, 0xe8, 0x00, 0xa9, 0x85, 0x0d, 0x39, 0x2a, 0x80, 0xbb, 0x66, 0x62, - 0x67, 0x86, 0xb1, 0xa1, 0xea, 0x55, 0x35, 0xb3, 0xa4, 0x08, 0xf8, 0x6a, - 0xa4, 0x4d, 0x92, 0x18, 0x61, 0xb9, 0x92, 0x9e, 0x86, 0x2f, 0x8c, 0xeb, - 0xd9, 0x29, 0xa5, 0xb2, 0xf9, 0x73, 0xec, 0xb5, 0x1e, 0xf8, 0x21, 0x44, - 0x6d, 0x18, 0x35, 0x09, 0xaa, 0x01, 0x20, 0x25, 0x1c, 0x14, 0x1d, 0x3f, - 0x10, 0xb5, 0x27, 0x1c, 0x71, 0xba, 0x69, 0x75, 0x37, 0x34, 0xe0, 0xc2, - 0x9e, 0x81, 0xc4, 0x71, 0xae, 0xb6, 0x69, 0x95, 0xd8, 0xc3, 0x65, 0x5b, - 0xe4, 0xac, 0x95, 0x1c, 0x0c, 0x05, 0x09, 0x8a, 0xf0, 0x11, 0xc9, 0x4f, - 0xfa, 0x7e, 0x11, 0x49, 0xef, 0xd5, 0xd6, 0x6e, 0xe7, 0x15, 0x40, 0x10, - 0xb4, 0x56, 0xb3, 0x52, 0x84, 0xae, 0x1d, 0x6d, 0xb2, 0xab, 0xfa, 0x90, - 0xdc, 0x2e, 0x81, 0xe8, 0xd5, 0x08, 0xfe, 0x17, 0xa1, 0x7d, 0x55, 0xb7, - 0x7e, 0x4f, 0xcc, 0x18, 0xec, 0xaf, 0xc3, 0x92, 0xc3, 0x2e, 0x97, 0xf6, - 0x0f, 0xf7, 0x7f, 0x1b, 0xc5, 0x8d, 0x19, 0x07, 0x10, 0x83, 0x60, 0x2b, - 0x35, 0xfa, 0x81, 0x34, 0x03, 0x45, 0xdc, 0x88, 0x15, 0x14, 0x31, 0x64, - 0x63, 0x19, 0xb1, 0x04, 0xec, 0xeb, 0x7b, 0x7d, 0xda, 0xfa, 0x09, 0x31, - 0x07, 0x31, 0xe9, 0xe1, 0x2d, 0x42, 0x56, 0x50, 0x89, 0x21, 0xe7, 0x1b, - 0xfa, 0x54, 0xb9, 0x67, 0xc7, 0x26, 0x69, 0x89, 0xdb, 0x04, 0xc9, 0x31, - 0xae, 0xf0, 0x19, 0xc5, 0xe5, 0x71, 0x20, 0x46, 0xb2, 0xb2, 0x7b, 0x72, - 0xb3, 0x4a, 0x84, 0xbc, 0xcf, 0x74, 0x3d, 0x36, 0x0e, 0xa5, 0x42, 0x78, - 0xd0, 0x3e, 0x66, 0x23, 0xaf, 0x32, 0x4d, 0x5a, 0x57, 0x3e, 0xff, 0xbf, - 0x7a, 0xd1, 0xcf, 0x00, 0x54, 0x9b, 0xd6, 0xd5, 0x57, 0x8a, 0x55, 0x03, - 0xee, 0x59, 0x5e, 0xbb, 0xb3, 0x55, 0x56, 0x5d, 0x6b, 0x12, 0x18, 0xf3, - 0xc6, 0xbc, 0xca, 0x96, 0x12, 0x12, 0x8b, 0x52, 0xe1, 0xc5, 0x99, 0xca, - 0x34, 0x54, 0xb1, 0x0b, 0xd7, 0x57, 0x82, 0x07, 0x01, 0x52, 0xef, 0xf5, - 0x62, 0xcd, 0xa8, 0xaf, 0x1b, 0xc9, 0xb2, 0xd9, 0x0b, 0x18, 0x40, 0x59, - 0x96, 0xfb, 0xd4, 0xea, 0xf5, 0xc2, 0x73, 0x83, 0x45, 0x46, 0xa7, 0x1b, - 0xef, 0xea, 0x4e, 0xb8, 0x72, 0xde, 0x35, 0xe7, 0xab, 0x2f, 0xb7, 0x85, - 0x5a, 0xec, 0xbb, 0x8c, 0x6f, 0xd0, 0xef, 0xfb, 0x47, 0x01, 0xbb, 0x5b, - 0x95, 0x58, 0x5f, 0x2c, 0x6a, 0x89, 0x71, 0xd6, 0xa5, 0xf2, 0x80, 0x68, - 0x93, 0xb6, 0x8d, 0x54, 0x78, 0xa7, 0x9f, 0xed, 0x29, 0xf3, 0x5c, 0xe7, - 0x6e, 0xda, 0x80, 0x04, 0x66, 0xd2, 0xd5, 0x75, 0xaa, 0x2d, 0x50, 0xc9, - 0x6a, 0x2c, 0x5c, 0x94, 0xd7, 0x9b, 0x3f, 0x1a, 0x31, 0x4b, 0x9a, 0x79, - 0x22, 0x7a, 0xe5, 0xcf, 0x4d, 0x7a, 0xf2, 0x4a, 0x72, 0xa2, 0xf8, 0x42, - 0xaf, 0x3c, 0xc6, 0xbb, 0x5f, 0x01, 0xd9, 0xc0, 0xff, 0xf1, 0x50, 0x80, - 0x2d, 0x1f, 0xfc, 0x21, 0x1a, 0xcb, 0x09, 0xdf, 0xbf, 0x80, 0x42, 0x82, - 0x82, 0xb3, 0xd2, 0x8c, 0x35, 0xdc, 0xd6, 0x92, 0xa6, 0x4f, 0x9f, 0xdf, - 0x9a, 0xf2, 0xe5, 0xa4, 0x2a, 0x33, 0x65, 0x24, 0xe2, 0xbc, 0x0d, 0x85, - 0x7b, 0x79, 0x9b, 0x18, 0x9e, 0x16, 0xaf, 0x8f, 0x53, 0x87, 0xc0, 0x6b, - 0xc9, 0x8d, 0x75, 0x3d, 0xa6, 0x1a, 0xee, 0xba, 0x49, 0xfa, 0x11, 0x6e, - 0x7b, 0xe8, 0x0b, 0xc1, 0x77, 0xf9, 0x0f, 0x79, 0x95, 0xc2, 0x69, 0x00, - 0x92, 0xd0, 0xaa, 0x8c, 0x41, 0x6d, 0x7f, 0x51, 0x02, 0x36, 0x5c, 0x02, - 0xcf, 0x2c, 0xdd, 0x50, 0x07, 0x47, 0x7f, 0x8e, 0xd6, 0xb5, 0xf2, 0xc3, - 0x2a, 0xb5, 0xfb, 0x5f, 0xd6, 0xf5, 0x85, 0x0c, 0x73, 0x7c, 0xec, 0x9e, - 0xd1, 0x16, 0x37, 0x46, 0xdf, 0x67, 0xa7, 0x6e, 0xdb, 0x4a, 0x98, 0x0d, - 0x9e, 0x44, 0x8b, 0x3f, 0x31, 0x37, 0x18, 0x31, 0xef, 0x4f, 0x2b, 0x28, - 0x55, 0x39, 0x7a, 0x95, 0x3a, 0x7c, 0x50, 0x06, 0x0b, 0x4d, 0x35, 0x90, - 0x26, 0x10, 0x1a, 0xa8, 0xab, 0xe3, 0x35, 0x41, 0xd3, 0x64, 0x66, 0x52, - 0xb3, 0x80, 0xdb, 0x8c, 0x17, 0x25, 0x68, 0xde, 0x2d, 0x9f, 0xb2, 0x96, - 0x2e, 0x68, 0xbd, 0xe3, 0x16, 0x55, 0x56, 0x36, 0x57, 0x73, 0x72, 0xff, - 0x73, 0x02, 0x4f, 0x5d, 0x08, 0x86, 0x3b, 0x70, 0x34, 0x48, 0x08, 0xdc, - 0x81, 0x5c, 0xd6, 0x32, 0x36, 0x42, 0x69, 0xa2, 0xc1, 0x46, 0x95, 0xdf, - 0x7a, 0x1b, 0xc4, 0xc3, 0x6b, 0x8c, 0x3a, 0xcb, 0x2d, 0x6b, 0x3d, 0x06, - 0xe2, 0x92, 0x67, 0x01, 0xba, 0xda, 0x55, 0xae, 0x80, 0xb0, 0xd4, 0x15, - 0x28, 0x2f, 0x41, 0x07, 0xc8, 0x81, 0xf0, 0x97, 0xed, 0x55, 0xa7, 0x0f, - 0x13, 0xe7, 0xef, 0x55, 0xe5, 0xcb, 0x8a, 0x85, 0x41, 0x32, 0x14, 0x99, - 0xc5, 0x7a, 0xc0, 0xd1, 0x2e, 0x07, 0xd1, 0x61, 0xea, 0x33, 0x1e, 0x58, - 0xaf, 0x6b, 0x4c, 0xe5, 0x09, 0x27, 0x72, 0x97, 0x18, 0x06, 0x50, 0x63, - 0xa2, 0xb5, 0x16, 0x32, 0x87, 0x0c, 0x6d, 0xd8, 0x02, 0x43, 0x58, 0xb4, - 0xe7, 0x53, 0x28, 0x9e, 0x88, 0x10, 0x07, 0x20, 0x12, 0x57, 0x67, 0x4c, - 0x33, 0xa1, 0xad, 0x7b, 0x37, 0xd9, 0x0a, 0x5c, 0x62, 0x2a, 0x8e, 0xda, - 0xa0, 0x2e, 0x33, 0x21, 0xc6, 0x00, 0x66, 0x66, 0x23, 0x1c, 0x3a, 0x79, - 0xfa, 0xa3, 0x29, 0x59, 0x33, 0x85, 0x6a, 0x2c, 0x01, 0xa5, 0x9f, 0x85, - 0xbc, 0xb9, 0x54, 0x4f, 0xec, 0xd3, 0x27, 0x64, 0xf4, 0x11, 0xba, 0x80, - 0xff, 0x23, 0xf6, 0xc3, 0xf7, 0xcd, 0x51, 0xc0, 0xff, 0xf1, 0x50, 0x80, - 0x2b, 0x1f, 0xfc, 0x21, 0x1a, 0xcf, 0x71, 0xee, 0x7f, 0x80, 0x20, 0x7f, - 0xa2, 0xc2, 0xd8, 0x84, 0x54, 0x21, 0x1d, 0x08, 0xc3, 0x35, 0xab, 0x93, - 0x1d, 0x77, 0x5e, 0xbf, 0x1c, 0xdf, 0x0e, 0x75, 0x00, 0x57, 0x34, 0x4a, - 0x9d, 0x3d, 0x15, 0x80, 0xaf, 0xdd, 0x61, 0x09, 0xd5, 0xe1, 0x1b, 0x2c, - 0xdb, 0x84, 0xf3, 0xa7, 0x0b, 0x0d, 0x60, 0x70, 0x49, 0xc2, 0x03, 0xc9, - 0x5d, 0x28, 0x47, 0x27, 0xbc, 0x03, 0x4c, 0x39, 0x46, 0x7d, 0xce, 0x71, - 0x3f, 0x62, 0x95, 0x53, 0xc6, 0x73, 0x8a, 0xa6, 0x51, 0x01, 0x12, 0x23, - 0xae, 0x17, 0x42, 0xb8, 0x10, 0x0b, 0x51, 0xf2, 0xcf, 0xf6, 0x49, 0x98, - 0xc6, 0x0c, 0x0c, 0x27, 0x45, 0x44, 0x39, 0xfe, 0x05, 0xa7, 0x69, 0xe0, - 0xed, 0x27, 0xbd, 0x25, 0xa4, 0xf3, 0x3c, 0xbc, 0x3d, 0x8c, 0xe3, 0x3d, - 0xdc, 0x92, 0xd9, 0xce, 0xf8, 0x13, 0xb9, 0xe8, 0x73, 0x66, 0x48, 0x38, - 0x4c, 0xa8, 0x9c, 0xe7, 0x84, 0xd2, 0x12, 0x3f, 0xaf, 0x4b, 0x37, 0x38, - 0x52, 0x60, 0x73, 0xe8, 0xfd, 0x8c, 0x6c, 0xf7, 0xd7, 0xf3, 0x81, 0x22, - 0x9b, 0xd9, 0xad, 0xc4, 0x4b, 0xd2, 0xe6, 0x6a, 0x91, 0x59, 0x5f, 0xac, - 0x09, 0x87, 0xbb, 0x0a, 0x44, 0xcd, 0x53, 0x42, 0x6e, 0xcd, 0x9b, 0xba, - 0xf0, 0x4c, 0xd3, 0x38, 0xc0, 0xe9, 0xe2, 0xa7, 0xcd, 0xa8, 0x7f, 0x99, - 0x09, 0xb9, 0xb8, 0x9a, 0x85, 0xcd, 0x5c, 0x21, 0xc1, 0xaa, 0x56, 0x76, - 0x63, 0x6c, 0xf3, 0xdd, 0x48, 0xb9, 0x77, 0x41, 0xd6, 0x0e, 0xf6, 0x85, - 0xcf, 0x45, 0x72, 0x50, 0xe8, 0x9b, 0x01, 0xec, 0xa2, 0x77, 0xfb, 0x0d, - 0x31, 0x88, 0xeb, 0xf4, 0x98, 0x7c, 0xa6, 0xba, 0x48, 0x32, 0xbd, 0x7e, - 0x2a, 0xb8, 0x37, 0xa0, 0x0c, 0xb8, 0x99, 0x51, 0x52, 0x80, 0xb1, 0xe5, - 0x99, 0xd3, 0x25, 0xf1, 0xbe, 0xa7, 0x02, 0xc6, 0xc0, 0xcd, 0x12, 0xcd, - 0x3d, 0x22, 0xc9, 0x41, 0x69, 0x04, 0x28, 0x25, 0x6e, 0xdc, 0x86, 0xbe, - 0x23, 0xfb, 0x59, 0x35, 0xaa, 0x6e, 0x0b, 0x3f, 0xdf, 0xf9, 0x03, 0xbc, - 0x46, 0xd9, 0x30, 0xd9, 0x64, 0xde, 0x58, 0x67, 0x8b, 0xcf, 0x97, 0xb1, - 0x8a, 0xc5, 0x18, 0xc4, 0x29, 0x3b, 0x79, 0x58, 0xda, 0xe0, 0xaa, 0xd9, - 0x69, 0x16, 0x90, 0x4c, 0xca, 0x6a, 0x23, 0xa6, 0x6e, 0x90, 0x17, 0x44, - 0x12, 0xab, 0x90, 0x55, 0x61, 0x8c, 0xde, 0x6b, 0x4d, 0xdc, 0x54, 0x9f, - 0x0f, 0xa6, 0x5f, 0x80, 0xff, 0xf1, 0x50, 0x80, 0x32, 0x7f, 0xfc, 0x21, - 0x1a, 0xcf, 0xd6, 0xef, 0xef, 0xc0, 0x0e, 0x7f, 0xa1, 0x44, 0x95, 0x2c, - 0x34, 0x3b, 0x11, 0xc7, 0x12, 0x59, 0x0a, 0xfb, 0x7e, 0x15, 0x00, 0x02, - 0x94, 0x17, 0x58, 0x56, 0x07, 0x7e, 0x26, 0xf3, 0x70, 0xf6, 0xac, 0xfd, - 0xce, 0x0f, 0xb1, 0x7d, 0x99, 0x29, 0x35, 0xe1, 0x6f, 0xd4, 0x53, 0xa6, - 0x08, 0x43, 0x09, 0x16, 0x6c, 0xc4, 0xbb, 0x74, 0x05, 0x3d, 0x34, 0x4d, - 0xa7, 0x0e, 0x9a, 0x43, 0x64, 0x65, 0x1e, 0x07, 0x47, 0x18, 0xd2, 0x19, - 0xd6, 0x67, 0x40, 0x8b, 0x88, 0x02, 0x41, 0xef, 0x19, 0xca, 0x5d, 0x89, - 0xc6, 0x25, 0xbe, 0xd2, 0xd6, 0x6b, 0x83, 0x1b, 0x29, 0x40, 0x2e, 0xd9, - 0xcd, 0x3b, 0xf7, 0xac, 0x6c, 0x28, 0x89, 0x20, 0xa5, 0xd1, 0x4b, 0xbb, - 0x5c, 0x34, 0xef, 0x42, 0x37, 0x3a, 0xa8, 0x85, 0xd4, 0x54, 0x93, 0xb5, - 0xaf, 0x5a, 0xc4, 0x94, 0x01, 0xbc, 0x02, 0x87, 0x3a, 0xc0, 0xde, 0x55, - 0xa6, 0x44, 0x85, 0x1b, 0x18, 0x75, 0xb9, 0x1a, 0x9e, 0x41, 0x18, 0x09, - 0x26, 0xec, 0x97, 0xd6, 0xc0, 0x6f, 0xe8, 0x91, 0x8d, 0xf9, 0xf7, 0x60, - 0xa0, 0x4f, 0xb3, 0xf0, 0x37, 0x76, 0xbe, 0x81, 0xb2, 0xa4, 0xe2, 0x3f, - 0xa1, 0xbc, 0xd6, 0xee, 0x89, 0x83, 0x23, 0xaa, 0xe9, 0x4a, 0xe0, 0x17, - 0x68, 0x73, 0xc1, 0x82, 0x0b, 0x53, 0x6c, 0x14, 0xf5, 0xe9, 0x7a, 0xc1, - 0x64, 0x29, 0x73, 0xc5, 0x58, 0xc0, 0x76, 0xbe, 0x4d, 0xaf, 0xd4, 0xf7, - 0x42, 0x82, 0x42, 0xcf, 0x6d, 0xa8, 0xb5, 0xdc, 0xe5, 0x5a, 0x51, 0x8e, - 0xb6, 0x2b, 0x81, 0x2d, 0xce, 0x33, 0x2a, 0x8a, 0x2e, 0x9e, 0x5c, 0x03, - 0xb5, 0x30, 0x41, 0x9a, 0xe9, 0x5d, 0x26, 0xb2, 0x62, 0xf5, 0x68, 0x5b, - 0x37, 0xe3, 0xd5, 0xe5, 0xf6, 0x58, 0xff, 0x61, 0xa6, 0x41, 0xd1, 0x3e, - 0xa6, 0x1f, 0x85, 0xc7, 0x12, 0x6a, 0xa1, 0x93, 0xed, 0xf8, 0xdd, 0xc0, - 0x00, 0xa4, 0x02, 0xf6, 0xac, 0x0d, 0x5e, 0x3f, 0xaa, 0x28, 0xb3, 0xa8, - 0xa5, 0xca, 0xde, 0xad, 0xc0, 0x4b, 0x14, 0xdc, 0x84, 0xa7, 0x3c, 0x9b, - 0xa4, 0x10, 0x1a, 0x66, 0xce, 0x28, 0x42, 0xe1, 0x22, 0x27, 0x84, 0x86, - 0x68, 0x12, 0x23, 0x1e, 0xe3, 0xf5, 0xc9, 0xd5, 0x12, 0xc9, 0x61, 0x40, - 0x4f, 0xc5, 0x29, 0xc1, 0x10, 0x96, 0xa7, 0x3c, 0x55, 0xf1, 0xf3, 0xd4, - 0xa5, 0x05, 0x96, 0xb3, 0xc6, 0x5d, 0xfa, 0xa3, 0xa0, 0x8a, 0xcb, 0xaf, - 0x86, 0x6a, 0x72, 0x78, 0x5f, 0xad, 0xa6, 0x84, 0xc9, 0x23, 0xd9, 0x4d, - 0x76, 0xe5, 0x51, 0x72, 0xda, 0x92, 0x9a, 0x5b, 0x28, 0x0b, 0x1e, 0x47, - 0x34, 0x13, 0x44, 0x2d, 0x69, 0x2c, 0x59, 0x6d, 0x4c, 0x08, 0x98, 0xb0, - 0x86, 0x0a, 0xf7, 0x5e, 0x3f, 0x8a, 0xaa, 0x4b, 0x2d, 0x7a, 0xad, 0xa4, - 0xc8, 0x3a, 0x57, 0x5d, 0xfd, 0x40, 0xef, 0x57, 0x4e, 0xe9, 0x78, 0xff, - 0xf1, 0x50, 0x80, 0x27, 0xdf, 0xfc, 0x21, 0x1a, 0xcd, 0xd5, 0x0d, 0x7f, - 0x80, 0x00, 0x7f, 0x88, 0x21, 0xcc, 0x88, 0xd6, 0x1b, 0x44, 0xe2, 0xf2, - 0x6b, 0x6f, 0x1f, 0x5b, 0xae, 0x95, 0x00, 0x39, 0x50, 0xab, 0xd6, 0x6e, - 0x8a, 0x10, 0xdf, 0xad, 0xb9, 0xfc, 0x07, 0x0c, 0x9e, 0x69, 0x2d, 0x2d, - 0x61, 0x9b, 0x7c, 0xe5, 0xdf, 0x79, 0x05, 0xac, 0xa8, 0x5e, 0x42, 0x7b, - 0x49, 0x67, 0x5e, 0x7c, 0x60, 0x79, 0xc0, 0x22, 0xde, 0x3d, 0xf0, 0x2d, - 0xa4, 0x63, 0xa8, 0x93, 0xc2, 0xe0, 0x2e, 0xbf, 0x6c, 0xeb, 0x90, 0x86, - 0xda, 0x0d, 0x5b, 0x99, 0x49, 0x98, 0xdb, 0xf0, 0x2a, 0x21, 0xba, 0xcf, - 0xa1, 0xc9, 0xbb, 0x02, 0xed, 0x69, 0x89, 0x5d, 0xc8, 0xb3, 0x2d, 0xec, - 0x52, 0x54, 0x6e, 0x46, 0x4b, 0x80, 0x37, 0x91, 0xc4, 0xd8, 0xe7, 0x12, - 0xd1, 0x5a, 0xf9, 0x37, 0xb9, 0xd4, 0x1b, 0xa7, 0xf1, 0xf7, 0xed, 0x5f, - 0x72, 0x1c, 0x20, 0x04, 0xe7, 0x26, 0x2e, 0x70, 0xce, 0x15, 0x8e, 0x96, - 0xa8, 0x42, 0xac, 0xed, 0xe5, 0xe5, 0x17, 0xc1, 0x2d, 0xe6, 0x6d, 0xc9, - 0xa1, 0x0d, 0xc2, 0x21, 0xbc, 0xab, 0xd2, 0xe7, 0xc7, 0x2b, 0x4e, 0x77, - 0x3c, 0x19, 0x70, 0x11, 0xb8, 0x39, 0x2a, 0xa3, 0xed, 0xbc, 0xf2, 0x5d, - 0x96, 0x9f, 0xd5, 0x50, 0x51, 0xf3, 0x05, 0xb3, 0x91, 0x46, 0x10, 0x62, - 0x12, 0xe5, 0x65, 0xc4, 0xf4, 0x43, 0x44, 0xb7, 0x67, 0x31, 0xa2, 0xcb, - 0x20, 0x04, 0xcf, 0x47, 0x61, 0x19, 0x4e, 0x78, 0xa1, 0xd8, 0xb2, 0x6b, - 0xea, 0x37, 0x37, 0xfb, 0x1b, 0x26, 0x48, 0x83, 0x34, 0xfb, 0xcd, 0x25, - 0x7b, 0x77, 0xc7, 0x1c, 0xfa, 0xce, 0x2b, 0xae, 0xb9, 0xd7, 0xcf, 0xdc, - 0x06, 0xf8, 0xc4, 0x95, 0x2a, 0x91, 0x78, 0x0e, 0xff, 0xea, 0xe0, 0x1f, - 0x4e, 0x3f, 0xb3, 0xc9, 0x24, 0x33, 0x5d, 0x04, 0xb1, 0x89, 0xc0, 0x0d, - 0x9d, 0xcc, 0x16, 0x49, 0x17, 0xf8, 0x1e, 0xbf, 0x6a, 0xa9, 0x5a, 0x19, - 0xf4, 0xbb, 0xdb, 0x0b, 0xdc, 0x9e, 0x83, 0x09, 0xe9, 0x74, 0x1f, 0x39, - 0x23, 0x28, 0xeb, 0x5f, 0x7f, 0x43, 0x36, 0xdb, 0x52, 0xa8, 0x12, 0xae, - 0x4f, 0xb1, 0xec, 0x6c, 0x5d, 0xa6, 0xd8, 0xad, 0x78, 0xf8, 0x2f, 0xcc, - 0x97, 0x51, 0xbb, 0xbb, 0x80, 0xff, 0xf1, 0x50, 0x80, 0x3a, 0x1f, 0xfc, - 0x21, 0x1a, 0xcf, 0x3b, 0x3e, 0xff, 0xfe, 0x6d, 0x7f, 0xa8, 0x21, 0x04, - 0xe8, 0x36, 0x2a, 0x11, 0x85, 0x02, 0x75, 0x3c, 0xa0, 0x1d, 0xf4, 0x00, - 0x00, 0x50, 0x8c, 0x53, 0x00, 0x34, 0x3c, 0x98, 0x27, 0x56, 0xb3, 0x29, - 0xee, 0x8e, 0x93, 0x4e, 0x6c, 0x57, 0x35, 0x70, 0x8c, 0x84, 0x02, 0x04, - 0xb1, 0x7b, 0xae, 0x06, 0xba, 0xa7, 0x8e, 0x32, 0x57, 0x16, 0x29, 0x83, - 0x51, 0x23, 0x10, 0x6b, 0xe4, 0x99, 0xdc, 0x0d, 0x8b, 0x5e, 0xef, 0x65, - 0xe4, 0x10, 0x43, 0x9f, 0x3e, 0xd4, 0x2b, 0x9d, 0xca, 0xf1, 0xd1, 0x69, - 0x1a, 0x65, 0xa7, 0x1c, 0x04, 0xa1, 0x6c, 0x5a, 0x27, 0x69, 0x8c, 0x85, - 0xfd, 0x6f, 0x7f, 0x02, 0xa3, 0x1b, 0x13, 0x6b, 0xcc, 0xdb, 0x1d, 0x19, - 0x26, 0xab, 0xfe, 0xe3, 0xdd, 0x0b, 0x90, 0x36, 0x7a, 0x96, 0xdb, 0x54, - 0x9e, 0x32, 0x09, 0xa7, 0x08, 0x99, 0x26, 0x61, 0x65, 0xb9, 0xe5, 0x71, - 0x8a, 0x61, 0xe4, 0x17, 0x92, 0x70, 0x61, 0x76, 0xb8, 0xe2, 0xa6, 0xbd, - 0xde, 0xcb, 0x4c, 0x68, 0x32, 0xb0, 0x61, 0x15, 0x73, 0x3a, 0xc5, 0x17, - 0x29, 0xa5, 0x4c, 0xd6, 0x6b, 0x9e, 0x42, 0x04, 0xfc, 0x2f, 0x32, 0x8d, - 0x11, 0x7b, 0xf7, 0x58, 0x34, 0x99, 0xce, 0x62, 0x8d, 0x9c, 0xa0, 0xa2, - 0xa1, 0x09, 0xdf, 0x14, 0x80, 0x54, 0xbb, 0x44, 0xe3, 0xfe, 0x9b, 0xb4, - 0x92, 0xd7, 0x16, 0x01, 0x2e, 0x14, 0x44, 0x5e, 0x72, 0xce, 0xfd, 0x43, - 0x9b, 0xac, 0xce, 0x61, 0xbd, 0x1e, 0xe4, 0xc9, 0x59, 0x9a, 0xf9, 0x78, - 0xa3, 0x9f, 0x64, 0x94, 0x02, 0x1f, 0x8c, 0xc3, 0xaa, 0x21, 0x97, 0xcc, - 0x4b, 0xc0, 0x14, 0xd4, 0x21, 0x4e, 0x45, 0xe8, 0xa3, 0x61, 0xfe, 0xc5, - 0x4e, 0x62, 0x09, 0xd0, 0xfe, 0x24, 0x17, 0x86, 0x08, 0xea, 0x5b, 0xa0, - 0x73, 0xfa, 0x7f, 0xa0, 0x00, 0x01, 0x4a, 0xd4, 0x32, 0xb0, 0x16, 0xf9, - 0xcb, 0xb0, 0x12, 0x6c, 0x33, 0x96, 0xac, 0xc7, 0x50, 0x7a, 0xac, 0x20, - 0x13, 0x11, 0x8a, 0x54, 0x94, 0x37, 0x00, 0x9f, 0x15, 0x39, 0xd3, 0x1b, - 0x78, 0x71, 0x23, 0xab, 0x20, 0x50, 0xe4, 0x03, 0x10, 0x4e, 0xe0, 0x64, - 0x6c, 0x52, 0x52, 0xa2, 0xd3, 0x70, 0x28, 0xac, 0x00, 0xcc, 0x57, 0x2f, - 0x4e, 0xe4, 0x64, 0x03, 0xf5, 0xe0, 0xe5, 0x54, 0xd3, 0x65, 0xb3, 0xb4, - 0x22, 0x57, 0x93, 0xc1, 0xc3, 0xac, 0x17, 0xbb, 0xb3, 0x1c, 0xea, 0xf6, - 0xad, 0x91, 0x61, 0xb7, 0x98, 0x82, 0x9c, 0x46, 0x12, 0x22, 0xbf, 0x67, - 0x81, 0x07, 0x3c, 0xf1, 0xec, 0xf2, 0xbb, 0xdb, 0xff, 0x96, 0xdc, 0x09, - 0x05, 0x82, 0x02, 0x76, 0x7b, 0xbf, 0xe6, 0x3f, 0x33, 0xfa, 0x3e, 0x71, - 0xf7, 0xe1, 0x3f, 0x07, 0xe3, 0x7f, 0x7c, 0x0e, 0xcd, 0x10, 0x47, 0xfe, - 0x9a, 0x7c, 0x3e, 0xfd, 0x5f, 0x00, 0xfe, 0xef, 0xcd, 0x74, 0x75, 0x6b, - 0x08, 0x5b, 0x31, 0xdb, 0xaf, 0x74, 0xcc, 0x06, 0x69, 0x50, 0x18, 0x37, - 0x9a, 0x20, 0xe5, 0x04, 0x20, 0xbc, 0xb6, 0x96, 0x11, 0xea, 0x53, 0xa6, - 0xca, 0x96, 0x08, 0x59, 0x29, 0x98, 0xd0, 0x47, 0x38, 0x26, 0xa1, 0x49, - 0xe4, 0xf0, 0x41, 0x29, 0x19, 0xc8, 0xd5, 0xa1, 0x55, 0x52, 0xe6, 0xe0, - 0x48, 0xf1, 0xa9, 0xc5, 0x60, 0x0f, 0x4f, 0x8f, 0x48, 0x88, 0x58, 0x7b, - 0x80, 0xff, 0xf1, 0x50, 0x80, 0x1a, 0xbf, 0xfc, 0x21, 0x1a, 0xce, 0xac, - 0xfc, 0x3e, 0x08, 0x0f, 0x7f, 0x76, 0x8b, 0xb4, 0x82, 0x1d, 0x08, 0x8b, - 0x48, 0x24, 0x03, 0xf7, 0xf7, 0xad, 0xd1, 0xad, 0xeb, 0xe2, 0xfe, 0x7c, - 0xe8, 0x37, 0x8f, 0x26, 0x3d, 0xe8, 0x96, 0xe4, 0x02, 0xbc, 0x07, 0xaa, - 0xfa, 0x3a, 0x2e, 0x7c, 0xe5, 0x92, 0xc0, 0x02, 0xeb, 0x3d, 0xfb, 0x10, - 0x6f, 0xab, 0x97, 0x4f, 0xad, 0x4b, 0x91, 0xcc, 0x13, 0xac, 0x4e, 0x02, - 0xe1, 0x1a, 0x74, 0xd2, 0x01, 0x20, 0x01, 0x40, 0x22, 0x40, 0x01, 0x12, - 0xb4, 0xc7, 0x08, 0xca, 0xab, 0x5e, 0x18, 0x06, 0x5b, 0x21, 0x3f, 0x97, - 0x1f, 0xc6, 0x4f, 0xf6, 0x25, 0x80, 0xbf, 0x44, 0x28, 0x03, 0xef, 0xdd, - 0x71, 0xee, 0x6b, 0x7a, 0xf8, 0xbe, 0xf3, 0xd8, 0x37, 0xae, 0x77, 0xbd, - 0xa3, 0xd9, 0xd4, 0xce, 0xc0, 0x2a, 0xf0, 0x16, 0xa1, 0x3d, 0xbb, 0xe7, - 0x8b, 0xa1, 0xf3, 0x9a, 0xb6, 0xe0, 0xbe, 0xa2, 0x87, 0xec, 0xec, 0x40, - 0x35, 0xab, 0x46, 0x06, 0x67, 0x1d, 0x73, 0x95, 0xbf, 0x4f, 0xec, 0x7f, - 0x6f, 0x37, 0xd1, 0x68, 0x50, 0xc4, 0x02, 0x72, 0x86, 0x2d, 0x77, 0x5c, - 0xbc, 0xfd, 0x5b, 0x36, 0xfc, 0xa1, 0x5d, 0x3f, 0x5c, 0x7f, 0x34, 0xe9, - 0xa2, 0x55, 0x0b, 0x85, 0x14, 0x00, 0x00, 0xa8, 0x35, 0xa1, 0x79, 0x07, - 0xc9, 0x05, 0x90, 0x2f, 0x42, 0x00, 0x2e, 0x00, 0x07, 0xe7, 0x44, 0xd5, - 0x4b, 0x4e, 0xbc, 0x19, 0x3b, 0x46, 0x38, 0x67, 0x2f, 0x4f, 0xff, 0xf1, - 0x50, 0x80, 0x31, 0xbf, 0xfc, 0x21, 0x1a, 0xce, 0x1f, 0xbf, 0xf7, 0xef, - 0xff, 0x7f, 0x91, 0x43, 0x90, 0x82, 0x82, 0x12, 0x85, 0x08, 0x44, 0x62, - 0x1d, 0x71, 0x5a, 0x80, 0xab, 0xa8, 0x00, 0x28, 0xc9, 0x41, 0x4a, 0x56, - 0x04, 0xfe, 0x0b, 0x77, 0x5a, 0xef, 0x27, 0x47, 0xc8, 0xa3, 0x3e, 0x7b, - 0xe3, 0x9e, 0xe0, 0xb8, 0x90, 0x81, 0xc5, 0xca, 0x14, 0x40, 0xeb, 0x9e, - 0xae, 0xef, 0xf1, 0x01, 0x10, 0x34, 0xe1, 0x4a, 0xd6, 0xa4, 0x8c, 0x73, - 0x9d, 0x48, 0xf2, 0x29, 0x67, 0xe6, 0xe8, 0x77, 0x6e, 0xd9, 0x27, 0xf3, - 0x2c, 0xa8, 0x02, 0x5e, 0xfc, 0xd9, 0x75, 0x2a, 0xac, 0x1c, 0xef, 0x02, - 0x35, 0xe0, 0x83, 0x75, 0x7a, 0xd2, 0x7d, 0x7d, 0x11, 0x27, 0xbb, 0xad, - 0xce, 0xfa, 0x1d, 0x4e, 0xa9, 0x3e, 0x71, 0x57, 0x7d, 0xf6, 0xb7, 0x77, - 0x0e, 0xaa, 0x64, 0xf1, 0x43, 0x69, 0x2c, 0xc9, 0x47, 0x28, 0x0d, 0x40, - 0x20, 0xd3, 0x44, 0x21, 0x15, 0x8a, 0x3a, 0xa5, 0x3e, 0x1d, 0x46, 0x8b, - 0xb1, 0x9d, 0x9a, 0xda, 0x42, 0xe6, 0x9e, 0x14, 0xe0, 0x0c, 0x00, 0x3a, - 0x50, 0x10, 0x70, 0x1f, 0x9e, 0xfe, 0x88, 0x2e, 0xea, 0x9c, 0x49, 0x02, - 0x02, 0xee, 0xc6, 0xe5, 0x0a, 0x53, 0xd4, 0x3c, 0x8d, 0x2c, 0xa2, 0x4b, - 0x66, 0x40, 0x9e, 0xc9, 0x68, 0x4c, 0x2d, 0x9e, 0xec, 0x54, 0x8e, 0x02, - 0xe0, 0xc6, 0x88, 0xb5, 0x50, 0x73, 0x67, 0xb2, 0xfb, 0x0e, 0x04, 0x62, - 0xfb, 0xa8, 0x7f, 0xa1, 0xc2, 0x94, 0xe2, 0x84, 0x29, 0x1d, 0x86, 0x7c, - 0x6a, 0x96, 0x0f, 0xcf, 0xeb, 0x50, 0x00, 0x51, 0x4a, 0x0a, 0x52, 0xb0, - 0x15, 0xef, 0xca, 0x16, 0x5c, 0x46, 0x94, 0x4e, 0xbf, 0x33, 0xcf, 0x1b, - 0x89, 0x2a, 0x2c, 0x2c, 0xac, 0x0b, 0x81, 0xd6, 0x6a, 0x7e, 0x9a, 0x0b, - 0x06, 0xb4, 0xb4, 0x78, 0x37, 0xd7, 0xfc, 0x3b, 0x75, 0xe9, 0xe8, 0x5c, - 0xda, 0xda, 0xba, 0x4a, 0xb2, 0xf0, 0xe3, 0x21, 0x75, 0xe9, 0x70, 0xb0, - 0x88, 0x45, 0xf3, 0xfd, 0xbb, 0xd8, 0x37, 0x4f, 0x2b, 0xd0, 0xdb, 0x82, - 0x54, 0x60, 0x65, 0xda, 0x6b, 0xa9, 0x3e, 0x6e, 0x75, 0x28, 0xc5, 0x19, - 0x0c, 0xa3, 0x04, 0x37, 0xe7, 0x84, 0x3d, 0x7e, 0x2b, 0xab, 0xad, 0x4c, - 0x12, 0x9e, 0x5f, 0x43, 0x4e, 0xf2, 0x4e, 0xae, 0xe8, 0x4a, 0x6c, 0x73, - 0xfa, 0x89, 0xf5, 0xb6, 0x79, 0x27, 0x26, 0x4f, 0xaa, 0xa4, 0xac, 0xbc, - 0x83, 0x2d, 0xcf, 0x40, 0x82, 0x05, 0x8a, 0x82, 0x80, 0x51, 0x80, 0x50, - 0x5a, 0x21, 0x5b, 0x2f, 0x4d, 0x34, 0x69, 0x93, 0xcb, 0xe3, 0xb4, 0x7f, - 0x23, 0x9f, 0xbd, 0x7a, 0x65, 0x0a, 0x68, 0x9d, 0xe0, 0xa6, 0x9c, 0x17, - 0x6c, 0x78, 0x5f, 0x08, 0x33, 0xa6, 0x44, 0x35, 0x1d, 0x46, 0x6d, 0x76, - 0x96, 0x85, 0xa2, 0x16, 0x67, 0x25, 0xb6, 0xfa, 0x6b, 0x29, 0x78, 0xff, - 0xf1, 0x50, 0x80, 0x2f, 0x5f, 0xfc, 0x21, 0x1a, 0xcc, 0xff, 0xa7, 0xff, - 0xff, 0xff, 0x7f, 0x83, 0x32, 0x1c, 0xc2, 0x94, 0x30, 0x91, 0x88, 0x71, - 0xd2, 0xd2, 0x28, 0x20, 0x00, 0xaa, 0x28, 0x28, 0xa6, 0x50, 0x02, 0x3b, - 0x39, 0x94, 0x7f, 0x1c, 0xf6, 0x11, 0x4b, 0xbe, 0x23, 0xd5, 0x60, 0x0b, - 0xd6, 0x55, 0x5c, 0x5e, 0x21, 0x15, 0x66, 0xd1, 0xf9, 0x55, 0x75, 0x73, - 0x5b, 0xf3, 0xbb, 0x7f, 0x83, 0xb9, 0xb8, 0x66, 0x20, 0xc6, 0x6f, 0x3c, - 0x32, 0xd2, 0x21, 0x78, 0x9f, 0xaa, 0x25, 0x80, 0x12, 0xcb, 0xbb, 0x28, - 0x85, 0x25, 0x4c, 0x51, 0xde, 0x92, 0x77, 0xf8, 0x77, 0xd4, 0x97, 0x2a, - 0x7c, 0x83, 0x1d, 0x55, 0x9c, 0xa7, 0x5d, 0x4c, 0x16, 0x37, 0x97, 0x3e, - 0xda, 0xca, 0xd4, 0x2f, 0x98, 0x7a, 0xb0, 0x09, 0xee, 0x6d, 0xb6, 0x20, - 0xb4, 0x95, 0x2e, 0xb1, 0xeb, 0x0a, 0x09, 0x51, 0xb6, 0x1a, 0x75, 0x49, - 0x1a, 0x71, 0xc3, 0x46, 0xe1, 0x93, 0x72, 0xdc, 0x32, 0x74, 0x50, 0x11, - 0x5c, 0xef, 0x3a, 0x33, 0x97, 0xd7, 0x0c, 0xdf, 0xe7, 0x68, 0x12, 0xbd, - 0x60, 0xc8, 0x02, 0x68, 0x44, 0x02, 0x96, 0xc5, 0x82, 0xb5, 0xc1, 0xcb, - 0x79, 0x30, 0x9d, 0x00, 0x63, 0x23, 0x8c, 0xc4, 0x98, 0x43, 0x27, 0x81, - 0xc8, 0xa8, 0x92, 0x9d, 0x67, 0x6c, 0xae, 0xb3, 0xa0, 0x5a, 0xcf, 0xf4, - 0x18, 0x7a, 0x0c, 0x52, 0x87, 0x11, 0xa0, 0x98, 0x6e, 0xb8, 0x2d, 0x22, - 0x9f, 0x6f, 0xbe, 0x4b, 0x00, 0x2a, 0x8a, 0x0a, 0x29, 0x94, 0x18, 0xeb, - 0x1e, 0x3b, 0xab, 0x1b, 0xcd, 0xef, 0xeb, 0x5e, 0x60, 0xc9, 0x24, 0xb2, - 0x41, 0x84, 0x6a, 0x5c, 0x96, 0xeb, 0xc6, 0xe5, 0xe9, 0x45, 0x2b, 0xc4, - 0x20, 0x2c, 0xe0, 0x58, 0x4a, 0xb0, 0x46, 0xbc, 0xcb, 0x8a, 0x98, 0x03, - 0x6f, 0x6c, 0xba, 0xd7, 0x5a, 0xb6, 0x46, 0xf4, 0x01, 0x28, 0x02, 0x5c, - 0xb3, 0x37, 0x85, 0xa5, 0x7d, 0xbd, 0x23, 0xdf, 0x51, 0x08, 0x52, 0xb0, - 0xaa, 0x98, 0xa2, 0x73, 0x08, 0x29, 0xc8, 0x54, 0xec, 0x77, 0x3d, 0xb6, - 0x1b, 0x7e, 0xcb, 0x03, 0xc8, 0xdc, 0xe5, 0xdd, 0x9c, 0x97, 0x3c, 0x29, - 0x16, 0xb7, 0xb0, 0xcd, 0xd2, 0x6b, 0xa2, 0xbb, 0x99, 0x87, 0xe5, 0xd8, - 0x49, 0xd1, 0xa0, 0x44, 0x68, 0x6a, 0x9a, 0x7b, 0xd9, 0xd3, 0x47, 0x6c, - 0xb8, 0x27, 0x15, 0xc1, 0xda, 0x88, 0x56, 0x9c, 0xa7, 0x2a, 0x49, 0x49, - 0xee, 0x08, 0x0c, 0x84, 0x1f, 0xd2, 0x09, 0xc9, 0x55, 0xf4, 0x2d, 0x14, - 0xe7, 0x9d, 0xb1, 0x45, 0xaa, 0x27, 0x06, 0x6a, 0x6b, 0x75, 0x84, 0x02, - 0x96, 0xe4, 0xd3, 0x50, 0xf5, 0x1f, 0x16, 0x27, 0x62, 0x19, 0xd6, 0x2b, - 0x82, 0x56, 0x78, 0x0b, 0x38, 0xff, 0xf1, 0x50, 0x80, 0x2a, 0x5f, 0xfc, - 0x21, 0x1a, 0xce, 0x0e, 0x0d, 0xff, 0x23, 0x2d, 0x7f, 0x82, 0x33, 0x45, - 0x68, 0x42, 0x20, 0x89, 0x06, 0xc2, 0x3a, 0x9c, 0x0b, 0x98, 0x12, 0x00, - 0x54, 0xca, 0x28, 0x28, 0xa6, 0x50, 0x56, 0xf0, 0x5d, 0xdf, 0x95, 0x33, - 0x28, 0x7e, 0x61, 0x6a, 0xdf, 0x62, 0x40, 0xa1, 0xb3, 0x61, 0x5d, 0xb6, - 0x75, 0xd3, 0x26, 0xaa, 0xca, 0x92, 0x42, 0x39, 0x97, 0xba, 0x67, 0x81, - 0xa0, 0x00, 0xf5, 0xe9, 0xe9, 0x93, 0x5e, 0x7f, 0x8c, 0x64, 0x13, 0x00, - 0x02, 0x91, 0xd0, 0x00, 0xc0, 0x2f, 0xd6, 0xfc, 0xe4, 0xaa, 0xaf, 0x7c, - 0x5c, 0xd3, 0xd0, 0x7a, 0x79, 0xfb, 0x48, 0x2d, 0xd1, 0xdc, 0x01, 0x05, - 0x2c, 0x9b, 0xa2, 0x7c, 0x30, 0xef, 0x0f, 0xda, 0xd6, 0x1b, 0xd0, 0xf0, - 0xcf, 0x5e, 0x94, 0x57, 0xb4, 0xc0, 0x87, 0xfd, 0x4c, 0xeb, 0xbc, 0xc2, - 0x5d, 0x2b, 0xf4, 0x4b, 0x19, 0x19, 0x7a, 0x6b, 0x8e, 0x69, 0x20, 0x37, - 0xe7, 0x60, 0xbd, 0x41, 0x2e, 0xa7, 0xb4, 0xb5, 0xcf, 0x3b, 0x7e, 0x1f, - 0xf0, 0x55, 0x48, 0x32, 0x1a, 0x1a, 0x8c, 0x07, 0xb6, 0x26, 0xd7, 0x69, - 0x69, 0x5e, 0xbf, 0xe6, 0x1e, 0xf0, 0x5d, 0x11, 0x3b, 0xda, 0x52, 0x4b, - 0x25, 0x6b, 0xcf, 0x1c, 0x91, 0x80, 0x54, 0x48, 0x8c, 0x4c, 0x04, 0x9a, - 0x97, 0xf8, 0x22, 0xb0, 0x5a, 0x46, 0xd0, 0x88, 0xd0, 0x86, 0xae, 0x1c, - 0x5c, 0xf5, 0x5b, 0xe9, 0xd4, 0xe7, 0x80, 0x2a, 0x78, 0xae, 0x3b, 0x38, - 0x3c, 0xfb, 0xe3, 0xbe, 0x1e, 0x34, 0x30, 0x6b, 0xf3, 0x6b, 0xae, 0x17, - 0x08, 0xfc, 0x2b, 0xb9, 0x15, 0xd1, 0x89, 0x9e, 0x76, 0xaa, 0xfb, 0xd2, - 0xfe, 0x93, 0x9f, 0xbf, 0x14, 0x0d, 0x75, 0xbf, 0xcf, 0x75, 0x25, 0x1a, - 0xa7, 0xe6, 0xdb, 0x9a, 0xe0, 0xe4, 0x01, 0x00, 0x59, 0xec, 0x4c, 0x2e, - 0xdf, 0x1e, 0xd5, 0x10, 0x52, 0xab, 0x4e, 0xcb, 0x83, 0x4f, 0xca, 0x94, - 0x1f, 0xec, 0xa4, 0x4e, 0x83, 0x3a, 0x4c, 0x02, 0x7a, 0x0e, 0x58, 0x30, - 0x29, 0x84, 0x35, 0x5e, 0xb2, 0x57, 0x6f, 0x5a, 0x93, 0x96, 0x5b, 0xcd, - 0x3d, 0x89, 0xdb, 0x82, 0x04, 0x01, 0x7b, 0xcd, 0x4b, 0x48, 0x0d, 0x35, - 0xbf, 0x15, 0xec, 0x23, 0x8f, 0x1a, 0xe3, 0xe8, 0x51, 0x14, 0x67, 0x5f, - 0x06, 0x6b, 0x73, 0x89, 0xa7, 0xc8, 0x8e, 0x15, 0xae, 0xff, 0xbf, 0x8a, - 0x8c, 0x6f, 0x51, 0x7e, 0x76, 0xe9, 0xbe, 0xff, 0xf1, 0x50, 0x80, 0x30, - 0x5f, 0xfc, 0x21, 0x1a, 0xca, 0x21, 0x87, 0xf6, 0xe9, 0xd0, 0x7b, 0x82, - 0xa1, 0xa0, 0x4a, 0x33, 0x5a, 0x24, 0x44, 0x83, 0x61, 0x2b, 0x3c, 0xce, - 0xb9, 0x5d, 0x9b, 0x1a, 0x00, 0x6f, 0x54, 0xe7, 0x5c, 0xb8, 0xa7, 0x3a, - 0xe5, 0x98, 0x12, 0xb8, 0x20, 0xd4, 0x2b, 0x65, 0xdd, 0xc6, 0x62, 0x81, - 0xe6, 0xe4, 0xbc, 0x0f, 0xe4, 0x08, 0x90, 0xf6, 0xc7, 0x21, 0x83, 0x98, - 0x86, 0xfb, 0xba, 0x5e, 0xcb, 0x88, 0xc3, 0x10, 0xa5, 0xbc, 0x50, 0x66, - 0x90, 0x09, 0xc0, 0x00, 0x6b, 0xa5, 0x61, 0x56, 0xeb, 0x38, 0xf7, 0x94, - 0x96, 0x33, 0x34, 0x48, 0x88, 0xe7, 0x62, 0xcb, 0xe0, 0x36, 0x72, 0xe3, - 0x95, 0xcf, 0x5a, 0x6a, 0xf1, 0x23, 0x66, 0xc0, 0x15, 0xde, 0x24, 0x4a, - 0x08, 0xc1, 0x0a, 0x21, 0x1c, 0x4a, 0xb9, 0xb1, 0xc8, 0x8b, 0x87, 0xd2, - 0x27, 0x60, 0xe7, 0x6a, 0x26, 0xb4, 0xad, 0x03, 0x8f, 0x5e, 0x66, 0xc3, - 0x84, 0xa1, 0x25, 0x62, 0xcf, 0xa8, 0x72, 0xb4, 0x52, 0x25, 0x41, 0x84, - 0x62, 0x8b, 0xdf, 0xf5, 0x2a, 0x7a, 0x98, 0x3f, 0x62, 0xd3, 0x2e, 0x69, - 0x1a, 0xcb, 0x50, 0xe0, 0x07, 0xfa, 0xb7, 0x1c, 0x84, 0xc5, 0x6a, 0xd9, - 0x48, 0x28, 0xab, 0x28, 0xc6, 0x37, 0xac, 0x85, 0x6b, 0x89, 0x10, 0x1b, - 0xa8, 0x66, 0xaf, 0xf2, 0x18, 0x79, 0xb5, 0x06, 0xc3, 0x12, 0x31, 0x1a, - 0xf8, 0x49, 0x48, 0x00, 0x00, 0x28, 0xa1, 0x45, 0x29, 0xb0, 0xf9, 0x5f, - 0xac, 0x38, 0x93, 0xe2, 0x82, 0x73, 0x02, 0x73, 0x99, 0x40, 0x63, 0x3c, - 0x67, 0x8c, 0x73, 0xcb, 0x0b, 0x92, 0x85, 0x36, 0x79, 0x75, 0xe1, 0x71, - 0xa0, 0x52, 0x3a, 0x72, 0xda, 0x08, 0x83, 0xb6, 0xdf, 0x59, 0x27, 0x6b, - 0xdd, 0x61, 0x12, 0xd1, 0x00, 0x02, 0x52, 0x40, 0xb9, 0x7b, 0x6a, 0xdb, - 0x5e, 0x8e, 0xa7, 0x7b, 0xb8, 0x4a, 0x2a, 0xf0, 0xc6, 0xf9, 0xf2, 0xcb, - 0x5e, 0x65, 0xcd, 0x24, 0x3a, 0x0d, 0x69, 0x7d, 0x17, 0xca, 0xfe, 0x83, - 0xa8, 0xc4, 0xe2, 0x86, 0xf2, 0x3b, 0x83, 0x77, 0xbf, 0xef, 0xf0, 0x15, - 0x84, 0x02, 0x0c, 0x14, 0x44, 0x2e, 0x9a, 0x42, 0x80, 0xaa, 0xe8, 0x18, - 0xc6, 0xf9, 0x6e, 0x92, 0xeb, 0x85, 0x99, 0x99, 0xa0, 0x80, 0xc2, 0xb3, - 0x03, 0x63, 0x16, 0xb5, 0xad, 0x26, 0xfc, 0x33, 0xf9, 0x5e, 0xd6, 0x75, - 0x11, 0xa6, 0x1b, 0xc6, 0xfb, 0xa0, 0x72, 0xcf, 0xe4, 0xc7, 0x93, 0x2a, - 0xef, 0xd7, 0x96, 0x09, 0xef, 0xa2, 0x85, 0xc6, 0xbc, 0xe4, 0xb9, 0x8a, - 0x1a, 0x7b, 0xb2, 0xce, 0xca, 0xb5, 0x73, 0xeb, 0x95, 0x3c, 0xdd, 0xe6, - 0xe2, 0x0c, 0x3d, 0x97, 0xd1, 0x75, 0x5d, 0xbe, 0x4a, 0x71, 0x1a, 0x5c, - 0x78, 0xd8, 0x62, 0x9c, 0xb2, 0xde, 0xf8, 0x0f, 0x7f, 0xff, 0xf1, 0x50, - 0x80, 0x2f, 0x9f, 0xfc, 0x21, 0x1a, 0xcc, 0xf4, 0x8f, 0xf7, 0x20, 0x16, - 0x7e, 0x83, 0xaa, 0x45, 0xa4, 0x61, 0x1a, 0x09, 0x86, 0x3e, 0xf0, 0xe7, - 0xae, 0xe7, 0x1c, 0xf1, 0xcf, 0x00, 0x03, 0x9a, 0xe3, 0xb0, 0xa3, 0x5c, - 0xeb, 0x3b, 0x0b, 0x42, 0xd0, 0xd0, 0x57, 0xdf, 0xd3, 0x10, 0xfd, 0x6a, - 0xc1, 0x23, 0xa4, 0x89, 0x86, 0x31, 0x97, 0x57, 0x4f, 0xe3, 0xad, 0xf0, - 0x42, 0xb4, 0xcc, 0xf2, 0x71, 0xeb, 0x71, 0xe3, 0x63, 0x44, 0x1f, 0xe9, - 0x60, 0xec, 0x00, 0x00, 0x09, 0x04, 0x71, 0x25, 0xfb, 0x29, 0xaf, 0xbb, - 0x97, 0x25, 0xb5, 0xdf, 0xbf, 0x4b, 0x38, 0xbb, 0xd1, 0xc9, 0x6c, 0xe9, - 0x38, 0x9f, 0xdc, 0xd9, 0x1f, 0x53, 0x3a, 0x94, 0x68, 0xe0, 0x48, 0xfe, - 0xf0, 0x37, 0x13, 0xd1, 0x46, 0x60, 0x73, 0x3f, 0x8b, 0x2c, 0x2b, 0xb0, - 0x4f, 0xb5, 0xc2, 0xea, 0x5e, 0xb2, 0xd7, 0x30, 0x88, 0x13, 0xb6, 0x91, - 0xa9, 0x2a, 0x0f, 0x3f, 0x69, 0xd4, 0x39, 0x67, 0x74, 0x97, 0x1d, 0x90, - 0x38, 0x58, 0x70, 0x81, 0x10, 0x06, 0x70, 0xa9, 0xd4, 0x33, 0x33, 0xc1, - 0x27, 0x53, 0x59, 0x8e, 0x2c, 0x87, 0xbe, 0xca, 0x61, 0x8d, 0x62, 0x79, - 0x8a, 0xea, 0x05, 0x1e, 0x0c, 0x9f, 0xa2, 0x06, 0xb4, 0x1b, 0x18, 0x86, - 0xc4, 0x5f, 0xc2, 0x08, 0x00, 0x00, 0x28, 0xa0, 0xa5, 0x2b, 0x00, 0xfb, - 0x3e, 0x55, 0xa6, 0xba, 0xac, 0xa7, 0xe0, 0x6d, 0x83, 0x29, 0x78, 0x0c, - 0x20, 0x96, 0x17, 0x9c, 0xd1, 0x0e, 0x38, 0xe6, 0x47, 0xdf, 0xec, 0x8d, - 0x28, 0xa1, 0xac, 0xc2, 0xa6, 0xa4, 0x40, 0x11, 0x5f, 0x87, 0x9c, 0x8e, - 0x8b, 0xd2, 0xb8, 0x80, 0x88, 0x4c, 0x44, 0x0b, 0xec, 0xda, 0x37, 0x0a, - 0x96, 0x82, 0x68, 0xbf, 0xbc, 0xce, 0x27, 0x46, 0xf7, 0x18, 0x60, 0xd4, - 0x14, 0x9a, 0xf4, 0x10, 0xed, 0x0b, 0xa9, 0x74, 0xa1, 0xa9, 0x90, 0xad, - 0xdb, 0x84, 0x68, 0x9d, 0x04, 0x8d, 0x18, 0x41, 0x2f, 0x59, 0xab, 0x68, - 0x1e, 0xe7, 0x31, 0x33, 0x96, 0xda, 0x5d, 0x2f, 0xb4, 0x67, 0x83, 0x1b, - 0x8e, 0x2f, 0x4b, 0xad, 0x9c, 0xd8, 0x58, 0x2a, 0x81, 0xba, 0xe3, 0x16, - 0xa2, 0x2b, 0x47, 0x38, 0xb1, 0xc0, 0x38, 0x18, 0xca, 0x0c, 0x46, 0xa6, - 0x56, 0xcc, 0x26, 0xa0, 0x1a, 0x2f, 0x8b, 0x90, 0x9b, 0xfc, 0x4e, 0x06, - 0x4a, 0x31, 0x39, 0x88, 0xe9, 0x97, 0xa1, 0x1c, 0x2b, 0x43, 0xbd, 0xd2, - 0xe3, 0xa8, 0x62, 0xdb, 0x0a, 0xd5, 0x51, 0xb9, 0x81, 0x9a, 0xf3, 0xcb, - 0x9d, 0xf4, 0xdf, 0x2f, 0x5d, 0x77, 0x1c, 0x8f, 0x49, 0x8c, 0x8b, 0x6a, - 0x2b, 0x61, 0x28, 0x4f, 0x37, 0x65, 0x9e, 0x69, 0x92, 0x8e, 0x2d, 0xaf, - 0x0a, 0xe3, 0xfc, 0xab, 0x80, 0xff, 0xf1, 0x50, 0x80, 0x25, 0x7f, 0xfc, - 0x21, 0x1a, 0xcf, 0x16, 0xe5, 0xe7, 0x5d, 0x1f, 0x7f, 0x82, 0x22, 0x46, - 0xfa, 0x24, 0x2b, 0x8e, 0x3b, 0xb9, 0xd7, 0x37, 0xc7, 0xae, 0x17, 0xdb, - 0xc8, 0x03, 0x3b, 0xd6, 0xec, 0xe7, 0x5b, 0x78, 0xd5, 0xa9, 0xb0, 0xed, - 0xf9, 0xee, 0x1e, 0x88, 0x14, 0xcd, 0x2b, 0x2a, 0x48, 0x5f, 0x2b, 0xe1, - 0xd5, 0x35, 0x0f, 0x49, 0xa4, 0xcf, 0xfd, 0xe1, 0x68, 0x85, 0xf5, 0x78, - 0x80, 0x00, 0x00, 0x6e, 0x15, 0x12, 0x79, 0x36, 0xc8, 0xaa, 0x45, 0x65, - 0x05, 0x61, 0x58, 0x5e, 0x74, 0xef, 0x78, 0xd2, 0x4c, 0x96, 0xc3, 0x7d, - 0xd7, 0xe7, 0x5f, 0x67, 0x1a, 0x66, 0xd5, 0xbf, 0xf5, 0x8c, 0xf0, 0x4b, - 0xe3, 0xe8, 0xd9, 0xc4, 0x0a, 0x51, 0xc1, 0xfb, 0xda, 0x75, 0xa6, 0x20, - 0x5a, 0xe9, 0x59, 0x3e, 0xef, 0x7e, 0x79, 0x16, 0xbf, 0x31, 0x54, 0x0f, - 0xc1, 0x3d, 0x02, 0x32, 0xc3, 0x01, 0x62, 0xa3, 0xe4, 0xe0, 0x39, 0x3f, - 0x41, 0xd0, 0x5a, 0xf8, 0x28, 0x8b, 0x64, 0xea, 0x59, 0x70, 0x53, 0xfc, - 0x15, 0x50, 0x30, 0x22, 0x20, 0x44, 0xa8, 0x47, 0x1c, 0x1a, 0x33, 0x8e, - 0x62, 0xf1, 0xa0, 0x06, 0x55, 0x28, 0x50, 0xa5, 0x36, 0x0d, 0xb4, 0x88, - 0xd6, 0xa6, 0xfa, 0x76, 0xd8, 0x84, 0x6a, 0xa7, 0x79, 0x92, 0x13, 0x53, - 0xe8, 0x6f, 0xa9, 0x55, 0x8e, 0x1c, 0x25, 0x2f, 0xe1, 0xf1, 0xc9, 0x9a, - 0x3c, 0xec, 0x5b, 0x2c, 0xe6, 0x08, 0x96, 0x9c, 0x8b, 0x00, 0x1a, 0xd4, - 0x10, 0x10, 0x76, 0xaa, 0xd5, 0x25, 0xbe, 0x68, 0xcf, 0xe0, 0xc9, 0xff, - 0x6c, 0xcc, 0xaf, 0x59, 0xfa, 0xd0, 0xcb, 0x87, 0xf8, 0x2e, 0x5d, 0x72, - 0x55, 0x1a, 0xca, 0x2c, 0x31, 0xe9, 0x4f, 0x6c, 0xf7, 0xed, 0x7d, 0x1a, - 0x2c, 0x1c, 0x27, 0xb7, 0xef, 0x1f, 0x79, 0x6e, 0x87, 0x8c, 0xbc, 0x7d, - 0x75, 0x94, 0x94, 0x07, 0x45, 0xfc, 0x8b, 0x64, 0xa0, 0x9f, 0x36, 0xba, - 0x77, 0x95, 0x62, 0x25, 0x56, 0xde, 0x99, 0x06, 0xbf, 0x2d, 0x59, 0x51, - 0x44, 0xb8, 0xfb, 0x66, 0x9e, 0xf0, 0xab, 0xe1, 0x87, 0xfe, 0x99, 0xdc, - 0xac, 0x70, 0x62, 0x1e, 0xff, 0xf1, 0x50, 0x80, 0x1c, 0xbf, 0xfc, 0x21, - 0x1a, 0xcd, 0xe9, 0xbf, 0xe1, 0xff, 0xff, 0x7f, 0x62, 0x9a, 0x87, 0x48, - 0x23, 0xca, 0x2f, 0x2f, 0x8e, 0xcd, 0x62, 0x45, 0x83, 0x78, 0xa5, 0x0a, - 0x14, 0xaa, 0xc0, 0x57, 0xf4, 0xd1, 0x18, 0xa3, 0xd5, 0x4d, 0x6c, 0xa6, - 0xb3, 0x6f, 0x4b, 0xf6, 0x70, 0x78, 0xc9, 0xbe, 0x8d, 0x80, 0x24, 0x5d, - 0x15, 0x80, 0x00, 0x08, 0x28, 0x09, 0x00, 0xec, 0x21, 0x70, 0x05, 0xa5, - 0x34, 0xe3, 0x2b, 0xb8, 0xd2, 0xa8, 0x19, 0x21, 0xc3, 0xab, 0x31, 0x2a, - 0x25, 0x2e, 0x1a, 0x7e, 0x29, 0x20, 0x43, 0x0d, 0x26, 0x02, 0x60, 0x00, - 0x02, 0x4a, 0x55, 0x69, 0x09, 0xa5, 0xfb, 0xf3, 0x95, 0x2f, 0x37, 0xeb, - 0x4d, 0x75, 0xf0, 0xd5, 0x7b, 0x6a, 0x03, 0xdb, 0xa4, 0x2c, 0x39, 0x16, - 0x20, 0xf5, 0x2e, 0x62, 0xcb, 0xfc, 0x0d, 0x1a, 0x28, 0xd1, 0x0b, 0x88, - 0x6e, 0xa2, 0x38, 0xe7, 0xcb, 0xb3, 0x58, 0x91, 0x60, 0xe5, 0xf5, 0xda, - 0x94, 0x28, 0x52, 0xab, 0x01, 0xf1, 0xe0, 0x8c, 0xcb, 0x1a, 0xd1, 0x6a, - 0x2d, 0x2e, 0x4c, 0x3d, 0xb8, 0xcb, 0x4c, 0xf2, 0xda, 0x7d, 0x79, 0xae, - 0x51, 0xa4, 0x14, 0xac, 0x2c, 0x0a, 0x85, 0xc0, 0x00, 0x90, 0x1a, 0x8d, - 0x19, 0x0d, 0x49, 0x16, 0xac, 0x6f, 0xd8, 0xc5, 0xc9, 0x77, 0xfe, 0xca, - 0x5a, 0x92, 0xad, 0xb6, 0xa5, 0x8e, 0xfa, 0x71, 0xd9, 0xaa, 0x1c, 0x68, - 0xa5, 0xd6, 0x00, 0x00, 0x00, 0x02, 0x8c, 0x85, 0x39, 0xa3, 0x28, 0x75, - 0x2e, 0xbe, 0x01, 0x25, 0xbe, 0x78, 0xac, 0x91, 0x82, 0xb7, 0x73, 0x01, - 0x9e, 0x0b, 0x9d, 0xd3, 0x17, 0xff, 0xf1, 0x50, 0x80, 0x22, 0x1f, 0xfc, - 0x21, 0x1a, 0xcf, 0xb5, 0xff, 0xa7, 0xdd, 0xbf, 0x7f, 0x62, 0x9e, 0x90, - 0x62, 0x54, 0x22, 0x6b, 0x88, 0xd6, 0xdc, 0x6c, 0xba, 0x58, 0x05, 0x62, - 0x94, 0x28, 0x52, 0xab, 0x60, 0x3c, 0xfd, 0x61, 0x9b, 0x9c, 0xf7, 0x04, - 0x61, 0xc9, 0xa6, 0x15, 0xda, 0x39, 0xfe, 0xe2, 0xd7, 0x3a, 0x36, 0x96, - 0x9c, 0x11, 0xb8, 0xd7, 0xb8, 0x00, 0x00, 0x1a, 0xa0, 0x02, 0x80, 0x89, - 0x60, 0xad, 0x7c, 0x73, 0x6b, 0x27, 0x54, 0xc7, 0x24, 0x0e, 0x2a, 0x27, - 0x7b, 0x25, 0xa5, 0x48, 0xcb, 0x8e, 0x5f, 0x66, 0x5f, 0x80, 0x2d, 0x86, - 0x51, 0xf5, 0x37, 0x70, 0x0c, 0xba, 0x2e, 0xcc, 0x71, 0x53, 0x91, 0x3c, - 0x1e, 0x62, 0x0b, 0x47, 0x5a, 0x62, 0xe0, 0x2c, 0x28, 0xa4, 0x54, 0xcc, - 0x6a, 0x85, 0x09, 0xb0, 0xa9, 0x10, 0xf3, 0x79, 0xb8, 0xcd, 0xc2, 0x40, - 0x04, 0x32, 0x32, 0x23, 0x9d, 0x1d, 0x0d, 0x1c, 0xb8, 0xa5, 0x0b, 0xad, - 0x75, 0x2e, 0xe2, 0xfe, 0xc4, 0x3d, 0x11, 0xb4, 0x22, 0x24, 0x22, 0x6b, - 0x8c, 0xd6, 0xa7, 0x35, 0x65, 0xd2, 0xc0, 0x2b, 0x15, 0xbb, 0x37, 0x67, - 0x92, 0x95, 0x5b, 0x0f, 0xd1, 0x28, 0xed, 0x2a, 0x31, 0xc5, 0xca, 0xc9, - 0xa9, 0x78, 0x4f, 0x5a, 0xff, 0x11, 0x7c, 0x29, 0xd2, 0x5b, 0xdc, 0xda, - 0x53, 0x40, 0x48, 0x00, 0x14, 0x00, 0x00, 0x05, 0x94, 0x71, 0xe4, 0x33, - 0xab, 0x74, 0xa3, 0x49, 0x97, 0x9d, 0xa7, 0x1b, 0x04, 0x09, 0x42, 0x47, - 0x8c, 0x0f, 0x2d, 0xe5, 0x98, 0xf3, 0xe9, 0x14, 0x9a, 0xf7, 0x98, 0x61, - 0x02, 0xc0, 0xf2, 0x15, 0xfa, 0x3c, 0x76, 0xa1, 0xb0, 0x44, 0x4e, 0xa5, - 0xa5, 0x85, 0x0a, 0x82, 0xb8, 0xc4, 0x70, 0xfe, 0xad, 0x38, 0x64, 0x03, - 0x80, 0xa8, 0x7d, 0xb0, 0x23, 0xb2, 0xbb, 0x23, 0x14, 0x8c, 0x99, 0x4f, - 0x28, 0x4b, 0x2b, 0x1c, 0x13, 0x41, 0xd0, 0x24, 0x0b, 0xb7, 0x2b, 0x65, - 0xe0, 0xff, 0xf1, 0x50, 0x80, 0x20, 0x3f, 0xfc, 0x21, 0x1a, 0xca, 0xd2, - 0xb3, 0xc4, 0xbd, 0x97, 0x7f, 0x62, 0x8d, 0x34, 0x42, 0x9d, 0x10, 0x8d, - 0x04, 0x95, 0xf0, 0xdf, 0xc7, 0xbb, 0xe3, 0xd6, 0xb7, 0xed, 0xbd, 0xdf, - 0x15, 0x00, 0xed, 0xae, 0x79, 0xd2, 0x9c, 0xf0, 0xcd, 0xe7, 0x3a, 0x60, - 0x0f, 0x55, 0x09, 0x1c, 0x5f, 0x54, 0xe3, 0xa1, 0x75, 0xc6, 0x4b, 0xaf, - 0x79, 0x70, 0x00, 0x6e, 0x00, 0x00, 0x00, 0x12, 0xee, 0x76, 0x0f, 0xd7, - 0x10, 0xef, 0x46, 0x08, 0xd5, 0x26, 0xf8, 0xda, 0x28, 0xe6, 0xdf, 0xc2, - 0xef, 0xd9, 0xb6, 0x55, 0xec, 0xf6, 0xe2, 0x41, 0x2f, 0x74, 0x4e, 0x2c, - 0x7e, 0x2e, 0xe4, 0xe0, 0x19, 0x40, 0x2a, 0x50, 0x73, 0xc5, 0x1c, 0x89, - 0x52, 0x31, 0x8e, 0x4a, 0xce, 0x11, 0x73, 0x0a, 0xc7, 0x0c, 0xc4, 0x43, - 0xda, 0xf3, 0xa9, 0x87, 0x7f, 0x81, 0xaa, 0x85, 0xe4, 0x61, 0x21, 0x0d, - 0x04, 0x9d, 0x59, 0xd3, 0x9e, 0x39, 0x38, 0xdb, 0x4a, 0x80, 0xca, 0xa5, - 0x28, 0x29, 0x4a, 0x60, 0x5e, 0xc1, 0x85, 0xd1, 0x9d, 0xb8, 0xaa, 0xe1, - 0x34, 0xbc, 0x54, 0xa4, 0x45, 0xab, 0xa2, 0x1f, 0xb8, 0x32, 0xee, 0xc5, - 0x14, 0xe9, 0x19, 0x27, 0x1a, 0x48, 0x22, 0x00, 0x00, 0xdc, 0x80, 0xb2, - 0xe0, 0x45, 0x47, 0x1e, 0x81, 0xee, 0xad, 0x0c, 0x52, 0xcc, 0xf8, 0x48, - 0x45, 0x81, 0xc9, 0x20, 0x61, 0xde, 0xc4, 0x35, 0xbd, 0xc9, 0xd9, 0x7b, - 0x1a, 0x03, 0x9e, 0x87, 0x8e, 0xe0, 0xaf, 0x04, 0xf7, 0x6f, 0xd8, 0x5d, - 0xdd, 0x35, 0xf1, 0x52, 0x00, 0x9c, 0x39, 0x15, 0x04, 0x68, 0xa0, 0x26, - 0x24, 0x95, 0x1b, 0xe8, 0x2c, 0xce, 0x14, 0x52, 0xf0, 0x36, 0x8c, 0x5e, - 0x2e, 0x92, 0x3e, 0x68, 0xa7, 0xed, 0xc3, 0x3c, 0x61, 0x39, 0x7a, 0xad, - 0x40, 0x07, 0xd4, 0x92, 0x78, 0xf8, 0xff, 0xf1, 0x50, 0x80, 0x1c, 0x5f, - 0xfc, 0x21, 0x1a, 0xcd, 0xe7, 0x39, 0xe3, 0xff, 0xff, 0x7f, 0x62, 0x8c, - 0x40, 0x5a, 0x11, 0x82, 0x08, 0xe3, 0x45, 0x3d, 0x9d, 0xd3, 0xcf, 0x2e, - 0x00, 0xe8, 0xdd, 0x52, 0x94, 0x14, 0xa5, 0x56, 0x00, 0xdd, 0xb1, 0xe8, - 0x34, 0xeb, 0xa4, 0xe3, 0x27, 0x5b, 0x8b, 0x56, 0x78, 0x09, 0x0f, 0x91, - 0x10, 0x00, 0x26, 0x90, 0x00, 0xdc, 0x10, 0xa6, 0x3b, 0x26, 0xbc, 0x0c, - 0x99, 0xaa, 0x9f, 0x3e, 0x15, 0x30, 0xf5, 0x8b, 0x57, 0x89, 0x5b, 0xd6, - 0xa9, 0x52, 0x7d, 0xa1, 0x5d, 0x45, 0xf5, 0x9d, 0x52, 0xbc, 0xa6, 0x1b, - 0x90, 0x00, 0x00, 0x02, 0x97, 0x5d, 0x8b, 0x1c, 0xed, 0x7b, 0x68, 0xd9, - 0x29, 0xf5, 0xaf, 0x2b, 0xd2, 0x8e, 0x6a, 0x5b, 0x31, 0x1a, 0x37, 0x88, - 0x53, 0x73, 0xab, 0xc6, 0xb1, 0x17, 0x7f, 0x56, 0x8a, 0x40, 0x5a, 0x11, - 0x82, 0x09, 0x35, 0xa3, 0xc8, 0xec, 0xeb, 0x95, 0xd6, 0x83, 0x9f, 0x75, - 0x4a, 0x50, 0x52, 0x95, 0x58, 0x1f, 0xab, 0xe9, 0xcf, 0x7f, 0x1e, 0xfd, - 0x6d, 0x46, 0x61, 0x70, 0x0b, 0x5d, 0xf1, 0xdc, 0xfb, 0x51, 0x5b, 0x3f, - 0xe9, 0x8f, 0x41, 0x90, 0x65, 0x22, 0x00, 0x00, 0x58, 0x49, 0x49, 0x06, - 0x88, 0xd4, 0x05, 0x14, 0xc3, 0x58, 0x5b, 0x7c, 0x6f, 0x71, 0xc6, 0xeb, - 0x45, 0xca, 0xfa, 0x1e, 0x9a, 0x53, 0x5b, 0x8d, 0x87, 0x7d, 0xf1, 0xdf, - 0x70, 0x11, 0x00, 0x00, 0x04, 0x43, 0x4e, 0x74, 0x0f, 0xac, 0x07, 0x9a, - 0xd6, 0xa4, 0x83, 0x31, 0x75, 0xd7, 0x52, 0x5b, 0x61, 0xb2, 0x9d, 0x59, - 0x1a, 0x3b, 0xee, 0xf0, 0xff, 0xf1, 0x50, 0x80, 0x19, 0x5f, 0xfc, 0x21, - 0x1a, 0xce, 0x91, 0x9d, 0xe2, 0x7f, 0x9f, 0x7f, 0x62, 0x8c, 0x40, 0x5a, - 0x11, 0x0e, 0x88, 0x51, 0xa1, 0x11, 0x90, 0xda, 0xad, 0x1d, 0x72, 0xeb, - 0x75, 0x5e, 0x7b, 0x9d, 0x01, 0xcd, 0x5e, 0xf9, 0xe1, 0x45, 0x0a, 0x53, - 0x00, 0x39, 0xf2, 0x2b, 0x6a, 0x32, 0x90, 0xa3, 0x2c, 0x9b, 0xee, 0x2d, - 0x75, 0x2e, 0x86, 0x48, 0x85, 0x80, 0x00, 0x00, 0x91, 0x19, 0x26, 0x6f, - 0x82, 0xd2, 0x1d, 0xd0, 0x8b, 0x25, 0x60, 0x18, 0x93, 0xcb, 0x71, 0x38, - 0x41, 0x75, 0xe6, 0x38, 0x85, 0x40, 0x01, 0x60, 0x14, 0x4a, 0x05, 0x51, - 0x63, 0x61, 0xda, 0xfa, 0x64, 0x1a, 0x01, 0x3c, 0xe7, 0x15, 0xb1, 0xee, - 0x26, 0xfa, 0x1d, 0x45, 0x3b, 0xfb, 0x10, 0xe4, 0x02, 0xd0, 0x88, 0x74, - 0x22, 0x9d, 0x10, 0x88, 0x86, 0xd5, 0x9f, 0x1b, 0xd1, 0xdd, 0x3c, 0xf8, - 0x9d, 0x73, 0xc0, 0x73, 0xd7, 0x8e, 0xe9, 0x45, 0x1d, 0xaa, 0x98, 0x01, - 0xc5, 0xd2, 0x54, 0x62, 0x84, 0xef, 0xab, 0x2e, 0x32, 0xf9, 0x9e, 0xa8, - 0x13, 0x20, 0x98, 0xd4, 0x00, 0x00, 0x16, 0x5a, 0xa4, 0xa8, 0x40, 0x15, - 0x54, 0x32, 0x32, 0xb3, 0xd2, 0x41, 0x3a, 0x7e, 0xaa, 0x72, 0x56, 0x91, - 0x44, 0x01, 0x21, 0x20, 0x05, 0x02, 0xb4, 0x3a, 0xdc, 0x00, 0xdb, 0x0e, - 0x6b, 0x3c, 0x80, 0x7b, 0x59, 0xa8, 0x2a, 0xfe, 0xdc, 0x86, 0x98, 0x8f, - 0x6f, 0x77, 0xff, 0xf1, 0x50, 0x80, 0x11, 0x9f, 0xfc, 0x21, 0x1a, 0xce, - 0xd2, 0x19, 0x00, 0x3c, 0x1f, 0x7f, 0x61, 0x8c, 0x00, 0xba, 0x51, 0x26, - 0x84, 0x43, 0xa3, 0x12, 0x96, 0x4f, 0x3e, 0x2f, 0xdb, 0xdc, 0xf8, 0xf7, - 0x9f, 0x15, 0x8e, 0x5e, 0xfb, 0xad, 0xfa, 0xa3, 0x75, 0xab, 0x50, 0x03, - 0xe5, 0x62, 0x43, 0x80, 0x01, 0x50, 0x48, 0x05, 0x80, 0x00, 0x00, 0x88, - 0x00, 0x4c, 0x00, 0x00, 0x4a, 0x37, 0xa1, 0xd5, 0x4c, 0x6a, 0x5f, 0x26, - 0xff, 0x7c, 0x78, 0x47, 0xf0, 0xe1, 0xcb, 0x63, 0xf9, 0xcc, 0x0b, 0xa5, - 0x12, 0xe9, 0x04, 0xa4, 0x8b, 0xeb, 0x52, 0xbe, 0x4e, 0x37, 0x38, 0x47, - 0x37, 0x8c, 0xdd, 0x7a, 0xa2, 0xb1, 0x72, 0x80, 0x7a, 0xdf, 0xa5, 0x70, - 0x0d, 0x8b, 0x40, 0x18, 0x60, 0x5a, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x2c, 0x01, 0x55, 0x52, 0x89, 0x24, 0xd3, 0x93, 0x1c, 0xb8, 0xd6, 0x76, - 0xaf, 0x2f, 0xf2, 0x5b, 0x9f, 0x87, 0x47, 0x7e, 0xbe, 0x4e, 0xff, 0xf1, - 0x50, 0x80, 0x16, 0x3f, 0xfc, 0x21, 0x1a, 0xcd, 0xde, 0xeb, 0xc0, 0xff, - 0x9b, 0x7e, 0x61, 0x0d, 0xf4, 0x42, 0x9d, 0x10, 0x88, 0x86, 0xb9, 0x13, - 0xda, 0xa3, 0x7b, 0x79, 0xf1, 0x3a, 0x54, 0x03, 0xb3, 0x9f, 0x14, 0xa5, - 0x0c, 0xca, 0xa6, 0x01, 0x73, 0xea, 0x8d, 0x3a, 0xfe, 0x38, 0x13, 0x11, - 0x6c, 0x47, 0x90, 0x00, 0x4d, 0x60, 0x00, 0x00, 0x48, 0x02, 0xe1, 0x04, - 0xeb, 0x0d, 0x20, 0x48, 0xac, 0x45, 0xd5, 0xad, 0x40, 0x0c, 0x64, 0x91, - 0x00, 0x20, 0x00, 0x00, 0xa4, 0xe3, 0x50, 0x90, 0xbe, 0xbe, 0x7f, 0xb1, - 0x92, 0x33, 0x70, 0x71, 0x2a, 0xe6, 0xba, 0x6d, 0x43, 0xf8, 0x89, 0xe8, - 0x7e, 0x3a, 0x80, 0xf4, 0x62, 0x9d, 0x10, 0x95, 0x2e, 0x3a, 0xd5, 0x47, - 0xa8, 0xf3, 0xea, 0xfa, 0x54, 0x39, 0x7d, 0xf3, 0xc5, 0x29, 0x43, 0x9a, - 0xaa, 0xc0, 0x66, 0xf5, 0xb3, 0x5e, 0x71, 0x9d, 0x6d, 0x55, 0xc4, 0x05, - 0x74, 0x02, 0xc0, 0x54, 0x00, 0x00, 0x5c, 0x45, 0x30, 0x90, 0x80, 0xa2, - 0x03, 0x00, 0x98, 0xb4, 0x69, 0x70, 0x80, 0x00, 0x00, 0x77, 0xd3, 0x38, - 0x15, 0x9f, 0x66, 0x62, 0xdf, 0x69, 0xc5, 0x0b, 0xd7, 0x9f, 0x83, 0x17, - 0x3f, 0x62, 0x5a, 0x3b, 0x66, 0xf0, 0x70, 0xff, 0xf1, 0x50, 0x80, 0x15, - 0x3f, 0xfc, 0x21, 0x1a, 0xce, 0x07, 0x6e, 0x80, 0xff, 0x9f, 0x7f, 0x62, - 0x0c, 0x80, 0x5a, 0x31, 0x4e, 0x88, 0x4a, 0xd5, 0x46, 0xbd, 0xbc, 0x79, - 0x7a, 0xbc, 0xeb, 0xc5, 0xf9, 0x03, 0x9b, 0xc6, 0xfd, 0x51, 0x42, 0xb7, - 0x54, 0xaa, 0x00, 0xaf, 0x08, 0x29, 0x51, 0xf3, 0x09, 0x00, 0x02, 0x01, - 0x64, 0x00, 0x88, 0x00, 0x2f, 0x60, 0xb1, 0x61, 0x92, 0xec, 0x01, 0x00, - 0x8c, 0x68, 0xc9, 0x30, 0x05, 0x93, 0x00, 0x00, 0x00, 0x5c, 0xca, 0x8d, - 0x2e, 0xb5, 0x58, 0xee, 0x85, 0xf8, 0x73, 0xfc, 0x38, 0xf2, 0x7d, 0x65, - 0xbf, 0x36, 0x84, 0xf0, 0x3f, 0x8d, 0x00, 0x9a, 0x31, 0x4e, 0x88, 0x46, - 0x42, 0x6a, 0x27, 0x51, 0x6f, 0x47, 0x5c, 0xbc, 0xb9, 0xe1, 0xcf, 0xce, - 0x7a, 0xa2, 0x85, 0x73, 0x54, 0xaa, 0x0d, 0xbc, 0xb8, 0xf1, 0xa4, 0x65, - 0x0b, 0x5c, 0x5c, 0x12, 0x05, 0x80, 0x00, 0x00, 0x0a, 0xad, 0x4b, 0x22, - 0x9e, 0x10, 0x4a, 0x71, 0x84, 0x17, 0x54, 0x04, 0xa4, 0xb2, 0x73, 0x90, - 0x00, 0x0b, 0x11, 0x2c, 0xb1, 0x4c, 0x80, 0x50, 0x72, 0x29, 0xdd, 0x5c, - 0xb6, 0x1f, 0x78, 0xe4, 0x6d, 0x8c, 0x6b, 0x80, 0xff, 0xf1, 0x50, 0x80, - 0x14, 0xdf, 0xfc, 0x21, 0x1a, 0xcd, 0xc3, 0x77, 0xc0, 0xff, 0x1f, 0x7e, - 0x53, 0x00, 0x86, 0x3a, 0x21, 0x46, 0x8c, 0x4a, 0x92, 0x27, 0xc3, 0xd4, - 0xaf, 0x3e, 0x1e, 0x50, 0x07, 0x66, 0xfd, 0x52, 0x94, 0x7a, 0xe2, 0xaa, - 0x94, 0x17, 0xf0, 0xa8, 0x72, 0x44, 0xa5, 0x30, 0x2e, 0xb8, 0x00, 0x50, - 0x05, 0xc0, 0x02, 0x60, 0x98, 0x01, 0x00, 0x2a, 0x22, 0x59, 0x30, 0x02, - 0xa9, 0xd0, 0x48, 0x05, 0x80, 0x02, 0x31, 0x2a, 0x9a, 0xc3, 0xa4, 0xbc, - 0x93, 0xfc, 0xea, 0xac, 0x75, 0x41, 0x4c, 0xcf, 0x87, 0x5f, 0xd3, 0x34, - 0xbe, 0xcf, 0xca, 0x21, 0x98, 0x0f, 0x42, 0x21, 0xd0, 0x8a, 0x74, 0x42, - 0x55, 0xa2, 0xfd, 0x95, 0x3d, 0x1e, 0x7c, 0x6b, 0x48, 0x3a, 0x33, 0xd7, - 0x74, 0xa5, 0x0e, 0x6a, 0xa9, 0x40, 0xd7, 0x9a, 0xdd, 0x4d, 0xf1, 0xf0, - 0x76, 0x16, 0x01, 0x10, 0x00, 0x01, 0x30, 0xb0, 0x17, 0x00, 0x02, 0x24, - 0xee, 0x0b, 0x5c, 0x24, 0x00, 0x05, 0xc3, 0x5a, 0x60, 0x0b, 0x01, 0x44, - 0xd2, 0x82, 0x6f, 0x06, 0x6f, 0x26, 0x69, 0x33, 0x6a, 0xe0, 0xb2, 0x58, - 0x6d, 0xc3, 0xbf, 0x68, 0x53, 0x80, 0xff, 0xf1, 0x50, 0x80, 0x12, 0x5f, - 0xfc, 0x21, 0x1a, 0xcf, 0xae, 0x15, 0xc0, 0x1e, 0x0f, 0x7f, 0x3b, 0x00, - 0xb4, 0x62, 0x8d, 0x20, 0x91, 0x24, 0x7d, 0x37, 0xc3, 0xc6, 0xbb, 0xf8, - 0xf7, 0xbe, 0xaa, 0xc3, 0x97, 0xc5, 0x7c, 0xa8, 0xa3, 0xe7, 0x89, 0xe3, - 0x00, 0xfc, 0x96, 0xac, 0xd1, 0xa8, 0x00, 0x01, 0x10, 0x90, 0x09, 0x89, - 0xa2, 0x24, 0x08, 0x88, 0x95, 0x24, 0x15, 0x3a, 0xa8, 0x00, 0x5c, 0x03, - 0xa5, 0x48, 0x93, 0x05, 0x49, 0xd1, 0x25, 0x21, 0x9a, 0xb5, 0x6e, 0x92, - 0x57, 0x7e, 0xdb, 0x90, 0xe5, 0x5c, 0xaf, 0xe3, 0x50, 0x1e, 0x98, 0x49, - 0xa4, 0x12, 0xa4, 0x95, 0xf7, 0xf5, 0xc7, 0x0f, 0xb5, 0xd6, 0xb2, 0xfa, - 0xcd, 0x0e, 0x6e, 0xf7, 0xba, 0xcf, 0xb2, 0x99, 0x38, 0xcc, 0x01, 0xb7, - 0xc9, 0x1b, 0x42, 0x0b, 0x80, 0x98, 0x08, 0x80, 0x00, 0x09, 0xdd, 0x42, - 0x00, 0xb0, 0x00, 0x02, 0xe0, 0x20, 0x05, 0x61, 0x09, 0x5f, 0x12, 0xef, - 0x8e, 0x4f, 0xc3, 0xd1, 0x6c, 0x71, 0x18, 0x78, 0xff, 0xf1, 0x50, 0x80, - 0x14, 0x9f, 0xfc, 0x21, 0x1a, 0xca, 0x82, 0x97, 0x00, 0xff, 0x1f, 0x7e, - 0x39, 0x81, 0x74, 0x62, 0x8d, 0x18, 0x95, 0x68, 0x9f, 0x1e, 0xbd, 0x9e, - 0xb5, 0x35, 0xbd, 0xf9, 0x1c, 0xfc, 0xf7, 0xea, 0x94, 0x2b, 0x72, 0xaa, - 0x94, 0x1e, 0xbf, 0x55, 0xa3, 0x38, 0xa5, 0x40, 0x2a, 0x19, 0xc4, 0xa0, - 0x34, 0xc0, 0x00, 0x00, 0x13, 0x55, 0x70, 0x20, 0x0f, 0x82, 0xe6, 0x70, - 0x4b, 0x30, 0x00, 0x85, 0x4a, 0x1b, 0x81, 0x6c, 0x24, 0x21, 0x5d, 0xfa, - 0xe9, 0x34, 0xa7, 0x0c, 0x37, 0xf7, 0xc1, 0xcf, 0x57, 0xda, 0xcf, 0xe6, - 0x60, 0x09, 0xcf, 0x42, 0x28, 0xd1, 0x89, 0x5a, 0x89, 0xc6, 0x93, 0xb9, - 0xbf, 0x6d, 0xef, 0xc8, 0x07, 0x2e, 0xed, 0xd2, 0x85, 0x77, 0x75, 0x54, - 0xa0, 0xfc, 0xf6, 0x87, 0x04, 0xab, 0x0b, 0x5e, 0x69, 0x0f, 0x74, 0x00, - 0x0d, 0x20, 0x00, 0x00, 0x24, 0x14, 0x00, 0x00, 0x17, 0x38, 0x02, 0xca, - 0x11, 0x42, 0xe5, 0x84, 0x2b, 0xd8, 0x9c, 0xf4, 0x29, 0x05, 0x40, 0x00, - 0x00, 0xd4, 0x63, 0x1e, 0x62, 0xac, 0x72, 0xfb, 0x4a, 0x18, 0x57, 0xae, - 0x0f, 0x5c, 0x76, 0x70, 0xff, 0xf1, 0x50, 0x80, 0x13, 0xdf, 0xfc, 0x21, - 0x1a, 0xca, 0x9a, 0x04, 0xc0, 0xb9, 0x1f, 0x7f, 0x3a, 0x80, 0xf4, 0x62, - 0x8d, 0x18, 0x95, 0x71, 0xed, 0xa7, 0x3c, 0x37, 0xdf, 0x5a, 0xf5, 0xed, - 0xbc, 0x71, 0x1c, 0xfd, 0xd7, 0xbd, 0x28, 0x57, 0xab, 0x95, 0x40, 0x79, - 0x99, 0x5a, 0xd2, 0x84, 0x93, 0x26, 0x44, 0xd5, 0x24, 0x00, 0x00, 0x00, - 0x40, 0x05, 0x40, 0x00, 0x04, 0xd7, 0xd6, 0xac, 0x6e, 0xa4, 0x17, 0xb5, - 0x00, 0x00, 0x0a, 0xd0, 0x01, 0x52, 0x33, 0x7b, 0xeb, 0x6c, 0x5f, 0x1b, - 0xc6, 0x9f, 0xeb, 0x6f, 0xbf, 0x87, 0xb1, 0xfc, 0xd0, 0x01, 0x16, 0x00, - 0xf4, 0x62, 0x1d, 0x08, 0x8f, 0x44, 0x21, 0xd1, 0x89, 0x57, 0x57, 0x5c, - 0x7b, 0x73, 0xc7, 0xbd, 0x3a, 0xe3, 0xdf, 0xa9, 0xcf, 0x51, 0xcd, 0xde, - 0xfd, 0xf9, 0xaa, 0x6d, 0x4e, 0xaa, 0xa8, 0x0f, 0xc9, 0xe7, 0x4e, 0x11, - 0x1b, 0xc1, 0x30, 0x00, 0x00, 0x00, 0x05, 0x80, 0x48, 0x00, 0x00, 0x00, - 0x12, 0x16, 0x9c, 0xca, 0x60, 0xb5, 0x92, 0xc3, 0x4b, 0x4e, 0x73, 0xd9, - 0xb7, 0x35, 0xf6, 0xe7, 0xc5, 0xc0, 0xff, 0xf1, 0x50, 0x80, 0x14, 0x1f, - 0xfc, 0x21, 0x1a, 0xcb, 0xaa, 0x3b, 0xc0, 0xbf, 0x9f, 0x7e, 0x13, 0x00, - 0x45, 0xc0, 0x3d, 0x18, 0x87, 0x42, 0x27, 0xd1, 0x09, 0x57, 0x1f, 0x45, - 0x4c, 0xdf, 0x58, 0xb0, 0x39, 0xfc, 0x57, 0xae, 0x6a, 0x8a, 0x29, 0x54, - 0xa0, 0xc5, 0xa6, 0x74, 0x89, 0x8a, 0x36, 0xa2, 0xc8, 0x2e, 0xb0, 0xba, - 0xe0, 0x00, 0x00, 0x00, 0x2e, 0x00, 0x98, 0xca, 0x04, 0x02, 0x60, 0x00, - 0x00, 0x25, 0x65, 0x4a, 0xa3, 0x2b, 0x25, 0x92, 0xba, 0xd9, 0x1d, 0x3b, - 0x79, 0x6d, 0x8d, 0x5d, 0xbf, 0x87, 0x0d, 0x23, 0xf1, 0xd4, 0x07, 0xa3, - 0x14, 0xe8, 0x44, 0xcb, 0x87, 0xd0, 0x78, 0xbd, 0xf4, 0xb0, 0x3a, 0x3b, - 0xcf, 0x54, 0xa2, 0x8d, 0xd2, 0x94, 0x1f, 0x95, 0x7e, 0xd5, 0x16, 0xbc, - 0x00, 0x54, 0x00, 0x50, 0x00, 0x00, 0x11, 0x20, 0x16, 0x00, 0x4f, 0xb0, - 0xd9, 0x55, 0x85, 0x6a, 0xb5, 0x0b, 0x08, 0x01, 0x10, 0x00, 0x14, 0x14, - 0x5c, 0x40, 0x9b, 0xad, 0xb7, 0x64, 0xb4, 0x9e, 0xb7, 0x9e, 0x94, 0x23, - 0x92, 0x3c, 0x33, 0x5b, 0xbf, 0xf7, 0x64, 0xff, 0x98, 0x78, 0xff, 0xf1, - 0x50, 0x80, 0x16, 0xff, 0xfc, 0x21, 0x1a, 0xcc, 0x5c, 0x87, 0x00, 0x14, - 0x1b, 0x7d, 0x39, 0x81, 0x44, 0x3a, 0x51, 0x0e, 0x84, 0x43, 0xa5, 0x11, - 0xa0, 0x99, 0x1e, 0x77, 0xf5, 0x51, 0xf3, 0xd7, 0x3e, 0xdc, 0xf3, 0xf5, - 0x92, 0x3b, 0xb2, 0xb7, 0x9e, 0xf8, 0xab, 0x69, 0x95, 0x92, 0x87, 0x83, - 0xa3, 0x41, 0x7d, 0x20, 0x00, 0x00, 0x02, 0x48, 0x42, 0x13, 0x95, 0xcf, - 0x74, 0x22, 0x2e, 0x02, 0xf4, 0x4c, 0x12, 0xe1, 0x47, 0x1e, 0x3f, 0x8e, - 0xba, 0x95, 0x45, 0x45, 0x69, 0x47, 0x9e, 0x92, 0x16, 0xc1, 0x08, 0xe7, - 0xce, 0xb5, 0x3b, 0xe8, 0xd0, 0x09, 0xa3, 0x14, 0xe8, 0x84, 0x68, 0x25, - 0xc5, 0xf9, 0x54, 0x72, 0xcf, 0x39, 0xbe, 0xb2, 0x47, 0x3f, 0x8d, 0xf8, - 0xa5, 0x14, 0x6f, 0x2a, 0x95, 0x42, 0x9f, 0xbf, 0x8b, 0x13, 0x78, 0x37, - 0x89, 0x8c, 0xf2, 0x00, 0x6b, 0x05, 0x00, 0x51, 0x89, 0xdc, 0xdd, 0x36, - 0x8a, 0x82, 0x99, 0xef, 0x9b, 0x1a, 0xb0, 0xa1, 0x4d, 0x52, 0xcb, 0x15, - 0x29, 0xdb, 0x5f, 0x45, 0x72, 0xb1, 0xdb, 0xa3, 0x78, 0xea, 0x5b, 0xa8, - 0x2e, 0x42, 0xa5, 0x49, 0x2d, 0x77, 0xf7, 0x67, 0xfa, 0xe3, 0xbe, 0x3c, - 0xbd, 0x6a, 0xaf, 0x6b, 0xd2, 0x9c, 0xcb, 0xdc, 0x13, 0xba, 0xac, 0x69, - 0x0e, 0xff, 0xf1, 0x50, 0x80, 0x11, 0x9f, 0xfc, 0x21, 0x1a, 0xcc, 0x67, - 0x02, 0xc0, 0x10, 0x0f, 0x7f, 0x13, 0x00, 0x45, 0xe0, 0x2d, 0x30, 0x87, - 0x44, 0x21, 0xd2, 0x09, 0x44, 0xf6, 0xe8, 0x3e, 0x6b, 0xe3, 0xd7, 0xd2, - 0xb3, 0x43, 0x9b, 0x9e, 0xf9, 0xaa, 0xfc, 0xd4, 0xca, 0x97, 0x60, 0x14, - 0xd0, 0x2f, 0x69, 0x55, 0x10, 0x00, 0x09, 0x80, 0x80, 0x12, 0x01, 0x60, - 0x05, 0xd7, 0x04, 0xea, 0xbd, 0x0c, 0xd0, 0x42, 0x52, 0xb3, 0xa1, 0x07, - 0x49, 0x4b, 0x8d, 0x26, 0x7f, 0x13, 0x00, 0x85, 0xa0, 0x3d, 0x28, 0x93, - 0x42, 0x21, 0xd2, 0x09, 0x11, 0x3c, 0xf4, 0x3e, 0x5e, 0xde, 0x38, 0x6a, - 0xac, 0xe5, 0xf1, 0x9b, 0xdf, 0xda, 0x95, 0xb5, 0xeb, 0x39, 0xa0, 0x29, - 0x64, 0x80, 0x00, 0x00, 0x00, 0xa8, 0x50, 0x00, 0x29, 0x9d, 0x40, 0x52, - 0x36, 0x02, 0xa0, 0xa8, 0x3d, 0xe6, 0xeb, 0xa9, 0x6a, 0x34, 0x43, 0x64, - 0x6f, 0x18, 0xd6, 0x7c, 0xbf, 0x5f, 0x47, 0x9a, 0x1c, 0xff, 0xf1, 0x50, - 0x80, 0x14, 0x1f, 0xfc, 0x21, 0x1a, 0xce, 0x10, 0xfd, 0xc0, 0xbf, 0x9f, - 0x7e, 0x23, 0x81, 0x05, 0x40, 0x3d, 0x18, 0xa7, 0x44, 0x22, 0x21, 0xaf, - 0x5c, 0x34, 0x6f, 0x4d, 0xe9, 0x96, 0x73, 0xf3, 0xbf, 0x54, 0xa2, 0x8d, - 0xee, 0xa9, 0x40, 0x9e, 0x20, 0xa1, 0x84, 0xc9, 0x22, 0x14, 0x48, 0x00, - 0x00, 0x09, 0x0a, 0x93, 0x88, 0x20, 0x39, 0xbb, 0xc8, 0x8c, 0xd2, 0x12, - 0xe6, 0x48, 0xa0, 0x94, 0x40, 0x00, 0x00, 0x16, 0x00, 0x7b, 0x46, 0x10, - 0xf9, 0xf6, 0x46, 0xfa, 0x51, 0xec, 0x66, 0x26, 0xf1, 0xdc, 0xcb, 0x3d, - 0x65, 0x43, 0x4f, 0xc2, 0x40, 0x10, 0x88, 0x02, 0x2c, 0x01, 0xe8, 0xc4, - 0x3a, 0x11, 0x3e, 0x88, 0x4a, 0xb4, 0xfa, 0x0d, 0xe9, 0xbd, 0x32, 0xce, - 0x5f, 0x5b, 0xf5, 0xba, 0xa2, 0x82, 0xa9, 0x40, 0xba, 0x49, 0x77, 0x99, - 0x40, 0x69, 0x00, 0x00, 0x00, 0xb9, 0x50, 0x59, 0x51, 0x40, 0xce, 0x88, - 0x0b, 0x17, 0x00, 0x11, 0x00, 0x00, 0x00, 0x65, 0x5e, 0x0f, 0x1f, 0x8a, - 0x75, 0x29, 0x0f, 0xf7, 0x6e, 0x55, 0xd7, 0x7f, 0xe1, 0x2a, 0xe0, 0xf2, - 0xf0, 0xff, 0xf1, 0x50, 0x80, 0x10, 0x9f, 0xfc, 0x21, 0x1a, 0xcf, 0x0f, - 0x21, 0x60, 0x04, 0x0f, 0x7f, 0x11, 0x80, 0xc6, 0x00, 0x2d, 0x28, 0x87, - 0x44, 0x21, 0xd0, 0x88, 0x74, 0x82, 0x42, 0x7e, 0xa1, 0xbb, 0x9e, 0xf7, - 0xe7, 0x2c, 0x39, 0xf9, 0xcd, 0xd7, 0xdb, 0x75, 0x84, 0xba, 0xdd, 0x01, - 0x48, 0xad, 0x05, 0x02, 0x54, 0x00, 0x00, 0x00, 0x02, 0xb2, 0x00, 0x52, - 0x00, 0x00, 0x42, 0x68, 0x30, 0x7b, 0x49, 0x60, 0x9a, 0x99, 0xb9, 0x2d, - 0xfb, 0x59, 0x2f, 0xc9, 0xfc, 0x4a, 0x02, 0x17, 0x80, 0xb4, 0xe2, 0x2d, - 0x30, 0x90, 0x8f, 0xbd, 0x8f, 0x7c, 0xfb, 0xe4, 0x9c, 0xf1, 0xbd, 0x1c, - 0xbe, 0xb3, 0x78, 0xdf, 0xe8, 0x35, 0x79, 0xba, 0x00, 0x2c, 0x00, 0x0a, - 0x00, 0x0b, 0x26, 0x0a, 0x5a, 0xe8, 0x00, 0x00, 0x14, 0xae, 0x75, 0xa2, - 0xd1, 0x64, 0x46, 0x4f, 0xeb, 0xdb, 0x67, 0x09, 0x6b, 0xb6, 0xde, 0x51, - 0xe0, 0xff, 0xf1, 0x50, 0x80, 0x14, 0x5f, 0xfc, 0x21, 0x1a, 0xca, 0x00, - 0xba, 0x00, 0x7f, 0x9f, 0x7b, 0x14, 0x00, 0xc5, 0xda, 0x21, 0x4e, 0x88, - 0x4a, 0xa9, 0x1f, 0x00, 0x0d, 0xe8, 0x0f, 0x1b, 0xf4, 0xa5, 0x07, 0x8a, - 0xc5, 0x50, 0x21, 0x54, 0x29, 0x4a, 0x92, 0x5c, 0x00, 0x2b, 0x70, 0x00, - 0x00, 0x28, 0x13, 0xb4, 0xc2, 0x53, 0x00, 0x09, 0xd6, 0xc0, 0x00, 0x20, - 0x05, 0x8b, 0x26, 0x11, 0x0a, 0x00, 0x00, 0x92, 0xd2, 0xbc, 0x73, 0xc9, - 0x25, 0x21, 0x0d, 0x5e, 0xc6, 0x35, 0xd3, 0xac, 0xbc, 0xf1, 0xf3, 0x47, - 0xde, 0xfc, 0x24, 0x02, 0x08, 0x40, 0x22, 0x90, 0x2e, 0x88, 0x43, 0xa1, - 0x14, 0x68, 0x84, 0xab, 0xe3, 0x7f, 0x47, 0xaf, 0x60, 0x39, 0xfb, 0xf1, - 0xdd, 0x52, 0x82, 0xab, 0x15, 0x41, 0x62, 0x94, 0x54, 0x68, 0x11, 0x72, - 0x02, 0xa2, 0xa0, 0x4a, 0x53, 0x05, 0x84, 0x22, 0x0c, 0x23, 0x14, 0x56, - 0x4d, 0x26, 0x55, 0x42, 0x95, 0x41, 0x14, 0x08, 0xd8, 0x00, 0x01, 0x49, - 0x32, 0x6d, 0x4b, 0xe4, 0x5e, 0xe6, 0x4a, 0x35, 0x53, 0x25, 0xb3, 0xf0, - 0x85, 0xfa, 0x5d, 0x9b, 0x24, 0x77, 0x70, 0xff, 0xf1, 0x50, 0x80, 0x11, - 0xdf, 0xfc, 0x21, 0x1a, 0xce, 0x23, 0x85, 0x60, 0x3e, 0x0f, 0x7f, 0x13, - 0x00, 0x48, 0x20, 0x11, 0x68, 0x0b, 0x48, 0x26, 0xd0, 0x88, 0x74, 0x42, - 0x54, 0x9f, 0x56, 0x01, 0x97, 0xbd, 0x07, 0x3f, 0x79, 0xbf, 0x9a, 0x29, - 0xcb, 0x8a, 0xaa, 0x02, 0x93, 0x5e, 0x68, 0x40, 0x0a, 0xa8, 0x01, 0x70, - 0x00, 0x5b, 0x47, 0x10, 0x00, 0x00, 0x00, 0x00, 0x40, 0x54, 0x08, 0x10, - 0xb9, 0x98, 0x00, 0x25, 0x72, 0x03, 0x7d, 0xa5, 0xf9, 0xec, 0xb5, 0xa9, - 0xff, 0xbf, 0x6d, 0xb5, 0x7f, 0x13, 0x80, 0x86, 0x1a, 0x41, 0x36, 0x90, - 0x48, 0x8f, 0x6f, 0x1f, 0x80, 0xf9, 0xeb, 0x38, 0xbd, 0xe8, 0x07, 0x65, - 0x66, 0xfe, 0xc5, 0x2b, 0x2d, 0xdd, 0x00, 0x08, 0xd6, 0x29, 0x0c, 0xc0, - 0x01, 0x50, 0x00, 0x00, 0x09, 0x80, 0x00, 0x01, 0x40, 0x00, 0x02, 0x80, - 0x05, 0x43, 0x2a, 0xe4, 0xb1, 0x64, 0xc2, 0xed, 0x0e, 0xda, 0x36, 0x6f, - 0x94, 0x66, 0x94, 0x38, 0xf0, 0xff, 0xf1, 0x50, 0x80, 0x13, 0x5f, 0xfc, - 0x21, 0x1a, 0xc8, 0x14, 0xb1, 0x80, 0x08, 0x1f, 0x7e, 0x13, 0x80, 0x44, - 0x20, 0x21, 0x50, 0x0f, 0x46, 0x29, 0xd1, 0x09, 0x57, 0xc1, 0xe4, 0xc9, - 0x02, 0xa1, 0xcf, 0xde, 0xfd, 0x29, 0x41, 0xcb, 0x14, 0xa0, 0xba, 0x51, - 0x58, 0x2e, 0x10, 0x50, 0x00, 0x80, 0x0a, 0x4b, 0x01, 0x15, 0xb8, 0xc6, - 0x6d, 0x73, 0x9d, 0x7b, 0x35, 0x2f, 0x76, 0xb0, 0x10, 0x41, 0x82, 0x72, - 0x9d, 0x2b, 0x4a, 0x67, 0xae, 0x65, 0xc8, 0x01, 0x30, 0x00, 0x12, 0x5d, - 0x5a, 0x75, 0xcd, 0x64, 0xb9, 0x92, 0xad, 0x29, 0x14, 0x30, 0xe2, 0x95, - 0x14, 0xe3, 0x48, 0x68, 0x3f, 0x81, 0x84, 0x20, 0x11, 0x08, 0x08, 0x52, - 0x04, 0xd3, 0x88, 0xb4, 0xa6, 0x5f, 0xdd, 0x9f, 0x3f, 0x87, 0x7d, 0x77, - 0xc4, 0x72, 0xf8, 0xce, 0x73, 0x6f, 0x9e, 0x4b, 0xb5, 0xa9, 0x40, 0x00, - 0x00, 0x00, 0x4a, 0x70, 0x00, 0x90, 0x00, 0x09, 0xa0, 0x84, 0x9c, 0x5b, - 0x7c, 0xad, 0x8e, 0x2d, 0x7d, 0x1b, 0xc2, 0x9c, 0x30, 0xd5, 0x5a, 0x9e, - 0xa9, 0xba, 0xe0, 0xff, 0xf1, 0x50, 0x80, 0x13, 0x1f, 0xfc, 0x21, 0x1a, - 0xcc, 0x9d, 0x98, 0xc0, 0x5f, 0x0f, 0x7e, 0x12, 0x00, 0x84, 0x60, 0x11, - 0x58, 0x0f, 0x48, 0x21, 0xd0, 0x89, 0x74, 0x82, 0x42, 0x79, 0xe8, 0x62, - 0xc7, 0x3a, 0x87, 0x3f, 0x7c, 0xbd, 0xfb, 0xa2, 0x95, 0xc5, 0x6f, 0x14, - 0x14, 0x2c, 0x01, 0x15, 0x01, 0x14, 0x00, 0x00, 0x2c, 0x08, 0x54, 0x56, - 0x24, 0xa4, 0x15, 0x48, 0xb0, 0x01, 0x40, 0x00, 0x47, 0xe8, 0xf1, 0x88, - 0xb0, 0x75, 0xe9, 0x18, 0xf6, 0xa6, 0xef, 0x57, 0xf4, 0xc1, 0x7e, 0xcf, - 0xc2, 0x50, 0x08, 0xc0, 0x05, 0xa3, 0x14, 0xe8, 0x84, 0xab, 0x9c, 0x6f, - 0xe0, 0x33, 0xbf, 0x60, 0x07, 0x3f, 0x8a, 0xf5, 0x4a, 0x28, 0xee, 0xaa, - 0x94, 0x04, 0xd1, 0x02, 0x84, 0x05, 0x64, 0x58, 0x88, 0x02, 0x60, 0x13, - 0x80, 0x02, 0x73, 0x4f, 0xdf, 0x7c, 0x68, 0x10, 0x03, 0x02, 0xa0, 0x21, - 0x54, 0x42, 0xe0, 0x00, 0x34, 0x01, 0x24, 0xc0, 0x24, 0x2d, 0x9d, 0x79, - 0xf5, 0xb1, 0x1e, 0x78, 0x63, 0xcb, 0xbd, 0xa9, 0xe2, 0xed, 0x1e, 0xff, - 0xf1, 0x50, 0x80, 0x16, 0xff, 0xfc, 0x21, 0x1a, 0xc8, 0x6d, 0xbd, 0xc1, - 0xff, 0x1f, 0x7d, 0x23, 0x00, 0x44, 0x40, 0x11, 0x60, 0x0b, 0x44, 0x2a, - 0xd0, 0x89, 0x08, 0x4f, 0x6c, 0xf2, 0x00, 0x00, 0xe8, 0xe7, 0xba, 0x52, - 0x85, 0x7a, 0x52, 0xaa, 0x82, 0x6c, 0x3a, 0x8c, 0x40, 0xee, 0x48, 0x10, - 0x13, 0x98, 0x15, 0x2c, 0x00, 0x00, 0x19, 0x42, 0x10, 0xb9, 0xf3, 0xaa, - 0xc6, 0x9a, 0xdd, 0x35, 0x05, 0xd2, 0x86, 0x64, 0x2f, 0x20, 0xdc, 0x1c, - 0xd2, 0x13, 0x06, 0x90, 0x29, 0x6e, 0x01, 0x15, 0x8a, 0x8a, 0xd8, 0x9f, - 0xbf, 0xe9, 0x7e, 0x4a, 0xcc, 0x24, 0x32, 0x97, 0xdc, 0x83, 0x78, 0x23, - 0xeb, 0x89, 0x53, 0xfb, 0x7f, 0x13, 0x00, 0x45, 0xc0, 0x3d, 0x10, 0xa7, - 0x46, 0x22, 0x21, 0xae, 0x3e, 0xe0, 0x7a, 0xf6, 0x1b, 0xd0, 0xe5, 0xf1, - 0xdd, 0x29, 0x43, 0xe7, 0x4c, 0x55, 0x50, 0xb0, 0x90, 0x20, 0x98, 0x00, - 0x10, 0x00, 0x0a, 0x00, 0xa0, 0xa9, 0x62, 0x40, 0x66, 0xb3, 0x29, 0x02, - 0x2b, 0x35, 0xe8, 0x86, 0x7c, 0xad, 0x54, 0x92, 0xd6, 0xae, 0x95, 0xa5, - 0x34, 0xeb, 0x58, 0x02, 0x56, 0x2a, 0x98, 0x6c, 0xbc, 0x4b, 0xde, 0xa8, - 0x60, 0x59, 0xc5, 0x03, 0x49, 0xd3, 0x0b, 0xde, 0x0c, 0x4a, 0x1b, 0xbd, - 0x1b, 0x38, 0xff, 0xf1, 0x50, 0x80, 0x13, 0x1f, 0xfc, 0x21, 0x1a, 0xc8, - 0x2c, 0x61, 0x80, 0x08, 0x1f, 0x7e, 0x1a, 0x01, 0x34, 0x62, 0x9d, 0x08, - 0x99, 0x7d, 0x74, 0x00, 0xa8, 0x6e, 0xe3, 0x9f, 0xbd, 0xfa, 0x52, 0x85, - 0x6d, 0x4a, 0xa0, 0xa2, 0x02, 0x82, 0x2e, 0x15, 0x00, 0x00, 0x00, 0x4c, - 0x16, 0x2b, 0x1a, 0xaa, 0xa2, 0xec, 0xd7, 0x38, 0xd6, 0xfb, 0x84, 0x67, - 0x5a, 0x07, 0x1a, 0x32, 0x6b, 0x57, 0xef, 0x28, 0x05, 0x64, 0x8a, 0x60, - 0x00, 0x90, 0x54, 0x14, 0x19, 0xca, 0x68, 0xbd, 0x72, 0x28, 0x43, 0xbf, - 0x0b, 0x45, 0xc6, 0x3a, 0x72, 0xd3, 0xcb, 0xeb, 0xb3, 0xf8, 0x98, 0x02, - 0x2f, 0x01, 0x69, 0xc4, 0x3a, 0x61, 0x29, 0xf7, 0xf7, 0xf8, 0xd0, 0xee, - 0x71, 0x55, 0xcf, 0x9d, 0xdc, 0xa8, 0xe6, 0xf1, 0xba, 0xe5, 0x9f, 0x35, - 0x24, 0xb6, 0x2a, 0x82, 0xa0, 0x00, 0x11, 0x00, 0x00, 0x13, 0x0f, 0xd8, - 0x45, 0x71, 0x50, 0x4d, 0x72, 0x2a, 0xdb, 0x92, 0xf1, 0xbe, 0xc9, 0x41, - 0x9f, 0xfa, 0x9a, 0x35, 0x75, 0xc1, 0x7f, 0xed, 0x87, 0x80, 0xff, 0xf1, - 0x50, 0x80, 0x11, 0xbf, 0xfc, 0x21, 0x1a, 0xc8, 0x00, 0x81, 0x00, 0x28, - 0x0f, 0x7f, 0x12, 0x80, 0x84, 0x20, 0x21, 0x48, 0x17, 0x48, 0x27, 0xd2, - 0x09, 0x13, 0xe3, 0x3c, 0x81, 0x53, 0x76, 0xe6, 0xe7, 0x9d, 0xfb, 0xd2, - 0x87, 0x36, 0xde, 0x01, 0x2b, 0xda, 0x93, 0xb2, 0x62, 0x50, 0x0a, 0x80, - 0x15, 0x54, 0x02, 0xa2, 0x60, 0x15, 0x00, 0xb8, 0x22, 0x05, 0x4c, 0x60, - 0x14, 0xdc, 0x4e, 0xed, 0x24, 0xdf, 0x3a, 0x66, 0xcf, 0x7c, 0x7c, 0x78, - 0x74, 0x8c, 0xb1, 0x47, 0xdf, 0xff, 0x5f, 0xc4, 0x20, 0x51, 0x08, 0x08, - 0x54, 0x05, 0xd2, 0x88, 0x74, 0x22, 0x2d, 0x30, 0x88, 0x84, 0xfd, 0xeb, - 0x7c, 0x6e, 0x66, 0x71, 0xe3, 0xd9, 0xcd, 0xdf, 0x35, 0x9f, 0x3c, 0xd6, - 0x17, 0x75, 0xb0, 0x16, 0x8d, 0x00, 0x00, 0x00, 0x00, 0x2e, 0x00, 0x56, - 0xe1, 0x94, 0x15, 0x4b, 0xb6, 0xd4, 0xd5, 0x71, 0xcf, 0x85, 0x6e, 0xcf, - 0xf4, 0xfe, 0xb2, 0x8e, 0xbe, 0xe3, 0x70, 0xff, 0xf1, 0x50, 0x80, 0x15, - 0x7f, 0xfc, 0x21, 0x1a, 0xc9, 0x8f, 0xba, 0x41, 0x7f, 0x1f, 0x7e, 0x22, - 0x00, 0x85, 0xe0, 0x3d, 0x10, 0x87, 0x42, 0x27, 0xd1, 0x89, 0x57, 0xf1, - 0x20, 0x03, 0x2f, 0x7a, 0x39, 0xfb, 0xf1, 0xdd, 0x52, 0x87, 0x3a, 0xac, - 0x55, 0x01, 0xd9, 0x7a, 0x64, 0x5c, 0x00, 0x17, 0x02, 0xc0, 0x00, 0x9a, - 0x73, 0x2c, 0x13, 0xb0, 0x55, 0x7d, 0x21, 0x15, 0xd6, 0x9a, 0xd4, 0x72, - 0xad, 0xd1, 0x54, 0x16, 0x2e, 0x01, 0x01, 0x01, 0x74, 0xe5, 0x78, 0xb3, - 0x42, 0xcb, 0x93, 0xf5, 0x3e, 0xf2, 0xa5, 0x5d, 0x7d, 0x1d, 0xbe, 0xaf, - 0xc2, 0x40, 0x08, 0x84, 0x02, 0x2e, 0x01, 0xe8, 0x85, 0x5a, 0x21, 0x2b, - 0xe2, 0xe4, 0x07, 0x8f, 0x20, 0xcb, 0x73, 0xf7, 0xe2, 0x94, 0xa0, 0xf0, - 0xc5, 0x50, 0x56, 0x4b, 0xda, 0xc0, 0x22, 0x28, 0x00, 0x98, 0x00, 0x0d, - 0x05, 0x83, 0x35, 0xee, 0x12, 0xa4, 0x45, 0xc5, 0x0a, 0x3b, 0x83, 0xa7, - 0x6d, 0xcc, 0x10, 0x50, 0xda, 0x9c, 0x40, 0x81, 0x22, 0xe5, 0x41, 0x30, - 0x8a, 0xa9, 0x6d, 0x9a, 0x8a, 0xd6, 0xfe, 0xde, 0x94, 0x74, 0xad, 0x2b, - 0x7f, 0xf7, 0xaf, 0x97, 0xde, 0x75, 0xe5, 0xec, 0xf6, 0x70, 0xff, 0xf1, - 0x50, 0x80, 0x12, 0x9f, 0xfc, 0x21, 0x1a, 0xc8, 0x41, 0xbf, 0xc0, 0x7f, - 0x8f, 0x7a, 0x11, 0x00, 0x48, 0x60, 0x31, 0x18, 0x04, 0x50, 0x03, 0xd1, - 0x0a, 0xf4, 0x42, 0x47, 0x57, 0x00, 0x01, 0xd9, 0xe3, 0xd2, 0x94, 0x50, - 0xef, 0x78, 0xa0, 0x95, 0xe9, 0xbd, 0x33, 0x02, 0x00, 0x16, 0x1d, 0x64, - 0x09, 0x88, 0x10, 0x90, 0x00, 0x2d, 0x70, 0x49, 0x30, 0x20, 0xbb, 0x45, - 0x82, 0x51, 0x00, 0x00, 0x00, 0x09, 0x00, 0x89, 0x20, 0x5a, 0x1d, 0x87, - 0xc3, 0x3c, 0xd0, 0x96, 0xfe, 0xba, 0xb0, 0x7d, 0x18, 0x5f, 0x84, 0xa0, - 0x51, 0x58, 0x0f, 0x48, 0x28, 0xd1, 0x08, 0xc8, 0x4b, 0xeb, 0xa1, 0xeb, - 0xd8, 0x03, 0x9f, 0xc6, 0x57, 0xba, 0x8a, 0x33, 0x2a, 0x94, 0x16, 0x9a, - 0xc0, 0x40, 0x2c, 0x00, 0xb0, 0x01, 0x50, 0x2c, 0x00, 0x04, 0x8a, 0x02, - 0x7a, 0xcb, 0x06, 0x12, 0x50, 0x00, 0x02, 0x62, 0x88, 0x00, 0x5a, 0x64, - 0xed, 0x4d, 0xe4, 0xe2, 0xac, 0x53, 0x87, 0xa5, 0x2f, 0x10, 0x6a, 0xef, - 0x94, 0xfc, 0xff, 0xf1, 0x50, 0x80, 0x14, 0xff, 0xfc, 0x21, 0x1a, 0xca, - 0x04, 0x3e, 0x81, 0x7f, 0x8f, 0x7f, 0x11, 0x00, 0x44, 0x20, 0x22, 0x18, - 0x04, 0x54, 0x04, 0xd1, 0x08, 0x74, 0x22, 0x8d, 0x19, 0x91, 0xf5, 0xc2, - 0xa1, 0x96, 0x19, 0x6e, 0x5e, 0xfc, 0x7a, 0xaa, 0x50, 0x32, 0xb2, 0xa8, - 0x0a, 0x9d, 0x85, 0x41, 0x50, 0xb0, 0x00, 0x00, 0x00, 0x20, 0x13, 0x24, - 0xc4, 0x24, 0x24, 0xb8, 0x4a, 0xe5, 0x14, 0x04, 0xaf, 0x59, 0xc0, 0x00, - 0x58, 0x15, 0x99, 0x42, 0xf0, 0xad, 0xa3, 0xd5, 0xc6, 0x6d, 0xbe, 0xbd, - 0x92, 0x38, 0xb6, 0x2d, 0x6f, 0x15, 0xf8, 0x4c, 0x02, 0x10, 0x80, 0xc5, - 0x00, 0x4d, 0x10, 0xaf, 0x42, 0x23, 0x21, 0x3e, 0x37, 0xc6, 0x8f, 0x1e, - 0x40, 0x74, 0x6f, 0xc5, 0x29, 0x41, 0x5e, 0xa9, 0x54, 0x08, 0x28, 0xc0, - 0x44, 0x00, 0x22, 0x01, 0x30, 0xa0, 0x91, 0x53, 0x68, 0x01, 0x90, 0x98, - 0x41, 0x81, 0x24, 0x97, 0x55, 0x60, 0x88, 0x8d, 0xc0, 0x00, 0x45, 0x20, - 0x11, 0xcd, 0x54, 0xb7, 0xa5, 0x32, 0xb5, 0xb5, 0x5c, 0xbe, 0x76, 0x71, - 0x29, 0x46, 0x44, 0x7a, 0x18, 0x23, 0xff, 0x6b, 0x85, 0xd6, 0x66, 0xb3, - 0x80, 0xff, 0xf1, 0x50, 0x80, 0x14, 0x7f, 0xfc, 0x21, 0x1a, 0xca, 0x43, - 0xd7, 0xa0, 0x01, 0x0f, 0x72, 0x00, 0x88, 0x80, 0x22, 0x10, 0x18, 0xb8, - 0x05, 0xaa, 0x10, 0xe9, 0x08, 0x9e, 0x3c, 0x83, 0xbb, 0xd0, 0x07, 0x9f, - 0x79, 0xce, 0xe9, 0x92, 0xbe, 0x75, 0x6d, 0xe3, 0x01, 0x69, 0xd0, 0x90, - 0x00, 0x09, 0x80, 0x01, 0x14, 0x42, 0x80, 0x09, 0x48, 0x43, 0xe2, 0x77, - 0xd4, 0x47, 0xf6, 0x80, 0x7f, 0x95, 0xcd, 0x6b, 0x23, 0xab, 0x07, 0xe7, - 0xe2, 0xd7, 0xeb, 0x4b, 0xc0, 0x94, 0x08, 0x2d, 0x01, 0x68, 0xc5, 0x3a, - 0x21, 0x28, 0xf2, 0x1d, 0xf4, 0x00, 0xec, 0xef, 0x7e, 0x29, 0x4a, 0x1c, - 0xaa, 0x98, 0x16, 0x46, 0xa0, 0xa8, 0x3b, 0x35, 0x00, 0x00, 0x05, 0xc0, - 0xb0, 0xfa, 0x20, 0xbf, 0x7c, 0x4b, 0x3b, 0x62, 0x4a, 0x15, 0x95, 0x71, - 0x76, 0x9f, 0x1e, 0xf3, 0xdd, 0xaa, 0x52, 0x9e, 0xf2, 0x7d, 0x7f, 0x1a, - 0xf7, 0xed, 0xac, 0x3a, 0x67, 0x0b, 0x2e, 0x46, 0xa9, 0xa5, 0x19, 0xa9, - 0x96, 0xd4, 0x49, 0x62, 0x2a, 0xb2, 0x92, 0xa4, 0x5b, 0x6f, 0x99, 0xf1, - 0x8f, 0x8d, 0xcb, 0x9f, 0xf9, 0xc7, 0xf9, 0xf0, 0xff, 0xf1, 0x50, 0x80, - 0x12, 0x5f, 0xfc, 0x21, 0x1a, 0xcc, 0x31, 0xba, 0xc0, 0x3e, 0x0f, 0x76, - 0x10, 0x80, 0xc4, 0x80, 0x21, 0x58, 0x0f, 0x4a, 0x25, 0xd2, 0x99, 0x1c, - 0x03, 0x7a, 0x00, 0xf0, 0xe7, 0x79, 0x5e, 0xf4, 0xa7, 0x77, 0x2f, 0xbc, - 0x50, 0x25, 0x25, 0xc0, 0x44, 0x00, 0x00, 0x00, 0x03, 0x63, 0x6c, 0xe1, - 0x30, 0x09, 0x80, 0x00, 0x24, 0x69, 0xb6, 0xf6, 0x94, 0xec, 0xaa, 0xd5, - 0xcf, 0xd1, 0xab, 0x3a, 0xcd, 0xc7, 0x6c, 0x1c, 0xff, 0x31, 0xb2, 0xec, - 0x43, 0x01, 0x08, 0x80, 0x42, 0xc0, 0x1e, 0x8c, 0x51, 0xa3, 0x11, 0x90, - 0x9c, 0x01, 0xbd, 0x00, 0x78, 0xbd, 0x7a, 0xa5, 0x28, 0xe6, 0x2a, 0x94, - 0x0b, 0x67, 0x81, 0xa4, 0x18, 0x40, 0x00, 0x00, 0x22, 0x14, 0x90, 0x14, - 0x00, 0x01, 0x40, 0x89, 0x49, 0xac, 0x04, 0x89, 0x99, 0x2c, 0x02, 0x97, - 0xd0, 0xad, 0x04, 0x6f, 0x19, 0x23, 0x3b, 0xb3, 0x5a, 0xab, 0xda, 0x56, - 0x83, 0x7b, 0x0c, 0x61, 0x4a, 0xfc, 0xa1, 0xf2, 0xb3, 0x80, 0xff, 0xf1, - 0x50, 0x80, 0x14, 0xff, 0xfc, 0x21, 0x1a, 0xc8, 0x0a, 0xb6, 0xc0, 0x7f, - 0x3f, 0x78, 0x12, 0x80, 0x85, 0xc0, 0x3d, 0x18, 0xa3, 0x44, 0x26, 0x67, - 0x5a, 0x00, 0x0c, 0xb3, 0xaf, 0xde, 0xbd, 0x29, 0x43, 0xc4, 0xaa, 0x55, - 0x05, 0x55, 0xdd, 0x36, 0x40, 0x50, 0x0b, 0x4c, 0x16, 0x4c, 0x10, 0x30, - 0x93, 0x01, 0x20, 0x2e, 0x11, 0xa8, 0x49, 0x12, 0xab, 0x10, 0xa0, 0x90, - 0x6b, 0x15, 0x40, 0x00, 0x01, 0x79, 0x01, 0x5a, 0xb9, 0x22, 0x2f, 0xaf, - 0xf5, 0x68, 0xc9, 0xdf, 0x37, 0xc2, 0x9c, 0xbf, 0x34, 0xdf, 0xff, 0xbb, - 0x90, 0x04, 0x42, 0x03, 0x10, 0x80, 0x44, 0x20, 0x11, 0x8e, 0x88, 0x4f, - 0xa2, 0x12, 0xa0, 0x77, 0xa0, 0xe7, 0x86, 0xf4, 0x00, 0x7d, 0xe7, 0xaa, - 0x50, 0xe7, 0x55, 0x4a, 0xa0, 0xbd, 0x80, 0x02, 0x60, 0x82, 0xa0, 0x00, - 0x00, 0x11, 0x04, 0x40, 0x44, 0x12, 0xa2, 0xe8, 0xd6, 0xaf, 0xd4, 0xa4, - 0x76, 0x23, 0x34, 0xcb, 0x22, 0x94, 0x91, 0xb0, 0x4e, 0x48, 0x00, 0x00, - 0x27, 0x53, 0x12, 0x7a, 0xeb, 0x0b, 0xd6, 0xa5, 0x6f, 0x12, 0x0f, 0xcb, - 0x4d, 0x51, 0x66, 0xfe, 0xee, 0xbe, 0x29, 0x30, 0x37, 0xff, 0xf1, 0x50, - 0x80, 0x1a, 0x9f, 0xfc, 0x21, 0x1a, 0xca, 0x09, 0xf6, 0x41, 0x5b, 0x97, - 0x73, 0x21, 0x80, 0x48, 0x40, 0x11, 0x08, 0x08, 0x58, 0x02, 0xd1, 0x08, - 0x74, 0x22, 0x9d, 0x08, 0x88, 0x86, 0x80, 0x00, 0x0f, 0x2e, 0x79, 0xf1, - 0x5b, 0xb7, 0x3a, 0x37, 0xbb, 0xe7, 0x59, 0x41, 0x6b, 0xae, 0x02, 0x00, - 0x08, 0x00, 0x00, 0x80, 0x01, 0x12, 0x96, 0x12, 0x3d, 0xe5, 0xc1, 0x96, - 0xdf, 0x8d, 0xf3, 0xab, 0x18, 0x42, 0xd3, 0x10, 0x47, 0xec, 0x01, 0x6d, - 0x2d, 0xc5, 0x01, 0x52, 0x00, 0x30, 0x0c, 0xe1, 0x0f, 0xc9, 0x16, 0x31, - 0xbd, 0xd4, 0x39, 0x5b, 0x9c, 0x06, 0x30, 0x97, 0xdd, 0x8a, 0xb4, 0x49, - 0xeb, 0x4d, 0x9e, 0x15, 0x7b, 0x3c, 0x10, 0x80, 0x24, 0x10, 0x10, 0x8c, - 0x02, 0x2b, 0x01, 0xe8, 0x45, 0xe8, 0x37, 0x90, 0x00, 0x0e, 0xc7, 0x2a, - 0x51, 0x42, 0x94, 0xca, 0x09, 0x2a, 0x65, 0x49, 0x00, 0x11, 0xc0, 0x5c, - 0x00, 0x05, 0xc1, 0x1a, 0x52, 0x0d, 0x7e, 0xe4, 0xa9, 0xd5, 0x78, 0x25, - 0xcf, 0x54, 0x6b, 0x7c, 0x9a, 0x7f, 0xbd, 0xe1, 0x7f, 0xda, 0x2f, 0x53, - 0x9c, 0xb1, 0xce, 0x53, 0xa3, 0x1f, 0xff, 0xfd, 0xe9, 0x76, 0x79, 0x8a, - 0xca, 0x9e, 0xfb, 0xc1, 0x22, 0x76, 0x45, 0x21, 0x01, 0x70, 0x02, 0x28, - 0x07, 0xd0, 0x26, 0x96, 0x5e, 0x3f, 0xcf, 0xd3, 0x4d, 0x34, 0x35, 0x50, - 0xd7, 0xb2, 0xa3, 0xd5, 0x55, 0xbb, 0x0b, 0x35, 0xa2, 0x33, 0x65, 0xe8, - 0x06, 0x84, 0xd5, 0x06, 0xe0, 0xff, 0xf1, 0x50, 0x80, 0x12, 0xdf, 0xfc, - 0x21, 0x1a, 0xce, 0x0f, 0x75, 0xc0, 0x3f, 0x8f, 0x7a, 0x11, 0x80, 0x88, - 0x20, 0x11, 0x20, 0x04, 0x52, 0x03, 0xd1, 0x8a, 0x74, 0x62, 0x47, 0xc0, - 0x00, 0x07, 0x4f, 0xbd, 0x7a, 0x52, 0x85, 0x76, 0xe6, 0xaa, 0x80, 0xa3, - 0xb1, 0xb0, 0x48, 0x02, 0xe2, 0x00, 0x05, 0x62, 0x4a, 0x0b, 0x00, 0x02, - 0x82, 0x81, 0x77, 0x11, 0x54, 0xac, 0x02, 0xb5, 0xa0, 0x00, 0x01, 0xaa, - 0x67, 0xaf, 0x9c, 0xad, 0x41, 0xb7, 0x06, 0xf5, 0xdc, 0x51, 0x96, 0x9f, - 0xc6, 0x9c, 0x66, 0xc9, 0xde, 0x8f, 0x42, 0x30, 0x20, 0xb8, 0x07, 0xa5, - 0x13, 0xe8, 0x84, 0xaf, 0x8e, 0x78, 0x00, 0x6f, 0x43, 0xaf, 0xbc, 0xdd, - 0x7b, 0xa8, 0x54, 0xcc, 0x95, 0x40, 0xc1, 0x30, 0x4c, 0x0b, 0x00, 0x0a, - 0x20, 0x05, 0x00, 0x82, 0x42, 0xa1, 0x12, 0xc6, 0x70, 0x2c, 0x13, 0x00, - 0x00, 0x32, 0x88, 0x0e, 0xea, 0x21, 0x35, 0xd3, 0xd5, 0x0c, 0x8d, 0xcd, - 0x73, 0xc1, 0xc3, 0xb7, 0xa3, 0xbf, 0xa7, 0x6c, 0x11, 0xd1, 0xc0, 0xff, - 0xf1, 0x50, 0x80, 0x14, 0x1f, 0xfc, 0x21, 0x1a, 0xcc, 0x91, 0xff, 0x80, - 0xff, 0x1f, 0x76, 0x12, 0x00, 0x84, 0x20, 0x31, 0x50, 0x13, 0x46, 0x28, - 0xd1, 0x88, 0xc8, 0x4e, 0x2a, 0x00, 0x07, 0x87, 0x3b, 0xf1, 0x4a, 0x29, - 0xeb, 0x85, 0x53, 0x01, 0x3e, 0xaa, 0x00, 0x02, 0x00, 0x03, 0x78, 0x00, - 0x05, 0xd5, 0xa0, 0x2c, 0x03, 0x02, 0xe5, 0x91, 0x81, 0x32, 0xf4, 0x36, - 0x45, 0x1c, 0xe0, 0x05, 0x4b, 0xab, 0x4a, 0x54, 0xc0, 0xcd, 0x3d, 0x5b, - 0x5f, 0x66, 0xce, 0xa3, 0x9a, 0xe5, 0xb5, 0x49, 0xe0, 0xf2, 0x99, 0x14, - 0xbb, 0x08, 0x40, 0x42, 0x40, 0x18, 0xb0, 0x05, 0xa3, 0x14, 0xe8, 0x84, - 0xae, 0x00, 0x00, 0x3c, 0x39, 0xdf, 0x8a, 0x51, 0x46, 0xea, 0xa9, 0x81, - 0x48, 0x40, 0x98, 0x4c, 0x20, 0x00, 0x01, 0x00, 0x80, 0x02, 0x4d, 0xc4, - 0xa1, 0x98, 0x01, 0x33, 0x9e, 0x95, 0x60, 0xcf, 0x64, 0x63, 0x36, 0xf4, - 0x40, 0x00, 0xbd, 0x4e, 0x3a, 0x28, 0x0b, 0x0b, 0x89, 0xd9, 0x1d, 0xab, - 0x49, 0xec, 0xaf, 0xf8, 0x6a, 0xad, 0x7e, 0xde, 0x29, 0xf5, 0x29, 0xb5, - 0xdf, 0xa3, 0x80, 0xff, 0xf1, 0x50, 0x80, 0x10, 0x7f, 0xfc, 0x21, 0x1a, - 0xc8, 0x34, 0xa7, 0x60, 0x30, 0x0f, 0x75, 0x10, 0x80, 0xc4, 0x60, 0x11, - 0x08, 0x04, 0x58, 0x02, 0xd2, 0x09, 0xb4, 0x82, 0x56, 0x85, 0x40, 0x65, - 0x87, 0x8f, 0x79, 0xbf, 0x9a, 0x51, 0xdd, 0xdb, 0x00, 0x4a, 0x81, 0x60, - 0x00, 0x00, 0x00, 0x26, 0x00, 0x00, 0x09, 0x95, 0x10, 0x02, 0x82, 0x71, - 0x98, 0x02, 0xe0, 0x07, 0x70, 0xb2, 0x73, 0xbf, 0x39, 0xee, 0xf9, 0xf9, - 0xa2, 0x87, 0xd6, 0xd1, 0xf0, 0x3b, 0x08, 0x80, 0x22, 0x10, 0x08, 0xc4, - 0x05, 0xa5, 0x11, 0x6a, 0x04, 0x89, 0x5c, 0x07, 0xaf, 0x6f, 0x1e, 0x77, - 0xa6, 0x5d, 0x43, 0xbf, 0xbd, 0xb9, 0xfb, 0x39, 0xa4, 0x92, 0xfb, 0xd8, - 0x08, 0xca, 0xc2, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x23, 0x21, 0x1a, - 0x20, 0xd7, 0xb5, 0x61, 0x91, 0x42, 0x3b, 0xb6, 0x6f, 0x85, 0xf6, 0x7b, - 0x7c, 0x1c, 0xff, 0xf1, 0x50, 0x80, 0x12, 0x1f, 0xfc, 0x21, 0x1a, 0xc8, - 0x00, 0x67, 0xc0, 0x7d, 0x9f, 0x72, 0x00, 0x88, 0x40, 0x82, 0x10, 0x28, - 0xa8, 0x05, 0xa3, 0x14, 0xe8, 0x84, 0xa6, 0xf5, 0x96, 0x03, 0xd3, 0x9d, - 0xfa, 0xa5, 0x14, 0x72, 0xaa, 0xa0, 0x52, 0xeb, 0x95, 0xaa, 0xa0, 0x00, - 0x40, 0x0a, 0x82, 0x40, 0x29, 0x6d, 0xa9, 0x00, 0x14, 0x0b, 0x00, 0x25, - 0x42, 0xd4, 0xb8, 0x58, 0x00, 0x09, 0x08, 0x22, 0x42, 0xc8, 0x69, 0x15, - 0x84, 0x75, 0x16, 0xfb, 0xc7, 0x96, 0x27, 0x2d, 0xca, 0x5a, 0x7f, 0x58, - 0xb9, 0x00, 0x44, 0x20, 0xa1, 0x48, 0x0f, 0x48, 0x25, 0xd0, 0x88, 0xf4, - 0x22, 0x5e, 0xef, 0x4e, 0x78, 0x07, 0xae, 0x77, 0x5e, 0xea, 0x3b, 0xa5, - 0x5a, 0xa8, 0x17, 0x0b, 0x80, 0x50, 0x00, 0x13, 0x00, 0x00, 0x14, 0x00, - 0x5c, 0x01, 0x54, 0x80, 0x2c, 0x01, 0x60, 0x12, 0xa9, 0x35, 0xff, 0x4e, - 0x55, 0x52, 0x53, 0x5f, 0x6c, 0xac, 0x8e, 0x3e, 0x85, 0xba, 0xe4, 0xe1, - 0xf0, 0xe0, 0xff, 0xf1, 0x50, 0x80, 0x11, 0x7f, 0xfc, 0x21, 0x1a, 0xca, - 0x09, 0xbb, 0x40, 0x1e, 0x0f, 0x72, 0x00, 0x88, 0x40, 0xa4, 0x10, 0x08, - 0xc3, 0x46, 0x26, 0xd1, 0x88, 0x74, 0x22, 0x4d, 0xe8, 0x54, 0x0c, 0x58, - 0x3e, 0x37, 0xbf, 0x9a, 0x51, 0xdf, 0x0a, 0xe6, 0x80, 0x9d, 0x31, 0x12, - 0x44, 0x04, 0x00, 0x00, 0x00, 0x01, 0x40, 0x50, 0x14, 0x12, 0x00, 0x00, - 0x01, 0x31, 0x30, 0x02, 0xa0, 0x5d, 0x51, 0x6a, 0xab, 0x28, 0xa3, 0x8f, - 0xf2, 0xc1, 0xb7, 0xab, 0xa4, 0x3e, 0xbb, 0xf3, 0xe0, 0x7a, 0x11, 0x80, - 0x84, 0x20, 0x11, 0x10, 0x04, 0x58, 0x02, 0xd3, 0x09, 0x74, 0x82, 0x47, - 0x5a, 0x07, 0xaf, 0x63, 0xc7, 0x9c, 0xce, 0x0e, 0xbe, 0x79, 0xad, 0xe7, - 0xda, 0x84, 0x99, 0x98, 0x01, 0x29, 0xa6, 0x00, 0x00, 0x00, 0x44, 0x00, - 0xe2, 0x00, 0x00, 0x80, 0x01, 0x30, 0x00, 0x90, 0x53, 0x7f, 0xe1, 0xf3, - 0x77, 0xae, 0xd4, 0x7e, 0x9d, 0xf5, 0x98, 0x7e, 0x8e, 0xff, 0xf1, 0x50, - 0x80, 0x14, 0xbf, 0xfc, 0x21, 0x1a, 0xcc, 0x21, 0xc7, 0x80, 0x5e, 0x8f, - 0x73, 0x10, 0x80, 0xc8, 0x40, 0x41, 0x60, 0x0b, 0x48, 0x21, 0xd0, 0x89, - 0x34, 0x22, 0x1d, 0x11, 0x95, 0x0a, 0x80, 0x07, 0x7f, 0xcd, 0x73, 0xeb, - 0x9a, 0x50, 0xe3, 0x7c, 0xda, 0xa8, 0x10, 0x40, 0x07, 0x65, 0x80, 0x00, - 0x2a, 0x58, 0x59, 0x29, 0x74, 0xa8, 0x2e, 0x17, 0x01, 0x59, 0x93, 0x00, - 0x04, 0xc0, 0xe3, 0x35, 0xa7, 0x0a, 0xae, 0x4b, 0x0e, 0xd5, 0xb6, 0x24, - 0xd4, 0xe3, 0xac, 0xce, 0x7f, 0x31, 0xeb, 0x57, 0xe8, 0x1c, 0xab, 0xb9, - 0x91, 0x00, 0x22, 0x10, 0x10, 0xb0, 0x09, 0xa2, 0x15, 0xe8, 0x44, 0xa9, - 0x50, 0x2a, 0x00, 0x7a, 0x6f, 0xc2, 0x94, 0xa0, 0xf5, 0x54, 0xaa, 0x18, - 0x29, 0xb4, 0x27, 0x54, 0x17, 0x00, 0x2a, 0x00, 0x2d, 0x51, 0x74, 0x00, - 0x18, 0x96, 0x7d, 0xfa, 0x55, 0x20, 0x17, 0x56, 0xe0, 0xe4, 0x08, 0x85, - 0x17, 0x9c, 0x82, 0x7a, 0xf4, 0x80, 0x04, 0x80, 0x08, 0x54, 0x2e, 0xa5, - 0xcc, 0xf9, 0x34, 0xb2, 0x31, 0x4b, 0xf3, 0x6d, 0xfd, 0xb1, 0xef, 0xaf, - 0xc3, 0xe0, 0xe9, 0x2f, 0xc3, 0x80, 0xff, 0xf1, 0x50, 0x80, 0x12, 0xbf, - 0xfc, 0x21, 0x1a, 0xca, 0xb4, 0x27, 0x00, 0x00, 0x0f, 0x74, 0x12, 0x00, - 0x45, 0x20, 0x11, 0x20, 0x17, 0x46, 0x29, 0xd1, 0x09, 0x49, 0x00, 0xa8, - 0x2a, 0x3c, 0x7b, 0xdf, 0xa5, 0x28, 0x36, 0xc5, 0x28, 0x67, 0x62, 0x15, - 0x15, 0xa3, 0x82, 0x4b, 0x00, 0x00, 0x93, 0xdf, 0x35, 0xa3, 0xd0, 0xb6, - 0x11, 0x81, 0xe7, 0xb6, 0x5c, 0x9e, 0xa0, 0xa4, 0x13, 0x2f, 0x5e, 0x40, - 0xd8, 0x55, 0x5d, 0xf7, 0xdf, 0x69, 0xc6, 0xf7, 0x00, 0x49, 0x70, 0xd7, - 0x90, 0x12, 0x33, 0xe4, 0x9a, 0x3b, 0x4a, 0x45, 0x9c, 0x8c, 0x6a, 0x56, - 0x16, 0xf2, 0xd1, 0xe0, 0x44, 0x01, 0x10, 0x80, 0x44, 0x40, 0x11, 0x10, - 0x0c, 0x4c, 0x05, 0xd7, 0x91, 0x1c, 0x58, 0xa9, 0xcd, 0x75, 0x51, 0xdb, - 0xe3, 0x37, 0x5b, 0xc9, 0x52, 0x34, 0xdf, 0xe6, 0x94, 0x0a, 0x20, 0x88, - 0x00, 0x01, 0x67, 0x09, 0x35, 0x74, 0xab, 0x88, 0x61, 0x06, 0xc7, 0xb1, - 0x90, 0x4c, 0xa8, 0x93, 0x7f, 0x7a, 0x0f, 0x26, 0x37, 0x2b, 0x80, 0xff, - 0xf1, 0x50, 0x80, 0x12, 0x9f, 0xfc, 0x21, 0x1a, 0xca, 0x21, 0x35, 0x40, - 0x0e, 0x0f, 0x72, 0x00, 0x88, 0x40, 0x62, 0x30, 0x10, 0x84, 0x04, 0x28, - 0x01, 0xe8, 0x84, 0x3a, 0x11, 0x3e, 0x8c, 0x4a, 0x15, 0x06, 0xe2, 0xde, - 0x5e, 0xbd, 0x73, 0x54, 0xa1, 0xbb, 0xcc, 0x50, 0x2c, 0x94, 0x0b, 0x00, - 0x02, 0xa0, 0x26, 0x17, 0x13, 0x2c, 0x48, 0xb3, 0x89, 0x19, 0xc8, 0x05, - 0x02, 0x62, 0x16, 0x04, 0x53, 0x03, 0xb8, 0xa9, 0x11, 0x78, 0x3d, 0xd6, - 0x26, 0x44, 0x8d, 0x9b, 0xba, 0xd1, 0x2c, 0xb0, 0xc9, 0xd5, 0x9a, 0xdf, - 0x53, 0x90, 0x04, 0x42, 0x02, 0x11, 0x00, 0xc4, 0xc0, 0x11, 0x28, 0x0f, - 0x4e, 0x23, 0xd2, 0x88, 0x88, 0x43, 0x25, 0x73, 0xe4, 0xaa, 0x5b, 0xcf, - 0xbe, 0x6b, 0x37, 0x5f, 0x62, 0xaf, 0x8c, 0xde, 0x50, 0x10, 0x41, 0x6b, - 0x80, 0x00, 0x00, 0x09, 0x91, 0x81, 0x51, 0x40, 0x02, 0x49, 0x49, 0x05, - 0xba, 0x2c, 0x68, 0x08, 0x1f, 0x5c, 0x36, 0x33, 0x57, 0x91, 0x4b, 0x1f, - 0x67, 0xd3, 0x80, 0xff, 0xf1, 0x50, 0x80, 0x13, 0xbf, 0xfc, 0x21, 0x1a, - 0xca, 0x05, 0x77, 0xc0, 0x7f, 0x1f, 0x72, 0x00, 0x90, 0x40, 0x82, 0x10, - 0x08, 0x84, 0x02, 0x2b, 0x01, 0xe8, 0x85, 0x3a, 0x31, 0x19, 0x08, 0x00, - 0x2a, 0x3c, 0xfc, 0x78, 0xa5, 0x28, 0x7b, 0xea, 0xb1, 0x54, 0x05, 0x70, - 0x0a, 0xa6, 0x00, 0x00, 0x2c, 0x01, 0x65, 0xc0, 0x00, 0x12, 0xb0, 0xa0, - 0xec, 0x4e, 0x92, 0x46, 0x35, 0x28, 0x4e, 0xc5, 0xc2, 0x4b, 0x80, 0x1c, - 0x12, 0xa5, 0x3b, 0xd3, 0x42, 0xed, 0x43, 0xcb, 0x2e, 0xbb, 0xc1, 0x8a, - 0x07, 0x1f, 0x91, 0xa9, 0x0e, 0xc7, 0x20, 0x08, 0x84, 0x04, 0x42, 0x00, - 0x90, 0x80, 0x22, 0xb0, 0x1e, 0x90, 0x51, 0xa1, 0x13, 0x00, 0x01, 0x51, - 0xeb, 0xbd, 0xe7, 0xaa, 0x50, 0x55, 0x52, 0xa8, 0x10, 0x62, 0xc2, 0x0a, - 0x17, 0x8a, 0x8a, 0x80, 0x00, 0x00, 0x49, 0x42, 0xea, 0xc8, 0x02, 0xe8, - 0xdc, 0xa7, 0x75, 0xe6, 0x02, 0x33, 0x01, 0x10, 0x00, 0x48, 0x05, 0x42, - 0x04, 0xe0, 0x9a, 0x63, 0x65, 0x10, 0x52, 0x1d, 0x36, 0x72, 0x4a, 0x73, - 0x87, 0x8b, 0xe3, 0xc0, 0xff, 0xf1, 0x50, 0x80, 0x13, 0xdf, 0xfc, 0x21, - 0x1a, 0xc9, 0x19, 0x6d, 0xe0, 0x7f, 0x1f, 0x72, 0x01, 0x08, 0x40, 0x62, - 0x30, 0x08, 0xb0, 0x05, 0xa1, 0x10, 0xe8, 0x45, 0x3a, 0x21, 0x28, 0x00, - 0x03, 0xcf, 0x3d, 0x73, 0x54, 0x50, 0xe6, 0xaa, 0xaa, 0x80, 0x44, 0x00, - 0x49, 0x75, 0x00, 0x00, 0x05, 0x85, 0x40, 0x2c, 0x02, 0x4c, 0xf6, 0x15, - 0x2a, 0x79, 0xd6, 0x50, 0x05, 0xec, 0x89, 0x30, 0x02, 0x0b, 0x1b, 0x40, - 0x13, 0x59, 0x43, 0x84, 0x25, 0x51, 0x69, 0x78, 0xf7, 0xc6, 0xb9, 0x56, - 0x6d, 0xcc, 0xf4, 0xf5, 0x2d, 0x57, 0x20, 0x10, 0x84, 0x06, 0x22, 0x01, - 0x0b, 0x00, 0x5a, 0x41, 0x3e, 0x8c, 0x48, 0x81, 0x0c, 0xb6, 0x5b, 0xbe, - 0x83, 0xcf, 0x9e, 0x73, 0xd5, 0x14, 0x6e, 0xea, 0xaa, 0xa8, 0x00, 0xb0, - 0x50, 0x00, 0x00, 0x01, 0x28, 0x0e, 0xe0, 0xe7, 0x43, 0x71, 0x75, 0xb4, - 0x8a, 0xec, 0x26, 0x2b, 0x87, 0x68, 0x86, 0x37, 0x24, 0x05, 0xec, 0x01, - 0x28, 0x81, 0x27, 0x01, 0x2e, 0x16, 0xdf, 0x1e, 0x8c, 0xf4, 0x7a, 0x58, - 0x5a, 0xc5, 0x84, 0x21, 0xc1, 0x3f, 0xff, 0xf1, 0x50, 0x80, 0x13, 0x5f, - 0xfc, 0x21, 0x1a, 0xc8, 0x09, 0xf6, 0x00, 0x26, 0x0f, 0x72, 0x01, 0x08, - 0x80, 0x62, 0xd0, 0x26, 0x8c, 0x55, 0xa1, 0x12, 0x80, 0x01, 0x4f, 0x4e, - 0xf3, 0xba, 0x52, 0x83, 0x75, 0x4c, 0x0a, 0xd2, 0x7f, 0x39, 0x00, 0xb8, - 0x80, 0x01, 0x70, 0x5e, 0x60, 0xd9, 0x7e, 0x04, 0xe6, 0x9b, 0x57, 0x68, - 0xc6, 0xd2, 0x71, 0x6e, 0xa6, 0x98, 0x76, 0x4e, 0x16, 0xb0, 0x2d, 0xa1, - 0x18, 0x5e, 0x6c, 0xb2, 0x85, 0xc5, 0x80, 0x5e, 0x80, 0x04, 0x6c, 0x11, - 0x88, 0x8c, 0x8a, 0x7a, 0xa7, 0xe2, 0xfb, 0x70, 0xdb, 0x1d, 0x15, 0xde, - 0xaa, 0x6c, 0xce, 0x31, 0xf0, 0xd9, 0xc8, 0x0c, 0x2c, 0x03, 0x69, 0x44, - 0x3a, 0x21, 0x16, 0x94, 0x46, 0x81, 0x00, 0x6f, 0x47, 0x9f, 0x8c, 0xde, - 0xfd, 0x33, 0x0b, 0xd7, 0x35, 0x8c, 0x0b, 0x02, 0x44, 0xc5, 0x80, 0x55, - 0x20, 0x15, 0x5e, 0xc5, 0x01, 0xbd, 0x50, 0xcf, 0x83, 0x91, 0xd6, 0xf3, - 0xe7, 0xae, 0xef, 0xe3, 0xb7, 0x75, 0x25, 0xc2, 0xde, 0x53, 0x78, 0xda, - 0xe9, 0x67, 0x29, 0xc0, 0xff, 0xf1, 0x50, 0x80, 0x11, 0x1f, 0xfc, 0x21, - 0x1a, 0xc8, 0x30, 0x10, 0x80, 0x10, 0x0f, 0x72, 0x01, 0x08, 0x40, 0x42, - 0x20, 0x08, 0x88, 0x08, 0x26, 0x02, 0x69, 0x84, 0x5a, 0x61, 0x19, 0x0a, - 0xa1, 0x97, 0x49, 0xcf, 0x0f, 0x1f, 0x1c, 0xd6, 0x77, 0xf6, 0xda, 0xae, - 0x5e, 0x00, 0x54, 0x94, 0x80, 0x00, 0x01, 0x70, 0xa0, 0x02, 0x70, 0x00, - 0x0b, 0xd8, 0x92, 0xda, 0x39, 0x2e, 0xb8, 0x38, 0x2b, 0x9d, 0x45, 0xbc, - 0xf5, 0x77, 0x39, 0x01, 0x04, 0x60, 0x31, 0x60, 0x0b, 0x48, 0x27, 0xd2, - 0x09, 0x2a, 0x32, 0xc0, 0x07, 0x9f, 0x8d, 0xd7, 0xca, 0x94, 0x73, 0x73, - 0xbc, 0x02, 0x02, 0xf2, 0x20, 0x98, 0x88, 0x00, 0x04, 0xc1, 0x70, 0x32, - 0x90, 0x4c, 0x5c, 0xbd, 0x32, 0x96, 0xd0, 0x73, 0xb2, 0x53, 0x6a, 0x5e, - 0xc8, 0x80, 0xd0, 0x00, 0x5e, 0x23, 0xbd, 0x74, 0x3d, 0x16, 0xdb, 0x9d, - 0x1c, 0xcc, 0x79, 0x21, 0xf8, 0xf0, 0x72, 0xb7, 0xff, 0xf1, 0x50, 0x80, - 0x12, 0xdf, 0xfc, 0x21, 0x1a, 0xc8, 0x00, 0x1c, 0x40, 0x77, 0x07, 0x72, - 0x03, 0x08, 0x40, 0xe2, 0x70, 0x1e, 0x90, 0x4f, 0xa4, 0x11, 0x10, 0x83, - 0x9e, 0x07, 0x97, 0x7b, 0xe7, 0xd5, 0x39, 0xd1, 0x93, 0x75, 0xcc, 0xa0, - 0xaa, 0xc0, 0x00, 0x0d, 0xe0, 0x02, 0xe4, 0x4b, 0x10, 0x8f, 0x04, 0x80, - 0x25, 0x65, 0x96, 0x44, 0x92, 0x80, 0x05, 0x04, 0x69, 0x9c, 0x32, 0x74, - 0x27, 0x4f, 0x31, 0x26, 0x67, 0x54, 0x9d, 0xd3, 0x93, 0xe3, 0x07, 0x20, - 0x38, 0x84, 0x06, 0x2b, 0x01, 0x68, 0x84, 0x3a, 0x11, 0x46, 0x88, 0x4a, - 0xde, 0xb2, 0xca, 0xab, 0x1e, 0x7d, 0xf8, 0xee, 0xa9, 0x42, 0xaa, 0xa9, - 0x54, 0x15, 0x89, 0x15, 0x40, 0x5c, 0x48, 0xb8, 0x40, 0x14, 0x08, 0xae, - 0x05, 0x40, 0x11, 0x0b, 0x34, 0x46, 0xf5, 0x41, 0x58, 0x84, 0xc0, 0x44, - 0x59, 0x34, 0x40, 0x5c, 0xd9, 0x7a, 0x91, 0x4f, 0xbf, 0x39, 0xe8, 0xac, - 0x27, 0x6c, 0x72, 0x9e, 0x64, 0x94, 0xd3, 0xc1, 0x7c, 0x11, 0x97, 0xb7, - 0x13, 0x8f, 0xff, 0xf1, 0x50, 0x80, 0x14, 0x5f, 0xfc, 0x21, 0x1a, 0xc8, - 0x04, 0xfc, 0xc0, 0xff, 0x1f, 0x72, 0x00, 0x90, 0x80, 0x82, 0xf0, 0x16, - 0x8c, 0x53, 0xa1, 0x13, 0x00, 0x00, 0x1e, 0x7e, 0x2b, 0xc5, 0x29, 0x43, - 0xba, 0x53, 0x02, 0x07, 0x20, 0xa2, 0x80, 0x02, 0x45, 0x40, 0x17, 0x00, - 0x01, 0x61, 0x01, 0x02, 0xd1, 0xa2, 0xf0, 0x8f, 0x56, 0x49, 0xc5, 0xf4, - 0x88, 0xea, 0x44, 0x00, 0x80, 0x12, 0x13, 0x63, 0x00, 0x90, 0x25, 0xdc, - 0xb0, 0x84, 0xef, 0x4a, 0xdb, 0x95, 0x37, 0x72, 0x4f, 0x9f, 0x86, 0xdb, - 0x29, 0x23, 0x67, 0x0f, 0x67, 0x6d, 0x7a, 0xbe, 0xce, 0x40, 0x41, 0x08, - 0x0c, 0x42, 0x02, 0x15, 0x00, 0xf4, 0x62, 0x8d, 0x18, 0x94, 0x02, 0xaa, - 0xcf, 0x2e, 0xf9, 0xf1, 0x4a, 0x51, 0xe3, 0x55, 0x54, 0xc0, 0xa8, 0x13, - 0x04, 0x00, 0x00, 0x88, 0x2e, 0x0b, 0x34, 0x98, 0x88, 0x09, 0x15, 0x42, - 0xb3, 0x5a, 0x53, 0xb1, 0x24, 0x82, 0x89, 0x14, 0x17, 0xaa, 0x22, 0x53, - 0x98, 0x6c, 0x03, 0x16, 0x95, 0x62, 0xe7, 0x3d, 0x90, 0xac, 0xe1, 0xd1, - 0xa6, 0xef, 0xfd, 0xbe, 0xf6, 0x87, 0xa3, 0x80, 0xff, 0xf1, 0x50, 0x80, - 0x15, 0x5f, 0xfc, 0x21, 0x1a, 0xc8, 0x01, 0x87, 0x80, 0x08, 0x1f, 0x72, - 0x01, 0x88, 0x80, 0x42, 0x10, 0x10, 0x88, 0x04, 0x2b, 0xd1, 0x0a, 0x74, - 0x22, 0x42, 0x10, 0x01, 0x50, 0x1f, 0x1b, 0xf0, 0xa2, 0x86, 0xea, 0x94, - 0xc1, 0x4b, 0xc4, 0x2e, 0xb0, 0x00, 0x05, 0xc0, 0x00, 0xea, 0xea, 0xa5, - 0x20, 0x16, 0xe9, 0xac, 0x8d, 0x59, 0xeb, 0x9a, 0x5b, 0xe6, 0xe0, 0xfb, - 0x45, 0x7b, 0x1c, 0x4e, 0x16, 0xd5, 0x6f, 0x57, 0x8d, 0xbb, 0x7c, 0x35, - 0x65, 0xa5, 0x8c, 0xd5, 0xe7, 0xce, 0x04, 0x9c, 0xbe, 0x45, 0xe2, 0xb0, - 0xb0, 0x04, 0x85, 0xa8, 0x60, 0xb6, 0xe9, 0x6f, 0x63, 0xb2, 0x3f, 0x4d, - 0x17, 0x44, 0xd9, 0x26, 0x30, 0xd9, 0xe3, 0x3d, 0x41, 0x91, 0x6d, 0x9d, - 0xa7, 0x20, 0x50, 0xa8, 0x09, 0xa7, 0x11, 0xe9, 0x04, 0xa7, 0x8d, 0xdd, - 0xf4, 0x53, 0xd3, 0x9d, 0xe6, 0xf2, 0xbc, 0x78, 0x2e, 0xda, 0x53, 0x00, - 0x00, 0x02, 0x00, 0x2b, 0xd2, 0x5c, 0x52, 0x01, 0x60, 0x90, 0x04, 0x88, - 0xe1, 0x91, 0x4a, 0xe0, 0x87, 0x29, 0xca, 0xdd, 0x9b, 0x0d, 0x17, 0xac, - 0x23, 0xfd, 0xfa, 0xeb, 0xa7, 0xe9, 0xb2, 0x33, 0xef, 0xc0, 0xff, 0xf1, - 0x50, 0x80, 0x0e, 0x1f, 0xfc, 0x21, 0x1a, 0xc8, 0x02, 0x00, 0x00, 0x22, - 0x0f, 0x72, 0x04, 0x88, 0x41, 0xda, 0x51, 0x0e, 0x8c, 0x43, 0xa5, 0x12, - 0x1e, 0x9d, 0xe6, 0x77, 0xf6, 0xa5, 0x55, 0xd7, 0x59, 0x98, 0x04, 0x80, - 0x0b, 0xd0, 0x02, 0x68, 0xc0, 0x15, 0xcf, 0x1d, 0x77, 0x9e, 0x2e, 0x90, - 0xf1, 0x56, 0xf9, 0x21, 0x6f, 0x75, 0xe4, 0x72, 0x01, 0x08, 0x40, 0x22, - 0x10, 0x20, 0x90, 0x16, 0x21, 0xd1, 0x89, 0xb4, 0xa2, 0x40, 0x0f, 0x7e, - 0xf3, 0xec, 0xa1, 0xcc, 0xeb, 0x9d, 0xe0, 0x16, 0x99, 0x48, 0xb6, 0x82, - 0x20, 0x98, 0x04, 0x40, 0x80, 0x52, 0x62, 0x6c, 0xb3, 0x14, 0x49, 0x60, - 0x00, 0x40, 0x21, 0x72, 0x12, 0xe5, 0x4e, 0xbd, 0xab, 0xda, 0x3f, 0xad, - 0xb4, 0x1c, 0xff, 0xf1, 0x50, 0x80, 0x10, 0x5f, 0xfc, 0x21, 0x1a, 0xc8, - 0x00, 0x9d, 0x40, 0x12, 0x1f, 0x72, 0x05, 0x8a, 0x80, 0x7a, 0x61, 0x0e, - 0x88, 0x43, 0xa4, 0x12, 0xaa, 0x19, 0xbd, 0x6f, 0xa7, 0xa7, 0x3b, 0xca, - 0xdf, 0xdb, 0x14, 0xb9, 0x75, 0x40, 0x00, 0x00, 0x00, 0x09, 0xa2, 0x00, - 0x00, 0x48, 0x2a, 0xa6, 0x52, 0x55, 0x9d, 0x33, 0x51, 0x5e, 0xd2, 0x60, - 0xdb, 0x29, 0x41, 0xed, 0xd3, 0xf8, 0x1c, 0x80, 0x62, 0x30, 0x10, 0x84, - 0x02, 0x2b, 0x01, 0xe9, 0x04, 0xfa, 0x31, 0x28, 0x01, 0x99, 0x75, 0xa7, - 0x9f, 0x6f, 0x1f, 0x2a, 0x29, 0xb9, 0x2a, 0x80, 0xa4, 0xef, 0x17, 0x10, - 0x00, 0x00, 0x00, 0x54, 0x54, 0x50, 0x00, 0x25, 0x44, 0x13, 0xa3, 0x4c, - 0x8c, 0x60, 0x48, 0x46, 0x05, 0x00, 0x22, 0x00, 0xbc, 0xeb, 0xa6, 0x8b, - 0x6f, 0xfe, 0xaa, 0xfc, 0xda, 0xc9, 0xfe, 0x50, 0x9c, 0x73, 0xcf, 0x80, - 0xff, 0xf1, 0x50, 0x80, 0x13, 0x9f, 0xfc, 0x21, 0x1a, 0xc8, 0x0c, 0xfd, - 0x40, 0x7f, 0x1f, 0x72, 0x01, 0x09, 0x00, 0x22, 0xf0, 0x16, 0x90, 0x51, - 0xa2, 0x12, 0x8a, 0x85, 0x40, 0x6f, 0x41, 0xe5, 0xe3, 0x39, 0xf5, 0x4a, - 0x15, 0x2a, 0x95, 0x41, 0x2a, 0xd6, 0x95, 0xb2, 0x60, 0x02, 0x80, 0xa8, - 0x0b, 0x04, 0x86, 0xf0, 0x22, 0x12, 0x89, 0x52, 0x2b, 0x22, 0x53, 0xc6, - 0x2d, 0x10, 0x9b, 0x60, 0x01, 0x51, 0x00, 0x05, 0x49, 0x25, 0xf1, 0x81, - 0xc1, 0x7f, 0xde, 0xdb, 0xaf, 0x72, 0x4a, 0x78, 0x17, 0xd5, 0xab, 0x35, - 0x61, 0xfa, 0x6f, 0xe6, 0xe4, 0x01, 0x20, 0x81, 0x44, 0x40, 0x21, 0x50, - 0x0f, 0x44, 0x29, 0xd1, 0x09, 0x80, 0x06, 0x5b, 0xd3, 0xbf, 0x14, 0xa5, - 0x0f, 0x17, 0x54, 0xaa, 0x0c, 0x00, 0x54, 0x05, 0xac, 0x00, 0x09, 0x02, - 0xa0, 0x40, 0x06, 0xd2, 0xa2, 0x29, 0x58, 0x00, 0x13, 0x16, 0x89, 0x69, - 0x1a, 0xa2, 0x31, 0x80, 0x58, 0x01, 0x6a, 0x92, 0xaa, 0xb9, 0xa0, 0x97, - 0x75, 0x12, 0xe3, 0x3b, 0x5a, 0x93, 0x85, 0x3f, 0x95, 0x08, 0x79, 0xf8, - 0xff, 0xf1, 0x50, 0x80, 0x15, 0xbf, 0xfc, 0x21, 0x1a, 0xc8, 0x01, 0xbd, - 0xc0, 0x7f, 0x3f, 0x72, 0x04, 0x0b, 0x40, 0x7a, 0x21, 0x56, 0x84, 0x44, - 0x44, 0x00, 0x03, 0xcf, 0xbe, 0xe9, 0x45, 0x0f, 0x7a, 0xa5, 0x60, 0x46, - 0x00, 0x00, 0x5c, 0x00, 0x02, 0xe2, 0x40, 0x23, 0xc9, 0x6b, 0x81, 0x45, - 0x6f, 0xa2, 0xc6, 0xe9, 0x3a, 0x39, 0x6b, 0xfb, 0xaf, 0x52, 0xf6, 0x42, - 0xca, 0xa3, 0x23, 0xa9, 0x21, 0xb2, 0x77, 0xdc, 0x56, 0xa0, 0x00, 0x5a, - 0x2e, 0x06, 0x07, 0x8f, 0x4e, 0x24, 0xb7, 0x3f, 0x47, 0x3a, 0x1c, 0x93, - 0xdc, 0x21, 0x0a, 0xb6, 0x95, 0x10, 0xf4, 0xf9, 0xbe, 0x26, 0x72, 0x02, - 0x08, 0x40, 0x22, 0x20, 0x10, 0x88, 0x02, 0x29, 0x01, 0x69, 0x04, 0xfa, - 0x11, 0x39, 0x50, 0x1c, 0xf0, 0x1e, 0x7e, 0x33, 0x7e, 0x28, 0xa2, 0xb7, - 0x74, 0xac, 0x09, 0xd2, 0x40, 0x2a, 0x00, 0x01, 0x60, 0x05, 0xc0, 0x2a, - 0x54, 0xcf, 0x4b, 0xaf, 0x3d, 0x15, 0xbe, 0x8a, 0x49, 0xbe, 0x16, 0x80, - 0x30, 0x04, 0x66, 0x2e, 0x4c, 0x00, 0x01, 0x71, 0x05, 0x33, 0xf0, 0x74, - 0x94, 0xaa, 0x9b, 0xde, 0x4b, 0x24, 0xa6, 0x84, 0xff, 0xf7, 0x81, 0x7a, - 0x39, 0xed, 0xe5, 0xff, 0x78, 0xff, 0xf1, 0x50, 0x80, 0x11, 0xbf, 0xfc, - 0x21, 0x1a, 0xc8, 0x01, 0xdf, 0x80, 0x3f, 0x0f, 0x72, 0x01, 0x08, 0x80, - 0x22, 0x10, 0x08, 0x84, 0x04, 0x2c, 0x01, 0x68, 0xc5, 0x3a, 0x21, 0x28, - 0x02, 0xa6, 0x4a, 0xb3, 0xcf, 0x9d, 0xfa, 0xaa, 0x28, 0x76, 0xc5, 0x02, - 0x51, 0xa4, 0x80, 0x58, 0x00, 0x04, 0x41, 0x50, 0xd8, 0x02, 0xf4, 0x16, - 0x00, 0x21, 0x10, 0x12, 0x40, 0x54, 0x04, 0xc9, 0xd1, 0x70, 0x12, 0x05, - 0x0b, 0x2c, 0x0a, 0x10, 0x44, 0xa4, 0xef, 0x97, 0x0a, 0x15, 0x93, 0x9c, - 0x23, 0x9a, 0xad, 0x97, 0x8f, 0x77, 0x20, 0x08, 0x84, 0x0c, 0x2c, 0x02, - 0x69, 0x44, 0xda, 0x41, 0x26, 0xf4, 0x15, 0x2a, 0x64, 0xa7, 0x9f, 0x79, - 0xce, 0x7a, 0x28, 0x4a, 0xcc, 0xa0, 0x4f, 0xb8, 0x17, 0x00, 0x00, 0x00, - 0xa0, 0x03, 0x0a, 0xc0, 0x2c, 0x2c, 0x00, 0x00, 0x59, 0x56, 0xb0, 0x9d, - 0xd2, 0x8d, 0x24, 0xc3, 0x87, 0xb3, 0xf2, 0x23, 0xf4, 0xe5, 0xd7, 0x47, - 0xcf, 0x80, 0xff, 0xf1, 0x50, 0x80, 0x15, 0x1f, 0xfc, 0x21, 0x1a, 0xc8, - 0x00, 0x2d, 0x40, 0x0a, 0x1f, 0x72, 0x02, 0x08, 0x40, 0x63, 0xdd, 0x08, - 0x8c, 0x84, 0x00, 0x2a, 0x03, 0x37, 0x4a, 0x50, 0xa3, 0xfe, 0x94, 0xc1, - 0x6b, 0x82, 0x83, 0x38, 0x16, 0xb0, 0x00, 0x00, 0x01, 0x61, 0x00, 0xa8, - 0x05, 0x62, 0x5a, 0x09, 0x8c, 0x85, 0xe8, 0xad, 0xa9, 0xec, 0xbd, 0x27, - 0xb8, 0x06, 0xfc, 0x8d, 0x2e, 0x8c, 0x24, 0x3b, 0x1c, 0xa4, 0x68, 0xbc, - 0xa9, 0x17, 0x39, 0xc9, 0x5b, 0x13, 0x54, 0x01, 0x39, 0x2e, 0x3f, 0x78, - 0x49, 0x93, 0x64, 0x87, 0xb2, 0xf2, 0xc2, 0x55, 0x8b, 0xbc, 0xfd, 0x2d, - 0xa8, 0xe9, 0xd3, 0xcd, 0x43, 0x76, 0xe2, 0xe4, 0x03, 0x10, 0x81, 0xc5, - 0x40, 0x3d, 0x38, 0x87, 0x42, 0x21, 0xd2, 0x09, 0x6a, 0x77, 0xd7, 0xcf, - 0xd3, 0xbe, 0x33, 0x4f, 0x2f, 0x19, 0xbd, 0xe5, 0x7b, 0xd1, 0xae, 0x33, - 0x14, 0xc1, 0x14, 0x40, 0x01, 0x60, 0x54, 0x54, 0xed, 0x69, 0x56, 0x29, - 0xd2, 0xe0, 0x5a, 0xce, 0xc2, 0x77, 0x25, 0x59, 0xba, 0xf5, 0xbc, 0x23, - 0x3b, 0x32, 0xd2, 0x32, 0xdb, 0xf8, 0x74, 0x9f, 0xdf, 0xcb, 0xdb, 0x53, - 0xc3, 0xc0, 0xff, 0xf1, 0x50, 0x80, 0x19, 0x7f, 0xfc, 0x21, 0x1a, 0xc8, - 0x03, 0xa9, 0x80, 0x98, 0x1d, 0x72, 0x02, 0x10, 0x40, 0x22, 0xe0, 0x26, - 0x8c, 0x43, 0xa1, 0x12, 0xe9, 0x08, 0x88, 0x10, 0x1d, 0xf5, 0xbd, 0x56, - 0x68, 0xf4, 0xef, 0x39, 0xef, 0xc7, 0x0e, 0xeb, 0x77, 0xa7, 0x15, 0x5b, - 0x80, 0xb3, 0xac, 0x00, 0x05, 0x40, 0x11, 0x09, 0x00, 0x4a, 0x69, 0x74, - 0x79, 0xd8, 0xb9, 0x71, 0x37, 0x39, 0x78, 0xa8, 0xd2, 0xa9, 0x59, 0x00, - 0x01, 0x24, 0x04, 0xa1, 0x88, 0x75, 0x63, 0x15, 0x69, 0xac, 0x85, 0x64, - 0x2c, 0xb9, 0xb5, 0x29, 0x17, 0x96, 0x1e, 0xd2, 0x05, 0x3d, 0xe5, 0xfa, - 0xc6, 0x84, 0x39, 0x00, 0xc4, 0x60, 0x31, 0x60, 0x0f, 0x42, 0x2b, 0xd0, - 0x88, 0x88, 0x80, 0x00, 0x1e, 0x5b, 0xa5, 0x29, 0x43, 0xf4, 0xa5, 0x53, - 0x05, 0xda, 0x40, 0x16, 0xb8, 0x09, 0x03, 0xba, 0x61, 0x40, 0x42, 0x45, - 0xd8, 0x99, 0x5a, 0xb6, 0x57, 0x5e, 0x4b, 0xff, 0x8d, 0x56, 0xfe, 0xce, - 0xf6, 0xfc, 0x96, 0x71, 0xa3, 0x82, 0xb9, 0xa4, 0x8c, 0xdf, 0xd6, 0x5f, - 0xf5, 0x0b, 0x52, 0x10, 0xf4, 0x53, 0x54, 0x3d, 0xbc, 0xfa, 0x46, 0xba, - 0xfe, 0x6a, 0xc1, 0x3a, 0x33, 0xc0, 0x90, 0x2a, 0x0a, 0xda, 0x32, 0x97, - 0x2b, 0x02, 0xcf, 0xea, 0x2d, 0xad, 0x95, 0xe9, 0x1e, 0xc2, 0x72, 0x5f, - 0x67, 0x6e, 0x6d, 0xfc, 0x79, 0xff, 0x60, 0xc8, 0x5b, 0xfd, 0x7e, 0x49, - 0xe0, 0xff, 0xf1, 0x50, 0x80, 0x12, 0x9f, 0xfc, 0x21, 0x1a, 0xc8, 0x0b, - 0x9e, 0x80, 0xff, 0x1f, 0x72, 0x01, 0x88, 0x40, 0x62, 0xd0, 0x26, 0x88, - 0x53, 0xa3, 0x11, 0x90, 0x80, 0x06, 0x59, 0xe3, 0xe3, 0xc5, 0x52, 0x85, - 0x78, 0xaa, 0xaa, 0x50, 0x55, 0x30, 0x17, 0xa8, 0x4c, 0xb9, 0x20, 0x2c, - 0xb0, 0x00, 0x90, 0x1b, 0x43, 0x20, 0x50, 0x17, 0x2a, 0x2d, 0x28, 0x52, - 0x24, 0xa0, 0x15, 0x02, 0x20, 0x08, 0x15, 0x54, 0x71, 0xb5, 0x13, 0xdf, - 0xc3, 0x8b, 0x99, 0xb7, 0xa1, 0x8c, 0xb6, 0xca, 0xee, 0xf9, 0x6a, 0x75, - 0x10, 0x39, 0x01, 0xc4, 0xa0, 0x21, 0x40, 0x0b, 0x46, 0x28, 0xd1, 0x89, - 0x40, 0x32, 0xb4, 0x3c, 0xfb, 0xdf, 0xaa, 0x50, 0xaf, 0x5c, 0x55, 0x52, - 0x81, 0xac, 0x2e, 0x00, 0x00, 0x22, 0x00, 0x00, 0x13, 0x40, 0x3b, 0x21, - 0x22, 0x6b, 0xa5, 0x60, 0x4e, 0x42, 0x62, 0xd7, 0xb9, 0x60, 0x00, 0xb0, - 0x06, 0x32, 0x38, 0x72, 0x47, 0x15, 0xad, 0x6a, 0x66, 0x53, 0x05, 0xbe, - 0x73, 0x56, 0xf5, 0xfe, 0xbc, 0xff, 0xf1, 0x50, 0x80, 0x10, 0x1f, 0xfc, - 0x21, 0x1a, 0xc8, 0x01, 0x8b, 0x00, 0x7e, 0x0f, 0x72, 0x01, 0x90, 0x80, - 0x22, 0x30, 0x08, 0xb0, 0x05, 0xa4, 0x13, 0x68, 0x44, 0x3a, 0x21, 0x28, - 0x00, 0x54, 0xcb, 0x3c, 0xfb, 0xcd, 0xfc, 0xd2, 0x8f, 0x15, 0xc4, 0xa0, - 0x16, 0x9e, 0xcb, 0x00, 0x03, 0xa0, 0x00, 0x22, 0x01, 0x20, 0x00, 0x13, - 0x00, 0x00, 0x00, 0x2e, 0x00, 0x14, 0x01, 0x4a, 0x2c, 0xbb, 0x7c, 0xb8, - 0x4b, 0x08, 0x9b, 0x5c, 0x6b, 0x2e, 0xea, 0x1c, 0x80, 0xa2, 0x10, 0x11, - 0x04, 0x02, 0x29, 0x02, 0xe9, 0x04, 0xda, 0x51, 0x11, 0x08, 0x37, 0xa3, - 0xbe, 0x23, 0xcb, 0xd3, 0x7f, 0x34, 0xa3, 0xb7, 0x19, 0xce, 0x01, 0x5a, - 0x40, 0x1a, 0x80, 0x00, 0x00, 0x00, 0x04, 0xc0, 0x00, 0x04, 0x40, 0x02, - 0xa1, 0xcf, 0x0a, 0xb2, 0xc4, 0xc9, 0xc8, 0xde, 0x91, 0x31, 0x34, 0x55, - 0x4f, 0xff, 0xf1, 0x50, 0x80, 0x0e, 0x5f, 0xfc, 0x21, 0x1a, 0xc8, 0x00, - 0x1f, 0x40, 0x00, 0x0f, 0x72, 0x01, 0x88, 0x40, 0x22, 0x10, 0x40, 0x9c, - 0x07, 0xaa, 0x10, 0xe9, 0x04, 0x44, 0x20, 0x32, 0xb4, 0xf3, 0xe7, 0x9d, - 0xe5, 0x30, 0xfd, 0xae, 0xeb, 0x60, 0x25, 0x30, 0x00, 0x01, 0x20, 0x01, - 0x40, 0xbf, 0x03, 0x3c, 0x92, 0xbc, 0x24, 0x51, 0xd1, 0xbd, 0xca, 0xf5, - 0xb7, 0xf6, 0x3c, 0x3b, 0xdc, 0x81, 0xc2, 0x70, 0x1e, 0x98, 0x49, 0xa1, - 0x10, 0xe8, 0xc4, 0x81, 0x96, 0x79, 0xf7, 0xbc, 0xcc, 0xfc, 0xa9, 0x4e, - 0x1d, 0xd0, 0x00, 0x00, 0x00, 0x20, 0x19, 0x92, 0xa0, 0x15, 0x9c, 0xe0, - 0x05, 0x05, 0x80, 0x01, 0x10, 0x34, 0xef, 0x42, 0xfa, 0xf8, 0xe7, 0xf7, - 0xd2, 0x8f, 0xca, 0x3f, 0xff, 0x86, 0x0e, 0xff, 0xf1, 0x50, 0x80, 0x0f, - 0xbf, 0xfc, 0x21, 0x1a, 0xc8, 0x13, 0xec, 0x00, 0x6f, 0x0f, 0x72, 0x02, - 0x8c, 0x40, 0x5a, 0x31, 0x46, 0x90, 0x46, 0x81, 0x00, 0x0d, 0xa5, 0x9e, - 0x9c, 0xe7, 0xca, 0x8a, 0x3b, 0x95, 0x98, 0x04, 0x55, 0xa8, 0x22, 0x40, - 0x00, 0x00, 0x28, 0x13, 0x28, 0x00, 0x01, 0x70, 0x9a, 0x65, 0x40, 0x00, - 0x00, 0x00, 0x80, 0x88, 0x81, 0x22, 0x96, 0xa4, 0xad, 0x2d, 0xff, 0x0a, - 0xe3, 0x34, 0x23, 0xd3, 0xaa, 0xca, 0x47, 0x20, 0x30, 0x84, 0x04, 0x29, - 0x03, 0x69, 0x04, 0x5a, 0x11, 0x2e, 0x88, 0x4a, 0x00, 0x3d, 0x37, 0xde, - 0x7c, 0xd7, 0x2a, 0x1a, 0xac, 0x00, 0x54, 0x00, 0x05, 0xc0, 0x00, 0x00, - 0x00, 0x00, 0x02, 0xe2, 0x20, 0x00, 0xb0, 0x00, 0x94, 0x58, 0xfe, 0x37, - 0xb7, 0x06, 0x9b, 0x52, 0x90, 0x8c, 0x3f, 0xaf, 0xf7, 0xad, 0xbf, 0xcf, - 0xff, 0xf1, 0x50, 0x80, 0x11, 0xff, 0xfc, 0x21, 0x1a, 0xc8, 0x11, 0x9b, - 0x40, 0x7d, 0x0f, 0x72, 0x03, 0x08, 0x80, 0x22, 0xc0, 0x1e, 0x8c, 0x4d, - 0xa1, 0x10, 0xe8, 0xc4, 0xa0, 0x00, 0x3d, 0x79, 0xcf, 0x75, 0x29, 0xe9, - 0x71, 0x8a, 0x03, 0x54, 0x80, 0x00, 0x01, 0x10, 0x26, 0x26, 0x00, 0x00, - 0x09, 0xd0, 0x02, 0x95, 0x58, 0x26, 0x12, 0x05, 0x88, 0x88, 0x01, 0xc2, - 0xf5, 0x13, 0x89, 0xe9, 0xeb, 0x69, 0x46, 0xf2, 0xc5, 0xe4, 0x97, 0x58, - 0x7e, 0xae, 0x40, 0x31, 0x08, 0x04, 0x44, 0x01, 0x20, 0x80, 0x44, 0xc0, - 0x11, 0x20, 0x0f, 0x48, 0x27, 0xd2, 0x08, 0x90, 0x40, 0x73, 0xc0, 0x65, - 0xbd, 0x3b, 0xae, 0x7d, 0xe9, 0x4c, 0xbd, 0xdb, 0x79, 0x40, 0x95, 0x84, - 0x02, 0x40, 0x00, 0x00, 0x49, 0x70, 0xa8, 0x24, 0xb4, 0xc1, 0x60, 0x2e, - 0x02, 0x71, 0x00, 0x80, 0x48, 0x85, 0x5c, 0xdc, 0x49, 0x21, 0xa8, 0x2d, - 0x56, 0x96, 0x81, 0x8c, 0xaa, 0xf6, 0x60, 0xf7, 0xa1, 0x6d, 0xf8, 0xff, - 0xf1, 0x50, 0x80, 0x13, 0xbf, 0xfc, 0x21, 0x1a, 0xc8, 0x41, 0xf7, 0x80, - 0x7f, 0x0f, 0x72, 0x02, 0x08, 0x80, 0x44, 0x10, 0x08, 0xb0, 0x05, 0xa3, - 0x14, 0x68, 0xc4, 0xa0, 0x00, 0x1e, 0x3e, 0xb7, 0xe2, 0x94, 0xa3, 0xbb, - 0xac, 0x55, 0x02, 0x24, 0xb8, 0x02, 0x2b, 0x80, 0x10, 0x44, 0x3b, 0xb4, - 0x1d, 0x8a, 0x32, 0x80, 0xb4, 0x60, 0xb1, 0x06, 0xb7, 0x51, 0x6b, 0x01, - 0x6a, 0xd2, 0xd0, 0x48, 0x8c, 0xc1, 0x51, 0x00, 0x28, 0x53, 0x35, 0x86, - 0x95, 0x79, 0xa1, 0xc1, 0x53, 0xf5, 0x71, 0x97, 0xed, 0xee, 0xa7, 0x5e, - 0x0e, 0x3d, 0xac, 0xe8, 0x43, 0x00, 0x88, 0x40, 0x82, 0xc0, 0x1e, 0x90, - 0x4f, 0xa4, 0x12, 0x2c, 0x00, 0x18, 0xf1, 0xf5, 0x99, 0xda, 0x94, 0x6f, - 0x8d, 0xef, 0x2a, 0x80, 0xf1, 0x1d, 0x20, 0x20, 0x00, 0x0b, 0x10, 0x16, - 0x33, 0x00, 0xde, 0x58, 0x0a, 0xda, 0xe4, 0x95, 0xa8, 0xb5, 0x57, 0x13, - 0x0a, 0xd0, 0x13, 0xba, 0x67, 0x5a, 0x3b, 0x31, 0xbe, 0x2d, 0xd4, 0xfd, - 0xe9, 0x1b, 0x2f, 0xc1, 0xe0, 0xbe, 0xdc, 0x3c, 0xb9, 0x21, 0xcf, 0x80, - 0xff, 0xf1, 0x50, 0x80, 0x10, 0x1f, 0xfc, 0x21, 0x1a, 0xc8, 0x05, 0xaa, - 0x20, 0x06, 0x0f, 0x72, 0x03, 0x8b, 0xc0, 0x5a, 0x81, 0x16, 0x90, 0x4a, - 0x73, 0xc2, 0xa5, 0x4e, 0x78, 0xee, 0xeb, 0x87, 0xa7, 0x79, 0xbd, 0xe1, - 0xf9, 0x46, 0x98, 0x02, 0x60, 0x00, 0x00, 0x00, 0x00, 0xa8, 0x00, 0x05, - 0x84, 0x53, 0xbd, 0xd5, 0x82, 0x76, 0xc1, 0x25, 0x3b, 0x90, 0x4f, 0x73, - 0xcf, 0x7f, 0x42, 0x1e, 0x58, 0xb9, 0x00, 0xc8, 0x40, 0x11, 0x08, 0x04, - 0x5c, 0x02, 0xd2, 0x89, 0xb4, 0x82, 0x4c, 0xb0, 0x01, 0xbb, 0x87, 0x9f, - 0xaa, 0xde, 0x7d, 0x94, 0x21, 0xbc, 0x00, 0x99, 0x45, 0xc0, 0x01, 0x70, - 0x00, 0x00, 0x17, 0xd7, 0x1a, 0x0c, 0x30, 0x6a, 0x04, 0x80, 0x0a, 0x4c, - 0x29, 0x20, 0x02, 0xb5, 0x44, 0x3b, 0x5d, 0x2a, 0x72, 0x4f, 0x16, 0xeb, - 0x65, 0x86, 0x7e, 0x55, 0xc9, 0x0b, 0x74, 0xe0, 0xff, 0xf1, 0x50, 0x80, - 0x11, 0x7f, 0xfc, 0x21, 0x1a, 0xc8, 0x45, 0x29, 0x80, 0x1d, 0x0f, 0x72, - 0x02, 0x08, 0x40, 0x42, 0x20, 0x08, 0xac, 0x07, 0xa6, 0x11, 0xe8, 0x44, - 0x3a, 0x41, 0x20, 0x56, 0xf8, 0xa9, 0xbc, 0xe2, 0x53, 0xcf, 0xbd, 0xe6, - 0x67, 0xcd, 0x31, 0x35, 0x59, 0x80, 0x2c, 0x12, 0x0a, 0x80, 0x00, 0x40, - 0x2c, 0x02, 0x8b, 0x00, 0xb0, 0x00, 0x0b, 0x05, 0x4c, 0x47, 0xca, 0x71, - 0xdb, 0x82, 0x51, 0x8f, 0x9e, 0xbf, 0x92, 0x5f, 0xc3, 0xf6, 0x72, 0x02, - 0x08, 0x40, 0x62, 0x10, 0x08, 0xbf, 0x46, 0x28, 0xd1, 0x89, 0x00, 0x14, - 0x80, 0x7c, 0x73, 0x9f, 0x34, 0xa1, 0x55, 0x5c, 0xd0, 0x12, 0x00, 0x02, - 0xe0, 0x00, 0x00, 0x02, 0xa1, 0x01, 0x62, 0x04, 0xe6, 0x00, 0x15, 0x08, - 0x00, 0xe5, 0x47, 0x91, 0x39, 0x24, 0x22, 0x16, 0x48, 0x00, 0x2e, 0x3a, - 0x5a, 0x36, 0x26, 0x95, 0xba, 0xfe, 0x7c, 0xb8, 0xfe, 0x12, 0xf2, 0xfc, - 0x7b, 0x4b, 0x80, 0xff, 0xf1, 0x50, 0x80, 0x12, 0xff, 0xfc, 0x21, 0x1a, - 0xc9, 0x00, 0x5c, 0xe0, 0x00, 0x0f, 0x72, 0x01, 0x08, 0x40, 0x42, 0x10, - 0x08, 0x84, 0x04, 0x2c, 0x01, 0x69, 0x05, 0x1a, 0x13, 0x30, 0x01, 0x96, - 0x1e, 0x7e, 0x37, 0x5e, 0xea, 0x14, 0xca, 0x52, 0x81, 0x38, 0xd2, 0xe2, - 0xcb, 0x00, 0x00, 0x01, 0x75, 0xc9, 0x17, 0x51, 0xc6, 0x09, 0xd1, 0x16, - 0xfa, 0xda, 0x92, 0xb4, 0xa1, 0xa7, 0xf7, 0xf9, 0x67, 0xc6, 0x7f, 0x3c, - 0xd1, 0x2f, 0x23, 0xac, 0x2c, 0x9c, 0x54, 0x15, 0xb8, 0x01, 0x40, 0x57, - 0x62, 0xd7, 0xac, 0x80, 0x89, 0xad, 0xb7, 0x81, 0x56, 0xbe, 0x8e, 0x65, - 0xcf, 0x45, 0xc3, 0x93, 0x62, 0xf2, 0xda, 0xb6, 0xff, 0x69, 0xc8, 0x04, - 0x21, 0x04, 0x8a, 0x80, 0x5a, 0xf1, 0x11, 0x08, 0xf7, 0xf8, 0x39, 0xdf, - 0xb0, 0xf4, 0xe7, 0x9c, 0xe4, 0xc9, 0x53, 0x57, 0x5c, 0xfe, 0x8a, 0x00, - 0x00, 0x00, 0x00, 0x65, 0xaa, 0x90, 0x65, 0x52, 0x13, 0x11, 0xf9, 0xe0, - 0x99, 0xd1, 0x85, 0x05, 0xa9, 0xa7, 0x8c, 0x4a, 0xaf, 0x80, 0xff, 0xf1, - 0x50, 0x80, 0x11, 0xff, 0xfc, 0x21, 0x1a, 0xc8, 0x00, 0x7f, 0x80, 0x3f, - 0x9f, 0x72, 0x00, 0x88, 0x40, 0x24, 0x20, 0x38, 0xa0, 0x09, 0xa3, 0x14, - 0xe8, 0x84, 0xa0, 0x15, 0x0f, 0x4e, 0x73, 0xd2, 0x94, 0x2b, 0x79, 0x4a, - 0xa0, 0x89, 0x60, 0x40, 0x01, 0x50, 0x02, 0xd9, 0xe5, 0x7c, 0xca, 0xe4, - 0xac, 0x00, 0x03, 0x4d, 0x82, 0x93, 0x22, 0x0a, 0x48, 0x48, 0x02, 0xa9, - 0xa6, 0x00, 0x00, 0x12, 0x29, 0x35, 0xd8, 0xa5, 0x54, 0xfb, 0xe0, 0x47, - 0x26, 0x16, 0x4f, 0xfd, 0x7e, 0xdc, 0x2b, 0xde, 0x72, 0x72, 0x06, 0x0a, - 0x00, 0x9a, 0x51, 0x3e, 0x84, 0x4c, 0x0a, 0x87, 0x9f, 0x8a, 0xde, 0x7b, - 0xa8, 0x54, 0xa2, 0xa8, 0x00, 0xb0, 0x00, 0x54, 0x30, 0xc2, 0x71, 0x20, - 0xd7, 0x20, 0x26, 0x0a, 0xb4, 0xe1, 0x2a, 0xa8, 0xc0, 0x00, 0x00, 0x04, - 0x40, 0x26, 0x9c, 0xdb, 0xbe, 0xea, 0x00, 0xfe, 0x3c, 0x2b, 0xad, 0xab, - 0x57, 0xf4, 0xf9, 0x47, 0x74, 0xed, 0xd9, 0x96, 0x7c, 0xff, 0xf1, 0x50, - 0x80, 0x10, 0x7f, 0xfc, 0x21, 0x1a, 0xc8, 0x05, 0xa3, 0xc0, 0x3e, 0x1f, - 0x72, 0x01, 0x90, 0x40, 0x42, 0x10, 0x08, 0x84, 0x02, 0x2b, 0x01, 0xe9, - 0x44, 0xba, 0x41, 0x28, 0x15, 0x9a, 0x15, 0x1e, 0x7d, 0xf3, 0x5b, 0xf9, - 0xa5, 0x29, 0x6a, 0xa0, 0x20, 0x90, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x50, 0x00, 0x00, 0x00, 0x25, 0x42, 0x51, 0xd4, 0x52, 0x25, 0xa6, 0xb9, - 0x8b, 0x4a, 0x13, 0x7d, 0x16, 0xf0, 0x77, 0x72, 0x00, 0x88, 0x40, 0xa2, - 0x20, 0x08, 0xac, 0x07, 0xa4, 0x14, 0x68, 0x84, 0xa0, 0xc9, 0x02, 0xa3, - 0xd3, 0x7c, 0xe7, 0xca, 0x94, 0x2b, 0x2a, 0x80, 0xa6, 0x50, 0x2c, 0x0b, - 0x80, 0x00, 0x5c, 0x00, 0x01, 0x14, 0x01, 0x10, 0x00, 0x10, 0x22, 0x95, - 0xc2, 0xe0, 0x00, 0x08, 0xa7, 0x50, 0x42, 0xc7, 0x09, 0xb9, 0x4e, 0x38, - 0x30, 0xf5, 0xf7, 0x3b, 0xc6, 0xd5, 0xdd, 0x2e, 0xff, 0xf1, 0x50, 0x80, - 0x10, 0x1f, 0xfc, 0x21, 0x1a, 0xc8, 0x18, 0x98, 0xe0, 0x0e, 0x1f, 0x72, - 0x02, 0x88, 0xc0, 0x23, 0x0d, 0x20, 0x87, 0x42, 0x23, 0xd2, 0x08, 0xc8, - 0x59, 0x65, 0x4c, 0xcd, 0x4a, 0x99, 0x60, 0x7c, 0x73, 0x95, 0xf9, 0xda, - 0x95, 0x7a, 0xca, 0x01, 0x23, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0xba, - 0x24, 0x00, 0x00, 0x00, 0x00, 0x00, 0x4d, 0x13, 0xe6, 0x8f, 0x43, 0x5b, - 0x66, 0x76, 0xd5, 0x94, 0x0f, 0xe0, 0xcf, 0x04, 0x66, 0xe9, 0x8f, 0x4b, - 0x90, 0x18, 0x44, 0x03, 0x15, 0x80, 0xb4, 0xc2, 0x5d, 0x18, 0x94, 0x65, - 0xd4, 0xef, 0x55, 0xa1, 0xe3, 0xef, 0x5d, 0xd5, 0x7e, 0x54, 0x56, 0x94, - 0x00, 0x00, 0x00, 0x00, 0x01, 0x40, 0x00, 0x00, 0x00, 0x90, 0x00, 0x00, - 0x05, 0x93, 0x7b, 0xb9, 0x49, 0x5a, 0x25, 0x4b, 0xda, 0x73, 0xb7, 0x7d, - 0x58, 0xa3, 0xe4, 0xe0, 0xff, 0xf1, 0x50, 0x80, 0x0e, 0x3f, 0xfc, 0x21, - 0x1a, 0xc8, 0x03, 0x01, 0x00, 0x0b, 0x0f, 0x72, 0x04, 0x8a, 0x80, 0xba, - 0x71, 0x26, 0x8c, 0x4a, 0x6f, 0x59, 0x6c, 0x5b, 0xcf, 0xbe, 0x73, 0x31, - 0xfa, 0x15, 0x2e, 0x80, 0x00, 0x04, 0x0d, 0xa0, 0x00, 0x75, 0xd4, 0x10, - 0x00, 0x00, 0x04, 0x91, 0x02, 0x22, 0xe2, 0xfa, 0x5d, 0x6d, 0xba, 0x97, - 0xfd, 0xb0, 0x5e, 0xfb, 0xa3, 0xf2, 0xaf, 0xd5, 0xed, 0x72, 0x04, 0x8a, - 0x80, 0xba, 0x71, 0x0e, 0x84, 0x45, 0xa4, 0x12, 0x1b, 0x67, 0x5e, 0xee, - 0x38, 0x79, 0xf7, 0x5e, 0xa6, 0x57, 0xe9, 0x4a, 0xbd, 0x66, 0xc0, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x2a, 0x21, 0x64, 0xcb, 0xa5, 0x26, 0x7c, - 0xd1, 0xa7, 0x7f, 0x4f, 0xd5, 0x2e, 0x76, 0xbf, 0x1e, 0xff, 0xf1, 0x50, - 0x80, 0x10, 0x7f, 0xfc, 0x21, 0x1a, 0xc8, 0x00, 0xa4, 0x00, 0x02, 0x0f, - 0x72, 0x04, 0x8b, 0x40, 0x5a, 0x41, 0x0e, 0x84, 0x43, 0xa1, 0x11, 0xe8, - 0xc4, 0xa3, 0x9d, 0x4f, 0x1d, 0x78, 0xbb, 0xd1, 0xe9, 0xce, 0x6f, 0xf3, - 0xdd, 0x65, 0x1d, 0x4a, 0x00, 0xa0, 0x00, 0x00, 0x01, 0x50, 0x04, 0x45, - 0x40, 0x04, 0x80, 0x0b, 0x80, 0x05, 0xc0, 0xd4, 0xa8, 0xd7, 0xa5, 0x2f, - 0x04, 0x65, 0x57, 0xbd, 0x6c, 0xd5, 0xd7, 0xeb, 0x66, 0xbe, 0xa7, 0x20, - 0x10, 0x84, 0x06, 0x21, 0x02, 0x0b, 0x74, 0xa2, 0x1d, 0x18, 0x87, 0x4a, - 0x24, 0x19, 0x1c, 0xf1, 0xdf, 0x9f, 0x1e, 0x43, 0xcf, 0x9e, 0x73, 0x7f, - 0xa6, 0x52, 0x93, 0xac, 0xe4, 0x04, 0xec, 0x00, 0x00, 0x00, 0x15, 0x00, - 0x13, 0x00, 0x00, 0x40, 0x14, 0x4b, 0xf5, 0xbd, 0x9e, 0xb4, 0xe1, 0xf2, - 0xf5, 0x7f, 0x1a, 0x5b, 0x35, 0x25, 0x8b, 0x6f, 0xff, 0xf1, 0x50, 0x80, - 0x13, 0xbf, 0xfc, 0x21, 0x1a, 0xc8, 0x03, 0x3f, 0x40, 0x76, 0x1f, 0x72, - 0x04, 0x8b, 0x00, 0x7a, 0x21, 0x5e, 0x84, 0x44, 0x43, 0x00, 0x2a, 0x3c, - 0xf9, 0xf0, 0xa5, 0x14, 0x3d, 0x55, 0x2a, 0x80, 0x11, 0x00, 0x02, 0xe0, - 0x05, 0x85, 0x64, 0x00, 0x35, 0x50, 0x10, 0x84, 0x78, 0x1d, 0x05, 0x63, - 0xad, 0x56, 0xf5, 0xc5, 0x05, 0x76, 0x44, 0x48, 0x00, 0x52, 0xc8, 0xe0, - 0x00, 0x0a, 0xce, 0x85, 0xed, 0x65, 0xa5, 0x3d, 0x1e, 0x71, 0x31, 0x6a, - 0x61, 0xf0, 0x2c, 0x70, 0x72, 0xdd, 0xa2, 0xde, 0x98, 0x2d, 0x3f, 0x5b, - 0x90, 0x08, 0x42, 0x05, 0x16, 0x80, 0xf4, 0x82, 0x7d, 0x18, 0x94, 0x32, - 0xc0, 0x1e, 0x5e, 0x39, 0xaf, 0x4a, 0xde, 0xab, 0x39, 0x97, 0x29, 0x54, - 0x2b, 0x10, 0x00, 0x02, 0x81, 0x50, 0x00, 0xa9, 0xec, 0x0b, 0xaf, 0x2a, - 0x0e, 0x89, 0x95, 0xb8, 0xe7, 0x9d, 0xef, 0xb8, 0x06, 0x40, 0x01, 0x49, - 0x8b, 0x2e, 0xab, 0x3c, 0xb1, 0x73, 0xc1, 0x6a, 0xfc, 0xba, 0xeb, 0x69, - 0xfe, 0x95, 0xf7, 0x89, 0xdf, 0x0e, 0xfd, 0xdb, 0xf8, 0xff, 0xf1, 0x50, - 0x80, 0x14, 0xbf, 0xfc, 0x21, 0x1a, 0xc8, 0x02, 0xff, 0x80, 0xff, 0x9f, - 0x72, 0x03, 0x88, 0x40, 0x22, 0x10, 0x08, 0xa4, 0x09, 0xa1, 0x10, 0xe8, - 0x45, 0x3a, 0x21, 0x28, 0x00, 0x78, 0xf3, 0xf3, 0x8a, 0x50, 0x72, 0xc5, - 0x64, 0x14, 0x00, 0x00, 0x00, 0x28, 0x00, 0x20, 0x82, 0xb2, 0x18, 0x6d, - 0x72, 0xc6, 0xbb, 0x54, 0xb5, 0x15, 0x89, 0x51, 0x1b, 0xd1, 0x1a, 0x4b, - 0x06, 0x58, 0x2b, 0x40, 0x20, 0x00, 0x07, 0x2e, 0x96, 0x82, 0xea, 0x70, - 0xe7, 0x72, 0x5f, 0xa9, 0xb6, 0x57, 0x3f, 0xbf, 0xea, 0xcf, 0xdf, 0x1d, - 0x3c, 0x78, 0xbd, 0x17, 0x72, 0x04, 0x0c, 0x74, 0x42, 0x9d, 0x08, 0x8c, - 0x86, 0x00, 0x00, 0x7c, 0x57, 0x85, 0x28, 0x3b, 0xaa, 0x56, 0x41, 0x54, - 0x00, 0x00, 0x00, 0x11, 0x00, 0x00, 0x88, 0x05, 0x88, 0x44, 0xa8, 0xb0, - 0x48, 0x94, 0x13, 0xad, 0xc2, 0xb4, 0x96, 0x82, 0xea, 0xd2, 0xb9, 0xc3, - 0x15, 0xf6, 0x2c, 0xa8, 0x00, 0x09, 0x00, 0x40, 0x38, 0x4a, 0x71, 0x1d, - 0x00, 0xe4, 0x4e, 0x53, 0xdc, 0xa8, 0x25, 0x32, 0xc3, 0xcd, 0x7d, 0x76, - 0xd5, 0x57, 0xe3, 0xb3, 0xcf, 0x80, 0xff, 0xf1, 0x50, 0x80, 0x13, 0x7f, - 0xfc, 0x21, 0x1a, 0xc8, 0x01, 0x96, 0x40, 0x1e, 0x1f, 0x72, 0x01, 0x08, - 0x40, 0x22, 0x10, 0x08, 0xcf, 0x4a, 0x24, 0xd2, 0x09, 0x10, 0x39, 0x10, - 0x07, 0x3c, 0x3b, 0xe8, 0x0f, 0x9c, 0xcd, 0xd7, 0xc9, 0x5b, 0xf3, 0x4a, - 0xa5, 0x04, 0x6d, 0x62, 0xa0, 0x00, 0x12, 0x00, 0x00, 0x00, 0x58, 0x00, - 0x42, 0x89, 0xf6, 0x2f, 0x5b, 0xce, 0x72, 0x01, 0x01, 0x60, 0x28, 0x23, - 0x4b, 0xdb, 0x7f, 0x5c, 0x15, 0x8f, 0x56, 0x7a, 0x9e, 0x28, 0xba, 0xda, - 0x4a, 0x9f, 0xaa, 0xec, 0xc1, 0x8e, 0xe4, 0x04, 0x20, 0x81, 0x45, 0x80, - 0x2d, 0x18, 0xa7, 0x44, 0x25, 0x02, 0xa3, 0x2c, 0x7a, 0x76, 0xf5, 0x4a, - 0x28, 0xf1, 0x55, 0x4a, 0x02, 0x0a, 0x10, 0x10, 0x00, 0x02, 0xa0, 0x16, - 0x22, 0x8d, 0x48, 0xae, 0x59, 0x74, 0x68, 0x50, 0x46, 0x47, 0x31, 0x5e, - 0xf7, 0x51, 0x52, 0x52, 0x04, 0xd6, 0x94, 0x8b, 0xac, 0x00, 0x10, 0x27, - 0x89, 0x0a, 0x14, 0xa1, 0x85, 0x92, 0x70, 0xbd, 0xfd, 0xc8, 0xe3, 0xc0, - 0xe9, 0xd1, 0x9e, 0x3e, 0xde, 0xff, 0xf1, 0x50, 0x80, 0x11, 0xff, 0xfc, - 0x21, 0x1a, 0xc9, 0x01, 0x7c, 0xc0, 0x3f, 0x0f, 0x72, 0x01, 0x08, 0x40, - 0xa2, 0x10, 0x08, 0xb0, 0x05, 0xa5, 0x13, 0x68, 0xc4, 0xa0, 0x00, 0x3d, - 0x39, 0xde, 0x6f, 0xdd, 0x43, 0x35, 0x5c, 0xc5, 0x05, 0xea, 0x99, 0x30, - 0x00, 0x00, 0x10, 0x48, 0x14, 0x0a, 0x38, 0x50, 0xb8, 0x29, 0x65, 0x28, - 0x01, 0x31, 0x29, 0x08, 0x80, 0x44, 0x54, 0x2d, 0xd6, 0xd6, 0x50, 0x22, - 0x2f, 0x82, 0xcf, 0xfa, 0x76, 0xc7, 0x5f, 0xfe, 0xe4, 0x01, 0x11, 0x00, - 0x84, 0x20, 0x41, 0x76, 0x8c, 0x51, 0xa3, 0x12, 0x80, 0x00, 0x1e, 0x7e, - 0xab, 0xd2, 0x94, 0x3c, 0x5b, 0x29, 0x41, 0x64, 0xc5, 0x40, 0x5c, 0x05, - 0xc0, 0x04, 0x00, 0x01, 0x75, 0xec, 0x46, 0x20, 0x11, 0x12, 0x8c, 0xef, - 0x48, 0x00, 0xb8, 0x10, 0x02, 0x40, 0x70, 0x5e, 0xc0, 0x28, 0xbc, 0xa4, - 0xb1, 0xe6, 0xad, 0xb2, 0x13, 0xd8, 0xbd, 0xf4, 0x1f, 0xee, 0x8d, 0x15, - 0xff, 0xb7, 0xd9, 0xc0, 0xff, 0xf1, 0x50, 0x80, 0x11, 0x9f, 0xfc, 0x21, - 0x1a, 0xc8, 0x05, 0x5f, 0x00, 0xff, 0x0f, 0x72, 0x03, 0x08, 0x40, 0x23, - 0x1d, 0x10, 0xa3, 0x46, 0x23, 0x21, 0x00, 0x19, 0x08, 0x1f, 0x19, 0xee, - 0xa5, 0x0f, 0x57, 0x58, 0xa0, 0x44, 0x44, 0x00, 0xb0, 0x05, 0xc1, 0x10, - 0x00, 0x13, 0x10, 0x00, 0x00, 0x01, 0x41, 0x55, 0xc0, 0x29, 0x52, 0x28, - 0x95, 0x14, 0x4e, 0xe0, 0x2e, 0x50, 0x2c, 0x0c, 0x49, 0x4e, 0x24, 0xb1, - 0x65, 0xb6, 0xce, 0xa7, 0x92, 0x7b, 0xdc, 0x71, 0x3d, 0x84, 0xe4, 0x04, - 0x10, 0x80, 0x84, 0x40, 0x11, 0x48, 0x17, 0x46, 0x29, 0xd1, 0x99, 0x00, - 0x0c, 0x8f, 0x2f, 0x1b, 0xf4, 0xa5, 0x0a, 0xcd, 0xee, 0xa8, 0x17, 0xad, - 0x4d, 0x80, 0x00, 0x00, 0x10, 0x40, 0x00, 0x10, 0x80, 0x50, 0x59, 0x65, - 0x85, 0xc1, 0x50, 0x00, 0x01, 0x30, 0x14, 0x2d, 0x42, 0xb0, 0x2b, 0xc1, - 0x9e, 0xbd, 0x1d, 0xab, 0x48, 0xba, 0xb5, 0x3f, 0xab, 0x9f, 0x37, 0x80, - 0xff, 0xf1, 0x50, 0x80, 0x11, 0x5f, 0xfc, 0x21, 0x1a, 0xc8, 0x20, 0xbe, - 0x00, 0x8f, 0x1f, 0x72, 0x02, 0x88, 0x40, 0xa2, 0x70, 0x36, 0x88, 0x45, - 0xa1, 0x10, 0xe8, 0x44, 0x9a, 0x33, 0x29, 0x96, 0x0f, 0x3e, 0xfd, 0x57, - 0x8a, 0xca, 0x53, 0x34, 0xaa, 0x00, 0x00, 0x04, 0xc1, 0x20, 0x02, 0x06, - 0xd4, 0x89, 0x00, 0x00, 0x02, 0x60, 0x00, 0x99, 0x0a, 0x89, 0x06, 0xce, - 0x38, 0xc7, 0x5a, 0x9a, 0xcd, 0x79, 0x4f, 0xa7, 0x6a, 0x4f, 0x0d, 0xf0, - 0x4f, 0x5f, 0xdf, 0x39, 0x01, 0x44, 0x20, 0x31, 0x08, 0x04, 0x5b, 0xa3, - 0x11, 0x68, 0x44, 0xba, 0x31, 0x2a, 0xa0, 0x18, 0xb0, 0xf2, 0xf1, 0xcf, - 0xbd, 0x73, 0x45, 0x2a, 0x4a, 0xa0, 0x17, 0x92, 0x64, 0x00, 0x02, 0x00, - 0x08, 0x80, 0x02, 0xc5, 0x05, 0x16, 0x0b, 0x70, 0xc4, 0x11, 0x00, 0x00, - 0x00, 0x00, 0x08, 0x80, 0x48, 0x93, 0xf4, 0x57, 0x8e, 0x7a, 0xfb, 0x7d, - 0xf4, 0xd7, 0x2a, 0x89, 0xf9, 0xf8, 0xff, 0xf1, 0x50, 0x80, 0x12, 0xbf, - 0xfc, 0x21, 0x1a, 0xc8, 0x03, 0xaa, 0x01, 0x5f, 0x9f, 0x72, 0x01, 0x90, - 0x80, 0x82, 0xa0, 0x2e, 0x88, 0x55, 0xa2, 0x11, 0x90, 0x80, 0x06, 0xde, - 0x7e, 0x37, 0x54, 0xa1, 0x4f, 0x0a, 0xa5, 0x04, 0xab, 0x00, 0x22, 0x22, - 0x10, 0x00, 0x28, 0xbc, 0x45, 0xc5, 0xa3, 0x21, 0x79, 0x93, 0xba, 0x89, - 0xc0, 0x00, 0x00, 0xe7, 0x45, 0xc2, 0xa0, 0x00, 0x0a, 0x2a, 0xd0, 0x10, - 0x50, 0xd9, 0x24, 0x41, 0xf2, 0x39, 0xc9, 0xb5, 0xf5, 0xb5, 0xdd, 0xe8, - 0xc8, 0x79, 0x9d, 0x72, 0x03, 0x10, 0x40, 0x42, 0x50, 0x08, 0x98, 0x07, - 0xa2, 0x10, 0xe8, 0x44, 0x3a, 0x11, 0x36, 0x88, 0x4a, 0x03, 0x9e, 0x03, - 0xcf, 0xbf, 0x1d, 0xe6, 0xe8, 0x50, 0xaa, 0x50, 0x5e, 0x05, 0xc0, 0x04, - 0x6e, 0x13, 0x00, 0x00, 0x00, 0xa0, 0x37, 0xf6, 0xbb, 0x88, 0xaa, 0x62, - 0xea, 0xc0, 0x00, 0x00, 0x02, 0xe6, 0x73, 0xa9, 0x6d, 0x9b, 0xa5, 0x3d, - 0x9d, 0xb3, 0xa1, 0xf7, 0xd5, 0xfa, 0xad, 0x82, 0x17, 0x5b, 0x80, 0xff, - 0xf1, 0x50, 0x80, 0x12, 0xdf, 0xfc, 0x21, 0x1a, 0xc8, 0x15, 0xba, 0xc0, - 0xdd, 0x0f, 0x72, 0x01, 0x10, 0x40, 0x62, 0x10, 0x08, 0xb8, 0x05, 0xa3, - 0x14, 0x69, 0x04, 0x80, 0x78, 0xf2, 0x2a, 0x07, 0x97, 0x8d, 0xfa, 0xac, - 0x86, 0x4f, 0x5a, 0x73, 0x8a, 0x07, 0x40, 0x00, 0x00, 0x00, 0x2e, 0x11, - 0x01, 0x30, 0x2a, 0x51, 0x10, 0x12, 0x17, 0x26, 0x98, 0x82, 0x40, 0x4d, - 0x60, 0x02, 0x89, 0x2d, 0x4d, 0xce, 0x57, 0xaa, 0x4a, 0x5b, 0x3e, 0xd7, - 0xd7, 0x5d, 0xf5, 0xe4, 0xfd, 0x69, 0xf5, 0x72, 0x00, 0x88, 0x40, 0x62, - 0x20, 0x08, 0x84, 0x02, 0x2b, 0x01, 0xe8, 0xc5, 0x3a, 0x21, 0x28, 0x00, - 0xac, 0xd1, 0xe5, 0xe3, 0x7e, 0xa9, 0x45, 0x1c, 0xca, 0xa5, 0x04, 0xe6, - 0x03, 0xa8, 0x00, 0xa8, 0x00, 0x00, 0x02, 0x60, 0x01, 0xcc, 0x4e, 0x2e, - 0xf1, 0x5b, 0x31, 0x45, 0x41, 0x50, 0x05, 0xef, 0x05, 0x40, 0xd6, 0x00, - 0x4a, 0xe7, 0x15, 0xa3, 0x6e, 0xe9, 0x43, 0xcd, 0x2b, 0xc6, 0xd6, 0x97, - 0xe7, 0x07, 0x1d, 0x7d, 0xb8, 0xff, 0xf1, 0x50, 0x80, 0x08, 0x7f, 0xfc, - 0x21, 0x1a, 0xc8, 0x00, 0x00, 0x00, 0x00, 0x0b, 0x72, 0x0c, 0x6b, 0xc4, - 0x8f, 0x1e, 0x73, 0x7d, 0xca, 0xbe, 0xe5, 0xcd, 0x57, 0x3f, 0xb0, 0x00, - 0x16, 0x00, 0x53, 0x0d, 0x00, 0x93, 0x03, 0xc8, 0xe4, 0x18, 0xd7, 0x88, - 0x74, 0x22, 0x27, 0x7f, 0x8d, 0xee, 0x6e, 0xfb, 0xdd, 0xcb, 0xf3, 0x9e, - 0x3f, 0x6d, 0x80, 0x02, 0xc2, 0x54, 0xc3, 0x3b, 0x9e, 0x68, 0x19, 0x78 -}; -unsigned int gs_16b_2c_44100hz_aac_len = 241596; diff --git a/tests-cmake/codec/aac-fdk-encode/CMakeLists.txt b/tests-cmake/codec/aac-fdk-encode/CMakeLists.txt index 3c7d16cfc0..bcf2c57f30 100644 --- a/tests-cmake/codec/aac-fdk-encode/CMakeLists.txt +++ b/tests-cmake/codec/aac-fdk-encode/CMakeLists.txt @@ -4,7 +4,10 @@ cmake_minimum_required(VERSION 3.20) project(aac-fdk-encode) set (CMAKE_CXX_STANDARD 11) set (DCMAKE_CXX_FLAGS "-Werror") -# add_compile_options(-Wstack-usage=1024) +if (CMAKE_CXX_COMPILER_ID STREQUAL "Clang") + set (CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -fno-omit-frame-pointer -fsanitize=address") + set (CMAKE_LINKER_FLAGS_DEBUG "${CMAKE_LINKER_FLAGS_DEBUG} -fno-omit-frame-pointer -fsanitize=address") +endif() include(FetchContent) @@ -22,9 +25,9 @@ if(NOT fdk_aac_POPULATED) endif() # build sketch as executable -add_executable (aac-fdk-encode aac-fdk-encode.cpp ) +add_executable (aac-fdk-encode aac-fdk-encode.cpp ../../main.cpp) # set preprocessor defines -target_compile_definitions(aac-fdk-encode PUBLIC -DARDUINO -DIS_DESKTOP) +target_compile_definitions(aac-fdk-encode PUBLIC -DARDUINO -DEXIT_ON_STOP -DIS_DESKTOP) # specify libraries target_link_libraries(aac-fdk-encode arduino_emulator fdk_aac arduino-audio-tools) diff --git a/tests-cmake/codec/aac-fdk-encode/aac-fdk-encode.cpp b/tests-cmake/codec/aac-fdk-encode/aac-fdk-encode.cpp index c72d604247..d4b19a1a0d 100644 --- a/tests-cmake/codec/aac-fdk-encode/aac-fdk-encode.cpp +++ b/tests-cmake/codec/aac-fdk-encode/aac-fdk-encode.cpp @@ -1,14 +1,14 @@ // Simple wrapper for Arduino sketch to compilable with cpp in cmake #include "Arduino.h" #include "AudioTools.h" -#include "AudioTools/AudioCodecs/CodecAACFDK.h" +#include "AudioCodecs/CodecAACFDK.h" //#include // for rand using namespace audio_tools; -HexDumpOutput out(Serial); +HexDumpStream out(Serial); AACEncoderFDK aac(out); -AudioInfo info; +AudioBaseInfo info; int16_t buffer[512]; void setup() { @@ -26,7 +26,7 @@ void loop() { for (int j=0;j<512;j++){ buffer[j] = (rand() % 100) - 50; } - if (aac.write((uint8_t*)buffer, 512*sizeof(int16_t))){ + if (aac.write(buffer, 512*sizeof(int16_t))){ out.flush(); Serial.println("512 samples of random data written"); } diff --git a/tests-cmake/codec/aac-fdk/CMakeLists.txt b/tests-cmake/codec/aac-fdk/CMakeLists.txt index 4d752834fa..66062037f6 100644 --- a/tests-cmake/codec/aac-fdk/CMakeLists.txt +++ b/tests-cmake/codec/aac-fdk/CMakeLists.txt @@ -4,7 +4,10 @@ cmake_minimum_required(VERSION 3.20) project(aac-fdk) set (CMAKE_CXX_STANDARD 11) set (DCMAKE_CXX_FLAGS "-Werror") -# add_compile_options(-Wstack-usage=1024) +if (CMAKE_CXX_COMPILER_ID STREQUAL "Clang") + set (CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -fno-omit-frame-pointer -fsanitize=address") + set (CMAKE_LINKER_FLAGS_DEBUG "${CMAKE_LINKER_FLAGS_DEBUG} -fno-omit-frame-pointer -fsanitize=address") +endif() include(FetchContent) # Build with arduino-audio-tools @@ -21,10 +24,10 @@ if(NOT fdk_aac_POPULATED) endif() # build sketch as executable -add_executable (aac-fdk aac-fdk.cpp ) +add_executable (aac-fdk aac-fdk.cpp ../../main.cpp) # set preprocessor defines -target_compile_definitions(aac-fdk PUBLIC -DARDUINO -DUSE_PORTAUDIO -DIS_DESKTOP) +target_compile_definitions(aac-fdk PUBLIC -DARDUINO -DEXIT_ON_STOP -DUSE_PORTAUDIO -DIS_DESKTOP) # OS/X might need this setting for core audio #target_compile_definitions(portaudio PUBLIC -DPA_USE_COREAUDIO=1) diff --git a/tests-cmake/codec/aac-fdk/README.md b/tests-cmake/codec/aac-fdk/README.md new file mode 100644 index 0000000000..d98386d7f8 --- /dev/null +++ b/tests-cmake/codec/aac-fdk/README.md @@ -0,0 +1,11 @@ +# Test Signal to Bluetooth Speaker + +Sometimes it is quite useful to be able to generate a test tone. +We can use the GeneratedSoundStream class together with a SoundGenerator class. In my example I use a SineWaveGenerator. + +To test the output I'm using this generated signal and write it to A2DP (e.g. a Bluetooth Speaker). + + +## Compile Settings + +Please set the Patition Scheme to __Hugh APP__ diff --git a/tests-cmake/codec/aac-fdk/aac-fdk.cpp b/tests-cmake/codec/aac-fdk/aac-fdk.cpp index c25125046d..cc4c99ed8e 100644 --- a/tests-cmake/codec/aac-fdk/aac-fdk.cpp +++ b/tests-cmake/codec/aac-fdk/aac-fdk.cpp @@ -1,7 +1,7 @@ #include "Arduino.h" #include "AudioTools.h" -#include "AudioTools/AudioCodecs/CodecAACFDK.h" -#include "AudioTools/AudioLibs/PortAudioStream.h" +#include "AudioCodecs/CodecAACFDK.h" +#include "AudioLibs/PortAudioStream.h" #include "audio.h" using namespace audio_tools; @@ -13,9 +13,9 @@ StreamCopy copier(dec, aac); // copy in to out void setup(){ Serial.begin(115200); - AudioToolsLogger.begin(Serial, AudioToolsLogLevel::Info); + AudioLogger::instance().begin(Serial, AudioLogger::Info); - dec.addNotifyAudioChange(portaudio_stream); + dec.setNotifyAudioChange(portaudio_stream); dec.begin(); portaudio_stream.begin(); diff --git a/tests-cmake/codec/aac-helix/CMakeLists.txt b/tests-cmake/codec/aac-helix/CMakeLists.txt index e20e40aff6..58c8aee170 100644 --- a/tests-cmake/codec/aac-helix/CMakeLists.txt +++ b/tests-cmake/codec/aac-helix/CMakeLists.txt @@ -4,7 +4,10 @@ cmake_minimum_required(VERSION 3.20) project(aac-helix) set (CMAKE_CXX_STANDARD 11) set (DCMAKE_CXX_FLAGS "-Werror") -# add_compile_options(-Wstack-usage=1024) +if (CMAKE_CXX_COMPILER_ID STREQUAL "Clang") + set (CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -fno-omit-frame-pointer -fsanitize=address") + set (CMAKE_LINKER_FLAGS_DEBUG "${CMAKE_LINKER_FLAGS_DEBUG} -fno-omit-frame-pointer -fsanitize=address") +endif() include(FetchContent) # Build with arduino-audio-tools @@ -21,10 +24,10 @@ if(NOT helix_POPULATED) endif() # build sketch as executable -add_executable (aac-helix aac-helix.cpp) +add_executable (aac-helix aac-helix.cpp ../../main.cpp) # set preprocessor defines -target_compile_definitions(aac-helix PUBLIC -DARDUINO -DUSE_PORTAUDIO -DIS_DESKTOP) +target_compile_definitions(aac-helix PUBLIC -DARDUINO -DEXIT_ON_STOP -DUSE_PORTAUDIO -DIS_DESKTOP) # OS/X might need this setting for core audio #target_compile_definitions(portaudio PUBLIC -DPA_USE_COREAUDIO=1) diff --git a/tests-cmake/codec/aac-helix/README.md b/tests-cmake/codec/aac-helix/README.md new file mode 100644 index 0000000000..d98386d7f8 --- /dev/null +++ b/tests-cmake/codec/aac-helix/README.md @@ -0,0 +1,11 @@ +# Test Signal to Bluetooth Speaker + +Sometimes it is quite useful to be able to generate a test tone. +We can use the GeneratedSoundStream class together with a SoundGenerator class. In my example I use a SineWaveGenerator. + +To test the output I'm using this generated signal and write it to A2DP (e.g. a Bluetooth Speaker). + + +## Compile Settings + +Please set the Patition Scheme to __Hugh APP__ diff --git a/tests-cmake/codec/aac-helix/aac-helix.cpp b/tests-cmake/codec/aac-helix/aac-helix.cpp index 68f31cb69b..7e1910178b 100644 --- a/tests-cmake/codec/aac-helix/aac-helix.cpp +++ b/tests-cmake/codec/aac-helix/aac-helix.cpp @@ -1,7 +1,7 @@ #include "Arduino.h" #include "AudioTools.h" -#include "AudioTools/AudioCodecs/CodecAACHelix.h" -#include "AudioTools/AudioLibs/PortAudioStream.h" +#include "AudioCodecs/CodecAACHelix.h" +#include "AudioLibs/PortAudioStream.h" #include "audio.h" using namespace audio_tools; @@ -13,9 +13,9 @@ StreamCopy copier(dec, aac); // copy in to out void setup(){ Serial.begin(115200); - AudioToolsLogger.begin(Serial, AudioToolsLogLevel::Info); + AudioLogger::instance().begin(Serial, AudioLogger::Info); - dec.addNotifyAudioChange(portaudio_stream); + dec.setNotifyAudioChange(portaudio_stream); dec.begin(); portaudio_stream.begin(); diff --git a/tests-cmake/codec/adpcm/CMakeLists.txt b/tests-cmake/codec/adpcm/CMakeLists.txt index 53b22eea64..d3e324a4b4 100644 --- a/tests-cmake/codec/adpcm/CMakeLists.txt +++ b/tests-cmake/codec/adpcm/CMakeLists.txt @@ -4,7 +4,6 @@ cmake_minimum_required(VERSION 3.20) project(adpcm-test) set (CMAKE_CXX_STANDARD 11) set (DCMAKE_CXX_FLAGS "-Werror") - if (CMAKE_CXX_COMPILER_ID STREQUAL "Clang") set (CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -fno-omit-frame-pointer -fsanitize=address") set (CMAKE_LINKER_FLAGS_DEBUG "${CMAKE_LINKER_FLAGS_DEBUG} -fno-omit-frame-pointer -fsanitize=address") @@ -12,15 +11,6 @@ endif() include(FetchContent) -# Add Portaduio for desktop build -add_compile_options(-DIS_DESKTOP) -FetchContent_Declare(portaudio GIT_REPOSITORY "https://github.com/PortAudio/portaudio.git" GIT_TAG v19.7.0 ) -FetchContent_GetProperties(portaudio) -if(NOT portaudio_POPULATED) - FetchContent_Populate(portaudio) - add_subdirectory(${portaudio_SOURCE_DIR} ${CMAKE_CURRENT_BINARY_DIR}/portaudio) -endif() - # Build with arduino-audio-tools if(CMAKE_SOURCE_DIR STREQUAL CMAKE_CURRENT_SOURCE_DIR) add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/../../.. ${CMAKE_CURRENT_BINARY_DIR}/arduino-audio-tools ) @@ -35,10 +25,10 @@ if(NOT adpcm_ffmpeg) endif() # build sketch as executable -add_executable (adpcm-test adpcm.cpp) +add_executable (adpcm-test adpcm.cpp ../../main.cpp) # set preprocessor defines -target_compile_definitions(adpcm-test PUBLIC -DARDUINO -DUSE_PORTAUDIO -DIS_DESKTOP) +target_compile_definitions(adpcm-test PUBLIC -DARDUINO -DEXIT_ON_STOP -DUSE_PORTAUDIO -DIS_DESKTOP) # specify libraries target_link_libraries(adpcm-test portaudio arduino_emulator adpcm_ffmpeg arduino-audio-tools) diff --git a/tests-cmake/codec/adpcm/adpcm.cpp b/tests-cmake/codec/adpcm/adpcm.cpp index 5b8d2d2ffd..234a771a4e 100644 --- a/tests-cmake/codec/adpcm/adpcm.cpp +++ b/tests-cmake/codec/adpcm/adpcm.cpp @@ -9,22 +9,22 @@ * */ #include "AudioTools.h" -#include "AudioTools/AudioCodecs/CodecADPCM.h" // https://github.com/pschatzmann/adpcm -#include "AudioTools/AudioLibs/PortAudioStream.h" +#include "AudioCodecs/CodecADPCM.h" // https://github.com/pschatzmann/adpcm +#include "AudioLibs/PortAudioStream.h" AudioInfo info(16000, 2, 16); SineWaveGenerator sineWave( 32000); // subclass of SoundGenerator with max amplitude of 32000 GeneratedSoundStream sound( sineWave); // Stream generated from sine wave //I2SStream out; PortAudioStream out; -//CsvOutput out(Serial); +//CsvStream out(Serial); EncodedAudioStream decoder(&out, new ADPCMDecoder(AV_CODEC_ID_ADPCM_IMA_WAV)); // encode and write EncodedAudioStream encoder(&decoder, new ADPCMEncoder(AV_CODEC_ID_ADPCM_IMA_WAV)); // encode and write StreamCopy copier(encoder, sound); void setup() { Serial.begin(115200); - AudioToolsLogger.begin(Serial, AudioToolsLogLevel::Warning); + AudioLogger::instance().begin(Serial, AudioLogger::Warning); // start I2S Serial.println("starting I2S..."); diff --git a/tests-cmake/codec/alac/CMakeLists.txt b/tests-cmake/codec/alac/CMakeLists.txt deleted file mode 100644 index 207dfb4cdc..0000000000 --- a/tests-cmake/codec/alac/CMakeLists.txt +++ /dev/null @@ -1,39 +0,0 @@ -cmake_minimum_required(VERSION 3.20) - -# set the project name -project(alac) -set (CMAKE_CXX_STANDARD 11) -set (DCMAKE_CXX_FLAGS "-Werror") - -include(FetchContent) -option(BUILD_SHARED_LIBS "Build using shared libraries" OFF) - -# Build with arduino-fdk-aac -FetchContent_Declare(codec-alac GIT_REPOSITORY "https://github.com/pschatzmann/codec-alac.git" GIT_TAG main ) -FetchContent_GetProperties(codec-alac) -if(NOT codec-alac_POPULATED) - FetchContent_Populate(codec-alac) - add_subdirectory(${codec-alac_SOURCE_DIR} ${CMAKE_CURRENT_BINARY_DIR}/codec-alac) -endif() - -# provide audio-tools -if(CMAKE_SOURCE_DIR STREQUAL CMAKE_CURRENT_SOURCE_DIR) - add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/../../.. ${CMAKE_CURRENT_BINARY_DIR}/arduino-audio-tools ) -endif() - -# build sketch as executable -set_source_files_properties(alac.ino PROPERTIES LANGUAGE CXX) -add_executable (alac alac.ino) - -# set preprocessor defines -target_compile_definitions(arduino_emulator PUBLIC -DDEFINE_MAIN) -target_compile_definitions(alac PUBLIC -DARDUINO -DIS_DESKTOP) -target_compile_options(alac PRIVATE -Wno-multichar) - - -# set compile optioins -target_compile_options(arduino-audio-tools INTERFACE -Wno-inconsistent-missing-override) - -# specify libraries -target_link_libraries(alac PRIVATE codec-alac arduino_emulator arduino-audio-tools ) - diff --git a/tests-cmake/codec/alac/alac.ino b/tests-cmake/codec/alac/alac.ino deleted file mode 100644 index 6fb774c4a9..0000000000 --- a/tests-cmake/codec/alac/alac.ino +++ /dev/null @@ -1,57 +0,0 @@ -/** - * @file test-codec-alac.ino - * @author Phil Schatzmann - * @brief generate sine wave -> encoder -> decoder -> audiokit (i2s) - * @version 0.1 - * - * @copyright Copyright (c) 2025 - * - */ -#include "AudioTools.h" -#include "AudioTools/AudioCodecs/CodecALAC.h" - -//SET_LOOP_TASK_STACK_SIZE(16*1024); // 16KB - -AudioInfo info(44100, 2, 16); -SineWaveGenerator sineWave( 32000); // subclass of SoundGenerator with max amplitude of 32000 -GeneratedSoundStream sound( sineWave); // Stream generated from sine wave -CsvOutput out(Serial); -EncoderALAC enc_alac; -DecoderALAC dec_alac; -CodecNOP dec_nop; -EncodedAudioStream decoder(&out, &dec_alac); // encode and write -EncodedAudioStream encoder(&decoder, &enc_alac); // encode and write -StreamCopy copier(encoder, sound); - -void setup() { - Serial.begin(115200); - AudioToolsLogger.begin(Serial, AudioToolsLogLevel::Debug); - - // start Output - Serial.println("starting Output..."); - auto cfgi = out.defaultConfig(TX_MODE); - cfgi.copyFrom(info); - out.begin(cfgi); - - // Setup sine wave - sineWave.begin(info, N_B4); - - // start encoder - encoder.begin(info); - - // optionally copy config from encoder to decoder - // since decoder already has audio info and frames - //dec_alac.setCodecConfig(enc_alac.config()); - //dec_alac.setCodecConfig(enc_alac.binaryConfig()); - - // start decoder - decoder.begin(info); - - - Serial.println("Test started..."); -} - - -void loop() { - copier.copy(); -} \ No newline at end of file diff --git a/tests-cmake/codec/container-avi-movie/CMakeLists.txt b/tests-cmake/codec/container-avi-movie/CMakeLists.txt index 53755c56d9..f7e330331c 100644 --- a/tests-cmake/codec/container-avi-movie/CMakeLists.txt +++ b/tests-cmake/codec/container-avi-movie/CMakeLists.txt @@ -4,18 +4,11 @@ cmake_minimum_required(VERSION 3.20) project(container-avi-movie) set (CMAKE_CXX_STANDARD 11) set (DCMAKE_CXX_FLAGS "-Werror") - -# add_compile_options(-Wstack-usage=1024) -include(FetchContent) - -# Add Portaduio for desktop build -add_compile_options(-DIS_DESKTOP) -FetchContent_Declare(portaudio GIT_REPOSITORY "https://github.com/PortAudio/portaudio.git" GIT_TAG v19.7.0 ) -FetchContent_GetProperties(portaudio) -if(NOT portaudio_POPULATED) - FetchContent_Populate(portaudio) - add_subdirectory(${portaudio_SOURCE_DIR} ${CMAKE_CURRENT_BINARY_DIR}/portaudio) +if (CMAKE_CXX_COMPILER_ID STREQUAL "Clang") + set (CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -fno-omit-frame-pointer -fsanitize=address") + set (CMAKE_LINKER_FLAGS_DEBUG "${CMAKE_LINKER_FLAGS_DEBUG} -fno-omit-frame-pointer -fsanitize=address") endif() +include(FetchContent) # Build with arduino-audio-tools if(CMAKE_SOURCE_DIR STREQUAL CMAKE_CURRENT_SOURCE_DIR) @@ -30,7 +23,7 @@ include_directories( ${OpenCV_INCLUDE_DIRS} ) add_executable (container-avi-movie container-avi-movie.cpp) # set preprocessor defines -target_compile_definitions(container-avi-movie PUBLIC -DUSE_PORTAUDIO -DIS_MIN_DESKTOP) +target_compile_definitions(container-avi-movie PUBLIC -DEXIT_ON_STOP -DUSE_PORTAUDIO -DIS_MIN_DESKTOP) target_include_directories(container-avi-movie PRIVATE "${arduino_emulator_SOURCE_DIR}/ArduinoCore-Linux/libraries/SdFat" ) # OS/X might need this setting for core audio diff --git a/tests-cmake/codec/container-avi-movie/container-avi-movie.cpp b/tests-cmake/codec/container-avi-movie/container-avi-movie.cpp index f9f79817c1..1b034e207f 100644 --- a/tests-cmake/codec/container-avi-movie/container-avi-movie.cpp +++ b/tests-cmake/codec/container-avi-movie/container-avi-movie.cpp @@ -15,9 +15,9 @@ */ #include "AudioTools.h" -#include "AudioTools/AudioCodecs/ContainerAVI.h" -#include "AudioTools/AudioLibs/Desktop/File.h" -#include "AudioTools/AudioLibs/PortAudioStream.h" +#include "AudioCodecs/ContainerAVI.h" +#include "AudioLibs/Desktop/File.h" +#include "AudioLibs/PortAudioStream.h" #include "Video/JpegOpenCV.h" PortAudioStream out; // Output of sound on desktop @@ -30,7 +30,7 @@ VideoAudioBufferedSync videoSync(10*1024, -20); void setup() { - AudioToolsLogger.begin(Serial, AudioToolsLogLevel::Info); + AudioLogger::instance().begin(Serial, AudioLogger::Info); file.open("/data/resources/test1.avi",FILE_READ); codec.setOutputVideoStream(jpegDisplay); codec.setVideoAudioSync(&videoSync); diff --git a/tests-cmake/codec/container-avi/CMakeLists.txt b/tests-cmake/codec/container-avi/CMakeLists.txt index 5733b73133..da707f516a 100644 --- a/tests-cmake/codec/container-avi/CMakeLists.txt +++ b/tests-cmake/codec/container-avi/CMakeLists.txt @@ -4,18 +4,11 @@ cmake_minimum_required(VERSION 3.20) project(container-avi) set (CMAKE_CXX_STANDARD 11) set (DCMAKE_CXX_FLAGS "-Werror") - -# add_compile_options(-Wstack-usage=1024) - -include(FetchContent) - -# Add Portaduio for desktop build -FetchContent_Declare(portaudio GIT_REPOSITORY "https://github.com/PortAudio/portaudio.git" GIT_TAG v19.7.0 ) -FetchContent_GetProperties(portaudio) -if(NOT portaudio_POPULATED) - FetchContent_Populate(portaudio) - add_subdirectory(${portaudio_SOURCE_DIR} ${CMAKE_CURRENT_BINARY_DIR}/portaudio) +if (CMAKE_CXX_COMPILER_ID STREQUAL "Clang") + set (CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -fno-omit-frame-pointer -fsanitize=address") + set (CMAKE_LINKER_FLAGS_DEBUG "${CMAKE_LINKER_FLAGS_DEBUG} -fno-omit-frame-pointer -fsanitize=address") endif() +include(FetchContent) # Build with arduino-audio-tools if(CMAKE_SOURCE_DIR STREQUAL CMAKE_CURRENT_SOURCE_DIR) @@ -35,7 +28,7 @@ endif() add_executable (container-avi container-avi.cpp) # set preprocessor defines -target_compile_definitions(container-avi PUBLIC -DUSE_PORTAUDIO -DIS_MIN_DESKTOP -DHELIX_PRINT) +target_compile_definitions(container-avi PUBLIC -DEXIT_ON_STOP -DUSE_PORTAUDIO -DIS_MIN_DESKTOP -DHELIX_PRINT) target_include_directories(container-avi PRIVATE "${arduino_emulator_SOURCE_DIR}/ArduinoCore-Linux/libraries/SdFat" ) # specify libraries diff --git a/tests-cmake/codec/container-avi/container-avi.cpp b/tests-cmake/codec/container-avi/container-avi.cpp index 748511e10a..57e17c05b9 100644 --- a/tests-cmake/codec/container-avi/container-avi.cpp +++ b/tests-cmake/codec/container-avi/container-avi.cpp @@ -9,9 +9,9 @@ * */ #include "AudioTools.h" -#include "AudioTools/AudioCodecs/ContainerAVI.h" -#include "AudioTools/AudioLibs/Desktop/File.h" -#include "AudioTools/AudioLibs/PortAudioStream.h" +#include "AudioCodecs/ContainerAVI.h" +#include "AudioLibs/Desktop/File.h" +#include "AudioLibs/PortAudioStream.h" //CsvOutput out; PortAudioStream out; // Output of sound on desktop @@ -22,7 +22,7 @@ File file; StreamCopy copier(riff, file); void setup() { - AudioToolsLogger.begin(Serial, AudioToolsLogLevel::Info); + AudioLogger::instance().begin(Serial, AudioLogger::Info); file.open("/data/resources/test1.avi",FILE_READ); } diff --git a/tests-cmake/codec/container-binary/CMakeLists.txt b/tests-cmake/codec/container-binary/CMakeLists.txt index d09abbd10c..0e47631d1d 100644 --- a/tests-cmake/codec/container-binary/CMakeLists.txt +++ b/tests-cmake/codec/container-binary/CMakeLists.txt @@ -4,7 +4,10 @@ cmake_minimum_required(VERSION 3.20) project(container-binary) set (CMAKE_CXX_STANDARD 11) set (DCMAKE_CXX_FLAGS "-Werror") -# add_compile_options(-Wstack-usage=1024) +if (CMAKE_CXX_COMPILER_ID STREQUAL "Clang") + set (CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -fno-omit-frame-pointer -fsanitize=address") + set (CMAKE_LINKER_FLAGS_DEBUG "${CMAKE_LINKER_FLAGS_DEBUG} -fno-omit-frame-pointer -fsanitize=address") +endif() include(FetchContent) # Build with arduino-audio-tools @@ -17,7 +20,7 @@ endif() add_executable (container-binary container-binary.cpp) # set preprocessor defines -target_compile_definitions(container-binary PUBLIC -DIS_MIN_DESKTOP) +target_compile_definitions(container-binary PUBLIC -DEXIT_ON_STOP -DIS_MIN_DESKTOP) # OS/X might need this setting for core audio #target_compile_definitions(portaudio PUBLIC -DPA_USE_COREAUDIO=1) diff --git a/tests-cmake/codec/container-binary/container-binary.cpp b/tests-cmake/codec/container-binary/container-binary.cpp index 70e91e6d1d..4f02456931 100644 --- a/tests-cmake/codec/container-binary/container-binary.cpp +++ b/tests-cmake/codec/container-binary/container-binary.cpp @@ -9,7 +9,7 @@ * */ #include "AudioTools.h" -#include "AudioTools/AudioCodecs/ContainerBinary.h" +#include "AudioCodecs/ContainerBinary.h" AudioInfo info(8000,1,16); SineWaveGenerator sineWave( 32000); // subclass of SoundGenerator with max amplitude of 32000 @@ -20,7 +20,7 @@ EncodedAudioStream encoder(&out,new BinaryContainerEncoder()); // encode and wri StreamCopy copier(encoder, sound); void setup() { - AudioToolsLogger.begin(Serial, AudioToolsLogLevel::Warning); + AudioLogger::instance().begin(Serial, AudioLogger::Warning); // start Serial.println("starting..."); diff --git a/tests-cmake/codec/container-m4a/CMakeLists.txt b/tests-cmake/codec/container-m4a/CMakeLists.txt deleted file mode 100644 index 4a34bfc6b6..0000000000 --- a/tests-cmake/codec/container-m4a/CMakeLists.txt +++ /dev/null @@ -1,52 +0,0 @@ -cmake_minimum_required(VERSION 3.20) - -# set the project name -project(m4a) -set (CMAKE_CXX_STANDARD 11) -set (DCMAKE_CXX_FLAGS "-Werror") - -include(FetchContent) -option(BUILD_SHARED_LIBS "Build using shared libraries" OFF) - -# Build with alac -FetchContent_Declare(codec-alac GIT_REPOSITORY "https://github.com/pschatzmann/codec-alac.git" GIT_TAG main ) -FetchContent_GetProperties(codec-alac) -if(NOT codec-alac_POPULATED) - FetchContent_Populate(codec-alac) - add_subdirectory(${codec-alac_SOURCE_DIR} ${CMAKE_CURRENT_BINARY_DIR}/codec-alac) -endif() - -# Build with libhelix -FetchContent_Declare(helix GIT_REPOSITORY "https://github.com/pschatzmann/arduino-libhelix.git" GIT_TAG main ) -FetchContent_GetProperties(helix) -if(NOT helix_POPULATED) - FetchContent_Populate(helix) - add_subdirectory(${helix_SOURCE_DIR} ${CMAKE_CURRENT_BINARY_DIR}/helix) -endif() - - -# provide audio-tools -if(CMAKE_SOURCE_DIR STREQUAL CMAKE_CURRENT_SOURCE_DIR) - add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/../../.. ${CMAKE_CURRENT_BINARY_DIR}/arduino-audio-tools ) -endif() - -# build sketch as executable -set_source_files_properties(m4a.ino PROPERTIES LANGUAGE CXX) -add_executable (m4a m4a.ino) - -# set preprocessor defines -target_compile_definitions(arduino_emulator PUBLIC -DDEFINE_MAIN) -target_compile_definitions(m4a PUBLIC -DARDUINO -DIS_DESKTOP) -target_compile_options(m4a PRIVATE -Wno-multichar) - - -# set compile optioins -target_compile_options(arduino-audio-tools INTERFACE -Wno-inconsistent-missing-override) -#target_compile_definitions(arduino-audio-tools INTERFACE -DUSE_ALLOCATOR) - -# specify libraries -target_link_libraries(m4a PRIVATE - codec-alac - arduino_emulator - arduino_helix - arduino-audio-tools) \ No newline at end of file diff --git a/tests-cmake/codec/container-m4a/m4a.ino b/tests-cmake/codec/container-m4a/m4a.ino deleted file mode 100644 index 9cd5872e74..0000000000 --- a/tests-cmake/codec/container-m4a/m4a.ino +++ /dev/null @@ -1,63 +0,0 @@ -/** - * @file test-codec-alac.ino - * @author Phil Schatzmann - * @brief generate sine wave -> encoder -> decoder -> audiokit (i2s) - * @version 0.1 - * - * @copyright Copyright (c) 2025 - * - */ -#include "AudioTools.h" -#include "AudioTools/AudioCodecs/CodecALAC.h" -#include "AudioTools/AudioCodecs/CodecAACHelix.h" -#include "AudioTools/AudioCodecs/ContainerM4A.h" -#include "AudioTools/AudioCodecs/MultiDecoder.h" -#include "SD.h" - -MultiDecoder multi_decoder; -ContainerM4A dec_m4a(multi_decoder); -AACDecoderHelix dec_aac; -DecoderALAC dec_alac; -CsvOutput out(Serial); -EncodedAudioOutput decoder_output(&out, &dec_m4a); -File file; -StreamCopy copier(decoder_output, file); - -void setup() { - Serial.begin(115200); - AudioToolsLogger.begin(Serial, AudioToolsLogLevel::Info); - - if (!SD.begin()){ - Serial.println("SD Card initialization failed!"); - return; - } - - file = SD.open("/home/pschatzmann/Music/m4a/aac.m4a"); - if (!file) { - Serial.println("Failed to open file!"); - return; - } - - // mp4 supports alac and aac - multi_decoder.addDecoder(dec_alac,"audio/alac"); - multi_decoder.addDecoder(dec_aac,"audio/aac"); - - // start decoder output - if(!decoder_output.begin()) { - Serial.println("Failed to start decoder output!"); - return; - } - - // start csv output - if (!out.begin()){ - Serial.println("Failed to start CSV output!"); - return; - } - - Serial.println("M4A decoding started..."); -} - - -void loop() { - copier.copy(); -} \ No newline at end of file diff --git a/tests-cmake/codec/hls/CMakeLists.txt b/tests-cmake/codec/hls/CMakeLists.txt deleted file mode 100644 index aa6a9a6dee..0000000000 --- a/tests-cmake/codec/hls/CMakeLists.txt +++ /dev/null @@ -1,54 +0,0 @@ - -cmake_minimum_required(VERSION 3.20) - -# set the project name -project(hls) -set (CMAKE_CXX_STANDARD 11) -set (DCMAKE_CXX_FLAGS "-Werror") - -include(FetchContent) - -# Activate Emulator and Portaudio -set(ADD_ARDUINO_EMULATOR ON CACHE BOOL "Add Arduino Emulator Library") -set(ADD_PORTAUDIO OFF CACHE BOOL "Add Portaudio Library") - -# Build with arduino-audio-tools -if(CMAKE_SOURCE_DIR STREQUAL CMAKE_CURRENT_SOURCE_DIR) -add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/../../.. ${CMAKE_CURRENT_BINARY_DIR}/arduino-audio-tools ) -endif() - -# Build with tms -FetchContent_Declare(tsdemux GIT_REPOSITORY "https://github.com/pschatzmann/arduino-tsdemux" ) -FetchContent_GetProperties(tsdemux) -if(NOT tsdemux_POPULATED) - FetchContent_Populate(tsdemux) - add_subdirectory(${tsdemux_SOURCE_DIR} tsdemux) -endif() - -FetchContent_Declare(helix GIT_REPOSITORY "https://github.com/pschatzmann/arduino-libhelix.git" GIT_TAG main ) -FetchContent_GetProperties(helix) -if(NOT helix_POPULATED) - FetchContent_Populate(helix helix) - add_subdirectory(${helix_SOURCE_DIR} ${CMAKE_CURRENT_BINARY_DIR}/helix) -endif() - -# Download miniaudio.h -file(DOWNLOAD https://raw.githubusercontent.com/mackron/miniaudio/master/miniaudio.h - ${CMAKE_CURRENT_SOURCE_DIR}/miniaudio.h) - -# build sketch as executable -set_source_files_properties(hls.ino PROPERTIES LANGUAGE CXX) -add_executable (hls hls.cpp ) - -# set preprocessor defines -target_compile_definitions(hls PUBLIC -DARDUINO -DIS_DESKTOP -DHELIX_PRINT) - -# access to miniaudio in sketch directory -target_include_directories(hls PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}) - -# specify libraries -target_link_libraries(hls arduino-audio-tools arduino_emulator tsdemux arduino_helix) - - - - diff --git a/tests-cmake/codec/hls/hls.cpp b/tests-cmake/codec/hls/hls.cpp deleted file mode 100644 index 021cb9589c..0000000000 --- a/tests-cmake/codec/hls/hls.cpp +++ /dev/null @@ -1,51 +0,0 @@ -#include "AudioTools.h" -#include "AudioTools/AudioCodecs/CodecTSDemux.h" -#include "AudioTools/AudioCodecs/CodecADTS.h" -#include "AudioTools/AudioCodecs/CodecHelix.h" -#include "AudioTools/AudioCodecs/CodecMTS.h" -#include "AudioTools/AudioLibs/MiniAudioStream.h" -#include "AudioTools/AudioLibs/HLSStream.h" - -AudioInfo info(48000,2,16); -HLSStream hls_stream("NA", "NA"); -// HexDumpOutput hex(Serial); -// NullStream null; -//CsvOutput out(Serial, 2); // Or use StdOuput -MiniAudioStream out; -//MTSDecoder mts; -//ADTSDecoder adts; -AACDecoderHelix aac; -MP3DecoderHelix mp3; -MultiDecoder multi; -//EncodedAudioStream aac_stream(&out, &aac); -//EncodedAudioStream adts_stream(&aac_stream, &adts); -//EncodedAudioStream mts_stream(&adts_stream, &mts); -EncodedAudioStream dec(&out, &mp3); -StreamCopy copier(dec, hls_stream); - -// Arduino Setup -void setup(void) { - //Serial.begin(115200); - AudioToolsLogger.begin(Serial, AudioToolsLogLevel::Info); - //hls_stream.setLogLevel(AudioLogger::Debug); // hls_stream is quite chatty at Info - //adts_stream.setLogLevel(AudioLogger::Debug); - //mts_stream.setLogLevel(AudioLogger::Debug); - - aac.setAudioInfoNotifications(false); - - auto cfg = out.defaultConfig(TX_MODE); - cfg.copyFrom(info); - out.begin(); - - //mts_stream.begin(); - //aac_stream.begin(); - //adts_stream.begin(); - - if (hls_stream.begin("http://audio-edge-cmc51.fra.h.radiomast.io/ref-128k-mp3-stereo/hls.m3u8")) - Serial.println("playing..."); -} - -// Arduino loop -void loop() { - copier.copy(); -} diff --git a/tests-cmake/codec/m4a-extractor/CMakeLists.txt b/tests-cmake/codec/m4a-extractor/CMakeLists.txt deleted file mode 100644 index cdb0131109..0000000000 --- a/tests-cmake/codec/m4a-extractor/CMakeLists.txt +++ /dev/null @@ -1,52 +0,0 @@ -cmake_minimum_required(VERSION 3.20) - -# set the project name -project(m4a-extractor) -set (CMAKE_CXX_STANDARD 11) -set (DCMAKE_CXX_FLAGS "-Werror") - -include(FetchContent) -option(BUILD_SHARED_LIBS "Build using shared libraries" OFF) - -# Build with alac -FetchContent_Declare(codec-alac GIT_REPOSITORY "https://github.com/pschatzmann/codec-alac.git" GIT_TAG main ) -FetchContent_GetProperties(codec-alac) -if(NOT codec-alac_POPULATED) - FetchContent_Populate(codec-alac) - add_subdirectory(${codec-alac_SOURCE_DIR} ${CMAKE_CURRENT_BINARY_DIR}/codec-alac) -endif() - -# Build with libhelix -FetchContent_Declare(helix GIT_REPOSITORY "https://github.com/pschatzmann/arduino-libhelix.git" GIT_TAG main ) -FetchContent_GetProperties(helix) -if(NOT helix_POPULATED) - FetchContent_Populate(helix) - add_subdirectory(${helix_SOURCE_DIR} ${CMAKE_CURRENT_BINARY_DIR}/helix) -endif() - - -# provide audio-tools -if(CMAKE_SOURCE_DIR STREQUAL CMAKE_CURRENT_SOURCE_DIR) - add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/../../.. ${CMAKE_CURRENT_BINARY_DIR}/arduino-audio-tools ) -endif() - -# build sketch as executable -set_source_files_properties(m4a-extrator.ino PROPERTIES LANGUAGE CXX) -add_executable (m4a-extractor m4a-extrator.ino) - -# set preprocessor defines -target_compile_definitions(arduino_emulator PUBLIC -DDEFINE_MAIN) -target_compile_definitions(m4a-extractor PUBLIC -DARDUINO -DIS_DESKTOP) -target_compile_options(m4a-extractor PRIVATE -Wno-multichar) - - -# set compile optioins -target_compile_options(arduino-audio-tools INTERFACE -Wno-inconsistent-missing-override) -#target_compile_definitions(arduino-audio-tools INTERFACE -DUSE_ALLOCATOR) - -# specify libraries -target_link_libraries(m4a-extractor PRIVATE - codec-alac - arduino_emulator - arduino_helix - arduino-audio-tools) \ No newline at end of file diff --git a/tests-cmake/codec/m4a-extractor/m4a-extrator.ino b/tests-cmake/codec/m4a-extractor/m4a-extrator.ino deleted file mode 100644 index c420e64f7e..0000000000 --- a/tests-cmake/codec/m4a-extractor/m4a-extrator.ino +++ /dev/null @@ -1,55 +0,0 @@ -/** - * @file m4a-extractor.ino - * @author Phil Schatzmann - * @brief Decode M4A file and output to CSV - * @version 0.1 - * - * @copyright Copyright (c) 2025 - * - */ -#include "AudioTools.h" -#include "AudioTools/AudioCodecs/M4AAudioFileDemuxer.h" -#include "AudioTools/AudioCodecs/CodecALAC.h" -#include "AudioTools/AudioCodecs/CodecAACHelix.h" -#include "AudioTools/AudioCodecs/MultiDecoder.h" -#include "SD.h" - -MultiDecoder multi_decoder; -AACDecoderHelix dec_aac; -DecoderALAC dec_alac; -CsvOutput out(Serial); - -//MP4Parser parser; -M4AAudioFileDemuxer demux; -File file; - -void setup() { - Serial.begin(115200); - AudioToolsLogger.begin(Serial, AudioToolsLogLevel::Info); - - if (!SD.begin()) { - Serial.println("SD Card initialization failed!"); - return; - } - - file = SD.open("/home/pschatzmann/Music/m4a/1-07 All You Need Is Love.m4a"); - if (!file) { - Serial.println("Failed to open file!"); - return; - } - - // Setup decoder - multi_decoder.setOutput(out); - multi_decoder.addDecoder(dec_aac, "audio/aac"); - multi_decoder.addDecoder(dec_alac, "audio/alac"); - demux.setDecoder(multi_decoder); - - if (!demux.begin(file)){ - Serial.println("Failed to open demuxer!"); - return; - } -} - -void loop() { - demux.copy(); -} \ No newline at end of file diff --git a/tests-cmake/codec/mp3-helix/CMakeLists.txt b/tests-cmake/codec/mp3-helix/CMakeLists.txt index 4a6c8c6b8f..255a258cb5 100644 --- a/tests-cmake/codec/mp3-helix/CMakeLists.txt +++ b/tests-cmake/codec/mp3-helix/CMakeLists.txt @@ -4,20 +4,13 @@ cmake_minimum_required(VERSION 3.20) project(mp3-helix) set (CMAKE_CXX_STANDARD 11) set (DCMAKE_CXX_FLAGS "-Werror") - -# add_compile_options(-Wstack-usage=1024) +if (CMAKE_CXX_COMPILER_ID STREQUAL "Clang") + set (CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -fno-omit-frame-pointer -fsanitize=address") + set (CMAKE_LINKER_FLAGS_DEBUG "${CMAKE_LINKER_FLAGS_DEBUG} -fno-omit-frame-pointer -fsanitize=address") +endif() include(FetchContent) -# Add Portaduio for desktop build -add_compile_options(-DIS_DESKTOP) -FetchContent_Declare(portaudio GIT_REPOSITORY "https://github.com/PortAudio/portaudio.git" GIT_TAG v19.7.0 ) -FetchContent_GetProperties(portaudio) -if(NOT portaudio_POPULATED) - FetchContent_Populate(portaudio) - add_subdirectory(${portaudio_SOURCE_DIR} ${CMAKE_CURRENT_BINARY_DIR}/portaudio) -endif() - # Build with arduino-audio-tools if(CMAKE_SOURCE_DIR STREQUAL CMAKE_CURRENT_SOURCE_DIR) add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/../../.. ${CMAKE_CURRENT_BINARY_DIR}/arduino-audio-tools ) @@ -32,9 +25,9 @@ if(NOT helix_POPULATED) endif() # build sketch as executable -add_executable (mp3-helix mp3-helix.cpp) +add_executable (mp3-helix mp3-helix.cpp ../../main.cpp) # set preprocessor defines -target_compile_definitions(mp3-helix PUBLIC -DARDUINO -DUSE_PORTAUDIO -DIS_DESKTOP) +target_compile_definitions(mp3-helix PUBLIC -DARDUINO -DEXIT_ON_STOP -DUSE_PORTAUDIO -DIS_DESKTOP) # OS/X might need this setting for core audio #target_compile_definitions(portaudio PUBLIC -DPA_USE_COREAUDIO=1) diff --git a/tests-cmake/codec/mp3-helix/mp3-helix.cpp b/tests-cmake/codec/mp3-helix/mp3-helix.cpp index d6504f1034..863ed6528f 100644 --- a/tests-cmake/codec/mp3-helix/mp3-helix.cpp +++ b/tests-cmake/codec/mp3-helix/mp3-helix.cpp @@ -1,8 +1,8 @@ // Simple wrapper for Arduino sketch to compilable with cpp in cmake #include "Arduino.h" #include "AudioTools.h" -#include "AudioTools/AudioCodecs/CodecMP3Helix.h" -#include "AudioTools/AudioLibs/PortAudioStream.h" +#include "AudioCodecs/CodecMP3Helix.h" +#include "AudioLibs/PortAudioStream.h" #include "BabyElephantWalk60_mp3.h" using namespace audio_tools; @@ -14,9 +14,9 @@ StreamCopy copier(dec, mp3); // copy in to out void setup(){ Serial.begin(115200); - AudioToolsLogger.begin(Serial, AudioToolsLogLevel::Info); + AudioLogger::instance().begin(Serial, AudioLogger::Info); - dec.addNotifyAudioChange(portaudio_stream); + dec.setNotifyAudioChange(portaudio_stream); dec.begin(); portaudio_stream.begin(); diff --git a/tests-cmake/codec/mp3-lame/CMakeLists.txt b/tests-cmake/codec/mp3-lame/CMakeLists.txt index 7b9026f055..53c3b4e982 100644 --- a/tests-cmake/codec/mp3-lame/CMakeLists.txt +++ b/tests-cmake/codec/mp3-lame/CMakeLists.txt @@ -25,9 +25,9 @@ if(NOT arduino_liblame_POPULATED) endif() # build sketch as executable -add_executable (mp3-lame mp3-lame.cpp) +add_executable (mp3-lame mp3-lame.cpp ../../main.cpp) # set preprocessor defines -target_compile_definitions(mp3-lame PUBLIC -DARDUINO -DUSE_PORTAUDIO -DIS_DESKTOP) +target_compile_definitions(mp3-lame PUBLIC -DARDUINO -DEXIT_ON_STOP -DUSE_PORTAUDIO -DIS_DESKTOP) # specify libraries target_link_libraries(mp3-lame portaudio arduino_emulator arduino_liblame arduino-audio-tools) diff --git a/tests-cmake/codec/mp3-lame/mp3-lame.cpp b/tests-cmake/codec/mp3-lame/mp3-lame.cpp index d8d3e7483e..9d61afe537 100644 --- a/tests-cmake/codec/mp3-lame/mp3-lame.cpp +++ b/tests-cmake/codec/mp3-lame/mp3-lame.cpp @@ -1,12 +1,12 @@ // Simple wrapper for Arduino sketch to compilable with cpp in cmake #include "Arduino.h" #include "AudioTools.h" -#include "AudioTools/AudioCodecs/CodecMP3LAME.h" +#include "AudioCodecs/CodecMP3LAME.h" //#include // for rand using namespace audio_tools; -HexDumpOutput out(Serial); +HexDumpStream out(Serial); MP3EncoderLAME mp3(out); AudioInfoLAME info; int16_t buffer[512]; @@ -26,7 +26,7 @@ void loop() { for (int j=0;j<512;j++){ buffer[j] = (rand() % 100) - 50; } - if (mp3.write((uint8_t*)buffer, 512*sizeof(int16_t))){ + if (mp3.write(buffer, 512*sizeof(int16_t))){ out.flush(); Serial.println("512 samples of random data written"); } diff --git a/tests-cmake/codec/mp3-mad/CMakeLists.txt b/tests-cmake/codec/mp3-mad/CMakeLists.txt index 9c290ebe94..79012e8670 100644 --- a/tests-cmake/codec/mp3-mad/CMakeLists.txt +++ b/tests-cmake/codec/mp3-mad/CMakeLists.txt @@ -7,15 +7,6 @@ set (DCMAKE_CXX_FLAGS "-Werror") include(FetchContent) -# Add Portaduio for desktop build -add_compile_options(-DIS_DESKTOP) -FetchContent_Declare(portaudio GIT_REPOSITORY "https://github.com/PortAudio/portaudio.git" GIT_TAG v19.7.0 ) -FetchContent_GetProperties(portaudio) -if(NOT portaudio_POPULATED) - FetchContent_Populate(portaudio) - add_subdirectory(${portaudio_SOURCE_DIR} ${CMAKE_CURRENT_BINARY_DIR}/portaudio) -endif() - # Build with arduino-audio-tools if(CMAKE_SOURCE_DIR STREQUAL CMAKE_CURRENT_SOURCE_DIR) add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/../../.. ${CMAKE_CURRENT_BINARY_DIR}/arduino-audio-tools ) @@ -31,9 +22,9 @@ if(NOT arduino_libmad_POPULATED) endif() # build sketch as executabl -add_executable (mp3-mad mp3-mad.cpp) +add_executable (mp3-mad mp3-mad.cpp ../../main.cpp) # set preprocessor defines -target_compile_definitions(mp3-mad PUBLIC -DARDUINO -DUSE_PORTAUDIO -DIS_DESKTOP ) +target_compile_definitions(mp3-mad PUBLIC -DARDUINO -DEXIT_ON_STOP -DUSE_PORTAUDIO -DIS_DESKTOP ) # specify libraries target_link_libraries(mp3-mad portaudio arduino_emulator arduino_libmad arduino-audio-tools ) diff --git a/tests-cmake/codec/mp3-mad/mp3-mad.cpp b/tests-cmake/codec/mp3-mad/mp3-mad.cpp index 66628a7f86..fb9c32cf18 100644 --- a/tests-cmake/codec/mp3-mad/mp3-mad.cpp +++ b/tests-cmake/codec/mp3-mad/mp3-mad.cpp @@ -1,8 +1,8 @@ // Simple wrapper for Arduino sketch to compilable with cpp in cmake #include "Arduino.h" #include "AudioTools.h" -#include "AudioTools/AudioCodecs/CodecMP3MAD.h" -#include "AudioTools/AudioLibs/PortAudioStream.h" +#include "AudioCodecs/CodecMP3MAD.h" +#include "AudioLibs/PortAudioStream.h" #include "BabyElephantWalk60_mp3.h" using namespace audio_tools; @@ -14,9 +14,9 @@ StreamCopy copier(dec, mp3); // copy in to out void setup(){ Serial.begin(115200); - AudioToolsLogger.begin(Serial, AudioToolsLogLevel::Info); + AudioLogger::instance().begin(Serial, AudioLogger::Info); - dec.addNotifyAudioChange(portaudio_stream); + dec.setNotifyAudioChange(portaudio_stream); dec.begin(); portaudio_stream.begin(); diff --git a/tests-cmake/codec/mp3-metadata/CMakeLists.txt b/tests-cmake/codec/mp3-metadata/CMakeLists.txt index 7cff3ef5b1..05f205b1c6 100644 --- a/tests-cmake/codec/mp3-metadata/CMakeLists.txt +++ b/tests-cmake/codec/mp3-metadata/CMakeLists.txt @@ -4,7 +4,10 @@ cmake_minimum_required(VERSION 3.20) project(mp3-metadata) set (CMAKE_CXX_STANDARD 11) set (DCMAKE_CXX_FLAGS "-Werror") -# add_compile_options(-Wstack-usage=1024) +if (CMAKE_CXX_COMPILER_ID STREQUAL "Clang") + set (CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -fno-omit-frame-pointer -fsanitize=address") + set (CMAKE_LINKER_FLAGS_DEBUG "${CMAKE_LINKER_FLAGS_DEBUG} -fno-omit-frame-pointer -fsanitize=address") +endif() # Build with arduino-audio-tools if(CMAKE_SOURCE_DIR STREQUAL CMAKE_CURRENT_SOURCE_DIR) @@ -12,10 +15,10 @@ if(CMAKE_SOURCE_DIR STREQUAL CMAKE_CURRENT_SOURCE_DIR) endif() # build sketch as executable -add_executable (mp3-metadata mp3-metadata.cpp) +add_executable (mp3-metadata mp3-metadata.cpp ../../main.cpp) # set preprocessor defines -target_compile_definitions(mp3-metadata PUBLIC -DIS_DESKTOP) +target_compile_definitions(mp3-metadata PUBLIC -DEXIT_ON_STOP -DIS_DESKTOP) # specify libraries target_link_libraries(mp3-metadata portaudio arduino_emulator arduino-audio-tools) diff --git a/tests-cmake/codec/mp3-metadata/mp3-metadata.cpp b/tests-cmake/codec/mp3-metadata/mp3-metadata.cpp index 5cad3fa347..5ab2f51fb7 100644 --- a/tests-cmake/codec/mp3-metadata/mp3-metadata.cpp +++ b/tests-cmake/codec/mp3-metadata/mp3-metadata.cpp @@ -6,7 +6,7 @@ using namespace audio_tools; MemoryStream mp3(sample_12s_mp3, sample_12s_mp3_len); -MetaDataOutput out; +MetaDataPrint out; StreamCopy copier(out, mp3); // copy in to out bool title_printed = false; @@ -21,7 +21,7 @@ void printMetaData(MetaDataType type, const char* str, int len){ void setup(){ Serial.begin(115200); - AudioToolsLogger.begin(Serial, AudioToolsLogLevel::Info); + AudioLogger::instance().begin(Serial, AudioLogger::Info); out.setCallback(printMetaData); out.begin(); diff --git a/tests-cmake/codec/mp3-mini/CMakeLists.txt b/tests-cmake/codec/mp3-mini/CMakeLists.txt index a4aaa2daf2..d22850906d 100644 --- a/tests-cmake/codec/mp3-mini/CMakeLists.txt +++ b/tests-cmake/codec/mp3-mini/CMakeLists.txt @@ -8,15 +8,6 @@ set (DCMAKE_CXX_FLAGS "-Werror") include(FetchContent) option(BUILD_SHARED_LIBS "Build using shared libraries" OFF) -# Add Portaduio for desktop build -add_compile_options(-DIS_DESKTOP) -FetchContent_Declare(portaudio GIT_REPOSITORY "https://github.com/PortAudio/portaudio.git" GIT_TAG v19.7.0 ) -FetchContent_GetProperties(portaudio) -if(NOT portaudio_POPULATED) - FetchContent_Populate(portaudio) - add_subdirectory(${portaudio_SOURCE_DIR} ${CMAKE_CURRENT_BINARY_DIR}/portaudio) -endif() - # provide minimp3 FetchContent_Declare(arduino_minimp3 GIT_REPOSITORY "https://github.com/pschatzmann/arduino-minimp3.git" ) FetchContent_GetProperties(arduino_minimp3) @@ -37,7 +28,7 @@ add_executable (mp3_mini mp3-mini.ino) # set preprocessor defines target_compile_definitions(arduino_emulator PUBLIC -DDEFINE_MAIN) target_compile_definitions(arduino_minimp3 INTERFACE -DARDUINO -DMINIMP3_NO_SIMD) -target_compile_definitions(mp3_mini PUBLIC -DARDUINO -DIS_DESKTOP) +target_compile_definitions(mp3_mini PUBLIC -DARDUINO -DIS_DESKTOP -DEXIT_ON_STOP) # set compile optioins target_compile_options(portaudio PRIVATE -Wno-deprecated -Wno-inconsistent-missing-override) diff --git a/tests-cmake/codec/mp3-mini/mp3-mini.ino b/tests-cmake/codec/mp3-mini/mp3-mini.ino index 2a2c45a87f..3f98e3ad52 100644 --- a/tests-cmake/codec/mp3-mini/mp3-mini.ino +++ b/tests-cmake/codec/mp3-mini/mp3-mini.ino @@ -4,8 +4,8 @@ #include "Arduino.h" #include "AudioTools.h" #include "BabyElephantWalk60_mp3.h" -#include "AudioTools/AudioLibs/PortAudioStream.h" -#include "AudioTools/AudioCodecs/CodecMP3Mini.h" +#include "AudioLibs/PortAudioStream.h" +#include "AudioCodecs/CodecMP3Mini.h" MemoryStream mp3(BabyElephantWalk60_mp3, BabyElephantWalk60_mp3_len); PortAudioStream out; // Output of sound on desktop @@ -14,7 +14,7 @@ StreamCopy copier(dec, mp3); // copy in to out void setup(){ Serial.begin(115200); - AudioToolsLogger.begin(Serial, AudioToolsLogLevel::Info); + AudioLogger::instance().begin(Serial, AudioLogger::Info); out.begin(); mp3.begin(); diff --git a/tests-cmake/codec/mp4-parser/CMakeLists.txt b/tests-cmake/codec/mp4-parser/CMakeLists.txt deleted file mode 100644 index 74ee0db8da..0000000000 --- a/tests-cmake/codec/mp4-parser/CMakeLists.txt +++ /dev/null @@ -1,32 +0,0 @@ -cmake_minimum_required(VERSION 3.20) - -# set the project name -project(mp4-parser) -set (CMAKE_CXX_STANDARD 11) -set (DCMAKE_CXX_FLAGS "-Werror") - -include(FetchContent) -option(BUILD_SHARED_LIBS "Build using shared libraries" OFF) - -# provide audio-tools -if(CMAKE_SOURCE_DIR STREQUAL CMAKE_CURRENT_SOURCE_DIR) - add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/../../.. ${CMAKE_CURRENT_BINARY_DIR}/arduino-audio-tools ) -endif() - -# build sketch as executable -set_source_files_properties(mp4-parser.ino PROPERTIES LANGUAGE CXX) -add_executable (mp4-parser mp4-parser.ino) - -# set preprocessor defines -target_compile_definitions(arduino_emulator PUBLIC -DDEFINE_MAIN) -target_compile_definitions(mp4-parser PUBLIC -DARDUINO -DIS_DESKTOP) -target_compile_options(mp4-parser PRIVATE -Wno-multichar) - - -# set compile optioins -target_compile_options(arduino-audio-tools INTERFACE -Wno-inconsistent-missing-override) - -# specify libraries -target_link_libraries(mp4-parser PRIVATE - arduino_emulator - arduino-audio-tools) \ No newline at end of file diff --git a/tests-cmake/codec/mp4-parser/mp4-parser.ino b/tests-cmake/codec/mp4-parser/mp4-parser.ino deleted file mode 100644 index 966e8cbf0c..0000000000 --- a/tests-cmake/codec/mp4-parser/mp4-parser.ino +++ /dev/null @@ -1,48 +0,0 @@ -/** - * @file test-codec-alac.ino - * @author Phil Schatzmann - * @brief generate sine wave -> encoder -> decoder -> audiokit (i2s) - * @version 0.1 - * - * @copyright Copyright (c) 2025 - * - */ -#include "AudioTools.h" -#include "AudioTools/AudioCodecs/MP4ParserIncremental.h" -#include "SD.h" - -//MP4Parser parser; -MP4ParserIncremental parser; -File file; - -void setup() { - Serial.begin(115200); - AudioToolsLogger.begin(Serial, AudioToolsLogLevel::Info); - - if (!SD.begin()) { - Serial.println("SD Card initialization failed!"); - return; - } - - file = SD.open("/home/pschatzmann/Music/m4a/1-07 All You Need Is Love.m4a"); - if (!file.isOpen()) { - Serial.println("Failed to open file!"); - return; - } - - //parser.resize(1024 * 1024); // Set buffer size to 2MB - parser.begin(); - - Serial.println("MP4 Boxes:"); -} - -void loop() { - char buffer[1024]; - int to_read = min(sizeof(buffer), parser.availableForWrite()); - size_t bytesRead = file.readBytes(buffer,to_read); - parser.write(buffer, bytesRead); - if (bytesRead == 0) { - Serial.println("End of file reached."); - exit(0); // Exit the process - } -} \ No newline at end of file diff --git a/tests-cmake/codec/mts/CMakeLists.txt b/tests-cmake/codec/mts/CMakeLists.txt deleted file mode 100644 index d102f26b4a..0000000000 --- a/tests-cmake/codec/mts/CMakeLists.txt +++ /dev/null @@ -1,28 +0,0 @@ -cmake_minimum_required(VERSION 3.20) - -# set the project name -project(mts) -set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -g -O0") -set(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} -g -O0") - -include(FetchContent) -option(BUILD_SHARED_LIBS "Build using shared libraries" OFF) - - -# provide audio-tools -if(CMAKE_SOURCE_DIR STREQUAL CMAKE_CURRENT_SOURCE_DIR) - add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/../../.. ${CMAKE_CURRENT_BINARY_DIR}/arduino-audio-tools ) -endif() - -# build sketch as executable -set_source_files_properties(mts.ino PROPERTIES LANGUAGE CXX) -add_executable (mts mts.ino) - -# set preprocessor defines -target_compile_definitions(mts PUBLIC -DARDUINO -DIS_DESKTOP) - -target_compile_options(arduino-audio-tools INTERFACE -Wno-inconsistent-missing-override) - -# specify libraries -target_link_libraries(mts PRIVATE arduino_emulator arduino-audio-tools ) - diff --git a/tests-cmake/codec/mts/mts.ino b/tests-cmake/codec/mts/mts.ino deleted file mode 100644 index 713277b637..0000000000 --- a/tests-cmake/codec/mts/mts.ino +++ /dev/null @@ -1,31 +0,0 @@ -#include "AudioTools.h" -#include "AudioTools/AudioCodecs/CodecMTS.h" - -HexDumpOutput out; -MTSDecoder codecMTS; -FILE *fp; -uint8_t buffer[1024]; - -void setup() { - AudioToolsLogger.begin(Serial, AudioToolsLogLevel::Info); - - fp = fopen( - "/home/pschatzmann/Downloads/tmp/bbc_radio_one-audio=96000-272169555.ts", - "rb"); - codecMTS.setOutput(out); - codecMTS.begin(); - const size_t fileSize = fread(buffer, sizeof(unsigned char), 1024, fp); - // Every 188th byte should be 0x47. - assert(buffer[0] == 0x47); - assert(buffer[188] == 0x47); - codecMTS.write(buffer, fileSize); -} - -void loop() { - const size_t fileSize = fread(buffer, sizeof(unsigned char), 1024, fp); - if (fileSize==0) { - LOGI("End of File"); - stop(); - } - codecMTS.write(buffer, fileSize); -} \ No newline at end of file diff --git a/tests-cmake/codec/opus/CMakeLists.txt b/tests-cmake/codec/opus/CMakeLists.txt index 8b3ba75af7..a320da5458 100644 --- a/tests-cmake/codec/opus/CMakeLists.txt +++ b/tests-cmake/codec/opus/CMakeLists.txt @@ -21,9 +21,9 @@ if(NOT arduino_libopus_POPULATED) endif() # build sketch as executable -add_executable (opus opus.cpp) +add_executable (opus opus.cpp ../../main.cpp) # set preprocessor defines -target_compile_definitions(opus PUBLIC -DARDUINO -DUSE_PORTAUDIO -DIS_DESKTOP ) +target_compile_definitions(opus PUBLIC -DARDUINO -DEXIT_ON_STOP -DUSE_PORTAUDIO -DIS_DESKTOP ) # specify libraries target_link_libraries(opus portaudio arduino_emulator arduino_libopus arduino-audio-tools ) diff --git a/tests-cmake/codec/opus/opus.cpp b/tests-cmake/codec/opus/opus.cpp index 0e2a8a0e5e..1f616df6ad 100644 --- a/tests-cmake/codec/opus/opus.cpp +++ b/tests-cmake/codec/opus/opus.cpp @@ -9,7 +9,7 @@ * */ #include "AudioTools.h" -#include "AudioTools/AudioCodecs/CodecOpus.h" +#include "AudioCodecs/CodecOpus.h" int sample_rate = 24000; int channels = 1; // The stream will have 2 channels @@ -25,7 +25,7 @@ StreamCopy copier(encoder, sound); void setup() { Serial.begin(115200); - AudioToolsLogger.begin(Serial, AudioToolsLogLevel::Debug); + AudioLogger::instance().begin(Serial, AudioLogger::Debug); // Setup sine wave diff --git a/tests-cmake/codec/opusogg/CMakeLists.txt b/tests-cmake/codec/opusogg/CMakeLists.txt index 0011a48897..7b05cacdd4 100644 --- a/tests-cmake/codec/opusogg/CMakeLists.txt +++ b/tests-cmake/codec/opusogg/CMakeLists.txt @@ -21,9 +21,9 @@ if(NOT arduino_libopus_POPULATED) endif() # build sketch as executable -add_executable (opusogg opusogg.cpp) +add_executable (opusogg opusogg.cpp ../../main.cpp) # set preprocessor defines -target_compile_definitions(opusogg PUBLIC -DARDUINO -DUSE_PORTAUDIO -DIS_DESKTOP ) +target_compile_definitions(opusogg PUBLIC -DARDUINO -DEXIT_ON_STOP -DUSE_PORTAUDIO -DIS_DESKTOP ) # specify libraries target_link_libraries(opusogg portaudio arduino_emulator arduino_libopus arduino-audio-tools ) diff --git a/tests-cmake/codec/opusogg/opusogg.cpp b/tests-cmake/codec/opusogg/opusogg.cpp index 9befd034ac..0ea089010f 100644 --- a/tests-cmake/codec/opusogg/opusogg.cpp +++ b/tests-cmake/codec/opusogg/opusogg.cpp @@ -9,7 +9,7 @@ * */ #include "AudioTools.h" -#include "AudioTools/AudioCodecs/CodecOpusOgg.h" +#include "AudioCodecs/CodecOpusOgg.h" int application = OPUS_APPLICATION_AUDIO; // Opus application AudioInfo info(24000, 1, 16); @@ -24,7 +24,7 @@ StreamCopy copier(encoder, sound); void setup() { Serial.begin(115200); - AudioToolsLogger.begin(Serial, AudioToolsLogLevel::Warning); + AudioLogger::instance().begin(Serial, AudioLogger::Warning); // Setup output auto cfgo = out.defaultConfig(); diff --git a/tests-cmake/codec/wav/CMakeLists.txt b/tests-cmake/codec/wav/CMakeLists.txt index 3c8c5f93ed..86e09d9a60 100644 --- a/tests-cmake/codec/wav/CMakeLists.txt +++ b/tests-cmake/codec/wav/CMakeLists.txt @@ -4,20 +4,13 @@ cmake_minimum_required(VERSION 3.20) project(wav-test) set (CMAKE_CXX_STANDARD 11) set (DCMAKE_CXX_FLAGS "-Werror") - -# add_compile_options(-Wstack-usage=1024) +if (CMAKE_CXX_COMPILER_ID STREQUAL "Clang") + set (CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -fno-omit-frame-pointer -fsanitize=address") + set (CMAKE_LINKER_FLAGS_DEBUG "${CMAKE_LINKER_FLAGS_DEBUG} -fno-omit-frame-pointer -fsanitize=address") +endif() include(FetchContent) -# Add Portaduio for desktop build -add_compile_options(-DIS_DESKTOP) -FetchContent_Declare(portaudio GIT_REPOSITORY "https://github.com/PortAudio/portaudio.git" GIT_TAG v19.7.0 ) -FetchContent_GetProperties(portaudio) -if(NOT portaudio_POPULATED) - FetchContent_Populate(portaudio) - add_subdirectory(${portaudio_SOURCE_DIR} ${CMAKE_CURRENT_BINARY_DIR}/portaudio) -endif() - # Build with arduino-adpcm FetchContent_Declare(adpcm_ffmpeg GIT_REPOSITORY "https://github.com/pschatzmann/adpcm" GIT_TAG main ) FetchContent_GetProperties(adpcm_ffmpeg) @@ -32,10 +25,10 @@ if(CMAKE_SOURCE_DIR STREQUAL CMAKE_CURRENT_SOURCE_DIR) endif() # build sketch as executable -add_executable (wav-test wav.cpp) +add_executable (wav-test wav.cpp ../../main.cpp) # set preprocessor defines -target_compile_definitions(wav-test PUBLIC -DARDUINO -DUSE_PORTAUDIO -DIS_DESKTOP) +target_compile_definitions(wav-test PUBLIC -DARDUINO -DEXIT_ON_STOP -DUSE_PORTAUDIO -DIS_DESKTOP) # specify libraries target_link_libraries(wav-test portaudio arduino_emulator adpcm_ffmpeg arduino-audio-tools) diff --git a/tests-cmake/codec/wav/wav.cpp b/tests-cmake/codec/wav/wav.cpp index b3e72f9748..3a8eaf04fb 100644 --- a/tests-cmake/codec/wav/wav.cpp +++ b/tests-cmake/codec/wav/wav.cpp @@ -10,9 +10,9 @@ * */ #include "AudioTools.h" -#include "AudioTools/AudioCodecs/CodecWAV.h" -#include "AudioTools/AudioCodecs/CodecADPCM.h" -#include "AudioTools/AudioLibs/PortAudioStream.h" +#include "AudioCodecs/CodecWAV.h" +#include "AudioCodecs/CodecADPCM.h" +#include "AudioLibs/PortAudioStream.h" #define USE_ADPCM true @@ -20,8 +20,8 @@ AudioInfo info(16000, 2, 16); SineWaveGenerator sineWave( 32000); // subclass of SoundGenerator with max amplitude of 32000 GeneratedSoundStream sound( sineWave); // Stream generated from sine wave //I2SStream out; -//PortAudioStream out; -CsvOutput out(Serial); +PortAudioStream out; +//CsvStream out(Serial); #if USE_ADPCM ADPCMDecoder adpcm_decoder(AV_CODEC_ID_ADPCM_IMA_WAV); ADPCMEncoder adpcm_encoder(AV_CODEC_ID_ADPCM_IMA_WAV); @@ -35,7 +35,7 @@ StreamCopy copier(encoder, sound); void setup() { Serial.begin(115200); - AudioToolsLogger.begin(Serial, AudioToolsLogLevel::Debug); + AudioLogger::instance().begin(Serial, AudioLogger::Warning); // start I2S Serial.println("starting Output..."); diff --git a/tests-cmake/effects/CMakeLists.txt b/tests-cmake/effects/CMakeLists.txt index 3545f677a2..cd4879a3f4 100644 --- a/tests-cmake/effects/CMakeLists.txt +++ b/tests-cmake/effects/CMakeLists.txt @@ -4,16 +4,9 @@ cmake_minimum_required(VERSION 3.20) project(effects) set (CMAKE_CXX_STANDARD 11) set (DCMAKE_CXX_FLAGS "-Werror") - -# add_compile_options(-Wstack-usage=1024) - -# Add Portaduio for desktop build -add_compile_options(-DIS_DESKTOP) -FetchContent_Declare(portaudio GIT_REPOSITORY "https://github.com/PortAudio/portaudio.git" GIT_TAG v19.7.0 ) -FetchContent_GetProperties(portaudio) -if(NOT portaudio_POPULATED) - FetchContent_Populate(portaudio) - add_subdirectory(${portaudio_SOURCE_DIR} ${CMAKE_CURRENT_BINARY_DIR}/portaudio) +if (CMAKE_CXX_COMPILER_ID STREQUAL "Clang") + set (CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -fno-omit-frame-pointer -fsanitize=address") + set (CMAKE_LINKER_FLAGS_DEBUG "${CMAKE_LINKER_FLAGS_DEBUG} -fno-omit-frame-pointer -fsanitize=address") endif() # Build with arduino-audio-tools @@ -23,9 +16,9 @@ endif() # build sketch as executable -add_executable (effects effects.cpp) +add_executable (effects effects.cpp ../main.cpp) # set preprocessor defines -target_compile_definitions(effects PUBLIC -DARDUINO -DIS_DESKTOP) +target_compile_definitions(effects PUBLIC -DARDUINO -DEXIT_ON_STOP -DIS_DESKTOP) # specify libraries target_link_libraries(effects arduino_emulator arduino-audio-tools portaudio) diff --git a/tests-cmake/effects/effects.cpp b/tests-cmake/effects/effects.cpp index d12fa388d1..356ce81c61 100644 --- a/tests-cmake/effects/effects.cpp +++ b/tests-cmake/effects/effects.cpp @@ -1,7 +1,7 @@ // Simple wrapper for Arduino sketch to compilable with cpp in cmake #include "Arduino.h" #include "AudioTools.h" -#include "AudioTools/AudioLibs/PortAudioStream.h" +#include "AudioLibs/PortAudioStream.h" using namespace audio_tools; diff --git a/tests-cmake/fft-effect/CMakeLists.txt b/tests-cmake/fft-effect/CMakeLists.txt deleted file mode 100644 index 373b8d73fe..0000000000 --- a/tests-cmake/fft-effect/CMakeLists.txt +++ /dev/null @@ -1,35 +0,0 @@ -cmake_minimum_required(VERSION 3.20) - - -# set the project name -project(fft-effect) -set (CMAKE_CXX_STANDARD 11) -set (CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -O0 -ldl -lpthread -lm -fno-omit-frame-pointer -fsanitize=address") -set (CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} -O0 -ldl -lpthread -lm -fno-omit-frame-pointer -fsanitize=address") -set (CMAKE_LINKER_FLAGS_DEBUG "${CMAKE_LINKER_FLAGS_DEBUG} -fno-omit-frame-pointer -fsanitize=address") - -# Emulator is not necessary for -DIS_MIN_DESKTOP -set(ADD_ARDUINO_EMULATOR OFF CACHE BOOL "Add Arduino Emulator Library") -set(ADD_PORTAUDIO OFF CACHE BOOL "No Portaudio") - -# Build with arduino-audio-tools -if(CMAKE_SOURCE_DIR STREQUAL CMAKE_CURRENT_SOURCE_DIR) - add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/../.. ${CMAKE_CURRENT_BINARY_DIR}/arduino-audio-tools ) -endif() - -# Download miniaudio.h -file(DOWNLOAD https://raw.githubusercontent.com/mackron/miniaudio/master/miniaudio.h - ${CMAKE_CURRENT_SOURCE_DIR}/miniaudio.h) - - -# build sketch as executable -add_executable (fft-effect fft-effect.cpp) - -# set preprocessor defines -target_compile_definitions(fft-effect PUBLIC -DIS_MIN_DESKTOP) - -# specify libraries -target_link_libraries(fft-effect arduino-audio-tools) - -# access to miniaudio in sketch directory -target_include_directories(fft-effect PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}) diff --git a/tests-cmake/fft-effect/fft-effect.cpp b/tests-cmake/fft-effect/fft-effect.cpp deleted file mode 100644 index e11eea2a92..0000000000 --- a/tests-cmake/fft-effect/fft-effect.cpp +++ /dev/null @@ -1,45 +0,0 @@ -#include "AudioTools.h" -#include "AudioTools/AudioLibs/MiniAudioStream.h" -#include "AudioTools/AudioLibs/FFTEffects.h" -#include "AudioTools/AudioLibs/Desktop/File.h" - -AudioInfo info(16000, 2, 16); -MiniAudioStream out; // final output of decoded stream -FFTPitchShift effect(out); -//FFTNop effect(out); -//FFTWhisper effect(out); -//FFTRobotize effect(out); -WAVDecoder wav; -EncodedAudioOutput decoder(&effect, &wav); // Decoding stream -StreamCopy copier; -File audioFile; - -void setup(){ - AudioToolsLogger.begin(Serial, AudioToolsLogLevel::Info); - - auto config = out.defaultConfig(TX_MODE); - config.copyFrom(info); - out.begin(config); - - auto econfig = effect.defaultConfig(); - econfig.copyFrom(info); - econfig.shift = -20; - effect.begin(econfig); - - // setup file: must be in current directory - audioFile.open("hal1600.wav"); - - // setup I2S based on sampling rate provided by decoder - decoder.begin(); - - // begin copy - copier.begin(decoder, audioFile); - -} - -void loop(){ - if (!copier.copy()) { - delay(5000); - exit(0); - } -} \ No newline at end of file diff --git a/tests-cmake/fft-effect/hal1600.wav b/tests-cmake/fft-effect/hal1600.wav deleted file mode 100644 index 925abc0eff..0000000000 Binary files a/tests-cmake/fft-effect/hal1600.wav and /dev/null differ diff --git a/tests-cmake/fft-effect/miniaudio.h b/tests-cmake/fft-effect/miniaudio.h deleted file mode 100644 index c74bebeb3c..0000000000 --- a/tests-cmake/fft-effect/miniaudio.h +++ /dev/null @@ -1,93468 +0,0 @@ -/* -Audio playback and capture library. Choice of public domain or MIT-0. See license statements at the end of this file. -miniaudio - v0.11.22 - 2025-02-24 - -David Reid - mackron@gmail.com - -Website: https://miniaud.io -Documentation: https://miniaud.io/docs -GitHub: https://github.com/mackron/miniaudio -*/ - -/* -1. Introduction -=============== -To use miniaudio, include "miniaudio.h": - - ```c - #include "miniaudio.h" - ``` - -The implementation is contained in "miniaudio.c". Just compile this like any other source file. You -can include miniaudio.c if you want to compile your project as a single translation unit: - - ```c - #include "miniaudio.c" - ``` - -miniaudio includes both low level and high level APIs. The low level API is good for those who want -to do all of their mixing themselves and only require a light weight interface to the underlying -audio device. The high level API is good for those who have complex mixing and effect requirements. - -In miniaudio, objects are transparent structures. Unlike many other libraries, there are no handles -to opaque objects which means you need to allocate memory for objects yourself. In the examples -presented in this documentation you will often see objects declared on the stack. You need to be -careful when translating these examples to your own code so that you don't accidentally declare -your objects on the stack and then cause them to become invalid once the function returns. In -addition, you must ensure the memory address of your objects remain the same throughout their -lifetime. You therefore cannot be making copies of your objects. - -A config/init pattern is used throughout the entire library. The idea is that you set up a config -object and pass that into the initialization routine. The advantage to this system is that the -config object can be initialized with logical defaults and new properties added to it without -breaking the API. The config object can be allocated on the stack and does not need to be -maintained after initialization of the corresponding object. - - -1.1. Low Level API ------------------- -The low level API gives you access to the raw audio data of an audio device. It supports playback, -capture, full-duplex and loopback (WASAPI only). You can enumerate over devices to determine which -physical device(s) you want to connect to. - -The low level API uses the concept of a "device" as the abstraction for physical devices. The idea -is that you choose a physical device to emit or capture audio from, and then move data to/from the -device when miniaudio tells you to. Data is delivered to and from devices asynchronously via a -callback which you specify when initializing the device. - -When initializing the device you first need to configure it. The device configuration allows you to -specify things like the format of the data delivered via the callback, the size of the internal -buffer and the ID of the device you want to emit or capture audio from. - -Once you have the device configuration set up you can initialize the device. When initializing a -device you need to allocate memory for the device object beforehand. This gives the application -complete control over how the memory is allocated. In the example below we initialize a playback -device on the stack, but you could allocate it on the heap if that suits your situation better. - - ```c - void data_callback(ma_device* pDevice, void* pOutput, const void* pInput, ma_uint32 frameCount) - { - // In playback mode copy data to pOutput. In capture mode read data from pInput. In full-duplex mode, both - // pOutput and pInput will be valid and you can move data from pInput into pOutput. Never process more than - // frameCount frames. - } - - int main() - { - ma_device_config config = ma_device_config_init(ma_device_type_playback); - config.playback.format = ma_format_f32; // Set to ma_format_unknown to use the device's native format. - config.playback.channels = 2; // Set to 0 to use the device's native channel count. - config.sampleRate = 48000; // Set to 0 to use the device's native sample rate. - config.dataCallback = data_callback; // This function will be called when miniaudio needs more data. - config.pUserData = pMyCustomData; // Can be accessed from the device object (device.pUserData). - - ma_device device; - if (ma_device_init(NULL, &config, &device) != MA_SUCCESS) { - return -1; // Failed to initialize the device. - } - - ma_device_start(&device); // The device is sleeping by default so you'll need to start it manually. - - // Do something here. Probably your program's main loop. - - ma_device_uninit(&device); - return 0; - } - ``` - -In the example above, `data_callback()` is where audio data is written and read from the device. -The idea is in playback mode you cause sound to be emitted from the speakers by writing audio data -to the output buffer (`pOutput` in the example). In capture mode you read data from the input -buffer (`pInput`) to extract sound captured by the microphone. The `frameCount` parameter tells you -how many frames can be written to the output buffer and read from the input buffer. A "frame" is -one sample for each channel. For example, in a stereo stream (2 channels), one frame is 2 -samples: one for the left, one for the right. The channel count is defined by the device config. -The size in bytes of an individual sample is defined by the sample format which is also specified -in the device config. Multi-channel audio data is always interleaved, which means the samples for -each frame are stored next to each other in memory. For example, in a stereo stream the first pair -of samples will be the left and right samples for the first frame, the second pair of samples will -be the left and right samples for the second frame, etc. - -The configuration of the device is defined by the `ma_device_config` structure. The config object -is always initialized with `ma_device_config_init()`. It's important to always initialize the -config with this function as it initializes it with logical defaults and ensures your program -doesn't break when new members are added to the `ma_device_config` structure. The example above -uses a fairly simple and standard device configuration. The call to `ma_device_config_init()` takes -a single parameter, which is whether or not the device is a playback, capture, duplex or loopback -device (loopback devices are not supported on all backends). The `config.playback.format` member -sets the sample format which can be one of the following (all formats are native-endian): - - +---------------+----------------------------------------+---------------------------+ - | Symbol | Description | Range | - +---------------+----------------------------------------+---------------------------+ - | ma_format_f32 | 32-bit floating point | [-1, 1] | - | ma_format_s16 | 16-bit signed integer | [-32768, 32767] | - | ma_format_s24 | 24-bit signed integer (tightly packed) | [-8388608, 8388607] | - | ma_format_s32 | 32-bit signed integer | [-2147483648, 2147483647] | - | ma_format_u8 | 8-bit unsigned integer | [0, 255] | - +---------------+----------------------------------------+---------------------------+ - -The `config.playback.channels` member sets the number of channels to use with the device. The -channel count cannot exceed MA_MAX_CHANNELS. The `config.sampleRate` member sets the sample rate -(which must be the same for both playback and capture in full-duplex configurations). This is -usually set to 44100 or 48000, but can be set to anything. It's recommended to keep this between -8000 and 384000, however. - -Note that leaving the format, channel count and/or sample rate at their default values will result -in the internal device's native configuration being used which is useful if you want to avoid the -overhead of miniaudio's automatic data conversion. - -In addition to the sample format, channel count and sample rate, the data callback and user data -pointer are also set via the config. The user data pointer is not passed into the callback as a -parameter, but is instead set to the `pUserData` member of `ma_device` which you can access -directly since all miniaudio structures are transparent. - -Initializing the device is done with `ma_device_init()`. This will return a result code telling you -what went wrong, if anything. On success it will return `MA_SUCCESS`. After initialization is -complete the device will be in a stopped state. To start it, use `ma_device_start()`. -Uninitializing the device will stop it, which is what the example above does, but you can also stop -the device with `ma_device_stop()`. To resume the device simply call `ma_device_start()` again. -Note that it's important to never stop or start the device from inside the callback. This will -result in a deadlock. Instead you set a variable or signal an event indicating that the device -needs to stop and handle it in a different thread. The following APIs must never be called inside -the callback: - - ```c - ma_device_init() - ma_device_init_ex() - ma_device_uninit() - ma_device_start() - ma_device_stop() - ``` - -You must never try uninitializing and reinitializing a device inside the callback. You must also -never try to stop and start it from inside the callback. There are a few other things you shouldn't -do in the callback depending on your requirements, however this isn't so much a thread-safety -thing, but rather a real-time processing thing which is beyond the scope of this introduction. - -The example above demonstrates the initialization of a playback device, but it works exactly the -same for capture. All you need to do is change the device type from `ma_device_type_playback` to -`ma_device_type_capture` when setting up the config, like so: - - ```c - ma_device_config config = ma_device_config_init(ma_device_type_capture); - config.capture.format = MY_FORMAT; - config.capture.channels = MY_CHANNEL_COUNT; - ``` - -In the data callback you just read from the input buffer (`pInput` in the example above) and leave -the output buffer alone (it will be set to NULL when the device type is set to -`ma_device_type_capture`). - -These are the available device types and how you should handle the buffers in the callback: - - +-------------------------+--------------------------------------------------------+ - | Device Type | Callback Behavior | - +-------------------------+--------------------------------------------------------+ - | ma_device_type_playback | Write to output buffer, leave input buffer untouched. | - | ma_device_type_capture | Read from input buffer, leave output buffer untouched. | - | ma_device_type_duplex | Read from input buffer, write to output buffer. | - | ma_device_type_loopback | Read from input buffer, leave output buffer untouched. | - +-------------------------+--------------------------------------------------------+ - -You will notice in the example above that the sample format and channel count is specified -separately for playback and capture. This is to support different data formats between the playback -and capture devices in a full-duplex system. An example may be that you want to capture audio data -as a monaural stream (one channel), but output sound to a stereo speaker system. Note that if you -use different formats between playback and capture in a full-duplex configuration you will need to -convert the data yourself. There are functions available to help you do this which will be -explained later. - -The example above did not specify a physical device to connect to which means it will use the -operating system's default device. If you have multiple physical devices connected and you want to -use a specific one you will need to specify the device ID in the configuration, like so: - - ```c - config.playback.pDeviceID = pMyPlaybackDeviceID; // Only if requesting a playback or duplex device. - config.capture.pDeviceID = pMyCaptureDeviceID; // Only if requesting a capture, duplex or loopback device. - ``` - -To retrieve the device ID you will need to perform device enumeration, however this requires the -use of a new concept called the "context". Conceptually speaking the context sits above the device. -There is one context to many devices. The purpose of the context is to represent the backend at a -more global level and to perform operations outside the scope of an individual device. Mainly it is -used for performing run-time linking against backend libraries, initializing backends and -enumerating devices. The example below shows how to enumerate devices. - - ```c - ma_context context; - if (ma_context_init(NULL, 0, NULL, &context) != MA_SUCCESS) { - // Error. - } - - ma_device_info* pPlaybackInfos; - ma_uint32 playbackCount; - ma_device_info* pCaptureInfos; - ma_uint32 captureCount; - if (ma_context_get_devices(&context, &pPlaybackInfos, &playbackCount, &pCaptureInfos, &captureCount) != MA_SUCCESS) { - // Error. - } - - // Loop over each device info and do something with it. Here we just print the name with their index. You may want - // to give the user the opportunity to choose which device they'd prefer. - for (ma_uint32 iDevice = 0; iDevice < playbackCount; iDevice += 1) { - printf("%d - %s\n", iDevice, pPlaybackInfos[iDevice].name); - } - - ma_device_config config = ma_device_config_init(ma_device_type_playback); - config.playback.pDeviceID = &pPlaybackInfos[chosenPlaybackDeviceIndex].id; - config.playback.format = MY_FORMAT; - config.playback.channels = MY_CHANNEL_COUNT; - config.sampleRate = MY_SAMPLE_RATE; - config.dataCallback = data_callback; - config.pUserData = pMyCustomData; - - ma_device device; - if (ma_device_init(&context, &config, &device) != MA_SUCCESS) { - // Error - } - - ... - - ma_device_uninit(&device); - ma_context_uninit(&context); - ``` - -The first thing we do in this example is initialize a `ma_context` object with `ma_context_init()`. -The first parameter is a pointer to a list of `ma_backend` values which are used to override the -default backend priorities. When this is NULL, as in this example, miniaudio's default priorities -are used. The second parameter is the number of backends listed in the array pointed to by the -first parameter. The third parameter is a pointer to a `ma_context_config` object which can be -NULL, in which case defaults are used. The context configuration is used for setting the logging -callback, custom memory allocation callbacks, user-defined data and some backend-specific -configurations. - -Once the context has been initialized you can enumerate devices. In the example above we use the -simpler `ma_context_get_devices()`, however you can also use a callback for handling devices by -using `ma_context_enumerate_devices()`. When using `ma_context_get_devices()` you provide a pointer -to a pointer that will, upon output, be set to a pointer to a buffer containing a list of -`ma_device_info` structures. You also provide a pointer to an unsigned integer that will receive -the number of items in the returned buffer. Do not free the returned buffers as their memory is -managed internally by miniaudio. - -The `ma_device_info` structure contains an `id` member which is the ID you pass to the device -config. It also contains the name of the device which is useful for presenting a list of devices -to the user via the UI. - -When creating your own context you will want to pass it to `ma_device_init()` when initializing the -device. Passing in NULL, like we do in the first example, will result in miniaudio creating the -context for you, which you don't want to do since you've already created a context. Note that -internally the context is only tracked by it's pointer which means you must not change the location -of the `ma_context` object. If this is an issue, consider using `malloc()` to allocate memory for -the context. - - -1.2. High Level API -------------------- -The high level API consists of three main parts: - - * Resource management for loading and streaming sounds. - * A node graph for advanced mixing and effect processing. - * A high level "engine" that wraps around the resource manager and node graph. - -The resource manager (`ma_resource_manager`) is used for loading sounds. It supports loading sounds -fully into memory and also streaming. It will also deal with reference counting for you which -avoids the same sound being loaded multiple times. - -The node graph is used for mixing and effect processing. The idea is that you connect a number of -nodes into the graph by connecting each node's outputs to another node's inputs. Each node can -implement its own effect. By chaining nodes together, advanced mixing and effect processing can -be achieved. - -The engine encapsulates both the resource manager and the node graph to create a simple, easy to -use high level API. The resource manager and node graph APIs are covered in more later sections of -this manual. - -The code below shows how you can initialize an engine using it's default configuration. - - ```c - ma_result result; - ma_engine engine; - - result = ma_engine_init(NULL, &engine); - if (result != MA_SUCCESS) { - return result; // Failed to initialize the engine. - } - ``` - -This creates an engine instance which will initialize a device internally which you can access with -`ma_engine_get_device()`. It will also initialize a resource manager for you which can be accessed -with `ma_engine_get_resource_manager()`. The engine itself is a node graph (`ma_node_graph`) which -means you can pass a pointer to the engine object into any of the `ma_node_graph` APIs (with a -cast). Alternatively, you can use `ma_engine_get_node_graph()` instead of a cast. - -Note that all objects in miniaudio, including the `ma_engine` object in the example above, are -transparent structures. There are no handles to opaque structures in miniaudio which means you need -to be mindful of how you declare them. In the example above we are declaring it on the stack, but -this will result in the struct being invalidated once the function encapsulating it returns. If -allocating the engine on the heap is more appropriate, you can easily do so with a standard call -to `malloc()` or whatever heap allocation routine you like: - - ```c - ma_engine* pEngine = malloc(sizeof(*pEngine)); - ``` - -The `ma_engine` API uses the same config/init pattern used all throughout miniaudio. To configure -an engine, you can fill out a `ma_engine_config` object and pass it into the first parameter of -`ma_engine_init()`: - - ```c - ma_result result; - ma_engine engine; - ma_engine_config engineConfig; - - engineConfig = ma_engine_config_init(); - engineConfig.pResourceManager = &myCustomResourceManager; // <-- Initialized as some earlier stage. - - result = ma_engine_init(&engineConfig, &engine); - if (result != MA_SUCCESS) { - return result; - } - ``` - -This creates an engine instance using a custom config. In this particular example it's showing how -you can specify a custom resource manager rather than having the engine initialize one internally. -This is particularly useful if you want to have multiple engine's share the same resource manager. - -The engine must be uninitialized with `ma_engine_uninit()` when it's no longer needed. - -By default the engine will be started, but nothing will be playing because no sounds have been -initialized. The easiest but least flexible way of playing a sound is like so: - - ```c - ma_engine_play_sound(&engine, "my_sound.wav", NULL); - ``` - -This plays what miniaudio calls an "inline" sound. It plays the sound once, and then puts the -internal sound up for recycling. The last parameter is used to specify which sound group the sound -should be associated with which will be explained later. This particular way of playing a sound is -simple, but lacks flexibility and features. A more flexible way of playing a sound is to first -initialize a sound: - - ```c - ma_result result; - ma_sound sound; - - result = ma_sound_init_from_file(&engine, "my_sound.wav", 0, NULL, NULL, &sound); - if (result != MA_SUCCESS) { - return result; - } - - ma_sound_start(&sound); - ``` - -This returns a `ma_sound` object which represents a single instance of the specified sound file. If -you want to play the same file multiple times simultaneously, you need to create one sound for each -instance. - -Sounds should be uninitialized with `ma_sound_uninit()`. - -Sounds are not started by default. Start a sound with `ma_sound_start()` and stop it with -`ma_sound_stop()`. When a sound is stopped, it is not rewound to the start. Use -`ma_sound_seek_to_pcm_frame(&sound, 0)` to seek back to the start of a sound. By default, starting -and stopping sounds happens immediately, but sometimes it might be convenient to schedule the sound -the be started and/or stopped at a specific time. This can be done with the following functions: - - ```c - ma_sound_set_start_time_in_pcm_frames() - ma_sound_set_start_time_in_milliseconds() - ma_sound_set_stop_time_in_pcm_frames() - ma_sound_set_stop_time_in_milliseconds() - ``` - -The start/stop time needs to be specified based on the absolute timer which is controlled by the -engine. The current global time in PCM frames can be retrieved with -`ma_engine_get_time_in_pcm_frames()`. The engine's global time can be changed with -`ma_engine_set_time_in_pcm_frames()` for synchronization purposes if required. Note that scheduling -a start time still requires an explicit call to `ma_sound_start()` before anything will play: - - ```c - ma_sound_set_start_time_in_pcm_frames(&sound, ma_engine_get_time_in_pcm_frames(&engine) + (ma_engine_get_sample_rate(&engine) * 2); - ma_sound_start(&sound); - ``` - -The third parameter of `ma_sound_init_from_file()` is a set of flags that control how the sound be -loaded and a few options on which features should be enabled for that sound. By default, the sound -is synchronously loaded fully into memory straight from the file system without any kind of -decoding. If you want to decode the sound before storing it in memory, you need to specify the -`MA_SOUND_FLAG_DECODE` flag. This is useful if you want to incur the cost of decoding at an earlier -stage, such as a loading stage. Without this option, decoding will happen dynamically at mixing -time which might be too expensive on the audio thread. - -If you want to load the sound asynchronously, you can specify the `MA_SOUND_FLAG_ASYNC` flag. This -will result in `ma_sound_init_from_file()` returning quickly, but the sound will not start playing -until the sound has had some audio decoded. - -The fourth parameter is a pointer to sound group. A sound group is used as a mechanism to organise -sounds into groups which have their own effect processing and volume control. An example is a game -which might have separate groups for sfx, voice and music. Each of these groups have their own -independent volume control. Use `ma_sound_group_init()` or `ma_sound_group_init_ex()` to initialize -a sound group. - -Sounds and sound groups are nodes in the engine's node graph and can be plugged into any `ma_node` -API. This makes it possible to connect sounds and sound groups to effect nodes to produce complex -effect chains. - -A sound can have its volume changed with `ma_sound_set_volume()`. If you prefer decibel volume -control you can use `ma_volume_db_to_linear()` to convert from decibel representation to linear. - -Panning and pitching is supported with `ma_sound_set_pan()` and `ma_sound_set_pitch()`. If you know -a sound will never have its pitch changed with `ma_sound_set_pitch()` or via the doppler effect, -you can specify the `MA_SOUND_FLAG_NO_PITCH` flag when initializing the sound for an optimization. - -By default, sounds and sound groups have spatialization enabled. If you don't ever want to -spatialize your sounds, initialize the sound with the `MA_SOUND_FLAG_NO_SPATIALIZATION` flag. The -spatialization model is fairly simple and is roughly on feature parity with OpenAL. HRTF and -environmental occlusion are not currently supported, but planned for the future. The supported -features include: - - * Sound and listener positioning and orientation with cones - * Attenuation models: none, inverse, linear and exponential - * Doppler effect - -Sounds can be faded in and out with `ma_sound_set_fade_in_pcm_frames()`. - -To check if a sound is currently playing, you can use `ma_sound_is_playing()`. To check if a sound -is at the end, use `ma_sound_at_end()`. Looping of a sound can be controlled with -`ma_sound_set_looping()`. Use `ma_sound_is_looping()` to check whether or not the sound is looping. - - - -2. Building -=========== -miniaudio should work cleanly out of the box without the need to download or install any -dependencies. See below for platform-specific details. - -Note that GCC and Clang require `-msse2`, `-mavx2`, etc. for SIMD optimizations. - -If you get errors about undefined references to `__sync_val_compare_and_swap_8`, `__atomic_load_8`, -etc. you need to link with `-latomic`. - - -2.1. Windows ------------- -The Windows build should compile cleanly on all popular compilers without the need to configure any -include paths nor link to any libraries. - -The UWP build may require linking to mmdevapi.lib if you get errors about an unresolved external -symbol for `ActivateAudioInterfaceAsync()`. - - -2.2. macOS and iOS ------------------- -The macOS build should compile cleanly without the need to download any dependencies nor link to -any libraries or frameworks. The iOS build needs to be compiled as Objective-C and will need to -link the relevant frameworks but should compile cleanly out of the box with Xcode. Compiling -through the command line requires linking to `-lpthread` and `-lm`. - -Due to the way miniaudio links to frameworks at runtime, your application may not pass Apple's -notarization process. To fix this there are two options. The first is to compile with -`-DMA_NO_RUNTIME_LINKING` which in turn will require linking with -`-framework CoreFoundation -framework CoreAudio -framework AudioToolbox`. If you get errors about -AudioToolbox, try with `-framework AudioUnit` instead. You may get this when using older versions -of iOS. Alternatively, if you would rather keep using runtime linking you can add the following to -your entitlements.xcent file: - - ``` - com.apple.security.cs.allow-dyld-environment-variables - - com.apple.security.cs.allow-unsigned-executable-memory - - ``` - -See this discussion for more info: https://github.com/mackron/miniaudio/issues/203. - - -2.3. Linux ----------- -The Linux build only requires linking to `-ldl`, `-lpthread` and `-lm`. You do not need any -development packages. You may need to link with `-latomic` if you're compiling for 32-bit ARM. - - -2.4. BSD --------- -The BSD build only requires linking to `-lpthread` and `-lm`. NetBSD uses audio(4), OpenBSD uses -sndio and FreeBSD uses OSS. You may need to link with `-latomic` if you're compiling for 32-bit -ARM. - - -2.5. Android ------------- -AAudio is the highest priority backend on Android. This should work out of the box without needing -any kind of compiler configuration. Support for AAudio starts with Android 8 which means older -versions will fall back to OpenSL|ES which requires API level 16+. - -There have been reports that the OpenSL|ES backend fails to initialize on some Android based -devices due to `dlopen()` failing to open "libOpenSLES.so". If this happens on your platform -you'll need to disable run-time linking with `MA_NO_RUNTIME_LINKING` and link with -lOpenSLES. - - -2.6. Emscripten ---------------- -The Emscripten build emits Web Audio JavaScript directly and should compile cleanly out of the box. -You cannot use `-std=c*` compiler flags, nor `-ansi`. - -You can enable the use of AudioWorkets by defining `MA_ENABLE_AUDIO_WORKLETS` and then compiling -with the following options: - - -sAUDIO_WORKLET=1 -sWASM_WORKERS=1 -sASYNCIFY - -An example for compiling with AudioWorklet support might look like this: - - emcc program.c -o bin/program.html -DMA_ENABLE_AUDIO_WORKLETS -sAUDIO_WORKLET=1 -sWASM_WORKERS=1 -sASYNCIFY - -To run locally, you'll need to use emrun: - - emrun bin/program.html - - - -2.7. Build Options ------------------- -`#define` these options before including miniaudio.c, or pass them as compiler flags: - - +----------------------------------+--------------------------------------------------------------------+ - | Option | Description | - +----------------------------------+--------------------------------------------------------------------+ - | MA_NO_WASAPI | Disables the WASAPI backend. | - +----------------------------------+--------------------------------------------------------------------+ - | MA_NO_DSOUND | Disables the DirectSound backend. | - +----------------------------------+--------------------------------------------------------------------+ - | MA_NO_WINMM | Disables the WinMM backend. | - +----------------------------------+--------------------------------------------------------------------+ - | MA_NO_ALSA | Disables the ALSA backend. | - +----------------------------------+--------------------------------------------------------------------+ - | MA_NO_PULSEAUDIO | Disables the PulseAudio backend. | - +----------------------------------+--------------------------------------------------------------------+ - | MA_NO_JACK | Disables the JACK backend. | - +----------------------------------+--------------------------------------------------------------------+ - | MA_NO_COREAUDIO | Disables the Core Audio backend. | - +----------------------------------+--------------------------------------------------------------------+ - | MA_NO_SNDIO | Disables the sndio backend. | - +----------------------------------+--------------------------------------------------------------------+ - | MA_NO_AUDIO4 | Disables the audio(4) backend. | - +----------------------------------+--------------------------------------------------------------------+ - | MA_NO_OSS | Disables the OSS backend. | - +----------------------------------+--------------------------------------------------------------------+ - | MA_NO_AAUDIO | Disables the AAudio backend. | - +----------------------------------+--------------------------------------------------------------------+ - | MA_NO_OPENSL | Disables the OpenSL|ES backend. | - +----------------------------------+--------------------------------------------------------------------+ - | MA_NO_WEBAUDIO | Disables the Web Audio backend. | - +----------------------------------+--------------------------------------------------------------------+ - | MA_NO_CUSTOM | Disables support for custom backends. | - +----------------------------------+--------------------------------------------------------------------+ - | MA_NO_NULL | Disables the null backend. | - +----------------------------------+--------------------------------------------------------------------+ - | MA_ENABLE_ONLY_SPECIFIC_BACKENDS | Disables all backends by default and requires `MA_ENABLE_*` to | - | | enable specific backends. | - +----------------------------------+--------------------------------------------------------------------+ - | MA_ENABLE_WASAPI | Used in conjunction with MA_ENABLE_ONLY_SPECIFIC_BACKENDS to | - | | enable the WASAPI backend. | - +----------------------------------+--------------------------------------------------------------------+ - | MA_ENABLE_DSOUND | Used in conjunction with MA_ENABLE_ONLY_SPECIFIC_BACKENDS to | - | | enable the DirectSound backend. | - +----------------------------------+--------------------------------------------------------------------+ - | MA_ENABLE_WINMM | Used in conjunction with MA_ENABLE_ONLY_SPECIFIC_BACKENDS to | - | | enable the WinMM backend. | - +----------------------------------+--------------------------------------------------------------------+ - | MA_ENABLE_ALSA | Used in conjunction with MA_ENABLE_ONLY_SPECIFIC_BACKENDS to | - | | enable the ALSA backend. | - +----------------------------------+--------------------------------------------------------------------+ - | MA_ENABLE_PULSEAUDIO | Used in conjunction with MA_ENABLE_ONLY_SPECIFIC_BACKENDS to | - | | enable the PulseAudio backend. | - +----------------------------------+--------------------------------------------------------------------+ - | MA_ENABLE_JACK | Used in conjunction with MA_ENABLE_ONLY_SPECIFIC_BACKENDS to | - | | enable the JACK backend. | - +----------------------------------+--------------------------------------------------------------------+ - | MA_ENABLE_COREAUDIO | Used in conjunction with MA_ENABLE_ONLY_SPECIFIC_BACKENDS to | - | | enable the Core Audio backend. | - +----------------------------------+--------------------------------------------------------------------+ - | MA_ENABLE_SNDIO | Used in conjunction with MA_ENABLE_ONLY_SPECIFIC_BACKENDS to | - | | enable the sndio backend. | - +----------------------------------+--------------------------------------------------------------------+ - | MA_ENABLE_AUDIO4 | Used in conjunction with MA_ENABLE_ONLY_SPECIFIC_BACKENDS to | - | | enable the audio(4) backend. | - +----------------------------------+--------------------------------------------------------------------+ - | MA_ENABLE_OSS | Used in conjunction with MA_ENABLE_ONLY_SPECIFIC_BACKENDS to | - | | enable the OSS backend. | - +----------------------------------+--------------------------------------------------------------------+ - | MA_ENABLE_AAUDIO | Used in conjunction with MA_ENABLE_ONLY_SPECIFIC_BACKENDS to | - | | enable the AAudio backend. | - +----------------------------------+--------------------------------------------------------------------+ - | MA_ENABLE_OPENSL | Used in conjunction with MA_ENABLE_ONLY_SPECIFIC_BACKENDS to | - | | enable the OpenSL|ES backend. | - +----------------------------------+--------------------------------------------------------------------+ - | MA_ENABLE_WEBAUDIO | Used in conjunction with MA_ENABLE_ONLY_SPECIFIC_BACKENDS to | - | | enable the Web Audio backend. | - +----------------------------------+--------------------------------------------------------------------+ - | MA_ENABLE_CUSTOM | Used in conjunction with MA_ENABLE_ONLY_SPECIFIC_BACKENDS to | - | | enable custom backends. | - +----------------------------------+--------------------------------------------------------------------+ - | MA_ENABLE_NULL | Used in conjunction with MA_ENABLE_ONLY_SPECIFIC_BACKENDS to | - | | enable the null backend. | - +----------------------------------+--------------------------------------------------------------------+ - | MA_NO_DECODING | Disables decoding APIs. | - +----------------------------------+--------------------------------------------------------------------+ - | MA_NO_ENCODING | Disables encoding APIs. | - +----------------------------------+--------------------------------------------------------------------+ - | MA_NO_WAV | Disables the built-in WAV decoder and encoder. | - +----------------------------------+--------------------------------------------------------------------+ - | MA_NO_FLAC | Disables the built-in FLAC decoder. | - +----------------------------------+--------------------------------------------------------------------+ - | MA_NO_MP3 | Disables the built-in MP3 decoder. | - +----------------------------------+--------------------------------------------------------------------+ - | MA_NO_DEVICE_IO | Disables playback and recording. This will disable `ma_context` | - | | and `ma_device` APIs. This is useful if you only want to use | - | | miniaudio's data conversion and/or decoding APIs. | - +----------------------------------+--------------------------------------------------------------------+ - | MA_NO_RESOURCE_MANAGER | Disables the resource manager. When using the engine this will | - | | also disable the following functions: | - | | | - | | ``` | - | | ma_sound_init_from_file() | - | | ma_sound_init_from_file_w() | - | | ma_sound_init_copy() | - | | ma_engine_play_sound_ex() | - | | ma_engine_play_sound() | - | | ``` | - | | | - | | The only way to initialize a `ma_sound` object is to initialize it | - | | from a data source. | - +----------------------------------+--------------------------------------------------------------------+ - | MA_NO_NODE_GRAPH | Disables the node graph API. This will also disable the engine API | - | | because it depends on the node graph. | - +----------------------------------+--------------------------------------------------------------------+ - | MA_NO_ENGINE | Disables the engine API. | - +----------------------------------+--------------------------------------------------------------------+ - | MA_NO_THREADING | Disables the `ma_thread`, `ma_mutex`, `ma_semaphore` and | - | | `ma_event` APIs. This option is useful if you only need to use | - | | miniaudio for data conversion, decoding and/or encoding. Some | - | | families of APIs require threading which means the following | - | | options must also be set: | - | | | - | | ``` | - | | MA_NO_DEVICE_IO | - | | ``` | - +----------------------------------+--------------------------------------------------------------------+ - | MA_NO_GENERATION | Disables generation APIs such a `ma_waveform` and `ma_noise`. | - +----------------------------------+--------------------------------------------------------------------+ - | MA_NO_SSE2 | Disables SSE2 optimizations. | - +----------------------------------+--------------------------------------------------------------------+ - | MA_NO_AVX2 | Disables AVX2 optimizations. | - +----------------------------------+--------------------------------------------------------------------+ - | MA_NO_NEON | Disables NEON optimizations. | - +----------------------------------+--------------------------------------------------------------------+ - | MA_NO_RUNTIME_LINKING | Disables runtime linking. This is useful for passing Apple's | - | | notarization process. When enabling this, you may need to avoid | - | | using `-std=c89` or `-std=c99` on Linux builds or else you may end | - | | up with compilation errors due to conflicts with `timespec` and | - | | `timeval` data types. | - | | | - | | You may need to enable this if your target platform does not allow | - | | runtime linking via `dlopen()`. | - +----------------------------------+--------------------------------------------------------------------+ - | MA_USE_STDINT | (Pass this in as a compiler flag. Do not `#define` this before | - | | miniaudio.c) Forces the use of stdint.h for sized types. | - +----------------------------------+--------------------------------------------------------------------+ - | MA_DEBUG_OUTPUT | Enable `printf()` output of debug logs (`MA_LOG_LEVEL_DEBUG`). | - +----------------------------------+--------------------------------------------------------------------+ - | MA_COINIT_VALUE | Windows only. The value to pass to internal calls to | - | | `CoInitializeEx()`. Defaults to `COINIT_MULTITHREADED`. | - +----------------------------------+--------------------------------------------------------------------+ - | MA_FORCE_UWP | Windows only. Affects only the WASAPI backend. Will force the | - | | WASAPI backend to use the UWP code path instead of the regular | - | | desktop path. This is normally auto-detected and should rarely be | - | | needed to be used explicitly, but can be useful for debugging. | - +----------------------------------+--------------------------------------------------------------------+ - | MA_ON_THREAD_ENTRY | Defines some code that will be executed as soon as an internal | - | | miniaudio-managed thread is created. This will be the first thing | - | | to be executed by the thread entry point. | - +----------------------------------+--------------------------------------------------------------------+ - | MA_ON_THREAD_EXIT | Defines some code that will be executed from the entry point of an | - | | internal miniaudio-managed thread upon exit. This will be the last | - | | thing to be executed before the thread's entry point exits. | - +----------------------------------+--------------------------------------------------------------------+ - | MA_THREAD_DEFAULT_STACK_SIZE | If set, specifies the default stack size used by miniaudio-managed | - | | threads. | - +----------------------------------+--------------------------------------------------------------------+ - | MA_API | Controls how public APIs should be decorated. Default is `extern`. | - +----------------------------------+--------------------------------------------------------------------+ - - -3. Definitions -============== -This section defines common terms used throughout miniaudio. Unfortunately there is often ambiguity -in the use of terms throughout the audio space, so this section is intended to clarify how miniaudio -uses each term. - -3.1. Sample ------------ -A sample is a single unit of audio data. If the sample format is f32, then one sample is one 32-bit -floating point number. - -3.2. Frame / PCM Frame ----------------------- -A frame is a group of samples equal to the number of channels. For a stereo stream a frame is 2 -samples, a mono frame is 1 sample, a 5.1 surround sound frame is 6 samples, etc. The terms "frame" -and "PCM frame" are the same thing in miniaudio. Note that this is different to a compressed frame. -If ever miniaudio needs to refer to a compressed frame, such as a FLAC frame, it will always -clarify what it's referring to with something like "FLAC frame". - -3.3. Channel ------------- -A stream of monaural audio that is emitted from an individual speaker in a speaker system, or -received from an individual microphone in a microphone system. A stereo stream has two channels (a -left channel, and a right channel), a 5.1 surround sound system has 6 channels, etc. Some audio -systems refer to a channel as a complex audio stream that's mixed with other channels to produce -the final mix - this is completely different to miniaudio's use of the term "channel" and should -not be confused. - -3.4. Sample Rate ----------------- -The sample rate in miniaudio is always expressed in Hz, such as 44100, 48000, etc. It's the number -of PCM frames that are processed per second. - -3.5. Formats ------------- -Throughout miniaudio you will see references to different sample formats: - - +---------------+----------------------------------------+---------------------------+ - | Symbol | Description | Range | - +---------------+----------------------------------------+---------------------------+ - | ma_format_f32 | 32-bit floating point | [-1, 1] | - | ma_format_s16 | 16-bit signed integer | [-32768, 32767] | - | ma_format_s24 | 24-bit signed integer (tightly packed) | [-8388608, 8388607] | - | ma_format_s32 | 32-bit signed integer | [-2147483648, 2147483647] | - | ma_format_u8 | 8-bit unsigned integer | [0, 255] | - +---------------+----------------------------------------+---------------------------+ - -All formats are native-endian. - - - -4. Data Sources -=============== -The data source abstraction in miniaudio is used for retrieving audio data from some source. A few -examples include `ma_decoder`, `ma_noise` and `ma_waveform`. You will need to be familiar with data -sources in order to make sense of some of the higher level concepts in miniaudio. - -The `ma_data_source` API is a generic interface for reading from a data source. Any object that -implements the data source interface can be plugged into any `ma_data_source` function. - -To read data from a data source: - - ```c - ma_result result; - ma_uint64 framesRead; - - result = ma_data_source_read_pcm_frames(pDataSource, pFramesOut, frameCount, &framesRead); - if (result != MA_SUCCESS) { - return result; // Failed to read data from the data source. - } - ``` - -If you don't need the number of frames that were successfully read you can pass in `NULL` to the -`pFramesRead` parameter. If this returns a value less than the number of frames requested it means -the end of the file has been reached. `MA_AT_END` will be returned only when the number of frames -read is 0. - -When calling any data source function, with the exception of `ma_data_source_init()` and -`ma_data_source_uninit()`, you can pass in any object that implements a data source. For example, -you could plug in a decoder like so: - - ```c - ma_result result; - ma_uint64 framesRead; - ma_decoder decoder; // <-- This would be initialized with `ma_decoder_init_*()`. - - result = ma_data_source_read_pcm_frames(&decoder, pFramesOut, frameCount, &framesRead); - if (result != MA_SUCCESS) { - return result; // Failed to read data from the decoder. - } - ``` - -If you want to seek forward you can pass in `NULL` to the `pFramesOut` parameter. Alternatively you -can use `ma_data_source_seek_pcm_frames()`. - -To seek to a specific PCM frame: - - ```c - result = ma_data_source_seek_to_pcm_frame(pDataSource, frameIndex); - if (result != MA_SUCCESS) { - return result; // Failed to seek to PCM frame. - } - ``` - -You can retrieve the total length of a data source in PCM frames, but note that some data sources -may not have the notion of a length, such as noise and waveforms, and others may just not have a -way of determining the length such as some decoders. To retrieve the length: - - ```c - ma_uint64 length; - - result = ma_data_source_get_length_in_pcm_frames(pDataSource, &length); - if (result != MA_SUCCESS) { - return result; // Failed to retrieve the length. - } - ``` - -Care should be taken when retrieving the length of a data source where the underlying decoder is -pulling data from a data stream with an undefined length, such as internet radio or some kind of -broadcast. If you do this, `ma_data_source_get_length_in_pcm_frames()` may never return. - -The current position of the cursor in PCM frames can also be retrieved: - - ```c - ma_uint64 cursor; - - result = ma_data_source_get_cursor_in_pcm_frames(pDataSource, &cursor); - if (result != MA_SUCCESS) { - return result; // Failed to retrieve the cursor. - } - ``` - -You will often need to know the data format that will be returned after reading. This can be -retrieved like so: - - ```c - ma_format format; - ma_uint32 channels; - ma_uint32 sampleRate; - ma_channel channelMap[MA_MAX_CHANNELS]; - - result = ma_data_source_get_data_format(pDataSource, &format, &channels, &sampleRate, channelMap, MA_MAX_CHANNELS); - if (result != MA_SUCCESS) { - return result; // Failed to retrieve data format. - } - ``` - -If you do not need a specific data format property, just pass in NULL to the respective parameter. - -There may be cases where you want to implement something like a sound bank where you only want to -read data within a certain range of the underlying data. To do this you can use a range: - - ```c - result = ma_data_source_set_range_in_pcm_frames(pDataSource, rangeBegInFrames, rangeEndInFrames); - if (result != MA_SUCCESS) { - return result; // Failed to set the range. - } - ``` - -This is useful if you have a sound bank where many sounds are stored in the same file and you want -the data source to only play one of those sub-sounds. Note that once the range is set, everything -that takes a position, such as cursors and loop points, should always be relatvie to the start of -the range. When the range is set, any previously defined loop point will be reset. - -Custom loop points can also be used with data sources. By default, data sources will loop after -they reach the end of the data source, but if you need to loop at a specific location, you can do -the following: - - ```c - result = ma_data_set_loop_point_in_pcm_frames(pDataSource, loopBegInFrames, loopEndInFrames); - if (result != MA_SUCCESS) { - return result; // Failed to set the loop point. - } - ``` - -The loop point is relative to the current range. - -It's sometimes useful to chain data sources together so that a seamless transition can be achieved. -To do this, you can use chaining: - - ```c - ma_decoder decoder1; - ma_decoder decoder2; - - // ... initialize decoders with ma_decoder_init_*() ... - - result = ma_data_source_set_next(&decoder1, &decoder2); - if (result != MA_SUCCESS) { - return result; // Failed to set the next data source. - } - - result = ma_data_source_read_pcm_frames(&decoder1, pFramesOut, frameCount, pFramesRead); - if (result != MA_SUCCESS) { - return result; // Failed to read from the decoder. - } - ``` - -In the example above we're using decoders. When reading from a chain, you always want to read from -the top level data source in the chain. In the example above, `decoder1` is the top level data -source in the chain. When `decoder1` reaches the end, `decoder2` will start seamlessly without any -gaps. - -Note that when looping is enabled, only the current data source will be looped. You can loop the -entire chain by linking in a loop like so: - - ```c - ma_data_source_set_next(&decoder1, &decoder2); // decoder1 -> decoder2 - ma_data_source_set_next(&decoder2, &decoder1); // decoder2 -> decoder1 (loop back to the start). - ``` - -Note that setting up chaining is not thread safe, so care needs to be taken if you're dynamically -changing links while the audio thread is in the middle of reading. - -Do not use `ma_decoder_seek_to_pcm_frame()` as a means to reuse a data source to play multiple -instances of the same sound simultaneously. This can be extremely inefficient depending on the type -of data source and can result in glitching due to subtle changes to the state of internal filters. -Instead, initialize multiple data sources for each instance. - - -4.1. Custom Data Sources ------------------------- -You can implement a custom data source by implementing the functions in `ma_data_source_vtable`. -Your custom object must have `ma_data_source_base` as it's first member: - - ```c - struct my_data_source - { - ma_data_source_base base; - ... - }; - ``` - -In your initialization routine, you need to call `ma_data_source_init()` in order to set up the -base object (`ma_data_source_base`): - - ```c - static ma_result my_data_source_read(ma_data_source* pDataSource, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead) - { - // Read data here. Output in the same format returned by my_data_source_get_data_format(). - } - - static ma_result my_data_source_seek(ma_data_source* pDataSource, ma_uint64 frameIndex) - { - // Seek to a specific PCM frame here. Return MA_NOT_IMPLEMENTED if seeking is not supported. - } - - static ma_result my_data_source_get_data_format(ma_data_source* pDataSource, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate, ma_channel* pChannelMap, size_t channelMapCap) - { - // Return the format of the data here. - } - - static ma_result my_data_source_get_cursor(ma_data_source* pDataSource, ma_uint64* pCursor) - { - // Retrieve the current position of the cursor here. Return MA_NOT_IMPLEMENTED and set *pCursor to 0 if there is no notion of a cursor. - } - - static ma_result my_data_source_get_length(ma_data_source* pDataSource, ma_uint64* pLength) - { - // Retrieve the length in PCM frames here. Return MA_NOT_IMPLEMENTED and set *pLength to 0 if there is no notion of a length or if the length is unknown. - } - - static ma_data_source_vtable g_my_data_source_vtable = - { - my_data_source_read, - my_data_source_seek, - my_data_source_get_data_format, - my_data_source_get_cursor, - my_data_source_get_length - }; - - ma_result my_data_source_init(my_data_source* pMyDataSource) - { - ma_result result; - ma_data_source_config baseConfig; - - baseConfig = ma_data_source_config_init(); - baseConfig.vtable = &g_my_data_source_vtable; - - result = ma_data_source_init(&baseConfig, &pMyDataSource->base); - if (result != MA_SUCCESS) { - return result; - } - - // ... do the initialization of your custom data source here ... - - return MA_SUCCESS; - } - - void my_data_source_uninit(my_data_source* pMyDataSource) - { - // ... do the uninitialization of your custom data source here ... - - // You must uninitialize the base data source. - ma_data_source_uninit(&pMyDataSource->base); - } - ``` - -Note that `ma_data_source_init()` and `ma_data_source_uninit()` are never called directly outside -of the custom data source. It's up to the custom data source itself to call these within their own -init/uninit functions. - - - -5. Engine -========= -The `ma_engine` API is a high level API for managing and mixing sounds and effect processing. The -`ma_engine` object encapsulates a resource manager and a node graph, both of which will be -explained in more detail later. - -Sounds are called `ma_sound` and are created from an engine. Sounds can be associated with a mixing -group called `ma_sound_group` which are also created from the engine. Both `ma_sound` and -`ma_sound_group` objects are nodes within the engine's node graph. - -When the engine is initialized, it will normally create a device internally. If you would rather -manage the device yourself, you can do so and just pass a pointer to it via the engine config when -you initialize the engine. You can also just use the engine without a device, which again can be -configured via the engine config. - -The most basic way to initialize the engine is with a default config, like so: - - ```c - ma_result result; - ma_engine engine; - - result = ma_engine_init(NULL, &engine); - if (result != MA_SUCCESS) { - return result; // Failed to initialize the engine. - } - ``` - -This will result in the engine initializing a playback device using the operating system's default -device. This will be sufficient for many use cases, but if you need more flexibility you'll want to -configure the engine with an engine config: - - ```c - ma_result result; - ma_engine engine; - ma_engine_config engineConfig; - - engineConfig = ma_engine_config_init(); - engineConfig.pDevice = &myDevice; - - result = ma_engine_init(&engineConfig, &engine); - if (result != MA_SUCCESS) { - return result; // Failed to initialize the engine. - } - ``` - -In the example above we're passing in a pre-initialized device. Since the caller is the one in -control of the device's data callback, it's their responsibility to manually call -`ma_engine_read_pcm_frames()` from inside their data callback: - - ```c - void playback_data_callback(ma_device* pDevice, void* pOutput, const void* pInput, ma_uint32 frameCount) - { - ma_engine_read_pcm_frames(&g_Engine, pOutput, frameCount, NULL); - } - ``` - -You can also use the engine independent of a device entirely: - - ```c - ma_result result; - ma_engine engine; - ma_engine_config engineConfig; - - engineConfig = ma_engine_config_init(); - engineConfig.noDevice = MA_TRUE; - engineConfig.channels = 2; // Must be set when not using a device. - engineConfig.sampleRate = 48000; // Must be set when not using a device. - - result = ma_engine_init(&engineConfig, &engine); - if (result != MA_SUCCESS) { - return result; // Failed to initialize the engine. - } - ``` - -Note that when you're not using a device, you must set the channel count and sample rate in the -config or else miniaudio won't know what to use (miniaudio will use the device to determine this -normally). When not using a device, you need to use `ma_engine_read_pcm_frames()` to process audio -data from the engine. This kind of setup is useful if you want to do something like offline -processing or want to use a different audio system for playback such as SDL. - -When a sound is loaded it goes through a resource manager. By default the engine will initialize a -resource manager internally, but you can also specify a pre-initialized resource manager: - - ```c - ma_result result; - ma_engine engine1; - ma_engine engine2; - ma_engine_config engineConfig; - - engineConfig = ma_engine_config_init(); - engineConfig.pResourceManager = &myResourceManager; - - ma_engine_init(&engineConfig, &engine1); - ma_engine_init(&engineConfig, &engine2); - ``` - -In this example we are initializing two engines, both of which are sharing the same resource -manager. This is especially useful for saving memory when loading the same file across multiple -engines. If you were not to use a shared resource manager, each engine instance would use their own -which would result in any sounds that are used between both engine's being loaded twice. By using -a shared resource manager, it would only be loaded once. Using multiple engine's is useful when you -need to output to multiple playback devices, such as in a local multiplayer game where each player -is using their own set of headphones. - -By default an engine will be in a started state. To make it so the engine is not automatically -started you can configure it as such: - - ```c - engineConfig.noAutoStart = MA_TRUE; - - // The engine will need to be started manually. - ma_engine_start(&engine); - - // Later on the engine can be stopped with ma_engine_stop(). - ma_engine_stop(&engine); - ``` - -The concept of starting or stopping an engine is only relevant when using the engine with a -device. Attempting to start or stop an engine that is not associated with a device will result in -`MA_INVALID_OPERATION`. - -The master volume of the engine can be controlled with `ma_engine_set_volume()` which takes a -linear scale, with 0 resulting in silence and anything above 1 resulting in amplification. If you -prefer decibel based volume control, use `ma_volume_db_to_linear()` to convert from dB to linear. - -When a sound is spatialized, it is done so relative to a listener. An engine can be configured to -have multiple listeners which can be configured via the config: - - ```c - engineConfig.listenerCount = 2; - ``` - -The maximum number of listeners is restricted to `MA_ENGINE_MAX_LISTENERS`. By default, when a -sound is spatialized, it will be done so relative to the closest listener. You can also pin a sound -to a specific listener which will be explained later. Listener's have a position, direction, cone, -and velocity (for doppler effect). A listener is referenced by an index, the meaning of which is up -to the caller (the index is 0 based and cannot go beyond the listener count, minus 1). The -position, direction and velocity are all specified in absolute terms: - - ```c - ma_engine_listener_set_position(&engine, listenerIndex, worldPosX, worldPosY, worldPosZ); - ``` - -The direction of the listener represents it's forward vector. The listener's up vector can also be -specified and defaults to +1 on the Y axis. - - ```c - ma_engine_listener_set_direction(&engine, listenerIndex, forwardX, forwardY, forwardZ); - ma_engine_listener_set_world_up(&engine, listenerIndex, 0, 1, 0); - ``` - -The engine supports directional attenuation. The listener can have a cone the controls how sound is -attenuated based on the listener's direction. When a sound is between the inner and outer cones, it -will be attenuated between 1 and the cone's outer gain: - - ```c - ma_engine_listener_set_cone(&engine, listenerIndex, innerAngleInRadians, outerAngleInRadians, outerGain); - ``` - -When a sound is inside the inner code, no directional attenuation is applied. When the sound is -outside of the outer cone, the attenuation will be set to `outerGain` in the example above. When -the sound is in between the inner and outer cones, the attenuation will be interpolated between 1 -and the outer gain. - -The engine's coordinate system follows the OpenGL coordinate system where positive X points right, -positive Y points up and negative Z points forward. - -The simplest and least flexible way to play a sound is like so: - - ```c - ma_engine_play_sound(&engine, "my_sound.wav", pGroup); - ``` - -This is a "fire and forget" style of function. The engine will manage the `ma_sound` object -internally. When the sound finishes playing, it'll be put up for recycling. For more flexibility -you'll want to initialize a sound object: - - ```c - ma_sound sound; - - result = ma_sound_init_from_file(&engine, "my_sound.wav", flags, pGroup, NULL, &sound); - if (result != MA_SUCCESS) { - return result; // Failed to load sound. - } - ``` - -Sounds need to be uninitialized with `ma_sound_uninit()`. - -The example above loads a sound from a file. If the resource manager has been disabled you will not -be able to use this function and instead you'll need to initialize a sound directly from a data -source: - - ```c - ma_sound sound; - - result = ma_sound_init_from_data_source(&engine, &dataSource, flags, pGroup, &sound); - if (result != MA_SUCCESS) { - return result; - } - ``` - -Each `ma_sound` object represents a single instance of the sound. If you want to play the same -sound multiple times at the same time, you need to initialize a separate `ma_sound` object. - -For the most flexibility when initializing sounds, use `ma_sound_init_ex()`. This uses miniaudio's -standard config/init pattern: - - ```c - ma_sound sound; - ma_sound_config soundConfig; - - soundConfig = ma_sound_config_init(); - soundConfig.pFilePath = NULL; // Set this to load from a file path. - soundConfig.pDataSource = NULL; // Set this to initialize from an existing data source. - soundConfig.pInitialAttachment = &someNodeInTheNodeGraph; - soundConfig.initialAttachmentInputBusIndex = 0; - soundConfig.channelsIn = 1; - soundConfig.channelsOut = 0; // Set to 0 to use the engine's native channel count. - - result = ma_sound_init_ex(&soundConfig, &sound); - if (result != MA_SUCCESS) { - return result; - } - ``` - -In the example above, the sound is being initialized without a file nor a data source. This is -valid, in which case the sound acts as a node in the middle of the node graph. This means you can -connect other sounds to this sound and allow it to act like a sound group. Indeed, this is exactly -what a `ma_sound_group` is. - -When loading a sound, you specify a set of flags that control how the sound is loaded and what -features are enabled for that sound. When no flags are set, the sound will be fully loaded into -memory in exactly the same format as how it's stored on the file system. The resource manager will -allocate a block of memory and then load the file directly into it. When reading audio data, it -will be decoded dynamically on the fly. In order to save processing time on the audio thread, it -might be beneficial to pre-decode the sound. You can do this with the `MA_SOUND_FLAG_DECODE` flag: - - ```c - ma_sound_init_from_file(&engine, "my_sound.wav", MA_SOUND_FLAG_DECODE, pGroup, NULL, &sound); - ``` - -By default, sounds will be loaded synchronously, meaning `ma_sound_init_*()` will not return until -the sound has been fully loaded. If this is prohibitive you can instead load sounds asynchronously -by specifying the `MA_SOUND_FLAG_ASYNC` flag: - - ```c - ma_sound_init_from_file(&engine, "my_sound.wav", MA_SOUND_FLAG_DECODE | MA_SOUND_FLAG_ASYNC, pGroup, NULL, &sound); - ``` - -This will result in `ma_sound_init_*()` returning quickly, but the sound won't yet have been fully -loaded. When you start the sound, it won't output anything until some sound is available. The sound -will start outputting audio before the sound has been fully decoded when the `MA_SOUND_FLAG_DECODE` -is specified. - -If you need to wait for an asynchronously loaded sound to be fully loaded, you can use a fence. A -fence in miniaudio is a simple synchronization mechanism which simply blocks until it's internal -counter hit's zero. You can specify a fence like so: - - ```c - ma_result result; - ma_fence fence; - ma_sound sounds[4]; - - result = ma_fence_init(&fence); - if (result != MA_SUCCESS) { - return result; - } - - // Load some sounds asynchronously. - for (int iSound = 0; iSound < 4; iSound += 1) { - ma_sound_init_from_file(&engine, mySoundFilesPaths[iSound], MA_SOUND_FLAG_DECODE | MA_SOUND_FLAG_ASYNC, pGroup, &fence, &sounds[iSound]); - } - - // ... do some other stuff here in the mean time ... - - // Wait for all sounds to finish loading. - ma_fence_wait(&fence); - ``` - -If loading the entire sound into memory is prohibitive, you can also configure the engine to stream -the audio data: - - ```c - ma_sound_init_from_file(&engine, "my_sound.wav", MA_SOUND_FLAG_STREAM, pGroup, NULL, &sound); - ``` - -When streaming sounds, 2 seconds worth of audio data is stored in memory. Although it should work -fine, it's inefficient to use streaming for short sounds. Streaming is useful for things like music -tracks in games. - -When loading a sound from a file path, the engine will reference count the file to prevent it from -being loaded if it's already in memory. When you uninitialize a sound, the reference count will be -decremented, and if it hits zero, the sound will be unloaded from memory. This reference counting -system is not used for streams. The engine will use a 64-bit hash of the file name when comparing -file paths which means there's a small chance you might encounter a name collision. If this is an -issue, you'll need to use a different name for one of the colliding file paths, or just not load -from files and instead load from a data source. - -You can use `ma_sound_init_copy()` to initialize a copy of another sound. Note, however, that this -only works for sounds that were initialized with `ma_sound_init_from_file()` and without the -`MA_SOUND_FLAG_STREAM` flag. - -When you initialize a sound, if you specify a sound group the sound will be attached to that group -automatically. If you set it to NULL, it will be automatically attached to the engine's endpoint. -If you would instead rather leave the sound unattached by default, you can specify the -`MA_SOUND_FLAG_NO_DEFAULT_ATTACHMENT` flag. This is useful if you want to set up a complex node -graph. - -Sounds are not started by default. To start a sound, use `ma_sound_start()`. Stop a sound with -`ma_sound_stop()`. - -Sounds can have their volume controlled with `ma_sound_set_volume()` in the same way as the -engine's master volume. - -Sounds support stereo panning and pitching. Set the pan with `ma_sound_set_pan()`. Setting the pan -to 0 will result in an unpanned sound. Setting it to -1 will shift everything to the left, whereas -+1 will shift it to the right. The pitch can be controlled with `ma_sound_set_pitch()`. A larger -value will result in a higher pitch. The pitch must be greater than 0. - -The engine supports 3D spatialization of sounds. By default sounds will have spatialization -enabled, but if a sound does not need to be spatialized it's best to disable it. There are two ways -to disable spatialization of a sound: - - ```c - // Disable spatialization at initialization time via a flag: - ma_sound_init_from_file(&engine, "my_sound.wav", MA_SOUND_FLAG_NO_SPATIALIZATION, NULL, NULL, &sound); - - // Dynamically disable or enable spatialization post-initialization: - ma_sound_set_spatialization_enabled(&sound, isSpatializationEnabled); - ``` - -By default sounds will be spatialized based on the closest listener. If a sound should always be -spatialized relative to a specific listener it can be pinned to one: - - ```c - ma_sound_set_pinned_listener_index(&sound, listenerIndex); - ``` - -Like listeners, sounds have a position. By default, the position of a sound is in absolute space, -but it can be changed to be relative to a listener: - - ```c - ma_sound_set_positioning(&sound, ma_positioning_relative); - ``` - -Note that relative positioning of a sound only makes sense if there is either only one listener, or -the sound is pinned to a specific listener. To set the position of a sound: - - ```c - ma_sound_set_position(&sound, posX, posY, posZ); - ``` - -The direction works the same way as a listener and represents the sound's forward direction: - - ```c - ma_sound_set_direction(&sound, forwardX, forwardY, forwardZ); - ``` - -Sound's also have a cone for controlling directional attenuation. This works exactly the same as -listeners: - - ```c - ma_sound_set_cone(&sound, innerAngleInRadians, outerAngleInRadians, outerGain); - ``` - -The velocity of a sound is used for doppler effect and can be set as such: - - ```c - ma_sound_set_velocity(&sound, velocityX, velocityY, velocityZ); - ``` - -The engine supports different attenuation models which can be configured on a per-sound basis. By -default the attenuation model is set to `ma_attenuation_model_inverse` which is the equivalent to -OpenAL's `AL_INVERSE_DISTANCE_CLAMPED`. Configure the attenuation model like so: - - ```c - ma_sound_set_attenuation_model(&sound, ma_attenuation_model_inverse); - ``` - -The supported attenuation models include the following: - - +----------------------------------+----------------------------------------------+ - | ma_attenuation_model_none | No distance attenuation. | - +----------------------------------+----------------------------------------------+ - | ma_attenuation_model_inverse | Equivalent to `AL_INVERSE_DISTANCE_CLAMPED`. | - +----------------------------------+----------------------------------------------+ - | ma_attenuation_model_linear | Linear attenuation. | - +----------------------------------+----------------------------------------------+ - | ma_attenuation_model_exponential | Exponential attenuation. | - +----------------------------------+----------------------------------------------+ - -To control how quickly a sound rolls off as it moves away from the listener, you need to configure -the rolloff: - - ```c - ma_sound_set_rolloff(&sound, rolloff); - ``` - -You can control the minimum and maximum gain to apply from spatialization: - - ```c - ma_sound_set_min_gain(&sound, minGain); - ma_sound_set_max_gain(&sound, maxGain); - ``` - -Likewise, in the calculation of attenuation, you can control the minimum and maximum distances for -the attenuation calculation. This is useful if you want to ensure sounds don't drop below a certain -volume after the listener moves further away and to have sounds play a maximum volume when the -listener is within a certain distance: - - ```c - ma_sound_set_min_distance(&sound, minDistance); - ma_sound_set_max_distance(&sound, maxDistance); - ``` - -The engine's spatialization system supports doppler effect. The doppler factor can be configure on -a per-sound basis like so: - - ```c - ma_sound_set_doppler_factor(&sound, dopplerFactor); - ``` - -You can fade sounds in and out with `ma_sound_set_fade_in_pcm_frames()` and -`ma_sound_set_fade_in_milliseconds()`. Set the volume to -1 to use the current volume as the -starting volume: - - ```c - // Fade in over 1 second. - ma_sound_set_fade_in_milliseconds(&sound, 0, 1, 1000); - - // ... sometime later ... - - // Fade out over 1 second, starting from the current volume. - ma_sound_set_fade_in_milliseconds(&sound, -1, 0, 1000); - ``` - -By default sounds will start immediately, but sometimes for timing and synchronization purposes it -can be useful to schedule a sound to start or stop: - - ```c - // Start the sound in 1 second from now. - ma_sound_set_start_time_in_pcm_frames(&sound, ma_engine_get_time_in_pcm_frames(&engine) + (ma_engine_get_sample_rate(&engine) * 1)); - - // Stop the sound in 2 seconds from now. - ma_sound_set_stop_time_in_pcm_frames(&sound, ma_engine_get_time_in_pcm_frames(&engine) + (ma_engine_get_sample_rate(&engine) * 2)); - ``` - -Note that scheduling a start time still requires an explicit call to `ma_sound_start()` before -anything will play. - -The time is specified in global time which is controlled by the engine. You can get the engine's -current time with `ma_engine_get_time_in_pcm_frames()`. The engine's global time is incremented -automatically as audio data is read, but it can be reset with `ma_engine_set_time_in_pcm_frames()` -in case it needs to be resynchronized for some reason. - -To determine whether or not a sound is currently playing, use `ma_sound_is_playing()`. This will -take the scheduled start and stop times into account. - -Whether or not a sound should loop can be controlled with `ma_sound_set_looping()`. Sounds will not -be looping by default. Use `ma_sound_is_looping()` to determine whether or not a sound is looping. - -Use `ma_sound_at_end()` to determine whether or not a sound is currently at the end. For a looping -sound this should never return true. Alternatively, you can configure a callback that will be fired -when the sound reaches the end. Note that the callback is fired from the audio thread which means -you cannot be uninitializing sound from the callback. To set the callback you can use -`ma_sound_set_end_callback()`. Alternatively, if you're using `ma_sound_init_ex()`, you can pass it -into the config like so: - - ```c - soundConfig.endCallback = my_end_callback; - soundConfig.pEndCallbackUserData = pMyEndCallbackUserData; - ``` - -The end callback is declared like so: - - ```c - void my_end_callback(void* pUserData, ma_sound* pSound) - { - ... - } - ``` - -Internally a sound wraps around a data source. Some APIs exist to control the underlying data -source, mainly for convenience: - - ```c - ma_sound_seek_to_pcm_frame(&sound, frameIndex); - ma_sound_get_data_format(&sound, &format, &channels, &sampleRate, pChannelMap, channelMapCapacity); - ma_sound_get_cursor_in_pcm_frames(&sound, &cursor); - ma_sound_get_length_in_pcm_frames(&sound, &length); - ``` - -Sound groups have the same API as sounds, only they are called `ma_sound_group`, and since they do -not have any notion of a data source, anything relating to a data source is unavailable. - -Internally, sound data is loaded via the `ma_decoder` API which means by default it only supports -file formats that have built-in support in miniaudio. You can extend this to support any kind of -file format through the use of custom decoders. To do this you'll need to use a self-managed -resource manager and configure it appropriately. See the "Resource Management" section below for -details on how to set this up. - - -6. Resource Management -====================== -Many programs will want to manage sound resources for things such as reference counting and -streaming. This is supported by miniaudio via the `ma_resource_manager` API. - -The resource manager is mainly responsible for the following: - - * Loading of sound files into memory with reference counting. - * Streaming of sound data. - -When loading a sound file, the resource manager will give you back a `ma_data_source` compatible -object called `ma_resource_manager_data_source`. This object can be passed into any -`ma_data_source` API which is how you can read and seek audio data. When loading a sound file, you -specify whether or not you want the sound to be fully loaded into memory (and optionally -pre-decoded) or streamed. When loading into memory, you can also specify whether or not you want -the data to be loaded asynchronously. - -The example below is how you can initialize a resource manager using it's default configuration: - - ```c - ma_resource_manager_config config; - ma_resource_manager resourceManager; - - config = ma_resource_manager_config_init(); - result = ma_resource_manager_init(&config, &resourceManager); - if (result != MA_SUCCESS) { - ma_device_uninit(&device); - printf("Failed to initialize the resource manager."); - return -1; - } - ``` - -You can configure the format, channels and sample rate of the decoded audio data. By default it -will use the file's native data format, but you can configure it to use a consistent format. This -is useful for offloading the cost of data conversion to load time rather than dynamically -converting at mixing time. To do this, you configure the decoded format, channels and sample rate -like the code below: - - ```c - config = ma_resource_manager_config_init(); - config.decodedFormat = device.playback.format; - config.decodedChannels = device.playback.channels; - config.decodedSampleRate = device.sampleRate; - ``` - -In the code above, the resource manager will be configured so that any decoded audio data will be -pre-converted at load time to the device's native data format. If instead you used defaults and -the data format of the file did not match the device's data format, you would need to convert the -data at mixing time which may be prohibitive in high-performance and large scale scenarios like -games. - -Internally the resource manager uses the `ma_decoder` API to load sounds. This means by default it -only supports decoders that are built into miniaudio. It's possible to support additional encoding -formats through the use of custom decoders. To do so, pass in your `ma_decoding_backend_vtable` -vtables into the resource manager config: - - ```c - ma_decoding_backend_vtable* pCustomBackendVTables[] = - { - &g_ma_decoding_backend_vtable_libvorbis, - &g_ma_decoding_backend_vtable_libopus - }; - - ... - - resourceManagerConfig.ppCustomDecodingBackendVTables = pCustomBackendVTables; - resourceManagerConfig.customDecodingBackendCount = sizeof(pCustomBackendVTables) / sizeof(pCustomBackendVTables[0]); - resourceManagerConfig.pCustomDecodingBackendUserData = NULL; - ``` - -This system can allow you to support any kind of file format. See the "Decoding" section for -details on how to implement custom decoders. The miniaudio repository includes examples for Opus -via libopus and libopusfile and Vorbis via libvorbis and libvorbisfile. - -Asynchronicity is achieved via a job system. When an operation needs to be performed, such as the -decoding of a page, a job will be posted to a queue which will then be processed by a job thread. -By default there will be only one job thread running, but this can be configured, like so: - - ```c - config = ma_resource_manager_config_init(); - config.jobThreadCount = MY_JOB_THREAD_COUNT; - ``` - -By default job threads are managed internally by the resource manager, however you can also self -manage your job threads if, for example, you want to integrate the job processing into your -existing job infrastructure, or if you simply don't like the way the resource manager does it. To -do this, just set the job thread count to 0 and process jobs manually. To process jobs, you first -need to retrieve a job using `ma_resource_manager_next_job()` and then process it using -`ma_job_process()`: - - ```c - config = ma_resource_manager_config_init(); - config.jobThreadCount = 0; // Don't manage any job threads internally. - config.flags = MA_RESOURCE_MANAGER_FLAG_NON_BLOCKING; // Optional. Makes `ma_resource_manager_next_job()` non-blocking. - - // ... Initialize your custom job threads ... - - void my_custom_job_thread(...) - { - for (;;) { - ma_job job; - ma_result result = ma_resource_manager_next_job(pMyResourceManager, &job); - if (result != MA_SUCCESS) { - if (result == MA_NO_DATA_AVAILABLE) { - // No jobs are available. Keep going. Will only get this if the resource manager was initialized - // with MA_RESOURCE_MANAGER_FLAG_NON_BLOCKING. - continue; - } else if (result == MA_CANCELLED) { - // MA_JOB_TYPE_QUIT was posted. Exit. - break; - } else { - // Some other error occurred. - break; - } - } - - ma_job_process(&job); - } - } - ``` - -In the example above, the `MA_JOB_TYPE_QUIT` event is the used as the termination -indicator, but you can use whatever you would like to terminate the thread. The call to -`ma_resource_manager_next_job()` is blocking by default, but can be configured to be non-blocking -by initializing the resource manager with the `MA_RESOURCE_MANAGER_FLAG_NON_BLOCKING` configuration -flag. Note that the `MA_JOB_TYPE_QUIT` will never be removed from the job queue. This -is to give every thread the opportunity to catch the event and terminate naturally. - -When loading a file, it's sometimes convenient to be able to customize how files are opened and -read instead of using standard `fopen()`, `fclose()`, etc. which is what miniaudio will use by -default. This can be done by setting `pVFS` member of the resource manager's config: - - ```c - // Initialize your custom VFS object. See documentation for VFS for information on how to do this. - my_custom_vfs vfs = my_custom_vfs_init(); - - config = ma_resource_manager_config_init(); - config.pVFS = &vfs; - ``` - -This is particularly useful in programs like games where you want to read straight from an archive -rather than the normal file system. If you do not specify a custom VFS, the resource manager will -use the operating system's normal file operations. - -To load a sound file and create a data source, call `ma_resource_manager_data_source_init()`. When -loading a sound you need to specify the file path and options for how the sounds should be loaded. -By default a sound will be loaded synchronously. The returned data source is owned by the caller -which means the caller is responsible for the allocation and freeing of the data source. Below is -an example for initializing a data source: - - ```c - ma_resource_manager_data_source dataSource; - ma_result result = ma_resource_manager_data_source_init(pResourceManager, pFilePath, flags, &dataSource); - if (result != MA_SUCCESS) { - // Error. - } - - // ... - - // A ma_resource_manager_data_source object is compatible with the `ma_data_source` API. To read data, just call - // the `ma_data_source_read_pcm_frames()` like you would with any normal data source. - result = ma_data_source_read_pcm_frames(&dataSource, pDecodedData, frameCount, &framesRead); - if (result != MA_SUCCESS) { - // Failed to read PCM frames. - } - - // ... - - ma_resource_manager_data_source_uninit(&dataSource); - ``` - -The `flags` parameter specifies how you want to perform loading of the sound file. It can be a -combination of the following flags: - - ``` - MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_STREAM - MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_DECODE - MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_ASYNC - MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_WAIT_INIT - MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_LOOPING - ``` - -When no flags are specified (set to 0), the sound will be fully loaded into memory, but not -decoded, meaning the raw file data will be stored in memory, and then dynamically decoded when -`ma_data_source_read_pcm_frames()` is called. To instead decode the audio data before storing it in -memory, use the `MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_DECODE` flag. By default, the sound file will -be loaded synchronously, meaning `ma_resource_manager_data_source_init()` will only return after -the entire file has been loaded. This is good for simplicity, but can be prohibitively slow. You -can instead load the sound asynchronously using the `MA_RESOURCE_MANAGER_DATA_SOURCE_ASYNC` flag. -This will result in `ma_resource_manager_data_source_init()` returning quickly, but no data will be -returned by `ma_data_source_read_pcm_frames()` until some data is available. When no data is -available because the asynchronous decoding hasn't caught up, `MA_BUSY` will be returned by -`ma_data_source_read_pcm_frames()`. - -For large sounds, it's often prohibitive to store the entire file in memory. To mitigate this, you -can instead stream audio data which you can do by specifying the -`MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_STREAM` flag. When streaming, data will be decoded in 1 -second pages. When a new page needs to be decoded, a job will be posted to the job queue and then -subsequently processed in a job thread. - -The `MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_LOOPING` flag can be used so that the sound will loop -when it reaches the end by default. It's recommended you use this flag when you want to have a -looping streaming sound. If you try loading a very short sound as a stream, you will get a glitch. -This is because the resource manager needs to pre-fill the initial buffer at initialization time, -and if you don't specify the `MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_LOOPING` flag, the resource -manager will assume the sound is not looping and will stop filling the buffer when it reaches the -end, therefore resulting in a discontinuous buffer. - -For in-memory sounds, reference counting is used to ensure the data is loaded only once. This means -multiple calls to `ma_resource_manager_data_source_init()` with the same file path will result in -the file data only being loaded once. Each call to `ma_resource_manager_data_source_init()` must be -matched up with a call to `ma_resource_manager_data_source_uninit()`. Sometimes it can be useful -for a program to register self-managed raw audio data and associate it with a file path. Use the -`ma_resource_manager_register_*()` and `ma_resource_manager_unregister_*()` APIs to do this. -`ma_resource_manager_register_decoded_data()` is used to associate a pointer to raw, self-managed -decoded audio data in the specified data format with the specified name. Likewise, -`ma_resource_manager_register_encoded_data()` is used to associate a pointer to raw self-managed -encoded audio data (the raw file data) with the specified name. Note that these names need not be -actual file paths. When `ma_resource_manager_data_source_init()` is called (without the -`MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_STREAM` flag), the resource manager will look for these -explicitly registered data buffers and, if found, will use it as the backing data for the data -source. Note that the resource manager does *not* make a copy of this data so it is up to the -caller to ensure the pointer stays valid for its lifetime. Use -`ma_resource_manager_unregister_data()` to unregister the self-managed data. You can also use -`ma_resource_manager_register_file()` and `ma_resource_manager_unregister_file()` to register and -unregister a file. It does not make sense to use the `MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_STREAM` -flag with a self-managed data pointer. - - -6.1. Asynchronous Loading and Synchronization ---------------------------------------------- -When loading asynchronously, it can be useful to poll whether or not loading has finished. Use -`ma_resource_manager_data_source_result()` to determine this. For in-memory sounds, this will -return `MA_SUCCESS` when the file has been *entirely* decoded. If the sound is still being decoded, -`MA_BUSY` will be returned. Otherwise, some other error code will be returned if the sound failed -to load. For streaming data sources, `MA_SUCCESS` will be returned when the first page has been -decoded and the sound is ready to be played. If the first page is still being decoded, `MA_BUSY` -will be returned. Otherwise, some other error code will be returned if the sound failed to load. - -In addition to polling, you can also use a simple synchronization object called a "fence" to wait -for asynchronously loaded sounds to finish. This is called `ma_fence`. The advantage to using a -fence is that it can be used to wait for a group of sounds to finish loading rather than waiting -for sounds on an individual basis. There are two stages to loading a sound: - - * Initialization of the internal decoder; and - * Completion of decoding of the file (the file is fully decoded) - -You can specify separate fences for each of the different stages. Waiting for the initialization -of the internal decoder is important for when you need to know the sample format, channels and -sample rate of the file. - -The example below shows how you could use a fence when loading a number of sounds: - - ```c - // This fence will be released when all sounds are finished loading entirely. - ma_fence fence; - ma_fence_init(&fence); - - // This will be passed into the initialization routine for each sound. - ma_resource_manager_pipeline_notifications notifications = ma_resource_manager_pipeline_notifications_init(); - notifications.done.pFence = &fence; - - // Now load a bunch of sounds: - for (iSound = 0; iSound < soundCount; iSound += 1) { - ma_resource_manager_data_source_init(pResourceManager, pSoundFilePaths[iSound], flags, ¬ifications, &pSoundSources[iSound]); - } - - // ... DO SOMETHING ELSE WHILE SOUNDS ARE LOADING ... - - // Wait for loading of sounds to finish. - ma_fence_wait(&fence); - ``` - -In the example above we used a fence for waiting until the entire file has been fully decoded. If -you only need to wait for the initialization of the internal decoder to complete, you can use the -`init` member of the `ma_resource_manager_pipeline_notifications` object: - - ```c - notifications.init.pFence = &fence; - ``` - -If a fence is not appropriate for your situation, you can instead use a callback that is fired on -an individual sound basis. This is done in a very similar way to fences: - - ```c - typedef struct - { - ma_async_notification_callbacks cb; - void* pMyData; - } my_notification; - - void my_notification_callback(ma_async_notification* pNotification) - { - my_notification* pMyNotification = (my_notification*)pNotification; - - // Do something in response to the sound finishing loading. - } - - ... - - my_notification myCallback; - myCallback.cb.onSignal = my_notification_callback; - myCallback.pMyData = pMyData; - - ma_resource_manager_pipeline_notifications notifications = ma_resource_manager_pipeline_notifications_init(); - notifications.done.pNotification = &myCallback; - - ma_resource_manager_data_source_init(pResourceManager, "my_sound.wav", flags, ¬ifications, &mySound); - ``` - -In the example above we just extend the `ma_async_notification_callbacks` object and pass an -instantiation into the `ma_resource_manager_pipeline_notifications` in the same way as we did with -the fence, only we set `pNotification` instead of `pFence`. You can set both of these at the same -time and they should both work as expected. If using the `pNotification` system, you need to ensure -your `ma_async_notification_callbacks` object stays valid. - - - -6.2. Resource Manager Implementation Details --------------------------------------------- -Resources are managed in two main ways: - - * By storing the entire sound inside an in-memory buffer (referred to as a data buffer) - * By streaming audio data on the fly (referred to as a data stream) - -A resource managed data source (`ma_resource_manager_data_source`) encapsulates a data buffer or -data stream, depending on whether or not the data source was initialized with the -`MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_STREAM` flag. If so, it will make use of a -`ma_resource_manager_data_stream` object. Otherwise it will use a `ma_resource_manager_data_buffer` -object. Both of these objects are data sources which means they can be used with any -`ma_data_source_*()` API. - -Another major feature of the resource manager is the ability to asynchronously decode audio files. -This relieves the audio thread of time-consuming decoding which can negatively affect scalability -due to the audio thread needing to complete it's work extremely quickly to avoid glitching. -Asynchronous decoding is achieved through a job system. There is a central multi-producer, -multi-consumer, fixed-capacity job queue. When some asynchronous work needs to be done, a job is -posted to the queue which is then read by a job thread. The number of job threads can be -configured for improved scalability, and job threads can all run in parallel without needing to -worry about the order of execution (how this is achieved is explained below). - -When a sound is being loaded asynchronously, playback can begin before the sound has been fully -decoded. This enables the application to start playback of the sound quickly, while at the same -time allowing to resource manager to keep loading in the background. Since there may be less -threads than the number of sounds being loaded at a given time, a simple scheduling system is used -to keep decoding time balanced and fair. The resource manager solves this by splitting decoding -into chunks called pages. By default, each page is 1 second long. When a page has been decoded, a -new job will be posted to start decoding the next page. By dividing up decoding into pages, an -individual sound shouldn't ever delay every other sound from having their first page decoded. Of -course, when loading many sounds at the same time, there will always be an amount of time required -to process jobs in the queue so in heavy load situations there will still be some delay. To -determine if a data source is ready to have some frames read, use -`ma_resource_manager_data_source_get_available_frames()`. This will return the number of frames -available starting from the current position. - - -6.2.1. Job Queue ----------------- -The resource manager uses a job queue which is multi-producer, multi-consumer, and fixed-capacity. -This job queue is not currently lock-free, and instead uses a spinlock to achieve thread-safety. -Only a fixed number of jobs can be allocated and inserted into the queue which is done through a -lock-free data structure for allocating an index into a fixed sized array, with reference counting -for mitigation of the ABA problem. The reference count is 32-bit. - -For many types of jobs it's important that they execute in a specific order. In these cases, jobs -are executed serially. For the resource manager, serial execution of jobs is only required on a -per-object basis (per data buffer or per data stream). Each of these objects stores an execution -counter. When a job is posted it is associated with an execution counter. When the job is -processed, it checks if the execution counter of the job equals the execution counter of the -owning object and if so, processes the job. If the counters are not equal, the job will be posted -back onto the job queue for later processing. When the job finishes processing the execution order -of the main object is incremented. This system means the no matter how many job threads are -executing, decoding of an individual sound will always get processed serially. The advantage to -having multiple threads comes into play when loading multiple sounds at the same time. - -The resource manager's job queue is not 100% lock-free and will use a spinlock to achieve -thread-safety for a very small section of code. This is only relevant when the resource manager -uses more than one job thread. If only using a single job thread, which is the default, the -lock should never actually wait in practice. The amount of time spent locking should be quite -short, but it's something to be aware of for those who have pedantic lock-free requirements and -need to use more than one job thread. There are plans to remove this lock in a future version. - -In addition, posting a job will release a semaphore, which on Win32 is implemented with -`ReleaseSemaphore` and on POSIX platforms via a condition variable: - - ```c - pthread_mutex_lock(&pSemaphore->lock); - { - pSemaphore->value += 1; - pthread_cond_signal(&pSemaphore->cond); - } - pthread_mutex_unlock(&pSemaphore->lock); - ``` - -Again, this is relevant for those with strict lock-free requirements in the audio thread. To avoid -this, you can use non-blocking mode (via the `MA_JOB_QUEUE_FLAG_NON_BLOCKING` -flag) and implement your own job processing routine (see the "Resource Manager" section above for -details on how to do this). - - - -6.2.2. Data Buffers -------------------- -When the `MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_STREAM` flag is excluded at initialization time, the -resource manager will try to load the data into an in-memory data buffer. Before doing so, however, -it will first check if the specified file is already loaded. If so, it will increment a reference -counter and just use the already loaded data. This saves both time and memory. When the data buffer -is uninitialized, the reference counter will be decremented. If the counter hits zero, the file -will be unloaded. This is a detail to keep in mind because it could result in excessive loading and -unloading of a sound. For example, the following sequence will result in a file be loaded twice, -once after the other: - - ```c - ma_resource_manager_data_source_init(pResourceManager, "my_file", ..., &myDataBuffer0); // Refcount = 1. Initial load. - ma_resource_manager_data_source_uninit(&myDataBuffer0); // Refcount = 0. Unloaded. - - ma_resource_manager_data_source_init(pResourceManager, "my_file", ..., &myDataBuffer1); // Refcount = 1. Reloaded because previous uninit() unloaded it. - ma_resource_manager_data_source_uninit(&myDataBuffer1); // Refcount = 0. Unloaded. - ``` - -A binary search tree (BST) is used for storing data buffers as it has good balance between -efficiency and simplicity. The key of the BST is a 64-bit hash of the file path that was passed -into `ma_resource_manager_data_source_init()`. The advantage of using a hash is that it saves -memory over storing the entire path, has faster comparisons, and results in a mostly balanced BST -due to the random nature of the hash. The disadvantages are that file names are case-sensitive and -there's a small chance of name collisions. If case-sensitivity is an issue, you should normalize -your file names to upper- or lower-case before initializing your data sources. If name collisions -become an issue, you'll need to change the name of one of the colliding names or just not use the -resource manager. - -When a sound file has not already been loaded and the `MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_ASYNC` -flag is excluded, the file will be decoded synchronously by the calling thread. There are two -options for controlling how the audio is stored in the data buffer - encoded or decoded. When the -`MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_DECODE` option is excluded, the raw file data will be stored -in memory. Otherwise the sound will be decoded before storing it in memory. Synchronous loading is -a very simple and standard process of simply adding an item to the BST, allocating a block of -memory and then decoding (if `MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_DECODE` is specified). - -When the `MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_ASYNC` flag is specified, loading of the data buffer -is done asynchronously. In this case, a job is posted to the queue to start loading and then the -function immediately returns, setting an internal result code to `MA_BUSY`. This result code is -returned when the program calls `ma_resource_manager_data_source_result()`. When decoding has fully -completed `MA_SUCCESS` will be returned. This can be used to know if loading has fully completed. - -When loading asynchronously, a single job is posted to the queue of the type -`MA_JOB_TYPE_RESOURCE_MANAGER_LOAD_DATA_BUFFER_NODE`. This involves making a copy of the file path and -associating it with job. When the job is processed by the job thread, it will first load the file -using the VFS associated with the resource manager. When using a custom VFS, it's important that it -be completely thread-safe because it will be used from one or more job threads at the same time. -Individual files should only ever be accessed by one thread at a time, however. After opening the -file via the VFS, the job will determine whether or not the file is being decoded. If not, it -simply allocates a block of memory and loads the raw file contents into it and returns. On the -other hand, when the file is being decoded, it will first allocate a decoder on the heap and -initialize it. Then it will check if the length of the file is known. If so it will allocate a -block of memory to store the decoded output and initialize it to silence. If the size is unknown, -it will allocate room for one page. After memory has been allocated, the first page will be -decoded. If the sound is shorter than a page, the result code will be set to `MA_SUCCESS` and the -completion event will be signalled and loading is now complete. If, however, there is more to -decode, a job with the code `MA_JOB_TYPE_RESOURCE_MANAGER_PAGE_DATA_BUFFER_NODE` is posted. This job -will decode the next page and perform the same process if it reaches the end. If there is more to -decode, the job will post another `MA_JOB_TYPE_RESOURCE_MANAGER_PAGE_DATA_BUFFER_NODE` job which will -keep on happening until the sound has been fully decoded. For sounds of an unknown length, each -page will be linked together as a linked list. Internally this is implemented via the -`ma_paged_audio_buffer` object. - - -6.2.3. Data Streams -------------------- -Data streams only ever store two pages worth of data for each instance. They are most useful for -large sounds like music tracks in games that would consume too much memory if fully decoded in -memory. After every frame from a page has been read, a job will be posted to load the next page -which is done from the VFS. - -For data streams, the `MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_ASYNC` flag will determine whether or -not initialization of the data source waits until the two pages have been decoded. When unset, -`ma_resource_manager_data_source_init()` will wait until the two pages have been loaded, otherwise -it will return immediately. - -When frames are read from a data stream using `ma_resource_manager_data_source_read_pcm_frames()`, -`MA_BUSY` will be returned if there are no frames available. If there are some frames available, -but less than the number requested, `MA_SUCCESS` will be returned, but the actual number of frames -read will be less than the number requested. Due to the asynchronous nature of data streams, -seeking is also asynchronous. If the data stream is in the middle of a seek, `MA_BUSY` will be -returned when trying to read frames. - -When `ma_resource_manager_data_source_read_pcm_frames()` results in a page getting fully consumed -a job is posted to load the next page. This will be posted from the same thread that called -`ma_resource_manager_data_source_read_pcm_frames()`. - -Data streams are uninitialized by posting a job to the queue, but the function won't return until -that job has been processed. The reason for this is that the caller owns the data stream object and -therefore miniaudio needs to ensure everything completes before handing back control to the caller. -Also, if the data stream is uninitialized while pages are in the middle of decoding, they must -complete before destroying any underlying object and the job system handles this cleanly. - -Note that when a new page needs to be loaded, a job will be posted to the resource manager's job -thread from the audio thread. You must keep in mind the details mentioned in the "Job Queue" -section above regarding locking when posting an event if you require a strictly lock-free audio -thread. - - - -7. Node Graph -============= -miniaudio's routing infrastructure follows a node graph paradigm. The idea is that you create a -node whose outputs are attached to inputs of another node, thereby creating a graph. There are -different types of nodes, with each node in the graph processing input data to produce output, -which is then fed through the chain. Each node in the graph can apply their own custom effects. At -the start of the graph will usually be one or more data source nodes which have no inputs and -instead pull their data from a data source. At the end of the graph is an endpoint which represents -the end of the chain and is where the final output is ultimately extracted from. - -Each node has a number of input buses and a number of output buses. An output bus from a node is -attached to an input bus of another. Multiple nodes can connect their output buses to another -node's input bus, in which case their outputs will be mixed before processing by the node. Below is -a diagram that illustrates a hypothetical node graph setup: - - ``` - >>>>>>>>>>>>>>>>>>>>>>>>>>>>>> Data flows left to right >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> - - +---------------+ +-----------------+ - | Data Source 1 =----+ +----------+ +----= Low Pass Filter =----+ - +---------------+ | | =----+ +-----------------+ | +----------+ - +----= Splitter | +----= ENDPOINT | - +---------------+ | | =----+ +-----------------+ | +----------+ - | Data Source 2 =----+ +----------+ +----= Echo / Delay =----+ - +---------------+ +-----------------+ - ``` - -In the above graph, it starts with two data sources whose outputs are attached to the input of a -splitter node. It's at this point that the two data sources are mixed. After mixing, the splitter -performs it's processing routine and produces two outputs which is simply a duplication of the -input stream. One output is attached to a low pass filter, whereas the other output is attached to -a echo/delay. The outputs of the low pass filter and the echo are attached to the endpoint, and -since they're both connected to the same input bus, they'll be mixed. - -Each input bus must be configured to accept the same number of channels, but the number of channels -used by input buses can be different to the number of channels for output buses in which case -miniaudio will automatically convert the input data to the output channel count before processing. -The number of channels of an output bus of one node must match the channel count of the input bus -it's attached to. The channel counts cannot be changed after the node has been initialized. If you -attempt to attach an output bus to an input bus with a different channel count, attachment will -fail. - -To use a node graph, you first need to initialize a `ma_node_graph` object. This is essentially a -container around the entire graph. The `ma_node_graph` object is required for some thread-safety -issues which will be explained later. A `ma_node_graph` object is initialized using miniaudio's -standard config/init system: - - ```c - ma_node_graph_config nodeGraphConfig = ma_node_graph_config_init(myChannelCount); - - result = ma_node_graph_init(&nodeGraphConfig, NULL, &nodeGraph); // Second parameter is a pointer to allocation callbacks. - if (result != MA_SUCCESS) { - // Failed to initialize node graph. - } - ``` - -When you initialize the node graph, you're specifying the channel count of the endpoint. The -endpoint is a special node which has one input bus and one output bus, both of which have the -same channel count, which is specified in the config. Any nodes that connect directly to the -endpoint must be configured such that their output buses have the same channel count. When you read -audio data from the node graph, it'll have the channel count you specified in the config. To read -data from the graph: - - ```c - ma_uint32 framesRead; - result = ma_node_graph_read_pcm_frames(&nodeGraph, pFramesOut, frameCount, &framesRead); - if (result != MA_SUCCESS) { - // Failed to read data from the node graph. - } - ``` - -When you read audio data, miniaudio starts at the node graph's endpoint node which then pulls in -data from its input attachments, which in turn recursively pull in data from their inputs, and so -on. At the start of the graph there will be some kind of data source node which will have zero -inputs and will instead read directly from a data source. The base nodes don't literally need to -read from a `ma_data_source` object, but they will always have some kind of underlying object that -sources some kind of audio. The `ma_data_source_node` node can be used to read from a -`ma_data_source`. Data is always in floating-point format and in the number of channels you -specified when the graph was initialized. The sample rate is defined by the underlying data sources. -It's up to you to ensure they use a consistent and appropriate sample rate. - -The `ma_node` API is designed to allow custom nodes to be implemented with relative ease, but -miniaudio includes a few stock nodes for common functionality. This is how you would initialize a -node which reads directly from a data source (`ma_data_source_node`) which is an example of one -of the stock nodes that comes with miniaudio: - - ```c - ma_data_source_node_config config = ma_data_source_node_config_init(pMyDataSource); - - ma_data_source_node dataSourceNode; - result = ma_data_source_node_init(&nodeGraph, &config, NULL, &dataSourceNode); - if (result != MA_SUCCESS) { - // Failed to create data source node. - } - ``` - -The data source node will use the output channel count to determine the channel count of the output -bus. There will be 1 output bus and 0 input buses (data will be drawn directly from the data -source). The data source must output to floating-point (`ma_format_f32`) or else an error will be -returned from `ma_data_source_node_init()`. - -By default the node will not be attached to the graph. To do so, use `ma_node_attach_output_bus()`: - - ```c - result = ma_node_attach_output_bus(&dataSourceNode, 0, ma_node_graph_get_endpoint(&nodeGraph), 0); - if (result != MA_SUCCESS) { - // Failed to attach node. - } - ``` - -The code above connects the data source node directly to the endpoint. Since the data source node -has only a single output bus, the index will always be 0. Likewise, the endpoint only has a single -input bus which means the input bus index will also always be 0. - -To detach a specific output bus, use `ma_node_detach_output_bus()`. To detach all output buses, use -`ma_node_detach_all_output_buses()`. If you want to just move the output bus from one attachment to -another, you do not need to detach first. You can just call `ma_node_attach_output_bus()` and it'll -deal with it for you. - -Less frequently you may want to create a specialized node. This will be a node where you implement -your own processing callback to apply a custom effect of some kind. This is similar to initializing -one of the stock node types, only this time you need to specify a pointer to a vtable containing a -pointer to the processing function and the number of input and output buses. Example: - - ```c - static void my_custom_node_process_pcm_frames(ma_node* pNode, const float** ppFramesIn, ma_uint32* pFrameCountIn, float** ppFramesOut, ma_uint32* pFrameCountOut) - { - // Do some processing of ppFramesIn (one stream of audio data per input bus) - const float* pFramesIn_0 = ppFramesIn[0]; // Input bus @ index 0. - const float* pFramesIn_1 = ppFramesIn[1]; // Input bus @ index 1. - float* pFramesOut_0 = ppFramesOut[0]; // Output bus @ index 0. - - // Do some processing. On input, `pFrameCountIn` will be the number of input frames in each - // buffer in `ppFramesIn` and `pFrameCountOut` will be the capacity of each of the buffers - // in `ppFramesOut`. On output, `pFrameCountIn` should be set to the number of input frames - // your node consumed and `pFrameCountOut` should be set the number of output frames that - // were produced. - // - // You should process as many frames as you can. If your effect consumes input frames at the - // same rate as output frames (always the case, unless you're doing resampling), you need - // only look at `ppFramesOut` and process that exact number of frames. If you're doing - // resampling, you'll need to be sure to set both `pFrameCountIn` and `pFrameCountOut` - // properly. - } - - static ma_node_vtable my_custom_node_vtable = - { - my_custom_node_process_pcm_frames, // The function that will be called to process your custom node. This is where you'd implement your effect processing. - NULL, // Optional. A callback for calculating the number of input frames that are required to process a specified number of output frames. - 2, // 2 input buses. - 1, // 1 output bus. - 0 // Default flags. - }; - - ... - - // Each bus needs to have a channel count specified. To do this you need to specify the channel - // counts in an array and then pass that into the node config. - ma_uint32 inputChannels[2]; // Equal in size to the number of input channels specified in the vtable. - ma_uint32 outputChannels[1]; // Equal in size to the number of output channels specified in the vtable. - - inputChannels[0] = channelsIn; - inputChannels[1] = channelsIn; - outputChannels[0] = channelsOut; - - ma_node_config nodeConfig = ma_node_config_init(); - nodeConfig.vtable = &my_custom_node_vtable; - nodeConfig.pInputChannels = inputChannels; - nodeConfig.pOutputChannels = outputChannels; - - ma_node_base node; - result = ma_node_init(&nodeGraph, &nodeConfig, NULL, &node); - if (result != MA_SUCCESS) { - // Failed to initialize node. - } - ``` - -When initializing a custom node, as in the code above, you'll normally just place your vtable in -static space. The number of input and output buses are specified as part of the vtable. If you need -a variable number of buses on a per-node bases, the vtable should have the relevant bus count set -to `MA_NODE_BUS_COUNT_UNKNOWN`. In this case, the bus count should be set in the node config: - - ```c - static ma_node_vtable my_custom_node_vtable = - { - my_custom_node_process_pcm_frames, // The function that will be called process your custom node. This is where you'd implement your effect processing. - NULL, // Optional. A callback for calculating the number of input frames that are required to process a specified number of output frames. - MA_NODE_BUS_COUNT_UNKNOWN, // The number of input buses is determined on a per-node basis. - 1, // 1 output bus. - 0 // Default flags. - }; - - ... - - ma_node_config nodeConfig = ma_node_config_init(); - nodeConfig.vtable = &my_custom_node_vtable; - nodeConfig.inputBusCount = myBusCount; // <-- Since the vtable specifies MA_NODE_BUS_COUNT_UNKNOWN, the input bus count should be set here. - nodeConfig.pInputChannels = inputChannels; // <-- Make sure there are nodeConfig.inputBusCount elements in this array. - nodeConfig.pOutputChannels = outputChannels; // <-- The vtable specifies 1 output bus, so there must be 1 element in this array. - ``` - -In the above example it's important to never set the `inputBusCount` and `outputBusCount` members -to anything other than their defaults if the vtable specifies an explicit count. They can only be -set if the vtable specifies MA_NODE_BUS_COUNT_UNKNOWN in the relevant bus count. - -Most often you'll want to create a structure to encapsulate your node with some extra data. You -need to make sure the `ma_node_base` object is your first member of the structure: - - ```c - typedef struct - { - ma_node_base base; // <-- Make sure this is always the first member. - float someCustomData; - } my_custom_node; - ``` - -By doing this, your object will be compatible with all `ma_node` APIs and you can attach it to the -graph just like any other node. - -In the custom processing callback (`my_custom_node_process_pcm_frames()` in the example above), the -number of channels for each bus is what was specified by the config when the node was initialized -with `ma_node_init()`. In addition, all attachments to each of the input buses will have been -pre-mixed by miniaudio. The config allows you to specify different channel counts for each -individual input and output bus. It's up to the effect to handle it appropriate, and if it can't, -return an error in it's initialization routine. - -Custom nodes can be assigned some flags to describe their behaviour. These are set via the vtable -and include the following: - - +-----------------------------------------+---------------------------------------------------+ - | Flag Name | Description | - +-----------------------------------------+---------------------------------------------------+ - | MA_NODE_FLAG_PASSTHROUGH | Useful for nodes that do not do any kind of audio | - | | processing, but are instead used for tracking | - | | time, handling events, etc. Also used by the | - | | internal endpoint node. It reads directly from | - | | the input bus to the output bus. Nodes with this | - | | flag must have exactly 1 input bus and 1 output | - | | bus, and both buses must have the same channel | - | | counts. | - +-----------------------------------------+---------------------------------------------------+ - | MA_NODE_FLAG_CONTINUOUS_PROCESSING | Causes the processing callback to be called even | - | | when no data is available to be read from input | - | | attachments. When a node has at least one input | - | | bus, but there are no inputs attached or the | - | | inputs do not deliver any data, the node's | - | | processing callback will not get fired. This flag | - | | will make it so the callback is always fired | - | | regardless of whether or not any input data is | - | | received. This is useful for effects like | - | | echos where there will be a tail of audio data | - | | that still needs to be processed even when the | - | | original data sources have reached their ends. It | - | | may also be useful for nodes that must always | - | | have their processing callback fired when there | - | | are no inputs attached. | - +-----------------------------------------+---------------------------------------------------+ - | MA_NODE_FLAG_ALLOW_NULL_INPUT | Used in conjunction with | - | | `MA_NODE_FLAG_CONTINUOUS_PROCESSING`. When this | - | | is set, the `ppFramesIn` parameter of the | - | | processing callback will be set to NULL when | - | | there are no input frames are available. When | - | | this is unset, silence will be posted to the | - | | processing callback. | - +-----------------------------------------+---------------------------------------------------+ - | MA_NODE_FLAG_DIFFERENT_PROCESSING_RATES | Used to tell miniaudio that input and output | - | | frames are processed at different rates. You | - | | should set this for any nodes that perform | - | | resampling. | - +-----------------------------------------+---------------------------------------------------+ - | MA_NODE_FLAG_SILENT_OUTPUT | Used to tell miniaudio that a node produces only | - | | silent output. This is useful for nodes where you | - | | don't want the output to contribute to the final | - | | mix. An example might be if you want split your | - | | stream and have one branch be output to a file. | - | | When using this flag, you should avoid writing to | - | | the output buffer of the node's processing | - | | callback because miniaudio will ignore it anyway. | - +-----------------------------------------+---------------------------------------------------+ - - -If you need to make a copy of an audio stream for effect processing you can use a splitter node -called `ma_splitter_node`. This takes has 1 input bus and splits the stream into 2 output buses. -You can use it like this: - - ```c - ma_splitter_node_config splitterNodeConfig = ma_splitter_node_config_init(channels); - - ma_splitter_node splitterNode; - result = ma_splitter_node_init(&nodeGraph, &splitterNodeConfig, NULL, &splitterNode); - if (result != MA_SUCCESS) { - // Failed to create node. - } - - // Attach your output buses to two different input buses (can be on two different nodes). - ma_node_attach_output_bus(&splitterNode, 0, ma_node_graph_get_endpoint(&nodeGraph), 0); // Attach directly to the endpoint. - ma_node_attach_output_bus(&splitterNode, 1, &myEffectNode, 0); // Attach to input bus 0 of some effect node. - ``` - -The volume of an output bus can be configured on a per-bus basis: - - ```c - ma_node_set_output_bus_volume(&splitterNode, 0, 0.5f); - ma_node_set_output_bus_volume(&splitterNode, 1, 0.5f); - ``` - -In the code above we're using the splitter node from before and changing the volume of each of the -copied streams. - -You can start and stop a node with the following: - - ```c - ma_node_set_state(&splitterNode, ma_node_state_started); // The default state. - ma_node_set_state(&splitterNode, ma_node_state_stopped); - ``` - -By default the node is in a started state, but since it won't be connected to anything won't -actually be invoked by the node graph until it's connected. When you stop a node, data will not be -read from any of its input connections. You can use this property to stop a group of sounds -atomically. - -You can configure the initial state of a node in it's config: - - ```c - nodeConfig.initialState = ma_node_state_stopped; - ``` - -Note that for the stock specialized nodes, all of their configs will have a `nodeConfig` member -which is the config to use with the base node. This is where the initial state can be configured -for specialized nodes: - - ```c - dataSourceNodeConfig.nodeConfig.initialState = ma_node_state_stopped; - ``` - -When using a specialized node like `ma_data_source_node` or `ma_splitter_node`, be sure to not -modify the `vtable` member of the `nodeConfig` object. - - -7.1. Timing ------------ -The node graph supports starting and stopping nodes at scheduled times. This is especially useful -for data source nodes where you want to get the node set up, but only start playback at a specific -time. There are two clocks: local and global. - -A local clock is per-node, whereas the global clock is per graph. Scheduling starts and stops can -only be done based on the global clock because the local clock will not be running while the node -is stopped. The global clocks advances whenever `ma_node_graph_read_pcm_frames()` is called. On the -other hand, the local clock only advances when the node's processing callback is fired, and is -advanced based on the output frame count. - -To retrieve the global time, use `ma_node_graph_get_time()`. The global time can be set with -`ma_node_graph_set_time()` which might be useful if you want to do seeking on a global timeline. -Getting and setting the local time is similar. Use `ma_node_get_time()` to retrieve the local time, -and `ma_node_set_time()` to set the local time. The global and local times will be advanced by the -audio thread, so care should be taken to avoid data races. Ideally you should avoid calling these -outside of the node processing callbacks which are always run on the audio thread. - -There is basic support for scheduling the starting and stopping of nodes. You can only schedule one -start and one stop at a time. This is mainly intended for putting nodes into a started or stopped -state in a frame-exact manner. Without this mechanism, starting and stopping of a node is limited -to the resolution of a call to `ma_node_graph_read_pcm_frames()` which would typically be in blocks -of several milliseconds. The following APIs can be used for scheduling node states: - - ```c - ma_node_set_state_time() - ma_node_get_state_time() - ``` - -The time is absolute and must be based on the global clock. An example is below: - - ```c - ma_node_set_state_time(&myNode, ma_node_state_started, sampleRate*1); // Delay starting to 1 second. - ma_node_set_state_time(&myNode, ma_node_state_stopped, sampleRate*5); // Delay stopping to 5 seconds. - ``` - -An example for changing the state using a relative time. - - ```c - ma_node_set_state_time(&myNode, ma_node_state_started, sampleRate*1 + ma_node_graph_get_time(&myNodeGraph)); - ma_node_set_state_time(&myNode, ma_node_state_stopped, sampleRate*5 + ma_node_graph_get_time(&myNodeGraph)); - ``` - -Note that due to the nature of multi-threading the times may not be 100% exact. If this is an -issue, consider scheduling state changes from within a processing callback. An idea might be to -have some kind of passthrough trigger node that is used specifically for tracking time and handling -events. - - - -7.2. Thread Safety and Locking ------------------------------- -When processing audio, it's ideal not to have any kind of locking in the audio thread. Since it's -expected that `ma_node_graph_read_pcm_frames()` would be run on the audio thread, it does so -without the use of any locks. This section discusses the implementation used by miniaudio and goes -over some of the compromises employed by miniaudio to achieve this goal. Note that the current -implementation may not be ideal - feedback and critiques are most welcome. - -The node graph API is not *entirely* lock-free. Only `ma_node_graph_read_pcm_frames()` is expected -to be lock-free. Attachment, detachment and uninitialization of nodes use locks to simplify the -implementation, but are crafted in a way such that such locking is not required when reading audio -data from the graph. Locking in these areas are achieved by means of spinlocks. - -The main complication with keeping `ma_node_graph_read_pcm_frames()` lock-free stems from the fact -that a node can be uninitialized, and it's memory potentially freed, while in the middle of being -processed on the audio thread. There are times when the audio thread will be referencing a node, -which means the uninitialization process of a node needs to make sure it delays returning until the -audio thread is finished so that control is not handed back to the caller thereby giving them a -chance to free the node's memory. - -When the audio thread is processing a node, it does so by reading from each of the output buses of -the node. In order for a node to process data for one of its output buses, it needs to read from -each of its input buses, and so on an so forth. It follows that once all output buses of a node -are detached, the node as a whole will be disconnected and no further processing will occur unless -it's output buses are reattached, which won't be happening when the node is being uninitialized. -By having `ma_node_detach_output_bus()` wait until the audio thread is finished with it, we can -simplify a few things, at the expense of making `ma_node_detach_output_bus()` a bit slower. By -doing this, the implementation of `ma_node_uninit()` becomes trivial - just detach all output -nodes, followed by each of the attachments to each of its input nodes, and then do any final clean -up. - -With the above design, the worst-case scenario is `ma_node_detach_output_bus()` taking as long as -it takes to process the output bus being detached. This will happen if it's called at just the -wrong moment where the audio thread has just iterated it and has just started processing. The -caller of `ma_node_detach_output_bus()` will stall until the audio thread is finished, which -includes the cost of recursively processing its inputs. This is the biggest compromise made with -the approach taken by miniaudio for its lock-free processing system. The cost of detaching nodes -earlier in the pipeline (data sources, for example) will be cheaper than the cost of detaching -higher level nodes, such as some kind of final post-processing endpoint. If you need to do mass -detachments, detach starting from the lowest level nodes and work your way towards the final -endpoint node (but don't try detaching the node graph's endpoint). If the audio thread is not -running, detachment will be fast and detachment in any order will be the same. The reason nodes -need to wait for their input attachments to complete is due to the potential for desyncs between -data sources. If the node was to terminate processing mid way through processing its inputs, -there's a chance that some of the underlying data sources will have been read, but then others not. -That will then result in a potential desynchronization when detaching and reattaching higher-level -nodes. A possible solution to this is to have an option when detaching to terminate processing -before processing all input attachments which should be fairly simple. - -Another compromise, albeit less significant, is locking when attaching and detaching nodes. This -locking is achieved by means of a spinlock in order to reduce memory overhead. A lock is present -for each input bus and output bus. When an output bus is connected to an input bus, both the output -bus and input bus is locked. This locking is specifically for attaching and detaching across -different threads and does not affect `ma_node_graph_read_pcm_frames()` in any way. The locking and -unlocking is mostly self-explanatory, but a slightly less intuitive aspect comes into it when -considering that iterating over attachments must not break as a result of attaching or detaching a -node while iteration is occurring. - -Attaching and detaching are both quite simple. When an output bus of a node is attached to an input -bus of another node, it's added to a linked list. Basically, an input bus is a linked list, where -each item in the list is and output bus. We have some intentional (and convenient) restrictions on -what can done with the linked list in order to simplify the implementation. First of all, whenever -something needs to iterate over the list, it must do so in a forward direction. Backwards iteration -is not supported. Also, items can only be added to the start of the list. - -The linked list is a doubly-linked list where each item in the list (an output bus) holds a pointer -to the next item in the list, and another to the previous item. A pointer to the previous item is -only required for fast detachment of the node - it is never used in iteration. This is an -important property because it means from the perspective of iteration, attaching and detaching of -an item can be done with a single atomic assignment. This is exploited by both the attachment and -detachment process. When attaching the node, the first thing that is done is the setting of the -local "next" and "previous" pointers of the node. After that, the item is "attached" to the list -by simply performing an atomic exchange with the head pointer. After that, the node is "attached" -to the list from the perspective of iteration. Even though the "previous" pointer of the next item -hasn't yet been set, from the perspective of iteration it's been attached because iteration will -only be happening in a forward direction which means the "previous" pointer won't actually ever get -used. The same general process applies to detachment. See `ma_node_attach_output_bus()` and -`ma_node_detach_output_bus()` for the implementation of this mechanism. - - - -8. Decoding -=========== -The `ma_decoder` API is used for reading audio files. Decoders are completely decoupled from -devices and can be used independently. Built-in support is included for the following formats: - - +---------+ - | Format | - +---------+ - | WAV | - | MP3 | - | FLAC | - +---------+ - -You can disable the built-in decoders by specifying one or more of the following options before the -miniaudio implementation: - - ```c - #define MA_NO_WAV - #define MA_NO_MP3 - #define MA_NO_FLAC - ``` - -miniaudio supports the ability to plug in custom decoders. See the section below for details on how -to use custom decoders. - -A decoder can be initialized from a file with `ma_decoder_init_file()`, a block of memory with -`ma_decoder_init_memory()`, or from data delivered via callbacks with `ma_decoder_init()`. Here is -an example for loading a decoder from a file: - - ```c - ma_decoder decoder; - ma_result result = ma_decoder_init_file("MySong.mp3", NULL, &decoder); - if (result != MA_SUCCESS) { - return false; // An error occurred. - } - - ... - - ma_decoder_uninit(&decoder); - ``` - -When initializing a decoder, you can optionally pass in a pointer to a `ma_decoder_config` object -(the `NULL` argument in the example above) which allows you to configure the output format, channel -count, sample rate and channel map: - - ```c - ma_decoder_config config = ma_decoder_config_init(ma_format_f32, 2, 48000); - ``` - -When passing in `NULL` for decoder config in `ma_decoder_init*()`, the output format will be the -same as that defined by the decoding backend. - -Data is read from the decoder as PCM frames. This will output the number of PCM frames actually -read. If this is less than the requested number of PCM frames it means you've reached the end. The -return value will be `MA_AT_END` if no samples have been read and the end has been reached. - - ```c - ma_result result = ma_decoder_read_pcm_frames(pDecoder, pFrames, framesToRead, &framesRead); - if (framesRead < framesToRead) { - // Reached the end. - } - ``` - -You can also seek to a specific frame like so: - - ```c - ma_result result = ma_decoder_seek_to_pcm_frame(pDecoder, targetFrame); - if (result != MA_SUCCESS) { - return false; // An error occurred. - } - ``` - -If you want to loop back to the start, you can simply seek back to the first PCM frame: - - ```c - ma_decoder_seek_to_pcm_frame(pDecoder, 0); - ``` - -When loading a decoder, miniaudio uses a trial and error technique to find the appropriate decoding -backend. This can be unnecessarily inefficient if the type is already known. In this case you can -use `encodingFormat` variable in the device config to specify a specific encoding format you want -to decode: - - ```c - decoderConfig.encodingFormat = ma_encoding_format_wav; - ``` - -See the `ma_encoding_format` enum for possible encoding formats. - -The `ma_decoder_init_file()` API will try using the file extension to determine which decoding -backend to prefer. - - -8.1. Custom Decoders --------------------- -It's possible to implement a custom decoder and plug it into miniaudio. This is extremely useful -when you want to use the `ma_decoder` API, but need to support an encoding format that's not one of -the stock formats supported by miniaudio. This can be put to particularly good use when using the -`ma_engine` and/or `ma_resource_manager` APIs because they use `ma_decoder` internally. If, for -example, you wanted to support Opus, you can do so with a custom decoder (there if a reference -Opus decoder in the "extras" folder of the miniaudio repository which uses libopus + libopusfile). - -A custom decoder must implement a data source. A vtable called `ma_decoding_backend_vtable` needs -to be implemented which is then passed into the decoder config: - - ```c - ma_decoding_backend_vtable* pCustomBackendVTables[] = - { - &g_ma_decoding_backend_vtable_libvorbis, - &g_ma_decoding_backend_vtable_libopus - }; - - ... - - decoderConfig = ma_decoder_config_init_default(); - decoderConfig.pCustomBackendUserData = NULL; - decoderConfig.ppCustomBackendVTables = pCustomBackendVTables; - decoderConfig.customBackendCount = sizeof(pCustomBackendVTables) / sizeof(pCustomBackendVTables[0]); - ``` - -The `ma_decoding_backend_vtable` vtable has the following functions: - - ``` - onInit - onInitFile - onInitFileW - onInitMemory - onUninit - ``` - -There are only two functions that must be implemented - `onInit` and `onUninit`. The other -functions can be implemented for a small optimization for loading from a file path or memory. If -these are not specified, miniaudio will deal with it for you via a generic implementation. - -When you initialize a custom data source (by implementing the `onInit` function in the vtable) you -will need to output a pointer to a `ma_data_source` which implements your custom decoder. See the -section about data sources for details on how to implement this. Alternatively, see the -"custom_decoders" example in the miniaudio repository. - -The `onInit` function takes a pointer to some callbacks for the purpose of reading raw audio data -from some arbitrary source. You'll use these functions to read from the raw data and perform the -decoding. When you call them, you will pass in the `pReadSeekTellUserData` pointer to the relevant -parameter. - -The `pConfig` parameter in `onInit` can be used to configure the backend if appropriate. It's only -used as a hint and can be ignored. However, if any of the properties are relevant to your decoder, -an optimal implementation will handle the relevant properties appropriately. - -If memory allocation is required, it should be done so via the specified allocation callbacks if -possible (the `pAllocationCallbacks` parameter). - -If an error occurs when initializing the decoder, you should leave `ppBackend` unset, or set to -NULL, and make sure everything is cleaned up appropriately and an appropriate result code returned. -When multiple custom backends are specified, miniaudio will cycle through the vtables in the order -they're listed in the array that's passed into the decoder config so it's important that your -initialization routine is clean. - -When a decoder is uninitialized, the `onUninit` callback will be fired which will give you an -opportunity to clean up and internal data. - - - -9. Encoding -=========== -The `ma_encoding` API is used for writing audio files. The only supported output format is WAV. -This can be disabled by specifying the following option before the implementation of miniaudio: - - ```c - #define MA_NO_WAV - ``` - -An encoder can be initialized to write to a file with `ma_encoder_init_file()` or from data -delivered via callbacks with `ma_encoder_init()`. Below is an example for initializing an encoder -to output to a file. - - ```c - ma_encoder_config config = ma_encoder_config_init(ma_encoding_format_wav, FORMAT, CHANNELS, SAMPLE_RATE); - ma_encoder encoder; - ma_result result = ma_encoder_init_file("my_file.wav", &config, &encoder); - if (result != MA_SUCCESS) { - // Error - } - - ... - - ma_encoder_uninit(&encoder); - ``` - -When initializing an encoder you must specify a config which is initialized with -`ma_encoder_config_init()`. Here you must specify the file type, the output sample format, output -channel count and output sample rate. The following file types are supported: - - +------------------------+-------------+ - | Enum | Description | - +------------------------+-------------+ - | ma_encoding_format_wav | WAV | - +------------------------+-------------+ - -If the format, channel count or sample rate is not supported by the output file type an error will -be returned. The encoder will not perform data conversion so you will need to convert it before -outputting any audio data. To output audio data, use `ma_encoder_write_pcm_frames()`, like in the -example below: - - ```c - ma_uint64 framesWritten; - result = ma_encoder_write_pcm_frames(&encoder, pPCMFramesToWrite, framesToWrite, &framesWritten); - if (result != MA_SUCCESS) { - ... handle error ... - } - ``` - -The `framesWritten` variable will contain the number of PCM frames that were actually written. This -is optionally and you can pass in `NULL` if you need this. - -Encoders must be uninitialized with `ma_encoder_uninit()`. - - - -10. Data Conversion -=================== -A data conversion API is included with miniaudio which supports the majority of data conversion -requirements. This supports conversion between sample formats, channel counts (with channel -mapping) and sample rates. - - -10.1. Sample Format Conversion ------------------------------- -Conversion between sample formats is achieved with the `ma_pcm_*_to_*()`, `ma_pcm_convert()` and -`ma_convert_pcm_frames_format()` APIs. Use `ma_pcm_*_to_*()` to convert between two specific -formats. Use `ma_pcm_convert()` to convert based on a `ma_format` variable. Use -`ma_convert_pcm_frames_format()` to convert PCM frames where you want to specify the frame count -and channel count as a variable instead of the total sample count. - - -10.1.1. Dithering ------------------ -Dithering can be set using the ditherMode parameter. - -The different dithering modes include the following, in order of efficiency: - - +-----------+--------------------------+ - | Type | Enum Token | - +-----------+--------------------------+ - | None | ma_dither_mode_none | - | Rectangle | ma_dither_mode_rectangle | - | Triangle | ma_dither_mode_triangle | - +-----------+--------------------------+ - -Note that even if the dither mode is set to something other than `ma_dither_mode_none`, it will be -ignored for conversions where dithering is not needed. Dithering is available for the following -conversions: - - ``` - s16 -> u8 - s24 -> u8 - s32 -> u8 - f32 -> u8 - s24 -> s16 - s32 -> s16 - f32 -> s16 - ``` - -Note that it is not an error to pass something other than ma_dither_mode_none for conversions where -dither is not used. It will just be ignored. - - - -10.2. Channel Conversion ------------------------- -Channel conversion is used for channel rearrangement and conversion from one channel count to -another. The `ma_channel_converter` API is used for channel conversion. Below is an example of -initializing a simple channel converter which converts from mono to stereo. - - ```c - ma_channel_converter_config config = ma_channel_converter_config_init( - ma_format, // Sample format - 1, // Input channels - NULL, // Input channel map - 2, // Output channels - NULL, // Output channel map - ma_channel_mix_mode_default); // The mixing algorithm to use when combining channels. - - result = ma_channel_converter_init(&config, NULL, &converter); - if (result != MA_SUCCESS) { - // Error. - } - ``` - -To perform the conversion simply call `ma_channel_converter_process_pcm_frames()` like so: - - ```c - ma_result result = ma_channel_converter_process_pcm_frames(&converter, pFramesOut, pFramesIn, frameCount); - if (result != MA_SUCCESS) { - // Error. - } - ``` - -It is up to the caller to ensure the output buffer is large enough to accommodate the new PCM -frames. - -Input and output PCM frames are always interleaved. Deinterleaved layouts are not supported. - - -10.2.1. Channel Mapping ------------------------ -In addition to converting from one channel count to another, like the example above, the channel -converter can also be used to rearrange channels. When initializing the channel converter, you can -optionally pass in channel maps for both the input and output frames. If the channel counts are the -same, and each channel map contains the same channel positions with the exception that they're in -a different order, a simple shuffling of the channels will be performed. If, however, there is not -a 1:1 mapping of channel positions, or the channel counts differ, the input channels will be mixed -based on a mixing mode which is specified when initializing the `ma_channel_converter_config` -object. - -When converting from mono to multi-channel, the mono channel is simply copied to each output -channel. When going the other way around, the audio of each output channel is simply averaged and -copied to the mono channel. - -In more complicated cases blending is used. The `ma_channel_mix_mode_simple` mode will drop excess -channels and silence extra channels. For example, converting from 4 to 2 channels, the 3rd and 4th -channels will be dropped, whereas converting from 2 to 4 channels will put silence into the 3rd and -4th channels. - -The `ma_channel_mix_mode_rectangle` mode uses spacial locality based on a rectangle to compute a -simple distribution between input and output. Imagine sitting in the middle of a room, with -speakers on the walls representing channel positions. The `MA_CHANNEL_FRONT_LEFT` position can be -thought of as being in the corner of the front and left walls. - -Finally, the `ma_channel_mix_mode_custom_weights` mode can be used to use custom user-defined -weights. Custom weights can be passed in as the last parameter of -`ma_channel_converter_config_init()`. - -Predefined channel maps can be retrieved with `ma_channel_map_init_standard()`. This takes a -`ma_standard_channel_map` enum as its first parameter, which can be one of the following: - - +-----------------------------------+-----------------------------------------------------------+ - | Name | Description | - +-----------------------------------+-----------------------------------------------------------+ - | ma_standard_channel_map_default | Default channel map used by miniaudio. See below. | - | ma_standard_channel_map_microsoft | Channel map used by Microsoft's bitfield channel maps. | - | ma_standard_channel_map_alsa | Default ALSA channel map. | - | ma_standard_channel_map_rfc3551 | RFC 3551. Based on AIFF. | - | ma_standard_channel_map_flac | FLAC channel map. | - | ma_standard_channel_map_vorbis | Vorbis channel map. | - | ma_standard_channel_map_sound4 | FreeBSD's sound(4). | - | ma_standard_channel_map_sndio | sndio channel map. http://www.sndio.org/tips.html. | - | ma_standard_channel_map_webaudio | https://webaudio.github.io/web-audio-api/#ChannelOrdering | - +-----------------------------------+-----------------------------------------------------------+ - -Below are the channel maps used by default in miniaudio (`ma_standard_channel_map_default`): - - +---------------+---------------------------------+ - | Channel Count | Mapping | - +---------------+---------------------------------+ - | 1 (Mono) | 0: MA_CHANNEL_MONO | - +---------------+---------------------------------+ - | 2 (Stereo) | 0: MA_CHANNEL_FRONT_LEFT
| - | | 1: MA_CHANNEL_FRONT_RIGHT | - +---------------+---------------------------------+ - | 3 | 0: MA_CHANNEL_FRONT_LEFT
| - | | 1: MA_CHANNEL_FRONT_RIGHT
| - | | 2: MA_CHANNEL_FRONT_CENTER | - +---------------+---------------------------------+ - | 4 (Surround) | 0: MA_CHANNEL_FRONT_LEFT
| - | | 1: MA_CHANNEL_FRONT_RIGHT
| - | | 2: MA_CHANNEL_FRONT_CENTER
| - | | 3: MA_CHANNEL_BACK_CENTER | - +---------------+---------------------------------+ - | 5 | 0: MA_CHANNEL_FRONT_LEFT
| - | | 1: MA_CHANNEL_FRONT_RIGHT
| - | | 2: MA_CHANNEL_FRONT_CENTER
| - | | 3: MA_CHANNEL_BACK_LEFT
| - | | 4: MA_CHANNEL_BACK_RIGHT | - +---------------+---------------------------------+ - | 6 (5.1) | 0: MA_CHANNEL_FRONT_LEFT
| - | | 1: MA_CHANNEL_FRONT_RIGHT
| - | | 2: MA_CHANNEL_FRONT_CENTER
| - | | 3: MA_CHANNEL_LFE
| - | | 4: MA_CHANNEL_SIDE_LEFT
| - | | 5: MA_CHANNEL_SIDE_RIGHT | - +---------------+---------------------------------+ - | 7 | 0: MA_CHANNEL_FRONT_LEFT
| - | | 1: MA_CHANNEL_FRONT_RIGHT
| - | | 2: MA_CHANNEL_FRONT_CENTER
| - | | 3: MA_CHANNEL_LFE
| - | | 4: MA_CHANNEL_BACK_CENTER
| - | | 4: MA_CHANNEL_SIDE_LEFT
| - | | 5: MA_CHANNEL_SIDE_RIGHT | - +---------------+---------------------------------+ - | 8 (7.1) | 0: MA_CHANNEL_FRONT_LEFT
| - | | 1: MA_CHANNEL_FRONT_RIGHT
| - | | 2: MA_CHANNEL_FRONT_CENTER
| - | | 3: MA_CHANNEL_LFE
| - | | 4: MA_CHANNEL_BACK_LEFT
| - | | 5: MA_CHANNEL_BACK_RIGHT
| - | | 6: MA_CHANNEL_SIDE_LEFT
| - | | 7: MA_CHANNEL_SIDE_RIGHT | - +---------------+---------------------------------+ - | Other | All channels set to 0. This | - | | is equivalent to the same | - | | mapping as the device. | - +---------------+---------------------------------+ - - - -10.3. Resampling ----------------- -Resampling is achieved with the `ma_resampler` object. To create a resampler object, do something -like the following: - - ```c - ma_resampler_config config = ma_resampler_config_init( - ma_format_s16, - channels, - sampleRateIn, - sampleRateOut, - ma_resample_algorithm_linear); - - ma_resampler resampler; - ma_result result = ma_resampler_init(&config, NULL, &resampler); - if (result != MA_SUCCESS) { - // An error occurred... - } - ``` - -Do the following to uninitialize the resampler: - - ```c - ma_resampler_uninit(&resampler); - ``` - -The following example shows how data can be processed - - ```c - ma_uint64 frameCountIn = 1000; - ma_uint64 frameCountOut = 2000; - ma_result result = ma_resampler_process_pcm_frames(&resampler, pFramesIn, &frameCountIn, pFramesOut, &frameCountOut); - if (result != MA_SUCCESS) { - // An error occurred... - } - - // At this point, frameCountIn contains the number of input frames that were consumed and frameCountOut contains the - // number of output frames written. - ``` - -To initialize the resampler you first need to set up a config (`ma_resampler_config`) with -`ma_resampler_config_init()`. You need to specify the sample format you want to use, the number of -channels, the input and output sample rate, and the algorithm. - -The sample format can be either `ma_format_s16` or `ma_format_f32`. If you need a different format -you will need to perform pre- and post-conversions yourself where necessary. Note that the format -is the same for both input and output. The format cannot be changed after initialization. - -The resampler supports multiple channels and is always interleaved (both input and output). The -channel count cannot be changed after initialization. - -The sample rates can be anything other than zero, and are always specified in hertz. They should be -set to something like 44100, etc. The sample rate is the only configuration property that can be -changed after initialization. - -The miniaudio resampler has built-in support for the following algorithms: - - +-----------+------------------------------+ - | Algorithm | Enum Token | - +-----------+------------------------------+ - | Linear | ma_resample_algorithm_linear | - | Custom | ma_resample_algorithm_custom | - +-----------+------------------------------+ - -The algorithm cannot be changed after initialization. - -Processing always happens on a per PCM frame basis and always assumes interleaved input and output. -De-interleaved processing is not supported. To process frames, use -`ma_resampler_process_pcm_frames()`. On input, this function takes the number of output frames you -can fit in the output buffer and the number of input frames contained in the input buffer. On -output these variables contain the number of output frames that were written to the output buffer -and the number of input frames that were consumed in the process. You can pass in NULL for the -input buffer in which case it will be treated as an infinitely large buffer of zeros. The output -buffer can also be NULL, in which case the processing will be treated as seek. - -The sample rate can be changed dynamically on the fly. You can change this with explicit sample -rates with `ma_resampler_set_rate()` and also with a decimal ratio with -`ma_resampler_set_rate_ratio()`. The ratio is in/out. - -Sometimes it's useful to know exactly how many input frames will be required to output a specific -number of frames. You can calculate this with `ma_resampler_get_required_input_frame_count()`. -Likewise, it's sometimes useful to know exactly how many frames would be output given a certain -number of input frames. You can do this with `ma_resampler_get_expected_output_frame_count()`. - -Due to the nature of how resampling works, the resampler introduces some latency. This can be -retrieved in terms of both the input rate and the output rate with -`ma_resampler_get_input_latency()` and `ma_resampler_get_output_latency()`. - - -10.3.1. Resampling Algorithms ------------------------------ -The choice of resampling algorithm depends on your situation and requirements. - - -10.3.1.1. Linear Resampling ---------------------------- -The linear resampler is the fastest, but comes at the expense of poorer quality. There is, however, -some control over the quality of the linear resampler which may make it a suitable option depending -on your requirements. - -The linear resampler performs low-pass filtering before or after downsampling or upsampling, -depending on the sample rates you're converting between. When decreasing the sample rate, the -low-pass filter will be applied before downsampling. When increasing the rate it will be performed -after upsampling. By default a fourth order low-pass filter will be applied. This can be configured -via the `lpfOrder` configuration variable. Setting this to 0 will disable filtering. - -The low-pass filter has a cutoff frequency which defaults to half the sample rate of the lowest of -the input and output sample rates (Nyquist Frequency). - -The API for the linear resampler is the same as the main resampler API, only it's called -`ma_linear_resampler`. - - -10.3.2. Custom Resamplers -------------------------- -You can implement a custom resampler by using the `ma_resample_algorithm_custom` resampling -algorithm and setting a vtable in the resampler config: - - ```c - ma_resampler_config config = ma_resampler_config_init(..., ma_resample_algorithm_custom); - config.pBackendVTable = &g_customResamplerVTable; - ``` - -Custom resamplers are useful if the stock algorithms are not appropriate for your use case. You -need to implement the required functions in `ma_resampling_backend_vtable`. Note that not all -functions in the vtable need to be implemented, but if it's possible to implement, they should be. - -You can use the `ma_linear_resampler` object for an example on how to implement the vtable. The -`onGetHeapSize` callback is used to calculate the size of any internal heap allocation the custom -resampler will need to make given the supplied config. When you initialize the resampler via the -`onInit` callback, you'll be given a pointer to a heap allocation which is where you should store -the heap allocated data. You should not free this data in `onUninit` because miniaudio will manage -it for you. - -The `onProcess` callback is where the actual resampling takes place. On input, `pFrameCountIn` -points to a variable containing the number of frames in the `pFramesIn` buffer and -`pFrameCountOut` points to a variable containing the capacity in frames of the `pFramesOut` buffer. -On output, `pFrameCountIn` should be set to the number of input frames that were fully consumed, -whereas `pFrameCountOut` should be set to the number of frames that were written to `pFramesOut`. - -The `onSetRate` callback is optional and is used for dynamically changing the sample rate. If -dynamic rate changes are not supported, you can set this callback to NULL. - -The `onGetInputLatency` and `onGetOutputLatency` functions are used for retrieving the latency in -input and output rates respectively. These can be NULL in which case latency calculations will be -assumed to be NULL. - -The `onGetRequiredInputFrameCount` callback is used to give miniaudio a hint as to how many input -frames are required to be available to produce the given number of output frames. Likewise, the -`onGetExpectedOutputFrameCount` callback is used to determine how many output frames will be -produced given the specified number of input frames. miniaudio will use these as a hint, but they -are optional and can be set to NULL if you're unable to implement them. - - - -10.4. General Data Conversion ------------------------------ -The `ma_data_converter` API can be used to wrap sample format conversion, channel conversion and -resampling into one operation. This is what miniaudio uses internally to convert between the format -requested when the device was initialized and the format of the backend's native device. The API -for general data conversion is very similar to the resampling API. Create a `ma_data_converter` -object like this: - - ```c - ma_data_converter_config config = ma_data_converter_config_init( - inputFormat, - outputFormat, - inputChannels, - outputChannels, - inputSampleRate, - outputSampleRate - ); - - ma_data_converter converter; - ma_result result = ma_data_converter_init(&config, NULL, &converter); - if (result != MA_SUCCESS) { - // An error occurred... - } - ``` - -In the example above we use `ma_data_converter_config_init()` to initialize the config, however -there's many more properties that can be configured, such as channel maps and resampling quality. -Something like the following may be more suitable depending on your requirements: - - ```c - ma_data_converter_config config = ma_data_converter_config_init_default(); - config.formatIn = inputFormat; - config.formatOut = outputFormat; - config.channelsIn = inputChannels; - config.channelsOut = outputChannels; - config.sampleRateIn = inputSampleRate; - config.sampleRateOut = outputSampleRate; - ma_channel_map_init_standard(ma_standard_channel_map_flac, config.channelMapIn, sizeof(config.channelMapIn)/sizeof(config.channelMapIn[0]), config.channelCountIn); - config.resampling.linear.lpfOrder = MA_MAX_FILTER_ORDER; - ``` - -Do the following to uninitialize the data converter: - - ```c - ma_data_converter_uninit(&converter, NULL); - ``` - -The following example shows how data can be processed - - ```c - ma_uint64 frameCountIn = 1000; - ma_uint64 frameCountOut = 2000; - ma_result result = ma_data_converter_process_pcm_frames(&converter, pFramesIn, &frameCountIn, pFramesOut, &frameCountOut); - if (result != MA_SUCCESS) { - // An error occurred... - } - - // At this point, frameCountIn contains the number of input frames that were consumed and frameCountOut contains the number - // of output frames written. - ``` - -The data converter supports multiple channels and is always interleaved (both input and output). -The channel count cannot be changed after initialization. - -Sample rates can be anything other than zero, and are always specified in hertz. They should be set -to something like 44100, etc. The sample rate is the only configuration property that can be -changed after initialization, but only if the `resampling.allowDynamicSampleRate` member of -`ma_data_converter_config` is set to `MA_TRUE`. To change the sample rate, use -`ma_data_converter_set_rate()` or `ma_data_converter_set_rate_ratio()`. The ratio must be in/out. -The resampling algorithm cannot be changed after initialization. - -Processing always happens on a per PCM frame basis and always assumes interleaved input and output. -De-interleaved processing is not supported. To process frames, use -`ma_data_converter_process_pcm_frames()`. On input, this function takes the number of output frames -you can fit in the output buffer and the number of input frames contained in the input buffer. On -output these variables contain the number of output frames that were written to the output buffer -and the number of input frames that were consumed in the process. You can pass in NULL for the -input buffer in which case it will be treated as an infinitely large -buffer of zeros. The output buffer can also be NULL, in which case the processing will be treated -as seek. - -Sometimes it's useful to know exactly how many input frames will be required to output a specific -number of frames. You can calculate this with `ma_data_converter_get_required_input_frame_count()`. -Likewise, it's sometimes useful to know exactly how many frames would be output given a certain -number of input frames. You can do this with `ma_data_converter_get_expected_output_frame_count()`. - -Due to the nature of how resampling works, the data converter introduces some latency if resampling -is required. This can be retrieved in terms of both the input rate and the output rate with -`ma_data_converter_get_input_latency()` and `ma_data_converter_get_output_latency()`. - - - -11. Filtering -============= - -11.1. Biquad Filtering ----------------------- -Biquad filtering is achieved with the `ma_biquad` API. Example: - - ```c - ma_biquad_config config = ma_biquad_config_init(ma_format_f32, channels, b0, b1, b2, a0, a1, a2); - ma_result result = ma_biquad_init(&config, NULL, &biquad); - if (result != MA_SUCCESS) { - // Error. - } - - ... - - ma_biquad_process_pcm_frames(&biquad, pFramesOut, pFramesIn, frameCount); - ``` - -Biquad filtering is implemented using transposed direct form 2. The numerator coefficients are b0, -b1 and b2, and the denominator coefficients are a0, a1 and a2. The a0 coefficient is required and -coefficients must not be pre-normalized. - -Supported formats are `ma_format_s16` and `ma_format_f32`. If you need to use a different format -you need to convert it yourself beforehand. When using `ma_format_s16` the biquad filter will use -fixed point arithmetic. When using `ma_format_f32`, floating point arithmetic will be used. - -Input and output frames are always interleaved. - -Filtering can be applied in-place by passing in the same pointer for both the input and output -buffers, like so: - - ```c - ma_biquad_process_pcm_frames(&biquad, pMyData, pMyData, frameCount); - ``` - -If you need to change the values of the coefficients, but maintain the values in the registers you -can do so with `ma_biquad_reinit()`. This is useful if you need to change the properties of the -filter while keeping the values of registers valid to avoid glitching. Do not use -`ma_biquad_init()` for this as it will do a full initialization which involves clearing the -registers to 0. Note that changing the format or channel count after initialization is invalid and -will result in an error. - - -11.2. Low-Pass Filtering ------------------------- -Low-pass filtering is achieved with the following APIs: - - +---------+------------------------------------------+ - | API | Description | - +---------+------------------------------------------+ - | ma_lpf1 | First order low-pass filter | - | ma_lpf2 | Second order low-pass filter | - | ma_lpf | High order low-pass filter (Butterworth) | - +---------+------------------------------------------+ - -Low-pass filter example: - - ```c - ma_lpf_config config = ma_lpf_config_init(ma_format_f32, channels, sampleRate, cutoffFrequency, order); - ma_result result = ma_lpf_init(&config, &lpf); - if (result != MA_SUCCESS) { - // Error. - } - - ... - - ma_lpf_process_pcm_frames(&lpf, pFramesOut, pFramesIn, frameCount); - ``` - -Supported formats are `ma_format_s16` and` ma_format_f32`. If you need to use a different format -you need to convert it yourself beforehand. Input and output frames are always interleaved. - -Filtering can be applied in-place by passing in the same pointer for both the input and output -buffers, like so: - - ```c - ma_lpf_process_pcm_frames(&lpf, pMyData, pMyData, frameCount); - ``` - -The maximum filter order is limited to `MA_MAX_FILTER_ORDER` which is set to 8. If you need more, -you can chain first and second order filters together. - - ```c - for (iFilter = 0; iFilter < filterCount; iFilter += 1) { - ma_lpf2_process_pcm_frames(&lpf2[iFilter], pMyData, pMyData, frameCount); - } - ``` - -If you need to change the configuration of the filter, but need to maintain the state of internal -registers you can do so with `ma_lpf_reinit()`. This may be useful if you need to change the sample -rate and/or cutoff frequency dynamically while maintaining smooth transitions. Note that changing the -format or channel count after initialization is invalid and will result in an error. - -The `ma_lpf` object supports a configurable order, but if you only need a first order filter you -may want to consider using `ma_lpf1`. Likewise, if you only need a second order filter you can use -`ma_lpf2`. The advantage of this is that they're lighter weight and a bit more efficient. - -If an even filter order is specified, a series of second order filters will be processed in a -chain. If an odd filter order is specified, a first order filter will be applied, followed by a -series of second order filters in a chain. - - -11.3. High-Pass Filtering -------------------------- -High-pass filtering is achieved with the following APIs: - - +---------+-------------------------------------------+ - | API | Description | - +---------+-------------------------------------------+ - | ma_hpf1 | First order high-pass filter | - | ma_hpf2 | Second order high-pass filter | - | ma_hpf | High order high-pass filter (Butterworth) | - +---------+-------------------------------------------+ - -High-pass filters work exactly the same as low-pass filters, only the APIs are called `ma_hpf1`, -`ma_hpf2` and `ma_hpf`. See example code for low-pass filters for example usage. - - -11.4. Band-Pass Filtering -------------------------- -Band-pass filtering is achieved with the following APIs: - - +---------+-------------------------------+ - | API | Description | - +---------+-------------------------------+ - | ma_bpf2 | Second order band-pass filter | - | ma_bpf | High order band-pass filter | - +---------+-------------------------------+ - -Band-pass filters work exactly the same as low-pass filters, only the APIs are called `ma_bpf2` and -`ma_hpf`. See example code for low-pass filters for example usage. Note that the order for -band-pass filters must be an even number which means there is no first order band-pass filter, -unlike low-pass and high-pass filters. - - -11.5. Notch Filtering ---------------------- -Notch filtering is achieved with the following APIs: - - +-----------+------------------------------------------+ - | API | Description | - +-----------+------------------------------------------+ - | ma_notch2 | Second order notching filter | - +-----------+------------------------------------------+ - - -11.6. Peaking EQ Filtering -------------------------- -Peaking filtering is achieved with the following APIs: - - +----------+------------------------------------------+ - | API | Description | - +----------+------------------------------------------+ - | ma_peak2 | Second order peaking filter | - +----------+------------------------------------------+ - - -11.7. Low Shelf Filtering -------------------------- -Low shelf filtering is achieved with the following APIs: - - +-------------+------------------------------------------+ - | API | Description | - +-------------+------------------------------------------+ - | ma_loshelf2 | Second order low shelf filter | - +-------------+------------------------------------------+ - -Where a high-pass filter is used to eliminate lower frequencies, a low shelf filter can be used to -just turn them down rather than eliminate them entirely. - - -11.8. High Shelf Filtering --------------------------- -High shelf filtering is achieved with the following APIs: - - +-------------+------------------------------------------+ - | API | Description | - +-------------+------------------------------------------+ - | ma_hishelf2 | Second order high shelf filter | - +-------------+------------------------------------------+ - -The high shelf filter has the same API as the low shelf filter, only you would use `ma_hishelf` -instead of `ma_loshelf`. Where a low shelf filter is used to adjust the volume of low frequencies, -the high shelf filter does the same thing for high frequencies. - - - - -12. Waveform and Noise Generation -================================= - -12.1. Waveforms ---------------- -miniaudio supports generation of sine, square, triangle and sawtooth waveforms. This is achieved -with the `ma_waveform` API. Example: - - ```c - ma_waveform_config config = ma_waveform_config_init( - FORMAT, - CHANNELS, - SAMPLE_RATE, - ma_waveform_type_sine, - amplitude, - frequency); - - ma_waveform waveform; - ma_result result = ma_waveform_init(&config, &waveform); - if (result != MA_SUCCESS) { - // Error. - } - - ... - - ma_waveform_read_pcm_frames(&waveform, pOutput, frameCount); - ``` - -The amplitude, frequency, type, and sample rate can be changed dynamically with -`ma_waveform_set_amplitude()`, `ma_waveform_set_frequency()`, `ma_waveform_set_type()`, and -`ma_waveform_set_sample_rate()` respectively. - -You can invert the waveform by setting the amplitude to a negative value. You can use this to -control whether or not a sawtooth has a positive or negative ramp, for example. - -Below are the supported waveform types: - - +---------------------------+ - | Enum Name | - +---------------------------+ - | ma_waveform_type_sine | - | ma_waveform_type_square | - | ma_waveform_type_triangle | - | ma_waveform_type_sawtooth | - +---------------------------+ - - - -12.2. Noise ------------ -miniaudio supports generation of white, pink and Brownian noise via the `ma_noise` API. Example: - - ```c - ma_noise_config config = ma_noise_config_init( - FORMAT, - CHANNELS, - ma_noise_type_white, - SEED, - amplitude); - - ma_noise noise; - ma_result result = ma_noise_init(&config, &noise); - if (result != MA_SUCCESS) { - // Error. - } - - ... - - ma_noise_read_pcm_frames(&noise, pOutput, frameCount); - ``` - -The noise API uses simple LCG random number generation. It supports a custom seed which is useful -for things like automated testing requiring reproducibility. Setting the seed to zero will default -to `MA_DEFAULT_LCG_SEED`. - -The amplitude and seed can be changed dynamically with `ma_noise_set_amplitude()` and -`ma_noise_set_seed()` respectively. - -By default, the noise API will use different values for different channels. So, for example, the -left side in a stereo stream will be different to the right side. To instead have each channel use -the same random value, set the `duplicateChannels` member of the noise config to true, like so: - - ```c - config.duplicateChannels = MA_TRUE; - ``` - -Below are the supported noise types. - - +------------------------+ - | Enum Name | - +------------------------+ - | ma_noise_type_white | - | ma_noise_type_pink | - | ma_noise_type_brownian | - +------------------------+ - - - -13. Audio Buffers -================= -miniaudio supports reading from a buffer of raw audio data via the `ma_audio_buffer` API. This can -read from memory that's managed by the application, but can also handle the memory management for -you internally. Memory management is flexible and should support most use cases. - -Audio buffers are initialized using the standard configuration system used everywhere in miniaudio: - - ```c - ma_audio_buffer_config config = ma_audio_buffer_config_init( - format, - channels, - sizeInFrames, - pExistingData, - &allocationCallbacks); - - ma_audio_buffer buffer; - result = ma_audio_buffer_init(&config, &buffer); - if (result != MA_SUCCESS) { - // Error. - } - - ... - - ma_audio_buffer_uninit(&buffer); - ``` - -In the example above, the memory pointed to by `pExistingData` will *not* be copied and is how an -application can do self-managed memory allocation. If you would rather make a copy of the data, use -`ma_audio_buffer_init_copy()`. To uninitialize the buffer, use `ma_audio_buffer_uninit()`. - -Sometimes it can be convenient to allocate the memory for the `ma_audio_buffer` structure and the -raw audio data in a contiguous block of memory. That is, the raw audio data will be located -immediately after the `ma_audio_buffer` structure. To do this, use -`ma_audio_buffer_alloc_and_init()`: - - ```c - ma_audio_buffer_config config = ma_audio_buffer_config_init( - format, - channels, - sizeInFrames, - pExistingData, - &allocationCallbacks); - - ma_audio_buffer* pBuffer - result = ma_audio_buffer_alloc_and_init(&config, &pBuffer); - if (result != MA_SUCCESS) { - // Error - } - - ... - - ma_audio_buffer_uninit_and_free(&buffer); - ``` - -If you initialize the buffer with `ma_audio_buffer_alloc_and_init()` you should uninitialize it -with `ma_audio_buffer_uninit_and_free()`. In the example above, the memory pointed to by -`pExistingData` will be copied into the buffer, which is contrary to the behavior of -`ma_audio_buffer_init()`. - -An audio buffer has a playback cursor just like a decoder. As you read frames from the buffer, the -cursor moves forward. The last parameter (`loop`) can be used to determine if the buffer should -loop. The return value is the number of frames actually read. If this is less than the number of -frames requested it means the end has been reached. This should never happen if the `loop` -parameter is set to true. If you want to manually loop back to the start, you can do so with with -`ma_audio_buffer_seek_to_pcm_frame(pAudioBuffer, 0)`. Below is an example for reading data from an -audio buffer. - - ```c - ma_uint64 framesRead = ma_audio_buffer_read_pcm_frames(pAudioBuffer, pFramesOut, desiredFrameCount, isLooping); - if (framesRead < desiredFrameCount) { - // If not looping, this means the end has been reached. This should never happen in looping mode with valid input. - } - ``` - -Sometimes you may want to avoid the cost of data movement between the internal buffer and the -output buffer. Instead you can use memory mapping to retrieve a pointer to a segment of data: - - ```c - void* pMappedFrames; - ma_uint64 frameCount = frameCountToTryMapping; - ma_result result = ma_audio_buffer_map(pAudioBuffer, &pMappedFrames, &frameCount); - if (result == MA_SUCCESS) { - // Map was successful. The value in frameCount will be how many frames were _actually_ mapped, which may be - // less due to the end of the buffer being reached. - ma_copy_pcm_frames(pFramesOut, pMappedFrames, frameCount, pAudioBuffer->format, pAudioBuffer->channels); - - // You must unmap the buffer. - ma_audio_buffer_unmap(pAudioBuffer, frameCount); - } - ``` - -When you use memory mapping, the read cursor is increment by the frame count passed in to -`ma_audio_buffer_unmap()`. If you decide not to process every frame you can pass in a value smaller -than the value returned by `ma_audio_buffer_map()`. The disadvantage to using memory mapping is -that it does not handle looping for you. You can determine if the buffer is at the end for the -purpose of looping with `ma_audio_buffer_at_end()` or by inspecting the return value of -`ma_audio_buffer_unmap()` and checking if it equals `MA_AT_END`. You should not treat `MA_AT_END` -as an error when returned by `ma_audio_buffer_unmap()`. - - - -14. Ring Buffers -================ -miniaudio supports lock free (single producer, single consumer) ring buffers which are exposed via -the `ma_rb` and `ma_pcm_rb` APIs. The `ma_rb` API operates on bytes, whereas the `ma_pcm_rb` -operates on PCM frames. They are otherwise identical as `ma_pcm_rb` is just a wrapper around -`ma_rb`. - -Unlike most other APIs in miniaudio, ring buffers support both interleaved and deinterleaved -streams. The caller can also allocate their own backing memory for the ring buffer to use -internally for added flexibility. Otherwise the ring buffer will manage it's internal memory for -you. - -The examples below use the PCM frame variant of the ring buffer since that's most likely the one -you will want to use. To initialize a ring buffer, do something like the following: - - ```c - ma_pcm_rb rb; - ma_result result = ma_pcm_rb_init(FORMAT, CHANNELS, BUFFER_SIZE_IN_FRAMES, NULL, NULL, &rb); - if (result != MA_SUCCESS) { - // Error - } - ``` - -The `ma_pcm_rb_init()` function takes the sample format and channel count as parameters because -it's the PCM variant of the ring buffer API. For the regular ring buffer that operates on bytes you -would call `ma_rb_init()` which leaves these out and just takes the size of the buffer in bytes -instead of frames. The fourth parameter is an optional pre-allocated buffer and the fifth parameter -is a pointer to a `ma_allocation_callbacks` structure for custom memory allocation routines. -Passing in `NULL` for this results in `MA_MALLOC()` and `MA_FREE()` being used. - -Use `ma_pcm_rb_init_ex()` if you need a deinterleaved buffer. The data for each sub-buffer is -offset from each other based on the stride. To manage your sub-buffers you can use -`ma_pcm_rb_get_subbuffer_stride()`, `ma_pcm_rb_get_subbuffer_offset()` and -`ma_pcm_rb_get_subbuffer_ptr()`. - -Use `ma_pcm_rb_acquire_read()` and `ma_pcm_rb_acquire_write()` to retrieve a pointer to a section -of the ring buffer. You specify the number of frames you need, and on output it will set to what -was actually acquired. If the read or write pointer is positioned such that the number of frames -requested will require a loop, it will be clamped to the end of the buffer. Therefore, the number -of frames you're given may be less than the number you requested. - -After calling `ma_pcm_rb_acquire_read()` or `ma_pcm_rb_acquire_write()`, you do your work on the -buffer and then "commit" it with `ma_pcm_rb_commit_read()` or `ma_pcm_rb_commit_write()`. This is -where the read/write pointers are updated. When you commit you need to pass in the buffer that was -returned by the earlier call to `ma_pcm_rb_acquire_read()` or `ma_pcm_rb_acquire_write()` and is -only used for validation. The number of frames passed to `ma_pcm_rb_commit_read()` and -`ma_pcm_rb_commit_write()` is what's used to increment the pointers, and can be less that what was -originally requested. - -If you want to correct for drift between the write pointer and the read pointer you can use a -combination of `ma_pcm_rb_pointer_distance()`, `ma_pcm_rb_seek_read()` and -`ma_pcm_rb_seek_write()`. Note that you can only move the pointers forward, and you should only -move the read pointer forward via the consumer thread, and the write pointer forward by the -producer thread. If there is too much space between the pointers, move the read pointer forward. If -there is too little space between the pointers, move the write pointer forward. - -You can use a ring buffer at the byte level instead of the PCM frame level by using the `ma_rb` -API. This is exactly the same, only you will use the `ma_rb` functions instead of `ma_pcm_rb` and -instead of frame counts you will pass around byte counts. - -The maximum size of the buffer in bytes is `0x7FFFFFFF-(MA_SIMD_ALIGNMENT-1)` due to the most -significant bit being used to encode a loop flag and the internally managed buffers always being -aligned to `MA_SIMD_ALIGNMENT`. - -Note that the ring buffer is only thread safe when used by a single consumer thread and single -producer thread. - - - -15. Backends -============ -The following backends are supported by miniaudio. These are listed in order of default priority. -When no backend is specified when initializing a context or device, miniaudio will attempt to use -each of these backends in the order listed in the table below. - -Note that backends that are not usable by the build target will not be included in the build. For -example, ALSA, which is specific to Linux, will not be included in the Windows build. - - +-------------+-----------------------+--------------------------------------------------------+ - | Name | Enum Name | Supported Operating Systems | - +-------------+-----------------------+--------------------------------------------------------+ - | WASAPI | ma_backend_wasapi | Windows Vista+ | - | DirectSound | ma_backend_dsound | Windows XP+ | - | WinMM | ma_backend_winmm | Windows 95+ | - | Core Audio | ma_backend_coreaudio | macOS, iOS | - | sndio | ma_backend_sndio | OpenBSD | - | audio(4) | ma_backend_audio4 | NetBSD, OpenBSD | - | OSS | ma_backend_oss | FreeBSD | - | PulseAudio | ma_backend_pulseaudio | Cross Platform (disabled on Windows, BSD and Android) | - | ALSA | ma_backend_alsa | Linux | - | JACK | ma_backend_jack | Cross Platform (disabled on BSD and Android) | - | AAudio | ma_backend_aaudio | Android 8+ | - | OpenSL ES | ma_backend_opensl | Android (API level 16+) | - | Web Audio | ma_backend_webaudio | Web (via Emscripten) | - | Custom | ma_backend_custom | Cross Platform | - | Null | ma_backend_null | Cross Platform (not used on Web) | - +-------------+-----------------------+--------------------------------------------------------+ - -Some backends have some nuance details you may want to be aware of. - -15.1. WASAPI ------------- -- Low-latency shared mode will be disabled when using an application-defined sample rate which is - different to the device's native sample rate. To work around this, set `wasapi.noAutoConvertSRC` - to true in the device config. This is due to IAudioClient3_InitializeSharedAudioStream() failing - when the `AUDCLNT_STREAMFLAGS_AUTOCONVERTPCM` flag is specified. Setting wasapi.noAutoConvertSRC - will result in miniaudio's internal resampler being used instead which will in turn enable the - use of low-latency shared mode. - -15.2. PulseAudio ----------------- -- If you experience bad glitching/noise on Arch Linux, consider this fix from the Arch wiki: - https://wiki.archlinux.org/index.php/PulseAudio/Troubleshooting#Glitches,_skips_or_crackling. - Alternatively, consider using a different backend such as ALSA. - -15.3. Android -------------- -- To capture audio on Android, remember to add the RECORD_AUDIO permission to your manifest: - `` -- With OpenSL|ES, only a single ma_context can be active at any given time. This is due to a - limitation with OpenSL|ES. -- With AAudio, only default devices are enumerated. This is due to AAudio not having an enumeration - API (devices are enumerated through Java). You can however perform your own device enumeration - through Java and then set the ID in the ma_device_id structure (ma_device_id.aaudio) and pass it - to ma_device_init(). -- The backend API will perform resampling where possible. The reason for this as opposed to using - miniaudio's built-in resampler is to take advantage of any potential device-specific - optimizations the driver may implement. - -BSD ---- -- The sndio backend is currently only enabled on OpenBSD builds. -- The audio(4) backend is supported on OpenBSD, but you may need to disable sndiod before you can - use it. - -15.4. UWP ---------- -- UWP only supports default playback and capture devices. -- UWP requires the Microphone capability to be enabled in the application's manifest (Package.appxmanifest): - - ``` - - ... - - - - - ``` - -15.5. Web Audio / Emscripten ----------------------------- -- You cannot use `-std=c*` compiler flags, nor `-ansi`. This only applies to the Emscripten build. -- The first time a context is initialized it will create a global object called "miniaudio" whose - primary purpose is to act as a factory for device objects. -- Currently the Web Audio backend uses ScriptProcessorNode's, but this may need to change later as - they've been deprecated. -- Google has implemented a policy in their browsers that prevent automatic media output without - first receiving some kind of user input. The following web page has additional details: - https://developers.google.com/web/updates/2017/09/autoplay-policy-changes. Starting the device - may fail if you try to start playback without first handling some kind of user input. - - - -16. Optimization Tips -===================== -See below for some tips on improving performance. - -16.1. Low Level API -------------------- -- In the data callback, if your data is already clipped prior to copying it into the output buffer, - set the `noClip` config option in the device config to true. This will disable miniaudio's built - in clipping function. -- By default, miniaudio will pre-silence the data callback's output buffer. If you know that you - will always write valid data to the output buffer you can disable pre-silencing by setting the - `noPreSilence` config option in the device config to true. - -16.2. High Level API --------------------- -- If a sound does not require doppler or pitch shifting, consider disabling pitching by - initializing the sound with the `MA_SOUND_FLAG_NO_PITCH` flag. -- If a sound does not require spatialization, disable it by initializing the sound with the - `MA_SOUND_FLAG_NO_SPATIALIZATION` flag. It can be re-enabled again post-initialization with - `ma_sound_set_spatialization_enabled()`. -- If you know all of your sounds will always be the same sample rate, set the engine's sample - rate to match that of the sounds. Likewise, if you're using a self-managed resource manager, - consider setting the decoded sample rate to match your sounds. By configuring everything to - use a consistent sample rate, sample rate conversion can be avoided. - - - -17. Miscellaneous Notes -======================= -- Automatic stream routing is enabled on a per-backend basis. Support is explicitly enabled for - WASAPI and Core Audio, however other backends such as PulseAudio may naturally support it, though - not all have been tested. -- When compiling with VC6 and earlier, decoding is restricted to files less than 2GB in size. This - is due to 64-bit file APIs not being available. -*/ - -#ifndef miniaudio_h -#define miniaudio_h - -#ifdef __cplusplus -extern "C" { -#endif - -#define MA_STRINGIFY(x) #x -#define MA_XSTRINGIFY(x) MA_STRINGIFY(x) - -#define MA_VERSION_MAJOR 0 -#define MA_VERSION_MINOR 11 -#define MA_VERSION_REVISION 22 -#define MA_VERSION_STRING MA_XSTRINGIFY(MA_VERSION_MAJOR) "." MA_XSTRINGIFY(MA_VERSION_MINOR) "." MA_XSTRINGIFY(MA_VERSION_REVISION) - -#if defined(_MSC_VER) && !defined(__clang__) - #pragma warning(push) - #pragma warning(disable:4201) /* nonstandard extension used: nameless struct/union */ - #pragma warning(disable:4214) /* nonstandard extension used: bit field types other than int */ - #pragma warning(disable:4324) /* structure was padded due to alignment specifier */ -#elif defined(__clang__) || (defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 8))) - #pragma GCC diagnostic push - #pragma GCC diagnostic ignored "-Wpedantic" /* For ISO C99 doesn't support unnamed structs/unions [-Wpedantic] */ - #if defined(__clang__) - #pragma GCC diagnostic ignored "-Wc11-extensions" /* anonymous unions are a C11 extension */ - #endif -#endif - - -#if defined(__LP64__) || defined(_WIN64) || (defined(__x86_64__) && !defined(__ILP32__)) || defined(_M_X64) || defined(__ia64) || defined(_M_IA64) || defined(__aarch64__) || defined(_M_ARM64) || defined(__powerpc64__) || defined(__ppc64__) - #define MA_SIZEOF_PTR 8 -#else - #define MA_SIZEOF_PTR 4 -#endif - -#include /* For size_t. */ - -/* Sized types. */ -#if defined(MA_USE_STDINT) - #include - typedef int8_t ma_int8; - typedef uint8_t ma_uint8; - typedef int16_t ma_int16; - typedef uint16_t ma_uint16; - typedef int32_t ma_int32; - typedef uint32_t ma_uint32; - typedef int64_t ma_int64; - typedef uint64_t ma_uint64; -#else - typedef signed char ma_int8; - typedef unsigned char ma_uint8; - typedef signed short ma_int16; - typedef unsigned short ma_uint16; - typedef signed int ma_int32; - typedef unsigned int ma_uint32; - #if defined(_MSC_VER) && !defined(__clang__) - typedef signed __int64 ma_int64; - typedef unsigned __int64 ma_uint64; - #else - #if defined(__clang__) || (defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6))) - #pragma GCC diagnostic push - #pragma GCC diagnostic ignored "-Wlong-long" - #if defined(__clang__) - #pragma GCC diagnostic ignored "-Wc++11-long-long" - #endif - #endif - typedef signed long long ma_int64; - typedef unsigned long long ma_uint64; - #if defined(__clang__) || (defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6))) - #pragma GCC diagnostic pop - #endif - #endif -#endif /* MA_USE_STDINT */ - -#if MA_SIZEOF_PTR == 8 - typedef ma_uint64 ma_uintptr; -#else - typedef ma_uint32 ma_uintptr; -#endif - -typedef ma_uint8 ma_bool8; -typedef ma_uint32 ma_bool32; -#define MA_TRUE 1 -#define MA_FALSE 0 - -/* These float types are not used universally by miniaudio. It's to simplify some macro expansion for atomic types. */ -typedef float ma_float; -typedef double ma_double; - -typedef void* ma_handle; -typedef void* ma_ptr; - -/* -ma_proc is annoying because when compiling with GCC we get pedantic warnings about converting -between `void*` and `void (*)()`. We can't use `void (*)()` with MSVC however, because we'll get -warning C4191 about "type cast between incompatible function types". To work around this I'm going -to use a different data type depending on the compiler. -*/ -#if defined(__GNUC__) -typedef void (*ma_proc)(void); -#else -typedef void* ma_proc; -#endif - -#if defined(_MSC_VER) && !defined(_WCHAR_T_DEFINED) -typedef ma_uint16 wchar_t; -#endif - -/* Define NULL for some compilers. */ -#ifndef NULL -#define NULL 0 -#endif - -#if defined(SIZE_MAX) - #define MA_SIZE_MAX SIZE_MAX -#else - #define MA_SIZE_MAX 0xFFFFFFFF /* When SIZE_MAX is not defined by the standard library just default to the maximum 32-bit unsigned integer. */ -#endif - - -/* Platform/backend detection. */ -#if defined(_WIN32) || defined(__COSMOPOLITAN__) - #define MA_WIN32 - #if defined(MA_FORCE_UWP) || (defined(WINAPI_FAMILY) && ((defined(WINAPI_FAMILY_PC_APP) && WINAPI_FAMILY == WINAPI_FAMILY_PC_APP) || (defined(WINAPI_FAMILY_PHONE_APP) && WINAPI_FAMILY == WINAPI_FAMILY_PHONE_APP))) - #define MA_WIN32_UWP - #elif defined(WINAPI_FAMILY) && (defined(WINAPI_FAMILY_GAMES) && WINAPI_FAMILY == WINAPI_FAMILY_GAMES) - #define MA_WIN32_GDK - #else - #define MA_WIN32_DESKTOP - #endif -#endif -#if !defined(_WIN32) /* If it's not Win32, assume POSIX. */ - #define MA_POSIX - - /* - Use the MA_NO_PTHREAD_IN_HEADER option at your own risk. This is intentionally undocumented. - You can use this to avoid including pthread.h in the header section. The downside is that it - results in some fixed sized structures being declared for the various types that are used in - miniaudio. The risk here is that these types might be too small for a given platform. This - risk is yours to take and no support will be offered if you enable this option. - */ - #ifndef MA_NO_PTHREAD_IN_HEADER - #include /* Unfortunate #include, but needed for pthread_t, pthread_mutex_t and pthread_cond_t types. */ - typedef pthread_t ma_pthread_t; - typedef pthread_mutex_t ma_pthread_mutex_t; - typedef pthread_cond_t ma_pthread_cond_t; - #else - typedef ma_uintptr ma_pthread_t; - typedef union ma_pthread_mutex_t { char __data[40]; ma_uint64 __alignment; } ma_pthread_mutex_t; - typedef union ma_pthread_cond_t { char __data[48]; ma_uint64 __alignment; } ma_pthread_cond_t; - #endif - - #if defined(__unix__) - #define MA_UNIX - #endif - #if defined(__linux__) - #define MA_LINUX - #endif - #if defined(__APPLE__) - #define MA_APPLE - #endif - #if defined(__DragonFly__) || defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__) - #define MA_BSD - #endif - #if defined(__ANDROID__) - #define MA_ANDROID - #endif - #if defined(__EMSCRIPTEN__) - #define MA_EMSCRIPTEN - #endif - #if defined(__ORBIS__) - #define MA_ORBIS - #endif - #if defined(__PROSPERO__) - #define MA_PROSPERO - #endif - #if defined(__NX__) - #define MA_NX - #endif - #if defined(__BEOS__) || defined(__HAIKU__) - #define MA_BEOS - #endif - #if defined(__HAIKU__) - #define MA_HAIKU - #endif -#endif - -#if defined(__has_c_attribute) - #if __has_c_attribute(fallthrough) - #define MA_FALLTHROUGH [[fallthrough]] - #endif -#endif -#if !defined(MA_FALLTHROUGH) && defined(__has_attribute) && (defined(__clang__) || defined(__GNUC__)) - #if __has_attribute(fallthrough) - #define MA_FALLTHROUGH __attribute__((fallthrough)) - #endif -#endif -#if !defined(MA_FALLTHROUGH) - #define MA_FALLTHROUGH ((void)0) -#endif - -#ifdef _MSC_VER - #define MA_INLINE __forceinline - - /* noinline was introduced in Visual Studio 2005. */ - #if _MSC_VER >= 1400 - #define MA_NO_INLINE __declspec(noinline) - #else - #define MA_NO_INLINE - #endif -#elif defined(__GNUC__) - /* - I've had a bug report where GCC is emitting warnings about functions possibly not being inlineable. This warning happens when - the __attribute__((always_inline)) attribute is defined without an "inline" statement. I think therefore there must be some - case where "__inline__" is not always defined, thus the compiler emitting these warnings. When using -std=c89 or -ansi on the - command line, we cannot use the "inline" keyword and instead need to use "__inline__". In an attempt to work around this issue - I am using "__inline__" only when we're compiling in strict ANSI mode. - */ - #if defined(__STRICT_ANSI__) - #define MA_GNUC_INLINE_HINT __inline__ - #else - #define MA_GNUC_INLINE_HINT inline - #endif - - #if (__GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 2)) || defined(__clang__) - #define MA_INLINE MA_GNUC_INLINE_HINT __attribute__((always_inline)) - #define MA_NO_INLINE __attribute__((noinline)) - #else - #define MA_INLINE MA_GNUC_INLINE_HINT - #define MA_NO_INLINE __attribute__((noinline)) - #endif -#elif defined(__WATCOMC__) - #define MA_INLINE __inline - #define MA_NO_INLINE -#else - #define MA_INLINE - #define MA_NO_INLINE -#endif - -/* MA_DLL is not officially supported. You're on your own if you want to use this. */ -#if defined(MA_DLL) - #if defined(_WIN32) - #define MA_DLL_IMPORT __declspec(dllimport) - #define MA_DLL_EXPORT __declspec(dllexport) - #define MA_DLL_PRIVATE static - #else - #if defined(__GNUC__) && __GNUC__ >= 4 - #define MA_DLL_IMPORT __attribute__((visibility("default"))) - #define MA_DLL_EXPORT __attribute__((visibility("default"))) - #define MA_DLL_PRIVATE __attribute__((visibility("hidden"))) - #else - #define MA_DLL_IMPORT - #define MA_DLL_EXPORT - #define MA_DLL_PRIVATE static - #endif - #endif -#endif - -#if !defined(MA_API) - #if defined(MA_DLL) - #if defined(MINIAUDIO_IMPLEMENTATION) || defined(MA_IMPLEMENTATION) - #define MA_API MA_DLL_EXPORT - #else - #define MA_API MA_DLL_IMPORT - #endif - #else - #define MA_API extern - #endif -#endif - -#if !defined(MA_STATIC) - #if defined(MA_DLL) - #define MA_PRIVATE MA_DLL_PRIVATE - #else - #define MA_PRIVATE static - #endif -#endif - - -/* SIMD alignment in bytes. Currently set to 32 bytes in preparation for future AVX optimizations. */ -#define MA_SIMD_ALIGNMENT 32 - -/* -Special wchar_t type to ensure any structures in the public sections that reference it have a -consistent size across all platforms. - -On Windows, wchar_t is 2 bytes, whereas everywhere else it's 4 bytes. Since Windows likes to use -wchar_t for its IDs, we need a special explicitly sized wchar type that is always 2 bytes on all -platforms. -*/ -#if !defined(MA_POSIX) && defined(MA_WIN32) -typedef wchar_t ma_wchar_win32; -#else -typedef ma_uint16 ma_wchar_win32; -#endif - - - -/* -Logging Levels -============== -Log levels are only used to give logging callbacks some context as to the severity of a log message -so they can do filtering. All log levels will be posted to registered logging callbacks. If you -don't want to output a certain log level you can discriminate against the log level in the callback. - -MA_LOG_LEVEL_DEBUG - Used for debugging. Useful for debug and test builds, but should be disabled in release builds. - -MA_LOG_LEVEL_INFO - Informational logging. Useful for debugging. This will never be called from within the data - callback. - -MA_LOG_LEVEL_WARNING - Warnings. You should enable this in you development builds and action them when encountered. These - logs usually indicate a potential problem or misconfiguration, but still allow you to keep - running. This will never be called from within the data callback. - -MA_LOG_LEVEL_ERROR - Error logging. This will be fired when an operation fails and is subsequently aborted. This can - be fired from within the data callback, in which case the device will be stopped. You should - always have this log level enabled. -*/ -typedef enum -{ - MA_LOG_LEVEL_DEBUG = 4, - MA_LOG_LEVEL_INFO = 3, - MA_LOG_LEVEL_WARNING = 2, - MA_LOG_LEVEL_ERROR = 1 -} ma_log_level; - -/* -Variables needing to be accessed atomically should be declared with this macro for two reasons: - - 1) It allows people who read the code to identify a variable as such; and - 2) It forces alignment on platforms where it's required or optimal. - -Note that for x86/64, alignment is not strictly necessary, but does have some performance -implications. Where supported by the compiler, alignment will be used, but otherwise if the CPU -architecture does not require it, it will simply leave it unaligned. This is the case with old -versions of Visual Studio, which I've confirmed with at least VC6. -*/ -#if !defined(_MSC_VER) && defined (__STDC_VERSION__) && (__STDC_VERSION__ >= 201112L) - #include - #define MA_ATOMIC(alignment, type) _Alignas(alignment) type -#else - #if defined(__GNUC__) - /* GCC-style compilers. */ - #define MA_ATOMIC(alignment, type) type __attribute__((aligned(alignment))) - #elif defined(_MSC_VER) && _MSC_VER > 1200 /* 1200 = VC6. Alignment not supported, but not necessary because x86 is the only supported target. */ - /* MSVC. */ - #define MA_ATOMIC(alignment, type) __declspec(align(alignment)) type - #else - /* Other compilers. */ - #define MA_ATOMIC(alignment, type) type - #endif -#endif - -typedef struct ma_context ma_context; -typedef struct ma_device ma_device; - -typedef ma_uint8 ma_channel; -typedef enum -{ - MA_CHANNEL_NONE = 0, - MA_CHANNEL_MONO = 1, - MA_CHANNEL_FRONT_LEFT = 2, - MA_CHANNEL_FRONT_RIGHT = 3, - MA_CHANNEL_FRONT_CENTER = 4, - MA_CHANNEL_LFE = 5, - MA_CHANNEL_BACK_LEFT = 6, - MA_CHANNEL_BACK_RIGHT = 7, - MA_CHANNEL_FRONT_LEFT_CENTER = 8, - MA_CHANNEL_FRONT_RIGHT_CENTER = 9, - MA_CHANNEL_BACK_CENTER = 10, - MA_CHANNEL_SIDE_LEFT = 11, - MA_CHANNEL_SIDE_RIGHT = 12, - MA_CHANNEL_TOP_CENTER = 13, - MA_CHANNEL_TOP_FRONT_LEFT = 14, - MA_CHANNEL_TOP_FRONT_CENTER = 15, - MA_CHANNEL_TOP_FRONT_RIGHT = 16, - MA_CHANNEL_TOP_BACK_LEFT = 17, - MA_CHANNEL_TOP_BACK_CENTER = 18, - MA_CHANNEL_TOP_BACK_RIGHT = 19, - MA_CHANNEL_AUX_0 = 20, - MA_CHANNEL_AUX_1 = 21, - MA_CHANNEL_AUX_2 = 22, - MA_CHANNEL_AUX_3 = 23, - MA_CHANNEL_AUX_4 = 24, - MA_CHANNEL_AUX_5 = 25, - MA_CHANNEL_AUX_6 = 26, - MA_CHANNEL_AUX_7 = 27, - MA_CHANNEL_AUX_8 = 28, - MA_CHANNEL_AUX_9 = 29, - MA_CHANNEL_AUX_10 = 30, - MA_CHANNEL_AUX_11 = 31, - MA_CHANNEL_AUX_12 = 32, - MA_CHANNEL_AUX_13 = 33, - MA_CHANNEL_AUX_14 = 34, - MA_CHANNEL_AUX_15 = 35, - MA_CHANNEL_AUX_16 = 36, - MA_CHANNEL_AUX_17 = 37, - MA_CHANNEL_AUX_18 = 38, - MA_CHANNEL_AUX_19 = 39, - MA_CHANNEL_AUX_20 = 40, - MA_CHANNEL_AUX_21 = 41, - MA_CHANNEL_AUX_22 = 42, - MA_CHANNEL_AUX_23 = 43, - MA_CHANNEL_AUX_24 = 44, - MA_CHANNEL_AUX_25 = 45, - MA_CHANNEL_AUX_26 = 46, - MA_CHANNEL_AUX_27 = 47, - MA_CHANNEL_AUX_28 = 48, - MA_CHANNEL_AUX_29 = 49, - MA_CHANNEL_AUX_30 = 50, - MA_CHANNEL_AUX_31 = 51, - MA_CHANNEL_LEFT = MA_CHANNEL_FRONT_LEFT, - MA_CHANNEL_RIGHT = MA_CHANNEL_FRONT_RIGHT, - MA_CHANNEL_POSITION_COUNT = (MA_CHANNEL_AUX_31 + 1) -} _ma_channel_position; /* Do not use `_ma_channel_position` directly. Use `ma_channel` instead. */ - -typedef enum -{ - MA_SUCCESS = 0, - MA_ERROR = -1, /* A generic error. */ - MA_INVALID_ARGS = -2, - MA_INVALID_OPERATION = -3, - MA_OUT_OF_MEMORY = -4, - MA_OUT_OF_RANGE = -5, - MA_ACCESS_DENIED = -6, - MA_DOES_NOT_EXIST = -7, - MA_ALREADY_EXISTS = -8, - MA_TOO_MANY_OPEN_FILES = -9, - MA_INVALID_FILE = -10, - MA_TOO_BIG = -11, - MA_PATH_TOO_LONG = -12, - MA_NAME_TOO_LONG = -13, - MA_NOT_DIRECTORY = -14, - MA_IS_DIRECTORY = -15, - MA_DIRECTORY_NOT_EMPTY = -16, - MA_AT_END = -17, - MA_NO_SPACE = -18, - MA_BUSY = -19, - MA_IO_ERROR = -20, - MA_INTERRUPT = -21, - MA_UNAVAILABLE = -22, - MA_ALREADY_IN_USE = -23, - MA_BAD_ADDRESS = -24, - MA_BAD_SEEK = -25, - MA_BAD_PIPE = -26, - MA_DEADLOCK = -27, - MA_TOO_MANY_LINKS = -28, - MA_NOT_IMPLEMENTED = -29, - MA_NO_MESSAGE = -30, - MA_BAD_MESSAGE = -31, - MA_NO_DATA_AVAILABLE = -32, - MA_INVALID_DATA = -33, - MA_TIMEOUT = -34, - MA_NO_NETWORK = -35, - MA_NOT_UNIQUE = -36, - MA_NOT_SOCKET = -37, - MA_NO_ADDRESS = -38, - MA_BAD_PROTOCOL = -39, - MA_PROTOCOL_UNAVAILABLE = -40, - MA_PROTOCOL_NOT_SUPPORTED = -41, - MA_PROTOCOL_FAMILY_NOT_SUPPORTED = -42, - MA_ADDRESS_FAMILY_NOT_SUPPORTED = -43, - MA_SOCKET_NOT_SUPPORTED = -44, - MA_CONNECTION_RESET = -45, - MA_ALREADY_CONNECTED = -46, - MA_NOT_CONNECTED = -47, - MA_CONNECTION_REFUSED = -48, - MA_NO_HOST = -49, - MA_IN_PROGRESS = -50, - MA_CANCELLED = -51, - MA_MEMORY_ALREADY_MAPPED = -52, - - /* General non-standard errors. */ - MA_CRC_MISMATCH = -100, - - /* General miniaudio-specific errors. */ - MA_FORMAT_NOT_SUPPORTED = -200, - MA_DEVICE_TYPE_NOT_SUPPORTED = -201, - MA_SHARE_MODE_NOT_SUPPORTED = -202, - MA_NO_BACKEND = -203, - MA_NO_DEVICE = -204, - MA_API_NOT_FOUND = -205, - MA_INVALID_DEVICE_CONFIG = -206, - MA_LOOP = -207, - MA_BACKEND_NOT_ENABLED = -208, - - /* State errors. */ - MA_DEVICE_NOT_INITIALIZED = -300, - MA_DEVICE_ALREADY_INITIALIZED = -301, - MA_DEVICE_NOT_STARTED = -302, - MA_DEVICE_NOT_STOPPED = -303, - - /* Operation errors. */ - MA_FAILED_TO_INIT_BACKEND = -400, - MA_FAILED_TO_OPEN_BACKEND_DEVICE = -401, - MA_FAILED_TO_START_BACKEND_DEVICE = -402, - MA_FAILED_TO_STOP_BACKEND_DEVICE = -403 -} ma_result; - - -#define MA_MIN_CHANNELS 1 -#ifndef MA_MAX_CHANNELS -#define MA_MAX_CHANNELS 254 -#endif - -#ifndef MA_MAX_FILTER_ORDER -#define MA_MAX_FILTER_ORDER 8 -#endif - -typedef enum -{ - ma_stream_format_pcm = 0 -} ma_stream_format; - -typedef enum -{ - ma_stream_layout_interleaved = 0, - ma_stream_layout_deinterleaved -} ma_stream_layout; - -typedef enum -{ - ma_dither_mode_none = 0, - ma_dither_mode_rectangle, - ma_dither_mode_triangle -} ma_dither_mode; - -typedef enum -{ - /* - I like to keep these explicitly defined because they're used as a key into a lookup table. When items are - added to this, make sure there are no gaps and that they're added to the lookup table in ma_get_bytes_per_sample(). - */ - ma_format_unknown = 0, /* Mainly used for indicating an error, but also used as the default for the output format for decoders. */ - ma_format_u8 = 1, - ma_format_s16 = 2, /* Seems to be the most widely supported format. */ - ma_format_s24 = 3, /* Tightly packed. 3 bytes per sample. */ - ma_format_s32 = 4, - ma_format_f32 = 5, - ma_format_count -} ma_format; - -typedef enum -{ - /* Standard rates need to be in priority order. */ - ma_standard_sample_rate_48000 = 48000, /* Most common */ - ma_standard_sample_rate_44100 = 44100, - - ma_standard_sample_rate_32000 = 32000, /* Lows */ - ma_standard_sample_rate_24000 = 24000, - ma_standard_sample_rate_22050 = 22050, - - ma_standard_sample_rate_88200 = 88200, /* Highs */ - ma_standard_sample_rate_96000 = 96000, - ma_standard_sample_rate_176400 = 176400, - ma_standard_sample_rate_192000 = 192000, - - ma_standard_sample_rate_16000 = 16000, /* Extreme lows */ - ma_standard_sample_rate_11025 = 11025, - ma_standard_sample_rate_8000 = 8000, - - ma_standard_sample_rate_352800 = 352800, /* Extreme highs */ - ma_standard_sample_rate_384000 = 384000, - - ma_standard_sample_rate_min = ma_standard_sample_rate_8000, - ma_standard_sample_rate_max = ma_standard_sample_rate_384000, - ma_standard_sample_rate_count = 14 /* Need to maintain the count manually. Make sure this is updated if items are added to enum. */ -} ma_standard_sample_rate; - - -typedef enum -{ - ma_channel_mix_mode_rectangular = 0, /* Simple averaging based on the plane(s) the channel is sitting on. */ - ma_channel_mix_mode_simple, /* Drop excess channels; zeroed out extra channels. */ - ma_channel_mix_mode_custom_weights, /* Use custom weights specified in ma_channel_converter_config. */ - ma_channel_mix_mode_default = ma_channel_mix_mode_rectangular -} ma_channel_mix_mode; - -typedef enum -{ - ma_standard_channel_map_microsoft, - ma_standard_channel_map_alsa, - ma_standard_channel_map_rfc3551, /* Based off AIFF. */ - ma_standard_channel_map_flac, - ma_standard_channel_map_vorbis, - ma_standard_channel_map_sound4, /* FreeBSD's sound(4). */ - ma_standard_channel_map_sndio, /* www.sndio.org/tips.html */ - ma_standard_channel_map_webaudio = ma_standard_channel_map_flac, /* https://webaudio.github.io/web-audio-api/#ChannelOrdering. Only 1, 2, 4 and 6 channels are defined, but can fill in the gaps with logical assumptions. */ - ma_standard_channel_map_default = ma_standard_channel_map_microsoft -} ma_standard_channel_map; - -typedef enum -{ - ma_performance_profile_low_latency = 0, - ma_performance_profile_conservative -} ma_performance_profile; - - -typedef struct -{ - void* pUserData; - void* (* onMalloc)(size_t sz, void* pUserData); - void* (* onRealloc)(void* p, size_t sz, void* pUserData); - void (* onFree)(void* p, void* pUserData); -} ma_allocation_callbacks; - -typedef struct -{ - ma_int32 state; -} ma_lcg; - - -/* -Atomics. - -These are typesafe structures to prevent errors as a result of forgetting to reference variables atomically. It's too -easy to introduce subtle bugs where you accidentally do a regular assignment instead of an atomic load/store, etc. By -using a struct we can enforce the use of atomics at compile time. - -These types are declared in the header section because we need to reference them in structs below, but functions for -using them are only exposed in the implementation section. I do not want these to be part of the public API. - -There's a few downsides to this system. The first is that you need to declare a new struct for each type. Below are -some macros to help with the declarations. They will be named like so: - - ma_atomic_uint32 - atomic ma_uint32 - ma_atomic_int32 - atomic ma_int32 - ma_atomic_uint64 - atomic ma_uint64 - ma_atomic_float - atomic float - ma_atomic_bool32 - atomic ma_bool32 - -The other downside is that atomic pointers are extremely messy. You need to declare a new struct for each specific -type of pointer you need to make atomic. For example, an atomic ma_node* will look like this: - - MA_ATOMIC_SAFE_TYPE_IMPL_PTR(node) - -Which will declare a type struct that's named like so: - - ma_atomic_ptr_node - -Functions to use the atomic types are declared in the implementation section. All atomic functions are prefixed with -the name of the struct. For example: - - ma_atomic_uint32_set() - Atomic store of ma_uint32 - ma_atomic_uint32_get() - Atomic load of ma_uint32 - etc. - -For pointer types it's the same, which makes them a bit messy to use due to the length of each function name, but in -return you get type safety and enforcement of atomic operations. -*/ -#define MA_ATOMIC_SAFE_TYPE_DECL(c89TypeExtension, typeSize, type) \ - typedef struct \ - { \ - MA_ATOMIC(typeSize, ma_##type) value; \ - } ma_atomic_##type; \ - -#define MA_ATOMIC_SAFE_TYPE_DECL_PTR(type) \ - typedef struct \ - { \ - MA_ATOMIC(MA_SIZEOF_PTR, ma_##type*) value; \ - } ma_atomic_ptr_##type; \ - -MA_ATOMIC_SAFE_TYPE_DECL(32, 4, uint32) -MA_ATOMIC_SAFE_TYPE_DECL(i32, 4, int32) -MA_ATOMIC_SAFE_TYPE_DECL(64, 8, uint64) -MA_ATOMIC_SAFE_TYPE_DECL(f32, 4, float) -MA_ATOMIC_SAFE_TYPE_DECL(32, 4, bool32) - - -/* Spinlocks are 32-bit for compatibility reasons. */ -typedef ma_uint32 ma_spinlock; - -#ifndef MA_NO_THREADING - /* Thread priorities should be ordered such that the default priority of the worker thread is 0. */ - typedef enum - { - ma_thread_priority_idle = -5, - ma_thread_priority_lowest = -4, - ma_thread_priority_low = -3, - ma_thread_priority_normal = -2, - ma_thread_priority_high = -1, - ma_thread_priority_highest = 0, - ma_thread_priority_realtime = 1, - ma_thread_priority_default = 0 - } ma_thread_priority; - - #if defined(MA_POSIX) - typedef ma_pthread_t ma_thread; - #elif defined(MA_WIN32) - typedef ma_handle ma_thread; - #endif - - #if defined(MA_POSIX) - typedef ma_pthread_mutex_t ma_mutex; - #elif defined(MA_WIN32) - typedef ma_handle ma_mutex; - #endif - - #if defined(MA_POSIX) - typedef struct - { - ma_uint32 value; - ma_pthread_mutex_t lock; - ma_pthread_cond_t cond; - } ma_event; - #elif defined(MA_WIN32) - typedef ma_handle ma_event; - #endif - - #if defined(MA_POSIX) - typedef struct - { - int value; - ma_pthread_mutex_t lock; - ma_pthread_cond_t cond; - } ma_semaphore; - #elif defined(MA_WIN32) - typedef ma_handle ma_semaphore; - #endif -#else - /* MA_NO_THREADING is set which means threading is disabled. Threading is required by some API families. If any of these are enabled we need to throw an error. */ - #ifndef MA_NO_DEVICE_IO - #error "MA_NO_THREADING cannot be used without MA_NO_DEVICE_IO"; - #endif -#endif /* MA_NO_THREADING */ - - -/* -Retrieves the version of miniaudio as separated integers. Each component can be NULL if it's not required. -*/ -MA_API void ma_version(ma_uint32* pMajor, ma_uint32* pMinor, ma_uint32* pRevision); - -/* -Retrieves the version of miniaudio as a string which can be useful for logging purposes. -*/ -MA_API const char* ma_version_string(void); - - -/************************************************************************************************************************************************************** - -Logging - -**************************************************************************************************************************************************************/ -#include /* For va_list. */ - -#if defined(__has_attribute) - #if __has_attribute(format) - #define MA_ATTRIBUTE_FORMAT(fmt, va) __attribute__((format(printf, fmt, va))) - #endif -#endif -#ifndef MA_ATTRIBUTE_FORMAT -#define MA_ATTRIBUTE_FORMAT(fmt, va) -#endif - -#ifndef MA_MAX_LOG_CALLBACKS -#define MA_MAX_LOG_CALLBACKS 4 -#endif - - -/* -The callback for handling log messages. - - -Parameters ----------- -pUserData (in) - The user data pointer that was passed into ma_log_register_callback(). - -logLevel (in) - The log level. This can be one of the following: - - +----------------------+ - | Log Level | - +----------------------+ - | MA_LOG_LEVEL_DEBUG | - | MA_LOG_LEVEL_INFO | - | MA_LOG_LEVEL_WARNING | - | MA_LOG_LEVEL_ERROR | - +----------------------+ - -pMessage (in) - The log message. -*/ -typedef void (* ma_log_callback_proc)(void* pUserData, ma_uint32 level, const char* pMessage); - -typedef struct -{ - ma_log_callback_proc onLog; - void* pUserData; -} ma_log_callback; - -MA_API ma_log_callback ma_log_callback_init(ma_log_callback_proc onLog, void* pUserData); - - -typedef struct -{ - ma_log_callback callbacks[MA_MAX_LOG_CALLBACKS]; - ma_uint32 callbackCount; - ma_allocation_callbacks allocationCallbacks; /* Need to store these persistently because ma_log_postv() might need to allocate a buffer on the heap. */ -#ifndef MA_NO_THREADING - ma_mutex lock; /* For thread safety just to make it easier and safer for the logging implementation. */ -#endif -} ma_log; - -MA_API ma_result ma_log_init(const ma_allocation_callbacks* pAllocationCallbacks, ma_log* pLog); -MA_API void ma_log_uninit(ma_log* pLog); -MA_API ma_result ma_log_register_callback(ma_log* pLog, ma_log_callback callback); -MA_API ma_result ma_log_unregister_callback(ma_log* pLog, ma_log_callback callback); -MA_API ma_result ma_log_post(ma_log* pLog, ma_uint32 level, const char* pMessage); -MA_API ma_result ma_log_postv(ma_log* pLog, ma_uint32 level, const char* pFormat, va_list args); -MA_API ma_result ma_log_postf(ma_log* pLog, ma_uint32 level, const char* pFormat, ...) MA_ATTRIBUTE_FORMAT(3, 4); - - -/************************************************************************************************************************************************************** - -Biquad Filtering - -**************************************************************************************************************************************************************/ -typedef union -{ - float f32; - ma_int32 s32; -} ma_biquad_coefficient; - -typedef struct -{ - ma_format format; - ma_uint32 channels; - double b0; - double b1; - double b2; - double a0; - double a1; - double a2; -} ma_biquad_config; - -MA_API ma_biquad_config ma_biquad_config_init(ma_format format, ma_uint32 channels, double b0, double b1, double b2, double a0, double a1, double a2); - -typedef struct -{ - ma_format format; - ma_uint32 channels; - ma_biquad_coefficient b0; - ma_biquad_coefficient b1; - ma_biquad_coefficient b2; - ma_biquad_coefficient a1; - ma_biquad_coefficient a2; - ma_biquad_coefficient* pR1; - ma_biquad_coefficient* pR2; - - /* Memory management. */ - void* _pHeap; - ma_bool32 _ownsHeap; -} ma_biquad; - -MA_API ma_result ma_biquad_get_heap_size(const ma_biquad_config* pConfig, size_t* pHeapSizeInBytes); -MA_API ma_result ma_biquad_init_preallocated(const ma_biquad_config* pConfig, void* pHeap, ma_biquad* pBQ); -MA_API ma_result ma_biquad_init(const ma_biquad_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_biquad* pBQ); -MA_API void ma_biquad_uninit(ma_biquad* pBQ, const ma_allocation_callbacks* pAllocationCallbacks); -MA_API ma_result ma_biquad_reinit(const ma_biquad_config* pConfig, ma_biquad* pBQ); -MA_API ma_result ma_biquad_clear_cache(ma_biquad* pBQ); -MA_API ma_result ma_biquad_process_pcm_frames(ma_biquad* pBQ, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount); -MA_API ma_uint32 ma_biquad_get_latency(const ma_biquad* pBQ); - - -/************************************************************************************************************************************************************** - -Low-Pass Filtering - -**************************************************************************************************************************************************************/ -typedef struct -{ - ma_format format; - ma_uint32 channels; - ma_uint32 sampleRate; - double cutoffFrequency; - double q; -} ma_lpf1_config, ma_lpf2_config; - -MA_API ma_lpf1_config ma_lpf1_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRate, double cutoffFrequency); -MA_API ma_lpf2_config ma_lpf2_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRate, double cutoffFrequency, double q); - -typedef struct -{ - ma_format format; - ma_uint32 channels; - ma_biquad_coefficient a; - ma_biquad_coefficient* pR1; - - /* Memory management. */ - void* _pHeap; - ma_bool32 _ownsHeap; -} ma_lpf1; - -MA_API ma_result ma_lpf1_get_heap_size(const ma_lpf1_config* pConfig, size_t* pHeapSizeInBytes); -MA_API ma_result ma_lpf1_init_preallocated(const ma_lpf1_config* pConfig, void* pHeap, ma_lpf1* pLPF); -MA_API ma_result ma_lpf1_init(const ma_lpf1_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_lpf1* pLPF); -MA_API void ma_lpf1_uninit(ma_lpf1* pLPF, const ma_allocation_callbacks* pAllocationCallbacks); -MA_API ma_result ma_lpf1_reinit(const ma_lpf1_config* pConfig, ma_lpf1* pLPF); -MA_API ma_result ma_lpf1_clear_cache(ma_lpf1* pLPF); -MA_API ma_result ma_lpf1_process_pcm_frames(ma_lpf1* pLPF, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount); -MA_API ma_uint32 ma_lpf1_get_latency(const ma_lpf1* pLPF); - -typedef struct -{ - ma_biquad bq; /* The second order low-pass filter is implemented as a biquad filter. */ -} ma_lpf2; - -MA_API ma_result ma_lpf2_get_heap_size(const ma_lpf2_config* pConfig, size_t* pHeapSizeInBytes); -MA_API ma_result ma_lpf2_init_preallocated(const ma_lpf2_config* pConfig, void* pHeap, ma_lpf2* pHPF); -MA_API ma_result ma_lpf2_init(const ma_lpf2_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_lpf2* pLPF); -MA_API void ma_lpf2_uninit(ma_lpf2* pLPF, const ma_allocation_callbacks* pAllocationCallbacks); -MA_API ma_result ma_lpf2_reinit(const ma_lpf2_config* pConfig, ma_lpf2* pLPF); -MA_API ma_result ma_lpf2_clear_cache(ma_lpf2* pLPF); -MA_API ma_result ma_lpf2_process_pcm_frames(ma_lpf2* pLPF, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount); -MA_API ma_uint32 ma_lpf2_get_latency(const ma_lpf2* pLPF); - - -typedef struct -{ - ma_format format; - ma_uint32 channels; - ma_uint32 sampleRate; - double cutoffFrequency; - ma_uint32 order; /* If set to 0, will be treated as a passthrough (no filtering will be applied). */ -} ma_lpf_config; - -MA_API ma_lpf_config ma_lpf_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRate, double cutoffFrequency, ma_uint32 order); - -typedef struct -{ - ma_format format; - ma_uint32 channels; - ma_uint32 sampleRate; - ma_uint32 lpf1Count; - ma_uint32 lpf2Count; - ma_lpf1* pLPF1; - ma_lpf2* pLPF2; - - /* Memory management. */ - void* _pHeap; - ma_bool32 _ownsHeap; -} ma_lpf; - -MA_API ma_result ma_lpf_get_heap_size(const ma_lpf_config* pConfig, size_t* pHeapSizeInBytes); -MA_API ma_result ma_lpf_init_preallocated(const ma_lpf_config* pConfig, void* pHeap, ma_lpf* pLPF); -MA_API ma_result ma_lpf_init(const ma_lpf_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_lpf* pLPF); -MA_API void ma_lpf_uninit(ma_lpf* pLPF, const ma_allocation_callbacks* pAllocationCallbacks); -MA_API ma_result ma_lpf_reinit(const ma_lpf_config* pConfig, ma_lpf* pLPF); -MA_API ma_result ma_lpf_clear_cache(ma_lpf* pLPF); -MA_API ma_result ma_lpf_process_pcm_frames(ma_lpf* pLPF, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount); -MA_API ma_uint32 ma_lpf_get_latency(const ma_lpf* pLPF); - - -/************************************************************************************************************************************************************** - -High-Pass Filtering - -**************************************************************************************************************************************************************/ -typedef struct -{ - ma_format format; - ma_uint32 channels; - ma_uint32 sampleRate; - double cutoffFrequency; - double q; -} ma_hpf1_config, ma_hpf2_config; - -MA_API ma_hpf1_config ma_hpf1_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRate, double cutoffFrequency); -MA_API ma_hpf2_config ma_hpf2_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRate, double cutoffFrequency, double q); - -typedef struct -{ - ma_format format; - ma_uint32 channels; - ma_biquad_coefficient a; - ma_biquad_coefficient* pR1; - - /* Memory management. */ - void* _pHeap; - ma_bool32 _ownsHeap; -} ma_hpf1; - -MA_API ma_result ma_hpf1_get_heap_size(const ma_hpf1_config* pConfig, size_t* pHeapSizeInBytes); -MA_API ma_result ma_hpf1_init_preallocated(const ma_hpf1_config* pConfig, void* pHeap, ma_hpf1* pLPF); -MA_API ma_result ma_hpf1_init(const ma_hpf1_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_hpf1* pHPF); -MA_API void ma_hpf1_uninit(ma_hpf1* pHPF, const ma_allocation_callbacks* pAllocationCallbacks); -MA_API ma_result ma_hpf1_reinit(const ma_hpf1_config* pConfig, ma_hpf1* pHPF); -MA_API ma_result ma_hpf1_process_pcm_frames(ma_hpf1* pHPF, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount); -MA_API ma_uint32 ma_hpf1_get_latency(const ma_hpf1* pHPF); - -typedef struct -{ - ma_biquad bq; /* The second order high-pass filter is implemented as a biquad filter. */ -} ma_hpf2; - -MA_API ma_result ma_hpf2_get_heap_size(const ma_hpf2_config* pConfig, size_t* pHeapSizeInBytes); -MA_API ma_result ma_hpf2_init_preallocated(const ma_hpf2_config* pConfig, void* pHeap, ma_hpf2* pHPF); -MA_API ma_result ma_hpf2_init(const ma_hpf2_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_hpf2* pHPF); -MA_API void ma_hpf2_uninit(ma_hpf2* pHPF, const ma_allocation_callbacks* pAllocationCallbacks); -MA_API ma_result ma_hpf2_reinit(const ma_hpf2_config* pConfig, ma_hpf2* pHPF); -MA_API ma_result ma_hpf2_process_pcm_frames(ma_hpf2* pHPF, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount); -MA_API ma_uint32 ma_hpf2_get_latency(const ma_hpf2* pHPF); - - -typedef struct -{ - ma_format format; - ma_uint32 channels; - ma_uint32 sampleRate; - double cutoffFrequency; - ma_uint32 order; /* If set to 0, will be treated as a passthrough (no filtering will be applied). */ -} ma_hpf_config; - -MA_API ma_hpf_config ma_hpf_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRate, double cutoffFrequency, ma_uint32 order); - -typedef struct -{ - ma_format format; - ma_uint32 channels; - ma_uint32 sampleRate; - ma_uint32 hpf1Count; - ma_uint32 hpf2Count; - ma_hpf1* pHPF1; - ma_hpf2* pHPF2; - - /* Memory management. */ - void* _pHeap; - ma_bool32 _ownsHeap; -} ma_hpf; - -MA_API ma_result ma_hpf_get_heap_size(const ma_hpf_config* pConfig, size_t* pHeapSizeInBytes); -MA_API ma_result ma_hpf_init_preallocated(const ma_hpf_config* pConfig, void* pHeap, ma_hpf* pLPF); -MA_API ma_result ma_hpf_init(const ma_hpf_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_hpf* pHPF); -MA_API void ma_hpf_uninit(ma_hpf* pHPF, const ma_allocation_callbacks* pAllocationCallbacks); -MA_API ma_result ma_hpf_reinit(const ma_hpf_config* pConfig, ma_hpf* pHPF); -MA_API ma_result ma_hpf_process_pcm_frames(ma_hpf* pHPF, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount); -MA_API ma_uint32 ma_hpf_get_latency(const ma_hpf* pHPF); - - -/************************************************************************************************************************************************************** - -Band-Pass Filtering - -**************************************************************************************************************************************************************/ -typedef struct -{ - ma_format format; - ma_uint32 channels; - ma_uint32 sampleRate; - double cutoffFrequency; - double q; -} ma_bpf2_config; - -MA_API ma_bpf2_config ma_bpf2_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRate, double cutoffFrequency, double q); - -typedef struct -{ - ma_biquad bq; /* The second order band-pass filter is implemented as a biquad filter. */ -} ma_bpf2; - -MA_API ma_result ma_bpf2_get_heap_size(const ma_bpf2_config* pConfig, size_t* pHeapSizeInBytes); -MA_API ma_result ma_bpf2_init_preallocated(const ma_bpf2_config* pConfig, void* pHeap, ma_bpf2* pBPF); -MA_API ma_result ma_bpf2_init(const ma_bpf2_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_bpf2* pBPF); -MA_API void ma_bpf2_uninit(ma_bpf2* pBPF, const ma_allocation_callbacks* pAllocationCallbacks); -MA_API ma_result ma_bpf2_reinit(const ma_bpf2_config* pConfig, ma_bpf2* pBPF); -MA_API ma_result ma_bpf2_process_pcm_frames(ma_bpf2* pBPF, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount); -MA_API ma_uint32 ma_bpf2_get_latency(const ma_bpf2* pBPF); - - -typedef struct -{ - ma_format format; - ma_uint32 channels; - ma_uint32 sampleRate; - double cutoffFrequency; - ma_uint32 order; /* If set to 0, will be treated as a passthrough (no filtering will be applied). */ -} ma_bpf_config; - -MA_API ma_bpf_config ma_bpf_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRate, double cutoffFrequency, ma_uint32 order); - -typedef struct -{ - ma_format format; - ma_uint32 channels; - ma_uint32 bpf2Count; - ma_bpf2* pBPF2; - - /* Memory management. */ - void* _pHeap; - ma_bool32 _ownsHeap; -} ma_bpf; - -MA_API ma_result ma_bpf_get_heap_size(const ma_bpf_config* pConfig, size_t* pHeapSizeInBytes); -MA_API ma_result ma_bpf_init_preallocated(const ma_bpf_config* pConfig, void* pHeap, ma_bpf* pBPF); -MA_API ma_result ma_bpf_init(const ma_bpf_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_bpf* pBPF); -MA_API void ma_bpf_uninit(ma_bpf* pBPF, const ma_allocation_callbacks* pAllocationCallbacks); -MA_API ma_result ma_bpf_reinit(const ma_bpf_config* pConfig, ma_bpf* pBPF); -MA_API ma_result ma_bpf_process_pcm_frames(ma_bpf* pBPF, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount); -MA_API ma_uint32 ma_bpf_get_latency(const ma_bpf* pBPF); - - -/************************************************************************************************************************************************************** - -Notching Filter - -**************************************************************************************************************************************************************/ -typedef struct -{ - ma_format format; - ma_uint32 channels; - ma_uint32 sampleRate; - double q; - double frequency; -} ma_notch2_config, ma_notch_config; - -MA_API ma_notch2_config ma_notch2_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRate, double q, double frequency); - -typedef struct -{ - ma_biquad bq; -} ma_notch2; - -MA_API ma_result ma_notch2_get_heap_size(const ma_notch2_config* pConfig, size_t* pHeapSizeInBytes); -MA_API ma_result ma_notch2_init_preallocated(const ma_notch2_config* pConfig, void* pHeap, ma_notch2* pFilter); -MA_API ma_result ma_notch2_init(const ma_notch2_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_notch2* pFilter); -MA_API void ma_notch2_uninit(ma_notch2* pFilter, const ma_allocation_callbacks* pAllocationCallbacks); -MA_API ma_result ma_notch2_reinit(const ma_notch2_config* pConfig, ma_notch2* pFilter); -MA_API ma_result ma_notch2_process_pcm_frames(ma_notch2* pFilter, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount); -MA_API ma_uint32 ma_notch2_get_latency(const ma_notch2* pFilter); - - -/************************************************************************************************************************************************************** - -Peaking EQ Filter - -**************************************************************************************************************************************************************/ -typedef struct -{ - ma_format format; - ma_uint32 channels; - ma_uint32 sampleRate; - double gainDB; - double q; - double frequency; -} ma_peak2_config, ma_peak_config; - -MA_API ma_peak2_config ma_peak2_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRate, double gainDB, double q, double frequency); - -typedef struct -{ - ma_biquad bq; -} ma_peak2; - -MA_API ma_result ma_peak2_get_heap_size(const ma_peak2_config* pConfig, size_t* pHeapSizeInBytes); -MA_API ma_result ma_peak2_init_preallocated(const ma_peak2_config* pConfig, void* pHeap, ma_peak2* pFilter); -MA_API ma_result ma_peak2_init(const ma_peak2_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_peak2* pFilter); -MA_API void ma_peak2_uninit(ma_peak2* pFilter, const ma_allocation_callbacks* pAllocationCallbacks); -MA_API ma_result ma_peak2_reinit(const ma_peak2_config* pConfig, ma_peak2* pFilter); -MA_API ma_result ma_peak2_process_pcm_frames(ma_peak2* pFilter, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount); -MA_API ma_uint32 ma_peak2_get_latency(const ma_peak2* pFilter); - - -/************************************************************************************************************************************************************** - -Low Shelf Filter - -**************************************************************************************************************************************************************/ -typedef struct -{ - ma_format format; - ma_uint32 channels; - ma_uint32 sampleRate; - double gainDB; - double shelfSlope; - double frequency; -} ma_loshelf2_config, ma_loshelf_config; - -MA_API ma_loshelf2_config ma_loshelf2_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRate, double gainDB, double shelfSlope, double frequency); - -typedef struct -{ - ma_biquad bq; -} ma_loshelf2; - -MA_API ma_result ma_loshelf2_get_heap_size(const ma_loshelf2_config* pConfig, size_t* pHeapSizeInBytes); -MA_API ma_result ma_loshelf2_init_preallocated(const ma_loshelf2_config* pConfig, void* pHeap, ma_loshelf2* pFilter); -MA_API ma_result ma_loshelf2_init(const ma_loshelf2_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_loshelf2* pFilter); -MA_API void ma_loshelf2_uninit(ma_loshelf2* pFilter, const ma_allocation_callbacks* pAllocationCallbacks); -MA_API ma_result ma_loshelf2_reinit(const ma_loshelf2_config* pConfig, ma_loshelf2* pFilter); -MA_API ma_result ma_loshelf2_process_pcm_frames(ma_loshelf2* pFilter, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount); -MA_API ma_uint32 ma_loshelf2_get_latency(const ma_loshelf2* pFilter); - - -/************************************************************************************************************************************************************** - -High Shelf Filter - -**************************************************************************************************************************************************************/ -typedef struct -{ - ma_format format; - ma_uint32 channels; - ma_uint32 sampleRate; - double gainDB; - double shelfSlope; - double frequency; -} ma_hishelf2_config, ma_hishelf_config; - -MA_API ma_hishelf2_config ma_hishelf2_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRate, double gainDB, double shelfSlope, double frequency); - -typedef struct -{ - ma_biquad bq; -} ma_hishelf2; - -MA_API ma_result ma_hishelf2_get_heap_size(const ma_hishelf2_config* pConfig, size_t* pHeapSizeInBytes); -MA_API ma_result ma_hishelf2_init_preallocated(const ma_hishelf2_config* pConfig, void* pHeap, ma_hishelf2* pFilter); -MA_API ma_result ma_hishelf2_init(const ma_hishelf2_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_hishelf2* pFilter); -MA_API void ma_hishelf2_uninit(ma_hishelf2* pFilter, const ma_allocation_callbacks* pAllocationCallbacks); -MA_API ma_result ma_hishelf2_reinit(const ma_hishelf2_config* pConfig, ma_hishelf2* pFilter); -MA_API ma_result ma_hishelf2_process_pcm_frames(ma_hishelf2* pFilter, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount); -MA_API ma_uint32 ma_hishelf2_get_latency(const ma_hishelf2* pFilter); - - - -/* -Delay -*/ -typedef struct -{ - ma_uint32 channels; - ma_uint32 sampleRate; - ma_uint32 delayInFrames; - ma_bool32 delayStart; /* Set to true to delay the start of the output; false otherwise. */ - float wet; /* 0..1. Default = 1. */ - float dry; /* 0..1. Default = 1. */ - float decay; /* 0..1. Default = 0 (no feedback). Feedback decay. Use this for echo. */ -} ma_delay_config; - -MA_API ma_delay_config ma_delay_config_init(ma_uint32 channels, ma_uint32 sampleRate, ma_uint32 delayInFrames, float decay); - - -typedef struct -{ - ma_delay_config config; - ma_uint32 cursor; /* Feedback is written to this cursor. Always equal or in front of the read cursor. */ - ma_uint32 bufferSizeInFrames; - float* pBuffer; -} ma_delay; - -MA_API ma_result ma_delay_init(const ma_delay_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_delay* pDelay); -MA_API void ma_delay_uninit(ma_delay* pDelay, const ma_allocation_callbacks* pAllocationCallbacks); -MA_API ma_result ma_delay_process_pcm_frames(ma_delay* pDelay, void* pFramesOut, const void* pFramesIn, ma_uint32 frameCount); -MA_API void ma_delay_set_wet(ma_delay* pDelay, float value); -MA_API float ma_delay_get_wet(const ma_delay* pDelay); -MA_API void ma_delay_set_dry(ma_delay* pDelay, float value); -MA_API float ma_delay_get_dry(const ma_delay* pDelay); -MA_API void ma_delay_set_decay(ma_delay* pDelay, float value); -MA_API float ma_delay_get_decay(const ma_delay* pDelay); - - -/* Gainer for smooth volume changes. */ -typedef struct -{ - ma_uint32 channels; - ma_uint32 smoothTimeInFrames; -} ma_gainer_config; - -MA_API ma_gainer_config ma_gainer_config_init(ma_uint32 channels, ma_uint32 smoothTimeInFrames); - - -typedef struct -{ - ma_gainer_config config; - ma_uint32 t; - float masterVolume; - float* pOldGains; - float* pNewGains; - - /* Memory management. */ - void* _pHeap; - ma_bool32 _ownsHeap; -} ma_gainer; - -MA_API ma_result ma_gainer_get_heap_size(const ma_gainer_config* pConfig, size_t* pHeapSizeInBytes); -MA_API ma_result ma_gainer_init_preallocated(const ma_gainer_config* pConfig, void* pHeap, ma_gainer* pGainer); -MA_API ma_result ma_gainer_init(const ma_gainer_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_gainer* pGainer); -MA_API void ma_gainer_uninit(ma_gainer* pGainer, const ma_allocation_callbacks* pAllocationCallbacks); -MA_API ma_result ma_gainer_process_pcm_frames(ma_gainer* pGainer, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount); -MA_API ma_result ma_gainer_set_gain(ma_gainer* pGainer, float newGain); -MA_API ma_result ma_gainer_set_gains(ma_gainer* pGainer, float* pNewGains); -MA_API ma_result ma_gainer_set_master_volume(ma_gainer* pGainer, float volume); -MA_API ma_result ma_gainer_get_master_volume(const ma_gainer* pGainer, float* pVolume); - - - -/* Stereo panner. */ -typedef enum -{ - ma_pan_mode_balance = 0, /* Does not blend one side with the other. Technically just a balance. Compatible with other popular audio engines and therefore the default. */ - ma_pan_mode_pan /* A true pan. The sound from one side will "move" to the other side and blend with it. */ -} ma_pan_mode; - -typedef struct -{ - ma_format format; - ma_uint32 channels; - ma_pan_mode mode; - float pan; -} ma_panner_config; - -MA_API ma_panner_config ma_panner_config_init(ma_format format, ma_uint32 channels); - - -typedef struct -{ - ma_format format; - ma_uint32 channels; - ma_pan_mode mode; - float pan; /* -1..1 where 0 is no pan, -1 is left side, +1 is right side. Defaults to 0. */ -} ma_panner; - -MA_API ma_result ma_panner_init(const ma_panner_config* pConfig, ma_panner* pPanner); -MA_API ma_result ma_panner_process_pcm_frames(ma_panner* pPanner, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount); -MA_API void ma_panner_set_mode(ma_panner* pPanner, ma_pan_mode mode); -MA_API ma_pan_mode ma_panner_get_mode(const ma_panner* pPanner); -MA_API void ma_panner_set_pan(ma_panner* pPanner, float pan); -MA_API float ma_panner_get_pan(const ma_panner* pPanner); - - - -/* Fader. */ -typedef struct -{ - ma_format format; - ma_uint32 channels; - ma_uint32 sampleRate; -} ma_fader_config; - -MA_API ma_fader_config ma_fader_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRate); - -typedef struct -{ - ma_fader_config config; - float volumeBeg; /* If volumeBeg and volumeEnd is equal to 1, no fading happens (ma_fader_process_pcm_frames() will run as a passthrough). */ - float volumeEnd; - ma_uint64 lengthInFrames; /* The total length of the fade. */ - ma_int64 cursorInFrames; /* The current time in frames. Incremented by ma_fader_process_pcm_frames(). Signed because it'll be offset by startOffsetInFrames in set_fade_ex(). */ -} ma_fader; - -MA_API ma_result ma_fader_init(const ma_fader_config* pConfig, ma_fader* pFader); -MA_API ma_result ma_fader_process_pcm_frames(ma_fader* pFader, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount); -MA_API void ma_fader_get_data_format(const ma_fader* pFader, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate); -MA_API void ma_fader_set_fade(ma_fader* pFader, float volumeBeg, float volumeEnd, ma_uint64 lengthInFrames); -MA_API void ma_fader_set_fade_ex(ma_fader* pFader, float volumeBeg, float volumeEnd, ma_uint64 lengthInFrames, ma_int64 startOffsetInFrames); -MA_API float ma_fader_get_current_volume(const ma_fader* pFader); - - - -/* Spatializer. */ -typedef struct -{ - float x; - float y; - float z; -} ma_vec3f; - -typedef struct -{ - ma_vec3f v; - ma_spinlock lock; -} ma_atomic_vec3f; - -typedef enum -{ - ma_attenuation_model_none, /* No distance attenuation and no spatialization. */ - ma_attenuation_model_inverse, /* Equivalent to OpenAL's AL_INVERSE_DISTANCE_CLAMPED. */ - ma_attenuation_model_linear, /* Linear attenuation. Equivalent to OpenAL's AL_LINEAR_DISTANCE_CLAMPED. */ - ma_attenuation_model_exponential /* Exponential attenuation. Equivalent to OpenAL's AL_EXPONENT_DISTANCE_CLAMPED. */ -} ma_attenuation_model; - -typedef enum -{ - ma_positioning_absolute, - ma_positioning_relative -} ma_positioning; - -typedef enum -{ - ma_handedness_right, - ma_handedness_left -} ma_handedness; - - -typedef struct -{ - ma_uint32 channelsOut; - ma_channel* pChannelMapOut; - ma_handedness handedness; /* Defaults to right. Forward is -1 on the Z axis. In a left handed system, forward is +1 on the Z axis. */ - float coneInnerAngleInRadians; - float coneOuterAngleInRadians; - float coneOuterGain; - float speedOfSound; - ma_vec3f worldUp; -} ma_spatializer_listener_config; - -MA_API ma_spatializer_listener_config ma_spatializer_listener_config_init(ma_uint32 channelsOut); - - -typedef struct -{ - ma_spatializer_listener_config config; - ma_atomic_vec3f position; /* The absolute position of the listener. */ - ma_atomic_vec3f direction; /* The direction the listener is facing. The world up vector is config.worldUp. */ - ma_atomic_vec3f velocity; - ma_bool32 isEnabled; - - /* Memory management. */ - ma_bool32 _ownsHeap; - void* _pHeap; -} ma_spatializer_listener; - -MA_API ma_result ma_spatializer_listener_get_heap_size(const ma_spatializer_listener_config* pConfig, size_t* pHeapSizeInBytes); -MA_API ma_result ma_spatializer_listener_init_preallocated(const ma_spatializer_listener_config* pConfig, void* pHeap, ma_spatializer_listener* pListener); -MA_API ma_result ma_spatializer_listener_init(const ma_spatializer_listener_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_spatializer_listener* pListener); -MA_API void ma_spatializer_listener_uninit(ma_spatializer_listener* pListener, const ma_allocation_callbacks* pAllocationCallbacks); -MA_API ma_channel* ma_spatializer_listener_get_channel_map(ma_spatializer_listener* pListener); -MA_API void ma_spatializer_listener_set_cone(ma_spatializer_listener* pListener, float innerAngleInRadians, float outerAngleInRadians, float outerGain); -MA_API void ma_spatializer_listener_get_cone(const ma_spatializer_listener* pListener, float* pInnerAngleInRadians, float* pOuterAngleInRadians, float* pOuterGain); -MA_API void ma_spatializer_listener_set_position(ma_spatializer_listener* pListener, float x, float y, float z); -MA_API ma_vec3f ma_spatializer_listener_get_position(const ma_spatializer_listener* pListener); -MA_API void ma_spatializer_listener_set_direction(ma_spatializer_listener* pListener, float x, float y, float z); -MA_API ma_vec3f ma_spatializer_listener_get_direction(const ma_spatializer_listener* pListener); -MA_API void ma_spatializer_listener_set_velocity(ma_spatializer_listener* pListener, float x, float y, float z); -MA_API ma_vec3f ma_spatializer_listener_get_velocity(const ma_spatializer_listener* pListener); -MA_API void ma_spatializer_listener_set_speed_of_sound(ma_spatializer_listener* pListener, float speedOfSound); -MA_API float ma_spatializer_listener_get_speed_of_sound(const ma_spatializer_listener* pListener); -MA_API void ma_spatializer_listener_set_world_up(ma_spatializer_listener* pListener, float x, float y, float z); -MA_API ma_vec3f ma_spatializer_listener_get_world_up(const ma_spatializer_listener* pListener); -MA_API void ma_spatializer_listener_set_enabled(ma_spatializer_listener* pListener, ma_bool32 isEnabled); -MA_API ma_bool32 ma_spatializer_listener_is_enabled(const ma_spatializer_listener* pListener); - - -typedef struct -{ - ma_uint32 channelsIn; - ma_uint32 channelsOut; - ma_channel* pChannelMapIn; - ma_attenuation_model attenuationModel; - ma_positioning positioning; - ma_handedness handedness; /* Defaults to right. Forward is -1 on the Z axis. In a left handed system, forward is +1 on the Z axis. */ - float minGain; - float maxGain; - float minDistance; - float maxDistance; - float rolloff; - float coneInnerAngleInRadians; - float coneOuterAngleInRadians; - float coneOuterGain; - float dopplerFactor; /* Set to 0 to disable doppler effect. */ - float directionalAttenuationFactor; /* Set to 0 to disable directional attenuation. */ - float minSpatializationChannelGain; /* The minimal scaling factor to apply to channel gains when accounting for the direction of the sound relative to the listener. Must be in the range of 0..1. Smaller values means more aggressive directional panning, larger values means more subtle directional panning. */ - ma_uint32 gainSmoothTimeInFrames; /* When the gain of a channel changes during spatialization, the transition will be linearly interpolated over this number of frames. */ -} ma_spatializer_config; - -MA_API ma_spatializer_config ma_spatializer_config_init(ma_uint32 channelsIn, ma_uint32 channelsOut); - - -typedef struct -{ - ma_uint32 channelsIn; - ma_uint32 channelsOut; - ma_channel* pChannelMapIn; - ma_attenuation_model attenuationModel; - ma_positioning positioning; - ma_handedness handedness; /* Defaults to right. Forward is -1 on the Z axis. In a left handed system, forward is +1 on the Z axis. */ - float minGain; - float maxGain; - float minDistance; - float maxDistance; - float rolloff; - float coneInnerAngleInRadians; - float coneOuterAngleInRadians; - float coneOuterGain; - float dopplerFactor; /* Set to 0 to disable doppler effect. */ - float directionalAttenuationFactor; /* Set to 0 to disable directional attenuation. */ - ma_uint32 gainSmoothTimeInFrames; /* When the gain of a channel changes during spatialization, the transition will be linearly interpolated over this number of frames. */ - ma_atomic_vec3f position; - ma_atomic_vec3f direction; - ma_atomic_vec3f velocity; /* For doppler effect. */ - float dopplerPitch; /* Will be updated by ma_spatializer_process_pcm_frames() and can be used by higher level functions to apply a pitch shift for doppler effect. */ - float minSpatializationChannelGain; - ma_gainer gainer; /* For smooth gain transitions. */ - float* pNewChannelGainsOut; /* An offset of _pHeap. Used by ma_spatializer_process_pcm_frames() to store new channel gains. The number of elements in this array is equal to config.channelsOut. */ - - /* Memory management. */ - void* _pHeap; - ma_bool32 _ownsHeap; -} ma_spatializer; - -MA_API ma_result ma_spatializer_get_heap_size(const ma_spatializer_config* pConfig, size_t* pHeapSizeInBytes); -MA_API ma_result ma_spatializer_init_preallocated(const ma_spatializer_config* pConfig, void* pHeap, ma_spatializer* pSpatializer); -MA_API ma_result ma_spatializer_init(const ma_spatializer_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_spatializer* pSpatializer); -MA_API void ma_spatializer_uninit(ma_spatializer* pSpatializer, const ma_allocation_callbacks* pAllocationCallbacks); -MA_API ma_result ma_spatializer_process_pcm_frames(ma_spatializer* pSpatializer, ma_spatializer_listener* pListener, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount); -MA_API ma_result ma_spatializer_set_master_volume(ma_spatializer* pSpatializer, float volume); -MA_API ma_result ma_spatializer_get_master_volume(const ma_spatializer* pSpatializer, float* pVolume); -MA_API ma_uint32 ma_spatializer_get_input_channels(const ma_spatializer* pSpatializer); -MA_API ma_uint32 ma_spatializer_get_output_channels(const ma_spatializer* pSpatializer); -MA_API void ma_spatializer_set_attenuation_model(ma_spatializer* pSpatializer, ma_attenuation_model attenuationModel); -MA_API ma_attenuation_model ma_spatializer_get_attenuation_model(const ma_spatializer* pSpatializer); -MA_API void ma_spatializer_set_positioning(ma_spatializer* pSpatializer, ma_positioning positioning); -MA_API ma_positioning ma_spatializer_get_positioning(const ma_spatializer* pSpatializer); -MA_API void ma_spatializer_set_rolloff(ma_spatializer* pSpatializer, float rolloff); -MA_API float ma_spatializer_get_rolloff(const ma_spatializer* pSpatializer); -MA_API void ma_spatializer_set_min_gain(ma_spatializer* pSpatializer, float minGain); -MA_API float ma_spatializer_get_min_gain(const ma_spatializer* pSpatializer); -MA_API void ma_spatializer_set_max_gain(ma_spatializer* pSpatializer, float maxGain); -MA_API float ma_spatializer_get_max_gain(const ma_spatializer* pSpatializer); -MA_API void ma_spatializer_set_min_distance(ma_spatializer* pSpatializer, float minDistance); -MA_API float ma_spatializer_get_min_distance(const ma_spatializer* pSpatializer); -MA_API void ma_spatializer_set_max_distance(ma_spatializer* pSpatializer, float maxDistance); -MA_API float ma_spatializer_get_max_distance(const ma_spatializer* pSpatializer); -MA_API void ma_spatializer_set_cone(ma_spatializer* pSpatializer, float innerAngleInRadians, float outerAngleInRadians, float outerGain); -MA_API void ma_spatializer_get_cone(const ma_spatializer* pSpatializer, float* pInnerAngleInRadians, float* pOuterAngleInRadians, float* pOuterGain); -MA_API void ma_spatializer_set_doppler_factor(ma_spatializer* pSpatializer, float dopplerFactor); -MA_API float ma_spatializer_get_doppler_factor(const ma_spatializer* pSpatializer); -MA_API void ma_spatializer_set_directional_attenuation_factor(ma_spatializer* pSpatializer, float directionalAttenuationFactor); -MA_API float ma_spatializer_get_directional_attenuation_factor(const ma_spatializer* pSpatializer); -MA_API void ma_spatializer_set_position(ma_spatializer* pSpatializer, float x, float y, float z); -MA_API ma_vec3f ma_spatializer_get_position(const ma_spatializer* pSpatializer); -MA_API void ma_spatializer_set_direction(ma_spatializer* pSpatializer, float x, float y, float z); -MA_API ma_vec3f ma_spatializer_get_direction(const ma_spatializer* pSpatializer); -MA_API void ma_spatializer_set_velocity(ma_spatializer* pSpatializer, float x, float y, float z); -MA_API ma_vec3f ma_spatializer_get_velocity(const ma_spatializer* pSpatializer); -MA_API void ma_spatializer_get_relative_position_and_direction(const ma_spatializer* pSpatializer, const ma_spatializer_listener* pListener, ma_vec3f* pRelativePos, ma_vec3f* pRelativeDir); - - - -/************************************************************************************************************************************************************ -************************************************************************************************************************************************************* - -DATA CONVERSION -=============== - -This section contains the APIs for data conversion. You will find everything here for channel mapping, sample format conversion, resampling, etc. - -************************************************************************************************************************************************************* -************************************************************************************************************************************************************/ - -/************************************************************************************************************************************************************** - -Resampling - -**************************************************************************************************************************************************************/ -typedef struct -{ - ma_format format; - ma_uint32 channels; - ma_uint32 sampleRateIn; - ma_uint32 sampleRateOut; - ma_uint32 lpfOrder; /* The low-pass filter order. Setting this to 0 will disable low-pass filtering. */ - double lpfNyquistFactor; /* 0..1. Defaults to 1. 1 = Half the sampling frequency (Nyquist Frequency), 0.5 = Quarter the sampling frequency (half Nyquest Frequency), etc. */ -} ma_linear_resampler_config; - -MA_API ma_linear_resampler_config ma_linear_resampler_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRateIn, ma_uint32 sampleRateOut); - -typedef struct -{ - ma_linear_resampler_config config; - ma_uint32 inAdvanceInt; - ma_uint32 inAdvanceFrac; - ma_uint32 inTimeInt; - ma_uint32 inTimeFrac; - union - { - float* f32; - ma_int16* s16; - } x0; /* The previous input frame. */ - union - { - float* f32; - ma_int16* s16; - } x1; /* The next input frame. */ - ma_lpf lpf; - - /* Memory management. */ - void* _pHeap; - ma_bool32 _ownsHeap; -} ma_linear_resampler; - -MA_API ma_result ma_linear_resampler_get_heap_size(const ma_linear_resampler_config* pConfig, size_t* pHeapSizeInBytes); -MA_API ma_result ma_linear_resampler_init_preallocated(const ma_linear_resampler_config* pConfig, void* pHeap, ma_linear_resampler* pResampler); -MA_API ma_result ma_linear_resampler_init(const ma_linear_resampler_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_linear_resampler* pResampler); -MA_API void ma_linear_resampler_uninit(ma_linear_resampler* pResampler, const ma_allocation_callbacks* pAllocationCallbacks); -MA_API ma_result ma_linear_resampler_process_pcm_frames(ma_linear_resampler* pResampler, const void* pFramesIn, ma_uint64* pFrameCountIn, void* pFramesOut, ma_uint64* pFrameCountOut); -MA_API ma_result ma_linear_resampler_set_rate(ma_linear_resampler* pResampler, ma_uint32 sampleRateIn, ma_uint32 sampleRateOut); -MA_API ma_result ma_linear_resampler_set_rate_ratio(ma_linear_resampler* pResampler, float ratioInOut); -MA_API ma_uint64 ma_linear_resampler_get_input_latency(const ma_linear_resampler* pResampler); -MA_API ma_uint64 ma_linear_resampler_get_output_latency(const ma_linear_resampler* pResampler); -MA_API ma_result ma_linear_resampler_get_required_input_frame_count(const ma_linear_resampler* pResampler, ma_uint64 outputFrameCount, ma_uint64* pInputFrameCount); -MA_API ma_result ma_linear_resampler_get_expected_output_frame_count(const ma_linear_resampler* pResampler, ma_uint64 inputFrameCount, ma_uint64* pOutputFrameCount); -MA_API ma_result ma_linear_resampler_reset(ma_linear_resampler* pResampler); - - -typedef struct ma_resampler_config ma_resampler_config; - -typedef void ma_resampling_backend; -typedef struct -{ - ma_result (* onGetHeapSize )(void* pUserData, const ma_resampler_config* pConfig, size_t* pHeapSizeInBytes); - ma_result (* onInit )(void* pUserData, const ma_resampler_config* pConfig, void* pHeap, ma_resampling_backend** ppBackend); - void (* onUninit )(void* pUserData, ma_resampling_backend* pBackend, const ma_allocation_callbacks* pAllocationCallbacks); - ma_result (* onProcess )(void* pUserData, ma_resampling_backend* pBackend, const void* pFramesIn, ma_uint64* pFrameCountIn, void* pFramesOut, ma_uint64* pFrameCountOut); - ma_result (* onSetRate )(void* pUserData, ma_resampling_backend* pBackend, ma_uint32 sampleRateIn, ma_uint32 sampleRateOut); /* Optional. Rate changes will be disabled. */ - ma_uint64 (* onGetInputLatency )(void* pUserData, const ma_resampling_backend* pBackend); /* Optional. Latency will be reported as 0. */ - ma_uint64 (* onGetOutputLatency )(void* pUserData, const ma_resampling_backend* pBackend); /* Optional. Latency will be reported as 0. */ - ma_result (* onGetRequiredInputFrameCount )(void* pUserData, const ma_resampling_backend* pBackend, ma_uint64 outputFrameCount, ma_uint64* pInputFrameCount); /* Optional. Latency mitigation will be disabled. */ - ma_result (* onGetExpectedOutputFrameCount)(void* pUserData, const ma_resampling_backend* pBackend, ma_uint64 inputFrameCount, ma_uint64* pOutputFrameCount); /* Optional. Latency mitigation will be disabled. */ - ma_result (* onReset )(void* pUserData, ma_resampling_backend* pBackend); -} ma_resampling_backend_vtable; - -typedef enum -{ - ma_resample_algorithm_linear = 0, /* Fastest, lowest quality. Optional low-pass filtering. Default. */ - ma_resample_algorithm_custom, -} ma_resample_algorithm; - -struct ma_resampler_config -{ - ma_format format; /* Must be either ma_format_f32 or ma_format_s16. */ - ma_uint32 channels; - ma_uint32 sampleRateIn; - ma_uint32 sampleRateOut; - ma_resample_algorithm algorithm; /* When set to ma_resample_algorithm_custom, pBackendVTable will be used. */ - ma_resampling_backend_vtable* pBackendVTable; - void* pBackendUserData; - struct - { - ma_uint32 lpfOrder; - } linear; -}; - -MA_API ma_resampler_config ma_resampler_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRateIn, ma_uint32 sampleRateOut, ma_resample_algorithm algorithm); - -typedef struct -{ - ma_resampling_backend* pBackend; - ma_resampling_backend_vtable* pBackendVTable; - void* pBackendUserData; - ma_format format; - ma_uint32 channels; - ma_uint32 sampleRateIn; - ma_uint32 sampleRateOut; - union - { - ma_linear_resampler linear; - } state; /* State for stock resamplers so we can avoid a malloc. For stock resamplers, pBackend will point here. */ - - /* Memory management. */ - void* _pHeap; - ma_bool32 _ownsHeap; -} ma_resampler; - -MA_API ma_result ma_resampler_get_heap_size(const ma_resampler_config* pConfig, size_t* pHeapSizeInBytes); -MA_API ma_result ma_resampler_init_preallocated(const ma_resampler_config* pConfig, void* pHeap, ma_resampler* pResampler); - -/* -Initializes a new resampler object from a config. -*/ -MA_API ma_result ma_resampler_init(const ma_resampler_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_resampler* pResampler); - -/* -Uninitializes a resampler. -*/ -MA_API void ma_resampler_uninit(ma_resampler* pResampler, const ma_allocation_callbacks* pAllocationCallbacks); - -/* -Converts the given input data. - -Both the input and output frames must be in the format specified in the config when the resampler was initialized. - -On input, [pFrameCountOut] contains the number of output frames to process. On output it contains the number of output frames that -were actually processed, which may be less than the requested amount which will happen if there's not enough input data. You can use -ma_resampler_get_expected_output_frame_count() to know how many output frames will be processed for a given number of input frames. - -On input, [pFrameCountIn] contains the number of input frames contained in [pFramesIn]. On output it contains the number of whole -input frames that were actually processed. You can use ma_resampler_get_required_input_frame_count() to know how many input frames -you should provide for a given number of output frames. [pFramesIn] can be NULL, in which case zeroes will be used instead. - -If [pFramesOut] is NULL, a seek is performed. In this case, if [pFrameCountOut] is not NULL it will seek by the specified number of -output frames. Otherwise, if [pFramesCountOut] is NULL and [pFrameCountIn] is not NULL, it will seek by the specified number of input -frames. When seeking, [pFramesIn] is allowed to NULL, in which case the internal timing state will be updated, but no input will be -processed. In this case, any internal filter state will be updated as if zeroes were passed in. - -It is an error for [pFramesOut] to be non-NULL and [pFrameCountOut] to be NULL. - -It is an error for both [pFrameCountOut] and [pFrameCountIn] to be NULL. -*/ -MA_API ma_result ma_resampler_process_pcm_frames(ma_resampler* pResampler, const void* pFramesIn, ma_uint64* pFrameCountIn, void* pFramesOut, ma_uint64* pFrameCountOut); - - -/* -Sets the input and output sample rate. -*/ -MA_API ma_result ma_resampler_set_rate(ma_resampler* pResampler, ma_uint32 sampleRateIn, ma_uint32 sampleRateOut); - -/* -Sets the input and output sample rate as a ratio. - -The ration is in/out. -*/ -MA_API ma_result ma_resampler_set_rate_ratio(ma_resampler* pResampler, float ratio); - -/* -Retrieves the latency introduced by the resampler in input frames. -*/ -MA_API ma_uint64 ma_resampler_get_input_latency(const ma_resampler* pResampler); - -/* -Retrieves the latency introduced by the resampler in output frames. -*/ -MA_API ma_uint64 ma_resampler_get_output_latency(const ma_resampler* pResampler); - -/* -Calculates the number of whole input frames that would need to be read from the client in order to output the specified -number of output frames. - -The returned value does not include cached input frames. It only returns the number of extra frames that would need to be -read from the input buffer in order to output the specified number of output frames. -*/ -MA_API ma_result ma_resampler_get_required_input_frame_count(const ma_resampler* pResampler, ma_uint64 outputFrameCount, ma_uint64* pInputFrameCount); - -/* -Calculates the number of whole output frames that would be output after fully reading and consuming the specified number of -input frames. -*/ -MA_API ma_result ma_resampler_get_expected_output_frame_count(const ma_resampler* pResampler, ma_uint64 inputFrameCount, ma_uint64* pOutputFrameCount); - -/* -Resets the resampler's timer and clears its internal cache. -*/ -MA_API ma_result ma_resampler_reset(ma_resampler* pResampler); - - -/************************************************************************************************************************************************************** - -Channel Conversion - -**************************************************************************************************************************************************************/ -typedef enum -{ - ma_channel_conversion_path_unknown, - ma_channel_conversion_path_passthrough, - ma_channel_conversion_path_mono_out, /* Converting to mono. */ - ma_channel_conversion_path_mono_in, /* Converting from mono. */ - ma_channel_conversion_path_shuffle, /* Simple shuffle. Will use this when all channels are present in both input and output channel maps, but just in a different order. */ - ma_channel_conversion_path_weights /* Blended based on weights. */ -} ma_channel_conversion_path; - -typedef enum -{ - ma_mono_expansion_mode_duplicate = 0, /* The default. */ - ma_mono_expansion_mode_average, /* Average the mono channel across all channels. */ - ma_mono_expansion_mode_stereo_only, /* Duplicate to the left and right channels only and ignore the others. */ - ma_mono_expansion_mode_default = ma_mono_expansion_mode_duplicate -} ma_mono_expansion_mode; - -typedef struct -{ - ma_format format; - ma_uint32 channelsIn; - ma_uint32 channelsOut; - const ma_channel* pChannelMapIn; - const ma_channel* pChannelMapOut; - ma_channel_mix_mode mixingMode; - ma_bool32 calculateLFEFromSpatialChannels; /* When an output LFE channel is present, but no input LFE, set to true to set the output LFE to the average of all spatial channels (LR, FR, etc.). Ignored when an input LFE is present. */ - float** ppWeights; /* [in][out]. Only used when mixingMode is set to ma_channel_mix_mode_custom_weights. */ -} ma_channel_converter_config; - -MA_API ma_channel_converter_config ma_channel_converter_config_init(ma_format format, ma_uint32 channelsIn, const ma_channel* pChannelMapIn, ma_uint32 channelsOut, const ma_channel* pChannelMapOut, ma_channel_mix_mode mixingMode); - -typedef struct -{ - ma_format format; - ma_uint32 channelsIn; - ma_uint32 channelsOut; - ma_channel_mix_mode mixingMode; - ma_channel_conversion_path conversionPath; - ma_channel* pChannelMapIn; - ma_channel* pChannelMapOut; - ma_uint8* pShuffleTable; /* Indexed by output channel index. */ - union - { - float** f32; - ma_int32** s16; - } weights; /* [in][out] */ - - /* Memory management. */ - void* _pHeap; - ma_bool32 _ownsHeap; -} ma_channel_converter; - -MA_API ma_result ma_channel_converter_get_heap_size(const ma_channel_converter_config* pConfig, size_t* pHeapSizeInBytes); -MA_API ma_result ma_channel_converter_init_preallocated(const ma_channel_converter_config* pConfig, void* pHeap, ma_channel_converter* pConverter); -MA_API ma_result ma_channel_converter_init(const ma_channel_converter_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_channel_converter* pConverter); -MA_API void ma_channel_converter_uninit(ma_channel_converter* pConverter, const ma_allocation_callbacks* pAllocationCallbacks); -MA_API ma_result ma_channel_converter_process_pcm_frames(ma_channel_converter* pConverter, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount); -MA_API ma_result ma_channel_converter_get_input_channel_map(const ma_channel_converter* pConverter, ma_channel* pChannelMap, size_t channelMapCap); -MA_API ma_result ma_channel_converter_get_output_channel_map(const ma_channel_converter* pConverter, ma_channel* pChannelMap, size_t channelMapCap); - - -/************************************************************************************************************************************************************** - -Data Conversion - -**************************************************************************************************************************************************************/ -typedef struct -{ - ma_format formatIn; - ma_format formatOut; - ma_uint32 channelsIn; - ma_uint32 channelsOut; - ma_uint32 sampleRateIn; - ma_uint32 sampleRateOut; - ma_channel* pChannelMapIn; - ma_channel* pChannelMapOut; - ma_dither_mode ditherMode; - ma_channel_mix_mode channelMixMode; - ma_bool32 calculateLFEFromSpatialChannels; /* When an output LFE channel is present, but no input LFE, set to true to set the output LFE to the average of all spatial channels (LR, FR, etc.). Ignored when an input LFE is present. */ - float** ppChannelWeights; /* [in][out]. Only used when mixingMode is set to ma_channel_mix_mode_custom_weights. */ - ma_bool32 allowDynamicSampleRate; - ma_resampler_config resampling; -} ma_data_converter_config; - -MA_API ma_data_converter_config ma_data_converter_config_init_default(void); -MA_API ma_data_converter_config ma_data_converter_config_init(ma_format formatIn, ma_format formatOut, ma_uint32 channelsIn, ma_uint32 channelsOut, ma_uint32 sampleRateIn, ma_uint32 sampleRateOut); - - -typedef enum -{ - ma_data_converter_execution_path_passthrough, /* No conversion. */ - ma_data_converter_execution_path_format_only, /* Only format conversion. */ - ma_data_converter_execution_path_channels_only, /* Only channel conversion. */ - ma_data_converter_execution_path_resample_only, /* Only resampling. */ - ma_data_converter_execution_path_resample_first, /* All conversions, but resample as the first step. */ - ma_data_converter_execution_path_channels_first /* All conversions, but channels as the first step. */ -} ma_data_converter_execution_path; - -typedef struct -{ - ma_format formatIn; - ma_format formatOut; - ma_uint32 channelsIn; - ma_uint32 channelsOut; - ma_uint32 sampleRateIn; - ma_uint32 sampleRateOut; - ma_dither_mode ditherMode; - ma_data_converter_execution_path executionPath; /* The execution path the data converter will follow when processing. */ - ma_channel_converter channelConverter; - ma_resampler resampler; - ma_bool8 hasPreFormatConversion; - ma_bool8 hasPostFormatConversion; - ma_bool8 hasChannelConverter; - ma_bool8 hasResampler; - ma_bool8 isPassthrough; - - /* Memory management. */ - ma_bool8 _ownsHeap; - void* _pHeap; -} ma_data_converter; - -MA_API ma_result ma_data_converter_get_heap_size(const ma_data_converter_config* pConfig, size_t* pHeapSizeInBytes); -MA_API ma_result ma_data_converter_init_preallocated(const ma_data_converter_config* pConfig, void* pHeap, ma_data_converter* pConverter); -MA_API ma_result ma_data_converter_init(const ma_data_converter_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_data_converter* pConverter); -MA_API void ma_data_converter_uninit(ma_data_converter* pConverter, const ma_allocation_callbacks* pAllocationCallbacks); -MA_API ma_result ma_data_converter_process_pcm_frames(ma_data_converter* pConverter, const void* pFramesIn, ma_uint64* pFrameCountIn, void* pFramesOut, ma_uint64* pFrameCountOut); -MA_API ma_result ma_data_converter_set_rate(ma_data_converter* pConverter, ma_uint32 sampleRateIn, ma_uint32 sampleRateOut); -MA_API ma_result ma_data_converter_set_rate_ratio(ma_data_converter* pConverter, float ratioInOut); -MA_API ma_uint64 ma_data_converter_get_input_latency(const ma_data_converter* pConverter); -MA_API ma_uint64 ma_data_converter_get_output_latency(const ma_data_converter* pConverter); -MA_API ma_result ma_data_converter_get_required_input_frame_count(const ma_data_converter* pConverter, ma_uint64 outputFrameCount, ma_uint64* pInputFrameCount); -MA_API ma_result ma_data_converter_get_expected_output_frame_count(const ma_data_converter* pConverter, ma_uint64 inputFrameCount, ma_uint64* pOutputFrameCount); -MA_API ma_result ma_data_converter_get_input_channel_map(const ma_data_converter* pConverter, ma_channel* pChannelMap, size_t channelMapCap); -MA_API ma_result ma_data_converter_get_output_channel_map(const ma_data_converter* pConverter, ma_channel* pChannelMap, size_t channelMapCap); -MA_API ma_result ma_data_converter_reset(ma_data_converter* pConverter); - - -/************************************************************************************************************************************************************ - -Format Conversion - -************************************************************************************************************************************************************/ -MA_API void ma_pcm_u8_to_s16(void* pOut, const void* pIn, ma_uint64 count, ma_dither_mode ditherMode); -MA_API void ma_pcm_u8_to_s24(void* pOut, const void* pIn, ma_uint64 count, ma_dither_mode ditherMode); -MA_API void ma_pcm_u8_to_s32(void* pOut, const void* pIn, ma_uint64 count, ma_dither_mode ditherMode); -MA_API void ma_pcm_u8_to_f32(void* pOut, const void* pIn, ma_uint64 count, ma_dither_mode ditherMode); -MA_API void ma_pcm_s16_to_u8(void* pOut, const void* pIn, ma_uint64 count, ma_dither_mode ditherMode); -MA_API void ma_pcm_s16_to_s24(void* pOut, const void* pIn, ma_uint64 count, ma_dither_mode ditherMode); -MA_API void ma_pcm_s16_to_s32(void* pOut, const void* pIn, ma_uint64 count, ma_dither_mode ditherMode); -MA_API void ma_pcm_s16_to_f32(void* pOut, const void* pIn, ma_uint64 count, ma_dither_mode ditherMode); -MA_API void ma_pcm_s24_to_u8(void* pOut, const void* pIn, ma_uint64 count, ma_dither_mode ditherMode); -MA_API void ma_pcm_s24_to_s16(void* pOut, const void* pIn, ma_uint64 count, ma_dither_mode ditherMode); -MA_API void ma_pcm_s24_to_s32(void* pOut, const void* pIn, ma_uint64 count, ma_dither_mode ditherMode); -MA_API void ma_pcm_s24_to_f32(void* pOut, const void* pIn, ma_uint64 count, ma_dither_mode ditherMode); -MA_API void ma_pcm_s32_to_u8(void* pOut, const void* pIn, ma_uint64 count, ma_dither_mode ditherMode); -MA_API void ma_pcm_s32_to_s16(void* pOut, const void* pIn, ma_uint64 count, ma_dither_mode ditherMode); -MA_API void ma_pcm_s32_to_s24(void* pOut, const void* pIn, ma_uint64 count, ma_dither_mode ditherMode); -MA_API void ma_pcm_s32_to_f32(void* pOut, const void* pIn, ma_uint64 count, ma_dither_mode ditherMode); -MA_API void ma_pcm_f32_to_u8(void* pOut, const void* pIn, ma_uint64 count, ma_dither_mode ditherMode); -MA_API void ma_pcm_f32_to_s16(void* pOut, const void* pIn, ma_uint64 count, ma_dither_mode ditherMode); -MA_API void ma_pcm_f32_to_s24(void* pOut, const void* pIn, ma_uint64 count, ma_dither_mode ditherMode); -MA_API void ma_pcm_f32_to_s32(void* pOut, const void* pIn, ma_uint64 count, ma_dither_mode ditherMode); -MA_API void ma_pcm_convert(void* pOut, ma_format formatOut, const void* pIn, ma_format formatIn, ma_uint64 sampleCount, ma_dither_mode ditherMode); -MA_API void ma_convert_pcm_frames_format(void* pOut, ma_format formatOut, const void* pIn, ma_format formatIn, ma_uint64 frameCount, ma_uint32 channels, ma_dither_mode ditherMode); - -/* -Deinterleaves an interleaved buffer. -*/ -MA_API void ma_deinterleave_pcm_frames(ma_format format, ma_uint32 channels, ma_uint64 frameCount, const void* pInterleavedPCMFrames, void** ppDeinterleavedPCMFrames); - -/* -Interleaves a group of deinterleaved buffers. -*/ -MA_API void ma_interleave_pcm_frames(ma_format format, ma_uint32 channels, ma_uint64 frameCount, const void** ppDeinterleavedPCMFrames, void* pInterleavedPCMFrames); - - -/************************************************************************************************************************************************************ - -Channel Maps - -************************************************************************************************************************************************************/ -/* -This is used in the shuffle table to indicate that the channel index is undefined and should be ignored. -*/ -#define MA_CHANNEL_INDEX_NULL 255 - -/* -Retrieves the channel position of the specified channel in the given channel map. - -The pChannelMap parameter can be null, in which case miniaudio's default channel map will be assumed. -*/ -MA_API ma_channel ma_channel_map_get_channel(const ma_channel* pChannelMap, ma_uint32 channelCount, ma_uint32 channelIndex); - -/* -Initializes a blank channel map. - -When a blank channel map is specified anywhere it indicates that the native channel map should be used. -*/ -MA_API void ma_channel_map_init_blank(ma_channel* pChannelMap, ma_uint32 channels); - -/* -Helper for retrieving a standard channel map. - -The output channel map buffer must have a capacity of at least `channelMapCap`. -*/ -MA_API void ma_channel_map_init_standard(ma_standard_channel_map standardChannelMap, ma_channel* pChannelMap, size_t channelMapCap, ma_uint32 channels); - -/* -Copies a channel map. - -Both input and output channel map buffers must have a capacity of at least `channels`. -*/ -MA_API void ma_channel_map_copy(ma_channel* pOut, const ma_channel* pIn, ma_uint32 channels); - -/* -Copies a channel map if one is specified, otherwise copies the default channel map. - -The output buffer must have a capacity of at least `channels`. If not NULL, the input channel map must also have a capacity of at least `channels`. -*/ -MA_API void ma_channel_map_copy_or_default(ma_channel* pOut, size_t channelMapCapOut, const ma_channel* pIn, ma_uint32 channels); - - -/* -Determines whether or not a channel map is valid. - -A blank channel map is valid (all channels set to MA_CHANNEL_NONE). The way a blank channel map is handled is context specific, but -is usually treated as a passthrough. - -Invalid channel maps: - - A channel map with no channels - - A channel map with more than one channel and a mono channel - -The channel map buffer must have a capacity of at least `channels`. -*/ -MA_API ma_bool32 ma_channel_map_is_valid(const ma_channel* pChannelMap, ma_uint32 channels); - -/* -Helper for comparing two channel maps for equality. - -This assumes the channel count is the same between the two. - -Both channels map buffers must have a capacity of at least `channels`. -*/ -MA_API ma_bool32 ma_channel_map_is_equal(const ma_channel* pChannelMapA, const ma_channel* pChannelMapB, ma_uint32 channels); - -/* -Helper for determining if a channel map is blank (all channels set to MA_CHANNEL_NONE). - -The channel map buffer must have a capacity of at least `channels`. -*/ -MA_API ma_bool32 ma_channel_map_is_blank(const ma_channel* pChannelMap, ma_uint32 channels); - -/* -Helper for determining whether or not a channel is present in the given channel map. - -The channel map buffer must have a capacity of at least `channels`. -*/ -MA_API ma_bool32 ma_channel_map_contains_channel_position(ma_uint32 channels, const ma_channel* pChannelMap, ma_channel channelPosition); - -/* -Find a channel position in the given channel map. Returns MA_TRUE if the channel is found; MA_FALSE otherwise. The -index of the channel is output to `pChannelIndex`. - -The channel map buffer must have a capacity of at least `channels`. -*/ -MA_API ma_bool32 ma_channel_map_find_channel_position(ma_uint32 channels, const ma_channel* pChannelMap, ma_channel channelPosition, ma_uint32* pChannelIndex); - -/* -Generates a string representing the given channel map. - -This is for printing and debugging purposes, not serialization/deserialization. - -Returns the length of the string, not including the null terminator. -*/ -MA_API size_t ma_channel_map_to_string(const ma_channel* pChannelMap, ma_uint32 channels, char* pBufferOut, size_t bufferCap); - -/* -Retrieves a human readable version of a channel position. -*/ -MA_API const char* ma_channel_position_to_string(ma_channel channel); - - -/************************************************************************************************************************************************************ - -Conversion Helpers - -************************************************************************************************************************************************************/ - -/* -High-level helper for doing a full format conversion in one go. Returns the number of output frames. Call this with pOut set to NULL to -determine the required size of the output buffer. frameCountOut should be set to the capacity of pOut. If pOut is NULL, frameCountOut is -ignored. - -A return value of 0 indicates an error. - -This function is useful for one-off bulk conversions, but if you're streaming data you should use the ma_data_converter APIs instead. -*/ -MA_API ma_uint64 ma_convert_frames(void* pOut, ma_uint64 frameCountOut, ma_format formatOut, ma_uint32 channelsOut, ma_uint32 sampleRateOut, const void* pIn, ma_uint64 frameCountIn, ma_format formatIn, ma_uint32 channelsIn, ma_uint32 sampleRateIn); -MA_API ma_uint64 ma_convert_frames_ex(void* pOut, ma_uint64 frameCountOut, const void* pIn, ma_uint64 frameCountIn, const ma_data_converter_config* pConfig); - - -/************************************************************************************************************************************************************ - -Data Source - -************************************************************************************************************************************************************/ -typedef void ma_data_source; - -#define MA_DATA_SOURCE_SELF_MANAGED_RANGE_AND_LOOP_POINT 0x00000001 - -typedef struct -{ - ma_result (* onRead)(ma_data_source* pDataSource, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead); - ma_result (* onSeek)(ma_data_source* pDataSource, ma_uint64 frameIndex); - ma_result (* onGetDataFormat)(ma_data_source* pDataSource, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate, ma_channel* pChannelMap, size_t channelMapCap); - ma_result (* onGetCursor)(ma_data_source* pDataSource, ma_uint64* pCursor); - ma_result (* onGetLength)(ma_data_source* pDataSource, ma_uint64* pLength); - ma_result (* onSetLooping)(ma_data_source* pDataSource, ma_bool32 isLooping); - ma_uint32 flags; -} ma_data_source_vtable; - -typedef ma_data_source* (* ma_data_source_get_next_proc)(ma_data_source* pDataSource); - -typedef struct -{ - const ma_data_source_vtable* vtable; -} ma_data_source_config; - -MA_API ma_data_source_config ma_data_source_config_init(void); - - -typedef struct -{ - const ma_data_source_vtable* vtable; - ma_uint64 rangeBegInFrames; - ma_uint64 rangeEndInFrames; /* Set to -1 for unranged (default). */ - ma_uint64 loopBegInFrames; /* Relative to rangeBegInFrames. */ - ma_uint64 loopEndInFrames; /* Relative to rangeBegInFrames. Set to -1 for the end of the range. */ - ma_data_source* pCurrent; /* When non-NULL, the data source being initialized will act as a proxy and will route all operations to pCurrent. Used in conjunction with pNext/onGetNext for seamless chaining. */ - ma_data_source* pNext; /* When set to NULL, onGetNext will be used. */ - ma_data_source_get_next_proc onGetNext; /* Will be used when pNext is NULL. If both are NULL, no next will be used. */ - MA_ATOMIC(4, ma_bool32) isLooping; -} ma_data_source_base; - -MA_API ma_result ma_data_source_init(const ma_data_source_config* pConfig, ma_data_source* pDataSource); -MA_API void ma_data_source_uninit(ma_data_source* pDataSource); -MA_API ma_result ma_data_source_read_pcm_frames(ma_data_source* pDataSource, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead); /* Must support pFramesOut = NULL in which case a forward seek should be performed. */ -MA_API ma_result ma_data_source_seek_pcm_frames(ma_data_source* pDataSource, ma_uint64 frameCount, ma_uint64* pFramesSeeked); /* Can only seek forward. Equivalent to ma_data_source_read_pcm_frames(pDataSource, NULL, frameCount, &framesRead); */ -MA_API ma_result ma_data_source_seek_to_pcm_frame(ma_data_source* pDataSource, ma_uint64 frameIndex); -MA_API ma_result ma_data_source_seek_seconds(ma_data_source* pDataSource, float secondCount, float* pSecondsSeeked); /* Can only seek forward. Abstraction to ma_data_source_seek_pcm_frames() */ -MA_API ma_result ma_data_source_seek_to_second(ma_data_source* pDataSource, float seekPointInSeconds); /* Abstraction to ma_data_source_seek_to_pcm_frame() */ -MA_API ma_result ma_data_source_get_data_format(ma_data_source* pDataSource, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate, ma_channel* pChannelMap, size_t channelMapCap); -MA_API ma_result ma_data_source_get_cursor_in_pcm_frames(ma_data_source* pDataSource, ma_uint64* pCursor); -MA_API ma_result ma_data_source_get_length_in_pcm_frames(ma_data_source* pDataSource, ma_uint64* pLength); /* Returns MA_NOT_IMPLEMENTED if the length is unknown or cannot be determined. Decoders can return this. */ -MA_API ma_result ma_data_source_get_cursor_in_seconds(ma_data_source* pDataSource, float* pCursor); -MA_API ma_result ma_data_source_get_length_in_seconds(ma_data_source* pDataSource, float* pLength); -MA_API ma_result ma_data_source_set_looping(ma_data_source* pDataSource, ma_bool32 isLooping); -MA_API ma_bool32 ma_data_source_is_looping(const ma_data_source* pDataSource); -MA_API ma_result ma_data_source_set_range_in_pcm_frames(ma_data_source* pDataSource, ma_uint64 rangeBegInFrames, ma_uint64 rangeEndInFrames); -MA_API void ma_data_source_get_range_in_pcm_frames(const ma_data_source* pDataSource, ma_uint64* pRangeBegInFrames, ma_uint64* pRangeEndInFrames); -MA_API ma_result ma_data_source_set_loop_point_in_pcm_frames(ma_data_source* pDataSource, ma_uint64 loopBegInFrames, ma_uint64 loopEndInFrames); -MA_API void ma_data_source_get_loop_point_in_pcm_frames(const ma_data_source* pDataSource, ma_uint64* pLoopBegInFrames, ma_uint64* pLoopEndInFrames); -MA_API ma_result ma_data_source_set_current(ma_data_source* pDataSource, ma_data_source* pCurrentDataSource); -MA_API ma_data_source* ma_data_source_get_current(const ma_data_source* pDataSource); -MA_API ma_result ma_data_source_set_next(ma_data_source* pDataSource, ma_data_source* pNextDataSource); -MA_API ma_data_source* ma_data_source_get_next(const ma_data_source* pDataSource); -MA_API ma_result ma_data_source_set_next_callback(ma_data_source* pDataSource, ma_data_source_get_next_proc onGetNext); -MA_API ma_data_source_get_next_proc ma_data_source_get_next_callback(const ma_data_source* pDataSource); - - -typedef struct -{ - ma_data_source_base ds; - ma_format format; - ma_uint32 channels; - ma_uint32 sampleRate; - ma_uint64 cursor; - ma_uint64 sizeInFrames; - const void* pData; -} ma_audio_buffer_ref; - -MA_API ma_result ma_audio_buffer_ref_init(ma_format format, ma_uint32 channels, const void* pData, ma_uint64 sizeInFrames, ma_audio_buffer_ref* pAudioBufferRef); -MA_API void ma_audio_buffer_ref_uninit(ma_audio_buffer_ref* pAudioBufferRef); -MA_API ma_result ma_audio_buffer_ref_set_data(ma_audio_buffer_ref* pAudioBufferRef, const void* pData, ma_uint64 sizeInFrames); -MA_API ma_uint64 ma_audio_buffer_ref_read_pcm_frames(ma_audio_buffer_ref* pAudioBufferRef, void* pFramesOut, ma_uint64 frameCount, ma_bool32 loop); -MA_API ma_result ma_audio_buffer_ref_seek_to_pcm_frame(ma_audio_buffer_ref* pAudioBufferRef, ma_uint64 frameIndex); -MA_API ma_result ma_audio_buffer_ref_map(ma_audio_buffer_ref* pAudioBufferRef, void** ppFramesOut, ma_uint64* pFrameCount); -MA_API ma_result ma_audio_buffer_ref_unmap(ma_audio_buffer_ref* pAudioBufferRef, ma_uint64 frameCount); /* Returns MA_AT_END if the end has been reached. This should be considered successful. */ -MA_API ma_bool32 ma_audio_buffer_ref_at_end(const ma_audio_buffer_ref* pAudioBufferRef); -MA_API ma_result ma_audio_buffer_ref_get_cursor_in_pcm_frames(const ma_audio_buffer_ref* pAudioBufferRef, ma_uint64* pCursor); -MA_API ma_result ma_audio_buffer_ref_get_length_in_pcm_frames(const ma_audio_buffer_ref* pAudioBufferRef, ma_uint64* pLength); -MA_API ma_result ma_audio_buffer_ref_get_available_frames(const ma_audio_buffer_ref* pAudioBufferRef, ma_uint64* pAvailableFrames); - - - -typedef struct -{ - ma_format format; - ma_uint32 channels; - ma_uint32 sampleRate; - ma_uint64 sizeInFrames; - const void* pData; /* If set to NULL, will allocate a block of memory for you. */ - ma_allocation_callbacks allocationCallbacks; -} ma_audio_buffer_config; - -MA_API ma_audio_buffer_config ma_audio_buffer_config_init(ma_format format, ma_uint32 channels, ma_uint64 sizeInFrames, const void* pData, const ma_allocation_callbacks* pAllocationCallbacks); - -typedef struct -{ - ma_audio_buffer_ref ref; - ma_allocation_callbacks allocationCallbacks; - ma_bool32 ownsData; /* Used to control whether or not miniaudio owns the data buffer. If set to true, pData will be freed in ma_audio_buffer_uninit(). */ - ma_uint8 _pExtraData[1]; /* For allocating a buffer with the memory located directly after the other memory of the structure. */ -} ma_audio_buffer; - -MA_API ma_result ma_audio_buffer_init(const ma_audio_buffer_config* pConfig, ma_audio_buffer* pAudioBuffer); -MA_API ma_result ma_audio_buffer_init_copy(const ma_audio_buffer_config* pConfig, ma_audio_buffer* pAudioBuffer); -MA_API ma_result ma_audio_buffer_alloc_and_init(const ma_audio_buffer_config* pConfig, ma_audio_buffer** ppAudioBuffer); /* Always copies the data. Doesn't make sense to use this otherwise. Use ma_audio_buffer_uninit_and_free() to uninit. */ -MA_API void ma_audio_buffer_uninit(ma_audio_buffer* pAudioBuffer); -MA_API void ma_audio_buffer_uninit_and_free(ma_audio_buffer* pAudioBuffer); -MA_API ma_uint64 ma_audio_buffer_read_pcm_frames(ma_audio_buffer* pAudioBuffer, void* pFramesOut, ma_uint64 frameCount, ma_bool32 loop); -MA_API ma_result ma_audio_buffer_seek_to_pcm_frame(ma_audio_buffer* pAudioBuffer, ma_uint64 frameIndex); -MA_API ma_result ma_audio_buffer_map(ma_audio_buffer* pAudioBuffer, void** ppFramesOut, ma_uint64* pFrameCount); -MA_API ma_result ma_audio_buffer_unmap(ma_audio_buffer* pAudioBuffer, ma_uint64 frameCount); /* Returns MA_AT_END if the end has been reached. This should be considered successful. */ -MA_API ma_bool32 ma_audio_buffer_at_end(const ma_audio_buffer* pAudioBuffer); -MA_API ma_result ma_audio_buffer_get_cursor_in_pcm_frames(const ma_audio_buffer* pAudioBuffer, ma_uint64* pCursor); -MA_API ma_result ma_audio_buffer_get_length_in_pcm_frames(const ma_audio_buffer* pAudioBuffer, ma_uint64* pLength); -MA_API ma_result ma_audio_buffer_get_available_frames(const ma_audio_buffer* pAudioBuffer, ma_uint64* pAvailableFrames); - - -/* -Paged Audio Buffer -================== -A paged audio buffer is made up of a linked list of pages. It's expandable, but not shrinkable. It -can be used for cases where audio data is streamed in asynchronously while allowing data to be read -at the same time. - -This is lock-free, but not 100% thread safe. You can append a page and read from the buffer across -simultaneously across different threads, however only one thread at a time can append, and only one -thread at a time can read and seek. -*/ -typedef struct ma_paged_audio_buffer_page ma_paged_audio_buffer_page; -struct ma_paged_audio_buffer_page -{ - MA_ATOMIC(MA_SIZEOF_PTR, ma_paged_audio_buffer_page*) pNext; - ma_uint64 sizeInFrames; - ma_uint8 pAudioData[1]; -}; - -typedef struct -{ - ma_format format; - ma_uint32 channels; - ma_paged_audio_buffer_page head; /* Dummy head for the lock-free algorithm. Always has a size of 0. */ - MA_ATOMIC(MA_SIZEOF_PTR, ma_paged_audio_buffer_page*) pTail; /* Never null. Initially set to &head. */ -} ma_paged_audio_buffer_data; - -MA_API ma_result ma_paged_audio_buffer_data_init(ma_format format, ma_uint32 channels, ma_paged_audio_buffer_data* pData); -MA_API void ma_paged_audio_buffer_data_uninit(ma_paged_audio_buffer_data* pData, const ma_allocation_callbacks* pAllocationCallbacks); -MA_API ma_paged_audio_buffer_page* ma_paged_audio_buffer_data_get_head(ma_paged_audio_buffer_data* pData); -MA_API ma_paged_audio_buffer_page* ma_paged_audio_buffer_data_get_tail(ma_paged_audio_buffer_data* pData); -MA_API ma_result ma_paged_audio_buffer_data_get_length_in_pcm_frames(ma_paged_audio_buffer_data* pData, ma_uint64* pLength); -MA_API ma_result ma_paged_audio_buffer_data_allocate_page(ma_paged_audio_buffer_data* pData, ma_uint64 pageSizeInFrames, const void* pInitialData, const ma_allocation_callbacks* pAllocationCallbacks, ma_paged_audio_buffer_page** ppPage); -MA_API ma_result ma_paged_audio_buffer_data_free_page(ma_paged_audio_buffer_data* pData, ma_paged_audio_buffer_page* pPage, const ma_allocation_callbacks* pAllocationCallbacks); -MA_API ma_result ma_paged_audio_buffer_data_append_page(ma_paged_audio_buffer_data* pData, ma_paged_audio_buffer_page* pPage); -MA_API ma_result ma_paged_audio_buffer_data_allocate_and_append_page(ma_paged_audio_buffer_data* pData, ma_uint32 pageSizeInFrames, const void* pInitialData, const ma_allocation_callbacks* pAllocationCallbacks); - - -typedef struct -{ - ma_paged_audio_buffer_data* pData; /* Must not be null. */ -} ma_paged_audio_buffer_config; - -MA_API ma_paged_audio_buffer_config ma_paged_audio_buffer_config_init(ma_paged_audio_buffer_data* pData); - - -typedef struct -{ - ma_data_source_base ds; - ma_paged_audio_buffer_data* pData; /* Audio data is read from here. Cannot be null. */ - ma_paged_audio_buffer_page* pCurrent; - ma_uint64 relativeCursor; /* Relative to the current page. */ - ma_uint64 absoluteCursor; -} ma_paged_audio_buffer; - -MA_API ma_result ma_paged_audio_buffer_init(const ma_paged_audio_buffer_config* pConfig, ma_paged_audio_buffer* pPagedAudioBuffer); -MA_API void ma_paged_audio_buffer_uninit(ma_paged_audio_buffer* pPagedAudioBuffer); -MA_API ma_result ma_paged_audio_buffer_read_pcm_frames(ma_paged_audio_buffer* pPagedAudioBuffer, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead); /* Returns MA_AT_END if no more pages available. */ -MA_API ma_result ma_paged_audio_buffer_seek_to_pcm_frame(ma_paged_audio_buffer* pPagedAudioBuffer, ma_uint64 frameIndex); -MA_API ma_result ma_paged_audio_buffer_get_cursor_in_pcm_frames(ma_paged_audio_buffer* pPagedAudioBuffer, ma_uint64* pCursor); -MA_API ma_result ma_paged_audio_buffer_get_length_in_pcm_frames(ma_paged_audio_buffer* pPagedAudioBuffer, ma_uint64* pLength); - - - -/************************************************************************************************************************************************************ - -Ring Buffer - -************************************************************************************************************************************************************/ -typedef struct -{ - void* pBuffer; - ma_uint32 subbufferSizeInBytes; - ma_uint32 subbufferCount; - ma_uint32 subbufferStrideInBytes; - MA_ATOMIC(4, ma_uint32) encodedReadOffset; /* Most significant bit is the loop flag. Lower 31 bits contains the actual offset in bytes. Must be used atomically. */ - MA_ATOMIC(4, ma_uint32) encodedWriteOffset; /* Most significant bit is the loop flag. Lower 31 bits contains the actual offset in bytes. Must be used atomically. */ - ma_bool8 ownsBuffer; /* Used to know whether or not miniaudio is responsible for free()-ing the buffer. */ - ma_bool8 clearOnWriteAcquire; /* When set, clears the acquired write buffer before returning from ma_rb_acquire_write(). */ - ma_allocation_callbacks allocationCallbacks; -} ma_rb; - -MA_API ma_result ma_rb_init_ex(size_t subbufferSizeInBytes, size_t subbufferCount, size_t subbufferStrideInBytes, void* pOptionalPreallocatedBuffer, const ma_allocation_callbacks* pAllocationCallbacks, ma_rb* pRB); -MA_API ma_result ma_rb_init(size_t bufferSizeInBytes, void* pOptionalPreallocatedBuffer, const ma_allocation_callbacks* pAllocationCallbacks, ma_rb* pRB); -MA_API void ma_rb_uninit(ma_rb* pRB); -MA_API void ma_rb_reset(ma_rb* pRB); -MA_API ma_result ma_rb_acquire_read(ma_rb* pRB, size_t* pSizeInBytes, void** ppBufferOut); -MA_API ma_result ma_rb_commit_read(ma_rb* pRB, size_t sizeInBytes); -MA_API ma_result ma_rb_acquire_write(ma_rb* pRB, size_t* pSizeInBytes, void** ppBufferOut); -MA_API ma_result ma_rb_commit_write(ma_rb* pRB, size_t sizeInBytes); -MA_API ma_result ma_rb_seek_read(ma_rb* pRB, size_t offsetInBytes); -MA_API ma_result ma_rb_seek_write(ma_rb* pRB, size_t offsetInBytes); -MA_API ma_int32 ma_rb_pointer_distance(ma_rb* pRB); /* Returns the distance between the write pointer and the read pointer. Should never be negative for a correct program. Will return the number of bytes that can be read before the read pointer hits the write pointer. */ -MA_API ma_uint32 ma_rb_available_read(ma_rb* pRB); -MA_API ma_uint32 ma_rb_available_write(ma_rb* pRB); -MA_API size_t ma_rb_get_subbuffer_size(ma_rb* pRB); -MA_API size_t ma_rb_get_subbuffer_stride(ma_rb* pRB); -MA_API size_t ma_rb_get_subbuffer_offset(ma_rb* pRB, size_t subbufferIndex); -MA_API void* ma_rb_get_subbuffer_ptr(ma_rb* pRB, size_t subbufferIndex, void* pBuffer); - - -typedef struct -{ - ma_data_source_base ds; - ma_rb rb; - ma_format format; - ma_uint32 channels; - ma_uint32 sampleRate; /* Not required for the ring buffer itself, but useful for associating the data with some sample rate, particularly for data sources. */ -} ma_pcm_rb; - -MA_API ma_result ma_pcm_rb_init_ex(ma_format format, ma_uint32 channels, ma_uint32 subbufferSizeInFrames, ma_uint32 subbufferCount, ma_uint32 subbufferStrideInFrames, void* pOptionalPreallocatedBuffer, const ma_allocation_callbacks* pAllocationCallbacks, ma_pcm_rb* pRB); -MA_API ma_result ma_pcm_rb_init(ma_format format, ma_uint32 channels, ma_uint32 bufferSizeInFrames, void* pOptionalPreallocatedBuffer, const ma_allocation_callbacks* pAllocationCallbacks, ma_pcm_rb* pRB); -MA_API void ma_pcm_rb_uninit(ma_pcm_rb* pRB); -MA_API void ma_pcm_rb_reset(ma_pcm_rb* pRB); -MA_API ma_result ma_pcm_rb_acquire_read(ma_pcm_rb* pRB, ma_uint32* pSizeInFrames, void** ppBufferOut); -MA_API ma_result ma_pcm_rb_commit_read(ma_pcm_rb* pRB, ma_uint32 sizeInFrames); -MA_API ma_result ma_pcm_rb_acquire_write(ma_pcm_rb* pRB, ma_uint32* pSizeInFrames, void** ppBufferOut); -MA_API ma_result ma_pcm_rb_commit_write(ma_pcm_rb* pRB, ma_uint32 sizeInFrames); -MA_API ma_result ma_pcm_rb_seek_read(ma_pcm_rb* pRB, ma_uint32 offsetInFrames); -MA_API ma_result ma_pcm_rb_seek_write(ma_pcm_rb* pRB, ma_uint32 offsetInFrames); -MA_API ma_int32 ma_pcm_rb_pointer_distance(ma_pcm_rb* pRB); /* Return value is in frames. */ -MA_API ma_uint32 ma_pcm_rb_available_read(ma_pcm_rb* pRB); -MA_API ma_uint32 ma_pcm_rb_available_write(ma_pcm_rb* pRB); -MA_API ma_uint32 ma_pcm_rb_get_subbuffer_size(ma_pcm_rb* pRB); -MA_API ma_uint32 ma_pcm_rb_get_subbuffer_stride(ma_pcm_rb* pRB); -MA_API ma_uint32 ma_pcm_rb_get_subbuffer_offset(ma_pcm_rb* pRB, ma_uint32 subbufferIndex); -MA_API void* ma_pcm_rb_get_subbuffer_ptr(ma_pcm_rb* pRB, ma_uint32 subbufferIndex, void* pBuffer); -MA_API ma_format ma_pcm_rb_get_format(const ma_pcm_rb* pRB); -MA_API ma_uint32 ma_pcm_rb_get_channels(const ma_pcm_rb* pRB); -MA_API ma_uint32 ma_pcm_rb_get_sample_rate(const ma_pcm_rb* pRB); -MA_API void ma_pcm_rb_set_sample_rate(ma_pcm_rb* pRB, ma_uint32 sampleRate); - - -/* -The idea of the duplex ring buffer is to act as the intermediary buffer when running two asynchronous devices in a duplex set up. The -capture device writes to it, and then a playback device reads from it. - -At the moment this is just a simple naive implementation, but in the future I want to implement some dynamic resampling to seamlessly -handle desyncs. Note that the API is work in progress and may change at any time in any version. - -The size of the buffer is based on the capture side since that's what'll be written to the buffer. It is based on the capture period size -in frames. The internal sample rate of the capture device is also needed in order to calculate the size. -*/ -typedef struct -{ - ma_pcm_rb rb; -} ma_duplex_rb; - -MA_API ma_result ma_duplex_rb_init(ma_format captureFormat, ma_uint32 captureChannels, ma_uint32 sampleRate, ma_uint32 captureInternalSampleRate, ma_uint32 captureInternalPeriodSizeInFrames, const ma_allocation_callbacks* pAllocationCallbacks, ma_duplex_rb* pRB); -MA_API ma_result ma_duplex_rb_uninit(ma_duplex_rb* pRB); - - -/************************************************************************************************************************************************************ - -Miscellaneous Helpers - -************************************************************************************************************************************************************/ -/* -Retrieves a human readable description of the given result code. -*/ -MA_API const char* ma_result_description(ma_result result); - -/* -malloc() -*/ -MA_API void* ma_malloc(size_t sz, const ma_allocation_callbacks* pAllocationCallbacks); - -/* -calloc() -*/ -MA_API void* ma_calloc(size_t sz, const ma_allocation_callbacks* pAllocationCallbacks); - -/* -realloc() -*/ -MA_API void* ma_realloc(void* p, size_t sz, const ma_allocation_callbacks* pAllocationCallbacks); - -/* -free() -*/ -MA_API void ma_free(void* p, const ma_allocation_callbacks* pAllocationCallbacks); - -/* -Performs an aligned malloc, with the assumption that the alignment is a power of 2. -*/ -MA_API void* ma_aligned_malloc(size_t sz, size_t alignment, const ma_allocation_callbacks* pAllocationCallbacks); - -/* -Free's an aligned malloc'd buffer. -*/ -MA_API void ma_aligned_free(void* p, const ma_allocation_callbacks* pAllocationCallbacks); - -/* -Retrieves a friendly name for a format. -*/ -MA_API const char* ma_get_format_name(ma_format format); - -/* -Blends two frames in floating point format. -*/ -MA_API void ma_blend_f32(float* pOut, float* pInA, float* pInB, float factor, ma_uint32 channels); - -/* -Retrieves the size of a sample in bytes for the given format. - -This API is efficient and is implemented using a lookup table. - -Thread Safety: SAFE - This API is pure. -*/ -MA_API ma_uint32 ma_get_bytes_per_sample(ma_format format); -static MA_INLINE ma_uint32 ma_get_bytes_per_frame(ma_format format, ma_uint32 channels) { return ma_get_bytes_per_sample(format) * channels; } - -/* -Converts a log level to a string. -*/ -MA_API const char* ma_log_level_to_string(ma_uint32 logLevel); - - - - -/************************************************************************************************************************************************************ - -Synchronization - -************************************************************************************************************************************************************/ -/* -Locks a spinlock. -*/ -MA_API ma_result ma_spinlock_lock(volatile ma_spinlock* pSpinlock); - -/* -Locks a spinlock, but does not yield() when looping. -*/ -MA_API ma_result ma_spinlock_lock_noyield(volatile ma_spinlock* pSpinlock); - -/* -Unlocks a spinlock. -*/ -MA_API ma_result ma_spinlock_unlock(volatile ma_spinlock* pSpinlock); - - -#ifndef MA_NO_THREADING - -/* -Creates a mutex. - -A mutex must be created from a valid context. A mutex is initially unlocked. -*/ -MA_API ma_result ma_mutex_init(ma_mutex* pMutex); - -/* -Deletes a mutex. -*/ -MA_API void ma_mutex_uninit(ma_mutex* pMutex); - -/* -Locks a mutex with an infinite timeout. -*/ -MA_API void ma_mutex_lock(ma_mutex* pMutex); - -/* -Unlocks a mutex. -*/ -MA_API void ma_mutex_unlock(ma_mutex* pMutex); - - -/* -Initializes an auto-reset event. -*/ -MA_API ma_result ma_event_init(ma_event* pEvent); - -/* -Uninitializes an auto-reset event. -*/ -MA_API void ma_event_uninit(ma_event* pEvent); - -/* -Waits for the specified auto-reset event to become signalled. -*/ -MA_API ma_result ma_event_wait(ma_event* pEvent); - -/* -Signals the specified auto-reset event. -*/ -MA_API ma_result ma_event_signal(ma_event* pEvent); - - -MA_API ma_result ma_semaphore_init(int initialValue, ma_semaphore* pSemaphore); -MA_API void ma_semaphore_uninit(ma_semaphore* pSemaphore); -MA_API ma_result ma_semaphore_wait(ma_semaphore* pSemaphore); -MA_API ma_result ma_semaphore_release(ma_semaphore* pSemaphore); -#endif /* MA_NO_THREADING */ - - -/* -Fence -===== -This locks while the counter is larger than 0. Counter can be incremented and decremented by any -thread, but care needs to be taken when waiting. It is possible for one thread to acquire the -fence just as another thread returns from ma_fence_wait(). - -The idea behind a fence is to allow you to wait for a group of operations to complete. When an -operation starts, the counter is incremented which locks the fence. When the operation completes, -the fence will be released which decrements the counter. ma_fence_wait() will block until the -counter hits zero. - -If threading is disabled, ma_fence_wait() will spin on the counter. -*/ -typedef struct -{ -#ifndef MA_NO_THREADING - ma_event e; -#endif - ma_uint32 counter; -} ma_fence; - -MA_API ma_result ma_fence_init(ma_fence* pFence); -MA_API void ma_fence_uninit(ma_fence* pFence); -MA_API ma_result ma_fence_acquire(ma_fence* pFence); /* Increment counter. */ -MA_API ma_result ma_fence_release(ma_fence* pFence); /* Decrement counter. */ -MA_API ma_result ma_fence_wait(ma_fence* pFence); /* Wait for counter to reach 0. */ - - - -/* -Notification callback for asynchronous operations. -*/ -typedef void ma_async_notification; - -typedef struct -{ - void (* onSignal)(ma_async_notification* pNotification); -} ma_async_notification_callbacks; - -MA_API ma_result ma_async_notification_signal(ma_async_notification* pNotification); - - -/* -Simple polling notification. - -This just sets a variable when the notification has been signalled which is then polled with ma_async_notification_poll_is_signalled() -*/ -typedef struct -{ - ma_async_notification_callbacks cb; - ma_bool32 signalled; -} ma_async_notification_poll; - -MA_API ma_result ma_async_notification_poll_init(ma_async_notification_poll* pNotificationPoll); -MA_API ma_bool32 ma_async_notification_poll_is_signalled(const ma_async_notification_poll* pNotificationPoll); - - -/* -Event Notification - -This uses an ma_event. If threading is disabled (MA_NO_THREADING), initialization will fail. -*/ -typedef struct -{ - ma_async_notification_callbacks cb; -#ifndef MA_NO_THREADING - ma_event e; -#endif -} ma_async_notification_event; - -MA_API ma_result ma_async_notification_event_init(ma_async_notification_event* pNotificationEvent); -MA_API ma_result ma_async_notification_event_uninit(ma_async_notification_event* pNotificationEvent); -MA_API ma_result ma_async_notification_event_wait(ma_async_notification_event* pNotificationEvent); -MA_API ma_result ma_async_notification_event_signal(ma_async_notification_event* pNotificationEvent); - - - - -/************************************************************************************************************************************************************ - -Job Queue - -************************************************************************************************************************************************************/ - -/* -Slot Allocator --------------- -The idea of the slot allocator is for it to be used in conjunction with a fixed sized buffer. You use the slot allocator to allocate an index that can be used -as the insertion point for an object. - -Slots are reference counted to help mitigate the ABA problem in the lock-free queue we use for tracking jobs. - -The slot index is stored in the low 32 bits. The reference counter is stored in the high 32 bits: - - +-----------------+-----------------+ - | 32 Bits | 32 Bits | - +-----------------+-----------------+ - | Reference Count | Slot Index | - +-----------------+-----------------+ -*/ -typedef struct -{ - ma_uint32 capacity; /* The number of slots to make available. */ -} ma_slot_allocator_config; - -MA_API ma_slot_allocator_config ma_slot_allocator_config_init(ma_uint32 capacity); - - -typedef struct -{ - MA_ATOMIC(4, ma_uint32) bitfield; /* Must be used atomically because the allocation and freeing routines need to make copies of this which must never be optimized away by the compiler. */ -} ma_slot_allocator_group; - -typedef struct -{ - ma_slot_allocator_group* pGroups; /* Slots are grouped in chunks of 32. */ - ma_uint32* pSlots; /* 32 bits for reference counting for ABA mitigation. */ - ma_uint32 count; /* Allocation count. */ - ma_uint32 capacity; - - /* Memory management. */ - ma_bool32 _ownsHeap; - void* _pHeap; -} ma_slot_allocator; - -MA_API ma_result ma_slot_allocator_get_heap_size(const ma_slot_allocator_config* pConfig, size_t* pHeapSizeInBytes); -MA_API ma_result ma_slot_allocator_init_preallocated(const ma_slot_allocator_config* pConfig, void* pHeap, ma_slot_allocator* pAllocator); -MA_API ma_result ma_slot_allocator_init(const ma_slot_allocator_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_slot_allocator* pAllocator); -MA_API void ma_slot_allocator_uninit(ma_slot_allocator* pAllocator, const ma_allocation_callbacks* pAllocationCallbacks); -MA_API ma_result ma_slot_allocator_alloc(ma_slot_allocator* pAllocator, ma_uint64* pSlot); -MA_API ma_result ma_slot_allocator_free(ma_slot_allocator* pAllocator, ma_uint64 slot); - - -typedef struct ma_job ma_job; - -/* -Callback for processing a job. Each job type will have their own processing callback which will be -called by ma_job_process(). -*/ -typedef ma_result (* ma_job_proc)(ma_job* pJob); - -/* When a job type is added here an callback needs to be added go "g_jobVTable" in the implementation section. */ -typedef enum -{ - /* Miscellaneous. */ - MA_JOB_TYPE_QUIT = 0, - MA_JOB_TYPE_CUSTOM, - - /* Resource Manager. */ - MA_JOB_TYPE_RESOURCE_MANAGER_LOAD_DATA_BUFFER_NODE, - MA_JOB_TYPE_RESOURCE_MANAGER_FREE_DATA_BUFFER_NODE, - MA_JOB_TYPE_RESOURCE_MANAGER_PAGE_DATA_BUFFER_NODE, - MA_JOB_TYPE_RESOURCE_MANAGER_LOAD_DATA_BUFFER, - MA_JOB_TYPE_RESOURCE_MANAGER_FREE_DATA_BUFFER, - MA_JOB_TYPE_RESOURCE_MANAGER_LOAD_DATA_STREAM, - MA_JOB_TYPE_RESOURCE_MANAGER_FREE_DATA_STREAM, - MA_JOB_TYPE_RESOURCE_MANAGER_PAGE_DATA_STREAM, - MA_JOB_TYPE_RESOURCE_MANAGER_SEEK_DATA_STREAM, - - /* Device. */ - MA_JOB_TYPE_DEVICE_AAUDIO_REROUTE, - - /* Count. Must always be last. */ - MA_JOB_TYPE_COUNT -} ma_job_type; - -struct ma_job -{ - union - { - struct - { - ma_uint16 code; /* Job type. */ - ma_uint16 slot; /* Index into a ma_slot_allocator. */ - ma_uint32 refcount; - } breakup; - ma_uint64 allocation; - } toc; /* 8 bytes. We encode the job code into the slot allocation data to save space. */ - MA_ATOMIC(8, ma_uint64) next; /* refcount + slot for the next item. Does not include the job code. */ - ma_uint32 order; /* Execution order. Used to create a data dependency and ensure a job is executed in order. Usage is contextual depending on the job type. */ - - union - { - /* Miscellaneous. */ - struct - { - ma_job_proc proc; - ma_uintptr data0; - ma_uintptr data1; - } custom; - - /* Resource Manager */ - union - { - struct - { - /*ma_resource_manager**/ void* pResourceManager; - /*ma_resource_manager_data_buffer_node**/ void* pDataBufferNode; - char* pFilePath; - wchar_t* pFilePathW; - ma_uint32 flags; /* Resource manager data source flags that were used when initializing the data buffer. */ - ma_async_notification* pInitNotification; /* Signalled when the data buffer has been initialized and the format/channels/rate can be retrieved. */ - ma_async_notification* pDoneNotification; /* Signalled when the data buffer has been fully decoded. Will be passed through to MA_JOB_TYPE_RESOURCE_MANAGER_PAGE_DATA_BUFFER_NODE when decoding. */ - ma_fence* pInitFence; /* Released when initialization of the decoder is complete. */ - ma_fence* pDoneFence; /* Released if initialization of the decoder fails. Passed through to PAGE_DATA_BUFFER_NODE untouched if init is successful. */ - } loadDataBufferNode; - struct - { - /*ma_resource_manager**/ void* pResourceManager; - /*ma_resource_manager_data_buffer_node**/ void* pDataBufferNode; - ma_async_notification* pDoneNotification; - ma_fence* pDoneFence; - } freeDataBufferNode; - struct - { - /*ma_resource_manager**/ void* pResourceManager; - /*ma_resource_manager_data_buffer_node**/ void* pDataBufferNode; - /*ma_decoder**/ void* pDecoder; - ma_async_notification* pDoneNotification; /* Signalled when the data buffer has been fully decoded. */ - ma_fence* pDoneFence; /* Passed through from LOAD_DATA_BUFFER_NODE and released when the data buffer completes decoding or an error occurs. */ - } pageDataBufferNode; - - struct - { - /*ma_resource_manager_data_buffer**/ void* pDataBuffer; - ma_async_notification* pInitNotification; /* Signalled when the data buffer has been initialized and the format/channels/rate can be retrieved. */ - ma_async_notification* pDoneNotification; /* Signalled when the data buffer has been fully decoded. */ - ma_fence* pInitFence; /* Released when the data buffer has been initialized and the format/channels/rate can be retrieved. */ - ma_fence* pDoneFence; /* Released when the data buffer has been fully decoded. */ - ma_uint64 rangeBegInPCMFrames; - ma_uint64 rangeEndInPCMFrames; - ma_uint64 loopPointBegInPCMFrames; - ma_uint64 loopPointEndInPCMFrames; - ma_uint32 isLooping; - } loadDataBuffer; - struct - { - /*ma_resource_manager_data_buffer**/ void* pDataBuffer; - ma_async_notification* pDoneNotification; - ma_fence* pDoneFence; - } freeDataBuffer; - - struct - { - /*ma_resource_manager_data_stream**/ void* pDataStream; - char* pFilePath; /* Allocated when the job is posted, freed by the job thread after loading. */ - wchar_t* pFilePathW; /* ^ As above ^. Only used if pFilePath is NULL. */ - ma_uint64 initialSeekPoint; - ma_async_notification* pInitNotification; /* Signalled after the first two pages have been decoded and frames can be read from the stream. */ - ma_fence* pInitFence; - } loadDataStream; - struct - { - /*ma_resource_manager_data_stream**/ void* pDataStream; - ma_async_notification* pDoneNotification; - ma_fence* pDoneFence; - } freeDataStream; - struct - { - /*ma_resource_manager_data_stream**/ void* pDataStream; - ma_uint32 pageIndex; /* The index of the page to decode into. */ - } pageDataStream; - struct - { - /*ma_resource_manager_data_stream**/ void* pDataStream; - ma_uint64 frameIndex; - } seekDataStream; - } resourceManager; - - /* Device. */ - union - { - union - { - struct - { - /*ma_device**/ void* pDevice; - /*ma_device_type*/ ma_uint32 deviceType; - } reroute; - } aaudio; - } device; - } data; -}; - -MA_API ma_job ma_job_init(ma_uint16 code); -MA_API ma_result ma_job_process(ma_job* pJob); - - -/* -When set, ma_job_queue_next() will not wait and no semaphore will be signaled in -ma_job_queue_post(). ma_job_queue_next() will return MA_NO_DATA_AVAILABLE if nothing is available. - -This flag should always be used for platforms that do not support multithreading. -*/ -typedef enum -{ - MA_JOB_QUEUE_FLAG_NON_BLOCKING = 0x00000001 -} ma_job_queue_flags; - -typedef struct -{ - ma_uint32 flags; - ma_uint32 capacity; /* The maximum number of jobs that can fit in the queue at a time. */ -} ma_job_queue_config; - -MA_API ma_job_queue_config ma_job_queue_config_init(ma_uint32 flags, ma_uint32 capacity); - - -typedef struct -{ - ma_uint32 flags; /* Flags passed in at initialization time. */ - ma_uint32 capacity; /* The maximum number of jobs that can fit in the queue at a time. Set by the config. */ - MA_ATOMIC(8, ma_uint64) head; /* The first item in the list. Required for removing from the top of the list. */ - MA_ATOMIC(8, ma_uint64) tail; /* The last item in the list. Required for appending to the end of the list. */ -#ifndef MA_NO_THREADING - ma_semaphore sem; /* Only used when MA_JOB_QUEUE_FLAG_NON_BLOCKING is unset. */ -#endif - ma_slot_allocator allocator; - ma_job* pJobs; -#ifndef MA_USE_EXPERIMENTAL_LOCK_FREE_JOB_QUEUE - ma_spinlock lock; -#endif - - /* Memory management. */ - void* _pHeap; - ma_bool32 _ownsHeap; -} ma_job_queue; - -MA_API ma_result ma_job_queue_get_heap_size(const ma_job_queue_config* pConfig, size_t* pHeapSizeInBytes); -MA_API ma_result ma_job_queue_init_preallocated(const ma_job_queue_config* pConfig, void* pHeap, ma_job_queue* pQueue); -MA_API ma_result ma_job_queue_init(const ma_job_queue_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_job_queue* pQueue); -MA_API void ma_job_queue_uninit(ma_job_queue* pQueue, const ma_allocation_callbacks* pAllocationCallbacks); -MA_API ma_result ma_job_queue_post(ma_job_queue* pQueue, const ma_job* pJob); -MA_API ma_result ma_job_queue_next(ma_job_queue* pQueue, ma_job* pJob); /* Returns MA_CANCELLED if the next job is a quit job. */ - - - -/************************************************************************************************************************************************************ -************************************************************************************************************************************************************* - -DEVICE I/O -========== - -This section contains the APIs for device playback and capture. Here is where you'll find ma_device_init(), etc. - -************************************************************************************************************************************************************* -************************************************************************************************************************************************************/ -#ifndef MA_NO_DEVICE_IO -/* Some backends are only supported on certain platforms. */ -#if defined(MA_WIN32) - #define MA_SUPPORT_WASAPI - - #if defined(MA_WIN32_DESKTOP) /* DirectSound and WinMM backends are only supported on desktops. */ - #define MA_SUPPORT_DSOUND - #define MA_SUPPORT_WINMM - - /* Don't enable JACK here if compiling with Cosmopolitan. It'll be enabled in the Linux section below. */ - #if !defined(__COSMOPOLITAN__) - #define MA_SUPPORT_JACK /* JACK is technically supported on Windows, but I don't know how many people use it in practice... */ - #endif - #endif -#endif -#if defined(MA_UNIX) && !defined(MA_ORBIS) && !defined(MA_PROSPERO) - #if defined(MA_LINUX) - #if !defined(MA_ANDROID) && !defined(__COSMOPOLITAN__) /* ALSA is not supported on Android. */ - #define MA_SUPPORT_ALSA - #endif - #endif - #if !defined(MA_BSD) && !defined(MA_ANDROID) && !defined(MA_EMSCRIPTEN) - #define MA_SUPPORT_PULSEAUDIO - #define MA_SUPPORT_JACK - #endif - #if defined(__OpenBSD__) /* <-- Change this to "#if defined(MA_BSD)" to enable sndio on all BSD flavors. */ - #define MA_SUPPORT_SNDIO /* sndio is only supported on OpenBSD for now. May be expanded later if there's demand. */ - #endif - #if defined(__NetBSD__) || defined(__OpenBSD__) - #define MA_SUPPORT_AUDIO4 /* Only support audio(4) on platforms with known support. */ - #endif - #if defined(__FreeBSD__) || defined(__DragonFly__) - #define MA_SUPPORT_OSS /* Only support OSS on specific platforms with known support. */ - #endif -#endif -#if defined(MA_ANDROID) - #define MA_SUPPORT_AAUDIO - #define MA_SUPPORT_OPENSL -#endif -#if defined(MA_APPLE) - #define MA_SUPPORT_COREAUDIO -#endif -#if defined(MA_EMSCRIPTEN) - #define MA_SUPPORT_WEBAUDIO -#endif - -/* All platforms should support custom backends. */ -#define MA_SUPPORT_CUSTOM - -/* Explicitly disable the Null backend for Emscripten because it uses a background thread which is not properly supported right now. */ -#if !defined(MA_EMSCRIPTEN) -#define MA_SUPPORT_NULL -#endif - - -#if defined(MA_SUPPORT_WASAPI) && !defined(MA_NO_WASAPI) && (!defined(MA_ENABLE_ONLY_SPECIFIC_BACKENDS) || defined(MA_ENABLE_WASAPI)) - #define MA_HAS_WASAPI -#endif -#if defined(MA_SUPPORT_DSOUND) && !defined(MA_NO_DSOUND) && (!defined(MA_ENABLE_ONLY_SPECIFIC_BACKENDS) || defined(MA_ENABLE_DSOUND)) - #define MA_HAS_DSOUND -#endif -#if defined(MA_SUPPORT_WINMM) && !defined(MA_NO_WINMM) && (!defined(MA_ENABLE_ONLY_SPECIFIC_BACKENDS) || defined(MA_ENABLE_WINMM)) - #define MA_HAS_WINMM -#endif -#if defined(MA_SUPPORT_ALSA) && !defined(MA_NO_ALSA) && (!defined(MA_ENABLE_ONLY_SPECIFIC_BACKENDS) || defined(MA_ENABLE_ALSA)) - #define MA_HAS_ALSA -#endif -#if defined(MA_SUPPORT_PULSEAUDIO) && !defined(MA_NO_PULSEAUDIO) && (!defined(MA_ENABLE_ONLY_SPECIFIC_BACKENDS) || defined(MA_ENABLE_PULSEAUDIO)) - #define MA_HAS_PULSEAUDIO -#endif -#if defined(MA_SUPPORT_JACK) && !defined(MA_NO_JACK) && (!defined(MA_ENABLE_ONLY_SPECIFIC_BACKENDS) || defined(MA_ENABLE_JACK)) - #define MA_HAS_JACK -#endif -#if defined(MA_SUPPORT_COREAUDIO) && !defined(MA_NO_COREAUDIO) && (!defined(MA_ENABLE_ONLY_SPECIFIC_BACKENDS) || defined(MA_ENABLE_COREAUDIO)) - #define MA_HAS_COREAUDIO -#endif -#if defined(MA_SUPPORT_SNDIO) && !defined(MA_NO_SNDIO) && (!defined(MA_ENABLE_ONLY_SPECIFIC_BACKENDS) || defined(MA_ENABLE_SNDIO)) - #define MA_HAS_SNDIO -#endif -#if defined(MA_SUPPORT_AUDIO4) && !defined(MA_NO_AUDIO4) && (!defined(MA_ENABLE_ONLY_SPECIFIC_BACKENDS) || defined(MA_ENABLE_AUDIO4)) - #define MA_HAS_AUDIO4 -#endif -#if defined(MA_SUPPORT_OSS) && !defined(MA_NO_OSS) && (!defined(MA_ENABLE_ONLY_SPECIFIC_BACKENDS) || defined(MA_ENABLE_OSS)) - #define MA_HAS_OSS -#endif -#if defined(MA_SUPPORT_AAUDIO) && !defined(MA_NO_AAUDIO) && (!defined(MA_ENABLE_ONLY_SPECIFIC_BACKENDS) || defined(MA_ENABLE_AAUDIO)) - #define MA_HAS_AAUDIO -#endif -#if defined(MA_SUPPORT_OPENSL) && !defined(MA_NO_OPENSL) && (!defined(MA_ENABLE_ONLY_SPECIFIC_BACKENDS) || defined(MA_ENABLE_OPENSL)) - #define MA_HAS_OPENSL -#endif -#if defined(MA_SUPPORT_WEBAUDIO) && !defined(MA_NO_WEBAUDIO) && (!defined(MA_ENABLE_ONLY_SPECIFIC_BACKENDS) || defined(MA_ENABLE_WEBAUDIO)) - #define MA_HAS_WEBAUDIO -#endif -#if defined(MA_SUPPORT_CUSTOM) && !defined(MA_NO_CUSTOM) && (!defined(MA_ENABLE_ONLY_SPECIFIC_BACKENDS) || defined(MA_ENABLE_CUSTOM)) - #define MA_HAS_CUSTOM -#endif -#if defined(MA_SUPPORT_NULL) && !defined(MA_NO_NULL) && (!defined(MA_ENABLE_ONLY_SPECIFIC_BACKENDS) || defined(MA_ENABLE_NULL)) - #define MA_HAS_NULL -#endif - -typedef enum -{ - ma_device_state_uninitialized = 0, - ma_device_state_stopped = 1, /* The device's default state after initialization. */ - ma_device_state_started = 2, /* The device is started and is requesting and/or delivering audio data. */ - ma_device_state_starting = 3, /* Transitioning from a stopped state to started. */ - ma_device_state_stopping = 4 /* Transitioning from a started state to stopped. */ -} ma_device_state; - -MA_ATOMIC_SAFE_TYPE_DECL(i32, 4, device_state) - - -#ifdef MA_SUPPORT_WASAPI -/* We need a IMMNotificationClient object for WASAPI. */ -typedef struct -{ - void* lpVtbl; - ma_uint32 counter; - ma_device* pDevice; -} ma_IMMNotificationClient; -#endif - -/* Backend enums must be in priority order. */ -typedef enum -{ - ma_backend_wasapi, - ma_backend_dsound, - ma_backend_winmm, - ma_backend_coreaudio, - ma_backend_sndio, - ma_backend_audio4, - ma_backend_oss, - ma_backend_pulseaudio, - ma_backend_alsa, - ma_backend_jack, - ma_backend_aaudio, - ma_backend_opensl, - ma_backend_webaudio, - ma_backend_custom, /* <-- Custom backend, with callbacks defined by the context config. */ - ma_backend_null /* <-- Must always be the last item. Lowest priority, and used as the terminator for backend enumeration. */ -} ma_backend; - -#define MA_BACKEND_COUNT (ma_backend_null+1) - - -/* -Device job thread. This is used by backends that require asynchronous processing of certain -operations. It is not used by all backends. - -The device job thread is made up of a thread and a job queue. You can post a job to the thread with -ma_device_job_thread_post(). The thread will do the processing of the job. -*/ -typedef struct -{ - ma_bool32 noThread; /* Set this to true if you want to process jobs yourself. */ - ma_uint32 jobQueueCapacity; - ma_uint32 jobQueueFlags; -} ma_device_job_thread_config; - -MA_API ma_device_job_thread_config ma_device_job_thread_config_init(void); - -typedef struct -{ - ma_thread thread; - ma_job_queue jobQueue; - ma_bool32 _hasThread; -} ma_device_job_thread; - -MA_API ma_result ma_device_job_thread_init(const ma_device_job_thread_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_device_job_thread* pJobThread); -MA_API void ma_device_job_thread_uninit(ma_device_job_thread* pJobThread, const ma_allocation_callbacks* pAllocationCallbacks); -MA_API ma_result ma_device_job_thread_post(ma_device_job_thread* pJobThread, const ma_job* pJob); -MA_API ma_result ma_device_job_thread_next(ma_device_job_thread* pJobThread, ma_job* pJob); - - - -/* Device notification types. */ -typedef enum -{ - ma_device_notification_type_started, - ma_device_notification_type_stopped, - ma_device_notification_type_rerouted, - ma_device_notification_type_interruption_began, - ma_device_notification_type_interruption_ended, - ma_device_notification_type_unlocked -} ma_device_notification_type; - -typedef struct -{ - ma_device* pDevice; - ma_device_notification_type type; - union - { - struct - { - int _unused; - } started; - struct - { - int _unused; - } stopped; - struct - { - int _unused; - } rerouted; - struct - { - int _unused; - } interruption; - } data; -} ma_device_notification; - -/* -The notification callback for when the application should be notified of a change to the device. - -This callback is used for notifying the application of changes such as when the device has started, -stopped, rerouted or an interruption has occurred. Note that not all backends will post all -notification types. For example, some backends will perform automatic stream routing without any -kind of notification to the host program which means miniaudio will never know about it and will -never be able to fire the rerouted notification. You should keep this in mind when designing your -program. - -The stopped notification will *not* get fired when a device is rerouted. - - -Parameters ----------- -pNotification (in) - A pointer to a structure containing information about the event. Use the `pDevice` member of - this object to retrieve the relevant device. The `type` member can be used to discriminate - against each of the notification types. - - -Remarks -------- -Do not restart or uninitialize the device from the callback. - -Not all notifications will be triggered by all backends, however the started and stopped events -should be reliable for all backends. Some backends do not have a good way to detect device -stoppages due to unplugging the device which may result in the stopped callback not getting -fired. This has been observed with at least one BSD variant. - -The rerouted notification is fired *after* the reroute has occurred. The stopped notification will -*not* get fired when a device is rerouted. The following backends are known to do automatic stream -rerouting, but do not have a way to be notified of the change: - - * DirectSound - -The interruption notifications are used on mobile platforms for detecting when audio is interrupted -due to things like an incoming phone call. Currently this is only implemented on iOS. None of the -Android backends will report this notification. -*/ -typedef void (* ma_device_notification_proc)(const ma_device_notification* pNotification); - - -/* -The callback for processing audio data from the device. - -The data callback is fired by miniaudio whenever the device needs to have more data delivered to a playback device, or when a capture device has some data -available. This is called as soon as the backend asks for more data which means it may be called with inconsistent frame counts. You cannot assume the -callback will be fired with a consistent frame count. - - -Parameters ----------- -pDevice (in) - A pointer to the relevant device. - -pOutput (out) - A pointer to the output buffer that will receive audio data that will later be played back through the speakers. This will be non-null for a playback or - full-duplex device and null for a capture and loopback device. - -pInput (in) - A pointer to the buffer containing input data from a recording device. This will be non-null for a capture, full-duplex or loopback device and null for a - playback device. - -frameCount (in) - The number of PCM frames to process. Note that this will not necessarily be equal to what you requested when you initialized the device. The - `periodSizeInFrames` and `periodSizeInMilliseconds` members of the device config are just hints, and are not necessarily exactly what you'll get. You must - not assume this will always be the same value each time the callback is fired. - - -Remarks -------- -You cannot stop and start the device from inside the callback or else you'll get a deadlock. You must also not uninitialize the device from inside the -callback. The following APIs cannot be called from inside the callback: - - ma_device_init() - ma_device_init_ex() - ma_device_uninit() - ma_device_start() - ma_device_stop() - -The proper way to stop the device is to call `ma_device_stop()` from a different thread, normally the main application thread. -*/ -typedef void (* ma_device_data_proc)(ma_device* pDevice, void* pOutput, const void* pInput, ma_uint32 frameCount); - - - - -/* -DEPRECATED. Use ma_device_notification_proc instead. - -The callback for when the device has been stopped. - -This will be called when the device is stopped explicitly with `ma_device_stop()` and also called implicitly when the device is stopped through external forces -such as being unplugged or an internal error occurring. - - -Parameters ----------- -pDevice (in) - A pointer to the device that has just stopped. - - -Remarks -------- -Do not restart or uninitialize the device from the callback. -*/ -typedef void (* ma_stop_proc)(ma_device* pDevice); /* DEPRECATED. Use ma_device_notification_proc instead. */ - -typedef enum -{ - ma_device_type_playback = 1, - ma_device_type_capture = 2, - ma_device_type_duplex = ma_device_type_playback | ma_device_type_capture, /* 3 */ - ma_device_type_loopback = 4 -} ma_device_type; - -typedef enum -{ - ma_share_mode_shared = 0, - ma_share_mode_exclusive -} ma_share_mode; - -/* iOS/tvOS/watchOS session categories. */ -typedef enum -{ - ma_ios_session_category_default = 0, /* AVAudioSessionCategoryPlayAndRecord. */ - ma_ios_session_category_none, /* Leave the session category unchanged. */ - ma_ios_session_category_ambient, /* AVAudioSessionCategoryAmbient */ - ma_ios_session_category_solo_ambient, /* AVAudioSessionCategorySoloAmbient */ - ma_ios_session_category_playback, /* AVAudioSessionCategoryPlayback */ - ma_ios_session_category_record, /* AVAudioSessionCategoryRecord */ - ma_ios_session_category_play_and_record, /* AVAudioSessionCategoryPlayAndRecord */ - ma_ios_session_category_multi_route /* AVAudioSessionCategoryMultiRoute */ -} ma_ios_session_category; - -/* iOS/tvOS/watchOS session category options */ -typedef enum -{ - ma_ios_session_category_option_mix_with_others = 0x01, /* AVAudioSessionCategoryOptionMixWithOthers */ - ma_ios_session_category_option_duck_others = 0x02, /* AVAudioSessionCategoryOptionDuckOthers */ - ma_ios_session_category_option_allow_bluetooth = 0x04, /* AVAudioSessionCategoryOptionAllowBluetooth */ - ma_ios_session_category_option_default_to_speaker = 0x08, /* AVAudioSessionCategoryOptionDefaultToSpeaker */ - ma_ios_session_category_option_interrupt_spoken_audio_and_mix_with_others = 0x11, /* AVAudioSessionCategoryOptionInterruptSpokenAudioAndMixWithOthers */ - ma_ios_session_category_option_allow_bluetooth_a2dp = 0x20, /* AVAudioSessionCategoryOptionAllowBluetoothA2DP */ - ma_ios_session_category_option_allow_air_play = 0x40, /* AVAudioSessionCategoryOptionAllowAirPlay */ -} ma_ios_session_category_option; - -/* OpenSL stream types. */ -typedef enum -{ - ma_opensl_stream_type_default = 0, /* Leaves the stream type unset. */ - ma_opensl_stream_type_voice, /* SL_ANDROID_STREAM_VOICE */ - ma_opensl_stream_type_system, /* SL_ANDROID_STREAM_SYSTEM */ - ma_opensl_stream_type_ring, /* SL_ANDROID_STREAM_RING */ - ma_opensl_stream_type_media, /* SL_ANDROID_STREAM_MEDIA */ - ma_opensl_stream_type_alarm, /* SL_ANDROID_STREAM_ALARM */ - ma_opensl_stream_type_notification /* SL_ANDROID_STREAM_NOTIFICATION */ -} ma_opensl_stream_type; - -/* OpenSL recording presets. */ -typedef enum -{ - ma_opensl_recording_preset_default = 0, /* Leaves the input preset unset. */ - ma_opensl_recording_preset_generic, /* SL_ANDROID_RECORDING_PRESET_GENERIC */ - ma_opensl_recording_preset_camcorder, /* SL_ANDROID_RECORDING_PRESET_CAMCORDER */ - ma_opensl_recording_preset_voice_recognition, /* SL_ANDROID_RECORDING_PRESET_VOICE_RECOGNITION */ - ma_opensl_recording_preset_voice_communication, /* SL_ANDROID_RECORDING_PRESET_VOICE_COMMUNICATION */ - ma_opensl_recording_preset_voice_unprocessed /* SL_ANDROID_RECORDING_PRESET_UNPROCESSED */ -} ma_opensl_recording_preset; - -/* WASAPI audio thread priority characteristics. */ -typedef enum -{ - ma_wasapi_usage_default = 0, - ma_wasapi_usage_games, - ma_wasapi_usage_pro_audio, -} ma_wasapi_usage; - -/* AAudio usage types. */ -typedef enum -{ - ma_aaudio_usage_default = 0, /* Leaves the usage type unset. */ - ma_aaudio_usage_media, /* AAUDIO_USAGE_MEDIA */ - ma_aaudio_usage_voice_communication, /* AAUDIO_USAGE_VOICE_COMMUNICATION */ - ma_aaudio_usage_voice_communication_signalling, /* AAUDIO_USAGE_VOICE_COMMUNICATION_SIGNALLING */ - ma_aaudio_usage_alarm, /* AAUDIO_USAGE_ALARM */ - ma_aaudio_usage_notification, /* AAUDIO_USAGE_NOTIFICATION */ - ma_aaudio_usage_notification_ringtone, /* AAUDIO_USAGE_NOTIFICATION_RINGTONE */ - ma_aaudio_usage_notification_event, /* AAUDIO_USAGE_NOTIFICATION_EVENT */ - ma_aaudio_usage_assistance_accessibility, /* AAUDIO_USAGE_ASSISTANCE_ACCESSIBILITY */ - ma_aaudio_usage_assistance_navigation_guidance, /* AAUDIO_USAGE_ASSISTANCE_NAVIGATION_GUIDANCE */ - ma_aaudio_usage_assistance_sonification, /* AAUDIO_USAGE_ASSISTANCE_SONIFICATION */ - ma_aaudio_usage_game, /* AAUDIO_USAGE_GAME */ - ma_aaudio_usage_assitant, /* AAUDIO_USAGE_ASSISTANT */ - ma_aaudio_usage_emergency, /* AAUDIO_SYSTEM_USAGE_EMERGENCY */ - ma_aaudio_usage_safety, /* AAUDIO_SYSTEM_USAGE_SAFETY */ - ma_aaudio_usage_vehicle_status, /* AAUDIO_SYSTEM_USAGE_VEHICLE_STATUS */ - ma_aaudio_usage_announcement /* AAUDIO_SYSTEM_USAGE_ANNOUNCEMENT */ -} ma_aaudio_usage; - -/* AAudio content types. */ -typedef enum -{ - ma_aaudio_content_type_default = 0, /* Leaves the content type unset. */ - ma_aaudio_content_type_speech, /* AAUDIO_CONTENT_TYPE_SPEECH */ - ma_aaudio_content_type_music, /* AAUDIO_CONTENT_TYPE_MUSIC */ - ma_aaudio_content_type_movie, /* AAUDIO_CONTENT_TYPE_MOVIE */ - ma_aaudio_content_type_sonification /* AAUDIO_CONTENT_TYPE_SONIFICATION */ -} ma_aaudio_content_type; - -/* AAudio input presets. */ -typedef enum -{ - ma_aaudio_input_preset_default = 0, /* Leaves the input preset unset. */ - ma_aaudio_input_preset_generic, /* AAUDIO_INPUT_PRESET_GENERIC */ - ma_aaudio_input_preset_camcorder, /* AAUDIO_INPUT_PRESET_CAMCORDER */ - ma_aaudio_input_preset_voice_recognition, /* AAUDIO_INPUT_PRESET_VOICE_RECOGNITION */ - ma_aaudio_input_preset_voice_communication, /* AAUDIO_INPUT_PRESET_VOICE_COMMUNICATION */ - ma_aaudio_input_preset_unprocessed, /* AAUDIO_INPUT_PRESET_UNPROCESSED */ - ma_aaudio_input_preset_voice_performance /* AAUDIO_INPUT_PRESET_VOICE_PERFORMANCE */ -} ma_aaudio_input_preset; - -typedef enum -{ - ma_aaudio_allow_capture_default = 0, /* Leaves the allowed capture policy unset. */ - ma_aaudio_allow_capture_by_all, /* AAUDIO_ALLOW_CAPTURE_BY_ALL */ - ma_aaudio_allow_capture_by_system, /* AAUDIO_ALLOW_CAPTURE_BY_SYSTEM */ - ma_aaudio_allow_capture_by_none /* AAUDIO_ALLOW_CAPTURE_BY_NONE */ -} ma_aaudio_allowed_capture_policy; - -typedef union -{ - ma_int64 counter; - double counterD; -} ma_timer; - -typedef union -{ - ma_wchar_win32 wasapi[64]; /* WASAPI uses a wchar_t string for identification. */ - ma_uint8 dsound[16]; /* DirectSound uses a GUID for identification. */ - /*UINT_PTR*/ ma_uint32 winmm; /* When creating a device, WinMM expects a Win32 UINT_PTR for device identification. In practice it's actually just a UINT. */ - char alsa[256]; /* ALSA uses a name string for identification. */ - char pulse[256]; /* PulseAudio uses a name string for identification. */ - int jack; /* JACK always uses default devices. */ - char coreaudio[256]; /* Core Audio uses a string for identification. */ - char sndio[256]; /* "snd/0", etc. */ - char audio4[256]; /* "/dev/audio", etc. */ - char oss[64]; /* "dev/dsp0", etc. "dev/dsp" for the default device. */ - ma_int32 aaudio; /* AAudio uses a 32-bit integer for identification. */ - ma_uint32 opensl; /* OpenSL|ES uses a 32-bit unsigned integer for identification. */ - char webaudio[32]; /* Web Audio always uses default devices for now, but if this changes it'll be a GUID. */ - union - { - int i; - char s[256]; - void* p; - } custom; /* The custom backend could be anything. Give them a few options. */ - int nullbackend; /* The null backend uses an integer for device IDs. */ -} ma_device_id; - -MA_API ma_bool32 ma_device_id_equal(const ma_device_id* pA, const ma_device_id* pB); - - -typedef struct ma_context_config ma_context_config; -typedef struct ma_device_config ma_device_config; -typedef struct ma_backend_callbacks ma_backend_callbacks; - -#define MA_DATA_FORMAT_FLAG_EXCLUSIVE_MODE (1U << 1) /* If set, this is supported in exclusive mode. Otherwise not natively supported by exclusive mode. */ - -#ifndef MA_MAX_DEVICE_NAME_LENGTH -#define MA_MAX_DEVICE_NAME_LENGTH 255 -#endif - -typedef struct -{ - /* Basic info. This is the only information guaranteed to be filled in during device enumeration. */ - ma_device_id id; - char name[MA_MAX_DEVICE_NAME_LENGTH + 1]; /* +1 for null terminator. */ - ma_bool32 isDefault; - - ma_uint32 nativeDataFormatCount; - struct - { - ma_format format; /* Sample format. If set to ma_format_unknown, all sample formats are supported. */ - ma_uint32 channels; /* If set to 0, all channels are supported. */ - ma_uint32 sampleRate; /* If set to 0, all sample rates are supported. */ - ma_uint32 flags; /* A combination of MA_DATA_FORMAT_FLAG_* flags. */ - } nativeDataFormats[/*ma_format_count * ma_standard_sample_rate_count * MA_MAX_CHANNELS*/ 64]; /* Not sure how big to make this. There can be *many* permutations for virtual devices which can support anything. */ -} ma_device_info; - -struct ma_device_config -{ - ma_device_type deviceType; - ma_uint32 sampleRate; - ma_uint32 periodSizeInFrames; - ma_uint32 periodSizeInMilliseconds; - ma_uint32 periods; - ma_performance_profile performanceProfile; - ma_bool8 noPreSilencedOutputBuffer; /* When set to true, the contents of the output buffer passed into the data callback will be left undefined rather than initialized to silence. */ - ma_bool8 noClip; /* When set to true, the contents of the output buffer passed into the data callback will not be clipped after returning. Only applies when the playback sample format is f32. */ - ma_bool8 noDisableDenormals; /* Do not disable denormals when firing the data callback. */ - ma_bool8 noFixedSizedCallback; /* Disables strict fixed-sized data callbacks. Setting this to true will result in the period size being treated only as a hint to the backend. This is an optimization for those who don't need fixed sized callbacks. */ - ma_device_data_proc dataCallback; - ma_device_notification_proc notificationCallback; - ma_stop_proc stopCallback; - void* pUserData; - ma_resampler_config resampling; - struct - { - const ma_device_id* pDeviceID; - ma_format format; - ma_uint32 channels; - ma_channel* pChannelMap; - ma_channel_mix_mode channelMixMode; - ma_bool32 calculateLFEFromSpatialChannels; /* When an output LFE channel is present, but no input LFE, set to true to set the output LFE to the average of all spatial channels (LR, FR, etc.). Ignored when an input LFE is present. */ - ma_share_mode shareMode; - } playback; - struct - { - const ma_device_id* pDeviceID; - ma_format format; - ma_uint32 channels; - ma_channel* pChannelMap; - ma_channel_mix_mode channelMixMode; - ma_bool32 calculateLFEFromSpatialChannels; /* When an output LFE channel is present, but no input LFE, set to true to set the output LFE to the average of all spatial channels (LR, FR, etc.). Ignored when an input LFE is present. */ - ma_share_mode shareMode; - } capture; - - struct - { - ma_wasapi_usage usage; /* When configured, uses Avrt APIs to set the thread characteristics. */ - ma_bool8 noAutoConvertSRC; /* When set to true, disables the use of AUDCLNT_STREAMFLAGS_AUTOCONVERTPCM. */ - ma_bool8 noDefaultQualitySRC; /* When set to true, disables the use of AUDCLNT_STREAMFLAGS_SRC_DEFAULT_QUALITY. */ - ma_bool8 noAutoStreamRouting; /* Disables automatic stream routing. */ - ma_bool8 noHardwareOffloading; /* Disables WASAPI's hardware offloading feature. */ - ma_uint32 loopbackProcessID; /* The process ID to include or exclude for loopback mode. Set to 0 to capture audio from all processes. Ignored when an explicit device ID is specified. */ - ma_bool8 loopbackProcessExclude; /* When set to true, excludes the process specified by loopbackProcessID. By default, the process will be included. */ - } wasapi; - struct - { - ma_bool32 noMMap; /* Disables MMap mode. */ - ma_bool32 noAutoFormat; /* Opens the ALSA device with SND_PCM_NO_AUTO_FORMAT. */ - ma_bool32 noAutoChannels; /* Opens the ALSA device with SND_PCM_NO_AUTO_CHANNELS. */ - ma_bool32 noAutoResample; /* Opens the ALSA device with SND_PCM_NO_AUTO_RESAMPLE. */ - } alsa; - struct - { - const char* pStreamNamePlayback; - const char* pStreamNameCapture; - int channelMap; - } pulse; - struct - { - ma_bool32 allowNominalSampleRateChange; /* Desktop only. When enabled, allows changing of the sample rate at the operating system level. */ - } coreaudio; - struct - { - ma_opensl_stream_type streamType; - ma_opensl_recording_preset recordingPreset; - ma_bool32 enableCompatibilityWorkarounds; - } opensl; - struct - { - ma_aaudio_usage usage; - ma_aaudio_content_type contentType; - ma_aaudio_input_preset inputPreset; - ma_aaudio_allowed_capture_policy allowedCapturePolicy; - ma_bool32 noAutoStartAfterReroute; - ma_bool32 enableCompatibilityWorkarounds; - ma_bool32 allowSetBufferCapacity; - } aaudio; -}; - - -/* -The callback for handling device enumeration. This is fired from `ma_context_enumerate_devices()`. - - -Parameters ----------- -pContext (in) - A pointer to the context performing the enumeration. - -deviceType (in) - The type of the device being enumerated. This will always be either `ma_device_type_playback` or `ma_device_type_capture`. - -pInfo (in) - A pointer to a `ma_device_info` containing the ID and name of the enumerated device. Note that this will not include detailed information about the device, - only basic information (ID and name). The reason for this is that it would otherwise require opening the backend device to probe for the information which - is too inefficient. - -pUserData (in) - The user data pointer passed into `ma_context_enumerate_devices()`. -*/ -typedef ma_bool32 (* ma_enum_devices_callback_proc)(ma_context* pContext, ma_device_type deviceType, const ma_device_info* pInfo, void* pUserData); - - -/* -Describes some basic details about a playback or capture device. -*/ -typedef struct -{ - const ma_device_id* pDeviceID; - ma_share_mode shareMode; - ma_format format; - ma_uint32 channels; - ma_uint32 sampleRate; - ma_channel channelMap[MA_MAX_CHANNELS]; - ma_uint32 periodSizeInFrames; - ma_uint32 periodSizeInMilliseconds; - ma_uint32 periodCount; -} ma_device_descriptor; - -/* -These are the callbacks required to be implemented for a backend. These callbacks are grouped into two parts: context and device. There is one context -to many devices. A device is created from a context. - -The general flow goes like this: - - 1) A context is created with `onContextInit()` - 1a) Available devices can be enumerated with `onContextEnumerateDevices()` if required. - 1b) Detailed information about a device can be queried with `onContextGetDeviceInfo()` if required. - 2) A device is created from the context that was created in the first step using `onDeviceInit()`, and optionally a device ID that was - selected from device enumeration via `onContextEnumerateDevices()`. - 3) A device is started or stopped with `onDeviceStart()` / `onDeviceStop()` - 4) Data is delivered to and from the device by the backend. This is always done based on the native format returned by the prior call - to `onDeviceInit()`. Conversion between the device's native format and the format requested by the application will be handled by - miniaudio internally. - -Initialization of the context is quite simple. You need to do any necessary initialization of internal objects and then output the -callbacks defined in this structure. - -Once the context has been initialized you can initialize a device. Before doing so, however, the application may want to know which -physical devices are available. This is where `onContextEnumerateDevices()` comes in. This is fairly simple. For each device, fire the -given callback with, at a minimum, the basic information filled out in `ma_device_info`. When the callback returns `MA_FALSE`, enumeration -needs to stop and the `onContextEnumerateDevices()` function returns with a success code. - -Detailed device information can be retrieved from a device ID using `onContextGetDeviceInfo()`. This takes as input the device type and ID, -and on output returns detailed information about the device in `ma_device_info`. The `onContextGetDeviceInfo()` callback must handle the -case when the device ID is NULL, in which case information about the default device needs to be retrieved. - -Once the context has been created and the device ID retrieved (if using anything other than the default device), the device can be created. -This is a little bit more complicated than initialization of the context due to its more complicated configuration. When initializing a -device, a duplex device may be requested. This means a separate data format needs to be specified for both playback and capture. On input, -the data format is set to what the application wants. On output it's set to the native format which should match as closely as possible to -the requested format. The conversion between the format requested by the application and the device's native format will be handled -internally by miniaudio. - -On input, if the sample format is set to `ma_format_unknown`, the backend is free to use whatever sample format it desires, so long as it's -supported by miniaudio. When the channel count is set to 0, the backend should use the device's native channel count. The same applies for -sample rate. For the channel map, the default should be used when `ma_channel_map_is_blank()` returns true (all channels set to -`MA_CHANNEL_NONE`). On input, the `periodSizeInFrames` or `periodSizeInMilliseconds` option should always be set. The backend should -inspect both of these variables. If `periodSizeInFrames` is set, it should take priority, otherwise it needs to be derived from the period -size in milliseconds (`periodSizeInMilliseconds`) and the sample rate, keeping in mind that the sample rate may be 0, in which case the -sample rate will need to be determined before calculating the period size in frames. On output, all members of the `ma_device_descriptor` -object should be set to a valid value, except for `periodSizeInMilliseconds` which is optional (`periodSizeInFrames` *must* be set). - -Starting and stopping of the device is done with `onDeviceStart()` and `onDeviceStop()` and should be self-explanatory. If the backend uses -asynchronous reading and writing, `onDeviceStart()` and `onDeviceStop()` should always be implemented. - -The handling of data delivery between the application and the device is the most complicated part of the process. To make this a bit -easier, some helper callbacks are available. If the backend uses a blocking read/write style of API, the `onDeviceRead()` and -`onDeviceWrite()` callbacks can optionally be implemented. These are blocking and work just like reading and writing from a file. If the -backend uses a callback for data delivery, that callback must call `ma_device_handle_backend_data_callback()` from within its callback. -This allows miniaudio to then process any necessary data conversion and then pass it to the miniaudio data callback. - -If the backend requires absolute flexibility with its data delivery, it can optionally implement the `onDeviceDataLoop()` callback -which will allow it to implement the logic that will run on the audio thread. This is much more advanced and is completely optional. - -The audio thread should run data delivery logic in a loop while `ma_device_get_state() == ma_device_state_started` and no errors have been -encountered. Do not start or stop the device here. That will be handled from outside the `onDeviceDataLoop()` callback. - -The invocation of the `onDeviceDataLoop()` callback will be handled by miniaudio. When you start the device, miniaudio will fire this -callback. When the device is stopped, the `ma_device_get_state() == ma_device_state_started` condition will fail and the loop will be terminated -which will then fall through to the part that stops the device. For an example on how to implement the `onDeviceDataLoop()` callback, -look at `ma_device_audio_thread__default_read_write()`. Implement the `onDeviceDataLoopWakeup()` callback if you need a mechanism to -wake up the audio thread. - -If the backend supports an optimized retrieval of device information from an initialized `ma_device` object, it should implement the -`onDeviceGetInfo()` callback. This is optional, in which case it will fall back to `onContextGetDeviceInfo()` which is less efficient. -*/ -struct ma_backend_callbacks -{ - ma_result (* onContextInit)(ma_context* pContext, const ma_context_config* pConfig, ma_backend_callbacks* pCallbacks); - ma_result (* onContextUninit)(ma_context* pContext); - ma_result (* onContextEnumerateDevices)(ma_context* pContext, ma_enum_devices_callback_proc callback, void* pUserData); - ma_result (* onContextGetDeviceInfo)(ma_context* pContext, ma_device_type deviceType, const ma_device_id* pDeviceID, ma_device_info* pDeviceInfo); - ma_result (* onDeviceInit)(ma_device* pDevice, const ma_device_config* pConfig, ma_device_descriptor* pDescriptorPlayback, ma_device_descriptor* pDescriptorCapture); - ma_result (* onDeviceUninit)(ma_device* pDevice); - ma_result (* onDeviceStart)(ma_device* pDevice); - ma_result (* onDeviceStop)(ma_device* pDevice); - ma_result (* onDeviceRead)(ma_device* pDevice, void* pFrames, ma_uint32 frameCount, ma_uint32* pFramesRead); - ma_result (* onDeviceWrite)(ma_device* pDevice, const void* pFrames, ma_uint32 frameCount, ma_uint32* pFramesWritten); - ma_result (* onDeviceDataLoop)(ma_device* pDevice); - ma_result (* onDeviceDataLoopWakeup)(ma_device* pDevice); - ma_result (* onDeviceGetInfo)(ma_device* pDevice, ma_device_type type, ma_device_info* pDeviceInfo); -}; - -struct ma_context_config -{ - ma_log* pLog; - ma_thread_priority threadPriority; - size_t threadStackSize; - void* pUserData; - ma_allocation_callbacks allocationCallbacks; - struct - { - ma_handle hWnd; /* HWND. Optional window handle to pass into SetCooperativeLevel(). Will default to the foreground window, and if that fails, the desktop window. */ - } dsound; - struct - { - ma_bool32 useVerboseDeviceEnumeration; - } alsa; - struct - { - const char* pApplicationName; - const char* pServerName; - ma_bool32 tryAutoSpawn; /* Enables autospawning of the PulseAudio daemon if necessary. */ - } pulse; - struct - { - ma_ios_session_category sessionCategory; - ma_uint32 sessionCategoryOptions; - ma_bool32 noAudioSessionActivate; /* iOS only. When set to true, does not perform an explicit [[AVAudioSession sharedInstace] setActive:true] on initialization. */ - ma_bool32 noAudioSessionDeactivate; /* iOS only. When set to true, does not perform an explicit [[AVAudioSession sharedInstace] setActive:false] on uninitialization. */ - } coreaudio; - struct - { - const char* pClientName; - ma_bool32 tryStartServer; - } jack; - ma_backend_callbacks custom; -}; - -/* WASAPI specific structure for some commands which must run on a common thread due to bugs in WASAPI. */ -typedef struct -{ - int code; - ma_event* pEvent; /* This will be signalled when the event is complete. */ - union - { - struct - { - int _unused; - } quit; - struct - { - ma_device_type deviceType; - void* pAudioClient; - void** ppAudioClientService; - ma_result* pResult; /* The result from creating the audio client service. */ - } createAudioClient; - struct - { - ma_device* pDevice; - ma_device_type deviceType; - } releaseAudioClient; - } data; -} ma_context_command__wasapi; - -struct ma_context -{ - ma_backend_callbacks callbacks; - ma_backend backend; /* DirectSound, ALSA, etc. */ - ma_log* pLog; - ma_log log; /* Only used if the log is owned by the context. The pLog member will be set to &log in this case. */ - ma_thread_priority threadPriority; - size_t threadStackSize; - void* pUserData; - ma_allocation_callbacks allocationCallbacks; - ma_mutex deviceEnumLock; /* Used to make ma_context_get_devices() thread safe. */ - ma_mutex deviceInfoLock; /* Used to make ma_context_get_device_info() thread safe. */ - ma_uint32 deviceInfoCapacity; /* Total capacity of pDeviceInfos. */ - ma_uint32 playbackDeviceInfoCount; - ma_uint32 captureDeviceInfoCount; - ma_device_info* pDeviceInfos; /* Playback devices first, then capture. */ - - union - { -#ifdef MA_SUPPORT_WASAPI - struct - { - ma_thread commandThread; - ma_mutex commandLock; - ma_semaphore commandSem; - ma_uint32 commandIndex; - ma_uint32 commandCount; - ma_context_command__wasapi commands[4]; - ma_handle hAvrt; - ma_proc AvSetMmThreadCharacteristicsA; - ma_proc AvRevertMmThreadcharacteristics; - ma_handle hMMDevapi; - ma_proc ActivateAudioInterfaceAsync; - } wasapi; -#endif -#ifdef MA_SUPPORT_DSOUND - struct - { - ma_handle hWnd; /* Can be null. */ - ma_handle hDSoundDLL; - ma_proc DirectSoundCreate; - ma_proc DirectSoundEnumerateA; - ma_proc DirectSoundCaptureCreate; - ma_proc DirectSoundCaptureEnumerateA; - } dsound; -#endif -#ifdef MA_SUPPORT_WINMM - struct - { - ma_handle hWinMM; - ma_proc waveOutGetNumDevs; - ma_proc waveOutGetDevCapsA; - ma_proc waveOutOpen; - ma_proc waveOutClose; - ma_proc waveOutPrepareHeader; - ma_proc waveOutUnprepareHeader; - ma_proc waveOutWrite; - ma_proc waveOutReset; - ma_proc waveInGetNumDevs; - ma_proc waveInGetDevCapsA; - ma_proc waveInOpen; - ma_proc waveInClose; - ma_proc waveInPrepareHeader; - ma_proc waveInUnprepareHeader; - ma_proc waveInAddBuffer; - ma_proc waveInStart; - ma_proc waveInReset; - } winmm; -#endif -#ifdef MA_SUPPORT_ALSA - struct - { - ma_handle asoundSO; - ma_proc snd_pcm_open; - ma_proc snd_pcm_close; - ma_proc snd_pcm_hw_params_sizeof; - ma_proc snd_pcm_hw_params_any; - ma_proc snd_pcm_hw_params_set_format; - ma_proc snd_pcm_hw_params_set_format_first; - ma_proc snd_pcm_hw_params_get_format_mask; - ma_proc snd_pcm_hw_params_set_channels; - ma_proc snd_pcm_hw_params_set_channels_near; - ma_proc snd_pcm_hw_params_set_channels_minmax; - ma_proc snd_pcm_hw_params_set_rate_resample; - ma_proc snd_pcm_hw_params_set_rate; - ma_proc snd_pcm_hw_params_set_rate_near; - ma_proc snd_pcm_hw_params_set_buffer_size_near; - ma_proc snd_pcm_hw_params_set_periods_near; - ma_proc snd_pcm_hw_params_set_access; - ma_proc snd_pcm_hw_params_get_format; - ma_proc snd_pcm_hw_params_get_channels; - ma_proc snd_pcm_hw_params_get_channels_min; - ma_proc snd_pcm_hw_params_get_channels_max; - ma_proc snd_pcm_hw_params_get_rate; - ma_proc snd_pcm_hw_params_get_rate_min; - ma_proc snd_pcm_hw_params_get_rate_max; - ma_proc snd_pcm_hw_params_get_buffer_size; - ma_proc snd_pcm_hw_params_get_periods; - ma_proc snd_pcm_hw_params_get_access; - ma_proc snd_pcm_hw_params_test_format; - ma_proc snd_pcm_hw_params_test_channels; - ma_proc snd_pcm_hw_params_test_rate; - ma_proc snd_pcm_hw_params; - ma_proc snd_pcm_sw_params_sizeof; - ma_proc snd_pcm_sw_params_current; - ma_proc snd_pcm_sw_params_get_boundary; - ma_proc snd_pcm_sw_params_set_avail_min; - ma_proc snd_pcm_sw_params_set_start_threshold; - ma_proc snd_pcm_sw_params_set_stop_threshold; - ma_proc snd_pcm_sw_params; - ma_proc snd_pcm_format_mask_sizeof; - ma_proc snd_pcm_format_mask_test; - ma_proc snd_pcm_get_chmap; - ma_proc snd_pcm_state; - ma_proc snd_pcm_prepare; - ma_proc snd_pcm_start; - ma_proc snd_pcm_drop; - ma_proc snd_pcm_drain; - ma_proc snd_pcm_reset; - ma_proc snd_device_name_hint; - ma_proc snd_device_name_get_hint; - ma_proc snd_card_get_index; - ma_proc snd_device_name_free_hint; - ma_proc snd_pcm_mmap_begin; - ma_proc snd_pcm_mmap_commit; - ma_proc snd_pcm_recover; - ma_proc snd_pcm_readi; - ma_proc snd_pcm_writei; - ma_proc snd_pcm_avail; - ma_proc snd_pcm_avail_update; - ma_proc snd_pcm_wait; - ma_proc snd_pcm_nonblock; - ma_proc snd_pcm_info; - ma_proc snd_pcm_info_sizeof; - ma_proc snd_pcm_info_get_name; - ma_proc snd_pcm_poll_descriptors; - ma_proc snd_pcm_poll_descriptors_count; - ma_proc snd_pcm_poll_descriptors_revents; - ma_proc snd_config_update_free_global; - - ma_mutex internalDeviceEnumLock; - ma_bool32 useVerboseDeviceEnumeration; - } alsa; -#endif -#ifdef MA_SUPPORT_PULSEAUDIO - struct - { - ma_handle pulseSO; - ma_proc pa_mainloop_new; - ma_proc pa_mainloop_free; - ma_proc pa_mainloop_quit; - ma_proc pa_mainloop_get_api; - ma_proc pa_mainloop_iterate; - ma_proc pa_mainloop_wakeup; - ma_proc pa_threaded_mainloop_new; - ma_proc pa_threaded_mainloop_free; - ma_proc pa_threaded_mainloop_start; - ma_proc pa_threaded_mainloop_stop; - ma_proc pa_threaded_mainloop_lock; - ma_proc pa_threaded_mainloop_unlock; - ma_proc pa_threaded_mainloop_wait; - ma_proc pa_threaded_mainloop_signal; - ma_proc pa_threaded_mainloop_accept; - ma_proc pa_threaded_mainloop_get_retval; - ma_proc pa_threaded_mainloop_get_api; - ma_proc pa_threaded_mainloop_in_thread; - ma_proc pa_threaded_mainloop_set_name; - ma_proc pa_context_new; - ma_proc pa_context_unref; - ma_proc pa_context_connect; - ma_proc pa_context_disconnect; - ma_proc pa_context_set_state_callback; - ma_proc pa_context_get_state; - ma_proc pa_context_get_sink_info_list; - ma_proc pa_context_get_source_info_list; - ma_proc pa_context_get_sink_info_by_name; - ma_proc pa_context_get_source_info_by_name; - ma_proc pa_operation_unref; - ma_proc pa_operation_get_state; - ma_proc pa_channel_map_init_extend; - ma_proc pa_channel_map_valid; - ma_proc pa_channel_map_compatible; - ma_proc pa_stream_new; - ma_proc pa_stream_unref; - ma_proc pa_stream_connect_playback; - ma_proc pa_stream_connect_record; - ma_proc pa_stream_disconnect; - ma_proc pa_stream_get_state; - ma_proc pa_stream_get_sample_spec; - ma_proc pa_stream_get_channel_map; - ma_proc pa_stream_get_buffer_attr; - ma_proc pa_stream_set_buffer_attr; - ma_proc pa_stream_get_device_name; - ma_proc pa_stream_set_write_callback; - ma_proc pa_stream_set_read_callback; - ma_proc pa_stream_set_suspended_callback; - ma_proc pa_stream_set_moved_callback; - ma_proc pa_stream_is_suspended; - ma_proc pa_stream_flush; - ma_proc pa_stream_drain; - ma_proc pa_stream_is_corked; - ma_proc pa_stream_cork; - ma_proc pa_stream_trigger; - ma_proc pa_stream_begin_write; - ma_proc pa_stream_write; - ma_proc pa_stream_peek; - ma_proc pa_stream_drop; - ma_proc pa_stream_writable_size; - ma_proc pa_stream_readable_size; - - /*pa_mainloop**/ ma_ptr pMainLoop; - /*pa_context**/ ma_ptr pPulseContext; - char* pApplicationName; /* Set when the context is initialized. Used by devices for their local pa_context objects. */ - char* pServerName; /* Set when the context is initialized. Used by devices for their local pa_context objects. */ - } pulse; -#endif -#ifdef MA_SUPPORT_JACK - struct - { - ma_handle jackSO; - ma_proc jack_client_open; - ma_proc jack_client_close; - ma_proc jack_client_name_size; - ma_proc jack_set_process_callback; - ma_proc jack_set_buffer_size_callback; - ma_proc jack_on_shutdown; - ma_proc jack_get_sample_rate; - ma_proc jack_get_buffer_size; - ma_proc jack_get_ports; - ma_proc jack_activate; - ma_proc jack_deactivate; - ma_proc jack_connect; - ma_proc jack_port_register; - ma_proc jack_port_name; - ma_proc jack_port_get_buffer; - ma_proc jack_free; - - char* pClientName; - ma_bool32 tryStartServer; - } jack; -#endif -#ifdef MA_SUPPORT_COREAUDIO - struct - { - ma_handle hCoreFoundation; - ma_proc CFStringGetCString; - ma_proc CFRelease; - - ma_handle hCoreAudio; - ma_proc AudioObjectGetPropertyData; - ma_proc AudioObjectGetPropertyDataSize; - ma_proc AudioObjectSetPropertyData; - ma_proc AudioObjectAddPropertyListener; - ma_proc AudioObjectRemovePropertyListener; - - ma_handle hAudioUnit; /* Could possibly be set to AudioToolbox on later versions of macOS. */ - ma_proc AudioComponentFindNext; - ma_proc AudioComponentInstanceDispose; - ma_proc AudioComponentInstanceNew; - ma_proc AudioOutputUnitStart; - ma_proc AudioOutputUnitStop; - ma_proc AudioUnitAddPropertyListener; - ma_proc AudioUnitGetPropertyInfo; - ma_proc AudioUnitGetProperty; - ma_proc AudioUnitSetProperty; - ma_proc AudioUnitInitialize; - ma_proc AudioUnitRender; - - /*AudioComponent*/ ma_ptr component; - ma_bool32 noAudioSessionDeactivate; /* For tracking whether or not the iOS audio session should be explicitly deactivated. Set from the config in ma_context_init__coreaudio(). */ - } coreaudio; -#endif -#ifdef MA_SUPPORT_SNDIO - struct - { - ma_handle sndioSO; - ma_proc sio_open; - ma_proc sio_close; - ma_proc sio_setpar; - ma_proc sio_getpar; - ma_proc sio_getcap; - ma_proc sio_start; - ma_proc sio_stop; - ma_proc sio_read; - ma_proc sio_write; - ma_proc sio_onmove; - ma_proc sio_nfds; - ma_proc sio_pollfd; - ma_proc sio_revents; - ma_proc sio_eof; - ma_proc sio_setvol; - ma_proc sio_onvol; - ma_proc sio_initpar; - } sndio; -#endif -#ifdef MA_SUPPORT_AUDIO4 - struct - { - int _unused; - } audio4; -#endif -#ifdef MA_SUPPORT_OSS - struct - { - int versionMajor; - int versionMinor; - } oss; -#endif -#ifdef MA_SUPPORT_AAUDIO - struct - { - ma_handle hAAudio; /* libaaudio.so */ - ma_proc AAudio_createStreamBuilder; - ma_proc AAudioStreamBuilder_delete; - ma_proc AAudioStreamBuilder_setDeviceId; - ma_proc AAudioStreamBuilder_setDirection; - ma_proc AAudioStreamBuilder_setSharingMode; - ma_proc AAudioStreamBuilder_setFormat; - ma_proc AAudioStreamBuilder_setChannelCount; - ma_proc AAudioStreamBuilder_setSampleRate; - ma_proc AAudioStreamBuilder_setBufferCapacityInFrames; - ma_proc AAudioStreamBuilder_setFramesPerDataCallback; - ma_proc AAudioStreamBuilder_setDataCallback; - ma_proc AAudioStreamBuilder_setErrorCallback; - ma_proc AAudioStreamBuilder_setPerformanceMode; - ma_proc AAudioStreamBuilder_setUsage; - ma_proc AAudioStreamBuilder_setContentType; - ma_proc AAudioStreamBuilder_setInputPreset; - ma_proc AAudioStreamBuilder_setAllowedCapturePolicy; - ma_proc AAudioStreamBuilder_openStream; - ma_proc AAudioStream_close; - ma_proc AAudioStream_getState; - ma_proc AAudioStream_waitForStateChange; - ma_proc AAudioStream_getFormat; - ma_proc AAudioStream_getChannelCount; - ma_proc AAudioStream_getSampleRate; - ma_proc AAudioStream_getBufferCapacityInFrames; - ma_proc AAudioStream_getFramesPerDataCallback; - ma_proc AAudioStream_getFramesPerBurst; - ma_proc AAudioStream_requestStart; - ma_proc AAudioStream_requestStop; - ma_device_job_thread jobThread; /* For processing operations outside of the error callback, specifically device disconnections and rerouting. */ - } aaudio; -#endif -#ifdef MA_SUPPORT_OPENSL - struct - { - ma_handle libOpenSLES; - ma_handle SL_IID_ENGINE; - ma_handle SL_IID_AUDIOIODEVICECAPABILITIES; - ma_handle SL_IID_ANDROIDSIMPLEBUFFERQUEUE; - ma_handle SL_IID_RECORD; - ma_handle SL_IID_PLAY; - ma_handle SL_IID_OUTPUTMIX; - ma_handle SL_IID_ANDROIDCONFIGURATION; - ma_proc slCreateEngine; - } opensl; -#endif -#ifdef MA_SUPPORT_WEBAUDIO - struct - { - int _unused; - } webaudio; -#endif -#ifdef MA_SUPPORT_NULL - struct - { - int _unused; - } null_backend; -#endif - }; - - union - { -#if defined(MA_WIN32) - struct - { - /*HMODULE*/ ma_handle hOle32DLL; - ma_proc CoInitialize; - ma_proc CoInitializeEx; - ma_proc CoUninitialize; - ma_proc CoCreateInstance; - ma_proc CoTaskMemFree; - ma_proc PropVariantClear; - ma_proc StringFromGUID2; - - /*HMODULE*/ ma_handle hUser32DLL; - ma_proc GetForegroundWindow; - ma_proc GetDesktopWindow; - - /*HMODULE*/ ma_handle hAdvapi32DLL; - ma_proc RegOpenKeyExA; - ma_proc RegCloseKey; - ma_proc RegQueryValueExA; - - /*HRESULT*/ long CoInitializeResult; - } win32; -#endif -#ifdef MA_POSIX - struct - { - int _unused; - } posix; -#endif - int _unused; - }; -}; - -struct ma_device -{ - ma_context* pContext; - ma_device_type type; - ma_uint32 sampleRate; - ma_atomic_device_state state; /* The state of the device is variable and can change at any time on any thread. Must be used atomically. */ - ma_device_data_proc onData; /* Set once at initialization time and should not be changed after. */ - ma_device_notification_proc onNotification; /* Set once at initialization time and should not be changed after. */ - ma_stop_proc onStop; /* DEPRECATED. Use the notification callback instead. Set once at initialization time and should not be changed after. */ - void* pUserData; /* Application defined data. */ - ma_mutex startStopLock; - ma_event wakeupEvent; - ma_event startEvent; - ma_event stopEvent; - ma_thread thread; - ma_result workResult; /* This is set by the worker thread after it's finished doing a job. */ - ma_bool8 isOwnerOfContext; /* When set to true, uninitializing the device will also uninitialize the context. Set to true when NULL is passed into ma_device_init(). */ - ma_bool8 noPreSilencedOutputBuffer; - ma_bool8 noClip; - ma_bool8 noDisableDenormals; - ma_bool8 noFixedSizedCallback; - ma_atomic_float masterVolumeFactor; /* Linear 0..1. Can be read and written simultaneously by different threads. Must be used atomically. */ - ma_duplex_rb duplexRB; /* Intermediary buffer for duplex device on asynchronous backends. */ - struct - { - ma_resample_algorithm algorithm; - ma_resampling_backend_vtable* pBackendVTable; - void* pBackendUserData; - struct - { - ma_uint32 lpfOrder; - } linear; - } resampling; - struct - { - ma_device_id* pID; /* Set to NULL if using default ID, otherwise set to the address of "id". */ - ma_device_id id; /* If using an explicit device, will be set to a copy of the ID used for initialization. Otherwise cleared to 0. */ - char name[MA_MAX_DEVICE_NAME_LENGTH + 1]; /* Maybe temporary. Likely to be replaced with a query API. */ - ma_share_mode shareMode; /* Set to whatever was passed in when the device was initialized. */ - ma_format format; - ma_uint32 channels; - ma_channel channelMap[MA_MAX_CHANNELS]; - ma_format internalFormat; - ma_uint32 internalChannels; - ma_uint32 internalSampleRate; - ma_channel internalChannelMap[MA_MAX_CHANNELS]; - ma_uint32 internalPeriodSizeInFrames; - ma_uint32 internalPeriods; - ma_channel_mix_mode channelMixMode; - ma_bool32 calculateLFEFromSpatialChannels; - ma_data_converter converter; - void* pIntermediaryBuffer; /* For implementing fixed sized buffer callbacks. Will be null if using variable sized callbacks. */ - ma_uint32 intermediaryBufferCap; - ma_uint32 intermediaryBufferLen; /* How many valid frames are sitting in the intermediary buffer. */ - void* pInputCache; /* In external format. Can be null. */ - ma_uint64 inputCacheCap; - ma_uint64 inputCacheConsumed; - ma_uint64 inputCacheRemaining; - } playback; - struct - { - ma_device_id* pID; /* Set to NULL if using default ID, otherwise set to the address of "id". */ - ma_device_id id; /* If using an explicit device, will be set to a copy of the ID used for initialization. Otherwise cleared to 0. */ - char name[MA_MAX_DEVICE_NAME_LENGTH + 1]; /* Maybe temporary. Likely to be replaced with a query API. */ - ma_share_mode shareMode; /* Set to whatever was passed in when the device was initialized. */ - ma_format format; - ma_uint32 channels; - ma_channel channelMap[MA_MAX_CHANNELS]; - ma_format internalFormat; - ma_uint32 internalChannels; - ma_uint32 internalSampleRate; - ma_channel internalChannelMap[MA_MAX_CHANNELS]; - ma_uint32 internalPeriodSizeInFrames; - ma_uint32 internalPeriods; - ma_channel_mix_mode channelMixMode; - ma_bool32 calculateLFEFromSpatialChannels; - ma_data_converter converter; - void* pIntermediaryBuffer; /* For implementing fixed sized buffer callbacks. Will be null if using variable sized callbacks. */ - ma_uint32 intermediaryBufferCap; - ma_uint32 intermediaryBufferLen; /* How many valid frames are sitting in the intermediary buffer. */ - } capture; - - union - { -#ifdef MA_SUPPORT_WASAPI - struct - { - /*IAudioClient**/ ma_ptr pAudioClientPlayback; - /*IAudioClient**/ ma_ptr pAudioClientCapture; - /*IAudioRenderClient**/ ma_ptr pRenderClient; - /*IAudioCaptureClient**/ ma_ptr pCaptureClient; - /*IMMDeviceEnumerator**/ ma_ptr pDeviceEnumerator; /* Used for IMMNotificationClient notifications. Required for detecting default device changes. */ - ma_IMMNotificationClient notificationClient; - /*HANDLE*/ ma_handle hEventPlayback; /* Auto reset. Initialized to signaled. */ - /*HANDLE*/ ma_handle hEventCapture; /* Auto reset. Initialized to unsignaled. */ - ma_uint32 actualBufferSizeInFramesPlayback; /* Value from GetBufferSize(). internalPeriodSizeInFrames is not set to the _actual_ buffer size when low-latency shared mode is being used due to the way the IAudioClient3 API works. */ - ma_uint32 actualBufferSizeInFramesCapture; - ma_uint32 originalPeriodSizeInFrames; - ma_uint32 originalPeriodSizeInMilliseconds; - ma_uint32 originalPeriods; - ma_performance_profile originalPerformanceProfile; - ma_uint32 periodSizeInFramesPlayback; - ma_uint32 periodSizeInFramesCapture; - void* pMappedBufferCapture; - ma_uint32 mappedBufferCaptureCap; - ma_uint32 mappedBufferCaptureLen; - void* pMappedBufferPlayback; - ma_uint32 mappedBufferPlaybackCap; - ma_uint32 mappedBufferPlaybackLen; - ma_atomic_bool32 isStartedCapture; /* Can be read and written simultaneously across different threads. Must be used atomically, and must be 32-bit. */ - ma_atomic_bool32 isStartedPlayback; /* Can be read and written simultaneously across different threads. Must be used atomically, and must be 32-bit. */ - ma_uint32 loopbackProcessID; - ma_bool8 loopbackProcessExclude; - ma_bool8 noAutoConvertSRC; /* When set to true, disables the use of AUDCLNT_STREAMFLAGS_AUTOCONVERTPCM. */ - ma_bool8 noDefaultQualitySRC; /* When set to true, disables the use of AUDCLNT_STREAMFLAGS_SRC_DEFAULT_QUALITY. */ - ma_bool8 noHardwareOffloading; - ma_bool8 allowCaptureAutoStreamRouting; - ma_bool8 allowPlaybackAutoStreamRouting; - ma_bool8 isDetachedPlayback; - ma_bool8 isDetachedCapture; - ma_wasapi_usage usage; - void* hAvrtHandle; - ma_mutex rerouteLock; - } wasapi; -#endif -#ifdef MA_SUPPORT_DSOUND - struct - { - /*LPDIRECTSOUND*/ ma_ptr pPlayback; - /*LPDIRECTSOUNDBUFFER*/ ma_ptr pPlaybackPrimaryBuffer; - /*LPDIRECTSOUNDBUFFER*/ ma_ptr pPlaybackBuffer; - /*LPDIRECTSOUNDCAPTURE*/ ma_ptr pCapture; - /*LPDIRECTSOUNDCAPTUREBUFFER*/ ma_ptr pCaptureBuffer; - } dsound; -#endif -#ifdef MA_SUPPORT_WINMM - struct - { - /*HWAVEOUT*/ ma_handle hDevicePlayback; - /*HWAVEIN*/ ma_handle hDeviceCapture; - /*HANDLE*/ ma_handle hEventPlayback; - /*HANDLE*/ ma_handle hEventCapture; - ma_uint32 fragmentSizeInFrames; - ma_uint32 iNextHeaderPlayback; /* [0,periods). Used as an index into pWAVEHDRPlayback. */ - ma_uint32 iNextHeaderCapture; /* [0,periods). Used as an index into pWAVEHDRCapture. */ - ma_uint32 headerFramesConsumedPlayback; /* The number of PCM frames consumed in the buffer in pWAVEHEADER[iNextHeader]. */ - ma_uint32 headerFramesConsumedCapture; /* ^^^ */ - /*WAVEHDR**/ ma_uint8* pWAVEHDRPlayback; /* One instantiation for each period. */ - /*WAVEHDR**/ ma_uint8* pWAVEHDRCapture; /* One instantiation for each period. */ - ma_uint8* pIntermediaryBufferPlayback; - ma_uint8* pIntermediaryBufferCapture; - ma_uint8* _pHeapData; /* Used internally and is used for the heap allocated data for the intermediary buffer and the WAVEHDR structures. */ - } winmm; -#endif -#ifdef MA_SUPPORT_ALSA - struct - { - /*snd_pcm_t**/ ma_ptr pPCMPlayback; - /*snd_pcm_t**/ ma_ptr pPCMCapture; - /*struct pollfd**/ void* pPollDescriptorsPlayback; - /*struct pollfd**/ void* pPollDescriptorsCapture; - int pollDescriptorCountPlayback; - int pollDescriptorCountCapture; - int wakeupfdPlayback; /* eventfd for waking up from poll() when the playback device is stopped. */ - int wakeupfdCapture; /* eventfd for waking up from poll() when the capture device is stopped. */ - ma_bool8 isUsingMMapPlayback; - ma_bool8 isUsingMMapCapture; - } alsa; -#endif -#ifdef MA_SUPPORT_PULSEAUDIO - struct - { - /*pa_mainloop**/ ma_ptr pMainLoop; - /*pa_context**/ ma_ptr pPulseContext; - /*pa_stream**/ ma_ptr pStreamPlayback; - /*pa_stream**/ ma_ptr pStreamCapture; - } pulse; -#endif -#ifdef MA_SUPPORT_JACK - struct - { - /*jack_client_t**/ ma_ptr pClient; - /*jack_port_t**/ ma_ptr* ppPortsPlayback; - /*jack_port_t**/ ma_ptr* ppPortsCapture; - float* pIntermediaryBufferPlayback; /* Typed as a float because JACK is always floating point. */ - float* pIntermediaryBufferCapture; - } jack; -#endif -#ifdef MA_SUPPORT_COREAUDIO - struct - { - ma_uint32 deviceObjectIDPlayback; - ma_uint32 deviceObjectIDCapture; - /*AudioUnit*/ ma_ptr audioUnitPlayback; - /*AudioUnit*/ ma_ptr audioUnitCapture; - /*AudioBufferList**/ ma_ptr pAudioBufferList; /* Only used for input devices. */ - ma_uint32 audioBufferCapInFrames; /* Only used for input devices. The capacity in frames of each buffer in pAudioBufferList. */ - ma_event stopEvent; - ma_uint32 originalPeriodSizeInFrames; - ma_uint32 originalPeriodSizeInMilliseconds; - ma_uint32 originalPeriods; - ma_performance_profile originalPerformanceProfile; - ma_bool32 isDefaultPlaybackDevice; - ma_bool32 isDefaultCaptureDevice; - ma_bool32 isSwitchingPlaybackDevice; /* <-- Set to true when the default device has changed and miniaudio is in the process of switching. */ - ma_bool32 isSwitchingCaptureDevice; /* <-- Set to true when the default device has changed and miniaudio is in the process of switching. */ - void* pNotificationHandler; /* Only used on mobile platforms. Obj-C object for handling route changes. */ - } coreaudio; -#endif -#ifdef MA_SUPPORT_SNDIO - struct - { - ma_ptr handlePlayback; - ma_ptr handleCapture; - ma_bool32 isStartedPlayback; - ma_bool32 isStartedCapture; - } sndio; -#endif -#ifdef MA_SUPPORT_AUDIO4 - struct - { - int fdPlayback; - int fdCapture; - } audio4; -#endif -#ifdef MA_SUPPORT_OSS - struct - { - int fdPlayback; - int fdCapture; - } oss; -#endif -#ifdef MA_SUPPORT_AAUDIO - struct - { - /*AAudioStream**/ ma_ptr pStreamPlayback; - /*AAudioStream**/ ma_ptr pStreamCapture; - ma_mutex rerouteLock; - ma_aaudio_usage usage; - ma_aaudio_content_type contentType; - ma_aaudio_input_preset inputPreset; - ma_aaudio_allowed_capture_policy allowedCapturePolicy; - ma_bool32 noAutoStartAfterReroute; - } aaudio; -#endif -#ifdef MA_SUPPORT_OPENSL - struct - { - /*SLObjectItf*/ ma_ptr pOutputMixObj; - /*SLOutputMixItf*/ ma_ptr pOutputMix; - /*SLObjectItf*/ ma_ptr pAudioPlayerObj; - /*SLPlayItf*/ ma_ptr pAudioPlayer; - /*SLObjectItf*/ ma_ptr pAudioRecorderObj; - /*SLRecordItf*/ ma_ptr pAudioRecorder; - /*SLAndroidSimpleBufferQueueItf*/ ma_ptr pBufferQueuePlayback; - /*SLAndroidSimpleBufferQueueItf*/ ma_ptr pBufferQueueCapture; - ma_bool32 isDrainingCapture; - ma_bool32 isDrainingPlayback; - ma_uint32 currentBufferIndexPlayback; - ma_uint32 currentBufferIndexCapture; - ma_uint8* pBufferPlayback; /* This is malloc()'d and is used for storing audio data. Typed as ma_uint8 for easy offsetting. */ - ma_uint8* pBufferCapture; - } opensl; -#endif -#ifdef MA_SUPPORT_WEBAUDIO - struct - { - /* AudioWorklets path. */ - /* EMSCRIPTEN_WEBAUDIO_T */ int audioContext; - /* EMSCRIPTEN_WEBAUDIO_T */ int audioWorklet; - float* pIntermediaryBuffer; - void* pStackBuffer; - ma_result initResult; /* Set to MA_BUSY while initialization is in progress. */ - int deviceIndex; /* We store the device in a list on the JavaScript side. This is used to map our C object to the JS object. */ - } webaudio; -#endif -#ifdef MA_SUPPORT_NULL - struct - { - ma_thread deviceThread; - ma_event operationEvent; - ma_event operationCompletionEvent; - ma_semaphore operationSemaphore; - ma_uint32 operation; - ma_result operationResult; - ma_timer timer; - double priorRunTime; - ma_uint32 currentPeriodFramesRemainingPlayback; - ma_uint32 currentPeriodFramesRemainingCapture; - ma_uint64 lastProcessedFramePlayback; - ma_uint64 lastProcessedFrameCapture; - ma_atomic_bool32 isStarted; /* Read and written by multiple threads. Must be used atomically, and must be 32-bit for compiler compatibility. */ - } null_device; -#endif - }; -}; -#if defined(_MSC_VER) && !defined(__clang__) - #pragma warning(pop) -#elif defined(__clang__) || (defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 8))) - #pragma GCC diagnostic pop /* For ISO C99 doesn't support unnamed structs/unions [-Wpedantic] */ -#endif - -/* -Initializes a `ma_context_config` object. - - -Return Value ------------- -A `ma_context_config` initialized to defaults. - - -Remarks -------- -You must always use this to initialize the default state of the `ma_context_config` object. Not using this will result in your program breaking when miniaudio -is updated and new members are added to `ma_context_config`. It also sets logical defaults. - -You can override members of the returned object by changing it's members directly. - - -See Also --------- -ma_context_init() -*/ -MA_API ma_context_config ma_context_config_init(void); - -/* -Initializes a context. - -The context is used for selecting and initializing an appropriate backend and to represent the backend at a more global level than that of an individual -device. There is one context to many devices, and a device is created from a context. A context is required to enumerate devices. - - -Parameters ----------- -backends (in, optional) - A list of backends to try initializing, in priority order. Can be NULL, in which case it uses default priority order. - -backendCount (in, optional) - The number of items in `backend`. Ignored if `backend` is NULL. - -pConfig (in, optional) - The context configuration. - -pContext (in) - A pointer to the context object being initialized. - - -Return Value ------------- -MA_SUCCESS if successful; any other error code otherwise. - - -Thread Safety -------------- -Unsafe. Do not call this function across multiple threads as some backends read and write to global state. - - -Remarks -------- -When `backends` is NULL, the default priority order will be used. Below is a list of backends in priority order: - - |-------------|-----------------------|--------------------------------------------------------| - | Name | Enum Name | Supported Operating Systems | - |-------------|-----------------------|--------------------------------------------------------| - | WASAPI | ma_backend_wasapi | Windows Vista+ | - | DirectSound | ma_backend_dsound | Windows XP+ | - | WinMM | ma_backend_winmm | Windows XP+ (may work on older versions, but untested) | - | Core Audio | ma_backend_coreaudio | macOS, iOS | - | ALSA | ma_backend_alsa | Linux | - | PulseAudio | ma_backend_pulseaudio | Cross Platform (disabled on Windows, BSD and Android) | - | JACK | ma_backend_jack | Cross Platform (disabled on BSD and Android) | - | sndio | ma_backend_sndio | OpenBSD | - | audio(4) | ma_backend_audio4 | NetBSD, OpenBSD | - | OSS | ma_backend_oss | FreeBSD | - | AAudio | ma_backend_aaudio | Android 8+ | - | OpenSL|ES | ma_backend_opensl | Android (API level 16+) | - | Web Audio | ma_backend_webaudio | Web (via Emscripten) | - | Null | ma_backend_null | Cross Platform (not used on Web) | - |-------------|-----------------------|--------------------------------------------------------| - -The context can be configured via the `pConfig` argument. The config object is initialized with `ma_context_config_init()`. Individual configuration settings -can then be set directly on the structure. Below are the members of the `ma_context_config` object. - - pLog - A pointer to the `ma_log` to post log messages to. Can be NULL if the application does not - require logging. See the `ma_log` API for details on how to use the logging system. - - threadPriority - The desired priority to use for the audio thread. Allowable values include the following: - - |--------------------------------------| - | Thread Priority | - |--------------------------------------| - | ma_thread_priority_idle | - | ma_thread_priority_lowest | - | ma_thread_priority_low | - | ma_thread_priority_normal | - | ma_thread_priority_high | - | ma_thread_priority_highest (default) | - | ma_thread_priority_realtime | - | ma_thread_priority_default | - |--------------------------------------| - - threadStackSize - The desired size of the stack for the audio thread. Defaults to the operating system's default. - - pUserData - A pointer to application-defined data. This can be accessed from the context object directly such as `context.pUserData`. - - allocationCallbacks - Structure containing custom allocation callbacks. Leaving this at defaults will cause it to use MA_MALLOC, MA_REALLOC and MA_FREE. These allocation - callbacks will be used for anything tied to the context, including devices. - - alsa.useVerboseDeviceEnumeration - ALSA will typically enumerate many different devices which can be intrusive and not user-friendly. To combat this, miniaudio will enumerate only unique - card/device pairs by default. The problem with this is that you lose a bit of flexibility and control. Setting alsa.useVerboseDeviceEnumeration makes - it so the ALSA backend includes all devices. Defaults to false. - - pulse.pApplicationName - PulseAudio only. The application name to use when initializing the PulseAudio context with `pa_context_new()`. - - pulse.pServerName - PulseAudio only. The name of the server to connect to with `pa_context_connect()`. - - pulse.tryAutoSpawn - PulseAudio only. Whether or not to try automatically starting the PulseAudio daemon. Defaults to false. If you set this to true, keep in mind that - miniaudio uses a trial and error method to find the most appropriate backend, and this will result in the PulseAudio daemon starting which may be - intrusive for the end user. - - coreaudio.sessionCategory - iOS only. The session category to use for the shared AudioSession instance. Below is a list of allowable values and their Core Audio equivalents. - - |-----------------------------------------|-------------------------------------| - | miniaudio Token | Core Audio Token | - |-----------------------------------------|-------------------------------------| - | ma_ios_session_category_ambient | AVAudioSessionCategoryAmbient | - | ma_ios_session_category_solo_ambient | AVAudioSessionCategorySoloAmbient | - | ma_ios_session_category_playback | AVAudioSessionCategoryPlayback | - | ma_ios_session_category_record | AVAudioSessionCategoryRecord | - | ma_ios_session_category_play_and_record | AVAudioSessionCategoryPlayAndRecord | - | ma_ios_session_category_multi_route | AVAudioSessionCategoryMultiRoute | - | ma_ios_session_category_none | AVAudioSessionCategoryAmbient | - | ma_ios_session_category_default | AVAudioSessionCategoryAmbient | - |-----------------------------------------|-------------------------------------| - - coreaudio.sessionCategoryOptions - iOS only. Session category options to use with the shared AudioSession instance. Below is a list of allowable values and their Core Audio equivalents. - - |---------------------------------------------------------------------------|------------------------------------------------------------------| - | miniaudio Token | Core Audio Token | - |---------------------------------------------------------------------------|------------------------------------------------------------------| - | ma_ios_session_category_option_mix_with_others | AVAudioSessionCategoryOptionMixWithOthers | - | ma_ios_session_category_option_duck_others | AVAudioSessionCategoryOptionDuckOthers | - | ma_ios_session_category_option_allow_bluetooth | AVAudioSessionCategoryOptionAllowBluetooth | - | ma_ios_session_category_option_default_to_speaker | AVAudioSessionCategoryOptionDefaultToSpeaker | - | ma_ios_session_category_option_interrupt_spoken_audio_and_mix_with_others | AVAudioSessionCategoryOptionInterruptSpokenAudioAndMixWithOthers | - | ma_ios_session_category_option_allow_bluetooth_a2dp | AVAudioSessionCategoryOptionAllowBluetoothA2DP | - | ma_ios_session_category_option_allow_air_play | AVAudioSessionCategoryOptionAllowAirPlay | - |---------------------------------------------------------------------------|------------------------------------------------------------------| - - coreaudio.noAudioSessionActivate - iOS only. When set to true, does not perform an explicit [[AVAudioSession sharedInstace] setActive:true] on initialization. - - coreaudio.noAudioSessionDeactivate - iOS only. When set to true, does not perform an explicit [[AVAudioSession sharedInstace] setActive:false] on uninitialization. - - jack.pClientName - The name of the client to pass to `jack_client_open()`. - - jack.tryStartServer - Whether or not to try auto-starting the JACK server. Defaults to false. - - -It is recommended that only a single context is active at any given time because it's a bulky data structure which performs run-time linking for the -relevant backends every time it's initialized. - -The location of the context cannot change throughout it's lifetime. Consider allocating the `ma_context` object with `malloc()` if this is an issue. The -reason for this is that a pointer to the context is stored in the `ma_device` structure. - - -Example 1 - Default Initialization ----------------------------------- -The example below shows how to initialize the context using the default configuration. - -```c -ma_context context; -ma_result result = ma_context_init(NULL, 0, NULL, &context); -if (result != MA_SUCCESS) { - // Error. -} -``` - - -Example 2 - Custom Configuration --------------------------------- -The example below shows how to initialize the context using custom backend priorities and a custom configuration. In this hypothetical example, the program -wants to prioritize ALSA over PulseAudio on Linux. They also want to avoid using the WinMM backend on Windows because it's latency is too high. They also -want an error to be returned if no valid backend is available which they achieve by excluding the Null backend. - -For the configuration, the program wants to capture any log messages so they can, for example, route it to a log file and user interface. - -```c -ma_backend backends[] = { - ma_backend_alsa, - ma_backend_pulseaudio, - ma_backend_wasapi, - ma_backend_dsound -}; - -ma_log log; -ma_log_init(&log); -ma_log_register_callback(&log, ma_log_callback_init(my_log_callbac, pMyLogUserData)); - -ma_context_config config = ma_context_config_init(); -config.pLog = &log; // Specify a custom log object in the config so any logs that are posted from ma_context_init() are captured. - -ma_context context; -ma_result result = ma_context_init(backends, sizeof(backends)/sizeof(backends[0]), &config, &context); -if (result != MA_SUCCESS) { - // Error. - if (result == MA_NO_BACKEND) { - // Couldn't find an appropriate backend. - } -} - -// You could also attach a log callback post-initialization: -ma_log_register_callback(ma_context_get_log(&context), ma_log_callback_init(my_log_callback, pMyLogUserData)); -``` - - -See Also --------- -ma_context_config_init() -ma_context_uninit() -*/ -MA_API ma_result ma_context_init(const ma_backend backends[], ma_uint32 backendCount, const ma_context_config* pConfig, ma_context* pContext); - -/* -Uninitializes a context. - - -Return Value ------------- -MA_SUCCESS if successful; any other error code otherwise. - - -Thread Safety -------------- -Unsafe. Do not call this function across multiple threads as some backends read and write to global state. - - -Remarks -------- -Results are undefined if you call this while any device created by this context is still active. - - -See Also --------- -ma_context_init() -*/ -MA_API ma_result ma_context_uninit(ma_context* pContext); - -/* -Retrieves the size of the ma_context object. - -This is mainly for the purpose of bindings to know how much memory to allocate. -*/ -MA_API size_t ma_context_sizeof(void); - -/* -Retrieves a pointer to the log object associated with this context. - - -Remarks -------- -Pass the returned pointer to `ma_log_post()`, `ma_log_postv()` or `ma_log_postf()` to post a log -message. - -You can attach your own logging callback to the log with `ma_log_register_callback()` - - -Return Value ------------- -A pointer to the `ma_log` object that the context uses to post log messages. If some error occurs, -NULL will be returned. -*/ -MA_API ma_log* ma_context_get_log(ma_context* pContext); - -/* -Enumerates over every device (both playback and capture). - -This is a lower-level enumeration function to the easier to use `ma_context_get_devices()`. Use `ma_context_enumerate_devices()` if you would rather not incur -an internal heap allocation, or it simply suits your code better. - -Note that this only retrieves the ID and name/description of the device. The reason for only retrieving basic information is that it would otherwise require -opening the backend device in order to probe it for more detailed information which can be inefficient. Consider using `ma_context_get_device_info()` for this, -but don't call it from within the enumeration callback. - -Returning false from the callback will stop enumeration. Returning true will continue enumeration. - - -Parameters ----------- -pContext (in) - A pointer to the context performing the enumeration. - -callback (in) - The callback to fire for each enumerated device. - -pUserData (in) - A pointer to application-defined data passed to the callback. - - -Return Value ------------- -MA_SUCCESS if successful; any other error code otherwise. - - -Thread Safety -------------- -Safe. This is guarded using a simple mutex lock. - - -Remarks -------- -Do _not_ assume the first enumerated device of a given type is the default device. - -Some backends and platforms may only support default playback and capture devices. - -In general, you should not do anything complicated from within the callback. In particular, do not try initializing a device from within the callback. Also, -do not try to call `ma_context_get_device_info()` from within the callback. - -Consider using `ma_context_get_devices()` for a simpler and safer API, albeit at the expense of an internal heap allocation. - - -Example 1 - Simple Enumeration ------------------------------- -ma_bool32 ma_device_enum_callback(ma_context* pContext, ma_device_type deviceType, const ma_device_info* pInfo, void* pUserData) -{ - printf("Device Name: %s\n", pInfo->name); - return MA_TRUE; -} - -ma_result result = ma_context_enumerate_devices(&context, my_device_enum_callback, pMyUserData); -if (result != MA_SUCCESS) { - // Error. -} - - -See Also --------- -ma_context_get_devices() -*/ -MA_API ma_result ma_context_enumerate_devices(ma_context* pContext, ma_enum_devices_callback_proc callback, void* pUserData); - -/* -Retrieves basic information about every active playback and/or capture device. - -This function will allocate memory internally for the device lists and return a pointer to them through the `ppPlaybackDeviceInfos` and `ppCaptureDeviceInfos` -parameters. If you do not want to incur the overhead of these allocations consider using `ma_context_enumerate_devices()` which will instead use a callback. - -Note that this only retrieves the ID and name/description of the device. The reason for only retrieving basic information is that it would otherwise require -opening the backend device in order to probe it for more detailed information which can be inefficient. Consider using `ma_context_get_device_info()` for this, -but don't call it from within the enumeration callback. - - -Parameters ----------- -pContext (in) - A pointer to the context performing the enumeration. - -ppPlaybackDeviceInfos (out) - A pointer to a pointer that will receive the address of a buffer containing the list of `ma_device_info` structures for playback devices. - -pPlaybackDeviceCount (out) - A pointer to an unsigned integer that will receive the number of playback devices. - -ppCaptureDeviceInfos (out) - A pointer to a pointer that will receive the address of a buffer containing the list of `ma_device_info` structures for capture devices. - -pCaptureDeviceCount (out) - A pointer to an unsigned integer that will receive the number of capture devices. - - -Return Value ------------- -MA_SUCCESS if successful; any other error code otherwise. - - -Thread Safety -------------- -Unsafe. Since each call to this function invalidates the pointers from the previous call, you should not be calling this simultaneously across multiple -threads. Instead, you need to make a copy of the returned data with your own higher level synchronization. - - -Remarks -------- -It is _not_ safe to assume the first device in the list is the default device. - -You can pass in NULL for the playback or capture lists in which case they'll be ignored. - -The returned pointers will become invalid upon the next call this this function, or when the context is uninitialized. Do not free the returned pointers. - - -See Also --------- -ma_context_enumerate_devices() -*/ -MA_API ma_result ma_context_get_devices(ma_context* pContext, ma_device_info** ppPlaybackDeviceInfos, ma_uint32* pPlaybackDeviceCount, ma_device_info** ppCaptureDeviceInfos, ma_uint32* pCaptureDeviceCount); - -/* -Retrieves information about a device of the given type, with the specified ID and share mode. - - -Parameters ----------- -pContext (in) - A pointer to the context performing the query. - -deviceType (in) - The type of the device being queried. Must be either `ma_device_type_playback` or `ma_device_type_capture`. - -pDeviceID (in) - The ID of the device being queried. - -pDeviceInfo (out) - A pointer to the `ma_device_info` structure that will receive the device information. - - -Return Value ------------- -MA_SUCCESS if successful; any other error code otherwise. - - -Thread Safety -------------- -Safe. This is guarded using a simple mutex lock. - - -Remarks -------- -Do _not_ call this from within the `ma_context_enumerate_devices()` callback. - -It's possible for a device to have different information and capabilities depending on whether or not it's opened in shared or exclusive mode. For example, in -shared mode, WASAPI always uses floating point samples for mixing, but in exclusive mode it can be anything. Therefore, this function allows you to specify -which share mode you want information for. Note that not all backends and devices support shared or exclusive mode, in which case this function will fail if -the requested share mode is unsupported. - -This leaves pDeviceInfo unmodified in the result of an error. -*/ -MA_API ma_result ma_context_get_device_info(ma_context* pContext, ma_device_type deviceType, const ma_device_id* pDeviceID, ma_device_info* pDeviceInfo); - -/* -Determines if the given context supports loopback mode. - - -Parameters ----------- -pContext (in) - A pointer to the context getting queried. - - -Return Value ------------- -MA_TRUE if the context supports loopback mode; MA_FALSE otherwise. -*/ -MA_API ma_bool32 ma_context_is_loopback_supported(ma_context* pContext); - - - -/* -Initializes a device config with default settings. - - -Parameters ----------- -deviceType (in) - The type of the device this config is being initialized for. This must set to one of the following: - - |-------------------------| - | Device Type | - |-------------------------| - | ma_device_type_playback | - | ma_device_type_capture | - | ma_device_type_duplex | - | ma_device_type_loopback | - |-------------------------| - - -Return Value ------------- -A new device config object with default settings. You will typically want to adjust the config after this function returns. See remarks. - - -Thread Safety -------------- -Safe. - - -Callback Safety ---------------- -Safe, but don't try initializing a device in a callback. - - -Remarks -------- -The returned config will be initialized to defaults. You will normally want to customize a few variables before initializing the device. See Example 1 for a -typical configuration which sets the sample format, channel count, sample rate, data callback and user data. These are usually things you will want to change -before initializing the device. - -See `ma_device_init()` for details on specific configuration options. - - -Example 1 - Simple Configuration --------------------------------- -The example below is what a program will typically want to configure for each device at a minimum. Notice how `ma_device_config_init()` is called first, and -then the returned object is modified directly. This is important because it ensures that your program continues to work as new configuration options are added -to the `ma_device_config` structure. - -```c -ma_device_config config = ma_device_config_init(ma_device_type_playback); -config.playback.format = ma_format_f32; -config.playback.channels = 2; -config.sampleRate = 48000; -config.dataCallback = ma_data_callback; -config.pUserData = pMyUserData; -``` - - -See Also --------- -ma_device_init() -ma_device_init_ex() -*/ -MA_API ma_device_config ma_device_config_init(ma_device_type deviceType); - - -/* -Initializes a device. - -A device represents a physical audio device. The idea is you send or receive audio data from the device to either play it back through a speaker, or capture it -from a microphone. Whether or not you should send or receive data from the device (or both) depends on the type of device you are initializing which can be -playback, capture, full-duplex or loopback. (Note that loopback mode is only supported on select backends.) Sending and receiving audio data to and from the -device is done via a callback which is fired by miniaudio at periodic time intervals. - -The frequency at which data is delivered to and from a device depends on the size of its period. The size of the period can be defined in terms of PCM frames -or milliseconds, whichever is more convenient. Generally speaking, the smaller the period, the lower the latency at the expense of higher CPU usage and -increased risk of glitching due to the more frequent and granular data deliver intervals. The size of a period will depend on your requirements, but -miniaudio's defaults should work fine for most scenarios. If you're building a game you should leave this fairly small, whereas if you're building a simple -media player you can make it larger. Note that the period size you request is actually just a hint - miniaudio will tell the backend what you want, but the -backend is ultimately responsible for what it gives you. You cannot assume you will get exactly what you ask for. - -When delivering data to and from a device you need to make sure it's in the correct format which you can set through the device configuration. You just set the -format that you want to use and miniaudio will perform all of the necessary conversion for you internally. When delivering data to and from the callback you -can assume the format is the same as what you requested when you initialized the device. See Remarks for more details on miniaudio's data conversion pipeline. - - -Parameters ----------- -pContext (in, optional) - A pointer to the context that owns the device. This can be null, in which case it creates a default context internally. - -pConfig (in) - A pointer to the device configuration. Cannot be null. See remarks for details. - -pDevice (out) - A pointer to the device object being initialized. - - -Return Value ------------- -MA_SUCCESS if successful; any other error code otherwise. - - -Thread Safety -------------- -Unsafe. It is not safe to call this function simultaneously for different devices because some backends depend on and mutate global state. The same applies to -calling this at the same time as `ma_device_uninit()`. - - -Callback Safety ---------------- -Unsafe. It is not safe to call this inside any callback. - - -Remarks -------- -Setting `pContext` to NULL will result in miniaudio creating a default context internally and is equivalent to passing in a context initialized like so: - - ```c - ma_context_init(NULL, 0, NULL, &context); - ``` - -Do not set `pContext` to NULL if you are needing to open multiple devices. You can, however, use NULL when initializing the first device, and then use -device.pContext for the initialization of other devices. - -The device can be configured via the `pConfig` argument. The config object is initialized with `ma_device_config_init()`. Individual configuration settings can -then be set directly on the structure. Below are the members of the `ma_device_config` object. - - deviceType - Must be `ma_device_type_playback`, `ma_device_type_capture`, `ma_device_type_duplex` of `ma_device_type_loopback`. - - sampleRate - The sample rate, in hertz. The most common sample rates are 48000 and 44100. Setting this to 0 will use the device's native sample rate. - - periodSizeInFrames - The desired size of a period in PCM frames. If this is 0, `periodSizeInMilliseconds` will be used instead. If both are 0 the default buffer size will - be used depending on the selected performance profile. This value affects latency. See below for details. - - periodSizeInMilliseconds - The desired size of a period in milliseconds. If this is 0, `periodSizeInFrames` will be used instead. If both are 0 the default buffer size will be - used depending on the selected performance profile. The value affects latency. See below for details. - - periods - The number of periods making up the device's entire buffer. The total buffer size is `periodSizeInFrames` or `periodSizeInMilliseconds` multiplied by - this value. This is just a hint as backends will be the ones who ultimately decide how your periods will be configured. - - performanceProfile - A hint to miniaudio as to the performance requirements of your program. Can be either `ma_performance_profile_low_latency` (default) or - `ma_performance_profile_conservative`. This mainly affects the size of default buffers and can usually be left at its default value. - - noPreSilencedOutputBuffer - When set to true, the contents of the output buffer passed into the data callback will be left undefined. When set to false (default), the contents of - the output buffer will be cleared the zero. You can use this to avoid the overhead of zeroing out the buffer if you can guarantee that your data - callback will write to every sample in the output buffer, or if you are doing your own clearing. - - noClip - When set to true, the contents of the output buffer are left alone after returning and it will be left up to the backend itself to decide whether or - not to clip. When set to false (default), the contents of the output buffer passed into the data callback will be clipped after returning. This only - applies when the playback sample format is f32. - - noDisableDenormals - By default, miniaudio will disable denormals when the data callback is called. Setting this to true will prevent the disabling of denormals. - - noFixedSizedCallback - Allows miniaudio to fire the data callback with any frame count. When this is set to false (the default), the data callback will be fired with a - consistent frame count as specified by `periodSizeInFrames` or `periodSizeInMilliseconds`. When set to true, miniaudio will fire the callback with - whatever the backend requests, which could be anything. - - dataCallback - The callback to fire whenever data is ready to be delivered to or from the device. - - notificationCallback - The callback to fire when something has changed with the device, such as whether or not it has been started or stopped. - - pUserData - The user data pointer to use with the device. You can access this directly from the device object like `device.pUserData`. - - resampling.algorithm - The resampling algorithm to use when miniaudio needs to perform resampling between the rate specified by `sampleRate` and the device's native rate. The - default value is `ma_resample_algorithm_linear`, and the quality can be configured with `resampling.linear.lpfOrder`. - - resampling.pBackendVTable - A pointer to an optional vtable that can be used for plugging in a custom resampler. - - resampling.pBackendUserData - A pointer that will passed to callbacks in pBackendVTable. - - resampling.linear.lpfOrder - The linear resampler applies a low-pass filter as part of its processing for anti-aliasing. This setting controls the order of the filter. The higher - the value, the better the quality, in general. Setting this to 0 will disable low-pass filtering altogether. The maximum value is - `MA_MAX_FILTER_ORDER`. The default value is `min(4, MA_MAX_FILTER_ORDER)`. - - playback.pDeviceID - A pointer to a `ma_device_id` structure containing the ID of the playback device to initialize. Setting this NULL (default) will use the system's - default playback device. Retrieve the device ID from the `ma_device_info` structure, which can be retrieved using device enumeration. - - playback.format - The sample format to use for playback. When set to `ma_format_unknown` the device's native format will be used. This can be retrieved after - initialization from the device object directly with `device.playback.format`. - - playback.channels - The number of channels to use for playback. When set to 0 the device's native channel count will be used. This can be retrieved after initialization - from the device object directly with `device.playback.channels`. - - playback.pChannelMap - The channel map to use for playback. When left empty, the device's native channel map will be used. This can be retrieved after initialization from the - device object direct with `device.playback.pChannelMap`. When set, the buffer should contain `channels` items. - - playback.shareMode - The preferred share mode to use for playback. Can be either `ma_share_mode_shared` (default) or `ma_share_mode_exclusive`. Note that if you specify - exclusive mode, but it's not supported by the backend, initialization will fail. You can then fall back to shared mode if desired by changing this to - ma_share_mode_shared and reinitializing. - - capture.pDeviceID - A pointer to a `ma_device_id` structure containing the ID of the capture device to initialize. Setting this NULL (default) will use the system's - default capture device. Retrieve the device ID from the `ma_device_info` structure, which can be retrieved using device enumeration. - - capture.format - The sample format to use for capture. When set to `ma_format_unknown` the device's native format will be used. This can be retrieved after - initialization from the device object directly with `device.capture.format`. - - capture.channels - The number of channels to use for capture. When set to 0 the device's native channel count will be used. This can be retrieved after initialization - from the device object directly with `device.capture.channels`. - - capture.pChannelMap - The channel map to use for capture. When left empty, the device's native channel map will be used. This can be retrieved after initialization from the - device object direct with `device.capture.pChannelMap`. When set, the buffer should contain `channels` items. - - capture.shareMode - The preferred share mode to use for capture. Can be either `ma_share_mode_shared` (default) or `ma_share_mode_exclusive`. Note that if you specify - exclusive mode, but it's not supported by the backend, initialization will fail. You can then fall back to shared mode if desired by changing this to - ma_share_mode_shared and reinitializing. - - wasapi.noAutoConvertSRC - WASAPI only. When set to true, disables WASAPI's automatic resampling and forces the use of miniaudio's resampler. Defaults to false. - - wasapi.noDefaultQualitySRC - WASAPI only. Only used when `wasapi.noAutoConvertSRC` is set to false. When set to true, disables the use of `AUDCLNT_STREAMFLAGS_SRC_DEFAULT_QUALITY`. - You should usually leave this set to false, which is the default. - - wasapi.noAutoStreamRouting - WASAPI only. When set to true, disables automatic stream routing on the WASAPI backend. Defaults to false. - - wasapi.noHardwareOffloading - WASAPI only. When set to true, disables the use of WASAPI's hardware offloading feature. Defaults to false. - - alsa.noMMap - ALSA only. When set to true, disables MMap mode. Defaults to false. - - alsa.noAutoFormat - ALSA only. When set to true, disables ALSA's automatic format conversion by including the SND_PCM_NO_AUTO_FORMAT flag. Defaults to false. - - alsa.noAutoChannels - ALSA only. When set to true, disables ALSA's automatic channel conversion by including the SND_PCM_NO_AUTO_CHANNELS flag. Defaults to false. - - alsa.noAutoResample - ALSA only. When set to true, disables ALSA's automatic resampling by including the SND_PCM_NO_AUTO_RESAMPLE flag. Defaults to false. - - pulse.pStreamNamePlayback - PulseAudio only. Sets the stream name for playback. - - pulse.pStreamNameCapture - PulseAudio only. Sets the stream name for capture. - - pulse.channelMap - PulseAudio only. Sets the channel map that is requested from PulseAudio. See MA_PA_CHANNEL_MAP_* constants. Defaults to MA_PA_CHANNEL_MAP_AIFF. - - coreaudio.allowNominalSampleRateChange - Core Audio only. Desktop only. When enabled, allows the sample rate of the device to be changed at the operating system level. This - is disabled by default in order to prevent intrusive changes to the user's system. This is useful if you want to use a sample rate - that is known to be natively supported by the hardware thereby avoiding the cost of resampling. When set to true, miniaudio will - find the closest match between the sample rate requested in the device config and the sample rates natively supported by the - hardware. When set to false, the sample rate currently set by the operating system will always be used. - - opensl.streamType - OpenSL only. Explicitly sets the stream type. If left unset (`ma_opensl_stream_type_default`), the - stream type will be left unset. Think of this as the type of audio you're playing. - - opensl.recordingPreset - OpenSL only. Explicitly sets the type of recording your program will be doing. When left - unset, the recording preset will be left unchanged. - - aaudio.usage - AAudio only. Explicitly sets the nature of the audio the program will be consuming. When - left unset, the usage will be left unchanged. - - aaudio.contentType - AAudio only. Sets the content type. When left unset, the content type will be left unchanged. - - aaudio.inputPreset - AAudio only. Explicitly sets the type of recording your program will be doing. When left - unset, the input preset will be left unchanged. - - aaudio.noAutoStartAfterReroute - AAudio only. Controls whether or not the device should be automatically restarted after a - stream reroute. When set to false (default) the device will be restarted automatically; - otherwise the device will be stopped. - - -Once initialized, the device's config is immutable. If you need to change the config you will need to initialize a new device. - -After initializing the device it will be in a stopped state. To start it, use `ma_device_start()`. - -If both `periodSizeInFrames` and `periodSizeInMilliseconds` are set to zero, it will default to `MA_DEFAULT_PERIOD_SIZE_IN_MILLISECONDS_LOW_LATENCY` or -`MA_DEFAULT_PERIOD_SIZE_IN_MILLISECONDS_CONSERVATIVE`, depending on whether or not `performanceProfile` is set to `ma_performance_profile_low_latency` or -`ma_performance_profile_conservative`. - -If you request exclusive mode and the backend does not support it an error will be returned. For robustness, you may want to first try initializing the device -in exclusive mode, and then fall back to shared mode if required. Alternatively you can just request shared mode (the default if you leave it unset in the -config) which is the most reliable option. Some backends do not have a practical way of choosing whether or not the device should be exclusive or not (ALSA, -for example) in which case it just acts as a hint. Unless you have special requirements you should try avoiding exclusive mode as it's intrusive to the user. -Starting with Windows 10, miniaudio will use low-latency shared mode where possible which may make exclusive mode unnecessary. - -When sending or receiving data to/from a device, miniaudio will internally perform a format conversion to convert between the format specified by the config -and the format used internally by the backend. If you pass in 0 for the sample format, channel count, sample rate _and_ channel map, data transmission will run -on an optimized pass-through fast path. You can retrieve the format, channel count and sample rate by inspecting the `playback/capture.format`, -`playback/capture.channels` and `sampleRate` members of the device object. - -When compiling for UWP you must ensure you call this function on the main UI thread because the operating system may need to present the user with a message -asking for permissions. Please refer to the official documentation for ActivateAudioInterfaceAsync() for more information. - -ALSA Specific: When initializing the default device, requesting shared mode will try using the "dmix" device for playback and the "dsnoop" device for capture. -If these fail it will try falling back to the "hw" device. - - -Example 1 - Simple Initialization ---------------------------------- -This example shows how to initialize a simple playback device using a standard configuration. If you are just needing to do simple playback from the default -playback device this is usually all you need. - -```c -ma_device_config config = ma_device_config_init(ma_device_type_playback); -config.playback.format = ma_format_f32; -config.playback.channels = 2; -config.sampleRate = 48000; -config.dataCallback = ma_data_callback; -config.pMyUserData = pMyUserData; - -ma_device device; -ma_result result = ma_device_init(NULL, &config, &device); -if (result != MA_SUCCESS) { - // Error -} -``` - - -Example 2 - Advanced Initialization ------------------------------------ -This example shows how you might do some more advanced initialization. In this hypothetical example we want to control the latency by setting the buffer size -and period count. We also want to allow the user to be able to choose which device to output from which means we need a context so we can perform device -enumeration. - -```c -ma_context context; -ma_result result = ma_context_init(NULL, 0, NULL, &context); -if (result != MA_SUCCESS) { - // Error -} - -ma_device_info* pPlaybackDeviceInfos; -ma_uint32 playbackDeviceCount; -result = ma_context_get_devices(&context, &pPlaybackDeviceInfos, &playbackDeviceCount, NULL, NULL); -if (result != MA_SUCCESS) { - // Error -} - -// ... choose a device from pPlaybackDeviceInfos ... - -ma_device_config config = ma_device_config_init(ma_device_type_playback); -config.playback.pDeviceID = pMyChosenDeviceID; // <-- Get this from the `id` member of one of the `ma_device_info` objects returned by ma_context_get_devices(). -config.playback.format = ma_format_f32; -config.playback.channels = 2; -config.sampleRate = 48000; -config.dataCallback = ma_data_callback; -config.pUserData = pMyUserData; -config.periodSizeInMilliseconds = 10; -config.periods = 3; - -ma_device device; -result = ma_device_init(&context, &config, &device); -if (result != MA_SUCCESS) { - // Error -} -``` - - -See Also --------- -ma_device_config_init() -ma_device_uninit() -ma_device_start() -ma_context_init() -ma_context_get_devices() -ma_context_enumerate_devices() -*/ -MA_API ma_result ma_device_init(ma_context* pContext, const ma_device_config* pConfig, ma_device* pDevice); - -/* -Initializes a device without a context, with extra parameters for controlling the configuration of the internal self-managed context. - -This is the same as `ma_device_init()`, only instead of a context being passed in, the parameters from `ma_context_init()` are passed in instead. This function -allows you to configure the internally created context. - - -Parameters ----------- -backends (in, optional) - A list of backends to try initializing, in priority order. Can be NULL, in which case it uses default priority order. - -backendCount (in, optional) - The number of items in `backend`. Ignored if `backend` is NULL. - -pContextConfig (in, optional) - The context configuration. - -pConfig (in) - A pointer to the device configuration. Cannot be null. See remarks for details. - -pDevice (out) - A pointer to the device object being initialized. - - -Return Value ------------- -MA_SUCCESS if successful; any other error code otherwise. - - -Thread Safety -------------- -Unsafe. It is not safe to call this function simultaneously for different devices because some backends depend on and mutate global state. The same applies to -calling this at the same time as `ma_device_uninit()`. - - -Callback Safety ---------------- -Unsafe. It is not safe to call this inside any callback. - - -Remarks -------- -You only need to use this function if you want to configure the context differently to its defaults. You should never use this function if you want to manage -your own context. - -See the documentation for `ma_context_init()` for information on the different context configuration options. - - -See Also --------- -ma_device_init() -ma_device_uninit() -ma_device_config_init() -ma_context_init() -*/ -MA_API ma_result ma_device_init_ex(const ma_backend backends[], ma_uint32 backendCount, const ma_context_config* pContextConfig, const ma_device_config* pConfig, ma_device* pDevice); - -/* -Uninitializes a device. - -This will explicitly stop the device. You do not need to call `ma_device_stop()` beforehand, but it's harmless if you do. - - -Parameters ----------- -pDevice (in) - A pointer to the device to stop. - - -Return Value ------------- -Nothing - - -Thread Safety -------------- -Unsafe. As soon as this API is called the device should be considered undefined. - - -Callback Safety ---------------- -Unsafe. It is not safe to call this inside any callback. Doing this will result in a deadlock. - - -See Also --------- -ma_device_init() -ma_device_stop() -*/ -MA_API void ma_device_uninit(ma_device* pDevice); - - -/* -Retrieves a pointer to the context that owns the given device. -*/ -MA_API ma_context* ma_device_get_context(ma_device* pDevice); - -/* -Helper function for retrieving the log object associated with the context that owns this device. -*/ -MA_API ma_log* ma_device_get_log(ma_device* pDevice); - - -/* -Retrieves information about the device. - - -Parameters ----------- -pDevice (in) - A pointer to the device whose information is being retrieved. - -type (in) - The device type. This parameter is required for duplex devices. When retrieving device - information, you are doing so for an individual playback or capture device. - -pDeviceInfo (out) - A pointer to the `ma_device_info` that will receive the device information. - - -Return Value ------------- -MA_SUCCESS if successful; any other error code otherwise. - - -Thread Safety -------------- -Unsafe. This should be considered unsafe because it may be calling into the backend which may or -may not be safe. - - -Callback Safety ---------------- -Unsafe. You should avoid calling this in the data callback because it may call into the backend -which may or may not be safe. -*/ -MA_API ma_result ma_device_get_info(ma_device* pDevice, ma_device_type type, ma_device_info* pDeviceInfo); - - -/* -Retrieves the name of the device. - - -Parameters ----------- -pDevice (in) - A pointer to the device whose information is being retrieved. - -type (in) - The device type. This parameter is required for duplex devices. When retrieving device - information, you are doing so for an individual playback or capture device. - -pName (out) - A pointer to the buffer that will receive the name. - -nameCap (in) - The capacity of the output buffer, including space for the null terminator. - -pLengthNotIncludingNullTerminator (out, optional) - A pointer to the variable that will receive the length of the name, not including the null - terminator. - - -Return Value ------------- -MA_SUCCESS if successful; any other error code otherwise. - - -Thread Safety -------------- -Unsafe. This should be considered unsafe because it may be calling into the backend which may or -may not be safe. - - -Callback Safety ---------------- -Unsafe. You should avoid calling this in the data callback because it may call into the backend -which may or may not be safe. - - -Remarks -------- -If the name does not fully fit into the output buffer, it'll be truncated. You can pass in NULL to -`pName` if you want to first get the length of the name for the purpose of memory allocation of the -output buffer. Allocating a buffer of size `MA_MAX_DEVICE_NAME_LENGTH + 1` should be enough for -most cases and will avoid the need for the inefficiency of calling this function twice. - -This is implemented in terms of `ma_device_get_info()`. -*/ -MA_API ma_result ma_device_get_name(ma_device* pDevice, ma_device_type type, char* pName, size_t nameCap, size_t* pLengthNotIncludingNullTerminator); - - -/* -Starts the device. For playback devices this begins playback. For capture devices it begins recording. - -Use `ma_device_stop()` to stop the device. - - -Parameters ----------- -pDevice (in) - A pointer to the device to start. - - -Return Value ------------- -MA_SUCCESS if successful; any other error code otherwise. - - -Thread Safety -------------- -Safe. It's safe to call this from any thread with the exception of the callback thread. - - -Callback Safety ---------------- -Unsafe. It is not safe to call this inside any callback. - - -Remarks -------- -For a playback device, this will retrieve an initial chunk of audio data from the client before returning. The reason for this is to ensure there is valid -audio data in the buffer, which needs to be done before the device begins playback. - -This API waits until the backend device has been started for real by the worker thread. It also waits on a mutex for thread-safety. - -Do not call this in any callback. - - -See Also --------- -ma_device_stop() -*/ -MA_API ma_result ma_device_start(ma_device* pDevice); - -/* -Stops the device. For playback devices this stops playback. For capture devices it stops recording. - -Use `ma_device_start()` to start the device again. - - -Parameters ----------- -pDevice (in) - A pointer to the device to stop. - - -Return Value ------------- -MA_SUCCESS if successful; any other error code otherwise. - - -Thread Safety -------------- -Safe. It's safe to call this from any thread with the exception of the callback thread. - - -Callback Safety ---------------- -Unsafe. It is not safe to call this inside any callback. Doing this will result in a deadlock. - - -Remarks -------- -This API needs to wait on the worker thread to stop the backend device properly before returning. It also waits on a mutex for thread-safety. In addition, some -backends need to wait for the device to finish playback/recording of the current fragment which can take some time (usually proportionate to the buffer size -that was specified at initialization time). - -Backends are required to either pause the stream in-place or drain the buffer if pausing is not possible. The reason for this is that stopping the device and -the resuming it with ma_device_start() (which you might do when your program loses focus) may result in a situation where those samples are never output to the -speakers or received from the microphone which can in turn result in de-syncs. - -Do not call this in any callback. - - -See Also --------- -ma_device_start() -*/ -MA_API ma_result ma_device_stop(ma_device* pDevice); - -/* -Determines whether or not the device is started. - - -Parameters ----------- -pDevice (in) - A pointer to the device whose start state is being retrieved. - - -Return Value ------------- -True if the device is started, false otherwise. - - -Thread Safety -------------- -Safe. If another thread calls `ma_device_start()` or `ma_device_stop()` at this same time as this function is called, there's a very small chance the return -value will be out of sync. - - -Callback Safety ---------------- -Safe. This is implemented as a simple accessor. - - -See Also --------- -ma_device_start() -ma_device_stop() -*/ -MA_API ma_bool32 ma_device_is_started(const ma_device* pDevice); - - -/* -Retrieves the state of the device. - - -Parameters ----------- -pDevice (in) - A pointer to the device whose state is being retrieved. - - -Return Value ------------- -The current state of the device. The return value will be one of the following: - - +-------------------------------+------------------------------------------------------------------------------+ - | ma_device_state_uninitialized | Will only be returned if the device is in the middle of initialization. | - +-------------------------------+------------------------------------------------------------------------------+ - | ma_device_state_stopped | The device is stopped. The initial state of the device after initialization. | - +-------------------------------+------------------------------------------------------------------------------+ - | ma_device_state_started | The device started and requesting and/or delivering audio data. | - +-------------------------------+------------------------------------------------------------------------------+ - | ma_device_state_starting | The device is in the process of starting. | - +-------------------------------+------------------------------------------------------------------------------+ - | ma_device_state_stopping | The device is in the process of stopping. | - +-------------------------------+------------------------------------------------------------------------------+ - - -Thread Safety -------------- -Safe. This is implemented as a simple accessor. Note that if the device is started or stopped at the same time as this function is called, -there's a possibility the return value could be out of sync. See remarks. - - -Callback Safety ---------------- -Safe. This is implemented as a simple accessor. - - -Remarks -------- -The general flow of a devices state goes like this: - - ``` - ma_device_init() -> ma_device_state_uninitialized -> ma_device_state_stopped - ma_device_start() -> ma_device_state_starting -> ma_device_state_started - ma_device_stop() -> ma_device_state_stopping -> ma_device_state_stopped - ``` - -When the state of the device is changed with `ma_device_start()` or `ma_device_stop()` at this same time as this function is called, the -value returned by this function could potentially be out of sync. If this is significant to your program you need to implement your own -synchronization. -*/ -MA_API ma_device_state ma_device_get_state(const ma_device* pDevice); - - -/* -Performs post backend initialization routines for setting up internal data conversion. - -This should be called whenever the backend is initialized. The only time this should be called from -outside of miniaudio is if you're implementing a custom backend, and you would only do it if you -are reinitializing the backend due to rerouting or reinitializing for some reason. - - -Parameters ----------- -pDevice [in] - A pointer to the device. - -deviceType [in] - The type of the device that was just reinitialized. - -pPlaybackDescriptor [in] - The descriptor of the playback device containing the internal data format and buffer sizes. - -pPlaybackDescriptor [in] - The descriptor of the capture device containing the internal data format and buffer sizes. - - -Return Value ------------- -MA_SUCCESS if successful; any other error otherwise. - - -Thread Safety -------------- -Unsafe. This will be reinitializing internal data converters which may be in use by another thread. - - -Callback Safety ---------------- -Unsafe. This will be reinitializing internal data converters which may be in use by the callback. - - -Remarks -------- -For a duplex device, you can call this for only one side of the system. This is why the deviceType -is specified as a parameter rather than deriving it from the device. - -You do not need to call this manually unless you are doing a custom backend, in which case you need -only do it if you're manually performing rerouting or reinitialization. -*/ -MA_API ma_result ma_device_post_init(ma_device* pDevice, ma_device_type deviceType, const ma_device_descriptor* pPlaybackDescriptor, const ma_device_descriptor* pCaptureDescriptor); - - -/* -Sets the master volume factor for the device. - -The volume factor must be between 0 (silence) and 1 (full volume). Use `ma_device_set_master_volume_db()` to use decibel notation, where 0 is full volume and -values less than 0 decreases the volume. - - -Parameters ----------- -pDevice (in) - A pointer to the device whose volume is being set. - -volume (in) - The new volume factor. Must be >= 0. - - -Return Value ------------- -MA_SUCCESS if the volume was set successfully. -MA_INVALID_ARGS if pDevice is NULL. -MA_INVALID_ARGS if volume is negative. - - -Thread Safety -------------- -Safe. This just sets a local member of the device object. - - -Callback Safety ---------------- -Safe. If you set the volume in the data callback, that data written to the output buffer will have the new volume applied. - - -Remarks -------- -This applies the volume factor across all channels. - -This does not change the operating system's volume. It only affects the volume for the given `ma_device` object's audio stream. - - -See Also --------- -ma_device_get_master_volume() -ma_device_set_master_volume_db() -ma_device_get_master_volume_db() -*/ -MA_API ma_result ma_device_set_master_volume(ma_device* pDevice, float volume); - -/* -Retrieves the master volume factor for the device. - - -Parameters ----------- -pDevice (in) - A pointer to the device whose volume factor is being retrieved. - -pVolume (in) - A pointer to the variable that will receive the volume factor. The returned value will be in the range of [0, 1]. - - -Return Value ------------- -MA_SUCCESS if successful. -MA_INVALID_ARGS if pDevice is NULL. -MA_INVALID_ARGS if pVolume is NULL. - - -Thread Safety -------------- -Safe. This just a simple member retrieval. - - -Callback Safety ---------------- -Safe. - - -Remarks -------- -If an error occurs, `*pVolume` will be set to 0. - - -See Also --------- -ma_device_set_master_volume() -ma_device_set_master_volume_gain_db() -ma_device_get_master_volume_gain_db() -*/ -MA_API ma_result ma_device_get_master_volume(ma_device* pDevice, float* pVolume); - -/* -Sets the master volume for the device as gain in decibels. - -A gain of 0 is full volume, whereas a gain of < 0 will decrease the volume. - - -Parameters ----------- -pDevice (in) - A pointer to the device whose gain is being set. - -gainDB (in) - The new volume as gain in decibels. Must be less than or equal to 0, where 0 is full volume and anything less than 0 decreases the volume. - - -Return Value ------------- -MA_SUCCESS if the volume was set successfully. -MA_INVALID_ARGS if pDevice is NULL. -MA_INVALID_ARGS if the gain is > 0. - - -Thread Safety -------------- -Safe. This just sets a local member of the device object. - - -Callback Safety ---------------- -Safe. If you set the volume in the data callback, that data written to the output buffer will have the new volume applied. - - -Remarks -------- -This applies the gain across all channels. - -This does not change the operating system's volume. It only affects the volume for the given `ma_device` object's audio stream. - - -See Also --------- -ma_device_get_master_volume_gain_db() -ma_device_set_master_volume() -ma_device_get_master_volume() -*/ -MA_API ma_result ma_device_set_master_volume_db(ma_device* pDevice, float gainDB); - -/* -Retrieves the master gain in decibels. - - -Parameters ----------- -pDevice (in) - A pointer to the device whose gain is being retrieved. - -pGainDB (in) - A pointer to the variable that will receive the gain in decibels. The returned value will be <= 0. - - -Return Value ------------- -MA_SUCCESS if successful. -MA_INVALID_ARGS if pDevice is NULL. -MA_INVALID_ARGS if pGainDB is NULL. - - -Thread Safety -------------- -Safe. This just a simple member retrieval. - - -Callback Safety ---------------- -Safe. - - -Remarks -------- -If an error occurs, `*pGainDB` will be set to 0. - - -See Also --------- -ma_device_set_master_volume_db() -ma_device_set_master_volume() -ma_device_get_master_volume() -*/ -MA_API ma_result ma_device_get_master_volume_db(ma_device* pDevice, float* pGainDB); - - -/* -Called from the data callback of asynchronous backends to allow miniaudio to process the data and fire the miniaudio data callback. - - -Parameters ----------- -pDevice (in) - A pointer to device whose processing the data callback. - -pOutput (out) - A pointer to the buffer that will receive the output PCM frame data. On a playback device this must not be NULL. On a duplex device - this can be NULL, in which case pInput must not be NULL. - -pInput (in) - A pointer to the buffer containing input PCM frame data. On a capture device this must not be NULL. On a duplex device this can be - NULL, in which case `pOutput` must not be NULL. - -frameCount (in) - The number of frames being processed. - - -Return Value ------------- -MA_SUCCESS if successful; any other result code otherwise. - - -Thread Safety -------------- -This function should only ever be called from the internal data callback of the backend. It is safe to call this simultaneously between a -playback and capture device in duplex setups. - - -Callback Safety ---------------- -Do not call this from the miniaudio data callback. It should only ever be called from the internal data callback of the backend. - - -Remarks -------- -If both `pOutput` and `pInput` are NULL, and error will be returned. In duplex scenarios, both `pOutput` and `pInput` can be non-NULL, in -which case `pInput` will be processed first, followed by `pOutput`. - -If you are implementing a custom backend, and that backend uses a callback for data delivery, you'll need to call this from inside that -callback. -*/ -MA_API ma_result ma_device_handle_backend_data_callback(ma_device* pDevice, void* pOutput, const void* pInput, ma_uint32 frameCount); - - -/* -Calculates an appropriate buffer size from a descriptor, native sample rate and performance profile. - -This function is used by backends for helping determine an appropriately sized buffer to use with -the device depending on the values of `periodSizeInFrames` and `periodSizeInMilliseconds` in the -`pDescriptor` object. Since buffer size calculations based on time depends on the sample rate, a -best guess at the device's native sample rate is also required which is where `nativeSampleRate` -comes in. In addition, the performance profile is also needed for cases where both the period size -in frames and milliseconds are both zero. - - -Parameters ----------- -pDescriptor (in) - A pointer to device descriptor whose `periodSizeInFrames` and `periodSizeInMilliseconds` members - will be used for the calculation of the buffer size. - -nativeSampleRate (in) - The device's native sample rate. This is only ever used when the `periodSizeInFrames` member of - `pDescriptor` is zero. In this case, `periodSizeInMilliseconds` will be used instead, in which - case a sample rate is required to convert to a size in frames. - -performanceProfile (in) - When both the `periodSizeInFrames` and `periodSizeInMilliseconds` members of `pDescriptor` are - zero, miniaudio will fall back to a buffer size based on the performance profile. The profile - to use for this calculation is determine by this parameter. - - -Return Value ------------- -The calculated buffer size in frames. - - -Thread Safety -------------- -This is safe so long as nothing modifies `pDescriptor` at the same time. However, this function -should only ever be called from within the backend's device initialization routine and therefore -shouldn't have any multithreading concerns. - - -Callback Safety ---------------- -This is safe to call within the data callback, but there is no reason to ever do this. - - -Remarks -------- -If `nativeSampleRate` is zero, this function will fall back to `pDescriptor->sampleRate`. If that -is also zero, `MA_DEFAULT_SAMPLE_RATE` will be used instead. -*/ -MA_API ma_uint32 ma_calculate_buffer_size_in_frames_from_descriptor(const ma_device_descriptor* pDescriptor, ma_uint32 nativeSampleRate, ma_performance_profile performanceProfile); - - - -/* -Retrieves a friendly name for a backend. -*/ -MA_API const char* ma_get_backend_name(ma_backend backend); - -/* -Retrieves the backend enum from the given name. -*/ -MA_API ma_result ma_get_backend_from_name(const char* pBackendName, ma_backend* pBackend); - -/* -Determines whether or not the given backend is available by the compilation environment. -*/ -MA_API ma_bool32 ma_is_backend_enabled(ma_backend backend); - -/* -Retrieves compile-time enabled backends. - - -Parameters ----------- -pBackends (out, optional) - A pointer to the buffer that will receive the enabled backends. Set to NULL to retrieve the backend count. Setting - the capacity of the buffer to `MA_BUFFER_COUNT` will guarantee it's large enough for all backends. - -backendCap (in) - The capacity of the `pBackends` buffer. - -pBackendCount (out) - A pointer to the variable that will receive the enabled backend count. - - -Return Value ------------- -MA_SUCCESS if successful. -MA_INVALID_ARGS if `pBackendCount` is NULL. -MA_NO_SPACE if the capacity of `pBackends` is not large enough. - -If `MA_NO_SPACE` is returned, the `pBackends` buffer will be filled with `*pBackendCount` values. - - -Thread Safety -------------- -Safe. - - -Callback Safety ---------------- -Safe. - - -Remarks -------- -If you want to retrieve the number of backends so you can determine the capacity of `pBackends` buffer, you can call -this function with `pBackends` set to NULL. - -This will also enumerate the null backend. If you don't want to include this you need to check for `ma_backend_null` -when you enumerate over the returned backends and handle it appropriately. Alternatively, you can disable it at -compile time with `MA_NO_NULL`. - -The returned backends are determined based on compile time settings, not the platform it's currently running on. For -example, PulseAudio will be returned if it was enabled at compile time, even when the user doesn't actually have -PulseAudio installed. - - -Example 1 ---------- -The example below retrieves the enabled backend count using a fixed sized buffer allocated on the stack. The buffer is -given a capacity of `MA_BACKEND_COUNT` which will guarantee it'll be large enough to store all available backends. -Since `MA_BACKEND_COUNT` is always a relatively small value, this should be suitable for most scenarios. - -``` -ma_backend enabledBackends[MA_BACKEND_COUNT]; -size_t enabledBackendCount; - -result = ma_get_enabled_backends(enabledBackends, MA_BACKEND_COUNT, &enabledBackendCount); -if (result != MA_SUCCESS) { - // Failed to retrieve enabled backends. Should never happen in this example since all inputs are valid. -} -``` - - -See Also --------- -ma_is_backend_enabled() -*/ -MA_API ma_result ma_get_enabled_backends(ma_backend* pBackends, size_t backendCap, size_t* pBackendCount); - -/* -Determines whether or not loopback mode is support by a backend. -*/ -MA_API ma_bool32 ma_is_loopback_supported(ma_backend backend); - -#endif /* MA_NO_DEVICE_IO */ - - - -/************************************************************************************************************************************************************ - -Utilities - -************************************************************************************************************************************************************/ - -/* -Calculates a buffer size in milliseconds (rounded up) from the specified number of frames and sample rate. -*/ -MA_API ma_uint32 ma_calculate_buffer_size_in_milliseconds_from_frames(ma_uint32 bufferSizeInFrames, ma_uint32 sampleRate); - -/* -Calculates a buffer size in frames from the specified number of milliseconds and sample rate. -*/ -MA_API ma_uint32 ma_calculate_buffer_size_in_frames_from_milliseconds(ma_uint32 bufferSizeInMilliseconds, ma_uint32 sampleRate); - -/* -Copies PCM frames from one buffer to another. -*/ -MA_API void ma_copy_pcm_frames(void* dst, const void* src, ma_uint64 frameCount, ma_format format, ma_uint32 channels); - -/* -Copies silent frames into the given buffer. - -Remarks -------- -For all formats except `ma_format_u8`, the output buffer will be filled with 0. For `ma_format_u8` it will be filled with 128. The reason for this is that it -makes more sense for the purpose of mixing to initialize it to the center point. -*/ -MA_API void ma_silence_pcm_frames(void* p, ma_uint64 frameCount, ma_format format, ma_uint32 channels); - - -/* -Offsets a pointer by the specified number of PCM frames. -*/ -MA_API void* ma_offset_pcm_frames_ptr(void* p, ma_uint64 offsetInFrames, ma_format format, ma_uint32 channels); -MA_API const void* ma_offset_pcm_frames_const_ptr(const void* p, ma_uint64 offsetInFrames, ma_format format, ma_uint32 channels); -static MA_INLINE float* ma_offset_pcm_frames_ptr_f32(float* p, ma_uint64 offsetInFrames, ma_uint32 channels) { return (float*)ma_offset_pcm_frames_ptr((void*)p, offsetInFrames, ma_format_f32, channels); } -static MA_INLINE const float* ma_offset_pcm_frames_const_ptr_f32(const float* p, ma_uint64 offsetInFrames, ma_uint32 channels) { return (const float*)ma_offset_pcm_frames_const_ptr((const void*)p, offsetInFrames, ma_format_f32, channels); } - - -/* -Clips samples. -*/ -MA_API void ma_clip_samples_u8(ma_uint8* pDst, const ma_int16* pSrc, ma_uint64 count); -MA_API void ma_clip_samples_s16(ma_int16* pDst, const ma_int32* pSrc, ma_uint64 count); -MA_API void ma_clip_samples_s24(ma_uint8* pDst, const ma_int64* pSrc, ma_uint64 count); -MA_API void ma_clip_samples_s32(ma_int32* pDst, const ma_int64* pSrc, ma_uint64 count); -MA_API void ma_clip_samples_f32(float* pDst, const float* pSrc, ma_uint64 count); -MA_API void ma_clip_pcm_frames(void* pDst, const void* pSrc, ma_uint64 frameCount, ma_format format, ma_uint32 channels); - -/* -Helper for applying a volume factor to samples. - -Note that the source and destination buffers can be the same, in which case it'll perform the operation in-place. -*/ -MA_API void ma_copy_and_apply_volume_factor_u8(ma_uint8* pSamplesOut, const ma_uint8* pSamplesIn, ma_uint64 sampleCount, float factor); -MA_API void ma_copy_and_apply_volume_factor_s16(ma_int16* pSamplesOut, const ma_int16* pSamplesIn, ma_uint64 sampleCount, float factor); -MA_API void ma_copy_and_apply_volume_factor_s24(void* pSamplesOut, const void* pSamplesIn, ma_uint64 sampleCount, float factor); -MA_API void ma_copy_and_apply_volume_factor_s32(ma_int32* pSamplesOut, const ma_int32* pSamplesIn, ma_uint64 sampleCount, float factor); -MA_API void ma_copy_and_apply_volume_factor_f32(float* pSamplesOut, const float* pSamplesIn, ma_uint64 sampleCount, float factor); - -MA_API void ma_apply_volume_factor_u8(ma_uint8* pSamples, ma_uint64 sampleCount, float factor); -MA_API void ma_apply_volume_factor_s16(ma_int16* pSamples, ma_uint64 sampleCount, float factor); -MA_API void ma_apply_volume_factor_s24(void* pSamples, ma_uint64 sampleCount, float factor); -MA_API void ma_apply_volume_factor_s32(ma_int32* pSamples, ma_uint64 sampleCount, float factor); -MA_API void ma_apply_volume_factor_f32(float* pSamples, ma_uint64 sampleCount, float factor); - -MA_API void ma_copy_and_apply_volume_factor_pcm_frames_u8(ma_uint8* pFramesOut, const ma_uint8* pFramesIn, ma_uint64 frameCount, ma_uint32 channels, float factor); -MA_API void ma_copy_and_apply_volume_factor_pcm_frames_s16(ma_int16* pFramesOut, const ma_int16* pFramesIn, ma_uint64 frameCount, ma_uint32 channels, float factor); -MA_API void ma_copy_and_apply_volume_factor_pcm_frames_s24(void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount, ma_uint32 channels, float factor); -MA_API void ma_copy_and_apply_volume_factor_pcm_frames_s32(ma_int32* pFramesOut, const ma_int32* pFramesIn, ma_uint64 frameCount, ma_uint32 channels, float factor); -MA_API void ma_copy_and_apply_volume_factor_pcm_frames_f32(float* pFramesOut, const float* pFramesIn, ma_uint64 frameCount, ma_uint32 channels, float factor); -MA_API void ma_copy_and_apply_volume_factor_pcm_frames(void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount, ma_format format, ma_uint32 channels, float factor); - -MA_API void ma_apply_volume_factor_pcm_frames_u8(ma_uint8* pFrames, ma_uint64 frameCount, ma_uint32 channels, float factor); -MA_API void ma_apply_volume_factor_pcm_frames_s16(ma_int16* pFrames, ma_uint64 frameCount, ma_uint32 channels, float factor); -MA_API void ma_apply_volume_factor_pcm_frames_s24(void* pFrames, ma_uint64 frameCount, ma_uint32 channels, float factor); -MA_API void ma_apply_volume_factor_pcm_frames_s32(ma_int32* pFrames, ma_uint64 frameCount, ma_uint32 channels, float factor); -MA_API void ma_apply_volume_factor_pcm_frames_f32(float* pFrames, ma_uint64 frameCount, ma_uint32 channels, float factor); -MA_API void ma_apply_volume_factor_pcm_frames(void* pFrames, ma_uint64 frameCount, ma_format format, ma_uint32 channels, float factor); - -MA_API void ma_copy_and_apply_volume_factor_per_channel_f32(float* pFramesOut, const float* pFramesIn, ma_uint64 frameCount, ma_uint32 channels, float* pChannelGains); - - -MA_API void ma_copy_and_apply_volume_and_clip_samples_u8(ma_uint8* pDst, const ma_int16* pSrc, ma_uint64 count, float volume); -MA_API void ma_copy_and_apply_volume_and_clip_samples_s16(ma_int16* pDst, const ma_int32* pSrc, ma_uint64 count, float volume); -MA_API void ma_copy_and_apply_volume_and_clip_samples_s24(ma_uint8* pDst, const ma_int64* pSrc, ma_uint64 count, float volume); -MA_API void ma_copy_and_apply_volume_and_clip_samples_s32(ma_int32* pDst, const ma_int64* pSrc, ma_uint64 count, float volume); -MA_API void ma_copy_and_apply_volume_and_clip_samples_f32(float* pDst, const float* pSrc, ma_uint64 count, float volume); -MA_API void ma_copy_and_apply_volume_and_clip_pcm_frames(void* pDst, const void* pSrc, ma_uint64 frameCount, ma_format format, ma_uint32 channels, float volume); - - -/* -Helper for converting a linear factor to gain in decibels. -*/ -MA_API float ma_volume_linear_to_db(float factor); - -/* -Helper for converting gain in decibels to a linear factor. -*/ -MA_API float ma_volume_db_to_linear(float gain); - - -/* -Mixes the specified number of frames in floating point format with a volume factor. - -This will run on an optimized path when the volume is equal to 1. -*/ -MA_API ma_result ma_mix_pcm_frames_f32(float* pDst, const float* pSrc, ma_uint64 frameCount, ma_uint32 channels, float volume); - - - - -/************************************************************************************************************************************************************ - -VFS -=== - -The VFS object (virtual file system) is what's used to customize file access. This is useful in cases where stdio FILE* based APIs may not be entirely -appropriate for a given situation. - -************************************************************************************************************************************************************/ -typedef void ma_vfs; -typedef ma_handle ma_vfs_file; - -typedef enum -{ - MA_OPEN_MODE_READ = 0x00000001, - MA_OPEN_MODE_WRITE = 0x00000002 -} ma_open_mode_flags; - -typedef enum -{ - ma_seek_origin_start, - ma_seek_origin_current, - ma_seek_origin_end /* Not used by decoders. */ -} ma_seek_origin; - -typedef struct -{ - ma_uint64 sizeInBytes; -} ma_file_info; - -typedef struct -{ - ma_result (* onOpen) (ma_vfs* pVFS, const char* pFilePath, ma_uint32 openMode, ma_vfs_file* pFile); - ma_result (* onOpenW)(ma_vfs* pVFS, const wchar_t* pFilePath, ma_uint32 openMode, ma_vfs_file* pFile); - ma_result (* onClose)(ma_vfs* pVFS, ma_vfs_file file); - ma_result (* onRead) (ma_vfs* pVFS, ma_vfs_file file, void* pDst, size_t sizeInBytes, size_t* pBytesRead); - ma_result (* onWrite)(ma_vfs* pVFS, ma_vfs_file file, const void* pSrc, size_t sizeInBytes, size_t* pBytesWritten); - ma_result (* onSeek) (ma_vfs* pVFS, ma_vfs_file file, ma_int64 offset, ma_seek_origin origin); - ma_result (* onTell) (ma_vfs* pVFS, ma_vfs_file file, ma_int64* pCursor); - ma_result (* onInfo) (ma_vfs* pVFS, ma_vfs_file file, ma_file_info* pInfo); -} ma_vfs_callbacks; - -MA_API ma_result ma_vfs_open(ma_vfs* pVFS, const char* pFilePath, ma_uint32 openMode, ma_vfs_file* pFile); -MA_API ma_result ma_vfs_open_w(ma_vfs* pVFS, const wchar_t* pFilePath, ma_uint32 openMode, ma_vfs_file* pFile); -MA_API ma_result ma_vfs_close(ma_vfs* pVFS, ma_vfs_file file); -MA_API ma_result ma_vfs_read(ma_vfs* pVFS, ma_vfs_file file, void* pDst, size_t sizeInBytes, size_t* pBytesRead); -MA_API ma_result ma_vfs_write(ma_vfs* pVFS, ma_vfs_file file, const void* pSrc, size_t sizeInBytes, size_t* pBytesWritten); -MA_API ma_result ma_vfs_seek(ma_vfs* pVFS, ma_vfs_file file, ma_int64 offset, ma_seek_origin origin); -MA_API ma_result ma_vfs_tell(ma_vfs* pVFS, ma_vfs_file file, ma_int64* pCursor); -MA_API ma_result ma_vfs_info(ma_vfs* pVFS, ma_vfs_file file, ma_file_info* pInfo); -MA_API ma_result ma_vfs_open_and_read_file(ma_vfs* pVFS, const char* pFilePath, void** ppData, size_t* pSize, const ma_allocation_callbacks* pAllocationCallbacks); - -typedef struct -{ - ma_vfs_callbacks cb; - ma_allocation_callbacks allocationCallbacks; /* Only used for the wchar_t version of open() on non-Windows platforms. */ -} ma_default_vfs; - -MA_API ma_result ma_default_vfs_init(ma_default_vfs* pVFS, const ma_allocation_callbacks* pAllocationCallbacks); - - - -typedef ma_result (* ma_read_proc)(void* pUserData, void* pBufferOut, size_t bytesToRead, size_t* pBytesRead); -typedef ma_result (* ma_seek_proc)(void* pUserData, ma_int64 offset, ma_seek_origin origin); -typedef ma_result (* ma_tell_proc)(void* pUserData, ma_int64* pCursor); - - - -#if !defined(MA_NO_DECODING) || !defined(MA_NO_ENCODING) -typedef enum -{ - ma_encoding_format_unknown = 0, - ma_encoding_format_wav, - ma_encoding_format_flac, - ma_encoding_format_mp3, - ma_encoding_format_vorbis -} ma_encoding_format; -#endif - -/************************************************************************************************************************************************************ - -Decoding -======== - -Decoders are independent of the main device API. Decoding APIs can be called freely inside the device's data callback, but they are not thread safe unless -you do your own synchronization. - -************************************************************************************************************************************************************/ -#ifndef MA_NO_DECODING -typedef struct ma_decoder ma_decoder; - - -typedef struct -{ - ma_format preferredFormat; - ma_uint32 seekPointCount; /* Set to > 0 to generate a seektable if the decoding backend supports it. */ -} ma_decoding_backend_config; - -MA_API ma_decoding_backend_config ma_decoding_backend_config_init(ma_format preferredFormat, ma_uint32 seekPointCount); - - -typedef struct -{ - ma_result (* onInit )(void* pUserData, ma_read_proc onRead, ma_seek_proc onSeek, ma_tell_proc onTell, void* pReadSeekTellUserData, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_data_source** ppBackend); - ma_result (* onInitFile )(void* pUserData, const char* pFilePath, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_data_source** ppBackend); /* Optional. */ - ma_result (* onInitFileW )(void* pUserData, const wchar_t* pFilePath, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_data_source** ppBackend); /* Optional. */ - ma_result (* onInitMemory)(void* pUserData, const void* pData, size_t dataSize, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_data_source** ppBackend); /* Optional. */ - void (* onUninit )(void* pUserData, ma_data_source* pBackend, const ma_allocation_callbacks* pAllocationCallbacks); -} ma_decoding_backend_vtable; - - -typedef ma_result (* ma_decoder_read_proc)(ma_decoder* pDecoder, void* pBufferOut, size_t bytesToRead, size_t* pBytesRead); /* Returns the number of bytes read. */ -typedef ma_result (* ma_decoder_seek_proc)(ma_decoder* pDecoder, ma_int64 byteOffset, ma_seek_origin origin); -typedef ma_result (* ma_decoder_tell_proc)(ma_decoder* pDecoder, ma_int64* pCursor); - -typedef struct -{ - ma_format format; /* Set to 0 or ma_format_unknown to use the stream's internal format. */ - ma_uint32 channels; /* Set to 0 to use the stream's internal channels. */ - ma_uint32 sampleRate; /* Set to 0 to use the stream's internal sample rate. */ - ma_channel* pChannelMap; - ma_channel_mix_mode channelMixMode; - ma_dither_mode ditherMode; - ma_resampler_config resampling; - ma_allocation_callbacks allocationCallbacks; - ma_encoding_format encodingFormat; - ma_uint32 seekPointCount; /* When set to > 0, specifies the number of seek points to use for the generation of a seek table. Not all decoding backends support this. */ - ma_decoding_backend_vtable** ppCustomBackendVTables; - ma_uint32 customBackendCount; - void* pCustomBackendUserData; -} ma_decoder_config; - -struct ma_decoder -{ - ma_data_source_base ds; - ma_data_source* pBackend; /* The decoding backend we'll be pulling data from. */ - const ma_decoding_backend_vtable* pBackendVTable; /* The vtable for the decoding backend. This needs to be stored so we can access the onUninit() callback. */ - void* pBackendUserData; - ma_decoder_read_proc onRead; - ma_decoder_seek_proc onSeek; - ma_decoder_tell_proc onTell; - void* pUserData; - ma_uint64 readPointerInPCMFrames; /* In output sample rate. Used for keeping track of how many frames are available for decoding. */ - ma_format outputFormat; - ma_uint32 outputChannels; - ma_uint32 outputSampleRate; - ma_data_converter converter; /* Data conversion is achieved by running frames through this. */ - void* pInputCache; /* In input format. Can be null if it's not needed. */ - ma_uint64 inputCacheCap; /* The capacity of the input cache. */ - ma_uint64 inputCacheConsumed; /* The number of frames that have been consumed in the cache. Used for determining the next valid frame. */ - ma_uint64 inputCacheRemaining; /* The number of valid frames remaining in the cache. */ - ma_allocation_callbacks allocationCallbacks; - union - { - struct - { - ma_vfs* pVFS; - ma_vfs_file file; - } vfs; - struct - { - const ma_uint8* pData; - size_t dataSize; - size_t currentReadPos; - } memory; /* Only used for decoders that were opened against a block of memory. */ - } data; -}; - -MA_API ma_decoder_config ma_decoder_config_init(ma_format outputFormat, ma_uint32 outputChannels, ma_uint32 outputSampleRate); -MA_API ma_decoder_config ma_decoder_config_init_default(void); - -MA_API ma_result ma_decoder_init(ma_decoder_read_proc onRead, ma_decoder_seek_proc onSeek, void* pUserData, const ma_decoder_config* pConfig, ma_decoder* pDecoder); -MA_API ma_result ma_decoder_init_memory(const void* pData, size_t dataSize, const ma_decoder_config* pConfig, ma_decoder* pDecoder); -MA_API ma_result ma_decoder_init_vfs(ma_vfs* pVFS, const char* pFilePath, const ma_decoder_config* pConfig, ma_decoder* pDecoder); -MA_API ma_result ma_decoder_init_vfs_w(ma_vfs* pVFS, const wchar_t* pFilePath, const ma_decoder_config* pConfig, ma_decoder* pDecoder); -MA_API ma_result ma_decoder_init_file(const char* pFilePath, const ma_decoder_config* pConfig, ma_decoder* pDecoder); -MA_API ma_result ma_decoder_init_file_w(const wchar_t* pFilePath, const ma_decoder_config* pConfig, ma_decoder* pDecoder); - -/* -Uninitializes a decoder. -*/ -MA_API ma_result ma_decoder_uninit(ma_decoder* pDecoder); - -/* -Reads PCM frames from the given decoder. - -This is not thread safe without your own synchronization. -*/ -MA_API ma_result ma_decoder_read_pcm_frames(ma_decoder* pDecoder, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead); - -/* -Seeks to a PCM frame based on its absolute index. - -This is not thread safe without your own synchronization. -*/ -MA_API ma_result ma_decoder_seek_to_pcm_frame(ma_decoder* pDecoder, ma_uint64 frameIndex); - -/* -Retrieves the decoder's output data format. -*/ -MA_API ma_result ma_decoder_get_data_format(ma_decoder* pDecoder, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate, ma_channel* pChannelMap, size_t channelMapCap); - -/* -Retrieves the current position of the read cursor in PCM frames. -*/ -MA_API ma_result ma_decoder_get_cursor_in_pcm_frames(ma_decoder* pDecoder, ma_uint64* pCursor); - -/* -Retrieves the length of the decoder in PCM frames. - -Do not call this on streams of an undefined length, such as internet radio. - -If the length is unknown or an error occurs, 0 will be returned. - -This will always return 0 for Vorbis decoders. This is due to a limitation with stb_vorbis in push mode which is what miniaudio -uses internally. - -For MP3's, this will decode the entire file. Do not call this in time critical scenarios. - -This function is not thread safe without your own synchronization. -*/ -MA_API ma_result ma_decoder_get_length_in_pcm_frames(ma_decoder* pDecoder, ma_uint64* pLength); - -/* -Retrieves the number of frames that can be read before reaching the end. - -This calls `ma_decoder_get_length_in_pcm_frames()` so you need to be aware of the rules for that function, in -particular ensuring you do not call it on streams of an undefined length, such as internet radio. - -If the total length of the decoder cannot be retrieved, such as with Vorbis decoders, `MA_NOT_IMPLEMENTED` will be -returned. -*/ -MA_API ma_result ma_decoder_get_available_frames(ma_decoder* pDecoder, ma_uint64* pAvailableFrames); - -/* -Helper for opening and decoding a file into a heap allocated block of memory. Free the returned pointer with ma_free(). On input, -pConfig should be set to what you want. On output it will be set to what you got. -*/ -MA_API ma_result ma_decode_from_vfs(ma_vfs* pVFS, const char* pFilePath, ma_decoder_config* pConfig, ma_uint64* pFrameCountOut, void** ppPCMFramesOut); -MA_API ma_result ma_decode_file(const char* pFilePath, ma_decoder_config* pConfig, ma_uint64* pFrameCountOut, void** ppPCMFramesOut); -MA_API ma_result ma_decode_memory(const void* pData, size_t dataSize, ma_decoder_config* pConfig, ma_uint64* pFrameCountOut, void** ppPCMFramesOut); - -#endif /* MA_NO_DECODING */ - - -/************************************************************************************************************************************************************ - -Encoding -======== - -Encoders do not perform any format conversion for you. If your target format does not support the format, and error will be returned. - -************************************************************************************************************************************************************/ -#ifndef MA_NO_ENCODING -typedef struct ma_encoder ma_encoder; - -typedef ma_result (* ma_encoder_write_proc) (ma_encoder* pEncoder, const void* pBufferIn, size_t bytesToWrite, size_t* pBytesWritten); -typedef ma_result (* ma_encoder_seek_proc) (ma_encoder* pEncoder, ma_int64 offset, ma_seek_origin origin); -typedef ma_result (* ma_encoder_init_proc) (ma_encoder* pEncoder); -typedef void (* ma_encoder_uninit_proc) (ma_encoder* pEncoder); -typedef ma_result (* ma_encoder_write_pcm_frames_proc)(ma_encoder* pEncoder, const void* pFramesIn, ma_uint64 frameCount, ma_uint64* pFramesWritten); - -typedef struct -{ - ma_encoding_format encodingFormat; - ma_format format; - ma_uint32 channels; - ma_uint32 sampleRate; - ma_allocation_callbacks allocationCallbacks; -} ma_encoder_config; - -MA_API ma_encoder_config ma_encoder_config_init(ma_encoding_format encodingFormat, ma_format format, ma_uint32 channels, ma_uint32 sampleRate); - -struct ma_encoder -{ - ma_encoder_config config; - ma_encoder_write_proc onWrite; - ma_encoder_seek_proc onSeek; - ma_encoder_init_proc onInit; - ma_encoder_uninit_proc onUninit; - ma_encoder_write_pcm_frames_proc onWritePCMFrames; - void* pUserData; - void* pInternalEncoder; - union - { - struct - { - ma_vfs* pVFS; - ma_vfs_file file; - } vfs; - } data; -}; - -MA_API ma_result ma_encoder_init(ma_encoder_write_proc onWrite, ma_encoder_seek_proc onSeek, void* pUserData, const ma_encoder_config* pConfig, ma_encoder* pEncoder); -MA_API ma_result ma_encoder_init_vfs(ma_vfs* pVFS, const char* pFilePath, const ma_encoder_config* pConfig, ma_encoder* pEncoder); -MA_API ma_result ma_encoder_init_vfs_w(ma_vfs* pVFS, const wchar_t* pFilePath, const ma_encoder_config* pConfig, ma_encoder* pEncoder); -MA_API ma_result ma_encoder_init_file(const char* pFilePath, const ma_encoder_config* pConfig, ma_encoder* pEncoder); -MA_API ma_result ma_encoder_init_file_w(const wchar_t* pFilePath, const ma_encoder_config* pConfig, ma_encoder* pEncoder); -MA_API void ma_encoder_uninit(ma_encoder* pEncoder); -MA_API ma_result ma_encoder_write_pcm_frames(ma_encoder* pEncoder, const void* pFramesIn, ma_uint64 frameCount, ma_uint64* pFramesWritten); - -#endif /* MA_NO_ENCODING */ - - -/************************************************************************************************************************************************************ - -Generation - -************************************************************************************************************************************************************/ -#ifndef MA_NO_GENERATION -typedef enum -{ - ma_waveform_type_sine, - ma_waveform_type_square, - ma_waveform_type_triangle, - ma_waveform_type_sawtooth -} ma_waveform_type; - -typedef struct -{ - ma_format format; - ma_uint32 channels; - ma_uint32 sampleRate; - ma_waveform_type type; - double amplitude; - double frequency; -} ma_waveform_config; - -MA_API ma_waveform_config ma_waveform_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRate, ma_waveform_type type, double amplitude, double frequency); - -typedef struct -{ - ma_data_source_base ds; - ma_waveform_config config; - double advance; - double time; -} ma_waveform; - -MA_API ma_result ma_waveform_init(const ma_waveform_config* pConfig, ma_waveform* pWaveform); -MA_API void ma_waveform_uninit(ma_waveform* pWaveform); -MA_API ma_result ma_waveform_read_pcm_frames(ma_waveform* pWaveform, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead); -MA_API ma_result ma_waveform_seek_to_pcm_frame(ma_waveform* pWaveform, ma_uint64 frameIndex); -MA_API ma_result ma_waveform_set_amplitude(ma_waveform* pWaveform, double amplitude); -MA_API ma_result ma_waveform_set_frequency(ma_waveform* pWaveform, double frequency); -MA_API ma_result ma_waveform_set_type(ma_waveform* pWaveform, ma_waveform_type type); -MA_API ma_result ma_waveform_set_sample_rate(ma_waveform* pWaveform, ma_uint32 sampleRate); - -typedef struct -{ - ma_format format; - ma_uint32 channels; - ma_uint32 sampleRate; - double dutyCycle; - double amplitude; - double frequency; -} ma_pulsewave_config; - -MA_API ma_pulsewave_config ma_pulsewave_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRate, double dutyCycle, double amplitude, double frequency); - -typedef struct -{ - ma_waveform waveform; - ma_pulsewave_config config; -} ma_pulsewave; - -MA_API ma_result ma_pulsewave_init(const ma_pulsewave_config* pConfig, ma_pulsewave* pWaveform); -MA_API void ma_pulsewave_uninit(ma_pulsewave* pWaveform); -MA_API ma_result ma_pulsewave_read_pcm_frames(ma_pulsewave* pWaveform, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead); -MA_API ma_result ma_pulsewave_seek_to_pcm_frame(ma_pulsewave* pWaveform, ma_uint64 frameIndex); -MA_API ma_result ma_pulsewave_set_amplitude(ma_pulsewave* pWaveform, double amplitude); -MA_API ma_result ma_pulsewave_set_frequency(ma_pulsewave* pWaveform, double frequency); -MA_API ma_result ma_pulsewave_set_sample_rate(ma_pulsewave* pWaveform, ma_uint32 sampleRate); -MA_API ma_result ma_pulsewave_set_duty_cycle(ma_pulsewave* pWaveform, double dutyCycle); - -typedef enum -{ - ma_noise_type_white, - ma_noise_type_pink, - ma_noise_type_brownian -} ma_noise_type; - - -typedef struct -{ - ma_format format; - ma_uint32 channels; - ma_noise_type type; - ma_int32 seed; - double amplitude; - ma_bool32 duplicateChannels; -} ma_noise_config; - -MA_API ma_noise_config ma_noise_config_init(ma_format format, ma_uint32 channels, ma_noise_type type, ma_int32 seed, double amplitude); - -typedef struct -{ - ma_data_source_base ds; - ma_noise_config config; - ma_lcg lcg; - union - { - struct - { - double** bin; - double* accumulation; - ma_uint32* counter; - } pink; - struct - { - double* accumulation; - } brownian; - } state; - - /* Memory management. */ - void* _pHeap; - ma_bool32 _ownsHeap; -} ma_noise; - -MA_API ma_result ma_noise_get_heap_size(const ma_noise_config* pConfig, size_t* pHeapSizeInBytes); -MA_API ma_result ma_noise_init_preallocated(const ma_noise_config* pConfig, void* pHeap, ma_noise* pNoise); -MA_API ma_result ma_noise_init(const ma_noise_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_noise* pNoise); -MA_API void ma_noise_uninit(ma_noise* pNoise, const ma_allocation_callbacks* pAllocationCallbacks); -MA_API ma_result ma_noise_read_pcm_frames(ma_noise* pNoise, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead); -MA_API ma_result ma_noise_set_amplitude(ma_noise* pNoise, double amplitude); -MA_API ma_result ma_noise_set_seed(ma_noise* pNoise, ma_int32 seed); -MA_API ma_result ma_noise_set_type(ma_noise* pNoise, ma_noise_type type); - -#endif /* MA_NO_GENERATION */ - - - -/************************************************************************************************************************************************************ - -Resource Manager - -************************************************************************************************************************************************************/ -/* The resource manager cannot be enabled if there is no decoder. */ -#if !defined(MA_NO_RESOURCE_MANAGER) && defined(MA_NO_DECODING) -#define MA_NO_RESOURCE_MANAGER -#endif - -#ifndef MA_NO_RESOURCE_MANAGER -typedef struct ma_resource_manager ma_resource_manager; -typedef struct ma_resource_manager_data_buffer_node ma_resource_manager_data_buffer_node; -typedef struct ma_resource_manager_data_buffer ma_resource_manager_data_buffer; -typedef struct ma_resource_manager_data_stream ma_resource_manager_data_stream; -typedef struct ma_resource_manager_data_source ma_resource_manager_data_source; - -typedef enum -{ - MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_STREAM = 0x00000001, /* When set, does not load the entire data source in memory. Disk I/O will happen on job threads. */ - MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_DECODE = 0x00000002, /* Decode data before storing in memory. When set, decoding is done at the resource manager level rather than the mixing thread. Results in faster mixing, but higher memory usage. */ - MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_ASYNC = 0x00000004, /* When set, the resource manager will load the data source asynchronously. */ - MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_WAIT_INIT = 0x00000008, /* When set, waits for initialization of the underlying data source before returning from ma_resource_manager_data_source_init(). */ - MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_UNKNOWN_LENGTH = 0x00000010, /* Gives the resource manager a hint that the length of the data source is unknown and calling `ma_data_source_get_length_in_pcm_frames()` should be avoided. */ - MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_LOOPING = 0x00000020 /* When set, configures the data source to loop by default. */ -} ma_resource_manager_data_source_flags; - - -/* -Pipeline notifications used by the resource manager. Made up of both an async notification and a fence, both of which are optional. -*/ -typedef struct -{ - ma_async_notification* pNotification; - ma_fence* pFence; -} ma_resource_manager_pipeline_stage_notification; - -typedef struct -{ - ma_resource_manager_pipeline_stage_notification init; /* Initialization of the decoder. */ - ma_resource_manager_pipeline_stage_notification done; /* Decoding fully completed. */ -} ma_resource_manager_pipeline_notifications; - -MA_API ma_resource_manager_pipeline_notifications ma_resource_manager_pipeline_notifications_init(void); - - - -/* BEGIN BACKWARDS COMPATIBILITY */ -/* TODO: Remove this block in version 0.12. */ -#if 1 -#define ma_resource_manager_job ma_job -#define ma_resource_manager_job_init ma_job_init -#define MA_JOB_TYPE_RESOURCE_MANAGER_QUEUE_FLAG_NON_BLOCKING MA_JOB_QUEUE_FLAG_NON_BLOCKING -#define ma_resource_manager_job_queue_config ma_job_queue_config -#define ma_resource_manager_job_queue_config_init ma_job_queue_config_init -#define ma_resource_manager_job_queue ma_job_queue -#define ma_resource_manager_job_queue_get_heap_size ma_job_queue_get_heap_size -#define ma_resource_manager_job_queue_init_preallocated ma_job_queue_init_preallocated -#define ma_resource_manager_job_queue_init ma_job_queue_init -#define ma_resource_manager_job_queue_uninit ma_job_queue_uninit -#define ma_resource_manager_job_queue_post ma_job_queue_post -#define ma_resource_manager_job_queue_next ma_job_queue_next -#endif -/* END BACKWARDS COMPATIBILITY */ - - - - -/* Maximum job thread count will be restricted to this, but this may be removed later and replaced with a heap allocation thereby removing any limitation. */ -#ifndef MA_RESOURCE_MANAGER_MAX_JOB_THREAD_COUNT -#define MA_RESOURCE_MANAGER_MAX_JOB_THREAD_COUNT 64 -#endif - -typedef enum -{ - /* Indicates ma_resource_manager_next_job() should not block. Only valid when the job thread count is 0. */ - MA_RESOURCE_MANAGER_FLAG_NON_BLOCKING = 0x00000001, - - /* Disables any kind of multithreading. Implicitly enables MA_RESOURCE_MANAGER_FLAG_NON_BLOCKING. */ - MA_RESOURCE_MANAGER_FLAG_NO_THREADING = 0x00000002 -} ma_resource_manager_flags; - -typedef struct -{ - const char* pFilePath; - const wchar_t* pFilePathW; - const ma_resource_manager_pipeline_notifications* pNotifications; - ma_uint64 initialSeekPointInPCMFrames; - ma_uint64 rangeBegInPCMFrames; - ma_uint64 rangeEndInPCMFrames; - ma_uint64 loopPointBegInPCMFrames; - ma_uint64 loopPointEndInPCMFrames; - ma_uint32 flags; - ma_bool32 isLooping; /* Deprecated. Use the MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_LOOPING flag in `flags` instead. */ -} ma_resource_manager_data_source_config; - -MA_API ma_resource_manager_data_source_config ma_resource_manager_data_source_config_init(void); - - -typedef enum -{ - ma_resource_manager_data_supply_type_unknown = 0, /* Used for determining whether or the data supply has been initialized. */ - ma_resource_manager_data_supply_type_encoded, /* Data supply is an encoded buffer. Connector is ma_decoder. */ - ma_resource_manager_data_supply_type_decoded, /* Data supply is a decoded buffer. Connector is ma_audio_buffer. */ - ma_resource_manager_data_supply_type_decoded_paged /* Data supply is a linked list of decoded buffers. Connector is ma_paged_audio_buffer. */ -} ma_resource_manager_data_supply_type; - -typedef struct -{ - MA_ATOMIC(4, ma_resource_manager_data_supply_type) type; /* Read and written from different threads so needs to be accessed atomically. */ - union - { - struct - { - const void* pData; - size_t sizeInBytes; - } encoded; - struct - { - const void* pData; - ma_uint64 totalFrameCount; - ma_uint64 decodedFrameCount; - ma_format format; - ma_uint32 channels; - ma_uint32 sampleRate; - } decoded; - struct - { - ma_paged_audio_buffer_data data; - ma_uint64 decodedFrameCount; - ma_uint32 sampleRate; - } decodedPaged; - } backend; -} ma_resource_manager_data_supply; - -struct ma_resource_manager_data_buffer_node -{ - ma_uint32 hashedName32; /* The hashed name. This is the key. */ - ma_uint32 refCount; - MA_ATOMIC(4, ma_result) result; /* Result from asynchronous loading. When loading set to MA_BUSY. When fully loaded set to MA_SUCCESS. When deleting set to MA_UNAVAILABLE. */ - MA_ATOMIC(4, ma_uint32) executionCounter; /* For allocating execution orders for jobs. */ - MA_ATOMIC(4, ma_uint32) executionPointer; /* For managing the order of execution for asynchronous jobs relating to this object. Incremented as jobs complete processing. */ - ma_bool32 isDataOwnedByResourceManager; /* Set to true when the underlying data buffer was allocated the resource manager. Set to false if it is owned by the application (via ma_resource_manager_register_*()). */ - ma_resource_manager_data_supply data; - ma_resource_manager_data_buffer_node* pParent; - ma_resource_manager_data_buffer_node* pChildLo; - ma_resource_manager_data_buffer_node* pChildHi; -}; - -struct ma_resource_manager_data_buffer -{ - ma_data_source_base ds; /* Base data source. A data buffer is a data source. */ - ma_resource_manager* pResourceManager; /* A pointer to the resource manager that owns this buffer. */ - ma_resource_manager_data_buffer_node* pNode; /* The data node. This is reference counted and is what supplies the data. */ - ma_uint32 flags; /* The flags that were passed used to initialize the buffer. */ - MA_ATOMIC(4, ma_uint32) executionCounter; /* For allocating execution orders for jobs. */ - MA_ATOMIC(4, ma_uint32) executionPointer; /* For managing the order of execution for asynchronous jobs relating to this object. Incremented as jobs complete processing. */ - ma_uint64 seekTargetInPCMFrames; /* Only updated by the public API. Never written nor read from the job thread. */ - ma_bool32 seekToCursorOnNextRead; /* On the next read we need to seek to the frame cursor. */ - MA_ATOMIC(4, ma_result) result; /* Keeps track of a result of decoding. Set to MA_BUSY while the buffer is still loading. Set to MA_SUCCESS when loading is finished successfully. Otherwise set to some other code. */ - MA_ATOMIC(4, ma_bool32) isLooping; /* Can be read and written by different threads at the same time. Must be used atomically. */ - ma_atomic_bool32 isConnectorInitialized; /* Used for asynchronous loading to ensure we don't try to initialize the connector multiple times while waiting for the node to fully load. */ - union - { - ma_decoder decoder; /* Supply type is ma_resource_manager_data_supply_type_encoded */ - ma_audio_buffer buffer; /* Supply type is ma_resource_manager_data_supply_type_decoded */ - ma_paged_audio_buffer pagedBuffer; /* Supply type is ma_resource_manager_data_supply_type_decoded_paged */ - } connector; /* Connects this object to the node's data supply. */ -}; - -struct ma_resource_manager_data_stream -{ - ma_data_source_base ds; /* Base data source. A data stream is a data source. */ - ma_resource_manager* pResourceManager; /* A pointer to the resource manager that owns this data stream. */ - ma_uint32 flags; /* The flags that were passed used to initialize the stream. */ - ma_decoder decoder; /* Used for filling pages with data. This is only ever accessed by the job thread. The public API should never touch this. */ - ma_bool32 isDecoderInitialized; /* Required for determining whether or not the decoder should be uninitialized in MA_JOB_TYPE_RESOURCE_MANAGER_FREE_DATA_STREAM. */ - ma_uint64 totalLengthInPCMFrames; /* This is calculated when first loaded by the MA_JOB_TYPE_RESOURCE_MANAGER_LOAD_DATA_STREAM. */ - ma_uint32 relativeCursor; /* The playback cursor, relative to the current page. Only ever accessed by the public API. Never accessed by the job thread. */ - MA_ATOMIC(8, ma_uint64) absoluteCursor; /* The playback cursor, in absolute position starting from the start of the file. */ - ma_uint32 currentPageIndex; /* Toggles between 0 and 1. Index 0 is the first half of pPageData. Index 1 is the second half. Only ever accessed by the public API. Never accessed by the job thread. */ - MA_ATOMIC(4, ma_uint32) executionCounter; /* For allocating execution orders for jobs. */ - MA_ATOMIC(4, ma_uint32) executionPointer; /* For managing the order of execution for asynchronous jobs relating to this object. Incremented as jobs complete processing. */ - - /* Written by the public API, read by the job thread. */ - MA_ATOMIC(4, ma_bool32) isLooping; /* Whether or not the stream is looping. It's important to set the looping flag at the data stream level for smooth loop transitions. */ - - /* Written by the job thread, read by the public API. */ - void* pPageData; /* Buffer containing the decoded data of each page. Allocated once at initialization time. */ - MA_ATOMIC(4, ma_uint32) pageFrameCount[2]; /* The number of valid PCM frames in each page. Used to determine the last valid frame. */ - - /* Written and read by both the public API and the job thread. These must be atomic. */ - MA_ATOMIC(4, ma_result) result; /* Result from asynchronous loading. When loading set to MA_BUSY. When initialized set to MA_SUCCESS. When deleting set to MA_UNAVAILABLE. If an error occurs when loading, set to an error code. */ - MA_ATOMIC(4, ma_bool32) isDecoderAtEnd; /* Whether or not the decoder has reached the end. */ - MA_ATOMIC(4, ma_bool32) isPageValid[2]; /* Booleans to indicate whether or not a page is valid. Set to false by the public API, set to true by the job thread. Set to false as the pages are consumed, true when they are filled. */ - MA_ATOMIC(4, ma_bool32) seekCounter; /* When 0, no seeking is being performed. When > 0, a seek is being performed and reading should be delayed with MA_BUSY. */ -}; - -struct ma_resource_manager_data_source -{ - union - { - ma_resource_manager_data_buffer buffer; - ma_resource_manager_data_stream stream; - } backend; /* Must be the first item because we need the first item to be the data source callbacks for the buffer or stream. */ - - ma_uint32 flags; /* The flags that were passed in to ma_resource_manager_data_source_init(). */ - MA_ATOMIC(4, ma_uint32) executionCounter; /* For allocating execution orders for jobs. */ - MA_ATOMIC(4, ma_uint32) executionPointer; /* For managing the order of execution for asynchronous jobs relating to this object. Incremented as jobs complete processing. */ -}; - -typedef struct -{ - ma_allocation_callbacks allocationCallbacks; - ma_log* pLog; - ma_format decodedFormat; /* The decoded format to use. Set to ma_format_unknown (default) to use the file's native format. */ - ma_uint32 decodedChannels; /* The decoded channel count to use. Set to 0 (default) to use the file's native channel count. */ - ma_uint32 decodedSampleRate; /* the decoded sample rate to use. Set to 0 (default) to use the file's native sample rate. */ - ma_uint32 jobThreadCount; /* Set to 0 if you want to self-manage your job threads. Defaults to 1. */ - size_t jobThreadStackSize; - ma_uint32 jobQueueCapacity; /* The maximum number of jobs that can fit in the queue at a time. Defaults to MA_JOB_TYPE_RESOURCE_MANAGER_QUEUE_CAPACITY. Cannot be zero. */ - ma_uint32 flags; - ma_vfs* pVFS; /* Can be NULL in which case defaults will be used. */ - ma_decoding_backend_vtable** ppCustomDecodingBackendVTables; - ma_uint32 customDecodingBackendCount; - void* pCustomDecodingBackendUserData; -} ma_resource_manager_config; - -MA_API ma_resource_manager_config ma_resource_manager_config_init(void); - -struct ma_resource_manager -{ - ma_resource_manager_config config; - ma_resource_manager_data_buffer_node* pRootDataBufferNode; /* The root buffer in the binary tree. */ -#ifndef MA_NO_THREADING - ma_mutex dataBufferBSTLock; /* For synchronizing access to the data buffer binary tree. */ - ma_thread jobThreads[MA_RESOURCE_MANAGER_MAX_JOB_THREAD_COUNT]; /* The threads for executing jobs. */ -#endif - ma_job_queue jobQueue; /* Multi-consumer, multi-producer job queue for managing jobs for asynchronous decoding and streaming. */ - ma_default_vfs defaultVFS; /* Only used if a custom VFS is not specified. */ - ma_log log; /* Only used if no log was specified in the config. */ -}; - -/* Init. */ -MA_API ma_result ma_resource_manager_init(const ma_resource_manager_config* pConfig, ma_resource_manager* pResourceManager); -MA_API void ma_resource_manager_uninit(ma_resource_manager* pResourceManager); -MA_API ma_log* ma_resource_manager_get_log(ma_resource_manager* pResourceManager); - -/* Registration. */ -MA_API ma_result ma_resource_manager_register_file(ma_resource_manager* pResourceManager, const char* pFilePath, ma_uint32 flags); -MA_API ma_result ma_resource_manager_register_file_w(ma_resource_manager* pResourceManager, const wchar_t* pFilePath, ma_uint32 flags); -MA_API ma_result ma_resource_manager_register_decoded_data(ma_resource_manager* pResourceManager, const char* pName, const void* pData, ma_uint64 frameCount, ma_format format, ma_uint32 channels, ma_uint32 sampleRate); /* Does not copy. Increments the reference count if already exists and returns MA_SUCCESS. */ -MA_API ma_result ma_resource_manager_register_decoded_data_w(ma_resource_manager* pResourceManager, const wchar_t* pName, const void* pData, ma_uint64 frameCount, ma_format format, ma_uint32 channels, ma_uint32 sampleRate); -MA_API ma_result ma_resource_manager_register_encoded_data(ma_resource_manager* pResourceManager, const char* pName, const void* pData, size_t sizeInBytes); /* Does not copy. Increments the reference count if already exists and returns MA_SUCCESS. */ -MA_API ma_result ma_resource_manager_register_encoded_data_w(ma_resource_manager* pResourceManager, const wchar_t* pName, const void* pData, size_t sizeInBytes); -MA_API ma_result ma_resource_manager_unregister_file(ma_resource_manager* pResourceManager, const char* pFilePath); -MA_API ma_result ma_resource_manager_unregister_file_w(ma_resource_manager* pResourceManager, const wchar_t* pFilePath); -MA_API ma_result ma_resource_manager_unregister_data(ma_resource_manager* pResourceManager, const char* pName); -MA_API ma_result ma_resource_manager_unregister_data_w(ma_resource_manager* pResourceManager, const wchar_t* pName); - -/* Data Buffers. */ -MA_API ma_result ma_resource_manager_data_buffer_init_ex(ma_resource_manager* pResourceManager, const ma_resource_manager_data_source_config* pConfig, ma_resource_manager_data_buffer* pDataBuffer); -MA_API ma_result ma_resource_manager_data_buffer_init(ma_resource_manager* pResourceManager, const char* pFilePath, ma_uint32 flags, const ma_resource_manager_pipeline_notifications* pNotifications, ma_resource_manager_data_buffer* pDataBuffer); -MA_API ma_result ma_resource_manager_data_buffer_init_w(ma_resource_manager* pResourceManager, const wchar_t* pFilePath, ma_uint32 flags, const ma_resource_manager_pipeline_notifications* pNotifications, ma_resource_manager_data_buffer* pDataBuffer); -MA_API ma_result ma_resource_manager_data_buffer_init_copy(ma_resource_manager* pResourceManager, const ma_resource_manager_data_buffer* pExistingDataBuffer, ma_resource_manager_data_buffer* pDataBuffer); -MA_API ma_result ma_resource_manager_data_buffer_uninit(ma_resource_manager_data_buffer* pDataBuffer); -MA_API ma_result ma_resource_manager_data_buffer_read_pcm_frames(ma_resource_manager_data_buffer* pDataBuffer, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead); -MA_API ma_result ma_resource_manager_data_buffer_seek_to_pcm_frame(ma_resource_manager_data_buffer* pDataBuffer, ma_uint64 frameIndex); -MA_API ma_result ma_resource_manager_data_buffer_get_data_format(ma_resource_manager_data_buffer* pDataBuffer, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate, ma_channel* pChannelMap, size_t channelMapCap); -MA_API ma_result ma_resource_manager_data_buffer_get_cursor_in_pcm_frames(ma_resource_manager_data_buffer* pDataBuffer, ma_uint64* pCursor); -MA_API ma_result ma_resource_manager_data_buffer_get_length_in_pcm_frames(ma_resource_manager_data_buffer* pDataBuffer, ma_uint64* pLength); -MA_API ma_result ma_resource_manager_data_buffer_result(const ma_resource_manager_data_buffer* pDataBuffer); -MA_API ma_result ma_resource_manager_data_buffer_set_looping(ma_resource_manager_data_buffer* pDataBuffer, ma_bool32 isLooping); -MA_API ma_bool32 ma_resource_manager_data_buffer_is_looping(const ma_resource_manager_data_buffer* pDataBuffer); -MA_API ma_result ma_resource_manager_data_buffer_get_available_frames(ma_resource_manager_data_buffer* pDataBuffer, ma_uint64* pAvailableFrames); - -/* Data Streams. */ -MA_API ma_result ma_resource_manager_data_stream_init_ex(ma_resource_manager* pResourceManager, const ma_resource_manager_data_source_config* pConfig, ma_resource_manager_data_stream* pDataStream); -MA_API ma_result ma_resource_manager_data_stream_init(ma_resource_manager* pResourceManager, const char* pFilePath, ma_uint32 flags, const ma_resource_manager_pipeline_notifications* pNotifications, ma_resource_manager_data_stream* pDataStream); -MA_API ma_result ma_resource_manager_data_stream_init_w(ma_resource_manager* pResourceManager, const wchar_t* pFilePath, ma_uint32 flags, const ma_resource_manager_pipeline_notifications* pNotifications, ma_resource_manager_data_stream* pDataStream); -MA_API ma_result ma_resource_manager_data_stream_uninit(ma_resource_manager_data_stream* pDataStream); -MA_API ma_result ma_resource_manager_data_stream_read_pcm_frames(ma_resource_manager_data_stream* pDataStream, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead); -MA_API ma_result ma_resource_manager_data_stream_seek_to_pcm_frame(ma_resource_manager_data_stream* pDataStream, ma_uint64 frameIndex); -MA_API ma_result ma_resource_manager_data_stream_get_data_format(ma_resource_manager_data_stream* pDataStream, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate, ma_channel* pChannelMap, size_t channelMapCap); -MA_API ma_result ma_resource_manager_data_stream_get_cursor_in_pcm_frames(ma_resource_manager_data_stream* pDataStream, ma_uint64* pCursor); -MA_API ma_result ma_resource_manager_data_stream_get_length_in_pcm_frames(ma_resource_manager_data_stream* pDataStream, ma_uint64* pLength); -MA_API ma_result ma_resource_manager_data_stream_result(const ma_resource_manager_data_stream* pDataStream); -MA_API ma_result ma_resource_manager_data_stream_set_looping(ma_resource_manager_data_stream* pDataStream, ma_bool32 isLooping); -MA_API ma_bool32 ma_resource_manager_data_stream_is_looping(const ma_resource_manager_data_stream* pDataStream); -MA_API ma_result ma_resource_manager_data_stream_get_available_frames(ma_resource_manager_data_stream* pDataStream, ma_uint64* pAvailableFrames); - -/* Data Sources. */ -MA_API ma_result ma_resource_manager_data_source_init_ex(ma_resource_manager* pResourceManager, const ma_resource_manager_data_source_config* pConfig, ma_resource_manager_data_source* pDataSource); -MA_API ma_result ma_resource_manager_data_source_init(ma_resource_manager* pResourceManager, const char* pName, ma_uint32 flags, const ma_resource_manager_pipeline_notifications* pNotifications, ma_resource_manager_data_source* pDataSource); -MA_API ma_result ma_resource_manager_data_source_init_w(ma_resource_manager* pResourceManager, const wchar_t* pName, ma_uint32 flags, const ma_resource_manager_pipeline_notifications* pNotifications, ma_resource_manager_data_source* pDataSource); -MA_API ma_result ma_resource_manager_data_source_init_copy(ma_resource_manager* pResourceManager, const ma_resource_manager_data_source* pExistingDataSource, ma_resource_manager_data_source* pDataSource); -MA_API ma_result ma_resource_manager_data_source_uninit(ma_resource_manager_data_source* pDataSource); -MA_API ma_result ma_resource_manager_data_source_read_pcm_frames(ma_resource_manager_data_source* pDataSource, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead); -MA_API ma_result ma_resource_manager_data_source_seek_to_pcm_frame(ma_resource_manager_data_source* pDataSource, ma_uint64 frameIndex); -MA_API ma_result ma_resource_manager_data_source_get_data_format(ma_resource_manager_data_source* pDataSource, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate, ma_channel* pChannelMap, size_t channelMapCap); -MA_API ma_result ma_resource_manager_data_source_get_cursor_in_pcm_frames(ma_resource_manager_data_source* pDataSource, ma_uint64* pCursor); -MA_API ma_result ma_resource_manager_data_source_get_length_in_pcm_frames(ma_resource_manager_data_source* pDataSource, ma_uint64* pLength); -MA_API ma_result ma_resource_manager_data_source_result(const ma_resource_manager_data_source* pDataSource); -MA_API ma_result ma_resource_manager_data_source_set_looping(ma_resource_manager_data_source* pDataSource, ma_bool32 isLooping); -MA_API ma_bool32 ma_resource_manager_data_source_is_looping(const ma_resource_manager_data_source* pDataSource); -MA_API ma_result ma_resource_manager_data_source_get_available_frames(ma_resource_manager_data_source* pDataSource, ma_uint64* pAvailableFrames); - -/* Job management. */ -MA_API ma_result ma_resource_manager_post_job(ma_resource_manager* pResourceManager, const ma_job* pJob); -MA_API ma_result ma_resource_manager_post_job_quit(ma_resource_manager* pResourceManager); /* Helper for posting a quit job. */ -MA_API ma_result ma_resource_manager_next_job(ma_resource_manager* pResourceManager, ma_job* pJob); -MA_API ma_result ma_resource_manager_process_job(ma_resource_manager* pResourceManager, ma_job* pJob); /* DEPRECATED. Use ma_job_process(). Will be removed in version 0.12. */ -MA_API ma_result ma_resource_manager_process_next_job(ma_resource_manager* pResourceManager); /* Returns MA_CANCELLED if a MA_JOB_TYPE_QUIT job is found. In non-blocking mode, returns MA_NO_DATA_AVAILABLE if no jobs are available. */ -#endif /* MA_NO_RESOURCE_MANAGER */ - - - -/************************************************************************************************************************************************************ - -Node Graph - -************************************************************************************************************************************************************/ -#ifndef MA_NO_NODE_GRAPH -/* Must never exceed 254. */ -#ifndef MA_MAX_NODE_BUS_COUNT -#define MA_MAX_NODE_BUS_COUNT 254 -#endif - -/* Used internally by miniaudio for memory management. Must never exceed MA_MAX_NODE_BUS_COUNT. */ -#ifndef MA_MAX_NODE_LOCAL_BUS_COUNT -#define MA_MAX_NODE_LOCAL_BUS_COUNT 2 -#endif - -/* Use this when the bus count is determined by the node instance rather than the vtable. */ -#define MA_NODE_BUS_COUNT_UNKNOWN 255 - - -/* For some internal memory management of ma_node_graph. */ -typedef struct -{ - size_t offset; - size_t sizeInBytes; - unsigned char _data[1]; -} ma_stack; - - -typedef struct ma_node_graph ma_node_graph; -typedef void ma_node; - - -/* Node flags. */ -typedef enum -{ - MA_NODE_FLAG_PASSTHROUGH = 0x00000001, - MA_NODE_FLAG_CONTINUOUS_PROCESSING = 0x00000002, - MA_NODE_FLAG_ALLOW_NULL_INPUT = 0x00000004, - MA_NODE_FLAG_DIFFERENT_PROCESSING_RATES = 0x00000008, - MA_NODE_FLAG_SILENT_OUTPUT = 0x00000010 -} ma_node_flags; - - -/* The playback state of a node. Either started or stopped. */ -typedef enum -{ - ma_node_state_started = 0, - ma_node_state_stopped = 1 -} ma_node_state; - - -typedef struct -{ - /* - Extended processing callback. This callback is used for effects that process input and output - at different rates (i.e. they perform resampling). This is similar to the simple version, only - they take two separate frame counts: one for input, and one for output. - - On input, `pFrameCountOut` is equal to the capacity of the output buffer for each bus, whereas - `pFrameCountIn` will be equal to the number of PCM frames in each of the buffers in `ppFramesIn`. - - On output, set `pFrameCountOut` to the number of PCM frames that were actually output and set - `pFrameCountIn` to the number of input frames that were consumed. - */ - void (* onProcess)(ma_node* pNode, const float** ppFramesIn, ma_uint32* pFrameCountIn, float** ppFramesOut, ma_uint32* pFrameCountOut); - - /* - A callback for retrieving the number of input frames that are required to output the - specified number of output frames. You would only want to implement this when the node performs - resampling. This is optional, even for nodes that perform resampling, but it does offer a - small reduction in latency as it allows miniaudio to calculate the exact number of input frames - to read at a time instead of having to estimate. - */ - ma_result (* onGetRequiredInputFrameCount)(ma_node* pNode, ma_uint32 outputFrameCount, ma_uint32* pInputFrameCount); - - /* - The number of input buses. This is how many sub-buffers will be contained in the `ppFramesIn` - parameters of the callbacks above. - */ - ma_uint8 inputBusCount; - - /* - The number of output buses. This is how many sub-buffers will be contained in the `ppFramesOut` - parameters of the callbacks above. - */ - ma_uint8 outputBusCount; - - /* - Flags describing characteristics of the node. This is currently just a placeholder for some - ideas for later on. - */ - ma_uint32 flags; -} ma_node_vtable; - -typedef struct -{ - const ma_node_vtable* vtable; /* Should never be null. Initialization of the node will fail if so. */ - ma_node_state initialState; /* Defaults to ma_node_state_started. */ - ma_uint32 inputBusCount; /* Only used if the vtable specifies an input bus count of `MA_NODE_BUS_COUNT_UNKNOWN`, otherwise must be set to `MA_NODE_BUS_COUNT_UNKNOWN` (default). */ - ma_uint32 outputBusCount; /* Only used if the vtable specifies an output bus count of `MA_NODE_BUS_COUNT_UNKNOWN`, otherwise be set to `MA_NODE_BUS_COUNT_UNKNOWN` (default). */ - const ma_uint32* pInputChannels; /* The number of elements are determined by the input bus count as determined by the vtable, or `inputBusCount` if the vtable specifies `MA_NODE_BUS_COUNT_UNKNOWN`. */ - const ma_uint32* pOutputChannels; /* The number of elements are determined by the output bus count as determined by the vtable, or `outputBusCount` if the vtable specifies `MA_NODE_BUS_COUNT_UNKNOWN`. */ -} ma_node_config; - -MA_API ma_node_config ma_node_config_init(void); - - -/* -A node has multiple output buses. An output bus is attached to an input bus as an item in a linked -list. Think of the input bus as a linked list, with the output bus being an item in that list. -*/ -typedef struct ma_node_output_bus ma_node_output_bus; -struct ma_node_output_bus -{ - /* Immutable. */ - ma_node* pNode; /* The node that owns this output bus. The input node. Will be null for dummy head and tail nodes. */ - ma_uint8 outputBusIndex; /* The index of the output bus on pNode that this output bus represents. */ - ma_uint8 channels; /* The number of channels in the audio stream for this bus. */ - - /* Mutable via multiple threads. Must be used atomically. The weird ordering here is for packing reasons. */ - ma_uint8 inputNodeInputBusIndex; /* The index of the input bus on the input. Required for detaching. Will only be used within the spinlock so does not need to be atomic. */ - MA_ATOMIC(4, ma_uint32) flags; /* Some state flags for tracking the read state of the output buffer. A combination of MA_NODE_OUTPUT_BUS_FLAG_*. */ - MA_ATOMIC(4, ma_uint32) refCount; /* Reference count for some thread-safety when detaching. */ - MA_ATOMIC(4, ma_bool32) isAttached; /* This is used to prevent iteration of nodes that are in the middle of being detached. Used for thread safety. */ - MA_ATOMIC(4, ma_spinlock) lock; /* Unfortunate lock, but significantly simplifies the implementation. Required for thread-safe attaching and detaching. */ - MA_ATOMIC(4, float) volume; /* Linear. */ - MA_ATOMIC(MA_SIZEOF_PTR, ma_node_output_bus*) pNext; /* If null, it's the tail node or detached. */ - MA_ATOMIC(MA_SIZEOF_PTR, ma_node_output_bus*) pPrev; /* If null, it's the head node or detached. */ - MA_ATOMIC(MA_SIZEOF_PTR, ma_node*) pInputNode; /* The node that this output bus is attached to. Required for detaching. */ -}; - -/* -A node has multiple input buses. The output buses of a node are connecting to the input busses of -another. An input bus is essentially just a linked list of output buses. -*/ -typedef struct ma_node_input_bus ma_node_input_bus; -struct ma_node_input_bus -{ - /* Mutable via multiple threads. */ - ma_node_output_bus head; /* Dummy head node for simplifying some lock-free thread-safety stuff. */ - MA_ATOMIC(4, ma_uint32) nextCounter; /* This is used to determine whether or not the input bus is finding the next node in the list. Used for thread safety when detaching output buses. */ - MA_ATOMIC(4, ma_spinlock) lock; /* Unfortunate lock, but significantly simplifies the implementation. Required for thread-safe attaching and detaching. */ - - /* Set once at startup. */ - ma_uint8 channels; /* The number of channels in the audio stream for this bus. */ -}; - - -typedef struct ma_node_base ma_node_base; -struct ma_node_base -{ - /* These variables are set once at startup. */ - ma_node_graph* pNodeGraph; /* The graph this node belongs to. */ - const ma_node_vtable* vtable; - ma_uint32 inputBusCount; - ma_uint32 outputBusCount; - ma_node_input_bus* pInputBuses; - ma_node_output_bus* pOutputBuses; - float* pCachedData; /* Allocated on the heap. Fixed size. Needs to be stored on the heap because reading from output buses is done in separate function calls. */ - ma_uint16 cachedDataCapInFramesPerBus; /* The capacity of the input data cache in frames, per bus. */ - - /* These variables are read and written only from the audio thread. */ - ma_uint16 cachedFrameCountOut; - ma_uint16 cachedFrameCountIn; - ma_uint16 consumedFrameCountIn; - - /* These variables are read and written between different threads. */ - MA_ATOMIC(4, ma_node_state) state; /* When set to stopped, nothing will be read, regardless of the times in stateTimes. */ - MA_ATOMIC(8, ma_uint64) stateTimes[2]; /* Indexed by ma_node_state. Specifies the time based on the global clock that a node should be considered to be in the relevant state. */ - MA_ATOMIC(8, ma_uint64) localTime; /* The node's local clock. This is just a running sum of the number of output frames that have been processed. Can be modified by any thread with `ma_node_set_time()`. */ - - /* Memory management. */ - ma_node_input_bus _inputBuses[MA_MAX_NODE_LOCAL_BUS_COUNT]; - ma_node_output_bus _outputBuses[MA_MAX_NODE_LOCAL_BUS_COUNT]; - void* _pHeap; /* A heap allocation for internal use only. pInputBuses and/or pOutputBuses will point to this if the bus count exceeds MA_MAX_NODE_LOCAL_BUS_COUNT. */ - ma_bool32 _ownsHeap; /* If set to true, the node owns the heap allocation and _pHeap will be freed in ma_node_uninit(). */ -}; - -MA_API ma_result ma_node_get_heap_size(ma_node_graph* pNodeGraph, const ma_node_config* pConfig, size_t* pHeapSizeInBytes); -MA_API ma_result ma_node_init_preallocated(ma_node_graph* pNodeGraph, const ma_node_config* pConfig, void* pHeap, ma_node* pNode); -MA_API ma_result ma_node_init(ma_node_graph* pNodeGraph, const ma_node_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_node* pNode); -MA_API void ma_node_uninit(ma_node* pNode, const ma_allocation_callbacks* pAllocationCallbacks); -MA_API ma_node_graph* ma_node_get_node_graph(const ma_node* pNode); -MA_API ma_uint32 ma_node_get_input_bus_count(const ma_node* pNode); -MA_API ma_uint32 ma_node_get_output_bus_count(const ma_node* pNode); -MA_API ma_uint32 ma_node_get_input_channels(const ma_node* pNode, ma_uint32 inputBusIndex); -MA_API ma_uint32 ma_node_get_output_channels(const ma_node* pNode, ma_uint32 outputBusIndex); -MA_API ma_result ma_node_attach_output_bus(ma_node* pNode, ma_uint32 outputBusIndex, ma_node* pOtherNode, ma_uint32 otherNodeInputBusIndex); -MA_API ma_result ma_node_detach_output_bus(ma_node* pNode, ma_uint32 outputBusIndex); -MA_API ma_result ma_node_detach_all_output_buses(ma_node* pNode); -MA_API ma_result ma_node_set_output_bus_volume(ma_node* pNode, ma_uint32 outputBusIndex, float volume); -MA_API float ma_node_get_output_bus_volume(const ma_node* pNode, ma_uint32 outputBusIndex); -MA_API ma_result ma_node_set_state(ma_node* pNode, ma_node_state state); -MA_API ma_node_state ma_node_get_state(const ma_node* pNode); -MA_API ma_result ma_node_set_state_time(ma_node* pNode, ma_node_state state, ma_uint64 globalTime); -MA_API ma_uint64 ma_node_get_state_time(const ma_node* pNode, ma_node_state state); -MA_API ma_node_state ma_node_get_state_by_time(const ma_node* pNode, ma_uint64 globalTime); -MA_API ma_node_state ma_node_get_state_by_time_range(const ma_node* pNode, ma_uint64 globalTimeBeg, ma_uint64 globalTimeEnd); -MA_API ma_uint64 ma_node_get_time(const ma_node* pNode); -MA_API ma_result ma_node_set_time(ma_node* pNode, ma_uint64 localTime); - - -typedef struct -{ - ma_uint32 channels; - ma_uint32 processingSizeInFrames; /* This is the preferred processing size for node processing callbacks unless overridden by a node itself. Can be 0 in which case it will be based on the frame count passed into ma_node_graph_read_pcm_frames(), but will not be well defined. */ - size_t preMixStackSizeInBytes; /* Defaults to 512KB per channel. Reducing this will save memory, but the depth of your node graph will be more restricted. */ -} ma_node_graph_config; - -MA_API ma_node_graph_config ma_node_graph_config_init(ma_uint32 channels); - - -struct ma_node_graph -{ - /* Immutable. */ - ma_node_base base; /* The node graph itself is a node so it can be connected as an input to different node graph. This has zero inputs and calls ma_node_graph_read_pcm_frames() to generate it's output. */ - ma_node_base endpoint; /* Special node that all nodes eventually connect to. Data is read from this node in ma_node_graph_read_pcm_frames(). */ - float* pProcessingCache; /* This will be allocated when processingSizeInFrames is non-zero. This is needed because ma_node_graph_read_pcm_frames() can be called with a variable number of frames, and we may need to do some buffering in situations where the caller requests a frame count that's not a multiple of processingSizeInFrames. */ - ma_uint32 processingCacheFramesRemaining; - ma_uint32 processingSizeInFrames; - - /* Read and written by multiple threads. */ - MA_ATOMIC(4, ma_bool32) isReading; - - /* Modified only by the audio thread. */ - ma_stack* pPreMixStack; -}; - -MA_API ma_result ma_node_graph_init(const ma_node_graph_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_node_graph* pNodeGraph); -MA_API void ma_node_graph_uninit(ma_node_graph* pNodeGraph, const ma_allocation_callbacks* pAllocationCallbacks); -MA_API ma_node* ma_node_graph_get_endpoint(ma_node_graph* pNodeGraph); -MA_API ma_result ma_node_graph_read_pcm_frames(ma_node_graph* pNodeGraph, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead); -MA_API ma_uint32 ma_node_graph_get_channels(const ma_node_graph* pNodeGraph); -MA_API ma_uint64 ma_node_graph_get_time(const ma_node_graph* pNodeGraph); -MA_API ma_result ma_node_graph_set_time(ma_node_graph* pNodeGraph, ma_uint64 globalTime); - - - -/* Data source node. 0 input buses, 1 output bus. Used for reading from a data source. */ -typedef struct -{ - ma_node_config nodeConfig; - ma_data_source* pDataSource; -} ma_data_source_node_config; - -MA_API ma_data_source_node_config ma_data_source_node_config_init(ma_data_source* pDataSource); - - -typedef struct -{ - ma_node_base base; - ma_data_source* pDataSource; -} ma_data_source_node; - -MA_API ma_result ma_data_source_node_init(ma_node_graph* pNodeGraph, const ma_data_source_node_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_data_source_node* pDataSourceNode); -MA_API void ma_data_source_node_uninit(ma_data_source_node* pDataSourceNode, const ma_allocation_callbacks* pAllocationCallbacks); -MA_API ma_result ma_data_source_node_set_looping(ma_data_source_node* pDataSourceNode, ma_bool32 isLooping); -MA_API ma_bool32 ma_data_source_node_is_looping(ma_data_source_node* pDataSourceNode); - - -/* Splitter Node. 1 input, many outputs. Used for splitting/copying a stream so it can be as input into two separate output nodes. */ -typedef struct -{ - ma_node_config nodeConfig; - ma_uint32 channels; - ma_uint32 outputBusCount; -} ma_splitter_node_config; - -MA_API ma_splitter_node_config ma_splitter_node_config_init(ma_uint32 channels); - - -typedef struct -{ - ma_node_base base; -} ma_splitter_node; - -MA_API ma_result ma_splitter_node_init(ma_node_graph* pNodeGraph, const ma_splitter_node_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_splitter_node* pSplitterNode); -MA_API void ma_splitter_node_uninit(ma_splitter_node* pSplitterNode, const ma_allocation_callbacks* pAllocationCallbacks); - - -/* -Biquad Node -*/ -typedef struct -{ - ma_node_config nodeConfig; - ma_biquad_config biquad; -} ma_biquad_node_config; - -MA_API ma_biquad_node_config ma_biquad_node_config_init(ma_uint32 channels, float b0, float b1, float b2, float a0, float a1, float a2); - - -typedef struct -{ - ma_node_base baseNode; - ma_biquad biquad; -} ma_biquad_node; - -MA_API ma_result ma_biquad_node_init(ma_node_graph* pNodeGraph, const ma_biquad_node_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_biquad_node* pNode); -MA_API ma_result ma_biquad_node_reinit(const ma_biquad_config* pConfig, ma_biquad_node* pNode); -MA_API void ma_biquad_node_uninit(ma_biquad_node* pNode, const ma_allocation_callbacks* pAllocationCallbacks); - - -/* -Low Pass Filter Node -*/ -typedef struct -{ - ma_node_config nodeConfig; - ma_lpf_config lpf; -} ma_lpf_node_config; - -MA_API ma_lpf_node_config ma_lpf_node_config_init(ma_uint32 channels, ma_uint32 sampleRate, double cutoffFrequency, ma_uint32 order); - - -typedef struct -{ - ma_node_base baseNode; - ma_lpf lpf; -} ma_lpf_node; - -MA_API ma_result ma_lpf_node_init(ma_node_graph* pNodeGraph, const ma_lpf_node_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_lpf_node* pNode); -MA_API ma_result ma_lpf_node_reinit(const ma_lpf_config* pConfig, ma_lpf_node* pNode); -MA_API void ma_lpf_node_uninit(ma_lpf_node* pNode, const ma_allocation_callbacks* pAllocationCallbacks); - - -/* -High Pass Filter Node -*/ -typedef struct -{ - ma_node_config nodeConfig; - ma_hpf_config hpf; -} ma_hpf_node_config; - -MA_API ma_hpf_node_config ma_hpf_node_config_init(ma_uint32 channels, ma_uint32 sampleRate, double cutoffFrequency, ma_uint32 order); - - -typedef struct -{ - ma_node_base baseNode; - ma_hpf hpf; -} ma_hpf_node; - -MA_API ma_result ma_hpf_node_init(ma_node_graph* pNodeGraph, const ma_hpf_node_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_hpf_node* pNode); -MA_API ma_result ma_hpf_node_reinit(const ma_hpf_config* pConfig, ma_hpf_node* pNode); -MA_API void ma_hpf_node_uninit(ma_hpf_node* pNode, const ma_allocation_callbacks* pAllocationCallbacks); - - -/* -Band Pass Filter Node -*/ -typedef struct -{ - ma_node_config nodeConfig; - ma_bpf_config bpf; -} ma_bpf_node_config; - -MA_API ma_bpf_node_config ma_bpf_node_config_init(ma_uint32 channels, ma_uint32 sampleRate, double cutoffFrequency, ma_uint32 order); - - -typedef struct -{ - ma_node_base baseNode; - ma_bpf bpf; -} ma_bpf_node; - -MA_API ma_result ma_bpf_node_init(ma_node_graph* pNodeGraph, const ma_bpf_node_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_bpf_node* pNode); -MA_API ma_result ma_bpf_node_reinit(const ma_bpf_config* pConfig, ma_bpf_node* pNode); -MA_API void ma_bpf_node_uninit(ma_bpf_node* pNode, const ma_allocation_callbacks* pAllocationCallbacks); - - -/* -Notching Filter Node -*/ -typedef struct -{ - ma_node_config nodeConfig; - ma_notch_config notch; -} ma_notch_node_config; - -MA_API ma_notch_node_config ma_notch_node_config_init(ma_uint32 channels, ma_uint32 sampleRate, double q, double frequency); - - -typedef struct -{ - ma_node_base baseNode; - ma_notch2 notch; -} ma_notch_node; - -MA_API ma_result ma_notch_node_init(ma_node_graph* pNodeGraph, const ma_notch_node_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_notch_node* pNode); -MA_API ma_result ma_notch_node_reinit(const ma_notch_config* pConfig, ma_notch_node* pNode); -MA_API void ma_notch_node_uninit(ma_notch_node* pNode, const ma_allocation_callbacks* pAllocationCallbacks); - - -/* -Peaking Filter Node -*/ -typedef struct -{ - ma_node_config nodeConfig; - ma_peak_config peak; -} ma_peak_node_config; - -MA_API ma_peak_node_config ma_peak_node_config_init(ma_uint32 channels, ma_uint32 sampleRate, double gainDB, double q, double frequency); - - -typedef struct -{ - ma_node_base baseNode; - ma_peak2 peak; -} ma_peak_node; - -MA_API ma_result ma_peak_node_init(ma_node_graph* pNodeGraph, const ma_peak_node_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_peak_node* pNode); -MA_API ma_result ma_peak_node_reinit(const ma_peak_config* pConfig, ma_peak_node* pNode); -MA_API void ma_peak_node_uninit(ma_peak_node* pNode, const ma_allocation_callbacks* pAllocationCallbacks); - - -/* -Low Shelf Filter Node -*/ -typedef struct -{ - ma_node_config nodeConfig; - ma_loshelf_config loshelf; -} ma_loshelf_node_config; - -MA_API ma_loshelf_node_config ma_loshelf_node_config_init(ma_uint32 channels, ma_uint32 sampleRate, double gainDB, double q, double frequency); - - -typedef struct -{ - ma_node_base baseNode; - ma_loshelf2 loshelf; -} ma_loshelf_node; - -MA_API ma_result ma_loshelf_node_init(ma_node_graph* pNodeGraph, const ma_loshelf_node_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_loshelf_node* pNode); -MA_API ma_result ma_loshelf_node_reinit(const ma_loshelf_config* pConfig, ma_loshelf_node* pNode); -MA_API void ma_loshelf_node_uninit(ma_loshelf_node* pNode, const ma_allocation_callbacks* pAllocationCallbacks); - - -/* -High Shelf Filter Node -*/ -typedef struct -{ - ma_node_config nodeConfig; - ma_hishelf_config hishelf; -} ma_hishelf_node_config; - -MA_API ma_hishelf_node_config ma_hishelf_node_config_init(ma_uint32 channels, ma_uint32 sampleRate, double gainDB, double q, double frequency); - - -typedef struct -{ - ma_node_base baseNode; - ma_hishelf2 hishelf; -} ma_hishelf_node; - -MA_API ma_result ma_hishelf_node_init(ma_node_graph* pNodeGraph, const ma_hishelf_node_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_hishelf_node* pNode); -MA_API ma_result ma_hishelf_node_reinit(const ma_hishelf_config* pConfig, ma_hishelf_node* pNode); -MA_API void ma_hishelf_node_uninit(ma_hishelf_node* pNode, const ma_allocation_callbacks* pAllocationCallbacks); - - -typedef struct -{ - ma_node_config nodeConfig; - ma_delay_config delay; -} ma_delay_node_config; - -MA_API ma_delay_node_config ma_delay_node_config_init(ma_uint32 channels, ma_uint32 sampleRate, ma_uint32 delayInFrames, float decay); - - -typedef struct -{ - ma_node_base baseNode; - ma_delay delay; -} ma_delay_node; - -MA_API ma_result ma_delay_node_init(ma_node_graph* pNodeGraph, const ma_delay_node_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_delay_node* pDelayNode); -MA_API void ma_delay_node_uninit(ma_delay_node* pDelayNode, const ma_allocation_callbacks* pAllocationCallbacks); -MA_API void ma_delay_node_set_wet(ma_delay_node* pDelayNode, float value); -MA_API float ma_delay_node_get_wet(const ma_delay_node* pDelayNode); -MA_API void ma_delay_node_set_dry(ma_delay_node* pDelayNode, float value); -MA_API float ma_delay_node_get_dry(const ma_delay_node* pDelayNode); -MA_API void ma_delay_node_set_decay(ma_delay_node* pDelayNode, float value); -MA_API float ma_delay_node_get_decay(const ma_delay_node* pDelayNode); -#endif /* MA_NO_NODE_GRAPH */ - - -/* SECTION: miniaudio_engine.h */ -/************************************************************************************************************************************************************ - -Engine - -************************************************************************************************************************************************************/ -#if !defined(MA_NO_ENGINE) && !defined(MA_NO_NODE_GRAPH) -typedef struct ma_engine ma_engine; -typedef struct ma_sound ma_sound; - - -/* Sound flags. */ -typedef enum -{ - /* Resource manager flags. */ - MA_SOUND_FLAG_STREAM = 0x00000001, /* MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_STREAM */ - MA_SOUND_FLAG_DECODE = 0x00000002, /* MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_DECODE */ - MA_SOUND_FLAG_ASYNC = 0x00000004, /* MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_ASYNC */ - MA_SOUND_FLAG_WAIT_INIT = 0x00000008, /* MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_WAIT_INIT */ - MA_SOUND_FLAG_UNKNOWN_LENGTH = 0x00000010, /* MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_UNKNOWN_LENGTH */ - MA_SOUND_FLAG_LOOPING = 0x00000020, /* MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_LOOPING */ - - /* ma_sound specific flags. */ - MA_SOUND_FLAG_NO_DEFAULT_ATTACHMENT = 0x00001000, /* Do not attach to the endpoint by default. Useful for when setting up nodes in a complex graph system. */ - MA_SOUND_FLAG_NO_PITCH = 0x00002000, /* Disable pitch shifting with ma_sound_set_pitch() and ma_sound_group_set_pitch(). This is an optimization. */ - MA_SOUND_FLAG_NO_SPATIALIZATION = 0x00004000 /* Disable spatialization. */ -} ma_sound_flags; - -#ifndef MA_ENGINE_MAX_LISTENERS -#define MA_ENGINE_MAX_LISTENERS 4 -#endif - -#define MA_LISTENER_INDEX_CLOSEST ((ma_uint8)-1) - -typedef enum -{ - ma_engine_node_type_sound, - ma_engine_node_type_group -} ma_engine_node_type; - -typedef struct -{ - ma_engine* pEngine; - ma_engine_node_type type; - ma_uint32 channelsIn; - ma_uint32 channelsOut; - ma_uint32 sampleRate; /* Only used when the type is set to ma_engine_node_type_sound. */ - ma_uint32 volumeSmoothTimeInPCMFrames; /* The number of frames to smooth over volume changes. Defaults to 0 in which case no smoothing is used. */ - ma_mono_expansion_mode monoExpansionMode; - ma_bool8 isPitchDisabled; /* Pitching can be explicitly disabled with MA_SOUND_FLAG_NO_PITCH to optimize processing. */ - ma_bool8 isSpatializationDisabled; /* Spatialization can be explicitly disabled with MA_SOUND_FLAG_NO_SPATIALIZATION. */ - ma_uint8 pinnedListenerIndex; /* The index of the listener this node should always use for spatialization. If set to MA_LISTENER_INDEX_CLOSEST the engine will use the closest listener. */ -} ma_engine_node_config; - -MA_API ma_engine_node_config ma_engine_node_config_init(ma_engine* pEngine, ma_engine_node_type type, ma_uint32 flags); - - -/* Base node object for both ma_sound and ma_sound_group. */ -typedef struct -{ - ma_node_base baseNode; /* Must be the first member for compatibility with the ma_node API. */ - ma_engine* pEngine; /* A pointer to the engine. Set based on the value from the config. */ - ma_uint32 sampleRate; /* The sample rate of the input data. For sounds backed by a data source, this will be the data source's sample rate. Otherwise it'll be the engine's sample rate. */ - ma_uint32 volumeSmoothTimeInPCMFrames; - ma_mono_expansion_mode monoExpansionMode; - ma_fader fader; - ma_linear_resampler resampler; /* For pitch shift. */ - ma_spatializer spatializer; - ma_panner panner; - ma_gainer volumeGainer; /* This will only be used if volumeSmoothTimeInPCMFrames is > 0. */ - ma_atomic_float volume; /* Defaults to 1. */ - MA_ATOMIC(4, float) pitch; - float oldPitch; /* For determining whether or not the resampler needs to be updated to reflect the new pitch. The resampler will be updated on the mixing thread. */ - float oldDopplerPitch; /* For determining whether or not the resampler needs to be updated to take a new doppler pitch into account. */ - MA_ATOMIC(4, ma_bool32) isPitchDisabled; /* When set to true, pitching will be disabled which will allow the resampler to be bypassed to save some computation. */ - MA_ATOMIC(4, ma_bool32) isSpatializationDisabled; /* Set to false by default. When set to false, will not have spatialisation applied. */ - MA_ATOMIC(4, ma_uint32) pinnedListenerIndex; /* The index of the listener this node should always use for spatialization. If set to MA_LISTENER_INDEX_CLOSEST the engine will use the closest listener. */ - - /* When setting a fade, it's not done immediately in ma_sound_set_fade(). It's deferred to the audio thread which means we need to store the settings here. */ - struct - { - ma_atomic_float volumeBeg; - ma_atomic_float volumeEnd; - ma_atomic_uint64 fadeLengthInFrames; /* <-- Defaults to (~(ma_uint64)0) which is used to indicate that no fade should be applied. */ - ma_atomic_uint64 absoluteGlobalTimeInFrames; /* <-- The time to start the fade. */ - } fadeSettings; - - /* Memory management. */ - ma_bool8 _ownsHeap; - void* _pHeap; -} ma_engine_node; - -MA_API ma_result ma_engine_node_get_heap_size(const ma_engine_node_config* pConfig, size_t* pHeapSizeInBytes); -MA_API ma_result ma_engine_node_init_preallocated(const ma_engine_node_config* pConfig, void* pHeap, ma_engine_node* pEngineNode); -MA_API ma_result ma_engine_node_init(const ma_engine_node_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_engine_node* pEngineNode); -MA_API void ma_engine_node_uninit(ma_engine_node* pEngineNode, const ma_allocation_callbacks* pAllocationCallbacks); - - -#define MA_SOUND_SOURCE_CHANNEL_COUNT 0xFFFFFFFF - -/* Callback for when a sound reaches the end. */ -typedef void (* ma_sound_end_proc)(void* pUserData, ma_sound* pSound); - -typedef struct -{ - const char* pFilePath; /* Set this to load from the resource manager. */ - const wchar_t* pFilePathW; /* Set this to load from the resource manager. */ - ma_data_source* pDataSource; /* Set this to load from an existing data source. */ - ma_node* pInitialAttachment; /* If set, the sound will be attached to an input of this node. This can be set to a ma_sound. If set to NULL, the sound will be attached directly to the endpoint unless MA_SOUND_FLAG_NO_DEFAULT_ATTACHMENT is set in `flags`. */ - ma_uint32 initialAttachmentInputBusIndex; /* The index of the input bus of pInitialAttachment to attach the sound to. */ - ma_uint32 channelsIn; /* Ignored if using a data source as input (the data source's channel count will be used always). Otherwise, setting to 0 will cause the engine's channel count to be used. */ - ma_uint32 channelsOut; /* Set this to 0 (default) to use the engine's channel count. Set to MA_SOUND_SOURCE_CHANNEL_COUNT to use the data source's channel count (only used if using a data source as input). */ - ma_mono_expansion_mode monoExpansionMode; /* Controls how the mono channel should be expanded to other channels when spatialization is disabled on a sound. */ - ma_uint32 flags; /* A combination of MA_SOUND_FLAG_* flags. */ - ma_uint32 volumeSmoothTimeInPCMFrames; /* The number of frames to smooth over volume changes. Defaults to 0 in which case no smoothing is used. */ - ma_uint64 initialSeekPointInPCMFrames; /* Initializes the sound such that it's seeked to this location by default. */ - ma_uint64 rangeBegInPCMFrames; - ma_uint64 rangeEndInPCMFrames; - ma_uint64 loopPointBegInPCMFrames; - ma_uint64 loopPointEndInPCMFrames; - ma_sound_end_proc endCallback; /* Fired when the sound reaches the end. Will be fired from the audio thread. Do not restart, uninitialize or otherwise change the state of the sound from here. Instead fire an event or set a variable to indicate to a different thread to change the start of the sound. Will not be fired in response to a scheduled stop with ma_sound_set_stop_time_*(). */ - void* pEndCallbackUserData; -#ifndef MA_NO_RESOURCE_MANAGER - ma_resource_manager_pipeline_notifications initNotifications; -#endif - ma_fence* pDoneFence; /* Deprecated. Use initNotifications instead. Released when the resource manager has finished decoding the entire sound. Not used with streams. */ - ma_bool32 isLooping; /* Deprecated. Use the MA_SOUND_FLAG_LOOPING flag in `flags` instead. */ -} ma_sound_config; - -MA_API ma_sound_config ma_sound_config_init(void); /* Deprecated. Will be removed in version 0.12. Use ma_sound_config_2() instead. */ -MA_API ma_sound_config ma_sound_config_init_2(ma_engine* pEngine); /* Will be renamed to ma_sound_config_init() in version 0.12. */ - -struct ma_sound -{ - ma_engine_node engineNode; /* Must be the first member for compatibility with the ma_node API. */ - ma_data_source* pDataSource; - MA_ATOMIC(8, ma_uint64) seekTarget; /* The PCM frame index to seek to in the mixing thread. Set to (~(ma_uint64)0) to not perform any seeking. */ - MA_ATOMIC(4, ma_bool32) atEnd; - ma_sound_end_proc endCallback; - void* pEndCallbackUserData; - ma_bool8 ownsDataSource; - - /* - We're declaring a resource manager data source object here to save us a malloc when loading a - sound via the resource manager, which I *think* will be the most common scenario. - */ -#ifndef MA_NO_RESOURCE_MANAGER - ma_resource_manager_data_source* pResourceManagerDataSource; -#endif -}; - -/* Structure specifically for sounds played with ma_engine_play_sound(). Making this a separate structure to reduce overhead. */ -typedef struct ma_sound_inlined ma_sound_inlined; -struct ma_sound_inlined -{ - ma_sound sound; - ma_sound_inlined* pNext; - ma_sound_inlined* pPrev; -}; - -/* A sound group is just a sound. */ -typedef ma_sound_config ma_sound_group_config; -typedef ma_sound ma_sound_group; - -MA_API ma_sound_group_config ma_sound_group_config_init(void); /* Deprecated. Will be removed in version 0.12. Use ma_sound_config_2() instead. */ -MA_API ma_sound_group_config ma_sound_group_config_init_2(ma_engine* pEngine); /* Will be renamed to ma_sound_config_init() in version 0.12. */ - -typedef void (* ma_engine_process_proc)(void* pUserData, float* pFramesOut, ma_uint64 frameCount); - -typedef struct -{ -#if !defined(MA_NO_RESOURCE_MANAGER) - ma_resource_manager* pResourceManager; /* Can be null in which case a resource manager will be created for you. */ -#endif -#if !defined(MA_NO_DEVICE_IO) - ma_context* pContext; - ma_device* pDevice; /* If set, the caller is responsible for calling ma_engine_data_callback() in the device's data callback. */ - ma_device_id* pPlaybackDeviceID; /* The ID of the playback device to use with the default listener. */ - ma_device_data_proc dataCallback; /* Can be null. Can be used to provide a custom device data callback. */ - ma_device_notification_proc notificationCallback; -#endif - ma_log* pLog; /* When set to NULL, will use the context's log. */ - ma_uint32 listenerCount; /* Must be between 1 and MA_ENGINE_MAX_LISTENERS. */ - ma_uint32 channels; /* The number of channels to use when mixing and spatializing. When set to 0, will use the native channel count of the device. */ - ma_uint32 sampleRate; /* The sample rate. When set to 0 will use the native channel count of the device. */ - ma_uint32 periodSizeInFrames; /* If set to something other than 0, updates will always be exactly this size. The underlying device may be a different size, but from the perspective of the mixer that won't matter.*/ - ma_uint32 periodSizeInMilliseconds; /* Used if periodSizeInFrames is unset. */ - ma_uint32 gainSmoothTimeInFrames; /* The number of frames to interpolate the gain of spatialized sounds across. If set to 0, will use gainSmoothTimeInMilliseconds. */ - ma_uint32 gainSmoothTimeInMilliseconds; /* When set to 0, gainSmoothTimeInFrames will be used. If both are set to 0, a default value will be used. */ - ma_uint32 defaultVolumeSmoothTimeInPCMFrames; /* Defaults to 0. Controls the default amount of smoothing to apply to volume changes to sounds. High values means more smoothing at the expense of high latency (will take longer to reach the new volume). */ - ma_uint32 preMixStackSizeInBytes; /* A stack is used for internal processing in the node graph. This allows you to configure the size of this stack. Smaller values will reduce the maximum depth of your node graph. You should rarely need to modify this. */ - ma_allocation_callbacks allocationCallbacks; - ma_bool32 noAutoStart; /* When set to true, requires an explicit call to ma_engine_start(). This is false by default, meaning the engine will be started automatically in ma_engine_init(). */ - ma_bool32 noDevice; /* When set to true, don't create a default device. ma_engine_read_pcm_frames() can be called manually to read data. */ - ma_mono_expansion_mode monoExpansionMode; /* Controls how the mono channel should be expanded to other channels when spatialization is disabled on a sound. */ - ma_vfs* pResourceManagerVFS; /* A pointer to a pre-allocated VFS object to use with the resource manager. This is ignored if pResourceManager is not NULL. */ - ma_engine_process_proc onProcess; /* Fired at the end of each call to ma_engine_read_pcm_frames(). For engine's that manage their own internal device (the default configuration), this will be fired from the audio thread, and you do not need to call ma_engine_read_pcm_frames() manually in order to trigger this. */ - void* pProcessUserData; /* User data that's passed into onProcess. */ -} ma_engine_config; - -MA_API ma_engine_config ma_engine_config_init(void); - - -struct ma_engine -{ - ma_node_graph nodeGraph; /* An engine is a node graph. It should be able to be plugged into any ma_node_graph API (with a cast) which means this must be the first member of this struct. */ -#if !defined(MA_NO_RESOURCE_MANAGER) - ma_resource_manager* pResourceManager; -#endif -#if !defined(MA_NO_DEVICE_IO) - ma_device* pDevice; /* Optionally set via the config, otherwise allocated by the engine in ma_engine_init(). */ -#endif - ma_log* pLog; - ma_uint32 sampleRate; - ma_uint32 listenerCount; - ma_spatializer_listener listeners[MA_ENGINE_MAX_LISTENERS]; - ma_allocation_callbacks allocationCallbacks; - ma_bool8 ownsResourceManager; - ma_bool8 ownsDevice; - ma_spinlock inlinedSoundLock; /* For synchronizing access to the inlined sound list. */ - ma_sound_inlined* pInlinedSoundHead; /* The first inlined sound. Inlined sounds are tracked in a linked list. */ - MA_ATOMIC(4, ma_uint32) inlinedSoundCount; /* The total number of allocated inlined sound objects. Used for debugging. */ - ma_uint32 gainSmoothTimeInFrames; /* The number of frames to interpolate the gain of spatialized sounds across. */ - ma_uint32 defaultVolumeSmoothTimeInPCMFrames; - ma_mono_expansion_mode monoExpansionMode; - ma_engine_process_proc onProcess; - void* pProcessUserData; -}; - -MA_API ma_result ma_engine_init(const ma_engine_config* pConfig, ma_engine* pEngine); -MA_API void ma_engine_uninit(ma_engine* pEngine); -MA_API ma_result ma_engine_read_pcm_frames(ma_engine* pEngine, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead); -MA_API ma_node_graph* ma_engine_get_node_graph(ma_engine* pEngine); -#if !defined(MA_NO_RESOURCE_MANAGER) -MA_API ma_resource_manager* ma_engine_get_resource_manager(ma_engine* pEngine); -#endif -MA_API ma_device* ma_engine_get_device(ma_engine* pEngine); -MA_API ma_log* ma_engine_get_log(ma_engine* pEngine); -MA_API ma_node* ma_engine_get_endpoint(ma_engine* pEngine); -MA_API ma_uint64 ma_engine_get_time_in_pcm_frames(const ma_engine* pEngine); -MA_API ma_uint64 ma_engine_get_time_in_milliseconds(const ma_engine* pEngine); -MA_API ma_result ma_engine_set_time_in_pcm_frames(ma_engine* pEngine, ma_uint64 globalTime); -MA_API ma_result ma_engine_set_time_in_milliseconds(ma_engine* pEngine, ma_uint64 globalTime); -MA_API ma_uint64 ma_engine_get_time(const ma_engine* pEngine); /* Deprecated. Use ma_engine_get_time_in_pcm_frames(). Will be removed in version 0.12. */ -MA_API ma_result ma_engine_set_time(ma_engine* pEngine, ma_uint64 globalTime); /* Deprecated. Use ma_engine_set_time_in_pcm_frames(). Will be removed in version 0.12. */ -MA_API ma_uint32 ma_engine_get_channels(const ma_engine* pEngine); -MA_API ma_uint32 ma_engine_get_sample_rate(const ma_engine* pEngine); - -MA_API ma_result ma_engine_start(ma_engine* pEngine); -MA_API ma_result ma_engine_stop(ma_engine* pEngine); -MA_API ma_result ma_engine_set_volume(ma_engine* pEngine, float volume); -MA_API float ma_engine_get_volume(ma_engine* pEngine); -MA_API ma_result ma_engine_set_gain_db(ma_engine* pEngine, float gainDB); -MA_API float ma_engine_get_gain_db(ma_engine* pEngine); - -MA_API ma_uint32 ma_engine_get_listener_count(const ma_engine* pEngine); -MA_API ma_uint32 ma_engine_find_closest_listener(const ma_engine* pEngine, float absolutePosX, float absolutePosY, float absolutePosZ); -MA_API void ma_engine_listener_set_position(ma_engine* pEngine, ma_uint32 listenerIndex, float x, float y, float z); -MA_API ma_vec3f ma_engine_listener_get_position(const ma_engine* pEngine, ma_uint32 listenerIndex); -MA_API void ma_engine_listener_set_direction(ma_engine* pEngine, ma_uint32 listenerIndex, float x, float y, float z); -MA_API ma_vec3f ma_engine_listener_get_direction(const ma_engine* pEngine, ma_uint32 listenerIndex); -MA_API void ma_engine_listener_set_velocity(ma_engine* pEngine, ma_uint32 listenerIndex, float x, float y, float z); -MA_API ma_vec3f ma_engine_listener_get_velocity(const ma_engine* pEngine, ma_uint32 listenerIndex); -MA_API void ma_engine_listener_set_cone(ma_engine* pEngine, ma_uint32 listenerIndex, float innerAngleInRadians, float outerAngleInRadians, float outerGain); -MA_API void ma_engine_listener_get_cone(const ma_engine* pEngine, ma_uint32 listenerIndex, float* pInnerAngleInRadians, float* pOuterAngleInRadians, float* pOuterGain); -MA_API void ma_engine_listener_set_world_up(ma_engine* pEngine, ma_uint32 listenerIndex, float x, float y, float z); -MA_API ma_vec3f ma_engine_listener_get_world_up(const ma_engine* pEngine, ma_uint32 listenerIndex); -MA_API void ma_engine_listener_set_enabled(ma_engine* pEngine, ma_uint32 listenerIndex, ma_bool32 isEnabled); -MA_API ma_bool32 ma_engine_listener_is_enabled(const ma_engine* pEngine, ma_uint32 listenerIndex); - -#ifndef MA_NO_RESOURCE_MANAGER -MA_API ma_result ma_engine_play_sound_ex(ma_engine* pEngine, const char* pFilePath, ma_node* pNode, ma_uint32 nodeInputBusIndex); -MA_API ma_result ma_engine_play_sound(ma_engine* pEngine, const char* pFilePath, ma_sound_group* pGroup); /* Fire and forget. */ -#endif - -#ifndef MA_NO_RESOURCE_MANAGER -MA_API ma_result ma_sound_init_from_file(ma_engine* pEngine, const char* pFilePath, ma_uint32 flags, ma_sound_group* pGroup, ma_fence* pDoneFence, ma_sound* pSound); -MA_API ma_result ma_sound_init_from_file_w(ma_engine* pEngine, const wchar_t* pFilePath, ma_uint32 flags, ma_sound_group* pGroup, ma_fence* pDoneFence, ma_sound* pSound); -MA_API ma_result ma_sound_init_copy(ma_engine* pEngine, const ma_sound* pExistingSound, ma_uint32 flags, ma_sound_group* pGroup, ma_sound* pSound); -#endif -MA_API ma_result ma_sound_init_from_data_source(ma_engine* pEngine, ma_data_source* pDataSource, ma_uint32 flags, ma_sound_group* pGroup, ma_sound* pSound); -MA_API ma_result ma_sound_init_ex(ma_engine* pEngine, const ma_sound_config* pConfig, ma_sound* pSound); -MA_API void ma_sound_uninit(ma_sound* pSound); -MA_API ma_engine* ma_sound_get_engine(const ma_sound* pSound); -MA_API ma_data_source* ma_sound_get_data_source(const ma_sound* pSound); -MA_API ma_result ma_sound_start(ma_sound* pSound); -MA_API ma_result ma_sound_stop(ma_sound* pSound); -MA_API ma_result ma_sound_stop_with_fade_in_pcm_frames(ma_sound* pSound, ma_uint64 fadeLengthInFrames); /* Will overwrite any scheduled stop and fade. */ -MA_API ma_result ma_sound_stop_with_fade_in_milliseconds(ma_sound* pSound, ma_uint64 fadeLengthInFrames); /* Will overwrite any scheduled stop and fade. */ -MA_API void ma_sound_set_volume(ma_sound* pSound, float volume); -MA_API float ma_sound_get_volume(const ma_sound* pSound); -MA_API void ma_sound_set_pan(ma_sound* pSound, float pan); -MA_API float ma_sound_get_pan(const ma_sound* pSound); -MA_API void ma_sound_set_pan_mode(ma_sound* pSound, ma_pan_mode panMode); -MA_API ma_pan_mode ma_sound_get_pan_mode(const ma_sound* pSound); -MA_API void ma_sound_set_pitch(ma_sound* pSound, float pitch); -MA_API float ma_sound_get_pitch(const ma_sound* pSound); -MA_API void ma_sound_set_spatialization_enabled(ma_sound* pSound, ma_bool32 enabled); -MA_API ma_bool32 ma_sound_is_spatialization_enabled(const ma_sound* pSound); -MA_API void ma_sound_set_pinned_listener_index(ma_sound* pSound, ma_uint32 listenerIndex); -MA_API ma_uint32 ma_sound_get_pinned_listener_index(const ma_sound* pSound); -MA_API ma_uint32 ma_sound_get_listener_index(const ma_sound* pSound); -MA_API ma_vec3f ma_sound_get_direction_to_listener(const ma_sound* pSound); -MA_API void ma_sound_set_position(ma_sound* pSound, float x, float y, float z); -MA_API ma_vec3f ma_sound_get_position(const ma_sound* pSound); -MA_API void ma_sound_set_direction(ma_sound* pSound, float x, float y, float z); -MA_API ma_vec3f ma_sound_get_direction(const ma_sound* pSound); -MA_API void ma_sound_set_velocity(ma_sound* pSound, float x, float y, float z); -MA_API ma_vec3f ma_sound_get_velocity(const ma_sound* pSound); -MA_API void ma_sound_set_attenuation_model(ma_sound* pSound, ma_attenuation_model attenuationModel); -MA_API ma_attenuation_model ma_sound_get_attenuation_model(const ma_sound* pSound); -MA_API void ma_sound_set_positioning(ma_sound* pSound, ma_positioning positioning); -MA_API ma_positioning ma_sound_get_positioning(const ma_sound* pSound); -MA_API void ma_sound_set_rolloff(ma_sound* pSound, float rolloff); -MA_API float ma_sound_get_rolloff(const ma_sound* pSound); -MA_API void ma_sound_set_min_gain(ma_sound* pSound, float minGain); -MA_API float ma_sound_get_min_gain(const ma_sound* pSound); -MA_API void ma_sound_set_max_gain(ma_sound* pSound, float maxGain); -MA_API float ma_sound_get_max_gain(const ma_sound* pSound); -MA_API void ma_sound_set_min_distance(ma_sound* pSound, float minDistance); -MA_API float ma_sound_get_min_distance(const ma_sound* pSound); -MA_API void ma_sound_set_max_distance(ma_sound* pSound, float maxDistance); -MA_API float ma_sound_get_max_distance(const ma_sound* pSound); -MA_API void ma_sound_set_cone(ma_sound* pSound, float innerAngleInRadians, float outerAngleInRadians, float outerGain); -MA_API void ma_sound_get_cone(const ma_sound* pSound, float* pInnerAngleInRadians, float* pOuterAngleInRadians, float* pOuterGain); -MA_API void ma_sound_set_doppler_factor(ma_sound* pSound, float dopplerFactor); -MA_API float ma_sound_get_doppler_factor(const ma_sound* pSound); -MA_API void ma_sound_set_directional_attenuation_factor(ma_sound* pSound, float directionalAttenuationFactor); -MA_API float ma_sound_get_directional_attenuation_factor(const ma_sound* pSound); -MA_API void ma_sound_set_fade_in_pcm_frames(ma_sound* pSound, float volumeBeg, float volumeEnd, ma_uint64 fadeLengthInFrames); -MA_API void ma_sound_set_fade_in_milliseconds(ma_sound* pSound, float volumeBeg, float volumeEnd, ma_uint64 fadeLengthInMilliseconds); -MA_API void ma_sound_set_fade_start_in_pcm_frames(ma_sound* pSound, float volumeBeg, float volumeEnd, ma_uint64 fadeLengthInFrames, ma_uint64 absoluteGlobalTimeInFrames); -MA_API void ma_sound_set_fade_start_in_milliseconds(ma_sound* pSound, float volumeBeg, float volumeEnd, ma_uint64 fadeLengthInMilliseconds, ma_uint64 absoluteGlobalTimeInMilliseconds); -MA_API float ma_sound_get_current_fade_volume(const ma_sound* pSound); -MA_API void ma_sound_set_start_time_in_pcm_frames(ma_sound* pSound, ma_uint64 absoluteGlobalTimeInFrames); -MA_API void ma_sound_set_start_time_in_milliseconds(ma_sound* pSound, ma_uint64 absoluteGlobalTimeInMilliseconds); -MA_API void ma_sound_set_stop_time_in_pcm_frames(ma_sound* pSound, ma_uint64 absoluteGlobalTimeInFrames); -MA_API void ma_sound_set_stop_time_in_milliseconds(ma_sound* pSound, ma_uint64 absoluteGlobalTimeInMilliseconds); -MA_API void ma_sound_set_stop_time_with_fade_in_pcm_frames(ma_sound* pSound, ma_uint64 stopAbsoluteGlobalTimeInFrames, ma_uint64 fadeLengthInFrames); -MA_API void ma_sound_set_stop_time_with_fade_in_milliseconds(ma_sound* pSound, ma_uint64 stopAbsoluteGlobalTimeInMilliseconds, ma_uint64 fadeLengthInMilliseconds); -MA_API ma_bool32 ma_sound_is_playing(const ma_sound* pSound); -MA_API ma_uint64 ma_sound_get_time_in_pcm_frames(const ma_sound* pSound); -MA_API ma_uint64 ma_sound_get_time_in_milliseconds(const ma_sound* pSound); -MA_API void ma_sound_set_looping(ma_sound* pSound, ma_bool32 isLooping); -MA_API ma_bool32 ma_sound_is_looping(const ma_sound* pSound); -MA_API ma_bool32 ma_sound_at_end(const ma_sound* pSound); -MA_API ma_result ma_sound_seek_to_pcm_frame(ma_sound* pSound, ma_uint64 frameIndex); /* Just a wrapper around ma_data_source_seek_to_pcm_frame(). */ -MA_API ma_result ma_sound_seek_to_second(ma_sound* pSound, float seekPointInSeconds); /* Abstraction to ma_sound_seek_to_pcm_frame() */ -MA_API ma_result ma_sound_get_data_format(ma_sound* pSound, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate, ma_channel* pChannelMap, size_t channelMapCap); -MA_API ma_result ma_sound_get_cursor_in_pcm_frames(ma_sound* pSound, ma_uint64* pCursor); -MA_API ma_result ma_sound_get_length_in_pcm_frames(ma_sound* pSound, ma_uint64* pLength); -MA_API ma_result ma_sound_get_cursor_in_seconds(ma_sound* pSound, float* pCursor); -MA_API ma_result ma_sound_get_length_in_seconds(ma_sound* pSound, float* pLength); -MA_API ma_result ma_sound_set_end_callback(ma_sound* pSound, ma_sound_end_proc callback, void* pUserData); - -MA_API ma_result ma_sound_group_init(ma_engine* pEngine, ma_uint32 flags, ma_sound_group* pParentGroup, ma_sound_group* pGroup); -MA_API ma_result ma_sound_group_init_ex(ma_engine* pEngine, const ma_sound_group_config* pConfig, ma_sound_group* pGroup); -MA_API void ma_sound_group_uninit(ma_sound_group* pGroup); -MA_API ma_engine* ma_sound_group_get_engine(const ma_sound_group* pGroup); -MA_API ma_result ma_sound_group_start(ma_sound_group* pGroup); -MA_API ma_result ma_sound_group_stop(ma_sound_group* pGroup); -MA_API void ma_sound_group_set_volume(ma_sound_group* pGroup, float volume); -MA_API float ma_sound_group_get_volume(const ma_sound_group* pGroup); -MA_API void ma_sound_group_set_pan(ma_sound_group* pGroup, float pan); -MA_API float ma_sound_group_get_pan(const ma_sound_group* pGroup); -MA_API void ma_sound_group_set_pan_mode(ma_sound_group* pGroup, ma_pan_mode panMode); -MA_API ma_pan_mode ma_sound_group_get_pan_mode(const ma_sound_group* pGroup); -MA_API void ma_sound_group_set_pitch(ma_sound_group* pGroup, float pitch); -MA_API float ma_sound_group_get_pitch(const ma_sound_group* pGroup); -MA_API void ma_sound_group_set_spatialization_enabled(ma_sound_group* pGroup, ma_bool32 enabled); -MA_API ma_bool32 ma_sound_group_is_spatialization_enabled(const ma_sound_group* pGroup); -MA_API void ma_sound_group_set_pinned_listener_index(ma_sound_group* pGroup, ma_uint32 listenerIndex); -MA_API ma_uint32 ma_sound_group_get_pinned_listener_index(const ma_sound_group* pGroup); -MA_API ma_uint32 ma_sound_group_get_listener_index(const ma_sound_group* pGroup); -MA_API ma_vec3f ma_sound_group_get_direction_to_listener(const ma_sound_group* pGroup); -MA_API void ma_sound_group_set_position(ma_sound_group* pGroup, float x, float y, float z); -MA_API ma_vec3f ma_sound_group_get_position(const ma_sound_group* pGroup); -MA_API void ma_sound_group_set_direction(ma_sound_group* pGroup, float x, float y, float z); -MA_API ma_vec3f ma_sound_group_get_direction(const ma_sound_group* pGroup); -MA_API void ma_sound_group_set_velocity(ma_sound_group* pGroup, float x, float y, float z); -MA_API ma_vec3f ma_sound_group_get_velocity(const ma_sound_group* pGroup); -MA_API void ma_sound_group_set_attenuation_model(ma_sound_group* pGroup, ma_attenuation_model attenuationModel); -MA_API ma_attenuation_model ma_sound_group_get_attenuation_model(const ma_sound_group* pGroup); -MA_API void ma_sound_group_set_positioning(ma_sound_group* pGroup, ma_positioning positioning); -MA_API ma_positioning ma_sound_group_get_positioning(const ma_sound_group* pGroup); -MA_API void ma_sound_group_set_rolloff(ma_sound_group* pGroup, float rolloff); -MA_API float ma_sound_group_get_rolloff(const ma_sound_group* pGroup); -MA_API void ma_sound_group_set_min_gain(ma_sound_group* pGroup, float minGain); -MA_API float ma_sound_group_get_min_gain(const ma_sound_group* pGroup); -MA_API void ma_sound_group_set_max_gain(ma_sound_group* pGroup, float maxGain); -MA_API float ma_sound_group_get_max_gain(const ma_sound_group* pGroup); -MA_API void ma_sound_group_set_min_distance(ma_sound_group* pGroup, float minDistance); -MA_API float ma_sound_group_get_min_distance(const ma_sound_group* pGroup); -MA_API void ma_sound_group_set_max_distance(ma_sound_group* pGroup, float maxDistance); -MA_API float ma_sound_group_get_max_distance(const ma_sound_group* pGroup); -MA_API void ma_sound_group_set_cone(ma_sound_group* pGroup, float innerAngleInRadians, float outerAngleInRadians, float outerGain); -MA_API void ma_sound_group_get_cone(const ma_sound_group* pGroup, float* pInnerAngleInRadians, float* pOuterAngleInRadians, float* pOuterGain); -MA_API void ma_sound_group_set_doppler_factor(ma_sound_group* pGroup, float dopplerFactor); -MA_API float ma_sound_group_get_doppler_factor(const ma_sound_group* pGroup); -MA_API void ma_sound_group_set_directional_attenuation_factor(ma_sound_group* pGroup, float directionalAttenuationFactor); -MA_API float ma_sound_group_get_directional_attenuation_factor(const ma_sound_group* pGroup); -MA_API void ma_sound_group_set_fade_in_pcm_frames(ma_sound_group* pGroup, float volumeBeg, float volumeEnd, ma_uint64 fadeLengthInFrames); -MA_API void ma_sound_group_set_fade_in_milliseconds(ma_sound_group* pGroup, float volumeBeg, float volumeEnd, ma_uint64 fadeLengthInMilliseconds); -MA_API float ma_sound_group_get_current_fade_volume(ma_sound_group* pGroup); -MA_API void ma_sound_group_set_start_time_in_pcm_frames(ma_sound_group* pGroup, ma_uint64 absoluteGlobalTimeInFrames); -MA_API void ma_sound_group_set_start_time_in_milliseconds(ma_sound_group* pGroup, ma_uint64 absoluteGlobalTimeInMilliseconds); -MA_API void ma_sound_group_set_stop_time_in_pcm_frames(ma_sound_group* pGroup, ma_uint64 absoluteGlobalTimeInFrames); -MA_API void ma_sound_group_set_stop_time_in_milliseconds(ma_sound_group* pGroup, ma_uint64 absoluteGlobalTimeInMilliseconds); -MA_API ma_bool32 ma_sound_group_is_playing(const ma_sound_group* pGroup); -MA_API ma_uint64 ma_sound_group_get_time_in_pcm_frames(const ma_sound_group* pGroup); -#endif /* MA_NO_ENGINE */ -/* END SECTION: miniaudio_engine.h */ - -#ifdef __cplusplus -} -#endif -#endif /* miniaudio_h */ - - -/* -This is for preventing greying out of the implementation section. -*/ -#if defined(Q_CREATOR_RUN) || defined(__INTELLISENSE__) || defined(__CDT_PARSER__) -#define MINIAUDIO_IMPLEMENTATION -#endif - -/************************************************************************************************************************************************************ -************************************************************************************************************************************************************* - -IMPLEMENTATION - -************************************************************************************************************************************************************* -************************************************************************************************************************************************************/ -#if defined(MINIAUDIO_IMPLEMENTATION) || defined(MA_IMPLEMENTATION) -#ifndef miniaudio_c -#define miniaudio_c - -#include -#include /* For INT_MAX */ -#include /* sin(), etc. */ -#include /* For malloc(), free(), wcstombs(). */ -#include /* For memset() */ - -#include -#include -#if !defined(_MSC_VER) && !defined(__DMC__) - #include /* For strcasecmp(). */ - #include /* For wcslen(), wcsrtombs() */ -#endif -#ifdef _MSC_VER - #include /* For _controlfp_s constants */ -#endif - -#if defined(MA_WIN32) - #include - - /* - There's a possibility that WIN32_LEAN_AND_MEAN has been defined which will exclude some symbols - such as STGM_READ and CLSCTL_ALL. We need to check these and define them ourselves if they're - unavailable. - */ - #ifndef STGM_READ - #define STGM_READ 0x00000000L - #endif - #ifndef CLSCTX_ALL - #define CLSCTX_ALL 23 - #endif - - /* IUnknown is used by both the WASAPI and DirectSound backends. It easier to just declare our version here. */ - typedef struct ma_IUnknown ma_IUnknown; -#endif - -#if !defined(MA_WIN32) -#include -#include /* select() (used for ma_sleep()). */ -#include -#endif - -#ifdef MA_NX -#include /* For nanosleep() */ -#endif - -#include /* For fstat(), etc. */ - -#ifdef MA_EMSCRIPTEN -#include -#endif - - -/* Architecture Detection */ -#if !defined(MA_64BIT) && !defined(MA_32BIT) -#ifdef _WIN32 -#ifdef _WIN64 -#define MA_64BIT -#else -#define MA_32BIT -#endif -#endif -#endif - -#if !defined(MA_64BIT) && !defined(MA_32BIT) -#ifdef __GNUC__ -#ifdef __LP64__ -#define MA_64BIT -#else -#define MA_32BIT -#endif -#endif -#endif - -#if !defined(MA_64BIT) && !defined(MA_32BIT) -#include -#if INTPTR_MAX == INT64_MAX -#define MA_64BIT -#else -#define MA_32BIT -#endif -#endif - -#if defined(__arm__) || defined(_M_ARM) -#define MA_ARM32 -#endif -#if defined(__arm64) || defined(__arm64__) || defined(__aarch64__) || defined(_M_ARM64) -#define MA_ARM64 -#endif - -#if defined(__x86_64__) || defined(_M_X64) -#define MA_X64 -#elif defined(__i386) || defined(_M_IX86) -#define MA_X86 -#elif defined(MA_ARM32) || defined(MA_ARM64) -#define MA_ARM -#endif - -/* Intrinsics Support */ -#if (defined(MA_X64) || defined(MA_X86)) && !defined(__COSMOPOLITAN__) - #if defined(_MSC_VER) && !defined(__clang__) - /* MSVC. */ - #if _MSC_VER >= 1400 && !defined(MA_NO_SSE2) /* 2005 */ - #define MA_SUPPORT_SSE2 - #endif - /*#if _MSC_VER >= 1600 && !defined(MA_NO_AVX)*/ /* 2010 */ - /* #define MA_SUPPORT_AVX*/ - /*#endif*/ - #if _MSC_VER >= 1700 && !defined(MA_NO_AVX2) /* 2012 */ - #define MA_SUPPORT_AVX2 - #endif - #else - /* Assume GNUC-style. */ - #if defined(__SSE2__) && !defined(MA_NO_SSE2) - #define MA_SUPPORT_SSE2 - #endif - /*#if defined(__AVX__) && !defined(MA_NO_AVX)*/ - /* #define MA_SUPPORT_AVX*/ - /*#endif*/ - #if defined(__AVX2__) && !defined(MA_NO_AVX2) - #define MA_SUPPORT_AVX2 - #endif - #endif - - /* If at this point we still haven't determined compiler support for the intrinsics just fall back to __has_include. */ - #if !defined(__GNUC__) && !defined(__clang__) && defined(__has_include) - #if !defined(MA_SUPPORT_SSE2) && !defined(MA_NO_SSE2) && __has_include() - #define MA_SUPPORT_SSE2 - #endif - /*#if !defined(MA_SUPPORT_AVX) && !defined(MA_NO_AVX) && __has_include()*/ - /* #define MA_SUPPORT_AVX*/ - /*#endif*/ - #if !defined(MA_SUPPORT_AVX2) && !defined(MA_NO_AVX2) && __has_include() - #define MA_SUPPORT_AVX2 - #endif - #endif - - #if defined(MA_SUPPORT_AVX2) || defined(MA_SUPPORT_AVX) - #include - #elif defined(MA_SUPPORT_SSE2) - #include - #endif -#endif - -#if defined(MA_ARM) - #if !defined(MA_NO_NEON) && (defined(__ARM_NEON) || defined(__aarch64__) || defined(_M_ARM64)) - #define MA_SUPPORT_NEON - #include - #endif -#endif - -/* Begin globally disabled warnings. */ -#if defined(_MSC_VER) - #pragma warning(push) - #pragma warning(disable:4752) /* found Intel(R) Advanced Vector Extensions; consider using /arch:AVX */ - #pragma warning(disable:4049) /* compiler limit : terminating line number emission */ -#endif - -#if defined(MA_X64) || defined(MA_X86) - #if defined(_MSC_VER) && !defined(__clang__) - #if _MSC_VER >= 1400 - #include - static MA_INLINE void ma_cpuid(int info[4], int fid) - { - __cpuid(info, fid); - } - #else - #define MA_NO_CPUID - #endif - - #if _MSC_VER >= 1600 && (defined(_MSC_FULL_VER) && _MSC_FULL_VER >= 160040219) - static MA_INLINE unsigned __int64 ma_xgetbv(int reg) - { - return _xgetbv(reg); - } - #else - #define MA_NO_XGETBV - #endif - #elif (defined(__GNUC__) || defined(__clang__)) && !defined(MA_ANDROID) - static MA_INLINE void ma_cpuid(int info[4], int fid) - { - /* - It looks like the -fPIC option uses the ebx register which GCC complains about. We can work around this by just using a different register, the - specific register of which I'm letting the compiler decide on. The "k" prefix is used to specify a 32-bit register. The {...} syntax is for - supporting different assembly dialects. - - What's basically happening is that we're saving and restoring the ebx register manually. - */ - #if defined(MA_X86) && defined(__PIC__) - __asm__ __volatile__ ( - "xchg{l} {%%}ebx, %k1;" - "cpuid;" - "xchg{l} {%%}ebx, %k1;" - : "=a"(info[0]), "=&r"(info[1]), "=c"(info[2]), "=d"(info[3]) : "a"(fid), "c"(0) - ); - #else - __asm__ __volatile__ ( - "cpuid" : "=a"(info[0]), "=b"(info[1]), "=c"(info[2]), "=d"(info[3]) : "a"(fid), "c"(0) - ); - #endif - } - - static MA_INLINE ma_uint64 ma_xgetbv(int reg) - { - unsigned int hi; - unsigned int lo; - - __asm__ __volatile__ ( - "xgetbv" : "=a"(lo), "=d"(hi) : "c"(reg) - ); - - return ((ma_uint64)hi << 32) | (ma_uint64)lo; - } - #else - #define MA_NO_CPUID - #define MA_NO_XGETBV - #endif -#else - #define MA_NO_CPUID - #define MA_NO_XGETBV -#endif - -static MA_INLINE ma_bool32 ma_has_sse2(void) -{ -#if defined(MA_SUPPORT_SSE2) - #if (defined(MA_X64) || defined(MA_X86)) && !defined(MA_NO_SSE2) - #if defined(MA_X64) - return MA_TRUE; /* 64-bit targets always support SSE2. */ - #elif (defined(_M_IX86_FP) && _M_IX86_FP == 2) || defined(__SSE2__) - return MA_TRUE; /* If the compiler is allowed to freely generate SSE2 code we can assume support. */ - #else - #if defined(MA_NO_CPUID) - return MA_FALSE; - #else - int info[4]; - ma_cpuid(info, 1); - return (info[3] & (1 << 26)) != 0; - #endif - #endif - #else - return MA_FALSE; /* SSE2 is only supported on x86 and x64 architectures. */ - #endif -#else - return MA_FALSE; /* No compiler support. */ -#endif -} - -#if 0 -static MA_INLINE ma_bool32 ma_has_avx() -{ -#if defined(MA_SUPPORT_AVX) - #if (defined(MA_X64) || defined(MA_X86)) && !defined(MA_NO_AVX) - #if defined(_AVX_) || defined(__AVX__) - return MA_TRUE; /* If the compiler is allowed to freely generate AVX code we can assume support. */ - #else - /* AVX requires both CPU and OS support. */ - #if defined(MA_NO_CPUID) || defined(MA_NO_XGETBV) - return MA_FALSE; - #else - int info[4]; - ma_cpuid(info, 1); - if (((info[2] & (1 << 27)) != 0) && ((info[2] & (1 << 28)) != 0)) { - ma_uint64 xrc = ma_xgetbv(0); - if ((xrc & 0x06) == 0x06) { - return MA_TRUE; - } else { - return MA_FALSE; - } - } else { - return MA_FALSE; - } - #endif - #endif - #else - return MA_FALSE; /* AVX is only supported on x86 and x64 architectures. */ - #endif -#else - return MA_FALSE; /* No compiler support. */ -#endif -} -#endif - -static MA_INLINE ma_bool32 ma_has_avx2(void) -{ -#if defined(MA_SUPPORT_AVX2) - #if (defined(MA_X64) || defined(MA_X86)) && !defined(MA_NO_AVX2) - #if defined(_AVX2_) || defined(__AVX2__) - return MA_TRUE; /* If the compiler is allowed to freely generate AVX2 code we can assume support. */ - #else - /* AVX2 requires both CPU and OS support. */ - #if defined(MA_NO_CPUID) || defined(MA_NO_XGETBV) - return MA_FALSE; - #else - int info1[4]; - int info7[4]; - ma_cpuid(info1, 1); - ma_cpuid(info7, 7); - if (((info1[2] & (1 << 27)) != 0) && ((info7[1] & (1 << 5)) != 0)) { - ma_uint64 xrc = ma_xgetbv(0); - if ((xrc & 0x06) == 0x06) { - return MA_TRUE; - } else { - return MA_FALSE; - } - } else { - return MA_FALSE; - } - #endif - #endif - #else - return MA_FALSE; /* AVX2 is only supported on x86 and x64 architectures. */ - #endif -#else - return MA_FALSE; /* No compiler support. */ -#endif -} - -static MA_INLINE ma_bool32 ma_has_neon(void) -{ -#if defined(MA_SUPPORT_NEON) - #if defined(MA_ARM) && !defined(MA_NO_NEON) - #if (defined(__ARM_NEON) || defined(__aarch64__) || defined(_M_ARM64)) - return MA_TRUE; /* If the compiler is allowed to freely generate NEON code we can assume support. */ - #else - /* TODO: Runtime check. */ - return MA_FALSE; - #endif - #else - return MA_FALSE; /* NEON is only supported on ARM architectures. */ - #endif -#else - return MA_FALSE; /* No compiler support. */ -#endif -} - -#if defined(__has_builtin) - #define MA_COMPILER_HAS_BUILTIN(x) __has_builtin(x) -#else - #define MA_COMPILER_HAS_BUILTIN(x) 0 -#endif - -#ifndef MA_ASSUME - #if MA_COMPILER_HAS_BUILTIN(__builtin_assume) - #define MA_ASSUME(x) __builtin_assume(x) - #elif MA_COMPILER_HAS_BUILTIN(__builtin_unreachable) - #define MA_ASSUME(x) do { if (!(x)) __builtin_unreachable(); } while (0) - #elif defined(_MSC_VER) - #define MA_ASSUME(x) __assume(x) - #else - #define MA_ASSUME(x) (void)(x) - #endif -#endif - -#ifndef MA_RESTRICT - #if defined(__clang__) || defined(__GNUC__) || defined(_MSC_VER) - #define MA_RESTRICT __restrict - #else - #define MA_RESTRICT - #endif -#endif - -#if defined(_MSC_VER) && _MSC_VER >= 1400 - #define MA_HAS_BYTESWAP16_INTRINSIC - #define MA_HAS_BYTESWAP32_INTRINSIC - #define MA_HAS_BYTESWAP64_INTRINSIC -#elif defined(__clang__) - #if MA_COMPILER_HAS_BUILTIN(__builtin_bswap16) - #define MA_HAS_BYTESWAP16_INTRINSIC - #endif - #if MA_COMPILER_HAS_BUILTIN(__builtin_bswap32) - #define MA_HAS_BYTESWAP32_INTRINSIC - #endif - #if MA_COMPILER_HAS_BUILTIN(__builtin_bswap64) - #define MA_HAS_BYTESWAP64_INTRINSIC - #endif -#elif defined(__GNUC__) - #if ((__GNUC__ > 4) || (__GNUC__ == 4 && __GNUC_MINOR__ >= 3)) - #define MA_HAS_BYTESWAP32_INTRINSIC - #define MA_HAS_BYTESWAP64_INTRINSIC - #endif - #if ((__GNUC__ > 4) || (__GNUC__ == 4 && __GNUC_MINOR__ >= 8)) - #define MA_HAS_BYTESWAP16_INTRINSIC - #endif -#endif - - -static MA_INLINE ma_bool32 ma_is_little_endian(void) -{ -#if defined(MA_X86) || defined(MA_X64) - return MA_TRUE; -#else - int n = 1; - return (*(char*)&n) == 1; -#endif -} - -static MA_INLINE ma_bool32 ma_is_big_endian(void) -{ - return !ma_is_little_endian(); -} - - -static MA_INLINE ma_uint32 ma_swap_endian_uint32(ma_uint32 n) -{ -#ifdef MA_HAS_BYTESWAP32_INTRINSIC - #if defined(_MSC_VER) - return _byteswap_ulong(n); - #elif defined(__GNUC__) || defined(__clang__) - #if defined(MA_ARM) && (defined(__ARM_ARCH) && __ARM_ARCH >= 6) && !defined(MA_64BIT) /* <-- 64-bit inline assembly has not been tested, so disabling for now. */ - /* Inline assembly optimized implementation for ARM. In my testing, GCC does not generate optimized code with __builtin_bswap32(). */ - ma_uint32 r; - __asm__ __volatile__ ( - #if defined(MA_64BIT) - "rev %w[out], %w[in]" : [out]"=r"(r) : [in]"r"(n) /* <-- This is untested. If someone in the community could test this, that would be appreciated! */ - #else - "rev %[out], %[in]" : [out]"=r"(r) : [in]"r"(n) - #endif - ); - return r; - #else - return __builtin_bswap32(n); - #endif - #else - #error "This compiler does not support the byte swap intrinsic." - #endif -#else - return ((n & 0xFF000000) >> 24) | - ((n & 0x00FF0000) >> 8) | - ((n & 0x0000FF00) << 8) | - ((n & 0x000000FF) << 24); -#endif -} - - -#if !defined(MA_EMSCRIPTEN) -#ifdef MA_WIN32 -static void ma_sleep__win32(ma_uint32 milliseconds) -{ - Sleep((DWORD)milliseconds); -} -#endif -#ifdef MA_POSIX -static void ma_sleep__posix(ma_uint32 milliseconds) -{ -#ifdef MA_EMSCRIPTEN - (void)milliseconds; - MA_ASSERT(MA_FALSE); /* The Emscripten build should never sleep. */ -#else - #if (defined(_POSIX_C_SOURCE) && _POSIX_C_SOURCE >= 199309L) || defined(MA_NX) - struct timespec ts; - ts.tv_sec = milliseconds / 1000; - ts.tv_nsec = milliseconds % 1000 * 1000000; - nanosleep(&ts, NULL); - #else - struct timeval tv; - tv.tv_sec = milliseconds / 1000; - tv.tv_usec = milliseconds % 1000 * 1000; - select(0, NULL, NULL, NULL, &tv); - #endif -#endif -} -#endif - -static MA_INLINE void ma_sleep(ma_uint32 milliseconds) -{ -#ifdef MA_WIN32 - ma_sleep__win32(milliseconds); -#endif -#ifdef MA_POSIX - ma_sleep__posix(milliseconds); -#endif -} -#endif - -static MA_INLINE void ma_yield(void) -{ -#if defined(__i386) || defined(_M_IX86) || defined(__x86_64__) || defined(_M_X64) - /* x86/x64 */ - #if (defined(_MSC_VER) || defined(__WATCOMC__) || defined(__DMC__)) && !defined(__clang__) - #if _MSC_VER >= 1400 - _mm_pause(); - #else - #if defined(__DMC__) - /* Digital Mars does not recognize the PAUSE opcode. Fall back to NOP. */ - __asm nop; - #else - __asm pause; - #endif - #endif - #else - __asm__ __volatile__ ("pause"); - #endif -#elif (defined(__arm__) && defined(__ARM_ARCH) && __ARM_ARCH >= 7) || defined(_M_ARM64) || (defined(_M_ARM) && _M_ARM >= 7) || defined(__ARM_ARCH_6K__) || defined(__ARM_ARCH_6T2__) - /* ARM */ - #if defined(_MSC_VER) - /* Apparently there is a __yield() intrinsic that's compatible with ARM, but I cannot find documentation for it nor can I find where it's declared. */ - __yield(); - #else - __asm__ __volatile__ ("yield"); /* ARMv6K/ARMv6T2 and above. */ - #endif -#else - /* Unknown or unsupported architecture. No-op. */ -#endif -} - - -#define MA_MM_DENORMALS_ZERO_MASK 0x0040 -#define MA_MM_FLUSH_ZERO_MASK 0x8000 - -static MA_INLINE unsigned int ma_disable_denormals(void) -{ - unsigned int prevState; - - #if defined(_MSC_VER) - { - /* - Older versions of Visual Studio don't support the "safe" versions of _controlfp_s(). I don't - know which version of Visual Studio first added support for _controlfp_s(), but I do know - that VC6 lacks support. _MSC_VER = 1200 is VC6, but if you get compilation errors on older - versions of Visual Studio, let me know and I'll make the necessary adjustment. - */ - #if _MSC_VER <= 1200 - { - prevState = _statusfp(); - _controlfp(prevState | _DN_FLUSH, _MCW_DN); - } - #else - { - unsigned int unused; - _controlfp_s(&prevState, 0, 0); - _controlfp_s(&unused, prevState | _DN_FLUSH, _MCW_DN); - } - #endif - } - #elif defined(MA_X86) || defined(MA_X64) - { - #if defined(__SSE2__) && !(defined(__TINYC__) || defined(__WATCOMC__) || defined(__COSMOPOLITAN__)) /* <-- Add compilers that lack support for _mm_getcsr() and _mm_setcsr() to this list. */ - { - prevState = _mm_getcsr(); - _mm_setcsr(prevState | MA_MM_DENORMALS_ZERO_MASK | MA_MM_FLUSH_ZERO_MASK); - } - #else - { - /* x88/64, but no support for _mm_getcsr()/_mm_setcsr(). May need to fall back to inlined assembly here. */ - prevState = 0; - } - #endif - } - #else - { - /* Unknown or unsupported architecture. No-op. */ - prevState = 0; - } - #endif - - return prevState; -} - -static MA_INLINE void ma_restore_denormals(unsigned int prevState) -{ - #if defined(_MSC_VER) - { - /* Older versions of Visual Studio do not support _controlfp_s(). See ma_disable_denormals(). */ - #if _MSC_VER <= 1200 - { - _controlfp(prevState, _MCW_DN); - } - #else - { - unsigned int unused; - _controlfp_s(&unused, prevState, _MCW_DN); - } - #endif - } - #elif defined(MA_X86) || defined(MA_X64) - { - #if defined(__SSE2__) && !(defined(__TINYC__) || defined(__WATCOMC__) || defined(__COSMOPOLITAN__)) /* <-- Add compilers that lack support for _mm_getcsr() and _mm_setcsr() to this list. */ - { - _mm_setcsr(prevState); - } - #else - { - /* x88/64, but no support for _mm_getcsr()/_mm_setcsr(). May need to fall back to inlined assembly here. */ - (void)prevState; - } - #endif - } - #else - { - /* Unknown or unsupported architecture. No-op. */ - (void)prevState; - } - #endif -} - - -#ifdef MA_ANDROID -#include - -int ma_android_sdk_version() -{ - char sdkVersion[PROP_VALUE_MAX + 1] = {0, }; - if (__system_property_get("ro.build.version.sdk", sdkVersion)) { - return atoi(sdkVersion); - } - - return 0; -} -#endif - - -#ifndef MA_COINIT_VALUE -#define MA_COINIT_VALUE 0 /* 0 = COINIT_MULTITHREADED */ -#endif - - -#ifndef MA_FLT_MAX - #ifdef FLT_MAX - #define MA_FLT_MAX FLT_MAX - #else - #define MA_FLT_MAX 3.402823466e+38F - #endif -#endif - - -#ifndef MA_PI -#define MA_PI 3.14159265358979323846264f -#endif -#ifndef MA_PI_D -#define MA_PI_D 3.14159265358979323846264 -#endif -#ifndef MA_TAU -#define MA_TAU 6.28318530717958647693f -#endif -#ifndef MA_TAU_D -#define MA_TAU_D 6.28318530717958647693 -#endif - - -/* The default format when ma_format_unknown (0) is requested when initializing a device. */ -#ifndef MA_DEFAULT_FORMAT -#define MA_DEFAULT_FORMAT ma_format_f32 -#endif - -/* The default channel count to use when 0 is used when initializing a device. */ -#ifndef MA_DEFAULT_CHANNELS -#define MA_DEFAULT_CHANNELS 2 -#endif - -/* The default sample rate to use when 0 is used when initializing a device. */ -#ifndef MA_DEFAULT_SAMPLE_RATE -#define MA_DEFAULT_SAMPLE_RATE 48000 -#endif - -/* Default periods when none is specified in ma_device_init(). More periods means more work on the CPU. */ -#ifndef MA_DEFAULT_PERIODS -#define MA_DEFAULT_PERIODS 3 -#endif - -/* The default period size in milliseconds for low latency mode. */ -#ifndef MA_DEFAULT_PERIOD_SIZE_IN_MILLISECONDS_LOW_LATENCY -#define MA_DEFAULT_PERIOD_SIZE_IN_MILLISECONDS_LOW_LATENCY 10 -#endif - -/* The default buffer size in milliseconds for conservative mode. */ -#ifndef MA_DEFAULT_PERIOD_SIZE_IN_MILLISECONDS_CONSERVATIVE -#define MA_DEFAULT_PERIOD_SIZE_IN_MILLISECONDS_CONSERVATIVE 100 -#endif - -/* The default LPF filter order for linear resampling. Note that this is clamped to MA_MAX_FILTER_ORDER. */ -#ifndef MA_DEFAULT_RESAMPLER_LPF_ORDER - #if MA_MAX_FILTER_ORDER >= 4 - #define MA_DEFAULT_RESAMPLER_LPF_ORDER 4 - #else - #define MA_DEFAULT_RESAMPLER_LPF_ORDER MA_MAX_FILTER_ORDER - #endif -#endif - - -#if defined(__clang__) || (defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6))) - #pragma GCC diagnostic push - #pragma GCC diagnostic ignored "-Wunused-variable" -#endif - -/* Standard sample rates, in order of priority. */ -static ma_uint32 g_maStandardSampleRatePriorities[] = { - (ma_uint32)ma_standard_sample_rate_48000, - (ma_uint32)ma_standard_sample_rate_44100, - - (ma_uint32)ma_standard_sample_rate_32000, - (ma_uint32)ma_standard_sample_rate_24000, - (ma_uint32)ma_standard_sample_rate_22050, - - (ma_uint32)ma_standard_sample_rate_88200, - (ma_uint32)ma_standard_sample_rate_96000, - (ma_uint32)ma_standard_sample_rate_176400, - (ma_uint32)ma_standard_sample_rate_192000, - - (ma_uint32)ma_standard_sample_rate_16000, - (ma_uint32)ma_standard_sample_rate_11025, - (ma_uint32)ma_standard_sample_rate_8000, - - (ma_uint32)ma_standard_sample_rate_352800, - (ma_uint32)ma_standard_sample_rate_384000 -}; - -static MA_INLINE ma_bool32 ma_is_standard_sample_rate(ma_uint32 sampleRate) -{ - ma_uint32 iSampleRate; - - for (iSampleRate = 0; iSampleRate < sizeof(g_maStandardSampleRatePriorities) / sizeof(g_maStandardSampleRatePriorities[0]); iSampleRate += 1) { - if (g_maStandardSampleRatePriorities[iSampleRate] == sampleRate) { - return MA_TRUE; - } - } - - /* Getting here means the sample rate is not supported. */ - return MA_FALSE; -} - - -static ma_format g_maFormatPriorities[] = { - ma_format_s16, /* Most common */ - ma_format_f32, - - /*ma_format_s24_32,*/ /* Clean alignment */ - ma_format_s32, - - ma_format_s24, /* Unclean alignment */ - - ma_format_u8 /* Low quality */ -}; -#if defined(__clang__) || (defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6))) - #pragma GCC diagnostic pop -#endif - - -MA_API void ma_version(ma_uint32* pMajor, ma_uint32* pMinor, ma_uint32* pRevision) -{ - if (pMajor) { - *pMajor = MA_VERSION_MAJOR; - } - - if (pMinor) { - *pMinor = MA_VERSION_MINOR; - } - - if (pRevision) { - *pRevision = MA_VERSION_REVISION; - } -} - -MA_API const char* ma_version_string(void) -{ - return MA_VERSION_STRING; -} - - -/****************************************************************************** - -Standard Library Stuff - -******************************************************************************/ -#ifndef MA_ASSERT -#define MA_ASSERT(condition) assert(condition) -#endif - -#ifndef MA_MALLOC -#define MA_MALLOC(sz) malloc((sz)) -#endif -#ifndef MA_REALLOC -#define MA_REALLOC(p, sz) realloc((p), (sz)) -#endif -#ifndef MA_FREE -#define MA_FREE(p) free((p)) -#endif - -static MA_INLINE void ma_zero_memory_default(void* p, size_t sz) -{ - if (p == NULL) { - MA_ASSERT(sz == 0); /* If this is triggered there's an error with the calling code. */ - return; - } - - if (sz > 0) { - memset(p, 0, sz); - } -} - - -#ifndef MA_ZERO_MEMORY -#define MA_ZERO_MEMORY(p, sz) ma_zero_memory_default((p), (sz)) -#endif -#ifndef MA_COPY_MEMORY -#define MA_COPY_MEMORY(dst, src, sz) memcpy((dst), (src), (sz)) -#endif -#ifndef MA_MOVE_MEMORY -#define MA_MOVE_MEMORY(dst, src, sz) memmove((dst), (src), (sz)) -#endif - -#define MA_ZERO_OBJECT(p) MA_ZERO_MEMORY((p), sizeof(*(p))) - -#define ma_countof(x) (sizeof(x) / sizeof(x[0])) -#define ma_max(x, y) (((x) > (y)) ? (x) : (y)) -#define ma_min(x, y) (((x) < (y)) ? (x) : (y)) -#define ma_abs(x) (((x) > 0) ? (x) : -(x)) -#define ma_clamp(x, lo, hi) (ma_max(lo, ma_min(x, hi))) -#define ma_offset_ptr(p, offset) (((ma_uint8*)(p)) + (offset)) -#define ma_align(x, a) (((x) + ((a)-1)) & ~((a)-1)) -#define ma_align_64(x) ma_align(x, 8) - -#define ma_buffer_frame_capacity(buffer, channels, format) (sizeof(buffer) / ma_get_bytes_per_sample(format) / (channels)) - -static MA_INLINE double ma_sind(double x) -{ - /* TODO: Implement custom sin(x). */ - return sin(x); -} - -static MA_INLINE double ma_expd(double x) -{ - /* TODO: Implement custom exp(x). */ - return exp(x); -} - -static MA_INLINE double ma_logd(double x) -{ - /* TODO: Implement custom log(x). */ - return log(x); -} - -static MA_INLINE double ma_powd(double x, double y) -{ - /* TODO: Implement custom pow(x, y). */ - return pow(x, y); -} - -static MA_INLINE double ma_sqrtd(double x) -{ - /* TODO: Implement custom sqrt(x). */ - return sqrt(x); -} - - -static MA_INLINE float ma_rsqrtf(float x) -{ - #if defined(MA_SUPPORT_SSE2) && !defined(MA_NO_SSE2) && (defined(MA_X64) || (defined(_M_IX86_FP) && _M_IX86_FP == 2) || defined(__SSE2__)) - { - /* - For SSE we can use RSQRTSS. - - This Stack Overflow post suggests that compilers don't necessarily generate optimal code - when using intrinsics: - - https://web.archive.org/web/20221211012522/https://stackoverflow.com/questions/32687079/getting-fewest-instructions-for-rsqrtss-wrapper - - I'm going to do something similar here, but a bit simpler. - */ - #if defined(__GNUC__) || defined(__clang__) - { - float result; - __asm__ __volatile__("rsqrtss %1, %0" : "=x"(result) : "x"(x)); - return result; - } - #else - { - return _mm_cvtss_f32(_mm_rsqrt_ss(_mm_set_ps1(x))); - } - #endif - } - #else - { - return 1 / (float)ma_sqrtd(x); - } - #endif -} - - -static MA_INLINE float ma_sinf(float x) -{ - return (float)ma_sind((float)x); -} - -static MA_INLINE double ma_cosd(double x) -{ - return ma_sind((MA_PI_D*0.5) - x); -} - -static MA_INLINE float ma_cosf(float x) -{ - return (float)ma_cosd((float)x); -} - -static MA_INLINE double ma_log10d(double x) -{ - return ma_logd(x) * 0.43429448190325182765; -} - -static MA_INLINE float ma_powf(float x, float y) -{ - return (float)ma_powd((double)x, (double)y); -} - -static MA_INLINE float ma_log10f(float x) -{ - return (float)ma_log10d((double)x); -} - - -static MA_INLINE double ma_degrees_to_radians(double degrees) -{ - return degrees * 0.01745329252; -} - -static MA_INLINE double ma_radians_to_degrees(double radians) -{ - return radians * 57.295779512896; -} - -static MA_INLINE float ma_degrees_to_radians_f(float degrees) -{ - return degrees * 0.01745329252f; -} - -static MA_INLINE float ma_radians_to_degrees_f(float radians) -{ - return radians * 57.295779512896f; -} - - -/* -Return Values: - 0: Success - 22: EINVAL - 34: ERANGE - -Not using symbolic constants for errors because I want to avoid #including errno.h - -These are marked as no-inline because of some bad code generation by Clang. None of these functions -are used in any performance-critical code within miniaudio. -*/ -MA_API MA_NO_INLINE int ma_strcpy_s(char* dst, size_t dstSizeInBytes, const char* src) -{ - size_t i; - - if (dst == 0) { - return 22; - } - if (dstSizeInBytes == 0) { - return 34; - } - if (src == 0) { - dst[0] = '\0'; - return 22; - } - - for (i = 0; i < dstSizeInBytes && src[i] != '\0'; ++i) { - dst[i] = src[i]; - } - - if (i < dstSizeInBytes) { - dst[i] = '\0'; - return 0; - } - - dst[0] = '\0'; - return 34; -} - -MA_API MA_NO_INLINE int ma_wcscpy_s(wchar_t* dst, size_t dstCap, const wchar_t* src) -{ - size_t i; - - if (dst == 0) { - return 22; - } - if (dstCap == 0) { - return 34; - } - if (src == 0) { - dst[0] = '\0'; - return 22; - } - - for (i = 0; i < dstCap && src[i] != '\0'; ++i) { - dst[i] = src[i]; - } - - if (i < dstCap) { - dst[i] = '\0'; - return 0; - } - - dst[0] = '\0'; - return 34; -} - - -MA_API MA_NO_INLINE int ma_strncpy_s(char* dst, size_t dstSizeInBytes, const char* src, size_t count) -{ - size_t maxcount; - size_t i; - - if (dst == 0) { - return 22; - } - if (dstSizeInBytes == 0) { - return 34; - } - if (src == 0) { - dst[0] = '\0'; - return 22; - } - - maxcount = count; - if (count == ((size_t)-1) || count >= dstSizeInBytes) { /* -1 = _TRUNCATE */ - maxcount = dstSizeInBytes - 1; - } - - for (i = 0; i < maxcount && src[i] != '\0'; ++i) { - dst[i] = src[i]; - } - - if (src[i] == '\0' || i == count || count == ((size_t)-1)) { - dst[i] = '\0'; - return 0; - } - - dst[0] = '\0'; - return 34; -} - -MA_API MA_NO_INLINE int ma_strcat_s(char* dst, size_t dstSizeInBytes, const char* src) -{ - char* dstorig; - - if (dst == 0) { - return 22; - } - if (dstSizeInBytes == 0) { - return 34; - } - if (src == 0) { - dst[0] = '\0'; - return 22; - } - - dstorig = dst; - - while (dstSizeInBytes > 0 && dst[0] != '\0') { - dst += 1; - dstSizeInBytes -= 1; - } - - if (dstSizeInBytes == 0) { - return 22; /* Unterminated. */ - } - - - while (dstSizeInBytes > 0 && src[0] != '\0') { - *dst++ = *src++; - dstSizeInBytes -= 1; - } - - if (dstSizeInBytes > 0) { - dst[0] = '\0'; - } else { - dstorig[0] = '\0'; - return 34; - } - - return 0; -} - -MA_API MA_NO_INLINE int ma_strncat_s(char* dst, size_t dstSizeInBytes, const char* src, size_t count) -{ - char* dstorig; - - if (dst == 0) { - return 22; - } - if (dstSizeInBytes == 0) { - return 34; - } - if (src == 0) { - return 22; - } - - dstorig = dst; - - while (dstSizeInBytes > 0 && dst[0] != '\0') { - dst += 1; - dstSizeInBytes -= 1; - } - - if (dstSizeInBytes == 0) { - return 22; /* Unterminated. */ - } - - - if (count == ((size_t)-1)) { /* _TRUNCATE */ - count = dstSizeInBytes - 1; - } - - while (dstSizeInBytes > 0 && src[0] != '\0' && count > 0) { - *dst++ = *src++; - dstSizeInBytes -= 1; - count -= 1; - } - - if (dstSizeInBytes > 0) { - dst[0] = '\0'; - } else { - dstorig[0] = '\0'; - return 34; - } - - return 0; -} - -MA_API MA_NO_INLINE int ma_itoa_s(int value, char* dst, size_t dstSizeInBytes, int radix) -{ - int sign; - unsigned int valueU; - char* dstEnd; - - if (dst == NULL || dstSizeInBytes == 0) { - return 22; - } - if (radix < 2 || radix > 36) { - dst[0] = '\0'; - return 22; - } - - sign = (value < 0 && radix == 10) ? -1 : 1; /* The negative sign is only used when the base is 10. */ - - if (value < 0) { - valueU = -value; - } else { - valueU = value; - } - - dstEnd = dst; - do - { - int remainder = valueU % radix; - if (remainder > 9) { - *dstEnd = (char)((remainder - 10) + 'a'); - } else { - *dstEnd = (char)(remainder + '0'); - } - - dstEnd += 1; - dstSizeInBytes -= 1; - valueU /= radix; - } while (dstSizeInBytes > 0 && valueU > 0); - - if (dstSizeInBytes == 0) { - dst[0] = '\0'; - return 22; /* Ran out of room in the output buffer. */ - } - - if (sign < 0) { - *dstEnd++ = '-'; - dstSizeInBytes -= 1; - } - - if (dstSizeInBytes == 0) { - dst[0] = '\0'; - return 22; /* Ran out of room in the output buffer. */ - } - - *dstEnd = '\0'; - - - /* At this point the string will be reversed. */ - dstEnd -= 1; - while (dst < dstEnd) { - char temp = *dst; - *dst = *dstEnd; - *dstEnd = temp; - - dst += 1; - dstEnd -= 1; - } - - return 0; -} - -MA_API MA_NO_INLINE int ma_strcmp(const char* str1, const char* str2) -{ - if (str1 == str2) return 0; - - /* These checks differ from the standard implementation. It's not important, but I prefer it just for sanity. */ - if (str1 == NULL) return -1; - if (str2 == NULL) return 1; - - for (;;) { - if (str1[0] == '\0') { - break; - } - if (str1[0] != str2[0]) { - break; - } - - str1 += 1; - str2 += 1; - } - - return ((unsigned char*)str1)[0] - ((unsigned char*)str2)[0]; -} - -MA_API MA_NO_INLINE int ma_strappend(char* dst, size_t dstSize, const char* srcA, const char* srcB) -{ - int result; - - result = ma_strncpy_s(dst, dstSize, srcA, (size_t)-1); - if (result != 0) { - return result; - } - - result = ma_strncat_s(dst, dstSize, srcB, (size_t)-1); - if (result != 0) { - return result; - } - - return result; -} - -MA_API MA_NO_INLINE char* ma_copy_string(const char* src, const ma_allocation_callbacks* pAllocationCallbacks) -{ - size_t sz; - char* dst; - - if (src == NULL) { - return NULL; - } - - sz = strlen(src)+1; - dst = (char*)ma_malloc(sz, pAllocationCallbacks); - if (dst == NULL) { - return NULL; - } - - ma_strcpy_s(dst, sz, src); - - return dst; -} - -MA_API MA_NO_INLINE wchar_t* ma_copy_string_w(const wchar_t* src, const ma_allocation_callbacks* pAllocationCallbacks) -{ - size_t sz = wcslen(src)+1; - wchar_t* dst = (wchar_t*)ma_malloc(sz * sizeof(*dst), pAllocationCallbacks); - if (dst == NULL) { - return NULL; - } - - ma_wcscpy_s(dst, sz, src); - - return dst; -} - - - -#include -static ma_result ma_result_from_errno(int e) -{ - if (e == 0) { - return MA_SUCCESS; - } -#ifdef EPERM - else if (e == EPERM) { return MA_INVALID_OPERATION; } -#endif -#ifdef ENOENT - else if (e == ENOENT) { return MA_DOES_NOT_EXIST; } -#endif -#ifdef ESRCH - else if (e == ESRCH) { return MA_DOES_NOT_EXIST; } -#endif -#ifdef EINTR - else if (e == EINTR) { return MA_INTERRUPT; } -#endif -#ifdef EIO - else if (e == EIO) { return MA_IO_ERROR; } -#endif -#ifdef ENXIO - else if (e == ENXIO) { return MA_DOES_NOT_EXIST; } -#endif -#ifdef E2BIG - else if (e == E2BIG) { return MA_INVALID_ARGS; } -#endif -#ifdef ENOEXEC - else if (e == ENOEXEC) { return MA_INVALID_FILE; } -#endif -#ifdef EBADF - else if (e == EBADF) { return MA_INVALID_FILE; } -#endif -#ifdef ECHILD - else if (e == ECHILD) { return MA_ERROR; } -#endif -#ifdef EAGAIN - else if (e == EAGAIN) { return MA_UNAVAILABLE; } -#endif -#ifdef ENOMEM - else if (e == ENOMEM) { return MA_OUT_OF_MEMORY; } -#endif -#ifdef EACCES - else if (e == EACCES) { return MA_ACCESS_DENIED; } -#endif -#ifdef EFAULT - else if (e == EFAULT) { return MA_BAD_ADDRESS; } -#endif -#ifdef ENOTBLK - else if (e == ENOTBLK) { return MA_ERROR; } -#endif -#ifdef EBUSY - else if (e == EBUSY) { return MA_BUSY; } -#endif -#ifdef EEXIST - else if (e == EEXIST) { return MA_ALREADY_EXISTS; } -#endif -#ifdef EXDEV - else if (e == EXDEV) { return MA_ERROR; } -#endif -#ifdef ENODEV - else if (e == ENODEV) { return MA_DOES_NOT_EXIST; } -#endif -#ifdef ENOTDIR - else if (e == ENOTDIR) { return MA_NOT_DIRECTORY; } -#endif -#ifdef EISDIR - else if (e == EISDIR) { return MA_IS_DIRECTORY; } -#endif -#ifdef EINVAL - else if (e == EINVAL) { return MA_INVALID_ARGS; } -#endif -#ifdef ENFILE - else if (e == ENFILE) { return MA_TOO_MANY_OPEN_FILES; } -#endif -#ifdef EMFILE - else if (e == EMFILE) { return MA_TOO_MANY_OPEN_FILES; } -#endif -#ifdef ENOTTY - else if (e == ENOTTY) { return MA_INVALID_OPERATION; } -#endif -#ifdef ETXTBSY - else if (e == ETXTBSY) { return MA_BUSY; } -#endif -#ifdef EFBIG - else if (e == EFBIG) { return MA_TOO_BIG; } -#endif -#ifdef ENOSPC - else if (e == ENOSPC) { return MA_NO_SPACE; } -#endif -#ifdef ESPIPE - else if (e == ESPIPE) { return MA_BAD_SEEK; } -#endif -#ifdef EROFS - else if (e == EROFS) { return MA_ACCESS_DENIED; } -#endif -#ifdef EMLINK - else if (e == EMLINK) { return MA_TOO_MANY_LINKS; } -#endif -#ifdef EPIPE - else if (e == EPIPE) { return MA_BAD_PIPE; } -#endif -#ifdef EDOM - else if (e == EDOM) { return MA_OUT_OF_RANGE; } -#endif -#ifdef ERANGE - else if (e == ERANGE) { return MA_OUT_OF_RANGE; } -#endif -#ifdef EDEADLK - else if (e == EDEADLK) { return MA_DEADLOCK; } -#endif -#ifdef ENAMETOOLONG - else if (e == ENAMETOOLONG) { return MA_PATH_TOO_LONG; } -#endif -#ifdef ENOLCK - else if (e == ENOLCK) { return MA_ERROR; } -#endif -#ifdef ENOSYS - else if (e == ENOSYS) { return MA_NOT_IMPLEMENTED; } -#endif -#ifdef ENOTEMPTY - else if (e == ENOTEMPTY) { return MA_DIRECTORY_NOT_EMPTY; } -#endif -#ifdef ELOOP - else if (e == ELOOP) { return MA_TOO_MANY_LINKS; } -#endif -#ifdef ENOMSG - else if (e == ENOMSG) { return MA_NO_MESSAGE; } -#endif -#ifdef EIDRM - else if (e == EIDRM) { return MA_ERROR; } -#endif -#ifdef ECHRNG - else if (e == ECHRNG) { return MA_ERROR; } -#endif -#ifdef EL2NSYNC - else if (e == EL2NSYNC) { return MA_ERROR; } -#endif -#ifdef EL3HLT - else if (e == EL3HLT) { return MA_ERROR; } -#endif -#ifdef EL3RST - else if (e == EL3RST) { return MA_ERROR; } -#endif -#ifdef ELNRNG - else if (e == ELNRNG) { return MA_OUT_OF_RANGE; } -#endif -#ifdef EUNATCH - else if (e == EUNATCH) { return MA_ERROR; } -#endif -#ifdef ENOCSI - else if (e == ENOCSI) { return MA_ERROR; } -#endif -#ifdef EL2HLT - else if (e == EL2HLT) { return MA_ERROR; } -#endif -#ifdef EBADE - else if (e == EBADE) { return MA_ERROR; } -#endif -#ifdef EBADR - else if (e == EBADR) { return MA_ERROR; } -#endif -#ifdef EXFULL - else if (e == EXFULL) { return MA_ERROR; } -#endif -#ifdef ENOANO - else if (e == ENOANO) { return MA_ERROR; } -#endif -#ifdef EBADRQC - else if (e == EBADRQC) { return MA_ERROR; } -#endif -#ifdef EBADSLT - else if (e == EBADSLT) { return MA_ERROR; } -#endif -#ifdef EBFONT - else if (e == EBFONT) { return MA_INVALID_FILE; } -#endif -#ifdef ENOSTR - else if (e == ENOSTR) { return MA_ERROR; } -#endif -#ifdef ENODATA - else if (e == ENODATA) { return MA_NO_DATA_AVAILABLE; } -#endif -#ifdef ETIME - else if (e == ETIME) { return MA_TIMEOUT; } -#endif -#ifdef ENOSR - else if (e == ENOSR) { return MA_NO_DATA_AVAILABLE; } -#endif -#ifdef ENONET - else if (e == ENONET) { return MA_NO_NETWORK; } -#endif -#ifdef ENOPKG - else if (e == ENOPKG) { return MA_ERROR; } -#endif -#ifdef EREMOTE - else if (e == EREMOTE) { return MA_ERROR; } -#endif -#ifdef ENOLINK - else if (e == ENOLINK) { return MA_ERROR; } -#endif -#ifdef EADV - else if (e == EADV) { return MA_ERROR; } -#endif -#ifdef ESRMNT - else if (e == ESRMNT) { return MA_ERROR; } -#endif -#ifdef ECOMM - else if (e == ECOMM) { return MA_ERROR; } -#endif -#ifdef EPROTO - else if (e == EPROTO) { return MA_ERROR; } -#endif -#ifdef EMULTIHOP - else if (e == EMULTIHOP) { return MA_ERROR; } -#endif -#ifdef EDOTDOT - else if (e == EDOTDOT) { return MA_ERROR; } -#endif -#ifdef EBADMSG - else if (e == EBADMSG) { return MA_BAD_MESSAGE; } -#endif -#ifdef EOVERFLOW - else if (e == EOVERFLOW) { return MA_TOO_BIG; } -#endif -#ifdef ENOTUNIQ - else if (e == ENOTUNIQ) { return MA_NOT_UNIQUE; } -#endif -#ifdef EBADFD - else if (e == EBADFD) { return MA_ERROR; } -#endif -#ifdef EREMCHG - else if (e == EREMCHG) { return MA_ERROR; } -#endif -#ifdef ELIBACC - else if (e == ELIBACC) { return MA_ACCESS_DENIED; } -#endif -#ifdef ELIBBAD - else if (e == ELIBBAD) { return MA_INVALID_FILE; } -#endif -#ifdef ELIBSCN - else if (e == ELIBSCN) { return MA_INVALID_FILE; } -#endif -#ifdef ELIBMAX - else if (e == ELIBMAX) { return MA_ERROR; } -#endif -#ifdef ELIBEXEC - else if (e == ELIBEXEC) { return MA_ERROR; } -#endif -#ifdef EILSEQ - else if (e == EILSEQ) { return MA_INVALID_DATA; } -#endif -#ifdef ERESTART - else if (e == ERESTART) { return MA_ERROR; } -#endif -#ifdef ESTRPIPE - else if (e == ESTRPIPE) { return MA_ERROR; } -#endif -#ifdef EUSERS - else if (e == EUSERS) { return MA_ERROR; } -#endif -#ifdef ENOTSOCK - else if (e == ENOTSOCK) { return MA_NOT_SOCKET; } -#endif -#ifdef EDESTADDRREQ - else if (e == EDESTADDRREQ) { return MA_NO_ADDRESS; } -#endif -#ifdef EMSGSIZE - else if (e == EMSGSIZE) { return MA_TOO_BIG; } -#endif -#ifdef EPROTOTYPE - else if (e == EPROTOTYPE) { return MA_BAD_PROTOCOL; } -#endif -#ifdef ENOPROTOOPT - else if (e == ENOPROTOOPT) { return MA_PROTOCOL_UNAVAILABLE; } -#endif -#ifdef EPROTONOSUPPORT - else if (e == EPROTONOSUPPORT) { return MA_PROTOCOL_NOT_SUPPORTED; } -#endif -#ifdef ESOCKTNOSUPPORT - else if (e == ESOCKTNOSUPPORT) { return MA_SOCKET_NOT_SUPPORTED; } -#endif -#ifdef EOPNOTSUPP - else if (e == EOPNOTSUPP) { return MA_INVALID_OPERATION; } -#endif -#ifdef EPFNOSUPPORT - else if (e == EPFNOSUPPORT) { return MA_PROTOCOL_FAMILY_NOT_SUPPORTED; } -#endif -#ifdef EAFNOSUPPORT - else if (e == EAFNOSUPPORT) { return MA_ADDRESS_FAMILY_NOT_SUPPORTED; } -#endif -#ifdef EADDRINUSE - else if (e == EADDRINUSE) { return MA_ALREADY_IN_USE; } -#endif -#ifdef EADDRNOTAVAIL - else if (e == EADDRNOTAVAIL) { return MA_ERROR; } -#endif -#ifdef ENETDOWN - else if (e == ENETDOWN) { return MA_NO_NETWORK; } -#endif -#ifdef ENETUNREACH - else if (e == ENETUNREACH) { return MA_NO_NETWORK; } -#endif -#ifdef ENETRESET - else if (e == ENETRESET) { return MA_NO_NETWORK; } -#endif -#ifdef ECONNABORTED - else if (e == ECONNABORTED) { return MA_NO_NETWORK; } -#endif -#ifdef ECONNRESET - else if (e == ECONNRESET) { return MA_CONNECTION_RESET; } -#endif -#ifdef ENOBUFS - else if (e == ENOBUFS) { return MA_NO_SPACE; } -#endif -#ifdef EISCONN - else if (e == EISCONN) { return MA_ALREADY_CONNECTED; } -#endif -#ifdef ENOTCONN - else if (e == ENOTCONN) { return MA_NOT_CONNECTED; } -#endif -#ifdef ESHUTDOWN - else if (e == ESHUTDOWN) { return MA_ERROR; } -#endif -#ifdef ETOOMANYREFS - else if (e == ETOOMANYREFS) { return MA_ERROR; } -#endif -#ifdef ETIMEDOUT - else if (e == ETIMEDOUT) { return MA_TIMEOUT; } -#endif -#ifdef ECONNREFUSED - else if (e == ECONNREFUSED) { return MA_CONNECTION_REFUSED; } -#endif -#ifdef EHOSTDOWN - else if (e == EHOSTDOWN) { return MA_NO_HOST; } -#endif -#ifdef EHOSTUNREACH - else if (e == EHOSTUNREACH) { return MA_NO_HOST; } -#endif -#ifdef EALREADY - else if (e == EALREADY) { return MA_IN_PROGRESS; } -#endif -#ifdef EINPROGRESS - else if (e == EINPROGRESS) { return MA_IN_PROGRESS; } -#endif -#ifdef ESTALE - else if (e == ESTALE) { return MA_INVALID_FILE; } -#endif -#ifdef EUCLEAN - else if (e == EUCLEAN) { return MA_ERROR; } -#endif -#ifdef ENOTNAM - else if (e == ENOTNAM) { return MA_ERROR; } -#endif -#ifdef ENAVAIL - else if (e == ENAVAIL) { return MA_ERROR; } -#endif -#ifdef EISNAM - else if (e == EISNAM) { return MA_ERROR; } -#endif -#ifdef EREMOTEIO - else if (e == EREMOTEIO) { return MA_IO_ERROR; } -#endif -#ifdef EDQUOT - else if (e == EDQUOT) { return MA_NO_SPACE; } -#endif -#ifdef ENOMEDIUM - else if (e == ENOMEDIUM) { return MA_DOES_NOT_EXIST; } -#endif -#ifdef EMEDIUMTYPE - else if (e == EMEDIUMTYPE) { return MA_ERROR; } -#endif -#ifdef ECANCELED - else if (e == ECANCELED) { return MA_CANCELLED; } -#endif -#ifdef ENOKEY - else if (e == ENOKEY) { return MA_ERROR; } -#endif -#ifdef EKEYEXPIRED - else if (e == EKEYEXPIRED) { return MA_ERROR; } -#endif -#ifdef EKEYREVOKED - else if (e == EKEYREVOKED) { return MA_ERROR; } -#endif -#ifdef EKEYREJECTED - else if (e == EKEYREJECTED) { return MA_ERROR; } -#endif -#ifdef EOWNERDEAD - else if (e == EOWNERDEAD) { return MA_ERROR; } -#endif -#ifdef ENOTRECOVERABLE - else if (e == ENOTRECOVERABLE) { return MA_ERROR; } -#endif -#ifdef ERFKILL - else if (e == ERFKILL) { return MA_ERROR; } -#endif -#ifdef EHWPOISON - else if (e == EHWPOISON) { return MA_ERROR; } -#endif - else { - return MA_ERROR; - } -} - -MA_API ma_result ma_fopen(FILE** ppFile, const char* pFilePath, const char* pOpenMode) -{ -#if defined(_MSC_VER) && _MSC_VER >= 1400 - errno_t err; -#endif - - if (ppFile != NULL) { - *ppFile = NULL; /* Safety. */ - } - - if (pFilePath == NULL || pOpenMode == NULL || ppFile == NULL) { - return MA_INVALID_ARGS; - } - -#if defined(_MSC_VER) && _MSC_VER >= 1400 - err = fopen_s(ppFile, pFilePath, pOpenMode); - if (err != 0) { - return ma_result_from_errno(err); - } -#else -#if defined(_WIN32) || defined(__APPLE__) - *ppFile = fopen(pFilePath, pOpenMode); -#else - #if defined(_FILE_OFFSET_BITS) && _FILE_OFFSET_BITS == 64 && defined(_LARGEFILE64_SOURCE) - *ppFile = fopen64(pFilePath, pOpenMode); - #else - *ppFile = fopen(pFilePath, pOpenMode); - #endif -#endif - if (*ppFile == NULL) { - ma_result result = ma_result_from_errno(errno); - if (result == MA_SUCCESS) { - result = MA_ERROR; /* Just a safety check to make sure we never ever return success when pFile == NULL. */ - } - - return result; - } -#endif - - return MA_SUCCESS; -} - - - -/* -_wfopen() isn't always available in all compilation environments. - - * Windows only. - * MSVC seems to support it universally as far back as VC6 from what I can tell (haven't checked further back). - * MinGW-64 (both 32- and 64-bit) seems to support it. - * MinGW wraps it in !defined(__STRICT_ANSI__). - * OpenWatcom wraps it in !defined(_NO_EXT_KEYS). - -This can be reviewed as compatibility issues arise. The preference is to use _wfopen_s() and _wfopen() as opposed to the wcsrtombs() -fallback, so if you notice your compiler not detecting this properly I'm happy to look at adding support. -*/ -#if defined(_WIN32) - #if defined(_MSC_VER) || defined(__MINGW64__) || (!defined(__STRICT_ANSI__) && !defined(_NO_EXT_KEYS)) - #define MA_HAS_WFOPEN - #endif -#endif - -MA_API ma_result ma_wfopen(FILE** ppFile, const wchar_t* pFilePath, const wchar_t* pOpenMode, const ma_allocation_callbacks* pAllocationCallbacks) -{ - if (ppFile != NULL) { - *ppFile = NULL; /* Safety. */ - } - - if (pFilePath == NULL || pOpenMode == NULL || ppFile == NULL) { - return MA_INVALID_ARGS; - } - -#if defined(MA_HAS_WFOPEN) - { - /* Use _wfopen() on Windows. */ - #if defined(_MSC_VER) && _MSC_VER >= 1400 - errno_t err = _wfopen_s(ppFile, pFilePath, pOpenMode); - if (err != 0) { - return ma_result_from_errno(err); - } - #else - *ppFile = _wfopen(pFilePath, pOpenMode); - if (*ppFile == NULL) { - return ma_result_from_errno(errno); - } - #endif - (void)pAllocationCallbacks; - } -#else - /* - Use fopen() on anything other than Windows. Requires a conversion. This is annoying because fopen() is locale specific. The only real way I can - think of to do this is with wcsrtombs(). Note that wcstombs() is apparently not thread-safe because it uses a static global mbstate_t object for - maintaining state. I've checked this with -std=c89 and it works, but if somebody get's a compiler error I'll look into improving compatibility. - */ - { - mbstate_t mbs; - size_t lenMB; - const wchar_t* pFilePathTemp = pFilePath; - char* pFilePathMB = NULL; - char pOpenModeMB[32] = {0}; - - /* Get the length first. */ - MA_ZERO_OBJECT(&mbs); - lenMB = wcsrtombs(NULL, &pFilePathTemp, 0, &mbs); - if (lenMB == (size_t)-1) { - return ma_result_from_errno(errno); - } - - pFilePathMB = (char*)ma_malloc(lenMB + 1, pAllocationCallbacks); - if (pFilePathMB == NULL) { - return MA_OUT_OF_MEMORY; - } - - pFilePathTemp = pFilePath; - MA_ZERO_OBJECT(&mbs); - wcsrtombs(pFilePathMB, &pFilePathTemp, lenMB + 1, &mbs); - - /* The open mode should always consist of ASCII characters so we should be able to do a trivial conversion. */ - { - size_t i = 0; - for (;;) { - if (pOpenMode[i] == 0) { - pOpenModeMB[i] = '\0'; - break; - } - - pOpenModeMB[i] = (char)pOpenMode[i]; - i += 1; - } - } - - *ppFile = fopen(pFilePathMB, pOpenModeMB); - - ma_free(pFilePathMB, pAllocationCallbacks); - } - - if (*ppFile == NULL) { - return MA_ERROR; - } -#endif - - return MA_SUCCESS; -} - - - -static MA_INLINE void ma_copy_memory_64(void* dst, const void* src, ma_uint64 sizeInBytes) -{ -#if 0xFFFFFFFFFFFFFFFF <= MA_SIZE_MAX - MA_COPY_MEMORY(dst, src, (size_t)sizeInBytes); -#else - while (sizeInBytes > 0) { - ma_uint64 bytesToCopyNow = sizeInBytes; - if (bytesToCopyNow > MA_SIZE_MAX) { - bytesToCopyNow = MA_SIZE_MAX; - } - - MA_COPY_MEMORY(dst, src, (size_t)bytesToCopyNow); /* Safe cast to size_t. */ - - sizeInBytes -= bytesToCopyNow; - dst = ( void*)(( ma_uint8*)dst + bytesToCopyNow); - src = (const void*)((const ma_uint8*)src + bytesToCopyNow); - } -#endif -} - -static MA_INLINE void ma_zero_memory_64(void* dst, ma_uint64 sizeInBytes) -{ -#if 0xFFFFFFFFFFFFFFFF <= MA_SIZE_MAX - MA_ZERO_MEMORY(dst, (size_t)sizeInBytes); -#else - while (sizeInBytes > 0) { - ma_uint64 bytesToZeroNow = sizeInBytes; - if (bytesToZeroNow > MA_SIZE_MAX) { - bytesToZeroNow = MA_SIZE_MAX; - } - - MA_ZERO_MEMORY(dst, (size_t)bytesToZeroNow); /* Safe cast to size_t. */ - - sizeInBytes -= bytesToZeroNow; - dst = (void*)((ma_uint8*)dst + bytesToZeroNow); - } -#endif -} - - -/* Thanks to good old Bit Twiddling Hacks for this one: http://graphics.stanford.edu/~seander/bithacks.html#RoundUpPowerOf2 */ -static MA_INLINE unsigned int ma_next_power_of_2(unsigned int x) -{ - x--; - x |= x >> 1; - x |= x >> 2; - x |= x >> 4; - x |= x >> 8; - x |= x >> 16; - x++; - - return x; -} - -static MA_INLINE unsigned int ma_prev_power_of_2(unsigned int x) -{ - return ma_next_power_of_2(x) >> 1; -} - -static MA_INLINE unsigned int ma_round_to_power_of_2(unsigned int x) -{ - unsigned int prev = ma_prev_power_of_2(x); - unsigned int next = ma_next_power_of_2(x); - if ((next - x) > (x - prev)) { - return prev; - } else { - return next; - } -} - -static MA_INLINE unsigned int ma_count_set_bits(unsigned int x) -{ - unsigned int count = 0; - while (x != 0) { - if (x & 1) { - count += 1; - } - - x = x >> 1; - } - - return count; -} - - - -/************************************************************************************************************************************************************** - -Allocation Callbacks - -**************************************************************************************************************************************************************/ -static void* ma__malloc_default(size_t sz, void* pUserData) -{ - (void)pUserData; - return MA_MALLOC(sz); -} - -static void* ma__realloc_default(void* p, size_t sz, void* pUserData) -{ - (void)pUserData; - return MA_REALLOC(p, sz); -} - -static void ma__free_default(void* p, void* pUserData) -{ - (void)pUserData; - MA_FREE(p); -} - -static ma_allocation_callbacks ma_allocation_callbacks_init_default(void) -{ - ma_allocation_callbacks callbacks; - callbacks.pUserData = NULL; - callbacks.onMalloc = ma__malloc_default; - callbacks.onRealloc = ma__realloc_default; - callbacks.onFree = ma__free_default; - - return callbacks; -} - -static ma_result ma_allocation_callbacks_init_copy(ma_allocation_callbacks* pDst, const ma_allocation_callbacks* pSrc) -{ - if (pDst == NULL) { - return MA_INVALID_ARGS; - } - - if (pSrc == NULL) { - *pDst = ma_allocation_callbacks_init_default(); - } else { - if (pSrc->pUserData == NULL && pSrc->onFree == NULL && pSrc->onMalloc == NULL && pSrc->onRealloc == NULL) { - *pDst = ma_allocation_callbacks_init_default(); - } else { - if (pSrc->onFree == NULL || (pSrc->onMalloc == NULL && pSrc->onRealloc == NULL)) { - return MA_INVALID_ARGS; /* Invalid allocation callbacks. */ - } else { - *pDst = *pSrc; - } - } - } - - return MA_SUCCESS; -} - - - - -/************************************************************************************************************************************************************** - -Logging - -**************************************************************************************************************************************************************/ -MA_API const char* ma_log_level_to_string(ma_uint32 logLevel) -{ - switch (logLevel) - { - case MA_LOG_LEVEL_DEBUG: return "DEBUG"; - case MA_LOG_LEVEL_INFO: return "INFO"; - case MA_LOG_LEVEL_WARNING: return "WARNING"; - case MA_LOG_LEVEL_ERROR: return "ERROR"; - default: return "ERROR"; - } -} - -#if defined(MA_DEBUG_OUTPUT) -#if defined(MA_ANDROID) - #include -#endif - -/* Customize this to use a specific tag in __android_log_print() for debug output messages. */ -#ifndef MA_ANDROID_LOG_TAG -#define MA_ANDROID_LOG_TAG "miniaudio" -#endif - -void ma_log_callback_debug(void* pUserData, ma_uint32 level, const char* pMessage) -{ - (void)pUserData; - - /* Special handling for some platforms. */ - #if defined(MA_ANDROID) - { - /* Android. */ - __android_log_print(ANDROID_LOG_DEBUG, MA_ANDROID_LOG_TAG, "%s: %s", ma_log_level_to_string(level), pMessage); - } - #else - { - /* Everything else. */ - printf("%s: %s", ma_log_level_to_string(level), pMessage); - } - #endif -} -#endif - -MA_API ma_log_callback ma_log_callback_init(ma_log_callback_proc onLog, void* pUserData) -{ - ma_log_callback callback; - - MA_ZERO_OBJECT(&callback); - callback.onLog = onLog; - callback.pUserData = pUserData; - - return callback; -} - - -MA_API ma_result ma_log_init(const ma_allocation_callbacks* pAllocationCallbacks, ma_log* pLog) -{ - if (pLog == NULL) { - return MA_INVALID_ARGS; - } - - MA_ZERO_OBJECT(pLog); - ma_allocation_callbacks_init_copy(&pLog->allocationCallbacks, pAllocationCallbacks); - - /* We need a mutex for thread safety. */ - #ifndef MA_NO_THREADING - { - ma_result result = ma_mutex_init(&pLog->lock); - if (result != MA_SUCCESS) { - return result; - } - } - #endif - - /* If we're using debug output, enable it. */ - #if defined(MA_DEBUG_OUTPUT) - { - ma_log_register_callback(pLog, ma_log_callback_init(ma_log_callback_debug, NULL)); /* Doesn't really matter if this fails. */ - } - #endif - - return MA_SUCCESS; -} - -MA_API void ma_log_uninit(ma_log* pLog) -{ - if (pLog == NULL) { - return; - } - -#ifndef MA_NO_THREADING - ma_mutex_uninit(&pLog->lock); -#endif -} - -static void ma_log_lock(ma_log* pLog) -{ -#ifndef MA_NO_THREADING - ma_mutex_lock(&pLog->lock); -#else - (void)pLog; -#endif -} - -static void ma_log_unlock(ma_log* pLog) -{ -#ifndef MA_NO_THREADING - ma_mutex_unlock(&pLog->lock); -#else - (void)pLog; -#endif -} - -MA_API ma_result ma_log_register_callback(ma_log* pLog, ma_log_callback callback) -{ - ma_result result = MA_SUCCESS; - - if (pLog == NULL || callback.onLog == NULL) { - return MA_INVALID_ARGS; - } - - ma_log_lock(pLog); - { - if (pLog->callbackCount == ma_countof(pLog->callbacks)) { - result = MA_OUT_OF_MEMORY; /* Reached the maximum allowed log callbacks. */ - } else { - pLog->callbacks[pLog->callbackCount] = callback; - pLog->callbackCount += 1; - } - } - ma_log_unlock(pLog); - - return result; -} - -MA_API ma_result ma_log_unregister_callback(ma_log* pLog, ma_log_callback callback) -{ - if (pLog == NULL) { - return MA_INVALID_ARGS; - } - - ma_log_lock(pLog); - { - ma_uint32 iLog; - for (iLog = 0; iLog < pLog->callbackCount; ) { - if (pLog->callbacks[iLog].onLog == callback.onLog) { - /* Found. Move everything down a slot. */ - ma_uint32 jLog; - for (jLog = iLog; jLog < pLog->callbackCount-1; jLog += 1) { - pLog->callbacks[jLog] = pLog->callbacks[jLog + 1]; - } - - pLog->callbackCount -= 1; - } else { - /* Not found. */ - iLog += 1; - } - } - } - ma_log_unlock(pLog); - - return MA_SUCCESS; -} - -MA_API ma_result ma_log_post(ma_log* pLog, ma_uint32 level, const char* pMessage) -{ - if (pLog == NULL || pMessage == NULL) { - return MA_INVALID_ARGS; - } - - ma_log_lock(pLog); - { - ma_uint32 iLog; - for (iLog = 0; iLog < pLog->callbackCount; iLog += 1) { - if (pLog->callbacks[iLog].onLog) { - pLog->callbacks[iLog].onLog(pLog->callbacks[iLog].pUserData, level, pMessage); - } - } - } - ma_log_unlock(pLog); - - return MA_SUCCESS; -} - - -/* -We need to emulate _vscprintf() for the VC6 build. This can be more efficient, but since it's only VC6, and it's just a -logging function, I'm happy to keep this simple. In the VC6 build we can implement this in terms of _vsnprintf(). -*/ -#if defined(_MSC_VER) && _MSC_VER < 1900 -static int ma_vscprintf(const ma_allocation_callbacks* pAllocationCallbacks, const char* format, va_list args) -{ -#if _MSC_VER > 1200 - return _vscprintf(format, args); -#else - int result; - char* pTempBuffer = NULL; - size_t tempBufferCap = 1024; - - if (format == NULL) { - errno = EINVAL; - return -1; - } - - for (;;) { - char* pNewTempBuffer = (char*)ma_realloc(pTempBuffer, tempBufferCap, pAllocationCallbacks); - if (pNewTempBuffer == NULL) { - ma_free(pTempBuffer, pAllocationCallbacks); - errno = ENOMEM; - return -1; /* Out of memory. */ - } - - pTempBuffer = pNewTempBuffer; - - result = _vsnprintf(pTempBuffer, tempBufferCap, format, args); - ma_free(pTempBuffer, NULL); - - if (result != -1) { - break; /* Got it. */ - } - - /* Buffer wasn't big enough. Ideally it'd be nice to use an error code to know the reason for sure, but this is reliable enough. */ - tempBufferCap *= 2; - } - - return result; -#endif -} -#endif - -MA_API ma_result ma_log_postv(ma_log* pLog, ma_uint32 level, const char* pFormat, va_list args) -{ - if (pLog == NULL || pFormat == NULL) { - return MA_INVALID_ARGS; - } - - #if (defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L) || ((!defined(_MSC_VER) || _MSC_VER >= 1900) && !defined(__STRICT_ANSI__) && !defined(_NO_EXT_KEYS)) || (defined(__cplusplus) && __cplusplus >= 201103L) - { - ma_result result; - int length; - char pFormattedMessageStack[1024]; - char* pFormattedMessageHeap = NULL; - - /* First try formatting into our fixed sized stack allocated buffer. If this is too small we'll fallback to a heap allocation. */ - length = vsnprintf(pFormattedMessageStack, sizeof(pFormattedMessageStack), pFormat, args); - if (length < 0) { - return MA_INVALID_OPERATION; /* An error occurred when trying to convert the buffer. */ - } - - if ((size_t)length < sizeof(pFormattedMessageStack)) { - /* The string was written to the stack. */ - result = ma_log_post(pLog, level, pFormattedMessageStack); - } else { - /* The stack buffer was too small, try the heap. */ - pFormattedMessageHeap = (char*)ma_malloc(length + 1, &pLog->allocationCallbacks); - if (pFormattedMessageHeap == NULL) { - return MA_OUT_OF_MEMORY; - } - - length = vsnprintf(pFormattedMessageHeap, length + 1, pFormat, args); - if (length < 0) { - ma_free(pFormattedMessageHeap, &pLog->allocationCallbacks); - return MA_INVALID_OPERATION; - } - - result = ma_log_post(pLog, level, pFormattedMessageHeap); - ma_free(pFormattedMessageHeap, &pLog->allocationCallbacks); - } - - return result; - } - #else - { - /* - Without snprintf() we need to first measure the string and then heap allocate it. I'm only aware of Visual Studio having support for this without snprintf(), so we'll - need to restrict this branch to Visual Studio. For other compilers we need to just not support formatted logging because I don't want the security risk of overflowing - a fixed sized stack allocated buffer. - */ - #if defined(_MSC_VER) && _MSC_VER >= 1200 /* 1200 = VC6 */ - { - ma_result result; - int formattedLen; - char* pFormattedMessage = NULL; - va_list args2; - - #if _MSC_VER >= 1800 - { - va_copy(args2, args); - } - #else - { - args2 = args; - } - #endif - - formattedLen = ma_vscprintf(&pLog->allocationCallbacks, pFormat, args2); - va_end(args2); - - if (formattedLen <= 0) { - return MA_INVALID_OPERATION; - } - - pFormattedMessage = (char*)ma_malloc(formattedLen + 1, &pLog->allocationCallbacks); - if (pFormattedMessage == NULL) { - return MA_OUT_OF_MEMORY; - } - - /* We'll get errors on newer versions of Visual Studio if we try to use vsprintf(). */ - #if _MSC_VER >= 1400 /* 1400 = Visual Studio 2005 */ - { - vsprintf_s(pFormattedMessage, formattedLen + 1, pFormat, args); - } - #else - { - vsprintf(pFormattedMessage, pFormat, args); - } - #endif - - result = ma_log_post(pLog, level, pFormattedMessage); - ma_free(pFormattedMessage, &pLog->allocationCallbacks); - - return result; - } - #else - { - /* Can't do anything because we don't have a safe way of to emulate vsnprintf() without a manual solution. */ - (void)level; - (void)args; - - return MA_INVALID_OPERATION; - } - #endif - } - #endif -} - -MA_API ma_result ma_log_postf(ma_log* pLog, ma_uint32 level, const char* pFormat, ...) -{ - ma_result result; - va_list args; - - if (pLog == NULL || pFormat == NULL) { - return MA_INVALID_ARGS; - } - - va_start(args, pFormat); - { - result = ma_log_postv(pLog, level, pFormat, args); - } - va_end(args); - - return result; -} - - - -static MA_INLINE ma_uint8 ma_clip_u8(ma_int32 x) -{ - return (ma_uint8)(ma_clamp(x, -128, 127) + 128); -} - -static MA_INLINE ma_int16 ma_clip_s16(ma_int32 x) -{ - return (ma_int16)ma_clamp(x, -32768, 32767); -} - -static MA_INLINE ma_int64 ma_clip_s24(ma_int64 x) -{ - return (ma_int64)ma_clamp(x, -8388608, 8388607); -} - -static MA_INLINE ma_int32 ma_clip_s32(ma_int64 x) -{ - /* This dance is to silence warnings with -std=c89. A good compiler should be able to optimize this away. */ - ma_int64 clipMin; - ma_int64 clipMax; - clipMin = -((ma_int64)2147483647 + 1); - clipMax = (ma_int64)2147483647; - - return (ma_int32)ma_clamp(x, clipMin, clipMax); -} - -static MA_INLINE float ma_clip_f32(float x) -{ - if (x < -1) return -1; - if (x > +1) return +1; - return x; -} - - -static MA_INLINE float ma_mix_f32(float x, float y, float a) -{ - return x*(1-a) + y*a; -} -static MA_INLINE float ma_mix_f32_fast(float x, float y, float a) -{ - float r0 = (y - x); - float r1 = r0*a; - return x + r1; - /*return x + (y - x)*a;*/ -} - -#if defined(MA_SUPPORT_SSE2) -static MA_INLINE __m128 ma_mix_f32_fast__sse2(__m128 x, __m128 y, __m128 a) -{ - return _mm_add_ps(x, _mm_mul_ps(_mm_sub_ps(y, x), a)); -} -#endif -#if defined(MA_SUPPORT_AVX2) -static MA_INLINE __m256 ma_mix_f32_fast__avx2(__m256 x, __m256 y, __m256 a) -{ - return _mm256_add_ps(x, _mm256_mul_ps(_mm256_sub_ps(y, x), a)); -} -#endif -#if defined(MA_SUPPORT_NEON) -static MA_INLINE float32x4_t ma_mix_f32_fast__neon(float32x4_t x, float32x4_t y, float32x4_t a) -{ - return vaddq_f32(x, vmulq_f32(vsubq_f32(y, x), a)); -} -#endif - - -static MA_INLINE double ma_mix_f64(double x, double y, double a) -{ - return x*(1-a) + y*a; -} -static MA_INLINE double ma_mix_f64_fast(double x, double y, double a) -{ - return x + (y - x)*a; -} - -static MA_INLINE float ma_scale_to_range_f32(float x, float lo, float hi) -{ - return lo + x*(hi-lo); -} - - -/* -Greatest common factor using Euclid's algorithm iteratively. -*/ -static MA_INLINE ma_uint32 ma_gcf_u32(ma_uint32 a, ma_uint32 b) -{ - for (;;) { - if (b == 0) { - break; - } else { - ma_uint32 t = a; - a = b; - b = t % a; - } - } - - return a; -} - - -static ma_uint32 ma_ffs_32(ma_uint32 x) -{ - ma_uint32 i; - - /* Just a naive implementation just to get things working for now. Will optimize this later. */ - for (i = 0; i < 32; i += 1) { - if ((x & (1U << i)) != 0) { - return i; - } - } - - return i; -} - -static MA_INLINE ma_int16 ma_float_to_fixed_16(float x) -{ - return (ma_int16)(x * (1 << 8)); -} - - - -/* -Random Number Generation - -miniaudio uses the LCG random number generation algorithm. This is good enough for audio. - -Note that miniaudio's global LCG implementation uses global state which is _not_ thread-local. When this is called across -multiple threads, results will be unpredictable. However, it won't crash and results will still be random enough for -miniaudio's purposes. -*/ -#ifndef MA_DEFAULT_LCG_SEED -#define MA_DEFAULT_LCG_SEED 4321 -#endif - -#define MA_LCG_M 2147483647 -#define MA_LCG_A 48271 -#define MA_LCG_C 0 - -static ma_lcg g_maLCG = {MA_DEFAULT_LCG_SEED}; /* Non-zero initial seed. Use ma_seed() to use an explicit seed. */ - -static MA_INLINE void ma_lcg_seed(ma_lcg* pLCG, ma_int32 seed) -{ - MA_ASSERT(pLCG != NULL); - pLCG->state = seed; -} - -static MA_INLINE ma_int32 ma_lcg_rand_s32(ma_lcg* pLCG) -{ - pLCG->state = (MA_LCG_A * pLCG->state + MA_LCG_C) % MA_LCG_M; - return pLCG->state; -} - -static MA_INLINE ma_uint32 ma_lcg_rand_u32(ma_lcg* pLCG) -{ - return (ma_uint32)ma_lcg_rand_s32(pLCG); -} - -static MA_INLINE ma_int16 ma_lcg_rand_s16(ma_lcg* pLCG) -{ - return (ma_int16)(ma_lcg_rand_s32(pLCG) & 0xFFFF); -} - -static MA_INLINE double ma_lcg_rand_f64(ma_lcg* pLCG) -{ - return ma_lcg_rand_s32(pLCG) / (double)0x7FFFFFFF; -} - -static MA_INLINE float ma_lcg_rand_f32(ma_lcg* pLCG) -{ - return (float)ma_lcg_rand_f64(pLCG); -} - -static MA_INLINE float ma_lcg_rand_range_f32(ma_lcg* pLCG, float lo, float hi) -{ - return ma_scale_to_range_f32(ma_lcg_rand_f32(pLCG), lo, hi); -} - -static MA_INLINE ma_int32 ma_lcg_rand_range_s32(ma_lcg* pLCG, ma_int32 lo, ma_int32 hi) -{ - if (lo == hi) { - return lo; - } - - return lo + ma_lcg_rand_u32(pLCG) / (0xFFFFFFFF / (hi - lo + 1) + 1); -} - - - -static MA_INLINE void ma_seed(ma_int32 seed) -{ - ma_lcg_seed(&g_maLCG, seed); -} - -static MA_INLINE ma_int32 ma_rand_s32(void) -{ - return ma_lcg_rand_s32(&g_maLCG); -} - -static MA_INLINE ma_uint32 ma_rand_u32(void) -{ - return ma_lcg_rand_u32(&g_maLCG); -} - -static MA_INLINE double ma_rand_f64(void) -{ - return ma_lcg_rand_f64(&g_maLCG); -} - -static MA_INLINE float ma_rand_f32(void) -{ - return ma_lcg_rand_f32(&g_maLCG); -} - -static MA_INLINE float ma_rand_range_f32(float lo, float hi) -{ - return ma_lcg_rand_range_f32(&g_maLCG, lo, hi); -} - -static MA_INLINE ma_int32 ma_rand_range_s32(ma_int32 lo, ma_int32 hi) -{ - return ma_lcg_rand_range_s32(&g_maLCG, lo, hi); -} - - -static MA_INLINE float ma_dither_f32_rectangle(float ditherMin, float ditherMax) -{ - return ma_rand_range_f32(ditherMin, ditherMax); -} - -static MA_INLINE float ma_dither_f32_triangle(float ditherMin, float ditherMax) -{ - float a = ma_rand_range_f32(ditherMin, 0); - float b = ma_rand_range_f32(0, ditherMax); - return a + b; -} - -static MA_INLINE float ma_dither_f32(ma_dither_mode ditherMode, float ditherMin, float ditherMax) -{ - if (ditherMode == ma_dither_mode_rectangle) { - return ma_dither_f32_rectangle(ditherMin, ditherMax); - } - if (ditherMode == ma_dither_mode_triangle) { - return ma_dither_f32_triangle(ditherMin, ditherMax); - } - - return 0; -} - -static MA_INLINE ma_int32 ma_dither_s32(ma_dither_mode ditherMode, ma_int32 ditherMin, ma_int32 ditherMax) -{ - if (ditherMode == ma_dither_mode_rectangle) { - ma_int32 a = ma_rand_range_s32(ditherMin, ditherMax); - return a; - } - if (ditherMode == ma_dither_mode_triangle) { - ma_int32 a = ma_rand_range_s32(ditherMin, 0); - ma_int32 b = ma_rand_range_s32(0, ditherMax); - return a + b; - } - - return 0; -} - - -/************************************************************************************************************************************************************** - -Atomics - -**************************************************************************************************************************************************************/ -/* c89atomic.h begin */ -#ifndef ma_atomic_h -#if defined(__cplusplus) -extern "C" { -#endif -#if defined(__clang__) || (defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6))) - #pragma GCC diagnostic push - #pragma GCC diagnostic ignored "-Wlong-long" - #if defined(__clang__) - #pragma GCC diagnostic ignored "-Wc++11-long-long" - #endif -#endif -typedef int ma_atomic_memory_order; -#define MA_ATOMIC_HAS_8 -#define MA_ATOMIC_HAS_16 -#define MA_ATOMIC_HAS_32 -#define MA_ATOMIC_HAS_64 -#if (defined(_MSC_VER) ) || defined(__WATCOMC__) || defined(__DMC__) - #define MA_ATOMIC_MSVC_ARM_INTRINSIC(dst, src, order, intrin, ma_atomicType, msvcType) \ - ma_atomicType result; \ - switch (order) \ - { \ - case ma_atomic_memory_order_relaxed: \ - { \ - result = (ma_atomicType)intrin##_nf((volatile msvcType*)dst, (msvcType)src); \ - } break; \ - case ma_atomic_memory_order_consume: \ - case ma_atomic_memory_order_acquire: \ - { \ - result = (ma_atomicType)intrin##_acq((volatile msvcType*)dst, (msvcType)src); \ - } break; \ - case ma_atomic_memory_order_release: \ - { \ - result = (ma_atomicType)intrin##_rel((volatile msvcType*)dst, (msvcType)src); \ - } break; \ - case ma_atomic_memory_order_acq_rel: \ - case ma_atomic_memory_order_seq_cst: \ - default: \ - { \ - result = (ma_atomicType)intrin((volatile msvcType*)dst, (msvcType)src); \ - } break; \ - } \ - return result; - #define MA_ATOMIC_MSVC_ARM_INTRINSIC_COMPARE_EXCHANGE(ptr, expected, desired, order, intrin, ma_atomicType, msvcType) \ - ma_atomicType result; \ - switch (order) \ - { \ - case ma_atomic_memory_order_relaxed: \ - { \ - result = (ma_atomicType)intrin##_nf((volatile msvcType*)ptr, (msvcType)expected, (msvcType)desired); \ - } break; \ - case ma_atomic_memory_order_consume: \ - case ma_atomic_memory_order_acquire: \ - { \ - result = (ma_atomicType)intrin##_acq((volatile msvcType*)ptr, (msvcType)expected, (msvcType)desired); \ - } break; \ - case ma_atomic_memory_order_release: \ - { \ - result = (ma_atomicType)intrin##_rel((volatile msvcType*)ptr, (msvcType)expected, (msvcType)desired); \ - } break; \ - case ma_atomic_memory_order_acq_rel: \ - case ma_atomic_memory_order_seq_cst: \ - default: \ - { \ - result = (ma_atomicType)intrin((volatile msvcType*)ptr, (msvcType)expected, (msvcType)desired); \ - } break; \ - } \ - return result; - #define ma_atomic_memory_order_relaxed 0 - #define ma_atomic_memory_order_consume 1 - #define ma_atomic_memory_order_acquire 2 - #define ma_atomic_memory_order_release 3 - #define ma_atomic_memory_order_acq_rel 4 - #define ma_atomic_memory_order_seq_cst 5 - #if _MSC_VER < 1600 && defined(MA_X86) - #define MA_ATOMIC_MSVC_USE_INLINED_ASSEMBLY - #endif - #if _MSC_VER < 1600 - #undef MA_ATOMIC_HAS_8 - #undef MA_ATOMIC_HAS_16 - #endif - #if !defined(MA_ATOMIC_MSVC_USE_INLINED_ASSEMBLY) - #include - #endif - #if defined(MA_ATOMIC_MSVC_USE_INLINED_ASSEMBLY) - #if defined(MA_ATOMIC_HAS_8) - static MA_INLINE ma_uint8 __stdcall ma_atomic_compare_and_swap_8(volatile ma_uint8* dst, ma_uint8 expected, ma_uint8 desired) - { - ma_uint8 result = 0; - __asm { - mov ecx, dst - mov al, expected - mov dl, desired - lock cmpxchg [ecx], dl - mov result, al - } - return result; - } - #endif - #if defined(MA_ATOMIC_HAS_16) - static MA_INLINE ma_uint16 __stdcall ma_atomic_compare_and_swap_16(volatile ma_uint16* dst, ma_uint16 expected, ma_uint16 desired) - { - ma_uint16 result = 0; - __asm { - mov ecx, dst - mov ax, expected - mov dx, desired - lock cmpxchg [ecx], dx - mov result, ax - } - return result; - } - #endif - #if defined(MA_ATOMIC_HAS_32) - static MA_INLINE ma_uint32 __stdcall ma_atomic_compare_and_swap_32(volatile ma_uint32* dst, ma_uint32 expected, ma_uint32 desired) - { - ma_uint32 result = 0; - __asm { - mov ecx, dst - mov eax, expected - mov edx, desired - lock cmpxchg [ecx], edx - mov result, eax - } - return result; - } - #endif - #if defined(MA_ATOMIC_HAS_64) - static MA_INLINE ma_uint64 __stdcall ma_atomic_compare_and_swap_64(volatile ma_uint64* dst, ma_uint64 expected, ma_uint64 desired) - { - ma_uint32 resultEAX = 0; - ma_uint32 resultEDX = 0; - __asm { - mov esi, dst - mov eax, dword ptr expected - mov edx, dword ptr expected + 4 - mov ebx, dword ptr desired - mov ecx, dword ptr desired + 4 - lock cmpxchg8b qword ptr [esi] - mov resultEAX, eax - mov resultEDX, edx - } - return ((ma_uint64)resultEDX << 32) | resultEAX; - } - #endif - #else - #if defined(MA_ATOMIC_HAS_8) - #define ma_atomic_compare_and_swap_8( dst, expected, desired) (ma_uint8 )_InterlockedCompareExchange8((volatile char*)dst, (char)desired, (char)expected) - #endif - #if defined(MA_ATOMIC_HAS_16) - #define ma_atomic_compare_and_swap_16(dst, expected, desired) (ma_uint16)_InterlockedCompareExchange16((volatile short*)dst, (short)desired, (short)expected) - #endif - #if defined(MA_ATOMIC_HAS_32) - #define ma_atomic_compare_and_swap_32(dst, expected, desired) (ma_uint32)_InterlockedCompareExchange((volatile long*)dst, (long)desired, (long)expected) - #endif - #if defined(MA_ATOMIC_HAS_64) - #define ma_atomic_compare_and_swap_64(dst, expected, desired) (ma_uint64)_InterlockedCompareExchange64((volatile ma_int64*)dst, (ma_int64)desired, (ma_int64)expected) - #endif - #endif - #if defined(MA_ATOMIC_MSVC_USE_INLINED_ASSEMBLY) - #if defined(MA_ATOMIC_HAS_8) - static MA_INLINE ma_uint8 __stdcall ma_atomic_exchange_explicit_8(volatile ma_uint8* dst, ma_uint8 src, ma_atomic_memory_order order) - { - ma_uint8 result = 0; - (void)order; - __asm { - mov ecx, dst - mov al, src - lock xchg [ecx], al - mov result, al - } - return result; - } - #endif - #if defined(MA_ATOMIC_HAS_16) - static MA_INLINE ma_uint16 __stdcall ma_atomic_exchange_explicit_16(volatile ma_uint16* dst, ma_uint16 src, ma_atomic_memory_order order) - { - ma_uint16 result = 0; - (void)order; - __asm { - mov ecx, dst - mov ax, src - lock xchg [ecx], ax - mov result, ax - } - return result; - } - #endif - #if defined(MA_ATOMIC_HAS_32) - static MA_INLINE ma_uint32 __stdcall ma_atomic_exchange_explicit_32(volatile ma_uint32* dst, ma_uint32 src, ma_atomic_memory_order order) - { - ma_uint32 result = 0; - (void)order; - __asm { - mov ecx, dst - mov eax, src - lock xchg [ecx], eax - mov result, eax - } - return result; - } - #endif - #else - #if defined(MA_ATOMIC_HAS_8) - static MA_INLINE ma_uint8 __stdcall ma_atomic_exchange_explicit_8(volatile ma_uint8* dst, ma_uint8 src, ma_atomic_memory_order order) - { - #if defined(MA_ARM) - MA_ATOMIC_MSVC_ARM_INTRINSIC(dst, src, order, _InterlockedExchange8, ma_uint8, char); - #else - (void)order; - return (ma_uint8)_InterlockedExchange8((volatile char*)dst, (char)src); - #endif - } - #endif - #if defined(MA_ATOMIC_HAS_16) - static MA_INLINE ma_uint16 __stdcall ma_atomic_exchange_explicit_16(volatile ma_uint16* dst, ma_uint16 src, ma_atomic_memory_order order) - { - #if defined(MA_ARM) - MA_ATOMIC_MSVC_ARM_INTRINSIC(dst, src, order, _InterlockedExchange16, ma_uint16, short); - #else - (void)order; - return (ma_uint16)_InterlockedExchange16((volatile short*)dst, (short)src); - #endif - } - #endif - #if defined(MA_ATOMIC_HAS_32) - static MA_INLINE ma_uint32 __stdcall ma_atomic_exchange_explicit_32(volatile ma_uint32* dst, ma_uint32 src, ma_atomic_memory_order order) - { - #if defined(MA_ARM) - MA_ATOMIC_MSVC_ARM_INTRINSIC(dst, src, order, _InterlockedExchange, ma_uint32, long); - #else - (void)order; - return (ma_uint32)_InterlockedExchange((volatile long*)dst, (long)src); - #endif - } - #endif - #if defined(MA_ATOMIC_HAS_64) && defined(MA_64BIT) - static MA_INLINE ma_uint64 __stdcall ma_atomic_exchange_explicit_64(volatile ma_uint64* dst, ma_uint64 src, ma_atomic_memory_order order) - { - #if defined(MA_ARM) - MA_ATOMIC_MSVC_ARM_INTRINSIC(dst, src, order, _InterlockedExchange64, ma_uint64, long long); - #else - (void)order; - return (ma_uint64)_InterlockedExchange64((volatile long long*)dst, (long long)src); - #endif - } - #else - #endif - #endif - #if defined(MA_ATOMIC_HAS_64) && !defined(MA_64BIT) - static MA_INLINE ma_uint64 __stdcall ma_atomic_exchange_explicit_64(volatile ma_uint64* dst, ma_uint64 src, ma_atomic_memory_order order) - { - ma_uint64 oldValue; - do { - oldValue = *dst; - } while (ma_atomic_compare_and_swap_64(dst, oldValue, src) != oldValue); - (void)order; - return oldValue; - } - #endif - #if defined(MA_ATOMIC_MSVC_USE_INLINED_ASSEMBLY) - #if defined(MA_ATOMIC_HAS_8) - static MA_INLINE ma_uint8 __stdcall ma_atomic_fetch_add_explicit_8(volatile ma_uint8* dst, ma_uint8 src, ma_atomic_memory_order order) - { - ma_uint8 result = 0; - (void)order; - __asm { - mov ecx, dst - mov al, src - lock xadd [ecx], al - mov result, al - } - return result; - } - #endif - #if defined(MA_ATOMIC_HAS_16) - static MA_INLINE ma_uint16 __stdcall ma_atomic_fetch_add_explicit_16(volatile ma_uint16* dst, ma_uint16 src, ma_atomic_memory_order order) - { - ma_uint16 result = 0; - (void)order; - __asm { - mov ecx, dst - mov ax, src - lock xadd [ecx], ax - mov result, ax - } - return result; - } - #endif - #if defined(MA_ATOMIC_HAS_32) - static MA_INLINE ma_uint32 __stdcall ma_atomic_fetch_add_explicit_32(volatile ma_uint32* dst, ma_uint32 src, ma_atomic_memory_order order) - { - ma_uint32 result = 0; - (void)order; - __asm { - mov ecx, dst - mov eax, src - lock xadd [ecx], eax - mov result, eax - } - return result; - } - #endif - #else - #if defined(MA_ATOMIC_HAS_8) - static MA_INLINE ma_uint8 __stdcall ma_atomic_fetch_add_explicit_8(volatile ma_uint8* dst, ma_uint8 src, ma_atomic_memory_order order) - { - #if defined(MA_ARM) - MA_ATOMIC_MSVC_ARM_INTRINSIC(dst, src, order, _InterlockedExchangeAdd8, ma_uint8, char); - #else - (void)order; - return (ma_uint8)_InterlockedExchangeAdd8((volatile char*)dst, (char)src); - #endif - } - #endif - #if defined(MA_ATOMIC_HAS_16) - static MA_INLINE ma_uint16 __stdcall ma_atomic_fetch_add_explicit_16(volatile ma_uint16* dst, ma_uint16 src, ma_atomic_memory_order order) - { - #if defined(MA_ARM) - MA_ATOMIC_MSVC_ARM_INTRINSIC(dst, src, order, _InterlockedExchangeAdd16, ma_uint16, short); - #else - (void)order; - return (ma_uint16)_InterlockedExchangeAdd16((volatile short*)dst, (short)src); - #endif - } - #endif - #if defined(MA_ATOMIC_HAS_32) - static MA_INLINE ma_uint32 __stdcall ma_atomic_fetch_add_explicit_32(volatile ma_uint32* dst, ma_uint32 src, ma_atomic_memory_order order) - { - #if defined(MA_ARM) - MA_ATOMIC_MSVC_ARM_INTRINSIC(dst, src, order, _InterlockedExchangeAdd, ma_uint32, long); - #else - (void)order; - return (ma_uint32)_InterlockedExchangeAdd((volatile long*)dst, (long)src); - #endif - } - #endif - #if defined(MA_ATOMIC_HAS_64) && defined(MA_64BIT) - static MA_INLINE ma_uint64 __stdcall ma_atomic_fetch_add_explicit_64(volatile ma_uint64* dst, ma_uint64 src, ma_atomic_memory_order order) - { - #if defined(MA_ARM) - MA_ATOMIC_MSVC_ARM_INTRINSIC(dst, src, order, _InterlockedExchangeAdd64, ma_uint64, long long); - #else - (void)order; - return (ma_uint64)_InterlockedExchangeAdd64((volatile long long*)dst, (long long)src); - #endif - } - #else - #endif - #endif - #if defined(MA_ATOMIC_HAS_64) && !defined(MA_64BIT) - static MA_INLINE ma_uint64 __stdcall ma_atomic_fetch_add_explicit_64(volatile ma_uint64* dst, ma_uint64 src, ma_atomic_memory_order order) - { - ma_uint64 oldValue; - ma_uint64 newValue; - do { - oldValue = *dst; - newValue = oldValue + src; - } while (ma_atomic_compare_and_swap_64(dst, oldValue, newValue) != oldValue); - (void)order; - return oldValue; - } - #endif - #if defined(MA_ATOMIC_MSVC_USE_INLINED_ASSEMBLY) - static MA_INLINE void __stdcall ma_atomic_thread_fence(ma_atomic_memory_order order) - { - (void)order; - __asm { - lock add [esp], 0 - } - } - #else - #if defined(MA_X64) - #define ma_atomic_thread_fence(order) __faststorefence(), (void)order - #elif defined(MA_ARM64) - #define ma_atomic_thread_fence(order) __dmb(_ARM64_BARRIER_ISH), (void)order - #else - static MA_INLINE void ma_atomic_thread_fence(ma_atomic_memory_order order) - { - volatile ma_uint32 barrier = 0; - ma_atomic_fetch_add_explicit_32(&barrier, 0, order); - } - #endif - #endif - #define ma_atomic_compiler_fence() ma_atomic_thread_fence(ma_atomic_memory_order_seq_cst) - #define ma_atomic_signal_fence(order) ma_atomic_thread_fence(order) - #if defined(MA_ATOMIC_HAS_8) - static MA_INLINE ma_uint8 ma_atomic_load_explicit_8(volatile const ma_uint8* ptr, ma_atomic_memory_order order) - { - #if defined(MA_ARM) - MA_ATOMIC_MSVC_ARM_INTRINSIC_COMPARE_EXCHANGE(ptr, 0, 0, order, _InterlockedCompareExchange8, ma_uint8, char); - #else - (void)order; - return ma_atomic_compare_and_swap_8((volatile ma_uint8*)ptr, 0, 0); - #endif - } - #endif - #if defined(MA_ATOMIC_HAS_16) - static MA_INLINE ma_uint16 ma_atomic_load_explicit_16(volatile const ma_uint16* ptr, ma_atomic_memory_order order) - { - #if defined(MA_ARM) - MA_ATOMIC_MSVC_ARM_INTRINSIC_COMPARE_EXCHANGE(ptr, 0, 0, order, _InterlockedCompareExchange16, ma_uint16, short); - #else - (void)order; - return ma_atomic_compare_and_swap_16((volatile ma_uint16*)ptr, 0, 0); - #endif - } - #endif - #if defined(MA_ATOMIC_HAS_32) - static MA_INLINE ma_uint32 ma_atomic_load_explicit_32(volatile const ma_uint32* ptr, ma_atomic_memory_order order) - { - #if defined(MA_ARM) - MA_ATOMIC_MSVC_ARM_INTRINSIC_COMPARE_EXCHANGE(ptr, 0, 0, order, _InterlockedCompareExchange, ma_uint32, long); - #else - (void)order; - return ma_atomic_compare_and_swap_32((volatile ma_uint32*)ptr, 0, 0); - #endif - } - #endif - #if defined(MA_ATOMIC_HAS_64) - static MA_INLINE ma_uint64 ma_atomic_load_explicit_64(volatile const ma_uint64* ptr, ma_atomic_memory_order order) - { - #if defined(MA_ARM) - MA_ATOMIC_MSVC_ARM_INTRINSIC_COMPARE_EXCHANGE(ptr, 0, 0, order, _InterlockedCompareExchange64, ma_uint64, long long); - #else - (void)order; - return ma_atomic_compare_and_swap_64((volatile ma_uint64*)ptr, 0, 0); - #endif - } - #endif - #if defined(MA_ATOMIC_HAS_8) - #define ma_atomic_store_explicit_8( dst, src, order) (void)ma_atomic_exchange_explicit_8 (dst, src, order) - #endif - #if defined(MA_ATOMIC_HAS_16) - #define ma_atomic_store_explicit_16(dst, src, order) (void)ma_atomic_exchange_explicit_16(dst, src, order) - #endif - #if defined(MA_ATOMIC_HAS_32) - #define ma_atomic_store_explicit_32(dst, src, order) (void)ma_atomic_exchange_explicit_32(dst, src, order) - #endif - #if defined(MA_ATOMIC_HAS_64) - #define ma_atomic_store_explicit_64(dst, src, order) (void)ma_atomic_exchange_explicit_64(dst, src, order) - #endif - #if defined(MA_ATOMIC_HAS_8) - static MA_INLINE ma_uint8 __stdcall ma_atomic_fetch_sub_explicit_8(volatile ma_uint8* dst, ma_uint8 src, ma_atomic_memory_order order) - { - ma_uint8 oldValue; - ma_uint8 newValue; - do { - oldValue = *dst; - newValue = (ma_uint8)(oldValue - src); - } while (ma_atomic_compare_and_swap_8(dst, oldValue, newValue) != oldValue); - (void)order; - return oldValue; - } - #endif - #if defined(MA_ATOMIC_HAS_16) - static MA_INLINE ma_uint16 __stdcall ma_atomic_fetch_sub_explicit_16(volatile ma_uint16* dst, ma_uint16 src, ma_atomic_memory_order order) - { - ma_uint16 oldValue; - ma_uint16 newValue; - do { - oldValue = *dst; - newValue = (ma_uint16)(oldValue - src); - } while (ma_atomic_compare_and_swap_16(dst, oldValue, newValue) != oldValue); - (void)order; - return oldValue; - } - #endif - #if defined(MA_ATOMIC_HAS_32) - static MA_INLINE ma_uint32 __stdcall ma_atomic_fetch_sub_explicit_32(volatile ma_uint32* dst, ma_uint32 src, ma_atomic_memory_order order) - { - ma_uint32 oldValue; - ma_uint32 newValue; - do { - oldValue = *dst; - newValue = oldValue - src; - } while (ma_atomic_compare_and_swap_32(dst, oldValue, newValue) != oldValue); - (void)order; - return oldValue; - } - #endif - #if defined(MA_ATOMIC_HAS_64) - static MA_INLINE ma_uint64 __stdcall ma_atomic_fetch_sub_explicit_64(volatile ma_uint64* dst, ma_uint64 src, ma_atomic_memory_order order) - { - ma_uint64 oldValue; - ma_uint64 newValue; - do { - oldValue = *dst; - newValue = oldValue - src; - } while (ma_atomic_compare_and_swap_64(dst, oldValue, newValue) != oldValue); - (void)order; - return oldValue; - } - #endif - #if defined(MA_ATOMIC_HAS_8) - static MA_INLINE ma_uint8 __stdcall ma_atomic_fetch_and_explicit_8(volatile ma_uint8* dst, ma_uint8 src, ma_atomic_memory_order order) - { - #if defined(MA_ARM) - MA_ATOMIC_MSVC_ARM_INTRINSIC(dst, src, order, _InterlockedAnd8, ma_uint8, char); - #else - ma_uint8 oldValue; - ma_uint8 newValue; - do { - oldValue = *dst; - newValue = (ma_uint8)(oldValue & src); - } while (ma_atomic_compare_and_swap_8(dst, oldValue, newValue) != oldValue); - (void)order; - return oldValue; - #endif - } - #endif - #if defined(MA_ATOMIC_HAS_16) - static MA_INLINE ma_uint16 __stdcall ma_atomic_fetch_and_explicit_16(volatile ma_uint16* dst, ma_uint16 src, ma_atomic_memory_order order) - { - #if defined(MA_ARM) - MA_ATOMIC_MSVC_ARM_INTRINSIC(dst, src, order, _InterlockedAnd16, ma_uint16, short); - #else - ma_uint16 oldValue; - ma_uint16 newValue; - do { - oldValue = *dst; - newValue = (ma_uint16)(oldValue & src); - } while (ma_atomic_compare_and_swap_16(dst, oldValue, newValue) != oldValue); - (void)order; - return oldValue; - #endif - } - #endif - #if defined(MA_ATOMIC_HAS_32) - static MA_INLINE ma_uint32 __stdcall ma_atomic_fetch_and_explicit_32(volatile ma_uint32* dst, ma_uint32 src, ma_atomic_memory_order order) - { - #if defined(MA_ARM) - MA_ATOMIC_MSVC_ARM_INTRINSIC(dst, src, order, _InterlockedAnd, ma_uint32, long); - #else - ma_uint32 oldValue; - ma_uint32 newValue; - do { - oldValue = *dst; - newValue = oldValue & src; - } while (ma_atomic_compare_and_swap_32(dst, oldValue, newValue) != oldValue); - (void)order; - return oldValue; - #endif - } - #endif - #if defined(MA_ATOMIC_HAS_64) - static MA_INLINE ma_uint64 __stdcall ma_atomic_fetch_and_explicit_64(volatile ma_uint64* dst, ma_uint64 src, ma_atomic_memory_order order) - { - #if defined(MA_ARM) - MA_ATOMIC_MSVC_ARM_INTRINSIC(dst, src, order, _InterlockedAnd64, ma_uint64, long long); - #else - ma_uint64 oldValue; - ma_uint64 newValue; - do { - oldValue = *dst; - newValue = oldValue & src; - } while (ma_atomic_compare_and_swap_64(dst, oldValue, newValue) != oldValue); - (void)order; - return oldValue; - #endif - } - #endif - #if defined(MA_ATOMIC_HAS_8) - static MA_INLINE ma_uint8 __stdcall ma_atomic_fetch_xor_explicit_8(volatile ma_uint8* dst, ma_uint8 src, ma_atomic_memory_order order) - { - #if defined(MA_ARM) - MA_ATOMIC_MSVC_ARM_INTRINSIC(dst, src, order, _InterlockedXor8, ma_uint8, char); - #else - ma_uint8 oldValue; - ma_uint8 newValue; - do { - oldValue = *dst; - newValue = (ma_uint8)(oldValue ^ src); - } while (ma_atomic_compare_and_swap_8(dst, oldValue, newValue) != oldValue); - (void)order; - return oldValue; - #endif - } - #endif - #if defined(MA_ATOMIC_HAS_16) - static MA_INLINE ma_uint16 __stdcall ma_atomic_fetch_xor_explicit_16(volatile ma_uint16* dst, ma_uint16 src, ma_atomic_memory_order order) - { - #if defined(MA_ARM) - MA_ATOMIC_MSVC_ARM_INTRINSIC(dst, src, order, _InterlockedXor16, ma_uint16, short); - #else - ma_uint16 oldValue; - ma_uint16 newValue; - do { - oldValue = *dst; - newValue = (ma_uint16)(oldValue ^ src); - } while (ma_atomic_compare_and_swap_16(dst, oldValue, newValue) != oldValue); - (void)order; - return oldValue; - #endif - } - #endif - #if defined(MA_ATOMIC_HAS_32) - static MA_INLINE ma_uint32 __stdcall ma_atomic_fetch_xor_explicit_32(volatile ma_uint32* dst, ma_uint32 src, ma_atomic_memory_order order) - { - #if defined(MA_ARM) - MA_ATOMIC_MSVC_ARM_INTRINSIC(dst, src, order, _InterlockedXor, ma_uint32, long); - #else - ma_uint32 oldValue; - ma_uint32 newValue; - do { - oldValue = *dst; - newValue = oldValue ^ src; - } while (ma_atomic_compare_and_swap_32(dst, oldValue, newValue) != oldValue); - (void)order; - return oldValue; - #endif - } - #endif - #if defined(MA_ATOMIC_HAS_64) - static MA_INLINE ma_uint64 __stdcall ma_atomic_fetch_xor_explicit_64(volatile ma_uint64* dst, ma_uint64 src, ma_atomic_memory_order order) - { - #if defined(MA_ARM) - MA_ATOMIC_MSVC_ARM_INTRINSIC(dst, src, order, _InterlockedXor64, ma_uint64, long long); - #else - ma_uint64 oldValue; - ma_uint64 newValue; - do { - oldValue = *dst; - newValue = oldValue ^ src; - } while (ma_atomic_compare_and_swap_64(dst, oldValue, newValue) != oldValue); - (void)order; - return oldValue; - #endif - } - #endif - #if defined(MA_ATOMIC_HAS_8) - static MA_INLINE ma_uint8 __stdcall ma_atomic_fetch_or_explicit_8(volatile ma_uint8* dst, ma_uint8 src, ma_atomic_memory_order order) - { - #if defined(MA_ARM) - MA_ATOMIC_MSVC_ARM_INTRINSIC(dst, src, order, _InterlockedOr8, ma_uint8, char); - #else - ma_uint8 oldValue; - ma_uint8 newValue; - do { - oldValue = *dst; - newValue = (ma_uint8)(oldValue | src); - } while (ma_atomic_compare_and_swap_8(dst, oldValue, newValue) != oldValue); - (void)order; - return oldValue; - #endif - } - #endif - #if defined(MA_ATOMIC_HAS_16) - static MA_INLINE ma_uint16 __stdcall ma_atomic_fetch_or_explicit_16(volatile ma_uint16* dst, ma_uint16 src, ma_atomic_memory_order order) - { - #if defined(MA_ARM) - MA_ATOMIC_MSVC_ARM_INTRINSIC(dst, src, order, _InterlockedOr16, ma_uint16, short); - #else - ma_uint16 oldValue; - ma_uint16 newValue; - do { - oldValue = *dst; - newValue = (ma_uint16)(oldValue | src); - } while (ma_atomic_compare_and_swap_16(dst, oldValue, newValue) != oldValue); - (void)order; - return oldValue; - #endif - } - #endif - #if defined(MA_ATOMIC_HAS_32) - static MA_INLINE ma_uint32 __stdcall ma_atomic_fetch_or_explicit_32(volatile ma_uint32* dst, ma_uint32 src, ma_atomic_memory_order order) - { - #if defined(MA_ARM) - MA_ATOMIC_MSVC_ARM_INTRINSIC(dst, src, order, _InterlockedOr, ma_uint32, long); - #else - ma_uint32 oldValue; - ma_uint32 newValue; - do { - oldValue = *dst; - newValue = oldValue | src; - } while (ma_atomic_compare_and_swap_32(dst, oldValue, newValue) != oldValue); - (void)order; - return oldValue; - #endif - } - #endif - #if defined(MA_ATOMIC_HAS_64) - static MA_INLINE ma_uint64 __stdcall ma_atomic_fetch_or_explicit_64(volatile ma_uint64* dst, ma_uint64 src, ma_atomic_memory_order order) - { - #if defined(MA_ARM) - MA_ATOMIC_MSVC_ARM_INTRINSIC(dst, src, order, _InterlockedOr64, ma_uint64, long long); - #else - ma_uint64 oldValue; - ma_uint64 newValue; - do { - oldValue = *dst; - newValue = oldValue | src; - } while (ma_atomic_compare_and_swap_64(dst, oldValue, newValue) != oldValue); - (void)order; - return oldValue; - #endif - } - #endif - #if defined(MA_ATOMIC_HAS_8) - #define ma_atomic_test_and_set_explicit_8( dst, order) ma_atomic_exchange_explicit_8 (dst, 1, order) - #endif - #if defined(MA_ATOMIC_HAS_16) - #define ma_atomic_test_and_set_explicit_16(dst, order) ma_atomic_exchange_explicit_16(dst, 1, order) - #endif - #if defined(MA_ATOMIC_HAS_32) - #define ma_atomic_test_and_set_explicit_32(dst, order) ma_atomic_exchange_explicit_32(dst, 1, order) - #endif - #if defined(MA_ATOMIC_HAS_64) - #define ma_atomic_test_and_set_explicit_64(dst, order) ma_atomic_exchange_explicit_64(dst, 1, order) - #endif - #if defined(MA_ATOMIC_HAS_8) - #define ma_atomic_clear_explicit_8( dst, order) ma_atomic_store_explicit_8 (dst, 0, order) - #endif - #if defined(MA_ATOMIC_HAS_16) - #define ma_atomic_clear_explicit_16(dst, order) ma_atomic_store_explicit_16(dst, 0, order) - #endif - #if defined(MA_ATOMIC_HAS_32) - #define ma_atomic_clear_explicit_32(dst, order) ma_atomic_store_explicit_32(dst, 0, order) - #endif - #if defined(MA_ATOMIC_HAS_64) - #define ma_atomic_clear_explicit_64(dst, order) ma_atomic_store_explicit_64(dst, 0, order) - #endif - #if defined(MA_ATOMIC_HAS_8) - typedef ma_uint8 ma_atomic_flag; - #define ma_atomic_flag_test_and_set_explicit(ptr, order) (ma_bool32)ma_atomic_test_and_set_explicit_8(ptr, order) - #define ma_atomic_flag_clear_explicit(ptr, order) ma_atomic_clear_explicit_8(ptr, order) - #define ma_atomic_flag_load_explicit(ptr, order) ma_atomic_load_explicit_8(ptr, order) - #else - typedef ma_uint32 ma_atomic_flag; - #define ma_atomic_flag_test_and_set_explicit(ptr, order) (ma_bool32)ma_atomic_test_and_set_explicit_32(ptr, order) - #define ma_atomic_flag_clear_explicit(ptr, order) ma_atomic_clear_explicit_32(ptr, order) - #define ma_atomic_flag_load_explicit(ptr, order) ma_atomic_load_explicit_32(ptr, order) - #endif -#elif defined(__clang__) || (defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 7))) - #define MA_ATOMIC_HAS_NATIVE_COMPARE_EXCHANGE - #define MA_ATOMIC_HAS_NATIVE_IS_LOCK_FREE - #define ma_atomic_memory_order_relaxed __ATOMIC_RELAXED - #define ma_atomic_memory_order_consume __ATOMIC_CONSUME - #define ma_atomic_memory_order_acquire __ATOMIC_ACQUIRE - #define ma_atomic_memory_order_release __ATOMIC_RELEASE - #define ma_atomic_memory_order_acq_rel __ATOMIC_ACQ_REL - #define ma_atomic_memory_order_seq_cst __ATOMIC_SEQ_CST - #define ma_atomic_compiler_fence() __asm__ __volatile__("":::"memory") - #define ma_atomic_thread_fence(order) __atomic_thread_fence(order) - #define ma_atomic_signal_fence(order) __atomic_signal_fence(order) - #define ma_atomic_is_lock_free_8(ptr) __atomic_is_lock_free(1, ptr) - #define ma_atomic_is_lock_free_16(ptr) __atomic_is_lock_free(2, ptr) - #define ma_atomic_is_lock_free_32(ptr) __atomic_is_lock_free(4, ptr) - #define ma_atomic_is_lock_free_64(ptr) __atomic_is_lock_free(8, ptr) - #define ma_atomic_test_and_set_explicit_8( dst, order) __atomic_exchange_n(dst, 1, order) - #define ma_atomic_test_and_set_explicit_16(dst, order) __atomic_exchange_n(dst, 1, order) - #define ma_atomic_test_and_set_explicit_32(dst, order) __atomic_exchange_n(dst, 1, order) - #define ma_atomic_test_and_set_explicit_64(dst, order) __atomic_exchange_n(dst, 1, order) - #define ma_atomic_clear_explicit_8( dst, order) __atomic_store_n(dst, 0, order) - #define ma_atomic_clear_explicit_16(dst, order) __atomic_store_n(dst, 0, order) - #define ma_atomic_clear_explicit_32(dst, order) __atomic_store_n(dst, 0, order) - #define ma_atomic_clear_explicit_64(dst, order) __atomic_store_n(dst, 0, order) - #define ma_atomic_store_explicit_8( dst, src, order) __atomic_store_n(dst, src, order) - #define ma_atomic_store_explicit_16(dst, src, order) __atomic_store_n(dst, src, order) - #define ma_atomic_store_explicit_32(dst, src, order) __atomic_store_n(dst, src, order) - #define ma_atomic_store_explicit_64(dst, src, order) __atomic_store_n(dst, src, order) - #define ma_atomic_load_explicit_8( dst, order) __atomic_load_n(dst, order) - #define ma_atomic_load_explicit_16(dst, order) __atomic_load_n(dst, order) - #define ma_atomic_load_explicit_32(dst, order) __atomic_load_n(dst, order) - #define ma_atomic_load_explicit_64(dst, order) __atomic_load_n(dst, order) - #define ma_atomic_exchange_explicit_8( dst, src, order) __atomic_exchange_n(dst, src, order) - #define ma_atomic_exchange_explicit_16(dst, src, order) __atomic_exchange_n(dst, src, order) - #define ma_atomic_exchange_explicit_32(dst, src, order) __atomic_exchange_n(dst, src, order) - #define ma_atomic_exchange_explicit_64(dst, src, order) __atomic_exchange_n(dst, src, order) - #define ma_atomic_compare_exchange_strong_explicit_8( dst, expected, desired, successOrder, failureOrder) __atomic_compare_exchange_n(dst, expected, desired, 0, successOrder, failureOrder) - #define ma_atomic_compare_exchange_strong_explicit_16(dst, expected, desired, successOrder, failureOrder) __atomic_compare_exchange_n(dst, expected, desired, 0, successOrder, failureOrder) - #define ma_atomic_compare_exchange_strong_explicit_32(dst, expected, desired, successOrder, failureOrder) __atomic_compare_exchange_n(dst, expected, desired, 0, successOrder, failureOrder) - #define ma_atomic_compare_exchange_strong_explicit_64(dst, expected, desired, successOrder, failureOrder) __atomic_compare_exchange_n(dst, expected, desired, 0, successOrder, failureOrder) - #define ma_atomic_compare_exchange_weak_explicit_8( dst, expected, desired, successOrder, failureOrder) __atomic_compare_exchange_n(dst, expected, desired, 1, successOrder, failureOrder) - #define ma_atomic_compare_exchange_weak_explicit_16(dst, expected, desired, successOrder, failureOrder) __atomic_compare_exchange_n(dst, expected, desired, 1, successOrder, failureOrder) - #define ma_atomic_compare_exchange_weak_explicit_32(dst, expected, desired, successOrder, failureOrder) __atomic_compare_exchange_n(dst, expected, desired, 1, successOrder, failureOrder) - #define ma_atomic_compare_exchange_weak_explicit_64(dst, expected, desired, successOrder, failureOrder) __atomic_compare_exchange_n(dst, expected, desired, 1, successOrder, failureOrder) - #define ma_atomic_fetch_add_explicit_8( dst, src, order) __atomic_fetch_add(dst, src, order) - #define ma_atomic_fetch_add_explicit_16(dst, src, order) __atomic_fetch_add(dst, src, order) - #define ma_atomic_fetch_add_explicit_32(dst, src, order) __atomic_fetch_add(dst, src, order) - #define ma_atomic_fetch_add_explicit_64(dst, src, order) __atomic_fetch_add(dst, src, order) - #define ma_atomic_fetch_sub_explicit_8( dst, src, order) __atomic_fetch_sub(dst, src, order) - #define ma_atomic_fetch_sub_explicit_16(dst, src, order) __atomic_fetch_sub(dst, src, order) - #define ma_atomic_fetch_sub_explicit_32(dst, src, order) __atomic_fetch_sub(dst, src, order) - #define ma_atomic_fetch_sub_explicit_64(dst, src, order) __atomic_fetch_sub(dst, src, order) - #define ma_atomic_fetch_or_explicit_8( dst, src, order) __atomic_fetch_or(dst, src, order) - #define ma_atomic_fetch_or_explicit_16(dst, src, order) __atomic_fetch_or(dst, src, order) - #define ma_atomic_fetch_or_explicit_32(dst, src, order) __atomic_fetch_or(dst, src, order) - #define ma_atomic_fetch_or_explicit_64(dst, src, order) __atomic_fetch_or(dst, src, order) - #define ma_atomic_fetch_xor_explicit_8( dst, src, order) __atomic_fetch_xor(dst, src, order) - #define ma_atomic_fetch_xor_explicit_16(dst, src, order) __atomic_fetch_xor(dst, src, order) - #define ma_atomic_fetch_xor_explicit_32(dst, src, order) __atomic_fetch_xor(dst, src, order) - #define ma_atomic_fetch_xor_explicit_64(dst, src, order) __atomic_fetch_xor(dst, src, order) - #define ma_atomic_fetch_and_explicit_8( dst, src, order) __atomic_fetch_and(dst, src, order) - #define ma_atomic_fetch_and_explicit_16(dst, src, order) __atomic_fetch_and(dst, src, order) - #define ma_atomic_fetch_and_explicit_32(dst, src, order) __atomic_fetch_and(dst, src, order) - #define ma_atomic_fetch_and_explicit_64(dst, src, order) __atomic_fetch_and(dst, src, order) - static MA_INLINE ma_uint8 ma_atomic_compare_and_swap_8(volatile ma_uint8* dst, ma_uint8 expected, ma_uint8 desired) - { - __atomic_compare_exchange_n(dst, &expected, desired, 0, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST); - return expected; - } - static MA_INLINE ma_uint16 ma_atomic_compare_and_swap_16(volatile ma_uint16* dst, ma_uint16 expected, ma_uint16 desired) - { - __atomic_compare_exchange_n(dst, &expected, desired, 0, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST); - return expected; - } - static MA_INLINE ma_uint32 ma_atomic_compare_and_swap_32(volatile ma_uint32* dst, ma_uint32 expected, ma_uint32 desired) - { - __atomic_compare_exchange_n(dst, &expected, desired, 0, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST); - return expected; - } - #if defined(__clang__) - #pragma clang diagnostic push - #if __clang_major__ >= 8 - #pragma clang diagnostic ignored "-Watomic-alignment" - #endif - #endif - static MA_INLINE ma_uint64 ma_atomic_compare_and_swap_64(volatile ma_uint64* dst, ma_uint64 expected, ma_uint64 desired) - { - __atomic_compare_exchange_n(dst, &expected, desired, 0, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST); - return expected; - } - #if defined(__clang__) - #pragma clang diagnostic pop - #endif - typedef ma_uint8 ma_atomic_flag; - #define ma_atomic_flag_test_and_set_explicit(dst, order) (ma_bool32)__atomic_test_and_set(dst, order) - #define ma_atomic_flag_clear_explicit(dst, order) __atomic_clear(dst, order) - #define ma_atomic_flag_load_explicit(ptr, order) ma_atomic_load_explicit_8(ptr, order) -#else - #define ma_atomic_memory_order_relaxed 1 - #define ma_atomic_memory_order_consume 2 - #define ma_atomic_memory_order_acquire 3 - #define ma_atomic_memory_order_release 4 - #define ma_atomic_memory_order_acq_rel 5 - #define ma_atomic_memory_order_seq_cst 6 - #define ma_atomic_compiler_fence() __asm__ __volatile__("":::"memory") - #if defined(__GNUC__) - #define ma_atomic_thread_fence(order) __sync_synchronize(), (void)order - static MA_INLINE ma_uint8 ma_atomic_exchange_explicit_8(volatile ma_uint8* dst, ma_uint8 src, ma_atomic_memory_order order) - { - if (order > ma_atomic_memory_order_acquire) { - __sync_synchronize(); - } - return __sync_lock_test_and_set(dst, src); - } - static MA_INLINE ma_uint16 ma_atomic_exchange_explicit_16(volatile ma_uint16* dst, ma_uint16 src, ma_atomic_memory_order order) - { - ma_uint16 oldValue; - do { - oldValue = *dst; - } while (__sync_val_compare_and_swap(dst, oldValue, src) != oldValue); - (void)order; - return oldValue; - } - static MA_INLINE ma_uint32 ma_atomic_exchange_explicit_32(volatile ma_uint32* dst, ma_uint32 src, ma_atomic_memory_order order) - { - ma_uint32 oldValue; - do { - oldValue = *dst; - } while (__sync_val_compare_and_swap(dst, oldValue, src) != oldValue); - (void)order; - return oldValue; - } - static MA_INLINE ma_uint64 ma_atomic_exchange_explicit_64(volatile ma_uint64* dst, ma_uint64 src, ma_atomic_memory_order order) - { - ma_uint64 oldValue; - do { - oldValue = *dst; - } while (__sync_val_compare_and_swap(dst, oldValue, src) != oldValue); - (void)order; - return oldValue; - } - static MA_INLINE ma_uint8 ma_atomic_fetch_add_explicit_8(volatile ma_uint8* dst, ma_uint8 src, ma_atomic_memory_order order) - { - (void)order; - return __sync_fetch_and_add(dst, src); - } - static MA_INLINE ma_uint16 ma_atomic_fetch_add_explicit_16(volatile ma_uint16* dst, ma_uint16 src, ma_atomic_memory_order order) - { - (void)order; - return __sync_fetch_and_add(dst, src); - } - static MA_INLINE ma_uint32 ma_atomic_fetch_add_explicit_32(volatile ma_uint32* dst, ma_uint32 src, ma_atomic_memory_order order) - { - (void)order; - return __sync_fetch_and_add(dst, src); - } - static MA_INLINE ma_uint64 ma_atomic_fetch_add_explicit_64(volatile ma_uint64* dst, ma_uint64 src, ma_atomic_memory_order order) - { - (void)order; - return __sync_fetch_and_add(dst, src); - } - static MA_INLINE ma_uint8 ma_atomic_fetch_sub_explicit_8(volatile ma_uint8* dst, ma_uint8 src, ma_atomic_memory_order order) - { - (void)order; - return __sync_fetch_and_sub(dst, src); - } - static MA_INLINE ma_uint16 ma_atomic_fetch_sub_explicit_16(volatile ma_uint16* dst, ma_uint16 src, ma_atomic_memory_order order) - { - (void)order; - return __sync_fetch_and_sub(dst, src); - } - static MA_INLINE ma_uint32 ma_atomic_fetch_sub_explicit_32(volatile ma_uint32* dst, ma_uint32 src, ma_atomic_memory_order order) - { - (void)order; - return __sync_fetch_and_sub(dst, src); - } - static MA_INLINE ma_uint64 ma_atomic_fetch_sub_explicit_64(volatile ma_uint64* dst, ma_uint64 src, ma_atomic_memory_order order) - { - (void)order; - return __sync_fetch_and_sub(dst, src); - } - static MA_INLINE ma_uint8 ma_atomic_fetch_or_explicit_8(volatile ma_uint8* dst, ma_uint8 src, ma_atomic_memory_order order) - { - (void)order; - return __sync_fetch_and_or(dst, src); - } - static MA_INLINE ma_uint16 ma_atomic_fetch_or_explicit_16(volatile ma_uint16* dst, ma_uint16 src, ma_atomic_memory_order order) - { - (void)order; - return __sync_fetch_and_or(dst, src); - } - static MA_INLINE ma_uint32 ma_atomic_fetch_or_explicit_32(volatile ma_uint32* dst, ma_uint32 src, ma_atomic_memory_order order) - { - (void)order; - return __sync_fetch_and_or(dst, src); - } - static MA_INLINE ma_uint64 ma_atomic_fetch_or_explicit_64(volatile ma_uint64* dst, ma_uint64 src, ma_atomic_memory_order order) - { - (void)order; - return __sync_fetch_and_or(dst, src); - } - static MA_INLINE ma_uint8 ma_atomic_fetch_xor_explicit_8(volatile ma_uint8* dst, ma_uint8 src, ma_atomic_memory_order order) - { - (void)order; - return __sync_fetch_and_xor(dst, src); - } - static MA_INLINE ma_uint16 ma_atomic_fetch_xor_explicit_16(volatile ma_uint16* dst, ma_uint16 src, ma_atomic_memory_order order) - { - (void)order; - return __sync_fetch_and_xor(dst, src); - } - static MA_INLINE ma_uint32 ma_atomic_fetch_xor_explicit_32(volatile ma_uint32* dst, ma_uint32 src, ma_atomic_memory_order order) - { - (void)order; - return __sync_fetch_and_xor(dst, src); - } - static MA_INLINE ma_uint64 ma_atomic_fetch_xor_explicit_64(volatile ma_uint64* dst, ma_uint64 src, ma_atomic_memory_order order) - { - (void)order; - return __sync_fetch_and_xor(dst, src); - } - static MA_INLINE ma_uint8 ma_atomic_fetch_and_explicit_8(volatile ma_uint8* dst, ma_uint8 src, ma_atomic_memory_order order) - { - (void)order; - return __sync_fetch_and_and(dst, src); - } - static MA_INLINE ma_uint16 ma_atomic_fetch_and_explicit_16(volatile ma_uint16* dst, ma_uint16 src, ma_atomic_memory_order order) - { - (void)order; - return __sync_fetch_and_and(dst, src); - } - static MA_INLINE ma_uint32 ma_atomic_fetch_and_explicit_32(volatile ma_uint32* dst, ma_uint32 src, ma_atomic_memory_order order) - { - (void)order; - return __sync_fetch_and_and(dst, src); - } - static MA_INLINE ma_uint64 ma_atomic_fetch_and_explicit_64(volatile ma_uint64* dst, ma_uint64 src, ma_atomic_memory_order order) - { - (void)order; - return __sync_fetch_and_and(dst, src); - } - #define ma_atomic_compare_and_swap_8( dst, expected, desired) __sync_val_compare_and_swap(dst, expected, desired) - #define ma_atomic_compare_and_swap_16(dst, expected, desired) __sync_val_compare_and_swap(dst, expected, desired) - #define ma_atomic_compare_and_swap_32(dst, expected, desired) __sync_val_compare_and_swap(dst, expected, desired) - #define ma_atomic_compare_and_swap_64(dst, expected, desired) __sync_val_compare_and_swap(dst, expected, desired) - #else - #if defined(MA_X86) - #define ma_atomic_thread_fence(order) __asm__ __volatile__("lock; addl $0, (%%esp)" ::: "memory", "cc") - #elif defined(MA_X64) - #define ma_atomic_thread_fence(order) __asm__ __volatile__("lock; addq $0, (%%rsp)" ::: "memory", "cc") - #else - #error Unsupported architecture. Please submit a feature request. - #endif - static MA_INLINE ma_uint8 ma_atomic_compare_and_swap_8(volatile ma_uint8* dst, ma_uint8 expected, ma_uint8 desired) - { - ma_uint8 result; - #if defined(MA_X86) || defined(MA_X64) - __asm__ __volatile__("lock; cmpxchg %3, %0" : "+m"(*dst), "=a"(result) : "a"(expected), "d"(desired) : "cc"); - #else - #error Unsupported architecture. Please submit a feature request. - #endif - return result; - } - static MA_INLINE ma_uint16 ma_atomic_compare_and_swap_16(volatile ma_uint16* dst, ma_uint16 expected, ma_uint16 desired) - { - ma_uint16 result; - #if defined(MA_X86) || defined(MA_X64) - __asm__ __volatile__("lock; cmpxchg %3, %0" : "+m"(*dst), "=a"(result) : "a"(expected), "d"(desired) : "cc"); - #else - #error Unsupported architecture. Please submit a feature request. - #endif - return result; - } - static MA_INLINE ma_uint32 ma_atomic_compare_and_swap_32(volatile ma_uint32* dst, ma_uint32 expected, ma_uint32 desired) - { - ma_uint32 result; - #if defined(MA_X86) || defined(MA_X64) - __asm__ __volatile__("lock; cmpxchg %3, %0" : "+m"(*dst), "=a"(result) : "a"(expected), "d"(desired) : "cc"); - #else - #error Unsupported architecture. Please submit a feature request. - #endif - return result; - } - static MA_INLINE ma_uint64 ma_atomic_compare_and_swap_64(volatile ma_uint64* dst, ma_uint64 expected, ma_uint64 desired) - { - volatile ma_uint64 result; - #if defined(MA_X86) - ma_uint32 resultEAX; - ma_uint32 resultEDX; - __asm__ __volatile__("push %%ebx; xchg %5, %%ebx; lock; cmpxchg8b %0; pop %%ebx" : "+m"(*dst), "=a"(resultEAX), "=d"(resultEDX) : "a"(expected & 0xFFFFFFFF), "d"(expected >> 32), "r"(desired & 0xFFFFFFFF), "c"(desired >> 32) : "cc"); - result = ((ma_uint64)resultEDX << 32) | resultEAX; - #elif defined(MA_X64) - __asm__ __volatile__("lock; cmpxchg %3, %0" : "+m"(*dst), "=a"(result) : "a"(expected), "d"(desired) : "cc"); - #else - #error Unsupported architecture. Please submit a feature request. - #endif - return result; - } - static MA_INLINE ma_uint8 ma_atomic_exchange_explicit_8(volatile ma_uint8* dst, ma_uint8 src, ma_atomic_memory_order order) - { - ma_uint8 result = 0; - (void)order; - #if defined(MA_X86) || defined(MA_X64) - __asm__ __volatile__("lock; xchg %1, %0" : "+m"(*dst), "=a"(result) : "a"(src)); - #else - #error Unsupported architecture. Please submit a feature request. - #endif - return result; - } - static MA_INLINE ma_uint16 ma_atomic_exchange_explicit_16(volatile ma_uint16* dst, ma_uint16 src, ma_atomic_memory_order order) - { - ma_uint16 result = 0; - (void)order; - #if defined(MA_X86) || defined(MA_X64) - __asm__ __volatile__("lock; xchg %1, %0" : "+m"(*dst), "=a"(result) : "a"(src)); - #else - #error Unsupported architecture. Please submit a feature request. - #endif - return result; - } - static MA_INLINE ma_uint32 ma_atomic_exchange_explicit_32(volatile ma_uint32* dst, ma_uint32 src, ma_atomic_memory_order order) - { - ma_uint32 result; - (void)order; - #if defined(MA_X86) || defined(MA_X64) - __asm__ __volatile__("lock; xchg %1, %0" : "+m"(*dst), "=a"(result) : "a"(src)); - #else - #error Unsupported architecture. Please submit a feature request. - #endif - return result; - } - static MA_INLINE ma_uint64 ma_atomic_exchange_explicit_64(volatile ma_uint64* dst, ma_uint64 src, ma_atomic_memory_order order) - { - ma_uint64 result; - (void)order; - #if defined(MA_X86) - do { - result = *dst; - } while (ma_atomic_compare_and_swap_64(dst, result, src) != result); - #elif defined(MA_X64) - __asm__ __volatile__("lock; xchg %1, %0" : "+m"(*dst), "=a"(result) : "a"(src)); - #else - #error Unsupported architecture. Please submit a feature request. - #endif - return result; - } - static MA_INLINE ma_uint8 ma_atomic_fetch_add_explicit_8(volatile ma_uint8* dst, ma_uint8 src, ma_atomic_memory_order order) - { - ma_uint8 result; - (void)order; - #if defined(MA_X86) || defined(MA_X64) - __asm__ __volatile__("lock; xadd %1, %0" : "+m"(*dst), "=a"(result) : "a"(src) : "cc"); - #else - #error Unsupported architecture. Please submit a feature request. - #endif - return result; - } - static MA_INLINE ma_uint16 ma_atomic_fetch_add_explicit_16(volatile ma_uint16* dst, ma_uint16 src, ma_atomic_memory_order order) - { - ma_uint16 result; - (void)order; - #if defined(MA_X86) || defined(MA_X64) - __asm__ __volatile__("lock; xadd %1, %0" : "+m"(*dst), "=a"(result) : "a"(src) : "cc"); - #else - #error Unsupported architecture. Please submit a feature request. - #endif - return result; - } - static MA_INLINE ma_uint32 ma_atomic_fetch_add_explicit_32(volatile ma_uint32* dst, ma_uint32 src, ma_atomic_memory_order order) - { - ma_uint32 result; - (void)order; - #if defined(MA_X86) || defined(MA_X64) - __asm__ __volatile__("lock; xadd %1, %0" : "+m"(*dst), "=a"(result) : "a"(src) : "cc"); - #else - #error Unsupported architecture. Please submit a feature request. - #endif - return result; - } - static MA_INLINE ma_uint64 ma_atomic_fetch_add_explicit_64(volatile ma_uint64* dst, ma_uint64 src, ma_atomic_memory_order order) - { - #if defined(MA_X86) - ma_uint64 oldValue; - ma_uint64 newValue; - (void)order; - do { - oldValue = *dst; - newValue = oldValue + src; - } while (ma_atomic_compare_and_swap_64(dst, oldValue, newValue) != oldValue); - return oldValue; - #elif defined(MA_X64) - ma_uint64 result; - (void)order; - __asm__ __volatile__("lock; xadd %1, %0" : "+m"(*dst), "=a"(result) : "a"(src) : "cc"); - return result; - #endif - } - static MA_INLINE ma_uint8 ma_atomic_fetch_sub_explicit_8(volatile ma_uint8* dst, ma_uint8 src, ma_atomic_memory_order order) - { - ma_uint8 oldValue; - ma_uint8 newValue; - do { - oldValue = *dst; - newValue = (ma_uint8)(oldValue - src); - } while (ma_atomic_compare_and_swap_8(dst, oldValue, newValue) != oldValue); - (void)order; - return oldValue; - } - static MA_INLINE ma_uint16 ma_atomic_fetch_sub_explicit_16(volatile ma_uint16* dst, ma_uint16 src, ma_atomic_memory_order order) - { - ma_uint16 oldValue; - ma_uint16 newValue; - do { - oldValue = *dst; - newValue = (ma_uint16)(oldValue - src); - } while (ma_atomic_compare_and_swap_16(dst, oldValue, newValue) != oldValue); - (void)order; - return oldValue; - } - static MA_INLINE ma_uint32 ma_atomic_fetch_sub_explicit_32(volatile ma_uint32* dst, ma_uint32 src, ma_atomic_memory_order order) - { - ma_uint32 oldValue; - ma_uint32 newValue; - do { - oldValue = *dst; - newValue = oldValue - src; - } while (ma_atomic_compare_and_swap_32(dst, oldValue, newValue) != oldValue); - (void)order; - return oldValue; - } - static MA_INLINE ma_uint64 ma_atomic_fetch_sub_explicit_64(volatile ma_uint64* dst, ma_uint64 src, ma_atomic_memory_order order) - { - ma_uint64 oldValue; - ma_uint64 newValue; - do { - oldValue = *dst; - newValue = oldValue - src; - } while (ma_atomic_compare_and_swap_64(dst, oldValue, newValue) != oldValue); - (void)order; - return oldValue; - } - static MA_INLINE ma_uint8 ma_atomic_fetch_and_explicit_8(volatile ma_uint8* dst, ma_uint8 src, ma_atomic_memory_order order) - { - ma_uint8 oldValue; - ma_uint8 newValue; - do { - oldValue = *dst; - newValue = (ma_uint8)(oldValue & src); - } while (ma_atomic_compare_and_swap_8(dst, oldValue, newValue) != oldValue); - (void)order; - return oldValue; - } - static MA_INLINE ma_uint16 ma_atomic_fetch_and_explicit_16(volatile ma_uint16* dst, ma_uint16 src, ma_atomic_memory_order order) - { - ma_uint16 oldValue; - ma_uint16 newValue; - do { - oldValue = *dst; - newValue = (ma_uint16)(oldValue & src); - } while (ma_atomic_compare_and_swap_16(dst, oldValue, newValue) != oldValue); - (void)order; - return oldValue; - } - static MA_INLINE ma_uint32 ma_atomic_fetch_and_explicit_32(volatile ma_uint32* dst, ma_uint32 src, ma_atomic_memory_order order) - { - ma_uint32 oldValue; - ma_uint32 newValue; - do { - oldValue = *dst; - newValue = oldValue & src; - } while (ma_atomic_compare_and_swap_32(dst, oldValue, newValue) != oldValue); - (void)order; - return oldValue; - } - static MA_INLINE ma_uint64 ma_atomic_fetch_and_explicit_64(volatile ma_uint64* dst, ma_uint64 src, ma_atomic_memory_order order) - { - ma_uint64 oldValue; - ma_uint64 newValue; - do { - oldValue = *dst; - newValue = oldValue & src; - } while (ma_atomic_compare_and_swap_64(dst, oldValue, newValue) != oldValue); - (void)order; - return oldValue; - } - static MA_INLINE ma_uint8 ma_atomic_fetch_xor_explicit_8(volatile ma_uint8* dst, ma_uint8 src, ma_atomic_memory_order order) - { - ma_uint8 oldValue; - ma_uint8 newValue; - do { - oldValue = *dst; - newValue = (ma_uint8)(oldValue ^ src); - } while (ma_atomic_compare_and_swap_8(dst, oldValue, newValue) != oldValue); - (void)order; - return oldValue; - } - static MA_INLINE ma_uint16 ma_atomic_fetch_xor_explicit_16(volatile ma_uint16* dst, ma_uint16 src, ma_atomic_memory_order order) - { - ma_uint16 oldValue; - ma_uint16 newValue; - do { - oldValue = *dst; - newValue = (ma_uint16)(oldValue ^ src); - } while (ma_atomic_compare_and_swap_16(dst, oldValue, newValue) != oldValue); - (void)order; - return oldValue; - } - static MA_INLINE ma_uint32 ma_atomic_fetch_xor_explicit_32(volatile ma_uint32* dst, ma_uint32 src, ma_atomic_memory_order order) - { - ma_uint32 oldValue; - ma_uint32 newValue; - do { - oldValue = *dst; - newValue = oldValue ^ src; - } while (ma_atomic_compare_and_swap_32(dst, oldValue, newValue) != oldValue); - (void)order; - return oldValue; - } - static MA_INLINE ma_uint64 ma_atomic_fetch_xor_explicit_64(volatile ma_uint64* dst, ma_uint64 src, ma_atomic_memory_order order) - { - ma_uint64 oldValue; - ma_uint64 newValue; - do { - oldValue = *dst; - newValue = oldValue ^ src; - } while (ma_atomic_compare_and_swap_64(dst, oldValue, newValue) != oldValue); - (void)order; - return oldValue; - } - static MA_INLINE ma_uint8 ma_atomic_fetch_or_explicit_8(volatile ma_uint8* dst, ma_uint8 src, ma_atomic_memory_order order) - { - ma_uint8 oldValue; - ma_uint8 newValue; - do { - oldValue = *dst; - newValue = (ma_uint8)(oldValue | src); - } while (ma_atomic_compare_and_swap_8(dst, oldValue, newValue) != oldValue); - (void)order; - return oldValue; - } - static MA_INLINE ma_uint16 ma_atomic_fetch_or_explicit_16(volatile ma_uint16* dst, ma_uint16 src, ma_atomic_memory_order order) - { - ma_uint16 oldValue; - ma_uint16 newValue; - do { - oldValue = *dst; - newValue = (ma_uint16)(oldValue | src); - } while (ma_atomic_compare_and_swap_16(dst, oldValue, newValue) != oldValue); - (void)order; - return oldValue; - } - static MA_INLINE ma_uint32 ma_atomic_fetch_or_explicit_32(volatile ma_uint32* dst, ma_uint32 src, ma_atomic_memory_order order) - { - ma_uint32 oldValue; - ma_uint32 newValue; - do { - oldValue = *dst; - newValue = oldValue | src; - } while (ma_atomic_compare_and_swap_32(dst, oldValue, newValue) != oldValue); - (void)order; - return oldValue; - } - static MA_INLINE ma_uint64 ma_atomic_fetch_or_explicit_64(volatile ma_uint64* dst, ma_uint64 src, ma_atomic_memory_order order) - { - ma_uint64 oldValue; - ma_uint64 newValue; - do { - oldValue = *dst; - newValue = oldValue | src; - } while (ma_atomic_compare_and_swap_64(dst, oldValue, newValue) != oldValue); - (void)order; - return oldValue; - } - #endif - #define ma_atomic_signal_fence(order) ma_atomic_thread_fence(order) - static MA_INLINE ma_uint8 ma_atomic_load_explicit_8(volatile const ma_uint8* ptr, ma_atomic_memory_order order) - { - (void)order; - return ma_atomic_compare_and_swap_8((ma_uint8*)ptr, 0, 0); - } - static MA_INLINE ma_uint16 ma_atomic_load_explicit_16(volatile const ma_uint16* ptr, ma_atomic_memory_order order) - { - (void)order; - return ma_atomic_compare_and_swap_16((ma_uint16*)ptr, 0, 0); - } - static MA_INLINE ma_uint32 ma_atomic_load_explicit_32(volatile const ma_uint32* ptr, ma_atomic_memory_order order) - { - (void)order; - return ma_atomic_compare_and_swap_32((ma_uint32*)ptr, 0, 0); - } - static MA_INLINE ma_uint64 ma_atomic_load_explicit_64(volatile const ma_uint64* ptr, ma_atomic_memory_order order) - { - (void)order; - return ma_atomic_compare_and_swap_64((ma_uint64*)ptr, 0, 0); - } - #define ma_atomic_store_explicit_8( dst, src, order) (void)ma_atomic_exchange_explicit_8 (dst, src, order) - #define ma_atomic_store_explicit_16(dst, src, order) (void)ma_atomic_exchange_explicit_16(dst, src, order) - #define ma_atomic_store_explicit_32(dst, src, order) (void)ma_atomic_exchange_explicit_32(dst, src, order) - #define ma_atomic_store_explicit_64(dst, src, order) (void)ma_atomic_exchange_explicit_64(dst, src, order) - #define ma_atomic_test_and_set_explicit_8( dst, order) ma_atomic_exchange_explicit_8 (dst, 1, order) - #define ma_atomic_test_and_set_explicit_16(dst, order) ma_atomic_exchange_explicit_16(dst, 1, order) - #define ma_atomic_test_and_set_explicit_32(dst, order) ma_atomic_exchange_explicit_32(dst, 1, order) - #define ma_atomic_test_and_set_explicit_64(dst, order) ma_atomic_exchange_explicit_64(dst, 1, order) - #define ma_atomic_clear_explicit_8( dst, order) ma_atomic_store_explicit_8 (dst, 0, order) - #define ma_atomic_clear_explicit_16(dst, order) ma_atomic_store_explicit_16(dst, 0, order) - #define ma_atomic_clear_explicit_32(dst, order) ma_atomic_store_explicit_32(dst, 0, order) - #define ma_atomic_clear_explicit_64(dst, order) ma_atomic_store_explicit_64(dst, 0, order) - typedef ma_uint8 ma_atomic_flag; - #define ma_atomic_flag_test_and_set_explicit(ptr, order) (ma_bool32)ma_atomic_test_and_set_explicit_8(ptr, order) - #define ma_atomic_flag_clear_explicit(ptr, order) ma_atomic_clear_explicit_8(ptr, order) - #define ma_atomic_flag_load_explicit(ptr, order) ma_atomic_load_explicit_8(ptr, order) -#endif -#if !defined(MA_ATOMIC_HAS_NATIVE_COMPARE_EXCHANGE) - #if defined(MA_ATOMIC_HAS_8) - static MA_INLINE ma_bool32 ma_atomic_compare_exchange_strong_explicit_8(volatile ma_uint8* dst, ma_uint8* expected, ma_uint8 desired, ma_atomic_memory_order successOrder, ma_atomic_memory_order failureOrder) - { - ma_uint8 expectedValue; - ma_uint8 result; - (void)successOrder; - (void)failureOrder; - expectedValue = ma_atomic_load_explicit_8(expected, ma_atomic_memory_order_seq_cst); - result = ma_atomic_compare_and_swap_8(dst, expectedValue, desired); - if (result == expectedValue) { - return 1; - } else { - ma_atomic_store_explicit_8(expected, result, failureOrder); - return 0; - } - } - #endif - #if defined(MA_ATOMIC_HAS_16) - static MA_INLINE ma_bool32 ma_atomic_compare_exchange_strong_explicit_16(volatile ma_uint16* dst, ma_uint16* expected, ma_uint16 desired, ma_atomic_memory_order successOrder, ma_atomic_memory_order failureOrder) - { - ma_uint16 expectedValue; - ma_uint16 result; - (void)successOrder; - (void)failureOrder; - expectedValue = ma_atomic_load_explicit_16(expected, ma_atomic_memory_order_seq_cst); - result = ma_atomic_compare_and_swap_16(dst, expectedValue, desired); - if (result == expectedValue) { - return 1; - } else { - ma_atomic_store_explicit_16(expected, result, failureOrder); - return 0; - } - } - #endif - #if defined(MA_ATOMIC_HAS_32) - static MA_INLINE ma_bool32 ma_atomic_compare_exchange_strong_explicit_32(volatile ma_uint32* dst, ma_uint32* expected, ma_uint32 desired, ma_atomic_memory_order successOrder, ma_atomic_memory_order failureOrder) - { - ma_uint32 expectedValue; - ma_uint32 result; - (void)successOrder; - (void)failureOrder; - expectedValue = ma_atomic_load_explicit_32(expected, ma_atomic_memory_order_seq_cst); - result = ma_atomic_compare_and_swap_32(dst, expectedValue, desired); - if (result == expectedValue) { - return 1; - } else { - ma_atomic_store_explicit_32(expected, result, failureOrder); - return 0; - } - } - #endif - #if defined(MA_ATOMIC_HAS_64) - static MA_INLINE ma_bool32 ma_atomic_compare_exchange_strong_explicit_64(volatile ma_uint64* dst, volatile ma_uint64* expected, ma_uint64 desired, ma_atomic_memory_order successOrder, ma_atomic_memory_order failureOrder) - { - ma_uint64 expectedValue; - ma_uint64 result; - (void)successOrder; - (void)failureOrder; - expectedValue = ma_atomic_load_explicit_64(expected, ma_atomic_memory_order_seq_cst); - result = ma_atomic_compare_and_swap_64(dst, expectedValue, desired); - if (result == expectedValue) { - return 1; - } else { - ma_atomic_store_explicit_64(expected, result, failureOrder); - return 0; - } - } - #endif - #define ma_atomic_compare_exchange_weak_explicit_8( dst, expected, desired, successOrder, failureOrder) ma_atomic_compare_exchange_strong_explicit_8 (dst, expected, desired, successOrder, failureOrder) - #define ma_atomic_compare_exchange_weak_explicit_16(dst, expected, desired, successOrder, failureOrder) ma_atomic_compare_exchange_strong_explicit_16(dst, expected, desired, successOrder, failureOrder) - #define ma_atomic_compare_exchange_weak_explicit_32(dst, expected, desired, successOrder, failureOrder) ma_atomic_compare_exchange_strong_explicit_32(dst, expected, desired, successOrder, failureOrder) - #define ma_atomic_compare_exchange_weak_explicit_64(dst, expected, desired, successOrder, failureOrder) ma_atomic_compare_exchange_strong_explicit_64(dst, expected, desired, successOrder, failureOrder) -#endif -#if !defined(MA_ATOMIC_HAS_NATIVE_IS_LOCK_FREE) - static MA_INLINE ma_bool32 ma_atomic_is_lock_free_8(volatile void* ptr) - { - (void)ptr; - return 1; - } - static MA_INLINE ma_bool32 ma_atomic_is_lock_free_16(volatile void* ptr) - { - (void)ptr; - return 1; - } - static MA_INLINE ma_bool32 ma_atomic_is_lock_free_32(volatile void* ptr) - { - (void)ptr; - return 1; - } - static MA_INLINE ma_bool32 ma_atomic_is_lock_free_64(volatile void* ptr) - { - (void)ptr; - #if defined(MA_64BIT) - return 1; - #else - #if defined(MA_X86) || defined(MA_X64) - return 1; - #else - return 0; - #endif - #endif - } -#endif -#if defined(MA_64BIT) - static MA_INLINE ma_bool32 ma_atomic_is_lock_free_ptr(volatile void** ptr) - { - return ma_atomic_is_lock_free_64((volatile ma_uint64*)ptr); - } - static MA_INLINE void* ma_atomic_load_explicit_ptr(volatile void** ptr, ma_atomic_memory_order order) - { - return (void*)ma_atomic_load_explicit_64((volatile ma_uint64*)ptr, order); - } - static MA_INLINE void ma_atomic_store_explicit_ptr(volatile void** dst, void* src, ma_atomic_memory_order order) - { - ma_atomic_store_explicit_64((volatile ma_uint64*)dst, (ma_uint64)src, order); - } - static MA_INLINE void* ma_atomic_exchange_explicit_ptr(volatile void** dst, void* src, ma_atomic_memory_order order) - { - return (void*)ma_atomic_exchange_explicit_64((volatile ma_uint64*)dst, (ma_uint64)src, order); - } - static MA_INLINE ma_bool32 ma_atomic_compare_exchange_strong_explicit_ptr(volatile void** dst, void** expected, void* desired, ma_atomic_memory_order successOrder, ma_atomic_memory_order failureOrder) - { - return ma_atomic_compare_exchange_strong_explicit_64((volatile ma_uint64*)dst, (ma_uint64*)expected, (ma_uint64)desired, successOrder, failureOrder); - } - static MA_INLINE ma_bool32 ma_atomic_compare_exchange_weak_explicit_ptr(volatile void** dst, void** expected, void* desired, ma_atomic_memory_order successOrder, ma_atomic_memory_order failureOrder) - { - return ma_atomic_compare_exchange_weak_explicit_64((volatile ma_uint64*)dst, (ma_uint64*)expected, (ma_uint64)desired, successOrder, failureOrder); - } - static MA_INLINE void* ma_atomic_compare_and_swap_ptr(volatile void** dst, void* expected, void* desired) - { - return (void*)ma_atomic_compare_and_swap_64((volatile ma_uint64*)dst, (ma_uint64)expected, (ma_uint64)desired); - } -#elif defined(MA_32BIT) - static MA_INLINE ma_bool32 ma_atomic_is_lock_free_ptr(volatile void** ptr) - { - return ma_atomic_is_lock_free_32((volatile ma_uint32*)ptr); - } - static MA_INLINE void* ma_atomic_load_explicit_ptr(volatile void** ptr, ma_atomic_memory_order order) - { - return (void*)ma_atomic_load_explicit_32((volatile ma_uint32*)ptr, order); - } - static MA_INLINE void ma_atomic_store_explicit_ptr(volatile void** dst, void* src, ma_atomic_memory_order order) - { - ma_atomic_store_explicit_32((volatile ma_uint32*)dst, (ma_uint32)src, order); - } - static MA_INLINE void* ma_atomic_exchange_explicit_ptr(volatile void** dst, void* src, ma_atomic_memory_order order) - { - return (void*)ma_atomic_exchange_explicit_32((volatile ma_uint32*)dst, (ma_uint32)src, order); - } - static MA_INLINE ma_bool32 ma_atomic_compare_exchange_strong_explicit_ptr(volatile void** dst, void** expected, void* desired, ma_atomic_memory_order successOrder, ma_atomic_memory_order failureOrder) - { - return ma_atomic_compare_exchange_strong_explicit_32((volatile ma_uint32*)dst, (ma_uint32*)expected, (ma_uint32)desired, successOrder, failureOrder); - } - static MA_INLINE ma_bool32 ma_atomic_compare_exchange_weak_explicit_ptr(volatile void** dst, void** expected, void* desired, ma_atomic_memory_order successOrder, ma_atomic_memory_order failureOrder) - { - return ma_atomic_compare_exchange_weak_explicit_32((volatile ma_uint32*)dst, (ma_uint32*)expected, (ma_uint32)desired, successOrder, failureOrder); - } - static MA_INLINE void* ma_atomic_compare_and_swap_ptr(volatile void** dst, void* expected, void* desired) - { - return (void*)ma_atomic_compare_and_swap_32((volatile ma_uint32*)dst, (ma_uint32)expected, (ma_uint32)desired); - } -#else - #error Unsupported architecture. -#endif -#define ma_atomic_flag_test_and_set(ptr) ma_atomic_flag_test_and_set_explicit(ptr, ma_atomic_memory_order_seq_cst) -#define ma_atomic_flag_clear(ptr) ma_atomic_flag_clear_explicit(ptr, ma_atomic_memory_order_seq_cst) -#define ma_atomic_store_ptr(dst, src) ma_atomic_store_explicit_ptr((volatile void**)dst, (void*)src, ma_atomic_memory_order_seq_cst) -#define ma_atomic_load_ptr(ptr) ma_atomic_load_explicit_ptr((volatile void**)ptr, ma_atomic_memory_order_seq_cst) -#define ma_atomic_exchange_ptr(dst, src) ma_atomic_exchange_explicit_ptr((volatile void**)dst, (void*)src, ma_atomic_memory_order_seq_cst) -#define ma_atomic_compare_exchange_strong_ptr(dst, expected, desired) ma_atomic_compare_exchange_strong_explicit_ptr((volatile void**)dst, (void**)expected, (void*)desired, ma_atomic_memory_order_seq_cst, ma_atomic_memory_order_seq_cst) -#define ma_atomic_compare_exchange_weak_ptr(dst, expected, desired) ma_atomic_compare_exchange_weak_explicit_ptr((volatile void**)dst, (void**)expected, (void*)desired, ma_atomic_memory_order_seq_cst, ma_atomic_memory_order_seq_cst) -#define ma_atomic_test_and_set_8( ptr) ma_atomic_test_and_set_explicit_8( ptr, ma_atomic_memory_order_seq_cst) -#define ma_atomic_test_and_set_16(ptr) ma_atomic_test_and_set_explicit_16(ptr, ma_atomic_memory_order_seq_cst) -#define ma_atomic_test_and_set_32(ptr) ma_atomic_test_and_set_explicit_32(ptr, ma_atomic_memory_order_seq_cst) -#define ma_atomic_test_and_set_64(ptr) ma_atomic_test_and_set_explicit_64(ptr, ma_atomic_memory_order_seq_cst) -#define ma_atomic_clear_8( ptr) ma_atomic_clear_explicit_8( ptr, ma_atomic_memory_order_seq_cst) -#define ma_atomic_clear_16(ptr) ma_atomic_clear_explicit_16(ptr, ma_atomic_memory_order_seq_cst) -#define ma_atomic_clear_32(ptr) ma_atomic_clear_explicit_32(ptr, ma_atomic_memory_order_seq_cst) -#define ma_atomic_clear_64(ptr) ma_atomic_clear_explicit_64(ptr, ma_atomic_memory_order_seq_cst) -#define ma_atomic_store_8( dst, src) ma_atomic_store_explicit_8( dst, src, ma_atomic_memory_order_seq_cst) -#define ma_atomic_store_16(dst, src) ma_atomic_store_explicit_16(dst, src, ma_atomic_memory_order_seq_cst) -#define ma_atomic_store_32(dst, src) ma_atomic_store_explicit_32(dst, src, ma_atomic_memory_order_seq_cst) -#define ma_atomic_store_64(dst, src) ma_atomic_store_explicit_64(dst, src, ma_atomic_memory_order_seq_cst) -#define ma_atomic_load_8( ptr) ma_atomic_load_explicit_8( ptr, ma_atomic_memory_order_seq_cst) -#define ma_atomic_load_16(ptr) ma_atomic_load_explicit_16(ptr, ma_atomic_memory_order_seq_cst) -#define ma_atomic_load_32(ptr) ma_atomic_load_explicit_32(ptr, ma_atomic_memory_order_seq_cst) -#define ma_atomic_load_64(ptr) ma_atomic_load_explicit_64(ptr, ma_atomic_memory_order_seq_cst) -#define ma_atomic_exchange_8( dst, src) ma_atomic_exchange_explicit_8( dst, src, ma_atomic_memory_order_seq_cst) -#define ma_atomic_exchange_16(dst, src) ma_atomic_exchange_explicit_16(dst, src, ma_atomic_memory_order_seq_cst) -#define ma_atomic_exchange_32(dst, src) ma_atomic_exchange_explicit_32(dst, src, ma_atomic_memory_order_seq_cst) -#define ma_atomic_exchange_64(dst, src) ma_atomic_exchange_explicit_64(dst, src, ma_atomic_memory_order_seq_cst) -#define ma_atomic_compare_exchange_strong_8( dst, expected, desired) ma_atomic_compare_exchange_strong_explicit_8( dst, expected, desired, ma_atomic_memory_order_seq_cst, ma_atomic_memory_order_seq_cst) -#define ma_atomic_compare_exchange_strong_16(dst, expected, desired) ma_atomic_compare_exchange_strong_explicit_16(dst, expected, desired, ma_atomic_memory_order_seq_cst, ma_atomic_memory_order_seq_cst) -#define ma_atomic_compare_exchange_strong_32(dst, expected, desired) ma_atomic_compare_exchange_strong_explicit_32(dst, expected, desired, ma_atomic_memory_order_seq_cst, ma_atomic_memory_order_seq_cst) -#define ma_atomic_compare_exchange_strong_64(dst, expected, desired) ma_atomic_compare_exchange_strong_explicit_64(dst, expected, desired, ma_atomic_memory_order_seq_cst, ma_atomic_memory_order_seq_cst) -#define ma_atomic_compare_exchange_weak_8( dst, expected, desired) ma_atomic_compare_exchange_weak_explicit_8( dst, expected, desired, ma_atomic_memory_order_seq_cst, ma_atomic_memory_order_seq_cst) -#define ma_atomic_compare_exchange_weak_16( dst, expected, desired) ma_atomic_compare_exchange_weak_explicit_16(dst, expected, desired, ma_atomic_memory_order_seq_cst, ma_atomic_memory_order_seq_cst) -#define ma_atomic_compare_exchange_weak_32( dst, expected, desired) ma_atomic_compare_exchange_weak_explicit_32(dst, expected, desired, ma_atomic_memory_order_seq_cst, ma_atomic_memory_order_seq_cst) -#define ma_atomic_compare_exchange_weak_64( dst, expected, desired) ma_atomic_compare_exchange_weak_explicit_64(dst, expected, desired, ma_atomic_memory_order_seq_cst, ma_atomic_memory_order_seq_cst) -#define ma_atomic_fetch_add_8( dst, src) ma_atomic_fetch_add_explicit_8( dst, src, ma_atomic_memory_order_seq_cst) -#define ma_atomic_fetch_add_16(dst, src) ma_atomic_fetch_add_explicit_16(dst, src, ma_atomic_memory_order_seq_cst) -#define ma_atomic_fetch_add_32(dst, src) ma_atomic_fetch_add_explicit_32(dst, src, ma_atomic_memory_order_seq_cst) -#define ma_atomic_fetch_add_64(dst, src) ma_atomic_fetch_add_explicit_64(dst, src, ma_atomic_memory_order_seq_cst) -#define ma_atomic_fetch_sub_8( dst, src) ma_atomic_fetch_sub_explicit_8( dst, src, ma_atomic_memory_order_seq_cst) -#define ma_atomic_fetch_sub_16(dst, src) ma_atomic_fetch_sub_explicit_16(dst, src, ma_atomic_memory_order_seq_cst) -#define ma_atomic_fetch_sub_32(dst, src) ma_atomic_fetch_sub_explicit_32(dst, src, ma_atomic_memory_order_seq_cst) -#define ma_atomic_fetch_sub_64(dst, src) ma_atomic_fetch_sub_explicit_64(dst, src, ma_atomic_memory_order_seq_cst) -#define ma_atomic_fetch_or_8( dst, src) ma_atomic_fetch_or_explicit_8( dst, src, ma_atomic_memory_order_seq_cst) -#define ma_atomic_fetch_or_16(dst, src) ma_atomic_fetch_or_explicit_16(dst, src, ma_atomic_memory_order_seq_cst) -#define ma_atomic_fetch_or_32(dst, src) ma_atomic_fetch_or_explicit_32(dst, src, ma_atomic_memory_order_seq_cst) -#define ma_atomic_fetch_or_64(dst, src) ma_atomic_fetch_or_explicit_64(dst, src, ma_atomic_memory_order_seq_cst) -#define ma_atomic_fetch_xor_8( dst, src) ma_atomic_fetch_xor_explicit_8( dst, src, ma_atomic_memory_order_seq_cst) -#define ma_atomic_fetch_xor_16(dst, src) ma_atomic_fetch_xor_explicit_16(dst, src, ma_atomic_memory_order_seq_cst) -#define ma_atomic_fetch_xor_32(dst, src) ma_atomic_fetch_xor_explicit_32(dst, src, ma_atomic_memory_order_seq_cst) -#define ma_atomic_fetch_xor_64(dst, src) ma_atomic_fetch_xor_explicit_64(dst, src, ma_atomic_memory_order_seq_cst) -#define ma_atomic_fetch_and_8( dst, src) ma_atomic_fetch_and_explicit_8 (dst, src, ma_atomic_memory_order_seq_cst) -#define ma_atomic_fetch_and_16(dst, src) ma_atomic_fetch_and_explicit_16(dst, src, ma_atomic_memory_order_seq_cst) -#define ma_atomic_fetch_and_32(dst, src) ma_atomic_fetch_and_explicit_32(dst, src, ma_atomic_memory_order_seq_cst) -#define ma_atomic_fetch_and_64(dst, src) ma_atomic_fetch_and_explicit_64(dst, src, ma_atomic_memory_order_seq_cst) -#define ma_atomic_test_and_set_explicit_i8( ptr, order) (ma_int8 )ma_atomic_test_and_set_explicit_8( (ma_uint8* )ptr, order) -#define ma_atomic_test_and_set_explicit_i16(ptr, order) (ma_int16)ma_atomic_test_and_set_explicit_16((ma_uint16*)ptr, order) -#define ma_atomic_test_and_set_explicit_i32(ptr, order) (ma_int32)ma_atomic_test_and_set_explicit_32((ma_uint32*)ptr, order) -#define ma_atomic_test_and_set_explicit_i64(ptr, order) (ma_int64)ma_atomic_test_and_set_explicit_64((ma_uint64*)ptr, order) -#define ma_atomic_clear_explicit_i8( ptr, order) ma_atomic_clear_explicit_8( (ma_uint8* )ptr, order) -#define ma_atomic_clear_explicit_i16(ptr, order) ma_atomic_clear_explicit_16((ma_uint16*)ptr, order) -#define ma_atomic_clear_explicit_i32(ptr, order) ma_atomic_clear_explicit_32((ma_uint32*)ptr, order) -#define ma_atomic_clear_explicit_i64(ptr, order) ma_atomic_clear_explicit_64((ma_uint64*)ptr, order) -#define ma_atomic_store_explicit_i8( dst, src, order) ma_atomic_store_explicit_8( (ma_uint8* )dst, (ma_uint8 )src, order) -#define ma_atomic_store_explicit_i16(dst, src, order) ma_atomic_store_explicit_16((ma_uint16*)dst, (ma_uint16)src, order) -#define ma_atomic_store_explicit_i32(dst, src, order) ma_atomic_store_explicit_32((ma_uint32*)dst, (ma_uint32)src, order) -#define ma_atomic_store_explicit_i64(dst, src, order) ma_atomic_store_explicit_64((ma_uint64*)dst, (ma_uint64)src, order) -#define ma_atomic_load_explicit_i8( ptr, order) (ma_int8 )ma_atomic_load_explicit_8( (ma_uint8* )ptr, order) -#define ma_atomic_load_explicit_i16(ptr, order) (ma_int16)ma_atomic_load_explicit_16((ma_uint16*)ptr, order) -#define ma_atomic_load_explicit_i32(ptr, order) (ma_int32)ma_atomic_load_explicit_32((ma_uint32*)ptr, order) -#define ma_atomic_load_explicit_i64(ptr, order) (ma_int64)ma_atomic_load_explicit_64((ma_uint64*)ptr, order) -#define ma_atomic_exchange_explicit_i8( dst, src, order) (ma_int8 )ma_atomic_exchange_explicit_8 ((ma_uint8* )dst, (ma_uint8 )src, order) -#define ma_atomic_exchange_explicit_i16(dst, src, order) (ma_int16)ma_atomic_exchange_explicit_16((ma_uint16*)dst, (ma_uint16)src, order) -#define ma_atomic_exchange_explicit_i32(dst, src, order) (ma_int32)ma_atomic_exchange_explicit_32((ma_uint32*)dst, (ma_uint32)src, order) -#define ma_atomic_exchange_explicit_i64(dst, src, order) (ma_int64)ma_atomic_exchange_explicit_64((ma_uint64*)dst, (ma_uint64)src, order) -#define ma_atomic_compare_exchange_strong_explicit_i8( dst, expected, desired, successOrder, failureOrder) ma_atomic_compare_exchange_strong_explicit_8( (ma_uint8* )dst, (ma_uint8* )expected, (ma_uint8 )desired, successOrder, failureOrder) -#define ma_atomic_compare_exchange_strong_explicit_i16(dst, expected, desired, successOrder, failureOrder) ma_atomic_compare_exchange_strong_explicit_16((ma_uint16*)dst, (ma_uint16*)expected, (ma_uint16)desired, successOrder, failureOrder) -#define ma_atomic_compare_exchange_strong_explicit_i32(dst, expected, desired, successOrder, failureOrder) ma_atomic_compare_exchange_strong_explicit_32((ma_uint32*)dst, (ma_uint32*)expected, (ma_uint32)desired, successOrder, failureOrder) -#define ma_atomic_compare_exchange_strong_explicit_i64(dst, expected, desired, successOrder, failureOrder) ma_atomic_compare_exchange_strong_explicit_64((ma_uint64*)dst, (ma_uint64*)expected, (ma_uint64)desired, successOrder, failureOrder) -#define ma_atomic_compare_exchange_weak_explicit_i8( dst, expected, desired, successOrder, failureOrder) ma_atomic_compare_exchange_weak_explicit_8( (ma_uint8* )dst, (ma_uint8* )expected, (ma_uint8 )desired, successOrder, failureOrder) -#define ma_atomic_compare_exchange_weak_explicit_i16(dst, expected, desired, successOrder, failureOrder) ma_atomic_compare_exchange_weak_explicit_16((ma_uint16*)dst, (ma_uint16*)expected, (ma_uint16)desired, successOrder, failureOrder) -#define ma_atomic_compare_exchange_weak_explicit_i32(dst, expected, desired, successOrder, failureOrder) ma_atomic_compare_exchange_weak_explicit_32((ma_uint32*)dst, (ma_uint32*)expected, (ma_uint32)desired, successOrder, failureOrder) -#define ma_atomic_compare_exchange_weak_explicit_i64(dst, expected, desired, successOrder, failureOrder) ma_atomic_compare_exchange_weak_explicit_64((ma_uint64*)dst, (ma_uint64*)expected, (ma_uint64)desired, successOrder, failureOrder) -#define ma_atomic_fetch_add_explicit_i8( dst, src, order) (ma_int8 )ma_atomic_fetch_add_explicit_8( (ma_uint8* )dst, (ma_uint8 )src, order) -#define ma_atomic_fetch_add_explicit_i16(dst, src, order) (ma_int16)ma_atomic_fetch_add_explicit_16((ma_uint16*)dst, (ma_uint16)src, order) -#define ma_atomic_fetch_add_explicit_i32(dst, src, order) (ma_int32)ma_atomic_fetch_add_explicit_32((ma_uint32*)dst, (ma_uint32)src, order) -#define ma_atomic_fetch_add_explicit_i64(dst, src, order) (ma_int64)ma_atomic_fetch_add_explicit_64((ma_uint64*)dst, (ma_uint64)src, order) -#define ma_atomic_fetch_sub_explicit_i8( dst, src, order) (ma_int8 )ma_atomic_fetch_sub_explicit_8( (ma_uint8* )dst, (ma_uint8 )src, order) -#define ma_atomic_fetch_sub_explicit_i16(dst, src, order) (ma_int16)ma_atomic_fetch_sub_explicit_16((ma_uint16*)dst, (ma_uint16)src, order) -#define ma_atomic_fetch_sub_explicit_i32(dst, src, order) (ma_int32)ma_atomic_fetch_sub_explicit_32((ma_uint32*)dst, (ma_uint32)src, order) -#define ma_atomic_fetch_sub_explicit_i64(dst, src, order) (ma_int64)ma_atomic_fetch_sub_explicit_64((ma_uint64*)dst, (ma_uint64)src, order) -#define ma_atomic_fetch_or_explicit_i8( dst, src, order) (ma_int8 )ma_atomic_fetch_or_explicit_8( (ma_uint8* )dst, (ma_uint8 )src, order) -#define ma_atomic_fetch_or_explicit_i16(dst, src, order) (ma_int16)ma_atomic_fetch_or_explicit_16((ma_uint16*)dst, (ma_uint16)src, order) -#define ma_atomic_fetch_or_explicit_i32(dst, src, order) (ma_int32)ma_atomic_fetch_or_explicit_32((ma_uint32*)dst, (ma_uint32)src, order) -#define ma_atomic_fetch_or_explicit_i64(dst, src, order) (ma_int64)ma_atomic_fetch_or_explicit_64((ma_uint64*)dst, (ma_uint64)src, order) -#define ma_atomic_fetch_xor_explicit_i8( dst, src, order) (ma_int8 )ma_atomic_fetch_xor_explicit_8( (ma_uint8* )dst, (ma_uint8 )src, order) -#define ma_atomic_fetch_xor_explicit_i16(dst, src, order) (ma_int16)ma_atomic_fetch_xor_explicit_16((ma_uint16*)dst, (ma_uint16)src, order) -#define ma_atomic_fetch_xor_explicit_i32(dst, src, order) (ma_int32)ma_atomic_fetch_xor_explicit_32((ma_uint32*)dst, (ma_uint32)src, order) -#define ma_atomic_fetch_xor_explicit_i64(dst, src, order) (ma_int64)ma_atomic_fetch_xor_explicit_64((ma_uint64*)dst, (ma_uint64)src, order) -#define ma_atomic_fetch_and_explicit_i8( dst, src, order) (ma_int8 )ma_atomic_fetch_and_explicit_8( (ma_uint8* )dst, (ma_uint8 )src, order) -#define ma_atomic_fetch_and_explicit_i16(dst, src, order) (ma_int16)ma_atomic_fetch_and_explicit_16((ma_uint16*)dst, (ma_uint16)src, order) -#define ma_atomic_fetch_and_explicit_i32(dst, src, order) (ma_int32)ma_atomic_fetch_and_explicit_32((ma_uint32*)dst, (ma_uint32)src, order) -#define ma_atomic_fetch_and_explicit_i64(dst, src, order) (ma_int64)ma_atomic_fetch_and_explicit_64((ma_uint64*)dst, (ma_uint64)src, order) -#define ma_atomic_test_and_set_i8( ptr) ma_atomic_test_and_set_explicit_i8( ptr, ma_atomic_memory_order_seq_cst) -#define ma_atomic_test_and_set_i16(ptr) ma_atomic_test_and_set_explicit_i16(ptr, ma_atomic_memory_order_seq_cst) -#define ma_atomic_test_and_set_i32(ptr) ma_atomic_test_and_set_explicit_i32(ptr, ma_atomic_memory_order_seq_cst) -#define ma_atomic_test_and_set_i64(ptr) ma_atomic_test_and_set_explicit_i64(ptr, ma_atomic_memory_order_seq_cst) -#define ma_atomic_clear_i8( ptr) ma_atomic_clear_explicit_i8( ptr, ma_atomic_memory_order_seq_cst) -#define ma_atomic_clear_i16(ptr) ma_atomic_clear_explicit_i16(ptr, ma_atomic_memory_order_seq_cst) -#define ma_atomic_clear_i32(ptr) ma_atomic_clear_explicit_i32(ptr, ma_atomic_memory_order_seq_cst) -#define ma_atomic_clear_i64(ptr) ma_atomic_clear_explicit_i64(ptr, ma_atomic_memory_order_seq_cst) -#define ma_atomic_store_i8( dst, src) ma_atomic_store_explicit_i8( dst, src, ma_atomic_memory_order_seq_cst) -#define ma_atomic_store_i16(dst, src) ma_atomic_store_explicit_i16(dst, src, ma_atomic_memory_order_seq_cst) -#define ma_atomic_store_i32(dst, src) ma_atomic_store_explicit_i32(dst, src, ma_atomic_memory_order_seq_cst) -#define ma_atomic_store_i64(dst, src) ma_atomic_store_explicit_i64(dst, src, ma_atomic_memory_order_seq_cst) -#define ma_atomic_load_i8( ptr) ma_atomic_load_explicit_i8( ptr, ma_atomic_memory_order_seq_cst) -#define ma_atomic_load_i16(ptr) ma_atomic_load_explicit_i16(ptr, ma_atomic_memory_order_seq_cst) -#define ma_atomic_load_i32(ptr) ma_atomic_load_explicit_i32(ptr, ma_atomic_memory_order_seq_cst) -#define ma_atomic_load_i64(ptr) ma_atomic_load_explicit_i64(ptr, ma_atomic_memory_order_seq_cst) -#define ma_atomic_exchange_i8( dst, src) ma_atomic_exchange_explicit_i8( dst, src, ma_atomic_memory_order_seq_cst) -#define ma_atomic_exchange_i16(dst, src) ma_atomic_exchange_explicit_i16(dst, src, ma_atomic_memory_order_seq_cst) -#define ma_atomic_exchange_i32(dst, src) ma_atomic_exchange_explicit_i32(dst, src, ma_atomic_memory_order_seq_cst) -#define ma_atomic_exchange_i64(dst, src) ma_atomic_exchange_explicit_i64(dst, src, ma_atomic_memory_order_seq_cst) -#define ma_atomic_compare_exchange_strong_i8( dst, expected, desired) ma_atomic_compare_exchange_strong_explicit_i8( dst, expected, desired, ma_atomic_memory_order_seq_cst, ma_atomic_memory_order_seq_cst) -#define ma_atomic_compare_exchange_strong_i16(dst, expected, desired) ma_atomic_compare_exchange_strong_explicit_i16(dst, expected, desired, ma_atomic_memory_order_seq_cst, ma_atomic_memory_order_seq_cst) -#define ma_atomic_compare_exchange_strong_i32(dst, expected, desired) ma_atomic_compare_exchange_strong_explicit_i32(dst, expected, desired, ma_atomic_memory_order_seq_cst, ma_atomic_memory_order_seq_cst) -#define ma_atomic_compare_exchange_strong_i64(dst, expected, desired) ma_atomic_compare_exchange_strong_explicit_i64(dst, expected, desired, ma_atomic_memory_order_seq_cst, ma_atomic_memory_order_seq_cst) -#define ma_atomic_compare_exchange_weak_i8( dst, expected, desired) ma_atomic_compare_exchange_weak_explicit_i8( dst, expected, desired, ma_atomic_memory_order_seq_cst, ma_atomic_memory_order_seq_cst) -#define ma_atomic_compare_exchange_weak_i16(dst, expected, desired) ma_atomic_compare_exchange_weak_explicit_i16(dst, expected, desired, ma_atomic_memory_order_seq_cst, ma_atomic_memory_order_seq_cst) -#define ma_atomic_compare_exchange_weak_i32(dst, expected, desired) ma_atomic_compare_exchange_weak_explicit_i32(dst, expected, desired, ma_atomic_memory_order_seq_cst, ma_atomic_memory_order_seq_cst) -#define ma_atomic_compare_exchange_weak_i64(dst, expected, desired) ma_atomic_compare_exchange_weak_explicit_i64(dst, expected, desired, ma_atomic_memory_order_seq_cst, ma_atomic_memory_order_seq_cst) -#define ma_atomic_fetch_add_i8( dst, src) ma_atomic_fetch_add_explicit_i8( dst, src, ma_atomic_memory_order_seq_cst) -#define ma_atomic_fetch_add_i16(dst, src) ma_atomic_fetch_add_explicit_i16(dst, src, ma_atomic_memory_order_seq_cst) -#define ma_atomic_fetch_add_i32(dst, src) ma_atomic_fetch_add_explicit_i32(dst, src, ma_atomic_memory_order_seq_cst) -#define ma_atomic_fetch_add_i64(dst, src) ma_atomic_fetch_add_explicit_i64(dst, src, ma_atomic_memory_order_seq_cst) -#define ma_atomic_fetch_sub_i8( dst, src) ma_atomic_fetch_sub_explicit_i8( dst, src, ma_atomic_memory_order_seq_cst) -#define ma_atomic_fetch_sub_i16(dst, src) ma_atomic_fetch_sub_explicit_i16(dst, src, ma_atomic_memory_order_seq_cst) -#define ma_atomic_fetch_sub_i32(dst, src) ma_atomic_fetch_sub_explicit_i32(dst, src, ma_atomic_memory_order_seq_cst) -#define ma_atomic_fetch_sub_i64(dst, src) ma_atomic_fetch_sub_explicit_i64(dst, src, ma_atomic_memory_order_seq_cst) -#define ma_atomic_fetch_or_i8( dst, src) ma_atomic_fetch_or_explicit_i8( dst, src, ma_atomic_memory_order_seq_cst) -#define ma_atomic_fetch_or_i16(dst, src) ma_atomic_fetch_or_explicit_i16(dst, src, ma_atomic_memory_order_seq_cst) -#define ma_atomic_fetch_or_i32(dst, src) ma_atomic_fetch_or_explicit_i32(dst, src, ma_atomic_memory_order_seq_cst) -#define ma_atomic_fetch_or_i64(dst, src) ma_atomic_fetch_or_explicit_i64(dst, src, ma_atomic_memory_order_seq_cst) -#define ma_atomic_fetch_xor_i8( dst, src) ma_atomic_fetch_xor_explicit_i8( dst, src, ma_atomic_memory_order_seq_cst) -#define ma_atomic_fetch_xor_i16(dst, src) ma_atomic_fetch_xor_explicit_i16(dst, src, ma_atomic_memory_order_seq_cst) -#define ma_atomic_fetch_xor_i32(dst, src) ma_atomic_fetch_xor_explicit_i32(dst, src, ma_atomic_memory_order_seq_cst) -#define ma_atomic_fetch_xor_i64(dst, src) ma_atomic_fetch_xor_explicit_i64(dst, src, ma_atomic_memory_order_seq_cst) -#define ma_atomic_fetch_and_i8( dst, src) ma_atomic_fetch_and_explicit_i8( dst, src, ma_atomic_memory_order_seq_cst) -#define ma_atomic_fetch_and_i16(dst, src) ma_atomic_fetch_and_explicit_i16(dst, src, ma_atomic_memory_order_seq_cst) -#define ma_atomic_fetch_and_i32(dst, src) ma_atomic_fetch_and_explicit_i32(dst, src, ma_atomic_memory_order_seq_cst) -#define ma_atomic_fetch_and_i64(dst, src) ma_atomic_fetch_and_explicit_i64(dst, src, ma_atomic_memory_order_seq_cst) -#define ma_atomic_compare_and_swap_i8( dst, expected, dedsired) (ma_int8 )ma_atomic_compare_and_swap_8( (ma_uint8* )dst, (ma_uint8 )expected, (ma_uint8 )dedsired) -#define ma_atomic_compare_and_swap_i16(dst, expected, dedsired) (ma_int16)ma_atomic_compare_and_swap_16((ma_uint16*)dst, (ma_uint16)expected, (ma_uint16)dedsired) -#define ma_atomic_compare_and_swap_i32(dst, expected, dedsired) (ma_int32)ma_atomic_compare_and_swap_32((ma_uint32*)dst, (ma_uint32)expected, (ma_uint32)dedsired) -#define ma_atomic_compare_and_swap_i64(dst, expected, dedsired) (ma_int64)ma_atomic_compare_and_swap_64((ma_uint64*)dst, (ma_uint64)expected, (ma_uint64)dedsired) -typedef union -{ - ma_uint32 i; - float f; -} ma_atomic_if32; -typedef union -{ - ma_uint64 i; - double f; -} ma_atomic_if64; -#define ma_atomic_clear_explicit_f32(ptr, order) ma_atomic_clear_explicit_32((ma_uint32*)ptr, order) -#define ma_atomic_clear_explicit_f64(ptr, order) ma_atomic_clear_explicit_64((ma_uint64*)ptr, order) -static MA_INLINE void ma_atomic_store_explicit_f32(volatile float* dst, float src, ma_atomic_memory_order order) -{ - ma_atomic_if32 x; - x.f = src; - ma_atomic_store_explicit_32((volatile ma_uint32*)dst, x.i, order); -} -static MA_INLINE void ma_atomic_store_explicit_f64(volatile double* dst, double src, ma_atomic_memory_order order) -{ - ma_atomic_if64 x; - x.f = src; - ma_atomic_store_explicit_64((volatile ma_uint64*)dst, x.i, order); -} -static MA_INLINE float ma_atomic_load_explicit_f32(volatile const float* ptr, ma_atomic_memory_order order) -{ - ma_atomic_if32 r; - r.i = ma_atomic_load_explicit_32((volatile const ma_uint32*)ptr, order); - return r.f; -} -static MA_INLINE double ma_atomic_load_explicit_f64(volatile const double* ptr, ma_atomic_memory_order order) -{ - ma_atomic_if64 r; - r.i = ma_atomic_load_explicit_64((volatile const ma_uint64*)ptr, order); - return r.f; -} -static MA_INLINE float ma_atomic_exchange_explicit_f32(volatile float* dst, float src, ma_atomic_memory_order order) -{ - ma_atomic_if32 r; - ma_atomic_if32 x; - x.f = src; - r.i = ma_atomic_exchange_explicit_32((volatile ma_uint32*)dst, x.i, order); - return r.f; -} -static MA_INLINE double ma_atomic_exchange_explicit_f64(volatile double* dst, double src, ma_atomic_memory_order order) -{ - ma_atomic_if64 r; - ma_atomic_if64 x; - x.f = src; - r.i = ma_atomic_exchange_explicit_64((volatile ma_uint64*)dst, x.i, order); - return r.f; -} -static MA_INLINE ma_bool32 ma_atomic_compare_exchange_strong_explicit_f32(volatile float* dst, float* expected, float desired, ma_atomic_memory_order successOrder, ma_atomic_memory_order failureOrder) -{ - ma_atomic_if32 d; - d.f = desired; - return ma_atomic_compare_exchange_strong_explicit_32((volatile ma_uint32*)dst, (ma_uint32*)expected, d.i, successOrder, failureOrder); -} -static MA_INLINE ma_bool32 ma_atomic_compare_exchange_strong_explicit_f64(volatile double* dst, double* expected, double desired, ma_atomic_memory_order successOrder, ma_atomic_memory_order failureOrder) -{ - ma_atomic_if64 d; - d.f = desired; - return ma_atomic_compare_exchange_strong_explicit_64((volatile ma_uint64*)dst, (ma_uint64*)expected, d.i, successOrder, failureOrder); -} -static MA_INLINE ma_bool32 ma_atomic_compare_exchange_weak_explicit_f32(volatile float* dst, float* expected, float desired, ma_atomic_memory_order successOrder, ma_atomic_memory_order failureOrder) -{ - ma_atomic_if32 d; - d.f = desired; - return ma_atomic_compare_exchange_weak_explicit_32((volatile ma_uint32*)dst, (ma_uint32*)expected, d.i, successOrder, failureOrder); -} -static MA_INLINE ma_bool32 ma_atomic_compare_exchange_weak_explicit_f64(volatile double* dst, double* expected, double desired, ma_atomic_memory_order successOrder, ma_atomic_memory_order failureOrder) -{ - ma_atomic_if64 d; - d.f = desired; - return ma_atomic_compare_exchange_weak_explicit_64((volatile ma_uint64*)dst, (ma_uint64*)expected, d.i, successOrder, failureOrder); -} -static MA_INLINE float ma_atomic_fetch_add_explicit_f32(volatile float* dst, float src, ma_atomic_memory_order order) -{ - ma_atomic_if32 r; - ma_atomic_if32 x; - x.f = src; - r.i = ma_atomic_fetch_add_explicit_32((volatile ma_uint32*)dst, x.i, order); - return r.f; -} -static MA_INLINE double ma_atomic_fetch_add_explicit_f64(volatile double* dst, double src, ma_atomic_memory_order order) -{ - ma_atomic_if64 r; - ma_atomic_if64 x; - x.f = src; - r.i = ma_atomic_fetch_add_explicit_64((volatile ma_uint64*)dst, x.i, order); - return r.f; -} -static MA_INLINE float ma_atomic_fetch_sub_explicit_f32(volatile float* dst, float src, ma_atomic_memory_order order) -{ - ma_atomic_if32 r; - ma_atomic_if32 x; - x.f = src; - r.i = ma_atomic_fetch_sub_explicit_32((volatile ma_uint32*)dst, x.i, order); - return r.f; -} -static MA_INLINE double ma_atomic_fetch_sub_explicit_f64(volatile double* dst, double src, ma_atomic_memory_order order) -{ - ma_atomic_if64 r; - ma_atomic_if64 x; - x.f = src; - r.i = ma_atomic_fetch_sub_explicit_64((volatile ma_uint64*)dst, x.i, order); - return r.f; -} -static MA_INLINE float ma_atomic_fetch_or_explicit_f32(volatile float* dst, float src, ma_atomic_memory_order order) -{ - ma_atomic_if32 r; - ma_atomic_if32 x; - x.f = src; - r.i = ma_atomic_fetch_or_explicit_32((volatile ma_uint32*)dst, x.i, order); - return r.f; -} -static MA_INLINE double ma_atomic_fetch_or_explicit_f64(volatile double* dst, double src, ma_atomic_memory_order order) -{ - ma_atomic_if64 r; - ma_atomic_if64 x; - x.f = src; - r.i = ma_atomic_fetch_or_explicit_64((volatile ma_uint64*)dst, x.i, order); - return r.f; -} -static MA_INLINE float ma_atomic_fetch_xor_explicit_f32(volatile float* dst, float src, ma_atomic_memory_order order) -{ - ma_atomic_if32 r; - ma_atomic_if32 x; - x.f = src; - r.i = ma_atomic_fetch_xor_explicit_32((volatile ma_uint32*)dst, x.i, order); - return r.f; -} -static MA_INLINE double ma_atomic_fetch_xor_explicit_f64(volatile double* dst, double src, ma_atomic_memory_order order) -{ - ma_atomic_if64 r; - ma_atomic_if64 x; - x.f = src; - r.i = ma_atomic_fetch_xor_explicit_64((volatile ma_uint64*)dst, x.i, order); - return r.f; -} -static MA_INLINE float ma_atomic_fetch_and_explicit_f32(volatile float* dst, float src, ma_atomic_memory_order order) -{ - ma_atomic_if32 r; - ma_atomic_if32 x; - x.f = src; - r.i = ma_atomic_fetch_and_explicit_32((volatile ma_uint32*)dst, x.i, order); - return r.f; -} -static MA_INLINE double ma_atomic_fetch_and_explicit_f64(volatile double* dst, double src, ma_atomic_memory_order order) -{ - ma_atomic_if64 r; - ma_atomic_if64 x; - x.f = src; - r.i = ma_atomic_fetch_and_explicit_64((volatile ma_uint64*)dst, x.i, order); - return r.f; -} -#define ma_atomic_clear_f32(ptr) (float )ma_atomic_clear_explicit_f32(ptr, ma_atomic_memory_order_seq_cst) -#define ma_atomic_clear_f64(ptr) (double)ma_atomic_clear_explicit_f64(ptr, ma_atomic_memory_order_seq_cst) -#define ma_atomic_store_f32(dst, src) ma_atomic_store_explicit_f32(dst, src, ma_atomic_memory_order_seq_cst) -#define ma_atomic_store_f64(dst, src) ma_atomic_store_explicit_f64(dst, src, ma_atomic_memory_order_seq_cst) -#define ma_atomic_load_f32(ptr) (float )ma_atomic_load_explicit_f32(ptr, ma_atomic_memory_order_seq_cst) -#define ma_atomic_load_f64(ptr) (double)ma_atomic_load_explicit_f64(ptr, ma_atomic_memory_order_seq_cst) -#define ma_atomic_exchange_f32(dst, src) (float )ma_atomic_exchange_explicit_f32(dst, src, ma_atomic_memory_order_seq_cst) -#define ma_atomic_exchange_f64(dst, src) (double)ma_atomic_exchange_explicit_f64(dst, src, ma_atomic_memory_order_seq_cst) -#define ma_atomic_compare_exchange_strong_f32(dst, expected, desired) ma_atomic_compare_exchange_strong_explicit_f32(dst, expected, desired, ma_atomic_memory_order_seq_cst, ma_atomic_memory_order_seq_cst) -#define ma_atomic_compare_exchange_strong_f64(dst, expected, desired) ma_atomic_compare_exchange_strong_explicit_f64(dst, expected, desired, ma_atomic_memory_order_seq_cst, ma_atomic_memory_order_seq_cst) -#define ma_atomic_compare_exchange_weak_f32(dst, expected, desired) ma_atomic_compare_exchange_weak_explicit_f32(dst, expected, desired, ma_atomic_memory_order_seq_cst, ma_atomic_memory_order_seq_cst) -#define ma_atomic_compare_exchange_weak_f64(dst, expected, desired) ma_atomic_compare_exchange_weak_explicit_f64(dst, expected, desired, ma_atomic_memory_order_seq_cst, ma_atomic_memory_order_seq_cst) -#define ma_atomic_fetch_add_f32(dst, src) ma_atomic_fetch_add_explicit_f32(dst, src, ma_atomic_memory_order_seq_cst) -#define ma_atomic_fetch_add_f64(dst, src) ma_atomic_fetch_add_explicit_f64(dst, src, ma_atomic_memory_order_seq_cst) -#define ma_atomic_fetch_sub_f32(dst, src) ma_atomic_fetch_sub_explicit_f32(dst, src, ma_atomic_memory_order_seq_cst) -#define ma_atomic_fetch_sub_f64(dst, src) ma_atomic_fetch_sub_explicit_f64(dst, src, ma_atomic_memory_order_seq_cst) -#define ma_atomic_fetch_or_f32(dst, src) ma_atomic_fetch_or_explicit_f32(dst, src, ma_atomic_memory_order_seq_cst) -#define ma_atomic_fetch_or_f64(dst, src) ma_atomic_fetch_or_explicit_f64(dst, src, ma_atomic_memory_order_seq_cst) -#define ma_atomic_fetch_xor_f32(dst, src) ma_atomic_fetch_xor_explicit_f32(dst, src, ma_atomic_memory_order_seq_cst) -#define ma_atomic_fetch_xor_f64(dst, src) ma_atomic_fetch_xor_explicit_f64(dst, src, ma_atomic_memory_order_seq_cst) -#define ma_atomic_fetch_and_f32(dst, src) ma_atomic_fetch_and_explicit_f32(dst, src, ma_atomic_memory_order_seq_cst) -#define ma_atomic_fetch_and_f64(dst, src) ma_atomic_fetch_and_explicit_f64(dst, src, ma_atomic_memory_order_seq_cst) -static MA_INLINE float ma_atomic_compare_and_swap_f32(volatile float* dst, float expected, float desired) -{ - ma_atomic_if32 r; - ma_atomic_if32 e, d; - e.f = expected; - d.f = desired; - r.i = ma_atomic_compare_and_swap_32((volatile ma_uint32*)dst, e.i, d.i); - return r.f; -} -static MA_INLINE double ma_atomic_compare_and_swap_f64(volatile double* dst, double expected, double desired) -{ - ma_atomic_if64 r; - ma_atomic_if64 e, d; - e.f = expected; - d.f = desired; - r.i = ma_atomic_compare_and_swap_64((volatile ma_uint64*)dst, e.i, d.i); - return r.f; -} -typedef ma_atomic_flag ma_atomic_spinlock; -static MA_INLINE void ma_atomic_spinlock_lock(volatile ma_atomic_spinlock* pSpinlock) -{ - for (;;) { - if (ma_atomic_flag_test_and_set_explicit(pSpinlock, ma_atomic_memory_order_acquire) == 0) { - break; - } - while (ma_atomic_flag_load_explicit(pSpinlock, ma_atomic_memory_order_relaxed) == 1) { - } - } -} -static MA_INLINE void ma_atomic_spinlock_unlock(volatile ma_atomic_spinlock* pSpinlock) -{ - ma_atomic_flag_clear_explicit(pSpinlock, ma_atomic_memory_order_release); -} -#if defined(__clang__) || (defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6))) - #pragma GCC diagnostic pop -#endif -#if defined(__cplusplus) -} -#endif -#endif -/* c89atomic.h end */ - -#define MA_ATOMIC_SAFE_TYPE_IMPL(c89TypeExtension, type) \ - static MA_INLINE ma_##type ma_atomic_##type##_get(ma_atomic_##type* x) \ - { \ - return (ma_##type)ma_atomic_load_##c89TypeExtension(&x->value); \ - } \ - static MA_INLINE void ma_atomic_##type##_set(ma_atomic_##type* x, ma_##type value) \ - { \ - ma_atomic_store_##c89TypeExtension(&x->value, value); \ - } \ - static MA_INLINE ma_##type ma_atomic_##type##_exchange(ma_atomic_##type* x, ma_##type value) \ - { \ - return (ma_##type)ma_atomic_exchange_##c89TypeExtension(&x->value, value); \ - } \ - static MA_INLINE ma_bool32 ma_atomic_##type##_compare_exchange(ma_atomic_##type* x, ma_##type* expected, ma_##type desired) \ - { \ - return ma_atomic_compare_exchange_weak_##c89TypeExtension(&x->value, expected, desired); \ - } \ - static MA_INLINE ma_##type ma_atomic_##type##_fetch_add(ma_atomic_##type* x, ma_##type y) \ - { \ - return (ma_##type)ma_atomic_fetch_add_##c89TypeExtension(&x->value, y); \ - } \ - static MA_INLINE ma_##type ma_atomic_##type##_fetch_sub(ma_atomic_##type* x, ma_##type y) \ - { \ - return (ma_##type)ma_atomic_fetch_sub_##c89TypeExtension(&x->value, y); \ - } \ - static MA_INLINE ma_##type ma_atomic_##type##_fetch_or(ma_atomic_##type* x, ma_##type y) \ - { \ - return (ma_##type)ma_atomic_fetch_or_##c89TypeExtension(&x->value, y); \ - } \ - static MA_INLINE ma_##type ma_atomic_##type##_fetch_xor(ma_atomic_##type* x, ma_##type y) \ - { \ - return (ma_##type)ma_atomic_fetch_xor_##c89TypeExtension(&x->value, y); \ - } \ - static MA_INLINE ma_##type ma_atomic_##type##_fetch_and(ma_atomic_##type* x, ma_##type y) \ - { \ - return (ma_##type)ma_atomic_fetch_and_##c89TypeExtension(&x->value, y); \ - } \ - static MA_INLINE ma_##type ma_atomic_##type##_compare_and_swap(ma_atomic_##type* x, ma_##type expected, ma_##type desired) \ - { \ - return (ma_##type)ma_atomic_compare_and_swap_##c89TypeExtension(&x->value, expected, desired); \ - } \ - -#define MA_ATOMIC_SAFE_TYPE_IMPL_PTR(type) \ - static MA_INLINE ma_##type* ma_atomic_ptr_##type##_get(ma_atomic_ptr_##type* x) \ - { \ - return ma_atomic_load_ptr((void**)&x->value); \ - } \ - static MA_INLINE void ma_atomic_ptr_##type##_set(ma_atomic_ptr_##type* x, ma_##type* value) \ - { \ - ma_atomic_store_ptr((void**)&x->value, (void*)value); \ - } \ - static MA_INLINE ma_##type* ma_atomic_ptr_##type##_exchange(ma_atomic_ptr_##type* x, ma_##type* value) \ - { \ - return ma_atomic_exchange_ptr((void**)&x->value, (void*)value); \ - } \ - static MA_INLINE ma_bool32 ma_atomic_ptr_##type##_compare_exchange(ma_atomic_ptr_##type* x, ma_##type** expected, ma_##type* desired) \ - { \ - return ma_atomic_compare_exchange_weak_ptr((void**)&x->value, (void*)expected, (void*)desired); \ - } \ - static MA_INLINE ma_##type* ma_atomic_ptr_##type##_compare_and_swap(ma_atomic_ptr_##type* x, ma_##type* expected, ma_##type* desired) \ - { \ - return (ma_##type*)ma_atomic_compare_and_swap_ptr((void**)&x->value, (void*)expected, (void*)desired); \ - } \ - -MA_ATOMIC_SAFE_TYPE_IMPL(32, uint32) -MA_ATOMIC_SAFE_TYPE_IMPL(i32, int32) -MA_ATOMIC_SAFE_TYPE_IMPL(64, uint64) -MA_ATOMIC_SAFE_TYPE_IMPL(f32, float) -MA_ATOMIC_SAFE_TYPE_IMPL(32, bool32) - -#if !defined(MA_NO_DEVICE_IO) -MA_ATOMIC_SAFE_TYPE_IMPL(i32, device_state) -#endif - - -MA_API ma_uint64 ma_calculate_frame_count_after_resampling(ma_uint32 sampleRateOut, ma_uint32 sampleRateIn, ma_uint64 frameCountIn) -{ - /* This is based on the calculation in ma_linear_resampler_get_expected_output_frame_count(). */ - ma_uint64 outputFrameCount; - ma_uint64 preliminaryInputFrameCountFromFrac; - ma_uint64 preliminaryInputFrameCount; - - if (sampleRateIn == 0 || sampleRateOut == 0 || frameCountIn == 0) { - return 0; - } - - if (sampleRateOut == sampleRateIn) { - return frameCountIn; - } - - outputFrameCount = (frameCountIn * sampleRateOut) / sampleRateIn; - - preliminaryInputFrameCountFromFrac = (outputFrameCount * (sampleRateIn / sampleRateOut)) / sampleRateOut; - preliminaryInputFrameCount = (outputFrameCount * (sampleRateIn % sampleRateOut)) + preliminaryInputFrameCountFromFrac; - - if (preliminaryInputFrameCount <= frameCountIn) { - outputFrameCount += 1; - } - - return outputFrameCount; -} - -#ifndef MA_DATA_CONVERTER_STACK_BUFFER_SIZE -#define MA_DATA_CONVERTER_STACK_BUFFER_SIZE 4096 -#endif - - - -#if defined(MA_WIN32) -static ma_result ma_result_from_GetLastError(DWORD error) -{ - switch (error) - { - case ERROR_SUCCESS: return MA_SUCCESS; - case ERROR_PATH_NOT_FOUND: return MA_DOES_NOT_EXIST; - case ERROR_TOO_MANY_OPEN_FILES: return MA_TOO_MANY_OPEN_FILES; - case ERROR_NOT_ENOUGH_MEMORY: return MA_OUT_OF_MEMORY; - case ERROR_DISK_FULL: return MA_NO_SPACE; - case ERROR_HANDLE_EOF: return MA_AT_END; - case ERROR_NEGATIVE_SEEK: return MA_BAD_SEEK; - case ERROR_INVALID_PARAMETER: return MA_INVALID_ARGS; - case ERROR_ACCESS_DENIED: return MA_ACCESS_DENIED; - case ERROR_SEM_TIMEOUT: return MA_TIMEOUT; - case ERROR_FILE_NOT_FOUND: return MA_DOES_NOT_EXIST; - default: break; - } - - return MA_ERROR; -} -#endif /* MA_WIN32 */ - - -/******************************************************************************* - -Threading - -*******************************************************************************/ -static MA_INLINE ma_result ma_spinlock_lock_ex(volatile ma_spinlock* pSpinlock, ma_bool32 yield) -{ - if (pSpinlock == NULL) { - return MA_INVALID_ARGS; - } - - for (;;) { - if (ma_atomic_exchange_explicit_32(pSpinlock, 1, ma_atomic_memory_order_acquire) == 0) { - break; - } - - while (ma_atomic_load_explicit_32(pSpinlock, ma_atomic_memory_order_relaxed) == 1) { - if (yield) { - ma_yield(); - } - } - } - - return MA_SUCCESS; -} - -MA_API ma_result ma_spinlock_lock(volatile ma_spinlock* pSpinlock) -{ - return ma_spinlock_lock_ex(pSpinlock, MA_TRUE); -} - -MA_API ma_result ma_spinlock_lock_noyield(volatile ma_spinlock* pSpinlock) -{ - return ma_spinlock_lock_ex(pSpinlock, MA_FALSE); -} - -MA_API ma_result ma_spinlock_unlock(volatile ma_spinlock* pSpinlock) -{ - if (pSpinlock == NULL) { - return MA_INVALID_ARGS; - } - - ma_atomic_store_explicit_32(pSpinlock, 0, ma_atomic_memory_order_release); - return MA_SUCCESS; -} - - -#ifndef MA_NO_THREADING -#if defined(MA_POSIX) - #define MA_THREADCALL - typedef void* ma_thread_result; -#elif defined(MA_WIN32) - #define MA_THREADCALL WINAPI - typedef unsigned long ma_thread_result; -#endif - -typedef ma_thread_result (MA_THREADCALL * ma_thread_entry_proc)(void* pData); - -#ifdef MA_POSIX -static ma_result ma_thread_create__posix(ma_thread* pThread, ma_thread_priority priority, size_t stackSize, ma_thread_entry_proc entryProc, void* pData) -{ - int result; - pthread_attr_t* pAttr = NULL; - -#if !defined(__EMSCRIPTEN__) && !defined(__3DS__) - /* Try setting the thread priority. It's not critical if anything fails here. */ - pthread_attr_t attr; - if (pthread_attr_init(&attr) == 0) { - int scheduler = -1; - - /* We successfully initialized our attributes object so we can assign the pointer so it's passed into pthread_create(). */ - pAttr = &attr; - - /* We need to set the scheduler policy. Only do this if the OS supports pthread_attr_setschedpolicy() */ - #if !defined(MA_BEOS) - { - if (priority == ma_thread_priority_idle) { - #ifdef SCHED_IDLE - if (pthread_attr_setschedpolicy(&attr, SCHED_IDLE) == 0) { - scheduler = SCHED_IDLE; - } - #endif - } else if (priority == ma_thread_priority_realtime) { - #ifdef SCHED_FIFO - if (pthread_attr_setschedpolicy(&attr, SCHED_FIFO) == 0) { - scheduler = SCHED_FIFO; - } - #endif - #ifdef MA_LINUX - } else { - scheduler = sched_getscheduler(0); - #endif - } - } - #endif - - if (stackSize > 0) { - pthread_attr_setstacksize(&attr, stackSize); - } - - if (scheduler != -1) { - int priorityMin = sched_get_priority_min(scheduler); - int priorityMax = sched_get_priority_max(scheduler); - int priorityStep = (priorityMax - priorityMin) / 7; /* 7 = number of priorities supported by miniaudio. */ - - struct sched_param sched; - if (pthread_attr_getschedparam(&attr, &sched) == 0) { - if (priority == ma_thread_priority_idle) { - sched.sched_priority = priorityMin; - } else if (priority == ma_thread_priority_realtime) { - #if defined(MA_PTHREAD_REALTIME_THREAD_PRIORITY) - { - sched.sched_priority = MA_PTHREAD_REALTIME_THREAD_PRIORITY; - } - #else - { - sched.sched_priority = priorityMax; - } - #endif - } else { - sched.sched_priority += ((int)priority + 5) * priorityStep; /* +5 because the lowest priority is -5. */ - } - - if (sched.sched_priority < priorityMin) { - sched.sched_priority = priorityMin; - } - if (sched.sched_priority > priorityMax) { - sched.sched_priority = priorityMax; - } - - /* I'm not treating a failure of setting the priority as a critical error so not aborting on failure here. */ - if (pthread_attr_setschedparam(&attr, &sched) == 0) { - #if !defined(MA_ANDROID) || (defined(__ANDROID_API__) && __ANDROID_API__ >= 28) - { - pthread_attr_setinheritsched(&attr, PTHREAD_EXPLICIT_SCHED); - } - #endif - } - } - } - } -#else - /* It's the emscripten build. We'll have a few unused parameters. */ - (void)priority; - (void)stackSize; -#endif - - result = pthread_create((pthread_t*)pThread, pAttr, entryProc, pData); - - /* The thread attributes object is no longer required. */ - if (pAttr != NULL) { - pthread_attr_destroy(pAttr); - } - - if (result != 0) { - return ma_result_from_errno(result); - } - - return MA_SUCCESS; -} - -static void ma_thread_wait__posix(ma_thread* pThread) -{ - pthread_join((pthread_t)*pThread, NULL); -} - - -static ma_result ma_mutex_init__posix(ma_mutex* pMutex) -{ - int result; - - if (pMutex == NULL) { - return MA_INVALID_ARGS; - } - - MA_ZERO_OBJECT(pMutex); - - result = pthread_mutex_init((pthread_mutex_t*)pMutex, NULL); - if (result != 0) { - return ma_result_from_errno(result); - } - - return MA_SUCCESS; -} - -static void ma_mutex_uninit__posix(ma_mutex* pMutex) -{ - pthread_mutex_destroy((pthread_mutex_t*)pMutex); -} - -static void ma_mutex_lock__posix(ma_mutex* pMutex) -{ - pthread_mutex_lock((pthread_mutex_t*)pMutex); -} - -static void ma_mutex_unlock__posix(ma_mutex* pMutex) -{ - pthread_mutex_unlock((pthread_mutex_t*)pMutex); -} - - -static ma_result ma_event_init__posix(ma_event* pEvent) -{ - int result; - - result = pthread_mutex_init((pthread_mutex_t*)&pEvent->lock, NULL); - if (result != 0) { - return ma_result_from_errno(result); - } - - result = pthread_cond_init((pthread_cond_t*)&pEvent->cond, NULL); - if (result != 0) { - pthread_mutex_destroy((pthread_mutex_t*)&pEvent->lock); - return ma_result_from_errno(result); - } - - pEvent->value = 0; - return MA_SUCCESS; -} - -static void ma_event_uninit__posix(ma_event* pEvent) -{ - pthread_cond_destroy((pthread_cond_t*)&pEvent->cond); - pthread_mutex_destroy((pthread_mutex_t*)&pEvent->lock); -} - -static ma_result ma_event_wait__posix(ma_event* pEvent) -{ - pthread_mutex_lock((pthread_mutex_t*)&pEvent->lock); - { - while (pEvent->value == 0) { - pthread_cond_wait((pthread_cond_t*)&pEvent->cond, (pthread_mutex_t*)&pEvent->lock); - } - pEvent->value = 0; /* Auto-reset. */ - } - pthread_mutex_unlock((pthread_mutex_t*)&pEvent->lock); - - return MA_SUCCESS; -} - -static ma_result ma_event_signal__posix(ma_event* pEvent) -{ - pthread_mutex_lock((pthread_mutex_t*)&pEvent->lock); - { - pEvent->value = 1; - pthread_cond_signal((pthread_cond_t*)&pEvent->cond); - } - pthread_mutex_unlock((pthread_mutex_t*)&pEvent->lock); - - return MA_SUCCESS; -} - - -static ma_result ma_semaphore_init__posix(int initialValue, ma_semaphore* pSemaphore) -{ - int result; - - if (pSemaphore == NULL) { - return MA_INVALID_ARGS; - } - - pSemaphore->value = initialValue; - - result = pthread_mutex_init((pthread_mutex_t*)&pSemaphore->lock, NULL); - if (result != 0) { - return ma_result_from_errno(result); /* Failed to create mutex. */ - } - - result = pthread_cond_init((pthread_cond_t*)&pSemaphore->cond, NULL); - if (result != 0) { - pthread_mutex_destroy((pthread_mutex_t*)&pSemaphore->lock); - return ma_result_from_errno(result); /* Failed to create condition variable. */ - } - - return MA_SUCCESS; -} - -static void ma_semaphore_uninit__posix(ma_semaphore* pSemaphore) -{ - if (pSemaphore == NULL) { - return; - } - - pthread_cond_destroy((pthread_cond_t*)&pSemaphore->cond); - pthread_mutex_destroy((pthread_mutex_t*)&pSemaphore->lock); -} - -static ma_result ma_semaphore_wait__posix(ma_semaphore* pSemaphore) -{ - if (pSemaphore == NULL) { - return MA_INVALID_ARGS; - } - - pthread_mutex_lock((pthread_mutex_t*)&pSemaphore->lock); - { - /* We need to wait on a condition variable before escaping. We can't return from this function until the semaphore has been signaled. */ - while (pSemaphore->value == 0) { - pthread_cond_wait((pthread_cond_t*)&pSemaphore->cond, (pthread_mutex_t*)&pSemaphore->lock); - } - - pSemaphore->value -= 1; - } - pthread_mutex_unlock((pthread_mutex_t*)&pSemaphore->lock); - - return MA_SUCCESS; -} - -static ma_result ma_semaphore_release__posix(ma_semaphore* pSemaphore) -{ - if (pSemaphore == NULL) { - return MA_INVALID_ARGS; - } - - pthread_mutex_lock((pthread_mutex_t*)&pSemaphore->lock); - { - pSemaphore->value += 1; - pthread_cond_signal((pthread_cond_t*)&pSemaphore->cond); - } - pthread_mutex_unlock((pthread_mutex_t*)&pSemaphore->lock); - - return MA_SUCCESS; -} -#elif defined(MA_WIN32) -static int ma_thread_priority_to_win32(ma_thread_priority priority) -{ - switch (priority) { - case ma_thread_priority_idle: return THREAD_PRIORITY_IDLE; - case ma_thread_priority_lowest: return THREAD_PRIORITY_LOWEST; - case ma_thread_priority_low: return THREAD_PRIORITY_BELOW_NORMAL; - case ma_thread_priority_normal: return THREAD_PRIORITY_NORMAL; - case ma_thread_priority_high: return THREAD_PRIORITY_ABOVE_NORMAL; - case ma_thread_priority_highest: return THREAD_PRIORITY_HIGHEST; - case ma_thread_priority_realtime: return THREAD_PRIORITY_TIME_CRITICAL; - default: return THREAD_PRIORITY_NORMAL; - } -} - -static ma_result ma_thread_create__win32(ma_thread* pThread, ma_thread_priority priority, size_t stackSize, ma_thread_entry_proc entryProc, void* pData) -{ - DWORD threadID; /* Not used. Only used for passing into CreateThread() so it doesn't fail on Windows 98. */ - - *pThread = CreateThread(NULL, stackSize, entryProc, pData, 0, &threadID); - if (*pThread == NULL) { - return ma_result_from_GetLastError(GetLastError()); - } - - SetThreadPriority((HANDLE)*pThread, ma_thread_priority_to_win32(priority)); - - return MA_SUCCESS; -} - -static void ma_thread_wait__win32(ma_thread* pThread) -{ - WaitForSingleObject((HANDLE)*pThread, INFINITE); - CloseHandle((HANDLE)*pThread); -} - - -static ma_result ma_mutex_init__win32(ma_mutex* pMutex) -{ - *pMutex = CreateEventA(NULL, FALSE, TRUE, NULL); - if (*pMutex == NULL) { - return ma_result_from_GetLastError(GetLastError()); - } - - return MA_SUCCESS; -} - -static void ma_mutex_uninit__win32(ma_mutex* pMutex) -{ - CloseHandle((HANDLE)*pMutex); -} - -static void ma_mutex_lock__win32(ma_mutex* pMutex) -{ - WaitForSingleObject((HANDLE)*pMutex, INFINITE); -} - -static void ma_mutex_unlock__win32(ma_mutex* pMutex) -{ - SetEvent((HANDLE)*pMutex); -} - - -static ma_result ma_event_init__win32(ma_event* pEvent) -{ - *pEvent = CreateEventA(NULL, FALSE, FALSE, NULL); - if (*pEvent == NULL) { - return ma_result_from_GetLastError(GetLastError()); - } - - return MA_SUCCESS; -} - -static void ma_event_uninit__win32(ma_event* pEvent) -{ - CloseHandle((HANDLE)*pEvent); -} - -static ma_result ma_event_wait__win32(ma_event* pEvent) -{ - DWORD result = WaitForSingleObject((HANDLE)*pEvent, INFINITE); - if (result == WAIT_OBJECT_0) { - return MA_SUCCESS; - } - - if (result == WAIT_TIMEOUT) { - return MA_TIMEOUT; - } - - return ma_result_from_GetLastError(GetLastError()); -} - -static ma_result ma_event_signal__win32(ma_event* pEvent) -{ - BOOL result = SetEvent((HANDLE)*pEvent); - if (result == 0) { - return ma_result_from_GetLastError(GetLastError()); - } - - return MA_SUCCESS; -} - - -static ma_result ma_semaphore_init__win32(int initialValue, ma_semaphore* pSemaphore) -{ - *pSemaphore = CreateSemaphoreW(NULL, (LONG)initialValue, LONG_MAX, NULL); - if (*pSemaphore == NULL) { - return ma_result_from_GetLastError(GetLastError()); - } - - return MA_SUCCESS; -} - -static void ma_semaphore_uninit__win32(ma_semaphore* pSemaphore) -{ - CloseHandle((HANDLE)*pSemaphore); -} - -static ma_result ma_semaphore_wait__win32(ma_semaphore* pSemaphore) -{ - DWORD result = WaitForSingleObject((HANDLE)*pSemaphore, INFINITE); - if (result == WAIT_OBJECT_0) { - return MA_SUCCESS; - } - - if (result == WAIT_TIMEOUT) { - return MA_TIMEOUT; - } - - return ma_result_from_GetLastError(GetLastError()); -} - -static ma_result ma_semaphore_release__win32(ma_semaphore* pSemaphore) -{ - BOOL result = ReleaseSemaphore((HANDLE)*pSemaphore, 1, NULL); - if (result == 0) { - return ma_result_from_GetLastError(GetLastError()); - } - - return MA_SUCCESS; -} -#endif - -typedef struct -{ - ma_thread_entry_proc entryProc; - void* pData; - ma_allocation_callbacks allocationCallbacks; -} ma_thread_proxy_data; - -static ma_thread_result MA_THREADCALL ma_thread_entry_proxy(void* pData) -{ - ma_thread_proxy_data* pProxyData = (ma_thread_proxy_data*)pData; - ma_thread_entry_proc entryProc; - void* pEntryProcData; - ma_thread_result result; - - #if defined(MA_ON_THREAD_ENTRY) - MA_ON_THREAD_ENTRY - #endif - - entryProc = pProxyData->entryProc; - pEntryProcData = pProxyData->pData; - - /* Free the proxy data before getting into the real thread entry proc. */ - ma_free(pProxyData, &pProxyData->allocationCallbacks); - - result = entryProc(pEntryProcData); - - #if defined(MA_ON_THREAD_EXIT) - MA_ON_THREAD_EXIT - #endif - - return result; -} - -static ma_result ma_thread_create(ma_thread* pThread, ma_thread_priority priority, size_t stackSize, ma_thread_entry_proc entryProc, void* pData, const ma_allocation_callbacks* pAllocationCallbacks) -{ - ma_result result; - ma_thread_proxy_data* pProxyData; - - if (pThread == NULL || entryProc == NULL) { - return MA_INVALID_ARGS; - } - - pProxyData = (ma_thread_proxy_data*)ma_malloc(sizeof(*pProxyData), pAllocationCallbacks); /* Will be freed by the proxy entry proc. */ - if (pProxyData == NULL) { - return MA_OUT_OF_MEMORY; - } - -#if defined(MA_THREAD_DEFAULT_STACK_SIZE) - if (stackSize == 0) { - stackSize = MA_THREAD_DEFAULT_STACK_SIZE; - } -#endif - - pProxyData->entryProc = entryProc; - pProxyData->pData = pData; - ma_allocation_callbacks_init_copy(&pProxyData->allocationCallbacks, pAllocationCallbacks); - -#if defined(MA_POSIX) - result = ma_thread_create__posix(pThread, priority, stackSize, ma_thread_entry_proxy, pProxyData); -#elif defined(MA_WIN32) - result = ma_thread_create__win32(pThread, priority, stackSize, ma_thread_entry_proxy, pProxyData); -#endif - - if (result != MA_SUCCESS) { - ma_free(pProxyData, pAllocationCallbacks); - return result; - } - - return MA_SUCCESS; -} - -static void ma_thread_wait(ma_thread* pThread) -{ - if (pThread == NULL) { - return; - } - -#if defined(MA_POSIX) - ma_thread_wait__posix(pThread); -#elif defined(MA_WIN32) - ma_thread_wait__win32(pThread); -#endif -} - - -MA_API ma_result ma_mutex_init(ma_mutex* pMutex) -{ - if (pMutex == NULL) { - MA_ASSERT(MA_FALSE); /* Fire an assert so the caller is aware of this bug. */ - return MA_INVALID_ARGS; - } - -#if defined(MA_POSIX) - return ma_mutex_init__posix(pMutex); -#elif defined(MA_WIN32) - return ma_mutex_init__win32(pMutex); -#endif -} - -MA_API void ma_mutex_uninit(ma_mutex* pMutex) -{ - if (pMutex == NULL) { - return; - } - -#if defined(MA_POSIX) - ma_mutex_uninit__posix(pMutex); -#elif defined(MA_WIN32) - ma_mutex_uninit__win32(pMutex); -#endif -} - -MA_API void ma_mutex_lock(ma_mutex* pMutex) -{ - if (pMutex == NULL) { - MA_ASSERT(MA_FALSE); /* Fire an assert so the caller is aware of this bug. */ - return; - } - -#if defined(MA_POSIX) - ma_mutex_lock__posix(pMutex); -#elif defined(MA_WIN32) - ma_mutex_lock__win32(pMutex); -#endif -} - -MA_API void ma_mutex_unlock(ma_mutex* pMutex) -{ - if (pMutex == NULL) { - MA_ASSERT(MA_FALSE); /* Fire an assert so the caller is aware of this bug. */ - return; - } - -#if defined(MA_POSIX) - ma_mutex_unlock__posix(pMutex); -#elif defined(MA_WIN32) - ma_mutex_unlock__win32(pMutex); -#endif -} - - -MA_API ma_result ma_event_init(ma_event* pEvent) -{ - if (pEvent == NULL) { - MA_ASSERT(MA_FALSE); /* Fire an assert so the caller is aware of this bug. */ - return MA_INVALID_ARGS; - } - -#if defined(MA_POSIX) - return ma_event_init__posix(pEvent); -#elif defined(MA_WIN32) - return ma_event_init__win32(pEvent); -#endif -} - -#if 0 -static ma_result ma_event_alloc_and_init(ma_event** ppEvent, ma_allocation_callbacks* pAllocationCallbacks) -{ - ma_result result; - ma_event* pEvent; - - if (ppEvent == NULL) { - return MA_INVALID_ARGS; - } - - *ppEvent = NULL; - - pEvent = ma_malloc(sizeof(*pEvent), pAllocationCallbacks); - if (pEvent == NULL) { - return MA_OUT_OF_MEMORY; - } - - result = ma_event_init(pEvent); - if (result != MA_SUCCESS) { - ma_free(pEvent, pAllocationCallbacks); - return result; - } - - *ppEvent = pEvent; - return result; -} -#endif - -MA_API void ma_event_uninit(ma_event* pEvent) -{ - if (pEvent == NULL) { - return; - } - -#if defined(MA_POSIX) - ma_event_uninit__posix(pEvent); -#elif defined(MA_WIN32) - ma_event_uninit__win32(pEvent); -#endif -} - -#if 0 -static void ma_event_uninit_and_free(ma_event* pEvent, ma_allocation_callbacks* pAllocationCallbacks) -{ - if (pEvent == NULL) { - return; - } - - ma_event_uninit(pEvent); - ma_free(pEvent, pAllocationCallbacks); -} -#endif - -MA_API ma_result ma_event_wait(ma_event* pEvent) -{ - if (pEvent == NULL) { - MA_ASSERT(MA_FALSE); /* Fire an assert to the caller is aware of this bug. */ - return MA_INVALID_ARGS; - } - -#if defined(MA_POSIX) - return ma_event_wait__posix(pEvent); -#elif defined(MA_WIN32) - return ma_event_wait__win32(pEvent); -#endif -} - -MA_API ma_result ma_event_signal(ma_event* pEvent) -{ - if (pEvent == NULL) { - MA_ASSERT(MA_FALSE); /* Fire an assert to the caller is aware of this bug. */ - return MA_INVALID_ARGS; - } - -#if defined(MA_POSIX) - return ma_event_signal__posix(pEvent); -#elif defined(MA_WIN32) - return ma_event_signal__win32(pEvent); -#endif -} - - -MA_API ma_result ma_semaphore_init(int initialValue, ma_semaphore* pSemaphore) -{ - if (pSemaphore == NULL) { - MA_ASSERT(MA_FALSE); /* Fire an assert so the caller is aware of this bug. */ - return MA_INVALID_ARGS; - } - -#if defined(MA_POSIX) - return ma_semaphore_init__posix(initialValue, pSemaphore); -#elif defined(MA_WIN32) - return ma_semaphore_init__win32(initialValue, pSemaphore); -#endif -} - -MA_API void ma_semaphore_uninit(ma_semaphore* pSemaphore) -{ - if (pSemaphore == NULL) { - MA_ASSERT(MA_FALSE); /* Fire an assert so the caller is aware of this bug. */ - return; - } - -#if defined(MA_POSIX) - ma_semaphore_uninit__posix(pSemaphore); -#elif defined(MA_WIN32) - ma_semaphore_uninit__win32(pSemaphore); -#endif -} - -MA_API ma_result ma_semaphore_wait(ma_semaphore* pSemaphore) -{ - if (pSemaphore == NULL) { - MA_ASSERT(MA_FALSE); /* Fire an assert so the caller is aware of this bug. */ - return MA_INVALID_ARGS; - } - -#if defined(MA_POSIX) - return ma_semaphore_wait__posix(pSemaphore); -#elif defined(MA_WIN32) - return ma_semaphore_wait__win32(pSemaphore); -#endif -} - -MA_API ma_result ma_semaphore_release(ma_semaphore* pSemaphore) -{ - if (pSemaphore == NULL) { - MA_ASSERT(MA_FALSE); /* Fire an assert so the caller is aware of this bug. */ - return MA_INVALID_ARGS; - } - -#if defined(MA_POSIX) - return ma_semaphore_release__posix(pSemaphore); -#elif defined(MA_WIN32) - return ma_semaphore_release__win32(pSemaphore); -#endif -} -#else -/* MA_NO_THREADING is set which means threading is disabled. Threading is required by some API families. If any of these are enabled we need to throw an error. */ -#ifndef MA_NO_DEVICE_IO -#error "MA_NO_THREADING cannot be used without MA_NO_DEVICE_IO"; -#endif -#endif /* MA_NO_THREADING */ - - - -#define MA_FENCE_COUNTER_MAX 0x7FFFFFFF - -MA_API ma_result ma_fence_init(ma_fence* pFence) -{ - if (pFence == NULL) { - return MA_INVALID_ARGS; - } - - MA_ZERO_OBJECT(pFence); - pFence->counter = 0; - - #ifndef MA_NO_THREADING - { - ma_result result; - - result = ma_event_init(&pFence->e); - if (result != MA_SUCCESS) { - return result; - } - } - #endif - - return MA_SUCCESS; -} - -MA_API void ma_fence_uninit(ma_fence* pFence) -{ - if (pFence == NULL) { - return; - } - - #ifndef MA_NO_THREADING - { - ma_event_uninit(&pFence->e); - } - #endif - - MA_ZERO_OBJECT(pFence); -} - -MA_API ma_result ma_fence_acquire(ma_fence* pFence) -{ - if (pFence == NULL) { - return MA_INVALID_ARGS; - } - - for (;;) { - ma_uint32 oldCounter = ma_atomic_load_32(&pFence->counter); - ma_uint32 newCounter = oldCounter + 1; - - /* Make sure we're not about to exceed our maximum value. */ - if (newCounter > MA_FENCE_COUNTER_MAX) { - MA_ASSERT(MA_FALSE); - return MA_OUT_OF_RANGE; - } - - if (ma_atomic_compare_exchange_weak_32(&pFence->counter, &oldCounter, newCounter)) { - return MA_SUCCESS; - } else { - if (oldCounter == MA_FENCE_COUNTER_MAX) { - MA_ASSERT(MA_FALSE); - return MA_OUT_OF_RANGE; /* The other thread took the last available slot. Abort. */ - } - } - } - - /* Should never get here. */ - /*return MA_SUCCESS;*/ -} - -MA_API ma_result ma_fence_release(ma_fence* pFence) -{ - if (pFence == NULL) { - return MA_INVALID_ARGS; - } - - for (;;) { - ma_uint32 oldCounter = ma_atomic_load_32(&pFence->counter); - ma_uint32 newCounter = oldCounter - 1; - - if (oldCounter == 0) { - MA_ASSERT(MA_FALSE); - return MA_INVALID_OPERATION; /* Acquire/release mismatch. */ - } - - if (ma_atomic_compare_exchange_weak_32(&pFence->counter, &oldCounter, newCounter)) { - #ifndef MA_NO_THREADING - { - if (newCounter == 0) { - ma_event_signal(&pFence->e); /* <-- ma_fence_wait() will be waiting on this. */ - } - } - #endif - - return MA_SUCCESS; - } else { - if (oldCounter == 0) { - MA_ASSERT(MA_FALSE); - return MA_INVALID_OPERATION; /* Another thread has taken the 0 slot. Acquire/release mismatch. */ - } - } - } - - /* Should never get here. */ - /*return MA_SUCCESS;*/ -} - -MA_API ma_result ma_fence_wait(ma_fence* pFence) -{ - if (pFence == NULL) { - return MA_INVALID_ARGS; - } - - for (;;) { - ma_uint32 counter; - - counter = ma_atomic_load_32(&pFence->counter); - if (counter == 0) { - /* - Counter has hit zero. By the time we get here some other thread may have acquired the - fence again, but that is where the caller needs to take care with how they se the fence. - */ - return MA_SUCCESS; - } - - /* Getting here means the counter is > 0. We'll need to wait for something to happen. */ - #ifndef MA_NO_THREADING - { - ma_result result; - - result = ma_event_wait(&pFence->e); - if (result != MA_SUCCESS) { - return result; - } - } - #endif - } - - /* Should never get here. */ - /*return MA_INVALID_OPERATION;*/ -} - - -MA_API ma_result ma_async_notification_signal(ma_async_notification* pNotification) -{ - ma_async_notification_callbacks* pNotificationCallbacks = (ma_async_notification_callbacks*)pNotification; - - if (pNotification == NULL) { - return MA_INVALID_ARGS; - } - - if (pNotificationCallbacks->onSignal == NULL) { - return MA_NOT_IMPLEMENTED; - } - - pNotificationCallbacks->onSignal(pNotification); - return MA_INVALID_ARGS; -} - - -static void ma_async_notification_poll__on_signal(ma_async_notification* pNotification) -{ - ((ma_async_notification_poll*)pNotification)->signalled = MA_TRUE; -} - -MA_API ma_result ma_async_notification_poll_init(ma_async_notification_poll* pNotificationPoll) -{ - if (pNotificationPoll == NULL) { - return MA_INVALID_ARGS; - } - - pNotificationPoll->cb.onSignal = ma_async_notification_poll__on_signal; - pNotificationPoll->signalled = MA_FALSE; - - return MA_SUCCESS; -} - -MA_API ma_bool32 ma_async_notification_poll_is_signalled(const ma_async_notification_poll* pNotificationPoll) -{ - if (pNotificationPoll == NULL) { - return MA_FALSE; - } - - return pNotificationPoll->signalled; -} - - -static void ma_async_notification_event__on_signal(ma_async_notification* pNotification) -{ - ma_async_notification_event_signal((ma_async_notification_event*)pNotification); -} - -MA_API ma_result ma_async_notification_event_init(ma_async_notification_event* pNotificationEvent) -{ - if (pNotificationEvent == NULL) { - return MA_INVALID_ARGS; - } - - pNotificationEvent->cb.onSignal = ma_async_notification_event__on_signal; - - #ifndef MA_NO_THREADING - { - ma_result result; - - result = ma_event_init(&pNotificationEvent->e); - if (result != MA_SUCCESS) { - return result; - } - - return MA_SUCCESS; - } - #else - { - return MA_NOT_IMPLEMENTED; /* Threading is disabled. */ - } - #endif -} - -MA_API ma_result ma_async_notification_event_uninit(ma_async_notification_event* pNotificationEvent) -{ - if (pNotificationEvent == NULL) { - return MA_INVALID_ARGS; - } - - #ifndef MA_NO_THREADING - { - ma_event_uninit(&pNotificationEvent->e); - return MA_SUCCESS; - } - #else - { - return MA_NOT_IMPLEMENTED; /* Threading is disabled. */ - } - #endif -} - -MA_API ma_result ma_async_notification_event_wait(ma_async_notification_event* pNotificationEvent) -{ - if (pNotificationEvent == NULL) { - return MA_INVALID_ARGS; - } - - #ifndef MA_NO_THREADING - { - return ma_event_wait(&pNotificationEvent->e); - } - #else - { - return MA_NOT_IMPLEMENTED; /* Threading is disabled. */ - } - #endif -} - -MA_API ma_result ma_async_notification_event_signal(ma_async_notification_event* pNotificationEvent) -{ - if (pNotificationEvent == NULL) { - return MA_INVALID_ARGS; - } - - #ifndef MA_NO_THREADING - { - return ma_event_signal(&pNotificationEvent->e); - } - #else - { - return MA_NOT_IMPLEMENTED; /* Threading is disabled. */ - } - #endif -} - - - -/************************************************************************************************************************************************************ - -Job Queue - -************************************************************************************************************************************************************/ -MA_API ma_slot_allocator_config ma_slot_allocator_config_init(ma_uint32 capacity) -{ - ma_slot_allocator_config config; - - MA_ZERO_OBJECT(&config); - config.capacity = capacity; - - return config; -} - - -static MA_INLINE ma_uint32 ma_slot_allocator_calculate_group_capacity(ma_uint32 slotCapacity) -{ - ma_uint32 cap = slotCapacity / 32; - if ((slotCapacity % 32) != 0) { - cap += 1; - } - - return cap; -} - -static MA_INLINE ma_uint32 ma_slot_allocator_group_capacity(const ma_slot_allocator* pAllocator) -{ - return ma_slot_allocator_calculate_group_capacity(pAllocator->capacity); -} - - -typedef struct -{ - size_t sizeInBytes; - size_t groupsOffset; - size_t slotsOffset; -} ma_slot_allocator_heap_layout; - -static ma_result ma_slot_allocator_get_heap_layout(const ma_slot_allocator_config* pConfig, ma_slot_allocator_heap_layout* pHeapLayout) -{ - MA_ASSERT(pHeapLayout != NULL); - - MA_ZERO_OBJECT(pHeapLayout); - - if (pConfig == NULL) { - return MA_INVALID_ARGS; - } - - if (pConfig->capacity == 0) { - return MA_INVALID_ARGS; - } - - pHeapLayout->sizeInBytes = 0; - - /* Groups. */ - pHeapLayout->groupsOffset = pHeapLayout->sizeInBytes; - pHeapLayout->sizeInBytes += ma_align_64(ma_slot_allocator_calculate_group_capacity(pConfig->capacity) * sizeof(ma_slot_allocator_group)); - - /* Slots. */ - pHeapLayout->slotsOffset = pHeapLayout->sizeInBytes; - pHeapLayout->sizeInBytes += ma_align_64(pConfig->capacity * sizeof(ma_uint32)); - - return MA_SUCCESS; -} - -MA_API ma_result ma_slot_allocator_get_heap_size(const ma_slot_allocator_config* pConfig, size_t* pHeapSizeInBytes) -{ - ma_result result; - ma_slot_allocator_heap_layout layout; - - if (pHeapSizeInBytes == NULL) { - return MA_INVALID_ARGS; - } - - *pHeapSizeInBytes = 0; - - result = ma_slot_allocator_get_heap_layout(pConfig, &layout); - if (result != MA_SUCCESS) { - return result; - } - - *pHeapSizeInBytes = layout.sizeInBytes; - - return result; -} - -MA_API ma_result ma_slot_allocator_init_preallocated(const ma_slot_allocator_config* pConfig, void* pHeap, ma_slot_allocator* pAllocator) -{ - ma_result result; - ma_slot_allocator_heap_layout heapLayout; - - if (pAllocator == NULL) { - return MA_INVALID_ARGS; - } - - MA_ZERO_OBJECT(pAllocator); - - if (pHeap == NULL) { - return MA_INVALID_ARGS; - } - - result = ma_slot_allocator_get_heap_layout(pConfig, &heapLayout); - if (result != MA_SUCCESS) { - return result; - } - - pAllocator->_pHeap = pHeap; - MA_ZERO_MEMORY(pHeap, heapLayout.sizeInBytes); - - pAllocator->pGroups = (ma_slot_allocator_group*)ma_offset_ptr(pHeap, heapLayout.groupsOffset); - pAllocator->pSlots = (ma_uint32*)ma_offset_ptr(pHeap, heapLayout.slotsOffset); - pAllocator->capacity = pConfig->capacity; - - return MA_SUCCESS; -} - -MA_API ma_result ma_slot_allocator_init(const ma_slot_allocator_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_slot_allocator* pAllocator) -{ - ma_result result; - size_t heapSizeInBytes; - void* pHeap; - - result = ma_slot_allocator_get_heap_size(pConfig, &heapSizeInBytes); - if (result != MA_SUCCESS) { - return result; /* Failed to retrieve the size of the heap allocation. */ - } - - if (heapSizeInBytes > 0) { - pHeap = ma_malloc(heapSizeInBytes, pAllocationCallbacks); - if (pHeap == NULL) { - return MA_OUT_OF_MEMORY; - } - } else { - pHeap = NULL; - } - - result = ma_slot_allocator_init_preallocated(pConfig, pHeap, pAllocator); - if (result != MA_SUCCESS) { - ma_free(pHeap, pAllocationCallbacks); - return result; - } - - pAllocator->_ownsHeap = MA_TRUE; - return MA_SUCCESS; -} - -MA_API void ma_slot_allocator_uninit(ma_slot_allocator* pAllocator, const ma_allocation_callbacks* pAllocationCallbacks) -{ - if (pAllocator == NULL) { - return; - } - - if (pAllocator->_ownsHeap) { - ma_free(pAllocator->_pHeap, pAllocationCallbacks); - } -} - -MA_API ma_result ma_slot_allocator_alloc(ma_slot_allocator* pAllocator, ma_uint64* pSlot) -{ - ma_uint32 iAttempt; - const ma_uint32 maxAttempts = 2; /* The number of iterations to perform until returning MA_OUT_OF_MEMORY if no slots can be found. */ - - if (pAllocator == NULL || pSlot == NULL) { - return MA_INVALID_ARGS; - } - - for (iAttempt = 0; iAttempt < maxAttempts; iAttempt += 1) { - /* We need to acquire a suitable bitfield first. This is a bitfield that's got an available slot within it. */ - ma_uint32 iGroup; - for (iGroup = 0; iGroup < ma_slot_allocator_group_capacity(pAllocator); iGroup += 1) { - /* CAS */ - for (;;) { - ma_uint32 oldBitfield; - ma_uint32 newBitfield; - ma_uint32 bitOffset; - - oldBitfield = ma_atomic_load_32(&pAllocator->pGroups[iGroup].bitfield); /* <-- This copy must happen. The compiler must not optimize this away. */ - - /* Fast check to see if anything is available. */ - if (oldBitfield == 0xFFFFFFFF) { - break; /* No available bits in this bitfield. */ - } - - bitOffset = ma_ffs_32(~oldBitfield); - MA_ASSERT(bitOffset < 32); - - newBitfield = oldBitfield | (1 << bitOffset); - - if (ma_atomic_compare_and_swap_32(&pAllocator->pGroups[iGroup].bitfield, oldBitfield, newBitfield) == oldBitfield) { - ma_uint32 slotIndex; - - /* Increment the counter as soon as possible to have other threads report out-of-memory sooner than later. */ - ma_atomic_fetch_add_32(&pAllocator->count, 1); - - /* The slot index is required for constructing the output value. */ - slotIndex = (iGroup << 5) + bitOffset; /* iGroup << 5 = iGroup * 32 */ - if (slotIndex >= pAllocator->capacity) { - return MA_OUT_OF_MEMORY; - } - - /* Increment the reference count before constructing the output value. */ - pAllocator->pSlots[slotIndex] += 1; - - /* Construct the output value. */ - *pSlot = (((ma_uint64)pAllocator->pSlots[slotIndex] << 32) | slotIndex); - - return MA_SUCCESS; - } - } - } - - /* We weren't able to find a slot. If it's because we've reached our capacity we need to return MA_OUT_OF_MEMORY. Otherwise we need to do another iteration and try again. */ - if (pAllocator->count < pAllocator->capacity) { - ma_yield(); - } else { - return MA_OUT_OF_MEMORY; - } - } - - /* We couldn't find a slot within the maximum number of attempts. */ - return MA_OUT_OF_MEMORY; -} - -MA_API ma_result ma_slot_allocator_free(ma_slot_allocator* pAllocator, ma_uint64 slot) -{ - ma_uint32 iGroup; - ma_uint32 iBit; - - if (pAllocator == NULL) { - return MA_INVALID_ARGS; - } - - iGroup = (ma_uint32)((slot & 0xFFFFFFFF) >> 5); /* slot / 32 */ - iBit = (ma_uint32)((slot & 0xFFFFFFFF) & 31); /* slot % 32 */ - - if (iGroup >= ma_slot_allocator_group_capacity(pAllocator)) { - return MA_INVALID_ARGS; - } - - MA_ASSERT(iBit < 32); /* This must be true due to the logic we used to actually calculate it. */ - - while (ma_atomic_load_32(&pAllocator->count) > 0) { - /* CAS */ - ma_uint32 oldBitfield; - ma_uint32 newBitfield; - - oldBitfield = ma_atomic_load_32(&pAllocator->pGroups[iGroup].bitfield); /* <-- This copy must happen. The compiler must not optimize this away. */ - newBitfield = oldBitfield & ~(1 << iBit); - - /* Debugging for checking for double-frees. */ - #if defined(MA_DEBUG_OUTPUT) - { - if ((oldBitfield & (1 << iBit)) == 0) { - MA_ASSERT(MA_FALSE); /* Double free detected.*/ - } - } - #endif - - if (ma_atomic_compare_and_swap_32(&pAllocator->pGroups[iGroup].bitfield, oldBitfield, newBitfield) == oldBitfield) { - ma_atomic_fetch_sub_32(&pAllocator->count, 1); - return MA_SUCCESS; - } - } - - /* Getting here means there are no allocations available for freeing. */ - return MA_INVALID_OPERATION; -} - - -#define MA_JOB_ID_NONE ~((ma_uint64)0) -#define MA_JOB_SLOT_NONE (ma_uint16)(~0) - -static MA_INLINE ma_uint32 ma_job_extract_refcount(ma_uint64 toc) -{ - return (ma_uint32)(toc >> 32); -} - -static MA_INLINE ma_uint16 ma_job_extract_slot(ma_uint64 toc) -{ - return (ma_uint16)(toc & 0x0000FFFF); -} - -static MA_INLINE ma_uint16 ma_job_extract_code(ma_uint64 toc) -{ - return (ma_uint16)((toc & 0xFFFF0000) >> 16); -} - -static MA_INLINE ma_uint64 ma_job_toc_to_allocation(ma_uint64 toc) -{ - return ((ma_uint64)ma_job_extract_refcount(toc) << 32) | (ma_uint64)ma_job_extract_slot(toc); -} - -static MA_INLINE ma_uint64 ma_job_set_refcount(ma_uint64 toc, ma_uint32 refcount) -{ - /* Clear the reference count first. */ - toc = toc & ~((ma_uint64)0xFFFFFFFF << 32); - toc = toc | ((ma_uint64)refcount << 32); - - return toc; -} - - -MA_API ma_job ma_job_init(ma_uint16 code) -{ - ma_job job; - - MA_ZERO_OBJECT(&job); - job.toc.breakup.code = code; - job.toc.breakup.slot = MA_JOB_SLOT_NONE; /* Temp value. Will be allocated when posted to a queue. */ - job.next = MA_JOB_ID_NONE; - - return job; -} - - -static ma_result ma_job_process__noop(ma_job* pJob); -static ma_result ma_job_process__quit(ma_job* pJob); -static ma_result ma_job_process__custom(ma_job* pJob); -static ma_result ma_job_process__resource_manager__load_data_buffer_node(ma_job* pJob); -static ma_result ma_job_process__resource_manager__free_data_buffer_node(ma_job* pJob); -static ma_result ma_job_process__resource_manager__page_data_buffer_node(ma_job* pJob); -static ma_result ma_job_process__resource_manager__load_data_buffer(ma_job* pJob); -static ma_result ma_job_process__resource_manager__free_data_buffer(ma_job* pJob); -static ma_result ma_job_process__resource_manager__load_data_stream(ma_job* pJob); -static ma_result ma_job_process__resource_manager__free_data_stream(ma_job* pJob); -static ma_result ma_job_process__resource_manager__page_data_stream(ma_job* pJob); -static ma_result ma_job_process__resource_manager__seek_data_stream(ma_job* pJob); - -#if !defined(MA_NO_DEVICE_IO) -static ma_result ma_job_process__device__aaudio_reroute(ma_job* pJob); -#endif - -static ma_job_proc g_jobVTable[MA_JOB_TYPE_COUNT] = -{ - /* Miscellaneous. */ - ma_job_process__quit, /* MA_JOB_TYPE_QUIT */ - ma_job_process__custom, /* MA_JOB_TYPE_CUSTOM */ - - /* Resource Manager. */ - ma_job_process__resource_manager__load_data_buffer_node, /* MA_JOB_TYPE_RESOURCE_MANAGER_LOAD_DATA_BUFFER_NODE */ - ma_job_process__resource_manager__free_data_buffer_node, /* MA_JOB_TYPE_RESOURCE_MANAGER_FREE_DATA_BUFFER_NODE */ - ma_job_process__resource_manager__page_data_buffer_node, /* MA_JOB_TYPE_RESOURCE_MANAGER_PAGE_DATA_BUFFER_NODE */ - ma_job_process__resource_manager__load_data_buffer, /* MA_JOB_TYPE_RESOURCE_MANAGER_LOAD_DATA_BUFFER */ - ma_job_process__resource_manager__free_data_buffer, /* MA_JOB_TYPE_RESOURCE_MANAGER_FREE_DATA_BUFFER */ - ma_job_process__resource_manager__load_data_stream, /* MA_JOB_TYPE_RESOURCE_MANAGER_LOAD_DATA_STREAM */ - ma_job_process__resource_manager__free_data_stream, /* MA_JOB_TYPE_RESOURCE_MANAGER_FREE_DATA_STREAM */ - ma_job_process__resource_manager__page_data_stream, /* MA_JOB_TYPE_RESOURCE_MANAGER_PAGE_DATA_STREAM */ - ma_job_process__resource_manager__seek_data_stream, /* MA_JOB_TYPE_RESOURCE_MANAGER_SEEK_DATA_STREAM */ - - /* Device. */ -#if !defined(MA_NO_DEVICE_IO) - ma_job_process__device__aaudio_reroute /* MA_JOB_TYPE_DEVICE_AAUDIO_REROUTE */ -#endif -}; - -MA_API ma_result ma_job_process(ma_job* pJob) -{ - if (pJob == NULL) { - return MA_INVALID_ARGS; - } - - if (pJob->toc.breakup.code >= MA_JOB_TYPE_COUNT) { - return MA_INVALID_OPERATION; - } - - return g_jobVTable[pJob->toc.breakup.code](pJob); -} - -static ma_result ma_job_process__noop(ma_job* pJob) -{ - MA_ASSERT(pJob != NULL); - - /* No-op. */ - (void)pJob; - - return MA_SUCCESS; -} - -static ma_result ma_job_process__quit(ma_job* pJob) -{ - return ma_job_process__noop(pJob); -} - -static ma_result ma_job_process__custom(ma_job* pJob) -{ - MA_ASSERT(pJob != NULL); - - /* No-op if there's no callback. */ - if (pJob->data.custom.proc == NULL) { - return MA_SUCCESS; - } - - return pJob->data.custom.proc(pJob); -} - - - -MA_API ma_job_queue_config ma_job_queue_config_init(ma_uint32 flags, ma_uint32 capacity) -{ - ma_job_queue_config config; - - config.flags = flags; - config.capacity = capacity; - - return config; -} - - -typedef struct -{ - size_t sizeInBytes; - size_t allocatorOffset; - size_t jobsOffset; -} ma_job_queue_heap_layout; - -static ma_result ma_job_queue_get_heap_layout(const ma_job_queue_config* pConfig, ma_job_queue_heap_layout* pHeapLayout) -{ - ma_result result; - - MA_ASSERT(pHeapLayout != NULL); - - MA_ZERO_OBJECT(pHeapLayout); - - if (pConfig == NULL) { - return MA_INVALID_ARGS; - } - - if (pConfig->capacity == 0) { - return MA_INVALID_ARGS; - } - - pHeapLayout->sizeInBytes = 0; - - /* Allocator. */ - { - ma_slot_allocator_config allocatorConfig; - size_t allocatorHeapSizeInBytes; - - allocatorConfig = ma_slot_allocator_config_init(pConfig->capacity); - result = ma_slot_allocator_get_heap_size(&allocatorConfig, &allocatorHeapSizeInBytes); - if (result != MA_SUCCESS) { - return result; - } - - pHeapLayout->allocatorOffset = pHeapLayout->sizeInBytes; - pHeapLayout->sizeInBytes += allocatorHeapSizeInBytes; - } - - /* Jobs. */ - pHeapLayout->jobsOffset = pHeapLayout->sizeInBytes; - pHeapLayout->sizeInBytes += ma_align_64(pConfig->capacity * sizeof(ma_job)); - - return MA_SUCCESS; -} - -MA_API ma_result ma_job_queue_get_heap_size(const ma_job_queue_config* pConfig, size_t* pHeapSizeInBytes) -{ - ma_result result; - ma_job_queue_heap_layout layout; - - if (pHeapSizeInBytes == NULL) { - return MA_INVALID_ARGS; - } - - *pHeapSizeInBytes = 0; - - result = ma_job_queue_get_heap_layout(pConfig, &layout); - if (result != MA_SUCCESS) { - return result; - } - - *pHeapSizeInBytes = layout.sizeInBytes; - - return MA_SUCCESS; -} - -MA_API ma_result ma_job_queue_init_preallocated(const ma_job_queue_config* pConfig, void* pHeap, ma_job_queue* pQueue) -{ - ma_result result; - ma_job_queue_heap_layout heapLayout; - ma_slot_allocator_config allocatorConfig; - - if (pQueue == NULL) { - return MA_INVALID_ARGS; - } - - MA_ZERO_OBJECT(pQueue); - - result = ma_job_queue_get_heap_layout(pConfig, &heapLayout); - if (result != MA_SUCCESS) { - return result; - } - - pQueue->_pHeap = pHeap; - MA_ZERO_MEMORY(pHeap, heapLayout.sizeInBytes); - - pQueue->flags = pConfig->flags; - pQueue->capacity = pConfig->capacity; - pQueue->pJobs = (ma_job*)ma_offset_ptr(pHeap, heapLayout.jobsOffset); - - allocatorConfig = ma_slot_allocator_config_init(pConfig->capacity); - result = ma_slot_allocator_init_preallocated(&allocatorConfig, ma_offset_ptr(pHeap, heapLayout.allocatorOffset), &pQueue->allocator); - if (result != MA_SUCCESS) { - return result; - } - - /* We need a semaphore if we're running in non-blocking mode. If threading is disabled we need to return an error. */ - if ((pQueue->flags & MA_JOB_QUEUE_FLAG_NON_BLOCKING) == 0) { - #ifndef MA_NO_THREADING - { - ma_semaphore_init(0, &pQueue->sem); - } - #else - { - /* Threading is disabled and we've requested non-blocking mode. */ - return MA_INVALID_OPERATION; - } - #endif - } - - /* - Our queue needs to be initialized with a free standing node. This should always be slot 0. Required for the lock free algorithm. The first job in the queue is - just a dummy item for giving us the first item in the list which is stored in the "next" member. - */ - ma_slot_allocator_alloc(&pQueue->allocator, &pQueue->head); /* Will never fail. */ - pQueue->pJobs[ma_job_extract_slot(pQueue->head)].next = MA_JOB_ID_NONE; - pQueue->tail = pQueue->head; - - return MA_SUCCESS; -} - -MA_API ma_result ma_job_queue_init(const ma_job_queue_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_job_queue* pQueue) -{ - ma_result result; - size_t heapSizeInBytes; - void* pHeap; - - result = ma_job_queue_get_heap_size(pConfig, &heapSizeInBytes); - if (result != MA_SUCCESS) { - return result; - } - - if (heapSizeInBytes > 0) { - pHeap = ma_malloc(heapSizeInBytes, pAllocationCallbacks); - if (pHeap == NULL) { - return MA_OUT_OF_MEMORY; - } - } else { - pHeap = NULL; - } - - result = ma_job_queue_init_preallocated(pConfig, pHeap, pQueue); - if (result != MA_SUCCESS) { - ma_free(pHeap, pAllocationCallbacks); - return result; - } - - pQueue->_ownsHeap = MA_TRUE; - return MA_SUCCESS; -} - -MA_API void ma_job_queue_uninit(ma_job_queue* pQueue, const ma_allocation_callbacks* pAllocationCallbacks) -{ - if (pQueue == NULL) { - return; - } - - /* All we need to do is uninitialize the semaphore. */ - if ((pQueue->flags & MA_JOB_QUEUE_FLAG_NON_BLOCKING) == 0) { - #ifndef MA_NO_THREADING - { - ma_semaphore_uninit(&pQueue->sem); - } - #else - { - MA_ASSERT(MA_FALSE); /* Should never get here. Should have been checked at initialization time. */ - } - #endif - } - - ma_slot_allocator_uninit(&pQueue->allocator, pAllocationCallbacks); - - if (pQueue->_ownsHeap) { - ma_free(pQueue->_pHeap, pAllocationCallbacks); - } -} - -static ma_bool32 ma_job_queue_cas(volatile ma_uint64* dst, ma_uint64 expected, ma_uint64 desired) -{ - /* The new counter is taken from the expected value. */ - return ma_atomic_compare_and_swap_64(dst, expected, ma_job_set_refcount(desired, ma_job_extract_refcount(expected) + 1)) == expected; -} - -MA_API ma_result ma_job_queue_post(ma_job_queue* pQueue, const ma_job* pJob) -{ - /* - Lock free queue implementation based on the paper by Michael and Scott: Nonblocking Algorithms and Preemption-Safe Locking on Multiprogrammed Shared Memory Multiprocessors - */ - ma_result result; - ma_uint64 slot; - ma_uint64 tail; - ma_uint64 next; - - if (pQueue == NULL || pJob == NULL) { - return MA_INVALID_ARGS; - } - - /* We need a new slot. */ - result = ma_slot_allocator_alloc(&pQueue->allocator, &slot); - if (result != MA_SUCCESS) { - return result; /* Probably ran out of slots. If so, MA_OUT_OF_MEMORY will be returned. */ - } - - /* At this point we should have a slot to place the job. */ - MA_ASSERT(ma_job_extract_slot(slot) < pQueue->capacity); - - /* We need to put the job into memory before we do anything. */ - pQueue->pJobs[ma_job_extract_slot(slot)] = *pJob; - pQueue->pJobs[ma_job_extract_slot(slot)].toc.allocation = slot; /* This will overwrite the job code. */ - pQueue->pJobs[ma_job_extract_slot(slot)].toc.breakup.code = pJob->toc.breakup.code; /* The job code needs to be applied again because the line above overwrote it. */ - pQueue->pJobs[ma_job_extract_slot(slot)].next = MA_JOB_ID_NONE; /* Reset for safety. */ - - #ifndef MA_USE_EXPERIMENTAL_LOCK_FREE_JOB_QUEUE - ma_spinlock_lock(&pQueue->lock); - #endif - { - /* The job is stored in memory so now we need to add it to our linked list. We only ever add items to the end of the list. */ - for (;;) { - tail = ma_atomic_load_64(&pQueue->tail); - next = ma_atomic_load_64(&pQueue->pJobs[ma_job_extract_slot(tail)].next); - - if (ma_job_toc_to_allocation(tail) == ma_job_toc_to_allocation(ma_atomic_load_64(&pQueue->tail))) { - if (ma_job_extract_slot(next) == 0xFFFF) { - if (ma_job_queue_cas(&pQueue->pJobs[ma_job_extract_slot(tail)].next, next, slot)) { - break; - } - } else { - ma_job_queue_cas(&pQueue->tail, tail, ma_job_extract_slot(next)); - } - } - } - ma_job_queue_cas(&pQueue->tail, tail, slot); - } - #ifndef MA_USE_EXPERIMENTAL_LOCK_FREE_JOB_QUEUE - ma_spinlock_unlock(&pQueue->lock); - #endif - - - /* Signal the semaphore as the last step if we're using synchronous mode. */ - if ((pQueue->flags & MA_JOB_QUEUE_FLAG_NON_BLOCKING) == 0) { - #ifndef MA_NO_THREADING - { - ma_semaphore_release(&pQueue->sem); - } - #else - { - MA_ASSERT(MA_FALSE); /* Should never get here. Should have been checked at initialization time. */ - } - #endif - } - - return MA_SUCCESS; -} - -MA_API ma_result ma_job_queue_next(ma_job_queue* pQueue, ma_job* pJob) -{ - ma_uint64 head; - ma_uint64 tail; - ma_uint64 next; - - if (pQueue == NULL || pJob == NULL) { - return MA_INVALID_ARGS; - } - - /* If we're running in synchronous mode we'll need to wait on a semaphore. */ - if ((pQueue->flags & MA_JOB_QUEUE_FLAG_NON_BLOCKING) == 0) { - #ifndef MA_NO_THREADING - { - ma_semaphore_wait(&pQueue->sem); - } - #else - { - MA_ASSERT(MA_FALSE); /* Should never get here. Should have been checked at initialization time. */ - } - #endif - } - - #ifndef MA_USE_EXPERIMENTAL_LOCK_FREE_JOB_QUEUE - ma_spinlock_lock(&pQueue->lock); - #endif - { - /* - BUG: In lock-free mode, multiple threads can be in this section of code. The "head" variable in the loop below - is stored. One thread can fall through to the freeing of this item while another is still using "head" for the - retrieval of the "next" variable. - - The slot allocator might need to make use of some reference counting to ensure it's only truly freed when - there are no more references to the item. This must be fixed before removing these locks. - */ - - /* Now we need to remove the root item from the list. */ - for (;;) { - head = ma_atomic_load_64(&pQueue->head); - tail = ma_atomic_load_64(&pQueue->tail); - next = ma_atomic_load_64(&pQueue->pJobs[ma_job_extract_slot(head)].next); - - if (ma_job_toc_to_allocation(head) == ma_job_toc_to_allocation(ma_atomic_load_64(&pQueue->head))) { - if (ma_job_extract_slot(head) == ma_job_extract_slot(tail)) { - if (ma_job_extract_slot(next) == 0xFFFF) { - #ifndef MA_USE_EXPERIMENTAL_LOCK_FREE_JOB_QUEUE - ma_spinlock_unlock(&pQueue->lock); - #endif - return MA_NO_DATA_AVAILABLE; - } - ma_job_queue_cas(&pQueue->tail, tail, ma_job_extract_slot(next)); - } else { - *pJob = pQueue->pJobs[ma_job_extract_slot(next)]; - if (ma_job_queue_cas(&pQueue->head, head, ma_job_extract_slot(next))) { - break; - } - } - } - } - } - #ifndef MA_USE_EXPERIMENTAL_LOCK_FREE_JOB_QUEUE - ma_spinlock_unlock(&pQueue->lock); - #endif - - ma_slot_allocator_free(&pQueue->allocator, head); - - /* - If it's a quit job make sure it's put back on the queue to ensure other threads have an opportunity to detect it and terminate naturally. We - could instead just leave it on the queue, but that would involve fiddling with the lock-free code above and I want to keep that as simple as - possible. - */ - if (pJob->toc.breakup.code == MA_JOB_TYPE_QUIT) { - ma_job_queue_post(pQueue, pJob); - return MA_CANCELLED; /* Return a cancelled status just in case the thread is checking return codes and not properly checking for a quit job. */ - } - - return MA_SUCCESS; -} - - - -/******************************************************************************* - -Dynamic Linking - -*******************************************************************************/ -#ifdef MA_POSIX - /* No need for dlfcn.h if we're not using runtime linking. */ - #ifndef MA_NO_RUNTIME_LINKING - #include - #endif -#endif - -MA_API ma_handle ma_dlopen(ma_log* pLog, const char* filename) -{ -#ifndef MA_NO_RUNTIME_LINKING - ma_handle handle; - - ma_log_postf(pLog, MA_LOG_LEVEL_DEBUG, "Loading library: %s\n", filename); - - #ifdef MA_WIN32 - /* From MSDN: Desktop applications cannot use LoadPackagedLibrary; if a desktop application calls this function it fails with APPMODEL_ERROR_NO_PACKAGE.*/ - #if !defined(MA_WIN32_UWP) || !(defined(WINAPI_FAMILY) && ((defined(WINAPI_FAMILY_PHONE_APP) && WINAPI_FAMILY == WINAPI_FAMILY_PHONE_APP))) - handle = (ma_handle)LoadLibraryA(filename); - #else - /* *sigh* It appears there is no ANSI version of LoadPackagedLibrary()... */ - WCHAR filenameW[4096]; - if (MultiByteToWideChar(CP_UTF8, 0, filename, -1, filenameW, sizeof(filenameW)) == 0) { - handle = NULL; - } else { - handle = (ma_handle)LoadPackagedLibrary(filenameW, 0); - } - #endif - #else - handle = (ma_handle)dlopen(filename, RTLD_NOW); - #endif - - /* - I'm not considering failure to load a library an error nor a warning because seamlessly falling through to a lower-priority - backend is a deliberate design choice. Instead I'm logging it as an informational message. - */ - if (handle == NULL) { - ma_log_postf(pLog, MA_LOG_LEVEL_INFO, "Failed to load library: %s\n", filename); - } - - return handle; -#else - /* Runtime linking is disabled. */ - (void)pLog; - (void)filename; - return NULL; -#endif -} - -MA_API void ma_dlclose(ma_log* pLog, ma_handle handle) -{ -#ifndef MA_NO_RUNTIME_LINKING - #ifdef MA_WIN32 - FreeLibrary((HMODULE)handle); - #else - /* Hack for Android bug (see https://github.com/android/ndk/issues/360). Calling dlclose() pre-API 28 may segfault. */ - #if !defined(MA_ANDROID) || (defined(__ANDROID_API__) && __ANDROID_API__ >= 28) - { - dlclose((void*)handle); - } - #else - { - (void)handle; - } - #endif - #endif - - (void)pLog; -#else - /* Runtime linking is disabled. */ - (void)pLog; - (void)handle; -#endif -} - -MA_API ma_proc ma_dlsym(ma_log* pLog, ma_handle handle, const char* symbol) -{ -#ifndef MA_NO_RUNTIME_LINKING - ma_proc proc; - - ma_log_postf(pLog, MA_LOG_LEVEL_DEBUG, "Loading symbol: %s\n", symbol); - -#ifdef _WIN32 - proc = (ma_proc)GetProcAddress((HMODULE)handle, symbol); -#else -#if (defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 8))) || defined(__clang__) - #pragma GCC diagnostic push - #pragma GCC diagnostic ignored "-Wpedantic" -#endif - proc = (ma_proc)dlsym((void*)handle, symbol); -#if (defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 8))) || defined(__clang__) - #pragma GCC diagnostic pop -#endif -#endif - - if (proc == NULL) { - ma_log_postf(pLog, MA_LOG_LEVEL_WARNING, "Failed to load symbol: %s\n", symbol); - } - - (void)pLog; /* It's possible for pContext to be unused. */ - return proc; -#else - /* Runtime linking is disabled. */ - (void)pLog; - (void)handle; - (void)symbol; - return NULL; -#endif -} - - - -/************************************************************************************************************************************************************ -************************************************************************************************************************************************************* - -DEVICE I/O -========== - -************************************************************************************************************************************************************* -************************************************************************************************************************************************************/ - -/* Disable run-time linking on certain backends and platforms. */ -#ifndef MA_NO_RUNTIME_LINKING - #if defined(MA_EMSCRIPTEN) || defined(MA_ORBIS) || defined(MA_PROSPERO) - #define MA_NO_RUNTIME_LINKING - #endif -#endif - -#ifdef MA_APPLE - #include -#endif - -#ifndef MA_NO_DEVICE_IO - -#if defined(MA_APPLE) && (MAC_OS_X_VERSION_MIN_REQUIRED < 101200) - #include /* For mach_absolute_time() */ -#endif - -#ifdef MA_POSIX - #include - #include - - /* No need for dlfcn.h if we're not using runtime linking. */ - #ifndef MA_NO_RUNTIME_LINKING - #include - #endif -#endif - -/* This must be set to at least 26. */ -#ifndef MA_AAUDIO_MIN_ANDROID_SDK_VERSION -#define MA_AAUDIO_MIN_ANDROID_SDK_VERSION 27 -#endif - - -MA_API void ma_device_info_add_native_data_format(ma_device_info* pDeviceInfo, ma_format format, ma_uint32 channels, ma_uint32 sampleRate, ma_uint32 flags) -{ - if (pDeviceInfo == NULL) { - return; - } - - if (pDeviceInfo->nativeDataFormatCount < ma_countof(pDeviceInfo->nativeDataFormats)) { - pDeviceInfo->nativeDataFormats[pDeviceInfo->nativeDataFormatCount].format = format; - pDeviceInfo->nativeDataFormats[pDeviceInfo->nativeDataFormatCount].channels = channels; - pDeviceInfo->nativeDataFormats[pDeviceInfo->nativeDataFormatCount].sampleRate = sampleRate; - pDeviceInfo->nativeDataFormats[pDeviceInfo->nativeDataFormatCount].flags = flags; - pDeviceInfo->nativeDataFormatCount += 1; - } -} - - -typedef struct -{ - ma_backend backend; - const char* pName; -} ma_backend_info; - -static ma_backend_info gBackendInfo[] = /* Indexed by the backend enum. Must be in the order backends are declared in the ma_backend enum. */ -{ - {ma_backend_wasapi, "WASAPI"}, - {ma_backend_dsound, "DirectSound"}, - {ma_backend_winmm, "WinMM"}, - {ma_backend_coreaudio, "Core Audio"}, - {ma_backend_sndio, "sndio"}, - {ma_backend_audio4, "audio(4)"}, - {ma_backend_oss, "OSS"}, - {ma_backend_pulseaudio, "PulseAudio"}, - {ma_backend_alsa, "ALSA"}, - {ma_backend_jack, "JACK"}, - {ma_backend_aaudio, "AAudio"}, - {ma_backend_opensl, "OpenSL|ES"}, - {ma_backend_webaudio, "Web Audio"}, - {ma_backend_custom, "Custom"}, - {ma_backend_null, "Null"} -}; - -MA_API const char* ma_get_backend_name(ma_backend backend) -{ - if (backend < 0 || backend >= (int)ma_countof(gBackendInfo)) { - return "Unknown"; - } - - return gBackendInfo[backend].pName; -} - -MA_API ma_result ma_get_backend_from_name(const char* pBackendName, ma_backend* pBackend) -{ - size_t iBackend; - - if (pBackendName == NULL) { - return MA_INVALID_ARGS; - } - - for (iBackend = 0; iBackend < ma_countof(gBackendInfo); iBackend += 1) { - if (ma_strcmp(pBackendName, gBackendInfo[iBackend].pName) == 0) { - if (pBackend != NULL) { - *pBackend = gBackendInfo[iBackend].backend; - } - - return MA_SUCCESS; - } - } - - /* Getting here means the backend name is unknown. */ - return MA_INVALID_ARGS; -} - -MA_API ma_bool32 ma_is_backend_enabled(ma_backend backend) -{ - /* - This looks a little bit gross, but we want all backends to be included in the switch to avoid warnings on some compilers - about some enums not being handled by the switch statement. - */ - switch (backend) - { - case ma_backend_wasapi: - #if defined(MA_HAS_WASAPI) - return MA_TRUE; - #else - return MA_FALSE; - #endif - case ma_backend_dsound: - #if defined(MA_HAS_DSOUND) - return MA_TRUE; - #else - return MA_FALSE; - #endif - case ma_backend_winmm: - #if defined(MA_HAS_WINMM) - return MA_TRUE; - #else - return MA_FALSE; - #endif - case ma_backend_coreaudio: - #if defined(MA_HAS_COREAUDIO) - return MA_TRUE; - #else - return MA_FALSE; - #endif - case ma_backend_sndio: - #if defined(MA_HAS_SNDIO) - return MA_TRUE; - #else - return MA_FALSE; - #endif - case ma_backend_audio4: - #if defined(MA_HAS_AUDIO4) - return MA_TRUE; - #else - return MA_FALSE; - #endif - case ma_backend_oss: - #if defined(MA_HAS_OSS) - return MA_TRUE; - #else - return MA_FALSE; - #endif - case ma_backend_pulseaudio: - #if defined(MA_HAS_PULSEAUDIO) - return MA_TRUE; - #else - return MA_FALSE; - #endif - case ma_backend_alsa: - #if defined(MA_HAS_ALSA) - return MA_TRUE; - #else - return MA_FALSE; - #endif - case ma_backend_jack: - #if defined(MA_HAS_JACK) - return MA_TRUE; - #else - return MA_FALSE; - #endif - case ma_backend_aaudio: - #if defined(MA_HAS_AAUDIO) - #if defined(MA_ANDROID) - { - return ma_android_sdk_version() >= MA_AAUDIO_MIN_ANDROID_SDK_VERSION; - } - #else - return MA_FALSE; - #endif - #else - return MA_FALSE; - #endif - case ma_backend_opensl: - #if defined(MA_HAS_OPENSL) - #if defined(MA_ANDROID) - { - return ma_android_sdk_version() >= 9; - } - #else - return MA_TRUE; - #endif - #else - return MA_FALSE; - #endif - case ma_backend_webaudio: - #if defined(MA_HAS_WEBAUDIO) - return MA_TRUE; - #else - return MA_FALSE; - #endif - case ma_backend_custom: - #if defined(MA_HAS_CUSTOM) - return MA_TRUE; - #else - return MA_FALSE; - #endif - case ma_backend_null: - #if defined(MA_HAS_NULL) - return MA_TRUE; - #else - return MA_FALSE; - #endif - - default: return MA_FALSE; - } -} - -MA_API ma_result ma_get_enabled_backends(ma_backend* pBackends, size_t backendCap, size_t* pBackendCount) -{ - size_t backendCount; - size_t iBackend; - ma_result result = MA_SUCCESS; - - if (pBackendCount == NULL) { - return MA_INVALID_ARGS; - } - - backendCount = 0; - - for (iBackend = 0; iBackend <= ma_backend_null; iBackend += 1) { - ma_backend backend = (ma_backend)iBackend; - - if (ma_is_backend_enabled(backend)) { - /* The backend is enabled. Try adding it to the list. If there's no room, MA_NO_SPACE needs to be returned. */ - if (backendCount == backendCap) { - result = MA_NO_SPACE; - break; - } else { - pBackends[backendCount] = backend; - backendCount += 1; - } - } - } - - if (pBackendCount != NULL) { - *pBackendCount = backendCount; - } - - return result; -} - -MA_API ma_bool32 ma_is_loopback_supported(ma_backend backend) -{ - switch (backend) - { - case ma_backend_wasapi: return MA_TRUE; - case ma_backend_dsound: return MA_FALSE; - case ma_backend_winmm: return MA_FALSE; - case ma_backend_coreaudio: return MA_FALSE; - case ma_backend_sndio: return MA_FALSE; - case ma_backend_audio4: return MA_FALSE; - case ma_backend_oss: return MA_FALSE; - case ma_backend_pulseaudio: return MA_FALSE; - case ma_backend_alsa: return MA_FALSE; - case ma_backend_jack: return MA_FALSE; - case ma_backend_aaudio: return MA_FALSE; - case ma_backend_opensl: return MA_FALSE; - case ma_backend_webaudio: return MA_FALSE; - case ma_backend_custom: return MA_FALSE; /* <-- Will depend on the implementation of the backend. */ - case ma_backend_null: return MA_FALSE; - default: return MA_FALSE; - } -} - - - -#if defined(MA_WIN32) -/* WASAPI error codes. */ -#define MA_AUDCLNT_E_NOT_INITIALIZED ((HRESULT)0x88890001) -#define MA_AUDCLNT_E_ALREADY_INITIALIZED ((HRESULT)0x88890002) -#define MA_AUDCLNT_E_WRONG_ENDPOINT_TYPE ((HRESULT)0x88890003) -#define MA_AUDCLNT_E_DEVICE_INVALIDATED ((HRESULT)0x88890004) -#define MA_AUDCLNT_E_NOT_STOPPED ((HRESULT)0x88890005) -#define MA_AUDCLNT_E_BUFFER_TOO_LARGE ((HRESULT)0x88890006) -#define MA_AUDCLNT_E_OUT_OF_ORDER ((HRESULT)0x88890007) -#define MA_AUDCLNT_E_UNSUPPORTED_FORMAT ((HRESULT)0x88890008) -#define MA_AUDCLNT_E_INVALID_SIZE ((HRESULT)0x88890009) -#define MA_AUDCLNT_E_DEVICE_IN_USE ((HRESULT)0x8889000A) -#define MA_AUDCLNT_E_BUFFER_OPERATION_PENDING ((HRESULT)0x8889000B) -#define MA_AUDCLNT_E_THREAD_NOT_REGISTERED ((HRESULT)0x8889000C) -#define MA_AUDCLNT_E_NO_SINGLE_PROCESS ((HRESULT)0x8889000D) -#define MA_AUDCLNT_E_EXCLUSIVE_MODE_NOT_ALLOWED ((HRESULT)0x8889000E) -#define MA_AUDCLNT_E_ENDPOINT_CREATE_FAILED ((HRESULT)0x8889000F) -#define MA_AUDCLNT_E_SERVICE_NOT_RUNNING ((HRESULT)0x88890010) -#define MA_AUDCLNT_E_EVENTHANDLE_NOT_EXPECTED ((HRESULT)0x88890011) -#define MA_AUDCLNT_E_EXCLUSIVE_MODE_ONLY ((HRESULT)0x88890012) -#define MA_AUDCLNT_E_BUFDURATION_PERIOD_NOT_EQUAL ((HRESULT)0x88890013) -#define MA_AUDCLNT_E_EVENTHANDLE_NOT_SET ((HRESULT)0x88890014) -#define MA_AUDCLNT_E_INCORRECT_BUFFER_SIZE ((HRESULT)0x88890015) -#define MA_AUDCLNT_E_BUFFER_SIZE_ERROR ((HRESULT)0x88890016) -#define MA_AUDCLNT_E_CPUUSAGE_EXCEEDED ((HRESULT)0x88890017) -#define MA_AUDCLNT_E_BUFFER_ERROR ((HRESULT)0x88890018) -#define MA_AUDCLNT_E_BUFFER_SIZE_NOT_ALIGNED ((HRESULT)0x88890019) -#define MA_AUDCLNT_E_INVALID_DEVICE_PERIOD ((HRESULT)0x88890020) -#define MA_AUDCLNT_E_INVALID_STREAM_FLAG ((HRESULT)0x88890021) -#define MA_AUDCLNT_E_ENDPOINT_OFFLOAD_NOT_CAPABLE ((HRESULT)0x88890022) -#define MA_AUDCLNT_E_OUT_OF_OFFLOAD_RESOURCES ((HRESULT)0x88890023) -#define MA_AUDCLNT_E_OFFLOAD_MODE_ONLY ((HRESULT)0x88890024) -#define MA_AUDCLNT_E_NONOFFLOAD_MODE_ONLY ((HRESULT)0x88890025) -#define MA_AUDCLNT_E_RESOURCES_INVALIDATED ((HRESULT)0x88890026) -#define MA_AUDCLNT_E_RAW_MODE_UNSUPPORTED ((HRESULT)0x88890027) -#define MA_AUDCLNT_E_ENGINE_PERIODICITY_LOCKED ((HRESULT)0x88890028) -#define MA_AUDCLNT_E_ENGINE_FORMAT_LOCKED ((HRESULT)0x88890029) -#define MA_AUDCLNT_E_HEADTRACKING_ENABLED ((HRESULT)0x88890030) -#define MA_AUDCLNT_E_HEADTRACKING_UNSUPPORTED ((HRESULT)0x88890040) -#define MA_AUDCLNT_S_BUFFER_EMPTY ((HRESULT)0x08890001) -#define MA_AUDCLNT_S_THREAD_ALREADY_REGISTERED ((HRESULT)0x08890002) -#define MA_AUDCLNT_S_POSITION_STALLED ((HRESULT)0x08890003) - -#define MA_DS_OK ((HRESULT)0) -#define MA_DS_NO_VIRTUALIZATION ((HRESULT)0x0878000A) -#define MA_DSERR_ALLOCATED ((HRESULT)0x8878000A) -#define MA_DSERR_CONTROLUNAVAIL ((HRESULT)0x8878001E) -#define MA_DSERR_INVALIDPARAM ((HRESULT)0x80070057) /*E_INVALIDARG*/ -#define MA_DSERR_INVALIDCALL ((HRESULT)0x88780032) -#define MA_DSERR_GENERIC ((HRESULT)0x80004005) /*E_FAIL*/ -#define MA_DSERR_PRIOLEVELNEEDED ((HRESULT)0x88780046) -#define MA_DSERR_OUTOFMEMORY ((HRESULT)0x8007000E) /*E_OUTOFMEMORY*/ -#define MA_DSERR_BADFORMAT ((HRESULT)0x88780064) -#define MA_DSERR_UNSUPPORTED ((HRESULT)0x80004001) /*E_NOTIMPL*/ -#define MA_DSERR_NODRIVER ((HRESULT)0x88780078) -#define MA_DSERR_ALREADYINITIALIZED ((HRESULT)0x88780082) -#define MA_DSERR_NOAGGREGATION ((HRESULT)0x80040110) /*CLASS_E_NOAGGREGATION*/ -#define MA_DSERR_BUFFERLOST ((HRESULT)0x88780096) -#define MA_DSERR_OTHERAPPHASPRIO ((HRESULT)0x887800A0) -#define MA_DSERR_UNINITIALIZED ((HRESULT)0x887800AA) -#define MA_DSERR_NOINTERFACE ((HRESULT)0x80004002) /*E_NOINTERFACE*/ -#define MA_DSERR_ACCESSDENIED ((HRESULT)0x80070005) /*E_ACCESSDENIED*/ -#define MA_DSERR_BUFFERTOOSMALL ((HRESULT)0x887800B4) -#define MA_DSERR_DS8_REQUIRED ((HRESULT)0x887800BE) -#define MA_DSERR_SENDLOOP ((HRESULT)0x887800C8) -#define MA_DSERR_BADSENDBUFFERGUID ((HRESULT)0x887800D2) -#define MA_DSERR_OBJECTNOTFOUND ((HRESULT)0x88781161) -#define MA_DSERR_FXUNAVAILABLE ((HRESULT)0x887800DC) - -static ma_result ma_result_from_HRESULT(HRESULT hr) -{ - switch (hr) - { - case NOERROR: return MA_SUCCESS; - /*case S_OK: return MA_SUCCESS;*/ - - case E_POINTER: return MA_INVALID_ARGS; - case E_UNEXPECTED: return MA_ERROR; - case E_NOTIMPL: return MA_NOT_IMPLEMENTED; - case E_OUTOFMEMORY: return MA_OUT_OF_MEMORY; - case E_INVALIDARG: return MA_INVALID_ARGS; - case E_NOINTERFACE: return MA_API_NOT_FOUND; - case E_HANDLE: return MA_INVALID_ARGS; - case E_ABORT: return MA_ERROR; - case E_FAIL: return MA_ERROR; - case E_ACCESSDENIED: return MA_ACCESS_DENIED; - - /* WASAPI */ - case MA_AUDCLNT_E_NOT_INITIALIZED: return MA_DEVICE_NOT_INITIALIZED; - case MA_AUDCLNT_E_ALREADY_INITIALIZED: return MA_DEVICE_ALREADY_INITIALIZED; - case MA_AUDCLNT_E_WRONG_ENDPOINT_TYPE: return MA_INVALID_ARGS; - case MA_AUDCLNT_E_DEVICE_INVALIDATED: return MA_UNAVAILABLE; - case MA_AUDCLNT_E_NOT_STOPPED: return MA_DEVICE_NOT_STOPPED; - case MA_AUDCLNT_E_BUFFER_TOO_LARGE: return MA_TOO_BIG; - case MA_AUDCLNT_E_OUT_OF_ORDER: return MA_INVALID_OPERATION; - case MA_AUDCLNT_E_UNSUPPORTED_FORMAT: return MA_FORMAT_NOT_SUPPORTED; - case MA_AUDCLNT_E_INVALID_SIZE: return MA_INVALID_ARGS; - case MA_AUDCLNT_E_DEVICE_IN_USE: return MA_BUSY; - case MA_AUDCLNT_E_BUFFER_OPERATION_PENDING: return MA_INVALID_OPERATION; - case MA_AUDCLNT_E_THREAD_NOT_REGISTERED: return MA_DOES_NOT_EXIST; - case MA_AUDCLNT_E_NO_SINGLE_PROCESS: return MA_INVALID_OPERATION; - case MA_AUDCLNT_E_EXCLUSIVE_MODE_NOT_ALLOWED: return MA_SHARE_MODE_NOT_SUPPORTED; - case MA_AUDCLNT_E_ENDPOINT_CREATE_FAILED: return MA_FAILED_TO_OPEN_BACKEND_DEVICE; - case MA_AUDCLNT_E_SERVICE_NOT_RUNNING: return MA_NOT_CONNECTED; - case MA_AUDCLNT_E_EVENTHANDLE_NOT_EXPECTED: return MA_INVALID_ARGS; - case MA_AUDCLNT_E_EXCLUSIVE_MODE_ONLY: return MA_SHARE_MODE_NOT_SUPPORTED; - case MA_AUDCLNT_E_BUFDURATION_PERIOD_NOT_EQUAL: return MA_INVALID_ARGS; - case MA_AUDCLNT_E_EVENTHANDLE_NOT_SET: return MA_INVALID_ARGS; - case MA_AUDCLNT_E_INCORRECT_BUFFER_SIZE: return MA_INVALID_ARGS; - case MA_AUDCLNT_E_BUFFER_SIZE_ERROR: return MA_INVALID_ARGS; - case MA_AUDCLNT_E_CPUUSAGE_EXCEEDED: return MA_ERROR; - case MA_AUDCLNT_E_BUFFER_ERROR: return MA_ERROR; - case MA_AUDCLNT_E_BUFFER_SIZE_NOT_ALIGNED: return MA_INVALID_ARGS; - case MA_AUDCLNT_E_INVALID_DEVICE_PERIOD: return MA_INVALID_ARGS; - case MA_AUDCLNT_E_INVALID_STREAM_FLAG: return MA_INVALID_ARGS; - case MA_AUDCLNT_E_ENDPOINT_OFFLOAD_NOT_CAPABLE: return MA_INVALID_OPERATION; - case MA_AUDCLNT_E_OUT_OF_OFFLOAD_RESOURCES: return MA_OUT_OF_MEMORY; - case MA_AUDCLNT_E_OFFLOAD_MODE_ONLY: return MA_INVALID_OPERATION; - case MA_AUDCLNT_E_NONOFFLOAD_MODE_ONLY: return MA_INVALID_OPERATION; - case MA_AUDCLNT_E_RESOURCES_INVALIDATED: return MA_INVALID_DATA; - case MA_AUDCLNT_E_RAW_MODE_UNSUPPORTED: return MA_INVALID_OPERATION; - case MA_AUDCLNT_E_ENGINE_PERIODICITY_LOCKED: return MA_INVALID_OPERATION; - case MA_AUDCLNT_E_ENGINE_FORMAT_LOCKED: return MA_INVALID_OPERATION; - case MA_AUDCLNT_E_HEADTRACKING_ENABLED: return MA_INVALID_OPERATION; - case MA_AUDCLNT_E_HEADTRACKING_UNSUPPORTED: return MA_INVALID_OPERATION; - case MA_AUDCLNT_S_BUFFER_EMPTY: return MA_NO_SPACE; - case MA_AUDCLNT_S_THREAD_ALREADY_REGISTERED: return MA_ALREADY_EXISTS; - case MA_AUDCLNT_S_POSITION_STALLED: return MA_ERROR; - - /* DirectSound */ - /*case MA_DS_OK: return MA_SUCCESS;*/ /* S_OK */ - case MA_DS_NO_VIRTUALIZATION: return MA_SUCCESS; - case MA_DSERR_ALLOCATED: return MA_ALREADY_IN_USE; - case MA_DSERR_CONTROLUNAVAIL: return MA_INVALID_OPERATION; - /*case MA_DSERR_INVALIDPARAM: return MA_INVALID_ARGS;*/ /* E_INVALIDARG */ - case MA_DSERR_INVALIDCALL: return MA_INVALID_OPERATION; - /*case MA_DSERR_GENERIC: return MA_ERROR;*/ /* E_FAIL */ - case MA_DSERR_PRIOLEVELNEEDED: return MA_INVALID_OPERATION; - /*case MA_DSERR_OUTOFMEMORY: return MA_OUT_OF_MEMORY;*/ /* E_OUTOFMEMORY */ - case MA_DSERR_BADFORMAT: return MA_FORMAT_NOT_SUPPORTED; - /*case MA_DSERR_UNSUPPORTED: return MA_NOT_IMPLEMENTED;*/ /* E_NOTIMPL */ - case MA_DSERR_NODRIVER: return MA_FAILED_TO_INIT_BACKEND; - case MA_DSERR_ALREADYINITIALIZED: return MA_DEVICE_ALREADY_INITIALIZED; - case MA_DSERR_NOAGGREGATION: return MA_ERROR; - case MA_DSERR_BUFFERLOST: return MA_UNAVAILABLE; - case MA_DSERR_OTHERAPPHASPRIO: return MA_ACCESS_DENIED; - case MA_DSERR_UNINITIALIZED: return MA_DEVICE_NOT_INITIALIZED; - /*case MA_DSERR_NOINTERFACE: return MA_API_NOT_FOUND;*/ /* E_NOINTERFACE */ - /*case MA_DSERR_ACCESSDENIED: return MA_ACCESS_DENIED;*/ /* E_ACCESSDENIED */ - case MA_DSERR_BUFFERTOOSMALL: return MA_NO_SPACE; - case MA_DSERR_DS8_REQUIRED: return MA_INVALID_OPERATION; - case MA_DSERR_SENDLOOP: return MA_DEADLOCK; - case MA_DSERR_BADSENDBUFFERGUID: return MA_INVALID_ARGS; - case MA_DSERR_OBJECTNOTFOUND: return MA_NO_DEVICE; - case MA_DSERR_FXUNAVAILABLE: return MA_UNAVAILABLE; - - default: return MA_ERROR; - } -} - -/* PROPVARIANT */ -#define MA_VT_LPWSTR 31 -#define MA_VT_BLOB 65 - -#if defined(_MSC_VER) && !defined(__clang__) - #pragma warning(push) - #pragma warning(disable:4201) /* nonstandard extension used: nameless struct/union */ -#elif defined(__clang__) || (defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 8))) - #pragma GCC diagnostic push - #pragma GCC diagnostic ignored "-Wpedantic" /* For ISO C99 doesn't support unnamed structs/unions [-Wpedantic] */ - #if defined(__clang__) - #pragma GCC diagnostic ignored "-Wc11-extensions" /* anonymous unions are a C11 extension */ - #endif -#endif -typedef struct -{ - WORD vt; - WORD wReserved1; - WORD wReserved2; - WORD wReserved3; - union - { - struct - { - ULONG cbSize; - BYTE* pBlobData; - } blob; - WCHAR* pwszVal; - char pad[16]; /* Just to ensure the size of the struct matches the official version. */ - }; -} MA_PROPVARIANT; -#if defined(_MSC_VER) && !defined(__clang__) - #pragma warning(pop) -#elif defined(__clang__) || (defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 8))) - #pragma GCC diagnostic pop -#endif - -typedef HRESULT (WINAPI * MA_PFN_CoInitialize)(void* pvReserved); -typedef HRESULT (WINAPI * MA_PFN_CoInitializeEx)(void* pvReserved, DWORD dwCoInit); -typedef void (WINAPI * MA_PFN_CoUninitialize)(void); -typedef HRESULT (WINAPI * MA_PFN_CoCreateInstance)(const IID* rclsid, void* pUnkOuter, DWORD dwClsContext, const IID* riid, void* ppv); -typedef void (WINAPI * MA_PFN_CoTaskMemFree)(void* pv); -typedef HRESULT (WINAPI * MA_PFN_PropVariantClear)(MA_PROPVARIANT *pvar); -typedef int (WINAPI * MA_PFN_StringFromGUID2)(const GUID* const rguid, WCHAR* lpsz, int cchMax); - -typedef HWND (WINAPI * MA_PFN_GetForegroundWindow)(void); -typedef HWND (WINAPI * MA_PFN_GetDesktopWindow)(void); - -#if defined(MA_WIN32_DESKTOP) -/* Microsoft documents these APIs as returning LSTATUS, but the Win32 API shipping with some compilers do not define it. It's just a LONG. */ -typedef LONG (WINAPI * MA_PFN_RegOpenKeyExA)(HKEY hKey, const char* lpSubKey, DWORD ulOptions, DWORD samDesired, HKEY* phkResult); -typedef LONG (WINAPI * MA_PFN_RegCloseKey)(HKEY hKey); -typedef LONG (WINAPI * MA_PFN_RegQueryValueExA)(HKEY hKey, const char* lpValueName, DWORD* lpReserved, DWORD* lpType, BYTE* lpData, DWORD* lpcbData); -#endif /* MA_WIN32_DESKTOP */ - -MA_API size_t ma_strlen_WCHAR(const WCHAR* str) -{ - size_t len = 0; - while (str[len] != '\0') { - len += 1; - } - - return len; -} - -MA_API int ma_strcmp_WCHAR(const WCHAR *s1, const WCHAR *s2) -{ - while (*s1 != '\0' && *s1 == *s2) { - s1 += 1; - s2 += 1; - } - - return *s1 - *s2; -} - -MA_API int ma_strcpy_s_WCHAR(WCHAR* dst, size_t dstCap, const WCHAR* src) -{ - size_t i; - - if (dst == 0) { - return 22; - } - if (dstCap == 0) { - return 34; - } - if (src == 0) { - dst[0] = '\0'; - return 22; - } - - for (i = 0; i < dstCap && src[i] != '\0'; ++i) { - dst[i] = src[i]; - } - - if (i < dstCap) { - dst[i] = '\0'; - return 0; - } - - dst[0] = '\0'; - return 34; -} -#endif /* MA_WIN32 */ - - -#define MA_DEFAULT_PLAYBACK_DEVICE_NAME "Default Playback Device" -#define MA_DEFAULT_CAPTURE_DEVICE_NAME "Default Capture Device" - - - - -/******************************************************************************* - -Timing - -*******************************************************************************/ -#if defined(MA_WIN32) && !defined(MA_POSIX) - static LARGE_INTEGER g_ma_TimerFrequency; /* <-- Initialized to zero since it's static. */ - static void ma_timer_init(ma_timer* pTimer) - { - LARGE_INTEGER counter; - - if (g_ma_TimerFrequency.QuadPart == 0) { - QueryPerformanceFrequency(&g_ma_TimerFrequency); - } - - QueryPerformanceCounter(&counter); - pTimer->counter = counter.QuadPart; - } - - static double ma_timer_get_time_in_seconds(ma_timer* pTimer) - { - LARGE_INTEGER counter; - if (!QueryPerformanceCounter(&counter)) { - return 0; - } - - return (double)(counter.QuadPart - pTimer->counter) / g_ma_TimerFrequency.QuadPart; - } -#elif defined(MA_APPLE) && (MAC_OS_X_VERSION_MIN_REQUIRED < 101200) - static ma_uint64 g_ma_TimerFrequency = 0; - static void ma_timer_init(ma_timer* pTimer) - { - mach_timebase_info_data_t baseTime; - mach_timebase_info(&baseTime); - g_ma_TimerFrequency = (baseTime.denom * 1e9) / baseTime.numer; - - pTimer->counter = mach_absolute_time(); - } - - static double ma_timer_get_time_in_seconds(ma_timer* pTimer) - { - ma_uint64 newTimeCounter = mach_absolute_time(); - ma_uint64 oldTimeCounter = pTimer->counter; - - return (newTimeCounter - oldTimeCounter) / g_ma_TimerFrequency; - } -#elif defined(MA_EMSCRIPTEN) - static MA_INLINE void ma_timer_init(ma_timer* pTimer) - { - pTimer->counterD = emscripten_get_now(); - } - - static MA_INLINE double ma_timer_get_time_in_seconds(ma_timer* pTimer) - { - return (emscripten_get_now() - pTimer->counterD) / 1000; /* Emscripten is in milliseconds. */ - } -#else - #if defined(_POSIX_C_SOURCE) && _POSIX_C_SOURCE >= 199309L - #if defined(CLOCK_MONOTONIC) - #define MA_CLOCK_ID CLOCK_MONOTONIC - #else - #define MA_CLOCK_ID CLOCK_REALTIME - #endif - - static void ma_timer_init(ma_timer* pTimer) - { - struct timespec newTime; - clock_gettime(MA_CLOCK_ID, &newTime); - - pTimer->counter = (newTime.tv_sec * 1000000000) + newTime.tv_nsec; - } - - static double ma_timer_get_time_in_seconds(ma_timer* pTimer) - { - ma_uint64 newTimeCounter; - ma_uint64 oldTimeCounter; - - struct timespec newTime; - clock_gettime(MA_CLOCK_ID, &newTime); - - newTimeCounter = (newTime.tv_sec * 1000000000) + newTime.tv_nsec; - oldTimeCounter = pTimer->counter; - - return (newTimeCounter - oldTimeCounter) / 1000000000.0; - } - #else - static void ma_timer_init(ma_timer* pTimer) - { - struct timeval newTime; - gettimeofday(&newTime, NULL); - - pTimer->counter = (newTime.tv_sec * 1000000) + newTime.tv_usec; - } - - static double ma_timer_get_time_in_seconds(ma_timer* pTimer) - { - ma_uint64 newTimeCounter; - ma_uint64 oldTimeCounter; - - struct timeval newTime; - gettimeofday(&newTime, NULL); - - newTimeCounter = (newTime.tv_sec * 1000000) + newTime.tv_usec; - oldTimeCounter = pTimer->counter; - - return (newTimeCounter - oldTimeCounter) / 1000000.0; - } - #endif -#endif - - - -#if 0 -static ma_uint32 ma_get_closest_standard_sample_rate(ma_uint32 sampleRateIn) -{ - ma_uint32 closestRate = 0; - ma_uint32 closestDiff = 0xFFFFFFFF; - size_t iStandardRate; - - for (iStandardRate = 0; iStandardRate < ma_countof(g_maStandardSampleRatePriorities); ++iStandardRate) { - ma_uint32 standardRate = g_maStandardSampleRatePriorities[iStandardRate]; - ma_uint32 diff; - - if (sampleRateIn > standardRate) { - diff = sampleRateIn - standardRate; - } else { - diff = standardRate - sampleRateIn; - } - - if (diff == 0) { - return standardRate; /* The input sample rate is a standard rate. */ - } - - if (closestDiff > diff) { - closestDiff = diff; - closestRate = standardRate; - } - } - - return closestRate; -} -#endif - - -static MA_INLINE unsigned int ma_device_disable_denormals(ma_device* pDevice) -{ - MA_ASSERT(pDevice != NULL); - - if (!pDevice->noDisableDenormals) { - return ma_disable_denormals(); - } else { - return 0; - } -} - -static MA_INLINE void ma_device_restore_denormals(ma_device* pDevice, unsigned int prevState) -{ - MA_ASSERT(pDevice != NULL); - - if (!pDevice->noDisableDenormals) { - ma_restore_denormals(prevState); - } else { - /* Do nothing. */ - (void)prevState; - } -} - -static ma_device_notification ma_device_notification_init(ma_device* pDevice, ma_device_notification_type type) -{ - ma_device_notification notification; - - MA_ZERO_OBJECT(¬ification); - notification.pDevice = pDevice; - notification.type = type; - - return notification; -} - -static void ma_device__on_notification(ma_device_notification notification) -{ - MA_ASSERT(notification.pDevice != NULL); - - if (notification.pDevice->onNotification != NULL) { - notification.pDevice->onNotification(¬ification); - } - - /* TEMP FOR COMPATIBILITY: If it's a stopped notification, fire the onStop callback as well. This is only for backwards compatibility and will be removed. */ - if (notification.pDevice->onStop != NULL && notification.type == ma_device_notification_type_stopped) { - notification.pDevice->onStop(notification.pDevice); - } -} - -static void ma_device__on_notification_started(ma_device* pDevice) -{ - ma_device__on_notification(ma_device_notification_init(pDevice, ma_device_notification_type_started)); -} - -static void ma_device__on_notification_stopped(ma_device* pDevice) -{ - ma_device__on_notification(ma_device_notification_init(pDevice, ma_device_notification_type_stopped)); -} - -/* Not all platforms support reroute notifications. */ -#if !defined(MA_EMSCRIPTEN) -static void ma_device__on_notification_rerouted(ma_device* pDevice) -{ - ma_device__on_notification(ma_device_notification_init(pDevice, ma_device_notification_type_rerouted)); -} -#endif - -#if defined(MA_EMSCRIPTEN) -#ifdef __cplusplus -extern "C" { -#endif -void EMSCRIPTEN_KEEPALIVE ma_device__on_notification_unlocked(ma_device* pDevice) -{ - ma_device__on_notification(ma_device_notification_init(pDevice, ma_device_notification_type_unlocked)); -} -#ifdef __cplusplus -} -#endif -#endif - - -static void ma_device__on_data_inner(ma_device* pDevice, void* pFramesOut, const void* pFramesIn, ma_uint32 frameCount) -{ - MA_ASSERT(pDevice != NULL); - MA_ASSERT(pDevice->onData != NULL); - - if (!pDevice->noPreSilencedOutputBuffer && pFramesOut != NULL) { - ma_silence_pcm_frames(pFramesOut, frameCount, pDevice->playback.format, pDevice->playback.channels); - } - - pDevice->onData(pDevice, pFramesOut, pFramesIn, frameCount); -} - -static void ma_device__on_data(ma_device* pDevice, void* pFramesOut, const void* pFramesIn, ma_uint32 frameCount) -{ - MA_ASSERT(pDevice != NULL); - - /* Don't read more data from the client if we're in the process of stopping. */ - if (ma_device_get_state(pDevice) == ma_device_state_stopping) { - return; - } - - if (pDevice->noFixedSizedCallback) { - /* Fast path. Not using a fixed sized callback. Process directly from the specified buffers. */ - ma_device__on_data_inner(pDevice, pFramesOut, pFramesIn, frameCount); - } else { - /* Slow path. Using a fixed sized callback. Need to use the intermediary buffer. */ - ma_uint32 totalFramesProcessed = 0; - - while (totalFramesProcessed < frameCount) { - ma_uint32 totalFramesRemaining = frameCount - totalFramesProcessed; - ma_uint32 framesToProcessThisIteration = 0; - - if (pFramesIn != NULL) { - /* Capturing. Write to the intermediary buffer. If there's no room, fire the callback to empty it. */ - if (pDevice->capture.intermediaryBufferLen < pDevice->capture.intermediaryBufferCap) { - /* There's some room left in the intermediary buffer. Write to it without firing the callback. */ - framesToProcessThisIteration = totalFramesRemaining; - if (framesToProcessThisIteration > pDevice->capture.intermediaryBufferCap - pDevice->capture.intermediaryBufferLen) { - framesToProcessThisIteration = pDevice->capture.intermediaryBufferCap - pDevice->capture.intermediaryBufferLen; - } - - ma_copy_pcm_frames( - ma_offset_pcm_frames_ptr(pDevice->capture.pIntermediaryBuffer, pDevice->capture.intermediaryBufferLen, pDevice->capture.format, pDevice->capture.channels), - ma_offset_pcm_frames_const_ptr(pFramesIn, totalFramesProcessed, pDevice->capture.format, pDevice->capture.channels), - framesToProcessThisIteration, - pDevice->capture.format, pDevice->capture.channels); - - pDevice->capture.intermediaryBufferLen += framesToProcessThisIteration; - } - - if (pDevice->capture.intermediaryBufferLen == pDevice->capture.intermediaryBufferCap) { - /* No room left in the intermediary buffer. Fire the data callback. */ - if (pDevice->type == ma_device_type_duplex) { - /* We'll do the duplex data callback later after we've processed the playback data. */ - } else { - ma_device__on_data_inner(pDevice, NULL, pDevice->capture.pIntermediaryBuffer, pDevice->capture.intermediaryBufferCap); - - /* The intermediary buffer has just been drained. */ - pDevice->capture.intermediaryBufferLen = 0; - } - } - } - - if (pFramesOut != NULL) { - /* Playing back. Read from the intermediary buffer. If there's nothing in it, fire the callback to fill it. */ - if (pDevice->playback.intermediaryBufferLen > 0) { - /* There's some content in the intermediary buffer. Read from that without firing the callback. */ - if (pDevice->type == ma_device_type_duplex) { - /* The frames processed this iteration for a duplex device will always be based on the capture side. Leave it unmodified. */ - } else { - framesToProcessThisIteration = totalFramesRemaining; - if (framesToProcessThisIteration > pDevice->playback.intermediaryBufferLen) { - framesToProcessThisIteration = pDevice->playback.intermediaryBufferLen; - } - } - - ma_copy_pcm_frames( - ma_offset_pcm_frames_ptr(pFramesOut, totalFramesProcessed, pDevice->playback.format, pDevice->playback.channels), - ma_offset_pcm_frames_ptr(pDevice->playback.pIntermediaryBuffer, pDevice->playback.intermediaryBufferCap - pDevice->playback.intermediaryBufferLen, pDevice->playback.format, pDevice->playback.channels), - framesToProcessThisIteration, - pDevice->playback.format, pDevice->playback.channels); - - pDevice->playback.intermediaryBufferLen -= framesToProcessThisIteration; - } - - if (pDevice->playback.intermediaryBufferLen == 0) { - /* There's nothing in the intermediary buffer. Fire the data callback to fill it. */ - if (pDevice->type == ma_device_type_duplex) { - /* In duplex mode, the data callback will be fired later. Nothing to do here. */ - } else { - ma_device__on_data_inner(pDevice, pDevice->playback.pIntermediaryBuffer, NULL, pDevice->playback.intermediaryBufferCap); - - /* The intermediary buffer has just been filled. */ - pDevice->playback.intermediaryBufferLen = pDevice->playback.intermediaryBufferCap; - } - } - } - - /* If we're in duplex mode we might need to do a refill of the data. */ - if (pDevice->type == ma_device_type_duplex) { - if (pDevice->capture.intermediaryBufferLen == pDevice->capture.intermediaryBufferCap) { - ma_device__on_data_inner(pDevice, pDevice->playback.pIntermediaryBuffer, pDevice->capture.pIntermediaryBuffer, pDevice->capture.intermediaryBufferCap); - - pDevice->playback.intermediaryBufferLen = pDevice->playback.intermediaryBufferCap; /* The playback buffer will have just been filled. */ - pDevice->capture.intermediaryBufferLen = 0; /* The intermediary buffer has just been drained. */ - } - } - - /* Make sure this is only incremented once in the duplex case. */ - totalFramesProcessed += framesToProcessThisIteration; - } - } -} - -static void ma_device__handle_data_callback(ma_device* pDevice, void* pFramesOut, const void* pFramesIn, ma_uint32 frameCount) -{ - float masterVolumeFactor; - - ma_device_get_master_volume(pDevice, &masterVolumeFactor); /* Use ma_device_get_master_volume() to ensure the volume is loaded atomically. */ - - if (pDevice->onData) { - unsigned int prevDenormalState = ma_device_disable_denormals(pDevice); - { - /* Volume control of input makes things a bit awkward because the input buffer is read-only. We'll need to use a temp buffer and loop in this case. */ - if (pFramesIn != NULL && masterVolumeFactor != 1) { - ma_uint8 tempFramesIn[MA_DATA_CONVERTER_STACK_BUFFER_SIZE]; - ma_uint32 bpfCapture = ma_get_bytes_per_frame(pDevice->capture.format, pDevice->capture.channels); - ma_uint32 bpfPlayback = ma_get_bytes_per_frame(pDevice->playback.format, pDevice->playback.channels); - ma_uint32 totalFramesProcessed = 0; - while (totalFramesProcessed < frameCount) { - ma_uint32 framesToProcessThisIteration = frameCount - totalFramesProcessed; - if (framesToProcessThisIteration > sizeof(tempFramesIn)/bpfCapture) { - framesToProcessThisIteration = sizeof(tempFramesIn)/bpfCapture; - } - - ma_copy_and_apply_volume_factor_pcm_frames(tempFramesIn, ma_offset_ptr(pFramesIn, totalFramesProcessed*bpfCapture), framesToProcessThisIteration, pDevice->capture.format, pDevice->capture.channels, masterVolumeFactor); - - ma_device__on_data(pDevice, ma_offset_ptr(pFramesOut, totalFramesProcessed*bpfPlayback), tempFramesIn, framesToProcessThisIteration); - - totalFramesProcessed += framesToProcessThisIteration; - } - } else { - ma_device__on_data(pDevice, pFramesOut, pFramesIn, frameCount); - } - - /* Volume control and clipping for playback devices. */ - if (pFramesOut != NULL) { - if (masterVolumeFactor != 1) { - if (pFramesIn == NULL) { /* <-- In full-duplex situations, the volume will have been applied to the input samples before the data callback. Applying it again post-callback will incorrectly compound it. */ - ma_apply_volume_factor_pcm_frames(pFramesOut, frameCount, pDevice->playback.format, pDevice->playback.channels, masterVolumeFactor); - } - } - - if (!pDevice->noClip && pDevice->playback.format == ma_format_f32) { - ma_clip_samples_f32((float*)pFramesOut, (const float*)pFramesOut, frameCount * pDevice->playback.channels); /* Intentionally specifying the same pointer for both input and output for in-place processing. */ - } - } - } - ma_device_restore_denormals(pDevice, prevDenormalState); - } else { - /* No data callback. Just silence the output. */ - if (pFramesOut != NULL) { - ma_silence_pcm_frames(pFramesOut, frameCount, pDevice->playback.format, pDevice->playback.channels); - } - } -} - - - -/* A helper function for reading sample data from the client. */ -static void ma_device__read_frames_from_client(ma_device* pDevice, ma_uint32 frameCount, void* pFramesOut) -{ - MA_ASSERT(pDevice != NULL); - MA_ASSERT(frameCount > 0); - MA_ASSERT(pFramesOut != NULL); - - if (pDevice->playback.converter.isPassthrough) { - ma_device__handle_data_callback(pDevice, pFramesOut, NULL, frameCount); - } else { - ma_result result; - ma_uint64 totalFramesReadOut; - void* pRunningFramesOut; - - totalFramesReadOut = 0; - pRunningFramesOut = pFramesOut; - - /* - We run slightly different logic depending on whether or not we're using a heap-allocated - buffer for caching input data. This will be the case if the data converter does not have - the ability to retrieve the required input frame count for a given output frame count. - */ - if (pDevice->playback.pInputCache != NULL) { - while (totalFramesReadOut < frameCount) { - ma_uint64 framesToReadThisIterationIn; - ma_uint64 framesToReadThisIterationOut; - - /* If there's any data available in the cache, that needs to get processed first. */ - if (pDevice->playback.inputCacheRemaining > 0) { - framesToReadThisIterationOut = (frameCount - totalFramesReadOut); - framesToReadThisIterationIn = framesToReadThisIterationOut; - if (framesToReadThisIterationIn > pDevice->playback.inputCacheRemaining) { - framesToReadThisIterationIn = pDevice->playback.inputCacheRemaining; - } - - result = ma_data_converter_process_pcm_frames(&pDevice->playback.converter, ma_offset_pcm_frames_ptr(pDevice->playback.pInputCache, pDevice->playback.inputCacheConsumed, pDevice->playback.format, pDevice->playback.channels), &framesToReadThisIterationIn, pRunningFramesOut, &framesToReadThisIterationOut); - if (result != MA_SUCCESS) { - break; - } - - pDevice->playback.inputCacheConsumed += framesToReadThisIterationIn; - pDevice->playback.inputCacheRemaining -= framesToReadThisIterationIn; - - totalFramesReadOut += framesToReadThisIterationOut; - pRunningFramesOut = ma_offset_ptr(pRunningFramesOut, framesToReadThisIterationOut * ma_get_bytes_per_frame(pDevice->playback.internalFormat, pDevice->playback.internalChannels)); - - if (framesToReadThisIterationIn == 0 && framesToReadThisIterationOut == 0) { - break; /* We're done. */ - } - } - - /* Getting here means there's no data in the cache and we need to fill it up with data from the client. */ - if (pDevice->playback.inputCacheRemaining == 0) { - ma_device__handle_data_callback(pDevice, pDevice->playback.pInputCache, NULL, (ma_uint32)pDevice->playback.inputCacheCap); - - pDevice->playback.inputCacheConsumed = 0; - pDevice->playback.inputCacheRemaining = pDevice->playback.inputCacheCap; - } - } - } else { - while (totalFramesReadOut < frameCount) { - ma_uint8 pIntermediaryBuffer[MA_DATA_CONVERTER_STACK_BUFFER_SIZE]; /* In client format. */ - ma_uint64 intermediaryBufferCap = sizeof(pIntermediaryBuffer) / ma_get_bytes_per_frame(pDevice->playback.format, pDevice->playback.channels); - ma_uint64 framesToReadThisIterationIn; - ma_uint64 framesReadThisIterationIn; - ma_uint64 framesToReadThisIterationOut; - ma_uint64 framesReadThisIterationOut; - ma_uint64 requiredInputFrameCount; - - framesToReadThisIterationOut = (frameCount - totalFramesReadOut); - framesToReadThisIterationIn = framesToReadThisIterationOut; - if (framesToReadThisIterationIn > intermediaryBufferCap) { - framesToReadThisIterationIn = intermediaryBufferCap; - } - - ma_data_converter_get_required_input_frame_count(&pDevice->playback.converter, framesToReadThisIterationOut, &requiredInputFrameCount); - if (framesToReadThisIterationIn > requiredInputFrameCount) { - framesToReadThisIterationIn = requiredInputFrameCount; - } - - ma_device__handle_data_callback(pDevice, pIntermediaryBuffer, NULL, (ma_uint32)framesToReadThisIterationIn); - - /* - At this point we have our decoded data in input format and now we need to convert to output format. Note that even if we didn't read any - input frames, we still want to try processing frames because there may some output frames generated from cached input data. - */ - framesReadThisIterationIn = framesToReadThisIterationIn; - framesReadThisIterationOut = framesToReadThisIterationOut; - result = ma_data_converter_process_pcm_frames(&pDevice->playback.converter, pIntermediaryBuffer, &framesReadThisIterationIn, pRunningFramesOut, &framesReadThisIterationOut); - if (result != MA_SUCCESS) { - break; - } - - totalFramesReadOut += framesReadThisIterationOut; - pRunningFramesOut = ma_offset_ptr(pRunningFramesOut, framesReadThisIterationOut * ma_get_bytes_per_frame(pDevice->playback.internalFormat, pDevice->playback.internalChannels)); - - if (framesReadThisIterationIn == 0 && framesReadThisIterationOut == 0) { - break; /* We're done. */ - } - } - } - } -} - -/* A helper for sending sample data to the client. */ -static void ma_device__send_frames_to_client(ma_device* pDevice, ma_uint32 frameCountInDeviceFormat, const void* pFramesInDeviceFormat) -{ - MA_ASSERT(pDevice != NULL); - MA_ASSERT(frameCountInDeviceFormat > 0); - MA_ASSERT(pFramesInDeviceFormat != NULL); - - if (pDevice->capture.converter.isPassthrough) { - ma_device__handle_data_callback(pDevice, NULL, pFramesInDeviceFormat, frameCountInDeviceFormat); - } else { - ma_result result; - ma_uint8 pFramesInClientFormat[MA_DATA_CONVERTER_STACK_BUFFER_SIZE]; - ma_uint64 framesInClientFormatCap = sizeof(pFramesInClientFormat) / ma_get_bytes_per_frame(pDevice->capture.format, pDevice->capture.channels); - ma_uint64 totalDeviceFramesProcessed = 0; - ma_uint64 totalClientFramesProcessed = 0; - const void* pRunningFramesInDeviceFormat = pFramesInDeviceFormat; - - /* We just keep going until we've exhausted all of our input frames and cannot generate any more output frames. */ - for (;;) { - ma_uint64 deviceFramesProcessedThisIteration; - ma_uint64 clientFramesProcessedThisIteration; - - deviceFramesProcessedThisIteration = (frameCountInDeviceFormat - totalDeviceFramesProcessed); - clientFramesProcessedThisIteration = framesInClientFormatCap; - - result = ma_data_converter_process_pcm_frames(&pDevice->capture.converter, pRunningFramesInDeviceFormat, &deviceFramesProcessedThisIteration, pFramesInClientFormat, &clientFramesProcessedThisIteration); - if (result != MA_SUCCESS) { - break; - } - - if (clientFramesProcessedThisIteration > 0) { - ma_device__handle_data_callback(pDevice, NULL, pFramesInClientFormat, (ma_uint32)clientFramesProcessedThisIteration); /* Safe cast. */ - } - - pRunningFramesInDeviceFormat = ma_offset_ptr(pRunningFramesInDeviceFormat, deviceFramesProcessedThisIteration * ma_get_bytes_per_frame(pDevice->capture.internalFormat, pDevice->capture.internalChannels)); - totalDeviceFramesProcessed += deviceFramesProcessedThisIteration; - totalClientFramesProcessed += clientFramesProcessedThisIteration; - - /* This is just to silence a warning. I might want to use this variable later so leaving in place for now. */ - (void)totalClientFramesProcessed; - - if (deviceFramesProcessedThisIteration == 0 && clientFramesProcessedThisIteration == 0) { - break; /* We're done. */ - } - } - } -} - -static ma_result ma_device__handle_duplex_callback_capture(ma_device* pDevice, ma_uint32 frameCountInDeviceFormat, const void* pFramesInDeviceFormat, ma_pcm_rb* pRB) -{ - ma_result result; - ma_uint32 totalDeviceFramesProcessed = 0; - const void* pRunningFramesInDeviceFormat = pFramesInDeviceFormat; - - MA_ASSERT(pDevice != NULL); - MA_ASSERT(frameCountInDeviceFormat > 0); - MA_ASSERT(pFramesInDeviceFormat != NULL); - MA_ASSERT(pRB != NULL); - - /* Write to the ring buffer. The ring buffer is in the client format which means we need to convert. */ - for (;;) { - ma_uint32 framesToProcessInDeviceFormat = (frameCountInDeviceFormat - totalDeviceFramesProcessed); - ma_uint32 framesToProcessInClientFormat = MA_DATA_CONVERTER_STACK_BUFFER_SIZE / ma_get_bytes_per_frame(pDevice->capture.format, pDevice->capture.channels); - ma_uint64 framesProcessedInDeviceFormat; - ma_uint64 framesProcessedInClientFormat; - void* pFramesInClientFormat; - - result = ma_pcm_rb_acquire_write(pRB, &framesToProcessInClientFormat, &pFramesInClientFormat); - if (result != MA_SUCCESS) { - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "Failed to acquire capture PCM frames from ring buffer."); - break; - } - - if (framesToProcessInClientFormat == 0) { - if (ma_pcm_rb_pointer_distance(pRB) == (ma_int32)ma_pcm_rb_get_subbuffer_size(pRB)) { - break; /* Overrun. Not enough room in the ring buffer for input frame. Excess frames are dropped. */ - } - } - - /* Convert. */ - framesProcessedInDeviceFormat = framesToProcessInDeviceFormat; - framesProcessedInClientFormat = framesToProcessInClientFormat; - result = ma_data_converter_process_pcm_frames(&pDevice->capture.converter, pRunningFramesInDeviceFormat, &framesProcessedInDeviceFormat, pFramesInClientFormat, &framesProcessedInClientFormat); - if (result != MA_SUCCESS) { - break; - } - - result = ma_pcm_rb_commit_write(pRB, (ma_uint32)framesProcessedInClientFormat); /* Safe cast. */ - if (result != MA_SUCCESS) { - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "Failed to commit capture PCM frames to ring buffer."); - break; - } - - pRunningFramesInDeviceFormat = ma_offset_ptr(pRunningFramesInDeviceFormat, framesProcessedInDeviceFormat * ma_get_bytes_per_frame(pDevice->capture.internalFormat, pDevice->capture.internalChannels)); - totalDeviceFramesProcessed += (ma_uint32)framesProcessedInDeviceFormat; /* Safe cast. */ - - /* We're done when we're unable to process any client nor device frames. */ - if (framesProcessedInClientFormat == 0 && framesProcessedInDeviceFormat == 0) { - break; /* Done. */ - } - } - - return MA_SUCCESS; -} - -static ma_result ma_device__handle_duplex_callback_playback(ma_device* pDevice, ma_uint32 frameCount, void* pFramesInInternalFormat, ma_pcm_rb* pRB) -{ - ma_result result; - ma_uint8 silentInputFrames[MA_DATA_CONVERTER_STACK_BUFFER_SIZE]; - ma_uint32 totalFramesReadOut = 0; - - MA_ASSERT(pDevice != NULL); - MA_ASSERT(frameCount > 0); - MA_ASSERT(pFramesInInternalFormat != NULL); - MA_ASSERT(pRB != NULL); - MA_ASSERT(pDevice->playback.pInputCache != NULL); - - /* - Sitting in the ring buffer should be captured data from the capture callback in external format. If there's not enough data in there for - the whole frameCount frames we just use silence instead for the input data. - */ - MA_ZERO_MEMORY(silentInputFrames, sizeof(silentInputFrames)); - - while (totalFramesReadOut < frameCount && ma_device_is_started(pDevice)) { - /* - We should have a buffer allocated on the heap. Any playback frames still sitting in there - need to be sent to the internal device before we process any more data from the client. - */ - if (pDevice->playback.inputCacheRemaining > 0) { - ma_uint64 framesConvertedIn = pDevice->playback.inputCacheRemaining; - ma_uint64 framesConvertedOut = (frameCount - totalFramesReadOut); - ma_data_converter_process_pcm_frames(&pDevice->playback.converter, ma_offset_pcm_frames_ptr(pDevice->playback.pInputCache, pDevice->playback.inputCacheConsumed, pDevice->playback.format, pDevice->playback.channels), &framesConvertedIn, pFramesInInternalFormat, &framesConvertedOut); - - pDevice->playback.inputCacheConsumed += framesConvertedIn; - pDevice->playback.inputCacheRemaining -= framesConvertedIn; - - totalFramesReadOut += (ma_uint32)framesConvertedOut; /* Safe cast. */ - pFramesInInternalFormat = ma_offset_ptr(pFramesInInternalFormat, framesConvertedOut * ma_get_bytes_per_frame(pDevice->playback.internalFormat, pDevice->playback.internalChannels)); - } - - /* If there's no more data in the cache we'll need to fill it with some. */ - if (totalFramesReadOut < frameCount && pDevice->playback.inputCacheRemaining == 0) { - ma_uint32 inputFrameCount; - void* pInputFrames; - - inputFrameCount = (ma_uint32)pDevice->playback.inputCacheCap; - result = ma_pcm_rb_acquire_read(pRB, &inputFrameCount, &pInputFrames); - if (result == MA_SUCCESS) { - if (inputFrameCount > 0) { - ma_device__handle_data_callback(pDevice, pDevice->playback.pInputCache, pInputFrames, inputFrameCount); - } else { - if (ma_pcm_rb_pointer_distance(pRB) == 0) { - break; /* Underrun. */ - } - } - } else { - /* No capture data available. Feed in silence. */ - inputFrameCount = (ma_uint32)ma_min(pDevice->playback.inputCacheCap, sizeof(silentInputFrames) / ma_get_bytes_per_frame(pDevice->capture.format, pDevice->capture.channels)); - ma_device__handle_data_callback(pDevice, pDevice->playback.pInputCache, silentInputFrames, inputFrameCount); - } - - pDevice->playback.inputCacheConsumed = 0; - pDevice->playback.inputCacheRemaining = inputFrameCount; - - result = ma_pcm_rb_commit_read(pRB, inputFrameCount); - if (result != MA_SUCCESS) { - return result; /* Should never happen. */ - } - } - } - - return MA_SUCCESS; -} - -/* A helper for changing the state of the device. */ -static MA_INLINE void ma_device__set_state(ma_device* pDevice, ma_device_state newState) -{ - ma_atomic_device_state_set(&pDevice->state, newState); -} - - -#if defined(MA_WIN32) - static GUID MA_GUID_KSDATAFORMAT_SUBTYPE_PCM = {0x00000001, 0x0000, 0x0010, {0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71}}; - static GUID MA_GUID_KSDATAFORMAT_SUBTYPE_IEEE_FLOAT = {0x00000003, 0x0000, 0x0010, {0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71}}; - /*static GUID MA_GUID_KSDATAFORMAT_SUBTYPE_ALAW = {0x00000006, 0x0000, 0x0010, {0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71}};*/ - /*static GUID MA_GUID_KSDATAFORMAT_SUBTYPE_MULAW = {0x00000007, 0x0000, 0x0010, {0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71}};*/ -#endif - - - -MA_API ma_uint32 ma_get_format_priority_index(ma_format format) /* Lower = better. */ -{ - ma_uint32 i; - for (i = 0; i < ma_countof(g_maFormatPriorities); ++i) { - if (g_maFormatPriorities[i] == format) { - return i; - } - } - - /* Getting here means the format could not be found or is equal to ma_format_unknown. */ - return (ma_uint32)-1; -} - -static ma_result ma_device__post_init_setup(ma_device* pDevice, ma_device_type deviceType); - -static ma_bool32 ma_device_descriptor_is_valid(const ma_device_descriptor* pDeviceDescriptor) -{ - if (pDeviceDescriptor == NULL) { - return MA_FALSE; - } - - if (pDeviceDescriptor->format == ma_format_unknown) { - return MA_FALSE; - } - - if (pDeviceDescriptor->channels == 0 || pDeviceDescriptor->channels > MA_MAX_CHANNELS) { - return MA_FALSE; - } - - if (pDeviceDescriptor->sampleRate == 0) { - return MA_FALSE; - } - - return MA_TRUE; -} - - -static ma_result ma_device_audio_thread__default_read_write(ma_device* pDevice) -{ - ma_result result = MA_SUCCESS; - ma_bool32 exitLoop = MA_FALSE; - ma_uint8 capturedDeviceData[MA_DATA_CONVERTER_STACK_BUFFER_SIZE]; - ma_uint8 playbackDeviceData[MA_DATA_CONVERTER_STACK_BUFFER_SIZE]; - ma_uint32 capturedDeviceDataCapInFrames = 0; - ma_uint32 playbackDeviceDataCapInFrames = 0; - - MA_ASSERT(pDevice != NULL); - - /* Just some quick validation on the device type and the available callbacks. */ - if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex || pDevice->type == ma_device_type_loopback) { - if (pDevice->pContext->callbacks.onDeviceRead == NULL) { - return MA_NOT_IMPLEMENTED; - } - - capturedDeviceDataCapInFrames = sizeof(capturedDeviceData) / ma_get_bytes_per_frame(pDevice->capture.internalFormat, pDevice->capture.internalChannels); - } - - if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) { - if (pDevice->pContext->callbacks.onDeviceWrite == NULL) { - return MA_NOT_IMPLEMENTED; - } - - playbackDeviceDataCapInFrames = sizeof(playbackDeviceData) / ma_get_bytes_per_frame(pDevice->playback.internalFormat, pDevice->playback.internalChannels); - } - - /* NOTE: The device was started outside of this function, in the worker thread. */ - - while (ma_device_get_state(pDevice) == ma_device_state_started && !exitLoop) { - switch (pDevice->type) { - case ma_device_type_duplex: - { - /* The process is: onDeviceRead() -> convert -> callback -> convert -> onDeviceWrite() */ - ma_uint32 totalCapturedDeviceFramesProcessed = 0; - ma_uint32 capturedDevicePeriodSizeInFrames = ma_min(pDevice->capture.internalPeriodSizeInFrames, pDevice->playback.internalPeriodSizeInFrames); - - while (totalCapturedDeviceFramesProcessed < capturedDevicePeriodSizeInFrames) { - ma_uint32 capturedDeviceFramesRemaining; - ma_uint32 capturedDeviceFramesProcessed; - ma_uint32 capturedDeviceFramesToProcess; - ma_uint32 capturedDeviceFramesToTryProcessing = capturedDevicePeriodSizeInFrames - totalCapturedDeviceFramesProcessed; - if (capturedDeviceFramesToTryProcessing > capturedDeviceDataCapInFrames) { - capturedDeviceFramesToTryProcessing = capturedDeviceDataCapInFrames; - } - - result = pDevice->pContext->callbacks.onDeviceRead(pDevice, capturedDeviceData, capturedDeviceFramesToTryProcessing, &capturedDeviceFramesToProcess); - if (result != MA_SUCCESS) { - exitLoop = MA_TRUE; - break; - } - - capturedDeviceFramesRemaining = capturedDeviceFramesToProcess; - capturedDeviceFramesProcessed = 0; - - /* At this point we have our captured data in device format and we now need to convert it to client format. */ - for (;;) { - ma_uint8 capturedClientData[MA_DATA_CONVERTER_STACK_BUFFER_SIZE]; - ma_uint8 playbackClientData[MA_DATA_CONVERTER_STACK_BUFFER_SIZE]; - ma_uint32 capturedClientDataCapInFrames = sizeof(capturedClientData) / ma_get_bytes_per_frame(pDevice->capture.format, pDevice->capture.channels); - ma_uint32 playbackClientDataCapInFrames = sizeof(playbackClientData) / ma_get_bytes_per_frame(pDevice->playback.format, pDevice->playback.channels); - ma_uint64 capturedClientFramesToProcessThisIteration = ma_min(capturedClientDataCapInFrames, playbackClientDataCapInFrames); - ma_uint64 capturedDeviceFramesToProcessThisIteration = capturedDeviceFramesRemaining; - ma_uint8* pRunningCapturedDeviceFrames = ma_offset_ptr(capturedDeviceData, capturedDeviceFramesProcessed * ma_get_bytes_per_frame(pDevice->capture.internalFormat, pDevice->capture.internalChannels)); - - /* Convert capture data from device format to client format. */ - result = ma_data_converter_process_pcm_frames(&pDevice->capture.converter, pRunningCapturedDeviceFrames, &capturedDeviceFramesToProcessThisIteration, capturedClientData, &capturedClientFramesToProcessThisIteration); - if (result != MA_SUCCESS) { - break; - } - - /* - If we weren't able to generate any output frames it must mean we've exhausted all of our input. The only time this would not be the case is if capturedClientData was too small - which should never be the case when it's of the size MA_DATA_CONVERTER_STACK_BUFFER_SIZE. - */ - if (capturedClientFramesToProcessThisIteration == 0) { - break; - } - - ma_device__handle_data_callback(pDevice, playbackClientData, capturedClientData, (ma_uint32)capturedClientFramesToProcessThisIteration); /* Safe cast .*/ - - capturedDeviceFramesProcessed += (ma_uint32)capturedDeviceFramesToProcessThisIteration; /* Safe cast. */ - capturedDeviceFramesRemaining -= (ma_uint32)capturedDeviceFramesToProcessThisIteration; /* Safe cast. */ - - /* At this point the playbackClientData buffer should be holding data that needs to be written to the device. */ - for (;;) { - ma_uint64 convertedClientFrameCount = capturedClientFramesToProcessThisIteration; - ma_uint64 convertedDeviceFrameCount = playbackDeviceDataCapInFrames; - result = ma_data_converter_process_pcm_frames(&pDevice->playback.converter, playbackClientData, &convertedClientFrameCount, playbackDeviceData, &convertedDeviceFrameCount); - if (result != MA_SUCCESS) { - break; - } - - result = pDevice->pContext->callbacks.onDeviceWrite(pDevice, playbackDeviceData, (ma_uint32)convertedDeviceFrameCount, NULL); /* Safe cast. */ - if (result != MA_SUCCESS) { - exitLoop = MA_TRUE; - break; - } - - capturedClientFramesToProcessThisIteration -= (ma_uint32)convertedClientFrameCount; /* Safe cast. */ - if (capturedClientFramesToProcessThisIteration == 0) { - break; - } - } - - /* In case an error happened from ma_device_write__null()... */ - if (result != MA_SUCCESS) { - exitLoop = MA_TRUE; - break; - } - } - - /* Make sure we don't get stuck in the inner loop. */ - if (capturedDeviceFramesProcessed == 0) { - break; - } - - totalCapturedDeviceFramesProcessed += capturedDeviceFramesProcessed; - } - } break; - - case ma_device_type_capture: - case ma_device_type_loopback: - { - ma_uint32 periodSizeInFrames = pDevice->capture.internalPeriodSizeInFrames; - ma_uint32 framesReadThisPeriod = 0; - while (framesReadThisPeriod < periodSizeInFrames) { - ma_uint32 framesRemainingInPeriod = periodSizeInFrames - framesReadThisPeriod; - ma_uint32 framesProcessed; - ma_uint32 framesToReadThisIteration = framesRemainingInPeriod; - if (framesToReadThisIteration > capturedDeviceDataCapInFrames) { - framesToReadThisIteration = capturedDeviceDataCapInFrames; - } - - result = pDevice->pContext->callbacks.onDeviceRead(pDevice, capturedDeviceData, framesToReadThisIteration, &framesProcessed); - if (result != MA_SUCCESS) { - exitLoop = MA_TRUE; - break; - } - - /* Make sure we don't get stuck in the inner loop. */ - if (framesProcessed == 0) { - break; - } - - ma_device__send_frames_to_client(pDevice, framesProcessed, capturedDeviceData); - - framesReadThisPeriod += framesProcessed; - } - } break; - - case ma_device_type_playback: - { - /* We write in chunks of the period size, but use a stack allocated buffer for the intermediary. */ - ma_uint32 periodSizeInFrames = pDevice->playback.internalPeriodSizeInFrames; - ma_uint32 framesWrittenThisPeriod = 0; - while (framesWrittenThisPeriod < periodSizeInFrames) { - ma_uint32 framesRemainingInPeriod = periodSizeInFrames - framesWrittenThisPeriod; - ma_uint32 framesProcessed; - ma_uint32 framesToWriteThisIteration = framesRemainingInPeriod; - if (framesToWriteThisIteration > playbackDeviceDataCapInFrames) { - framesToWriteThisIteration = playbackDeviceDataCapInFrames; - } - - ma_device__read_frames_from_client(pDevice, framesToWriteThisIteration, playbackDeviceData); - - result = pDevice->pContext->callbacks.onDeviceWrite(pDevice, playbackDeviceData, framesToWriteThisIteration, &framesProcessed); - if (result != MA_SUCCESS) { - exitLoop = MA_TRUE; - break; - } - - /* Make sure we don't get stuck in the inner loop. */ - if (framesProcessed == 0) { - break; - } - - framesWrittenThisPeriod += framesProcessed; - } - } break; - - /* Should never get here. */ - default: break; - } - } - - return result; -} - - - -/******************************************************************************* - -Null Backend - -*******************************************************************************/ -#ifdef MA_HAS_NULL - -#define MA_DEVICE_OP_NONE__NULL 0 -#define MA_DEVICE_OP_START__NULL 1 -#define MA_DEVICE_OP_SUSPEND__NULL 2 -#define MA_DEVICE_OP_KILL__NULL 3 - -static ma_thread_result MA_THREADCALL ma_device_thread__null(void* pData) -{ - ma_device* pDevice = (ma_device*)pData; - MA_ASSERT(pDevice != NULL); - - for (;;) { /* Keep the thread alive until the device is uninitialized. */ - ma_uint32 operation; - - /* Wait for an operation to be requested. */ - ma_event_wait(&pDevice->null_device.operationEvent); - - /* At this point an event should have been triggered. */ - operation = pDevice->null_device.operation; - - /* Starting the device needs to put the thread into a loop. */ - if (operation == MA_DEVICE_OP_START__NULL) { - /* Reset the timer just in case. */ - ma_timer_init(&pDevice->null_device.timer); - - /* Getting here means a suspend or kill operation has been requested. */ - pDevice->null_device.operationResult = MA_SUCCESS; - ma_event_signal(&pDevice->null_device.operationCompletionEvent); - ma_semaphore_release(&pDevice->null_device.operationSemaphore); - continue; - } - - /* Suspending the device means we need to stop the timer and just continue the loop. */ - if (operation == MA_DEVICE_OP_SUSPEND__NULL) { - /* We need to add the current run time to the prior run time, then reset the timer. */ - pDevice->null_device.priorRunTime += ma_timer_get_time_in_seconds(&pDevice->null_device.timer); - ma_timer_init(&pDevice->null_device.timer); - - /* We're done. */ - pDevice->null_device.operationResult = MA_SUCCESS; - ma_event_signal(&pDevice->null_device.operationCompletionEvent); - ma_semaphore_release(&pDevice->null_device.operationSemaphore); - continue; - } - - /* Killing the device means we need to get out of this loop so that this thread can terminate. */ - if (operation == MA_DEVICE_OP_KILL__NULL) { - pDevice->null_device.operationResult = MA_SUCCESS; - ma_event_signal(&pDevice->null_device.operationCompletionEvent); - ma_semaphore_release(&pDevice->null_device.operationSemaphore); - break; - } - - /* Getting a signal on a "none" operation probably means an error. Return invalid operation. */ - if (operation == MA_DEVICE_OP_NONE__NULL) { - MA_ASSERT(MA_FALSE); /* <-- Trigger this in debug mode to ensure developers are aware they're doing something wrong (or there's a bug in a miniaudio). */ - pDevice->null_device.operationResult = MA_INVALID_OPERATION; - ma_event_signal(&pDevice->null_device.operationCompletionEvent); - ma_semaphore_release(&pDevice->null_device.operationSemaphore); - continue; /* Continue the loop. Don't terminate. */ - } - } - - return (ma_thread_result)0; -} - -static ma_result ma_device_do_operation__null(ma_device* pDevice, ma_uint32 operation) -{ - ma_result result; - - /* - TODO: Need to review this and consider just using mutual exclusion. I think the original motivation - for this was to just post the event to a queue and return immediately, but that has since changed - and now this function is synchronous. I think this can be simplified to just use a mutex. - */ - - /* - The first thing to do is wait for an operation slot to become available. We only have a single slot for this, but we could extend this later - to support queuing of operations. - */ - result = ma_semaphore_wait(&pDevice->null_device.operationSemaphore); - if (result != MA_SUCCESS) { - return result; /* Failed to wait for the event. */ - } - - /* - When we get here it means the background thread is not referencing the operation code and it can be changed. After changing this we need to - signal an event to the worker thread to let it know that it can start work. - */ - pDevice->null_device.operation = operation; - - /* Once the operation code has been set, the worker thread can start work. */ - if (ma_event_signal(&pDevice->null_device.operationEvent) != MA_SUCCESS) { - return MA_ERROR; - } - - /* We want everything to be synchronous so we're going to wait for the worker thread to complete it's operation. */ - if (ma_event_wait(&pDevice->null_device.operationCompletionEvent) != MA_SUCCESS) { - return MA_ERROR; - } - - return pDevice->null_device.operationResult; -} - -static ma_uint64 ma_device_get_total_run_time_in_frames__null(ma_device* pDevice) -{ - ma_uint32 internalSampleRate; - if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) { - internalSampleRate = pDevice->capture.internalSampleRate; - } else { - internalSampleRate = pDevice->playback.internalSampleRate; - } - - return (ma_uint64)((pDevice->null_device.priorRunTime + ma_timer_get_time_in_seconds(&pDevice->null_device.timer)) * internalSampleRate); -} - -static ma_result ma_context_enumerate_devices__null(ma_context* pContext, ma_enum_devices_callback_proc callback, void* pUserData) -{ - ma_bool32 cbResult = MA_TRUE; - - MA_ASSERT(pContext != NULL); - MA_ASSERT(callback != NULL); - - /* Playback. */ - if (cbResult) { - ma_device_info deviceInfo; - MA_ZERO_OBJECT(&deviceInfo); - ma_strncpy_s(deviceInfo.name, sizeof(deviceInfo.name), "NULL Playback Device", (size_t)-1); - deviceInfo.isDefault = MA_TRUE; /* Only one playback and capture device for the null backend, so might as well mark as default. */ - cbResult = callback(pContext, ma_device_type_playback, &deviceInfo, pUserData); - } - - /* Capture. */ - if (cbResult) { - ma_device_info deviceInfo; - MA_ZERO_OBJECT(&deviceInfo); - ma_strncpy_s(deviceInfo.name, sizeof(deviceInfo.name), "NULL Capture Device", (size_t)-1); - deviceInfo.isDefault = MA_TRUE; /* Only one playback and capture device for the null backend, so might as well mark as default. */ - cbResult = callback(pContext, ma_device_type_capture, &deviceInfo, pUserData); - } - - (void)cbResult; /* Silence a static analysis warning. */ - - return MA_SUCCESS; -} - -static ma_result ma_context_get_device_info__null(ma_context* pContext, ma_device_type deviceType, const ma_device_id* pDeviceID, ma_device_info* pDeviceInfo) -{ - MA_ASSERT(pContext != NULL); - - if (pDeviceID != NULL && pDeviceID->nullbackend != 0) { - return MA_NO_DEVICE; /* Don't know the device. */ - } - - /* Name / Description */ - if (deviceType == ma_device_type_playback) { - ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), "NULL Playback Device", (size_t)-1); - } else { - ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), "NULL Capture Device", (size_t)-1); - } - - pDeviceInfo->isDefault = MA_TRUE; /* Only one playback and capture device for the null backend, so might as well mark as default. */ - - /* Support everything on the null backend. */ - pDeviceInfo->nativeDataFormats[0].format = ma_format_unknown; - pDeviceInfo->nativeDataFormats[0].channels = 0; - pDeviceInfo->nativeDataFormats[0].sampleRate = 0; - pDeviceInfo->nativeDataFormats[0].flags = 0; - pDeviceInfo->nativeDataFormatCount = 1; - - (void)pContext; - return MA_SUCCESS; -} - - -static ma_result ma_device_uninit__null(ma_device* pDevice) -{ - MA_ASSERT(pDevice != NULL); - - /* Keep it clean and wait for the device thread to finish before returning. */ - ma_device_do_operation__null(pDevice, MA_DEVICE_OP_KILL__NULL); - - /* Wait for the thread to finish before continuing. */ - ma_thread_wait(&pDevice->null_device.deviceThread); - - /* At this point the loop in the device thread is as good as terminated so we can uninitialize our events. */ - ma_semaphore_uninit(&pDevice->null_device.operationSemaphore); - ma_event_uninit(&pDevice->null_device.operationCompletionEvent); - ma_event_uninit(&pDevice->null_device.operationEvent); - - return MA_SUCCESS; -} - -static ma_result ma_device_init__null(ma_device* pDevice, const ma_device_config* pConfig, ma_device_descriptor* pDescriptorPlayback, ma_device_descriptor* pDescriptorCapture) -{ - ma_result result; - - MA_ASSERT(pDevice != NULL); - - MA_ZERO_OBJECT(&pDevice->null_device); - - if (pConfig->deviceType == ma_device_type_loopback) { - return MA_DEVICE_TYPE_NOT_SUPPORTED; - } - - /* The null backend supports everything exactly as we specify it. */ - if (pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) { - pDescriptorCapture->format = (pDescriptorCapture->format != ma_format_unknown) ? pDescriptorCapture->format : MA_DEFAULT_FORMAT; - pDescriptorCapture->channels = (pDescriptorCapture->channels != 0) ? pDescriptorCapture->channels : MA_DEFAULT_CHANNELS; - pDescriptorCapture->sampleRate = (pDescriptorCapture->sampleRate != 0) ? pDescriptorCapture->sampleRate : MA_DEFAULT_SAMPLE_RATE; - - if (pDescriptorCapture->channelMap[0] == MA_CHANNEL_NONE) { - ma_channel_map_init_standard(ma_standard_channel_map_default, pDescriptorCapture->channelMap, ma_countof(pDescriptorCapture->channelMap), pDescriptorCapture->channels); - } - - pDescriptorCapture->periodSizeInFrames = ma_calculate_buffer_size_in_frames_from_descriptor(pDescriptorCapture, pDescriptorCapture->sampleRate, pConfig->performanceProfile); - } - - if (pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) { - pDescriptorPlayback->format = (pDescriptorPlayback->format != ma_format_unknown) ? pDescriptorPlayback->format : MA_DEFAULT_FORMAT; - pDescriptorPlayback->channels = (pDescriptorPlayback->channels != 0) ? pDescriptorPlayback->channels : MA_DEFAULT_CHANNELS; - pDescriptorPlayback->sampleRate = (pDescriptorPlayback->sampleRate != 0) ? pDescriptorPlayback->sampleRate : MA_DEFAULT_SAMPLE_RATE; - - if (pDescriptorPlayback->channelMap[0] == MA_CHANNEL_NONE) { - ma_channel_map_init_standard(ma_standard_channel_map_default, pDescriptorPlayback->channelMap, ma_countof(pDescriptorCapture->channelMap), pDescriptorPlayback->channels); - } - - pDescriptorPlayback->periodSizeInFrames = ma_calculate_buffer_size_in_frames_from_descriptor(pDescriptorPlayback, pDescriptorPlayback->sampleRate, pConfig->performanceProfile); - } - - /* - In order to get timing right, we need to create a thread that does nothing but keeps track of the timer. This timer is started when the - first period is "written" to it, and then stopped in ma_device_stop__null(). - */ - result = ma_event_init(&pDevice->null_device.operationEvent); - if (result != MA_SUCCESS) { - return result; - } - - result = ma_event_init(&pDevice->null_device.operationCompletionEvent); - if (result != MA_SUCCESS) { - return result; - } - - result = ma_semaphore_init(1, &pDevice->null_device.operationSemaphore); /* <-- It's important that the initial value is set to 1. */ - if (result != MA_SUCCESS) { - return result; - } - - result = ma_thread_create(&pDevice->null_device.deviceThread, pDevice->pContext->threadPriority, 0, ma_device_thread__null, pDevice, &pDevice->pContext->allocationCallbacks); - if (result != MA_SUCCESS) { - return result; - } - - return MA_SUCCESS; -} - -static ma_result ma_device_start__null(ma_device* pDevice) -{ - MA_ASSERT(pDevice != NULL); - - ma_device_do_operation__null(pDevice, MA_DEVICE_OP_START__NULL); - - ma_atomic_bool32_set(&pDevice->null_device.isStarted, MA_TRUE); - return MA_SUCCESS; -} - -static ma_result ma_device_stop__null(ma_device* pDevice) -{ - MA_ASSERT(pDevice != NULL); - - ma_device_do_operation__null(pDevice, MA_DEVICE_OP_SUSPEND__NULL); - - ma_atomic_bool32_set(&pDevice->null_device.isStarted, MA_FALSE); - return MA_SUCCESS; -} - -static ma_bool32 ma_device_is_started__null(ma_device* pDevice) -{ - MA_ASSERT(pDevice != NULL); - - return ma_atomic_bool32_get(&pDevice->null_device.isStarted); -} - -static ma_result ma_device_write__null(ma_device* pDevice, const void* pPCMFrames, ma_uint32 frameCount, ma_uint32* pFramesWritten) -{ - ma_result result = MA_SUCCESS; - ma_uint32 totalPCMFramesProcessed; - ma_bool32 wasStartedOnEntry; - - if (pFramesWritten != NULL) { - *pFramesWritten = 0; - } - - wasStartedOnEntry = ma_device_is_started__null(pDevice); - - /* Keep going until everything has been read. */ - totalPCMFramesProcessed = 0; - while (totalPCMFramesProcessed < frameCount) { - ma_uint64 targetFrame; - - /* If there are any frames remaining in the current period, consume those first. */ - if (pDevice->null_device.currentPeriodFramesRemainingPlayback > 0) { - ma_uint32 framesRemaining = (frameCount - totalPCMFramesProcessed); - ma_uint32 framesToProcess = pDevice->null_device.currentPeriodFramesRemainingPlayback; - if (framesToProcess > framesRemaining) { - framesToProcess = framesRemaining; - } - - /* We don't actually do anything with pPCMFrames, so just mark it as unused to prevent a warning. */ - (void)pPCMFrames; - - pDevice->null_device.currentPeriodFramesRemainingPlayback -= framesToProcess; - totalPCMFramesProcessed += framesToProcess; - } - - /* If we've consumed the current period we'll need to mark it as such an ensure the device is started if it's not already. */ - if (pDevice->null_device.currentPeriodFramesRemainingPlayback == 0) { - pDevice->null_device.currentPeriodFramesRemainingPlayback = 0; - - if (!ma_device_is_started__null(pDevice) && !wasStartedOnEntry) { - result = ma_device_start__null(pDevice); - if (result != MA_SUCCESS) { - break; - } - } - } - - /* If we've consumed the whole buffer we can return now. */ - MA_ASSERT(totalPCMFramesProcessed <= frameCount); - if (totalPCMFramesProcessed == frameCount) { - break; - } - - /* Getting here means we've still got more frames to consume, we but need to wait for it to become available. */ - targetFrame = pDevice->null_device.lastProcessedFramePlayback; - for (;;) { - ma_uint64 currentFrame; - - /* Stop waiting if the device has been stopped. */ - if (!ma_device_is_started__null(pDevice)) { - break; - } - - currentFrame = ma_device_get_total_run_time_in_frames__null(pDevice); - if (currentFrame >= targetFrame) { - break; - } - - /* Getting here means we haven't yet reached the target sample, so continue waiting. */ - ma_sleep(10); - } - - pDevice->null_device.lastProcessedFramePlayback += pDevice->playback.internalPeriodSizeInFrames; - pDevice->null_device.currentPeriodFramesRemainingPlayback = pDevice->playback.internalPeriodSizeInFrames; - } - - if (pFramesWritten != NULL) { - *pFramesWritten = totalPCMFramesProcessed; - } - - return result; -} - -static ma_result ma_device_read__null(ma_device* pDevice, void* pPCMFrames, ma_uint32 frameCount, ma_uint32* pFramesRead) -{ - ma_result result = MA_SUCCESS; - ma_uint32 totalPCMFramesProcessed; - - if (pFramesRead != NULL) { - *pFramesRead = 0; - } - - /* Keep going until everything has been read. */ - totalPCMFramesProcessed = 0; - while (totalPCMFramesProcessed < frameCount) { - ma_uint64 targetFrame; - - /* If there are any frames remaining in the current period, consume those first. */ - if (pDevice->null_device.currentPeriodFramesRemainingCapture > 0) { - ma_uint32 bpf = ma_get_bytes_per_frame(pDevice->capture.internalFormat, pDevice->capture.internalChannels); - ma_uint32 framesRemaining = (frameCount - totalPCMFramesProcessed); - ma_uint32 framesToProcess = pDevice->null_device.currentPeriodFramesRemainingCapture; - if (framesToProcess > framesRemaining) { - framesToProcess = framesRemaining; - } - - /* We need to ensure the output buffer is zeroed. */ - MA_ZERO_MEMORY(ma_offset_ptr(pPCMFrames, totalPCMFramesProcessed*bpf), framesToProcess*bpf); - - pDevice->null_device.currentPeriodFramesRemainingCapture -= framesToProcess; - totalPCMFramesProcessed += framesToProcess; - } - - /* If we've consumed the current period we'll need to mark it as such an ensure the device is started if it's not already. */ - if (pDevice->null_device.currentPeriodFramesRemainingCapture == 0) { - pDevice->null_device.currentPeriodFramesRemainingCapture = 0; - } - - /* If we've consumed the whole buffer we can return now. */ - MA_ASSERT(totalPCMFramesProcessed <= frameCount); - if (totalPCMFramesProcessed == frameCount) { - break; - } - - /* Getting here means we've still got more frames to consume, we but need to wait for it to become available. */ - targetFrame = pDevice->null_device.lastProcessedFrameCapture + pDevice->capture.internalPeriodSizeInFrames; - for (;;) { - ma_uint64 currentFrame; - - /* Stop waiting if the device has been stopped. */ - if (!ma_device_is_started__null(pDevice)) { - break; - } - - currentFrame = ma_device_get_total_run_time_in_frames__null(pDevice); - if (currentFrame >= targetFrame) { - break; - } - - /* Getting here means we haven't yet reached the target sample, so continue waiting. */ - ma_sleep(10); - } - - pDevice->null_device.lastProcessedFrameCapture += pDevice->capture.internalPeriodSizeInFrames; - pDevice->null_device.currentPeriodFramesRemainingCapture = pDevice->capture.internalPeriodSizeInFrames; - } - - if (pFramesRead != NULL) { - *pFramesRead = totalPCMFramesProcessed; - } - - return result; -} - -static ma_result ma_context_uninit__null(ma_context* pContext) -{ - MA_ASSERT(pContext != NULL); - MA_ASSERT(pContext->backend == ma_backend_null); - - (void)pContext; - return MA_SUCCESS; -} - -static ma_result ma_context_init__null(ma_context* pContext, const ma_context_config* pConfig, ma_backend_callbacks* pCallbacks) -{ - MA_ASSERT(pContext != NULL); - - (void)pConfig; - (void)pContext; - - pCallbacks->onContextInit = ma_context_init__null; - pCallbacks->onContextUninit = ma_context_uninit__null; - pCallbacks->onContextEnumerateDevices = ma_context_enumerate_devices__null; - pCallbacks->onContextGetDeviceInfo = ma_context_get_device_info__null; - pCallbacks->onDeviceInit = ma_device_init__null; - pCallbacks->onDeviceUninit = ma_device_uninit__null; - pCallbacks->onDeviceStart = ma_device_start__null; - pCallbacks->onDeviceStop = ma_device_stop__null; - pCallbacks->onDeviceRead = ma_device_read__null; - pCallbacks->onDeviceWrite = ma_device_write__null; - pCallbacks->onDeviceDataLoop = NULL; /* Our backend is asynchronous with a blocking read-write API which means we can get miniaudio to deal with the audio thread. */ - - /* The null backend always works. */ - return MA_SUCCESS; -} -#endif - - - -/******************************************************************************* - -WIN32 COMMON - -*******************************************************************************/ -#if defined(MA_WIN32) -#if defined(MA_WIN32_DESKTOP) || defined(MA_WIN32_GDK) - #define ma_CoInitializeEx(pContext, pvReserved, dwCoInit) ((pContext->win32.CoInitializeEx) ? ((MA_PFN_CoInitializeEx)pContext->win32.CoInitializeEx)(pvReserved, dwCoInit) : ((MA_PFN_CoInitialize)pContext->win32.CoInitialize)(pvReserved)) - #define ma_CoUninitialize(pContext) ((MA_PFN_CoUninitialize)pContext->win32.CoUninitialize)() - #define ma_CoCreateInstance(pContext, rclsid, pUnkOuter, dwClsContext, riid, ppv) ((MA_PFN_CoCreateInstance)pContext->win32.CoCreateInstance)(rclsid, pUnkOuter, dwClsContext, riid, ppv) - #define ma_CoTaskMemFree(pContext, pv) ((MA_PFN_CoTaskMemFree)pContext->win32.CoTaskMemFree)(pv) - #define ma_PropVariantClear(pContext, pvar) ((MA_PFN_PropVariantClear)pContext->win32.PropVariantClear)(pvar) -#else - #define ma_CoInitializeEx(pContext, pvReserved, dwCoInit) CoInitializeEx(pvReserved, dwCoInit) - #define ma_CoUninitialize(pContext) CoUninitialize() - #define ma_CoCreateInstance(pContext, rclsid, pUnkOuter, dwClsContext, riid, ppv) CoCreateInstance(rclsid, pUnkOuter, dwClsContext, riid, ppv) - #define ma_CoTaskMemFree(pContext, pv) CoTaskMemFree(pv) - #define ma_PropVariantClear(pContext, pvar) PropVariantClear(pvar) -#endif - -#if !defined(MAXULONG_PTR) && !defined(__WATCOMC__) -typedef size_t DWORD_PTR; -#endif - -#if !defined(WAVE_FORMAT_1M08) -#define WAVE_FORMAT_1M08 0x00000001 -#define WAVE_FORMAT_1S08 0x00000002 -#define WAVE_FORMAT_1M16 0x00000004 -#define WAVE_FORMAT_1S16 0x00000008 -#define WAVE_FORMAT_2M08 0x00000010 -#define WAVE_FORMAT_2S08 0x00000020 -#define WAVE_FORMAT_2M16 0x00000040 -#define WAVE_FORMAT_2S16 0x00000080 -#define WAVE_FORMAT_4M08 0x00000100 -#define WAVE_FORMAT_4S08 0x00000200 -#define WAVE_FORMAT_4M16 0x00000400 -#define WAVE_FORMAT_4S16 0x00000800 -#endif - -#if !defined(WAVE_FORMAT_44M08) -#define WAVE_FORMAT_44M08 0x00000100 -#define WAVE_FORMAT_44S08 0x00000200 -#define WAVE_FORMAT_44M16 0x00000400 -#define WAVE_FORMAT_44S16 0x00000800 -#define WAVE_FORMAT_48M08 0x00001000 -#define WAVE_FORMAT_48S08 0x00002000 -#define WAVE_FORMAT_48M16 0x00004000 -#define WAVE_FORMAT_48S16 0x00008000 -#define WAVE_FORMAT_96M08 0x00010000 -#define WAVE_FORMAT_96S08 0x00020000 -#define WAVE_FORMAT_96M16 0x00040000 -#define WAVE_FORMAT_96S16 0x00080000 -#endif - -#ifndef SPEAKER_FRONT_LEFT -#define SPEAKER_FRONT_LEFT 0x1 -#define SPEAKER_FRONT_RIGHT 0x2 -#define SPEAKER_FRONT_CENTER 0x4 -#define SPEAKER_LOW_FREQUENCY 0x8 -#define SPEAKER_BACK_LEFT 0x10 -#define SPEAKER_BACK_RIGHT 0x20 -#define SPEAKER_FRONT_LEFT_OF_CENTER 0x40 -#define SPEAKER_FRONT_RIGHT_OF_CENTER 0x80 -#define SPEAKER_BACK_CENTER 0x100 -#define SPEAKER_SIDE_LEFT 0x200 -#define SPEAKER_SIDE_RIGHT 0x400 -#define SPEAKER_TOP_CENTER 0x800 -#define SPEAKER_TOP_FRONT_LEFT 0x1000 -#define SPEAKER_TOP_FRONT_CENTER 0x2000 -#define SPEAKER_TOP_FRONT_RIGHT 0x4000 -#define SPEAKER_TOP_BACK_LEFT 0x8000 -#define SPEAKER_TOP_BACK_CENTER 0x10000 -#define SPEAKER_TOP_BACK_RIGHT 0x20000 -#endif - -/* -Implement our own version of MA_WAVEFORMATEXTENSIBLE so we can avoid a header. Be careful with this -because MA_WAVEFORMATEX has an extra two bytes over standard WAVEFORMATEX due to padding. The -standard version uses tight packing, but for compiler compatibility we're not doing that with ours. -*/ -typedef struct -{ - WORD wFormatTag; - WORD nChannels; - DWORD nSamplesPerSec; - DWORD nAvgBytesPerSec; - WORD nBlockAlign; - WORD wBitsPerSample; - WORD cbSize; -} MA_WAVEFORMATEX; - -typedef struct -{ - WORD wFormatTag; - WORD nChannels; - DWORD nSamplesPerSec; - DWORD nAvgBytesPerSec; - WORD nBlockAlign; - WORD wBitsPerSample; - WORD cbSize; - union - { - WORD wValidBitsPerSample; - WORD wSamplesPerBlock; - WORD wReserved; - } Samples; - DWORD dwChannelMask; - GUID SubFormat; -} MA_WAVEFORMATEXTENSIBLE; - - - -#ifndef WAVE_FORMAT_EXTENSIBLE -#define WAVE_FORMAT_EXTENSIBLE 0xFFFE -#endif - -#ifndef WAVE_FORMAT_PCM -#define WAVE_FORMAT_PCM 1 -#endif - -#ifndef WAVE_FORMAT_IEEE_FLOAT -#define WAVE_FORMAT_IEEE_FLOAT 0x0003 -#endif - -/* Converts an individual Win32-style channel identifier (SPEAKER_FRONT_LEFT, etc.) to miniaudio. */ -static ma_uint8 ma_channel_id_to_ma__win32(DWORD id) -{ - switch (id) - { - case SPEAKER_FRONT_LEFT: return MA_CHANNEL_FRONT_LEFT; - case SPEAKER_FRONT_RIGHT: return MA_CHANNEL_FRONT_RIGHT; - case SPEAKER_FRONT_CENTER: return MA_CHANNEL_FRONT_CENTER; - case SPEAKER_LOW_FREQUENCY: return MA_CHANNEL_LFE; - case SPEAKER_BACK_LEFT: return MA_CHANNEL_BACK_LEFT; - case SPEAKER_BACK_RIGHT: return MA_CHANNEL_BACK_RIGHT; - case SPEAKER_FRONT_LEFT_OF_CENTER: return MA_CHANNEL_FRONT_LEFT_CENTER; - case SPEAKER_FRONT_RIGHT_OF_CENTER: return MA_CHANNEL_FRONT_RIGHT_CENTER; - case SPEAKER_BACK_CENTER: return MA_CHANNEL_BACK_CENTER; - case SPEAKER_SIDE_LEFT: return MA_CHANNEL_SIDE_LEFT; - case SPEAKER_SIDE_RIGHT: return MA_CHANNEL_SIDE_RIGHT; - case SPEAKER_TOP_CENTER: return MA_CHANNEL_TOP_CENTER; - case SPEAKER_TOP_FRONT_LEFT: return MA_CHANNEL_TOP_FRONT_LEFT; - case SPEAKER_TOP_FRONT_CENTER: return MA_CHANNEL_TOP_FRONT_CENTER; - case SPEAKER_TOP_FRONT_RIGHT: return MA_CHANNEL_TOP_FRONT_RIGHT; - case SPEAKER_TOP_BACK_LEFT: return MA_CHANNEL_TOP_BACK_LEFT; - case SPEAKER_TOP_BACK_CENTER: return MA_CHANNEL_TOP_BACK_CENTER; - case SPEAKER_TOP_BACK_RIGHT: return MA_CHANNEL_TOP_BACK_RIGHT; - default: return 0; - } -} - -/* Converts an individual miniaudio channel identifier (MA_CHANNEL_FRONT_LEFT, etc.) to Win32-style. */ -static DWORD ma_channel_id_to_win32(DWORD id) -{ - switch (id) - { - case MA_CHANNEL_MONO: return SPEAKER_FRONT_CENTER; - case MA_CHANNEL_FRONT_LEFT: return SPEAKER_FRONT_LEFT; - case MA_CHANNEL_FRONT_RIGHT: return SPEAKER_FRONT_RIGHT; - case MA_CHANNEL_FRONT_CENTER: return SPEAKER_FRONT_CENTER; - case MA_CHANNEL_LFE: return SPEAKER_LOW_FREQUENCY; - case MA_CHANNEL_BACK_LEFT: return SPEAKER_BACK_LEFT; - case MA_CHANNEL_BACK_RIGHT: return SPEAKER_BACK_RIGHT; - case MA_CHANNEL_FRONT_LEFT_CENTER: return SPEAKER_FRONT_LEFT_OF_CENTER; - case MA_CHANNEL_FRONT_RIGHT_CENTER: return SPEAKER_FRONT_RIGHT_OF_CENTER; - case MA_CHANNEL_BACK_CENTER: return SPEAKER_BACK_CENTER; - case MA_CHANNEL_SIDE_LEFT: return SPEAKER_SIDE_LEFT; - case MA_CHANNEL_SIDE_RIGHT: return SPEAKER_SIDE_RIGHT; - case MA_CHANNEL_TOP_CENTER: return SPEAKER_TOP_CENTER; - case MA_CHANNEL_TOP_FRONT_LEFT: return SPEAKER_TOP_FRONT_LEFT; - case MA_CHANNEL_TOP_FRONT_CENTER: return SPEAKER_TOP_FRONT_CENTER; - case MA_CHANNEL_TOP_FRONT_RIGHT: return SPEAKER_TOP_FRONT_RIGHT; - case MA_CHANNEL_TOP_BACK_LEFT: return SPEAKER_TOP_BACK_LEFT; - case MA_CHANNEL_TOP_BACK_CENTER: return SPEAKER_TOP_BACK_CENTER; - case MA_CHANNEL_TOP_BACK_RIGHT: return SPEAKER_TOP_BACK_RIGHT; - default: return 0; - } -} - -/* Converts a channel mapping to a Win32-style channel mask. */ -static DWORD ma_channel_map_to_channel_mask__win32(const ma_channel* pChannelMap, ma_uint32 channels) -{ - DWORD dwChannelMask = 0; - ma_uint32 iChannel; - - for (iChannel = 0; iChannel < channels; ++iChannel) { - dwChannelMask |= ma_channel_id_to_win32(pChannelMap[iChannel]); - } - - return dwChannelMask; -} - -/* Converts a Win32-style channel mask to a miniaudio channel map. */ -static void ma_channel_mask_to_channel_map__win32(DWORD dwChannelMask, ma_uint32 channels, ma_channel* pChannelMap) -{ - /* If the channel mask is set to 0, just assume a default Win32 channel map. */ - if (dwChannelMask == 0) { - ma_channel_map_init_standard(ma_standard_channel_map_microsoft, pChannelMap, channels, channels); - } else { - if (channels == 1 && (dwChannelMask & SPEAKER_FRONT_CENTER) != 0) { - pChannelMap[0] = MA_CHANNEL_MONO; - } else { - /* Just iterate over each bit. */ - ma_uint32 iChannel = 0; - ma_uint32 iBit; - - for (iBit = 0; iBit < 32 && iChannel < channels; ++iBit) { - DWORD bitValue = (dwChannelMask & (1UL << iBit)); - if (bitValue != 0) { - /* The bit is set. */ - pChannelMap[iChannel] = ma_channel_id_to_ma__win32(bitValue); - iChannel += 1; - } - } - } - } -} - -#ifdef __cplusplus -static ma_bool32 ma_is_guid_equal(const void* a, const void* b) -{ - return IsEqualGUID(*(const GUID*)a, *(const GUID*)b); -} -#else -#define ma_is_guid_equal(a, b) IsEqualGUID((const GUID*)a, (const GUID*)b) -#endif - -static MA_INLINE ma_bool32 ma_is_guid_null(const void* guid) -{ - static GUID nullguid = {0x00000000, 0x0000, 0x0000, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}; - return ma_is_guid_equal(guid, &nullguid); -} - -static ma_format ma_format_from_WAVEFORMATEX(const MA_WAVEFORMATEX* pWF) -{ - MA_ASSERT(pWF != NULL); - - if (pWF->wFormatTag == WAVE_FORMAT_EXTENSIBLE) { - const MA_WAVEFORMATEXTENSIBLE* pWFEX = (const MA_WAVEFORMATEXTENSIBLE*)pWF; - if (ma_is_guid_equal(&pWFEX->SubFormat, &MA_GUID_KSDATAFORMAT_SUBTYPE_PCM)) { - if (pWFEX->Samples.wValidBitsPerSample == 32) { - return ma_format_s32; - } - if (pWFEX->Samples.wValidBitsPerSample == 24) { - if (pWFEX->wBitsPerSample == 32) { - return ma_format_s32; - } - if (pWFEX->wBitsPerSample == 24) { - return ma_format_s24; - } - } - if (pWFEX->Samples.wValidBitsPerSample == 16) { - return ma_format_s16; - } - if (pWFEX->Samples.wValidBitsPerSample == 8) { - return ma_format_u8; - } - } - if (ma_is_guid_equal(&pWFEX->SubFormat, &MA_GUID_KSDATAFORMAT_SUBTYPE_IEEE_FLOAT)) { - if (pWFEX->Samples.wValidBitsPerSample == 32) { - return ma_format_f32; - } - /* - if (pWFEX->Samples.wValidBitsPerSample == 64) { - return ma_format_f64; - } - */ - } - } else { - if (pWF->wFormatTag == WAVE_FORMAT_PCM) { - if (pWF->wBitsPerSample == 32) { - return ma_format_s32; - } - if (pWF->wBitsPerSample == 24) { - return ma_format_s24; - } - if (pWF->wBitsPerSample == 16) { - return ma_format_s16; - } - if (pWF->wBitsPerSample == 8) { - return ma_format_u8; - } - } - if (pWF->wFormatTag == WAVE_FORMAT_IEEE_FLOAT) { - if (pWF->wBitsPerSample == 32) { - return ma_format_f32; - } - if (pWF->wBitsPerSample == 64) { - /*return ma_format_f64;*/ - } - } - } - - return ma_format_unknown; -} -#endif - - -/******************************************************************************* - -WASAPI Backend - -*******************************************************************************/ -#ifdef MA_HAS_WASAPI -#if 0 -#if defined(_MSC_VER) - #pragma warning(push) - #pragma warning(disable:4091) /* 'typedef ': ignored on left of '' when no variable is declared */ -#endif -#include -#include -#if defined(_MSC_VER) - #pragma warning(pop) -#endif -#endif /* 0 */ - -static ma_result ma_device_reroute__wasapi(ma_device* pDevice, ma_device_type deviceType); - -/* Some compilers don't define VerifyVersionInfoW. Need to write this ourselves. */ -#define MA_WIN32_WINNT_VISTA 0x0600 -#define MA_VER_MINORVERSION 0x01 -#define MA_VER_MAJORVERSION 0x02 -#define MA_VER_SERVICEPACKMAJOR 0x20 -#define MA_VER_GREATER_EQUAL 0x03 - -typedef struct { - DWORD dwOSVersionInfoSize; - DWORD dwMajorVersion; - DWORD dwMinorVersion; - DWORD dwBuildNumber; - DWORD dwPlatformId; - WCHAR szCSDVersion[128]; - WORD wServicePackMajor; - WORD wServicePackMinor; - WORD wSuiteMask; - BYTE wProductType; - BYTE wReserved; -} ma_OSVERSIONINFOEXW; - -typedef BOOL (WINAPI * ma_PFNVerifyVersionInfoW) (ma_OSVERSIONINFOEXW* lpVersionInfo, DWORD dwTypeMask, DWORDLONG dwlConditionMask); -typedef ULONGLONG (WINAPI * ma_PFNVerSetConditionMask)(ULONGLONG dwlConditionMask, DWORD dwTypeBitMask, BYTE dwConditionMask); - - -#ifndef PROPERTYKEY_DEFINED -#define PROPERTYKEY_DEFINED -#ifndef __WATCOMC__ -typedef struct -{ - GUID fmtid; - DWORD pid; -} PROPERTYKEY; -#endif -#endif - -/* Some compilers don't define PropVariantInit(). We just do this ourselves since it's just a memset(). */ -static MA_INLINE void ma_PropVariantInit(MA_PROPVARIANT* pProp) -{ - MA_ZERO_OBJECT(pProp); -} - - -static const PROPERTYKEY MA_PKEY_Device_FriendlyName = {{0xA45C254E, 0xDF1C, 0x4EFD, {0x80, 0x20, 0x67, 0xD1, 0x46, 0xA8, 0x50, 0xE0}}, 14}; -static const PROPERTYKEY MA_PKEY_AudioEngine_DeviceFormat = {{0xF19F064D, 0x82C, 0x4E27, {0xBC, 0x73, 0x68, 0x82, 0xA1, 0xBB, 0x8E, 0x4C}}, 0}; - -static const IID MA_IID_IUnknown = {0x00000000, 0x0000, 0x0000, {0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46}}; /* 00000000-0000-0000-C000-000000000046 */ -#if !defined(MA_WIN32_DESKTOP) && !defined(MA_WIN32_GDK) -static const IID MA_IID_IAgileObject = {0x94EA2B94, 0xE9CC, 0x49E0, {0xC0, 0xFF, 0xEE, 0x64, 0xCA, 0x8F, 0x5B, 0x90}}; /* 94EA2B94-E9CC-49E0-C0FF-EE64CA8F5B90 */ -#endif - -static const IID MA_IID_IAudioClient = {0x1CB9AD4C, 0xDBFA, 0x4C32, {0xB1, 0x78, 0xC2, 0xF5, 0x68, 0xA7, 0x03, 0xB2}}; /* 1CB9AD4C-DBFA-4C32-B178-C2F568A703B2 = __uuidof(IAudioClient) */ -static const IID MA_IID_IAudioClient2 = {0x726778CD, 0xF60A, 0x4EDA, {0x82, 0xDE, 0xE4, 0x76, 0x10, 0xCD, 0x78, 0xAA}}; /* 726778CD-F60A-4EDA-82DE-E47610CD78AA = __uuidof(IAudioClient2) */ -static const IID MA_IID_IAudioClient3 = {0x7ED4EE07, 0x8E67, 0x4CD4, {0x8C, 0x1A, 0x2B, 0x7A, 0x59, 0x87, 0xAD, 0x42}}; /* 7ED4EE07-8E67-4CD4-8C1A-2B7A5987AD42 = __uuidof(IAudioClient3) */ -static const IID MA_IID_IAudioRenderClient = {0xF294ACFC, 0x3146, 0x4483, {0xA7, 0xBF, 0xAD, 0xDC, 0xA7, 0xC2, 0x60, 0xE2}}; /* F294ACFC-3146-4483-A7BF-ADDCA7C260E2 = __uuidof(IAudioRenderClient) */ -static const IID MA_IID_IAudioCaptureClient = {0xC8ADBD64, 0xE71E, 0x48A0, {0xA4, 0xDE, 0x18, 0x5C, 0x39, 0x5C, 0xD3, 0x17}}; /* C8ADBD64-E71E-48A0-A4DE-185C395CD317 = __uuidof(IAudioCaptureClient) */ -static const IID MA_IID_IMMNotificationClient = {0x7991EEC9, 0x7E89, 0x4D85, {0x83, 0x90, 0x6C, 0x70, 0x3C, 0xEC, 0x60, 0xC0}}; /* 7991EEC9-7E89-4D85-8390-6C703CEC60C0 = __uuidof(IMMNotificationClient) */ -#if !defined(MA_WIN32_DESKTOP) && !defined(MA_WIN32_GDK) -static const IID MA_IID_DEVINTERFACE_AUDIO_RENDER = {0xE6327CAD, 0xDCEC, 0x4949, {0xAE, 0x8A, 0x99, 0x1E, 0x97, 0x6A, 0x79, 0xD2}}; /* E6327CAD-DCEC-4949-AE8A-991E976A79D2 */ -static const IID MA_IID_DEVINTERFACE_AUDIO_CAPTURE = {0x2EEF81BE, 0x33FA, 0x4800, {0x96, 0x70, 0x1C, 0xD4, 0x74, 0x97, 0x2C, 0x3F}}; /* 2EEF81BE-33FA-4800-9670-1CD474972C3F */ -static const IID MA_IID_IActivateAudioInterfaceCompletionHandler = {0x41D949AB, 0x9862, 0x444A, {0x80, 0xF6, 0xC2, 0x61, 0x33, 0x4D, 0xA5, 0xEB}}; /* 41D949AB-9862-444A-80F6-C261334DA5EB */ -#endif - -static const IID MA_CLSID_MMDeviceEnumerator = {0xBCDE0395, 0xE52F, 0x467C, {0x8E, 0x3D, 0xC4, 0x57, 0x92, 0x91, 0x69, 0x2E}}; /* BCDE0395-E52F-467C-8E3D-C4579291692E = __uuidof(MMDeviceEnumerator) */ -static const IID MA_IID_IMMDeviceEnumerator = {0xA95664D2, 0x9614, 0x4F35, {0xA7, 0x46, 0xDE, 0x8D, 0xB6, 0x36, 0x17, 0xE6}}; /* A95664D2-9614-4F35-A746-DE8DB63617E6 = __uuidof(IMMDeviceEnumerator) */ - -#if defined(MA_WIN32_DESKTOP) || defined(MA_WIN32_GDK) -#define MA_MM_DEVICE_STATE_ACTIVE 1 -#define MA_MM_DEVICE_STATE_DISABLED 2 -#define MA_MM_DEVICE_STATE_NOTPRESENT 4 -#define MA_MM_DEVICE_STATE_UNPLUGGED 8 - -typedef struct ma_IMMDeviceEnumerator ma_IMMDeviceEnumerator; -typedef struct ma_IMMDeviceCollection ma_IMMDeviceCollection; -typedef struct ma_IMMDevice ma_IMMDevice; -#else -typedef struct ma_IActivateAudioInterfaceCompletionHandler ma_IActivateAudioInterfaceCompletionHandler; -typedef struct ma_IActivateAudioInterfaceAsyncOperation ma_IActivateAudioInterfaceAsyncOperation; -#endif -typedef struct ma_IPropertyStore ma_IPropertyStore; -typedef struct ma_IAudioClient ma_IAudioClient; -typedef struct ma_IAudioClient2 ma_IAudioClient2; -typedef struct ma_IAudioClient3 ma_IAudioClient3; -typedef struct ma_IAudioRenderClient ma_IAudioRenderClient; -typedef struct ma_IAudioCaptureClient ma_IAudioCaptureClient; - -typedef ma_int64 MA_REFERENCE_TIME; - -#define MA_AUDCLNT_STREAMFLAGS_CROSSPROCESS 0x00010000 -#define MA_AUDCLNT_STREAMFLAGS_LOOPBACK 0x00020000 -#define MA_AUDCLNT_STREAMFLAGS_EVENTCALLBACK 0x00040000 -#define MA_AUDCLNT_STREAMFLAGS_NOPERSIST 0x00080000 -#define MA_AUDCLNT_STREAMFLAGS_RATEADJUST 0x00100000 -#define MA_AUDCLNT_STREAMFLAGS_SRC_DEFAULT_QUALITY 0x08000000 -#define MA_AUDCLNT_STREAMFLAGS_AUTOCONVERTPCM 0x80000000 -#define MA_AUDCLNT_SESSIONFLAGS_EXPIREWHENUNOWNED 0x10000000 -#define MA_AUDCLNT_SESSIONFLAGS_DISPLAY_HIDE 0x20000000 -#define MA_AUDCLNT_SESSIONFLAGS_DISPLAY_HIDEWHENEXPIRED 0x40000000 - -/* Buffer flags. */ -#define MA_AUDCLNT_BUFFERFLAGS_DATA_DISCONTINUITY 1 -#define MA_AUDCLNT_BUFFERFLAGS_SILENT 2 -#define MA_AUDCLNT_BUFFERFLAGS_TIMESTAMP_ERROR 4 - -typedef enum -{ - ma_eRender = 0, - ma_eCapture = 1, - ma_eAll = 2 -} ma_EDataFlow; - -typedef enum -{ - ma_eConsole = 0, - ma_eMultimedia = 1, - ma_eCommunications = 2 -} ma_ERole; - -typedef enum -{ - MA_AUDCLNT_SHAREMODE_SHARED, - MA_AUDCLNT_SHAREMODE_EXCLUSIVE -} MA_AUDCLNT_SHAREMODE; - -typedef enum -{ - MA_AudioCategory_Other = 0 /* <-- miniaudio is only caring about Other. */ -} MA_AUDIO_STREAM_CATEGORY; - -typedef struct -{ - ma_uint32 cbSize; - BOOL bIsOffload; - MA_AUDIO_STREAM_CATEGORY eCategory; -} ma_AudioClientProperties; - -/* IUnknown */ -typedef struct -{ - /* IUnknown */ - HRESULT (STDMETHODCALLTYPE * QueryInterface)(ma_IUnknown* pThis, const IID* const riid, void** ppObject); - ULONG (STDMETHODCALLTYPE * AddRef) (ma_IUnknown* pThis); - ULONG (STDMETHODCALLTYPE * Release) (ma_IUnknown* pThis); -} ma_IUnknownVtbl; -struct ma_IUnknown -{ - ma_IUnknownVtbl* lpVtbl; -}; -static MA_INLINE HRESULT ma_IUnknown_QueryInterface(ma_IUnknown* pThis, const IID* const riid, void** ppObject) { return pThis->lpVtbl->QueryInterface(pThis, riid, ppObject); } -static MA_INLINE ULONG ma_IUnknown_AddRef(ma_IUnknown* pThis) { return pThis->lpVtbl->AddRef(pThis); } -static MA_INLINE ULONG ma_IUnknown_Release(ma_IUnknown* pThis) { return pThis->lpVtbl->Release(pThis); } - -#if defined(MA_WIN32_DESKTOP) || defined(MA_WIN32_GDK) - /* IMMNotificationClient */ - typedef struct - { - /* IUnknown */ - HRESULT (STDMETHODCALLTYPE * QueryInterface)(ma_IMMNotificationClient* pThis, const IID* const riid, void** ppObject); - ULONG (STDMETHODCALLTYPE * AddRef) (ma_IMMNotificationClient* pThis); - ULONG (STDMETHODCALLTYPE * Release) (ma_IMMNotificationClient* pThis); - - /* IMMNotificationClient */ - HRESULT (STDMETHODCALLTYPE * OnDeviceStateChanged) (ma_IMMNotificationClient* pThis, const WCHAR* pDeviceID, DWORD dwNewState); - HRESULT (STDMETHODCALLTYPE * OnDeviceAdded) (ma_IMMNotificationClient* pThis, const WCHAR* pDeviceID); - HRESULT (STDMETHODCALLTYPE * OnDeviceRemoved) (ma_IMMNotificationClient* pThis, const WCHAR* pDeviceID); - HRESULT (STDMETHODCALLTYPE * OnDefaultDeviceChanged)(ma_IMMNotificationClient* pThis, ma_EDataFlow dataFlow, ma_ERole role, const WCHAR* pDefaultDeviceID); - HRESULT (STDMETHODCALLTYPE * OnPropertyValueChanged)(ma_IMMNotificationClient* pThis, const WCHAR* pDeviceID, const PROPERTYKEY key); - } ma_IMMNotificationClientVtbl; - - /* IMMDeviceEnumerator */ - typedef struct - { - /* IUnknown */ - HRESULT (STDMETHODCALLTYPE * QueryInterface)(ma_IMMDeviceEnumerator* pThis, const IID* const riid, void** ppObject); - ULONG (STDMETHODCALLTYPE * AddRef) (ma_IMMDeviceEnumerator* pThis); - ULONG (STDMETHODCALLTYPE * Release) (ma_IMMDeviceEnumerator* pThis); - - /* IMMDeviceEnumerator */ - HRESULT (STDMETHODCALLTYPE * EnumAudioEndpoints) (ma_IMMDeviceEnumerator* pThis, ma_EDataFlow dataFlow, DWORD dwStateMask, ma_IMMDeviceCollection** ppDevices); - HRESULT (STDMETHODCALLTYPE * GetDefaultAudioEndpoint) (ma_IMMDeviceEnumerator* pThis, ma_EDataFlow dataFlow, ma_ERole role, ma_IMMDevice** ppEndpoint); - HRESULT (STDMETHODCALLTYPE * GetDevice) (ma_IMMDeviceEnumerator* pThis, const WCHAR* pID, ma_IMMDevice** ppDevice); - HRESULT (STDMETHODCALLTYPE * RegisterEndpointNotificationCallback) (ma_IMMDeviceEnumerator* pThis, ma_IMMNotificationClient* pClient); - HRESULT (STDMETHODCALLTYPE * UnregisterEndpointNotificationCallback)(ma_IMMDeviceEnumerator* pThis, ma_IMMNotificationClient* pClient); - } ma_IMMDeviceEnumeratorVtbl; - struct ma_IMMDeviceEnumerator - { - ma_IMMDeviceEnumeratorVtbl* lpVtbl; - }; - static MA_INLINE HRESULT ma_IMMDeviceEnumerator_QueryInterface(ma_IMMDeviceEnumerator* pThis, const IID* const riid, void** ppObject) { return pThis->lpVtbl->QueryInterface(pThis, riid, ppObject); } - static MA_INLINE ULONG ma_IMMDeviceEnumerator_AddRef(ma_IMMDeviceEnumerator* pThis) { return pThis->lpVtbl->AddRef(pThis); } - static MA_INLINE ULONG ma_IMMDeviceEnumerator_Release(ma_IMMDeviceEnumerator* pThis) { return pThis->lpVtbl->Release(pThis); } - static MA_INLINE HRESULT ma_IMMDeviceEnumerator_EnumAudioEndpoints(ma_IMMDeviceEnumerator* pThis, ma_EDataFlow dataFlow, DWORD dwStateMask, ma_IMMDeviceCollection** ppDevices) { return pThis->lpVtbl->EnumAudioEndpoints(pThis, dataFlow, dwStateMask, ppDevices); } - static MA_INLINE HRESULT ma_IMMDeviceEnumerator_GetDefaultAudioEndpoint(ma_IMMDeviceEnumerator* pThis, ma_EDataFlow dataFlow, ma_ERole role, ma_IMMDevice** ppEndpoint) { return pThis->lpVtbl->GetDefaultAudioEndpoint(pThis, dataFlow, role, ppEndpoint); } - static MA_INLINE HRESULT ma_IMMDeviceEnumerator_GetDevice(ma_IMMDeviceEnumerator* pThis, const WCHAR* pID, ma_IMMDevice** ppDevice) { return pThis->lpVtbl->GetDevice(pThis, pID, ppDevice); } - static MA_INLINE HRESULT ma_IMMDeviceEnumerator_RegisterEndpointNotificationCallback(ma_IMMDeviceEnumerator* pThis, ma_IMMNotificationClient* pClient) { return pThis->lpVtbl->RegisterEndpointNotificationCallback(pThis, pClient); } - static MA_INLINE HRESULT ma_IMMDeviceEnumerator_UnregisterEndpointNotificationCallback(ma_IMMDeviceEnumerator* pThis, ma_IMMNotificationClient* pClient) { return pThis->lpVtbl->UnregisterEndpointNotificationCallback(pThis, pClient); } - - - /* IMMDeviceCollection */ - typedef struct - { - /* IUnknown */ - HRESULT (STDMETHODCALLTYPE * QueryInterface)(ma_IMMDeviceCollection* pThis, const IID* const riid, void** ppObject); - ULONG (STDMETHODCALLTYPE * AddRef) (ma_IMMDeviceCollection* pThis); - ULONG (STDMETHODCALLTYPE * Release) (ma_IMMDeviceCollection* pThis); - - /* IMMDeviceCollection */ - HRESULT (STDMETHODCALLTYPE * GetCount)(ma_IMMDeviceCollection* pThis, UINT* pDevices); - HRESULT (STDMETHODCALLTYPE * Item) (ma_IMMDeviceCollection* pThis, UINT nDevice, ma_IMMDevice** ppDevice); - } ma_IMMDeviceCollectionVtbl; - struct ma_IMMDeviceCollection - { - ma_IMMDeviceCollectionVtbl* lpVtbl; - }; - static MA_INLINE HRESULT ma_IMMDeviceCollection_QueryInterface(ma_IMMDeviceCollection* pThis, const IID* const riid, void** ppObject) { return pThis->lpVtbl->QueryInterface(pThis, riid, ppObject); } - static MA_INLINE ULONG ma_IMMDeviceCollection_AddRef(ma_IMMDeviceCollection* pThis) { return pThis->lpVtbl->AddRef(pThis); } - static MA_INLINE ULONG ma_IMMDeviceCollection_Release(ma_IMMDeviceCollection* pThis) { return pThis->lpVtbl->Release(pThis); } - static MA_INLINE HRESULT ma_IMMDeviceCollection_GetCount(ma_IMMDeviceCollection* pThis, UINT* pDevices) { return pThis->lpVtbl->GetCount(pThis, pDevices); } - static MA_INLINE HRESULT ma_IMMDeviceCollection_Item(ma_IMMDeviceCollection* pThis, UINT nDevice, ma_IMMDevice** ppDevice) { return pThis->lpVtbl->Item(pThis, nDevice, ppDevice); } - - - /* IMMDevice */ - typedef struct - { - /* IUnknown */ - HRESULT (STDMETHODCALLTYPE * QueryInterface)(ma_IMMDevice* pThis, const IID* const riid, void** ppObject); - ULONG (STDMETHODCALLTYPE * AddRef) (ma_IMMDevice* pThis); - ULONG (STDMETHODCALLTYPE * Release) (ma_IMMDevice* pThis); - - /* IMMDevice */ - HRESULT (STDMETHODCALLTYPE * Activate) (ma_IMMDevice* pThis, const IID* const iid, DWORD dwClsCtx, MA_PROPVARIANT* pActivationParams, void** ppInterface); - HRESULT (STDMETHODCALLTYPE * OpenPropertyStore)(ma_IMMDevice* pThis, DWORD stgmAccess, ma_IPropertyStore** ppProperties); - HRESULT (STDMETHODCALLTYPE * GetId) (ma_IMMDevice* pThis, WCHAR** pID); - HRESULT (STDMETHODCALLTYPE * GetState) (ma_IMMDevice* pThis, DWORD *pState); - } ma_IMMDeviceVtbl; - struct ma_IMMDevice - { - ma_IMMDeviceVtbl* lpVtbl; - }; - static MA_INLINE HRESULT ma_IMMDevice_QueryInterface(ma_IMMDevice* pThis, const IID* const riid, void** ppObject) { return pThis->lpVtbl->QueryInterface(pThis, riid, ppObject); } - static MA_INLINE ULONG ma_IMMDevice_AddRef(ma_IMMDevice* pThis) { return pThis->lpVtbl->AddRef(pThis); } - static MA_INLINE ULONG ma_IMMDevice_Release(ma_IMMDevice* pThis) { return pThis->lpVtbl->Release(pThis); } - static MA_INLINE HRESULT ma_IMMDevice_Activate(ma_IMMDevice* pThis, const IID* const iid, DWORD dwClsCtx, MA_PROPVARIANT* pActivationParams, void** ppInterface) { return pThis->lpVtbl->Activate(pThis, iid, dwClsCtx, pActivationParams, ppInterface); } - static MA_INLINE HRESULT ma_IMMDevice_OpenPropertyStore(ma_IMMDevice* pThis, DWORD stgmAccess, ma_IPropertyStore** ppProperties) { return pThis->lpVtbl->OpenPropertyStore(pThis, stgmAccess, ppProperties); } - static MA_INLINE HRESULT ma_IMMDevice_GetId(ma_IMMDevice* pThis, WCHAR** pID) { return pThis->lpVtbl->GetId(pThis, pID); } - static MA_INLINE HRESULT ma_IMMDevice_GetState(ma_IMMDevice* pThis, DWORD *pState) { return pThis->lpVtbl->GetState(pThis, pState); } -#else - /* IActivateAudioInterfaceAsyncOperation */ - typedef struct - { - /* IUnknown */ - HRESULT (STDMETHODCALLTYPE * QueryInterface)(ma_IActivateAudioInterfaceAsyncOperation* pThis, const IID* const riid, void** ppObject); - ULONG (STDMETHODCALLTYPE * AddRef) (ma_IActivateAudioInterfaceAsyncOperation* pThis); - ULONG (STDMETHODCALLTYPE * Release) (ma_IActivateAudioInterfaceAsyncOperation* pThis); - - /* IActivateAudioInterfaceAsyncOperation */ - HRESULT (STDMETHODCALLTYPE * GetActivateResult)(ma_IActivateAudioInterfaceAsyncOperation* pThis, HRESULT *pActivateResult, ma_IUnknown** ppActivatedInterface); - } ma_IActivateAudioInterfaceAsyncOperationVtbl; - struct ma_IActivateAudioInterfaceAsyncOperation - { - ma_IActivateAudioInterfaceAsyncOperationVtbl* lpVtbl; - }; - static MA_INLINE HRESULT ma_IActivateAudioInterfaceAsyncOperation_QueryInterface(ma_IActivateAudioInterfaceAsyncOperation* pThis, const IID* const riid, void** ppObject) { return pThis->lpVtbl->QueryInterface(pThis, riid, ppObject); } - static MA_INLINE ULONG ma_IActivateAudioInterfaceAsyncOperation_AddRef(ma_IActivateAudioInterfaceAsyncOperation* pThis) { return pThis->lpVtbl->AddRef(pThis); } - static MA_INLINE ULONG ma_IActivateAudioInterfaceAsyncOperation_Release(ma_IActivateAudioInterfaceAsyncOperation* pThis) { return pThis->lpVtbl->Release(pThis); } - static MA_INLINE HRESULT ma_IActivateAudioInterfaceAsyncOperation_GetActivateResult(ma_IActivateAudioInterfaceAsyncOperation* pThis, HRESULT *pActivateResult, ma_IUnknown** ppActivatedInterface) { return pThis->lpVtbl->GetActivateResult(pThis, pActivateResult, ppActivatedInterface); } -#endif - -/* IPropertyStore */ -typedef struct -{ - /* IUnknown */ - HRESULT (STDMETHODCALLTYPE * QueryInterface)(ma_IPropertyStore* pThis, const IID* const riid, void** ppObject); - ULONG (STDMETHODCALLTYPE * AddRef) (ma_IPropertyStore* pThis); - ULONG (STDMETHODCALLTYPE * Release) (ma_IPropertyStore* pThis); - - /* IPropertyStore */ - HRESULT (STDMETHODCALLTYPE * GetCount)(ma_IPropertyStore* pThis, DWORD* pPropCount); - HRESULT (STDMETHODCALLTYPE * GetAt) (ma_IPropertyStore* pThis, DWORD propIndex, PROPERTYKEY* pPropKey); - HRESULT (STDMETHODCALLTYPE * GetValue)(ma_IPropertyStore* pThis, const PROPERTYKEY* const pKey, MA_PROPVARIANT* pPropVar); - HRESULT (STDMETHODCALLTYPE * SetValue)(ma_IPropertyStore* pThis, const PROPERTYKEY* const pKey, const MA_PROPVARIANT* const pPropVar); - HRESULT (STDMETHODCALLTYPE * Commit) (ma_IPropertyStore* pThis); -} ma_IPropertyStoreVtbl; -struct ma_IPropertyStore -{ - ma_IPropertyStoreVtbl* lpVtbl; -}; -static MA_INLINE HRESULT ma_IPropertyStore_QueryInterface(ma_IPropertyStore* pThis, const IID* const riid, void** ppObject) { return pThis->lpVtbl->QueryInterface(pThis, riid, ppObject); } -static MA_INLINE ULONG ma_IPropertyStore_AddRef(ma_IPropertyStore* pThis) { return pThis->lpVtbl->AddRef(pThis); } -static MA_INLINE ULONG ma_IPropertyStore_Release(ma_IPropertyStore* pThis) { return pThis->lpVtbl->Release(pThis); } -static MA_INLINE HRESULT ma_IPropertyStore_GetCount(ma_IPropertyStore* pThis, DWORD* pPropCount) { return pThis->lpVtbl->GetCount(pThis, pPropCount); } -static MA_INLINE HRESULT ma_IPropertyStore_GetAt(ma_IPropertyStore* pThis, DWORD propIndex, PROPERTYKEY* pPropKey) { return pThis->lpVtbl->GetAt(pThis, propIndex, pPropKey); } -static MA_INLINE HRESULT ma_IPropertyStore_GetValue(ma_IPropertyStore* pThis, const PROPERTYKEY* const pKey, MA_PROPVARIANT* pPropVar) { return pThis->lpVtbl->GetValue(pThis, pKey, pPropVar); } -static MA_INLINE HRESULT ma_IPropertyStore_SetValue(ma_IPropertyStore* pThis, const PROPERTYKEY* const pKey, const MA_PROPVARIANT* const pPropVar) { return pThis->lpVtbl->SetValue(pThis, pKey, pPropVar); } -static MA_INLINE HRESULT ma_IPropertyStore_Commit(ma_IPropertyStore* pThis) { return pThis->lpVtbl->Commit(pThis); } - - -/* IAudioClient */ -typedef struct -{ - /* IUnknown */ - HRESULT (STDMETHODCALLTYPE * QueryInterface)(ma_IAudioClient* pThis, const IID* const riid, void** ppObject); - ULONG (STDMETHODCALLTYPE * AddRef) (ma_IAudioClient* pThis); - ULONG (STDMETHODCALLTYPE * Release) (ma_IAudioClient* pThis); - - /* IAudioClient */ - HRESULT (STDMETHODCALLTYPE * Initialize) (ma_IAudioClient* pThis, MA_AUDCLNT_SHAREMODE shareMode, DWORD streamFlags, MA_REFERENCE_TIME bufferDuration, MA_REFERENCE_TIME periodicity, const MA_WAVEFORMATEX* pFormat, const GUID* pAudioSessionGuid); - HRESULT (STDMETHODCALLTYPE * GetBufferSize) (ma_IAudioClient* pThis, ma_uint32* pNumBufferFrames); - HRESULT (STDMETHODCALLTYPE * GetStreamLatency) (ma_IAudioClient* pThis, MA_REFERENCE_TIME* pLatency); - HRESULT (STDMETHODCALLTYPE * GetCurrentPadding)(ma_IAudioClient* pThis, ma_uint32* pNumPaddingFrames); - HRESULT (STDMETHODCALLTYPE * IsFormatSupported)(ma_IAudioClient* pThis, MA_AUDCLNT_SHAREMODE shareMode, const MA_WAVEFORMATEX* pFormat, MA_WAVEFORMATEX** ppClosestMatch); - HRESULT (STDMETHODCALLTYPE * GetMixFormat) (ma_IAudioClient* pThis, MA_WAVEFORMATEX** ppDeviceFormat); - HRESULT (STDMETHODCALLTYPE * GetDevicePeriod) (ma_IAudioClient* pThis, MA_REFERENCE_TIME* pDefaultDevicePeriod, MA_REFERENCE_TIME* pMinimumDevicePeriod); - HRESULT (STDMETHODCALLTYPE * Start) (ma_IAudioClient* pThis); - HRESULT (STDMETHODCALLTYPE * Stop) (ma_IAudioClient* pThis); - HRESULT (STDMETHODCALLTYPE * Reset) (ma_IAudioClient* pThis); - HRESULT (STDMETHODCALLTYPE * SetEventHandle) (ma_IAudioClient* pThis, HANDLE eventHandle); - HRESULT (STDMETHODCALLTYPE * GetService) (ma_IAudioClient* pThis, const IID* const riid, void** pp); -} ma_IAudioClientVtbl; -struct ma_IAudioClient -{ - ma_IAudioClientVtbl* lpVtbl; -}; -static MA_INLINE HRESULT ma_IAudioClient_QueryInterface(ma_IAudioClient* pThis, const IID* const riid, void** ppObject) { return pThis->lpVtbl->QueryInterface(pThis, riid, ppObject); } -static MA_INLINE ULONG ma_IAudioClient_AddRef(ma_IAudioClient* pThis) { return pThis->lpVtbl->AddRef(pThis); } -static MA_INLINE ULONG ma_IAudioClient_Release(ma_IAudioClient* pThis) { return pThis->lpVtbl->Release(pThis); } -static MA_INLINE HRESULT ma_IAudioClient_Initialize(ma_IAudioClient* pThis, MA_AUDCLNT_SHAREMODE shareMode, DWORD streamFlags, MA_REFERENCE_TIME bufferDuration, MA_REFERENCE_TIME periodicity, const MA_WAVEFORMATEX* pFormat, const GUID* pAudioSessionGuid) { return pThis->lpVtbl->Initialize(pThis, shareMode, streamFlags, bufferDuration, periodicity, pFormat, pAudioSessionGuid); } -static MA_INLINE HRESULT ma_IAudioClient_GetBufferSize(ma_IAudioClient* pThis, ma_uint32* pNumBufferFrames) { return pThis->lpVtbl->GetBufferSize(pThis, pNumBufferFrames); } -static MA_INLINE HRESULT ma_IAudioClient_GetStreamLatency(ma_IAudioClient* pThis, MA_REFERENCE_TIME* pLatency) { return pThis->lpVtbl->GetStreamLatency(pThis, pLatency); } -static MA_INLINE HRESULT ma_IAudioClient_GetCurrentPadding(ma_IAudioClient* pThis, ma_uint32* pNumPaddingFrames) { return pThis->lpVtbl->GetCurrentPadding(pThis, pNumPaddingFrames); } -static MA_INLINE HRESULT ma_IAudioClient_IsFormatSupported(ma_IAudioClient* pThis, MA_AUDCLNT_SHAREMODE shareMode, const MA_WAVEFORMATEX* pFormat, MA_WAVEFORMATEX** ppClosestMatch) { return pThis->lpVtbl->IsFormatSupported(pThis, shareMode, pFormat, ppClosestMatch); } -static MA_INLINE HRESULT ma_IAudioClient_GetMixFormat(ma_IAudioClient* pThis, MA_WAVEFORMATEX** ppDeviceFormat) { return pThis->lpVtbl->GetMixFormat(pThis, ppDeviceFormat); } -static MA_INLINE HRESULT ma_IAudioClient_GetDevicePeriod(ma_IAudioClient* pThis, MA_REFERENCE_TIME* pDefaultDevicePeriod, MA_REFERENCE_TIME* pMinimumDevicePeriod) { return pThis->lpVtbl->GetDevicePeriod(pThis, pDefaultDevicePeriod, pMinimumDevicePeriod); } -static MA_INLINE HRESULT ma_IAudioClient_Start(ma_IAudioClient* pThis) { return pThis->lpVtbl->Start(pThis); } -static MA_INLINE HRESULT ma_IAudioClient_Stop(ma_IAudioClient* pThis) { return pThis->lpVtbl->Stop(pThis); } -static MA_INLINE HRESULT ma_IAudioClient_Reset(ma_IAudioClient* pThis) { return pThis->lpVtbl->Reset(pThis); } -static MA_INLINE HRESULT ma_IAudioClient_SetEventHandle(ma_IAudioClient* pThis, HANDLE eventHandle) { return pThis->lpVtbl->SetEventHandle(pThis, eventHandle); } -static MA_INLINE HRESULT ma_IAudioClient_GetService(ma_IAudioClient* pThis, const IID* const riid, void** pp) { return pThis->lpVtbl->GetService(pThis, riid, pp); } - -/* IAudioClient2 */ -typedef struct -{ - /* IUnknown */ - HRESULT (STDMETHODCALLTYPE * QueryInterface)(ma_IAudioClient2* pThis, const IID* const riid, void** ppObject); - ULONG (STDMETHODCALLTYPE * AddRef) (ma_IAudioClient2* pThis); - ULONG (STDMETHODCALLTYPE * Release) (ma_IAudioClient2* pThis); - - /* IAudioClient */ - HRESULT (STDMETHODCALLTYPE * Initialize) (ma_IAudioClient2* pThis, MA_AUDCLNT_SHAREMODE shareMode, DWORD streamFlags, MA_REFERENCE_TIME bufferDuration, MA_REFERENCE_TIME periodicity, const MA_WAVEFORMATEX* pFormat, const GUID* pAudioSessionGuid); - HRESULT (STDMETHODCALLTYPE * GetBufferSize) (ma_IAudioClient2* pThis, ma_uint32* pNumBufferFrames); - HRESULT (STDMETHODCALLTYPE * GetStreamLatency) (ma_IAudioClient2* pThis, MA_REFERENCE_TIME* pLatency); - HRESULT (STDMETHODCALLTYPE * GetCurrentPadding)(ma_IAudioClient2* pThis, ma_uint32* pNumPaddingFrames); - HRESULT (STDMETHODCALLTYPE * IsFormatSupported)(ma_IAudioClient2* pThis, MA_AUDCLNT_SHAREMODE shareMode, const MA_WAVEFORMATEX* pFormat, MA_WAVEFORMATEX** ppClosestMatch); - HRESULT (STDMETHODCALLTYPE * GetMixFormat) (ma_IAudioClient2* pThis, MA_WAVEFORMATEX** ppDeviceFormat); - HRESULT (STDMETHODCALLTYPE * GetDevicePeriod) (ma_IAudioClient2* pThis, MA_REFERENCE_TIME* pDefaultDevicePeriod, MA_REFERENCE_TIME* pMinimumDevicePeriod); - HRESULT (STDMETHODCALLTYPE * Start) (ma_IAudioClient2* pThis); - HRESULT (STDMETHODCALLTYPE * Stop) (ma_IAudioClient2* pThis); - HRESULT (STDMETHODCALLTYPE * Reset) (ma_IAudioClient2* pThis); - HRESULT (STDMETHODCALLTYPE * SetEventHandle) (ma_IAudioClient2* pThis, HANDLE eventHandle); - HRESULT (STDMETHODCALLTYPE * GetService) (ma_IAudioClient2* pThis, const IID* const riid, void** pp); - - /* IAudioClient2 */ - HRESULT (STDMETHODCALLTYPE * IsOffloadCapable) (ma_IAudioClient2* pThis, MA_AUDIO_STREAM_CATEGORY category, BOOL* pOffloadCapable); - HRESULT (STDMETHODCALLTYPE * SetClientProperties)(ma_IAudioClient2* pThis, const ma_AudioClientProperties* pProperties); - HRESULT (STDMETHODCALLTYPE * GetBufferSizeLimits)(ma_IAudioClient2* pThis, const MA_WAVEFORMATEX* pFormat, BOOL eventDriven, MA_REFERENCE_TIME* pMinBufferDuration, MA_REFERENCE_TIME* pMaxBufferDuration); -} ma_IAudioClient2Vtbl; -struct ma_IAudioClient2 -{ - ma_IAudioClient2Vtbl* lpVtbl; -}; -static MA_INLINE HRESULT ma_IAudioClient2_QueryInterface(ma_IAudioClient2* pThis, const IID* const riid, void** ppObject) { return pThis->lpVtbl->QueryInterface(pThis, riid, ppObject); } -static MA_INLINE ULONG ma_IAudioClient2_AddRef(ma_IAudioClient2* pThis) { return pThis->lpVtbl->AddRef(pThis); } -static MA_INLINE ULONG ma_IAudioClient2_Release(ma_IAudioClient2* pThis) { return pThis->lpVtbl->Release(pThis); } -static MA_INLINE HRESULT ma_IAudioClient2_Initialize(ma_IAudioClient2* pThis, MA_AUDCLNT_SHAREMODE shareMode, DWORD streamFlags, MA_REFERENCE_TIME bufferDuration, MA_REFERENCE_TIME periodicity, const MA_WAVEFORMATEX* pFormat, const GUID* pAudioSessionGuid) { return pThis->lpVtbl->Initialize(pThis, shareMode, streamFlags, bufferDuration, periodicity, pFormat, pAudioSessionGuid); } -static MA_INLINE HRESULT ma_IAudioClient2_GetBufferSize(ma_IAudioClient2* pThis, ma_uint32* pNumBufferFrames) { return pThis->lpVtbl->GetBufferSize(pThis, pNumBufferFrames); } -static MA_INLINE HRESULT ma_IAudioClient2_GetStreamLatency(ma_IAudioClient2* pThis, MA_REFERENCE_TIME* pLatency) { return pThis->lpVtbl->GetStreamLatency(pThis, pLatency); } -static MA_INLINE HRESULT ma_IAudioClient2_GetCurrentPadding(ma_IAudioClient2* pThis, ma_uint32* pNumPaddingFrames) { return pThis->lpVtbl->GetCurrentPadding(pThis, pNumPaddingFrames); } -static MA_INLINE HRESULT ma_IAudioClient2_IsFormatSupported(ma_IAudioClient2* pThis, MA_AUDCLNT_SHAREMODE shareMode, const MA_WAVEFORMATEX* pFormat, MA_WAVEFORMATEX** ppClosestMatch) { return pThis->lpVtbl->IsFormatSupported(pThis, shareMode, pFormat, ppClosestMatch); } -static MA_INLINE HRESULT ma_IAudioClient2_GetMixFormat(ma_IAudioClient2* pThis, MA_WAVEFORMATEX** ppDeviceFormat) { return pThis->lpVtbl->GetMixFormat(pThis, ppDeviceFormat); } -static MA_INLINE HRESULT ma_IAudioClient2_GetDevicePeriod(ma_IAudioClient2* pThis, MA_REFERENCE_TIME* pDefaultDevicePeriod, MA_REFERENCE_TIME* pMinimumDevicePeriod) { return pThis->lpVtbl->GetDevicePeriod(pThis, pDefaultDevicePeriod, pMinimumDevicePeriod); } -static MA_INLINE HRESULT ma_IAudioClient2_Start(ma_IAudioClient2* pThis) { return pThis->lpVtbl->Start(pThis); } -static MA_INLINE HRESULT ma_IAudioClient2_Stop(ma_IAudioClient2* pThis) { return pThis->lpVtbl->Stop(pThis); } -static MA_INLINE HRESULT ma_IAudioClient2_Reset(ma_IAudioClient2* pThis) { return pThis->lpVtbl->Reset(pThis); } -static MA_INLINE HRESULT ma_IAudioClient2_SetEventHandle(ma_IAudioClient2* pThis, HANDLE eventHandle) { return pThis->lpVtbl->SetEventHandle(pThis, eventHandle); } -static MA_INLINE HRESULT ma_IAudioClient2_GetService(ma_IAudioClient2* pThis, const IID* const riid, void** pp) { return pThis->lpVtbl->GetService(pThis, riid, pp); } -static MA_INLINE HRESULT ma_IAudioClient2_IsOffloadCapable(ma_IAudioClient2* pThis, MA_AUDIO_STREAM_CATEGORY category, BOOL* pOffloadCapable) { return pThis->lpVtbl->IsOffloadCapable(pThis, category, pOffloadCapable); } -static MA_INLINE HRESULT ma_IAudioClient2_SetClientProperties(ma_IAudioClient2* pThis, const ma_AudioClientProperties* pProperties) { return pThis->lpVtbl->SetClientProperties(pThis, pProperties); } -static MA_INLINE HRESULT ma_IAudioClient2_GetBufferSizeLimits(ma_IAudioClient2* pThis, const MA_WAVEFORMATEX* pFormat, BOOL eventDriven, MA_REFERENCE_TIME* pMinBufferDuration, MA_REFERENCE_TIME* pMaxBufferDuration) { return pThis->lpVtbl->GetBufferSizeLimits(pThis, pFormat, eventDriven, pMinBufferDuration, pMaxBufferDuration); } - - -/* IAudioClient3 */ -typedef struct -{ - /* IUnknown */ - HRESULT (STDMETHODCALLTYPE * QueryInterface)(ma_IAudioClient3* pThis, const IID* const riid, void** ppObject); - ULONG (STDMETHODCALLTYPE * AddRef) (ma_IAudioClient3* pThis); - ULONG (STDMETHODCALLTYPE * Release) (ma_IAudioClient3* pThis); - - /* IAudioClient */ - HRESULT (STDMETHODCALLTYPE * Initialize) (ma_IAudioClient3* pThis, MA_AUDCLNT_SHAREMODE shareMode, DWORD streamFlags, MA_REFERENCE_TIME bufferDuration, MA_REFERENCE_TIME periodicity, const MA_WAVEFORMATEX* pFormat, const GUID* pAudioSessionGuid); - HRESULT (STDMETHODCALLTYPE * GetBufferSize) (ma_IAudioClient3* pThis, ma_uint32* pNumBufferFrames); - HRESULT (STDMETHODCALLTYPE * GetStreamLatency) (ma_IAudioClient3* pThis, MA_REFERENCE_TIME* pLatency); - HRESULT (STDMETHODCALLTYPE * GetCurrentPadding)(ma_IAudioClient3* pThis, ma_uint32* pNumPaddingFrames); - HRESULT (STDMETHODCALLTYPE * IsFormatSupported)(ma_IAudioClient3* pThis, MA_AUDCLNT_SHAREMODE shareMode, const MA_WAVEFORMATEX* pFormat, MA_WAVEFORMATEX** ppClosestMatch); - HRESULT (STDMETHODCALLTYPE * GetMixFormat) (ma_IAudioClient3* pThis, MA_WAVEFORMATEX** ppDeviceFormat); - HRESULT (STDMETHODCALLTYPE * GetDevicePeriod) (ma_IAudioClient3* pThis, MA_REFERENCE_TIME* pDefaultDevicePeriod, MA_REFERENCE_TIME* pMinimumDevicePeriod); - HRESULT (STDMETHODCALLTYPE * Start) (ma_IAudioClient3* pThis); - HRESULT (STDMETHODCALLTYPE * Stop) (ma_IAudioClient3* pThis); - HRESULT (STDMETHODCALLTYPE * Reset) (ma_IAudioClient3* pThis); - HRESULT (STDMETHODCALLTYPE * SetEventHandle) (ma_IAudioClient3* pThis, HANDLE eventHandle); - HRESULT (STDMETHODCALLTYPE * GetService) (ma_IAudioClient3* pThis, const IID* const riid, void** pp); - - /* IAudioClient2 */ - HRESULT (STDMETHODCALLTYPE * IsOffloadCapable) (ma_IAudioClient3* pThis, MA_AUDIO_STREAM_CATEGORY category, BOOL* pOffloadCapable); - HRESULT (STDMETHODCALLTYPE * SetClientProperties)(ma_IAudioClient3* pThis, const ma_AudioClientProperties* pProperties); - HRESULT (STDMETHODCALLTYPE * GetBufferSizeLimits)(ma_IAudioClient3* pThis, const MA_WAVEFORMATEX* pFormat, BOOL eventDriven, MA_REFERENCE_TIME* pMinBufferDuration, MA_REFERENCE_TIME* pMaxBufferDuration); - - /* IAudioClient3 */ - HRESULT (STDMETHODCALLTYPE * GetSharedModeEnginePeriod) (ma_IAudioClient3* pThis, const MA_WAVEFORMATEX* pFormat, ma_uint32* pDefaultPeriodInFrames, ma_uint32* pFundamentalPeriodInFrames, ma_uint32* pMinPeriodInFrames, ma_uint32* pMaxPeriodInFrames); - HRESULT (STDMETHODCALLTYPE * GetCurrentSharedModeEnginePeriod)(ma_IAudioClient3* pThis, MA_WAVEFORMATEX** ppFormat, ma_uint32* pCurrentPeriodInFrames); - HRESULT (STDMETHODCALLTYPE * InitializeSharedAudioStream) (ma_IAudioClient3* pThis, DWORD streamFlags, ma_uint32 periodInFrames, const MA_WAVEFORMATEX* pFormat, const GUID* pAudioSessionGuid); -} ma_IAudioClient3Vtbl; -struct ma_IAudioClient3 -{ - ma_IAudioClient3Vtbl* lpVtbl; -}; -static MA_INLINE HRESULT ma_IAudioClient3_QueryInterface(ma_IAudioClient3* pThis, const IID* const riid, void** ppObject) { return pThis->lpVtbl->QueryInterface(pThis, riid, ppObject); } -static MA_INLINE ULONG ma_IAudioClient3_AddRef(ma_IAudioClient3* pThis) { return pThis->lpVtbl->AddRef(pThis); } -static MA_INLINE ULONG ma_IAudioClient3_Release(ma_IAudioClient3* pThis) { return pThis->lpVtbl->Release(pThis); } -static MA_INLINE HRESULT ma_IAudioClient3_Initialize(ma_IAudioClient3* pThis, MA_AUDCLNT_SHAREMODE shareMode, DWORD streamFlags, MA_REFERENCE_TIME bufferDuration, MA_REFERENCE_TIME periodicity, const MA_WAVEFORMATEX* pFormat, const GUID* pAudioSessionGuid) { return pThis->lpVtbl->Initialize(pThis, shareMode, streamFlags, bufferDuration, periodicity, pFormat, pAudioSessionGuid); } -static MA_INLINE HRESULT ma_IAudioClient3_GetBufferSize(ma_IAudioClient3* pThis, ma_uint32* pNumBufferFrames) { return pThis->lpVtbl->GetBufferSize(pThis, pNumBufferFrames); } -static MA_INLINE HRESULT ma_IAudioClient3_GetStreamLatency(ma_IAudioClient3* pThis, MA_REFERENCE_TIME* pLatency) { return pThis->lpVtbl->GetStreamLatency(pThis, pLatency); } -static MA_INLINE HRESULT ma_IAudioClient3_GetCurrentPadding(ma_IAudioClient3* pThis, ma_uint32* pNumPaddingFrames) { return pThis->lpVtbl->GetCurrentPadding(pThis, pNumPaddingFrames); } -static MA_INLINE HRESULT ma_IAudioClient3_IsFormatSupported(ma_IAudioClient3* pThis, MA_AUDCLNT_SHAREMODE shareMode, const MA_WAVEFORMATEX* pFormat, MA_WAVEFORMATEX** ppClosestMatch) { return pThis->lpVtbl->IsFormatSupported(pThis, shareMode, pFormat, ppClosestMatch); } -static MA_INLINE HRESULT ma_IAudioClient3_GetMixFormat(ma_IAudioClient3* pThis, MA_WAVEFORMATEX** ppDeviceFormat) { return pThis->lpVtbl->GetMixFormat(pThis, ppDeviceFormat); } -static MA_INLINE HRESULT ma_IAudioClient3_GetDevicePeriod(ma_IAudioClient3* pThis, MA_REFERENCE_TIME* pDefaultDevicePeriod, MA_REFERENCE_TIME* pMinimumDevicePeriod) { return pThis->lpVtbl->GetDevicePeriod(pThis, pDefaultDevicePeriod, pMinimumDevicePeriod); } -static MA_INLINE HRESULT ma_IAudioClient3_Start(ma_IAudioClient3* pThis) { return pThis->lpVtbl->Start(pThis); } -static MA_INLINE HRESULT ma_IAudioClient3_Stop(ma_IAudioClient3* pThis) { return pThis->lpVtbl->Stop(pThis); } -static MA_INLINE HRESULT ma_IAudioClient3_Reset(ma_IAudioClient3* pThis) { return pThis->lpVtbl->Reset(pThis); } -static MA_INLINE HRESULT ma_IAudioClient3_SetEventHandle(ma_IAudioClient3* pThis, HANDLE eventHandle) { return pThis->lpVtbl->SetEventHandle(pThis, eventHandle); } -static MA_INLINE HRESULT ma_IAudioClient3_GetService(ma_IAudioClient3* pThis, const IID* const riid, void** pp) { return pThis->lpVtbl->GetService(pThis, riid, pp); } -static MA_INLINE HRESULT ma_IAudioClient3_IsOffloadCapable(ma_IAudioClient3* pThis, MA_AUDIO_STREAM_CATEGORY category, BOOL* pOffloadCapable) { return pThis->lpVtbl->IsOffloadCapable(pThis, category, pOffloadCapable); } -static MA_INLINE HRESULT ma_IAudioClient3_SetClientProperties(ma_IAudioClient3* pThis, const ma_AudioClientProperties* pProperties) { return pThis->lpVtbl->SetClientProperties(pThis, pProperties); } -static MA_INLINE HRESULT ma_IAudioClient3_GetBufferSizeLimits(ma_IAudioClient3* pThis, const MA_WAVEFORMATEX* pFormat, BOOL eventDriven, MA_REFERENCE_TIME* pMinBufferDuration, MA_REFERENCE_TIME* pMaxBufferDuration) { return pThis->lpVtbl->GetBufferSizeLimits(pThis, pFormat, eventDriven, pMinBufferDuration, pMaxBufferDuration); } -static MA_INLINE HRESULT ma_IAudioClient3_GetSharedModeEnginePeriod(ma_IAudioClient3* pThis, const MA_WAVEFORMATEX* pFormat, ma_uint32* pDefaultPeriodInFrames, ma_uint32* pFundamentalPeriodInFrames, ma_uint32* pMinPeriodInFrames, ma_uint32* pMaxPeriodInFrames) { return pThis->lpVtbl->GetSharedModeEnginePeriod(pThis, pFormat, pDefaultPeriodInFrames, pFundamentalPeriodInFrames, pMinPeriodInFrames, pMaxPeriodInFrames); } -static MA_INLINE HRESULT ma_IAudioClient3_GetCurrentSharedModeEnginePeriod(ma_IAudioClient3* pThis, MA_WAVEFORMATEX** ppFormat, ma_uint32* pCurrentPeriodInFrames) { return pThis->lpVtbl->GetCurrentSharedModeEnginePeriod(pThis, ppFormat, pCurrentPeriodInFrames); } -static MA_INLINE HRESULT ma_IAudioClient3_InitializeSharedAudioStream(ma_IAudioClient3* pThis, DWORD streamFlags, ma_uint32 periodInFrames, const MA_WAVEFORMATEX* pFormat, const GUID* pAudioSessionGUID) { return pThis->lpVtbl->InitializeSharedAudioStream(pThis, streamFlags, periodInFrames, pFormat, pAudioSessionGUID); } - - -/* IAudioRenderClient */ -typedef struct -{ - /* IUnknown */ - HRESULT (STDMETHODCALLTYPE * QueryInterface)(ma_IAudioRenderClient* pThis, const IID* const riid, void** ppObject); - ULONG (STDMETHODCALLTYPE * AddRef) (ma_IAudioRenderClient* pThis); - ULONG (STDMETHODCALLTYPE * Release) (ma_IAudioRenderClient* pThis); - - /* IAudioRenderClient */ - HRESULT (STDMETHODCALLTYPE * GetBuffer) (ma_IAudioRenderClient* pThis, ma_uint32 numFramesRequested, BYTE** ppData); - HRESULT (STDMETHODCALLTYPE * ReleaseBuffer)(ma_IAudioRenderClient* pThis, ma_uint32 numFramesWritten, DWORD dwFlags); -} ma_IAudioRenderClientVtbl; -struct ma_IAudioRenderClient -{ - ma_IAudioRenderClientVtbl* lpVtbl; -}; -static MA_INLINE HRESULT ma_IAudioRenderClient_QueryInterface(ma_IAudioRenderClient* pThis, const IID* const riid, void** ppObject) { return pThis->lpVtbl->QueryInterface(pThis, riid, ppObject); } -static MA_INLINE ULONG ma_IAudioRenderClient_AddRef(ma_IAudioRenderClient* pThis) { return pThis->lpVtbl->AddRef(pThis); } -static MA_INLINE ULONG ma_IAudioRenderClient_Release(ma_IAudioRenderClient* pThis) { return pThis->lpVtbl->Release(pThis); } -static MA_INLINE HRESULT ma_IAudioRenderClient_GetBuffer(ma_IAudioRenderClient* pThis, ma_uint32 numFramesRequested, BYTE** ppData) { return pThis->lpVtbl->GetBuffer(pThis, numFramesRequested, ppData); } -static MA_INLINE HRESULT ma_IAudioRenderClient_ReleaseBuffer(ma_IAudioRenderClient* pThis, ma_uint32 numFramesWritten, DWORD dwFlags) { return pThis->lpVtbl->ReleaseBuffer(pThis, numFramesWritten, dwFlags); } - - -/* IAudioCaptureClient */ -typedef struct -{ - /* IUnknown */ - HRESULT (STDMETHODCALLTYPE * QueryInterface)(ma_IAudioCaptureClient* pThis, const IID* const riid, void** ppObject); - ULONG (STDMETHODCALLTYPE * AddRef) (ma_IAudioCaptureClient* pThis); - ULONG (STDMETHODCALLTYPE * Release) (ma_IAudioCaptureClient* pThis); - - /* IAudioRenderClient */ - HRESULT (STDMETHODCALLTYPE * GetBuffer) (ma_IAudioCaptureClient* pThis, BYTE** ppData, ma_uint32* pNumFramesToRead, DWORD* pFlags, ma_uint64* pDevicePosition, ma_uint64* pQPCPosition); - HRESULT (STDMETHODCALLTYPE * ReleaseBuffer) (ma_IAudioCaptureClient* pThis, ma_uint32 numFramesRead); - HRESULT (STDMETHODCALLTYPE * GetNextPacketSize)(ma_IAudioCaptureClient* pThis, ma_uint32* pNumFramesInNextPacket); -} ma_IAudioCaptureClientVtbl; -struct ma_IAudioCaptureClient -{ - ma_IAudioCaptureClientVtbl* lpVtbl; -}; -static MA_INLINE HRESULT ma_IAudioCaptureClient_QueryInterface(ma_IAudioCaptureClient* pThis, const IID* const riid, void** ppObject) { return pThis->lpVtbl->QueryInterface(pThis, riid, ppObject); } -static MA_INLINE ULONG ma_IAudioCaptureClient_AddRef(ma_IAudioCaptureClient* pThis) { return pThis->lpVtbl->AddRef(pThis); } -static MA_INLINE ULONG ma_IAudioCaptureClient_Release(ma_IAudioCaptureClient* pThis) { return pThis->lpVtbl->Release(pThis); } -static MA_INLINE HRESULT ma_IAudioCaptureClient_GetBuffer(ma_IAudioCaptureClient* pThis, BYTE** ppData, ma_uint32* pNumFramesToRead, DWORD* pFlags, ma_uint64* pDevicePosition, ma_uint64* pQPCPosition) { return pThis->lpVtbl->GetBuffer(pThis, ppData, pNumFramesToRead, pFlags, pDevicePosition, pQPCPosition); } -static MA_INLINE HRESULT ma_IAudioCaptureClient_ReleaseBuffer(ma_IAudioCaptureClient* pThis, ma_uint32 numFramesRead) { return pThis->lpVtbl->ReleaseBuffer(pThis, numFramesRead); } -static MA_INLINE HRESULT ma_IAudioCaptureClient_GetNextPacketSize(ma_IAudioCaptureClient* pThis, ma_uint32* pNumFramesInNextPacket) { return pThis->lpVtbl->GetNextPacketSize(pThis, pNumFramesInNextPacket); } - -#if defined(MA_WIN32_UWP) -/* mmdevapi Functions */ -typedef HRESULT (WINAPI * MA_PFN_ActivateAudioInterfaceAsync)(const wchar_t* deviceInterfacePath, const IID* riid, MA_PROPVARIANT* activationParams, ma_IActivateAudioInterfaceCompletionHandler* completionHandler, ma_IActivateAudioInterfaceAsyncOperation** activationOperation); -#endif - -/* Avrt Functions */ -typedef HANDLE (WINAPI * MA_PFN_AvSetMmThreadCharacteristicsA)(const char* TaskName, DWORD* TaskIndex); -typedef BOOL (WINAPI * MA_PFN_AvRevertMmThreadCharacteristics)(HANDLE AvrtHandle); - -#if !defined(MA_WIN32_DESKTOP) && !defined(MA_WIN32_GDK) -typedef struct ma_completion_handler_uwp ma_completion_handler_uwp; - -typedef struct -{ - /* IUnknown */ - HRESULT (STDMETHODCALLTYPE * QueryInterface)(ma_completion_handler_uwp* pThis, const IID* const riid, void** ppObject); - ULONG (STDMETHODCALLTYPE * AddRef) (ma_completion_handler_uwp* pThis); - ULONG (STDMETHODCALLTYPE * Release) (ma_completion_handler_uwp* pThis); - - /* IActivateAudioInterfaceCompletionHandler */ - HRESULT (STDMETHODCALLTYPE * ActivateCompleted)(ma_completion_handler_uwp* pThis, ma_IActivateAudioInterfaceAsyncOperation* pActivateOperation); -} ma_completion_handler_uwp_vtbl; -struct ma_completion_handler_uwp -{ - ma_completion_handler_uwp_vtbl* lpVtbl; - MA_ATOMIC(4, ma_uint32) counter; - HANDLE hEvent; -}; - -static HRESULT STDMETHODCALLTYPE ma_completion_handler_uwp_QueryInterface(ma_completion_handler_uwp* pThis, const IID* const riid, void** ppObject) -{ - /* - We need to "implement" IAgileObject which is just an indicator that's used internally by WASAPI for some multithreading management. To - "implement" this, we just make sure we return pThis when the IAgileObject is requested. - */ - if (!ma_is_guid_equal(riid, &MA_IID_IUnknown) && !ma_is_guid_equal(riid, &MA_IID_IActivateAudioInterfaceCompletionHandler) && !ma_is_guid_equal(riid, &MA_IID_IAgileObject)) { - *ppObject = NULL; - return E_NOINTERFACE; - } - - /* Getting here means the IID is IUnknown or IMMNotificationClient. */ - *ppObject = (void*)pThis; - ((ma_completion_handler_uwp_vtbl*)pThis->lpVtbl)->AddRef(pThis); - return S_OK; -} - -static ULONG STDMETHODCALLTYPE ma_completion_handler_uwp_AddRef(ma_completion_handler_uwp* pThis) -{ - return (ULONG)ma_atomic_fetch_add_32(&pThis->counter, 1) + 1; -} - -static ULONG STDMETHODCALLTYPE ma_completion_handler_uwp_Release(ma_completion_handler_uwp* pThis) -{ - ma_uint32 newRefCount = ma_atomic_fetch_sub_32(&pThis->counter, 1) - 1; - if (newRefCount == 0) { - return 0; /* We don't free anything here because we never allocate the object on the heap. */ - } - - return (ULONG)newRefCount; -} - -static HRESULT STDMETHODCALLTYPE ma_completion_handler_uwp_ActivateCompleted(ma_completion_handler_uwp* pThis, ma_IActivateAudioInterfaceAsyncOperation* pActivateOperation) -{ - (void)pActivateOperation; - SetEvent(pThis->hEvent); - return S_OK; -} - - -static ma_completion_handler_uwp_vtbl g_maCompletionHandlerVtblInstance = { - ma_completion_handler_uwp_QueryInterface, - ma_completion_handler_uwp_AddRef, - ma_completion_handler_uwp_Release, - ma_completion_handler_uwp_ActivateCompleted -}; - -static ma_result ma_completion_handler_uwp_init(ma_completion_handler_uwp* pHandler) -{ - MA_ASSERT(pHandler != NULL); - MA_ZERO_OBJECT(pHandler); - - pHandler->lpVtbl = &g_maCompletionHandlerVtblInstance; - pHandler->counter = 1; - pHandler->hEvent = CreateEventA(NULL, FALSE, FALSE, NULL); - if (pHandler->hEvent == NULL) { - return ma_result_from_GetLastError(GetLastError()); - } - - return MA_SUCCESS; -} - -static void ma_completion_handler_uwp_uninit(ma_completion_handler_uwp* pHandler) -{ - if (pHandler->hEvent != NULL) { - CloseHandle(pHandler->hEvent); - } -} - -static void ma_completion_handler_uwp_wait(ma_completion_handler_uwp* pHandler) -{ - WaitForSingleObject((HANDLE)pHandler->hEvent, INFINITE); -} -#endif /* !MA_WIN32_DESKTOP */ - -/* We need a virtual table for our notification client object that's used for detecting changes to the default device. */ -#if defined(MA_WIN32_DESKTOP) || defined(MA_WIN32_GDK) -static HRESULT STDMETHODCALLTYPE ma_IMMNotificationClient_QueryInterface(ma_IMMNotificationClient* pThis, const IID* const riid, void** ppObject) -{ - /* - We care about two interfaces - IUnknown and IMMNotificationClient. If the requested IID is something else - we just return E_NOINTERFACE. Otherwise we need to increment the reference counter and return S_OK. - */ - if (!ma_is_guid_equal(riid, &MA_IID_IUnknown) && !ma_is_guid_equal(riid, &MA_IID_IMMNotificationClient)) { - *ppObject = NULL; - return E_NOINTERFACE; - } - - /* Getting here means the IID is IUnknown or IMMNotificationClient. */ - *ppObject = (void*)pThis; - ((ma_IMMNotificationClientVtbl*)pThis->lpVtbl)->AddRef(pThis); - return S_OK; -} - -static ULONG STDMETHODCALLTYPE ma_IMMNotificationClient_AddRef(ma_IMMNotificationClient* pThis) -{ - return (ULONG)ma_atomic_fetch_add_32(&pThis->counter, 1) + 1; -} - -static ULONG STDMETHODCALLTYPE ma_IMMNotificationClient_Release(ma_IMMNotificationClient* pThis) -{ - ma_uint32 newRefCount = ma_atomic_fetch_sub_32(&pThis->counter, 1) - 1; - if (newRefCount == 0) { - return 0; /* We don't free anything here because we never allocate the object on the heap. */ - } - - return (ULONG)newRefCount; -} - -static HRESULT STDMETHODCALLTYPE ma_IMMNotificationClient_OnDeviceStateChanged(ma_IMMNotificationClient* pThis, const WCHAR* pDeviceID, DWORD dwNewState) -{ - ma_bool32 isThisDevice = MA_FALSE; - ma_bool32 isCapture = MA_FALSE; - ma_bool32 isPlayback = MA_FALSE; - -#ifdef MA_DEBUG_OUTPUT - /*ma_log_postf(ma_device_get_log(pThis->pDevice), MA_LOG_LEVEL_DEBUG, "IMMNotificationClient_OnDeviceStateChanged(pDeviceID=%S, dwNewState=%u)\n", (pDeviceID != NULL) ? pDeviceID : L"(NULL)", (unsigned int)dwNewState);*/ -#endif - - /* - There have been reports of a hang when a playback device is disconnected. The idea with this code is to explicitly stop the device if we detect - that the device is disabled or has been unplugged. - */ - if (pThis->pDevice->wasapi.allowCaptureAutoStreamRouting && (pThis->pDevice->type == ma_device_type_capture || pThis->pDevice->type == ma_device_type_duplex || pThis->pDevice->type == ma_device_type_loopback)) { - isCapture = MA_TRUE; - if (ma_strcmp_WCHAR(pThis->pDevice->capture.id.wasapi, pDeviceID) == 0) { - isThisDevice = MA_TRUE; - } - } - - if (pThis->pDevice->wasapi.allowPlaybackAutoStreamRouting && (pThis->pDevice->type == ma_device_type_playback || pThis->pDevice->type == ma_device_type_duplex)) { - isPlayback = MA_TRUE; - if (ma_strcmp_WCHAR(pThis->pDevice->playback.id.wasapi, pDeviceID) == 0) { - isThisDevice = MA_TRUE; - } - } - - - /* - If the device ID matches our device we need to mark our device as detached and stop it. When a - device is added in OnDeviceAdded(), we'll restart it. We only mark it as detached if the device - was started at the time of being removed. - */ - if (isThisDevice) { - if ((dwNewState & MA_MM_DEVICE_STATE_ACTIVE) == 0) { - /* - Unplugged or otherwise unavailable. Mark as detached if we were in a playing state. We'll - use this to determine whether or not we need to automatically start the device when it's - plugged back in again. - */ - if (ma_device_get_state(pThis->pDevice) == ma_device_state_started) { - if (isPlayback) { - pThis->pDevice->wasapi.isDetachedPlayback = MA_TRUE; - } - if (isCapture) { - pThis->pDevice->wasapi.isDetachedCapture = MA_TRUE; - } - - ma_device_stop(pThis->pDevice); - } - } - - if ((dwNewState & MA_MM_DEVICE_STATE_ACTIVE) != 0) { - /* The device was activated. If we were detached, we need to start it again. */ - ma_bool8 tryRestartingDevice = MA_FALSE; - - if (isPlayback) { - if (pThis->pDevice->wasapi.isDetachedPlayback) { - pThis->pDevice->wasapi.isDetachedPlayback = MA_FALSE; - ma_device_reroute__wasapi(pThis->pDevice, ma_device_type_playback); - tryRestartingDevice = MA_TRUE; - } - } - - if (isCapture) { - if (pThis->pDevice->wasapi.isDetachedCapture) { - pThis->pDevice->wasapi.isDetachedCapture = MA_FALSE; - ma_device_reroute__wasapi(pThis->pDevice, (pThis->pDevice->type == ma_device_type_loopback) ? ma_device_type_loopback : ma_device_type_capture); - tryRestartingDevice = MA_TRUE; - } - } - - if (tryRestartingDevice) { - if (pThis->pDevice->wasapi.isDetachedPlayback == MA_FALSE && pThis->pDevice->wasapi.isDetachedCapture == MA_FALSE) { - ma_device_start(pThis->pDevice); - } - } - } - } - - return S_OK; -} - -static HRESULT STDMETHODCALLTYPE ma_IMMNotificationClient_OnDeviceAdded(ma_IMMNotificationClient* pThis, const WCHAR* pDeviceID) -{ -#ifdef MA_DEBUG_OUTPUT - /*ma_log_postf(ma_device_get_log(pThis->pDevice), MA_LOG_LEVEL_DEBUG, "IMMNotificationClient_OnDeviceAdded(pDeviceID=%S)\n", (pDeviceID != NULL) ? pDeviceID : L"(NULL)");*/ -#endif - - /* We don't need to worry about this event for our purposes. */ - (void)pThis; - (void)pDeviceID; - return S_OK; -} - -static HRESULT STDMETHODCALLTYPE ma_IMMNotificationClient_OnDeviceRemoved(ma_IMMNotificationClient* pThis, const WCHAR* pDeviceID) -{ -#ifdef MA_DEBUG_OUTPUT - /*ma_log_postf(ma_device_get_log(pThis->pDevice), MA_LOG_LEVEL_DEBUG, "IMMNotificationClient_OnDeviceRemoved(pDeviceID=%S)\n", (pDeviceID != NULL) ? pDeviceID : L"(NULL)");*/ -#endif - - /* We don't need to worry about this event for our purposes. */ - (void)pThis; - (void)pDeviceID; - return S_OK; -} - -static HRESULT STDMETHODCALLTYPE ma_IMMNotificationClient_OnDefaultDeviceChanged(ma_IMMNotificationClient* pThis, ma_EDataFlow dataFlow, ma_ERole role, const WCHAR* pDefaultDeviceID) -{ -#ifdef MA_DEBUG_OUTPUT - /*ma_log_postf(ma_device_get_log(pThis->pDevice), MA_LOG_LEVEL_DEBUG, "IMMNotificationClient_OnDefaultDeviceChanged(dataFlow=%d, role=%d, pDefaultDeviceID=%S)\n", dataFlow, role, (pDefaultDeviceID != NULL) ? pDefaultDeviceID : L"(NULL)");*/ -#endif - - (void)role; - - /* We only care about devices with the same data flow as the current device. */ - if ((pThis->pDevice->type == ma_device_type_playback && dataFlow != ma_eRender) || - (pThis->pDevice->type == ma_device_type_capture && dataFlow != ma_eCapture) || - (pThis->pDevice->type == ma_device_type_loopback && dataFlow != ma_eRender)) { - ma_log_postf(ma_device_get_log(pThis->pDevice), MA_LOG_LEVEL_DEBUG, "[WASAPI] Stream rerouting abandoned because dataFlow does match device type.\n"); - return S_OK; - } - - /* We need to consider dataFlow as ma_eCapture if device is ma_device_type_loopback */ - if (pThis->pDevice->type == ma_device_type_loopback) { - dataFlow = ma_eCapture; - } - - /* Don't do automatic stream routing if we're not allowed. */ - if ((dataFlow == ma_eRender && pThis->pDevice->wasapi.allowPlaybackAutoStreamRouting == MA_FALSE) || - (dataFlow == ma_eCapture && pThis->pDevice->wasapi.allowCaptureAutoStreamRouting == MA_FALSE)) { - ma_log_postf(ma_device_get_log(pThis->pDevice), MA_LOG_LEVEL_DEBUG, "[WASAPI] Stream rerouting abandoned because automatic stream routing has been disabled by the device config.\n"); - return S_OK; - } - - /* - Not currently supporting automatic stream routing in exclusive mode. This is not working correctly on my machine due to - AUDCLNT_E_DEVICE_IN_USE errors when reinitializing the device. If this is a bug in miniaudio, we can try re-enabling this once - it's fixed. - */ - if ((dataFlow == ma_eRender && pThis->pDevice->playback.shareMode == ma_share_mode_exclusive) || - (dataFlow == ma_eCapture && pThis->pDevice->capture.shareMode == ma_share_mode_exclusive)) { - ma_log_postf(ma_device_get_log(pThis->pDevice), MA_LOG_LEVEL_DEBUG, "[WASAPI] Stream rerouting abandoned because the device shared mode is exclusive.\n"); - return S_OK; - } - - - - /* - Second attempt at device rerouting. We're going to retrieve the device's state at the time of - the route change. We're then going to stop the device, reinitialize the device, and then start - it again if the state before stopping was ma_device_state_started. - */ - { - ma_uint32 previousState = ma_device_get_state(pThis->pDevice); - ma_bool8 restartDevice = MA_FALSE; - - if (previousState == ma_device_state_uninitialized || previousState == ma_device_state_starting) { - ma_log_postf(ma_device_get_log(pThis->pDevice), MA_LOG_LEVEL_DEBUG, "[WASAPI] Stream rerouting abandoned because the device is in the process of starting.\n"); - return S_OK; - } - - if (previousState == ma_device_state_started) { - ma_device_stop(pThis->pDevice); - restartDevice = MA_TRUE; - } - - if (pDefaultDeviceID != NULL) { /* <-- The input device ID will be null if there's no other device available. */ - ma_mutex_lock(&pThis->pDevice->wasapi.rerouteLock); - { - if (dataFlow == ma_eRender) { - ma_device_reroute__wasapi(pThis->pDevice, ma_device_type_playback); - - if (pThis->pDevice->wasapi.isDetachedPlayback) { - pThis->pDevice->wasapi.isDetachedPlayback = MA_FALSE; - - if (pThis->pDevice->type == ma_device_type_duplex && pThis->pDevice->wasapi.isDetachedCapture) { - restartDevice = MA_FALSE; /* It's a duplex device and the capture side is detached. We cannot be restarting the device just yet. */ - } - else { - restartDevice = MA_TRUE; /* It's not a duplex device, or the capture side is also attached so we can go ahead and restart the device. */ - } - } - } - else { - ma_device_reroute__wasapi(pThis->pDevice, (pThis->pDevice->type == ma_device_type_loopback) ? ma_device_type_loopback : ma_device_type_capture); - - if (pThis->pDevice->wasapi.isDetachedCapture) { - pThis->pDevice->wasapi.isDetachedCapture = MA_FALSE; - - if (pThis->pDevice->type == ma_device_type_duplex && pThis->pDevice->wasapi.isDetachedPlayback) { - restartDevice = MA_FALSE; /* It's a duplex device and the playback side is detached. We cannot be restarting the device just yet. */ - } - else { - restartDevice = MA_TRUE; /* It's not a duplex device, or the playback side is also attached so we can go ahead and restart the device. */ - } - } - } - } - ma_mutex_unlock(&pThis->pDevice->wasapi.rerouteLock); - - if (restartDevice) { - ma_device_start(pThis->pDevice); - } - } - } - - return S_OK; -} - -static HRESULT STDMETHODCALLTYPE ma_IMMNotificationClient_OnPropertyValueChanged(ma_IMMNotificationClient* pThis, const WCHAR* pDeviceID, const PROPERTYKEY key) -{ -#ifdef MA_DEBUG_OUTPUT - /*ma_log_postf(ma_device_get_log(pThis->pDevice), MA_LOG_LEVEL_DEBUG, "IMMNotificationClient_OnPropertyValueChanged(pDeviceID=%S)\n", (pDeviceID != NULL) ? pDeviceID : L"(NULL)");*/ -#endif - - (void)pThis; - (void)pDeviceID; - (void)key; - return S_OK; -} - -static ma_IMMNotificationClientVtbl g_maNotificationCientVtbl = { - ma_IMMNotificationClient_QueryInterface, - ma_IMMNotificationClient_AddRef, - ma_IMMNotificationClient_Release, - ma_IMMNotificationClient_OnDeviceStateChanged, - ma_IMMNotificationClient_OnDeviceAdded, - ma_IMMNotificationClient_OnDeviceRemoved, - ma_IMMNotificationClient_OnDefaultDeviceChanged, - ma_IMMNotificationClient_OnPropertyValueChanged -}; -#endif /* MA_WIN32_DESKTOP */ - -static const char* ma_to_usage_string__wasapi(ma_wasapi_usage usage) -{ - switch (usage) - { - case ma_wasapi_usage_default: return NULL; - case ma_wasapi_usage_games: return "Games"; - case ma_wasapi_usage_pro_audio: return "Pro Audio"; - default: break; - } - - return NULL; -} - -#if defined(MA_WIN32_DESKTOP) || defined(MA_WIN32_GDK) -typedef ma_IMMDevice ma_WASAPIDeviceInterface; -#else -typedef ma_IUnknown ma_WASAPIDeviceInterface; -#endif - - -#define MA_CONTEXT_COMMAND_QUIT__WASAPI 1 -#define MA_CONTEXT_COMMAND_CREATE_IAUDIOCLIENT__WASAPI 2 -#define MA_CONTEXT_COMMAND_RELEASE_IAUDIOCLIENT__WASAPI 3 - -static ma_context_command__wasapi ma_context_init_command__wasapi(int code) -{ - ma_context_command__wasapi cmd; - - MA_ZERO_OBJECT(&cmd); - cmd.code = code; - - return cmd; -} - -static ma_result ma_context_post_command__wasapi(ma_context* pContext, const ma_context_command__wasapi* pCmd) -{ - /* For now we are doing everything synchronously, but I might relax this later if the need arises. */ - ma_result result; - ma_bool32 isUsingLocalEvent = MA_FALSE; - ma_event localEvent; - - MA_ASSERT(pContext != NULL); - MA_ASSERT(pCmd != NULL); - - if (pCmd->pEvent == NULL) { - isUsingLocalEvent = MA_TRUE; - - result = ma_event_init(&localEvent); - if (result != MA_SUCCESS) { - return result; /* Failed to create the event for this command. */ - } - } - - /* Here is where we add the command to the list. If there's not enough room we'll spin until there is. */ - ma_mutex_lock(&pContext->wasapi.commandLock); - { - ma_uint32 index; - - /* Spin until we've got some space available. */ - while (pContext->wasapi.commandCount == ma_countof(pContext->wasapi.commands)) { - ma_yield(); - } - - /* Space is now available. Can safely add to the list. */ - index = (pContext->wasapi.commandIndex + pContext->wasapi.commandCount) % ma_countof(pContext->wasapi.commands); - pContext->wasapi.commands[index] = *pCmd; - pContext->wasapi.commands[index].pEvent = &localEvent; - pContext->wasapi.commandCount += 1; - - /* Now that the command has been added, release the semaphore so ma_context_next_command__wasapi() can return. */ - ma_semaphore_release(&pContext->wasapi.commandSem); - } - ma_mutex_unlock(&pContext->wasapi.commandLock); - - if (isUsingLocalEvent) { - ma_event_wait(&localEvent); - ma_event_uninit(&localEvent); - } - - return MA_SUCCESS; -} - -static ma_result ma_context_next_command__wasapi(ma_context* pContext, ma_context_command__wasapi* pCmd) -{ - ma_result result = MA_SUCCESS; - - MA_ASSERT(pContext != NULL); - MA_ASSERT(pCmd != NULL); - - result = ma_semaphore_wait(&pContext->wasapi.commandSem); - if (result == MA_SUCCESS) { - ma_mutex_lock(&pContext->wasapi.commandLock); - { - *pCmd = pContext->wasapi.commands[pContext->wasapi.commandIndex]; - pContext->wasapi.commandIndex = (pContext->wasapi.commandIndex + 1) % ma_countof(pContext->wasapi.commands); - pContext->wasapi.commandCount -= 1; - } - ma_mutex_unlock(&pContext->wasapi.commandLock); - } - - return result; -} - -static ma_thread_result MA_THREADCALL ma_context_command_thread__wasapi(void* pUserData) -{ - ma_result result; - ma_context* pContext = (ma_context*)pUserData; - MA_ASSERT(pContext != NULL); - - for (;;) { - ma_context_command__wasapi cmd; - result = ma_context_next_command__wasapi(pContext, &cmd); - if (result != MA_SUCCESS) { - break; - } - - switch (cmd.code) - { - case MA_CONTEXT_COMMAND_QUIT__WASAPI: - { - /* Do nothing. Handled after the switch. */ - } break; - - case MA_CONTEXT_COMMAND_CREATE_IAUDIOCLIENT__WASAPI: - { - if (cmd.data.createAudioClient.deviceType == ma_device_type_playback) { - *cmd.data.createAudioClient.pResult = ma_result_from_HRESULT(ma_IAudioClient_GetService((ma_IAudioClient*)cmd.data.createAudioClient.pAudioClient, &MA_IID_IAudioRenderClient, cmd.data.createAudioClient.ppAudioClientService)); - } else { - *cmd.data.createAudioClient.pResult = ma_result_from_HRESULT(ma_IAudioClient_GetService((ma_IAudioClient*)cmd.data.createAudioClient.pAudioClient, &MA_IID_IAudioCaptureClient, cmd.data.createAudioClient.ppAudioClientService)); - } - } break; - - case MA_CONTEXT_COMMAND_RELEASE_IAUDIOCLIENT__WASAPI: - { - if (cmd.data.releaseAudioClient.deviceType == ma_device_type_playback) { - if (cmd.data.releaseAudioClient.pDevice->wasapi.pAudioClientPlayback != NULL) { - ma_IAudioClient_Release((ma_IAudioClient*)cmd.data.releaseAudioClient.pDevice->wasapi.pAudioClientPlayback); - cmd.data.releaseAudioClient.pDevice->wasapi.pAudioClientPlayback = NULL; - } - } - - if (cmd.data.releaseAudioClient.deviceType == ma_device_type_capture) { - if (cmd.data.releaseAudioClient.pDevice->wasapi.pAudioClientCapture != NULL) { - ma_IAudioClient_Release((ma_IAudioClient*)cmd.data.releaseAudioClient.pDevice->wasapi.pAudioClientCapture); - cmd.data.releaseAudioClient.pDevice->wasapi.pAudioClientCapture = NULL; - } - } - } break; - - default: - { - /* Unknown command. Ignore it, but trigger an assert in debug mode so we're aware of it. */ - MA_ASSERT(MA_FALSE); - } break; - } - - if (cmd.pEvent != NULL) { - ma_event_signal(cmd.pEvent); - } - - if (cmd.code == MA_CONTEXT_COMMAND_QUIT__WASAPI) { - break; /* Received a quit message. Get out of here. */ - } - } - - return (ma_thread_result)0; -} - -static ma_result ma_device_create_IAudioClient_service__wasapi(ma_context* pContext, ma_device_type deviceType, ma_IAudioClient* pAudioClient, void** ppAudioClientService) -{ - ma_result result; - ma_result cmdResult; - ma_context_command__wasapi cmd = ma_context_init_command__wasapi(MA_CONTEXT_COMMAND_CREATE_IAUDIOCLIENT__WASAPI); - cmd.data.createAudioClient.deviceType = deviceType; - cmd.data.createAudioClient.pAudioClient = (void*)pAudioClient; - cmd.data.createAudioClient.ppAudioClientService = ppAudioClientService; - cmd.data.createAudioClient.pResult = &cmdResult; /* Declared locally, but won't be dereferenced after this function returns since execution of the command will wait here. */ - - result = ma_context_post_command__wasapi(pContext, &cmd); /* This will not return until the command has actually been run. */ - if (result != MA_SUCCESS) { - return result; - } - - return *cmd.data.createAudioClient.pResult; -} - -#if 0 /* Not used at the moment, but leaving here for future use. */ -static ma_result ma_device_release_IAudioClient_service__wasapi(ma_device* pDevice, ma_device_type deviceType) -{ - ma_result result; - ma_context_command__wasapi cmd = ma_context_init_command__wasapi(MA_CONTEXT_COMMAND_RELEASE_IAUDIOCLIENT__WASAPI); - cmd.data.releaseAudioClient.pDevice = pDevice; - cmd.data.releaseAudioClient.deviceType = deviceType; - - result = ma_context_post_command__wasapi(pDevice->pContext, &cmd); /* This will not return until the command has actually been run. */ - if (result != MA_SUCCESS) { - return result; - } - - return MA_SUCCESS; -} -#endif - - -static void ma_add_native_data_format_to_device_info_from_WAVEFORMATEX(const MA_WAVEFORMATEX* pWF, ma_share_mode shareMode, ma_device_info* pInfo) -{ - MA_ASSERT(pWF != NULL); - MA_ASSERT(pInfo != NULL); - - if (pInfo->nativeDataFormatCount >= ma_countof(pInfo->nativeDataFormats)) { - return; /* Too many data formats. Need to ignore this one. Don't think this should ever happen with WASAPI. */ - } - - pInfo->nativeDataFormats[pInfo->nativeDataFormatCount].format = ma_format_from_WAVEFORMATEX(pWF); - pInfo->nativeDataFormats[pInfo->nativeDataFormatCount].channels = pWF->nChannels; - pInfo->nativeDataFormats[pInfo->nativeDataFormatCount].sampleRate = pWF->nSamplesPerSec; - pInfo->nativeDataFormats[pInfo->nativeDataFormatCount].flags = (shareMode == ma_share_mode_exclusive) ? MA_DATA_FORMAT_FLAG_EXCLUSIVE_MODE : 0; - pInfo->nativeDataFormatCount += 1; -} - -static ma_result ma_context_get_device_info_from_IAudioClient__wasapi(ma_context* pContext, /*ma_IMMDevice**/void* pMMDevice, ma_IAudioClient* pAudioClient, ma_device_info* pInfo) -{ - HRESULT hr; - MA_WAVEFORMATEX* pWF = NULL; - - MA_ASSERT(pAudioClient != NULL); - MA_ASSERT(pInfo != NULL); - - /* Shared Mode. We use GetMixFormat() here. */ - hr = ma_IAudioClient_GetMixFormat((ma_IAudioClient*)pAudioClient, (MA_WAVEFORMATEX**)&pWF); - if (SUCCEEDED(hr)) { - ma_add_native_data_format_to_device_info_from_WAVEFORMATEX(pWF, ma_share_mode_shared, pInfo); - } else { - ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to retrieve mix format for device info retrieval."); - return ma_result_from_HRESULT(hr); - } - - /* - Exclusive Mode. We repeatedly call IsFormatSupported() here. This is not currently supported on - UWP. Failure to retrieve the exclusive mode format is not considered an error, so from here on - out, MA_SUCCESS is guaranteed to be returned. - */ - #if defined(MA_WIN32_DESKTOP) || defined(MA_WIN32_GDK) - { - ma_IPropertyStore *pProperties; - - /* - The first thing to do is get the format from PKEY_AudioEngine_DeviceFormat. This should give us a channel count we assume is - correct which will simplify our searching. - */ - hr = ma_IMMDevice_OpenPropertyStore((ma_IMMDevice*)pMMDevice, STGM_READ, &pProperties); - if (SUCCEEDED(hr)) { - MA_PROPVARIANT var; - ma_PropVariantInit(&var); - - hr = ma_IPropertyStore_GetValue(pProperties, &MA_PKEY_AudioEngine_DeviceFormat, &var); - if (SUCCEEDED(hr)) { - pWF = (MA_WAVEFORMATEX*)var.blob.pBlobData; - - /* - In my testing, the format returned by PKEY_AudioEngine_DeviceFormat is suitable for exclusive mode so we check this format - first. If this fails, fall back to a search. - */ - hr = ma_IAudioClient_IsFormatSupported((ma_IAudioClient*)pAudioClient, MA_AUDCLNT_SHAREMODE_EXCLUSIVE, pWF, NULL); - if (SUCCEEDED(hr)) { - /* The format returned by PKEY_AudioEngine_DeviceFormat is supported. */ - ma_add_native_data_format_to_device_info_from_WAVEFORMATEX(pWF, ma_share_mode_exclusive, pInfo); - } else { - /* - The format returned by PKEY_AudioEngine_DeviceFormat is not supported, so fall back to a search. We assume the channel - count returned by MA_PKEY_AudioEngine_DeviceFormat is valid and correct. For simplicity we're only returning one format. - */ - ma_uint32 channels = pWF->nChannels; - ma_channel defaultChannelMap[MA_MAX_CHANNELS]; - MA_WAVEFORMATEXTENSIBLE wf; - ma_bool32 found; - ma_uint32 iFormat; - - /* Make sure we don't overflow the channel map. */ - if (channels > MA_MAX_CHANNELS) { - channels = MA_MAX_CHANNELS; - } - - ma_channel_map_init_standard(ma_standard_channel_map_microsoft, defaultChannelMap, ma_countof(defaultChannelMap), channels); - - MA_ZERO_OBJECT(&wf); - wf.cbSize = sizeof(wf); - wf.wFormatTag = WAVE_FORMAT_EXTENSIBLE; - wf.nChannels = (WORD)channels; - wf.dwChannelMask = ma_channel_map_to_channel_mask__win32(defaultChannelMap, channels); - - found = MA_FALSE; - for (iFormat = 0; iFormat < ma_countof(g_maFormatPriorities); ++iFormat) { - ma_format format = g_maFormatPriorities[iFormat]; - ma_uint32 iSampleRate; - - wf.wBitsPerSample = (WORD)(ma_get_bytes_per_sample(format)*8); - wf.nBlockAlign = (WORD)(wf.nChannels * wf.wBitsPerSample / 8); - wf.nAvgBytesPerSec = wf.nBlockAlign * wf.nSamplesPerSec; - wf.Samples.wValidBitsPerSample = /*(format == ma_format_s24_32) ? 24 :*/ wf.wBitsPerSample; - if (format == ma_format_f32) { - wf.SubFormat = MA_GUID_KSDATAFORMAT_SUBTYPE_IEEE_FLOAT; - } else { - wf.SubFormat = MA_GUID_KSDATAFORMAT_SUBTYPE_PCM; - } - - for (iSampleRate = 0; iSampleRate < ma_countof(g_maStandardSampleRatePriorities); ++iSampleRate) { - wf.nSamplesPerSec = g_maStandardSampleRatePriorities[iSampleRate]; - - hr = ma_IAudioClient_IsFormatSupported((ma_IAudioClient*)pAudioClient, MA_AUDCLNT_SHAREMODE_EXCLUSIVE, (MA_WAVEFORMATEX*)&wf, NULL); - if (SUCCEEDED(hr)) { - ma_add_native_data_format_to_device_info_from_WAVEFORMATEX((MA_WAVEFORMATEX*)&wf, ma_share_mode_exclusive, pInfo); - found = MA_TRUE; - break; - } - } - - if (found) { - break; - } - } - - ma_PropVariantClear(pContext, &var); - - if (!found) { - ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_WARNING, "[WASAPI] Failed to find suitable device format for device info retrieval."); - } - } - } else { - ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_WARNING, "[WASAPI] Failed to retrieve device format for device info retrieval."); - } - - ma_IPropertyStore_Release(pProperties); - } else { - ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_WARNING, "[WASAPI] Failed to open property store for device info retrieval."); - } - } - #else - { - (void)pMMDevice; /* Unused. */ - } - #endif - - return MA_SUCCESS; -} - -#if defined(MA_WIN32_DESKTOP) || defined(MA_WIN32_GDK) -static ma_EDataFlow ma_device_type_to_EDataFlow(ma_device_type deviceType) -{ - if (deviceType == ma_device_type_playback) { - return ma_eRender; - } else if (deviceType == ma_device_type_capture) { - return ma_eCapture; - } else { - MA_ASSERT(MA_FALSE); - return ma_eRender; /* Should never hit this. */ - } -} - -static ma_result ma_context_create_IMMDeviceEnumerator__wasapi(ma_context* pContext, ma_IMMDeviceEnumerator** ppDeviceEnumerator) -{ - HRESULT hr; - ma_IMMDeviceEnumerator* pDeviceEnumerator; - - MA_ASSERT(pContext != NULL); - MA_ASSERT(ppDeviceEnumerator != NULL); - - *ppDeviceEnumerator = NULL; /* Safety. */ - - hr = ma_CoCreateInstance(pContext, &MA_CLSID_MMDeviceEnumerator, NULL, CLSCTX_ALL, &MA_IID_IMMDeviceEnumerator, (void**)&pDeviceEnumerator); - if (FAILED(hr)) { - ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to create device enumerator."); - return ma_result_from_HRESULT(hr); - } - - *ppDeviceEnumerator = pDeviceEnumerator; - - return MA_SUCCESS; -} - -static WCHAR* ma_context_get_default_device_id_from_IMMDeviceEnumerator__wasapi(ma_context* pContext, ma_IMMDeviceEnumerator* pDeviceEnumerator, ma_device_type deviceType) -{ - HRESULT hr; - ma_IMMDevice* pMMDefaultDevice = NULL; - WCHAR* pDefaultDeviceID = NULL; - ma_EDataFlow dataFlow; - ma_ERole role; - - MA_ASSERT(pContext != NULL); - MA_ASSERT(pDeviceEnumerator != NULL); - - (void)pContext; - - /* Grab the EDataFlow type from the device type. */ - dataFlow = ma_device_type_to_EDataFlow(deviceType); - - /* The role is always eConsole, but we may make this configurable later. */ - role = ma_eConsole; - - hr = ma_IMMDeviceEnumerator_GetDefaultAudioEndpoint(pDeviceEnumerator, dataFlow, role, &pMMDefaultDevice); - if (FAILED(hr)) { - return NULL; - } - - hr = ma_IMMDevice_GetId(pMMDefaultDevice, &pDefaultDeviceID); - - ma_IMMDevice_Release(pMMDefaultDevice); - pMMDefaultDevice = NULL; - - if (FAILED(hr)) { - return NULL; - } - - return pDefaultDeviceID; -} - -static WCHAR* ma_context_get_default_device_id__wasapi(ma_context* pContext, ma_device_type deviceType) /* Free the returned pointer with ma_CoTaskMemFree() */ -{ - ma_result result; - ma_IMMDeviceEnumerator* pDeviceEnumerator; - WCHAR* pDefaultDeviceID = NULL; - - MA_ASSERT(pContext != NULL); - - result = ma_context_create_IMMDeviceEnumerator__wasapi(pContext, &pDeviceEnumerator); - if (result != MA_SUCCESS) { - return NULL; - } - - pDefaultDeviceID = ma_context_get_default_device_id_from_IMMDeviceEnumerator__wasapi(pContext, pDeviceEnumerator, deviceType); - - ma_IMMDeviceEnumerator_Release(pDeviceEnumerator); - return pDefaultDeviceID; -} - -static ma_result ma_context_get_MMDevice__wasapi(ma_context* pContext, ma_device_type deviceType, const ma_device_id* pDeviceID, ma_IMMDevice** ppMMDevice) -{ - ma_IMMDeviceEnumerator* pDeviceEnumerator; - HRESULT hr; - - MA_ASSERT(pContext != NULL); - MA_ASSERT(ppMMDevice != NULL); - - /* - This weird COM init/uninit here is a hack to work around a crash when changing devices. What is happening is - WASAPI fires a callback from another thread when the device is changed. It's from that thread where this - function is getting called. What I'm suspecting is that the other thread is not initializing COM which in turn - results in CoCreateInstance() failing. - - The community has reported that this seems to fix the crash. There are future plans to move all WASAPI operation - over to a single thread to make everything safer, but in the meantime while we wait for that to come online I'm - happy enough to use this hack instead. - */ - ma_CoInitializeEx(pContext, NULL, MA_COINIT_VALUE); - { - hr = ma_CoCreateInstance(pContext, &MA_CLSID_MMDeviceEnumerator, NULL, CLSCTX_ALL, &MA_IID_IMMDeviceEnumerator, (void**)&pDeviceEnumerator); - } - ma_CoUninitialize(pContext); - - if (FAILED(hr)) { /* <-- This is checking the call above to ma_CoCreateInstance(). */ - ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to create IMMDeviceEnumerator.\n"); - return ma_result_from_HRESULT(hr); - } - - if (pDeviceID == NULL) { - hr = ma_IMMDeviceEnumerator_GetDefaultAudioEndpoint(pDeviceEnumerator, (deviceType == ma_device_type_capture) ? ma_eCapture : ma_eRender, ma_eConsole, ppMMDevice); - } else { - hr = ma_IMMDeviceEnumerator_GetDevice(pDeviceEnumerator, pDeviceID->wasapi, ppMMDevice); - } - - ma_IMMDeviceEnumerator_Release(pDeviceEnumerator); - if (FAILED(hr)) { - ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to retrieve IMMDevice.\n"); - return ma_result_from_HRESULT(hr); - } - - return MA_SUCCESS; -} - -static ma_result ma_context_get_device_id_from_MMDevice__wasapi(ma_context* pContext, ma_IMMDevice* pMMDevice, ma_device_id* pDeviceID) -{ - WCHAR* pDeviceIDString; - HRESULT hr; - - MA_ASSERT(pDeviceID != NULL); - - hr = ma_IMMDevice_GetId(pMMDevice, &pDeviceIDString); - if (SUCCEEDED(hr)) { - size_t idlen = ma_strlen_WCHAR(pDeviceIDString); - if (idlen+1 > ma_countof(pDeviceID->wasapi)) { - ma_CoTaskMemFree(pContext, pDeviceIDString); - MA_ASSERT(MA_FALSE); /* NOTE: If this is triggered, please report it. It means the format of the ID must have changed and is too long to fit in our fixed sized buffer. */ - return MA_ERROR; - } - - MA_COPY_MEMORY(pDeviceID->wasapi, pDeviceIDString, idlen * sizeof(wchar_t)); - pDeviceID->wasapi[idlen] = '\0'; - - ma_CoTaskMemFree(pContext, pDeviceIDString); - - return MA_SUCCESS; - } - - return MA_ERROR; -} - -static ma_result ma_context_get_device_info_from_MMDevice__wasapi(ma_context* pContext, ma_IMMDevice* pMMDevice, WCHAR* pDefaultDeviceID, ma_bool32 onlySimpleInfo, ma_device_info* pInfo) -{ - ma_result result; - HRESULT hr; - - MA_ASSERT(pContext != NULL); - MA_ASSERT(pMMDevice != NULL); - MA_ASSERT(pInfo != NULL); - - /* ID. */ - result = ma_context_get_device_id_from_MMDevice__wasapi(pContext, pMMDevice, &pInfo->id); - if (result == MA_SUCCESS) { - if (pDefaultDeviceID != NULL) { - if (ma_strcmp_WCHAR(pInfo->id.wasapi, pDefaultDeviceID) == 0) { - pInfo->isDefault = MA_TRUE; - } - } - } - - /* Description / Friendly Name */ - { - ma_IPropertyStore *pProperties; - hr = ma_IMMDevice_OpenPropertyStore(pMMDevice, STGM_READ, &pProperties); - if (SUCCEEDED(hr)) { - MA_PROPVARIANT var; - - ma_PropVariantInit(&var); - hr = ma_IPropertyStore_GetValue(pProperties, &MA_PKEY_Device_FriendlyName, &var); - if (SUCCEEDED(hr)) { - WideCharToMultiByte(CP_UTF8, 0, var.pwszVal, -1, pInfo->name, sizeof(pInfo->name), 0, FALSE); - ma_PropVariantClear(pContext, &var); - } - - ma_IPropertyStore_Release(pProperties); - } - } - - /* Format */ - if (!onlySimpleInfo) { - ma_IAudioClient* pAudioClient; - hr = ma_IMMDevice_Activate(pMMDevice, &MA_IID_IAudioClient, CLSCTX_ALL, NULL, (void**)&pAudioClient); - if (SUCCEEDED(hr)) { - result = ma_context_get_device_info_from_IAudioClient__wasapi(pContext, pMMDevice, pAudioClient, pInfo); - - ma_IAudioClient_Release(pAudioClient); - return result; - } else { - ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to activate audio client for device info retrieval."); - return ma_result_from_HRESULT(hr); - } - } - - return MA_SUCCESS; -} - -static ma_result ma_context_enumerate_devices_by_type__wasapi(ma_context* pContext, ma_IMMDeviceEnumerator* pDeviceEnumerator, ma_device_type deviceType, ma_enum_devices_callback_proc callback, void* pUserData) -{ - ma_result result = MA_SUCCESS; - UINT deviceCount; - HRESULT hr; - ma_uint32 iDevice; - WCHAR* pDefaultDeviceID = NULL; - ma_IMMDeviceCollection* pDeviceCollection = NULL; - - MA_ASSERT(pContext != NULL); - MA_ASSERT(callback != NULL); - - /* Grab the default device. We use this to know whether or not flag the returned device info as being the default. */ - pDefaultDeviceID = ma_context_get_default_device_id_from_IMMDeviceEnumerator__wasapi(pContext, pDeviceEnumerator, deviceType); - - /* We need to enumerate the devices which returns a device collection. */ - hr = ma_IMMDeviceEnumerator_EnumAudioEndpoints(pDeviceEnumerator, ma_device_type_to_EDataFlow(deviceType), MA_MM_DEVICE_STATE_ACTIVE, &pDeviceCollection); - if (SUCCEEDED(hr)) { - hr = ma_IMMDeviceCollection_GetCount(pDeviceCollection, &deviceCount); - if (FAILED(hr)) { - ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to get device count.\n"); - result = ma_result_from_HRESULT(hr); - goto done; - } - - for (iDevice = 0; iDevice < deviceCount; ++iDevice) { - ma_device_info deviceInfo; - ma_IMMDevice* pMMDevice; - - MA_ZERO_OBJECT(&deviceInfo); - - hr = ma_IMMDeviceCollection_Item(pDeviceCollection, iDevice, &pMMDevice); - if (SUCCEEDED(hr)) { - result = ma_context_get_device_info_from_MMDevice__wasapi(pContext, pMMDevice, pDefaultDeviceID, MA_TRUE, &deviceInfo); /* MA_TRUE = onlySimpleInfo. */ - - ma_IMMDevice_Release(pMMDevice); - if (result == MA_SUCCESS) { - ma_bool32 cbResult = callback(pContext, deviceType, &deviceInfo, pUserData); - if (cbResult == MA_FALSE) { - break; - } - } - } - } - } - -done: - if (pDefaultDeviceID != NULL) { - ma_CoTaskMemFree(pContext, pDefaultDeviceID); - pDefaultDeviceID = NULL; - } - - if (pDeviceCollection != NULL) { - ma_IMMDeviceCollection_Release(pDeviceCollection); - pDeviceCollection = NULL; - } - - return result; -} - -static ma_result ma_context_get_IAudioClient_Desktop__wasapi(ma_context* pContext, ma_device_type deviceType, const ma_device_id* pDeviceID, MA_PROPVARIANT* pActivationParams, ma_IAudioClient** ppAudioClient, ma_IMMDevice** ppMMDevice) -{ - ma_result result; - HRESULT hr; - - MA_ASSERT(pContext != NULL); - MA_ASSERT(ppAudioClient != NULL); - MA_ASSERT(ppMMDevice != NULL); - - result = ma_context_get_MMDevice__wasapi(pContext, deviceType, pDeviceID, ppMMDevice); - if (result != MA_SUCCESS) { - return result; - } - - hr = ma_IMMDevice_Activate(*ppMMDevice, &MA_IID_IAudioClient, CLSCTX_ALL, pActivationParams, (void**)ppAudioClient); - if (FAILED(hr)) { - return ma_result_from_HRESULT(hr); - } - - return MA_SUCCESS; -} -#else -static ma_result ma_context_get_IAudioClient_UWP__wasapi(ma_context* pContext, ma_device_type deviceType, const ma_device_id* pDeviceID, MA_PROPVARIANT* pActivationParams, ma_IAudioClient** ppAudioClient, ma_IUnknown** ppActivatedInterface) -{ - ma_IActivateAudioInterfaceAsyncOperation *pAsyncOp = NULL; - ma_completion_handler_uwp completionHandler; - IID iid; - WCHAR* iidStr; - HRESULT hr; - ma_result result; - HRESULT activateResult; - ma_IUnknown* pActivatedInterface; - - MA_ASSERT(pContext != NULL); - MA_ASSERT(ppAudioClient != NULL); - - if (pDeviceID != NULL) { - iidStr = (WCHAR*)pDeviceID->wasapi; - } else { - if (deviceType == ma_device_type_capture) { - iid = MA_IID_DEVINTERFACE_AUDIO_CAPTURE; - } else { - iid = MA_IID_DEVINTERFACE_AUDIO_RENDER; - } - - #if defined(__cplusplus) - hr = StringFromIID(iid, &iidStr); - #else - hr = StringFromIID(&iid, &iidStr); - #endif - if (FAILED(hr)) { - ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to convert device IID to string for ActivateAudioInterfaceAsync(). Out of memory.\n"); - return ma_result_from_HRESULT(hr); - } - } - - result = ma_completion_handler_uwp_init(&completionHandler); - if (result != MA_SUCCESS) { - ma_CoTaskMemFree(pContext, iidStr); - ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to create event for waiting for ActivateAudioInterfaceAsync().\n"); - return result; - } - - hr = ((MA_PFN_ActivateAudioInterfaceAsync)pContext->wasapi.ActivateAudioInterfaceAsync)(iidStr, &MA_IID_IAudioClient, pActivationParams, (ma_IActivateAudioInterfaceCompletionHandler*)&completionHandler, (ma_IActivateAudioInterfaceAsyncOperation**)&pAsyncOp); - if (FAILED(hr)) { - ma_completion_handler_uwp_uninit(&completionHandler); - ma_CoTaskMemFree(pContext, iidStr); - ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[WASAPI] ActivateAudioInterfaceAsync() failed.\n"); - return ma_result_from_HRESULT(hr); - } - - if (pDeviceID == NULL) { - ma_CoTaskMemFree(pContext, iidStr); - } - - /* Wait for the async operation for finish. */ - ma_completion_handler_uwp_wait(&completionHandler); - ma_completion_handler_uwp_uninit(&completionHandler); - - hr = ma_IActivateAudioInterfaceAsyncOperation_GetActivateResult(pAsyncOp, &activateResult, &pActivatedInterface); - ma_IActivateAudioInterfaceAsyncOperation_Release(pAsyncOp); - - if (FAILED(hr) || FAILED(activateResult)) { - ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to activate device.\n"); - return FAILED(hr) ? ma_result_from_HRESULT(hr) : ma_result_from_HRESULT(activateResult); - } - - /* Here is where we grab the IAudioClient interface. */ - hr = ma_IUnknown_QueryInterface(pActivatedInterface, &MA_IID_IAudioClient, (void**)ppAudioClient); - if (FAILED(hr)) { - ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to query IAudioClient interface.\n"); - return ma_result_from_HRESULT(hr); - } - - if (ppActivatedInterface) { - *ppActivatedInterface = pActivatedInterface; - } else { - ma_IUnknown_Release(pActivatedInterface); - } - - return MA_SUCCESS; -} -#endif - - -/* https://docs.microsoft.com/en-us/windows/win32/api/audioclientactivationparams/ne-audioclientactivationparams-audioclient_activation_type */ -typedef enum -{ - MA_AUDIOCLIENT_ACTIVATION_TYPE_DEFAULT, - MA_AUDIOCLIENT_ACTIVATION_TYPE_PROCESS_LOOPBACK -} MA_AUDIOCLIENT_ACTIVATION_TYPE; - -/* https://docs.microsoft.com/en-us/windows/win32/api/audioclientactivationparams/ne-audioclientactivationparams-process_loopback_mode */ -typedef enum -{ - MA_PROCESS_LOOPBACK_MODE_INCLUDE_TARGET_PROCESS_TREE, - MA_PROCESS_LOOPBACK_MODE_EXCLUDE_TARGET_PROCESS_TREE -} MA_PROCESS_LOOPBACK_MODE; - -/* https://docs.microsoft.com/en-us/windows/win32/api/audioclientactivationparams/ns-audioclientactivationparams-audioclient_process_loopback_params */ -typedef struct -{ - DWORD TargetProcessId; - MA_PROCESS_LOOPBACK_MODE ProcessLoopbackMode; -} MA_AUDIOCLIENT_PROCESS_LOOPBACK_PARAMS; - -#if defined(_MSC_VER) && !defined(__clang__) - #pragma warning(push) - #pragma warning(disable:4201) /* nonstandard extension used: nameless struct/union */ -#elif defined(__clang__) || (defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 8))) - #pragma GCC diagnostic push - #pragma GCC diagnostic ignored "-Wpedantic" /* For ISO C99 doesn't support unnamed structs/unions [-Wpedantic] */ - #if defined(__clang__) - #pragma GCC diagnostic ignored "-Wc11-extensions" /* anonymous unions are a C11 extension */ - #endif -#endif -/* https://docs.microsoft.com/en-us/windows/win32/api/audioclientactivationparams/ns-audioclientactivationparams-audioclient_activation_params */ -typedef struct -{ - MA_AUDIOCLIENT_ACTIVATION_TYPE ActivationType; - union - { - MA_AUDIOCLIENT_PROCESS_LOOPBACK_PARAMS ProcessLoopbackParams; - }; -} MA_AUDIOCLIENT_ACTIVATION_PARAMS; -#if defined(_MSC_VER) && !defined(__clang__) - #pragma warning(pop) -#elif defined(__clang__) || (defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 8))) - #pragma GCC diagnostic pop -#endif - -#define MA_VIRTUAL_AUDIO_DEVICE_PROCESS_LOOPBACK L"VAD\\Process_Loopback" - -static ma_result ma_context_get_IAudioClient__wasapi(ma_context* pContext, ma_device_type deviceType, const ma_device_id* pDeviceID, ma_uint32 loopbackProcessID, ma_bool32 loopbackProcessExclude, ma_IAudioClient** ppAudioClient, ma_WASAPIDeviceInterface** ppDeviceInterface) -{ - ma_result result; - ma_bool32 usingProcessLoopback = MA_FALSE; - MA_AUDIOCLIENT_ACTIVATION_PARAMS audioclientActivationParams; - MA_PROPVARIANT activationParams; - MA_PROPVARIANT* pActivationParams = NULL; - ma_device_id virtualDeviceID; - - /* Activation parameters specific to loopback mode. Note that process-specific loopback will only work when a default device ID is specified. */ - if (deviceType == ma_device_type_loopback && loopbackProcessID != 0 && pDeviceID == NULL) { - usingProcessLoopback = MA_TRUE; - } - - if (usingProcessLoopback) { - MA_ZERO_OBJECT(&audioclientActivationParams); - audioclientActivationParams.ActivationType = MA_AUDIOCLIENT_ACTIVATION_TYPE_PROCESS_LOOPBACK; - audioclientActivationParams.ProcessLoopbackParams.ProcessLoopbackMode = (loopbackProcessExclude) ? MA_PROCESS_LOOPBACK_MODE_EXCLUDE_TARGET_PROCESS_TREE : MA_PROCESS_LOOPBACK_MODE_INCLUDE_TARGET_PROCESS_TREE; - audioclientActivationParams.ProcessLoopbackParams.TargetProcessId = (DWORD)loopbackProcessID; - - ma_PropVariantInit(&activationParams); - activationParams.vt = MA_VT_BLOB; - activationParams.blob.cbSize = sizeof(audioclientActivationParams); - activationParams.blob.pBlobData = (BYTE*)&audioclientActivationParams; - pActivationParams = &activationParams; - - /* When requesting a specific device ID we need to use a special device ID. */ - MA_COPY_MEMORY(virtualDeviceID.wasapi, MA_VIRTUAL_AUDIO_DEVICE_PROCESS_LOOPBACK, (wcslen(MA_VIRTUAL_AUDIO_DEVICE_PROCESS_LOOPBACK) + 1) * sizeof(wchar_t)); /* +1 for the null terminator. */ - pDeviceID = &virtualDeviceID; - } else { - pActivationParams = NULL; /* No activation parameters required. */ - } - -#if defined(MA_WIN32_DESKTOP) || defined(MA_WIN32_GDK) - result = ma_context_get_IAudioClient_Desktop__wasapi(pContext, deviceType, pDeviceID, pActivationParams, ppAudioClient, ppDeviceInterface); -#else - result = ma_context_get_IAudioClient_UWP__wasapi(pContext, deviceType, pDeviceID, pActivationParams, ppAudioClient, ppDeviceInterface); -#endif - - /* - If loopback mode was requested with a process ID and initialization failed, it could be because it's - trying to run on an older version of Windows where it's not supported. We need to let the caller - know about this with a log message. - */ - if (result != MA_SUCCESS) { - if (usingProcessLoopback) { - ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[WASAPI] Loopback mode requested to %s process ID %u, but initialization failed. Support for this feature begins with Windows 10 Build 20348. Confirm your version of Windows or consider not using process-specific loopback.\n", (loopbackProcessExclude) ? "exclude" : "include", loopbackProcessID); - } - } - - return result; -} - - -static ma_result ma_context_enumerate_devices__wasapi(ma_context* pContext, ma_enum_devices_callback_proc callback, void* pUserData) -{ - /* Different enumeration for desktop and UWP. */ -#if defined(MA_WIN32_DESKTOP) || defined(MA_WIN32_GDK) - /* Desktop */ - HRESULT hr; - ma_IMMDeviceEnumerator* pDeviceEnumerator; - - hr = ma_CoCreateInstance(pContext, &MA_CLSID_MMDeviceEnumerator, NULL, CLSCTX_ALL, &MA_IID_IMMDeviceEnumerator, (void**)&pDeviceEnumerator); - if (FAILED(hr)) { - ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to create device enumerator."); - return ma_result_from_HRESULT(hr); - } - - ma_context_enumerate_devices_by_type__wasapi(pContext, pDeviceEnumerator, ma_device_type_playback, callback, pUserData); - ma_context_enumerate_devices_by_type__wasapi(pContext, pDeviceEnumerator, ma_device_type_capture, callback, pUserData); - - ma_IMMDeviceEnumerator_Release(pDeviceEnumerator); -#else - /* - UWP - - The MMDevice API is only supported on desktop applications. For now, while I'm still figuring out how to properly enumerate - over devices without using MMDevice, I'm restricting devices to defaults. - - Hint: DeviceInformation::FindAllAsync() with DeviceClass.AudioCapture/AudioRender. https://blogs.windows.com/buildingapps/2014/05/15/real-time-audio-in-windows-store-and-windows-phone-apps/ - */ - if (callback) { - ma_bool32 cbResult = MA_TRUE; - - /* Playback. */ - if (cbResult) { - ma_device_info deviceInfo; - MA_ZERO_OBJECT(&deviceInfo); - ma_strncpy_s(deviceInfo.name, sizeof(deviceInfo.name), MA_DEFAULT_PLAYBACK_DEVICE_NAME, (size_t)-1); - deviceInfo.isDefault = MA_TRUE; - cbResult = callback(pContext, ma_device_type_playback, &deviceInfo, pUserData); - } - - /* Capture. */ - if (cbResult) { - ma_device_info deviceInfo; - MA_ZERO_OBJECT(&deviceInfo); - ma_strncpy_s(deviceInfo.name, sizeof(deviceInfo.name), MA_DEFAULT_CAPTURE_DEVICE_NAME, (size_t)-1); - deviceInfo.isDefault = MA_TRUE; - cbResult = callback(pContext, ma_device_type_capture, &deviceInfo, pUserData); - } - } -#endif - - return MA_SUCCESS; -} - -static ma_result ma_context_get_device_info__wasapi(ma_context* pContext, ma_device_type deviceType, const ma_device_id* pDeviceID, ma_device_info* pDeviceInfo) -{ -#if defined(MA_WIN32_DESKTOP) || defined(MA_WIN32_GDK) - ma_result result; - ma_IMMDevice* pMMDevice = NULL; - WCHAR* pDefaultDeviceID = NULL; - - result = ma_context_get_MMDevice__wasapi(pContext, deviceType, pDeviceID, &pMMDevice); - if (result != MA_SUCCESS) { - return result; - } - - /* We need the default device ID so we can set the isDefault flag in the device info. */ - pDefaultDeviceID = ma_context_get_default_device_id__wasapi(pContext, deviceType); - - result = ma_context_get_device_info_from_MMDevice__wasapi(pContext, pMMDevice, pDefaultDeviceID, MA_FALSE, pDeviceInfo); /* MA_FALSE = !onlySimpleInfo. */ - - if (pDefaultDeviceID != NULL) { - ma_CoTaskMemFree(pContext, pDefaultDeviceID); - pDefaultDeviceID = NULL; - } - - ma_IMMDevice_Release(pMMDevice); - - return result; -#else - ma_IAudioClient* pAudioClient; - ma_result result; - - /* UWP currently only uses default devices. */ - if (deviceType == ma_device_type_playback) { - ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), MA_DEFAULT_PLAYBACK_DEVICE_NAME, (size_t)-1); - } else { - ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), MA_DEFAULT_CAPTURE_DEVICE_NAME, (size_t)-1); - } - - result = ma_context_get_IAudioClient_UWP__wasapi(pContext, deviceType, pDeviceID, NULL, &pAudioClient, NULL); - if (result != MA_SUCCESS) { - return result; - } - - result = ma_context_get_device_info_from_IAudioClient__wasapi(pContext, NULL, pAudioClient, pDeviceInfo); - - pDeviceInfo->isDefault = MA_TRUE; /* UWP only supports default devices. */ - - ma_IAudioClient_Release(pAudioClient); - return result; -#endif -} - -static ma_result ma_device_uninit__wasapi(ma_device* pDevice) -{ - MA_ASSERT(pDevice != NULL); - - #if defined(MA_WIN32_DESKTOP) || defined(MA_WIN32_GDK) - { - if (pDevice->wasapi.pDeviceEnumerator) { - ((ma_IMMDeviceEnumerator*)pDevice->wasapi.pDeviceEnumerator)->lpVtbl->UnregisterEndpointNotificationCallback((ma_IMMDeviceEnumerator*)pDevice->wasapi.pDeviceEnumerator, &pDevice->wasapi.notificationClient); - ma_IMMDeviceEnumerator_Release((ma_IMMDeviceEnumerator*)pDevice->wasapi.pDeviceEnumerator); - } - - ma_mutex_uninit(&pDevice->wasapi.rerouteLock); - } - #endif - - if (pDevice->wasapi.pRenderClient) { - if (pDevice->wasapi.pMappedBufferPlayback != NULL) { - ma_IAudioRenderClient_ReleaseBuffer((ma_IAudioRenderClient*)pDevice->wasapi.pRenderClient, pDevice->wasapi.mappedBufferPlaybackCap, 0); - pDevice->wasapi.pMappedBufferPlayback = NULL; - pDevice->wasapi.mappedBufferPlaybackCap = 0; - pDevice->wasapi.mappedBufferPlaybackLen = 0; - } - - ma_IAudioRenderClient_Release((ma_IAudioRenderClient*)pDevice->wasapi.pRenderClient); - } - if (pDevice->wasapi.pCaptureClient) { - if (pDevice->wasapi.pMappedBufferCapture != NULL) { - ma_IAudioCaptureClient_ReleaseBuffer((ma_IAudioCaptureClient*)pDevice->wasapi.pCaptureClient, pDevice->wasapi.mappedBufferCaptureCap); - pDevice->wasapi.pMappedBufferCapture = NULL; - pDevice->wasapi.mappedBufferCaptureCap = 0; - pDevice->wasapi.mappedBufferCaptureLen = 0; - } - - ma_IAudioCaptureClient_Release((ma_IAudioCaptureClient*)pDevice->wasapi.pCaptureClient); - } - - if (pDevice->wasapi.pAudioClientPlayback) { - ma_IAudioClient_Release((ma_IAudioClient*)pDevice->wasapi.pAudioClientPlayback); - } - if (pDevice->wasapi.pAudioClientCapture) { - ma_IAudioClient_Release((ma_IAudioClient*)pDevice->wasapi.pAudioClientCapture); - } - - if (pDevice->wasapi.hEventPlayback) { - CloseHandle((HANDLE)pDevice->wasapi.hEventPlayback); - } - if (pDevice->wasapi.hEventCapture) { - CloseHandle((HANDLE)pDevice->wasapi.hEventCapture); - } - - return MA_SUCCESS; -} - - -typedef struct -{ - /* Input. */ - ma_format formatIn; - ma_uint32 channelsIn; - ma_uint32 sampleRateIn; - ma_channel channelMapIn[MA_MAX_CHANNELS]; - ma_uint32 periodSizeInFramesIn; - ma_uint32 periodSizeInMillisecondsIn; - ma_uint32 periodsIn; - ma_share_mode shareMode; - ma_performance_profile performanceProfile; - ma_bool32 noAutoConvertSRC; - ma_bool32 noDefaultQualitySRC; - ma_bool32 noHardwareOffloading; - ma_uint32 loopbackProcessID; - ma_bool32 loopbackProcessExclude; - - /* Output. */ - ma_IAudioClient* pAudioClient; - ma_IAudioRenderClient* pRenderClient; - ma_IAudioCaptureClient* pCaptureClient; - ma_format formatOut; - ma_uint32 channelsOut; - ma_uint32 sampleRateOut; - ma_channel channelMapOut[MA_MAX_CHANNELS]; - ma_uint32 periodSizeInFramesOut; - ma_uint32 periodsOut; - ma_bool32 usingAudioClient3; - char deviceName[256]; - ma_device_id id; -} ma_device_init_internal_data__wasapi; - -static ma_result ma_device_init_internal__wasapi(ma_context* pContext, ma_device_type deviceType, const ma_device_id* pDeviceID, ma_device_init_internal_data__wasapi* pData) -{ - HRESULT hr; - ma_result result = MA_SUCCESS; - const char* errorMsg = ""; - MA_AUDCLNT_SHAREMODE shareMode = MA_AUDCLNT_SHAREMODE_SHARED; - DWORD streamFlags = 0; - MA_REFERENCE_TIME periodDurationInMicroseconds; - ma_bool32 wasInitializedUsingIAudioClient3 = MA_FALSE; - MA_WAVEFORMATEXTENSIBLE wf; - ma_WASAPIDeviceInterface* pDeviceInterface = NULL; - ma_IAudioClient2* pAudioClient2; - ma_uint32 nativeSampleRate; - ma_bool32 usingProcessLoopback = MA_FALSE; - - MA_ASSERT(pContext != NULL); - MA_ASSERT(pData != NULL); - - /* This function is only used to initialize one device type: either playback, capture or loopback. Never full-duplex. */ - if (deviceType == ma_device_type_duplex) { - return MA_INVALID_ARGS; - } - - usingProcessLoopback = deviceType == ma_device_type_loopback && pData->loopbackProcessID != 0 && pDeviceID == NULL; - - pData->pAudioClient = NULL; - pData->pRenderClient = NULL; - pData->pCaptureClient = NULL; - - streamFlags = MA_AUDCLNT_STREAMFLAGS_EVENTCALLBACK; - if (!pData->noAutoConvertSRC && pData->sampleRateIn != 0 && pData->shareMode != ma_share_mode_exclusive) { /* <-- Exclusive streams must use the native sample rate. */ - streamFlags |= MA_AUDCLNT_STREAMFLAGS_AUTOCONVERTPCM; - } - if (!pData->noDefaultQualitySRC && pData->sampleRateIn != 0 && (streamFlags & MA_AUDCLNT_STREAMFLAGS_AUTOCONVERTPCM) != 0) { - streamFlags |= MA_AUDCLNT_STREAMFLAGS_SRC_DEFAULT_QUALITY; - } - if (deviceType == ma_device_type_loopback) { - streamFlags |= MA_AUDCLNT_STREAMFLAGS_LOOPBACK; - } - - result = ma_context_get_IAudioClient__wasapi(pContext, deviceType, pDeviceID, pData->loopbackProcessID, pData->loopbackProcessExclude, &pData->pAudioClient, &pDeviceInterface); - if (result != MA_SUCCESS) { - goto done; - } - - MA_ZERO_OBJECT(&wf); - - /* Try enabling hardware offloading. */ - if (!pData->noHardwareOffloading) { - hr = ma_IAudioClient_QueryInterface(pData->pAudioClient, &MA_IID_IAudioClient2, (void**)&pAudioClient2); - if (SUCCEEDED(hr)) { - BOOL isHardwareOffloadingSupported = 0; - hr = ma_IAudioClient2_IsOffloadCapable(pAudioClient2, MA_AudioCategory_Other, &isHardwareOffloadingSupported); - if (SUCCEEDED(hr) && isHardwareOffloadingSupported) { - ma_AudioClientProperties clientProperties; - MA_ZERO_OBJECT(&clientProperties); - clientProperties.cbSize = sizeof(clientProperties); - clientProperties.bIsOffload = 1; - clientProperties.eCategory = MA_AudioCategory_Other; - ma_IAudioClient2_SetClientProperties(pAudioClient2, &clientProperties); - } - - pAudioClient2->lpVtbl->Release(pAudioClient2); - } - } - - /* Here is where we try to determine the best format to use with the device. If the client if wanting exclusive mode, first try finding the best format for that. If this fails, fall back to shared mode. */ - result = MA_FORMAT_NOT_SUPPORTED; - if (pData->shareMode == ma_share_mode_exclusive) { - #if defined(MA_WIN32_DESKTOP) || defined(MA_WIN32_GDK) - /* In exclusive mode on desktop we always use the backend's native format. */ - ma_IPropertyStore* pStore = NULL; - hr = ma_IMMDevice_OpenPropertyStore(pDeviceInterface, STGM_READ, &pStore); - if (SUCCEEDED(hr)) { - MA_PROPVARIANT prop; - ma_PropVariantInit(&prop); - hr = ma_IPropertyStore_GetValue(pStore, &MA_PKEY_AudioEngine_DeviceFormat, &prop); - if (SUCCEEDED(hr)) { - MA_WAVEFORMATEX* pActualFormat = (MA_WAVEFORMATEX*)prop.blob.pBlobData; - hr = ma_IAudioClient_IsFormatSupported((ma_IAudioClient*)pData->pAudioClient, MA_AUDCLNT_SHAREMODE_EXCLUSIVE, pActualFormat, NULL); - if (SUCCEEDED(hr)) { - MA_COPY_MEMORY(&wf, pActualFormat, sizeof(MA_WAVEFORMATEXTENSIBLE)); - } - - ma_PropVariantClear(pContext, &prop); - } - - ma_IPropertyStore_Release(pStore); - } - #else - /* - I do not know how to query the device's native format on UWP so for now I'm just disabling support for - exclusive mode. The alternative is to enumerate over different formats and check IsFormatSupported() - until you find one that works. - - TODO: Add support for exclusive mode to UWP. - */ - hr = S_FALSE; - #endif - - if (hr == S_OK) { - shareMode = MA_AUDCLNT_SHAREMODE_EXCLUSIVE; - result = MA_SUCCESS; - } else { - result = MA_SHARE_MODE_NOT_SUPPORTED; - } - } else { - /* In shared mode we are always using the format reported by the operating system. */ - MA_WAVEFORMATEXTENSIBLE* pNativeFormat = NULL; - hr = ma_IAudioClient_GetMixFormat((ma_IAudioClient*)pData->pAudioClient, (MA_WAVEFORMATEX**)&pNativeFormat); - if (hr != S_OK) { - /* When using process-specific loopback, GetMixFormat() seems to always fail. */ - if (usingProcessLoopback) { - wf.wFormatTag = WAVE_FORMAT_IEEE_FLOAT; - wf.nChannels = 2; - wf.nSamplesPerSec = 44100; - wf.wBitsPerSample = 32; - wf.nBlockAlign = wf.nChannels * wf.wBitsPerSample / 8; - wf.nAvgBytesPerSec = wf.nSamplesPerSec * wf.nBlockAlign; - wf.cbSize = sizeof(MA_WAVEFORMATEX); - - result = MA_SUCCESS; - } else { - result = MA_FORMAT_NOT_SUPPORTED; - } - } else { - /* - I've seen cases where cbSize will be set to sizeof(WAVEFORMATEX) even though the structure itself - is given the format tag of WAVE_FORMAT_EXTENSIBLE. If the format tag is WAVE_FORMAT_EXTENSIBLE - want to make sure we copy the whole WAVEFORMATEXTENSIBLE structure. Otherwise we'll have to be - safe and only copy the WAVEFORMATEX part. - */ - if (pNativeFormat->wFormatTag == WAVE_FORMAT_EXTENSIBLE) { - MA_COPY_MEMORY(&wf, pNativeFormat, sizeof(MA_WAVEFORMATEXTENSIBLE)); - } else { - /* I've seen a case where cbSize was set to 0. Assume sizeof(WAVEFORMATEX) in this case. */ - size_t cbSize = pNativeFormat->cbSize; - if (cbSize == 0) { - cbSize = sizeof(MA_WAVEFORMATEX); - } - - /* Make sure we don't copy more than the capacity of `wf`. */ - if (cbSize > sizeof(wf)) { - cbSize = sizeof(wf); - } - - MA_COPY_MEMORY(&wf, pNativeFormat, cbSize); - } - - result = MA_SUCCESS; - } - - ma_CoTaskMemFree(pContext, pNativeFormat); - - shareMode = MA_AUDCLNT_SHAREMODE_SHARED; - } - - /* Return an error if we still haven't found a format. */ - if (result != MA_SUCCESS) { - errorMsg = "[WASAPI] Failed to find best device mix format."; - goto done; - } - - /* - Override the native sample rate with the one requested by the caller, but only if we're not using the default sample rate. We'll use - WASAPI to perform the sample rate conversion. - */ - nativeSampleRate = wf.nSamplesPerSec; - if (streamFlags & MA_AUDCLNT_STREAMFLAGS_AUTOCONVERTPCM) { - wf.nSamplesPerSec = (pData->sampleRateIn != 0) ? pData->sampleRateIn : MA_DEFAULT_SAMPLE_RATE; - wf.nAvgBytesPerSec = wf.nSamplesPerSec * wf.nBlockAlign; - } - - pData->formatOut = ma_format_from_WAVEFORMATEX((MA_WAVEFORMATEX*)&wf); - if (pData->formatOut == ma_format_unknown) { - /* - The format isn't supported. This is almost certainly because the exclusive mode format isn't supported by miniaudio. We need to return MA_SHARE_MODE_NOT_SUPPORTED - in this case so that the caller can detect it and fall back to shared mode if desired. We should never get here if shared mode was requested, but just for - completeness we'll check for it and return MA_FORMAT_NOT_SUPPORTED. - */ - if (shareMode == MA_AUDCLNT_SHAREMODE_EXCLUSIVE) { - result = MA_SHARE_MODE_NOT_SUPPORTED; - } else { - result = MA_FORMAT_NOT_SUPPORTED; - } - - errorMsg = "[WASAPI] Native format not supported."; - goto done; - } - - pData->channelsOut = wf.nChannels; - pData->sampleRateOut = wf.nSamplesPerSec; - - /* - Get the internal channel map based on the channel mask. There is a possibility that GetMixFormat() returns - a WAVEFORMATEX instead of a WAVEFORMATEXTENSIBLE, in which case the channel mask will be undefined. In this - case we'll just use the default channel map. - */ - if (wf.wFormatTag == WAVE_FORMAT_EXTENSIBLE || wf.cbSize >= sizeof(MA_WAVEFORMATEXTENSIBLE)) { - ma_channel_mask_to_channel_map__win32(wf.dwChannelMask, pData->channelsOut, pData->channelMapOut); - } else { - ma_channel_map_init_standard(ma_standard_channel_map_microsoft, pData->channelMapOut, ma_countof(pData->channelMapOut), pData->channelsOut); - } - - /* Period size. */ - pData->periodsOut = (pData->periodsIn != 0) ? pData->periodsIn : MA_DEFAULT_PERIODS; - pData->periodSizeInFramesOut = pData->periodSizeInFramesIn; - if (pData->periodSizeInFramesOut == 0) { - if (pData->periodSizeInMillisecondsIn == 0) { - if (pData->performanceProfile == ma_performance_profile_low_latency) { - pData->periodSizeInFramesOut = ma_calculate_buffer_size_in_frames_from_milliseconds(MA_DEFAULT_PERIOD_SIZE_IN_MILLISECONDS_LOW_LATENCY, wf.nSamplesPerSec); - } else { - pData->periodSizeInFramesOut = ma_calculate_buffer_size_in_frames_from_milliseconds(MA_DEFAULT_PERIOD_SIZE_IN_MILLISECONDS_CONSERVATIVE, wf.nSamplesPerSec); - } - } else { - pData->periodSizeInFramesOut = ma_calculate_buffer_size_in_frames_from_milliseconds(pData->periodSizeInMillisecondsIn, wf.nSamplesPerSec); - } - } - - periodDurationInMicroseconds = ((ma_uint64)pData->periodSizeInFramesOut * 1000 * 1000) / wf.nSamplesPerSec; - - - /* Slightly different initialization for shared and exclusive modes. We try exclusive mode first, and if it fails, fall back to shared mode. */ - if (shareMode == MA_AUDCLNT_SHAREMODE_EXCLUSIVE) { - MA_REFERENCE_TIME bufferDuration = periodDurationInMicroseconds * pData->periodsOut * 10; - - /* - If the periodicity is too small, Initialize() will fail with AUDCLNT_E_INVALID_DEVICE_PERIOD. In this case we should just keep increasing - it and trying it again. - */ - hr = E_FAIL; - for (;;) { - hr = ma_IAudioClient_Initialize((ma_IAudioClient*)pData->pAudioClient, shareMode, streamFlags, bufferDuration, bufferDuration, (MA_WAVEFORMATEX*)&wf, NULL); - if (hr == MA_AUDCLNT_E_INVALID_DEVICE_PERIOD) { - if (bufferDuration > 500*10000) { - break; - } else { - if (bufferDuration == 0) { /* <-- Just a sanity check to prevent an infinite loop. Should never happen, but it makes me feel better. */ - break; - } - - bufferDuration = bufferDuration * 2; - continue; - } - } else { - break; - } - } - - if (hr == MA_AUDCLNT_E_BUFFER_SIZE_NOT_ALIGNED) { - ma_uint32 bufferSizeInFrames; - hr = ma_IAudioClient_GetBufferSize((ma_IAudioClient*)pData->pAudioClient, &bufferSizeInFrames); - if (SUCCEEDED(hr)) { - bufferDuration = (MA_REFERENCE_TIME)((10000.0 * 1000 / wf.nSamplesPerSec * bufferSizeInFrames) + 0.5); - - /* Unfortunately we need to release and re-acquire the audio client according to MSDN. Seems silly - why not just call IAudioClient_Initialize() again?! */ - ma_IAudioClient_Release((ma_IAudioClient*)pData->pAudioClient); - - #if defined(MA_WIN32_DESKTOP) || defined(MA_WIN32_GDK) - hr = ma_IMMDevice_Activate(pDeviceInterface, &MA_IID_IAudioClient, CLSCTX_ALL, NULL, (void**)&pData->pAudioClient); - #else - hr = ma_IUnknown_QueryInterface(pDeviceInterface, &MA_IID_IAudioClient, (void**)&pData->pAudioClient); - #endif - - if (SUCCEEDED(hr)) { - hr = ma_IAudioClient_Initialize((ma_IAudioClient*)pData->pAudioClient, shareMode, streamFlags, bufferDuration, bufferDuration, (MA_WAVEFORMATEX*)&wf, NULL); - } - } - } - - if (FAILED(hr)) { - /* Failed to initialize in exclusive mode. Don't fall back to shared mode - instead tell the client about it. They can reinitialize in shared mode if they want. */ - if (hr == E_ACCESSDENIED) { - errorMsg = "[WASAPI] Failed to initialize device in exclusive mode. Access denied.", result = MA_ACCESS_DENIED; - } else if (hr == MA_AUDCLNT_E_DEVICE_IN_USE) { - errorMsg = "[WASAPI] Failed to initialize device in exclusive mode. Device in use.", result = MA_BUSY; - } else { - errorMsg = "[WASAPI] Failed to initialize device in exclusive mode."; result = ma_result_from_HRESULT(hr); - } - goto done; - } - } - - if (shareMode == MA_AUDCLNT_SHAREMODE_SHARED) { - /* - Low latency shared mode via IAudioClient3. - - NOTE - ==== - Contrary to the documentation on MSDN (https://docs.microsoft.com/en-us/windows/win32/api/audioclient/nf-audioclient-iaudioclient3-initializesharedaudiostream), the - use of AUDCLNT_STREAMFLAGS_AUTOCONVERTPCM and AUDCLNT_STREAMFLAGS_SRC_DEFAULT_QUALITY with IAudioClient3_InitializeSharedAudioStream() absolutely does not work. Using - any of these flags will result in HRESULT code 0x88890021. The other problem is that calling IAudioClient3_GetSharedModeEnginePeriod() with a sample rate different to - that returned by IAudioClient_GetMixFormat() also results in an error. I'm therefore disabling low-latency shared mode with AUDCLNT_STREAMFLAGS_AUTOCONVERTPCM. - */ - #ifndef MA_WASAPI_NO_LOW_LATENCY_SHARED_MODE - { - if ((streamFlags & MA_AUDCLNT_STREAMFLAGS_AUTOCONVERTPCM) == 0 || nativeSampleRate == wf.nSamplesPerSec) { - ma_IAudioClient3* pAudioClient3 = NULL; - hr = ma_IAudioClient_QueryInterface(pData->pAudioClient, &MA_IID_IAudioClient3, (void**)&pAudioClient3); - if (SUCCEEDED(hr)) { - ma_uint32 defaultPeriodInFrames; - ma_uint32 fundamentalPeriodInFrames; - ma_uint32 minPeriodInFrames; - ma_uint32 maxPeriodInFrames; - hr = ma_IAudioClient3_GetSharedModeEnginePeriod(pAudioClient3, (MA_WAVEFORMATEX*)&wf, &defaultPeriodInFrames, &fundamentalPeriodInFrames, &minPeriodInFrames, &maxPeriodInFrames); - if (SUCCEEDED(hr)) { - ma_uint32 desiredPeriodInFrames = pData->periodSizeInFramesOut; - ma_uint32 actualPeriodInFrames = desiredPeriodInFrames; - - /* Make sure the period size is a multiple of fundamentalPeriodInFrames. */ - actualPeriodInFrames = actualPeriodInFrames / fundamentalPeriodInFrames; - actualPeriodInFrames = actualPeriodInFrames * fundamentalPeriodInFrames; - - /* The period needs to be clamped between minPeriodInFrames and maxPeriodInFrames. */ - actualPeriodInFrames = ma_clamp(actualPeriodInFrames, minPeriodInFrames, maxPeriodInFrames); - - ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_DEBUG, "[WASAPI] Trying IAudioClient3_InitializeSharedAudioStream(actualPeriodInFrames=%d)\n", actualPeriodInFrames); - ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_DEBUG, " defaultPeriodInFrames=%d\n", defaultPeriodInFrames); - ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_DEBUG, " fundamentalPeriodInFrames=%d\n", fundamentalPeriodInFrames); - ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_DEBUG, " minPeriodInFrames=%d\n", minPeriodInFrames); - ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_DEBUG, " maxPeriodInFrames=%d\n", maxPeriodInFrames); - - /* If the client requested a largish buffer than we don't actually want to use low latency shared mode because it forces small buffers. */ - if (actualPeriodInFrames >= desiredPeriodInFrames) { - /* - MA_AUDCLNT_STREAMFLAGS_AUTOCONVERTPCM | MA_AUDCLNT_STREAMFLAGS_SRC_DEFAULT_QUALITY must not be in the stream flags. If either of these are specified, - IAudioClient3_InitializeSharedAudioStream() will fail. - */ - hr = ma_IAudioClient3_InitializeSharedAudioStream(pAudioClient3, streamFlags & ~(MA_AUDCLNT_STREAMFLAGS_AUTOCONVERTPCM | MA_AUDCLNT_STREAMFLAGS_SRC_DEFAULT_QUALITY), actualPeriodInFrames, (MA_WAVEFORMATEX*)&wf, NULL); - if (SUCCEEDED(hr)) { - wasInitializedUsingIAudioClient3 = MA_TRUE; - pData->periodSizeInFramesOut = actualPeriodInFrames; - - ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_DEBUG, "[WASAPI] Using IAudioClient3\n"); - ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_DEBUG, " periodSizeInFramesOut=%d\n", pData->periodSizeInFramesOut); - } else { - ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_DEBUG, "[WASAPI] IAudioClient3_InitializeSharedAudioStream failed. Falling back to IAudioClient.\n"); - } - } else { - ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_DEBUG, "[WASAPI] Not using IAudioClient3 because the desired period size is larger than the maximum supported by IAudioClient3.\n"); - } - } else { - ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_DEBUG, "[WASAPI] IAudioClient3_GetSharedModeEnginePeriod failed. Falling back to IAudioClient.\n"); - } - - ma_IAudioClient3_Release(pAudioClient3); - pAudioClient3 = NULL; - } - } - } - #else - { - ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_DEBUG, "[WASAPI] Not using IAudioClient3 because MA_WASAPI_NO_LOW_LATENCY_SHARED_MODE is enabled.\n"); - } - #endif - - /* If we don't have an IAudioClient3 then we need to use the normal initialization routine. */ - if (!wasInitializedUsingIAudioClient3) { - MA_REFERENCE_TIME bufferDuration = periodDurationInMicroseconds * pData->periodsOut * 10; /* <-- Multiply by 10 for microseconds to 100-nanoseconds. */ - hr = ma_IAudioClient_Initialize((ma_IAudioClient*)pData->pAudioClient, shareMode, streamFlags, bufferDuration, 0, (const MA_WAVEFORMATEX*)&wf, NULL); - if (FAILED(hr)) { - if (hr == E_ACCESSDENIED) { - errorMsg = "[WASAPI] Failed to initialize device. Access denied.", result = MA_ACCESS_DENIED; - } else if (hr == MA_AUDCLNT_E_DEVICE_IN_USE) { - errorMsg = "[WASAPI] Failed to initialize device. Device in use.", result = MA_BUSY; - } else { - errorMsg = "[WASAPI] Failed to initialize device.", result = ma_result_from_HRESULT(hr); - } - - goto done; - } - } - } - - if (!wasInitializedUsingIAudioClient3) { - ma_uint32 bufferSizeInFrames = 0; - hr = ma_IAudioClient_GetBufferSize((ma_IAudioClient*)pData->pAudioClient, &bufferSizeInFrames); - if (FAILED(hr)) { - errorMsg = "[WASAPI] Failed to get audio client's actual buffer size.", result = ma_result_from_HRESULT(hr); - goto done; - } - - /* - When using process loopback mode, retrieval of the buffer size seems to result in totally - incorrect values. In this case we'll just assume it's the same size as what we requested - when we initialized the client. - */ - if (usingProcessLoopback) { - bufferSizeInFrames = (ma_uint32)((periodDurationInMicroseconds * pData->periodsOut) * pData->sampleRateOut / 1000000); - } - - pData->periodSizeInFramesOut = bufferSizeInFrames / pData->periodsOut; - } - - pData->usingAudioClient3 = wasInitializedUsingIAudioClient3; - - - if (deviceType == ma_device_type_playback) { - result = ma_device_create_IAudioClient_service__wasapi(pContext, deviceType, (ma_IAudioClient*)pData->pAudioClient, (void**)&pData->pRenderClient); - } else { - result = ma_device_create_IAudioClient_service__wasapi(pContext, deviceType, (ma_IAudioClient*)pData->pAudioClient, (void**)&pData->pCaptureClient); - } - - /*if (FAILED(hr)) {*/ - if (result != MA_SUCCESS) { - errorMsg = "[WASAPI] Failed to get audio client service."; - goto done; - } - - - /* Grab the name of the device. */ - #if defined(MA_WIN32_DESKTOP) || defined(MA_WIN32_GDK) - { - ma_IPropertyStore *pProperties; - hr = ma_IMMDevice_OpenPropertyStore(pDeviceInterface, STGM_READ, &pProperties); - if (SUCCEEDED(hr)) { - MA_PROPVARIANT varName; - ma_PropVariantInit(&varName); - hr = ma_IPropertyStore_GetValue(pProperties, &MA_PKEY_Device_FriendlyName, &varName); - if (SUCCEEDED(hr)) { - WideCharToMultiByte(CP_UTF8, 0, varName.pwszVal, -1, pData->deviceName, sizeof(pData->deviceName), 0, FALSE); - ma_PropVariantClear(pContext, &varName); - } - - ma_IPropertyStore_Release(pProperties); - } - } - #endif - - /* - For the WASAPI backend we need to know the actual IDs of the device in order to do automatic - stream routing so that IDs can be compared and we can determine which device has been detached - and whether or not it matches with our ma_device. - */ - #if defined(MA_WIN32_DESKTOP) || defined(MA_WIN32_GDK) - { - /* Desktop */ - ma_context_get_device_id_from_MMDevice__wasapi(pContext, pDeviceInterface, &pData->id); - } - #else - { - /* UWP */ - /* TODO: Implement me. Need to figure out how to get the ID of the default device. */ - } - #endif - -done: - /* Clean up. */ -#if defined(MA_WIN32_DESKTOP) || defined(MA_WIN32_GDK) - if (pDeviceInterface != NULL) { - ma_IMMDevice_Release(pDeviceInterface); - } -#else - if (pDeviceInterface != NULL) { - ma_IUnknown_Release(pDeviceInterface); - } -#endif - - if (result != MA_SUCCESS) { - if (pData->pRenderClient) { - ma_IAudioRenderClient_Release((ma_IAudioRenderClient*)pData->pRenderClient); - pData->pRenderClient = NULL; - } - if (pData->pCaptureClient) { - ma_IAudioCaptureClient_Release((ma_IAudioCaptureClient*)pData->pCaptureClient); - pData->pCaptureClient = NULL; - } - if (pData->pAudioClient) { - ma_IAudioClient_Release((ma_IAudioClient*)pData->pAudioClient); - pData->pAudioClient = NULL; - } - - if (errorMsg != NULL && errorMsg[0] != '\0') { - ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "%s\n", errorMsg); - } - - return result; - } else { - return MA_SUCCESS; - } -} - -static ma_result ma_device_reinit__wasapi(ma_device* pDevice, ma_device_type deviceType) -{ - ma_device_init_internal_data__wasapi data; - ma_result result; - - MA_ASSERT(pDevice != NULL); - - /* We only re-initialize the playback or capture device. Never a full-duplex device. */ - if (deviceType == ma_device_type_duplex) { - return MA_INVALID_ARGS; - } - - - /* - Before reinitializing the device we need to free the previous audio clients. - - There's a known memory leak here. We will be calling this from the routing change callback that - is fired by WASAPI. If we attempt to release the IAudioClient we will deadlock. In my opinion - this is a bug. I'm not sure what I need to do to handle this cleanly, but I think we'll probably - need some system where we post an event, but delay the execution of it until the callback has - returned. I'm not sure how to do this reliably, however. I have set up some infrastructure for - a command thread which might be useful for this. - */ - if (deviceType == ma_device_type_capture || deviceType == ma_device_type_loopback) { - if (pDevice->wasapi.pCaptureClient) { - ma_IAudioCaptureClient_Release((ma_IAudioCaptureClient*)pDevice->wasapi.pCaptureClient); - pDevice->wasapi.pCaptureClient = NULL; - } - - if (pDevice->wasapi.pAudioClientCapture) { - /*ma_device_release_IAudioClient_service__wasapi(pDevice, ma_device_type_capture);*/ - pDevice->wasapi.pAudioClientCapture = NULL; - } - } - - if (deviceType == ma_device_type_playback) { - if (pDevice->wasapi.pRenderClient) { - ma_IAudioRenderClient_Release((ma_IAudioRenderClient*)pDevice->wasapi.pRenderClient); - pDevice->wasapi.pRenderClient = NULL; - } - - if (pDevice->wasapi.pAudioClientPlayback) { - /*ma_device_release_IAudioClient_service__wasapi(pDevice, ma_device_type_playback);*/ - pDevice->wasapi.pAudioClientPlayback = NULL; - } - } - - - if (deviceType == ma_device_type_playback) { - data.formatIn = pDevice->playback.format; - data.channelsIn = pDevice->playback.channels; - MA_COPY_MEMORY(data.channelMapIn, pDevice->playback.channelMap, sizeof(pDevice->playback.channelMap)); - data.shareMode = pDevice->playback.shareMode; - } else { - data.formatIn = pDevice->capture.format; - data.channelsIn = pDevice->capture.channels; - MA_COPY_MEMORY(data.channelMapIn, pDevice->capture.channelMap, sizeof(pDevice->capture.channelMap)); - data.shareMode = pDevice->capture.shareMode; - } - - data.sampleRateIn = pDevice->sampleRate; - data.periodSizeInFramesIn = pDevice->wasapi.originalPeriodSizeInFrames; - data.periodSizeInMillisecondsIn = pDevice->wasapi.originalPeriodSizeInMilliseconds; - data.periodsIn = pDevice->wasapi.originalPeriods; - data.performanceProfile = pDevice->wasapi.originalPerformanceProfile; - data.noAutoConvertSRC = pDevice->wasapi.noAutoConvertSRC; - data.noDefaultQualitySRC = pDevice->wasapi.noDefaultQualitySRC; - data.noHardwareOffloading = pDevice->wasapi.noHardwareOffloading; - data.loopbackProcessID = pDevice->wasapi.loopbackProcessID; - data.loopbackProcessExclude = pDevice->wasapi.loopbackProcessExclude; - result = ma_device_init_internal__wasapi(pDevice->pContext, deviceType, NULL, &data); - if (result != MA_SUCCESS) { - return result; - } - - /* At this point we have some new objects ready to go. We need to uninitialize the previous ones and then set the new ones. */ - if (deviceType == ma_device_type_capture || deviceType == ma_device_type_loopback) { - pDevice->wasapi.pAudioClientCapture = data.pAudioClient; - pDevice->wasapi.pCaptureClient = data.pCaptureClient; - - pDevice->capture.internalFormat = data.formatOut; - pDevice->capture.internalChannels = data.channelsOut; - pDevice->capture.internalSampleRate = data.sampleRateOut; - MA_COPY_MEMORY(pDevice->capture.internalChannelMap, data.channelMapOut, sizeof(data.channelMapOut)); - pDevice->capture.internalPeriodSizeInFrames = data.periodSizeInFramesOut; - pDevice->capture.internalPeriods = data.periodsOut; - ma_strcpy_s(pDevice->capture.name, sizeof(pDevice->capture.name), data.deviceName); - - ma_IAudioClient_SetEventHandle((ma_IAudioClient*)pDevice->wasapi.pAudioClientCapture, (HANDLE)pDevice->wasapi.hEventCapture); - - pDevice->wasapi.periodSizeInFramesCapture = data.periodSizeInFramesOut; - ma_IAudioClient_GetBufferSize((ma_IAudioClient*)pDevice->wasapi.pAudioClientCapture, &pDevice->wasapi.actualBufferSizeInFramesCapture); - - /* We must always have a valid ID. */ - ma_strcpy_s_WCHAR(pDevice->capture.id.wasapi, sizeof(pDevice->capture.id.wasapi), data.id.wasapi); - } - - if (deviceType == ma_device_type_playback) { - pDevice->wasapi.pAudioClientPlayback = data.pAudioClient; - pDevice->wasapi.pRenderClient = data.pRenderClient; - - pDevice->playback.internalFormat = data.formatOut; - pDevice->playback.internalChannels = data.channelsOut; - pDevice->playback.internalSampleRate = data.sampleRateOut; - MA_COPY_MEMORY(pDevice->playback.internalChannelMap, data.channelMapOut, sizeof(data.channelMapOut)); - pDevice->playback.internalPeriodSizeInFrames = data.periodSizeInFramesOut; - pDevice->playback.internalPeriods = data.periodsOut; - ma_strcpy_s(pDevice->playback.name, sizeof(pDevice->playback.name), data.deviceName); - - ma_IAudioClient_SetEventHandle((ma_IAudioClient*)pDevice->wasapi.pAudioClientPlayback, (HANDLE)pDevice->wasapi.hEventPlayback); - - pDevice->wasapi.periodSizeInFramesPlayback = data.periodSizeInFramesOut; - ma_IAudioClient_GetBufferSize((ma_IAudioClient*)pDevice->wasapi.pAudioClientPlayback, &pDevice->wasapi.actualBufferSizeInFramesPlayback); - - /* We must always have a valid ID because rerouting will look at it. */ - ma_strcpy_s_WCHAR(pDevice->playback.id.wasapi, sizeof(pDevice->playback.id.wasapi), data.id.wasapi); - } - - return MA_SUCCESS; -} - -static ma_result ma_device_init__wasapi(ma_device* pDevice, const ma_device_config* pConfig, ma_device_descriptor* pDescriptorPlayback, ma_device_descriptor* pDescriptorCapture) -{ - ma_result result = MA_SUCCESS; - -#if defined(MA_WIN32_DESKTOP) || defined(MA_WIN32_GDK) - HRESULT hr; - ma_IMMDeviceEnumerator* pDeviceEnumerator; -#endif - - MA_ASSERT(pDevice != NULL); - - MA_ZERO_OBJECT(&pDevice->wasapi); - pDevice->wasapi.usage = pConfig->wasapi.usage; - pDevice->wasapi.noAutoConvertSRC = pConfig->wasapi.noAutoConvertSRC; - pDevice->wasapi.noDefaultQualitySRC = pConfig->wasapi.noDefaultQualitySRC; - pDevice->wasapi.noHardwareOffloading = pConfig->wasapi.noHardwareOffloading; - pDevice->wasapi.loopbackProcessID = pConfig->wasapi.loopbackProcessID; - pDevice->wasapi.loopbackProcessExclude = pConfig->wasapi.loopbackProcessExclude; - - /* Exclusive mode is not allowed with loopback. */ - if (pConfig->deviceType == ma_device_type_loopback && pConfig->playback.shareMode == ma_share_mode_exclusive) { - return MA_INVALID_DEVICE_CONFIG; - } - - if (pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex || pConfig->deviceType == ma_device_type_loopback) { - ma_device_init_internal_data__wasapi data; - data.formatIn = pDescriptorCapture->format; - data.channelsIn = pDescriptorCapture->channels; - data.sampleRateIn = pDescriptorCapture->sampleRate; - MA_COPY_MEMORY(data.channelMapIn, pDescriptorCapture->channelMap, sizeof(pDescriptorCapture->channelMap)); - data.periodSizeInFramesIn = pDescriptorCapture->periodSizeInFrames; - data.periodSizeInMillisecondsIn = pDescriptorCapture->periodSizeInMilliseconds; - data.periodsIn = pDescriptorCapture->periodCount; - data.shareMode = pDescriptorCapture->shareMode; - data.performanceProfile = pConfig->performanceProfile; - data.noAutoConvertSRC = pConfig->wasapi.noAutoConvertSRC; - data.noDefaultQualitySRC = pConfig->wasapi.noDefaultQualitySRC; - data.noHardwareOffloading = pConfig->wasapi.noHardwareOffloading; - data.loopbackProcessID = pConfig->wasapi.loopbackProcessID; - data.loopbackProcessExclude = pConfig->wasapi.loopbackProcessExclude; - - result = ma_device_init_internal__wasapi(pDevice->pContext, (pConfig->deviceType == ma_device_type_loopback) ? ma_device_type_loopback : ma_device_type_capture, pDescriptorCapture->pDeviceID, &data); - if (result != MA_SUCCESS) { - return result; - } - - pDevice->wasapi.pAudioClientCapture = data.pAudioClient; - pDevice->wasapi.pCaptureClient = data.pCaptureClient; - pDevice->wasapi.originalPeriodSizeInMilliseconds = pDescriptorCapture->periodSizeInMilliseconds; - pDevice->wasapi.originalPeriodSizeInFrames = pDescriptorCapture->periodSizeInFrames; - pDevice->wasapi.originalPeriods = pDescriptorCapture->periodCount; - pDevice->wasapi.originalPerformanceProfile = pConfig->performanceProfile; - - /* - The event for capture needs to be manual reset for the same reason as playback. We keep the initial state set to unsignaled, - however, because we want to block until we actually have something for the first call to ma_device_read(). - */ - pDevice->wasapi.hEventCapture = (ma_handle)CreateEventA(NULL, FALSE, FALSE, NULL); /* Auto reset, unsignaled by default. */ - if (pDevice->wasapi.hEventCapture == NULL) { - result = ma_result_from_GetLastError(GetLastError()); - - if (pDevice->wasapi.pCaptureClient != NULL) { - ma_IAudioCaptureClient_Release((ma_IAudioCaptureClient*)pDevice->wasapi.pCaptureClient); - pDevice->wasapi.pCaptureClient = NULL; - } - if (pDevice->wasapi.pAudioClientCapture != NULL) { - ma_IAudioClient_Release((ma_IAudioClient*)pDevice->wasapi.pAudioClientCapture); - pDevice->wasapi.pAudioClientCapture = NULL; - } - - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to create event for capture."); - return result; - } - ma_IAudioClient_SetEventHandle((ma_IAudioClient*)pDevice->wasapi.pAudioClientCapture, (HANDLE)pDevice->wasapi.hEventCapture); - - pDevice->wasapi.periodSizeInFramesCapture = data.periodSizeInFramesOut; - ma_IAudioClient_GetBufferSize((ma_IAudioClient*)pDevice->wasapi.pAudioClientCapture, &pDevice->wasapi.actualBufferSizeInFramesCapture); - - /* We must always have a valid ID. */ - ma_strcpy_s_WCHAR(pDevice->capture.id.wasapi, sizeof(pDevice->capture.id.wasapi), data.id.wasapi); - - /* The descriptor needs to be updated with actual values. */ - pDescriptorCapture->format = data.formatOut; - pDescriptorCapture->channels = data.channelsOut; - pDescriptorCapture->sampleRate = data.sampleRateOut; - MA_COPY_MEMORY(pDescriptorCapture->channelMap, data.channelMapOut, sizeof(data.channelMapOut)); - pDescriptorCapture->periodSizeInFrames = data.periodSizeInFramesOut; - pDescriptorCapture->periodCount = data.periodsOut; - } - - if (pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) { - ma_device_init_internal_data__wasapi data; - data.formatIn = pDescriptorPlayback->format; - data.channelsIn = pDescriptorPlayback->channels; - data.sampleRateIn = pDescriptorPlayback->sampleRate; - MA_COPY_MEMORY(data.channelMapIn, pDescriptorPlayback->channelMap, sizeof(pDescriptorPlayback->channelMap)); - data.periodSizeInFramesIn = pDescriptorPlayback->periodSizeInFrames; - data.periodSizeInMillisecondsIn = pDescriptorPlayback->periodSizeInMilliseconds; - data.periodsIn = pDescriptorPlayback->periodCount; - data.shareMode = pDescriptorPlayback->shareMode; - data.performanceProfile = pConfig->performanceProfile; - data.noAutoConvertSRC = pConfig->wasapi.noAutoConvertSRC; - data.noDefaultQualitySRC = pConfig->wasapi.noDefaultQualitySRC; - data.noHardwareOffloading = pConfig->wasapi.noHardwareOffloading; - data.loopbackProcessID = pConfig->wasapi.loopbackProcessID; - data.loopbackProcessExclude = pConfig->wasapi.loopbackProcessExclude; - - result = ma_device_init_internal__wasapi(pDevice->pContext, ma_device_type_playback, pDescriptorPlayback->pDeviceID, &data); - if (result != MA_SUCCESS) { - if (pConfig->deviceType == ma_device_type_duplex) { - if (pDevice->wasapi.pCaptureClient != NULL) { - ma_IAudioCaptureClient_Release((ma_IAudioCaptureClient*)pDevice->wasapi.pCaptureClient); - pDevice->wasapi.pCaptureClient = NULL; - } - if (pDevice->wasapi.pAudioClientCapture != NULL) { - ma_IAudioClient_Release((ma_IAudioClient*)pDevice->wasapi.pAudioClientCapture); - pDevice->wasapi.pAudioClientCapture = NULL; - } - - CloseHandle((HANDLE)pDevice->wasapi.hEventCapture); - pDevice->wasapi.hEventCapture = NULL; - } - return result; - } - - pDevice->wasapi.pAudioClientPlayback = data.pAudioClient; - pDevice->wasapi.pRenderClient = data.pRenderClient; - pDevice->wasapi.originalPeriodSizeInMilliseconds = pDescriptorPlayback->periodSizeInMilliseconds; - pDevice->wasapi.originalPeriodSizeInFrames = pDescriptorPlayback->periodSizeInFrames; - pDevice->wasapi.originalPeriods = pDescriptorPlayback->periodCount; - pDevice->wasapi.originalPerformanceProfile = pConfig->performanceProfile; - - /* - The event for playback is needs to be manual reset because we want to explicitly control the fact that it becomes signalled - only after the whole available space has been filled, never before. - - The playback event also needs to be initially set to a signaled state so that the first call to ma_device_write() is able - to get passed WaitForMultipleObjects(). - */ - pDevice->wasapi.hEventPlayback = (ma_handle)CreateEventA(NULL, FALSE, TRUE, NULL); /* Auto reset, signaled by default. */ - if (pDevice->wasapi.hEventPlayback == NULL) { - result = ma_result_from_GetLastError(GetLastError()); - - if (pConfig->deviceType == ma_device_type_duplex) { - if (pDevice->wasapi.pCaptureClient != NULL) { - ma_IAudioCaptureClient_Release((ma_IAudioCaptureClient*)pDevice->wasapi.pCaptureClient); - pDevice->wasapi.pCaptureClient = NULL; - } - if (pDevice->wasapi.pAudioClientCapture != NULL) { - ma_IAudioClient_Release((ma_IAudioClient*)pDevice->wasapi.pAudioClientCapture); - pDevice->wasapi.pAudioClientCapture = NULL; - } - - CloseHandle((HANDLE)pDevice->wasapi.hEventCapture); - pDevice->wasapi.hEventCapture = NULL; - } - - if (pDevice->wasapi.pRenderClient != NULL) { - ma_IAudioRenderClient_Release((ma_IAudioRenderClient*)pDevice->wasapi.pRenderClient); - pDevice->wasapi.pRenderClient = NULL; - } - if (pDevice->wasapi.pAudioClientPlayback != NULL) { - ma_IAudioClient_Release((ma_IAudioClient*)pDevice->wasapi.pAudioClientPlayback); - pDevice->wasapi.pAudioClientPlayback = NULL; - } - - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to create event for playback."); - return result; - } - ma_IAudioClient_SetEventHandle((ma_IAudioClient*)pDevice->wasapi.pAudioClientPlayback, (HANDLE)pDevice->wasapi.hEventPlayback); - - pDevice->wasapi.periodSizeInFramesPlayback = data.periodSizeInFramesOut; - ma_IAudioClient_GetBufferSize((ma_IAudioClient*)pDevice->wasapi.pAudioClientPlayback, &pDevice->wasapi.actualBufferSizeInFramesPlayback); - - /* We must always have a valid ID because rerouting will look at it. */ - ma_strcpy_s_WCHAR(pDevice->playback.id.wasapi, sizeof(pDevice->playback.id.wasapi), data.id.wasapi); - - /* The descriptor needs to be updated with actual values. */ - pDescriptorPlayback->format = data.formatOut; - pDescriptorPlayback->channels = data.channelsOut; - pDescriptorPlayback->sampleRate = data.sampleRateOut; - MA_COPY_MEMORY(pDescriptorPlayback->channelMap, data.channelMapOut, sizeof(data.channelMapOut)); - pDescriptorPlayback->periodSizeInFrames = data.periodSizeInFramesOut; - pDescriptorPlayback->periodCount = data.periodsOut; - } - - /* - We need to register a notification client to detect when the device has been disabled, unplugged or re-routed (when the default device changes). When - we are connecting to the default device we want to do automatic stream routing when the device is disabled or unplugged. Otherwise we want to just - stop the device outright and let the application handle it. - */ -#if defined(MA_WIN32_DESKTOP) || defined(MA_WIN32_GDK) - if (pConfig->wasapi.noAutoStreamRouting == MA_FALSE) { - if ((pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex || pConfig->deviceType == ma_device_type_loopback) && pConfig->capture.pDeviceID == NULL) { - pDevice->wasapi.allowCaptureAutoStreamRouting = MA_TRUE; - } - if ((pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) && pConfig->playback.pDeviceID == NULL) { - pDevice->wasapi.allowPlaybackAutoStreamRouting = MA_TRUE; - } - } - - ma_mutex_init(&pDevice->wasapi.rerouteLock); - - hr = ma_CoCreateInstance(pDevice->pContext, &MA_CLSID_MMDeviceEnumerator, NULL, CLSCTX_ALL, &MA_IID_IMMDeviceEnumerator, (void**)&pDeviceEnumerator); - if (FAILED(hr)) { - ma_device_uninit__wasapi(pDevice); - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to create device enumerator."); - return ma_result_from_HRESULT(hr); - } - - pDevice->wasapi.notificationClient.lpVtbl = (void*)&g_maNotificationCientVtbl; - pDevice->wasapi.notificationClient.counter = 1; - pDevice->wasapi.notificationClient.pDevice = pDevice; - - hr = pDeviceEnumerator->lpVtbl->RegisterEndpointNotificationCallback(pDeviceEnumerator, &pDevice->wasapi.notificationClient); - if (SUCCEEDED(hr)) { - pDevice->wasapi.pDeviceEnumerator = (ma_ptr)pDeviceEnumerator; - } else { - /* Not the end of the world if we fail to register the notification callback. We just won't support automatic stream routing. */ - ma_IMMDeviceEnumerator_Release(pDeviceEnumerator); - } -#endif - - ma_atomic_bool32_set(&pDevice->wasapi.isStartedCapture, MA_FALSE); - ma_atomic_bool32_set(&pDevice->wasapi.isStartedPlayback, MA_FALSE); - - return MA_SUCCESS; -} - -static ma_result ma_device__get_available_frames__wasapi(ma_device* pDevice, ma_IAudioClient* pAudioClient, ma_uint32* pFrameCount) -{ - ma_uint32 paddingFramesCount; - HRESULT hr; - ma_share_mode shareMode; - - MA_ASSERT(pDevice != NULL); - MA_ASSERT(pFrameCount != NULL); - - *pFrameCount = 0; - - if ((ma_ptr)pAudioClient != pDevice->wasapi.pAudioClientPlayback && (ma_ptr)pAudioClient != pDevice->wasapi.pAudioClientCapture) { - return MA_INVALID_OPERATION; - } - - /* - I've had a report that GetCurrentPadding() is returning a frame count of 0 which is preventing - higher level function calls from doing anything because it thinks nothing is available. I have - taken a look at the documentation and it looks like this is unnecessary in exclusive mode. - - From Microsoft's documentation: - - For an exclusive-mode rendering or capture stream that was initialized with the - AUDCLNT_STREAMFLAGS_EVENTCALLBACK flag, the client typically has no use for the padding - value reported by GetCurrentPadding. Instead, the client accesses an entire buffer during - each processing pass. - - Considering this, I'm going to skip GetCurrentPadding() for exclusive mode and just report the - entire buffer. This depends on the caller making sure they wait on the event handler. - */ - shareMode = ((ma_ptr)pAudioClient == pDevice->wasapi.pAudioClientPlayback) ? pDevice->playback.shareMode : pDevice->capture.shareMode; - if (shareMode == ma_share_mode_shared) { - /* Shared mode. */ - hr = ma_IAudioClient_GetCurrentPadding(pAudioClient, &paddingFramesCount); - if (FAILED(hr)) { - return ma_result_from_HRESULT(hr); - } - - if ((ma_ptr)pAudioClient == pDevice->wasapi.pAudioClientPlayback) { - *pFrameCount = pDevice->wasapi.actualBufferSizeInFramesPlayback - paddingFramesCount; - } else { - *pFrameCount = paddingFramesCount; - } - } else { - /* Exclusive mode. */ - if ((ma_ptr)pAudioClient == pDevice->wasapi.pAudioClientPlayback) { - *pFrameCount = pDevice->wasapi.actualBufferSizeInFramesPlayback; - } else { - *pFrameCount = pDevice->wasapi.actualBufferSizeInFramesCapture; - } - } - - return MA_SUCCESS; -} - - -static ma_result ma_device_reroute__wasapi(ma_device* pDevice, ma_device_type deviceType) -{ - ma_result result; - - if (deviceType == ma_device_type_duplex) { - return MA_INVALID_ARGS; - } - - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "=== CHANGING DEVICE ===\n"); - - result = ma_device_reinit__wasapi(pDevice, deviceType); - if (result != MA_SUCCESS) { - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_WARNING, "[WASAPI] Reinitializing device after route change failed.\n"); - return result; - } - - ma_device__post_init_setup(pDevice, deviceType); - ma_device__on_notification_rerouted(pDevice); - - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "=== DEVICE CHANGED ===\n"); - - return MA_SUCCESS; -} - -static ma_result ma_device_start__wasapi_nolock(ma_device* pDevice) -{ - HRESULT hr; - - if (pDevice->pContext->wasapi.hAvrt) { - const char* pTaskName = ma_to_usage_string__wasapi(pDevice->wasapi.usage); - if (pTaskName) { - DWORD idx = 0; - pDevice->wasapi.hAvrtHandle = (ma_handle)((MA_PFN_AvSetMmThreadCharacteristicsA)pDevice->pContext->wasapi.AvSetMmThreadCharacteristicsA)(pTaskName, &idx); - } - } - - if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex || pDevice->type == ma_device_type_loopback) { - hr = ma_IAudioClient_Start((ma_IAudioClient*)pDevice->wasapi.pAudioClientCapture); - if (FAILED(hr)) { - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to start internal capture device. HRESULT = %d.", (int)hr); - return ma_result_from_HRESULT(hr); - } - - ma_atomic_bool32_set(&pDevice->wasapi.isStartedCapture, MA_TRUE); - } - - if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) { - hr = ma_IAudioClient_Start((ma_IAudioClient*)pDevice->wasapi.pAudioClientPlayback); - if (FAILED(hr)) { - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to start internal playback device. HRESULT = %d.", (int)hr); - return ma_result_from_HRESULT(hr); - } - - ma_atomic_bool32_set(&pDevice->wasapi.isStartedPlayback, MA_TRUE); - } - - return MA_SUCCESS; -} - -static ma_result ma_device_start__wasapi(ma_device* pDevice) -{ - ma_result result; - - MA_ASSERT(pDevice != NULL); - - /* Wait for any rerouting to finish before attempting to start the device. */ - ma_mutex_lock(&pDevice->wasapi.rerouteLock); - { - result = ma_device_start__wasapi_nolock(pDevice); - } - ma_mutex_unlock(&pDevice->wasapi.rerouteLock); - - return result; -} - -static ma_result ma_device_stop__wasapi_nolock(ma_device* pDevice) -{ - ma_result result; - HRESULT hr; - - MA_ASSERT(pDevice != NULL); - - if (pDevice->wasapi.hAvrtHandle) { - ((MA_PFN_AvRevertMmThreadCharacteristics)pDevice->pContext->wasapi.AvRevertMmThreadcharacteristics)((HANDLE)pDevice->wasapi.hAvrtHandle); - pDevice->wasapi.hAvrtHandle = NULL; - } - - if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex || pDevice->type == ma_device_type_loopback) { - /* If we have a mapped buffer we need to release it. */ - if (pDevice->wasapi.pMappedBufferCapture != NULL) { - ma_IAudioCaptureClient_ReleaseBuffer((ma_IAudioCaptureClient*)pDevice->wasapi.pCaptureClient, pDevice->wasapi.mappedBufferCaptureCap); - pDevice->wasapi.pMappedBufferCapture = NULL; - pDevice->wasapi.mappedBufferCaptureCap = 0; - pDevice->wasapi.mappedBufferCaptureLen = 0; - } - - hr = ma_IAudioClient_Stop((ma_IAudioClient*)pDevice->wasapi.pAudioClientCapture); - if (FAILED(hr)) { - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to stop internal capture device."); - return ma_result_from_HRESULT(hr); - } - - /* The audio client needs to be reset otherwise restarting will fail. */ - hr = ma_IAudioClient_Reset((ma_IAudioClient*)pDevice->wasapi.pAudioClientCapture); - if (FAILED(hr)) { - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to reset internal capture device."); - return ma_result_from_HRESULT(hr); - } - - ma_atomic_bool32_set(&pDevice->wasapi.isStartedCapture, MA_FALSE); - } - - if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) { - if (pDevice->wasapi.pMappedBufferPlayback != NULL) { - ma_silence_pcm_frames( - ma_offset_pcm_frames_ptr(pDevice->wasapi.pMappedBufferPlayback, pDevice->wasapi.mappedBufferPlaybackLen, pDevice->playback.internalFormat, pDevice->playback.internalChannels), - pDevice->wasapi.mappedBufferPlaybackCap - pDevice->wasapi.mappedBufferPlaybackLen, - pDevice->playback.internalFormat, pDevice->playback.internalChannels - ); - ma_IAudioRenderClient_ReleaseBuffer((ma_IAudioRenderClient*)pDevice->wasapi.pRenderClient, pDevice->wasapi.mappedBufferPlaybackCap, 0); - pDevice->wasapi.pMappedBufferPlayback = NULL; - pDevice->wasapi.mappedBufferPlaybackCap = 0; - pDevice->wasapi.mappedBufferPlaybackLen = 0; - } - - /* - The buffer needs to be drained before stopping the device. Not doing this will result in the last few frames not getting output to - the speakers. This is a problem for very short sounds because it'll result in a significant portion of it not getting played. - */ - if (ma_atomic_bool32_get(&pDevice->wasapi.isStartedPlayback)) { - /* We need to make sure we put a timeout here or else we'll risk getting stuck in a deadlock in some cases. */ - DWORD waitTime = (pDevice->wasapi.actualBufferSizeInFramesPlayback * 1000) / pDevice->playback.internalSampleRate; - - if (pDevice->playback.shareMode == ma_share_mode_exclusive) { - WaitForSingleObject((HANDLE)pDevice->wasapi.hEventPlayback, waitTime); - } else { - ma_uint32 prevFramesAvailablePlayback = (ma_uint32)-1; - ma_uint32 framesAvailablePlayback; - for (;;) { - result = ma_device__get_available_frames__wasapi(pDevice, (ma_IAudioClient*)pDevice->wasapi.pAudioClientPlayback, &framesAvailablePlayback); - if (result != MA_SUCCESS) { - break; - } - - if (framesAvailablePlayback >= pDevice->wasapi.actualBufferSizeInFramesPlayback) { - break; - } - - /* - Just a safety check to avoid an infinite loop. If this iteration results in a situation where the number of available frames - has not changed, get out of the loop. I don't think this should ever happen, but I think it's nice to have just in case. - */ - if (framesAvailablePlayback == prevFramesAvailablePlayback) { - break; - } - prevFramesAvailablePlayback = framesAvailablePlayback; - - ResetEvent((HANDLE)pDevice->wasapi.hEventPlayback); /* Manual reset. */ - WaitForSingleObject((HANDLE)pDevice->wasapi.hEventPlayback, waitTime); - } - } - } - - hr = ma_IAudioClient_Stop((ma_IAudioClient*)pDevice->wasapi.pAudioClientPlayback); - if (FAILED(hr)) { - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to stop internal playback device."); - return ma_result_from_HRESULT(hr); - } - - /* The audio client needs to be reset otherwise restarting will fail. */ - { - ma_int32 retries = 5; - - while ((hr = ma_IAudioClient_Reset((ma_IAudioClient*)pDevice->wasapi.pAudioClientPlayback)) == MA_AUDCLNT_E_BUFFER_OPERATION_PENDING && retries > 0) { - ma_sleep(10); - retries -= 1; - } - } - - if (FAILED(hr)) { - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to reset internal playback device."); - return ma_result_from_HRESULT(hr); - } - - ma_atomic_bool32_set(&pDevice->wasapi.isStartedPlayback, MA_FALSE); - } - - return MA_SUCCESS; -} - -static ma_result ma_device_stop__wasapi(ma_device* pDevice) -{ - ma_result result; - - MA_ASSERT(pDevice != NULL); - - /* Wait for any rerouting to finish before attempting to stop the device. */ - ma_mutex_lock(&pDevice->wasapi.rerouteLock); - { - result = ma_device_stop__wasapi_nolock(pDevice); - } - ma_mutex_unlock(&pDevice->wasapi.rerouteLock); - - return result; -} - - -#ifndef MA_WASAPI_WAIT_TIMEOUT_MILLISECONDS -#define MA_WASAPI_WAIT_TIMEOUT_MILLISECONDS 5000 -#endif - -static ma_result ma_device_read__wasapi(ma_device* pDevice, void* pFrames, ma_uint32 frameCount, ma_uint32* pFramesRead) -{ - ma_result result = MA_SUCCESS; - ma_uint32 totalFramesProcessed = 0; - - /* - When reading, we need to get a buffer and process all of it before releasing it. Because the - frame count (frameCount) can be different to the size of the buffer, we'll need to cache the - pointer to the buffer. - */ - - /* Keep running until we've processed the requested number of frames. */ - while (ma_device_get_state(pDevice) == ma_device_state_started && totalFramesProcessed < frameCount) { - ma_uint32 framesRemaining = frameCount - totalFramesProcessed; - - /* If we have a mapped data buffer, consume that first. */ - if (pDevice->wasapi.pMappedBufferCapture != NULL) { - /* We have a cached data pointer so consume that before grabbing another one from WASAPI. */ - ma_uint32 framesToProcessNow = framesRemaining; - if (framesToProcessNow > pDevice->wasapi.mappedBufferCaptureLen) { - framesToProcessNow = pDevice->wasapi.mappedBufferCaptureLen; - } - - /* Now just copy the data over to the output buffer. */ - ma_copy_pcm_frames( - ma_offset_pcm_frames_ptr(pFrames, totalFramesProcessed, pDevice->capture.internalFormat, pDevice->capture.internalChannels), - ma_offset_pcm_frames_const_ptr(pDevice->wasapi.pMappedBufferCapture, pDevice->wasapi.mappedBufferCaptureCap - pDevice->wasapi.mappedBufferCaptureLen, pDevice->capture.internalFormat, pDevice->capture.internalChannels), - framesToProcessNow, - pDevice->capture.internalFormat, pDevice->capture.internalChannels - ); - - totalFramesProcessed += framesToProcessNow; - pDevice->wasapi.mappedBufferCaptureLen -= framesToProcessNow; - - /* If the data buffer has been fully consumed we need to release it. */ - if (pDevice->wasapi.mappedBufferCaptureLen == 0) { - ma_IAudioCaptureClient_ReleaseBuffer((ma_IAudioCaptureClient*)pDevice->wasapi.pCaptureClient, pDevice->wasapi.mappedBufferCaptureCap); - pDevice->wasapi.pMappedBufferCapture = NULL; - pDevice->wasapi.mappedBufferCaptureCap = 0; - } - } else { - /* We don't have any cached data pointer, so grab another one. */ - HRESULT hr; - DWORD flags = 0; - - /* First just ask WASAPI for a data buffer. If it's not available, we'll wait for more. */ - hr = ma_IAudioCaptureClient_GetBuffer((ma_IAudioCaptureClient*)pDevice->wasapi.pCaptureClient, (BYTE**)&pDevice->wasapi.pMappedBufferCapture, &pDevice->wasapi.mappedBufferCaptureCap, &flags, NULL, NULL); - if (hr == S_OK) { - /* We got a data buffer. Continue to the next loop iteration which will then read from the mapped pointer. */ - pDevice->wasapi.mappedBufferCaptureLen = pDevice->wasapi.mappedBufferCaptureCap; - - /* - There have been reports that indicate that at times the AUDCLNT_BUFFERFLAGS_DATA_DISCONTINUITY is reported for every - call to IAudioCaptureClient_GetBuffer() above which results in spamming of the debug messages below. To partially - work around this, I'm only outputting these messages when MA_DEBUG_OUTPUT is explicitly defined. The better solution - would be to figure out why the flag is always getting reported. - */ - #if defined(MA_DEBUG_OUTPUT) - { - if (flags != 0) { - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "[WASAPI] Capture Flags: %ld\n", flags); - - if ((flags & MA_AUDCLNT_BUFFERFLAGS_DATA_DISCONTINUITY) != 0) { - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "[WASAPI] Data discontinuity (possible overrun). Attempting recovery. mappedBufferCaptureCap=%d\n", pDevice->wasapi.mappedBufferCaptureCap); - } - } - } - #endif - - /* Overrun detection. */ - if ((flags & MA_AUDCLNT_BUFFERFLAGS_DATA_DISCONTINUITY) != 0) { - /* Glitched. Probably due to an overrun. */ - - /* - If we got an overrun it probably means we're straddling the end of the buffer. In normal capture - mode this is the fault of the client application because they're responsible for ensuring data is - processed fast enough. In duplex mode, however, the processing of audio is tied to the playback - device, so this can possibly be the result of a timing de-sync. - - In capture mode we're not going to do any kind of recovery because the real fix is for the client - application to process faster. In duplex mode, we'll treat this as a desync and reset the buffers - to prevent a never-ending sequence of glitches due to straddling the end of the buffer. - */ - if (pDevice->type == ma_device_type_duplex) { - /* - Experiment: - - If we empty out the *entire* buffer we may end up putting ourselves into an underrun position - which isn't really any better than the overrun we're probably in right now. Instead we'll just - empty out about half. - */ - ma_uint32 i; - ma_uint32 periodCount = (pDevice->wasapi.actualBufferSizeInFramesCapture / pDevice->wasapi.periodSizeInFramesCapture); - ma_uint32 iterationCount = periodCount / 2; - if ((periodCount % 2) > 0) { - iterationCount += 1; - } - - for (i = 0; i < iterationCount; i += 1) { - hr = ma_IAudioCaptureClient_ReleaseBuffer((ma_IAudioCaptureClient*)pDevice->wasapi.pCaptureClient, pDevice->wasapi.mappedBufferCaptureCap); - if (FAILED(hr)) { - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "[WASAPI] Data discontinuity recovery: IAudioCaptureClient_ReleaseBuffer() failed with %ld.\n", hr); - break; - } - - flags = 0; - hr = ma_IAudioCaptureClient_GetBuffer((ma_IAudioCaptureClient*)pDevice->wasapi.pCaptureClient, (BYTE**)&pDevice->wasapi.pMappedBufferCapture, &pDevice->wasapi.mappedBufferCaptureCap, &flags, NULL, NULL); - if (hr == MA_AUDCLNT_S_BUFFER_EMPTY || FAILED(hr)) { - /* - The buffer has been completely emptied or an error occurred. In this case we'll need - to reset the state of the mapped buffer which will trigger the next iteration to get - a fresh buffer from WASAPI. - */ - pDevice->wasapi.pMappedBufferCapture = NULL; - pDevice->wasapi.mappedBufferCaptureCap = 0; - pDevice->wasapi.mappedBufferCaptureLen = 0; - - if (hr == MA_AUDCLNT_S_BUFFER_EMPTY) { - if ((flags & MA_AUDCLNT_BUFFERFLAGS_DATA_DISCONTINUITY) != 0) { - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "[WASAPI] Data discontinuity recovery: Buffer emptied, and data discontinuity still reported.\n"); - } else { - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "[WASAPI] Data discontinuity recovery: Buffer emptied.\n"); - } - } - - if (FAILED(hr)) { - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "[WASAPI] Data discontinuity recovery: IAudioCaptureClient_GetBuffer() failed with %ld.\n", hr); - } - - break; - } - } - - /* If at this point we have a valid buffer mapped, make sure the buffer length is set appropriately. */ - if (pDevice->wasapi.pMappedBufferCapture != NULL) { - pDevice->wasapi.mappedBufferCaptureLen = pDevice->wasapi.mappedBufferCaptureCap; - } - } - } - - continue; - } else { - if (hr == MA_AUDCLNT_S_BUFFER_EMPTY || hr == MA_AUDCLNT_E_BUFFER_ERROR) { - /* - No data is available. We need to wait for more. There's two situations to consider - here. The first is normal capture mode. If this times out it probably means the - microphone isn't delivering data for whatever reason. In this case we'll just - abort the read and return whatever we were able to get. The other situations is - loopback mode, in which case a timeout probably just means the nothing is playing - through the speakers. - */ - - /* Experiment: Use a shorter timeout for loopback mode. */ - DWORD timeoutInMilliseconds = MA_WASAPI_WAIT_TIMEOUT_MILLISECONDS; - if (pDevice->type == ma_device_type_loopback) { - timeoutInMilliseconds = 10; - } - - if (WaitForSingleObject((HANDLE)pDevice->wasapi.hEventCapture, timeoutInMilliseconds) != WAIT_OBJECT_0) { - if (pDevice->type == ma_device_type_loopback) { - continue; /* Keep waiting in loopback mode. */ - } else { - result = MA_ERROR; - break; /* Wait failed. */ - } - } - - /* At this point we should be able to loop back to the start of the loop and try retrieving a data buffer again. */ - } else { - /* An error occurred and we need to abort. */ - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to retrieve internal buffer from capture device in preparation for reading from the device. HRESULT = %d. Stopping device.\n", (int)hr); - result = ma_result_from_HRESULT(hr); - break; - } - } - } - } - - /* - If we were unable to process the entire requested frame count, but we still have a mapped buffer, - there's a good chance either an error occurred or the device was stopped mid-read. In this case - we'll need to make sure the buffer is released. - */ - if (totalFramesProcessed < frameCount && pDevice->wasapi.pMappedBufferCapture != NULL) { - ma_IAudioCaptureClient_ReleaseBuffer((ma_IAudioCaptureClient*)pDevice->wasapi.pCaptureClient, pDevice->wasapi.mappedBufferCaptureCap); - pDevice->wasapi.pMappedBufferCapture = NULL; - pDevice->wasapi.mappedBufferCaptureCap = 0; - pDevice->wasapi.mappedBufferCaptureLen = 0; - } - - if (pFramesRead != NULL) { - *pFramesRead = totalFramesProcessed; - } - - return result; -} - -static ma_result ma_device_write__wasapi(ma_device* pDevice, const void* pFrames, ma_uint32 frameCount, ma_uint32* pFramesWritten) -{ - ma_result result = MA_SUCCESS; - ma_uint32 totalFramesProcessed = 0; - - /* Keep writing to the device until it's stopped or we've consumed all of our input. */ - while (ma_device_get_state(pDevice) == ma_device_state_started && totalFramesProcessed < frameCount) { - ma_uint32 framesRemaining = frameCount - totalFramesProcessed; - - /* - We're going to do this in a similar way to capture. We'll first check if the cached data pointer - is valid, and if so, read from that. Otherwise We will call IAudioRenderClient_GetBuffer() with - a requested buffer size equal to our actual period size. If it returns AUDCLNT_E_BUFFER_TOO_LARGE - it means we need to wait for some data to become available. - */ - if (pDevice->wasapi.pMappedBufferPlayback != NULL) { - /* We still have some space available in the mapped data buffer. Write to it. */ - ma_uint32 framesToProcessNow = framesRemaining; - if (framesToProcessNow > (pDevice->wasapi.mappedBufferPlaybackCap - pDevice->wasapi.mappedBufferPlaybackLen)) { - framesToProcessNow = (pDevice->wasapi.mappedBufferPlaybackCap - pDevice->wasapi.mappedBufferPlaybackLen); - } - - /* Now just copy the data over to the output buffer. */ - ma_copy_pcm_frames( - ma_offset_pcm_frames_ptr(pDevice->wasapi.pMappedBufferPlayback, pDevice->wasapi.mappedBufferPlaybackLen, pDevice->playback.internalFormat, pDevice->playback.internalChannels), - ma_offset_pcm_frames_const_ptr(pFrames, totalFramesProcessed, pDevice->playback.internalFormat, pDevice->playback.internalChannels), - framesToProcessNow, - pDevice->playback.internalFormat, pDevice->playback.internalChannels - ); - - totalFramesProcessed += framesToProcessNow; - pDevice->wasapi.mappedBufferPlaybackLen += framesToProcessNow; - - /* If the data buffer has been fully consumed we need to release it. */ - if (pDevice->wasapi.mappedBufferPlaybackLen == pDevice->wasapi.mappedBufferPlaybackCap) { - ma_IAudioRenderClient_ReleaseBuffer((ma_IAudioRenderClient*)pDevice->wasapi.pRenderClient, pDevice->wasapi.mappedBufferPlaybackCap, 0); - pDevice->wasapi.pMappedBufferPlayback = NULL; - pDevice->wasapi.mappedBufferPlaybackCap = 0; - pDevice->wasapi.mappedBufferPlaybackLen = 0; - - /* - In exclusive mode we need to wait here. Exclusive mode is weird because GetBuffer() never - seems to return AUDCLNT_E_BUFFER_TOO_LARGE, which is what we normally use to determine - whether or not we need to wait for more data. - */ - if (pDevice->playback.shareMode == ma_share_mode_exclusive) { - if (WaitForSingleObject((HANDLE)pDevice->wasapi.hEventPlayback, MA_WASAPI_WAIT_TIMEOUT_MILLISECONDS) != WAIT_OBJECT_0) { - result = MA_ERROR; - break; /* Wait failed. Probably timed out. */ - } - } - } - } else { - /* We don't have a mapped data buffer so we'll need to get one. */ - HRESULT hr; - ma_uint32 bufferSizeInFrames; - - /* Special rules for exclusive mode. */ - if (pDevice->playback.shareMode == ma_share_mode_exclusive) { - bufferSizeInFrames = pDevice->wasapi.actualBufferSizeInFramesPlayback; - } else { - bufferSizeInFrames = pDevice->wasapi.periodSizeInFramesPlayback; - } - - hr = ma_IAudioRenderClient_GetBuffer((ma_IAudioRenderClient*)pDevice->wasapi.pRenderClient, bufferSizeInFrames, (BYTE**)&pDevice->wasapi.pMappedBufferPlayback); - if (hr == S_OK) { - /* We have data available. */ - pDevice->wasapi.mappedBufferPlaybackCap = bufferSizeInFrames; - pDevice->wasapi.mappedBufferPlaybackLen = 0; - } else { - if (hr == MA_AUDCLNT_E_BUFFER_TOO_LARGE || hr == MA_AUDCLNT_E_BUFFER_ERROR) { - /* Not enough data available. We need to wait for more. */ - if (WaitForSingleObject((HANDLE)pDevice->wasapi.hEventPlayback, MA_WASAPI_WAIT_TIMEOUT_MILLISECONDS) != WAIT_OBJECT_0) { - result = MA_ERROR; - break; /* Wait failed. Probably timed out. */ - } - } else { - /* Some error occurred. We'll need to abort. */ - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to retrieve internal buffer from playback device in preparation for writing to the device. HRESULT = %d. Stopping device.\n", (int)hr); - result = ma_result_from_HRESULT(hr); - break; - } - } - } - } - - if (pFramesWritten != NULL) { - *pFramesWritten = totalFramesProcessed; - } - - return result; -} - -static ma_result ma_device_data_loop_wakeup__wasapi(ma_device* pDevice) -{ - MA_ASSERT(pDevice != NULL); - - if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex || pDevice->type == ma_device_type_loopback) { - SetEvent((HANDLE)pDevice->wasapi.hEventCapture); - } - - if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) { - SetEvent((HANDLE)pDevice->wasapi.hEventPlayback); - } - - return MA_SUCCESS; -} - - -static ma_result ma_context_uninit__wasapi(ma_context* pContext) -{ - ma_context_command__wasapi cmd = ma_context_init_command__wasapi(MA_CONTEXT_COMMAND_QUIT__WASAPI); - - MA_ASSERT(pContext != NULL); - MA_ASSERT(pContext->backend == ma_backend_wasapi); - - ma_context_post_command__wasapi(pContext, &cmd); - ma_thread_wait(&pContext->wasapi.commandThread); - - if (pContext->wasapi.hAvrt) { - ma_dlclose(ma_context_get_log(pContext), pContext->wasapi.hAvrt); - pContext->wasapi.hAvrt = NULL; - } - - #if defined(MA_WIN32_UWP) - { - if (pContext->wasapi.hMMDevapi) { - ma_dlclose(ma_context_get_log(pContext), pContext->wasapi.hMMDevapi); - pContext->wasapi.hMMDevapi = NULL; - } - } - #endif - - /* Only after the thread has been terminated can we uninitialize the sync objects for the command thread. */ - ma_semaphore_uninit(&pContext->wasapi.commandSem); - ma_mutex_uninit(&pContext->wasapi.commandLock); - - return MA_SUCCESS; -} - -static ma_result ma_context_init__wasapi(ma_context* pContext, const ma_context_config* pConfig, ma_backend_callbacks* pCallbacks) -{ - ma_result result = MA_SUCCESS; - - MA_ASSERT(pContext != NULL); - - (void)pConfig; - -#ifdef MA_WIN32_DESKTOP - /* - WASAPI is only supported in Vista SP1 and newer. The reason for SP1 and not the base version of Vista is that event-driven - exclusive mode does not work until SP1. - - Unfortunately older compilers don't define these functions so we need to dynamically load them in order to avoid a link error. - */ - { - ma_OSVERSIONINFOEXW osvi; - ma_handle kernel32DLL; - ma_PFNVerifyVersionInfoW _VerifyVersionInfoW; - ma_PFNVerSetConditionMask _VerSetConditionMask; - - kernel32DLL = ma_dlopen(ma_context_get_log(pContext), "kernel32.dll"); - if (kernel32DLL == NULL) { - return MA_NO_BACKEND; - } - - _VerifyVersionInfoW = (ma_PFNVerifyVersionInfoW )ma_dlsym(ma_context_get_log(pContext), kernel32DLL, "VerifyVersionInfoW"); - _VerSetConditionMask = (ma_PFNVerSetConditionMask)ma_dlsym(ma_context_get_log(pContext), kernel32DLL, "VerSetConditionMask"); - if (_VerifyVersionInfoW == NULL || _VerSetConditionMask == NULL) { - ma_dlclose(ma_context_get_log(pContext), kernel32DLL); - return MA_NO_BACKEND; - } - - MA_ZERO_OBJECT(&osvi); - osvi.dwOSVersionInfoSize = sizeof(osvi); - osvi.dwMajorVersion = ((MA_WIN32_WINNT_VISTA >> 8) & 0xFF); - osvi.dwMinorVersion = ((MA_WIN32_WINNT_VISTA >> 0) & 0xFF); - osvi.wServicePackMajor = 1; - if (_VerifyVersionInfoW(&osvi, MA_VER_MAJORVERSION | MA_VER_MINORVERSION | MA_VER_SERVICEPACKMAJOR, _VerSetConditionMask(_VerSetConditionMask(_VerSetConditionMask(0, MA_VER_MAJORVERSION, MA_VER_GREATER_EQUAL), MA_VER_MINORVERSION, MA_VER_GREATER_EQUAL), MA_VER_SERVICEPACKMAJOR, MA_VER_GREATER_EQUAL))) { - result = MA_SUCCESS; - } else { - result = MA_NO_BACKEND; - } - - ma_dlclose(ma_context_get_log(pContext), kernel32DLL); - } -#endif - - if (result != MA_SUCCESS) { - return result; - } - - MA_ZERO_OBJECT(&pContext->wasapi); - - - #if defined(MA_WIN32_UWP) - { - /* Link to mmdevapi so we can get access to ActivateAudioInterfaceAsync(). */ - pContext->wasapi.hMMDevapi = ma_dlopen(ma_context_get_log(pContext), "mmdevapi.dll"); - if (pContext->wasapi.hMMDevapi) { - pContext->wasapi.ActivateAudioInterfaceAsync = ma_dlsym(ma_context_get_log(pContext), pContext->wasapi.hMMDevapi, "ActivateAudioInterfaceAsync"); - if (pContext->wasapi.ActivateAudioInterfaceAsync == NULL) { - ma_dlclose(ma_context_get_log(pContext), pContext->wasapi.hMMDevapi); - return MA_NO_BACKEND; /* ActivateAudioInterfaceAsync() could not be loaded. */ - } - } else { - return MA_NO_BACKEND; /* Failed to load mmdevapi.dll which is required for ActivateAudioInterfaceAsync() */ - } - } - #endif - - /* Optionally use the Avrt API to specify the audio thread's latency sensitivity requirements */ - pContext->wasapi.hAvrt = ma_dlopen(ma_context_get_log(pContext), "avrt.dll"); - if (pContext->wasapi.hAvrt) { - pContext->wasapi.AvSetMmThreadCharacteristicsA = ma_dlsym(ma_context_get_log(pContext), pContext->wasapi.hAvrt, "AvSetMmThreadCharacteristicsA"); - pContext->wasapi.AvRevertMmThreadcharacteristics = ma_dlsym(ma_context_get_log(pContext), pContext->wasapi.hAvrt, "AvRevertMmThreadCharacteristics"); - - /* If either function could not be found, disable use of avrt entirely. */ - if (!pContext->wasapi.AvSetMmThreadCharacteristicsA || !pContext->wasapi.AvRevertMmThreadcharacteristics) { - pContext->wasapi.AvSetMmThreadCharacteristicsA = NULL; - pContext->wasapi.AvRevertMmThreadcharacteristics = NULL; - ma_dlclose(ma_context_get_log(pContext), pContext->wasapi.hAvrt); - pContext->wasapi.hAvrt = NULL; - } - } - - - /* - Annoyingly, WASAPI does not allow you to release an IAudioClient object from a different thread - than the one that retrieved it with GetService(). This can result in a deadlock in two - situations: - - 1) When calling ma_device_uninit() from a different thread to ma_device_init(); and - 2) When uninitializing and reinitializing the internal IAudioClient object in response to - automatic stream routing. - - We could define ma_device_uninit() such that it must be called on the same thread as - ma_device_init(). We could also just not release the IAudioClient when performing automatic - stream routing to avoid the deadlock. Neither of these are acceptable solutions in my view so - we're going to have to work around this with a worker thread. This is not ideal, but I can't - think of a better way to do this. - - More information about this can be found here: - - https://docs.microsoft.com/en-us/windows/win32/api/audioclient/nn-audioclient-iaudiorenderclient - - Note this section: - - When releasing an IAudioRenderClient interface instance, the client must call the interface's - Release method from the same thread as the call to IAudioClient::GetService that created the - object. - */ - { - result = ma_mutex_init(&pContext->wasapi.commandLock); - if (result != MA_SUCCESS) { - return result; - } - - result = ma_semaphore_init(0, &pContext->wasapi.commandSem); - if (result != MA_SUCCESS) { - ma_mutex_uninit(&pContext->wasapi.commandLock); - return result; - } - - result = ma_thread_create(&pContext->wasapi.commandThread, ma_thread_priority_normal, 0, ma_context_command_thread__wasapi, pContext, &pContext->allocationCallbacks); - if (result != MA_SUCCESS) { - ma_semaphore_uninit(&pContext->wasapi.commandSem); - ma_mutex_uninit(&pContext->wasapi.commandLock); - return result; - } - } - - - pCallbacks->onContextInit = ma_context_init__wasapi; - pCallbacks->onContextUninit = ma_context_uninit__wasapi; - pCallbacks->onContextEnumerateDevices = ma_context_enumerate_devices__wasapi; - pCallbacks->onContextGetDeviceInfo = ma_context_get_device_info__wasapi; - pCallbacks->onDeviceInit = ma_device_init__wasapi; - pCallbacks->onDeviceUninit = ma_device_uninit__wasapi; - pCallbacks->onDeviceStart = ma_device_start__wasapi; - pCallbacks->onDeviceStop = ma_device_stop__wasapi; - pCallbacks->onDeviceRead = ma_device_read__wasapi; - pCallbacks->onDeviceWrite = ma_device_write__wasapi; - pCallbacks->onDeviceDataLoop = NULL; - pCallbacks->onDeviceDataLoopWakeup = ma_device_data_loop_wakeup__wasapi; - - return MA_SUCCESS; -} -#endif - -/****************************************************************************** - -DirectSound Backend - -******************************************************************************/ -#ifdef MA_HAS_DSOUND -/*#include */ - -/*static const GUID MA_GUID_IID_DirectSoundNotify = {0xb0210783, 0x89cd, 0x11d0, {0xaf, 0x08, 0x00, 0xa0, 0xc9, 0x25, 0xcd, 0x16}};*/ - -/* miniaudio only uses priority or exclusive modes. */ -#define MA_DSSCL_NORMAL 1 -#define MA_DSSCL_PRIORITY 2 -#define MA_DSSCL_EXCLUSIVE 3 -#define MA_DSSCL_WRITEPRIMARY 4 - -#define MA_DSCAPS_PRIMARYMONO 0x00000001 -#define MA_DSCAPS_PRIMARYSTEREO 0x00000002 -#define MA_DSCAPS_PRIMARY8BIT 0x00000004 -#define MA_DSCAPS_PRIMARY16BIT 0x00000008 -#define MA_DSCAPS_CONTINUOUSRATE 0x00000010 -#define MA_DSCAPS_EMULDRIVER 0x00000020 -#define MA_DSCAPS_CERTIFIED 0x00000040 -#define MA_DSCAPS_SECONDARYMONO 0x00000100 -#define MA_DSCAPS_SECONDARYSTEREO 0x00000200 -#define MA_DSCAPS_SECONDARY8BIT 0x00000400 -#define MA_DSCAPS_SECONDARY16BIT 0x00000800 - -#define MA_DSBCAPS_PRIMARYBUFFER 0x00000001 -#define MA_DSBCAPS_STATIC 0x00000002 -#define MA_DSBCAPS_LOCHARDWARE 0x00000004 -#define MA_DSBCAPS_LOCSOFTWARE 0x00000008 -#define MA_DSBCAPS_CTRL3D 0x00000010 -#define MA_DSBCAPS_CTRLFREQUENCY 0x00000020 -#define MA_DSBCAPS_CTRLPAN 0x00000040 -#define MA_DSBCAPS_CTRLVOLUME 0x00000080 -#define MA_DSBCAPS_CTRLPOSITIONNOTIFY 0x00000100 -#define MA_DSBCAPS_CTRLFX 0x00000200 -#define MA_DSBCAPS_STICKYFOCUS 0x00004000 -#define MA_DSBCAPS_GLOBALFOCUS 0x00008000 -#define MA_DSBCAPS_GETCURRENTPOSITION2 0x00010000 -#define MA_DSBCAPS_MUTE3DATMAXDISTANCE 0x00020000 -#define MA_DSBCAPS_LOCDEFER 0x00040000 -#define MA_DSBCAPS_TRUEPLAYPOSITION 0x00080000 - -#define MA_DSBPLAY_LOOPING 0x00000001 -#define MA_DSBPLAY_LOCHARDWARE 0x00000002 -#define MA_DSBPLAY_LOCSOFTWARE 0x00000004 -#define MA_DSBPLAY_TERMINATEBY_TIME 0x00000008 -#define MA_DSBPLAY_TERMINATEBY_DISTANCE 0x00000010 -#define MA_DSBPLAY_TERMINATEBY_PRIORITY 0x00000020 - -#define MA_DSBSTATUS_PLAYING 0x00000001 -#define MA_DSBSTATUS_BUFFERLOST 0x00000002 -#define MA_DSBSTATUS_LOOPING 0x00000004 -#define MA_DSBSTATUS_LOCHARDWARE 0x00000008 -#define MA_DSBSTATUS_LOCSOFTWARE 0x00000010 -#define MA_DSBSTATUS_TERMINATED 0x00000020 - -#define MA_DSCBSTART_LOOPING 0x00000001 - -typedef struct -{ - DWORD dwSize; - DWORD dwFlags; - DWORD dwBufferBytes; - DWORD dwReserved; - MA_WAVEFORMATEX* lpwfxFormat; - GUID guid3DAlgorithm; -} MA_DSBUFFERDESC; - -typedef struct -{ - DWORD dwSize; - DWORD dwFlags; - DWORD dwBufferBytes; - DWORD dwReserved; - MA_WAVEFORMATEX* lpwfxFormat; - DWORD dwFXCount; - void* lpDSCFXDesc; /* <-- miniaudio doesn't use this, so set to void*. */ -} MA_DSCBUFFERDESC; - -typedef struct -{ - DWORD dwSize; - DWORD dwFlags; - DWORD dwMinSecondarySampleRate; - DWORD dwMaxSecondarySampleRate; - DWORD dwPrimaryBuffers; - DWORD dwMaxHwMixingAllBuffers; - DWORD dwMaxHwMixingStaticBuffers; - DWORD dwMaxHwMixingStreamingBuffers; - DWORD dwFreeHwMixingAllBuffers; - DWORD dwFreeHwMixingStaticBuffers; - DWORD dwFreeHwMixingStreamingBuffers; - DWORD dwMaxHw3DAllBuffers; - DWORD dwMaxHw3DStaticBuffers; - DWORD dwMaxHw3DStreamingBuffers; - DWORD dwFreeHw3DAllBuffers; - DWORD dwFreeHw3DStaticBuffers; - DWORD dwFreeHw3DStreamingBuffers; - DWORD dwTotalHwMemBytes; - DWORD dwFreeHwMemBytes; - DWORD dwMaxContigFreeHwMemBytes; - DWORD dwUnlockTransferRateHwBuffers; - DWORD dwPlayCpuOverheadSwBuffers; - DWORD dwReserved1; - DWORD dwReserved2; -} MA_DSCAPS; - -typedef struct -{ - DWORD dwSize; - DWORD dwFlags; - DWORD dwBufferBytes; - DWORD dwUnlockTransferRate; - DWORD dwPlayCpuOverhead; -} MA_DSBCAPS; - -typedef struct -{ - DWORD dwSize; - DWORD dwFlags; - DWORD dwFormats; - DWORD dwChannels; -} MA_DSCCAPS; - -typedef struct -{ - DWORD dwSize; - DWORD dwFlags; - DWORD dwBufferBytes; - DWORD dwReserved; -} MA_DSCBCAPS; - -typedef struct -{ - DWORD dwOffset; - HANDLE hEventNotify; -} MA_DSBPOSITIONNOTIFY; - -typedef struct ma_IDirectSound ma_IDirectSound; -typedef struct ma_IDirectSoundBuffer ma_IDirectSoundBuffer; -typedef struct ma_IDirectSoundCapture ma_IDirectSoundCapture; -typedef struct ma_IDirectSoundCaptureBuffer ma_IDirectSoundCaptureBuffer; -typedef struct ma_IDirectSoundNotify ma_IDirectSoundNotify; - - -/* -COM objects. The way these work is that you have a vtable (a list of function pointers, kind of -like how C++ works internally), and then you have a structure with a single member, which is a -pointer to the vtable. The vtable is where the methods of the object are defined. Methods need -to be in a specific order, and parent classes need to have their methods declared first. -*/ - -/* IDirectSound */ -typedef struct -{ - /* IUnknown */ - HRESULT (STDMETHODCALLTYPE * QueryInterface)(ma_IDirectSound* pThis, const IID* const riid, void** ppObject); - ULONG (STDMETHODCALLTYPE * AddRef) (ma_IDirectSound* pThis); - ULONG (STDMETHODCALLTYPE * Release) (ma_IDirectSound* pThis); - - /* IDirectSound */ - HRESULT (STDMETHODCALLTYPE * CreateSoundBuffer) (ma_IDirectSound* pThis, const MA_DSBUFFERDESC* pDSBufferDesc, ma_IDirectSoundBuffer** ppDSBuffer, void* pUnkOuter); - HRESULT (STDMETHODCALLTYPE * GetCaps) (ma_IDirectSound* pThis, MA_DSCAPS* pDSCaps); - HRESULT (STDMETHODCALLTYPE * DuplicateSoundBuffer)(ma_IDirectSound* pThis, ma_IDirectSoundBuffer* pDSBufferOriginal, ma_IDirectSoundBuffer** ppDSBufferDuplicate); - HRESULT (STDMETHODCALLTYPE * SetCooperativeLevel) (ma_IDirectSound* pThis, HWND hwnd, DWORD dwLevel); - HRESULT (STDMETHODCALLTYPE * Compact) (ma_IDirectSound* pThis); - HRESULT (STDMETHODCALLTYPE * GetSpeakerConfig) (ma_IDirectSound* pThis, DWORD* pSpeakerConfig); - HRESULT (STDMETHODCALLTYPE * SetSpeakerConfig) (ma_IDirectSound* pThis, DWORD dwSpeakerConfig); - HRESULT (STDMETHODCALLTYPE * Initialize) (ma_IDirectSound* pThis, const GUID* pGuidDevice); -} ma_IDirectSoundVtbl; -struct ma_IDirectSound -{ - ma_IDirectSoundVtbl* lpVtbl; -}; -static MA_INLINE HRESULT ma_IDirectSound_QueryInterface(ma_IDirectSound* pThis, const IID* const riid, void** ppObject) { return pThis->lpVtbl->QueryInterface(pThis, riid, ppObject); } -static MA_INLINE ULONG ma_IDirectSound_AddRef(ma_IDirectSound* pThis) { return pThis->lpVtbl->AddRef(pThis); } -static MA_INLINE ULONG ma_IDirectSound_Release(ma_IDirectSound* pThis) { return pThis->lpVtbl->Release(pThis); } -static MA_INLINE HRESULT ma_IDirectSound_CreateSoundBuffer(ma_IDirectSound* pThis, const MA_DSBUFFERDESC* pDSBufferDesc, ma_IDirectSoundBuffer** ppDSBuffer, void* pUnkOuter) { return pThis->lpVtbl->CreateSoundBuffer(pThis, pDSBufferDesc, ppDSBuffer, pUnkOuter); } -static MA_INLINE HRESULT ma_IDirectSound_GetCaps(ma_IDirectSound* pThis, MA_DSCAPS* pDSCaps) { return pThis->lpVtbl->GetCaps(pThis, pDSCaps); } -static MA_INLINE HRESULT ma_IDirectSound_DuplicateSoundBuffer(ma_IDirectSound* pThis, ma_IDirectSoundBuffer* pDSBufferOriginal, ma_IDirectSoundBuffer** ppDSBufferDuplicate) { return pThis->lpVtbl->DuplicateSoundBuffer(pThis, pDSBufferOriginal, ppDSBufferDuplicate); } -static MA_INLINE HRESULT ma_IDirectSound_SetCooperativeLevel(ma_IDirectSound* pThis, HWND hwnd, DWORD dwLevel) { return pThis->lpVtbl->SetCooperativeLevel(pThis, hwnd, dwLevel); } -static MA_INLINE HRESULT ma_IDirectSound_Compact(ma_IDirectSound* pThis) { return pThis->lpVtbl->Compact(pThis); } -static MA_INLINE HRESULT ma_IDirectSound_GetSpeakerConfig(ma_IDirectSound* pThis, DWORD* pSpeakerConfig) { return pThis->lpVtbl->GetSpeakerConfig(pThis, pSpeakerConfig); } -static MA_INLINE HRESULT ma_IDirectSound_SetSpeakerConfig(ma_IDirectSound* pThis, DWORD dwSpeakerConfig) { return pThis->lpVtbl->SetSpeakerConfig(pThis, dwSpeakerConfig); } -static MA_INLINE HRESULT ma_IDirectSound_Initialize(ma_IDirectSound* pThis, const GUID* pGuidDevice) { return pThis->lpVtbl->Initialize(pThis, pGuidDevice); } - - -/* IDirectSoundBuffer */ -typedef struct -{ - /* IUnknown */ - HRESULT (STDMETHODCALLTYPE * QueryInterface)(ma_IDirectSoundBuffer* pThis, const IID* const riid, void** ppObject); - ULONG (STDMETHODCALLTYPE * AddRef) (ma_IDirectSoundBuffer* pThis); - ULONG (STDMETHODCALLTYPE * Release) (ma_IDirectSoundBuffer* pThis); - - /* IDirectSoundBuffer */ - HRESULT (STDMETHODCALLTYPE * GetCaps) (ma_IDirectSoundBuffer* pThis, MA_DSBCAPS* pDSBufferCaps); - HRESULT (STDMETHODCALLTYPE * GetCurrentPosition)(ma_IDirectSoundBuffer* pThis, DWORD* pCurrentPlayCursor, DWORD* pCurrentWriteCursor); - HRESULT (STDMETHODCALLTYPE * GetFormat) (ma_IDirectSoundBuffer* pThis, MA_WAVEFORMATEX* pFormat, DWORD dwSizeAllocated, DWORD* pSizeWritten); - HRESULT (STDMETHODCALLTYPE * GetVolume) (ma_IDirectSoundBuffer* pThis, LONG* pVolume); - HRESULT (STDMETHODCALLTYPE * GetPan) (ma_IDirectSoundBuffer* pThis, LONG* pPan); - HRESULT (STDMETHODCALLTYPE * GetFrequency) (ma_IDirectSoundBuffer* pThis, DWORD* pFrequency); - HRESULT (STDMETHODCALLTYPE * GetStatus) (ma_IDirectSoundBuffer* pThis, DWORD* pStatus); - HRESULT (STDMETHODCALLTYPE * Initialize) (ma_IDirectSoundBuffer* pThis, ma_IDirectSound* pDirectSound, const MA_DSBUFFERDESC* pDSBufferDesc); - HRESULT (STDMETHODCALLTYPE * Lock) (ma_IDirectSoundBuffer* pThis, DWORD dwOffset, DWORD dwBytes, void** ppAudioPtr1, DWORD* pAudioBytes1, void** ppAudioPtr2, DWORD* pAudioBytes2, DWORD dwFlags); - HRESULT (STDMETHODCALLTYPE * Play) (ma_IDirectSoundBuffer* pThis, DWORD dwReserved1, DWORD dwPriority, DWORD dwFlags); - HRESULT (STDMETHODCALLTYPE * SetCurrentPosition)(ma_IDirectSoundBuffer* pThis, DWORD dwNewPosition); - HRESULT (STDMETHODCALLTYPE * SetFormat) (ma_IDirectSoundBuffer* pThis, const MA_WAVEFORMATEX* pFormat); - HRESULT (STDMETHODCALLTYPE * SetVolume) (ma_IDirectSoundBuffer* pThis, LONG volume); - HRESULT (STDMETHODCALLTYPE * SetPan) (ma_IDirectSoundBuffer* pThis, LONG pan); - HRESULT (STDMETHODCALLTYPE * SetFrequency) (ma_IDirectSoundBuffer* pThis, DWORD dwFrequency); - HRESULT (STDMETHODCALLTYPE * Stop) (ma_IDirectSoundBuffer* pThis); - HRESULT (STDMETHODCALLTYPE * Unlock) (ma_IDirectSoundBuffer* pThis, void* pAudioPtr1, DWORD dwAudioBytes1, void* pAudioPtr2, DWORD dwAudioBytes2); - HRESULT (STDMETHODCALLTYPE * Restore) (ma_IDirectSoundBuffer* pThis); -} ma_IDirectSoundBufferVtbl; -struct ma_IDirectSoundBuffer -{ - ma_IDirectSoundBufferVtbl* lpVtbl; -}; -static MA_INLINE HRESULT ma_IDirectSoundBuffer_QueryInterface(ma_IDirectSoundBuffer* pThis, const IID* const riid, void** ppObject) { return pThis->lpVtbl->QueryInterface(pThis, riid, ppObject); } -static MA_INLINE ULONG ma_IDirectSoundBuffer_AddRef(ma_IDirectSoundBuffer* pThis) { return pThis->lpVtbl->AddRef(pThis); } -static MA_INLINE ULONG ma_IDirectSoundBuffer_Release(ma_IDirectSoundBuffer* pThis) { return pThis->lpVtbl->Release(pThis); } -static MA_INLINE HRESULT ma_IDirectSoundBuffer_GetCaps(ma_IDirectSoundBuffer* pThis, MA_DSBCAPS* pDSBufferCaps) { return pThis->lpVtbl->GetCaps(pThis, pDSBufferCaps); } -static MA_INLINE HRESULT ma_IDirectSoundBuffer_GetCurrentPosition(ma_IDirectSoundBuffer* pThis, DWORD* pCurrentPlayCursor, DWORD* pCurrentWriteCursor) { return pThis->lpVtbl->GetCurrentPosition(pThis, pCurrentPlayCursor, pCurrentWriteCursor); } -static MA_INLINE HRESULT ma_IDirectSoundBuffer_GetFormat(ma_IDirectSoundBuffer* pThis, MA_WAVEFORMATEX* pFormat, DWORD dwSizeAllocated, DWORD* pSizeWritten) { return pThis->lpVtbl->GetFormat(pThis, pFormat, dwSizeAllocated, pSizeWritten); } -static MA_INLINE HRESULT ma_IDirectSoundBuffer_GetVolume(ma_IDirectSoundBuffer* pThis, LONG* pVolume) { return pThis->lpVtbl->GetVolume(pThis, pVolume); } -static MA_INLINE HRESULT ma_IDirectSoundBuffer_GetPan(ma_IDirectSoundBuffer* pThis, LONG* pPan) { return pThis->lpVtbl->GetPan(pThis, pPan); } -static MA_INLINE HRESULT ma_IDirectSoundBuffer_GetFrequency(ma_IDirectSoundBuffer* pThis, DWORD* pFrequency) { return pThis->lpVtbl->GetFrequency(pThis, pFrequency); } -static MA_INLINE HRESULT ma_IDirectSoundBuffer_GetStatus(ma_IDirectSoundBuffer* pThis, DWORD* pStatus) { return pThis->lpVtbl->GetStatus(pThis, pStatus); } -static MA_INLINE HRESULT ma_IDirectSoundBuffer_Initialize(ma_IDirectSoundBuffer* pThis, ma_IDirectSound* pDirectSound, const MA_DSBUFFERDESC* pDSBufferDesc) { return pThis->lpVtbl->Initialize(pThis, pDirectSound, pDSBufferDesc); } -static MA_INLINE HRESULT ma_IDirectSoundBuffer_Lock(ma_IDirectSoundBuffer* pThis, DWORD dwOffset, DWORD dwBytes, void** ppAudioPtr1, DWORD* pAudioBytes1, void** ppAudioPtr2, DWORD* pAudioBytes2, DWORD dwFlags) { return pThis->lpVtbl->Lock(pThis, dwOffset, dwBytes, ppAudioPtr1, pAudioBytes1, ppAudioPtr2, pAudioBytes2, dwFlags); } -static MA_INLINE HRESULT ma_IDirectSoundBuffer_Play(ma_IDirectSoundBuffer* pThis, DWORD dwReserved1, DWORD dwPriority, DWORD dwFlags) { return pThis->lpVtbl->Play(pThis, dwReserved1, dwPriority, dwFlags); } -static MA_INLINE HRESULT ma_IDirectSoundBuffer_SetCurrentPosition(ma_IDirectSoundBuffer* pThis, DWORD dwNewPosition) { return pThis->lpVtbl->SetCurrentPosition(pThis, dwNewPosition); } -static MA_INLINE HRESULT ma_IDirectSoundBuffer_SetFormat(ma_IDirectSoundBuffer* pThis, const MA_WAVEFORMATEX* pFormat) { return pThis->lpVtbl->SetFormat(pThis, pFormat); } -static MA_INLINE HRESULT ma_IDirectSoundBuffer_SetVolume(ma_IDirectSoundBuffer* pThis, LONG volume) { return pThis->lpVtbl->SetVolume(pThis, volume); } -static MA_INLINE HRESULT ma_IDirectSoundBuffer_SetPan(ma_IDirectSoundBuffer* pThis, LONG pan) { return pThis->lpVtbl->SetPan(pThis, pan); } -static MA_INLINE HRESULT ma_IDirectSoundBuffer_SetFrequency(ma_IDirectSoundBuffer* pThis, DWORD dwFrequency) { return pThis->lpVtbl->SetFrequency(pThis, dwFrequency); } -static MA_INLINE HRESULT ma_IDirectSoundBuffer_Stop(ma_IDirectSoundBuffer* pThis) { return pThis->lpVtbl->Stop(pThis); } -static MA_INLINE HRESULT ma_IDirectSoundBuffer_Unlock(ma_IDirectSoundBuffer* pThis, void* pAudioPtr1, DWORD dwAudioBytes1, void* pAudioPtr2, DWORD dwAudioBytes2) { return pThis->lpVtbl->Unlock(pThis, pAudioPtr1, dwAudioBytes1, pAudioPtr2, dwAudioBytes2); } -static MA_INLINE HRESULT ma_IDirectSoundBuffer_Restore(ma_IDirectSoundBuffer* pThis) { return pThis->lpVtbl->Restore(pThis); } - - -/* IDirectSoundCapture */ -typedef struct -{ - /* IUnknown */ - HRESULT (STDMETHODCALLTYPE * QueryInterface)(ma_IDirectSoundCapture* pThis, const IID* const riid, void** ppObject); - ULONG (STDMETHODCALLTYPE * AddRef) (ma_IDirectSoundCapture* pThis); - ULONG (STDMETHODCALLTYPE * Release) (ma_IDirectSoundCapture* pThis); - - /* IDirectSoundCapture */ - HRESULT (STDMETHODCALLTYPE * CreateCaptureBuffer)(ma_IDirectSoundCapture* pThis, const MA_DSCBUFFERDESC* pDSCBufferDesc, ma_IDirectSoundCaptureBuffer** ppDSCBuffer, void* pUnkOuter); - HRESULT (STDMETHODCALLTYPE * GetCaps) (ma_IDirectSoundCapture* pThis, MA_DSCCAPS* pDSCCaps); - HRESULT (STDMETHODCALLTYPE * Initialize) (ma_IDirectSoundCapture* pThis, const GUID* pGuidDevice); -} ma_IDirectSoundCaptureVtbl; -struct ma_IDirectSoundCapture -{ - ma_IDirectSoundCaptureVtbl* lpVtbl; -}; -static MA_INLINE HRESULT ma_IDirectSoundCapture_QueryInterface (ma_IDirectSoundCapture* pThis, const IID* const riid, void** ppObject) { return pThis->lpVtbl->QueryInterface(pThis, riid, ppObject); } -static MA_INLINE ULONG ma_IDirectSoundCapture_AddRef (ma_IDirectSoundCapture* pThis) { return pThis->lpVtbl->AddRef(pThis); } -static MA_INLINE ULONG ma_IDirectSoundCapture_Release (ma_IDirectSoundCapture* pThis) { return pThis->lpVtbl->Release(pThis); } -static MA_INLINE HRESULT ma_IDirectSoundCapture_CreateCaptureBuffer(ma_IDirectSoundCapture* pThis, const MA_DSCBUFFERDESC* pDSCBufferDesc, ma_IDirectSoundCaptureBuffer** ppDSCBuffer, void* pUnkOuter) { return pThis->lpVtbl->CreateCaptureBuffer(pThis, pDSCBufferDesc, ppDSCBuffer, pUnkOuter); } -static MA_INLINE HRESULT ma_IDirectSoundCapture_GetCaps (ma_IDirectSoundCapture* pThis, MA_DSCCAPS* pDSCCaps) { return pThis->lpVtbl->GetCaps(pThis, pDSCCaps); } -static MA_INLINE HRESULT ma_IDirectSoundCapture_Initialize (ma_IDirectSoundCapture* pThis, const GUID* pGuidDevice) { return pThis->lpVtbl->Initialize(pThis, pGuidDevice); } - - -/* IDirectSoundCaptureBuffer */ -typedef struct -{ - /* IUnknown */ - HRESULT (STDMETHODCALLTYPE * QueryInterface)(ma_IDirectSoundCaptureBuffer* pThis, const IID* const riid, void** ppObject); - ULONG (STDMETHODCALLTYPE * AddRef) (ma_IDirectSoundCaptureBuffer* pThis); - ULONG (STDMETHODCALLTYPE * Release) (ma_IDirectSoundCaptureBuffer* pThis); - - /* IDirectSoundCaptureBuffer */ - HRESULT (STDMETHODCALLTYPE * GetCaps) (ma_IDirectSoundCaptureBuffer* pThis, MA_DSCBCAPS* pDSCBCaps); - HRESULT (STDMETHODCALLTYPE * GetCurrentPosition)(ma_IDirectSoundCaptureBuffer* pThis, DWORD* pCapturePosition, DWORD* pReadPosition); - HRESULT (STDMETHODCALLTYPE * GetFormat) (ma_IDirectSoundCaptureBuffer* pThis, MA_WAVEFORMATEX* pFormat, DWORD dwSizeAllocated, DWORD* pSizeWritten); - HRESULT (STDMETHODCALLTYPE * GetStatus) (ma_IDirectSoundCaptureBuffer* pThis, DWORD* pStatus); - HRESULT (STDMETHODCALLTYPE * Initialize) (ma_IDirectSoundCaptureBuffer* pThis, ma_IDirectSoundCapture* pDirectSoundCapture, const MA_DSCBUFFERDESC* pDSCBufferDesc); - HRESULT (STDMETHODCALLTYPE * Lock) (ma_IDirectSoundCaptureBuffer* pThis, DWORD dwOffset, DWORD dwBytes, void** ppAudioPtr1, DWORD* pAudioBytes1, void** ppAudioPtr2, DWORD* pAudioBytes2, DWORD dwFlags); - HRESULT (STDMETHODCALLTYPE * Start) (ma_IDirectSoundCaptureBuffer* pThis, DWORD dwFlags); - HRESULT (STDMETHODCALLTYPE * Stop) (ma_IDirectSoundCaptureBuffer* pThis); - HRESULT (STDMETHODCALLTYPE * Unlock) (ma_IDirectSoundCaptureBuffer* pThis, void* pAudioPtr1, DWORD dwAudioBytes1, void* pAudioPtr2, DWORD dwAudioBytes2); -} ma_IDirectSoundCaptureBufferVtbl; -struct ma_IDirectSoundCaptureBuffer -{ - ma_IDirectSoundCaptureBufferVtbl* lpVtbl; -}; -static MA_INLINE HRESULT ma_IDirectSoundCaptureBuffer_QueryInterface(ma_IDirectSoundCaptureBuffer* pThis, const IID* const riid, void** ppObject) { return pThis->lpVtbl->QueryInterface(pThis, riid, ppObject); } -static MA_INLINE ULONG ma_IDirectSoundCaptureBuffer_AddRef(ma_IDirectSoundCaptureBuffer* pThis) { return pThis->lpVtbl->AddRef(pThis); } -static MA_INLINE ULONG ma_IDirectSoundCaptureBuffer_Release(ma_IDirectSoundCaptureBuffer* pThis) { return pThis->lpVtbl->Release(pThis); } -static MA_INLINE HRESULT ma_IDirectSoundCaptureBuffer_GetCaps(ma_IDirectSoundCaptureBuffer* pThis, MA_DSCBCAPS* pDSCBCaps) { return pThis->lpVtbl->GetCaps(pThis, pDSCBCaps); } -static MA_INLINE HRESULT ma_IDirectSoundCaptureBuffer_GetCurrentPosition(ma_IDirectSoundCaptureBuffer* pThis, DWORD* pCapturePosition, DWORD* pReadPosition) { return pThis->lpVtbl->GetCurrentPosition(pThis, pCapturePosition, pReadPosition); } -static MA_INLINE HRESULT ma_IDirectSoundCaptureBuffer_GetFormat(ma_IDirectSoundCaptureBuffer* pThis, MA_WAVEFORMATEX* pFormat, DWORD dwSizeAllocated, DWORD* pSizeWritten) { return pThis->lpVtbl->GetFormat(pThis, pFormat, dwSizeAllocated, pSizeWritten); } -static MA_INLINE HRESULT ma_IDirectSoundCaptureBuffer_GetStatus(ma_IDirectSoundCaptureBuffer* pThis, DWORD* pStatus) { return pThis->lpVtbl->GetStatus(pThis, pStatus); } -static MA_INLINE HRESULT ma_IDirectSoundCaptureBuffer_Initialize(ma_IDirectSoundCaptureBuffer* pThis, ma_IDirectSoundCapture* pDirectSoundCapture, const MA_DSCBUFFERDESC* pDSCBufferDesc) { return pThis->lpVtbl->Initialize(pThis, pDirectSoundCapture, pDSCBufferDesc); } -static MA_INLINE HRESULT ma_IDirectSoundCaptureBuffer_Lock(ma_IDirectSoundCaptureBuffer* pThis, DWORD dwOffset, DWORD dwBytes, void** ppAudioPtr1, DWORD* pAudioBytes1, void** ppAudioPtr2, DWORD* pAudioBytes2, DWORD dwFlags) { return pThis->lpVtbl->Lock(pThis, dwOffset, dwBytes, ppAudioPtr1, pAudioBytes1, ppAudioPtr2, pAudioBytes2, dwFlags); } -static MA_INLINE HRESULT ma_IDirectSoundCaptureBuffer_Start(ma_IDirectSoundCaptureBuffer* pThis, DWORD dwFlags) { return pThis->lpVtbl->Start(pThis, dwFlags); } -static MA_INLINE HRESULT ma_IDirectSoundCaptureBuffer_Stop(ma_IDirectSoundCaptureBuffer* pThis) { return pThis->lpVtbl->Stop(pThis); } -static MA_INLINE HRESULT ma_IDirectSoundCaptureBuffer_Unlock(ma_IDirectSoundCaptureBuffer* pThis, void* pAudioPtr1, DWORD dwAudioBytes1, void* pAudioPtr2, DWORD dwAudioBytes2) { return pThis->lpVtbl->Unlock(pThis, pAudioPtr1, dwAudioBytes1, pAudioPtr2, dwAudioBytes2); } - - -/* IDirectSoundNotify */ -typedef struct -{ - /* IUnknown */ - HRESULT (STDMETHODCALLTYPE * QueryInterface)(ma_IDirectSoundNotify* pThis, const IID* const riid, void** ppObject); - ULONG (STDMETHODCALLTYPE * AddRef) (ma_IDirectSoundNotify* pThis); - ULONG (STDMETHODCALLTYPE * Release) (ma_IDirectSoundNotify* pThis); - - /* IDirectSoundNotify */ - HRESULT (STDMETHODCALLTYPE * SetNotificationPositions)(ma_IDirectSoundNotify* pThis, DWORD dwPositionNotifies, const MA_DSBPOSITIONNOTIFY* pPositionNotifies); -} ma_IDirectSoundNotifyVtbl; -struct ma_IDirectSoundNotify -{ - ma_IDirectSoundNotifyVtbl* lpVtbl; -}; -static MA_INLINE HRESULT ma_IDirectSoundNotify_QueryInterface(ma_IDirectSoundNotify* pThis, const IID* const riid, void** ppObject) { return pThis->lpVtbl->QueryInterface(pThis, riid, ppObject); } -static MA_INLINE ULONG ma_IDirectSoundNotify_AddRef(ma_IDirectSoundNotify* pThis) { return pThis->lpVtbl->AddRef(pThis); } -static MA_INLINE ULONG ma_IDirectSoundNotify_Release(ma_IDirectSoundNotify* pThis) { return pThis->lpVtbl->Release(pThis); } -static MA_INLINE HRESULT ma_IDirectSoundNotify_SetNotificationPositions(ma_IDirectSoundNotify* pThis, DWORD dwPositionNotifies, const MA_DSBPOSITIONNOTIFY* pPositionNotifies) { return pThis->lpVtbl->SetNotificationPositions(pThis, dwPositionNotifies, pPositionNotifies); } - - -typedef BOOL (CALLBACK * ma_DSEnumCallbackAProc) (GUID* pDeviceGUID, const char* pDeviceDescription, const char* pModule, void* pContext); -typedef HRESULT (WINAPI * ma_DirectSoundCreateProc) (const GUID* pcGuidDevice, ma_IDirectSound** ppDS8, ma_IUnknown* pUnkOuter); -typedef HRESULT (WINAPI * ma_DirectSoundEnumerateAProc) (ma_DSEnumCallbackAProc pDSEnumCallback, void* pContext); -typedef HRESULT (WINAPI * ma_DirectSoundCaptureCreateProc) (const GUID* pcGuidDevice, ma_IDirectSoundCapture** ppDSC8, ma_IUnknown* pUnkOuter); -typedef HRESULT (WINAPI * ma_DirectSoundCaptureEnumerateAProc)(ma_DSEnumCallbackAProc pDSEnumCallback, void* pContext); - -static ma_uint32 ma_get_best_sample_rate_within_range(ma_uint32 sampleRateMin, ma_uint32 sampleRateMax) -{ - /* Normalize the range in case we were given something stupid. */ - if (sampleRateMin < (ma_uint32)ma_standard_sample_rate_min) { - sampleRateMin = (ma_uint32)ma_standard_sample_rate_min; - } - if (sampleRateMax > (ma_uint32)ma_standard_sample_rate_max) { - sampleRateMax = (ma_uint32)ma_standard_sample_rate_max; - } - if (sampleRateMin > sampleRateMax) { - sampleRateMin = sampleRateMax; - } - - if (sampleRateMin == sampleRateMax) { - return sampleRateMax; - } else { - size_t iStandardRate; - for (iStandardRate = 0; iStandardRate < ma_countof(g_maStandardSampleRatePriorities); ++iStandardRate) { - ma_uint32 standardRate = g_maStandardSampleRatePriorities[iStandardRate]; - if (standardRate >= sampleRateMin && standardRate <= sampleRateMax) { - return standardRate; - } - } - } - - /* Should never get here. */ - MA_ASSERT(MA_FALSE); - return 0; -} - -/* -Retrieves the channel count and channel map for the given speaker configuration. If the speaker configuration is unknown, -the channel count and channel map will be left unmodified. -*/ -static void ma_get_channels_from_speaker_config__dsound(DWORD speakerConfig, WORD* pChannelsOut, DWORD* pChannelMapOut) -{ - WORD channels; - DWORD channelMap; - - channels = 0; - if (pChannelsOut != NULL) { - channels = *pChannelsOut; - } - - channelMap = 0; - if (pChannelMapOut != NULL) { - channelMap = *pChannelMapOut; - } - - /* - The speaker configuration is a combination of speaker config and speaker geometry. The lower 8 bits is what we care about. The upper - 16 bits is for the geometry. - */ - switch ((BYTE)(speakerConfig)) { - case 1 /*DSSPEAKER_HEADPHONE*/: channels = 2; channelMap = SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT; break; - case 2 /*DSSPEAKER_MONO*/: channels = 1; channelMap = SPEAKER_FRONT_CENTER; break; - case 3 /*DSSPEAKER_QUAD*/: channels = 4; channelMap = SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT | SPEAKER_BACK_LEFT | SPEAKER_BACK_RIGHT; break; - case 4 /*DSSPEAKER_STEREO*/: channels = 2; channelMap = SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT; break; - case 5 /*DSSPEAKER_SURROUND*/: channels = 4; channelMap = SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT | SPEAKER_FRONT_CENTER | SPEAKER_BACK_CENTER; break; - case 6 /*DSSPEAKER_5POINT1_BACK*/ /*DSSPEAKER_5POINT1*/: channels = 6; channelMap = SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT | SPEAKER_FRONT_CENTER | SPEAKER_LOW_FREQUENCY | SPEAKER_BACK_LEFT | SPEAKER_BACK_RIGHT; break; - case 7 /*DSSPEAKER_7POINT1_WIDE*/ /*DSSPEAKER_7POINT1*/: channels = 8; channelMap = SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT | SPEAKER_FRONT_CENTER | SPEAKER_LOW_FREQUENCY | SPEAKER_BACK_LEFT | SPEAKER_BACK_RIGHT | SPEAKER_FRONT_LEFT_OF_CENTER | SPEAKER_FRONT_RIGHT_OF_CENTER; break; - case 8 /*DSSPEAKER_7POINT1_SURROUND*/: channels = 8; channelMap = SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT | SPEAKER_FRONT_CENTER | SPEAKER_LOW_FREQUENCY | SPEAKER_BACK_LEFT | SPEAKER_BACK_RIGHT | SPEAKER_SIDE_LEFT | SPEAKER_SIDE_RIGHT; break; - case 9 /*DSSPEAKER_5POINT1_SURROUND*/: channels = 6; channelMap = SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT | SPEAKER_FRONT_CENTER | SPEAKER_LOW_FREQUENCY | SPEAKER_SIDE_LEFT | SPEAKER_SIDE_RIGHT; break; - default: break; - } - - if (pChannelsOut != NULL) { - *pChannelsOut = channels; - } - - if (pChannelMapOut != NULL) { - *pChannelMapOut = channelMap; - } -} - - -static ma_result ma_context_create_IDirectSound__dsound(ma_context* pContext, ma_share_mode shareMode, const ma_device_id* pDeviceID, ma_IDirectSound** ppDirectSound) -{ - ma_IDirectSound* pDirectSound; - HWND hWnd; - HRESULT hr; - - MA_ASSERT(pContext != NULL); - MA_ASSERT(ppDirectSound != NULL); - - *ppDirectSound = NULL; - pDirectSound = NULL; - - if (FAILED(((ma_DirectSoundCreateProc)pContext->dsound.DirectSoundCreate)((pDeviceID == NULL) ? NULL : (const GUID*)pDeviceID->dsound, &pDirectSound, NULL))) { - ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[DirectSound] DirectSoundCreate() failed for playback device."); - return MA_FAILED_TO_OPEN_BACKEND_DEVICE; - } - - /* The cooperative level must be set before doing anything else. */ - hWnd = (HWND)pContext->dsound.hWnd; - if (hWnd == 0) { - hWnd = ((MA_PFN_GetForegroundWindow)pContext->win32.GetForegroundWindow)(); - if (hWnd == 0) { - hWnd = ((MA_PFN_GetDesktopWindow)pContext->win32.GetDesktopWindow)(); - } - } - - hr = ma_IDirectSound_SetCooperativeLevel(pDirectSound, hWnd, (shareMode == ma_share_mode_exclusive) ? MA_DSSCL_EXCLUSIVE : MA_DSSCL_PRIORITY); - if (FAILED(hr)) { - ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[DirectSound] IDirectSound_SetCooperateiveLevel() failed for playback device."); - return ma_result_from_HRESULT(hr); - } - - *ppDirectSound = pDirectSound; - return MA_SUCCESS; -} - -static ma_result ma_context_create_IDirectSoundCapture__dsound(ma_context* pContext, ma_share_mode shareMode, const ma_device_id* pDeviceID, ma_IDirectSoundCapture** ppDirectSoundCapture) -{ - ma_IDirectSoundCapture* pDirectSoundCapture; - HRESULT hr; - - MA_ASSERT(pContext != NULL); - MA_ASSERT(ppDirectSoundCapture != NULL); - - /* DirectSound does not support exclusive mode for capture. */ - if (shareMode == ma_share_mode_exclusive) { - return MA_SHARE_MODE_NOT_SUPPORTED; - } - - *ppDirectSoundCapture = NULL; - pDirectSoundCapture = NULL; - - hr = ((ma_DirectSoundCaptureCreateProc)pContext->dsound.DirectSoundCaptureCreate)((pDeviceID == NULL) ? NULL : (const GUID*)pDeviceID->dsound, &pDirectSoundCapture, NULL); - if (FAILED(hr)) { - ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[DirectSound] DirectSoundCaptureCreate() failed for capture device."); - return ma_result_from_HRESULT(hr); - } - - *ppDirectSoundCapture = pDirectSoundCapture; - return MA_SUCCESS; -} - -static ma_result ma_context_get_format_info_for_IDirectSoundCapture__dsound(ma_context* pContext, ma_IDirectSoundCapture* pDirectSoundCapture, WORD* pChannels, WORD* pBitsPerSample, DWORD* pSampleRate) -{ - HRESULT hr; - MA_DSCCAPS caps; - WORD bitsPerSample; - DWORD sampleRate; - - MA_ASSERT(pContext != NULL); - MA_ASSERT(pDirectSoundCapture != NULL); - - if (pChannels) { - *pChannels = 0; - } - if (pBitsPerSample) { - *pBitsPerSample = 0; - } - if (pSampleRate) { - *pSampleRate = 0; - } - - MA_ZERO_OBJECT(&caps); - caps.dwSize = sizeof(caps); - hr = ma_IDirectSoundCapture_GetCaps(pDirectSoundCapture, &caps); - if (FAILED(hr)) { - ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[DirectSound] IDirectSoundCapture_GetCaps() failed for capture device."); - return ma_result_from_HRESULT(hr); - } - - if (pChannels) { - *pChannels = (WORD)caps.dwChannels; - } - - /* The device can support multiple formats. We just go through the different formats in order of priority and pick the first one. This the same type of system as the WinMM backend. */ - bitsPerSample = 16; - sampleRate = 48000; - - if (caps.dwChannels == 1) { - if ((caps.dwFormats & WAVE_FORMAT_48M16) != 0) { - sampleRate = 48000; - } else if ((caps.dwFormats & WAVE_FORMAT_44M16) != 0) { - sampleRate = 44100; - } else if ((caps.dwFormats & WAVE_FORMAT_2M16) != 0) { - sampleRate = 22050; - } else if ((caps.dwFormats & WAVE_FORMAT_1M16) != 0) { - sampleRate = 11025; - } else if ((caps.dwFormats & WAVE_FORMAT_96M16) != 0) { - sampleRate = 96000; - } else { - bitsPerSample = 8; - if ((caps.dwFormats & WAVE_FORMAT_48M08) != 0) { - sampleRate = 48000; - } else if ((caps.dwFormats & WAVE_FORMAT_44M08) != 0) { - sampleRate = 44100; - } else if ((caps.dwFormats & WAVE_FORMAT_2M08) != 0) { - sampleRate = 22050; - } else if ((caps.dwFormats & WAVE_FORMAT_1M08) != 0) { - sampleRate = 11025; - } else if ((caps.dwFormats & WAVE_FORMAT_96M08) != 0) { - sampleRate = 96000; - } else { - bitsPerSample = 16; /* Didn't find it. Just fall back to 16-bit. */ - } - } - } else if (caps.dwChannels == 2) { - if ((caps.dwFormats & WAVE_FORMAT_48S16) != 0) { - sampleRate = 48000; - } else if ((caps.dwFormats & WAVE_FORMAT_44S16) != 0) { - sampleRate = 44100; - } else if ((caps.dwFormats & WAVE_FORMAT_2S16) != 0) { - sampleRate = 22050; - } else if ((caps.dwFormats & WAVE_FORMAT_1S16) != 0) { - sampleRate = 11025; - } else if ((caps.dwFormats & WAVE_FORMAT_96S16) != 0) { - sampleRate = 96000; - } else { - bitsPerSample = 8; - if ((caps.dwFormats & WAVE_FORMAT_48S08) != 0) { - sampleRate = 48000; - } else if ((caps.dwFormats & WAVE_FORMAT_44S08) != 0) { - sampleRate = 44100; - } else if ((caps.dwFormats & WAVE_FORMAT_2S08) != 0) { - sampleRate = 22050; - } else if ((caps.dwFormats & WAVE_FORMAT_1S08) != 0) { - sampleRate = 11025; - } else if ((caps.dwFormats & WAVE_FORMAT_96S08) != 0) { - sampleRate = 96000; - } else { - bitsPerSample = 16; /* Didn't find it. Just fall back to 16-bit. */ - } - } - } - - if (pBitsPerSample) { - *pBitsPerSample = bitsPerSample; - } - if (pSampleRate) { - *pSampleRate = sampleRate; - } - - return MA_SUCCESS; -} - - -typedef struct -{ - ma_context* pContext; - ma_device_type deviceType; - ma_enum_devices_callback_proc callback; - void* pUserData; - ma_bool32 terminated; -} ma_context_enumerate_devices_callback_data__dsound; - -static BOOL CALLBACK ma_context_enumerate_devices_callback__dsound(GUID* lpGuid, const char* lpcstrDescription, const char* lpcstrModule, void* lpContext) -{ - ma_context_enumerate_devices_callback_data__dsound* pData = (ma_context_enumerate_devices_callback_data__dsound*)lpContext; - ma_device_info deviceInfo; - - (void)lpcstrModule; - - MA_ZERO_OBJECT(&deviceInfo); - - /* ID. */ - if (lpGuid != NULL) { - MA_COPY_MEMORY(deviceInfo.id.dsound, lpGuid, 16); - } else { - MA_ZERO_MEMORY(deviceInfo.id.dsound, 16); - deviceInfo.isDefault = MA_TRUE; - } - - /* Name / Description */ - ma_strncpy_s(deviceInfo.name, sizeof(deviceInfo.name), lpcstrDescription, (size_t)-1); - - - /* Call the callback function, but make sure we stop enumerating if the callee requested so. */ - MA_ASSERT(pData != NULL); - pData->terminated = (pData->callback(pData->pContext, pData->deviceType, &deviceInfo, pData->pUserData) == MA_FALSE); - if (pData->terminated) { - return FALSE; /* Stop enumeration. */ - } else { - return TRUE; /* Continue enumeration. */ - } -} - -static ma_result ma_context_enumerate_devices__dsound(ma_context* pContext, ma_enum_devices_callback_proc callback, void* pUserData) -{ - ma_context_enumerate_devices_callback_data__dsound data; - - MA_ASSERT(pContext != NULL); - MA_ASSERT(callback != NULL); - - data.pContext = pContext; - data.callback = callback; - data.pUserData = pUserData; - data.terminated = MA_FALSE; - - /* Playback. */ - if (!data.terminated) { - data.deviceType = ma_device_type_playback; - ((ma_DirectSoundEnumerateAProc)pContext->dsound.DirectSoundEnumerateA)(ma_context_enumerate_devices_callback__dsound, &data); - } - - /* Capture. */ - if (!data.terminated) { - data.deviceType = ma_device_type_capture; - ((ma_DirectSoundCaptureEnumerateAProc)pContext->dsound.DirectSoundCaptureEnumerateA)(ma_context_enumerate_devices_callback__dsound, &data); - } - - return MA_SUCCESS; -} - - -typedef struct -{ - const ma_device_id* pDeviceID; - ma_device_info* pDeviceInfo; - ma_bool32 found; -} ma_context_get_device_info_callback_data__dsound; - -static BOOL CALLBACK ma_context_get_device_info_callback__dsound(GUID* lpGuid, const char* lpcstrDescription, const char* lpcstrModule, void* lpContext) -{ - ma_context_get_device_info_callback_data__dsound* pData = (ma_context_get_device_info_callback_data__dsound*)lpContext; - MA_ASSERT(pData != NULL); - - if ((pData->pDeviceID == NULL || ma_is_guid_null(pData->pDeviceID->dsound)) && (lpGuid == NULL || ma_is_guid_null(lpGuid))) { - /* Default device. */ - ma_strncpy_s(pData->pDeviceInfo->name, sizeof(pData->pDeviceInfo->name), lpcstrDescription, (size_t)-1); - pData->pDeviceInfo->isDefault = MA_TRUE; - pData->found = MA_TRUE; - return FALSE; /* Stop enumeration. */ - } else { - /* Not the default device. */ - if (lpGuid != NULL && pData->pDeviceID != NULL) { - if (memcmp(pData->pDeviceID->dsound, lpGuid, sizeof(pData->pDeviceID->dsound)) == 0) { - ma_strncpy_s(pData->pDeviceInfo->name, sizeof(pData->pDeviceInfo->name), lpcstrDescription, (size_t)-1); - pData->found = MA_TRUE; - return FALSE; /* Stop enumeration. */ - } - } - } - - (void)lpcstrModule; - return TRUE; -} - -static ma_result ma_context_get_device_info__dsound(ma_context* pContext, ma_device_type deviceType, const ma_device_id* pDeviceID, ma_device_info* pDeviceInfo) -{ - ma_result result; - HRESULT hr; - - if (pDeviceID != NULL) { - ma_context_get_device_info_callback_data__dsound data; - - /* ID. */ - MA_COPY_MEMORY(pDeviceInfo->id.dsound, pDeviceID->dsound, 16); - - /* Name / Description. This is retrieved by enumerating over each device until we find that one that matches the input ID. */ - data.pDeviceID = pDeviceID; - data.pDeviceInfo = pDeviceInfo; - data.found = MA_FALSE; - if (deviceType == ma_device_type_playback) { - ((ma_DirectSoundEnumerateAProc)pContext->dsound.DirectSoundEnumerateA)(ma_context_get_device_info_callback__dsound, &data); - } else { - ((ma_DirectSoundCaptureEnumerateAProc)pContext->dsound.DirectSoundCaptureEnumerateA)(ma_context_get_device_info_callback__dsound, &data); - } - - if (!data.found) { - return MA_NO_DEVICE; - } - } else { - /* I don't think there's a way to get the name of the default device with DirectSound. In this case we just need to use defaults. */ - - /* ID */ - MA_ZERO_MEMORY(pDeviceInfo->id.dsound, 16); - - /* Name / Description */ - if (deviceType == ma_device_type_playback) { - ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), MA_DEFAULT_PLAYBACK_DEVICE_NAME, (size_t)-1); - } else { - ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), MA_DEFAULT_CAPTURE_DEVICE_NAME, (size_t)-1); - } - - pDeviceInfo->isDefault = MA_TRUE; - } - - /* Retrieving detailed information is slightly different depending on the device type. */ - if (deviceType == ma_device_type_playback) { - /* Playback. */ - ma_IDirectSound* pDirectSound; - MA_DSCAPS caps; - WORD channels; - - result = ma_context_create_IDirectSound__dsound(pContext, ma_share_mode_shared, pDeviceID, &pDirectSound); - if (result != MA_SUCCESS) { - return result; - } - - MA_ZERO_OBJECT(&caps); - caps.dwSize = sizeof(caps); - hr = ma_IDirectSound_GetCaps(pDirectSound, &caps); - if (FAILED(hr)) { - ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[DirectSound] IDirectSound_GetCaps() failed for playback device."); - return ma_result_from_HRESULT(hr); - } - - - /* Channels. Only a single channel count is reported for DirectSound. */ - if ((caps.dwFlags & MA_DSCAPS_PRIMARYSTEREO) != 0) { - /* It supports at least stereo, but could support more. */ - DWORD speakerConfig; - - channels = 2; - - /* Look at the speaker configuration to get a better idea on the channel count. */ - hr = ma_IDirectSound_GetSpeakerConfig(pDirectSound, &speakerConfig); - if (SUCCEEDED(hr)) { - ma_get_channels_from_speaker_config__dsound(speakerConfig, &channels, NULL); - } - } else { - /* It does not support stereo, which means we are stuck with mono. */ - channels = 1; - } - - - /* - In DirectSound, our native formats are centered around sample rates. All formats are supported, and we're only reporting a single channel - count. However, DirectSound can report a range of supported sample rates. We're only going to include standard rates known by miniaudio - in order to keep the size of this within reason. - */ - if ((caps.dwFlags & MA_DSCAPS_CONTINUOUSRATE) != 0) { - /* Multiple sample rates are supported. We'll report in order of our preferred sample rates. */ - size_t iStandardSampleRate; - for (iStandardSampleRate = 0; iStandardSampleRate < ma_countof(g_maStandardSampleRatePriorities); iStandardSampleRate += 1) { - ma_uint32 sampleRate = g_maStandardSampleRatePriorities[iStandardSampleRate]; - if (sampleRate >= caps.dwMinSecondarySampleRate && sampleRate <= caps.dwMaxSecondarySampleRate) { - pDeviceInfo->nativeDataFormats[pDeviceInfo->nativeDataFormatCount].format = ma_format_unknown; - pDeviceInfo->nativeDataFormats[pDeviceInfo->nativeDataFormatCount].channels = channels; - pDeviceInfo->nativeDataFormats[pDeviceInfo->nativeDataFormatCount].sampleRate = sampleRate; - pDeviceInfo->nativeDataFormats[pDeviceInfo->nativeDataFormatCount].flags = 0; - pDeviceInfo->nativeDataFormatCount += 1; - } - } - } else { - /* Only a single sample rate is supported. */ - pDeviceInfo->nativeDataFormats[pDeviceInfo->nativeDataFormatCount].format = ma_format_unknown; - pDeviceInfo->nativeDataFormats[pDeviceInfo->nativeDataFormatCount].channels = channels; - pDeviceInfo->nativeDataFormats[pDeviceInfo->nativeDataFormatCount].sampleRate = caps.dwMaxSecondarySampleRate; - pDeviceInfo->nativeDataFormats[pDeviceInfo->nativeDataFormatCount].flags = 0; - pDeviceInfo->nativeDataFormatCount += 1; - } - - ma_IDirectSound_Release(pDirectSound); - } else { - /* - Capture. This is a little different to playback due to the say the supported formats are reported. Technically capture - devices can support a number of different formats, but for simplicity and consistency with ma_device_init() I'm just - reporting the best format. - */ - ma_IDirectSoundCapture* pDirectSoundCapture; - WORD channels; - WORD bitsPerSample; - DWORD sampleRate; - - result = ma_context_create_IDirectSoundCapture__dsound(pContext, ma_share_mode_shared, pDeviceID, &pDirectSoundCapture); - if (result != MA_SUCCESS) { - return result; - } - - result = ma_context_get_format_info_for_IDirectSoundCapture__dsound(pContext, pDirectSoundCapture, &channels, &bitsPerSample, &sampleRate); - if (result != MA_SUCCESS) { - ma_IDirectSoundCapture_Release(pDirectSoundCapture); - return result; - } - - ma_IDirectSoundCapture_Release(pDirectSoundCapture); - - /* The format is always an integer format and is based on the bits per sample. */ - if (bitsPerSample == 8) { - pDeviceInfo->nativeDataFormats[0].format = ma_format_u8; - } else if (bitsPerSample == 16) { - pDeviceInfo->nativeDataFormats[0].format = ma_format_s16; - } else if (bitsPerSample == 24) { - pDeviceInfo->nativeDataFormats[0].format = ma_format_s24; - } else if (bitsPerSample == 32) { - pDeviceInfo->nativeDataFormats[0].format = ma_format_s32; - } else { - return MA_FORMAT_NOT_SUPPORTED; - } - - pDeviceInfo->nativeDataFormats[0].channels = channels; - pDeviceInfo->nativeDataFormats[0].sampleRate = sampleRate; - pDeviceInfo->nativeDataFormats[0].flags = 0; - pDeviceInfo->nativeDataFormatCount = 1; - } - - return MA_SUCCESS; -} - - - -static ma_result ma_device_uninit__dsound(ma_device* pDevice) -{ - MA_ASSERT(pDevice != NULL); - - if (pDevice->dsound.pCaptureBuffer != NULL) { - ma_IDirectSoundCaptureBuffer_Release((ma_IDirectSoundCaptureBuffer*)pDevice->dsound.pCaptureBuffer); - } - if (pDevice->dsound.pCapture != NULL) { - ma_IDirectSoundCapture_Release((ma_IDirectSoundCapture*)pDevice->dsound.pCapture); - } - - if (pDevice->dsound.pPlaybackBuffer != NULL) { - ma_IDirectSoundBuffer_Release((ma_IDirectSoundBuffer*)pDevice->dsound.pPlaybackBuffer); - } - if (pDevice->dsound.pPlaybackPrimaryBuffer != NULL) { - ma_IDirectSoundBuffer_Release((ma_IDirectSoundBuffer*)pDevice->dsound.pPlaybackPrimaryBuffer); - } - if (pDevice->dsound.pPlayback != NULL) { - ma_IDirectSound_Release((ma_IDirectSound*)pDevice->dsound.pPlayback); - } - - return MA_SUCCESS; -} - -static ma_result ma_config_to_WAVEFORMATEXTENSIBLE(ma_format format, ma_uint32 channels, ma_uint32 sampleRate, const ma_channel* pChannelMap, MA_WAVEFORMATEXTENSIBLE* pWF) -{ - GUID subformat; - - if (format == ma_format_unknown) { - format = MA_DEFAULT_FORMAT; - } - - if (channels == 0) { - channels = MA_DEFAULT_CHANNELS; - } - - if (sampleRate == 0) { - sampleRate = MA_DEFAULT_SAMPLE_RATE; - } - - switch (format) - { - case ma_format_u8: - case ma_format_s16: - case ma_format_s24: - /*case ma_format_s24_32:*/ - case ma_format_s32: - { - subformat = MA_GUID_KSDATAFORMAT_SUBTYPE_PCM; - } break; - - case ma_format_f32: - { - subformat = MA_GUID_KSDATAFORMAT_SUBTYPE_IEEE_FLOAT; - } break; - - default: - return MA_FORMAT_NOT_SUPPORTED; - } - - MA_ZERO_OBJECT(pWF); - pWF->cbSize = sizeof(*pWF); - pWF->wFormatTag = WAVE_FORMAT_EXTENSIBLE; - pWF->nChannels = (WORD)channels; - pWF->nSamplesPerSec = (DWORD)sampleRate; - pWF->wBitsPerSample = (WORD)(ma_get_bytes_per_sample(format)*8); - pWF->nBlockAlign = (WORD)(pWF->nChannels * pWF->wBitsPerSample / 8); - pWF->nAvgBytesPerSec = pWF->nBlockAlign * pWF->nSamplesPerSec; - pWF->Samples.wValidBitsPerSample = pWF->wBitsPerSample; - pWF->dwChannelMask = ma_channel_map_to_channel_mask__win32(pChannelMap, channels); - pWF->SubFormat = subformat; - - return MA_SUCCESS; -} - -static ma_uint32 ma_calculate_period_size_in_frames_from_descriptor__dsound(const ma_device_descriptor* pDescriptor, ma_uint32 nativeSampleRate, ma_performance_profile performanceProfile) -{ - /* - DirectSound has a minimum period size of 20ms. In practice, this doesn't seem to be enough for - reliable glitch-free processing so going to use 30ms instead. - */ - ma_uint32 minPeriodSizeInFrames = ma_calculate_buffer_size_in_frames_from_milliseconds(30, nativeSampleRate); - ma_uint32 periodSizeInFrames; - - periodSizeInFrames = ma_calculate_buffer_size_in_frames_from_descriptor(pDescriptor, nativeSampleRate, performanceProfile); - if (periodSizeInFrames < minPeriodSizeInFrames) { - periodSizeInFrames = minPeriodSizeInFrames; - } - - return periodSizeInFrames; -} - -static ma_result ma_device_init__dsound(ma_device* pDevice, const ma_device_config* pConfig, ma_device_descriptor* pDescriptorPlayback, ma_device_descriptor* pDescriptorCapture) -{ - ma_result result; - HRESULT hr; - - MA_ASSERT(pDevice != NULL); - - MA_ZERO_OBJECT(&pDevice->dsound); - - if (pConfig->deviceType == ma_device_type_loopback) { - return MA_DEVICE_TYPE_NOT_SUPPORTED; - } - - /* - Unfortunately DirectSound uses different APIs and data structures for playback and capture devices. We need to initialize - the capture device first because we'll want to match its buffer size and period count on the playback side if we're using - full-duplex mode. - */ - if (pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) { - MA_WAVEFORMATEXTENSIBLE wf; - MA_DSCBUFFERDESC descDS; - ma_uint32 periodSizeInFrames; - ma_uint32 periodCount; - char rawdata[1024]; /* <-- Ugly hack to avoid a malloc() due to a crappy DirectSound API. */ - MA_WAVEFORMATEXTENSIBLE* pActualFormat; - - result = ma_config_to_WAVEFORMATEXTENSIBLE(pDescriptorCapture->format, pDescriptorCapture->channels, pDescriptorCapture->sampleRate, pDescriptorCapture->channelMap, &wf); - if (result != MA_SUCCESS) { - return result; - } - - result = ma_context_create_IDirectSoundCapture__dsound(pDevice->pContext, pDescriptorCapture->shareMode, pDescriptorCapture->pDeviceID, (ma_IDirectSoundCapture**)&pDevice->dsound.pCapture); - if (result != MA_SUCCESS) { - ma_device_uninit__dsound(pDevice); - return result; - } - - result = ma_context_get_format_info_for_IDirectSoundCapture__dsound(pDevice->pContext, (ma_IDirectSoundCapture*)pDevice->dsound.pCapture, &wf.nChannels, &wf.wBitsPerSample, &wf.nSamplesPerSec); - if (result != MA_SUCCESS) { - ma_device_uninit__dsound(pDevice); - return result; - } - - wf.nBlockAlign = (WORD)(wf.nChannels * wf.wBitsPerSample / 8); - wf.nAvgBytesPerSec = wf.nBlockAlign * wf.nSamplesPerSec; - wf.Samples.wValidBitsPerSample = wf.wBitsPerSample; - wf.SubFormat = MA_GUID_KSDATAFORMAT_SUBTYPE_PCM; - - /* The size of the buffer must be a clean multiple of the period count. */ - periodSizeInFrames = ma_calculate_period_size_in_frames_from_descriptor__dsound(pDescriptorCapture, wf.nSamplesPerSec, pConfig->performanceProfile); - periodCount = (pDescriptorCapture->periodCount > 0) ? pDescriptorCapture->periodCount : MA_DEFAULT_PERIODS; - - MA_ZERO_OBJECT(&descDS); - descDS.dwSize = sizeof(descDS); - descDS.dwFlags = 0; - descDS.dwBufferBytes = periodSizeInFrames * periodCount * wf.nBlockAlign; - descDS.lpwfxFormat = (MA_WAVEFORMATEX*)&wf; - hr = ma_IDirectSoundCapture_CreateCaptureBuffer((ma_IDirectSoundCapture*)pDevice->dsound.pCapture, &descDS, (ma_IDirectSoundCaptureBuffer**)&pDevice->dsound.pCaptureBuffer, NULL); - if (FAILED(hr)) { - ma_device_uninit__dsound(pDevice); - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[DirectSound] IDirectSoundCapture_CreateCaptureBuffer() failed for capture device."); - return ma_result_from_HRESULT(hr); - } - - /* Get the _actual_ properties of the buffer. */ - pActualFormat = (MA_WAVEFORMATEXTENSIBLE*)rawdata; - hr = ma_IDirectSoundCaptureBuffer_GetFormat((ma_IDirectSoundCaptureBuffer*)pDevice->dsound.pCaptureBuffer, (MA_WAVEFORMATEX*)pActualFormat, sizeof(rawdata), NULL); - if (FAILED(hr)) { - ma_device_uninit__dsound(pDevice); - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[DirectSound] Failed to retrieve the actual format of the capture device's buffer."); - return ma_result_from_HRESULT(hr); - } - - /* We can now start setting the output data formats. */ - pDescriptorCapture->format = ma_format_from_WAVEFORMATEX((MA_WAVEFORMATEX*)pActualFormat); - pDescriptorCapture->channels = pActualFormat->nChannels; - pDescriptorCapture->sampleRate = pActualFormat->nSamplesPerSec; - - /* Get the native channel map based on the channel mask. */ - if (pActualFormat->wFormatTag == WAVE_FORMAT_EXTENSIBLE) { - ma_channel_mask_to_channel_map__win32(pActualFormat->dwChannelMask, pDescriptorCapture->channels, pDescriptorCapture->channelMap); - } else { - ma_channel_mask_to_channel_map__win32(wf.dwChannelMask, pDescriptorCapture->channels, pDescriptorCapture->channelMap); - } - - /* - After getting the actual format the size of the buffer in frames may have actually changed. However, we want this to be as close to what the - user has asked for as possible, so let's go ahead and release the old capture buffer and create a new one in this case. - */ - if (periodSizeInFrames != (descDS.dwBufferBytes / ma_get_bytes_per_frame(pDescriptorCapture->format, pDescriptorCapture->channels) / periodCount)) { - descDS.dwBufferBytes = periodSizeInFrames * ma_get_bytes_per_frame(pDescriptorCapture->format, pDescriptorCapture->channels) * periodCount; - ma_IDirectSoundCaptureBuffer_Release((ma_IDirectSoundCaptureBuffer*)pDevice->dsound.pCaptureBuffer); - - hr = ma_IDirectSoundCapture_CreateCaptureBuffer((ma_IDirectSoundCapture*)pDevice->dsound.pCapture, &descDS, (ma_IDirectSoundCaptureBuffer**)&pDevice->dsound.pCaptureBuffer, NULL); - if (FAILED(hr)) { - ma_device_uninit__dsound(pDevice); - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[DirectSound] Second attempt at IDirectSoundCapture_CreateCaptureBuffer() failed for capture device."); - return ma_result_from_HRESULT(hr); - } - } - - /* DirectSound should give us a buffer exactly the size we asked for. */ - pDescriptorCapture->periodSizeInFrames = periodSizeInFrames; - pDescriptorCapture->periodCount = periodCount; - } - - if (pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) { - MA_WAVEFORMATEXTENSIBLE wf; - MA_DSBUFFERDESC descDSPrimary; - MA_DSCAPS caps; - char rawdata[1024]; /* <-- Ugly hack to avoid a malloc() due to a crappy DirectSound API. */ - MA_WAVEFORMATEXTENSIBLE* pActualFormat; - ma_uint32 periodSizeInFrames; - ma_uint32 periodCount; - MA_DSBUFFERDESC descDS; - WORD nativeChannelCount; - DWORD nativeChannelMask = 0; - - result = ma_config_to_WAVEFORMATEXTENSIBLE(pDescriptorPlayback->format, pDescriptorPlayback->channels, pDescriptorPlayback->sampleRate, pDescriptorPlayback->channelMap, &wf); - if (result != MA_SUCCESS) { - return result; - } - - result = ma_context_create_IDirectSound__dsound(pDevice->pContext, pDescriptorPlayback->shareMode, pDescriptorPlayback->pDeviceID, (ma_IDirectSound**)&pDevice->dsound.pPlayback); - if (result != MA_SUCCESS) { - ma_device_uninit__dsound(pDevice); - return result; - } - - MA_ZERO_OBJECT(&descDSPrimary); - descDSPrimary.dwSize = sizeof(MA_DSBUFFERDESC); - descDSPrimary.dwFlags = MA_DSBCAPS_PRIMARYBUFFER | MA_DSBCAPS_CTRLVOLUME; - hr = ma_IDirectSound_CreateSoundBuffer((ma_IDirectSound*)pDevice->dsound.pPlayback, &descDSPrimary, (ma_IDirectSoundBuffer**)&pDevice->dsound.pPlaybackPrimaryBuffer, NULL); - if (FAILED(hr)) { - ma_device_uninit__dsound(pDevice); - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[DirectSound] IDirectSound_CreateSoundBuffer() failed for playback device's primary buffer."); - return ma_result_from_HRESULT(hr); - } - - - /* We may want to make some adjustments to the format if we are using defaults. */ - MA_ZERO_OBJECT(&caps); - caps.dwSize = sizeof(caps); - hr = ma_IDirectSound_GetCaps((ma_IDirectSound*)pDevice->dsound.pPlayback, &caps); - if (FAILED(hr)) { - ma_device_uninit__dsound(pDevice); - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[DirectSound] IDirectSound_GetCaps() failed for playback device."); - return ma_result_from_HRESULT(hr); - } - - if ((caps.dwFlags & MA_DSCAPS_PRIMARYSTEREO) != 0) { - DWORD speakerConfig; - - /* It supports at least stereo, but could support more. */ - nativeChannelCount = 2; - - /* Look at the speaker configuration to get a better idea on the channel count. */ - if (SUCCEEDED(ma_IDirectSound_GetSpeakerConfig((ma_IDirectSound*)pDevice->dsound.pPlayback, &speakerConfig))) { - ma_get_channels_from_speaker_config__dsound(speakerConfig, &nativeChannelCount, &nativeChannelMask); - } - } else { - /* It does not support stereo, which means we are stuck with mono. */ - nativeChannelCount = 1; - nativeChannelMask = 0x00000001; - } - - if (pDescriptorPlayback->channels == 0) { - wf.nChannels = nativeChannelCount; - wf.dwChannelMask = nativeChannelMask; - } - - if (pDescriptorPlayback->sampleRate == 0) { - /* We base the sample rate on the values returned by GetCaps(). */ - if ((caps.dwFlags & MA_DSCAPS_CONTINUOUSRATE) != 0) { - wf.nSamplesPerSec = ma_get_best_sample_rate_within_range(caps.dwMinSecondarySampleRate, caps.dwMaxSecondarySampleRate); - } else { - wf.nSamplesPerSec = caps.dwMaxSecondarySampleRate; - } - } - - wf.nBlockAlign = (WORD)(wf.nChannels * wf.wBitsPerSample / 8); - wf.nAvgBytesPerSec = wf.nBlockAlign * wf.nSamplesPerSec; - - /* - From MSDN: - - The method succeeds even if the hardware does not support the requested format; DirectSound sets the buffer to the closest - supported format. To determine whether this has happened, an application can call the GetFormat method for the primary buffer - and compare the result with the format that was requested with the SetFormat method. - */ - hr = ma_IDirectSoundBuffer_SetFormat((ma_IDirectSoundBuffer*)pDevice->dsound.pPlaybackPrimaryBuffer, (MA_WAVEFORMATEX*)&wf); - if (FAILED(hr)) { - /* - If setting of the format failed we'll try again with some fallback settings. On Windows 98 I have - observed that IEEE_FLOAT does not work. We'll therefore enforce PCM. I also had issues where a - sample rate of 48000 did not work correctly. Not sure if it was a driver issue or not, but will - use 44100 for the sample rate. - */ - wf.cbSize = 18; /* NOTE: Don't use sizeof(MA_WAVEFORMATEX) here because it's got an extra 2 bytes due to padding. */ - wf.wFormatTag = WAVE_FORMAT_PCM; - wf.wBitsPerSample = 16; - wf.nChannels = nativeChannelCount; - wf.nSamplesPerSec = 44100; - wf.nBlockAlign = wf.nChannels * (wf.wBitsPerSample / 8); - wf.nAvgBytesPerSec = wf.nSamplesPerSec * wf.nBlockAlign; - - hr = ma_IDirectSoundBuffer_SetFormat((ma_IDirectSoundBuffer*)pDevice->dsound.pPlaybackPrimaryBuffer, (MA_WAVEFORMATEX*)&wf); - if (FAILED(hr)) { - ma_device_uninit__dsound(pDevice); - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[DirectSound] Failed to set format of playback device's primary buffer."); - return ma_result_from_HRESULT(hr); - } - } - - /* Get the _actual_ properties of the buffer. */ - pActualFormat = (MA_WAVEFORMATEXTENSIBLE*)rawdata; - hr = ma_IDirectSoundBuffer_GetFormat((ma_IDirectSoundBuffer*)pDevice->dsound.pPlaybackPrimaryBuffer, (MA_WAVEFORMATEX*)pActualFormat, sizeof(rawdata), NULL); - if (FAILED(hr)) { - ma_device_uninit__dsound(pDevice); - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[DirectSound] Failed to retrieve the actual format of the playback device's primary buffer."); - return ma_result_from_HRESULT(hr); - } - - /* We now have enough information to start setting some output properties. */ - pDescriptorPlayback->format = ma_format_from_WAVEFORMATEX((MA_WAVEFORMATEX*)pActualFormat); - pDescriptorPlayback->channels = pActualFormat->nChannels; - pDescriptorPlayback->sampleRate = pActualFormat->nSamplesPerSec; - - /* Get the internal channel map based on the channel mask. */ - if (pActualFormat->wFormatTag == WAVE_FORMAT_EXTENSIBLE) { - ma_channel_mask_to_channel_map__win32(pActualFormat->dwChannelMask, pDescriptorPlayback->channels, pDescriptorPlayback->channelMap); - } else { - ma_channel_mask_to_channel_map__win32(wf.dwChannelMask, pDescriptorPlayback->channels, pDescriptorPlayback->channelMap); - } - - /* The size of the buffer must be a clean multiple of the period count. */ - periodSizeInFrames = ma_calculate_period_size_in_frames_from_descriptor__dsound(pDescriptorPlayback, pDescriptorPlayback->sampleRate, pConfig->performanceProfile); - periodCount = (pDescriptorPlayback->periodCount > 0) ? pDescriptorPlayback->periodCount : MA_DEFAULT_PERIODS; - - /* - Meaning of dwFlags (from MSDN): - - DSBCAPS_CTRLPOSITIONNOTIFY - The buffer has position notification capability. - - DSBCAPS_GLOBALFOCUS - With this flag set, an application using DirectSound can continue to play its buffers if the user switches focus to - another application, even if the new application uses DirectSound. - - DSBCAPS_GETCURRENTPOSITION2 - In the first version of DirectSound, the play cursor was significantly ahead of the actual playing sound on emulated - sound cards; it was directly behind the write cursor. Now, if the DSBCAPS_GETCURRENTPOSITION2 flag is specified, the - application can get a more accurate play cursor. - */ - MA_ZERO_OBJECT(&descDS); - descDS.dwSize = sizeof(descDS); - descDS.dwFlags = MA_DSBCAPS_CTRLPOSITIONNOTIFY | MA_DSBCAPS_GLOBALFOCUS | MA_DSBCAPS_GETCURRENTPOSITION2; - descDS.dwBufferBytes = periodSizeInFrames * periodCount * ma_get_bytes_per_frame(pDescriptorPlayback->format, pDescriptorPlayback->channels); - descDS.lpwfxFormat = (MA_WAVEFORMATEX*)pActualFormat; - hr = ma_IDirectSound_CreateSoundBuffer((ma_IDirectSound*)pDevice->dsound.pPlayback, &descDS, (ma_IDirectSoundBuffer**)&pDevice->dsound.pPlaybackBuffer, NULL); - if (FAILED(hr)) { - ma_device_uninit__dsound(pDevice); - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[DirectSound] IDirectSound_CreateSoundBuffer() failed for playback device's secondary buffer."); - return ma_result_from_HRESULT(hr); - } - - /* DirectSound should give us a buffer exactly the size we asked for. */ - pDescriptorPlayback->periodSizeInFrames = periodSizeInFrames; - pDescriptorPlayback->periodCount = periodCount; - } - - return MA_SUCCESS; -} - - -static ma_result ma_device_data_loop__dsound(ma_device* pDevice) -{ - ma_result result = MA_SUCCESS; - ma_uint32 bpfDeviceCapture = ma_get_bytes_per_frame(pDevice->capture.internalFormat, pDevice->capture.internalChannels); - ma_uint32 bpfDevicePlayback = ma_get_bytes_per_frame(pDevice->playback.internalFormat, pDevice->playback.internalChannels); - HRESULT hr; - DWORD lockOffsetInBytesCapture; - DWORD lockSizeInBytesCapture; - DWORD mappedSizeInBytesCapture; - DWORD mappedDeviceFramesProcessedCapture; - void* pMappedDeviceBufferCapture; - DWORD lockOffsetInBytesPlayback; - DWORD lockSizeInBytesPlayback; - DWORD mappedSizeInBytesPlayback; - void* pMappedDeviceBufferPlayback; - DWORD prevReadCursorInBytesCapture = 0; - DWORD prevPlayCursorInBytesPlayback = 0; - ma_bool32 physicalPlayCursorLoopFlagPlayback = 0; - DWORD virtualWriteCursorInBytesPlayback = 0; - ma_bool32 virtualWriteCursorLoopFlagPlayback = 0; - ma_bool32 isPlaybackDeviceStarted = MA_FALSE; - ma_uint32 framesWrittenToPlaybackDevice = 0; /* For knowing whether or not the playback device needs to be started. */ - ma_uint32 waitTimeInMilliseconds = 1; - DWORD playbackBufferStatus = 0; - - MA_ASSERT(pDevice != NULL); - - /* The first thing to do is start the capture device. The playback device is only started after the first period is written. */ - if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) { - hr = ma_IDirectSoundCaptureBuffer_Start((ma_IDirectSoundCaptureBuffer*)pDevice->dsound.pCaptureBuffer, MA_DSCBSTART_LOOPING); - if (FAILED(hr)) { - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[DirectSound] IDirectSoundCaptureBuffer_Start() failed."); - return ma_result_from_HRESULT(hr); - } - } - - while (ma_device_get_state(pDevice) == ma_device_state_started) { - switch (pDevice->type) - { - case ma_device_type_duplex: - { - DWORD physicalCaptureCursorInBytes; - DWORD physicalReadCursorInBytes; - hr = ma_IDirectSoundCaptureBuffer_GetCurrentPosition((ma_IDirectSoundCaptureBuffer*)pDevice->dsound.pCaptureBuffer, &physicalCaptureCursorInBytes, &physicalReadCursorInBytes); - if (FAILED(hr)) { - return ma_result_from_HRESULT(hr); - } - - /* If nothing is available we just sleep for a bit and return from this iteration. */ - if (physicalReadCursorInBytes == prevReadCursorInBytesCapture) { - ma_sleep(waitTimeInMilliseconds); - continue; /* Nothing is available in the capture buffer. */ - } - - /* - The current position has moved. We need to map all of the captured samples and write them to the playback device, making sure - we don't return until every frame has been copied over. - */ - if (prevReadCursorInBytesCapture < physicalReadCursorInBytes) { - /* The capture position has not looped. This is the simple case. */ - lockOffsetInBytesCapture = prevReadCursorInBytesCapture; - lockSizeInBytesCapture = (physicalReadCursorInBytes - prevReadCursorInBytesCapture); - } else { - /* - The capture position has looped. This is the more complex case. Map to the end of the buffer. If this does not return anything, - do it again from the start. - */ - if (prevReadCursorInBytesCapture < pDevice->capture.internalPeriodSizeInFrames*pDevice->capture.internalPeriods*bpfDeviceCapture) { - /* Lock up to the end of the buffer. */ - lockOffsetInBytesCapture = prevReadCursorInBytesCapture; - lockSizeInBytesCapture = (pDevice->capture.internalPeriodSizeInFrames*pDevice->capture.internalPeriods*bpfDeviceCapture) - prevReadCursorInBytesCapture; - } else { - /* Lock starting from the start of the buffer. */ - lockOffsetInBytesCapture = 0; - lockSizeInBytesCapture = physicalReadCursorInBytes; - } - } - - if (lockSizeInBytesCapture == 0) { - ma_sleep(waitTimeInMilliseconds); - continue; /* Nothing is available in the capture buffer. */ - } - - hr = ma_IDirectSoundCaptureBuffer_Lock((ma_IDirectSoundCaptureBuffer*)pDevice->dsound.pCaptureBuffer, lockOffsetInBytesCapture, lockSizeInBytesCapture, &pMappedDeviceBufferCapture, &mappedSizeInBytesCapture, NULL, NULL, 0); - if (FAILED(hr)) { - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[DirectSound] Failed to map buffer from capture device in preparation for writing to the device."); - return ma_result_from_HRESULT(hr); - } - - - /* At this point we have some input data that we need to output. We do not return until every mapped frame of the input data is written to the playback device. */ - mappedDeviceFramesProcessedCapture = 0; - - for (;;) { /* Keep writing to the playback device. */ - ma_uint8 inputFramesInClientFormat[MA_DATA_CONVERTER_STACK_BUFFER_SIZE]; - ma_uint32 inputFramesInClientFormatCap = sizeof(inputFramesInClientFormat) / ma_get_bytes_per_frame(pDevice->capture.format, pDevice->capture.channels); - ma_uint8 outputFramesInClientFormat[MA_DATA_CONVERTER_STACK_BUFFER_SIZE]; - ma_uint32 outputFramesInClientFormatCap = sizeof(outputFramesInClientFormat) / ma_get_bytes_per_frame(pDevice->playback.format, pDevice->playback.channels); - ma_uint32 outputFramesInClientFormatCount; - ma_uint32 outputFramesInClientFormatConsumed = 0; - ma_uint64 clientCapturedFramesToProcess = ma_min(inputFramesInClientFormatCap, outputFramesInClientFormatCap); - ma_uint64 deviceCapturedFramesToProcess = (mappedSizeInBytesCapture / bpfDeviceCapture) - mappedDeviceFramesProcessedCapture; - void* pRunningMappedDeviceBufferCapture = ma_offset_ptr(pMappedDeviceBufferCapture, mappedDeviceFramesProcessedCapture * bpfDeviceCapture); - - result = ma_data_converter_process_pcm_frames(&pDevice->capture.converter, pRunningMappedDeviceBufferCapture, &deviceCapturedFramesToProcess, inputFramesInClientFormat, &clientCapturedFramesToProcess); - if (result != MA_SUCCESS) { - break; - } - - outputFramesInClientFormatCount = (ma_uint32)clientCapturedFramesToProcess; - mappedDeviceFramesProcessedCapture += (ma_uint32)deviceCapturedFramesToProcess; - - ma_device__handle_data_callback(pDevice, outputFramesInClientFormat, inputFramesInClientFormat, (ma_uint32)clientCapturedFramesToProcess); - - /* At this point we have input and output data in client format. All we need to do now is convert it to the output device format. This may take a few passes. */ - for (;;) { - ma_uint32 framesWrittenThisIteration; - DWORD physicalPlayCursorInBytes; - DWORD physicalWriteCursorInBytes; - DWORD availableBytesPlayback; - DWORD silentPaddingInBytes = 0; /* <-- Must be initialized to 0. */ - - /* We need the physical play and write cursors. */ - if (FAILED(ma_IDirectSoundBuffer_GetCurrentPosition((ma_IDirectSoundBuffer*)pDevice->dsound.pPlaybackBuffer, &physicalPlayCursorInBytes, &physicalWriteCursorInBytes))) { - break; - } - - if (physicalPlayCursorInBytes < prevPlayCursorInBytesPlayback) { - physicalPlayCursorLoopFlagPlayback = !physicalPlayCursorLoopFlagPlayback; - } - prevPlayCursorInBytesPlayback = physicalPlayCursorInBytes; - - /* If there's any bytes available for writing we can do that now. The space between the virtual cursor position and play cursor. */ - if (physicalPlayCursorLoopFlagPlayback == virtualWriteCursorLoopFlagPlayback) { - /* Same loop iteration. The available bytes wraps all the way around from the virtual write cursor to the physical play cursor. */ - if (physicalPlayCursorInBytes <= virtualWriteCursorInBytesPlayback) { - availableBytesPlayback = (pDevice->playback.internalPeriodSizeInFrames*pDevice->playback.internalPeriods*bpfDevicePlayback) - virtualWriteCursorInBytesPlayback; - availableBytesPlayback += physicalPlayCursorInBytes; /* Wrap around. */ - } else { - /* This is an error. */ - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_WARNING, "[DirectSound] (Duplex/Playback): Play cursor has moved in front of the write cursor (same loop iteration). physicalPlayCursorInBytes=%ld, virtualWriteCursorInBytes=%ld.\n", physicalPlayCursorInBytes, virtualWriteCursorInBytesPlayback); - availableBytesPlayback = 0; - } - } else { - /* Different loop iterations. The available bytes only goes from the virtual write cursor to the physical play cursor. */ - if (physicalPlayCursorInBytes >= virtualWriteCursorInBytesPlayback) { - availableBytesPlayback = physicalPlayCursorInBytes - virtualWriteCursorInBytesPlayback; - } else { - /* This is an error. */ - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_WARNING, "[DirectSound] (Duplex/Playback): Write cursor has moved behind the play cursor (different loop iterations). physicalPlayCursorInBytes=%ld, virtualWriteCursorInBytes=%ld.\n", physicalPlayCursorInBytes, virtualWriteCursorInBytesPlayback); - availableBytesPlayback = 0; - } - } - - /* If there's no room available for writing we need to wait for more. */ - if (availableBytesPlayback == 0) { - /* If we haven't started the device yet, this will never get beyond 0. In this case we need to get the device started. */ - if (!isPlaybackDeviceStarted) { - hr = ma_IDirectSoundBuffer_Play((ma_IDirectSoundBuffer*)pDevice->dsound.pPlaybackBuffer, 0, 0, MA_DSBPLAY_LOOPING); - if (FAILED(hr)) { - ma_IDirectSoundCaptureBuffer_Stop((ma_IDirectSoundCaptureBuffer*)pDevice->dsound.pCaptureBuffer); - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[DirectSound] IDirectSoundBuffer_Play() failed."); - return ma_result_from_HRESULT(hr); - } - isPlaybackDeviceStarted = MA_TRUE; - } else { - ma_sleep(waitTimeInMilliseconds); - continue; - } - } - - - /* Getting here means there room available somewhere. We limit this to either the end of the buffer or the physical play cursor, whichever is closest. */ - lockOffsetInBytesPlayback = virtualWriteCursorInBytesPlayback; - if (physicalPlayCursorLoopFlagPlayback == virtualWriteCursorLoopFlagPlayback) { - /* Same loop iteration. Go up to the end of the buffer. */ - lockSizeInBytesPlayback = (pDevice->playback.internalPeriodSizeInFrames*pDevice->playback.internalPeriods*bpfDevicePlayback) - virtualWriteCursorInBytesPlayback; - } else { - /* Different loop iterations. Go up to the physical play cursor. */ - lockSizeInBytesPlayback = physicalPlayCursorInBytes - virtualWriteCursorInBytesPlayback; - } - - hr = ma_IDirectSoundBuffer_Lock((ma_IDirectSoundBuffer*)pDevice->dsound.pPlaybackBuffer, lockOffsetInBytesPlayback, lockSizeInBytesPlayback, &pMappedDeviceBufferPlayback, &mappedSizeInBytesPlayback, NULL, NULL, 0); - if (FAILED(hr)) { - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[DirectSound] Failed to map buffer from playback device in preparation for writing to the device."); - result = ma_result_from_HRESULT(hr); - break; - } - - /* - Experiment: If the playback buffer is being starved, pad it with some silence to get it back in sync. This will cause a glitch, but it may prevent - endless glitching due to it constantly running out of data. - */ - if (isPlaybackDeviceStarted) { - DWORD bytesQueuedForPlayback = (pDevice->playback.internalPeriodSizeInFrames*pDevice->playback.internalPeriods*bpfDevicePlayback) - availableBytesPlayback; - if (bytesQueuedForPlayback < (pDevice->playback.internalPeriodSizeInFrames*bpfDevicePlayback)) { - silentPaddingInBytes = (pDevice->playback.internalPeriodSizeInFrames*2*bpfDevicePlayback) - bytesQueuedForPlayback; - if (silentPaddingInBytes > lockSizeInBytesPlayback) { - silentPaddingInBytes = lockSizeInBytesPlayback; - } - - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_WARNING, "[DirectSound] (Duplex/Playback) Playback buffer starved. availableBytesPlayback=%ld, silentPaddingInBytes=%ld\n", availableBytesPlayback, silentPaddingInBytes); - } - } - - /* At this point we have a buffer for output. */ - if (silentPaddingInBytes > 0) { - MA_ZERO_MEMORY(pMappedDeviceBufferPlayback, silentPaddingInBytes); - framesWrittenThisIteration = silentPaddingInBytes/bpfDevicePlayback; - } else { - ma_uint64 convertedFrameCountIn = (outputFramesInClientFormatCount - outputFramesInClientFormatConsumed); - ma_uint64 convertedFrameCountOut = mappedSizeInBytesPlayback/bpfDevicePlayback; - void* pConvertedFramesIn = ma_offset_ptr(outputFramesInClientFormat, outputFramesInClientFormatConsumed * bpfDevicePlayback); - void* pConvertedFramesOut = pMappedDeviceBufferPlayback; - - result = ma_data_converter_process_pcm_frames(&pDevice->playback.converter, pConvertedFramesIn, &convertedFrameCountIn, pConvertedFramesOut, &convertedFrameCountOut); - if (result != MA_SUCCESS) { - break; - } - - outputFramesInClientFormatConsumed += (ma_uint32)convertedFrameCountOut; - framesWrittenThisIteration = (ma_uint32)convertedFrameCountOut; - } - - - hr = ma_IDirectSoundBuffer_Unlock((ma_IDirectSoundBuffer*)pDevice->dsound.pPlaybackBuffer, pMappedDeviceBufferPlayback, framesWrittenThisIteration*bpfDevicePlayback, NULL, 0); - if (FAILED(hr)) { - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[DirectSound] Failed to unlock internal buffer from playback device after writing to the device."); - result = ma_result_from_HRESULT(hr); - break; - } - - virtualWriteCursorInBytesPlayback += framesWrittenThisIteration*bpfDevicePlayback; - if ((virtualWriteCursorInBytesPlayback/bpfDevicePlayback) == pDevice->playback.internalPeriodSizeInFrames*pDevice->playback.internalPeriods) { - virtualWriteCursorInBytesPlayback = 0; - virtualWriteCursorLoopFlagPlayback = !virtualWriteCursorLoopFlagPlayback; - } - - /* - We may need to start the device. We want two full periods to be written before starting the playback device. Having an extra period adds - a bit of a buffer to prevent the playback buffer from getting starved. - */ - framesWrittenToPlaybackDevice += framesWrittenThisIteration; - if (!isPlaybackDeviceStarted && framesWrittenToPlaybackDevice >= (pDevice->playback.internalPeriodSizeInFrames*2)) { - hr = ma_IDirectSoundBuffer_Play((ma_IDirectSoundBuffer*)pDevice->dsound.pPlaybackBuffer, 0, 0, MA_DSBPLAY_LOOPING); - if (FAILED(hr)) { - ma_IDirectSoundCaptureBuffer_Stop((ma_IDirectSoundCaptureBuffer*)pDevice->dsound.pCaptureBuffer); - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[DirectSound] IDirectSoundBuffer_Play() failed."); - return ma_result_from_HRESULT(hr); - } - isPlaybackDeviceStarted = MA_TRUE; - } - - if (framesWrittenThisIteration < mappedSizeInBytesPlayback/bpfDevicePlayback) { - break; /* We're finished with the output data.*/ - } - } - - if (clientCapturedFramesToProcess == 0) { - break; /* We just consumed every input sample. */ - } - } - - - /* At this point we're done with the mapped portion of the capture buffer. */ - hr = ma_IDirectSoundCaptureBuffer_Unlock((ma_IDirectSoundCaptureBuffer*)pDevice->dsound.pCaptureBuffer, pMappedDeviceBufferCapture, mappedSizeInBytesCapture, NULL, 0); - if (FAILED(hr)) { - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[DirectSound] Failed to unlock internal buffer from capture device after reading from the device."); - return ma_result_from_HRESULT(hr); - } - prevReadCursorInBytesCapture = (lockOffsetInBytesCapture + mappedSizeInBytesCapture); - } break; - - - - case ma_device_type_capture: - { - DWORD physicalCaptureCursorInBytes; - DWORD physicalReadCursorInBytes; - hr = ma_IDirectSoundCaptureBuffer_GetCurrentPosition((ma_IDirectSoundCaptureBuffer*)pDevice->dsound.pCaptureBuffer, &physicalCaptureCursorInBytes, &physicalReadCursorInBytes); - if (FAILED(hr)) { - return MA_ERROR; - } - - /* If the previous capture position is the same as the current position we need to wait a bit longer. */ - if (prevReadCursorInBytesCapture == physicalReadCursorInBytes) { - ma_sleep(waitTimeInMilliseconds); - continue; - } - - /* Getting here means we have capture data available. */ - if (prevReadCursorInBytesCapture < physicalReadCursorInBytes) { - /* The capture position has not looped. This is the simple case. */ - lockOffsetInBytesCapture = prevReadCursorInBytesCapture; - lockSizeInBytesCapture = (physicalReadCursorInBytes - prevReadCursorInBytesCapture); - } else { - /* - The capture position has looped. This is the more complex case. Map to the end of the buffer. If this does not return anything, - do it again from the start. - */ - if (prevReadCursorInBytesCapture < pDevice->capture.internalPeriodSizeInFrames*pDevice->capture.internalPeriods*bpfDeviceCapture) { - /* Lock up to the end of the buffer. */ - lockOffsetInBytesCapture = prevReadCursorInBytesCapture; - lockSizeInBytesCapture = (pDevice->capture.internalPeriodSizeInFrames*pDevice->capture.internalPeriods*bpfDeviceCapture) - prevReadCursorInBytesCapture; - } else { - /* Lock starting from the start of the buffer. */ - lockOffsetInBytesCapture = 0; - lockSizeInBytesCapture = physicalReadCursorInBytes; - } - } - - if (lockSizeInBytesCapture < pDevice->capture.internalPeriodSizeInFrames) { - ma_sleep(waitTimeInMilliseconds); - continue; /* Nothing is available in the capture buffer. */ - } - - hr = ma_IDirectSoundCaptureBuffer_Lock((ma_IDirectSoundCaptureBuffer*)pDevice->dsound.pCaptureBuffer, lockOffsetInBytesCapture, lockSizeInBytesCapture, &pMappedDeviceBufferCapture, &mappedSizeInBytesCapture, NULL, NULL, 0); - if (FAILED(hr)) { - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[DirectSound] Failed to map buffer from capture device in preparation for writing to the device."); - result = ma_result_from_HRESULT(hr); - } - - if (lockSizeInBytesCapture != mappedSizeInBytesCapture) { - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "[DirectSound] (Capture) lockSizeInBytesCapture=%ld != mappedSizeInBytesCapture=%ld\n", lockSizeInBytesCapture, mappedSizeInBytesCapture); - } - - ma_device__send_frames_to_client(pDevice, mappedSizeInBytesCapture/bpfDeviceCapture, pMappedDeviceBufferCapture); - - hr = ma_IDirectSoundCaptureBuffer_Unlock((ma_IDirectSoundCaptureBuffer*)pDevice->dsound.pCaptureBuffer, pMappedDeviceBufferCapture, mappedSizeInBytesCapture, NULL, 0); - if (FAILED(hr)) { - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[DirectSound] Failed to unlock internal buffer from capture device after reading from the device."); - return ma_result_from_HRESULT(hr); - } - prevReadCursorInBytesCapture = lockOffsetInBytesCapture + mappedSizeInBytesCapture; - - if (prevReadCursorInBytesCapture == (pDevice->capture.internalPeriodSizeInFrames*pDevice->capture.internalPeriods*bpfDeviceCapture)) { - prevReadCursorInBytesCapture = 0; - } - } break; - - - - case ma_device_type_playback: - { - DWORD availableBytesPlayback; - DWORD physicalPlayCursorInBytes; - DWORD physicalWriteCursorInBytes; - hr = ma_IDirectSoundBuffer_GetCurrentPosition((ma_IDirectSoundBuffer*)pDevice->dsound.pPlaybackBuffer, &physicalPlayCursorInBytes, &physicalWriteCursorInBytes); - if (FAILED(hr)) { - break; - } - - hr = ma_IDirectSoundBuffer_GetStatus((ma_IDirectSoundBuffer*)pDevice->dsound.pPlaybackBuffer, &playbackBufferStatus); - if (SUCCEEDED(hr) && (playbackBufferStatus & MA_DSBSTATUS_PLAYING) == 0 && isPlaybackDeviceStarted) { - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, "[DirectSound] Attempting to resume audio due to state: %d.", (int)playbackBufferStatus); - hr = ma_IDirectSoundBuffer_Play((ma_IDirectSoundBuffer*)pDevice->dsound.pPlaybackBuffer, 0, 0, MA_DSBPLAY_LOOPING); - if (FAILED(hr)) { - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[DirectSound] IDirectSoundBuffer_Play() failed after attempting to resume from state %d.", (int)playbackBufferStatus); - return ma_result_from_HRESULT(hr); - } - - isPlaybackDeviceStarted = MA_TRUE; - ma_sleep(waitTimeInMilliseconds); - continue; - } - - if (physicalPlayCursorInBytes < prevPlayCursorInBytesPlayback) { - physicalPlayCursorLoopFlagPlayback = !physicalPlayCursorLoopFlagPlayback; - } - prevPlayCursorInBytesPlayback = physicalPlayCursorInBytes; - - /* If there's any bytes available for writing we can do that now. The space between the virtual cursor position and play cursor. */ - if (physicalPlayCursorLoopFlagPlayback == virtualWriteCursorLoopFlagPlayback) { - /* Same loop iteration. The available bytes wraps all the way around from the virtual write cursor to the physical play cursor. */ - if (physicalPlayCursorInBytes <= virtualWriteCursorInBytesPlayback) { - availableBytesPlayback = (pDevice->playback.internalPeriodSizeInFrames*pDevice->playback.internalPeriods*bpfDevicePlayback) - virtualWriteCursorInBytesPlayback; - availableBytesPlayback += physicalPlayCursorInBytes; /* Wrap around. */ - } else { - /* This is an error. */ - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_WARNING, "[DirectSound] (Playback): Play cursor has moved in front of the write cursor (same loop iterations). physicalPlayCursorInBytes=%ld, virtualWriteCursorInBytes=%ld.\n", physicalPlayCursorInBytes, virtualWriteCursorInBytesPlayback); - availableBytesPlayback = 0; - } - } else { - /* Different loop iterations. The available bytes only goes from the virtual write cursor to the physical play cursor. */ - if (physicalPlayCursorInBytes >= virtualWriteCursorInBytesPlayback) { - availableBytesPlayback = physicalPlayCursorInBytes - virtualWriteCursorInBytesPlayback; - } else { - /* This is an error. */ - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_WARNING, "[DirectSound] (Playback): Write cursor has moved behind the play cursor (different loop iterations). physicalPlayCursorInBytes=%ld, virtualWriteCursorInBytes=%ld.\n", physicalPlayCursorInBytes, virtualWriteCursorInBytesPlayback); - availableBytesPlayback = 0; - } - } - - /* If there's no room available for writing we need to wait for more. */ - if (availableBytesPlayback < pDevice->playback.internalPeriodSizeInFrames) { - /* If we haven't started the device yet, this will never get beyond 0. In this case we need to get the device started. */ - if (availableBytesPlayback == 0 && !isPlaybackDeviceStarted) { - hr = ma_IDirectSoundBuffer_Play((ma_IDirectSoundBuffer*)pDevice->dsound.pPlaybackBuffer, 0, 0, MA_DSBPLAY_LOOPING); - if (FAILED(hr)) { - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[DirectSound] IDirectSoundBuffer_Play() failed."); - return ma_result_from_HRESULT(hr); - } - isPlaybackDeviceStarted = MA_TRUE; - } else { - ma_sleep(waitTimeInMilliseconds); - continue; - } - } - - /* Getting here means there room available somewhere. We limit this to either the end of the buffer or the physical play cursor, whichever is closest. */ - lockOffsetInBytesPlayback = virtualWriteCursorInBytesPlayback; - if (physicalPlayCursorLoopFlagPlayback == virtualWriteCursorLoopFlagPlayback) { - /* Same loop iteration. Go up to the end of the buffer. */ - lockSizeInBytesPlayback = (pDevice->playback.internalPeriodSizeInFrames*pDevice->playback.internalPeriods*bpfDevicePlayback) - virtualWriteCursorInBytesPlayback; - } else { - /* Different loop iterations. Go up to the physical play cursor. */ - lockSizeInBytesPlayback = physicalPlayCursorInBytes - virtualWriteCursorInBytesPlayback; - } - - hr = ma_IDirectSoundBuffer_Lock((ma_IDirectSoundBuffer*)pDevice->dsound.pPlaybackBuffer, lockOffsetInBytesPlayback, lockSizeInBytesPlayback, &pMappedDeviceBufferPlayback, &mappedSizeInBytesPlayback, NULL, NULL, 0); - if (FAILED(hr)) { - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[DirectSound] Failed to map buffer from playback device in preparation for writing to the device."); - result = ma_result_from_HRESULT(hr); - break; - } - - /* At this point we have a buffer for output. */ - ma_device__read_frames_from_client(pDevice, (mappedSizeInBytesPlayback/bpfDevicePlayback), pMappedDeviceBufferPlayback); - - hr = ma_IDirectSoundBuffer_Unlock((ma_IDirectSoundBuffer*)pDevice->dsound.pPlaybackBuffer, pMappedDeviceBufferPlayback, mappedSizeInBytesPlayback, NULL, 0); - if (FAILED(hr)) { - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[DirectSound] Failed to unlock internal buffer from playback device after writing to the device."); - result = ma_result_from_HRESULT(hr); - break; - } - - virtualWriteCursorInBytesPlayback += mappedSizeInBytesPlayback; - if (virtualWriteCursorInBytesPlayback == pDevice->playback.internalPeriodSizeInFrames*pDevice->playback.internalPeriods*bpfDevicePlayback) { - virtualWriteCursorInBytesPlayback = 0; - virtualWriteCursorLoopFlagPlayback = !virtualWriteCursorLoopFlagPlayback; - } - - /* - We may need to start the device. We want two full periods to be written before starting the playback device. Having an extra period adds - a bit of a buffer to prevent the playback buffer from getting starved. - */ - framesWrittenToPlaybackDevice += mappedSizeInBytesPlayback/bpfDevicePlayback; - if (!isPlaybackDeviceStarted && framesWrittenToPlaybackDevice >= pDevice->playback.internalPeriodSizeInFrames) { - hr = ma_IDirectSoundBuffer_Play((ma_IDirectSoundBuffer*)pDevice->dsound.pPlaybackBuffer, 0, 0, MA_DSBPLAY_LOOPING); - if (FAILED(hr)) { - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[DirectSound] IDirectSoundBuffer_Play() failed."); - return ma_result_from_HRESULT(hr); - } - isPlaybackDeviceStarted = MA_TRUE; - } - } break; - - - default: return MA_INVALID_ARGS; /* Invalid device type. */ - } - - if (result != MA_SUCCESS) { - return result; - } - } - - /* Getting here means the device is being stopped. */ - if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) { - hr = ma_IDirectSoundCaptureBuffer_Stop((ma_IDirectSoundCaptureBuffer*)pDevice->dsound.pCaptureBuffer); - if (FAILED(hr)) { - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[DirectSound] IDirectSoundCaptureBuffer_Stop() failed."); - return ma_result_from_HRESULT(hr); - } - } - - if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) { - /* The playback device should be drained before stopping. All we do is wait until the available bytes is equal to the size of the buffer. */ - if (isPlaybackDeviceStarted) { - for (;;) { - DWORD availableBytesPlayback = 0; - DWORD physicalPlayCursorInBytes; - DWORD physicalWriteCursorInBytes; - hr = ma_IDirectSoundBuffer_GetCurrentPosition((ma_IDirectSoundBuffer*)pDevice->dsound.pPlaybackBuffer, &physicalPlayCursorInBytes, &physicalWriteCursorInBytes); - if (FAILED(hr)) { - break; - } - - if (physicalPlayCursorInBytes < prevPlayCursorInBytesPlayback) { - physicalPlayCursorLoopFlagPlayback = !physicalPlayCursorLoopFlagPlayback; - } - prevPlayCursorInBytesPlayback = physicalPlayCursorInBytes; - - if (physicalPlayCursorLoopFlagPlayback == virtualWriteCursorLoopFlagPlayback) { - /* Same loop iteration. The available bytes wraps all the way around from the virtual write cursor to the physical play cursor. */ - if (physicalPlayCursorInBytes <= virtualWriteCursorInBytesPlayback) { - availableBytesPlayback = (pDevice->playback.internalPeriodSizeInFrames*pDevice->playback.internalPeriods*bpfDevicePlayback) - virtualWriteCursorInBytesPlayback; - availableBytesPlayback += physicalPlayCursorInBytes; /* Wrap around. */ - } else { - break; - } - } else { - /* Different loop iterations. The available bytes only goes from the virtual write cursor to the physical play cursor. */ - if (physicalPlayCursorInBytes >= virtualWriteCursorInBytesPlayback) { - availableBytesPlayback = physicalPlayCursorInBytes - virtualWriteCursorInBytesPlayback; - } else { - break; - } - } - - if (availableBytesPlayback >= (pDevice->playback.internalPeriodSizeInFrames*pDevice->playback.internalPeriods*bpfDevicePlayback)) { - break; - } - - ma_sleep(waitTimeInMilliseconds); - } - } - - hr = ma_IDirectSoundBuffer_Stop((ma_IDirectSoundBuffer*)pDevice->dsound.pPlaybackBuffer); - if (FAILED(hr)) { - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[DirectSound] IDirectSoundBuffer_Stop() failed."); - return ma_result_from_HRESULT(hr); - } - - ma_IDirectSoundBuffer_SetCurrentPosition((ma_IDirectSoundBuffer*)pDevice->dsound.pPlaybackBuffer, 0); - } - - return MA_SUCCESS; -} - -static ma_result ma_context_uninit__dsound(ma_context* pContext) -{ - MA_ASSERT(pContext != NULL); - MA_ASSERT(pContext->backend == ma_backend_dsound); - - ma_dlclose(ma_context_get_log(pContext), pContext->dsound.hDSoundDLL); - - return MA_SUCCESS; -} - -static ma_result ma_context_init__dsound(ma_context* pContext, const ma_context_config* pConfig, ma_backend_callbacks* pCallbacks) -{ - MA_ASSERT(pContext != NULL); - - (void)pConfig; - - pContext->dsound.hDSoundDLL = ma_dlopen(ma_context_get_log(pContext), "dsound.dll"); - if (pContext->dsound.hDSoundDLL == NULL) { - return MA_API_NOT_FOUND; - } - - pContext->dsound.DirectSoundCreate = ma_dlsym(ma_context_get_log(pContext), pContext->dsound.hDSoundDLL, "DirectSoundCreate"); - pContext->dsound.DirectSoundEnumerateA = ma_dlsym(ma_context_get_log(pContext), pContext->dsound.hDSoundDLL, "DirectSoundEnumerateA"); - pContext->dsound.DirectSoundCaptureCreate = ma_dlsym(ma_context_get_log(pContext), pContext->dsound.hDSoundDLL, "DirectSoundCaptureCreate"); - pContext->dsound.DirectSoundCaptureEnumerateA = ma_dlsym(ma_context_get_log(pContext), pContext->dsound.hDSoundDLL, "DirectSoundCaptureEnumerateA"); - - /* - We need to support all functions or nothing. DirectSound with Windows 95 seems to not work too - well in my testing. For example, it's missing DirectSoundCaptureEnumerateA(). This is a convenient - place to just disable the DirectSound backend for Windows 95. - */ - if (pContext->dsound.DirectSoundCreate == NULL || - pContext->dsound.DirectSoundEnumerateA == NULL || - pContext->dsound.DirectSoundCaptureCreate == NULL || - pContext->dsound.DirectSoundCaptureEnumerateA == NULL) { - return MA_API_NOT_FOUND; - } - - pContext->dsound.hWnd = pConfig->dsound.hWnd; - - pCallbacks->onContextInit = ma_context_init__dsound; - pCallbacks->onContextUninit = ma_context_uninit__dsound; - pCallbacks->onContextEnumerateDevices = ma_context_enumerate_devices__dsound; - pCallbacks->onContextGetDeviceInfo = ma_context_get_device_info__dsound; - pCallbacks->onDeviceInit = ma_device_init__dsound; - pCallbacks->onDeviceUninit = ma_device_uninit__dsound; - pCallbacks->onDeviceStart = NULL; /* Not used. Started in onDeviceDataLoop. */ - pCallbacks->onDeviceStop = NULL; /* Not used. Stopped in onDeviceDataLoop. */ - pCallbacks->onDeviceRead = NULL; /* Not used. Data is read directly in onDeviceDataLoop. */ - pCallbacks->onDeviceWrite = NULL; /* Not used. Data is written directly in onDeviceDataLoop. */ - pCallbacks->onDeviceDataLoop = ma_device_data_loop__dsound; - - return MA_SUCCESS; -} -#endif - - - -/****************************************************************************** - -WinMM Backend - -******************************************************************************/ -#ifdef MA_HAS_WINMM - -/* -Some build configurations will exclude the WinMM API. An example is when WIN32_LEAN_AND_MEAN -is defined. We need to define the types and functions we need manually. -*/ -#define MA_MMSYSERR_NOERROR 0 -#define MA_MMSYSERR_ERROR 1 -#define MA_MMSYSERR_BADDEVICEID 2 -#define MA_MMSYSERR_INVALHANDLE 5 -#define MA_MMSYSERR_NOMEM 7 -#define MA_MMSYSERR_INVALFLAG 10 -#define MA_MMSYSERR_INVALPARAM 11 -#define MA_MMSYSERR_HANDLEBUSY 12 - -#define MA_CALLBACK_EVENT 0x00050000 -#define MA_WAVE_ALLOWSYNC 0x0002 - -#define MA_WHDR_DONE 0x00000001 -#define MA_WHDR_PREPARED 0x00000002 -#define MA_WHDR_BEGINLOOP 0x00000004 -#define MA_WHDR_ENDLOOP 0x00000008 -#define MA_WHDR_INQUEUE 0x00000010 - -#define MA_MAXPNAMELEN 32 - -typedef void* MA_HWAVEIN; -typedef void* MA_HWAVEOUT; -typedef UINT MA_MMRESULT; -typedef UINT MA_MMVERSION; - -typedef struct -{ - WORD wMid; - WORD wPid; - MA_MMVERSION vDriverVersion; - CHAR szPname[MA_MAXPNAMELEN]; - DWORD dwFormats; - WORD wChannels; - WORD wReserved1; -} MA_WAVEINCAPSA; - -typedef struct -{ - WORD wMid; - WORD wPid; - MA_MMVERSION vDriverVersion; - CHAR szPname[MA_MAXPNAMELEN]; - DWORD dwFormats; - WORD wChannels; - WORD wReserved1; - DWORD dwSupport; -} MA_WAVEOUTCAPSA; - -typedef struct tagWAVEHDR -{ - char* lpData; - DWORD dwBufferLength; - DWORD dwBytesRecorded; - DWORD_PTR dwUser; - DWORD dwFlags; - DWORD dwLoops; - struct tagWAVEHDR* lpNext; - DWORD_PTR reserved; -} MA_WAVEHDR; - -typedef struct -{ - WORD wMid; - WORD wPid; - MA_MMVERSION vDriverVersion; - CHAR szPname[MA_MAXPNAMELEN]; - DWORD dwFormats; - WORD wChannels; - WORD wReserved1; - DWORD dwSupport; - GUID ManufacturerGuid; - GUID ProductGuid; - GUID NameGuid; -} MA_WAVEOUTCAPS2A; - -typedef struct -{ - WORD wMid; - WORD wPid; - MA_MMVERSION vDriverVersion; - CHAR szPname[MA_MAXPNAMELEN]; - DWORD dwFormats; - WORD wChannels; - WORD wReserved1; - GUID ManufacturerGuid; - GUID ProductGuid; - GUID NameGuid; -} MA_WAVEINCAPS2A; - -typedef UINT (WINAPI * MA_PFN_waveOutGetNumDevs)(void); -typedef MA_MMRESULT (WINAPI * MA_PFN_waveOutGetDevCapsA)(ma_uintptr uDeviceID, MA_WAVEOUTCAPSA* pwoc, UINT cbwoc); -typedef MA_MMRESULT (WINAPI * MA_PFN_waveOutOpen)(MA_HWAVEOUT* phwo, UINT uDeviceID, const MA_WAVEFORMATEX* pwfx, DWORD_PTR dwCallback, DWORD_PTR dwInstance, DWORD fdwOpen); -typedef MA_MMRESULT (WINAPI * MA_PFN_waveOutClose)(MA_HWAVEOUT hwo); -typedef MA_MMRESULT (WINAPI * MA_PFN_waveOutPrepareHeader)(MA_HWAVEOUT hwo, MA_WAVEHDR* pwh, UINT cbwh); -typedef MA_MMRESULT (WINAPI * MA_PFN_waveOutUnprepareHeader)(MA_HWAVEOUT hwo, MA_WAVEHDR* pwh, UINT cbwh); -typedef MA_MMRESULT (WINAPI * MA_PFN_waveOutWrite)(MA_HWAVEOUT hwo, MA_WAVEHDR* pwh, UINT cbwh); -typedef MA_MMRESULT (WINAPI * MA_PFN_waveOutReset)(MA_HWAVEOUT hwo); -typedef UINT (WINAPI * MA_PFN_waveInGetNumDevs)(void); -typedef MA_MMRESULT (WINAPI * MA_PFN_waveInGetDevCapsA)(ma_uintptr uDeviceID, MA_WAVEINCAPSA* pwic, UINT cbwic); -typedef MA_MMRESULT (WINAPI * MA_PFN_waveInOpen)(MA_HWAVEIN* phwi, UINT uDeviceID, const MA_WAVEFORMATEX* pwfx, DWORD_PTR dwCallback, DWORD_PTR dwInstance, DWORD fdwOpen); -typedef MA_MMRESULT (WINAPI * MA_PFN_waveInClose)(MA_HWAVEIN hwi); -typedef MA_MMRESULT (WINAPI * MA_PFN_waveInPrepareHeader)(MA_HWAVEIN hwi, MA_WAVEHDR* pwh, UINT cbwh); -typedef MA_MMRESULT (WINAPI * MA_PFN_waveInUnprepareHeader)(MA_HWAVEIN hwi, MA_WAVEHDR* pwh, UINT cbwh); -typedef MA_MMRESULT (WINAPI * MA_PFN_waveInAddBuffer)(MA_HWAVEIN hwi, MA_WAVEHDR* pwh, UINT cbwh); -typedef MA_MMRESULT (WINAPI * MA_PFN_waveInStart)(MA_HWAVEIN hwi); -typedef MA_MMRESULT (WINAPI * MA_PFN_waveInReset)(MA_HWAVEIN hwi); - -static ma_result ma_result_from_MMRESULT(MA_MMRESULT resultMM) -{ - switch (resultMM) - { - case MA_MMSYSERR_NOERROR: return MA_SUCCESS; - case MA_MMSYSERR_BADDEVICEID: return MA_INVALID_ARGS; - case MA_MMSYSERR_INVALHANDLE: return MA_INVALID_ARGS; - case MA_MMSYSERR_NOMEM: return MA_OUT_OF_MEMORY; - case MA_MMSYSERR_INVALFLAG: return MA_INVALID_ARGS; - case MA_MMSYSERR_INVALPARAM: return MA_INVALID_ARGS; - case MA_MMSYSERR_HANDLEBUSY: return MA_BUSY; - case MA_MMSYSERR_ERROR: return MA_ERROR; - default: return MA_ERROR; - } -} - -static char* ma_find_last_character(char* str, char ch) -{ - char* last; - - if (str == NULL) { - return NULL; - } - - last = NULL; - while (*str != '\0') { - if (*str == ch) { - last = str; - } - - str += 1; - } - - return last; -} - -static ma_uint32 ma_get_period_size_in_bytes(ma_uint32 periodSizeInFrames, ma_format format, ma_uint32 channels) -{ - return periodSizeInFrames * ma_get_bytes_per_frame(format, channels); -} - - -/* -Our own "WAVECAPS" structure that contains generic information shared between WAVEOUTCAPS2 and WAVEINCAPS2 so -we can do things generically and typesafely. Names are being kept the same for consistency. -*/ -typedef struct -{ - CHAR szPname[MA_MAXPNAMELEN]; - DWORD dwFormats; - WORD wChannels; - GUID NameGuid; -} MA_WAVECAPSA; - -static ma_result ma_get_best_info_from_formats_flags__winmm(DWORD dwFormats, WORD channels, WORD* pBitsPerSample, DWORD* pSampleRate) -{ - WORD bitsPerSample = 0; - DWORD sampleRate = 0; - - if (pBitsPerSample) { - *pBitsPerSample = 0; - } - if (pSampleRate) { - *pSampleRate = 0; - } - - if (channels == 1) { - bitsPerSample = 16; - if ((dwFormats & WAVE_FORMAT_48M16) != 0) { - sampleRate = 48000; - } else if ((dwFormats & WAVE_FORMAT_44M16) != 0) { - sampleRate = 44100; - } else if ((dwFormats & WAVE_FORMAT_2M16) != 0) { - sampleRate = 22050; - } else if ((dwFormats & WAVE_FORMAT_1M16) != 0) { - sampleRate = 11025; - } else if ((dwFormats & WAVE_FORMAT_96M16) != 0) { - sampleRate = 96000; - } else { - bitsPerSample = 8; - if ((dwFormats & WAVE_FORMAT_48M08) != 0) { - sampleRate = 48000; - } else if ((dwFormats & WAVE_FORMAT_44M08) != 0) { - sampleRate = 44100; - } else if ((dwFormats & WAVE_FORMAT_2M08) != 0) { - sampleRate = 22050; - } else if ((dwFormats & WAVE_FORMAT_1M08) != 0) { - sampleRate = 11025; - } else if ((dwFormats & WAVE_FORMAT_96M08) != 0) { - sampleRate = 96000; - } else { - return MA_FORMAT_NOT_SUPPORTED; - } - } - } else { - bitsPerSample = 16; - if ((dwFormats & WAVE_FORMAT_48S16) != 0) { - sampleRate = 48000; - } else if ((dwFormats & WAVE_FORMAT_44S16) != 0) { - sampleRate = 44100; - } else if ((dwFormats & WAVE_FORMAT_2S16) != 0) { - sampleRate = 22050; - } else if ((dwFormats & WAVE_FORMAT_1S16) != 0) { - sampleRate = 11025; - } else if ((dwFormats & WAVE_FORMAT_96S16) != 0) { - sampleRate = 96000; - } else { - bitsPerSample = 8; - if ((dwFormats & WAVE_FORMAT_48S08) != 0) { - sampleRate = 48000; - } else if ((dwFormats & WAVE_FORMAT_44S08) != 0) { - sampleRate = 44100; - } else if ((dwFormats & WAVE_FORMAT_2S08) != 0) { - sampleRate = 22050; - } else if ((dwFormats & WAVE_FORMAT_1S08) != 0) { - sampleRate = 11025; - } else if ((dwFormats & WAVE_FORMAT_96S08) != 0) { - sampleRate = 96000; - } else { - return MA_FORMAT_NOT_SUPPORTED; - } - } - } - - if (pBitsPerSample) { - *pBitsPerSample = bitsPerSample; - } - if (pSampleRate) { - *pSampleRate = sampleRate; - } - - return MA_SUCCESS; -} - -static ma_result ma_formats_flags_to_WAVEFORMATEX__winmm(DWORD dwFormats, WORD channels, MA_WAVEFORMATEX* pWF) -{ - ma_result result; - - MA_ASSERT(pWF != NULL); - - MA_ZERO_OBJECT(pWF); - pWF->cbSize = sizeof(*pWF); - pWF->wFormatTag = WAVE_FORMAT_PCM; - pWF->nChannels = (WORD)channels; - if (pWF->nChannels > 2) { - pWF->nChannels = 2; - } - - result = ma_get_best_info_from_formats_flags__winmm(dwFormats, channels, &pWF->wBitsPerSample, &pWF->nSamplesPerSec); - if (result != MA_SUCCESS) { - return result; - } - - pWF->nBlockAlign = (WORD)(pWF->nChannels * pWF->wBitsPerSample / 8); - pWF->nAvgBytesPerSec = pWF->nBlockAlign * pWF->nSamplesPerSec; - - return MA_SUCCESS; -} - -static ma_result ma_context_get_device_info_from_WAVECAPS(ma_context* pContext, MA_WAVECAPSA* pCaps, ma_device_info* pDeviceInfo) -{ - WORD bitsPerSample; - DWORD sampleRate; - ma_result result; - - MA_ASSERT(pContext != NULL); - MA_ASSERT(pCaps != NULL); - MA_ASSERT(pDeviceInfo != NULL); - - /* - Name / Description - - Unfortunately the name specified in WAVE(OUT/IN)CAPS2 is limited to 31 characters. This results in an unprofessional looking - situation where the names of the devices are truncated. To help work around this, we need to look at the name GUID and try - looking in the registry for the full name. If we can't find it there, we need to just fall back to the default name. - */ - - /* Set the default to begin with. */ - ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), pCaps->szPname, (size_t)-1); - - /* - Now try the registry. There's a few things to consider here: - - The name GUID can be null, in which we case we just need to stick to the original 31 characters. - - If the name GUID is not present in the registry we'll also need to stick to the original 31 characters. - - I like consistency, so I want the returned device names to be consistent with those returned by WASAPI and DirectSound. The - problem, however is that WASAPI and DirectSound use " ()" format (such as "Speakers (High Definition Audio)"), - but WinMM does not specify the component name. From my admittedly limited testing, I've notice the component name seems to - usually fit within the 31 characters of the fixed sized buffer, so what I'm going to do is parse that string for the component - name, and then concatenate the name from the registry. - */ - if (!ma_is_guid_null(&pCaps->NameGuid)) { - WCHAR guidStrW[256]; - if (((MA_PFN_StringFromGUID2)pContext->win32.StringFromGUID2)(&pCaps->NameGuid, guidStrW, ma_countof(guidStrW)) > 0) { - char guidStr[256]; - char keyStr[1024]; - HKEY hKey; - - WideCharToMultiByte(CP_UTF8, 0, guidStrW, -1, guidStr, sizeof(guidStr), 0, FALSE); - - ma_strcpy_s(keyStr, sizeof(keyStr), "SYSTEM\\CurrentControlSet\\Control\\MediaCategories\\"); - ma_strcat_s(keyStr, sizeof(keyStr), guidStr); - - if (((MA_PFN_RegOpenKeyExA)pContext->win32.RegOpenKeyExA)(HKEY_LOCAL_MACHINE, keyStr, 0, KEY_READ, &hKey) == ERROR_SUCCESS) { - BYTE nameFromReg[512]; - DWORD nameFromRegSize = sizeof(nameFromReg); - LONG resultWin32 = ((MA_PFN_RegQueryValueExA)pContext->win32.RegQueryValueExA)(hKey, "Name", 0, NULL, (BYTE*)nameFromReg, (DWORD*)&nameFromRegSize); - ((MA_PFN_RegCloseKey)pContext->win32.RegCloseKey)(hKey); - - if (resultWin32 == ERROR_SUCCESS) { - /* We have the value from the registry, so now we need to construct the name string. */ - char name[1024]; - if (ma_strcpy_s(name, sizeof(name), pDeviceInfo->name) == 0) { - char* nameBeg = ma_find_last_character(name, '('); - if (nameBeg != NULL) { - size_t leadingLen = (nameBeg - name); - ma_strncpy_s(nameBeg + 1, sizeof(name) - leadingLen, (const char*)nameFromReg, (size_t)-1); - - /* The closing ")", if it can fit. */ - if (leadingLen + nameFromRegSize < sizeof(name)-1) { - ma_strcat_s(name, sizeof(name), ")"); - } - - ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), name, (size_t)-1); - } - } - } - } - } - } - - - result = ma_get_best_info_from_formats_flags__winmm(pCaps->dwFormats, pCaps->wChannels, &bitsPerSample, &sampleRate); - if (result != MA_SUCCESS) { - return result; - } - - if (bitsPerSample == 8) { - pDeviceInfo->nativeDataFormats[0].format = ma_format_u8; - } else if (bitsPerSample == 16) { - pDeviceInfo->nativeDataFormats[0].format = ma_format_s16; - } else if (bitsPerSample == 24) { - pDeviceInfo->nativeDataFormats[0].format = ma_format_s24; - } else if (bitsPerSample == 32) { - pDeviceInfo->nativeDataFormats[0].format = ma_format_s32; - } else { - return MA_FORMAT_NOT_SUPPORTED; - } - pDeviceInfo->nativeDataFormats[0].channels = pCaps->wChannels; - pDeviceInfo->nativeDataFormats[0].sampleRate = sampleRate; - pDeviceInfo->nativeDataFormats[0].flags = 0; - pDeviceInfo->nativeDataFormatCount = 1; - - return MA_SUCCESS; -} - -static ma_result ma_context_get_device_info_from_WAVEOUTCAPS2(ma_context* pContext, MA_WAVEOUTCAPS2A* pCaps, ma_device_info* pDeviceInfo) -{ - MA_WAVECAPSA caps; - - MA_ASSERT(pContext != NULL); - MA_ASSERT(pCaps != NULL); - MA_ASSERT(pDeviceInfo != NULL); - - MA_COPY_MEMORY(caps.szPname, pCaps->szPname, sizeof(caps.szPname)); - caps.dwFormats = pCaps->dwFormats; - caps.wChannels = pCaps->wChannels; - caps.NameGuid = pCaps->NameGuid; - return ma_context_get_device_info_from_WAVECAPS(pContext, &caps, pDeviceInfo); -} - -static ma_result ma_context_get_device_info_from_WAVEINCAPS2(ma_context* pContext, MA_WAVEINCAPS2A* pCaps, ma_device_info* pDeviceInfo) -{ - MA_WAVECAPSA caps; - - MA_ASSERT(pContext != NULL); - MA_ASSERT(pCaps != NULL); - MA_ASSERT(pDeviceInfo != NULL); - - MA_COPY_MEMORY(caps.szPname, pCaps->szPname, sizeof(caps.szPname)); - caps.dwFormats = pCaps->dwFormats; - caps.wChannels = pCaps->wChannels; - caps.NameGuid = pCaps->NameGuid; - return ma_context_get_device_info_from_WAVECAPS(pContext, &caps, pDeviceInfo); -} - - -static ma_result ma_context_enumerate_devices__winmm(ma_context* pContext, ma_enum_devices_callback_proc callback, void* pUserData) -{ - UINT playbackDeviceCount; - UINT captureDeviceCount; - UINT iPlaybackDevice; - UINT iCaptureDevice; - - MA_ASSERT(pContext != NULL); - MA_ASSERT(callback != NULL); - - /* Playback. */ - playbackDeviceCount = ((MA_PFN_waveOutGetNumDevs)pContext->winmm.waveOutGetNumDevs)(); - for (iPlaybackDevice = 0; iPlaybackDevice < playbackDeviceCount; ++iPlaybackDevice) { - MA_MMRESULT result; - MA_WAVEOUTCAPS2A caps; - - MA_ZERO_OBJECT(&caps); - - result = ((MA_PFN_waveOutGetDevCapsA)pContext->winmm.waveOutGetDevCapsA)(iPlaybackDevice, (MA_WAVEOUTCAPSA*)&caps, sizeof(caps)); - if (result == MA_MMSYSERR_NOERROR) { - ma_device_info deviceInfo; - - MA_ZERO_OBJECT(&deviceInfo); - deviceInfo.id.winmm = iPlaybackDevice; - - /* The first enumerated device is the default device. */ - if (iPlaybackDevice == 0) { - deviceInfo.isDefault = MA_TRUE; - } - - if (ma_context_get_device_info_from_WAVEOUTCAPS2(pContext, &caps, &deviceInfo) == MA_SUCCESS) { - ma_bool32 cbResult = callback(pContext, ma_device_type_playback, &deviceInfo, pUserData); - if (cbResult == MA_FALSE) { - return MA_SUCCESS; /* Enumeration was stopped. */ - } - } - } - } - - /* Capture. */ - captureDeviceCount = ((MA_PFN_waveInGetNumDevs)pContext->winmm.waveInGetNumDevs)(); - for (iCaptureDevice = 0; iCaptureDevice < captureDeviceCount; ++iCaptureDevice) { - MA_MMRESULT result; - MA_WAVEINCAPS2A caps; - - MA_ZERO_OBJECT(&caps); - - result = ((MA_PFN_waveInGetDevCapsA)pContext->winmm.waveInGetDevCapsA)(iCaptureDevice, (MA_WAVEINCAPSA*)&caps, sizeof(caps)); - if (result == MA_MMSYSERR_NOERROR) { - ma_device_info deviceInfo; - - MA_ZERO_OBJECT(&deviceInfo); - deviceInfo.id.winmm = iCaptureDevice; - - /* The first enumerated device is the default device. */ - if (iCaptureDevice == 0) { - deviceInfo.isDefault = MA_TRUE; - } - - if (ma_context_get_device_info_from_WAVEINCAPS2(pContext, &caps, &deviceInfo) == MA_SUCCESS) { - ma_bool32 cbResult = callback(pContext, ma_device_type_capture, &deviceInfo, pUserData); - if (cbResult == MA_FALSE) { - return MA_SUCCESS; /* Enumeration was stopped. */ - } - } - } - } - - return MA_SUCCESS; -} - -static ma_result ma_context_get_device_info__winmm(ma_context* pContext, ma_device_type deviceType, const ma_device_id* pDeviceID, ma_device_info* pDeviceInfo) -{ - UINT winMMDeviceID; - - MA_ASSERT(pContext != NULL); - - winMMDeviceID = 0; - if (pDeviceID != NULL) { - winMMDeviceID = (UINT)pDeviceID->winmm; - } - - pDeviceInfo->id.winmm = winMMDeviceID; - - /* The first ID is the default device. */ - if (winMMDeviceID == 0) { - pDeviceInfo->isDefault = MA_TRUE; - } - - if (deviceType == ma_device_type_playback) { - MA_MMRESULT result; - MA_WAVEOUTCAPS2A caps; - - MA_ZERO_OBJECT(&caps); - - result = ((MA_PFN_waveOutGetDevCapsA)pContext->winmm.waveOutGetDevCapsA)(winMMDeviceID, (MA_WAVEOUTCAPSA*)&caps, sizeof(caps)); - if (result == MA_MMSYSERR_NOERROR) { - return ma_context_get_device_info_from_WAVEOUTCAPS2(pContext, &caps, pDeviceInfo); - } - } else { - MA_MMRESULT result; - MA_WAVEINCAPS2A caps; - - MA_ZERO_OBJECT(&caps); - - result = ((MA_PFN_waveInGetDevCapsA)pContext->winmm.waveInGetDevCapsA)(winMMDeviceID, (MA_WAVEINCAPSA*)&caps, sizeof(caps)); - if (result == MA_MMSYSERR_NOERROR) { - return ma_context_get_device_info_from_WAVEINCAPS2(pContext, &caps, pDeviceInfo); - } - } - - return MA_NO_DEVICE; -} - - -static ma_result ma_device_uninit__winmm(ma_device* pDevice) -{ - MA_ASSERT(pDevice != NULL); - - if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) { - ((MA_PFN_waveInClose)pDevice->pContext->winmm.waveInClose)((MA_HWAVEIN)pDevice->winmm.hDeviceCapture); - CloseHandle((HANDLE)pDevice->winmm.hEventCapture); - } - - if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) { - ((MA_PFN_waveOutReset)pDevice->pContext->winmm.waveOutReset)((MA_HWAVEOUT)pDevice->winmm.hDevicePlayback); - ((MA_PFN_waveOutClose)pDevice->pContext->winmm.waveOutClose)((MA_HWAVEOUT)pDevice->winmm.hDevicePlayback); - CloseHandle((HANDLE)pDevice->winmm.hEventPlayback); - } - - ma_free(pDevice->winmm._pHeapData, &pDevice->pContext->allocationCallbacks); - - MA_ZERO_OBJECT(&pDevice->winmm); /* Safety. */ - - return MA_SUCCESS; -} - -static ma_uint32 ma_calculate_period_size_in_frames_from_descriptor__winmm(const ma_device_descriptor* pDescriptor, ma_uint32 nativeSampleRate, ma_performance_profile performanceProfile) -{ - /* WinMM has a minimum period size of 40ms. */ - ma_uint32 minPeriodSizeInFrames = ma_calculate_buffer_size_in_frames_from_milliseconds(40, nativeSampleRate); - ma_uint32 periodSizeInFrames; - - periodSizeInFrames = ma_calculate_buffer_size_in_frames_from_descriptor(pDescriptor, nativeSampleRate, performanceProfile); - if (periodSizeInFrames < minPeriodSizeInFrames) { - periodSizeInFrames = minPeriodSizeInFrames; - } - - return periodSizeInFrames; -} - -static ma_result ma_device_init__winmm(ma_device* pDevice, const ma_device_config* pConfig, ma_device_descriptor* pDescriptorPlayback, ma_device_descriptor* pDescriptorCapture) -{ - const char* errorMsg = ""; - ma_result errorCode = MA_ERROR; - ma_result result = MA_SUCCESS; - ma_uint32 heapSize; - UINT winMMDeviceIDPlayback = 0; - UINT winMMDeviceIDCapture = 0; - - MA_ASSERT(pDevice != NULL); - - MA_ZERO_OBJECT(&pDevice->winmm); - - if (pConfig->deviceType == ma_device_type_loopback) { - return MA_DEVICE_TYPE_NOT_SUPPORTED; - } - - /* No exclusive mode with WinMM. */ - if (((pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) && pDescriptorPlayback->shareMode == ma_share_mode_exclusive) || - ((pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) && pDescriptorCapture->shareMode == ma_share_mode_exclusive)) { - return MA_SHARE_MODE_NOT_SUPPORTED; - } - - if (pDescriptorPlayback->pDeviceID != NULL) { - winMMDeviceIDPlayback = (UINT)pDescriptorPlayback->pDeviceID->winmm; - } - if (pDescriptorCapture->pDeviceID != NULL) { - winMMDeviceIDCapture = (UINT)pDescriptorCapture->pDeviceID->winmm; - } - - /* The capture device needs to be initialized first. */ - if (pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) { - MA_WAVEINCAPSA caps; - MA_WAVEFORMATEX wf; - MA_MMRESULT resultMM; - - /* We use an event to know when a new fragment needs to be enqueued. */ - pDevice->winmm.hEventCapture = (ma_handle)CreateEventA(NULL, TRUE, TRUE, NULL); - if (pDevice->winmm.hEventCapture == NULL) { - errorMsg = "[WinMM] Failed to create event for fragment enqueuing for the capture device.", errorCode = ma_result_from_GetLastError(GetLastError()); - goto on_error; - } - - /* The format should be based on the device's actual format. */ - if (((MA_PFN_waveInGetDevCapsA)pDevice->pContext->winmm.waveInGetDevCapsA)(winMMDeviceIDCapture, &caps, sizeof(caps)) != MA_MMSYSERR_NOERROR) { - errorMsg = "[WinMM] Failed to retrieve internal device caps.", errorCode = MA_FORMAT_NOT_SUPPORTED; - goto on_error; - } - - result = ma_formats_flags_to_WAVEFORMATEX__winmm(caps.dwFormats, caps.wChannels, &wf); - if (result != MA_SUCCESS) { - errorMsg = "[WinMM] Could not find appropriate format for internal device.", errorCode = result; - goto on_error; - } - - resultMM = ((MA_PFN_waveInOpen)pDevice->pContext->winmm.waveInOpen)((MA_HWAVEIN*)&pDevice->winmm.hDeviceCapture, winMMDeviceIDCapture, &wf, (DWORD_PTR)pDevice->winmm.hEventCapture, (DWORD_PTR)pDevice, MA_CALLBACK_EVENT | MA_WAVE_ALLOWSYNC); - if (resultMM != MA_MMSYSERR_NOERROR) { - errorMsg = "[WinMM] Failed to open capture device.", errorCode = MA_FAILED_TO_OPEN_BACKEND_DEVICE; - goto on_error; - } - - pDescriptorCapture->format = ma_format_from_WAVEFORMATEX(&wf); - pDescriptorCapture->channels = wf.nChannels; - pDescriptorCapture->sampleRate = wf.nSamplesPerSec; - ma_channel_map_init_standard(ma_standard_channel_map_microsoft, pDescriptorCapture->channelMap, ma_countof(pDescriptorCapture->channelMap), pDescriptorCapture->channels); - pDescriptorCapture->periodCount = pDescriptorCapture->periodCount; - pDescriptorCapture->periodSizeInFrames = ma_calculate_period_size_in_frames_from_descriptor__winmm(pDescriptorCapture, pDescriptorCapture->sampleRate, pConfig->performanceProfile); - } - - if (pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) { - MA_WAVEOUTCAPSA caps; - MA_WAVEFORMATEX wf; - MA_MMRESULT resultMM; - - /* We use an event to know when a new fragment needs to be enqueued. */ - pDevice->winmm.hEventPlayback = (ma_handle)CreateEventA(NULL, TRUE, TRUE, NULL); - if (pDevice->winmm.hEventPlayback == NULL) { - errorMsg = "[WinMM] Failed to create event for fragment enqueuing for the playback device.", errorCode = ma_result_from_GetLastError(GetLastError()); - goto on_error; - } - - /* The format should be based on the device's actual format. */ - if (((MA_PFN_waveOutGetDevCapsA)pDevice->pContext->winmm.waveOutGetDevCapsA)(winMMDeviceIDPlayback, &caps, sizeof(caps)) != MA_MMSYSERR_NOERROR) { - errorMsg = "[WinMM] Failed to retrieve internal device caps.", errorCode = MA_FORMAT_NOT_SUPPORTED; - goto on_error; - } - - result = ma_formats_flags_to_WAVEFORMATEX__winmm(caps.dwFormats, caps.wChannels, &wf); - if (result != MA_SUCCESS) { - errorMsg = "[WinMM] Could not find appropriate format for internal device.", errorCode = result; - goto on_error; - } - - resultMM = ((MA_PFN_waveOutOpen)pDevice->pContext->winmm.waveOutOpen)((MA_HWAVEOUT*)&pDevice->winmm.hDevicePlayback, winMMDeviceIDPlayback, &wf, (DWORD_PTR)pDevice->winmm.hEventPlayback, (DWORD_PTR)pDevice, MA_CALLBACK_EVENT | MA_WAVE_ALLOWSYNC); - if (resultMM != MA_MMSYSERR_NOERROR) { - errorMsg = "[WinMM] Failed to open playback device.", errorCode = MA_FAILED_TO_OPEN_BACKEND_DEVICE; - goto on_error; - } - - pDescriptorPlayback->format = ma_format_from_WAVEFORMATEX(&wf); - pDescriptorPlayback->channels = wf.nChannels; - pDescriptorPlayback->sampleRate = wf.nSamplesPerSec; - ma_channel_map_init_standard(ma_standard_channel_map_microsoft, pDescriptorPlayback->channelMap, ma_countof(pDescriptorPlayback->channelMap), pDescriptorPlayback->channels); - pDescriptorPlayback->periodCount = pDescriptorPlayback->periodCount; - pDescriptorPlayback->periodSizeInFrames = ma_calculate_period_size_in_frames_from_descriptor__winmm(pDescriptorPlayback, pDescriptorPlayback->sampleRate, pConfig->performanceProfile); - } - - /* - The heap allocated data is allocated like so: - - [Capture WAVEHDRs][Playback WAVEHDRs][Capture Intermediary Buffer][Playback Intermediary Buffer] - */ - heapSize = 0; - if (pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) { - heapSize += sizeof(MA_WAVEHDR)*pDescriptorCapture->periodCount + (pDescriptorCapture->periodSizeInFrames * pDescriptorCapture->periodCount * ma_get_bytes_per_frame(pDescriptorCapture->format, pDescriptorCapture->channels)); - } - if (pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) { - heapSize += sizeof(MA_WAVEHDR)*pDescriptorPlayback->periodCount + (pDescriptorPlayback->periodSizeInFrames * pDescriptorPlayback->periodCount * ma_get_bytes_per_frame(pDescriptorPlayback->format, pDescriptorPlayback->channels)); - } - - pDevice->winmm._pHeapData = (ma_uint8*)ma_calloc(heapSize, &pDevice->pContext->allocationCallbacks); - if (pDevice->winmm._pHeapData == NULL) { - errorMsg = "[WinMM] Failed to allocate memory for the intermediary buffer.", errorCode = MA_OUT_OF_MEMORY; - goto on_error; - } - - MA_ZERO_MEMORY(pDevice->winmm._pHeapData, heapSize); - - if (pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) { - ma_uint32 iPeriod; - - if (pConfig->deviceType == ma_device_type_capture) { - pDevice->winmm.pWAVEHDRCapture = pDevice->winmm._pHeapData; - pDevice->winmm.pIntermediaryBufferCapture = pDevice->winmm._pHeapData + (sizeof(MA_WAVEHDR)*(pDescriptorCapture->periodCount)); - } else { - pDevice->winmm.pWAVEHDRCapture = pDevice->winmm._pHeapData; - pDevice->winmm.pIntermediaryBufferCapture = pDevice->winmm._pHeapData + (sizeof(MA_WAVEHDR)*(pDescriptorCapture->periodCount + pDescriptorPlayback->periodCount)); - } - - /* Prepare headers. */ - for (iPeriod = 0; iPeriod < pDescriptorCapture->periodCount; ++iPeriod) { - ma_uint32 periodSizeInBytes = ma_get_period_size_in_bytes(pDescriptorCapture->periodSizeInFrames, pDescriptorCapture->format, pDescriptorCapture->channels); - - ((MA_WAVEHDR*)pDevice->winmm.pWAVEHDRCapture)[iPeriod].lpData = (char*)(pDevice->winmm.pIntermediaryBufferCapture + (periodSizeInBytes*iPeriod)); - ((MA_WAVEHDR*)pDevice->winmm.pWAVEHDRCapture)[iPeriod].dwBufferLength = periodSizeInBytes; - ((MA_WAVEHDR*)pDevice->winmm.pWAVEHDRCapture)[iPeriod].dwFlags = 0L; - ((MA_WAVEHDR*)pDevice->winmm.pWAVEHDRCapture)[iPeriod].dwLoops = 0L; - ((MA_PFN_waveInPrepareHeader)pDevice->pContext->winmm.waveInPrepareHeader)((MA_HWAVEIN)pDevice->winmm.hDeviceCapture, &((MA_WAVEHDR*)pDevice->winmm.pWAVEHDRCapture)[iPeriod], sizeof(MA_WAVEHDR)); - - /* - The user data of the MA_WAVEHDR structure is a single flag the controls whether or not it is ready for writing. Consider it to be named "isLocked". A value of 0 means - it's unlocked and available for writing. A value of 1 means it's locked. - */ - ((MA_WAVEHDR*)pDevice->winmm.pWAVEHDRCapture)[iPeriod].dwUser = 0; - } - } - - if (pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) { - ma_uint32 iPeriod; - - if (pConfig->deviceType == ma_device_type_playback) { - pDevice->winmm.pWAVEHDRPlayback = pDevice->winmm._pHeapData; - pDevice->winmm.pIntermediaryBufferPlayback = pDevice->winmm._pHeapData + (sizeof(MA_WAVEHDR)*pDescriptorPlayback->periodCount); - } else { - pDevice->winmm.pWAVEHDRPlayback = pDevice->winmm._pHeapData + (sizeof(MA_WAVEHDR)*(pDescriptorCapture->periodCount)); - pDevice->winmm.pIntermediaryBufferPlayback = pDevice->winmm._pHeapData + (sizeof(MA_WAVEHDR)*(pDescriptorCapture->periodCount + pDescriptorPlayback->periodCount)) + (pDescriptorCapture->periodSizeInFrames*pDescriptorCapture->periodCount*ma_get_bytes_per_frame(pDescriptorCapture->format, pDescriptorCapture->channels)); - } - - /* Prepare headers. */ - for (iPeriod = 0; iPeriod < pDescriptorPlayback->periodCount; ++iPeriod) { - ma_uint32 periodSizeInBytes = ma_get_period_size_in_bytes(pDescriptorPlayback->periodSizeInFrames, pDescriptorPlayback->format, pDescriptorPlayback->channels); - - ((MA_WAVEHDR*)pDevice->winmm.pWAVEHDRPlayback)[iPeriod].lpData = (char*)(pDevice->winmm.pIntermediaryBufferPlayback + (periodSizeInBytes*iPeriod)); - ((MA_WAVEHDR*)pDevice->winmm.pWAVEHDRPlayback)[iPeriod].dwBufferLength = periodSizeInBytes; - ((MA_WAVEHDR*)pDevice->winmm.pWAVEHDRPlayback)[iPeriod].dwFlags = 0L; - ((MA_WAVEHDR*)pDevice->winmm.pWAVEHDRPlayback)[iPeriod].dwLoops = 0L; - ((MA_PFN_waveOutPrepareHeader)pDevice->pContext->winmm.waveOutPrepareHeader)((MA_HWAVEOUT)pDevice->winmm.hDevicePlayback, &((MA_WAVEHDR*)pDevice->winmm.pWAVEHDRPlayback)[iPeriod], sizeof(MA_WAVEHDR)); - - /* - The user data of the MA_WAVEHDR structure is a single flag the controls whether or not it is ready for writing. Consider it to be named "isLocked". A value of 0 means - it's unlocked and available for writing. A value of 1 means it's locked. - */ - ((MA_WAVEHDR*)pDevice->winmm.pWAVEHDRPlayback)[iPeriod].dwUser = 0; - } - } - - return MA_SUCCESS; - -on_error: - if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) { - if (pDevice->winmm.pWAVEHDRCapture != NULL) { - ma_uint32 iPeriod; - for (iPeriod = 0; iPeriod < pDescriptorCapture->periodCount; ++iPeriod) { - ((MA_PFN_waveInUnprepareHeader)pDevice->pContext->winmm.waveInUnprepareHeader)((MA_HWAVEIN)pDevice->winmm.hDeviceCapture, &((MA_WAVEHDR*)pDevice->winmm.pWAVEHDRCapture)[iPeriod], sizeof(MA_WAVEHDR)); - } - } - - ((MA_PFN_waveInClose)pDevice->pContext->winmm.waveInClose)((MA_HWAVEIN)pDevice->winmm.hDeviceCapture); - } - - if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) { - if (pDevice->winmm.pWAVEHDRCapture != NULL) { - ma_uint32 iPeriod; - for (iPeriod = 0; iPeriod < pDescriptorPlayback->periodCount; ++iPeriod) { - ((MA_PFN_waveOutUnprepareHeader)pDevice->pContext->winmm.waveOutUnprepareHeader)((MA_HWAVEOUT)pDevice->winmm.hDevicePlayback, &((MA_WAVEHDR*)pDevice->winmm.pWAVEHDRPlayback)[iPeriod], sizeof(MA_WAVEHDR)); - } - } - - ((MA_PFN_waveOutClose)pDevice->pContext->winmm.waveOutClose)((MA_HWAVEOUT)pDevice->winmm.hDevicePlayback); - } - - ma_free(pDevice->winmm._pHeapData, &pDevice->pContext->allocationCallbacks); - - if (errorMsg != NULL && errorMsg[0] != '\0') { - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "%s", errorMsg); - } - - return errorCode; -} - -static ma_result ma_device_start__winmm(ma_device* pDevice) -{ - MA_ASSERT(pDevice != NULL); - - if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) { - MA_MMRESULT resultMM; - MA_WAVEHDR* pWAVEHDR; - ma_uint32 iPeriod; - - pWAVEHDR = (MA_WAVEHDR*)pDevice->winmm.pWAVEHDRCapture; - - /* Make sure the event is reset to a non-signaled state to ensure we don't prematurely return from WaitForSingleObject(). */ - ResetEvent((HANDLE)pDevice->winmm.hEventCapture); - - /* To start the device we attach all of the buffers and then start it. As the buffers are filled with data we will get notifications. */ - for (iPeriod = 0; iPeriod < pDevice->capture.internalPeriods; ++iPeriod) { - resultMM = ((MA_PFN_waveInAddBuffer)pDevice->pContext->winmm.waveInAddBuffer)((MA_HWAVEIN)pDevice->winmm.hDeviceCapture, &((MA_WAVEHDR*)pDevice->winmm.pWAVEHDRCapture)[iPeriod], sizeof(MA_WAVEHDR)); - if (resultMM != MA_MMSYSERR_NOERROR) { - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[WinMM] Failed to attach input buffers to capture device in preparation for capture."); - return ma_result_from_MMRESULT(resultMM); - } - - /* Make sure all of the buffers start out locked. We don't want to access them until the backend tells us we can. */ - pWAVEHDR[iPeriod].dwUser = 1; /* 1 = locked. */ - } - - /* Capture devices need to be explicitly started, unlike playback devices. */ - resultMM = ((MA_PFN_waveInStart)pDevice->pContext->winmm.waveInStart)((MA_HWAVEIN)pDevice->winmm.hDeviceCapture); - if (resultMM != MA_MMSYSERR_NOERROR) { - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[WinMM] Failed to start backend device."); - return ma_result_from_MMRESULT(resultMM); - } - } - - if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) { - /* Don't need to do anything for playback. It'll be started automatically in ma_device_start__winmm(). */ - } - - return MA_SUCCESS; -} - -static ma_result ma_device_stop__winmm(ma_device* pDevice) -{ - MA_MMRESULT resultMM; - - MA_ASSERT(pDevice != NULL); - - if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) { - if (pDevice->winmm.hDeviceCapture == NULL) { - return MA_INVALID_ARGS; - } - - resultMM = ((MA_PFN_waveInReset)pDevice->pContext->winmm.waveInReset)((MA_HWAVEIN)pDevice->winmm.hDeviceCapture); - if (resultMM != MA_MMSYSERR_NOERROR) { - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_WARNING, "[WinMM] WARNING: Failed to reset capture device."); - } - } - - if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) { - ma_uint32 iPeriod; - MA_WAVEHDR* pWAVEHDR; - - if (pDevice->winmm.hDevicePlayback == NULL) { - return MA_INVALID_ARGS; - } - - /* We need to drain the device. To do this we just loop over each header and if it's locked just wait for the event. */ - pWAVEHDR = (MA_WAVEHDR*)pDevice->winmm.pWAVEHDRPlayback; - for (iPeriod = 0; iPeriod < pDevice->playback.internalPeriods; iPeriod += 1) { - if (pWAVEHDR[iPeriod].dwUser == 1) { /* 1 = locked. */ - if (WaitForSingleObject((HANDLE)pDevice->winmm.hEventPlayback, INFINITE) != WAIT_OBJECT_0) { - break; /* An error occurred so just abandon ship and stop the device without draining. */ - } - - pWAVEHDR[iPeriod].dwUser = 0; - } - } - - resultMM = ((MA_PFN_waveOutReset)pDevice->pContext->winmm.waveOutReset)((MA_HWAVEOUT)pDevice->winmm.hDevicePlayback); - if (resultMM != MA_MMSYSERR_NOERROR) { - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_WARNING, "[WinMM] WARNING: Failed to reset playback device."); - } - } - - return MA_SUCCESS; -} - -static ma_result ma_device_write__winmm(ma_device* pDevice, const void* pPCMFrames, ma_uint32 frameCount, ma_uint32* pFramesWritten) -{ - ma_result result = MA_SUCCESS; - MA_MMRESULT resultMM; - ma_uint32 totalFramesWritten; - MA_WAVEHDR* pWAVEHDR; - - MA_ASSERT(pDevice != NULL); - MA_ASSERT(pPCMFrames != NULL); - - if (pFramesWritten != NULL) { - *pFramesWritten = 0; - } - - pWAVEHDR = (MA_WAVEHDR*)pDevice->winmm.pWAVEHDRPlayback; - - /* Keep processing as much data as possible. */ - totalFramesWritten = 0; - while (totalFramesWritten < frameCount) { - /* If the current header has some space available we need to write part of it. */ - if (pWAVEHDR[pDevice->winmm.iNextHeaderPlayback].dwUser == 0) { /* 0 = unlocked. */ - /* - This header has room in it. We copy as much of it as we can. If we end up fully consuming the buffer we need to - write it out and move on to the next iteration. - */ - ma_uint32 bpf = ma_get_bytes_per_frame(pDevice->playback.internalFormat, pDevice->playback.internalChannels); - ma_uint32 framesRemainingInHeader = (pWAVEHDR[pDevice->winmm.iNextHeaderPlayback].dwBufferLength/bpf) - pDevice->winmm.headerFramesConsumedPlayback; - - ma_uint32 framesToCopy = ma_min(framesRemainingInHeader, (frameCount - totalFramesWritten)); - const void* pSrc = ma_offset_ptr(pPCMFrames, totalFramesWritten*bpf); - void* pDst = ma_offset_ptr(pWAVEHDR[pDevice->winmm.iNextHeaderPlayback].lpData, pDevice->winmm.headerFramesConsumedPlayback*bpf); - MA_COPY_MEMORY(pDst, pSrc, framesToCopy*bpf); - - pDevice->winmm.headerFramesConsumedPlayback += framesToCopy; - totalFramesWritten += framesToCopy; - - /* If we've consumed the buffer entirely we need to write it out to the device. */ - if (pDevice->winmm.headerFramesConsumedPlayback == (pWAVEHDR[pDevice->winmm.iNextHeaderPlayback].dwBufferLength/bpf)) { - pWAVEHDR[pDevice->winmm.iNextHeaderPlayback].dwUser = 1; /* 1 = locked. */ - pWAVEHDR[pDevice->winmm.iNextHeaderPlayback].dwFlags &= ~MA_WHDR_DONE; /* <-- Need to make sure the WHDR_DONE flag is unset. */ - - /* Make sure the event is reset to a non-signaled state to ensure we don't prematurely return from WaitForSingleObject(). */ - ResetEvent((HANDLE)pDevice->winmm.hEventPlayback); - - /* The device will be started here. */ - resultMM = ((MA_PFN_waveOutWrite)pDevice->pContext->winmm.waveOutWrite)((MA_HWAVEOUT)pDevice->winmm.hDevicePlayback, &pWAVEHDR[pDevice->winmm.iNextHeaderPlayback], sizeof(MA_WAVEHDR)); - if (resultMM != MA_MMSYSERR_NOERROR) { - result = ma_result_from_MMRESULT(resultMM); - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[WinMM] waveOutWrite() failed."); - break; - } - - /* Make sure we move to the next header. */ - pDevice->winmm.iNextHeaderPlayback = (pDevice->winmm.iNextHeaderPlayback + 1) % pDevice->playback.internalPeriods; - pDevice->winmm.headerFramesConsumedPlayback = 0; - } - - /* If at this point we have consumed the entire input buffer we can return. */ - MA_ASSERT(totalFramesWritten <= frameCount); - if (totalFramesWritten == frameCount) { - break; - } - - /* Getting here means there's more to process. */ - continue; - } - - /* Getting here means there isn't enough room in the buffer and we need to wait for one to become available. */ - if (WaitForSingleObject((HANDLE)pDevice->winmm.hEventPlayback, INFINITE) != WAIT_OBJECT_0) { - result = MA_ERROR; - break; - } - - /* Something happened. If the next buffer has been marked as done we need to reset a bit of state. */ - if ((pWAVEHDR[pDevice->winmm.iNextHeaderPlayback].dwFlags & MA_WHDR_DONE) != 0) { - pWAVEHDR[pDevice->winmm.iNextHeaderPlayback].dwUser = 0; /* 0 = unlocked (make it available for writing). */ - pDevice->winmm.headerFramesConsumedPlayback = 0; - } - - /* If the device has been stopped we need to break. */ - if (ma_device_get_state(pDevice) != ma_device_state_started) { - break; - } - } - - if (pFramesWritten != NULL) { - *pFramesWritten = totalFramesWritten; - } - - return result; -} - -static ma_result ma_device_read__winmm(ma_device* pDevice, void* pPCMFrames, ma_uint32 frameCount, ma_uint32* pFramesRead) -{ - ma_result result = MA_SUCCESS; - MA_MMRESULT resultMM; - ma_uint32 totalFramesRead; - MA_WAVEHDR* pWAVEHDR; - - MA_ASSERT(pDevice != NULL); - MA_ASSERT(pPCMFrames != NULL); - - if (pFramesRead != NULL) { - *pFramesRead = 0; - } - - pWAVEHDR = (MA_WAVEHDR*)pDevice->winmm.pWAVEHDRCapture; - - /* Keep processing as much data as possible. */ - totalFramesRead = 0; - while (totalFramesRead < frameCount) { - /* If the current header has some space available we need to write part of it. */ - if (pWAVEHDR[pDevice->winmm.iNextHeaderCapture].dwUser == 0) { /* 0 = unlocked. */ - /* The buffer is available for reading. If we fully consume it we need to add it back to the buffer. */ - ma_uint32 bpf = ma_get_bytes_per_frame(pDevice->capture.internalFormat, pDevice->capture.internalChannels); - ma_uint32 framesRemainingInHeader = (pWAVEHDR[pDevice->winmm.iNextHeaderCapture].dwBufferLength/bpf) - pDevice->winmm.headerFramesConsumedCapture; - - ma_uint32 framesToCopy = ma_min(framesRemainingInHeader, (frameCount - totalFramesRead)); - const void* pSrc = ma_offset_ptr(pWAVEHDR[pDevice->winmm.iNextHeaderCapture].lpData, pDevice->winmm.headerFramesConsumedCapture*bpf); - void* pDst = ma_offset_ptr(pPCMFrames, totalFramesRead*bpf); - MA_COPY_MEMORY(pDst, pSrc, framesToCopy*bpf); - - pDevice->winmm.headerFramesConsumedCapture += framesToCopy; - totalFramesRead += framesToCopy; - - /* If we've consumed the buffer entirely we need to add it back to the device. */ - if (pDevice->winmm.headerFramesConsumedCapture == (pWAVEHDR[pDevice->winmm.iNextHeaderCapture].dwBufferLength/bpf)) { - pWAVEHDR[pDevice->winmm.iNextHeaderCapture].dwUser = 1; /* 1 = locked. */ - pWAVEHDR[pDevice->winmm.iNextHeaderCapture].dwFlags &= ~MA_WHDR_DONE; /* <-- Need to make sure the WHDR_DONE flag is unset. */ - - /* Make sure the event is reset to a non-signaled state to ensure we don't prematurely return from WaitForSingleObject(). */ - ResetEvent((HANDLE)pDevice->winmm.hEventCapture); - - /* The device will be started here. */ - resultMM = ((MA_PFN_waveInAddBuffer)pDevice->pContext->winmm.waveInAddBuffer)((MA_HWAVEIN)pDevice->winmm.hDeviceCapture, &((MA_WAVEHDR*)pDevice->winmm.pWAVEHDRCapture)[pDevice->winmm.iNextHeaderCapture], sizeof(MA_WAVEHDR)); - if (resultMM != MA_MMSYSERR_NOERROR) { - result = ma_result_from_MMRESULT(resultMM); - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[WinMM] waveInAddBuffer() failed."); - break; - } - - /* Make sure we move to the next header. */ - pDevice->winmm.iNextHeaderCapture = (pDevice->winmm.iNextHeaderCapture + 1) % pDevice->capture.internalPeriods; - pDevice->winmm.headerFramesConsumedCapture = 0; - } - - /* If at this point we have filled the entire input buffer we can return. */ - MA_ASSERT(totalFramesRead <= frameCount); - if (totalFramesRead == frameCount) { - break; - } - - /* Getting here means there's more to process. */ - continue; - } - - /* Getting here means there isn't enough any data left to send to the client which means we need to wait for more. */ - if (WaitForSingleObject((HANDLE)pDevice->winmm.hEventCapture, INFINITE) != WAIT_OBJECT_0) { - result = MA_ERROR; - break; - } - - /* Something happened. If the next buffer has been marked as done we need to reset a bit of state. */ - if ((pWAVEHDR[pDevice->winmm.iNextHeaderCapture].dwFlags & MA_WHDR_DONE) != 0) { - pWAVEHDR[pDevice->winmm.iNextHeaderCapture].dwUser = 0; /* 0 = unlocked (make it available for reading). */ - pDevice->winmm.headerFramesConsumedCapture = 0; - } - - /* If the device has been stopped we need to break. */ - if (ma_device_get_state(pDevice) != ma_device_state_started) { - break; - } - } - - if (pFramesRead != NULL) { - *pFramesRead = totalFramesRead; - } - - return result; -} - -static ma_result ma_context_uninit__winmm(ma_context* pContext) -{ - MA_ASSERT(pContext != NULL); - MA_ASSERT(pContext->backend == ma_backend_winmm); - - ma_dlclose(ma_context_get_log(pContext), pContext->winmm.hWinMM); - return MA_SUCCESS; -} - -static ma_result ma_context_init__winmm(ma_context* pContext, const ma_context_config* pConfig, ma_backend_callbacks* pCallbacks) -{ - MA_ASSERT(pContext != NULL); - - (void)pConfig; - - pContext->winmm.hWinMM = ma_dlopen(ma_context_get_log(pContext), "winmm.dll"); - if (pContext->winmm.hWinMM == NULL) { - return MA_NO_BACKEND; - } - - pContext->winmm.waveOutGetNumDevs = ma_dlsym(ma_context_get_log(pContext), pContext->winmm.hWinMM, "waveOutGetNumDevs"); - pContext->winmm.waveOutGetDevCapsA = ma_dlsym(ma_context_get_log(pContext), pContext->winmm.hWinMM, "waveOutGetDevCapsA"); - pContext->winmm.waveOutOpen = ma_dlsym(ma_context_get_log(pContext), pContext->winmm.hWinMM, "waveOutOpen"); - pContext->winmm.waveOutClose = ma_dlsym(ma_context_get_log(pContext), pContext->winmm.hWinMM, "waveOutClose"); - pContext->winmm.waveOutPrepareHeader = ma_dlsym(ma_context_get_log(pContext), pContext->winmm.hWinMM, "waveOutPrepareHeader"); - pContext->winmm.waveOutUnprepareHeader = ma_dlsym(ma_context_get_log(pContext), pContext->winmm.hWinMM, "waveOutUnprepareHeader"); - pContext->winmm.waveOutWrite = ma_dlsym(ma_context_get_log(pContext), pContext->winmm.hWinMM, "waveOutWrite"); - pContext->winmm.waveOutReset = ma_dlsym(ma_context_get_log(pContext), pContext->winmm.hWinMM, "waveOutReset"); - pContext->winmm.waveInGetNumDevs = ma_dlsym(ma_context_get_log(pContext), pContext->winmm.hWinMM, "waveInGetNumDevs"); - pContext->winmm.waveInGetDevCapsA = ma_dlsym(ma_context_get_log(pContext), pContext->winmm.hWinMM, "waveInGetDevCapsA"); - pContext->winmm.waveInOpen = ma_dlsym(ma_context_get_log(pContext), pContext->winmm.hWinMM, "waveInOpen"); - pContext->winmm.waveInClose = ma_dlsym(ma_context_get_log(pContext), pContext->winmm.hWinMM, "waveInClose"); - pContext->winmm.waveInPrepareHeader = ma_dlsym(ma_context_get_log(pContext), pContext->winmm.hWinMM, "waveInPrepareHeader"); - pContext->winmm.waveInUnprepareHeader = ma_dlsym(ma_context_get_log(pContext), pContext->winmm.hWinMM, "waveInUnprepareHeader"); - pContext->winmm.waveInAddBuffer = ma_dlsym(ma_context_get_log(pContext), pContext->winmm.hWinMM, "waveInAddBuffer"); - pContext->winmm.waveInStart = ma_dlsym(ma_context_get_log(pContext), pContext->winmm.hWinMM, "waveInStart"); - pContext->winmm.waveInReset = ma_dlsym(ma_context_get_log(pContext), pContext->winmm.hWinMM, "waveInReset"); - - pCallbacks->onContextInit = ma_context_init__winmm; - pCallbacks->onContextUninit = ma_context_uninit__winmm; - pCallbacks->onContextEnumerateDevices = ma_context_enumerate_devices__winmm; - pCallbacks->onContextGetDeviceInfo = ma_context_get_device_info__winmm; - pCallbacks->onDeviceInit = ma_device_init__winmm; - pCallbacks->onDeviceUninit = ma_device_uninit__winmm; - pCallbacks->onDeviceStart = ma_device_start__winmm; - pCallbacks->onDeviceStop = ma_device_stop__winmm; - pCallbacks->onDeviceRead = ma_device_read__winmm; - pCallbacks->onDeviceWrite = ma_device_write__winmm; - pCallbacks->onDeviceDataLoop = NULL; /* This is a blocking read-write API, so this can be NULL since miniaudio will manage the audio thread for us. */ - - return MA_SUCCESS; -} -#endif - - - - -/****************************************************************************** - -ALSA Backend - -******************************************************************************/ -#ifdef MA_HAS_ALSA - -#include /* poll(), struct pollfd */ -#include /* eventfd() */ - -#ifdef MA_NO_RUNTIME_LINKING - -/* asoundlib.h marks some functions with "inline" which isn't always supported. Need to emulate it. */ -#if !defined(__cplusplus) - #if defined(__STRICT_ANSI__) - #if !defined(inline) - #define inline __inline__ __attribute__((always_inline)) - #define MA_INLINE_DEFINED - #endif - #endif -#endif -#include -#if defined(MA_INLINE_DEFINED) - #undef inline - #undef MA_INLINE_DEFINED -#endif - -typedef snd_pcm_uframes_t ma_snd_pcm_uframes_t; -typedef snd_pcm_sframes_t ma_snd_pcm_sframes_t; -typedef snd_pcm_stream_t ma_snd_pcm_stream_t; -typedef snd_pcm_format_t ma_snd_pcm_format_t; -typedef snd_pcm_access_t ma_snd_pcm_access_t; -typedef snd_pcm_t ma_snd_pcm_t; -typedef snd_pcm_hw_params_t ma_snd_pcm_hw_params_t; -typedef snd_pcm_sw_params_t ma_snd_pcm_sw_params_t; -typedef snd_pcm_format_mask_t ma_snd_pcm_format_mask_t; -typedef snd_pcm_info_t ma_snd_pcm_info_t; -typedef snd_pcm_channel_area_t ma_snd_pcm_channel_area_t; -typedef snd_pcm_chmap_t ma_snd_pcm_chmap_t; -typedef snd_pcm_state_t ma_snd_pcm_state_t; - -/* snd_pcm_stream_t */ -#define MA_SND_PCM_STREAM_PLAYBACK SND_PCM_STREAM_PLAYBACK -#define MA_SND_PCM_STREAM_CAPTURE SND_PCM_STREAM_CAPTURE - -/* snd_pcm_format_t */ -#define MA_SND_PCM_FORMAT_UNKNOWN SND_PCM_FORMAT_UNKNOWN -#define MA_SND_PCM_FORMAT_U8 SND_PCM_FORMAT_U8 -#define MA_SND_PCM_FORMAT_S16_LE SND_PCM_FORMAT_S16_LE -#define MA_SND_PCM_FORMAT_S16_BE SND_PCM_FORMAT_S16_BE -#define MA_SND_PCM_FORMAT_S24_LE SND_PCM_FORMAT_S24_LE -#define MA_SND_PCM_FORMAT_S24_BE SND_PCM_FORMAT_S24_BE -#define MA_SND_PCM_FORMAT_S32_LE SND_PCM_FORMAT_S32_LE -#define MA_SND_PCM_FORMAT_S32_BE SND_PCM_FORMAT_S32_BE -#define MA_SND_PCM_FORMAT_FLOAT_LE SND_PCM_FORMAT_FLOAT_LE -#define MA_SND_PCM_FORMAT_FLOAT_BE SND_PCM_FORMAT_FLOAT_BE -#define MA_SND_PCM_FORMAT_FLOAT64_LE SND_PCM_FORMAT_FLOAT64_LE -#define MA_SND_PCM_FORMAT_FLOAT64_BE SND_PCM_FORMAT_FLOAT64_BE -#define MA_SND_PCM_FORMAT_MU_LAW SND_PCM_FORMAT_MU_LAW -#define MA_SND_PCM_FORMAT_A_LAW SND_PCM_FORMAT_A_LAW -#define MA_SND_PCM_FORMAT_S24_3LE SND_PCM_FORMAT_S24_3LE -#define MA_SND_PCM_FORMAT_S24_3BE SND_PCM_FORMAT_S24_3BE - -/* ma_snd_pcm_access_t */ -#define MA_SND_PCM_ACCESS_MMAP_INTERLEAVED SND_PCM_ACCESS_MMAP_INTERLEAVED -#define MA_SND_PCM_ACCESS_MMAP_NONINTERLEAVED SND_PCM_ACCESS_MMAP_NONINTERLEAVED -#define MA_SND_PCM_ACCESS_MMAP_COMPLEX SND_PCM_ACCESS_MMAP_COMPLEX -#define MA_SND_PCM_ACCESS_RW_INTERLEAVED SND_PCM_ACCESS_RW_INTERLEAVED -#define MA_SND_PCM_ACCESS_RW_NONINTERLEAVED SND_PCM_ACCESS_RW_NONINTERLEAVED - -/* Channel positions. */ -#define MA_SND_CHMAP_UNKNOWN SND_CHMAP_UNKNOWN -#define MA_SND_CHMAP_NA SND_CHMAP_NA -#define MA_SND_CHMAP_MONO SND_CHMAP_MONO -#define MA_SND_CHMAP_FL SND_CHMAP_FL -#define MA_SND_CHMAP_FR SND_CHMAP_FR -#define MA_SND_CHMAP_RL SND_CHMAP_RL -#define MA_SND_CHMAP_RR SND_CHMAP_RR -#define MA_SND_CHMAP_FC SND_CHMAP_FC -#define MA_SND_CHMAP_LFE SND_CHMAP_LFE -#define MA_SND_CHMAP_SL SND_CHMAP_SL -#define MA_SND_CHMAP_SR SND_CHMAP_SR -#define MA_SND_CHMAP_RC SND_CHMAP_RC -#define MA_SND_CHMAP_FLC SND_CHMAP_FLC -#define MA_SND_CHMAP_FRC SND_CHMAP_FRC -#define MA_SND_CHMAP_RLC SND_CHMAP_RLC -#define MA_SND_CHMAP_RRC SND_CHMAP_RRC -#define MA_SND_CHMAP_FLW SND_CHMAP_FLW -#define MA_SND_CHMAP_FRW SND_CHMAP_FRW -#define MA_SND_CHMAP_FLH SND_CHMAP_FLH -#define MA_SND_CHMAP_FCH SND_CHMAP_FCH -#define MA_SND_CHMAP_FRH SND_CHMAP_FRH -#define MA_SND_CHMAP_TC SND_CHMAP_TC -#define MA_SND_CHMAP_TFL SND_CHMAP_TFL -#define MA_SND_CHMAP_TFR SND_CHMAP_TFR -#define MA_SND_CHMAP_TFC SND_CHMAP_TFC -#define MA_SND_CHMAP_TRL SND_CHMAP_TRL -#define MA_SND_CHMAP_TRR SND_CHMAP_TRR -#define MA_SND_CHMAP_TRC SND_CHMAP_TRC -#define MA_SND_CHMAP_TFLC SND_CHMAP_TFLC -#define MA_SND_CHMAP_TFRC SND_CHMAP_TFRC -#define MA_SND_CHMAP_TSL SND_CHMAP_TSL -#define MA_SND_CHMAP_TSR SND_CHMAP_TSR -#define MA_SND_CHMAP_LLFE SND_CHMAP_LLFE -#define MA_SND_CHMAP_RLFE SND_CHMAP_RLFE -#define MA_SND_CHMAP_BC SND_CHMAP_BC -#define MA_SND_CHMAP_BLC SND_CHMAP_BLC -#define MA_SND_CHMAP_BRC SND_CHMAP_BRC - -/* Open mode flags. */ -#define MA_SND_PCM_NO_AUTO_RESAMPLE SND_PCM_NO_AUTO_RESAMPLE -#define MA_SND_PCM_NO_AUTO_CHANNELS SND_PCM_NO_AUTO_CHANNELS -#define MA_SND_PCM_NO_AUTO_FORMAT SND_PCM_NO_AUTO_FORMAT -#else -#include /* For EPIPE, etc. */ -typedef unsigned long ma_snd_pcm_uframes_t; -typedef long ma_snd_pcm_sframes_t; -typedef int ma_snd_pcm_stream_t; -typedef int ma_snd_pcm_format_t; -typedef int ma_snd_pcm_access_t; -typedef int ma_snd_pcm_state_t; -typedef struct ma_snd_pcm_t ma_snd_pcm_t; -typedef struct ma_snd_pcm_hw_params_t ma_snd_pcm_hw_params_t; -typedef struct ma_snd_pcm_sw_params_t ma_snd_pcm_sw_params_t; -typedef struct ma_snd_pcm_format_mask_t ma_snd_pcm_format_mask_t; -typedef struct ma_snd_pcm_info_t ma_snd_pcm_info_t; -typedef struct -{ - void* addr; - unsigned int first; - unsigned int step; -} ma_snd_pcm_channel_area_t; -typedef struct -{ - unsigned int channels; - unsigned int pos[1]; -} ma_snd_pcm_chmap_t; - -/* snd_pcm_state_t */ -#define MA_SND_PCM_STATE_OPEN 0 -#define MA_SND_PCM_STATE_SETUP 1 -#define MA_SND_PCM_STATE_PREPARED 2 -#define MA_SND_PCM_STATE_RUNNING 3 -#define MA_SND_PCM_STATE_XRUN 4 -#define MA_SND_PCM_STATE_DRAINING 5 -#define MA_SND_PCM_STATE_PAUSED 6 -#define MA_SND_PCM_STATE_SUSPENDED 7 -#define MA_SND_PCM_STATE_DISCONNECTED 8 - -/* snd_pcm_stream_t */ -#define MA_SND_PCM_STREAM_PLAYBACK 0 -#define MA_SND_PCM_STREAM_CAPTURE 1 - -/* snd_pcm_format_t */ -#define MA_SND_PCM_FORMAT_UNKNOWN -1 -#define MA_SND_PCM_FORMAT_U8 1 -#define MA_SND_PCM_FORMAT_S16_LE 2 -#define MA_SND_PCM_FORMAT_S16_BE 3 -#define MA_SND_PCM_FORMAT_S24_LE 6 -#define MA_SND_PCM_FORMAT_S24_BE 7 -#define MA_SND_PCM_FORMAT_S32_LE 10 -#define MA_SND_PCM_FORMAT_S32_BE 11 -#define MA_SND_PCM_FORMAT_FLOAT_LE 14 -#define MA_SND_PCM_FORMAT_FLOAT_BE 15 -#define MA_SND_PCM_FORMAT_FLOAT64_LE 16 -#define MA_SND_PCM_FORMAT_FLOAT64_BE 17 -#define MA_SND_PCM_FORMAT_MU_LAW 20 -#define MA_SND_PCM_FORMAT_A_LAW 21 -#define MA_SND_PCM_FORMAT_S24_3LE 32 -#define MA_SND_PCM_FORMAT_S24_3BE 33 - -/* snd_pcm_access_t */ -#define MA_SND_PCM_ACCESS_MMAP_INTERLEAVED 0 -#define MA_SND_PCM_ACCESS_MMAP_NONINTERLEAVED 1 -#define MA_SND_PCM_ACCESS_MMAP_COMPLEX 2 -#define MA_SND_PCM_ACCESS_RW_INTERLEAVED 3 -#define MA_SND_PCM_ACCESS_RW_NONINTERLEAVED 4 - -/* Channel positions. */ -#define MA_SND_CHMAP_UNKNOWN 0 -#define MA_SND_CHMAP_NA 1 -#define MA_SND_CHMAP_MONO 2 -#define MA_SND_CHMAP_FL 3 -#define MA_SND_CHMAP_FR 4 -#define MA_SND_CHMAP_RL 5 -#define MA_SND_CHMAP_RR 6 -#define MA_SND_CHMAP_FC 7 -#define MA_SND_CHMAP_LFE 8 -#define MA_SND_CHMAP_SL 9 -#define MA_SND_CHMAP_SR 10 -#define MA_SND_CHMAP_RC 11 -#define MA_SND_CHMAP_FLC 12 -#define MA_SND_CHMAP_FRC 13 -#define MA_SND_CHMAP_RLC 14 -#define MA_SND_CHMAP_RRC 15 -#define MA_SND_CHMAP_FLW 16 -#define MA_SND_CHMAP_FRW 17 -#define MA_SND_CHMAP_FLH 18 -#define MA_SND_CHMAP_FCH 19 -#define MA_SND_CHMAP_FRH 20 -#define MA_SND_CHMAP_TC 21 -#define MA_SND_CHMAP_TFL 22 -#define MA_SND_CHMAP_TFR 23 -#define MA_SND_CHMAP_TFC 24 -#define MA_SND_CHMAP_TRL 25 -#define MA_SND_CHMAP_TRR 26 -#define MA_SND_CHMAP_TRC 27 -#define MA_SND_CHMAP_TFLC 28 -#define MA_SND_CHMAP_TFRC 29 -#define MA_SND_CHMAP_TSL 30 -#define MA_SND_CHMAP_TSR 31 -#define MA_SND_CHMAP_LLFE 32 -#define MA_SND_CHMAP_RLFE 33 -#define MA_SND_CHMAP_BC 34 -#define MA_SND_CHMAP_BLC 35 -#define MA_SND_CHMAP_BRC 36 - -/* Open mode flags. */ -#define MA_SND_PCM_NO_AUTO_RESAMPLE 0x00010000 -#define MA_SND_PCM_NO_AUTO_CHANNELS 0x00020000 -#define MA_SND_PCM_NO_AUTO_FORMAT 0x00040000 -#endif - -typedef int (* ma_snd_pcm_open_proc) (ma_snd_pcm_t **pcm, const char *name, ma_snd_pcm_stream_t stream, int mode); -typedef int (* ma_snd_pcm_close_proc) (ma_snd_pcm_t *pcm); -typedef size_t (* ma_snd_pcm_hw_params_sizeof_proc) (void); -typedef int (* ma_snd_pcm_hw_params_any_proc) (ma_snd_pcm_t *pcm, ma_snd_pcm_hw_params_t *params); -typedef int (* ma_snd_pcm_hw_params_set_format_proc) (ma_snd_pcm_t *pcm, ma_snd_pcm_hw_params_t *params, ma_snd_pcm_format_t val); -typedef int (* ma_snd_pcm_hw_params_set_format_first_proc) (ma_snd_pcm_t *pcm, ma_snd_pcm_hw_params_t *params, ma_snd_pcm_format_t *format); -typedef void (* ma_snd_pcm_hw_params_get_format_mask_proc) (ma_snd_pcm_hw_params_t *params, ma_snd_pcm_format_mask_t *mask); -typedef int (* ma_snd_pcm_hw_params_set_channels_proc) (ma_snd_pcm_t *pcm, ma_snd_pcm_hw_params_t *params, unsigned int val); -typedef int (* ma_snd_pcm_hw_params_set_channels_near_proc) (ma_snd_pcm_t *pcm, ma_snd_pcm_hw_params_t *params, unsigned int *val); -typedef int (* ma_snd_pcm_hw_params_set_channels_minmax_proc) (ma_snd_pcm_t *pcm, ma_snd_pcm_hw_params_t *params, unsigned int *minimum, unsigned int *maximum); -typedef int (* ma_snd_pcm_hw_params_set_rate_resample_proc) (ma_snd_pcm_t *pcm, ma_snd_pcm_hw_params_t *params, unsigned int val); -typedef int (* ma_snd_pcm_hw_params_set_rate_proc) (ma_snd_pcm_t *pcm, ma_snd_pcm_hw_params_t *params, unsigned int val, int dir); -typedef int (* ma_snd_pcm_hw_params_set_rate_near_proc) (ma_snd_pcm_t *pcm, ma_snd_pcm_hw_params_t *params, unsigned int *val, int *dir); -typedef int (* ma_snd_pcm_hw_params_set_buffer_size_near_proc)(ma_snd_pcm_t *pcm, ma_snd_pcm_hw_params_t *params, ma_snd_pcm_uframes_t *val); -typedef int (* ma_snd_pcm_hw_params_set_periods_near_proc) (ma_snd_pcm_t *pcm, ma_snd_pcm_hw_params_t *params, unsigned int *val, int *dir); -typedef int (* ma_snd_pcm_hw_params_set_access_proc) (ma_snd_pcm_t *pcm, ma_snd_pcm_hw_params_t *params, ma_snd_pcm_access_t _access); -typedef int (* ma_snd_pcm_hw_params_get_format_proc) (const ma_snd_pcm_hw_params_t *params, ma_snd_pcm_format_t *format); -typedef int (* ma_snd_pcm_hw_params_get_channels_proc) (const ma_snd_pcm_hw_params_t *params, unsigned int *val); -typedef int (* ma_snd_pcm_hw_params_get_channels_min_proc) (const ma_snd_pcm_hw_params_t *params, unsigned int *val); -typedef int (* ma_snd_pcm_hw_params_get_channels_max_proc) (const ma_snd_pcm_hw_params_t *params, unsigned int *val); -typedef int (* ma_snd_pcm_hw_params_get_rate_proc) (const ma_snd_pcm_hw_params_t *params, unsigned int *rate, int *dir); -typedef int (* ma_snd_pcm_hw_params_get_rate_min_proc) (const ma_snd_pcm_hw_params_t *params, unsigned int *rate, int *dir); -typedef int (* ma_snd_pcm_hw_params_get_rate_max_proc) (const ma_snd_pcm_hw_params_t *params, unsigned int *rate, int *dir); -typedef int (* ma_snd_pcm_hw_params_get_buffer_size_proc) (const ma_snd_pcm_hw_params_t *params, ma_snd_pcm_uframes_t *val); -typedef int (* ma_snd_pcm_hw_params_get_periods_proc) (const ma_snd_pcm_hw_params_t *params, unsigned int *val, int *dir); -typedef int (* ma_snd_pcm_hw_params_get_access_proc) (const ma_snd_pcm_hw_params_t *params, ma_snd_pcm_access_t *_access); -typedef int (* ma_snd_pcm_hw_params_test_format_proc) (ma_snd_pcm_t *pcm, ma_snd_pcm_hw_params_t *params, ma_snd_pcm_format_t val); -typedef int (* ma_snd_pcm_hw_params_test_channels_proc) (ma_snd_pcm_t *pcm, ma_snd_pcm_hw_params_t *params, unsigned int val); -typedef int (* ma_snd_pcm_hw_params_test_rate_proc) (ma_snd_pcm_t *pcm, ma_snd_pcm_hw_params_t *params, unsigned int val, int dir); -typedef int (* ma_snd_pcm_hw_params_proc) (ma_snd_pcm_t *pcm, ma_snd_pcm_hw_params_t *params); -typedef size_t (* ma_snd_pcm_sw_params_sizeof_proc) (void); -typedef int (* ma_snd_pcm_sw_params_current_proc) (ma_snd_pcm_t *pcm, ma_snd_pcm_sw_params_t *params); -typedef int (* ma_snd_pcm_sw_params_get_boundary_proc) (const ma_snd_pcm_sw_params_t *params, ma_snd_pcm_uframes_t* val); -typedef int (* ma_snd_pcm_sw_params_set_avail_min_proc) (ma_snd_pcm_t *pcm, ma_snd_pcm_sw_params_t *params, ma_snd_pcm_uframes_t val); -typedef int (* ma_snd_pcm_sw_params_set_start_threshold_proc) (ma_snd_pcm_t *pcm, ma_snd_pcm_sw_params_t *params, ma_snd_pcm_uframes_t val); -typedef int (* ma_snd_pcm_sw_params_set_stop_threshold_proc) (ma_snd_pcm_t *pcm, ma_snd_pcm_sw_params_t *params, ma_snd_pcm_uframes_t val); -typedef int (* ma_snd_pcm_sw_params_proc) (ma_snd_pcm_t *pcm, ma_snd_pcm_sw_params_t *params); -typedef size_t (* ma_snd_pcm_format_mask_sizeof_proc) (void); -typedef int (* ma_snd_pcm_format_mask_test_proc) (const ma_snd_pcm_format_mask_t *mask, ma_snd_pcm_format_t val); -typedef ma_snd_pcm_chmap_t * (* ma_snd_pcm_get_chmap_proc) (ma_snd_pcm_t *pcm); -typedef ma_snd_pcm_state_t (* ma_snd_pcm_state_proc) (ma_snd_pcm_t *pcm); -typedef int (* ma_snd_pcm_prepare_proc) (ma_snd_pcm_t *pcm); -typedef int (* ma_snd_pcm_start_proc) (ma_snd_pcm_t *pcm); -typedef int (* ma_snd_pcm_drop_proc) (ma_snd_pcm_t *pcm); -typedef int (* ma_snd_pcm_drain_proc) (ma_snd_pcm_t *pcm); -typedef int (* ma_snd_pcm_reset_proc) (ma_snd_pcm_t *pcm); -typedef int (* ma_snd_device_name_hint_proc) (int card, const char *iface, void ***hints); -typedef char * (* ma_snd_device_name_get_hint_proc) (const void *hint, const char *id); -typedef int (* ma_snd_card_get_index_proc) (const char *name); -typedef int (* ma_snd_device_name_free_hint_proc) (void **hints); -typedef int (* ma_snd_pcm_mmap_begin_proc) (ma_snd_pcm_t *pcm, const ma_snd_pcm_channel_area_t **areas, ma_snd_pcm_uframes_t *offset, ma_snd_pcm_uframes_t *frames); -typedef ma_snd_pcm_sframes_t (* ma_snd_pcm_mmap_commit_proc) (ma_snd_pcm_t *pcm, ma_snd_pcm_uframes_t offset, ma_snd_pcm_uframes_t frames); -typedef int (* ma_snd_pcm_recover_proc) (ma_snd_pcm_t *pcm, int err, int silent); -typedef ma_snd_pcm_sframes_t (* ma_snd_pcm_readi_proc) (ma_snd_pcm_t *pcm, void *buffer, ma_snd_pcm_uframes_t size); -typedef ma_snd_pcm_sframes_t (* ma_snd_pcm_writei_proc) (ma_snd_pcm_t *pcm, const void *buffer, ma_snd_pcm_uframes_t size); -typedef ma_snd_pcm_sframes_t (* ma_snd_pcm_avail_proc) (ma_snd_pcm_t *pcm); -typedef ma_snd_pcm_sframes_t (* ma_snd_pcm_avail_update_proc) (ma_snd_pcm_t *pcm); -typedef int (* ma_snd_pcm_wait_proc) (ma_snd_pcm_t *pcm, int timeout); -typedef int (* ma_snd_pcm_nonblock_proc) (ma_snd_pcm_t *pcm, int nonblock); -typedef int (* ma_snd_pcm_info_proc) (ma_snd_pcm_t *pcm, ma_snd_pcm_info_t* info); -typedef size_t (* ma_snd_pcm_info_sizeof_proc) (void); -typedef const char* (* ma_snd_pcm_info_get_name_proc) (const ma_snd_pcm_info_t* info); -typedef int (* ma_snd_pcm_poll_descriptors_proc) (ma_snd_pcm_t *pcm, struct pollfd *pfds, unsigned int space); -typedef int (* ma_snd_pcm_poll_descriptors_count_proc) (ma_snd_pcm_t *pcm); -typedef int (* ma_snd_pcm_poll_descriptors_revents_proc) (ma_snd_pcm_t *pcm, struct pollfd *pfds, unsigned int nfds, unsigned short *revents); -typedef int (* ma_snd_config_update_free_global_proc) (void); - -/* This array specifies each of the common devices that can be used for both playback and capture. */ -static const char* g_maCommonDeviceNamesALSA[] = { - "default", - "null", - "pulse", - "jack" -}; - -/* This array allows us to blacklist specific playback devices. */ -static const char* g_maBlacklistedPlaybackDeviceNamesALSA[] = { - "" -}; - -/* This array allows us to blacklist specific capture devices. */ -static const char* g_maBlacklistedCaptureDeviceNamesALSA[] = { - "" -}; - - -static ma_snd_pcm_format_t ma_convert_ma_format_to_alsa_format(ma_format format) -{ - ma_snd_pcm_format_t ALSAFormats[] = { - MA_SND_PCM_FORMAT_UNKNOWN, /* ma_format_unknown */ - MA_SND_PCM_FORMAT_U8, /* ma_format_u8 */ - MA_SND_PCM_FORMAT_S16_LE, /* ma_format_s16 */ - MA_SND_PCM_FORMAT_S24_3LE, /* ma_format_s24 */ - MA_SND_PCM_FORMAT_S32_LE, /* ma_format_s32 */ - MA_SND_PCM_FORMAT_FLOAT_LE /* ma_format_f32 */ - }; - - if (ma_is_big_endian()) { - ALSAFormats[0] = MA_SND_PCM_FORMAT_UNKNOWN; - ALSAFormats[1] = MA_SND_PCM_FORMAT_U8; - ALSAFormats[2] = MA_SND_PCM_FORMAT_S16_BE; - ALSAFormats[3] = MA_SND_PCM_FORMAT_S24_3BE; - ALSAFormats[4] = MA_SND_PCM_FORMAT_S32_BE; - ALSAFormats[5] = MA_SND_PCM_FORMAT_FLOAT_BE; - } - - return ALSAFormats[format]; -} - -static ma_format ma_format_from_alsa(ma_snd_pcm_format_t formatALSA) -{ - if (ma_is_little_endian()) { - switch (formatALSA) { - case MA_SND_PCM_FORMAT_S16_LE: return ma_format_s16; - case MA_SND_PCM_FORMAT_S24_3LE: return ma_format_s24; - case MA_SND_PCM_FORMAT_S32_LE: return ma_format_s32; - case MA_SND_PCM_FORMAT_FLOAT_LE: return ma_format_f32; - default: break; - } - } else { - switch (formatALSA) { - case MA_SND_PCM_FORMAT_S16_BE: return ma_format_s16; - case MA_SND_PCM_FORMAT_S24_3BE: return ma_format_s24; - case MA_SND_PCM_FORMAT_S32_BE: return ma_format_s32; - case MA_SND_PCM_FORMAT_FLOAT_BE: return ma_format_f32; - default: break; - } - } - - /* Endian agnostic. */ - switch (formatALSA) { - case MA_SND_PCM_FORMAT_U8: return ma_format_u8; - default: return ma_format_unknown; - } -} - -static ma_channel ma_convert_alsa_channel_position_to_ma_channel(unsigned int alsaChannelPos) -{ - switch (alsaChannelPos) - { - case MA_SND_CHMAP_MONO: return MA_CHANNEL_MONO; - case MA_SND_CHMAP_FL: return MA_CHANNEL_FRONT_LEFT; - case MA_SND_CHMAP_FR: return MA_CHANNEL_FRONT_RIGHT; - case MA_SND_CHMAP_RL: return MA_CHANNEL_BACK_LEFT; - case MA_SND_CHMAP_RR: return MA_CHANNEL_BACK_RIGHT; - case MA_SND_CHMAP_FC: return MA_CHANNEL_FRONT_CENTER; - case MA_SND_CHMAP_LFE: return MA_CHANNEL_LFE; - case MA_SND_CHMAP_SL: return MA_CHANNEL_SIDE_LEFT; - case MA_SND_CHMAP_SR: return MA_CHANNEL_SIDE_RIGHT; - case MA_SND_CHMAP_RC: return MA_CHANNEL_BACK_CENTER; - case MA_SND_CHMAP_FLC: return MA_CHANNEL_FRONT_LEFT_CENTER; - case MA_SND_CHMAP_FRC: return MA_CHANNEL_FRONT_RIGHT_CENTER; - case MA_SND_CHMAP_RLC: return 0; - case MA_SND_CHMAP_RRC: return 0; - case MA_SND_CHMAP_FLW: return 0; - case MA_SND_CHMAP_FRW: return 0; - case MA_SND_CHMAP_FLH: return 0; - case MA_SND_CHMAP_FCH: return 0; - case MA_SND_CHMAP_FRH: return 0; - case MA_SND_CHMAP_TC: return MA_CHANNEL_TOP_CENTER; - case MA_SND_CHMAP_TFL: return MA_CHANNEL_TOP_FRONT_LEFT; - case MA_SND_CHMAP_TFR: return MA_CHANNEL_TOP_FRONT_RIGHT; - case MA_SND_CHMAP_TFC: return MA_CHANNEL_TOP_FRONT_CENTER; - case MA_SND_CHMAP_TRL: return MA_CHANNEL_TOP_BACK_LEFT; - case MA_SND_CHMAP_TRR: return MA_CHANNEL_TOP_BACK_RIGHT; - case MA_SND_CHMAP_TRC: return MA_CHANNEL_TOP_BACK_CENTER; - default: break; - } - - return 0; -} - -static ma_bool32 ma_is_common_device_name__alsa(const char* name) -{ - size_t iName; - for (iName = 0; iName < ma_countof(g_maCommonDeviceNamesALSA); ++iName) { - if (ma_strcmp(name, g_maCommonDeviceNamesALSA[iName]) == 0) { - return MA_TRUE; - } - } - - return MA_FALSE; -} - - -static ma_bool32 ma_is_playback_device_blacklisted__alsa(const char* name) -{ - size_t iName; - for (iName = 0; iName < ma_countof(g_maBlacklistedPlaybackDeviceNamesALSA); ++iName) { - if (ma_strcmp(name, g_maBlacklistedPlaybackDeviceNamesALSA[iName]) == 0) { - return MA_TRUE; - } - } - - return MA_FALSE; -} - -static ma_bool32 ma_is_capture_device_blacklisted__alsa(const char* name) -{ - size_t iName; - for (iName = 0; iName < ma_countof(g_maBlacklistedCaptureDeviceNamesALSA); ++iName) { - if (ma_strcmp(name, g_maBlacklistedCaptureDeviceNamesALSA[iName]) == 0) { - return MA_TRUE; - } - } - - return MA_FALSE; -} - -static ma_bool32 ma_is_device_blacklisted__alsa(ma_device_type deviceType, const char* name) -{ - if (deviceType == ma_device_type_playback) { - return ma_is_playback_device_blacklisted__alsa(name); - } else { - return ma_is_capture_device_blacklisted__alsa(name); - } -} - - -static const char* ma_find_char(const char* str, char c, int* index) -{ - int i = 0; - for (;;) { - if (str[i] == '\0') { - if (index) *index = -1; - return NULL; - } - - if (str[i] == c) { - if (index) *index = i; - return str + i; - } - - i += 1; - } - - /* Should never get here, but treat it as though the character was not found to make me feel better inside. */ - if (index) *index = -1; - return NULL; -} - -static ma_bool32 ma_is_device_name_in_hw_format__alsa(const char* hwid) -{ - /* This function is just checking whether or not hwid is in "hw:%d,%d" format. */ - - int commaPos; - const char* dev; - int i; - - if (hwid == NULL) { - return MA_FALSE; - } - - if (hwid[0] != 'h' || hwid[1] != 'w' || hwid[2] != ':') { - return MA_FALSE; - } - - hwid += 3; - - dev = ma_find_char(hwid, ',', &commaPos); - if (dev == NULL) { - return MA_FALSE; - } else { - dev += 1; /* Skip past the ",". */ - } - - /* Check if the part between the ":" and the "," contains only numbers. If not, return false. */ - for (i = 0; i < commaPos; ++i) { - if (hwid[i] < '0' || hwid[i] > '9') { - return MA_FALSE; - } - } - - /* Check if everything after the "," is numeric. If not, return false. */ - i = 0; - while (dev[i] != '\0') { - if (dev[i] < '0' || dev[i] > '9') { - return MA_FALSE; - } - i += 1; - } - - return MA_TRUE; -} - -static int ma_convert_device_name_to_hw_format__alsa(ma_context* pContext, char* dst, size_t dstSize, const char* src) /* Returns 0 on success, non-0 on error. */ -{ - /* src should look something like this: "hw:CARD=I82801AAICH,DEV=0" */ - - int colonPos; - int commaPos; - char card[256]; - const char* dev; - int cardIndex; - - if (dst == NULL) { - return -1; - } - if (dstSize < 7) { - return -1; /* Absolute minimum size of the output buffer is 7 bytes. */ - } - - *dst = '\0'; /* Safety. */ - if (src == NULL) { - return -1; - } - - /* If the input name is already in "hw:%d,%d" format, just return that verbatim. */ - if (ma_is_device_name_in_hw_format__alsa(src)) { - return ma_strcpy_s(dst, dstSize, src); - } - - src = ma_find_char(src, ':', &colonPos); - if (src == NULL) { - return -1; /* Couldn't find a colon */ - } - - dev = ma_find_char(src, ',', &commaPos); - if (dev == NULL) { - dev = "0"; - ma_strncpy_s(card, sizeof(card), src+6, (size_t)-1); /* +6 = ":CARD=" */ - } else { - dev = dev + 5; /* +5 = ",DEV=" */ - ma_strncpy_s(card, sizeof(card), src+6, commaPos-6); /* +6 = ":CARD=" */ - } - - cardIndex = ((ma_snd_card_get_index_proc)pContext->alsa.snd_card_get_index)(card); - if (cardIndex < 0) { - return -2; /* Failed to retrieve the card index. */ - } - - - /* Construction. */ - dst[0] = 'h'; dst[1] = 'w'; dst[2] = ':'; - if (ma_itoa_s(cardIndex, dst+3, dstSize-3, 10) != 0) { - return -3; - } - if (ma_strcat_s(dst, dstSize, ",") != 0) { - return -3; - } - if (ma_strcat_s(dst, dstSize, dev) != 0) { - return -3; - } - - return 0; -} - -static ma_bool32 ma_does_id_exist_in_list__alsa(ma_device_id* pUniqueIDs, ma_uint32 count, const char* pHWID) -{ - ma_uint32 i; - - MA_ASSERT(pHWID != NULL); - - for (i = 0; i < count; ++i) { - if (ma_strcmp(pUniqueIDs[i].alsa, pHWID) == 0) { - return MA_TRUE; - } - } - - return MA_FALSE; -} - - -static ma_result ma_context_open_pcm__alsa(ma_context* pContext, ma_share_mode shareMode, ma_device_type deviceType, const ma_device_id* pDeviceID, int openMode, ma_snd_pcm_t** ppPCM) -{ - ma_snd_pcm_t* pPCM; - ma_snd_pcm_stream_t stream; - - MA_ASSERT(pContext != NULL); - MA_ASSERT(ppPCM != NULL); - - *ppPCM = NULL; - pPCM = NULL; - - stream = (deviceType == ma_device_type_playback) ? MA_SND_PCM_STREAM_PLAYBACK : MA_SND_PCM_STREAM_CAPTURE; - - if (pDeviceID == NULL) { - ma_bool32 isDeviceOpen; - size_t i; - - /* - We're opening the default device. I don't know if trying anything other than "default" is necessary, but it makes - me feel better to try as hard as we can get to get _something_ working. - */ - const char* defaultDeviceNames[] = { - "default", - NULL, - NULL, - NULL, - NULL, - NULL, - NULL - }; - - if (shareMode == ma_share_mode_exclusive) { - defaultDeviceNames[1] = "hw"; - defaultDeviceNames[2] = "hw:0"; - defaultDeviceNames[3] = "hw:0,0"; - } else { - if (deviceType == ma_device_type_playback) { - defaultDeviceNames[1] = "dmix"; - defaultDeviceNames[2] = "dmix:0"; - defaultDeviceNames[3] = "dmix:0,0"; - } else { - defaultDeviceNames[1] = "dsnoop"; - defaultDeviceNames[2] = "dsnoop:0"; - defaultDeviceNames[3] = "dsnoop:0,0"; - } - defaultDeviceNames[4] = "hw"; - defaultDeviceNames[5] = "hw:0"; - defaultDeviceNames[6] = "hw:0,0"; - } - - isDeviceOpen = MA_FALSE; - for (i = 0; i < ma_countof(defaultDeviceNames); ++i) { - if (defaultDeviceNames[i] != NULL && defaultDeviceNames[i][0] != '\0') { - if (((ma_snd_pcm_open_proc)pContext->alsa.snd_pcm_open)(&pPCM, defaultDeviceNames[i], stream, openMode) == 0) { - isDeviceOpen = MA_TRUE; - break; - } - } - } - - if (!isDeviceOpen) { - ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[ALSA] snd_pcm_open() failed when trying to open an appropriate default device."); - return MA_FAILED_TO_OPEN_BACKEND_DEVICE; - } - } else { - /* - We're trying to open a specific device. There's a few things to consider here: - - miniaudio recognizes a special format of device id that excludes the "hw", "dmix", etc. prefix. It looks like this: ":0,0", ":0,1", etc. When - an ID of this format is specified, it indicates to miniaudio that it can try different combinations of plugins ("hw", "dmix", etc.) until it - finds an appropriate one that works. This comes in very handy when trying to open a device in shared mode ("dmix"), vs exclusive mode ("hw"). - */ - - /* May end up needing to make small adjustments to the ID, so make a copy. */ - ma_device_id deviceID = *pDeviceID; - int resultALSA = -ENODEV; - - if (deviceID.alsa[0] != ':') { - /* The ID is not in ":0,0" format. Use the ID exactly as-is. */ - resultALSA = ((ma_snd_pcm_open_proc)pContext->alsa.snd_pcm_open)(&pPCM, deviceID.alsa, stream, openMode); - } else { - char hwid[256]; - - /* The ID is in ":0,0" format. Try different plugins depending on the shared mode. */ - if (deviceID.alsa[1] == '\0') { - deviceID.alsa[0] = '\0'; /* An ID of ":" should be converted to "". */ - } - - if (shareMode == ma_share_mode_shared) { - if (deviceType == ma_device_type_playback) { - ma_strcpy_s(hwid, sizeof(hwid), "dmix"); - } else { - ma_strcpy_s(hwid, sizeof(hwid), "dsnoop"); - } - - if (ma_strcat_s(hwid, sizeof(hwid), deviceID.alsa) == 0) { - resultALSA = ((ma_snd_pcm_open_proc)pContext->alsa.snd_pcm_open)(&pPCM, hwid, stream, openMode); - } - } - - /* If at this point we still don't have an open device it means we're either preferencing exclusive mode or opening with "dmix"/"dsnoop" failed. */ - if (resultALSA != 0) { - ma_strcpy_s(hwid, sizeof(hwid), "hw"); - if (ma_strcat_s(hwid, sizeof(hwid), deviceID.alsa) == 0) { - resultALSA = ((ma_snd_pcm_open_proc)pContext->alsa.snd_pcm_open)(&pPCM, hwid, stream, openMode); - } - } - } - - if (resultALSA < 0) { - ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[ALSA] snd_pcm_open() failed."); - return ma_result_from_errno(-resultALSA); - } - } - - *ppPCM = pPCM; - return MA_SUCCESS; -} - - -static ma_result ma_context_enumerate_devices__alsa(ma_context* pContext, ma_enum_devices_callback_proc callback, void* pUserData) -{ - int resultALSA; - ma_bool32 cbResult = MA_TRUE; - char** ppDeviceHints; - ma_device_id* pUniqueIDs = NULL; - ma_uint32 uniqueIDCount = 0; - char** ppNextDeviceHint; - - MA_ASSERT(pContext != NULL); - MA_ASSERT(callback != NULL); - - ma_mutex_lock(&pContext->alsa.internalDeviceEnumLock); - - resultALSA = ((ma_snd_device_name_hint_proc)pContext->alsa.snd_device_name_hint)(-1, "pcm", (void***)&ppDeviceHints); - if (resultALSA < 0) { - ma_mutex_unlock(&pContext->alsa.internalDeviceEnumLock); - return ma_result_from_errno(-resultALSA); - } - - ppNextDeviceHint = ppDeviceHints; - while (*ppNextDeviceHint != NULL) { - char* NAME = ((ma_snd_device_name_get_hint_proc)pContext->alsa.snd_device_name_get_hint)(*ppNextDeviceHint, "NAME"); - char* DESC = ((ma_snd_device_name_get_hint_proc)pContext->alsa.snd_device_name_get_hint)(*ppNextDeviceHint, "DESC"); - char* IOID = ((ma_snd_device_name_get_hint_proc)pContext->alsa.snd_device_name_get_hint)(*ppNextDeviceHint, "IOID"); - ma_device_type deviceType = ma_device_type_playback; - ma_bool32 stopEnumeration = MA_FALSE; - char hwid[sizeof(pUniqueIDs->alsa)]; - ma_device_info deviceInfo; - - if ((IOID == NULL || ma_strcmp(IOID, "Output") == 0)) { - deviceType = ma_device_type_playback; - } - if ((IOID != NULL && ma_strcmp(IOID, "Input" ) == 0)) { - deviceType = ma_device_type_capture; - } - - if (NAME != NULL) { - if (pContext->alsa.useVerboseDeviceEnumeration) { - /* Verbose mode. Use the name exactly as-is. */ - ma_strncpy_s(hwid, sizeof(hwid), NAME, (size_t)-1); - } else { - /* Simplified mode. Use ":%d,%d" format. */ - if (ma_convert_device_name_to_hw_format__alsa(pContext, hwid, sizeof(hwid), NAME) == 0) { - /* - At this point, hwid looks like "hw:0,0". In simplified enumeration mode, we actually want to strip off the - plugin name so it looks like ":0,0". The reason for this is that this special format is detected at device - initialization time and is used as an indicator to try to use the most appropriate plugin depending on the - device type and sharing mode. - */ - char* dst = hwid; - char* src = hwid+2; - while ((*dst++ = *src++)); - } else { - /* Conversion to "hw:%d,%d" failed. Just use the name as-is. */ - ma_strncpy_s(hwid, sizeof(hwid), NAME, (size_t)-1); - } - - if (ma_does_id_exist_in_list__alsa(pUniqueIDs, uniqueIDCount, hwid)) { - goto next_device; /* The device has already been enumerated. Move on to the next one. */ - } else { - /* The device has not yet been enumerated. Make sure it's added to our list so that it's not enumerated again. */ - size_t newCapacity = sizeof(*pUniqueIDs) * (uniqueIDCount + 1); - ma_device_id* pNewUniqueIDs = (ma_device_id*)ma_realloc(pUniqueIDs, newCapacity, &pContext->allocationCallbacks); - if (pNewUniqueIDs == NULL) { - goto next_device; /* Failed to allocate memory. */ - } - - pUniqueIDs = pNewUniqueIDs; - MA_COPY_MEMORY(pUniqueIDs[uniqueIDCount].alsa, hwid, sizeof(hwid)); - uniqueIDCount += 1; - } - } - } else { - MA_ZERO_MEMORY(hwid, sizeof(hwid)); - } - - MA_ZERO_OBJECT(&deviceInfo); - ma_strncpy_s(deviceInfo.id.alsa, sizeof(deviceInfo.id.alsa), hwid, (size_t)-1); - - /* - There's no good way to determine whether or not a device is the default on Linux. We're just going to do something simple and - just use the name of "default" as the indicator. - */ - if (ma_strcmp(deviceInfo.id.alsa, "default") == 0) { - deviceInfo.isDefault = MA_TRUE; - } - - - /* - DESC is the friendly name. We treat this slightly differently depending on whether or not we are using verbose - device enumeration. In verbose mode we want to take the entire description so that the end-user can distinguish - between the subdevices of each card/dev pair. In simplified mode, however, we only want the first part of the - description. - - The value in DESC seems to be split into two lines, with the first line being the name of the device and the - second line being a description of the device. I don't like having the description be across two lines because - it makes formatting ugly and annoying. I'm therefore deciding to put it all on a single line with the second line - being put into parentheses. In simplified mode I'm just stripping the second line entirely. - */ - if (DESC != NULL) { - int lfPos; - const char* line2 = ma_find_char(DESC, '\n', &lfPos); - if (line2 != NULL) { - line2 += 1; /* Skip past the new-line character. */ - - if (pContext->alsa.useVerboseDeviceEnumeration) { - /* Verbose mode. Put the second line in brackets. */ - ma_strncpy_s(deviceInfo.name, sizeof(deviceInfo.name), DESC, lfPos); - ma_strcat_s (deviceInfo.name, sizeof(deviceInfo.name), " ("); - ma_strcat_s (deviceInfo.name, sizeof(deviceInfo.name), line2); - ma_strcat_s (deviceInfo.name, sizeof(deviceInfo.name), ")"); - } else { - /* Simplified mode. Strip the second line entirely. */ - ma_strncpy_s(deviceInfo.name, sizeof(deviceInfo.name), DESC, lfPos); - } - } else { - /* There's no second line. Just copy the whole description. */ - ma_strncpy_s(deviceInfo.name, sizeof(deviceInfo.name), DESC, (size_t)-1); - } - } - - if (!ma_is_device_blacklisted__alsa(deviceType, NAME)) { - cbResult = callback(pContext, deviceType, &deviceInfo, pUserData); - } - - /* - Some devices are both playback and capture, but they are only enumerated by ALSA once. We need to fire the callback - again for the other device type in this case. We do this for known devices and where the IOID hint is NULL, which - means both Input and Output. - */ - if (cbResult) { - if (ma_is_common_device_name__alsa(NAME) || IOID == NULL) { - if (deviceType == ma_device_type_playback) { - if (!ma_is_capture_device_blacklisted__alsa(NAME)) { - cbResult = callback(pContext, ma_device_type_capture, &deviceInfo, pUserData); - } - } else { - if (!ma_is_playback_device_blacklisted__alsa(NAME)) { - cbResult = callback(pContext, ma_device_type_playback, &deviceInfo, pUserData); - } - } - } - } - - if (cbResult == MA_FALSE) { - stopEnumeration = MA_TRUE; - } - - next_device: - free(NAME); - free(DESC); - free(IOID); - ppNextDeviceHint += 1; - - /* We need to stop enumeration if the callback returned false. */ - if (stopEnumeration) { - break; - } - } - - ma_free(pUniqueIDs, &pContext->allocationCallbacks); - ((ma_snd_device_name_free_hint_proc)pContext->alsa.snd_device_name_free_hint)((void**)ppDeviceHints); - - ma_mutex_unlock(&pContext->alsa.internalDeviceEnumLock); - - return MA_SUCCESS; -} - - -typedef struct -{ - ma_device_type deviceType; - const ma_device_id* pDeviceID; - ma_share_mode shareMode; - ma_device_info* pDeviceInfo; - ma_bool32 foundDevice; -} ma_context_get_device_info_enum_callback_data__alsa; - -static ma_bool32 ma_context_get_device_info_enum_callback__alsa(ma_context* pContext, ma_device_type deviceType, const ma_device_info* pDeviceInfo, void* pUserData) -{ - ma_context_get_device_info_enum_callback_data__alsa* pData = (ma_context_get_device_info_enum_callback_data__alsa*)pUserData; - MA_ASSERT(pData != NULL); - - (void)pContext; - - if (pData->pDeviceID == NULL && ma_strcmp(pDeviceInfo->id.alsa, "default") == 0) { - ma_strncpy_s(pData->pDeviceInfo->name, sizeof(pData->pDeviceInfo->name), pDeviceInfo->name, (size_t)-1); - pData->foundDevice = MA_TRUE; - } else { - if (pData->deviceType == deviceType && (pData->pDeviceID != NULL && ma_strcmp(pData->pDeviceID->alsa, pDeviceInfo->id.alsa) == 0)) { - ma_strncpy_s(pData->pDeviceInfo->name, sizeof(pData->pDeviceInfo->name), pDeviceInfo->name, (size_t)-1); - pData->foundDevice = MA_TRUE; - } - } - - /* Keep enumerating until we have found the device. */ - return !pData->foundDevice; -} - -static void ma_context_test_rate_and_add_native_data_format__alsa(ma_context* pContext, ma_snd_pcm_t* pPCM, ma_snd_pcm_hw_params_t* pHWParams, ma_format format, ma_uint32 channels, ma_uint32 sampleRate, ma_uint32 flags, ma_device_info* pDeviceInfo) -{ - MA_ASSERT(pPCM != NULL); - MA_ASSERT(pHWParams != NULL); - MA_ASSERT(pDeviceInfo != NULL); - - if (pDeviceInfo->nativeDataFormatCount < ma_countof(pDeviceInfo->nativeDataFormats) && ((ma_snd_pcm_hw_params_test_rate_proc)pContext->alsa.snd_pcm_hw_params_test_rate)(pPCM, pHWParams, sampleRate, 0) == 0) { - pDeviceInfo->nativeDataFormats[pDeviceInfo->nativeDataFormatCount].format = format; - pDeviceInfo->nativeDataFormats[pDeviceInfo->nativeDataFormatCount].channels = channels; - pDeviceInfo->nativeDataFormats[pDeviceInfo->nativeDataFormatCount].sampleRate = sampleRate; - pDeviceInfo->nativeDataFormats[pDeviceInfo->nativeDataFormatCount].flags = flags; - pDeviceInfo->nativeDataFormatCount += 1; - } -} - -static void ma_context_iterate_rates_and_add_native_data_format__alsa(ma_context* pContext, ma_snd_pcm_t* pPCM, ma_snd_pcm_hw_params_t* pHWParams, ma_format format, ma_uint32 channels, ma_uint32 flags, ma_device_info* pDeviceInfo) -{ - ma_uint32 iSampleRate; - unsigned int minSampleRate; - unsigned int maxSampleRate; - int sampleRateDir; /* Not used. Just passed into snd_pcm_hw_params_get_rate_min/max(). */ - - /* There could be a range. */ - ((ma_snd_pcm_hw_params_get_rate_min_proc)pContext->alsa.snd_pcm_hw_params_get_rate_min)(pHWParams, &minSampleRate, &sampleRateDir); - ((ma_snd_pcm_hw_params_get_rate_max_proc)pContext->alsa.snd_pcm_hw_params_get_rate_max)(pHWParams, &maxSampleRate, &sampleRateDir); - - /* Make sure our sample rates are clamped to sane values. Stupid devices like "pulse" will reports rates like "1" which is ridiculous. */ - minSampleRate = ma_clamp(minSampleRate, (unsigned int)ma_standard_sample_rate_min, (unsigned int)ma_standard_sample_rate_max); - maxSampleRate = ma_clamp(maxSampleRate, (unsigned int)ma_standard_sample_rate_min, (unsigned int)ma_standard_sample_rate_max); - - for (iSampleRate = 0; iSampleRate < ma_countof(g_maStandardSampleRatePriorities); iSampleRate += 1) { - ma_uint32 standardSampleRate = g_maStandardSampleRatePriorities[iSampleRate]; - - if (standardSampleRate >= minSampleRate && standardSampleRate <= maxSampleRate) { - ma_context_test_rate_and_add_native_data_format__alsa(pContext, pPCM, pHWParams, format, channels, standardSampleRate, flags, pDeviceInfo); - } - } - - /* Now make sure our min and max rates are included just in case they aren't in the range of our standard rates. */ - if (!ma_is_standard_sample_rate(minSampleRate)) { - ma_context_test_rate_and_add_native_data_format__alsa(pContext, pPCM, pHWParams, format, channels, minSampleRate, flags, pDeviceInfo); - } - - if (!ma_is_standard_sample_rate(maxSampleRate) && maxSampleRate != minSampleRate) { - ma_context_test_rate_and_add_native_data_format__alsa(pContext, pPCM, pHWParams, format, channels, maxSampleRate, flags, pDeviceInfo); - } -} - -static ma_result ma_context_get_device_info__alsa(ma_context* pContext, ma_device_type deviceType, const ma_device_id* pDeviceID, ma_device_info* pDeviceInfo) -{ - ma_context_get_device_info_enum_callback_data__alsa data; - ma_result result; - int resultALSA; - ma_snd_pcm_t* pPCM; - ma_snd_pcm_hw_params_t* pHWParams; - ma_uint32 iFormat; - ma_uint32 iChannel; - - MA_ASSERT(pContext != NULL); - - /* We just enumerate to find basic information about the device. */ - data.deviceType = deviceType; - data.pDeviceID = pDeviceID; - data.pDeviceInfo = pDeviceInfo; - data.foundDevice = MA_FALSE; - result = ma_context_enumerate_devices__alsa(pContext, ma_context_get_device_info_enum_callback__alsa, &data); - if (result != MA_SUCCESS) { - return result; - } - - if (!data.foundDevice) { - return MA_NO_DEVICE; - } - - if (ma_strcmp(pDeviceInfo->id.alsa, "default") == 0) { - pDeviceInfo->isDefault = MA_TRUE; - } - - /* For detailed info we need to open the device. */ - result = ma_context_open_pcm__alsa(pContext, ma_share_mode_shared, deviceType, pDeviceID, 0, &pPCM); - if (result != MA_SUCCESS) { - return result; - } - - /* We need to initialize a HW parameters object in order to know what formats are supported. */ - pHWParams = (ma_snd_pcm_hw_params_t*)ma_calloc(((ma_snd_pcm_hw_params_sizeof_proc)pContext->alsa.snd_pcm_hw_params_sizeof)(), &pContext->allocationCallbacks); - if (pHWParams == NULL) { - ((ma_snd_pcm_close_proc)pContext->alsa.snd_pcm_close)(pPCM); - return MA_OUT_OF_MEMORY; - } - - resultALSA = ((ma_snd_pcm_hw_params_any_proc)pContext->alsa.snd_pcm_hw_params_any)(pPCM, pHWParams); - if (resultALSA < 0) { - ma_free(pHWParams, &pContext->allocationCallbacks); - ((ma_snd_pcm_close_proc)pContext->alsa.snd_pcm_close)(pPCM); - ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[ALSA] Failed to initialize hardware parameters. snd_pcm_hw_params_any() failed."); - return ma_result_from_errno(-resultALSA); - } - - /* - Some ALSA devices can support many permutations of formats, channels and rates. We only support - a fixed number of permutations which means we need to employ some strategies to ensure the best - combinations are returned. An example is the "pulse" device which can do its own data conversion - in software and as a result can support any combination of format, channels and rate. - - We want to ensure that the first data formats are the best. We have a list of favored sample - formats and sample rates, so these will be the basis of our iteration. - */ - - /* Formats. We just iterate over our standard formats and test them, making sure we reset the configuration space each iteration. */ - for (iFormat = 0; iFormat < ma_countof(g_maFormatPriorities); iFormat += 1) { - ma_format format = g_maFormatPriorities[iFormat]; - - /* - For each format we need to make sure we reset the configuration space so we don't return - channel counts and rates that aren't compatible with a format. - */ - ((ma_snd_pcm_hw_params_any_proc)pContext->alsa.snd_pcm_hw_params_any)(pPCM, pHWParams); - - /* Test the format first. If this fails it means the format is not supported and we can skip it. */ - if (((ma_snd_pcm_hw_params_test_format_proc)pContext->alsa.snd_pcm_hw_params_test_format)(pPCM, pHWParams, ma_convert_ma_format_to_alsa_format(format)) == 0) { - /* The format is supported. */ - unsigned int minChannels; - unsigned int maxChannels; - - /* - The configuration space needs to be restricted to this format so we can get an accurate - picture of which sample rates and channel counts are support with this format. - */ - ((ma_snd_pcm_hw_params_set_format_proc)pContext->alsa.snd_pcm_hw_params_set_format)(pPCM, pHWParams, ma_convert_ma_format_to_alsa_format(format)); - - /* Now we need to check for supported channels. */ - ((ma_snd_pcm_hw_params_get_channels_min_proc)pContext->alsa.snd_pcm_hw_params_get_channels_min)(pHWParams, &minChannels); - ((ma_snd_pcm_hw_params_get_channels_max_proc)pContext->alsa.snd_pcm_hw_params_get_channels_max)(pHWParams, &maxChannels); - - if (minChannels > MA_MAX_CHANNELS) { - continue; /* Too many channels. */ - } - if (maxChannels < MA_MIN_CHANNELS) { - continue; /* Not enough channels. */ - } - - /* - Make sure the channel count is clamped. This is mainly intended for the max channels - because some devices can report an unbound maximum. - */ - minChannels = ma_clamp(minChannels, MA_MIN_CHANNELS, MA_MAX_CHANNELS); - maxChannels = ma_clamp(maxChannels, MA_MIN_CHANNELS, MA_MAX_CHANNELS); - - if (minChannels == MA_MIN_CHANNELS && maxChannels == MA_MAX_CHANNELS) { - /* The device supports all channels. Don't iterate over every single one. Instead just set the channels to 0 which means all channels are supported. */ - ma_context_iterate_rates_and_add_native_data_format__alsa(pContext, pPCM, pHWParams, format, 0, 0, pDeviceInfo); /* Intentionally setting the channel count to 0 as that means all channels are supported. */ - } else { - /* The device only supports a specific set of channels. We need to iterate over all of them. */ - for (iChannel = minChannels; iChannel <= maxChannels; iChannel += 1) { - /* Test the channel before applying it to the configuration space. */ - unsigned int channels = iChannel; - - /* Make sure our channel range is reset before testing again or else we'll always fail the test. */ - ((ma_snd_pcm_hw_params_any_proc)pContext->alsa.snd_pcm_hw_params_any)(pPCM, pHWParams); - ((ma_snd_pcm_hw_params_set_format_proc)pContext->alsa.snd_pcm_hw_params_set_format)(pPCM, pHWParams, ma_convert_ma_format_to_alsa_format(format)); - - if (((ma_snd_pcm_hw_params_test_channels_proc)pContext->alsa.snd_pcm_hw_params_test_channels)(pPCM, pHWParams, channels) == 0) { - /* The channel count is supported. */ - - /* The configuration space now needs to be restricted to the channel count before extracting the sample rate. */ - ((ma_snd_pcm_hw_params_set_channels_proc)pContext->alsa.snd_pcm_hw_params_set_channels)(pPCM, pHWParams, channels); - - /* Only after the configuration space has been restricted to the specific channel count should we iterate over our sample rates. */ - ma_context_iterate_rates_and_add_native_data_format__alsa(pContext, pPCM, pHWParams, format, channels, 0, pDeviceInfo); - } else { - /* The channel count is not supported. Skip. */ - } - } - } - } else { - /* The format is not supported. Skip. */ - } - } - - ma_free(pHWParams, &pContext->allocationCallbacks); - - ((ma_snd_pcm_close_proc)pContext->alsa.snd_pcm_close)(pPCM); - return MA_SUCCESS; -} - -static ma_result ma_device_uninit__alsa(ma_device* pDevice) -{ - MA_ASSERT(pDevice != NULL); - - if ((ma_snd_pcm_t*)pDevice->alsa.pPCMCapture) { - ((ma_snd_pcm_close_proc)pDevice->pContext->alsa.snd_pcm_close)((ma_snd_pcm_t*)pDevice->alsa.pPCMCapture); - close(pDevice->alsa.wakeupfdCapture); - ma_free(pDevice->alsa.pPollDescriptorsCapture, &pDevice->pContext->allocationCallbacks); - } - - if ((ma_snd_pcm_t*)pDevice->alsa.pPCMPlayback) { - ((ma_snd_pcm_close_proc)pDevice->pContext->alsa.snd_pcm_close)((ma_snd_pcm_t*)pDevice->alsa.pPCMPlayback); - close(pDevice->alsa.wakeupfdPlayback); - ma_free(pDevice->alsa.pPollDescriptorsPlayback, &pDevice->pContext->allocationCallbacks); - } - - return MA_SUCCESS; -} - -static ma_result ma_device_init_by_type__alsa(ma_device* pDevice, const ma_device_config* pConfig, ma_device_descriptor* pDescriptor, ma_device_type deviceType) -{ - ma_result result; - int resultALSA; - ma_snd_pcm_t* pPCM; - ma_bool32 isUsingMMap; - ma_snd_pcm_format_t formatALSA; - ma_format internalFormat; - ma_uint32 internalChannels; - ma_uint32 internalSampleRate; - ma_channel internalChannelMap[MA_MAX_CHANNELS]; - ma_uint32 internalPeriodSizeInFrames; - ma_uint32 internalPeriods; - int openMode; - ma_snd_pcm_hw_params_t* pHWParams; - ma_snd_pcm_sw_params_t* pSWParams; - ma_snd_pcm_uframes_t bufferBoundary; - int pollDescriptorCount; - struct pollfd* pPollDescriptors; - int wakeupfd; - - MA_ASSERT(pConfig != NULL); - MA_ASSERT(deviceType != ma_device_type_duplex); /* This function should only be called for playback _or_ capture, never duplex. */ - MA_ASSERT(pDevice != NULL); - - formatALSA = ma_convert_ma_format_to_alsa_format(pDescriptor->format); - - openMode = 0; - if (pConfig->alsa.noAutoResample) { - openMode |= MA_SND_PCM_NO_AUTO_RESAMPLE; - } - if (pConfig->alsa.noAutoChannels) { - openMode |= MA_SND_PCM_NO_AUTO_CHANNELS; - } - if (pConfig->alsa.noAutoFormat) { - openMode |= MA_SND_PCM_NO_AUTO_FORMAT; - } - - result = ma_context_open_pcm__alsa(pDevice->pContext, pDescriptor->shareMode, deviceType, pDescriptor->pDeviceID, openMode, &pPCM); - if (result != MA_SUCCESS) { - return result; - } - - - /* Hardware parameters. */ - pHWParams = (ma_snd_pcm_hw_params_t*)ma_calloc(((ma_snd_pcm_hw_params_sizeof_proc)pDevice->pContext->alsa.snd_pcm_hw_params_sizeof)(), &pDevice->pContext->allocationCallbacks); - if (pHWParams == NULL) { - ((ma_snd_pcm_close_proc)pDevice->pContext->alsa.snd_pcm_close)(pPCM); - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[ALSA] Failed to allocate memory for hardware parameters."); - return MA_OUT_OF_MEMORY; - } - - resultALSA = ((ma_snd_pcm_hw_params_any_proc)pDevice->pContext->alsa.snd_pcm_hw_params_any)(pPCM, pHWParams); - if (resultALSA < 0) { - ma_free(pHWParams, &pDevice->pContext->allocationCallbacks); - ((ma_snd_pcm_close_proc)pDevice->pContext->alsa.snd_pcm_close)(pPCM); - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[ALSA] Failed to initialize hardware parameters. snd_pcm_hw_params_any() failed."); - return ma_result_from_errno(-resultALSA); - } - - /* MMAP Mode. Try using interleaved MMAP access. If this fails, fall back to standard readi/writei. */ - isUsingMMap = MA_FALSE; -#if 0 /* NOTE: MMAP mode temporarily disabled. */ - if (deviceType != ma_device_type_capture) { /* <-- Disabling MMAP mode for capture devices because I apparently do not have a device that supports it which means I can't test it... Contributions welcome. */ - if (!pConfig->alsa.noMMap) { - if (((ma_snd_pcm_hw_params_set_access_proc)pDevice->pContext->alsa.snd_pcm_hw_params_set_access)(pPCM, pHWParams, MA_SND_PCM_ACCESS_MMAP_INTERLEAVED) == 0) { - pDevice->alsa.isUsingMMap = MA_TRUE; - } - } - } -#endif - - if (!isUsingMMap) { - resultALSA = ((ma_snd_pcm_hw_params_set_access_proc)pDevice->pContext->alsa.snd_pcm_hw_params_set_access)(pPCM, pHWParams, MA_SND_PCM_ACCESS_RW_INTERLEAVED); - if (resultALSA < 0) { - ma_free(pHWParams, &pDevice->pContext->allocationCallbacks); - ((ma_snd_pcm_close_proc)pDevice->pContext->alsa.snd_pcm_close)(pPCM); - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[ALSA] Failed to set access mode to neither SND_PCM_ACCESS_MMAP_INTERLEAVED nor SND_PCM_ACCESS_RW_INTERLEAVED. snd_pcm_hw_params_set_access() failed."); - return ma_result_from_errno(-resultALSA); - } - } - - /* - Most important properties first. The documentation for OSS (yes, I know this is ALSA!) recommends format, channels, then sample rate. I can't - find any documentation for ALSA specifically, so I'm going to copy the recommendation for OSS. - */ - - /* Format. */ - { - /* - At this point we should have a list of supported formats, so now we need to find the best one. We first check if the requested format is - supported, and if so, use that one. If it's not supported, we just run though a list of formats and try to find the best one. - */ - if (formatALSA == MA_SND_PCM_FORMAT_UNKNOWN || ((ma_snd_pcm_hw_params_test_format_proc)pDevice->pContext->alsa.snd_pcm_hw_params_test_format)(pPCM, pHWParams, formatALSA) != 0) { - /* We're either requesting the native format or the specified format is not supported. */ - size_t iFormat; - - formatALSA = MA_SND_PCM_FORMAT_UNKNOWN; - for (iFormat = 0; iFormat < ma_countof(g_maFormatPriorities); ++iFormat) { - if (((ma_snd_pcm_hw_params_test_format_proc)pDevice->pContext->alsa.snd_pcm_hw_params_test_format)(pPCM, pHWParams, ma_convert_ma_format_to_alsa_format(g_maFormatPriorities[iFormat])) == 0) { - formatALSA = ma_convert_ma_format_to_alsa_format(g_maFormatPriorities[iFormat]); - break; - } - } - - if (formatALSA == MA_SND_PCM_FORMAT_UNKNOWN) { - ma_free(pHWParams, &pDevice->pContext->allocationCallbacks); - ((ma_snd_pcm_close_proc)pDevice->pContext->alsa.snd_pcm_close)(pPCM); - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[ALSA] Format not supported. The device does not support any miniaudio formats."); - return MA_FORMAT_NOT_SUPPORTED; - } - } - - resultALSA = ((ma_snd_pcm_hw_params_set_format_proc)pDevice->pContext->alsa.snd_pcm_hw_params_set_format)(pPCM, pHWParams, formatALSA); - if (resultALSA < 0) { - ma_free(pHWParams, &pDevice->pContext->allocationCallbacks); - ((ma_snd_pcm_close_proc)pDevice->pContext->alsa.snd_pcm_close)(pPCM); - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[ALSA] Format not supported. snd_pcm_hw_params_set_format() failed."); - return ma_result_from_errno(-resultALSA); - } - - internalFormat = ma_format_from_alsa(formatALSA); - if (internalFormat == ma_format_unknown) { - ma_free(pHWParams, &pDevice->pContext->allocationCallbacks); - ((ma_snd_pcm_close_proc)pDevice->pContext->alsa.snd_pcm_close)(pPCM); - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[ALSA] The chosen format is not supported by miniaudio."); - return MA_FORMAT_NOT_SUPPORTED; - } - } - - /* Channels. */ - { - unsigned int channels = pDescriptor->channels; - if (channels == 0) { - channels = MA_DEFAULT_CHANNELS; - } - - resultALSA = ((ma_snd_pcm_hw_params_set_channels_near_proc)pDevice->pContext->alsa.snd_pcm_hw_params_set_channels_near)(pPCM, pHWParams, &channels); - if (resultALSA < 0) { - ma_free(pHWParams, &pDevice->pContext->allocationCallbacks); - ((ma_snd_pcm_close_proc)pDevice->pContext->alsa.snd_pcm_close)(pPCM); - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[ALSA] Failed to set channel count. snd_pcm_hw_params_set_channels_near() failed."); - return ma_result_from_errno(-resultALSA); - } - - internalChannels = (ma_uint32)channels; - } - - /* Sample Rate */ - { - unsigned int sampleRate; - - /* - It appears there's either a bug in ALSA, a bug in some drivers, or I'm doing something silly; but having resampling enabled causes - problems with some device configurations when used in conjunction with MMAP access mode. To fix this problem we need to disable - resampling. - - To reproduce this problem, open the "plug:dmix" device, and set the sample rate to 44100. Internally, it looks like dmix uses a - sample rate of 48000. The hardware parameters will get set correctly with no errors, but it looks like the 44100 -> 48000 resampling - doesn't work properly - but only with MMAP access mode. You will notice skipping/crackling in the audio, and it'll run at a slightly - faster rate. - - miniaudio has built-in support for sample rate conversion (albeit low quality at the moment), so disabling resampling should be fine - for us. The only problem is that it won't be taking advantage of any kind of hardware-accelerated resampling and it won't be very - good quality until I get a chance to improve the quality of miniaudio's software sample rate conversion. - - I don't currently know if the dmix plugin is the only one with this error. Indeed, this is the only one I've been able to reproduce - this error with. In the future, we may want to restrict the disabling of resampling to only known bad plugins. - */ - ((ma_snd_pcm_hw_params_set_rate_resample_proc)pDevice->pContext->alsa.snd_pcm_hw_params_set_rate_resample)(pPCM, pHWParams, 0); - - sampleRate = pDescriptor->sampleRate; - if (sampleRate == 0) { - sampleRate = MA_DEFAULT_SAMPLE_RATE; - } - - resultALSA = ((ma_snd_pcm_hw_params_set_rate_near_proc)pDevice->pContext->alsa.snd_pcm_hw_params_set_rate_near)(pPCM, pHWParams, &sampleRate, 0); - if (resultALSA < 0) { - ma_free(pHWParams, &pDevice->pContext->allocationCallbacks); - ((ma_snd_pcm_close_proc)pDevice->pContext->alsa.snd_pcm_close)(pPCM); - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[ALSA] Sample rate not supported. snd_pcm_hw_params_set_rate_near() failed."); - return ma_result_from_errno(-resultALSA); - } - - internalSampleRate = (ma_uint32)sampleRate; - } - - /* Periods. */ - { - ma_uint32 periods = pDescriptor->periodCount; - - resultALSA = ((ma_snd_pcm_hw_params_set_periods_near_proc)pDevice->pContext->alsa.snd_pcm_hw_params_set_periods_near)(pPCM, pHWParams, &periods, NULL); - if (resultALSA < 0) { - ma_free(pHWParams, &pDevice->pContext->allocationCallbacks); - ((ma_snd_pcm_close_proc)pDevice->pContext->alsa.snd_pcm_close)(pPCM); - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[ALSA] Failed to set period count. snd_pcm_hw_params_set_periods_near() failed."); - return ma_result_from_errno(-resultALSA); - } - - internalPeriods = periods; - } - - /* Buffer Size */ - { - ma_snd_pcm_uframes_t actualBufferSizeInFrames = ma_calculate_buffer_size_in_frames_from_descriptor(pDescriptor, internalSampleRate, pConfig->performanceProfile) * internalPeriods; - - resultALSA = ((ma_snd_pcm_hw_params_set_buffer_size_near_proc)pDevice->pContext->alsa.snd_pcm_hw_params_set_buffer_size_near)(pPCM, pHWParams, &actualBufferSizeInFrames); - if (resultALSA < 0) { - ma_free(pHWParams, &pDevice->pContext->allocationCallbacks); - ((ma_snd_pcm_close_proc)pDevice->pContext->alsa.snd_pcm_close)(pPCM); - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[ALSA] Failed to set buffer size for device. snd_pcm_hw_params_set_buffer_size() failed."); - return ma_result_from_errno(-resultALSA); - } - - internalPeriodSizeInFrames = actualBufferSizeInFrames / internalPeriods; - } - - /* Apply hardware parameters. */ - resultALSA = ((ma_snd_pcm_hw_params_proc)pDevice->pContext->alsa.snd_pcm_hw_params)(pPCM, pHWParams); - if (resultALSA < 0) { - ma_free(pHWParams, &pDevice->pContext->allocationCallbacks); - ((ma_snd_pcm_close_proc)pDevice->pContext->alsa.snd_pcm_close)(pPCM); - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[ALSA] Failed to set hardware parameters. snd_pcm_hw_params() failed."); - return ma_result_from_errno(-resultALSA); - } - - ma_free(pHWParams, &pDevice->pContext->allocationCallbacks); - pHWParams = NULL; - - - /* Software parameters. */ - pSWParams = (ma_snd_pcm_sw_params_t*)ma_calloc(((ma_snd_pcm_sw_params_sizeof_proc)pDevice->pContext->alsa.snd_pcm_sw_params_sizeof)(), &pDevice->pContext->allocationCallbacks); - if (pSWParams == NULL) { - ((ma_snd_pcm_close_proc)pDevice->pContext->alsa.snd_pcm_close)(pPCM); - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[ALSA] Failed to allocate memory for software parameters."); - return MA_OUT_OF_MEMORY; - } - - resultALSA = ((ma_snd_pcm_sw_params_current_proc)pDevice->pContext->alsa.snd_pcm_sw_params_current)(pPCM, pSWParams); - if (resultALSA < 0) { - ma_free(pSWParams, &pDevice->pContext->allocationCallbacks); - ((ma_snd_pcm_close_proc)pDevice->pContext->alsa.snd_pcm_close)(pPCM); - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[ALSA] Failed to initialize software parameters. snd_pcm_sw_params_current() failed."); - return ma_result_from_errno(-resultALSA); - } - - resultALSA = ((ma_snd_pcm_sw_params_set_avail_min_proc)pDevice->pContext->alsa.snd_pcm_sw_params_set_avail_min)(pPCM, pSWParams, ma_prev_power_of_2(internalPeriodSizeInFrames)); - if (resultALSA < 0) { - ma_free(pSWParams, &pDevice->pContext->allocationCallbacks); - ((ma_snd_pcm_close_proc)pDevice->pContext->alsa.snd_pcm_close)(pPCM); - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[ALSA] snd_pcm_sw_params_set_avail_min() failed."); - return ma_result_from_errno(-resultALSA); - } - - resultALSA = ((ma_snd_pcm_sw_params_get_boundary_proc)pDevice->pContext->alsa.snd_pcm_sw_params_get_boundary)(pSWParams, &bufferBoundary); - if (resultALSA < 0) { - bufferBoundary = internalPeriodSizeInFrames * internalPeriods; - } - - if (deviceType == ma_device_type_playback && !isUsingMMap) { /* Only playback devices in writei/readi mode need a start threshold. */ - /* - Subtle detail here with the start threshold. When in playback-only mode (no full-duplex) we can set the start threshold to - the size of a period. But for full-duplex we need to set it such that it is at least two periods. - */ - resultALSA = ((ma_snd_pcm_sw_params_set_start_threshold_proc)pDevice->pContext->alsa.snd_pcm_sw_params_set_start_threshold)(pPCM, pSWParams, internalPeriodSizeInFrames*2); - if (resultALSA < 0) { - ma_free(pSWParams, &pDevice->pContext->allocationCallbacks); - ((ma_snd_pcm_close_proc)pDevice->pContext->alsa.snd_pcm_close)(pPCM); - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[ALSA] Failed to set start threshold for playback device. snd_pcm_sw_params_set_start_threshold() failed."); - return ma_result_from_errno(-resultALSA); - } - - resultALSA = ((ma_snd_pcm_sw_params_set_stop_threshold_proc)pDevice->pContext->alsa.snd_pcm_sw_params_set_stop_threshold)(pPCM, pSWParams, bufferBoundary); - if (resultALSA < 0) { /* Set to boundary to loop instead of stop in the event of an xrun. */ - ma_free(pSWParams, &pDevice->pContext->allocationCallbacks); - ((ma_snd_pcm_close_proc)pDevice->pContext->alsa.snd_pcm_close)(pPCM); - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[ALSA] Failed to set stop threshold for playback device. snd_pcm_sw_params_set_stop_threshold() failed."); - return ma_result_from_errno(-resultALSA); - } - } - - resultALSA = ((ma_snd_pcm_sw_params_proc)pDevice->pContext->alsa.snd_pcm_sw_params)(pPCM, pSWParams); - if (resultALSA < 0) { - ma_free(pSWParams, &pDevice->pContext->allocationCallbacks); - ((ma_snd_pcm_close_proc)pDevice->pContext->alsa.snd_pcm_close)(pPCM); - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[ALSA] Failed to set software parameters. snd_pcm_sw_params() failed."); - return ma_result_from_errno(-resultALSA); - } - - ma_free(pSWParams, &pDevice->pContext->allocationCallbacks); - pSWParams = NULL; - - - /* Grab the internal channel map. For now we're not going to bother trying to change the channel map and instead just do it ourselves. */ - { - ma_snd_pcm_chmap_t* pChmap = NULL; - if (pDevice->pContext->alsa.snd_pcm_get_chmap != NULL) { - pChmap = ((ma_snd_pcm_get_chmap_proc)pDevice->pContext->alsa.snd_pcm_get_chmap)(pPCM); - } - - if (pChmap != NULL) { - ma_uint32 iChannel; - - /* There are cases where the returned channel map can have a different channel count than was returned by snd_pcm_hw_params_set_channels_near(). */ - if (pChmap->channels >= internalChannels) { - /* Drop excess channels. */ - for (iChannel = 0; iChannel < internalChannels; ++iChannel) { - internalChannelMap[iChannel] = ma_convert_alsa_channel_position_to_ma_channel(pChmap->pos[iChannel]); - } - } else { - ma_uint32 i; - - /* - Excess channels use defaults. Do an initial fill with defaults, overwrite the first pChmap->channels, validate to ensure there are no duplicate - channels. If validation fails, fall back to defaults. - */ - ma_bool32 isValid = MA_TRUE; - - /* Fill with defaults. */ - ma_channel_map_init_standard(ma_standard_channel_map_alsa, internalChannelMap, ma_countof(internalChannelMap), internalChannels); - - /* Overwrite first pChmap->channels channels. */ - for (iChannel = 0; iChannel < pChmap->channels; ++iChannel) { - internalChannelMap[iChannel] = ma_convert_alsa_channel_position_to_ma_channel(pChmap->pos[iChannel]); - } - - /* Validate. */ - for (i = 0; i < internalChannels && isValid; ++i) { - ma_uint32 j; - for (j = i+1; j < internalChannels; ++j) { - if (internalChannelMap[i] == internalChannelMap[j]) { - isValid = MA_FALSE; - break; - } - } - } - - /* If our channel map is invalid, fall back to defaults. */ - if (!isValid) { - ma_channel_map_init_standard(ma_standard_channel_map_alsa, internalChannelMap, ma_countof(internalChannelMap), internalChannels); - } - } - - free(pChmap); - pChmap = NULL; - } else { - /* Could not retrieve the channel map. Fall back to a hard-coded assumption. */ - ma_channel_map_init_standard(ma_standard_channel_map_alsa, internalChannelMap, ma_countof(internalChannelMap), internalChannels); - } - } - - - /* - We need to retrieve the poll descriptors so we can use poll() to wait for data to become - available for reading or writing. There's no well defined maximum for this so we're just going - to allocate this on the heap. - */ - pollDescriptorCount = ((ma_snd_pcm_poll_descriptors_count_proc)pDevice->pContext->alsa.snd_pcm_poll_descriptors_count)(pPCM); - if (pollDescriptorCount <= 0) { - ((ma_snd_pcm_close_proc)pDevice->pContext->alsa.snd_pcm_close)(pPCM); - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[ALSA] Failed to retrieve poll descriptors count."); - return MA_ERROR; - } - - pPollDescriptors = (struct pollfd*)ma_malloc(sizeof(*pPollDescriptors) * (pollDescriptorCount + 1), &pDevice->pContext->allocationCallbacks); /* +1 because we want room for the wakeup descriptor. */ - if (pPollDescriptors == NULL) { - ((ma_snd_pcm_close_proc)pDevice->pContext->alsa.snd_pcm_close)(pPCM); - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[ALSA] Failed to allocate memory for poll descriptors."); - return MA_OUT_OF_MEMORY; - } - - /* - We need an eventfd to wakeup from poll() and avoid a deadlock in situations where the driver - never returns from writei() and readi(). This has been observed with the "pulse" device. - */ - wakeupfd = eventfd(0, 0); - if (wakeupfd < 0) { - ma_free(pPollDescriptors, &pDevice->pContext->allocationCallbacks); - ((ma_snd_pcm_close_proc)pDevice->pContext->alsa.snd_pcm_close)(pPCM); - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[ALSA] Failed to create eventfd for poll wakeup."); - return ma_result_from_errno(errno); - } - - /* We'll place the wakeup fd at the start of the buffer. */ - pPollDescriptors[0].fd = wakeupfd; - pPollDescriptors[0].events = POLLIN; /* We only care about waiting to read from the wakeup file descriptor. */ - pPollDescriptors[0].revents = 0; - - /* We can now extract the PCM poll descriptors which we place after the wakeup descriptor. */ - pollDescriptorCount = ((ma_snd_pcm_poll_descriptors_proc)pDevice->pContext->alsa.snd_pcm_poll_descriptors)(pPCM, pPollDescriptors + 1, pollDescriptorCount); /* +1 because we want to place these descriptors after the wakeup descriptor. */ - if (pollDescriptorCount <= 0) { - close(wakeupfd); - ma_free(pPollDescriptors, &pDevice->pContext->allocationCallbacks); - ((ma_snd_pcm_close_proc)pDevice->pContext->alsa.snd_pcm_close)(pPCM); - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[ALSA] Failed to retrieve poll descriptors."); - return MA_ERROR; - } - - if (deviceType == ma_device_type_capture) { - pDevice->alsa.pollDescriptorCountCapture = pollDescriptorCount; - pDevice->alsa.pPollDescriptorsCapture = pPollDescriptors; - pDevice->alsa.wakeupfdCapture = wakeupfd; - } else { - pDevice->alsa.pollDescriptorCountPlayback = pollDescriptorCount; - pDevice->alsa.pPollDescriptorsPlayback = pPollDescriptors; - pDevice->alsa.wakeupfdPlayback = wakeupfd; - } - - - /* We're done. Prepare the device. */ - resultALSA = ((ma_snd_pcm_prepare_proc)pDevice->pContext->alsa.snd_pcm_prepare)(pPCM); - if (resultALSA < 0) { - close(wakeupfd); - ma_free(pPollDescriptors, &pDevice->pContext->allocationCallbacks); - ((ma_snd_pcm_close_proc)pDevice->pContext->alsa.snd_pcm_close)(pPCM); - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[ALSA] Failed to prepare device."); - return ma_result_from_errno(-resultALSA); - } - - - if (deviceType == ma_device_type_capture) { - pDevice->alsa.pPCMCapture = (ma_ptr)pPCM; - pDevice->alsa.isUsingMMapCapture = isUsingMMap; - } else { - pDevice->alsa.pPCMPlayback = (ma_ptr)pPCM; - pDevice->alsa.isUsingMMapPlayback = isUsingMMap; - } - - pDescriptor->format = internalFormat; - pDescriptor->channels = internalChannels; - pDescriptor->sampleRate = internalSampleRate; - ma_channel_map_copy(pDescriptor->channelMap, internalChannelMap, ma_min(internalChannels, MA_MAX_CHANNELS)); - pDescriptor->periodSizeInFrames = internalPeriodSizeInFrames; - pDescriptor->periodCount = internalPeriods; - - return MA_SUCCESS; -} - -static ma_result ma_device_init__alsa(ma_device* pDevice, const ma_device_config* pConfig, ma_device_descriptor* pDescriptorPlayback, ma_device_descriptor* pDescriptorCapture) -{ - MA_ASSERT(pDevice != NULL); - - MA_ZERO_OBJECT(&pDevice->alsa); - - if (pConfig->deviceType == ma_device_type_loopback) { - return MA_DEVICE_TYPE_NOT_SUPPORTED; - } - - if (pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) { - ma_result result = ma_device_init_by_type__alsa(pDevice, pConfig, pDescriptorCapture, ma_device_type_capture); - if (result != MA_SUCCESS) { - return result; - } - } - - if (pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) { - ma_result result = ma_device_init_by_type__alsa(pDevice, pConfig, pDescriptorPlayback, ma_device_type_playback); - if (result != MA_SUCCESS) { - return result; - } - } - - return MA_SUCCESS; -} - -static ma_result ma_device_start__alsa(ma_device* pDevice) -{ - int resultALSA; - - if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) { - resultALSA = ((ma_snd_pcm_start_proc)pDevice->pContext->alsa.snd_pcm_start)((ma_snd_pcm_t*)pDevice->alsa.pPCMCapture); - if (resultALSA < 0) { - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[ALSA] Failed to start capture device."); - return ma_result_from_errno(-resultALSA); - } - } - - if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) { - /* - When data is written to the device we wait for the device to get ready to receive data with poll(). In my testing - I have observed that poll() can sometimes block forever unless the device is started explicitly with snd_pcm_start() - or some data is written with snd_pcm_writei(). - - To resolve this I've decided to do an explicit start with snd_pcm_start(). The problem with this is that the device - is started without any data in the internal buffer which will result in an immediate underrun. If instead we were - to call into snd_pcm_writei() in an attempt to prevent the underrun, we would run the risk of a weird deadlock - issue as documented inside ma_device_write__alsa(). - */ - resultALSA = ((ma_snd_pcm_start_proc)pDevice->pContext->alsa.snd_pcm_start)((ma_snd_pcm_t*)pDevice->alsa.pPCMPlayback); - if (resultALSA < 0) { - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[ALSA] Failed to start playback device."); - return ma_result_from_errno(-resultALSA); - } - } - - return MA_SUCCESS; -} - -static ma_result ma_device_stop__alsa(ma_device* pDevice) -{ - /* - The stop callback will get called on the worker thread after read/write__alsa() has returned. At this point there is - a small chance that our wakeupfd has not been cleared. We'll clear that out now if applicable. - */ - int resultPoll; - int resultRead; - - if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) { - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "[ALSA] Dropping capture device...\n"); - ((ma_snd_pcm_drop_proc)pDevice->pContext->alsa.snd_pcm_drop)((ma_snd_pcm_t*)pDevice->alsa.pPCMCapture); - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "[ALSA] Dropping capture device successful.\n"); - - /* We need to prepare the device again, otherwise we won't be able to restart the device. */ - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "[ALSA] Preparing capture device...\n"); - if (((ma_snd_pcm_prepare_proc)pDevice->pContext->alsa.snd_pcm_prepare)((ma_snd_pcm_t*)pDevice->alsa.pPCMCapture) < 0) { - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "[ALSA] Preparing capture device failed.\n"); - } else { - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "[ALSA] Preparing capture device successful.\n"); - } - - /* Clear the wakeupfd. */ - resultPoll = poll((struct pollfd*)pDevice->alsa.pPollDescriptorsCapture, 1, 0); - if (resultPoll > 0) { - ma_uint64 t; - resultRead = read(((struct pollfd*)pDevice->alsa.pPollDescriptorsCapture)[0].fd, &t, sizeof(t)); - if (resultRead != sizeof(t)) { - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "[ALSA] Failed to read from capture wakeupfd. read() = %d\n", resultRead); - } - } - } - - if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) { - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "[ALSA] Dropping playback device...\n"); - ((ma_snd_pcm_drop_proc)pDevice->pContext->alsa.snd_pcm_drop)((ma_snd_pcm_t*)pDevice->alsa.pPCMPlayback); - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "[ALSA] Dropping playback device successful.\n"); - - /* We need to prepare the device again, otherwise we won't be able to restart the device. */ - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "[ALSA] Preparing playback device...\n"); - if (((ma_snd_pcm_prepare_proc)pDevice->pContext->alsa.snd_pcm_prepare)((ma_snd_pcm_t*)pDevice->alsa.pPCMPlayback) < 0) { - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "[ALSA] Preparing playback device failed.\n"); - } else { - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "[ALSA] Preparing playback device successful.\n"); - } - - /* Clear the wakeupfd. */ - resultPoll = poll((struct pollfd*)pDevice->alsa.pPollDescriptorsPlayback, 1, 0); - if (resultPoll > 0) { - ma_uint64 t; - resultRead = read(((struct pollfd*)pDevice->alsa.pPollDescriptorsPlayback)[0].fd, &t, sizeof(t)); - if (resultRead != sizeof(t)) { - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "[ALSA] Failed to read from playback wakeupfd. read() = %d\n", resultRead); - } - } - } - - return MA_SUCCESS; -} - -static ma_result ma_device_wait__alsa(ma_device* pDevice, ma_snd_pcm_t* pPCM, struct pollfd* pPollDescriptors, int pollDescriptorCount, short requiredEvent) -{ - for (;;) { - unsigned short revents; - int resultALSA; - int resultPoll = poll(pPollDescriptors, pollDescriptorCount, -1); - if (resultPoll < 0) { - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_WARNING, "[ALSA] poll() failed.\n"); - - /* - There have been reports that poll() is returning an error randomly and that instead of - returning an error, simply trying again will work. I'm experimenting with adopting this - advice. - */ - continue; - /*return ma_result_from_errno(errno);*/ - } - - /* - Before checking the ALSA poll descriptor flag we need to check if the wakeup descriptor - has had it's POLLIN flag set. If so, we need to actually read the data and then exit the - function. The wakeup descriptor will be the first item in the descriptors buffer. - */ - if ((pPollDescriptors[0].revents & POLLIN) != 0) { - ma_uint64 t; - int resultRead = read(pPollDescriptors[0].fd, &t, sizeof(t)); /* <-- Important that we read here so that the next write() does not block. */ - if (resultRead < 0) { - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[ALSA] read() failed.\n"); - return ma_result_from_errno(errno); - } - - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "[ALSA] POLLIN set for wakeupfd\n"); - return MA_DEVICE_NOT_STARTED; - } - - /* - Getting here means that some data should be able to be read. We need to use ALSA to - translate the revents flags for us. - */ - resultALSA = ((ma_snd_pcm_poll_descriptors_revents_proc)pDevice->pContext->alsa.snd_pcm_poll_descriptors_revents)(pPCM, pPollDescriptors + 1, pollDescriptorCount - 1, &revents); /* +1, -1 to ignore the wakeup descriptor. */ - if (resultALSA < 0) { - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[ALSA] snd_pcm_poll_descriptors_revents() failed.\n"); - return ma_result_from_errno(-resultALSA); - } - - if ((revents & POLLERR) != 0) { - ma_snd_pcm_state_t state = ((ma_snd_pcm_state_proc)pDevice->pContext->alsa.snd_pcm_state)(pPCM); - if (state == MA_SND_PCM_STATE_XRUN) { - /* The PCM is in a xrun state. This will be recovered from at a higher level. We can disregard this. */ - } else { - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_WARNING, "[ALSA] POLLERR detected. status = %d\n", ((ma_snd_pcm_state_proc)pDevice->pContext->alsa.snd_pcm_state)(pPCM)); - } - } - - if ((revents & requiredEvent) == requiredEvent) { - break; /* We're done. Data available for reading or writing. */ - } - } - - return MA_SUCCESS; -} - -static ma_result ma_device_wait_read__alsa(ma_device* pDevice) -{ - return ma_device_wait__alsa(pDevice, (ma_snd_pcm_t*)pDevice->alsa.pPCMCapture, (struct pollfd*)pDevice->alsa.pPollDescriptorsCapture, pDevice->alsa.pollDescriptorCountCapture + 1, POLLIN); /* +1 to account for the wakeup descriptor. */ -} - -static ma_result ma_device_wait_write__alsa(ma_device* pDevice) -{ - return ma_device_wait__alsa(pDevice, (ma_snd_pcm_t*)pDevice->alsa.pPCMPlayback, (struct pollfd*)pDevice->alsa.pPollDescriptorsPlayback, pDevice->alsa.pollDescriptorCountPlayback + 1, POLLOUT); /* +1 to account for the wakeup descriptor. */ -} - -static ma_result ma_device_read__alsa(ma_device* pDevice, void* pFramesOut, ma_uint32 frameCount, ma_uint32* pFramesRead) -{ - ma_snd_pcm_sframes_t resultALSA = 0; - - MA_ASSERT(pDevice != NULL); - MA_ASSERT(pFramesOut != NULL); - - if (pFramesRead != NULL) { - *pFramesRead = 0; - } - - while (ma_device_get_state(pDevice) == ma_device_state_started) { - ma_result result; - - /* The first thing to do is wait for data to become available for reading. This will return an error code if the device has been stopped. */ - result = ma_device_wait_read__alsa(pDevice); - if (result != MA_SUCCESS) { - return result; - } - - /* Getting here means we should have data available. */ - resultALSA = ((ma_snd_pcm_readi_proc)pDevice->pContext->alsa.snd_pcm_readi)((ma_snd_pcm_t*)pDevice->alsa.pPCMCapture, pFramesOut, frameCount); - if (resultALSA >= 0) { - break; /* Success. */ - } else { - if (resultALSA == -EAGAIN) { - /*ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "EGAIN (read)\n");*/ - continue; /* Try again. */ - } else if (resultALSA == -EPIPE) { - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "EPIPE (read)\n"); - - /* Overrun. Recover and try again. If this fails we need to return an error. */ - resultALSA = ((ma_snd_pcm_recover_proc)pDevice->pContext->alsa.snd_pcm_recover)((ma_snd_pcm_t*)pDevice->alsa.pPCMCapture, resultALSA, MA_TRUE); - if (resultALSA < 0) { - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[ALSA] Failed to recover device after overrun."); - return ma_result_from_errno((int)-resultALSA); - } - - resultALSA = ((ma_snd_pcm_start_proc)pDevice->pContext->alsa.snd_pcm_start)((ma_snd_pcm_t*)pDevice->alsa.pPCMCapture); - if (resultALSA < 0) { - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[ALSA] Failed to start device after underrun."); - return ma_result_from_errno((int)-resultALSA); - } - - continue; /* Try reading again. */ - } - } - } - - if (pFramesRead != NULL) { - *pFramesRead = resultALSA; - } - - return MA_SUCCESS; -} - -static ma_result ma_device_write__alsa(ma_device* pDevice, const void* pFrames, ma_uint32 frameCount, ma_uint32* pFramesWritten) -{ - ma_snd_pcm_sframes_t resultALSA = 0; - - MA_ASSERT(pDevice != NULL); - MA_ASSERT(pFrames != NULL); - - if (pFramesWritten != NULL) { - *pFramesWritten = 0; - } - - while (ma_device_get_state(pDevice) == ma_device_state_started) { - ma_result result; - - /* The first thing to do is wait for space to become available for writing. This will return an error code if the device has been stopped. */ - result = ma_device_wait_write__alsa(pDevice); - if (result != MA_SUCCESS) { - return result; - } - - resultALSA = ((ma_snd_pcm_writei_proc)pDevice->pContext->alsa.snd_pcm_writei)((ma_snd_pcm_t*)pDevice->alsa.pPCMPlayback, pFrames, frameCount); - if (resultALSA >= 0) { - break; /* Success. */ - } else { - if (resultALSA == -EAGAIN) { - /*ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "EGAIN (write)\n");*/ - continue; /* Try again. */ - } else if (resultALSA == -EPIPE) { - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "EPIPE (write)\n"); - - /* Underrun. Recover and try again. If this fails we need to return an error. */ - resultALSA = ((ma_snd_pcm_recover_proc)pDevice->pContext->alsa.snd_pcm_recover)((ma_snd_pcm_t*)pDevice->alsa.pPCMPlayback, resultALSA, MA_TRUE); /* MA_TRUE=silent (don't print anything on error). */ - if (resultALSA < 0) { - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[ALSA] Failed to recover device after underrun."); - return ma_result_from_errno((int)-resultALSA); - } - - /* - In my testing I have had a situation where writei() does not automatically restart the device even though I've set it - up as such in the software parameters. What will happen is writei() will block indefinitely even though the number of - frames is well beyond the auto-start threshold. To work around this I've needed to add an explicit start here. Not sure - if this is me just being stupid and not recovering the device properly, but this definitely feels like something isn't - quite right here. - */ - resultALSA = ((ma_snd_pcm_start_proc)pDevice->pContext->alsa.snd_pcm_start)((ma_snd_pcm_t*)pDevice->alsa.pPCMPlayback); - if (resultALSA < 0) { - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[ALSA] Failed to start device after underrun."); - return ma_result_from_errno((int)-resultALSA); - } - - continue; /* Try writing again. */ - } - } - } - - if (pFramesWritten != NULL) { - *pFramesWritten = resultALSA; - } - - return MA_SUCCESS; -} - -static ma_result ma_device_data_loop_wakeup__alsa(ma_device* pDevice) -{ - ma_uint64 t = 1; - int resultWrite = 0; - - MA_ASSERT(pDevice != NULL); - - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "[ALSA] Waking up...\n"); - - /* Write to an eventfd to trigger a wakeup from poll() and abort any reading or writing. */ - if (pDevice->alsa.pPollDescriptorsCapture != NULL) { - resultWrite = write(pDevice->alsa.wakeupfdCapture, &t, sizeof(t)); - } - if (pDevice->alsa.pPollDescriptorsPlayback != NULL) { - resultWrite = write(pDevice->alsa.wakeupfdPlayback, &t, sizeof(t)); - } - - if (resultWrite < 0) { - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[ALSA] write() failed.\n"); - return ma_result_from_errno(errno); - } - - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "[ALSA] Waking up completed successfully.\n"); - - return MA_SUCCESS; -} - -static ma_result ma_context_uninit__alsa(ma_context* pContext) -{ - MA_ASSERT(pContext != NULL); - MA_ASSERT(pContext->backend == ma_backend_alsa); - - /* Clean up memory for memory leak checkers. */ - ((ma_snd_config_update_free_global_proc)pContext->alsa.snd_config_update_free_global)(); - -#ifndef MA_NO_RUNTIME_LINKING - ma_dlclose(ma_context_get_log(pContext), pContext->alsa.asoundSO); -#endif - - ma_mutex_uninit(&pContext->alsa.internalDeviceEnumLock); - - return MA_SUCCESS; -} - -static ma_result ma_context_init__alsa(ma_context* pContext, const ma_context_config* pConfig, ma_backend_callbacks* pCallbacks) -{ - ma_result result; -#ifndef MA_NO_RUNTIME_LINKING - const char* libasoundNames[] = { - "libasound.so.2", - "libasound.so" - }; - size_t i; - - for (i = 0; i < ma_countof(libasoundNames); ++i) { - pContext->alsa.asoundSO = ma_dlopen(ma_context_get_log(pContext), libasoundNames[i]); - if (pContext->alsa.asoundSO != NULL) { - break; - } - } - - if (pContext->alsa.asoundSO == NULL) { - ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_DEBUG, "[ALSA] Failed to open shared object.\n"); - return MA_NO_BACKEND; - } - - pContext->alsa.snd_pcm_open = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_open"); - pContext->alsa.snd_pcm_close = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_close"); - pContext->alsa.snd_pcm_hw_params_sizeof = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_hw_params_sizeof"); - pContext->alsa.snd_pcm_hw_params_any = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_hw_params_any"); - pContext->alsa.snd_pcm_hw_params_set_format = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_hw_params_set_format"); - pContext->alsa.snd_pcm_hw_params_set_format_first = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_hw_params_set_format_first"); - pContext->alsa.snd_pcm_hw_params_get_format_mask = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_hw_params_get_format_mask"); - pContext->alsa.snd_pcm_hw_params_set_channels = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_hw_params_set_channels"); - pContext->alsa.snd_pcm_hw_params_set_channels_near = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_hw_params_set_channels_near"); - pContext->alsa.snd_pcm_hw_params_set_channels_minmax = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_hw_params_set_channels_minmax"); - pContext->alsa.snd_pcm_hw_params_set_rate_resample = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_hw_params_set_rate_resample"); - pContext->alsa.snd_pcm_hw_params_set_rate = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_hw_params_set_rate"); - pContext->alsa.snd_pcm_hw_params_set_rate_near = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_hw_params_set_rate_near"); - pContext->alsa.snd_pcm_hw_params_set_buffer_size_near = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_hw_params_set_buffer_size_near"); - pContext->alsa.snd_pcm_hw_params_set_periods_near = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_hw_params_set_periods_near"); - pContext->alsa.snd_pcm_hw_params_set_access = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_hw_params_set_access"); - pContext->alsa.snd_pcm_hw_params_get_format = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_hw_params_get_format"); - pContext->alsa.snd_pcm_hw_params_get_channels = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_hw_params_get_channels"); - pContext->alsa.snd_pcm_hw_params_get_channels_min = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_hw_params_get_channels_min"); - pContext->alsa.snd_pcm_hw_params_get_channels_max = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_hw_params_get_channels_max"); - pContext->alsa.snd_pcm_hw_params_get_rate = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_hw_params_get_rate"); - pContext->alsa.snd_pcm_hw_params_get_rate_min = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_hw_params_get_rate_min"); - pContext->alsa.snd_pcm_hw_params_get_rate_max = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_hw_params_get_rate_max"); - pContext->alsa.snd_pcm_hw_params_get_buffer_size = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_hw_params_get_buffer_size"); - pContext->alsa.snd_pcm_hw_params_get_periods = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_hw_params_get_periods"); - pContext->alsa.snd_pcm_hw_params_get_access = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_hw_params_get_access"); - pContext->alsa.snd_pcm_hw_params_test_format = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_hw_params_test_format"); - pContext->alsa.snd_pcm_hw_params_test_channels = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_hw_params_test_channels"); - pContext->alsa.snd_pcm_hw_params_test_rate = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_hw_params_test_rate"); - pContext->alsa.snd_pcm_hw_params = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_hw_params"); - pContext->alsa.snd_pcm_sw_params_sizeof = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_sw_params_sizeof"); - pContext->alsa.snd_pcm_sw_params_current = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_sw_params_current"); - pContext->alsa.snd_pcm_sw_params_get_boundary = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_sw_params_get_boundary"); - pContext->alsa.snd_pcm_sw_params_set_avail_min = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_sw_params_set_avail_min"); - pContext->alsa.snd_pcm_sw_params_set_start_threshold = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_sw_params_set_start_threshold"); - pContext->alsa.snd_pcm_sw_params_set_stop_threshold = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_sw_params_set_stop_threshold"); - pContext->alsa.snd_pcm_sw_params = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_sw_params"); - pContext->alsa.snd_pcm_format_mask_sizeof = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_format_mask_sizeof"); - pContext->alsa.snd_pcm_format_mask_test = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_format_mask_test"); - pContext->alsa.snd_pcm_get_chmap = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_get_chmap"); - pContext->alsa.snd_pcm_state = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_state"); - pContext->alsa.snd_pcm_prepare = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_prepare"); - pContext->alsa.snd_pcm_start = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_start"); - pContext->alsa.snd_pcm_drop = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_drop"); - pContext->alsa.snd_pcm_drain = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_drain"); - pContext->alsa.snd_pcm_reset = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_reset"); - pContext->alsa.snd_device_name_hint = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_device_name_hint"); - pContext->alsa.snd_device_name_get_hint = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_device_name_get_hint"); - pContext->alsa.snd_card_get_index = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_card_get_index"); - pContext->alsa.snd_device_name_free_hint = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_device_name_free_hint"); - pContext->alsa.snd_pcm_mmap_begin = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_mmap_begin"); - pContext->alsa.snd_pcm_mmap_commit = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_mmap_commit"); - pContext->alsa.snd_pcm_recover = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_recover"); - pContext->alsa.snd_pcm_readi = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_readi"); - pContext->alsa.snd_pcm_writei = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_writei"); - pContext->alsa.snd_pcm_avail = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_avail"); - pContext->alsa.snd_pcm_avail_update = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_avail_update"); - pContext->alsa.snd_pcm_wait = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_wait"); - pContext->alsa.snd_pcm_nonblock = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_nonblock"); - pContext->alsa.snd_pcm_info = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_info"); - pContext->alsa.snd_pcm_info_sizeof = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_info_sizeof"); - pContext->alsa.snd_pcm_info_get_name = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_info_get_name"); - pContext->alsa.snd_pcm_poll_descriptors = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_poll_descriptors"); - pContext->alsa.snd_pcm_poll_descriptors_count = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_poll_descriptors_count"); - pContext->alsa.snd_pcm_poll_descriptors_revents = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_poll_descriptors_revents"); - pContext->alsa.snd_config_update_free_global = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_config_update_free_global"); -#else - /* The system below is just for type safety. */ - ma_snd_pcm_open_proc _snd_pcm_open = snd_pcm_open; - ma_snd_pcm_close_proc _snd_pcm_close = snd_pcm_close; - ma_snd_pcm_hw_params_sizeof_proc _snd_pcm_hw_params_sizeof = snd_pcm_hw_params_sizeof; - ma_snd_pcm_hw_params_any_proc _snd_pcm_hw_params_any = snd_pcm_hw_params_any; - ma_snd_pcm_hw_params_set_format_proc _snd_pcm_hw_params_set_format = snd_pcm_hw_params_set_format; - ma_snd_pcm_hw_params_set_format_first_proc _snd_pcm_hw_params_set_format_first = snd_pcm_hw_params_set_format_first; - ma_snd_pcm_hw_params_get_format_mask_proc _snd_pcm_hw_params_get_format_mask = snd_pcm_hw_params_get_format_mask; - ma_snd_pcm_hw_params_set_channels_proc _snd_pcm_hw_params_set_channels = snd_pcm_hw_params_set_channels; - ma_snd_pcm_hw_params_set_channels_near_proc _snd_pcm_hw_params_set_channels_near = snd_pcm_hw_params_set_channels_near; - ma_snd_pcm_hw_params_set_rate_resample_proc _snd_pcm_hw_params_set_rate_resample = snd_pcm_hw_params_set_rate_resample; - ma_snd_pcm_hw_params_set_rate_near _snd_pcm_hw_params_set_rate = snd_pcm_hw_params_set_rate; - ma_snd_pcm_hw_params_set_rate_near_proc _snd_pcm_hw_params_set_rate_near = snd_pcm_hw_params_set_rate_near; - ma_snd_pcm_hw_params_set_rate_minmax_proc _snd_pcm_hw_params_set_rate_minmax = snd_pcm_hw_params_set_rate_minmax; - ma_snd_pcm_hw_params_set_buffer_size_near_proc _snd_pcm_hw_params_set_buffer_size_near = snd_pcm_hw_params_set_buffer_size_near; - ma_snd_pcm_hw_params_set_periods_near_proc _snd_pcm_hw_params_set_periods_near = snd_pcm_hw_params_set_periods_near; - ma_snd_pcm_hw_params_set_access_proc _snd_pcm_hw_params_set_access = snd_pcm_hw_params_set_access; - ma_snd_pcm_hw_params_get_format_proc _snd_pcm_hw_params_get_format = snd_pcm_hw_params_get_format; - ma_snd_pcm_hw_params_get_channels_proc _snd_pcm_hw_params_get_channels = snd_pcm_hw_params_get_channels; - ma_snd_pcm_hw_params_get_channels_min_proc _snd_pcm_hw_params_get_channels_min = snd_pcm_hw_params_get_channels_min; - ma_snd_pcm_hw_params_get_channels_max_proc _snd_pcm_hw_params_get_channels_max = snd_pcm_hw_params_get_channels_max; - ma_snd_pcm_hw_params_get_rate_proc _snd_pcm_hw_params_get_rate = snd_pcm_hw_params_get_rate; - ma_snd_pcm_hw_params_get_rate_min_proc _snd_pcm_hw_params_get_rate_min = snd_pcm_hw_params_get_rate_min; - ma_snd_pcm_hw_params_get_rate_max_proc _snd_pcm_hw_params_get_rate_max = snd_pcm_hw_params_get_rate_max; - ma_snd_pcm_hw_params_get_buffer_size_proc _snd_pcm_hw_params_get_buffer_size = snd_pcm_hw_params_get_buffer_size; - ma_snd_pcm_hw_params_get_periods_proc _snd_pcm_hw_params_get_periods = snd_pcm_hw_params_get_periods; - ma_snd_pcm_hw_params_get_access_proc _snd_pcm_hw_params_get_access = snd_pcm_hw_params_get_access; - ma_snd_pcm_hw_params_test_format_proc _snd_pcm_hw_params_test_format = snd_pcm_hw_params_test_format; - ma_snd_pcm_hw_params_test_channels_proc _snd_pcm_hw_params_test_channels = snd_pcm_hw_params_test_channels; - ma_snd_pcm_hw_params_test_rate_proc _snd_pcm_hw_params_test_rate = snd_pcm_hw_params_test_rate; - ma_snd_pcm_hw_params_proc _snd_pcm_hw_params = snd_pcm_hw_params; - ma_snd_pcm_sw_params_sizeof_proc _snd_pcm_sw_params_sizeof = snd_pcm_sw_params_sizeof; - ma_snd_pcm_sw_params_current_proc _snd_pcm_sw_params_current = snd_pcm_sw_params_current; - ma_snd_pcm_sw_params_get_boundary_proc _snd_pcm_sw_params_get_boundary = snd_pcm_sw_params_get_boundary; - ma_snd_pcm_sw_params_set_avail_min_proc _snd_pcm_sw_params_set_avail_min = snd_pcm_sw_params_set_avail_min; - ma_snd_pcm_sw_params_set_start_threshold_proc _snd_pcm_sw_params_set_start_threshold = snd_pcm_sw_params_set_start_threshold; - ma_snd_pcm_sw_params_set_stop_threshold_proc _snd_pcm_sw_params_set_stop_threshold = snd_pcm_sw_params_set_stop_threshold; - ma_snd_pcm_sw_params_proc _snd_pcm_sw_params = snd_pcm_sw_params; - ma_snd_pcm_format_mask_sizeof_proc _snd_pcm_format_mask_sizeof = snd_pcm_format_mask_sizeof; - ma_snd_pcm_format_mask_test_proc _snd_pcm_format_mask_test = snd_pcm_format_mask_test; - ma_snd_pcm_get_chmap_proc _snd_pcm_get_chmap = snd_pcm_get_chmap; - ma_snd_pcm_state_proc _snd_pcm_state = snd_pcm_state; - ma_snd_pcm_prepare_proc _snd_pcm_prepare = snd_pcm_prepare; - ma_snd_pcm_start_proc _snd_pcm_start = snd_pcm_start; - ma_snd_pcm_drop_proc _snd_pcm_drop = snd_pcm_drop; - ma_snd_pcm_drain_proc _snd_pcm_drain = snd_pcm_drain; - ma_snd_pcm_reset_proc _snd_pcm_reset = snd_pcm_reset; - ma_snd_device_name_hint_proc _snd_device_name_hint = snd_device_name_hint; - ma_snd_device_name_get_hint_proc _snd_device_name_get_hint = snd_device_name_get_hint; - ma_snd_card_get_index_proc _snd_card_get_index = snd_card_get_index; - ma_snd_device_name_free_hint_proc _snd_device_name_free_hint = snd_device_name_free_hint; - ma_snd_pcm_mmap_begin_proc _snd_pcm_mmap_begin = snd_pcm_mmap_begin; - ma_snd_pcm_mmap_commit_proc _snd_pcm_mmap_commit = snd_pcm_mmap_commit; - ma_snd_pcm_recover_proc _snd_pcm_recover = snd_pcm_recover; - ma_snd_pcm_readi_proc _snd_pcm_readi = snd_pcm_readi; - ma_snd_pcm_writei_proc _snd_pcm_writei = snd_pcm_writei; - ma_snd_pcm_avail_proc _snd_pcm_avail = snd_pcm_avail; - ma_snd_pcm_avail_update_proc _snd_pcm_avail_update = snd_pcm_avail_update; - ma_snd_pcm_wait_proc _snd_pcm_wait = snd_pcm_wait; - ma_snd_pcm_nonblock_proc _snd_pcm_nonblock = snd_pcm_nonblock; - ma_snd_pcm_info_proc _snd_pcm_info = snd_pcm_info; - ma_snd_pcm_info_sizeof_proc _snd_pcm_info_sizeof = snd_pcm_info_sizeof; - ma_snd_pcm_info_get_name_proc _snd_pcm_info_get_name = snd_pcm_info_get_name; - ma_snd_pcm_poll_descriptors _snd_pcm_poll_descriptors = snd_pcm_poll_descriptors; - ma_snd_pcm_poll_descriptors_count _snd_pcm_poll_descriptors_count = snd_pcm_poll_descriptors_count; - ma_snd_pcm_poll_descriptors_revents _snd_pcm_poll_descriptors_revents = snd_pcm_poll_descriptors_revents; - ma_snd_config_update_free_global_proc _snd_config_update_free_global = snd_config_update_free_global; - - pContext->alsa.snd_pcm_open = (ma_proc)_snd_pcm_open; - pContext->alsa.snd_pcm_close = (ma_proc)_snd_pcm_close; - pContext->alsa.snd_pcm_hw_params_sizeof = (ma_proc)_snd_pcm_hw_params_sizeof; - pContext->alsa.snd_pcm_hw_params_any = (ma_proc)_snd_pcm_hw_params_any; - pContext->alsa.snd_pcm_hw_params_set_format = (ma_proc)_snd_pcm_hw_params_set_format; - pContext->alsa.snd_pcm_hw_params_set_format_first = (ma_proc)_snd_pcm_hw_params_set_format_first; - pContext->alsa.snd_pcm_hw_params_get_format_mask = (ma_proc)_snd_pcm_hw_params_get_format_mask; - pContext->alsa.snd_pcm_hw_params_set_channels = (ma_proc)_snd_pcm_hw_params_set_channels; - pContext->alsa.snd_pcm_hw_params_set_channels_near = (ma_proc)_snd_pcm_hw_params_set_channels_near; - pContext->alsa.snd_pcm_hw_params_set_channels_minmax = (ma_proc)_snd_pcm_hw_params_set_channels_minmax; - pContext->alsa.snd_pcm_hw_params_set_rate_resample = (ma_proc)_snd_pcm_hw_params_set_rate_resample; - pContext->alsa.snd_pcm_hw_params_set_rate = (ma_proc)_snd_pcm_hw_params_set_rate; - pContext->alsa.snd_pcm_hw_params_set_rate_near = (ma_proc)_snd_pcm_hw_params_set_rate_near; - pContext->alsa.snd_pcm_hw_params_set_buffer_size_near = (ma_proc)_snd_pcm_hw_params_set_buffer_size_near; - pContext->alsa.snd_pcm_hw_params_set_periods_near = (ma_proc)_snd_pcm_hw_params_set_periods_near; - pContext->alsa.snd_pcm_hw_params_set_access = (ma_proc)_snd_pcm_hw_params_set_access; - pContext->alsa.snd_pcm_hw_params_get_format = (ma_proc)_snd_pcm_hw_params_get_format; - pContext->alsa.snd_pcm_hw_params_get_channels = (ma_proc)_snd_pcm_hw_params_get_channels; - pContext->alsa.snd_pcm_hw_params_get_channels_min = (ma_proc)_snd_pcm_hw_params_get_channels_min; - pContext->alsa.snd_pcm_hw_params_get_channels_max = (ma_proc)_snd_pcm_hw_params_get_channels_max; - pContext->alsa.snd_pcm_hw_params_get_rate = (ma_proc)_snd_pcm_hw_params_get_rate; - pContext->alsa.snd_pcm_hw_params_get_rate_min = (ma_proc)_snd_pcm_hw_params_get_rate_min; - pContext->alsa.snd_pcm_hw_params_get_rate_max = (ma_proc)_snd_pcm_hw_params_get_rate_max; - pContext->alsa.snd_pcm_hw_params_get_buffer_size = (ma_proc)_snd_pcm_hw_params_get_buffer_size; - pContext->alsa.snd_pcm_hw_params_get_periods = (ma_proc)_snd_pcm_hw_params_get_periods; - pContext->alsa.snd_pcm_hw_params_get_access = (ma_proc)_snd_pcm_hw_params_get_access; - pContext->alsa.snd_pcm_hw_params_test_format = (ma_proc)_snd_pcm_hw_params_test_format; - pContext->alsa.snd_pcm_hw_params_test_channels = (ma_proc)_snd_pcm_hw_params_test_channels; - pContext->alsa.snd_pcm_hw_params_test_rate = (ma_proc)_snd_pcm_hw_params_test_rate; - pContext->alsa.snd_pcm_hw_params = (ma_proc)_snd_pcm_hw_params; - pContext->alsa.snd_pcm_sw_params_sizeof = (ma_proc)_snd_pcm_sw_params_sizeof; - pContext->alsa.snd_pcm_sw_params_current = (ma_proc)_snd_pcm_sw_params_current; - pContext->alsa.snd_pcm_sw_params_get_boundary = (ma_proc)_snd_pcm_sw_params_get_boundary; - pContext->alsa.snd_pcm_sw_params_set_avail_min = (ma_proc)_snd_pcm_sw_params_set_avail_min; - pContext->alsa.snd_pcm_sw_params_set_start_threshold = (ma_proc)_snd_pcm_sw_params_set_start_threshold; - pContext->alsa.snd_pcm_sw_params_set_stop_threshold = (ma_proc)_snd_pcm_sw_params_set_stop_threshold; - pContext->alsa.snd_pcm_sw_params = (ma_proc)_snd_pcm_sw_params; - pContext->alsa.snd_pcm_format_mask_sizeof = (ma_proc)_snd_pcm_format_mask_sizeof; - pContext->alsa.snd_pcm_format_mask_test = (ma_proc)_snd_pcm_format_mask_test; - pContext->alsa.snd_pcm_get_chmap = (ma_proc)_snd_pcm_get_chmap; - pContext->alsa.snd_pcm_state = (ma_proc)_snd_pcm_state; - pContext->alsa.snd_pcm_prepare = (ma_proc)_snd_pcm_prepare; - pContext->alsa.snd_pcm_start = (ma_proc)_snd_pcm_start; - pContext->alsa.snd_pcm_drop = (ma_proc)_snd_pcm_drop; - pContext->alsa.snd_pcm_drain = (ma_proc)_snd_pcm_drain; - pContext->alsa.snd_pcm_reset = (ma_proc)_snd_pcm_reset; - pContext->alsa.snd_device_name_hint = (ma_proc)_snd_device_name_hint; - pContext->alsa.snd_device_name_get_hint = (ma_proc)_snd_device_name_get_hint; - pContext->alsa.snd_card_get_index = (ma_proc)_snd_card_get_index; - pContext->alsa.snd_device_name_free_hint = (ma_proc)_snd_device_name_free_hint; - pContext->alsa.snd_pcm_mmap_begin = (ma_proc)_snd_pcm_mmap_begin; - pContext->alsa.snd_pcm_mmap_commit = (ma_proc)_snd_pcm_mmap_commit; - pContext->alsa.snd_pcm_recover = (ma_proc)_snd_pcm_recover; - pContext->alsa.snd_pcm_readi = (ma_proc)_snd_pcm_readi; - pContext->alsa.snd_pcm_writei = (ma_proc)_snd_pcm_writei; - pContext->alsa.snd_pcm_avail = (ma_proc)_snd_pcm_avail; - pContext->alsa.snd_pcm_avail_update = (ma_proc)_snd_pcm_avail_update; - pContext->alsa.snd_pcm_wait = (ma_proc)_snd_pcm_wait; - pContext->alsa.snd_pcm_nonblock = (ma_proc)_snd_pcm_nonblock; - pContext->alsa.snd_pcm_info = (ma_proc)_snd_pcm_info; - pContext->alsa.snd_pcm_info_sizeof = (ma_proc)_snd_pcm_info_sizeof; - pContext->alsa.snd_pcm_info_get_name = (ma_proc)_snd_pcm_info_get_name; - pContext->alsa.snd_pcm_poll_descriptors = (ma_proc)_snd_pcm_poll_descriptors; - pContext->alsa.snd_pcm_poll_descriptors_count = (ma_proc)_snd_pcm_poll_descriptors_count; - pContext->alsa.snd_pcm_poll_descriptors_revents = (ma_proc)_snd_pcm_poll_descriptors_revents; - pContext->alsa.snd_config_update_free_global = (ma_proc)_snd_config_update_free_global; -#endif - - pContext->alsa.useVerboseDeviceEnumeration = pConfig->alsa.useVerboseDeviceEnumeration; - - result = ma_mutex_init(&pContext->alsa.internalDeviceEnumLock); - if (result != MA_SUCCESS) { - ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[ALSA] WARNING: Failed to initialize mutex for internal device enumeration."); - return result; - } - - pCallbacks->onContextInit = ma_context_init__alsa; - pCallbacks->onContextUninit = ma_context_uninit__alsa; - pCallbacks->onContextEnumerateDevices = ma_context_enumerate_devices__alsa; - pCallbacks->onContextGetDeviceInfo = ma_context_get_device_info__alsa; - pCallbacks->onDeviceInit = ma_device_init__alsa; - pCallbacks->onDeviceUninit = ma_device_uninit__alsa; - pCallbacks->onDeviceStart = ma_device_start__alsa; - pCallbacks->onDeviceStop = ma_device_stop__alsa; - pCallbacks->onDeviceRead = ma_device_read__alsa; - pCallbacks->onDeviceWrite = ma_device_write__alsa; - pCallbacks->onDeviceDataLoop = NULL; - pCallbacks->onDeviceDataLoopWakeup = ma_device_data_loop_wakeup__alsa; - - return MA_SUCCESS; -} -#endif /* MA_HAS_ALSA */ - - - -/****************************************************************************** - -PulseAudio Backend - -******************************************************************************/ -#ifdef MA_HAS_PULSEAUDIO -/* -The PulseAudio API, along with Apple's Core Audio, is the worst of the mainstream audio APIs. This is a brief description of what's going on -in the PulseAudio backend. I apologize if this gets a bit ranty for your liking - you might want to skip this discussion. - -PulseAudio has something they call the "Simple API", which unfortunately isn't suitable for miniaudio. I've not seen anywhere where it -allows you to enumerate over devices, nor does it seem to support the ability to stop and start streams. Looking at the documentation, it -appears as though the stream is constantly running and you prevent sound from being emitted or captured by simply not calling the read or -write functions. This is not a professional solution as it would be much better to *actually* stop the underlying stream. Perhaps the -simple API has some smarts to do this automatically, but I'm not sure. Another limitation with the simple API is that it seems inefficient -when you want to have multiple streams to a single context. For these reasons, miniaudio is not using the simple API. - -Since we're not using the simple API, we're left with the asynchronous API as our only other option. And boy, is this where it starts to -get fun, and I don't mean that in a good way... - -The problems start with the very name of the API - "asynchronous". Yes, this is an asynchronous oriented API which means your commands -don't immediately take effect. You instead need to issue your commands, and then wait for them to complete. The waiting mechanism is -enabled through the use of a "main loop". In the asynchronous API you cannot get away from the main loop, and the main loop is where almost -all of PulseAudio's problems stem from. - -When you first initialize PulseAudio you need an object referred to as "main loop". You can implement this yourself by defining your own -vtable, but it's much easier to just use one of the built-in main loop implementations. There's two generic implementations called -pa_mainloop and pa_threaded_mainloop, and another implementation specific to GLib called pa_glib_mainloop. We're using pa_threaded_mainloop -because it simplifies management of the worker thread. The idea of the main loop object is pretty self explanatory - you're supposed to use -it to implement a worker thread which runs in a loop. The main loop is where operations are actually executed. - -To initialize the main loop, you just use `pa_threaded_mainloop_new()`. This is the first function you'll call. You can then get a pointer -to the vtable with `pa_threaded_mainloop_get_api()` (the main loop vtable is called `pa_mainloop_api`). Again, you can bypass the threaded -main loop object entirely and just implement `pa_mainloop_api` directly, but there's no need for it unless you're doing something extremely -specialized such as if you want to integrate it into your application's existing main loop infrastructure. - -(EDIT 2021-01-26: miniaudio is no longer using `pa_threaded_mainloop` due to this issue: https://github.com/mackron/miniaudio/issues/262. -It is now using `pa_mainloop` which turns out to be a simpler solution anyway. The rest of this rant still applies, however.) - -Once you have your main loop vtable (the `pa_mainloop_api` object) you can create the PulseAudio context. This is very similar to -miniaudio's context and they map to each other quite well. You have one context to many streams, which is basically the same as miniaudio's -one `ma_context` to many `ma_device`s. Here's where it starts to get annoying, however. When you first create the PulseAudio context, which -is done with `pa_context_new()`, it's not actually connected to anything. When you connect, you call `pa_context_connect()`. However, if -you remember, PulseAudio is an asynchronous API. That means you cannot just assume the context is connected after `pa_context_context()` -has returned. You instead need to wait for it to connect. To do this, you need to either wait for a callback to get fired, which you can -set with `pa_context_set_state_callback()`, or you can continuously poll the context's state. Either way, you need to run this in a loop. -All objects from here out are created from the context, and, I believe, you can't be creating these objects until the context is connected. -This waiting loop is therefore unavoidable. In order for the waiting to ever complete, however, the main loop needs to be running. Before -attempting to connect the context, the main loop needs to be started with `pa_threaded_mainloop_start()`. - -The reason for this asynchronous design is to support cases where you're connecting to a remote server, say through a local network or an -internet connection. However, the *VAST* majority of cases don't involve this at all - they just connect to a local "server" running on the -host machine. The fact that this would be the default rather than making `pa_context_connect()` synchronous tends to boggle the mind. - -Once the context has been created and connected you can start creating a stream. A PulseAudio stream is analogous to miniaudio's device. -The initialization of a stream is fairly standard - you configure some attributes (analogous to miniaudio's device config) and then call -`pa_stream_new()` to actually create it. Here is where we start to get into "operations". When configuring the stream, you can get -information about the source (such as sample format, sample rate, etc.), however it's not synchronous. Instead, a `pa_operation` object -is returned from `pa_context_get_source_info_by_name()` (capture) or `pa_context_get_sink_info_by_name()` (playback). Then, you need to -run a loop (again!) to wait for the operation to complete which you can determine via a callback or polling, just like we did with the -context. Then, as an added bonus, you need to decrement the reference counter of the `pa_operation` object to ensure memory is cleaned up. -All of that just to retrieve basic information about a device! - -Once the basic information about the device has been retrieved, miniaudio can now create the stream with `ma_stream_new()`. Like the -context, this needs to be connected. But we need to be careful here, because we're now about to introduce one of the most horrific design -choices in PulseAudio. - -PulseAudio allows you to specify a callback that is fired when data can be written to or read from a stream. The language is important here -because PulseAudio takes it literally, specifically the "can be". You would think these callbacks would be appropriate as the place for -writing and reading data to and from the stream, and that would be right, except when it's not. When you initialize the stream, you can -set a flag that tells PulseAudio to not start the stream automatically. This is required because miniaudio does not auto-start devices -straight after initialization - you need to call `ma_device_start()` manually. The problem is that even when this flag is specified, -PulseAudio will immediately fire its write or read callback. This is *technically* correct (based on the wording in the documentation) -because indeed, data *can* be written at this point. The problem is that it's not *practical*. It makes sense that the write/read callback -would be where a program will want to write or read data to or from the stream, but when it's called before the application has even -requested that the stream be started, it's just not practical because the program probably isn't ready for any kind of data delivery at -that point (it may still need to load files or whatnot). Instead, this callback should only be fired when the application requests the -stream be started which is how it works with literally *every* other callback-based audio API. Since miniaudio forbids firing of the data -callback until the device has been started (as it should be with *all* callback based APIs), logic needs to be added to ensure miniaudio -doesn't just blindly fire the application-defined data callback from within the PulseAudio callback before the stream has actually been -started. The device state is used for this - if the state is anything other than `ma_device_state_starting` or `ma_device_state_started`, the main data -callback is not fired. - -This, unfortunately, is not the end of the problems with the PulseAudio write callback. Any normal callback based audio API will -continuously fire the callback at regular intervals based on the size of the internal buffer. This will only ever be fired when the device -is running, and will be fired regardless of whether or not the user actually wrote anything to the device/stream. This not the case in -PulseAudio. In PulseAudio, the data callback will *only* be called if you wrote something to it previously. That means, if you don't call -`pa_stream_write()`, the callback will not get fired. On the surface you wouldn't think this would matter because you should be always -writing data, and if you don't have anything to write, just write silence. That's fine until you want to drain the stream. You see, if -you're continuously writing data to the stream, the stream will never get drained! That means in order to drain the stream, you need to -*not* write data to it! But remember, when you don't write data to the stream, the callback won't get fired again! Why is draining -important? Because that's how we've defined stopping to work in miniaudio. In miniaudio, stopping the device requires it to be drained -before returning from ma_device_stop(). So we've stopped the device, which requires us to drain, but draining requires us to *not* write -data to the stream (or else it won't ever complete draining), but not writing to the stream means the callback won't get fired again! - -This becomes a problem when stopping and then restarting the device. When the device is stopped, it's drained, which requires us to *not* -write anything to the stream. But then, since we didn't write anything to it, the write callback will *never* get called again if we just -resume the stream naively. This means that starting the stream requires us to write data to the stream from outside the callback. This -disconnect is something PulseAudio has got seriously wrong - there should only ever be a single source of data delivery, that being the -callback. (I have tried using `pa_stream_flush()` to trigger the write callback to fire, but this just doesn't work for some reason.) - -Once you've created the stream, you need to connect it which involves the whole waiting procedure. This is the same process as the context, -only this time you'll poll for the state with `pa_stream_get_status()`. The starting and stopping of a streaming is referred to as -"corking" in PulseAudio. The analogy is corking a barrel. To start the stream, you uncork it, to stop it you cork it. Personally I think -it's silly - why would you not just call it "starting" and "stopping" like any other normal audio API? Anyway, the act of corking is, you -guessed it, asynchronous. This means you'll need our waiting loop as usual. Again, why this asynchronous design is the default is -absolutely beyond me. Would it really be that hard to just make it run synchronously? - -Teardown is pretty simple (what?!). It's just a matter of calling the relevant `_unref()` function on each object in reverse order that -they were initialized in. - -That's about it from the PulseAudio side. A bit ranty, I know, but they really need to fix that main loop and callback system. They're -embarrassingly unpractical. The main loop thing is an easy fix - have synchronous versions of all APIs. If an application wants these to -run asynchronously, they can execute them in a separate thread themselves. The desire to run these asynchronously is such a niche -requirement - it makes no sense to make it the default. The stream write callback needs to be change, or an alternative provided, that is -constantly fired, regardless of whether or not `pa_stream_write()` has been called, and it needs to take a pointer to a buffer as a -parameter which the program just writes to directly rather than having to call `pa_stream_writable_size()` and `pa_stream_write()`. These -changes alone will change PulseAudio from one of the worst audio APIs to one of the best. -*/ - - -/* -It is assumed pulseaudio.h is available when linking at compile time. When linking at compile time, we use the declarations in the header -to check for type safety. We cannot do this when linking at run time because the header might not be available. -*/ -#ifdef MA_NO_RUNTIME_LINKING - -/* pulseaudio.h marks some functions with "inline" which isn't always supported. Need to emulate it. */ -#if !defined(__cplusplus) - #if defined(__STRICT_ANSI__) - #if !defined(inline) - #define inline __inline__ __attribute__((always_inline)) - #define MA_INLINE_DEFINED - #endif - #endif -#endif -#include -#if defined(MA_INLINE_DEFINED) - #undef inline - #undef MA_INLINE_DEFINED -#endif - -#define MA_PA_OK PA_OK -#define MA_PA_ERR_ACCESS PA_ERR_ACCESS -#define MA_PA_ERR_INVALID PA_ERR_INVALID -#define MA_PA_ERR_NOENTITY PA_ERR_NOENTITY -#define MA_PA_ERR_NOTSUPPORTED PA_ERR_NOTSUPPORTED - -#define MA_PA_CHANNELS_MAX PA_CHANNELS_MAX -#define MA_PA_RATE_MAX PA_RATE_MAX - -typedef pa_context_flags_t ma_pa_context_flags_t; -#define MA_PA_CONTEXT_NOFLAGS PA_CONTEXT_NOFLAGS -#define MA_PA_CONTEXT_NOAUTOSPAWN PA_CONTEXT_NOAUTOSPAWN -#define MA_PA_CONTEXT_NOFAIL PA_CONTEXT_NOFAIL - -typedef pa_stream_flags_t ma_pa_stream_flags_t; -#define MA_PA_STREAM_NOFLAGS PA_STREAM_NOFLAGS -#define MA_PA_STREAM_START_CORKED PA_STREAM_START_CORKED -#define MA_PA_STREAM_INTERPOLATE_TIMING PA_STREAM_INTERPOLATE_TIMING -#define MA_PA_STREAM_NOT_MONOTONIC PA_STREAM_NOT_MONOTONIC -#define MA_PA_STREAM_AUTO_TIMING_UPDATE PA_STREAM_AUTO_TIMING_UPDATE -#define MA_PA_STREAM_NO_REMAP_CHANNELS PA_STREAM_NO_REMAP_CHANNELS -#define MA_PA_STREAM_NO_REMIX_CHANNELS PA_STREAM_NO_REMIX_CHANNELS -#define MA_PA_STREAM_FIX_FORMAT PA_STREAM_FIX_FORMAT -#define MA_PA_STREAM_FIX_RATE PA_STREAM_FIX_RATE -#define MA_PA_STREAM_FIX_CHANNELS PA_STREAM_FIX_CHANNELS -#define MA_PA_STREAM_DONT_MOVE PA_STREAM_DONT_MOVE -#define MA_PA_STREAM_VARIABLE_RATE PA_STREAM_VARIABLE_RATE -#define MA_PA_STREAM_PEAK_DETECT PA_STREAM_PEAK_DETECT -#define MA_PA_STREAM_START_MUTED PA_STREAM_START_MUTED -#define MA_PA_STREAM_ADJUST_LATENCY PA_STREAM_ADJUST_LATENCY -#define MA_PA_STREAM_EARLY_REQUESTS PA_STREAM_EARLY_REQUESTS -#define MA_PA_STREAM_DONT_INHIBIT_AUTO_SUSPEND PA_STREAM_DONT_INHIBIT_AUTO_SUSPEND -#define MA_PA_STREAM_START_UNMUTED PA_STREAM_START_UNMUTED -#define MA_PA_STREAM_FAIL_ON_SUSPEND PA_STREAM_FAIL_ON_SUSPEND -#define MA_PA_STREAM_RELATIVE_VOLUME PA_STREAM_RELATIVE_VOLUME -#define MA_PA_STREAM_PASSTHROUGH PA_STREAM_PASSTHROUGH - -typedef pa_sink_flags_t ma_pa_sink_flags_t; -#define MA_PA_SINK_NOFLAGS PA_SINK_NOFLAGS -#define MA_PA_SINK_HW_VOLUME_CTRL PA_SINK_HW_VOLUME_CTRL -#define MA_PA_SINK_LATENCY PA_SINK_LATENCY -#define MA_PA_SINK_HARDWARE PA_SINK_HARDWARE -#define MA_PA_SINK_NETWORK PA_SINK_NETWORK -#define MA_PA_SINK_HW_MUTE_CTRL PA_SINK_HW_MUTE_CTRL -#define MA_PA_SINK_DECIBEL_VOLUME PA_SINK_DECIBEL_VOLUME -#define MA_PA_SINK_FLAT_VOLUME PA_SINK_FLAT_VOLUME -#define MA_PA_SINK_DYNAMIC_LATENCY PA_SINK_DYNAMIC_LATENCY -#define MA_PA_SINK_SET_FORMATS PA_SINK_SET_FORMATS - -typedef pa_source_flags_t ma_pa_source_flags_t; -#define MA_PA_SOURCE_NOFLAGS PA_SOURCE_NOFLAGS -#define MA_PA_SOURCE_HW_VOLUME_CTRL PA_SOURCE_HW_VOLUME_CTRL -#define MA_PA_SOURCE_LATENCY PA_SOURCE_LATENCY -#define MA_PA_SOURCE_HARDWARE PA_SOURCE_HARDWARE -#define MA_PA_SOURCE_NETWORK PA_SOURCE_NETWORK -#define MA_PA_SOURCE_HW_MUTE_CTRL PA_SOURCE_HW_MUTE_CTRL -#define MA_PA_SOURCE_DECIBEL_VOLUME PA_SOURCE_DECIBEL_VOLUME -#define MA_PA_SOURCE_DYNAMIC_LATENCY PA_SOURCE_DYNAMIC_LATENCY -#define MA_PA_SOURCE_FLAT_VOLUME PA_SOURCE_FLAT_VOLUME - -typedef pa_context_state_t ma_pa_context_state_t; -#define MA_PA_CONTEXT_UNCONNECTED PA_CONTEXT_UNCONNECTED -#define MA_PA_CONTEXT_CONNECTING PA_CONTEXT_CONNECTING -#define MA_PA_CONTEXT_AUTHORIZING PA_CONTEXT_AUTHORIZING -#define MA_PA_CONTEXT_SETTING_NAME PA_CONTEXT_SETTING_NAME -#define MA_PA_CONTEXT_READY PA_CONTEXT_READY -#define MA_PA_CONTEXT_FAILED PA_CONTEXT_FAILED -#define MA_PA_CONTEXT_TERMINATED PA_CONTEXT_TERMINATED - -typedef pa_stream_state_t ma_pa_stream_state_t; -#define MA_PA_STREAM_UNCONNECTED PA_STREAM_UNCONNECTED -#define MA_PA_STREAM_CREATING PA_STREAM_CREATING -#define MA_PA_STREAM_READY PA_STREAM_READY -#define MA_PA_STREAM_FAILED PA_STREAM_FAILED -#define MA_PA_STREAM_TERMINATED PA_STREAM_TERMINATED - -typedef pa_operation_state_t ma_pa_operation_state_t; -#define MA_PA_OPERATION_RUNNING PA_OPERATION_RUNNING -#define MA_PA_OPERATION_DONE PA_OPERATION_DONE -#define MA_PA_OPERATION_CANCELLED PA_OPERATION_CANCELLED - -typedef pa_sink_state_t ma_pa_sink_state_t; -#define MA_PA_SINK_INVALID_STATE PA_SINK_INVALID_STATE -#define MA_PA_SINK_RUNNING PA_SINK_RUNNING -#define MA_PA_SINK_IDLE PA_SINK_IDLE -#define MA_PA_SINK_SUSPENDED PA_SINK_SUSPENDED - -typedef pa_source_state_t ma_pa_source_state_t; -#define MA_PA_SOURCE_INVALID_STATE PA_SOURCE_INVALID_STATE -#define MA_PA_SOURCE_RUNNING PA_SOURCE_RUNNING -#define MA_PA_SOURCE_IDLE PA_SOURCE_IDLE -#define MA_PA_SOURCE_SUSPENDED PA_SOURCE_SUSPENDED - -typedef pa_seek_mode_t ma_pa_seek_mode_t; -#define MA_PA_SEEK_RELATIVE PA_SEEK_RELATIVE -#define MA_PA_SEEK_ABSOLUTE PA_SEEK_ABSOLUTE -#define MA_PA_SEEK_RELATIVE_ON_READ PA_SEEK_RELATIVE_ON_READ -#define MA_PA_SEEK_RELATIVE_END PA_SEEK_RELATIVE_END - -typedef pa_channel_position_t ma_pa_channel_position_t; -#define MA_PA_CHANNEL_POSITION_INVALID PA_CHANNEL_POSITION_INVALID -#define MA_PA_CHANNEL_POSITION_MONO PA_CHANNEL_POSITION_MONO -#define MA_PA_CHANNEL_POSITION_FRONT_LEFT PA_CHANNEL_POSITION_FRONT_LEFT -#define MA_PA_CHANNEL_POSITION_FRONT_RIGHT PA_CHANNEL_POSITION_FRONT_RIGHT -#define MA_PA_CHANNEL_POSITION_FRONT_CENTER PA_CHANNEL_POSITION_FRONT_CENTER -#define MA_PA_CHANNEL_POSITION_REAR_CENTER PA_CHANNEL_POSITION_REAR_CENTER -#define MA_PA_CHANNEL_POSITION_REAR_LEFT PA_CHANNEL_POSITION_REAR_LEFT -#define MA_PA_CHANNEL_POSITION_REAR_RIGHT PA_CHANNEL_POSITION_REAR_RIGHT -#define MA_PA_CHANNEL_POSITION_LFE PA_CHANNEL_POSITION_LFE -#define MA_PA_CHANNEL_POSITION_FRONT_LEFT_OF_CENTER PA_CHANNEL_POSITION_FRONT_LEFT_OF_CENTER -#define MA_PA_CHANNEL_POSITION_FRONT_RIGHT_OF_CENTER PA_CHANNEL_POSITION_FRONT_RIGHT_OF_CENTER -#define MA_PA_CHANNEL_POSITION_SIDE_LEFT PA_CHANNEL_POSITION_SIDE_LEFT -#define MA_PA_CHANNEL_POSITION_SIDE_RIGHT PA_CHANNEL_POSITION_SIDE_RIGHT -#define MA_PA_CHANNEL_POSITION_AUX0 PA_CHANNEL_POSITION_AUX0 -#define MA_PA_CHANNEL_POSITION_AUX1 PA_CHANNEL_POSITION_AUX1 -#define MA_PA_CHANNEL_POSITION_AUX2 PA_CHANNEL_POSITION_AUX2 -#define MA_PA_CHANNEL_POSITION_AUX3 PA_CHANNEL_POSITION_AUX3 -#define MA_PA_CHANNEL_POSITION_AUX4 PA_CHANNEL_POSITION_AUX4 -#define MA_PA_CHANNEL_POSITION_AUX5 PA_CHANNEL_POSITION_AUX5 -#define MA_PA_CHANNEL_POSITION_AUX6 PA_CHANNEL_POSITION_AUX6 -#define MA_PA_CHANNEL_POSITION_AUX7 PA_CHANNEL_POSITION_AUX7 -#define MA_PA_CHANNEL_POSITION_AUX8 PA_CHANNEL_POSITION_AUX8 -#define MA_PA_CHANNEL_POSITION_AUX9 PA_CHANNEL_POSITION_AUX9 -#define MA_PA_CHANNEL_POSITION_AUX10 PA_CHANNEL_POSITION_AUX10 -#define MA_PA_CHANNEL_POSITION_AUX11 PA_CHANNEL_POSITION_AUX11 -#define MA_PA_CHANNEL_POSITION_AUX12 PA_CHANNEL_POSITION_AUX12 -#define MA_PA_CHANNEL_POSITION_AUX13 PA_CHANNEL_POSITION_AUX13 -#define MA_PA_CHANNEL_POSITION_AUX14 PA_CHANNEL_POSITION_AUX14 -#define MA_PA_CHANNEL_POSITION_AUX15 PA_CHANNEL_POSITION_AUX15 -#define MA_PA_CHANNEL_POSITION_AUX16 PA_CHANNEL_POSITION_AUX16 -#define MA_PA_CHANNEL_POSITION_AUX17 PA_CHANNEL_POSITION_AUX17 -#define MA_PA_CHANNEL_POSITION_AUX18 PA_CHANNEL_POSITION_AUX18 -#define MA_PA_CHANNEL_POSITION_AUX19 PA_CHANNEL_POSITION_AUX19 -#define MA_PA_CHANNEL_POSITION_AUX20 PA_CHANNEL_POSITION_AUX20 -#define MA_PA_CHANNEL_POSITION_AUX21 PA_CHANNEL_POSITION_AUX21 -#define MA_PA_CHANNEL_POSITION_AUX22 PA_CHANNEL_POSITION_AUX22 -#define MA_PA_CHANNEL_POSITION_AUX23 PA_CHANNEL_POSITION_AUX23 -#define MA_PA_CHANNEL_POSITION_AUX24 PA_CHANNEL_POSITION_AUX24 -#define MA_PA_CHANNEL_POSITION_AUX25 PA_CHANNEL_POSITION_AUX25 -#define MA_PA_CHANNEL_POSITION_AUX26 PA_CHANNEL_POSITION_AUX26 -#define MA_PA_CHANNEL_POSITION_AUX27 PA_CHANNEL_POSITION_AUX27 -#define MA_PA_CHANNEL_POSITION_AUX28 PA_CHANNEL_POSITION_AUX28 -#define MA_PA_CHANNEL_POSITION_AUX29 PA_CHANNEL_POSITION_AUX29 -#define MA_PA_CHANNEL_POSITION_AUX30 PA_CHANNEL_POSITION_AUX30 -#define MA_PA_CHANNEL_POSITION_AUX31 PA_CHANNEL_POSITION_AUX31 -#define MA_PA_CHANNEL_POSITION_TOP_CENTER PA_CHANNEL_POSITION_TOP_CENTER -#define MA_PA_CHANNEL_POSITION_TOP_FRONT_LEFT PA_CHANNEL_POSITION_TOP_FRONT_LEFT -#define MA_PA_CHANNEL_POSITION_TOP_FRONT_RIGHT PA_CHANNEL_POSITION_TOP_FRONT_RIGHT -#define MA_PA_CHANNEL_POSITION_TOP_FRONT_CENTER PA_CHANNEL_POSITION_TOP_FRONT_CENTER -#define MA_PA_CHANNEL_POSITION_TOP_REAR_LEFT PA_CHANNEL_POSITION_TOP_REAR_LEFT -#define MA_PA_CHANNEL_POSITION_TOP_REAR_RIGHT PA_CHANNEL_POSITION_TOP_REAR_RIGHT -#define MA_PA_CHANNEL_POSITION_TOP_REAR_CENTER PA_CHANNEL_POSITION_TOP_REAR_CENTER -#define MA_PA_CHANNEL_POSITION_LEFT PA_CHANNEL_POSITION_LEFT -#define MA_PA_CHANNEL_POSITION_RIGHT PA_CHANNEL_POSITION_RIGHT -#define MA_PA_CHANNEL_POSITION_CENTER PA_CHANNEL_POSITION_CENTER -#define MA_PA_CHANNEL_POSITION_SUBWOOFER PA_CHANNEL_POSITION_SUBWOOFER - -typedef pa_channel_map_def_t ma_pa_channel_map_def_t; -#define MA_PA_CHANNEL_MAP_AIFF PA_CHANNEL_MAP_AIFF -#define MA_PA_CHANNEL_MAP_ALSA PA_CHANNEL_MAP_ALSA -#define MA_PA_CHANNEL_MAP_AUX PA_CHANNEL_MAP_AUX -#define MA_PA_CHANNEL_MAP_WAVEEX PA_CHANNEL_MAP_WAVEEX -#define MA_PA_CHANNEL_MAP_OSS PA_CHANNEL_MAP_OSS -#define MA_PA_CHANNEL_MAP_DEFAULT PA_CHANNEL_MAP_DEFAULT - -typedef pa_sample_format_t ma_pa_sample_format_t; -#define MA_PA_SAMPLE_INVALID PA_SAMPLE_INVALID -#define MA_PA_SAMPLE_U8 PA_SAMPLE_U8 -#define MA_PA_SAMPLE_ALAW PA_SAMPLE_ALAW -#define MA_PA_SAMPLE_ULAW PA_SAMPLE_ULAW -#define MA_PA_SAMPLE_S16LE PA_SAMPLE_S16LE -#define MA_PA_SAMPLE_S16BE PA_SAMPLE_S16BE -#define MA_PA_SAMPLE_FLOAT32LE PA_SAMPLE_FLOAT32LE -#define MA_PA_SAMPLE_FLOAT32BE PA_SAMPLE_FLOAT32BE -#define MA_PA_SAMPLE_S32LE PA_SAMPLE_S32LE -#define MA_PA_SAMPLE_S32BE PA_SAMPLE_S32BE -#define MA_PA_SAMPLE_S24LE PA_SAMPLE_S24LE -#define MA_PA_SAMPLE_S24BE PA_SAMPLE_S24BE -#define MA_PA_SAMPLE_S24_32LE PA_SAMPLE_S24_32LE -#define MA_PA_SAMPLE_S24_32BE PA_SAMPLE_S24_32BE - -typedef pa_mainloop ma_pa_mainloop; -typedef pa_threaded_mainloop ma_pa_threaded_mainloop; -typedef pa_mainloop_api ma_pa_mainloop_api; -typedef pa_context ma_pa_context; -typedef pa_operation ma_pa_operation; -typedef pa_stream ma_pa_stream; -typedef pa_spawn_api ma_pa_spawn_api; -typedef pa_buffer_attr ma_pa_buffer_attr; -typedef pa_channel_map ma_pa_channel_map; -typedef pa_cvolume ma_pa_cvolume; -typedef pa_sample_spec ma_pa_sample_spec; -typedef pa_sink_info ma_pa_sink_info; -typedef pa_source_info ma_pa_source_info; - -typedef pa_context_notify_cb_t ma_pa_context_notify_cb_t; -typedef pa_sink_info_cb_t ma_pa_sink_info_cb_t; -typedef pa_source_info_cb_t ma_pa_source_info_cb_t; -typedef pa_stream_success_cb_t ma_pa_stream_success_cb_t; -typedef pa_stream_request_cb_t ma_pa_stream_request_cb_t; -typedef pa_stream_notify_cb_t ma_pa_stream_notify_cb_t; -typedef pa_free_cb_t ma_pa_free_cb_t; -#else -#define MA_PA_OK 0 -#define MA_PA_ERR_ACCESS 1 -#define MA_PA_ERR_INVALID 2 -#define MA_PA_ERR_NOENTITY 5 -#define MA_PA_ERR_NOTSUPPORTED 19 - -#define MA_PA_CHANNELS_MAX 32 -#define MA_PA_RATE_MAX 384000 - -typedef int ma_pa_context_flags_t; -#define MA_PA_CONTEXT_NOFLAGS 0x00000000 -#define MA_PA_CONTEXT_NOAUTOSPAWN 0x00000001 -#define MA_PA_CONTEXT_NOFAIL 0x00000002 - -typedef int ma_pa_stream_flags_t; -#define MA_PA_STREAM_NOFLAGS 0x00000000 -#define MA_PA_STREAM_START_CORKED 0x00000001 -#define MA_PA_STREAM_INTERPOLATE_TIMING 0x00000002 -#define MA_PA_STREAM_NOT_MONOTONIC 0x00000004 -#define MA_PA_STREAM_AUTO_TIMING_UPDATE 0x00000008 -#define MA_PA_STREAM_NO_REMAP_CHANNELS 0x00000010 -#define MA_PA_STREAM_NO_REMIX_CHANNELS 0x00000020 -#define MA_PA_STREAM_FIX_FORMAT 0x00000040 -#define MA_PA_STREAM_FIX_RATE 0x00000080 -#define MA_PA_STREAM_FIX_CHANNELS 0x00000100 -#define MA_PA_STREAM_DONT_MOVE 0x00000200 -#define MA_PA_STREAM_VARIABLE_RATE 0x00000400 -#define MA_PA_STREAM_PEAK_DETECT 0x00000800 -#define MA_PA_STREAM_START_MUTED 0x00001000 -#define MA_PA_STREAM_ADJUST_LATENCY 0x00002000 -#define MA_PA_STREAM_EARLY_REQUESTS 0x00004000 -#define MA_PA_STREAM_DONT_INHIBIT_AUTO_SUSPEND 0x00008000 -#define MA_PA_STREAM_START_UNMUTED 0x00010000 -#define MA_PA_STREAM_FAIL_ON_SUSPEND 0x00020000 -#define MA_PA_STREAM_RELATIVE_VOLUME 0x00040000 -#define MA_PA_STREAM_PASSTHROUGH 0x00080000 - -typedef int ma_pa_sink_flags_t; -#define MA_PA_SINK_NOFLAGS 0x00000000 -#define MA_PA_SINK_HW_VOLUME_CTRL 0x00000001 -#define MA_PA_SINK_LATENCY 0x00000002 -#define MA_PA_SINK_HARDWARE 0x00000004 -#define MA_PA_SINK_NETWORK 0x00000008 -#define MA_PA_SINK_HW_MUTE_CTRL 0x00000010 -#define MA_PA_SINK_DECIBEL_VOLUME 0x00000020 -#define MA_PA_SINK_FLAT_VOLUME 0x00000040 -#define MA_PA_SINK_DYNAMIC_LATENCY 0x00000080 -#define MA_PA_SINK_SET_FORMATS 0x00000100 - -typedef int ma_pa_source_flags_t; -#define MA_PA_SOURCE_NOFLAGS 0x00000000 -#define MA_PA_SOURCE_HW_VOLUME_CTRL 0x00000001 -#define MA_PA_SOURCE_LATENCY 0x00000002 -#define MA_PA_SOURCE_HARDWARE 0x00000004 -#define MA_PA_SOURCE_NETWORK 0x00000008 -#define MA_PA_SOURCE_HW_MUTE_CTRL 0x00000010 -#define MA_PA_SOURCE_DECIBEL_VOLUME 0x00000020 -#define MA_PA_SOURCE_DYNAMIC_LATENCY 0x00000040 -#define MA_PA_SOURCE_FLAT_VOLUME 0x00000080 - -typedef int ma_pa_context_state_t; -#define MA_PA_CONTEXT_UNCONNECTED 0 -#define MA_PA_CONTEXT_CONNECTING 1 -#define MA_PA_CONTEXT_AUTHORIZING 2 -#define MA_PA_CONTEXT_SETTING_NAME 3 -#define MA_PA_CONTEXT_READY 4 -#define MA_PA_CONTEXT_FAILED 5 -#define MA_PA_CONTEXT_TERMINATED 6 - -typedef int ma_pa_stream_state_t; -#define MA_PA_STREAM_UNCONNECTED 0 -#define MA_PA_STREAM_CREATING 1 -#define MA_PA_STREAM_READY 2 -#define MA_PA_STREAM_FAILED 3 -#define MA_PA_STREAM_TERMINATED 4 - -typedef int ma_pa_operation_state_t; -#define MA_PA_OPERATION_RUNNING 0 -#define MA_PA_OPERATION_DONE 1 -#define MA_PA_OPERATION_CANCELLED 2 - -typedef int ma_pa_sink_state_t; -#define MA_PA_SINK_INVALID_STATE -1 -#define MA_PA_SINK_RUNNING 0 -#define MA_PA_SINK_IDLE 1 -#define MA_PA_SINK_SUSPENDED 2 - -typedef int ma_pa_source_state_t; -#define MA_PA_SOURCE_INVALID_STATE -1 -#define MA_PA_SOURCE_RUNNING 0 -#define MA_PA_SOURCE_IDLE 1 -#define MA_PA_SOURCE_SUSPENDED 2 - -typedef int ma_pa_seek_mode_t; -#define MA_PA_SEEK_RELATIVE 0 -#define MA_PA_SEEK_ABSOLUTE 1 -#define MA_PA_SEEK_RELATIVE_ON_READ 2 -#define MA_PA_SEEK_RELATIVE_END 3 - -typedef int ma_pa_channel_position_t; -#define MA_PA_CHANNEL_POSITION_INVALID -1 -#define MA_PA_CHANNEL_POSITION_MONO 0 -#define MA_PA_CHANNEL_POSITION_FRONT_LEFT 1 -#define MA_PA_CHANNEL_POSITION_FRONT_RIGHT 2 -#define MA_PA_CHANNEL_POSITION_FRONT_CENTER 3 -#define MA_PA_CHANNEL_POSITION_REAR_CENTER 4 -#define MA_PA_CHANNEL_POSITION_REAR_LEFT 5 -#define MA_PA_CHANNEL_POSITION_REAR_RIGHT 6 -#define MA_PA_CHANNEL_POSITION_LFE 7 -#define MA_PA_CHANNEL_POSITION_FRONT_LEFT_OF_CENTER 8 -#define MA_PA_CHANNEL_POSITION_FRONT_RIGHT_OF_CENTER 9 -#define MA_PA_CHANNEL_POSITION_SIDE_LEFT 10 -#define MA_PA_CHANNEL_POSITION_SIDE_RIGHT 11 -#define MA_PA_CHANNEL_POSITION_AUX0 12 -#define MA_PA_CHANNEL_POSITION_AUX1 13 -#define MA_PA_CHANNEL_POSITION_AUX2 14 -#define MA_PA_CHANNEL_POSITION_AUX3 15 -#define MA_PA_CHANNEL_POSITION_AUX4 16 -#define MA_PA_CHANNEL_POSITION_AUX5 17 -#define MA_PA_CHANNEL_POSITION_AUX6 18 -#define MA_PA_CHANNEL_POSITION_AUX7 19 -#define MA_PA_CHANNEL_POSITION_AUX8 20 -#define MA_PA_CHANNEL_POSITION_AUX9 21 -#define MA_PA_CHANNEL_POSITION_AUX10 22 -#define MA_PA_CHANNEL_POSITION_AUX11 23 -#define MA_PA_CHANNEL_POSITION_AUX12 24 -#define MA_PA_CHANNEL_POSITION_AUX13 25 -#define MA_PA_CHANNEL_POSITION_AUX14 26 -#define MA_PA_CHANNEL_POSITION_AUX15 27 -#define MA_PA_CHANNEL_POSITION_AUX16 28 -#define MA_PA_CHANNEL_POSITION_AUX17 29 -#define MA_PA_CHANNEL_POSITION_AUX18 30 -#define MA_PA_CHANNEL_POSITION_AUX19 31 -#define MA_PA_CHANNEL_POSITION_AUX20 32 -#define MA_PA_CHANNEL_POSITION_AUX21 33 -#define MA_PA_CHANNEL_POSITION_AUX22 34 -#define MA_PA_CHANNEL_POSITION_AUX23 35 -#define MA_PA_CHANNEL_POSITION_AUX24 36 -#define MA_PA_CHANNEL_POSITION_AUX25 37 -#define MA_PA_CHANNEL_POSITION_AUX26 38 -#define MA_PA_CHANNEL_POSITION_AUX27 39 -#define MA_PA_CHANNEL_POSITION_AUX28 40 -#define MA_PA_CHANNEL_POSITION_AUX29 41 -#define MA_PA_CHANNEL_POSITION_AUX30 42 -#define MA_PA_CHANNEL_POSITION_AUX31 43 -#define MA_PA_CHANNEL_POSITION_TOP_CENTER 44 -#define MA_PA_CHANNEL_POSITION_TOP_FRONT_LEFT 45 -#define MA_PA_CHANNEL_POSITION_TOP_FRONT_RIGHT 46 -#define MA_PA_CHANNEL_POSITION_TOP_FRONT_CENTER 47 -#define MA_PA_CHANNEL_POSITION_TOP_REAR_LEFT 48 -#define MA_PA_CHANNEL_POSITION_TOP_REAR_RIGHT 49 -#define MA_PA_CHANNEL_POSITION_TOP_REAR_CENTER 50 -#define MA_PA_CHANNEL_POSITION_LEFT MA_PA_CHANNEL_POSITION_FRONT_LEFT -#define MA_PA_CHANNEL_POSITION_RIGHT MA_PA_CHANNEL_POSITION_FRONT_RIGHT -#define MA_PA_CHANNEL_POSITION_CENTER MA_PA_CHANNEL_POSITION_FRONT_CENTER -#define MA_PA_CHANNEL_POSITION_SUBWOOFER MA_PA_CHANNEL_POSITION_LFE - -typedef int ma_pa_channel_map_def_t; -#define MA_PA_CHANNEL_MAP_AIFF 0 -#define MA_PA_CHANNEL_MAP_ALSA 1 -#define MA_PA_CHANNEL_MAP_AUX 2 -#define MA_PA_CHANNEL_MAP_WAVEEX 3 -#define MA_PA_CHANNEL_MAP_OSS 4 -#define MA_PA_CHANNEL_MAP_DEFAULT MA_PA_CHANNEL_MAP_AIFF - -typedef int ma_pa_sample_format_t; -#define MA_PA_SAMPLE_INVALID -1 -#define MA_PA_SAMPLE_U8 0 -#define MA_PA_SAMPLE_ALAW 1 -#define MA_PA_SAMPLE_ULAW 2 -#define MA_PA_SAMPLE_S16LE 3 -#define MA_PA_SAMPLE_S16BE 4 -#define MA_PA_SAMPLE_FLOAT32LE 5 -#define MA_PA_SAMPLE_FLOAT32BE 6 -#define MA_PA_SAMPLE_S32LE 7 -#define MA_PA_SAMPLE_S32BE 8 -#define MA_PA_SAMPLE_S24LE 9 -#define MA_PA_SAMPLE_S24BE 10 -#define MA_PA_SAMPLE_S24_32LE 11 -#define MA_PA_SAMPLE_S24_32BE 12 - -typedef struct ma_pa_mainloop ma_pa_mainloop; -typedef struct ma_pa_threaded_mainloop ma_pa_threaded_mainloop; -typedef struct ma_pa_mainloop_api ma_pa_mainloop_api; -typedef struct ma_pa_context ma_pa_context; -typedef struct ma_pa_operation ma_pa_operation; -typedef struct ma_pa_stream ma_pa_stream; -typedef struct ma_pa_spawn_api ma_pa_spawn_api; - -typedef struct -{ - ma_uint32 maxlength; - ma_uint32 tlength; - ma_uint32 prebuf; - ma_uint32 minreq; - ma_uint32 fragsize; -} ma_pa_buffer_attr; - -typedef struct -{ - ma_uint8 channels; - ma_pa_channel_position_t map[MA_PA_CHANNELS_MAX]; -} ma_pa_channel_map; - -typedef struct -{ - ma_uint8 channels; - ma_uint32 values[MA_PA_CHANNELS_MAX]; -} ma_pa_cvolume; - -typedef struct -{ - ma_pa_sample_format_t format; - ma_uint32 rate; - ma_uint8 channels; -} ma_pa_sample_spec; - -typedef struct -{ - const char* name; - ma_uint32 index; - const char* description; - ma_pa_sample_spec sample_spec; - ma_pa_channel_map channel_map; - ma_uint32 owner_module; - ma_pa_cvolume volume; - int mute; - ma_uint32 monitor_source; - const char* monitor_source_name; - ma_uint64 latency; - const char* driver; - ma_pa_sink_flags_t flags; - void* proplist; - ma_uint64 configured_latency; - ma_uint32 base_volume; - ma_pa_sink_state_t state; - ma_uint32 n_volume_steps; - ma_uint32 card; - ma_uint32 n_ports; - void** ports; - void* active_port; - ma_uint8 n_formats; - void** formats; -} ma_pa_sink_info; - -typedef struct -{ - const char *name; - ma_uint32 index; - const char *description; - ma_pa_sample_spec sample_spec; - ma_pa_channel_map channel_map; - ma_uint32 owner_module; - ma_pa_cvolume volume; - int mute; - ma_uint32 monitor_of_sink; - const char *monitor_of_sink_name; - ma_uint64 latency; - const char *driver; - ma_pa_source_flags_t flags; - void* proplist; - ma_uint64 configured_latency; - ma_uint32 base_volume; - ma_pa_source_state_t state; - ma_uint32 n_volume_steps; - ma_uint32 card; - ma_uint32 n_ports; - void** ports; - void* active_port; - ma_uint8 n_formats; - void** formats; -} ma_pa_source_info; - -typedef void (* ma_pa_context_notify_cb_t)(ma_pa_context* c, void* userdata); -typedef void (* ma_pa_sink_info_cb_t) (ma_pa_context* c, const ma_pa_sink_info* i, int eol, void* userdata); -typedef void (* ma_pa_source_info_cb_t) (ma_pa_context* c, const ma_pa_source_info* i, int eol, void* userdata); -typedef void (* ma_pa_stream_success_cb_t)(ma_pa_stream* s, int success, void* userdata); -typedef void (* ma_pa_stream_request_cb_t)(ma_pa_stream* s, size_t nbytes, void* userdata); -typedef void (* ma_pa_stream_notify_cb_t) (ma_pa_stream* s, void* userdata); -typedef void (* ma_pa_free_cb_t) (void* p); -#endif - - -typedef ma_pa_mainloop* (* ma_pa_mainloop_new_proc) (void); -typedef void (* ma_pa_mainloop_free_proc) (ma_pa_mainloop* m); -typedef void (* ma_pa_mainloop_quit_proc) (ma_pa_mainloop* m, int retval); -typedef ma_pa_mainloop_api* (* ma_pa_mainloop_get_api_proc) (ma_pa_mainloop* m); -typedef int (* ma_pa_mainloop_iterate_proc) (ma_pa_mainloop* m, int block, int* retval); -typedef void (* ma_pa_mainloop_wakeup_proc) (ma_pa_mainloop* m); -typedef ma_pa_threaded_mainloop* (* ma_pa_threaded_mainloop_new_proc) (void); -typedef void (* ma_pa_threaded_mainloop_free_proc) (ma_pa_threaded_mainloop* m); -typedef int (* ma_pa_threaded_mainloop_start_proc) (ma_pa_threaded_mainloop* m); -typedef void (* ma_pa_threaded_mainloop_stop_proc) (ma_pa_threaded_mainloop* m); -typedef void (* ma_pa_threaded_mainloop_lock_proc) (ma_pa_threaded_mainloop* m); -typedef void (* ma_pa_threaded_mainloop_unlock_proc) (ma_pa_threaded_mainloop* m); -typedef void (* ma_pa_threaded_mainloop_wait_proc) (ma_pa_threaded_mainloop* m); -typedef void (* ma_pa_threaded_mainloop_signal_proc) (ma_pa_threaded_mainloop* m, int wait_for_accept); -typedef void (* ma_pa_threaded_mainloop_accept_proc) (ma_pa_threaded_mainloop* m); -typedef int (* ma_pa_threaded_mainloop_get_retval_proc) (ma_pa_threaded_mainloop* m); -typedef ma_pa_mainloop_api* (* ma_pa_threaded_mainloop_get_api_proc) (ma_pa_threaded_mainloop* m); -typedef int (* ma_pa_threaded_mainloop_in_thread_proc) (ma_pa_threaded_mainloop* m); -typedef void (* ma_pa_threaded_mainloop_set_name_proc) (ma_pa_threaded_mainloop* m, const char* name); -typedef ma_pa_context* (* ma_pa_context_new_proc) (ma_pa_mainloop_api* mainloop, const char* name); -typedef void (* ma_pa_context_unref_proc) (ma_pa_context* c); -typedef int (* ma_pa_context_connect_proc) (ma_pa_context* c, const char* server, ma_pa_context_flags_t flags, const ma_pa_spawn_api* api); -typedef void (* ma_pa_context_disconnect_proc) (ma_pa_context* c); -typedef void (* ma_pa_context_set_state_callback_proc) (ma_pa_context* c, ma_pa_context_notify_cb_t cb, void* userdata); -typedef ma_pa_context_state_t (* ma_pa_context_get_state_proc) (ma_pa_context* c); -typedef ma_pa_operation* (* ma_pa_context_get_sink_info_list_proc) (ma_pa_context* c, ma_pa_sink_info_cb_t cb, void* userdata); -typedef ma_pa_operation* (* ma_pa_context_get_source_info_list_proc) (ma_pa_context* c, ma_pa_source_info_cb_t cb, void* userdata); -typedef ma_pa_operation* (* ma_pa_context_get_sink_info_by_name_proc) (ma_pa_context* c, const char* name, ma_pa_sink_info_cb_t cb, void* userdata); -typedef ma_pa_operation* (* ma_pa_context_get_source_info_by_name_proc)(ma_pa_context* c, const char* name, ma_pa_source_info_cb_t cb, void* userdata); -typedef void (* ma_pa_operation_unref_proc) (ma_pa_operation* o); -typedef ma_pa_operation_state_t (* ma_pa_operation_get_state_proc) (ma_pa_operation* o); -typedef ma_pa_channel_map* (* ma_pa_channel_map_init_extend_proc) (ma_pa_channel_map* m, unsigned channels, ma_pa_channel_map_def_t def); -typedef int (* ma_pa_channel_map_valid_proc) (const ma_pa_channel_map* m); -typedef int (* ma_pa_channel_map_compatible_proc) (const ma_pa_channel_map* m, const ma_pa_sample_spec* ss); -typedef ma_pa_stream* (* ma_pa_stream_new_proc) (ma_pa_context* c, const char* name, const ma_pa_sample_spec* ss, const ma_pa_channel_map* map); -typedef void (* ma_pa_stream_unref_proc) (ma_pa_stream* s); -typedef int (* ma_pa_stream_connect_playback_proc) (ma_pa_stream* s, const char* dev, const ma_pa_buffer_attr* attr, ma_pa_stream_flags_t flags, const ma_pa_cvolume* volume, ma_pa_stream* sync_stream); -typedef int (* ma_pa_stream_connect_record_proc) (ma_pa_stream* s, const char* dev, const ma_pa_buffer_attr* attr, ma_pa_stream_flags_t flags); -typedef int (* ma_pa_stream_disconnect_proc) (ma_pa_stream* s); -typedef ma_pa_stream_state_t (* ma_pa_stream_get_state_proc) (ma_pa_stream* s); -typedef const ma_pa_sample_spec* (* ma_pa_stream_get_sample_spec_proc) (ma_pa_stream* s); -typedef const ma_pa_channel_map* (* ma_pa_stream_get_channel_map_proc) (ma_pa_stream* s); -typedef const ma_pa_buffer_attr* (* ma_pa_stream_get_buffer_attr_proc) (ma_pa_stream* s); -typedef ma_pa_operation* (* ma_pa_stream_set_buffer_attr_proc) (ma_pa_stream* s, const ma_pa_buffer_attr* attr, ma_pa_stream_success_cb_t cb, void* userdata); -typedef const char* (* ma_pa_stream_get_device_name_proc) (ma_pa_stream* s); -typedef void (* ma_pa_stream_set_write_callback_proc) (ma_pa_stream* s, ma_pa_stream_request_cb_t cb, void* userdata); -typedef void (* ma_pa_stream_set_read_callback_proc) (ma_pa_stream* s, ma_pa_stream_request_cb_t cb, void* userdata); -typedef void (* ma_pa_stream_set_suspended_callback_proc) (ma_pa_stream* s, ma_pa_stream_notify_cb_t cb, void* userdata); -typedef void (* ma_pa_stream_set_moved_callback_proc) (ma_pa_stream* s, ma_pa_stream_notify_cb_t cb, void* userdata); -typedef int (* ma_pa_stream_is_suspended_proc) (const ma_pa_stream* s); -typedef ma_pa_operation* (* ma_pa_stream_flush_proc) (ma_pa_stream* s, ma_pa_stream_success_cb_t cb, void* userdata); -typedef ma_pa_operation* (* ma_pa_stream_drain_proc) (ma_pa_stream* s, ma_pa_stream_success_cb_t cb, void* userdata); -typedef int (* ma_pa_stream_is_corked_proc) (ma_pa_stream* s); -typedef ma_pa_operation* (* ma_pa_stream_cork_proc) (ma_pa_stream* s, int b, ma_pa_stream_success_cb_t cb, void* userdata); -typedef ma_pa_operation* (* ma_pa_stream_trigger_proc) (ma_pa_stream* s, ma_pa_stream_success_cb_t cb, void* userdata); -typedef int (* ma_pa_stream_begin_write_proc) (ma_pa_stream* s, void** data, size_t* nbytes); -typedef int (* ma_pa_stream_write_proc) (ma_pa_stream* s, const void* data, size_t nbytes, ma_pa_free_cb_t free_cb, int64_t offset, ma_pa_seek_mode_t seek); -typedef int (* ma_pa_stream_peek_proc) (ma_pa_stream* s, const void** data, size_t* nbytes); -typedef int (* ma_pa_stream_drop_proc) (ma_pa_stream* s); -typedef size_t (* ma_pa_stream_writable_size_proc) (ma_pa_stream* s); -typedef size_t (* ma_pa_stream_readable_size_proc) (ma_pa_stream* s); - -typedef struct -{ - ma_uint32 count; - ma_uint32 capacity; - ma_device_info* pInfo; -} ma_pulse_device_enum_data; - -static ma_result ma_result_from_pulse(int result) -{ - if (result < 0) { - return MA_ERROR; - } - - switch (result) { - case MA_PA_OK: return MA_SUCCESS; - case MA_PA_ERR_ACCESS: return MA_ACCESS_DENIED; - case MA_PA_ERR_INVALID: return MA_INVALID_ARGS; - case MA_PA_ERR_NOENTITY: return MA_NO_DEVICE; - default: return MA_ERROR; - } -} - -#if 0 -static ma_pa_sample_format_t ma_format_to_pulse(ma_format format) -{ - if (ma_is_little_endian()) { - switch (format) { - case ma_format_s16: return MA_PA_SAMPLE_S16LE; - case ma_format_s24: return MA_PA_SAMPLE_S24LE; - case ma_format_s32: return MA_PA_SAMPLE_S32LE; - case ma_format_f32: return MA_PA_SAMPLE_FLOAT32LE; - default: break; - } - } else { - switch (format) { - case ma_format_s16: return MA_PA_SAMPLE_S16BE; - case ma_format_s24: return MA_PA_SAMPLE_S24BE; - case ma_format_s32: return MA_PA_SAMPLE_S32BE; - case ma_format_f32: return MA_PA_SAMPLE_FLOAT32BE; - default: break; - } - } - - /* Endian agnostic. */ - switch (format) { - case ma_format_u8: return MA_PA_SAMPLE_U8; - default: return MA_PA_SAMPLE_INVALID; - } -} -#endif - -static ma_format ma_format_from_pulse(ma_pa_sample_format_t format) -{ - if (ma_is_little_endian()) { - switch (format) { - case MA_PA_SAMPLE_S16LE: return ma_format_s16; - case MA_PA_SAMPLE_S24LE: return ma_format_s24; - case MA_PA_SAMPLE_S32LE: return ma_format_s32; - case MA_PA_SAMPLE_FLOAT32LE: return ma_format_f32; - default: break; - } - } else { - switch (format) { - case MA_PA_SAMPLE_S16BE: return ma_format_s16; - case MA_PA_SAMPLE_S24BE: return ma_format_s24; - case MA_PA_SAMPLE_S32BE: return ma_format_s32; - case MA_PA_SAMPLE_FLOAT32BE: return ma_format_f32; - default: break; - } - } - - /* Endian agnostic. */ - switch (format) { - case MA_PA_SAMPLE_U8: return ma_format_u8; - default: return ma_format_unknown; - } -} - -static ma_channel ma_channel_position_from_pulse(ma_pa_channel_position_t position) -{ - switch (position) - { - case MA_PA_CHANNEL_POSITION_INVALID: return MA_CHANNEL_NONE; - case MA_PA_CHANNEL_POSITION_MONO: return MA_CHANNEL_MONO; - case MA_PA_CHANNEL_POSITION_FRONT_LEFT: return MA_CHANNEL_FRONT_LEFT; - case MA_PA_CHANNEL_POSITION_FRONT_RIGHT: return MA_CHANNEL_FRONT_RIGHT; - case MA_PA_CHANNEL_POSITION_FRONT_CENTER: return MA_CHANNEL_FRONT_CENTER; - case MA_PA_CHANNEL_POSITION_REAR_CENTER: return MA_CHANNEL_BACK_CENTER; - case MA_PA_CHANNEL_POSITION_REAR_LEFT: return MA_CHANNEL_BACK_LEFT; - case MA_PA_CHANNEL_POSITION_REAR_RIGHT: return MA_CHANNEL_BACK_RIGHT; - case MA_PA_CHANNEL_POSITION_LFE: return MA_CHANNEL_LFE; - case MA_PA_CHANNEL_POSITION_FRONT_LEFT_OF_CENTER: return MA_CHANNEL_FRONT_LEFT_CENTER; - case MA_PA_CHANNEL_POSITION_FRONT_RIGHT_OF_CENTER: return MA_CHANNEL_FRONT_RIGHT_CENTER; - case MA_PA_CHANNEL_POSITION_SIDE_LEFT: return MA_CHANNEL_SIDE_LEFT; - case MA_PA_CHANNEL_POSITION_SIDE_RIGHT: return MA_CHANNEL_SIDE_RIGHT; - case MA_PA_CHANNEL_POSITION_AUX0: return MA_CHANNEL_AUX_0; - case MA_PA_CHANNEL_POSITION_AUX1: return MA_CHANNEL_AUX_1; - case MA_PA_CHANNEL_POSITION_AUX2: return MA_CHANNEL_AUX_2; - case MA_PA_CHANNEL_POSITION_AUX3: return MA_CHANNEL_AUX_3; - case MA_PA_CHANNEL_POSITION_AUX4: return MA_CHANNEL_AUX_4; - case MA_PA_CHANNEL_POSITION_AUX5: return MA_CHANNEL_AUX_5; - case MA_PA_CHANNEL_POSITION_AUX6: return MA_CHANNEL_AUX_6; - case MA_PA_CHANNEL_POSITION_AUX7: return MA_CHANNEL_AUX_7; - case MA_PA_CHANNEL_POSITION_AUX8: return MA_CHANNEL_AUX_8; - case MA_PA_CHANNEL_POSITION_AUX9: return MA_CHANNEL_AUX_9; - case MA_PA_CHANNEL_POSITION_AUX10: return MA_CHANNEL_AUX_10; - case MA_PA_CHANNEL_POSITION_AUX11: return MA_CHANNEL_AUX_11; - case MA_PA_CHANNEL_POSITION_AUX12: return MA_CHANNEL_AUX_12; - case MA_PA_CHANNEL_POSITION_AUX13: return MA_CHANNEL_AUX_13; - case MA_PA_CHANNEL_POSITION_AUX14: return MA_CHANNEL_AUX_14; - case MA_PA_CHANNEL_POSITION_AUX15: return MA_CHANNEL_AUX_15; - case MA_PA_CHANNEL_POSITION_AUX16: return MA_CHANNEL_AUX_16; - case MA_PA_CHANNEL_POSITION_AUX17: return MA_CHANNEL_AUX_17; - case MA_PA_CHANNEL_POSITION_AUX18: return MA_CHANNEL_AUX_18; - case MA_PA_CHANNEL_POSITION_AUX19: return MA_CHANNEL_AUX_19; - case MA_PA_CHANNEL_POSITION_AUX20: return MA_CHANNEL_AUX_20; - case MA_PA_CHANNEL_POSITION_AUX21: return MA_CHANNEL_AUX_21; - case MA_PA_CHANNEL_POSITION_AUX22: return MA_CHANNEL_AUX_22; - case MA_PA_CHANNEL_POSITION_AUX23: return MA_CHANNEL_AUX_23; - case MA_PA_CHANNEL_POSITION_AUX24: return MA_CHANNEL_AUX_24; - case MA_PA_CHANNEL_POSITION_AUX25: return MA_CHANNEL_AUX_25; - case MA_PA_CHANNEL_POSITION_AUX26: return MA_CHANNEL_AUX_26; - case MA_PA_CHANNEL_POSITION_AUX27: return MA_CHANNEL_AUX_27; - case MA_PA_CHANNEL_POSITION_AUX28: return MA_CHANNEL_AUX_28; - case MA_PA_CHANNEL_POSITION_AUX29: return MA_CHANNEL_AUX_29; - case MA_PA_CHANNEL_POSITION_AUX30: return MA_CHANNEL_AUX_30; - case MA_PA_CHANNEL_POSITION_AUX31: return MA_CHANNEL_AUX_31; - case MA_PA_CHANNEL_POSITION_TOP_CENTER: return MA_CHANNEL_TOP_CENTER; - case MA_PA_CHANNEL_POSITION_TOP_FRONT_LEFT: return MA_CHANNEL_TOP_FRONT_LEFT; - case MA_PA_CHANNEL_POSITION_TOP_FRONT_RIGHT: return MA_CHANNEL_TOP_FRONT_RIGHT; - case MA_PA_CHANNEL_POSITION_TOP_FRONT_CENTER: return MA_CHANNEL_TOP_FRONT_CENTER; - case MA_PA_CHANNEL_POSITION_TOP_REAR_LEFT: return MA_CHANNEL_TOP_BACK_LEFT; - case MA_PA_CHANNEL_POSITION_TOP_REAR_RIGHT: return MA_CHANNEL_TOP_BACK_RIGHT; - case MA_PA_CHANNEL_POSITION_TOP_REAR_CENTER: return MA_CHANNEL_TOP_BACK_CENTER; - default: return MA_CHANNEL_NONE; - } -} - -#if 0 -static ma_pa_channel_position_t ma_channel_position_to_pulse(ma_channel position) -{ - switch (position) - { - case MA_CHANNEL_NONE: return MA_PA_CHANNEL_POSITION_INVALID; - case MA_CHANNEL_FRONT_LEFT: return MA_PA_CHANNEL_POSITION_FRONT_LEFT; - case MA_CHANNEL_FRONT_RIGHT: return MA_PA_CHANNEL_POSITION_FRONT_RIGHT; - case MA_CHANNEL_FRONT_CENTER: return MA_PA_CHANNEL_POSITION_FRONT_CENTER; - case MA_CHANNEL_LFE: return MA_PA_CHANNEL_POSITION_LFE; - case MA_CHANNEL_BACK_LEFT: return MA_PA_CHANNEL_POSITION_REAR_LEFT; - case MA_CHANNEL_BACK_RIGHT: return MA_PA_CHANNEL_POSITION_REAR_RIGHT; - case MA_CHANNEL_FRONT_LEFT_CENTER: return MA_PA_CHANNEL_POSITION_FRONT_LEFT_OF_CENTER; - case MA_CHANNEL_FRONT_RIGHT_CENTER: return MA_PA_CHANNEL_POSITION_FRONT_RIGHT_OF_CENTER; - case MA_CHANNEL_BACK_CENTER: return MA_PA_CHANNEL_POSITION_REAR_CENTER; - case MA_CHANNEL_SIDE_LEFT: return MA_PA_CHANNEL_POSITION_SIDE_LEFT; - case MA_CHANNEL_SIDE_RIGHT: return MA_PA_CHANNEL_POSITION_SIDE_RIGHT; - case MA_CHANNEL_TOP_CENTER: return MA_PA_CHANNEL_POSITION_TOP_CENTER; - case MA_CHANNEL_TOP_FRONT_LEFT: return MA_PA_CHANNEL_POSITION_TOP_FRONT_LEFT; - case MA_CHANNEL_TOP_FRONT_CENTER: return MA_PA_CHANNEL_POSITION_TOP_FRONT_CENTER; - case MA_CHANNEL_TOP_FRONT_RIGHT: return MA_PA_CHANNEL_POSITION_TOP_FRONT_RIGHT; - case MA_CHANNEL_TOP_BACK_LEFT: return MA_PA_CHANNEL_POSITION_TOP_REAR_LEFT; - case MA_CHANNEL_TOP_BACK_CENTER: return MA_PA_CHANNEL_POSITION_TOP_REAR_CENTER; - case MA_CHANNEL_TOP_BACK_RIGHT: return MA_PA_CHANNEL_POSITION_TOP_REAR_RIGHT; - case MA_CHANNEL_19: return MA_PA_CHANNEL_POSITION_AUX18; - case MA_CHANNEL_20: return MA_PA_CHANNEL_POSITION_AUX19; - case MA_CHANNEL_21: return MA_PA_CHANNEL_POSITION_AUX20; - case MA_CHANNEL_22: return MA_PA_CHANNEL_POSITION_AUX21; - case MA_CHANNEL_23: return MA_PA_CHANNEL_POSITION_AUX22; - case MA_CHANNEL_24: return MA_PA_CHANNEL_POSITION_AUX23; - case MA_CHANNEL_25: return MA_PA_CHANNEL_POSITION_AUX24; - case MA_CHANNEL_26: return MA_PA_CHANNEL_POSITION_AUX25; - case MA_CHANNEL_27: return MA_PA_CHANNEL_POSITION_AUX26; - case MA_CHANNEL_28: return MA_PA_CHANNEL_POSITION_AUX27; - case MA_CHANNEL_29: return MA_PA_CHANNEL_POSITION_AUX28; - case MA_CHANNEL_30: return MA_PA_CHANNEL_POSITION_AUX29; - case MA_CHANNEL_31: return MA_PA_CHANNEL_POSITION_AUX30; - case MA_CHANNEL_32: return MA_PA_CHANNEL_POSITION_AUX31; - default: return (ma_pa_channel_position_t)position; - } -} -#endif - -static ma_result ma_wait_for_operation__pulse(ma_context* pContext, ma_ptr pMainLoop, ma_pa_operation* pOP) -{ - int resultPA; - ma_pa_operation_state_t state; - - MA_ASSERT(pContext != NULL); - MA_ASSERT(pOP != NULL); - - for (;;) { - state = ((ma_pa_operation_get_state_proc)pContext->pulse.pa_operation_get_state)(pOP); - if (state != MA_PA_OPERATION_RUNNING) { - break; /* Done. */ - } - - resultPA = ((ma_pa_mainloop_iterate_proc)pContext->pulse.pa_mainloop_iterate)((ma_pa_mainloop*)pMainLoop, 1, NULL); - if (resultPA < 0) { - return ma_result_from_pulse(resultPA); - } - } - - return MA_SUCCESS; -} - -static ma_result ma_wait_for_operation_and_unref__pulse(ma_context* pContext, ma_ptr pMainLoop, ma_pa_operation* pOP) -{ - ma_result result; - - if (pOP == NULL) { - return MA_INVALID_ARGS; - } - - result = ma_wait_for_operation__pulse(pContext, pMainLoop, pOP); - ((ma_pa_operation_unref_proc)pContext->pulse.pa_operation_unref)(pOP); - - return result; -} - -static ma_result ma_wait_for_pa_context_to_connect__pulse(ma_context* pContext, ma_ptr pMainLoop, ma_ptr pPulseContext) -{ - int resultPA; - ma_pa_context_state_t state; - - for (;;) { - state = ((ma_pa_context_get_state_proc)pContext->pulse.pa_context_get_state)((ma_pa_context*)pPulseContext); - if (state == MA_PA_CONTEXT_READY) { - break; /* Done. */ - } - - if (state == MA_PA_CONTEXT_FAILED || state == MA_PA_CONTEXT_TERMINATED) { - ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[PulseAudio] An error occurred while connecting the PulseAudio context."); - return MA_ERROR; - } - - resultPA = ((ma_pa_mainloop_iterate_proc)pContext->pulse.pa_mainloop_iterate)((ma_pa_mainloop*)pMainLoop, 1, NULL); - if (resultPA < 0) { - return ma_result_from_pulse(resultPA); - } - } - - /* Should never get here. */ - return MA_SUCCESS; -} - -static ma_result ma_wait_for_pa_stream_to_connect__pulse(ma_context* pContext, ma_ptr pMainLoop, ma_ptr pStream) -{ - int resultPA; - ma_pa_stream_state_t state; - - for (;;) { - state = ((ma_pa_stream_get_state_proc)pContext->pulse.pa_stream_get_state)((ma_pa_stream*)pStream); - if (state == MA_PA_STREAM_READY) { - break; /* Done. */ - } - - if (state == MA_PA_STREAM_FAILED || state == MA_PA_STREAM_TERMINATED) { - ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[PulseAudio] An error occurred while connecting the PulseAudio stream."); - return MA_ERROR; - } - - resultPA = ((ma_pa_mainloop_iterate_proc)pContext->pulse.pa_mainloop_iterate)((ma_pa_mainloop*)pMainLoop, 1, NULL); - if (resultPA < 0) { - return ma_result_from_pulse(resultPA); - } - } - - return MA_SUCCESS; -} - - -static ma_result ma_init_pa_mainloop_and_pa_context__pulse(ma_context* pContext, const char* pApplicationName, const char* pServerName, ma_bool32 tryAutoSpawn, ma_ptr* ppMainLoop, ma_ptr* ppPulseContext) -{ - ma_result result; - ma_ptr pMainLoop; - ma_ptr pPulseContext; - - MA_ASSERT(ppMainLoop != NULL); - MA_ASSERT(ppPulseContext != NULL); - - /* The PulseAudio context maps well to miniaudio's notion of a context. The pa_context object will be initialized as part of the ma_context. */ - pMainLoop = ((ma_pa_mainloop_new_proc)pContext->pulse.pa_mainloop_new)(); - if (pMainLoop == NULL) { - ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[PulseAudio] Failed to create mainloop."); - return MA_FAILED_TO_INIT_BACKEND; - } - - pPulseContext = ((ma_pa_context_new_proc)pContext->pulse.pa_context_new)(((ma_pa_mainloop_get_api_proc)pContext->pulse.pa_mainloop_get_api)((ma_pa_mainloop*)pMainLoop), pApplicationName); - if (pPulseContext == NULL) { - ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[PulseAudio] Failed to create PulseAudio context."); - ((ma_pa_mainloop_free_proc)pContext->pulse.pa_mainloop_free)((ma_pa_mainloop*)(pMainLoop)); - return MA_FAILED_TO_INIT_BACKEND; - } - - /* Now we need to connect to the context. Everything is asynchronous so we need to wait for it to connect before returning. */ - result = ma_result_from_pulse(((ma_pa_context_connect_proc)pContext->pulse.pa_context_connect)((ma_pa_context*)pPulseContext, pServerName, (tryAutoSpawn) ? 0 : MA_PA_CONTEXT_NOAUTOSPAWN, NULL)); - if (result != MA_SUCCESS) { - ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[PulseAudio] Failed to connect PulseAudio context."); - ((ma_pa_mainloop_free_proc)pContext->pulse.pa_mainloop_free)((ma_pa_mainloop*)(pMainLoop)); - return result; - } - - /* Since ma_context_init() runs synchronously we need to wait for the PulseAudio context to connect before we return. */ - result = ma_wait_for_pa_context_to_connect__pulse(pContext, pMainLoop, pPulseContext); - if (result != MA_SUCCESS) { - ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[PulseAudio] Waiting for connection failed."); - ((ma_pa_mainloop_free_proc)pContext->pulse.pa_mainloop_free)((ma_pa_mainloop*)(pMainLoop)); - return result; - } - - *ppMainLoop = pMainLoop; - *ppPulseContext = pPulseContext; - - return MA_SUCCESS; -} - - -static void ma_device_sink_info_callback(ma_pa_context* pPulseContext, const ma_pa_sink_info* pInfo, int endOfList, void* pUserData) -{ - ma_pa_sink_info* pInfoOut; - - if (endOfList > 0) { - return; - } - - /* - There has been a report that indicates that pInfo can be null which results - in a null pointer dereference below. We'll check for this for safety. - */ - if (pInfo == NULL) { - return; - } - - pInfoOut = (ma_pa_sink_info*)pUserData; - MA_ASSERT(pInfoOut != NULL); - - *pInfoOut = *pInfo; - - (void)pPulseContext; /* Unused. */ -} - -static void ma_device_source_info_callback(ma_pa_context* pPulseContext, const ma_pa_source_info* pInfo, int endOfList, void* pUserData) -{ - ma_pa_source_info* pInfoOut; - - if (endOfList > 0) { - return; - } - - /* - There has been a report that indicates that pInfo can be null which results - in a null pointer dereference below. We'll check for this for safety. - */ - if (pInfo == NULL) { - return; - } - - pInfoOut = (ma_pa_source_info*)pUserData; - MA_ASSERT(pInfoOut != NULL); - - *pInfoOut = *pInfo; - - (void)pPulseContext; /* Unused. */ -} - -#if 0 -static void ma_device_sink_name_callback(ma_pa_context* pPulseContext, const ma_pa_sink_info* pInfo, int endOfList, void* pUserData) -{ - ma_device* pDevice; - - if (endOfList > 0) { - return; - } - - pDevice = (ma_device*)pUserData; - MA_ASSERT(pDevice != NULL); - - ma_strncpy_s(pDevice->playback.name, sizeof(pDevice->playback.name), pInfo->description, (size_t)-1); - - (void)pPulseContext; /* Unused. */ -} - -static void ma_device_source_name_callback(ma_pa_context* pPulseContext, const ma_pa_source_info* pInfo, int endOfList, void* pUserData) -{ - ma_device* pDevice; - - if (endOfList > 0) { - return; - } - - pDevice = (ma_device*)pUserData; - MA_ASSERT(pDevice != NULL); - - ma_strncpy_s(pDevice->capture.name, sizeof(pDevice->capture.name), pInfo->description, (size_t)-1); - - (void)pPulseContext; /* Unused. */ -} -#endif - -static ma_result ma_context_get_sink_info__pulse(ma_context* pContext, const char* pDeviceName, ma_pa_sink_info* pSinkInfo) -{ - ma_pa_operation* pOP; - - pOP = ((ma_pa_context_get_sink_info_by_name_proc)pContext->pulse.pa_context_get_sink_info_by_name)((ma_pa_context*)pContext->pulse.pPulseContext, pDeviceName, ma_device_sink_info_callback, pSinkInfo); - if (pOP == NULL) { - return MA_ERROR; - } - - return ma_wait_for_operation_and_unref__pulse(pContext, pContext->pulse.pMainLoop, pOP); -} - -static ma_result ma_context_get_source_info__pulse(ma_context* pContext, const char* pDeviceName, ma_pa_source_info* pSourceInfo) -{ - ma_pa_operation* pOP; - - pOP = ((ma_pa_context_get_source_info_by_name_proc)pContext->pulse.pa_context_get_source_info_by_name)((ma_pa_context*)pContext->pulse.pPulseContext, pDeviceName, ma_device_source_info_callback, pSourceInfo); - if (pOP == NULL) { - return MA_ERROR; - } - - return ma_wait_for_operation_and_unref__pulse(pContext, pContext->pulse.pMainLoop, pOP); -} - -static ma_result ma_context_get_default_device_index__pulse(ma_context* pContext, ma_device_type deviceType, ma_uint32* pIndex) -{ - ma_result result; - - MA_ASSERT(pContext != NULL); - MA_ASSERT(pIndex != NULL); - - if (pIndex != NULL) { - *pIndex = (ma_uint32)-1; - } - - if (deviceType == ma_device_type_playback) { - ma_pa_sink_info sinkInfo; - result = ma_context_get_sink_info__pulse(pContext, NULL, &sinkInfo); - if (result != MA_SUCCESS) { - return result; - } - - if (pIndex != NULL) { - *pIndex = sinkInfo.index; - } - } - - if (deviceType == ma_device_type_capture) { - ma_pa_source_info sourceInfo; - result = ma_context_get_source_info__pulse(pContext, NULL, &sourceInfo); - if (result != MA_SUCCESS) { - return result; - } - - if (pIndex != NULL) { - *pIndex = sourceInfo.index; - } - } - - return MA_SUCCESS; -} - - -typedef struct -{ - ma_context* pContext; - ma_enum_devices_callback_proc callback; - void* pUserData; - ma_bool32 isTerminated; - ma_uint32 defaultDeviceIndexPlayback; - ma_uint32 defaultDeviceIndexCapture; -} ma_context_enumerate_devices_callback_data__pulse; - -static void ma_context_enumerate_devices_sink_callback__pulse(ma_pa_context* pPulseContext, const ma_pa_sink_info* pSinkInfo, int endOfList, void* pUserData) -{ - ma_context_enumerate_devices_callback_data__pulse* pData = (ma_context_enumerate_devices_callback_data__pulse*)pUserData; - ma_device_info deviceInfo; - - MA_ASSERT(pData != NULL); - - if (endOfList || pData->isTerminated) { - return; - } - - MA_ZERO_OBJECT(&deviceInfo); - - /* The name from PulseAudio is the ID for miniaudio. */ - if (pSinkInfo->name != NULL) { - ma_strncpy_s(deviceInfo.id.pulse, sizeof(deviceInfo.id.pulse), pSinkInfo->name, (size_t)-1); - } - - /* The description from PulseAudio is the name for miniaudio. */ - if (pSinkInfo->description != NULL) { - ma_strncpy_s(deviceInfo.name, sizeof(deviceInfo.name), pSinkInfo->description, (size_t)-1); - } - - if (pSinkInfo->index == pData->defaultDeviceIndexPlayback) { - deviceInfo.isDefault = MA_TRUE; - } - - pData->isTerminated = !pData->callback(pData->pContext, ma_device_type_playback, &deviceInfo, pData->pUserData); - - (void)pPulseContext; /* Unused. */ -} - -static void ma_context_enumerate_devices_source_callback__pulse(ma_pa_context* pPulseContext, const ma_pa_source_info* pSourceInfo, int endOfList, void* pUserData) -{ - ma_context_enumerate_devices_callback_data__pulse* pData = (ma_context_enumerate_devices_callback_data__pulse*)pUserData; - ma_device_info deviceInfo; - - MA_ASSERT(pData != NULL); - - if (endOfList || pData->isTerminated) { - return; - } - - MA_ZERO_OBJECT(&deviceInfo); - - /* The name from PulseAudio is the ID for miniaudio. */ - if (pSourceInfo->name != NULL) { - ma_strncpy_s(deviceInfo.id.pulse, sizeof(deviceInfo.id.pulse), pSourceInfo->name, (size_t)-1); - } - - /* The description from PulseAudio is the name for miniaudio. */ - if (pSourceInfo->description != NULL) { - ma_strncpy_s(deviceInfo.name, sizeof(deviceInfo.name), pSourceInfo->description, (size_t)-1); - } - - if (pSourceInfo->index == pData->defaultDeviceIndexCapture) { - deviceInfo.isDefault = MA_TRUE; - } - - pData->isTerminated = !pData->callback(pData->pContext, ma_device_type_capture, &deviceInfo, pData->pUserData); - - (void)pPulseContext; /* Unused. */ -} - -static ma_result ma_context_enumerate_devices__pulse(ma_context* pContext, ma_enum_devices_callback_proc callback, void* pUserData) -{ - ma_result result = MA_SUCCESS; - ma_context_enumerate_devices_callback_data__pulse callbackData; - ma_pa_operation* pOP = NULL; - - MA_ASSERT(pContext != NULL); - MA_ASSERT(callback != NULL); - - callbackData.pContext = pContext; - callbackData.callback = callback; - callbackData.pUserData = pUserData; - callbackData.isTerminated = MA_FALSE; - callbackData.defaultDeviceIndexPlayback = (ma_uint32)-1; - callbackData.defaultDeviceIndexCapture = (ma_uint32)-1; - - /* We need to get the index of the default devices. */ - ma_context_get_default_device_index__pulse(pContext, ma_device_type_playback, &callbackData.defaultDeviceIndexPlayback); - ma_context_get_default_device_index__pulse(pContext, ma_device_type_capture, &callbackData.defaultDeviceIndexCapture); - - /* Playback. */ - if (!callbackData.isTerminated) { - pOP = ((ma_pa_context_get_sink_info_list_proc)pContext->pulse.pa_context_get_sink_info_list)((ma_pa_context*)(pContext->pulse.pPulseContext), ma_context_enumerate_devices_sink_callback__pulse, &callbackData); - if (pOP == NULL) { - result = MA_ERROR; - goto done; - } - - result = ma_wait_for_operation__pulse(pContext, pContext->pulse.pMainLoop, pOP); - ((ma_pa_operation_unref_proc)pContext->pulse.pa_operation_unref)(pOP); - - if (result != MA_SUCCESS) { - goto done; - } - } - - - /* Capture. */ - if (!callbackData.isTerminated) { - pOP = ((ma_pa_context_get_source_info_list_proc)pContext->pulse.pa_context_get_source_info_list)((ma_pa_context*)(pContext->pulse.pPulseContext), ma_context_enumerate_devices_source_callback__pulse, &callbackData); - if (pOP == NULL) { - result = MA_ERROR; - goto done; - } - - result = ma_wait_for_operation__pulse(pContext, pContext->pulse.pMainLoop, pOP); - ((ma_pa_operation_unref_proc)pContext->pulse.pa_operation_unref)(pOP); - - if (result != MA_SUCCESS) { - goto done; - } - } - -done: - return result; -} - - -typedef struct -{ - ma_device_info* pDeviceInfo; - ma_uint32 defaultDeviceIndex; - ma_bool32 foundDevice; -} ma_context_get_device_info_callback_data__pulse; - -static void ma_context_get_device_info_sink_callback__pulse(ma_pa_context* pPulseContext, const ma_pa_sink_info* pInfo, int endOfList, void* pUserData) -{ - ma_context_get_device_info_callback_data__pulse* pData = (ma_context_get_device_info_callback_data__pulse*)pUserData; - - if (endOfList > 0) { - return; - } - - MA_ASSERT(pData != NULL); - pData->foundDevice = MA_TRUE; - - if (pInfo->name != NULL) { - ma_strncpy_s(pData->pDeviceInfo->id.pulse, sizeof(pData->pDeviceInfo->id.pulse), pInfo->name, (size_t)-1); - } - - if (pInfo->description != NULL) { - ma_strncpy_s(pData->pDeviceInfo->name, sizeof(pData->pDeviceInfo->name), pInfo->description, (size_t)-1); - } - - /* - We're just reporting a single data format here. I think technically PulseAudio might support - all formats, but I don't trust that PulseAudio will do *anything* right, so I'm just going to - report the "native" device format. - */ - pData->pDeviceInfo->nativeDataFormats[0].format = ma_format_from_pulse(pInfo->sample_spec.format); - pData->pDeviceInfo->nativeDataFormats[0].channels = pInfo->sample_spec.channels; - pData->pDeviceInfo->nativeDataFormats[0].sampleRate = pInfo->sample_spec.rate; - pData->pDeviceInfo->nativeDataFormats[0].flags = 0; - pData->pDeviceInfo->nativeDataFormatCount = 1; - - if (pData->defaultDeviceIndex == pInfo->index) { - pData->pDeviceInfo->isDefault = MA_TRUE; - } - - (void)pPulseContext; /* Unused. */ -} - -static void ma_context_get_device_info_source_callback__pulse(ma_pa_context* pPulseContext, const ma_pa_source_info* pInfo, int endOfList, void* pUserData) -{ - ma_context_get_device_info_callback_data__pulse* pData = (ma_context_get_device_info_callback_data__pulse*)pUserData; - - if (endOfList > 0) { - return; - } - - MA_ASSERT(pData != NULL); - pData->foundDevice = MA_TRUE; - - if (pInfo->name != NULL) { - ma_strncpy_s(pData->pDeviceInfo->id.pulse, sizeof(pData->pDeviceInfo->id.pulse), pInfo->name, (size_t)-1); - } - - if (pInfo->description != NULL) { - ma_strncpy_s(pData->pDeviceInfo->name, sizeof(pData->pDeviceInfo->name), pInfo->description, (size_t)-1); - } - - /* - We're just reporting a single data format here. I think technically PulseAudio might support - all formats, but I don't trust that PulseAudio will do *anything* right, so I'm just going to - report the "native" device format. - */ - pData->pDeviceInfo->nativeDataFormats[0].format = ma_format_from_pulse(pInfo->sample_spec.format); - pData->pDeviceInfo->nativeDataFormats[0].channels = pInfo->sample_spec.channels; - pData->pDeviceInfo->nativeDataFormats[0].sampleRate = pInfo->sample_spec.rate; - pData->pDeviceInfo->nativeDataFormats[0].flags = 0; - pData->pDeviceInfo->nativeDataFormatCount = 1; - - if (pData->defaultDeviceIndex == pInfo->index) { - pData->pDeviceInfo->isDefault = MA_TRUE; - } - - (void)pPulseContext; /* Unused. */ -} - -static ma_result ma_context_get_device_info__pulse(ma_context* pContext, ma_device_type deviceType, const ma_device_id* pDeviceID, ma_device_info* pDeviceInfo) -{ - ma_result result = MA_SUCCESS; - ma_context_get_device_info_callback_data__pulse callbackData; - ma_pa_operation* pOP = NULL; - const char* pDeviceName = NULL; - - MA_ASSERT(pContext != NULL); - - callbackData.pDeviceInfo = pDeviceInfo; - callbackData.foundDevice = MA_FALSE; - - if (pDeviceID != NULL) { - pDeviceName = pDeviceID->pulse; - } else { - pDeviceName = NULL; - } - - result = ma_context_get_default_device_index__pulse(pContext, deviceType, &callbackData.defaultDeviceIndex); - - if (deviceType == ma_device_type_playback) { - pOP = ((ma_pa_context_get_sink_info_by_name_proc)pContext->pulse.pa_context_get_sink_info_by_name)((ma_pa_context*)(pContext->pulse.pPulseContext), pDeviceName, ma_context_get_device_info_sink_callback__pulse, &callbackData); - } else { - pOP = ((ma_pa_context_get_source_info_by_name_proc)pContext->pulse.pa_context_get_source_info_by_name)((ma_pa_context*)(pContext->pulse.pPulseContext), pDeviceName, ma_context_get_device_info_source_callback__pulse, &callbackData); - } - - if (pOP != NULL) { - ma_wait_for_operation_and_unref__pulse(pContext, pContext->pulse.pMainLoop, pOP); - } else { - result = MA_ERROR; - goto done; - } - - if (!callbackData.foundDevice) { - result = MA_NO_DEVICE; - goto done; - } - -done: - return result; -} - -static ma_result ma_device_uninit__pulse(ma_device* pDevice) -{ - ma_context* pContext; - - MA_ASSERT(pDevice != NULL); - - pContext = pDevice->pContext; - MA_ASSERT(pContext != NULL); - - if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) { - ((ma_pa_stream_disconnect_proc)pContext->pulse.pa_stream_disconnect)((ma_pa_stream*)pDevice->pulse.pStreamCapture); - ((ma_pa_stream_unref_proc)pContext->pulse.pa_stream_unref)((ma_pa_stream*)pDevice->pulse.pStreamCapture); - } - - if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) { - ((ma_pa_stream_disconnect_proc)pContext->pulse.pa_stream_disconnect)((ma_pa_stream*)pDevice->pulse.pStreamPlayback); - ((ma_pa_stream_unref_proc)pContext->pulse.pa_stream_unref)((ma_pa_stream*)pDevice->pulse.pStreamPlayback); - } - - if (pDevice->type == ma_device_type_duplex) { - ma_duplex_rb_uninit(&pDevice->duplexRB); - } - - ((ma_pa_context_disconnect_proc)pContext->pulse.pa_context_disconnect)((ma_pa_context*)pDevice->pulse.pPulseContext); - ((ma_pa_context_unref_proc)pContext->pulse.pa_context_unref)((ma_pa_context*)pDevice->pulse.pPulseContext); - ((ma_pa_mainloop_free_proc)pContext->pulse.pa_mainloop_free)((ma_pa_mainloop*)pDevice->pulse.pMainLoop); - - return MA_SUCCESS; -} - -static ma_pa_buffer_attr ma_device__pa_buffer_attr_new(ma_uint32 periodSizeInFrames, ma_uint32 periods, const ma_pa_sample_spec* ss) -{ - ma_pa_buffer_attr attr; - attr.maxlength = periodSizeInFrames * periods * ma_get_bytes_per_frame(ma_format_from_pulse(ss->format), ss->channels); - attr.tlength = attr.maxlength / periods; - attr.prebuf = (ma_uint32)-1; - attr.minreq = (ma_uint32)-1; - attr.fragsize = attr.maxlength / periods; - - return attr; -} - -static ma_pa_stream* ma_device__pa_stream_new__pulse(ma_device* pDevice, const char* pStreamName, const ma_pa_sample_spec* ss, const ma_pa_channel_map* cmap) -{ - static ma_atomic_uint32 g_StreamCounter = { 0 }; - char actualStreamName[256]; - - if (pStreamName != NULL) { - ma_strncpy_s(actualStreamName, sizeof(actualStreamName), pStreamName, (size_t)-1); - } else { - const char* pBaseName = "miniaudio:"; - size_t baseNameLen = strlen(pBaseName); - ma_strcpy_s(actualStreamName, sizeof(actualStreamName), pBaseName); - ma_itoa_s((int)ma_atomic_uint32_get(&g_StreamCounter), actualStreamName + baseNameLen, sizeof(actualStreamName)-baseNameLen, 10); - } - ma_atomic_uint32_fetch_add(&g_StreamCounter, 1); - - return ((ma_pa_stream_new_proc)pDevice->pContext->pulse.pa_stream_new)((ma_pa_context*)pDevice->pulse.pPulseContext, actualStreamName, ss, cmap); -} - - -static void ma_device_on_read__pulse(ma_pa_stream* pStream, size_t byteCount, void* pUserData) -{ - ma_device* pDevice = (ma_device*)pUserData; - ma_uint32 bpf; - ma_uint32 deviceState; - ma_uint64 frameCount; - ma_uint64 framesProcessed; - - MA_ASSERT(pDevice != NULL); - - /* - Don't do anything if the device isn't initialized yet. Yes, this can happen because PulseAudio - can fire this callback before the stream has even started. Ridiculous. - */ - deviceState = ma_device_get_state(pDevice); - if (deviceState != ma_device_state_starting && deviceState != ma_device_state_started) { - return; - } - - bpf = ma_get_bytes_per_frame(pDevice->capture.internalFormat, pDevice->capture.internalChannels); - MA_ASSERT(bpf > 0); - - frameCount = byteCount / bpf; - framesProcessed = 0; - - while (ma_device_get_state(pDevice) == ma_device_state_started && framesProcessed < frameCount) { - const void* pMappedPCMFrames; - size_t bytesMapped; - ma_uint64 framesMapped; - - int pulseResult = ((ma_pa_stream_peek_proc)pDevice->pContext->pulse.pa_stream_peek)(pStream, &pMappedPCMFrames, &bytesMapped); - if (pulseResult < 0) { - break; /* Failed to map. Abort. */ - } - - framesMapped = bytesMapped / bpf; - if (framesMapped > 0) { - if (pMappedPCMFrames != NULL) { - ma_device_handle_backend_data_callback(pDevice, NULL, pMappedPCMFrames, framesMapped); - } else { - /* It's a hole. */ - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "[PulseAudio] ma_device_on_read__pulse: Hole.\n"); - } - - pulseResult = ((ma_pa_stream_drop_proc)pDevice->pContext->pulse.pa_stream_drop)(pStream); - if (pulseResult < 0) { - break; /* Failed to drop the buffer. */ - } - - framesProcessed += framesMapped; - - } else { - /* Nothing was mapped. Just abort. */ - break; - } - } -} - -static ma_result ma_device_write_to_stream__pulse(ma_device* pDevice, ma_pa_stream* pStream, ma_uint64* pFramesProcessed) -{ - ma_result result = MA_SUCCESS; - ma_uint64 framesProcessed = 0; - size_t bytesMapped; - ma_uint32 bpf; - ma_uint32 deviceState; - - MA_ASSERT(pDevice != NULL); - MA_ASSERT(pStream != NULL); - - bpf = ma_get_bytes_per_frame(pDevice->playback.internalFormat, pDevice->playback.internalChannels); - MA_ASSERT(bpf > 0); - - deviceState = ma_device_get_state(pDevice); - - bytesMapped = ((ma_pa_stream_writable_size_proc)pDevice->pContext->pulse.pa_stream_writable_size)(pStream); - if (bytesMapped != (size_t)-1) { - if (bytesMapped > 0) { - ma_uint64 framesMapped; - void* pMappedPCMFrames; - int pulseResult = ((ma_pa_stream_begin_write_proc)pDevice->pContext->pulse.pa_stream_begin_write)(pStream, &pMappedPCMFrames, &bytesMapped); - if (pulseResult < 0) { - result = ma_result_from_pulse(pulseResult); - goto done; - } - - framesMapped = bytesMapped / bpf; - - if (deviceState == ma_device_state_started || deviceState == ma_device_state_starting) { /* Check for starting state just in case this is being used to do the initial fill. */ - ma_device_handle_backend_data_callback(pDevice, pMappedPCMFrames, NULL, framesMapped); - } else { - /* Device is not started. Write silence. */ - ma_silence_pcm_frames(pMappedPCMFrames, framesMapped, pDevice->playback.format, pDevice->playback.channels); - } - - pulseResult = ((ma_pa_stream_write_proc)pDevice->pContext->pulse.pa_stream_write)(pStream, pMappedPCMFrames, bytesMapped, NULL, 0, MA_PA_SEEK_RELATIVE); - if (pulseResult < 0) { - result = ma_result_from_pulse(pulseResult); - goto done; /* Failed to write data to stream. */ - } - - framesProcessed += framesMapped; - } else { - result = MA_SUCCESS; /* No data available for writing. */ - goto done; - } - } else { - result = MA_ERROR; /* Failed to retrieve the writable size. Abort. */ - goto done; - } - -done: - if (pFramesProcessed != NULL) { - *pFramesProcessed = framesProcessed; - } - - return result; -} - -static void ma_device_on_write__pulse(ma_pa_stream* pStream, size_t byteCount, void* pUserData) -{ - ma_device* pDevice = (ma_device*)pUserData; - ma_uint32 bpf; - ma_uint64 frameCount; - ma_uint64 framesProcessed; - ma_uint32 deviceState; - ma_result result; - - MA_ASSERT(pDevice != NULL); - - /* - Don't do anything if the device isn't initialized yet. Yes, this can happen because PulseAudio - can fire this callback before the stream has even started. Ridiculous. - */ - deviceState = ma_device_get_state(pDevice); - if (deviceState != ma_device_state_starting && deviceState != ma_device_state_started) { - return; - } - - bpf = ma_get_bytes_per_frame(pDevice->playback.internalFormat, pDevice->playback.internalChannels); - MA_ASSERT(bpf > 0); - - frameCount = byteCount / bpf; - framesProcessed = 0; - - while (framesProcessed < frameCount) { - ma_uint64 framesProcessedThisIteration; - - /* Don't keep trying to process frames if the device isn't started. */ - deviceState = ma_device_get_state(pDevice); - if (deviceState != ma_device_state_starting && deviceState != ma_device_state_started) { - break; - } - - result = ma_device_write_to_stream__pulse(pDevice, pStream, &framesProcessedThisIteration); - if (result != MA_SUCCESS) { - break; - } - - framesProcessed += framesProcessedThisIteration; - } -} - -static void ma_device_on_suspended__pulse(ma_pa_stream* pStream, void* pUserData) -{ - ma_device* pDevice = (ma_device*)pUserData; - int suspended; - - (void)pStream; - - suspended = ((ma_pa_stream_is_suspended_proc)pDevice->pContext->pulse.pa_stream_is_suspended)(pStream); - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "[Pulse] Device suspended state changed. pa_stream_is_suspended() returned %d.\n", suspended); - - if (suspended < 0) { - return; - } - - if (suspended == 1) { - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "[Pulse] Device suspended state changed. Suspended.\n"); - ma_device__on_notification_stopped(pDevice); - } else { - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "[Pulse] Device suspended state changed. Resumed.\n"); - ma_device__on_notification_started(pDevice); - } -} - -static void ma_device_on_rerouted__pulse(ma_pa_stream* pStream, void* pUserData) -{ - ma_device* pDevice = (ma_device*)pUserData; - - (void)pStream; - (void)pUserData; - - ma_device__on_notification_rerouted(pDevice); -} - -static ma_uint32 ma_calculate_period_size_in_frames_from_descriptor__pulse(const ma_device_descriptor* pDescriptor, ma_uint32 nativeSampleRate, ma_performance_profile performanceProfile) -{ - /* - There have been reports from users where buffers of < ~20ms result glitches when running through - PipeWire. To work around this we're going to have to use a different default buffer size. - */ - const ma_uint32 defaultPeriodSizeInMilliseconds_LowLatency = 25; - const ma_uint32 defaultPeriodSizeInMilliseconds_Conservative = MA_DEFAULT_PERIOD_SIZE_IN_MILLISECONDS_CONSERVATIVE; - - MA_ASSERT(nativeSampleRate != 0); - - if (pDescriptor->periodSizeInFrames == 0) { - if (pDescriptor->periodSizeInMilliseconds == 0) { - if (performanceProfile == ma_performance_profile_low_latency) { - return ma_calculate_buffer_size_in_frames_from_milliseconds(defaultPeriodSizeInMilliseconds_LowLatency, nativeSampleRate); - } else { - return ma_calculate_buffer_size_in_frames_from_milliseconds(defaultPeriodSizeInMilliseconds_Conservative, nativeSampleRate); - } - } else { - return ma_calculate_buffer_size_in_frames_from_milliseconds(pDescriptor->periodSizeInMilliseconds, nativeSampleRate); - } - } else { - return pDescriptor->periodSizeInFrames; - } -} - -static ma_result ma_device_init__pulse(ma_device* pDevice, const ma_device_config* pConfig, ma_device_descriptor* pDescriptorPlayback, ma_device_descriptor* pDescriptorCapture) -{ - /* - Notes for PulseAudio: - - - When both the period size in frames and milliseconds are 0, we default to miniaudio's - default buffer sizes rather than leaving it up to PulseAudio because I don't trust - PulseAudio to give us any kind of reasonable latency by default. - - - Do not ever, *ever* forget to use MA_PA_STREAM_ADJUST_LATENCY. If you don't specify this - flag, capture mode will just not work properly until you open another PulseAudio app. - */ - - ma_result result = MA_SUCCESS; - int error = 0; - const char* devPlayback = NULL; - const char* devCapture = NULL; - ma_format format = ma_format_unknown; - ma_uint32 channels = 0; - ma_uint32 sampleRate = 0; - ma_pa_sink_info sinkInfo; - ma_pa_source_info sourceInfo; - ma_pa_sample_spec ss; - ma_pa_channel_map cmap; - ma_pa_buffer_attr attr; - const ma_pa_sample_spec* pActualSS = NULL; - const ma_pa_buffer_attr* pActualAttr = NULL; - const ma_pa_channel_map* pActualChannelMap = NULL; - ma_uint32 iChannel; - ma_pa_stream_flags_t streamFlags; - - MA_ASSERT(pDevice != NULL); - MA_ZERO_OBJECT(&pDevice->pulse); - - if (pConfig->deviceType == ma_device_type_loopback) { - return MA_DEVICE_TYPE_NOT_SUPPORTED; - } - - /* No exclusive mode with the PulseAudio backend. */ - if (((pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) && pConfig->playback.shareMode == ma_share_mode_exclusive) || - ((pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) && pConfig->capture.shareMode == ma_share_mode_exclusive)) { - return MA_SHARE_MODE_NOT_SUPPORTED; - } - - if (pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) { - if (pDescriptorPlayback->pDeviceID != NULL) { - devPlayback = pDescriptorPlayback->pDeviceID->pulse; - } - - format = pDescriptorPlayback->format; - channels = pDescriptorPlayback->channels; - sampleRate = pDescriptorPlayback->sampleRate; - } - - if (pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) { - if (pDescriptorCapture->pDeviceID != NULL) { - devCapture = pDescriptorCapture->pDeviceID->pulse; - } - - format = pDescriptorCapture->format; - channels = pDescriptorCapture->channels; - sampleRate = pDescriptorCapture->sampleRate; - } - - - - result = ma_init_pa_mainloop_and_pa_context__pulse(pDevice->pContext, pDevice->pContext->pulse.pApplicationName, pDevice->pContext->pulse.pServerName, MA_FALSE, &pDevice->pulse.pMainLoop, &pDevice->pulse.pPulseContext); - if (result != MA_SUCCESS) { - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[PulseAudio] Failed to initialize PA mainloop and context for device.\n"); - return result; - } - - if (pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) { - result = ma_context_get_source_info__pulse(pDevice->pContext, devCapture, &sourceInfo); - if (result != MA_SUCCESS) { - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[PulseAudio] Failed to retrieve source info for capture device."); - goto on_error0; - } - - ss = sourceInfo.sample_spec; - cmap = sourceInfo.channel_map; - - /* Use the requested channel count if we have one. */ - if (pDescriptorCapture->channels != 0) { - ss.channels = pDescriptorCapture->channels; - } - - /* Use a default channel map. */ - ((ma_pa_channel_map_init_extend_proc)pDevice->pContext->pulse.pa_channel_map_init_extend)(&cmap, ss.channels, pConfig->pulse.channelMap); - - /* Use the requested sample rate if one was specified. */ - if (pDescriptorCapture->sampleRate != 0) { - ss.rate = pDescriptorCapture->sampleRate; - } - streamFlags = MA_PA_STREAM_START_CORKED | MA_PA_STREAM_ADJUST_LATENCY; - - if (ma_format_from_pulse(ss.format) == ma_format_unknown) { - if (ma_is_little_endian()) { - ss.format = MA_PA_SAMPLE_FLOAT32LE; - } else { - ss.format = MA_PA_SAMPLE_FLOAT32BE; - } - streamFlags |= MA_PA_STREAM_FIX_FORMAT; - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, "[PulseAudio] sample_spec.format not supported by miniaudio. Defaulting to PA_SAMPLE_FLOAT32.\n"); - } - if (ss.rate == 0) { - ss.rate = MA_DEFAULT_SAMPLE_RATE; - streamFlags |= MA_PA_STREAM_FIX_RATE; - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, "[PulseAudio] sample_spec.rate = 0. Defaulting to %d.\n", ss.rate); - } - if (ss.channels == 0) { - ss.channels = MA_DEFAULT_CHANNELS; - streamFlags |= MA_PA_STREAM_FIX_CHANNELS; - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, "[PulseAudio] sample_spec.channels = 0. Defaulting to %d.\n", ss.channels); - } - - /* We now have enough information to calculate our actual period size in frames. */ - pDescriptorCapture->periodSizeInFrames = ma_calculate_period_size_in_frames_from_descriptor__pulse(pDescriptorCapture, ss.rate, pConfig->performanceProfile); - - attr = ma_device__pa_buffer_attr_new(pDescriptorCapture->periodSizeInFrames, pDescriptorCapture->periodCount, &ss); - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, "[PulseAudio] Capture attr: maxlength=%d, tlength=%d, prebuf=%d, minreq=%d, fragsize=%d; periodSizeInFrames=%d\n", attr.maxlength, attr.tlength, attr.prebuf, attr.minreq, attr.fragsize, pDescriptorCapture->periodSizeInFrames); - - pDevice->pulse.pStreamCapture = ma_device__pa_stream_new__pulse(pDevice, pConfig->pulse.pStreamNameCapture, &ss, &cmap); - if (pDevice->pulse.pStreamCapture == NULL) { - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[PulseAudio] Failed to create PulseAudio capture stream.\n"); - result = MA_ERROR; - goto on_error0; - } - - - /* The callback needs to be set before connecting the stream. */ - ((ma_pa_stream_set_read_callback_proc)pDevice->pContext->pulse.pa_stream_set_read_callback)((ma_pa_stream*)pDevice->pulse.pStreamCapture, ma_device_on_read__pulse, pDevice); - - /* State callback for checking when the device has been corked. */ - ((ma_pa_stream_set_suspended_callback_proc)pDevice->pContext->pulse.pa_stream_set_suspended_callback)((ma_pa_stream*)pDevice->pulse.pStreamCapture, ma_device_on_suspended__pulse, pDevice); - - /* Rerouting notification. */ - ((ma_pa_stream_set_moved_callback_proc)pDevice->pContext->pulse.pa_stream_set_moved_callback)((ma_pa_stream*)pDevice->pulse.pStreamCapture, ma_device_on_rerouted__pulse, pDevice); - - - /* Connect after we've got all of our internal state set up. */ - if (devCapture != NULL) { - streamFlags |= MA_PA_STREAM_DONT_MOVE; - } - - error = ((ma_pa_stream_connect_record_proc)pDevice->pContext->pulse.pa_stream_connect_record)((ma_pa_stream*)pDevice->pulse.pStreamCapture, devCapture, &attr, streamFlags); - if (error != MA_PA_OK) { - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[PulseAudio] Failed to connect PulseAudio capture stream."); - result = ma_result_from_pulse(error); - goto on_error1; - } - - result = ma_wait_for_pa_stream_to_connect__pulse(pDevice->pContext, pDevice->pulse.pMainLoop, (ma_pa_stream*)pDevice->pulse.pStreamCapture); - if (result != MA_SUCCESS) { - goto on_error2; - } - - - /* Internal format. */ - pActualSS = ((ma_pa_stream_get_sample_spec_proc)pDevice->pContext->pulse.pa_stream_get_sample_spec)((ma_pa_stream*)pDevice->pulse.pStreamCapture); - if (pActualSS != NULL) { - ss = *pActualSS; - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, "[PulseAudio] Capture sample spec: format=%s, channels=%d, rate=%d\n", ma_get_format_name(ma_format_from_pulse(ss.format)), ss.channels, ss.rate); - } else { - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, "[PulseAudio] Failed to retrieve capture sample spec.\n"); - } - - pDescriptorCapture->format = ma_format_from_pulse(ss.format); - pDescriptorCapture->channels = ss.channels; - pDescriptorCapture->sampleRate = ss.rate; - - if (pDescriptorCapture->format == ma_format_unknown || pDescriptorCapture->channels == 0 || pDescriptorCapture->sampleRate == 0) { - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[PulseAudio] Capture sample spec is invalid. Device unusable by miniaudio. format=%s, channels=%d, sampleRate=%d.\n", ma_get_format_name(pDescriptorCapture->format), pDescriptorCapture->channels, pDescriptorCapture->sampleRate); - result = MA_ERROR; - goto on_error4; - } - - - /* Internal channel map. */ - pActualChannelMap = ((ma_pa_stream_get_channel_map_proc)pDevice->pContext->pulse.pa_stream_get_channel_map)((ma_pa_stream*)pDevice->pulse.pStreamCapture); - if (pActualChannelMap == NULL) { - pActualChannelMap = &cmap; /* Fallback just in case. */ - } - - /* - Bug in PipeWire. There have been reports that PipeWire is returning AUX channels when reporting - the channel map. To somewhat workaround this, I'm hacking in a hard coded channel map for mono - and stereo. In this case it should be safe to assume mono = MONO and stereo = LEFT/RIGHT. For - all other channel counts we need to just put up with whatever PipeWire reports and hope it gets - fixed sooner than later. I might remove this hack later. - */ - if (pDescriptorCapture->channels > 2) { - for (iChannel = 0; iChannel < pDescriptorCapture->channels; iChannel += 1) { - pDescriptorCapture->channelMap[iChannel] = ma_channel_position_from_pulse(pActualChannelMap->map[iChannel]); - } - } else { - /* Hack for mono and stereo. */ - if (pDescriptorCapture->channels == 1) { - pDescriptorCapture->channelMap[0] = MA_CHANNEL_MONO; - } else if (pDescriptorCapture->channels == 2) { - pDescriptorCapture->channelMap[0] = MA_CHANNEL_FRONT_LEFT; - pDescriptorCapture->channelMap[1] = MA_CHANNEL_FRONT_RIGHT; - } else { - MA_ASSERT(MA_FALSE); /* Should never hit this. */ - } - } - - - /* Buffer. */ - pActualAttr = ((ma_pa_stream_get_buffer_attr_proc)pDevice->pContext->pulse.pa_stream_get_buffer_attr)((ma_pa_stream*)pDevice->pulse.pStreamCapture); - if (pActualAttr != NULL) { - attr = *pActualAttr; - } - - if (attr.fragsize > 0) { - pDescriptorCapture->periodCount = ma_max(attr.maxlength / attr.fragsize, 1); - } else { - pDescriptorCapture->periodCount = 1; - } - - pDescriptorCapture->periodSizeInFrames = attr.maxlength / ma_get_bytes_per_frame(pDescriptorCapture->format, pDescriptorCapture->channels) / pDescriptorCapture->periodCount; - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, "[PulseAudio] Capture actual attr: maxlength=%d, tlength=%d, prebuf=%d, minreq=%d, fragsize=%d; periodSizeInFrames=%d\n", attr.maxlength, attr.tlength, attr.prebuf, attr.minreq, attr.fragsize, pDescriptorCapture->periodSizeInFrames); - } - - if (pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) { - result = ma_context_get_sink_info__pulse(pDevice->pContext, devPlayback, &sinkInfo); - if (result != MA_SUCCESS) { - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[PulseAudio] Failed to retrieve sink info for playback device.\n"); - goto on_error2; - } - - ss = sinkInfo.sample_spec; - cmap = sinkInfo.channel_map; - - /* Use the requested channel count if we have one. */ - if (pDescriptorPlayback->channels != 0) { - ss.channels = pDescriptorPlayback->channels; - } - - /* Use a default channel map. */ - ((ma_pa_channel_map_init_extend_proc)pDevice->pContext->pulse.pa_channel_map_init_extend)(&cmap, ss.channels, pConfig->pulse.channelMap); - - - /* Use the requested sample rate if one was specified. */ - if (pDescriptorPlayback->sampleRate != 0) { - ss.rate = pDescriptorPlayback->sampleRate; - } - - streamFlags = MA_PA_STREAM_START_CORKED | MA_PA_STREAM_ADJUST_LATENCY; - if (ma_format_from_pulse(ss.format) == ma_format_unknown) { - if (ma_is_little_endian()) { - ss.format = MA_PA_SAMPLE_FLOAT32LE; - } else { - ss.format = MA_PA_SAMPLE_FLOAT32BE; - } - streamFlags |= MA_PA_STREAM_FIX_FORMAT; - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, "[PulseAudio] sample_spec.format not supported by miniaudio. Defaulting to PA_SAMPLE_FLOAT32.\n"); - } - if (ss.rate == 0) { - ss.rate = MA_DEFAULT_SAMPLE_RATE; - streamFlags |= MA_PA_STREAM_FIX_RATE; - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, "[PulseAudio] sample_spec.rate = 0. Defaulting to %d.\n", ss.rate); - } - if (ss.channels == 0) { - ss.channels = MA_DEFAULT_CHANNELS; - streamFlags |= MA_PA_STREAM_FIX_CHANNELS; - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, "[PulseAudio] sample_spec.channels = 0. Defaulting to %d.\n", ss.channels); - } - - /* We now have enough information to calculate the actual buffer size in frames. */ - pDescriptorPlayback->periodSizeInFrames = ma_calculate_period_size_in_frames_from_descriptor__pulse(pDescriptorPlayback, ss.rate, pConfig->performanceProfile); - - attr = ma_device__pa_buffer_attr_new(pDescriptorPlayback->periodSizeInFrames, pDescriptorPlayback->periodCount, &ss); - - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, "[PulseAudio] Playback attr: maxlength=%d, tlength=%d, prebuf=%d, minreq=%d, fragsize=%d; periodSizeInFrames=%d\n", attr.maxlength, attr.tlength, attr.prebuf, attr.minreq, attr.fragsize, pDescriptorPlayback->periodSizeInFrames); - - pDevice->pulse.pStreamPlayback = ma_device__pa_stream_new__pulse(pDevice, pConfig->pulse.pStreamNamePlayback, &ss, &cmap); - if (pDevice->pulse.pStreamPlayback == NULL) { - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[PulseAudio] Failed to create PulseAudio playback stream.\n"); - result = MA_ERROR; - goto on_error2; - } - - - /* - Note that this callback will be fired as soon as the stream is connected, even though it's started as corked. The callback needs to handle a - device state of ma_device_state_uninitialized. - */ - ((ma_pa_stream_set_write_callback_proc)pDevice->pContext->pulse.pa_stream_set_write_callback)((ma_pa_stream*)pDevice->pulse.pStreamPlayback, ma_device_on_write__pulse, pDevice); - - /* State callback for checking when the device has been corked. */ - ((ma_pa_stream_set_suspended_callback_proc)pDevice->pContext->pulse.pa_stream_set_suspended_callback)((ma_pa_stream*)pDevice->pulse.pStreamPlayback, ma_device_on_suspended__pulse, pDevice); - - /* Rerouting notification. */ - ((ma_pa_stream_set_moved_callback_proc)pDevice->pContext->pulse.pa_stream_set_moved_callback)((ma_pa_stream*)pDevice->pulse.pStreamPlayback, ma_device_on_rerouted__pulse, pDevice); - - - /* Connect after we've got all of our internal state set up. */ - if (devPlayback != NULL) { - streamFlags |= MA_PA_STREAM_DONT_MOVE; - } - - error = ((ma_pa_stream_connect_playback_proc)pDevice->pContext->pulse.pa_stream_connect_playback)((ma_pa_stream*)pDevice->pulse.pStreamPlayback, devPlayback, &attr, streamFlags, NULL, NULL); - if (error != MA_PA_OK) { - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[PulseAudio] Failed to connect PulseAudio playback stream."); - result = ma_result_from_pulse(error); - goto on_error3; - } - - result = ma_wait_for_pa_stream_to_connect__pulse(pDevice->pContext, pDevice->pulse.pMainLoop, (ma_pa_stream*)pDevice->pulse.pStreamPlayback); - if (result != MA_SUCCESS) { - goto on_error3; - } - - - /* Internal format. */ - pActualSS = ((ma_pa_stream_get_sample_spec_proc)pDevice->pContext->pulse.pa_stream_get_sample_spec)((ma_pa_stream*)pDevice->pulse.pStreamPlayback); - if (pActualSS != NULL) { - ss = *pActualSS; - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, "[PulseAudio] Playback sample spec: format=%s, channels=%d, rate=%d\n", ma_get_format_name(ma_format_from_pulse(ss.format)), ss.channels, ss.rate); - } else { - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, "[PulseAudio] Failed to retrieve playback sample spec.\n"); - } - - pDescriptorPlayback->format = ma_format_from_pulse(ss.format); - pDescriptorPlayback->channels = ss.channels; - pDescriptorPlayback->sampleRate = ss.rate; - - if (pDescriptorPlayback->format == ma_format_unknown || pDescriptorPlayback->channels == 0 || pDescriptorPlayback->sampleRate == 0) { - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[PulseAudio] Playback sample spec is invalid. Device unusable by miniaudio. format=%s, channels=%d, sampleRate=%d.\n", ma_get_format_name(pDescriptorPlayback->format), pDescriptorPlayback->channels, pDescriptorPlayback->sampleRate); - result = MA_ERROR; - goto on_error4; - } - - - /* Internal channel map. */ - pActualChannelMap = ((ma_pa_stream_get_channel_map_proc)pDevice->pContext->pulse.pa_stream_get_channel_map)((ma_pa_stream*)pDevice->pulse.pStreamPlayback); - if (pActualChannelMap == NULL) { - pActualChannelMap = &cmap; /* Fallback just in case. */ - } - - /* - Bug in PipeWire. There have been reports that PipeWire is returning AUX channels when reporting - the channel map. To somewhat workaround this, I'm hacking in a hard coded channel map for mono - and stereo. In this case it should be safe to assume mono = MONO and stereo = LEFT/RIGHT. For - all other channel counts we need to just put up with whatever PipeWire reports and hope it gets - fixed sooner than later. I might remove this hack later. - */ - if (pDescriptorPlayback->channels > 2) { - for (iChannel = 0; iChannel < pDescriptorPlayback->channels; iChannel += 1) { - pDescriptorPlayback->channelMap[iChannel] = ma_channel_position_from_pulse(pActualChannelMap->map[iChannel]); - } - } else { - /* Hack for mono and stereo. */ - if (pDescriptorPlayback->channels == 1) { - pDescriptorPlayback->channelMap[0] = MA_CHANNEL_MONO; - } else if (pDescriptorPlayback->channels == 2) { - pDescriptorPlayback->channelMap[0] = MA_CHANNEL_FRONT_LEFT; - pDescriptorPlayback->channelMap[1] = MA_CHANNEL_FRONT_RIGHT; - } else { - MA_ASSERT(MA_FALSE); /* Should never hit this. */ - } - } - - - /* Buffer. */ - pActualAttr = ((ma_pa_stream_get_buffer_attr_proc)pDevice->pContext->pulse.pa_stream_get_buffer_attr)((ma_pa_stream*)pDevice->pulse.pStreamPlayback); - if (pActualAttr != NULL) { - attr = *pActualAttr; - } - - if (attr.tlength > 0) { - pDescriptorPlayback->periodCount = ma_max(attr.maxlength / attr.tlength, 1); - } else { - pDescriptorPlayback->periodCount = 1; - } - - pDescriptorPlayback->periodSizeInFrames = attr.maxlength / ma_get_bytes_per_frame(pDescriptorPlayback->format, pDescriptorPlayback->channels) / pDescriptorPlayback->periodCount; - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, "[PulseAudio] Playback actual attr: maxlength=%d, tlength=%d, prebuf=%d, minreq=%d, fragsize=%d; internalPeriodSizeInFrames=%d\n", attr.maxlength, attr.tlength, attr.prebuf, attr.minreq, attr.fragsize, pDescriptorPlayback->periodSizeInFrames); - } - - - /* - We need a ring buffer for handling duplex mode. We can use the main duplex ring buffer in the main - part of the ma_device struct. We cannot, however, depend on ma_device_init() initializing this for - us later on because that will only do it if it's a fully asynchronous backend - i.e. the - onDeviceDataLoop callback is NULL, which is not the case for PulseAudio. - */ - if (pConfig->deviceType == ma_device_type_duplex) { - ma_format rbFormat = (format != ma_format_unknown) ? format : pDescriptorCapture->format; - ma_uint32 rbChannels = (channels > 0) ? channels : pDescriptorCapture->channels; - ma_uint32 rbSampleRate = (sampleRate > 0) ? sampleRate : pDescriptorCapture->sampleRate; - - result = ma_duplex_rb_init(rbFormat, rbChannels, rbSampleRate, pDescriptorCapture->sampleRate, pDescriptorCapture->periodSizeInFrames, &pDevice->pContext->allocationCallbacks, &pDevice->duplexRB); - if (result != MA_SUCCESS) { - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[PulseAudio] Failed to initialize ring buffer. %s.\n", ma_result_description(result)); - goto on_error4; - } - } - - return MA_SUCCESS; - - -on_error4: - if (pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) { - ((ma_pa_stream_disconnect_proc)pDevice->pContext->pulse.pa_stream_disconnect)((ma_pa_stream*)pDevice->pulse.pStreamPlayback); - } -on_error3: - if (pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) { - ((ma_pa_stream_unref_proc)pDevice->pContext->pulse.pa_stream_unref)((ma_pa_stream*)pDevice->pulse.pStreamPlayback); - } -on_error2: - if (pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) { - ((ma_pa_stream_disconnect_proc)pDevice->pContext->pulse.pa_stream_disconnect)((ma_pa_stream*)pDevice->pulse.pStreamCapture); - } -on_error1: - if (pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) { - ((ma_pa_stream_unref_proc)pDevice->pContext->pulse.pa_stream_unref)((ma_pa_stream*)pDevice->pulse.pStreamCapture); - } -on_error0: - return result; -} - - -static void ma_pulse_operation_complete_callback(ma_pa_stream* pStream, int success, void* pUserData) -{ - ma_bool32* pIsSuccessful = (ma_bool32*)pUserData; - MA_ASSERT(pIsSuccessful != NULL); - - *pIsSuccessful = (ma_bool32)success; - - (void)pStream; /* Unused. */ -} - -static ma_result ma_device__cork_stream__pulse(ma_device* pDevice, ma_device_type deviceType, int cork) -{ - ma_context* pContext = pDevice->pContext; - ma_bool32 wasSuccessful; - ma_pa_stream* pStream; - ma_pa_operation* pOP; - ma_result result; - - /* This should not be called with a duplex device type. */ - if (deviceType == ma_device_type_duplex) { - return MA_INVALID_ARGS; - } - - wasSuccessful = MA_FALSE; - - pStream = (ma_pa_stream*)((deviceType == ma_device_type_capture) ? pDevice->pulse.pStreamCapture : pDevice->pulse.pStreamPlayback); - MA_ASSERT(pStream != NULL); - - pOP = ((ma_pa_stream_cork_proc)pContext->pulse.pa_stream_cork)(pStream, cork, ma_pulse_operation_complete_callback, &wasSuccessful); - if (pOP == NULL) { - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[PulseAudio] Failed to cork PulseAudio stream."); - return MA_ERROR; - } - - result = ma_wait_for_operation_and_unref__pulse(pDevice->pContext, pDevice->pulse.pMainLoop, pOP); - if (result != MA_SUCCESS) { - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[PulseAudio] An error occurred while waiting for the PulseAudio stream to cork."); - return result; - } - - if (!wasSuccessful) { - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[PulseAudio] Failed to %s PulseAudio stream.", (cork) ? "stop" : "start"); - return MA_ERROR; - } - - return MA_SUCCESS; -} - -static ma_result ma_device_start__pulse(ma_device* pDevice) -{ - ma_result result; - - MA_ASSERT(pDevice != NULL); - - if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) { - result = ma_device__cork_stream__pulse(pDevice, ma_device_type_capture, 0); - if (result != MA_SUCCESS) { - return result; - } - } - - if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) { - /* - We need to fill some data before uncorking. Not doing this will result in the write callback - never getting fired. We're not going to abort if writing fails because I still want the device - to get uncorked. - */ - ma_device_write_to_stream__pulse(pDevice, (ma_pa_stream*)(pDevice->pulse.pStreamPlayback), NULL); /* No need to check the result here. Always want to fall through an uncork.*/ - - result = ma_device__cork_stream__pulse(pDevice, ma_device_type_playback, 0); - if (result != MA_SUCCESS) { - return result; - } - } - - return MA_SUCCESS; -} - -static ma_result ma_device_stop__pulse(ma_device* pDevice) -{ - ma_result result; - - MA_ASSERT(pDevice != NULL); - - if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) { - result = ma_device__cork_stream__pulse(pDevice, ma_device_type_capture, 1); - if (result != MA_SUCCESS) { - return result; - } - } - - if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) { - /* - Ideally we would drain the device here, but there's been cases where PulseAudio seems to be - broken on some systems to the point where no audio processing seems to happen. When this - happens, draining never completes and we get stuck here. For now I'm disabling draining of - the device so we don't just freeze the application. - */ - #if 0 - ma_pa_operation* pOP = ((ma_pa_stream_drain_proc)pDevice->pContext->pulse.pa_stream_drain)((ma_pa_stream*)pDevice->pulse.pStreamPlayback, ma_pulse_operation_complete_callback, &wasSuccessful); - ma_wait_for_operation_and_unref__pulse(pDevice->pContext, pDevice->pulse.pMainLoop, pOP); - #endif - - result = ma_device__cork_stream__pulse(pDevice, ma_device_type_playback, 1); - if (result != MA_SUCCESS) { - return result; - } - } - - return MA_SUCCESS; -} - -static ma_result ma_device_data_loop__pulse(ma_device* pDevice) -{ - int resultPA; - - MA_ASSERT(pDevice != NULL); - - /* NOTE: Don't start the device here. It'll be done at a higher level. */ - - /* - All data is handled through callbacks. All we need to do is iterate over the main loop and let - the callbacks deal with it. - */ - while (ma_device_get_state(pDevice) == ma_device_state_started) { - resultPA = ((ma_pa_mainloop_iterate_proc)pDevice->pContext->pulse.pa_mainloop_iterate)((ma_pa_mainloop*)pDevice->pulse.pMainLoop, 1, NULL); - if (resultPA < 0) { - break; - } - } - - /* NOTE: Don't stop the device here. It'll be done at a higher level. */ - return MA_SUCCESS; -} - -static ma_result ma_device_data_loop_wakeup__pulse(ma_device* pDevice) -{ - MA_ASSERT(pDevice != NULL); - - ((ma_pa_mainloop_wakeup_proc)pDevice->pContext->pulse.pa_mainloop_wakeup)((ma_pa_mainloop*)pDevice->pulse.pMainLoop); - - return MA_SUCCESS; -} - -static ma_result ma_context_uninit__pulse(ma_context* pContext) -{ - MA_ASSERT(pContext != NULL); - MA_ASSERT(pContext->backend == ma_backend_pulseaudio); - - ((ma_pa_context_disconnect_proc)pContext->pulse.pa_context_disconnect)((ma_pa_context*)pContext->pulse.pPulseContext); - ((ma_pa_context_unref_proc)pContext->pulse.pa_context_unref)((ma_pa_context*)pContext->pulse.pPulseContext); - ((ma_pa_mainloop_free_proc)pContext->pulse.pa_mainloop_free)((ma_pa_mainloop*)pContext->pulse.pMainLoop); - - ma_free(pContext->pulse.pServerName, &pContext->allocationCallbacks); - ma_free(pContext->pulse.pApplicationName, &pContext->allocationCallbacks); - -#ifndef MA_NO_RUNTIME_LINKING - ma_dlclose(ma_context_get_log(pContext), pContext->pulse.pulseSO); -#endif - - return MA_SUCCESS; -} - -static ma_result ma_context_init__pulse(ma_context* pContext, const ma_context_config* pConfig, ma_backend_callbacks* pCallbacks) -{ - ma_result result; -#ifndef MA_NO_RUNTIME_LINKING - const char* libpulseNames[] = { - "libpulse.so", - "libpulse.so.0" - }; - size_t i; - - for (i = 0; i < ma_countof(libpulseNames); ++i) { - pContext->pulse.pulseSO = ma_dlopen(ma_context_get_log(pContext), libpulseNames[i]); - if (pContext->pulse.pulseSO != NULL) { - break; - } - } - - if (pContext->pulse.pulseSO == NULL) { - return MA_NO_BACKEND; - } - - pContext->pulse.pa_mainloop_new = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_mainloop_new"); - pContext->pulse.pa_mainloop_free = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_mainloop_free"); - pContext->pulse.pa_mainloop_quit = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_mainloop_quit"); - pContext->pulse.pa_mainloop_get_api = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_mainloop_get_api"); - pContext->pulse.pa_mainloop_iterate = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_mainloop_iterate"); - pContext->pulse.pa_mainloop_wakeup = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_mainloop_wakeup"); - pContext->pulse.pa_threaded_mainloop_new = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_threaded_mainloop_new"); - pContext->pulse.pa_threaded_mainloop_free = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_threaded_mainloop_free"); - pContext->pulse.pa_threaded_mainloop_start = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_threaded_mainloop_start"); - pContext->pulse.pa_threaded_mainloop_stop = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_threaded_mainloop_stop"); - pContext->pulse.pa_threaded_mainloop_lock = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_threaded_mainloop_lock"); - pContext->pulse.pa_threaded_mainloop_unlock = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_threaded_mainloop_unlock"); - pContext->pulse.pa_threaded_mainloop_wait = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_threaded_mainloop_wait"); - pContext->pulse.pa_threaded_mainloop_signal = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_threaded_mainloop_signal"); - pContext->pulse.pa_threaded_mainloop_accept = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_threaded_mainloop_accept"); - pContext->pulse.pa_threaded_mainloop_get_retval = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_threaded_mainloop_get_retval"); - pContext->pulse.pa_threaded_mainloop_get_api = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_threaded_mainloop_get_api"); - pContext->pulse.pa_threaded_mainloop_in_thread = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_threaded_mainloop_in_thread"); - pContext->pulse.pa_threaded_mainloop_set_name = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_threaded_mainloop_set_name"); - pContext->pulse.pa_context_new = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_context_new"); - pContext->pulse.pa_context_unref = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_context_unref"); - pContext->pulse.pa_context_connect = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_context_connect"); - pContext->pulse.pa_context_disconnect = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_context_disconnect"); - pContext->pulse.pa_context_set_state_callback = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_context_set_state_callback"); - pContext->pulse.pa_context_get_state = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_context_get_state"); - pContext->pulse.pa_context_get_sink_info_list = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_context_get_sink_info_list"); - pContext->pulse.pa_context_get_source_info_list = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_context_get_source_info_list"); - pContext->pulse.pa_context_get_sink_info_by_name = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_context_get_sink_info_by_name"); - pContext->pulse.pa_context_get_source_info_by_name = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_context_get_source_info_by_name"); - pContext->pulse.pa_operation_unref = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_operation_unref"); - pContext->pulse.pa_operation_get_state = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_operation_get_state"); - pContext->pulse.pa_channel_map_init_extend = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_channel_map_init_extend"); - pContext->pulse.pa_channel_map_valid = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_channel_map_valid"); - pContext->pulse.pa_channel_map_compatible = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_channel_map_compatible"); - pContext->pulse.pa_stream_new = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_stream_new"); - pContext->pulse.pa_stream_unref = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_stream_unref"); - pContext->pulse.pa_stream_connect_playback = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_stream_connect_playback"); - pContext->pulse.pa_stream_connect_record = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_stream_connect_record"); - pContext->pulse.pa_stream_disconnect = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_stream_disconnect"); - pContext->pulse.pa_stream_get_state = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_stream_get_state"); - pContext->pulse.pa_stream_get_sample_spec = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_stream_get_sample_spec"); - pContext->pulse.pa_stream_get_channel_map = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_stream_get_channel_map"); - pContext->pulse.pa_stream_get_buffer_attr = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_stream_get_buffer_attr"); - pContext->pulse.pa_stream_set_buffer_attr = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_stream_set_buffer_attr"); - pContext->pulse.pa_stream_get_device_name = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_stream_get_device_name"); - pContext->pulse.pa_stream_set_write_callback = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_stream_set_write_callback"); - pContext->pulse.pa_stream_set_read_callback = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_stream_set_read_callback"); - pContext->pulse.pa_stream_set_suspended_callback = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_stream_set_suspended_callback"); - pContext->pulse.pa_stream_set_moved_callback = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_stream_set_moved_callback"); - pContext->pulse.pa_stream_is_suspended = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_stream_is_suspended"); - pContext->pulse.pa_stream_flush = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_stream_flush"); - pContext->pulse.pa_stream_drain = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_stream_drain"); - pContext->pulse.pa_stream_is_corked = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_stream_is_corked"); - pContext->pulse.pa_stream_cork = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_stream_cork"); - pContext->pulse.pa_stream_trigger = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_stream_trigger"); - pContext->pulse.pa_stream_begin_write = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_stream_begin_write"); - pContext->pulse.pa_stream_write = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_stream_write"); - pContext->pulse.pa_stream_peek = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_stream_peek"); - pContext->pulse.pa_stream_drop = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_stream_drop"); - pContext->pulse.pa_stream_writable_size = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_stream_writable_size"); - pContext->pulse.pa_stream_readable_size = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_stream_readable_size"); -#else - /* This strange assignment system is just for type safety. */ - ma_pa_mainloop_new_proc _pa_mainloop_new = pa_mainloop_new; - ma_pa_mainloop_free_proc _pa_mainloop_free = pa_mainloop_free; - ma_pa_mainloop_quit_proc _pa_mainloop_quit = pa_mainloop_quit; - ma_pa_mainloop_get_api_proc _pa_mainloop_get_api = pa_mainloop_get_api; - ma_pa_mainloop_iterate_proc _pa_mainloop_iterate = pa_mainloop_iterate; - ma_pa_mainloop_wakeup_proc _pa_mainloop_wakeup = pa_mainloop_wakeup; - ma_pa_threaded_mainloop_new_proc _pa_threaded_mainloop_new = pa_threaded_mainloop_new; - ma_pa_threaded_mainloop_free_proc _pa_threaded_mainloop_free = pa_threaded_mainloop_free; - ma_pa_threaded_mainloop_start_proc _pa_threaded_mainloop_start = pa_threaded_mainloop_start; - ma_pa_threaded_mainloop_stop_proc _pa_threaded_mainloop_stop = pa_threaded_mainloop_stop; - ma_pa_threaded_mainloop_lock_proc _pa_threaded_mainloop_lock = pa_threaded_mainloop_lock; - ma_pa_threaded_mainloop_unlock_proc _pa_threaded_mainloop_unlock = pa_threaded_mainloop_unlock; - ma_pa_threaded_mainloop_wait_proc _pa_threaded_mainloop_wait = pa_threaded_mainloop_wait; - ma_pa_threaded_mainloop_signal_proc _pa_threaded_mainloop_signal = pa_threaded_mainloop_signal; - ma_pa_threaded_mainloop_accept_proc _pa_threaded_mainloop_accept = pa_threaded_mainloop_accept; - ma_pa_threaded_mainloop_get_retval_proc _pa_threaded_mainloop_get_retval = pa_threaded_mainloop_get_retval; - ma_pa_threaded_mainloop_get_api_proc _pa_threaded_mainloop_get_api = pa_threaded_mainloop_get_api; - ma_pa_threaded_mainloop_in_thread_proc _pa_threaded_mainloop_in_thread = pa_threaded_mainloop_in_thread; - ma_pa_threaded_mainloop_set_name_proc _pa_threaded_mainloop_set_name = pa_threaded_mainloop_set_name; - ma_pa_context_new_proc _pa_context_new = pa_context_new; - ma_pa_context_unref_proc _pa_context_unref = pa_context_unref; - ma_pa_context_connect_proc _pa_context_connect = pa_context_connect; - ma_pa_context_disconnect_proc _pa_context_disconnect = pa_context_disconnect; - ma_pa_context_set_state_callback_proc _pa_context_set_state_callback = pa_context_set_state_callback; - ma_pa_context_get_state_proc _pa_context_get_state = pa_context_get_state; - ma_pa_context_get_sink_info_list_proc _pa_context_get_sink_info_list = pa_context_get_sink_info_list; - ma_pa_context_get_source_info_list_proc _pa_context_get_source_info_list = pa_context_get_source_info_list; - ma_pa_context_get_sink_info_by_name_proc _pa_context_get_sink_info_by_name = pa_context_get_sink_info_by_name; - ma_pa_context_get_source_info_by_name_proc _pa_context_get_source_info_by_name= pa_context_get_source_info_by_name; - ma_pa_operation_unref_proc _pa_operation_unref = pa_operation_unref; - ma_pa_operation_get_state_proc _pa_operation_get_state = pa_operation_get_state; - ma_pa_channel_map_init_extend_proc _pa_channel_map_init_extend = pa_channel_map_init_extend; - ma_pa_channel_map_valid_proc _pa_channel_map_valid = pa_channel_map_valid; - ma_pa_channel_map_compatible_proc _pa_channel_map_compatible = pa_channel_map_compatible; - ma_pa_stream_new_proc _pa_stream_new = pa_stream_new; - ma_pa_stream_unref_proc _pa_stream_unref = pa_stream_unref; - ma_pa_stream_connect_playback_proc _pa_stream_connect_playback = pa_stream_connect_playback; - ma_pa_stream_connect_record_proc _pa_stream_connect_record = pa_stream_connect_record; - ma_pa_stream_disconnect_proc _pa_stream_disconnect = pa_stream_disconnect; - ma_pa_stream_get_state_proc _pa_stream_get_state = pa_stream_get_state; - ma_pa_stream_get_sample_spec_proc _pa_stream_get_sample_spec = pa_stream_get_sample_spec; - ma_pa_stream_get_channel_map_proc _pa_stream_get_channel_map = pa_stream_get_channel_map; - ma_pa_stream_get_buffer_attr_proc _pa_stream_get_buffer_attr = pa_stream_get_buffer_attr; - ma_pa_stream_set_buffer_attr_proc _pa_stream_set_buffer_attr = pa_stream_set_buffer_attr; - ma_pa_stream_get_device_name_proc _pa_stream_get_device_name = pa_stream_get_device_name; - ma_pa_stream_set_write_callback_proc _pa_stream_set_write_callback = pa_stream_set_write_callback; - ma_pa_stream_set_read_callback_proc _pa_stream_set_read_callback = pa_stream_set_read_callback; - ma_pa_stream_set_suspended_callback_proc _pa_stream_set_suspended_callback = pa_stream_set_suspended_callback; - ma_pa_stream_set_moved_callback_proc _pa_stream_set_moved_callback = pa_stream_set_moved_callback; - ma_pa_stream_is_suspended_proc _pa_stream_is_suspended = pa_stream_is_suspended; - ma_pa_stream_flush_proc _pa_stream_flush = pa_stream_flush; - ma_pa_stream_drain_proc _pa_stream_drain = pa_stream_drain; - ma_pa_stream_is_corked_proc _pa_stream_is_corked = pa_stream_is_corked; - ma_pa_stream_cork_proc _pa_stream_cork = pa_stream_cork; - ma_pa_stream_trigger_proc _pa_stream_trigger = pa_stream_trigger; - ma_pa_stream_begin_write_proc _pa_stream_begin_write = pa_stream_begin_write; - ma_pa_stream_write_proc _pa_stream_write = pa_stream_write; - ma_pa_stream_peek_proc _pa_stream_peek = pa_stream_peek; - ma_pa_stream_drop_proc _pa_stream_drop = pa_stream_drop; - ma_pa_stream_writable_size_proc _pa_stream_writable_size = pa_stream_writable_size; - ma_pa_stream_readable_size_proc _pa_stream_readable_size = pa_stream_readable_size; - - pContext->pulse.pa_mainloop_new = (ma_proc)_pa_mainloop_new; - pContext->pulse.pa_mainloop_free = (ma_proc)_pa_mainloop_free; - pContext->pulse.pa_mainloop_quit = (ma_proc)_pa_mainloop_quit; - pContext->pulse.pa_mainloop_get_api = (ma_proc)_pa_mainloop_get_api; - pContext->pulse.pa_mainloop_iterate = (ma_proc)_pa_mainloop_iterate; - pContext->pulse.pa_mainloop_wakeup = (ma_proc)_pa_mainloop_wakeup; - pContext->pulse.pa_threaded_mainloop_new = (ma_proc)_pa_threaded_mainloop_new; - pContext->pulse.pa_threaded_mainloop_free = (ma_proc)_pa_threaded_mainloop_free; - pContext->pulse.pa_threaded_mainloop_start = (ma_proc)_pa_threaded_mainloop_start; - pContext->pulse.pa_threaded_mainloop_stop = (ma_proc)_pa_threaded_mainloop_stop; - pContext->pulse.pa_threaded_mainloop_lock = (ma_proc)_pa_threaded_mainloop_lock; - pContext->pulse.pa_threaded_mainloop_unlock = (ma_proc)_pa_threaded_mainloop_unlock; - pContext->pulse.pa_threaded_mainloop_wait = (ma_proc)_pa_threaded_mainloop_wait; - pContext->pulse.pa_threaded_mainloop_signal = (ma_proc)_pa_threaded_mainloop_signal; - pContext->pulse.pa_threaded_mainloop_accept = (ma_proc)_pa_threaded_mainloop_accept; - pContext->pulse.pa_threaded_mainloop_get_retval = (ma_proc)_pa_threaded_mainloop_get_retval; - pContext->pulse.pa_threaded_mainloop_get_api = (ma_proc)_pa_threaded_mainloop_get_api; - pContext->pulse.pa_threaded_mainloop_in_thread = (ma_proc)_pa_threaded_mainloop_in_thread; - pContext->pulse.pa_threaded_mainloop_set_name = (ma_proc)_pa_threaded_mainloop_set_name; - pContext->pulse.pa_context_new = (ma_proc)_pa_context_new; - pContext->pulse.pa_context_unref = (ma_proc)_pa_context_unref; - pContext->pulse.pa_context_connect = (ma_proc)_pa_context_connect; - pContext->pulse.pa_context_disconnect = (ma_proc)_pa_context_disconnect; - pContext->pulse.pa_context_set_state_callback = (ma_proc)_pa_context_set_state_callback; - pContext->pulse.pa_context_get_state = (ma_proc)_pa_context_get_state; - pContext->pulse.pa_context_get_sink_info_list = (ma_proc)_pa_context_get_sink_info_list; - pContext->pulse.pa_context_get_source_info_list = (ma_proc)_pa_context_get_source_info_list; - pContext->pulse.pa_context_get_sink_info_by_name = (ma_proc)_pa_context_get_sink_info_by_name; - pContext->pulse.pa_context_get_source_info_by_name = (ma_proc)_pa_context_get_source_info_by_name; - pContext->pulse.pa_operation_unref = (ma_proc)_pa_operation_unref; - pContext->pulse.pa_operation_get_state = (ma_proc)_pa_operation_get_state; - pContext->pulse.pa_channel_map_init_extend = (ma_proc)_pa_channel_map_init_extend; - pContext->pulse.pa_channel_map_valid = (ma_proc)_pa_channel_map_valid; - pContext->pulse.pa_channel_map_compatible = (ma_proc)_pa_channel_map_compatible; - pContext->pulse.pa_stream_new = (ma_proc)_pa_stream_new; - pContext->pulse.pa_stream_unref = (ma_proc)_pa_stream_unref; - pContext->pulse.pa_stream_connect_playback = (ma_proc)_pa_stream_connect_playback; - pContext->pulse.pa_stream_connect_record = (ma_proc)_pa_stream_connect_record; - pContext->pulse.pa_stream_disconnect = (ma_proc)_pa_stream_disconnect; - pContext->pulse.pa_stream_get_state = (ma_proc)_pa_stream_get_state; - pContext->pulse.pa_stream_get_sample_spec = (ma_proc)_pa_stream_get_sample_spec; - pContext->pulse.pa_stream_get_channel_map = (ma_proc)_pa_stream_get_channel_map; - pContext->pulse.pa_stream_get_buffer_attr = (ma_proc)_pa_stream_get_buffer_attr; - pContext->pulse.pa_stream_set_buffer_attr = (ma_proc)_pa_stream_set_buffer_attr; - pContext->pulse.pa_stream_get_device_name = (ma_proc)_pa_stream_get_device_name; - pContext->pulse.pa_stream_set_write_callback = (ma_proc)_pa_stream_set_write_callback; - pContext->pulse.pa_stream_set_read_callback = (ma_proc)_pa_stream_set_read_callback; - pContext->pulse.pa_stream_set_suspended_callback = (ma_proc)_pa_stream_set_suspended_callback; - pContext->pulse.pa_stream_set_moved_callback = (ma_proc)_pa_stream_set_moved_callback; - pContext->pulse.pa_stream_is_suspended = (ma_proc)_pa_stream_is_suspended; - pContext->pulse.pa_stream_flush = (ma_proc)_pa_stream_flush; - pContext->pulse.pa_stream_drain = (ma_proc)_pa_stream_drain; - pContext->pulse.pa_stream_is_corked = (ma_proc)_pa_stream_is_corked; - pContext->pulse.pa_stream_cork = (ma_proc)_pa_stream_cork; - pContext->pulse.pa_stream_trigger = (ma_proc)_pa_stream_trigger; - pContext->pulse.pa_stream_begin_write = (ma_proc)_pa_stream_begin_write; - pContext->pulse.pa_stream_write = (ma_proc)_pa_stream_write; - pContext->pulse.pa_stream_peek = (ma_proc)_pa_stream_peek; - pContext->pulse.pa_stream_drop = (ma_proc)_pa_stream_drop; - pContext->pulse.pa_stream_writable_size = (ma_proc)_pa_stream_writable_size; - pContext->pulse.pa_stream_readable_size = (ma_proc)_pa_stream_readable_size; -#endif - - /* We need to make a copy of the application and server names so we can pass them to the pa_context of each device. */ - pContext->pulse.pApplicationName = ma_copy_string(pConfig->pulse.pApplicationName, &pContext->allocationCallbacks); - if (pContext->pulse.pApplicationName == NULL && pConfig->pulse.pApplicationName != NULL) { - return MA_OUT_OF_MEMORY; - } - - pContext->pulse.pServerName = ma_copy_string(pConfig->pulse.pServerName, &pContext->allocationCallbacks); - if (pContext->pulse.pServerName == NULL && pConfig->pulse.pServerName != NULL) { - ma_free(pContext->pulse.pApplicationName, &pContext->allocationCallbacks); - return MA_OUT_OF_MEMORY; - } - - result = ma_init_pa_mainloop_and_pa_context__pulse(pContext, pConfig->pulse.pApplicationName, pConfig->pulse.pServerName, pConfig->pulse.tryAutoSpawn, &pContext->pulse.pMainLoop, &pContext->pulse.pPulseContext); - if (result != MA_SUCCESS) { - ma_free(pContext->pulse.pServerName, &pContext->allocationCallbacks); - ma_free(pContext->pulse.pApplicationName, &pContext->allocationCallbacks); - #ifndef MA_NO_RUNTIME_LINKING - ma_dlclose(ma_context_get_log(pContext), pContext->pulse.pulseSO); - #endif - return result; - } - - /* With pa_mainloop we run a synchronous backend, but we implement our own main loop. */ - pCallbacks->onContextInit = ma_context_init__pulse; - pCallbacks->onContextUninit = ma_context_uninit__pulse; - pCallbacks->onContextEnumerateDevices = ma_context_enumerate_devices__pulse; - pCallbacks->onContextGetDeviceInfo = ma_context_get_device_info__pulse; - pCallbacks->onDeviceInit = ma_device_init__pulse; - pCallbacks->onDeviceUninit = ma_device_uninit__pulse; - pCallbacks->onDeviceStart = ma_device_start__pulse; - pCallbacks->onDeviceStop = ma_device_stop__pulse; - pCallbacks->onDeviceRead = NULL; /* Not used because we're implementing onDeviceDataLoop. */ - pCallbacks->onDeviceWrite = NULL; /* Not used because we're implementing onDeviceDataLoop. */ - pCallbacks->onDeviceDataLoop = ma_device_data_loop__pulse; - pCallbacks->onDeviceDataLoopWakeup = ma_device_data_loop_wakeup__pulse; - - return MA_SUCCESS; -} -#endif - - -/****************************************************************************** - -JACK Backend - -******************************************************************************/ -#ifdef MA_HAS_JACK - -/* It is assumed jack.h is available when compile-time linking is being used. */ -#ifdef MA_NO_RUNTIME_LINKING -#include - -typedef jack_nframes_t ma_jack_nframes_t; -typedef jack_options_t ma_jack_options_t; -typedef jack_status_t ma_jack_status_t; -typedef jack_client_t ma_jack_client_t; -typedef jack_port_t ma_jack_port_t; -typedef JackProcessCallback ma_JackProcessCallback; -typedef JackBufferSizeCallback ma_JackBufferSizeCallback; -typedef JackShutdownCallback ma_JackShutdownCallback; -#define MA_JACK_DEFAULT_AUDIO_TYPE JACK_DEFAULT_AUDIO_TYPE -#define ma_JackNoStartServer JackNoStartServer -#define ma_JackPortIsInput JackPortIsInput -#define ma_JackPortIsOutput JackPortIsOutput -#define ma_JackPortIsPhysical JackPortIsPhysical -#else -typedef ma_uint32 ma_jack_nframes_t; -typedef int ma_jack_options_t; -typedef int ma_jack_status_t; -typedef struct ma_jack_client_t ma_jack_client_t; -typedef struct ma_jack_port_t ma_jack_port_t; -typedef int (* ma_JackProcessCallback) (ma_jack_nframes_t nframes, void* arg); -typedef int (* ma_JackBufferSizeCallback)(ma_jack_nframes_t nframes, void* arg); -typedef void (* ma_JackShutdownCallback) (void* arg); -#define MA_JACK_DEFAULT_AUDIO_TYPE "32 bit float mono audio" -#define ma_JackNoStartServer 1 -#define ma_JackPortIsInput 1 -#define ma_JackPortIsOutput 2 -#define ma_JackPortIsPhysical 4 -#endif - -typedef ma_jack_client_t* (* ma_jack_client_open_proc) (const char* client_name, ma_jack_options_t options, ma_jack_status_t* status, ...); -typedef int (* ma_jack_client_close_proc) (ma_jack_client_t* client); -typedef int (* ma_jack_client_name_size_proc) (void); -typedef int (* ma_jack_set_process_callback_proc) (ma_jack_client_t* client, ma_JackProcessCallback process_callback, void* arg); -typedef int (* ma_jack_set_buffer_size_callback_proc)(ma_jack_client_t* client, ma_JackBufferSizeCallback bufsize_callback, void* arg); -typedef void (* ma_jack_on_shutdown_proc) (ma_jack_client_t* client, ma_JackShutdownCallback function, void* arg); -typedef ma_jack_nframes_t (* ma_jack_get_sample_rate_proc) (ma_jack_client_t* client); -typedef ma_jack_nframes_t (* ma_jack_get_buffer_size_proc) (ma_jack_client_t* client); -typedef const char** (* ma_jack_get_ports_proc) (ma_jack_client_t* client, const char* port_name_pattern, const char* type_name_pattern, unsigned long flags); -typedef int (* ma_jack_activate_proc) (ma_jack_client_t* client); -typedef int (* ma_jack_deactivate_proc) (ma_jack_client_t* client); -typedef int (* ma_jack_connect_proc) (ma_jack_client_t* client, const char* source_port, const char* destination_port); -typedef ma_jack_port_t* (* ma_jack_port_register_proc) (ma_jack_client_t* client, const char* port_name, const char* port_type, unsigned long flags, unsigned long buffer_size); -typedef const char* (* ma_jack_port_name_proc) (const ma_jack_port_t* port); -typedef void* (* ma_jack_port_get_buffer_proc) (ma_jack_port_t* port, ma_jack_nframes_t nframes); -typedef void (* ma_jack_free_proc) (void* ptr); - -static ma_result ma_context_open_client__jack(ma_context* pContext, ma_jack_client_t** ppClient) -{ - size_t maxClientNameSize; - char clientName[256]; - ma_jack_status_t status; - ma_jack_client_t* pClient; - - MA_ASSERT(pContext != NULL); - MA_ASSERT(ppClient != NULL); - - if (ppClient) { - *ppClient = NULL; - } - - maxClientNameSize = ((ma_jack_client_name_size_proc)pContext->jack.jack_client_name_size)(); /* Includes null terminator. */ - ma_strncpy_s(clientName, ma_min(sizeof(clientName), maxClientNameSize), (pContext->jack.pClientName != NULL) ? pContext->jack.pClientName : "miniaudio", (size_t)-1); - - pClient = ((ma_jack_client_open_proc)pContext->jack.jack_client_open)(clientName, (pContext->jack.tryStartServer) ? 0 : ma_JackNoStartServer, &status, NULL); - if (pClient == NULL) { - return MA_FAILED_TO_OPEN_BACKEND_DEVICE; - } - - if (ppClient) { - *ppClient = pClient; - } - - return MA_SUCCESS; -} - - -static ma_result ma_context_enumerate_devices__jack(ma_context* pContext, ma_enum_devices_callback_proc callback, void* pUserData) -{ - ma_bool32 cbResult = MA_TRUE; - - MA_ASSERT(pContext != NULL); - MA_ASSERT(callback != NULL); - - /* Playback. */ - if (cbResult) { - ma_device_info deviceInfo; - MA_ZERO_OBJECT(&deviceInfo); - ma_strncpy_s(deviceInfo.name, sizeof(deviceInfo.name), MA_DEFAULT_PLAYBACK_DEVICE_NAME, (size_t)-1); - deviceInfo.isDefault = MA_TRUE; /* JACK only uses default devices. */ - cbResult = callback(pContext, ma_device_type_playback, &deviceInfo, pUserData); - } - - /* Capture. */ - if (cbResult) { - ma_device_info deviceInfo; - MA_ZERO_OBJECT(&deviceInfo); - ma_strncpy_s(deviceInfo.name, sizeof(deviceInfo.name), MA_DEFAULT_CAPTURE_DEVICE_NAME, (size_t)-1); - deviceInfo.isDefault = MA_TRUE; /* JACK only uses default devices. */ - cbResult = callback(pContext, ma_device_type_capture, &deviceInfo, pUserData); - } - - (void)cbResult; /* For silencing a static analysis warning. */ - - return MA_SUCCESS; -} - -static ma_result ma_context_get_device_info__jack(ma_context* pContext, ma_device_type deviceType, const ma_device_id* pDeviceID, ma_device_info* pDeviceInfo) -{ - ma_jack_client_t* pClient; - ma_result result; - const char** ppPorts; - - MA_ASSERT(pContext != NULL); - - if (pDeviceID != NULL && pDeviceID->jack != 0) { - return MA_NO_DEVICE; /* Don't know the device. */ - } - - /* Name / Description */ - if (deviceType == ma_device_type_playback) { - ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), MA_DEFAULT_PLAYBACK_DEVICE_NAME, (size_t)-1); - } else { - ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), MA_DEFAULT_CAPTURE_DEVICE_NAME, (size_t)-1); - } - - /* Jack only uses default devices. */ - pDeviceInfo->isDefault = MA_TRUE; - - /* Jack only supports f32 and has a specific channel count and sample rate. */ - pDeviceInfo->nativeDataFormats[0].format = ma_format_f32; - - /* The channel count and sample rate can only be determined by opening the device. */ - result = ma_context_open_client__jack(pContext, &pClient); - if (result != MA_SUCCESS) { - ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[JACK] Failed to open client."); - return result; - } - - pDeviceInfo->nativeDataFormats[0].sampleRate = ((ma_jack_get_sample_rate_proc)pContext->jack.jack_get_sample_rate)((ma_jack_client_t*)pClient); - pDeviceInfo->nativeDataFormats[0].channels = 0; - - ppPorts = ((ma_jack_get_ports_proc)pContext->jack.jack_get_ports)((ma_jack_client_t*)pClient, NULL, MA_JACK_DEFAULT_AUDIO_TYPE, ma_JackPortIsPhysical | ((deviceType == ma_device_type_playback) ? ma_JackPortIsInput : ma_JackPortIsOutput)); - if (ppPorts == NULL) { - ((ma_jack_client_close_proc)pContext->jack.jack_client_close)((ma_jack_client_t*)pClient); - ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[JACK] Failed to query physical ports."); - return MA_FAILED_TO_OPEN_BACKEND_DEVICE; - } - - while (ppPorts[pDeviceInfo->nativeDataFormats[0].channels] != NULL) { - pDeviceInfo->nativeDataFormats[0].channels += 1; - } - - pDeviceInfo->nativeDataFormats[0].flags = 0; - pDeviceInfo->nativeDataFormatCount = 1; - - ((ma_jack_free_proc)pContext->jack.jack_free)((void*)ppPorts); - ((ma_jack_client_close_proc)pContext->jack.jack_client_close)((ma_jack_client_t*)pClient); - - (void)pContext; - return MA_SUCCESS; -} - - -static ma_result ma_device_uninit__jack(ma_device* pDevice) -{ - ma_context* pContext; - - MA_ASSERT(pDevice != NULL); - - pContext = pDevice->pContext; - MA_ASSERT(pContext != NULL); - - if (pDevice->jack.pClient != NULL) { - ((ma_jack_client_close_proc)pContext->jack.jack_client_close)((ma_jack_client_t*)pDevice->jack.pClient); - } - - if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) { - ma_free(pDevice->jack.pIntermediaryBufferCapture, &pDevice->pContext->allocationCallbacks); - ma_free(pDevice->jack.ppPortsCapture, &pDevice->pContext->allocationCallbacks); - } - - if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) { - ma_free(pDevice->jack.pIntermediaryBufferPlayback, &pDevice->pContext->allocationCallbacks); - ma_free(pDevice->jack.ppPortsPlayback, &pDevice->pContext->allocationCallbacks); - } - - return MA_SUCCESS; -} - -static void ma_device__jack_shutdown_callback(void* pUserData) -{ - /* JACK died. Stop the device. */ - ma_device* pDevice = (ma_device*)pUserData; - MA_ASSERT(pDevice != NULL); - - ma_device_stop(pDevice); -} - -static int ma_device__jack_buffer_size_callback(ma_jack_nframes_t frameCount, void* pUserData) -{ - ma_device* pDevice = (ma_device*)pUserData; - MA_ASSERT(pDevice != NULL); - - if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) { - size_t newBufferSize = frameCount * (pDevice->capture.internalChannels * ma_get_bytes_per_sample(pDevice->capture.internalFormat)); - float* pNewBuffer = (float*)ma_calloc(newBufferSize, &pDevice->pContext->allocationCallbacks); - if (pNewBuffer == NULL) { - return MA_OUT_OF_MEMORY; - } - - ma_free(pDevice->jack.pIntermediaryBufferCapture, &pDevice->pContext->allocationCallbacks); - - pDevice->jack.pIntermediaryBufferCapture = pNewBuffer; - pDevice->playback.internalPeriodSizeInFrames = frameCount; - } - - if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) { - size_t newBufferSize = frameCount * (pDevice->playback.internalChannels * ma_get_bytes_per_sample(pDevice->playback.internalFormat)); - float* pNewBuffer = (float*)ma_calloc(newBufferSize, &pDevice->pContext->allocationCallbacks); - if (pNewBuffer == NULL) { - return MA_OUT_OF_MEMORY; - } - - ma_free(pDevice->jack.pIntermediaryBufferPlayback, &pDevice->pContext->allocationCallbacks); - - pDevice->jack.pIntermediaryBufferPlayback = pNewBuffer; - pDevice->playback.internalPeriodSizeInFrames = frameCount; - } - - return 0; -} - -static int ma_device__jack_process_callback(ma_jack_nframes_t frameCount, void* pUserData) -{ - ma_device* pDevice; - ma_context* pContext; - ma_uint32 iChannel; - - pDevice = (ma_device*)pUserData; - MA_ASSERT(pDevice != NULL); - - pContext = pDevice->pContext; - MA_ASSERT(pContext != NULL); - - if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) { - /* Channels need to be interleaved. */ - for (iChannel = 0; iChannel < pDevice->capture.internalChannels; ++iChannel) { - const float* pSrc = (const float*)((ma_jack_port_get_buffer_proc)pContext->jack.jack_port_get_buffer)((ma_jack_port_t*)pDevice->jack.ppPortsCapture[iChannel], frameCount); - if (pSrc != NULL) { - float* pDst = pDevice->jack.pIntermediaryBufferCapture + iChannel; - ma_jack_nframes_t iFrame; - for (iFrame = 0; iFrame < frameCount; ++iFrame) { - *pDst = *pSrc; - - pDst += pDevice->capture.internalChannels; - pSrc += 1; - } - } - } - - ma_device_handle_backend_data_callback(pDevice, NULL, pDevice->jack.pIntermediaryBufferCapture, frameCount); - } - - if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) { - ma_device_handle_backend_data_callback(pDevice, pDevice->jack.pIntermediaryBufferPlayback, NULL, frameCount); - - /* Channels need to be deinterleaved. */ - for (iChannel = 0; iChannel < pDevice->playback.internalChannels; ++iChannel) { - float* pDst = (float*)((ma_jack_port_get_buffer_proc)pContext->jack.jack_port_get_buffer)((ma_jack_port_t*)pDevice->jack.ppPortsPlayback[iChannel], frameCount); - if (pDst != NULL) { - const float* pSrc = pDevice->jack.pIntermediaryBufferPlayback + iChannel; - ma_jack_nframes_t iFrame; - for (iFrame = 0; iFrame < frameCount; ++iFrame) { - *pDst = *pSrc; - - pDst += 1; - pSrc += pDevice->playback.internalChannels; - } - } - } - } - - return 0; -} - -static ma_result ma_device_init__jack(ma_device* pDevice, const ma_device_config* pConfig, ma_device_descriptor* pDescriptorPlayback, ma_device_descriptor* pDescriptorCapture) -{ - ma_result result; - ma_uint32 periodSizeInFrames; - - MA_ASSERT(pConfig != NULL); - MA_ASSERT(pDevice != NULL); - - if (pConfig->deviceType == ma_device_type_loopback) { - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[JACK] Loopback mode not supported."); - return MA_DEVICE_TYPE_NOT_SUPPORTED; - } - - /* Only supporting default devices with JACK. */ - if (((pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) && pDescriptorPlayback->pDeviceID != NULL && pDescriptorPlayback->pDeviceID->jack != 0) || - ((pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) && pDescriptorCapture->pDeviceID != NULL && pDescriptorCapture->pDeviceID->jack != 0)) { - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[JACK] Only default devices are supported."); - return MA_NO_DEVICE; - } - - /* No exclusive mode with the JACK backend. */ - if (((pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) && pDescriptorPlayback->shareMode == ma_share_mode_exclusive) || - ((pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) && pDescriptorCapture->shareMode == ma_share_mode_exclusive)) { - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[JACK] Exclusive mode not supported."); - return MA_SHARE_MODE_NOT_SUPPORTED; - } - - /* Open the client. */ - result = ma_context_open_client__jack(pDevice->pContext, (ma_jack_client_t**)&pDevice->jack.pClient); - if (result != MA_SUCCESS) { - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[JACK] Failed to open client."); - return result; - } - - /* Callbacks. */ - if (((ma_jack_set_process_callback_proc)pDevice->pContext->jack.jack_set_process_callback)((ma_jack_client_t*)pDevice->jack.pClient, ma_device__jack_process_callback, pDevice) != 0) { - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[JACK] Failed to set process callback."); - return MA_FAILED_TO_OPEN_BACKEND_DEVICE; - } - if (((ma_jack_set_buffer_size_callback_proc)pDevice->pContext->jack.jack_set_buffer_size_callback)((ma_jack_client_t*)pDevice->jack.pClient, ma_device__jack_buffer_size_callback, pDevice) != 0) { - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[JACK] Failed to set buffer size callback."); - return MA_FAILED_TO_OPEN_BACKEND_DEVICE; - } - - ((ma_jack_on_shutdown_proc)pDevice->pContext->jack.jack_on_shutdown)((ma_jack_client_t*)pDevice->jack.pClient, ma_device__jack_shutdown_callback, pDevice); - - - /* The buffer size in frames can change. */ - periodSizeInFrames = ((ma_jack_get_buffer_size_proc)pDevice->pContext->jack.jack_get_buffer_size)((ma_jack_client_t*)pDevice->jack.pClient); - - if (pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) { - ma_uint32 iPort; - const char** ppPorts; - - pDescriptorCapture->format = ma_format_f32; - pDescriptorCapture->channels = 0; - pDescriptorCapture->sampleRate = ((ma_jack_get_sample_rate_proc)pDevice->pContext->jack.jack_get_sample_rate)((ma_jack_client_t*)pDevice->jack.pClient); - ma_channel_map_init_standard(ma_standard_channel_map_alsa, pDescriptorCapture->channelMap, ma_countof(pDescriptorCapture->channelMap), pDescriptorCapture->channels); - - ppPorts = ((ma_jack_get_ports_proc)pDevice->pContext->jack.jack_get_ports)((ma_jack_client_t*)pDevice->jack.pClient, NULL, MA_JACK_DEFAULT_AUDIO_TYPE, ma_JackPortIsPhysical | ma_JackPortIsOutput); - if (ppPorts == NULL) { - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[JACK] Failed to query physical ports."); - return MA_FAILED_TO_OPEN_BACKEND_DEVICE; - } - - /* Need to count the number of ports first so we can allocate some memory. */ - while (ppPorts[pDescriptorCapture->channels] != NULL) { - pDescriptorCapture->channels += 1; - } - - pDevice->jack.ppPortsCapture = (ma_ptr*)ma_malloc(sizeof(*pDevice->jack.ppPortsCapture) * pDescriptorCapture->channels, &pDevice->pContext->allocationCallbacks); - if (pDevice->jack.ppPortsCapture == NULL) { - return MA_OUT_OF_MEMORY; - } - - for (iPort = 0; iPort < pDescriptorCapture->channels; iPort += 1) { - char name[64]; - ma_strcpy_s(name, sizeof(name), "capture"); - ma_itoa_s((int)iPort, name+7, sizeof(name)-7, 10); /* 7 = length of "capture" */ - - pDevice->jack.ppPortsCapture[iPort] = ((ma_jack_port_register_proc)pDevice->pContext->jack.jack_port_register)((ma_jack_client_t*)pDevice->jack.pClient, name, MA_JACK_DEFAULT_AUDIO_TYPE, ma_JackPortIsInput, 0); - if (pDevice->jack.ppPortsCapture[iPort] == NULL) { - ((ma_jack_free_proc)pDevice->pContext->jack.jack_free)((void*)ppPorts); - ma_device_uninit__jack(pDevice); - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[JACK] Failed to register ports."); - return MA_FAILED_TO_OPEN_BACKEND_DEVICE; - } - } - - ((ma_jack_free_proc)pDevice->pContext->jack.jack_free)((void*)ppPorts); - - pDescriptorCapture->periodSizeInFrames = periodSizeInFrames; - pDescriptorCapture->periodCount = 1; /* There's no notion of a period in JACK. Just set to 1. */ - - pDevice->jack.pIntermediaryBufferCapture = (float*)ma_calloc(pDescriptorCapture->periodSizeInFrames * ma_get_bytes_per_frame(pDescriptorCapture->format, pDescriptorCapture->channels), &pDevice->pContext->allocationCallbacks); - if (pDevice->jack.pIntermediaryBufferCapture == NULL) { - ma_device_uninit__jack(pDevice); - return MA_OUT_OF_MEMORY; - } - } - - if (pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) { - ma_uint32 iPort; - const char** ppPorts; - - pDescriptorPlayback->format = ma_format_f32; - pDescriptorPlayback->channels = 0; - pDescriptorPlayback->sampleRate = ((ma_jack_get_sample_rate_proc)pDevice->pContext->jack.jack_get_sample_rate)((ma_jack_client_t*)pDevice->jack.pClient); - ma_channel_map_init_standard(ma_standard_channel_map_alsa, pDescriptorPlayback->channelMap, ma_countof(pDescriptorPlayback->channelMap), pDescriptorPlayback->channels); - - ppPorts = ((ma_jack_get_ports_proc)pDevice->pContext->jack.jack_get_ports)((ma_jack_client_t*)pDevice->jack.pClient, NULL, MA_JACK_DEFAULT_AUDIO_TYPE, ma_JackPortIsPhysical | ma_JackPortIsInput); - if (ppPorts == NULL) { - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[JACK] Failed to query physical ports."); - return MA_FAILED_TO_OPEN_BACKEND_DEVICE; - } - - /* Need to count the number of ports first so we can allocate some memory. */ - while (ppPorts[pDescriptorPlayback->channels] != NULL) { - pDescriptorPlayback->channels += 1; - } - - pDevice->jack.ppPortsPlayback = (ma_ptr*)ma_malloc(sizeof(*pDevice->jack.ppPortsPlayback) * pDescriptorPlayback->channels, &pDevice->pContext->allocationCallbacks); - if (pDevice->jack.ppPortsPlayback == NULL) { - ma_free(pDevice->jack.ppPortsCapture, &pDevice->pContext->allocationCallbacks); - return MA_OUT_OF_MEMORY; - } - - for (iPort = 0; iPort < pDescriptorPlayback->channels; iPort += 1) { - char name[64]; - ma_strcpy_s(name, sizeof(name), "playback"); - ma_itoa_s((int)iPort, name+8, sizeof(name)-8, 10); /* 8 = length of "playback" */ - - pDevice->jack.ppPortsPlayback[iPort] = ((ma_jack_port_register_proc)pDevice->pContext->jack.jack_port_register)((ma_jack_client_t*)pDevice->jack.pClient, name, MA_JACK_DEFAULT_AUDIO_TYPE, ma_JackPortIsOutput, 0); - if (pDevice->jack.ppPortsPlayback[iPort] == NULL) { - ((ma_jack_free_proc)pDevice->pContext->jack.jack_free)((void*)ppPorts); - ma_device_uninit__jack(pDevice); - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[JACK] Failed to register ports."); - return MA_FAILED_TO_OPEN_BACKEND_DEVICE; - } - } - - ((ma_jack_free_proc)pDevice->pContext->jack.jack_free)((void*)ppPorts); - - pDescriptorPlayback->periodSizeInFrames = periodSizeInFrames; - pDescriptorPlayback->periodCount = 1; /* There's no notion of a period in JACK. Just set to 1. */ - - pDevice->jack.pIntermediaryBufferPlayback = (float*)ma_calloc(pDescriptorPlayback->periodSizeInFrames * ma_get_bytes_per_frame(pDescriptorPlayback->format, pDescriptorPlayback->channels), &pDevice->pContext->allocationCallbacks); - if (pDevice->jack.pIntermediaryBufferPlayback == NULL) { - ma_device_uninit__jack(pDevice); - return MA_OUT_OF_MEMORY; - } - } - - return MA_SUCCESS; -} - - -static ma_result ma_device_start__jack(ma_device* pDevice) -{ - ma_context* pContext = pDevice->pContext; - int resultJACK; - size_t i; - - resultJACK = ((ma_jack_activate_proc)pContext->jack.jack_activate)((ma_jack_client_t*)pDevice->jack.pClient); - if (resultJACK != 0) { - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[JACK] Failed to activate the JACK client."); - return MA_FAILED_TO_START_BACKEND_DEVICE; - } - - if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) { - const char** ppServerPorts = ((ma_jack_get_ports_proc)pContext->jack.jack_get_ports)((ma_jack_client_t*)pDevice->jack.pClient, NULL, MA_JACK_DEFAULT_AUDIO_TYPE, ma_JackPortIsPhysical | ma_JackPortIsOutput); - if (ppServerPorts == NULL) { - ((ma_jack_deactivate_proc)pContext->jack.jack_deactivate)((ma_jack_client_t*)pDevice->jack.pClient); - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[JACK] Failed to retrieve physical ports."); - return MA_ERROR; - } - - for (i = 0; ppServerPorts[i] != NULL; ++i) { - const char* pServerPort = ppServerPorts[i]; - const char* pClientPort = ((ma_jack_port_name_proc)pContext->jack.jack_port_name)((ma_jack_port_t*)pDevice->jack.ppPortsCapture[i]); - - resultJACK = ((ma_jack_connect_proc)pContext->jack.jack_connect)((ma_jack_client_t*)pDevice->jack.pClient, pServerPort, pClientPort); - if (resultJACK != 0) { - ((ma_jack_free_proc)pContext->jack.jack_free)((void*)ppServerPorts); - ((ma_jack_deactivate_proc)pContext->jack.jack_deactivate)((ma_jack_client_t*)pDevice->jack.pClient); - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[JACK] Failed to connect ports."); - return MA_ERROR; - } - } - - ((ma_jack_free_proc)pContext->jack.jack_free)((void*)ppServerPorts); - } - - if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) { - const char** ppServerPorts = ((ma_jack_get_ports_proc)pContext->jack.jack_get_ports)((ma_jack_client_t*)pDevice->jack.pClient, NULL, MA_JACK_DEFAULT_AUDIO_TYPE, ma_JackPortIsPhysical | ma_JackPortIsInput); - if (ppServerPorts == NULL) { - ((ma_jack_deactivate_proc)pContext->jack.jack_deactivate)((ma_jack_client_t*)pDevice->jack.pClient); - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[JACK] Failed to retrieve physical ports."); - return MA_ERROR; - } - - for (i = 0; ppServerPorts[i] != NULL; ++i) { - const char* pServerPort = ppServerPorts[i]; - const char* pClientPort = ((ma_jack_port_name_proc)pContext->jack.jack_port_name)((ma_jack_port_t*)pDevice->jack.ppPortsPlayback[i]); - - resultJACK = ((ma_jack_connect_proc)pContext->jack.jack_connect)((ma_jack_client_t*)pDevice->jack.pClient, pClientPort, pServerPort); - if (resultJACK != 0) { - ((ma_jack_free_proc)pContext->jack.jack_free)((void*)ppServerPorts); - ((ma_jack_deactivate_proc)pContext->jack.jack_deactivate)((ma_jack_client_t*)pDevice->jack.pClient); - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[JACK] Failed to connect ports."); - return MA_ERROR; - } - } - - ((ma_jack_free_proc)pContext->jack.jack_free)((void*)ppServerPorts); - } - - return MA_SUCCESS; -} - -static ma_result ma_device_stop__jack(ma_device* pDevice) -{ - ma_context* pContext = pDevice->pContext; - - if (((ma_jack_deactivate_proc)pContext->jack.jack_deactivate)((ma_jack_client_t*)pDevice->jack.pClient) != 0) { - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[JACK] An error occurred when deactivating the JACK client."); - return MA_ERROR; - } - - ma_device__on_notification_stopped(pDevice); - - return MA_SUCCESS; -} - - -static ma_result ma_context_uninit__jack(ma_context* pContext) -{ - MA_ASSERT(pContext != NULL); - MA_ASSERT(pContext->backend == ma_backend_jack); - - ma_free(pContext->jack.pClientName, &pContext->allocationCallbacks); - pContext->jack.pClientName = NULL; - -#ifndef MA_NO_RUNTIME_LINKING - ma_dlclose(ma_context_get_log(pContext), pContext->jack.jackSO); -#endif - - return MA_SUCCESS; -} - -static ma_result ma_context_init__jack(ma_context* pContext, const ma_context_config* pConfig, ma_backend_callbacks* pCallbacks) -{ -#ifndef MA_NO_RUNTIME_LINKING - const char* libjackNames[] = { -#if defined(MA_WIN32) - "libjack.dll", - "libjack64.dll" -#endif -#if defined(MA_UNIX) - "libjack.so", - "libjack.so.0" -#endif - }; - size_t i; - - for (i = 0; i < ma_countof(libjackNames); ++i) { - pContext->jack.jackSO = ma_dlopen(ma_context_get_log(pContext), libjackNames[i]); - if (pContext->jack.jackSO != NULL) { - break; - } - } - - if (pContext->jack.jackSO == NULL) { - return MA_NO_BACKEND; - } - - pContext->jack.jack_client_open = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->jack.jackSO, "jack_client_open"); - pContext->jack.jack_client_close = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->jack.jackSO, "jack_client_close"); - pContext->jack.jack_client_name_size = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->jack.jackSO, "jack_client_name_size"); - pContext->jack.jack_set_process_callback = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->jack.jackSO, "jack_set_process_callback"); - pContext->jack.jack_set_buffer_size_callback = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->jack.jackSO, "jack_set_buffer_size_callback"); - pContext->jack.jack_on_shutdown = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->jack.jackSO, "jack_on_shutdown"); - pContext->jack.jack_get_sample_rate = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->jack.jackSO, "jack_get_sample_rate"); - pContext->jack.jack_get_buffer_size = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->jack.jackSO, "jack_get_buffer_size"); - pContext->jack.jack_get_ports = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->jack.jackSO, "jack_get_ports"); - pContext->jack.jack_activate = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->jack.jackSO, "jack_activate"); - pContext->jack.jack_deactivate = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->jack.jackSO, "jack_deactivate"); - pContext->jack.jack_connect = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->jack.jackSO, "jack_connect"); - pContext->jack.jack_port_register = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->jack.jackSO, "jack_port_register"); - pContext->jack.jack_port_name = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->jack.jackSO, "jack_port_name"); - pContext->jack.jack_port_get_buffer = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->jack.jackSO, "jack_port_get_buffer"); - pContext->jack.jack_free = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->jack.jackSO, "jack_free"); -#else - /* - This strange assignment system is here just to ensure type safety of miniaudio's function pointer - types. If anything differs slightly the compiler should throw a warning. - */ - ma_jack_client_open_proc _jack_client_open = jack_client_open; - ma_jack_client_close_proc _jack_client_close = jack_client_close; - ma_jack_client_name_size_proc _jack_client_name_size = jack_client_name_size; - ma_jack_set_process_callback_proc _jack_set_process_callback = jack_set_process_callback; - ma_jack_set_buffer_size_callback_proc _jack_set_buffer_size_callback = jack_set_buffer_size_callback; - ma_jack_on_shutdown_proc _jack_on_shutdown = jack_on_shutdown; - ma_jack_get_sample_rate_proc _jack_get_sample_rate = jack_get_sample_rate; - ma_jack_get_buffer_size_proc _jack_get_buffer_size = jack_get_buffer_size; - ma_jack_get_ports_proc _jack_get_ports = jack_get_ports; - ma_jack_activate_proc _jack_activate = jack_activate; - ma_jack_deactivate_proc _jack_deactivate = jack_deactivate; - ma_jack_connect_proc _jack_connect = jack_connect; - ma_jack_port_register_proc _jack_port_register = jack_port_register; - ma_jack_port_name_proc _jack_port_name = jack_port_name; - ma_jack_port_get_buffer_proc _jack_port_get_buffer = jack_port_get_buffer; - ma_jack_free_proc _jack_free = jack_free; - - pContext->jack.jack_client_open = (ma_proc)_jack_client_open; - pContext->jack.jack_client_close = (ma_proc)_jack_client_close; - pContext->jack.jack_client_name_size = (ma_proc)_jack_client_name_size; - pContext->jack.jack_set_process_callback = (ma_proc)_jack_set_process_callback; - pContext->jack.jack_set_buffer_size_callback = (ma_proc)_jack_set_buffer_size_callback; - pContext->jack.jack_on_shutdown = (ma_proc)_jack_on_shutdown; - pContext->jack.jack_get_sample_rate = (ma_proc)_jack_get_sample_rate; - pContext->jack.jack_get_buffer_size = (ma_proc)_jack_get_buffer_size; - pContext->jack.jack_get_ports = (ma_proc)_jack_get_ports; - pContext->jack.jack_activate = (ma_proc)_jack_activate; - pContext->jack.jack_deactivate = (ma_proc)_jack_deactivate; - pContext->jack.jack_connect = (ma_proc)_jack_connect; - pContext->jack.jack_port_register = (ma_proc)_jack_port_register; - pContext->jack.jack_port_name = (ma_proc)_jack_port_name; - pContext->jack.jack_port_get_buffer = (ma_proc)_jack_port_get_buffer; - pContext->jack.jack_free = (ma_proc)_jack_free; -#endif - - if (pConfig->jack.pClientName != NULL) { - pContext->jack.pClientName = ma_copy_string(pConfig->jack.pClientName, &pContext->allocationCallbacks); - } - pContext->jack.tryStartServer = pConfig->jack.tryStartServer; - - /* - Getting here means the JACK library is installed, but it doesn't necessarily mean it's usable. We need to quickly test this by connecting - a temporary client. - */ - { - ma_jack_client_t* pDummyClient; - ma_result result = ma_context_open_client__jack(pContext, &pDummyClient); - if (result != MA_SUCCESS) { - ma_free(pContext->jack.pClientName, &pContext->allocationCallbacks); - #ifndef MA_NO_RUNTIME_LINKING - ma_dlclose(ma_context_get_log(pContext), pContext->jack.jackSO); - #endif - return MA_NO_BACKEND; - } - - ((ma_jack_client_close_proc)pContext->jack.jack_client_close)((ma_jack_client_t*)pDummyClient); - } - - - pCallbacks->onContextInit = ma_context_init__jack; - pCallbacks->onContextUninit = ma_context_uninit__jack; - pCallbacks->onContextEnumerateDevices = ma_context_enumerate_devices__jack; - pCallbacks->onContextGetDeviceInfo = ma_context_get_device_info__jack; - pCallbacks->onDeviceInit = ma_device_init__jack; - pCallbacks->onDeviceUninit = ma_device_uninit__jack; - pCallbacks->onDeviceStart = ma_device_start__jack; - pCallbacks->onDeviceStop = ma_device_stop__jack; - pCallbacks->onDeviceRead = NULL; /* Not used because JACK is asynchronous. */ - pCallbacks->onDeviceWrite = NULL; /* Not used because JACK is asynchronous. */ - pCallbacks->onDeviceDataLoop = NULL; /* Not used because JACK is asynchronous. */ - - return MA_SUCCESS; -} -#endif /* MA_HAS_JACK */ - - - -/****************************************************************************** - -Core Audio Backend - -References -========== -- Technical Note TN2091: Device input using the HAL Output Audio Unit - https://developer.apple.com/library/archive/technotes/tn2091/_index.html - -******************************************************************************/ -#ifdef MA_HAS_COREAUDIO -#include - -#if defined(TARGET_OS_IPHONE) && TARGET_OS_IPHONE == 1 - #define MA_APPLE_MOBILE - #if defined(TARGET_OS_TV) && TARGET_OS_TV == 1 - #define MA_APPLE_TV - #endif - #if defined(TARGET_OS_WATCH) && TARGET_OS_WATCH == 1 - #define MA_APPLE_WATCH - #endif - #if __has_feature(objc_arc) - #define MA_BRIDGE_TRANSFER __bridge_transfer - #define MA_BRIDGE_RETAINED __bridge_retained - #else - #define MA_BRIDGE_TRANSFER - #define MA_BRIDGE_RETAINED - #endif -#else - #define MA_APPLE_DESKTOP -#endif - -#if defined(MA_APPLE_DESKTOP) -#include -#else -#include -#endif - -#include - -/* CoreFoundation */ -typedef Boolean (* ma_CFStringGetCString_proc)(CFStringRef theString, char* buffer, CFIndex bufferSize, CFStringEncoding encoding); -typedef void (* ma_CFRelease_proc)(CFTypeRef cf); - -/* CoreAudio */ -#if defined(MA_APPLE_DESKTOP) -typedef OSStatus (* ma_AudioObjectGetPropertyData_proc)(AudioObjectID inObjectID, const AudioObjectPropertyAddress* inAddress, UInt32 inQualifierDataSize, const void* inQualifierData, UInt32* ioDataSize, void* outData); -typedef OSStatus (* ma_AudioObjectGetPropertyDataSize_proc)(AudioObjectID inObjectID, const AudioObjectPropertyAddress* inAddress, UInt32 inQualifierDataSize, const void* inQualifierData, UInt32* outDataSize); -typedef OSStatus (* ma_AudioObjectSetPropertyData_proc)(AudioObjectID inObjectID, const AudioObjectPropertyAddress* inAddress, UInt32 inQualifierDataSize, const void* inQualifierData, UInt32 inDataSize, const void* inData); -typedef OSStatus (* ma_AudioObjectAddPropertyListener_proc)(AudioObjectID inObjectID, const AudioObjectPropertyAddress* inAddress, AudioObjectPropertyListenerProc inListener, void* inClientData); -typedef OSStatus (* ma_AudioObjectRemovePropertyListener_proc)(AudioObjectID inObjectID, const AudioObjectPropertyAddress* inAddress, AudioObjectPropertyListenerProc inListener, void* inClientData); -#endif - -/* AudioToolbox */ -typedef AudioComponent (* ma_AudioComponentFindNext_proc)(AudioComponent inComponent, const AudioComponentDescription* inDesc); -typedef OSStatus (* ma_AudioComponentInstanceDispose_proc)(AudioComponentInstance inInstance); -typedef OSStatus (* ma_AudioComponentInstanceNew_proc)(AudioComponent inComponent, AudioComponentInstance* outInstance); -typedef OSStatus (* ma_AudioOutputUnitStart_proc)(AudioUnit inUnit); -typedef OSStatus (* ma_AudioOutputUnitStop_proc)(AudioUnit inUnit); -typedef OSStatus (* ma_AudioUnitAddPropertyListener_proc)(AudioUnit inUnit, AudioUnitPropertyID inID, AudioUnitPropertyListenerProc inProc, void* inProcUserData); -typedef OSStatus (* ma_AudioUnitGetPropertyInfo_proc)(AudioUnit inUnit, AudioUnitPropertyID inID, AudioUnitScope inScope, AudioUnitElement inElement, UInt32* outDataSize, Boolean* outWriteable); -typedef OSStatus (* ma_AudioUnitGetProperty_proc)(AudioUnit inUnit, AudioUnitPropertyID inID, AudioUnitScope inScope, AudioUnitElement inElement, void* outData, UInt32* ioDataSize); -typedef OSStatus (* ma_AudioUnitSetProperty_proc)(AudioUnit inUnit, AudioUnitPropertyID inID, AudioUnitScope inScope, AudioUnitElement inElement, const void* inData, UInt32 inDataSize); -typedef OSStatus (* ma_AudioUnitInitialize_proc)(AudioUnit inUnit); -typedef OSStatus (* ma_AudioUnitRender_proc)(AudioUnit inUnit, AudioUnitRenderActionFlags* ioActionFlags, const AudioTimeStamp* inTimeStamp, UInt32 inOutputBusNumber, UInt32 inNumberFrames, AudioBufferList* ioData); - - -#define MA_COREAUDIO_OUTPUT_BUS 0 -#define MA_COREAUDIO_INPUT_BUS 1 - -#if defined(MA_APPLE_DESKTOP) -static ma_result ma_device_reinit_internal__coreaudio(ma_device* pDevice, ma_device_type deviceType, ma_bool32 disposePreviousAudioUnit); -#endif - -/* -Core Audio - -So far, Core Audio has been the worst backend to work with due to being both unintuitive and having almost no documentation -apart from comments in the headers (which admittedly are quite good). For my own purposes, and for anybody out there whose -needing to figure out how this darn thing works, I'm going to outline a few things here. - -Since miniaudio is a fairly low-level API, one of the things it needs is control over specific devices, and it needs to be -able to identify whether or not it can be used as playback and/or capture. The AudioObject API is the only one I've seen -that supports this level of detail. There was some public domain sample code I stumbled across that used the AudioComponent -and AudioUnit APIs, but I couldn't see anything that gave low-level control over device selection and capabilities (the -distinction between playback and capture in particular). Therefore, miniaudio is using the AudioObject API. - -Most (all?) functions in the AudioObject API take a AudioObjectID as its input. This is the device identifier. When -retrieving global information, such as the device list, you use kAudioObjectSystemObject. When retrieving device-specific -data, you pass in the ID for that device. In order to retrieve device-specific IDs you need to enumerate over each of the -devices. This is done using the AudioObjectGetPropertyDataSize() and AudioObjectGetPropertyData() APIs which seem to be -the central APIs for retrieving information about the system and specific devices. - -To use the AudioObjectGetPropertyData() API you need to use the notion of a property address. A property address is a -structure with three variables and is used to identify which property you are getting or setting. The first is the "selector" -which is basically the specific property that you're wanting to retrieve or set. The second is the "scope", which is -typically set to kAudioObjectPropertyScopeGlobal, kAudioObjectPropertyScopeInput for input-specific properties and -kAudioObjectPropertyScopeOutput for output-specific properties. The last is the "element" which is always set to -kAudioObjectPropertyElementMain in miniaudio's case. I don't know of any cases where this would be set to anything different. - -Back to the earlier issue of device retrieval, you first use the AudioObjectGetPropertyDataSize() API to retrieve the size -of the raw data which is just a list of AudioDeviceID's. You use the kAudioObjectSystemObject AudioObjectID, and a property -address with the kAudioHardwarePropertyDevices selector and the kAudioObjectPropertyScopeGlobal scope. Once you have the -size, allocate a block of memory of that size and then call AudioObjectGetPropertyData(). The data is just a list of -AudioDeviceID's so just do "dataSize/sizeof(AudioDeviceID)" to know the device count. -*/ - -#if defined(MA_APPLE_MOBILE) -static void ma_device__on_notification_interruption_began(ma_device* pDevice) -{ - ma_device__on_notification(ma_device_notification_init(pDevice, ma_device_notification_type_interruption_began)); -} - -static void ma_device__on_notification_interruption_ended(ma_device* pDevice) -{ - ma_device__on_notification(ma_device_notification_init(pDevice, ma_device_notification_type_interruption_ended)); -} -#endif - -static ma_result ma_result_from_OSStatus(OSStatus status) -{ - switch (status) - { - case noErr: return MA_SUCCESS; - #if defined(MA_APPLE_DESKTOP) - case kAudioHardwareNotRunningError: return MA_DEVICE_NOT_STARTED; - case kAudioHardwareUnspecifiedError: return MA_ERROR; - case kAudioHardwareUnknownPropertyError: return MA_INVALID_ARGS; - case kAudioHardwareBadPropertySizeError: return MA_INVALID_OPERATION; - case kAudioHardwareIllegalOperationError: return MA_INVALID_OPERATION; - case kAudioHardwareBadObjectError: return MA_INVALID_ARGS; - case kAudioHardwareBadDeviceError: return MA_INVALID_ARGS; - case kAudioHardwareBadStreamError: return MA_INVALID_ARGS; - case kAudioHardwareUnsupportedOperationError: return MA_INVALID_OPERATION; - case kAudioDeviceUnsupportedFormatError: return MA_FORMAT_NOT_SUPPORTED; - case kAudioDevicePermissionsError: return MA_ACCESS_DENIED; - #endif - default: return MA_ERROR; - } -} - -#if 0 -static ma_channel ma_channel_from_AudioChannelBitmap(AudioChannelBitmap bit) -{ - switch (bit) - { - case kAudioChannelBit_Left: return MA_CHANNEL_LEFT; - case kAudioChannelBit_Right: return MA_CHANNEL_RIGHT; - case kAudioChannelBit_Center: return MA_CHANNEL_FRONT_CENTER; - case kAudioChannelBit_LFEScreen: return MA_CHANNEL_LFE; - case kAudioChannelBit_LeftSurround: return MA_CHANNEL_BACK_LEFT; - case kAudioChannelBit_RightSurround: return MA_CHANNEL_BACK_RIGHT; - case kAudioChannelBit_LeftCenter: return MA_CHANNEL_FRONT_LEFT_CENTER; - case kAudioChannelBit_RightCenter: return MA_CHANNEL_FRONT_RIGHT_CENTER; - case kAudioChannelBit_CenterSurround: return MA_CHANNEL_BACK_CENTER; - case kAudioChannelBit_LeftSurroundDirect: return MA_CHANNEL_SIDE_LEFT; - case kAudioChannelBit_RightSurroundDirect: return MA_CHANNEL_SIDE_RIGHT; - case kAudioChannelBit_TopCenterSurround: return MA_CHANNEL_TOP_CENTER; - case kAudioChannelBit_VerticalHeightLeft: return MA_CHANNEL_TOP_FRONT_LEFT; - case kAudioChannelBit_VerticalHeightCenter: return MA_CHANNEL_TOP_FRONT_CENTER; - case kAudioChannelBit_VerticalHeightRight: return MA_CHANNEL_TOP_FRONT_RIGHT; - case kAudioChannelBit_TopBackLeft: return MA_CHANNEL_TOP_BACK_LEFT; - case kAudioChannelBit_TopBackCenter: return MA_CHANNEL_TOP_BACK_CENTER; - case kAudioChannelBit_TopBackRight: return MA_CHANNEL_TOP_BACK_RIGHT; - default: return MA_CHANNEL_NONE; - } -} -#endif - -static ma_result ma_format_from_AudioStreamBasicDescription(const AudioStreamBasicDescription* pDescription, ma_format* pFormatOut) -{ - MA_ASSERT(pDescription != NULL); - MA_ASSERT(pFormatOut != NULL); - - *pFormatOut = ma_format_unknown; /* Safety. */ - - /* There's a few things miniaudio doesn't support. */ - if (pDescription->mFormatID != kAudioFormatLinearPCM) { - return MA_FORMAT_NOT_SUPPORTED; - } - - /* We don't support any non-packed formats that are aligned high. */ - if ((pDescription->mFormatFlags & kLinearPCMFormatFlagIsAlignedHigh) != 0) { - return MA_FORMAT_NOT_SUPPORTED; - } - - /* Only supporting native-endian. */ - if ((ma_is_little_endian() && (pDescription->mFormatFlags & kAudioFormatFlagIsBigEndian) != 0) || (ma_is_big_endian() && (pDescription->mFormatFlags & kAudioFormatFlagIsBigEndian) == 0)) { - return MA_FORMAT_NOT_SUPPORTED; - } - - /* We are not currently supporting non-interleaved formats (this will be added in a future version of miniaudio). */ - /*if ((pDescription->mFormatFlags & kAudioFormatFlagIsNonInterleaved) != 0) { - return MA_FORMAT_NOT_SUPPORTED; - }*/ - - if ((pDescription->mFormatFlags & kLinearPCMFormatFlagIsFloat) != 0) { - if (pDescription->mBitsPerChannel == 32) { - *pFormatOut = ma_format_f32; - return MA_SUCCESS; - } - } else { - if ((pDescription->mFormatFlags & kLinearPCMFormatFlagIsSignedInteger) != 0) { - if (pDescription->mBitsPerChannel == 16) { - *pFormatOut = ma_format_s16; - return MA_SUCCESS; - } else if (pDescription->mBitsPerChannel == 24) { - if (pDescription->mBytesPerFrame == (pDescription->mBitsPerChannel/8 * pDescription->mChannelsPerFrame)) { - *pFormatOut = ma_format_s24; - return MA_SUCCESS; - } else { - if (pDescription->mBytesPerFrame/pDescription->mChannelsPerFrame == sizeof(ma_int32)) { - /* TODO: Implement ma_format_s24_32. */ - /**pFormatOut = ma_format_s24_32;*/ - /*return MA_SUCCESS;*/ - return MA_FORMAT_NOT_SUPPORTED; - } - } - } else if (pDescription->mBitsPerChannel == 32) { - *pFormatOut = ma_format_s32; - return MA_SUCCESS; - } - } else { - if (pDescription->mBitsPerChannel == 8) { - *pFormatOut = ma_format_u8; - return MA_SUCCESS; - } - } - } - - /* Getting here means the format is not supported. */ - return MA_FORMAT_NOT_SUPPORTED; -} - -#if defined(MA_APPLE_DESKTOP) -static ma_channel ma_channel_from_AudioChannelLabel(AudioChannelLabel label) -{ - switch (label) - { - case kAudioChannelLabel_Unknown: return MA_CHANNEL_NONE; - case kAudioChannelLabel_Unused: return MA_CHANNEL_NONE; - case kAudioChannelLabel_UseCoordinates: return MA_CHANNEL_NONE; - case kAudioChannelLabel_Left: return MA_CHANNEL_LEFT; - case kAudioChannelLabel_Right: return MA_CHANNEL_RIGHT; - case kAudioChannelLabel_Center: return MA_CHANNEL_FRONT_CENTER; - case kAudioChannelLabel_LFEScreen: return MA_CHANNEL_LFE; - case kAudioChannelLabel_LeftSurround: return MA_CHANNEL_BACK_LEFT; - case kAudioChannelLabel_RightSurround: return MA_CHANNEL_BACK_RIGHT; - case kAudioChannelLabel_LeftCenter: return MA_CHANNEL_FRONT_LEFT_CENTER; - case kAudioChannelLabel_RightCenter: return MA_CHANNEL_FRONT_RIGHT_CENTER; - case kAudioChannelLabel_CenterSurround: return MA_CHANNEL_BACK_CENTER; - case kAudioChannelLabel_LeftSurroundDirect: return MA_CHANNEL_SIDE_LEFT; - case kAudioChannelLabel_RightSurroundDirect: return MA_CHANNEL_SIDE_RIGHT; - case kAudioChannelLabel_TopCenterSurround: return MA_CHANNEL_TOP_CENTER; - case kAudioChannelLabel_VerticalHeightLeft: return MA_CHANNEL_TOP_FRONT_LEFT; - case kAudioChannelLabel_VerticalHeightCenter: return MA_CHANNEL_TOP_FRONT_CENTER; - case kAudioChannelLabel_VerticalHeightRight: return MA_CHANNEL_TOP_FRONT_RIGHT; - case kAudioChannelLabel_TopBackLeft: return MA_CHANNEL_TOP_BACK_LEFT; - case kAudioChannelLabel_TopBackCenter: return MA_CHANNEL_TOP_BACK_CENTER; - case kAudioChannelLabel_TopBackRight: return MA_CHANNEL_TOP_BACK_RIGHT; - case kAudioChannelLabel_RearSurroundLeft: return MA_CHANNEL_BACK_LEFT; - case kAudioChannelLabel_RearSurroundRight: return MA_CHANNEL_BACK_RIGHT; - case kAudioChannelLabel_LeftWide: return MA_CHANNEL_SIDE_LEFT; - case kAudioChannelLabel_RightWide: return MA_CHANNEL_SIDE_RIGHT; - case kAudioChannelLabel_LFE2: return MA_CHANNEL_LFE; - case kAudioChannelLabel_LeftTotal: return MA_CHANNEL_LEFT; - case kAudioChannelLabel_RightTotal: return MA_CHANNEL_RIGHT; - case kAudioChannelLabel_HearingImpaired: return MA_CHANNEL_NONE; - case kAudioChannelLabel_Narration: return MA_CHANNEL_MONO; - case kAudioChannelLabel_Mono: return MA_CHANNEL_MONO; - case kAudioChannelLabel_DialogCentricMix: return MA_CHANNEL_MONO; - case kAudioChannelLabel_CenterSurroundDirect: return MA_CHANNEL_BACK_CENTER; - case kAudioChannelLabel_Haptic: return MA_CHANNEL_NONE; - case kAudioChannelLabel_Ambisonic_W: return MA_CHANNEL_NONE; - case kAudioChannelLabel_Ambisonic_X: return MA_CHANNEL_NONE; - case kAudioChannelLabel_Ambisonic_Y: return MA_CHANNEL_NONE; - case kAudioChannelLabel_Ambisonic_Z: return MA_CHANNEL_NONE; - case kAudioChannelLabel_MS_Mid: return MA_CHANNEL_LEFT; - case kAudioChannelLabel_MS_Side: return MA_CHANNEL_RIGHT; - case kAudioChannelLabel_XY_X: return MA_CHANNEL_LEFT; - case kAudioChannelLabel_XY_Y: return MA_CHANNEL_RIGHT; - case kAudioChannelLabel_HeadphonesLeft: return MA_CHANNEL_LEFT; - case kAudioChannelLabel_HeadphonesRight: return MA_CHANNEL_RIGHT; - case kAudioChannelLabel_ClickTrack: return MA_CHANNEL_NONE; - case kAudioChannelLabel_ForeignLanguage: return MA_CHANNEL_NONE; - case kAudioChannelLabel_Discrete: return MA_CHANNEL_NONE; - case kAudioChannelLabel_Discrete_0: return MA_CHANNEL_AUX_0; - case kAudioChannelLabel_Discrete_1: return MA_CHANNEL_AUX_1; - case kAudioChannelLabel_Discrete_2: return MA_CHANNEL_AUX_2; - case kAudioChannelLabel_Discrete_3: return MA_CHANNEL_AUX_3; - case kAudioChannelLabel_Discrete_4: return MA_CHANNEL_AUX_4; - case kAudioChannelLabel_Discrete_5: return MA_CHANNEL_AUX_5; - case kAudioChannelLabel_Discrete_6: return MA_CHANNEL_AUX_6; - case kAudioChannelLabel_Discrete_7: return MA_CHANNEL_AUX_7; - case kAudioChannelLabel_Discrete_8: return MA_CHANNEL_AUX_8; - case kAudioChannelLabel_Discrete_9: return MA_CHANNEL_AUX_9; - case kAudioChannelLabel_Discrete_10: return MA_CHANNEL_AUX_10; - case kAudioChannelLabel_Discrete_11: return MA_CHANNEL_AUX_11; - case kAudioChannelLabel_Discrete_12: return MA_CHANNEL_AUX_12; - case kAudioChannelLabel_Discrete_13: return MA_CHANNEL_AUX_13; - case kAudioChannelLabel_Discrete_14: return MA_CHANNEL_AUX_14; - case kAudioChannelLabel_Discrete_15: return MA_CHANNEL_AUX_15; - case kAudioChannelLabel_Discrete_65535: return MA_CHANNEL_NONE; - - #if 0 /* Introduced in a later version of macOS. */ - case kAudioChannelLabel_HOA_ACN: return MA_CHANNEL_NONE; - case kAudioChannelLabel_HOA_ACN_0: return MA_CHANNEL_AUX_0; - case kAudioChannelLabel_HOA_ACN_1: return MA_CHANNEL_AUX_1; - case kAudioChannelLabel_HOA_ACN_2: return MA_CHANNEL_AUX_2; - case kAudioChannelLabel_HOA_ACN_3: return MA_CHANNEL_AUX_3; - case kAudioChannelLabel_HOA_ACN_4: return MA_CHANNEL_AUX_4; - case kAudioChannelLabel_HOA_ACN_5: return MA_CHANNEL_AUX_5; - case kAudioChannelLabel_HOA_ACN_6: return MA_CHANNEL_AUX_6; - case kAudioChannelLabel_HOA_ACN_7: return MA_CHANNEL_AUX_7; - case kAudioChannelLabel_HOA_ACN_8: return MA_CHANNEL_AUX_8; - case kAudioChannelLabel_HOA_ACN_9: return MA_CHANNEL_AUX_9; - case kAudioChannelLabel_HOA_ACN_10: return MA_CHANNEL_AUX_10; - case kAudioChannelLabel_HOA_ACN_11: return MA_CHANNEL_AUX_11; - case kAudioChannelLabel_HOA_ACN_12: return MA_CHANNEL_AUX_12; - case kAudioChannelLabel_HOA_ACN_13: return MA_CHANNEL_AUX_13; - case kAudioChannelLabel_HOA_ACN_14: return MA_CHANNEL_AUX_14; - case kAudioChannelLabel_HOA_ACN_15: return MA_CHANNEL_AUX_15; - case kAudioChannelLabel_HOA_ACN_65024: return MA_CHANNEL_NONE; - #endif - - default: return MA_CHANNEL_NONE; - } -} - -static ma_result ma_get_channel_map_from_AudioChannelLayout(AudioChannelLayout* pChannelLayout, ma_channel* pChannelMap, size_t channelMapCap) -{ - MA_ASSERT(pChannelLayout != NULL); - - if (pChannelLayout->mChannelLayoutTag == kAudioChannelLayoutTag_UseChannelDescriptions) { - UInt32 iChannel; - for (iChannel = 0; iChannel < pChannelLayout->mNumberChannelDescriptions && iChannel < channelMapCap; ++iChannel) { - pChannelMap[iChannel] = ma_channel_from_AudioChannelLabel(pChannelLayout->mChannelDescriptions[iChannel].mChannelLabel); - } - } else -#if 0 - if (pChannelLayout->mChannelLayoutTag == kAudioChannelLayoutTag_UseChannelBitmap) { - /* This is the same kind of system that's used by Windows audio APIs. */ - UInt32 iChannel = 0; - UInt32 iBit; - AudioChannelBitmap bitmap = pChannelLayout->mChannelBitmap; - for (iBit = 0; iBit < 32 && iChannel < channelMapCap; ++iBit) { - AudioChannelBitmap bit = bitmap & (1 << iBit); - if (bit != 0) { - pChannelMap[iChannel++] = ma_channel_from_AudioChannelBit(bit); - } - } - } else -#endif - { - /* - Need to use the tag to determine the channel map. For now I'm just assuming a default channel map, but later on this should - be updated to determine the mapping based on the tag. - */ - UInt32 channelCount; - - /* Our channel map retrieval APIs below take 32-bit integers, so we'll want to clamp the channel map capacity. */ - if (channelMapCap > 0xFFFFFFFF) { - channelMapCap = 0xFFFFFFFF; - } - - channelCount = ma_min(AudioChannelLayoutTag_GetNumberOfChannels(pChannelLayout->mChannelLayoutTag), (UInt32)channelMapCap); - - switch (pChannelLayout->mChannelLayoutTag) - { - case kAudioChannelLayoutTag_Mono: - case kAudioChannelLayoutTag_Stereo: - case kAudioChannelLayoutTag_StereoHeadphones: - case kAudioChannelLayoutTag_MatrixStereo: - case kAudioChannelLayoutTag_MidSide: - case kAudioChannelLayoutTag_XY: - case kAudioChannelLayoutTag_Binaural: - case kAudioChannelLayoutTag_Ambisonic_B_Format: - { - ma_channel_map_init_standard(ma_standard_channel_map_default, pChannelMap, channelMapCap, channelCount); - } break; - - case kAudioChannelLayoutTag_Octagonal: - { - pChannelMap[7] = MA_CHANNEL_SIDE_RIGHT; - pChannelMap[6] = MA_CHANNEL_SIDE_LEFT; - } MA_FALLTHROUGH; /* Intentional fallthrough. */ - case kAudioChannelLayoutTag_Hexagonal: - { - pChannelMap[5] = MA_CHANNEL_BACK_CENTER; - } MA_FALLTHROUGH; /* Intentional fallthrough. */ - case kAudioChannelLayoutTag_Pentagonal: - { - pChannelMap[4] = MA_CHANNEL_FRONT_CENTER; - } MA_FALLTHROUGH; /* Intentional fallthrough. */ - case kAudioChannelLayoutTag_Quadraphonic: - { - pChannelMap[3] = MA_CHANNEL_BACK_RIGHT; - pChannelMap[2] = MA_CHANNEL_BACK_LEFT; - pChannelMap[1] = MA_CHANNEL_RIGHT; - pChannelMap[0] = MA_CHANNEL_LEFT; - } break; - - /* TODO: Add support for more tags here. */ - - default: - { - ma_channel_map_init_standard(ma_standard_channel_map_default, pChannelMap, channelMapCap, channelCount); - } break; - } - } - - return MA_SUCCESS; -} - -#if (defined(MAC_OS_VERSION_12_0) && MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_VERSION_12_0) || \ - (defined(__IPHONE_15_0) && __IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_15_0) -#define AUDIO_OBJECT_PROPERTY_ELEMENT kAudioObjectPropertyElementMain -#else -/* kAudioObjectPropertyElementMaster is deprecated. */ -#define AUDIO_OBJECT_PROPERTY_ELEMENT kAudioObjectPropertyElementMaster -#endif - -/* kAudioDevicePropertyScope* were renamed to kAudioObjectPropertyScope* in 10.8. */ -#if !defined(MAC_OS_X_VERSION_10_8) || (MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_8) -#define kAudioObjectPropertyScopeInput kAudioDevicePropertyScopeInput -#define kAudioObjectPropertyScopeOutput kAudioDevicePropertyScopeOutput -#endif - -static ma_result ma_get_device_object_ids__coreaudio(ma_context* pContext, UInt32* pDeviceCount, AudioObjectID** ppDeviceObjectIDs) /* NOTE: Free the returned buffer with ma_free(). */ -{ - AudioObjectPropertyAddress propAddressDevices; - UInt32 deviceObjectsDataSize; - OSStatus status; - AudioObjectID* pDeviceObjectIDs; - - MA_ASSERT(pContext != NULL); - MA_ASSERT(pDeviceCount != NULL); - MA_ASSERT(ppDeviceObjectIDs != NULL); - - /* Safety. */ - *pDeviceCount = 0; - *ppDeviceObjectIDs = NULL; - - propAddressDevices.mSelector = kAudioHardwarePropertyDevices; - propAddressDevices.mScope = kAudioObjectPropertyScopeGlobal; - propAddressDevices.mElement = AUDIO_OBJECT_PROPERTY_ELEMENT; - - status = ((ma_AudioObjectGetPropertyDataSize_proc)pContext->coreaudio.AudioObjectGetPropertyDataSize)(kAudioObjectSystemObject, &propAddressDevices, 0, NULL, &deviceObjectsDataSize); - if (status != noErr) { - return ma_result_from_OSStatus(status); - } - - pDeviceObjectIDs = (AudioObjectID*)ma_malloc(deviceObjectsDataSize, &pContext->allocationCallbacks); - if (pDeviceObjectIDs == NULL) { - return MA_OUT_OF_MEMORY; - } - - status = ((ma_AudioObjectGetPropertyData_proc)pContext->coreaudio.AudioObjectGetPropertyData)(kAudioObjectSystemObject, &propAddressDevices, 0, NULL, &deviceObjectsDataSize, pDeviceObjectIDs); - if (status != noErr) { - ma_free(pDeviceObjectIDs, &pContext->allocationCallbacks); - return ma_result_from_OSStatus(status); - } - - *pDeviceCount = deviceObjectsDataSize / sizeof(AudioObjectID); - *ppDeviceObjectIDs = pDeviceObjectIDs; - - return MA_SUCCESS; -} - -static ma_result ma_get_AudioObject_uid_as_CFStringRef(ma_context* pContext, AudioObjectID objectID, CFStringRef* pUID) -{ - AudioObjectPropertyAddress propAddress; - UInt32 dataSize; - OSStatus status; - - MA_ASSERT(pContext != NULL); - - propAddress.mSelector = kAudioDevicePropertyDeviceUID; - propAddress.mScope = kAudioObjectPropertyScopeGlobal; - propAddress.mElement = AUDIO_OBJECT_PROPERTY_ELEMENT; - - dataSize = sizeof(*pUID); - status = ((ma_AudioObjectGetPropertyData_proc)pContext->coreaudio.AudioObjectGetPropertyData)(objectID, &propAddress, 0, NULL, &dataSize, pUID); - if (status != noErr) { - return ma_result_from_OSStatus(status); - } - - return MA_SUCCESS; -} - -static ma_result ma_get_AudioObject_uid(ma_context* pContext, AudioObjectID objectID, size_t bufferSize, char* bufferOut) -{ - CFStringRef uid; - ma_result result; - - MA_ASSERT(pContext != NULL); - - result = ma_get_AudioObject_uid_as_CFStringRef(pContext, objectID, &uid); - if (result != MA_SUCCESS) { - return result; - } - - if (!((ma_CFStringGetCString_proc)pContext->coreaudio.CFStringGetCString)(uid, bufferOut, bufferSize, kCFStringEncodingUTF8)) { - return MA_ERROR; - } - - ((ma_CFRelease_proc)pContext->coreaudio.CFRelease)(uid); - return MA_SUCCESS; -} - -static ma_result ma_get_AudioObject_name(ma_context* pContext, AudioObjectID objectID, size_t bufferSize, char* bufferOut) -{ - AudioObjectPropertyAddress propAddress; - CFStringRef deviceName = NULL; - UInt32 dataSize; - OSStatus status; - - MA_ASSERT(pContext != NULL); - - propAddress.mSelector = kAudioDevicePropertyDeviceNameCFString; - propAddress.mScope = kAudioObjectPropertyScopeGlobal; - propAddress.mElement = AUDIO_OBJECT_PROPERTY_ELEMENT; - - dataSize = sizeof(deviceName); - status = ((ma_AudioObjectGetPropertyData_proc)pContext->coreaudio.AudioObjectGetPropertyData)(objectID, &propAddress, 0, NULL, &dataSize, &deviceName); - if (status != noErr) { - return ma_result_from_OSStatus(status); - } - - if (!((ma_CFStringGetCString_proc)pContext->coreaudio.CFStringGetCString)(deviceName, bufferOut, bufferSize, kCFStringEncodingUTF8)) { - return MA_ERROR; - } - - ((ma_CFRelease_proc)pContext->coreaudio.CFRelease)(deviceName); - return MA_SUCCESS; -} - -static ma_bool32 ma_does_AudioObject_support_scope(ma_context* pContext, AudioObjectID deviceObjectID, AudioObjectPropertyScope scope) -{ - AudioObjectPropertyAddress propAddress; - UInt32 dataSize; - OSStatus status; - AudioBufferList* pBufferList; - ma_bool32 isSupported; - - MA_ASSERT(pContext != NULL); - - /* To know whether or not a device is an input device we need ot look at the stream configuration. If it has an output channel it's a playback device. */ - propAddress.mSelector = kAudioDevicePropertyStreamConfiguration; - propAddress.mScope = scope; - propAddress.mElement = AUDIO_OBJECT_PROPERTY_ELEMENT; - - status = ((ma_AudioObjectGetPropertyDataSize_proc)pContext->coreaudio.AudioObjectGetPropertyDataSize)(deviceObjectID, &propAddress, 0, NULL, &dataSize); - if (status != noErr) { - return MA_FALSE; - } - - pBufferList = (AudioBufferList*)ma_malloc(dataSize, &pContext->allocationCallbacks); - if (pBufferList == NULL) { - return MA_FALSE; /* Out of memory. */ - } - - status = ((ma_AudioObjectGetPropertyData_proc)pContext->coreaudio.AudioObjectGetPropertyData)(deviceObjectID, &propAddress, 0, NULL, &dataSize, pBufferList); - if (status != noErr) { - ma_free(pBufferList, &pContext->allocationCallbacks); - return MA_FALSE; - } - - isSupported = MA_FALSE; - if (pBufferList->mNumberBuffers > 0) { - isSupported = MA_TRUE; - } - - ma_free(pBufferList, &pContext->allocationCallbacks); - return isSupported; -} - -static ma_bool32 ma_does_AudioObject_support_playback(ma_context* pContext, AudioObjectID deviceObjectID) -{ - return ma_does_AudioObject_support_scope(pContext, deviceObjectID, kAudioObjectPropertyScopeOutput); -} - -static ma_bool32 ma_does_AudioObject_support_capture(ma_context* pContext, AudioObjectID deviceObjectID) -{ - return ma_does_AudioObject_support_scope(pContext, deviceObjectID, kAudioObjectPropertyScopeInput); -} - - -static ma_result ma_get_AudioObject_stream_descriptions(ma_context* pContext, AudioObjectID deviceObjectID, ma_device_type deviceType, UInt32* pDescriptionCount, AudioStreamRangedDescription** ppDescriptions) /* NOTE: Free the returned pointer with ma_free(). */ -{ - AudioObjectPropertyAddress propAddress; - UInt32 dataSize; - OSStatus status; - AudioStreamRangedDescription* pDescriptions; - - MA_ASSERT(pContext != NULL); - MA_ASSERT(pDescriptionCount != NULL); - MA_ASSERT(ppDescriptions != NULL); - - /* - TODO: Experiment with kAudioStreamPropertyAvailablePhysicalFormats instead of (or in addition to) kAudioStreamPropertyAvailableVirtualFormats. My - MacBook Pro uses s24/32 format, however, which miniaudio does not currently support. - */ - propAddress.mSelector = kAudioStreamPropertyAvailableVirtualFormats; /*kAudioStreamPropertyAvailablePhysicalFormats;*/ - propAddress.mScope = (deviceType == ma_device_type_playback) ? kAudioObjectPropertyScopeOutput : kAudioObjectPropertyScopeInput; - propAddress.mElement = AUDIO_OBJECT_PROPERTY_ELEMENT; - - status = ((ma_AudioObjectGetPropertyDataSize_proc)pContext->coreaudio.AudioObjectGetPropertyDataSize)(deviceObjectID, &propAddress, 0, NULL, &dataSize); - if (status != noErr) { - return ma_result_from_OSStatus(status); - } - - pDescriptions = (AudioStreamRangedDescription*)ma_malloc(dataSize, &pContext->allocationCallbacks); - if (pDescriptions == NULL) { - return MA_OUT_OF_MEMORY; - } - - status = ((ma_AudioObjectGetPropertyData_proc)pContext->coreaudio.AudioObjectGetPropertyData)(deviceObjectID, &propAddress, 0, NULL, &dataSize, pDescriptions); - if (status != noErr) { - ma_free(pDescriptions, &pContext->allocationCallbacks); - return ma_result_from_OSStatus(status); - } - - *pDescriptionCount = dataSize / sizeof(*pDescriptions); - *ppDescriptions = pDescriptions; - return MA_SUCCESS; -} - - -static ma_result ma_get_AudioObject_channel_layout(ma_context* pContext, AudioObjectID deviceObjectID, ma_device_type deviceType, AudioChannelLayout** ppChannelLayout) /* NOTE: Free the returned pointer with ma_free(). */ -{ - AudioObjectPropertyAddress propAddress; - UInt32 dataSize; - OSStatus status; - AudioChannelLayout* pChannelLayout; - - MA_ASSERT(pContext != NULL); - MA_ASSERT(ppChannelLayout != NULL); - - *ppChannelLayout = NULL; /* Safety. */ - - propAddress.mSelector = kAudioDevicePropertyPreferredChannelLayout; - propAddress.mScope = (deviceType == ma_device_type_playback) ? kAudioObjectPropertyScopeOutput : kAudioObjectPropertyScopeInput; - propAddress.mElement = AUDIO_OBJECT_PROPERTY_ELEMENT; - - status = ((ma_AudioObjectGetPropertyDataSize_proc)pContext->coreaudio.AudioObjectGetPropertyDataSize)(deviceObjectID, &propAddress, 0, NULL, &dataSize); - if (status != noErr) { - return ma_result_from_OSStatus(status); - } - - pChannelLayout = (AudioChannelLayout*)ma_malloc(dataSize, &pContext->allocationCallbacks); - if (pChannelLayout == NULL) { - return MA_OUT_OF_MEMORY; - } - - status = ((ma_AudioObjectGetPropertyData_proc)pContext->coreaudio.AudioObjectGetPropertyData)(deviceObjectID, &propAddress, 0, NULL, &dataSize, pChannelLayout); - if (status != noErr) { - ma_free(pChannelLayout, &pContext->allocationCallbacks); - return ma_result_from_OSStatus(status); - } - - *ppChannelLayout = pChannelLayout; - return MA_SUCCESS; -} - -static ma_result ma_get_AudioObject_channel_count(ma_context* pContext, AudioObjectID deviceObjectID, ma_device_type deviceType, ma_uint32* pChannelCount) -{ - AudioChannelLayout* pChannelLayout; - ma_result result; - - MA_ASSERT(pContext != NULL); - MA_ASSERT(pChannelCount != NULL); - - *pChannelCount = 0; /* Safety. */ - - result = ma_get_AudioObject_channel_layout(pContext, deviceObjectID, deviceType, &pChannelLayout); - if (result != MA_SUCCESS) { - return result; - } - - if (pChannelLayout->mChannelLayoutTag == kAudioChannelLayoutTag_UseChannelDescriptions) { - *pChannelCount = pChannelLayout->mNumberChannelDescriptions; - } else if (pChannelLayout->mChannelLayoutTag == kAudioChannelLayoutTag_UseChannelBitmap) { - *pChannelCount = ma_count_set_bits(pChannelLayout->mChannelBitmap); - } else { - *pChannelCount = AudioChannelLayoutTag_GetNumberOfChannels(pChannelLayout->mChannelLayoutTag); - } - - ma_free(pChannelLayout, &pContext->allocationCallbacks); - return MA_SUCCESS; -} - -#if 0 -static ma_result ma_get_AudioObject_channel_map(ma_context* pContext, AudioObjectID deviceObjectID, ma_device_type deviceType, ma_channel* pChannelMap, size_t channelMapCap) -{ - AudioChannelLayout* pChannelLayout; - ma_result result; - - MA_ASSERT(pContext != NULL); - - result = ma_get_AudioObject_channel_layout(pContext, deviceObjectID, deviceType, &pChannelLayout); - if (result != MA_SUCCESS) { - return result; /* Rather than always failing here, would it be more robust to simply assume a default? */ - } - - result = ma_get_channel_map_from_AudioChannelLayout(pChannelLayout, pChannelMap, channelMapCap); - if (result != MA_SUCCESS) { - ma_free(pChannelLayout, &pContext->allocationCallbacks); - return result; - } - - ma_free(pChannelLayout, &pContext->allocationCallbacks); - return result; -} -#endif - -static ma_result ma_get_AudioObject_sample_rates(ma_context* pContext, AudioObjectID deviceObjectID, ma_device_type deviceType, UInt32* pSampleRateRangesCount, AudioValueRange** ppSampleRateRanges) /* NOTE: Free the returned pointer with ma_free(). */ -{ - AudioObjectPropertyAddress propAddress; - UInt32 dataSize; - OSStatus status; - AudioValueRange* pSampleRateRanges; - - MA_ASSERT(pContext != NULL); - MA_ASSERT(pSampleRateRangesCount != NULL); - MA_ASSERT(ppSampleRateRanges != NULL); - - /* Safety. */ - *pSampleRateRangesCount = 0; - *ppSampleRateRanges = NULL; - - propAddress.mSelector = kAudioDevicePropertyAvailableNominalSampleRates; - propAddress.mScope = (deviceType == ma_device_type_playback) ? kAudioObjectPropertyScopeOutput : kAudioObjectPropertyScopeInput; - propAddress.mElement = AUDIO_OBJECT_PROPERTY_ELEMENT; - - status = ((ma_AudioObjectGetPropertyDataSize_proc)pContext->coreaudio.AudioObjectGetPropertyDataSize)(deviceObjectID, &propAddress, 0, NULL, &dataSize); - if (status != noErr) { - return ma_result_from_OSStatus(status); - } - - pSampleRateRanges = (AudioValueRange*)ma_malloc(dataSize, &pContext->allocationCallbacks); - if (pSampleRateRanges == NULL) { - return MA_OUT_OF_MEMORY; - } - - status = ((ma_AudioObjectGetPropertyData_proc)pContext->coreaudio.AudioObjectGetPropertyData)(deviceObjectID, &propAddress, 0, NULL, &dataSize, pSampleRateRanges); - if (status != noErr) { - ma_free(pSampleRateRanges, &pContext->allocationCallbacks); - return ma_result_from_OSStatus(status); - } - - *pSampleRateRangesCount = dataSize / sizeof(*pSampleRateRanges); - *ppSampleRateRanges = pSampleRateRanges; - return MA_SUCCESS; -} - -#if 0 -static ma_result ma_get_AudioObject_get_closest_sample_rate(ma_context* pContext, AudioObjectID deviceObjectID, ma_device_type deviceType, ma_uint32 sampleRateIn, ma_uint32* pSampleRateOut) -{ - UInt32 sampleRateRangeCount; - AudioValueRange* pSampleRateRanges; - ma_result result; - - MA_ASSERT(pContext != NULL); - MA_ASSERT(pSampleRateOut != NULL); - - *pSampleRateOut = 0; /* Safety. */ - - result = ma_get_AudioObject_sample_rates(pContext, deviceObjectID, deviceType, &sampleRateRangeCount, &pSampleRateRanges); - if (result != MA_SUCCESS) { - return result; - } - - if (sampleRateRangeCount == 0) { - ma_free(pSampleRateRanges, &pContext->allocationCallbacks); - return MA_ERROR; /* Should never hit this case should we? */ - } - - if (sampleRateIn == 0) { - /* Search in order of miniaudio's preferred priority. */ - UInt32 iMALSampleRate; - for (iMALSampleRate = 0; iMALSampleRate < ma_countof(g_maStandardSampleRatePriorities); ++iMALSampleRate) { - ma_uint32 malSampleRate = g_maStandardSampleRatePriorities[iMALSampleRate]; - UInt32 iCASampleRate; - for (iCASampleRate = 0; iCASampleRate < sampleRateRangeCount; ++iCASampleRate) { - AudioValueRange caSampleRate = pSampleRateRanges[iCASampleRate]; - if (caSampleRate.mMinimum <= malSampleRate && caSampleRate.mMaximum >= malSampleRate) { - *pSampleRateOut = malSampleRate; - ma_free(pSampleRateRanges, &pContext->allocationCallbacks); - return MA_SUCCESS; - } - } - } - - /* - If we get here it means none of miniaudio's standard sample rates matched any of the supported sample rates from the device. In this - case we just fall back to the first one reported by Core Audio. - */ - MA_ASSERT(sampleRateRangeCount > 0); - - *pSampleRateOut = pSampleRateRanges[0].mMinimum; - ma_free(pSampleRateRanges, &pContext->allocationCallbacks); - return MA_SUCCESS; - } else { - /* Find the closest match to this sample rate. */ - UInt32 currentAbsoluteDifference = INT32_MAX; - UInt32 iCurrentClosestRange = (UInt32)-1; - UInt32 iRange; - for (iRange = 0; iRange < sampleRateRangeCount; ++iRange) { - if (pSampleRateRanges[iRange].mMinimum <= sampleRateIn && pSampleRateRanges[iRange].mMaximum >= sampleRateIn) { - *pSampleRateOut = sampleRateIn; - ma_free(pSampleRateRanges, &pContext->allocationCallbacks); - return MA_SUCCESS; - } else { - UInt32 absoluteDifference; - if (pSampleRateRanges[iRange].mMinimum > sampleRateIn) { - absoluteDifference = pSampleRateRanges[iRange].mMinimum - sampleRateIn; - } else { - absoluteDifference = sampleRateIn - pSampleRateRanges[iRange].mMaximum; - } - - if (currentAbsoluteDifference > absoluteDifference) { - currentAbsoluteDifference = absoluteDifference; - iCurrentClosestRange = iRange; - } - } - } - - MA_ASSERT(iCurrentClosestRange != (UInt32)-1); - - *pSampleRateOut = pSampleRateRanges[iCurrentClosestRange].mMinimum; - ma_free(pSampleRateRanges, &pContext->allocationCallbacks); - return MA_SUCCESS; - } - - /* Should never get here, but it would mean we weren't able to find any suitable sample rates. */ - /*ma_free(pSampleRateRanges, &pContext->allocationCallbacks);*/ - /*return MA_ERROR;*/ -} -#endif - -static ma_result ma_get_AudioObject_closest_buffer_size_in_frames(ma_context* pContext, AudioObjectID deviceObjectID, ma_device_type deviceType, ma_uint32 bufferSizeInFramesIn, ma_uint32* pBufferSizeInFramesOut) -{ - AudioObjectPropertyAddress propAddress; - AudioValueRange bufferSizeRange; - UInt32 dataSize; - OSStatus status; - - MA_ASSERT(pContext != NULL); - MA_ASSERT(pBufferSizeInFramesOut != NULL); - - *pBufferSizeInFramesOut = 0; /* Safety. */ - - propAddress.mSelector = kAudioDevicePropertyBufferFrameSizeRange; - propAddress.mScope = (deviceType == ma_device_type_playback) ? kAudioObjectPropertyScopeOutput : kAudioObjectPropertyScopeInput; - propAddress.mElement = AUDIO_OBJECT_PROPERTY_ELEMENT; - - dataSize = sizeof(bufferSizeRange); - status = ((ma_AudioObjectGetPropertyData_proc)pContext->coreaudio.AudioObjectGetPropertyData)(deviceObjectID, &propAddress, 0, NULL, &dataSize, &bufferSizeRange); - if (status != noErr) { - return ma_result_from_OSStatus(status); - } - - /* This is just a clamp. */ - if (bufferSizeInFramesIn < bufferSizeRange.mMinimum) { - *pBufferSizeInFramesOut = (ma_uint32)bufferSizeRange.mMinimum; - } else if (bufferSizeInFramesIn > bufferSizeRange.mMaximum) { - *pBufferSizeInFramesOut = (ma_uint32)bufferSizeRange.mMaximum; - } else { - *pBufferSizeInFramesOut = bufferSizeInFramesIn; - } - - return MA_SUCCESS; -} - -static ma_result ma_set_AudioObject_buffer_size_in_frames(ma_context* pContext, AudioObjectID deviceObjectID, ma_device_type deviceType, ma_uint32* pPeriodSizeInOut) -{ - ma_result result; - ma_uint32 chosenBufferSizeInFrames; - AudioObjectPropertyAddress propAddress; - UInt32 dataSize; - OSStatus status; - - MA_ASSERT(pContext != NULL); - - result = ma_get_AudioObject_closest_buffer_size_in_frames(pContext, deviceObjectID, deviceType, *pPeriodSizeInOut, &chosenBufferSizeInFrames); - if (result != MA_SUCCESS) { - return result; - } - - /* Try setting the size of the buffer... If this fails we just use whatever is currently set. */ - propAddress.mSelector = kAudioDevicePropertyBufferFrameSize; - propAddress.mScope = (deviceType == ma_device_type_playback) ? kAudioObjectPropertyScopeOutput : kAudioObjectPropertyScopeInput; - propAddress.mElement = AUDIO_OBJECT_PROPERTY_ELEMENT; - - ((ma_AudioObjectSetPropertyData_proc)pContext->coreaudio.AudioObjectSetPropertyData)(deviceObjectID, &propAddress, 0, NULL, sizeof(chosenBufferSizeInFrames), &chosenBufferSizeInFrames); - - /* Get the actual size of the buffer. */ - dataSize = sizeof(*pPeriodSizeInOut); - status = ((ma_AudioObjectGetPropertyData_proc)pContext->coreaudio.AudioObjectGetPropertyData)(deviceObjectID, &propAddress, 0, NULL, &dataSize, &chosenBufferSizeInFrames); - if (status != noErr) { - return ma_result_from_OSStatus(status); - } - - *pPeriodSizeInOut = chosenBufferSizeInFrames; - return MA_SUCCESS; -} - -static ma_result ma_find_default_AudioObjectID(ma_context* pContext, ma_device_type deviceType, AudioObjectID* pDeviceObjectID) -{ - AudioObjectPropertyAddress propAddressDefaultDevice; - UInt32 defaultDeviceObjectIDSize = sizeof(AudioObjectID); - AudioObjectID defaultDeviceObjectID; - OSStatus status; - - MA_ASSERT(pContext != NULL); - MA_ASSERT(pDeviceObjectID != NULL); - - /* Safety. */ - *pDeviceObjectID = 0; - - propAddressDefaultDevice.mScope = kAudioObjectPropertyScopeGlobal; - propAddressDefaultDevice.mElement = AUDIO_OBJECT_PROPERTY_ELEMENT; - if (deviceType == ma_device_type_playback) { - propAddressDefaultDevice.mSelector = kAudioHardwarePropertyDefaultOutputDevice; - } else { - propAddressDefaultDevice.mSelector = kAudioHardwarePropertyDefaultInputDevice; - } - - defaultDeviceObjectIDSize = sizeof(AudioObjectID); - status = ((ma_AudioObjectGetPropertyData_proc)pContext->coreaudio.AudioObjectGetPropertyData)(kAudioObjectSystemObject, &propAddressDefaultDevice, 0, NULL, &defaultDeviceObjectIDSize, &defaultDeviceObjectID); - if (status == noErr) { - *pDeviceObjectID = defaultDeviceObjectID; - return MA_SUCCESS; - } - - /* If we get here it means we couldn't find the device. */ - return MA_NO_DEVICE; -} - -static ma_result ma_find_AudioObjectID(ma_context* pContext, ma_device_type deviceType, const ma_device_id* pDeviceID, AudioObjectID* pDeviceObjectID) -{ - MA_ASSERT(pContext != NULL); - MA_ASSERT(pDeviceObjectID != NULL); - - /* Safety. */ - *pDeviceObjectID = 0; - - if (pDeviceID == NULL) { - /* Default device. */ - return ma_find_default_AudioObjectID(pContext, deviceType, pDeviceObjectID); - } else { - /* Explicit device. */ - UInt32 deviceCount; - AudioObjectID* pDeviceObjectIDs; - ma_result result; - UInt32 iDevice; - - result = ma_get_device_object_ids__coreaudio(pContext, &deviceCount, &pDeviceObjectIDs); - if (result != MA_SUCCESS) { - return result; - } - - for (iDevice = 0; iDevice < deviceCount; ++iDevice) { - AudioObjectID deviceObjectID = pDeviceObjectIDs[iDevice]; - - char uid[256]; - if (ma_get_AudioObject_uid(pContext, deviceObjectID, sizeof(uid), uid) != MA_SUCCESS) { - continue; - } - - if (deviceType == ma_device_type_playback) { - if (ma_does_AudioObject_support_playback(pContext, deviceObjectID)) { - if (strcmp(uid, pDeviceID->coreaudio) == 0) { - *pDeviceObjectID = deviceObjectID; - ma_free(pDeviceObjectIDs, &pContext->allocationCallbacks); - return MA_SUCCESS; - } - } - } else { - if (ma_does_AudioObject_support_capture(pContext, deviceObjectID)) { - if (strcmp(uid, pDeviceID->coreaudio) == 0) { - *pDeviceObjectID = deviceObjectID; - ma_free(pDeviceObjectIDs, &pContext->allocationCallbacks); - return MA_SUCCESS; - } - } - } - } - - ma_free(pDeviceObjectIDs, &pContext->allocationCallbacks); - } - - /* If we get here it means we couldn't find the device. */ - return MA_NO_DEVICE; -} - - -static ma_result ma_find_best_format__coreaudio(ma_context* pContext, AudioObjectID deviceObjectID, ma_device_type deviceType, ma_format format, ma_uint32 channels, ma_uint32 sampleRate, const AudioStreamBasicDescription* pOrigFormat, AudioStreamBasicDescription* pFormat) -{ - UInt32 deviceFormatDescriptionCount; - AudioStreamRangedDescription* pDeviceFormatDescriptions; - ma_result result; - ma_uint32 desiredSampleRate; - ma_uint32 desiredChannelCount; - ma_format desiredFormat; - AudioStreamBasicDescription bestDeviceFormatSoFar; - ma_bool32 hasSupportedFormat; - UInt32 iFormat; - - result = ma_get_AudioObject_stream_descriptions(pContext, deviceObjectID, deviceType, &deviceFormatDescriptionCount, &pDeviceFormatDescriptions); - if (result != MA_SUCCESS) { - return result; - } - - desiredSampleRate = sampleRate; - if (desiredSampleRate == 0) { - desiredSampleRate = (ma_uint32)pOrigFormat->mSampleRate; - } - - desiredChannelCount = channels; - if (desiredChannelCount == 0) { - desiredChannelCount = pOrigFormat->mChannelsPerFrame; - } - - desiredFormat = format; - if (desiredFormat == ma_format_unknown) { - result = ma_format_from_AudioStreamBasicDescription(pOrigFormat, &desiredFormat); - if (result != MA_SUCCESS || desiredFormat == ma_format_unknown) { - desiredFormat = g_maFormatPriorities[0]; - } - } - - /* - If we get here it means we don't have an exact match to what the client is asking for. We'll need to find the closest one. The next - loop will check for formats that have the same sample rate to what we're asking for. If there is, we prefer that one in all cases. - */ - MA_ZERO_OBJECT(&bestDeviceFormatSoFar); - - hasSupportedFormat = MA_FALSE; - for (iFormat = 0; iFormat < deviceFormatDescriptionCount; ++iFormat) { - ma_format formatFromDescription; - ma_result formatResult = ma_format_from_AudioStreamBasicDescription(&pDeviceFormatDescriptions[iFormat].mFormat, &formatFromDescription); - if (formatResult == MA_SUCCESS && formatFromDescription != ma_format_unknown) { - hasSupportedFormat = MA_TRUE; - bestDeviceFormatSoFar = pDeviceFormatDescriptions[iFormat].mFormat; - break; - } - } - - if (!hasSupportedFormat) { - ma_free(pDeviceFormatDescriptions, &pContext->allocationCallbacks); - return MA_FORMAT_NOT_SUPPORTED; - } - - - for (iFormat = 0; iFormat < deviceFormatDescriptionCount; ++iFormat) { - AudioStreamBasicDescription thisDeviceFormat = pDeviceFormatDescriptions[iFormat].mFormat; - ma_format thisSampleFormat; - ma_result formatResult; - ma_format bestSampleFormatSoFar; - - /* If the format is not supported by miniaudio we need to skip this one entirely. */ - formatResult = ma_format_from_AudioStreamBasicDescription(&pDeviceFormatDescriptions[iFormat].mFormat, &thisSampleFormat); - if (formatResult != MA_SUCCESS || thisSampleFormat == ma_format_unknown) { - continue; /* The format is not supported by miniaudio. Skip. */ - } - - ma_format_from_AudioStreamBasicDescription(&bestDeviceFormatSoFar, &bestSampleFormatSoFar); - - /* Getting here means the format is supported by miniaudio which makes this format a candidate. */ - if (thisDeviceFormat.mSampleRate != desiredSampleRate) { - /* - The sample rate does not match, but this format could still be usable, although it's a very low priority. If the best format - so far has an equal sample rate we can just ignore this one. - */ - if (bestDeviceFormatSoFar.mSampleRate == desiredSampleRate) { - continue; /* The best sample rate so far has the same sample rate as what we requested which means it's still the best so far. Skip this format. */ - } else { - /* In this case, neither the best format so far nor this one have the same sample rate. Check the channel count next. */ - if (thisDeviceFormat.mChannelsPerFrame != desiredChannelCount) { - /* This format has a different sample rate _and_ a different channel count. */ - if (bestDeviceFormatSoFar.mChannelsPerFrame == desiredChannelCount) { - continue; /* No change to the best format. */ - } else { - /* - Both this format and the best so far have different sample rates and different channel counts. Whichever has the - best format is the new best. - */ - if (ma_get_format_priority_index(thisSampleFormat) < ma_get_format_priority_index(bestSampleFormatSoFar)) { - bestDeviceFormatSoFar = thisDeviceFormat; - continue; - } else { - continue; /* No change to the best format. */ - } - } - } else { - /* This format has a different sample rate but the desired channel count. */ - if (bestDeviceFormatSoFar.mChannelsPerFrame == desiredChannelCount) { - /* Both this format and the best so far have the desired channel count. Whichever has the best format is the new best. */ - if (ma_get_format_priority_index(thisSampleFormat) < ma_get_format_priority_index(bestSampleFormatSoFar)) { - bestDeviceFormatSoFar = thisDeviceFormat; - continue; - } else { - continue; /* No change to the best format for now. */ - } - } else { - /* This format has the desired channel count, but the best so far does not. We have a new best. */ - bestDeviceFormatSoFar = thisDeviceFormat; - continue; - } - } - } - } else { - /* - The sample rates match which makes this format a very high priority contender. If the best format so far has a different - sample rate it needs to be replaced with this one. - */ - if (bestDeviceFormatSoFar.mSampleRate != desiredSampleRate) { - bestDeviceFormatSoFar = thisDeviceFormat; - continue; - } else { - /* In this case both this format and the best format so far have the same sample rate. Check the channel count next. */ - if (thisDeviceFormat.mChannelsPerFrame == desiredChannelCount) { - /* - In this case this format has the same channel count as what the client is requesting. If the best format so far has - a different count, this one becomes the new best. - */ - if (bestDeviceFormatSoFar.mChannelsPerFrame != desiredChannelCount) { - bestDeviceFormatSoFar = thisDeviceFormat; - continue; - } else { - /* In this case both this format and the best so far have the ideal sample rate and channel count. Check the format. */ - if (thisSampleFormat == desiredFormat) { - bestDeviceFormatSoFar = thisDeviceFormat; - break; /* Found the exact match. */ - } else { - /* The formats are different. The new best format is the one with the highest priority format according to miniaudio. */ - if (ma_get_format_priority_index(thisSampleFormat) < ma_get_format_priority_index(bestSampleFormatSoFar)) { - bestDeviceFormatSoFar = thisDeviceFormat; - continue; - } else { - continue; /* No change to the best format for now. */ - } - } - } - } else { - /* - In this case the channel count is different to what the client has requested. If the best so far has the same channel - count as the requested count then it remains the best. - */ - if (bestDeviceFormatSoFar.mChannelsPerFrame == desiredChannelCount) { - continue; - } else { - /* - This is the case where both have the same sample rate (good) but different channel counts. Right now both have about - the same priority, but we need to compare the format now. - */ - if (thisSampleFormat == bestSampleFormatSoFar) { - if (ma_get_format_priority_index(thisSampleFormat) < ma_get_format_priority_index(bestSampleFormatSoFar)) { - bestDeviceFormatSoFar = thisDeviceFormat; - continue; - } else { - continue; /* No change to the best format for now. */ - } - } - } - } - } - } - } - - *pFormat = bestDeviceFormatSoFar; - - ma_free(pDeviceFormatDescriptions, &pContext->allocationCallbacks); - return MA_SUCCESS; -} - -static ma_result ma_get_AudioUnit_channel_map(ma_context* pContext, AudioUnit audioUnit, ma_device_type deviceType, ma_channel* pChannelMap, size_t channelMapCap) -{ - AudioUnitScope deviceScope; - AudioUnitElement deviceBus; - UInt32 channelLayoutSize; - OSStatus status; - AudioChannelLayout* pChannelLayout; - ma_result result; - - MA_ASSERT(pContext != NULL); - - if (deviceType == ma_device_type_playback) { - deviceScope = kAudioUnitScope_Input; - deviceBus = MA_COREAUDIO_OUTPUT_BUS; - } else { - deviceScope = kAudioUnitScope_Output; - deviceBus = MA_COREAUDIO_INPUT_BUS; - } - - status = ((ma_AudioUnitGetPropertyInfo_proc)pContext->coreaudio.AudioUnitGetPropertyInfo)(audioUnit, kAudioUnitProperty_AudioChannelLayout, deviceScope, deviceBus, &channelLayoutSize, NULL); - if (status != noErr) { - return ma_result_from_OSStatus(status); - } - - pChannelLayout = (AudioChannelLayout*)ma_malloc(channelLayoutSize, &pContext->allocationCallbacks); - if (pChannelLayout == NULL) { - return MA_OUT_OF_MEMORY; - } - - status = ((ma_AudioUnitGetProperty_proc)pContext->coreaudio.AudioUnitGetProperty)(audioUnit, kAudioUnitProperty_AudioChannelLayout, deviceScope, deviceBus, pChannelLayout, &channelLayoutSize); - if (status != noErr) { - ma_free(pChannelLayout, &pContext->allocationCallbacks); - return ma_result_from_OSStatus(status); - } - - result = ma_get_channel_map_from_AudioChannelLayout(pChannelLayout, pChannelMap, channelMapCap); - if (result != MA_SUCCESS) { - ma_free(pChannelLayout, &pContext->allocationCallbacks); - return result; - } - - ma_free(pChannelLayout, &pContext->allocationCallbacks); - return MA_SUCCESS; -} -#endif /* MA_APPLE_DESKTOP */ - - -#if !defined(MA_APPLE_DESKTOP) -static void ma_AVAudioSessionPortDescription_to_device_info(AVAudioSessionPortDescription* pPortDesc, ma_device_info* pInfo) -{ - MA_ZERO_OBJECT(pInfo); - ma_strncpy_s(pInfo->name, sizeof(pInfo->name), [pPortDesc.portName UTF8String], (size_t)-1); - ma_strncpy_s(pInfo->id.coreaudio, sizeof(pInfo->id.coreaudio), [pPortDesc.UID UTF8String], (size_t)-1); -} -#endif - -static ma_result ma_context_enumerate_devices__coreaudio(ma_context* pContext, ma_enum_devices_callback_proc callback, void* pUserData) -{ -#if defined(MA_APPLE_DESKTOP) - UInt32 deviceCount; - AudioObjectID* pDeviceObjectIDs; - AudioObjectID defaultDeviceObjectIDPlayback; - AudioObjectID defaultDeviceObjectIDCapture; - ma_result result; - UInt32 iDevice; - - ma_find_default_AudioObjectID(pContext, ma_device_type_playback, &defaultDeviceObjectIDPlayback); /* OK if this fails. */ - ma_find_default_AudioObjectID(pContext, ma_device_type_capture, &defaultDeviceObjectIDCapture); /* OK if this fails. */ - - result = ma_get_device_object_ids__coreaudio(pContext, &deviceCount, &pDeviceObjectIDs); - if (result != MA_SUCCESS) { - return result; - } - - for (iDevice = 0; iDevice < deviceCount; ++iDevice) { - AudioObjectID deviceObjectID = pDeviceObjectIDs[iDevice]; - ma_device_info info; - - MA_ZERO_OBJECT(&info); - if (ma_get_AudioObject_uid(pContext, deviceObjectID, sizeof(info.id.coreaudio), info.id.coreaudio) != MA_SUCCESS) { - continue; - } - if (ma_get_AudioObject_name(pContext, deviceObjectID, sizeof(info.name), info.name) != MA_SUCCESS) { - continue; - } - - if (ma_does_AudioObject_support_playback(pContext, deviceObjectID)) { - if (deviceObjectID == defaultDeviceObjectIDPlayback) { - info.isDefault = MA_TRUE; - } - - if (!callback(pContext, ma_device_type_playback, &info, pUserData)) { - break; - } - } - if (ma_does_AudioObject_support_capture(pContext, deviceObjectID)) { - if (deviceObjectID == defaultDeviceObjectIDCapture) { - info.isDefault = MA_TRUE; - } - - if (!callback(pContext, ma_device_type_capture, &info, pUserData)) { - break; - } - } - } - - ma_free(pDeviceObjectIDs, &pContext->allocationCallbacks); -#else - ma_device_info info; - NSArray *pInputs = [[[AVAudioSession sharedInstance] currentRoute] inputs]; - NSArray *pOutputs = [[[AVAudioSession sharedInstance] currentRoute] outputs]; - - for (AVAudioSessionPortDescription* pPortDesc in pOutputs) { - ma_AVAudioSessionPortDescription_to_device_info(pPortDesc, &info); - if (!callback(pContext, ma_device_type_playback, &info, pUserData)) { - return MA_SUCCESS; - } - } - - for (AVAudioSessionPortDescription* pPortDesc in pInputs) { - ma_AVAudioSessionPortDescription_to_device_info(pPortDesc, &info); - if (!callback(pContext, ma_device_type_capture, &info, pUserData)) { - return MA_SUCCESS; - } - } -#endif - - return MA_SUCCESS; -} - -static ma_result ma_context_get_device_info__coreaudio(ma_context* pContext, ma_device_type deviceType, const ma_device_id* pDeviceID, ma_device_info* pDeviceInfo) -{ - ma_result result; - - MA_ASSERT(pContext != NULL); - -#if defined(MA_APPLE_DESKTOP) - /* Desktop */ - { - AudioObjectID deviceObjectID; - AudioObjectID defaultDeviceObjectID; - UInt32 streamDescriptionCount; - AudioStreamRangedDescription* pStreamDescriptions; - UInt32 iStreamDescription; - UInt32 sampleRateRangeCount; - AudioValueRange* pSampleRateRanges; - - ma_find_default_AudioObjectID(pContext, deviceType, &defaultDeviceObjectID); /* OK if this fails. */ - - result = ma_find_AudioObjectID(pContext, deviceType, pDeviceID, &deviceObjectID); - if (result != MA_SUCCESS) { - return result; - } - - result = ma_get_AudioObject_uid(pContext, deviceObjectID, sizeof(pDeviceInfo->id.coreaudio), pDeviceInfo->id.coreaudio); - if (result != MA_SUCCESS) { - return result; - } - - result = ma_get_AudioObject_name(pContext, deviceObjectID, sizeof(pDeviceInfo->name), pDeviceInfo->name); - if (result != MA_SUCCESS) { - return result; - } - - if (deviceObjectID == defaultDeviceObjectID) { - pDeviceInfo->isDefault = MA_TRUE; - } - - /* - There could be a large number of permutations here. Fortunately there is only a single channel count - being reported which reduces this quite a bit. For sample rates we're only reporting those that are - one of miniaudio's recognized "standard" rates. If there are still more formats than can fit into - our fixed sized array we'll just need to truncate them. This is unlikely and will probably only happen - if some driver performs software data conversion and therefore reports every possible format and - sample rate. - */ - pDeviceInfo->nativeDataFormatCount = 0; - - /* Formats. */ - { - ma_format uniqueFormats[ma_format_count]; - ma_uint32 uniqueFormatCount = 0; - ma_uint32 channels; - - /* Channels. */ - result = ma_get_AudioObject_channel_count(pContext, deviceObjectID, deviceType, &channels); - if (result != MA_SUCCESS) { - return result; - } - - /* Formats. */ - result = ma_get_AudioObject_stream_descriptions(pContext, deviceObjectID, deviceType, &streamDescriptionCount, &pStreamDescriptions); - if (result != MA_SUCCESS) { - return result; - } - - for (iStreamDescription = 0; iStreamDescription < streamDescriptionCount; ++iStreamDescription) { - ma_format format; - ma_bool32 hasFormatBeenHandled = MA_FALSE; - ma_uint32 iOutputFormat; - ma_uint32 iSampleRate; - - result = ma_format_from_AudioStreamBasicDescription(&pStreamDescriptions[iStreamDescription].mFormat, &format); - if (result != MA_SUCCESS) { - continue; - } - - MA_ASSERT(format != ma_format_unknown); - - /* Make sure the format isn't already in the output list. */ - for (iOutputFormat = 0; iOutputFormat < uniqueFormatCount; ++iOutputFormat) { - if (uniqueFormats[iOutputFormat] == format) { - hasFormatBeenHandled = MA_TRUE; - break; - } - } - - /* If we've already handled this format just skip it. */ - if (hasFormatBeenHandled) { - continue; - } - - uniqueFormats[uniqueFormatCount] = format; - uniqueFormatCount += 1; - - /* Sample Rates */ - result = ma_get_AudioObject_sample_rates(pContext, deviceObjectID, deviceType, &sampleRateRangeCount, &pSampleRateRanges); - if (result != MA_SUCCESS) { - return result; - } - - /* - Annoyingly Core Audio reports a sample rate range. We just get all the standard rates that are - between this range. - */ - for (iSampleRate = 0; iSampleRate < sampleRateRangeCount; ++iSampleRate) { - ma_uint32 iStandardSampleRate; - for (iStandardSampleRate = 0; iStandardSampleRate < ma_countof(g_maStandardSampleRatePriorities); iStandardSampleRate += 1) { - ma_uint32 standardSampleRate = g_maStandardSampleRatePriorities[iStandardSampleRate]; - if (standardSampleRate >= pSampleRateRanges[iSampleRate].mMinimum && standardSampleRate <= pSampleRateRanges[iSampleRate].mMaximum) { - /* We have a new data format. Add it to the list. */ - pDeviceInfo->nativeDataFormats[pDeviceInfo->nativeDataFormatCount].format = format; - pDeviceInfo->nativeDataFormats[pDeviceInfo->nativeDataFormatCount].channels = channels; - pDeviceInfo->nativeDataFormats[pDeviceInfo->nativeDataFormatCount].sampleRate = standardSampleRate; - pDeviceInfo->nativeDataFormats[pDeviceInfo->nativeDataFormatCount].flags = 0; - pDeviceInfo->nativeDataFormatCount += 1; - - if (pDeviceInfo->nativeDataFormatCount >= ma_countof(pDeviceInfo->nativeDataFormats)) { - break; /* No more room for any more formats. */ - } - } - } - } - - ma_free(pSampleRateRanges, &pContext->allocationCallbacks); - - if (pDeviceInfo->nativeDataFormatCount >= ma_countof(pDeviceInfo->nativeDataFormats)) { - break; /* No more room for any more formats. */ - } - } - - ma_free(pStreamDescriptions, &pContext->allocationCallbacks); - } - } -#else - /* Mobile */ - { - AudioComponentDescription desc; - AudioComponent component; - AudioUnit audioUnit; - OSStatus status; - AudioUnitScope formatScope; - AudioUnitElement formatElement; - AudioStreamBasicDescription bestFormat; - UInt32 propSize; - - /* We want to ensure we use a consistent device name to device enumeration. */ - if (pDeviceID != NULL && pDeviceID->coreaudio[0] != '\0') { - ma_bool32 found = MA_FALSE; - if (deviceType == ma_device_type_playback) { - NSArray *pOutputs = [[[AVAudioSession sharedInstance] currentRoute] outputs]; - for (AVAudioSessionPortDescription* pPortDesc in pOutputs) { - if (strcmp(pDeviceID->coreaudio, [pPortDesc.UID UTF8String]) == 0) { - ma_AVAudioSessionPortDescription_to_device_info(pPortDesc, pDeviceInfo); - found = MA_TRUE; - break; - } - } - } else { - NSArray *pInputs = [[[AVAudioSession sharedInstance] currentRoute] inputs]; - for (AVAudioSessionPortDescription* pPortDesc in pInputs) { - if (strcmp(pDeviceID->coreaudio, [pPortDesc.UID UTF8String]) == 0) { - ma_AVAudioSessionPortDescription_to_device_info(pPortDesc, pDeviceInfo); - found = MA_TRUE; - break; - } - } - } - - if (!found) { - return MA_DOES_NOT_EXIST; - } - } else { - if (deviceType == ma_device_type_playback) { - ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), MA_DEFAULT_PLAYBACK_DEVICE_NAME, (size_t)-1); - } else { - ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), MA_DEFAULT_CAPTURE_DEVICE_NAME, (size_t)-1); - } - } - - - /* - Retrieving device information is more annoying on mobile than desktop. For simplicity I'm locking this down to whatever format is - reported on a temporary I/O unit. The problem, however, is that this doesn't return a value for the sample rate which we need to - retrieve from the AVAudioSession shared instance. - */ - desc.componentType = kAudioUnitType_Output; - desc.componentSubType = kAudioUnitSubType_RemoteIO; - desc.componentManufacturer = kAudioUnitManufacturer_Apple; - desc.componentFlags = 0; - desc.componentFlagsMask = 0; - - component = ((ma_AudioComponentFindNext_proc)pContext->coreaudio.AudioComponentFindNext)(NULL, &desc); - if (component == NULL) { - return MA_FAILED_TO_INIT_BACKEND; - } - - status = ((ma_AudioComponentInstanceNew_proc)pContext->coreaudio.AudioComponentInstanceNew)(component, &audioUnit); - if (status != noErr) { - return ma_result_from_OSStatus(status); - } - - formatScope = (deviceType == ma_device_type_playback) ? kAudioUnitScope_Input : kAudioUnitScope_Output; - formatElement = (deviceType == ma_device_type_playback) ? MA_COREAUDIO_OUTPUT_BUS : MA_COREAUDIO_INPUT_BUS; - - propSize = sizeof(bestFormat); - status = ((ma_AudioUnitGetProperty_proc)pContext->coreaudio.AudioUnitGetProperty)(audioUnit, kAudioUnitProperty_StreamFormat, formatScope, formatElement, &bestFormat, &propSize); - if (status != noErr) { - ((ma_AudioComponentInstanceDispose_proc)pContext->coreaudio.AudioComponentInstanceDispose)(audioUnit); - return ma_result_from_OSStatus(status); - } - - ((ma_AudioComponentInstanceDispose_proc)pContext->coreaudio.AudioComponentInstanceDispose)(audioUnit); - audioUnit = NULL; - - /* Only a single format is being reported for iOS. */ - pDeviceInfo->nativeDataFormatCount = 1; - - result = ma_format_from_AudioStreamBasicDescription(&bestFormat, &pDeviceInfo->nativeDataFormats[0].format); - if (result != MA_SUCCESS) { - return result; - } - - pDeviceInfo->nativeDataFormats[0].channels = bestFormat.mChannelsPerFrame; - - /* - It looks like Apple are wanting to push the whole AVAudioSession thing. Thus, we need to use that to determine device settings. To do - this we just get the shared instance and inspect. - */ - @autoreleasepool { - AVAudioSession* pAudioSession = [AVAudioSession sharedInstance]; - MA_ASSERT(pAudioSession != NULL); - - pDeviceInfo->nativeDataFormats[0].sampleRate = (ma_uint32)pAudioSession.sampleRate; - } - } -#endif - - (void)pDeviceInfo; /* Unused. */ - return MA_SUCCESS; -} - -static AudioBufferList* ma_allocate_AudioBufferList__coreaudio(ma_uint32 sizeInFrames, ma_format format, ma_uint32 channels, ma_stream_layout layout, const ma_allocation_callbacks* pAllocationCallbacks) -{ - AudioBufferList* pBufferList; - UInt32 audioBufferSizeInBytes; - size_t allocationSize; - - MA_ASSERT(sizeInFrames > 0); - MA_ASSERT(format != ma_format_unknown); - MA_ASSERT(channels > 0); - - allocationSize = sizeof(AudioBufferList) - sizeof(AudioBuffer); /* Subtract sizeof(AudioBuffer) because that part is dynamically sized. */ - if (layout == ma_stream_layout_interleaved) { - /* Interleaved case. This is the simple case because we just have one buffer. */ - allocationSize += sizeof(AudioBuffer) * 1; - } else { - /* Non-interleaved case. This is the more complex case because there's more than one buffer. */ - allocationSize += sizeof(AudioBuffer) * channels; - } - - allocationSize += sizeInFrames * ma_get_bytes_per_frame(format, channels); - - pBufferList = (AudioBufferList*)ma_malloc(allocationSize, pAllocationCallbacks); - if (pBufferList == NULL) { - return NULL; - } - - audioBufferSizeInBytes = (UInt32)(sizeInFrames * ma_get_bytes_per_sample(format)); - - if (layout == ma_stream_layout_interleaved) { - pBufferList->mNumberBuffers = 1; - pBufferList->mBuffers[0].mNumberChannels = channels; - pBufferList->mBuffers[0].mDataByteSize = audioBufferSizeInBytes * channels; - pBufferList->mBuffers[0].mData = (ma_uint8*)pBufferList + sizeof(AudioBufferList); - } else { - ma_uint32 iBuffer; - pBufferList->mNumberBuffers = channels; - for (iBuffer = 0; iBuffer < pBufferList->mNumberBuffers; ++iBuffer) { - pBufferList->mBuffers[iBuffer].mNumberChannels = 1; - pBufferList->mBuffers[iBuffer].mDataByteSize = audioBufferSizeInBytes; - pBufferList->mBuffers[iBuffer].mData = (ma_uint8*)pBufferList + ((sizeof(AudioBufferList) - sizeof(AudioBuffer)) + (sizeof(AudioBuffer) * channels)) + (audioBufferSizeInBytes * iBuffer); - } - } - - return pBufferList; -} - -static ma_result ma_device_realloc_AudioBufferList__coreaudio(ma_device* pDevice, ma_uint32 sizeInFrames, ma_format format, ma_uint32 channels, ma_stream_layout layout) -{ - MA_ASSERT(pDevice != NULL); - MA_ASSERT(format != ma_format_unknown); - MA_ASSERT(channels > 0); - - /* Only resize the buffer if necessary. */ - if (pDevice->coreaudio.audioBufferCapInFrames < sizeInFrames) { - AudioBufferList* pNewAudioBufferList; - - pNewAudioBufferList = ma_allocate_AudioBufferList__coreaudio(sizeInFrames, format, channels, layout, &pDevice->pContext->allocationCallbacks); - if (pNewAudioBufferList == NULL) { - return MA_OUT_OF_MEMORY; - } - - /* At this point we'll have a new AudioBufferList and we can free the old one. */ - ma_free(pDevice->coreaudio.pAudioBufferList, &pDevice->pContext->allocationCallbacks); - pDevice->coreaudio.pAudioBufferList = pNewAudioBufferList; - pDevice->coreaudio.audioBufferCapInFrames = sizeInFrames; - } - - /* Getting here means the capacity of the audio is fine. */ - return MA_SUCCESS; -} - - -static OSStatus ma_on_output__coreaudio(void* pUserData, AudioUnitRenderActionFlags* pActionFlags, const AudioTimeStamp* pTimeStamp, UInt32 busNumber, UInt32 frameCount, AudioBufferList* pBufferList) -{ - ma_device* pDevice = (ma_device*)pUserData; - ma_stream_layout layout; - - MA_ASSERT(pDevice != NULL); - - /*ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "INFO: Output Callback: busNumber=%d, frameCount=%d, mNumberBuffers=%d\n", (int)busNumber, (int)frameCount, (int)pBufferList->mNumberBuffers);*/ - - /* We need to check whether or not we are outputting interleaved or non-interleaved samples. The way we do this is slightly different for each type. */ - layout = ma_stream_layout_interleaved; - if (pBufferList->mBuffers[0].mNumberChannels != pDevice->playback.internalChannels) { - layout = ma_stream_layout_deinterleaved; - } - - if (layout == ma_stream_layout_interleaved) { - /* For now we can assume everything is interleaved. */ - UInt32 iBuffer; - for (iBuffer = 0; iBuffer < pBufferList->mNumberBuffers; ++iBuffer) { - if (pBufferList->mBuffers[iBuffer].mNumberChannels == pDevice->playback.internalChannels) { - ma_uint32 frameCountForThisBuffer = pBufferList->mBuffers[iBuffer].mDataByteSize / ma_get_bytes_per_frame(pDevice->playback.internalFormat, pDevice->playback.internalChannels); - if (frameCountForThisBuffer > 0) { - ma_device_handle_backend_data_callback(pDevice, pBufferList->mBuffers[iBuffer].mData, NULL, frameCountForThisBuffer); - } - - /*a_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, " frameCount=%d, mNumberChannels=%d, mDataByteSize=%d\n", (int)frameCount, (int)pBufferList->mBuffers[iBuffer].mNumberChannels, (int)pBufferList->mBuffers[iBuffer].mDataByteSize);*/ - } else { - /* - This case is where the number of channels in the output buffer do not match our internal channels. It could mean that it's - not interleaved, in which case we can't handle right now since miniaudio does not yet support non-interleaved streams. We just - output silence here. - */ - MA_ZERO_MEMORY(pBufferList->mBuffers[iBuffer].mData, pBufferList->mBuffers[iBuffer].mDataByteSize); - /*ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, " WARNING: Outputting silence. frameCount=%d, mNumberChannels=%d, mDataByteSize=%d\n", (int)frameCount, (int)pBufferList->mBuffers[iBuffer].mNumberChannels, (int)pBufferList->mBuffers[iBuffer].mDataByteSize);*/ - } - } - } else { - /* This is the deinterleaved case. We need to update each buffer in groups of internalChannels. This assumes each buffer is the same size. */ - MA_ASSERT(pDevice->playback.internalChannels <= MA_MAX_CHANNELS); /* This should have been validated at initialization time. */ - - /* - For safety we'll check that the internal channels is a multiple of the buffer count. If it's not it means something - very strange has happened and we're not going to support it. - */ - if ((pBufferList->mNumberBuffers % pDevice->playback.internalChannels) == 0) { - ma_uint8 tempBuffer[4096]; - UInt32 iBuffer; - - for (iBuffer = 0; iBuffer < pBufferList->mNumberBuffers; iBuffer += pDevice->playback.internalChannels) { - ma_uint32 frameCountPerBuffer = pBufferList->mBuffers[iBuffer].mDataByteSize / ma_get_bytes_per_sample(pDevice->playback.internalFormat); - ma_uint32 framesRemaining = frameCountPerBuffer; - - while (framesRemaining > 0) { - void* ppDeinterleavedBuffers[MA_MAX_CHANNELS]; - ma_uint32 iChannel; - ma_uint32 framesToRead = sizeof(tempBuffer) / ma_get_bytes_per_frame(pDevice->playback.internalFormat, pDevice->playback.internalChannels); - if (framesToRead > framesRemaining) { - framesToRead = framesRemaining; - } - - ma_device_handle_backend_data_callback(pDevice, tempBuffer, NULL, framesToRead); - - for (iChannel = 0; iChannel < pDevice->playback.internalChannels; ++iChannel) { - ppDeinterleavedBuffers[iChannel] = (void*)ma_offset_ptr(pBufferList->mBuffers[iBuffer+iChannel].mData, (frameCountPerBuffer - framesRemaining) * ma_get_bytes_per_sample(pDevice->playback.internalFormat)); - } - - ma_deinterleave_pcm_frames(pDevice->playback.internalFormat, pDevice->playback.internalChannels, framesToRead, tempBuffer, ppDeinterleavedBuffers); - - framesRemaining -= framesToRead; - } - } - } - } - - (void)pActionFlags; - (void)pTimeStamp; - (void)busNumber; - (void)frameCount; - - return noErr; -} - -static OSStatus ma_on_input__coreaudio(void* pUserData, AudioUnitRenderActionFlags* pActionFlags, const AudioTimeStamp* pTimeStamp, UInt32 busNumber, UInt32 frameCount, AudioBufferList* pUnusedBufferList) -{ - ma_device* pDevice = (ma_device*)pUserData; - AudioBufferList* pRenderedBufferList; - ma_result result; - ma_stream_layout layout; - ma_uint32 iBuffer; - OSStatus status; - - MA_ASSERT(pDevice != NULL); - - pRenderedBufferList = (AudioBufferList*)pDevice->coreaudio.pAudioBufferList; - MA_ASSERT(pRenderedBufferList); - - /* We need to check whether or not we are outputting interleaved or non-interleaved samples. The way we do this is slightly different for each type. */ - layout = ma_stream_layout_interleaved; - if (pRenderedBufferList->mBuffers[0].mNumberChannels != pDevice->capture.internalChannels) { - layout = ma_stream_layout_deinterleaved; - } - - /*ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "INFO: Input Callback: busNumber=%d, frameCount=%d, mNumberBuffers=%d\n", (int)busNumber, (int)frameCount, (int)pRenderedBufferList->mNumberBuffers);*/ - - /* - There has been a situation reported where frame count passed into this function is greater than the capacity of - our capture buffer. There doesn't seem to be a reliable way to determine what the maximum frame count will be, - so we need to instead resort to dynamically reallocating our buffer to ensure it's large enough to capture the - number of frames requested by this callback. - */ - result = ma_device_realloc_AudioBufferList__coreaudio(pDevice, frameCount, pDevice->capture.internalFormat, pDevice->capture.internalChannels, layout); - if (result != MA_SUCCESS) { - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "Failed to allocate AudioBufferList for capture.\n"); - return noErr; - } - - pRenderedBufferList = (AudioBufferList*)pDevice->coreaudio.pAudioBufferList; - MA_ASSERT(pRenderedBufferList); - - /* - When you call AudioUnitRender(), Core Audio tries to be helpful by setting the mDataByteSize to the number of bytes - that were actually rendered. The problem with this is that the next call can fail with -50 due to the size no longer - being set to the capacity of the buffer, but instead the size in bytes of the previous render. This will cause a - problem when a future call to this callback specifies a larger number of frames. - - To work around this we need to explicitly set the size of each buffer to their respective size in bytes. - */ - for (iBuffer = 0; iBuffer < pRenderedBufferList->mNumberBuffers; ++iBuffer) { - pRenderedBufferList->mBuffers[iBuffer].mDataByteSize = pDevice->coreaudio.audioBufferCapInFrames * ma_get_bytes_per_sample(pDevice->capture.internalFormat) * pRenderedBufferList->mBuffers[iBuffer].mNumberChannels; - /*printf("DEBUG: nDataByteSize = %d\n", (int)pRenderedBufferList->mBuffers[iBuffer].mDataByteSize);*/ - } - - status = ((ma_AudioUnitRender_proc)pDevice->pContext->coreaudio.AudioUnitRender)((AudioUnit)pDevice->coreaudio.audioUnitCapture, pActionFlags, pTimeStamp, busNumber, frameCount, pRenderedBufferList); - if (status != noErr) { - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "ERROR: AudioUnitRender() failed with %d.\n", (int)status); - return status; - } - - if (layout == ma_stream_layout_interleaved) { - for (iBuffer = 0; iBuffer < pRenderedBufferList->mNumberBuffers; ++iBuffer) { - if (pRenderedBufferList->mBuffers[iBuffer].mNumberChannels == pDevice->capture.internalChannels) { - ma_device_handle_backend_data_callback(pDevice, NULL, pRenderedBufferList->mBuffers[iBuffer].mData, frameCount); - /*ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, " mDataByteSize=%d.\n", (int)pRenderedBufferList->mBuffers[iBuffer].mDataByteSize);*/ - } else { - /* - This case is where the number of channels in the output buffer do not match our internal channels. It could mean that it's - not interleaved, in which case we can't handle right now since miniaudio does not yet support non-interleaved streams. - */ - ma_uint8 silentBuffer[4096]; - ma_uint32 framesRemaining; - - MA_ZERO_MEMORY(silentBuffer, sizeof(silentBuffer)); - - framesRemaining = frameCount; - while (framesRemaining > 0) { - ma_uint32 framesToSend = sizeof(silentBuffer) / ma_get_bytes_per_frame(pDevice->capture.internalFormat, pDevice->capture.internalChannels); - if (framesToSend > framesRemaining) { - framesToSend = framesRemaining; - } - - ma_device_handle_backend_data_callback(pDevice, NULL, silentBuffer, framesToSend); - - framesRemaining -= framesToSend; - } - - /*ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, " WARNING: Outputting silence. frameCount=%d, mNumberChannels=%d, mDataByteSize=%d\n", (int)frameCount, (int)pRenderedBufferList->mBuffers[iBuffer].mNumberChannels, (int)pRenderedBufferList->mBuffers[iBuffer].mDataByteSize);*/ - } - } - } else { - /* This is the deinterleaved case. We need to interleave the audio data before sending it to the client. This assumes each buffer is the same size. */ - MA_ASSERT(pDevice->capture.internalChannels <= MA_MAX_CHANNELS); /* This should have been validated at initialization time. */ - - /* - For safety we'll check that the internal channels is a multiple of the buffer count. If it's not it means something - very strange has happened and we're not going to support it. - */ - if ((pRenderedBufferList->mNumberBuffers % pDevice->capture.internalChannels) == 0) { - ma_uint8 tempBuffer[4096]; - for (iBuffer = 0; iBuffer < pRenderedBufferList->mNumberBuffers; iBuffer += pDevice->capture.internalChannels) { - ma_uint32 framesRemaining = frameCount; - while (framesRemaining > 0) { - void* ppDeinterleavedBuffers[MA_MAX_CHANNELS]; - ma_uint32 iChannel; - ma_uint32 framesToSend = sizeof(tempBuffer) / ma_get_bytes_per_frame(pDevice->capture.internalFormat, pDevice->capture.internalChannels); - if (framesToSend > framesRemaining) { - framesToSend = framesRemaining; - } - - for (iChannel = 0; iChannel < pDevice->capture.internalChannels; ++iChannel) { - ppDeinterleavedBuffers[iChannel] = (void*)ma_offset_ptr(pRenderedBufferList->mBuffers[iBuffer+iChannel].mData, (frameCount - framesRemaining) * ma_get_bytes_per_sample(pDevice->capture.internalFormat)); - } - - ma_interleave_pcm_frames(pDevice->capture.internalFormat, pDevice->capture.internalChannels, framesToSend, (const void**)ppDeinterleavedBuffers, tempBuffer); - ma_device_handle_backend_data_callback(pDevice, NULL, tempBuffer, framesToSend); - - framesRemaining -= framesToSend; - } - } - } - } - - (void)pActionFlags; - (void)pTimeStamp; - (void)busNumber; - (void)frameCount; - (void)pUnusedBufferList; - - return noErr; -} - -static void on_start_stop__coreaudio(void* pUserData, AudioUnit audioUnit, AudioUnitPropertyID propertyID, AudioUnitScope scope, AudioUnitElement element) -{ - ma_device* pDevice = (ma_device*)pUserData; - MA_ASSERT(pDevice != NULL); - - /* Don't do anything if it looks like we're just reinitializing due to a device switch. */ - if (((audioUnit == pDevice->coreaudio.audioUnitPlayback) && pDevice->coreaudio.isSwitchingPlaybackDevice) || - ((audioUnit == pDevice->coreaudio.audioUnitCapture) && pDevice->coreaudio.isSwitchingCaptureDevice)) { - return; - } - - /* - There's been a report of a deadlock here when triggered by ma_device_uninit(). It looks like - AudioUnitGetProprty (called below) and AudioComponentInstanceDispose (called in ma_device_uninit) - can try waiting on the same lock. I'm going to try working around this by not calling any Core - Audio APIs in the callback when the device has been stopped or uninitialized. - */ - if (ma_device_get_state(pDevice) == ma_device_state_uninitialized || ma_device_get_state(pDevice) == ma_device_state_stopping || ma_device_get_state(pDevice) == ma_device_state_stopped) { - ma_device__on_notification_stopped(pDevice); - } else { - UInt32 isRunning; - UInt32 isRunningSize = sizeof(isRunning); - OSStatus status = ((ma_AudioUnitGetProperty_proc)pDevice->pContext->coreaudio.AudioUnitGetProperty)(audioUnit, kAudioOutputUnitProperty_IsRunning, scope, element, &isRunning, &isRunningSize); - if (status != noErr) { - goto done; /* Don't really know what to do in this case... just ignore it, I suppose... */ - } - - if (!isRunning) { - /* - The stop event is a bit annoying in Core Audio because it will be called when we automatically switch the default device. Some scenarios to consider: - - 1) When the device is unplugged, this will be called _before_ the default device change notification. - 2) When the device is changed via the default device change notification, this will be called _after_ the switch. - - For case #1, we just check if there's a new default device available. If so, we just ignore the stop event. For case #2 we check a flag. - */ - if (((audioUnit == pDevice->coreaudio.audioUnitPlayback) && pDevice->coreaudio.isDefaultPlaybackDevice) || - ((audioUnit == pDevice->coreaudio.audioUnitCapture) && pDevice->coreaudio.isDefaultCaptureDevice)) { - /* - It looks like the device is switching through an external event, such as the user unplugging the device or changing the default device - via the operating system's sound settings. If we're re-initializing the device, we just terminate because we want the stopping of the - device to be seamless to the client (we don't want them receiving the stopped event and thinking that the device has stopped when it - hasn't!). - */ - if (((audioUnit == pDevice->coreaudio.audioUnitPlayback) && pDevice->coreaudio.isSwitchingPlaybackDevice) || - ((audioUnit == pDevice->coreaudio.audioUnitCapture) && pDevice->coreaudio.isSwitchingCaptureDevice)) { - goto done; - } - - /* - Getting here means the device is not reinitializing which means it may have been unplugged. From what I can see, it looks like Core Audio - will try switching to the new default device seamlessly. We need to somehow find a way to determine whether or not Core Audio will most - likely be successful in switching to the new device. - - TODO: Try to predict if Core Audio will switch devices. If not, the stopped callback needs to be posted. - */ - goto done; - } - - /* Getting here means we need to stop the device. */ - ma_device__on_notification_stopped(pDevice); - } - } - - (void)propertyID; /* Unused. */ - -done: - /* Always signal the stop event. It's possible for the "else" case to get hit which can happen during an interruption. */ - ma_event_signal(&pDevice->coreaudio.stopEvent); -} - -#if defined(MA_APPLE_DESKTOP) -static ma_spinlock g_DeviceTrackingInitLock_CoreAudio = 0; /* A spinlock for mutal exclusion of the init/uninit of the global tracking data. Initialization to 0 is what we need. */ -static ma_uint32 g_DeviceTrackingInitCounter_CoreAudio = 0; -static ma_mutex g_DeviceTrackingMutex_CoreAudio; -static ma_device** g_ppTrackedDevices_CoreAudio = NULL; -static ma_uint32 g_TrackedDeviceCap_CoreAudio = 0; -static ma_uint32 g_TrackedDeviceCount_CoreAudio = 0; - -static OSStatus ma_default_device_changed__coreaudio(AudioObjectID objectID, UInt32 addressCount, const AudioObjectPropertyAddress* pAddresses, void* pUserData) -{ - ma_device_type deviceType; - - /* Not sure if I really need to check this, but it makes me feel better. */ - if (addressCount == 0) { - return noErr; - } - - if (pAddresses[0].mSelector == kAudioHardwarePropertyDefaultOutputDevice) { - deviceType = ma_device_type_playback; - } else if (pAddresses[0].mSelector == kAudioHardwarePropertyDefaultInputDevice) { - deviceType = ma_device_type_capture; - } else { - return noErr; /* Should never hit this. */ - } - - ma_mutex_lock(&g_DeviceTrackingMutex_CoreAudio); - { - ma_uint32 iDevice; - for (iDevice = 0; iDevice < g_TrackedDeviceCount_CoreAudio; iDevice += 1) { - ma_result reinitResult; - ma_device* pDevice; - - pDevice = g_ppTrackedDevices_CoreAudio[iDevice]; - if (pDevice->type == deviceType || pDevice->type == ma_device_type_duplex) { - if (deviceType == ma_device_type_playback) { - pDevice->coreaudio.isSwitchingPlaybackDevice = MA_TRUE; - reinitResult = ma_device_reinit_internal__coreaudio(pDevice, deviceType, MA_TRUE); - pDevice->coreaudio.isSwitchingPlaybackDevice = MA_FALSE; - } else { - pDevice->coreaudio.isSwitchingCaptureDevice = MA_TRUE; - reinitResult = ma_device_reinit_internal__coreaudio(pDevice, deviceType, MA_TRUE); - pDevice->coreaudio.isSwitchingCaptureDevice = MA_FALSE; - } - - if (reinitResult == MA_SUCCESS) { - ma_device__post_init_setup(pDevice, deviceType); - - /* Restart the device if required. If this fails we need to stop the device entirely. */ - if (ma_device_get_state(pDevice) == ma_device_state_started) { - OSStatus status; - if (deviceType == ma_device_type_playback) { - status = ((ma_AudioOutputUnitStart_proc)pDevice->pContext->coreaudio.AudioOutputUnitStart)((AudioUnit)pDevice->coreaudio.audioUnitPlayback); - if (status != noErr) { - if (pDevice->type == ma_device_type_duplex) { - ((ma_AudioOutputUnitStop_proc)pDevice->pContext->coreaudio.AudioOutputUnitStop)((AudioUnit)pDevice->coreaudio.audioUnitCapture); - } - ma_device__set_state(pDevice, ma_device_state_stopped); - } - } else if (deviceType == ma_device_type_capture) { - status = ((ma_AudioOutputUnitStart_proc)pDevice->pContext->coreaudio.AudioOutputUnitStart)((AudioUnit)pDevice->coreaudio.audioUnitCapture); - if (status != noErr) { - if (pDevice->type == ma_device_type_duplex) { - ((ma_AudioOutputUnitStop_proc)pDevice->pContext->coreaudio.AudioOutputUnitStop)((AudioUnit)pDevice->coreaudio.audioUnitPlayback); - } - ma_device__set_state(pDevice, ma_device_state_stopped); - } - } - } - - ma_device__on_notification_rerouted(pDevice); - } - } - } - } - ma_mutex_unlock(&g_DeviceTrackingMutex_CoreAudio); - - /* Unused parameters. */ - (void)objectID; - (void)pUserData; - - return noErr; -} - -static ma_result ma_context__init_device_tracking__coreaudio(ma_context* pContext) -{ - MA_ASSERT(pContext != NULL); - - ma_spinlock_lock(&g_DeviceTrackingInitLock_CoreAudio); - { - /* Don't do anything if we've already initialized device tracking. */ - if (g_DeviceTrackingInitCounter_CoreAudio == 0) { - AudioObjectPropertyAddress propAddress; - propAddress.mScope = kAudioObjectPropertyScopeGlobal; - propAddress.mElement = AUDIO_OBJECT_PROPERTY_ELEMENT; - - ma_mutex_init(&g_DeviceTrackingMutex_CoreAudio); - - propAddress.mSelector = kAudioHardwarePropertyDefaultInputDevice; - ((ma_AudioObjectAddPropertyListener_proc)pContext->coreaudio.AudioObjectAddPropertyListener)(kAudioObjectSystemObject, &propAddress, &ma_default_device_changed__coreaudio, NULL); - - propAddress.mSelector = kAudioHardwarePropertyDefaultOutputDevice; - ((ma_AudioObjectAddPropertyListener_proc)pContext->coreaudio.AudioObjectAddPropertyListener)(kAudioObjectSystemObject, &propAddress, &ma_default_device_changed__coreaudio, NULL); - - } - g_DeviceTrackingInitCounter_CoreAudio += 1; - } - ma_spinlock_unlock(&g_DeviceTrackingInitLock_CoreAudio); - - return MA_SUCCESS; -} - -static ma_result ma_context__uninit_device_tracking__coreaudio(ma_context* pContext) -{ - MA_ASSERT(pContext != NULL); - - ma_spinlock_lock(&g_DeviceTrackingInitLock_CoreAudio); - { - if (g_DeviceTrackingInitCounter_CoreAudio > 0) - g_DeviceTrackingInitCounter_CoreAudio -= 1; - - if (g_DeviceTrackingInitCounter_CoreAudio == 0) { - AudioObjectPropertyAddress propAddress; - propAddress.mScope = kAudioObjectPropertyScopeGlobal; - propAddress.mElement = AUDIO_OBJECT_PROPERTY_ELEMENT; - - propAddress.mSelector = kAudioHardwarePropertyDefaultInputDevice; - ((ma_AudioObjectRemovePropertyListener_proc)pContext->coreaudio.AudioObjectRemovePropertyListener)(kAudioObjectSystemObject, &propAddress, &ma_default_device_changed__coreaudio, NULL); - - propAddress.mSelector = kAudioHardwarePropertyDefaultOutputDevice; - ((ma_AudioObjectRemovePropertyListener_proc)pContext->coreaudio.AudioObjectRemovePropertyListener)(kAudioObjectSystemObject, &propAddress, &ma_default_device_changed__coreaudio, NULL); - - /* At this point there should be no tracked devices. If not there's an error somewhere. */ - if (g_ppTrackedDevices_CoreAudio != NULL) { - ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_WARNING, "You have uninitialized all contexts while an associated device is still active."); - ma_spinlock_unlock(&g_DeviceTrackingInitLock_CoreAudio); - return MA_INVALID_OPERATION; - } - - ma_mutex_uninit(&g_DeviceTrackingMutex_CoreAudio); - } - } - ma_spinlock_unlock(&g_DeviceTrackingInitLock_CoreAudio); - - return MA_SUCCESS; -} - -static ma_result ma_device__track__coreaudio(ma_device* pDevice) -{ - MA_ASSERT(pDevice != NULL); - - ma_mutex_lock(&g_DeviceTrackingMutex_CoreAudio); - { - /* Allocate memory if required. */ - if (g_TrackedDeviceCap_CoreAudio <= g_TrackedDeviceCount_CoreAudio) { - ma_uint32 newCap; - ma_device** ppNewDevices; - - newCap = g_TrackedDeviceCap_CoreAudio * 2; - if (newCap == 0) { - newCap = 1; - } - - ppNewDevices = (ma_device**)ma_realloc(g_ppTrackedDevices_CoreAudio, sizeof(*g_ppTrackedDevices_CoreAudio)*newCap, &pDevice->pContext->allocationCallbacks); - if (ppNewDevices == NULL) { - ma_mutex_unlock(&g_DeviceTrackingMutex_CoreAudio); - return MA_OUT_OF_MEMORY; - } - - g_ppTrackedDevices_CoreAudio = ppNewDevices; - g_TrackedDeviceCap_CoreAudio = newCap; - } - - g_ppTrackedDevices_CoreAudio[g_TrackedDeviceCount_CoreAudio] = pDevice; - g_TrackedDeviceCount_CoreAudio += 1; - } - ma_mutex_unlock(&g_DeviceTrackingMutex_CoreAudio); - - return MA_SUCCESS; -} - -static ma_result ma_device__untrack__coreaudio(ma_device* pDevice) -{ - MA_ASSERT(pDevice != NULL); - - ma_mutex_lock(&g_DeviceTrackingMutex_CoreAudio); - { - ma_uint32 iDevice; - for (iDevice = 0; iDevice < g_TrackedDeviceCount_CoreAudio; iDevice += 1) { - if (g_ppTrackedDevices_CoreAudio[iDevice] == pDevice) { - /* We've found the device. We now need to remove it from the list. */ - ma_uint32 jDevice; - for (jDevice = iDevice; jDevice < g_TrackedDeviceCount_CoreAudio-1; jDevice += 1) { - g_ppTrackedDevices_CoreAudio[jDevice] = g_ppTrackedDevices_CoreAudio[jDevice+1]; - } - - g_TrackedDeviceCount_CoreAudio -= 1; - - /* If there's nothing else in the list we need to free memory. */ - if (g_TrackedDeviceCount_CoreAudio == 0) { - ma_free(g_ppTrackedDevices_CoreAudio, &pDevice->pContext->allocationCallbacks); - g_ppTrackedDevices_CoreAudio = NULL; - g_TrackedDeviceCap_CoreAudio = 0; - } - - break; - } - } - } - ma_mutex_unlock(&g_DeviceTrackingMutex_CoreAudio); - - return MA_SUCCESS; -} -#endif - -#if defined(MA_APPLE_MOBILE) -@interface ma_ios_notification_handler:NSObject { - ma_device* m_pDevice; -} -@end - -@implementation ma_ios_notification_handler --(id)init:(ma_device*)pDevice -{ - self = [super init]; - m_pDevice = pDevice; - - /* For route changes. */ - [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(handle_route_change:) name:AVAudioSessionRouteChangeNotification object:[AVAudioSession sharedInstance]]; - - /* For interruptions. */ - [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(handle_interruption:) name:AVAudioSessionInterruptionNotification object:[AVAudioSession sharedInstance]]; - - return self; -} - --(void)dealloc -{ - [self remove_handler]; - - #if defined(__has_feature) - #if !__has_feature(objc_arc) - [super dealloc]; - #endif - #endif -} - --(void)remove_handler -{ - [[NSNotificationCenter defaultCenter] removeObserver:self name:AVAudioSessionRouteChangeNotification object:nil]; - [[NSNotificationCenter defaultCenter] removeObserver:self name:AVAudioSessionInterruptionNotification object:nil]; -} - --(void)handle_interruption:(NSNotification*)pNotification -{ - NSInteger type = [[[pNotification userInfo] objectForKey:AVAudioSessionInterruptionTypeKey] integerValue]; - switch (type) - { - case AVAudioSessionInterruptionTypeBegan: - { - ma_log_postf(ma_device_get_log(m_pDevice), MA_LOG_LEVEL_INFO, "[Core Audio] Interruption: AVAudioSessionInterruptionTypeBegan\n"); - - /* - Core Audio will have stopped the internal device automatically, but we need explicitly - stop it at a higher level to ensure miniaudio-specific state is updated for consistency. - */ - ma_device_stop(m_pDevice); - - /* - Fire the notification after the device has been stopped to ensure it's in the correct - state when the notification handler is invoked. - */ - ma_device__on_notification_interruption_began(m_pDevice); - } break; - - case AVAudioSessionInterruptionTypeEnded: - { - ma_log_postf(ma_device_get_log(m_pDevice), MA_LOG_LEVEL_INFO, "[Core Audio] Interruption: AVAudioSessionInterruptionTypeEnded\n"); - ma_device__on_notification_interruption_ended(m_pDevice); - } break; - } -} - --(void)handle_route_change:(NSNotification*)pNotification -{ - AVAudioSession* pSession = [AVAudioSession sharedInstance]; - - NSInteger reason = [[[pNotification userInfo] objectForKey:AVAudioSessionRouteChangeReasonKey] integerValue]; - switch (reason) - { - case AVAudioSessionRouteChangeReasonOldDeviceUnavailable: - { - ma_log_postf(ma_device_get_log(m_pDevice), MA_LOG_LEVEL_INFO, "[Core Audio] Route Changed: AVAudioSessionRouteChangeReasonOldDeviceUnavailable\n"); - } break; - - case AVAudioSessionRouteChangeReasonNewDeviceAvailable: - { - ma_log_postf(ma_device_get_log(m_pDevice), MA_LOG_LEVEL_INFO, "[Core Audio] Route Changed: AVAudioSessionRouteChangeReasonNewDeviceAvailable\n"); - } break; - - case AVAudioSessionRouteChangeReasonNoSuitableRouteForCategory: - { - ma_log_postf(ma_device_get_log(m_pDevice), MA_LOG_LEVEL_INFO, "[Core Audio] Route Changed: AVAudioSessionRouteChangeReasonNoSuitableRouteForCategory\n"); - } break; - - case AVAudioSessionRouteChangeReasonWakeFromSleep: - { - ma_log_postf(ma_device_get_log(m_pDevice), MA_LOG_LEVEL_INFO, "[Core Audio] Route Changed: AVAudioSessionRouteChangeReasonWakeFromSleep\n"); - } break; - - case AVAudioSessionRouteChangeReasonOverride: - { - ma_log_postf(ma_device_get_log(m_pDevice), MA_LOG_LEVEL_INFO, "[Core Audio] Route Changed: AVAudioSessionRouteChangeReasonOverride\n"); - } break; - - case AVAudioSessionRouteChangeReasonCategoryChange: - { - ma_log_postf(ma_device_get_log(m_pDevice), MA_LOG_LEVEL_INFO, "[Core Audio] Route Changed: AVAudioSessionRouteChangeReasonCategoryChange\n"); - } break; - - case AVAudioSessionRouteChangeReasonUnknown: - default: - { - ma_log_postf(ma_device_get_log(m_pDevice), MA_LOG_LEVEL_INFO, "[Core Audio] Route Changed: AVAudioSessionRouteChangeReasonUnknown\n"); - } break; - } - - ma_log_postf(ma_device_get_log(m_pDevice), MA_LOG_LEVEL_DEBUG, "[Core Audio] Changing Route. inputNumberChannels=%d; outputNumberOfChannels=%d\n", (int)pSession.inputNumberOfChannels, (int)pSession.outputNumberOfChannels); - - /* Let the application know about the route change. */ - ma_device__on_notification_rerouted(m_pDevice); -} -@end -#endif - -static ma_result ma_device_uninit__coreaudio(ma_device* pDevice) -{ - MA_ASSERT(pDevice != NULL); - MA_ASSERT(ma_device_get_state(pDevice) == ma_device_state_uninitialized); - -#if defined(MA_APPLE_DESKTOP) - /* - Make sure we're no longer tracking the device. It doesn't matter if we call this for a non-default device because it'll - just gracefully ignore it. - */ - ma_device__untrack__coreaudio(pDevice); -#endif -#if defined(MA_APPLE_MOBILE) - if (pDevice->coreaudio.pNotificationHandler != NULL) { - ma_ios_notification_handler* pNotificationHandler = (MA_BRIDGE_TRANSFER ma_ios_notification_handler*)pDevice->coreaudio.pNotificationHandler; - [pNotificationHandler remove_handler]; - } -#endif - - if (pDevice->coreaudio.audioUnitCapture != NULL) { - ((ma_AudioComponentInstanceDispose_proc)pDevice->pContext->coreaudio.AudioComponentInstanceDispose)((AudioUnit)pDevice->coreaudio.audioUnitCapture); - } - if (pDevice->coreaudio.audioUnitPlayback != NULL) { - ((ma_AudioComponentInstanceDispose_proc)pDevice->pContext->coreaudio.AudioComponentInstanceDispose)((AudioUnit)pDevice->coreaudio.audioUnitPlayback); - } - - if (pDevice->coreaudio.pAudioBufferList) { - ma_free(pDevice->coreaudio.pAudioBufferList, &pDevice->pContext->allocationCallbacks); - } - - return MA_SUCCESS; -} - -typedef struct -{ - ma_bool32 allowNominalSampleRateChange; - - /* Input. */ - ma_format formatIn; - ma_uint32 channelsIn; - ma_uint32 sampleRateIn; - ma_channel channelMapIn[MA_MAX_CHANNELS]; - ma_uint32 periodSizeInFramesIn; - ma_uint32 periodSizeInMillisecondsIn; - ma_uint32 periodsIn; - ma_share_mode shareMode; - ma_performance_profile performanceProfile; - ma_bool32 registerStopEvent; - - /* Output. */ -#if defined(MA_APPLE_DESKTOP) - AudioObjectID deviceObjectID; -#endif - AudioComponent component; - AudioUnit audioUnit; - AudioBufferList* pAudioBufferList; /* Only used for input devices. */ - ma_format formatOut; - ma_uint32 channelsOut; - ma_uint32 sampleRateOut; - ma_channel channelMapOut[MA_MAX_CHANNELS]; - ma_uint32 periodSizeInFramesOut; - ma_uint32 periodsOut; - char deviceName[256]; -} ma_device_init_internal_data__coreaudio; - -static ma_result ma_device_init_internal__coreaudio(ma_context* pContext, ma_device_type deviceType, const ma_device_id* pDeviceID, ma_device_init_internal_data__coreaudio* pData, void* pDevice_DoNotReference) /* <-- pDevice is typed as void* intentionally so as to avoid accidentally referencing it. */ -{ - ma_result result = MA_SUCCESS; - OSStatus status; - UInt32 enableIOFlag; - AudioStreamBasicDescription bestFormat; - ma_uint32 actualPeriodSizeInFrames; - AURenderCallbackStruct callbackInfo; -#if defined(MA_APPLE_DESKTOP) - AudioObjectID deviceObjectID; -#endif - - /* This API should only be used for a single device type: playback or capture. No full-duplex mode. */ - if (deviceType == ma_device_type_duplex) { - return MA_INVALID_ARGS; - } - - MA_ASSERT(pContext != NULL); - MA_ASSERT(deviceType == ma_device_type_playback || deviceType == ma_device_type_capture); - -#if defined(MA_APPLE_DESKTOP) - pData->deviceObjectID = 0; -#endif - pData->component = NULL; - pData->audioUnit = NULL; - pData->pAudioBufferList = NULL; - -#if defined(MA_APPLE_DESKTOP) - result = ma_find_AudioObjectID(pContext, deviceType, pDeviceID, &deviceObjectID); - if (result != MA_SUCCESS) { - return result; - } - - pData->deviceObjectID = deviceObjectID; -#endif - - /* Core audio doesn't really use the notion of a period so we can leave this unmodified, but not too over the top. */ - pData->periodsOut = pData->periodsIn; - if (pData->periodsOut == 0) { - pData->periodsOut = MA_DEFAULT_PERIODS; - } - if (pData->periodsOut > 16) { - pData->periodsOut = 16; - } - - - /* Audio unit. */ - status = ((ma_AudioComponentInstanceNew_proc)pContext->coreaudio.AudioComponentInstanceNew)((AudioComponent)pContext->coreaudio.component, (AudioUnit*)&pData->audioUnit); - if (status != noErr) { - return ma_result_from_OSStatus(status); - } - - - /* The input/output buses need to be explicitly enabled and disabled. We set the flag based on the output unit first, then we just swap it for input. */ - enableIOFlag = 1; - if (deviceType == ma_device_type_capture) { - enableIOFlag = 0; - } - - status = ((ma_AudioUnitSetProperty_proc)pContext->coreaudio.AudioUnitSetProperty)(pData->audioUnit, kAudioOutputUnitProperty_EnableIO, kAudioUnitScope_Output, MA_COREAUDIO_OUTPUT_BUS, &enableIOFlag, sizeof(enableIOFlag)); - if (status != noErr) { - ((ma_AudioComponentInstanceDispose_proc)pContext->coreaudio.AudioComponentInstanceDispose)(pData->audioUnit); - return ma_result_from_OSStatus(status); - } - - enableIOFlag = (enableIOFlag == 0) ? 1 : 0; - status = ((ma_AudioUnitSetProperty_proc)pContext->coreaudio.AudioUnitSetProperty)(pData->audioUnit, kAudioOutputUnitProperty_EnableIO, kAudioUnitScope_Input, MA_COREAUDIO_INPUT_BUS, &enableIOFlag, sizeof(enableIOFlag)); - if (status != noErr) { - ((ma_AudioComponentInstanceDispose_proc)pContext->coreaudio.AudioComponentInstanceDispose)(pData->audioUnit); - return ma_result_from_OSStatus(status); - } - - - /* Set the device to use with this audio unit. This is only used on desktop since we are using defaults on mobile. */ -#if defined(MA_APPLE_DESKTOP) - status = ((ma_AudioUnitSetProperty_proc)pContext->coreaudio.AudioUnitSetProperty)(pData->audioUnit, kAudioOutputUnitProperty_CurrentDevice, kAudioUnitScope_Global, 0, &deviceObjectID, sizeof(deviceObjectID)); - if (status != noErr) { - ((ma_AudioComponentInstanceDispose_proc)pContext->coreaudio.AudioComponentInstanceDispose)(pData->audioUnit); - return ma_result_from_OSStatus(result); - } -#else - /* - For some reason it looks like Apple is only allowing selection of the input device. There does not appear to be any way to change - the default output route. I have no idea why this is like this, but for now we'll only be able to configure capture devices. - */ - if (pDeviceID != NULL) { - if (deviceType == ma_device_type_capture) { - ma_bool32 found = MA_FALSE; - NSArray *pInputs = [[[AVAudioSession sharedInstance] currentRoute] inputs]; - for (AVAudioSessionPortDescription* pPortDesc in pInputs) { - if (strcmp(pDeviceID->coreaudio, [pPortDesc.UID UTF8String]) == 0) { - [[AVAudioSession sharedInstance] setPreferredInput:pPortDesc error:nil]; - found = MA_TRUE; - break; - } - } - - if (found == MA_FALSE) { - return MA_DOES_NOT_EXIST; - } - } - } -#endif - - /* - Format. This is the hardest part of initialization because there's a few variables to take into account. - 1) The format must be supported by the device. - 2) The format must be supported miniaudio. - 3) There's a priority that miniaudio prefers. - - Ideally we would like to use a format that's as close to the hardware as possible so we can get as close to a passthrough as possible. The - most important property is the sample rate. miniaudio can do format conversion for any sample rate and channel count, but cannot do the same - for the sample data format. If the sample data format is not supported by miniaudio it must be ignored completely. - - On mobile platforms this is a bit different. We just force the use of whatever the audio unit's current format is set to. - */ - { - AudioStreamBasicDescription origFormat; - UInt32 origFormatSize = sizeof(origFormat); - AudioUnitScope formatScope = (deviceType == ma_device_type_playback) ? kAudioUnitScope_Input : kAudioUnitScope_Output; - AudioUnitElement formatElement = (deviceType == ma_device_type_playback) ? MA_COREAUDIO_OUTPUT_BUS : MA_COREAUDIO_INPUT_BUS; - - if (deviceType == ma_device_type_playback) { - status = ((ma_AudioUnitGetProperty_proc)pContext->coreaudio.AudioUnitGetProperty)(pData->audioUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Output, MA_COREAUDIO_OUTPUT_BUS, &origFormat, &origFormatSize); - } else { - status = ((ma_AudioUnitGetProperty_proc)pContext->coreaudio.AudioUnitGetProperty)(pData->audioUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Input, MA_COREAUDIO_INPUT_BUS, &origFormat, &origFormatSize); - } - if (status != noErr) { - ((ma_AudioComponentInstanceDispose_proc)pContext->coreaudio.AudioComponentInstanceDispose)(pData->audioUnit); - return ma_result_from_OSStatus(status); - } - - #if defined(MA_APPLE_DESKTOP) - result = ma_find_best_format__coreaudio(pContext, deviceObjectID, deviceType, pData->formatIn, pData->channelsIn, pData->sampleRateIn, &origFormat, &bestFormat); - if (result != MA_SUCCESS) { - ((ma_AudioComponentInstanceDispose_proc)pContext->coreaudio.AudioComponentInstanceDispose)(pData->audioUnit); - return result; - } - - /* - Technical Note TN2091: Device input using the HAL Output Audio Unit - https://developer.apple.com/library/archive/technotes/tn2091/_index.html - - This documentation says the following: - - The internal AudioConverter can handle any *simple* conversion. Typically, this means that a client can specify ANY - variant of the PCM formats. Consequently, the device's sample rate should match the desired sample rate. If sample rate - conversion is needed, it can be accomplished by buffering the input and converting the data on a separate thread with - another AudioConverter. - - The important part here is the mention that it can handle *simple* conversions, which does *not* include sample rate. We - therefore want to ensure the sample rate stays consistent. This document is specifically for input, but I'm going to play it - safe and apply the same rule to output as well. - - I have tried going against the documentation by setting the sample rate anyway, but this just results in AudioUnitRender() - returning a result code of -10863. I have also tried changing the format directly on the input scope on the input bus, but - this just results in `ca_require: IsStreamFormatWritable(inScope, inElement) NotWritable` when trying to set the format. - - Something that does seem to work, however, has been setting the nominal sample rate on the device object. The problem with - this, however, is that it actually changes the sample rate at the operating system level and not just the application. This - could be intrusive to the user, however, so I don't think it's wise to make this the default. Instead I'm making this a - configuration option. When the `coreaudio.allowNominalSampleRateChange` config option is set to true, changing the sample - rate will be allowed. Otherwise it'll be fixed to the current sample rate. To check the system-defined sample rate, run - the Audio MIDI Setup program that comes installed on macOS and observe how the sample rate changes as the sample rate is - changed by miniaudio. - */ - if (pData->allowNominalSampleRateChange) { - AudioValueRange sampleRateRange; - AudioObjectPropertyAddress propAddress; - - sampleRateRange.mMinimum = bestFormat.mSampleRate; - sampleRateRange.mMaximum = bestFormat.mSampleRate; - - propAddress.mSelector = kAudioDevicePropertyNominalSampleRate; - propAddress.mScope = (deviceType == ma_device_type_playback) ? kAudioObjectPropertyScopeOutput : kAudioObjectPropertyScopeInput; - propAddress.mElement = AUDIO_OBJECT_PROPERTY_ELEMENT; - - status = ((ma_AudioObjectSetPropertyData_proc)pContext->coreaudio.AudioObjectSetPropertyData)(deviceObjectID, &propAddress, 0, NULL, sizeof(sampleRateRange), &sampleRateRange); - if (status != noErr) { - bestFormat.mSampleRate = origFormat.mSampleRate; - } - } else { - bestFormat.mSampleRate = origFormat.mSampleRate; - } - - status = ((ma_AudioUnitSetProperty_proc)pContext->coreaudio.AudioUnitSetProperty)(pData->audioUnit, kAudioUnitProperty_StreamFormat, formatScope, formatElement, &bestFormat, sizeof(bestFormat)); - if (status != noErr) { - /* We failed to set the format, so fall back to the current format of the audio unit. */ - bestFormat = origFormat; - } - #else - bestFormat = origFormat; - - /* - Sample rate is a little different here because for some reason kAudioUnitProperty_StreamFormat returns 0... Oh well. We need to instead try - setting the sample rate to what the user has requested and then just see the results of it. Need to use some Objective-C here for this since - it depends on Apple's AVAudioSession API. To do this we just get the shared AVAudioSession instance and then set it. Note that from what I - can tell, it looks like the sample rate is shared between playback and capture for everything. - */ - @autoreleasepool { - AVAudioSession* pAudioSession = [AVAudioSession sharedInstance]; - MA_ASSERT(pAudioSession != NULL); - - [pAudioSession setPreferredSampleRate:(double)pData->sampleRateIn error:nil]; - bestFormat.mSampleRate = pAudioSession.sampleRate; - - /* - I've had a report that the channel count returned by AudioUnitGetProperty above is inconsistent with - AVAudioSession outputNumberOfChannels. I'm going to try using the AVAudioSession values instead. - - UPDATE 20/02/2025: - When testing on the simulator with an iPhone 15 and iOS 17 I get an error when initializing the audio - unit if set the input channels to pAudioSession.inputNumberOfChannels. What is happening is the channel - count returned from AudioUnitGetProperty() above is set to 2, but pAudioSession is reporting a channel - count of 1. When this happens, the call to AudioUnitSetProprty() below just down below will succeed, but - AudioUnitInitialize() further down will fail. The only solution I have come up with is to not set the - channel count to pAudioSession.inputNumberOfChannels. - */ - if (deviceType == ma_device_type_playback) { - bestFormat.mChannelsPerFrame = (UInt32)pAudioSession.outputNumberOfChannels; - } - - #if 0 - if (deviceType == ma_device_type_capture) { - /*printf("DEBUG: bestFormat.mChannelsPerFrame = %d; pAudioSession.inputNumberOfChannels = %d\n", (int)bestFormat.mChannelsPerFrame, (int)pAudioSession.inputNumberOfChannels);*/ - bestFormat.mChannelsPerFrame = (UInt32)pAudioSession.inputNumberOfChannels; - } - #endif - } - - - status = ((ma_AudioUnitSetProperty_proc)pContext->coreaudio.AudioUnitSetProperty)(pData->audioUnit, kAudioUnitProperty_StreamFormat, formatScope, formatElement, &bestFormat, sizeof(bestFormat)); - if (status != noErr) { - ((ma_AudioComponentInstanceDispose_proc)pContext->coreaudio.AudioComponentInstanceDispose)(pData->audioUnit); - return ma_result_from_OSStatus(status); - } - #endif - - result = ma_format_from_AudioStreamBasicDescription(&bestFormat, &pData->formatOut); - if (result != MA_SUCCESS) { - ((ma_AudioComponentInstanceDispose_proc)pContext->coreaudio.AudioComponentInstanceDispose)(pData->audioUnit); - return result; - } - - if (pData->formatOut == ma_format_unknown) { - ((ma_AudioComponentInstanceDispose_proc)pContext->coreaudio.AudioComponentInstanceDispose)(pData->audioUnit); - return MA_FORMAT_NOT_SUPPORTED; - } - - pData->channelsOut = bestFormat.mChannelsPerFrame; - pData->sampleRateOut = (ma_uint32)bestFormat.mSampleRate; - } - - /* Clamp the channel count for safety. */ - if (pData->channelsOut > MA_MAX_CHANNELS) { - pData->channelsOut = MA_MAX_CHANNELS; - } - - /* - Internal channel map. This is weird in my testing. If I use the AudioObject to get the - channel map, the channel descriptions are set to "Unknown" for some reason. To work around - this it looks like retrieving it from the AudioUnit will work. However, and this is where - it gets weird, it doesn't seem to work with capture devices, nor at all on iOS... Therefore - I'm going to fall back to a default assumption in these cases. - */ -#if defined(MA_APPLE_DESKTOP) - result = ma_get_AudioUnit_channel_map(pContext, pData->audioUnit, deviceType, pData->channelMapOut, pData->channelsOut); - if (result != MA_SUCCESS) { - #if 0 - /* Try falling back to the channel map from the AudioObject. */ - result = ma_get_AudioObject_channel_map(pContext, deviceObjectID, deviceType, pData->channelMapOut, pData->channelsOut); - if (result != MA_SUCCESS) { - return result; - } - #else - /* Fall back to default assumptions. */ - ma_channel_map_init_standard(ma_standard_channel_map_default, pData->channelMapOut, ma_countof(pData->channelMapOut), pData->channelsOut); - #endif - } -#else - /* TODO: Figure out how to get the channel map using AVAudioSession. */ - ma_channel_map_init_standard(ma_standard_channel_map_default, pData->channelMapOut, ma_countof(pData->channelMapOut), pData->channelsOut); -#endif - - - /* Buffer size. Not allowing this to be configurable on iOS. */ - if (pData->periodSizeInFramesIn == 0) { - if (pData->periodSizeInMillisecondsIn == 0) { - if (pData->performanceProfile == ma_performance_profile_low_latency) { - actualPeriodSizeInFrames = ma_calculate_buffer_size_in_frames_from_milliseconds(MA_DEFAULT_PERIOD_SIZE_IN_MILLISECONDS_LOW_LATENCY, pData->sampleRateOut); - } else { - actualPeriodSizeInFrames = ma_calculate_buffer_size_in_frames_from_milliseconds(MA_DEFAULT_PERIOD_SIZE_IN_MILLISECONDS_CONSERVATIVE, pData->sampleRateOut); - } - } else { - actualPeriodSizeInFrames = ma_calculate_buffer_size_in_frames_from_milliseconds(pData->periodSizeInMillisecondsIn, pData->sampleRateOut); - } - } else { - actualPeriodSizeInFrames = pData->periodSizeInFramesIn; - } - -#if defined(MA_APPLE_DESKTOP) - result = ma_set_AudioObject_buffer_size_in_frames(pContext, deviceObjectID, deviceType, &actualPeriodSizeInFrames); - if (result != MA_SUCCESS) { - return result; - } -#else - /* - On iOS, the size of the IO buffer needs to be specified in seconds and is a floating point - number. I don't trust any potential truncation errors due to converting from float to integer - so I'm going to explicitly set the actual period size to the next power of 2. - */ - @autoreleasepool { - AVAudioSession* pAudioSession = [AVAudioSession sharedInstance]; - MA_ASSERT(pAudioSession != NULL); - - [pAudioSession setPreferredIOBufferDuration:((float)actualPeriodSizeInFrames / pAudioSession.sampleRate) error:nil]; - actualPeriodSizeInFrames = ma_next_power_of_2((ma_uint32)(pAudioSession.IOBufferDuration * pAudioSession.sampleRate)); - } -#endif - - - /* - During testing I discovered that the buffer size can be too big. You'll get an error like this: - - kAudioUnitErr_TooManyFramesToProcess : inFramesToProcess=4096, mMaxFramesPerSlice=512 - - Note how inFramesToProcess is smaller than mMaxFramesPerSlice. To fix, we need to set kAudioUnitProperty_MaximumFramesPerSlice to that - of the size of our buffer, or do it the other way around and set our buffer size to the kAudioUnitProperty_MaximumFramesPerSlice. - */ - status = ((ma_AudioUnitSetProperty_proc)pContext->coreaudio.AudioUnitSetProperty)(pData->audioUnit, kAudioUnitProperty_MaximumFramesPerSlice, kAudioUnitScope_Global, 0, &actualPeriodSizeInFrames, sizeof(actualPeriodSizeInFrames)); - if (status != noErr) { - ((ma_AudioComponentInstanceDispose_proc)pContext->coreaudio.AudioComponentInstanceDispose)(pData->audioUnit); - return ma_result_from_OSStatus(status); - } - - pData->periodSizeInFramesOut = (ma_uint32)actualPeriodSizeInFrames; - - /* We need a buffer list if this is an input device. We render into this in the input callback. */ - if (deviceType == ma_device_type_capture) { - ma_bool32 isInterleaved = (bestFormat.mFormatFlags & kAudioFormatFlagIsNonInterleaved) == 0; - AudioBufferList* pBufferList; - - pBufferList = ma_allocate_AudioBufferList__coreaudio(pData->periodSizeInFramesOut, pData->formatOut, pData->channelsOut, (isInterleaved) ? ma_stream_layout_interleaved : ma_stream_layout_deinterleaved, &pContext->allocationCallbacks); - if (pBufferList == NULL) { - ((ma_AudioComponentInstanceDispose_proc)pContext->coreaudio.AudioComponentInstanceDispose)(pData->audioUnit); - return MA_OUT_OF_MEMORY; - } - - pData->pAudioBufferList = pBufferList; - } - - /* Callbacks. */ - callbackInfo.inputProcRefCon = pDevice_DoNotReference; - if (deviceType == ma_device_type_playback) { - callbackInfo.inputProc = ma_on_output__coreaudio; - status = ((ma_AudioUnitSetProperty_proc)pContext->coreaudio.AudioUnitSetProperty)(pData->audioUnit, kAudioUnitProperty_SetRenderCallback, kAudioUnitScope_Global, 0, &callbackInfo, sizeof(callbackInfo)); - if (status != noErr) { - ((ma_AudioComponentInstanceDispose_proc)pContext->coreaudio.AudioComponentInstanceDispose)(pData->audioUnit); - return ma_result_from_OSStatus(status); - } - } else { - callbackInfo.inputProc = ma_on_input__coreaudio; - status = ((ma_AudioUnitSetProperty_proc)pContext->coreaudio.AudioUnitSetProperty)(pData->audioUnit, kAudioOutputUnitProperty_SetInputCallback, kAudioUnitScope_Global, 0, &callbackInfo, sizeof(callbackInfo)); - if (status != noErr) { - ((ma_AudioComponentInstanceDispose_proc)pContext->coreaudio.AudioComponentInstanceDispose)(pData->audioUnit); - return ma_result_from_OSStatus(status); - } - } - - /* We need to listen for stop events. */ - if (pData->registerStopEvent) { - status = ((ma_AudioUnitAddPropertyListener_proc)pContext->coreaudio.AudioUnitAddPropertyListener)(pData->audioUnit, kAudioOutputUnitProperty_IsRunning, on_start_stop__coreaudio, pDevice_DoNotReference); - if (status != noErr) { - ((ma_AudioComponentInstanceDispose_proc)pContext->coreaudio.AudioComponentInstanceDispose)(pData->audioUnit); - return ma_result_from_OSStatus(status); - } - } - - /* Initialize the audio unit. */ - status = ((ma_AudioUnitInitialize_proc)pContext->coreaudio.AudioUnitInitialize)(pData->audioUnit); - if (status != noErr) { - ma_free(pData->pAudioBufferList, &pContext->allocationCallbacks); - pData->pAudioBufferList = NULL; - ((ma_AudioComponentInstanceDispose_proc)pContext->coreaudio.AudioComponentInstanceDispose)(pData->audioUnit); - return ma_result_from_OSStatus(status); - } - - /* Grab the name. */ -#if defined(MA_APPLE_DESKTOP) - ma_get_AudioObject_name(pContext, deviceObjectID, sizeof(pData->deviceName), pData->deviceName); -#else - if (deviceType == ma_device_type_playback) { - ma_strcpy_s(pData->deviceName, sizeof(pData->deviceName), MA_DEFAULT_PLAYBACK_DEVICE_NAME); - } else { - ma_strcpy_s(pData->deviceName, sizeof(pData->deviceName), MA_DEFAULT_CAPTURE_DEVICE_NAME); - } -#endif - - return result; -} - -#if defined(MA_APPLE_DESKTOP) -static ma_result ma_device_reinit_internal__coreaudio(ma_device* pDevice, ma_device_type deviceType, ma_bool32 disposePreviousAudioUnit) -{ - ma_device_init_internal_data__coreaudio data; - ma_result result; - - /* This should only be called for playback or capture, not duplex. */ - if (deviceType == ma_device_type_duplex) { - return MA_INVALID_ARGS; - } - - data.allowNominalSampleRateChange = MA_FALSE; /* Don't change the nominal sample rate when switching devices. */ - - if (deviceType == ma_device_type_capture) { - data.formatIn = pDevice->capture.format; - data.channelsIn = pDevice->capture.channels; - data.sampleRateIn = pDevice->sampleRate; - MA_COPY_MEMORY(data.channelMapIn, pDevice->capture.channelMap, sizeof(pDevice->capture.channelMap)); - data.shareMode = pDevice->capture.shareMode; - data.performanceProfile = pDevice->coreaudio.originalPerformanceProfile; - data.registerStopEvent = MA_TRUE; - - if (disposePreviousAudioUnit) { - ((ma_AudioOutputUnitStop_proc)pDevice->pContext->coreaudio.AudioOutputUnitStop)((AudioUnit)pDevice->coreaudio.audioUnitCapture); - ((ma_AudioComponentInstanceDispose_proc)pDevice->pContext->coreaudio.AudioComponentInstanceDispose)((AudioUnit)pDevice->coreaudio.audioUnitCapture); - } - if (pDevice->coreaudio.pAudioBufferList) { - ma_free(pDevice->coreaudio.pAudioBufferList, &pDevice->pContext->allocationCallbacks); - } - } else if (deviceType == ma_device_type_playback) { - data.formatIn = pDevice->playback.format; - data.channelsIn = pDevice->playback.channels; - data.sampleRateIn = pDevice->sampleRate; - MA_COPY_MEMORY(data.channelMapIn, pDevice->playback.channelMap, sizeof(pDevice->playback.channelMap)); - data.shareMode = pDevice->playback.shareMode; - data.performanceProfile = pDevice->coreaudio.originalPerformanceProfile; - data.registerStopEvent = (pDevice->type != ma_device_type_duplex); - - if (disposePreviousAudioUnit) { - ((ma_AudioOutputUnitStop_proc)pDevice->pContext->coreaudio.AudioOutputUnitStop)((AudioUnit)pDevice->coreaudio.audioUnitPlayback); - ((ma_AudioComponentInstanceDispose_proc)pDevice->pContext->coreaudio.AudioComponentInstanceDispose)((AudioUnit)pDevice->coreaudio.audioUnitPlayback); - } - } - data.periodSizeInFramesIn = pDevice->coreaudio.originalPeriodSizeInFrames; - data.periodSizeInMillisecondsIn = pDevice->coreaudio.originalPeriodSizeInMilliseconds; - data.periodsIn = pDevice->coreaudio.originalPeriods; - - /* Need at least 3 periods for duplex. */ - if (data.periodsIn < 3 && pDevice->type == ma_device_type_duplex) { - data.periodsIn = 3; - } - - result = ma_device_init_internal__coreaudio(pDevice->pContext, deviceType, NULL, &data, (void*)pDevice); - if (result != MA_SUCCESS) { - return result; - } - - if (deviceType == ma_device_type_capture) { - #if defined(MA_APPLE_DESKTOP) - pDevice->coreaudio.deviceObjectIDCapture = (ma_uint32)data.deviceObjectID; - ma_get_AudioObject_uid(pDevice->pContext, pDevice->coreaudio.deviceObjectIDCapture, sizeof(pDevice->capture.id.coreaudio), pDevice->capture.id.coreaudio); - #endif - pDevice->coreaudio.audioUnitCapture = (ma_ptr)data.audioUnit; - pDevice->coreaudio.pAudioBufferList = (ma_ptr)data.pAudioBufferList; - pDevice->coreaudio.audioBufferCapInFrames = data.periodSizeInFramesOut; - - pDevice->capture.internalFormat = data.formatOut; - pDevice->capture.internalChannels = data.channelsOut; - pDevice->capture.internalSampleRate = data.sampleRateOut; - MA_COPY_MEMORY(pDevice->capture.internalChannelMap, data.channelMapOut, sizeof(data.channelMapOut)); - pDevice->capture.internalPeriodSizeInFrames = data.periodSizeInFramesOut; - pDevice->capture.internalPeriods = data.periodsOut; - } else if (deviceType == ma_device_type_playback) { - #if defined(MA_APPLE_DESKTOP) - pDevice->coreaudio.deviceObjectIDPlayback = (ma_uint32)data.deviceObjectID; - ma_get_AudioObject_uid(pDevice->pContext, pDevice->coreaudio.deviceObjectIDPlayback, sizeof(pDevice->playback.id.coreaudio), pDevice->playback.id.coreaudio); - #endif - pDevice->coreaudio.audioUnitPlayback = (ma_ptr)data.audioUnit; - - pDevice->playback.internalFormat = data.formatOut; - pDevice->playback.internalChannels = data.channelsOut; - pDevice->playback.internalSampleRate = data.sampleRateOut; - MA_COPY_MEMORY(pDevice->playback.internalChannelMap, data.channelMapOut, sizeof(data.channelMapOut)); - pDevice->playback.internalPeriodSizeInFrames = data.periodSizeInFramesOut; - pDevice->playback.internalPeriods = data.periodsOut; - } - - return MA_SUCCESS; -} -#endif /* MA_APPLE_DESKTOP */ - -static ma_result ma_device_init__coreaudio(ma_device* pDevice, const ma_device_config* pConfig, ma_device_descriptor* pDescriptorPlayback, ma_device_descriptor* pDescriptorCapture) -{ - ma_result result; - - MA_ASSERT(pDevice != NULL); - MA_ASSERT(pConfig != NULL); - - if (pConfig->deviceType == ma_device_type_loopback) { - return MA_DEVICE_TYPE_NOT_SUPPORTED; - } - - /* No exclusive mode with the Core Audio backend for now. */ - if (((pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) && pDescriptorCapture->shareMode == ma_share_mode_exclusive) || - ((pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) && pDescriptorPlayback->shareMode == ma_share_mode_exclusive)) { - return MA_SHARE_MODE_NOT_SUPPORTED; - } - - /* Capture needs to be initialized first. */ - if (pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) { - ma_device_init_internal_data__coreaudio data; - data.allowNominalSampleRateChange = pConfig->coreaudio.allowNominalSampleRateChange; - data.formatIn = pDescriptorCapture->format; - data.channelsIn = pDescriptorCapture->channels; - data.sampleRateIn = pDescriptorCapture->sampleRate; - MA_COPY_MEMORY(data.channelMapIn, pDescriptorCapture->channelMap, sizeof(pDescriptorCapture->channelMap)); - data.periodSizeInFramesIn = pDescriptorCapture->periodSizeInFrames; - data.periodSizeInMillisecondsIn = pDescriptorCapture->periodSizeInMilliseconds; - data.periodsIn = pDescriptorCapture->periodCount; - data.shareMode = pDescriptorCapture->shareMode; - data.performanceProfile = pConfig->performanceProfile; - data.registerStopEvent = MA_TRUE; - - /* Need at least 3 periods for duplex. */ - if (data.periodsIn < 3 && pConfig->deviceType == ma_device_type_duplex) { - data.periodsIn = 3; - } - - result = ma_device_init_internal__coreaudio(pDevice->pContext, ma_device_type_capture, pDescriptorCapture->pDeviceID, &data, (void*)pDevice); - if (result != MA_SUCCESS) { - return result; - } - - pDevice->coreaudio.isDefaultCaptureDevice = (pConfig->capture.pDeviceID == NULL); - #if defined(MA_APPLE_DESKTOP) - pDevice->coreaudio.deviceObjectIDCapture = (ma_uint32)data.deviceObjectID; - #endif - pDevice->coreaudio.audioUnitCapture = (ma_ptr)data.audioUnit; - pDevice->coreaudio.pAudioBufferList = (ma_ptr)data.pAudioBufferList; - pDevice->coreaudio.audioBufferCapInFrames = data.periodSizeInFramesOut; - pDevice->coreaudio.originalPeriodSizeInFrames = pDescriptorCapture->periodSizeInFrames; - pDevice->coreaudio.originalPeriodSizeInMilliseconds = pDescriptorCapture->periodSizeInMilliseconds; - pDevice->coreaudio.originalPeriods = pDescriptorCapture->periodCount; - pDevice->coreaudio.originalPerformanceProfile = pConfig->performanceProfile; - - pDescriptorCapture->format = data.formatOut; - pDescriptorCapture->channels = data.channelsOut; - pDescriptorCapture->sampleRate = data.sampleRateOut; - MA_COPY_MEMORY(pDescriptorCapture->channelMap, data.channelMapOut, sizeof(data.channelMapOut)); - pDescriptorCapture->periodSizeInFrames = data.periodSizeInFramesOut; - pDescriptorCapture->periodCount = data.periodsOut; - - #if defined(MA_APPLE_DESKTOP) - ma_get_AudioObject_uid(pDevice->pContext, pDevice->coreaudio.deviceObjectIDCapture, sizeof(pDevice->capture.id.coreaudio), pDevice->capture.id.coreaudio); - - /* - If we are using the default device we'll need to listen for changes to the system's default device so we can seamlessly - switch the device in the background. - */ - if (pConfig->capture.pDeviceID == NULL) { - ma_device__track__coreaudio(pDevice); - } - #endif - } - - /* Playback. */ - if (pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) { - ma_device_init_internal_data__coreaudio data; - data.allowNominalSampleRateChange = pConfig->coreaudio.allowNominalSampleRateChange; - data.formatIn = pDescriptorPlayback->format; - data.channelsIn = pDescriptorPlayback->channels; - data.sampleRateIn = pDescriptorPlayback->sampleRate; - MA_COPY_MEMORY(data.channelMapIn, pDescriptorPlayback->channelMap, sizeof(pDescriptorPlayback->channelMap)); - data.shareMode = pDescriptorPlayback->shareMode; - data.performanceProfile = pConfig->performanceProfile; - - /* In full-duplex mode we want the playback buffer to be the same size as the capture buffer. */ - if (pConfig->deviceType == ma_device_type_duplex) { - data.periodSizeInFramesIn = pDescriptorCapture->periodSizeInFrames; - data.periodsIn = pDescriptorCapture->periodCount; - data.registerStopEvent = MA_FALSE; - } else { - data.periodSizeInFramesIn = pDescriptorPlayback->periodSizeInFrames; - data.periodSizeInMillisecondsIn = pDescriptorPlayback->periodSizeInMilliseconds; - data.periodsIn = pDescriptorPlayback->periodCount; - data.registerStopEvent = MA_TRUE; - } - - result = ma_device_init_internal__coreaudio(pDevice->pContext, ma_device_type_playback, pDescriptorPlayback->pDeviceID, &data, (void*)pDevice); - if (result != MA_SUCCESS) { - if (pConfig->deviceType == ma_device_type_duplex) { - ((ma_AudioComponentInstanceDispose_proc)pDevice->pContext->coreaudio.AudioComponentInstanceDispose)((AudioUnit)pDevice->coreaudio.audioUnitCapture); - if (pDevice->coreaudio.pAudioBufferList) { - ma_free(pDevice->coreaudio.pAudioBufferList, &pDevice->pContext->allocationCallbacks); - } - } - return result; - } - - pDevice->coreaudio.isDefaultPlaybackDevice = (pConfig->playback.pDeviceID == NULL); - #if defined(MA_APPLE_DESKTOP) - pDevice->coreaudio.deviceObjectIDPlayback = (ma_uint32)data.deviceObjectID; - #endif - pDevice->coreaudio.audioUnitPlayback = (ma_ptr)data.audioUnit; - pDevice->coreaudio.originalPeriodSizeInFrames = pDescriptorPlayback->periodSizeInFrames; - pDevice->coreaudio.originalPeriodSizeInMilliseconds = pDescriptorPlayback->periodSizeInMilliseconds; - pDevice->coreaudio.originalPeriods = pDescriptorPlayback->periodCount; - pDevice->coreaudio.originalPerformanceProfile = pConfig->performanceProfile; - - pDescriptorPlayback->format = data.formatOut; - pDescriptorPlayback->channels = data.channelsOut; - pDescriptorPlayback->sampleRate = data.sampleRateOut; - MA_COPY_MEMORY(pDescriptorPlayback->channelMap, data.channelMapOut, sizeof(data.channelMapOut)); - pDescriptorPlayback->periodSizeInFrames = data.periodSizeInFramesOut; - pDescriptorPlayback->periodCount = data.periodsOut; - - #if defined(MA_APPLE_DESKTOP) - ma_get_AudioObject_uid(pDevice->pContext, pDevice->coreaudio.deviceObjectIDPlayback, sizeof(pDevice->playback.id.coreaudio), pDevice->playback.id.coreaudio); - - /* - If we are using the default device we'll need to listen for changes to the system's default device so we can seamlessly - switch the device in the background. - */ - if (pDescriptorPlayback->pDeviceID == NULL && (pConfig->deviceType != ma_device_type_duplex || pDescriptorCapture->pDeviceID != NULL)) { - ma_device__track__coreaudio(pDevice); - } - #endif - } - - - - /* - When stopping the device, a callback is called on another thread. We need to wait for this callback - before returning from ma_device_stop(). This event is used for this. - */ - ma_event_init(&pDevice->coreaudio.stopEvent); - - /* - We need to detect when a route has changed so we can update the data conversion pipeline accordingly. This is done - differently on non-Desktop Apple platforms. - */ -#if defined(MA_APPLE_MOBILE) - pDevice->coreaudio.pNotificationHandler = (MA_BRIDGE_RETAINED void*)[[ma_ios_notification_handler alloc] init:pDevice]; -#endif - - return MA_SUCCESS; -} - - -static ma_result ma_device_start__coreaudio(ma_device* pDevice) -{ - MA_ASSERT(pDevice != NULL); - - if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) { - OSStatus status = ((ma_AudioOutputUnitStart_proc)pDevice->pContext->coreaudio.AudioOutputUnitStart)((AudioUnit)pDevice->coreaudio.audioUnitCapture); - if (status != noErr) { - return ma_result_from_OSStatus(status); - } - } - - if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) { - OSStatus status = ((ma_AudioOutputUnitStart_proc)pDevice->pContext->coreaudio.AudioOutputUnitStart)((AudioUnit)pDevice->coreaudio.audioUnitPlayback); - if (status != noErr) { - if (pDevice->type == ma_device_type_duplex) { - ((ma_AudioOutputUnitStop_proc)pDevice->pContext->coreaudio.AudioOutputUnitStop)((AudioUnit)pDevice->coreaudio.audioUnitCapture); - } - return ma_result_from_OSStatus(status); - } - } - - return MA_SUCCESS; -} - -static ma_result ma_device_stop__coreaudio(ma_device* pDevice) -{ - MA_ASSERT(pDevice != NULL); - - /* It's not clear from the documentation whether or not AudioOutputUnitStop() actually drains the device or not. */ - - if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) { - OSStatus status = ((ma_AudioOutputUnitStop_proc)pDevice->pContext->coreaudio.AudioOutputUnitStop)((AudioUnit)pDevice->coreaudio.audioUnitCapture); - if (status != noErr) { - return ma_result_from_OSStatus(status); - } - } - - if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) { - OSStatus status = ((ma_AudioOutputUnitStop_proc)pDevice->pContext->coreaudio.AudioOutputUnitStop)((AudioUnit)pDevice->coreaudio.audioUnitPlayback); - if (status != noErr) { - return ma_result_from_OSStatus(status); - } - } - - /* We need to wait for the callback to finish before returning. */ - ma_event_wait(&pDevice->coreaudio.stopEvent); - return MA_SUCCESS; -} - - -static ma_result ma_context_uninit__coreaudio(ma_context* pContext) -{ - MA_ASSERT(pContext != NULL); - MA_ASSERT(pContext->backend == ma_backend_coreaudio); - -#if defined(MA_APPLE_MOBILE) - if (!pContext->coreaudio.noAudioSessionDeactivate) { - if (![[AVAudioSession sharedInstance] setActive:false error:nil]) { - ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "Failed to deactivate audio session."); - return MA_FAILED_TO_INIT_BACKEND; - } - } -#endif - -#if !defined(MA_NO_RUNTIME_LINKING) && !defined(MA_APPLE_MOBILE) - ma_dlclose(ma_context_get_log(pContext), pContext->coreaudio.hAudioUnit); - ma_dlclose(ma_context_get_log(pContext), pContext->coreaudio.hCoreAudio); - ma_dlclose(ma_context_get_log(pContext), pContext->coreaudio.hCoreFoundation); -#endif - -#if !defined(MA_APPLE_MOBILE) - ma_context__uninit_device_tracking__coreaudio(pContext); -#endif - - (void)pContext; - return MA_SUCCESS; -} - -#if defined(MA_APPLE_MOBILE) && defined(__IPHONE_12_0) -static AVAudioSessionCategory ma_to_AVAudioSessionCategory(ma_ios_session_category category) -{ - /* The "default" and "none" categories are treated different and should not be used as an input into this function. */ - MA_ASSERT(category != ma_ios_session_category_default); - MA_ASSERT(category != ma_ios_session_category_none); - - switch (category) { - case ma_ios_session_category_ambient: return AVAudioSessionCategoryAmbient; - case ma_ios_session_category_solo_ambient: return AVAudioSessionCategorySoloAmbient; - case ma_ios_session_category_playback: return AVAudioSessionCategoryPlayback; - case ma_ios_session_category_record: return AVAudioSessionCategoryRecord; - case ma_ios_session_category_play_and_record: return AVAudioSessionCategoryPlayAndRecord; - case ma_ios_session_category_multi_route: return AVAudioSessionCategoryMultiRoute; - case ma_ios_session_category_none: return AVAudioSessionCategoryAmbient; - case ma_ios_session_category_default: return AVAudioSessionCategoryAmbient; - default: return AVAudioSessionCategoryAmbient; - } -} -#endif - -static ma_result ma_context_init__coreaudio(ma_context* pContext, const ma_context_config* pConfig, ma_backend_callbacks* pCallbacks) -{ -#if !defined(MA_APPLE_MOBILE) - ma_result result; -#endif - - MA_ASSERT(pConfig != NULL); - MA_ASSERT(pContext != NULL); - -#if defined(MA_APPLE_MOBILE) - @autoreleasepool { - AVAudioSession* pAudioSession = [AVAudioSession sharedInstance]; - AVAudioSessionCategoryOptions options = pConfig->coreaudio.sessionCategoryOptions; - - MA_ASSERT(pAudioSession != NULL); - - if (pConfig->coreaudio.sessionCategory == ma_ios_session_category_default) { - /* - I'm going to use trial and error to determine our default session category. First we'll try PlayAndRecord. If that fails - we'll try Playback and if that fails we'll try record. If all of these fail we'll just not set the category. - */ - #if !defined(MA_APPLE_TV) && !defined(MA_APPLE_WATCH) - options |= AVAudioSessionCategoryOptionDefaultToSpeaker; - #endif - - if ([pAudioSession setCategory: AVAudioSessionCategoryPlayAndRecord withOptions:options error:nil]) { - /* Using PlayAndRecord */ - } else if ([pAudioSession setCategory: AVAudioSessionCategoryPlayback withOptions:options error:nil]) { - /* Using Playback */ - } else if ([pAudioSession setCategory: AVAudioSessionCategoryRecord withOptions:options error:nil]) { - /* Using Record */ - } else { - /* Leave as default? */ - } - } else { - if (pConfig->coreaudio.sessionCategory != ma_ios_session_category_none) { - #if defined(__IPHONE_12_0) - if (![pAudioSession setCategory: ma_to_AVAudioSessionCategory(pConfig->coreaudio.sessionCategory) withOptions:options error:nil]) { - return MA_INVALID_OPERATION; /* Failed to set session category. */ - } - #else - /* Ignore the session category on version 11 and older, but post a warning. */ - ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_WARNING, "Session category only supported in iOS 12 and newer."); - #endif - } - } - - if (!pConfig->coreaudio.noAudioSessionActivate) { - if (![pAudioSession setActive:true error:nil]) { - ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "Failed to activate audio session."); - return MA_FAILED_TO_INIT_BACKEND; - } - } - } -#endif - -#if !defined(MA_NO_RUNTIME_LINKING) && !defined(MA_APPLE_MOBILE) - pContext->coreaudio.hCoreFoundation = ma_dlopen(ma_context_get_log(pContext), "/System/Library/Frameworks/CoreFoundation.framework/CoreFoundation"); - if (pContext->coreaudio.hCoreFoundation == NULL) { - return MA_API_NOT_FOUND; - } - - pContext->coreaudio.CFStringGetCString = ma_dlsym(ma_context_get_log(pContext), pContext->coreaudio.hCoreFoundation, "CFStringGetCString"); - pContext->coreaudio.CFRelease = ma_dlsym(ma_context_get_log(pContext), pContext->coreaudio.hCoreFoundation, "CFRelease"); - - - pContext->coreaudio.hCoreAudio = ma_dlopen(ma_context_get_log(pContext), "/System/Library/Frameworks/CoreAudio.framework/CoreAudio"); - if (pContext->coreaudio.hCoreAudio == NULL) { - ma_dlclose(ma_context_get_log(pContext), pContext->coreaudio.hCoreFoundation); - return MA_API_NOT_FOUND; - } - - pContext->coreaudio.AudioObjectGetPropertyData = ma_dlsym(ma_context_get_log(pContext), pContext->coreaudio.hCoreAudio, "AudioObjectGetPropertyData"); - pContext->coreaudio.AudioObjectGetPropertyDataSize = ma_dlsym(ma_context_get_log(pContext), pContext->coreaudio.hCoreAudio, "AudioObjectGetPropertyDataSize"); - pContext->coreaudio.AudioObjectSetPropertyData = ma_dlsym(ma_context_get_log(pContext), pContext->coreaudio.hCoreAudio, "AudioObjectSetPropertyData"); - pContext->coreaudio.AudioObjectAddPropertyListener = ma_dlsym(ma_context_get_log(pContext), pContext->coreaudio.hCoreAudio, "AudioObjectAddPropertyListener"); - pContext->coreaudio.AudioObjectRemovePropertyListener = ma_dlsym(ma_context_get_log(pContext), pContext->coreaudio.hCoreAudio, "AudioObjectRemovePropertyListener"); - - /* - It looks like Apple has moved some APIs from AudioUnit into AudioToolbox on more recent versions of macOS. They are still - defined in AudioUnit, but just in case they decide to remove them from there entirely I'm going to implement a fallback. - The way it'll work is that it'll first try AudioUnit, and if the required symbols are not present there we'll fall back to - AudioToolbox. - */ - pContext->coreaudio.hAudioUnit = ma_dlopen(ma_context_get_log(pContext), "/System/Library/Frameworks/AudioUnit.framework/AudioUnit"); - if (pContext->coreaudio.hAudioUnit == NULL) { - ma_dlclose(ma_context_get_log(pContext), pContext->coreaudio.hCoreAudio); - ma_dlclose(ma_context_get_log(pContext), pContext->coreaudio.hCoreFoundation); - return MA_API_NOT_FOUND; - } - - if (ma_dlsym(ma_context_get_log(pContext), pContext->coreaudio.hAudioUnit, "AudioComponentFindNext") == NULL) { - /* Couldn't find the required symbols in AudioUnit, so fall back to AudioToolbox. */ - ma_dlclose(ma_context_get_log(pContext), pContext->coreaudio.hAudioUnit); - pContext->coreaudio.hAudioUnit = ma_dlopen(ma_context_get_log(pContext), "/System/Library/Frameworks/AudioToolbox.framework/AudioToolbox"); - if (pContext->coreaudio.hAudioUnit == NULL) { - ma_dlclose(ma_context_get_log(pContext), pContext->coreaudio.hCoreAudio); - ma_dlclose(ma_context_get_log(pContext), pContext->coreaudio.hCoreFoundation); - return MA_API_NOT_FOUND; - } - } - - pContext->coreaudio.AudioComponentFindNext = ma_dlsym(ma_context_get_log(pContext), pContext->coreaudio.hAudioUnit, "AudioComponentFindNext"); - pContext->coreaudio.AudioComponentInstanceDispose = ma_dlsym(ma_context_get_log(pContext), pContext->coreaudio.hAudioUnit, "AudioComponentInstanceDispose"); - pContext->coreaudio.AudioComponentInstanceNew = ma_dlsym(ma_context_get_log(pContext), pContext->coreaudio.hAudioUnit, "AudioComponentInstanceNew"); - pContext->coreaudio.AudioOutputUnitStart = ma_dlsym(ma_context_get_log(pContext), pContext->coreaudio.hAudioUnit, "AudioOutputUnitStart"); - pContext->coreaudio.AudioOutputUnitStop = ma_dlsym(ma_context_get_log(pContext), pContext->coreaudio.hAudioUnit, "AudioOutputUnitStop"); - pContext->coreaudio.AudioUnitAddPropertyListener = ma_dlsym(ma_context_get_log(pContext), pContext->coreaudio.hAudioUnit, "AudioUnitAddPropertyListener"); - pContext->coreaudio.AudioUnitGetPropertyInfo = ma_dlsym(ma_context_get_log(pContext), pContext->coreaudio.hAudioUnit, "AudioUnitGetPropertyInfo"); - pContext->coreaudio.AudioUnitGetProperty = ma_dlsym(ma_context_get_log(pContext), pContext->coreaudio.hAudioUnit, "AudioUnitGetProperty"); - pContext->coreaudio.AudioUnitSetProperty = ma_dlsym(ma_context_get_log(pContext), pContext->coreaudio.hAudioUnit, "AudioUnitSetProperty"); - pContext->coreaudio.AudioUnitInitialize = ma_dlsym(ma_context_get_log(pContext), pContext->coreaudio.hAudioUnit, "AudioUnitInitialize"); - pContext->coreaudio.AudioUnitRender = ma_dlsym(ma_context_get_log(pContext), pContext->coreaudio.hAudioUnit, "AudioUnitRender"); -#else - pContext->coreaudio.CFStringGetCString = (ma_proc)CFStringGetCString; - pContext->coreaudio.CFRelease = (ma_proc)CFRelease; - - #if defined(MA_APPLE_DESKTOP) - pContext->coreaudio.AudioObjectGetPropertyData = (ma_proc)AudioObjectGetPropertyData; - pContext->coreaudio.AudioObjectGetPropertyDataSize = (ma_proc)AudioObjectGetPropertyDataSize; - pContext->coreaudio.AudioObjectSetPropertyData = (ma_proc)AudioObjectSetPropertyData; - pContext->coreaudio.AudioObjectAddPropertyListener = (ma_proc)AudioObjectAddPropertyListener; - pContext->coreaudio.AudioObjectRemovePropertyListener = (ma_proc)AudioObjectRemovePropertyListener; - #endif - - pContext->coreaudio.AudioComponentFindNext = (ma_proc)AudioComponentFindNext; - pContext->coreaudio.AudioComponentInstanceDispose = (ma_proc)AudioComponentInstanceDispose; - pContext->coreaudio.AudioComponentInstanceNew = (ma_proc)AudioComponentInstanceNew; - pContext->coreaudio.AudioOutputUnitStart = (ma_proc)AudioOutputUnitStart; - pContext->coreaudio.AudioOutputUnitStop = (ma_proc)AudioOutputUnitStop; - pContext->coreaudio.AudioUnitAddPropertyListener = (ma_proc)AudioUnitAddPropertyListener; - pContext->coreaudio.AudioUnitGetPropertyInfo = (ma_proc)AudioUnitGetPropertyInfo; - pContext->coreaudio.AudioUnitGetProperty = (ma_proc)AudioUnitGetProperty; - pContext->coreaudio.AudioUnitSetProperty = (ma_proc)AudioUnitSetProperty; - pContext->coreaudio.AudioUnitInitialize = (ma_proc)AudioUnitInitialize; - pContext->coreaudio.AudioUnitRender = (ma_proc)AudioUnitRender; -#endif - - /* Audio component. */ - { - AudioComponentDescription desc; - desc.componentType = kAudioUnitType_Output; - #if defined(MA_APPLE_DESKTOP) - desc.componentSubType = kAudioUnitSubType_HALOutput; - #else - desc.componentSubType = kAudioUnitSubType_RemoteIO; - #endif - desc.componentManufacturer = kAudioUnitManufacturer_Apple; - desc.componentFlags = 0; - desc.componentFlagsMask = 0; - - pContext->coreaudio.component = ((ma_AudioComponentFindNext_proc)pContext->coreaudio.AudioComponentFindNext)(NULL, &desc); - if (pContext->coreaudio.component == NULL) { - #if !defined(MA_NO_RUNTIME_LINKING) && !defined(MA_APPLE_MOBILE) - ma_dlclose(ma_context_get_log(pContext), pContext->coreaudio.hAudioUnit); - ma_dlclose(ma_context_get_log(pContext), pContext->coreaudio.hCoreAudio); - ma_dlclose(ma_context_get_log(pContext), pContext->coreaudio.hCoreFoundation); - #endif - return MA_FAILED_TO_INIT_BACKEND; - } - } - -#if !defined(MA_APPLE_MOBILE) - result = ma_context__init_device_tracking__coreaudio(pContext); - if (result != MA_SUCCESS) { - #if !defined(MA_NO_RUNTIME_LINKING) && !defined(MA_APPLE_MOBILE) - ma_dlclose(ma_context_get_log(pContext), pContext->coreaudio.hAudioUnit); - ma_dlclose(ma_context_get_log(pContext), pContext->coreaudio.hCoreAudio); - ma_dlclose(ma_context_get_log(pContext), pContext->coreaudio.hCoreFoundation); - #endif - return result; - } -#endif - - pContext->coreaudio.noAudioSessionDeactivate = pConfig->coreaudio.noAudioSessionDeactivate; - - pCallbacks->onContextInit = ma_context_init__coreaudio; - pCallbacks->onContextUninit = ma_context_uninit__coreaudio; - pCallbacks->onContextEnumerateDevices = ma_context_enumerate_devices__coreaudio; - pCallbacks->onContextGetDeviceInfo = ma_context_get_device_info__coreaudio; - pCallbacks->onDeviceInit = ma_device_init__coreaudio; - pCallbacks->onDeviceUninit = ma_device_uninit__coreaudio; - pCallbacks->onDeviceStart = ma_device_start__coreaudio; - pCallbacks->onDeviceStop = ma_device_stop__coreaudio; - pCallbacks->onDeviceRead = NULL; - pCallbacks->onDeviceWrite = NULL; - pCallbacks->onDeviceDataLoop = NULL; - - return MA_SUCCESS; -} -#endif /* MA_HAS_COREAUDIO */ - - - -/****************************************************************************** - -sndio Backend - -******************************************************************************/ -#ifdef MA_HAS_SNDIO -#include - -/* -Only supporting OpenBSD. This did not work very well at all on FreeBSD when I tried it. Not sure if this is due -to miniaudio's implementation or if it's some kind of system configuration issue, but basically the default device -just doesn't emit any sound, or at times you'll hear tiny pieces. I will consider enabling this when there's -demand for it or if I can get it tested and debugged more thoroughly. -*/ -#if 0 -#if defined(__NetBSD__) || defined(__OpenBSD__) -#include -#endif -#if defined(__FreeBSD__) || defined(__DragonFly__) -#include -#endif -#endif - -#define MA_SIO_DEVANY "default" -#define MA_SIO_PLAY 1 -#define MA_SIO_REC 2 -#define MA_SIO_NENC 8 -#define MA_SIO_NCHAN 8 -#define MA_SIO_NRATE 16 -#define MA_SIO_NCONF 4 - -struct ma_sio_hdl; /* <-- Opaque */ - -struct ma_sio_par -{ - unsigned int bits; - unsigned int bps; - unsigned int sig; - unsigned int le; - unsigned int msb; - unsigned int rchan; - unsigned int pchan; - unsigned int rate; - unsigned int bufsz; - unsigned int xrun; - unsigned int round; - unsigned int appbufsz; - int __pad[3]; - unsigned int __magic; -}; - -struct ma_sio_enc -{ - unsigned int bits; - unsigned int bps; - unsigned int sig; - unsigned int le; - unsigned int msb; -}; - -struct ma_sio_conf -{ - unsigned int enc; - unsigned int rchan; - unsigned int pchan; - unsigned int rate; -}; - -struct ma_sio_cap -{ - struct ma_sio_enc enc[MA_SIO_NENC]; - unsigned int rchan[MA_SIO_NCHAN]; - unsigned int pchan[MA_SIO_NCHAN]; - unsigned int rate[MA_SIO_NRATE]; - int __pad[7]; - unsigned int nconf; - struct ma_sio_conf confs[MA_SIO_NCONF]; -}; - -typedef struct ma_sio_hdl* (* ma_sio_open_proc) (const char*, unsigned int, int); -typedef void (* ma_sio_close_proc) (struct ma_sio_hdl*); -typedef int (* ma_sio_setpar_proc) (struct ma_sio_hdl*, struct ma_sio_par*); -typedef int (* ma_sio_getpar_proc) (struct ma_sio_hdl*, struct ma_sio_par*); -typedef int (* ma_sio_getcap_proc) (struct ma_sio_hdl*, struct ma_sio_cap*); -typedef size_t (* ma_sio_write_proc) (struct ma_sio_hdl*, const void*, size_t); -typedef size_t (* ma_sio_read_proc) (struct ma_sio_hdl*, void*, size_t); -typedef int (* ma_sio_start_proc) (struct ma_sio_hdl*); -typedef int (* ma_sio_stop_proc) (struct ma_sio_hdl*); -typedef int (* ma_sio_initpar_proc)(struct ma_sio_par*); - -static ma_uint32 ma_get_standard_sample_rate_priority_index__sndio(ma_uint32 sampleRate) /* Lower = higher priority */ -{ - ma_uint32 i; - for (i = 0; i < ma_countof(g_maStandardSampleRatePriorities); ++i) { - if (g_maStandardSampleRatePriorities[i] == sampleRate) { - return i; - } - } - - return (ma_uint32)-1; -} - -static ma_format ma_format_from_sio_enc__sndio(unsigned int bits, unsigned int bps, unsigned int sig, unsigned int le, unsigned int msb) -{ - /* We only support native-endian right now. */ - if ((ma_is_little_endian() && le == 0) || (ma_is_big_endian() && le == 1)) { - return ma_format_unknown; - } - - if (bits == 8 && bps == 1 && sig == 0) { - return ma_format_u8; - } - if (bits == 16 && bps == 2 && sig == 1) { - return ma_format_s16; - } - if (bits == 24 && bps == 3 && sig == 1) { - return ma_format_s24; - } - if (bits == 24 && bps == 4 && sig == 1 && msb == 0) { - /*return ma_format_s24_32;*/ - } - if (bits == 32 && bps == 4 && sig == 1) { - return ma_format_s32; - } - - return ma_format_unknown; -} - -static ma_format ma_find_best_format_from_sio_cap__sndio(struct ma_sio_cap* caps) -{ - ma_format bestFormat; - unsigned int iConfig; - - MA_ASSERT(caps != NULL); - - bestFormat = ma_format_unknown; - for (iConfig = 0; iConfig < caps->nconf; iConfig += 1) { - unsigned int iEncoding; - for (iEncoding = 0; iEncoding < MA_SIO_NENC; iEncoding += 1) { - unsigned int bits; - unsigned int bps; - unsigned int sig; - unsigned int le; - unsigned int msb; - ma_format format; - - if ((caps->confs[iConfig].enc & (1UL << iEncoding)) == 0) { - continue; - } - - bits = caps->enc[iEncoding].bits; - bps = caps->enc[iEncoding].bps; - sig = caps->enc[iEncoding].sig; - le = caps->enc[iEncoding].le; - msb = caps->enc[iEncoding].msb; - format = ma_format_from_sio_enc__sndio(bits, bps, sig, le, msb); - if (format == ma_format_unknown) { - continue; /* Format not supported. */ - } - - if (bestFormat == ma_format_unknown) { - bestFormat = format; - } else { - if (ma_get_format_priority_index(bestFormat) > ma_get_format_priority_index(format)) { /* <-- Lower = better. */ - bestFormat = format; - } - } - } - } - - return bestFormat; -} - -static ma_uint32 ma_find_best_channels_from_sio_cap__sndio(struct ma_sio_cap* caps, ma_device_type deviceType, ma_format requiredFormat) -{ - ma_uint32 maxChannels; - unsigned int iConfig; - - MA_ASSERT(caps != NULL); - MA_ASSERT(requiredFormat != ma_format_unknown); - - /* Just pick whatever configuration has the most channels. */ - maxChannels = 0; - for (iConfig = 0; iConfig < caps->nconf; iConfig += 1) { - /* The encoding should be of requiredFormat. */ - unsigned int iEncoding; - for (iEncoding = 0; iEncoding < MA_SIO_NENC; iEncoding += 1) { - unsigned int iChannel; - unsigned int bits; - unsigned int bps; - unsigned int sig; - unsigned int le; - unsigned int msb; - ma_format format; - - if ((caps->confs[iConfig].enc & (1UL << iEncoding)) == 0) { - continue; - } - - bits = caps->enc[iEncoding].bits; - bps = caps->enc[iEncoding].bps; - sig = caps->enc[iEncoding].sig; - le = caps->enc[iEncoding].le; - msb = caps->enc[iEncoding].msb; - format = ma_format_from_sio_enc__sndio(bits, bps, sig, le, msb); - if (format != requiredFormat) { - continue; - } - - /* Getting here means the format is supported. Iterate over each channel count and grab the biggest one. */ - for (iChannel = 0; iChannel < MA_SIO_NCHAN; iChannel += 1) { - unsigned int chan = 0; - unsigned int channels; - - if (deviceType == ma_device_type_playback) { - chan = caps->confs[iConfig].pchan; - } else { - chan = caps->confs[iConfig].rchan; - } - - if ((chan & (1UL << iChannel)) == 0) { - continue; - } - - if (deviceType == ma_device_type_playback) { - channels = caps->pchan[iChannel]; - } else { - channels = caps->rchan[iChannel]; - } - - if (maxChannels < channels) { - maxChannels = channels; - } - } - } - } - - return maxChannels; -} - -static ma_uint32 ma_find_best_sample_rate_from_sio_cap__sndio(struct ma_sio_cap* caps, ma_device_type deviceType, ma_format requiredFormat, ma_uint32 requiredChannels) -{ - ma_uint32 firstSampleRate; - ma_uint32 bestSampleRate; - unsigned int iConfig; - - MA_ASSERT(caps != NULL); - MA_ASSERT(requiredFormat != ma_format_unknown); - MA_ASSERT(requiredChannels > 0); - MA_ASSERT(requiredChannels <= MA_MAX_CHANNELS); - - firstSampleRate = 0; /* <-- If the device does not support a standard rate we'll fall back to the first one that's found. */ - bestSampleRate = 0; - - for (iConfig = 0; iConfig < caps->nconf; iConfig += 1) { - /* The encoding should be of requiredFormat. */ - unsigned int iEncoding; - for (iEncoding = 0; iEncoding < MA_SIO_NENC; iEncoding += 1) { - unsigned int iChannel; - unsigned int bits; - unsigned int bps; - unsigned int sig; - unsigned int le; - unsigned int msb; - ma_format format; - - if ((caps->confs[iConfig].enc & (1UL << iEncoding)) == 0) { - continue; - } - - bits = caps->enc[iEncoding].bits; - bps = caps->enc[iEncoding].bps; - sig = caps->enc[iEncoding].sig; - le = caps->enc[iEncoding].le; - msb = caps->enc[iEncoding].msb; - format = ma_format_from_sio_enc__sndio(bits, bps, sig, le, msb); - if (format != requiredFormat) { - continue; - } - - /* Getting here means the format is supported. Iterate over each channel count and grab the biggest one. */ - for (iChannel = 0; iChannel < MA_SIO_NCHAN; iChannel += 1) { - unsigned int chan = 0; - unsigned int channels; - unsigned int iRate; - - if (deviceType == ma_device_type_playback) { - chan = caps->confs[iConfig].pchan; - } else { - chan = caps->confs[iConfig].rchan; - } - - if ((chan & (1UL << iChannel)) == 0) { - continue; - } - - if (deviceType == ma_device_type_playback) { - channels = caps->pchan[iChannel]; - } else { - channels = caps->rchan[iChannel]; - } - - if (channels != requiredChannels) { - continue; - } - - /* Getting here means we have found a compatible encoding/channel pair. */ - for (iRate = 0; iRate < MA_SIO_NRATE; iRate += 1) { - ma_uint32 rate = (ma_uint32)caps->rate[iRate]; - ma_uint32 ratePriority; - - if (firstSampleRate == 0) { - firstSampleRate = rate; - } - - /* Disregard this rate if it's not a standard one. */ - ratePriority = ma_get_standard_sample_rate_priority_index__sndio(rate); - if (ratePriority == (ma_uint32)-1) { - continue; - } - - if (ma_get_standard_sample_rate_priority_index__sndio(bestSampleRate) > ratePriority) { /* Lower = better. */ - bestSampleRate = rate; - } - } - } - } - } - - /* If a standard sample rate was not found just fall back to the first one that was iterated. */ - if (bestSampleRate == 0) { - bestSampleRate = firstSampleRate; - } - - return bestSampleRate; -} - - -static ma_result ma_context_enumerate_devices__sndio(ma_context* pContext, ma_enum_devices_callback_proc callback, void* pUserData) -{ - ma_bool32 isTerminating = MA_FALSE; - struct ma_sio_hdl* handle; - - MA_ASSERT(pContext != NULL); - MA_ASSERT(callback != NULL); - - /* sndio doesn't seem to have a good device enumeration API, so I'm therefore only enumerating over default devices for now. */ - - /* Playback. */ - if (!isTerminating) { - handle = ((ma_sio_open_proc)pContext->sndio.sio_open)(MA_SIO_DEVANY, MA_SIO_PLAY, 0); - if (handle != NULL) { - /* Supports playback. */ - ma_device_info deviceInfo; - MA_ZERO_OBJECT(&deviceInfo); - ma_strcpy_s(deviceInfo.id.sndio, sizeof(deviceInfo.id.sndio), MA_SIO_DEVANY); - ma_strcpy_s(deviceInfo.name, sizeof(deviceInfo.name), MA_DEFAULT_PLAYBACK_DEVICE_NAME); - - isTerminating = !callback(pContext, ma_device_type_playback, &deviceInfo, pUserData); - - ((ma_sio_close_proc)pContext->sndio.sio_close)(handle); - } - } - - /* Capture. */ - if (!isTerminating) { - handle = ((ma_sio_open_proc)pContext->sndio.sio_open)(MA_SIO_DEVANY, MA_SIO_REC, 0); - if (handle != NULL) { - /* Supports capture. */ - ma_device_info deviceInfo; - MA_ZERO_OBJECT(&deviceInfo); - ma_strcpy_s(deviceInfo.id.sndio, sizeof(deviceInfo.id.sndio), "default"); - ma_strcpy_s(deviceInfo.name, sizeof(deviceInfo.name), MA_DEFAULT_CAPTURE_DEVICE_NAME); - - isTerminating = !callback(pContext, ma_device_type_capture, &deviceInfo, pUserData); - - ((ma_sio_close_proc)pContext->sndio.sio_close)(handle); - } - } - - return MA_SUCCESS; -} - -static ma_result ma_context_get_device_info__sndio(ma_context* pContext, ma_device_type deviceType, const ma_device_id* pDeviceID, ma_device_info* pDeviceInfo) -{ - char devid[256]; - struct ma_sio_hdl* handle; - struct ma_sio_cap caps; - unsigned int iConfig; - - MA_ASSERT(pContext != NULL); - - /* We need to open the device before we can get information about it. */ - if (pDeviceID == NULL) { - ma_strcpy_s(devid, sizeof(devid), MA_SIO_DEVANY); - ma_strcpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), (deviceType == ma_device_type_playback) ? MA_DEFAULT_PLAYBACK_DEVICE_NAME : MA_DEFAULT_CAPTURE_DEVICE_NAME); - } else { - ma_strcpy_s(devid, sizeof(devid), pDeviceID->sndio); - ma_strcpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), devid); - } - - handle = ((ma_sio_open_proc)pContext->sndio.sio_open)(devid, (deviceType == ma_device_type_playback) ? MA_SIO_PLAY : MA_SIO_REC, 0); - if (handle == NULL) { - return MA_NO_DEVICE; - } - - if (((ma_sio_getcap_proc)pContext->sndio.sio_getcap)(handle, &caps) == 0) { - return MA_ERROR; - } - - pDeviceInfo->nativeDataFormatCount = 0; - - for (iConfig = 0; iConfig < caps.nconf; iConfig += 1) { - /* - The main thing we care about is that the encoding is supported by miniaudio. If it is, we want to give - preference to some formats over others. - */ - unsigned int iEncoding; - unsigned int iChannel; - unsigned int iRate; - - for (iEncoding = 0; iEncoding < MA_SIO_NENC; iEncoding += 1) { - unsigned int bits; - unsigned int bps; - unsigned int sig; - unsigned int le; - unsigned int msb; - ma_format format; - - if ((caps.confs[iConfig].enc & (1UL << iEncoding)) == 0) { - continue; - } - - bits = caps.enc[iEncoding].bits; - bps = caps.enc[iEncoding].bps; - sig = caps.enc[iEncoding].sig; - le = caps.enc[iEncoding].le; - msb = caps.enc[iEncoding].msb; - format = ma_format_from_sio_enc__sndio(bits, bps, sig, le, msb); - if (format == ma_format_unknown) { - continue; /* Format not supported. */ - } - - - /* Channels. */ - for (iChannel = 0; iChannel < MA_SIO_NCHAN; iChannel += 1) { - unsigned int chan = 0; - unsigned int channels; - - if (deviceType == ma_device_type_playback) { - chan = caps.confs[iConfig].pchan; - } else { - chan = caps.confs[iConfig].rchan; - } - - if ((chan & (1UL << iChannel)) == 0) { - continue; - } - - if (deviceType == ma_device_type_playback) { - channels = caps.pchan[iChannel]; - } else { - channels = caps.rchan[iChannel]; - } - - - /* Sample Rates. */ - for (iRate = 0; iRate < MA_SIO_NRATE; iRate += 1) { - if ((caps.confs[iConfig].rate & (1UL << iRate)) != 0) { - ma_device_info_add_native_data_format(pDeviceInfo, format, channels, caps.rate[iRate], 0); - } - } - } - } - } - - ((ma_sio_close_proc)pContext->sndio.sio_close)(handle); - return MA_SUCCESS; -} - -static ma_result ma_device_uninit__sndio(ma_device* pDevice) -{ - MA_ASSERT(pDevice != NULL); - - if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) { - ((ma_sio_close_proc)pDevice->pContext->sndio.sio_close)((struct ma_sio_hdl*)pDevice->sndio.handleCapture); - } - - if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) { - ((ma_sio_close_proc)pDevice->pContext->sndio.sio_close)((struct ma_sio_hdl*)pDevice->sndio.handlePlayback); - } - - return MA_SUCCESS; -} - -static ma_result ma_device_init_handle__sndio(ma_device* pDevice, const ma_device_config* pConfig, ma_device_descriptor* pDescriptor, ma_device_type deviceType) -{ - const char* pDeviceName; - ma_ptr handle; - int openFlags = 0; - struct ma_sio_cap caps; - struct ma_sio_par par; - const ma_device_id* pDeviceID; - ma_format format; - ma_uint32 channels; - ma_uint32 sampleRate; - ma_format internalFormat; - ma_uint32 internalChannels; - ma_uint32 internalSampleRate; - ma_uint32 internalPeriodSizeInFrames; - ma_uint32 internalPeriods; - - MA_ASSERT(pConfig != NULL); - MA_ASSERT(deviceType != ma_device_type_duplex); - MA_ASSERT(pDevice != NULL); - - if (deviceType == ma_device_type_capture) { - openFlags = MA_SIO_REC; - } else { - openFlags = MA_SIO_PLAY; - } - - pDeviceID = pDescriptor->pDeviceID; - format = pDescriptor->format; - channels = pDescriptor->channels; - sampleRate = pDescriptor->sampleRate; - - pDeviceName = MA_SIO_DEVANY; - if (pDeviceID != NULL) { - pDeviceName = pDeviceID->sndio; - } - - handle = (ma_ptr)((ma_sio_open_proc)pDevice->pContext->sndio.sio_open)(pDeviceName, openFlags, 0); - if (handle == NULL) { - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[sndio] Failed to open device."); - return MA_FAILED_TO_OPEN_BACKEND_DEVICE; - } - - /* We need to retrieve the device caps to determine the most appropriate format to use. */ - if (((ma_sio_getcap_proc)pDevice->pContext->sndio.sio_getcap)((struct ma_sio_hdl*)handle, &caps) == 0) { - ((ma_sio_close_proc)pDevice->pContext->sndio.sio_close)((struct ma_sio_hdl*)handle); - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[sndio] Failed to retrieve device caps."); - return MA_ERROR; - } - - /* - Note: sndio reports a huge range of available channels. This is inconvenient for us because there's no real - way, as far as I can tell, to get the _actual_ channel count of the device. I'm therefore restricting this - to the requested channels, regardless of whether or not the default channel count is requested. - - For hardware devices, I'm suspecting only a single channel count will be reported and we can safely use the - value returned by ma_find_best_channels_from_sio_cap__sndio(). - */ - if (deviceType == ma_device_type_capture) { - if (format == ma_format_unknown) { - format = ma_find_best_format_from_sio_cap__sndio(&caps); - } - - if (channels == 0) { - if (strlen(pDeviceName) > strlen("rsnd/") && strncmp(pDeviceName, "rsnd/", strlen("rsnd/")) == 0) { - channels = ma_find_best_channels_from_sio_cap__sndio(&caps, deviceType, format); - } else { - channels = MA_DEFAULT_CHANNELS; - } - } - } else { - if (format == ma_format_unknown) { - format = ma_find_best_format_from_sio_cap__sndio(&caps); - } - - if (channels == 0) { - if (strlen(pDeviceName) > strlen("rsnd/") && strncmp(pDeviceName, "rsnd/", strlen("rsnd/")) == 0) { - channels = ma_find_best_channels_from_sio_cap__sndio(&caps, deviceType, format); - } else { - channels = MA_DEFAULT_CHANNELS; - } - } - } - - if (sampleRate == 0) { - sampleRate = ma_find_best_sample_rate_from_sio_cap__sndio(&caps, pConfig->deviceType, format, channels); - } - - - ((ma_sio_initpar_proc)pDevice->pContext->sndio.sio_initpar)(&par); - par.msb = 0; - par.le = ma_is_little_endian(); - - switch (format) { - case ma_format_u8: - { - par.bits = 8; - par.bps = 1; - par.sig = 0; - } break; - - case ma_format_s24: - { - par.bits = 24; - par.bps = 3; - par.sig = 1; - } break; - - case ma_format_s32: - { - par.bits = 32; - par.bps = 4; - par.sig = 1; - } break; - - case ma_format_s16: - case ma_format_f32: - case ma_format_unknown: - default: - { - par.bits = 16; - par.bps = 2; - par.sig = 1; - } break; - } - - if (deviceType == ma_device_type_capture) { - par.rchan = channels; - } else { - par.pchan = channels; - } - - par.rate = sampleRate; - - internalPeriodSizeInFrames = ma_calculate_buffer_size_in_frames_from_descriptor(pDescriptor, par.rate, pConfig->performanceProfile); - - par.round = internalPeriodSizeInFrames; - par.appbufsz = par.round * pDescriptor->periodCount; - - if (((ma_sio_setpar_proc)pDevice->pContext->sndio.sio_setpar)((struct ma_sio_hdl*)handle, &par) == 0) { - ((ma_sio_close_proc)pDevice->pContext->sndio.sio_close)((struct ma_sio_hdl*)handle); - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[sndio] Failed to set buffer size."); - return MA_ERROR; - } - - if (((ma_sio_getpar_proc)pDevice->pContext->sndio.sio_getpar)((struct ma_sio_hdl*)handle, &par) == 0) { - ((ma_sio_close_proc)pDevice->pContext->sndio.sio_close)((struct ma_sio_hdl*)handle); - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[sndio] Failed to retrieve buffer size."); - return MA_ERROR; - } - - internalFormat = ma_format_from_sio_enc__sndio(par.bits, par.bps, par.sig, par.le, par.msb); - internalChannels = (deviceType == ma_device_type_capture) ? par.rchan : par.pchan; - internalSampleRate = par.rate; - internalPeriods = par.appbufsz / par.round; - internalPeriodSizeInFrames = par.round; - - if (deviceType == ma_device_type_capture) { - pDevice->sndio.handleCapture = handle; - } else { - pDevice->sndio.handlePlayback = handle; - } - - pDescriptor->format = internalFormat; - pDescriptor->channels = internalChannels; - pDescriptor->sampleRate = internalSampleRate; - ma_channel_map_init_standard(ma_standard_channel_map_sndio, pDescriptor->channelMap, ma_countof(pDescriptor->channelMap), internalChannels); - pDescriptor->periodSizeInFrames = internalPeriodSizeInFrames; - pDescriptor->periodCount = internalPeriods; - - return MA_SUCCESS; -} - -static ma_result ma_device_init__sndio(ma_device* pDevice, const ma_device_config* pConfig, ma_device_descriptor* pDescriptorPlayback, ma_device_descriptor* pDescriptorCapture) -{ - MA_ASSERT(pDevice != NULL); - - MA_ZERO_OBJECT(&pDevice->sndio); - - if (pConfig->deviceType == ma_device_type_loopback) { - return MA_DEVICE_TYPE_NOT_SUPPORTED; - } - - if (pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) { - ma_result result = ma_device_init_handle__sndio(pDevice, pConfig, pDescriptorCapture, ma_device_type_capture); - if (result != MA_SUCCESS) { - return result; - } - } - - if (pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) { - ma_result result = ma_device_init_handle__sndio(pDevice, pConfig, pDescriptorPlayback, ma_device_type_playback); - if (result != MA_SUCCESS) { - return result; - } - } - - return MA_SUCCESS; -} - -static ma_result ma_device_start__sndio(ma_device* pDevice) -{ - MA_ASSERT(pDevice != NULL); - - if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) { - ((ma_sio_start_proc)pDevice->pContext->sndio.sio_start)((struct ma_sio_hdl*)pDevice->sndio.handleCapture); - } - - if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) { - ((ma_sio_start_proc)pDevice->pContext->sndio.sio_start)((struct ma_sio_hdl*)pDevice->sndio.handlePlayback); /* <-- Doesn't actually playback until data is written. */ - } - - return MA_SUCCESS; -} - -static ma_result ma_device_stop__sndio(ma_device* pDevice) -{ - MA_ASSERT(pDevice != NULL); - - /* - From the documentation: - - The sio_stop() function puts the audio subsystem in the same state as before sio_start() is called. It stops recording, drains the play buffer and then - stops playback. If samples to play are queued but playback hasn't started yet then playback is forced immediately; playback will actually stop once the - buffer is drained. In no case are samples in the play buffer discarded. - - Therefore, sio_stop() performs all of the necessary draining for us. - */ - - if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) { - ((ma_sio_stop_proc)pDevice->pContext->sndio.sio_stop)((struct ma_sio_hdl*)pDevice->sndio.handleCapture); - } - - if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) { - ((ma_sio_stop_proc)pDevice->pContext->sndio.sio_stop)((struct ma_sio_hdl*)pDevice->sndio.handlePlayback); - } - - return MA_SUCCESS; -} - -static ma_result ma_device_write__sndio(ma_device* pDevice, const void* pPCMFrames, ma_uint32 frameCount, ma_uint32* pFramesWritten) -{ - int result; - - if (pFramesWritten != NULL) { - *pFramesWritten = 0; - } - - result = ((ma_sio_write_proc)pDevice->pContext->sndio.sio_write)((struct ma_sio_hdl*)pDevice->sndio.handlePlayback, pPCMFrames, frameCount * ma_get_bytes_per_frame(pDevice->playback.internalFormat, pDevice->playback.internalChannels)); - if (result == 0) { - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[sndio] Failed to send data from the client to the device."); - return MA_IO_ERROR; - } - - if (pFramesWritten != NULL) { - *pFramesWritten = frameCount; - } - - return MA_SUCCESS; -} - -static ma_result ma_device_read__sndio(ma_device* pDevice, void* pPCMFrames, ma_uint32 frameCount, ma_uint32* pFramesRead) -{ - int result; - - if (pFramesRead != NULL) { - *pFramesRead = 0; - } - - result = ((ma_sio_read_proc)pDevice->pContext->sndio.sio_read)((struct ma_sio_hdl*)pDevice->sndio.handleCapture, pPCMFrames, frameCount * ma_get_bytes_per_frame(pDevice->capture.internalFormat, pDevice->capture.internalChannels)); - if (result == 0) { - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[sndio] Failed to read data from the device to be sent to the device."); - return MA_IO_ERROR; - } - - if (pFramesRead != NULL) { - *pFramesRead = frameCount; - } - - return MA_SUCCESS; -} - -static ma_result ma_context_uninit__sndio(ma_context* pContext) -{ - MA_ASSERT(pContext != NULL); - MA_ASSERT(pContext->backend == ma_backend_sndio); - - (void)pContext; - return MA_SUCCESS; -} - -static ma_result ma_context_init__sndio(ma_context* pContext, const ma_context_config* pConfig, ma_backend_callbacks* pCallbacks) -{ -#ifndef MA_NO_RUNTIME_LINKING - const char* libsndioNames[] = { - "libsndio.so" - }; - size_t i; - - for (i = 0; i < ma_countof(libsndioNames); ++i) { - pContext->sndio.sndioSO = ma_dlopen(ma_context_get_log(pContext), libsndioNames[i]); - if (pContext->sndio.sndioSO != NULL) { - break; - } - } - - if (pContext->sndio.sndioSO == NULL) { - return MA_NO_BACKEND; - } - - pContext->sndio.sio_open = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->sndio.sndioSO, "sio_open"); - pContext->sndio.sio_close = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->sndio.sndioSO, "sio_close"); - pContext->sndio.sio_setpar = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->sndio.sndioSO, "sio_setpar"); - pContext->sndio.sio_getpar = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->sndio.sndioSO, "sio_getpar"); - pContext->sndio.sio_getcap = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->sndio.sndioSO, "sio_getcap"); - pContext->sndio.sio_write = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->sndio.sndioSO, "sio_write"); - pContext->sndio.sio_read = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->sndio.sndioSO, "sio_read"); - pContext->sndio.sio_start = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->sndio.sndioSO, "sio_start"); - pContext->sndio.sio_stop = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->sndio.sndioSO, "sio_stop"); - pContext->sndio.sio_initpar = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->sndio.sndioSO, "sio_initpar"); -#else - pContext->sndio.sio_open = sio_open; - pContext->sndio.sio_close = sio_close; - pContext->sndio.sio_setpar = sio_setpar; - pContext->sndio.sio_getpar = sio_getpar; - pContext->sndio.sio_getcap = sio_getcap; - pContext->sndio.sio_write = sio_write; - pContext->sndio.sio_read = sio_read; - pContext->sndio.sio_start = sio_start; - pContext->sndio.sio_stop = sio_stop; - pContext->sndio.sio_initpar = sio_initpar; -#endif - - pCallbacks->onContextInit = ma_context_init__sndio; - pCallbacks->onContextUninit = ma_context_uninit__sndio; - pCallbacks->onContextEnumerateDevices = ma_context_enumerate_devices__sndio; - pCallbacks->onContextGetDeviceInfo = ma_context_get_device_info__sndio; - pCallbacks->onDeviceInit = ma_device_init__sndio; - pCallbacks->onDeviceUninit = ma_device_uninit__sndio; - pCallbacks->onDeviceStart = ma_device_start__sndio; - pCallbacks->onDeviceStop = ma_device_stop__sndio; - pCallbacks->onDeviceRead = ma_device_read__sndio; - pCallbacks->onDeviceWrite = ma_device_write__sndio; - pCallbacks->onDeviceDataLoop = NULL; - - (void)pConfig; - return MA_SUCCESS; -} -#endif /* MA_HAS_SNDIO */ - - - -/****************************************************************************** - -audio(4) Backend - -******************************************************************************/ -#ifdef MA_HAS_AUDIO4 -#include -#include -#include -#include -#include -#include -#include - -#ifdef __NetBSD__ -#include -#endif - -#if defined(__OpenBSD__) - #include - #if defined(OpenBSD) && OpenBSD >= 201709 - #define MA_AUDIO4_USE_NEW_API - #endif -#endif - -static void ma_construct_device_id__audio4(char* id, size_t idSize, const char* base, int deviceIndex) -{ - size_t baseLen; - - MA_ASSERT(id != NULL); - MA_ASSERT(idSize > 0); - MA_ASSERT(deviceIndex >= 0); - - baseLen = strlen(base); - MA_ASSERT(idSize > baseLen); - - ma_strcpy_s(id, idSize, base); - ma_itoa_s(deviceIndex, id+baseLen, idSize-baseLen, 10); -} - -static ma_result ma_extract_device_index_from_id__audio4(const char* id, const char* base, int* pIndexOut) -{ - size_t idLen; - size_t baseLen; - const char* deviceIndexStr; - - MA_ASSERT(id != NULL); - MA_ASSERT(base != NULL); - MA_ASSERT(pIndexOut != NULL); - - idLen = strlen(id); - baseLen = strlen(base); - if (idLen <= baseLen) { - return MA_ERROR; /* Doesn't look like the id starts with the base. */ - } - - if (strncmp(id, base, baseLen) != 0) { - return MA_ERROR; /* ID does not begin with base. */ - } - - deviceIndexStr = id + baseLen; - if (deviceIndexStr[0] == '\0') { - return MA_ERROR; /* No index specified in the ID. */ - } - - if (pIndexOut) { - *pIndexOut = atoi(deviceIndexStr); - } - - return MA_SUCCESS; -} - - -#if !defined(MA_AUDIO4_USE_NEW_API) /* Old API */ -static ma_format ma_format_from_encoding__audio4(unsigned int encoding, unsigned int precision) -{ - if (precision == 8 && (encoding == AUDIO_ENCODING_ULINEAR || encoding == AUDIO_ENCODING_ULINEAR || encoding == AUDIO_ENCODING_ULINEAR_LE || encoding == AUDIO_ENCODING_ULINEAR_BE)) { - return ma_format_u8; - } else { - if (ma_is_little_endian() && encoding == AUDIO_ENCODING_SLINEAR_LE) { - if (precision == 16) { - return ma_format_s16; - } else if (precision == 24) { - return ma_format_s24; - } else if (precision == 32) { - return ma_format_s32; - } - } else if (ma_is_big_endian() && encoding == AUDIO_ENCODING_SLINEAR_BE) { - if (precision == 16) { - return ma_format_s16; - } else if (precision == 24) { - return ma_format_s24; - } else if (precision == 32) { - return ma_format_s32; - } - } - } - - return ma_format_unknown; /* Encoding not supported. */ -} - -static void ma_encoding_from_format__audio4(ma_format format, unsigned int* pEncoding, unsigned int* pPrecision) -{ - MA_ASSERT(pEncoding != NULL); - MA_ASSERT(pPrecision != NULL); - - switch (format) - { - case ma_format_u8: - { - *pEncoding = AUDIO_ENCODING_ULINEAR; - *pPrecision = 8; - } break; - - case ma_format_s24: - { - *pEncoding = (ma_is_little_endian()) ? AUDIO_ENCODING_SLINEAR_LE : AUDIO_ENCODING_SLINEAR_BE; - *pPrecision = 24; - } break; - - case ma_format_s32: - { - *pEncoding = (ma_is_little_endian()) ? AUDIO_ENCODING_SLINEAR_LE : AUDIO_ENCODING_SLINEAR_BE; - *pPrecision = 32; - } break; - - case ma_format_s16: - case ma_format_f32: - case ma_format_unknown: - default: - { - *pEncoding = (ma_is_little_endian()) ? AUDIO_ENCODING_SLINEAR_LE : AUDIO_ENCODING_SLINEAR_BE; - *pPrecision = 16; - } break; - } -} - -static ma_format ma_format_from_prinfo__audio4(struct audio_prinfo* prinfo) -{ - return ma_format_from_encoding__audio4(prinfo->encoding, prinfo->precision); -} - -static ma_format ma_best_format_from_fd__audio4(int fd, ma_format preferredFormat) -{ - audio_encoding_t encoding; - ma_uint32 iFormat; - int counter = 0; - - /* First check to see if the preferred format is supported. */ - if (preferredFormat != ma_format_unknown) { - counter = 0; - for (;;) { - MA_ZERO_OBJECT(&encoding); - encoding.index = counter; - if (ioctl(fd, AUDIO_GETENC, &encoding) < 0) { - break; - } - - if (preferredFormat == ma_format_from_encoding__audio4(encoding.encoding, encoding.precision)) { - return preferredFormat; /* Found the preferred format. */ - } - - /* Getting here means this encoding does not match our preferred format so we need to more on to the next encoding. */ - counter += 1; - } - } - - /* Getting here means our preferred format is not supported, so fall back to our standard priorities. */ - for (iFormat = 0; iFormat < ma_countof(g_maFormatPriorities); iFormat += 1) { - ma_format format = g_maFormatPriorities[iFormat]; - - counter = 0; - for (;;) { - MA_ZERO_OBJECT(&encoding); - encoding.index = counter; - if (ioctl(fd, AUDIO_GETENC, &encoding) < 0) { - break; - } - - if (format == ma_format_from_encoding__audio4(encoding.encoding, encoding.precision)) { - return format; /* Found a workable format. */ - } - - /* Getting here means this encoding does not match our preferred format so we need to more on to the next encoding. */ - counter += 1; - } - } - - /* Getting here means not appropriate format was found. */ - return ma_format_unknown; -} -#else -static ma_format ma_format_from_swpar__audio4(struct audio_swpar* par) -{ - if (par->bits == 8 && par->bps == 1 && par->sig == 0) { - return ma_format_u8; - } - if (par->bits == 16 && par->bps == 2 && par->sig == 1 && par->le == ma_is_little_endian()) { - return ma_format_s16; - } - if (par->bits == 24 && par->bps == 3 && par->sig == 1 && par->le == ma_is_little_endian()) { - return ma_format_s24; - } - if (par->bits == 32 && par->bps == 4 && par->sig == 1 && par->le == ma_is_little_endian()) { - return ma_format_f32; - } - - /* Format not supported. */ - return ma_format_unknown; -} -#endif - -static ma_result ma_context_get_device_info_from_fd__audio4(ma_context* pContext, ma_device_type deviceType, int fd, ma_device_info* pDeviceInfo) -{ - audio_device_t fdDevice; - - MA_ASSERT(pContext != NULL); - MA_ASSERT(fd >= 0); - MA_ASSERT(pDeviceInfo != NULL); - - (void)pContext; - (void)deviceType; - - if (ioctl(fd, AUDIO_GETDEV, &fdDevice) < 0) { - return MA_ERROR; /* Failed to retrieve device info. */ - } - - /* Name. */ - ma_strcpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), fdDevice.name); - - #if !defined(MA_AUDIO4_USE_NEW_API) - { - audio_info_t fdInfo; - int counter = 0; - ma_uint32 channels; - ma_uint32 sampleRate; - -#if defined(__NetBSD__) && (__NetBSD_Version__ >= 900000000) - if (ioctl(fd, AUDIO_GETFORMAT, &fdInfo) < 0) { - return MA_ERROR; - } -#else - if (ioctl(fd, AUDIO_GETINFO, &fdInfo) < 0) { - return MA_ERROR; - } -#endif - - if (deviceType == ma_device_type_playback) { - channels = fdInfo.play.channels; - sampleRate = fdInfo.play.sample_rate; - } else { - channels = fdInfo.record.channels; - sampleRate = fdInfo.record.sample_rate; - } - - /* Supported formats. We get this by looking at the encodings. */ - pDeviceInfo->nativeDataFormatCount = 0; - for (;;) { - audio_encoding_t encoding; - ma_format format; - - MA_ZERO_OBJECT(&encoding); - encoding.index = counter; - if (ioctl(fd, AUDIO_GETENC, &encoding) < 0) { - break; - } - - format = ma_format_from_encoding__audio4(encoding.encoding, encoding.precision); - if (format != ma_format_unknown) { - ma_device_info_add_native_data_format(pDeviceInfo, format, channels, sampleRate, 0); - } - - counter += 1; - } - } - #else - { - struct audio_swpar fdPar; - ma_format format; - ma_uint32 channels; - ma_uint32 sampleRate; - - if (ioctl(fd, AUDIO_GETPAR, &fdPar) < 0) { - return MA_ERROR; - } - - format = ma_format_from_swpar__audio4(&fdPar); - if (format == ma_format_unknown) { - return MA_FORMAT_NOT_SUPPORTED; - } - - if (deviceType == ma_device_type_playback) { - channels = fdPar.pchan; - } else { - channels = fdPar.rchan; - } - - sampleRate = fdPar.rate; - - pDeviceInfo->nativeDataFormatCount = 0; - ma_device_info_add_native_data_format(pDeviceInfo, format, channels, sampleRate, 0); - } - #endif - - return MA_SUCCESS; -} - -static ma_result ma_context_enumerate_devices__audio4(ma_context* pContext, ma_enum_devices_callback_proc callback, void* pUserData) -{ - const int maxDevices = 64; - char devpath[256]; - int iDevice; - - MA_ASSERT(pContext != NULL); - MA_ASSERT(callback != NULL); - - /* - Every device will be named "/dev/audioN", with a "/dev/audioctlN" equivalent. We use the "/dev/audioctlN" - version here since we can open it even when another process has control of the "/dev/audioN" device. - */ - for (iDevice = 0; iDevice < maxDevices; ++iDevice) { - struct stat st; - int fd; - ma_bool32 isTerminating = MA_FALSE; - - ma_strcpy_s(devpath, sizeof(devpath), "/dev/audioctl"); - ma_itoa_s(iDevice, devpath+strlen(devpath), sizeof(devpath)-strlen(devpath), 10); - - if (stat(devpath, &st) < 0) { - break; - } - - /* The device exists, but we need to check if it's usable as playback and/or capture. */ - - /* Playback. */ - if (!isTerminating) { - fd = open(devpath, O_RDONLY, 0); - if (fd >= 0) { - /* Supports playback. */ - ma_device_info deviceInfo; - MA_ZERO_OBJECT(&deviceInfo); - ma_construct_device_id__audio4(deviceInfo.id.audio4, sizeof(deviceInfo.id.audio4), "/dev/audio", iDevice); - if (ma_context_get_device_info_from_fd__audio4(pContext, ma_device_type_playback, fd, &deviceInfo) == MA_SUCCESS) { - isTerminating = !callback(pContext, ma_device_type_playback, &deviceInfo, pUserData); - } - - close(fd); - } - } - - /* Capture. */ - if (!isTerminating) { - fd = open(devpath, O_WRONLY, 0); - if (fd >= 0) { - /* Supports capture. */ - ma_device_info deviceInfo; - MA_ZERO_OBJECT(&deviceInfo); - ma_construct_device_id__audio4(deviceInfo.id.audio4, sizeof(deviceInfo.id.audio4), "/dev/audio", iDevice); - if (ma_context_get_device_info_from_fd__audio4(pContext, ma_device_type_capture, fd, &deviceInfo) == MA_SUCCESS) { - isTerminating = !callback(pContext, ma_device_type_capture, &deviceInfo, pUserData); - } - - close(fd); - } - } - - if (isTerminating) { - break; - } - } - - return MA_SUCCESS; -} - -static ma_result ma_context_get_device_info__audio4(ma_context* pContext, ma_device_type deviceType, const ma_device_id* pDeviceID, ma_device_info* pDeviceInfo) -{ - int fd = -1; - int deviceIndex = -1; - char ctlid[256]; - ma_result result; - - MA_ASSERT(pContext != NULL); - - /* - We need to open the "/dev/audioctlN" device to get the info. To do this we need to extract the number - from the device ID which will be in "/dev/audioN" format. - */ - if (pDeviceID == NULL) { - /* Default device. */ - ma_strcpy_s(ctlid, sizeof(ctlid), "/dev/audioctl"); - } else { - /* Specific device. We need to convert from "/dev/audioN" to "/dev/audioctlN". */ - result = ma_extract_device_index_from_id__audio4(pDeviceID->audio4, "/dev/audio", &deviceIndex); - if (result != MA_SUCCESS) { - return result; - } - - ma_construct_device_id__audio4(ctlid, sizeof(ctlid), "/dev/audioctl", deviceIndex); - } - - fd = open(ctlid, (deviceType == ma_device_type_playback) ? O_WRONLY : O_RDONLY, 0); - if (fd == -1) { - return MA_NO_DEVICE; - } - - if (deviceIndex == -1) { - ma_strcpy_s(pDeviceInfo->id.audio4, sizeof(pDeviceInfo->id.audio4), "/dev/audio"); - } else { - ma_construct_device_id__audio4(pDeviceInfo->id.audio4, sizeof(pDeviceInfo->id.audio4), "/dev/audio", deviceIndex); - } - - result = ma_context_get_device_info_from_fd__audio4(pContext, deviceType, fd, pDeviceInfo); - - close(fd); - return result; -} - -static ma_result ma_device_uninit__audio4(ma_device* pDevice) -{ - MA_ASSERT(pDevice != NULL); - - if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) { - close(pDevice->audio4.fdCapture); - } - - if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) { - close(pDevice->audio4.fdPlayback); - } - - return MA_SUCCESS; -} - -static ma_result ma_device_init_fd__audio4(ma_device* pDevice, const ma_device_config* pConfig, ma_device_descriptor* pDescriptor, ma_device_type deviceType) -{ - const char* pDefaultDeviceNames[] = { - "/dev/audio", - "/dev/audio0" - }; - const char* pDefaultDeviceCtlNames[] = { - "/dev/audioctl", - "/dev/audioctl0" - }; - int fd; - int fdFlags = 0; - size_t iDefaultDevice = (size_t)-1; - ma_format internalFormat; - ma_uint32 internalChannels; - ma_uint32 internalSampleRate; - ma_uint32 internalPeriodSizeInFrames; - ma_uint32 internalPeriods; - - MA_ASSERT(pConfig != NULL); - MA_ASSERT(deviceType != ma_device_type_duplex); - MA_ASSERT(pDevice != NULL); - - /* The first thing to do is open the file. */ - if (deviceType == ma_device_type_capture) { - fdFlags = O_RDONLY; - } else { - fdFlags = O_WRONLY; - } - /*fdFlags |= O_NONBLOCK;*/ - - /* Find the index of the default device as a start. We'll use this index later. Set it to (size_t)-1 otherwise. */ - if (pDescriptor->pDeviceID == NULL) { - /* Default device. */ - for (iDefaultDevice = 0; iDefaultDevice < ma_countof(pDefaultDeviceNames); ++iDefaultDevice) { - fd = open(pDefaultDeviceNames[iDefaultDevice], fdFlags, 0); - if (fd != -1) { - break; - } - } - } else { - /* Specific device. */ - fd = open(pDescriptor->pDeviceID->audio4, fdFlags, 0); - - for (iDefaultDevice = 0; iDefaultDevice < ma_countof(pDefaultDeviceNames); iDefaultDevice += 1) { - if (ma_strcmp(pDefaultDeviceNames[iDefaultDevice], pDescriptor->pDeviceID->audio4) == 0) { - break; - } - } - - if (iDefaultDevice == ma_countof(pDefaultDeviceNames)) { - iDefaultDevice = (size_t)-1; - } - } - - if (fd == -1) { - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[audio4] Failed to open device."); - return ma_result_from_errno(errno); - } - - #if !defined(MA_AUDIO4_USE_NEW_API) /* Old API */ - { - audio_info_t fdInfo; - int fdInfoResult = -1; - - /* - The documentation is a little bit unclear to me as to how it handles formats. It says the - following: - - Regardless of formats supported by underlying driver, the audio driver accepts the - following formats. - - By then the next sentence says this: - - `encoding` and `precision` are one of the values obtained by AUDIO_GETENC. - - It sounds like a direct contradiction to me. I'm going to play this safe any only use the - best sample format returned by AUDIO_GETENC. If the requested format is supported we'll - use that, but otherwise we'll just use our standard format priorities to pick an - appropriate one. - */ - AUDIO_INITINFO(&fdInfo); - - /* - Get the default format from the audioctl file if we're asking for a default device. If we - retrieve it from /dev/audio it'll default to mono 8000Hz. - */ - if (iDefaultDevice != (size_t)-1) { - /* We're using a default device. Get the info from the /dev/audioctl file instead of /dev/audio. */ - int fdctl = open(pDefaultDeviceCtlNames[iDefaultDevice], fdFlags, 0); - if (fdctl != -1) { -#if defined(__NetBSD__) && (__NetBSD_Version__ >= 900000000) - fdInfoResult = ioctl(fdctl, AUDIO_GETFORMAT, &fdInfo); -#else - fdInfoResult = ioctl(fdctl, AUDIO_GETINFO, &fdInfo); -#endif - close(fdctl); - } - } - - if (fdInfoResult == -1) { - /* We still don't have the default device info so just retrieve it from the main audio device. */ - if (ioctl(fd, AUDIO_GETINFO, &fdInfo) < 0) { - close(fd); - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[audio4] AUDIO_GETINFO failed."); - return ma_result_from_errno(errno); - } - } - - /* We get the driver to do as much of the data conversion as possible. */ - if (deviceType == ma_device_type_capture) { - fdInfo.mode = AUMODE_RECORD; - ma_encoding_from_format__audio4(ma_best_format_from_fd__audio4(fd, pDescriptor->format), &fdInfo.record.encoding, &fdInfo.record.precision); - - if (pDescriptor->channels != 0) { - fdInfo.record.channels = ma_clamp(pDescriptor->channels, 1, 12); /* From the documentation: `channels` ranges from 1 to 12. */ - } - - if (pDescriptor->sampleRate != 0) { - fdInfo.record.sample_rate = ma_clamp(pDescriptor->sampleRate, 1000, 192000); /* From the documentation: `frequency` ranges from 1000Hz to 192000Hz. (They mean `sample_rate` instead of `frequency`.) */ - } - } else { - fdInfo.mode = AUMODE_PLAY; - ma_encoding_from_format__audio4(ma_best_format_from_fd__audio4(fd, pDescriptor->format), &fdInfo.play.encoding, &fdInfo.play.precision); - - if (pDescriptor->channels != 0) { - fdInfo.play.channels = ma_clamp(pDescriptor->channels, 1, 12); /* From the documentation: `channels` ranges from 1 to 12. */ - } - - if (pDescriptor->sampleRate != 0) { - fdInfo.play.sample_rate = ma_clamp(pDescriptor->sampleRate, 1000, 192000); /* From the documentation: `frequency` ranges from 1000Hz to 192000Hz. (They mean `sample_rate` instead of `frequency`.) */ - } - } - - if (ioctl(fd, AUDIO_SETINFO, &fdInfo) < 0) { - close(fd); - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[audio4] Failed to set device format. AUDIO_SETINFO failed."); - return ma_result_from_errno(errno); - } - - if (ioctl(fd, AUDIO_GETINFO, &fdInfo) < 0) { - close(fd); - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[audio4] AUDIO_GETINFO failed."); - return ma_result_from_errno(errno); - } - - if (deviceType == ma_device_type_capture) { - internalFormat = ma_format_from_prinfo__audio4(&fdInfo.record); - internalChannels = fdInfo.record.channels; - internalSampleRate = fdInfo.record.sample_rate; - } else { - internalFormat = ma_format_from_prinfo__audio4(&fdInfo.play); - internalChannels = fdInfo.play.channels; - internalSampleRate = fdInfo.play.sample_rate; - } - - if (internalFormat == ma_format_unknown) { - close(fd); - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[audio4] The device's internal device format is not supported by miniaudio. The device is unusable."); - return MA_FORMAT_NOT_SUPPORTED; - } - - /* Buffer. */ - { - ma_uint32 internalPeriodSizeInBytes; - - internalPeriodSizeInFrames = ma_calculate_buffer_size_in_frames_from_descriptor(pDescriptor, internalSampleRate, pConfig->performanceProfile); - - internalPeriodSizeInBytes = internalPeriodSizeInFrames * ma_get_bytes_per_frame(internalFormat, internalChannels); - if (internalPeriodSizeInBytes < 16) { - internalPeriodSizeInBytes = 16; - } - - internalPeriods = pDescriptor->periodCount; - if (internalPeriods < 2) { - internalPeriods = 2; - } - - /* What miniaudio calls a period, audio4 calls a block. */ - AUDIO_INITINFO(&fdInfo); - fdInfo.hiwat = internalPeriods; - fdInfo.lowat = internalPeriods-1; - fdInfo.blocksize = internalPeriodSizeInBytes; - if (ioctl(fd, AUDIO_SETINFO, &fdInfo) < 0) { - close(fd); - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[audio4] Failed to set internal buffer size. AUDIO_SETINFO failed."); - return ma_result_from_errno(errno); - } - - internalPeriods = fdInfo.hiwat; - internalPeriodSizeInFrames = fdInfo.blocksize / ma_get_bytes_per_frame(internalFormat, internalChannels); - } - } - #else - { - struct audio_swpar fdPar; - - /* We need to retrieve the format of the device so we can know the channel count and sample rate. Then we can calculate the buffer size. */ - if (ioctl(fd, AUDIO_GETPAR, &fdPar) < 0) { - close(fd); - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[audio4] Failed to retrieve initial device parameters."); - return ma_result_from_errno(errno); - } - - internalFormat = ma_format_from_swpar__audio4(&fdPar); - internalChannels = (deviceType == ma_device_type_capture) ? fdPar.rchan : fdPar.pchan; - internalSampleRate = fdPar.rate; - - if (internalFormat == ma_format_unknown) { - close(fd); - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[audio4] The device's internal device format is not supported by miniaudio. The device is unusable."); - return MA_FORMAT_NOT_SUPPORTED; - } - - /* Buffer. */ - { - ma_uint32 internalPeriodSizeInBytes; - - internalPeriodSizeInFrames = ma_calculate_buffer_size_in_frames_from_descriptor(pDescriptor, internalSampleRate, pConfig->performanceProfile); - - /* What miniaudio calls a period, audio4 calls a block. */ - internalPeriodSizeInBytes = internalPeriodSizeInFrames * ma_get_bytes_per_frame(internalFormat, internalChannels); - if (internalPeriodSizeInBytes < 16) { - internalPeriodSizeInBytes = 16; - } - - fdPar.nblks = pDescriptor->periodCount; - fdPar.round = internalPeriodSizeInBytes; - - if (ioctl(fd, AUDIO_SETPAR, &fdPar) < 0) { - close(fd); - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[audio4] Failed to set device parameters."); - return ma_result_from_errno(errno); - } - - if (ioctl(fd, AUDIO_GETPAR, &fdPar) < 0) { - close(fd); - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[audio4] Failed to retrieve actual device parameters."); - return ma_result_from_errno(errno); - } - } - - internalFormat = ma_format_from_swpar__audio4(&fdPar); - internalChannels = (deviceType == ma_device_type_capture) ? fdPar.rchan : fdPar.pchan; - internalSampleRate = fdPar.rate; - internalPeriods = fdPar.nblks; - internalPeriodSizeInFrames = fdPar.round / ma_get_bytes_per_frame(internalFormat, internalChannels); - } - #endif - - if (internalFormat == ma_format_unknown) { - close(fd); - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[audio4] The device's internal device format is not supported by miniaudio. The device is unusable."); - return MA_FORMAT_NOT_SUPPORTED; - } - - if (deviceType == ma_device_type_capture) { - pDevice->audio4.fdCapture = fd; - } else { - pDevice->audio4.fdPlayback = fd; - } - - pDescriptor->format = internalFormat; - pDescriptor->channels = internalChannels; - pDescriptor->sampleRate = internalSampleRate; - ma_channel_map_init_standard(ma_standard_channel_map_sound4, pDescriptor->channelMap, ma_countof(pDescriptor->channelMap), internalChannels); - pDescriptor->periodSizeInFrames = internalPeriodSizeInFrames; - pDescriptor->periodCount = internalPeriods; - - return MA_SUCCESS; -} - -static ma_result ma_device_init__audio4(ma_device* pDevice, const ma_device_config* pConfig, ma_device_descriptor* pDescriptorPlayback, ma_device_descriptor* pDescriptorCapture) -{ - MA_ASSERT(pDevice != NULL); - - MA_ZERO_OBJECT(&pDevice->audio4); - - if (pConfig->deviceType == ma_device_type_loopback) { - return MA_DEVICE_TYPE_NOT_SUPPORTED; - } - - pDevice->audio4.fdCapture = -1; - pDevice->audio4.fdPlayback = -1; - - /* - The version of the operating system dictates whether or not the device is exclusive or shared. NetBSD - introduced in-kernel mixing which means it's shared. All other BSD flavours are exclusive as far as - I'm aware. - */ -#if defined(__NetBSD_Version__) && __NetBSD_Version__ >= 800000000 - /* NetBSD 8.0+ */ - if (((pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) && pDescriptorPlayback->shareMode == ma_share_mode_exclusive) || - ((pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) && pDescriptorCapture->shareMode == ma_share_mode_exclusive)) { - return MA_SHARE_MODE_NOT_SUPPORTED; - } -#else - /* All other flavors. */ -#endif - - if (pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) { - ma_result result = ma_device_init_fd__audio4(pDevice, pConfig, pDescriptorCapture, ma_device_type_capture); - if (result != MA_SUCCESS) { - return result; - } - } - - if (pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) { - ma_result result = ma_device_init_fd__audio4(pDevice, pConfig, pDescriptorPlayback, ma_device_type_playback); - if (result != MA_SUCCESS) { - if (pConfig->deviceType == ma_device_type_duplex) { - close(pDevice->audio4.fdCapture); - } - return result; - } - } - - return MA_SUCCESS; -} - -static ma_result ma_device_start__audio4(ma_device* pDevice) -{ - MA_ASSERT(pDevice != NULL); - - if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) { - if (pDevice->audio4.fdCapture == -1) { - return MA_INVALID_ARGS; - } - } - - if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) { - if (pDevice->audio4.fdPlayback == -1) { - return MA_INVALID_ARGS; - } - } - - return MA_SUCCESS; -} - -static ma_result ma_device_stop_fd__audio4(ma_device* pDevice, int fd) -{ - if (fd == -1) { - return MA_INVALID_ARGS; - } - -#if !defined(MA_AUDIO4_USE_NEW_API) - if (ioctl(fd, AUDIO_FLUSH, 0) < 0) { - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[audio4] Failed to stop device. AUDIO_FLUSH failed."); - return ma_result_from_errno(errno); - } -#else - if (ioctl(fd, AUDIO_STOP, 0) < 0) { - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[audio4] Failed to stop device. AUDIO_STOP failed."); - return ma_result_from_errno(errno); - } -#endif - - return MA_SUCCESS; -} - -static ma_result ma_device_stop__audio4(ma_device* pDevice) -{ - MA_ASSERT(pDevice != NULL); - - if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) { - ma_result result; - - result = ma_device_stop_fd__audio4(pDevice, pDevice->audio4.fdCapture); - if (result != MA_SUCCESS) { - return result; - } - } - - if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) { - ma_result result; - - /* Drain the device first. If this fails we'll just need to flush without draining. Unfortunately draining isn't available on newer version of OpenBSD. */ - #if !defined(MA_AUDIO4_USE_NEW_API) - ioctl(pDevice->audio4.fdPlayback, AUDIO_DRAIN, 0); - #endif - - /* Here is where the device is stopped immediately. */ - result = ma_device_stop_fd__audio4(pDevice, pDevice->audio4.fdPlayback); - if (result != MA_SUCCESS) { - return result; - } - } - - return MA_SUCCESS; -} - -static ma_result ma_device_write__audio4(ma_device* pDevice, const void* pPCMFrames, ma_uint32 frameCount, ma_uint32* pFramesWritten) -{ - int result; - - if (pFramesWritten != NULL) { - *pFramesWritten = 0; - } - - result = write(pDevice->audio4.fdPlayback, pPCMFrames, frameCount * ma_get_bytes_per_frame(pDevice->playback.internalFormat, pDevice->playback.internalChannels)); - if (result < 0) { - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[audio4] Failed to write data to the device."); - return ma_result_from_errno(errno); - } - - if (pFramesWritten != NULL) { - *pFramesWritten = (ma_uint32)result / ma_get_bytes_per_frame(pDevice->playback.internalFormat, pDevice->playback.internalChannels); - } - - return MA_SUCCESS; -} - -static ma_result ma_device_read__audio4(ma_device* pDevice, void* pPCMFrames, ma_uint32 frameCount, ma_uint32* pFramesRead) -{ - int result; - - if (pFramesRead != NULL) { - *pFramesRead = 0; - } - - result = read(pDevice->audio4.fdCapture, pPCMFrames, frameCount * ma_get_bytes_per_frame(pDevice->capture.internalFormat, pDevice->capture.internalChannels)); - if (result < 0) { - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[audio4] Failed to read data from the device."); - return ma_result_from_errno(errno); - } - - if (pFramesRead != NULL) { - *pFramesRead = (ma_uint32)result / ma_get_bytes_per_frame(pDevice->capture.internalFormat, pDevice->capture.internalChannels); - } - - return MA_SUCCESS; -} - -static ma_result ma_context_uninit__audio4(ma_context* pContext) -{ - MA_ASSERT(pContext != NULL); - MA_ASSERT(pContext->backend == ma_backend_audio4); - - (void)pContext; - return MA_SUCCESS; -} - -static ma_result ma_context_init__audio4(ma_context* pContext, const ma_context_config* pConfig, ma_backend_callbacks* pCallbacks) -{ - MA_ASSERT(pContext != NULL); - - (void)pConfig; - - pCallbacks->onContextInit = ma_context_init__audio4; - pCallbacks->onContextUninit = ma_context_uninit__audio4; - pCallbacks->onContextEnumerateDevices = ma_context_enumerate_devices__audio4; - pCallbacks->onContextGetDeviceInfo = ma_context_get_device_info__audio4; - pCallbacks->onDeviceInit = ma_device_init__audio4; - pCallbacks->onDeviceUninit = ma_device_uninit__audio4; - pCallbacks->onDeviceStart = ma_device_start__audio4; - pCallbacks->onDeviceStop = ma_device_stop__audio4; - pCallbacks->onDeviceRead = ma_device_read__audio4; - pCallbacks->onDeviceWrite = ma_device_write__audio4; - pCallbacks->onDeviceDataLoop = NULL; - - return MA_SUCCESS; -} -#endif /* MA_HAS_AUDIO4 */ - - -/****************************************************************************** - -OSS Backend - -******************************************************************************/ -#ifdef MA_HAS_OSS -#include -#include -#include -#include - -#ifndef SNDCTL_DSP_HALT -#define SNDCTL_DSP_HALT SNDCTL_DSP_RESET -#endif - -#define MA_OSS_DEFAULT_DEVICE_NAME "/dev/dsp" - -static int ma_open_temp_device__oss() -{ - /* The OSS sample code uses "/dev/mixer" as the device for getting system properties so I'm going to do the same. */ - int fd = open("/dev/mixer", O_RDONLY, 0); - if (fd >= 0) { - return fd; - } - - return -1; -} - -static ma_result ma_context_open_device__oss(ma_context* pContext, ma_device_type deviceType, const ma_device_id* pDeviceID, ma_share_mode shareMode, int* pfd) -{ - const char* deviceName; - int flags; - - MA_ASSERT(pContext != NULL); - MA_ASSERT(pfd != NULL); - (void)pContext; - - *pfd = -1; - - /* This function should only be called for playback or capture, not duplex. */ - if (deviceType == ma_device_type_duplex) { - return MA_INVALID_ARGS; - } - - deviceName = MA_OSS_DEFAULT_DEVICE_NAME; - if (pDeviceID != NULL) { - deviceName = pDeviceID->oss; - } - - flags = (deviceType == ma_device_type_playback) ? O_WRONLY : O_RDONLY; - if (shareMode == ma_share_mode_exclusive) { - flags |= O_EXCL; - } - - *pfd = open(deviceName, flags, 0); - if (*pfd == -1) { - return ma_result_from_errno(errno); - } - - return MA_SUCCESS; -} - -static ma_result ma_context_enumerate_devices__oss(ma_context* pContext, ma_enum_devices_callback_proc callback, void* pUserData) -{ - int fd; - oss_sysinfo si; - int result; - - MA_ASSERT(pContext != NULL); - MA_ASSERT(callback != NULL); - - fd = ma_open_temp_device__oss(); - if (fd == -1) { - ma_log_post(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[OSS] Failed to open a temporary device for retrieving system information used for device enumeration."); - return MA_NO_BACKEND; - } - - result = ioctl(fd, SNDCTL_SYSINFO, &si); - if (result != -1) { - int iAudioDevice; - for (iAudioDevice = 0; iAudioDevice < si.numaudios; ++iAudioDevice) { - oss_audioinfo ai; - ai.dev = iAudioDevice; - result = ioctl(fd, SNDCTL_AUDIOINFO, &ai); - if (result != -1) { - if (ai.devnode[0] != '\0') { /* <-- Can be blank, according to documentation. */ - ma_device_info deviceInfo; - ma_bool32 isTerminating = MA_FALSE; - - MA_ZERO_OBJECT(&deviceInfo); - - /* ID */ - ma_strncpy_s(deviceInfo.id.oss, sizeof(deviceInfo.id.oss), ai.devnode, (size_t)-1); - - /* - The human readable device name should be in the "ai.handle" variable, but it can - sometimes be empty in which case we just fall back to "ai.name" which is less user - friendly, but usually has a value. - */ - if (ai.handle[0] != '\0') { - ma_strncpy_s(deviceInfo.name, sizeof(deviceInfo.name), ai.handle, (size_t)-1); - } else { - ma_strncpy_s(deviceInfo.name, sizeof(deviceInfo.name), ai.name, (size_t)-1); - } - - /* The device can be both playback and capture. */ - if (!isTerminating && (ai.caps & PCM_CAP_OUTPUT) != 0) { - isTerminating = !callback(pContext, ma_device_type_playback, &deviceInfo, pUserData); - } - if (!isTerminating && (ai.caps & PCM_CAP_INPUT) != 0) { - isTerminating = !callback(pContext, ma_device_type_capture, &deviceInfo, pUserData); - } - - if (isTerminating) { - break; - } - } - } - } - } else { - close(fd); - ma_log_post(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[OSS] Failed to retrieve system information for device enumeration."); - return MA_NO_BACKEND; - } - - close(fd); - return MA_SUCCESS; -} - -static void ma_context_add_native_data_format__oss(ma_context* pContext, oss_audioinfo* pAudioInfo, ma_format format, ma_device_info* pDeviceInfo) -{ - unsigned int minChannels; - unsigned int maxChannels; - unsigned int iRate; - - MA_ASSERT(pContext != NULL); - MA_ASSERT(pAudioInfo != NULL); - MA_ASSERT(pDeviceInfo != NULL); - - /* If we support all channels we just report 0. */ - minChannels = ma_clamp(pAudioInfo->min_channels, MA_MIN_CHANNELS, MA_MAX_CHANNELS); - maxChannels = ma_clamp(pAudioInfo->max_channels, MA_MIN_CHANNELS, MA_MAX_CHANNELS); - - /* - OSS has this annoying thing where sample rates can be reported in two ways. We prefer explicitness, - which OSS has in the form of nrates/rates, however there are times where nrates can be 0, in which - case we'll need to use min_rate and max_rate and report only standard rates. - */ - if (pAudioInfo->nrates > 0) { - for (iRate = 0; iRate < pAudioInfo->nrates; iRate += 1) { - unsigned int rate = pAudioInfo->rates[iRate]; - - if (minChannels == MA_MIN_CHANNELS && maxChannels == MA_MAX_CHANNELS) { - ma_device_info_add_native_data_format(pDeviceInfo, format, 0, rate, 0); /* Set the channel count to 0 to indicate that all channel counts are supported. */ - } else { - unsigned int iChannel; - for (iChannel = minChannels; iChannel <= maxChannels; iChannel += 1) { - ma_device_info_add_native_data_format(pDeviceInfo, format, iChannel, rate, 0); - } - } - } - } else { - for (iRate = 0; iRate < ma_countof(g_maStandardSampleRatePriorities); iRate += 1) { - ma_uint32 standardRate = g_maStandardSampleRatePriorities[iRate]; - - if (standardRate >= (ma_uint32)pAudioInfo->min_rate && standardRate <= (ma_uint32)pAudioInfo->max_rate) { - if (minChannels == MA_MIN_CHANNELS && maxChannels == MA_MAX_CHANNELS) { - ma_device_info_add_native_data_format(pDeviceInfo, format, 0, standardRate, 0); /* Set the channel count to 0 to indicate that all channel counts are supported. */ - } else { - unsigned int iChannel; - for (iChannel = minChannels; iChannel <= maxChannels; iChannel += 1) { - ma_device_info_add_native_data_format(pDeviceInfo, format, iChannel, standardRate, 0); - } - } - } - } - } -} - -static ma_result ma_context_get_device_info__oss(ma_context* pContext, ma_device_type deviceType, const ma_device_id* pDeviceID, ma_device_info* pDeviceInfo) -{ - ma_bool32 foundDevice; - int fdTemp; - oss_sysinfo si; - int result; - - MA_ASSERT(pContext != NULL); - - /* Handle the default device a little differently. */ - if (pDeviceID == NULL) { - if (deviceType == ma_device_type_playback) { - ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), MA_DEFAULT_PLAYBACK_DEVICE_NAME, (size_t)-1); - } else { - ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), MA_DEFAULT_CAPTURE_DEVICE_NAME, (size_t)-1); - } - - return MA_SUCCESS; - } - - - /* If we get here it means we are _not_ using the default device. */ - foundDevice = MA_FALSE; - - fdTemp = ma_open_temp_device__oss(); - if (fdTemp == -1) { - ma_log_post(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[OSS] Failed to open a temporary device for retrieving system information used for device enumeration."); - return MA_NO_BACKEND; - } - - result = ioctl(fdTemp, SNDCTL_SYSINFO, &si); - if (result != -1) { - int iAudioDevice; - for (iAudioDevice = 0; iAudioDevice < si.numaudios; ++iAudioDevice) { - oss_audioinfo ai; - ai.dev = iAudioDevice; - result = ioctl(fdTemp, SNDCTL_AUDIOINFO, &ai); - if (result != -1) { - if (ma_strcmp(ai.devnode, pDeviceID->oss) == 0) { - /* It has the same name, so now just confirm the type. */ - if ((deviceType == ma_device_type_playback && ((ai.caps & PCM_CAP_OUTPUT) != 0)) || - (deviceType == ma_device_type_capture && ((ai.caps & PCM_CAP_INPUT) != 0))) { - unsigned int formatMask; - - /* ID */ - ma_strncpy_s(pDeviceInfo->id.oss, sizeof(pDeviceInfo->id.oss), ai.devnode, (size_t)-1); - - /* - The human readable device name should be in the "ai.handle" variable, but it can - sometimes be empty in which case we just fall back to "ai.name" which is less user - friendly, but usually has a value. - */ - if (ai.handle[0] != '\0') { - ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), ai.handle, (size_t)-1); - } else { - ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), ai.name, (size_t)-1); - } - - - pDeviceInfo->nativeDataFormatCount = 0; - - if (deviceType == ma_device_type_playback) { - formatMask = ai.oformats; - } else { - formatMask = ai.iformats; - } - - if (((formatMask & AFMT_S16_LE) != 0 && ma_is_little_endian()) || (AFMT_S16_BE && ma_is_big_endian())) { - ma_context_add_native_data_format__oss(pContext, &ai, ma_format_s16, pDeviceInfo); - } - if (((formatMask & AFMT_S32_LE) != 0 && ma_is_little_endian()) || (AFMT_S32_BE && ma_is_big_endian())) { - ma_context_add_native_data_format__oss(pContext, &ai, ma_format_s32, pDeviceInfo); - } - if ((formatMask & AFMT_U8) != 0) { - ma_context_add_native_data_format__oss(pContext, &ai, ma_format_u8, pDeviceInfo); - } - - foundDevice = MA_TRUE; - break; - } - } - } - } - } else { - close(fdTemp); - ma_log_post(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[OSS] Failed to retrieve system information for device enumeration."); - return MA_NO_BACKEND; - } - - - close(fdTemp); - - if (!foundDevice) { - return MA_NO_DEVICE; - } - - return MA_SUCCESS; -} - -static ma_result ma_device_uninit__oss(ma_device* pDevice) -{ - MA_ASSERT(pDevice != NULL); - - if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) { - close(pDevice->oss.fdCapture); - } - - if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) { - close(pDevice->oss.fdPlayback); - } - - return MA_SUCCESS; -} - -static int ma_format_to_oss(ma_format format) -{ - int ossFormat = AFMT_U8; - switch (format) { - case ma_format_s16: ossFormat = (ma_is_little_endian()) ? AFMT_S16_LE : AFMT_S16_BE; break; - case ma_format_s24: ossFormat = (ma_is_little_endian()) ? AFMT_S32_LE : AFMT_S32_BE; break; - case ma_format_s32: ossFormat = (ma_is_little_endian()) ? AFMT_S32_LE : AFMT_S32_BE; break; - case ma_format_f32: ossFormat = (ma_is_little_endian()) ? AFMT_S16_LE : AFMT_S16_BE; break; - case ma_format_u8: - default: ossFormat = AFMT_U8; break; - } - - return ossFormat; -} - -static ma_format ma_format_from_oss(int ossFormat) -{ - if (ossFormat == AFMT_U8) { - return ma_format_u8; - } else { - if (ma_is_little_endian()) { - switch (ossFormat) { - case AFMT_S16_LE: return ma_format_s16; - case AFMT_S32_LE: return ma_format_s32; - default: return ma_format_unknown; - } - } else { - switch (ossFormat) { - case AFMT_S16_BE: return ma_format_s16; - case AFMT_S32_BE: return ma_format_s32; - default: return ma_format_unknown; - } - } - } - - return ma_format_unknown; -} - -static ma_result ma_device_init_fd__oss(ma_device* pDevice, const ma_device_config* pConfig, ma_device_descriptor* pDescriptor, ma_device_type deviceType) -{ - ma_result result; - int ossResult; - int fd; - const ma_device_id* pDeviceID = NULL; - ma_share_mode shareMode; - int ossFormat; - int ossChannels; - int ossSampleRate; - int ossFragment; - - MA_ASSERT(pDevice != NULL); - MA_ASSERT(pConfig != NULL); - MA_ASSERT(deviceType != ma_device_type_duplex); - - pDeviceID = pDescriptor->pDeviceID; - shareMode = pDescriptor->shareMode; - ossFormat = ma_format_to_oss((pDescriptor->format != ma_format_unknown) ? pDescriptor->format : ma_format_s16); /* Use s16 by default because OSS doesn't like floating point. */ - ossChannels = (int)(pDescriptor->channels > 0) ? pDescriptor->channels : MA_DEFAULT_CHANNELS; - ossSampleRate = (int)(pDescriptor->sampleRate > 0) ? pDescriptor->sampleRate : MA_DEFAULT_SAMPLE_RATE; - - result = ma_context_open_device__oss(pDevice->pContext, deviceType, pDeviceID, shareMode, &fd); - if (result != MA_SUCCESS) { - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[OSS] Failed to open device."); - return result; - } - - /* - The OSS documentation is very clear about the order we should be initializing the device's properties: - 1) Format - 2) Channels - 3) Sample rate. - */ - - /* Format. */ - ossResult = ioctl(fd, SNDCTL_DSP_SETFMT, &ossFormat); - if (ossResult == -1) { - close(fd); - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[OSS] Failed to set format."); - return ma_result_from_errno(errno); - } - - /* Channels. */ - ossResult = ioctl(fd, SNDCTL_DSP_CHANNELS, &ossChannels); - if (ossResult == -1) { - close(fd); - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[OSS] Failed to set channel count."); - return ma_result_from_errno(errno); - } - - /* Sample Rate. */ - ossResult = ioctl(fd, SNDCTL_DSP_SPEED, &ossSampleRate); - if (ossResult == -1) { - close(fd); - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[OSS] Failed to set sample rate."); - return ma_result_from_errno(errno); - } - - /* - Buffer. - - The documentation says that the fragment settings should be set as soon as possible, but I'm not sure if - it should be done before or after format/channels/rate. - - OSS wants the fragment size in bytes and a power of 2. When setting, we specify the power, not the actual - value. - */ - { - ma_uint32 periodSizeInFrames; - ma_uint32 periodSizeInBytes; - ma_uint32 ossFragmentSizePower; - - periodSizeInFrames = ma_calculate_buffer_size_in_frames_from_descriptor(pDescriptor, (ma_uint32)ossSampleRate, pConfig->performanceProfile); - - periodSizeInBytes = ma_round_to_power_of_2(periodSizeInFrames * ma_get_bytes_per_frame(ma_format_from_oss(ossFormat), ossChannels)); - if (periodSizeInBytes < 16) { - periodSizeInBytes = 16; - } - - ossFragmentSizePower = 4; - periodSizeInBytes >>= 4; - while (periodSizeInBytes >>= 1) { - ossFragmentSizePower += 1; - } - - ossFragment = (int)((pConfig->periods << 16) | ossFragmentSizePower); - ossResult = ioctl(fd, SNDCTL_DSP_SETFRAGMENT, &ossFragment); - if (ossResult == -1) { - close(fd); - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[OSS] Failed to set fragment size and period count."); - return ma_result_from_errno(errno); - } - } - - /* Internal settings. */ - if (deviceType == ma_device_type_capture) { - pDevice->oss.fdCapture = fd; - } else { - pDevice->oss.fdPlayback = fd; - } - - pDescriptor->format = ma_format_from_oss(ossFormat); - pDescriptor->channels = ossChannels; - pDescriptor->sampleRate = ossSampleRate; - ma_channel_map_init_standard(ma_standard_channel_map_sound4, pDescriptor->channelMap, ma_countof(pDescriptor->channelMap), pDescriptor->channels); - pDescriptor->periodCount = (ma_uint32)(ossFragment >> 16); - pDescriptor->periodSizeInFrames = (ma_uint32)(1 << (ossFragment & 0xFFFF)) / ma_get_bytes_per_frame(pDescriptor->format, pDescriptor->channels); - - if (pDescriptor->format == ma_format_unknown) { - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[OSS] The device's internal format is not supported by miniaudio."); - return MA_FORMAT_NOT_SUPPORTED; - } - - return MA_SUCCESS; -} - -static ma_result ma_device_init__oss(ma_device* pDevice, const ma_device_config* pConfig, ma_device_descriptor* pDescriptorPlayback, ma_device_descriptor* pDescriptorCapture) -{ - MA_ASSERT(pDevice != NULL); - MA_ASSERT(pConfig != NULL); - - MA_ZERO_OBJECT(&pDevice->oss); - - if (pConfig->deviceType == ma_device_type_loopback) { - return MA_DEVICE_TYPE_NOT_SUPPORTED; - } - - if (pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) { - ma_result result = ma_device_init_fd__oss(pDevice, pConfig, pDescriptorCapture, ma_device_type_capture); - if (result != MA_SUCCESS) { - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[OSS] Failed to open device."); - return result; - } - } - - if (pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) { - ma_result result = ma_device_init_fd__oss(pDevice, pConfig, pDescriptorPlayback, ma_device_type_playback); - if (result != MA_SUCCESS) { - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[OSS] Failed to open device."); - return result; - } - } - - return MA_SUCCESS; -} - -/* -Note on Starting and Stopping -============================= -In the past I was using SNDCTL_DSP_HALT to stop the device, however this results in issues when -trying to resume the device again. If we use SNDCTL_DSP_HALT, the next write() or read() will -fail. Instead what we need to do is just not write or read to and from the device when the -device is not running. - -As a result, both the start and stop functions for OSS are just empty stubs. The starting and -stopping logic is handled by ma_device_write__oss() and ma_device_read__oss(). These will check -the device state, and if the device is stopped they will simply not do any kind of processing. - -The downside to this technique is that I've noticed a fairly lengthy delay in stopping the -device, up to a second. This is on a virtual machine, and as such might just be due to the -virtual drivers, but I'm not fully sure. I am not sure how to work around this problem so for -the moment that's just how it's going to have to be. - -When starting the device, OSS will automatically start it when write() or read() is called. -*/ -static ma_result ma_device_start__oss(ma_device* pDevice) -{ - MA_ASSERT(pDevice != NULL); - - /* The device is automatically started with reading and writing. */ - (void)pDevice; - - return MA_SUCCESS; -} - -static ma_result ma_device_stop__oss(ma_device* pDevice) -{ - MA_ASSERT(pDevice != NULL); - - /* See note above on why this is empty. */ - (void)pDevice; - - return MA_SUCCESS; -} - -static ma_result ma_device_write__oss(ma_device* pDevice, const void* pPCMFrames, ma_uint32 frameCount, ma_uint32* pFramesWritten) -{ - int resultOSS; - ma_uint32 deviceState; - - if (pFramesWritten != NULL) { - *pFramesWritten = 0; - } - - /* Don't do any processing if the device is stopped. */ - deviceState = ma_device_get_state(pDevice); - if (deviceState != ma_device_state_started && deviceState != ma_device_state_starting) { - return MA_SUCCESS; - } - - resultOSS = write(pDevice->oss.fdPlayback, pPCMFrames, frameCount * ma_get_bytes_per_frame(pDevice->playback.internalFormat, pDevice->playback.internalChannels)); - if (resultOSS < 0) { - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[OSS] Failed to send data from the client to the device."); - return ma_result_from_errno(errno); - } - - if (pFramesWritten != NULL) { - *pFramesWritten = (ma_uint32)resultOSS / ma_get_bytes_per_frame(pDevice->playback.internalFormat, pDevice->playback.internalChannels); - } - - return MA_SUCCESS; -} - -static ma_result ma_device_read__oss(ma_device* pDevice, void* pPCMFrames, ma_uint32 frameCount, ma_uint32* pFramesRead) -{ - int resultOSS; - ma_uint32 deviceState; - - if (pFramesRead != NULL) { - *pFramesRead = 0; - } - - /* Don't do any processing if the device is stopped. */ - deviceState = ma_device_get_state(pDevice); - if (deviceState != ma_device_state_started && deviceState != ma_device_state_starting) { - return MA_SUCCESS; - } - - resultOSS = read(pDevice->oss.fdCapture, pPCMFrames, frameCount * ma_get_bytes_per_frame(pDevice->capture.internalFormat, pDevice->capture.internalChannels)); - if (resultOSS < 0) { - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[OSS] Failed to read data from the device to be sent to the client."); - return ma_result_from_errno(errno); - } - - if (pFramesRead != NULL) { - *pFramesRead = (ma_uint32)resultOSS / ma_get_bytes_per_frame(pDevice->capture.internalFormat, pDevice->capture.internalChannels); - } - - return MA_SUCCESS; -} - -static ma_result ma_context_uninit__oss(ma_context* pContext) -{ - MA_ASSERT(pContext != NULL); - MA_ASSERT(pContext->backend == ma_backend_oss); - - (void)pContext; - return MA_SUCCESS; -} - -static ma_result ma_context_init__oss(ma_context* pContext, const ma_context_config* pConfig, ma_backend_callbacks* pCallbacks) -{ - int fd; - int ossVersion; - int result; - - MA_ASSERT(pContext != NULL); - - (void)pConfig; - - /* Try opening a temporary device first so we can get version information. This is closed at the end. */ - fd = ma_open_temp_device__oss(); - if (fd == -1) { - ma_log_post(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[OSS] Failed to open temporary device for retrieving system properties."); /* Looks liks OSS isn't installed, or there are no available devices. */ - return MA_NO_BACKEND; - } - - /* Grab the OSS version. */ - ossVersion = 0; - result = ioctl(fd, OSS_GETVERSION, &ossVersion); - if (result == -1) { - close(fd); - ma_log_post(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[OSS] Failed to retrieve OSS version."); - return MA_NO_BACKEND; - } - - /* The file handle to temp device is no longer needed. Close ASAP. */ - close(fd); - - pContext->oss.versionMajor = ((ossVersion & 0xFF0000) >> 16); - pContext->oss.versionMinor = ((ossVersion & 0x00FF00) >> 8); - - pCallbacks->onContextInit = ma_context_init__oss; - pCallbacks->onContextUninit = ma_context_uninit__oss; - pCallbacks->onContextEnumerateDevices = ma_context_enumerate_devices__oss; - pCallbacks->onContextGetDeviceInfo = ma_context_get_device_info__oss; - pCallbacks->onDeviceInit = ma_device_init__oss; - pCallbacks->onDeviceUninit = ma_device_uninit__oss; - pCallbacks->onDeviceStart = ma_device_start__oss; - pCallbacks->onDeviceStop = ma_device_stop__oss; - pCallbacks->onDeviceRead = ma_device_read__oss; - pCallbacks->onDeviceWrite = ma_device_write__oss; - pCallbacks->onDeviceDataLoop = NULL; - - return MA_SUCCESS; -} -#endif /* MA_HAS_OSS */ - - - - - -/****************************************************************************** - -AAudio Backend - -******************************************************************************/ -#ifdef MA_HAS_AAUDIO - -#ifdef MA_NO_RUNTIME_LINKING - #include -#endif - -typedef int32_t ma_aaudio_result_t; -typedef int32_t ma_aaudio_direction_t; -typedef int32_t ma_aaudio_sharing_mode_t; -typedef int32_t ma_aaudio_format_t; -typedef int32_t ma_aaudio_stream_state_t; -typedef int32_t ma_aaudio_performance_mode_t; -typedef int32_t ma_aaudio_usage_t; -typedef int32_t ma_aaudio_content_type_t; -typedef int32_t ma_aaudio_input_preset_t; -typedef int32_t ma_aaudio_allowed_capture_policy_t; -typedef int32_t ma_aaudio_data_callback_result_t; -typedef struct ma_AAudioStreamBuilder_t* ma_AAudioStreamBuilder; -typedef struct ma_AAudioStream_t* ma_AAudioStream; - -#define MA_AAUDIO_UNSPECIFIED 0 - -/* Result codes. miniaudio only cares about the success code. */ -#define MA_AAUDIO_OK 0 - -/* Directions. */ -#define MA_AAUDIO_DIRECTION_OUTPUT 0 -#define MA_AAUDIO_DIRECTION_INPUT 1 - -/* Sharing modes. */ -#define MA_AAUDIO_SHARING_MODE_EXCLUSIVE 0 -#define MA_AAUDIO_SHARING_MODE_SHARED 1 - -/* Formats. */ -#define MA_AAUDIO_FORMAT_PCM_I16 1 -#define MA_AAUDIO_FORMAT_PCM_FLOAT 2 - -/* Stream states. */ -#define MA_AAUDIO_STREAM_STATE_UNINITIALIZED 0 -#define MA_AAUDIO_STREAM_STATE_UNKNOWN 1 -#define MA_AAUDIO_STREAM_STATE_OPEN 2 -#define MA_AAUDIO_STREAM_STATE_STARTING 3 -#define MA_AAUDIO_STREAM_STATE_STARTED 4 -#define MA_AAUDIO_STREAM_STATE_PAUSING 5 -#define MA_AAUDIO_STREAM_STATE_PAUSED 6 -#define MA_AAUDIO_STREAM_STATE_FLUSHING 7 -#define MA_AAUDIO_STREAM_STATE_FLUSHED 8 -#define MA_AAUDIO_STREAM_STATE_STOPPING 9 -#define MA_AAUDIO_STREAM_STATE_STOPPED 10 -#define MA_AAUDIO_STREAM_STATE_CLOSING 11 -#define MA_AAUDIO_STREAM_STATE_CLOSED 12 -#define MA_AAUDIO_STREAM_STATE_DISCONNECTED 13 - -/* Performance modes. */ -#define MA_AAUDIO_PERFORMANCE_MODE_NONE 10 -#define MA_AAUDIO_PERFORMANCE_MODE_POWER_SAVING 11 -#define MA_AAUDIO_PERFORMANCE_MODE_LOW_LATENCY 12 - -/* Usage types. */ -#define MA_AAUDIO_USAGE_MEDIA 1 -#define MA_AAUDIO_USAGE_VOICE_COMMUNICATION 2 -#define MA_AAUDIO_USAGE_VOICE_COMMUNICATION_SIGNALLING 3 -#define MA_AAUDIO_USAGE_ALARM 4 -#define MA_AAUDIO_USAGE_NOTIFICATION 5 -#define MA_AAUDIO_USAGE_NOTIFICATION_RINGTONE 6 -#define MA_AAUDIO_USAGE_NOTIFICATION_EVENT 10 -#define MA_AAUDIO_USAGE_ASSISTANCE_ACCESSIBILITY 11 -#define MA_AAUDIO_USAGE_ASSISTANCE_NAVIGATION_GUIDANCE 12 -#define MA_AAUDIO_USAGE_ASSISTANCE_SONIFICATION 13 -#define MA_AAUDIO_USAGE_GAME 14 -#define MA_AAUDIO_USAGE_ASSISTANT 16 -#define MA_AAUDIO_SYSTEM_USAGE_EMERGENCY 1000 -#define MA_AAUDIO_SYSTEM_USAGE_SAFETY 1001 -#define MA_AAUDIO_SYSTEM_USAGE_VEHICLE_STATUS 1002 -#define MA_AAUDIO_SYSTEM_USAGE_ANNOUNCEMENT 1003 - -/* Content types. */ -#define MA_AAUDIO_CONTENT_TYPE_SPEECH 1 -#define MA_AAUDIO_CONTENT_TYPE_MUSIC 2 -#define MA_AAUDIO_CONTENT_TYPE_MOVIE 3 -#define MA_AAUDIO_CONTENT_TYPE_SONIFICATION 4 - -/* Input presets. */ -#define MA_AAUDIO_INPUT_PRESET_GENERIC 1 -#define MA_AAUDIO_INPUT_PRESET_CAMCORDER 5 -#define MA_AAUDIO_INPUT_PRESET_VOICE_RECOGNITION 6 -#define MA_AAUDIO_INPUT_PRESET_VOICE_COMMUNICATION 7 -#define MA_AAUDIO_INPUT_PRESET_UNPROCESSED 9 -#define MA_AAUDIO_INPUT_PRESET_VOICE_PERFORMANCE 10 - -/* Allowed Capture Policies */ -#define MA_AAUDIO_ALLOW_CAPTURE_BY_ALL 1 -#define MA_AAUDIO_ALLOW_CAPTURE_BY_SYSTEM 2 -#define MA_AAUDIO_ALLOW_CAPTURE_BY_NONE 3 - -/* Callback results. */ -#define MA_AAUDIO_CALLBACK_RESULT_CONTINUE 0 -#define MA_AAUDIO_CALLBACK_RESULT_STOP 1 - - -typedef ma_aaudio_data_callback_result_t (* ma_AAudioStream_dataCallback) (ma_AAudioStream* pStream, void* pUserData, void* pAudioData, int32_t numFrames); -typedef void (* ma_AAudioStream_errorCallback)(ma_AAudioStream *pStream, void *pUserData, ma_aaudio_result_t error); - -typedef ma_aaudio_result_t (* MA_PFN_AAudio_createStreamBuilder) (ma_AAudioStreamBuilder** ppBuilder); -typedef ma_aaudio_result_t (* MA_PFN_AAudioStreamBuilder_delete) (ma_AAudioStreamBuilder* pBuilder); -typedef void (* MA_PFN_AAudioStreamBuilder_setDeviceId) (ma_AAudioStreamBuilder* pBuilder, int32_t deviceId); -typedef void (* MA_PFN_AAudioStreamBuilder_setDirection) (ma_AAudioStreamBuilder* pBuilder, ma_aaudio_direction_t direction); -typedef void (* MA_PFN_AAudioStreamBuilder_setSharingMode) (ma_AAudioStreamBuilder* pBuilder, ma_aaudio_sharing_mode_t sharingMode); -typedef void (* MA_PFN_AAudioStreamBuilder_setFormat) (ma_AAudioStreamBuilder* pBuilder, ma_aaudio_format_t format); -typedef void (* MA_PFN_AAudioStreamBuilder_setChannelCount) (ma_AAudioStreamBuilder* pBuilder, int32_t channelCount); -typedef void (* MA_PFN_AAudioStreamBuilder_setSampleRate) (ma_AAudioStreamBuilder* pBuilder, int32_t sampleRate); -typedef void (* MA_PFN_AAudioStreamBuilder_setBufferCapacityInFrames)(ma_AAudioStreamBuilder* pBuilder, int32_t numFrames); -typedef void (* MA_PFN_AAudioStreamBuilder_setFramesPerDataCallback) (ma_AAudioStreamBuilder* pBuilder, int32_t numFrames); -typedef void (* MA_PFN_AAudioStreamBuilder_setDataCallback) (ma_AAudioStreamBuilder* pBuilder, ma_AAudioStream_dataCallback callback, void* pUserData); -typedef void (* MA_PFN_AAudioStreamBuilder_setErrorCallback) (ma_AAudioStreamBuilder* pBuilder, ma_AAudioStream_errorCallback callback, void* pUserData); -typedef void (* MA_PFN_AAudioStreamBuilder_setPerformanceMode) (ma_AAudioStreamBuilder* pBuilder, ma_aaudio_performance_mode_t mode); -typedef void (* MA_PFN_AAudioStreamBuilder_setUsage) (ma_AAudioStreamBuilder* pBuilder, ma_aaudio_usage_t contentType); -typedef void (* MA_PFN_AAudioStreamBuilder_setContentType) (ma_AAudioStreamBuilder* pBuilder, ma_aaudio_content_type_t contentType); -typedef void (* MA_PFN_AAudioStreamBuilder_setInputPreset) (ma_AAudioStreamBuilder* pBuilder, ma_aaudio_input_preset_t inputPreset); -typedef void (* MA_PFN_AAudioStreamBuilder_setAllowedCapturePolicy) (ma_AAudioStreamBuilder* pBuilder, ma_aaudio_allowed_capture_policy_t policy); -typedef ma_aaudio_result_t (* MA_PFN_AAudioStreamBuilder_openStream) (ma_AAudioStreamBuilder* pBuilder, ma_AAudioStream** ppStream); -typedef ma_aaudio_result_t (* MA_PFN_AAudioStream_close) (ma_AAudioStream* pStream); -typedef ma_aaudio_stream_state_t (* MA_PFN_AAudioStream_getState) (ma_AAudioStream* pStream); -typedef ma_aaudio_result_t (* MA_PFN_AAudioStream_waitForStateChange) (ma_AAudioStream* pStream, ma_aaudio_stream_state_t inputState, ma_aaudio_stream_state_t* pNextState, int64_t timeoutInNanoseconds); -typedef ma_aaudio_format_t (* MA_PFN_AAudioStream_getFormat) (ma_AAudioStream* pStream); -typedef int32_t (* MA_PFN_AAudioStream_getChannelCount) (ma_AAudioStream* pStream); -typedef int32_t (* MA_PFN_AAudioStream_getSampleRate) (ma_AAudioStream* pStream); -typedef int32_t (* MA_PFN_AAudioStream_getBufferCapacityInFrames) (ma_AAudioStream* pStream); -typedef int32_t (* MA_PFN_AAudioStream_getFramesPerDataCallback) (ma_AAudioStream* pStream); -typedef int32_t (* MA_PFN_AAudioStream_getFramesPerBurst) (ma_AAudioStream* pStream); -typedef ma_aaudio_result_t (* MA_PFN_AAudioStream_requestStart) (ma_AAudioStream* pStream); -typedef ma_aaudio_result_t (* MA_PFN_AAudioStream_requestStop) (ma_AAudioStream* pStream); - -static ma_result ma_result_from_aaudio(ma_aaudio_result_t resultAA) -{ - switch (resultAA) - { - case MA_AAUDIO_OK: return MA_SUCCESS; - default: break; - } - - return MA_ERROR; -} - -static ma_aaudio_usage_t ma_to_usage__aaudio(ma_aaudio_usage usage) -{ - switch (usage) { - case ma_aaudio_usage_media: return MA_AAUDIO_USAGE_MEDIA; - case ma_aaudio_usage_voice_communication: return MA_AAUDIO_USAGE_VOICE_COMMUNICATION; - case ma_aaudio_usage_voice_communication_signalling: return MA_AAUDIO_USAGE_VOICE_COMMUNICATION_SIGNALLING; - case ma_aaudio_usage_alarm: return MA_AAUDIO_USAGE_ALARM; - case ma_aaudio_usage_notification: return MA_AAUDIO_USAGE_NOTIFICATION; - case ma_aaudio_usage_notification_ringtone: return MA_AAUDIO_USAGE_NOTIFICATION_RINGTONE; - case ma_aaudio_usage_notification_event: return MA_AAUDIO_USAGE_NOTIFICATION_EVENT; - case ma_aaudio_usage_assistance_accessibility: return MA_AAUDIO_USAGE_ASSISTANCE_ACCESSIBILITY; - case ma_aaudio_usage_assistance_navigation_guidance: return MA_AAUDIO_USAGE_ASSISTANCE_NAVIGATION_GUIDANCE; - case ma_aaudio_usage_assistance_sonification: return MA_AAUDIO_USAGE_ASSISTANCE_SONIFICATION; - case ma_aaudio_usage_game: return MA_AAUDIO_USAGE_GAME; - case ma_aaudio_usage_assitant: return MA_AAUDIO_USAGE_ASSISTANT; - case ma_aaudio_usage_emergency: return MA_AAUDIO_SYSTEM_USAGE_EMERGENCY; - case ma_aaudio_usage_safety: return MA_AAUDIO_SYSTEM_USAGE_SAFETY; - case ma_aaudio_usage_vehicle_status: return MA_AAUDIO_SYSTEM_USAGE_VEHICLE_STATUS; - case ma_aaudio_usage_announcement: return MA_AAUDIO_SYSTEM_USAGE_ANNOUNCEMENT; - default: break; - } - - return MA_AAUDIO_USAGE_MEDIA; -} - -static ma_aaudio_content_type_t ma_to_content_type__aaudio(ma_aaudio_content_type contentType) -{ - switch (contentType) { - case ma_aaudio_content_type_speech: return MA_AAUDIO_CONTENT_TYPE_SPEECH; - case ma_aaudio_content_type_music: return MA_AAUDIO_CONTENT_TYPE_MUSIC; - case ma_aaudio_content_type_movie: return MA_AAUDIO_CONTENT_TYPE_MOVIE; - case ma_aaudio_content_type_sonification: return MA_AAUDIO_CONTENT_TYPE_SONIFICATION; - default: break; - } - - return MA_AAUDIO_CONTENT_TYPE_SPEECH; -} - -static ma_aaudio_input_preset_t ma_to_input_preset__aaudio(ma_aaudio_input_preset inputPreset) -{ - switch (inputPreset) { - case ma_aaudio_input_preset_generic: return MA_AAUDIO_INPUT_PRESET_GENERIC; - case ma_aaudio_input_preset_camcorder: return MA_AAUDIO_INPUT_PRESET_CAMCORDER; - case ma_aaudio_input_preset_voice_recognition: return MA_AAUDIO_INPUT_PRESET_VOICE_RECOGNITION; - case ma_aaudio_input_preset_voice_communication: return MA_AAUDIO_INPUT_PRESET_VOICE_COMMUNICATION; - case ma_aaudio_input_preset_unprocessed: return MA_AAUDIO_INPUT_PRESET_UNPROCESSED; - case ma_aaudio_input_preset_voice_performance: return MA_AAUDIO_INPUT_PRESET_VOICE_PERFORMANCE; - default: break; - } - - return MA_AAUDIO_INPUT_PRESET_GENERIC; -} - -static ma_aaudio_allowed_capture_policy_t ma_to_allowed_capture_policy__aaudio(ma_aaudio_allowed_capture_policy allowedCapturePolicy) -{ - switch (allowedCapturePolicy) { - case ma_aaudio_allow_capture_by_all: return MA_AAUDIO_ALLOW_CAPTURE_BY_ALL; - case ma_aaudio_allow_capture_by_system: return MA_AAUDIO_ALLOW_CAPTURE_BY_SYSTEM; - case ma_aaudio_allow_capture_by_none: return MA_AAUDIO_ALLOW_CAPTURE_BY_NONE; - default: break; - } - - return MA_AAUDIO_ALLOW_CAPTURE_BY_ALL; -} - -static void ma_stream_error_callback__aaudio(ma_AAudioStream* pStream, void* pUserData, ma_aaudio_result_t error) -{ - ma_result result; - ma_job job; - ma_device* pDevice = (ma_device*)pUserData; - MA_ASSERT(pDevice != NULL); - - (void)error; - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, "[AAudio] ERROR CALLBACK: error=%d, AAudioStream_getState()=%d\n", error, ((MA_PFN_AAudioStream_getState)pDevice->pContext->aaudio.AAudioStream_getState)(pStream)); - /* - When we get an error, we'll assume that the stream is in an erroneous state and needs to be restarted. From the documentation, - we cannot do this from the error callback. Therefore we are going to use an event thread for the AAudio backend to do this - cleanly and safely. - */ - job = ma_job_init(MA_JOB_TYPE_DEVICE_AAUDIO_REROUTE); - job.data.device.aaudio.reroute.pDevice = pDevice; - - if (pStream == pDevice->aaudio.pStreamCapture) { - job.data.device.aaudio.reroute.deviceType = ma_device_type_capture; - } - else { - job.data.device.aaudio.reroute.deviceType = ma_device_type_playback; - } - - result = ma_device_job_thread_post(&pDevice->pContext->aaudio.jobThread, &job); - if (result != MA_SUCCESS) { - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, "[AAudio] Device Disconnected. Failed to post job for rerouting.\n"); - return; - } -} - -static ma_aaudio_data_callback_result_t ma_stream_data_callback_capture__aaudio(ma_AAudioStream* pStream, void* pUserData, void* pAudioData, int32_t frameCount) -{ - ma_device* pDevice = (ma_device*)pUserData; - MA_ASSERT(pDevice != NULL); - - if (frameCount > 0) { - ma_device_handle_backend_data_callback(pDevice, NULL, pAudioData, (ma_uint32)frameCount); - } - - (void)pStream; - return MA_AAUDIO_CALLBACK_RESULT_CONTINUE; -} - -static ma_aaudio_data_callback_result_t ma_stream_data_callback_playback__aaudio(ma_AAudioStream* pStream, void* pUserData, void* pAudioData, int32_t frameCount) -{ - ma_device* pDevice = (ma_device*)pUserData; - MA_ASSERT(pDevice != NULL); - - /* - I've had a report that AAudio can sometimes post a frame count of 0. We need to check for that here - so we don't get any errors at a deeper level. I'm doing the same with the capture side for safety, - though I've not yet had any reports about that one. - */ - if (frameCount > 0) { - ma_device_handle_backend_data_callback(pDevice, pAudioData, NULL, (ma_uint32)frameCount); - } - - (void)pStream; - return MA_AAUDIO_CALLBACK_RESULT_CONTINUE; -} - -static ma_result ma_create_and_configure_AAudioStreamBuilder__aaudio(ma_context* pContext, const ma_device_id* pDeviceID, ma_device_type deviceType, ma_share_mode shareMode, const ma_device_descriptor* pDescriptor, const ma_device_config* pConfig, ma_device* pDevice, ma_AAudioStreamBuilder** ppBuilder) -{ - ma_AAudioStreamBuilder* pBuilder; - ma_aaudio_result_t resultAA; - - /* Safety. */ - *ppBuilder = NULL; - - resultAA = ((MA_PFN_AAudio_createStreamBuilder)pContext->aaudio.AAudio_createStreamBuilder)(&pBuilder); - if (resultAA != MA_AAUDIO_OK) { - return ma_result_from_aaudio(resultAA); - } - - if (pDeviceID != NULL) { - ((MA_PFN_AAudioStreamBuilder_setDeviceId)pContext->aaudio.AAudioStreamBuilder_setDeviceId)(pBuilder, pDeviceID->aaudio); - } - - ((MA_PFN_AAudioStreamBuilder_setDirection)pContext->aaudio.AAudioStreamBuilder_setDirection)(pBuilder, (deviceType == ma_device_type_playback) ? MA_AAUDIO_DIRECTION_OUTPUT : MA_AAUDIO_DIRECTION_INPUT); - ((MA_PFN_AAudioStreamBuilder_setSharingMode)pContext->aaudio.AAudioStreamBuilder_setSharingMode)(pBuilder, (shareMode == ma_share_mode_shared) ? MA_AAUDIO_SHARING_MODE_SHARED : MA_AAUDIO_SHARING_MODE_EXCLUSIVE); - - - /* If we have a device descriptor make sure we configure the stream builder to take our requested parameters. */ - if (pDescriptor != NULL) { - MA_ASSERT(pConfig != NULL); /* We must have a device config if we also have a descriptor. The config is required for AAudio specific configuration options. */ - - if (pDescriptor->sampleRate != 0) { - ((MA_PFN_AAudioStreamBuilder_setSampleRate)pContext->aaudio.AAudioStreamBuilder_setSampleRate)(pBuilder, pDescriptor->sampleRate); - } - - if (pDescriptor->channels != 0) { - ((MA_PFN_AAudioStreamBuilder_setChannelCount)pContext->aaudio.AAudioStreamBuilder_setChannelCount)(pBuilder, pDescriptor->channels); - } - - if (pDescriptor->format != ma_format_unknown) { - ((MA_PFN_AAudioStreamBuilder_setFormat)pContext->aaudio.AAudioStreamBuilder_setFormat)(pBuilder, (pDescriptor->format == ma_format_s16) ? MA_AAUDIO_FORMAT_PCM_I16 : MA_AAUDIO_FORMAT_PCM_FLOAT); - } - - - /* - There have been reports where setting the frames per data callback results in an error. - In particular, re-routing may inadvertently switch from low-latency mode, resulting in a less stable - stream from the legacy path (AudioStreamLegacy). To address this, we simply don't set the value. It - can still be set if it's explicitly requested via the aaudio.allowSetBufferCapacity variable in the - device config. - */ - if ((!pConfig->aaudio.enableCompatibilityWorkarounds || ma_android_sdk_version() > 30) && pConfig->aaudio.allowSetBufferCapacity) { - /* - AAudio is annoying when it comes to its buffer calculation stuff because it doesn't let you - retrieve the actual sample rate until after you've opened the stream. But you need to configure - the buffer capacity before you open the stream... :/ - - To solve, we're just going to assume MA_DEFAULT_SAMPLE_RATE (48000) and move on. - */ - ma_uint32 bufferCapacityInFrames = ma_calculate_buffer_size_in_frames_from_descriptor(pDescriptor, pDescriptor->sampleRate, pConfig->performanceProfile) * pDescriptor->periodCount; - - ((MA_PFN_AAudioStreamBuilder_setBufferCapacityInFrames)pContext->aaudio.AAudioStreamBuilder_setBufferCapacityInFrames)(pBuilder, bufferCapacityInFrames); - ((MA_PFN_AAudioStreamBuilder_setFramesPerDataCallback)pContext->aaudio.AAudioStreamBuilder_setFramesPerDataCallback)(pBuilder, bufferCapacityInFrames / pDescriptor->periodCount); - } - - if (deviceType == ma_device_type_capture) { - if (pConfig->aaudio.inputPreset != ma_aaudio_input_preset_default && pContext->aaudio.AAudioStreamBuilder_setInputPreset != NULL) { - ((MA_PFN_AAudioStreamBuilder_setInputPreset)pContext->aaudio.AAudioStreamBuilder_setInputPreset)(pBuilder, ma_to_input_preset__aaudio(pConfig->aaudio.inputPreset)); - } - - ((MA_PFN_AAudioStreamBuilder_setDataCallback)pContext->aaudio.AAudioStreamBuilder_setDataCallback)(pBuilder, ma_stream_data_callback_capture__aaudio, (void*)pDevice); - } else { - if (pConfig->aaudio.usage != ma_aaudio_usage_default && pContext->aaudio.AAudioStreamBuilder_setUsage != NULL) { - ((MA_PFN_AAudioStreamBuilder_setUsage)pContext->aaudio.AAudioStreamBuilder_setUsage)(pBuilder, ma_to_usage__aaudio(pConfig->aaudio.usage)); - } - - if (pConfig->aaudio.contentType != ma_aaudio_content_type_default && pContext->aaudio.AAudioStreamBuilder_setContentType != NULL) { - ((MA_PFN_AAudioStreamBuilder_setContentType)pContext->aaudio.AAudioStreamBuilder_setContentType)(pBuilder, ma_to_content_type__aaudio(pConfig->aaudio.contentType)); - } - - if (pConfig->aaudio.allowedCapturePolicy != ma_aaudio_allow_capture_default && pContext->aaudio.AAudioStreamBuilder_setAllowedCapturePolicy != NULL) { - ((MA_PFN_AAudioStreamBuilder_setAllowedCapturePolicy)pContext->aaudio.AAudioStreamBuilder_setAllowedCapturePolicy)(pBuilder, ma_to_allowed_capture_policy__aaudio(pConfig->aaudio.allowedCapturePolicy)); - } - - ((MA_PFN_AAudioStreamBuilder_setDataCallback)pContext->aaudio.AAudioStreamBuilder_setDataCallback)(pBuilder, ma_stream_data_callback_playback__aaudio, (void*)pDevice); - } - - /* - If we set AAUDIO_PERFORMANCE_MODE_LOW_LATENCY, we allow for MMAP (non-legacy path). - Since there's a mapping between miniaudio's performance profiles and AAudio's performance modes, let's use it. - Beware though, with a conservative performance profile, AAudio will indeed take the legacy path. - */ - ((MA_PFN_AAudioStreamBuilder_setPerformanceMode)pContext->aaudio.AAudioStreamBuilder_setPerformanceMode)(pBuilder, (pConfig->performanceProfile == ma_performance_profile_low_latency) ? MA_AAUDIO_PERFORMANCE_MODE_LOW_LATENCY : MA_AAUDIO_PERFORMANCE_MODE_NONE); - - /* We need to set an error callback to detect device changes. */ - if (pDevice != NULL) { /* <-- pDevice should never be null if pDescriptor is not null, which is always the case if we hit this branch. Check anyway for safety. */ - ((MA_PFN_AAudioStreamBuilder_setErrorCallback)pContext->aaudio.AAudioStreamBuilder_setErrorCallback)(pBuilder, ma_stream_error_callback__aaudio, (void*)pDevice); - } - } - - *ppBuilder = pBuilder; - - return MA_SUCCESS; -} - -static ma_result ma_open_stream_and_close_builder__aaudio(ma_context* pContext, ma_AAudioStreamBuilder* pBuilder, ma_AAudioStream** ppStream) -{ - ma_result result; - - result = ma_result_from_aaudio(((MA_PFN_AAudioStreamBuilder_openStream)pContext->aaudio.AAudioStreamBuilder_openStream)(pBuilder, ppStream)); - ((MA_PFN_AAudioStreamBuilder_delete)pContext->aaudio.AAudioStreamBuilder_delete)(pBuilder); - - return result; -} - -static ma_result ma_open_stream_basic__aaudio(ma_context* pContext, const ma_device_id* pDeviceID, ma_device_type deviceType, ma_share_mode shareMode, ma_AAudioStream** ppStream) -{ - ma_result result; - ma_AAudioStreamBuilder* pBuilder; - - *ppStream = NULL; - - result = ma_create_and_configure_AAudioStreamBuilder__aaudio(pContext, pDeviceID, deviceType, shareMode, NULL, NULL, NULL, &pBuilder); - if (result != MA_SUCCESS) { - return result; - } - - /* Let's give AAudio a hint to avoid the legacy path (AudioStreamLegacy). */ - ((MA_PFN_AAudioStreamBuilder_setPerformanceMode)pContext->aaudio.AAudioStreamBuilder_setPerformanceMode)(pBuilder, MA_AAUDIO_PERFORMANCE_MODE_LOW_LATENCY); - - return ma_open_stream_and_close_builder__aaudio(pContext, pBuilder, ppStream); -} - -static ma_result ma_open_stream__aaudio(ma_device* pDevice, const ma_device_config* pConfig, ma_device_type deviceType, const ma_device_descriptor* pDescriptor, ma_AAudioStream** ppStream) -{ - ma_result result; - ma_AAudioStreamBuilder* pBuilder; - - MA_ASSERT(pDevice != NULL); - MA_ASSERT(pDescriptor != NULL); - MA_ASSERT(deviceType != ma_device_type_duplex); /* This function should not be called for a full-duplex device type. */ - - *ppStream = NULL; - - result = ma_create_and_configure_AAudioStreamBuilder__aaudio(pDevice->pContext, pDescriptor->pDeviceID, deviceType, pDescriptor->shareMode, pDescriptor, pConfig, pDevice, &pBuilder); - if (result != MA_SUCCESS) { - return result; - } - - return ma_open_stream_and_close_builder__aaudio(pDevice->pContext, pBuilder, ppStream); -} - -static ma_result ma_close_stream__aaudio(ma_context* pContext, ma_AAudioStream* pStream) -{ - if (pStream == NULL) { - return MA_INVALID_ARGS; - } - - return ma_result_from_aaudio(((MA_PFN_AAudioStream_close)pContext->aaudio.AAudioStream_close)(pStream)); -} - -static ma_bool32 ma_has_default_device__aaudio(ma_context* pContext, ma_device_type deviceType) -{ - /* The only way to know this is to try creating a stream. */ - ma_AAudioStream* pStream; - ma_result result = ma_open_stream_basic__aaudio(pContext, NULL, deviceType, ma_share_mode_shared, &pStream); - if (result != MA_SUCCESS) { - return MA_FALSE; - } - - ma_close_stream__aaudio(pContext, pStream); - return MA_TRUE; -} - -static ma_result ma_wait_for_simple_state_transition__aaudio(ma_context* pContext, ma_AAudioStream* pStream, ma_aaudio_stream_state_t oldState, ma_aaudio_stream_state_t newState) -{ - ma_aaudio_stream_state_t actualNewState; - ma_aaudio_result_t resultAA = ((MA_PFN_AAudioStream_waitForStateChange)pContext->aaudio.AAudioStream_waitForStateChange)(pStream, oldState, &actualNewState, 5000000000); /* 5 second timeout. */ - if (resultAA != MA_AAUDIO_OK) { - return ma_result_from_aaudio(resultAA); - } - - if (newState != actualNewState) { - return MA_ERROR; /* Failed to transition into the expected state. */ - } - - return MA_SUCCESS; -} - - -static ma_result ma_context_enumerate_devices__aaudio(ma_context* pContext, ma_enum_devices_callback_proc callback, void* pUserData) -{ - ma_bool32 cbResult = MA_TRUE; - - MA_ASSERT(pContext != NULL); - MA_ASSERT(callback != NULL); - - /* Unfortunately AAudio does not have an enumeration API. Therefore I'm only going to report default devices, but only if it can instantiate a stream. */ - - /* Playback. */ - if (cbResult) { - ma_device_info deviceInfo; - MA_ZERO_OBJECT(&deviceInfo); - deviceInfo.id.aaudio = MA_AAUDIO_UNSPECIFIED; - ma_strncpy_s(deviceInfo.name, sizeof(deviceInfo.name), MA_DEFAULT_PLAYBACK_DEVICE_NAME, (size_t)-1); - - if (ma_has_default_device__aaudio(pContext, ma_device_type_playback)) { - cbResult = callback(pContext, ma_device_type_playback, &deviceInfo, pUserData); - } - } - - /* Capture. */ - if (cbResult) { - ma_device_info deviceInfo; - MA_ZERO_OBJECT(&deviceInfo); - deviceInfo.id.aaudio = MA_AAUDIO_UNSPECIFIED; - ma_strncpy_s(deviceInfo.name, sizeof(deviceInfo.name), MA_DEFAULT_CAPTURE_DEVICE_NAME, (size_t)-1); - - if (ma_has_default_device__aaudio(pContext, ma_device_type_capture)) { - cbResult = callback(pContext, ma_device_type_capture, &deviceInfo, pUserData); - } - } - - return MA_SUCCESS; -} - -static void ma_context_add_native_data_format_from_AAudioStream_ex__aaudio(ma_context* pContext, ma_AAudioStream* pStream, ma_format format, ma_uint32 flags, ma_device_info* pDeviceInfo) -{ - MA_ASSERT(pContext != NULL); - MA_ASSERT(pStream != NULL); - MA_ASSERT(pDeviceInfo != NULL); - - pDeviceInfo->nativeDataFormats[pDeviceInfo->nativeDataFormatCount].format = format; - pDeviceInfo->nativeDataFormats[pDeviceInfo->nativeDataFormatCount].channels = ((MA_PFN_AAudioStream_getChannelCount)pContext->aaudio.AAudioStream_getChannelCount)(pStream); - pDeviceInfo->nativeDataFormats[pDeviceInfo->nativeDataFormatCount].sampleRate = ((MA_PFN_AAudioStream_getSampleRate)pContext->aaudio.AAudioStream_getSampleRate)(pStream); - pDeviceInfo->nativeDataFormats[pDeviceInfo->nativeDataFormatCount].flags = flags; - pDeviceInfo->nativeDataFormatCount += 1; -} - -static void ma_context_add_native_data_format_from_AAudioStream__aaudio(ma_context* pContext, ma_AAudioStream* pStream, ma_uint32 flags, ma_device_info* pDeviceInfo) -{ - /* AAudio supports s16 and f32. */ - ma_context_add_native_data_format_from_AAudioStream_ex__aaudio(pContext, pStream, ma_format_f32, flags, pDeviceInfo); - ma_context_add_native_data_format_from_AAudioStream_ex__aaudio(pContext, pStream, ma_format_s16, flags, pDeviceInfo); -} - -static ma_result ma_context_get_device_info__aaudio(ma_context* pContext, ma_device_type deviceType, const ma_device_id* pDeviceID, ma_device_info* pDeviceInfo) -{ - ma_AAudioStream* pStream; - ma_result result; - - MA_ASSERT(pContext != NULL); - - /* ID */ - if (pDeviceID != NULL) { - pDeviceInfo->id.aaudio = pDeviceID->aaudio; - } else { - pDeviceInfo->id.aaudio = MA_AAUDIO_UNSPECIFIED; - } - - /* Name */ - if (deviceType == ma_device_type_playback) { - ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), MA_DEFAULT_PLAYBACK_DEVICE_NAME, (size_t)-1); - } else { - ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), MA_DEFAULT_CAPTURE_DEVICE_NAME, (size_t)-1); - } - - - pDeviceInfo->nativeDataFormatCount = 0; - - /* We'll need to open the device to get accurate sample rate and channel count information. */ - result = ma_open_stream_basic__aaudio(pContext, pDeviceID, deviceType, ma_share_mode_shared, &pStream); - if (result != MA_SUCCESS) { - return result; - } - - ma_context_add_native_data_format_from_AAudioStream__aaudio(pContext, pStream, 0, pDeviceInfo); - - ma_close_stream__aaudio(pContext, pStream); - pStream = NULL; - - return MA_SUCCESS; -} - -static ma_result ma_close_streams__aaudio(ma_device* pDevice) -{ - MA_ASSERT(pDevice != NULL); - - /* When re-routing, streams may have been closed and never re-opened. Hence the extra checks below. */ - if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) { - ma_close_stream__aaudio(pDevice->pContext, (ma_AAudioStream*)pDevice->aaudio.pStreamCapture); - pDevice->aaudio.pStreamCapture = NULL; - } - if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) { - ma_close_stream__aaudio(pDevice->pContext, (ma_AAudioStream*)pDevice->aaudio.pStreamPlayback); - pDevice->aaudio.pStreamPlayback = NULL; - } - - return MA_SUCCESS; -} - -static ma_result ma_device_uninit__aaudio(ma_device* pDevice) -{ - MA_ASSERT(pDevice != NULL); - - /* Wait for any rerouting to finish before attempting to close the streams. */ - ma_mutex_lock(&pDevice->aaudio.rerouteLock); - { - ma_close_streams__aaudio(pDevice); - } - ma_mutex_unlock(&pDevice->aaudio.rerouteLock); - - /* Destroy re-routing lock. */ - ma_mutex_uninit(&pDevice->aaudio.rerouteLock); - - return MA_SUCCESS; -} - -static ma_result ma_device_init_by_type__aaudio(ma_device* pDevice, const ma_device_config* pConfig, ma_device_type deviceType, ma_device_descriptor* pDescriptor, ma_AAudioStream** ppStream) -{ - ma_result result; - int32_t bufferCapacityInFrames; - int32_t framesPerDataCallback; - ma_AAudioStream* pStream; - - MA_ASSERT(pDevice != NULL); - MA_ASSERT(pConfig != NULL); - MA_ASSERT(pDescriptor != NULL); - - *ppStream = NULL; /* Safety. */ - - /* First step is to open the stream. From there we'll be able to extract the internal configuration. */ - result = ma_open_stream__aaudio(pDevice, pConfig, deviceType, pDescriptor, &pStream); - if (result != MA_SUCCESS) { - return result; /* Failed to open the AAudio stream. */ - } - - /* Now extract the internal configuration. */ - pDescriptor->format = (((MA_PFN_AAudioStream_getFormat)pDevice->pContext->aaudio.AAudioStream_getFormat)(pStream) == MA_AAUDIO_FORMAT_PCM_I16) ? ma_format_s16 : ma_format_f32; - pDescriptor->channels = ((MA_PFN_AAudioStream_getChannelCount)pDevice->pContext->aaudio.AAudioStream_getChannelCount)(pStream); - pDescriptor->sampleRate = ((MA_PFN_AAudioStream_getSampleRate)pDevice->pContext->aaudio.AAudioStream_getSampleRate)(pStream); - - /* For the channel map we need to be sure we don't overflow any buffers. */ - if (pDescriptor->channels <= MA_MAX_CHANNELS) { - ma_channel_map_init_standard(ma_standard_channel_map_default, pDescriptor->channelMap, ma_countof(pDescriptor->channelMap), pDescriptor->channels); /* <-- Cannot find info on channel order, so assuming a default. */ - } else { - ma_channel_map_init_blank(pDescriptor->channelMap, MA_MAX_CHANNELS); /* Too many channels. Use a blank channel map. */ - } - - bufferCapacityInFrames = ((MA_PFN_AAudioStream_getBufferCapacityInFrames)pDevice->pContext->aaudio.AAudioStream_getBufferCapacityInFrames)(pStream); - framesPerDataCallback = ((MA_PFN_AAudioStream_getFramesPerDataCallback)pDevice->pContext->aaudio.AAudioStream_getFramesPerDataCallback)(pStream); - - if (framesPerDataCallback > 0) { - pDescriptor->periodSizeInFrames = framesPerDataCallback; - pDescriptor->periodCount = bufferCapacityInFrames / framesPerDataCallback; - } else { - pDescriptor->periodSizeInFrames = bufferCapacityInFrames; - pDescriptor->periodCount = 1; - } - - *ppStream = pStream; - - return MA_SUCCESS; -} - -static ma_result ma_device_init_streams__aaudio(ma_device* pDevice, const ma_device_config* pConfig, ma_device_descriptor* pDescriptorPlayback, ma_device_descriptor* pDescriptorCapture) -{ - ma_result result; - - MA_ASSERT(pDevice != NULL); - - if (pConfig->deviceType == ma_device_type_loopback) { - return MA_DEVICE_TYPE_NOT_SUPPORTED; - } - - pDevice->aaudio.usage = pConfig->aaudio.usage; - pDevice->aaudio.contentType = pConfig->aaudio.contentType; - pDevice->aaudio.inputPreset = pConfig->aaudio.inputPreset; - pDevice->aaudio.allowedCapturePolicy = pConfig->aaudio.allowedCapturePolicy; - pDevice->aaudio.noAutoStartAfterReroute = pConfig->aaudio.noAutoStartAfterReroute; - - if (pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) { - result = ma_device_init_by_type__aaudio(pDevice, pConfig, ma_device_type_capture, pDescriptorCapture, (ma_AAudioStream**)&pDevice->aaudio.pStreamCapture); - if (result != MA_SUCCESS) { - return result; - } - } - - if (pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) { - result = ma_device_init_by_type__aaudio(pDevice, pConfig, ma_device_type_playback, pDescriptorPlayback, (ma_AAudioStream**)&pDevice->aaudio.pStreamPlayback); - if (result != MA_SUCCESS) { - return result; - } - } - - return MA_SUCCESS; -} - -static ma_result ma_device_init__aaudio(ma_device* pDevice, const ma_device_config* pConfig, ma_device_descriptor* pDescriptorPlayback, ma_device_descriptor* pDescriptorCapture) -{ - ma_result result; - - MA_ASSERT(pDevice != NULL); - - result = ma_device_init_streams__aaudio(pDevice, pConfig, pDescriptorPlayback, pDescriptorCapture); - if (result != MA_SUCCESS) { - return result; - } - - result = ma_mutex_init(&pDevice->aaudio.rerouteLock); - if (result != MA_SUCCESS) { - return result; - } - - return MA_SUCCESS; -} - -static ma_result ma_device_start_stream__aaudio(ma_device* pDevice, ma_AAudioStream* pStream) -{ - ma_aaudio_result_t resultAA; - ma_aaudio_stream_state_t currentState; - - MA_ASSERT(pDevice != NULL); - - if (pStream == NULL) { - return MA_INVALID_ARGS; - } - - resultAA = ((MA_PFN_AAudioStream_requestStart)pDevice->pContext->aaudio.AAudioStream_requestStart)(pStream); - if (resultAA != MA_AAUDIO_OK) { - return ma_result_from_aaudio(resultAA); - } - - /* Do we actually need to wait for the device to transition into its started state? */ - - /* The device should be in either a starting or started state. If it's not set to started we need to wait for it to transition. It should go from starting to started. */ - currentState = ((MA_PFN_AAudioStream_getState)pDevice->pContext->aaudio.AAudioStream_getState)(pStream); - if (currentState != MA_AAUDIO_STREAM_STATE_STARTED) { - ma_result result; - - if (currentState != MA_AAUDIO_STREAM_STATE_STARTING) { - return MA_ERROR; /* Expecting the stream to be a starting or started state. */ - } - - result = ma_wait_for_simple_state_transition__aaudio(pDevice->pContext, pStream, currentState, MA_AAUDIO_STREAM_STATE_STARTED); - if (result != MA_SUCCESS) { - return result; - } - } - - return MA_SUCCESS; -} - -static ma_result ma_device_stop_stream__aaudio(ma_device* pDevice, ma_AAudioStream* pStream) -{ - ma_aaudio_result_t resultAA; - ma_aaudio_stream_state_t currentState; - - MA_ASSERT(pDevice != NULL); - - if (pStream == NULL) { - return MA_INVALID_ARGS; - } - - /* - From the AAudio documentation: - - The stream will stop after all of the data currently buffered has been played. - - This maps with miniaudio's requirement that device's be drained which means we don't need to implement any draining logic. - */ - currentState = ((MA_PFN_AAudioStream_getState)pDevice->pContext->aaudio.AAudioStream_getState)(pStream); - if (currentState == MA_AAUDIO_STREAM_STATE_DISCONNECTED) { - return MA_SUCCESS; /* The device is disconnected. Don't try stopping it. */ - } - - resultAA = ((MA_PFN_AAudioStream_requestStop)pDevice->pContext->aaudio.AAudioStream_requestStop)(pStream); - if (resultAA != MA_AAUDIO_OK) { - return ma_result_from_aaudio(resultAA); - } - - /* The device should be in either a stopping or stopped state. If it's not set to started we need to wait for it to transition. It should go from stopping to stopped. */ - currentState = ((MA_PFN_AAudioStream_getState)pDevice->pContext->aaudio.AAudioStream_getState)(pStream); - if (currentState != MA_AAUDIO_STREAM_STATE_STOPPED) { - ma_result result; - - if (currentState != MA_AAUDIO_STREAM_STATE_STOPPING) { - return MA_ERROR; /* Expecting the stream to be a stopping or stopped state. */ - } - - result = ma_wait_for_simple_state_transition__aaudio(pDevice->pContext, pStream, currentState, MA_AAUDIO_STREAM_STATE_STOPPED); - if (result != MA_SUCCESS) { - return result; - } - } - - return MA_SUCCESS; -} - -static ma_result ma_device_start__aaudio(ma_device* pDevice) -{ - MA_ASSERT(pDevice != NULL); - - if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) { - ma_result result = ma_device_start_stream__aaudio(pDevice, (ma_AAudioStream*)pDevice->aaudio.pStreamCapture); - if (result != MA_SUCCESS) { - return result; - } - } - - if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) { - ma_result result = ma_device_start_stream__aaudio(pDevice, (ma_AAudioStream*)pDevice->aaudio.pStreamPlayback); - if (result != MA_SUCCESS) { - if (pDevice->type == ma_device_type_duplex) { - ma_device_stop_stream__aaudio(pDevice, (ma_AAudioStream*)pDevice->aaudio.pStreamCapture); - } - return result; - } - } - - return MA_SUCCESS; -} - -static ma_result ma_device_stop__aaudio(ma_device* pDevice) -{ - MA_ASSERT(pDevice != NULL); - - if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) { - ma_result result = ma_device_stop_stream__aaudio(pDevice, (ma_AAudioStream*)pDevice->aaudio.pStreamCapture); - if (result != MA_SUCCESS) { - return result; - } - } - - if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) { - ma_result result = ma_device_stop_stream__aaudio(pDevice, (ma_AAudioStream*)pDevice->aaudio.pStreamPlayback); - if (result != MA_SUCCESS) { - return result; - } - } - - ma_device__on_notification_stopped(pDevice); - - return MA_SUCCESS; -} - -static ma_result ma_device_reinit__aaudio(ma_device* pDevice, ma_device_type deviceType) -{ - ma_result result; - int32_t retries = 0; - - MA_ASSERT(pDevice != NULL); - - /* - TODO: Stop retrying if main thread is about to uninit device. - */ - ma_mutex_lock(&pDevice->aaudio.rerouteLock); - { -error_disconnected: - /* The first thing to do is close the streams. */ - ma_close_streams__aaudio(pDevice); - - /* Now we need to reinitialize each streams. The hardest part with this is just filling output the config and descriptors. */ - ma_device_config deviceConfig; - ma_device_descriptor descriptorPlayback; - ma_device_descriptor descriptorCapture; - - deviceConfig = ma_device_config_init(deviceType); - deviceConfig.playback.pDeviceID = NULL; /* Only doing rerouting with default devices. */ - deviceConfig.playback.shareMode = pDevice->playback.shareMode; - deviceConfig.playback.format = pDevice->playback.format; - deviceConfig.playback.channels = pDevice->playback.channels; - deviceConfig.capture.pDeviceID = NULL; /* Only doing rerouting with default devices. */ - deviceConfig.capture.shareMode = pDevice->capture.shareMode; - deviceConfig.capture.format = pDevice->capture.format; - deviceConfig.capture.channels = pDevice->capture.channels; - deviceConfig.sampleRate = pDevice->sampleRate; - deviceConfig.aaudio.usage = pDevice->aaudio.usage; - deviceConfig.aaudio.contentType = pDevice->aaudio.contentType; - deviceConfig.aaudio.inputPreset = pDevice->aaudio.inputPreset; - deviceConfig.aaudio.allowedCapturePolicy = pDevice->aaudio.allowedCapturePolicy; - deviceConfig.aaudio.noAutoStartAfterReroute = pDevice->aaudio.noAutoStartAfterReroute; - deviceConfig.periods = 1; - - /* Try to get an accurate period size. */ - if (deviceType == ma_device_type_playback || deviceType == ma_device_type_duplex) { - deviceConfig.periodSizeInFrames = pDevice->playback.internalPeriodSizeInFrames; - } else { - deviceConfig.periodSizeInFrames = pDevice->capture.internalPeriodSizeInFrames; - } - - if (deviceType == ma_device_type_capture || deviceType == ma_device_type_duplex || deviceType == ma_device_type_loopback) { - descriptorCapture.pDeviceID = deviceConfig.capture.pDeviceID; - descriptorCapture.shareMode = deviceConfig.capture.shareMode; - descriptorCapture.format = deviceConfig.capture.format; - descriptorCapture.channels = deviceConfig.capture.channels; - descriptorCapture.sampleRate = deviceConfig.sampleRate; - descriptorCapture.periodSizeInFrames = deviceConfig.periodSizeInFrames; - descriptorCapture.periodCount = deviceConfig.periods; - } - - if (deviceType == ma_device_type_playback || deviceType == ma_device_type_duplex) { - descriptorPlayback.pDeviceID = deviceConfig.playback.pDeviceID; - descriptorPlayback.shareMode = deviceConfig.playback.shareMode; - descriptorPlayback.format = deviceConfig.playback.format; - descriptorPlayback.channels = deviceConfig.playback.channels; - descriptorPlayback.sampleRate = deviceConfig.sampleRate; - descriptorPlayback.periodSizeInFrames = deviceConfig.periodSizeInFrames; - descriptorPlayback.periodCount = deviceConfig.periods; - } - - result = ma_device_init_streams__aaudio(pDevice, &deviceConfig, &descriptorPlayback, &descriptorCapture); - if (result != MA_SUCCESS) { - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_WARNING, "[AAudio] Failed to create stream after route change."); - goto done; - } - - result = ma_device_post_init(pDevice, deviceType, &descriptorPlayback, &descriptorCapture); - if (result != MA_SUCCESS) { - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_WARNING, "[AAudio] Failed to initialize device after route change."); - ma_close_streams__aaudio(pDevice); - goto done; - } - - /* We'll only ever do this in response to a reroute. */ - ma_device__on_notification_rerouted(pDevice); - - /* If the device is started, start the streams. Maybe make this configurable? */ - if (ma_device_get_state(pDevice) == ma_device_state_started) { - if (pDevice->aaudio.noAutoStartAfterReroute == MA_FALSE) { - result = ma_device_start__aaudio(pDevice); - if (result != MA_SUCCESS) { - /* We got disconnected! Retry a few times, until we find a connected device! */ - retries += 1; - if (retries <= 3) { - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, "[AAudio] Failed to start stream after route change, retrying(%d)", retries); - goto error_disconnected; - } - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, "[AAudio] Failed to start stream after route change."); - goto done; - } - } else { - ma_device_stop(pDevice); /* Do a full device stop so we set internal state correctly. */ - } - } - - result = MA_SUCCESS; - } -done: - /* Re-routing done */ - ma_mutex_unlock(&pDevice->aaudio.rerouteLock); - - return result; -} - -static ma_result ma_device_get_info__aaudio(ma_device* pDevice, ma_device_type type, ma_device_info* pDeviceInfo) -{ - ma_AAudioStream* pStream = NULL; - - MA_ASSERT(pDevice != NULL); - MA_ASSERT(type != ma_device_type_duplex); - MA_ASSERT(pDeviceInfo != NULL); - - if (type == ma_device_type_capture) { - pStream = (ma_AAudioStream*)pDevice->aaudio.pStreamCapture; - pDeviceInfo->id.aaudio = pDevice->capture.id.aaudio; - ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), MA_DEFAULT_CAPTURE_DEVICE_NAME, (size_t)-1); /* Only supporting default devices. */ - } - if (type == ma_device_type_playback) { - pStream = (ma_AAudioStream*)pDevice->aaudio.pStreamPlayback; - pDeviceInfo->id.aaudio = pDevice->playback.id.aaudio; - ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), MA_DEFAULT_PLAYBACK_DEVICE_NAME, (size_t)-1); /* Only supporting default devices. */ - } - - /* Safety. Should never happen. */ - if (pStream == NULL) { - return MA_INVALID_OPERATION; - } - - pDeviceInfo->nativeDataFormatCount = 0; - ma_context_add_native_data_format_from_AAudioStream__aaudio(pDevice->pContext, pStream, 0, pDeviceInfo); - - return MA_SUCCESS; -} - - -static ma_result ma_context_uninit__aaudio(ma_context* pContext) -{ - MA_ASSERT(pContext != NULL); - MA_ASSERT(pContext->backend == ma_backend_aaudio); - - ma_device_job_thread_uninit(&pContext->aaudio.jobThread, &pContext->allocationCallbacks); - - ma_dlclose(ma_context_get_log(pContext), pContext->aaudio.hAAudio); - pContext->aaudio.hAAudio = NULL; - - return MA_SUCCESS; -} - -static ma_result ma_context_init__aaudio(ma_context* pContext, const ma_context_config* pConfig, ma_backend_callbacks* pCallbacks) -{ -#if !defined(MA_NO_RUNTIME_LINKING) - size_t i; - const char* libNames[] = { - "libaaudio.so" - }; - - for (i = 0; i < ma_countof(libNames); ++i) { - pContext->aaudio.hAAudio = ma_dlopen(ma_context_get_log(pContext), libNames[i]); - if (pContext->aaudio.hAAudio != NULL) { - break; - } - } - - if (pContext->aaudio.hAAudio == NULL) { - return MA_FAILED_TO_INIT_BACKEND; - } - - pContext->aaudio.AAudio_createStreamBuilder = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->aaudio.hAAudio, "AAudio_createStreamBuilder"); - pContext->aaudio.AAudioStreamBuilder_delete = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->aaudio.hAAudio, "AAudioStreamBuilder_delete"); - pContext->aaudio.AAudioStreamBuilder_setDeviceId = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->aaudio.hAAudio, "AAudioStreamBuilder_setDeviceId"); - pContext->aaudio.AAudioStreamBuilder_setDirection = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->aaudio.hAAudio, "AAudioStreamBuilder_setDirection"); - pContext->aaudio.AAudioStreamBuilder_setSharingMode = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->aaudio.hAAudio, "AAudioStreamBuilder_setSharingMode"); - pContext->aaudio.AAudioStreamBuilder_setFormat = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->aaudio.hAAudio, "AAudioStreamBuilder_setFormat"); - pContext->aaudio.AAudioStreamBuilder_setChannelCount = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->aaudio.hAAudio, "AAudioStreamBuilder_setChannelCount"); - pContext->aaudio.AAudioStreamBuilder_setSampleRate = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->aaudio.hAAudio, "AAudioStreamBuilder_setSampleRate"); - pContext->aaudio.AAudioStreamBuilder_setBufferCapacityInFrames = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->aaudio.hAAudio, "AAudioStreamBuilder_setBufferCapacityInFrames"); - pContext->aaudio.AAudioStreamBuilder_setFramesPerDataCallback = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->aaudio.hAAudio, "AAudioStreamBuilder_setFramesPerDataCallback"); - pContext->aaudio.AAudioStreamBuilder_setDataCallback = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->aaudio.hAAudio, "AAudioStreamBuilder_setDataCallback"); - pContext->aaudio.AAudioStreamBuilder_setErrorCallback = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->aaudio.hAAudio, "AAudioStreamBuilder_setErrorCallback"); - pContext->aaudio.AAudioStreamBuilder_setPerformanceMode = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->aaudio.hAAudio, "AAudioStreamBuilder_setPerformanceMode"); - pContext->aaudio.AAudioStreamBuilder_setUsage = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->aaudio.hAAudio, "AAudioStreamBuilder_setUsage"); - pContext->aaudio.AAudioStreamBuilder_setContentType = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->aaudio.hAAudio, "AAudioStreamBuilder_setContentType"); - pContext->aaudio.AAudioStreamBuilder_setInputPreset = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->aaudio.hAAudio, "AAudioStreamBuilder_setInputPreset"); - pContext->aaudio.AAudioStreamBuilder_setAllowedCapturePolicy = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->aaudio.hAAudio, "AAudioStreamBuilder_setAllowedCapturePolicy"); - pContext->aaudio.AAudioStreamBuilder_openStream = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->aaudio.hAAudio, "AAudioStreamBuilder_openStream"); - pContext->aaudio.AAudioStream_close = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->aaudio.hAAudio, "AAudioStream_close"); - pContext->aaudio.AAudioStream_getState = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->aaudio.hAAudio, "AAudioStream_getState"); - pContext->aaudio.AAudioStream_waitForStateChange = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->aaudio.hAAudio, "AAudioStream_waitForStateChange"); - pContext->aaudio.AAudioStream_getFormat = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->aaudio.hAAudio, "AAudioStream_getFormat"); - pContext->aaudio.AAudioStream_getChannelCount = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->aaudio.hAAudio, "AAudioStream_getChannelCount"); - pContext->aaudio.AAudioStream_getSampleRate = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->aaudio.hAAudio, "AAudioStream_getSampleRate"); - pContext->aaudio.AAudioStream_getBufferCapacityInFrames = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->aaudio.hAAudio, "AAudioStream_getBufferCapacityInFrames"); - pContext->aaudio.AAudioStream_getFramesPerDataCallback = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->aaudio.hAAudio, "AAudioStream_getFramesPerDataCallback"); - pContext->aaudio.AAudioStream_getFramesPerBurst = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->aaudio.hAAudio, "AAudioStream_getFramesPerBurst"); - pContext->aaudio.AAudioStream_requestStart = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->aaudio.hAAudio, "AAudioStream_requestStart"); - pContext->aaudio.AAudioStream_requestStop = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->aaudio.hAAudio, "AAudioStream_requestStop"); -#else - pContext->aaudio.AAudio_createStreamBuilder = (ma_proc)AAudio_createStreamBuilder; - pContext->aaudio.AAudioStreamBuilder_delete = (ma_proc)AAudioStreamBuilder_delete; - pContext->aaudio.AAudioStreamBuilder_setDeviceId = (ma_proc)AAudioStreamBuilder_setDeviceId; - pContext->aaudio.AAudioStreamBuilder_setDirection = (ma_proc)AAudioStreamBuilder_setDirection; - pContext->aaudio.AAudioStreamBuilder_setSharingMode = (ma_proc)AAudioStreamBuilder_setSharingMode; - pContext->aaudio.AAudioStreamBuilder_setFormat = (ma_proc)AAudioStreamBuilder_setFormat; - pContext->aaudio.AAudioStreamBuilder_setChannelCount = (ma_proc)AAudioStreamBuilder_setChannelCount; - pContext->aaudio.AAudioStreamBuilder_setSampleRate = (ma_proc)AAudioStreamBuilder_setSampleRate; - pContext->aaudio.AAudioStreamBuilder_setBufferCapacityInFrames = (ma_proc)AAudioStreamBuilder_setBufferCapacityInFrames; - pContext->aaudio.AAudioStreamBuilder_setFramesPerDataCallback = (ma_proc)AAudioStreamBuilder_setFramesPerDataCallback; - pContext->aaudio.AAudioStreamBuilder_setDataCallback = (ma_proc)AAudioStreamBuilder_setDataCallback; - pContext->aaudio.AAudioStreamBuilder_setErrorCallback = (ma_proc)AAudioStreamBuilder_setErrorCallback; - pContext->aaudio.AAudioStreamBuilder_setPerformanceMode = (ma_proc)AAudioStreamBuilder_setPerformanceMode; - pContext->aaudio.AAudioStreamBuilder_setUsage = (ma_proc)AAudioStreamBuilder_setUsage; - pContext->aaudio.AAudioStreamBuilder_setContentType = (ma_proc)AAudioStreamBuilder_setContentType; - pContext->aaudio.AAudioStreamBuilder_setInputPreset = (ma_proc)AAudioStreamBuilder_setInputPreset; - #if defined(__ANDROID_API__) && __ANDROID_API__ >= 29 - pContext->aaudio.AAudioStreamBuilder_setAllowedCapturePolicy = (ma_proc)AAudioStreamBuilder_setAllowedCapturePolicy; - #endif - pContext->aaudio.AAudioStreamBuilder_openStream = (ma_proc)AAudioStreamBuilder_openStream; - pContext->aaudio.AAudioStream_close = (ma_proc)AAudioStream_close; - pContext->aaudio.AAudioStream_getState = (ma_proc)AAudioStream_getState; - pContext->aaudio.AAudioStream_waitForStateChange = (ma_proc)AAudioStream_waitForStateChange; - pContext->aaudio.AAudioStream_getFormat = (ma_proc)AAudioStream_getFormat; - pContext->aaudio.AAudioStream_getChannelCount = (ma_proc)AAudioStream_getChannelCount; - pContext->aaudio.AAudioStream_getSampleRate = (ma_proc)AAudioStream_getSampleRate; - pContext->aaudio.AAudioStream_getBufferCapacityInFrames = (ma_proc)AAudioStream_getBufferCapacityInFrames; - pContext->aaudio.AAudioStream_getFramesPerDataCallback = (ma_proc)AAudioStream_getFramesPerDataCallback; - pContext->aaudio.AAudioStream_getFramesPerBurst = (ma_proc)AAudioStream_getFramesPerBurst; - pContext->aaudio.AAudioStream_requestStart = (ma_proc)AAudioStream_requestStart; - pContext->aaudio.AAudioStream_requestStop = (ma_proc)AAudioStream_requestStop; -#endif - - pCallbacks->onContextInit = ma_context_init__aaudio; - pCallbacks->onContextUninit = ma_context_uninit__aaudio; - pCallbacks->onContextEnumerateDevices = ma_context_enumerate_devices__aaudio; - pCallbacks->onContextGetDeviceInfo = ma_context_get_device_info__aaudio; - pCallbacks->onDeviceInit = ma_device_init__aaudio; - pCallbacks->onDeviceUninit = ma_device_uninit__aaudio; - pCallbacks->onDeviceStart = ma_device_start__aaudio; - pCallbacks->onDeviceStop = ma_device_stop__aaudio; - pCallbacks->onDeviceRead = NULL; /* Not used because AAudio is asynchronous. */ - pCallbacks->onDeviceWrite = NULL; /* Not used because AAudio is asynchronous. */ - pCallbacks->onDeviceDataLoop = NULL; /* Not used because AAudio is asynchronous. */ - pCallbacks->onDeviceGetInfo = ma_device_get_info__aaudio; - - - /* We need a job thread so we can deal with rerouting. */ - { - ma_result result; - ma_device_job_thread_config jobThreadConfig; - - jobThreadConfig = ma_device_job_thread_config_init(); - - result = ma_device_job_thread_init(&jobThreadConfig, &pContext->allocationCallbacks, &pContext->aaudio.jobThread); - if (result != MA_SUCCESS) { - ma_dlclose(ma_context_get_log(pContext), pContext->aaudio.hAAudio); - pContext->aaudio.hAAudio = NULL; - return result; - } - } - - - (void)pConfig; - return MA_SUCCESS; -} - -static ma_result ma_job_process__device__aaudio_reroute(ma_job* pJob) -{ - ma_result result; - ma_device* pDevice; - - MA_ASSERT(pJob != NULL); - - pDevice = (ma_device*)pJob->data.device.aaudio.reroute.pDevice; - MA_ASSERT(pDevice != NULL); - - /* Here is where we need to reroute the device. To do this we need to uninitialize the stream and reinitialize it. */ - result = ma_device_reinit__aaudio(pDevice, (ma_device_type)pJob->data.device.aaudio.reroute.deviceType); - if (result != MA_SUCCESS) { - /* - Getting here means we failed to reroute the device. The best thing I can think of here is to - just stop the device. - */ - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[AAudio] Stopping device due to reroute failure."); - ma_device_stop(pDevice); - return result; - } - - return MA_SUCCESS; -} -#else -/* Getting here means there is no AAudio backend so we need a no-op job implementation. */ -static ma_result ma_job_process__device__aaudio_reroute(ma_job* pJob) -{ - return ma_job_process__noop(pJob); -} -#endif /* AAudio */ - - -/****************************************************************************** - -OpenSL|ES Backend - -******************************************************************************/ -#ifdef MA_HAS_OPENSL -#include -#ifdef MA_ANDROID -#include -#endif - -typedef SLresult (SLAPIENTRY * ma_slCreateEngine_proc)(SLObjectItf* pEngine, SLuint32 numOptions, SLEngineOption* pEngineOptions, SLuint32 numInterfaces, SLInterfaceID* pInterfaceIds, SLboolean* pInterfaceRequired); - -/* OpenSL|ES has one-per-application objects :( */ -static SLObjectItf g_maEngineObjectSL = NULL; -static SLEngineItf g_maEngineSL = NULL; -static ma_uint32 g_maOpenSLInitCounter = 0; -static ma_spinlock g_maOpenSLSpinlock = 0; /* For init/uninit. */ - -#define MA_OPENSL_OBJ(p) (*((SLObjectItf)(p))) -#define MA_OPENSL_OUTPUTMIX(p) (*((SLOutputMixItf)(p))) -#define MA_OPENSL_PLAY(p) (*((SLPlayItf)(p))) -#define MA_OPENSL_RECORD(p) (*((SLRecordItf)(p))) - -#ifdef MA_ANDROID -#define MA_OPENSL_BUFFERQUEUE(p) (*((SLAndroidSimpleBufferQueueItf)(p))) -#else -#define MA_OPENSL_BUFFERQUEUE(p) (*((SLBufferQueueItf)(p))) -#endif - -static ma_result ma_result_from_OpenSL(SLuint32 result) -{ - switch (result) - { - case SL_RESULT_SUCCESS: return MA_SUCCESS; - case SL_RESULT_PRECONDITIONS_VIOLATED: return MA_ERROR; - case SL_RESULT_PARAMETER_INVALID: return MA_INVALID_ARGS; - case SL_RESULT_MEMORY_FAILURE: return MA_OUT_OF_MEMORY; - case SL_RESULT_RESOURCE_ERROR: return MA_INVALID_DATA; - case SL_RESULT_RESOURCE_LOST: return MA_ERROR; - case SL_RESULT_IO_ERROR: return MA_IO_ERROR; - case SL_RESULT_BUFFER_INSUFFICIENT: return MA_NO_SPACE; - case SL_RESULT_CONTENT_CORRUPTED: return MA_INVALID_DATA; - case SL_RESULT_CONTENT_UNSUPPORTED: return MA_FORMAT_NOT_SUPPORTED; - case SL_RESULT_CONTENT_NOT_FOUND: return MA_ERROR; - case SL_RESULT_PERMISSION_DENIED: return MA_ACCESS_DENIED; - case SL_RESULT_FEATURE_UNSUPPORTED: return MA_NOT_IMPLEMENTED; - case SL_RESULT_INTERNAL_ERROR: return MA_ERROR; - case SL_RESULT_UNKNOWN_ERROR: return MA_ERROR; - case SL_RESULT_OPERATION_ABORTED: return MA_ERROR; - case SL_RESULT_CONTROL_LOST: return MA_ERROR; - default: return MA_ERROR; - } -} - -/* Converts an individual OpenSL-style channel identifier (SL_SPEAKER_FRONT_LEFT, etc.) to miniaudio. */ -static ma_uint8 ma_channel_id_to_ma__opensl(SLuint32 id) -{ - switch (id) - { - case SL_SPEAKER_FRONT_LEFT: return MA_CHANNEL_FRONT_LEFT; - case SL_SPEAKER_FRONT_RIGHT: return MA_CHANNEL_FRONT_RIGHT; - case SL_SPEAKER_FRONT_CENTER: return MA_CHANNEL_FRONT_CENTER; - case SL_SPEAKER_LOW_FREQUENCY: return MA_CHANNEL_LFE; - case SL_SPEAKER_BACK_LEFT: return MA_CHANNEL_BACK_LEFT; - case SL_SPEAKER_BACK_RIGHT: return MA_CHANNEL_BACK_RIGHT; - case SL_SPEAKER_FRONT_LEFT_OF_CENTER: return MA_CHANNEL_FRONT_LEFT_CENTER; - case SL_SPEAKER_FRONT_RIGHT_OF_CENTER: return MA_CHANNEL_FRONT_RIGHT_CENTER; - case SL_SPEAKER_BACK_CENTER: return MA_CHANNEL_BACK_CENTER; - case SL_SPEAKER_SIDE_LEFT: return MA_CHANNEL_SIDE_LEFT; - case SL_SPEAKER_SIDE_RIGHT: return MA_CHANNEL_SIDE_RIGHT; - case SL_SPEAKER_TOP_CENTER: return MA_CHANNEL_TOP_CENTER; - case SL_SPEAKER_TOP_FRONT_LEFT: return MA_CHANNEL_TOP_FRONT_LEFT; - case SL_SPEAKER_TOP_FRONT_CENTER: return MA_CHANNEL_TOP_FRONT_CENTER; - case SL_SPEAKER_TOP_FRONT_RIGHT: return MA_CHANNEL_TOP_FRONT_RIGHT; - case SL_SPEAKER_TOP_BACK_LEFT: return MA_CHANNEL_TOP_BACK_LEFT; - case SL_SPEAKER_TOP_BACK_CENTER: return MA_CHANNEL_TOP_BACK_CENTER; - case SL_SPEAKER_TOP_BACK_RIGHT: return MA_CHANNEL_TOP_BACK_RIGHT; - default: return 0; - } -} - -/* Converts an individual miniaudio channel identifier (MA_CHANNEL_FRONT_LEFT, etc.) to OpenSL-style. */ -static SLuint32 ma_channel_id_to_opensl(ma_uint8 id) -{ - switch (id) - { - case MA_CHANNEL_MONO: return SL_SPEAKER_FRONT_CENTER; - case MA_CHANNEL_FRONT_LEFT: return SL_SPEAKER_FRONT_LEFT; - case MA_CHANNEL_FRONT_RIGHT: return SL_SPEAKER_FRONT_RIGHT; - case MA_CHANNEL_FRONT_CENTER: return SL_SPEAKER_FRONT_CENTER; - case MA_CHANNEL_LFE: return SL_SPEAKER_LOW_FREQUENCY; - case MA_CHANNEL_BACK_LEFT: return SL_SPEAKER_BACK_LEFT; - case MA_CHANNEL_BACK_RIGHT: return SL_SPEAKER_BACK_RIGHT; - case MA_CHANNEL_FRONT_LEFT_CENTER: return SL_SPEAKER_FRONT_LEFT_OF_CENTER; - case MA_CHANNEL_FRONT_RIGHT_CENTER: return SL_SPEAKER_FRONT_RIGHT_OF_CENTER; - case MA_CHANNEL_BACK_CENTER: return SL_SPEAKER_BACK_CENTER; - case MA_CHANNEL_SIDE_LEFT: return SL_SPEAKER_SIDE_LEFT; - case MA_CHANNEL_SIDE_RIGHT: return SL_SPEAKER_SIDE_RIGHT; - case MA_CHANNEL_TOP_CENTER: return SL_SPEAKER_TOP_CENTER; - case MA_CHANNEL_TOP_FRONT_LEFT: return SL_SPEAKER_TOP_FRONT_LEFT; - case MA_CHANNEL_TOP_FRONT_CENTER: return SL_SPEAKER_TOP_FRONT_CENTER; - case MA_CHANNEL_TOP_FRONT_RIGHT: return SL_SPEAKER_TOP_FRONT_RIGHT; - case MA_CHANNEL_TOP_BACK_LEFT: return SL_SPEAKER_TOP_BACK_LEFT; - case MA_CHANNEL_TOP_BACK_CENTER: return SL_SPEAKER_TOP_BACK_CENTER; - case MA_CHANNEL_TOP_BACK_RIGHT: return SL_SPEAKER_TOP_BACK_RIGHT; - default: return 0; - } -} - -/* Converts a channel mapping to an OpenSL-style channel mask. */ -static SLuint32 ma_channel_map_to_channel_mask__opensl(const ma_channel* pChannelMap, ma_uint32 channels) -{ - SLuint32 channelMask = 0; - ma_uint32 iChannel; - for (iChannel = 0; iChannel < channels; ++iChannel) { - channelMask |= ma_channel_id_to_opensl(pChannelMap[iChannel]); - } - - return channelMask; -} - -/* Converts an OpenSL-style channel mask to a miniaudio channel map. */ -static void ma_channel_mask_to_channel_map__opensl(SLuint32 channelMask, ma_uint32 channels, ma_channel* pChannelMap) -{ - if (channels == 1 && channelMask == 0) { - pChannelMap[0] = MA_CHANNEL_MONO; - } else if (channels == 2 && channelMask == 0) { - pChannelMap[0] = MA_CHANNEL_FRONT_LEFT; - pChannelMap[1] = MA_CHANNEL_FRONT_RIGHT; - } else { - if (channels == 1 && (channelMask & SL_SPEAKER_FRONT_CENTER) != 0) { - pChannelMap[0] = MA_CHANNEL_MONO; - } else { - /* Just iterate over each bit. */ - ma_uint32 iChannel = 0; - ma_uint32 iBit; - for (iBit = 0; iBit < 32 && iChannel < channels; ++iBit) { - SLuint32 bitValue = (channelMask & (1UL << iBit)); - if (bitValue != 0) { - /* The bit is set. */ - pChannelMap[iChannel] = ma_channel_id_to_ma__opensl(bitValue); - iChannel += 1; - } - } - } - } -} - -static SLuint32 ma_round_to_standard_sample_rate__opensl(SLuint32 samplesPerSec) -{ - if (samplesPerSec <= SL_SAMPLINGRATE_8) { - return SL_SAMPLINGRATE_8; - } - if (samplesPerSec <= SL_SAMPLINGRATE_11_025) { - return SL_SAMPLINGRATE_11_025; - } - if (samplesPerSec <= SL_SAMPLINGRATE_12) { - return SL_SAMPLINGRATE_12; - } - if (samplesPerSec <= SL_SAMPLINGRATE_16) { - return SL_SAMPLINGRATE_16; - } - if (samplesPerSec <= SL_SAMPLINGRATE_22_05) { - return SL_SAMPLINGRATE_22_05; - } - if (samplesPerSec <= SL_SAMPLINGRATE_24) { - return SL_SAMPLINGRATE_24; - } - if (samplesPerSec <= SL_SAMPLINGRATE_32) { - return SL_SAMPLINGRATE_32; - } - if (samplesPerSec <= SL_SAMPLINGRATE_44_1) { - return SL_SAMPLINGRATE_44_1; - } - if (samplesPerSec <= SL_SAMPLINGRATE_48) { - return SL_SAMPLINGRATE_48; - } - - /* Android doesn't support more than 48000. */ -#ifndef MA_ANDROID - if (samplesPerSec <= SL_SAMPLINGRATE_64) { - return SL_SAMPLINGRATE_64; - } - if (samplesPerSec <= SL_SAMPLINGRATE_88_2) { - return SL_SAMPLINGRATE_88_2; - } - if (samplesPerSec <= SL_SAMPLINGRATE_96) { - return SL_SAMPLINGRATE_96; - } - if (samplesPerSec <= SL_SAMPLINGRATE_192) { - return SL_SAMPLINGRATE_192; - } -#endif - - return SL_SAMPLINGRATE_16; -} - - -static SLint32 ma_to_stream_type__opensl(ma_opensl_stream_type streamType) -{ - switch (streamType) { - case ma_opensl_stream_type_voice: return SL_ANDROID_STREAM_VOICE; - case ma_opensl_stream_type_system: return SL_ANDROID_STREAM_SYSTEM; - case ma_opensl_stream_type_ring: return SL_ANDROID_STREAM_RING; - case ma_opensl_stream_type_media: return SL_ANDROID_STREAM_MEDIA; - case ma_opensl_stream_type_alarm: return SL_ANDROID_STREAM_ALARM; - case ma_opensl_stream_type_notification: return SL_ANDROID_STREAM_NOTIFICATION; - default: break; - } - - return SL_ANDROID_STREAM_VOICE; -} - -static SLint32 ma_to_recording_preset__opensl(ma_opensl_recording_preset recordingPreset) -{ - switch (recordingPreset) { - case ma_opensl_recording_preset_generic: return SL_ANDROID_RECORDING_PRESET_GENERIC; - case ma_opensl_recording_preset_camcorder: return SL_ANDROID_RECORDING_PRESET_CAMCORDER; - case ma_opensl_recording_preset_voice_recognition: return SL_ANDROID_RECORDING_PRESET_VOICE_RECOGNITION; - case ma_opensl_recording_preset_voice_communication: return SL_ANDROID_RECORDING_PRESET_VOICE_COMMUNICATION; - case ma_opensl_recording_preset_voice_unprocessed: return SL_ANDROID_RECORDING_PRESET_UNPROCESSED; - default: break; - } - - return SL_ANDROID_RECORDING_PRESET_NONE; -} - - -static ma_result ma_context_enumerate_devices__opensl(ma_context* pContext, ma_enum_devices_callback_proc callback, void* pUserData) -{ - ma_bool32 cbResult; - - MA_ASSERT(pContext != NULL); - MA_ASSERT(callback != NULL); - - MA_ASSERT(g_maOpenSLInitCounter > 0); /* <-- If you trigger this it means you've either not initialized the context, or you've uninitialized it and then attempted to enumerate devices. */ - if (g_maOpenSLInitCounter == 0) { - return MA_INVALID_OPERATION; - } - - /* - TODO: Test Me. - - This is currently untested, so for now we are just returning default devices. - */ -#if 0 && !defined(MA_ANDROID) - ma_bool32 isTerminated = MA_FALSE; - - SLuint32 pDeviceIDs[128]; - SLint32 deviceCount = sizeof(pDeviceIDs) / sizeof(pDeviceIDs[0]); - - SLAudioIODeviceCapabilitiesItf deviceCaps; - SLresult resultSL = (*g_maEngineObjectSL)->GetInterface(g_maEngineObjectSL, (SLInterfaceID)pContext->opensl.SL_IID_AUDIOIODEVICECAPABILITIES, &deviceCaps); - if (resultSL != SL_RESULT_SUCCESS) { - /* The interface may not be supported so just report a default device. */ - goto return_default_device; - } - - /* Playback */ - if (!isTerminated) { - resultSL = (*deviceCaps)->GetAvailableAudioOutputs(deviceCaps, &deviceCount, pDeviceIDs); - if (resultSL != SL_RESULT_SUCCESS) { - return ma_result_from_OpenSL(resultSL); - } - - for (SLint32 iDevice = 0; iDevice < deviceCount; ++iDevice) { - ma_device_info deviceInfo; - MA_ZERO_OBJECT(&deviceInfo); - deviceInfo.id.opensl = pDeviceIDs[iDevice]; - - SLAudioOutputDescriptor desc; - resultSL = (*deviceCaps)->QueryAudioOutputCapabilities(deviceCaps, deviceInfo.id.opensl, &desc); - if (resultSL == SL_RESULT_SUCCESS) { - ma_strncpy_s(deviceInfo.name, sizeof(deviceInfo.name), (const char*)desc.pDeviceName, (size_t)-1); - - ma_bool32 cbResult = callback(pContext, ma_device_type_playback, &deviceInfo, pUserData); - if (cbResult == MA_FALSE) { - isTerminated = MA_TRUE; - break; - } - } - } - } - - /* Capture */ - if (!isTerminated) { - resultSL = (*deviceCaps)->GetAvailableAudioInputs(deviceCaps, &deviceCount, pDeviceIDs); - if (resultSL != SL_RESULT_SUCCESS) { - return ma_result_from_OpenSL(resultSL); - } - - for (SLint32 iDevice = 0; iDevice < deviceCount; ++iDevice) { - ma_device_info deviceInfo; - MA_ZERO_OBJECT(&deviceInfo); - deviceInfo.id.opensl = pDeviceIDs[iDevice]; - - SLAudioInputDescriptor desc; - resultSL = (*deviceCaps)->QueryAudioInputCapabilities(deviceCaps, deviceInfo.id.opensl, &desc); - if (resultSL == SL_RESULT_SUCCESS) { - ma_strncpy_s(deviceInfo.name, sizeof(deviceInfo.name), (const char*)desc.deviceName, (size_t)-1); - - ma_bool32 cbResult = callback(pContext, ma_device_type_capture, &deviceInfo, pUserData); - if (cbResult == MA_FALSE) { - isTerminated = MA_TRUE; - break; - } - } - } - } - - return MA_SUCCESS; -#else - goto return_default_device; -#endif - -return_default_device:; - cbResult = MA_TRUE; - - /* Playback. */ - if (cbResult) { - ma_device_info deviceInfo; - MA_ZERO_OBJECT(&deviceInfo); - deviceInfo.id.opensl = SL_DEFAULTDEVICEID_AUDIOOUTPUT; - ma_strncpy_s(deviceInfo.name, sizeof(deviceInfo.name), MA_DEFAULT_PLAYBACK_DEVICE_NAME, (size_t)-1); - cbResult = callback(pContext, ma_device_type_playback, &deviceInfo, pUserData); - } - - /* Capture. */ - if (cbResult) { - ma_device_info deviceInfo; - MA_ZERO_OBJECT(&deviceInfo); - deviceInfo.id.opensl = SL_DEFAULTDEVICEID_AUDIOINPUT; - ma_strncpy_s(deviceInfo.name, sizeof(deviceInfo.name), MA_DEFAULT_CAPTURE_DEVICE_NAME, (size_t)-1); - cbResult = callback(pContext, ma_device_type_capture, &deviceInfo, pUserData); - } - - return MA_SUCCESS; -} - -static void ma_context_add_data_format_ex__opensl(ma_context* pContext, ma_format format, ma_uint32 channels, ma_uint32 sampleRate, ma_device_info* pDeviceInfo) -{ - MA_ASSERT(pContext != NULL); - MA_ASSERT(pDeviceInfo != NULL); - - pDeviceInfo->nativeDataFormats[pDeviceInfo->nativeDataFormatCount].format = format; - pDeviceInfo->nativeDataFormats[pDeviceInfo->nativeDataFormatCount].channels = channels; - pDeviceInfo->nativeDataFormats[pDeviceInfo->nativeDataFormatCount].sampleRate = sampleRate; - pDeviceInfo->nativeDataFormats[pDeviceInfo->nativeDataFormatCount].flags = 0; - pDeviceInfo->nativeDataFormatCount += 1; -} - -static void ma_context_add_data_format__opensl(ma_context* pContext, ma_format format, ma_device_info* pDeviceInfo) -{ - ma_uint32 minChannels = 1; - ma_uint32 maxChannels = 2; - ma_uint32 minSampleRate = (ma_uint32)ma_standard_sample_rate_8000; - ma_uint32 maxSampleRate = (ma_uint32)ma_standard_sample_rate_48000; - ma_uint32 iChannel; - ma_uint32 iSampleRate; - - MA_ASSERT(pContext != NULL); - MA_ASSERT(pDeviceInfo != NULL); - - /* - Each sample format can support mono and stereo, and we'll support a small subset of standard - rates (up to 48000). A better solution would be to somehow find a native sample rate. - */ - for (iChannel = minChannels; iChannel < maxChannels; iChannel += 1) { - for (iSampleRate = 0; iSampleRate < ma_countof(g_maStandardSampleRatePriorities); iSampleRate += 1) { - ma_uint32 standardSampleRate = g_maStandardSampleRatePriorities[iSampleRate]; - if (standardSampleRate >= minSampleRate && standardSampleRate <= maxSampleRate) { - ma_context_add_data_format_ex__opensl(pContext, format, iChannel, standardSampleRate, pDeviceInfo); - } - } - } -} - -static ma_result ma_context_get_device_info__opensl(ma_context* pContext, ma_device_type deviceType, const ma_device_id* pDeviceID, ma_device_info* pDeviceInfo) -{ - MA_ASSERT(pContext != NULL); - - MA_ASSERT(g_maOpenSLInitCounter > 0); /* <-- If you trigger this it means you've either not initialized the context, or you've uninitialized it and then attempted to get device info. */ - if (g_maOpenSLInitCounter == 0) { - return MA_INVALID_OPERATION; - } - - /* - TODO: Test Me. - - This is currently untested, so for now we are just returning default devices. - */ -#if 0 && !defined(MA_ANDROID) - SLAudioIODeviceCapabilitiesItf deviceCaps; - SLresult resultSL = (*g_maEngineObjectSL)->GetInterface(g_maEngineObjectSL, (SLInterfaceID)pContext->opensl.SL_IID_AUDIOIODEVICECAPABILITIES, &deviceCaps); - if (resultSL != SL_RESULT_SUCCESS) { - /* The interface may not be supported so just report a default device. */ - goto return_default_device; - } - - if (deviceType == ma_device_type_playback) { - SLAudioOutputDescriptor desc; - resultSL = (*deviceCaps)->QueryAudioOutputCapabilities(deviceCaps, pDeviceID->opensl, &desc); - if (resultSL != SL_RESULT_SUCCESS) { - return ma_result_from_OpenSL(resultSL); - } - - ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), (const char*)desc.pDeviceName, (size_t)-1); - } else { - SLAudioInputDescriptor desc; - resultSL = (*deviceCaps)->QueryAudioInputCapabilities(deviceCaps, pDeviceID->opensl, &desc); - if (resultSL != SL_RESULT_SUCCESS) { - return ma_result_from_OpenSL(resultSL); - } - - ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), (const char*)desc.deviceName, (size_t)-1); - } - - goto return_detailed_info; -#else - goto return_default_device; -#endif - -return_default_device: - if (pDeviceID != NULL) { - if ((deviceType == ma_device_type_playback && pDeviceID->opensl != SL_DEFAULTDEVICEID_AUDIOOUTPUT) || - (deviceType == ma_device_type_capture && pDeviceID->opensl != SL_DEFAULTDEVICEID_AUDIOINPUT)) { - return MA_NO_DEVICE; /* Don't know the device. */ - } - } - - /* ID and Name / Description */ - if (deviceType == ma_device_type_playback) { - pDeviceInfo->id.opensl = SL_DEFAULTDEVICEID_AUDIOOUTPUT; - ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), MA_DEFAULT_PLAYBACK_DEVICE_NAME, (size_t)-1); - } else { - pDeviceInfo->id.opensl = SL_DEFAULTDEVICEID_AUDIOINPUT; - ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), MA_DEFAULT_CAPTURE_DEVICE_NAME, (size_t)-1); - } - - pDeviceInfo->isDefault = MA_TRUE; - - goto return_detailed_info; - - -return_detailed_info: - - /* - For now we're just outputting a set of values that are supported by the API but not necessarily supported - by the device natively. Later on we should work on this so that it more closely reflects the device's - actual native format. - */ - pDeviceInfo->nativeDataFormatCount = 0; -#if defined(MA_ANDROID) && __ANDROID_API__ >= 21 - ma_context_add_data_format__opensl(pContext, ma_format_f32, pDeviceInfo); -#endif - ma_context_add_data_format__opensl(pContext, ma_format_s16, pDeviceInfo); - ma_context_add_data_format__opensl(pContext, ma_format_u8, pDeviceInfo); - - return MA_SUCCESS; -} - - -#ifdef MA_ANDROID -/*void ma_buffer_queue_callback_capture__opensl_android(SLAndroidSimpleBufferQueueItf pBufferQueue, SLuint32 eventFlags, const void* pBuffer, SLuint32 bufferSize, SLuint32 dataUsed, void* pContext)*/ -static void ma_buffer_queue_callback_capture__opensl_android(SLAndroidSimpleBufferQueueItf pBufferQueue, void* pUserData) -{ - ma_device* pDevice = (ma_device*)pUserData; - size_t periodSizeInBytes; - ma_uint8* pBuffer; - SLresult resultSL; - - MA_ASSERT(pDevice != NULL); - - (void)pBufferQueue; - - /* - For now, don't do anything unless the buffer was fully processed. From what I can tell, it looks like - OpenSL|ES 1.1 improves on buffer queues to the point that we could much more intelligently handle this, - but unfortunately it looks like Android is only supporting OpenSL|ES 1.0.1 for now :( - */ - - /* Don't do anything if the device is not started. */ - if (ma_device_get_state(pDevice) != ma_device_state_started) { - return; - } - - /* Don't do anything if the device is being drained. */ - if (pDevice->opensl.isDrainingCapture) { - return; - } - - periodSizeInBytes = pDevice->capture.internalPeriodSizeInFrames * ma_get_bytes_per_frame(pDevice->capture.internalFormat, pDevice->capture.internalChannels); - pBuffer = pDevice->opensl.pBufferCapture + (pDevice->opensl.currentBufferIndexCapture * periodSizeInBytes); - - ma_device_handle_backend_data_callback(pDevice, NULL, pBuffer, pDevice->capture.internalPeriodSizeInFrames); - - resultSL = MA_OPENSL_BUFFERQUEUE(pDevice->opensl.pBufferQueueCapture)->Enqueue((SLAndroidSimpleBufferQueueItf)pDevice->opensl.pBufferQueueCapture, pBuffer, periodSizeInBytes); - if (resultSL != SL_RESULT_SUCCESS) { - return; - } - - pDevice->opensl.currentBufferIndexCapture = (pDevice->opensl.currentBufferIndexCapture + 1) % pDevice->capture.internalPeriods; -} - -static void ma_buffer_queue_callback_playback__opensl_android(SLAndroidSimpleBufferQueueItf pBufferQueue, void* pUserData) -{ - ma_device* pDevice = (ma_device*)pUserData; - size_t periodSizeInBytes; - ma_uint8* pBuffer; - SLresult resultSL; - - MA_ASSERT(pDevice != NULL); - - (void)pBufferQueue; - - /* Don't do anything if the device is not started. */ - if (ma_device_get_state(pDevice) != ma_device_state_started) { - return; - } - - /* Don't do anything if the device is being drained. */ - if (pDevice->opensl.isDrainingPlayback) { - return; - } - - periodSizeInBytes = pDevice->playback.internalPeriodSizeInFrames * ma_get_bytes_per_frame(pDevice->playback.internalFormat, pDevice->playback.internalChannels); - pBuffer = pDevice->opensl.pBufferPlayback + (pDevice->opensl.currentBufferIndexPlayback * periodSizeInBytes); - - ma_device_handle_backend_data_callback(pDevice, pBuffer, NULL, pDevice->playback.internalPeriodSizeInFrames); - - resultSL = MA_OPENSL_BUFFERQUEUE(pDevice->opensl.pBufferQueuePlayback)->Enqueue((SLAndroidSimpleBufferQueueItf)pDevice->opensl.pBufferQueuePlayback, pBuffer, periodSizeInBytes); - if (resultSL != SL_RESULT_SUCCESS) { - return; - } - - pDevice->opensl.currentBufferIndexPlayback = (pDevice->opensl.currentBufferIndexPlayback + 1) % pDevice->playback.internalPeriods; -} -#endif - -static ma_result ma_device_uninit__opensl(ma_device* pDevice) -{ - MA_ASSERT(pDevice != NULL); - - MA_ASSERT(g_maOpenSLInitCounter > 0); /* <-- If you trigger this it means you've either not initialized the context, or you've uninitialized it before uninitializing the device. */ - if (g_maOpenSLInitCounter == 0) { - return MA_INVALID_OPERATION; - } - - if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) { - if (pDevice->opensl.pAudioRecorderObj) { - MA_OPENSL_OBJ(pDevice->opensl.pAudioRecorderObj)->Destroy((SLObjectItf)pDevice->opensl.pAudioRecorderObj); - } - - ma_free(pDevice->opensl.pBufferCapture, &pDevice->pContext->allocationCallbacks); - } - - if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) { - if (pDevice->opensl.pAudioPlayerObj) { - MA_OPENSL_OBJ(pDevice->opensl.pAudioPlayerObj)->Destroy((SLObjectItf)pDevice->opensl.pAudioPlayerObj); - } - if (pDevice->opensl.pOutputMixObj) { - MA_OPENSL_OBJ(pDevice->opensl.pOutputMixObj)->Destroy((SLObjectItf)pDevice->opensl.pOutputMixObj); - } - - ma_free(pDevice->opensl.pBufferPlayback, &pDevice->pContext->allocationCallbacks); - } - - return MA_SUCCESS; -} - -#if defined(MA_ANDROID) && __ANDROID_API__ >= 21 -typedef SLAndroidDataFormat_PCM_EX ma_SLDataFormat_PCM; -#else -typedef SLDataFormat_PCM ma_SLDataFormat_PCM; -#endif - -static ma_result ma_SLDataFormat_PCM_init__opensl(ma_format format, ma_uint32 channels, ma_uint32 sampleRate, const ma_channel* channelMap, ma_SLDataFormat_PCM* pDataFormat) -{ - /* We need to convert our format/channels/rate so that they aren't set to default. */ - if (format == ma_format_unknown) { - format = MA_DEFAULT_FORMAT; - } - if (channels == 0) { - channels = MA_DEFAULT_CHANNELS; - } - if (sampleRate == 0) { - sampleRate = MA_DEFAULT_SAMPLE_RATE; - } - -#if defined(MA_ANDROID) && __ANDROID_API__ >= 21 - if (format == ma_format_f32) { - pDataFormat->formatType = SL_ANDROID_DATAFORMAT_PCM_EX; - pDataFormat->representation = SL_ANDROID_PCM_REPRESENTATION_FLOAT; - } else { - pDataFormat->formatType = SL_DATAFORMAT_PCM; - } -#else - pDataFormat->formatType = SL_DATAFORMAT_PCM; -#endif - - pDataFormat->numChannels = channels; - ((SLDataFormat_PCM*)pDataFormat)->samplesPerSec = ma_round_to_standard_sample_rate__opensl(sampleRate * 1000); /* In millihertz. Annoyingly, the sample rate variable is named differently between SLAndroidDataFormat_PCM_EX and SLDataFormat_PCM */ - pDataFormat->bitsPerSample = ma_get_bytes_per_sample(format) * 8; - pDataFormat->channelMask = ma_channel_map_to_channel_mask__opensl(channelMap, channels); - pDataFormat->endianness = (ma_is_little_endian()) ? SL_BYTEORDER_LITTLEENDIAN : SL_BYTEORDER_BIGENDIAN; - - /* - Android has a few restrictions on the format as documented here: https://developer.android.com/ndk/guides/audio/opensl-for-android.html - - Only mono and stereo is supported. - - Only u8 and s16 formats are supported. - - Maximum sample rate of 48000. - */ -#ifdef MA_ANDROID - if (pDataFormat->numChannels > 2) { - pDataFormat->numChannels = 2; - } -#if __ANDROID_API__ >= 21 - if (pDataFormat->formatType == SL_ANDROID_DATAFORMAT_PCM_EX) { - /* It's floating point. */ - MA_ASSERT(pDataFormat->representation == SL_ANDROID_PCM_REPRESENTATION_FLOAT); - if (pDataFormat->bitsPerSample > 32) { - pDataFormat->bitsPerSample = 32; - } - } else { - if (pDataFormat->bitsPerSample > 16) { - pDataFormat->bitsPerSample = 16; - } - } -#else - if (pDataFormat->bitsPerSample > 16) { - pDataFormat->bitsPerSample = 16; - } -#endif - if (((SLDataFormat_PCM*)pDataFormat)->samplesPerSec > SL_SAMPLINGRATE_48) { - ((SLDataFormat_PCM*)pDataFormat)->samplesPerSec = SL_SAMPLINGRATE_48; - } -#endif - - pDataFormat->containerSize = pDataFormat->bitsPerSample; /* Always tightly packed for now. */ - - return MA_SUCCESS; -} - -static ma_result ma_deconstruct_SLDataFormat_PCM__opensl(ma_SLDataFormat_PCM* pDataFormat, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate, ma_channel* pChannelMap, size_t channelMapCap) -{ - ma_bool32 isFloatingPoint = MA_FALSE; -#if defined(MA_ANDROID) && __ANDROID_API__ >= 21 - if (pDataFormat->formatType == SL_ANDROID_DATAFORMAT_PCM_EX) { - MA_ASSERT(pDataFormat->representation == SL_ANDROID_PCM_REPRESENTATION_FLOAT); - isFloatingPoint = MA_TRUE; - } -#endif - if (isFloatingPoint) { - if (pDataFormat->bitsPerSample == 32) { - *pFormat = ma_format_f32; - } - } else { - if (pDataFormat->bitsPerSample == 8) { - *pFormat = ma_format_u8; - } else if (pDataFormat->bitsPerSample == 16) { - *pFormat = ma_format_s16; - } else if (pDataFormat->bitsPerSample == 24) { - *pFormat = ma_format_s24; - } else if (pDataFormat->bitsPerSample == 32) { - *pFormat = ma_format_s32; - } - } - - *pChannels = pDataFormat->numChannels; - *pSampleRate = ((SLDataFormat_PCM*)pDataFormat)->samplesPerSec / 1000; - ma_channel_mask_to_channel_map__opensl(pDataFormat->channelMask, ma_min(pDataFormat->numChannels, channelMapCap), pChannelMap); - - return MA_SUCCESS; -} - -static ma_result ma_device_init__opensl(ma_device* pDevice, const ma_device_config* pConfig, ma_device_descriptor* pDescriptorPlayback, ma_device_descriptor* pDescriptorCapture) -{ -#ifdef MA_ANDROID - SLDataLocator_AndroidSimpleBufferQueue queue; - SLresult resultSL; - size_t bufferSizeInBytes; - SLInterfaceID itfIDs[2]; - const SLboolean itfIDsRequired[] = { - SL_BOOLEAN_TRUE, /* SL_IID_ANDROIDSIMPLEBUFFERQUEUE */ - SL_BOOLEAN_FALSE /* SL_IID_ANDROIDCONFIGURATION */ - }; -#endif - - MA_ASSERT(g_maOpenSLInitCounter > 0); /* <-- If you trigger this it means you've either not initialized the context, or you've uninitialized it and then attempted to initialize a new device. */ - if (g_maOpenSLInitCounter == 0) { - return MA_INVALID_OPERATION; - } - - if (pConfig->deviceType == ma_device_type_loopback) { - return MA_DEVICE_TYPE_NOT_SUPPORTED; - } - - /* - For now, only supporting Android implementations of OpenSL|ES since that's the only one I've - been able to test with and I currently depend on Android-specific extensions (simple buffer - queues). - */ -#ifdef MA_ANDROID - itfIDs[0] = (SLInterfaceID)pDevice->pContext->opensl.SL_IID_ANDROIDSIMPLEBUFFERQUEUE; - itfIDs[1] = (SLInterfaceID)pDevice->pContext->opensl.SL_IID_ANDROIDCONFIGURATION; - - /* No exclusive mode with OpenSL|ES. */ - if (((pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) && pDescriptorPlayback->shareMode == ma_share_mode_exclusive) || - ((pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) && pDescriptorCapture->shareMode == ma_share_mode_exclusive)) { - return MA_SHARE_MODE_NOT_SUPPORTED; - } - - /* Now we can start initializing the device properly. */ - MA_ASSERT(pDevice != NULL); - MA_ZERO_OBJECT(&pDevice->opensl); - - queue.locatorType = SL_DATALOCATOR_ANDROIDSIMPLEBUFFERQUEUE; - - if (pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) { - ma_SLDataFormat_PCM pcm; - SLDataLocator_IODevice locatorDevice; - SLDataSource source; - SLDataSink sink; - SLAndroidConfigurationItf pRecorderConfig; - - ma_SLDataFormat_PCM_init__opensl(pDescriptorCapture->format, pDescriptorCapture->channels, pDescriptorCapture->sampleRate, pDescriptorCapture->channelMap, &pcm); - - locatorDevice.locatorType = SL_DATALOCATOR_IODEVICE; - locatorDevice.deviceType = SL_IODEVICE_AUDIOINPUT; - locatorDevice.deviceID = SL_DEFAULTDEVICEID_AUDIOINPUT; /* Must always use the default device with Android. */ - locatorDevice.device = NULL; - - source.pLocator = &locatorDevice; - source.pFormat = NULL; - - queue.numBuffers = pDescriptorCapture->periodCount; - - sink.pLocator = &queue; - sink.pFormat = (SLDataFormat_PCM*)&pcm; - - resultSL = (*g_maEngineSL)->CreateAudioRecorder(g_maEngineSL, (SLObjectItf*)&pDevice->opensl.pAudioRecorderObj, &source, &sink, ma_countof(itfIDs), itfIDs, itfIDsRequired); - if (resultSL == SL_RESULT_CONTENT_UNSUPPORTED || resultSL == SL_RESULT_PARAMETER_INVALID) { - /* Unsupported format. Fall back to something safer and try again. If this fails, just abort. */ - pcm.formatType = SL_DATAFORMAT_PCM; - pcm.numChannels = 1; - ((SLDataFormat_PCM*)&pcm)->samplesPerSec = SL_SAMPLINGRATE_16; /* The name of the sample rate variable is different between SLAndroidDataFormat_PCM_EX and SLDataFormat_PCM. */ - pcm.bitsPerSample = 16; - pcm.containerSize = pcm.bitsPerSample; /* Always tightly packed for now. */ - pcm.channelMask = 0; - resultSL = (*g_maEngineSL)->CreateAudioRecorder(g_maEngineSL, (SLObjectItf*)&pDevice->opensl.pAudioRecorderObj, &source, &sink, ma_countof(itfIDs), itfIDs, itfIDsRequired); - } - - if (resultSL != SL_RESULT_SUCCESS) { - ma_device_uninit__opensl(pDevice); - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[OpenSL] Failed to create audio recorder."); - return ma_result_from_OpenSL(resultSL); - } - - - /* Set the recording preset before realizing the player. */ - if (pConfig->opensl.recordingPreset != ma_opensl_recording_preset_default) { - resultSL = MA_OPENSL_OBJ(pDevice->opensl.pAudioRecorderObj)->GetInterface((SLObjectItf)pDevice->opensl.pAudioRecorderObj, (SLInterfaceID)pDevice->pContext->opensl.SL_IID_ANDROIDCONFIGURATION, &pRecorderConfig); - if (resultSL == SL_RESULT_SUCCESS) { - SLint32 recordingPreset = ma_to_recording_preset__opensl(pConfig->opensl.recordingPreset); - resultSL = (*pRecorderConfig)->SetConfiguration(pRecorderConfig, SL_ANDROID_KEY_RECORDING_PRESET, &recordingPreset, sizeof(SLint32)); - if (resultSL != SL_RESULT_SUCCESS) { - /* Failed to set the configuration. Just keep going. */ - } - } - } - - resultSL = MA_OPENSL_OBJ(pDevice->opensl.pAudioRecorderObj)->Realize((SLObjectItf)pDevice->opensl.pAudioRecorderObj, SL_BOOLEAN_FALSE); - if (resultSL != SL_RESULT_SUCCESS) { - ma_device_uninit__opensl(pDevice); - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[OpenSL] Failed to realize audio recorder."); - return ma_result_from_OpenSL(resultSL); - } - - resultSL = MA_OPENSL_OBJ(pDevice->opensl.pAudioRecorderObj)->GetInterface((SLObjectItf)pDevice->opensl.pAudioRecorderObj, (SLInterfaceID)pDevice->pContext->opensl.SL_IID_RECORD, &pDevice->opensl.pAudioRecorder); - if (resultSL != SL_RESULT_SUCCESS) { - ma_device_uninit__opensl(pDevice); - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[OpenSL] Failed to retrieve SL_IID_RECORD interface."); - return ma_result_from_OpenSL(resultSL); - } - - resultSL = MA_OPENSL_OBJ(pDevice->opensl.pAudioRecorderObj)->GetInterface((SLObjectItf)pDevice->opensl.pAudioRecorderObj, (SLInterfaceID)pDevice->pContext->opensl.SL_IID_ANDROIDSIMPLEBUFFERQUEUE, &pDevice->opensl.pBufferQueueCapture); - if (resultSL != SL_RESULT_SUCCESS) { - ma_device_uninit__opensl(pDevice); - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[OpenSL] Failed to retrieve SL_IID_ANDROIDSIMPLEBUFFERQUEUE interface."); - return ma_result_from_OpenSL(resultSL); - } - - resultSL = MA_OPENSL_BUFFERQUEUE(pDevice->opensl.pBufferQueueCapture)->RegisterCallback((SLAndroidSimpleBufferQueueItf)pDevice->opensl.pBufferQueueCapture, ma_buffer_queue_callback_capture__opensl_android, pDevice); - if (resultSL != SL_RESULT_SUCCESS) { - ma_device_uninit__opensl(pDevice); - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[OpenSL] Failed to register buffer queue callback."); - return ma_result_from_OpenSL(resultSL); - } - - /* The internal format is determined by the "pcm" object. */ - ma_deconstruct_SLDataFormat_PCM__opensl(&pcm, &pDescriptorCapture->format, &pDescriptorCapture->channels, &pDescriptorCapture->sampleRate, pDescriptorCapture->channelMap, ma_countof(pDescriptorCapture->channelMap)); - - /* Buffer. */ - pDescriptorCapture->periodSizeInFrames = ma_calculate_buffer_size_in_frames_from_descriptor(pDescriptorCapture, pDescriptorCapture->sampleRate, pConfig->performanceProfile); - pDevice->opensl.currentBufferIndexCapture = 0; - - bufferSizeInBytes = pDescriptorCapture->periodSizeInFrames * ma_get_bytes_per_frame(pDescriptorCapture->format, pDescriptorCapture->channels) * pDescriptorCapture->periodCount; - pDevice->opensl.pBufferCapture = (ma_uint8*)ma_calloc(bufferSizeInBytes, &pDevice->pContext->allocationCallbacks); - if (pDevice->opensl.pBufferCapture == NULL) { - ma_device_uninit__opensl(pDevice); - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[OpenSL] Failed to allocate memory for data buffer."); - return MA_OUT_OF_MEMORY; - } - MA_ZERO_MEMORY(pDevice->opensl.pBufferCapture, bufferSizeInBytes); - } - - if (pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) { - ma_SLDataFormat_PCM pcm; - SLDataSource source; - SLDataLocator_OutputMix outmixLocator; - SLDataSink sink; - SLAndroidConfigurationItf pPlayerConfig; - - ma_SLDataFormat_PCM_init__opensl(pDescriptorPlayback->format, pDescriptorPlayback->channels, pDescriptorPlayback->sampleRate, pDescriptorPlayback->channelMap, &pcm); - - resultSL = (*g_maEngineSL)->CreateOutputMix(g_maEngineSL, (SLObjectItf*)&pDevice->opensl.pOutputMixObj, 0, NULL, NULL); - if (resultSL != SL_RESULT_SUCCESS) { - ma_device_uninit__opensl(pDevice); - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[OpenSL] Failed to create output mix."); - return ma_result_from_OpenSL(resultSL); - } - - resultSL = MA_OPENSL_OBJ(pDevice->opensl.pOutputMixObj)->Realize((SLObjectItf)pDevice->opensl.pOutputMixObj, SL_BOOLEAN_FALSE); - if (resultSL != SL_RESULT_SUCCESS) { - ma_device_uninit__opensl(pDevice); - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[OpenSL] Failed to realize output mix object."); - return ma_result_from_OpenSL(resultSL); - } - - resultSL = MA_OPENSL_OBJ(pDevice->opensl.pOutputMixObj)->GetInterface((SLObjectItf)pDevice->opensl.pOutputMixObj, (SLInterfaceID)pDevice->pContext->opensl.SL_IID_OUTPUTMIX, &pDevice->opensl.pOutputMix); - if (resultSL != SL_RESULT_SUCCESS) { - ma_device_uninit__opensl(pDevice); - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[OpenSL] Failed to retrieve SL_IID_OUTPUTMIX interface."); - return ma_result_from_OpenSL(resultSL); - } - - /* Set the output device. */ - if (pDescriptorPlayback->pDeviceID != NULL) { - SLuint32 deviceID_OpenSL = pDescriptorPlayback->pDeviceID->opensl; - MA_OPENSL_OUTPUTMIX(pDevice->opensl.pOutputMix)->ReRoute((SLOutputMixItf)pDevice->opensl.pOutputMix, 1, &deviceID_OpenSL); - } - - queue.numBuffers = pDescriptorPlayback->periodCount; - - source.pLocator = &queue; - source.pFormat = (SLDataFormat_PCM*)&pcm; - - outmixLocator.locatorType = SL_DATALOCATOR_OUTPUTMIX; - outmixLocator.outputMix = (SLObjectItf)pDevice->opensl.pOutputMixObj; - - sink.pLocator = &outmixLocator; - sink.pFormat = NULL; - - resultSL = (*g_maEngineSL)->CreateAudioPlayer(g_maEngineSL, (SLObjectItf*)&pDevice->opensl.pAudioPlayerObj, &source, &sink, ma_countof(itfIDs), itfIDs, itfIDsRequired); - if (resultSL == SL_RESULT_CONTENT_UNSUPPORTED || resultSL == SL_RESULT_PARAMETER_INVALID) { - /* Unsupported format. Fall back to something safer and try again. If this fails, just abort. */ - pcm.formatType = SL_DATAFORMAT_PCM; - pcm.numChannels = 2; - ((SLDataFormat_PCM*)&pcm)->samplesPerSec = SL_SAMPLINGRATE_16; - pcm.bitsPerSample = 16; - pcm.containerSize = pcm.bitsPerSample; /* Always tightly packed for now. */ - pcm.channelMask = SL_SPEAKER_FRONT_LEFT | SL_SPEAKER_FRONT_RIGHT; - resultSL = (*g_maEngineSL)->CreateAudioPlayer(g_maEngineSL, (SLObjectItf*)&pDevice->opensl.pAudioPlayerObj, &source, &sink, ma_countof(itfIDs), itfIDs, itfIDsRequired); - } - - if (resultSL != SL_RESULT_SUCCESS) { - ma_device_uninit__opensl(pDevice); - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[OpenSL] Failed to create audio player."); - return ma_result_from_OpenSL(resultSL); - } - - - /* Set the stream type before realizing the player. */ - if (pConfig->opensl.streamType != ma_opensl_stream_type_default) { - resultSL = MA_OPENSL_OBJ(pDevice->opensl.pAudioPlayerObj)->GetInterface((SLObjectItf)pDevice->opensl.pAudioPlayerObj, (SLInterfaceID)pDevice->pContext->opensl.SL_IID_ANDROIDCONFIGURATION, &pPlayerConfig); - if (resultSL == SL_RESULT_SUCCESS) { - SLint32 streamType = ma_to_stream_type__opensl(pConfig->opensl.streamType); - resultSL = (*pPlayerConfig)->SetConfiguration(pPlayerConfig, SL_ANDROID_KEY_STREAM_TYPE, &streamType, sizeof(SLint32)); - if (resultSL != SL_RESULT_SUCCESS) { - /* Failed to set the configuration. Just keep going. */ - } - } - } - - resultSL = MA_OPENSL_OBJ(pDevice->opensl.pAudioPlayerObj)->Realize((SLObjectItf)pDevice->opensl.pAudioPlayerObj, SL_BOOLEAN_FALSE); - if (resultSL != SL_RESULT_SUCCESS) { - ma_device_uninit__opensl(pDevice); - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[OpenSL] Failed to realize audio player."); - return ma_result_from_OpenSL(resultSL); - } - - resultSL = MA_OPENSL_OBJ(pDevice->opensl.pAudioPlayerObj)->GetInterface((SLObjectItf)pDevice->opensl.pAudioPlayerObj, (SLInterfaceID)pDevice->pContext->opensl.SL_IID_PLAY, &pDevice->opensl.pAudioPlayer); - if (resultSL != SL_RESULT_SUCCESS) { - ma_device_uninit__opensl(pDevice); - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[OpenSL] Failed to retrieve SL_IID_PLAY interface."); - return ma_result_from_OpenSL(resultSL); - } - - resultSL = MA_OPENSL_OBJ(pDevice->opensl.pAudioPlayerObj)->GetInterface((SLObjectItf)pDevice->opensl.pAudioPlayerObj, (SLInterfaceID)pDevice->pContext->opensl.SL_IID_ANDROIDSIMPLEBUFFERQUEUE, &pDevice->opensl.pBufferQueuePlayback); - if (resultSL != SL_RESULT_SUCCESS) { - ma_device_uninit__opensl(pDevice); - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[OpenSL] Failed to retrieve SL_IID_ANDROIDSIMPLEBUFFERQUEUE interface."); - return ma_result_from_OpenSL(resultSL); - } - - resultSL = MA_OPENSL_BUFFERQUEUE(pDevice->opensl.pBufferQueuePlayback)->RegisterCallback((SLAndroidSimpleBufferQueueItf)pDevice->opensl.pBufferQueuePlayback, ma_buffer_queue_callback_playback__opensl_android, pDevice); - if (resultSL != SL_RESULT_SUCCESS) { - ma_device_uninit__opensl(pDevice); - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[OpenSL] Failed to register buffer queue callback."); - return ma_result_from_OpenSL(resultSL); - } - - /* The internal format is determined by the "pcm" object. */ - ma_deconstruct_SLDataFormat_PCM__opensl(&pcm, &pDescriptorPlayback->format, &pDescriptorPlayback->channels, &pDescriptorPlayback->sampleRate, pDescriptorPlayback->channelMap, ma_countof(pDescriptorPlayback->channelMap)); - - /* Buffer. */ - pDescriptorPlayback->periodSizeInFrames = ma_calculate_buffer_size_in_frames_from_descriptor(pDescriptorPlayback, pDescriptorPlayback->sampleRate, pConfig->performanceProfile); - pDevice->opensl.currentBufferIndexPlayback = 0; - - bufferSizeInBytes = pDescriptorPlayback->periodSizeInFrames * ma_get_bytes_per_frame(pDescriptorPlayback->format, pDescriptorPlayback->channels) * pDescriptorPlayback->periodCount; - pDevice->opensl.pBufferPlayback = (ma_uint8*)ma_calloc(bufferSizeInBytes, &pDevice->pContext->allocationCallbacks); - if (pDevice->opensl.pBufferPlayback == NULL) { - ma_device_uninit__opensl(pDevice); - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[OpenSL] Failed to allocate memory for data buffer."); - return MA_OUT_OF_MEMORY; - } - MA_ZERO_MEMORY(pDevice->opensl.pBufferPlayback, bufferSizeInBytes); - } - - return MA_SUCCESS; -#else - return MA_NO_BACKEND; /* Non-Android implementations are not supported. */ -#endif -} - -static ma_result ma_device_start__opensl(ma_device* pDevice) -{ - SLresult resultSL; - size_t periodSizeInBytes; - ma_uint32 iPeriod; - - MA_ASSERT(pDevice != NULL); - - MA_ASSERT(g_maOpenSLInitCounter > 0); /* <-- If you trigger this it means you've either not initialized the context, or you've uninitialized it and then attempted to start the device. */ - if (g_maOpenSLInitCounter == 0) { - return MA_INVALID_OPERATION; - } - - if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) { - resultSL = MA_OPENSL_RECORD(pDevice->opensl.pAudioRecorder)->SetRecordState((SLRecordItf)pDevice->opensl.pAudioRecorder, SL_RECORDSTATE_RECORDING); - if (resultSL != SL_RESULT_SUCCESS) { - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[OpenSL] Failed to start internal capture device."); - return ma_result_from_OpenSL(resultSL); - } - - periodSizeInBytes = pDevice->capture.internalPeriodSizeInFrames * ma_get_bytes_per_frame(pDevice->capture.internalFormat, pDevice->capture.internalChannels); - for (iPeriod = 0; iPeriod < pDevice->capture.internalPeriods; ++iPeriod) { - resultSL = MA_OPENSL_BUFFERQUEUE(pDevice->opensl.pBufferQueueCapture)->Enqueue((SLAndroidSimpleBufferQueueItf)pDevice->opensl.pBufferQueueCapture, pDevice->opensl.pBufferCapture + (periodSizeInBytes * iPeriod), periodSizeInBytes); - if (resultSL != SL_RESULT_SUCCESS) { - MA_OPENSL_RECORD(pDevice->opensl.pAudioRecorder)->SetRecordState((SLRecordItf)pDevice->opensl.pAudioRecorder, SL_RECORDSTATE_STOPPED); - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[OpenSL] Failed to enqueue buffer for capture device."); - return ma_result_from_OpenSL(resultSL); - } - } - } - - if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) { - resultSL = MA_OPENSL_PLAY(pDevice->opensl.pAudioPlayer)->SetPlayState((SLPlayItf)pDevice->opensl.pAudioPlayer, SL_PLAYSTATE_PLAYING); - if (resultSL != SL_RESULT_SUCCESS) { - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[OpenSL] Failed to start internal playback device."); - return ma_result_from_OpenSL(resultSL); - } - - /* In playback mode (no duplex) we need to load some initial buffers. In duplex mode we need to enqueue silent buffers. */ - if (pDevice->type == ma_device_type_duplex) { - MA_ZERO_MEMORY(pDevice->opensl.pBufferPlayback, pDevice->playback.internalPeriodSizeInFrames * pDevice->playback.internalPeriods * ma_get_bytes_per_frame(pDevice->playback.internalFormat, pDevice->playback.internalChannels)); - } else { - ma_device__read_frames_from_client(pDevice, pDevice->playback.internalPeriodSizeInFrames * pDevice->playback.internalPeriods, pDevice->opensl.pBufferPlayback); - } - - periodSizeInBytes = pDevice->playback.internalPeriodSizeInFrames * ma_get_bytes_per_frame(pDevice->playback.internalFormat, pDevice->playback.internalChannels); - for (iPeriod = 0; iPeriod < pDevice->playback.internalPeriods; ++iPeriod) { - resultSL = MA_OPENSL_BUFFERQUEUE(pDevice->opensl.pBufferQueuePlayback)->Enqueue((SLAndroidSimpleBufferQueueItf)pDevice->opensl.pBufferQueuePlayback, pDevice->opensl.pBufferPlayback + (periodSizeInBytes * iPeriod), periodSizeInBytes); - if (resultSL != SL_RESULT_SUCCESS) { - MA_OPENSL_PLAY(pDevice->opensl.pAudioPlayer)->SetPlayState((SLPlayItf)pDevice->opensl.pAudioPlayer, SL_PLAYSTATE_STOPPED); - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[OpenSL] Failed to enqueue buffer for playback device."); - return ma_result_from_OpenSL(resultSL); - } - } - } - - return MA_SUCCESS; -} - -static ma_result ma_device_drain__opensl(ma_device* pDevice, ma_device_type deviceType) -{ - SLAndroidSimpleBufferQueueItf pBufferQueue; - - MA_ASSERT(deviceType == ma_device_type_capture || deviceType == ma_device_type_playback); - - if (pDevice->type == ma_device_type_capture) { - pBufferQueue = (SLAndroidSimpleBufferQueueItf)pDevice->opensl.pBufferQueueCapture; - pDevice->opensl.isDrainingCapture = MA_TRUE; - } else { - pBufferQueue = (SLAndroidSimpleBufferQueueItf)pDevice->opensl.pBufferQueuePlayback; - pDevice->opensl.isDrainingPlayback = MA_TRUE; - } - - for (;;) { - SLAndroidSimpleBufferQueueState state; - - MA_OPENSL_BUFFERQUEUE(pBufferQueue)->GetState(pBufferQueue, &state); - if (state.count == 0) { - break; - } - - ma_sleep(10); - } - - if (pDevice->type == ma_device_type_capture) { - pDevice->opensl.isDrainingCapture = MA_FALSE; - } else { - pDevice->opensl.isDrainingPlayback = MA_FALSE; - } - - return MA_SUCCESS; -} - -static ma_result ma_device_stop__opensl(ma_device* pDevice) -{ - SLresult resultSL; - - MA_ASSERT(pDevice != NULL); - - MA_ASSERT(g_maOpenSLInitCounter > 0); /* <-- If you trigger this it means you've either not initialized the context, or you've uninitialized it before stopping/uninitializing the device. */ - if (g_maOpenSLInitCounter == 0) { - return MA_INVALID_OPERATION; - } - - if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) { - ma_device_drain__opensl(pDevice, ma_device_type_capture); - - resultSL = MA_OPENSL_RECORD(pDevice->opensl.pAudioRecorder)->SetRecordState((SLRecordItf)pDevice->opensl.pAudioRecorder, SL_RECORDSTATE_STOPPED); - if (resultSL != SL_RESULT_SUCCESS) { - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[OpenSL] Failed to stop internal capture device."); - return ma_result_from_OpenSL(resultSL); - } - - MA_OPENSL_BUFFERQUEUE(pDevice->opensl.pBufferQueueCapture)->Clear((SLAndroidSimpleBufferQueueItf)pDevice->opensl.pBufferQueueCapture); - } - - if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) { - ma_device_drain__opensl(pDevice, ma_device_type_playback); - - resultSL = MA_OPENSL_PLAY(pDevice->opensl.pAudioPlayer)->SetPlayState((SLPlayItf)pDevice->opensl.pAudioPlayer, SL_PLAYSTATE_STOPPED); - if (resultSL != SL_RESULT_SUCCESS) { - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[OpenSL] Failed to stop internal playback device."); - return ma_result_from_OpenSL(resultSL); - } - - MA_OPENSL_BUFFERQUEUE(pDevice->opensl.pBufferQueuePlayback)->Clear((SLAndroidSimpleBufferQueueItf)pDevice->opensl.pBufferQueuePlayback); - } - - /* Make sure the client is aware that the device has stopped. There may be an OpenSL|ES callback for this, but I haven't found it. */ - ma_device__on_notification_stopped(pDevice); - - return MA_SUCCESS; -} - - -static ma_result ma_context_uninit__opensl(ma_context* pContext) -{ - MA_ASSERT(pContext != NULL); - MA_ASSERT(pContext->backend == ma_backend_opensl); - (void)pContext; - - /* Uninit global data. */ - ma_spinlock_lock(&g_maOpenSLSpinlock); - { - MA_ASSERT(g_maOpenSLInitCounter > 0); /* If you've triggered this, it means you have ma_context_init/uninit mismatch. Each successful call to ma_context_init() must be matched up with a call to ma_context_uninit(). */ - - g_maOpenSLInitCounter -= 1; - if (g_maOpenSLInitCounter == 0) { - (*g_maEngineObjectSL)->Destroy(g_maEngineObjectSL); - } - } - ma_spinlock_unlock(&g_maOpenSLSpinlock); - - return MA_SUCCESS; -} - -static ma_result ma_dlsym_SLInterfaceID__opensl(ma_context* pContext, const char* pName, ma_handle* pHandle) -{ - /* We need to return an error if the symbol cannot be found. This is important because there have been reports that some symbols do not exist. */ - ma_handle* p = (ma_handle*)ma_dlsym(ma_context_get_log(pContext), pContext->opensl.libOpenSLES, pName); - if (p == NULL) { - ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_INFO, "[OpenSL] Cannot find symbol %s", pName); - return MA_NO_BACKEND; - } - - *pHandle = *p; - return MA_SUCCESS; -} - -static ma_result ma_context_init_engine_nolock__opensl(ma_context* pContext) -{ - g_maOpenSLInitCounter += 1; - if (g_maOpenSLInitCounter == 1) { - SLresult resultSL; - - resultSL = ((ma_slCreateEngine_proc)pContext->opensl.slCreateEngine)(&g_maEngineObjectSL, 0, NULL, 0, NULL, NULL); - if (resultSL != SL_RESULT_SUCCESS) { - g_maOpenSLInitCounter -= 1; - return ma_result_from_OpenSL(resultSL); - } - - (*g_maEngineObjectSL)->Realize(g_maEngineObjectSL, SL_BOOLEAN_FALSE); - - resultSL = (*g_maEngineObjectSL)->GetInterface(g_maEngineObjectSL, (SLInterfaceID)pContext->opensl.SL_IID_ENGINE, &g_maEngineSL); - if (resultSL != SL_RESULT_SUCCESS) { - (*g_maEngineObjectSL)->Destroy(g_maEngineObjectSL); - g_maOpenSLInitCounter -= 1; - return ma_result_from_OpenSL(resultSL); - } - } - - return MA_SUCCESS; -} - -static ma_result ma_context_init__opensl(ma_context* pContext, const ma_context_config* pConfig, ma_backend_callbacks* pCallbacks) -{ - ma_result result; - -#if !defined(MA_NO_RUNTIME_LINKING) - size_t i; - const char* libOpenSLESNames[] = { - "libOpenSLES.so" - }; -#endif - - MA_ASSERT(pContext != NULL); - - (void)pConfig; - -#if !defined(MA_NO_RUNTIME_LINKING) - /* - Dynamically link against libOpenSLES.so. I have now had multiple reports that SL_IID_ANDROIDSIMPLEBUFFERQUEUE cannot be found. One - report was happening at compile time and another at runtime. To try working around this, I'm going to link to libOpenSLES at runtime - and extract the symbols rather than reference them directly. This should, hopefully, fix these issues as the compiler won't see any - references to the symbols and will hopefully skip the checks. - */ - for (i = 0; i < ma_countof(libOpenSLESNames); i += 1) { - pContext->opensl.libOpenSLES = ma_dlopen(ma_context_get_log(pContext), libOpenSLESNames[i]); - if (pContext->opensl.libOpenSLES != NULL) { - break; - } - } - - if (pContext->opensl.libOpenSLES == NULL) { - ma_log_post(ma_context_get_log(pContext), MA_LOG_LEVEL_INFO, "[OpenSL] Could not find libOpenSLES.so"); - return MA_NO_BACKEND; - } - - result = ma_dlsym_SLInterfaceID__opensl(pContext, "SL_IID_ENGINE", &pContext->opensl.SL_IID_ENGINE); - if (result != MA_SUCCESS) { - ma_dlclose(ma_context_get_log(pContext), pContext->opensl.libOpenSLES); - return result; - } - - result = ma_dlsym_SLInterfaceID__opensl(pContext, "SL_IID_AUDIOIODEVICECAPABILITIES", &pContext->opensl.SL_IID_AUDIOIODEVICECAPABILITIES); - if (result != MA_SUCCESS) { - ma_dlclose(ma_context_get_log(pContext), pContext->opensl.libOpenSLES); - return result; - } - - result = ma_dlsym_SLInterfaceID__opensl(pContext, "SL_IID_ANDROIDSIMPLEBUFFERQUEUE", &pContext->opensl.SL_IID_ANDROIDSIMPLEBUFFERQUEUE); - if (result != MA_SUCCESS) { - ma_dlclose(ma_context_get_log(pContext), pContext->opensl.libOpenSLES); - return result; - } - - result = ma_dlsym_SLInterfaceID__opensl(pContext, "SL_IID_RECORD", &pContext->opensl.SL_IID_RECORD); - if (result != MA_SUCCESS) { - ma_dlclose(ma_context_get_log(pContext), pContext->opensl.libOpenSLES); - return result; - } - - result = ma_dlsym_SLInterfaceID__opensl(pContext, "SL_IID_PLAY", &pContext->opensl.SL_IID_PLAY); - if (result != MA_SUCCESS) { - ma_dlclose(ma_context_get_log(pContext), pContext->opensl.libOpenSLES); - return result; - } - - result = ma_dlsym_SLInterfaceID__opensl(pContext, "SL_IID_OUTPUTMIX", &pContext->opensl.SL_IID_OUTPUTMIX); - if (result != MA_SUCCESS) { - ma_dlclose(ma_context_get_log(pContext), pContext->opensl.libOpenSLES); - return result; - } - - result = ma_dlsym_SLInterfaceID__opensl(pContext, "SL_IID_ANDROIDCONFIGURATION", &pContext->opensl.SL_IID_ANDROIDCONFIGURATION); - if (result != MA_SUCCESS) { - ma_dlclose(ma_context_get_log(pContext), pContext->opensl.libOpenSLES); - return result; - } - - pContext->opensl.slCreateEngine = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->opensl.libOpenSLES, "slCreateEngine"); - if (pContext->opensl.slCreateEngine == NULL) { - ma_dlclose(ma_context_get_log(pContext), pContext->opensl.libOpenSLES); - ma_log_post(ma_context_get_log(pContext), MA_LOG_LEVEL_INFO, "[OpenSL] Cannot find symbol slCreateEngine."); - return MA_NO_BACKEND; - } -#else - pContext->opensl.SL_IID_ENGINE = (ma_handle)SL_IID_ENGINE; - pContext->opensl.SL_IID_AUDIOIODEVICECAPABILITIES = (ma_handle)SL_IID_AUDIOIODEVICECAPABILITIES; - pContext->opensl.SL_IID_ANDROIDSIMPLEBUFFERQUEUE = (ma_handle)SL_IID_ANDROIDSIMPLEBUFFERQUEUE; - pContext->opensl.SL_IID_RECORD = (ma_handle)SL_IID_RECORD; - pContext->opensl.SL_IID_PLAY = (ma_handle)SL_IID_PLAY; - pContext->opensl.SL_IID_OUTPUTMIX = (ma_handle)SL_IID_OUTPUTMIX; - pContext->opensl.SL_IID_ANDROIDCONFIGURATION = (ma_handle)SL_IID_ANDROIDCONFIGURATION; - pContext->opensl.slCreateEngine = (ma_proc)slCreateEngine; -#endif - - - /* Initialize global data first if applicable. */ - ma_spinlock_lock(&g_maOpenSLSpinlock); - { - result = ma_context_init_engine_nolock__opensl(pContext); - } - ma_spinlock_unlock(&g_maOpenSLSpinlock); - - if (result != MA_SUCCESS) { - ma_dlclose(ma_context_get_log(pContext), pContext->opensl.libOpenSLES); - ma_log_post(ma_context_get_log(pContext), MA_LOG_LEVEL_INFO, "[OpenSL] Failed to initialize OpenSL engine."); - return result; - } - - pCallbacks->onContextInit = ma_context_init__opensl; - pCallbacks->onContextUninit = ma_context_uninit__opensl; - pCallbacks->onContextEnumerateDevices = ma_context_enumerate_devices__opensl; - pCallbacks->onContextGetDeviceInfo = ma_context_get_device_info__opensl; - pCallbacks->onDeviceInit = ma_device_init__opensl; - pCallbacks->onDeviceUninit = ma_device_uninit__opensl; - pCallbacks->onDeviceStart = ma_device_start__opensl; - pCallbacks->onDeviceStop = ma_device_stop__opensl; - pCallbacks->onDeviceRead = NULL; /* Not needed because OpenSL|ES is asynchronous. */ - pCallbacks->onDeviceWrite = NULL; /* Not needed because OpenSL|ES is asynchronous. */ - pCallbacks->onDeviceDataLoop = NULL; /* Not needed because OpenSL|ES is asynchronous. */ - - return MA_SUCCESS; -} -#endif /* OpenSL|ES */ - - -/****************************************************************************** - -Web Audio Backend - -******************************************************************************/ -#ifdef MA_HAS_WEBAUDIO -#include - -#if (__EMSCRIPTEN_major__ > 3) || (__EMSCRIPTEN_major__ == 3 && (__EMSCRIPTEN_minor__ > 1 || (__EMSCRIPTEN_minor__ == 1 && __EMSCRIPTEN_tiny__ >= 32))) - #include - #define MA_SUPPORT_AUDIO_WORKLETS - - #if (__EMSCRIPTEN_major__ > 3) || (__EMSCRIPTEN_major__ == 3 && (__EMSCRIPTEN_minor__ > 1 || (__EMSCRIPTEN_minor__ == 1 && __EMSCRIPTEN_tiny__ >= 70))) - #define MA_SUPPORT_AUDIO_WORKLETS_VARIABLE_BUFFER_SIZE - #endif -#endif - -/* -TODO: Version 0.12: Swap this logic around so that AudioWorklets are used by default. Add MA_NO_AUDIO_WORKLETS. -*/ -#if defined(MA_ENABLE_AUDIO_WORKLETS) && defined(MA_SUPPORT_AUDIO_WORKLETS) - #define MA_USE_AUDIO_WORKLETS -#endif - -/* The thread stack size must be a multiple of 16. */ -#ifndef MA_AUDIO_WORKLETS_THREAD_STACK_SIZE -#define MA_AUDIO_WORKLETS_THREAD_STACK_SIZE 131072 -#endif - -#if defined(MA_USE_AUDIO_WORKLETS) -#define MA_WEBAUDIO_LATENCY_HINT_BALANCED "balanced" -#define MA_WEBAUDIO_LATENCY_HINT_INTERACTIVE "interactive" -#define MA_WEBAUDIO_LATENCY_HINT_PLAYBACK "playback" -#endif - -static ma_bool32 ma_is_capture_supported__webaudio() -{ - return EM_ASM_INT({ - return (navigator.mediaDevices !== undefined && navigator.mediaDevices.getUserMedia !== undefined); - }, 0) != 0; /* Must pass in a dummy argument for C99 compatibility. */ -} - -#ifdef __cplusplus -extern "C" { -#endif -void* EMSCRIPTEN_KEEPALIVE ma_malloc_emscripten(size_t sz, const ma_allocation_callbacks* pAllocationCallbacks) -{ - return ma_malloc(sz, pAllocationCallbacks); -} - -void EMSCRIPTEN_KEEPALIVE ma_free_emscripten(void* p, const ma_allocation_callbacks* pAllocationCallbacks) -{ - ma_free(p, pAllocationCallbacks); -} - -void EMSCRIPTEN_KEEPALIVE ma_device_process_pcm_frames_capture__webaudio(ma_device* pDevice, int frameCount, float* pFrames) -{ - ma_device_handle_backend_data_callback(pDevice, NULL, pFrames, (ma_uint32)frameCount); -} - -void EMSCRIPTEN_KEEPALIVE ma_device_process_pcm_frames_playback__webaudio(ma_device* pDevice, int frameCount, float* pFrames) -{ - ma_device_handle_backend_data_callback(pDevice, pFrames, NULL, (ma_uint32)frameCount); -} -#ifdef __cplusplus -} -#endif - -static ma_result ma_context_enumerate_devices__webaudio(ma_context* pContext, ma_enum_devices_callback_proc callback, void* pUserData) -{ - ma_bool32 cbResult = MA_TRUE; - - MA_ASSERT(pContext != NULL); - MA_ASSERT(callback != NULL); - - /* Only supporting default devices for now. */ - - /* Playback. */ - if (cbResult) { - ma_device_info deviceInfo; - MA_ZERO_OBJECT(&deviceInfo); - ma_strncpy_s(deviceInfo.name, sizeof(deviceInfo.name), MA_DEFAULT_PLAYBACK_DEVICE_NAME, (size_t)-1); - deviceInfo.isDefault = MA_TRUE; /* Only supporting default devices. */ - cbResult = callback(pContext, ma_device_type_playback, &deviceInfo, pUserData); - } - - /* Capture. */ - if (cbResult) { - if (ma_is_capture_supported__webaudio()) { - ma_device_info deviceInfo; - MA_ZERO_OBJECT(&deviceInfo); - ma_strncpy_s(deviceInfo.name, sizeof(deviceInfo.name), MA_DEFAULT_CAPTURE_DEVICE_NAME, (size_t)-1); - deviceInfo.isDefault = MA_TRUE; /* Only supporting default devices. */ - cbResult = callback(pContext, ma_device_type_capture, &deviceInfo, pUserData); - } - } - - return MA_SUCCESS; -} - -static ma_result ma_context_get_device_info__webaudio(ma_context* pContext, ma_device_type deviceType, const ma_device_id* pDeviceID, ma_device_info* pDeviceInfo) -{ - MA_ASSERT(pContext != NULL); - - if (deviceType == ma_device_type_capture && !ma_is_capture_supported__webaudio()) { - return MA_NO_DEVICE; - } - - MA_ZERO_MEMORY(pDeviceInfo->id.webaudio, sizeof(pDeviceInfo->id.webaudio)); - - /* Only supporting default devices for now. */ - (void)pDeviceID; - if (deviceType == ma_device_type_playback) { - ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), MA_DEFAULT_PLAYBACK_DEVICE_NAME, (size_t)-1); - } else { - ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), MA_DEFAULT_CAPTURE_DEVICE_NAME, (size_t)-1); - } - - /* Only supporting default devices. */ - pDeviceInfo->isDefault = MA_TRUE; - - /* Web Audio can support any number of channels and sample rates. It only supports f32 formats, however. */ - pDeviceInfo->nativeDataFormats[0].flags = 0; - pDeviceInfo->nativeDataFormats[0].format = ma_format_unknown; - pDeviceInfo->nativeDataFormats[0].channels = 0; /* All channels are supported. */ - pDeviceInfo->nativeDataFormats[0].sampleRate = EM_ASM_INT({ - try { - var temp = new (window.AudioContext || window.webkitAudioContext)(); - var sampleRate = temp.sampleRate; - temp.close(); - return sampleRate; - } catch(e) { - return 0; - } - }, 0); /* Must pass in a dummy argument for C99 compatibility. */ - - if (pDeviceInfo->nativeDataFormats[0].sampleRate == 0) { - return MA_NO_DEVICE; - } - - pDeviceInfo->nativeDataFormatCount = 1; - - return MA_SUCCESS; -} - -static ma_result ma_device_uninit__webaudio(ma_device* pDevice) -{ - MA_ASSERT(pDevice != NULL); - - #if defined(MA_USE_AUDIO_WORKLETS) - { - EM_ASM({ - var device = window.miniaudio.get_device_by_index($0); - - if (device.streamNode !== undefined) { - device.streamNode.disconnect(); - device.streamNode = undefined; - } - - device.pDevice = undefined; - }, pDevice->webaudio.deviceIndex); - - emscripten_destroy_web_audio_node(pDevice->webaudio.audioWorklet); - emscripten_destroy_audio_context(pDevice->webaudio.audioContext); - ma_free(pDevice->webaudio.pStackBuffer, &pDevice->pContext->allocationCallbacks); - } - #else - { - EM_ASM({ - var device = window.miniaudio.get_device_by_index($0); - - /* Make sure all nodes are disconnected and marked for collection. */ - if (device.scriptNode !== undefined) { - device.scriptNode.onaudioprocess = function(e) {}; /* We want to reset the callback to ensure it doesn't get called after AudioContext.close() has returned. Shouldn't happen since we're disconnecting, but just to be safe... */ - device.scriptNode.disconnect(); - device.scriptNode = undefined; - } - - if (device.streamNode !== undefined) { - device.streamNode.disconnect(); - device.streamNode = undefined; - } - - /* - Stop the device. I think there is a chance the callback could get fired after calling this, hence why we want - to clear the callback before closing. - */ - device.webaudio.close(); - device.webaudio = undefined; - device.pDevice = undefined; - }, pDevice->webaudio.deviceIndex); - } - #endif - - /* Clean up the device on the JS side. */ - EM_ASM({ - window.miniaudio.untrack_device_by_index($0); - }, pDevice->webaudio.deviceIndex); - - ma_free(pDevice->webaudio.pIntermediaryBuffer, &pDevice->pContext->allocationCallbacks); - - return MA_SUCCESS; -} - -#if !defined(MA_USE_AUDIO_WORKLETS) -static ma_uint32 ma_calculate_period_size_in_frames_from_descriptor__webaudio(const ma_device_descriptor* pDescriptor, ma_uint32 nativeSampleRate, ma_performance_profile performanceProfile) -{ - /* - There have been reports of the default buffer size being too small on some browsers. If we're using - the default buffer size, we'll make sure the period size is bigger than our standard defaults. - */ - ma_uint32 periodSizeInFrames; - - if (nativeSampleRate == 0) { - nativeSampleRate = MA_DEFAULT_SAMPLE_RATE; - } - - if (pDescriptor->periodSizeInFrames == 0) { - if (pDescriptor->periodSizeInMilliseconds == 0) { - if (performanceProfile == ma_performance_profile_low_latency) { - periodSizeInFrames = ma_calculate_buffer_size_in_frames_from_milliseconds(33, nativeSampleRate); /* 1 frame @ 30 FPS */ - } else { - periodSizeInFrames = ma_calculate_buffer_size_in_frames_from_milliseconds(333, nativeSampleRate); - } - } else { - periodSizeInFrames = ma_calculate_buffer_size_in_frames_from_milliseconds(pDescriptor->periodSizeInMilliseconds, nativeSampleRate); - } - } else { - periodSizeInFrames = pDescriptor->periodSizeInFrames; - } - - /* The size of the buffer must be a power of 2 and between 256 and 16384. */ - if (periodSizeInFrames < 256) { - periodSizeInFrames = 256; - } else if (periodSizeInFrames > 16384) { - periodSizeInFrames = 16384; - } else { - periodSizeInFrames = ma_next_power_of_2(periodSizeInFrames); - } - - return periodSizeInFrames; -} -#endif - - -#if defined(MA_USE_AUDIO_WORKLETS) -typedef struct -{ - ma_device* pDevice; - const ma_device_config* pConfig; - ma_device_descriptor* pDescriptorPlayback; - ma_device_descriptor* pDescriptorCapture; -} ma_audio_worklet_thread_initialized_data; - -static EM_BOOL ma_audio_worklet_process_callback__webaudio(int inputCount, const AudioSampleFrame* pInputs, int outputCount, AudioSampleFrame* pOutputs, int paramCount, const AudioParamFrame* pParams, void* pUserData) -{ - ma_device* pDevice = (ma_device*)pUserData; - ma_uint32 frameCount; - - (void)paramCount; - (void)pParams; - - /* - The Emscripten documentation says that it'll always be 128 frames being passed in. Hard coding it like that feels - like a very bad idea to me. Even if it's hard coded in the backend, the API and documentation should always refer - to variables instead of a hard coded number. In any case, will follow along for the time being. - - Unfortunately the audio data is not interleaved so we'll need to convert it before we give the data to miniaudio - for further processing. - */ - if (pDevice->type == ma_device_type_playback) { - frameCount = pDevice->playback.internalPeriodSizeInFrames; - } else { - frameCount = pDevice->capture.internalPeriodSizeInFrames; - } - - if (ma_device_get_state(pDevice) != ma_device_state_started) { - /* Fill the output buffer with zero to avoid a noise sound */ - for (int i = 0; i < outputCount; i += 1) { - MA_ZERO_MEMORY(pOutputs[i].data, pOutputs[i].numberOfChannels * frameCount * sizeof(float)); - } - - return EM_TRUE; - } - - if (inputCount > 0) { - /* Input data needs to be interleaved before we hand it to the client. */ - for (ma_uint32 iChannel = 0; iChannel < pDevice->capture.internalChannels; iChannel += 1) { - for (ma_uint32 iFrame = 0; iFrame < frameCount; iFrame += 1) { - pDevice->webaudio.pIntermediaryBuffer[iFrame*pDevice->capture.internalChannels + iChannel] = pInputs[0].data[frameCount*iChannel + iFrame]; - } - } - - ma_device_process_pcm_frames_capture__webaudio(pDevice, frameCount, pDevice->webaudio.pIntermediaryBuffer); - } - - if (outputCount > 0) { - /* If it's a capture-only device, we'll need to output silence. */ - if (pDevice->type == ma_device_type_capture) { - MA_ZERO_MEMORY(pOutputs[0].data, frameCount * pDevice->playback.internalChannels * sizeof(float)); - } else { - ma_device_process_pcm_frames_playback__webaudio(pDevice, frameCount, pDevice->webaudio.pIntermediaryBuffer); - - /* We've read the data from the client. Now we need to deinterleave the buffer and output to the output buffer. */ - for (ma_uint32 iChannel = 0; iChannel < pDevice->playback.internalChannels; iChannel += 1) { - for (ma_uint32 iFrame = 0; iFrame < frameCount; iFrame += 1) { - pOutputs[0].data[frameCount*iChannel + iFrame] = pDevice->webaudio.pIntermediaryBuffer[iFrame*pDevice->playback.internalChannels + iChannel]; - } - } - } - } - - return EM_TRUE; -} - - -static void ma_audio_worklet_processor_created__webaudio(EMSCRIPTEN_WEBAUDIO_T audioContext, EM_BOOL success, void* pUserData) -{ - ma_audio_worklet_thread_initialized_data* pParameters = (ma_audio_worklet_thread_initialized_data*)pUserData; - EmscriptenAudioWorkletNodeCreateOptions audioWorkletOptions; - int channels = 0; - size_t intermediaryBufferSizeInFrames; - int sampleRate; - - if (success == EM_FALSE) { - pParameters->pDevice->webaudio.initResult = MA_ERROR; - ma_free(pParameters, &pParameters->pDevice->pContext->allocationCallbacks); - return; - } - - /* The next step is to initialize the audio worklet node. */ - MA_ZERO_OBJECT(&audioWorkletOptions); - - /* - The way channel counts work with Web Audio is confusing. As far as I can tell, there's no way to know the channel - count from MediaStreamAudioSourceNode (what we use for capture)? The only way to have control is to configure an - output channel count on the capture side. This is slightly confusing for capture mode because intuitively you - wouldn't actually connect an output to an input-only node, but this is what we'll have to do in order to have - proper control over the channel count. In the capture case, we'll have to output silence to its output node. - */ - if (pParameters->pConfig->deviceType == ma_device_type_capture) { - channels = (int)((pParameters->pDescriptorCapture->channels > 0) ? pParameters->pDescriptorCapture->channels : MA_DEFAULT_CHANNELS); - audioWorkletOptions.numberOfInputs = 1; - } else { - channels = (int)((pParameters->pDescriptorPlayback->channels > 0) ? pParameters->pDescriptorPlayback->channels : MA_DEFAULT_CHANNELS); - - if (pParameters->pConfig->deviceType == ma_device_type_duplex) { - audioWorkletOptions.numberOfInputs = 1; - } else { - audioWorkletOptions.numberOfInputs = 0; - } - } - - audioWorkletOptions.numberOfOutputs = 1; - audioWorkletOptions.outputChannelCounts = &channels; - - - /* - Now that we know the channel count to use we can allocate the intermediary buffer. The - intermediary buffer is used for interleaving and deinterleaving. - */ - #if defined(MA_SUPPORT_AUDIO_WORKLETS_VARIABLE_BUFFER_SIZE) - { - intermediaryBufferSizeInFrames = (size_t)emscripten_audio_context_quantum_size(audioContext); - } - #else - { - intermediaryBufferSizeInFrames = 128; - } - #endif - - pParameters->pDevice->webaudio.pIntermediaryBuffer = (float*)ma_malloc(intermediaryBufferSizeInFrames * (ma_uint32)channels * sizeof(float), &pParameters->pDevice->pContext->allocationCallbacks); - if (pParameters->pDevice->webaudio.pIntermediaryBuffer == NULL) { - pParameters->pDevice->webaudio.initResult = MA_OUT_OF_MEMORY; - ma_free(pParameters, &pParameters->pDevice->pContext->allocationCallbacks); - return; - } - - pParameters->pDevice->webaudio.audioWorklet = emscripten_create_wasm_audio_worklet_node(audioContext, "miniaudio", &audioWorkletOptions, &ma_audio_worklet_process_callback__webaudio, pParameters->pDevice); - - /* With the audio worklet initialized we can now attach it to the graph. */ - if (pParameters->pConfig->deviceType == ma_device_type_capture || pParameters->pConfig->deviceType == ma_device_type_duplex) { - ma_result attachmentResult = (ma_result)EM_ASM_INT({ - var getUserMediaResult = 0; - var audioWorklet = emscriptenGetAudioObject($0); - var audioContext = emscriptenGetAudioObject($1); - - navigator.mediaDevices.getUserMedia({audio:true, video:false}) - .then(function(stream) { - audioContext.streamNode = audioContext.createMediaStreamSource(stream); - audioContext.streamNode.connect(audioWorklet); - audioWorklet.connect(audioContext.destination); - getUserMediaResult = 0; /* 0 = MA_SUCCESS */ - }) - .catch(function(error) { - console.log("navigator.mediaDevices.getUserMedia Failed: " + error); - getUserMediaResult = -1; /* -1 = MA_ERROR */ - }); - - return getUserMediaResult; - }, pParameters->pDevice->webaudio.audioWorklet, audioContext); - - if (attachmentResult != MA_SUCCESS) { - ma_log_postf(ma_device_get_log(pParameters->pDevice), MA_LOG_LEVEL_ERROR, "Web Audio: Failed to connect capture node."); - emscripten_destroy_web_audio_node(pParameters->pDevice->webaudio.audioWorklet); - pParameters->pDevice->webaudio.initResult = attachmentResult; - ma_free(pParameters, &pParameters->pDevice->pContext->allocationCallbacks); - return; - } - } - - /* If it's playback only we can now attach the worklet node to the graph. This has already been done for the duplex case. */ - if (pParameters->pConfig->deviceType == ma_device_type_playback) { - ma_result attachmentResult = (ma_result)EM_ASM_INT({ - var audioWorklet = emscriptenGetAudioObject($0); - var audioContext = emscriptenGetAudioObject($1); - audioWorklet.connect(audioContext.destination); - return 0; /* 0 = MA_SUCCESS */ - }, pParameters->pDevice->webaudio.audioWorklet, audioContext); - - if (attachmentResult != MA_SUCCESS) { - ma_log_postf(ma_device_get_log(pParameters->pDevice), MA_LOG_LEVEL_ERROR, "Web Audio: Failed to connect playback node."); - pParameters->pDevice->webaudio.initResult = attachmentResult; - ma_free(pParameters, &pParameters->pDevice->pContext->allocationCallbacks); - return; - } - } - - /* We need to update the descriptors so that they reflect the internal data format. Both capture and playback should be the same. */ - sampleRate = EM_ASM_INT({ return emscriptenGetAudioObject($0).sampleRate; }, audioContext); - - if (pParameters->pDescriptorCapture != NULL) { - pParameters->pDescriptorCapture->format = ma_format_f32; - pParameters->pDescriptorCapture->channels = (ma_uint32)channels; - pParameters->pDescriptorCapture->sampleRate = (ma_uint32)sampleRate; - ma_channel_map_init_standard(ma_standard_channel_map_webaudio, pParameters->pDescriptorCapture->channelMap, ma_countof(pParameters->pDescriptorCapture->channelMap), pParameters->pDescriptorCapture->channels); - pParameters->pDescriptorCapture->periodSizeInFrames = intermediaryBufferSizeInFrames; - pParameters->pDescriptorCapture->periodCount = 1; - } - - if (pParameters->pDescriptorPlayback != NULL) { - pParameters->pDescriptorPlayback->format = ma_format_f32; - pParameters->pDescriptorPlayback->channels = (ma_uint32)channels; - pParameters->pDescriptorPlayback->sampleRate = (ma_uint32)sampleRate; - ma_channel_map_init_standard(ma_standard_channel_map_webaudio, pParameters->pDescriptorPlayback->channelMap, ma_countof(pParameters->pDescriptorPlayback->channelMap), pParameters->pDescriptorPlayback->channels); - pParameters->pDescriptorPlayback->periodSizeInFrames = intermediaryBufferSizeInFrames; - pParameters->pDescriptorPlayback->periodCount = 1; - } - - /* At this point we're done and we can return. */ - ma_log_postf(ma_device_get_log(pParameters->pDevice), MA_LOG_LEVEL_DEBUG, "AudioWorklets: Created worklet node: %d\n", pParameters->pDevice->webaudio.audioWorklet); - pParameters->pDevice->webaudio.initResult = MA_SUCCESS; - ma_free(pParameters, &pParameters->pDevice->pContext->allocationCallbacks); -} - -static void ma_audio_worklet_thread_initialized__webaudio(EMSCRIPTEN_WEBAUDIO_T audioContext, EM_BOOL success, void* pUserData) -{ - ma_audio_worklet_thread_initialized_data* pParameters = (ma_audio_worklet_thread_initialized_data*)pUserData; - WebAudioWorkletProcessorCreateOptions workletProcessorOptions; - - MA_ASSERT(pParameters != NULL); - - if (success == EM_FALSE) { - pParameters->pDevice->webaudio.initResult = MA_ERROR; - return; - } - - MA_ZERO_OBJECT(&workletProcessorOptions); - workletProcessorOptions.name = "miniaudio"; /* I'm not entirely sure what to call this. Does this need to be globally unique, or does it need only be unique for a given AudioContext? */ - - emscripten_create_wasm_audio_worklet_processor_async(audioContext, &workletProcessorOptions, ma_audio_worklet_processor_created__webaudio, pParameters); -} -#endif - -static ma_result ma_device_init__webaudio(ma_device* pDevice, const ma_device_config* pConfig, ma_device_descriptor* pDescriptorPlayback, ma_device_descriptor* pDescriptorCapture) -{ - if (pConfig->deviceType == ma_device_type_loopback) { - return MA_DEVICE_TYPE_NOT_SUPPORTED; - } - - /* No exclusive mode with Web Audio. */ - if (((pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) && pDescriptorPlayback->shareMode == ma_share_mode_exclusive) || - ((pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) && pDescriptorCapture->shareMode == ma_share_mode_exclusive)) { - return MA_SHARE_MODE_NOT_SUPPORTED; - } - - /* - With AudioWorklets we'll have just a single AudioContext. I'm not sure why I'm not doing this for ScriptProcessorNode so - it might be worthwhile to look into that as well. - */ - #if defined(MA_USE_AUDIO_WORKLETS) - { - EmscriptenWebAudioCreateAttributes audioContextAttributes; - ma_audio_worklet_thread_initialized_data* pInitParameters; - void* pStackBuffer; - - if (pConfig->performanceProfile == ma_performance_profile_conservative) { - audioContextAttributes.latencyHint = MA_WEBAUDIO_LATENCY_HINT_PLAYBACK; - } else { - audioContextAttributes.latencyHint = MA_WEBAUDIO_LATENCY_HINT_INTERACTIVE; - } - - /* - In my testing, Firefox does not seem to capture audio data properly if the sample rate is set - to anything other than 48K. This does not seem to be the case for other browsers. For this reason, - if the device type is anything other than playback, we'll leave the sample rate as-is and let the - browser pick the appropriate rate for us. - */ - if (pConfig->deviceType == ma_device_type_playback) { - audioContextAttributes.sampleRate = pDescriptorPlayback->sampleRate; - } else { - audioContextAttributes.sampleRate = 0; - } - - /* It's not clear if this can return an error. None of the tests in the Emscripten repository check for this, so neither am I for now. */ - pDevice->webaudio.audioContext = emscripten_create_audio_context(&audioContextAttributes); - - /* - With the context created we can now create the worklet. We can only have a single worklet per audio - context which means we'll need to craft this appropriately to handle duplex devices correctly. - */ - - /* - We now need to create a worker thread. This is a bit weird because we need to allocate our - own buffer for the thread's stack. The stack needs to be aligned to 16 bytes. I'm going to - allocate this on the heap to keep it simple. - */ - pStackBuffer = ma_aligned_malloc(MA_AUDIO_WORKLETS_THREAD_STACK_SIZE, 16, &pDevice->pContext->allocationCallbacks); - if (pStackBuffer == NULL) { - emscripten_destroy_audio_context(pDevice->webaudio.audioContext); - return MA_OUT_OF_MEMORY; - } - - /* Our thread initialization parameters need to be allocated on the heap so they don't go out of scope. */ - pInitParameters = (ma_audio_worklet_thread_initialized_data*)ma_malloc(sizeof(*pInitParameters), &pDevice->pContext->allocationCallbacks); - if (pInitParameters == NULL) { - ma_free(pStackBuffer, &pDevice->pContext->allocationCallbacks); - emscripten_destroy_audio_context(pDevice->webaudio.audioContext); - return MA_OUT_OF_MEMORY; - } - - pInitParameters->pDevice = pDevice; - pInitParameters->pConfig = pConfig; - pInitParameters->pDescriptorPlayback = pDescriptorPlayback; - pInitParameters->pDescriptorCapture = pDescriptorCapture; - - /* - We need to flag the device as not yet initialized so we can wait on it later. Unfortunately all of - the Emscripten WebAudio stuff is asynchronous. - */ - pDevice->webaudio.initResult = MA_BUSY; - { - emscripten_start_wasm_audio_worklet_thread_async(pDevice->webaudio.audioContext, pStackBuffer, MA_AUDIO_WORKLETS_THREAD_STACK_SIZE, ma_audio_worklet_thread_initialized__webaudio, pInitParameters); - } - while (pDevice->webaudio.initResult == MA_BUSY) { emscripten_sleep(1); } /* We must wait for initialization to complete. We're just spinning here. The emscripten_sleep() call is why we need to build with `-sASYNCIFY`. */ - - /* Initialization is now complete. Descriptors were updated when the worklet was initialized. */ - if (pDevice->webaudio.initResult != MA_SUCCESS) { - ma_free(pStackBuffer, &pDevice->pContext->allocationCallbacks); - emscripten_destroy_audio_context(pDevice->webaudio.audioContext); - return pDevice->webaudio.initResult; - } - - /* We need to add an entry to the miniaudio.devices list on the JS side so we can do some JS/C interop. */ - pDevice->webaudio.deviceIndex = EM_ASM_INT({ - return window.miniaudio.track_device({ - webaudio: emscriptenGetAudioObject($0), - state: 1, /* 1 = ma_device_state_stopped */ - pDevice: $1 - }); - }, pDevice->webaudio.audioContext, pDevice); - - return MA_SUCCESS; - } - #else - { - /* ScriptProcessorNode. This path requires us to do almost everything in JS, but we'll do as much as we can in C. */ - ma_uint32 deviceIndex; - ma_uint32 channels; - ma_uint32 sampleRate; - ma_uint32 periodSizeInFrames; - - /* The channel count will depend on the device type. If it's a capture, use its, otherwise use the playback side. */ - if (pConfig->deviceType == ma_device_type_capture) { - channels = (pDescriptorCapture->channels > 0) ? pDescriptorCapture->channels : MA_DEFAULT_CHANNELS; - } else { - channels = (pDescriptorPlayback->channels > 0) ? pDescriptorPlayback->channels : MA_DEFAULT_CHANNELS; - } - - /* - When testing in Firefox, I've seen it where capture mode fails if the sample rate is changed to anything other than it's - native rate. For this reason we're leaving the sample rate untouched for capture devices. - */ - if (pConfig->deviceType == ma_device_type_playback) { - sampleRate = pDescriptorPlayback->sampleRate; - } else { - sampleRate = 0; /* Let the browser decide when capturing. */ - } - - /* The period size needs to be a power of 2. */ - if (pConfig->deviceType == ma_device_type_capture) { - periodSizeInFrames = ma_calculate_period_size_in_frames_from_descriptor__webaudio(pDescriptorCapture, sampleRate, pConfig->performanceProfile); - } else { - periodSizeInFrames = ma_calculate_period_size_in_frames_from_descriptor__webaudio(pDescriptorPlayback, sampleRate, pConfig->performanceProfile); - } - - /* We need an intermediary buffer for doing interleaving and deinterleaving. */ - pDevice->webaudio.pIntermediaryBuffer = (float*)ma_malloc(periodSizeInFrames * channels * sizeof(float), &pDevice->pContext->allocationCallbacks); - if (pDevice->webaudio.pIntermediaryBuffer == NULL) { - return MA_OUT_OF_MEMORY; - } - - deviceIndex = EM_ASM_INT({ - var deviceType = $0; - var channels = $1; - var sampleRate = $2; - var bufferSize = $3; - var pIntermediaryBuffer = $4; - var pDevice = $5; - - if (typeof(window.miniaudio) === 'undefined') { - return -1; /* Context not initialized. */ - } - - var device = {}; - - /* First thing we need is an AudioContext. */ - var audioContextOptions = {}; - if (deviceType == window.miniaudio.device_type.playback && sampleRate != 0) { - audioContextOptions.sampleRate = sampleRate; - } - - device.webaudio = new (window.AudioContext || window.webkitAudioContext)(audioContextOptions); - device.webaudio.suspend(); /* The AudioContext must be created in a suspended state. */ - device.state = window.miniaudio.device_state.stopped; - - /* - We need to create a ScriptProcessorNode. The channel situation is the same as the AudioWorklet path in that we - need to specify an output and configure the channel count there. - */ - var channelCountIn = 0; - var channelCountOut = channels; - if (deviceType != window.miniaudio.device_type.playback) { - channelCountIn = channels; - } - - device.scriptNode = device.webaudio.createScriptProcessor(bufferSize, channelCountIn, channelCountOut); - - /* The node processing callback. */ - device.scriptNode.onaudioprocess = function(e) { - if (device.intermediaryBufferView == null || device.intermediaryBufferView.length == 0) { - device.intermediaryBufferView = new Float32Array(HEAPF32.buffer, pIntermediaryBuffer, bufferSize * channels); - } - - /* Do the capture side first. */ - if (deviceType == window.miniaudio.device_type.capture || deviceType == window.miniaudio.device_type.duplex) { - /* The data must be interleaved before being processed miniaudio. */ - for (var iChannel = 0; iChannel < channels; iChannel += 1) { - var inputBuffer = e.inputBuffer.getChannelData(iChannel); - var intermediaryBuffer = device.intermediaryBufferView; - - for (var iFrame = 0; iFrame < bufferSize; iFrame += 1) { - intermediaryBuffer[iFrame*channels + iChannel] = inputBuffer[iFrame]; - } - } - - _ma_device_process_pcm_frames_capture__webaudio(pDevice, bufferSize, pIntermediaryBuffer); - } - - if (deviceType == window.miniaudio.device_type.playback || deviceType == window.miniaudio.device_type.duplex) { - _ma_device_process_pcm_frames_playback__webaudio(pDevice, bufferSize, pIntermediaryBuffer); - - for (var iChannel = 0; iChannel < e.outputBuffer.numberOfChannels; ++iChannel) { - var outputBuffer = e.outputBuffer.getChannelData(iChannel); - var intermediaryBuffer = device.intermediaryBufferView; - - for (var iFrame = 0; iFrame < bufferSize; iFrame += 1) { - outputBuffer[iFrame] = intermediaryBuffer[iFrame*channels + iChannel]; - } - } - } else { - /* It's a capture-only device. Make sure the output is silenced. */ - for (var iChannel = 0; iChannel < e.outputBuffer.numberOfChannels; ++iChannel) { - e.outputBuffer.getChannelData(iChannel).fill(0.0); - } - } - }; - - /* Now we need to connect our node to the graph. */ - if (deviceType == window.miniaudio.device_type.capture || deviceType == window.miniaudio.device_type.duplex) { - navigator.mediaDevices.getUserMedia({audio:true, video:false}) - .then(function(stream) { - device.streamNode = device.webaudio.createMediaStreamSource(stream); - device.streamNode.connect(device.scriptNode); - device.scriptNode.connect(device.webaudio.destination); - }) - .catch(function(error) { - console.log("Failed to get user media: " + error); - }); - } - - if (deviceType == window.miniaudio.device_type.playback) { - device.scriptNode.connect(device.webaudio.destination); - } - - device.pDevice = pDevice; - - return window.miniaudio.track_device(device); - }, pConfig->deviceType, channels, sampleRate, periodSizeInFrames, pDevice->webaudio.pIntermediaryBuffer, pDevice); - - if (deviceIndex < 0) { - return MA_FAILED_TO_OPEN_BACKEND_DEVICE; - } - - pDevice->webaudio.deviceIndex = deviceIndex; - - /* Grab the sample rate from the audio context directly. */ - sampleRate = (ma_uint32)EM_ASM_INT({ return window.miniaudio.get_device_by_index($0).webaudio.sampleRate; }, deviceIndex); - - if (pDescriptorCapture != NULL) { - pDescriptorCapture->format = ma_format_f32; - pDescriptorCapture->channels = channels; - pDescriptorCapture->sampleRate = sampleRate; - ma_channel_map_init_standard(ma_standard_channel_map_webaudio, pDescriptorCapture->channelMap, ma_countof(pDescriptorCapture->channelMap), pDescriptorCapture->channels); - pDescriptorCapture->periodSizeInFrames = periodSizeInFrames; - pDescriptorCapture->periodCount = 1; - } - - if (pDescriptorPlayback != NULL) { - pDescriptorPlayback->format = ma_format_f32; - pDescriptorPlayback->channels = channels; - pDescriptorPlayback->sampleRate = sampleRate; - ma_channel_map_init_standard(ma_standard_channel_map_webaudio, pDescriptorPlayback->channelMap, ma_countof(pDescriptorPlayback->channelMap), pDescriptorPlayback->channels); - pDescriptorPlayback->periodSizeInFrames = periodSizeInFrames; - pDescriptorPlayback->periodCount = 1; - } - - return MA_SUCCESS; - } - #endif -} - -static ma_result ma_device_start__webaudio(ma_device* pDevice) -{ - MA_ASSERT(pDevice != NULL); - - EM_ASM({ - var device = window.miniaudio.get_device_by_index($0); - device.webaudio.resume(); - device.state = window.miniaudio.device_state.started; - }, pDevice->webaudio.deviceIndex); - - return MA_SUCCESS; -} - -static ma_result ma_device_stop__webaudio(ma_device* pDevice) -{ - MA_ASSERT(pDevice != NULL); - - /* - From the WebAudio API documentation for AudioContext.suspend(): - - Suspends the progression of AudioContext's currentTime, allows any current context processing blocks that are already processed to be played to the - destination, and then allows the system to release its claim on audio hardware. - - I read this to mean that "any current context processing blocks" are processed by suspend() - i.e. They they are drained. We therefore shouldn't need to - do any kind of explicit draining. - */ - EM_ASM({ - var device = window.miniaudio.get_device_by_index($0); - device.webaudio.suspend(); - device.state = window.miniaudio.device_state.stopped; - }, pDevice->webaudio.deviceIndex); - - ma_device__on_notification_stopped(pDevice); - - return MA_SUCCESS; -} - -static ma_result ma_context_uninit__webaudio(ma_context* pContext) -{ - MA_ASSERT(pContext != NULL); - MA_ASSERT(pContext->backend == ma_backend_webaudio); - - (void)pContext; /* Unused. */ - - /* Remove the global miniaudio object from window if there are no more references to it. */ - EM_ASM({ - if (typeof(window.miniaudio) !== 'undefined') { - miniaudio.unlock_event_types.map(function(event_type) { - document.removeEventListener(event_type, miniaudio.unlock, true); - }); - - window.miniaudio.referenceCount -= 1; - if (window.miniaudio.referenceCount === 0) { - delete window.miniaudio; - } - } - }); - - return MA_SUCCESS; -} - -static ma_result ma_context_init__webaudio(ma_context* pContext, const ma_context_config* pConfig, ma_backend_callbacks* pCallbacks) -{ - int resultFromJS; - - MA_ASSERT(pContext != NULL); - - (void)pConfig; /* Unused. */ - - /* Here is where our global JavaScript object is initialized. */ - resultFromJS = EM_ASM_INT({ - if (typeof window === 'undefined' || (window.AudioContext || window.webkitAudioContext) === undefined) { - return 0; /* Web Audio not supported. */ - } - - if (typeof(window.miniaudio) === 'undefined') { - window.miniaudio = { - referenceCount: 0 - }; - - /* Device types. */ - window.miniaudio.device_type = {}; - window.miniaudio.device_type.playback = $0; - window.miniaudio.device_type.capture = $1; - window.miniaudio.device_type.duplex = $2; - - /* Device states. */ - window.miniaudio.device_state = {}; - window.miniaudio.device_state.stopped = $3; - window.miniaudio.device_state.started = $4; - - /* Device cache for mapping devices to indexes for JavaScript/C interop. */ - let miniaudio = window.miniaudio; - miniaudio.devices = []; - - miniaudio.track_device = function(device) { - /* Try inserting into a free slot first. */ - for (var iDevice = 0; iDevice < miniaudio.devices.length; ++iDevice) { - if (miniaudio.devices[iDevice] == null) { - miniaudio.devices[iDevice] = device; - return iDevice; - } - } - - /* Getting here means there is no empty slots in the array so we just push to the end. */ - miniaudio.devices.push(device); - return miniaudio.devices.length - 1; - }; - - miniaudio.untrack_device_by_index = function(deviceIndex) { - /* We just set the device's slot to null. The slot will get reused in the next call to ma_track_device. */ - miniaudio.devices[deviceIndex] = null; - - /* Trim the array if possible. */ - while (miniaudio.devices.length > 0) { - if (miniaudio.devices[miniaudio.devices.length-1] == null) { - miniaudio.devices.pop(); - } else { - break; - } - } - }; - - miniaudio.untrack_device = function(device) { - for (var iDevice = 0; iDevice < miniaudio.devices.length; ++iDevice) { - if (miniaudio.devices[iDevice] == device) { - return miniaudio.untrack_device_by_index(iDevice); - } - } - }; - - miniaudio.get_device_by_index = function(deviceIndex) { - return miniaudio.devices[deviceIndex]; - }; - - miniaudio.unlock_event_types = (function(){ - return ['touchend', 'click']; - })(); - - miniaudio.unlock = function() { - for(var i = 0; i < miniaudio.devices.length; ++i) { - var device = miniaudio.devices[i]; - if (device != null && - device.webaudio != null && - device.state === miniaudio.device_state.started) { - - device.webaudio.resume().then(() => { - _ma_device__on_notification_unlocked(device.pDevice); - }, - (error) => {console.error("Failed to resume audiocontext", error); - }); - } - } - miniaudio.unlock_event_types.map(function(event_type) { - document.removeEventListener(event_type, miniaudio.unlock, true); - }); - }; - - miniaudio.unlock_event_types.map(function(event_type) { - document.addEventListener(event_type, miniaudio.unlock, true); - }); - } - - window.miniaudio.referenceCount += 1; - - return 1; - }, ma_device_type_playback, ma_device_type_capture, ma_device_type_duplex, ma_device_state_stopped, ma_device_state_started); - - if (resultFromJS != 1) { - return MA_FAILED_TO_INIT_BACKEND; - } - - pCallbacks->onContextInit = ma_context_init__webaudio; - pCallbacks->onContextUninit = ma_context_uninit__webaudio; - pCallbacks->onContextEnumerateDevices = ma_context_enumerate_devices__webaudio; - pCallbacks->onContextGetDeviceInfo = ma_context_get_device_info__webaudio; - pCallbacks->onDeviceInit = ma_device_init__webaudio; - pCallbacks->onDeviceUninit = ma_device_uninit__webaudio; - pCallbacks->onDeviceStart = ma_device_start__webaudio; - pCallbacks->onDeviceStop = ma_device_stop__webaudio; - pCallbacks->onDeviceRead = NULL; /* Not needed because WebAudio is asynchronous. */ - pCallbacks->onDeviceWrite = NULL; /* Not needed because WebAudio is asynchronous. */ - pCallbacks->onDeviceDataLoop = NULL; /* Not needed because WebAudio is asynchronous. */ - - return MA_SUCCESS; -} -#endif /* MA_HAS_WEBAUDIO */ - - - -static ma_bool32 ma__is_channel_map_valid(const ma_channel* pChannelMap, ma_uint32 channels) -{ - /* A blank channel map should be allowed, in which case it should use an appropriate default which will depend on context. */ - if (pChannelMap != NULL && pChannelMap[0] != MA_CHANNEL_NONE) { - ma_uint32 iChannel; - - if (channels == 0 || channels > MA_MAX_CHANNELS) { - return MA_FALSE; /* Channel count out of range. */ - } - - /* A channel cannot be present in the channel map more than once. */ - for (iChannel = 0; iChannel < channels; ++iChannel) { - ma_uint32 jChannel; - for (jChannel = iChannel + 1; jChannel < channels; ++jChannel) { - if (pChannelMap[iChannel] == pChannelMap[jChannel]) { - return MA_FALSE; - } - } - } - } - - return MA_TRUE; -} - - -static ma_bool32 ma_context_is_backend_asynchronous(ma_context* pContext) -{ - MA_ASSERT(pContext != NULL); - - if (pContext->callbacks.onDeviceRead == NULL && pContext->callbacks.onDeviceWrite == NULL) { - if (pContext->callbacks.onDeviceDataLoop == NULL) { - return MA_TRUE; - } else { - return MA_FALSE; - } - } else { - return MA_FALSE; - } -} - - -static ma_result ma_device__post_init_setup(ma_device* pDevice, ma_device_type deviceType) -{ - ma_result result; - - MA_ASSERT(pDevice != NULL); - - if (deviceType == ma_device_type_capture || deviceType == ma_device_type_duplex || deviceType == ma_device_type_loopback) { - if (pDevice->capture.format == ma_format_unknown) { - pDevice->capture.format = pDevice->capture.internalFormat; - } - if (pDevice->capture.channels == 0) { - pDevice->capture.channels = pDevice->capture.internalChannels; - } - if (pDevice->capture.channelMap[0] == MA_CHANNEL_NONE) { - MA_ASSERT(pDevice->capture.channels <= MA_MAX_CHANNELS); - if (pDevice->capture.internalChannels == pDevice->capture.channels) { - ma_channel_map_copy(pDevice->capture.channelMap, pDevice->capture.internalChannelMap, pDevice->capture.channels); - } else { - if (pDevice->capture.channelMixMode == ma_channel_mix_mode_simple) { - ma_channel_map_init_blank(pDevice->capture.channelMap, pDevice->capture.channels); - } else { - ma_channel_map_init_standard(ma_standard_channel_map_default, pDevice->capture.channelMap, ma_countof(pDevice->capture.channelMap), pDevice->capture.channels); - } - } - } - } - - if (deviceType == ma_device_type_playback || deviceType == ma_device_type_duplex) { - if (pDevice->playback.format == ma_format_unknown) { - pDevice->playback.format = pDevice->playback.internalFormat; - } - if (pDevice->playback.channels == 0) { - pDevice->playback.channels = pDevice->playback.internalChannels; - } - if (pDevice->playback.channelMap[0] == MA_CHANNEL_NONE) { - MA_ASSERT(pDevice->playback.channels <= MA_MAX_CHANNELS); - if (pDevice->playback.internalChannels == pDevice->playback.channels) { - ma_channel_map_copy(pDevice->playback.channelMap, pDevice->playback.internalChannelMap, pDevice->playback.channels); - } else { - if (pDevice->playback.channelMixMode == ma_channel_mix_mode_simple) { - ma_channel_map_init_blank(pDevice->playback.channelMap, pDevice->playback.channels); - } else { - ma_channel_map_init_standard(ma_standard_channel_map_default, pDevice->playback.channelMap, ma_countof(pDevice->playback.channelMap), pDevice->playback.channels); - } - } - } - } - - if (pDevice->sampleRate == 0) { - if (deviceType == ma_device_type_capture || deviceType == ma_device_type_duplex || deviceType == ma_device_type_loopback) { - pDevice->sampleRate = pDevice->capture.internalSampleRate; - } else { - pDevice->sampleRate = pDevice->playback.internalSampleRate; - } - } - - /* Data converters. */ - if (deviceType == ma_device_type_capture || deviceType == ma_device_type_duplex || deviceType == ma_device_type_loopback) { - /* Converting from internal device format to client format. */ - ma_data_converter_config converterConfig = ma_data_converter_config_init_default(); - converterConfig.formatIn = pDevice->capture.internalFormat; - converterConfig.channelsIn = pDevice->capture.internalChannels; - converterConfig.sampleRateIn = pDevice->capture.internalSampleRate; - converterConfig.pChannelMapIn = pDevice->capture.internalChannelMap; - converterConfig.formatOut = pDevice->capture.format; - converterConfig.channelsOut = pDevice->capture.channels; - converterConfig.sampleRateOut = pDevice->sampleRate; - converterConfig.pChannelMapOut = pDevice->capture.channelMap; - converterConfig.channelMixMode = pDevice->capture.channelMixMode; - converterConfig.calculateLFEFromSpatialChannels = pDevice->capture.calculateLFEFromSpatialChannels; - converterConfig.allowDynamicSampleRate = MA_FALSE; - converterConfig.resampling.algorithm = pDevice->resampling.algorithm; - converterConfig.resampling.linear.lpfOrder = pDevice->resampling.linear.lpfOrder; - converterConfig.resampling.pBackendVTable = pDevice->resampling.pBackendVTable; - converterConfig.resampling.pBackendUserData = pDevice->resampling.pBackendUserData; - - /* Make sure the old converter is uninitialized first. */ - if (ma_device_get_state(pDevice) != ma_device_state_uninitialized) { - ma_data_converter_uninit(&pDevice->capture.converter, &pDevice->pContext->allocationCallbacks); - } - - result = ma_data_converter_init(&converterConfig, &pDevice->pContext->allocationCallbacks, &pDevice->capture.converter); - if (result != MA_SUCCESS) { - return result; - } - } - - if (deviceType == ma_device_type_playback || deviceType == ma_device_type_duplex) { - /* Converting from client format to device format. */ - ma_data_converter_config converterConfig = ma_data_converter_config_init_default(); - converterConfig.formatIn = pDevice->playback.format; - converterConfig.channelsIn = pDevice->playback.channels; - converterConfig.sampleRateIn = pDevice->sampleRate; - converterConfig.pChannelMapIn = pDevice->playback.channelMap; - converterConfig.formatOut = pDevice->playback.internalFormat; - converterConfig.channelsOut = pDevice->playback.internalChannels; - converterConfig.sampleRateOut = pDevice->playback.internalSampleRate; - converterConfig.pChannelMapOut = pDevice->playback.internalChannelMap; - converterConfig.channelMixMode = pDevice->playback.channelMixMode; - converterConfig.calculateLFEFromSpatialChannels = pDevice->playback.calculateLFEFromSpatialChannels; - converterConfig.allowDynamicSampleRate = MA_FALSE; - converterConfig.resampling.algorithm = pDevice->resampling.algorithm; - converterConfig.resampling.linear.lpfOrder = pDevice->resampling.linear.lpfOrder; - converterConfig.resampling.pBackendVTable = pDevice->resampling.pBackendVTable; - converterConfig.resampling.pBackendUserData = pDevice->resampling.pBackendUserData; - - /* Make sure the old converter is uninitialized first. */ - if (ma_device_get_state(pDevice) != ma_device_state_uninitialized) { - ma_data_converter_uninit(&pDevice->playback.converter, &pDevice->pContext->allocationCallbacks); - } - - result = ma_data_converter_init(&converterConfig, &pDevice->pContext->allocationCallbacks, &pDevice->playback.converter); - if (result != MA_SUCCESS) { - return result; - } - } - - - /* - If the device is doing playback (ma_device_type_playback or ma_device_type_duplex), there's - a couple of situations where we'll need a heap allocated cache. - - The first is a duplex device for backends that use a callback for data delivery. The reason - this is needed is that the input stage needs to have a buffer to place the input data while it - waits for the playback stage, after which the miniaudio data callback will get fired. This is - not needed for backends that use a blocking API because miniaudio manages temporary buffers on - the stack to achieve this. - - The other situation is when the data converter does not have the ability to query the number - of input frames that are required in order to process a given number of output frames. When - performing data conversion, it's useful if miniaudio know exactly how many frames it needs - from the client in order to generate a given number of output frames. This way, only exactly - the number of frames are needed to be read from the client which means no cache is necessary. - On the other hand, if miniaudio doesn't know how many frames to read, it is forced to read - in fixed sized chunks and then cache any residual unused input frames, those of which will be - processed at a later stage. - */ - if (deviceType == ma_device_type_playback || deviceType == ma_device_type_duplex) { - ma_uint64 unused; - - pDevice->playback.inputCacheConsumed = 0; - pDevice->playback.inputCacheRemaining = 0; - - if (pDevice->type == ma_device_type_duplex || /* Duplex. backend may decide to use ma_device_handle_backend_data_callback() which will require this cache. */ - ma_data_converter_get_required_input_frame_count(&pDevice->playback.converter, 1, &unused) != MA_SUCCESS) /* Data conversion required input frame calculation not supported. */ - { - /* We need a heap allocated cache. We want to size this based on the period size. */ - void* pNewInputCache; - ma_uint64 newInputCacheCap; - ma_uint64 newInputCacheSizeInBytes; - - newInputCacheCap = ma_calculate_frame_count_after_resampling(pDevice->playback.internalSampleRate, pDevice->sampleRate, pDevice->playback.internalPeriodSizeInFrames); - - newInputCacheSizeInBytes = newInputCacheCap * ma_get_bytes_per_frame(pDevice->playback.format, pDevice->playback.channels); - if (newInputCacheSizeInBytes > MA_SIZE_MAX) { - ma_free(pDevice->playback.pInputCache, &pDevice->pContext->allocationCallbacks); - pDevice->playback.pInputCache = NULL; - pDevice->playback.inputCacheCap = 0; - return MA_OUT_OF_MEMORY; /* Allocation too big. Should never hit this, but makes the cast below safer for 32-bit builds. */ - } - - pNewInputCache = ma_realloc(pDevice->playback.pInputCache, (size_t)newInputCacheSizeInBytes, &pDevice->pContext->allocationCallbacks); - if (pNewInputCache == NULL) { - ma_free(pDevice->playback.pInputCache, &pDevice->pContext->allocationCallbacks); - pDevice->playback.pInputCache = NULL; - pDevice->playback.inputCacheCap = 0; - return MA_OUT_OF_MEMORY; - } - - pDevice->playback.pInputCache = pNewInputCache; - pDevice->playback.inputCacheCap = newInputCacheCap; - } else { - /* Heap allocation not required. Make sure we clear out the old cache just in case this function was called in response to a route change. */ - ma_free(pDevice->playback.pInputCache, &pDevice->pContext->allocationCallbacks); - pDevice->playback.pInputCache = NULL; - pDevice->playback.inputCacheCap = 0; - } - } - - return MA_SUCCESS; -} - -MA_API ma_result ma_device_post_init(ma_device* pDevice, ma_device_type deviceType, const ma_device_descriptor* pDescriptorPlayback, const ma_device_descriptor* pDescriptorCapture) -{ - ma_result result; - - if (pDevice == NULL) { - return MA_INVALID_ARGS; - } - - /* Capture. */ - if (deviceType == ma_device_type_capture || deviceType == ma_device_type_duplex || deviceType == ma_device_type_loopback) { - if (ma_device_descriptor_is_valid(pDescriptorCapture) == MA_FALSE) { - return MA_INVALID_ARGS; - } - - pDevice->capture.internalFormat = pDescriptorCapture->format; - pDevice->capture.internalChannels = pDescriptorCapture->channels; - pDevice->capture.internalSampleRate = pDescriptorCapture->sampleRate; - MA_COPY_MEMORY(pDevice->capture.internalChannelMap, pDescriptorCapture->channelMap, sizeof(pDescriptorCapture->channelMap)); - pDevice->capture.internalPeriodSizeInFrames = pDescriptorCapture->periodSizeInFrames; - pDevice->capture.internalPeriods = pDescriptorCapture->periodCount; - - if (pDevice->capture.internalPeriodSizeInFrames == 0) { - pDevice->capture.internalPeriodSizeInFrames = ma_calculate_buffer_size_in_frames_from_milliseconds(pDescriptorCapture->periodSizeInMilliseconds, pDescriptorCapture->sampleRate); - } - } - - /* Playback. */ - if (deviceType == ma_device_type_playback || deviceType == ma_device_type_duplex) { - if (ma_device_descriptor_is_valid(pDescriptorPlayback) == MA_FALSE) { - return MA_INVALID_ARGS; - } - - pDevice->playback.internalFormat = pDescriptorPlayback->format; - pDevice->playback.internalChannels = pDescriptorPlayback->channels; - pDevice->playback.internalSampleRate = pDescriptorPlayback->sampleRate; - MA_COPY_MEMORY(pDevice->playback.internalChannelMap, pDescriptorPlayback->channelMap, sizeof(pDescriptorPlayback->channelMap)); - pDevice->playback.internalPeriodSizeInFrames = pDescriptorPlayback->periodSizeInFrames; - pDevice->playback.internalPeriods = pDescriptorPlayback->periodCount; - - if (pDevice->playback.internalPeriodSizeInFrames == 0) { - pDevice->playback.internalPeriodSizeInFrames = ma_calculate_buffer_size_in_frames_from_milliseconds(pDescriptorPlayback->periodSizeInMilliseconds, pDescriptorPlayback->sampleRate); - } - } - - /* - The name of the device can be retrieved from device info. This may be temporary and replaced with a `ma_device_get_info(pDevice, deviceType)` instead. - For loopback devices, we need to retrieve the name of the playback device. - */ - { - ma_device_info deviceInfo; - - if (deviceType == ma_device_type_capture || deviceType == ma_device_type_duplex || deviceType == ma_device_type_loopback) { - result = ma_device_get_info(pDevice, ma_device_type_capture, &deviceInfo); - if (result == MA_SUCCESS) { - ma_strncpy_s(pDevice->capture.name, sizeof(pDevice->capture.name), deviceInfo.name, (size_t)-1); - } else { - /* We failed to retrieve the device info. Fall back to a default name. */ - if (pDescriptorCapture->pDeviceID == NULL) { - ma_strncpy_s(pDevice->capture.name, sizeof(pDevice->capture.name), MA_DEFAULT_CAPTURE_DEVICE_NAME, (size_t)-1); - } else { - ma_strncpy_s(pDevice->capture.name, sizeof(pDevice->capture.name), "Capture Device", (size_t)-1); - } - } - } - - if (deviceType == ma_device_type_playback || deviceType == ma_device_type_duplex) { - result = ma_device_get_info(pDevice, ma_device_type_playback, &deviceInfo); - if (result == MA_SUCCESS) { - ma_strncpy_s(pDevice->playback.name, sizeof(pDevice->playback.name), deviceInfo.name, (size_t)-1); - } else { - /* We failed to retrieve the device info. Fall back to a default name. */ - if (pDescriptorPlayback->pDeviceID == NULL) { - ma_strncpy_s(pDevice->playback.name, sizeof(pDevice->playback.name), MA_DEFAULT_PLAYBACK_DEVICE_NAME, (size_t)-1); - } else { - ma_strncpy_s(pDevice->playback.name, sizeof(pDevice->playback.name), "Playback Device", (size_t)-1); - } - } - } - } - - /* Update data conversion. */ - return ma_device__post_init_setup(pDevice, deviceType); /* TODO: Should probably rename ma_device__post_init_setup() to something better. */ -} - - -static ma_thread_result MA_THREADCALL ma_worker_thread(void* pData) -{ - ma_device* pDevice = (ma_device*)pData; -#ifdef MA_WIN32 - HRESULT CoInitializeResult; -#endif - - MA_ASSERT(pDevice != NULL); - -#ifdef MA_WIN32 - CoInitializeResult = ma_CoInitializeEx(pDevice->pContext, NULL, MA_COINIT_VALUE); -#endif - - /* - When the device is being initialized its initial state is set to ma_device_state_uninitialized. Before returning from - ma_device_init(), the state needs to be set to something valid. In miniaudio the device's default state immediately - after initialization is stopped, so therefore we need to mark the device as such. miniaudio will wait on the worker - thread to signal an event to know when the worker thread is ready for action. - */ - ma_device__set_state(pDevice, ma_device_state_stopped); - ma_event_signal(&pDevice->stopEvent); - - for (;;) { /* <-- This loop just keeps the thread alive. The main audio loop is inside. */ - ma_result startResult; - ma_result stopResult; /* <-- This will store the result from onDeviceStop(). If it returns an error, we don't fire the stopped notification callback. */ - - /* We wait on an event to know when something has requested that the device be started and the main loop entered. */ - ma_event_wait(&pDevice->wakeupEvent); - - /* Default result code. */ - pDevice->workResult = MA_SUCCESS; - - /* If the reason for the wake up is that we are terminating, just break from the loop. */ - if (ma_device_get_state(pDevice) == ma_device_state_uninitialized) { - break; - } - - /* - Getting to this point means the device is wanting to get started. The function that has requested that the device - be started will be waiting on an event (pDevice->startEvent) which means we need to make sure we signal the event - in both the success and error case. It's important that the state of the device is set _before_ signaling the event. - */ - MA_ASSERT(ma_device_get_state(pDevice) == ma_device_state_starting); - - /* If the device has a start callback, start it now. */ - if (pDevice->pContext->callbacks.onDeviceStart != NULL) { - startResult = pDevice->pContext->callbacks.onDeviceStart(pDevice); - } else { - startResult = MA_SUCCESS; - } - - /* - If starting was not successful we'll need to loop back to the start and wait for something - to happen (pDevice->wakeupEvent). - */ - if (startResult != MA_SUCCESS) { - pDevice->workResult = startResult; - ma_event_signal(&pDevice->startEvent); /* <-- Always signal the start event so ma_device_start() can return as it'll be waiting on it. */ - continue; - } - - /* Make sure the state is set appropriately. */ - ma_device__set_state(pDevice, ma_device_state_started); /* <-- Set this before signaling the event so that the state is always guaranteed to be good after ma_device_start() has returned. */ - ma_event_signal(&pDevice->startEvent); - - ma_device__on_notification_started(pDevice); - - if (pDevice->pContext->callbacks.onDeviceDataLoop != NULL) { - pDevice->pContext->callbacks.onDeviceDataLoop(pDevice); - } else { - /* The backend is not using a custom main loop implementation, so now fall back to the blocking read-write implementation. */ - ma_device_audio_thread__default_read_write(pDevice); - } - - /* Getting here means we have broken from the main loop which happens the application has requested that device be stopped. */ - if (pDevice->pContext->callbacks.onDeviceStop != NULL) { - stopResult = pDevice->pContext->callbacks.onDeviceStop(pDevice); - } else { - stopResult = MA_SUCCESS; /* No stop callback with the backend. Just assume successful. */ - } - - /* - After the device has stopped, make sure an event is posted. Don't post a stopped event if - stopping failed. This can happen on some backends when the underlying stream has been - stopped due to the device being physically unplugged or disabled via an OS setting. - */ - if (stopResult == MA_SUCCESS) { - ma_device__on_notification_stopped(pDevice); - } - - /* If we stopped because the device has been uninitialized, abort now. */ - if (ma_device_get_state(pDevice) == ma_device_state_uninitialized) { - break; - } - - /* A function somewhere is waiting for the device to have stopped for real so we need to signal an event to allow it to continue. */ - ma_device__set_state(pDevice, ma_device_state_stopped); - ma_event_signal(&pDevice->stopEvent); - } - -#ifdef MA_WIN32 - if (CoInitializeResult == S_OK) { - ma_CoUninitialize(pDevice->pContext); - } -#endif - - return (ma_thread_result)0; -} - - -/* Helper for determining whether or not the given device is initialized. */ -static ma_bool32 ma_device__is_initialized(ma_device* pDevice) -{ - if (pDevice == NULL) { - return MA_FALSE; - } - - return ma_device_get_state(pDevice) != ma_device_state_uninitialized; -} - - -#ifdef MA_WIN32 -static ma_result ma_context_uninit_backend_apis__win32(ma_context* pContext) -{ - /* For some reason UWP complains when CoUninitialize() is called. I'm just not going to call it on UWP. */ -#if defined(MA_WIN32_DESKTOP) || defined(MA_WIN32_GDK) - if (pContext->win32.CoInitializeResult == S_OK) { - ma_CoUninitialize(pContext); - } - - #if defined(MA_WIN32_DESKTOP) - ma_dlclose(ma_context_get_log(pContext), pContext->win32.hUser32DLL); - ma_dlclose(ma_context_get_log(pContext), pContext->win32.hAdvapi32DLL); - #endif - - ma_dlclose(ma_context_get_log(pContext), pContext->win32.hOle32DLL); -#else - (void)pContext; -#endif - - return MA_SUCCESS; -} - -static ma_result ma_context_init_backend_apis__win32(ma_context* pContext) -{ -#if defined(MA_WIN32_DESKTOP) || defined(MA_WIN32_GDK) - #if defined(MA_WIN32_DESKTOP) - /* User32.dll */ - pContext->win32.hUser32DLL = ma_dlopen(ma_context_get_log(pContext), "user32.dll"); - if (pContext->win32.hUser32DLL == NULL) { - return MA_FAILED_TO_INIT_BACKEND; - } - - pContext->win32.GetForegroundWindow = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->win32.hUser32DLL, "GetForegroundWindow"); - pContext->win32.GetDesktopWindow = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->win32.hUser32DLL, "GetDesktopWindow"); - - - /* Advapi32.dll */ - pContext->win32.hAdvapi32DLL = ma_dlopen(ma_context_get_log(pContext), "advapi32.dll"); - if (pContext->win32.hAdvapi32DLL == NULL) { - return MA_FAILED_TO_INIT_BACKEND; - } - - pContext->win32.RegOpenKeyExA = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->win32.hAdvapi32DLL, "RegOpenKeyExA"); - pContext->win32.RegCloseKey = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->win32.hAdvapi32DLL, "RegCloseKey"); - pContext->win32.RegQueryValueExA = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->win32.hAdvapi32DLL, "RegQueryValueExA"); - #endif - - /* Ole32.dll */ - pContext->win32.hOle32DLL = ma_dlopen(ma_context_get_log(pContext), "ole32.dll"); - if (pContext->win32.hOle32DLL == NULL) { - return MA_FAILED_TO_INIT_BACKEND; - } - - pContext->win32.CoInitialize = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->win32.hOle32DLL, "CoInitialize"); - pContext->win32.CoInitializeEx = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->win32.hOle32DLL, "CoInitializeEx"); - pContext->win32.CoUninitialize = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->win32.hOle32DLL, "CoUninitialize"); - pContext->win32.CoCreateInstance = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->win32.hOle32DLL, "CoCreateInstance"); - pContext->win32.CoTaskMemFree = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->win32.hOle32DLL, "CoTaskMemFree"); - pContext->win32.PropVariantClear = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->win32.hOle32DLL, "PropVariantClear"); - pContext->win32.StringFromGUID2 = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->win32.hOle32DLL, "StringFromGUID2"); -#else - (void)pContext; /* Unused. */ -#endif - - pContext->win32.CoInitializeResult = ma_CoInitializeEx(pContext, NULL, MA_COINIT_VALUE); - return MA_SUCCESS; -} -#else -static ma_result ma_context_uninit_backend_apis__nix(ma_context* pContext) -{ - (void)pContext; - - return MA_SUCCESS; -} - -static ma_result ma_context_init_backend_apis__nix(ma_context* pContext) -{ - (void)pContext; - - return MA_SUCCESS; -} -#endif - -static ma_result ma_context_init_backend_apis(ma_context* pContext) -{ - ma_result result; -#ifdef MA_WIN32 - result = ma_context_init_backend_apis__win32(pContext); -#else - result = ma_context_init_backend_apis__nix(pContext); -#endif - - return result; -} - -static ma_result ma_context_uninit_backend_apis(ma_context* pContext) -{ - ma_result result; -#ifdef MA_WIN32 - result = ma_context_uninit_backend_apis__win32(pContext); -#else - result = ma_context_uninit_backend_apis__nix(pContext); -#endif - - return result; -} - - -/* The default capacity doesn't need to be too big. */ -#ifndef MA_DEFAULT_DEVICE_JOB_QUEUE_CAPACITY -#define MA_DEFAULT_DEVICE_JOB_QUEUE_CAPACITY 32 -#endif - -MA_API ma_device_job_thread_config ma_device_job_thread_config_init(void) -{ - ma_device_job_thread_config config; - - MA_ZERO_OBJECT(&config); - config.noThread = MA_FALSE; - config.jobQueueCapacity = MA_DEFAULT_DEVICE_JOB_QUEUE_CAPACITY; - config.jobQueueFlags = 0; - - return config; -} - - -static ma_thread_result MA_THREADCALL ma_device_job_thread_entry(void* pUserData) -{ - ma_device_job_thread* pJobThread = (ma_device_job_thread*)pUserData; - MA_ASSERT(pJobThread != NULL); - - for (;;) { - ma_result result; - ma_job job; - - result = ma_device_job_thread_next(pJobThread, &job); - if (result != MA_SUCCESS) { - break; - } - - if (job.toc.breakup.code == MA_JOB_TYPE_QUIT) { - break; - } - - ma_job_process(&job); - } - - return (ma_thread_result)0; -} - -MA_API ma_result ma_device_job_thread_init(const ma_device_job_thread_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_device_job_thread* pJobThread) -{ - ma_result result; - ma_job_queue_config jobQueueConfig; - - if (pJobThread == NULL) { - return MA_INVALID_ARGS; - } - - MA_ZERO_OBJECT(pJobThread); - - if (pConfig == NULL) { - return MA_INVALID_ARGS; - } - - - /* Initialize the job queue before the thread to ensure it's in a valid state. */ - jobQueueConfig = ma_job_queue_config_init(pConfig->jobQueueFlags, pConfig->jobQueueCapacity); - - result = ma_job_queue_init(&jobQueueConfig, pAllocationCallbacks, &pJobThread->jobQueue); - if (result != MA_SUCCESS) { - return result; /* Failed to initialize job queue. */ - } - - - /* The thread needs to be initialized after the job queue to ensure the thread doesn't try to access it prematurely. */ - if (pConfig->noThread == MA_FALSE) { - result = ma_thread_create(&pJobThread->thread, ma_thread_priority_normal, 0, ma_device_job_thread_entry, pJobThread, pAllocationCallbacks); - if (result != MA_SUCCESS) { - ma_job_queue_uninit(&pJobThread->jobQueue, pAllocationCallbacks); - return result; /* Failed to create the job thread. */ - } - - pJobThread->_hasThread = MA_TRUE; - } else { - pJobThread->_hasThread = MA_FALSE; - } - - - return MA_SUCCESS; -} - -MA_API void ma_device_job_thread_uninit(ma_device_job_thread* pJobThread, const ma_allocation_callbacks* pAllocationCallbacks) -{ - if (pJobThread == NULL) { - return; - } - - /* The first thing to do is post a quit message to the job queue. If we're using a thread we'll need to wait for it. */ - { - ma_job job = ma_job_init(MA_JOB_TYPE_QUIT); - ma_device_job_thread_post(pJobThread, &job); - } - - /* Wait for the thread to terminate naturally. */ - if (pJobThread->_hasThread) { - ma_thread_wait(&pJobThread->thread); - } - - /* At this point the thread should be terminated so we can safely uninitialize the job queue. */ - ma_job_queue_uninit(&pJobThread->jobQueue, pAllocationCallbacks); -} - -MA_API ma_result ma_device_job_thread_post(ma_device_job_thread* pJobThread, const ma_job* pJob) -{ - if (pJobThread == NULL || pJob == NULL) { - return MA_INVALID_ARGS; - } - - return ma_job_queue_post(&pJobThread->jobQueue, pJob); -} - -MA_API ma_result ma_device_job_thread_next(ma_device_job_thread* pJobThread, ma_job* pJob) -{ - if (pJob == NULL) { - return MA_INVALID_ARGS; - } - - MA_ZERO_OBJECT(pJob); - - if (pJobThread == NULL) { - return MA_INVALID_ARGS; - } - - return ma_job_queue_next(&pJobThread->jobQueue, pJob); -} - - -MA_API ma_bool32 ma_device_id_equal(const ma_device_id* pA, const ma_device_id* pB) -{ - size_t i; - - if (pA == NULL || pB == NULL) { - return MA_FALSE; - } - - for (i = 0; i < sizeof(ma_device_id); i += 1) { - if (((const char*)pA)[i] != ((const char*)pB)[i]) { - return MA_FALSE; - } - } - - return MA_TRUE; -} - - - -MA_API ma_context_config ma_context_config_init(void) -{ - ma_context_config config; - MA_ZERO_OBJECT(&config); - - return config; -} - -MA_API ma_result ma_context_init(const ma_backend backends[], ma_uint32 backendCount, const ma_context_config* pConfig, ma_context* pContext) -{ - ma_result result; - ma_context_config defaultConfig; - ma_backend defaultBackends[ma_backend_null+1]; - ma_uint32 iBackend; - ma_backend* pBackendsToIterate; - ma_uint32 backendsToIterateCount; - - if (pContext == NULL) { - return MA_INVALID_ARGS; - } - - MA_ZERO_OBJECT(pContext); - - /* Always make sure the config is set first to ensure properties are available as soon as possible. */ - if (pConfig == NULL) { - defaultConfig = ma_context_config_init(); - pConfig = &defaultConfig; - } - - /* Allocation callbacks need to come first because they'll be passed around to other areas. */ - result = ma_allocation_callbacks_init_copy(&pContext->allocationCallbacks, &pConfig->allocationCallbacks); - if (result != MA_SUCCESS) { - return result; - } - - /* Get a lot set up first so we can start logging ASAP. */ - if (pConfig->pLog != NULL) { - pContext->pLog = pConfig->pLog; - } else { - result = ma_log_init(&pContext->allocationCallbacks, &pContext->log); - if (result == MA_SUCCESS) { - pContext->pLog = &pContext->log; - } else { - pContext->pLog = NULL; /* Logging is not available. */ - } - } - - pContext->threadPriority = pConfig->threadPriority; - pContext->threadStackSize = pConfig->threadStackSize; - pContext->pUserData = pConfig->pUserData; - - /* Backend APIs need to be initialized first. This is where external libraries will be loaded and linked. */ - result = ma_context_init_backend_apis(pContext); - if (result != MA_SUCCESS) { - return result; - } - - for (iBackend = 0; iBackend <= ma_backend_null; ++iBackend) { - defaultBackends[iBackend] = (ma_backend)iBackend; - } - - pBackendsToIterate = (ma_backend*)backends; - backendsToIterateCount = backendCount; - if (pBackendsToIterate == NULL) { - pBackendsToIterate = (ma_backend*)defaultBackends; - backendsToIterateCount = ma_countof(defaultBackends); - } - - MA_ASSERT(pBackendsToIterate != NULL); - - for (iBackend = 0; iBackend < backendsToIterateCount; iBackend += 1) { - ma_backend backend = pBackendsToIterate[iBackend]; - - /* Make sure all callbacks are reset so we don't accidentally drag in any from previously failed initialization attempts. */ - MA_ZERO_OBJECT(&pContext->callbacks); - - /* These backends are using the new callback system. */ - switch (backend) { - #ifdef MA_HAS_WASAPI - case ma_backend_wasapi: - { - pContext->callbacks.onContextInit = ma_context_init__wasapi; - } break; - #endif - #ifdef MA_HAS_DSOUND - case ma_backend_dsound: - { - pContext->callbacks.onContextInit = ma_context_init__dsound; - } break; - #endif - #ifdef MA_HAS_WINMM - case ma_backend_winmm: - { - pContext->callbacks.onContextInit = ma_context_init__winmm; - } break; - #endif - #ifdef MA_HAS_COREAUDIO - case ma_backend_coreaudio: - { - pContext->callbacks.onContextInit = ma_context_init__coreaudio; - } break; - #endif - #ifdef MA_HAS_SNDIO - case ma_backend_sndio: - { - pContext->callbacks.onContextInit = ma_context_init__sndio; - } break; - #endif - #ifdef MA_HAS_AUDIO4 - case ma_backend_audio4: - { - pContext->callbacks.onContextInit = ma_context_init__audio4; - } break; - #endif - #ifdef MA_HAS_OSS - case ma_backend_oss: - { - pContext->callbacks.onContextInit = ma_context_init__oss; - } break; - #endif - #ifdef MA_HAS_PULSEAUDIO - case ma_backend_pulseaudio: - { - pContext->callbacks.onContextInit = ma_context_init__pulse; - } break; - #endif - #ifdef MA_HAS_ALSA - case ma_backend_alsa: - { - pContext->callbacks.onContextInit = ma_context_init__alsa; - } break; - #endif - #ifdef MA_HAS_JACK - case ma_backend_jack: - { - pContext->callbacks.onContextInit = ma_context_init__jack; - } break; - #endif - #ifdef MA_HAS_AAUDIO - case ma_backend_aaudio: - { - if (ma_is_backend_enabled(backend)) { - pContext->callbacks.onContextInit = ma_context_init__aaudio; - } - } break; - #endif - #ifdef MA_HAS_OPENSL - case ma_backend_opensl: - { - if (ma_is_backend_enabled(backend)) { - pContext->callbacks.onContextInit = ma_context_init__opensl; - } - } break; - #endif - #ifdef MA_HAS_WEBAUDIO - case ma_backend_webaudio: - { - pContext->callbacks.onContextInit = ma_context_init__webaudio; - } break; - #endif - #ifdef MA_HAS_CUSTOM - case ma_backend_custom: - { - /* Slightly different logic for custom backends. Custom backends can optionally set all of their callbacks in the config. */ - pContext->callbacks = pConfig->custom; - } break; - #endif - #ifdef MA_HAS_NULL - case ma_backend_null: - { - pContext->callbacks.onContextInit = ma_context_init__null; - } break; - #endif - - default: break; - } - - if (pContext->callbacks.onContextInit != NULL) { - ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_DEBUG, "Attempting to initialize %s backend...\n", ma_get_backend_name(backend)); - result = pContext->callbacks.onContextInit(pContext, pConfig, &pContext->callbacks); - } else { - /* Getting here means the onContextInit callback is not set which means the backend is not enabled. Special case for the custom backend. */ - if (backend != ma_backend_custom) { - result = MA_BACKEND_NOT_ENABLED; - } else { - #if !defined(MA_HAS_CUSTOM) - result = MA_BACKEND_NOT_ENABLED; - #else - result = MA_NO_BACKEND; - #endif - } - } - - /* If this iteration was successful, return. */ - if (result == MA_SUCCESS) { - result = ma_mutex_init(&pContext->deviceEnumLock); - if (result != MA_SUCCESS) { - ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_WARNING, "Failed to initialize mutex for device enumeration. ma_context_get_devices() is not thread safe.\n"); - } - - result = ma_mutex_init(&pContext->deviceInfoLock); - if (result != MA_SUCCESS) { - ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_WARNING, "Failed to initialize mutex for device info retrieval. ma_context_get_device_info() is not thread safe.\n"); - } - - ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_DEBUG, "System Architecture:\n"); - ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_DEBUG, " Endian: %s\n", ma_is_little_endian() ? "LE" : "BE"); - ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_DEBUG, " SSE2: %s\n", ma_has_sse2() ? "YES" : "NO"); - ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_DEBUG, " AVX2: %s\n", ma_has_avx2() ? "YES" : "NO"); - ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_DEBUG, " NEON: %s\n", ma_has_neon() ? "YES" : "NO"); - - pContext->backend = backend; - return result; - } else { - if (result == MA_BACKEND_NOT_ENABLED) { - ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_DEBUG, "%s backend is disabled.\n", ma_get_backend_name(backend)); - } else { - ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_DEBUG, "Failed to initialize %s backend.\n", ma_get_backend_name(backend)); - } - } - } - - /* If we get here it means an error occurred. */ - MA_ZERO_OBJECT(pContext); /* Safety. */ - return MA_NO_BACKEND; -} - -MA_API ma_result ma_context_uninit(ma_context* pContext) -{ - if (pContext == NULL) { - return MA_INVALID_ARGS; - } - - if (pContext->callbacks.onContextUninit != NULL) { - pContext->callbacks.onContextUninit(pContext); - } - - ma_mutex_uninit(&pContext->deviceEnumLock); - ma_mutex_uninit(&pContext->deviceInfoLock); - ma_free(pContext->pDeviceInfos, &pContext->allocationCallbacks); - ma_context_uninit_backend_apis(pContext); - - if (pContext->pLog == &pContext->log) { - ma_log_uninit(&pContext->log); - } - - return MA_SUCCESS; -} - -MA_API size_t ma_context_sizeof(void) -{ - return sizeof(ma_context); -} - - -MA_API ma_log* ma_context_get_log(ma_context* pContext) -{ - if (pContext == NULL) { - return NULL; - } - - return pContext->pLog; -} - - -MA_API ma_result ma_context_enumerate_devices(ma_context* pContext, ma_enum_devices_callback_proc callback, void* pUserData) -{ - ma_result result; - - if (pContext == NULL || callback == NULL) { - return MA_INVALID_ARGS; - } - - if (pContext->callbacks.onContextEnumerateDevices == NULL) { - return MA_INVALID_OPERATION; - } - - ma_mutex_lock(&pContext->deviceEnumLock); - { - result = pContext->callbacks.onContextEnumerateDevices(pContext, callback, pUserData); - } - ma_mutex_unlock(&pContext->deviceEnumLock); - - return result; -} - - -static ma_bool32 ma_context_get_devices__enum_callback(ma_context* pContext, ma_device_type deviceType, const ma_device_info* pInfo, void* pUserData) -{ - /* - We need to insert the device info into our main internal buffer. Where it goes depends on the device type. If it's a capture device - it's just appended to the end. If it's a playback device it's inserted just before the first capture device. - */ - - /* - First make sure we have room. Since the number of devices we add to the list is usually relatively small I've decided to use a - simple fixed size increment for buffer expansion. - */ - const ma_uint32 bufferExpansionCount = 2; - const ma_uint32 totalDeviceInfoCount = pContext->playbackDeviceInfoCount + pContext->captureDeviceInfoCount; - - if (totalDeviceInfoCount >= pContext->deviceInfoCapacity) { - ma_uint32 newCapacity = pContext->deviceInfoCapacity + bufferExpansionCount; - ma_device_info* pNewInfos = (ma_device_info*)ma_realloc(pContext->pDeviceInfos, sizeof(*pContext->pDeviceInfos)*newCapacity, &pContext->allocationCallbacks); - if (pNewInfos == NULL) { - return MA_FALSE; /* Out of memory. */ - } - - pContext->pDeviceInfos = pNewInfos; - pContext->deviceInfoCapacity = newCapacity; - } - - if (deviceType == ma_device_type_playback) { - /* Playback. Insert just before the first capture device. */ - - /* The first thing to do is move all of the capture devices down a slot. */ - ma_uint32 iFirstCaptureDevice = pContext->playbackDeviceInfoCount; - size_t iCaptureDevice; - for (iCaptureDevice = totalDeviceInfoCount; iCaptureDevice > iFirstCaptureDevice; --iCaptureDevice) { - pContext->pDeviceInfos[iCaptureDevice] = pContext->pDeviceInfos[iCaptureDevice-1]; - } - - /* Now just insert where the first capture device was before moving it down a slot. */ - pContext->pDeviceInfos[iFirstCaptureDevice] = *pInfo; - pContext->playbackDeviceInfoCount += 1; - } else { - /* Capture. Insert at the end. */ - pContext->pDeviceInfos[totalDeviceInfoCount] = *pInfo; - pContext->captureDeviceInfoCount += 1; - } - - (void)pUserData; - return MA_TRUE; -} - -MA_API ma_result ma_context_get_devices(ma_context* pContext, ma_device_info** ppPlaybackDeviceInfos, ma_uint32* pPlaybackDeviceCount, ma_device_info** ppCaptureDeviceInfos, ma_uint32* pCaptureDeviceCount) -{ - ma_result result; - - /* Safety. */ - if (ppPlaybackDeviceInfos != NULL) *ppPlaybackDeviceInfos = NULL; - if (pPlaybackDeviceCount != NULL) *pPlaybackDeviceCount = 0; - if (ppCaptureDeviceInfos != NULL) *ppCaptureDeviceInfos = NULL; - if (pCaptureDeviceCount != NULL) *pCaptureDeviceCount = 0; - - if (pContext == NULL) { - return MA_INVALID_ARGS; - } - - if (pContext->callbacks.onContextEnumerateDevices == NULL) { - return MA_INVALID_OPERATION; - } - - /* Note that we don't use ma_context_enumerate_devices() here because we want to do locking at a higher level. */ - ma_mutex_lock(&pContext->deviceEnumLock); - { - /* Reset everything first. */ - pContext->playbackDeviceInfoCount = 0; - pContext->captureDeviceInfoCount = 0; - - /* Now enumerate over available devices. */ - result = pContext->callbacks.onContextEnumerateDevices(pContext, ma_context_get_devices__enum_callback, NULL); - if (result == MA_SUCCESS) { - /* Playback devices. */ - if (ppPlaybackDeviceInfos != NULL) { - *ppPlaybackDeviceInfos = pContext->pDeviceInfos; - } - if (pPlaybackDeviceCount != NULL) { - *pPlaybackDeviceCount = pContext->playbackDeviceInfoCount; - } - - /* Capture devices. */ - if (ppCaptureDeviceInfos != NULL) { - *ppCaptureDeviceInfos = pContext->pDeviceInfos; - /* Capture devices come after playback devices. */ - if (pContext->playbackDeviceInfoCount > 0) { - /* Conditional, because NULL+0 is undefined behavior. */ - *ppCaptureDeviceInfos += pContext->playbackDeviceInfoCount; - } - } - if (pCaptureDeviceCount != NULL) { - *pCaptureDeviceCount = pContext->captureDeviceInfoCount; - } - } - } - ma_mutex_unlock(&pContext->deviceEnumLock); - - return result; -} - -MA_API ma_result ma_context_get_device_info(ma_context* pContext, ma_device_type deviceType, const ma_device_id* pDeviceID, ma_device_info* pDeviceInfo) -{ - ma_result result; - ma_device_info deviceInfo; - - /* NOTE: Do not clear pDeviceInfo on entry. The reason is the pDeviceID may actually point to pDeviceInfo->id which will break things. */ - if (pContext == NULL || pDeviceInfo == NULL) { - return MA_INVALID_ARGS; - } - - MA_ZERO_OBJECT(&deviceInfo); - - /* Help the backend out by copying over the device ID if we have one. */ - if (pDeviceID != NULL) { - MA_COPY_MEMORY(&deviceInfo.id, pDeviceID, sizeof(*pDeviceID)); - } - - if (pContext->callbacks.onContextGetDeviceInfo == NULL) { - return MA_INVALID_OPERATION; - } - - ma_mutex_lock(&pContext->deviceInfoLock); - { - result = pContext->callbacks.onContextGetDeviceInfo(pContext, deviceType, pDeviceID, &deviceInfo); - } - ma_mutex_unlock(&pContext->deviceInfoLock); - - *pDeviceInfo = deviceInfo; - return result; -} - -MA_API ma_bool32 ma_context_is_loopback_supported(ma_context* pContext) -{ - if (pContext == NULL) { - return MA_FALSE; - } - - return ma_is_loopback_supported(pContext->backend); -} - - -MA_API ma_device_config ma_device_config_init(ma_device_type deviceType) -{ - ma_device_config config; - MA_ZERO_OBJECT(&config); - config.deviceType = deviceType; - config.resampling = ma_resampler_config_init(ma_format_unknown, 0, 0, 0, ma_resample_algorithm_linear); /* Format/channels/rate don't matter here. */ - - return config; -} - -MA_API ma_result ma_device_init(ma_context* pContext, const ma_device_config* pConfig, ma_device* pDevice) -{ - ma_result result; - ma_device_descriptor descriptorPlayback; - ma_device_descriptor descriptorCapture; - - /* The context can be null, in which case we self-manage it. */ - if (pContext == NULL) { - return ma_device_init_ex(NULL, 0, NULL, pConfig, pDevice); - } - - if (pDevice == NULL) { - return MA_INVALID_ARGS; - } - - MA_ZERO_OBJECT(pDevice); - - if (pConfig == NULL) { - return MA_INVALID_ARGS; - } - - /* Check that we have our callbacks defined. */ - if (pContext->callbacks.onDeviceInit == NULL) { - return MA_INVALID_OPERATION; - } - - /* Basic config validation. */ - if (pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) { - if (pConfig->capture.channels > MA_MAX_CHANNELS) { - return MA_INVALID_ARGS; - } - - if (!ma__is_channel_map_valid(pConfig->capture.pChannelMap, pConfig->capture.channels)) { - return MA_INVALID_ARGS; - } - } - - if (pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex || pConfig->deviceType == ma_device_type_loopback) { - if (pConfig->playback.channels > MA_MAX_CHANNELS) { - return MA_INVALID_ARGS; - } - - if (!ma__is_channel_map_valid(pConfig->playback.pChannelMap, pConfig->playback.channels)) { - return MA_INVALID_ARGS; - } - } - - pDevice->pContext = pContext; - - /* Set the user data and log callback ASAP to ensure it is available for the entire initialization process. */ - pDevice->pUserData = pConfig->pUserData; - pDevice->onData = pConfig->dataCallback; - pDevice->onNotification = pConfig->notificationCallback; - pDevice->onStop = pConfig->stopCallback; - - if (pConfig->playback.pDeviceID != NULL) { - MA_COPY_MEMORY(&pDevice->playback.id, pConfig->playback.pDeviceID, sizeof(pDevice->playback.id)); - pDevice->playback.pID = &pDevice->playback.id; - } else { - pDevice->playback.pID = NULL; - } - - if (pConfig->capture.pDeviceID != NULL) { - MA_COPY_MEMORY(&pDevice->capture.id, pConfig->capture.pDeviceID, sizeof(pDevice->capture.id)); - pDevice->capture.pID = &pDevice->capture.id; - } else { - pDevice->capture.pID = NULL; - } - - pDevice->noPreSilencedOutputBuffer = pConfig->noPreSilencedOutputBuffer; - pDevice->noClip = pConfig->noClip; - pDevice->noDisableDenormals = pConfig->noDisableDenormals; - pDevice->noFixedSizedCallback = pConfig->noFixedSizedCallback; - ma_atomic_float_set(&pDevice->masterVolumeFactor, 1); - - pDevice->type = pConfig->deviceType; - pDevice->sampleRate = pConfig->sampleRate; - pDevice->resampling.algorithm = pConfig->resampling.algorithm; - pDevice->resampling.linear.lpfOrder = pConfig->resampling.linear.lpfOrder; - pDevice->resampling.pBackendVTable = pConfig->resampling.pBackendVTable; - pDevice->resampling.pBackendUserData = pConfig->resampling.pBackendUserData; - - pDevice->capture.shareMode = pConfig->capture.shareMode; - pDevice->capture.format = pConfig->capture.format; - pDevice->capture.channels = pConfig->capture.channels; - ma_channel_map_copy_or_default(pDevice->capture.channelMap, ma_countof(pDevice->capture.channelMap), pConfig->capture.pChannelMap, pConfig->capture.channels); - pDevice->capture.channelMixMode = pConfig->capture.channelMixMode; - pDevice->capture.calculateLFEFromSpatialChannels = pConfig->capture.calculateLFEFromSpatialChannels; - - pDevice->playback.shareMode = pConfig->playback.shareMode; - pDevice->playback.format = pConfig->playback.format; - pDevice->playback.channels = pConfig->playback.channels; - ma_channel_map_copy_or_default(pDevice->playback.channelMap, ma_countof(pDevice->playback.channelMap), pConfig->playback.pChannelMap, pConfig->playback.channels); - pDevice->playback.channelMixMode = pConfig->playback.channelMixMode; - pDevice->playback.calculateLFEFromSpatialChannels = pConfig->playback.calculateLFEFromSpatialChannels; - - result = ma_mutex_init(&pDevice->startStopLock); - if (result != MA_SUCCESS) { - return result; - } - - /* - When the device is started, the worker thread is the one that does the actual startup of the backend device. We - use a semaphore to wait for the background thread to finish the work. The same applies for stopping the device. - - Each of these semaphores is released internally by the worker thread when the work is completed. The start - semaphore is also used to wake up the worker thread. - */ - result = ma_event_init(&pDevice->wakeupEvent); - if (result != MA_SUCCESS) { - ma_mutex_uninit(&pDevice->startStopLock); - return result; - } - - result = ma_event_init(&pDevice->startEvent); - if (result != MA_SUCCESS) { - ma_event_uninit(&pDevice->wakeupEvent); - ma_mutex_uninit(&pDevice->startStopLock); - return result; - } - - result = ma_event_init(&pDevice->stopEvent); - if (result != MA_SUCCESS) { - ma_event_uninit(&pDevice->startEvent); - ma_event_uninit(&pDevice->wakeupEvent); - ma_mutex_uninit(&pDevice->startStopLock); - return result; - } - - - MA_ZERO_OBJECT(&descriptorPlayback); - descriptorPlayback.pDeviceID = pConfig->playback.pDeviceID; - descriptorPlayback.shareMode = pConfig->playback.shareMode; - descriptorPlayback.format = pConfig->playback.format; - descriptorPlayback.channels = pConfig->playback.channels; - descriptorPlayback.sampleRate = pConfig->sampleRate; - ma_channel_map_copy_or_default(descriptorPlayback.channelMap, ma_countof(descriptorPlayback.channelMap), pConfig->playback.pChannelMap, pConfig->playback.channels); - descriptorPlayback.periodSizeInFrames = pConfig->periodSizeInFrames; - descriptorPlayback.periodSizeInMilliseconds = pConfig->periodSizeInMilliseconds; - descriptorPlayback.periodCount = pConfig->periods; - - if (descriptorPlayback.periodCount == 0) { - descriptorPlayback.periodCount = MA_DEFAULT_PERIODS; - } - - - MA_ZERO_OBJECT(&descriptorCapture); - descriptorCapture.pDeviceID = pConfig->capture.pDeviceID; - descriptorCapture.shareMode = pConfig->capture.shareMode; - descriptorCapture.format = pConfig->capture.format; - descriptorCapture.channels = pConfig->capture.channels; - descriptorCapture.sampleRate = pConfig->sampleRate; - ma_channel_map_copy_or_default(descriptorCapture.channelMap, ma_countof(descriptorCapture.channelMap), pConfig->capture.pChannelMap, pConfig->capture.channels); - descriptorCapture.periodSizeInFrames = pConfig->periodSizeInFrames; - descriptorCapture.periodSizeInMilliseconds = pConfig->periodSizeInMilliseconds; - descriptorCapture.periodCount = pConfig->periods; - - if (descriptorCapture.periodCount == 0) { - descriptorCapture.periodCount = MA_DEFAULT_PERIODS; - } - - - result = pContext->callbacks.onDeviceInit(pDevice, pConfig, &descriptorPlayback, &descriptorCapture); - if (result != MA_SUCCESS) { - ma_event_uninit(&pDevice->startEvent); - ma_event_uninit(&pDevice->wakeupEvent); - ma_mutex_uninit(&pDevice->startStopLock); - return result; - } - -#if 0 - /* - On output the descriptors will contain the *actual* data format of the device. We need this to know how to convert the data between - the requested format and the internal format. - */ - if (pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex || pConfig->deviceType == ma_device_type_loopback) { - if (!ma_device_descriptor_is_valid(&descriptorCapture)) { - ma_device_uninit(pDevice); - return MA_INVALID_ARGS; - } - - pDevice->capture.internalFormat = descriptorCapture.format; - pDevice->capture.internalChannels = descriptorCapture.channels; - pDevice->capture.internalSampleRate = descriptorCapture.sampleRate; - ma_channel_map_copy(pDevice->capture.internalChannelMap, descriptorCapture.channelMap, descriptorCapture.channels); - pDevice->capture.internalPeriodSizeInFrames = descriptorCapture.periodSizeInFrames; - pDevice->capture.internalPeriods = descriptorCapture.periodCount; - - if (pDevice->capture.internalPeriodSizeInFrames == 0) { - pDevice->capture.internalPeriodSizeInFrames = ma_calculate_buffer_size_in_frames_from_milliseconds(descriptorCapture.periodSizeInMilliseconds, descriptorCapture.sampleRate); - } - } - - if (pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) { - if (!ma_device_descriptor_is_valid(&descriptorPlayback)) { - ma_device_uninit(pDevice); - return MA_INVALID_ARGS; - } - - pDevice->playback.internalFormat = descriptorPlayback.format; - pDevice->playback.internalChannels = descriptorPlayback.channels; - pDevice->playback.internalSampleRate = descriptorPlayback.sampleRate; - ma_channel_map_copy(pDevice->playback.internalChannelMap, descriptorPlayback.channelMap, descriptorPlayback.channels); - pDevice->playback.internalPeriodSizeInFrames = descriptorPlayback.periodSizeInFrames; - pDevice->playback.internalPeriods = descriptorPlayback.periodCount; - - if (pDevice->playback.internalPeriodSizeInFrames == 0) { - pDevice->playback.internalPeriodSizeInFrames = ma_calculate_buffer_size_in_frames_from_milliseconds(descriptorPlayback.periodSizeInMilliseconds, descriptorPlayback.sampleRate); - } - } - - - /* - The name of the device can be retrieved from device info. This may be temporary and replaced with a `ma_device_get_info(pDevice, deviceType)` instead. - For loopback devices, we need to retrieve the name of the playback device. - */ - { - ma_device_info deviceInfo; - - if (pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex || pConfig->deviceType == ma_device_type_loopback) { - result = ma_device_get_info(pDevice, (pConfig->deviceType == ma_device_type_loopback) ? ma_device_type_playback : ma_device_type_capture, &deviceInfo); - if (result == MA_SUCCESS) { - ma_strncpy_s(pDevice->capture.name, sizeof(pDevice->capture.name), deviceInfo.name, (size_t)-1); - } else { - /* We failed to retrieve the device info. Fall back to a default name. */ - if (descriptorCapture.pDeviceID == NULL) { - ma_strncpy_s(pDevice->capture.name, sizeof(pDevice->capture.name), MA_DEFAULT_CAPTURE_DEVICE_NAME, (size_t)-1); - } else { - ma_strncpy_s(pDevice->capture.name, sizeof(pDevice->capture.name), "Capture Device", (size_t)-1); - } - } - } - - if (pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) { - result = ma_device_get_info(pDevice, ma_device_type_playback, &deviceInfo); - if (result == MA_SUCCESS) { - ma_strncpy_s(pDevice->playback.name, sizeof(pDevice->playback.name), deviceInfo.name, (size_t)-1); - } else { - /* We failed to retrieve the device info. Fall back to a default name. */ - if (descriptorPlayback.pDeviceID == NULL) { - ma_strncpy_s(pDevice->playback.name, sizeof(pDevice->playback.name), MA_DEFAULT_PLAYBACK_DEVICE_NAME, (size_t)-1); - } else { - ma_strncpy_s(pDevice->playback.name, sizeof(pDevice->playback.name), "Playback Device", (size_t)-1); - } - } - } - } - - - ma_device__post_init_setup(pDevice, pConfig->deviceType); -#endif - - result = ma_device_post_init(pDevice, pConfig->deviceType, &descriptorPlayback, &descriptorCapture); - if (result != MA_SUCCESS) { - ma_device_uninit(pDevice); - return result; - } - - - /* - If we're using fixed sized callbacks we'll need to make use of an intermediary buffer. Needs to - be done after post_init_setup() because we'll need access to the sample rate. - */ - if (pConfig->noFixedSizedCallback == MA_FALSE) { - /* We're using a fixed sized data callback so we'll need an intermediary buffer. */ - ma_uint32 intermediaryBufferCap = pConfig->periodSizeInFrames; - if (intermediaryBufferCap == 0) { - intermediaryBufferCap = ma_calculate_buffer_size_in_frames_from_milliseconds(pConfig->periodSizeInMilliseconds, pDevice->sampleRate); - } - - if (pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex || pConfig->deviceType == ma_device_type_loopback) { - ma_uint32 intermediaryBufferSizeInBytes; - - pDevice->capture.intermediaryBufferLen = 0; - pDevice->capture.intermediaryBufferCap = intermediaryBufferCap; - if (pDevice->capture.intermediaryBufferCap == 0) { - pDevice->capture.intermediaryBufferCap = pDevice->capture.internalPeriodSizeInFrames; - } - - intermediaryBufferSizeInBytes = pDevice->capture.intermediaryBufferCap * ma_get_bytes_per_frame(pDevice->capture.format, pDevice->capture.channels); - - pDevice->capture.pIntermediaryBuffer = ma_malloc((size_t)intermediaryBufferSizeInBytes, &pContext->allocationCallbacks); - if (pDevice->capture.pIntermediaryBuffer == NULL) { - ma_device_uninit(pDevice); - return MA_OUT_OF_MEMORY; - } - - /* Silence the buffer for safety. */ - ma_silence_pcm_frames(pDevice->capture.pIntermediaryBuffer, pDevice->capture.intermediaryBufferCap, pDevice->capture.format, pDevice->capture.channels); - pDevice->capture.intermediaryBufferLen = pDevice->capture.intermediaryBufferCap; - } - - if (pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) { - ma_uint64 intermediaryBufferSizeInBytes; - - pDevice->playback.intermediaryBufferLen = 0; - if (pConfig->deviceType == ma_device_type_duplex) { - pDevice->playback.intermediaryBufferCap = pDevice->capture.intermediaryBufferCap; /* In duplex mode, make sure the intermediary buffer is always the same size as the capture side. */ - } else { - pDevice->playback.intermediaryBufferCap = intermediaryBufferCap; - if (pDevice->playback.intermediaryBufferCap == 0) { - pDevice->playback.intermediaryBufferCap = pDevice->playback.internalPeriodSizeInFrames; - } - } - - intermediaryBufferSizeInBytes = pDevice->playback.intermediaryBufferCap * ma_get_bytes_per_frame(pDevice->playback.format, pDevice->playback.channels); - - pDevice->playback.pIntermediaryBuffer = ma_malloc((size_t)intermediaryBufferSizeInBytes, &pContext->allocationCallbacks); - if (pDevice->playback.pIntermediaryBuffer == NULL) { - ma_device_uninit(pDevice); - return MA_OUT_OF_MEMORY; - } - - /* Silence the buffer for safety. */ - ma_silence_pcm_frames(pDevice->playback.pIntermediaryBuffer, pDevice->playback.intermediaryBufferCap, pDevice->playback.format, pDevice->playback.channels); - pDevice->playback.intermediaryBufferLen = 0; - } - } else { - /* Not using a fixed sized data callback so no need for an intermediary buffer. */ - } - - - /* Some backends don't require the worker thread. */ - if (!ma_context_is_backend_asynchronous(pContext)) { - /* The worker thread. */ - result = ma_thread_create(&pDevice->thread, pContext->threadPriority, pContext->threadStackSize, ma_worker_thread, pDevice, &pContext->allocationCallbacks); - if (result != MA_SUCCESS) { - ma_device_uninit(pDevice); - return result; - } - - /* Wait for the worker thread to put the device into its stopped state for real. */ - ma_event_wait(&pDevice->stopEvent); - MA_ASSERT(ma_device_get_state(pDevice) == ma_device_state_stopped); - } else { - /* - If the backend is asynchronous and the device is duplex, we'll need an intermediary ring buffer. Note that this needs to be done - after ma_device__post_init_setup(). - */ - if (ma_context_is_backend_asynchronous(pContext)) { - if (pConfig->deviceType == ma_device_type_duplex) { - result = ma_duplex_rb_init(pDevice->capture.format, pDevice->capture.channels, pDevice->sampleRate, pDevice->capture.internalSampleRate, pDevice->capture.internalPeriodSizeInFrames, &pDevice->pContext->allocationCallbacks, &pDevice->duplexRB); - if (result != MA_SUCCESS) { - ma_device_uninit(pDevice); - return result; - } - } - } - - ma_device__set_state(pDevice, ma_device_state_stopped); - } - - /* Log device information. */ - { - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, "[%s]\n", ma_get_backend_name(pDevice->pContext->backend)); - if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex || pDevice->type == ma_device_type_loopback) { - char name[MA_MAX_DEVICE_NAME_LENGTH + 1]; - ma_device_get_name(pDevice, ma_device_type_capture, name, sizeof(name), NULL); - - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, " %s (%s)\n", name, "Capture"); - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, " Format: %s -> %s\n", ma_get_format_name(pDevice->capture.internalFormat), ma_get_format_name(pDevice->capture.format)); - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, " Channels: %d -> %d\n", pDevice->capture.internalChannels, pDevice->capture.channels); - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, " Sample Rate: %d -> %d\n", pDevice->capture.internalSampleRate, pDevice->sampleRate); - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, " Buffer Size: %d*%d (%d)\n", pDevice->capture.internalPeriodSizeInFrames, pDevice->capture.internalPeriods, (pDevice->capture.internalPeriodSizeInFrames * pDevice->capture.internalPeriods)); - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, " Conversion:\n"); - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, " Pre Format Conversion: %s\n", pDevice->capture.converter.hasPreFormatConversion ? "YES" : "NO"); - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, " Post Format Conversion: %s\n", pDevice->capture.converter.hasPostFormatConversion ? "YES" : "NO"); - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, " Channel Routing: %s\n", pDevice->capture.converter.hasChannelConverter ? "YES" : "NO"); - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, " Resampling: %s\n", pDevice->capture.converter.hasResampler ? "YES" : "NO"); - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, " Passthrough: %s\n", pDevice->capture.converter.isPassthrough ? "YES" : "NO"); - { - char channelMapStr[1024]; - ma_channel_map_to_string(pDevice->capture.internalChannelMap, pDevice->capture.internalChannels, channelMapStr, sizeof(channelMapStr)); - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, " Channel Map In: {%s}\n", channelMapStr); - - ma_channel_map_to_string(pDevice->capture.channelMap, pDevice->capture.channels, channelMapStr, sizeof(channelMapStr)); - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, " Channel Map Out: {%s}\n", channelMapStr); - } - } - if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) { - char name[MA_MAX_DEVICE_NAME_LENGTH + 1]; - ma_device_get_name(pDevice, ma_device_type_playback, name, sizeof(name), NULL); - - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, " %s (%s)\n", name, "Playback"); - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, " Format: %s -> %s\n", ma_get_format_name(pDevice->playback.format), ma_get_format_name(pDevice->playback.internalFormat)); - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, " Channels: %d -> %d\n", pDevice->playback.channels, pDevice->playback.internalChannels); - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, " Sample Rate: %d -> %d\n", pDevice->sampleRate, pDevice->playback.internalSampleRate); - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, " Buffer Size: %d*%d (%d)\n", pDevice->playback.internalPeriodSizeInFrames, pDevice->playback.internalPeriods, (pDevice->playback.internalPeriodSizeInFrames * pDevice->playback.internalPeriods)); - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, " Conversion:\n"); - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, " Pre Format Conversion: %s\n", pDevice->playback.converter.hasPreFormatConversion ? "YES" : "NO"); - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, " Post Format Conversion: %s\n", pDevice->playback.converter.hasPostFormatConversion ? "YES" : "NO"); - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, " Channel Routing: %s\n", pDevice->playback.converter.hasChannelConverter ? "YES" : "NO"); - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, " Resampling: %s\n", pDevice->playback.converter.hasResampler ? "YES" : "NO"); - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, " Passthrough: %s\n", pDevice->playback.converter.isPassthrough ? "YES" : "NO"); - { - char channelMapStr[1024]; - ma_channel_map_to_string(pDevice->playback.channelMap, pDevice->playback.channels, channelMapStr, sizeof(channelMapStr)); - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, " Channel Map In: {%s}\n", channelMapStr); - - ma_channel_map_to_string(pDevice->playback.internalChannelMap, pDevice->playback.internalChannels, channelMapStr, sizeof(channelMapStr)); - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, " Channel Map Out: {%s}\n", channelMapStr); - } - } - } - - MA_ASSERT(ma_device_get_state(pDevice) == ma_device_state_stopped); - return MA_SUCCESS; -} - -MA_API ma_result ma_device_init_ex(const ma_backend backends[], ma_uint32 backendCount, const ma_context_config* pContextConfig, const ma_device_config* pConfig, ma_device* pDevice) -{ - ma_result result; - ma_context* pContext; - ma_backend defaultBackends[ma_backend_null+1]; - ma_uint32 iBackend; - ma_backend* pBackendsToIterate; - ma_uint32 backendsToIterateCount; - ma_allocation_callbacks allocationCallbacks; - - if (pConfig == NULL) { - return MA_INVALID_ARGS; - } - - if (pContextConfig != NULL) { - result = ma_allocation_callbacks_init_copy(&allocationCallbacks, &pContextConfig->allocationCallbacks); - if (result != MA_SUCCESS) { - return result; - } - } else { - allocationCallbacks = ma_allocation_callbacks_init_default(); - } - - pContext = (ma_context*)ma_malloc(sizeof(*pContext), &allocationCallbacks); - if (pContext == NULL) { - return MA_OUT_OF_MEMORY; - } - - for (iBackend = 0; iBackend <= ma_backend_null; ++iBackend) { - defaultBackends[iBackend] = (ma_backend)iBackend; - } - - pBackendsToIterate = (ma_backend*)backends; - backendsToIterateCount = backendCount; - if (pBackendsToIterate == NULL) { - pBackendsToIterate = (ma_backend*)defaultBackends; - backendsToIterateCount = ma_countof(defaultBackends); - } - - result = MA_NO_BACKEND; - - for (iBackend = 0; iBackend < backendsToIterateCount; ++iBackend) { - /* - This is a hack for iOS. If the context config is null, there's a good chance the - `ma_device_init(NULL, &deviceConfig, pDevice);` pattern is being used. In this - case, set the session category based on the device type. - */ - #if defined(MA_APPLE_MOBILE) - ma_context_config contextConfig; - - if (pContextConfig == NULL) { - contextConfig = ma_context_config_init(); - switch (pConfig->deviceType) { - case ma_device_type_duplex: { - contextConfig.coreaudio.sessionCategory = ma_ios_session_category_play_and_record; - } break; - case ma_device_type_capture: { - contextConfig.coreaudio.sessionCategory = ma_ios_session_category_record; - } break; - case ma_device_type_playback: - default: { - contextConfig.coreaudio.sessionCategory = ma_ios_session_category_playback; - } break; - } - - pContextConfig = &contextConfig; - } - #endif - - result = ma_context_init(&pBackendsToIterate[iBackend], 1, pContextConfig, pContext); - if (result == MA_SUCCESS) { - result = ma_device_init(pContext, pConfig, pDevice); - if (result == MA_SUCCESS) { - break; /* Success. */ - } else { - ma_context_uninit(pContext); /* Failure. */ - } - } - } - - if (result != MA_SUCCESS) { - ma_free(pContext, &allocationCallbacks); - return result; - } - - pDevice->isOwnerOfContext = MA_TRUE; - return result; -} - -MA_API void ma_device_uninit(ma_device* pDevice) -{ - if (!ma_device__is_initialized(pDevice)) { - return; - } - - /* - It's possible for the miniaudio side of the device and the backend to not be in sync due to - system-level situations such as the computer being put into sleep mode and the backend not - notifying miniaudio of the fact the device has stopped. It's possible for this to result in a - deadlock due to miniaudio thinking the device is in a running state, when in fact it's not - running at all. For this reason I am no longer explicitly stopping the device. I don't think - this should affect anyone in practice since uninitializing the backend will naturally stop the - device anyway. - */ - #if 0 - { - /* Make sure the device is stopped first. The backends will probably handle this naturally, but I like to do it explicitly for my own sanity. */ - if (ma_device_is_started(pDevice)) { - ma_device_stop(pDevice); - } - } - #endif - - /* Putting the device into an uninitialized state will make the worker thread return. */ - ma_device__set_state(pDevice, ma_device_state_uninitialized); - - /* Wake up the worker thread and wait for it to properly terminate. */ - if (!ma_context_is_backend_asynchronous(pDevice->pContext)) { - ma_event_signal(&pDevice->wakeupEvent); - ma_thread_wait(&pDevice->thread); - } - - if (pDevice->pContext->callbacks.onDeviceUninit != NULL) { - pDevice->pContext->callbacks.onDeviceUninit(pDevice); - } - - - ma_event_uninit(&pDevice->stopEvent); - ma_event_uninit(&pDevice->startEvent); - ma_event_uninit(&pDevice->wakeupEvent); - ma_mutex_uninit(&pDevice->startStopLock); - - if (ma_context_is_backend_asynchronous(pDevice->pContext)) { - if (pDevice->type == ma_device_type_duplex) { - ma_duplex_rb_uninit(&pDevice->duplexRB); - } - } - - if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex || pDevice->type == ma_device_type_loopback) { - ma_data_converter_uninit(&pDevice->capture.converter, &pDevice->pContext->allocationCallbacks); - } - if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) { - ma_data_converter_uninit(&pDevice->playback.converter, &pDevice->pContext->allocationCallbacks); - } - - if (pDevice->playback.pInputCache != NULL) { - ma_free(pDevice->playback.pInputCache, &pDevice->pContext->allocationCallbacks); - } - - if (pDevice->capture.pIntermediaryBuffer != NULL) { - ma_free(pDevice->capture.pIntermediaryBuffer, &pDevice->pContext->allocationCallbacks); - } - if (pDevice->playback.pIntermediaryBuffer != NULL) { - ma_free(pDevice->playback.pIntermediaryBuffer, &pDevice->pContext->allocationCallbacks); - } - - if (pDevice->isOwnerOfContext) { - ma_allocation_callbacks allocationCallbacks = pDevice->pContext->allocationCallbacks; - - ma_context_uninit(pDevice->pContext); - ma_free(pDevice->pContext, &allocationCallbacks); - } - - MA_ZERO_OBJECT(pDevice); -} - -MA_API ma_context* ma_device_get_context(ma_device* pDevice) -{ - if (pDevice == NULL) { - return NULL; - } - - return pDevice->pContext; -} - -MA_API ma_log* ma_device_get_log(ma_device* pDevice) -{ - return ma_context_get_log(ma_device_get_context(pDevice)); -} - -MA_API ma_result ma_device_get_info(ma_device* pDevice, ma_device_type type, ma_device_info* pDeviceInfo) -{ - if (pDeviceInfo == NULL) { - return MA_INVALID_ARGS; - } - - MA_ZERO_OBJECT(pDeviceInfo); - - if (pDevice == NULL) { - return MA_INVALID_ARGS; - } - - /* If the onDeviceGetInfo() callback is set, use that. Otherwise we'll fall back to ma_context_get_device_info(). */ - if (pDevice->pContext->callbacks.onDeviceGetInfo != NULL) { - return pDevice->pContext->callbacks.onDeviceGetInfo(pDevice, type, pDeviceInfo); - } - - /* Getting here means onDeviceGetInfo is not implemented so we need to fall back to an alternative. */ - if (type == ma_device_type_playback) { - return ma_context_get_device_info(pDevice->pContext, type, pDevice->playback.pID, pDeviceInfo); - } else { - /* - Here we're getting the capture side, which is the branch we'll be entering for a loopback - device, since loopback is capturing. However, if the device is using the default device ID, - it won't get the correct information because it'll think we're asking for the default - capture device, where in fact for loopback we want the default *playback* device. We'll do - a bit of a hack here to make sure we get the correct info. - */ - if (pDevice->type == ma_device_type_loopback && pDevice->capture.pID == NULL) { - type = ma_device_type_playback; - } - - return ma_context_get_device_info(pDevice->pContext, type, pDevice->capture.pID, pDeviceInfo); - } -} - -MA_API ma_result ma_device_get_name(ma_device* pDevice, ma_device_type type, char* pName, size_t nameCap, size_t* pLengthNotIncludingNullTerminator) -{ - ma_result result; - ma_device_info deviceInfo; - - if (pLengthNotIncludingNullTerminator != NULL) { - *pLengthNotIncludingNullTerminator = 0; - } - - if (pName != NULL && nameCap > 0) { - pName[0] = '\0'; - } - - result = ma_device_get_info(pDevice, type, &deviceInfo); - if (result != MA_SUCCESS) { - return result; - } - - if (pName != NULL) { - ma_strncpy_s(pName, nameCap, deviceInfo.name, (size_t)-1); - - /* - For safety, make sure the length is based on the truncated output string rather than the - source. Otherwise the caller might assume the output buffer contains more content than it - actually does. - */ - if (pLengthNotIncludingNullTerminator != NULL) { - *pLengthNotIncludingNullTerminator = strlen(pName); - } - } else { - /* Name not specified. Just report the length of the source string. */ - if (pLengthNotIncludingNullTerminator != NULL) { - *pLengthNotIncludingNullTerminator = strlen(deviceInfo.name); - } - } - - return MA_SUCCESS; -} - -MA_API ma_result ma_device_start(ma_device* pDevice) -{ - ma_result result; - - if (pDevice == NULL) { - return MA_INVALID_ARGS; - } - - if (ma_device_get_state(pDevice) == ma_device_state_uninitialized) { - return MA_INVALID_OPERATION; /* Not initialized. */ - } - - if (ma_device_get_state(pDevice) == ma_device_state_started) { - return MA_SUCCESS; /* Already started. */ - } - - ma_mutex_lock(&pDevice->startStopLock); - { - /* - We need to check again if the device is in a started state because it's possible for one thread to have started the device - while another was waiting on the mutex. - */ - if (ma_device_get_state(pDevice) == ma_device_state_started) { - ma_mutex_unlock(&pDevice->startStopLock); - return MA_SUCCESS; /* Already started. */ - } - - /* Starting and stopping are wrapped in a mutex which means we can assert that the device is in a stopped or paused state. */ - MA_ASSERT(ma_device_get_state(pDevice) == ma_device_state_stopped); - - ma_device__set_state(pDevice, ma_device_state_starting); - - /* Asynchronous backends need to be handled differently. */ - if (ma_context_is_backend_asynchronous(pDevice->pContext)) { - if (pDevice->pContext->callbacks.onDeviceStart != NULL) { - result = pDevice->pContext->callbacks.onDeviceStart(pDevice); - } else { - result = MA_INVALID_OPERATION; - } - - if (result == MA_SUCCESS) { - ma_device__set_state(pDevice, ma_device_state_started); - ma_device__on_notification_started(pDevice); - } - } else { - /* - Synchronous backends are started by signaling an event that's being waited on in the worker thread. We first wake up the - thread and then wait for the start event. - */ - ma_event_signal(&pDevice->wakeupEvent); - - /* - Wait for the worker thread to finish starting the device. Note that the worker thread will be the one who puts the device - into the started state. Don't call ma_device__set_state() here. - */ - ma_event_wait(&pDevice->startEvent); - result = pDevice->workResult; - } - - /* We changed the state from stopped to started, so if we failed, make sure we put the state back to stopped. */ - if (result != MA_SUCCESS) { - ma_device__set_state(pDevice, ma_device_state_stopped); - } - } - ma_mutex_unlock(&pDevice->startStopLock); - - return result; -} - -MA_API ma_result ma_device_stop(ma_device* pDevice) -{ - ma_result result; - - if (pDevice == NULL) { - return MA_INVALID_ARGS; - } - - if (ma_device_get_state(pDevice) == ma_device_state_uninitialized) { - return MA_INVALID_OPERATION; /* Not initialized. */ - } - - if (ma_device_get_state(pDevice) == ma_device_state_stopped) { - return MA_SUCCESS; /* Already stopped. */ - } - - ma_mutex_lock(&pDevice->startStopLock); - { - /* - We need to check again if the device is in a stopped state because it's possible for one thread to have stopped the device - while another was waiting on the mutex. - */ - if (ma_device_get_state(pDevice) == ma_device_state_stopped) { - ma_mutex_unlock(&pDevice->startStopLock); - return MA_SUCCESS; /* Already stopped. */ - } - - /* Starting and stopping are wrapped in a mutex which means we can assert that the device is in a started or paused state. */ - MA_ASSERT(ma_device_get_state(pDevice) == ma_device_state_started); - - ma_device__set_state(pDevice, ma_device_state_stopping); - - /* Asynchronous backends need to be handled differently. */ - if (ma_context_is_backend_asynchronous(pDevice->pContext)) { - /* Asynchronous backends must have a stop operation. */ - if (pDevice->pContext->callbacks.onDeviceStop != NULL) { - result = pDevice->pContext->callbacks.onDeviceStop(pDevice); - } else { - result = MA_INVALID_OPERATION; - } - - ma_device__set_state(pDevice, ma_device_state_stopped); - } else { - /* - Synchronous backends. The stop callback is always called from the worker thread. Do not call the stop callback here. If - the backend is implementing its own audio thread loop we'll need to wake it up if required. Note that we need to make - sure the state of the device is *not* playing right now, which it shouldn't be since we set it above. This is super - important though, so I'm asserting it here as well for extra safety in case we accidentally change something later. - */ - MA_ASSERT(ma_device_get_state(pDevice) != ma_device_state_started); - - if (pDevice->pContext->callbacks.onDeviceDataLoopWakeup != NULL) { - pDevice->pContext->callbacks.onDeviceDataLoopWakeup(pDevice); - } - - /* - We need to wait for the worker thread to become available for work before returning. Note that the worker thread will be - the one who puts the device into the stopped state. Don't call ma_device__set_state() here. - */ - ma_event_wait(&pDevice->stopEvent); - result = MA_SUCCESS; - } - - /* - This is a safety measure to ensure the internal buffer has been cleared so any leftover - does not get played the next time the device starts. Ideally this should be drained by - the backend first. - */ - pDevice->playback.intermediaryBufferLen = 0; - pDevice->playback.inputCacheConsumed = 0; - pDevice->playback.inputCacheRemaining = 0; - } - ma_mutex_unlock(&pDevice->startStopLock); - - return result; -} - -MA_API ma_bool32 ma_device_is_started(const ma_device* pDevice) -{ - return ma_device_get_state(pDevice) == ma_device_state_started; -} - -MA_API ma_device_state ma_device_get_state(const ma_device* pDevice) -{ - if (pDevice == NULL) { - return ma_device_state_uninitialized; - } - - return ma_atomic_device_state_get((ma_atomic_device_state*)&pDevice->state); /* Naughty cast to get rid of a const warning. */ -} - -MA_API ma_result ma_device_set_master_volume(ma_device* pDevice, float volume) -{ - if (pDevice == NULL) { - return MA_INVALID_ARGS; - } - - if (volume < 0.0f) { - return MA_INVALID_ARGS; - } - - ma_atomic_float_set(&pDevice->masterVolumeFactor, volume); - - return MA_SUCCESS; -} - -MA_API ma_result ma_device_get_master_volume(ma_device* pDevice, float* pVolume) -{ - if (pVolume == NULL) { - return MA_INVALID_ARGS; - } - - if (pDevice == NULL) { - *pVolume = 0; - return MA_INVALID_ARGS; - } - - *pVolume = ma_atomic_float_get(&pDevice->masterVolumeFactor); - - return MA_SUCCESS; -} - -MA_API ma_result ma_device_set_master_volume_db(ma_device* pDevice, float gainDB) -{ - if (gainDB > 0) { - return MA_INVALID_ARGS; - } - - return ma_device_set_master_volume(pDevice, ma_volume_db_to_linear(gainDB)); -} - -MA_API ma_result ma_device_get_master_volume_db(ma_device* pDevice, float* pGainDB) -{ - float factor; - ma_result result; - - if (pGainDB == NULL) { - return MA_INVALID_ARGS; - } - - result = ma_device_get_master_volume(pDevice, &factor); - if (result != MA_SUCCESS) { - *pGainDB = 0; - return result; - } - - *pGainDB = ma_volume_linear_to_db(factor); - - return MA_SUCCESS; -} - - -MA_API ma_result ma_device_handle_backend_data_callback(ma_device* pDevice, void* pOutput, const void* pInput, ma_uint32 frameCount) -{ - if (pDevice == NULL) { - return MA_INVALID_ARGS; - } - - if (pOutput == NULL && pInput == NULL) { - return MA_INVALID_ARGS; - } - - /* - There is an assert deeper in the code that checks that frameCount > 0. Since this is a public facing - API we'll need to check for that here. I've had reports that AAudio can sometimes post a frame count - of 0. - */ - if (frameCount == 0) { - return MA_INVALID_ARGS; - } - - if (pDevice->type == ma_device_type_duplex) { - if (pInput != NULL) { - ma_device__handle_duplex_callback_capture(pDevice, frameCount, pInput, &pDevice->duplexRB.rb); - } - - if (pOutput != NULL) { - ma_device__handle_duplex_callback_playback(pDevice, frameCount, pOutput, &pDevice->duplexRB.rb); - } - } else { - if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_loopback) { - if (pInput == NULL) { - return MA_INVALID_ARGS; - } - - ma_device__send_frames_to_client(pDevice, frameCount, pInput); - } - - if (pDevice->type == ma_device_type_playback) { - if (pOutput == NULL) { - return MA_INVALID_ARGS; - } - - ma_device__read_frames_from_client(pDevice, frameCount, pOutput); - } - } - - return MA_SUCCESS; -} - -MA_API ma_uint32 ma_calculate_buffer_size_in_frames_from_descriptor(const ma_device_descriptor* pDescriptor, ma_uint32 nativeSampleRate, ma_performance_profile performanceProfile) -{ - if (pDescriptor == NULL) { - return 0; - } - - /* - We must have a non-0 native sample rate, but some backends don't allow retrieval of this at the - time when the size of the buffer needs to be determined. In this case we need to just take a best - guess and move on. We'll try using the sample rate in pDescriptor first. If that's not set we'll - just fall back to MA_DEFAULT_SAMPLE_RATE. - */ - if (nativeSampleRate == 0) { - nativeSampleRate = pDescriptor->sampleRate; - } - if (nativeSampleRate == 0) { - nativeSampleRate = MA_DEFAULT_SAMPLE_RATE; - } - - MA_ASSERT(nativeSampleRate != 0); - - if (pDescriptor->periodSizeInFrames == 0) { - if (pDescriptor->periodSizeInMilliseconds == 0) { - if (performanceProfile == ma_performance_profile_low_latency) { - return ma_calculate_buffer_size_in_frames_from_milliseconds(MA_DEFAULT_PERIOD_SIZE_IN_MILLISECONDS_LOW_LATENCY, nativeSampleRate); - } else { - return ma_calculate_buffer_size_in_frames_from_milliseconds(MA_DEFAULT_PERIOD_SIZE_IN_MILLISECONDS_CONSERVATIVE, nativeSampleRate); - } - } else { - return ma_calculate_buffer_size_in_frames_from_milliseconds(pDescriptor->periodSizeInMilliseconds, nativeSampleRate); - } - } else { - return pDescriptor->periodSizeInFrames; - } -} -#endif /* MA_NO_DEVICE_IO */ - - -MA_API ma_uint32 ma_calculate_buffer_size_in_milliseconds_from_frames(ma_uint32 bufferSizeInFrames, ma_uint32 sampleRate) -{ - /* Prevent a division by zero. */ - if (sampleRate == 0) { - return 0; - } - - return (bufferSizeInFrames*1000 + (sampleRate - 1)) / sampleRate; -} - -MA_API ma_uint32 ma_calculate_buffer_size_in_frames_from_milliseconds(ma_uint32 bufferSizeInMilliseconds, ma_uint32 sampleRate) -{ - /* Prevent a division by zero. */ - if (sampleRate == 0) { - return 0; - } - - return bufferSizeInMilliseconds*sampleRate / 1000; -} - -MA_API void ma_copy_pcm_frames(void* dst, const void* src, ma_uint64 frameCount, ma_format format, ma_uint32 channels) -{ - if (dst == src) { - return; /* No-op. */ - } - - ma_copy_memory_64(dst, src, frameCount * ma_get_bytes_per_frame(format, channels)); -} - -MA_API void ma_silence_pcm_frames(void* p, ma_uint64 frameCount, ma_format format, ma_uint32 channels) -{ - if (format == ma_format_u8) { - ma_uint64 sampleCount = frameCount * channels; - ma_uint64 iSample; - for (iSample = 0; iSample < sampleCount; iSample += 1) { - ((ma_uint8*)p)[iSample] = 128; - } - } else { - ma_zero_memory_64(p, frameCount * ma_get_bytes_per_frame(format, channels)); - } -} - -MA_API void* ma_offset_pcm_frames_ptr(void* p, ma_uint64 offsetInFrames, ma_format format, ma_uint32 channels) -{ - return ma_offset_ptr(p, offsetInFrames * ma_get_bytes_per_frame(format, channels)); -} - -MA_API const void* ma_offset_pcm_frames_const_ptr(const void* p, ma_uint64 offsetInFrames, ma_format format, ma_uint32 channels) -{ - return ma_offset_ptr(p, offsetInFrames * ma_get_bytes_per_frame(format, channels)); -} - - -MA_API void ma_clip_samples_u8(ma_uint8* pDst, const ma_int16* pSrc, ma_uint64 count) -{ - ma_uint64 iSample; - - MA_ASSERT(pDst != NULL); - MA_ASSERT(pSrc != NULL); - - for (iSample = 0; iSample < count; iSample += 1) { - pDst[iSample] = ma_clip_u8(pSrc[iSample]); - } -} - -MA_API void ma_clip_samples_s16(ma_int16* pDst, const ma_int32* pSrc, ma_uint64 count) -{ - ma_uint64 iSample; - - MA_ASSERT(pDst != NULL); - MA_ASSERT(pSrc != NULL); - - for (iSample = 0; iSample < count; iSample += 1) { - pDst[iSample] = ma_clip_s16(pSrc[iSample]); - } -} - -MA_API void ma_clip_samples_s24(ma_uint8* pDst, const ma_int64* pSrc, ma_uint64 count) -{ - ma_uint64 iSample; - - MA_ASSERT(pDst != NULL); - MA_ASSERT(pSrc != NULL); - - for (iSample = 0; iSample < count; iSample += 1) { - ma_int64 s = ma_clip_s24(pSrc[iSample]); - pDst[iSample*3 + 0] = (ma_uint8)((s & 0x000000FF) >> 0); - pDst[iSample*3 + 1] = (ma_uint8)((s & 0x0000FF00) >> 8); - pDst[iSample*3 + 2] = (ma_uint8)((s & 0x00FF0000) >> 16); - } -} - -MA_API void ma_clip_samples_s32(ma_int32* pDst, const ma_int64* pSrc, ma_uint64 count) -{ - ma_uint64 iSample; - - MA_ASSERT(pDst != NULL); - MA_ASSERT(pSrc != NULL); - - for (iSample = 0; iSample < count; iSample += 1) { - pDst[iSample] = ma_clip_s32(pSrc[iSample]); - } -} - -MA_API void ma_clip_samples_f32(float* pDst, const float* pSrc, ma_uint64 count) -{ - ma_uint64 iSample; - - MA_ASSERT(pDst != NULL); - MA_ASSERT(pSrc != NULL); - - for (iSample = 0; iSample < count; iSample += 1) { - pDst[iSample] = ma_clip_f32(pSrc[iSample]); - } -} - -MA_API void ma_clip_pcm_frames(void* pDst, const void* pSrc, ma_uint64 frameCount, ma_format format, ma_uint32 channels) -{ - ma_uint64 sampleCount; - - MA_ASSERT(pDst != NULL); - MA_ASSERT(pSrc != NULL); - - sampleCount = frameCount * channels; - - switch (format) { - case ma_format_u8: ma_clip_samples_u8( (ma_uint8*)pDst, (const ma_int16*)pSrc, sampleCount); break; - case ma_format_s16: ma_clip_samples_s16((ma_int16*)pDst, (const ma_int32*)pSrc, sampleCount); break; - case ma_format_s24: ma_clip_samples_s24((ma_uint8*)pDst, (const ma_int64*)pSrc, sampleCount); break; - case ma_format_s32: ma_clip_samples_s32((ma_int32*)pDst, (const ma_int64*)pSrc, sampleCount); break; - case ma_format_f32: ma_clip_samples_f32(( float*)pDst, (const float*)pSrc, sampleCount); break; - - /* Do nothing if we don't know the format. We're including these here to silence a compiler warning about enums not being handled by the switch. */ - case ma_format_unknown: - case ma_format_count: - break; - } -} - - -MA_API void ma_copy_and_apply_volume_factor_u8(ma_uint8* pSamplesOut, const ma_uint8* pSamplesIn, ma_uint64 sampleCount, float factor) -{ - ma_uint64 iSample; - - if (pSamplesOut == NULL || pSamplesIn == NULL) { - return; - } - - for (iSample = 0; iSample < sampleCount; iSample += 1) { - pSamplesOut[iSample] = (ma_uint8)(pSamplesIn[iSample] * factor); - } -} - -MA_API void ma_copy_and_apply_volume_factor_s16(ma_int16* pSamplesOut, const ma_int16* pSamplesIn, ma_uint64 sampleCount, float factor) -{ - ma_uint64 iSample; - - if (pSamplesOut == NULL || pSamplesIn == NULL) { - return; - } - - for (iSample = 0; iSample < sampleCount; iSample += 1) { - pSamplesOut[iSample] = (ma_int16)(pSamplesIn[iSample] * factor); - } -} - -MA_API void ma_copy_and_apply_volume_factor_s24(void* pSamplesOut, const void* pSamplesIn, ma_uint64 sampleCount, float factor) -{ - ma_uint64 iSample; - ma_uint8* pSamplesOut8; - ma_uint8* pSamplesIn8; - - if (pSamplesOut == NULL || pSamplesIn == NULL) { - return; - } - - pSamplesOut8 = (ma_uint8*)pSamplesOut; - pSamplesIn8 = (ma_uint8*)pSamplesIn; - - for (iSample = 0; iSample < sampleCount; iSample += 1) { - ma_int32 sampleS32; - - sampleS32 = (ma_int32)(((ma_uint32)(pSamplesIn8[iSample*3+0]) << 8) | ((ma_uint32)(pSamplesIn8[iSample*3+1]) << 16) | ((ma_uint32)(pSamplesIn8[iSample*3+2])) << 24); - sampleS32 = (ma_int32)(sampleS32 * factor); - - pSamplesOut8[iSample*3+0] = (ma_uint8)(((ma_uint32)sampleS32 & 0x0000FF00) >> 8); - pSamplesOut8[iSample*3+1] = (ma_uint8)(((ma_uint32)sampleS32 & 0x00FF0000) >> 16); - pSamplesOut8[iSample*3+2] = (ma_uint8)(((ma_uint32)sampleS32 & 0xFF000000) >> 24); - } -} - -MA_API void ma_copy_and_apply_volume_factor_s32(ma_int32* pSamplesOut, const ma_int32* pSamplesIn, ma_uint64 sampleCount, float factor) -{ - ma_uint64 iSample; - - if (pSamplesOut == NULL || pSamplesIn == NULL) { - return; - } - - for (iSample = 0; iSample < sampleCount; iSample += 1) { - pSamplesOut[iSample] = (ma_int32)(pSamplesIn[iSample] * factor); - } -} - -MA_API void ma_copy_and_apply_volume_factor_f32(float* pSamplesOut, const float* pSamplesIn, ma_uint64 sampleCount, float factor) -{ - ma_uint64 iSample; - - if (pSamplesOut == NULL || pSamplesIn == NULL) { - return; - } - - if (factor == 1) { - if (pSamplesOut == pSamplesIn) { - /* In place. No-op. */ - } else { - /* Just a copy. */ - for (iSample = 0; iSample < sampleCount; iSample += 1) { - pSamplesOut[iSample] = pSamplesIn[iSample]; - } - } - } else { - for (iSample = 0; iSample < sampleCount; iSample += 1) { - pSamplesOut[iSample] = pSamplesIn[iSample] * factor; - } - } -} - -MA_API void ma_apply_volume_factor_u8(ma_uint8* pSamples, ma_uint64 sampleCount, float factor) -{ - ma_copy_and_apply_volume_factor_u8(pSamples, pSamples, sampleCount, factor); -} - -MA_API void ma_apply_volume_factor_s16(ma_int16* pSamples, ma_uint64 sampleCount, float factor) -{ - ma_copy_and_apply_volume_factor_s16(pSamples, pSamples, sampleCount, factor); -} - -MA_API void ma_apply_volume_factor_s24(void* pSamples, ma_uint64 sampleCount, float factor) -{ - ma_copy_and_apply_volume_factor_s24(pSamples, pSamples, sampleCount, factor); -} - -MA_API void ma_apply_volume_factor_s32(ma_int32* pSamples, ma_uint64 sampleCount, float factor) -{ - ma_copy_and_apply_volume_factor_s32(pSamples, pSamples, sampleCount, factor); -} - -MA_API void ma_apply_volume_factor_f32(float* pSamples, ma_uint64 sampleCount, float factor) -{ - ma_copy_and_apply_volume_factor_f32(pSamples, pSamples, sampleCount, factor); -} - -MA_API void ma_copy_and_apply_volume_factor_pcm_frames_u8(ma_uint8* pFramesOut, const ma_uint8* pFramesIn, ma_uint64 frameCount, ma_uint32 channels, float factor) -{ - ma_copy_and_apply_volume_factor_u8(pFramesOut, pFramesIn, frameCount*channels, factor); -} - -MA_API void ma_copy_and_apply_volume_factor_pcm_frames_s16(ma_int16* pFramesOut, const ma_int16* pFramesIn, ma_uint64 frameCount, ma_uint32 channels, float factor) -{ - ma_copy_and_apply_volume_factor_s16(pFramesOut, pFramesIn, frameCount*channels, factor); -} - -MA_API void ma_copy_and_apply_volume_factor_pcm_frames_s24(void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount, ma_uint32 channels, float factor) -{ - ma_copy_and_apply_volume_factor_s24(pFramesOut, pFramesIn, frameCount*channels, factor); -} - -MA_API void ma_copy_and_apply_volume_factor_pcm_frames_s32(ma_int32* pFramesOut, const ma_int32* pFramesIn, ma_uint64 frameCount, ma_uint32 channels, float factor) -{ - ma_copy_and_apply_volume_factor_s32(pFramesOut, pFramesIn, frameCount*channels, factor); -} - -MA_API void ma_copy_and_apply_volume_factor_pcm_frames_f32(float* pFramesOut, const float* pFramesIn, ma_uint64 frameCount, ma_uint32 channels, float factor) -{ - ma_copy_and_apply_volume_factor_f32(pFramesOut, pFramesIn, frameCount*channels, factor); -} - -MA_API void ma_copy_and_apply_volume_factor_pcm_frames(void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount, ma_format format, ma_uint32 channels, float factor) -{ - switch (format) - { - case ma_format_u8: ma_copy_and_apply_volume_factor_pcm_frames_u8 ((ma_uint8*)pFramesOut, (const ma_uint8*)pFramesIn, frameCount, channels, factor); return; - case ma_format_s16: ma_copy_and_apply_volume_factor_pcm_frames_s16((ma_int16*)pFramesOut, (const ma_int16*)pFramesIn, frameCount, channels, factor); return; - case ma_format_s24: ma_copy_and_apply_volume_factor_pcm_frames_s24( pFramesOut, pFramesIn, frameCount, channels, factor); return; - case ma_format_s32: ma_copy_and_apply_volume_factor_pcm_frames_s32((ma_int32*)pFramesOut, (const ma_int32*)pFramesIn, frameCount, channels, factor); return; - case ma_format_f32: ma_copy_and_apply_volume_factor_pcm_frames_f32( (float*)pFramesOut, (const float*)pFramesIn, frameCount, channels, factor); return; - default: return; /* Do nothing. */ - } -} - -MA_API void ma_apply_volume_factor_pcm_frames_u8(ma_uint8* pFrames, ma_uint64 frameCount, ma_uint32 channels, float factor) -{ - ma_copy_and_apply_volume_factor_pcm_frames_u8(pFrames, pFrames, frameCount, channels, factor); -} - -MA_API void ma_apply_volume_factor_pcm_frames_s16(ma_int16* pFrames, ma_uint64 frameCount, ma_uint32 channels, float factor) -{ - ma_copy_and_apply_volume_factor_pcm_frames_s16(pFrames, pFrames, frameCount, channels, factor); -} - -MA_API void ma_apply_volume_factor_pcm_frames_s24(void* pFrames, ma_uint64 frameCount, ma_uint32 channels, float factor) -{ - ma_copy_and_apply_volume_factor_pcm_frames_s24(pFrames, pFrames, frameCount, channels, factor); -} - -MA_API void ma_apply_volume_factor_pcm_frames_s32(ma_int32* pFrames, ma_uint64 frameCount, ma_uint32 channels, float factor) -{ - ma_copy_and_apply_volume_factor_pcm_frames_s32(pFrames, pFrames, frameCount, channels, factor); -} - -MA_API void ma_apply_volume_factor_pcm_frames_f32(float* pFrames, ma_uint64 frameCount, ma_uint32 channels, float factor) -{ - ma_copy_and_apply_volume_factor_pcm_frames_f32(pFrames, pFrames, frameCount, channels, factor); -} - -MA_API void ma_apply_volume_factor_pcm_frames(void* pFramesOut, ma_uint64 frameCount, ma_format format, ma_uint32 channels, float factor) -{ - ma_copy_and_apply_volume_factor_pcm_frames(pFramesOut, pFramesOut, frameCount, format, channels, factor); -} - - -MA_API void ma_copy_and_apply_volume_factor_per_channel_f32(float* pFramesOut, const float* pFramesIn, ma_uint64 frameCount, ma_uint32 channels, float* pChannelGains) -{ - ma_uint64 iFrame; - - if (channels == 2) { - /* TODO: Do an optimized implementation for stereo and mono. Can do a SIMD optimized implementation as well. */ - } - - for (iFrame = 0; iFrame < frameCount; iFrame += 1) { - ma_uint32 iChannel; - for (iChannel = 0; iChannel < channels; iChannel += 1) { - pFramesOut[iFrame * channels + iChannel] = pFramesIn[iFrame * channels + iChannel] * pChannelGains[iChannel]; - } - } -} - - - -static MA_INLINE ma_int16 ma_apply_volume_unclipped_u8(ma_int16 x, ma_int16 volume) -{ - return (ma_int16)(((ma_int32)x * (ma_int32)volume) >> 8); -} - -static MA_INLINE ma_int32 ma_apply_volume_unclipped_s16(ma_int32 x, ma_int16 volume) -{ - return (ma_int32)((x * volume) >> 8); -} - -static MA_INLINE ma_int64 ma_apply_volume_unclipped_s24(ma_int64 x, ma_int16 volume) -{ - return (ma_int64)((x * volume) >> 8); -} - -static MA_INLINE ma_int64 ma_apply_volume_unclipped_s32(ma_int64 x, ma_int16 volume) -{ - return (ma_int64)((x * volume) >> 8); -} - -static MA_INLINE float ma_apply_volume_unclipped_f32(float x, float volume) -{ - return x * volume; -} - - -MA_API void ma_copy_and_apply_volume_and_clip_samples_u8(ma_uint8* pDst, const ma_int16* pSrc, ma_uint64 count, float volume) -{ - ma_uint64 iSample; - ma_int16 volumeFixed; - - MA_ASSERT(pDst != NULL); - MA_ASSERT(pSrc != NULL); - - volumeFixed = ma_float_to_fixed_16(volume); - - for (iSample = 0; iSample < count; iSample += 1) { - pDst[iSample] = ma_clip_u8(ma_apply_volume_unclipped_u8(pSrc[iSample], volumeFixed)); - } -} - -MA_API void ma_copy_and_apply_volume_and_clip_samples_s16(ma_int16* pDst, const ma_int32* pSrc, ma_uint64 count, float volume) -{ - ma_uint64 iSample; - ma_int16 volumeFixed; - - MA_ASSERT(pDst != NULL); - MA_ASSERT(pSrc != NULL); - - volumeFixed = ma_float_to_fixed_16(volume); - - for (iSample = 0; iSample < count; iSample += 1) { - pDst[iSample] = ma_clip_s16(ma_apply_volume_unclipped_s16(pSrc[iSample], volumeFixed)); - } -} - -MA_API void ma_copy_and_apply_volume_and_clip_samples_s24(ma_uint8* pDst, const ma_int64* pSrc, ma_uint64 count, float volume) -{ - ma_uint64 iSample; - ma_int16 volumeFixed; - - MA_ASSERT(pDst != NULL); - MA_ASSERT(pSrc != NULL); - - volumeFixed = ma_float_to_fixed_16(volume); - - for (iSample = 0; iSample < count; iSample += 1) { - ma_int64 s = ma_clip_s24(ma_apply_volume_unclipped_s24(pSrc[iSample], volumeFixed)); - pDst[iSample*3 + 0] = (ma_uint8)((s & 0x000000FF) >> 0); - pDst[iSample*3 + 1] = (ma_uint8)((s & 0x0000FF00) >> 8); - pDst[iSample*3 + 2] = (ma_uint8)((s & 0x00FF0000) >> 16); - } -} - -MA_API void ma_copy_and_apply_volume_and_clip_samples_s32(ma_int32* pDst, const ma_int64* pSrc, ma_uint64 count, float volume) -{ - ma_uint64 iSample; - ma_int16 volumeFixed; - - MA_ASSERT(pDst != NULL); - MA_ASSERT(pSrc != NULL); - - volumeFixed = ma_float_to_fixed_16(volume); - - for (iSample = 0; iSample < count; iSample += 1) { - pDst[iSample] = ma_clip_s32(ma_apply_volume_unclipped_s32(pSrc[iSample], volumeFixed)); - } -} - -MA_API void ma_copy_and_apply_volume_and_clip_samples_f32(float* pDst, const float* pSrc, ma_uint64 count, float volume) -{ - ma_uint64 iSample; - - MA_ASSERT(pDst != NULL); - MA_ASSERT(pSrc != NULL); - - /* For the f32 case we need to make sure this supports in-place processing where the input and output buffers are the same. */ - - for (iSample = 0; iSample < count; iSample += 1) { - pDst[iSample] = ma_clip_f32(ma_apply_volume_unclipped_f32(pSrc[iSample], volume)); - } -} - -MA_API void ma_copy_and_apply_volume_and_clip_pcm_frames(void* pDst, const void* pSrc, ma_uint64 frameCount, ma_format format, ma_uint32 channels, float volume) -{ - MA_ASSERT(pDst != NULL); - MA_ASSERT(pSrc != NULL); - - if (volume == 1) { - ma_clip_pcm_frames(pDst, pSrc, frameCount, format, channels); /* Optimized case for volume = 1. */ - } else if (volume == 0) { - ma_silence_pcm_frames(pDst, frameCount, format, channels); /* Optimized case for volume = 0. */ - } else { - ma_uint64 sampleCount = frameCount * channels; - - switch (format) { - case ma_format_u8: ma_copy_and_apply_volume_and_clip_samples_u8( (ma_uint8*)pDst, (const ma_int16*)pSrc, sampleCount, volume); break; - case ma_format_s16: ma_copy_and_apply_volume_and_clip_samples_s16((ma_int16*)pDst, (const ma_int32*)pSrc, sampleCount, volume); break; - case ma_format_s24: ma_copy_and_apply_volume_and_clip_samples_s24((ma_uint8*)pDst, (const ma_int64*)pSrc, sampleCount, volume); break; - case ma_format_s32: ma_copy_and_apply_volume_and_clip_samples_s32((ma_int32*)pDst, (const ma_int64*)pSrc, sampleCount, volume); break; - case ma_format_f32: ma_copy_and_apply_volume_and_clip_samples_f32(( float*)pDst, (const float*)pSrc, sampleCount, volume); break; - - /* Do nothing if we don't know the format. We're including these here to silence a compiler warning about enums not being handled by the switch. */ - case ma_format_unknown: - case ma_format_count: - break; - } - } -} - - - -MA_API float ma_volume_linear_to_db(float factor) -{ - return 20*ma_log10f(factor); -} - -MA_API float ma_volume_db_to_linear(float gain) -{ - return ma_powf(10, gain/20.0f); -} - - -MA_API ma_result ma_mix_pcm_frames_f32(float* pDst, const float* pSrc, ma_uint64 frameCount, ma_uint32 channels, float volume) -{ - ma_uint64 iSample; - ma_uint64 sampleCount; - - if (pDst == NULL || pSrc == NULL || channels == 0) { - return MA_INVALID_ARGS; - } - - if (volume == 0) { - return MA_SUCCESS; /* No changes if the volume is 0. */ - } - - sampleCount = frameCount * channels; - - if (volume == 1) { - for (iSample = 0; iSample < sampleCount; iSample += 1) { - pDst[iSample] += pSrc[iSample]; - } - } else { - for (iSample = 0; iSample < sampleCount; iSample += 1) { - pDst[iSample] += ma_apply_volume_unclipped_f32(pSrc[iSample], volume); - } - } - - return MA_SUCCESS; -} - - - -/************************************************************************************************************************************************************** - -Format Conversion - -**************************************************************************************************************************************************************/ - -static MA_INLINE ma_int16 ma_pcm_sample_f32_to_s16(float x) -{ - return (ma_int16)(x * 32767.0f); -} - -static MA_INLINE ma_int16 ma_pcm_sample_u8_to_s16_no_scale(ma_uint8 x) -{ - return (ma_int16)((ma_int16)x - 128); -} - -static MA_INLINE ma_int64 ma_pcm_sample_s24_to_s32_no_scale(const ma_uint8* x) -{ - return (ma_int64)(((ma_uint64)x[0] << 40) | ((ma_uint64)x[1] << 48) | ((ma_uint64)x[2] << 56)) >> 40; /* Make sure the sign bits are maintained. */ -} - -static MA_INLINE void ma_pcm_sample_s32_to_s24_no_scale(ma_int64 x, ma_uint8* s24) -{ - s24[0] = (ma_uint8)((x & 0x000000FF) >> 0); - s24[1] = (ma_uint8)((x & 0x0000FF00) >> 8); - s24[2] = (ma_uint8)((x & 0x00FF0000) >> 16); -} - - -/* u8 */ -MA_API void ma_pcm_u8_to_u8(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) -{ - (void)ditherMode; - ma_copy_memory_64(dst, src, count * sizeof(ma_uint8)); -} - - -static MA_INLINE void ma_pcm_u8_to_s16__reference(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) -{ - ma_int16* dst_s16 = (ma_int16*)dst; - const ma_uint8* src_u8 = (const ma_uint8*)src; - - ma_uint64 i; - for (i = 0; i < count; i += 1) { - ma_int16 x = src_u8[i]; - x = (ma_int16)(x - 128); - x = (ma_int16)(x << 8); - dst_s16[i] = x; - } - - (void)ditherMode; -} - -static MA_INLINE void ma_pcm_u8_to_s16__optimized(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) -{ - ma_pcm_u8_to_s16__reference(dst, src, count, ditherMode); -} - -#if defined(MA_SUPPORT_SSE2) -static MA_INLINE void ma_pcm_u8_to_s16__sse2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) -{ - ma_pcm_u8_to_s16__optimized(dst, src, count, ditherMode); -} -#endif -#if defined(MA_SUPPORT_NEON) -static MA_INLINE void ma_pcm_u8_to_s16__neon(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) -{ - ma_pcm_u8_to_s16__optimized(dst, src, count, ditherMode); -} -#endif - -MA_API void ma_pcm_u8_to_s16(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) -{ -#ifdef MA_USE_REFERENCE_CONVERSION_APIS - ma_pcm_u8_to_s16__reference(dst, src, count, ditherMode); -#else - # if defined(MA_SUPPORT_SSE2) - if (ma_has_sse2()) { - ma_pcm_u8_to_s16__sse2(dst, src, count, ditherMode); - } else - #elif defined(MA_SUPPORT_NEON) - if (ma_has_neon()) { - ma_pcm_u8_to_s16__neon(dst, src, count, ditherMode); - } else - #endif - { - ma_pcm_u8_to_s16__optimized(dst, src, count, ditherMode); - } -#endif -} - - -static MA_INLINE void ma_pcm_u8_to_s24__reference(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) -{ - ma_uint8* dst_s24 = (ma_uint8*)dst; - const ma_uint8* src_u8 = (const ma_uint8*)src; - - ma_uint64 i; - for (i = 0; i < count; i += 1) { - ma_int16 x = src_u8[i]; - x = (ma_int16)(x - 128); - - dst_s24[i*3+0] = 0; - dst_s24[i*3+1] = 0; - dst_s24[i*3+2] = (ma_uint8)((ma_int8)x); - } - - (void)ditherMode; -} - -static MA_INLINE void ma_pcm_u8_to_s24__optimized(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) -{ - ma_pcm_u8_to_s24__reference(dst, src, count, ditherMode); -} - -#if defined(MA_SUPPORT_SSE2) -static MA_INLINE void ma_pcm_u8_to_s24__sse2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) -{ - ma_pcm_u8_to_s24__optimized(dst, src, count, ditherMode); -} -#endif -#if defined(MA_SUPPORT_NEON) -static MA_INLINE void ma_pcm_u8_to_s24__neon(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) -{ - ma_pcm_u8_to_s24__optimized(dst, src, count, ditherMode); -} -#endif - -MA_API void ma_pcm_u8_to_s24(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) -{ -#ifdef MA_USE_REFERENCE_CONVERSION_APIS - ma_pcm_u8_to_s24__reference(dst, src, count, ditherMode); -#else - # if defined(MA_SUPPORT_SSE2) - if (ma_has_sse2()) { - ma_pcm_u8_to_s24__sse2(dst, src, count, ditherMode); - } else - #elif defined(MA_SUPPORT_NEON) - if (ma_has_neon()) { - ma_pcm_u8_to_s24__neon(dst, src, count, ditherMode); - } else - #endif - { - ma_pcm_u8_to_s24__optimized(dst, src, count, ditherMode); - } -#endif -} - - -static MA_INLINE void ma_pcm_u8_to_s32__reference(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) -{ - ma_int32* dst_s32 = (ma_int32*)dst; - const ma_uint8* src_u8 = (const ma_uint8*)src; - - ma_uint64 i; - for (i = 0; i < count; i += 1) { - ma_int32 x = src_u8[i]; - x = x - 128; - x = x << 24; - dst_s32[i] = x; - } - - (void)ditherMode; -} - -static MA_INLINE void ma_pcm_u8_to_s32__optimized(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) -{ - ma_pcm_u8_to_s32__reference(dst, src, count, ditherMode); -} - -#if defined(MA_SUPPORT_SSE2) -static MA_INLINE void ma_pcm_u8_to_s32__sse2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) -{ - ma_pcm_u8_to_s32__optimized(dst, src, count, ditherMode); -} -#endif -#if defined(MA_SUPPORT_NEON) -static MA_INLINE void ma_pcm_u8_to_s32__neon(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) -{ - ma_pcm_u8_to_s32__optimized(dst, src, count, ditherMode); -} -#endif - -MA_API void ma_pcm_u8_to_s32(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) -{ -#ifdef MA_USE_REFERENCE_CONVERSION_APIS - ma_pcm_u8_to_s32__reference(dst, src, count, ditherMode); -#else - # if defined(MA_SUPPORT_SSE2) - if (ma_has_sse2()) { - ma_pcm_u8_to_s32__sse2(dst, src, count, ditherMode); - } else - #elif defined(MA_SUPPORT_NEON) - if (ma_has_neon()) { - ma_pcm_u8_to_s32__neon(dst, src, count, ditherMode); - } else - #endif - { - ma_pcm_u8_to_s32__optimized(dst, src, count, ditherMode); - } -#endif -} - - -static MA_INLINE void ma_pcm_u8_to_f32__reference(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) -{ - float* dst_f32 = (float*)dst; - const ma_uint8* src_u8 = (const ma_uint8*)src; - - ma_uint64 i; - for (i = 0; i < count; i += 1) { - float x = (float)src_u8[i]; - x = x * 0.00784313725490196078f; /* 0..255 to 0..2 */ - x = x - 1; /* 0..2 to -1..1 */ - - dst_f32[i] = x; - } - - (void)ditherMode; -} - -static MA_INLINE void ma_pcm_u8_to_f32__optimized(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) -{ - ma_pcm_u8_to_f32__reference(dst, src, count, ditherMode); -} - -#if defined(MA_SUPPORT_SSE2) -static MA_INLINE void ma_pcm_u8_to_f32__sse2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) -{ - ma_pcm_u8_to_f32__optimized(dst, src, count, ditherMode); -} -#endif -#if defined(MA_SUPPORT_NEON) -static MA_INLINE void ma_pcm_u8_to_f32__neon(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) -{ - ma_pcm_u8_to_f32__optimized(dst, src, count, ditherMode); -} -#endif - -MA_API void ma_pcm_u8_to_f32(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) -{ -#ifdef MA_USE_REFERENCE_CONVERSION_APIS - ma_pcm_u8_to_f32__reference(dst, src, count, ditherMode); -#else - # if defined(MA_SUPPORT_SSE2) - if (ma_has_sse2()) { - ma_pcm_u8_to_f32__sse2(dst, src, count, ditherMode); - } else - #elif defined(MA_SUPPORT_NEON) - if (ma_has_neon()) { - ma_pcm_u8_to_f32__neon(dst, src, count, ditherMode); - } else - #endif - { - ma_pcm_u8_to_f32__optimized(dst, src, count, ditherMode); - } -#endif -} - - -#ifdef MA_USE_REFERENCE_CONVERSION_APIS -static MA_INLINE void ma_pcm_interleave_u8__reference(void* dst, const void** src, ma_uint64 frameCount, ma_uint32 channels) -{ - ma_uint8* dst_u8 = (ma_uint8*)dst; - const ma_uint8** src_u8 = (const ma_uint8**)src; - - ma_uint64 iFrame; - for (iFrame = 0; iFrame < frameCount; iFrame += 1) { - ma_uint32 iChannel; - for (iChannel = 0; iChannel < channels; iChannel += 1) { - dst_u8[iFrame*channels + iChannel] = src_u8[iChannel][iFrame]; - } - } -} -#else -static MA_INLINE void ma_pcm_interleave_u8__optimized(void* dst, const void** src, ma_uint64 frameCount, ma_uint32 channels) -{ - ma_uint8* dst_u8 = (ma_uint8*)dst; - const ma_uint8** src_u8 = (const ma_uint8**)src; - - if (channels == 1) { - ma_copy_memory_64(dst, src[0], frameCount * sizeof(ma_uint8)); - } else if (channels == 2) { - ma_uint64 iFrame; - for (iFrame = 0; iFrame < frameCount; iFrame += 1) { - dst_u8[iFrame*2 + 0] = src_u8[0][iFrame]; - dst_u8[iFrame*2 + 1] = src_u8[1][iFrame]; - } - } else { - ma_uint64 iFrame; - for (iFrame = 0; iFrame < frameCount; iFrame += 1) { - ma_uint32 iChannel; - for (iChannel = 0; iChannel < channels; iChannel += 1) { - dst_u8[iFrame*channels + iChannel] = src_u8[iChannel][iFrame]; - } - } - } -} -#endif - -MA_API void ma_pcm_interleave_u8(void* dst, const void** src, ma_uint64 frameCount, ma_uint32 channels) -{ -#ifdef MA_USE_REFERENCE_CONVERSION_APIS - ma_pcm_interleave_u8__reference(dst, src, frameCount, channels); -#else - ma_pcm_interleave_u8__optimized(dst, src, frameCount, channels); -#endif -} - - -static MA_INLINE void ma_pcm_deinterleave_u8__reference(void** dst, const void* src, ma_uint64 frameCount, ma_uint32 channels) -{ - ma_uint8** dst_u8 = (ma_uint8**)dst; - const ma_uint8* src_u8 = (const ma_uint8*)src; - - ma_uint64 iFrame; - for (iFrame = 0; iFrame < frameCount; iFrame += 1) { - ma_uint32 iChannel; - for (iChannel = 0; iChannel < channels; iChannel += 1) { - dst_u8[iChannel][iFrame] = src_u8[iFrame*channels + iChannel]; - } - } -} - -static MA_INLINE void ma_pcm_deinterleave_u8__optimized(void** dst, const void* src, ma_uint64 frameCount, ma_uint32 channels) -{ - ma_pcm_deinterleave_u8__reference(dst, src, frameCount, channels); -} - -MA_API void ma_pcm_deinterleave_u8(void** dst, const void* src, ma_uint64 frameCount, ma_uint32 channels) -{ -#ifdef MA_USE_REFERENCE_CONVERSION_APIS - ma_pcm_deinterleave_u8__reference(dst, src, frameCount, channels); -#else - ma_pcm_deinterleave_u8__optimized(dst, src, frameCount, channels); -#endif -} - - -/* s16 */ -static MA_INLINE void ma_pcm_s16_to_u8__reference(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) -{ - ma_uint8* dst_u8 = (ma_uint8*)dst; - const ma_int16* src_s16 = (const ma_int16*)src; - - if (ditherMode == ma_dither_mode_none) { - ma_uint64 i; - for (i = 0; i < count; i += 1) { - ma_int16 x = src_s16[i]; - x = (ma_int16)(x >> 8); - x = (ma_int16)(x + 128); - dst_u8[i] = (ma_uint8)x; - } - } else { - ma_uint64 i; - for (i = 0; i < count; i += 1) { - ma_int16 x = src_s16[i]; - - /* Dither. Don't overflow. */ - ma_int32 dither = ma_dither_s32(ditherMode, -0x80, 0x7F); - if ((x + dither) <= 0x7FFF) { - x = (ma_int16)(x + dither); - } else { - x = 0x7FFF; - } - - x = (ma_int16)(x >> 8); - x = (ma_int16)(x + 128); - dst_u8[i] = (ma_uint8)x; - } - } -} - -static MA_INLINE void ma_pcm_s16_to_u8__optimized(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) -{ - ma_pcm_s16_to_u8__reference(dst, src, count, ditherMode); -} - -#if defined(MA_SUPPORT_SSE2) -static MA_INLINE void ma_pcm_s16_to_u8__sse2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) -{ - ma_pcm_s16_to_u8__optimized(dst, src, count, ditherMode); -} -#endif -#if defined(MA_SUPPORT_NEON) -static MA_INLINE void ma_pcm_s16_to_u8__neon(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) -{ - ma_pcm_s16_to_u8__optimized(dst, src, count, ditherMode); -} -#endif - -MA_API void ma_pcm_s16_to_u8(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) -{ -#ifdef MA_USE_REFERENCE_CONVERSION_APIS - ma_pcm_s16_to_u8__reference(dst, src, count, ditherMode); -#else - # if defined(MA_SUPPORT_SSE2) - if (ma_has_sse2()) { - ma_pcm_s16_to_u8__sse2(dst, src, count, ditherMode); - } else - #elif defined(MA_SUPPORT_NEON) - if (ma_has_neon()) { - ma_pcm_s16_to_u8__neon(dst, src, count, ditherMode); - } else - #endif - { - ma_pcm_s16_to_u8__optimized(dst, src, count, ditherMode); - } -#endif -} - - -MA_API void ma_pcm_s16_to_s16(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) -{ - (void)ditherMode; - ma_copy_memory_64(dst, src, count * sizeof(ma_int16)); -} - - -static MA_INLINE void ma_pcm_s16_to_s24__reference(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) -{ - ma_uint8* dst_s24 = (ma_uint8*)dst; - const ma_int16* src_s16 = (const ma_int16*)src; - - ma_uint64 i; - for (i = 0; i < count; i += 1) { - dst_s24[i*3+0] = 0; - dst_s24[i*3+1] = (ma_uint8)(src_s16[i] & 0xFF); - dst_s24[i*3+2] = (ma_uint8)(src_s16[i] >> 8); - } - - (void)ditherMode; -} - -static MA_INLINE void ma_pcm_s16_to_s24__optimized(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) -{ - ma_pcm_s16_to_s24__reference(dst, src, count, ditherMode); -} - -#if defined(MA_SUPPORT_SSE2) -static MA_INLINE void ma_pcm_s16_to_s24__sse2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) -{ - ma_pcm_s16_to_s24__optimized(dst, src, count, ditherMode); -} -#endif -#if defined(MA_SUPPORT_NEON) -static MA_INLINE void ma_pcm_s16_to_s24__neon(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) -{ - ma_pcm_s16_to_s24__optimized(dst, src, count, ditherMode); -} -#endif - -MA_API void ma_pcm_s16_to_s24(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) -{ -#ifdef MA_USE_REFERENCE_CONVERSION_APIS - ma_pcm_s16_to_s24__reference(dst, src, count, ditherMode); -#else - # if defined(MA_SUPPORT_SSE2) - if (ma_has_sse2()) { - ma_pcm_s16_to_s24__sse2(dst, src, count, ditherMode); - } else - #elif defined(MA_SUPPORT_NEON) - if (ma_has_neon()) { - ma_pcm_s16_to_s24__neon(dst, src, count, ditherMode); - } else - #endif - { - ma_pcm_s16_to_s24__optimized(dst, src, count, ditherMode); - } -#endif -} - - -static MA_INLINE void ma_pcm_s16_to_s32__reference(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) -{ - ma_int32* dst_s32 = (ma_int32*)dst; - const ma_int16* src_s16 = (const ma_int16*)src; - - ma_uint64 i; - for (i = 0; i < count; i += 1) { - dst_s32[i] = src_s16[i] << 16; - } - - (void)ditherMode; -} - -static MA_INLINE void ma_pcm_s16_to_s32__optimized(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) -{ - ma_pcm_s16_to_s32__reference(dst, src, count, ditherMode); -} - -#if defined(MA_SUPPORT_SSE2) -static MA_INLINE void ma_pcm_s16_to_s32__sse2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) -{ - ma_pcm_s16_to_s32__optimized(dst, src, count, ditherMode); -} -#endif -#if defined(MA_SUPPORT_NEON) -static MA_INLINE void ma_pcm_s16_to_s32__neon(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) -{ - ma_pcm_s16_to_s32__optimized(dst, src, count, ditherMode); -} -#endif - -MA_API void ma_pcm_s16_to_s32(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) -{ -#ifdef MA_USE_REFERENCE_CONVERSION_APIS - ma_pcm_s16_to_s32__reference(dst, src, count, ditherMode); -#else - # if defined(MA_SUPPORT_SSE2) - if (ma_has_sse2()) { - ma_pcm_s16_to_s32__sse2(dst, src, count, ditherMode); - } else - #elif defined(MA_SUPPORT_NEON) - if (ma_has_neon()) { - ma_pcm_s16_to_s32__neon(dst, src, count, ditherMode); - } else - #endif - { - ma_pcm_s16_to_s32__optimized(dst, src, count, ditherMode); - } -#endif -} - - -static MA_INLINE void ma_pcm_s16_to_f32__reference(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) -{ - float* dst_f32 = (float*)dst; - const ma_int16* src_s16 = (const ma_int16*)src; - - ma_uint64 i; - for (i = 0; i < count; i += 1) { - float x = (float)src_s16[i]; - -#if 0 - /* The accurate way. */ - x = x + 32768.0f; /* -32768..32767 to 0..65535 */ - x = x * 0.00003051804379339284f; /* 0..65535 to 0..2 */ - x = x - 1; /* 0..2 to -1..1 */ -#else - /* The fast way. */ - x = x * 0.000030517578125f; /* -32768..32767 to -1..0.999969482421875 */ -#endif - - dst_f32[i] = x; - } - - (void)ditherMode; -} - -static MA_INLINE void ma_pcm_s16_to_f32__optimized(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) -{ - ma_pcm_s16_to_f32__reference(dst, src, count, ditherMode); -} - -#if defined(MA_SUPPORT_SSE2) -static MA_INLINE void ma_pcm_s16_to_f32__sse2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) -{ - ma_pcm_s16_to_f32__optimized(dst, src, count, ditherMode); -} -#endif -#if defined(MA_SUPPORT_NEON) -static MA_INLINE void ma_pcm_s16_to_f32__neon(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) -{ - ma_pcm_s16_to_f32__optimized(dst, src, count, ditherMode); -} -#endif - -MA_API void ma_pcm_s16_to_f32(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) -{ -#ifdef MA_USE_REFERENCE_CONVERSION_APIS - ma_pcm_s16_to_f32__reference(dst, src, count, ditherMode); -#else - # if defined(MA_SUPPORT_SSE2) - if (ma_has_sse2()) { - ma_pcm_s16_to_f32__sse2(dst, src, count, ditherMode); - } else - #elif defined(MA_SUPPORT_NEON) - if (ma_has_neon()) { - ma_pcm_s16_to_f32__neon(dst, src, count, ditherMode); - } else - #endif - { - ma_pcm_s16_to_f32__optimized(dst, src, count, ditherMode); - } -#endif -} - - -static MA_INLINE void ma_pcm_interleave_s16__reference(void* dst, const void** src, ma_uint64 frameCount, ma_uint32 channels) -{ - ma_int16* dst_s16 = (ma_int16*)dst; - const ma_int16** src_s16 = (const ma_int16**)src; - - ma_uint64 iFrame; - for (iFrame = 0; iFrame < frameCount; iFrame += 1) { - ma_uint32 iChannel; - for (iChannel = 0; iChannel < channels; iChannel += 1) { - dst_s16[iFrame*channels + iChannel] = src_s16[iChannel][iFrame]; - } - } -} - -static MA_INLINE void ma_pcm_interleave_s16__optimized(void* dst, const void** src, ma_uint64 frameCount, ma_uint32 channels) -{ - ma_pcm_interleave_s16__reference(dst, src, frameCount, channels); -} - -MA_API void ma_pcm_interleave_s16(void* dst, const void** src, ma_uint64 frameCount, ma_uint32 channels) -{ -#ifdef MA_USE_REFERENCE_CONVERSION_APIS - ma_pcm_interleave_s16__reference(dst, src, frameCount, channels); -#else - ma_pcm_interleave_s16__optimized(dst, src, frameCount, channels); -#endif -} - - -static MA_INLINE void ma_pcm_deinterleave_s16__reference(void** dst, const void* src, ma_uint64 frameCount, ma_uint32 channels) -{ - ma_int16** dst_s16 = (ma_int16**)dst; - const ma_int16* src_s16 = (const ma_int16*)src; - - ma_uint64 iFrame; - for (iFrame = 0; iFrame < frameCount; iFrame += 1) { - ma_uint32 iChannel; - for (iChannel = 0; iChannel < channels; iChannel += 1) { - dst_s16[iChannel][iFrame] = src_s16[iFrame*channels + iChannel]; - } - } -} - -static MA_INLINE void ma_pcm_deinterleave_s16__optimized(void** dst, const void* src, ma_uint64 frameCount, ma_uint32 channels) -{ - ma_pcm_deinterleave_s16__reference(dst, src, frameCount, channels); -} - -MA_API void ma_pcm_deinterleave_s16(void** dst, const void* src, ma_uint64 frameCount, ma_uint32 channels) -{ -#ifdef MA_USE_REFERENCE_CONVERSION_APIS - ma_pcm_deinterleave_s16__reference(dst, src, frameCount, channels); -#else - ma_pcm_deinterleave_s16__optimized(dst, src, frameCount, channels); -#endif -} - - -/* s24 */ -static MA_INLINE void ma_pcm_s24_to_u8__reference(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) -{ - ma_uint8* dst_u8 = (ma_uint8*)dst; - const ma_uint8* src_s24 = (const ma_uint8*)src; - - if (ditherMode == ma_dither_mode_none) { - ma_uint64 i; - for (i = 0; i < count; i += 1) { - dst_u8[i] = (ma_uint8)((ma_int8)src_s24[i*3 + 2] + 128); - } - } else { - ma_uint64 i; - for (i = 0; i < count; i += 1) { - ma_int32 x = (ma_int32)(((ma_uint32)(src_s24[i*3+0]) << 8) | ((ma_uint32)(src_s24[i*3+1]) << 16) | ((ma_uint32)(src_s24[i*3+2])) << 24); - - /* Dither. Don't overflow. */ - ma_int32 dither = ma_dither_s32(ditherMode, -0x800000, 0x7FFFFF); - if ((ma_int64)x + dither <= 0x7FFFFFFF) { - x = x + dither; - } else { - x = 0x7FFFFFFF; - } - - x = x >> 24; - x = x + 128; - dst_u8[i] = (ma_uint8)x; - } - } -} - -static MA_INLINE void ma_pcm_s24_to_u8__optimized(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) -{ - ma_pcm_s24_to_u8__reference(dst, src, count, ditherMode); -} - -#if defined(MA_SUPPORT_SSE2) -static MA_INLINE void ma_pcm_s24_to_u8__sse2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) -{ - ma_pcm_s24_to_u8__optimized(dst, src, count, ditherMode); -} -#endif -#if defined(MA_SUPPORT_NEON) -static MA_INLINE void ma_pcm_s24_to_u8__neon(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) -{ - ma_pcm_s24_to_u8__optimized(dst, src, count, ditherMode); -} -#endif - -MA_API void ma_pcm_s24_to_u8(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) -{ -#ifdef MA_USE_REFERENCE_CONVERSION_APIS - ma_pcm_s24_to_u8__reference(dst, src, count, ditherMode); -#else - # if defined(MA_SUPPORT_SSE2) - if (ma_has_sse2()) { - ma_pcm_s24_to_u8__sse2(dst, src, count, ditherMode); - } else - #elif defined(MA_SUPPORT_NEON) - if (ma_has_neon()) { - ma_pcm_s24_to_u8__neon(dst, src, count, ditherMode); - } else - #endif - { - ma_pcm_s24_to_u8__optimized(dst, src, count, ditherMode); - } -#endif -} - - -static MA_INLINE void ma_pcm_s24_to_s16__reference(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) -{ - ma_int16* dst_s16 = (ma_int16*)dst; - const ma_uint8* src_s24 = (const ma_uint8*)src; - - if (ditherMode == ma_dither_mode_none) { - ma_uint64 i; - for (i = 0; i < count; i += 1) { - ma_uint16 dst_lo = ((ma_uint16)src_s24[i*3 + 1]); - ma_uint16 dst_hi = (ma_uint16)((ma_uint16)src_s24[i*3 + 2] << 8); - dst_s16[i] = (ma_int16)(dst_lo | dst_hi); - } - } else { - ma_uint64 i; - for (i = 0; i < count; i += 1) { - ma_int32 x = (ma_int32)(((ma_uint32)(src_s24[i*3+0]) << 8) | ((ma_uint32)(src_s24[i*3+1]) << 16) | ((ma_uint32)(src_s24[i*3+2])) << 24); - - /* Dither. Don't overflow. */ - ma_int32 dither = ma_dither_s32(ditherMode, -0x8000, 0x7FFF); - if ((ma_int64)x + dither <= 0x7FFFFFFF) { - x = x + dither; - } else { - x = 0x7FFFFFFF; - } - - x = x >> 16; - dst_s16[i] = (ma_int16)x; - } - } -} - -static MA_INLINE void ma_pcm_s24_to_s16__optimized(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) -{ - ma_pcm_s24_to_s16__reference(dst, src, count, ditherMode); -} - -#if defined(MA_SUPPORT_SSE2) -static MA_INLINE void ma_pcm_s24_to_s16__sse2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) -{ - ma_pcm_s24_to_s16__optimized(dst, src, count, ditherMode); -} -#endif -#if defined(MA_SUPPORT_NEON) -static MA_INLINE void ma_pcm_s24_to_s16__neon(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) -{ - ma_pcm_s24_to_s16__optimized(dst, src, count, ditherMode); -} -#endif - -MA_API void ma_pcm_s24_to_s16(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) -{ -#ifdef MA_USE_REFERENCE_CONVERSION_APIS - ma_pcm_s24_to_s16__reference(dst, src, count, ditherMode); -#else - # if defined(MA_SUPPORT_SSE2) - if (ma_has_sse2()) { - ma_pcm_s24_to_s16__sse2(dst, src, count, ditherMode); - } else - #elif defined(MA_SUPPORT_NEON) - if (ma_has_neon()) { - ma_pcm_s24_to_s16__neon(dst, src, count, ditherMode); - } else - #endif - { - ma_pcm_s24_to_s16__optimized(dst, src, count, ditherMode); - } -#endif -} - - -MA_API void ma_pcm_s24_to_s24(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) -{ - (void)ditherMode; - - ma_copy_memory_64(dst, src, count * 3); -} - - -static MA_INLINE void ma_pcm_s24_to_s32__reference(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) -{ - ma_int32* dst_s32 = (ma_int32*)dst; - const ma_uint8* src_s24 = (const ma_uint8*)src; - - ma_uint64 i; - for (i = 0; i < count; i += 1) { - dst_s32[i] = (ma_int32)(((ma_uint32)(src_s24[i*3+0]) << 8) | ((ma_uint32)(src_s24[i*3+1]) << 16) | ((ma_uint32)(src_s24[i*3+2])) << 24); - } - - (void)ditherMode; -} - -static MA_INLINE void ma_pcm_s24_to_s32__optimized(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) -{ - ma_pcm_s24_to_s32__reference(dst, src, count, ditherMode); -} - -#if defined(MA_SUPPORT_SSE2) -static MA_INLINE void ma_pcm_s24_to_s32__sse2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) -{ - ma_pcm_s24_to_s32__optimized(dst, src, count, ditherMode); -} -#endif -#if defined(MA_SUPPORT_NEON) -static MA_INLINE void ma_pcm_s24_to_s32__neon(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) -{ - ma_pcm_s24_to_s32__optimized(dst, src, count, ditherMode); -} -#endif - -MA_API void ma_pcm_s24_to_s32(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) -{ -#ifdef MA_USE_REFERENCE_CONVERSION_APIS - ma_pcm_s24_to_s32__reference(dst, src, count, ditherMode); -#else - # if defined(MA_SUPPORT_SSE2) - if (ma_has_sse2()) { - ma_pcm_s24_to_s32__sse2(dst, src, count, ditherMode); - } else - #elif defined(MA_SUPPORT_NEON) - if (ma_has_neon()) { - ma_pcm_s24_to_s32__neon(dst, src, count, ditherMode); - } else - #endif - { - ma_pcm_s24_to_s32__optimized(dst, src, count, ditherMode); - } -#endif -} - - -static MA_INLINE void ma_pcm_s24_to_f32__reference(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) -{ - float* dst_f32 = (float*)dst; - const ma_uint8* src_s24 = (const ma_uint8*)src; - - ma_uint64 i; - for (i = 0; i < count; i += 1) { - float x = (float)(((ma_int32)(((ma_uint32)(src_s24[i*3+0]) << 8) | ((ma_uint32)(src_s24[i*3+1]) << 16) | ((ma_uint32)(src_s24[i*3+2])) << 24)) >> 8); - -#if 0 - /* The accurate way. */ - x = x + 8388608.0f; /* -8388608..8388607 to 0..16777215 */ - x = x * 0.00000011920929665621f; /* 0..16777215 to 0..2 */ - x = x - 1; /* 0..2 to -1..1 */ -#else - /* The fast way. */ - x = x * 0.00000011920928955078125f; /* -8388608..8388607 to -1..0.999969482421875 */ -#endif - - dst_f32[i] = x; - } - - (void)ditherMode; -} - -static MA_INLINE void ma_pcm_s24_to_f32__optimized(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) -{ - ma_pcm_s24_to_f32__reference(dst, src, count, ditherMode); -} - -#if defined(MA_SUPPORT_SSE2) -static MA_INLINE void ma_pcm_s24_to_f32__sse2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) -{ - ma_pcm_s24_to_f32__optimized(dst, src, count, ditherMode); -} -#endif -#if defined(MA_SUPPORT_NEON) -static MA_INLINE void ma_pcm_s24_to_f32__neon(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) -{ - ma_pcm_s24_to_f32__optimized(dst, src, count, ditherMode); -} -#endif - -MA_API void ma_pcm_s24_to_f32(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) -{ -#ifdef MA_USE_REFERENCE_CONVERSION_APIS - ma_pcm_s24_to_f32__reference(dst, src, count, ditherMode); -#else - # if defined(MA_SUPPORT_SSE2) - if (ma_has_sse2()) { - ma_pcm_s24_to_f32__sse2(dst, src, count, ditherMode); - } else - #elif defined(MA_SUPPORT_NEON) - if (ma_has_neon()) { - ma_pcm_s24_to_f32__neon(dst, src, count, ditherMode); - } else - #endif - { - ma_pcm_s24_to_f32__optimized(dst, src, count, ditherMode); - } -#endif -} - - -static MA_INLINE void ma_pcm_interleave_s24__reference(void* dst, const void** src, ma_uint64 frameCount, ma_uint32 channels) -{ - ma_uint8* dst8 = (ma_uint8*)dst; - const ma_uint8** src8 = (const ma_uint8**)src; - - ma_uint64 iFrame; - for (iFrame = 0; iFrame < frameCount; iFrame += 1) { - ma_uint32 iChannel; - for (iChannel = 0; iChannel < channels; iChannel += 1) { - dst8[iFrame*3*channels + iChannel*3 + 0] = src8[iChannel][iFrame*3 + 0]; - dst8[iFrame*3*channels + iChannel*3 + 1] = src8[iChannel][iFrame*3 + 1]; - dst8[iFrame*3*channels + iChannel*3 + 2] = src8[iChannel][iFrame*3 + 2]; - } - } -} - -static MA_INLINE void ma_pcm_interleave_s24__optimized(void* dst, const void** src, ma_uint64 frameCount, ma_uint32 channels) -{ - ma_pcm_interleave_s24__reference(dst, src, frameCount, channels); -} - -MA_API void ma_pcm_interleave_s24(void* dst, const void** src, ma_uint64 frameCount, ma_uint32 channels) -{ -#ifdef MA_USE_REFERENCE_CONVERSION_APIS - ma_pcm_interleave_s24__reference(dst, src, frameCount, channels); -#else - ma_pcm_interleave_s24__optimized(dst, src, frameCount, channels); -#endif -} - - -static MA_INLINE void ma_pcm_deinterleave_s24__reference(void** dst, const void* src, ma_uint64 frameCount, ma_uint32 channels) -{ - ma_uint8** dst8 = (ma_uint8**)dst; - const ma_uint8* src8 = (const ma_uint8*)src; - - ma_uint32 iFrame; - for (iFrame = 0; iFrame < frameCount; iFrame += 1) { - ma_uint32 iChannel; - for (iChannel = 0; iChannel < channels; iChannel += 1) { - dst8[iChannel][iFrame*3 + 0] = src8[iFrame*3*channels + iChannel*3 + 0]; - dst8[iChannel][iFrame*3 + 1] = src8[iFrame*3*channels + iChannel*3 + 1]; - dst8[iChannel][iFrame*3 + 2] = src8[iFrame*3*channels + iChannel*3 + 2]; - } - } -} - -static MA_INLINE void ma_pcm_deinterleave_s24__optimized(void** dst, const void* src, ma_uint64 frameCount, ma_uint32 channels) -{ - ma_pcm_deinterleave_s24__reference(dst, src, frameCount, channels); -} - -MA_API void ma_pcm_deinterleave_s24(void** dst, const void* src, ma_uint64 frameCount, ma_uint32 channels) -{ -#ifdef MA_USE_REFERENCE_CONVERSION_APIS - ma_pcm_deinterleave_s24__reference(dst, src, frameCount, channels); -#else - ma_pcm_deinterleave_s24__optimized(dst, src, frameCount, channels); -#endif -} - - - -/* s32 */ -static MA_INLINE void ma_pcm_s32_to_u8__reference(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) -{ - ma_uint8* dst_u8 = (ma_uint8*)dst; - const ma_int32* src_s32 = (const ma_int32*)src; - - if (ditherMode == ma_dither_mode_none) { - ma_uint64 i; - for (i = 0; i < count; i += 1) { - ma_int32 x = src_s32[i]; - x = x >> 24; - x = x + 128; - dst_u8[i] = (ma_uint8)x; - } - } else { - ma_uint64 i; - for (i = 0; i < count; i += 1) { - ma_int32 x = src_s32[i]; - - /* Dither. Don't overflow. */ - ma_int32 dither = ma_dither_s32(ditherMode, -0x800000, 0x7FFFFF); - if ((ma_int64)x + dither <= 0x7FFFFFFF) { - x = x + dither; - } else { - x = 0x7FFFFFFF; - } - - x = x >> 24; - x = x + 128; - dst_u8[i] = (ma_uint8)x; - } - } -} - -static MA_INLINE void ma_pcm_s32_to_u8__optimized(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) -{ - ma_pcm_s32_to_u8__reference(dst, src, count, ditherMode); -} - -#if defined(MA_SUPPORT_SSE2) -static MA_INLINE void ma_pcm_s32_to_u8__sse2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) -{ - ma_pcm_s32_to_u8__optimized(dst, src, count, ditherMode); -} -#endif -#if defined(MA_SUPPORT_NEON) -static MA_INLINE void ma_pcm_s32_to_u8__neon(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) -{ - ma_pcm_s32_to_u8__optimized(dst, src, count, ditherMode); -} -#endif - -MA_API void ma_pcm_s32_to_u8(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) -{ -#ifdef MA_USE_REFERENCE_CONVERSION_APIS - ma_pcm_s32_to_u8__reference(dst, src, count, ditherMode); -#else - # if defined(MA_SUPPORT_SSE2) - if (ma_has_sse2()) { - ma_pcm_s32_to_u8__sse2(dst, src, count, ditherMode); - } else - #elif defined(MA_SUPPORT_NEON) - if (ma_has_neon()) { - ma_pcm_s32_to_u8__neon(dst, src, count, ditherMode); - } else - #endif - { - ma_pcm_s32_to_u8__optimized(dst, src, count, ditherMode); - } -#endif -} - - -static MA_INLINE void ma_pcm_s32_to_s16__reference(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) -{ - ma_int16* dst_s16 = (ma_int16*)dst; - const ma_int32* src_s32 = (const ma_int32*)src; - - if (ditherMode == ma_dither_mode_none) { - ma_uint64 i; - for (i = 0; i < count; i += 1) { - ma_int32 x = src_s32[i]; - x = x >> 16; - dst_s16[i] = (ma_int16)x; - } - } else { - ma_uint64 i; - for (i = 0; i < count; i += 1) { - ma_int32 x = src_s32[i]; - - /* Dither. Don't overflow. */ - ma_int32 dither = ma_dither_s32(ditherMode, -0x8000, 0x7FFF); - if ((ma_int64)x + dither <= 0x7FFFFFFF) { - x = x + dither; - } else { - x = 0x7FFFFFFF; - } - - x = x >> 16; - dst_s16[i] = (ma_int16)x; - } - } -} - -static MA_INLINE void ma_pcm_s32_to_s16__optimized(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) -{ - ma_pcm_s32_to_s16__reference(dst, src, count, ditherMode); -} - -#if defined(MA_SUPPORT_SSE2) -static MA_INLINE void ma_pcm_s32_to_s16__sse2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) -{ - ma_pcm_s32_to_s16__optimized(dst, src, count, ditherMode); -} -#endif -#if defined(MA_SUPPORT_NEON) -static MA_INLINE void ma_pcm_s32_to_s16__neon(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) -{ - ma_pcm_s32_to_s16__optimized(dst, src, count, ditherMode); -} -#endif - -MA_API void ma_pcm_s32_to_s16(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) -{ -#ifdef MA_USE_REFERENCE_CONVERSION_APIS - ma_pcm_s32_to_s16__reference(dst, src, count, ditherMode); -#else - # if defined(MA_SUPPORT_SSE2) - if (ma_has_sse2()) { - ma_pcm_s32_to_s16__sse2(dst, src, count, ditherMode); - } else - #elif defined(MA_SUPPORT_NEON) - if (ma_has_neon()) { - ma_pcm_s32_to_s16__neon(dst, src, count, ditherMode); - } else - #endif - { - ma_pcm_s32_to_s16__optimized(dst, src, count, ditherMode); - } -#endif -} - - -static MA_INLINE void ma_pcm_s32_to_s24__reference(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) -{ - ma_uint8* dst_s24 = (ma_uint8*)dst; - const ma_int32* src_s32 = (const ma_int32*)src; - - ma_uint64 i; - for (i = 0; i < count; i += 1) { - ma_uint32 x = (ma_uint32)src_s32[i]; - dst_s24[i*3+0] = (ma_uint8)((x & 0x0000FF00) >> 8); - dst_s24[i*3+1] = (ma_uint8)((x & 0x00FF0000) >> 16); - dst_s24[i*3+2] = (ma_uint8)((x & 0xFF000000) >> 24); - } - - (void)ditherMode; /* No dithering for s32 -> s24. */ -} - -static MA_INLINE void ma_pcm_s32_to_s24__optimized(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) -{ - ma_pcm_s32_to_s24__reference(dst, src, count, ditherMode); -} - -#if defined(MA_SUPPORT_SSE2) -static MA_INLINE void ma_pcm_s32_to_s24__sse2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) -{ - ma_pcm_s32_to_s24__optimized(dst, src, count, ditherMode); -} -#endif -#if defined(MA_SUPPORT_NEON) -static MA_INLINE void ma_pcm_s32_to_s24__neon(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) -{ - ma_pcm_s32_to_s24__optimized(dst, src, count, ditherMode); -} -#endif - -MA_API void ma_pcm_s32_to_s24(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) -{ -#ifdef MA_USE_REFERENCE_CONVERSION_APIS - ma_pcm_s32_to_s24__reference(dst, src, count, ditherMode); -#else - # if defined(MA_SUPPORT_SSE2) - if (ma_has_sse2()) { - ma_pcm_s32_to_s24__sse2(dst, src, count, ditherMode); - } else - #elif defined(MA_SUPPORT_NEON) - if (ma_has_neon()) { - ma_pcm_s32_to_s24__neon(dst, src, count, ditherMode); - } else - #endif - { - ma_pcm_s32_to_s24__optimized(dst, src, count, ditherMode); - } -#endif -} - - -MA_API void ma_pcm_s32_to_s32(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) -{ - (void)ditherMode; - - ma_copy_memory_64(dst, src, count * sizeof(ma_int32)); -} - - -static MA_INLINE void ma_pcm_s32_to_f32__reference(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) -{ - float* dst_f32 = (float*)dst; - const ma_int32* src_s32 = (const ma_int32*)src; - - ma_uint64 i; - for (i = 0; i < count; i += 1) { - double x = src_s32[i]; - -#if 0 - x = x + 2147483648.0; - x = x * 0.0000000004656612873077392578125; - x = x - 1; -#else - x = x / 2147483648.0; -#endif - - dst_f32[i] = (float)x; - } - - (void)ditherMode; /* No dithering for s32 -> f32. */ -} - -static MA_INLINE void ma_pcm_s32_to_f32__optimized(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) -{ - ma_pcm_s32_to_f32__reference(dst, src, count, ditherMode); -} - -#if defined(MA_SUPPORT_SSE2) -static MA_INLINE void ma_pcm_s32_to_f32__sse2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) -{ - ma_pcm_s32_to_f32__optimized(dst, src, count, ditherMode); -} -#endif -#if defined(MA_SUPPORT_NEON) -static MA_INLINE void ma_pcm_s32_to_f32__neon(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) -{ - ma_pcm_s32_to_f32__optimized(dst, src, count, ditherMode); -} -#endif - -MA_API void ma_pcm_s32_to_f32(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) -{ -#ifdef MA_USE_REFERENCE_CONVERSION_APIS - ma_pcm_s32_to_f32__reference(dst, src, count, ditherMode); -#else - # if defined(MA_SUPPORT_SSE2) - if (ma_has_sse2()) { - ma_pcm_s32_to_f32__sse2(dst, src, count, ditherMode); - } else - #elif defined(MA_SUPPORT_NEON) - if (ma_has_neon()) { - ma_pcm_s32_to_f32__neon(dst, src, count, ditherMode); - } else - #endif - { - ma_pcm_s32_to_f32__optimized(dst, src, count, ditherMode); - } -#endif -} - - -static MA_INLINE void ma_pcm_interleave_s32__reference(void* dst, const void** src, ma_uint64 frameCount, ma_uint32 channels) -{ - ma_int32* dst_s32 = (ma_int32*)dst; - const ma_int32** src_s32 = (const ma_int32**)src; - - ma_uint64 iFrame; - for (iFrame = 0; iFrame < frameCount; iFrame += 1) { - ma_uint32 iChannel; - for (iChannel = 0; iChannel < channels; iChannel += 1) { - dst_s32[iFrame*channels + iChannel] = src_s32[iChannel][iFrame]; - } - } -} - -static MA_INLINE void ma_pcm_interleave_s32__optimized(void* dst, const void** src, ma_uint64 frameCount, ma_uint32 channels) -{ - ma_pcm_interleave_s32__reference(dst, src, frameCount, channels); -} - -MA_API void ma_pcm_interleave_s32(void* dst, const void** src, ma_uint64 frameCount, ma_uint32 channels) -{ -#ifdef MA_USE_REFERENCE_CONVERSION_APIS - ma_pcm_interleave_s32__reference(dst, src, frameCount, channels); -#else - ma_pcm_interleave_s32__optimized(dst, src, frameCount, channels); -#endif -} - - -static MA_INLINE void ma_pcm_deinterleave_s32__reference(void** dst, const void* src, ma_uint64 frameCount, ma_uint32 channels) -{ - ma_int32** dst_s32 = (ma_int32**)dst; - const ma_int32* src_s32 = (const ma_int32*)src; - - ma_uint64 iFrame; - for (iFrame = 0; iFrame < frameCount; iFrame += 1) { - ma_uint32 iChannel; - for (iChannel = 0; iChannel < channels; iChannel += 1) { - dst_s32[iChannel][iFrame] = src_s32[iFrame*channels + iChannel]; - } - } -} - -static MA_INLINE void ma_pcm_deinterleave_s32__optimized(void** dst, const void* src, ma_uint64 frameCount, ma_uint32 channels) -{ - ma_pcm_deinterleave_s32__reference(dst, src, frameCount, channels); -} - -MA_API void ma_pcm_deinterleave_s32(void** dst, const void* src, ma_uint64 frameCount, ma_uint32 channels) -{ -#ifdef MA_USE_REFERENCE_CONVERSION_APIS - ma_pcm_deinterleave_s32__reference(dst, src, frameCount, channels); -#else - ma_pcm_deinterleave_s32__optimized(dst, src, frameCount, channels); -#endif -} - - -/* f32 */ -static MA_INLINE void ma_pcm_f32_to_u8__reference(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) -{ - ma_uint64 i; - - ma_uint8* dst_u8 = (ma_uint8*)dst; - const float* src_f32 = (const float*)src; - - float ditherMin = 0; - float ditherMax = 0; - if (ditherMode != ma_dither_mode_none) { - ditherMin = 1.0f / -128; - ditherMax = 1.0f / 127; - } - - for (i = 0; i < count; i += 1) { - float x = src_f32[i]; - x = x + ma_dither_f32(ditherMode, ditherMin, ditherMax); - x = ((x < -1) ? -1 : ((x > 1) ? 1 : x)); /* clip */ - x = x + 1; /* -1..1 to 0..2 */ - x = x * 127.5f; /* 0..2 to 0..255 */ - - dst_u8[i] = (ma_uint8)x; - } -} - -static MA_INLINE void ma_pcm_f32_to_u8__optimized(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) -{ - ma_pcm_f32_to_u8__reference(dst, src, count, ditherMode); -} - -#if defined(MA_SUPPORT_SSE2) -static MA_INLINE void ma_pcm_f32_to_u8__sse2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) -{ - ma_pcm_f32_to_u8__optimized(dst, src, count, ditherMode); -} -#endif -#if defined(MA_SUPPORT_NEON) -static MA_INLINE void ma_pcm_f32_to_u8__neon(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) -{ - ma_pcm_f32_to_u8__optimized(dst, src, count, ditherMode); -} -#endif - -MA_API void ma_pcm_f32_to_u8(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) -{ -#ifdef MA_USE_REFERENCE_CONVERSION_APIS - ma_pcm_f32_to_u8__reference(dst, src, count, ditherMode); -#else - # if defined(MA_SUPPORT_SSE2) - if (ma_has_sse2()) { - ma_pcm_f32_to_u8__sse2(dst, src, count, ditherMode); - } else - #elif defined(MA_SUPPORT_NEON) - if (ma_has_neon()) { - ma_pcm_f32_to_u8__neon(dst, src, count, ditherMode); - } else - #endif - { - ma_pcm_f32_to_u8__optimized(dst, src, count, ditherMode); - } -#endif -} - -#ifdef MA_USE_REFERENCE_CONVERSION_APIS -static MA_INLINE void ma_pcm_f32_to_s16__reference(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) -{ - ma_uint64 i; - - ma_int16* dst_s16 = (ma_int16*)dst; - const float* src_f32 = (const float*)src; - - float ditherMin = 0; - float ditherMax = 0; - if (ditherMode != ma_dither_mode_none) { - ditherMin = 1.0f / -32768; - ditherMax = 1.0f / 32767; - } - - for (i = 0; i < count; i += 1) { - float x = src_f32[i]; - x = x + ma_dither_f32(ditherMode, ditherMin, ditherMax); - x = ((x < -1) ? -1 : ((x > 1) ? 1 : x)); /* clip */ - -#if 0 - /* The accurate way. */ - x = x + 1; /* -1..1 to 0..2 */ - x = x * 32767.5f; /* 0..2 to 0..65535 */ - x = x - 32768.0f; /* 0...65535 to -32768..32767 */ -#else - /* The fast way. */ - x = x * 32767.0f; /* -1..1 to -32767..32767 */ -#endif - - dst_s16[i] = (ma_int16)x; - } -} -#else -static MA_INLINE void ma_pcm_f32_to_s16__optimized(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) -{ - ma_uint64 i; - ma_uint64 i4; - ma_uint64 count4; - - ma_int16* dst_s16 = (ma_int16*)dst; - const float* src_f32 = (const float*)src; - - float ditherMin = 0; - float ditherMax = 0; - if (ditherMode != ma_dither_mode_none) { - ditherMin = 1.0f / -32768; - ditherMax = 1.0f / 32767; - } - - /* Unrolled. */ - i = 0; - count4 = count >> 2; - for (i4 = 0; i4 < count4; i4 += 1) { - float d0 = ma_dither_f32(ditherMode, ditherMin, ditherMax); - float d1 = ma_dither_f32(ditherMode, ditherMin, ditherMax); - float d2 = ma_dither_f32(ditherMode, ditherMin, ditherMax); - float d3 = ma_dither_f32(ditherMode, ditherMin, ditherMax); - - float x0 = src_f32[i+0]; - float x1 = src_f32[i+1]; - float x2 = src_f32[i+2]; - float x3 = src_f32[i+3]; - - x0 = x0 + d0; - x1 = x1 + d1; - x2 = x2 + d2; - x3 = x3 + d3; - - x0 = ((x0 < -1) ? -1 : ((x0 > 1) ? 1 : x0)); - x1 = ((x1 < -1) ? -1 : ((x1 > 1) ? 1 : x1)); - x2 = ((x2 < -1) ? -1 : ((x2 > 1) ? 1 : x2)); - x3 = ((x3 < -1) ? -1 : ((x3 > 1) ? 1 : x3)); - - x0 = x0 * 32767.0f; - x1 = x1 * 32767.0f; - x2 = x2 * 32767.0f; - x3 = x3 * 32767.0f; - - dst_s16[i+0] = (ma_int16)x0; - dst_s16[i+1] = (ma_int16)x1; - dst_s16[i+2] = (ma_int16)x2; - dst_s16[i+3] = (ma_int16)x3; - - i += 4; - } - - /* Leftover. */ - for (; i < count; i += 1) { - float x = src_f32[i]; - x = x + ma_dither_f32(ditherMode, ditherMin, ditherMax); - x = ((x < -1) ? -1 : ((x > 1) ? 1 : x)); /* clip */ - x = x * 32767.0f; /* -1..1 to -32767..32767 */ - - dst_s16[i] = (ma_int16)x; - } -} - -#if defined(MA_SUPPORT_SSE2) -static MA_INLINE void ma_pcm_f32_to_s16__sse2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) -{ - ma_uint64 i; - ma_uint64 i8; - ma_uint64 count8; - ma_int16* dst_s16; - const float* src_f32; - float ditherMin; - float ditherMax; - - /* Both the input and output buffers need to be aligned to 16 bytes. */ - if ((((ma_uintptr)dst & 15) != 0) || (((ma_uintptr)src & 15) != 0)) { - ma_pcm_f32_to_s16__optimized(dst, src, count, ditherMode); - return; - } - - dst_s16 = (ma_int16*)dst; - src_f32 = (const float*)src; - - ditherMin = 0; - ditherMax = 0; - if (ditherMode != ma_dither_mode_none) { - ditherMin = 1.0f / -32768; - ditherMax = 1.0f / 32767; - } - - i = 0; - - /* SSE2. SSE allows us to output 8 s16's at a time which means our loop is unrolled 8 times. */ - count8 = count >> 3; - for (i8 = 0; i8 < count8; i8 += 1) { - __m128 d0; - __m128 d1; - __m128 x0; - __m128 x1; - - if (ditherMode == ma_dither_mode_none) { - d0 = _mm_set1_ps(0); - d1 = _mm_set1_ps(0); - } else if (ditherMode == ma_dither_mode_rectangle) { - d0 = _mm_set_ps( - ma_dither_f32_rectangle(ditherMin, ditherMax), - ma_dither_f32_rectangle(ditherMin, ditherMax), - ma_dither_f32_rectangle(ditherMin, ditherMax), - ma_dither_f32_rectangle(ditherMin, ditherMax) - ); - d1 = _mm_set_ps( - ma_dither_f32_rectangle(ditherMin, ditherMax), - ma_dither_f32_rectangle(ditherMin, ditherMax), - ma_dither_f32_rectangle(ditherMin, ditherMax), - ma_dither_f32_rectangle(ditherMin, ditherMax) - ); - } else { - d0 = _mm_set_ps( - ma_dither_f32_triangle(ditherMin, ditherMax), - ma_dither_f32_triangle(ditherMin, ditherMax), - ma_dither_f32_triangle(ditherMin, ditherMax), - ma_dither_f32_triangle(ditherMin, ditherMax) - ); - d1 = _mm_set_ps( - ma_dither_f32_triangle(ditherMin, ditherMax), - ma_dither_f32_triangle(ditherMin, ditherMax), - ma_dither_f32_triangle(ditherMin, ditherMax), - ma_dither_f32_triangle(ditherMin, ditherMax) - ); - } - - x0 = *((__m128*)(src_f32 + i) + 0); - x1 = *((__m128*)(src_f32 + i) + 1); - - x0 = _mm_add_ps(x0, d0); - x1 = _mm_add_ps(x1, d1); - - x0 = _mm_mul_ps(x0, _mm_set1_ps(32767.0f)); - x1 = _mm_mul_ps(x1, _mm_set1_ps(32767.0f)); - - _mm_stream_si128(((__m128i*)(dst_s16 + i)), _mm_packs_epi32(_mm_cvttps_epi32(x0), _mm_cvttps_epi32(x1))); - - i += 8; - } - - - /* Leftover. */ - for (; i < count; i += 1) { - float x = src_f32[i]; - x = x + ma_dither_f32(ditherMode, ditherMin, ditherMax); - x = ((x < -1) ? -1 : ((x > 1) ? 1 : x)); /* clip */ - x = x * 32767.0f; /* -1..1 to -32767..32767 */ - - dst_s16[i] = (ma_int16)x; - } -} -#endif /* SSE2 */ - -#if defined(MA_SUPPORT_NEON) -static MA_INLINE void ma_pcm_f32_to_s16__neon(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) -{ - ma_uint64 i; - ma_uint64 i8; - ma_uint64 count8; - ma_int16* dst_s16; - const float* src_f32; - float ditherMin; - float ditherMax; - - if (!ma_has_neon()) { - ma_pcm_f32_to_s16__optimized(dst, src, count, ditherMode); - return; - } - - /* Both the input and output buffers need to be aligned to 16 bytes. */ - if ((((ma_uintptr)dst & 15) != 0) || (((ma_uintptr)src & 15) != 0)) { - ma_pcm_f32_to_s16__optimized(dst, src, count, ditherMode); - return; - } - - dst_s16 = (ma_int16*)dst; - src_f32 = (const float*)src; - - ditherMin = 0; - ditherMax = 0; - if (ditherMode != ma_dither_mode_none) { - ditherMin = 1.0f / -32768; - ditherMax = 1.0f / 32767; - } - - i = 0; - - /* NEON. NEON allows us to output 8 s16's at a time which means our loop is unrolled 8 times. */ - count8 = count >> 3; - for (i8 = 0; i8 < count8; i8 += 1) { - float32x4_t d0; - float32x4_t d1; - float32x4_t x0; - float32x4_t x1; - int32x4_t i0; - int32x4_t i1; - - if (ditherMode == ma_dither_mode_none) { - d0 = vmovq_n_f32(0); - d1 = vmovq_n_f32(0); - } else if (ditherMode == ma_dither_mode_rectangle) { - float d0v[4]; - float d1v[4]; - - d0v[0] = ma_dither_f32_rectangle(ditherMin, ditherMax); - d0v[1] = ma_dither_f32_rectangle(ditherMin, ditherMax); - d0v[2] = ma_dither_f32_rectangle(ditherMin, ditherMax); - d0v[3] = ma_dither_f32_rectangle(ditherMin, ditherMax); - d0 = vld1q_f32(d0v); - - d1v[0] = ma_dither_f32_rectangle(ditherMin, ditherMax); - d1v[1] = ma_dither_f32_rectangle(ditherMin, ditherMax); - d1v[2] = ma_dither_f32_rectangle(ditherMin, ditherMax); - d1v[3] = ma_dither_f32_rectangle(ditherMin, ditherMax); - d1 = vld1q_f32(d1v); - } else { - float d0v[4]; - float d1v[4]; - - d0v[0] = ma_dither_f32_triangle(ditherMin, ditherMax); - d0v[1] = ma_dither_f32_triangle(ditherMin, ditherMax); - d0v[2] = ma_dither_f32_triangle(ditherMin, ditherMax); - d0v[3] = ma_dither_f32_triangle(ditherMin, ditherMax); - d0 = vld1q_f32(d0v); - - d1v[0] = ma_dither_f32_triangle(ditherMin, ditherMax); - d1v[1] = ma_dither_f32_triangle(ditherMin, ditherMax); - d1v[2] = ma_dither_f32_triangle(ditherMin, ditherMax); - d1v[3] = ma_dither_f32_triangle(ditherMin, ditherMax); - d1 = vld1q_f32(d1v); - } - - x0 = *((float32x4_t*)(src_f32 + i) + 0); - x1 = *((float32x4_t*)(src_f32 + i) + 1); - - x0 = vaddq_f32(x0, d0); - x1 = vaddq_f32(x1, d1); - - x0 = vmulq_n_f32(x0, 32767.0f); - x1 = vmulq_n_f32(x1, 32767.0f); - - i0 = vcvtq_s32_f32(x0); - i1 = vcvtq_s32_f32(x1); - *((int16x8_t*)(dst_s16 + i)) = vcombine_s16(vqmovn_s32(i0), vqmovn_s32(i1)); - - i += 8; - } - - - /* Leftover. */ - for (; i < count; i += 1) { - float x = src_f32[i]; - x = x + ma_dither_f32(ditherMode, ditherMin, ditherMax); - x = ((x < -1) ? -1 : ((x > 1) ? 1 : x)); /* clip */ - x = x * 32767.0f; /* -1..1 to -32767..32767 */ - - dst_s16[i] = (ma_int16)x; - } -} -#endif /* Neon */ -#endif /* MA_USE_REFERENCE_CONVERSION_APIS */ - -MA_API void ma_pcm_f32_to_s16(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) -{ -#ifdef MA_USE_REFERENCE_CONVERSION_APIS - ma_pcm_f32_to_s16__reference(dst, src, count, ditherMode); -#else - # if defined(MA_SUPPORT_SSE2) - if (ma_has_sse2()) { - ma_pcm_f32_to_s16__sse2(dst, src, count, ditherMode); - } else - #elif defined(MA_SUPPORT_NEON) - if (ma_has_neon()) { - ma_pcm_f32_to_s16__neon(dst, src, count, ditherMode); - } else - #endif - { - ma_pcm_f32_to_s16__optimized(dst, src, count, ditherMode); - } -#endif -} - - -static MA_INLINE void ma_pcm_f32_to_s24__reference(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) -{ - ma_uint8* dst_s24 = (ma_uint8*)dst; - const float* src_f32 = (const float*)src; - - ma_uint64 i; - for (i = 0; i < count; i += 1) { - ma_int32 r; - float x = src_f32[i]; - x = ((x < -1) ? -1 : ((x > 1) ? 1 : x)); /* clip */ - -#if 0 - /* The accurate way. */ - x = x + 1; /* -1..1 to 0..2 */ - x = x * 8388607.5f; /* 0..2 to 0..16777215 */ - x = x - 8388608.0f; /* 0..16777215 to -8388608..8388607 */ -#else - /* The fast way. */ - x = x * 8388607.0f; /* -1..1 to -8388607..8388607 */ -#endif - - r = (ma_int32)x; - dst_s24[(i*3)+0] = (ma_uint8)((r & 0x0000FF) >> 0); - dst_s24[(i*3)+1] = (ma_uint8)((r & 0x00FF00) >> 8); - dst_s24[(i*3)+2] = (ma_uint8)((r & 0xFF0000) >> 16); - } - - (void)ditherMode; /* No dithering for f32 -> s24. */ -} - -static MA_INLINE void ma_pcm_f32_to_s24__optimized(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) -{ - ma_pcm_f32_to_s24__reference(dst, src, count, ditherMode); -} - -#if defined(MA_SUPPORT_SSE2) -static MA_INLINE void ma_pcm_f32_to_s24__sse2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) -{ - ma_pcm_f32_to_s24__optimized(dst, src, count, ditherMode); -} -#endif -#if defined(MA_SUPPORT_NEON) -static MA_INLINE void ma_pcm_f32_to_s24__neon(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) -{ - ma_pcm_f32_to_s24__optimized(dst, src, count, ditherMode); -} -#endif - -MA_API void ma_pcm_f32_to_s24(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) -{ -#ifdef MA_USE_REFERENCE_CONVERSION_APIS - ma_pcm_f32_to_s24__reference(dst, src, count, ditherMode); -#else - # if defined(MA_SUPPORT_SSE2) - if (ma_has_sse2()) { - ma_pcm_f32_to_s24__sse2(dst, src, count, ditherMode); - } else - #elif defined(MA_SUPPORT_NEON) - if (ma_has_neon()) { - ma_pcm_f32_to_s24__neon(dst, src, count, ditherMode); - } else - #endif - { - ma_pcm_f32_to_s24__optimized(dst, src, count, ditherMode); - } -#endif -} - - -static MA_INLINE void ma_pcm_f32_to_s32__reference(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) -{ - ma_int32* dst_s32 = (ma_int32*)dst; - const float* src_f32 = (const float*)src; - - ma_uint32 i; - for (i = 0; i < count; i += 1) { - double x = src_f32[i]; - x = ((x < -1) ? -1 : ((x > 1) ? 1 : x)); /* clip */ - -#if 0 - /* The accurate way. */ - x = x + 1; /* -1..1 to 0..2 */ - x = x * 2147483647.5; /* 0..2 to 0..4294967295 */ - x = x - 2147483648.0; /* 0...4294967295 to -2147483648..2147483647 */ -#else - /* The fast way. */ - x = x * 2147483647.0; /* -1..1 to -2147483647..2147483647 */ -#endif - - dst_s32[i] = (ma_int32)x; - } - - (void)ditherMode; /* No dithering for f32 -> s32. */ -} - -static MA_INLINE void ma_pcm_f32_to_s32__optimized(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) -{ - ma_pcm_f32_to_s32__reference(dst, src, count, ditherMode); -} - -#if defined(MA_SUPPORT_SSE2) -static MA_INLINE void ma_pcm_f32_to_s32__sse2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) -{ - ma_pcm_f32_to_s32__optimized(dst, src, count, ditherMode); -} -#endif -#if defined(MA_SUPPORT_NEON) -static MA_INLINE void ma_pcm_f32_to_s32__neon(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) -{ - ma_pcm_f32_to_s32__optimized(dst, src, count, ditherMode); -} -#endif - -MA_API void ma_pcm_f32_to_s32(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) -{ -#ifdef MA_USE_REFERENCE_CONVERSION_APIS - ma_pcm_f32_to_s32__reference(dst, src, count, ditherMode); -#else - # if defined(MA_SUPPORT_SSE2) - if (ma_has_sse2()) { - ma_pcm_f32_to_s32__sse2(dst, src, count, ditherMode); - } else - #elif defined(MA_SUPPORT_NEON) - if (ma_has_neon()) { - ma_pcm_f32_to_s32__neon(dst, src, count, ditherMode); - } else - #endif - { - ma_pcm_f32_to_s32__optimized(dst, src, count, ditherMode); - } -#endif -} - - -MA_API void ma_pcm_f32_to_f32(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) -{ - (void)ditherMode; - - ma_copy_memory_64(dst, src, count * sizeof(float)); -} - - -static void ma_pcm_interleave_f32__reference(void* dst, const void** src, ma_uint64 frameCount, ma_uint32 channels) -{ - float* dst_f32 = (float*)dst; - const float** src_f32 = (const float**)src; - - ma_uint64 iFrame; - for (iFrame = 0; iFrame < frameCount; iFrame += 1) { - ma_uint32 iChannel; - for (iChannel = 0; iChannel < channels; iChannel += 1) { - dst_f32[iFrame*channels + iChannel] = src_f32[iChannel][iFrame]; - } - } -} - -static void ma_pcm_interleave_f32__optimized(void* dst, const void** src, ma_uint64 frameCount, ma_uint32 channels) -{ - ma_pcm_interleave_f32__reference(dst, src, frameCount, channels); -} - -MA_API void ma_pcm_interleave_f32(void* dst, const void** src, ma_uint64 frameCount, ma_uint32 channels) -{ -#ifdef MA_USE_REFERENCE_CONVERSION_APIS - ma_pcm_interleave_f32__reference(dst, src, frameCount, channels); -#else - ma_pcm_interleave_f32__optimized(dst, src, frameCount, channels); -#endif -} - - -static void ma_pcm_deinterleave_f32__reference(void** dst, const void* src, ma_uint64 frameCount, ma_uint32 channels) -{ - float** dst_f32 = (float**)dst; - const float* src_f32 = (const float*)src; - - ma_uint64 iFrame; - for (iFrame = 0; iFrame < frameCount; iFrame += 1) { - ma_uint32 iChannel; - for (iChannel = 0; iChannel < channels; iChannel += 1) { - dst_f32[iChannel][iFrame] = src_f32[iFrame*channels + iChannel]; - } - } -} - -static void ma_pcm_deinterleave_f32__optimized(void** dst, const void* src, ma_uint64 frameCount, ma_uint32 channels) -{ - ma_pcm_deinterleave_f32__reference(dst, src, frameCount, channels); -} - -MA_API void ma_pcm_deinterleave_f32(void** dst, const void* src, ma_uint64 frameCount, ma_uint32 channels) -{ -#ifdef MA_USE_REFERENCE_CONVERSION_APIS - ma_pcm_deinterleave_f32__reference(dst, src, frameCount, channels); -#else - ma_pcm_deinterleave_f32__optimized(dst, src, frameCount, channels); -#endif -} - - -MA_API void ma_pcm_convert(void* pOut, ma_format formatOut, const void* pIn, ma_format formatIn, ma_uint64 sampleCount, ma_dither_mode ditherMode) -{ - if (formatOut == formatIn) { - ma_copy_memory_64(pOut, pIn, sampleCount * ma_get_bytes_per_sample(formatOut)); - return; - } - - switch (formatIn) - { - case ma_format_u8: - { - switch (formatOut) - { - case ma_format_s16: ma_pcm_u8_to_s16(pOut, pIn, sampleCount, ditherMode); return; - case ma_format_s24: ma_pcm_u8_to_s24(pOut, pIn, sampleCount, ditherMode); return; - case ma_format_s32: ma_pcm_u8_to_s32(pOut, pIn, sampleCount, ditherMode); return; - case ma_format_f32: ma_pcm_u8_to_f32(pOut, pIn, sampleCount, ditherMode); return; - default: break; - } - } break; - - case ma_format_s16: - { - switch (formatOut) - { - case ma_format_u8: ma_pcm_s16_to_u8( pOut, pIn, sampleCount, ditherMode); return; - case ma_format_s24: ma_pcm_s16_to_s24(pOut, pIn, sampleCount, ditherMode); return; - case ma_format_s32: ma_pcm_s16_to_s32(pOut, pIn, sampleCount, ditherMode); return; - case ma_format_f32: ma_pcm_s16_to_f32(pOut, pIn, sampleCount, ditherMode); return; - default: break; - } - } break; - - case ma_format_s24: - { - switch (formatOut) - { - case ma_format_u8: ma_pcm_s24_to_u8( pOut, pIn, sampleCount, ditherMode); return; - case ma_format_s16: ma_pcm_s24_to_s16(pOut, pIn, sampleCount, ditherMode); return; - case ma_format_s32: ma_pcm_s24_to_s32(pOut, pIn, sampleCount, ditherMode); return; - case ma_format_f32: ma_pcm_s24_to_f32(pOut, pIn, sampleCount, ditherMode); return; - default: break; - } - } break; - - case ma_format_s32: - { - switch (formatOut) - { - case ma_format_u8: ma_pcm_s32_to_u8( pOut, pIn, sampleCount, ditherMode); return; - case ma_format_s16: ma_pcm_s32_to_s16(pOut, pIn, sampleCount, ditherMode); return; - case ma_format_s24: ma_pcm_s32_to_s24(pOut, pIn, sampleCount, ditherMode); return; - case ma_format_f32: ma_pcm_s32_to_f32(pOut, pIn, sampleCount, ditherMode); return; - default: break; - } - } break; - - case ma_format_f32: - { - switch (formatOut) - { - case ma_format_u8: ma_pcm_f32_to_u8( pOut, pIn, sampleCount, ditherMode); return; - case ma_format_s16: ma_pcm_f32_to_s16(pOut, pIn, sampleCount, ditherMode); return; - case ma_format_s24: ma_pcm_f32_to_s24(pOut, pIn, sampleCount, ditherMode); return; - case ma_format_s32: ma_pcm_f32_to_s32(pOut, pIn, sampleCount, ditherMode); return; - default: break; - } - } break; - - default: break; - } -} - -MA_API void ma_convert_pcm_frames_format(void* pOut, ma_format formatOut, const void* pIn, ma_format formatIn, ma_uint64 frameCount, ma_uint32 channels, ma_dither_mode ditherMode) -{ - ma_pcm_convert(pOut, formatOut, pIn, formatIn, frameCount * channels, ditherMode); -} - -MA_API void ma_deinterleave_pcm_frames(ma_format format, ma_uint32 channels, ma_uint64 frameCount, const void* pInterleavedPCMFrames, void** ppDeinterleavedPCMFrames) -{ - if (pInterleavedPCMFrames == NULL || ppDeinterleavedPCMFrames == NULL) { - return; /* Invalid args. */ - } - - /* For efficiency we do this per format. */ - switch (format) { - case ma_format_s16: - { - const ma_int16* pSrcS16 = (const ma_int16*)pInterleavedPCMFrames; - ma_uint64 iPCMFrame; - for (iPCMFrame = 0; iPCMFrame < frameCount; ++iPCMFrame) { - ma_uint32 iChannel; - for (iChannel = 0; iChannel < channels; ++iChannel) { - ma_int16* pDstS16 = (ma_int16*)ppDeinterleavedPCMFrames[iChannel]; - pDstS16[iPCMFrame] = pSrcS16[iPCMFrame*channels+iChannel]; - } - } - } break; - - case ma_format_f32: - { - const float* pSrcF32 = (const float*)pInterleavedPCMFrames; - ma_uint64 iPCMFrame; - for (iPCMFrame = 0; iPCMFrame < frameCount; ++iPCMFrame) { - ma_uint32 iChannel; - for (iChannel = 0; iChannel < channels; ++iChannel) { - float* pDstF32 = (float*)ppDeinterleavedPCMFrames[iChannel]; - pDstF32[iPCMFrame] = pSrcF32[iPCMFrame*channels+iChannel]; - } - } - } break; - - default: - { - ma_uint32 sampleSizeInBytes = ma_get_bytes_per_sample(format); - ma_uint64 iPCMFrame; - for (iPCMFrame = 0; iPCMFrame < frameCount; ++iPCMFrame) { - ma_uint32 iChannel; - for (iChannel = 0; iChannel < channels; ++iChannel) { - void* pDst = ma_offset_ptr(ppDeinterleavedPCMFrames[iChannel], iPCMFrame*sampleSizeInBytes); - const void* pSrc = ma_offset_ptr(pInterleavedPCMFrames, (iPCMFrame*channels+iChannel)*sampleSizeInBytes); - memcpy(pDst, pSrc, sampleSizeInBytes); - } - } - } break; - } -} - -MA_API void ma_interleave_pcm_frames(ma_format format, ma_uint32 channels, ma_uint64 frameCount, const void** ppDeinterleavedPCMFrames, void* pInterleavedPCMFrames) -{ - switch (format) - { - case ma_format_s16: - { - ma_int16* pDstS16 = (ma_int16*)pInterleavedPCMFrames; - ma_uint64 iPCMFrame; - for (iPCMFrame = 0; iPCMFrame < frameCount; ++iPCMFrame) { - ma_uint32 iChannel; - for (iChannel = 0; iChannel < channels; ++iChannel) { - const ma_int16* pSrcS16 = (const ma_int16*)ppDeinterleavedPCMFrames[iChannel]; - pDstS16[iPCMFrame*channels+iChannel] = pSrcS16[iPCMFrame]; - } - } - } break; - - case ma_format_f32: - { - float* pDstF32 = (float*)pInterleavedPCMFrames; - ma_uint64 iPCMFrame; - for (iPCMFrame = 0; iPCMFrame < frameCount; ++iPCMFrame) { - ma_uint32 iChannel; - for (iChannel = 0; iChannel < channels; ++iChannel) { - const float* pSrcF32 = (const float*)ppDeinterleavedPCMFrames[iChannel]; - pDstF32[iPCMFrame*channels+iChannel] = pSrcF32[iPCMFrame]; - } - } - } break; - - default: - { - ma_uint32 sampleSizeInBytes = ma_get_bytes_per_sample(format); - ma_uint64 iPCMFrame; - for (iPCMFrame = 0; iPCMFrame < frameCount; ++iPCMFrame) { - ma_uint32 iChannel; - for (iChannel = 0; iChannel < channels; ++iChannel) { - void* pDst = ma_offset_ptr(pInterleavedPCMFrames, (iPCMFrame*channels+iChannel)*sampleSizeInBytes); - const void* pSrc = ma_offset_ptr(ppDeinterleavedPCMFrames[iChannel], iPCMFrame*sampleSizeInBytes); - memcpy(pDst, pSrc, sampleSizeInBytes); - } - } - } break; - } -} - - -/************************************************************************************************************************************************************** - -Biquad Filter - -**************************************************************************************************************************************************************/ -#ifndef MA_BIQUAD_FIXED_POINT_SHIFT -#define MA_BIQUAD_FIXED_POINT_SHIFT 14 -#endif - -static ma_int32 ma_biquad_float_to_fp(double x) -{ - return (ma_int32)(x * (1 << MA_BIQUAD_FIXED_POINT_SHIFT)); -} - -MA_API ma_biquad_config ma_biquad_config_init(ma_format format, ma_uint32 channels, double b0, double b1, double b2, double a0, double a1, double a2) -{ - ma_biquad_config config; - - MA_ZERO_OBJECT(&config); - config.format = format; - config.channels = channels; - config.b0 = b0; - config.b1 = b1; - config.b2 = b2; - config.a0 = a0; - config.a1 = a1; - config.a2 = a2; - - return config; -} - - -typedef struct -{ - size_t sizeInBytes; - size_t r1Offset; - size_t r2Offset; -} ma_biquad_heap_layout; - -static ma_result ma_biquad_get_heap_layout(const ma_biquad_config* pConfig, ma_biquad_heap_layout* pHeapLayout) -{ - MA_ASSERT(pHeapLayout != NULL); - - MA_ZERO_OBJECT(pHeapLayout); - - if (pConfig == NULL) { - return MA_INVALID_ARGS; - } - - if (pConfig->channels == 0) { - return MA_INVALID_ARGS; - } - - pHeapLayout->sizeInBytes = 0; - - /* R0 */ - pHeapLayout->r1Offset = pHeapLayout->sizeInBytes; - pHeapLayout->sizeInBytes += sizeof(ma_biquad_coefficient) * pConfig->channels; - - /* R1 */ - pHeapLayout->r2Offset = pHeapLayout->sizeInBytes; - pHeapLayout->sizeInBytes += sizeof(ma_biquad_coefficient) * pConfig->channels; - - /* Make sure allocation size is aligned. */ - pHeapLayout->sizeInBytes = ma_align_64(pHeapLayout->sizeInBytes); - - return MA_SUCCESS; -} - -MA_API ma_result ma_biquad_get_heap_size(const ma_biquad_config* pConfig, size_t* pHeapSizeInBytes) -{ - ma_result result; - ma_biquad_heap_layout heapLayout; - - if (pHeapSizeInBytes == NULL) { - return MA_INVALID_ARGS; - } - - *pHeapSizeInBytes = 0; - - result = ma_biquad_get_heap_layout(pConfig, &heapLayout); - if (result != MA_SUCCESS) { - return result; - } - - *pHeapSizeInBytes = heapLayout.sizeInBytes; - - return MA_SUCCESS; -} - -MA_API ma_result ma_biquad_init_preallocated(const ma_biquad_config* pConfig, void* pHeap, ma_biquad* pBQ) -{ - ma_result result; - ma_biquad_heap_layout heapLayout; - - if (pBQ == NULL) { - return MA_INVALID_ARGS; - } - - MA_ZERO_OBJECT(pBQ); - - result = ma_biquad_get_heap_layout(pConfig, &heapLayout); - if (result != MA_SUCCESS) { - return result; - } - - pBQ->_pHeap = pHeap; - MA_ZERO_MEMORY(pHeap, heapLayout.sizeInBytes); - - pBQ->pR1 = (ma_biquad_coefficient*)ma_offset_ptr(pHeap, heapLayout.r1Offset); - pBQ->pR2 = (ma_biquad_coefficient*)ma_offset_ptr(pHeap, heapLayout.r2Offset); - - return ma_biquad_reinit(pConfig, pBQ); -} - -MA_API ma_result ma_biquad_init(const ma_biquad_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_biquad* pBQ) -{ - ma_result result; - size_t heapSizeInBytes; - void* pHeap; - - result = ma_biquad_get_heap_size(pConfig, &heapSizeInBytes); - if (result != MA_SUCCESS) { - return result; - } - - if (heapSizeInBytes > 0) { - pHeap = ma_malloc(heapSizeInBytes, pAllocationCallbacks); - if (pHeap == NULL) { - return MA_OUT_OF_MEMORY; - } - } else { - pHeap = NULL; - } - - result = ma_biquad_init_preallocated(pConfig, pHeap, pBQ); - if (result != MA_SUCCESS) { - ma_free(pHeap, pAllocationCallbacks); - return result; - } - - pBQ->_ownsHeap = MA_TRUE; - return MA_SUCCESS; -} - -MA_API void ma_biquad_uninit(ma_biquad* pBQ, const ma_allocation_callbacks* pAllocationCallbacks) -{ - if (pBQ == NULL) { - return; - } - - if (pBQ->_ownsHeap) { - ma_free(pBQ->_pHeap, pAllocationCallbacks); - } -} - -MA_API ma_result ma_biquad_reinit(const ma_biquad_config* pConfig, ma_biquad* pBQ) -{ - if (pBQ == NULL || pConfig == NULL) { - return MA_INVALID_ARGS; - } - - if (pConfig->a0 == 0) { - return MA_INVALID_ARGS; /* Division by zero. */ - } - - /* Only supporting f32 and s16. */ - if (pConfig->format != ma_format_f32 && pConfig->format != ma_format_s16) { - return MA_INVALID_ARGS; - } - - /* The format cannot be changed after initialization. */ - if (pBQ->format != ma_format_unknown && pBQ->format != pConfig->format) { - return MA_INVALID_OPERATION; - } - - /* The channel count cannot be changed after initialization. */ - if (pBQ->channels != 0 && pBQ->channels != pConfig->channels) { - return MA_INVALID_OPERATION; - } - - - pBQ->format = pConfig->format; - pBQ->channels = pConfig->channels; - - /* Normalize. */ - if (pConfig->format == ma_format_f32) { - pBQ->b0.f32 = (float)(pConfig->b0 / pConfig->a0); - pBQ->b1.f32 = (float)(pConfig->b1 / pConfig->a0); - pBQ->b2.f32 = (float)(pConfig->b2 / pConfig->a0); - pBQ->a1.f32 = (float)(pConfig->a1 / pConfig->a0); - pBQ->a2.f32 = (float)(pConfig->a2 / pConfig->a0); - } else { - pBQ->b0.s32 = ma_biquad_float_to_fp(pConfig->b0 / pConfig->a0); - pBQ->b1.s32 = ma_biquad_float_to_fp(pConfig->b1 / pConfig->a0); - pBQ->b2.s32 = ma_biquad_float_to_fp(pConfig->b2 / pConfig->a0); - pBQ->a1.s32 = ma_biquad_float_to_fp(pConfig->a1 / pConfig->a0); - pBQ->a2.s32 = ma_biquad_float_to_fp(pConfig->a2 / pConfig->a0); - } - - return MA_SUCCESS; -} - -MA_API ma_result ma_biquad_clear_cache(ma_biquad* pBQ) -{ - if (pBQ == NULL) { - return MA_INVALID_ARGS; - } - - if (pBQ->format == ma_format_f32) { - pBQ->pR1->f32 = 0; - pBQ->pR2->f32 = 0; - } else { - pBQ->pR1->s32 = 0; - pBQ->pR2->s32 = 0; - } - - return MA_SUCCESS; -} - -static MA_INLINE void ma_biquad_process_pcm_frame_f32__direct_form_2_transposed(ma_biquad* pBQ, float* pY, const float* pX) -{ - ma_uint32 c; - const ma_uint32 channels = pBQ->channels; - const float b0 = pBQ->b0.f32; - const float b1 = pBQ->b1.f32; - const float b2 = pBQ->b2.f32; - const float a1 = pBQ->a1.f32; - const float a2 = pBQ->a2.f32; - - MA_ASSUME(channels > 0); - for (c = 0; c < channels; c += 1) { - float r1 = pBQ->pR1[c].f32; - float r2 = pBQ->pR2[c].f32; - float x = pX[c]; - float y; - - y = b0*x + r1; - r1 = b1*x - a1*y + r2; - r2 = b2*x - a2*y; - - pY[c] = y; - pBQ->pR1[c].f32 = r1; - pBQ->pR2[c].f32 = r2; - } -} - -static MA_INLINE void ma_biquad_process_pcm_frame_f32(ma_biquad* pBQ, float* pY, const float* pX) -{ - ma_biquad_process_pcm_frame_f32__direct_form_2_transposed(pBQ, pY, pX); -} - -static MA_INLINE void ma_biquad_process_pcm_frame_s16__direct_form_2_transposed(ma_biquad* pBQ, ma_int16* pY, const ma_int16* pX) -{ - ma_uint32 c; - const ma_uint32 channels = pBQ->channels; - const ma_int32 b0 = pBQ->b0.s32; - const ma_int32 b1 = pBQ->b1.s32; - const ma_int32 b2 = pBQ->b2.s32; - const ma_int32 a1 = pBQ->a1.s32; - const ma_int32 a2 = pBQ->a2.s32; - - MA_ASSUME(channels > 0); - for (c = 0; c < channels; c += 1) { - ma_int32 r1 = pBQ->pR1[c].s32; - ma_int32 r2 = pBQ->pR2[c].s32; - ma_int32 x = pX[c]; - ma_int32 y; - - y = (b0*x + r1) >> MA_BIQUAD_FIXED_POINT_SHIFT; - r1 = (b1*x - a1*y + r2); - r2 = (b2*x - a2*y); - - pY[c] = (ma_int16)ma_clamp(y, -32768, 32767); - pBQ->pR1[c].s32 = r1; - pBQ->pR2[c].s32 = r2; - } -} - -static MA_INLINE void ma_biquad_process_pcm_frame_s16(ma_biquad* pBQ, ma_int16* pY, const ma_int16* pX) -{ - ma_biquad_process_pcm_frame_s16__direct_form_2_transposed(pBQ, pY, pX); -} - -MA_API ma_result ma_biquad_process_pcm_frames(ma_biquad* pBQ, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount) -{ - ma_uint32 n; - - if (pBQ == NULL || pFramesOut == NULL || pFramesIn == NULL) { - return MA_INVALID_ARGS; - } - - /* Note that the logic below needs to support in-place filtering. That is, it must support the case where pFramesOut and pFramesIn are the same. */ - - if (pBQ->format == ma_format_f32) { - /* */ float* pY = ( float*)pFramesOut; - const float* pX = (const float*)pFramesIn; - - for (n = 0; n < frameCount; n += 1) { - ma_biquad_process_pcm_frame_f32__direct_form_2_transposed(pBQ, pY, pX); - pY += pBQ->channels; - pX += pBQ->channels; - } - } else if (pBQ->format == ma_format_s16) { - /* */ ma_int16* pY = ( ma_int16*)pFramesOut; - const ma_int16* pX = (const ma_int16*)pFramesIn; - - for (n = 0; n < frameCount; n += 1) { - ma_biquad_process_pcm_frame_s16__direct_form_2_transposed(pBQ, pY, pX); - pY += pBQ->channels; - pX += pBQ->channels; - } - } else { - MA_ASSERT(MA_FALSE); - return MA_INVALID_ARGS; /* Format not supported. Should never hit this because it's checked in ma_biquad_init() and ma_biquad_reinit(). */ - } - - return MA_SUCCESS; -} - -MA_API ma_uint32 ma_biquad_get_latency(const ma_biquad* pBQ) -{ - if (pBQ == NULL) { - return 0; - } - - return 2; -} - - -/************************************************************************************************************************************************************** - -Low-Pass Filter - -**************************************************************************************************************************************************************/ -MA_API ma_lpf1_config ma_lpf1_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRate, double cutoffFrequency) -{ - ma_lpf1_config config; - - MA_ZERO_OBJECT(&config); - config.format = format; - config.channels = channels; - config.sampleRate = sampleRate; - config.cutoffFrequency = cutoffFrequency; - config.q = 0.5; - - return config; -} - -MA_API ma_lpf2_config ma_lpf2_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRate, double cutoffFrequency, double q) -{ - ma_lpf2_config config; - - MA_ZERO_OBJECT(&config); - config.format = format; - config.channels = channels; - config.sampleRate = sampleRate; - config.cutoffFrequency = cutoffFrequency; - config.q = q; - - /* Q cannot be 0 or else it'll result in a division by 0. In this case just default to 0.707107. */ - if (config.q == 0) { - config.q = 0.707107; - } - - return config; -} - - -typedef struct -{ - size_t sizeInBytes; - size_t r1Offset; -} ma_lpf1_heap_layout; - -static ma_result ma_lpf1_get_heap_layout(const ma_lpf1_config* pConfig, ma_lpf1_heap_layout* pHeapLayout) -{ - MA_ASSERT(pHeapLayout != NULL); - - MA_ZERO_OBJECT(pHeapLayout); - - if (pConfig == NULL) { - return MA_INVALID_ARGS; - } - - if (pConfig->channels == 0) { - return MA_INVALID_ARGS; - } - - pHeapLayout->sizeInBytes = 0; - - /* R1 */ - pHeapLayout->r1Offset = pHeapLayout->sizeInBytes; - pHeapLayout->sizeInBytes += sizeof(ma_biquad_coefficient) * pConfig->channels; - - /* Make sure allocation size is aligned. */ - pHeapLayout->sizeInBytes = ma_align_64(pHeapLayout->sizeInBytes); - - return MA_SUCCESS; -} - -MA_API ma_result ma_lpf1_get_heap_size(const ma_lpf1_config* pConfig, size_t* pHeapSizeInBytes) -{ - ma_result result; - ma_lpf1_heap_layout heapLayout; - - if (pHeapSizeInBytes == NULL) { - return MA_INVALID_ARGS; - } - - result = ma_lpf1_get_heap_layout(pConfig, &heapLayout); - if (result != MA_SUCCESS) { - return result; - } - - *pHeapSizeInBytes = heapLayout.sizeInBytes; - - return MA_SUCCESS; -} - -MA_API ma_result ma_lpf1_init_preallocated(const ma_lpf1_config* pConfig, void* pHeap, ma_lpf1* pLPF) -{ - ma_result result; - ma_lpf1_heap_layout heapLayout; - - if (pLPF == NULL) { - return MA_INVALID_ARGS; - } - - MA_ZERO_OBJECT(pLPF); - - result = ma_lpf1_get_heap_layout(pConfig, &heapLayout); - if (result != MA_SUCCESS) { - return result; - } - - pLPF->_pHeap = pHeap; - MA_ZERO_MEMORY(pHeap, heapLayout.sizeInBytes); - - pLPF->pR1 = (ma_biquad_coefficient*)ma_offset_ptr(pHeap, heapLayout.r1Offset); - - return ma_lpf1_reinit(pConfig, pLPF); -} - -MA_API ma_result ma_lpf1_init(const ma_lpf1_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_lpf1* pLPF) -{ - ma_result result; - size_t heapSizeInBytes; - void* pHeap; - - result = ma_lpf1_get_heap_size(pConfig, &heapSizeInBytes); - if (result != MA_SUCCESS) { - return result; - } - - if (heapSizeInBytes > 0) { - pHeap = ma_malloc(heapSizeInBytes, pAllocationCallbacks); - if (pHeap == NULL) { - return MA_OUT_OF_MEMORY; - } - } else { - pHeap = NULL; - } - - result = ma_lpf1_init_preallocated(pConfig, pHeap, pLPF); - if (result != MA_SUCCESS) { - ma_free(pHeap, pAllocationCallbacks); - return result; - } - - pLPF->_ownsHeap = MA_TRUE; - return MA_SUCCESS; -} - -MA_API void ma_lpf1_uninit(ma_lpf1* pLPF, const ma_allocation_callbacks* pAllocationCallbacks) -{ - if (pLPF == NULL) { - return; - } - - if (pLPF->_ownsHeap) { - ma_free(pLPF->_pHeap, pAllocationCallbacks); - } -} - -MA_API ma_result ma_lpf1_reinit(const ma_lpf1_config* pConfig, ma_lpf1* pLPF) -{ - double a; - - if (pLPF == NULL || pConfig == NULL) { - return MA_INVALID_ARGS; - } - - /* Only supporting f32 and s16. */ - if (pConfig->format != ma_format_f32 && pConfig->format != ma_format_s16) { - return MA_INVALID_ARGS; - } - - /* The format cannot be changed after initialization. */ - if (pLPF->format != ma_format_unknown && pLPF->format != pConfig->format) { - return MA_INVALID_OPERATION; - } - - /* The channel count cannot be changed after initialization. */ - if (pLPF->channels != 0 && pLPF->channels != pConfig->channels) { - return MA_INVALID_OPERATION; - } - - pLPF->format = pConfig->format; - pLPF->channels = pConfig->channels; - - a = ma_expd(-2 * MA_PI_D * pConfig->cutoffFrequency / pConfig->sampleRate); - if (pConfig->format == ma_format_f32) { - pLPF->a.f32 = (float)a; - } else { - pLPF->a.s32 = ma_biquad_float_to_fp(a); - } - - return MA_SUCCESS; -} - -MA_API ma_result ma_lpf1_clear_cache(ma_lpf1* pLPF) -{ - if (pLPF == NULL) { - return MA_INVALID_ARGS; - } - - if (pLPF->format == ma_format_f32) { - pLPF->a.f32 = 0; - } else { - pLPF->a.s32 = 0; - } - - return MA_SUCCESS; -} - -static MA_INLINE void ma_lpf1_process_pcm_frame_f32(ma_lpf1* pLPF, float* pY, const float* pX) -{ - ma_uint32 c; - const ma_uint32 channels = pLPF->channels; - const float a = pLPF->a.f32; - const float b = 1 - a; - - MA_ASSUME(channels > 0); - for (c = 0; c < channels; c += 1) { - float r1 = pLPF->pR1[c].f32; - float x = pX[c]; - float y; - - y = b*x + a*r1; - - pY[c] = y; - pLPF->pR1[c].f32 = y; - } -} - -static MA_INLINE void ma_lpf1_process_pcm_frame_s16(ma_lpf1* pLPF, ma_int16* pY, const ma_int16* pX) -{ - ma_uint32 c; - const ma_uint32 channels = pLPF->channels; - const ma_int32 a = pLPF->a.s32; - const ma_int32 b = ((1 << MA_BIQUAD_FIXED_POINT_SHIFT) - a); - - MA_ASSUME(channels > 0); - for (c = 0; c < channels; c += 1) { - ma_int32 r1 = pLPF->pR1[c].s32; - ma_int32 x = pX[c]; - ma_int32 y; - - y = (b*x + a*r1) >> MA_BIQUAD_FIXED_POINT_SHIFT; - - pY[c] = (ma_int16)y; - pLPF->pR1[c].s32 = (ma_int32)y; - } -} - -MA_API ma_result ma_lpf1_process_pcm_frames(ma_lpf1* pLPF, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount) -{ - ma_uint32 n; - - if (pLPF == NULL || pFramesOut == NULL || pFramesIn == NULL) { - return MA_INVALID_ARGS; - } - - /* Note that the logic below needs to support in-place filtering. That is, it must support the case where pFramesOut and pFramesIn are the same. */ - - if (pLPF->format == ma_format_f32) { - /* */ float* pY = ( float*)pFramesOut; - const float* pX = (const float*)pFramesIn; - - for (n = 0; n < frameCount; n += 1) { - ma_lpf1_process_pcm_frame_f32(pLPF, pY, pX); - pY += pLPF->channels; - pX += pLPF->channels; - } - } else if (pLPF->format == ma_format_s16) { - /* */ ma_int16* pY = ( ma_int16*)pFramesOut; - const ma_int16* pX = (const ma_int16*)pFramesIn; - - for (n = 0; n < frameCount; n += 1) { - ma_lpf1_process_pcm_frame_s16(pLPF, pY, pX); - pY += pLPF->channels; - pX += pLPF->channels; - } - } else { - MA_ASSERT(MA_FALSE); - return MA_INVALID_ARGS; /* Format not supported. Should never hit this because it's checked in ma_biquad_init() and ma_biquad_reinit(). */ - } - - return MA_SUCCESS; -} - -MA_API ma_uint32 ma_lpf1_get_latency(const ma_lpf1* pLPF) -{ - if (pLPF == NULL) { - return 0; - } - - return 1; -} - - -static MA_INLINE ma_biquad_config ma_lpf2__get_biquad_config(const ma_lpf2_config* pConfig) -{ - ma_biquad_config bqConfig; - double q; - double w; - double s; - double c; - double a; - - MA_ASSERT(pConfig != NULL); - - q = pConfig->q; - w = 2 * MA_PI_D * pConfig->cutoffFrequency / pConfig->sampleRate; - s = ma_sind(w); - c = ma_cosd(w); - a = s / (2*q); - - bqConfig.b0 = (1 - c) / 2; - bqConfig.b1 = 1 - c; - bqConfig.b2 = (1 - c) / 2; - bqConfig.a0 = 1 + a; - bqConfig.a1 = -2 * c; - bqConfig.a2 = 1 - a; - - bqConfig.format = pConfig->format; - bqConfig.channels = pConfig->channels; - - return bqConfig; -} - -MA_API ma_result ma_lpf2_get_heap_size(const ma_lpf2_config* pConfig, size_t* pHeapSizeInBytes) -{ - ma_biquad_config bqConfig; - bqConfig = ma_lpf2__get_biquad_config(pConfig); - - return ma_biquad_get_heap_size(&bqConfig, pHeapSizeInBytes); -} - -MA_API ma_result ma_lpf2_init_preallocated(const ma_lpf2_config* pConfig, void* pHeap, ma_lpf2* pLPF) -{ - ma_result result; - ma_biquad_config bqConfig; - - if (pLPF == NULL) { - return MA_INVALID_ARGS; - } - - MA_ZERO_OBJECT(pLPF); - - if (pConfig == NULL) { - return MA_INVALID_ARGS; - } - - bqConfig = ma_lpf2__get_biquad_config(pConfig); - result = ma_biquad_init_preallocated(&bqConfig, pHeap, &pLPF->bq); - if (result != MA_SUCCESS) { - return result; - } - - return MA_SUCCESS; -} - -MA_API ma_result ma_lpf2_init(const ma_lpf2_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_lpf2* pLPF) -{ - ma_result result; - size_t heapSizeInBytes; - void* pHeap; - - result = ma_lpf2_get_heap_size(pConfig, &heapSizeInBytes); - if (result != MA_SUCCESS) { - return result; - } - - if (heapSizeInBytes > 0) { - pHeap = ma_malloc(heapSizeInBytes, pAllocationCallbacks); - if (pHeap == NULL) { - return MA_OUT_OF_MEMORY; - } - } else { - pHeap = NULL; - } - - result = ma_lpf2_init_preallocated(pConfig, pHeap, pLPF); - if (result != MA_SUCCESS) { - ma_free(pHeap, pAllocationCallbacks); - return result; - } - - pLPF->bq._ownsHeap = MA_TRUE; /* <-- This will cause the biquad to take ownership of the heap and free it when it's uninitialized. */ - return MA_SUCCESS; -} - -MA_API void ma_lpf2_uninit(ma_lpf2* pLPF, const ma_allocation_callbacks* pAllocationCallbacks) -{ - if (pLPF == NULL) { - return; - } - - ma_biquad_uninit(&pLPF->bq, pAllocationCallbacks); /* <-- This will free the heap allocation. */ -} - -MA_API ma_result ma_lpf2_reinit(const ma_lpf2_config* pConfig, ma_lpf2* pLPF) -{ - ma_result result; - ma_biquad_config bqConfig; - - if (pLPF == NULL || pConfig == NULL) { - return MA_INVALID_ARGS; - } - - bqConfig = ma_lpf2__get_biquad_config(pConfig); - result = ma_biquad_reinit(&bqConfig, &pLPF->bq); - if (result != MA_SUCCESS) { - return result; - } - - return MA_SUCCESS; -} - -MA_API ma_result ma_lpf2_clear_cache(ma_lpf2* pLPF) -{ - if (pLPF == NULL) { - return MA_INVALID_ARGS; - } - - ma_biquad_clear_cache(&pLPF->bq); - - return MA_SUCCESS; -} - -static MA_INLINE void ma_lpf2_process_pcm_frame_s16(ma_lpf2* pLPF, ma_int16* pFrameOut, const ma_int16* pFrameIn) -{ - ma_biquad_process_pcm_frame_s16(&pLPF->bq, pFrameOut, pFrameIn); -} - -static MA_INLINE void ma_lpf2_process_pcm_frame_f32(ma_lpf2* pLPF, float* pFrameOut, const float* pFrameIn) -{ - ma_biquad_process_pcm_frame_f32(&pLPF->bq, pFrameOut, pFrameIn); -} - -MA_API ma_result ma_lpf2_process_pcm_frames(ma_lpf2* pLPF, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount) -{ - if (pLPF == NULL) { - return MA_INVALID_ARGS; - } - - return ma_biquad_process_pcm_frames(&pLPF->bq, pFramesOut, pFramesIn, frameCount); -} - -MA_API ma_uint32 ma_lpf2_get_latency(const ma_lpf2* pLPF) -{ - if (pLPF == NULL) { - return 0; - } - - return ma_biquad_get_latency(&pLPF->bq); -} - - -MA_API ma_lpf_config ma_lpf_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRate, double cutoffFrequency, ma_uint32 order) -{ - ma_lpf_config config; - - MA_ZERO_OBJECT(&config); - config.format = format; - config.channels = channels; - config.sampleRate = sampleRate; - config.cutoffFrequency = cutoffFrequency; - config.order = ma_min(order, MA_MAX_FILTER_ORDER); - - return config; -} - - -typedef struct -{ - size_t sizeInBytes; - size_t lpf1Offset; - size_t lpf2Offset; /* Offset of the first second order filter. Subsequent filters will come straight after, and will each have the same heap size. */ -} ma_lpf_heap_layout; - -static void ma_lpf_calculate_sub_lpf_counts(ma_uint32 order, ma_uint32* pLPF1Count, ma_uint32* pLPF2Count) -{ - MA_ASSERT(pLPF1Count != NULL); - MA_ASSERT(pLPF2Count != NULL); - - *pLPF1Count = order % 2; - *pLPF2Count = order / 2; -} - -static ma_result ma_lpf_get_heap_layout(const ma_lpf_config* pConfig, ma_lpf_heap_layout* pHeapLayout) -{ - ma_result result; - ma_uint32 lpf1Count; - ma_uint32 lpf2Count; - ma_uint32 ilpf1; - ma_uint32 ilpf2; - - MA_ASSERT(pHeapLayout != NULL); - - MA_ZERO_OBJECT(pHeapLayout); - - if (pConfig == NULL) { - return MA_INVALID_ARGS; - } - - if (pConfig->channels == 0) { - return MA_INVALID_ARGS; - } - - if (pConfig->order > MA_MAX_FILTER_ORDER) { - return MA_INVALID_ARGS; - } - - ma_lpf_calculate_sub_lpf_counts(pConfig->order, &lpf1Count, &lpf2Count); - - pHeapLayout->sizeInBytes = 0; - - /* LPF 1 */ - pHeapLayout->lpf1Offset = pHeapLayout->sizeInBytes; - for (ilpf1 = 0; ilpf1 < lpf1Count; ilpf1 += 1) { - size_t lpf1HeapSizeInBytes; - ma_lpf1_config lpf1Config = ma_lpf1_config_init(pConfig->format, pConfig->channels, pConfig->sampleRate, pConfig->cutoffFrequency); - - result = ma_lpf1_get_heap_size(&lpf1Config, &lpf1HeapSizeInBytes); - if (result != MA_SUCCESS) { - return result; - } - - pHeapLayout->sizeInBytes += sizeof(ma_lpf1) + lpf1HeapSizeInBytes; - } - - /* LPF 2*/ - pHeapLayout->lpf2Offset = pHeapLayout->sizeInBytes; - for (ilpf2 = 0; ilpf2 < lpf2Count; ilpf2 += 1) { - size_t lpf2HeapSizeInBytes; - ma_lpf2_config lpf2Config = ma_lpf2_config_init(pConfig->format, pConfig->channels, pConfig->sampleRate, pConfig->cutoffFrequency, 0.707107); /* <-- The "q" parameter does not matter for the purpose of calculating the heap size. */ - - result = ma_lpf2_get_heap_size(&lpf2Config, &lpf2HeapSizeInBytes); - if (result != MA_SUCCESS) { - return result; - } - - pHeapLayout->sizeInBytes += sizeof(ma_lpf2) + lpf2HeapSizeInBytes; - } - - /* Make sure allocation size is aligned. */ - pHeapLayout->sizeInBytes = ma_align_64(pHeapLayout->sizeInBytes); - - return MA_SUCCESS; -} - -static ma_result ma_lpf_reinit__internal(const ma_lpf_config* pConfig, void* pHeap, ma_lpf* pLPF, ma_bool32 isNew) -{ - ma_result result; - ma_uint32 lpf1Count; - ma_uint32 lpf2Count; - ma_uint32 ilpf1; - ma_uint32 ilpf2; - ma_lpf_heap_layout heapLayout; /* Only used if isNew is true. */ - - if (pLPF == NULL || pConfig == NULL) { - return MA_INVALID_ARGS; - } - - /* Only supporting f32 and s16. */ - if (pConfig->format != ma_format_f32 && pConfig->format != ma_format_s16) { - return MA_INVALID_ARGS; - } - - /* The format cannot be changed after initialization. */ - if (pLPF->format != ma_format_unknown && pLPF->format != pConfig->format) { - return MA_INVALID_OPERATION; - } - - /* The channel count cannot be changed after initialization. */ - if (pLPF->channels != 0 && pLPF->channels != pConfig->channels) { - return MA_INVALID_OPERATION; - } - - if (pConfig->order > MA_MAX_FILTER_ORDER) { - return MA_INVALID_ARGS; - } - - ma_lpf_calculate_sub_lpf_counts(pConfig->order, &lpf1Count, &lpf2Count); - - /* The filter order can't change between reinits. */ - if (!isNew) { - if (pLPF->lpf1Count != lpf1Count || pLPF->lpf2Count != lpf2Count) { - return MA_INVALID_OPERATION; - } - } - - if (isNew) { - result = ma_lpf_get_heap_layout(pConfig, &heapLayout); - if (result != MA_SUCCESS) { - return result; - } - - pLPF->_pHeap = pHeap; - MA_ZERO_MEMORY(pHeap, heapLayout.sizeInBytes); - - pLPF->pLPF1 = (ma_lpf1*)ma_offset_ptr(pHeap, heapLayout.lpf1Offset); - pLPF->pLPF2 = (ma_lpf2*)ma_offset_ptr(pHeap, heapLayout.lpf2Offset); - } else { - MA_ZERO_OBJECT(&heapLayout); /* To silence a compiler warning. */ - } - - for (ilpf1 = 0; ilpf1 < lpf1Count; ilpf1 += 1) { - ma_lpf1_config lpf1Config = ma_lpf1_config_init(pConfig->format, pConfig->channels, pConfig->sampleRate, pConfig->cutoffFrequency); - - if (isNew) { - size_t lpf1HeapSizeInBytes; - - result = ma_lpf1_get_heap_size(&lpf1Config, &lpf1HeapSizeInBytes); - if (result == MA_SUCCESS) { - result = ma_lpf1_init_preallocated(&lpf1Config, ma_offset_ptr(pHeap, heapLayout.lpf1Offset + (sizeof(ma_lpf1) * lpf1Count) + (ilpf1 * lpf1HeapSizeInBytes)), &pLPF->pLPF1[ilpf1]); - } - } else { - result = ma_lpf1_reinit(&lpf1Config, &pLPF->pLPF1[ilpf1]); - } - - if (result != MA_SUCCESS) { - ma_uint32 jlpf1; - - for (jlpf1 = 0; jlpf1 < ilpf1; jlpf1 += 1) { - ma_lpf1_uninit(&pLPF->pLPF1[jlpf1], NULL); /* No need for allocation callbacks here since we used a preallocated heap allocation. */ - } - - return result; - } - } - - for (ilpf2 = 0; ilpf2 < lpf2Count; ilpf2 += 1) { - ma_lpf2_config lpf2Config; - double q; - double a; - - /* Tempting to use 0.707107, but won't result in a Butterworth filter if the order is > 2. */ - if (lpf1Count == 1) { - a = (1 + ilpf2*1) * (MA_PI_D/(pConfig->order*1)); /* Odd order. */ - } else { - a = (1 + ilpf2*2) * (MA_PI_D/(pConfig->order*2)); /* Even order. */ - } - q = 1 / (2*ma_cosd(a)); - - lpf2Config = ma_lpf2_config_init(pConfig->format, pConfig->channels, pConfig->sampleRate, pConfig->cutoffFrequency, q); - - if (isNew) { - size_t lpf2HeapSizeInBytes; - - result = ma_lpf2_get_heap_size(&lpf2Config, &lpf2HeapSizeInBytes); - if (result == MA_SUCCESS) { - result = ma_lpf2_init_preallocated(&lpf2Config, ma_offset_ptr(pHeap, heapLayout.lpf2Offset + (sizeof(ma_lpf2) * lpf2Count) + (ilpf2 * lpf2HeapSizeInBytes)), &pLPF->pLPF2[ilpf2]); - } - } else { - result = ma_lpf2_reinit(&lpf2Config, &pLPF->pLPF2[ilpf2]); - } - - if (result != MA_SUCCESS) { - ma_uint32 jlpf1; - ma_uint32 jlpf2; - - for (jlpf1 = 0; jlpf1 < lpf1Count; jlpf1 += 1) { - ma_lpf1_uninit(&pLPF->pLPF1[jlpf1], NULL); /* No need for allocation callbacks here since we used a preallocated heap allocation. */ - } - - for (jlpf2 = 0; jlpf2 < ilpf2; jlpf2 += 1) { - ma_lpf2_uninit(&pLPF->pLPF2[jlpf2], NULL); /* No need for allocation callbacks here since we used a preallocated heap allocation. */ - } - - return result; - } - } - - pLPF->lpf1Count = lpf1Count; - pLPF->lpf2Count = lpf2Count; - pLPF->format = pConfig->format; - pLPF->channels = pConfig->channels; - pLPF->sampleRate = pConfig->sampleRate; - - return MA_SUCCESS; -} - -MA_API ma_result ma_lpf_get_heap_size(const ma_lpf_config* pConfig, size_t* pHeapSizeInBytes) -{ - ma_result result; - ma_lpf_heap_layout heapLayout; - - if (pHeapSizeInBytes == NULL) { - return MA_INVALID_ARGS; - } - - *pHeapSizeInBytes = 0; - - result = ma_lpf_get_heap_layout(pConfig, &heapLayout); - if (result != MA_SUCCESS) { - return result; - } - - *pHeapSizeInBytes = heapLayout.sizeInBytes; - - return result; -} - -MA_API ma_result ma_lpf_init_preallocated(const ma_lpf_config* pConfig, void* pHeap, ma_lpf* pLPF) -{ - if (pLPF == NULL) { - return MA_INVALID_ARGS; - } - - MA_ZERO_OBJECT(pLPF); - - return ma_lpf_reinit__internal(pConfig, pHeap, pLPF, /*isNew*/MA_TRUE); -} - -MA_API ma_result ma_lpf_init(const ma_lpf_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_lpf* pLPF) -{ - ma_result result; - size_t heapSizeInBytes; - void* pHeap; - - result = ma_lpf_get_heap_size(pConfig, &heapSizeInBytes); - if (result != MA_SUCCESS) { - return result; - } - - if (heapSizeInBytes > 0) { - pHeap = ma_malloc(heapSizeInBytes, pAllocationCallbacks); - if (pHeap == NULL) { - return MA_OUT_OF_MEMORY; - } - } else { - pHeap = NULL; - } - - result = ma_lpf_init_preallocated(pConfig, pHeap, pLPF); - if (result != MA_SUCCESS) { - ma_free(pHeap, pAllocationCallbacks); - return result; - } - - pLPF->_ownsHeap = MA_TRUE; - return MA_SUCCESS; -} - -MA_API void ma_lpf_uninit(ma_lpf* pLPF, const ma_allocation_callbacks* pAllocationCallbacks) -{ - ma_uint32 ilpf1; - ma_uint32 ilpf2; - - if (pLPF == NULL) { - return; - } - - for (ilpf1 = 0; ilpf1 < pLPF->lpf1Count; ilpf1 += 1) { - ma_lpf1_uninit(&pLPF->pLPF1[ilpf1], pAllocationCallbacks); - } - - for (ilpf2 = 0; ilpf2 < pLPF->lpf2Count; ilpf2 += 1) { - ma_lpf2_uninit(&pLPF->pLPF2[ilpf2], pAllocationCallbacks); - } - - if (pLPF->_ownsHeap) { - ma_free(pLPF->_pHeap, pAllocationCallbacks); - } -} - -MA_API ma_result ma_lpf_reinit(const ma_lpf_config* pConfig, ma_lpf* pLPF) -{ - return ma_lpf_reinit__internal(pConfig, NULL, pLPF, /*isNew*/MA_FALSE); -} - -MA_API ma_result ma_lpf_clear_cache(ma_lpf* pLPF) -{ - ma_uint32 ilpf1; - ma_uint32 ilpf2; - - if (pLPF == NULL) { - return MA_INVALID_ARGS; - } - - for (ilpf1 = 0; ilpf1 < pLPF->lpf1Count; ilpf1 += 1) { - ma_lpf1_clear_cache(&pLPF->pLPF1[ilpf1]); - } - - for (ilpf2 = 0; ilpf2 < pLPF->lpf2Count; ilpf2 += 1) { - ma_lpf2_clear_cache(&pLPF->pLPF2[ilpf2]); - } - - return MA_SUCCESS; -} - -static MA_INLINE void ma_lpf_process_pcm_frame_f32(ma_lpf* pLPF, float* pY, const void* pX) -{ - ma_uint32 ilpf1; - ma_uint32 ilpf2; - - MA_ASSERT(pLPF->format == ma_format_f32); - - MA_MOVE_MEMORY(pY, pX, ma_get_bytes_per_frame(pLPF->format, pLPF->channels)); - - for (ilpf1 = 0; ilpf1 < pLPF->lpf1Count; ilpf1 += 1) { - ma_lpf1_process_pcm_frame_f32(&pLPF->pLPF1[ilpf1], pY, pY); - } - - for (ilpf2 = 0; ilpf2 < pLPF->lpf2Count; ilpf2 += 1) { - ma_lpf2_process_pcm_frame_f32(&pLPF->pLPF2[ilpf2], pY, pY); - } -} - -static MA_INLINE void ma_lpf_process_pcm_frame_s16(ma_lpf* pLPF, ma_int16* pY, const ma_int16* pX) -{ - ma_uint32 ilpf1; - ma_uint32 ilpf2; - - MA_ASSERT(pLPF->format == ma_format_s16); - - MA_MOVE_MEMORY(pY, pX, ma_get_bytes_per_frame(pLPF->format, pLPF->channels)); - - for (ilpf1 = 0; ilpf1 < pLPF->lpf1Count; ilpf1 += 1) { - ma_lpf1_process_pcm_frame_s16(&pLPF->pLPF1[ilpf1], pY, pY); - } - - for (ilpf2 = 0; ilpf2 < pLPF->lpf2Count; ilpf2 += 1) { - ma_lpf2_process_pcm_frame_s16(&pLPF->pLPF2[ilpf2], pY, pY); - } -} - -MA_API ma_result ma_lpf_process_pcm_frames(ma_lpf* pLPF, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount) -{ - ma_result result; - ma_uint32 ilpf1; - ma_uint32 ilpf2; - - if (pLPF == NULL) { - return MA_INVALID_ARGS; - } - - /* Faster path for in-place. */ - if (pFramesOut == pFramesIn) { - for (ilpf1 = 0; ilpf1 < pLPF->lpf1Count; ilpf1 += 1) { - result = ma_lpf1_process_pcm_frames(&pLPF->pLPF1[ilpf1], pFramesOut, pFramesOut, frameCount); - if (result != MA_SUCCESS) { - return result; - } - } - - for (ilpf2 = 0; ilpf2 < pLPF->lpf2Count; ilpf2 += 1) { - result = ma_lpf2_process_pcm_frames(&pLPF->pLPF2[ilpf2], pFramesOut, pFramesOut, frameCount); - if (result != MA_SUCCESS) { - return result; - } - } - } - - /* Slightly slower path for copying. */ - if (pFramesOut != pFramesIn) { - ma_uint32 iFrame; - - /* */ if (pLPF->format == ma_format_f32) { - /* */ float* pFramesOutF32 = ( float*)pFramesOut; - const float* pFramesInF32 = (const float*)pFramesIn; - - for (iFrame = 0; iFrame < frameCount; iFrame += 1) { - ma_lpf_process_pcm_frame_f32(pLPF, pFramesOutF32, pFramesInF32); - pFramesOutF32 += pLPF->channels; - pFramesInF32 += pLPF->channels; - } - } else if (pLPF->format == ma_format_s16) { - /* */ ma_int16* pFramesOutS16 = ( ma_int16*)pFramesOut; - const ma_int16* pFramesInS16 = (const ma_int16*)pFramesIn; - - for (iFrame = 0; iFrame < frameCount; iFrame += 1) { - ma_lpf_process_pcm_frame_s16(pLPF, pFramesOutS16, pFramesInS16); - pFramesOutS16 += pLPF->channels; - pFramesInS16 += pLPF->channels; - } - } else { - MA_ASSERT(MA_FALSE); - return MA_INVALID_OPERATION; /* Should never hit this. */ - } - } - - return MA_SUCCESS; -} - -MA_API ma_uint32 ma_lpf_get_latency(const ma_lpf* pLPF) -{ - if (pLPF == NULL) { - return 0; - } - - return pLPF->lpf2Count*2 + pLPF->lpf1Count; -} - - -/************************************************************************************************************************************************************** - -High-Pass Filtering - -**************************************************************************************************************************************************************/ -MA_API ma_hpf1_config ma_hpf1_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRate, double cutoffFrequency) -{ - ma_hpf1_config config; - - MA_ZERO_OBJECT(&config); - config.format = format; - config.channels = channels; - config.sampleRate = sampleRate; - config.cutoffFrequency = cutoffFrequency; - - return config; -} - -MA_API ma_hpf2_config ma_hpf2_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRate, double cutoffFrequency, double q) -{ - ma_hpf2_config config; - - MA_ZERO_OBJECT(&config); - config.format = format; - config.channels = channels; - config.sampleRate = sampleRate; - config.cutoffFrequency = cutoffFrequency; - config.q = q; - - /* Q cannot be 0 or else it'll result in a division by 0. In this case just default to 0.707107. */ - if (config.q == 0) { - config.q = 0.707107; - } - - return config; -} - - -typedef struct -{ - size_t sizeInBytes; - size_t r1Offset; -} ma_hpf1_heap_layout; - -static ma_result ma_hpf1_get_heap_layout(const ma_hpf1_config* pConfig, ma_hpf1_heap_layout* pHeapLayout) -{ - MA_ASSERT(pHeapLayout != NULL); - - MA_ZERO_OBJECT(pHeapLayout); - - if (pConfig == NULL) { - return MA_INVALID_ARGS; - } - - if (pConfig->channels == 0) { - return MA_INVALID_ARGS; - } - - pHeapLayout->sizeInBytes = 0; - - /* R1 */ - pHeapLayout->r1Offset = pHeapLayout->sizeInBytes; - pHeapLayout->sizeInBytes += sizeof(ma_biquad_coefficient) * pConfig->channels; - - /* Make sure allocation size is aligned. */ - pHeapLayout->sizeInBytes = ma_align_64(pHeapLayout->sizeInBytes); - - return MA_SUCCESS; -} - -MA_API ma_result ma_hpf1_get_heap_size(const ma_hpf1_config* pConfig, size_t* pHeapSizeInBytes) -{ - ma_result result; - ma_hpf1_heap_layout heapLayout; - - if (pHeapSizeInBytes == NULL) { - return MA_INVALID_ARGS; - } - - result = ma_hpf1_get_heap_layout(pConfig, &heapLayout); - if (result != MA_SUCCESS) { - return result; - } - - *pHeapSizeInBytes = heapLayout.sizeInBytes; - - return MA_SUCCESS; -} - -MA_API ma_result ma_hpf1_init_preallocated(const ma_hpf1_config* pConfig, void* pHeap, ma_hpf1* pLPF) -{ - ma_result result; - ma_hpf1_heap_layout heapLayout; - - if (pLPF == NULL) { - return MA_INVALID_ARGS; - } - - MA_ZERO_OBJECT(pLPF); - - result = ma_hpf1_get_heap_layout(pConfig, &heapLayout); - if (result != MA_SUCCESS) { - return result; - } - - pLPF->_pHeap = pHeap; - MA_ZERO_MEMORY(pHeap, heapLayout.sizeInBytes); - - pLPF->pR1 = (ma_biquad_coefficient*)ma_offset_ptr(pHeap, heapLayout.r1Offset); - - return ma_hpf1_reinit(pConfig, pLPF); -} - -MA_API ma_result ma_hpf1_init(const ma_hpf1_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_hpf1* pLPF) -{ - ma_result result; - size_t heapSizeInBytes; - void* pHeap; - - result = ma_hpf1_get_heap_size(pConfig, &heapSizeInBytes); - if (result != MA_SUCCESS) { - return result; - } - - if (heapSizeInBytes > 0) { - pHeap = ma_malloc(heapSizeInBytes, pAllocationCallbacks); - if (pHeap == NULL) { - return MA_OUT_OF_MEMORY; - } - } else { - pHeap = NULL; - } - - result = ma_hpf1_init_preallocated(pConfig, pHeap, pLPF); - if (result != MA_SUCCESS) { - ma_free(pHeap, pAllocationCallbacks); - return result; - } - - pLPF->_ownsHeap = MA_TRUE; - return MA_SUCCESS; -} - -MA_API void ma_hpf1_uninit(ma_hpf1* pHPF, const ma_allocation_callbacks* pAllocationCallbacks) -{ - if (pHPF == NULL) { - return; - } - - if (pHPF->_ownsHeap) { - ma_free(pHPF->_pHeap, pAllocationCallbacks); - } -} - -MA_API ma_result ma_hpf1_reinit(const ma_hpf1_config* pConfig, ma_hpf1* pHPF) -{ - double a; - - if (pHPF == NULL || pConfig == NULL) { - return MA_INVALID_ARGS; - } - - /* Only supporting f32 and s16. */ - if (pConfig->format != ma_format_f32 && pConfig->format != ma_format_s16) { - return MA_INVALID_ARGS; - } - - /* The format cannot be changed after initialization. */ - if (pHPF->format != ma_format_unknown && pHPF->format != pConfig->format) { - return MA_INVALID_OPERATION; - } - - /* The channel count cannot be changed after initialization. */ - if (pHPF->channels != 0 && pHPF->channels != pConfig->channels) { - return MA_INVALID_OPERATION; - } - - pHPF->format = pConfig->format; - pHPF->channels = pConfig->channels; - - a = ma_expd(-2 * MA_PI_D * pConfig->cutoffFrequency / pConfig->sampleRate); - if (pConfig->format == ma_format_f32) { - pHPF->a.f32 = (float)a; - } else { - pHPF->a.s32 = ma_biquad_float_to_fp(a); - } - - return MA_SUCCESS; -} - -static MA_INLINE void ma_hpf1_process_pcm_frame_f32(ma_hpf1* pHPF, float* pY, const float* pX) -{ - ma_uint32 c; - const ma_uint32 channels = pHPF->channels; - const float a = 1 - pHPF->a.f32; - const float b = 1 - a; - - MA_ASSUME(channels > 0); - for (c = 0; c < channels; c += 1) { - float r1 = pHPF->pR1[c].f32; - float x = pX[c]; - float y; - - y = b*x - a*r1; - - pY[c] = y; - pHPF->pR1[c].f32 = y; - } -} - -static MA_INLINE void ma_hpf1_process_pcm_frame_s16(ma_hpf1* pHPF, ma_int16* pY, const ma_int16* pX) -{ - ma_uint32 c; - const ma_uint32 channels = pHPF->channels; - const ma_int32 a = ((1 << MA_BIQUAD_FIXED_POINT_SHIFT) - pHPF->a.s32); - const ma_int32 b = ((1 << MA_BIQUAD_FIXED_POINT_SHIFT) - a); - - MA_ASSUME(channels > 0); - for (c = 0; c < channels; c += 1) { - ma_int32 r1 = pHPF->pR1[c].s32; - ma_int32 x = pX[c]; - ma_int32 y; - - y = (b*x - a*r1) >> MA_BIQUAD_FIXED_POINT_SHIFT; - - pY[c] = (ma_int16)y; - pHPF->pR1[c].s32 = (ma_int32)y; - } -} - -MA_API ma_result ma_hpf1_process_pcm_frames(ma_hpf1* pHPF, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount) -{ - ma_uint32 n; - - if (pHPF == NULL || pFramesOut == NULL || pFramesIn == NULL) { - return MA_INVALID_ARGS; - } - - /* Note that the logic below needs to support in-place filtering. That is, it must support the case where pFramesOut and pFramesIn are the same. */ - - if (pHPF->format == ma_format_f32) { - /* */ float* pY = ( float*)pFramesOut; - const float* pX = (const float*)pFramesIn; - - for (n = 0; n < frameCount; n += 1) { - ma_hpf1_process_pcm_frame_f32(pHPF, pY, pX); - pY += pHPF->channels; - pX += pHPF->channels; - } - } else if (pHPF->format == ma_format_s16) { - /* */ ma_int16* pY = ( ma_int16*)pFramesOut; - const ma_int16* pX = (const ma_int16*)pFramesIn; - - for (n = 0; n < frameCount; n += 1) { - ma_hpf1_process_pcm_frame_s16(pHPF, pY, pX); - pY += pHPF->channels; - pX += pHPF->channels; - } - } else { - MA_ASSERT(MA_FALSE); - return MA_INVALID_ARGS; /* Format not supported. Should never hit this because it's checked in ma_biquad_init() and ma_biquad_reinit(). */ - } - - return MA_SUCCESS; -} - -MA_API ma_uint32 ma_hpf1_get_latency(const ma_hpf1* pHPF) -{ - if (pHPF == NULL) { - return 0; - } - - return 1; -} - - -static MA_INLINE ma_biquad_config ma_hpf2__get_biquad_config(const ma_hpf2_config* pConfig) -{ - ma_biquad_config bqConfig; - double q; - double w; - double s; - double c; - double a; - - MA_ASSERT(pConfig != NULL); - - q = pConfig->q; - w = 2 * MA_PI_D * pConfig->cutoffFrequency / pConfig->sampleRate; - s = ma_sind(w); - c = ma_cosd(w); - a = s / (2*q); - - bqConfig.b0 = (1 + c) / 2; - bqConfig.b1 = -(1 + c); - bqConfig.b2 = (1 + c) / 2; - bqConfig.a0 = 1 + a; - bqConfig.a1 = -2 * c; - bqConfig.a2 = 1 - a; - - bqConfig.format = pConfig->format; - bqConfig.channels = pConfig->channels; - - return bqConfig; -} - -MA_API ma_result ma_hpf2_get_heap_size(const ma_hpf2_config* pConfig, size_t* pHeapSizeInBytes) -{ - ma_biquad_config bqConfig; - bqConfig = ma_hpf2__get_biquad_config(pConfig); - - return ma_biquad_get_heap_size(&bqConfig, pHeapSizeInBytes); -} - -MA_API ma_result ma_hpf2_init_preallocated(const ma_hpf2_config* pConfig, void* pHeap, ma_hpf2* pHPF) -{ - ma_result result; - ma_biquad_config bqConfig; - - if (pHPF == NULL) { - return MA_INVALID_ARGS; - } - - MA_ZERO_OBJECT(pHPF); - - if (pConfig == NULL) { - return MA_INVALID_ARGS; - } - - bqConfig = ma_hpf2__get_biquad_config(pConfig); - result = ma_biquad_init_preallocated(&bqConfig, pHeap, &pHPF->bq); - if (result != MA_SUCCESS) { - return result; - } - - return MA_SUCCESS; -} - -MA_API ma_result ma_hpf2_init(const ma_hpf2_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_hpf2* pHPF) -{ - ma_result result; - size_t heapSizeInBytes; - void* pHeap; - - result = ma_hpf2_get_heap_size(pConfig, &heapSizeInBytes); - if (result != MA_SUCCESS) { - return result; - } - - if (heapSizeInBytes > 0) { - pHeap = ma_malloc(heapSizeInBytes, pAllocationCallbacks); - if (pHeap == NULL) { - return MA_OUT_OF_MEMORY; - } - } else { - pHeap = NULL; - } - - result = ma_hpf2_init_preallocated(pConfig, pHeap, pHPF); - if (result != MA_SUCCESS) { - ma_free(pHeap, pAllocationCallbacks); - return result; - } - - pHPF->bq._ownsHeap = MA_TRUE; /* <-- This will cause the biquad to take ownership of the heap and free it when it's uninitialized. */ - return MA_SUCCESS; -} - -MA_API void ma_hpf2_uninit(ma_hpf2* pHPF, const ma_allocation_callbacks* pAllocationCallbacks) -{ - if (pHPF == NULL) { - return; - } - - ma_biquad_uninit(&pHPF->bq, pAllocationCallbacks); /* <-- This will free the heap allocation. */ -} - -MA_API ma_result ma_hpf2_reinit(const ma_hpf2_config* pConfig, ma_hpf2* pHPF) -{ - ma_result result; - ma_biquad_config bqConfig; - - if (pHPF == NULL || pConfig == NULL) { - return MA_INVALID_ARGS; - } - - bqConfig = ma_hpf2__get_biquad_config(pConfig); - result = ma_biquad_reinit(&bqConfig, &pHPF->bq); - if (result != MA_SUCCESS) { - return result; - } - - return MA_SUCCESS; -} - -static MA_INLINE void ma_hpf2_process_pcm_frame_s16(ma_hpf2* pHPF, ma_int16* pFrameOut, const ma_int16* pFrameIn) -{ - ma_biquad_process_pcm_frame_s16(&pHPF->bq, pFrameOut, pFrameIn); -} - -static MA_INLINE void ma_hpf2_process_pcm_frame_f32(ma_hpf2* pHPF, float* pFrameOut, const float* pFrameIn) -{ - ma_biquad_process_pcm_frame_f32(&pHPF->bq, pFrameOut, pFrameIn); -} - -MA_API ma_result ma_hpf2_process_pcm_frames(ma_hpf2* pHPF, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount) -{ - if (pHPF == NULL) { - return MA_INVALID_ARGS; - } - - return ma_biquad_process_pcm_frames(&pHPF->bq, pFramesOut, pFramesIn, frameCount); -} - -MA_API ma_uint32 ma_hpf2_get_latency(const ma_hpf2* pHPF) -{ - if (pHPF == NULL) { - return 0; - } - - return ma_biquad_get_latency(&pHPF->bq); -} - - -MA_API ma_hpf_config ma_hpf_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRate, double cutoffFrequency, ma_uint32 order) -{ - ma_hpf_config config; - - MA_ZERO_OBJECT(&config); - config.format = format; - config.channels = channels; - config.sampleRate = sampleRate; - config.cutoffFrequency = cutoffFrequency; - config.order = ma_min(order, MA_MAX_FILTER_ORDER); - - return config; -} - - -typedef struct -{ - size_t sizeInBytes; - size_t hpf1Offset; - size_t hpf2Offset; /* Offset of the first second order filter. Subsequent filters will come straight after, and will each have the same heap size. */ -} ma_hpf_heap_layout; - -static void ma_hpf_calculate_sub_hpf_counts(ma_uint32 order, ma_uint32* pHPF1Count, ma_uint32* pHPF2Count) -{ - MA_ASSERT(pHPF1Count != NULL); - MA_ASSERT(pHPF2Count != NULL); - - *pHPF1Count = order % 2; - *pHPF2Count = order / 2; -} - -static ma_result ma_hpf_get_heap_layout(const ma_hpf_config* pConfig, ma_hpf_heap_layout* pHeapLayout) -{ - ma_result result; - ma_uint32 hpf1Count; - ma_uint32 hpf2Count; - ma_uint32 ihpf1; - ma_uint32 ihpf2; - - MA_ASSERT(pHeapLayout != NULL); - - MA_ZERO_OBJECT(pHeapLayout); - - if (pConfig == NULL) { - return MA_INVALID_ARGS; - } - - if (pConfig->channels == 0) { - return MA_INVALID_ARGS; - } - - if (pConfig->order > MA_MAX_FILTER_ORDER) { - return MA_INVALID_ARGS; - } - - ma_hpf_calculate_sub_hpf_counts(pConfig->order, &hpf1Count, &hpf2Count); - - pHeapLayout->sizeInBytes = 0; - - /* HPF 1 */ - pHeapLayout->hpf1Offset = pHeapLayout->sizeInBytes; - for (ihpf1 = 0; ihpf1 < hpf1Count; ihpf1 += 1) { - size_t hpf1HeapSizeInBytes; - ma_hpf1_config hpf1Config = ma_hpf1_config_init(pConfig->format, pConfig->channels, pConfig->sampleRate, pConfig->cutoffFrequency); - - result = ma_hpf1_get_heap_size(&hpf1Config, &hpf1HeapSizeInBytes); - if (result != MA_SUCCESS) { - return result; - } - - pHeapLayout->sizeInBytes += sizeof(ma_hpf1) + hpf1HeapSizeInBytes; - } - - /* HPF 2*/ - pHeapLayout->hpf2Offset = pHeapLayout->sizeInBytes; - for (ihpf2 = 0; ihpf2 < hpf2Count; ihpf2 += 1) { - size_t hpf2HeapSizeInBytes; - ma_hpf2_config hpf2Config = ma_hpf2_config_init(pConfig->format, pConfig->channels, pConfig->sampleRate, pConfig->cutoffFrequency, 0.707107); /* <-- The "q" parameter does not matter for the purpose of calculating the heap size. */ - - result = ma_hpf2_get_heap_size(&hpf2Config, &hpf2HeapSizeInBytes); - if (result != MA_SUCCESS) { - return result; - } - - pHeapLayout->sizeInBytes += sizeof(ma_hpf2) + hpf2HeapSizeInBytes; - } - - /* Make sure allocation size is aligned. */ - pHeapLayout->sizeInBytes = ma_align_64(pHeapLayout->sizeInBytes); - - return MA_SUCCESS; -} - -static ma_result ma_hpf_reinit__internal(const ma_hpf_config* pConfig, void* pHeap, ma_hpf* pHPF, ma_bool32 isNew) -{ - ma_result result; - ma_uint32 hpf1Count; - ma_uint32 hpf2Count; - ma_uint32 ihpf1; - ma_uint32 ihpf2; - ma_hpf_heap_layout heapLayout; /* Only used if isNew is true. */ - - if (pHPF == NULL || pConfig == NULL) { - return MA_INVALID_ARGS; - } - - /* Only supporting f32 and s16. */ - if (pConfig->format != ma_format_f32 && pConfig->format != ma_format_s16) { - return MA_INVALID_ARGS; - } - - /* The format cannot be changed after initialization. */ - if (pHPF->format != ma_format_unknown && pHPF->format != pConfig->format) { - return MA_INVALID_OPERATION; - } - - /* The channel count cannot be changed after initialization. */ - if (pHPF->channels != 0 && pHPF->channels != pConfig->channels) { - return MA_INVALID_OPERATION; - } - - if (pConfig->order > MA_MAX_FILTER_ORDER) { - return MA_INVALID_ARGS; - } - - ma_hpf_calculate_sub_hpf_counts(pConfig->order, &hpf1Count, &hpf2Count); - - /* The filter order can't change between reinits. */ - if (!isNew) { - if (pHPF->hpf1Count != hpf1Count || pHPF->hpf2Count != hpf2Count) { - return MA_INVALID_OPERATION; - } - } - - if (isNew) { - result = ma_hpf_get_heap_layout(pConfig, &heapLayout); - if (result != MA_SUCCESS) { - return result; - } - - pHPF->_pHeap = pHeap; - MA_ZERO_MEMORY(pHeap, heapLayout.sizeInBytes); - - pHPF->pHPF1 = (ma_hpf1*)ma_offset_ptr(pHeap, heapLayout.hpf1Offset); - pHPF->pHPF2 = (ma_hpf2*)ma_offset_ptr(pHeap, heapLayout.hpf2Offset); - } else { - MA_ZERO_OBJECT(&heapLayout); /* To silence a compiler warning. */ - } - - for (ihpf1 = 0; ihpf1 < hpf1Count; ihpf1 += 1) { - ma_hpf1_config hpf1Config = ma_hpf1_config_init(pConfig->format, pConfig->channels, pConfig->sampleRate, pConfig->cutoffFrequency); - - if (isNew) { - size_t hpf1HeapSizeInBytes; - - result = ma_hpf1_get_heap_size(&hpf1Config, &hpf1HeapSizeInBytes); - if (result == MA_SUCCESS) { - result = ma_hpf1_init_preallocated(&hpf1Config, ma_offset_ptr(pHeap, heapLayout.hpf1Offset + (sizeof(ma_hpf1) * hpf1Count) + (ihpf1 * hpf1HeapSizeInBytes)), &pHPF->pHPF1[ihpf1]); - } - } else { - result = ma_hpf1_reinit(&hpf1Config, &pHPF->pHPF1[ihpf1]); - } - - if (result != MA_SUCCESS) { - ma_uint32 jhpf1; - - for (jhpf1 = 0; jhpf1 < ihpf1; jhpf1 += 1) { - ma_hpf1_uninit(&pHPF->pHPF1[jhpf1], NULL); /* No need for allocation callbacks here since we used a preallocated heap allocation. */ - } - - return result; - } - } - - for (ihpf2 = 0; ihpf2 < hpf2Count; ihpf2 += 1) { - ma_hpf2_config hpf2Config; - double q; - double a; - - /* Tempting to use 0.707107, but won't result in a Butterworth filter if the order is > 2. */ - if (hpf1Count == 1) { - a = (1 + ihpf2*1) * (MA_PI_D/(pConfig->order*1)); /* Odd order. */ - } else { - a = (1 + ihpf2*2) * (MA_PI_D/(pConfig->order*2)); /* Even order. */ - } - q = 1 / (2*ma_cosd(a)); - - hpf2Config = ma_hpf2_config_init(pConfig->format, pConfig->channels, pConfig->sampleRate, pConfig->cutoffFrequency, q); - - if (isNew) { - size_t hpf2HeapSizeInBytes; - - result = ma_hpf2_get_heap_size(&hpf2Config, &hpf2HeapSizeInBytes); - if (result == MA_SUCCESS) { - result = ma_hpf2_init_preallocated(&hpf2Config, ma_offset_ptr(pHeap, heapLayout.hpf2Offset + (sizeof(ma_hpf2) * hpf2Count) + (ihpf2 * hpf2HeapSizeInBytes)), &pHPF->pHPF2[ihpf2]); - } - } else { - result = ma_hpf2_reinit(&hpf2Config, &pHPF->pHPF2[ihpf2]); - } - - if (result != MA_SUCCESS) { - ma_uint32 jhpf1; - ma_uint32 jhpf2; - - for (jhpf1 = 0; jhpf1 < hpf1Count; jhpf1 += 1) { - ma_hpf1_uninit(&pHPF->pHPF1[jhpf1], NULL); /* No need for allocation callbacks here since we used a preallocated heap allocation. */ - } - - for (jhpf2 = 0; jhpf2 < ihpf2; jhpf2 += 1) { - ma_hpf2_uninit(&pHPF->pHPF2[jhpf2], NULL); /* No need for allocation callbacks here since we used a preallocated heap allocation. */ - } - - return result; - } - } - - pHPF->hpf1Count = hpf1Count; - pHPF->hpf2Count = hpf2Count; - pHPF->format = pConfig->format; - pHPF->channels = pConfig->channels; - pHPF->sampleRate = pConfig->sampleRate; - - return MA_SUCCESS; -} - -MA_API ma_result ma_hpf_get_heap_size(const ma_hpf_config* pConfig, size_t* pHeapSizeInBytes) -{ - ma_result result; - ma_hpf_heap_layout heapLayout; - - if (pHeapSizeInBytes == NULL) { - return MA_INVALID_ARGS; - } - - *pHeapSizeInBytes = 0; - - result = ma_hpf_get_heap_layout(pConfig, &heapLayout); - if (result != MA_SUCCESS) { - return result; - } - - *pHeapSizeInBytes = heapLayout.sizeInBytes; - - return result; -} - -MA_API ma_result ma_hpf_init_preallocated(const ma_hpf_config* pConfig, void* pHeap, ma_hpf* pLPF) -{ - if (pLPF == NULL) { - return MA_INVALID_ARGS; - } - - MA_ZERO_OBJECT(pLPF); - - return ma_hpf_reinit__internal(pConfig, pHeap, pLPF, /*isNew*/MA_TRUE); -} - -MA_API ma_result ma_hpf_init(const ma_hpf_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_hpf* pHPF) -{ - ma_result result; - size_t heapSizeInBytes; - void* pHeap; - - result = ma_hpf_get_heap_size(pConfig, &heapSizeInBytes); - if (result != MA_SUCCESS) { - return result; - } - - if (heapSizeInBytes > 0) { - pHeap = ma_malloc(heapSizeInBytes, pAllocationCallbacks); - if (pHeap == NULL) { - return MA_OUT_OF_MEMORY; - } - } else { - pHeap = NULL; - } - - result = ma_hpf_init_preallocated(pConfig, pHeap, pHPF); - if (result != MA_SUCCESS) { - ma_free(pHeap, pAllocationCallbacks); - return result; - } - - pHPF->_ownsHeap = MA_TRUE; - return MA_SUCCESS; -} - -MA_API void ma_hpf_uninit(ma_hpf* pHPF, const ma_allocation_callbacks* pAllocationCallbacks) -{ - ma_uint32 ihpf1; - ma_uint32 ihpf2; - - if (pHPF == NULL) { - return; - } - - for (ihpf1 = 0; ihpf1 < pHPF->hpf1Count; ihpf1 += 1) { - ma_hpf1_uninit(&pHPF->pHPF1[ihpf1], pAllocationCallbacks); - } - - for (ihpf2 = 0; ihpf2 < pHPF->hpf2Count; ihpf2 += 1) { - ma_hpf2_uninit(&pHPF->pHPF2[ihpf2], pAllocationCallbacks); - } - - if (pHPF->_ownsHeap) { - ma_free(pHPF->_pHeap, pAllocationCallbacks); - } -} - -MA_API ma_result ma_hpf_reinit(const ma_hpf_config* pConfig, ma_hpf* pHPF) -{ - return ma_hpf_reinit__internal(pConfig, NULL, pHPF, /*isNew*/MA_FALSE); -} - -MA_API ma_result ma_hpf_process_pcm_frames(ma_hpf* pHPF, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount) -{ - ma_result result; - ma_uint32 ihpf1; - ma_uint32 ihpf2; - - if (pHPF == NULL) { - return MA_INVALID_ARGS; - } - - /* Faster path for in-place. */ - if (pFramesOut == pFramesIn) { - for (ihpf1 = 0; ihpf1 < pHPF->hpf1Count; ihpf1 += 1) { - result = ma_hpf1_process_pcm_frames(&pHPF->pHPF1[ihpf1], pFramesOut, pFramesOut, frameCount); - if (result != MA_SUCCESS) { - return result; - } - } - - for (ihpf2 = 0; ihpf2 < pHPF->hpf2Count; ihpf2 += 1) { - result = ma_hpf2_process_pcm_frames(&pHPF->pHPF2[ihpf2], pFramesOut, pFramesOut, frameCount); - if (result != MA_SUCCESS) { - return result; - } - } - } - - /* Slightly slower path for copying. */ - if (pFramesOut != pFramesIn) { - ma_uint32 iFrame; - - /* */ if (pHPF->format == ma_format_f32) { - /* */ float* pFramesOutF32 = ( float*)pFramesOut; - const float* pFramesInF32 = (const float*)pFramesIn; - - for (iFrame = 0; iFrame < frameCount; iFrame += 1) { - MA_COPY_MEMORY(pFramesOutF32, pFramesInF32, ma_get_bytes_per_frame(pHPF->format, pHPF->channels)); - - for (ihpf1 = 0; ihpf1 < pHPF->hpf1Count; ihpf1 += 1) { - ma_hpf1_process_pcm_frame_f32(&pHPF->pHPF1[ihpf1], pFramesOutF32, pFramesOutF32); - } - - for (ihpf2 = 0; ihpf2 < pHPF->hpf2Count; ihpf2 += 1) { - ma_hpf2_process_pcm_frame_f32(&pHPF->pHPF2[ihpf2], pFramesOutF32, pFramesOutF32); - } - - pFramesOutF32 += pHPF->channels; - pFramesInF32 += pHPF->channels; - } - } else if (pHPF->format == ma_format_s16) { - /* */ ma_int16* pFramesOutS16 = ( ma_int16*)pFramesOut; - const ma_int16* pFramesInS16 = (const ma_int16*)pFramesIn; - - for (iFrame = 0; iFrame < frameCount; iFrame += 1) { - MA_COPY_MEMORY(pFramesOutS16, pFramesInS16, ma_get_bytes_per_frame(pHPF->format, pHPF->channels)); - - for (ihpf1 = 0; ihpf1 < pHPF->hpf1Count; ihpf1 += 1) { - ma_hpf1_process_pcm_frame_s16(&pHPF->pHPF1[ihpf1], pFramesOutS16, pFramesOutS16); - } - - for (ihpf2 = 0; ihpf2 < pHPF->hpf2Count; ihpf2 += 1) { - ma_hpf2_process_pcm_frame_s16(&pHPF->pHPF2[ihpf2], pFramesOutS16, pFramesOutS16); - } - - pFramesOutS16 += pHPF->channels; - pFramesInS16 += pHPF->channels; - } - } else { - MA_ASSERT(MA_FALSE); - return MA_INVALID_OPERATION; /* Should never hit this. */ - } - } - - return MA_SUCCESS; -} - -MA_API ma_uint32 ma_hpf_get_latency(const ma_hpf* pHPF) -{ - if (pHPF == NULL) { - return 0; - } - - return pHPF->hpf2Count*2 + pHPF->hpf1Count; -} - - -/************************************************************************************************************************************************************** - -Band-Pass Filtering - -**************************************************************************************************************************************************************/ -MA_API ma_bpf2_config ma_bpf2_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRate, double cutoffFrequency, double q) -{ - ma_bpf2_config config; - - MA_ZERO_OBJECT(&config); - config.format = format; - config.channels = channels; - config.sampleRate = sampleRate; - config.cutoffFrequency = cutoffFrequency; - config.q = q; - - /* Q cannot be 0 or else it'll result in a division by 0. In this case just default to 0.707107. */ - if (config.q == 0) { - config.q = 0.707107; - } - - return config; -} - - -static MA_INLINE ma_biquad_config ma_bpf2__get_biquad_config(const ma_bpf2_config* pConfig) -{ - ma_biquad_config bqConfig; - double q; - double w; - double s; - double c; - double a; - - MA_ASSERT(pConfig != NULL); - - q = pConfig->q; - w = 2 * MA_PI_D * pConfig->cutoffFrequency / pConfig->sampleRate; - s = ma_sind(w); - c = ma_cosd(w); - a = s / (2*q); - - bqConfig.b0 = q * a; - bqConfig.b1 = 0; - bqConfig.b2 = -q * a; - bqConfig.a0 = 1 + a; - bqConfig.a1 = -2 * c; - bqConfig.a2 = 1 - a; - - bqConfig.format = pConfig->format; - bqConfig.channels = pConfig->channels; - - return bqConfig; -} - -MA_API ma_result ma_bpf2_get_heap_size(const ma_bpf2_config* pConfig, size_t* pHeapSizeInBytes) -{ - ma_biquad_config bqConfig; - bqConfig = ma_bpf2__get_biquad_config(pConfig); - - return ma_biquad_get_heap_size(&bqConfig, pHeapSizeInBytes); -} - -MA_API ma_result ma_bpf2_init_preallocated(const ma_bpf2_config* pConfig, void* pHeap, ma_bpf2* pBPF) -{ - ma_result result; - ma_biquad_config bqConfig; - - if (pBPF == NULL) { - return MA_INVALID_ARGS; - } - - MA_ZERO_OBJECT(pBPF); - - if (pConfig == NULL) { - return MA_INVALID_ARGS; - } - - bqConfig = ma_bpf2__get_biquad_config(pConfig); - result = ma_biquad_init_preallocated(&bqConfig, pHeap, &pBPF->bq); - if (result != MA_SUCCESS) { - return result; - } - - return MA_SUCCESS; -} - -MA_API ma_result ma_bpf2_init(const ma_bpf2_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_bpf2* pBPF) -{ - ma_result result; - size_t heapSizeInBytes; - void* pHeap; - - result = ma_bpf2_get_heap_size(pConfig, &heapSizeInBytes); - if (result != MA_SUCCESS) { - return result; - } - - if (heapSizeInBytes > 0) { - pHeap = ma_malloc(heapSizeInBytes, pAllocationCallbacks); - if (pHeap == NULL) { - return MA_OUT_OF_MEMORY; - } - } else { - pHeap = NULL; - } - - result = ma_bpf2_init_preallocated(pConfig, pHeap, pBPF); - if (result != MA_SUCCESS) { - ma_free(pHeap, pAllocationCallbacks); - return result; - } - - pBPF->bq._ownsHeap = MA_TRUE; /* <-- This will cause the biquad to take ownership of the heap and free it when it's uninitialized. */ - return MA_SUCCESS; -} - -MA_API void ma_bpf2_uninit(ma_bpf2* pBPF, const ma_allocation_callbacks* pAllocationCallbacks) -{ - if (pBPF == NULL) { - return; - } - - ma_biquad_uninit(&pBPF->bq, pAllocationCallbacks); /* <-- This will free the heap allocation. */ -} - -MA_API ma_result ma_bpf2_reinit(const ma_bpf2_config* pConfig, ma_bpf2* pBPF) -{ - ma_result result; - ma_biquad_config bqConfig; - - if (pBPF == NULL || pConfig == NULL) { - return MA_INVALID_ARGS; - } - - bqConfig = ma_bpf2__get_biquad_config(pConfig); - result = ma_biquad_reinit(&bqConfig, &pBPF->bq); - if (result != MA_SUCCESS) { - return result; - } - - return MA_SUCCESS; -} - -static MA_INLINE void ma_bpf2_process_pcm_frame_s16(ma_bpf2* pBPF, ma_int16* pFrameOut, const ma_int16* pFrameIn) -{ - ma_biquad_process_pcm_frame_s16(&pBPF->bq, pFrameOut, pFrameIn); -} - -static MA_INLINE void ma_bpf2_process_pcm_frame_f32(ma_bpf2* pBPF, float* pFrameOut, const float* pFrameIn) -{ - ma_biquad_process_pcm_frame_f32(&pBPF->bq, pFrameOut, pFrameIn); -} - -MA_API ma_result ma_bpf2_process_pcm_frames(ma_bpf2* pBPF, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount) -{ - if (pBPF == NULL) { - return MA_INVALID_ARGS; - } - - return ma_biquad_process_pcm_frames(&pBPF->bq, pFramesOut, pFramesIn, frameCount); -} - -MA_API ma_uint32 ma_bpf2_get_latency(const ma_bpf2* pBPF) -{ - if (pBPF == NULL) { - return 0; - } - - return ma_biquad_get_latency(&pBPF->bq); -} - - -MA_API ma_bpf_config ma_bpf_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRate, double cutoffFrequency, ma_uint32 order) -{ - ma_bpf_config config; - - MA_ZERO_OBJECT(&config); - config.format = format; - config.channels = channels; - config.sampleRate = sampleRate; - config.cutoffFrequency = cutoffFrequency; - config.order = ma_min(order, MA_MAX_FILTER_ORDER); - - return config; -} - - -typedef struct -{ - size_t sizeInBytes; - size_t bpf2Offset; -} ma_bpf_heap_layout; - -static ma_result ma_bpf_get_heap_layout(const ma_bpf_config* pConfig, ma_bpf_heap_layout* pHeapLayout) -{ - ma_result result; - ma_uint32 bpf2Count; - ma_uint32 ibpf2; - - MA_ASSERT(pHeapLayout != NULL); - - MA_ZERO_OBJECT(pHeapLayout); - - if (pConfig == NULL) { - return MA_INVALID_ARGS; - } - - if (pConfig->order > MA_MAX_FILTER_ORDER) { - return MA_INVALID_ARGS; - } - - /* We must have an even number of order. */ - if ((pConfig->order & 0x1) != 0) { - return MA_INVALID_ARGS; - } - - bpf2Count = pConfig->order / 2; - - pHeapLayout->sizeInBytes = 0; - - /* BPF 2 */ - pHeapLayout->bpf2Offset = pHeapLayout->sizeInBytes; - for (ibpf2 = 0; ibpf2 < bpf2Count; ibpf2 += 1) { - size_t bpf2HeapSizeInBytes; - ma_bpf2_config bpf2Config = ma_bpf2_config_init(pConfig->format, pConfig->channels, pConfig->sampleRate, pConfig->cutoffFrequency, 0.707107); /* <-- The "q" parameter does not matter for the purpose of calculating the heap size. */ - - result = ma_bpf2_get_heap_size(&bpf2Config, &bpf2HeapSizeInBytes); - if (result != MA_SUCCESS) { - return result; - } - - pHeapLayout->sizeInBytes += sizeof(ma_bpf2) + bpf2HeapSizeInBytes; - } - - /* Make sure allocation size is aligned. */ - pHeapLayout->sizeInBytes = ma_align_64(pHeapLayout->sizeInBytes); - - return MA_SUCCESS; -} - -static ma_result ma_bpf_reinit__internal(const ma_bpf_config* pConfig, void* pHeap, ma_bpf* pBPF, ma_bool32 isNew) -{ - ma_result result; - ma_uint32 bpf2Count; - ma_uint32 ibpf2; - ma_bpf_heap_layout heapLayout; /* Only used if isNew is true. */ - - if (pBPF == NULL || pConfig == NULL) { - return MA_INVALID_ARGS; - } - - /* Only supporting f32 and s16. */ - if (pConfig->format != ma_format_f32 && pConfig->format != ma_format_s16) { - return MA_INVALID_ARGS; - } - - /* The format cannot be changed after initialization. */ - if (pBPF->format != ma_format_unknown && pBPF->format != pConfig->format) { - return MA_INVALID_OPERATION; - } - - /* The channel count cannot be changed after initialization. */ - if (pBPF->channels != 0 && pBPF->channels != pConfig->channels) { - return MA_INVALID_OPERATION; - } - - if (pConfig->order > MA_MAX_FILTER_ORDER) { - return MA_INVALID_ARGS; - } - - /* We must have an even number of order. */ - if ((pConfig->order & 0x1) != 0) { - return MA_INVALID_ARGS; - } - - bpf2Count = pConfig->order / 2; - - /* The filter order can't change between reinits. */ - if (!isNew) { - if (pBPF->bpf2Count != bpf2Count) { - return MA_INVALID_OPERATION; - } - } - - if (isNew) { - result = ma_bpf_get_heap_layout(pConfig, &heapLayout); - if (result != MA_SUCCESS) { - return result; - } - - pBPF->_pHeap = pHeap; - MA_ZERO_MEMORY(pHeap, heapLayout.sizeInBytes); - - pBPF->pBPF2 = (ma_bpf2*)ma_offset_ptr(pHeap, heapLayout.bpf2Offset); - } else { - MA_ZERO_OBJECT(&heapLayout); - } - - for (ibpf2 = 0; ibpf2 < bpf2Count; ibpf2 += 1) { - ma_bpf2_config bpf2Config; - double q; - - /* TODO: Calculate Q to make this a proper Butterworth filter. */ - q = 0.707107; - - bpf2Config = ma_bpf2_config_init(pConfig->format, pConfig->channels, pConfig->sampleRate, pConfig->cutoffFrequency, q); - - if (isNew) { - size_t bpf2HeapSizeInBytes; - - result = ma_bpf2_get_heap_size(&bpf2Config, &bpf2HeapSizeInBytes); - if (result == MA_SUCCESS) { - result = ma_bpf2_init_preallocated(&bpf2Config, ma_offset_ptr(pHeap, heapLayout.bpf2Offset + (sizeof(ma_bpf2) * bpf2Count) + (ibpf2 * bpf2HeapSizeInBytes)), &pBPF->pBPF2[ibpf2]); - } - } else { - result = ma_bpf2_reinit(&bpf2Config, &pBPF->pBPF2[ibpf2]); - } - - if (result != MA_SUCCESS) { - return result; - } - } - - pBPF->bpf2Count = bpf2Count; - pBPF->format = pConfig->format; - pBPF->channels = pConfig->channels; - - return MA_SUCCESS; -} - - -MA_API ma_result ma_bpf_get_heap_size(const ma_bpf_config* pConfig, size_t* pHeapSizeInBytes) -{ - ma_result result; - ma_bpf_heap_layout heapLayout; - - if (pHeapSizeInBytes == NULL) { - return MA_INVALID_ARGS; - } - - *pHeapSizeInBytes = 0; - - result = ma_bpf_get_heap_layout(pConfig, &heapLayout); - if (result != MA_SUCCESS) { - return result; - } - - *pHeapSizeInBytes = heapLayout.sizeInBytes; - - return MA_SUCCESS; -} - -MA_API ma_result ma_bpf_init_preallocated(const ma_bpf_config* pConfig, void* pHeap, ma_bpf* pBPF) -{ - if (pBPF == NULL) { - return MA_INVALID_ARGS; - } - - MA_ZERO_OBJECT(pBPF); - - return ma_bpf_reinit__internal(pConfig, pHeap, pBPF, /*isNew*/MA_TRUE); -} - -MA_API ma_result ma_bpf_init(const ma_bpf_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_bpf* pBPF) -{ - ma_result result; - size_t heapSizeInBytes; - void* pHeap; - - result = ma_bpf_get_heap_size(pConfig, &heapSizeInBytes); - if (result != MA_SUCCESS) { - return result; - } - - if (heapSizeInBytes > 0) { - pHeap = ma_malloc(heapSizeInBytes, pAllocationCallbacks); - if (pHeap == NULL) { - return MA_OUT_OF_MEMORY; - } - } else { - pHeap = NULL; - } - - result = ma_bpf_init_preallocated(pConfig, pHeap, pBPF); - if (result != MA_SUCCESS) { - ma_free(pHeap, pAllocationCallbacks); - return result; - } - - pBPF->_ownsHeap = MA_TRUE; - return MA_SUCCESS; -} - -MA_API void ma_bpf_uninit(ma_bpf* pBPF, const ma_allocation_callbacks* pAllocationCallbacks) -{ - ma_uint32 ibpf2; - - if (pBPF == NULL) { - return; - } - - for (ibpf2 = 0; ibpf2 < pBPF->bpf2Count; ibpf2 += 1) { - ma_bpf2_uninit(&pBPF->pBPF2[ibpf2], pAllocationCallbacks); - } - - if (pBPF->_ownsHeap) { - ma_free(pBPF->_pHeap, pAllocationCallbacks); - } -} - -MA_API ma_result ma_bpf_reinit(const ma_bpf_config* pConfig, ma_bpf* pBPF) -{ - return ma_bpf_reinit__internal(pConfig, NULL, pBPF, /*isNew*/MA_FALSE); -} - -MA_API ma_result ma_bpf_process_pcm_frames(ma_bpf* pBPF, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount) -{ - ma_result result; - ma_uint32 ibpf2; - - if (pBPF == NULL) { - return MA_INVALID_ARGS; - } - - /* Faster path for in-place. */ - if (pFramesOut == pFramesIn) { - for (ibpf2 = 0; ibpf2 < pBPF->bpf2Count; ibpf2 += 1) { - result = ma_bpf2_process_pcm_frames(&pBPF->pBPF2[ibpf2], pFramesOut, pFramesOut, frameCount); - if (result != MA_SUCCESS) { - return result; - } - } - } - - /* Slightly slower path for copying. */ - if (pFramesOut != pFramesIn) { - ma_uint32 iFrame; - - /* */ if (pBPF->format == ma_format_f32) { - /* */ float* pFramesOutF32 = ( float*)pFramesOut; - const float* pFramesInF32 = (const float*)pFramesIn; - - for (iFrame = 0; iFrame < frameCount; iFrame += 1) { - MA_COPY_MEMORY(pFramesOutF32, pFramesInF32, ma_get_bytes_per_frame(pBPF->format, pBPF->channels)); - - for (ibpf2 = 0; ibpf2 < pBPF->bpf2Count; ibpf2 += 1) { - ma_bpf2_process_pcm_frame_f32(&pBPF->pBPF2[ibpf2], pFramesOutF32, pFramesOutF32); - } - - pFramesOutF32 += pBPF->channels; - pFramesInF32 += pBPF->channels; - } - } else if (pBPF->format == ma_format_s16) { - /* */ ma_int16* pFramesOutS16 = ( ma_int16*)pFramesOut; - const ma_int16* pFramesInS16 = (const ma_int16*)pFramesIn; - - for (iFrame = 0; iFrame < frameCount; iFrame += 1) { - MA_COPY_MEMORY(pFramesOutS16, pFramesInS16, ma_get_bytes_per_frame(pBPF->format, pBPF->channels)); - - for (ibpf2 = 0; ibpf2 < pBPF->bpf2Count; ibpf2 += 1) { - ma_bpf2_process_pcm_frame_s16(&pBPF->pBPF2[ibpf2], pFramesOutS16, pFramesOutS16); - } - - pFramesOutS16 += pBPF->channels; - pFramesInS16 += pBPF->channels; - } - } else { - MA_ASSERT(MA_FALSE); - return MA_INVALID_OPERATION; /* Should never hit this. */ - } - } - - return MA_SUCCESS; -} - -MA_API ma_uint32 ma_bpf_get_latency(const ma_bpf* pBPF) -{ - if (pBPF == NULL) { - return 0; - } - - return pBPF->bpf2Count*2; -} - - -/************************************************************************************************************************************************************** - -Notching Filter - -**************************************************************************************************************************************************************/ -MA_API ma_notch2_config ma_notch2_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRate, double q, double frequency) -{ - ma_notch2_config config; - - MA_ZERO_OBJECT(&config); - config.format = format; - config.channels = channels; - config.sampleRate = sampleRate; - config.q = q; - config.frequency = frequency; - - if (config.q == 0) { - config.q = 0.707107; - } - - return config; -} - - -static MA_INLINE ma_biquad_config ma_notch2__get_biquad_config(const ma_notch2_config* pConfig) -{ - ma_biquad_config bqConfig; - double q; - double w; - double s; - double c; - double a; - - MA_ASSERT(pConfig != NULL); - - q = pConfig->q; - w = 2 * MA_PI_D * pConfig->frequency / pConfig->sampleRate; - s = ma_sind(w); - c = ma_cosd(w); - a = s / (2*q); - - bqConfig.b0 = 1; - bqConfig.b1 = -2 * c; - bqConfig.b2 = 1; - bqConfig.a0 = 1 + a; - bqConfig.a1 = -2 * c; - bqConfig.a2 = 1 - a; - - bqConfig.format = pConfig->format; - bqConfig.channels = pConfig->channels; - - return bqConfig; -} - -MA_API ma_result ma_notch2_get_heap_size(const ma_notch2_config* pConfig, size_t* pHeapSizeInBytes) -{ - ma_biquad_config bqConfig; - bqConfig = ma_notch2__get_biquad_config(pConfig); - - return ma_biquad_get_heap_size(&bqConfig, pHeapSizeInBytes); -} - -MA_API ma_result ma_notch2_init_preallocated(const ma_notch2_config* pConfig, void* pHeap, ma_notch2* pFilter) -{ - ma_result result; - ma_biquad_config bqConfig; - - if (pFilter == NULL) { - return MA_INVALID_ARGS; - } - - MA_ZERO_OBJECT(pFilter); - - if (pConfig == NULL) { - return MA_INVALID_ARGS; - } - - bqConfig = ma_notch2__get_biquad_config(pConfig); - result = ma_biquad_init_preallocated(&bqConfig, pHeap, &pFilter->bq); - if (result != MA_SUCCESS) { - return result; - } - - return MA_SUCCESS; -} - -MA_API ma_result ma_notch2_init(const ma_notch2_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_notch2* pFilter) -{ - ma_result result; - size_t heapSizeInBytes; - void* pHeap; - - result = ma_notch2_get_heap_size(pConfig, &heapSizeInBytes); - if (result != MA_SUCCESS) { - return result; - } - - if (heapSizeInBytes > 0) { - pHeap = ma_malloc(heapSizeInBytes, pAllocationCallbacks); - if (pHeap == NULL) { - return MA_OUT_OF_MEMORY; - } - } else { - pHeap = NULL; - } - - result = ma_notch2_init_preallocated(pConfig, pHeap, pFilter); - if (result != MA_SUCCESS) { - ma_free(pHeap, pAllocationCallbacks); - return result; - } - - pFilter->bq._ownsHeap = MA_TRUE; /* <-- This will cause the biquad to take ownership of the heap and free it when it's uninitialized. */ - return MA_SUCCESS; -} - -MA_API void ma_notch2_uninit(ma_notch2* pFilter, const ma_allocation_callbacks* pAllocationCallbacks) -{ - if (pFilter == NULL) { - return; - } - - ma_biquad_uninit(&pFilter->bq, pAllocationCallbacks); /* <-- This will free the heap allocation. */ -} - -MA_API ma_result ma_notch2_reinit(const ma_notch2_config* pConfig, ma_notch2* pFilter) -{ - ma_result result; - ma_biquad_config bqConfig; - - if (pFilter == NULL || pConfig == NULL) { - return MA_INVALID_ARGS; - } - - bqConfig = ma_notch2__get_biquad_config(pConfig); - result = ma_biquad_reinit(&bqConfig, &pFilter->bq); - if (result != MA_SUCCESS) { - return result; - } - - return MA_SUCCESS; -} - -static MA_INLINE void ma_notch2_process_pcm_frame_s16(ma_notch2* pFilter, ma_int16* pFrameOut, const ma_int16* pFrameIn) -{ - ma_biquad_process_pcm_frame_s16(&pFilter->bq, pFrameOut, pFrameIn); -} - -static MA_INLINE void ma_notch2_process_pcm_frame_f32(ma_notch2* pFilter, float* pFrameOut, const float* pFrameIn) -{ - ma_biquad_process_pcm_frame_f32(&pFilter->bq, pFrameOut, pFrameIn); -} - -MA_API ma_result ma_notch2_process_pcm_frames(ma_notch2* pFilter, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount) -{ - if (pFilter == NULL) { - return MA_INVALID_ARGS; - } - - return ma_biquad_process_pcm_frames(&pFilter->bq, pFramesOut, pFramesIn, frameCount); -} - -MA_API ma_uint32 ma_notch2_get_latency(const ma_notch2* pFilter) -{ - if (pFilter == NULL) { - return 0; - } - - return ma_biquad_get_latency(&pFilter->bq); -} - - - -/************************************************************************************************************************************************************** - -Peaking EQ Filter - -**************************************************************************************************************************************************************/ -MA_API ma_peak2_config ma_peak2_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRate, double gainDB, double q, double frequency) -{ - ma_peak2_config config; - - MA_ZERO_OBJECT(&config); - config.format = format; - config.channels = channels; - config.sampleRate = sampleRate; - config.gainDB = gainDB; - config.q = q; - config.frequency = frequency; - - if (config.q == 0) { - config.q = 0.707107; - } - - return config; -} - - -static MA_INLINE ma_biquad_config ma_peak2__get_biquad_config(const ma_peak2_config* pConfig) -{ - ma_biquad_config bqConfig; - double q; - double w; - double s; - double c; - double a; - double A; - - MA_ASSERT(pConfig != NULL); - - q = pConfig->q; - w = 2 * MA_PI_D * pConfig->frequency / pConfig->sampleRate; - s = ma_sind(w); - c = ma_cosd(w); - a = s / (2*q); - A = ma_powd(10, (pConfig->gainDB / 40)); - - bqConfig.b0 = 1 + (a * A); - bqConfig.b1 = -2 * c; - bqConfig.b2 = 1 - (a * A); - bqConfig.a0 = 1 + (a / A); - bqConfig.a1 = -2 * c; - bqConfig.a2 = 1 - (a / A); - - bqConfig.format = pConfig->format; - bqConfig.channels = pConfig->channels; - - return bqConfig; -} - -MA_API ma_result ma_peak2_get_heap_size(const ma_peak2_config* pConfig, size_t* pHeapSizeInBytes) -{ - ma_biquad_config bqConfig; - bqConfig = ma_peak2__get_biquad_config(pConfig); - - return ma_biquad_get_heap_size(&bqConfig, pHeapSizeInBytes); -} - -MA_API ma_result ma_peak2_init_preallocated(const ma_peak2_config* pConfig, void* pHeap, ma_peak2* pFilter) -{ - ma_result result; - ma_biquad_config bqConfig; - - if (pFilter == NULL) { - return MA_INVALID_ARGS; - } - - MA_ZERO_OBJECT(pFilter); - - if (pConfig == NULL) { - return MA_INVALID_ARGS; - } - - bqConfig = ma_peak2__get_biquad_config(pConfig); - result = ma_biquad_init_preallocated(&bqConfig, pHeap, &pFilter->bq); - if (result != MA_SUCCESS) { - return result; - } - - return MA_SUCCESS; -} - -MA_API ma_result ma_peak2_init(const ma_peak2_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_peak2* pFilter) -{ - ma_result result; - size_t heapSizeInBytes; - void* pHeap; - - result = ma_peak2_get_heap_size(pConfig, &heapSizeInBytes); - if (result != MA_SUCCESS) { - return result; - } - - if (heapSizeInBytes > 0) { - pHeap = ma_malloc(heapSizeInBytes, pAllocationCallbacks); - if (pHeap == NULL) { - return MA_OUT_OF_MEMORY; - } - } else { - pHeap = NULL; - } - - result = ma_peak2_init_preallocated(pConfig, pHeap, pFilter); - if (result != MA_SUCCESS) { - ma_free(pHeap, pAllocationCallbacks); - return result; - } - - pFilter->bq._ownsHeap = MA_TRUE; /* <-- This will cause the biquad to take ownership of the heap and free it when it's uninitialized. */ - return MA_SUCCESS; -} - -MA_API void ma_peak2_uninit(ma_peak2* pFilter, const ma_allocation_callbacks* pAllocationCallbacks) -{ - if (pFilter == NULL) { - return; - } - - ma_biquad_uninit(&pFilter->bq, pAllocationCallbacks); /* <-- This will free the heap allocation. */ -} - -MA_API ma_result ma_peak2_reinit(const ma_peak2_config* pConfig, ma_peak2* pFilter) -{ - ma_result result; - ma_biquad_config bqConfig; - - if (pFilter == NULL || pConfig == NULL) { - return MA_INVALID_ARGS; - } - - bqConfig = ma_peak2__get_biquad_config(pConfig); - result = ma_biquad_reinit(&bqConfig, &pFilter->bq); - if (result != MA_SUCCESS) { - return result; - } - - return MA_SUCCESS; -} - -static MA_INLINE void ma_peak2_process_pcm_frame_s16(ma_peak2* pFilter, ma_int16* pFrameOut, const ma_int16* pFrameIn) -{ - ma_biquad_process_pcm_frame_s16(&pFilter->bq, pFrameOut, pFrameIn); -} - -static MA_INLINE void ma_peak2_process_pcm_frame_f32(ma_peak2* pFilter, float* pFrameOut, const float* pFrameIn) -{ - ma_biquad_process_pcm_frame_f32(&pFilter->bq, pFrameOut, pFrameIn); -} - -MA_API ma_result ma_peak2_process_pcm_frames(ma_peak2* pFilter, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount) -{ - if (pFilter == NULL) { - return MA_INVALID_ARGS; - } - - return ma_biquad_process_pcm_frames(&pFilter->bq, pFramesOut, pFramesIn, frameCount); -} - -MA_API ma_uint32 ma_peak2_get_latency(const ma_peak2* pFilter) -{ - if (pFilter == NULL) { - return 0; - } - - return ma_biquad_get_latency(&pFilter->bq); -} - - -/************************************************************************************************************************************************************** - -Low Shelf Filter - -**************************************************************************************************************************************************************/ -MA_API ma_loshelf2_config ma_loshelf2_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRate, double gainDB, double shelfSlope, double frequency) -{ - ma_loshelf2_config config; - - MA_ZERO_OBJECT(&config); - config.format = format; - config.channels = channels; - config.sampleRate = sampleRate; - config.gainDB = gainDB; - config.shelfSlope = shelfSlope; - config.frequency = frequency; - - return config; -} - - -static MA_INLINE ma_biquad_config ma_loshelf2__get_biquad_config(const ma_loshelf2_config* pConfig) -{ - ma_biquad_config bqConfig; - double w; - double s; - double c; - double A; - double S; - double a; - double sqrtA; - - MA_ASSERT(pConfig != NULL); - - w = 2 * MA_PI_D * pConfig->frequency / pConfig->sampleRate; - s = ma_sind(w); - c = ma_cosd(w); - A = ma_powd(10, (pConfig->gainDB / 40)); - S = pConfig->shelfSlope; - a = s/2 * ma_sqrtd((A + 1/A) * (1/S - 1) + 2); - sqrtA = 2*ma_sqrtd(A)*a; - - bqConfig.b0 = A * ((A + 1) - (A - 1)*c + sqrtA); - bqConfig.b1 = 2 * A * ((A - 1) - (A + 1)*c); - bqConfig.b2 = A * ((A + 1) - (A - 1)*c - sqrtA); - bqConfig.a0 = (A + 1) + (A - 1)*c + sqrtA; - bqConfig.a1 = -2 * ((A - 1) + (A + 1)*c); - bqConfig.a2 = (A + 1) + (A - 1)*c - sqrtA; - - bqConfig.format = pConfig->format; - bqConfig.channels = pConfig->channels; - - return bqConfig; -} - -MA_API ma_result ma_loshelf2_get_heap_size(const ma_loshelf2_config* pConfig, size_t* pHeapSizeInBytes) -{ - ma_biquad_config bqConfig; - bqConfig = ma_loshelf2__get_biquad_config(pConfig); - - return ma_biquad_get_heap_size(&bqConfig, pHeapSizeInBytes); -} - -MA_API ma_result ma_loshelf2_init_preallocated(const ma_loshelf2_config* pConfig, void* pHeap, ma_loshelf2* pFilter) -{ - ma_result result; - ma_biquad_config bqConfig; - - if (pFilter == NULL) { - return MA_INVALID_ARGS; - } - - MA_ZERO_OBJECT(pFilter); - - if (pConfig == NULL) { - return MA_INVALID_ARGS; - } - - bqConfig = ma_loshelf2__get_biquad_config(pConfig); - result = ma_biquad_init_preallocated(&bqConfig, pHeap, &pFilter->bq); - if (result != MA_SUCCESS) { - return result; - } - - return MA_SUCCESS; -} - -MA_API ma_result ma_loshelf2_init(const ma_loshelf2_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_loshelf2* pFilter) -{ - ma_result result; - size_t heapSizeInBytes; - void* pHeap; - - result = ma_loshelf2_get_heap_size(pConfig, &heapSizeInBytes); - if (result != MA_SUCCESS) { - return result; - } - - if (heapSizeInBytes > 0) { - pHeap = ma_malloc(heapSizeInBytes, pAllocationCallbacks); - if (pHeap == NULL) { - return MA_OUT_OF_MEMORY; - } - } else { - pHeap = NULL; - } - - result = ma_loshelf2_init_preallocated(pConfig, pHeap, pFilter); - if (result != MA_SUCCESS) { - ma_free(pHeap, pAllocationCallbacks); - return result; - } - - pFilter->bq._ownsHeap = MA_TRUE; /* <-- This will cause the biquad to take ownership of the heap and free it when it's uninitialized. */ - return MA_SUCCESS; -} - -MA_API void ma_loshelf2_uninit(ma_loshelf2* pFilter, const ma_allocation_callbacks* pAllocationCallbacks) -{ - if (pFilter == NULL) { - return; - } - - ma_biquad_uninit(&pFilter->bq, pAllocationCallbacks); /* <-- This will free the heap allocation. */ -} - -MA_API ma_result ma_loshelf2_reinit(const ma_loshelf2_config* pConfig, ma_loshelf2* pFilter) -{ - ma_result result; - ma_biquad_config bqConfig; - - if (pFilter == NULL || pConfig == NULL) { - return MA_INVALID_ARGS; - } - - bqConfig = ma_loshelf2__get_biquad_config(pConfig); - result = ma_biquad_reinit(&bqConfig, &pFilter->bq); - if (result != MA_SUCCESS) { - return result; - } - - return MA_SUCCESS; -} - -static MA_INLINE void ma_loshelf2_process_pcm_frame_s16(ma_loshelf2* pFilter, ma_int16* pFrameOut, const ma_int16* pFrameIn) -{ - ma_biquad_process_pcm_frame_s16(&pFilter->bq, pFrameOut, pFrameIn); -} - -static MA_INLINE void ma_loshelf2_process_pcm_frame_f32(ma_loshelf2* pFilter, float* pFrameOut, const float* pFrameIn) -{ - ma_biquad_process_pcm_frame_f32(&pFilter->bq, pFrameOut, pFrameIn); -} - -MA_API ma_result ma_loshelf2_process_pcm_frames(ma_loshelf2* pFilter, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount) -{ - if (pFilter == NULL) { - return MA_INVALID_ARGS; - } - - return ma_biquad_process_pcm_frames(&pFilter->bq, pFramesOut, pFramesIn, frameCount); -} - -MA_API ma_uint32 ma_loshelf2_get_latency(const ma_loshelf2* pFilter) -{ - if (pFilter == NULL) { - return 0; - } - - return ma_biquad_get_latency(&pFilter->bq); -} - - -/************************************************************************************************************************************************************** - -High Shelf Filter - -**************************************************************************************************************************************************************/ -MA_API ma_hishelf2_config ma_hishelf2_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRate, double gainDB, double shelfSlope, double frequency) -{ - ma_hishelf2_config config; - - MA_ZERO_OBJECT(&config); - config.format = format; - config.channels = channels; - config.sampleRate = sampleRate; - config.gainDB = gainDB; - config.shelfSlope = shelfSlope; - config.frequency = frequency; - - return config; -} - - -static MA_INLINE ma_biquad_config ma_hishelf2__get_biquad_config(const ma_hishelf2_config* pConfig) -{ - ma_biquad_config bqConfig; - double w; - double s; - double c; - double A; - double S; - double a; - double sqrtA; - - MA_ASSERT(pConfig != NULL); - - w = 2 * MA_PI_D * pConfig->frequency / pConfig->sampleRate; - s = ma_sind(w); - c = ma_cosd(w); - A = ma_powd(10, (pConfig->gainDB / 40)); - S = pConfig->shelfSlope; - a = s/2 * ma_sqrtd((A + 1/A) * (1/S - 1) + 2); - sqrtA = 2*ma_sqrtd(A)*a; - - bqConfig.b0 = A * ((A + 1) + (A - 1)*c + sqrtA); - bqConfig.b1 = -2 * A * ((A - 1) + (A + 1)*c); - bqConfig.b2 = A * ((A + 1) + (A - 1)*c - sqrtA); - bqConfig.a0 = (A + 1) - (A - 1)*c + sqrtA; - bqConfig.a1 = 2 * ((A - 1) - (A + 1)*c); - bqConfig.a2 = (A + 1) - (A - 1)*c - sqrtA; - - bqConfig.format = pConfig->format; - bqConfig.channels = pConfig->channels; - - return bqConfig; -} - -MA_API ma_result ma_hishelf2_get_heap_size(const ma_hishelf2_config* pConfig, size_t* pHeapSizeInBytes) -{ - ma_biquad_config bqConfig; - bqConfig = ma_hishelf2__get_biquad_config(pConfig); - - return ma_biquad_get_heap_size(&bqConfig, pHeapSizeInBytes); -} - -MA_API ma_result ma_hishelf2_init_preallocated(const ma_hishelf2_config* pConfig, void* pHeap, ma_hishelf2* pFilter) -{ - ma_result result; - ma_biquad_config bqConfig; - - if (pFilter == NULL) { - return MA_INVALID_ARGS; - } - - MA_ZERO_OBJECT(pFilter); - - if (pConfig == NULL) { - return MA_INVALID_ARGS; - } - - bqConfig = ma_hishelf2__get_biquad_config(pConfig); - result = ma_biquad_init_preallocated(&bqConfig, pHeap, &pFilter->bq); - if (result != MA_SUCCESS) { - return result; - } - - return MA_SUCCESS; -} - -MA_API ma_result ma_hishelf2_init(const ma_hishelf2_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_hishelf2* pFilter) -{ - ma_result result; - size_t heapSizeInBytes; - void* pHeap; - - result = ma_hishelf2_get_heap_size(pConfig, &heapSizeInBytes); - if (result != MA_SUCCESS) { - return result; - } - - if (heapSizeInBytes > 0) { - pHeap = ma_malloc(heapSizeInBytes, pAllocationCallbacks); - if (pHeap == NULL) { - return MA_OUT_OF_MEMORY; - } - } else { - pHeap = NULL; - } - - result = ma_hishelf2_init_preallocated(pConfig, pHeap, pFilter); - if (result != MA_SUCCESS) { - ma_free(pHeap, pAllocationCallbacks); - return result; - } - - pFilter->bq._ownsHeap = MA_TRUE; /* <-- This will cause the biquad to take ownership of the heap and free it when it's uninitialized. */ - return MA_SUCCESS; -} - -MA_API void ma_hishelf2_uninit(ma_hishelf2* pFilter, const ma_allocation_callbacks* pAllocationCallbacks) -{ - if (pFilter == NULL) { - return; - } - - ma_biquad_uninit(&pFilter->bq, pAllocationCallbacks); /* <-- This will free the heap allocation. */ -} - -MA_API ma_result ma_hishelf2_reinit(const ma_hishelf2_config* pConfig, ma_hishelf2* pFilter) -{ - ma_result result; - ma_biquad_config bqConfig; - - if (pFilter == NULL || pConfig == NULL) { - return MA_INVALID_ARGS; - } - - bqConfig = ma_hishelf2__get_biquad_config(pConfig); - result = ma_biquad_reinit(&bqConfig, &pFilter->bq); - if (result != MA_SUCCESS) { - return result; - } - - return MA_SUCCESS; -} - -static MA_INLINE void ma_hishelf2_process_pcm_frame_s16(ma_hishelf2* pFilter, ma_int16* pFrameOut, const ma_int16* pFrameIn) -{ - ma_biquad_process_pcm_frame_s16(&pFilter->bq, pFrameOut, pFrameIn); -} - -static MA_INLINE void ma_hishelf2_process_pcm_frame_f32(ma_hishelf2* pFilter, float* pFrameOut, const float* pFrameIn) -{ - ma_biquad_process_pcm_frame_f32(&pFilter->bq, pFrameOut, pFrameIn); -} - -MA_API ma_result ma_hishelf2_process_pcm_frames(ma_hishelf2* pFilter, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount) -{ - if (pFilter == NULL) { - return MA_INVALID_ARGS; - } - - return ma_biquad_process_pcm_frames(&pFilter->bq, pFramesOut, pFramesIn, frameCount); -} - -MA_API ma_uint32 ma_hishelf2_get_latency(const ma_hishelf2* pFilter) -{ - if (pFilter == NULL) { - return 0; - } - - return ma_biquad_get_latency(&pFilter->bq); -} - - - -/* -Delay -*/ -MA_API ma_delay_config ma_delay_config_init(ma_uint32 channels, ma_uint32 sampleRate, ma_uint32 delayInFrames, float decay) -{ - ma_delay_config config; - - MA_ZERO_OBJECT(&config); - config.channels = channels; - config.sampleRate = sampleRate; - config.delayInFrames = delayInFrames; - config.delayStart = (decay == 0) ? MA_TRUE : MA_FALSE; /* Delay the start if it looks like we're not configuring an echo. */ - config.wet = 1; - config.dry = 1; - config.decay = decay; - - return config; -} - - -MA_API ma_result ma_delay_init(const ma_delay_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_delay* pDelay) -{ - if (pDelay == NULL) { - return MA_INVALID_ARGS; - } - - MA_ZERO_OBJECT(pDelay); - - if (pConfig == NULL) { - return MA_INVALID_ARGS; - } - - if (pConfig->decay < 0 || pConfig->decay > 1) { - return MA_INVALID_ARGS; - } - - pDelay->config = *pConfig; - pDelay->bufferSizeInFrames = pConfig->delayInFrames; - pDelay->cursor = 0; - - pDelay->pBuffer = (float*)ma_malloc((size_t)(pDelay->bufferSizeInFrames * ma_get_bytes_per_frame(ma_format_f32, pConfig->channels)), pAllocationCallbacks); - if (pDelay->pBuffer == NULL) { - return MA_OUT_OF_MEMORY; - } - - ma_silence_pcm_frames(pDelay->pBuffer, pDelay->bufferSizeInFrames, ma_format_f32, pConfig->channels); - - return MA_SUCCESS; -} - -MA_API void ma_delay_uninit(ma_delay* pDelay, const ma_allocation_callbacks* pAllocationCallbacks) -{ - if (pDelay == NULL) { - return; - } - - ma_free(pDelay->pBuffer, pAllocationCallbacks); -} - -MA_API ma_result ma_delay_process_pcm_frames(ma_delay* pDelay, void* pFramesOut, const void* pFramesIn, ma_uint32 frameCount) -{ - ma_uint32 iFrame; - ma_uint32 iChannel; - float* pFramesOutF32 = (float*)pFramesOut; - const float* pFramesInF32 = (const float*)pFramesIn; - - if (pDelay == NULL || pFramesOut == NULL || pFramesIn == NULL) { - return MA_INVALID_ARGS; - } - - for (iFrame = 0; iFrame < frameCount; iFrame += 1) { - for (iChannel = 0; iChannel < pDelay->config.channels; iChannel += 1) { - ma_uint32 iBuffer = (pDelay->cursor * pDelay->config.channels) + iChannel; - - if (pDelay->config.delayStart) { - /* Delayed start. */ - - /* Read */ - pFramesOutF32[iChannel] = pDelay->pBuffer[iBuffer] * pDelay->config.wet; - - /* Feedback */ - pDelay->pBuffer[iBuffer] = (pDelay->pBuffer[iBuffer] * pDelay->config.decay) + (pFramesInF32[iChannel] * pDelay->config.dry); - } else { - /* Immediate start */ - - /* Feedback */ - pDelay->pBuffer[iBuffer] = (pDelay->pBuffer[iBuffer] * pDelay->config.decay) + (pFramesInF32[iChannel] * pDelay->config.dry); - - /* Read */ - pFramesOutF32[iChannel] = pDelay->pBuffer[iBuffer] * pDelay->config.wet; - } - } - - pDelay->cursor = (pDelay->cursor + 1) % pDelay->bufferSizeInFrames; - - pFramesOutF32 += pDelay->config.channels; - pFramesInF32 += pDelay->config.channels; - } - - return MA_SUCCESS; -} - -MA_API void ma_delay_set_wet(ma_delay* pDelay, float value) -{ - if (pDelay == NULL) { - return; - } - - pDelay->config.wet = value; -} - -MA_API float ma_delay_get_wet(const ma_delay* pDelay) -{ - if (pDelay == NULL) { - return 0; - } - - return pDelay->config.wet; -} - -MA_API void ma_delay_set_dry(ma_delay* pDelay, float value) -{ - if (pDelay == NULL) { - return; - } - - pDelay->config.dry = value; -} - -MA_API float ma_delay_get_dry(const ma_delay* pDelay) -{ - if (pDelay == NULL) { - return 0; - } - - return pDelay->config.dry; -} - -MA_API void ma_delay_set_decay(ma_delay* pDelay, float value) -{ - if (pDelay == NULL) { - return; - } - - pDelay->config.decay = value; -} - -MA_API float ma_delay_get_decay(const ma_delay* pDelay) -{ - if (pDelay == NULL) { - return 0; - } - - return pDelay->config.decay; -} - - -MA_API ma_gainer_config ma_gainer_config_init(ma_uint32 channels, ma_uint32 smoothTimeInFrames) -{ - ma_gainer_config config; - - MA_ZERO_OBJECT(&config); - config.channels = channels; - config.smoothTimeInFrames = smoothTimeInFrames; - - return config; -} - - -typedef struct -{ - size_t sizeInBytes; - size_t oldGainsOffset; - size_t newGainsOffset; -} ma_gainer_heap_layout; - -static ma_result ma_gainer_get_heap_layout(const ma_gainer_config* pConfig, ma_gainer_heap_layout* pHeapLayout) -{ - MA_ASSERT(pHeapLayout != NULL); - - MA_ZERO_OBJECT(pHeapLayout); - - if (pConfig == NULL) { - return MA_INVALID_ARGS; - } - - if (pConfig->channels == 0) { - return MA_INVALID_ARGS; - } - - pHeapLayout->sizeInBytes = 0; - - /* Old gains. */ - pHeapLayout->oldGainsOffset = pHeapLayout->sizeInBytes; - pHeapLayout->sizeInBytes += sizeof(float) * pConfig->channels; - - /* New gains. */ - pHeapLayout->newGainsOffset = pHeapLayout->sizeInBytes; - pHeapLayout->sizeInBytes += sizeof(float) * pConfig->channels; - - /* Alignment. */ - pHeapLayout->sizeInBytes = ma_align_64(pHeapLayout->sizeInBytes); - - return MA_SUCCESS; -} - - -MA_API ma_result ma_gainer_get_heap_size(const ma_gainer_config* pConfig, size_t* pHeapSizeInBytes) -{ - ma_result result; - ma_gainer_heap_layout heapLayout; - - if (pHeapSizeInBytes == NULL) { - return MA_INVALID_ARGS; - } - - *pHeapSizeInBytes = 0; - - result = ma_gainer_get_heap_layout(pConfig, &heapLayout); - if (result != MA_SUCCESS) { - return MA_INVALID_ARGS; - } - - *pHeapSizeInBytes = heapLayout.sizeInBytes; - - return MA_SUCCESS; -} - - -MA_API ma_result ma_gainer_init_preallocated(const ma_gainer_config* pConfig, void* pHeap, ma_gainer* pGainer) -{ - ma_result result; - ma_gainer_heap_layout heapLayout; - ma_uint32 iChannel; - - if (pGainer == NULL) { - return MA_INVALID_ARGS; - } - - MA_ZERO_OBJECT(pGainer); - - if (pConfig == NULL || pHeap == NULL) { - return MA_INVALID_ARGS; - } - - result = ma_gainer_get_heap_layout(pConfig, &heapLayout); - if (result != MA_SUCCESS) { - return result; - } - - pGainer->_pHeap = pHeap; - MA_ZERO_MEMORY(pHeap, heapLayout.sizeInBytes); - - pGainer->pOldGains = (float*)ma_offset_ptr(pHeap, heapLayout.oldGainsOffset); - pGainer->pNewGains = (float*)ma_offset_ptr(pHeap, heapLayout.newGainsOffset); - pGainer->masterVolume = 1; - - pGainer->config = *pConfig; - pGainer->t = (ma_uint32)-1; /* No interpolation by default. */ - - for (iChannel = 0; iChannel < pConfig->channels; iChannel += 1) { - pGainer->pOldGains[iChannel] = 1; - pGainer->pNewGains[iChannel] = 1; - } - - return MA_SUCCESS; -} - -MA_API ma_result ma_gainer_init(const ma_gainer_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_gainer* pGainer) -{ - ma_result result; - size_t heapSizeInBytes; - void* pHeap; - - result = ma_gainer_get_heap_size(pConfig, &heapSizeInBytes); - if (result != MA_SUCCESS) { - return result; /* Failed to retrieve the size of the heap allocation. */ - } - - if (heapSizeInBytes > 0) { - pHeap = ma_malloc(heapSizeInBytes, pAllocationCallbacks); - if (pHeap == NULL) { - return MA_OUT_OF_MEMORY; - } - } else { - pHeap = NULL; - } - - result = ma_gainer_init_preallocated(pConfig, pHeap, pGainer); - if (result != MA_SUCCESS) { - ma_free(pHeap, pAllocationCallbacks); - return result; - } - - pGainer->_ownsHeap = MA_TRUE; - return MA_SUCCESS; -} - -MA_API void ma_gainer_uninit(ma_gainer* pGainer, const ma_allocation_callbacks* pAllocationCallbacks) -{ - if (pGainer == NULL) { - return; - } - - if (pGainer->_ownsHeap) { - ma_free(pGainer->_pHeap, pAllocationCallbacks); - } -} - -static float ma_gainer_calculate_current_gain(const ma_gainer* pGainer, ma_uint32 channel) -{ - float a = (float)pGainer->t / pGainer->config.smoothTimeInFrames; - return ma_mix_f32_fast(pGainer->pOldGains[channel], pGainer->pNewGains[channel], a); -} - -static /*__attribute__((noinline))*/ ma_result ma_gainer_process_pcm_frames_internal(ma_gainer * pGainer, void* MA_RESTRICT pFramesOut, const void* MA_RESTRICT pFramesIn, ma_uint64 frameCount) -{ - ma_uint64 iFrame; - ma_uint32 iChannel; - ma_uint64 interpolatedFrameCount; - - MA_ASSERT(pGainer != NULL); - - /* - We don't necessarily need to apply a linear interpolation for the entire frameCount frames. When - linear interpolation is not needed we can do a simple volume adjustment which will be more - efficient than a lerp with an alpha value of 1. - - To do this, all we need to do is determine how many frames need to have a lerp applied. Then we - just process that number of frames with linear interpolation. After that we run on an optimized - path which just applies the new gains without a lerp. - */ - if (pGainer->t >= pGainer->config.smoothTimeInFrames) { - interpolatedFrameCount = 0; - } else { - interpolatedFrameCount = pGainer->t - pGainer->config.smoothTimeInFrames; - if (interpolatedFrameCount > frameCount) { - interpolatedFrameCount = frameCount; - } - } - - /* - Start off with our interpolated frames. When we do this, we'll adjust frameCount and our pointers - so that the fast path can work naturally without consideration of the interpolated path. - */ - if (interpolatedFrameCount > 0) { - /* We can allow the input and output buffers to be null in which case we'll just update the internal timer. */ - if (pFramesOut != NULL && pFramesIn != NULL) { - /* - All we're really doing here is moving the old gains towards the new gains. We don't want to - be modifying the gains inside the ma_gainer object because that will break things. Instead - we can make a copy here on the stack. For extreme channel counts we can fall back to a slower - implementation which just uses a standard lerp. - */ - float* pFramesOutF32 = (float*)pFramesOut; - const float* pFramesInF32 = (const float*)pFramesIn; - float a = (float)pGainer->t / pGainer->config.smoothTimeInFrames; - float d = 1.0f / pGainer->config.smoothTimeInFrames; - - if (pGainer->config.channels <= 32) { - float pRunningGain[32]; - float pRunningGainDelta[32]; /* Could this be heap-allocated as part of the ma_gainer object? */ - - /* Initialize the running gain. */ - for (iChannel = 0; iChannel < pGainer->config.channels; iChannel += 1) { - float t = (pGainer->pNewGains[iChannel] - pGainer->pOldGains[iChannel]) * pGainer->masterVolume; - pRunningGainDelta[iChannel] = t * d; - pRunningGain[iChannel] = (pGainer->pOldGains[iChannel] * pGainer->masterVolume) + (t * a); - } - - iFrame = 0; - - /* Optimized paths for common channel counts. This is mostly just experimenting with some SIMD ideas. It's not necessarily final. */ - if (pGainer->config.channels == 2) { - #if defined(MA_SUPPORT_SSE2) - if (ma_has_sse2()) { - ma_uint64 unrolledLoopCount = interpolatedFrameCount >> 1; - - /* Expand some arrays so we can have a clean SIMD loop below. */ - __m128 runningGainDelta0 = _mm_set_ps(pRunningGainDelta[1], pRunningGainDelta[0], pRunningGainDelta[1], pRunningGainDelta[0]); - __m128 runningGain0 = _mm_set_ps(pRunningGain[1] + pRunningGainDelta[1], pRunningGain[0] + pRunningGainDelta[0], pRunningGain[1], pRunningGain[0]); - - for (; iFrame < unrolledLoopCount; iFrame += 1) { - _mm_storeu_ps(&pFramesOutF32[iFrame*4 + 0], _mm_mul_ps(_mm_loadu_ps(&pFramesInF32[iFrame*4 + 0]), runningGain0)); - runningGain0 = _mm_add_ps(runningGain0, runningGainDelta0); - } - - iFrame = unrolledLoopCount << 1; - } else - #endif - { - /* - Two different scalar implementations here. Clang (and I assume GCC) will vectorize - both of these, but the bottom version results in a nicer vectorization with less - instructions emitted. The problem, however, is that the bottom version runs slower - when compiled with MSVC. The top version will be partially vectorized by MSVC. - */ - #if defined(_MSC_VER) && !defined(__clang__) - ma_uint64 unrolledLoopCount = interpolatedFrameCount >> 1; - - /* Expand some arrays so we can have a clean 4x SIMD operation in the loop. */ - pRunningGainDelta[2] = pRunningGainDelta[0]; - pRunningGainDelta[3] = pRunningGainDelta[1]; - pRunningGain[2] = pRunningGain[0] + pRunningGainDelta[0]; - pRunningGain[3] = pRunningGain[1] + pRunningGainDelta[1]; - - for (; iFrame < unrolledLoopCount; iFrame += 1) { - pFramesOutF32[iFrame*4 + 0] = pFramesInF32[iFrame*4 + 0] * pRunningGain[0]; - pFramesOutF32[iFrame*4 + 1] = pFramesInF32[iFrame*4 + 1] * pRunningGain[1]; - pFramesOutF32[iFrame*4 + 2] = pFramesInF32[iFrame*4 + 2] * pRunningGain[2]; - pFramesOutF32[iFrame*4 + 3] = pFramesInF32[iFrame*4 + 3] * pRunningGain[3]; - - /* Move the running gain forward towards the new gain. */ - pRunningGain[0] += pRunningGainDelta[0]; - pRunningGain[1] += pRunningGainDelta[1]; - pRunningGain[2] += pRunningGainDelta[2]; - pRunningGain[3] += pRunningGainDelta[3]; - } - - iFrame = unrolledLoopCount << 1; - #else - for (; iFrame < interpolatedFrameCount; iFrame += 1) { - for (iChannel = 0; iChannel < 2; iChannel += 1) { - pFramesOutF32[iFrame*2 + iChannel] = pFramesInF32[iFrame*2 + iChannel] * pRunningGain[iChannel]; - } - - for (iChannel = 0; iChannel < 2; iChannel += 1) { - pRunningGain[iChannel] += pRunningGainDelta[iChannel]; - } - } - #endif - } - } else if (pGainer->config.channels == 6) { - #if defined(MA_SUPPORT_SSE2) - if (ma_has_sse2()) { - /* - For 6 channels things are a bit more complicated because 6 isn't cleanly divisible by 4. We need to do 2 frames - at a time, meaning we'll be doing 12 samples in a group. Like the stereo case we'll need to expand some arrays - so we can do clean 4x SIMD operations. - */ - ma_uint64 unrolledLoopCount = interpolatedFrameCount >> 1; - - /* Expand some arrays so we can have a clean SIMD loop below. */ - __m128 runningGainDelta0 = _mm_set_ps(pRunningGainDelta[3], pRunningGainDelta[2], pRunningGainDelta[1], pRunningGainDelta[0]); - __m128 runningGainDelta1 = _mm_set_ps(pRunningGainDelta[1], pRunningGainDelta[0], pRunningGainDelta[5], pRunningGainDelta[4]); - __m128 runningGainDelta2 = _mm_set_ps(pRunningGainDelta[5], pRunningGainDelta[4], pRunningGainDelta[3], pRunningGainDelta[2]); - - __m128 runningGain0 = _mm_set_ps(pRunningGain[3], pRunningGain[2], pRunningGain[1], pRunningGain[0]); - __m128 runningGain1 = _mm_set_ps(pRunningGain[1] + pRunningGainDelta[1], pRunningGain[0] + pRunningGainDelta[0], pRunningGain[5], pRunningGain[4]); - __m128 runningGain2 = _mm_set_ps(pRunningGain[5] + pRunningGainDelta[5], pRunningGain[4] + pRunningGainDelta[4], pRunningGain[3] + pRunningGainDelta[3], pRunningGain[2] + pRunningGainDelta[2]); - - for (; iFrame < unrolledLoopCount; iFrame += 1) { - _mm_storeu_ps(&pFramesOutF32[iFrame*12 + 0], _mm_mul_ps(_mm_loadu_ps(&pFramesInF32[iFrame*12 + 0]), runningGain0)); - _mm_storeu_ps(&pFramesOutF32[iFrame*12 + 4], _mm_mul_ps(_mm_loadu_ps(&pFramesInF32[iFrame*12 + 4]), runningGain1)); - _mm_storeu_ps(&pFramesOutF32[iFrame*12 + 8], _mm_mul_ps(_mm_loadu_ps(&pFramesInF32[iFrame*12 + 8]), runningGain2)); - - runningGain0 = _mm_add_ps(runningGain0, runningGainDelta0); - runningGain1 = _mm_add_ps(runningGain1, runningGainDelta1); - runningGain2 = _mm_add_ps(runningGain2, runningGainDelta2); - } - - iFrame = unrolledLoopCount << 1; - } else - #endif - { - for (; iFrame < interpolatedFrameCount; iFrame += 1) { - for (iChannel = 0; iChannel < 6; iChannel += 1) { - pFramesOutF32[iFrame*6 + iChannel] = pFramesInF32[iFrame*6 + iChannel] * pRunningGain[iChannel]; - } - - /* Move the running gain forward towards the new gain. */ - for (iChannel = 0; iChannel < 6; iChannel += 1) { - pRunningGain[iChannel] += pRunningGainDelta[iChannel]; - } - } - } - } else if (pGainer->config.channels == 8) { - /* For 8 channels we can just go over frame by frame and do all eight channels as 2 separate 4x SIMD operations. */ - #if defined(MA_SUPPORT_SSE2) - if (ma_has_sse2()) { - __m128 runningGainDelta0 = _mm_loadu_ps(&pRunningGainDelta[0]); - __m128 runningGainDelta1 = _mm_loadu_ps(&pRunningGainDelta[4]); - __m128 runningGain0 = _mm_loadu_ps(&pRunningGain[0]); - __m128 runningGain1 = _mm_loadu_ps(&pRunningGain[4]); - - for (; iFrame < interpolatedFrameCount; iFrame += 1) { - _mm_storeu_ps(&pFramesOutF32[iFrame*8 + 0], _mm_mul_ps(_mm_loadu_ps(&pFramesInF32[iFrame*8 + 0]), runningGain0)); - _mm_storeu_ps(&pFramesOutF32[iFrame*8 + 4], _mm_mul_ps(_mm_loadu_ps(&pFramesInF32[iFrame*8 + 4]), runningGain1)); - - runningGain0 = _mm_add_ps(runningGain0, runningGainDelta0); - runningGain1 = _mm_add_ps(runningGain1, runningGainDelta1); - } - } else - #endif - { - /* This is crafted so that it auto-vectorizes when compiled with Clang. */ - for (; iFrame < interpolatedFrameCount; iFrame += 1) { - for (iChannel = 0; iChannel < 8; iChannel += 1) { - pFramesOutF32[iFrame*8 + iChannel] = pFramesInF32[iFrame*8 + iChannel] * pRunningGain[iChannel]; - } - - /* Move the running gain forward towards the new gain. */ - for (iChannel = 0; iChannel < 8; iChannel += 1) { - pRunningGain[iChannel] += pRunningGainDelta[iChannel]; - } - } - } - } - - for (; iFrame < interpolatedFrameCount; iFrame += 1) { - for (iChannel = 0; iChannel < pGainer->config.channels; iChannel += 1) { - pFramesOutF32[iFrame*pGainer->config.channels + iChannel] = pFramesInF32[iFrame*pGainer->config.channels + iChannel] * pRunningGain[iChannel]; - pRunningGain[iChannel] += pRunningGainDelta[iChannel]; - } - } - } else { - /* Slower path for extreme channel counts where we can't fit enough on the stack. We could also move this to the heap as part of the ma_gainer object which might even be better since it'll only be updated when the gains actually change. */ - for (iFrame = 0; iFrame < interpolatedFrameCount; iFrame += 1) { - for (iChannel = 0; iChannel < pGainer->config.channels; iChannel += 1) { - pFramesOutF32[iFrame*pGainer->config.channels + iChannel] = pFramesInF32[iFrame*pGainer->config.channels + iChannel] * ma_mix_f32_fast(pGainer->pOldGains[iChannel], pGainer->pNewGains[iChannel], a) * pGainer->masterVolume; - } - - a += d; - } - } - } - - /* Make sure the timer is updated. */ - pGainer->t = (ma_uint32)ma_min(pGainer->t + interpolatedFrameCount, pGainer->config.smoothTimeInFrames); - - /* Adjust our arguments so the next part can work normally. */ - frameCount -= interpolatedFrameCount; - pFramesOut = ma_offset_ptr(pFramesOut, interpolatedFrameCount * sizeof(float)); - pFramesIn = ma_offset_ptr(pFramesIn, interpolatedFrameCount * sizeof(float)); - } - - /* All we need to do here is apply the new gains using an optimized path. */ - if (pFramesOut != NULL && pFramesIn != NULL) { - if (pGainer->config.channels <= 32) { - float gains[32]; - for (iChannel = 0; iChannel < pGainer->config.channels; iChannel += 1) { - gains[iChannel] = pGainer->pNewGains[iChannel] * pGainer->masterVolume; - } - - ma_copy_and_apply_volume_factor_per_channel_f32((float*)pFramesOut, (const float*)pFramesIn, frameCount, pGainer->config.channels, gains); - } else { - /* Slow path. Too many channels to fit on the stack. Need to apply a master volume as a separate path. */ - for (iFrame = 0; iFrame < frameCount; iFrame += 1) { - for (iChannel = 0; iChannel < pGainer->config.channels; iChannel += 1) { - ((float*)pFramesOut)[iFrame*pGainer->config.channels + iChannel] = ((const float*)pFramesIn)[iFrame*pGainer->config.channels + iChannel] * pGainer->pNewGains[iChannel] * pGainer->masterVolume; - } - } - } - } - - /* Now that some frames have been processed we need to make sure future changes to the gain are interpolated. */ - if (pGainer->t == (ma_uint32)-1) { - pGainer->t = (ma_uint32)ma_min(pGainer->config.smoothTimeInFrames, frameCount); - } - -#if 0 - if (pGainer->t >= pGainer->config.smoothTimeInFrames) { - /* Fast path. No gain calculation required. */ - ma_copy_and_apply_volume_factor_per_channel_f32(pFramesOutF32, pFramesInF32, frameCount, pGainer->config.channels, pGainer->pNewGains); - ma_apply_volume_factor_f32(pFramesOutF32, frameCount * pGainer->config.channels, pGainer->masterVolume); - - /* Now that some frames have been processed we need to make sure future changes to the gain are interpolated. */ - if (pGainer->t == (ma_uint32)-1) { - pGainer->t = pGainer->config.smoothTimeInFrames; - } - } else { - /* Slow path. Need to interpolate the gain for each channel individually. */ - - /* We can allow the input and output buffers to be null in which case we'll just update the internal timer. */ - if (pFramesOut != NULL && pFramesIn != NULL) { - float a = (float)pGainer->t / pGainer->config.smoothTimeInFrames; - float d = 1.0f / pGainer->config.smoothTimeInFrames; - ma_uint32 channelCount = pGainer->config.channels; - - for (iFrame = 0; iFrame < frameCount; iFrame += 1) { - for (iChannel = 0; iChannel < channelCount; iChannel += 1) { - pFramesOutF32[iChannel] = pFramesInF32[iChannel] * ma_mix_f32_fast(pGainer->pOldGains[iChannel], pGainer->pNewGains[iChannel], a) * pGainer->masterVolume; - } - - pFramesOutF32 += channelCount; - pFramesInF32 += channelCount; - - a += d; - if (a > 1) { - a = 1; - } - } - } - - pGainer->t = (ma_uint32)ma_min(pGainer->t + frameCount, pGainer->config.smoothTimeInFrames); - - #if 0 /* Reference implementation. */ - for (iFrame = 0; iFrame < frameCount; iFrame += 1) { - /* We can allow the input and output buffers to be null in which case we'll just update the internal timer. */ - if (pFramesOut != NULL && pFramesIn != NULL) { - for (iChannel = 0; iChannel < pGainer->config.channels; iChannel += 1) { - pFramesOutF32[iFrame * pGainer->config.channels + iChannel] = pFramesInF32[iFrame * pGainer->config.channels + iChannel] * ma_gainer_calculate_current_gain(pGainer, iChannel) * pGainer->masterVolume; - } - } - - /* Move interpolation time forward, but don't go beyond our smoothing time. */ - pGainer->t = ma_min(pGainer->t + 1, pGainer->config.smoothTimeInFrames); - } - #endif - } -#endif - - return MA_SUCCESS; -} - -MA_API ma_result ma_gainer_process_pcm_frames(ma_gainer* pGainer, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount) -{ - if (pGainer == NULL) { - return MA_INVALID_ARGS; - } - - /* - ma_gainer_process_pcm_frames_internal() marks pFramesOut and pFramesIn with MA_RESTRICT which - helps with auto-vectorization. - */ - return ma_gainer_process_pcm_frames_internal(pGainer, pFramesOut, pFramesIn, frameCount); -} - -static void ma_gainer_set_gain_by_index(ma_gainer* pGainer, float newGain, ma_uint32 iChannel) -{ - pGainer->pOldGains[iChannel] = ma_gainer_calculate_current_gain(pGainer, iChannel); - pGainer->pNewGains[iChannel] = newGain; -} - -static void ma_gainer_reset_smoothing_time(ma_gainer* pGainer) -{ - if (pGainer->t == (ma_uint32)-1) { - pGainer->t = pGainer->config.smoothTimeInFrames; /* No smoothing required for initial gains setting. */ - } else { - pGainer->t = 0; - } -} - -MA_API ma_result ma_gainer_set_gain(ma_gainer* pGainer, float newGain) -{ - ma_uint32 iChannel; - - if (pGainer == NULL) { - return MA_INVALID_ARGS; - } - - for (iChannel = 0; iChannel < pGainer->config.channels; iChannel += 1) { - ma_gainer_set_gain_by_index(pGainer, newGain, iChannel); - } - - /* The smoothing time needs to be reset to ensure we always interpolate by the configured smoothing time, but only if it's not the first setting. */ - ma_gainer_reset_smoothing_time(pGainer); - - return MA_SUCCESS; -} - -MA_API ma_result ma_gainer_set_gains(ma_gainer* pGainer, float* pNewGains) -{ - ma_uint32 iChannel; - - if (pGainer == NULL || pNewGains == NULL) { - return MA_INVALID_ARGS; - } - - for (iChannel = 0; iChannel < pGainer->config.channels; iChannel += 1) { - ma_gainer_set_gain_by_index(pGainer, pNewGains[iChannel], iChannel); - } - - /* The smoothing time needs to be reset to ensure we always interpolate by the configured smoothing time, but only if it's not the first setting. */ - ma_gainer_reset_smoothing_time(pGainer); - - return MA_SUCCESS; -} - -MA_API ma_result ma_gainer_set_master_volume(ma_gainer* pGainer, float volume) -{ - if (pGainer == NULL) { - return MA_INVALID_ARGS; - } - - pGainer->masterVolume = volume; - - return MA_SUCCESS; -} - -MA_API ma_result ma_gainer_get_master_volume(const ma_gainer* pGainer, float* pVolume) -{ - if (pGainer == NULL || pVolume == NULL) { - return MA_INVALID_ARGS; - } - - *pVolume = pGainer->masterVolume; - - return MA_SUCCESS; -} - - -MA_API ma_panner_config ma_panner_config_init(ma_format format, ma_uint32 channels) -{ - ma_panner_config config; - - MA_ZERO_OBJECT(&config); - config.format = format; - config.channels = channels; - config.mode = ma_pan_mode_balance; /* Set to balancing mode by default because it's consistent with other audio engines and most likely what the caller is expecting. */ - config.pan = 0; - - return config; -} - - -MA_API ma_result ma_panner_init(const ma_panner_config* pConfig, ma_panner* pPanner) -{ - if (pPanner == NULL) { - return MA_INVALID_ARGS; - } - - MA_ZERO_OBJECT(pPanner); - - if (pConfig == NULL) { - return MA_INVALID_ARGS; - } - - pPanner->format = pConfig->format; - pPanner->channels = pConfig->channels; - pPanner->mode = pConfig->mode; - pPanner->pan = pConfig->pan; - - return MA_SUCCESS; -} - -static void ma_stereo_balance_pcm_frames_f32(float* pFramesOut, const float* pFramesIn, ma_uint64 frameCount, float pan) -{ - ma_uint64 iFrame; - - if (pan > 0) { - float factor = 1.0f - pan; - if (pFramesOut == pFramesIn) { - for (iFrame = 0; iFrame < frameCount; iFrame += 1) { - pFramesOut[iFrame*2 + 0] = pFramesIn[iFrame*2 + 0] * factor; - } - } else { - for (iFrame = 0; iFrame < frameCount; iFrame += 1) { - pFramesOut[iFrame*2 + 0] = pFramesIn[iFrame*2 + 0] * factor; - pFramesOut[iFrame*2 + 1] = pFramesIn[iFrame*2 + 1]; - } - } - } else { - float factor = 1.0f + pan; - if (pFramesOut == pFramesIn) { - for (iFrame = 0; iFrame < frameCount; iFrame += 1) { - pFramesOut[iFrame*2 + 1] = pFramesIn[iFrame*2 + 1] * factor; - } - } else { - for (iFrame = 0; iFrame < frameCount; iFrame += 1) { - pFramesOut[iFrame*2 + 0] = pFramesIn[iFrame*2 + 0]; - pFramesOut[iFrame*2 + 1] = pFramesIn[iFrame*2 + 1] * factor; - } - } - } -} - -static void ma_stereo_balance_pcm_frames(void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount, ma_format format, float pan) -{ - if (pan == 0) { - /* Fast path. No panning required. */ - if (pFramesOut == pFramesIn) { - /* No-op */ - } else { - ma_copy_pcm_frames(pFramesOut, pFramesIn, frameCount, format, 2); - } - - return; - } - - switch (format) { - case ma_format_f32: ma_stereo_balance_pcm_frames_f32((float*)pFramesOut, (float*)pFramesIn, frameCount, pan); break; - - /* Unknown format. Just copy. */ - default: - { - ma_copy_pcm_frames(pFramesOut, pFramesIn, frameCount, format, 2); - } break; - } -} - - -static void ma_stereo_pan_pcm_frames_f32(float* pFramesOut, const float* pFramesIn, ma_uint64 frameCount, float pan) -{ - ma_uint64 iFrame; - - if (pan > 0) { - float factorL0 = 1.0f - pan; - float factorL1 = 0.0f + pan; - - for (iFrame = 0; iFrame < frameCount; iFrame += 1) { - float sample0 = (pFramesIn[iFrame*2 + 0] * factorL0); - float sample1 = (pFramesIn[iFrame*2 + 0] * factorL1) + pFramesIn[iFrame*2 + 1]; - - pFramesOut[iFrame*2 + 0] = sample0; - pFramesOut[iFrame*2 + 1] = sample1; - } - } else { - float factorR0 = 0.0f - pan; - float factorR1 = 1.0f + pan; - - for (iFrame = 0; iFrame < frameCount; iFrame += 1) { - float sample0 = pFramesIn[iFrame*2 + 0] + (pFramesIn[iFrame*2 + 1] * factorR0); - float sample1 = (pFramesIn[iFrame*2 + 1] * factorR1); - - pFramesOut[iFrame*2 + 0] = sample0; - pFramesOut[iFrame*2 + 1] = sample1; - } - } -} - -static void ma_stereo_pan_pcm_frames(void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount, ma_format format, float pan) -{ - if (pan == 0) { - /* Fast path. No panning required. */ - if (pFramesOut == pFramesIn) { - /* No-op */ - } else { - ma_copy_pcm_frames(pFramesOut, pFramesIn, frameCount, format, 2); - } - - return; - } - - switch (format) { - case ma_format_f32: ma_stereo_pan_pcm_frames_f32((float*)pFramesOut, (float*)pFramesIn, frameCount, pan); break; - - /* Unknown format. Just copy. */ - default: - { - ma_copy_pcm_frames(pFramesOut, pFramesIn, frameCount, format, 2); - } break; - } -} - -MA_API ma_result ma_panner_process_pcm_frames(ma_panner* pPanner, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount) -{ - if (pPanner == NULL || pFramesOut == NULL || pFramesIn == NULL) { - return MA_INVALID_ARGS; - } - - if (pPanner->channels == 2) { - /* Stereo case. For now assume channel 0 is left and channel right is 1, but should probably add support for a channel map. */ - if (pPanner->mode == ma_pan_mode_balance) { - ma_stereo_balance_pcm_frames(pFramesOut, pFramesIn, frameCount, pPanner->format, pPanner->pan); - } else { - ma_stereo_pan_pcm_frames(pFramesOut, pFramesIn, frameCount, pPanner->format, pPanner->pan); - } - } else { - if (pPanner->channels == 1) { - /* Panning has no effect on mono streams. */ - ma_copy_pcm_frames(pFramesOut, pFramesIn, frameCount, pPanner->format, pPanner->channels); - } else { - /* For now we're not going to support non-stereo set ups. Not sure how I want to handle this case just yet. */ - ma_copy_pcm_frames(pFramesOut, pFramesIn, frameCount, pPanner->format, pPanner->channels); - } - } - - return MA_SUCCESS; -} - -MA_API void ma_panner_set_mode(ma_panner* pPanner, ma_pan_mode mode) -{ - if (pPanner == NULL) { - return; - } - - pPanner->mode = mode; -} - -MA_API ma_pan_mode ma_panner_get_mode(const ma_panner* pPanner) -{ - if (pPanner == NULL) { - return ma_pan_mode_balance; - } - - return pPanner->mode; -} - -MA_API void ma_panner_set_pan(ma_panner* pPanner, float pan) -{ - if (pPanner == NULL) { - return; - } - - pPanner->pan = ma_clamp(pan, -1.0f, 1.0f); -} - -MA_API float ma_panner_get_pan(const ma_panner* pPanner) -{ - if (pPanner == NULL) { - return 0; - } - - return pPanner->pan; -} - - - - -MA_API ma_fader_config ma_fader_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRate) -{ - ma_fader_config config; - - MA_ZERO_OBJECT(&config); - config.format = format; - config.channels = channels; - config.sampleRate = sampleRate; - - return config; -} - - -MA_API ma_result ma_fader_init(const ma_fader_config* pConfig, ma_fader* pFader) -{ - if (pFader == NULL) { - return MA_INVALID_ARGS; - } - - MA_ZERO_OBJECT(pFader); - - if (pConfig == NULL) { - return MA_INVALID_ARGS; - } - - /* Only f32 is supported for now. */ - if (pConfig->format != ma_format_f32) { - return MA_INVALID_ARGS; - } - - pFader->config = *pConfig; - pFader->volumeBeg = 1; - pFader->volumeEnd = 1; - pFader->lengthInFrames = 0; - pFader->cursorInFrames = 0; - - return MA_SUCCESS; -} - -MA_API ma_result ma_fader_process_pcm_frames(ma_fader* pFader, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount) -{ - if (pFader == NULL) { - return MA_INVALID_ARGS; - } - - /* If the cursor is still negative we need to just copy the absolute number of those frames, but no more than frameCount. */ - if (pFader->cursorInFrames < 0) { - ma_uint64 absCursorInFrames = (ma_uint64)0 - pFader->cursorInFrames; - if (absCursorInFrames > frameCount) { - absCursorInFrames = frameCount; - } - - ma_copy_pcm_frames(pFramesOut, pFramesIn, absCursorInFrames, pFader->config.format, pFader->config.channels); - - pFader->cursorInFrames += absCursorInFrames; - frameCount -= absCursorInFrames; - pFramesOut = ma_offset_ptr(pFramesOut, ma_get_bytes_per_frame(pFader->config.format, pFader->config.channels)*absCursorInFrames); - pFramesIn = ma_offset_ptr(pFramesIn, ma_get_bytes_per_frame(pFader->config.format, pFader->config.channels)*absCursorInFrames); - } - - if (pFader->cursorInFrames >= 0) { - /* - For now we need to clamp frameCount so that the cursor never overflows 32-bits. This is required for - the conversion to a float which we use for the linear interpolation. This might be changed later. - */ - if (frameCount + pFader->cursorInFrames > UINT_MAX) { - frameCount = UINT_MAX - pFader->cursorInFrames; - } - - /* Optimized path if volumeBeg and volumeEnd are equal. */ - if (pFader->volumeBeg == pFader->volumeEnd) { - if (pFader->volumeBeg == 1) { - /* Straight copy. */ - ma_copy_pcm_frames(pFramesOut, pFramesIn, frameCount, pFader->config.format, pFader->config.channels); - } else { - /* Copy with volume. */ - ma_copy_and_apply_volume_and_clip_pcm_frames(pFramesOut, pFramesIn, frameCount, pFader->config.format, pFader->config.channels, pFader->volumeBeg); - } - } else { - /* Slower path. Volumes are different, so may need to do an interpolation. */ - if ((ma_uint64)pFader->cursorInFrames >= pFader->lengthInFrames) { - /* Fast path. We've gone past the end of the fade period so just apply the end volume to all samples. */ - ma_copy_and_apply_volume_and_clip_pcm_frames(pFramesOut, pFramesIn, frameCount, pFader->config.format, pFader->config.channels, pFader->volumeEnd); - } else { - /* Slow path. This is where we do the actual fading. */ - ma_uint64 iFrame; - ma_uint32 iChannel; - - /* For now we only support f32. Support for other formats might be added later. */ - if (pFader->config.format == ma_format_f32) { - const float* pFramesInF32 = (const float*)pFramesIn; - /* */ float* pFramesOutF32 = ( float*)pFramesOut; - - for (iFrame = 0; iFrame < frameCount; iFrame += 1) { - float a = (ma_uint32)ma_min(pFader->cursorInFrames + iFrame, pFader->lengthInFrames) / (float)((ma_uint32)pFader->lengthInFrames); /* Safe cast due to the frameCount clamp at the top of this function. */ - float volume = ma_mix_f32_fast(pFader->volumeBeg, pFader->volumeEnd, a); - - for (iChannel = 0; iChannel < pFader->config.channels; iChannel += 1) { - pFramesOutF32[iFrame*pFader->config.channels + iChannel] = pFramesInF32[iFrame*pFader->config.channels + iChannel] * volume; - } - } - } else { - return MA_NOT_IMPLEMENTED; - } - } - } - } - - pFader->cursorInFrames += frameCount; - - return MA_SUCCESS; -} - -MA_API void ma_fader_get_data_format(const ma_fader* pFader, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate) -{ - if (pFader == NULL) { - return; - } - - if (pFormat != NULL) { - *pFormat = pFader->config.format; - } - - if (pChannels != NULL) { - *pChannels = pFader->config.channels; - } - - if (pSampleRate != NULL) { - *pSampleRate = pFader->config.sampleRate; - } -} - -MA_API void ma_fader_set_fade(ma_fader* pFader, float volumeBeg, float volumeEnd, ma_uint64 lengthInFrames) -{ - ma_fader_set_fade_ex(pFader, volumeBeg, volumeEnd, lengthInFrames, 0); -} - -MA_API void ma_fader_set_fade_ex(ma_fader* pFader, float volumeBeg, float volumeEnd, ma_uint64 lengthInFrames, ma_int64 startOffsetInFrames) -{ - if (pFader == NULL) { - return; - } - - /* If the volume is negative, use current volume. */ - if (volumeBeg < 0) { - volumeBeg = ma_fader_get_current_volume(pFader); - } - - /* - The length needs to be clamped to 32-bits due to how we convert it to a float for linear - interpolation reasons. I might change this requirement later, but for now it's not important. - */ - if (lengthInFrames > UINT_MAX) { - lengthInFrames = UINT_MAX; - } - - /* The start offset needs to be clamped to ensure it doesn't overflow a signed number. */ - if (startOffsetInFrames > INT_MAX) { - startOffsetInFrames = INT_MAX; - } - - pFader->volumeBeg = volumeBeg; - pFader->volumeEnd = volumeEnd; - pFader->lengthInFrames = lengthInFrames; - pFader->cursorInFrames = -startOffsetInFrames; -} - -MA_API float ma_fader_get_current_volume(const ma_fader* pFader) -{ - if (pFader == NULL) { - return 0.0f; - } - - /* Any frames prior to the start of the fade period will be at unfaded volume. */ - if (pFader->cursorInFrames < 0) { - return 1.0f; - } - - /* The current volume depends on the position of the cursor. */ - if (pFader->cursorInFrames == 0) { - return pFader->volumeBeg; - } else if ((ma_uint64)pFader->cursorInFrames >= pFader->lengthInFrames) { /* Safe case because the < 0 case was checked above. */ - return pFader->volumeEnd; - } else { - /* The cursor is somewhere inside the fading period. We can figure this out with a simple linear interpolation between volumeBeg and volumeEnd based on our cursor position. */ - return ma_mix_f32_fast(pFader->volumeBeg, pFader->volumeEnd, (ma_uint32)pFader->cursorInFrames / (float)((ma_uint32)pFader->lengthInFrames)); /* Safe cast to uint32 because we clamp it in ma_fader_process_pcm_frames(). */ - } -} - - - - - -MA_API ma_vec3f ma_vec3f_init_3f(float x, float y, float z) -{ - ma_vec3f v; - - v.x = x; - v.y = y; - v.z = z; - - return v; -} - -MA_API ma_vec3f ma_vec3f_sub(ma_vec3f a, ma_vec3f b) -{ - return ma_vec3f_init_3f( - a.x - b.x, - a.y - b.y, - a.z - b.z - ); -} - -MA_API ma_vec3f ma_vec3f_neg(ma_vec3f a) -{ - return ma_vec3f_init_3f( - -a.x, - -a.y, - -a.z - ); -} - -MA_API float ma_vec3f_dot(ma_vec3f a, ma_vec3f b) -{ - return a.x*b.x + a.y*b.y + a.z*b.z; -} - -MA_API float ma_vec3f_len2(ma_vec3f v) -{ - return ma_vec3f_dot(v, v); -} - -MA_API float ma_vec3f_len(ma_vec3f v) -{ - return (float)ma_sqrtd(ma_vec3f_len2(v)); -} - - - -MA_API float ma_vec3f_dist(ma_vec3f a, ma_vec3f b) -{ - return ma_vec3f_len(ma_vec3f_sub(a, b)); -} - -MA_API ma_vec3f ma_vec3f_normalize(ma_vec3f v) -{ - float invLen; - float len2 = ma_vec3f_len2(v); - if (len2 == 0) { - return ma_vec3f_init_3f(0, 0, 0); - } - - invLen = ma_rsqrtf(len2); - v.x *= invLen; - v.y *= invLen; - v.z *= invLen; - - return v; -} - -MA_API ma_vec3f ma_vec3f_cross(ma_vec3f a, ma_vec3f b) -{ - return ma_vec3f_init_3f( - a.y*b.z - a.z*b.y, - a.z*b.x - a.x*b.z, - a.x*b.y - a.y*b.x - ); -} - - -MA_API void ma_atomic_vec3f_init(ma_atomic_vec3f* v, ma_vec3f value) -{ - v->v = value; - v->lock = 0; /* Important this is initialized to 0. */ -} - -MA_API void ma_atomic_vec3f_set(ma_atomic_vec3f* v, ma_vec3f value) -{ - ma_spinlock_lock(&v->lock); - { - v->v = value; - } - ma_spinlock_unlock(&v->lock); -} - -MA_API ma_vec3f ma_atomic_vec3f_get(ma_atomic_vec3f* v) -{ - ma_vec3f r; - - ma_spinlock_lock(&v->lock); - { - r = v->v; - } - ma_spinlock_unlock(&v->lock); - - return r; -} - - - -static void ma_channel_map_apply_f32(float* pFramesOut, const ma_channel* pChannelMapOut, ma_uint32 channelsOut, const float* pFramesIn, const ma_channel* pChannelMapIn, ma_uint32 channelsIn, ma_uint64 frameCount, ma_channel_mix_mode mode, ma_mono_expansion_mode monoExpansionMode); -static ma_bool32 ma_is_spatial_channel_position(ma_channel channelPosition); - - -#ifndef MA_DEFAULT_SPEED_OF_SOUND -#define MA_DEFAULT_SPEED_OF_SOUND 343.3f -#endif - -/* -These vectors represent the direction that speakers are facing from the center point. They're used -for panning in the spatializer. Must be normalized. -*/ -static ma_vec3f g_maChannelDirections[MA_CHANNEL_POSITION_COUNT] = { - { 0.0f, 0.0f, -1.0f }, /* MA_CHANNEL_NONE */ - { 0.0f, 0.0f, -1.0f }, /* MA_CHANNEL_MONO */ - {-0.7071f, 0.0f, -0.7071f }, /* MA_CHANNEL_FRONT_LEFT */ - {+0.7071f, 0.0f, -0.7071f }, /* MA_CHANNEL_FRONT_RIGHT */ - { 0.0f, 0.0f, -1.0f }, /* MA_CHANNEL_FRONT_CENTER */ - { 0.0f, 0.0f, -1.0f }, /* MA_CHANNEL_LFE */ - {-0.7071f, 0.0f, +0.7071f }, /* MA_CHANNEL_BACK_LEFT */ - {+0.7071f, 0.0f, +0.7071f }, /* MA_CHANNEL_BACK_RIGHT */ - {-0.3162f, 0.0f, -0.9487f }, /* MA_CHANNEL_FRONT_LEFT_CENTER */ - {+0.3162f, 0.0f, -0.9487f }, /* MA_CHANNEL_FRONT_RIGHT_CENTER */ - { 0.0f, 0.0f, +1.0f }, /* MA_CHANNEL_BACK_CENTER */ - {-1.0f, 0.0f, 0.0f }, /* MA_CHANNEL_SIDE_LEFT */ - {+1.0f, 0.0f, 0.0f }, /* MA_CHANNEL_SIDE_RIGHT */ - { 0.0f, +1.0f, 0.0f }, /* MA_CHANNEL_TOP_CENTER */ - {-0.5774f, +0.5774f, -0.5774f }, /* MA_CHANNEL_TOP_FRONT_LEFT */ - { 0.0f, +0.7071f, -0.7071f }, /* MA_CHANNEL_TOP_FRONT_CENTER */ - {+0.5774f, +0.5774f, -0.5774f }, /* MA_CHANNEL_TOP_FRONT_RIGHT */ - {-0.5774f, +0.5774f, +0.5774f }, /* MA_CHANNEL_TOP_BACK_LEFT */ - { 0.0f, +0.7071f, +0.7071f }, /* MA_CHANNEL_TOP_BACK_CENTER */ - {+0.5774f, +0.5774f, +0.5774f }, /* MA_CHANNEL_TOP_BACK_RIGHT */ - { 0.0f, 0.0f, -1.0f }, /* MA_CHANNEL_AUX_0 */ - { 0.0f, 0.0f, -1.0f }, /* MA_CHANNEL_AUX_1 */ - { 0.0f, 0.0f, -1.0f }, /* MA_CHANNEL_AUX_2 */ - { 0.0f, 0.0f, -1.0f }, /* MA_CHANNEL_AUX_3 */ - { 0.0f, 0.0f, -1.0f }, /* MA_CHANNEL_AUX_4 */ - { 0.0f, 0.0f, -1.0f }, /* MA_CHANNEL_AUX_5 */ - { 0.0f, 0.0f, -1.0f }, /* MA_CHANNEL_AUX_6 */ - { 0.0f, 0.0f, -1.0f }, /* MA_CHANNEL_AUX_7 */ - { 0.0f, 0.0f, -1.0f }, /* MA_CHANNEL_AUX_8 */ - { 0.0f, 0.0f, -1.0f }, /* MA_CHANNEL_AUX_9 */ - { 0.0f, 0.0f, -1.0f }, /* MA_CHANNEL_AUX_10 */ - { 0.0f, 0.0f, -1.0f }, /* MA_CHANNEL_AUX_11 */ - { 0.0f, 0.0f, -1.0f }, /* MA_CHANNEL_AUX_12 */ - { 0.0f, 0.0f, -1.0f }, /* MA_CHANNEL_AUX_13 */ - { 0.0f, 0.0f, -1.0f }, /* MA_CHANNEL_AUX_14 */ - { 0.0f, 0.0f, -1.0f }, /* MA_CHANNEL_AUX_15 */ - { 0.0f, 0.0f, -1.0f }, /* MA_CHANNEL_AUX_16 */ - { 0.0f, 0.0f, -1.0f }, /* MA_CHANNEL_AUX_17 */ - { 0.0f, 0.0f, -1.0f }, /* MA_CHANNEL_AUX_18 */ - { 0.0f, 0.0f, -1.0f }, /* MA_CHANNEL_AUX_19 */ - { 0.0f, 0.0f, -1.0f }, /* MA_CHANNEL_AUX_20 */ - { 0.0f, 0.0f, -1.0f }, /* MA_CHANNEL_AUX_21 */ - { 0.0f, 0.0f, -1.0f }, /* MA_CHANNEL_AUX_22 */ - { 0.0f, 0.0f, -1.0f }, /* MA_CHANNEL_AUX_23 */ - { 0.0f, 0.0f, -1.0f }, /* MA_CHANNEL_AUX_24 */ - { 0.0f, 0.0f, -1.0f }, /* MA_CHANNEL_AUX_25 */ - { 0.0f, 0.0f, -1.0f }, /* MA_CHANNEL_AUX_26 */ - { 0.0f, 0.0f, -1.0f }, /* MA_CHANNEL_AUX_27 */ - { 0.0f, 0.0f, -1.0f }, /* MA_CHANNEL_AUX_28 */ - { 0.0f, 0.0f, -1.0f }, /* MA_CHANNEL_AUX_29 */ - { 0.0f, 0.0f, -1.0f }, /* MA_CHANNEL_AUX_30 */ - { 0.0f, 0.0f, -1.0f } /* MA_CHANNEL_AUX_31 */ -}; - -static ma_vec3f ma_get_channel_direction(ma_channel channel) -{ - if (channel >= MA_CHANNEL_POSITION_COUNT) { - return ma_vec3f_init_3f(0, 0, -1); - } else { - return g_maChannelDirections[channel]; - } -} - - - -static float ma_attenuation_inverse(float distance, float minDistance, float maxDistance, float rolloff) -{ - if (minDistance >= maxDistance) { - return 1; /* To avoid division by zero. Do not attenuate. */ - } - - return minDistance / (minDistance + rolloff * (ma_clamp(distance, minDistance, maxDistance) - minDistance)); -} - -static float ma_attenuation_linear(float distance, float minDistance, float maxDistance, float rolloff) -{ - if (minDistance >= maxDistance) { - return 1; /* To avoid division by zero. Do not attenuate. */ - } - - return 1 - rolloff * (ma_clamp(distance, minDistance, maxDistance) - minDistance) / (maxDistance - minDistance); -} - -static float ma_attenuation_exponential(float distance, float minDistance, float maxDistance, float rolloff) -{ - if (minDistance >= maxDistance) { - return 1; /* To avoid division by zero. Do not attenuate. */ - } - - return (float)ma_powd(ma_clamp(distance, minDistance, maxDistance) / minDistance, -rolloff); -} - - -/* -Doppler Effect calculation taken from the OpenAL spec, with two main differences: - - 1) The source to listener vector will have already been calculated at an earlier step so we can - just use that directly. We need only the position of the source relative to the origin. - - 2) We don't scale by a frequency because we actually just want the ratio which we'll plug straight - into the resampler directly. -*/ -static float ma_doppler_pitch(ma_vec3f relativePosition, ma_vec3f sourceVelocity, ma_vec3f listenVelocity, float speedOfSound, float dopplerFactor) -{ - float len; - float vls; - float vss; - - len = ma_vec3f_len(relativePosition); - - /* - There's a case where the position of the source will be right on top of the listener in which - case the length will be 0 and we'll end up with a division by zero. We can just return a ratio - of 1.0 in this case. This is not considered in the OpenAL spec, but is necessary. - */ - if (len == 0) { - return 1.0; - } - - vls = ma_vec3f_dot(relativePosition, listenVelocity) / len; - vss = ma_vec3f_dot(relativePosition, sourceVelocity) / len; - - vls = ma_min(vls, speedOfSound / dopplerFactor); - vss = ma_min(vss, speedOfSound / dopplerFactor); - - return (speedOfSound - dopplerFactor*vls) / (speedOfSound - dopplerFactor*vss); -} - - -static void ma_get_default_channel_map_for_spatializer(ma_channel* pChannelMap, size_t channelMapCap, ma_uint32 channelCount) -{ - /* - Special case for stereo. Want to default the left and right speakers to side left and side - right so that they're facing directly down the X axis rather than slightly forward. Not - doing this will result in sounds being quieter when behind the listener. This might - actually be good for some scenarios, but I don't think it's an appropriate default because - it can be a bit unexpected. - */ - if (channelCount == 2) { - pChannelMap[0] = MA_CHANNEL_SIDE_LEFT; - pChannelMap[1] = MA_CHANNEL_SIDE_RIGHT; - } else { - ma_channel_map_init_standard(ma_standard_channel_map_default, pChannelMap, channelMapCap, channelCount); - } -} - - -MA_API ma_spatializer_listener_config ma_spatializer_listener_config_init(ma_uint32 channelsOut) -{ - ma_spatializer_listener_config config; - - MA_ZERO_OBJECT(&config); - config.channelsOut = channelsOut; - config.pChannelMapOut = NULL; - config.handedness = ma_handedness_right; - config.worldUp = ma_vec3f_init_3f(0, 1, 0); - config.coneInnerAngleInRadians = 6.283185f; /* 360 degrees. */ - config.coneOuterAngleInRadians = 6.283185f; /* 360 degrees. */ - config.coneOuterGain = 0; - config.speedOfSound = 343.3f; /* Same as OpenAL. Used for doppler effect. */ - - return config; -} - - -typedef struct -{ - size_t sizeInBytes; - size_t channelMapOutOffset; -} ma_spatializer_listener_heap_layout; - -static ma_result ma_spatializer_listener_get_heap_layout(const ma_spatializer_listener_config* pConfig, ma_spatializer_listener_heap_layout* pHeapLayout) -{ - MA_ASSERT(pHeapLayout != NULL); - - MA_ZERO_OBJECT(pHeapLayout); - - if (pConfig == NULL) { - return MA_INVALID_ARGS; - } - - if (pConfig->channelsOut == 0) { - return MA_INVALID_ARGS; - } - - pHeapLayout->sizeInBytes = 0; - - /* Channel map. We always need this, even for passthroughs. */ - pHeapLayout->channelMapOutOffset = pHeapLayout->sizeInBytes; - pHeapLayout->sizeInBytes += ma_align_64(sizeof(*pConfig->pChannelMapOut) * pConfig->channelsOut); - - return MA_SUCCESS; -} - - -MA_API ma_result ma_spatializer_listener_get_heap_size(const ma_spatializer_listener_config* pConfig, size_t* pHeapSizeInBytes) -{ - ma_result result; - ma_spatializer_listener_heap_layout heapLayout; - - if (pHeapSizeInBytes == NULL) { - return MA_INVALID_ARGS; - } - - *pHeapSizeInBytes = 0; - - result = ma_spatializer_listener_get_heap_layout(pConfig, &heapLayout); - if (result != MA_SUCCESS) { - return result; - } - - *pHeapSizeInBytes = heapLayout.sizeInBytes; - - return MA_SUCCESS; -} - -MA_API ma_result ma_spatializer_listener_init_preallocated(const ma_spatializer_listener_config* pConfig, void* pHeap, ma_spatializer_listener* pListener) -{ - ma_result result; - ma_spatializer_listener_heap_layout heapLayout; - - if (pListener == NULL) { - return MA_INVALID_ARGS; - } - - MA_ZERO_OBJECT(pListener); - - result = ma_spatializer_listener_get_heap_layout(pConfig, &heapLayout); - if (result != MA_SUCCESS) { - return result; - } - - pListener->_pHeap = pHeap; - MA_ZERO_MEMORY(pHeap, heapLayout.sizeInBytes); - - pListener->config = *pConfig; - ma_atomic_vec3f_init(&pListener->position, ma_vec3f_init_3f(0, 0, 0)); - ma_atomic_vec3f_init(&pListener->direction, ma_vec3f_init_3f(0, 0, -1)); - ma_atomic_vec3f_init(&pListener->velocity, ma_vec3f_init_3f(0, 0, 0)); - pListener->isEnabled = MA_TRUE; - - /* Swap the forward direction if we're left handed (it was initialized based on right handed). */ - if (pListener->config.handedness == ma_handedness_left) { - ma_vec3f negDir = ma_vec3f_neg(ma_spatializer_listener_get_direction(pListener)); - ma_spatializer_listener_set_direction(pListener, negDir.x, negDir.y, negDir.z); - } - - - /* We must always have a valid channel map. */ - pListener->config.pChannelMapOut = (ma_channel*)ma_offset_ptr(pHeap, heapLayout.channelMapOutOffset); - - /* Use a slightly different default channel map for stereo. */ - if (pConfig->pChannelMapOut == NULL) { - ma_get_default_channel_map_for_spatializer(pListener->config.pChannelMapOut, pConfig->channelsOut, pConfig->channelsOut); - } else { - ma_channel_map_copy_or_default(pListener->config.pChannelMapOut, pConfig->channelsOut, pConfig->pChannelMapOut, pConfig->channelsOut); - } - - return MA_SUCCESS; -} - -MA_API ma_result ma_spatializer_listener_init(const ma_spatializer_listener_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_spatializer_listener* pListener) -{ - ma_result result; - size_t heapSizeInBytes; - void* pHeap; - - result = ma_spatializer_listener_get_heap_size(pConfig, &heapSizeInBytes); - if (result != MA_SUCCESS) { - return result; - } - - if (heapSizeInBytes > 0) { - pHeap = ma_malloc(heapSizeInBytes, pAllocationCallbacks); - if (pHeap == NULL) { - return MA_OUT_OF_MEMORY; - } - } else { - pHeap = NULL; - } - - result = ma_spatializer_listener_init_preallocated(pConfig, pHeap, pListener); - if (result != MA_SUCCESS) { - ma_free(pHeap, pAllocationCallbacks); - return result; - } - - pListener->_ownsHeap = MA_TRUE; - return MA_SUCCESS; -} - -MA_API void ma_spatializer_listener_uninit(ma_spatializer_listener* pListener, const ma_allocation_callbacks* pAllocationCallbacks) -{ - if (pListener == NULL) { - return; - } - - if (pListener->_ownsHeap) { - ma_free(pListener->_pHeap, pAllocationCallbacks); - } -} - -MA_API ma_channel* ma_spatializer_listener_get_channel_map(ma_spatializer_listener* pListener) -{ - if (pListener == NULL) { - return NULL; - } - - return pListener->config.pChannelMapOut; -} - -MA_API void ma_spatializer_listener_set_cone(ma_spatializer_listener* pListener, float innerAngleInRadians, float outerAngleInRadians, float outerGain) -{ - if (pListener == NULL) { - return; - } - - pListener->config.coneInnerAngleInRadians = innerAngleInRadians; - pListener->config.coneOuterAngleInRadians = outerAngleInRadians; - pListener->config.coneOuterGain = outerGain; -} - -MA_API void ma_spatializer_listener_get_cone(const ma_spatializer_listener* pListener, float* pInnerAngleInRadians, float* pOuterAngleInRadians, float* pOuterGain) -{ - if (pListener == NULL) { - return; - } - - if (pInnerAngleInRadians != NULL) { - *pInnerAngleInRadians = pListener->config.coneInnerAngleInRadians; - } - - if (pOuterAngleInRadians != NULL) { - *pOuterAngleInRadians = pListener->config.coneOuterAngleInRadians; - } - - if (pOuterGain != NULL) { - *pOuterGain = pListener->config.coneOuterGain; - } -} - -MA_API void ma_spatializer_listener_set_position(ma_spatializer_listener* pListener, float x, float y, float z) -{ - if (pListener == NULL) { - return; - } - - ma_atomic_vec3f_set(&pListener->position, ma_vec3f_init_3f(x, y, z)); -} - -MA_API ma_vec3f ma_spatializer_listener_get_position(const ma_spatializer_listener* pListener) -{ - if (pListener == NULL) { - return ma_vec3f_init_3f(0, 0, 0); - } - - return ma_atomic_vec3f_get((ma_atomic_vec3f*)&pListener->position); /* Naughty const-cast. It's just for atomically loading the vec3 which should be safe. */ -} - -MA_API void ma_spatializer_listener_set_direction(ma_spatializer_listener* pListener, float x, float y, float z) -{ - if (pListener == NULL) { - return; - } - - ma_atomic_vec3f_set(&pListener->direction, ma_vec3f_init_3f(x, y, z)); -} - -MA_API ma_vec3f ma_spatializer_listener_get_direction(const ma_spatializer_listener* pListener) -{ - if (pListener == NULL) { - return ma_vec3f_init_3f(0, 0, -1); - } - - return ma_atomic_vec3f_get((ma_atomic_vec3f*)&pListener->direction); /* Naughty const-cast. It's just for atomically loading the vec3 which should be safe. */ -} - -MA_API void ma_spatializer_listener_set_velocity(ma_spatializer_listener* pListener, float x, float y, float z) -{ - if (pListener == NULL) { - return; - } - - ma_atomic_vec3f_set(&pListener->velocity, ma_vec3f_init_3f(x, y, z)); -} - -MA_API ma_vec3f ma_spatializer_listener_get_velocity(const ma_spatializer_listener* pListener) -{ - if (pListener == NULL) { - return ma_vec3f_init_3f(0, 0, 0); - } - - return ma_atomic_vec3f_get((ma_atomic_vec3f*)&pListener->velocity); /* Naughty const-cast. It's just for atomically loading the vec3 which should be safe. */ -} - -MA_API void ma_spatializer_listener_set_speed_of_sound(ma_spatializer_listener* pListener, float speedOfSound) -{ - if (pListener == NULL) { - return; - } - - pListener->config.speedOfSound = speedOfSound; -} - -MA_API float ma_spatializer_listener_get_speed_of_sound(const ma_spatializer_listener* pListener) -{ - if (pListener == NULL) { - return 0; - } - - return pListener->config.speedOfSound; -} - -MA_API void ma_spatializer_listener_set_world_up(ma_spatializer_listener* pListener, float x, float y, float z) -{ - if (pListener == NULL) { - return; - } - - pListener->config.worldUp = ma_vec3f_init_3f(x, y, z); -} - -MA_API ma_vec3f ma_spatializer_listener_get_world_up(const ma_spatializer_listener* pListener) -{ - if (pListener == NULL) { - return ma_vec3f_init_3f(0, 1, 0); - } - - return pListener->config.worldUp; -} - -MA_API void ma_spatializer_listener_set_enabled(ma_spatializer_listener* pListener, ma_bool32 isEnabled) -{ - if (pListener == NULL) { - return; - } - - pListener->isEnabled = isEnabled; -} - -MA_API ma_bool32 ma_spatializer_listener_is_enabled(const ma_spatializer_listener* pListener) -{ - if (pListener == NULL) { - return MA_FALSE; - } - - return pListener->isEnabled; -} - - - - -MA_API ma_spatializer_config ma_spatializer_config_init(ma_uint32 channelsIn, ma_uint32 channelsOut) -{ - ma_spatializer_config config; - - MA_ZERO_OBJECT(&config); - config.channelsIn = channelsIn; - config.channelsOut = channelsOut; - config.pChannelMapIn = NULL; - config.attenuationModel = ma_attenuation_model_inverse; - config.positioning = ma_positioning_absolute; - config.handedness = ma_handedness_right; - config.minGain = 0; - config.maxGain = 1; - config.minDistance = 1; - config.maxDistance = MA_FLT_MAX; - config.rolloff = 1; - config.coneInnerAngleInRadians = 6.283185f; /* 360 degrees. */ - config.coneOuterAngleInRadians = 6.283185f; /* 360 degrees. */ - config.coneOuterGain = 0.0f; - config.dopplerFactor = 1; - config.directionalAttenuationFactor = 1; - config.minSpatializationChannelGain = 0.2f; - config.gainSmoothTimeInFrames = 360; /* 7.5ms @ 48K. */ - - return config; -} - - -static ma_gainer_config ma_spatializer_gainer_config_init(const ma_spatializer_config* pConfig) -{ - MA_ASSERT(pConfig != NULL); - return ma_gainer_config_init(pConfig->channelsOut, pConfig->gainSmoothTimeInFrames); -} - -static ma_result ma_spatializer_validate_config(const ma_spatializer_config* pConfig) -{ - MA_ASSERT(pConfig != NULL); - - if (pConfig->channelsIn == 0 || pConfig->channelsOut == 0) { - return MA_INVALID_ARGS; - } - - return MA_SUCCESS; -} - -typedef struct -{ - size_t sizeInBytes; - size_t channelMapInOffset; - size_t newChannelGainsOffset; - size_t gainerOffset; -} ma_spatializer_heap_layout; - -static ma_result ma_spatializer_get_heap_layout(const ma_spatializer_config* pConfig, ma_spatializer_heap_layout* pHeapLayout) -{ - ma_result result; - - MA_ASSERT(pHeapLayout != NULL); - - MA_ZERO_OBJECT(pHeapLayout); - - if (pConfig == NULL) { - return MA_INVALID_ARGS; - } - - result = ma_spatializer_validate_config(pConfig); - if (result != MA_SUCCESS) { - return result; - } - - pHeapLayout->sizeInBytes = 0; - - /* Channel map. */ - pHeapLayout->channelMapInOffset = MA_SIZE_MAX; /* <-- MA_SIZE_MAX indicates no allocation necessary. */ - if (pConfig->pChannelMapIn != NULL) { - pHeapLayout->channelMapInOffset = pHeapLayout->sizeInBytes; - pHeapLayout->sizeInBytes += ma_align_64(sizeof(*pConfig->pChannelMapIn) * pConfig->channelsIn); - } - - /* New channel gains for output. */ - pHeapLayout->newChannelGainsOffset = pHeapLayout->sizeInBytes; - pHeapLayout->sizeInBytes += ma_align_64(sizeof(float) * pConfig->channelsOut); - - /* Gainer. */ - { - size_t gainerHeapSizeInBytes; - ma_gainer_config gainerConfig; - - gainerConfig = ma_spatializer_gainer_config_init(pConfig); - - result = ma_gainer_get_heap_size(&gainerConfig, &gainerHeapSizeInBytes); - if (result != MA_SUCCESS) { - return result; - } - - pHeapLayout->gainerOffset = pHeapLayout->sizeInBytes; - pHeapLayout->sizeInBytes += ma_align_64(gainerHeapSizeInBytes); - } - - return MA_SUCCESS; -} - -MA_API ma_result ma_spatializer_get_heap_size(const ma_spatializer_config* pConfig, size_t* pHeapSizeInBytes) -{ - ma_result result; - ma_spatializer_heap_layout heapLayout; - - if (pHeapSizeInBytes == NULL) { - return MA_INVALID_ARGS; - } - - *pHeapSizeInBytes = 0; /* Safety. */ - - result = ma_spatializer_get_heap_layout(pConfig, &heapLayout); - if (result != MA_SUCCESS) { - return result; - } - - *pHeapSizeInBytes = heapLayout.sizeInBytes; - - return MA_SUCCESS; -} - - -MA_API ma_result ma_spatializer_init_preallocated(const ma_spatializer_config* pConfig, void* pHeap, ma_spatializer* pSpatializer) -{ - ma_result result; - ma_spatializer_heap_layout heapLayout; - ma_gainer_config gainerConfig; - - if (pSpatializer == NULL) { - return MA_INVALID_ARGS; - } - - MA_ZERO_OBJECT(pSpatializer); - - if (pConfig == NULL || pHeap == NULL) { - return MA_INVALID_ARGS; - } - - result = ma_spatializer_get_heap_layout(pConfig, &heapLayout); - if (result != MA_SUCCESS) { - return result; - } - - pSpatializer->_pHeap = pHeap; - MA_ZERO_MEMORY(pHeap, heapLayout.sizeInBytes); - - pSpatializer->channelsIn = pConfig->channelsIn; - pSpatializer->channelsOut = pConfig->channelsOut; - pSpatializer->attenuationModel = pConfig->attenuationModel; - pSpatializer->positioning = pConfig->positioning; - pSpatializer->handedness = pConfig->handedness; - pSpatializer->minGain = pConfig->minGain; - pSpatializer->maxGain = pConfig->maxGain; - pSpatializer->minDistance = pConfig->minDistance; - pSpatializer->maxDistance = pConfig->maxDistance; - pSpatializer->rolloff = pConfig->rolloff; - pSpatializer->coneInnerAngleInRadians = pConfig->coneInnerAngleInRadians; - pSpatializer->coneOuterAngleInRadians = pConfig->coneOuterAngleInRadians; - pSpatializer->coneOuterGain = pConfig->coneOuterGain; - pSpatializer->dopplerFactor = pConfig->dopplerFactor; - pSpatializer->minSpatializationChannelGain = pConfig->minSpatializationChannelGain; - pSpatializer->directionalAttenuationFactor = pConfig->directionalAttenuationFactor; - pSpatializer->gainSmoothTimeInFrames = pConfig->gainSmoothTimeInFrames; - ma_atomic_vec3f_init(&pSpatializer->position, ma_vec3f_init_3f(0, 0, 0)); - ma_atomic_vec3f_init(&pSpatializer->direction, ma_vec3f_init_3f(0, 0, -1)); - ma_atomic_vec3f_init(&pSpatializer->velocity, ma_vec3f_init_3f(0, 0, 0)); - pSpatializer->dopplerPitch = 1; - - /* Swap the forward direction if we're left handed (it was initialized based on right handed). */ - if (pSpatializer->handedness == ma_handedness_left) { - ma_vec3f negDir = ma_vec3f_neg(ma_spatializer_get_direction(pSpatializer)); - ma_spatializer_set_direction(pSpatializer, negDir.x, negDir.y, negDir.z); - } - - /* Channel map. This will be on the heap. */ - if (pConfig->pChannelMapIn != NULL) { - pSpatializer->pChannelMapIn = (ma_channel*)ma_offset_ptr(pHeap, heapLayout.channelMapInOffset); - ma_channel_map_copy_or_default(pSpatializer->pChannelMapIn, pSpatializer->channelsIn, pConfig->pChannelMapIn, pSpatializer->channelsIn); - } - - /* New channel gains for output channels. */ - pSpatializer->pNewChannelGainsOut = (float*)ma_offset_ptr(pHeap, heapLayout.newChannelGainsOffset); - - /* Gainer. */ - gainerConfig = ma_spatializer_gainer_config_init(pConfig); - - result = ma_gainer_init_preallocated(&gainerConfig, ma_offset_ptr(pHeap, heapLayout.gainerOffset), &pSpatializer->gainer); - if (result != MA_SUCCESS) { - return result; /* Failed to initialize the gainer. */ - } - - return MA_SUCCESS; -} - -MA_API ma_result ma_spatializer_init(const ma_spatializer_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_spatializer* pSpatializer) -{ - ma_result result; - size_t heapSizeInBytes; - void* pHeap; - - /* We'll need a heap allocation to retrieve the size. */ - result = ma_spatializer_get_heap_size(pConfig, &heapSizeInBytes); - if (result != MA_SUCCESS) { - return result; - } - - if (heapSizeInBytes > 0) { - pHeap = ma_malloc(heapSizeInBytes, pAllocationCallbacks); - if (pHeap == NULL) { - return MA_OUT_OF_MEMORY; - } - } else { - pHeap = NULL; - } - - result = ma_spatializer_init_preallocated(pConfig, pHeap, pSpatializer); - if (result != MA_SUCCESS) { - ma_free(pHeap, pAllocationCallbacks); - return result; - } - - pSpatializer->_ownsHeap = MA_TRUE; - return MA_SUCCESS; -} - -MA_API void ma_spatializer_uninit(ma_spatializer* pSpatializer, const ma_allocation_callbacks* pAllocationCallbacks) -{ - if (pSpatializer == NULL) { - return; - } - - ma_gainer_uninit(&pSpatializer->gainer, pAllocationCallbacks); - - if (pSpatializer->_ownsHeap) { - ma_free(pSpatializer->_pHeap, pAllocationCallbacks); - } -} - -static float ma_calculate_angular_gain(ma_vec3f dirA, ma_vec3f dirB, float coneInnerAngleInRadians, float coneOuterAngleInRadians, float coneOuterGain) -{ - /* - Angular attenuation. - - Unlike distance gain, the math for this is not specified by the OpenAL spec so we'll just go ahead and figure - this out for ourselves at the expense of possibly being inconsistent with other implementations. - - To do cone attenuation, I'm just using the same math that we'd use to implement a basic spotlight in OpenGL. We - just need to get the direction from the source to the listener and then do a dot product against that and the - direction of the spotlight. Then we just compare that dot product against the cosine of the inner and outer - angles. If the dot product is greater than the outer angle, we just use coneOuterGain. If it's less than - the inner angle, we just use a gain of 1. Otherwise we linearly interpolate between 1 and coneOuterGain. - */ - if (coneInnerAngleInRadians < 6.283185f) { - float angularGain = 1; - float cutoffInner = (float)ma_cosd(coneInnerAngleInRadians*0.5f); - float cutoffOuter = (float)ma_cosd(coneOuterAngleInRadians*0.5f); - float d; - - d = ma_vec3f_dot(dirA, dirB); - - if (d > cutoffInner) { - /* It's inside the inner angle. */ - angularGain = 1; - } else { - /* It's outside the inner angle. */ - if (d > cutoffOuter) { - /* It's between the inner and outer angle. We need to linearly interpolate between 1 and coneOuterGain. */ - angularGain = ma_mix_f32(coneOuterGain, 1, (d - cutoffOuter) / (cutoffInner - cutoffOuter)); - } else { - /* It's outside the outer angle. */ - angularGain = coneOuterGain; - } - } - - /*printf("d = %f; cutoffInner = %f; cutoffOuter = %f; angularGain = %f\n", d, cutoffInner, cutoffOuter, angularGain);*/ - return angularGain; - } else { - /* Inner angle is 360 degrees so no need to do any attenuation. */ - return 1; - } -} - -MA_API ma_result ma_spatializer_process_pcm_frames(ma_spatializer* pSpatializer, ma_spatializer_listener* pListener, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount) -{ - ma_channel* pChannelMapIn = pSpatializer->pChannelMapIn; - ma_channel* pChannelMapOut = pListener->config.pChannelMapOut; - - if (pSpatializer == NULL) { - return MA_INVALID_ARGS; - } - - /* If we're not spatializing we need to run an optimized path. */ - if (ma_atomic_load_i32(&pSpatializer->attenuationModel) == ma_attenuation_model_none) { - if (ma_spatializer_listener_is_enabled(pListener)) { - /* No attenuation is required, but we'll need to do some channel conversion. */ - if (pSpatializer->channelsIn == pSpatializer->channelsOut) { - ma_copy_pcm_frames(pFramesOut, pFramesIn, frameCount, ma_format_f32, pSpatializer->channelsIn); - } else { - ma_channel_map_apply_f32((float*)pFramesOut, pChannelMapOut, pSpatializer->channelsOut, (const float*)pFramesIn, pChannelMapIn, pSpatializer->channelsIn, frameCount, ma_channel_mix_mode_rectangular, ma_mono_expansion_mode_default); /* Safe casts to float* because f32 is the only supported format. */ - } - } else { - /* The listener is disabled. Output silence. */ - ma_silence_pcm_frames(pFramesOut, frameCount, ma_format_f32, pSpatializer->channelsOut); - } - - /* - We're not doing attenuation so don't bother with doppler for now. I'm not sure if this is - the correct thinking so might need to review this later. - */ - pSpatializer->dopplerPitch = 1; - } else { - /* - Let's first determine which listener the sound is closest to. Need to keep in mind that we - might not have a world or any listeners, in which case we just spatializer based on the - listener being positioned at the origin (0, 0, 0). - */ - ma_vec3f relativePosNormalized; - ma_vec3f relativePos; /* The position relative to the listener. */ - ma_vec3f relativeDir; /* The direction of the sound, relative to the listener. */ - ma_vec3f listenerVel; /* The velocity of the listener. For doppler pitch calculation. */ - float speedOfSound; - float distance = 0; - float gain = 1; - ma_uint32 iChannel; - const ma_uint32 channelsOut = pSpatializer->channelsOut; - const ma_uint32 channelsIn = pSpatializer->channelsIn; - float minDistance = ma_spatializer_get_min_distance(pSpatializer); - float maxDistance = ma_spatializer_get_max_distance(pSpatializer); - float rolloff = ma_spatializer_get_rolloff(pSpatializer); - float dopplerFactor = ma_spatializer_get_doppler_factor(pSpatializer); - - /* - We'll need the listener velocity for doppler pitch calculations. The speed of sound is - defined by the listener, so we'll grab that here too. - */ - if (pListener != NULL) { - listenerVel = ma_spatializer_listener_get_velocity(pListener); - speedOfSound = pListener->config.speedOfSound; - } else { - listenerVel = ma_vec3f_init_3f(0, 0, 0); - speedOfSound = MA_DEFAULT_SPEED_OF_SOUND; - } - - if (pListener == NULL || ma_spatializer_get_positioning(pSpatializer) == ma_positioning_relative) { - /* There's no listener or we're using relative positioning. */ - relativePos = ma_spatializer_get_position(pSpatializer); - relativeDir = ma_spatializer_get_direction(pSpatializer); - } else { - /* - We've found a listener and we're using absolute positioning. We need to transform the - sound's position and direction so that it's relative to listener. Later on we'll use - this for determining the factors to apply to each channel to apply the panning effect. - */ - ma_spatializer_get_relative_position_and_direction(pSpatializer, pListener, &relativePos, &relativeDir); - } - - distance = ma_vec3f_len(relativePos); - - /* We've gathered the data, so now we can apply some spatialization. */ - switch (ma_spatializer_get_attenuation_model(pSpatializer)) { - case ma_attenuation_model_inverse: - { - gain = ma_attenuation_inverse(distance, minDistance, maxDistance, rolloff); - } break; - case ma_attenuation_model_linear: - { - gain = ma_attenuation_linear(distance, minDistance, maxDistance, rolloff); - } break; - case ma_attenuation_model_exponential: - { - gain = ma_attenuation_exponential(distance, minDistance, maxDistance, rolloff); - } break; - case ma_attenuation_model_none: - default: - { - gain = 1; - } break; - } - - /* Normalize the position. */ - if (distance > 0.001f) { - float distanceInv = 1/distance; - relativePosNormalized = relativePos; - relativePosNormalized.x *= distanceInv; - relativePosNormalized.y *= distanceInv; - relativePosNormalized.z *= distanceInv; - } else { - distance = 0; - relativePosNormalized = ma_vec3f_init_3f(0, 0, 0); - } - - /* - Angular attenuation. - - Unlike distance gain, the math for this is not specified by the OpenAL spec so we'll just go ahead and figure - this out for ourselves at the expense of possibly being inconsistent with other implementations. - - To do cone attenuation, I'm just using the same math that we'd use to implement a basic spotlight in OpenGL. We - just need to get the direction from the source to the listener and then do a dot product against that and the - direction of the spotlight. Then we just compare that dot product against the cosine of the inner and outer - angles. If the dot product is greater than the outer angle, we just use coneOuterGain. If it's less than - the inner angle, we just use a gain of 1. Otherwise we linearly interpolate between 1 and coneOuterGain. - */ - if (distance > 0) { - /* Source angular gain. */ - float spatializerConeInnerAngle; - float spatializerConeOuterAngle; - float spatializerConeOuterGain; - ma_spatializer_get_cone(pSpatializer, &spatializerConeInnerAngle, &spatializerConeOuterAngle, &spatializerConeOuterGain); - - gain *= ma_calculate_angular_gain(relativeDir, ma_vec3f_neg(relativePosNormalized), spatializerConeInnerAngle, spatializerConeOuterAngle, spatializerConeOuterGain); - - /* - We're supporting angular gain on the listener as well for those who want to reduce the volume of sounds that - are positioned behind the listener. On default settings, this will have no effect. - */ - if (pListener != NULL && pListener->config.coneInnerAngleInRadians < 6.283185f) { - ma_vec3f listenerDirection; - float listenerInnerAngle; - float listenerOuterAngle; - float listenerOuterGain; - - if (pListener->config.handedness == ma_handedness_right) { - listenerDirection = ma_vec3f_init_3f(0, 0, -1); - } else { - listenerDirection = ma_vec3f_init_3f(0, 0, +1); - } - - listenerInnerAngle = pListener->config.coneInnerAngleInRadians; - listenerOuterAngle = pListener->config.coneOuterAngleInRadians; - listenerOuterGain = pListener->config.coneOuterGain; - - gain *= ma_calculate_angular_gain(listenerDirection, relativePosNormalized, listenerInnerAngle, listenerOuterAngle, listenerOuterGain); - } - } else { - /* The sound is right on top of the listener. Don't do any angular attenuation. */ - } - - - /* Clamp the gain. */ - gain = ma_clamp(gain, ma_spatializer_get_min_gain(pSpatializer), ma_spatializer_get_max_gain(pSpatializer)); - - /* - The gain needs to be applied per-channel here. The spatialization code below will be changing the per-channel - gains which will then eventually be passed into the gainer which will deal with smoothing the gain transitions - to avoid harsh changes in gain. - */ - for (iChannel = 0; iChannel < channelsOut; iChannel += 1) { - pSpatializer->pNewChannelGainsOut[iChannel] = gain; - } - - /* - Convert to our output channel count. If the listener is disabled we just output silence here. We cannot ignore - the whole section of code here because we need to update some internal spatialization state. - */ - if (ma_spatializer_listener_is_enabled(pListener)) { - ma_channel_map_apply_f32((float*)pFramesOut, pChannelMapOut, channelsOut, (const float*)pFramesIn, pChannelMapIn, channelsIn, frameCount, ma_channel_mix_mode_rectangular, ma_mono_expansion_mode_default); - } else { - ma_silence_pcm_frames(pFramesOut, frameCount, ma_format_f32, pSpatializer->channelsOut); - } - - - /* - Panning. This is where we'll apply the gain and convert to the output channel count. We have an optimized path for - when we're converting to a mono stream. In that case we don't really need to do any panning - we just apply the - gain to the final output. - */ - /*printf("distance=%f; gain=%f\n", distance, gain);*/ - - /* We must have a valid channel map here to ensure we spatialize properly. */ - MA_ASSERT(pChannelMapOut != NULL); - - /* - We're not converting to mono so we'll want to apply some panning. This is where the feeling of something being - to the left, right, infront or behind the listener is calculated. I'm just using a basic model here. Note that - the code below is not based on any specific algorithm. I'm just implementing this off the top of my head and - seeing how it goes. There might be better ways to do this. - - To determine the direction of the sound relative to a speaker I'm using dot products. Each speaker is given a - direction. For example, the left channel in a stereo system will be -1 on the X axis and the right channel will - be +1 on the X axis. A dot product is performed against the direction vector of the channel and the normalized - position of the sound. - */ - - /* - Calculate our per-channel gains. We do this based on the normalized relative position of the sound and it's - relation to the direction of the channel. - */ - if (distance > 0) { - ma_vec3f unitPos = relativePos; - float distanceInv = 1/distance; - unitPos.x *= distanceInv; - unitPos.y *= distanceInv; - unitPos.z *= distanceInv; - - for (iChannel = 0; iChannel < channelsOut; iChannel += 1) { - ma_channel channelOut; - float d; - float dMin; - - channelOut = ma_channel_map_get_channel(pChannelMapOut, channelsOut, iChannel); - if (ma_is_spatial_channel_position(channelOut)) { - d = ma_mix_f32_fast(1, ma_vec3f_dot(unitPos, ma_get_channel_direction(channelOut)), ma_spatializer_get_directional_attenuation_factor(pSpatializer)); - } else { - d = 1; /* It's not a spatial channel so there's no real notion of direction. */ - } - - /* - In my testing, if the panning effect is too aggressive it makes spatialization feel uncomfortable. - The "dMin" variable below is used to control the aggressiveness of the panning effect. When set to - 0, panning will be most extreme and any sounds that are positioned on the opposite side of the - speaker will be completely silent from that speaker. Not only does this feel uncomfortable, it - doesn't even remotely represent the real world at all because sounds that come from your right side - are still clearly audible from your left side. Setting "dMin" to 1 will result in no panning at - all, which is also not ideal. By setting it to something greater than 0, the spatialization effect - becomes much less dramatic and a lot more bearable. - - Summary: 0 = more extreme panning; 1 = no panning. - */ - dMin = pSpatializer->minSpatializationChannelGain; - - /* - At this point, "d" will be positive if the sound is on the same side as the channel and negative if - it's on the opposite side. It will be in the range of -1..1. There's two ways I can think of to - calculate a panning value. The first is to simply convert it to 0..1, however this has a problem - which I'm not entirely happy with. Considering a stereo system, when a sound is positioned right - in front of the listener it'll result in each speaker getting a gain of 0.5. I don't know if I like - the idea of having a scaling factor of 0.5 being applied to a sound when it's sitting right in front - of the listener. I would intuitively expect that to be played at full volume, or close to it. - - The second idea I think of is to only apply a reduction in gain when the sound is on the opposite - side of the speaker. That is, reduce the gain only when the dot product is negative. The problem - with this is that there will not be any attenuation as the sound sweeps around the 180 degrees - where the dot product is positive. The idea with this option is that you leave the gain at 1 when - the sound is being played on the same side as the speaker and then you just reduce the volume when - the sound is on the other side. - - The summarize, I think the first option should give a better sense of spatialization, but the second - option is better for preserving the sound's power. - - UPDATE: In my testing, I find the first option to sound better. You can feel the sense of space a - bit better, but you can also hear the reduction in volume when it's right in front. - */ - #if 1 - { - /* - Scale the dot product from -1..1 to 0..1. Will result in a sound directly in front losing power - by being played at 0.5 gain. - */ - d = (d + 1) * 0.5f; /* -1..1 to 0..1 */ - d = ma_max(d, dMin); - pSpatializer->pNewChannelGainsOut[iChannel] *= d; - } - #else - { - /* - Only reduce the volume of the sound if it's on the opposite side. This path keeps the volume more - consistent, but comes at the expense of a worse sense of space and positioning. - */ - if (d < 0) { - d += 1; /* Move into the positive range. */ - d = ma_max(d, dMin); - channelGainsOut[iChannel] *= d; - } - } - #endif - } - } else { - /* Assume the sound is right on top of us. Don't do any panning. */ - } - - /* Now we need to apply the volume to each channel. This needs to run through the gainer to ensure we get a smooth volume transition. */ - ma_gainer_set_gains(&pSpatializer->gainer, pSpatializer->pNewChannelGainsOut); - ma_gainer_process_pcm_frames(&pSpatializer->gainer, pFramesOut, pFramesOut, frameCount); - - /* - Before leaving we'll want to update our doppler pitch so that the caller can apply some - pitch shifting if they desire. Note that we need to negate the relative position here - because the doppler calculation needs to be source-to-listener, but ours is listener-to- - source. - */ - if (dopplerFactor > 0) { - pSpatializer->dopplerPitch = ma_doppler_pitch(ma_vec3f_sub(ma_spatializer_listener_get_position(pListener), ma_spatializer_get_position(pSpatializer)), ma_spatializer_get_velocity(pSpatializer), listenerVel, speedOfSound, dopplerFactor); - } else { - pSpatializer->dopplerPitch = 1; - } - } - - return MA_SUCCESS; -} - -MA_API ma_result ma_spatializer_set_master_volume(ma_spatializer* pSpatializer, float volume) -{ - if (pSpatializer == NULL) { - return MA_INVALID_ARGS; - } - - return ma_gainer_set_master_volume(&pSpatializer->gainer, volume); -} - -MA_API ma_result ma_spatializer_get_master_volume(const ma_spatializer* pSpatializer, float* pVolume) -{ - if (pSpatializer == NULL) { - return MA_INVALID_ARGS; - } - - return ma_gainer_get_master_volume(&pSpatializer->gainer, pVolume); -} - -MA_API ma_uint32 ma_spatializer_get_input_channels(const ma_spatializer* pSpatializer) -{ - if (pSpatializer == NULL) { - return 0; - } - - return pSpatializer->channelsIn; -} - -MA_API ma_uint32 ma_spatializer_get_output_channels(const ma_spatializer* pSpatializer) -{ - if (pSpatializer == NULL) { - return 0; - } - - return pSpatializer->channelsOut; -} - -MA_API void ma_spatializer_set_attenuation_model(ma_spatializer* pSpatializer, ma_attenuation_model attenuationModel) -{ - if (pSpatializer == NULL) { - return; - } - - ma_atomic_exchange_i32(&pSpatializer->attenuationModel, attenuationModel); -} - -MA_API ma_attenuation_model ma_spatializer_get_attenuation_model(const ma_spatializer* pSpatializer) -{ - if (pSpatializer == NULL) { - return ma_attenuation_model_none; - } - - return (ma_attenuation_model)ma_atomic_load_i32(&pSpatializer->attenuationModel); -} - -MA_API void ma_spatializer_set_positioning(ma_spatializer* pSpatializer, ma_positioning positioning) -{ - if (pSpatializer == NULL) { - return; - } - - ma_atomic_exchange_i32(&pSpatializer->positioning, positioning); -} - -MA_API ma_positioning ma_spatializer_get_positioning(const ma_spatializer* pSpatializer) -{ - if (pSpatializer == NULL) { - return ma_positioning_absolute; - } - - return (ma_positioning)ma_atomic_load_i32(&pSpatializer->positioning); -} - -MA_API void ma_spatializer_set_rolloff(ma_spatializer* pSpatializer, float rolloff) -{ - if (pSpatializer == NULL) { - return; - } - - ma_atomic_exchange_f32(&pSpatializer->rolloff, rolloff); -} - -MA_API float ma_spatializer_get_rolloff(const ma_spatializer* pSpatializer) -{ - if (pSpatializer == NULL) { - return 0; - } - - return ma_atomic_load_f32(&pSpatializer->rolloff); -} - -MA_API void ma_spatializer_set_min_gain(ma_spatializer* pSpatializer, float minGain) -{ - if (pSpatializer == NULL) { - return; - } - - ma_atomic_exchange_f32(&pSpatializer->minGain, minGain); -} - -MA_API float ma_spatializer_get_min_gain(const ma_spatializer* pSpatializer) -{ - if (pSpatializer == NULL) { - return 0; - } - - return ma_atomic_load_f32(&pSpatializer->minGain); -} - -MA_API void ma_spatializer_set_max_gain(ma_spatializer* pSpatializer, float maxGain) -{ - if (pSpatializer == NULL) { - return; - } - - ma_atomic_exchange_f32(&pSpatializer->maxGain, maxGain); -} - -MA_API float ma_spatializer_get_max_gain(const ma_spatializer* pSpatializer) -{ - if (pSpatializer == NULL) { - return 0; - } - - return ma_atomic_load_f32(&pSpatializer->maxGain); -} - -MA_API void ma_spatializer_set_min_distance(ma_spatializer* pSpatializer, float minDistance) -{ - if (pSpatializer == NULL) { - return; - } - - ma_atomic_exchange_f32(&pSpatializer->minDistance, minDistance); -} - -MA_API float ma_spatializer_get_min_distance(const ma_spatializer* pSpatializer) -{ - if (pSpatializer == NULL) { - return 0; - } - - return ma_atomic_load_f32(&pSpatializer->minDistance); -} - -MA_API void ma_spatializer_set_max_distance(ma_spatializer* pSpatializer, float maxDistance) -{ - if (pSpatializer == NULL) { - return; - } - - ma_atomic_exchange_f32(&pSpatializer->maxDistance, maxDistance); -} - -MA_API float ma_spatializer_get_max_distance(const ma_spatializer* pSpatializer) -{ - if (pSpatializer == NULL) { - return 0; - } - - return ma_atomic_load_f32(&pSpatializer->maxDistance); -} - -MA_API void ma_spatializer_set_cone(ma_spatializer* pSpatializer, float innerAngleInRadians, float outerAngleInRadians, float outerGain) -{ - if (pSpatializer == NULL) { - return; - } - - ma_atomic_exchange_f32(&pSpatializer->coneInnerAngleInRadians, innerAngleInRadians); - ma_atomic_exchange_f32(&pSpatializer->coneOuterAngleInRadians, outerAngleInRadians); - ma_atomic_exchange_f32(&pSpatializer->coneOuterGain, outerGain); -} - -MA_API void ma_spatializer_get_cone(const ma_spatializer* pSpatializer, float* pInnerAngleInRadians, float* pOuterAngleInRadians, float* pOuterGain) -{ - if (pSpatializer == NULL) { - return; - } - - if (pInnerAngleInRadians != NULL) { - *pInnerAngleInRadians = ma_atomic_load_f32(&pSpatializer->coneInnerAngleInRadians); - } - - if (pOuterAngleInRadians != NULL) { - *pOuterAngleInRadians = ma_atomic_load_f32(&pSpatializer->coneOuterAngleInRadians); - } - - if (pOuterGain != NULL) { - *pOuterGain = ma_atomic_load_f32(&pSpatializer->coneOuterGain); - } -} - -MA_API void ma_spatializer_set_doppler_factor(ma_spatializer* pSpatializer, float dopplerFactor) -{ - if (pSpatializer == NULL) { - return; - } - - ma_atomic_exchange_f32(&pSpatializer->dopplerFactor, dopplerFactor); -} - -MA_API float ma_spatializer_get_doppler_factor(const ma_spatializer* pSpatializer) -{ - if (pSpatializer == NULL) { - return 1; - } - - return ma_atomic_load_f32(&pSpatializer->dopplerFactor); -} - -MA_API void ma_spatializer_set_directional_attenuation_factor(ma_spatializer* pSpatializer, float directionalAttenuationFactor) -{ - if (pSpatializer == NULL) { - return; - } - - ma_atomic_exchange_f32(&pSpatializer->directionalAttenuationFactor, directionalAttenuationFactor); -} - -MA_API float ma_spatializer_get_directional_attenuation_factor(const ma_spatializer* pSpatializer) -{ - if (pSpatializer == NULL) { - return 1; - } - - return ma_atomic_load_f32(&pSpatializer->directionalAttenuationFactor); -} - -MA_API void ma_spatializer_set_position(ma_spatializer* pSpatializer, float x, float y, float z) -{ - if (pSpatializer == NULL) { - return; - } - - ma_atomic_vec3f_set(&pSpatializer->position, ma_vec3f_init_3f(x, y, z)); -} - -MA_API ma_vec3f ma_spatializer_get_position(const ma_spatializer* pSpatializer) -{ - if (pSpatializer == NULL) { - return ma_vec3f_init_3f(0, 0, 0); - } - - return ma_atomic_vec3f_get((ma_atomic_vec3f*)&pSpatializer->position); /* Naughty const-cast. It's just for atomically loading the vec3 which should be safe. */ -} - -MA_API void ma_spatializer_set_direction(ma_spatializer* pSpatializer, float x, float y, float z) -{ - if (pSpatializer == NULL) { - return; - } - - ma_atomic_vec3f_set(&pSpatializer->direction, ma_vec3f_init_3f(x, y, z)); -} - -MA_API ma_vec3f ma_spatializer_get_direction(const ma_spatializer* pSpatializer) -{ - if (pSpatializer == NULL) { - return ma_vec3f_init_3f(0, 0, -1); - } - - return ma_atomic_vec3f_get((ma_atomic_vec3f*)&pSpatializer->direction); /* Naughty const-cast. It's just for atomically loading the vec3 which should be safe. */ -} - -MA_API void ma_spatializer_set_velocity(ma_spatializer* pSpatializer, float x, float y, float z) -{ - if (pSpatializer == NULL) { - return; - } - - ma_atomic_vec3f_set(&pSpatializer->velocity, ma_vec3f_init_3f(x, y, z)); -} - -MA_API ma_vec3f ma_spatializer_get_velocity(const ma_spatializer* pSpatializer) -{ - if (pSpatializer == NULL) { - return ma_vec3f_init_3f(0, 0, 0); - } - - return ma_atomic_vec3f_get((ma_atomic_vec3f*)&pSpatializer->velocity); /* Naughty const-cast. It's just for atomically loading the vec3 which should be safe. */ -} - -MA_API void ma_spatializer_get_relative_position_and_direction(const ma_spatializer* pSpatializer, const ma_spatializer_listener* pListener, ma_vec3f* pRelativePos, ma_vec3f* pRelativeDir) -{ - if (pRelativePos != NULL) { - pRelativePos->x = 0; - pRelativePos->y = 0; - pRelativePos->z = 0; - } - - if (pRelativeDir != NULL) { - pRelativeDir->x = 0; - pRelativeDir->y = 0; - pRelativeDir->z = -1; - } - - if (pSpatializer == NULL) { - return; - } - - if (pListener == NULL || ma_spatializer_get_positioning(pSpatializer) == ma_positioning_relative) { - /* There's no listener or we're using relative positioning. */ - if (pRelativePos != NULL) { - *pRelativePos = ma_spatializer_get_position(pSpatializer); - } - if (pRelativeDir != NULL) { - *pRelativeDir = ma_spatializer_get_direction(pSpatializer); - } - } else { - ma_vec3f spatializerPosition; - ma_vec3f spatializerDirection; - ma_vec3f listenerPosition; - ma_vec3f listenerDirection; - ma_vec3f v; - ma_vec3f axisX; - ma_vec3f axisY; - ma_vec3f axisZ; - float m[4][4]; - - spatializerPosition = ma_spatializer_get_position(pSpatializer); - spatializerDirection = ma_spatializer_get_direction(pSpatializer); - listenerPosition = ma_spatializer_listener_get_position(pListener); - listenerDirection = ma_spatializer_listener_get_direction(pListener); - - /* - We need to calculate the right vector from our forward and up vectors. This is done with - a cross product. - */ - axisZ = ma_vec3f_normalize(listenerDirection); /* Normalization required here because we can't trust the caller. */ - axisX = ma_vec3f_normalize(ma_vec3f_cross(axisZ, pListener->config.worldUp)); /* Normalization required here because the world up vector may not be perpendicular with the forward vector. */ - - /* - The calculation of axisX above can result in a zero-length vector if the listener is - looking straight up on the Y axis. We'll need to fall back to a +X in this case so that - the calculations below don't fall apart. This is where a quaternion based listener and - sound orientation would come in handy. - */ - if (ma_vec3f_len2(axisX) == 0) { - axisX = ma_vec3f_init_3f(1, 0, 0); - } - - axisY = ma_vec3f_cross(axisX, axisZ); /* No normalization is required here because axisX and axisZ are unit length and perpendicular. */ - - /* - We need to swap the X axis if we're left handed because otherwise the cross product above - will have resulted in it pointing in the wrong direction (right handed was assumed in the - cross products above). - */ - if (pListener->config.handedness == ma_handedness_left) { - axisX = ma_vec3f_neg(axisX); - } - - /* Lookat. */ - m[0][0] = axisX.x; m[1][0] = axisX.y; m[2][0] = axisX.z; m[3][0] = -ma_vec3f_dot(axisX, listenerPosition); - m[0][1] = axisY.x; m[1][1] = axisY.y; m[2][1] = axisY.z; m[3][1] = -ma_vec3f_dot(axisY, listenerPosition); - m[0][2] = -axisZ.x; m[1][2] = -axisZ.y; m[2][2] = -axisZ.z; m[3][2] = -ma_vec3f_dot(ma_vec3f_neg(axisZ), listenerPosition); - m[0][3] = 0; m[1][3] = 0; m[2][3] = 0; m[3][3] = 1; - - /* - Multiply the lookat matrix by the spatializer position to transform it to listener - space. This allows calculations to work based on the sound being relative to the - origin which makes things simpler. - */ - if (pRelativePos != NULL) { - v = spatializerPosition; - pRelativePos->x = m[0][0] * v.x + m[1][0] * v.y + m[2][0] * v.z + m[3][0] * 1; - pRelativePos->y = m[0][1] * v.x + m[1][1] * v.y + m[2][1] * v.z + m[3][1] * 1; - pRelativePos->z = m[0][2] * v.x + m[1][2] * v.y + m[2][2] * v.z + m[3][2] * 1; - } - - /* - The direction of the sound needs to also be transformed so that it's relative to the - rotation of the listener. - */ - if (pRelativeDir != NULL) { - v = spatializerDirection; - pRelativeDir->x = m[0][0] * v.x + m[1][0] * v.y + m[2][0] * v.z; - pRelativeDir->y = m[0][1] * v.x + m[1][1] * v.y + m[2][1] * v.z; - pRelativeDir->z = m[0][2] * v.x + m[1][2] * v.y + m[2][2] * v.z; - } - } -} - - - - -/************************************************************************************************************************************************************** - -Resampling - -**************************************************************************************************************************************************************/ -MA_API ma_linear_resampler_config ma_linear_resampler_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRateIn, ma_uint32 sampleRateOut) -{ - ma_linear_resampler_config config; - MA_ZERO_OBJECT(&config); - config.format = format; - config.channels = channels; - config.sampleRateIn = sampleRateIn; - config.sampleRateOut = sampleRateOut; - config.lpfOrder = ma_min(MA_DEFAULT_RESAMPLER_LPF_ORDER, MA_MAX_FILTER_ORDER); - config.lpfNyquistFactor = 1; - - return config; -} - - -typedef struct -{ - size_t sizeInBytes; - size_t x0Offset; - size_t x1Offset; - size_t lpfOffset; -} ma_linear_resampler_heap_layout; - - -static void ma_linear_resampler_adjust_timer_for_new_rate(ma_linear_resampler* pResampler, ma_uint32 oldSampleRateOut, ma_uint32 newSampleRateOut) -{ - /* - So what's happening here? Basically we need to adjust the fractional component of the time advance based on the new rate. The old time advance will - be based on the old sample rate, but we are needing to adjust it to that it's based on the new sample rate. - */ - ma_uint32 oldRateTimeWhole = pResampler->inTimeFrac / oldSampleRateOut; /* <-- This should almost never be anything other than 0, but leaving it here to make this more general and robust just in case. */ - ma_uint32 oldRateTimeFract = pResampler->inTimeFrac % oldSampleRateOut; - - pResampler->inTimeFrac = - (oldRateTimeWhole * newSampleRateOut) + - ((oldRateTimeFract * newSampleRateOut) / oldSampleRateOut); - - /* Make sure the fractional part is less than the output sample rate. */ - pResampler->inTimeInt += pResampler->inTimeFrac / pResampler->config.sampleRateOut; - pResampler->inTimeFrac = pResampler->inTimeFrac % pResampler->config.sampleRateOut; -} - -static ma_result ma_linear_resampler_set_rate_internal(ma_linear_resampler* pResampler, void* pHeap, ma_linear_resampler_heap_layout* pHeapLayout, ma_uint32 sampleRateIn, ma_uint32 sampleRateOut, ma_bool32 isResamplerAlreadyInitialized) -{ - ma_result result; - ma_uint32 gcf; - ma_uint32 lpfSampleRate; - double lpfCutoffFrequency; - ma_lpf_config lpfConfig; - ma_uint32 oldSampleRateOut; /* Required for adjusting time advance down the bottom. */ - - if (pResampler == NULL) { - return MA_INVALID_ARGS; - } - - if (sampleRateIn == 0 || sampleRateOut == 0) { - return MA_INVALID_ARGS; - } - - oldSampleRateOut = pResampler->config.sampleRateOut; - - pResampler->config.sampleRateIn = sampleRateIn; - pResampler->config.sampleRateOut = sampleRateOut; - - /* Simplify the sample rate. */ - gcf = ma_gcf_u32(pResampler->config.sampleRateIn, pResampler->config.sampleRateOut); - pResampler->config.sampleRateIn /= gcf; - pResampler->config.sampleRateOut /= gcf; - - /* Always initialize the low-pass filter, even when the order is 0. */ - if (pResampler->config.lpfOrder > MA_MAX_FILTER_ORDER) { - return MA_INVALID_ARGS; - } - - lpfSampleRate = (ma_uint32)(ma_max(pResampler->config.sampleRateIn, pResampler->config.sampleRateOut)); - lpfCutoffFrequency = ( double)(ma_min(pResampler->config.sampleRateIn, pResampler->config.sampleRateOut) * 0.5 * pResampler->config.lpfNyquistFactor); - - lpfConfig = ma_lpf_config_init(pResampler->config.format, pResampler->config.channels, lpfSampleRate, lpfCutoffFrequency, pResampler->config.lpfOrder); - - /* - If the resampler is already initialized we don't want to do a fresh initialization of the low-pass filter because it will result in the cached frames - getting cleared. Instead we re-initialize the filter which will maintain any cached frames. - */ - if (isResamplerAlreadyInitialized) { - result = ma_lpf_reinit(&lpfConfig, &pResampler->lpf); - } else { - result = ma_lpf_init_preallocated(&lpfConfig, ma_offset_ptr(pHeap, pHeapLayout->lpfOffset), &pResampler->lpf); - } - - if (result != MA_SUCCESS) { - return result; - } - - - pResampler->inAdvanceInt = pResampler->config.sampleRateIn / pResampler->config.sampleRateOut; - pResampler->inAdvanceFrac = pResampler->config.sampleRateIn % pResampler->config.sampleRateOut; - - /* Our timer was based on the old rate. We need to adjust it so that it's based on the new rate. */ - ma_linear_resampler_adjust_timer_for_new_rate(pResampler, oldSampleRateOut, pResampler->config.sampleRateOut); - - return MA_SUCCESS; -} - -static ma_result ma_linear_resampler_get_heap_layout(const ma_linear_resampler_config* pConfig, ma_linear_resampler_heap_layout* pHeapLayout) -{ - MA_ASSERT(pHeapLayout != NULL); - - MA_ZERO_OBJECT(pHeapLayout); - - if (pConfig == NULL) { - return MA_INVALID_ARGS; - } - - if (pConfig->format != ma_format_f32 && pConfig->format != ma_format_s16) { - return MA_INVALID_ARGS; - } - - if (pConfig->channels == 0) { - return MA_INVALID_ARGS; - } - - pHeapLayout->sizeInBytes = 0; - - /* x0 */ - pHeapLayout->x0Offset = pHeapLayout->sizeInBytes; - if (pConfig->format == ma_format_f32) { - pHeapLayout->sizeInBytes += sizeof(float) * pConfig->channels; - } else { - pHeapLayout->sizeInBytes += sizeof(ma_int16) * pConfig->channels; - } - - /* x1 */ - pHeapLayout->x1Offset = pHeapLayout->sizeInBytes; - if (pConfig->format == ma_format_f32) { - pHeapLayout->sizeInBytes += sizeof(float) * pConfig->channels; - } else { - pHeapLayout->sizeInBytes += sizeof(ma_int16) * pConfig->channels; - } - - /* LPF */ - pHeapLayout->lpfOffset = ma_align_64(pHeapLayout->sizeInBytes); - { - ma_result result; - size_t lpfHeapSizeInBytes; - ma_lpf_config lpfConfig = ma_lpf_config_init(pConfig->format, pConfig->channels, 1, 1, pConfig->lpfOrder); /* Sample rate and cutoff frequency do not matter. */ - - result = ma_lpf_get_heap_size(&lpfConfig, &lpfHeapSizeInBytes); - if (result != MA_SUCCESS) { - return result; - } - - pHeapLayout->sizeInBytes += lpfHeapSizeInBytes; - } - - /* Make sure allocation size is aligned. */ - pHeapLayout->sizeInBytes = ma_align_64(pHeapLayout->sizeInBytes); - - return MA_SUCCESS; -} - -MA_API ma_result ma_linear_resampler_get_heap_size(const ma_linear_resampler_config* pConfig, size_t* pHeapSizeInBytes) -{ - ma_result result; - ma_linear_resampler_heap_layout heapLayout; - - if (pHeapSizeInBytes == NULL) { - return MA_INVALID_ARGS; - } - - *pHeapSizeInBytes = 0; - - result = ma_linear_resampler_get_heap_layout(pConfig, &heapLayout); - if (result != MA_SUCCESS) { - return result; - } - - *pHeapSizeInBytes = heapLayout.sizeInBytes; - - return MA_SUCCESS; -} - -MA_API ma_result ma_linear_resampler_init_preallocated(const ma_linear_resampler_config* pConfig, void* pHeap, ma_linear_resampler* pResampler) -{ - ma_result result; - ma_linear_resampler_heap_layout heapLayout; - - if (pResampler == NULL) { - return MA_INVALID_ARGS; - } - - MA_ZERO_OBJECT(pResampler); - - result = ma_linear_resampler_get_heap_layout(pConfig, &heapLayout); - if (result != MA_SUCCESS) { - return result; - } - - pResampler->config = *pConfig; - - pResampler->_pHeap = pHeap; - MA_ZERO_MEMORY(pHeap, heapLayout.sizeInBytes); - - if (pConfig->format == ma_format_f32) { - pResampler->x0.f32 = (float*)ma_offset_ptr(pHeap, heapLayout.x0Offset); - pResampler->x1.f32 = (float*)ma_offset_ptr(pHeap, heapLayout.x1Offset); - } else { - pResampler->x0.s16 = (ma_int16*)ma_offset_ptr(pHeap, heapLayout.x0Offset); - pResampler->x1.s16 = (ma_int16*)ma_offset_ptr(pHeap, heapLayout.x1Offset); - } - - /* Setting the rate will set up the filter and time advances for us. */ - result = ma_linear_resampler_set_rate_internal(pResampler, pHeap, &heapLayout, pConfig->sampleRateIn, pConfig->sampleRateOut, /* isResamplerAlreadyInitialized = */ MA_FALSE); - if (result != MA_SUCCESS) { - return result; - } - - pResampler->inTimeInt = 1; /* Set this to one to force an input sample to always be loaded for the first output frame. */ - pResampler->inTimeFrac = 0; - - return MA_SUCCESS; -} - -MA_API ma_result ma_linear_resampler_init(const ma_linear_resampler_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_linear_resampler* pResampler) -{ - ma_result result; - size_t heapSizeInBytes; - void* pHeap; - - result = ma_linear_resampler_get_heap_size(pConfig, &heapSizeInBytes); - if (result != MA_SUCCESS) { - return result; - } - - if (heapSizeInBytes > 0) { - pHeap = ma_malloc(heapSizeInBytes, pAllocationCallbacks); - if (pHeap == NULL) { - return MA_OUT_OF_MEMORY; - } - } else { - pHeap = NULL; - } - - result = ma_linear_resampler_init_preallocated(pConfig, pHeap, pResampler); - if (result != MA_SUCCESS) { - ma_free(pHeap, pAllocationCallbacks); - return result; - } - - pResampler->_ownsHeap = MA_TRUE; - return MA_SUCCESS; -} - -MA_API void ma_linear_resampler_uninit(ma_linear_resampler* pResampler, const ma_allocation_callbacks* pAllocationCallbacks) -{ - if (pResampler == NULL) { - return; - } - - ma_lpf_uninit(&pResampler->lpf, pAllocationCallbacks); - - if (pResampler->_ownsHeap) { - ma_free(pResampler->_pHeap, pAllocationCallbacks); - } -} - -static MA_INLINE ma_int16 ma_linear_resampler_mix_s16(ma_int16 x, ma_int16 y, ma_int32 a, const ma_int32 shift) -{ - ma_int32 b; - ma_int32 c; - ma_int32 r; - - MA_ASSERT(a <= (1<> shift); -} - -static void ma_linear_resampler_interpolate_frame_s16(ma_linear_resampler* pResampler, ma_int16* MA_RESTRICT pFrameOut) -{ - ma_uint32 c; - ma_uint32 a; - const ma_uint32 channels = pResampler->config.channels; - const ma_uint32 shift = 12; - - MA_ASSERT(pResampler != NULL); - MA_ASSERT(pFrameOut != NULL); - - a = (pResampler->inTimeFrac << shift) / pResampler->config.sampleRateOut; - - MA_ASSUME(channels > 0); - for (c = 0; c < channels; c += 1) { - ma_int16 s = ma_linear_resampler_mix_s16(pResampler->x0.s16[c], pResampler->x1.s16[c], a, shift); - pFrameOut[c] = s; - } -} - - -static void ma_linear_resampler_interpolate_frame_f32(ma_linear_resampler* pResampler, float* MA_RESTRICT pFrameOut) -{ - ma_uint32 c; - float a; - const ma_uint32 channels = pResampler->config.channels; - - MA_ASSERT(pResampler != NULL); - MA_ASSERT(pFrameOut != NULL); - - a = (float)pResampler->inTimeFrac / pResampler->config.sampleRateOut; - - MA_ASSUME(channels > 0); - for (c = 0; c < channels; c += 1) { - float s = ma_mix_f32_fast(pResampler->x0.f32[c], pResampler->x1.f32[c], a); - pFrameOut[c] = s; - } -} - -static ma_result ma_linear_resampler_process_pcm_frames_s16_downsample(ma_linear_resampler* pResampler, const void* pFramesIn, ma_uint64* pFrameCountIn, void* pFramesOut, ma_uint64* pFrameCountOut) -{ - const ma_int16* pFramesInS16; - /* */ ma_int16* pFramesOutS16; - ma_uint64 frameCountIn; - ma_uint64 frameCountOut; - ma_uint64 framesProcessedIn; - ma_uint64 framesProcessedOut; - - MA_ASSERT(pResampler != NULL); - MA_ASSERT(pFrameCountIn != NULL); - MA_ASSERT(pFrameCountOut != NULL); - - pFramesInS16 = (const ma_int16*)pFramesIn; - pFramesOutS16 = ( ma_int16*)pFramesOut; - frameCountIn = *pFrameCountIn; - frameCountOut = *pFrameCountOut; - framesProcessedIn = 0; - framesProcessedOut = 0; - - while (framesProcessedOut < frameCountOut) { - /* Before interpolating we need to load the buffers. When doing this we need to ensure we run every input sample through the filter. */ - while (pResampler->inTimeInt > 0 && frameCountIn > framesProcessedIn) { - ma_uint32 iChannel; - - if (pFramesInS16 != NULL) { - for (iChannel = 0; iChannel < pResampler->config.channels; iChannel += 1) { - pResampler->x0.s16[iChannel] = pResampler->x1.s16[iChannel]; - pResampler->x1.s16[iChannel] = pFramesInS16[iChannel]; - } - pFramesInS16 += pResampler->config.channels; - } else { - for (iChannel = 0; iChannel < pResampler->config.channels; iChannel += 1) { - pResampler->x0.s16[iChannel] = pResampler->x1.s16[iChannel]; - pResampler->x1.s16[iChannel] = 0; - } - } - - /* Filter. Do not apply filtering if sample rates are the same or else you'll get dangerous glitching. */ - if (pResampler->config.sampleRateIn != pResampler->config.sampleRateOut) { - ma_lpf_process_pcm_frame_s16(&pResampler->lpf, pResampler->x1.s16, pResampler->x1.s16); - } - - framesProcessedIn += 1; - pResampler->inTimeInt -= 1; - } - - if (pResampler->inTimeInt > 0) { - break; /* Ran out of input data. */ - } - - /* Getting here means the frames have been loaded and filtered and we can generate the next output frame. */ - if (pFramesOutS16 != NULL) { - MA_ASSERT(pResampler->inTimeInt == 0); - ma_linear_resampler_interpolate_frame_s16(pResampler, pFramesOutS16); - - pFramesOutS16 += pResampler->config.channels; - } - - framesProcessedOut += 1; - - /* Advance time forward. */ - pResampler->inTimeInt += pResampler->inAdvanceInt; - pResampler->inTimeFrac += pResampler->inAdvanceFrac; - if (pResampler->inTimeFrac >= pResampler->config.sampleRateOut) { - pResampler->inTimeFrac -= pResampler->config.sampleRateOut; - pResampler->inTimeInt += 1; - } - } - - *pFrameCountIn = framesProcessedIn; - *pFrameCountOut = framesProcessedOut; - - return MA_SUCCESS; -} - -static ma_result ma_linear_resampler_process_pcm_frames_s16_upsample(ma_linear_resampler* pResampler, const void* pFramesIn, ma_uint64* pFrameCountIn, void* pFramesOut, ma_uint64* pFrameCountOut) -{ - const ma_int16* pFramesInS16; - /* */ ma_int16* pFramesOutS16; - ma_uint64 frameCountIn; - ma_uint64 frameCountOut; - ma_uint64 framesProcessedIn; - ma_uint64 framesProcessedOut; - - MA_ASSERT(pResampler != NULL); - MA_ASSERT(pFrameCountIn != NULL); - MA_ASSERT(pFrameCountOut != NULL); - - pFramesInS16 = (const ma_int16*)pFramesIn; - pFramesOutS16 = ( ma_int16*)pFramesOut; - frameCountIn = *pFrameCountIn; - frameCountOut = *pFrameCountOut; - framesProcessedIn = 0; - framesProcessedOut = 0; - - while (framesProcessedOut < frameCountOut) { - /* Before interpolating we need to load the buffers. */ - while (pResampler->inTimeInt > 0 && frameCountIn > framesProcessedIn) { - ma_uint32 iChannel; - - if (pFramesInS16 != NULL) { - for (iChannel = 0; iChannel < pResampler->config.channels; iChannel += 1) { - pResampler->x0.s16[iChannel] = pResampler->x1.s16[iChannel]; - pResampler->x1.s16[iChannel] = pFramesInS16[iChannel]; - } - pFramesInS16 += pResampler->config.channels; - } else { - for (iChannel = 0; iChannel < pResampler->config.channels; iChannel += 1) { - pResampler->x0.s16[iChannel] = pResampler->x1.s16[iChannel]; - pResampler->x1.s16[iChannel] = 0; - } - } - - framesProcessedIn += 1; - pResampler->inTimeInt -= 1; - } - - if (pResampler->inTimeInt > 0) { - break; /* Ran out of input data. */ - } - - /* Getting here means the frames have been loaded and we can generate the next output frame. */ - if (pFramesOutS16 != NULL) { - MA_ASSERT(pResampler->inTimeInt == 0); - ma_linear_resampler_interpolate_frame_s16(pResampler, pFramesOutS16); - - /* Filter. Do not apply filtering if sample rates are the same or else you'll get dangerous glitching. */ - if (pResampler->config.sampleRateIn != pResampler->config.sampleRateOut) { - ma_lpf_process_pcm_frame_s16(&pResampler->lpf, pFramesOutS16, pFramesOutS16); - } - - pFramesOutS16 += pResampler->config.channels; - } - - framesProcessedOut += 1; - - /* Advance time forward. */ - pResampler->inTimeInt += pResampler->inAdvanceInt; - pResampler->inTimeFrac += pResampler->inAdvanceFrac; - if (pResampler->inTimeFrac >= pResampler->config.sampleRateOut) { - pResampler->inTimeFrac -= pResampler->config.sampleRateOut; - pResampler->inTimeInt += 1; - } - } - - *pFrameCountIn = framesProcessedIn; - *pFrameCountOut = framesProcessedOut; - - return MA_SUCCESS; -} - -static ma_result ma_linear_resampler_process_pcm_frames_s16(ma_linear_resampler* pResampler, const void* pFramesIn, ma_uint64* pFrameCountIn, void* pFramesOut, ma_uint64* pFrameCountOut) -{ - MA_ASSERT(pResampler != NULL); - - if (pResampler->config.sampleRateIn > pResampler->config.sampleRateOut) { - return ma_linear_resampler_process_pcm_frames_s16_downsample(pResampler, pFramesIn, pFrameCountIn, pFramesOut, pFrameCountOut); - } else { - return ma_linear_resampler_process_pcm_frames_s16_upsample(pResampler, pFramesIn, pFrameCountIn, pFramesOut, pFrameCountOut); - } -} - - -static ma_result ma_linear_resampler_process_pcm_frames_f32_downsample(ma_linear_resampler* pResampler, const void* pFramesIn, ma_uint64* pFrameCountIn, void* pFramesOut, ma_uint64* pFrameCountOut) -{ - const float* pFramesInF32; - /* */ float* pFramesOutF32; - ma_uint64 frameCountIn; - ma_uint64 frameCountOut; - ma_uint64 framesProcessedIn; - ma_uint64 framesProcessedOut; - - MA_ASSERT(pResampler != NULL); - MA_ASSERT(pFrameCountIn != NULL); - MA_ASSERT(pFrameCountOut != NULL); - - pFramesInF32 = (const float*)pFramesIn; - pFramesOutF32 = ( float*)pFramesOut; - frameCountIn = *pFrameCountIn; - frameCountOut = *pFrameCountOut; - framesProcessedIn = 0; - framesProcessedOut = 0; - - while (framesProcessedOut < frameCountOut) { - /* Before interpolating we need to load the buffers. When doing this we need to ensure we run every input sample through the filter. */ - while (pResampler->inTimeInt > 0 && frameCountIn > framesProcessedIn) { - ma_uint32 iChannel; - - if (pFramesInF32 != NULL) { - for (iChannel = 0; iChannel < pResampler->config.channels; iChannel += 1) { - pResampler->x0.f32[iChannel] = pResampler->x1.f32[iChannel]; - pResampler->x1.f32[iChannel] = pFramesInF32[iChannel]; - } - pFramesInF32 += pResampler->config.channels; - } else { - for (iChannel = 0; iChannel < pResampler->config.channels; iChannel += 1) { - pResampler->x0.f32[iChannel] = pResampler->x1.f32[iChannel]; - pResampler->x1.f32[iChannel] = 0; - } - } - - /* Filter. Do not apply filtering if sample rates are the same or else you'll get dangerous glitching. */ - if (pResampler->config.sampleRateIn != pResampler->config.sampleRateOut) { - ma_lpf_process_pcm_frame_f32(&pResampler->lpf, pResampler->x1.f32, pResampler->x1.f32); - } - - framesProcessedIn += 1; - pResampler->inTimeInt -= 1; - } - - if (pResampler->inTimeInt > 0) { - break; /* Ran out of input data. */ - } - - /* Getting here means the frames have been loaded and filtered and we can generate the next output frame. */ - if (pFramesOutF32 != NULL) { - MA_ASSERT(pResampler->inTimeInt == 0); - ma_linear_resampler_interpolate_frame_f32(pResampler, pFramesOutF32); - - pFramesOutF32 += pResampler->config.channels; - } - - framesProcessedOut += 1; - - /* Advance time forward. */ - pResampler->inTimeInt += pResampler->inAdvanceInt; - pResampler->inTimeFrac += pResampler->inAdvanceFrac; - if (pResampler->inTimeFrac >= pResampler->config.sampleRateOut) { - pResampler->inTimeFrac -= pResampler->config.sampleRateOut; - pResampler->inTimeInt += 1; - } - } - - *pFrameCountIn = framesProcessedIn; - *pFrameCountOut = framesProcessedOut; - - return MA_SUCCESS; -} - -static ma_result ma_linear_resampler_process_pcm_frames_f32_upsample(ma_linear_resampler* pResampler, const void* pFramesIn, ma_uint64* pFrameCountIn, void* pFramesOut, ma_uint64* pFrameCountOut) -{ - const float* pFramesInF32; - /* */ float* pFramesOutF32; - ma_uint64 frameCountIn; - ma_uint64 frameCountOut; - ma_uint64 framesProcessedIn; - ma_uint64 framesProcessedOut; - - MA_ASSERT(pResampler != NULL); - MA_ASSERT(pFrameCountIn != NULL); - MA_ASSERT(pFrameCountOut != NULL); - - pFramesInF32 = (const float*)pFramesIn; - pFramesOutF32 = ( float*)pFramesOut; - frameCountIn = *pFrameCountIn; - frameCountOut = *pFrameCountOut; - framesProcessedIn = 0; - framesProcessedOut = 0; - - while (framesProcessedOut < frameCountOut) { - /* Before interpolating we need to load the buffers. */ - while (pResampler->inTimeInt > 0 && frameCountIn > framesProcessedIn) { - ma_uint32 iChannel; - - if (pFramesInF32 != NULL) { - for (iChannel = 0; iChannel < pResampler->config.channels; iChannel += 1) { - pResampler->x0.f32[iChannel] = pResampler->x1.f32[iChannel]; - pResampler->x1.f32[iChannel] = pFramesInF32[iChannel]; - } - pFramesInF32 += pResampler->config.channels; - } else { - for (iChannel = 0; iChannel < pResampler->config.channels; iChannel += 1) { - pResampler->x0.f32[iChannel] = pResampler->x1.f32[iChannel]; - pResampler->x1.f32[iChannel] = 0; - } - } - - framesProcessedIn += 1; - pResampler->inTimeInt -= 1; - } - - if (pResampler->inTimeInt > 0) { - break; /* Ran out of input data. */ - } - - /* Getting here means the frames have been loaded and we can generate the next output frame. */ - if (pFramesOutF32 != NULL) { - MA_ASSERT(pResampler->inTimeInt == 0); - ma_linear_resampler_interpolate_frame_f32(pResampler, pFramesOutF32); - - /* Filter. Do not apply filtering if sample rates are the same or else you'll get dangerous glitching. */ - if (pResampler->config.sampleRateIn != pResampler->config.sampleRateOut) { - ma_lpf_process_pcm_frame_f32(&pResampler->lpf, pFramesOutF32, pFramesOutF32); - } - - pFramesOutF32 += pResampler->config.channels; - } - - framesProcessedOut += 1; - - /* Advance time forward. */ - pResampler->inTimeInt += pResampler->inAdvanceInt; - pResampler->inTimeFrac += pResampler->inAdvanceFrac; - if (pResampler->inTimeFrac >= pResampler->config.sampleRateOut) { - pResampler->inTimeFrac -= pResampler->config.sampleRateOut; - pResampler->inTimeInt += 1; - } - } - - *pFrameCountIn = framesProcessedIn; - *pFrameCountOut = framesProcessedOut; - - return MA_SUCCESS; -} - -static ma_result ma_linear_resampler_process_pcm_frames_f32(ma_linear_resampler* pResampler, const void* pFramesIn, ma_uint64* pFrameCountIn, void* pFramesOut, ma_uint64* pFrameCountOut) -{ - MA_ASSERT(pResampler != NULL); - - if (pResampler->config.sampleRateIn > pResampler->config.sampleRateOut) { - return ma_linear_resampler_process_pcm_frames_f32_downsample(pResampler, pFramesIn, pFrameCountIn, pFramesOut, pFrameCountOut); - } else { - return ma_linear_resampler_process_pcm_frames_f32_upsample(pResampler, pFramesIn, pFrameCountIn, pFramesOut, pFrameCountOut); - } -} - - -MA_API ma_result ma_linear_resampler_process_pcm_frames(ma_linear_resampler* pResampler, const void* pFramesIn, ma_uint64* pFrameCountIn, void* pFramesOut, ma_uint64* pFrameCountOut) -{ - if (pResampler == NULL) { - return MA_INVALID_ARGS; - } - - /* */ if (pResampler->config.format == ma_format_s16) { - return ma_linear_resampler_process_pcm_frames_s16(pResampler, pFramesIn, pFrameCountIn, pFramesOut, pFrameCountOut); - } else if (pResampler->config.format == ma_format_f32) { - return ma_linear_resampler_process_pcm_frames_f32(pResampler, pFramesIn, pFrameCountIn, pFramesOut, pFrameCountOut); - } else { - /* Should never get here. Getting here means the format is not supported and you didn't check the return value of ma_linear_resampler_init(). */ - MA_ASSERT(MA_FALSE); - return MA_INVALID_ARGS; - } -} - - -MA_API ma_result ma_linear_resampler_set_rate(ma_linear_resampler* pResampler, ma_uint32 sampleRateIn, ma_uint32 sampleRateOut) -{ - return ma_linear_resampler_set_rate_internal(pResampler, NULL, NULL, sampleRateIn, sampleRateOut, /* isResamplerAlreadyInitialized = */ MA_TRUE); -} - -MA_API ma_result ma_linear_resampler_set_rate_ratio(ma_linear_resampler* pResampler, float ratioInOut) -{ - ma_uint32 n; - ma_uint32 d; - - if (pResampler == NULL) { - return MA_INVALID_ARGS; - } - - if (ratioInOut <= 0) { - return MA_INVALID_ARGS; - } - - d = 1000000; - n = (ma_uint32)(ratioInOut * d); - - if (n == 0) { - return MA_INVALID_ARGS; /* Ratio too small. */ - } - - MA_ASSERT(n != 0); - - return ma_linear_resampler_set_rate(pResampler, n, d); -} - -MA_API ma_uint64 ma_linear_resampler_get_input_latency(const ma_linear_resampler* pResampler) -{ - if (pResampler == NULL) { - return 0; - } - - return 1 + ma_lpf_get_latency(&pResampler->lpf); -} - -MA_API ma_uint64 ma_linear_resampler_get_output_latency(const ma_linear_resampler* pResampler) -{ - if (pResampler == NULL) { - return 0; - } - - return ma_linear_resampler_get_input_latency(pResampler) * pResampler->config.sampleRateOut / pResampler->config.sampleRateIn; -} - -MA_API ma_result ma_linear_resampler_get_required_input_frame_count(const ma_linear_resampler* pResampler, ma_uint64 outputFrameCount, ma_uint64* pInputFrameCount) -{ - ma_uint64 inputFrameCount; - - if (pInputFrameCount == NULL) { - return MA_INVALID_ARGS; - } - - *pInputFrameCount = 0; - - if (pResampler == NULL) { - return MA_INVALID_ARGS; - } - - if (outputFrameCount == 0) { - return MA_SUCCESS; - } - - /* Any whole input frames are consumed before the first output frame is generated. */ - inputFrameCount = pResampler->inTimeInt; - outputFrameCount -= 1; - - /* The rest of the output frames can be calculated in constant time. */ - inputFrameCount += outputFrameCount * pResampler->inAdvanceInt; - inputFrameCount += (pResampler->inTimeFrac + (outputFrameCount * pResampler->inAdvanceFrac)) / pResampler->config.sampleRateOut; - - *pInputFrameCount = inputFrameCount; - - return MA_SUCCESS; -} - -MA_API ma_result ma_linear_resampler_get_expected_output_frame_count(const ma_linear_resampler* pResampler, ma_uint64 inputFrameCount, ma_uint64* pOutputFrameCount) -{ - ma_uint64 outputFrameCount; - ma_uint64 preliminaryInputFrameCountFromFrac; - ma_uint64 preliminaryInputFrameCount; - - if (pOutputFrameCount == NULL) { - return MA_INVALID_ARGS; - } - - *pOutputFrameCount = 0; - - if (pResampler == NULL) { - return MA_INVALID_ARGS; - } - - /* - The first step is to get a preliminary output frame count. This will either be exactly equal to what we need, or less by 1. We need to - determine how many input frames will be consumed by this value. If it's greater than our original input frame count it means we won't - be able to generate an extra frame because we will have run out of input data. Otherwise we will have enough input for the generation - of an extra output frame. This add-by-one logic is necessary due to how the data loading logic works when processing frames. - */ - outputFrameCount = (inputFrameCount * pResampler->config.sampleRateOut) / pResampler->config.sampleRateIn; - - /* - We need to determine how many *whole* input frames will have been processed to generate our preliminary output frame count. This is - used in the logic below to determine whether or not we need to add an extra output frame. - */ - preliminaryInputFrameCountFromFrac = (pResampler->inTimeFrac + outputFrameCount*pResampler->inAdvanceFrac) / pResampler->config.sampleRateOut; - preliminaryInputFrameCount = (pResampler->inTimeInt + outputFrameCount*pResampler->inAdvanceInt ) + preliminaryInputFrameCountFromFrac; - - /* - If the total number of *whole* input frames that would be required to generate our preliminary output frame count is greater than - the amount of whole input frames we have available as input we need to *not* add an extra output frame as there won't be enough data - to actually process. Otherwise we need to add the extra output frame. - */ - if (preliminaryInputFrameCount <= inputFrameCount) { - outputFrameCount += 1; - } - - *pOutputFrameCount = outputFrameCount; - - return MA_SUCCESS; -} - -MA_API ma_result ma_linear_resampler_reset(ma_linear_resampler* pResampler) -{ - ma_uint32 iChannel; - - if (pResampler == NULL) { - return MA_INVALID_ARGS; - } - - /* Timers need to be cleared back to zero. */ - pResampler->inTimeInt = 1; /* Set this to one to force an input sample to always be loaded for the first output frame. */ - pResampler->inTimeFrac = 0; - - /* Cached samples need to be cleared. */ - if (pResampler->config.format == ma_format_f32) { - for (iChannel = 0; iChannel < pResampler->config.channels; iChannel += 1) { - pResampler->x0.f32[iChannel] = 0; - pResampler->x1.f32[iChannel] = 0; - } - } else { - for (iChannel = 0; iChannel < pResampler->config.channels; iChannel += 1) { - pResampler->x0.s16[iChannel] = 0; - pResampler->x1.s16[iChannel] = 0; - } - } - - /* The low pass filter needs to have its cache reset. */ - ma_lpf_clear_cache(&pResampler->lpf); - - return MA_SUCCESS; -} - - - -/* Linear resampler backend vtable. */ -static ma_linear_resampler_config ma_resampling_backend_get_config__linear(const ma_resampler_config* pConfig) -{ - ma_linear_resampler_config linearConfig; - - linearConfig = ma_linear_resampler_config_init(pConfig->format, pConfig->channels, pConfig->sampleRateIn, pConfig->sampleRateOut); - linearConfig.lpfOrder = pConfig->linear.lpfOrder; - - return linearConfig; -} - -static ma_result ma_resampling_backend_get_heap_size__linear(void* pUserData, const ma_resampler_config* pConfig, size_t* pHeapSizeInBytes) -{ - ma_linear_resampler_config linearConfig; - - (void)pUserData; - - linearConfig = ma_resampling_backend_get_config__linear(pConfig); - - return ma_linear_resampler_get_heap_size(&linearConfig, pHeapSizeInBytes); -} - -static ma_result ma_resampling_backend_init__linear(void* pUserData, const ma_resampler_config* pConfig, void* pHeap, ma_resampling_backend** ppBackend) -{ - ma_resampler* pResampler = (ma_resampler*)pUserData; - ma_result result; - ma_linear_resampler_config linearConfig; - - (void)pUserData; - - linearConfig = ma_resampling_backend_get_config__linear(pConfig); - - result = ma_linear_resampler_init_preallocated(&linearConfig, pHeap, &pResampler->state.linear); - if (result != MA_SUCCESS) { - return result; - } - - *ppBackend = &pResampler->state.linear; - - return MA_SUCCESS; -} - -static void ma_resampling_backend_uninit__linear(void* pUserData, ma_resampling_backend* pBackend, const ma_allocation_callbacks* pAllocationCallbacks) -{ - (void)pUserData; - - ma_linear_resampler_uninit((ma_linear_resampler*)pBackend, pAllocationCallbacks); -} - -static ma_result ma_resampling_backend_process__linear(void* pUserData, ma_resampling_backend* pBackend, const void* pFramesIn, ma_uint64* pFrameCountIn, void* pFramesOut, ma_uint64* pFrameCountOut) -{ - (void)pUserData; - - return ma_linear_resampler_process_pcm_frames((ma_linear_resampler*)pBackend, pFramesIn, pFrameCountIn, pFramesOut, pFrameCountOut); -} - -static ma_result ma_resampling_backend_set_rate__linear(void* pUserData, ma_resampling_backend* pBackend, ma_uint32 sampleRateIn, ma_uint32 sampleRateOut) -{ - (void)pUserData; - - return ma_linear_resampler_set_rate((ma_linear_resampler*)pBackend, sampleRateIn, sampleRateOut); -} - -static ma_uint64 ma_resampling_backend_get_input_latency__linear(void* pUserData, const ma_resampling_backend* pBackend) -{ - (void)pUserData; - - return ma_linear_resampler_get_input_latency((const ma_linear_resampler*)pBackend); -} - -static ma_uint64 ma_resampling_backend_get_output_latency__linear(void* pUserData, const ma_resampling_backend* pBackend) -{ - (void)pUserData; - - return ma_linear_resampler_get_output_latency((const ma_linear_resampler*)pBackend); -} - -static ma_result ma_resampling_backend_get_required_input_frame_count__linear(void* pUserData, const ma_resampling_backend* pBackend, ma_uint64 outputFrameCount, ma_uint64* pInputFrameCount) -{ - (void)pUserData; - - return ma_linear_resampler_get_required_input_frame_count((const ma_linear_resampler*)pBackend, outputFrameCount, pInputFrameCount); -} - -static ma_result ma_resampling_backend_get_expected_output_frame_count__linear(void* pUserData, const ma_resampling_backend* pBackend, ma_uint64 inputFrameCount, ma_uint64* pOutputFrameCount) -{ - (void)pUserData; - - return ma_linear_resampler_get_expected_output_frame_count((const ma_linear_resampler*)pBackend, inputFrameCount, pOutputFrameCount); -} - -static ma_result ma_resampling_backend_reset__linear(void* pUserData, ma_resampling_backend* pBackend) -{ - (void)pUserData; - - return ma_linear_resampler_reset((ma_linear_resampler*)pBackend); -} - -static ma_resampling_backend_vtable g_ma_linear_resampler_vtable = -{ - ma_resampling_backend_get_heap_size__linear, - ma_resampling_backend_init__linear, - ma_resampling_backend_uninit__linear, - ma_resampling_backend_process__linear, - ma_resampling_backend_set_rate__linear, - ma_resampling_backend_get_input_latency__linear, - ma_resampling_backend_get_output_latency__linear, - ma_resampling_backend_get_required_input_frame_count__linear, - ma_resampling_backend_get_expected_output_frame_count__linear, - ma_resampling_backend_reset__linear -}; - - - -MA_API ma_resampler_config ma_resampler_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRateIn, ma_uint32 sampleRateOut, ma_resample_algorithm algorithm) -{ - ma_resampler_config config; - - MA_ZERO_OBJECT(&config); - config.format = format; - config.channels = channels; - config.sampleRateIn = sampleRateIn; - config.sampleRateOut = sampleRateOut; - config.algorithm = algorithm; - - /* Linear. */ - config.linear.lpfOrder = ma_min(MA_DEFAULT_RESAMPLER_LPF_ORDER, MA_MAX_FILTER_ORDER); - - return config; -} - -static ma_result ma_resampler_get_vtable(const ma_resampler_config* pConfig, ma_resampler* pResampler, ma_resampling_backend_vtable** ppVTable, void** ppUserData) -{ - MA_ASSERT(pConfig != NULL); - MA_ASSERT(ppVTable != NULL); - MA_ASSERT(ppUserData != NULL); - - /* Safety. */ - *ppVTable = NULL; - *ppUserData = NULL; - - switch (pConfig->algorithm) - { - case ma_resample_algorithm_linear: - { - *ppVTable = &g_ma_linear_resampler_vtable; - *ppUserData = pResampler; - } break; - - case ma_resample_algorithm_custom: - { - *ppVTable = pConfig->pBackendVTable; - *ppUserData = pConfig->pBackendUserData; - } break; - - default: return MA_INVALID_ARGS; - } - - return MA_SUCCESS; -} - -MA_API ma_result ma_resampler_get_heap_size(const ma_resampler_config* pConfig, size_t* pHeapSizeInBytes) -{ - ma_result result; - ma_resampling_backend_vtable* pVTable; - void* pVTableUserData; - - if (pHeapSizeInBytes == NULL) { - return MA_INVALID_ARGS; - } - - *pHeapSizeInBytes = 0; - - if (pConfig == NULL) { - return MA_INVALID_ARGS; - } - - result = ma_resampler_get_vtable(pConfig, NULL, &pVTable, &pVTableUserData); - if (result != MA_SUCCESS) { - return result; - } - - if (pVTable == NULL || pVTable->onGetHeapSize == NULL) { - return MA_NOT_IMPLEMENTED; - } - - result = pVTable->onGetHeapSize(pVTableUserData, pConfig, pHeapSizeInBytes); - if (result != MA_SUCCESS) { - return result; - } - - return MA_SUCCESS; -} - -MA_API ma_result ma_resampler_init_preallocated(const ma_resampler_config* pConfig, void* pHeap, ma_resampler* pResampler) -{ - ma_result result; - - if (pResampler == NULL) { - return MA_INVALID_ARGS; - } - - MA_ZERO_OBJECT(pResampler); - - if (pConfig == NULL) { - return MA_INVALID_ARGS; - } - - pResampler->_pHeap = pHeap; - pResampler->format = pConfig->format; - pResampler->channels = pConfig->channels; - pResampler->sampleRateIn = pConfig->sampleRateIn; - pResampler->sampleRateOut = pConfig->sampleRateOut; - - result = ma_resampler_get_vtable(pConfig, pResampler, &pResampler->pBackendVTable, &pResampler->pBackendUserData); - if (result != MA_SUCCESS) { - return result; - } - - if (pResampler->pBackendVTable == NULL || pResampler->pBackendVTable->onInit == NULL) { - return MA_NOT_IMPLEMENTED; /* onInit not implemented. */ - } - - result = pResampler->pBackendVTable->onInit(pResampler->pBackendUserData, pConfig, pHeap, &pResampler->pBackend); - if (result != MA_SUCCESS) { - return result; - } - - return MA_SUCCESS; -} - -MA_API ma_result ma_resampler_init(const ma_resampler_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_resampler* pResampler) -{ - ma_result result; - size_t heapSizeInBytes; - void* pHeap; - - result = ma_resampler_get_heap_size(pConfig, &heapSizeInBytes); - if (result != MA_SUCCESS) { - return result; - } - - if (heapSizeInBytes > 0) { - pHeap = ma_malloc(heapSizeInBytes, pAllocationCallbacks); - if (pHeap == NULL) { - return MA_OUT_OF_MEMORY; - } - } else { - pHeap = NULL; - } - - result = ma_resampler_init_preallocated(pConfig, pHeap, pResampler); - if (result != MA_SUCCESS) { - ma_free(pHeap, pAllocationCallbacks); - return result; - } - - pResampler->_ownsHeap = MA_TRUE; - return MA_SUCCESS; -} - -MA_API void ma_resampler_uninit(ma_resampler* pResampler, const ma_allocation_callbacks* pAllocationCallbacks) -{ - if (pResampler == NULL) { - return; - } - - if (pResampler->pBackendVTable == NULL || pResampler->pBackendVTable->onUninit == NULL) { - return; - } - - pResampler->pBackendVTable->onUninit(pResampler->pBackendUserData, pResampler->pBackend, pAllocationCallbacks); - - if (pResampler->_ownsHeap) { - ma_free(pResampler->_pHeap, pAllocationCallbacks); - } -} - -MA_API ma_result ma_resampler_process_pcm_frames(ma_resampler* pResampler, const void* pFramesIn, ma_uint64* pFrameCountIn, void* pFramesOut, ma_uint64* pFrameCountOut) -{ - if (pResampler == NULL) { - return MA_INVALID_ARGS; - } - - if (pFrameCountOut == NULL && pFrameCountIn == NULL) { - return MA_INVALID_ARGS; - } - - if (pResampler->pBackendVTable == NULL || pResampler->pBackendVTable->onProcess == NULL) { - return MA_NOT_IMPLEMENTED; - } - - return pResampler->pBackendVTable->onProcess(pResampler->pBackendUserData, pResampler->pBackend, pFramesIn, pFrameCountIn, pFramesOut, pFrameCountOut); -} - -MA_API ma_result ma_resampler_set_rate(ma_resampler* pResampler, ma_uint32 sampleRateIn, ma_uint32 sampleRateOut) -{ - ma_result result; - - if (pResampler == NULL) { - return MA_INVALID_ARGS; - } - - if (sampleRateIn == 0 || sampleRateOut == 0) { - return MA_INVALID_ARGS; - } - - if (pResampler->pBackendVTable == NULL || pResampler->pBackendVTable->onSetRate == NULL) { - return MA_NOT_IMPLEMENTED; - } - - result = pResampler->pBackendVTable->onSetRate(pResampler->pBackendUserData, pResampler->pBackend, sampleRateIn, sampleRateOut); - if (result != MA_SUCCESS) { - return result; - } - - pResampler->sampleRateIn = sampleRateIn; - pResampler->sampleRateOut = sampleRateOut; - - return MA_SUCCESS; -} - -MA_API ma_result ma_resampler_set_rate_ratio(ma_resampler* pResampler, float ratio) -{ - ma_uint32 n; - ma_uint32 d; - - if (pResampler == NULL) { - return MA_INVALID_ARGS; - } - - if (ratio <= 0) { - return MA_INVALID_ARGS; - } - - d = 1000; - n = (ma_uint32)(ratio * d); - - if (n == 0) { - return MA_INVALID_ARGS; /* Ratio too small. */ - } - - MA_ASSERT(n != 0); - - return ma_resampler_set_rate(pResampler, n, d); -} - -MA_API ma_uint64 ma_resampler_get_input_latency(const ma_resampler* pResampler) -{ - if (pResampler == NULL) { - return 0; - } - - if (pResampler->pBackendVTable == NULL || pResampler->pBackendVTable->onGetInputLatency == NULL) { - return 0; - } - - return pResampler->pBackendVTable->onGetInputLatency(pResampler->pBackendUserData, pResampler->pBackend); -} - -MA_API ma_uint64 ma_resampler_get_output_latency(const ma_resampler* pResampler) -{ - if (pResampler == NULL) { - return 0; - } - - if (pResampler->pBackendVTable == NULL || pResampler->pBackendVTable->onGetOutputLatency == NULL) { - return 0; - } - - return pResampler->pBackendVTable->onGetOutputLatency(pResampler->pBackendUserData, pResampler->pBackend); -} - -MA_API ma_result ma_resampler_get_required_input_frame_count(const ma_resampler* pResampler, ma_uint64 outputFrameCount, ma_uint64* pInputFrameCount) -{ - if (pInputFrameCount == NULL) { - return MA_INVALID_ARGS; - } - - *pInputFrameCount = 0; - - if (pResampler == NULL) { - return MA_INVALID_ARGS; - } - - if (pResampler->pBackendVTable == NULL || pResampler->pBackendVTable->onGetRequiredInputFrameCount == NULL) { - return MA_NOT_IMPLEMENTED; - } - - return pResampler->pBackendVTable->onGetRequiredInputFrameCount(pResampler->pBackendUserData, pResampler->pBackend, outputFrameCount, pInputFrameCount); -} - -MA_API ma_result ma_resampler_get_expected_output_frame_count(const ma_resampler* pResampler, ma_uint64 inputFrameCount, ma_uint64* pOutputFrameCount) -{ - if (pOutputFrameCount == NULL) { - return MA_INVALID_ARGS; - } - - *pOutputFrameCount = 0; - - if (pResampler == NULL) { - return MA_INVALID_ARGS; - } - - if (pResampler->pBackendVTable == NULL || pResampler->pBackendVTable->onGetExpectedOutputFrameCount == NULL) { - return MA_NOT_IMPLEMENTED; - } - - return pResampler->pBackendVTable->onGetExpectedOutputFrameCount(pResampler->pBackendUserData, pResampler->pBackend, inputFrameCount, pOutputFrameCount); -} - -MA_API ma_result ma_resampler_reset(ma_resampler* pResampler) -{ - if (pResampler == NULL) { - return MA_INVALID_ARGS; - } - - if (pResampler->pBackendVTable == NULL || pResampler->pBackendVTable->onReset == NULL) { - return MA_NOT_IMPLEMENTED; - } - - return pResampler->pBackendVTable->onReset(pResampler->pBackendUserData, pResampler->pBackend); -} - -/************************************************************************************************************************************************************** - -Channel Conversion - -**************************************************************************************************************************************************************/ -#ifndef MA_CHANNEL_CONVERTER_FIXED_POINT_SHIFT -#define MA_CHANNEL_CONVERTER_FIXED_POINT_SHIFT 12 -#endif - -#define MA_PLANE_LEFT 0 -#define MA_PLANE_RIGHT 1 -#define MA_PLANE_FRONT 2 -#define MA_PLANE_BACK 3 -#define MA_PLANE_BOTTOM 4 -#define MA_PLANE_TOP 5 - -static float g_maChannelPlaneRatios[MA_CHANNEL_POSITION_COUNT][6] = { - { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_NONE */ - { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_MONO */ - { 0.5f, 0.0f, 0.5f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_FRONT_LEFT */ - { 0.0f, 0.5f, 0.5f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_FRONT_RIGHT */ - { 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_FRONT_CENTER */ - { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_LFE */ - { 0.5f, 0.0f, 0.0f, 0.5f, 0.0f, 0.0f}, /* MA_CHANNEL_BACK_LEFT */ - { 0.0f, 0.5f, 0.0f, 0.5f, 0.0f, 0.0f}, /* MA_CHANNEL_BACK_RIGHT */ - { 0.25f, 0.0f, 0.75f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_FRONT_LEFT_CENTER */ - { 0.0f, 0.25f, 0.75f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_FRONT_RIGHT_CENTER */ - { 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f}, /* MA_CHANNEL_BACK_CENTER */ - { 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_SIDE_LEFT */ - { 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_SIDE_RIGHT */ - { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f}, /* MA_CHANNEL_TOP_CENTER */ - { 0.33f, 0.0f, 0.33f, 0.0f, 0.0f, 0.34f}, /* MA_CHANNEL_TOP_FRONT_LEFT */ - { 0.0f, 0.0f, 0.5f, 0.0f, 0.0f, 0.5f}, /* MA_CHANNEL_TOP_FRONT_CENTER */ - { 0.0f, 0.33f, 0.33f, 0.0f, 0.0f, 0.34f}, /* MA_CHANNEL_TOP_FRONT_RIGHT */ - { 0.33f, 0.0f, 0.0f, 0.33f, 0.0f, 0.34f}, /* MA_CHANNEL_TOP_BACK_LEFT */ - { 0.0f, 0.0f, 0.0f, 0.5f, 0.0f, 0.5f}, /* MA_CHANNEL_TOP_BACK_CENTER */ - { 0.0f, 0.33f, 0.0f, 0.33f, 0.0f, 0.34f}, /* MA_CHANNEL_TOP_BACK_RIGHT */ - { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_0 */ - { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_1 */ - { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_2 */ - { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_3 */ - { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_4 */ - { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_5 */ - { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_6 */ - { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_7 */ - { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_8 */ - { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_9 */ - { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_10 */ - { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_11 */ - { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_12 */ - { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_13 */ - { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_14 */ - { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_15 */ - { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_16 */ - { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_17 */ - { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_18 */ - { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_19 */ - { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_20 */ - { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_21 */ - { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_22 */ - { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_23 */ - { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_24 */ - { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_25 */ - { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_26 */ - { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_27 */ - { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_28 */ - { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_29 */ - { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_30 */ - { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_31 */ -}; - -static float ma_calculate_channel_position_rectangular_weight(ma_channel channelPositionA, ma_channel channelPositionB) -{ - /* - Imagine the following simplified example: You have a single input speaker which is the front/left speaker which you want to convert to - the following output configuration: - - - front/left - - side/left - - back/left - - The front/left output is easy - it the same speaker position so it receives the full contribution of the front/left input. The amount - of contribution to apply to the side/left and back/left speakers, however, is a bit more complicated. - - Imagine the front/left speaker as emitting audio from two planes - the front plane and the left plane. You can think of the front/left - speaker emitting half of its total volume from the front, and the other half from the left. Since part of its volume is being emitted - from the left side, and the side/left and back/left channels also emit audio from the left plane, one would expect that they would - receive some amount of contribution from front/left speaker. The amount of contribution depends on how many planes are shared between - the two speakers. Note that in the examples below I've added a top/front/left speaker as an example just to show how the math works - across 3 spatial dimensions. - - The first thing to do is figure out how each speaker's volume is spread over each of plane: - - front/left: 2 planes (front and left) = 1/2 = half its total volume on each plane - - side/left: 1 plane (left only) = 1/1 = entire volume from left plane - - back/left: 2 planes (back and left) = 1/2 = half its total volume on each plane - - top/front/left: 3 planes (top, front and left) = 1/3 = one third its total volume on each plane - - The amount of volume each channel contributes to each of its planes is what controls how much it is willing to given and take to other - channels on the same plane. The volume that is willing to the given by one channel is multiplied by the volume that is willing to be - taken by the other to produce the final contribution. - */ - - /* Contribution = Sum(Volume to Give * Volume to Take) */ - float contribution = - g_maChannelPlaneRatios[channelPositionA][0] * g_maChannelPlaneRatios[channelPositionB][0] + - g_maChannelPlaneRatios[channelPositionA][1] * g_maChannelPlaneRatios[channelPositionB][1] + - g_maChannelPlaneRatios[channelPositionA][2] * g_maChannelPlaneRatios[channelPositionB][2] + - g_maChannelPlaneRatios[channelPositionA][3] * g_maChannelPlaneRatios[channelPositionB][3] + - g_maChannelPlaneRatios[channelPositionA][4] * g_maChannelPlaneRatios[channelPositionB][4] + - g_maChannelPlaneRatios[channelPositionA][5] * g_maChannelPlaneRatios[channelPositionB][5]; - - return contribution; -} - -MA_API ma_channel_converter_config ma_channel_converter_config_init(ma_format format, ma_uint32 channelsIn, const ma_channel* pChannelMapIn, ma_uint32 channelsOut, const ma_channel* pChannelMapOut, ma_channel_mix_mode mixingMode) -{ - ma_channel_converter_config config; - - MA_ZERO_OBJECT(&config); - config.format = format; - config.channelsIn = channelsIn; - config.channelsOut = channelsOut; - config.pChannelMapIn = pChannelMapIn; - config.pChannelMapOut = pChannelMapOut; - config.mixingMode = mixingMode; - - return config; -} - -static ma_int32 ma_channel_converter_float_to_fixed(float x) -{ - return (ma_int32)(x * (1< 0); - - for (iChannel = 0; iChannel < channels; ++iChannel) { - if (ma_is_spatial_channel_position(ma_channel_map_get_channel(pChannelMap, channels, iChannel))) { - spatialChannelCount++; - } - } - - return spatialChannelCount; -} - -static ma_bool32 ma_is_spatial_channel_position(ma_channel channelPosition) -{ - int i; - - if (channelPosition == MA_CHANNEL_NONE || channelPosition == MA_CHANNEL_MONO || channelPosition == MA_CHANNEL_LFE) { - return MA_FALSE; - } - - if (channelPosition >= MA_CHANNEL_AUX_0 && channelPosition <= MA_CHANNEL_AUX_31) { - return MA_FALSE; - } - - for (i = 0; i < 6; ++i) { /* Each side of a cube. */ - if (g_maChannelPlaneRatios[channelPosition][i] != 0) { - return MA_TRUE; - } - } - - return MA_FALSE; -} - - -static ma_bool32 ma_channel_map_is_passthrough(const ma_channel* pChannelMapIn, ma_uint32 channelsIn, const ma_channel* pChannelMapOut, ma_uint32 channelsOut) -{ - if (channelsOut == channelsIn) { - return ma_channel_map_is_equal(pChannelMapOut, pChannelMapIn, channelsOut); - } else { - return MA_FALSE; /* Channel counts differ, so cannot be a passthrough. */ - } -} - -static ma_channel_conversion_path ma_channel_map_get_conversion_path(const ma_channel* pChannelMapIn, ma_uint32 channelsIn, const ma_channel* pChannelMapOut, ma_uint32 channelsOut, ma_channel_mix_mode mode) -{ - if (ma_channel_map_is_passthrough(pChannelMapIn, channelsIn, pChannelMapOut, channelsOut)) { - return ma_channel_conversion_path_passthrough; - } - - if (channelsOut == 1 && (pChannelMapOut == NULL || pChannelMapOut[0] == MA_CHANNEL_MONO)) { - return ma_channel_conversion_path_mono_out; - } - - if (channelsIn == 1 && (pChannelMapIn == NULL || pChannelMapIn[0] == MA_CHANNEL_MONO)) { - return ma_channel_conversion_path_mono_in; - } - - if (mode == ma_channel_mix_mode_custom_weights) { - return ma_channel_conversion_path_weights; - } - - /* - We can use a simple shuffle if both channel maps have the same channel count and all channel - positions are present in both. - */ - if (channelsIn == channelsOut) { - ma_uint32 iChannelIn; - ma_bool32 areAllChannelPositionsPresent = MA_TRUE; - for (iChannelIn = 0; iChannelIn < channelsIn; ++iChannelIn) { - ma_bool32 isInputChannelPositionInOutput = ma_channel_map_contains_channel_position(channelsOut, pChannelMapOut, ma_channel_map_get_channel(pChannelMapIn, channelsIn, iChannelIn)); - if (!isInputChannelPositionInOutput) { - areAllChannelPositionsPresent = MA_FALSE; - break; - } - } - - if (areAllChannelPositionsPresent) { - return ma_channel_conversion_path_shuffle; - } - } - - /* Getting here means we'll need to use weights. */ - return ma_channel_conversion_path_weights; -} - - -static ma_result ma_channel_map_build_shuffle_table(const ma_channel* pChannelMapIn, ma_uint32 channelCountIn, const ma_channel* pChannelMapOut, ma_uint32 channelCountOut, ma_uint8* pShuffleTable) -{ - ma_uint32 iChannelIn; - ma_uint32 iChannelOut; - - if (pShuffleTable == NULL || channelCountIn == 0 || channelCountOut == 0) { - return MA_INVALID_ARGS; - } - - /* - When building the shuffle table we just do a 1:1 mapping based on the first occurrence of a channel. If the - input channel has more than one occurrence of a channel position, the second one will be ignored. - */ - for (iChannelOut = 0; iChannelOut < channelCountOut; iChannelOut += 1) { - ma_channel channelOut; - - /* Default to MA_CHANNEL_INDEX_NULL so that if a mapping is not found it'll be set appropriately. */ - pShuffleTable[iChannelOut] = MA_CHANNEL_INDEX_NULL; - - channelOut = ma_channel_map_get_channel(pChannelMapOut, channelCountOut, iChannelOut); - for (iChannelIn = 0; iChannelIn < channelCountIn; iChannelIn += 1) { - ma_channel channelIn; - - channelIn = ma_channel_map_get_channel(pChannelMapIn, channelCountIn, iChannelIn); - if (channelOut == channelIn) { - pShuffleTable[iChannelOut] = (ma_uint8)iChannelIn; - break; - } - - /* - Getting here means the channels don't exactly match, but we are going to support some - relaxed matching for practicality. If, for example, there are two stereo channel maps, - but one uses front left/right and the other uses side left/right, it makes logical - sense to just map these. The way we'll do it is we'll check if there is a logical - corresponding mapping, and if so, apply it, but we will *not* break from the loop, - thereby giving the loop a chance to find an exact match later which will take priority. - */ - switch (channelOut) - { - /* Left channels. */ - case MA_CHANNEL_FRONT_LEFT: - case MA_CHANNEL_SIDE_LEFT: - { - switch (channelIn) { - case MA_CHANNEL_FRONT_LEFT: - case MA_CHANNEL_SIDE_LEFT: - { - pShuffleTable[iChannelOut] = (ma_uint8)iChannelIn; - } break; - } - } break; - - /* Right channels. */ - case MA_CHANNEL_FRONT_RIGHT: - case MA_CHANNEL_SIDE_RIGHT: - { - switch (channelIn) { - case MA_CHANNEL_FRONT_RIGHT: - case MA_CHANNEL_SIDE_RIGHT: - { - pShuffleTable[iChannelOut] = (ma_uint8)iChannelIn; - } break; - } - } break; - - default: break; - } - } - } - - return MA_SUCCESS; -} - - -static void ma_channel_map_apply_shuffle_table_u8(ma_uint8* pFramesOut, ma_uint32 channelsOut, const ma_uint8* pFramesIn, ma_uint32 channelsIn, ma_uint64 frameCount, const ma_uint8* pShuffleTable) -{ - ma_uint64 iFrame; - ma_uint32 iChannelOut; - - for (iFrame = 0; iFrame < frameCount; iFrame += 1) { - for (iChannelOut = 0; iChannelOut < channelsOut; iChannelOut += 1) { - ma_uint8 iChannelIn = pShuffleTable[iChannelOut]; - if (iChannelIn < channelsIn) { /* For safety, and to deal with MA_CHANNEL_INDEX_NULL. */ - pFramesOut[iChannelOut] = pFramesIn[iChannelIn]; - } else { - pFramesOut[iChannelOut] = 0; - } - } - - pFramesOut += channelsOut; - pFramesIn += channelsIn; - } -} - -static void ma_channel_map_apply_shuffle_table_s16(ma_int16* pFramesOut, ma_uint32 channelsOut, const ma_int16* pFramesIn, ma_uint32 channelsIn, ma_uint64 frameCount, const ma_uint8* pShuffleTable) -{ - ma_uint64 iFrame; - ma_uint32 iChannelOut; - - for (iFrame = 0; iFrame < frameCount; iFrame += 1) { - for (iChannelOut = 0; iChannelOut < channelsOut; iChannelOut += 1) { - ma_uint8 iChannelIn = pShuffleTable[iChannelOut]; - if (iChannelIn < channelsIn) { /* For safety, and to deal with MA_CHANNEL_INDEX_NULL. */ - pFramesOut[iChannelOut] = pFramesIn[iChannelIn]; - } else { - pFramesOut[iChannelOut] = 0; - } - } - - pFramesOut += channelsOut; - pFramesIn += channelsIn; - } -} - -static void ma_channel_map_apply_shuffle_table_s24(ma_uint8* pFramesOut, ma_uint32 channelsOut, const ma_uint8* pFramesIn, ma_uint32 channelsIn, ma_uint64 frameCount, const ma_uint8* pShuffleTable) -{ - ma_uint64 iFrame; - ma_uint32 iChannelOut; - - for (iFrame = 0; iFrame < frameCount; iFrame += 1) { - for (iChannelOut = 0; iChannelOut < channelsOut; iChannelOut += 1) { - ma_uint8 iChannelIn = pShuffleTable[iChannelOut]; - if (iChannelIn < channelsIn) { /* For safety, and to deal with MA_CHANNEL_INDEX_NULL. */ - pFramesOut[iChannelOut*3 + 0] = pFramesIn[iChannelIn*3 + 0]; - pFramesOut[iChannelOut*3 + 1] = pFramesIn[iChannelIn*3 + 1]; - pFramesOut[iChannelOut*3 + 2] = pFramesIn[iChannelIn*3 + 2]; - } else { - pFramesOut[iChannelOut*3 + 0] = 0; - } pFramesOut[iChannelOut*3 + 1] = 0; - } pFramesOut[iChannelOut*3 + 2] = 0; - - pFramesOut += channelsOut*3; - pFramesIn += channelsIn*3; - } -} - -static void ma_channel_map_apply_shuffle_table_s32(ma_int32* pFramesOut, ma_uint32 channelsOut, const ma_int32* pFramesIn, ma_uint32 channelsIn, ma_uint64 frameCount, const ma_uint8* pShuffleTable) -{ - ma_uint64 iFrame; - ma_uint32 iChannelOut; - - for (iFrame = 0; iFrame < frameCount; iFrame += 1) { - for (iChannelOut = 0; iChannelOut < channelsOut; iChannelOut += 1) { - ma_uint8 iChannelIn = pShuffleTable[iChannelOut]; - if (iChannelIn < channelsIn) { /* For safety, and to deal with MA_CHANNEL_INDEX_NULL. */ - pFramesOut[iChannelOut] = pFramesIn[iChannelIn]; - } else { - pFramesOut[iChannelOut] = 0; - } - } - - pFramesOut += channelsOut; - pFramesIn += channelsIn; - } -} - -static void ma_channel_map_apply_shuffle_table_f32(float* pFramesOut, ma_uint32 channelsOut, const float* pFramesIn, ma_uint32 channelsIn, ma_uint64 frameCount, const ma_uint8* pShuffleTable) -{ - ma_uint64 iFrame; - ma_uint32 iChannelOut; - - for (iFrame = 0; iFrame < frameCount; iFrame += 1) { - for (iChannelOut = 0; iChannelOut < channelsOut; iChannelOut += 1) { - ma_uint8 iChannelIn = pShuffleTable[iChannelOut]; - if (iChannelIn < channelsIn) { /* For safety, and to deal with MA_CHANNEL_INDEX_NULL. */ - pFramesOut[iChannelOut] = pFramesIn[iChannelIn]; - } else { - pFramesOut[iChannelOut] = 0; - } - } - - pFramesOut += channelsOut; - pFramesIn += channelsIn; - } -} - -static ma_result ma_channel_map_apply_shuffle_table(void* pFramesOut, ma_uint32 channelsOut, const void* pFramesIn, ma_uint32 channelsIn, ma_uint64 frameCount, const ma_uint8* pShuffleTable, ma_format format) -{ - if (pFramesOut == NULL || pFramesIn == NULL || channelsOut == 0 || pShuffleTable == NULL) { - return MA_INVALID_ARGS; - } - - switch (format) - { - case ma_format_u8: - { - ma_channel_map_apply_shuffle_table_u8((ma_uint8*)pFramesOut, channelsOut, (const ma_uint8*)pFramesIn, channelsIn, frameCount, pShuffleTable); - } break; - - case ma_format_s16: - { - ma_channel_map_apply_shuffle_table_s16((ma_int16*)pFramesOut, channelsOut, (const ma_int16*)pFramesIn, channelsIn, frameCount, pShuffleTable); - } break; - - case ma_format_s24: - { - ma_channel_map_apply_shuffle_table_s24((ma_uint8*)pFramesOut, channelsOut, (const ma_uint8*)pFramesIn, channelsIn, frameCount, pShuffleTable); - } break; - - case ma_format_s32: - { - ma_channel_map_apply_shuffle_table_s32((ma_int32*)pFramesOut, channelsOut, (const ma_int32*)pFramesIn, channelsIn, frameCount, pShuffleTable); - } break; - - case ma_format_f32: - { - ma_channel_map_apply_shuffle_table_f32((float*)pFramesOut, channelsOut, (const float*)pFramesIn, channelsIn, frameCount, pShuffleTable); - } break; - - default: return MA_INVALID_ARGS; /* Unknown format. */ - } - - return MA_SUCCESS; -} - -static ma_result ma_channel_map_apply_mono_out_f32(float* pFramesOut, const float* pFramesIn, const ma_channel* pChannelMapIn, ma_uint32 channelsIn, ma_uint64 frameCount) -{ - ma_uint64 iFrame; - ma_uint32 iChannelIn; - ma_uint32 accumulationCount; - - if (pFramesOut == NULL || pFramesIn == NULL || channelsIn == 0) { - return MA_INVALID_ARGS; - } - - /* In this case the output stream needs to be the average of all channels, ignoring NONE. */ - - /* A quick pre-processing step to get the accumulation counter since we're ignoring NONE channels. */ - accumulationCount = 0; - for (iChannelIn = 0; iChannelIn < channelsIn; iChannelIn += 1) { - if (ma_channel_map_get_channel(pChannelMapIn, channelsIn, iChannelIn) != MA_CHANNEL_NONE) { - accumulationCount += 1; - } - } - - if (accumulationCount > 0) { /* <-- Prevent a division by zero. */ - for (iFrame = 0; iFrame < frameCount; iFrame += 1) { - float accumulation = 0; - - for (iChannelIn = 0; iChannelIn < channelsIn; iChannelIn += 1) { - ma_channel channelIn = ma_channel_map_get_channel(pChannelMapIn, channelsIn, iChannelIn); - if (channelIn != MA_CHANNEL_NONE) { - accumulation += pFramesIn[iChannelIn]; - } - } - - pFramesOut[0] = accumulation / accumulationCount; - pFramesOut += 1; - pFramesIn += channelsIn; - } - } else { - ma_silence_pcm_frames(pFramesOut, frameCount, ma_format_f32, 1); - } - - return MA_SUCCESS; -} - -static ma_result ma_channel_map_apply_mono_in_f32(float* MA_RESTRICT pFramesOut, const ma_channel* pChannelMapOut, ma_uint32 channelsOut, const float* MA_RESTRICT pFramesIn, ma_uint64 frameCount, ma_mono_expansion_mode monoExpansionMode) -{ - ma_uint64 iFrame; - ma_uint32 iChannelOut; - - if (pFramesOut == NULL || channelsOut == 0 || pFramesIn == NULL) { - return MA_INVALID_ARGS; - } - - /* Note that the MA_CHANNEL_NONE channel must be ignored in all cases. */ - switch (monoExpansionMode) - { - case ma_mono_expansion_mode_average: - { - float weight; - ma_uint32 validChannelCount = 0; - - for (iChannelOut = 0; iChannelOut < channelsOut; iChannelOut += 1) { - ma_channel channelOut = ma_channel_map_get_channel(pChannelMapOut, channelsOut, iChannelOut); - if (channelOut != MA_CHANNEL_NONE) { - validChannelCount += 1; - } - } - - weight = 1.0f / validChannelCount; - - for (iFrame = 0; iFrame < frameCount; iFrame += 1) { - for (iChannelOut = 0; iChannelOut < channelsOut; iChannelOut += 1) { - ma_channel channelOut = ma_channel_map_get_channel(pChannelMapOut, channelsOut, iChannelOut); - if (channelOut != MA_CHANNEL_NONE) { - pFramesOut[iChannelOut] = pFramesIn[0] * weight; - } - } - - pFramesOut += channelsOut; - pFramesIn += 1; - } - } break; - - case ma_mono_expansion_mode_stereo_only: - { - if (channelsOut >= 2) { - ma_uint32 iChannelLeft = (ma_uint32)-1; - ma_uint32 iChannelRight = (ma_uint32)-1; - - /* - We first need to find our stereo channels. We prefer front-left and front-right, but - if they're not available, we'll also try side-left and side-right. If neither are - available we'll fall through to the default case below. - */ - for (iChannelOut = 0; iChannelOut < channelsOut; iChannelOut += 1) { - ma_channel channelOut = ma_channel_map_get_channel(pChannelMapOut, channelsOut, iChannelOut); - if (channelOut == MA_CHANNEL_SIDE_LEFT) { - iChannelLeft = iChannelOut; - } - if (channelOut == MA_CHANNEL_SIDE_RIGHT) { - iChannelRight = iChannelOut; - } - } - - for (iChannelOut = 0; iChannelOut < channelsOut; iChannelOut += 1) { - ma_channel channelOut = ma_channel_map_get_channel(pChannelMapOut, channelsOut, iChannelOut); - if (channelOut == MA_CHANNEL_FRONT_LEFT) { - iChannelLeft = iChannelOut; - } - if (channelOut == MA_CHANNEL_FRONT_RIGHT) { - iChannelRight = iChannelOut; - } - } - - - if (iChannelLeft != (ma_uint32)-1 && iChannelRight != (ma_uint32)-1) { - /* We found our stereo channels so we can duplicate the signal across those channels. */ - for (iFrame = 0; iFrame < frameCount; iFrame += 1) { - for (iChannelOut = 0; iChannelOut < channelsOut; iChannelOut += 1) { - ma_channel channelOut = ma_channel_map_get_channel(pChannelMapOut, channelsOut, iChannelOut); - if (channelOut != MA_CHANNEL_NONE) { - if (iChannelOut == iChannelLeft || iChannelOut == iChannelRight) { - pFramesOut[iChannelOut] = pFramesIn[0]; - } else { - pFramesOut[iChannelOut] = 0.0f; - } - } - } - - pFramesOut += channelsOut; - pFramesIn += 1; - } - - break; /* Get out of the switch. */ - } else { - /* Fallthrough. Does not have left and right channels. */ - goto default_handler; - } - } else { - /* Fallthrough. Does not have stereo channels. */ - goto default_handler; - } - }; /* Fallthrough. See comments above. */ - - case ma_mono_expansion_mode_duplicate: - default: - { - default_handler: - { - if (channelsOut <= MA_MAX_CHANNELS) { - ma_bool32 hasEmptyChannel = MA_FALSE; - ma_channel channelPositions[MA_MAX_CHANNELS]; - for (iChannelOut = 0; iChannelOut < channelsOut; iChannelOut += 1) { - channelPositions[iChannelOut] = ma_channel_map_get_channel(pChannelMapOut, channelsOut, iChannelOut); - if (channelPositions[iChannelOut] == MA_CHANNEL_NONE) { - hasEmptyChannel = MA_TRUE; - } - } - - if (hasEmptyChannel == MA_FALSE) { - /* - Faster path when there's no MA_CHANNEL_NONE channel positions. This should hopefully - help the compiler with auto-vectorization.m - */ - if (channelsOut == 2) { - #if defined(MA_SUPPORT_SSE2) - if (ma_has_sse2()) { - /* We want to do two frames in each iteration. */ - ma_uint64 unrolledFrameCount = frameCount >> 1; - - for (iFrame = 0; iFrame < unrolledFrameCount; iFrame += 1) { - __m128 in0 = _mm_set1_ps(pFramesIn[iFrame*2 + 0]); - __m128 in1 = _mm_set1_ps(pFramesIn[iFrame*2 + 1]); - _mm_storeu_ps(&pFramesOut[iFrame*4 + 0], _mm_shuffle_ps(in0, in1, _MM_SHUFFLE(0, 0, 0, 0))); - } - - /* Tail. */ - iFrame = unrolledFrameCount << 1; - goto generic_on_fastpath; - } else - #endif - { - for (iFrame = 0; iFrame < frameCount; iFrame += 1) { - for (iChannelOut = 0; iChannelOut < 2; iChannelOut += 1) { - pFramesOut[iFrame*2 + iChannelOut] = pFramesIn[iFrame]; - } - } - } - } else if (channelsOut == 6) { - #if defined(MA_SUPPORT_SSE2) - if (ma_has_sse2()) { - /* We want to do two frames in each iteration so we can have a multiple of 4 samples. */ - ma_uint64 unrolledFrameCount = frameCount >> 1; - - for (iFrame = 0; iFrame < unrolledFrameCount; iFrame += 1) { - __m128 in0 = _mm_set1_ps(pFramesIn[iFrame*2 + 0]); - __m128 in1 = _mm_set1_ps(pFramesIn[iFrame*2 + 1]); - - _mm_storeu_ps(&pFramesOut[iFrame*12 + 0], in0); - _mm_storeu_ps(&pFramesOut[iFrame*12 + 4], _mm_shuffle_ps(in0, in1, _MM_SHUFFLE(0, 0, 0, 0))); - _mm_storeu_ps(&pFramesOut[iFrame*12 + 8], in1); - } - - /* Tail. */ - iFrame = unrolledFrameCount << 1; - goto generic_on_fastpath; - } else - #endif - { - for (iFrame = 0; iFrame < frameCount; iFrame += 1) { - for (iChannelOut = 0; iChannelOut < 6; iChannelOut += 1) { - pFramesOut[iFrame*6 + iChannelOut] = pFramesIn[iFrame]; - } - } - } - } else if (channelsOut == 8) { - #if defined(MA_SUPPORT_SSE2) - if (ma_has_sse2()) { - for (iFrame = 0; iFrame < frameCount; iFrame += 1) { - __m128 in = _mm_set1_ps(pFramesIn[iFrame]); - _mm_storeu_ps(&pFramesOut[iFrame*8 + 0], in); - _mm_storeu_ps(&pFramesOut[iFrame*8 + 4], in); - } - } else - #endif - { - for (iFrame = 0; iFrame < frameCount; iFrame += 1) { - for (iChannelOut = 0; iChannelOut < 8; iChannelOut += 1) { - pFramesOut[iFrame*8 + iChannelOut] = pFramesIn[iFrame]; - } - } - } - } else { - iFrame = 0; - - #if defined(MA_SUPPORT_SSE2) /* For silencing a warning with non-x86 builds. */ - generic_on_fastpath: - #endif - { - for (; iFrame < frameCount; iFrame += 1) { - for (iChannelOut = 0; iChannelOut < channelsOut; iChannelOut += 1) { - pFramesOut[iFrame*channelsOut + iChannelOut] = pFramesIn[iFrame]; - } - } - } - } - } else { - /* Slow path. Need to handle MA_CHANNEL_NONE. */ - for (iFrame = 0; iFrame < frameCount; iFrame += 1) { - for (iChannelOut = 0; iChannelOut < channelsOut; iChannelOut += 1) { - if (channelPositions[iChannelOut] != MA_CHANNEL_NONE) { - pFramesOut[iFrame*channelsOut + iChannelOut] = pFramesIn[iFrame]; - } - } - } - } - } else { - /* Slow path. Too many channels to store on the stack. */ - for (iFrame = 0; iFrame < frameCount; iFrame += 1) { - for (iChannelOut = 0; iChannelOut < channelsOut; iChannelOut += 1) { - ma_channel channelOut = ma_channel_map_get_channel(pChannelMapOut, channelsOut, iChannelOut); - if (channelOut != MA_CHANNEL_NONE) { - pFramesOut[iFrame*channelsOut + iChannelOut] = pFramesIn[iFrame]; - } - } - } - } - } - } break; - } - - return MA_SUCCESS; -} - -static void ma_channel_map_apply_f32(float* pFramesOut, const ma_channel* pChannelMapOut, ma_uint32 channelsOut, const float* pFramesIn, const ma_channel* pChannelMapIn, ma_uint32 channelsIn, ma_uint64 frameCount, ma_channel_mix_mode mode, ma_mono_expansion_mode monoExpansionMode) -{ - ma_channel_conversion_path conversionPath = ma_channel_map_get_conversion_path(pChannelMapIn, channelsIn, pChannelMapOut, channelsOut, mode); - - /* Optimized Path: Passthrough */ - if (conversionPath == ma_channel_conversion_path_passthrough) { - ma_copy_pcm_frames(pFramesOut, pFramesIn, frameCount, ma_format_f32, channelsOut); - return; - } - - /* Special Path: Mono Output. */ - if (conversionPath == ma_channel_conversion_path_mono_out) { - ma_channel_map_apply_mono_out_f32(pFramesOut, pFramesIn, pChannelMapIn, channelsIn, frameCount); - return; - } - - /* Special Path: Mono Input. */ - if (conversionPath == ma_channel_conversion_path_mono_in) { - ma_channel_map_apply_mono_in_f32(pFramesOut, pChannelMapOut, channelsOut, pFramesIn, frameCount, monoExpansionMode); - return; - } - - /* Getting here means we aren't running on an optimized conversion path. */ - if (channelsOut <= MA_MAX_CHANNELS) { - ma_result result; - - if (mode == ma_channel_mix_mode_simple) { - ma_channel shuffleTable[MA_MAX_CHANNELS]; - - result = ma_channel_map_build_shuffle_table(pChannelMapIn, channelsIn, pChannelMapOut, channelsOut, shuffleTable); - if (result != MA_SUCCESS) { - return; - } - - result = ma_channel_map_apply_shuffle_table(pFramesOut, channelsOut, pFramesIn, channelsIn, frameCount, shuffleTable, ma_format_f32); - if (result != MA_SUCCESS) { - return; - } - } else { - ma_uint32 iFrame; - ma_uint32 iChannelOut; - ma_uint32 iChannelIn; - float weights[32][32]; /* Do not use MA_MAX_CHANNELS here! */ - - /* - If we have a small enough number of channels, pre-compute the weights. Otherwise we'll just need to - fall back to a slower path because otherwise we'll run out of stack space. - */ - if (channelsIn <= ma_countof(weights) && channelsOut <= ma_countof(weights)) { - /* Pre-compute weights. */ - for (iChannelOut = 0; iChannelOut < channelsOut; iChannelOut += 1) { - ma_channel channelOut = ma_channel_map_get_channel(pChannelMapOut, channelsOut, iChannelOut); - for (iChannelIn = 0; iChannelIn < channelsIn; iChannelIn += 1) { - ma_channel channelIn = ma_channel_map_get_channel(pChannelMapIn, channelsIn, iChannelIn); - weights[iChannelOut][iChannelIn] = ma_calculate_channel_position_rectangular_weight(channelOut, channelIn); - } - } - - iFrame = 0; - - /* Experiment: Try an optimized unroll for some specific cases to see how it improves performance. RESULT: Good gains. */ - if (channelsOut == 8) { - /* Experiment 2: Expand the inner loop to see what kind of different it makes. RESULT: Small, but worthwhile gain. */ - if (channelsIn == 2) { - for (; iFrame < frameCount; iFrame += 1) { - float accumulation[8] = { 0, 0, 0, 0, 0, 0, 0, 0 }; - - accumulation[0] += pFramesIn[iFrame*2 + 0] * weights[0][0]; - accumulation[1] += pFramesIn[iFrame*2 + 0] * weights[1][0]; - accumulation[2] += pFramesIn[iFrame*2 + 0] * weights[2][0]; - accumulation[3] += pFramesIn[iFrame*2 + 0] * weights[3][0]; - accumulation[4] += pFramesIn[iFrame*2 + 0] * weights[4][0]; - accumulation[5] += pFramesIn[iFrame*2 + 0] * weights[5][0]; - accumulation[6] += pFramesIn[iFrame*2 + 0] * weights[6][0]; - accumulation[7] += pFramesIn[iFrame*2 + 0] * weights[7][0]; - - accumulation[0] += pFramesIn[iFrame*2 + 1] * weights[0][1]; - accumulation[1] += pFramesIn[iFrame*2 + 1] * weights[1][1]; - accumulation[2] += pFramesIn[iFrame*2 + 1] * weights[2][1]; - accumulation[3] += pFramesIn[iFrame*2 + 1] * weights[3][1]; - accumulation[4] += pFramesIn[iFrame*2 + 1] * weights[4][1]; - accumulation[5] += pFramesIn[iFrame*2 + 1] * weights[5][1]; - accumulation[6] += pFramesIn[iFrame*2 + 1] * weights[6][1]; - accumulation[7] += pFramesIn[iFrame*2 + 1] * weights[7][1]; - - pFramesOut[iFrame*8 + 0] = accumulation[0]; - pFramesOut[iFrame*8 + 1] = accumulation[1]; - pFramesOut[iFrame*8 + 2] = accumulation[2]; - pFramesOut[iFrame*8 + 3] = accumulation[3]; - pFramesOut[iFrame*8 + 4] = accumulation[4]; - pFramesOut[iFrame*8 + 5] = accumulation[5]; - pFramesOut[iFrame*8 + 6] = accumulation[6]; - pFramesOut[iFrame*8 + 7] = accumulation[7]; - } - } else { - /* When outputting to 8 channels, we can do everything in groups of two 4x SIMD operations. */ - for (; iFrame < frameCount; iFrame += 1) { - float accumulation[8] = { 0, 0, 0, 0, 0, 0, 0, 0 }; - - for (iChannelIn = 0; iChannelIn < channelsIn; iChannelIn += 1) { - accumulation[0] += pFramesIn[iFrame*channelsIn + iChannelIn] * weights[0][iChannelIn]; - accumulation[1] += pFramesIn[iFrame*channelsIn + iChannelIn] * weights[1][iChannelIn]; - accumulation[2] += pFramesIn[iFrame*channelsIn + iChannelIn] * weights[2][iChannelIn]; - accumulation[3] += pFramesIn[iFrame*channelsIn + iChannelIn] * weights[3][iChannelIn]; - accumulation[4] += pFramesIn[iFrame*channelsIn + iChannelIn] * weights[4][iChannelIn]; - accumulation[5] += pFramesIn[iFrame*channelsIn + iChannelIn] * weights[5][iChannelIn]; - accumulation[6] += pFramesIn[iFrame*channelsIn + iChannelIn] * weights[6][iChannelIn]; - accumulation[7] += pFramesIn[iFrame*channelsIn + iChannelIn] * weights[7][iChannelIn]; - } - - pFramesOut[iFrame*8 + 0] = accumulation[0]; - pFramesOut[iFrame*8 + 1] = accumulation[1]; - pFramesOut[iFrame*8 + 2] = accumulation[2]; - pFramesOut[iFrame*8 + 3] = accumulation[3]; - pFramesOut[iFrame*8 + 4] = accumulation[4]; - pFramesOut[iFrame*8 + 5] = accumulation[5]; - pFramesOut[iFrame*8 + 6] = accumulation[6]; - pFramesOut[iFrame*8 + 7] = accumulation[7]; - } - } - } else if (channelsOut == 6) { - /* - When outputting to 6 channels we unfortunately don't have a nice multiple of 4 to do 4x SIMD operations. Instead we'll - expand our weights and do two frames at a time. - */ - for (; iFrame < frameCount; iFrame += 1) { - float accumulation[12] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; - - for (iChannelIn = 0; iChannelIn < channelsIn; iChannelIn += 1) { - accumulation[0] += pFramesIn[iFrame*channelsIn + iChannelIn] * weights[0][iChannelIn]; - accumulation[1] += pFramesIn[iFrame*channelsIn + iChannelIn] * weights[1][iChannelIn]; - accumulation[2] += pFramesIn[iFrame*channelsIn + iChannelIn] * weights[2][iChannelIn]; - accumulation[3] += pFramesIn[iFrame*channelsIn + iChannelIn] * weights[3][iChannelIn]; - accumulation[4] += pFramesIn[iFrame*channelsIn + iChannelIn] * weights[4][iChannelIn]; - accumulation[5] += pFramesIn[iFrame*channelsIn + iChannelIn] * weights[5][iChannelIn]; - } - - pFramesOut[iFrame*6 + 0] = accumulation[0]; - pFramesOut[iFrame*6 + 1] = accumulation[1]; - pFramesOut[iFrame*6 + 2] = accumulation[2]; - pFramesOut[iFrame*6 + 3] = accumulation[3]; - pFramesOut[iFrame*6 + 4] = accumulation[4]; - pFramesOut[iFrame*6 + 5] = accumulation[5]; - } - } - - /* Leftover frames. */ - for (; iFrame < frameCount; iFrame += 1) { - for (iChannelOut = 0; iChannelOut < channelsOut; iChannelOut += 1) { - float accumulation = 0; - - for (iChannelIn = 0; iChannelIn < channelsIn; iChannelIn += 1) { - accumulation += pFramesIn[iFrame*channelsIn + iChannelIn] * weights[iChannelOut][iChannelIn]; - } - - pFramesOut[iFrame*channelsOut + iChannelOut] = accumulation; - } - } - } else { - /* Cannot pre-compute weights because not enough room in stack-allocated buffer. */ - for (iFrame = 0; iFrame < frameCount; iFrame += 1) { - for (iChannelOut = 0; iChannelOut < channelsOut; iChannelOut += 1) { - float accumulation = 0; - ma_channel channelOut = ma_channel_map_get_channel(pChannelMapOut, channelsOut, iChannelOut); - - for (iChannelIn = 0; iChannelIn < channelsIn; iChannelIn += 1) { - ma_channel channelIn = ma_channel_map_get_channel(pChannelMapIn, channelsIn, iChannelIn); - accumulation += pFramesIn[iFrame*channelsIn + iChannelIn] * ma_calculate_channel_position_rectangular_weight(channelOut, channelIn); - } - - pFramesOut[iFrame*channelsOut + iChannelOut] = accumulation; - } - } - } - } - } else { - /* Fall back to silence. If you hit this, what are you doing with so many channels?! */ - ma_silence_pcm_frames(pFramesOut, frameCount, ma_format_f32, channelsOut); - } -} - - -typedef struct -{ - size_t sizeInBytes; - size_t channelMapInOffset; - size_t channelMapOutOffset; - size_t shuffleTableOffset; - size_t weightsOffset; -} ma_channel_converter_heap_layout; - -static ma_channel_conversion_path ma_channel_converter_config_get_conversion_path(const ma_channel_converter_config* pConfig) -{ - return ma_channel_map_get_conversion_path(pConfig->pChannelMapIn, pConfig->channelsIn, pConfig->pChannelMapOut, pConfig->channelsOut, pConfig->mixingMode); -} - -static ma_result ma_channel_converter_get_heap_layout(const ma_channel_converter_config* pConfig, ma_channel_converter_heap_layout* pHeapLayout) -{ - ma_channel_conversion_path conversionPath; - - MA_ASSERT(pHeapLayout != NULL); - - if (pConfig == NULL) { - return MA_INVALID_ARGS; - } - - if (pConfig->channelsIn == 0 || pConfig->channelsOut == 0) { - return MA_INVALID_ARGS; - } - - if (!ma_channel_map_is_valid(pConfig->pChannelMapIn, pConfig->channelsIn)) { - return MA_INVALID_ARGS; - } - - if (!ma_channel_map_is_valid(pConfig->pChannelMapOut, pConfig->channelsOut)) { - return MA_INVALID_ARGS; - } - - pHeapLayout->sizeInBytes = 0; - - /* Input channel map. Only need to allocate this if we have an input channel map (otherwise default channel map is assumed). */ - pHeapLayout->channelMapInOffset = pHeapLayout->sizeInBytes; - if (pConfig->pChannelMapIn != NULL) { - pHeapLayout->sizeInBytes += sizeof(ma_channel) * pConfig->channelsIn; - } - - /* Output channel map. Only need to allocate this if we have an output channel map (otherwise default channel map is assumed). */ - pHeapLayout->channelMapOutOffset = pHeapLayout->sizeInBytes; - if (pConfig->pChannelMapOut != NULL) { - pHeapLayout->sizeInBytes += sizeof(ma_channel) * pConfig->channelsOut; - } - - /* Alignment for the next section. */ - pHeapLayout->sizeInBytes = ma_align_64(pHeapLayout->sizeInBytes); - - /* Whether or not we use weights of a shuffle table depends on the channel map themselves and the algorithm we've chosen. */ - conversionPath = ma_channel_converter_config_get_conversion_path(pConfig); - - /* Shuffle table */ - pHeapLayout->shuffleTableOffset = pHeapLayout->sizeInBytes; - if (conversionPath == ma_channel_conversion_path_shuffle) { - pHeapLayout->sizeInBytes += sizeof(ma_uint8) * pConfig->channelsOut; - } - - /* Weights */ - pHeapLayout->weightsOffset = pHeapLayout->sizeInBytes; - if (conversionPath == ma_channel_conversion_path_weights) { - pHeapLayout->sizeInBytes += sizeof(float*) * pConfig->channelsIn; - pHeapLayout->sizeInBytes += sizeof(float ) * pConfig->channelsIn * pConfig->channelsOut; - } - - /* Make sure allocation size is aligned. */ - pHeapLayout->sizeInBytes = ma_align_64(pHeapLayout->sizeInBytes); - - return MA_SUCCESS; -} - -MA_API ma_result ma_channel_converter_get_heap_size(const ma_channel_converter_config* pConfig, size_t* pHeapSizeInBytes) -{ - ma_result result; - ma_channel_converter_heap_layout heapLayout; - - if (pHeapSizeInBytes == NULL) { - return MA_INVALID_ARGS; - } - - *pHeapSizeInBytes = 0; - - result = ma_channel_converter_get_heap_layout(pConfig, &heapLayout); - if (result != MA_SUCCESS) { - return result; - } - - *pHeapSizeInBytes = heapLayout.sizeInBytes; - - return MA_SUCCESS; -} - -MA_API ma_result ma_channel_converter_init_preallocated(const ma_channel_converter_config* pConfig, void* pHeap, ma_channel_converter* pConverter) -{ - ma_result result; - ma_channel_converter_heap_layout heapLayout; - - if (pConverter == NULL) { - return MA_INVALID_ARGS; - } - - MA_ZERO_OBJECT(pConverter); - - result = ma_channel_converter_get_heap_layout(pConfig, &heapLayout); - if (result != MA_SUCCESS) { - return result; - } - - pConverter->_pHeap = pHeap; - MA_ZERO_MEMORY(pConverter->_pHeap, heapLayout.sizeInBytes); - - pConverter->format = pConfig->format; - pConverter->channelsIn = pConfig->channelsIn; - pConverter->channelsOut = pConfig->channelsOut; - pConverter->mixingMode = pConfig->mixingMode; - - if (pConfig->pChannelMapIn != NULL) { - pConverter->pChannelMapIn = (ma_channel*)ma_offset_ptr(pHeap, heapLayout.channelMapInOffset); - ma_channel_map_copy_or_default(pConverter->pChannelMapIn, pConfig->channelsIn, pConfig->pChannelMapIn, pConfig->channelsIn); - } else { - pConverter->pChannelMapIn = NULL; /* Use default channel map. */ - } - - if (pConfig->pChannelMapOut != NULL) { - pConverter->pChannelMapOut = (ma_channel*)ma_offset_ptr(pHeap, heapLayout.channelMapOutOffset); - ma_channel_map_copy_or_default(pConverter->pChannelMapOut, pConfig->channelsOut, pConfig->pChannelMapOut, pConfig->channelsOut); - } else { - pConverter->pChannelMapOut = NULL; /* Use default channel map. */ - } - - pConverter->conversionPath = ma_channel_converter_config_get_conversion_path(pConfig); - - if (pConverter->conversionPath == ma_channel_conversion_path_shuffle) { - pConverter->pShuffleTable = (ma_uint8*)ma_offset_ptr(pHeap, heapLayout.shuffleTableOffset); - ma_channel_map_build_shuffle_table(pConverter->pChannelMapIn, pConverter->channelsIn, pConverter->pChannelMapOut, pConverter->channelsOut, pConverter->pShuffleTable); - } - - if (pConverter->conversionPath == ma_channel_conversion_path_weights) { - ma_uint32 iChannelIn; - ma_uint32 iChannelOut; - - if (pConverter->format == ma_format_f32) { - pConverter->weights.f32 = (float** )ma_offset_ptr(pHeap, heapLayout.weightsOffset); - for (iChannelIn = 0; iChannelIn < pConverter->channelsIn; iChannelIn += 1) { - pConverter->weights.f32[iChannelIn] = (float*)ma_offset_ptr(pHeap, heapLayout.weightsOffset + ((sizeof(float*) * pConverter->channelsIn) + (sizeof(float) * pConverter->channelsOut * iChannelIn))); - } - } else { - pConverter->weights.s16 = (ma_int32**)ma_offset_ptr(pHeap, heapLayout.weightsOffset); - for (iChannelIn = 0; iChannelIn < pConverter->channelsIn; iChannelIn += 1) { - pConverter->weights.s16[iChannelIn] = (ma_int32*)ma_offset_ptr(pHeap, heapLayout.weightsOffset + ((sizeof(ma_int32*) * pConverter->channelsIn) + (sizeof(ma_int32) * pConverter->channelsOut * iChannelIn))); - } - } - - /* Silence our weights by default. */ - for (iChannelIn = 0; iChannelIn < pConverter->channelsIn; iChannelIn += 1) { - for (iChannelOut = 0; iChannelOut < pConverter->channelsOut; iChannelOut += 1) { - if (pConverter->format == ma_format_f32) { - pConverter->weights.f32[iChannelIn][iChannelOut] = 0.0f; - } else { - pConverter->weights.s16[iChannelIn][iChannelOut] = 0; - } - } - } - - /* - We now need to fill out our weights table. This is determined by the mixing mode. - */ - - /* In all cases we need to make sure all channels that are present in both channel maps have a 1:1 mapping. */ - for (iChannelIn = 0; iChannelIn < pConverter->channelsIn; ++iChannelIn) { - ma_channel channelPosIn = ma_channel_map_get_channel(pConverter->pChannelMapIn, pConverter->channelsIn, iChannelIn); - - for (iChannelOut = 0; iChannelOut < pConverter->channelsOut; ++iChannelOut) { - ma_channel channelPosOut = ma_channel_map_get_channel(pConverter->pChannelMapOut, pConverter->channelsOut, iChannelOut); - - if (channelPosIn == channelPosOut) { - float weight = 1; - - if (pConverter->format == ma_format_f32) { - pConverter->weights.f32[iChannelIn][iChannelOut] = weight; - } else { - pConverter->weights.s16[iChannelIn][iChannelOut] = ma_channel_converter_float_to_fixed(weight); - } - } - } - } - - switch (pConverter->mixingMode) - { - case ma_channel_mix_mode_custom_weights: - { - if (pConfig->ppWeights == NULL) { - return MA_INVALID_ARGS; /* Config specified a custom weights mixing mode, but no custom weights have been specified. */ - } - - for (iChannelIn = 0; iChannelIn < pConverter->channelsIn; iChannelIn += 1) { - for (iChannelOut = 0; iChannelOut < pConverter->channelsOut; iChannelOut += 1) { - float weight = pConfig->ppWeights[iChannelIn][iChannelOut]; - - if (pConverter->format == ma_format_f32) { - pConverter->weights.f32[iChannelIn][iChannelOut] = weight; - } else { - pConverter->weights.s16[iChannelIn][iChannelOut] = ma_channel_converter_float_to_fixed(weight); - } - } - } - } break; - - case ma_channel_mix_mode_simple: - { - /* - In simple mode, only set weights for channels that have exactly matching types, leave the rest at - zero. The 1:1 mappings have already been covered before this switch statement. - */ - } break; - - case ma_channel_mix_mode_rectangular: - default: - { - /* Unmapped input channels. */ - for (iChannelIn = 0; iChannelIn < pConverter->channelsIn; ++iChannelIn) { - ma_channel channelPosIn = ma_channel_map_get_channel(pConverter->pChannelMapIn, pConverter->channelsIn, iChannelIn); - - if (ma_is_spatial_channel_position(channelPosIn)) { - if (!ma_channel_map_contains_channel_position(pConverter->channelsOut, pConverter->pChannelMapOut, channelPosIn)) { - for (iChannelOut = 0; iChannelOut < pConverter->channelsOut; ++iChannelOut) { - ma_channel channelPosOut = ma_channel_map_get_channel(pConverter->pChannelMapOut, pConverter->channelsOut, iChannelOut); - - if (ma_is_spatial_channel_position(channelPosOut)) { - float weight = 0; - if (pConverter->mixingMode == ma_channel_mix_mode_rectangular) { - weight = ma_calculate_channel_position_rectangular_weight(channelPosIn, channelPosOut); - } - - /* Only apply the weight if we haven't already got some contribution from the respective channels. */ - if (pConverter->format == ma_format_f32) { - if (pConverter->weights.f32[iChannelIn][iChannelOut] == 0) { - pConverter->weights.f32[iChannelIn][iChannelOut] = weight; - } - } else { - if (pConverter->weights.s16[iChannelIn][iChannelOut] == 0) { - pConverter->weights.s16[iChannelIn][iChannelOut] = ma_channel_converter_float_to_fixed(weight); - } - } - } - } - } - } - } - - /* Unmapped output channels. */ - for (iChannelOut = 0; iChannelOut < pConverter->channelsOut; ++iChannelOut) { - ma_channel channelPosOut = ma_channel_map_get_channel(pConverter->pChannelMapOut, pConverter->channelsOut, iChannelOut); - - if (ma_is_spatial_channel_position(channelPosOut)) { - if (!ma_channel_map_contains_channel_position(pConverter->channelsIn, pConverter->pChannelMapIn, channelPosOut)) { - for (iChannelIn = 0; iChannelIn < pConverter->channelsIn; ++iChannelIn) { - ma_channel channelPosIn = ma_channel_map_get_channel(pConverter->pChannelMapIn, pConverter->channelsIn, iChannelIn); - - if (ma_is_spatial_channel_position(channelPosIn)) { - float weight = 0; - if (pConverter->mixingMode == ma_channel_mix_mode_rectangular) { - weight = ma_calculate_channel_position_rectangular_weight(channelPosIn, channelPosOut); - } - - /* Only apply the weight if we haven't already got some contribution from the respective channels. */ - if (pConverter->format == ma_format_f32) { - if (pConverter->weights.f32[iChannelIn][iChannelOut] == 0) { - pConverter->weights.f32[iChannelIn][iChannelOut] = weight; - } - } else { - if (pConverter->weights.s16[iChannelIn][iChannelOut] == 0) { - pConverter->weights.s16[iChannelIn][iChannelOut] = ma_channel_converter_float_to_fixed(weight); - } - } - } - } - } - } - } - - /* If LFE is in the output channel map but was not present in the input channel map, configure its weight now */ - if (pConfig->calculateLFEFromSpatialChannels) { - if (!ma_channel_map_contains_channel_position(pConverter->channelsIn, pConverter->pChannelMapIn, MA_CHANNEL_LFE)) { - ma_uint32 spatialChannelCount = ma_channel_map_get_spatial_channel_count(pConverter->pChannelMapIn, pConverter->channelsIn); - ma_uint32 iChannelOutLFE; - - if (spatialChannelCount > 0 && ma_channel_map_find_channel_position(pConverter->channelsOut, pConverter->pChannelMapOut, MA_CHANNEL_LFE, &iChannelOutLFE)) { - const float weightForLFE = 1.0f / spatialChannelCount; - for (iChannelIn = 0; iChannelIn < pConverter->channelsIn; ++iChannelIn) { - const ma_channel channelPosIn = ma_channel_map_get_channel(pConverter->pChannelMapIn, pConverter->channelsIn, iChannelIn); - if (ma_is_spatial_channel_position(channelPosIn)) { - if (pConverter->format == ma_format_f32) { - if (pConverter->weights.f32[iChannelIn][iChannelOutLFE] == 0) { - pConverter->weights.f32[iChannelIn][iChannelOutLFE] = weightForLFE; - } - } else { - if (pConverter->weights.s16[iChannelIn][iChannelOutLFE] == 0) { - pConverter->weights.s16[iChannelIn][iChannelOutLFE] = ma_channel_converter_float_to_fixed(weightForLFE); - } - } - } - } - } - } - } - } break; - } - } - - return MA_SUCCESS; -} - -MA_API ma_result ma_channel_converter_init(const ma_channel_converter_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_channel_converter* pConverter) -{ - ma_result result; - size_t heapSizeInBytes; - void* pHeap; - - result = ma_channel_converter_get_heap_size(pConfig, &heapSizeInBytes); - if (result != MA_SUCCESS) { - return result; - } - - if (heapSizeInBytes > 0) { - pHeap = ma_malloc(heapSizeInBytes, pAllocationCallbacks); - if (pHeap == NULL) { - return MA_OUT_OF_MEMORY; - } - } else { - pHeap = NULL; - } - - result = ma_channel_converter_init_preallocated(pConfig, pHeap, pConverter); - if (result != MA_SUCCESS) { - ma_free(pHeap, pAllocationCallbacks); - return result; - } - - pConverter->_ownsHeap = MA_TRUE; - return MA_SUCCESS; -} - -MA_API void ma_channel_converter_uninit(ma_channel_converter* pConverter, const ma_allocation_callbacks* pAllocationCallbacks) -{ - if (pConverter == NULL) { - return; - } - - if (pConverter->_ownsHeap) { - ma_free(pConverter->_pHeap, pAllocationCallbacks); - } -} - -static ma_result ma_channel_converter_process_pcm_frames__passthrough(ma_channel_converter* pConverter, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount) -{ - MA_ASSERT(pConverter != NULL); - MA_ASSERT(pFramesOut != NULL); - MA_ASSERT(pFramesIn != NULL); - - ma_copy_memory_64(pFramesOut, pFramesIn, frameCount * ma_get_bytes_per_frame(pConverter->format, pConverter->channelsOut)); - return MA_SUCCESS; -} - -static ma_result ma_channel_converter_process_pcm_frames__shuffle(ma_channel_converter* pConverter, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount) -{ - MA_ASSERT(pConverter != NULL); - MA_ASSERT(pFramesOut != NULL); - MA_ASSERT(pFramesIn != NULL); - MA_ASSERT(pConverter->channelsIn == pConverter->channelsOut); - - return ma_channel_map_apply_shuffle_table(pFramesOut, pConverter->channelsOut, pFramesIn, pConverter->channelsIn, frameCount, pConverter->pShuffleTable, pConverter->format); -} - -static ma_result ma_channel_converter_process_pcm_frames__mono_in(ma_channel_converter* pConverter, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount) -{ - ma_uint64 iFrame; - - MA_ASSERT(pConverter != NULL); - MA_ASSERT(pFramesOut != NULL); - MA_ASSERT(pFramesIn != NULL); - MA_ASSERT(pConverter->channelsIn == 1); - - switch (pConverter->format) - { - case ma_format_u8: - { - /* */ ma_uint8* pFramesOutU8 = ( ma_uint8*)pFramesOut; - const ma_uint8* pFramesInU8 = (const ma_uint8*)pFramesIn; - - for (iFrame = 0; iFrame < frameCount; ++iFrame) { - ma_uint32 iChannel; - for (iChannel = 0; iChannel < pConverter->channelsOut; iChannel += 1) { - pFramesOutU8[iFrame*pConverter->channelsOut + iChannel] = pFramesInU8[iFrame]; - } - } - } break; - - case ma_format_s16: - { - /* */ ma_int16* pFramesOutS16 = ( ma_int16*)pFramesOut; - const ma_int16* pFramesInS16 = (const ma_int16*)pFramesIn; - - if (pConverter->channelsOut == 2) { - for (iFrame = 0; iFrame < frameCount; ++iFrame) { - pFramesOutS16[iFrame*2 + 0] = pFramesInS16[iFrame]; - pFramesOutS16[iFrame*2 + 1] = pFramesInS16[iFrame]; - } - } else { - for (iFrame = 0; iFrame < frameCount; ++iFrame) { - ma_uint32 iChannel; - for (iChannel = 0; iChannel < pConverter->channelsOut; iChannel += 1) { - pFramesOutS16[iFrame*pConverter->channelsOut + iChannel] = pFramesInS16[iFrame]; - } - } - } - } break; - - case ma_format_s24: - { - /* */ ma_uint8* pFramesOutS24 = ( ma_uint8*)pFramesOut; - const ma_uint8* pFramesInS24 = (const ma_uint8*)pFramesIn; - - for (iFrame = 0; iFrame < frameCount; ++iFrame) { - ma_uint32 iChannel; - for (iChannel = 0; iChannel < pConverter->channelsOut; iChannel += 1) { - ma_uint64 iSampleOut = iFrame*pConverter->channelsOut + iChannel; - ma_uint64 iSampleIn = iFrame; - pFramesOutS24[iSampleOut*3 + 0] = pFramesInS24[iSampleIn*3 + 0]; - pFramesOutS24[iSampleOut*3 + 1] = pFramesInS24[iSampleIn*3 + 1]; - pFramesOutS24[iSampleOut*3 + 2] = pFramesInS24[iSampleIn*3 + 2]; - } - } - } break; - - case ma_format_s32: - { - /* */ ma_int32* pFramesOutS32 = ( ma_int32*)pFramesOut; - const ma_int32* pFramesInS32 = (const ma_int32*)pFramesIn; - - for (iFrame = 0; iFrame < frameCount; ++iFrame) { - ma_uint32 iChannel; - for (iChannel = 0; iChannel < pConverter->channelsOut; iChannel += 1) { - pFramesOutS32[iFrame*pConverter->channelsOut + iChannel] = pFramesInS32[iFrame]; - } - } - } break; - - case ma_format_f32: - { - /* */ float* pFramesOutF32 = ( float*)pFramesOut; - const float* pFramesInF32 = (const float*)pFramesIn; - - if (pConverter->channelsOut == 2) { - for (iFrame = 0; iFrame < frameCount; ++iFrame) { - pFramesOutF32[iFrame*2 + 0] = pFramesInF32[iFrame]; - pFramesOutF32[iFrame*2 + 1] = pFramesInF32[iFrame]; - } - } else { - for (iFrame = 0; iFrame < frameCount; ++iFrame) { - ma_uint32 iChannel; - for (iChannel = 0; iChannel < pConverter->channelsOut; iChannel += 1) { - pFramesOutF32[iFrame*pConverter->channelsOut + iChannel] = pFramesInF32[iFrame]; - } - } - } - } break; - - default: return MA_INVALID_OPERATION; /* Unknown format. */ - } - - return MA_SUCCESS; -} - -static ma_result ma_channel_converter_process_pcm_frames__mono_out(ma_channel_converter* pConverter, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount) -{ - ma_uint64 iFrame; - ma_uint32 iChannel; - - MA_ASSERT(pConverter != NULL); - MA_ASSERT(pFramesOut != NULL); - MA_ASSERT(pFramesIn != NULL); - MA_ASSERT(pConverter->channelsOut == 1); - - switch (pConverter->format) - { - case ma_format_u8: - { - /* */ ma_uint8* pFramesOutU8 = ( ma_uint8*)pFramesOut; - const ma_uint8* pFramesInU8 = (const ma_uint8*)pFramesIn; - - for (iFrame = 0; iFrame < frameCount; ++iFrame) { - ma_int32 t = 0; - for (iChannel = 0; iChannel < pConverter->channelsIn; iChannel += 1) { - t += ma_pcm_sample_u8_to_s16_no_scale(pFramesInU8[iFrame*pConverter->channelsIn + iChannel]); - } - - pFramesOutU8[iFrame] = ma_clip_u8(t / pConverter->channelsOut); - } - } break; - - case ma_format_s16: - { - /* */ ma_int16* pFramesOutS16 = ( ma_int16*)pFramesOut; - const ma_int16* pFramesInS16 = (const ma_int16*)pFramesIn; - - for (iFrame = 0; iFrame < frameCount; ++iFrame) { - ma_int32 t = 0; - for (iChannel = 0; iChannel < pConverter->channelsIn; iChannel += 1) { - t += pFramesInS16[iFrame*pConverter->channelsIn + iChannel]; - } - - pFramesOutS16[iFrame] = (ma_int16)(t / pConverter->channelsIn); - } - } break; - - case ma_format_s24: - { - /* */ ma_uint8* pFramesOutS24 = ( ma_uint8*)pFramesOut; - const ma_uint8* pFramesInS24 = (const ma_uint8*)pFramesIn; - - for (iFrame = 0; iFrame < frameCount; ++iFrame) { - ma_int64 t = 0; - for (iChannel = 0; iChannel < pConverter->channelsIn; iChannel += 1) { - t += ma_pcm_sample_s24_to_s32_no_scale(&pFramesInS24[(iFrame*pConverter->channelsIn + iChannel)*3]); - } - - ma_pcm_sample_s32_to_s24_no_scale(t / pConverter->channelsIn, &pFramesOutS24[iFrame*3]); - } - } break; - - case ma_format_s32: - { - /* */ ma_int32* pFramesOutS32 = ( ma_int32*)pFramesOut; - const ma_int32* pFramesInS32 = (const ma_int32*)pFramesIn; - - for (iFrame = 0; iFrame < frameCount; ++iFrame) { - ma_int64 t = 0; - for (iChannel = 0; iChannel < pConverter->channelsIn; iChannel += 1) { - t += pFramesInS32[iFrame*pConverter->channelsIn + iChannel]; - } - - pFramesOutS32[iFrame] = (ma_int32)(t / pConverter->channelsIn); - } - } break; - - case ma_format_f32: - { - /* */ float* pFramesOutF32 = ( float*)pFramesOut; - const float* pFramesInF32 = (const float*)pFramesIn; - - for (iFrame = 0; iFrame < frameCount; ++iFrame) { - float t = 0; - for (iChannel = 0; iChannel < pConverter->channelsIn; iChannel += 1) { - t += pFramesInF32[iFrame*pConverter->channelsIn + iChannel]; - } - - pFramesOutF32[iFrame] = t / pConverter->channelsIn; - } - } break; - - default: return MA_INVALID_OPERATION; /* Unknown format. */ - } - - return MA_SUCCESS; -} - -static ma_result ma_channel_converter_process_pcm_frames__weights(ma_channel_converter* pConverter, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount) -{ - ma_uint32 iFrame; - ma_uint32 iChannelIn; - ma_uint32 iChannelOut; - - MA_ASSERT(pConverter != NULL); - MA_ASSERT(pFramesOut != NULL); - MA_ASSERT(pFramesIn != NULL); - - /* This is the more complicated case. Each of the output channels is accumulated with 0 or more input channels. */ - - /* Clear. */ - ma_zero_memory_64(pFramesOut, frameCount * ma_get_bytes_per_frame(pConverter->format, pConverter->channelsOut)); - - /* Accumulate. */ - switch (pConverter->format) - { - case ma_format_u8: - { - /* */ ma_uint8* pFramesOutU8 = ( ma_uint8*)pFramesOut; - const ma_uint8* pFramesInU8 = (const ma_uint8*)pFramesIn; - - for (iFrame = 0; iFrame < frameCount; iFrame += 1) { - for (iChannelIn = 0; iChannelIn < pConverter->channelsIn; ++iChannelIn) { - for (iChannelOut = 0; iChannelOut < pConverter->channelsOut; ++iChannelOut) { - ma_int16 u8_O = ma_pcm_sample_u8_to_s16_no_scale(pFramesOutU8[iFrame*pConverter->channelsOut + iChannelOut]); - ma_int16 u8_I = ma_pcm_sample_u8_to_s16_no_scale(pFramesInU8 [iFrame*pConverter->channelsIn + iChannelIn ]); - ma_int32 s = (ma_int32)ma_clamp(u8_O + ((u8_I * pConverter->weights.s16[iChannelIn][iChannelOut]) >> MA_CHANNEL_CONVERTER_FIXED_POINT_SHIFT), -128, 127); - pFramesOutU8[iFrame*pConverter->channelsOut + iChannelOut] = ma_clip_u8((ma_int16)s); - } - } - } - } break; - - case ma_format_s16: - { - /* */ ma_int16* pFramesOutS16 = ( ma_int16*)pFramesOut; - const ma_int16* pFramesInS16 = (const ma_int16*)pFramesIn; - - for (iFrame = 0; iFrame < frameCount; iFrame += 1) { - for (iChannelIn = 0; iChannelIn < pConverter->channelsIn; ++iChannelIn) { - for (iChannelOut = 0; iChannelOut < pConverter->channelsOut; ++iChannelOut) { - ma_int32 s = pFramesOutS16[iFrame*pConverter->channelsOut + iChannelOut]; - s += (pFramesInS16[iFrame*pConverter->channelsIn + iChannelIn] * pConverter->weights.s16[iChannelIn][iChannelOut]) >> MA_CHANNEL_CONVERTER_FIXED_POINT_SHIFT; - - pFramesOutS16[iFrame*pConverter->channelsOut + iChannelOut] = (ma_int16)ma_clamp(s, -32768, 32767); - } - } - } - } break; - - case ma_format_s24: - { - /* */ ma_uint8* pFramesOutS24 = ( ma_uint8*)pFramesOut; - const ma_uint8* pFramesInS24 = (const ma_uint8*)pFramesIn; - - for (iFrame = 0; iFrame < frameCount; iFrame += 1) { - for (iChannelIn = 0; iChannelIn < pConverter->channelsIn; ++iChannelIn) { - for (iChannelOut = 0; iChannelOut < pConverter->channelsOut; ++iChannelOut) { - ma_int64 s24_O = ma_pcm_sample_s24_to_s32_no_scale(&pFramesOutS24[(iFrame*pConverter->channelsOut + iChannelOut)*3]); - ma_int64 s24_I = ma_pcm_sample_s24_to_s32_no_scale(&pFramesInS24 [(iFrame*pConverter->channelsIn + iChannelIn )*3]); - ma_int64 s24 = (ma_int32)ma_clamp(s24_O + ((s24_I * pConverter->weights.s16[iChannelIn][iChannelOut]) >> MA_CHANNEL_CONVERTER_FIXED_POINT_SHIFT), -8388608, 8388607); - ma_pcm_sample_s32_to_s24_no_scale(s24, &pFramesOutS24[(iFrame*pConverter->channelsOut + iChannelOut)*3]); - } - } - } - } break; - - case ma_format_s32: - { - /* */ ma_int32* pFramesOutS32 = ( ma_int32*)pFramesOut; - const ma_int32* pFramesInS32 = (const ma_int32*)pFramesIn; - - for (iFrame = 0; iFrame < frameCount; iFrame += 1) { - for (iChannelIn = 0; iChannelIn < pConverter->channelsIn; ++iChannelIn) { - for (iChannelOut = 0; iChannelOut < pConverter->channelsOut; ++iChannelOut) { - ma_int64 s = pFramesOutS32[iFrame*pConverter->channelsOut + iChannelOut]; - s += ((ma_int64)pFramesInS32[iFrame*pConverter->channelsIn + iChannelIn] * pConverter->weights.s16[iChannelIn][iChannelOut]) >> MA_CHANNEL_CONVERTER_FIXED_POINT_SHIFT; - - pFramesOutS32[iFrame*pConverter->channelsOut + iChannelOut] = ma_clip_s32(s); - } - } - } - } break; - - case ma_format_f32: - { - /* */ float* pFramesOutF32 = ( float*)pFramesOut; - const float* pFramesInF32 = (const float*)pFramesIn; - - for (iFrame = 0; iFrame < frameCount; iFrame += 1) { - for (iChannelIn = 0; iChannelIn < pConverter->channelsIn; ++iChannelIn) { - for (iChannelOut = 0; iChannelOut < pConverter->channelsOut; ++iChannelOut) { - pFramesOutF32[iFrame*pConverter->channelsOut + iChannelOut] += pFramesInF32[iFrame*pConverter->channelsIn + iChannelIn] * pConverter->weights.f32[iChannelIn][iChannelOut]; - } - } - } - } break; - - default: return MA_INVALID_OPERATION; /* Unknown format. */ - } - - return MA_SUCCESS; -} - -MA_API ma_result ma_channel_converter_process_pcm_frames(ma_channel_converter* pConverter, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount) -{ - if (pConverter == NULL) { - return MA_INVALID_ARGS; - } - - if (pFramesOut == NULL) { - return MA_INVALID_ARGS; - } - - if (pFramesIn == NULL) { - ma_zero_memory_64(pFramesOut, frameCount * ma_get_bytes_per_frame(pConverter->format, pConverter->channelsOut)); - return MA_SUCCESS; - } - - switch (pConverter->conversionPath) - { - case ma_channel_conversion_path_passthrough: return ma_channel_converter_process_pcm_frames__passthrough(pConverter, pFramesOut, pFramesIn, frameCount); - case ma_channel_conversion_path_mono_out: return ma_channel_converter_process_pcm_frames__mono_out(pConverter, pFramesOut, pFramesIn, frameCount); - case ma_channel_conversion_path_mono_in: return ma_channel_converter_process_pcm_frames__mono_in(pConverter, pFramesOut, pFramesIn, frameCount); - case ma_channel_conversion_path_shuffle: return ma_channel_converter_process_pcm_frames__shuffle(pConverter, pFramesOut, pFramesIn, frameCount); - case ma_channel_conversion_path_weights: - default: - { - return ma_channel_converter_process_pcm_frames__weights(pConverter, pFramesOut, pFramesIn, frameCount); - } - } -} - -MA_API ma_result ma_channel_converter_get_input_channel_map(const ma_channel_converter* pConverter, ma_channel* pChannelMap, size_t channelMapCap) -{ - if (pConverter == NULL || pChannelMap == NULL) { - return MA_INVALID_ARGS; - } - - ma_channel_map_copy_or_default(pChannelMap, channelMapCap, pConverter->pChannelMapIn, pConverter->channelsIn); - - return MA_SUCCESS; -} - -MA_API ma_result ma_channel_converter_get_output_channel_map(const ma_channel_converter* pConverter, ma_channel* pChannelMap, size_t channelMapCap) -{ - if (pConverter == NULL || pChannelMap == NULL) { - return MA_INVALID_ARGS; - } - - ma_channel_map_copy_or_default(pChannelMap, channelMapCap, pConverter->pChannelMapOut, pConverter->channelsOut); - - return MA_SUCCESS; -} - - -/************************************************************************************************************************************************************** - -Data Conversion - -**************************************************************************************************************************************************************/ -MA_API ma_data_converter_config ma_data_converter_config_init_default(void) -{ - ma_data_converter_config config; - MA_ZERO_OBJECT(&config); - - config.ditherMode = ma_dither_mode_none; - config.resampling.algorithm = ma_resample_algorithm_linear; - config.allowDynamicSampleRate = MA_FALSE; /* Disable dynamic sample rates by default because dynamic rate adjustments should be quite rare and it allows an optimization for cases when the in and out sample rates are the same. */ - - /* Linear resampling defaults. */ - config.resampling.linear.lpfOrder = 1; - - return config; -} - -MA_API ma_data_converter_config ma_data_converter_config_init(ma_format formatIn, ma_format formatOut, ma_uint32 channelsIn, ma_uint32 channelsOut, ma_uint32 sampleRateIn, ma_uint32 sampleRateOut) -{ - ma_data_converter_config config = ma_data_converter_config_init_default(); - config.formatIn = formatIn; - config.formatOut = formatOut; - config.channelsIn = channelsIn; - config.channelsOut = channelsOut; - config.sampleRateIn = sampleRateIn; - config.sampleRateOut = sampleRateOut; - - return config; -} - - -typedef struct -{ - size_t sizeInBytes; - size_t channelConverterOffset; - size_t resamplerOffset; -} ma_data_converter_heap_layout; - -static ma_bool32 ma_data_converter_config_is_resampler_required(const ma_data_converter_config* pConfig) -{ - MA_ASSERT(pConfig != NULL); - - return pConfig->allowDynamicSampleRate || pConfig->sampleRateIn != pConfig->sampleRateOut; -} - -static ma_format ma_data_converter_config_get_mid_format(const ma_data_converter_config* pConfig) -{ - MA_ASSERT(pConfig != NULL); - - /* - We want to avoid as much data conversion as possible. The channel converter and linear - resampler both support s16 and f32 natively. We need to decide on the format to use for this - stage. We call this the mid format because it's used in the middle stage of the conversion - pipeline. If the output format is either s16 or f32 we use that one. If that is not the case it - will do the same thing for the input format. If it's neither we just use f32. If we are using a - custom resampling backend, we can only guarantee that f32 will be supported so we'll be forced - to use that if resampling is required. - */ - if (ma_data_converter_config_is_resampler_required(pConfig) && pConfig->resampling.algorithm != ma_resample_algorithm_linear) { - return ma_format_f32; /* <-- Force f32 since that is the only one we can guarantee will be supported by the resampler. */ - } else { - /* */ if (pConfig->formatOut == ma_format_s16 || pConfig->formatOut == ma_format_f32) { - return pConfig->formatOut; - } else if (pConfig->formatIn == ma_format_s16 || pConfig->formatIn == ma_format_f32) { - return pConfig->formatIn; - } else { - return ma_format_f32; - } - } -} - -static ma_channel_converter_config ma_channel_converter_config_init_from_data_converter_config(const ma_data_converter_config* pConfig) -{ - ma_channel_converter_config channelConverterConfig; - - MA_ASSERT(pConfig != NULL); - - channelConverterConfig = ma_channel_converter_config_init(ma_data_converter_config_get_mid_format(pConfig), pConfig->channelsIn, pConfig->pChannelMapIn, pConfig->channelsOut, pConfig->pChannelMapOut, pConfig->channelMixMode); - channelConverterConfig.ppWeights = pConfig->ppChannelWeights; - channelConverterConfig.calculateLFEFromSpatialChannels = pConfig->calculateLFEFromSpatialChannels; - - return channelConverterConfig; -} - -static ma_resampler_config ma_resampler_config_init_from_data_converter_config(const ma_data_converter_config* pConfig) -{ - ma_resampler_config resamplerConfig; - ma_uint32 resamplerChannels; - - MA_ASSERT(pConfig != NULL); - - /* The resampler is the most expensive part of the conversion process, so we need to do it at the stage where the channel count is at it's lowest. */ - if (pConfig->channelsIn < pConfig->channelsOut) { - resamplerChannels = pConfig->channelsIn; - } else { - resamplerChannels = pConfig->channelsOut; - } - - resamplerConfig = ma_resampler_config_init(ma_data_converter_config_get_mid_format(pConfig), resamplerChannels, pConfig->sampleRateIn, pConfig->sampleRateOut, pConfig->resampling.algorithm); - resamplerConfig.linear = pConfig->resampling.linear; - resamplerConfig.pBackendVTable = pConfig->resampling.pBackendVTable; - resamplerConfig.pBackendUserData = pConfig->resampling.pBackendUserData; - - return resamplerConfig; -} - -static ma_result ma_data_converter_get_heap_layout(const ma_data_converter_config* pConfig, ma_data_converter_heap_layout* pHeapLayout) -{ - ma_result result; - - MA_ASSERT(pHeapLayout != NULL); - - MA_ZERO_OBJECT(pHeapLayout); - - if (pConfig == NULL) { - return MA_INVALID_ARGS; - } - - if (pConfig->channelsIn == 0 || pConfig->channelsOut == 0) { - return MA_INVALID_ARGS; - } - - pHeapLayout->sizeInBytes = 0; - - /* Channel converter. */ - pHeapLayout->channelConverterOffset = pHeapLayout->sizeInBytes; - { - size_t heapSizeInBytes; - ma_channel_converter_config channelConverterConfig = ma_channel_converter_config_init_from_data_converter_config(pConfig); - - result = ma_channel_converter_get_heap_size(&channelConverterConfig, &heapSizeInBytes); - if (result != MA_SUCCESS) { - return result; - } - - pHeapLayout->sizeInBytes += heapSizeInBytes; - } - - /* Resampler. */ - pHeapLayout->resamplerOffset = pHeapLayout->sizeInBytes; - if (ma_data_converter_config_is_resampler_required(pConfig)) { - size_t heapSizeInBytes; - ma_resampler_config resamplerConfig = ma_resampler_config_init_from_data_converter_config(pConfig); - - result = ma_resampler_get_heap_size(&resamplerConfig, &heapSizeInBytes); - if (result != MA_SUCCESS) { - return result; - } - - pHeapLayout->sizeInBytes += heapSizeInBytes; - } - - /* Make sure allocation size is aligned. */ - pHeapLayout->sizeInBytes = ma_align_64(pHeapLayout->sizeInBytes); - - return MA_SUCCESS; -} - -MA_API ma_result ma_data_converter_get_heap_size(const ma_data_converter_config* pConfig, size_t* pHeapSizeInBytes) -{ - ma_result result; - ma_data_converter_heap_layout heapLayout; - - if (pHeapSizeInBytes == NULL) { - return MA_INVALID_ARGS; - } - - *pHeapSizeInBytes = 0; - - result = ma_data_converter_get_heap_layout(pConfig, &heapLayout); - if (result != MA_SUCCESS) { - return result; - } - - *pHeapSizeInBytes = heapLayout.sizeInBytes; - - return MA_SUCCESS; -} - -MA_API ma_result ma_data_converter_init_preallocated(const ma_data_converter_config* pConfig, void* pHeap, ma_data_converter* pConverter) -{ - ma_result result; - ma_data_converter_heap_layout heapLayout; - ma_format midFormat; - ma_bool32 isResamplingRequired; - - if (pConverter == NULL) { - return MA_INVALID_ARGS; - } - - MA_ZERO_OBJECT(pConverter); - - result = ma_data_converter_get_heap_layout(pConfig, &heapLayout); - if (result != MA_SUCCESS) { - return result; - } - - pConverter->_pHeap = pHeap; - MA_ZERO_MEMORY(pHeap, heapLayout.sizeInBytes); - - pConverter->formatIn = pConfig->formatIn; - pConverter->formatOut = pConfig->formatOut; - pConverter->channelsIn = pConfig->channelsIn; - pConverter->channelsOut = pConfig->channelsOut; - pConverter->sampleRateIn = pConfig->sampleRateIn; - pConverter->sampleRateOut = pConfig->sampleRateOut; - pConverter->ditherMode = pConfig->ditherMode; - - /* - Determine if resampling is required. We need to do this so we can determine an appropriate - mid format to use. If resampling is required, the mid format must be ma_format_f32 since - that is the only one that is guaranteed to supported by custom resampling backends. - */ - isResamplingRequired = ma_data_converter_config_is_resampler_required(pConfig); - midFormat = ma_data_converter_config_get_mid_format(pConfig); - - - /* Channel converter. We always initialize this, but we check if it configures itself as a passthrough to determine whether or not it's needed. */ - { - ma_channel_converter_config channelConverterConfig = ma_channel_converter_config_init_from_data_converter_config(pConfig); - - result = ma_channel_converter_init_preallocated(&channelConverterConfig, ma_offset_ptr(pHeap, heapLayout.channelConverterOffset), &pConverter->channelConverter); - if (result != MA_SUCCESS) { - return result; - } - - /* If the channel converter is not a passthrough we need to enable it. Otherwise we can skip it. */ - if (pConverter->channelConverter.conversionPath != ma_channel_conversion_path_passthrough) { - pConverter->hasChannelConverter = MA_TRUE; - } - } - - - /* Resampler. */ - if (isResamplingRequired) { - ma_resampler_config resamplerConfig = ma_resampler_config_init_from_data_converter_config(pConfig); - - result = ma_resampler_init_preallocated(&resamplerConfig, ma_offset_ptr(pHeap, heapLayout.resamplerOffset), &pConverter->resampler); - if (result != MA_SUCCESS) { - return result; - } - - pConverter->hasResampler = MA_TRUE; - } - - - /* We can simplify pre- and post-format conversion if we have neither channel conversion nor resampling. */ - if (pConverter->hasChannelConverter == MA_FALSE && pConverter->hasResampler == MA_FALSE) { - /* We have neither channel conversion nor resampling so we'll only need one of pre- or post-format conversion, or none if the input and output formats are the same. */ - if (pConverter->formatIn == pConverter->formatOut) { - /* The formats are the same so we can just pass through. */ - pConverter->hasPreFormatConversion = MA_FALSE; - pConverter->hasPostFormatConversion = MA_FALSE; - } else { - /* The formats are different so we need to do either pre- or post-format conversion. It doesn't matter which. */ - pConverter->hasPreFormatConversion = MA_FALSE; - pConverter->hasPostFormatConversion = MA_TRUE; - } - } else { - /* We have a channel converter and/or resampler so we'll need channel conversion based on the mid format. */ - if (pConverter->formatIn != midFormat) { - pConverter->hasPreFormatConversion = MA_TRUE; - } - if (pConverter->formatOut != midFormat) { - pConverter->hasPostFormatConversion = MA_TRUE; - } - } - - /* We can enable passthrough optimizations if applicable. Note that we'll only be able to do this if the sample rate is static. */ - if (pConverter->hasPreFormatConversion == MA_FALSE && - pConverter->hasPostFormatConversion == MA_FALSE && - pConverter->hasChannelConverter == MA_FALSE && - pConverter->hasResampler == MA_FALSE) { - pConverter->isPassthrough = MA_TRUE; - } - - - /* We now need to determine our execution path. */ - if (pConverter->isPassthrough) { - pConverter->executionPath = ma_data_converter_execution_path_passthrough; - } else { - if (pConverter->channelsIn < pConverter->channelsOut) { - /* Do resampling first, if necessary. */ - MA_ASSERT(pConverter->hasChannelConverter == MA_TRUE); - - if (pConverter->hasResampler) { - pConverter->executionPath = ma_data_converter_execution_path_resample_first; - } else { - pConverter->executionPath = ma_data_converter_execution_path_channels_only; - } - } else { - /* Do channel conversion first, if necessary. */ - if (pConverter->hasChannelConverter) { - if (pConverter->hasResampler) { - pConverter->executionPath = ma_data_converter_execution_path_channels_first; - } else { - pConverter->executionPath = ma_data_converter_execution_path_channels_only; - } - } else { - /* Channel routing not required. */ - if (pConverter->hasResampler) { - pConverter->executionPath = ma_data_converter_execution_path_resample_only; - } else { - pConverter->executionPath = ma_data_converter_execution_path_format_only; - } - } - } - } - - return MA_SUCCESS; -} - -MA_API ma_result ma_data_converter_init(const ma_data_converter_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_data_converter* pConverter) -{ - ma_result result; - size_t heapSizeInBytes; - void* pHeap; - - result = ma_data_converter_get_heap_size(pConfig, &heapSizeInBytes); - if (result != MA_SUCCESS) { - return result; - } - - if (heapSizeInBytes > 0) { - pHeap = ma_malloc(heapSizeInBytes, pAllocationCallbacks); - if (pHeap == NULL) { - return MA_OUT_OF_MEMORY; - } - } else { - pHeap = NULL; - } - - result = ma_data_converter_init_preallocated(pConfig, pHeap, pConverter); - if (result != MA_SUCCESS) { - ma_free(pHeap, pAllocationCallbacks); - return result; - } - - pConverter->_ownsHeap = MA_TRUE; - return MA_SUCCESS; -} - -MA_API void ma_data_converter_uninit(ma_data_converter* pConverter, const ma_allocation_callbacks* pAllocationCallbacks) -{ - if (pConverter == NULL) { - return; - } - - if (pConverter->hasResampler) { - ma_resampler_uninit(&pConverter->resampler, pAllocationCallbacks); - } - - ma_channel_converter_uninit(&pConverter->channelConverter, pAllocationCallbacks); - - if (pConverter->_ownsHeap) { - ma_free(pConverter->_pHeap, pAllocationCallbacks); - } -} - -static ma_result ma_data_converter_process_pcm_frames__passthrough(ma_data_converter* pConverter, const void* pFramesIn, ma_uint64* pFrameCountIn, void* pFramesOut, ma_uint64* pFrameCountOut) -{ - ma_uint64 frameCountIn; - ma_uint64 frameCountOut; - ma_uint64 frameCount; - - MA_ASSERT(pConverter != NULL); - - frameCountIn = 0; - if (pFrameCountIn != NULL) { - frameCountIn = *pFrameCountIn; - } - - frameCountOut = 0; - if (pFrameCountOut != NULL) { - frameCountOut = *pFrameCountOut; - } - - frameCount = ma_min(frameCountIn, frameCountOut); - - if (pFramesOut != NULL) { - if (pFramesIn != NULL) { - ma_copy_memory_64(pFramesOut, pFramesIn, frameCount * ma_get_bytes_per_frame(pConverter->formatOut, pConverter->channelsOut)); - } else { - ma_zero_memory_64(pFramesOut, frameCount * ma_get_bytes_per_frame(pConverter->formatOut, pConverter->channelsOut)); - } - } - - if (pFrameCountIn != NULL) { - *pFrameCountIn = frameCount; - } - if (pFrameCountOut != NULL) { - *pFrameCountOut = frameCount; - } - - return MA_SUCCESS; -} - -static ma_result ma_data_converter_process_pcm_frames__format_only(ma_data_converter* pConverter, const void* pFramesIn, ma_uint64* pFrameCountIn, void* pFramesOut, ma_uint64* pFrameCountOut) -{ - ma_uint64 frameCountIn; - ma_uint64 frameCountOut; - ma_uint64 frameCount; - - MA_ASSERT(pConverter != NULL); - - frameCountIn = 0; - if (pFrameCountIn != NULL) { - frameCountIn = *pFrameCountIn; - } - - frameCountOut = 0; - if (pFrameCountOut != NULL) { - frameCountOut = *pFrameCountOut; - } - - frameCount = ma_min(frameCountIn, frameCountOut); - - if (pFramesOut != NULL) { - if (pFramesIn != NULL) { - ma_convert_pcm_frames_format(pFramesOut, pConverter->formatOut, pFramesIn, pConverter->formatIn, frameCount, pConverter->channelsIn, pConverter->ditherMode); - } else { - ma_zero_memory_64(pFramesOut, frameCount * ma_get_bytes_per_frame(pConverter->formatOut, pConverter->channelsOut)); - } - } - - if (pFrameCountIn != NULL) { - *pFrameCountIn = frameCount; - } - if (pFrameCountOut != NULL) { - *pFrameCountOut = frameCount; - } - - return MA_SUCCESS; -} - - -static ma_result ma_data_converter_process_pcm_frames__resample_with_format_conversion(ma_data_converter* pConverter, const void* pFramesIn, ma_uint64* pFrameCountIn, void* pFramesOut, ma_uint64* pFrameCountOut) -{ - ma_result result = MA_SUCCESS; - ma_uint64 frameCountIn; - ma_uint64 frameCountOut; - ma_uint64 framesProcessedIn; - ma_uint64 framesProcessedOut; - - MA_ASSERT(pConverter != NULL); - - frameCountIn = 0; - if (pFrameCountIn != NULL) { - frameCountIn = *pFrameCountIn; - } - - frameCountOut = 0; - if (pFrameCountOut != NULL) { - frameCountOut = *pFrameCountOut; - } - - framesProcessedIn = 0; - framesProcessedOut = 0; - - while (framesProcessedOut < frameCountOut) { - ma_uint8 pTempBufferOut[MA_DATA_CONVERTER_STACK_BUFFER_SIZE]; - const ma_uint32 tempBufferOutCap = sizeof(pTempBufferOut) / ma_get_bytes_per_frame(pConverter->resampler.format, pConverter->resampler.channels); - const void* pFramesInThisIteration; - /* */ void* pFramesOutThisIteration; - ma_uint64 frameCountInThisIteration; - ma_uint64 frameCountOutThisIteration; - - if (pFramesIn != NULL) { - pFramesInThisIteration = ma_offset_ptr(pFramesIn, framesProcessedIn * ma_get_bytes_per_frame(pConverter->formatIn, pConverter->channelsIn)); - } else { - pFramesInThisIteration = NULL; - } - - if (pFramesOut != NULL) { - pFramesOutThisIteration = ma_offset_ptr(pFramesOut, framesProcessedOut * ma_get_bytes_per_frame(pConverter->formatOut, pConverter->channelsOut)); - } else { - pFramesOutThisIteration = NULL; - } - - /* Do a pre format conversion if necessary. */ - if (pConverter->hasPreFormatConversion) { - ma_uint8 pTempBufferIn[MA_DATA_CONVERTER_STACK_BUFFER_SIZE]; - const ma_uint32 tempBufferInCap = sizeof(pTempBufferIn) / ma_get_bytes_per_frame(pConverter->resampler.format, pConverter->resampler.channels); - - frameCountInThisIteration = (frameCountIn - framesProcessedIn); - if (frameCountInThisIteration > tempBufferInCap) { - frameCountInThisIteration = tempBufferInCap; - } - - if (pConverter->hasPostFormatConversion) { - if (frameCountInThisIteration > tempBufferOutCap) { - frameCountInThisIteration = tempBufferOutCap; - } - } - - if (pFramesInThisIteration != NULL) { - ma_convert_pcm_frames_format(pTempBufferIn, pConverter->resampler.format, pFramesInThisIteration, pConverter->formatIn, frameCountInThisIteration, pConverter->channelsIn, pConverter->ditherMode); - } else { - MA_ZERO_MEMORY(pTempBufferIn, sizeof(pTempBufferIn)); - } - - frameCountOutThisIteration = (frameCountOut - framesProcessedOut); - - if (pConverter->hasPostFormatConversion) { - /* Both input and output conversion required. Output to the temp buffer. */ - if (frameCountOutThisIteration > tempBufferOutCap) { - frameCountOutThisIteration = tempBufferOutCap; - } - - result = ma_resampler_process_pcm_frames(&pConverter->resampler, pTempBufferIn, &frameCountInThisIteration, pTempBufferOut, &frameCountOutThisIteration); - } else { - /* Only pre-format required. Output straight to the output buffer. */ - result = ma_resampler_process_pcm_frames(&pConverter->resampler, pTempBufferIn, &frameCountInThisIteration, pFramesOutThisIteration, &frameCountOutThisIteration); - } - - if (result != MA_SUCCESS) { - break; - } - } else { - /* No pre-format required. Just read straight from the input buffer. */ - MA_ASSERT(pConverter->hasPostFormatConversion == MA_TRUE); - - frameCountInThisIteration = (frameCountIn - framesProcessedIn); - frameCountOutThisIteration = (frameCountOut - framesProcessedOut); - if (frameCountOutThisIteration > tempBufferOutCap) { - frameCountOutThisIteration = tempBufferOutCap; - } - - result = ma_resampler_process_pcm_frames(&pConverter->resampler, pFramesInThisIteration, &frameCountInThisIteration, pTempBufferOut, &frameCountOutThisIteration); - if (result != MA_SUCCESS) { - break; - } - } - - /* If we are doing a post format conversion we need to do that now. */ - if (pConverter->hasPostFormatConversion) { - if (pFramesOutThisIteration != NULL) { - ma_convert_pcm_frames_format(pFramesOutThisIteration, pConverter->formatOut, pTempBufferOut, pConverter->resampler.format, frameCountOutThisIteration, pConverter->resampler.channels, pConverter->ditherMode); - } - } - - framesProcessedIn += frameCountInThisIteration; - framesProcessedOut += frameCountOutThisIteration; - - MA_ASSERT(framesProcessedIn <= frameCountIn); - MA_ASSERT(framesProcessedOut <= frameCountOut); - - if (frameCountOutThisIteration == 0) { - break; /* Consumed all of our input data. */ - } - } - - if (pFrameCountIn != NULL) { - *pFrameCountIn = framesProcessedIn; - } - if (pFrameCountOut != NULL) { - *pFrameCountOut = framesProcessedOut; - } - - return result; -} - -static ma_result ma_data_converter_process_pcm_frames__resample_only(ma_data_converter* pConverter, const void* pFramesIn, ma_uint64* pFrameCountIn, void* pFramesOut, ma_uint64* pFrameCountOut) -{ - MA_ASSERT(pConverter != NULL); - - if (pConverter->hasPreFormatConversion == MA_FALSE && pConverter->hasPostFormatConversion == MA_FALSE) { - /* Neither pre- nor post-format required. This is simple case where only resampling is required. */ - return ma_resampler_process_pcm_frames(&pConverter->resampler, pFramesIn, pFrameCountIn, pFramesOut, pFrameCountOut); - } else { - /* Format conversion required. */ - return ma_data_converter_process_pcm_frames__resample_with_format_conversion(pConverter, pFramesIn, pFrameCountIn, pFramesOut, pFrameCountOut); - } -} - -static ma_result ma_data_converter_process_pcm_frames__channels_only(ma_data_converter* pConverter, const void* pFramesIn, ma_uint64* pFrameCountIn, void* pFramesOut, ma_uint64* pFrameCountOut) -{ - ma_result result; - ma_uint64 frameCountIn; - ma_uint64 frameCountOut; - ma_uint64 frameCount; - - MA_ASSERT(pConverter != NULL); - - frameCountIn = 0; - if (pFrameCountIn != NULL) { - frameCountIn = *pFrameCountIn; - } - - frameCountOut = 0; - if (pFrameCountOut != NULL) { - frameCountOut = *pFrameCountOut; - } - - frameCount = ma_min(frameCountIn, frameCountOut); - - if (pConverter->hasPreFormatConversion == MA_FALSE && pConverter->hasPostFormatConversion == MA_FALSE) { - /* No format conversion required. */ - result = ma_channel_converter_process_pcm_frames(&pConverter->channelConverter, pFramesOut, pFramesIn, frameCount); - if (result != MA_SUCCESS) { - return result; - } - } else { - /* Format conversion required. */ - ma_uint64 framesProcessed = 0; - - while (framesProcessed < frameCount) { - ma_uint8 pTempBufferOut[MA_DATA_CONVERTER_STACK_BUFFER_SIZE]; - const ma_uint32 tempBufferOutCap = sizeof(pTempBufferOut) / ma_get_bytes_per_frame(pConverter->channelConverter.format, pConverter->channelConverter.channelsOut); - const void* pFramesInThisIteration; - /* */ void* pFramesOutThisIteration; - ma_uint64 frameCountThisIteration; - - if (pFramesIn != NULL) { - pFramesInThisIteration = ma_offset_ptr(pFramesIn, framesProcessed * ma_get_bytes_per_frame(pConverter->formatIn, pConverter->channelsIn)); - } else { - pFramesInThisIteration = NULL; - } - - if (pFramesOut != NULL) { - pFramesOutThisIteration = ma_offset_ptr(pFramesOut, framesProcessed * ma_get_bytes_per_frame(pConverter->formatOut, pConverter->channelsOut)); - } else { - pFramesOutThisIteration = NULL; - } - - /* Do a pre format conversion if necessary. */ - if (pConverter->hasPreFormatConversion) { - ma_uint8 pTempBufferIn[MA_DATA_CONVERTER_STACK_BUFFER_SIZE]; - const ma_uint32 tempBufferInCap = sizeof(pTempBufferIn) / ma_get_bytes_per_frame(pConverter->channelConverter.format, pConverter->channelConverter.channelsIn); - - frameCountThisIteration = (frameCount - framesProcessed); - if (frameCountThisIteration > tempBufferInCap) { - frameCountThisIteration = tempBufferInCap; - } - - if (pConverter->hasPostFormatConversion) { - if (frameCountThisIteration > tempBufferOutCap) { - frameCountThisIteration = tempBufferOutCap; - } - } - - if (pFramesInThisIteration != NULL) { - ma_convert_pcm_frames_format(pTempBufferIn, pConverter->channelConverter.format, pFramesInThisIteration, pConverter->formatIn, frameCountThisIteration, pConverter->channelsIn, pConverter->ditherMode); - } else { - MA_ZERO_MEMORY(pTempBufferIn, sizeof(pTempBufferIn)); - } - - if (pConverter->hasPostFormatConversion) { - /* Both input and output conversion required. Output to the temp buffer. */ - result = ma_channel_converter_process_pcm_frames(&pConverter->channelConverter, pTempBufferOut, pTempBufferIn, frameCountThisIteration); - } else { - /* Only pre-format required. Output straight to the output buffer. */ - result = ma_channel_converter_process_pcm_frames(&pConverter->channelConverter, pFramesOutThisIteration, pTempBufferIn, frameCountThisIteration); - } - - if (result != MA_SUCCESS) { - break; - } - } else { - /* No pre-format required. Just read straight from the input buffer. */ - MA_ASSERT(pConverter->hasPostFormatConversion == MA_TRUE); - - frameCountThisIteration = (frameCount - framesProcessed); - if (frameCountThisIteration > tempBufferOutCap) { - frameCountThisIteration = tempBufferOutCap; - } - - result = ma_channel_converter_process_pcm_frames(&pConverter->channelConverter, pTempBufferOut, pFramesInThisIteration, frameCountThisIteration); - if (result != MA_SUCCESS) { - break; - } - } - - /* If we are doing a post format conversion we need to do that now. */ - if (pConverter->hasPostFormatConversion) { - if (pFramesOutThisIteration != NULL) { - ma_convert_pcm_frames_format(pFramesOutThisIteration, pConverter->formatOut, pTempBufferOut, pConverter->channelConverter.format, frameCountThisIteration, pConverter->channelConverter.channelsOut, pConverter->ditherMode); - } - } - - framesProcessed += frameCountThisIteration; - } - } - - if (pFrameCountIn != NULL) { - *pFrameCountIn = frameCount; - } - if (pFrameCountOut != NULL) { - *pFrameCountOut = frameCount; - } - - return MA_SUCCESS; -} - -static ma_result ma_data_converter_process_pcm_frames__resample_first(ma_data_converter* pConverter, const void* pFramesIn, ma_uint64* pFrameCountIn, void* pFramesOut, ma_uint64* pFrameCountOut) -{ - ma_result result; - ma_uint64 frameCountIn; - ma_uint64 frameCountOut; - ma_uint64 framesProcessedIn; - ma_uint64 framesProcessedOut; - ma_uint8 pTempBufferIn[MA_DATA_CONVERTER_STACK_BUFFER_SIZE]; /* In resampler format. */ - ma_uint64 tempBufferInCap; - ma_uint8 pTempBufferMid[MA_DATA_CONVERTER_STACK_BUFFER_SIZE]; /* In resampler format, channel converter input format. */ - ma_uint64 tempBufferMidCap; - ma_uint8 pTempBufferOut[MA_DATA_CONVERTER_STACK_BUFFER_SIZE]; /* In channel converter output format. */ - ma_uint64 tempBufferOutCap; - - MA_ASSERT(pConverter != NULL); - MA_ASSERT(pConverter->resampler.format == pConverter->channelConverter.format); - MA_ASSERT(pConverter->resampler.channels == pConverter->channelConverter.channelsIn); - MA_ASSERT(pConverter->resampler.channels < pConverter->channelConverter.channelsOut); - - frameCountIn = 0; - if (pFrameCountIn != NULL) { - frameCountIn = *pFrameCountIn; - } - - frameCountOut = 0; - if (pFrameCountOut != NULL) { - frameCountOut = *pFrameCountOut; - } - - framesProcessedIn = 0; - framesProcessedOut = 0; - - tempBufferInCap = sizeof(pTempBufferIn) / ma_get_bytes_per_frame(pConverter->resampler.format, pConverter->resampler.channels); - tempBufferMidCap = sizeof(pTempBufferIn) / ma_get_bytes_per_frame(pConverter->resampler.format, pConverter->resampler.channels); - tempBufferOutCap = sizeof(pTempBufferOut) / ma_get_bytes_per_frame(pConverter->channelConverter.format, pConverter->channelConverter.channelsOut); - - while (framesProcessedOut < frameCountOut) { - ma_uint64 frameCountInThisIteration; - ma_uint64 frameCountOutThisIteration; - const void* pRunningFramesIn = NULL; - void* pRunningFramesOut = NULL; - const void* pResampleBufferIn; - void* pChannelsBufferOut; - - if (pFramesIn != NULL) { - pRunningFramesIn = ma_offset_ptr(pFramesIn, framesProcessedIn * ma_get_bytes_per_frame(pConverter->formatIn, pConverter->channelsIn)); - } - if (pFramesOut != NULL) { - pRunningFramesOut = ma_offset_ptr(pFramesOut, framesProcessedOut * ma_get_bytes_per_frame(pConverter->formatOut, pConverter->channelsOut)); - } - - /* Run input data through the resampler and output it to the temporary buffer. */ - frameCountInThisIteration = (frameCountIn - framesProcessedIn); - - if (pConverter->hasPreFormatConversion) { - if (frameCountInThisIteration > tempBufferInCap) { - frameCountInThisIteration = tempBufferInCap; - } - } - - frameCountOutThisIteration = (frameCountOut - framesProcessedOut); - if (frameCountOutThisIteration > tempBufferMidCap) { - frameCountOutThisIteration = tempBufferMidCap; - } - - /* We can't read more frames than can fit in the output buffer. */ - if (pConverter->hasPostFormatConversion) { - if (frameCountOutThisIteration > tempBufferOutCap) { - frameCountOutThisIteration = tempBufferOutCap; - } - } - - /* We need to ensure we don't try to process too many input frames that we run out of room in the output buffer. If this happens we'll end up glitching. */ - - /* - We need to try to predict how many input frames will be required for the resampler. If the - resampler can tell us, we'll use that. Otherwise we'll need to make a best guess. The further - off we are from this, the more wasted format conversions we'll end up doing. - */ - #if 1 - { - ma_uint64 requiredInputFrameCount; - - result = ma_resampler_get_required_input_frame_count(&pConverter->resampler, frameCountOutThisIteration, &requiredInputFrameCount); - if (result != MA_SUCCESS) { - /* Fall back to a best guess. */ - requiredInputFrameCount = (frameCountOutThisIteration * pConverter->resampler.sampleRateIn) / pConverter->resampler.sampleRateOut; - } - - if (frameCountInThisIteration > requiredInputFrameCount) { - frameCountInThisIteration = requiredInputFrameCount; - } - } - #endif - - if (pConverter->hasPreFormatConversion) { - if (pFramesIn != NULL) { - ma_convert_pcm_frames_format(pTempBufferIn, pConverter->resampler.format, pRunningFramesIn, pConverter->formatIn, frameCountInThisIteration, pConverter->channelsIn, pConverter->ditherMode); - pResampleBufferIn = pTempBufferIn; - } else { - pResampleBufferIn = NULL; - } - } else { - pResampleBufferIn = pRunningFramesIn; - } - - result = ma_resampler_process_pcm_frames(&pConverter->resampler, pResampleBufferIn, &frameCountInThisIteration, pTempBufferMid, &frameCountOutThisIteration); - if (result != MA_SUCCESS) { - return result; - } - - - /* - The input data has been resampled so now we need to run it through the channel converter. The input data is always contained in pTempBufferMid. We only need to do - this part if we have an output buffer. - */ - if (pFramesOut != NULL) { - if (pConverter->hasPostFormatConversion) { - pChannelsBufferOut = pTempBufferOut; - } else { - pChannelsBufferOut = pRunningFramesOut; - } - - result = ma_channel_converter_process_pcm_frames(&pConverter->channelConverter, pChannelsBufferOut, pTempBufferMid, frameCountOutThisIteration); - if (result != MA_SUCCESS) { - return result; - } - - /* Finally we do post format conversion. */ - if (pConverter->hasPostFormatConversion) { - ma_convert_pcm_frames_format(pRunningFramesOut, pConverter->formatOut, pChannelsBufferOut, pConverter->channelConverter.format, frameCountOutThisIteration, pConverter->channelConverter.channelsOut, pConverter->ditherMode); - } - } - - - framesProcessedIn += frameCountInThisIteration; - framesProcessedOut += frameCountOutThisIteration; - - MA_ASSERT(framesProcessedIn <= frameCountIn); - MA_ASSERT(framesProcessedOut <= frameCountOut); - - if (frameCountOutThisIteration == 0) { - break; /* Consumed all of our input data. */ - } - } - - if (pFrameCountIn != NULL) { - *pFrameCountIn = framesProcessedIn; - } - if (pFrameCountOut != NULL) { - *pFrameCountOut = framesProcessedOut; - } - - return MA_SUCCESS; -} - -static ma_result ma_data_converter_process_pcm_frames__channels_first(ma_data_converter* pConverter, const void* pFramesIn, ma_uint64* pFrameCountIn, void* pFramesOut, ma_uint64* pFrameCountOut) -{ - ma_result result; - ma_uint64 frameCountIn; - ma_uint64 frameCountOut; - ma_uint64 framesProcessedIn; - ma_uint64 framesProcessedOut; - ma_uint8 pTempBufferIn[MA_DATA_CONVERTER_STACK_BUFFER_SIZE]; /* In resampler format. */ - ma_uint64 tempBufferInCap; - ma_uint8 pTempBufferMid[MA_DATA_CONVERTER_STACK_BUFFER_SIZE]; /* In resampler format, channel converter input format. */ - ma_uint64 tempBufferMidCap; - ma_uint8 pTempBufferOut[MA_DATA_CONVERTER_STACK_BUFFER_SIZE]; /* In channel converter output format. */ - ma_uint64 tempBufferOutCap; - - MA_ASSERT(pConverter != NULL); - MA_ASSERT(pConverter->resampler.format == pConverter->channelConverter.format); - MA_ASSERT(pConverter->resampler.channels == pConverter->channelConverter.channelsOut); - MA_ASSERT(pConverter->resampler.channels <= pConverter->channelConverter.channelsIn); - - frameCountIn = 0; - if (pFrameCountIn != NULL) { - frameCountIn = *pFrameCountIn; - } - - frameCountOut = 0; - if (pFrameCountOut != NULL) { - frameCountOut = *pFrameCountOut; - } - - framesProcessedIn = 0; - framesProcessedOut = 0; - - tempBufferInCap = sizeof(pTempBufferIn) / ma_get_bytes_per_frame(pConverter->channelConverter.format, pConverter->channelConverter.channelsIn); - tempBufferMidCap = sizeof(pTempBufferIn) / ma_get_bytes_per_frame(pConverter->channelConverter.format, pConverter->channelConverter.channelsOut); - tempBufferOutCap = sizeof(pTempBufferOut) / ma_get_bytes_per_frame(pConverter->resampler.format, pConverter->resampler.channels); - - while (framesProcessedOut < frameCountOut) { - ma_uint64 frameCountInThisIteration; - ma_uint64 frameCountOutThisIteration; - const void* pRunningFramesIn = NULL; - void* pRunningFramesOut = NULL; - const void* pChannelsBufferIn; - void* pResampleBufferOut; - - if (pFramesIn != NULL) { - pRunningFramesIn = ma_offset_ptr(pFramesIn, framesProcessedIn * ma_get_bytes_per_frame(pConverter->formatIn, pConverter->channelsIn)); - } - if (pFramesOut != NULL) { - pRunningFramesOut = ma_offset_ptr(pFramesOut, framesProcessedOut * ma_get_bytes_per_frame(pConverter->formatOut, pConverter->channelsOut)); - } - - /* - Before doing any processing we need to determine how many frames we should try processing - this iteration, for both input and output. The resampler requires us to perform format and - channel conversion before passing any data into it. If we get our input count wrong, we'll - end up performing redundant pre-processing. This isn't the end of the world, but it does - result in some inefficiencies proportionate to how far our estimates are off. - - If the resampler has a means to calculate exactly how much we'll need, we'll use that. - Otherwise we'll make a best guess. In order to do this, we'll need to calculate the output - frame count first. - */ - frameCountOutThisIteration = (frameCountOut - framesProcessedOut); - if (frameCountOutThisIteration > tempBufferMidCap) { - frameCountOutThisIteration = tempBufferMidCap; - } - - if (pConverter->hasPostFormatConversion) { - if (frameCountOutThisIteration > tempBufferOutCap) { - frameCountOutThisIteration = tempBufferOutCap; - } - } - - /* Now that we have the output frame count we can determine the input frame count. */ - frameCountInThisIteration = (frameCountIn - framesProcessedIn); - if (pConverter->hasPreFormatConversion) { - if (frameCountInThisIteration > tempBufferInCap) { - frameCountInThisIteration = tempBufferInCap; - } - } - - if (frameCountInThisIteration > tempBufferMidCap) { - frameCountInThisIteration = tempBufferMidCap; - } - - #if 1 - { - ma_uint64 requiredInputFrameCount; - - result = ma_resampler_get_required_input_frame_count(&pConverter->resampler, frameCountOutThisIteration, &requiredInputFrameCount); - if (result != MA_SUCCESS) { - /* Fall back to a best guess. */ - requiredInputFrameCount = (frameCountOutThisIteration * pConverter->resampler.sampleRateIn) / pConverter->resampler.sampleRateOut; - } - - if (frameCountInThisIteration > requiredInputFrameCount) { - frameCountInThisIteration = requiredInputFrameCount; - } - } - #endif - - - /* Pre format conversion. */ - if (pConverter->hasPreFormatConversion) { - if (pRunningFramesIn != NULL) { - ma_convert_pcm_frames_format(pTempBufferIn, pConverter->channelConverter.format, pRunningFramesIn, pConverter->formatIn, frameCountInThisIteration, pConverter->channelsIn, pConverter->ditherMode); - pChannelsBufferIn = pTempBufferIn; - } else { - pChannelsBufferIn = NULL; - } - } else { - pChannelsBufferIn = pRunningFramesIn; - } - - - /* Channel conversion. */ - result = ma_channel_converter_process_pcm_frames(&pConverter->channelConverter, pTempBufferMid, pChannelsBufferIn, frameCountInThisIteration); - if (result != MA_SUCCESS) { - return result; - } - - - /* Resampling. */ - if (pConverter->hasPostFormatConversion) { - pResampleBufferOut = pTempBufferOut; - } else { - pResampleBufferOut = pRunningFramesOut; - } - - result = ma_resampler_process_pcm_frames(&pConverter->resampler, pTempBufferMid, &frameCountInThisIteration, pResampleBufferOut, &frameCountOutThisIteration); - if (result != MA_SUCCESS) { - return result; - } - - - /* Post format conversion. */ - if (pConverter->hasPostFormatConversion) { - if (pRunningFramesOut != NULL) { - ma_convert_pcm_frames_format(pRunningFramesOut, pConverter->formatOut, pResampleBufferOut, pConverter->resampler.format, frameCountOutThisIteration, pConverter->channelsOut, pConverter->ditherMode); - } - } - - - framesProcessedIn += frameCountInThisIteration; - framesProcessedOut += frameCountOutThisIteration; - - MA_ASSERT(framesProcessedIn <= frameCountIn); - MA_ASSERT(framesProcessedOut <= frameCountOut); - - if (frameCountOutThisIteration == 0) { - break; /* Consumed all of our input data. */ - } - } - - if (pFrameCountIn != NULL) { - *pFrameCountIn = framesProcessedIn; - } - if (pFrameCountOut != NULL) { - *pFrameCountOut = framesProcessedOut; - } - - return MA_SUCCESS; -} - -MA_API ma_result ma_data_converter_process_pcm_frames(ma_data_converter* pConverter, const void* pFramesIn, ma_uint64* pFrameCountIn, void* pFramesOut, ma_uint64* pFrameCountOut) -{ - if (pConverter == NULL) { - return MA_INVALID_ARGS; - } - - switch (pConverter->executionPath) - { - case ma_data_converter_execution_path_passthrough: return ma_data_converter_process_pcm_frames__passthrough(pConverter, pFramesIn, pFrameCountIn, pFramesOut, pFrameCountOut); - case ma_data_converter_execution_path_format_only: return ma_data_converter_process_pcm_frames__format_only(pConverter, pFramesIn, pFrameCountIn, pFramesOut, pFrameCountOut); - case ma_data_converter_execution_path_channels_only: return ma_data_converter_process_pcm_frames__channels_only(pConverter, pFramesIn, pFrameCountIn, pFramesOut, pFrameCountOut); - case ma_data_converter_execution_path_resample_only: return ma_data_converter_process_pcm_frames__resample_only(pConverter, pFramesIn, pFrameCountIn, pFramesOut, pFrameCountOut); - case ma_data_converter_execution_path_resample_first: return ma_data_converter_process_pcm_frames__resample_first(pConverter, pFramesIn, pFrameCountIn, pFramesOut, pFrameCountOut); - case ma_data_converter_execution_path_channels_first: return ma_data_converter_process_pcm_frames__channels_first(pConverter, pFramesIn, pFrameCountIn, pFramesOut, pFrameCountOut); - default: return MA_INVALID_OPERATION; /* Should never hit this. */ - } -} - -MA_API ma_result ma_data_converter_set_rate(ma_data_converter* pConverter, ma_uint32 sampleRateIn, ma_uint32 sampleRateOut) -{ - if (pConverter == NULL) { - return MA_INVALID_ARGS; - } - - if (pConverter->hasResampler == MA_FALSE) { - return MA_INVALID_OPERATION; /* Dynamic resampling not enabled. */ - } - - return ma_resampler_set_rate(&pConverter->resampler, sampleRateIn, sampleRateOut); -} - -MA_API ma_result ma_data_converter_set_rate_ratio(ma_data_converter* pConverter, float ratioInOut) -{ - if (pConverter == NULL) { - return MA_INVALID_ARGS; - } - - if (pConverter->hasResampler == MA_FALSE) { - return MA_INVALID_OPERATION; /* Dynamic resampling not enabled. */ - } - - return ma_resampler_set_rate_ratio(&pConverter->resampler, ratioInOut); -} - -MA_API ma_uint64 ma_data_converter_get_input_latency(const ma_data_converter* pConverter) -{ - if (pConverter == NULL) { - return 0; - } - - if (pConverter->hasResampler) { - return ma_resampler_get_input_latency(&pConverter->resampler); - } - - return 0; /* No latency without a resampler. */ -} - -MA_API ma_uint64 ma_data_converter_get_output_latency(const ma_data_converter* pConverter) -{ - if (pConverter == NULL) { - return 0; - } - - if (pConverter->hasResampler) { - return ma_resampler_get_output_latency(&pConverter->resampler); - } - - return 0; /* No latency without a resampler. */ -} - -MA_API ma_result ma_data_converter_get_required_input_frame_count(const ma_data_converter* pConverter, ma_uint64 outputFrameCount, ma_uint64* pInputFrameCount) -{ - if (pInputFrameCount == NULL) { - return MA_INVALID_ARGS; - } - - *pInputFrameCount = 0; - - if (pConverter == NULL) { - return MA_INVALID_ARGS; - } - - if (pConverter->hasResampler) { - return ma_resampler_get_required_input_frame_count(&pConverter->resampler, outputFrameCount, pInputFrameCount); - } else { - *pInputFrameCount = outputFrameCount; /* 1:1 */ - return MA_SUCCESS; - } -} - -MA_API ma_result ma_data_converter_get_expected_output_frame_count(const ma_data_converter* pConverter, ma_uint64 inputFrameCount, ma_uint64* pOutputFrameCount) -{ - if (pOutputFrameCount == NULL) { - return MA_INVALID_ARGS; - } - - *pOutputFrameCount = 0; - - if (pConverter == NULL) { - return MA_INVALID_ARGS; - } - - if (pConverter->hasResampler) { - return ma_resampler_get_expected_output_frame_count(&pConverter->resampler, inputFrameCount, pOutputFrameCount); - } else { - *pOutputFrameCount = inputFrameCount; /* 1:1 */ - return MA_SUCCESS; - } -} - -MA_API ma_result ma_data_converter_get_input_channel_map(const ma_data_converter* pConverter, ma_channel* pChannelMap, size_t channelMapCap) -{ - if (pConverter == NULL || pChannelMap == NULL) { - return MA_INVALID_ARGS; - } - - if (pConverter->hasChannelConverter) { - ma_channel_converter_get_output_channel_map(&pConverter->channelConverter, pChannelMap, channelMapCap); - } else { - ma_channel_map_init_standard(ma_standard_channel_map_default, pChannelMap, channelMapCap, pConverter->channelsOut); - } - - return MA_SUCCESS; -} - -MA_API ma_result ma_data_converter_get_output_channel_map(const ma_data_converter* pConverter, ma_channel* pChannelMap, size_t channelMapCap) -{ - if (pConverter == NULL || pChannelMap == NULL) { - return MA_INVALID_ARGS; - } - - if (pConverter->hasChannelConverter) { - ma_channel_converter_get_input_channel_map(&pConverter->channelConverter, pChannelMap, channelMapCap); - } else { - ma_channel_map_init_standard(ma_standard_channel_map_default, pChannelMap, channelMapCap, pConverter->channelsIn); - } - - return MA_SUCCESS; -} - -MA_API ma_result ma_data_converter_reset(ma_data_converter* pConverter) -{ - if (pConverter == NULL) { - return MA_INVALID_ARGS; - } - - /* There's nothing to do if we're not resampling. */ - if (pConverter->hasResampler == MA_FALSE) { - return MA_SUCCESS; - } - - return ma_resampler_reset(&pConverter->resampler); -} - - - -/************************************************************************************************************************************************************** - -Channel Maps - -**************************************************************************************************************************************************************/ -static ma_channel ma_channel_map_init_standard_channel(ma_standard_channel_map standardChannelMap, ma_uint32 channelCount, ma_uint32 channelIndex); - -MA_API ma_channel ma_channel_map_get_channel(const ma_channel* pChannelMap, ma_uint32 channelCount, ma_uint32 channelIndex) -{ - if (pChannelMap == NULL) { - return ma_channel_map_init_standard_channel(ma_standard_channel_map_default, channelCount, channelIndex); - } else { - if (channelIndex >= channelCount) { - return MA_CHANNEL_NONE; - } - - return pChannelMap[channelIndex]; - } -} - -MA_API void ma_channel_map_init_blank(ma_channel* pChannelMap, ma_uint32 channels) -{ - if (pChannelMap == NULL) { - return; - } - - MA_ZERO_MEMORY(pChannelMap, sizeof(*pChannelMap) * channels); -} - - -static ma_channel ma_channel_map_init_standard_channel_microsoft(ma_uint32 channelCount, ma_uint32 channelIndex) -{ - if (channelCount == 0 || channelIndex >= channelCount) { - return MA_CHANNEL_NONE; - } - - /* This is the Microsoft channel map. Based off the speaker configurations mentioned here: https://docs.microsoft.com/en-us/windows-hardware/drivers/ddi/content/ksmedia/ns-ksmedia-ksaudio_channel_config */ - switch (channelCount) - { - case 0: return MA_CHANNEL_NONE; - - case 1: - { - return MA_CHANNEL_MONO; - } break; - - case 2: - { - switch (channelIndex) { - case 0: return MA_CHANNEL_FRONT_LEFT; - case 1: return MA_CHANNEL_FRONT_RIGHT; - } - } break; - - case 3: /* No defined, but best guess. */ - { - switch (channelIndex) { - case 0: return MA_CHANNEL_FRONT_LEFT; - case 1: return MA_CHANNEL_FRONT_RIGHT; - case 2: return MA_CHANNEL_FRONT_CENTER; - } - } break; - - case 4: - { - switch (channelIndex) { - #ifndef MA_USE_QUAD_MICROSOFT_CHANNEL_MAP - /* Surround. Using the Surround profile has the advantage of the 3rd channel (MA_CHANNEL_FRONT_CENTER) mapping nicely with higher channel counts. */ - case 0: return MA_CHANNEL_FRONT_LEFT; - case 1: return MA_CHANNEL_FRONT_RIGHT; - case 2: return MA_CHANNEL_FRONT_CENTER; - case 3: return MA_CHANNEL_BACK_CENTER; - #else - /* Quad. */ - case 0: return MA_CHANNEL_FRONT_LEFT; - case 1: return MA_CHANNEL_FRONT_RIGHT; - case 2: return MA_CHANNEL_BACK_LEFT; - case 3: return MA_CHANNEL_BACK_RIGHT; - #endif - } - } break; - - case 5: /* Not defined, but best guess. */ - { - switch (channelIndex) { - case 0: return MA_CHANNEL_FRONT_LEFT; - case 1: return MA_CHANNEL_FRONT_RIGHT; - case 2: return MA_CHANNEL_FRONT_CENTER; - case 3: return MA_CHANNEL_BACK_LEFT; - case 4: return MA_CHANNEL_BACK_RIGHT; - } - } break; - - case 6: - { - switch (channelIndex) { - case 0: return MA_CHANNEL_FRONT_LEFT; - case 1: return MA_CHANNEL_FRONT_RIGHT; - case 2: return MA_CHANNEL_FRONT_CENTER; - case 3: return MA_CHANNEL_LFE; - case 4: return MA_CHANNEL_SIDE_LEFT; - case 5: return MA_CHANNEL_SIDE_RIGHT; - } - } break; - - case 7: /* Not defined, but best guess. */ - { - switch (channelIndex) { - case 0: return MA_CHANNEL_FRONT_LEFT; - case 1: return MA_CHANNEL_FRONT_RIGHT; - case 2: return MA_CHANNEL_FRONT_CENTER; - case 3: return MA_CHANNEL_LFE; - case 4: return MA_CHANNEL_BACK_CENTER; - case 5: return MA_CHANNEL_SIDE_LEFT; - case 6: return MA_CHANNEL_SIDE_RIGHT; - } - } break; - - case 8: - default: - { - switch (channelIndex) { - case 0: return MA_CHANNEL_FRONT_LEFT; - case 1: return MA_CHANNEL_FRONT_RIGHT; - case 2: return MA_CHANNEL_FRONT_CENTER; - case 3: return MA_CHANNEL_LFE; - case 4: return MA_CHANNEL_BACK_LEFT; - case 5: return MA_CHANNEL_BACK_RIGHT; - case 6: return MA_CHANNEL_SIDE_LEFT; - case 7: return MA_CHANNEL_SIDE_RIGHT; - } - } break; - } - - if (channelCount > 8) { - if (channelIndex < 32) { /* We have 32 AUX channels. */ - return (ma_channel)(MA_CHANNEL_AUX_0 + (channelIndex - 8)); - } - } - - /* Getting here means we don't know how to map the channel position so just return MA_CHANNEL_NONE. */ - return MA_CHANNEL_NONE; -} - -static ma_channel ma_channel_map_init_standard_channel_alsa(ma_uint32 channelCount, ma_uint32 channelIndex) -{ - switch (channelCount) - { - case 0: return MA_CHANNEL_NONE; - - case 1: - { - return MA_CHANNEL_MONO; - } break; - - case 2: - { - switch (channelIndex) { - case 0: return MA_CHANNEL_FRONT_LEFT; - case 1: return MA_CHANNEL_FRONT_RIGHT; - } - } break; - - case 3: - { - switch (channelIndex) { - case 0: return MA_CHANNEL_FRONT_LEFT; - case 1: return MA_CHANNEL_FRONT_RIGHT; - case 2: return MA_CHANNEL_FRONT_CENTER; - } - } break; - - case 4: - { - switch (channelIndex) { - case 0: return MA_CHANNEL_FRONT_LEFT; - case 1: return MA_CHANNEL_FRONT_RIGHT; - case 2: return MA_CHANNEL_BACK_LEFT; - case 3: return MA_CHANNEL_BACK_RIGHT; - } - } break; - - case 5: - { - switch (channelIndex) { - case 0: return MA_CHANNEL_FRONT_LEFT; - case 1: return MA_CHANNEL_FRONT_RIGHT; - case 2: return MA_CHANNEL_BACK_LEFT; - case 3: return MA_CHANNEL_BACK_RIGHT; - case 4: return MA_CHANNEL_FRONT_CENTER; - } - } break; - - case 6: - { - switch (channelIndex) { - case 0: return MA_CHANNEL_FRONT_LEFT; - case 1: return MA_CHANNEL_FRONT_RIGHT; - case 2: return MA_CHANNEL_BACK_LEFT; - case 3: return MA_CHANNEL_BACK_RIGHT; - case 4: return MA_CHANNEL_FRONT_CENTER; - case 5: return MA_CHANNEL_LFE; - } - } break; - - case 7: - { - switch (channelIndex) { - case 0: return MA_CHANNEL_FRONT_LEFT; - case 1: return MA_CHANNEL_FRONT_RIGHT; - case 2: return MA_CHANNEL_BACK_LEFT; - case 3: return MA_CHANNEL_BACK_RIGHT; - case 4: return MA_CHANNEL_FRONT_CENTER; - case 5: return MA_CHANNEL_LFE; - case 6: return MA_CHANNEL_BACK_CENTER; - } - } break; - - case 8: - default: - { - switch (channelIndex) { - case 0: return MA_CHANNEL_FRONT_LEFT; - case 1: return MA_CHANNEL_FRONT_RIGHT; - case 2: return MA_CHANNEL_BACK_LEFT; - case 3: return MA_CHANNEL_BACK_RIGHT; - case 4: return MA_CHANNEL_FRONT_CENTER; - case 5: return MA_CHANNEL_LFE; - case 6: return MA_CHANNEL_SIDE_LEFT; - case 7: return MA_CHANNEL_SIDE_RIGHT; - } - } break; - } - - if (channelCount > 8) { - if (channelIndex < 32) { /* We have 32 AUX channels. */ - return (ma_channel)(MA_CHANNEL_AUX_0 + (channelIndex - 8)); - } - } - - /* Getting here means we don't know how to map the channel position so just return MA_CHANNEL_NONE. */ - return MA_CHANNEL_NONE; -} - -static ma_channel ma_channel_map_init_standard_channel_rfc3551(ma_uint32 channelCount, ma_uint32 channelIndex) -{ - switch (channelCount) - { - case 0: return MA_CHANNEL_NONE; - - case 1: - { - return MA_CHANNEL_MONO; - } break; - - case 2: - { - switch (channelIndex) { - case 0: return MA_CHANNEL_FRONT_LEFT; - case 1: return MA_CHANNEL_FRONT_RIGHT; - } - } break; - - case 3: - { - switch (channelIndex) { - case 0: return MA_CHANNEL_FRONT_LEFT; - case 1: return MA_CHANNEL_FRONT_RIGHT; - case 2: return MA_CHANNEL_FRONT_CENTER; - } - } break; - - case 4: - { - switch (channelIndex) { - case 0: return MA_CHANNEL_FRONT_LEFT; - case 2: return MA_CHANNEL_FRONT_CENTER; - case 1: return MA_CHANNEL_FRONT_RIGHT; - case 3: return MA_CHANNEL_BACK_CENTER; - } - } break; - - case 5: - { - switch (channelIndex) { - case 0: return MA_CHANNEL_FRONT_LEFT; - case 1: return MA_CHANNEL_FRONT_RIGHT; - case 2: return MA_CHANNEL_FRONT_CENTER; - case 3: return MA_CHANNEL_BACK_LEFT; - case 4: return MA_CHANNEL_BACK_RIGHT; - } - } break; - - case 6: - default: - { - switch (channelIndex) { - case 0: return MA_CHANNEL_FRONT_LEFT; - case 1: return MA_CHANNEL_SIDE_LEFT; - case 2: return MA_CHANNEL_FRONT_CENTER; - case 3: return MA_CHANNEL_FRONT_RIGHT; - case 4: return MA_CHANNEL_SIDE_RIGHT; - case 5: return MA_CHANNEL_BACK_CENTER; - } - } break; - } - - if (channelCount > 6) { - if (channelIndex < 32) { /* We have 32 AUX channels. */ - return (ma_channel)(MA_CHANNEL_AUX_0 + (channelIndex - 6)); - } - } - - /* Getting here means we don't know how to map the channel position so just return MA_CHANNEL_NONE. */ - return MA_CHANNEL_NONE; -} - -static ma_channel ma_channel_map_init_standard_channel_flac(ma_uint32 channelCount, ma_uint32 channelIndex) -{ - switch (channelCount) - { - case 0: return MA_CHANNEL_NONE; - - case 1: - { - return MA_CHANNEL_MONO; - } break; - - case 2: - { - switch (channelIndex) { - case 0: return MA_CHANNEL_FRONT_LEFT; - case 1: return MA_CHANNEL_FRONT_RIGHT; - } - } break; - - case 3: - { - switch (channelIndex) { - case 0: return MA_CHANNEL_FRONT_LEFT; - case 1: return MA_CHANNEL_FRONT_RIGHT; - case 2: return MA_CHANNEL_FRONT_CENTER; - } - } break; - - case 4: - { - switch (channelIndex) { - case 0: return MA_CHANNEL_FRONT_LEFT; - case 1: return MA_CHANNEL_FRONT_RIGHT; - case 2: return MA_CHANNEL_BACK_LEFT; - case 3: return MA_CHANNEL_BACK_RIGHT; - } - } break; - - case 5: - { - switch (channelIndex) { - case 0: return MA_CHANNEL_FRONT_LEFT; - case 1: return MA_CHANNEL_FRONT_RIGHT; - case 2: return MA_CHANNEL_FRONT_CENTER; - case 3: return MA_CHANNEL_BACK_LEFT; - case 4: return MA_CHANNEL_BACK_RIGHT; - } - } break; - - case 6: - { - switch (channelIndex) { - case 0: return MA_CHANNEL_FRONT_LEFT; - case 1: return MA_CHANNEL_FRONT_RIGHT; - case 2: return MA_CHANNEL_FRONT_CENTER; - case 3: return MA_CHANNEL_LFE; - case 4: return MA_CHANNEL_BACK_LEFT; - case 5: return MA_CHANNEL_BACK_RIGHT; - } - } break; - - case 7: - { - switch (channelIndex) { - case 0: return MA_CHANNEL_FRONT_LEFT; - case 1: return MA_CHANNEL_FRONT_RIGHT; - case 2: return MA_CHANNEL_FRONT_CENTER; - case 3: return MA_CHANNEL_LFE; - case 4: return MA_CHANNEL_BACK_CENTER; - case 5: return MA_CHANNEL_SIDE_LEFT; - case 6: return MA_CHANNEL_SIDE_RIGHT; - } - } break; - - case 8: - default: - { - switch (channelIndex) { - case 0: return MA_CHANNEL_FRONT_LEFT; - case 1: return MA_CHANNEL_FRONT_RIGHT; - case 2: return MA_CHANNEL_FRONT_CENTER; - case 3: return MA_CHANNEL_LFE; - case 4: return MA_CHANNEL_BACK_LEFT; - case 5: return MA_CHANNEL_BACK_RIGHT; - case 6: return MA_CHANNEL_SIDE_LEFT; - case 7: return MA_CHANNEL_SIDE_RIGHT; - } - } break; - } - - if (channelCount > 8) { - if (channelIndex < 32) { /* We have 32 AUX channels. */ - return (ma_channel)(MA_CHANNEL_AUX_0 + (channelIndex - 8)); - } - } - - /* Getting here means we don't know how to map the channel position so just return MA_CHANNEL_NONE. */ - return MA_CHANNEL_NONE; -} - -static ma_channel ma_channel_map_init_standard_channel_vorbis(ma_uint32 channelCount, ma_uint32 channelIndex) -{ - switch (channelCount) - { - case 0: return MA_CHANNEL_NONE; - - case 1: - { - return MA_CHANNEL_MONO; - } break; - - case 2: - { - switch (channelIndex) { - case 0: return MA_CHANNEL_FRONT_LEFT; - case 1: return MA_CHANNEL_FRONT_RIGHT; - } - } break; - - case 3: - { - switch (channelIndex) { - case 0: return MA_CHANNEL_FRONT_LEFT; - case 1: return MA_CHANNEL_FRONT_CENTER; - case 2: return MA_CHANNEL_FRONT_RIGHT; - } - } break; - - case 4: - { - switch (channelIndex) { - case 0: return MA_CHANNEL_FRONT_LEFT; - case 1: return MA_CHANNEL_FRONT_RIGHT; - case 2: return MA_CHANNEL_BACK_LEFT; - case 3: return MA_CHANNEL_BACK_RIGHT; - } - } break; - - case 5: - { - switch (channelIndex) { - case 0: return MA_CHANNEL_FRONT_LEFT; - case 1: return MA_CHANNEL_FRONT_CENTER; - case 2: return MA_CHANNEL_FRONT_RIGHT; - case 3: return MA_CHANNEL_BACK_LEFT; - case 4: return MA_CHANNEL_BACK_RIGHT; - } - } break; - - case 6: - { - switch (channelIndex) { - case 0: return MA_CHANNEL_FRONT_LEFT; - case 1: return MA_CHANNEL_FRONT_CENTER; - case 2: return MA_CHANNEL_FRONT_RIGHT; - case 3: return MA_CHANNEL_BACK_LEFT; - case 4: return MA_CHANNEL_BACK_RIGHT; - case 5: return MA_CHANNEL_LFE; - } - } break; - - case 7: - { - switch (channelIndex) { - case 0: return MA_CHANNEL_FRONT_LEFT; - case 1: return MA_CHANNEL_FRONT_CENTER; - case 2: return MA_CHANNEL_FRONT_RIGHT; - case 3: return MA_CHANNEL_SIDE_LEFT; - case 4: return MA_CHANNEL_SIDE_RIGHT; - case 5: return MA_CHANNEL_BACK_CENTER; - case 6: return MA_CHANNEL_LFE; - } - } break; - - case 8: - default: - { - switch (channelIndex) { - case 0: return MA_CHANNEL_FRONT_LEFT; - case 1: return MA_CHANNEL_FRONT_CENTER; - case 2: return MA_CHANNEL_FRONT_RIGHT; - case 3: return MA_CHANNEL_SIDE_LEFT; - case 4: return MA_CHANNEL_SIDE_RIGHT; - case 5: return MA_CHANNEL_BACK_LEFT; - case 6: return MA_CHANNEL_BACK_RIGHT; - case 7: return MA_CHANNEL_LFE; - } - } break; - } - - if (channelCount > 8) { - if (channelIndex < 32) { /* We have 32 AUX channels. */ - return (ma_channel)(MA_CHANNEL_AUX_0 + (channelIndex - 8)); - } - } - - /* Getting here means we don't know how to map the channel position so just return MA_CHANNEL_NONE. */ - return MA_CHANNEL_NONE; -} - -static ma_channel ma_channel_map_init_standard_channel_sound4(ma_uint32 channelCount, ma_uint32 channelIndex) -{ - switch (channelCount) - { - case 0: return MA_CHANNEL_NONE; - - case 1: - { - return MA_CHANNEL_MONO; - } break; - - case 2: - { - switch (channelIndex) { - case 0: return MA_CHANNEL_FRONT_LEFT; - case 1: return MA_CHANNEL_FRONT_RIGHT; - } - } break; - - case 3: - { - switch (channelIndex) { - case 0: return MA_CHANNEL_FRONT_LEFT; - case 1: return MA_CHANNEL_FRONT_RIGHT; - case 2: return MA_CHANNEL_FRONT_CENTER; - } - } break; - - case 4: - { - switch (channelIndex) { - case 0: return MA_CHANNEL_FRONT_LEFT; - case 1: return MA_CHANNEL_FRONT_RIGHT; - case 2: return MA_CHANNEL_BACK_LEFT; - case 3: return MA_CHANNEL_BACK_RIGHT; - } - } break; - - case 5: - { - switch (channelIndex) { - case 0: return MA_CHANNEL_FRONT_LEFT; - case 1: return MA_CHANNEL_FRONT_RIGHT; - case 2: return MA_CHANNEL_FRONT_CENTER; - case 3: return MA_CHANNEL_BACK_LEFT; - case 4: return MA_CHANNEL_BACK_RIGHT; - } - } break; - - case 6: - { - switch (channelIndex) { - case 0: return MA_CHANNEL_FRONT_LEFT; - case 1: return MA_CHANNEL_FRONT_CENTER; - case 2: return MA_CHANNEL_FRONT_RIGHT; - case 3: return MA_CHANNEL_BACK_LEFT; - case 4: return MA_CHANNEL_BACK_RIGHT; - case 5: return MA_CHANNEL_LFE; - } - } break; - - case 7: - { - switch (channelIndex) { - case 0: return MA_CHANNEL_FRONT_LEFT; - case 1: return MA_CHANNEL_FRONT_CENTER; - case 2: return MA_CHANNEL_FRONT_RIGHT; - case 3: return MA_CHANNEL_SIDE_LEFT; - case 4: return MA_CHANNEL_SIDE_RIGHT; - case 5: return MA_CHANNEL_BACK_CENTER; - case 6: return MA_CHANNEL_LFE; - } - } break; - - case 8: - default: - { - switch (channelIndex) { - case 0: return MA_CHANNEL_FRONT_LEFT; - case 1: return MA_CHANNEL_FRONT_CENTER; - case 2: return MA_CHANNEL_FRONT_RIGHT; - case 3: return MA_CHANNEL_SIDE_LEFT; - case 4: return MA_CHANNEL_SIDE_RIGHT; - case 5: return MA_CHANNEL_BACK_LEFT; - case 6: return MA_CHANNEL_BACK_RIGHT; - case 7: return MA_CHANNEL_LFE; - } - } break; - } - - if (channelCount > 8) { - if (channelIndex < 32) { /* We have 32 AUX channels. */ - return (ma_channel)(MA_CHANNEL_AUX_0 + (channelIndex - 8)); - } - } - - /* Getting here means we don't know how to map the channel position so just return MA_CHANNEL_NONE. */ - return MA_CHANNEL_NONE; -} - -static ma_channel ma_channel_map_init_standard_channel_sndio(ma_uint32 channelCount, ma_uint32 channelIndex) -{ - switch (channelCount) - { - case 0: return MA_CHANNEL_NONE; - - case 1: - { - return MA_CHANNEL_MONO; - } break; - - case 2: - { - switch (channelIndex) { - case 0: return MA_CHANNEL_FRONT_LEFT; - case 1: return MA_CHANNEL_FRONT_RIGHT; - } - } break; - - case 3: /* No defined, but best guess. */ - { - switch (channelIndex) { - case 0: return MA_CHANNEL_FRONT_LEFT; - case 1: return MA_CHANNEL_FRONT_RIGHT; - case 2: return MA_CHANNEL_FRONT_CENTER; - } - } break; - - case 4: - { - switch (channelIndex) { - case 0: return MA_CHANNEL_FRONT_LEFT; - case 1: return MA_CHANNEL_FRONT_RIGHT; - case 2: return MA_CHANNEL_BACK_LEFT; - case 3: return MA_CHANNEL_BACK_RIGHT; - } - } break; - - case 5: /* Not defined, but best guess. */ - { - switch (channelIndex) { - case 0: return MA_CHANNEL_FRONT_LEFT; - case 1: return MA_CHANNEL_FRONT_RIGHT; - case 2: return MA_CHANNEL_BACK_LEFT; - case 3: return MA_CHANNEL_BACK_RIGHT; - case 4: return MA_CHANNEL_FRONT_CENTER; - } - } break; - - case 6: - default: - { - switch (channelIndex) { - case 0: return MA_CHANNEL_FRONT_LEFT; - case 1: return MA_CHANNEL_FRONT_RIGHT; - case 2: return MA_CHANNEL_BACK_LEFT; - case 3: return MA_CHANNEL_BACK_RIGHT; - case 4: return MA_CHANNEL_FRONT_CENTER; - case 5: return MA_CHANNEL_LFE; - } - } break; - } - - if (channelCount > 6) { - if (channelIndex < 32) { /* We have 32 AUX channels. */ - return (ma_channel)(MA_CHANNEL_AUX_0 + (channelIndex - 6)); - } - } - - /* Getting here means we don't know how to map the channel position so just return MA_CHANNEL_NONE. */ - return MA_CHANNEL_NONE; -} - - -static ma_channel ma_channel_map_init_standard_channel(ma_standard_channel_map standardChannelMap, ma_uint32 channelCount, ma_uint32 channelIndex) -{ - if (channelCount == 0 || channelIndex >= channelCount) { - return MA_CHANNEL_NONE; - } - - switch (standardChannelMap) - { - case ma_standard_channel_map_alsa: - { - return ma_channel_map_init_standard_channel_alsa(channelCount, channelIndex); - } break; - - case ma_standard_channel_map_rfc3551: - { - return ma_channel_map_init_standard_channel_rfc3551(channelCount, channelIndex); - } break; - - case ma_standard_channel_map_flac: - { - return ma_channel_map_init_standard_channel_flac(channelCount, channelIndex); - } break; - - case ma_standard_channel_map_vorbis: - { - return ma_channel_map_init_standard_channel_vorbis(channelCount, channelIndex); - } break; - - case ma_standard_channel_map_sound4: - { - return ma_channel_map_init_standard_channel_sound4(channelCount, channelIndex); - } break; - - case ma_standard_channel_map_sndio: - { - return ma_channel_map_init_standard_channel_sndio(channelCount, channelIndex); - } break; - - case ma_standard_channel_map_microsoft: /* Also default. */ - /*case ma_standard_channel_map_default;*/ - default: - { - return ma_channel_map_init_standard_channel_microsoft(channelCount, channelIndex); - } break; - } -} - -MA_API void ma_channel_map_init_standard(ma_standard_channel_map standardChannelMap, ma_channel* pChannelMap, size_t channelMapCap, ma_uint32 channels) -{ - ma_uint32 iChannel; - - if (pChannelMap == NULL || channelMapCap == 0 || channels == 0) { - return; - } - - for (iChannel = 0; iChannel < channels; iChannel += 1) { - if (channelMapCap == 0) { - break; /* Ran out of room. */ - } - - pChannelMap[0] = ma_channel_map_init_standard_channel(standardChannelMap, channels, iChannel); - pChannelMap += 1; - channelMapCap -= 1; - } -} - -MA_API void ma_channel_map_copy(ma_channel* pOut, const ma_channel* pIn, ma_uint32 channels) -{ - if (pOut != NULL && pIn != NULL && channels > 0) { - MA_COPY_MEMORY(pOut, pIn, sizeof(*pOut) * channels); - } -} - -MA_API void ma_channel_map_copy_or_default(ma_channel* pOut, size_t channelMapCapOut, const ma_channel* pIn, ma_uint32 channels) -{ - if (pOut == NULL || channels == 0) { - return; - } - - if (pIn != NULL) { - ma_channel_map_copy(pOut, pIn, channels); - } else { - ma_channel_map_init_standard(ma_standard_channel_map_default, pOut, channelMapCapOut, channels); - } -} - -MA_API ma_bool32 ma_channel_map_is_valid(const ma_channel* pChannelMap, ma_uint32 channels) -{ - /* A channel count of 0 is invalid. */ - if (channels == 0) { - return MA_FALSE; - } - - /* It does not make sense to have a mono channel when there is more than 1 channel. */ - if (channels > 1) { - ma_uint32 iChannel; - for (iChannel = 0; iChannel < channels; ++iChannel) { - if (ma_channel_map_get_channel(pChannelMap, channels, iChannel) == MA_CHANNEL_MONO) { - return MA_FALSE; - } - } - } - - return MA_TRUE; -} - -MA_API ma_bool32 ma_channel_map_is_equal(const ma_channel* pChannelMapA, const ma_channel* pChannelMapB, ma_uint32 channels) -{ - ma_uint32 iChannel; - - if (pChannelMapA == pChannelMapB) { - return MA_TRUE; - } - - for (iChannel = 0; iChannel < channels; ++iChannel) { - if (ma_channel_map_get_channel(pChannelMapA, channels, iChannel) != ma_channel_map_get_channel(pChannelMapB, channels, iChannel)) { - return MA_FALSE; - } - } - - return MA_TRUE; -} - -MA_API ma_bool32 ma_channel_map_is_blank(const ma_channel* pChannelMap, ma_uint32 channels) -{ - ma_uint32 iChannel; - - /* A null channel map is equivalent to the default channel map. */ - if (pChannelMap == NULL) { - return MA_FALSE; - } - - for (iChannel = 0; iChannel < channels; ++iChannel) { - if (pChannelMap[iChannel] != MA_CHANNEL_NONE) { - return MA_FALSE; - } - } - - return MA_TRUE; -} - -MA_API ma_bool32 ma_channel_map_contains_channel_position(ma_uint32 channels, const ma_channel* pChannelMap, ma_channel channelPosition) -{ - return ma_channel_map_find_channel_position(channels, pChannelMap, channelPosition, NULL); -} - -MA_API ma_bool32 ma_channel_map_find_channel_position(ma_uint32 channels, const ma_channel* pChannelMap, ma_channel channelPosition, ma_uint32* pChannelIndex) -{ - ma_uint32 iChannel; - - if (pChannelIndex != NULL) { - *pChannelIndex = (ma_uint32)-1; - } - - for (iChannel = 0; iChannel < channels; ++iChannel) { - if (ma_channel_map_get_channel(pChannelMap, channels, iChannel) == channelPosition) { - if (pChannelIndex != NULL) { - *pChannelIndex = iChannel; - } - - return MA_TRUE; - } - } - - /* Getting here means the channel position was not found. */ - return MA_FALSE; -} - -MA_API size_t ma_channel_map_to_string(const ma_channel* pChannelMap, ma_uint32 channels, char* pBufferOut, size_t bufferCap) -{ - size_t len; - ma_uint32 iChannel; - - len = 0; - - for (iChannel = 0; iChannel < channels; iChannel += 1) { - const char* pChannelStr = ma_channel_position_to_string(ma_channel_map_get_channel(pChannelMap, channels, iChannel)); - size_t channelStrLen = strlen(pChannelStr); - - /* Append the string if necessary. */ - if (pBufferOut != NULL && bufferCap > len + channelStrLen) { - MA_COPY_MEMORY(pBufferOut + len, pChannelStr, channelStrLen); - } - len += channelStrLen; - - /* Append a space if it's not the last item. */ - if (iChannel+1 < channels) { - if (pBufferOut != NULL && bufferCap > len + 1) { - pBufferOut[len] = ' '; - } - len += 1; - } - } - - /* Null terminate. Don't increment the length here. */ - if (pBufferOut != NULL && bufferCap > len + 1) { - pBufferOut[len] = '\0'; - } - - return len; -} - -MA_API const char* ma_channel_position_to_string(ma_channel channel) -{ - switch (channel) - { - case MA_CHANNEL_NONE : return "CHANNEL_NONE"; - case MA_CHANNEL_MONO : return "CHANNEL_MONO"; - case MA_CHANNEL_FRONT_LEFT : return "CHANNEL_FRONT_LEFT"; - case MA_CHANNEL_FRONT_RIGHT : return "CHANNEL_FRONT_RIGHT"; - case MA_CHANNEL_FRONT_CENTER : return "CHANNEL_FRONT_CENTER"; - case MA_CHANNEL_LFE : return "CHANNEL_LFE"; - case MA_CHANNEL_BACK_LEFT : return "CHANNEL_BACK_LEFT"; - case MA_CHANNEL_BACK_RIGHT : return "CHANNEL_BACK_RIGHT"; - case MA_CHANNEL_FRONT_LEFT_CENTER : return "CHANNEL_FRONT_LEFT_CENTER"; - case MA_CHANNEL_FRONT_RIGHT_CENTER: return "CHANNEL_FRONT_RIGHT_CENTER"; - case MA_CHANNEL_BACK_CENTER : return "CHANNEL_BACK_CENTER"; - case MA_CHANNEL_SIDE_LEFT : return "CHANNEL_SIDE_LEFT"; - case MA_CHANNEL_SIDE_RIGHT : return "CHANNEL_SIDE_RIGHT"; - case MA_CHANNEL_TOP_CENTER : return "CHANNEL_TOP_CENTER"; - case MA_CHANNEL_TOP_FRONT_LEFT : return "CHANNEL_TOP_FRONT_LEFT"; - case MA_CHANNEL_TOP_FRONT_CENTER : return "CHANNEL_TOP_FRONT_CENTER"; - case MA_CHANNEL_TOP_FRONT_RIGHT : return "CHANNEL_TOP_FRONT_RIGHT"; - case MA_CHANNEL_TOP_BACK_LEFT : return "CHANNEL_TOP_BACK_LEFT"; - case MA_CHANNEL_TOP_BACK_CENTER : return "CHANNEL_TOP_BACK_CENTER"; - case MA_CHANNEL_TOP_BACK_RIGHT : return "CHANNEL_TOP_BACK_RIGHT"; - case MA_CHANNEL_AUX_0 : return "CHANNEL_AUX_0"; - case MA_CHANNEL_AUX_1 : return "CHANNEL_AUX_1"; - case MA_CHANNEL_AUX_2 : return "CHANNEL_AUX_2"; - case MA_CHANNEL_AUX_3 : return "CHANNEL_AUX_3"; - case MA_CHANNEL_AUX_4 : return "CHANNEL_AUX_4"; - case MA_CHANNEL_AUX_5 : return "CHANNEL_AUX_5"; - case MA_CHANNEL_AUX_6 : return "CHANNEL_AUX_6"; - case MA_CHANNEL_AUX_7 : return "CHANNEL_AUX_7"; - case MA_CHANNEL_AUX_8 : return "CHANNEL_AUX_8"; - case MA_CHANNEL_AUX_9 : return "CHANNEL_AUX_9"; - case MA_CHANNEL_AUX_10 : return "CHANNEL_AUX_10"; - case MA_CHANNEL_AUX_11 : return "CHANNEL_AUX_11"; - case MA_CHANNEL_AUX_12 : return "CHANNEL_AUX_12"; - case MA_CHANNEL_AUX_13 : return "CHANNEL_AUX_13"; - case MA_CHANNEL_AUX_14 : return "CHANNEL_AUX_14"; - case MA_CHANNEL_AUX_15 : return "CHANNEL_AUX_15"; - case MA_CHANNEL_AUX_16 : return "CHANNEL_AUX_16"; - case MA_CHANNEL_AUX_17 : return "CHANNEL_AUX_17"; - case MA_CHANNEL_AUX_18 : return "CHANNEL_AUX_18"; - case MA_CHANNEL_AUX_19 : return "CHANNEL_AUX_19"; - case MA_CHANNEL_AUX_20 : return "CHANNEL_AUX_20"; - case MA_CHANNEL_AUX_21 : return "CHANNEL_AUX_21"; - case MA_CHANNEL_AUX_22 : return "CHANNEL_AUX_22"; - case MA_CHANNEL_AUX_23 : return "CHANNEL_AUX_23"; - case MA_CHANNEL_AUX_24 : return "CHANNEL_AUX_24"; - case MA_CHANNEL_AUX_25 : return "CHANNEL_AUX_25"; - case MA_CHANNEL_AUX_26 : return "CHANNEL_AUX_26"; - case MA_CHANNEL_AUX_27 : return "CHANNEL_AUX_27"; - case MA_CHANNEL_AUX_28 : return "CHANNEL_AUX_28"; - case MA_CHANNEL_AUX_29 : return "CHANNEL_AUX_29"; - case MA_CHANNEL_AUX_30 : return "CHANNEL_AUX_30"; - case MA_CHANNEL_AUX_31 : return "CHANNEL_AUX_31"; - default: break; - } - - return "UNKNOWN"; -} - - - -/************************************************************************************************************************************************************** - -Conversion Helpers - -**************************************************************************************************************************************************************/ -MA_API ma_uint64 ma_convert_frames(void* pOut, ma_uint64 frameCountOut, ma_format formatOut, ma_uint32 channelsOut, ma_uint32 sampleRateOut, const void* pIn, ma_uint64 frameCountIn, ma_format formatIn, ma_uint32 channelsIn, ma_uint32 sampleRateIn) -{ - ma_data_converter_config config; - - config = ma_data_converter_config_init(formatIn, formatOut, channelsIn, channelsOut, sampleRateIn, sampleRateOut); - config.resampling.linear.lpfOrder = ma_min(MA_DEFAULT_RESAMPLER_LPF_ORDER, MA_MAX_FILTER_ORDER); - - return ma_convert_frames_ex(pOut, frameCountOut, pIn, frameCountIn, &config); -} - -MA_API ma_uint64 ma_convert_frames_ex(void* pOut, ma_uint64 frameCountOut, const void* pIn, ma_uint64 frameCountIn, const ma_data_converter_config* pConfig) -{ - ma_result result; - ma_data_converter converter; - - if (frameCountIn == 0 || pConfig == NULL) { - return 0; - } - - result = ma_data_converter_init(pConfig, NULL, &converter); - if (result != MA_SUCCESS) { - return 0; /* Failed to initialize the data converter. */ - } - - if (pOut == NULL) { - result = ma_data_converter_get_expected_output_frame_count(&converter, frameCountIn, &frameCountOut); - if (result != MA_SUCCESS) { - if (result == MA_NOT_IMPLEMENTED) { - /* No way to calculate the number of frames, so we'll need to brute force it and loop. */ - frameCountOut = 0; - - while (frameCountIn > 0) { - ma_uint64 framesProcessedIn = frameCountIn; - ma_uint64 framesProcessedOut = 0xFFFFFFFF; - - result = ma_data_converter_process_pcm_frames(&converter, pIn, &framesProcessedIn, NULL, &framesProcessedOut); - if (result != MA_SUCCESS) { - break; - } - - frameCountIn -= framesProcessedIn; - } - } - } - } else { - result = ma_data_converter_process_pcm_frames(&converter, pIn, &frameCountIn, pOut, &frameCountOut); - if (result != MA_SUCCESS) { - frameCountOut = 0; - } - } - - ma_data_converter_uninit(&converter, NULL); - return frameCountOut; -} - - -/************************************************************************************************************************************************************** - -Ring Buffer - -**************************************************************************************************************************************************************/ -static MA_INLINE ma_uint32 ma_rb__extract_offset_in_bytes(ma_uint32 encodedOffset) -{ - return encodedOffset & 0x7FFFFFFF; -} - -static MA_INLINE ma_uint32 ma_rb__extract_offset_loop_flag(ma_uint32 encodedOffset) -{ - return encodedOffset & 0x80000000; -} - -static MA_INLINE void* ma_rb__get_read_ptr(ma_rb* pRB) -{ - MA_ASSERT(pRB != NULL); - return ma_offset_ptr(pRB->pBuffer, ma_rb__extract_offset_in_bytes(ma_atomic_load_32(&pRB->encodedReadOffset))); -} - -static MA_INLINE void* ma_rb__get_write_ptr(ma_rb* pRB) -{ - MA_ASSERT(pRB != NULL); - return ma_offset_ptr(pRB->pBuffer, ma_rb__extract_offset_in_bytes(ma_atomic_load_32(&pRB->encodedWriteOffset))); -} - -static MA_INLINE ma_uint32 ma_rb__construct_offset(ma_uint32 offsetInBytes, ma_uint32 offsetLoopFlag) -{ - return offsetLoopFlag | offsetInBytes; -} - -static MA_INLINE void ma_rb__deconstruct_offset(ma_uint32 encodedOffset, ma_uint32* pOffsetInBytes, ma_uint32* pOffsetLoopFlag) -{ - MA_ASSERT(pOffsetInBytes != NULL); - MA_ASSERT(pOffsetLoopFlag != NULL); - - *pOffsetInBytes = ma_rb__extract_offset_in_bytes(encodedOffset); - *pOffsetLoopFlag = ma_rb__extract_offset_loop_flag(encodedOffset); -} - - -MA_API ma_result ma_rb_init_ex(size_t subbufferSizeInBytes, size_t subbufferCount, size_t subbufferStrideInBytes, void* pOptionalPreallocatedBuffer, const ma_allocation_callbacks* pAllocationCallbacks, ma_rb* pRB) -{ - ma_result result; - const ma_uint32 maxSubBufferSize = 0x7FFFFFFF - (MA_SIMD_ALIGNMENT-1); - - if (pRB == NULL) { - return MA_INVALID_ARGS; - } - - if (subbufferSizeInBytes == 0 || subbufferCount == 0) { - return MA_INVALID_ARGS; - } - - if (subbufferSizeInBytes > maxSubBufferSize) { - return MA_INVALID_ARGS; /* Maximum buffer size is ~2GB. The most significant bit is a flag for use internally. */ - } - - - MA_ZERO_OBJECT(pRB); - - result = ma_allocation_callbacks_init_copy(&pRB->allocationCallbacks, pAllocationCallbacks); - if (result != MA_SUCCESS) { - return result; - } - - pRB->subbufferSizeInBytes = (ma_uint32)subbufferSizeInBytes; - pRB->subbufferCount = (ma_uint32)subbufferCount; - - if (pOptionalPreallocatedBuffer != NULL) { - pRB->subbufferStrideInBytes = (ma_uint32)subbufferStrideInBytes; - pRB->pBuffer = pOptionalPreallocatedBuffer; - } else { - size_t bufferSizeInBytes; - - /* - Here is where we allocate our own buffer. We always want to align this to MA_SIMD_ALIGNMENT for future SIMD optimization opportunity. To do this - we need to make sure the stride is a multiple of MA_SIMD_ALIGNMENT. - */ - pRB->subbufferStrideInBytes = (pRB->subbufferSizeInBytes + (MA_SIMD_ALIGNMENT-1)) & ~MA_SIMD_ALIGNMENT; - - bufferSizeInBytes = (size_t)pRB->subbufferCount*pRB->subbufferStrideInBytes; - pRB->pBuffer = ma_aligned_malloc(bufferSizeInBytes, MA_SIMD_ALIGNMENT, &pRB->allocationCallbacks); - if (pRB->pBuffer == NULL) { - return MA_OUT_OF_MEMORY; - } - - MA_ZERO_MEMORY(pRB->pBuffer, bufferSizeInBytes); - pRB->ownsBuffer = MA_TRUE; - } - - return MA_SUCCESS; -} - -MA_API ma_result ma_rb_init(size_t bufferSizeInBytes, void* pOptionalPreallocatedBuffer, const ma_allocation_callbacks* pAllocationCallbacks, ma_rb* pRB) -{ - return ma_rb_init_ex(bufferSizeInBytes, 1, 0, pOptionalPreallocatedBuffer, pAllocationCallbacks, pRB); -} - -MA_API void ma_rb_uninit(ma_rb* pRB) -{ - if (pRB == NULL) { - return; - } - - if (pRB->ownsBuffer) { - ma_aligned_free(pRB->pBuffer, &pRB->allocationCallbacks); - } -} - -MA_API void ma_rb_reset(ma_rb* pRB) -{ - if (pRB == NULL) { - return; - } - - ma_atomic_exchange_32(&pRB->encodedReadOffset, 0); - ma_atomic_exchange_32(&pRB->encodedWriteOffset, 0); -} - -MA_API ma_result ma_rb_acquire_read(ma_rb* pRB, size_t* pSizeInBytes, void** ppBufferOut) -{ - ma_uint32 writeOffset; - ma_uint32 writeOffsetInBytes; - ma_uint32 writeOffsetLoopFlag; - ma_uint32 readOffset; - ma_uint32 readOffsetInBytes; - ma_uint32 readOffsetLoopFlag; - size_t bytesAvailable; - size_t bytesRequested; - - if (pRB == NULL || pSizeInBytes == NULL || ppBufferOut == NULL) { - return MA_INVALID_ARGS; - } - - /* The returned buffer should never move ahead of the write pointer. */ - writeOffset = ma_atomic_load_32(&pRB->encodedWriteOffset); - ma_rb__deconstruct_offset(writeOffset, &writeOffsetInBytes, &writeOffsetLoopFlag); - - readOffset = ma_atomic_load_32(&pRB->encodedReadOffset); - ma_rb__deconstruct_offset(readOffset, &readOffsetInBytes, &readOffsetLoopFlag); - - /* - The number of bytes available depends on whether or not the read and write pointers are on the same loop iteration. If so, we - can only read up to the write pointer. If not, we can only read up to the end of the buffer. - */ - if (readOffsetLoopFlag == writeOffsetLoopFlag) { - bytesAvailable = writeOffsetInBytes - readOffsetInBytes; - } else { - bytesAvailable = pRB->subbufferSizeInBytes - readOffsetInBytes; - } - - bytesRequested = *pSizeInBytes; - if (bytesRequested > bytesAvailable) { - bytesRequested = bytesAvailable; - } - - *pSizeInBytes = bytesRequested; - (*ppBufferOut) = ma_rb__get_read_ptr(pRB); - - return MA_SUCCESS; -} - -MA_API ma_result ma_rb_commit_read(ma_rb* pRB, size_t sizeInBytes) -{ - ma_uint32 readOffset; - ma_uint32 readOffsetInBytes; - ma_uint32 readOffsetLoopFlag; - ma_uint32 newReadOffsetInBytes; - ma_uint32 newReadOffsetLoopFlag; - - if (pRB == NULL) { - return MA_INVALID_ARGS; - } - - readOffset = ma_atomic_load_32(&pRB->encodedReadOffset); - ma_rb__deconstruct_offset(readOffset, &readOffsetInBytes, &readOffsetLoopFlag); - - /* Check that sizeInBytes is correct. It should never go beyond the end of the buffer. */ - newReadOffsetInBytes = (ma_uint32)(readOffsetInBytes + sizeInBytes); - if (newReadOffsetInBytes > pRB->subbufferSizeInBytes) { - return MA_INVALID_ARGS; /* <-- sizeInBytes will cause the read offset to overflow. */ - } - - /* Move the read pointer back to the start if necessary. */ - newReadOffsetLoopFlag = readOffsetLoopFlag; - if (newReadOffsetInBytes == pRB->subbufferSizeInBytes) { - newReadOffsetInBytes = 0; - newReadOffsetLoopFlag ^= 0x80000000; - } - - ma_atomic_exchange_32(&pRB->encodedReadOffset, ma_rb__construct_offset(newReadOffsetInBytes, newReadOffsetLoopFlag)); - - return MA_SUCCESS; -} - -MA_API ma_result ma_rb_acquire_write(ma_rb* pRB, size_t* pSizeInBytes, void** ppBufferOut) -{ - ma_uint32 readOffset; - ma_uint32 readOffsetInBytes; - ma_uint32 readOffsetLoopFlag; - ma_uint32 writeOffset; - ma_uint32 writeOffsetInBytes; - ma_uint32 writeOffsetLoopFlag; - size_t bytesAvailable; - size_t bytesRequested; - - if (pRB == NULL || pSizeInBytes == NULL || ppBufferOut == NULL) { - return MA_INVALID_ARGS; - } - - /* The returned buffer should never overtake the read buffer. */ - readOffset = ma_atomic_load_32(&pRB->encodedReadOffset); - ma_rb__deconstruct_offset(readOffset, &readOffsetInBytes, &readOffsetLoopFlag); - - writeOffset = ma_atomic_load_32(&pRB->encodedWriteOffset); - ma_rb__deconstruct_offset(writeOffset, &writeOffsetInBytes, &writeOffsetLoopFlag); - - /* - In the case of writing, if the write pointer and the read pointer are on the same loop iteration we can only - write up to the end of the buffer. Otherwise we can only write up to the read pointer. The write pointer should - never overtake the read pointer. - */ - if (writeOffsetLoopFlag == readOffsetLoopFlag) { - bytesAvailable = pRB->subbufferSizeInBytes - writeOffsetInBytes; - } else { - bytesAvailable = readOffsetInBytes - writeOffsetInBytes; - } - - bytesRequested = *pSizeInBytes; - if (bytesRequested > bytesAvailable) { - bytesRequested = bytesAvailable; - } - - *pSizeInBytes = bytesRequested; - *ppBufferOut = ma_rb__get_write_ptr(pRB); - - /* Clear the buffer if desired. */ - if (pRB->clearOnWriteAcquire) { - MA_ZERO_MEMORY(*ppBufferOut, *pSizeInBytes); - } - - return MA_SUCCESS; -} - -MA_API ma_result ma_rb_commit_write(ma_rb* pRB, size_t sizeInBytes) -{ - ma_uint32 writeOffset; - ma_uint32 writeOffsetInBytes; - ma_uint32 writeOffsetLoopFlag; - ma_uint32 newWriteOffsetInBytes; - ma_uint32 newWriteOffsetLoopFlag; - - if (pRB == NULL) { - return MA_INVALID_ARGS; - } - - writeOffset = ma_atomic_load_32(&pRB->encodedWriteOffset); - ma_rb__deconstruct_offset(writeOffset, &writeOffsetInBytes, &writeOffsetLoopFlag); - - /* Check that sizeInBytes is correct. It should never go beyond the end of the buffer. */ - newWriteOffsetInBytes = (ma_uint32)(writeOffsetInBytes + sizeInBytes); - if (newWriteOffsetInBytes > pRB->subbufferSizeInBytes) { - return MA_INVALID_ARGS; /* <-- sizeInBytes will cause the read offset to overflow. */ - } - - /* Move the read pointer back to the start if necessary. */ - newWriteOffsetLoopFlag = writeOffsetLoopFlag; - if (newWriteOffsetInBytes == pRB->subbufferSizeInBytes) { - newWriteOffsetInBytes = 0; - newWriteOffsetLoopFlag ^= 0x80000000; - } - - ma_atomic_exchange_32(&pRB->encodedWriteOffset, ma_rb__construct_offset(newWriteOffsetInBytes, newWriteOffsetLoopFlag)); - - return MA_SUCCESS; -} - -MA_API ma_result ma_rb_seek_read(ma_rb* pRB, size_t offsetInBytes) -{ - ma_uint32 readOffset; - ma_uint32 readOffsetInBytes; - ma_uint32 readOffsetLoopFlag; - ma_uint32 writeOffset; - ma_uint32 writeOffsetInBytes; - ma_uint32 writeOffsetLoopFlag; - ma_uint32 newReadOffsetInBytes; - ma_uint32 newReadOffsetLoopFlag; - - if (pRB == NULL || offsetInBytes > pRB->subbufferSizeInBytes) { - return MA_INVALID_ARGS; - } - - readOffset = ma_atomic_load_32(&pRB->encodedReadOffset); - ma_rb__deconstruct_offset(readOffset, &readOffsetInBytes, &readOffsetLoopFlag); - - writeOffset = ma_atomic_load_32(&pRB->encodedWriteOffset); - ma_rb__deconstruct_offset(writeOffset, &writeOffsetInBytes, &writeOffsetLoopFlag); - - newReadOffsetLoopFlag = readOffsetLoopFlag; - - /* We cannot go past the write buffer. */ - if (readOffsetLoopFlag == writeOffsetLoopFlag) { - if ((readOffsetInBytes + offsetInBytes) > writeOffsetInBytes) { - newReadOffsetInBytes = writeOffsetInBytes; - } else { - newReadOffsetInBytes = (ma_uint32)(readOffsetInBytes + offsetInBytes); - } - } else { - /* May end up looping. */ - if ((readOffsetInBytes + offsetInBytes) >= pRB->subbufferSizeInBytes) { - newReadOffsetInBytes = (ma_uint32)(readOffsetInBytes + offsetInBytes) - pRB->subbufferSizeInBytes; - newReadOffsetLoopFlag ^= 0x80000000; /* <-- Looped. */ - } else { - newReadOffsetInBytes = (ma_uint32)(readOffsetInBytes + offsetInBytes); - } - } - - ma_atomic_exchange_32(&pRB->encodedReadOffset, ma_rb__construct_offset(newReadOffsetInBytes, newReadOffsetLoopFlag)); - return MA_SUCCESS; -} - -MA_API ma_result ma_rb_seek_write(ma_rb* pRB, size_t offsetInBytes) -{ - ma_uint32 readOffset; - ma_uint32 readOffsetInBytes; - ma_uint32 readOffsetLoopFlag; - ma_uint32 writeOffset; - ma_uint32 writeOffsetInBytes; - ma_uint32 writeOffsetLoopFlag; - ma_uint32 newWriteOffsetInBytes; - ma_uint32 newWriteOffsetLoopFlag; - - if (pRB == NULL) { - return MA_INVALID_ARGS; - } - - readOffset = ma_atomic_load_32(&pRB->encodedReadOffset); - ma_rb__deconstruct_offset(readOffset, &readOffsetInBytes, &readOffsetLoopFlag); - - writeOffset = ma_atomic_load_32(&pRB->encodedWriteOffset); - ma_rb__deconstruct_offset(writeOffset, &writeOffsetInBytes, &writeOffsetLoopFlag); - - newWriteOffsetLoopFlag = writeOffsetLoopFlag; - - /* We cannot go past the write buffer. */ - if (readOffsetLoopFlag == writeOffsetLoopFlag) { - /* May end up looping. */ - if ((writeOffsetInBytes + offsetInBytes) >= pRB->subbufferSizeInBytes) { - newWriteOffsetInBytes = (ma_uint32)(writeOffsetInBytes + offsetInBytes) - pRB->subbufferSizeInBytes; - newWriteOffsetLoopFlag ^= 0x80000000; /* <-- Looped. */ - } else { - newWriteOffsetInBytes = (ma_uint32)(writeOffsetInBytes + offsetInBytes); - } - } else { - if ((writeOffsetInBytes + offsetInBytes) > readOffsetInBytes) { - newWriteOffsetInBytes = readOffsetInBytes; - } else { - newWriteOffsetInBytes = (ma_uint32)(writeOffsetInBytes + offsetInBytes); - } - } - - ma_atomic_exchange_32(&pRB->encodedWriteOffset, ma_rb__construct_offset(newWriteOffsetInBytes, newWriteOffsetLoopFlag)); - return MA_SUCCESS; -} - -MA_API ma_int32 ma_rb_pointer_distance(ma_rb* pRB) -{ - ma_uint32 readOffset; - ma_uint32 readOffsetInBytes; - ma_uint32 readOffsetLoopFlag; - ma_uint32 writeOffset; - ma_uint32 writeOffsetInBytes; - ma_uint32 writeOffsetLoopFlag; - - if (pRB == NULL) { - return 0; - } - - readOffset = ma_atomic_load_32(&pRB->encodedReadOffset); - ma_rb__deconstruct_offset(readOffset, &readOffsetInBytes, &readOffsetLoopFlag); - - writeOffset = ma_atomic_load_32(&pRB->encodedWriteOffset); - ma_rb__deconstruct_offset(writeOffset, &writeOffsetInBytes, &writeOffsetLoopFlag); - - if (readOffsetLoopFlag == writeOffsetLoopFlag) { - return writeOffsetInBytes - readOffsetInBytes; - } else { - return writeOffsetInBytes + (pRB->subbufferSizeInBytes - readOffsetInBytes); - } -} - -MA_API ma_uint32 ma_rb_available_read(ma_rb* pRB) -{ - ma_int32 dist; - - if (pRB == NULL) { - return 0; - } - - dist = ma_rb_pointer_distance(pRB); - if (dist < 0) { - return 0; - } - - return dist; -} - -MA_API ma_uint32 ma_rb_available_write(ma_rb* pRB) -{ - if (pRB == NULL) { - return 0; - } - - return (ma_uint32)(ma_rb_get_subbuffer_size(pRB) - ma_rb_pointer_distance(pRB)); -} - -MA_API size_t ma_rb_get_subbuffer_size(ma_rb* pRB) -{ - if (pRB == NULL) { - return 0; - } - - return pRB->subbufferSizeInBytes; -} - -MA_API size_t ma_rb_get_subbuffer_stride(ma_rb* pRB) -{ - if (pRB == NULL) { - return 0; - } - - if (pRB->subbufferStrideInBytes == 0) { - return (size_t)pRB->subbufferSizeInBytes; - } - - return (size_t)pRB->subbufferStrideInBytes; -} - -MA_API size_t ma_rb_get_subbuffer_offset(ma_rb* pRB, size_t subbufferIndex) -{ - if (pRB == NULL) { - return 0; - } - - return subbufferIndex * ma_rb_get_subbuffer_stride(pRB); -} - -MA_API void* ma_rb_get_subbuffer_ptr(ma_rb* pRB, size_t subbufferIndex, void* pBuffer) -{ - if (pRB == NULL) { - return NULL; - } - - return ma_offset_ptr(pBuffer, ma_rb_get_subbuffer_offset(pRB, subbufferIndex)); -} - - - -static ma_result ma_pcm_rb_data_source__on_read(ma_data_source* pDataSource, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead) -{ - /* Since there's no notion of an end, we don't ever want to return MA_AT_END here. But it is possible to return 0. */ - ma_pcm_rb* pRB = (ma_pcm_rb*)pDataSource; - ma_result result; - ma_uint64 totalFramesRead; - - MA_ASSERT(pRB != NULL); - - /* We need to run this in a loop since the ring buffer itself may loop. */ - totalFramesRead = 0; - while (totalFramesRead < frameCount) { - void* pMappedBuffer; - ma_uint32 mappedFrameCount; - ma_uint64 framesToRead = frameCount - totalFramesRead; - if (framesToRead > 0xFFFFFFFF) { - framesToRead = 0xFFFFFFFF; - } - - mappedFrameCount = (ma_uint32)framesToRead; - result = ma_pcm_rb_acquire_read(pRB, &mappedFrameCount, &pMappedBuffer); - if (result != MA_SUCCESS) { - break; - } - - if (mappedFrameCount == 0) { - break; /* <-- End of ring buffer. */ - } - - ma_copy_pcm_frames(ma_offset_pcm_frames_ptr(pFramesOut, totalFramesRead, pRB->format, pRB->channels), pMappedBuffer, mappedFrameCount, pRB->format, pRB->channels); - - result = ma_pcm_rb_commit_read(pRB, mappedFrameCount); - if (result != MA_SUCCESS) { - break; - } - - totalFramesRead += mappedFrameCount; - } - - /* - There is no notion of an "end" in a ring buffer. If we didn't have enough data to fill the requested frame - count we'll need to pad with silence. If we don't do this, totalFramesRead might equal 0 which will result - in the data source layer at a higher level translating this to MA_AT_END which is incorrect for a ring buffer. - */ - if (totalFramesRead < frameCount) { - ma_silence_pcm_frames(ma_offset_pcm_frames_ptr(pFramesOut, totalFramesRead, pRB->format, pRB->channels), (frameCount - totalFramesRead), pRB->format, pRB->channels); - totalFramesRead = frameCount; - } - - *pFramesRead = totalFramesRead; - return MA_SUCCESS; -} - -static ma_result ma_pcm_rb_data_source__on_get_data_format(ma_data_source* pDataSource, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate, ma_channel* pChannelMap, size_t channelMapCap) -{ - ma_pcm_rb* pRB = (ma_pcm_rb*)pDataSource; - MA_ASSERT(pRB != NULL); - - if (pFormat != NULL) { - *pFormat = pRB->format; - } - - if (pChannels != NULL) { - *pChannels = pRB->channels; - } - - if (pSampleRate != NULL) { - *pSampleRate = pRB->sampleRate; - } - - /* Just assume the default channel map. */ - if (pChannelMap != NULL) { - ma_channel_map_init_standard(ma_standard_channel_map_default, pChannelMap, channelMapCap, pRB->channels); - } - - return MA_SUCCESS; -} - -static ma_data_source_vtable ma_gRBDataSourceVTable = -{ - ma_pcm_rb_data_source__on_read, - NULL, /* onSeek */ - ma_pcm_rb_data_source__on_get_data_format, - NULL, /* onGetCursor */ - NULL, /* onGetLength */ - NULL, /* onSetLooping */ - 0 -}; - -static MA_INLINE ma_uint32 ma_pcm_rb_get_bpf(ma_pcm_rb* pRB) -{ - MA_ASSERT(pRB != NULL); - - return ma_get_bytes_per_frame(pRB->format, pRB->channels); -} - -MA_API ma_result ma_pcm_rb_init_ex(ma_format format, ma_uint32 channels, ma_uint32 subbufferSizeInFrames, ma_uint32 subbufferCount, ma_uint32 subbufferStrideInFrames, void* pOptionalPreallocatedBuffer, const ma_allocation_callbacks* pAllocationCallbacks, ma_pcm_rb* pRB) -{ - ma_uint32 bpf; - ma_result result; - - if (pRB == NULL) { - return MA_INVALID_ARGS; - } - - MA_ZERO_OBJECT(pRB); - - bpf = ma_get_bytes_per_frame(format, channels); - if (bpf == 0) { - return MA_INVALID_ARGS; - } - - result = ma_rb_init_ex(subbufferSizeInFrames*bpf, subbufferCount, subbufferStrideInFrames*bpf, pOptionalPreallocatedBuffer, pAllocationCallbacks, &pRB->rb); - if (result != MA_SUCCESS) { - return result; - } - - pRB->format = format; - pRB->channels = channels; - pRB->sampleRate = 0; /* The sample rate is not passed in as a parameter. */ - - /* The PCM ring buffer is a data source. We need to get that set up as well. */ - { - ma_data_source_config dataSourceConfig = ma_data_source_config_init(); - dataSourceConfig.vtable = &ma_gRBDataSourceVTable; - - result = ma_data_source_init(&dataSourceConfig, &pRB->ds); - if (result != MA_SUCCESS) { - ma_rb_uninit(&pRB->rb); - return result; - } - } - - return MA_SUCCESS; -} - -MA_API ma_result ma_pcm_rb_init(ma_format format, ma_uint32 channels, ma_uint32 bufferSizeInFrames, void* pOptionalPreallocatedBuffer, const ma_allocation_callbacks* pAllocationCallbacks, ma_pcm_rb* pRB) -{ - return ma_pcm_rb_init_ex(format, channels, bufferSizeInFrames, 1, 0, pOptionalPreallocatedBuffer, pAllocationCallbacks, pRB); -} - -MA_API void ma_pcm_rb_uninit(ma_pcm_rb* pRB) -{ - if (pRB == NULL) { - return; - } - - ma_data_source_uninit(&pRB->ds); - ma_rb_uninit(&pRB->rb); -} - -MA_API void ma_pcm_rb_reset(ma_pcm_rb* pRB) -{ - if (pRB == NULL) { - return; - } - - ma_rb_reset(&pRB->rb); -} - -MA_API ma_result ma_pcm_rb_acquire_read(ma_pcm_rb* pRB, ma_uint32* pSizeInFrames, void** ppBufferOut) -{ - size_t sizeInBytes; - ma_result result; - - if (pRB == NULL || pSizeInFrames == NULL) { - return MA_INVALID_ARGS; - } - - sizeInBytes = *pSizeInFrames * ma_pcm_rb_get_bpf(pRB); - - result = ma_rb_acquire_read(&pRB->rb, &sizeInBytes, ppBufferOut); - if (result != MA_SUCCESS) { - return result; - } - - *pSizeInFrames = (ma_uint32)(sizeInBytes / (size_t)ma_pcm_rb_get_bpf(pRB)); - return MA_SUCCESS; -} - -MA_API ma_result ma_pcm_rb_commit_read(ma_pcm_rb* pRB, ma_uint32 sizeInFrames) -{ - if (pRB == NULL) { - return MA_INVALID_ARGS; - } - - return ma_rb_commit_read(&pRB->rb, sizeInFrames * ma_pcm_rb_get_bpf(pRB)); -} - -MA_API ma_result ma_pcm_rb_acquire_write(ma_pcm_rb* pRB, ma_uint32* pSizeInFrames, void** ppBufferOut) -{ - size_t sizeInBytes; - ma_result result; - - if (pRB == NULL) { - return MA_INVALID_ARGS; - } - - sizeInBytes = *pSizeInFrames * ma_pcm_rb_get_bpf(pRB); - - result = ma_rb_acquire_write(&pRB->rb, &sizeInBytes, ppBufferOut); - if (result != MA_SUCCESS) { - return result; - } - - *pSizeInFrames = (ma_uint32)(sizeInBytes / ma_pcm_rb_get_bpf(pRB)); - return MA_SUCCESS; -} - -MA_API ma_result ma_pcm_rb_commit_write(ma_pcm_rb* pRB, ma_uint32 sizeInFrames) -{ - if (pRB == NULL) { - return MA_INVALID_ARGS; - } - - return ma_rb_commit_write(&pRB->rb, sizeInFrames * ma_pcm_rb_get_bpf(pRB)); -} - -MA_API ma_result ma_pcm_rb_seek_read(ma_pcm_rb* pRB, ma_uint32 offsetInFrames) -{ - if (pRB == NULL) { - return MA_INVALID_ARGS; - } - - return ma_rb_seek_read(&pRB->rb, offsetInFrames * ma_pcm_rb_get_bpf(pRB)); -} - -MA_API ma_result ma_pcm_rb_seek_write(ma_pcm_rb* pRB, ma_uint32 offsetInFrames) -{ - if (pRB == NULL) { - return MA_INVALID_ARGS; - } - - return ma_rb_seek_write(&pRB->rb, offsetInFrames * ma_pcm_rb_get_bpf(pRB)); -} - -MA_API ma_int32 ma_pcm_rb_pointer_distance(ma_pcm_rb* pRB) -{ - if (pRB == NULL) { - return 0; - } - - return ma_rb_pointer_distance(&pRB->rb) / ma_pcm_rb_get_bpf(pRB); -} - -MA_API ma_uint32 ma_pcm_rb_available_read(ma_pcm_rb* pRB) -{ - if (pRB == NULL) { - return 0; - } - - return ma_rb_available_read(&pRB->rb) / ma_pcm_rb_get_bpf(pRB); -} - -MA_API ma_uint32 ma_pcm_rb_available_write(ma_pcm_rb* pRB) -{ - if (pRB == NULL) { - return 0; - } - - return ma_rb_available_write(&pRB->rb) / ma_pcm_rb_get_bpf(pRB); -} - -MA_API ma_uint32 ma_pcm_rb_get_subbuffer_size(ma_pcm_rb* pRB) -{ - if (pRB == NULL) { - return 0; - } - - return (ma_uint32)(ma_rb_get_subbuffer_size(&pRB->rb) / ma_pcm_rb_get_bpf(pRB)); -} - -MA_API ma_uint32 ma_pcm_rb_get_subbuffer_stride(ma_pcm_rb* pRB) -{ - if (pRB == NULL) { - return 0; - } - - return (ma_uint32)(ma_rb_get_subbuffer_stride(&pRB->rb) / ma_pcm_rb_get_bpf(pRB)); -} - -MA_API ma_uint32 ma_pcm_rb_get_subbuffer_offset(ma_pcm_rb* pRB, ma_uint32 subbufferIndex) -{ - if (pRB == NULL) { - return 0; - } - - return (ma_uint32)(ma_rb_get_subbuffer_offset(&pRB->rb, subbufferIndex) / ma_pcm_rb_get_bpf(pRB)); -} - -MA_API void* ma_pcm_rb_get_subbuffer_ptr(ma_pcm_rb* pRB, ma_uint32 subbufferIndex, void* pBuffer) -{ - if (pRB == NULL) { - return NULL; - } - - return ma_rb_get_subbuffer_ptr(&pRB->rb, subbufferIndex, pBuffer); -} - -MA_API ma_format ma_pcm_rb_get_format(const ma_pcm_rb* pRB) -{ - if (pRB == NULL) { - return ma_format_unknown; - } - - return pRB->format; -} - -MA_API ma_uint32 ma_pcm_rb_get_channels(const ma_pcm_rb* pRB) -{ - if (pRB == NULL) { - return 0; - } - - return pRB->channels; -} - -MA_API ma_uint32 ma_pcm_rb_get_sample_rate(const ma_pcm_rb* pRB) -{ - if (pRB == NULL) { - return 0; - } - - return pRB->sampleRate; -} - -MA_API void ma_pcm_rb_set_sample_rate(ma_pcm_rb* pRB, ma_uint32 sampleRate) -{ - if (pRB == NULL) { - return; - } - - pRB->sampleRate = sampleRate; -} - - - -MA_API ma_result ma_duplex_rb_init(ma_format captureFormat, ma_uint32 captureChannels, ma_uint32 sampleRate, ma_uint32 captureInternalSampleRate, ma_uint32 captureInternalPeriodSizeInFrames, const ma_allocation_callbacks* pAllocationCallbacks, ma_duplex_rb* pRB) -{ - ma_result result; - ma_uint32 sizeInFrames; - - sizeInFrames = (ma_uint32)ma_calculate_frame_count_after_resampling(sampleRate, captureInternalSampleRate, captureInternalPeriodSizeInFrames * 5); - if (sizeInFrames == 0) { - return MA_INVALID_ARGS; - } - - result = ma_pcm_rb_init(captureFormat, captureChannels, sizeInFrames, NULL, pAllocationCallbacks, &pRB->rb); - if (result != MA_SUCCESS) { - return result; - } - - /* Seek forward a bit so we have a bit of a buffer in case of desyncs. */ - ma_pcm_rb_seek_write((ma_pcm_rb*)pRB, captureInternalPeriodSizeInFrames * 2); - - return MA_SUCCESS; -} - -MA_API ma_result ma_duplex_rb_uninit(ma_duplex_rb* pRB) -{ - ma_pcm_rb_uninit((ma_pcm_rb*)pRB); - return MA_SUCCESS; -} - - - -/************************************************************************************************************************************************************** - -Miscellaneous Helpers - -**************************************************************************************************************************************************************/ -MA_API const char* ma_result_description(ma_result result) -{ - switch (result) - { - case MA_SUCCESS: return "No error"; - case MA_ERROR: return "Unknown error"; - case MA_INVALID_ARGS: return "Invalid argument"; - case MA_INVALID_OPERATION: return "Invalid operation"; - case MA_OUT_OF_MEMORY: return "Out of memory"; - case MA_OUT_OF_RANGE: return "Out of range"; - case MA_ACCESS_DENIED: return "Permission denied"; - case MA_DOES_NOT_EXIST: return "Resource does not exist"; - case MA_ALREADY_EXISTS: return "Resource already exists"; - case MA_TOO_MANY_OPEN_FILES: return "Too many open files"; - case MA_INVALID_FILE: return "Invalid file"; - case MA_TOO_BIG: return "Too large"; - case MA_PATH_TOO_LONG: return "Path too long"; - case MA_NAME_TOO_LONG: return "Name too long"; - case MA_NOT_DIRECTORY: return "Not a directory"; - case MA_IS_DIRECTORY: return "Is a directory"; - case MA_DIRECTORY_NOT_EMPTY: return "Directory not empty"; - case MA_AT_END: return "At end"; - case MA_NO_SPACE: return "No space available"; - case MA_BUSY: return "Device or resource busy"; - case MA_IO_ERROR: return "Input/output error"; - case MA_INTERRUPT: return "Interrupted"; - case MA_UNAVAILABLE: return "Resource unavailable"; - case MA_ALREADY_IN_USE: return "Resource already in use"; - case MA_BAD_ADDRESS: return "Bad address"; - case MA_BAD_SEEK: return "Illegal seek"; - case MA_BAD_PIPE: return "Broken pipe"; - case MA_DEADLOCK: return "Deadlock"; - case MA_TOO_MANY_LINKS: return "Too many links"; - case MA_NOT_IMPLEMENTED: return "Not implemented"; - case MA_NO_MESSAGE: return "No message of desired type"; - case MA_BAD_MESSAGE: return "Invalid message"; - case MA_NO_DATA_AVAILABLE: return "No data available"; - case MA_INVALID_DATA: return "Invalid data"; - case MA_TIMEOUT: return "Timeout"; - case MA_NO_NETWORK: return "Network unavailable"; - case MA_NOT_UNIQUE: return "Not unique"; - case MA_NOT_SOCKET: return "Socket operation on non-socket"; - case MA_NO_ADDRESS: return "Destination address required"; - case MA_BAD_PROTOCOL: return "Protocol wrong type for socket"; - case MA_PROTOCOL_UNAVAILABLE: return "Protocol not available"; - case MA_PROTOCOL_NOT_SUPPORTED: return "Protocol not supported"; - case MA_PROTOCOL_FAMILY_NOT_SUPPORTED: return "Protocol family not supported"; - case MA_ADDRESS_FAMILY_NOT_SUPPORTED: return "Address family not supported"; - case MA_SOCKET_NOT_SUPPORTED: return "Socket type not supported"; - case MA_CONNECTION_RESET: return "Connection reset"; - case MA_ALREADY_CONNECTED: return "Already connected"; - case MA_NOT_CONNECTED: return "Not connected"; - case MA_CONNECTION_REFUSED: return "Connection refused"; - case MA_NO_HOST: return "No host"; - case MA_IN_PROGRESS: return "Operation in progress"; - case MA_CANCELLED: return "Operation cancelled"; - case MA_MEMORY_ALREADY_MAPPED: return "Memory already mapped"; - - case MA_FORMAT_NOT_SUPPORTED: return "Format not supported"; - case MA_DEVICE_TYPE_NOT_SUPPORTED: return "Device type not supported"; - case MA_SHARE_MODE_NOT_SUPPORTED: return "Share mode not supported"; - case MA_NO_BACKEND: return "No backend"; - case MA_NO_DEVICE: return "No device"; - case MA_API_NOT_FOUND: return "API not found"; - case MA_INVALID_DEVICE_CONFIG: return "Invalid device config"; - - case MA_DEVICE_NOT_INITIALIZED: return "Device not initialized"; - case MA_DEVICE_NOT_STARTED: return "Device not started"; - - case MA_FAILED_TO_INIT_BACKEND: return "Failed to initialize backend"; - case MA_FAILED_TO_OPEN_BACKEND_DEVICE: return "Failed to open backend device"; - case MA_FAILED_TO_START_BACKEND_DEVICE: return "Failed to start backend device"; - case MA_FAILED_TO_STOP_BACKEND_DEVICE: return "Failed to stop backend device"; - - default: return "Unknown error"; - } -} - -MA_API void* ma_malloc(size_t sz, const ma_allocation_callbacks* pAllocationCallbacks) -{ - if (pAllocationCallbacks != NULL) { - if (pAllocationCallbacks->onMalloc != NULL) { - return pAllocationCallbacks->onMalloc(sz, pAllocationCallbacks->pUserData); - } else { - return NULL; /* Do not fall back to the default implementation. */ - } - } else { - return ma__malloc_default(sz, NULL); - } -} - -MA_API void* ma_calloc(size_t sz, const ma_allocation_callbacks* pAllocationCallbacks) -{ - void* p = ma_malloc(sz, pAllocationCallbacks); - if (p != NULL) { - MA_ZERO_MEMORY(p, sz); - } - - return p; -} - -MA_API void* ma_realloc(void* p, size_t sz, const ma_allocation_callbacks* pAllocationCallbacks) -{ - if (pAllocationCallbacks != NULL) { - if (pAllocationCallbacks->onRealloc != NULL) { - return pAllocationCallbacks->onRealloc(p, sz, pAllocationCallbacks->pUserData); - } else { - return NULL; /* Do not fall back to the default implementation. */ - } - } else { - return ma__realloc_default(p, sz, NULL); - } -} - -MA_API void ma_free(void* p, const ma_allocation_callbacks* pAllocationCallbacks) -{ - if (p == NULL) { - return; - } - - if (pAllocationCallbacks != NULL) { - if (pAllocationCallbacks->onFree != NULL) { - pAllocationCallbacks->onFree(p, pAllocationCallbacks->pUserData); - } else { - return; /* Do no fall back to the default implementation. */ - } - } else { - ma__free_default(p, NULL); - } -} - -MA_API void* ma_aligned_malloc(size_t sz, size_t alignment, const ma_allocation_callbacks* pAllocationCallbacks) -{ - size_t extraBytes; - void* pUnaligned; - void* pAligned; - - if (alignment == 0) { - return 0; - } - - extraBytes = alignment-1 + sizeof(void*); - - pUnaligned = ma_malloc(sz + extraBytes, pAllocationCallbacks); - if (pUnaligned == NULL) { - return NULL; - } - - pAligned = (void*)(((ma_uintptr)pUnaligned + extraBytes) & ~((ma_uintptr)(alignment-1))); - ((void**)pAligned)[-1] = pUnaligned; - - return pAligned; -} - -MA_API void ma_aligned_free(void* p, const ma_allocation_callbacks* pAllocationCallbacks) -{ - ma_free(((void**)p)[-1], pAllocationCallbacks); -} - -MA_API const char* ma_get_format_name(ma_format format) -{ - switch (format) - { - case ma_format_unknown: return "Unknown"; - case ma_format_u8: return "8-bit Unsigned Integer"; - case ma_format_s16: return "16-bit Signed Integer"; - case ma_format_s24: return "24-bit Signed Integer (Tightly Packed)"; - case ma_format_s32: return "32-bit Signed Integer"; - case ma_format_f32: return "32-bit IEEE Floating Point"; - default: return "Invalid"; - } -} - -MA_API void ma_blend_f32(float* pOut, float* pInA, float* pInB, float factor, ma_uint32 channels) -{ - ma_uint32 i; - for (i = 0; i < channels; ++i) { - pOut[i] = ma_mix_f32(pInA[i], pInB[i], factor); - } -} - - -MA_API ma_uint32 ma_get_bytes_per_sample(ma_format format) -{ - ma_uint32 sizes[] = { - 0, /* unknown */ - 1, /* u8 */ - 2, /* s16 */ - 3, /* s24 */ - 4, /* s32 */ - 4, /* f32 */ - }; - return sizes[format]; -} - - - -#define MA_DATA_SOURCE_DEFAULT_RANGE_BEG 0 -#define MA_DATA_SOURCE_DEFAULT_RANGE_END ~((ma_uint64)0) -#define MA_DATA_SOURCE_DEFAULT_LOOP_POINT_BEG 0 -#define MA_DATA_SOURCE_DEFAULT_LOOP_POINT_END ~((ma_uint64)0) - -MA_API ma_data_source_config ma_data_source_config_init(void) -{ - ma_data_source_config config; - - MA_ZERO_OBJECT(&config); - - return config; -} - - -MA_API ma_result ma_data_source_init(const ma_data_source_config* pConfig, ma_data_source* pDataSource) -{ - ma_data_source_base* pDataSourceBase = (ma_data_source_base*)pDataSource; - - if (pDataSource == NULL) { - return MA_INVALID_ARGS; - } - - MA_ZERO_OBJECT(pDataSourceBase); - - if (pConfig == NULL) { - return MA_INVALID_ARGS; - } - - if (pConfig->vtable == NULL) { - return MA_INVALID_ARGS; - } - - pDataSourceBase->vtable = pConfig->vtable; - pDataSourceBase->rangeBegInFrames = MA_DATA_SOURCE_DEFAULT_RANGE_BEG; - pDataSourceBase->rangeEndInFrames = MA_DATA_SOURCE_DEFAULT_RANGE_END; - pDataSourceBase->loopBegInFrames = MA_DATA_SOURCE_DEFAULT_LOOP_POINT_BEG; - pDataSourceBase->loopEndInFrames = MA_DATA_SOURCE_DEFAULT_LOOP_POINT_END; - pDataSourceBase->pCurrent = pDataSource; /* Always read from ourself by default. */ - pDataSourceBase->pNext = NULL; - pDataSourceBase->onGetNext = NULL; - - return MA_SUCCESS; -} - -MA_API void ma_data_source_uninit(ma_data_source* pDataSource) -{ - if (pDataSource == NULL) { - return; - } - - /* - This is placeholder in case we need this later. Data sources need to call this in their - uninitialization routine to ensure things work later on if something is added here. - */ -} - -static ma_result ma_data_source_resolve_current(ma_data_source* pDataSource, ma_data_source** ppCurrentDataSource) -{ - ma_data_source_base* pCurrentDataSource = (ma_data_source_base*)pDataSource; - - MA_ASSERT(pDataSource != NULL); - MA_ASSERT(ppCurrentDataSource != NULL); - - if (pCurrentDataSource->pCurrent == NULL) { - /* - The current data source is NULL. If we're using this in the context of a chain we need to return NULL - here so that we don't end up looping. Otherwise we just return the data source itself. - */ - if (pCurrentDataSource->pNext != NULL || pCurrentDataSource->onGetNext != NULL) { - pCurrentDataSource = NULL; - } else { - pCurrentDataSource = (ma_data_source_base*)pDataSource; /* Not being used in a chain. Make sure we just always read from the data source itself at all times. */ - } - } else { - pCurrentDataSource = (ma_data_source_base*)pCurrentDataSource->pCurrent; - } - - *ppCurrentDataSource = pCurrentDataSource; - - return MA_SUCCESS; -} - -static ma_result ma_data_source_read_pcm_frames_from_backend(ma_data_source* pDataSource, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead) -{ - ma_data_source_base* pDataSourceBase = (ma_data_source_base*)pDataSource; - - MA_ASSERT(pDataSourceBase != NULL); - MA_ASSERT(pDataSourceBase->vtable != NULL); - MA_ASSERT(pDataSourceBase->vtable->onRead != NULL); - MA_ASSERT(pFramesRead != NULL); - - if (pFramesOut != NULL) { - return pDataSourceBase->vtable->onRead(pDataSourceBase, pFramesOut, frameCount, pFramesRead); - } else { - /* - No output buffer. Probably seeking forward. Read and discard. Can probably optimize this in terms of - onSeek and onGetCursor, but need to keep in mind that the data source may not implement these functions. - */ - ma_result result; - ma_uint64 framesRead; - ma_format format; - ma_uint32 channels; - ma_uint64 discardBufferCapInFrames; - ma_uint8 pDiscardBuffer[4096]; - - result = ma_data_source_get_data_format(pDataSource, &format, &channels, NULL, NULL, 0); - if (result != MA_SUCCESS) { - return result; - } - - discardBufferCapInFrames = sizeof(pDiscardBuffer) / ma_get_bytes_per_frame(format, channels); - - framesRead = 0; - while (framesRead < frameCount) { - ma_uint64 framesReadThisIteration = 0; - ma_uint64 framesToRead = frameCount - framesRead; - if (framesToRead > discardBufferCapInFrames) { - framesToRead = discardBufferCapInFrames; - } - - result = pDataSourceBase->vtable->onRead(pDataSourceBase, pDiscardBuffer, framesToRead, &framesReadThisIteration); - if (result != MA_SUCCESS) { - return result; - } - - framesRead += framesReadThisIteration; - } - - *pFramesRead = framesRead; - - return MA_SUCCESS; - } -} - -static ma_result ma_data_source_read_pcm_frames_within_range(ma_data_source* pDataSource, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead) -{ - ma_data_source_base* pDataSourceBase = (ma_data_source_base*)pDataSource; - ma_result result; - ma_uint64 framesRead = 0; - ma_bool32 loop = ma_data_source_is_looping(pDataSource); - - if (pDataSourceBase == NULL) { - return MA_AT_END; - } - - if (frameCount == 0) { - return MA_INVALID_ARGS; - } - - MA_ASSERT(pDataSourceBase->vtable != NULL); - - if ((pDataSourceBase->vtable->flags & MA_DATA_SOURCE_SELF_MANAGED_RANGE_AND_LOOP_POINT) != 0 || (pDataSourceBase->rangeEndInFrames == ~((ma_uint64)0) && (pDataSourceBase->loopEndInFrames == ~((ma_uint64)0) || loop == MA_FALSE))) { - /* Either the data source is self-managing the range, or no range is set - just read like normal. The data source itself will tell us when the end is reached. */ - result = ma_data_source_read_pcm_frames_from_backend(pDataSource, pFramesOut, frameCount, &framesRead); - } else { - /* Need to clamp to within the range. */ - ma_uint64 relativeCursor; - ma_uint64 absoluteCursor; - - result = ma_data_source_get_cursor_in_pcm_frames(pDataSourceBase, &relativeCursor); - if (result != MA_SUCCESS) { - /* Failed to retrieve the cursor. Cannot read within a range or loop points. Just read like normal - this may happen for things like noise data sources where it doesn't really matter. */ - result = ma_data_source_read_pcm_frames_from_backend(pDataSource, pFramesOut, frameCount, &framesRead); - } else { - ma_uint64 rangeBeg; - ma_uint64 rangeEnd; - - /* We have the cursor. We need to make sure we don't read beyond our range. */ - rangeBeg = pDataSourceBase->rangeBegInFrames; - rangeEnd = pDataSourceBase->rangeEndInFrames; - - absoluteCursor = rangeBeg + relativeCursor; - - /* If looping, make sure we're within range. */ - if (loop) { - if (pDataSourceBase->loopEndInFrames != ~((ma_uint64)0)) { - rangeEnd = ma_min(rangeEnd, pDataSourceBase->rangeBegInFrames + pDataSourceBase->loopEndInFrames); - } - } - - if (frameCount > (rangeEnd - absoluteCursor) && rangeEnd != ~((ma_uint64)0)) { - frameCount = (rangeEnd - absoluteCursor); - } - - /* - If the cursor is sitting on the end of the range the frame count will be set to 0 which can - result in MA_INVALID_ARGS. In this case, we don't want to try reading, but instead return - MA_AT_END so the higher level function can know about it. - */ - if (frameCount > 0) { - result = ma_data_source_read_pcm_frames_from_backend(pDataSource, pFramesOut, frameCount, &framesRead); - } else { - result = MA_AT_END; /* The cursor is sitting on the end of the range which means we're at the end. */ - } - } - } - - if (pFramesRead != NULL) { - *pFramesRead = framesRead; - } - - /* We need to make sure MA_AT_END is returned if we hit the end of the range. */ - if (result == MA_SUCCESS && framesRead == 0) { - result = MA_AT_END; - } - - return result; -} - -MA_API ma_result ma_data_source_read_pcm_frames(ma_data_source* pDataSource, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead) -{ - ma_result result = MA_SUCCESS; - ma_data_source_base* pDataSourceBase = (ma_data_source_base*)pDataSource; - ma_data_source_base* pCurrentDataSource; - void* pRunningFramesOut = pFramesOut; - ma_uint64 totalFramesProcessed = 0; - ma_format format; - ma_uint32 channels; - ma_uint32 emptyLoopCounter = 0; /* Keeps track of how many times 0 frames have been read. For infinite loop detection of sounds with no audio data. */ - ma_bool32 loop; - - if (pFramesRead != NULL) { - *pFramesRead = 0; - } - - if (frameCount == 0) { - return MA_INVALID_ARGS; - } - - if (pDataSourceBase == NULL) { - return MA_INVALID_ARGS; - } - - loop = ma_data_source_is_looping(pDataSource); - - /* - We need to know the data format so we can advance the output buffer as we read frames. If this - fails, chaining will not work and we'll just read as much as we can from the current source. - */ - if (ma_data_source_get_data_format(pDataSource, &format, &channels, NULL, NULL, 0) != MA_SUCCESS) { - result = ma_data_source_resolve_current(pDataSource, (ma_data_source**)&pCurrentDataSource); - if (result != MA_SUCCESS) { - return result; - } - - return ma_data_source_read_pcm_frames_within_range(pCurrentDataSource, pFramesOut, frameCount, pFramesRead); - } - - /* - Looping is a bit of a special case. When the `loop` argument is true, chaining will not work and - only the current data source will be read from. - */ - - /* Keep reading until we've read as many frames as possible. */ - while (totalFramesProcessed < frameCount) { - ma_uint64 framesProcessed; - ma_uint64 framesRemaining = frameCount - totalFramesProcessed; - - /* We need to resolve the data source that we'll actually be reading from. */ - result = ma_data_source_resolve_current(pDataSource, (ma_data_source**)&pCurrentDataSource); - if (result != MA_SUCCESS) { - break; - } - - if (pCurrentDataSource == NULL) { - break; - } - - result = ma_data_source_read_pcm_frames_within_range(pCurrentDataSource, pRunningFramesOut, framesRemaining, &framesProcessed); - totalFramesProcessed += framesProcessed; - - /* - If we encountered an error from the read callback, make sure it's propagated to the caller. The caller may need to know whether or not MA_BUSY is returned which is - not necessarily considered an error. - */ - if (result != MA_SUCCESS && result != MA_AT_END) { - break; - } - - /* - We can determine if we've reached the end by checking if ma_data_source_read_pcm_frames_within_range() returned - MA_AT_END. To loop back to the start, all we need to do is seek back to the first frame. - */ - if (result == MA_AT_END) { - /* - The result needs to be reset back to MA_SUCCESS (from MA_AT_END) so that we don't - accidentally return MA_AT_END when data has been read in prior loop iterations. at the - end of this function, the result will be checked for MA_SUCCESS, and if the total - number of frames processed is 0, will be explicitly set to MA_AT_END. - */ - result = MA_SUCCESS; - - /* - We reached the end. If we're looping, we just loop back to the start of the current - data source. If we're not looping we need to check if we have another in the chain, and - if so, switch to it. - */ - if (loop) { - if (framesProcessed == 0) { - emptyLoopCounter += 1; - if (emptyLoopCounter > 1) { - break; /* Infinite loop detected. Get out. */ - } - } else { - emptyLoopCounter = 0; - } - - result = ma_data_source_seek_to_pcm_frame(pCurrentDataSource, pCurrentDataSource->loopBegInFrames); - if (result != MA_SUCCESS) { - break; /* Failed to loop. Abort. */ - } - - /* Don't return MA_AT_END for looping sounds. */ - result = MA_SUCCESS; - } else { - if (pCurrentDataSource->pNext != NULL) { - pDataSourceBase->pCurrent = pCurrentDataSource->pNext; - } else if (pCurrentDataSource->onGetNext != NULL) { - pDataSourceBase->pCurrent = pCurrentDataSource->onGetNext(pCurrentDataSource); - if (pDataSourceBase->pCurrent == NULL) { - break; /* Our callback did not return a next data source. We're done. */ - } - } else { - /* Reached the end of the chain. We're done. */ - break; - } - - /* The next data source needs to be rewound to ensure data is read in looping scenarios. */ - result = ma_data_source_seek_to_pcm_frame(pDataSourceBase->pCurrent, 0); - if (result != MA_SUCCESS) { - break; - } - } - } - - if (pRunningFramesOut != NULL) { - pRunningFramesOut = ma_offset_ptr(pRunningFramesOut, framesProcessed * ma_get_bytes_per_frame(format, channels)); - } - } - - if (pFramesRead != NULL) { - *pFramesRead = totalFramesProcessed; - } - - MA_ASSERT(!(result == MA_AT_END && totalFramesProcessed > 0)); /* We should never be returning MA_AT_END if we read some data. */ - - if (result == MA_SUCCESS && totalFramesProcessed == 0) { - result = MA_AT_END; - } - - return result; -} - -MA_API ma_result ma_data_source_seek_pcm_frames(ma_data_source* pDataSource, ma_uint64 frameCount, ma_uint64* pFramesSeeked) -{ - return ma_data_source_read_pcm_frames(pDataSource, NULL, frameCount, pFramesSeeked); -} - -MA_API ma_result ma_data_source_seek_to_pcm_frame(ma_data_source* pDataSource, ma_uint64 frameIndex) -{ - ma_data_source_base* pDataSourceBase = (ma_data_source_base*)pDataSource; - - if (pDataSourceBase == NULL) { - return MA_INVALID_ARGS; - } - - if (pDataSourceBase->vtable->onSeek == NULL) { - return MA_NOT_IMPLEMENTED; - } - - if (frameIndex > pDataSourceBase->rangeEndInFrames) { - return MA_INVALID_OPERATION; /* Trying to seek too far forward. */ - } - - MA_ASSERT(pDataSourceBase->vtable != NULL); - - return pDataSourceBase->vtable->onSeek(pDataSource, pDataSourceBase->rangeBegInFrames + frameIndex); -} - -MA_API ma_result ma_data_source_seek_seconds(ma_data_source* pDataSource, float secondCount, float* pSecondsSeeked) -{ - ma_uint64 frameCount; - ma_uint64 framesSeeked = 0; - ma_uint32 sampleRate; - ma_result result; - - if (pDataSource == NULL) { - return MA_INVALID_ARGS; - } - - result = ma_data_source_get_data_format(pDataSource, NULL, NULL, &sampleRate, NULL, 0); - if (result != MA_SUCCESS) { - return result; - } - - /* We need PCM frames instead of seconds */ - frameCount = (ma_uint64)(secondCount * sampleRate); - - result = ma_data_source_seek_pcm_frames(pDataSource, frameCount, &framesSeeked); - - /* VC6 doesn't support division between unsigned 64-bit integer and floating point number. Signed integer needed. This shouldn't affect anything in practice */ - *pSecondsSeeked = (ma_int64)framesSeeked / (float)sampleRate; - return result; -} - -MA_API ma_result ma_data_source_seek_to_second(ma_data_source* pDataSource, float seekPointInSeconds) -{ - ma_uint64 frameIndex; - ma_uint32 sampleRate; - ma_result result; - - if (pDataSource == NULL) { - return MA_INVALID_ARGS; - } - - result = ma_data_source_get_data_format(pDataSource, NULL, NULL, &sampleRate, NULL, 0); - if (result != MA_SUCCESS) { - return result; - } - - /* We need PCM frames instead of seconds */ - frameIndex = (ma_uint64)(seekPointInSeconds * sampleRate); - - return ma_data_source_seek_to_pcm_frame(pDataSource, frameIndex); -} - -MA_API ma_result ma_data_source_get_data_format(ma_data_source* pDataSource, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate, ma_channel* pChannelMap, size_t channelMapCap) -{ - ma_data_source_base* pDataSourceBase = (ma_data_source_base*)pDataSource; - ma_result result; - ma_format format; - ma_uint32 channels; - ma_uint32 sampleRate; - - /* Initialize to defaults for safety just in case the data source does not implement this callback. */ - if (pFormat != NULL) { - *pFormat = ma_format_unknown; - } - if (pChannels != NULL) { - *pChannels = 0; - } - if (pSampleRate != NULL) { - *pSampleRate = 0; - } - if (pChannelMap != NULL) { - MA_ZERO_MEMORY(pChannelMap, sizeof(*pChannelMap) * channelMapCap); - } - - if (pDataSourceBase == NULL) { - return MA_INVALID_ARGS; - } - - MA_ASSERT(pDataSourceBase->vtable != NULL); - - if (pDataSourceBase->vtable->onGetDataFormat == NULL) { - return MA_NOT_IMPLEMENTED; - } - - result = pDataSourceBase->vtable->onGetDataFormat(pDataSource, &format, &channels, &sampleRate, pChannelMap, channelMapCap); - if (result != MA_SUCCESS) { - return result; - } - - if (pFormat != NULL) { - *pFormat = format; - } - if (pChannels != NULL) { - *pChannels = channels; - } - if (pSampleRate != NULL) { - *pSampleRate = sampleRate; - } - - /* Channel map was passed in directly to the callback. This is safe due to the channelMapCap parameter. */ - - return MA_SUCCESS; -} - -MA_API ma_result ma_data_source_get_cursor_in_pcm_frames(ma_data_source* pDataSource, ma_uint64* pCursor) -{ - ma_data_source_base* pDataSourceBase = (ma_data_source_base*)pDataSource; - ma_result result; - ma_uint64 cursor; - - if (pCursor == NULL) { - return MA_INVALID_ARGS; - } - - *pCursor = 0; - - if (pDataSourceBase == NULL) { - return MA_SUCCESS; - } - - MA_ASSERT(pDataSourceBase->vtable != NULL); - - if (pDataSourceBase->vtable->onGetCursor == NULL) { - return MA_NOT_IMPLEMENTED; - } - - result = pDataSourceBase->vtable->onGetCursor(pDataSourceBase, &cursor); - if (result != MA_SUCCESS) { - return result; - } - - /* The cursor needs to be made relative to the start of the range. */ - if (cursor < pDataSourceBase->rangeBegInFrames) { /* Safety check so we don't return some huge number. */ - *pCursor = 0; - } else { - *pCursor = cursor - pDataSourceBase->rangeBegInFrames; - } - - return MA_SUCCESS; -} - -MA_API ma_result ma_data_source_get_length_in_pcm_frames(ma_data_source* pDataSource, ma_uint64* pLength) -{ - ma_data_source_base* pDataSourceBase = (ma_data_source_base*)pDataSource; - - if (pLength == NULL) { - return MA_INVALID_ARGS; - } - - *pLength = 0; - - if (pDataSourceBase == NULL) { - return MA_INVALID_ARGS; - } - - MA_ASSERT(pDataSourceBase->vtable != NULL); - - /* - If we have a range defined we'll use that to determine the length. This is one of rare times - where we'll actually trust the caller. If they've set the range, I think it's mostly safe to - assume they've set it based on some higher level knowledge of the structure of the sound bank. - */ - if (pDataSourceBase->rangeEndInFrames != ~((ma_uint64)0)) { - *pLength = pDataSourceBase->rangeEndInFrames - pDataSourceBase->rangeBegInFrames; - return MA_SUCCESS; - } - - /* - Getting here means a range is not defined so we'll need to get the data source itself to tell - us the length. - */ - if (pDataSourceBase->vtable->onGetLength == NULL) { - return MA_NOT_IMPLEMENTED; - } - - return pDataSourceBase->vtable->onGetLength(pDataSource, pLength); -} - -MA_API ma_result ma_data_source_get_cursor_in_seconds(ma_data_source* pDataSource, float* pCursor) -{ - ma_result result; - ma_uint64 cursorInPCMFrames; - ma_uint32 sampleRate; - - if (pCursor == NULL) { - return MA_INVALID_ARGS; - } - - *pCursor = 0; - - result = ma_data_source_get_cursor_in_pcm_frames(pDataSource, &cursorInPCMFrames); - if (result != MA_SUCCESS) { - return result; - } - - result = ma_data_source_get_data_format(pDataSource, NULL, NULL, &sampleRate, NULL, 0); - if (result != MA_SUCCESS) { - return result; - } - - /* VC6 does not support division of unsigned 64-bit integers with floating point numbers. Need to use a signed number. This shouldn't effect anything in practice. */ - *pCursor = (ma_int64)cursorInPCMFrames / (float)sampleRate; - - return MA_SUCCESS; -} - -MA_API ma_result ma_data_source_get_length_in_seconds(ma_data_source* pDataSource, float* pLength) -{ - ma_result result; - ma_uint64 lengthInPCMFrames; - ma_uint32 sampleRate; - - if (pLength == NULL) { - return MA_INVALID_ARGS; - } - - *pLength = 0; - - result = ma_data_source_get_length_in_pcm_frames(pDataSource, &lengthInPCMFrames); - if (result != MA_SUCCESS) { - return result; - } - - result = ma_data_source_get_data_format(pDataSource, NULL, NULL, &sampleRate, NULL, 0); - if (result != MA_SUCCESS) { - return result; - } - - /* VC6 does not support division of unsigned 64-bit integers with floating point numbers. Need to use a signed number. This shouldn't effect anything in practice. */ - *pLength = (ma_int64)lengthInPCMFrames / (float)sampleRate; - - return MA_SUCCESS; -} - -MA_API ma_result ma_data_source_set_looping(ma_data_source* pDataSource, ma_bool32 isLooping) -{ - ma_data_source_base* pDataSourceBase = (ma_data_source_base*)pDataSource; - - if (pDataSource == NULL) { - return MA_INVALID_ARGS; - } - - ma_atomic_exchange_32(&pDataSourceBase->isLooping, isLooping); - - MA_ASSERT(pDataSourceBase->vtable != NULL); - - /* If there's no callback for this just treat it as a successful no-op. */ - if (pDataSourceBase->vtable->onSetLooping == NULL) { - return MA_SUCCESS; - } - - return pDataSourceBase->vtable->onSetLooping(pDataSource, isLooping); -} - -MA_API ma_bool32 ma_data_source_is_looping(const ma_data_source* pDataSource) -{ - const ma_data_source_base* pDataSourceBase = (const ma_data_source_base*)pDataSource; - - if (pDataSource == NULL) { - return MA_FALSE; - } - - return ma_atomic_load_32(&pDataSourceBase->isLooping); -} - -MA_API ma_result ma_data_source_set_range_in_pcm_frames(ma_data_source* pDataSource, ma_uint64 rangeBegInFrames, ma_uint64 rangeEndInFrames) -{ - ma_data_source_base* pDataSourceBase = (ma_data_source_base*)pDataSource; - ma_result result; - ma_uint64 relativeCursor; - ma_uint64 absoluteCursor; - ma_bool32 doSeekAdjustment = MA_FALSE; - - if (pDataSource == NULL) { - return MA_INVALID_ARGS; - } - - if (rangeEndInFrames < rangeBegInFrames) { - return MA_INVALID_ARGS; /* The end of the range must come after the beginning. */ - } - - /* - We may need to adjust the position of the cursor to ensure it's clamped to the range. Grab it now - so we can calculate its absolute position before we change the range. - */ - result = ma_data_source_get_cursor_in_pcm_frames(pDataSource, &relativeCursor); - if (result == MA_SUCCESS) { - doSeekAdjustment = MA_TRUE; - absoluteCursor = relativeCursor + pDataSourceBase->rangeBegInFrames; - } else { - /* - We couldn't get the position of the cursor. It probably means the data source has no notion - of a cursor. We'll just leave it at position 0. Don't treat this as an error. - */ - doSeekAdjustment = MA_FALSE; - relativeCursor = 0; - absoluteCursor = 0; - } - - pDataSourceBase->rangeBegInFrames = rangeBegInFrames; - pDataSourceBase->rangeEndInFrames = rangeEndInFrames; - - /* - The commented out logic below was intended to maintain loop points in response to a change in the - range. However, this is not useful because it results in the sound breaking when you move the range - outside of the old loop points. I'm simplifying this by simply resetting the loop points. The - caller is expected to update their loop points if they change the range. - - In practice this should be mostly a non-issue because the majority of the time the range will be - set once right after initialization. - */ - pDataSourceBase->loopBegInFrames = 0; - pDataSourceBase->loopEndInFrames = ~((ma_uint64)0); - - - /* - Seek to within range. Note that our seek positions here are relative to the new range. We don't want - to do this if we failed to retrieve the cursor earlier on because it probably means the data source - has no notion of a cursor. In practice the seek would probably fail (which we silently ignore), but - I'm just not even going to attempt it. - */ - if (doSeekAdjustment) { - if (absoluteCursor < rangeBegInFrames) { - ma_data_source_seek_to_pcm_frame(pDataSource, 0); - } else if (absoluteCursor > rangeEndInFrames) { - ma_data_source_seek_to_pcm_frame(pDataSource, rangeEndInFrames - rangeBegInFrames); - } - } - - return MA_SUCCESS; -} - -MA_API void ma_data_source_get_range_in_pcm_frames(const ma_data_source* pDataSource, ma_uint64* pRangeBegInFrames, ma_uint64* pRangeEndInFrames) -{ - const ma_data_source_base* pDataSourceBase = (const ma_data_source_base*)pDataSource; - - if (pRangeBegInFrames != NULL) { - *pRangeBegInFrames = 0; - } - if (pRangeEndInFrames != NULL) { - *pRangeEndInFrames = 0; - } - - if (pDataSource == NULL) { - return; - } - - if (pRangeBegInFrames != NULL) { - *pRangeBegInFrames = pDataSourceBase->rangeBegInFrames; - } - - if (pRangeEndInFrames != NULL) { - *pRangeEndInFrames = pDataSourceBase->rangeEndInFrames; - } -} - -MA_API ma_result ma_data_source_set_loop_point_in_pcm_frames(ma_data_source* pDataSource, ma_uint64 loopBegInFrames, ma_uint64 loopEndInFrames) -{ - ma_data_source_base* pDataSourceBase = (ma_data_source_base*)pDataSource; - - if (pDataSource == NULL) { - return MA_INVALID_ARGS; - } - - if (loopEndInFrames < loopBegInFrames) { - return MA_INVALID_ARGS; /* The end of the loop point must come after the beginning. */ - } - - if (loopEndInFrames > pDataSourceBase->rangeEndInFrames && loopEndInFrames != ~((ma_uint64)0)) { - return MA_INVALID_ARGS; /* The end of the loop point must not go beyond the range. */ - } - - pDataSourceBase->loopBegInFrames = loopBegInFrames; - pDataSourceBase->loopEndInFrames = loopEndInFrames; - - /* The end cannot exceed the range. */ - if (pDataSourceBase->loopEndInFrames > (pDataSourceBase->rangeEndInFrames - pDataSourceBase->rangeBegInFrames) && pDataSourceBase->loopEndInFrames != ~((ma_uint64)0)) { - pDataSourceBase->loopEndInFrames = (pDataSourceBase->rangeEndInFrames - pDataSourceBase->rangeBegInFrames); - } - - return MA_SUCCESS; -} - -MA_API void ma_data_source_get_loop_point_in_pcm_frames(const ma_data_source* pDataSource, ma_uint64* pLoopBegInFrames, ma_uint64* pLoopEndInFrames) -{ - const ma_data_source_base* pDataSourceBase = (const ma_data_source_base*)pDataSource; - - if (pLoopBegInFrames != NULL) { - *pLoopBegInFrames = 0; - } - if (pLoopEndInFrames != NULL) { - *pLoopEndInFrames = 0; - } - - if (pDataSource == NULL) { - return; - } - - if (pLoopBegInFrames != NULL) { - *pLoopBegInFrames = pDataSourceBase->loopBegInFrames; - } - - if (pLoopEndInFrames != NULL) { - *pLoopEndInFrames = pDataSourceBase->loopEndInFrames; - } -} - -MA_API ma_result ma_data_source_set_current(ma_data_source* pDataSource, ma_data_source* pCurrentDataSource) -{ - ma_data_source_base* pDataSourceBase = (ma_data_source_base*)pDataSource; - - if (pDataSource == NULL) { - return MA_INVALID_ARGS; - } - - pDataSourceBase->pCurrent = pCurrentDataSource; - - return MA_SUCCESS; -} - -MA_API ma_data_source* ma_data_source_get_current(const ma_data_source* pDataSource) -{ - const ma_data_source_base* pDataSourceBase = (const ma_data_source_base*)pDataSource; - - if (pDataSource == NULL) { - return NULL; - } - - return pDataSourceBase->pCurrent; -} - -MA_API ma_result ma_data_source_set_next(ma_data_source* pDataSource, ma_data_source* pNextDataSource) -{ - ma_data_source_base* pDataSourceBase = (ma_data_source_base*)pDataSource; - - if (pDataSource == NULL) { - return MA_INVALID_ARGS; - } - - pDataSourceBase->pNext = pNextDataSource; - - return MA_SUCCESS; -} - -MA_API ma_data_source* ma_data_source_get_next(const ma_data_source* pDataSource) -{ - const ma_data_source_base* pDataSourceBase = (const ma_data_source_base*)pDataSource; - - if (pDataSource == NULL) { - return NULL; - } - - return pDataSourceBase->pNext; -} - -MA_API ma_result ma_data_source_set_next_callback(ma_data_source* pDataSource, ma_data_source_get_next_proc onGetNext) -{ - ma_data_source_base* pDataSourceBase = (ma_data_source_base*)pDataSource; - - if (pDataSource == NULL) { - return MA_INVALID_ARGS; - } - - pDataSourceBase->onGetNext = onGetNext; - - return MA_SUCCESS; -} - -MA_API ma_data_source_get_next_proc ma_data_source_get_next_callback(const ma_data_source* pDataSource) -{ - const ma_data_source_base* pDataSourceBase = (const ma_data_source_base*)pDataSource; - - if (pDataSource == NULL) { - return NULL; - } - - return pDataSourceBase->onGetNext; -} - - -static ma_result ma_audio_buffer_ref__data_source_on_read(ma_data_source* pDataSource, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead) -{ - ma_audio_buffer_ref* pAudioBufferRef = (ma_audio_buffer_ref*)pDataSource; - ma_uint64 framesRead = ma_audio_buffer_ref_read_pcm_frames(pAudioBufferRef, pFramesOut, frameCount, MA_FALSE); - - if (pFramesRead != NULL) { - *pFramesRead = framesRead; - } - - if (framesRead < frameCount || framesRead == 0) { - return MA_AT_END; - } - - return MA_SUCCESS; -} - -static ma_result ma_audio_buffer_ref__data_source_on_seek(ma_data_source* pDataSource, ma_uint64 frameIndex) -{ - return ma_audio_buffer_ref_seek_to_pcm_frame((ma_audio_buffer_ref*)pDataSource, frameIndex); -} - -static ma_result ma_audio_buffer_ref__data_source_on_get_data_format(ma_data_source* pDataSource, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate, ma_channel* pChannelMap, size_t channelMapCap) -{ - ma_audio_buffer_ref* pAudioBufferRef = (ma_audio_buffer_ref*)pDataSource; - - *pFormat = pAudioBufferRef->format; - *pChannels = pAudioBufferRef->channels; - *pSampleRate = pAudioBufferRef->sampleRate; - ma_channel_map_init_standard(ma_standard_channel_map_default, pChannelMap, channelMapCap, pAudioBufferRef->channels); - - return MA_SUCCESS; -} - -static ma_result ma_audio_buffer_ref__data_source_on_get_cursor(ma_data_source* pDataSource, ma_uint64* pCursor) -{ - ma_audio_buffer_ref* pAudioBufferRef = (ma_audio_buffer_ref*)pDataSource; - - *pCursor = pAudioBufferRef->cursor; - - return MA_SUCCESS; -} - -static ma_result ma_audio_buffer_ref__data_source_on_get_length(ma_data_source* pDataSource, ma_uint64* pLength) -{ - ma_audio_buffer_ref* pAudioBufferRef = (ma_audio_buffer_ref*)pDataSource; - - *pLength = pAudioBufferRef->sizeInFrames; - - return MA_SUCCESS; -} - -static ma_data_source_vtable g_ma_audio_buffer_ref_data_source_vtable = -{ - ma_audio_buffer_ref__data_source_on_read, - ma_audio_buffer_ref__data_source_on_seek, - ma_audio_buffer_ref__data_source_on_get_data_format, - ma_audio_buffer_ref__data_source_on_get_cursor, - ma_audio_buffer_ref__data_source_on_get_length, - NULL, /* onSetLooping */ - 0 -}; - -MA_API ma_result ma_audio_buffer_ref_init(ma_format format, ma_uint32 channels, const void* pData, ma_uint64 sizeInFrames, ma_audio_buffer_ref* pAudioBufferRef) -{ - ma_result result; - ma_data_source_config dataSourceConfig; - - if (pAudioBufferRef == NULL) { - return MA_INVALID_ARGS; - } - - MA_ZERO_OBJECT(pAudioBufferRef); - - dataSourceConfig = ma_data_source_config_init(); - dataSourceConfig.vtable = &g_ma_audio_buffer_ref_data_source_vtable; - - result = ma_data_source_init(&dataSourceConfig, &pAudioBufferRef->ds); - if (result != MA_SUCCESS) { - return result; - } - - pAudioBufferRef->format = format; - pAudioBufferRef->channels = channels; - pAudioBufferRef->sampleRate = 0; /* TODO: Version 0.12. Set this to sampleRate. */ - pAudioBufferRef->cursor = 0; - pAudioBufferRef->sizeInFrames = sizeInFrames; - pAudioBufferRef->pData = pData; - - return MA_SUCCESS; -} - -MA_API void ma_audio_buffer_ref_uninit(ma_audio_buffer_ref* pAudioBufferRef) -{ - if (pAudioBufferRef == NULL) { - return; - } - - ma_data_source_uninit(&pAudioBufferRef->ds); -} - -MA_API ma_result ma_audio_buffer_ref_set_data(ma_audio_buffer_ref* pAudioBufferRef, const void* pData, ma_uint64 sizeInFrames) -{ - if (pAudioBufferRef == NULL) { - return MA_INVALID_ARGS; - } - - pAudioBufferRef->cursor = 0; - pAudioBufferRef->sizeInFrames = sizeInFrames; - pAudioBufferRef->pData = pData; - - return MA_SUCCESS; -} - -MA_API ma_uint64 ma_audio_buffer_ref_read_pcm_frames(ma_audio_buffer_ref* pAudioBufferRef, void* pFramesOut, ma_uint64 frameCount, ma_bool32 loop) -{ - ma_uint64 totalFramesRead = 0; - - if (pAudioBufferRef == NULL) { - return 0; - } - - if (frameCount == 0) { - return 0; - } - - while (totalFramesRead < frameCount) { - ma_uint64 framesAvailable = pAudioBufferRef->sizeInFrames - pAudioBufferRef->cursor; - ma_uint64 framesRemaining = frameCount - totalFramesRead; - ma_uint64 framesToRead; - - framesToRead = framesRemaining; - if (framesToRead > framesAvailable) { - framesToRead = framesAvailable; - } - - if (pFramesOut != NULL) { - ma_copy_pcm_frames(ma_offset_ptr(pFramesOut, totalFramesRead * ma_get_bytes_per_frame(pAudioBufferRef->format, pAudioBufferRef->channels)), ma_offset_ptr(pAudioBufferRef->pData, pAudioBufferRef->cursor * ma_get_bytes_per_frame(pAudioBufferRef->format, pAudioBufferRef->channels)), framesToRead, pAudioBufferRef->format, pAudioBufferRef->channels); - } - - totalFramesRead += framesToRead; - - pAudioBufferRef->cursor += framesToRead; - if (pAudioBufferRef->cursor == pAudioBufferRef->sizeInFrames) { - if (loop) { - pAudioBufferRef->cursor = 0; - } else { - break; /* We've reached the end and we're not looping. Done. */ - } - } - - MA_ASSERT(pAudioBufferRef->cursor < pAudioBufferRef->sizeInFrames); - } - - return totalFramesRead; -} - -MA_API ma_result ma_audio_buffer_ref_seek_to_pcm_frame(ma_audio_buffer_ref* pAudioBufferRef, ma_uint64 frameIndex) -{ - if (pAudioBufferRef == NULL) { - return MA_INVALID_ARGS; - } - - if (frameIndex > pAudioBufferRef->sizeInFrames) { - return MA_INVALID_ARGS; - } - - pAudioBufferRef->cursor = (size_t)frameIndex; - - return MA_SUCCESS; -} - -MA_API ma_result ma_audio_buffer_ref_map(ma_audio_buffer_ref* pAudioBufferRef, void** ppFramesOut, ma_uint64* pFrameCount) -{ - ma_uint64 framesAvailable; - ma_uint64 frameCount = 0; - - if (ppFramesOut != NULL) { - *ppFramesOut = NULL; /* Safety. */ - } - - if (pFrameCount != NULL) { - frameCount = *pFrameCount; - *pFrameCount = 0; /* Safety. */ - } - - if (pAudioBufferRef == NULL || ppFramesOut == NULL || pFrameCount == NULL) { - return MA_INVALID_ARGS; - } - - framesAvailable = pAudioBufferRef->sizeInFrames - pAudioBufferRef->cursor; - if (frameCount > framesAvailable) { - frameCount = framesAvailable; - } - - *ppFramesOut = ma_offset_ptr(pAudioBufferRef->pData, pAudioBufferRef->cursor * ma_get_bytes_per_frame(pAudioBufferRef->format, pAudioBufferRef->channels)); - *pFrameCount = frameCount; - - return MA_SUCCESS; -} - -MA_API ma_result ma_audio_buffer_ref_unmap(ma_audio_buffer_ref* pAudioBufferRef, ma_uint64 frameCount) -{ - ma_uint64 framesAvailable; - - if (pAudioBufferRef == NULL) { - return MA_INVALID_ARGS; - } - - framesAvailable = pAudioBufferRef->sizeInFrames - pAudioBufferRef->cursor; - if (frameCount > framesAvailable) { - return MA_INVALID_ARGS; /* The frame count was too big. This should never happen in an unmapping. Need to make sure the caller is aware of this. */ - } - - pAudioBufferRef->cursor += frameCount; - - if (pAudioBufferRef->cursor == pAudioBufferRef->sizeInFrames) { - return MA_AT_END; /* Successful. Need to tell the caller that the end has been reached so that it can loop if desired. */ - } else { - return MA_SUCCESS; - } -} - -MA_API ma_bool32 ma_audio_buffer_ref_at_end(const ma_audio_buffer_ref* pAudioBufferRef) -{ - if (pAudioBufferRef == NULL) { - return MA_FALSE; - } - - return pAudioBufferRef->cursor == pAudioBufferRef->sizeInFrames; -} - -MA_API ma_result ma_audio_buffer_ref_get_cursor_in_pcm_frames(const ma_audio_buffer_ref* pAudioBufferRef, ma_uint64* pCursor) -{ - if (pCursor == NULL) { - return MA_INVALID_ARGS; - } - - *pCursor = 0; - - if (pAudioBufferRef == NULL) { - return MA_INVALID_ARGS; - } - - *pCursor = pAudioBufferRef->cursor; - - return MA_SUCCESS; -} - -MA_API ma_result ma_audio_buffer_ref_get_length_in_pcm_frames(const ma_audio_buffer_ref* pAudioBufferRef, ma_uint64* pLength) -{ - if (pLength == NULL) { - return MA_INVALID_ARGS; - } - - *pLength = 0; - - if (pAudioBufferRef == NULL) { - return MA_INVALID_ARGS; - } - - *pLength = pAudioBufferRef->sizeInFrames; - - return MA_SUCCESS; -} - -MA_API ma_result ma_audio_buffer_ref_get_available_frames(const ma_audio_buffer_ref* pAudioBufferRef, ma_uint64* pAvailableFrames) -{ - if (pAvailableFrames == NULL) { - return MA_INVALID_ARGS; - } - - *pAvailableFrames = 0; - - if (pAudioBufferRef == NULL) { - return MA_INVALID_ARGS; - } - - if (pAudioBufferRef->sizeInFrames <= pAudioBufferRef->cursor) { - *pAvailableFrames = 0; - } else { - *pAvailableFrames = pAudioBufferRef->sizeInFrames - pAudioBufferRef->cursor; - } - - return MA_SUCCESS; -} - - - - -MA_API ma_audio_buffer_config ma_audio_buffer_config_init(ma_format format, ma_uint32 channels, ma_uint64 sizeInFrames, const void* pData, const ma_allocation_callbacks* pAllocationCallbacks) -{ - ma_audio_buffer_config config; - - MA_ZERO_OBJECT(&config); - config.format = format; - config.channels = channels; - config.sampleRate = 0; /* TODO: Version 0.12. Set this to sampleRate. */ - config.sizeInFrames = sizeInFrames; - config.pData = pData; - ma_allocation_callbacks_init_copy(&config.allocationCallbacks, pAllocationCallbacks); - - return config; -} - -static ma_result ma_audio_buffer_init_ex(const ma_audio_buffer_config* pConfig, ma_bool32 doCopy, ma_audio_buffer* pAudioBuffer) -{ - ma_result result; - - if (pAudioBuffer == NULL) { - return MA_INVALID_ARGS; - } - - MA_ZERO_MEMORY(pAudioBuffer, sizeof(*pAudioBuffer) - sizeof(pAudioBuffer->_pExtraData)); /* Safety. Don't overwrite the extra data. */ - - if (pConfig == NULL) { - return MA_INVALID_ARGS; - } - - if (pConfig->sizeInFrames == 0) { - return MA_INVALID_ARGS; /* Not allowing buffer sizes of 0 frames. */ - } - - result = ma_audio_buffer_ref_init(pConfig->format, pConfig->channels, NULL, 0, &pAudioBuffer->ref); - if (result != MA_SUCCESS) { - return result; - } - - /* TODO: Version 0.12. Set this in ma_audio_buffer_ref_init() instead of here. */ - pAudioBuffer->ref.sampleRate = pConfig->sampleRate; - - ma_allocation_callbacks_init_copy(&pAudioBuffer->allocationCallbacks, &pConfig->allocationCallbacks); - - if (doCopy) { - ma_uint64 allocationSizeInBytes; - void* pData; - - allocationSizeInBytes = pConfig->sizeInFrames * ma_get_bytes_per_frame(pConfig->format, pConfig->channels); - if (allocationSizeInBytes > MA_SIZE_MAX) { - return MA_OUT_OF_MEMORY; /* Too big. */ - } - - pData = ma_malloc((size_t)allocationSizeInBytes, &pAudioBuffer->allocationCallbacks); /* Safe cast to size_t. */ - if (pData == NULL) { - return MA_OUT_OF_MEMORY; - } - - if (pConfig->pData != NULL) { - ma_copy_pcm_frames(pData, pConfig->pData, pConfig->sizeInFrames, pConfig->format, pConfig->channels); - } else { - ma_silence_pcm_frames(pData, pConfig->sizeInFrames, pConfig->format, pConfig->channels); - } - - ma_audio_buffer_ref_set_data(&pAudioBuffer->ref, pData, pConfig->sizeInFrames); - pAudioBuffer->ownsData = MA_TRUE; - } else { - ma_audio_buffer_ref_set_data(&pAudioBuffer->ref, pConfig->pData, pConfig->sizeInFrames); - pAudioBuffer->ownsData = MA_FALSE; - } - - return MA_SUCCESS; -} - -static void ma_audio_buffer_uninit_ex(ma_audio_buffer* pAudioBuffer, ma_bool32 doFree) -{ - if (pAudioBuffer == NULL) { - return; - } - - if (pAudioBuffer->ownsData && pAudioBuffer->ref.pData != &pAudioBuffer->_pExtraData[0]) { - ma_free((void*)pAudioBuffer->ref.pData, &pAudioBuffer->allocationCallbacks); /* Naugty const cast, but OK in this case since we've guarded it with the ownsData check. */ - } - - if (doFree) { - ma_free(pAudioBuffer, &pAudioBuffer->allocationCallbacks); - } - - ma_audio_buffer_ref_uninit(&pAudioBuffer->ref); -} - -MA_API ma_result ma_audio_buffer_init(const ma_audio_buffer_config* pConfig, ma_audio_buffer* pAudioBuffer) -{ - return ma_audio_buffer_init_ex(pConfig, MA_FALSE, pAudioBuffer); -} - -MA_API ma_result ma_audio_buffer_init_copy(const ma_audio_buffer_config* pConfig, ma_audio_buffer* pAudioBuffer) -{ - return ma_audio_buffer_init_ex(pConfig, MA_TRUE, pAudioBuffer); -} - -MA_API ma_result ma_audio_buffer_alloc_and_init(const ma_audio_buffer_config* pConfig, ma_audio_buffer** ppAudioBuffer) -{ - ma_result result; - ma_audio_buffer* pAudioBuffer; - ma_audio_buffer_config innerConfig; /* We'll be making some changes to the config, so need to make a copy. */ - ma_uint64 allocationSizeInBytes; - - if (ppAudioBuffer == NULL) { - return MA_INVALID_ARGS; - } - - *ppAudioBuffer = NULL; /* Safety. */ - - if (pConfig == NULL) { - return MA_INVALID_ARGS; - } - - innerConfig = *pConfig; - ma_allocation_callbacks_init_copy(&innerConfig.allocationCallbacks, &pConfig->allocationCallbacks); - - allocationSizeInBytes = sizeof(*pAudioBuffer) - sizeof(pAudioBuffer->_pExtraData) + (pConfig->sizeInFrames * ma_get_bytes_per_frame(pConfig->format, pConfig->channels)); - if (allocationSizeInBytes > MA_SIZE_MAX) { - return MA_OUT_OF_MEMORY; /* Too big. */ - } - - pAudioBuffer = (ma_audio_buffer*)ma_malloc((size_t)allocationSizeInBytes, &innerConfig.allocationCallbacks); /* Safe cast to size_t. */ - if (pAudioBuffer == NULL) { - return MA_OUT_OF_MEMORY; - } - - if (pConfig->pData != NULL) { - ma_copy_pcm_frames(&pAudioBuffer->_pExtraData[0], pConfig->pData, pConfig->sizeInFrames, pConfig->format, pConfig->channels); - } else { - ma_silence_pcm_frames(&pAudioBuffer->_pExtraData[0], pConfig->sizeInFrames, pConfig->format, pConfig->channels); - } - - innerConfig.pData = &pAudioBuffer->_pExtraData[0]; - - result = ma_audio_buffer_init_ex(&innerConfig, MA_FALSE, pAudioBuffer); - if (result != MA_SUCCESS) { - ma_free(pAudioBuffer, &innerConfig.allocationCallbacks); - return result; - } - - *ppAudioBuffer = pAudioBuffer; - - return MA_SUCCESS; -} - -MA_API void ma_audio_buffer_uninit(ma_audio_buffer* pAudioBuffer) -{ - ma_audio_buffer_uninit_ex(pAudioBuffer, MA_FALSE); -} - -MA_API void ma_audio_buffer_uninit_and_free(ma_audio_buffer* pAudioBuffer) -{ - ma_audio_buffer_uninit_ex(pAudioBuffer, MA_TRUE); -} - -MA_API ma_uint64 ma_audio_buffer_read_pcm_frames(ma_audio_buffer* pAudioBuffer, void* pFramesOut, ma_uint64 frameCount, ma_bool32 loop) -{ - if (pAudioBuffer == NULL) { - return 0; - } - - return ma_audio_buffer_ref_read_pcm_frames(&pAudioBuffer->ref, pFramesOut, frameCount, loop); -} - -MA_API ma_result ma_audio_buffer_seek_to_pcm_frame(ma_audio_buffer* pAudioBuffer, ma_uint64 frameIndex) -{ - if (pAudioBuffer == NULL) { - return MA_INVALID_ARGS; - } - - return ma_audio_buffer_ref_seek_to_pcm_frame(&pAudioBuffer->ref, frameIndex); -} - -MA_API ma_result ma_audio_buffer_map(ma_audio_buffer* pAudioBuffer, void** ppFramesOut, ma_uint64* pFrameCount) -{ - if (ppFramesOut != NULL) { - *ppFramesOut = NULL; /* Safety. */ - } - - if (pAudioBuffer == NULL) { - if (pFrameCount != NULL) { - *pFrameCount = 0; - } - - return MA_INVALID_ARGS; - } - - return ma_audio_buffer_ref_map(&pAudioBuffer->ref, ppFramesOut, pFrameCount); -} - -MA_API ma_result ma_audio_buffer_unmap(ma_audio_buffer* pAudioBuffer, ma_uint64 frameCount) -{ - if (pAudioBuffer == NULL) { - return MA_INVALID_ARGS; - } - - return ma_audio_buffer_ref_unmap(&pAudioBuffer->ref, frameCount); -} - -MA_API ma_bool32 ma_audio_buffer_at_end(const ma_audio_buffer* pAudioBuffer) -{ - if (pAudioBuffer == NULL) { - return MA_FALSE; - } - - return ma_audio_buffer_ref_at_end(&pAudioBuffer->ref); -} - -MA_API ma_result ma_audio_buffer_get_cursor_in_pcm_frames(const ma_audio_buffer* pAudioBuffer, ma_uint64* pCursor) -{ - if (pAudioBuffer == NULL) { - return MA_INVALID_ARGS; - } - - return ma_audio_buffer_ref_get_cursor_in_pcm_frames(&pAudioBuffer->ref, pCursor); -} - -MA_API ma_result ma_audio_buffer_get_length_in_pcm_frames(const ma_audio_buffer* pAudioBuffer, ma_uint64* pLength) -{ - if (pAudioBuffer == NULL) { - return MA_INVALID_ARGS; - } - - return ma_audio_buffer_ref_get_length_in_pcm_frames(&pAudioBuffer->ref, pLength); -} - -MA_API ma_result ma_audio_buffer_get_available_frames(const ma_audio_buffer* pAudioBuffer, ma_uint64* pAvailableFrames) -{ - if (pAvailableFrames == NULL) { - return MA_INVALID_ARGS; - } - - *pAvailableFrames = 0; - - if (pAudioBuffer == NULL) { - return MA_INVALID_ARGS; - } - - return ma_audio_buffer_ref_get_available_frames(&pAudioBuffer->ref, pAvailableFrames); -} - - - - - -MA_API ma_result ma_paged_audio_buffer_data_init(ma_format format, ma_uint32 channels, ma_paged_audio_buffer_data* pData) -{ - if (pData == NULL) { - return MA_INVALID_ARGS; - } - - MA_ZERO_OBJECT(pData); - - pData->format = format; - pData->channels = channels; - pData->pTail = &pData->head; - - return MA_SUCCESS; -} - -MA_API void ma_paged_audio_buffer_data_uninit(ma_paged_audio_buffer_data* pData, const ma_allocation_callbacks* pAllocationCallbacks) -{ - ma_paged_audio_buffer_page* pPage; - - if (pData == NULL) { - return; - } - - /* All pages need to be freed. */ - pPage = (ma_paged_audio_buffer_page*)ma_atomic_load_ptr(&pData->head.pNext); - while (pPage != NULL) { - ma_paged_audio_buffer_page* pNext = (ma_paged_audio_buffer_page*)ma_atomic_load_ptr(&pPage->pNext); - - ma_free(pPage, pAllocationCallbacks); - pPage = pNext; - } -} - -MA_API ma_paged_audio_buffer_page* ma_paged_audio_buffer_data_get_head(ma_paged_audio_buffer_data* pData) -{ - if (pData == NULL) { - return NULL; - } - - return &pData->head; -} - -MA_API ma_paged_audio_buffer_page* ma_paged_audio_buffer_data_get_tail(ma_paged_audio_buffer_data* pData) -{ - if (pData == NULL) { - return NULL; - } - - return pData->pTail; -} - -MA_API ma_result ma_paged_audio_buffer_data_get_length_in_pcm_frames(ma_paged_audio_buffer_data* pData, ma_uint64* pLength) -{ - ma_paged_audio_buffer_page* pPage; - - if (pLength == NULL) { - return MA_INVALID_ARGS; - } - - *pLength = 0; - - if (pData == NULL) { - return MA_INVALID_ARGS; - } - - /* Calculate the length from the linked list. */ - for (pPage = (ma_paged_audio_buffer_page*)ma_atomic_load_ptr(&pData->head.pNext); pPage != NULL; pPage = (ma_paged_audio_buffer_page*)ma_atomic_load_ptr(&pPage->pNext)) { - *pLength += pPage->sizeInFrames; - } - - return MA_SUCCESS; -} - -MA_API ma_result ma_paged_audio_buffer_data_allocate_page(ma_paged_audio_buffer_data* pData, ma_uint64 pageSizeInFrames, const void* pInitialData, const ma_allocation_callbacks* pAllocationCallbacks, ma_paged_audio_buffer_page** ppPage) -{ - ma_paged_audio_buffer_page* pPage; - ma_uint64 allocationSize; - - if (ppPage == NULL) { - return MA_INVALID_ARGS; - } - - *ppPage = NULL; - - if (pData == NULL) { - return MA_INVALID_ARGS; - } - - allocationSize = sizeof(*pPage) + (pageSizeInFrames * ma_get_bytes_per_frame(pData->format, pData->channels)); - if (allocationSize > MA_SIZE_MAX) { - return MA_OUT_OF_MEMORY; /* Too big. */ - } - - pPage = (ma_paged_audio_buffer_page*)ma_malloc((size_t)allocationSize, pAllocationCallbacks); /* Safe cast to size_t. */ - if (pPage == NULL) { - return MA_OUT_OF_MEMORY; - } - - pPage->pNext = NULL; - pPage->sizeInFrames = pageSizeInFrames; - - if (pInitialData != NULL) { - ma_copy_pcm_frames(pPage->pAudioData, pInitialData, pageSizeInFrames, pData->format, pData->channels); - } - - *ppPage = pPage; - - return MA_SUCCESS; -} - -MA_API ma_result ma_paged_audio_buffer_data_free_page(ma_paged_audio_buffer_data* pData, ma_paged_audio_buffer_page* pPage, const ma_allocation_callbacks* pAllocationCallbacks) -{ - if (pData == NULL || pPage == NULL) { - return MA_INVALID_ARGS; - } - - /* It's assumed the page is not attached to the list. */ - ma_free(pPage, pAllocationCallbacks); - - return MA_SUCCESS; -} - -MA_API ma_result ma_paged_audio_buffer_data_append_page(ma_paged_audio_buffer_data* pData, ma_paged_audio_buffer_page* pPage) -{ - if (pData == NULL || pPage == NULL) { - return MA_INVALID_ARGS; - } - - /* This function assumes the page has been filled with audio data by this point. As soon as we append, the page will be available for reading. */ - - /* First thing to do is update the tail. */ - for (;;) { - ma_paged_audio_buffer_page* pOldTail = (ma_paged_audio_buffer_page*)ma_atomic_load_ptr(&pData->pTail); - ma_paged_audio_buffer_page* pNewTail = pPage; - - if (ma_atomic_compare_exchange_weak_ptr((volatile void**)&pData->pTail, (void**)&pOldTail, pNewTail)) { - /* Here is where we append the page to the list. After this, the page is attached to the list and ready to be read from. */ - ma_atomic_exchange_ptr(&pOldTail->pNext, pPage); - break; /* Done. */ - } - } - - return MA_SUCCESS; -} - -MA_API ma_result ma_paged_audio_buffer_data_allocate_and_append_page(ma_paged_audio_buffer_data* pData, ma_uint32 pageSizeInFrames, const void* pInitialData, const ma_allocation_callbacks* pAllocationCallbacks) -{ - ma_result result; - ma_paged_audio_buffer_page* pPage; - - result = ma_paged_audio_buffer_data_allocate_page(pData, pageSizeInFrames, pInitialData, pAllocationCallbacks, &pPage); - if (result != MA_SUCCESS) { - return result; - } - - return ma_paged_audio_buffer_data_append_page(pData, pPage); /* <-- Should never fail. */ -} - - -MA_API ma_paged_audio_buffer_config ma_paged_audio_buffer_config_init(ma_paged_audio_buffer_data* pData) -{ - ma_paged_audio_buffer_config config; - - MA_ZERO_OBJECT(&config); - config.pData = pData; - - return config; -} - - -static ma_result ma_paged_audio_buffer__data_source_on_read(ma_data_source* pDataSource, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead) -{ - return ma_paged_audio_buffer_read_pcm_frames((ma_paged_audio_buffer*)pDataSource, pFramesOut, frameCount, pFramesRead); -} - -static ma_result ma_paged_audio_buffer__data_source_on_seek(ma_data_source* pDataSource, ma_uint64 frameIndex) -{ - return ma_paged_audio_buffer_seek_to_pcm_frame((ma_paged_audio_buffer*)pDataSource, frameIndex); -} - -static ma_result ma_paged_audio_buffer__data_source_on_get_data_format(ma_data_source* pDataSource, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate, ma_channel* pChannelMap, size_t channelMapCap) -{ - ma_paged_audio_buffer* pPagedAudioBuffer = (ma_paged_audio_buffer*)pDataSource; - - *pFormat = pPagedAudioBuffer->pData->format; - *pChannels = pPagedAudioBuffer->pData->channels; - *pSampleRate = 0; /* There is no notion of a sample rate with audio buffers. */ - ma_channel_map_init_standard(ma_standard_channel_map_default, pChannelMap, channelMapCap, pPagedAudioBuffer->pData->channels); - - return MA_SUCCESS; -} - -static ma_result ma_paged_audio_buffer__data_source_on_get_cursor(ma_data_source* pDataSource, ma_uint64* pCursor) -{ - return ma_paged_audio_buffer_get_cursor_in_pcm_frames((ma_paged_audio_buffer*)pDataSource, pCursor); -} - -static ma_result ma_paged_audio_buffer__data_source_on_get_length(ma_data_source* pDataSource, ma_uint64* pLength) -{ - return ma_paged_audio_buffer_get_length_in_pcm_frames((ma_paged_audio_buffer*)pDataSource, pLength); -} - -static ma_data_source_vtable g_ma_paged_audio_buffer_data_source_vtable = -{ - ma_paged_audio_buffer__data_source_on_read, - ma_paged_audio_buffer__data_source_on_seek, - ma_paged_audio_buffer__data_source_on_get_data_format, - ma_paged_audio_buffer__data_source_on_get_cursor, - ma_paged_audio_buffer__data_source_on_get_length, - NULL, /* onSetLooping */ - 0 -}; - -MA_API ma_result ma_paged_audio_buffer_init(const ma_paged_audio_buffer_config* pConfig, ma_paged_audio_buffer* pPagedAudioBuffer) -{ - ma_result result; - ma_data_source_config dataSourceConfig; - - if (pPagedAudioBuffer == NULL) { - return MA_INVALID_ARGS; - } - - MA_ZERO_OBJECT(pPagedAudioBuffer); - - /* A config is required for the format and channel count. */ - if (pConfig == NULL) { - return MA_INVALID_ARGS; - } - - if (pConfig->pData == NULL) { - return MA_INVALID_ARGS; /* No underlying data specified. */ - } - - dataSourceConfig = ma_data_source_config_init(); - dataSourceConfig.vtable = &g_ma_paged_audio_buffer_data_source_vtable; - - result = ma_data_source_init(&dataSourceConfig, &pPagedAudioBuffer->ds); - if (result != MA_SUCCESS) { - return result; - } - - pPagedAudioBuffer->pData = pConfig->pData; - pPagedAudioBuffer->pCurrent = ma_paged_audio_buffer_data_get_head(pConfig->pData); - pPagedAudioBuffer->relativeCursor = 0; - pPagedAudioBuffer->absoluteCursor = 0; - - return MA_SUCCESS; -} - -MA_API void ma_paged_audio_buffer_uninit(ma_paged_audio_buffer* pPagedAudioBuffer) -{ - if (pPagedAudioBuffer == NULL) { - return; - } - - /* Nothing to do. The data needs to be deleted separately. */ -} - -MA_API ma_result ma_paged_audio_buffer_read_pcm_frames(ma_paged_audio_buffer* pPagedAudioBuffer, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead) -{ - ma_result result = MA_SUCCESS; - ma_uint64 totalFramesRead = 0; - ma_format format; - ma_uint32 channels; - - if (pPagedAudioBuffer == NULL) { - return MA_INVALID_ARGS; - } - - format = pPagedAudioBuffer->pData->format; - channels = pPagedAudioBuffer->pData->channels; - - while (totalFramesRead < frameCount) { - /* Read from the current page. The buffer should never be in a state where this is NULL. */ - ma_uint64 framesRemainingInCurrentPage; - ma_uint64 framesRemainingToRead = frameCount - totalFramesRead; - ma_uint64 framesToReadThisIteration; - - MA_ASSERT(pPagedAudioBuffer->pCurrent != NULL); - - framesRemainingInCurrentPage = pPagedAudioBuffer->pCurrent->sizeInFrames - pPagedAudioBuffer->relativeCursor; - - framesToReadThisIteration = ma_min(framesRemainingInCurrentPage, framesRemainingToRead); - ma_copy_pcm_frames(ma_offset_pcm_frames_ptr(pFramesOut, totalFramesRead, format, channels), ma_offset_pcm_frames_ptr(pPagedAudioBuffer->pCurrent->pAudioData, pPagedAudioBuffer->relativeCursor, format, channels), framesToReadThisIteration, format, channels); - totalFramesRead += framesToReadThisIteration; - - pPagedAudioBuffer->absoluteCursor += framesToReadThisIteration; - pPagedAudioBuffer->relativeCursor += framesToReadThisIteration; - - /* Move to the next page if necessary. If there's no more pages, we need to return MA_AT_END. */ - MA_ASSERT(pPagedAudioBuffer->relativeCursor <= pPagedAudioBuffer->pCurrent->sizeInFrames); - - if (pPagedAudioBuffer->relativeCursor == pPagedAudioBuffer->pCurrent->sizeInFrames) { - /* We reached the end of the page. Need to move to the next. If there's no more pages, we're done. */ - ma_paged_audio_buffer_page* pNext = (ma_paged_audio_buffer_page*)ma_atomic_load_ptr(&pPagedAudioBuffer->pCurrent->pNext); - if (pNext == NULL) { - result = MA_AT_END; - break; /* We've reached the end. */ - } else { - pPagedAudioBuffer->pCurrent = pNext; - pPagedAudioBuffer->relativeCursor = 0; - } - } - } - - if (pFramesRead != NULL) { - *pFramesRead = totalFramesRead; - } - - return result; -} - -MA_API ma_result ma_paged_audio_buffer_seek_to_pcm_frame(ma_paged_audio_buffer* pPagedAudioBuffer, ma_uint64 frameIndex) -{ - if (pPagedAudioBuffer == NULL) { - return MA_INVALID_ARGS; - } - - if (frameIndex == pPagedAudioBuffer->absoluteCursor) { - return MA_SUCCESS; /* Nothing to do. */ - } - - if (frameIndex < pPagedAudioBuffer->absoluteCursor) { - /* Moving backwards. Need to move the cursor back to the start, and then move forward. */ - pPagedAudioBuffer->pCurrent = ma_paged_audio_buffer_data_get_head(pPagedAudioBuffer->pData); - pPagedAudioBuffer->absoluteCursor = 0; - pPagedAudioBuffer->relativeCursor = 0; - - /* Fall through to the forward seeking section below. */ - } - - if (frameIndex > pPagedAudioBuffer->absoluteCursor) { - /* Moving forward. */ - ma_paged_audio_buffer_page* pPage; - ma_uint64 runningCursor = 0; - - for (pPage = (ma_paged_audio_buffer_page*)ma_atomic_load_ptr(&ma_paged_audio_buffer_data_get_head(pPagedAudioBuffer->pData)->pNext); pPage != NULL; pPage = (ma_paged_audio_buffer_page*)ma_atomic_load_ptr(&pPage->pNext)) { - ma_uint64 pageRangeBeg = runningCursor; - ma_uint64 pageRangeEnd = pageRangeBeg + pPage->sizeInFrames; - - if (frameIndex >= pageRangeBeg) { - if (frameIndex < pageRangeEnd || (frameIndex == pageRangeEnd && pPage == (ma_paged_audio_buffer_page*)ma_atomic_load_ptr(ma_paged_audio_buffer_data_get_tail(pPagedAudioBuffer->pData)))) { /* A small edge case - allow seeking to the very end of the buffer. */ - /* We found the page. */ - pPagedAudioBuffer->pCurrent = pPage; - pPagedAudioBuffer->absoluteCursor = frameIndex; - pPagedAudioBuffer->relativeCursor = frameIndex - pageRangeBeg; - return MA_SUCCESS; - } - } - - runningCursor = pageRangeEnd; - } - - /* Getting here means we tried seeking too far forward. Don't change any state. */ - return MA_BAD_SEEK; - } - - return MA_SUCCESS; -} - -MA_API ma_result ma_paged_audio_buffer_get_cursor_in_pcm_frames(ma_paged_audio_buffer* pPagedAudioBuffer, ma_uint64* pCursor) -{ - if (pCursor == NULL) { - return MA_INVALID_ARGS; - } - - *pCursor = 0; /* Safety. */ - - if (pPagedAudioBuffer == NULL) { - return MA_INVALID_ARGS; - } - - *pCursor = pPagedAudioBuffer->absoluteCursor; - - return MA_SUCCESS; -} - -MA_API ma_result ma_paged_audio_buffer_get_length_in_pcm_frames(ma_paged_audio_buffer* pPagedAudioBuffer, ma_uint64* pLength) -{ - return ma_paged_audio_buffer_data_get_length_in_pcm_frames(pPagedAudioBuffer->pData, pLength); -} - - - -/************************************************************************************************************************************************************** - -VFS - -**************************************************************************************************************************************************************/ -MA_API ma_result ma_vfs_open(ma_vfs* pVFS, const char* pFilePath, ma_uint32 openMode, ma_vfs_file* pFile) -{ - ma_vfs_callbacks* pCallbacks = (ma_vfs_callbacks*)pVFS; - - if (pFile == NULL) { - return MA_INVALID_ARGS; - } - - *pFile = NULL; - - if (pVFS == NULL || pFilePath == NULL || openMode == 0) { - return MA_INVALID_ARGS; - } - - if (pCallbacks->onOpen == NULL) { - return MA_NOT_IMPLEMENTED; - } - - return pCallbacks->onOpen(pVFS, pFilePath, openMode, pFile); -} - -MA_API ma_result ma_vfs_open_w(ma_vfs* pVFS, const wchar_t* pFilePath, ma_uint32 openMode, ma_vfs_file* pFile) -{ - ma_vfs_callbacks* pCallbacks = (ma_vfs_callbacks*)pVFS; - - if (pFile == NULL) { - return MA_INVALID_ARGS; - } - - *pFile = NULL; - - if (pVFS == NULL || pFilePath == NULL || openMode == 0) { - return MA_INVALID_ARGS; - } - - if (pCallbacks->onOpenW == NULL) { - return MA_NOT_IMPLEMENTED; - } - - return pCallbacks->onOpenW(pVFS, pFilePath, openMode, pFile); -} - -MA_API ma_result ma_vfs_close(ma_vfs* pVFS, ma_vfs_file file) -{ - ma_vfs_callbacks* pCallbacks = (ma_vfs_callbacks*)pVFS; - - if (pVFS == NULL || file == NULL) { - return MA_INVALID_ARGS; - } - - if (pCallbacks->onClose == NULL) { - return MA_NOT_IMPLEMENTED; - } - - return pCallbacks->onClose(pVFS, file); -} - -MA_API ma_result ma_vfs_read(ma_vfs* pVFS, ma_vfs_file file, void* pDst, size_t sizeInBytes, size_t* pBytesRead) -{ - ma_vfs_callbacks* pCallbacks = (ma_vfs_callbacks*)pVFS; - ma_result result; - size_t bytesRead = 0; - - if (pBytesRead != NULL) { - *pBytesRead = 0; - } - - if (pVFS == NULL || file == NULL || pDst == NULL) { - return MA_INVALID_ARGS; - } - - if (pCallbacks->onRead == NULL) { - return MA_NOT_IMPLEMENTED; - } - - result = pCallbacks->onRead(pVFS, file, pDst, sizeInBytes, &bytesRead); - - if (pBytesRead != NULL) { - *pBytesRead = bytesRead; - } - - if (result == MA_SUCCESS && bytesRead == 0 && sizeInBytes > 0) { - result = MA_AT_END; - } - - return result; -} - -MA_API ma_result ma_vfs_write(ma_vfs* pVFS, ma_vfs_file file, const void* pSrc, size_t sizeInBytes, size_t* pBytesWritten) -{ - ma_vfs_callbacks* pCallbacks = (ma_vfs_callbacks*)pVFS; - - if (pBytesWritten != NULL) { - *pBytesWritten = 0; - } - - if (pVFS == NULL || file == NULL || pSrc == NULL) { - return MA_INVALID_ARGS; - } - - if (pCallbacks->onWrite == NULL) { - return MA_NOT_IMPLEMENTED; - } - - return pCallbacks->onWrite(pVFS, file, pSrc, sizeInBytes, pBytesWritten); -} - -MA_API ma_result ma_vfs_seek(ma_vfs* pVFS, ma_vfs_file file, ma_int64 offset, ma_seek_origin origin) -{ - ma_vfs_callbacks* pCallbacks = (ma_vfs_callbacks*)pVFS; - - if (pVFS == NULL || file == NULL) { - return MA_INVALID_ARGS; - } - - if (pCallbacks->onSeek == NULL) { - return MA_NOT_IMPLEMENTED; - } - - return pCallbacks->onSeek(pVFS, file, offset, origin); -} - -MA_API ma_result ma_vfs_tell(ma_vfs* pVFS, ma_vfs_file file, ma_int64* pCursor) -{ - ma_vfs_callbacks* pCallbacks = (ma_vfs_callbacks*)pVFS; - - if (pCursor == NULL) { - return MA_INVALID_ARGS; - } - - *pCursor = 0; - - if (pVFS == NULL || file == NULL) { - return MA_INVALID_ARGS; - } - - if (pCallbacks->onTell == NULL) { - return MA_NOT_IMPLEMENTED; - } - - return pCallbacks->onTell(pVFS, file, pCursor); -} - -MA_API ma_result ma_vfs_info(ma_vfs* pVFS, ma_vfs_file file, ma_file_info* pInfo) -{ - ma_vfs_callbacks* pCallbacks = (ma_vfs_callbacks*)pVFS; - - if (pInfo == NULL) { - return MA_INVALID_ARGS; - } - - MA_ZERO_OBJECT(pInfo); - - if (pVFS == NULL || file == NULL) { - return MA_INVALID_ARGS; - } - - if (pCallbacks->onInfo == NULL) { - return MA_NOT_IMPLEMENTED; - } - - return pCallbacks->onInfo(pVFS, file, pInfo); -} - - -#if !defined(MA_USE_WIN32_FILEIO) && (defined(MA_WIN32) && defined(MA_WIN32_DESKTOP) && !defined(MA_NO_WIN32_FILEIO) && !defined(MA_POSIX)) - #define MA_USE_WIN32_FILEIO -#endif - -#if defined(MA_USE_WIN32_FILEIO) -/* -We need to dynamically load SetFilePointer or SetFilePointerEx because older versions of Windows do -not have the Ex version. We therefore need to do some dynamic branching depending on what's available. - -We load these when we load our first file from the default VFS. It's left open for the life of the -program and is left to the OS to uninitialize when the program terminates. -*/ -typedef DWORD (__stdcall * ma_SetFilePointer_proc)(HANDLE hFile, LONG lDistanceToMove, LONG* lpDistanceToMoveHigh, DWORD dwMoveMethod); -typedef BOOL (__stdcall * ma_SetFilePointerEx_proc)(HANDLE hFile, LARGE_INTEGER liDistanceToMove, LARGE_INTEGER* lpNewFilePointer, DWORD dwMoveMethod); - -static ma_handle hKernel32DLL = NULL; -static ma_SetFilePointer_proc ma_SetFilePointer = NULL; -static ma_SetFilePointerEx_proc ma_SetFilePointerEx = NULL; - -static void ma_win32_fileio_init(void) -{ - if (hKernel32DLL == NULL) { - hKernel32DLL = ma_dlopen(NULL, "kernel32.dll"); - if (hKernel32DLL != NULL) { - ma_SetFilePointer = (ma_SetFilePointer_proc) ma_dlsym(NULL, hKernel32DLL, "SetFilePointer"); - ma_SetFilePointerEx = (ma_SetFilePointerEx_proc)ma_dlsym(NULL, hKernel32DLL, "SetFilePointerEx"); - } - } -} - -static void ma_default_vfs__get_open_settings_win32(ma_uint32 openMode, DWORD* pDesiredAccess, DWORD* pShareMode, DWORD* pCreationDisposition) -{ - *pDesiredAccess = 0; - if ((openMode & MA_OPEN_MODE_READ) != 0) { - *pDesiredAccess |= GENERIC_READ; - } - if ((openMode & MA_OPEN_MODE_WRITE) != 0) { - *pDesiredAccess |= GENERIC_WRITE; - } - - *pShareMode = 0; - if ((openMode & MA_OPEN_MODE_READ) != 0) { - *pShareMode |= FILE_SHARE_READ; - } - - if ((openMode & MA_OPEN_MODE_WRITE) != 0) { - *pCreationDisposition = CREATE_ALWAYS; /* Opening in write mode. Truncate. */ - } else { - *pCreationDisposition = OPEN_EXISTING; /* Opening in read mode. File must exist. */ - } -} - -static ma_result ma_default_vfs_open__win32(ma_vfs* pVFS, const char* pFilePath, ma_uint32 openMode, ma_vfs_file* pFile) -{ - HANDLE hFile; - DWORD dwDesiredAccess; - DWORD dwShareMode; - DWORD dwCreationDisposition; - - (void)pVFS; - - /* Load some Win32 symbols dynamically so we can dynamically check for the existence of SetFilePointerEx. */ - ma_win32_fileio_init(); - - ma_default_vfs__get_open_settings_win32(openMode, &dwDesiredAccess, &dwShareMode, &dwCreationDisposition); - - hFile = CreateFileA(pFilePath, dwDesiredAccess, dwShareMode, NULL, dwCreationDisposition, FILE_ATTRIBUTE_NORMAL, NULL); - if (hFile == INVALID_HANDLE_VALUE) { - return ma_result_from_GetLastError(GetLastError()); - } - - *pFile = hFile; - return MA_SUCCESS; -} - -static ma_result ma_default_vfs_open_w__win32(ma_vfs* pVFS, const wchar_t* pFilePath, ma_uint32 openMode, ma_vfs_file* pFile) -{ - HANDLE hFile; - DWORD dwDesiredAccess; - DWORD dwShareMode; - DWORD dwCreationDisposition; - - (void)pVFS; - - /* Load some Win32 symbols dynamically so we can dynamically check for the existence of SetFilePointerEx. */ - ma_win32_fileio_init(); - - ma_default_vfs__get_open_settings_win32(openMode, &dwDesiredAccess, &dwShareMode, &dwCreationDisposition); - - hFile = CreateFileW(pFilePath, dwDesiredAccess, dwShareMode, NULL, dwCreationDisposition, FILE_ATTRIBUTE_NORMAL, NULL); - if (hFile == INVALID_HANDLE_VALUE) { - return ma_result_from_GetLastError(GetLastError()); - } - - *pFile = hFile; - return MA_SUCCESS; -} - -static ma_result ma_default_vfs_close__win32(ma_vfs* pVFS, ma_vfs_file file) -{ - (void)pVFS; - - if (CloseHandle((HANDLE)file) == 0) { - return ma_result_from_GetLastError(GetLastError()); - } - - return MA_SUCCESS; -} - - -static ma_result ma_default_vfs_read__win32(ma_vfs* pVFS, ma_vfs_file file, void* pDst, size_t sizeInBytes, size_t* pBytesRead) -{ - ma_result result = MA_SUCCESS; - size_t totalBytesRead; - - (void)pVFS; - - totalBytesRead = 0; - while (totalBytesRead < sizeInBytes) { - size_t bytesRemaining; - DWORD bytesToRead; - DWORD bytesRead; - BOOL readResult; - - bytesRemaining = sizeInBytes - totalBytesRead; - if (bytesRemaining >= 0xFFFFFFFF) { - bytesToRead = 0xFFFFFFFF; - } else { - bytesToRead = (DWORD)bytesRemaining; - } - - readResult = ReadFile((HANDLE)file, ma_offset_ptr(pDst, totalBytesRead), bytesToRead, &bytesRead, NULL); - if (readResult == 1 && bytesRead == 0) { - result = MA_AT_END; - break; /* EOF */ - } - - totalBytesRead += bytesRead; - - if (bytesRead < bytesToRead) { - break; /* EOF */ - } - - if (readResult == 0) { - result = ma_result_from_GetLastError(GetLastError()); - break; - } - } - - if (pBytesRead != NULL) { - *pBytesRead = totalBytesRead; - } - - return result; -} - -static ma_result ma_default_vfs_write__win32(ma_vfs* pVFS, ma_vfs_file file, const void* pSrc, size_t sizeInBytes, size_t* pBytesWritten) -{ - ma_result result = MA_SUCCESS; - size_t totalBytesWritten; - - (void)pVFS; - - totalBytesWritten = 0; - while (totalBytesWritten < sizeInBytes) { - size_t bytesRemaining; - DWORD bytesToWrite; - DWORD bytesWritten; - BOOL writeResult; - - bytesRemaining = sizeInBytes - totalBytesWritten; - if (bytesRemaining >= 0xFFFFFFFF) { - bytesToWrite = 0xFFFFFFFF; - } else { - bytesToWrite = (DWORD)bytesRemaining; - } - - writeResult = WriteFile((HANDLE)file, ma_offset_ptr(pSrc, totalBytesWritten), bytesToWrite, &bytesWritten, NULL); - totalBytesWritten += bytesWritten; - - if (writeResult == 0) { - result = ma_result_from_GetLastError(GetLastError()); - break; - } - } - - if (pBytesWritten != NULL) { - *pBytesWritten = totalBytesWritten; - } - - return result; -} - - -static ma_result ma_default_vfs_seek__win32(ma_vfs* pVFS, ma_vfs_file file, ma_int64 offset, ma_seek_origin origin) -{ - LARGE_INTEGER liDistanceToMove; - DWORD dwMoveMethod; - BOOL result; - - (void)pVFS; - - liDistanceToMove.QuadPart = offset; - - /* */ if (origin == ma_seek_origin_current) { - dwMoveMethod = FILE_CURRENT; - } else if (origin == ma_seek_origin_end) { - dwMoveMethod = FILE_END; - } else { - dwMoveMethod = FILE_BEGIN; - } - - if (ma_SetFilePointerEx != NULL) { - result = ma_SetFilePointerEx((HANDLE)file, liDistanceToMove, NULL, dwMoveMethod); - } else if (ma_SetFilePointer != NULL) { - /* No SetFilePointerEx() so restrict to 31 bits. */ - if (offset > 0x7FFFFFFF) { - return MA_OUT_OF_RANGE; - } - - result = ma_SetFilePointer((HANDLE)file, (LONG)liDistanceToMove.QuadPart, NULL, dwMoveMethod); - } else { - return MA_NOT_IMPLEMENTED; - } - - if (result == 0) { - return ma_result_from_GetLastError(GetLastError()); - } - - return MA_SUCCESS; -} - -static ma_result ma_default_vfs_tell__win32(ma_vfs* pVFS, ma_vfs_file file, ma_int64* pCursor) -{ - LARGE_INTEGER liZero; - LARGE_INTEGER liTell; - BOOL result; - - (void)pVFS; - - liZero.QuadPart = 0; - - if (ma_SetFilePointerEx != NULL) { - result = ma_SetFilePointerEx((HANDLE)file, liZero, &liTell, FILE_CURRENT); - } else if (ma_SetFilePointer != NULL) { - LONG tell; - - result = ma_SetFilePointer((HANDLE)file, (LONG)liZero.QuadPart, &tell, FILE_CURRENT); - liTell.QuadPart = tell; - } else { - return MA_NOT_IMPLEMENTED; - } - - if (result == 0) { - return ma_result_from_GetLastError(GetLastError()); - } - - if (pCursor != NULL) { - *pCursor = liTell.QuadPart; - } - - return MA_SUCCESS; -} - -static ma_result ma_default_vfs_info__win32(ma_vfs* pVFS, ma_vfs_file file, ma_file_info* pInfo) -{ - BY_HANDLE_FILE_INFORMATION fi; - BOOL result; - - (void)pVFS; - - result = GetFileInformationByHandle((HANDLE)file, &fi); - if (result == 0) { - return ma_result_from_GetLastError(GetLastError()); - } - - pInfo->sizeInBytes = ((ma_uint64)fi.nFileSizeHigh << 32) | ((ma_uint64)fi.nFileSizeLow); - - return MA_SUCCESS; -} -#else -static ma_result ma_default_vfs_open__stdio(ma_vfs* pVFS, const char* pFilePath, ma_uint32 openMode, ma_vfs_file* pFile) -{ - ma_result result; - FILE* pFileStd; - const char* pOpenModeStr; - - MA_ASSERT(pFilePath != NULL); - MA_ASSERT(openMode != 0); - MA_ASSERT(pFile != NULL); - - (void)pVFS; - - if ((openMode & MA_OPEN_MODE_READ) != 0) { - if ((openMode & MA_OPEN_MODE_WRITE) != 0) { - pOpenModeStr = "r+"; - } else { - pOpenModeStr = "rb"; - } - } else { - pOpenModeStr = "wb"; - } - - result = ma_fopen(&pFileStd, pFilePath, pOpenModeStr); - if (result != MA_SUCCESS) { - return result; - } - - *pFile = pFileStd; - - return MA_SUCCESS; -} - -static ma_result ma_default_vfs_open_w__stdio(ma_vfs* pVFS, const wchar_t* pFilePath, ma_uint32 openMode, ma_vfs_file* pFile) -{ - ma_result result; - FILE* pFileStd; - const wchar_t* pOpenModeStr; - - MA_ASSERT(pFilePath != NULL); - MA_ASSERT(openMode != 0); - MA_ASSERT(pFile != NULL); - - (void)pVFS; - - if ((openMode & MA_OPEN_MODE_READ) != 0) { - if ((openMode & MA_OPEN_MODE_WRITE) != 0) { - pOpenModeStr = L"r+"; - } else { - pOpenModeStr = L"rb"; - } - } else { - pOpenModeStr = L"wb"; - } - - result = ma_wfopen(&pFileStd, pFilePath, pOpenModeStr, (pVFS != NULL) ? &((ma_default_vfs*)pVFS)->allocationCallbacks : NULL); - if (result != MA_SUCCESS) { - return result; - } - - *pFile = pFileStd; - - return MA_SUCCESS; -} - -static ma_result ma_default_vfs_close__stdio(ma_vfs* pVFS, ma_vfs_file file) -{ - MA_ASSERT(file != NULL); - - (void)pVFS; - - fclose((FILE*)file); - - return MA_SUCCESS; -} - -static ma_result ma_default_vfs_read__stdio(ma_vfs* pVFS, ma_vfs_file file, void* pDst, size_t sizeInBytes, size_t* pBytesRead) -{ - size_t result; - - MA_ASSERT(file != NULL); - MA_ASSERT(pDst != NULL); - - (void)pVFS; - - result = fread(pDst, 1, sizeInBytes, (FILE*)file); - - if (pBytesRead != NULL) { - *pBytesRead = result; - } - - if (result != sizeInBytes) { - if (result == 0 && feof((FILE*)file)) { - return MA_AT_END; - } else { - return ma_result_from_errno(ferror((FILE*)file)); - } - } - - return MA_SUCCESS; -} - -static ma_result ma_default_vfs_write__stdio(ma_vfs* pVFS, ma_vfs_file file, const void* pSrc, size_t sizeInBytes, size_t* pBytesWritten) -{ - size_t result; - - MA_ASSERT(file != NULL); - MA_ASSERT(pSrc != NULL); - - (void)pVFS; - - result = fwrite(pSrc, 1, sizeInBytes, (FILE*)file); - - if (pBytesWritten != NULL) { - *pBytesWritten = result; - } - - if (result != sizeInBytes) { - return ma_result_from_errno(ferror((FILE*)file)); - } - - return MA_SUCCESS; -} - -static ma_result ma_default_vfs_seek__stdio(ma_vfs* pVFS, ma_vfs_file file, ma_int64 offset, ma_seek_origin origin) -{ - int result; - int whence; - - MA_ASSERT(file != NULL); - - (void)pVFS; - - if (origin == ma_seek_origin_start) { - whence = SEEK_SET; - } else if (origin == ma_seek_origin_end) { - whence = SEEK_END; - } else { - whence = SEEK_CUR; - } - -#if defined(_WIN32) - #if defined(_MSC_VER) && _MSC_VER > 1200 - result = _fseeki64((FILE*)file, offset, whence); - #else - /* No _fseeki64() so restrict to 31 bits. */ - if (offset > 0x7FFFFFFF) { - return MA_OUT_OF_RANGE; - } - - result = fseek((FILE*)file, (int)offset, whence); - #endif -#else - result = fseek((FILE*)file, (long int)offset, whence); -#endif - if (result != 0) { - return MA_ERROR; - } - - return MA_SUCCESS; -} - -static ma_result ma_default_vfs_tell__stdio(ma_vfs* pVFS, ma_vfs_file file, ma_int64* pCursor) -{ - ma_int64 result; - - MA_ASSERT(file != NULL); - MA_ASSERT(pCursor != NULL); - - (void)pVFS; - -#if defined(_WIN32) - #if defined(_MSC_VER) && _MSC_VER > 1200 - result = _ftelli64((FILE*)file); - #else - result = ftell((FILE*)file); - #endif -#else - result = ftell((FILE*)file); -#endif - - *pCursor = result; - - return MA_SUCCESS; -} - -#if !defined(_MSC_VER) && !((defined(_POSIX_C_SOURCE) && _POSIX_C_SOURCE >= 1) || defined(_XOPEN_SOURCE) || defined(_POSIX_SOURCE)) && !defined(MA_BSD) -int fileno(FILE *stream); -#endif - -static ma_result ma_default_vfs_info__stdio(ma_vfs* pVFS, ma_vfs_file file, ma_file_info* pInfo) -{ - int fd; - struct stat info; - - MA_ASSERT(file != NULL); - MA_ASSERT(pInfo != NULL); - - (void)pVFS; - -#if defined(_MSC_VER) - fd = _fileno((FILE*)file); -#else - fd = fileno((FILE*)file); -#endif - - if (fstat(fd, &info) != 0) { - return ma_result_from_errno(errno); - } - - pInfo->sizeInBytes = info.st_size; - - return MA_SUCCESS; -} -#endif - - -static ma_result ma_default_vfs_open(ma_vfs* pVFS, const char* pFilePath, ma_uint32 openMode, ma_vfs_file* pFile) -{ - if (pFile == NULL) { - return MA_INVALID_ARGS; - } - - *pFile = NULL; - - if (pFilePath == NULL || openMode == 0) { - return MA_INVALID_ARGS; - } - -#if defined(MA_USE_WIN32_FILEIO) - return ma_default_vfs_open__win32(pVFS, pFilePath, openMode, pFile); -#else - return ma_default_vfs_open__stdio(pVFS, pFilePath, openMode, pFile); -#endif -} - -static ma_result ma_default_vfs_open_w(ma_vfs* pVFS, const wchar_t* pFilePath, ma_uint32 openMode, ma_vfs_file* pFile) -{ - if (pFile == NULL) { - return MA_INVALID_ARGS; - } - - *pFile = NULL; - - if (pFilePath == NULL || openMode == 0) { - return MA_INVALID_ARGS; - } - -#if defined(MA_USE_WIN32_FILEIO) - return ma_default_vfs_open_w__win32(pVFS, pFilePath, openMode, pFile); -#else - return ma_default_vfs_open_w__stdio(pVFS, pFilePath, openMode, pFile); -#endif -} - -static ma_result ma_default_vfs_close(ma_vfs* pVFS, ma_vfs_file file) -{ - if (file == NULL) { - return MA_INVALID_ARGS; - } - -#if defined(MA_USE_WIN32_FILEIO) - return ma_default_vfs_close__win32(pVFS, file); -#else - return ma_default_vfs_close__stdio(pVFS, file); -#endif -} - -static ma_result ma_default_vfs_read(ma_vfs* pVFS, ma_vfs_file file, void* pDst, size_t sizeInBytes, size_t* pBytesRead) -{ - if (pBytesRead != NULL) { - *pBytesRead = 0; - } - - if (file == NULL || pDst == NULL) { - return MA_INVALID_ARGS; - } - -#if defined(MA_USE_WIN32_FILEIO) - return ma_default_vfs_read__win32(pVFS, file, pDst, sizeInBytes, pBytesRead); -#else - return ma_default_vfs_read__stdio(pVFS, file, pDst, sizeInBytes, pBytesRead); -#endif -} - -static ma_result ma_default_vfs_write(ma_vfs* pVFS, ma_vfs_file file, const void* pSrc, size_t sizeInBytes, size_t* pBytesWritten) -{ - if (pBytesWritten != NULL) { - *pBytesWritten = 0; - } - - if (file == NULL || pSrc == NULL) { - return MA_INVALID_ARGS; - } - -#if defined(MA_USE_WIN32_FILEIO) - return ma_default_vfs_write__win32(pVFS, file, pSrc, sizeInBytes, pBytesWritten); -#else - return ma_default_vfs_write__stdio(pVFS, file, pSrc, sizeInBytes, pBytesWritten); -#endif -} - -static ma_result ma_default_vfs_seek(ma_vfs* pVFS, ma_vfs_file file, ma_int64 offset, ma_seek_origin origin) -{ - if (file == NULL) { - return MA_INVALID_ARGS; - } - -#if defined(MA_USE_WIN32_FILEIO) - return ma_default_vfs_seek__win32(pVFS, file, offset, origin); -#else - return ma_default_vfs_seek__stdio(pVFS, file, offset, origin); -#endif -} - -static ma_result ma_default_vfs_tell(ma_vfs* pVFS, ma_vfs_file file, ma_int64* pCursor) -{ - if (pCursor == NULL) { - return MA_INVALID_ARGS; - } - - *pCursor = 0; - - if (file == NULL) { - return MA_INVALID_ARGS; - } - -#if defined(MA_USE_WIN32_FILEIO) - return ma_default_vfs_tell__win32(pVFS, file, pCursor); -#else - return ma_default_vfs_tell__stdio(pVFS, file, pCursor); -#endif -} - -static ma_result ma_default_vfs_info(ma_vfs* pVFS, ma_vfs_file file, ma_file_info* pInfo) -{ - if (pInfo == NULL) { - return MA_INVALID_ARGS; - } - - MA_ZERO_OBJECT(pInfo); - - if (file == NULL) { - return MA_INVALID_ARGS; - } - -#if defined(MA_USE_WIN32_FILEIO) - return ma_default_vfs_info__win32(pVFS, file, pInfo); -#else - return ma_default_vfs_info__stdio(pVFS, file, pInfo); -#endif -} - - -MA_API ma_result ma_default_vfs_init(ma_default_vfs* pVFS, const ma_allocation_callbacks* pAllocationCallbacks) -{ - if (pVFS == NULL) { - return MA_INVALID_ARGS; - } - - pVFS->cb.onOpen = ma_default_vfs_open; - pVFS->cb.onOpenW = ma_default_vfs_open_w; - pVFS->cb.onClose = ma_default_vfs_close; - pVFS->cb.onRead = ma_default_vfs_read; - pVFS->cb.onWrite = ma_default_vfs_write; - pVFS->cb.onSeek = ma_default_vfs_seek; - pVFS->cb.onTell = ma_default_vfs_tell; - pVFS->cb.onInfo = ma_default_vfs_info; - ma_allocation_callbacks_init_copy(&pVFS->allocationCallbacks, pAllocationCallbacks); - - return MA_SUCCESS; -} - - -MA_API ma_result ma_vfs_or_default_open(ma_vfs* pVFS, const char* pFilePath, ma_uint32 openMode, ma_vfs_file* pFile) -{ - if (pVFS != NULL) { - return ma_vfs_open(pVFS, pFilePath, openMode, pFile); - } else { - return ma_default_vfs_open(pVFS, pFilePath, openMode, pFile); - } -} - -MA_API ma_result ma_vfs_or_default_open_w(ma_vfs* pVFS, const wchar_t* pFilePath, ma_uint32 openMode, ma_vfs_file* pFile) -{ - if (pVFS != NULL) { - return ma_vfs_open_w(pVFS, pFilePath, openMode, pFile); - } else { - return ma_default_vfs_open_w(pVFS, pFilePath, openMode, pFile); - } -} - -MA_API ma_result ma_vfs_or_default_close(ma_vfs* pVFS, ma_vfs_file file) -{ - if (pVFS != NULL) { - return ma_vfs_close(pVFS, file); - } else { - return ma_default_vfs_close(pVFS, file); - } -} - -MA_API ma_result ma_vfs_or_default_read(ma_vfs* pVFS, ma_vfs_file file, void* pDst, size_t sizeInBytes, size_t* pBytesRead) -{ - if (pVFS != NULL) { - return ma_vfs_read(pVFS, file, pDst, sizeInBytes, pBytesRead); - } else { - return ma_default_vfs_read(pVFS, file, pDst, sizeInBytes, pBytesRead); - } -} - -MA_API ma_result ma_vfs_or_default_write(ma_vfs* pVFS, ma_vfs_file file, const void* pSrc, size_t sizeInBytes, size_t* pBytesWritten) -{ - if (pVFS != NULL) { - return ma_vfs_write(pVFS, file, pSrc, sizeInBytes, pBytesWritten); - } else { - return ma_default_vfs_write(pVFS, file, pSrc, sizeInBytes, pBytesWritten); - } -} - -MA_API ma_result ma_vfs_or_default_seek(ma_vfs* pVFS, ma_vfs_file file, ma_int64 offset, ma_seek_origin origin) -{ - if (pVFS != NULL) { - return ma_vfs_seek(pVFS, file, offset, origin); - } else { - return ma_default_vfs_seek(pVFS, file, offset, origin); - } -} - -MA_API ma_result ma_vfs_or_default_tell(ma_vfs* pVFS, ma_vfs_file file, ma_int64* pCursor) -{ - if (pVFS != NULL) { - return ma_vfs_tell(pVFS, file, pCursor); - } else { - return ma_default_vfs_tell(pVFS, file, pCursor); - } -} - -MA_API ma_result ma_vfs_or_default_info(ma_vfs* pVFS, ma_vfs_file file, ma_file_info* pInfo) -{ - if (pVFS != NULL) { - return ma_vfs_info(pVFS, file, pInfo); - } else { - return ma_default_vfs_info(pVFS, file, pInfo); - } -} - - - -static ma_result ma_vfs_open_and_read_file_ex(ma_vfs* pVFS, const char* pFilePath, const wchar_t* pFilePathW, void** ppData, size_t* pSize, const ma_allocation_callbacks* pAllocationCallbacks) -{ - ma_result result; - ma_vfs_file file; - ma_file_info info; - void* pData; - size_t bytesRead; - - if (ppData != NULL) { - *ppData = NULL; - } - if (pSize != NULL) { - *pSize = 0; - } - - if (ppData == NULL) { - return MA_INVALID_ARGS; - } - - if (pFilePath != NULL) { - result = ma_vfs_or_default_open(pVFS, pFilePath, MA_OPEN_MODE_READ, &file); - } else { - result = ma_vfs_or_default_open_w(pVFS, pFilePathW, MA_OPEN_MODE_READ, &file); - } - if (result != MA_SUCCESS) { - return result; - } - - result = ma_vfs_or_default_info(pVFS, file, &info); - if (result != MA_SUCCESS) { - ma_vfs_or_default_close(pVFS, file); - return result; - } - - if (info.sizeInBytes > MA_SIZE_MAX) { - ma_vfs_or_default_close(pVFS, file); - return MA_TOO_BIG; - } - - pData = ma_malloc((size_t)info.sizeInBytes, pAllocationCallbacks); /* Safe cast. */ - if (pData == NULL) { - ma_vfs_or_default_close(pVFS, file); - return result; - } - - result = ma_vfs_or_default_read(pVFS, file, pData, (size_t)info.sizeInBytes, &bytesRead); /* Safe cast. */ - ma_vfs_or_default_close(pVFS, file); - - if (result != MA_SUCCESS) { - ma_free(pData, pAllocationCallbacks); - return result; - } - - if (pSize != NULL) { - *pSize = bytesRead; - } - - MA_ASSERT(ppData != NULL); - *ppData = pData; - - return MA_SUCCESS; -} - -MA_API ma_result ma_vfs_open_and_read_file(ma_vfs* pVFS, const char* pFilePath, void** ppData, size_t* pSize, const ma_allocation_callbacks* pAllocationCallbacks) -{ - return ma_vfs_open_and_read_file_ex(pVFS, pFilePath, NULL, ppData, pSize, pAllocationCallbacks); -} - -MA_API ma_result ma_vfs_open_and_read_file_w(ma_vfs* pVFS, const wchar_t* pFilePath, void** ppData, size_t* pSize, const ma_allocation_callbacks* pAllocationCallbacks) -{ - return ma_vfs_open_and_read_file_ex(pVFS, NULL, pFilePath, ppData, pSize, pAllocationCallbacks); -} - - - -/************************************************************************************************************************************************************** - -Decoding and Encoding Headers. These are auto-generated from a tool. - -**************************************************************************************************************************************************************/ -#if !defined(MA_NO_WAV) && (!defined(MA_NO_DECODING) || !defined(MA_NO_ENCODING)) -/* dr_wav_h begin */ -#ifndef ma_dr_wav_h -#define ma_dr_wav_h -#ifdef __cplusplus -extern "C" { -#endif -#define MA_DR_WAV_STRINGIFY(x) #x -#define MA_DR_WAV_XSTRINGIFY(x) MA_DR_WAV_STRINGIFY(x) -#define MA_DR_WAV_VERSION_MAJOR 0 -#define MA_DR_WAV_VERSION_MINOR 13 -#define MA_DR_WAV_VERSION_REVISION 18 -#define MA_DR_WAV_VERSION_STRING MA_DR_WAV_XSTRINGIFY(MA_DR_WAV_VERSION_MAJOR) "." MA_DR_WAV_XSTRINGIFY(MA_DR_WAV_VERSION_MINOR) "." MA_DR_WAV_XSTRINGIFY(MA_DR_WAV_VERSION_REVISION) -#include -#define MA_DR_WAVE_FORMAT_PCM 0x1 -#define MA_DR_WAVE_FORMAT_ADPCM 0x2 -#define MA_DR_WAVE_FORMAT_IEEE_FLOAT 0x3 -#define MA_DR_WAVE_FORMAT_ALAW 0x6 -#define MA_DR_WAVE_FORMAT_MULAW 0x7 -#define MA_DR_WAVE_FORMAT_DVI_ADPCM 0x11 -#define MA_DR_WAVE_FORMAT_EXTENSIBLE 0xFFFE -#define MA_DR_WAV_SEQUENTIAL 0x00000001 -#define MA_DR_WAV_WITH_METADATA 0x00000002 -MA_API void ma_dr_wav_version(ma_uint32* pMajor, ma_uint32* pMinor, ma_uint32* pRevision); -MA_API const char* ma_dr_wav_version_string(void); -typedef enum -{ - ma_dr_wav_seek_origin_start, - ma_dr_wav_seek_origin_current -} ma_dr_wav_seek_origin; -typedef enum -{ - ma_dr_wav_container_riff, - ma_dr_wav_container_rifx, - ma_dr_wav_container_w64, - ma_dr_wav_container_rf64, - ma_dr_wav_container_aiff -} ma_dr_wav_container; -typedef struct -{ - union - { - ma_uint8 fourcc[4]; - ma_uint8 guid[16]; - } id; - ma_uint64 sizeInBytes; - unsigned int paddingSize; -} ma_dr_wav_chunk_header; -typedef struct -{ - ma_uint16 formatTag; - ma_uint16 channels; - ma_uint32 sampleRate; - ma_uint32 avgBytesPerSec; - ma_uint16 blockAlign; - ma_uint16 bitsPerSample; - ma_uint16 extendedSize; - ma_uint16 validBitsPerSample; - ma_uint32 channelMask; - ma_uint8 subFormat[16]; -} ma_dr_wav_fmt; -MA_API ma_uint16 ma_dr_wav_fmt_get_format(const ma_dr_wav_fmt* pFMT); -typedef size_t (* ma_dr_wav_read_proc)(void* pUserData, void* pBufferOut, size_t bytesToRead); -typedef size_t (* ma_dr_wav_write_proc)(void* pUserData, const void* pData, size_t bytesToWrite); -typedef ma_bool32 (* ma_dr_wav_seek_proc)(void* pUserData, int offset, ma_dr_wav_seek_origin origin); -typedef ma_uint64 (* ma_dr_wav_chunk_proc)(void* pChunkUserData, ma_dr_wav_read_proc onRead, ma_dr_wav_seek_proc onSeek, void* pReadSeekUserData, const ma_dr_wav_chunk_header* pChunkHeader, ma_dr_wav_container container, const ma_dr_wav_fmt* pFMT); -typedef struct -{ - const ma_uint8* data; - size_t dataSize; - size_t currentReadPos; -} ma_dr_wav__memory_stream; -typedef struct -{ - void** ppData; - size_t* pDataSize; - size_t dataSize; - size_t dataCapacity; - size_t currentWritePos; -} ma_dr_wav__memory_stream_write; -typedef struct -{ - ma_dr_wav_container container; - ma_uint32 format; - ma_uint32 channels; - ma_uint32 sampleRate; - ma_uint32 bitsPerSample; -} ma_dr_wav_data_format; -typedef enum -{ - ma_dr_wav_metadata_type_none = 0, - ma_dr_wav_metadata_type_unknown = 1 << 0, - ma_dr_wav_metadata_type_smpl = 1 << 1, - ma_dr_wav_metadata_type_inst = 1 << 2, - ma_dr_wav_metadata_type_cue = 1 << 3, - ma_dr_wav_metadata_type_acid = 1 << 4, - ma_dr_wav_metadata_type_bext = 1 << 5, - ma_dr_wav_metadata_type_list_label = 1 << 6, - ma_dr_wav_metadata_type_list_note = 1 << 7, - ma_dr_wav_metadata_type_list_labelled_cue_region = 1 << 8, - ma_dr_wav_metadata_type_list_info_software = 1 << 9, - ma_dr_wav_metadata_type_list_info_copyright = 1 << 10, - ma_dr_wav_metadata_type_list_info_title = 1 << 11, - ma_dr_wav_metadata_type_list_info_artist = 1 << 12, - ma_dr_wav_metadata_type_list_info_comment = 1 << 13, - ma_dr_wav_metadata_type_list_info_date = 1 << 14, - ma_dr_wav_metadata_type_list_info_genre = 1 << 15, - ma_dr_wav_metadata_type_list_info_album = 1 << 16, - ma_dr_wav_metadata_type_list_info_tracknumber = 1 << 17, - ma_dr_wav_metadata_type_list_all_info_strings = ma_dr_wav_metadata_type_list_info_software - | ma_dr_wav_metadata_type_list_info_copyright - | ma_dr_wav_metadata_type_list_info_title - | ma_dr_wav_metadata_type_list_info_artist - | ma_dr_wav_metadata_type_list_info_comment - | ma_dr_wav_metadata_type_list_info_date - | ma_dr_wav_metadata_type_list_info_genre - | ma_dr_wav_metadata_type_list_info_album - | ma_dr_wav_metadata_type_list_info_tracknumber, - ma_dr_wav_metadata_type_list_all_adtl = ma_dr_wav_metadata_type_list_label - | ma_dr_wav_metadata_type_list_note - | ma_dr_wav_metadata_type_list_labelled_cue_region, - ma_dr_wav_metadata_type_all = -2, - ma_dr_wav_metadata_type_all_including_unknown = -1 -} ma_dr_wav_metadata_type; -typedef enum -{ - ma_dr_wav_smpl_loop_type_forward = 0, - ma_dr_wav_smpl_loop_type_pingpong = 1, - ma_dr_wav_smpl_loop_type_backward = 2 -} ma_dr_wav_smpl_loop_type; -typedef struct -{ - ma_uint32 cuePointId; - ma_uint32 type; - ma_uint32 firstSampleByteOffset; - ma_uint32 lastSampleByteOffset; - ma_uint32 sampleFraction; - ma_uint32 playCount; -} ma_dr_wav_smpl_loop; -typedef struct -{ - ma_uint32 manufacturerId; - ma_uint32 productId; - ma_uint32 samplePeriodNanoseconds; - ma_uint32 midiUnityNote; - ma_uint32 midiPitchFraction; - ma_uint32 smpteFormat; - ma_uint32 smpteOffset; - ma_uint32 sampleLoopCount; - ma_uint32 samplerSpecificDataSizeInBytes; - ma_dr_wav_smpl_loop* pLoops; - ma_uint8* pSamplerSpecificData; -} ma_dr_wav_smpl; -typedef struct -{ - ma_int8 midiUnityNote; - ma_int8 fineTuneCents; - ma_int8 gainDecibels; - ma_int8 lowNote; - ma_int8 highNote; - ma_int8 lowVelocity; - ma_int8 highVelocity; -} ma_dr_wav_inst; -typedef struct -{ - ma_uint32 id; - ma_uint32 playOrderPosition; - ma_uint8 dataChunkId[4]; - ma_uint32 chunkStart; - ma_uint32 blockStart; - ma_uint32 sampleByteOffset; -} ma_dr_wav_cue_point; -typedef struct -{ - ma_uint32 cuePointCount; - ma_dr_wav_cue_point *pCuePoints; -} ma_dr_wav_cue; -typedef enum -{ - ma_dr_wav_acid_flag_one_shot = 1, - ma_dr_wav_acid_flag_root_note_set = 2, - ma_dr_wav_acid_flag_stretch = 4, - ma_dr_wav_acid_flag_disk_based = 8, - ma_dr_wav_acid_flag_acidizer = 16 -} ma_dr_wav_acid_flag; -typedef struct -{ - ma_uint32 flags; - ma_uint16 midiUnityNote; - ma_uint16 reserved1; - float reserved2; - ma_uint32 numBeats; - ma_uint16 meterDenominator; - ma_uint16 meterNumerator; - float tempo; -} ma_dr_wav_acid; -typedef struct -{ - ma_uint32 cuePointId; - ma_uint32 stringLength; - char* pString; -} ma_dr_wav_list_label_or_note; -typedef struct -{ - char* pDescription; - char* pOriginatorName; - char* pOriginatorReference; - char pOriginationDate[10]; - char pOriginationTime[8]; - ma_uint64 timeReference; - ma_uint16 version; - char* pCodingHistory; - ma_uint32 codingHistorySize; - ma_uint8* pUMID; - ma_uint16 loudnessValue; - ma_uint16 loudnessRange; - ma_uint16 maxTruePeakLevel; - ma_uint16 maxMomentaryLoudness; - ma_uint16 maxShortTermLoudness; -} ma_dr_wav_bext; -typedef struct -{ - ma_uint32 stringLength; - char* pString; -} ma_dr_wav_list_info_text; -typedef struct -{ - ma_uint32 cuePointId; - ma_uint32 sampleLength; - ma_uint8 purposeId[4]; - ma_uint16 country; - ma_uint16 language; - ma_uint16 dialect; - ma_uint16 codePage; - ma_uint32 stringLength; - char* pString; -} ma_dr_wav_list_labelled_cue_region; -typedef enum -{ - ma_dr_wav_metadata_location_invalid, - ma_dr_wav_metadata_location_top_level, - ma_dr_wav_metadata_location_inside_info_list, - ma_dr_wav_metadata_location_inside_adtl_list -} ma_dr_wav_metadata_location; -typedef struct -{ - ma_uint8 id[4]; - ma_dr_wav_metadata_location chunkLocation; - ma_uint32 dataSizeInBytes; - ma_uint8* pData; -} ma_dr_wav_unknown_metadata; -typedef struct -{ - ma_dr_wav_metadata_type type; - union - { - ma_dr_wav_cue cue; - ma_dr_wav_smpl smpl; - ma_dr_wav_acid acid; - ma_dr_wav_inst inst; - ma_dr_wav_bext bext; - ma_dr_wav_list_label_or_note labelOrNote; - ma_dr_wav_list_labelled_cue_region labelledCueRegion; - ma_dr_wav_list_info_text infoText; - ma_dr_wav_unknown_metadata unknown; - } data; -} ma_dr_wav_metadata; -typedef struct -{ - ma_dr_wav_read_proc onRead; - ma_dr_wav_write_proc onWrite; - ma_dr_wav_seek_proc onSeek; - void* pUserData; - ma_allocation_callbacks allocationCallbacks; - ma_dr_wav_container container; - ma_dr_wav_fmt fmt; - ma_uint32 sampleRate; - ma_uint16 channels; - ma_uint16 bitsPerSample; - ma_uint16 translatedFormatTag; - ma_uint64 totalPCMFrameCount; - ma_uint64 dataChunkDataSize; - ma_uint64 dataChunkDataPos; - ma_uint64 bytesRemaining; - ma_uint64 readCursorInPCMFrames; - ma_uint64 dataChunkDataSizeTargetWrite; - ma_bool32 isSequentialWrite; - ma_dr_wav_metadata* pMetadata; - ma_uint32 metadataCount; - ma_dr_wav__memory_stream memoryStream; - ma_dr_wav__memory_stream_write memoryStreamWrite; - struct - { - ma_uint32 bytesRemainingInBlock; - ma_uint16 predictor[2]; - ma_int32 delta[2]; - ma_int32 cachedFrames[4]; - ma_uint32 cachedFrameCount; - ma_int32 prevFrames[2][2]; - } msadpcm; - struct - { - ma_uint32 bytesRemainingInBlock; - ma_int32 predictor[2]; - ma_int32 stepIndex[2]; - ma_int32 cachedFrames[16]; - ma_uint32 cachedFrameCount; - } ima; - struct - { - ma_bool8 isLE; - ma_bool8 isUnsigned; - } aiff; -} ma_dr_wav; -MA_API ma_bool32 ma_dr_wav_init(ma_dr_wav* pWav, ma_dr_wav_read_proc onRead, ma_dr_wav_seek_proc onSeek, void* pUserData, const ma_allocation_callbacks* pAllocationCallbacks); -MA_API ma_bool32 ma_dr_wav_init_ex(ma_dr_wav* pWav, ma_dr_wav_read_proc onRead, ma_dr_wav_seek_proc onSeek, ma_dr_wav_chunk_proc onChunk, void* pReadSeekUserData, void* pChunkUserData, ma_uint32 flags, const ma_allocation_callbacks* pAllocationCallbacks); -MA_API ma_bool32 ma_dr_wav_init_with_metadata(ma_dr_wav* pWav, ma_dr_wav_read_proc onRead, ma_dr_wav_seek_proc onSeek, void* pUserData, ma_uint32 flags, const ma_allocation_callbacks* pAllocationCallbacks); -MA_API ma_bool32 ma_dr_wav_init_write(ma_dr_wav* pWav, const ma_dr_wav_data_format* pFormat, ma_dr_wav_write_proc onWrite, ma_dr_wav_seek_proc onSeek, void* pUserData, const ma_allocation_callbacks* pAllocationCallbacks); -MA_API ma_bool32 ma_dr_wav_init_write_sequential(ma_dr_wav* pWav, const ma_dr_wav_data_format* pFormat, ma_uint64 totalSampleCount, ma_dr_wav_write_proc onWrite, void* pUserData, const ma_allocation_callbacks* pAllocationCallbacks); -MA_API ma_bool32 ma_dr_wav_init_write_sequential_pcm_frames(ma_dr_wav* pWav, const ma_dr_wav_data_format* pFormat, ma_uint64 totalPCMFrameCount, ma_dr_wav_write_proc onWrite, void* pUserData, const ma_allocation_callbacks* pAllocationCallbacks); -MA_API ma_bool32 ma_dr_wav_init_write_with_metadata(ma_dr_wav* pWav, const ma_dr_wav_data_format* pFormat, ma_dr_wav_write_proc onWrite, ma_dr_wav_seek_proc onSeek, void* pUserData, const ma_allocation_callbacks* pAllocationCallbacks, ma_dr_wav_metadata* pMetadata, ma_uint32 metadataCount); -MA_API ma_uint64 ma_dr_wav_target_write_size_bytes(const ma_dr_wav_data_format* pFormat, ma_uint64 totalFrameCount, ma_dr_wav_metadata* pMetadata, ma_uint32 metadataCount); -MA_API ma_dr_wav_metadata* ma_dr_wav_take_ownership_of_metadata(ma_dr_wav* pWav); -MA_API ma_result ma_dr_wav_uninit(ma_dr_wav* pWav); -MA_API size_t ma_dr_wav_read_raw(ma_dr_wav* pWav, size_t bytesToRead, void* pBufferOut); -MA_API ma_uint64 ma_dr_wav_read_pcm_frames(ma_dr_wav* pWav, ma_uint64 framesToRead, void* pBufferOut); -MA_API ma_uint64 ma_dr_wav_read_pcm_frames_le(ma_dr_wav* pWav, ma_uint64 framesToRead, void* pBufferOut); -MA_API ma_uint64 ma_dr_wav_read_pcm_frames_be(ma_dr_wav* pWav, ma_uint64 framesToRead, void* pBufferOut); -MA_API ma_bool32 ma_dr_wav_seek_to_pcm_frame(ma_dr_wav* pWav, ma_uint64 targetFrameIndex); -MA_API ma_result ma_dr_wav_get_cursor_in_pcm_frames(ma_dr_wav* pWav, ma_uint64* pCursor); -MA_API ma_result ma_dr_wav_get_length_in_pcm_frames(ma_dr_wav* pWav, ma_uint64* pLength); -MA_API size_t ma_dr_wav_write_raw(ma_dr_wav* pWav, size_t bytesToWrite, const void* pData); -MA_API ma_uint64 ma_dr_wav_write_pcm_frames(ma_dr_wav* pWav, ma_uint64 framesToWrite, const void* pData); -MA_API ma_uint64 ma_dr_wav_write_pcm_frames_le(ma_dr_wav* pWav, ma_uint64 framesToWrite, const void* pData); -MA_API ma_uint64 ma_dr_wav_write_pcm_frames_be(ma_dr_wav* pWav, ma_uint64 framesToWrite, const void* pData); -#ifndef MA_DR_WAV_NO_CONVERSION_API -MA_API ma_uint64 ma_dr_wav_read_pcm_frames_s16(ma_dr_wav* pWav, ma_uint64 framesToRead, ma_int16* pBufferOut); -MA_API ma_uint64 ma_dr_wav_read_pcm_frames_s16le(ma_dr_wav* pWav, ma_uint64 framesToRead, ma_int16* pBufferOut); -MA_API ma_uint64 ma_dr_wav_read_pcm_frames_s16be(ma_dr_wav* pWav, ma_uint64 framesToRead, ma_int16* pBufferOut); -MA_API void ma_dr_wav_u8_to_s16(ma_int16* pOut, const ma_uint8* pIn, size_t sampleCount); -MA_API void ma_dr_wav_s24_to_s16(ma_int16* pOut, const ma_uint8* pIn, size_t sampleCount); -MA_API void ma_dr_wav_s32_to_s16(ma_int16* pOut, const ma_int32* pIn, size_t sampleCount); -MA_API void ma_dr_wav_f32_to_s16(ma_int16* pOut, const float* pIn, size_t sampleCount); -MA_API void ma_dr_wav_f64_to_s16(ma_int16* pOut, const double* pIn, size_t sampleCount); -MA_API void ma_dr_wav_alaw_to_s16(ma_int16* pOut, const ma_uint8* pIn, size_t sampleCount); -MA_API void ma_dr_wav_mulaw_to_s16(ma_int16* pOut, const ma_uint8* pIn, size_t sampleCount); -MA_API ma_uint64 ma_dr_wav_read_pcm_frames_f32(ma_dr_wav* pWav, ma_uint64 framesToRead, float* pBufferOut); -MA_API ma_uint64 ma_dr_wav_read_pcm_frames_f32le(ma_dr_wav* pWav, ma_uint64 framesToRead, float* pBufferOut); -MA_API ma_uint64 ma_dr_wav_read_pcm_frames_f32be(ma_dr_wav* pWav, ma_uint64 framesToRead, float* pBufferOut); -MA_API void ma_dr_wav_u8_to_f32(float* pOut, const ma_uint8* pIn, size_t sampleCount); -MA_API void ma_dr_wav_s16_to_f32(float* pOut, const ma_int16* pIn, size_t sampleCount); -MA_API void ma_dr_wav_s24_to_f32(float* pOut, const ma_uint8* pIn, size_t sampleCount); -MA_API void ma_dr_wav_s32_to_f32(float* pOut, const ma_int32* pIn, size_t sampleCount); -MA_API void ma_dr_wav_f64_to_f32(float* pOut, const double* pIn, size_t sampleCount); -MA_API void ma_dr_wav_alaw_to_f32(float* pOut, const ma_uint8* pIn, size_t sampleCount); -MA_API void ma_dr_wav_mulaw_to_f32(float* pOut, const ma_uint8* pIn, size_t sampleCount); -MA_API ma_uint64 ma_dr_wav_read_pcm_frames_s32(ma_dr_wav* pWav, ma_uint64 framesToRead, ma_int32* pBufferOut); -MA_API ma_uint64 ma_dr_wav_read_pcm_frames_s32le(ma_dr_wav* pWav, ma_uint64 framesToRead, ma_int32* pBufferOut); -MA_API ma_uint64 ma_dr_wav_read_pcm_frames_s32be(ma_dr_wav* pWav, ma_uint64 framesToRead, ma_int32* pBufferOut); -MA_API void ma_dr_wav_u8_to_s32(ma_int32* pOut, const ma_uint8* pIn, size_t sampleCount); -MA_API void ma_dr_wav_s16_to_s32(ma_int32* pOut, const ma_int16* pIn, size_t sampleCount); -MA_API void ma_dr_wav_s24_to_s32(ma_int32* pOut, const ma_uint8* pIn, size_t sampleCount); -MA_API void ma_dr_wav_f32_to_s32(ma_int32* pOut, const float* pIn, size_t sampleCount); -MA_API void ma_dr_wav_f64_to_s32(ma_int32* pOut, const double* pIn, size_t sampleCount); -MA_API void ma_dr_wav_alaw_to_s32(ma_int32* pOut, const ma_uint8* pIn, size_t sampleCount); -MA_API void ma_dr_wav_mulaw_to_s32(ma_int32* pOut, const ma_uint8* pIn, size_t sampleCount); -#endif -#ifndef MA_DR_WAV_NO_STDIO -MA_API ma_bool32 ma_dr_wav_init_file(ma_dr_wav* pWav, const char* filename, const ma_allocation_callbacks* pAllocationCallbacks); -MA_API ma_bool32 ma_dr_wav_init_file_ex(ma_dr_wav* pWav, const char* filename, ma_dr_wav_chunk_proc onChunk, void* pChunkUserData, ma_uint32 flags, const ma_allocation_callbacks* pAllocationCallbacks); -MA_API ma_bool32 ma_dr_wav_init_file_w(ma_dr_wav* pWav, const wchar_t* filename, const ma_allocation_callbacks* pAllocationCallbacks); -MA_API ma_bool32 ma_dr_wav_init_file_ex_w(ma_dr_wav* pWav, const wchar_t* filename, ma_dr_wav_chunk_proc onChunk, void* pChunkUserData, ma_uint32 flags, const ma_allocation_callbacks* pAllocationCallbacks); -MA_API ma_bool32 ma_dr_wav_init_file_with_metadata(ma_dr_wav* pWav, const char* filename, ma_uint32 flags, const ma_allocation_callbacks* pAllocationCallbacks); -MA_API ma_bool32 ma_dr_wav_init_file_with_metadata_w(ma_dr_wav* pWav, const wchar_t* filename, ma_uint32 flags, const ma_allocation_callbacks* pAllocationCallbacks); -MA_API ma_bool32 ma_dr_wav_init_file_write(ma_dr_wav* pWav, const char* filename, const ma_dr_wav_data_format* pFormat, const ma_allocation_callbacks* pAllocationCallbacks); -MA_API ma_bool32 ma_dr_wav_init_file_write_sequential(ma_dr_wav* pWav, const char* filename, const ma_dr_wav_data_format* pFormat, ma_uint64 totalSampleCount, const ma_allocation_callbacks* pAllocationCallbacks); -MA_API ma_bool32 ma_dr_wav_init_file_write_sequential_pcm_frames(ma_dr_wav* pWav, const char* filename, const ma_dr_wav_data_format* pFormat, ma_uint64 totalPCMFrameCount, const ma_allocation_callbacks* pAllocationCallbacks); -MA_API ma_bool32 ma_dr_wav_init_file_write_w(ma_dr_wav* pWav, const wchar_t* filename, const ma_dr_wav_data_format* pFormat, const ma_allocation_callbacks* pAllocationCallbacks); -MA_API ma_bool32 ma_dr_wav_init_file_write_sequential_w(ma_dr_wav* pWav, const wchar_t* filename, const ma_dr_wav_data_format* pFormat, ma_uint64 totalSampleCount, const ma_allocation_callbacks* pAllocationCallbacks); -MA_API ma_bool32 ma_dr_wav_init_file_write_sequential_pcm_frames_w(ma_dr_wav* pWav, const wchar_t* filename, const ma_dr_wav_data_format* pFormat, ma_uint64 totalPCMFrameCount, const ma_allocation_callbacks* pAllocationCallbacks); -#endif -MA_API ma_bool32 ma_dr_wav_init_memory(ma_dr_wav* pWav, const void* data, size_t dataSize, const ma_allocation_callbacks* pAllocationCallbacks); -MA_API ma_bool32 ma_dr_wav_init_memory_ex(ma_dr_wav* pWav, const void* data, size_t dataSize, ma_dr_wav_chunk_proc onChunk, void* pChunkUserData, ma_uint32 flags, const ma_allocation_callbacks* pAllocationCallbacks); -MA_API ma_bool32 ma_dr_wav_init_memory_with_metadata(ma_dr_wav* pWav, const void* data, size_t dataSize, ma_uint32 flags, const ma_allocation_callbacks* pAllocationCallbacks); -MA_API ma_bool32 ma_dr_wav_init_memory_write(ma_dr_wav* pWav, void** ppData, size_t* pDataSize, const ma_dr_wav_data_format* pFormat, const ma_allocation_callbacks* pAllocationCallbacks); -MA_API ma_bool32 ma_dr_wav_init_memory_write_sequential(ma_dr_wav* pWav, void** ppData, size_t* pDataSize, const ma_dr_wav_data_format* pFormat, ma_uint64 totalSampleCount, const ma_allocation_callbacks* pAllocationCallbacks); -MA_API ma_bool32 ma_dr_wav_init_memory_write_sequential_pcm_frames(ma_dr_wav* pWav, void** ppData, size_t* pDataSize, const ma_dr_wav_data_format* pFormat, ma_uint64 totalPCMFrameCount, const ma_allocation_callbacks* pAllocationCallbacks); -#ifndef MA_DR_WAV_NO_CONVERSION_API -MA_API ma_int16* ma_dr_wav_open_and_read_pcm_frames_s16(ma_dr_wav_read_proc onRead, ma_dr_wav_seek_proc onSeek, void* pUserData, unsigned int* channelsOut, unsigned int* sampleRateOut, ma_uint64* totalFrameCountOut, const ma_allocation_callbacks* pAllocationCallbacks); -MA_API float* ma_dr_wav_open_and_read_pcm_frames_f32(ma_dr_wav_read_proc onRead, ma_dr_wav_seek_proc onSeek, void* pUserData, unsigned int* channelsOut, unsigned int* sampleRateOut, ma_uint64* totalFrameCountOut, const ma_allocation_callbacks* pAllocationCallbacks); -MA_API ma_int32* ma_dr_wav_open_and_read_pcm_frames_s32(ma_dr_wav_read_proc onRead, ma_dr_wav_seek_proc onSeek, void* pUserData, unsigned int* channelsOut, unsigned int* sampleRateOut, ma_uint64* totalFrameCountOut, const ma_allocation_callbacks* pAllocationCallbacks); -#ifndef MA_DR_WAV_NO_STDIO -MA_API ma_int16* ma_dr_wav_open_file_and_read_pcm_frames_s16(const char* filename, unsigned int* channelsOut, unsigned int* sampleRateOut, ma_uint64* totalFrameCountOut, const ma_allocation_callbacks* pAllocationCallbacks); -MA_API float* ma_dr_wav_open_file_and_read_pcm_frames_f32(const char* filename, unsigned int* channelsOut, unsigned int* sampleRateOut, ma_uint64* totalFrameCountOut, const ma_allocation_callbacks* pAllocationCallbacks); -MA_API ma_int32* ma_dr_wav_open_file_and_read_pcm_frames_s32(const char* filename, unsigned int* channelsOut, unsigned int* sampleRateOut, ma_uint64* totalFrameCountOut, const ma_allocation_callbacks* pAllocationCallbacks); -MA_API ma_int16* ma_dr_wav_open_file_and_read_pcm_frames_s16_w(const wchar_t* filename, unsigned int* channelsOut, unsigned int* sampleRateOut, ma_uint64* totalFrameCountOut, const ma_allocation_callbacks* pAllocationCallbacks); -MA_API float* ma_dr_wav_open_file_and_read_pcm_frames_f32_w(const wchar_t* filename, unsigned int* channelsOut, unsigned int* sampleRateOut, ma_uint64* totalFrameCountOut, const ma_allocation_callbacks* pAllocationCallbacks); -MA_API ma_int32* ma_dr_wav_open_file_and_read_pcm_frames_s32_w(const wchar_t* filename, unsigned int* channelsOut, unsigned int* sampleRateOut, ma_uint64* totalFrameCountOut, const ma_allocation_callbacks* pAllocationCallbacks); -#endif -MA_API ma_int16* ma_dr_wav_open_memory_and_read_pcm_frames_s16(const void* data, size_t dataSize, unsigned int* channelsOut, unsigned int* sampleRateOut, ma_uint64* totalFrameCountOut, const ma_allocation_callbacks* pAllocationCallbacks); -MA_API float* ma_dr_wav_open_memory_and_read_pcm_frames_f32(const void* data, size_t dataSize, unsigned int* channelsOut, unsigned int* sampleRateOut, ma_uint64* totalFrameCountOut, const ma_allocation_callbacks* pAllocationCallbacks); -MA_API ma_int32* ma_dr_wav_open_memory_and_read_pcm_frames_s32(const void* data, size_t dataSize, unsigned int* channelsOut, unsigned int* sampleRateOut, ma_uint64* totalFrameCountOut, const ma_allocation_callbacks* pAllocationCallbacks); -#endif -MA_API void ma_dr_wav_free(void* p, const ma_allocation_callbacks* pAllocationCallbacks); -MA_API ma_uint16 ma_dr_wav_bytes_to_u16(const ma_uint8* data); -MA_API ma_int16 ma_dr_wav_bytes_to_s16(const ma_uint8* data); -MA_API ma_uint32 ma_dr_wav_bytes_to_u32(const ma_uint8* data); -MA_API ma_int32 ma_dr_wav_bytes_to_s32(const ma_uint8* data); -MA_API ma_uint64 ma_dr_wav_bytes_to_u64(const ma_uint8* data); -MA_API ma_int64 ma_dr_wav_bytes_to_s64(const ma_uint8* data); -MA_API float ma_dr_wav_bytes_to_f32(const ma_uint8* data); -MA_API ma_bool32 ma_dr_wav_guid_equal(const ma_uint8 a[16], const ma_uint8 b[16]); -MA_API ma_bool32 ma_dr_wav_fourcc_equal(const ma_uint8* a, const char* b); -#ifdef __cplusplus -} -#endif -#endif -/* dr_wav_h end */ -#endif /* MA_NO_WAV */ - -#if !defined(MA_NO_FLAC) && !defined(MA_NO_DECODING) -/* dr_flac_h begin */ -#ifndef ma_dr_flac_h -#define ma_dr_flac_h -#ifdef __cplusplus -extern "C" { -#endif -#define MA_DR_FLAC_STRINGIFY(x) #x -#define MA_DR_FLAC_XSTRINGIFY(x) MA_DR_FLAC_STRINGIFY(x) -#define MA_DR_FLAC_VERSION_MAJOR 0 -#define MA_DR_FLAC_VERSION_MINOR 12 -#define MA_DR_FLAC_VERSION_REVISION 43 -#define MA_DR_FLAC_VERSION_STRING MA_DR_FLAC_XSTRINGIFY(MA_DR_FLAC_VERSION_MAJOR) "." MA_DR_FLAC_XSTRINGIFY(MA_DR_FLAC_VERSION_MINOR) "." MA_DR_FLAC_XSTRINGIFY(MA_DR_FLAC_VERSION_REVISION) -#include -#if defined(_MSC_VER) && _MSC_VER >= 1700 - #define MA_DR_FLAC_DEPRECATED __declspec(deprecated) -#elif (defined(__GNUC__) && __GNUC__ >= 4) - #define MA_DR_FLAC_DEPRECATED __attribute__((deprecated)) -#elif defined(__has_feature) - #if __has_feature(attribute_deprecated) - #define MA_DR_FLAC_DEPRECATED __attribute__((deprecated)) - #else - #define MA_DR_FLAC_DEPRECATED - #endif -#else - #define MA_DR_FLAC_DEPRECATED -#endif -MA_API void ma_dr_flac_version(ma_uint32* pMajor, ma_uint32* pMinor, ma_uint32* pRevision); -MA_API const char* ma_dr_flac_version_string(void); -#ifndef MA_DR_FLAC_BUFFER_SIZE -#define MA_DR_FLAC_BUFFER_SIZE 4096 -#endif -#ifdef MA_64BIT -typedef ma_uint64 ma_dr_flac_cache_t; -#else -typedef ma_uint32 ma_dr_flac_cache_t; -#endif -#define MA_DR_FLAC_METADATA_BLOCK_TYPE_STREAMINFO 0 -#define MA_DR_FLAC_METADATA_BLOCK_TYPE_PADDING 1 -#define MA_DR_FLAC_METADATA_BLOCK_TYPE_APPLICATION 2 -#define MA_DR_FLAC_METADATA_BLOCK_TYPE_SEEKTABLE 3 -#define MA_DR_FLAC_METADATA_BLOCK_TYPE_VORBIS_COMMENT 4 -#define MA_DR_FLAC_METADATA_BLOCK_TYPE_CUESHEET 5 -#define MA_DR_FLAC_METADATA_BLOCK_TYPE_PICTURE 6 -#define MA_DR_FLAC_METADATA_BLOCK_TYPE_INVALID 127 -#define MA_DR_FLAC_PICTURE_TYPE_OTHER 0 -#define MA_DR_FLAC_PICTURE_TYPE_FILE_ICON 1 -#define MA_DR_FLAC_PICTURE_TYPE_OTHER_FILE_ICON 2 -#define MA_DR_FLAC_PICTURE_TYPE_COVER_FRONT 3 -#define MA_DR_FLAC_PICTURE_TYPE_COVER_BACK 4 -#define MA_DR_FLAC_PICTURE_TYPE_LEAFLET_PAGE 5 -#define MA_DR_FLAC_PICTURE_TYPE_MEDIA 6 -#define MA_DR_FLAC_PICTURE_TYPE_LEAD_ARTIST 7 -#define MA_DR_FLAC_PICTURE_TYPE_ARTIST 8 -#define MA_DR_FLAC_PICTURE_TYPE_CONDUCTOR 9 -#define MA_DR_FLAC_PICTURE_TYPE_BAND 10 -#define MA_DR_FLAC_PICTURE_TYPE_COMPOSER 11 -#define MA_DR_FLAC_PICTURE_TYPE_LYRICIST 12 -#define MA_DR_FLAC_PICTURE_TYPE_RECORDING_LOCATION 13 -#define MA_DR_FLAC_PICTURE_TYPE_DURING_RECORDING 14 -#define MA_DR_FLAC_PICTURE_TYPE_DURING_PERFORMANCE 15 -#define MA_DR_FLAC_PICTURE_TYPE_SCREEN_CAPTURE 16 -#define MA_DR_FLAC_PICTURE_TYPE_BRIGHT_COLORED_FISH 17 -#define MA_DR_FLAC_PICTURE_TYPE_ILLUSTRATION 18 -#define MA_DR_FLAC_PICTURE_TYPE_BAND_LOGOTYPE 19 -#define MA_DR_FLAC_PICTURE_TYPE_PUBLISHER_LOGOTYPE 20 -typedef enum -{ - ma_dr_flac_container_native, - ma_dr_flac_container_ogg, - ma_dr_flac_container_unknown -} ma_dr_flac_container; -typedef enum -{ - ma_dr_flac_seek_origin_start, - ma_dr_flac_seek_origin_current -} ma_dr_flac_seek_origin; -typedef struct -{ - ma_uint64 firstPCMFrame; - ma_uint64 flacFrameOffset; - ma_uint16 pcmFrameCount; -} ma_dr_flac_seekpoint; -typedef struct -{ - ma_uint16 minBlockSizeInPCMFrames; - ma_uint16 maxBlockSizeInPCMFrames; - ma_uint32 minFrameSizeInPCMFrames; - ma_uint32 maxFrameSizeInPCMFrames; - ma_uint32 sampleRate; - ma_uint8 channels; - ma_uint8 bitsPerSample; - ma_uint64 totalPCMFrameCount; - ma_uint8 md5[16]; -} ma_dr_flac_streaminfo; -typedef struct -{ - ma_uint32 type; - const void* pRawData; - ma_uint32 rawDataSize; - union - { - ma_dr_flac_streaminfo streaminfo; - struct - { - int unused; - } padding; - struct - { - ma_uint32 id; - const void* pData; - ma_uint32 dataSize; - } application; - struct - { - ma_uint32 seekpointCount; - const ma_dr_flac_seekpoint* pSeekpoints; - } seektable; - struct - { - ma_uint32 vendorLength; - const char* vendor; - ma_uint32 commentCount; - const void* pComments; - } vorbis_comment; - struct - { - char catalog[128]; - ma_uint64 leadInSampleCount; - ma_bool32 isCD; - ma_uint8 trackCount; - const void* pTrackData; - } cuesheet; - struct - { - ma_uint32 type; - ma_uint32 mimeLength; - const char* mime; - ma_uint32 descriptionLength; - const char* description; - ma_uint32 width; - ma_uint32 height; - ma_uint32 colorDepth; - ma_uint32 indexColorCount; - ma_uint32 pictureDataSize; - const ma_uint8* pPictureData; - } picture; - } data; -} ma_dr_flac_metadata; -typedef size_t (* ma_dr_flac_read_proc)(void* pUserData, void* pBufferOut, size_t bytesToRead); -typedef ma_bool32 (* ma_dr_flac_seek_proc)(void* pUserData, int offset, ma_dr_flac_seek_origin origin); -typedef void (* ma_dr_flac_meta_proc)(void* pUserData, ma_dr_flac_metadata* pMetadata); -typedef struct -{ - const ma_uint8* data; - size_t dataSize; - size_t currentReadPos; -} ma_dr_flac__memory_stream; -typedef struct -{ - ma_dr_flac_read_proc onRead; - ma_dr_flac_seek_proc onSeek; - void* pUserData; - size_t unalignedByteCount; - ma_dr_flac_cache_t unalignedCache; - ma_uint32 nextL2Line; - ma_uint32 consumedBits; - ma_dr_flac_cache_t cacheL2[MA_DR_FLAC_BUFFER_SIZE/sizeof(ma_dr_flac_cache_t)]; - ma_dr_flac_cache_t cache; - ma_uint16 crc16; - ma_dr_flac_cache_t crc16Cache; - ma_uint32 crc16CacheIgnoredBytes; -} ma_dr_flac_bs; -typedef struct -{ - ma_uint8 subframeType; - ma_uint8 wastedBitsPerSample; - ma_uint8 lpcOrder; - ma_int32* pSamplesS32; -} ma_dr_flac_subframe; -typedef struct -{ - ma_uint64 pcmFrameNumber; - ma_uint32 flacFrameNumber; - ma_uint32 sampleRate; - ma_uint16 blockSizeInPCMFrames; - ma_uint8 channelAssignment; - ma_uint8 bitsPerSample; - ma_uint8 crc8; -} ma_dr_flac_frame_header; -typedef struct -{ - ma_dr_flac_frame_header header; - ma_uint32 pcmFramesRemaining; - ma_dr_flac_subframe subframes[8]; -} ma_dr_flac_frame; -typedef struct -{ - ma_dr_flac_meta_proc onMeta; - void* pUserDataMD; - ma_allocation_callbacks allocationCallbacks; - ma_uint32 sampleRate; - ma_uint8 channels; - ma_uint8 bitsPerSample; - ma_uint16 maxBlockSizeInPCMFrames; - ma_uint64 totalPCMFrameCount; - ma_dr_flac_container container; - ma_uint32 seekpointCount; - ma_dr_flac_frame currentFLACFrame; - ma_uint64 currentPCMFrame; - ma_uint64 firstFLACFramePosInBytes; - ma_dr_flac__memory_stream memoryStream; - ma_int32* pDecodedSamples; - ma_dr_flac_seekpoint* pSeekpoints; - void* _oggbs; - ma_bool32 _noSeekTableSeek : 1; - ma_bool32 _noBinarySearchSeek : 1; - ma_bool32 _noBruteForceSeek : 1; - ma_dr_flac_bs bs; - ma_uint8 pExtraData[1]; -} ma_dr_flac; -MA_API ma_dr_flac* ma_dr_flac_open(ma_dr_flac_read_proc onRead, ma_dr_flac_seek_proc onSeek, void* pUserData, const ma_allocation_callbacks* pAllocationCallbacks); -MA_API ma_dr_flac* ma_dr_flac_open_relaxed(ma_dr_flac_read_proc onRead, ma_dr_flac_seek_proc onSeek, ma_dr_flac_container container, void* pUserData, const ma_allocation_callbacks* pAllocationCallbacks); -MA_API ma_dr_flac* ma_dr_flac_open_with_metadata(ma_dr_flac_read_proc onRead, ma_dr_flac_seek_proc onSeek, ma_dr_flac_meta_proc onMeta, void* pUserData, const ma_allocation_callbacks* pAllocationCallbacks); -MA_API ma_dr_flac* ma_dr_flac_open_with_metadata_relaxed(ma_dr_flac_read_proc onRead, ma_dr_flac_seek_proc onSeek, ma_dr_flac_meta_proc onMeta, ma_dr_flac_container container, void* pUserData, const ma_allocation_callbacks* pAllocationCallbacks); -MA_API void ma_dr_flac_close(ma_dr_flac* pFlac); -MA_API ma_uint64 ma_dr_flac_read_pcm_frames_s32(ma_dr_flac* pFlac, ma_uint64 framesToRead, ma_int32* pBufferOut); -MA_API ma_uint64 ma_dr_flac_read_pcm_frames_s16(ma_dr_flac* pFlac, ma_uint64 framesToRead, ma_int16* pBufferOut); -MA_API ma_uint64 ma_dr_flac_read_pcm_frames_f32(ma_dr_flac* pFlac, ma_uint64 framesToRead, float* pBufferOut); -MA_API ma_bool32 ma_dr_flac_seek_to_pcm_frame(ma_dr_flac* pFlac, ma_uint64 pcmFrameIndex); -#ifndef MA_DR_FLAC_NO_STDIO -MA_API ma_dr_flac* ma_dr_flac_open_file(const char* pFileName, const ma_allocation_callbacks* pAllocationCallbacks); -MA_API ma_dr_flac* ma_dr_flac_open_file_w(const wchar_t* pFileName, const ma_allocation_callbacks* pAllocationCallbacks); -MA_API ma_dr_flac* ma_dr_flac_open_file_with_metadata(const char* pFileName, ma_dr_flac_meta_proc onMeta, void* pUserData, const ma_allocation_callbacks* pAllocationCallbacks); -MA_API ma_dr_flac* ma_dr_flac_open_file_with_metadata_w(const wchar_t* pFileName, ma_dr_flac_meta_proc onMeta, void* pUserData, const ma_allocation_callbacks* pAllocationCallbacks); -#endif -MA_API ma_dr_flac* ma_dr_flac_open_memory(const void* pData, size_t dataSize, const ma_allocation_callbacks* pAllocationCallbacks); -MA_API ma_dr_flac* ma_dr_flac_open_memory_with_metadata(const void* pData, size_t dataSize, ma_dr_flac_meta_proc onMeta, void* pUserData, const ma_allocation_callbacks* pAllocationCallbacks); -MA_API ma_int32* ma_dr_flac_open_and_read_pcm_frames_s32(ma_dr_flac_read_proc onRead, ma_dr_flac_seek_proc onSeek, void* pUserData, unsigned int* channels, unsigned int* sampleRate, ma_uint64* totalPCMFrameCount, const ma_allocation_callbacks* pAllocationCallbacks); -MA_API ma_int16* ma_dr_flac_open_and_read_pcm_frames_s16(ma_dr_flac_read_proc onRead, ma_dr_flac_seek_proc onSeek, void* pUserData, unsigned int* channels, unsigned int* sampleRate, ma_uint64* totalPCMFrameCount, const ma_allocation_callbacks* pAllocationCallbacks); -MA_API float* ma_dr_flac_open_and_read_pcm_frames_f32(ma_dr_flac_read_proc onRead, ma_dr_flac_seek_proc onSeek, void* pUserData, unsigned int* channels, unsigned int* sampleRate, ma_uint64* totalPCMFrameCount, const ma_allocation_callbacks* pAllocationCallbacks); -#ifndef MA_DR_FLAC_NO_STDIO -MA_API ma_int32* ma_dr_flac_open_file_and_read_pcm_frames_s32(const char* filename, unsigned int* channels, unsigned int* sampleRate, ma_uint64* totalPCMFrameCount, const ma_allocation_callbacks* pAllocationCallbacks); -MA_API ma_int16* ma_dr_flac_open_file_and_read_pcm_frames_s16(const char* filename, unsigned int* channels, unsigned int* sampleRate, ma_uint64* totalPCMFrameCount, const ma_allocation_callbacks* pAllocationCallbacks); -MA_API float* ma_dr_flac_open_file_and_read_pcm_frames_f32(const char* filename, unsigned int* channels, unsigned int* sampleRate, ma_uint64* totalPCMFrameCount, const ma_allocation_callbacks* pAllocationCallbacks); -#endif -MA_API ma_int32* ma_dr_flac_open_memory_and_read_pcm_frames_s32(const void* data, size_t dataSize, unsigned int* channels, unsigned int* sampleRate, ma_uint64* totalPCMFrameCount, const ma_allocation_callbacks* pAllocationCallbacks); -MA_API ma_int16* ma_dr_flac_open_memory_and_read_pcm_frames_s16(const void* data, size_t dataSize, unsigned int* channels, unsigned int* sampleRate, ma_uint64* totalPCMFrameCount, const ma_allocation_callbacks* pAllocationCallbacks); -MA_API float* ma_dr_flac_open_memory_and_read_pcm_frames_f32(const void* data, size_t dataSize, unsigned int* channels, unsigned int* sampleRate, ma_uint64* totalPCMFrameCount, const ma_allocation_callbacks* pAllocationCallbacks); -MA_API void ma_dr_flac_free(void* p, const ma_allocation_callbacks* pAllocationCallbacks); -typedef struct -{ - ma_uint32 countRemaining; - const char* pRunningData; -} ma_dr_flac_vorbis_comment_iterator; -MA_API void ma_dr_flac_init_vorbis_comment_iterator(ma_dr_flac_vorbis_comment_iterator* pIter, ma_uint32 commentCount, const void* pComments); -MA_API const char* ma_dr_flac_next_vorbis_comment(ma_dr_flac_vorbis_comment_iterator* pIter, ma_uint32* pCommentLengthOut); -typedef struct -{ - ma_uint32 countRemaining; - const char* pRunningData; -} ma_dr_flac_cuesheet_track_iterator; -typedef struct -{ - ma_uint64 offset; - ma_uint8 index; - ma_uint8 reserved[3]; -} ma_dr_flac_cuesheet_track_index; -typedef struct -{ - ma_uint64 offset; - ma_uint8 trackNumber; - char ISRC[12]; - ma_bool8 isAudio; - ma_bool8 preEmphasis; - ma_uint8 indexCount; - const ma_dr_flac_cuesheet_track_index* pIndexPoints; -} ma_dr_flac_cuesheet_track; -MA_API void ma_dr_flac_init_cuesheet_track_iterator(ma_dr_flac_cuesheet_track_iterator* pIter, ma_uint32 trackCount, const void* pTrackData); -MA_API ma_bool32 ma_dr_flac_next_cuesheet_track(ma_dr_flac_cuesheet_track_iterator* pIter, ma_dr_flac_cuesheet_track* pCuesheetTrack); -#ifdef __cplusplus -} -#endif -#endif -/* dr_flac_h end */ -#endif /* MA_NO_FLAC */ - -#if !defined(MA_NO_MP3) && !defined(MA_NO_DECODING) -/* dr_mp3_h begin */ -#ifndef ma_dr_mp3_h -#define ma_dr_mp3_h -#ifdef __cplusplus -extern "C" { -#endif -#define MA_DR_MP3_STRINGIFY(x) #x -#define MA_DR_MP3_XSTRINGIFY(x) MA_DR_MP3_STRINGIFY(x) -#define MA_DR_MP3_VERSION_MAJOR 0 -#define MA_DR_MP3_VERSION_MINOR 6 -#define MA_DR_MP3_VERSION_REVISION 40 -#define MA_DR_MP3_VERSION_STRING MA_DR_MP3_XSTRINGIFY(MA_DR_MP3_VERSION_MAJOR) "." MA_DR_MP3_XSTRINGIFY(MA_DR_MP3_VERSION_MINOR) "." MA_DR_MP3_XSTRINGIFY(MA_DR_MP3_VERSION_REVISION) -#include -#define MA_DR_MP3_MAX_PCM_FRAMES_PER_MP3_FRAME 1152 -#define MA_DR_MP3_MAX_SAMPLES_PER_FRAME (MA_DR_MP3_MAX_PCM_FRAMES_PER_MP3_FRAME*2) -MA_API void ma_dr_mp3_version(ma_uint32* pMajor, ma_uint32* pMinor, ma_uint32* pRevision); -MA_API const char* ma_dr_mp3_version_string(void); -typedef struct -{ - int frame_bytes, channels, hz, layer, bitrate_kbps; -} ma_dr_mp3dec_frame_info; -typedef struct -{ - float mdct_overlap[2][9*32], qmf_state[15*2*32]; - int reserv, free_format_bytes; - ma_uint8 header[4], reserv_buf[511]; -} ma_dr_mp3dec; -MA_API void ma_dr_mp3dec_init(ma_dr_mp3dec *dec); -MA_API int ma_dr_mp3dec_decode_frame(ma_dr_mp3dec *dec, const ma_uint8 *mp3, int mp3_bytes, void *pcm, ma_dr_mp3dec_frame_info *info); -MA_API void ma_dr_mp3dec_f32_to_s16(const float *in, ma_int16 *out, size_t num_samples); -typedef enum -{ - ma_dr_mp3_seek_origin_start, - ma_dr_mp3_seek_origin_current -} ma_dr_mp3_seek_origin; -typedef struct -{ - ma_uint64 seekPosInBytes; - ma_uint64 pcmFrameIndex; - ma_uint16 mp3FramesToDiscard; - ma_uint16 pcmFramesToDiscard; -} ma_dr_mp3_seek_point; -typedef size_t (* ma_dr_mp3_read_proc)(void* pUserData, void* pBufferOut, size_t bytesToRead); -typedef ma_bool32 (* ma_dr_mp3_seek_proc)(void* pUserData, int offset, ma_dr_mp3_seek_origin origin); -typedef struct -{ - ma_uint32 channels; - ma_uint32 sampleRate; -} ma_dr_mp3_config; -typedef struct -{ - ma_dr_mp3dec decoder; - ma_uint32 channels; - ma_uint32 sampleRate; - ma_dr_mp3_read_proc onRead; - ma_dr_mp3_seek_proc onSeek; - void* pUserData; - ma_allocation_callbacks allocationCallbacks; - ma_uint32 mp3FrameChannels; - ma_uint32 mp3FrameSampleRate; - ma_uint32 pcmFramesConsumedInMP3Frame; - ma_uint32 pcmFramesRemainingInMP3Frame; - ma_uint8 pcmFrames[sizeof(float)*MA_DR_MP3_MAX_SAMPLES_PER_FRAME]; - ma_uint64 currentPCMFrame; - ma_uint64 streamCursor; - ma_dr_mp3_seek_point* pSeekPoints; - ma_uint32 seekPointCount; - size_t dataSize; - size_t dataCapacity; - size_t dataConsumed; - ma_uint8* pData; - ma_bool32 atEnd : 1; - struct - { - const ma_uint8* pData; - size_t dataSize; - size_t currentReadPos; - } memory; -} ma_dr_mp3; -MA_API ma_bool32 ma_dr_mp3_init(ma_dr_mp3* pMP3, ma_dr_mp3_read_proc onRead, ma_dr_mp3_seek_proc onSeek, void* pUserData, const ma_allocation_callbacks* pAllocationCallbacks); -MA_API ma_bool32 ma_dr_mp3_init_memory(ma_dr_mp3* pMP3, const void* pData, size_t dataSize, const ma_allocation_callbacks* pAllocationCallbacks); -#ifndef MA_DR_MP3_NO_STDIO -MA_API ma_bool32 ma_dr_mp3_init_file(ma_dr_mp3* pMP3, const char* pFilePath, const ma_allocation_callbacks* pAllocationCallbacks); -MA_API ma_bool32 ma_dr_mp3_init_file_w(ma_dr_mp3* pMP3, const wchar_t* pFilePath, const ma_allocation_callbacks* pAllocationCallbacks); -#endif -MA_API void ma_dr_mp3_uninit(ma_dr_mp3* pMP3); -MA_API ma_uint64 ma_dr_mp3_read_pcm_frames_f32(ma_dr_mp3* pMP3, ma_uint64 framesToRead, float* pBufferOut); -MA_API ma_uint64 ma_dr_mp3_read_pcm_frames_s16(ma_dr_mp3* pMP3, ma_uint64 framesToRead, ma_int16* pBufferOut); -MA_API ma_bool32 ma_dr_mp3_seek_to_pcm_frame(ma_dr_mp3* pMP3, ma_uint64 frameIndex); -MA_API ma_uint64 ma_dr_mp3_get_pcm_frame_count(ma_dr_mp3* pMP3); -MA_API ma_uint64 ma_dr_mp3_get_mp3_frame_count(ma_dr_mp3* pMP3); -MA_API ma_bool32 ma_dr_mp3_get_mp3_and_pcm_frame_count(ma_dr_mp3* pMP3, ma_uint64* pMP3FrameCount, ma_uint64* pPCMFrameCount); -MA_API ma_bool32 ma_dr_mp3_calculate_seek_points(ma_dr_mp3* pMP3, ma_uint32* pSeekPointCount, ma_dr_mp3_seek_point* pSeekPoints); -MA_API ma_bool32 ma_dr_mp3_bind_seek_table(ma_dr_mp3* pMP3, ma_uint32 seekPointCount, ma_dr_mp3_seek_point* pSeekPoints); -MA_API float* ma_dr_mp3_open_and_read_pcm_frames_f32(ma_dr_mp3_read_proc onRead, ma_dr_mp3_seek_proc onSeek, void* pUserData, ma_dr_mp3_config* pConfig, ma_uint64* pTotalFrameCount, const ma_allocation_callbacks* pAllocationCallbacks); -MA_API ma_int16* ma_dr_mp3_open_and_read_pcm_frames_s16(ma_dr_mp3_read_proc onRead, ma_dr_mp3_seek_proc onSeek, void* pUserData, ma_dr_mp3_config* pConfig, ma_uint64* pTotalFrameCount, const ma_allocation_callbacks* pAllocationCallbacks); -MA_API float* ma_dr_mp3_open_memory_and_read_pcm_frames_f32(const void* pData, size_t dataSize, ma_dr_mp3_config* pConfig, ma_uint64* pTotalFrameCount, const ma_allocation_callbacks* pAllocationCallbacks); -MA_API ma_int16* ma_dr_mp3_open_memory_and_read_pcm_frames_s16(const void* pData, size_t dataSize, ma_dr_mp3_config* pConfig, ma_uint64* pTotalFrameCount, const ma_allocation_callbacks* pAllocationCallbacks); -#ifndef MA_DR_MP3_NO_STDIO -MA_API float* ma_dr_mp3_open_file_and_read_pcm_frames_f32(const char* filePath, ma_dr_mp3_config* pConfig, ma_uint64* pTotalFrameCount, const ma_allocation_callbacks* pAllocationCallbacks); -MA_API ma_int16* ma_dr_mp3_open_file_and_read_pcm_frames_s16(const char* filePath, ma_dr_mp3_config* pConfig, ma_uint64* pTotalFrameCount, const ma_allocation_callbacks* pAllocationCallbacks); -#endif -MA_API void* ma_dr_mp3_malloc(size_t sz, const ma_allocation_callbacks* pAllocationCallbacks); -MA_API void ma_dr_mp3_free(void* p, const ma_allocation_callbacks* pAllocationCallbacks); -#ifdef __cplusplus -} -#endif -#endif -/* dr_mp3_h end */ -#endif /* MA_NO_MP3 */ - - -/************************************************************************************************************************************************************** - -Decoding - -**************************************************************************************************************************************************************/ -#ifndef MA_NO_DECODING - -static ma_result ma_decoder_read_bytes(ma_decoder* pDecoder, void* pBufferOut, size_t bytesToRead, size_t* pBytesRead) -{ - MA_ASSERT(pDecoder != NULL); - - return pDecoder->onRead(pDecoder, pBufferOut, bytesToRead, pBytesRead); -} - -static ma_result ma_decoder_seek_bytes(ma_decoder* pDecoder, ma_int64 byteOffset, ma_seek_origin origin) -{ - MA_ASSERT(pDecoder != NULL); - - return pDecoder->onSeek(pDecoder, byteOffset, origin); -} - -static ma_result ma_decoder_tell_bytes(ma_decoder* pDecoder, ma_int64* pCursor) -{ - MA_ASSERT(pDecoder != NULL); - - if (pDecoder->onTell == NULL) { - return MA_NOT_IMPLEMENTED; - } - - return pDecoder->onTell(pDecoder, pCursor); -} - - -MA_API ma_decoding_backend_config ma_decoding_backend_config_init(ma_format preferredFormat, ma_uint32 seekPointCount) -{ - ma_decoding_backend_config config; - - MA_ZERO_OBJECT(&config); - config.preferredFormat = preferredFormat; - config.seekPointCount = seekPointCount; - - return config; -} - - -MA_API ma_decoder_config ma_decoder_config_init(ma_format outputFormat, ma_uint32 outputChannels, ma_uint32 outputSampleRate) -{ - ma_decoder_config config; - MA_ZERO_OBJECT(&config); - config.format = outputFormat; - config.channels = outputChannels; - config.sampleRate = outputSampleRate; - config.resampling = ma_resampler_config_init(ma_format_unknown, 0, 0, 0, ma_resample_algorithm_linear); /* Format/channels/rate doesn't matter here. */ - config.encodingFormat = ma_encoding_format_unknown; - - /* Note that we are intentionally leaving the channel map empty here which will cause the default channel map to be used. */ - - return config; -} - -MA_API ma_decoder_config ma_decoder_config_init_default(void) -{ - return ma_decoder_config_init(ma_format_unknown, 0, 0); -} - -MA_API ma_decoder_config ma_decoder_config_init_copy(const ma_decoder_config* pConfig) -{ - ma_decoder_config config; - if (pConfig != NULL) { - config = *pConfig; - } else { - MA_ZERO_OBJECT(&config); - } - - return config; -} - -static ma_result ma_decoder__init_data_converter(ma_decoder* pDecoder, const ma_decoder_config* pConfig) -{ - ma_result result; - ma_data_converter_config converterConfig; - ma_format internalFormat; - ma_uint32 internalChannels; - ma_uint32 internalSampleRate; - ma_channel internalChannelMap[MA_MAX_CHANNELS]; - - MA_ASSERT(pDecoder != NULL); - MA_ASSERT(pConfig != NULL); - - result = ma_data_source_get_data_format(pDecoder->pBackend, &internalFormat, &internalChannels, &internalSampleRate, internalChannelMap, ma_countof(internalChannelMap)); - if (result != MA_SUCCESS) { - return result; /* Failed to retrieve the internal data format. */ - } - - - /* Make sure we're not asking for too many channels. */ - if (pConfig->channels > MA_MAX_CHANNELS) { - return MA_INVALID_ARGS; - } - - /* The internal channels should have already been validated at a higher level, but we'll do it again explicitly here for safety. */ - if (internalChannels > MA_MAX_CHANNELS) { - return MA_INVALID_ARGS; - } - - - /* Output format. */ - if (pConfig->format == ma_format_unknown) { - pDecoder->outputFormat = internalFormat; - } else { - pDecoder->outputFormat = pConfig->format; - } - - if (pConfig->channels == 0) { - pDecoder->outputChannels = internalChannels; - } else { - pDecoder->outputChannels = pConfig->channels; - } - - if (pConfig->sampleRate == 0) { - pDecoder->outputSampleRate = internalSampleRate; - } else { - pDecoder->outputSampleRate = pConfig->sampleRate; - } - - converterConfig = ma_data_converter_config_init( - internalFormat, pDecoder->outputFormat, - internalChannels, pDecoder->outputChannels, - internalSampleRate, pDecoder->outputSampleRate - ); - converterConfig.pChannelMapIn = internalChannelMap; - converterConfig.pChannelMapOut = pConfig->pChannelMap; - converterConfig.channelMixMode = pConfig->channelMixMode; - converterConfig.ditherMode = pConfig->ditherMode; - converterConfig.allowDynamicSampleRate = MA_FALSE; /* Never allow dynamic sample rate conversion. Setting this to true will disable passthrough optimizations. */ - converterConfig.resampling = pConfig->resampling; - - result = ma_data_converter_init(&converterConfig, &pDecoder->allocationCallbacks, &pDecoder->converter); - if (result != MA_SUCCESS) { - return result; - } - - /* - Now that we have the decoder we need to determine whether or not we need a heap-allocated cache. We'll - need this if the data converter does not support calculation of the required input frame count. To - determine support for this we'll just run a test. - */ - { - ma_uint64 unused; - - result = ma_data_converter_get_required_input_frame_count(&pDecoder->converter, 1, &unused); - if (result != MA_SUCCESS) { - /* - We were unable to calculate the required input frame count which means we'll need to use - a heap-allocated cache. - */ - ma_uint64 inputCacheCapSizeInBytes; - - pDecoder->inputCacheCap = MA_DATA_CONVERTER_STACK_BUFFER_SIZE / ma_get_bytes_per_frame(internalFormat, internalChannels); - - /* Not strictly necessary, but keeping here for safety in case we change the default value of pDecoder->inputCacheCap. */ - inputCacheCapSizeInBytes = pDecoder->inputCacheCap * ma_get_bytes_per_frame(internalFormat, internalChannels); - if (inputCacheCapSizeInBytes > MA_SIZE_MAX) { - ma_data_converter_uninit(&pDecoder->converter, &pDecoder->allocationCallbacks); - return MA_OUT_OF_MEMORY; - } - - pDecoder->pInputCache = ma_malloc((size_t)inputCacheCapSizeInBytes, &pDecoder->allocationCallbacks); /* Safe cast to size_t. */ - if (pDecoder->pInputCache == NULL) { - ma_data_converter_uninit(&pDecoder->converter, &pDecoder->allocationCallbacks); - return MA_OUT_OF_MEMORY; - } - } - } - - return MA_SUCCESS; -} - - - -static ma_result ma_decoder_internal_on_read__custom(void* pUserData, void* pBufferOut, size_t bytesToRead, size_t* pBytesRead) -{ - ma_decoder* pDecoder = (ma_decoder*)pUserData; - MA_ASSERT(pDecoder != NULL); - - return ma_decoder_read_bytes(pDecoder, pBufferOut, bytesToRead, pBytesRead); -} - -static ma_result ma_decoder_internal_on_seek__custom(void* pUserData, ma_int64 offset, ma_seek_origin origin) -{ - ma_decoder* pDecoder = (ma_decoder*)pUserData; - MA_ASSERT(pDecoder != NULL); - - return ma_decoder_seek_bytes(pDecoder, offset, origin); -} - -static ma_result ma_decoder_internal_on_tell__custom(void* pUserData, ma_int64* pCursor) -{ - ma_decoder* pDecoder = (ma_decoder*)pUserData; - MA_ASSERT(pDecoder != NULL); - - return ma_decoder_tell_bytes(pDecoder, pCursor); -} - - -static ma_result ma_decoder_init_from_vtable__internal(const ma_decoding_backend_vtable* pVTable, void* pVTableUserData, const ma_decoder_config* pConfig, ma_decoder* pDecoder) -{ - ma_result result; - ma_decoding_backend_config backendConfig; - ma_data_source* pBackend; - - MA_ASSERT(pVTable != NULL); - MA_ASSERT(pConfig != NULL); - MA_ASSERT(pDecoder != NULL); - - if (pVTable->onInit == NULL) { - return MA_NOT_IMPLEMENTED; - } - - backendConfig = ma_decoding_backend_config_init(pConfig->format, pConfig->seekPointCount); - - result = pVTable->onInit(pVTableUserData, ma_decoder_internal_on_read__custom, ma_decoder_internal_on_seek__custom, ma_decoder_internal_on_tell__custom, pDecoder, &backendConfig, &pDecoder->allocationCallbacks, &pBackend); - if (result != MA_SUCCESS) { - return result; /* Failed to initialize the backend from this vtable. */ - } - - /* Getting here means we were able to initialize the backend so we can now initialize the decoder. */ - pDecoder->pBackend = pBackend; - pDecoder->pBackendVTable = pVTable; - pDecoder->pBackendUserData = pConfig->pCustomBackendUserData; - - return MA_SUCCESS; -} - -static ma_result ma_decoder_init_from_file__internal(const ma_decoding_backend_vtable* pVTable, void* pVTableUserData, const char* pFilePath, const ma_decoder_config* pConfig, ma_decoder* pDecoder) -{ - ma_result result; - ma_decoding_backend_config backendConfig; - ma_data_source* pBackend; - - MA_ASSERT(pVTable != NULL); - MA_ASSERT(pConfig != NULL); - MA_ASSERT(pDecoder != NULL); - - if (pVTable->onInitFile == NULL) { - return MA_NOT_IMPLEMENTED; - } - - backendConfig = ma_decoding_backend_config_init(pConfig->format, pConfig->seekPointCount); - - result = pVTable->onInitFile(pVTableUserData, pFilePath, &backendConfig, &pDecoder->allocationCallbacks, &pBackend); - if (result != MA_SUCCESS) { - return result; /* Failed to initialize the backend from this vtable. */ - } - - /* Getting here means we were able to initialize the backend so we can now initialize the decoder. */ - pDecoder->pBackend = pBackend; - pDecoder->pBackendVTable = pVTable; - pDecoder->pBackendUserData = pConfig->pCustomBackendUserData; - - return MA_SUCCESS; -} - -static ma_result ma_decoder_init_from_file_w__internal(const ma_decoding_backend_vtable* pVTable, void* pVTableUserData, const wchar_t* pFilePath, const ma_decoder_config* pConfig, ma_decoder* pDecoder) -{ - ma_result result; - ma_decoding_backend_config backendConfig; - ma_data_source* pBackend; - - MA_ASSERT(pVTable != NULL); - MA_ASSERT(pConfig != NULL); - MA_ASSERT(pDecoder != NULL); - - if (pVTable->onInitFileW == NULL) { - return MA_NOT_IMPLEMENTED; - } - - backendConfig = ma_decoding_backend_config_init(pConfig->format, pConfig->seekPointCount); - - result = pVTable->onInitFileW(pVTableUserData, pFilePath, &backendConfig, &pDecoder->allocationCallbacks, &pBackend); - if (result != MA_SUCCESS) { - return result; /* Failed to initialize the backend from this vtable. */ - } - - /* Getting here means we were able to initialize the backend so we can now initialize the decoder. */ - pDecoder->pBackend = pBackend; - pDecoder->pBackendVTable = pVTable; - pDecoder->pBackendUserData = pConfig->pCustomBackendUserData; - - return MA_SUCCESS; -} - -static ma_result ma_decoder_init_from_memory__internal(const ma_decoding_backend_vtable* pVTable, void* pVTableUserData, const void* pData, size_t dataSize, const ma_decoder_config* pConfig, ma_decoder* pDecoder) -{ - ma_result result; - ma_decoding_backend_config backendConfig; - ma_data_source* pBackend; - - MA_ASSERT(pVTable != NULL); - MA_ASSERT(pConfig != NULL); - MA_ASSERT(pDecoder != NULL); - - if (pVTable->onInitMemory == NULL) { - return MA_NOT_IMPLEMENTED; - } - - backendConfig = ma_decoding_backend_config_init(pConfig->format, pConfig->seekPointCount); - - result = pVTable->onInitMemory(pVTableUserData, pData, dataSize, &backendConfig, &pDecoder->allocationCallbacks, &pBackend); - if (result != MA_SUCCESS) { - return result; /* Failed to initialize the backend from this vtable. */ - } - - /* Getting here means we were able to initialize the backend so we can now initialize the decoder. */ - pDecoder->pBackend = pBackend; - pDecoder->pBackendVTable = pVTable; - pDecoder->pBackendUserData = pConfig->pCustomBackendUserData; - - return MA_SUCCESS; -} - - - -static ma_result ma_decoder_init_custom__internal(const ma_decoder_config* pConfig, ma_decoder* pDecoder) -{ - ma_result result = MA_NO_BACKEND; - size_t ivtable; - - MA_ASSERT(pConfig != NULL); - MA_ASSERT(pDecoder != NULL); - - if (pConfig->ppCustomBackendVTables == NULL) { - return MA_NO_BACKEND; - } - - /* The order each backend is listed is what defines the priority. */ - for (ivtable = 0; ivtable < pConfig->customBackendCount; ivtable += 1) { - const ma_decoding_backend_vtable* pVTable = pConfig->ppCustomBackendVTables[ivtable]; - if (pVTable != NULL) { - result = ma_decoder_init_from_vtable__internal(pVTable, pConfig->pCustomBackendUserData, pConfig, pDecoder); - if (result == MA_SUCCESS) { - return MA_SUCCESS; - } else { - /* Initialization failed. Move on to the next one, but seek back to the start first so the next vtable starts from the first byte of the file. */ - result = ma_decoder_seek_bytes(pDecoder, 0, ma_seek_origin_start); - if (result != MA_SUCCESS) { - return result; /* Failed to seek back to the start. */ - } - } - } else { - /* No vtable. */ - } - } - - /* Getting here means we couldn't find a backend. */ - return MA_NO_BACKEND; -} - -static ma_result ma_decoder_init_custom_from_file__internal(const char* pFilePath, const ma_decoder_config* pConfig, ma_decoder* pDecoder) -{ - ma_result result = MA_NO_BACKEND; - size_t ivtable; - - MA_ASSERT(pConfig != NULL); - MA_ASSERT(pDecoder != NULL); - - if (pConfig->ppCustomBackendVTables == NULL) { - return MA_NO_BACKEND; - } - - /* The order each backend is listed is what defines the priority. */ - for (ivtable = 0; ivtable < pConfig->customBackendCount; ivtable += 1) { - const ma_decoding_backend_vtable* pVTable = pConfig->ppCustomBackendVTables[ivtable]; - if (pVTable != NULL) { - result = ma_decoder_init_from_file__internal(pVTable, pConfig->pCustomBackendUserData, pFilePath, pConfig, pDecoder); - if (result == MA_SUCCESS) { - return MA_SUCCESS; - } - } else { - /* No vtable. */ - } - } - - /* Getting here means we couldn't find a backend. */ - return MA_NO_BACKEND; -} - -static ma_result ma_decoder_init_custom_from_file_w__internal(const wchar_t* pFilePath, const ma_decoder_config* pConfig, ma_decoder* pDecoder) -{ - ma_result result = MA_NO_BACKEND; - size_t ivtable; - - MA_ASSERT(pConfig != NULL); - MA_ASSERT(pDecoder != NULL); - - if (pConfig->ppCustomBackendVTables == NULL) { - return MA_NO_BACKEND; - } - - /* The order each backend is listed is what defines the priority. */ - for (ivtable = 0; ivtable < pConfig->customBackendCount; ivtable += 1) { - const ma_decoding_backend_vtable* pVTable = pConfig->ppCustomBackendVTables[ivtable]; - if (pVTable != NULL) { - result = ma_decoder_init_from_file_w__internal(pVTable, pConfig->pCustomBackendUserData, pFilePath, pConfig, pDecoder); - if (result == MA_SUCCESS) { - return MA_SUCCESS; - } - } else { - /* No vtable. */ - } - } - - /* Getting here means we couldn't find a backend. */ - return MA_NO_BACKEND; -} - -static ma_result ma_decoder_init_custom_from_memory__internal(const void* pData, size_t dataSize, const ma_decoder_config* pConfig, ma_decoder* pDecoder) -{ - ma_result result = MA_NO_BACKEND; - size_t ivtable; - - MA_ASSERT(pConfig != NULL); - MA_ASSERT(pDecoder != NULL); - - if (pConfig->ppCustomBackendVTables == NULL) { - return MA_NO_BACKEND; - } - - /* The order each backend is listed is what defines the priority. */ - for (ivtable = 0; ivtable < pConfig->customBackendCount; ivtable += 1) { - const ma_decoding_backend_vtable* pVTable = pConfig->ppCustomBackendVTables[ivtable]; - if (pVTable != NULL) { - result = ma_decoder_init_from_memory__internal(pVTable, pConfig->pCustomBackendUserData, pData, dataSize, pConfig, pDecoder); - if (result == MA_SUCCESS) { - return MA_SUCCESS; - } - } else { - /* No vtable. */ - } - } - - /* Getting here means we couldn't find a backend. */ - return MA_NO_BACKEND; -} - - -/* WAV */ -#ifdef ma_dr_wav_h -#define MA_HAS_WAV - -typedef struct -{ - ma_data_source_base ds; - ma_read_proc onRead; - ma_seek_proc onSeek; - ma_tell_proc onTell; - void* pReadSeekTellUserData; - ma_format format; /* Can be f32, s16 or s32. */ -#if !defined(MA_NO_WAV) - ma_dr_wav dr; -#endif -} ma_wav; - -MA_API ma_result ma_wav_init(ma_read_proc onRead, ma_seek_proc onSeek, ma_tell_proc onTell, void* pReadSeekTellUserData, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_wav* pWav); -MA_API ma_result ma_wav_init_file(const char* pFilePath, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_wav* pWav); -MA_API ma_result ma_wav_init_file_w(const wchar_t* pFilePath, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_wav* pWav); -MA_API ma_result ma_wav_init_memory(const void* pData, size_t dataSize, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_wav* pWav); -MA_API void ma_wav_uninit(ma_wav* pWav, const ma_allocation_callbacks* pAllocationCallbacks); -MA_API ma_result ma_wav_read_pcm_frames(ma_wav* pWav, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead); -MA_API ma_result ma_wav_seek_to_pcm_frame(ma_wav* pWav, ma_uint64 frameIndex); -MA_API ma_result ma_wav_get_data_format(ma_wav* pWav, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate, ma_channel* pChannelMap, size_t channelMapCap); -MA_API ma_result ma_wav_get_cursor_in_pcm_frames(ma_wav* pWav, ma_uint64* pCursor); -MA_API ma_result ma_wav_get_length_in_pcm_frames(ma_wav* pWav, ma_uint64* pLength); - - -static ma_result ma_wav_ds_read(ma_data_source* pDataSource, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead) -{ - return ma_wav_read_pcm_frames((ma_wav*)pDataSource, pFramesOut, frameCount, pFramesRead); -} - -static ma_result ma_wav_ds_seek(ma_data_source* pDataSource, ma_uint64 frameIndex) -{ - return ma_wav_seek_to_pcm_frame((ma_wav*)pDataSource, frameIndex); -} - -static ma_result ma_wav_ds_get_data_format(ma_data_source* pDataSource, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate, ma_channel* pChannelMap, size_t channelMapCap) -{ - return ma_wav_get_data_format((ma_wav*)pDataSource, pFormat, pChannels, pSampleRate, pChannelMap, channelMapCap); -} - -static ma_result ma_wav_ds_get_cursor(ma_data_source* pDataSource, ma_uint64* pCursor) -{ - return ma_wav_get_cursor_in_pcm_frames((ma_wav*)pDataSource, pCursor); -} - -static ma_result ma_wav_ds_get_length(ma_data_source* pDataSource, ma_uint64* pLength) -{ - return ma_wav_get_length_in_pcm_frames((ma_wav*)pDataSource, pLength); -} - -static ma_data_source_vtable g_ma_wav_ds_vtable = -{ - ma_wav_ds_read, - ma_wav_ds_seek, - ma_wav_ds_get_data_format, - ma_wav_ds_get_cursor, - ma_wav_ds_get_length, - NULL, /* onSetLooping */ - 0 -}; - - -#if !defined(MA_NO_WAV) -static size_t ma_wav_dr_callback__read(void* pUserData, void* pBufferOut, size_t bytesToRead) -{ - ma_wav* pWav = (ma_wav*)pUserData; - ma_result result; - size_t bytesRead; - - MA_ASSERT(pWav != NULL); - - result = pWav->onRead(pWav->pReadSeekTellUserData, pBufferOut, bytesToRead, &bytesRead); - (void)result; - - return bytesRead; -} - -static ma_bool32 ma_wav_dr_callback__seek(void* pUserData, int offset, ma_dr_wav_seek_origin origin) -{ - ma_wav* pWav = (ma_wav*)pUserData; - ma_result result; - ma_seek_origin maSeekOrigin; - - MA_ASSERT(pWav != NULL); - - maSeekOrigin = ma_seek_origin_start; - if (origin == ma_dr_wav_seek_origin_current) { - maSeekOrigin = ma_seek_origin_current; - } - - result = pWav->onSeek(pWav->pReadSeekTellUserData, offset, maSeekOrigin); - if (result != MA_SUCCESS) { - return MA_FALSE; - } - - return MA_TRUE; -} -#endif - -static ma_result ma_wav_init_internal(const ma_decoding_backend_config* pConfig, ma_wav* pWav) -{ - ma_result result; - ma_data_source_config dataSourceConfig; - - if (pWav == NULL) { - return MA_INVALID_ARGS; - } - - MA_ZERO_OBJECT(pWav); - pWav->format = ma_format_unknown; /* Use closest match to source file by default. */ - - if (pConfig != NULL && (pConfig->preferredFormat == ma_format_f32 || pConfig->preferredFormat == ma_format_s16 || pConfig->preferredFormat == ma_format_s32)) { - pWav->format = pConfig->preferredFormat; - } else { - /* Getting here means something other than f32 and s16 was specified. Just leave this unset to use the default format. */ - } - - dataSourceConfig = ma_data_source_config_init(); - dataSourceConfig.vtable = &g_ma_wav_ds_vtable; - - result = ma_data_source_init(&dataSourceConfig, &pWav->ds); - if (result != MA_SUCCESS) { - return result; /* Failed to initialize the base data source. */ - } - - return MA_SUCCESS; -} - -static ma_result ma_wav_post_init(ma_wav* pWav) -{ - /* - If an explicit format was not specified, try picking the closest match based on the internal - format. The format needs to be supported by miniaudio. - */ - if (pWav->format == ma_format_unknown) { - switch (pWav->dr.translatedFormatTag) - { - case MA_DR_WAVE_FORMAT_PCM: - { - if (pWav->dr.bitsPerSample == 8) { - pWav->format = ma_format_u8; - } else if (pWav->dr.bitsPerSample == 16) { - pWav->format = ma_format_s16; - } else if (pWav->dr.bitsPerSample == 24) { - pWav->format = ma_format_s24; - } else if (pWav->dr.bitsPerSample == 32) { - pWav->format = ma_format_s32; - } - } break; - - case MA_DR_WAVE_FORMAT_IEEE_FLOAT: - { - if (pWav->dr.bitsPerSample == 32) { - pWav->format = ma_format_f32; - } - } break; - - default: break; - } - - /* Fall back to f32 if we couldn't find anything. */ - if (pWav->format == ma_format_unknown) { - pWav->format = ma_format_f32; - } - } - - return MA_SUCCESS; -} - -MA_API ma_result ma_wav_init(ma_read_proc onRead, ma_seek_proc onSeek, ma_tell_proc onTell, void* pReadSeekTellUserData, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_wav* pWav) -{ - ma_result result; - - result = ma_wav_init_internal(pConfig, pWav); - if (result != MA_SUCCESS) { - return result; - } - - if (onRead == NULL || onSeek == NULL) { - return MA_INVALID_ARGS; /* onRead and onSeek are mandatory. */ - } - - pWav->onRead = onRead; - pWav->onSeek = onSeek; - pWav->onTell = onTell; - pWav->pReadSeekTellUserData = pReadSeekTellUserData; - - #if !defined(MA_NO_WAV) - { - ma_bool32 wavResult; - - wavResult = ma_dr_wav_init(&pWav->dr, ma_wav_dr_callback__read, ma_wav_dr_callback__seek, pWav, pAllocationCallbacks); - if (wavResult != MA_TRUE) { - return MA_INVALID_FILE; - } - - ma_wav_post_init(pWav); - - return MA_SUCCESS; - } - #else - { - /* wav is disabled. */ - (void)pAllocationCallbacks; - return MA_NOT_IMPLEMENTED; - } - #endif -} - -MA_API ma_result ma_wav_init_file(const char* pFilePath, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_wav* pWav) -{ - ma_result result; - - result = ma_wav_init_internal(pConfig, pWav); - if (result != MA_SUCCESS) { - return result; - } - - #if !defined(MA_NO_WAV) - { - ma_bool32 wavResult; - - wavResult = ma_dr_wav_init_file(&pWav->dr, pFilePath, pAllocationCallbacks); - if (wavResult != MA_TRUE) { - return MA_INVALID_FILE; - } - - ma_wav_post_init(pWav); - - return MA_SUCCESS; - } - #else - { - /* wav is disabled. */ - (void)pFilePath; - (void)pAllocationCallbacks; - return MA_NOT_IMPLEMENTED; - } - #endif -} - -MA_API ma_result ma_wav_init_file_w(const wchar_t* pFilePath, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_wav* pWav) -{ - ma_result result; - - result = ma_wav_init_internal(pConfig, pWav); - if (result != MA_SUCCESS) { - return result; - } - - #if !defined(MA_NO_WAV) - { - ma_bool32 wavResult; - - wavResult = ma_dr_wav_init_file_w(&pWav->dr, pFilePath, pAllocationCallbacks); - if (wavResult != MA_TRUE) { - return MA_INVALID_FILE; - } - - ma_wav_post_init(pWav); - - return MA_SUCCESS; - } - #else - { - /* wav is disabled. */ - (void)pFilePath; - (void)pAllocationCallbacks; - return MA_NOT_IMPLEMENTED; - } - #endif -} - -MA_API ma_result ma_wav_init_memory(const void* pData, size_t dataSize, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_wav* pWav) -{ - ma_result result; - - result = ma_wav_init_internal(pConfig, pWav); - if (result != MA_SUCCESS) { - return result; - } - - #if !defined(MA_NO_WAV) - { - ma_bool32 wavResult; - - wavResult = ma_dr_wav_init_memory(&pWav->dr, pData, dataSize, pAllocationCallbacks); - if (wavResult != MA_TRUE) { - return MA_INVALID_FILE; - } - - ma_wav_post_init(pWav); - - return MA_SUCCESS; - } - #else - { - /* wav is disabled. */ - (void)pData; - (void)dataSize; - (void)pAllocationCallbacks; - return MA_NOT_IMPLEMENTED; - } - #endif -} - -MA_API void ma_wav_uninit(ma_wav* pWav, const ma_allocation_callbacks* pAllocationCallbacks) -{ - if (pWav == NULL) { - return; - } - - (void)pAllocationCallbacks; - - #if !defined(MA_NO_WAV) - { - ma_dr_wav_uninit(&pWav->dr); - } - #else - { - /* wav is disabled. Should never hit this since initialization would have failed. */ - MA_ASSERT(MA_FALSE); - } - #endif - - ma_data_source_uninit(&pWav->ds); -} - -MA_API ma_result ma_wav_read_pcm_frames(ma_wav* pWav, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead) -{ - if (pFramesRead != NULL) { - *pFramesRead = 0; - } - - if (frameCount == 0) { - return MA_INVALID_ARGS; - } - - if (pWav == NULL) { - return MA_INVALID_ARGS; - } - - #if !defined(MA_NO_WAV) - { - /* We always use floating point format. */ - ma_result result = MA_SUCCESS; /* Must be initialized to MA_SUCCESS. */ - ma_uint64 totalFramesRead = 0; - ma_format format; - - ma_wav_get_data_format(pWav, &format, NULL, NULL, NULL, 0); - - switch (format) - { - case ma_format_f32: - { - totalFramesRead = ma_dr_wav_read_pcm_frames_f32(&pWav->dr, frameCount, (float*)pFramesOut); - } break; - - case ma_format_s16: - { - totalFramesRead = ma_dr_wav_read_pcm_frames_s16(&pWav->dr, frameCount, (ma_int16*)pFramesOut); - } break; - - case ma_format_s32: - { - totalFramesRead = ma_dr_wav_read_pcm_frames_s32(&pWav->dr, frameCount, (ma_int32*)pFramesOut); - } break; - - /* Fallback to a raw read. */ - case ma_format_unknown: return MA_INVALID_OPERATION; /* <-- this should never be hit because initialization would just fall back to a supported format. */ - default: - { - totalFramesRead = ma_dr_wav_read_pcm_frames(&pWav->dr, frameCount, pFramesOut); - } break; - } - - /* In the future we'll update ma_dr_wav to return MA_AT_END for us. */ - if (totalFramesRead == 0) { - result = MA_AT_END; - } - - if (pFramesRead != NULL) { - *pFramesRead = totalFramesRead; - } - - if (result == MA_SUCCESS && totalFramesRead == 0) { - result = MA_AT_END; - } - - return result; - } - #else - { - /* wav is disabled. Should never hit this since initialization would have failed. */ - MA_ASSERT(MA_FALSE); - - (void)pFramesOut; - (void)frameCount; - (void)pFramesRead; - - return MA_NOT_IMPLEMENTED; - } - #endif -} - -MA_API ma_result ma_wav_seek_to_pcm_frame(ma_wav* pWav, ma_uint64 frameIndex) -{ - if (pWav == NULL) { - return MA_INVALID_ARGS; - } - - #if !defined(MA_NO_WAV) - { - ma_bool32 wavResult; - - wavResult = ma_dr_wav_seek_to_pcm_frame(&pWav->dr, frameIndex); - if (wavResult != MA_TRUE) { - return MA_ERROR; - } - - return MA_SUCCESS; - } - #else - { - /* wav is disabled. Should never hit this since initialization would have failed. */ - MA_ASSERT(MA_FALSE); - - (void)frameIndex; - - return MA_NOT_IMPLEMENTED; - } - #endif -} - -MA_API ma_result ma_wav_get_data_format(ma_wav* pWav, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate, ma_channel* pChannelMap, size_t channelMapCap) -{ - /* Defaults for safety. */ - if (pFormat != NULL) { - *pFormat = ma_format_unknown; - } - if (pChannels != NULL) { - *pChannels = 0; - } - if (pSampleRate != NULL) { - *pSampleRate = 0; - } - if (pChannelMap != NULL) { - MA_ZERO_MEMORY(pChannelMap, sizeof(*pChannelMap) * channelMapCap); - } - - if (pWav == NULL) { - return MA_INVALID_OPERATION; - } - - if (pFormat != NULL) { - *pFormat = pWav->format; - } - - #if !defined(MA_NO_WAV) - { - if (pChannels != NULL) { - *pChannels = pWav->dr.channels; - } - - if (pSampleRate != NULL) { - *pSampleRate = pWav->dr.sampleRate; - } - - if (pChannelMap != NULL) { - ma_channel_map_init_standard(ma_standard_channel_map_microsoft, pChannelMap, channelMapCap, pWav->dr.channels); - } - - return MA_SUCCESS; - } - #else - { - /* wav is disabled. Should never hit this since initialization would have failed. */ - MA_ASSERT(MA_FALSE); - return MA_NOT_IMPLEMENTED; - } - #endif -} - -MA_API ma_result ma_wav_get_cursor_in_pcm_frames(ma_wav* pWav, ma_uint64* pCursor) -{ - if (pCursor == NULL) { - return MA_INVALID_ARGS; - } - - *pCursor = 0; /* Safety. */ - - if (pWav == NULL) { - return MA_INVALID_ARGS; - } - - #if !defined(MA_NO_WAV) - { - ma_result wavResult = ma_dr_wav_get_cursor_in_pcm_frames(&pWav->dr, pCursor); - if (wavResult != MA_SUCCESS) { - return (ma_result)wavResult; /* ma_dr_wav result codes map to miniaudio's. */ - } - - return MA_SUCCESS; - } - #else - { - /* wav is disabled. Should never hit this since initialization would have failed. */ - MA_ASSERT(MA_FALSE); - return MA_NOT_IMPLEMENTED; - } - #endif -} - -MA_API ma_result ma_wav_get_length_in_pcm_frames(ma_wav* pWav, ma_uint64* pLength) -{ - if (pLength == NULL) { - return MA_INVALID_ARGS; - } - - *pLength = 0; /* Safety. */ - - if (pWav == NULL) { - return MA_INVALID_ARGS; - } - - #if !defined(MA_NO_WAV) - { - ma_result wavResult = ma_dr_wav_get_length_in_pcm_frames(&pWav->dr, pLength); - if (wavResult != MA_SUCCESS) { - return (ma_result)wavResult; /* ma_dr_wav result codes map to miniaudio's. */ - } - - return MA_SUCCESS; - } - #else - { - /* wav is disabled. Should never hit this since initialization would have failed. */ - MA_ASSERT(MA_FALSE); - return MA_NOT_IMPLEMENTED; - } - #endif -} - - -static ma_result ma_decoding_backend_init__wav(void* pUserData, ma_read_proc onRead, ma_seek_proc onSeek, ma_tell_proc onTell, void* pReadSeekTellUserData, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_data_source** ppBackend) -{ - ma_result result; - ma_wav* pWav; - - (void)pUserData; /* For now not using pUserData, but once we start storing the vorbis decoder state within the ma_decoder structure this will be set to the decoder so we can avoid a malloc. */ - - /* For now we're just allocating the decoder backend on the heap. */ - pWav = (ma_wav*)ma_malloc(sizeof(*pWav), pAllocationCallbacks); - if (pWav == NULL) { - return MA_OUT_OF_MEMORY; - } - - result = ma_wav_init(onRead, onSeek, onTell, pReadSeekTellUserData, pConfig, pAllocationCallbacks, pWav); - if (result != MA_SUCCESS) { - ma_free(pWav, pAllocationCallbacks); - return result; - } - - *ppBackend = pWav; - - return MA_SUCCESS; -} - -static ma_result ma_decoding_backend_init_file__wav(void* pUserData, const char* pFilePath, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_data_source** ppBackend) -{ - ma_result result; - ma_wav* pWav; - - (void)pUserData; /* For now not using pUserData, but once we start storing the vorbis decoder state within the ma_decoder structure this will be set to the decoder so we can avoid a malloc. */ - - /* For now we're just allocating the decoder backend on the heap. */ - pWav = (ma_wav*)ma_malloc(sizeof(*pWav), pAllocationCallbacks); - if (pWav == NULL) { - return MA_OUT_OF_MEMORY; - } - - result = ma_wav_init_file(pFilePath, pConfig, pAllocationCallbacks, pWav); - if (result != MA_SUCCESS) { - ma_free(pWav, pAllocationCallbacks); - return result; - } - - *ppBackend = pWav; - - return MA_SUCCESS; -} - -static ma_result ma_decoding_backend_init_file_w__wav(void* pUserData, const wchar_t* pFilePath, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_data_source** ppBackend) -{ - ma_result result; - ma_wav* pWav; - - (void)pUserData; /* For now not using pUserData, but once we start storing the vorbis decoder state within the ma_decoder structure this will be set to the decoder so we can avoid a malloc. */ - - /* For now we're just allocating the decoder backend on the heap. */ - pWav = (ma_wav*)ma_malloc(sizeof(*pWav), pAllocationCallbacks); - if (pWav == NULL) { - return MA_OUT_OF_MEMORY; - } - - result = ma_wav_init_file_w(pFilePath, pConfig, pAllocationCallbacks, pWav); - if (result != MA_SUCCESS) { - ma_free(pWav, pAllocationCallbacks); - return result; - } - - *ppBackend = pWav; - - return MA_SUCCESS; -} - -static ma_result ma_decoding_backend_init_memory__wav(void* pUserData, const void* pData, size_t dataSize, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_data_source** ppBackend) -{ - ma_result result; - ma_wav* pWav; - - (void)pUserData; /* For now not using pUserData, but once we start storing the vorbis decoder state within the ma_decoder structure this will be set to the decoder so we can avoid a malloc. */ - - /* For now we're just allocating the decoder backend on the heap. */ - pWav = (ma_wav*)ma_malloc(sizeof(*pWav), pAllocationCallbacks); - if (pWav == NULL) { - return MA_OUT_OF_MEMORY; - } - - result = ma_wav_init_memory(pData, dataSize, pConfig, pAllocationCallbacks, pWav); - if (result != MA_SUCCESS) { - ma_free(pWav, pAllocationCallbacks); - return result; - } - - *ppBackend = pWav; - - return MA_SUCCESS; -} - -static void ma_decoding_backend_uninit__wav(void* pUserData, ma_data_source* pBackend, const ma_allocation_callbacks* pAllocationCallbacks) -{ - ma_wav* pWav = (ma_wav*)pBackend; - - (void)pUserData; - - ma_wav_uninit(pWav, pAllocationCallbacks); - ma_free(pWav, pAllocationCallbacks); -} - -static ma_decoding_backend_vtable g_ma_decoding_backend_vtable_wav = -{ - ma_decoding_backend_init__wav, - ma_decoding_backend_init_file__wav, - ma_decoding_backend_init_file_w__wav, - ma_decoding_backend_init_memory__wav, - ma_decoding_backend_uninit__wav -}; - -static ma_result ma_decoder_init_wav__internal(const ma_decoder_config* pConfig, ma_decoder* pDecoder) -{ - return ma_decoder_init_from_vtable__internal(&g_ma_decoding_backend_vtable_wav, NULL, pConfig, pDecoder); -} - -static ma_result ma_decoder_init_wav_from_file__internal(const char* pFilePath, const ma_decoder_config* pConfig, ma_decoder* pDecoder) -{ - return ma_decoder_init_from_file__internal(&g_ma_decoding_backend_vtable_wav, NULL, pFilePath, pConfig, pDecoder); -} - -static ma_result ma_decoder_init_wav_from_file_w__internal(const wchar_t* pFilePath, const ma_decoder_config* pConfig, ma_decoder* pDecoder) -{ - return ma_decoder_init_from_file_w__internal(&g_ma_decoding_backend_vtable_wav, NULL, pFilePath, pConfig, pDecoder); -} - -static ma_result ma_decoder_init_wav_from_memory__internal(const void* pData, size_t dataSize, const ma_decoder_config* pConfig, ma_decoder* pDecoder) -{ - return ma_decoder_init_from_memory__internal(&g_ma_decoding_backend_vtable_wav, NULL, pData, dataSize, pConfig, pDecoder); -} -#endif /* ma_dr_wav_h */ - -/* FLAC */ -#ifdef ma_dr_flac_h -#define MA_HAS_FLAC - -typedef struct -{ - ma_data_source_base ds; - ma_read_proc onRead; - ma_seek_proc onSeek; - ma_tell_proc onTell; - void* pReadSeekTellUserData; - ma_format format; /* Can be f32, s16 or s32. */ -#if !defined(MA_NO_FLAC) - ma_dr_flac* dr; -#endif -} ma_flac; - -MA_API ma_result ma_flac_init(ma_read_proc onRead, ma_seek_proc onSeek, ma_tell_proc onTell, void* pReadSeekTellUserData, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_flac* pFlac); -MA_API ma_result ma_flac_init_file(const char* pFilePath, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_flac* pFlac); -MA_API ma_result ma_flac_init_file_w(const wchar_t* pFilePath, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_flac* pFlac); -MA_API ma_result ma_flac_init_memory(const void* pData, size_t dataSize, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_flac* pFlac); -MA_API void ma_flac_uninit(ma_flac* pFlac, const ma_allocation_callbacks* pAllocationCallbacks); -MA_API ma_result ma_flac_read_pcm_frames(ma_flac* pFlac, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead); -MA_API ma_result ma_flac_seek_to_pcm_frame(ma_flac* pFlac, ma_uint64 frameIndex); -MA_API ma_result ma_flac_get_data_format(ma_flac* pFlac, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate, ma_channel* pChannelMap, size_t channelMapCap); -MA_API ma_result ma_flac_get_cursor_in_pcm_frames(ma_flac* pFlac, ma_uint64* pCursor); -MA_API ma_result ma_flac_get_length_in_pcm_frames(ma_flac* pFlac, ma_uint64* pLength); - - -static ma_result ma_flac_ds_read(ma_data_source* pDataSource, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead) -{ - return ma_flac_read_pcm_frames((ma_flac*)pDataSource, pFramesOut, frameCount, pFramesRead); -} - -static ma_result ma_flac_ds_seek(ma_data_source* pDataSource, ma_uint64 frameIndex) -{ - return ma_flac_seek_to_pcm_frame((ma_flac*)pDataSource, frameIndex); -} - -static ma_result ma_flac_ds_get_data_format(ma_data_source* pDataSource, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate, ma_channel* pChannelMap, size_t channelMapCap) -{ - return ma_flac_get_data_format((ma_flac*)pDataSource, pFormat, pChannels, pSampleRate, pChannelMap, channelMapCap); -} - -static ma_result ma_flac_ds_get_cursor(ma_data_source* pDataSource, ma_uint64* pCursor) -{ - return ma_flac_get_cursor_in_pcm_frames((ma_flac*)pDataSource, pCursor); -} - -static ma_result ma_flac_ds_get_length(ma_data_source* pDataSource, ma_uint64* pLength) -{ - return ma_flac_get_length_in_pcm_frames((ma_flac*)pDataSource, pLength); -} - -static ma_data_source_vtable g_ma_flac_ds_vtable = -{ - ma_flac_ds_read, - ma_flac_ds_seek, - ma_flac_ds_get_data_format, - ma_flac_ds_get_cursor, - ma_flac_ds_get_length, - NULL, /* onSetLooping */ - 0 -}; - - -#if !defined(MA_NO_FLAC) -static size_t ma_flac_dr_callback__read(void* pUserData, void* pBufferOut, size_t bytesToRead) -{ - ma_flac* pFlac = (ma_flac*)pUserData; - ma_result result; - size_t bytesRead; - - MA_ASSERT(pFlac != NULL); - - result = pFlac->onRead(pFlac->pReadSeekTellUserData, pBufferOut, bytesToRead, &bytesRead); - (void)result; - - return bytesRead; -} - -static ma_bool32 ma_flac_dr_callback__seek(void* pUserData, int offset, ma_dr_flac_seek_origin origin) -{ - ma_flac* pFlac = (ma_flac*)pUserData; - ma_result result; - ma_seek_origin maSeekOrigin; - - MA_ASSERT(pFlac != NULL); - - maSeekOrigin = ma_seek_origin_start; - if (origin == ma_dr_flac_seek_origin_current) { - maSeekOrigin = ma_seek_origin_current; - } - - result = pFlac->onSeek(pFlac->pReadSeekTellUserData, offset, maSeekOrigin); - if (result != MA_SUCCESS) { - return MA_FALSE; - } - - return MA_TRUE; -} -#endif - -static ma_result ma_flac_init_internal(const ma_decoding_backend_config* pConfig, ma_flac* pFlac) -{ - ma_result result; - ma_data_source_config dataSourceConfig; - - if (pFlac == NULL) { - return MA_INVALID_ARGS; - } - - MA_ZERO_OBJECT(pFlac); - pFlac->format = ma_format_f32; /* f32 by default. */ - - if (pConfig != NULL && (pConfig->preferredFormat == ma_format_f32 || pConfig->preferredFormat == ma_format_s16 || pConfig->preferredFormat == ma_format_s32)) { - pFlac->format = pConfig->preferredFormat; - } else { - /* Getting here means something other than f32 and s16 was specified. Just leave this unset to use the default format. */ - } - - dataSourceConfig = ma_data_source_config_init(); - dataSourceConfig.vtable = &g_ma_flac_ds_vtable; - - result = ma_data_source_init(&dataSourceConfig, &pFlac->ds); - if (result != MA_SUCCESS) { - return result; /* Failed to initialize the base data source. */ - } - - return MA_SUCCESS; -} - -MA_API ma_result ma_flac_init(ma_read_proc onRead, ma_seek_proc onSeek, ma_tell_proc onTell, void* pReadSeekTellUserData, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_flac* pFlac) -{ - ma_result result; - - result = ma_flac_init_internal(pConfig, pFlac); - if (result != MA_SUCCESS) { - return result; - } - - if (onRead == NULL || onSeek == NULL) { - return MA_INVALID_ARGS; /* onRead and onSeek are mandatory. */ - } - - pFlac->onRead = onRead; - pFlac->onSeek = onSeek; - pFlac->onTell = onTell; - pFlac->pReadSeekTellUserData = pReadSeekTellUserData; - - #if !defined(MA_NO_FLAC) - { - pFlac->dr = ma_dr_flac_open(ma_flac_dr_callback__read, ma_flac_dr_callback__seek, pFlac, pAllocationCallbacks); - if (pFlac->dr == NULL) { - return MA_INVALID_FILE; - } - - return MA_SUCCESS; - } - #else - { - /* flac is disabled. */ - (void)pAllocationCallbacks; - return MA_NOT_IMPLEMENTED; - } - #endif -} - -MA_API ma_result ma_flac_init_file(const char* pFilePath, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_flac* pFlac) -{ - ma_result result; - - result = ma_flac_init_internal(pConfig, pFlac); - if (result != MA_SUCCESS) { - return result; - } - - #if !defined(MA_NO_FLAC) - { - pFlac->dr = ma_dr_flac_open_file(pFilePath, pAllocationCallbacks); - if (pFlac->dr == NULL) { - return MA_INVALID_FILE; - } - - return MA_SUCCESS; - } - #else - { - /* flac is disabled. */ - (void)pFilePath; - (void)pAllocationCallbacks; - return MA_NOT_IMPLEMENTED; - } - #endif -} - -MA_API ma_result ma_flac_init_file_w(const wchar_t* pFilePath, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_flac* pFlac) -{ - ma_result result; - - result = ma_flac_init_internal(pConfig, pFlac); - if (result != MA_SUCCESS) { - return result; - } - - #if !defined(MA_NO_FLAC) - { - pFlac->dr = ma_dr_flac_open_file_w(pFilePath, pAllocationCallbacks); - if (pFlac->dr == NULL) { - return MA_INVALID_FILE; - } - - return MA_SUCCESS; - } - #else - { - /* flac is disabled. */ - (void)pFilePath; - (void)pAllocationCallbacks; - return MA_NOT_IMPLEMENTED; - } - #endif -} - -MA_API ma_result ma_flac_init_memory(const void* pData, size_t dataSize, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_flac* pFlac) -{ - ma_result result; - - result = ma_flac_init_internal(pConfig, pFlac); - if (result != MA_SUCCESS) { - return result; - } - - #if !defined(MA_NO_FLAC) - { - pFlac->dr = ma_dr_flac_open_memory(pData, dataSize, pAllocationCallbacks); - if (pFlac->dr == NULL) { - return MA_INVALID_FILE; - } - - return MA_SUCCESS; - } - #else - { - /* flac is disabled. */ - (void)pData; - (void)dataSize; - (void)pAllocationCallbacks; - return MA_NOT_IMPLEMENTED; - } - #endif -} - -MA_API void ma_flac_uninit(ma_flac* pFlac, const ma_allocation_callbacks* pAllocationCallbacks) -{ - if (pFlac == NULL) { - return; - } - - (void)pAllocationCallbacks; - - #if !defined(MA_NO_FLAC) - { - ma_dr_flac_close(pFlac->dr); - } - #else - { - /* flac is disabled. Should never hit this since initialization would have failed. */ - MA_ASSERT(MA_FALSE); - } - #endif - - ma_data_source_uninit(&pFlac->ds); -} - -MA_API ma_result ma_flac_read_pcm_frames(ma_flac* pFlac, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead) -{ - if (pFramesRead != NULL) { - *pFramesRead = 0; - } - - if (frameCount == 0) { - return MA_INVALID_ARGS; - } - - if (pFlac == NULL) { - return MA_INVALID_ARGS; - } - - #if !defined(MA_NO_FLAC) - { - /* We always use floating point format. */ - ma_result result = MA_SUCCESS; /* Must be initialized to MA_SUCCESS. */ - ma_uint64 totalFramesRead = 0; - ma_format format; - - ma_flac_get_data_format(pFlac, &format, NULL, NULL, NULL, 0); - - switch (format) - { - case ma_format_f32: - { - totalFramesRead = ma_dr_flac_read_pcm_frames_f32(pFlac->dr, frameCount, (float*)pFramesOut); - } break; - - case ma_format_s16: - { - totalFramesRead = ma_dr_flac_read_pcm_frames_s16(pFlac->dr, frameCount, (ma_int16*)pFramesOut); - } break; - - case ma_format_s32: - { - totalFramesRead = ma_dr_flac_read_pcm_frames_s32(pFlac->dr, frameCount, (ma_int32*)pFramesOut); - } break; - - case ma_format_u8: - case ma_format_s24: - case ma_format_unknown: - default: - { - return MA_INVALID_OPERATION; - }; - } - - /* In the future we'll update ma_dr_flac to return MA_AT_END for us. */ - if (totalFramesRead == 0) { - result = MA_AT_END; - } - - if (pFramesRead != NULL) { - *pFramesRead = totalFramesRead; - } - - if (result == MA_SUCCESS && totalFramesRead == 0) { - result = MA_AT_END; - } - - return result; - } - #else - { - /* flac is disabled. Should never hit this since initialization would have failed. */ - MA_ASSERT(MA_FALSE); - - (void)pFramesOut; - (void)frameCount; - (void)pFramesRead; - - return MA_NOT_IMPLEMENTED; - } - #endif -} - -MA_API ma_result ma_flac_seek_to_pcm_frame(ma_flac* pFlac, ma_uint64 frameIndex) -{ - if (pFlac == NULL) { - return MA_INVALID_ARGS; - } - - #if !defined(MA_NO_FLAC) - { - ma_bool32 flacResult; - - flacResult = ma_dr_flac_seek_to_pcm_frame(pFlac->dr, frameIndex); - if (flacResult != MA_TRUE) { - return MA_ERROR; - } - - return MA_SUCCESS; - } - #else - { - /* flac is disabled. Should never hit this since initialization would have failed. */ - MA_ASSERT(MA_FALSE); - - (void)frameIndex; - - return MA_NOT_IMPLEMENTED; - } - #endif -} - -MA_API ma_result ma_flac_get_data_format(ma_flac* pFlac, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate, ma_channel* pChannelMap, size_t channelMapCap) -{ - /* Defaults for safety. */ - if (pFormat != NULL) { - *pFormat = ma_format_unknown; - } - if (pChannels != NULL) { - *pChannels = 0; - } - if (pSampleRate != NULL) { - *pSampleRate = 0; - } - if (pChannelMap != NULL) { - MA_ZERO_MEMORY(pChannelMap, sizeof(*pChannelMap) * channelMapCap); - } - - if (pFlac == NULL) { - return MA_INVALID_OPERATION; - } - - if (pFormat != NULL) { - *pFormat = pFlac->format; - } - - #if !defined(MA_NO_FLAC) - { - if (pChannels != NULL) { - *pChannels = pFlac->dr->channels; - } - - if (pSampleRate != NULL) { - *pSampleRate = pFlac->dr->sampleRate; - } - - if (pChannelMap != NULL) { - ma_channel_map_init_standard(ma_standard_channel_map_microsoft, pChannelMap, channelMapCap, pFlac->dr->channels); - } - - return MA_SUCCESS; - } - #else - { - /* flac is disabled. Should never hit this since initialization would have failed. */ - MA_ASSERT(MA_FALSE); - return MA_NOT_IMPLEMENTED; - } - #endif -} - -MA_API ma_result ma_flac_get_cursor_in_pcm_frames(ma_flac* pFlac, ma_uint64* pCursor) -{ - if (pCursor == NULL) { - return MA_INVALID_ARGS; - } - - *pCursor = 0; /* Safety. */ - - if (pFlac == NULL) { - return MA_INVALID_ARGS; - } - - #if !defined(MA_NO_FLAC) - { - *pCursor = pFlac->dr->currentPCMFrame; - - return MA_SUCCESS; - } - #else - { - /* flac is disabled. Should never hit this since initialization would have failed. */ - MA_ASSERT(MA_FALSE); - return MA_NOT_IMPLEMENTED; - } - #endif -} - -MA_API ma_result ma_flac_get_length_in_pcm_frames(ma_flac* pFlac, ma_uint64* pLength) -{ - if (pLength == NULL) { - return MA_INVALID_ARGS; - } - - *pLength = 0; /* Safety. */ - - if (pFlac == NULL) { - return MA_INVALID_ARGS; - } - - #if !defined(MA_NO_FLAC) - { - *pLength = pFlac->dr->totalPCMFrameCount; - - return MA_SUCCESS; - } - #else - { - /* flac is disabled. Should never hit this since initialization would have failed. */ - MA_ASSERT(MA_FALSE); - return MA_NOT_IMPLEMENTED; - } - #endif -} - - -static ma_result ma_decoding_backend_init__flac(void* pUserData, ma_read_proc onRead, ma_seek_proc onSeek, ma_tell_proc onTell, void* pReadSeekTellUserData, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_data_source** ppBackend) -{ - ma_result result; - ma_flac* pFlac; - - (void)pUserData; /* For now not using pUserData, but once we start storing the vorbis decoder state within the ma_decoder structure this will be set to the decoder so we can avoid a malloc. */ - - /* For now we're just allocating the decoder backend on the heap. */ - pFlac = (ma_flac*)ma_malloc(sizeof(*pFlac), pAllocationCallbacks); - if (pFlac == NULL) { - return MA_OUT_OF_MEMORY; - } - - result = ma_flac_init(onRead, onSeek, onTell, pReadSeekTellUserData, pConfig, pAllocationCallbacks, pFlac); - if (result != MA_SUCCESS) { - ma_free(pFlac, pAllocationCallbacks); - return result; - } - - *ppBackend = pFlac; - - return MA_SUCCESS; -} - -static ma_result ma_decoding_backend_init_file__flac(void* pUserData, const char* pFilePath, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_data_source** ppBackend) -{ - ma_result result; - ma_flac* pFlac; - - (void)pUserData; /* For now not using pUserData, but once we start storing the vorbis decoder state within the ma_decoder structure this will be set to the decoder so we can avoid a malloc. */ - - /* For now we're just allocating the decoder backend on the heap. */ - pFlac = (ma_flac*)ma_malloc(sizeof(*pFlac), pAllocationCallbacks); - if (pFlac == NULL) { - return MA_OUT_OF_MEMORY; - } - - result = ma_flac_init_file(pFilePath, pConfig, pAllocationCallbacks, pFlac); - if (result != MA_SUCCESS) { - ma_free(pFlac, pAllocationCallbacks); - return result; - } - - *ppBackend = pFlac; - - return MA_SUCCESS; -} - -static ma_result ma_decoding_backend_init_file_w__flac(void* pUserData, const wchar_t* pFilePath, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_data_source** ppBackend) -{ - ma_result result; - ma_flac* pFlac; - - (void)pUserData; /* For now not using pUserData, but once we start storing the vorbis decoder state within the ma_decoder structure this will be set to the decoder so we can avoid a malloc. */ - - /* For now we're just allocating the decoder backend on the heap. */ - pFlac = (ma_flac*)ma_malloc(sizeof(*pFlac), pAllocationCallbacks); - if (pFlac == NULL) { - return MA_OUT_OF_MEMORY; - } - - result = ma_flac_init_file_w(pFilePath, pConfig, pAllocationCallbacks, pFlac); - if (result != MA_SUCCESS) { - ma_free(pFlac, pAllocationCallbacks); - return result; - } - - *ppBackend = pFlac; - - return MA_SUCCESS; -} - -static ma_result ma_decoding_backend_init_memory__flac(void* pUserData, const void* pData, size_t dataSize, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_data_source** ppBackend) -{ - ma_result result; - ma_flac* pFlac; - - (void)pUserData; /* For now not using pUserData, but once we start storing the vorbis decoder state within the ma_decoder structure this will be set to the decoder so we can avoid a malloc. */ - - /* For now we're just allocating the decoder backend on the heap. */ - pFlac = (ma_flac*)ma_malloc(sizeof(*pFlac), pAllocationCallbacks); - if (pFlac == NULL) { - return MA_OUT_OF_MEMORY; - } - - result = ma_flac_init_memory(pData, dataSize, pConfig, pAllocationCallbacks, pFlac); - if (result != MA_SUCCESS) { - ma_free(pFlac, pAllocationCallbacks); - return result; - } - - *ppBackend = pFlac; - - return MA_SUCCESS; -} - -static void ma_decoding_backend_uninit__flac(void* pUserData, ma_data_source* pBackend, const ma_allocation_callbacks* pAllocationCallbacks) -{ - ma_flac* pFlac = (ma_flac*)pBackend; - - (void)pUserData; - - ma_flac_uninit(pFlac, pAllocationCallbacks); - ma_free(pFlac, pAllocationCallbacks); -} - -static ma_decoding_backend_vtable g_ma_decoding_backend_vtable_flac = -{ - ma_decoding_backend_init__flac, - ma_decoding_backend_init_file__flac, - ma_decoding_backend_init_file_w__flac, - ma_decoding_backend_init_memory__flac, - ma_decoding_backend_uninit__flac -}; - -static ma_result ma_decoder_init_flac__internal(const ma_decoder_config* pConfig, ma_decoder* pDecoder) -{ - return ma_decoder_init_from_vtable__internal(&g_ma_decoding_backend_vtable_flac, NULL, pConfig, pDecoder); -} - -static ma_result ma_decoder_init_flac_from_file__internal(const char* pFilePath, const ma_decoder_config* pConfig, ma_decoder* pDecoder) -{ - return ma_decoder_init_from_file__internal(&g_ma_decoding_backend_vtable_flac, NULL, pFilePath, pConfig, pDecoder); -} - -static ma_result ma_decoder_init_flac_from_file_w__internal(const wchar_t* pFilePath, const ma_decoder_config* pConfig, ma_decoder* pDecoder) -{ - return ma_decoder_init_from_file_w__internal(&g_ma_decoding_backend_vtable_flac, NULL, pFilePath, pConfig, pDecoder); -} - -static ma_result ma_decoder_init_flac_from_memory__internal(const void* pData, size_t dataSize, const ma_decoder_config* pConfig, ma_decoder* pDecoder) -{ - return ma_decoder_init_from_memory__internal(&g_ma_decoding_backend_vtable_flac, NULL, pData, dataSize, pConfig, pDecoder); -} -#endif /* ma_dr_flac_h */ - -/* MP3 */ -#ifdef ma_dr_mp3_h -#define MA_HAS_MP3 - -typedef struct -{ - ma_data_source_base ds; - ma_read_proc onRead; - ma_seek_proc onSeek; - ma_tell_proc onTell; - void* pReadSeekTellUserData; - ma_format format; /* Can be f32 or s16. */ -#if !defined(MA_NO_MP3) - ma_dr_mp3 dr; - ma_uint32 seekPointCount; - ma_dr_mp3_seek_point* pSeekPoints; /* Only used if seek table generation is used. */ -#endif -} ma_mp3; - -MA_API ma_result ma_mp3_init(ma_read_proc onRead, ma_seek_proc onSeek, ma_tell_proc onTell, void* pReadSeekTellUserData, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_mp3* pMP3); -MA_API ma_result ma_mp3_init_file(const char* pFilePath, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_mp3* pMP3); -MA_API ma_result ma_mp3_init_file_w(const wchar_t* pFilePath, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_mp3* pMP3); -MA_API ma_result ma_mp3_init_memory(const void* pData, size_t dataSize, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_mp3* pMP3); -MA_API void ma_mp3_uninit(ma_mp3* pMP3, const ma_allocation_callbacks* pAllocationCallbacks); -MA_API ma_result ma_mp3_read_pcm_frames(ma_mp3* pMP3, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead); -MA_API ma_result ma_mp3_seek_to_pcm_frame(ma_mp3* pMP3, ma_uint64 frameIndex); -MA_API ma_result ma_mp3_get_data_format(ma_mp3* pMP3, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate, ma_channel* pChannelMap, size_t channelMapCap); -MA_API ma_result ma_mp3_get_cursor_in_pcm_frames(ma_mp3* pMP3, ma_uint64* pCursor); -MA_API ma_result ma_mp3_get_length_in_pcm_frames(ma_mp3* pMP3, ma_uint64* pLength); - - -static ma_result ma_mp3_ds_read(ma_data_source* pDataSource, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead) -{ - return ma_mp3_read_pcm_frames((ma_mp3*)pDataSource, pFramesOut, frameCount, pFramesRead); -} - -static ma_result ma_mp3_ds_seek(ma_data_source* pDataSource, ma_uint64 frameIndex) -{ - return ma_mp3_seek_to_pcm_frame((ma_mp3*)pDataSource, frameIndex); -} - -static ma_result ma_mp3_ds_get_data_format(ma_data_source* pDataSource, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate, ma_channel* pChannelMap, size_t channelMapCap) -{ - return ma_mp3_get_data_format((ma_mp3*)pDataSource, pFormat, pChannels, pSampleRate, pChannelMap, channelMapCap); -} - -static ma_result ma_mp3_ds_get_cursor(ma_data_source* pDataSource, ma_uint64* pCursor) -{ - return ma_mp3_get_cursor_in_pcm_frames((ma_mp3*)pDataSource, pCursor); -} - -static ma_result ma_mp3_ds_get_length(ma_data_source* pDataSource, ma_uint64* pLength) -{ - return ma_mp3_get_length_in_pcm_frames((ma_mp3*)pDataSource, pLength); -} - -static ma_data_source_vtable g_ma_mp3_ds_vtable = -{ - ma_mp3_ds_read, - ma_mp3_ds_seek, - ma_mp3_ds_get_data_format, - ma_mp3_ds_get_cursor, - ma_mp3_ds_get_length, - NULL, /* onSetLooping */ - 0 -}; - - -#if !defined(MA_NO_MP3) -static size_t ma_mp3_dr_callback__read(void* pUserData, void* pBufferOut, size_t bytesToRead) -{ - ma_mp3* pMP3 = (ma_mp3*)pUserData; - ma_result result; - size_t bytesRead; - - MA_ASSERT(pMP3 != NULL); - - result = pMP3->onRead(pMP3->pReadSeekTellUserData, pBufferOut, bytesToRead, &bytesRead); - (void)result; - - return bytesRead; -} - -static ma_bool32 ma_mp3_dr_callback__seek(void* pUserData, int offset, ma_dr_mp3_seek_origin origin) -{ - ma_mp3* pMP3 = (ma_mp3*)pUserData; - ma_result result; - ma_seek_origin maSeekOrigin; - - MA_ASSERT(pMP3 != NULL); - - maSeekOrigin = ma_seek_origin_start; - if (origin == ma_dr_mp3_seek_origin_current) { - maSeekOrigin = ma_seek_origin_current; - } - - result = pMP3->onSeek(pMP3->pReadSeekTellUserData, offset, maSeekOrigin); - if (result != MA_SUCCESS) { - return MA_FALSE; - } - - return MA_TRUE; -} -#endif - -static ma_result ma_mp3_init_internal(const ma_decoding_backend_config* pConfig, ma_mp3* pMP3) -{ - ma_result result; - ma_data_source_config dataSourceConfig; - - if (pMP3 == NULL) { - return MA_INVALID_ARGS; - } - - MA_ZERO_OBJECT(pMP3); - pMP3->format = ma_format_f32; /* f32 by default. */ - - if (pConfig != NULL && (pConfig->preferredFormat == ma_format_f32 || pConfig->preferredFormat == ma_format_s16)) { - pMP3->format = pConfig->preferredFormat; - } else { - /* Getting here means something other than f32 and s16 was specified. Just leave this unset to use the default format. */ - } - - dataSourceConfig = ma_data_source_config_init(); - dataSourceConfig.vtable = &g_ma_mp3_ds_vtable; - - result = ma_data_source_init(&dataSourceConfig, &pMP3->ds); - if (result != MA_SUCCESS) { - return result; /* Failed to initialize the base data source. */ - } - - return MA_SUCCESS; -} - -static ma_result ma_mp3_generate_seek_table(ma_mp3* pMP3, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks) -{ - ma_bool32 mp3Result; - ma_uint32 seekPointCount = 0; - ma_dr_mp3_seek_point* pSeekPoints = NULL; - - MA_ASSERT(pMP3 != NULL); - MA_ASSERT(pConfig != NULL); - - seekPointCount = pConfig->seekPointCount; - if (seekPointCount > 0) { - pSeekPoints = (ma_dr_mp3_seek_point*)ma_malloc(sizeof(*pMP3->pSeekPoints) * seekPointCount, pAllocationCallbacks); - if (pSeekPoints == NULL) { - return MA_OUT_OF_MEMORY; - } - } - - mp3Result = ma_dr_mp3_calculate_seek_points(&pMP3->dr, &seekPointCount, pSeekPoints); - if (mp3Result != MA_TRUE) { - ma_free(pSeekPoints, pAllocationCallbacks); - return MA_ERROR; - } - - mp3Result = ma_dr_mp3_bind_seek_table(&pMP3->dr, seekPointCount, pSeekPoints); - if (mp3Result != MA_TRUE) { - ma_free(pSeekPoints, pAllocationCallbacks); - return MA_ERROR; - } - - pMP3->seekPointCount = seekPointCount; - pMP3->pSeekPoints = pSeekPoints; - - return MA_SUCCESS; -} - -static ma_result ma_mp3_post_init(ma_mp3* pMP3, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks) -{ - ma_result result; - - result = ma_mp3_generate_seek_table(pMP3, pConfig, pAllocationCallbacks); - if (result != MA_SUCCESS) { - return result; - } - - return MA_SUCCESS; -} - -MA_API ma_result ma_mp3_init(ma_read_proc onRead, ma_seek_proc onSeek, ma_tell_proc onTell, void* pReadSeekTellUserData, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_mp3* pMP3) -{ - ma_result result; - - result = ma_mp3_init_internal(pConfig, pMP3); - if (result != MA_SUCCESS) { - return result; - } - - if (onRead == NULL || onSeek == NULL) { - return MA_INVALID_ARGS; /* onRead and onSeek are mandatory. */ - } - - pMP3->onRead = onRead; - pMP3->onSeek = onSeek; - pMP3->onTell = onTell; - pMP3->pReadSeekTellUserData = pReadSeekTellUserData; - - #if !defined(MA_NO_MP3) - { - ma_bool32 mp3Result; - - mp3Result = ma_dr_mp3_init(&pMP3->dr, ma_mp3_dr_callback__read, ma_mp3_dr_callback__seek, pMP3, pAllocationCallbacks); - if (mp3Result != MA_TRUE) { - return MA_INVALID_FILE; - } - - ma_mp3_post_init(pMP3, pConfig, pAllocationCallbacks); - - return MA_SUCCESS; - } - #else - { - /* mp3 is disabled. */ - (void)pAllocationCallbacks; - return MA_NOT_IMPLEMENTED; - } - #endif -} - -MA_API ma_result ma_mp3_init_file(const char* pFilePath, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_mp3* pMP3) -{ - ma_result result; - - result = ma_mp3_init_internal(pConfig, pMP3); - if (result != MA_SUCCESS) { - return result; - } - - #if !defined(MA_NO_MP3) - { - ma_bool32 mp3Result; - - mp3Result = ma_dr_mp3_init_file(&pMP3->dr, pFilePath, pAllocationCallbacks); - if (mp3Result != MA_TRUE) { - return MA_INVALID_FILE; - } - - ma_mp3_post_init(pMP3, pConfig, pAllocationCallbacks); - - return MA_SUCCESS; - } - #else - { - /* mp3 is disabled. */ - (void)pFilePath; - (void)pAllocationCallbacks; - return MA_NOT_IMPLEMENTED; - } - #endif -} - -MA_API ma_result ma_mp3_init_file_w(const wchar_t* pFilePath, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_mp3* pMP3) -{ - ma_result result; - - result = ma_mp3_init_internal(pConfig, pMP3); - if (result != MA_SUCCESS) { - return result; - } - - #if !defined(MA_NO_MP3) - { - ma_bool32 mp3Result; - - mp3Result = ma_dr_mp3_init_file_w(&pMP3->dr, pFilePath, pAllocationCallbacks); - if (mp3Result != MA_TRUE) { - return MA_INVALID_FILE; - } - - ma_mp3_post_init(pMP3, pConfig, pAllocationCallbacks); - - return MA_SUCCESS; - } - #else - { - /* mp3 is disabled. */ - (void)pFilePath; - (void)pAllocationCallbacks; - return MA_NOT_IMPLEMENTED; - } - #endif -} - -MA_API ma_result ma_mp3_init_memory(const void* pData, size_t dataSize, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_mp3* pMP3) -{ - ma_result result; - - result = ma_mp3_init_internal(pConfig, pMP3); - if (result != MA_SUCCESS) { - return result; - } - - #if !defined(MA_NO_MP3) - { - ma_bool32 mp3Result; - - mp3Result = ma_dr_mp3_init_memory(&pMP3->dr, pData, dataSize, pAllocationCallbacks); - if (mp3Result != MA_TRUE) { - return MA_INVALID_FILE; - } - - ma_mp3_post_init(pMP3, pConfig, pAllocationCallbacks); - - return MA_SUCCESS; - } - #else - { - /* mp3 is disabled. */ - (void)pData; - (void)dataSize; - (void)pAllocationCallbacks; - return MA_NOT_IMPLEMENTED; - } - #endif -} - -MA_API void ma_mp3_uninit(ma_mp3* pMP3, const ma_allocation_callbacks* pAllocationCallbacks) -{ - if (pMP3 == NULL) { - return; - } - - #if !defined(MA_NO_MP3) - { - ma_dr_mp3_uninit(&pMP3->dr); - } - #else - { - /* mp3 is disabled. Should never hit this since initialization would have failed. */ - MA_ASSERT(MA_FALSE); - } - #endif - - /* Seek points need to be freed after the MP3 decoder has been uninitialized to ensure they're no longer being referenced. */ - ma_free(pMP3->pSeekPoints, pAllocationCallbacks); - - ma_data_source_uninit(&pMP3->ds); -} - -MA_API ma_result ma_mp3_read_pcm_frames(ma_mp3* pMP3, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead) -{ - if (pFramesRead != NULL) { - *pFramesRead = 0; - } - - if (frameCount == 0) { - return MA_INVALID_ARGS; - } - - if (pMP3 == NULL) { - return MA_INVALID_ARGS; - } - - #if !defined(MA_NO_MP3) - { - /* We always use floating point format. */ - ma_result result = MA_SUCCESS; /* Must be initialized to MA_SUCCESS. */ - ma_uint64 totalFramesRead = 0; - ma_format format; - - ma_mp3_get_data_format(pMP3, &format, NULL, NULL, NULL, 0); - - switch (format) - { - case ma_format_f32: - { - totalFramesRead = ma_dr_mp3_read_pcm_frames_f32(&pMP3->dr, frameCount, (float*)pFramesOut); - } break; - - case ma_format_s16: - { - totalFramesRead = ma_dr_mp3_read_pcm_frames_s16(&pMP3->dr, frameCount, (ma_int16*)pFramesOut); - } break; - - case ma_format_u8: - case ma_format_s24: - case ma_format_s32: - case ma_format_unknown: - default: - { - return MA_INVALID_OPERATION; - }; - } - - /* In the future we'll update ma_dr_mp3 to return MA_AT_END for us. */ - if (totalFramesRead == 0) { - result = MA_AT_END; - } - - if (pFramesRead != NULL) { - *pFramesRead = totalFramesRead; - } - - return result; - } - #else - { - /* mp3 is disabled. Should never hit this since initialization would have failed. */ - MA_ASSERT(MA_FALSE); - - (void)pFramesOut; - (void)frameCount; - (void)pFramesRead; - - return MA_NOT_IMPLEMENTED; - } - #endif -} - -MA_API ma_result ma_mp3_seek_to_pcm_frame(ma_mp3* pMP3, ma_uint64 frameIndex) -{ - if (pMP3 == NULL) { - return MA_INVALID_ARGS; - } - - #if !defined(MA_NO_MP3) - { - ma_bool32 mp3Result; - - mp3Result = ma_dr_mp3_seek_to_pcm_frame(&pMP3->dr, frameIndex); - if (mp3Result != MA_TRUE) { - return MA_ERROR; - } - - return MA_SUCCESS; - } - #else - { - /* mp3 is disabled. Should never hit this since initialization would have failed. */ - MA_ASSERT(MA_FALSE); - - (void)frameIndex; - - return MA_NOT_IMPLEMENTED; - } - #endif -} - -MA_API ma_result ma_mp3_get_data_format(ma_mp3* pMP3, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate, ma_channel* pChannelMap, size_t channelMapCap) -{ - /* Defaults for safety. */ - if (pFormat != NULL) { - *pFormat = ma_format_unknown; - } - if (pChannels != NULL) { - *pChannels = 0; - } - if (pSampleRate != NULL) { - *pSampleRate = 0; - } - if (pChannelMap != NULL) { - MA_ZERO_MEMORY(pChannelMap, sizeof(*pChannelMap) * channelMapCap); - } - - if (pMP3 == NULL) { - return MA_INVALID_OPERATION; - } - - if (pFormat != NULL) { - *pFormat = pMP3->format; - } - - #if !defined(MA_NO_MP3) - { - if (pChannels != NULL) { - *pChannels = pMP3->dr.channels; - } - - if (pSampleRate != NULL) { - *pSampleRate = pMP3->dr.sampleRate; - } - - if (pChannelMap != NULL) { - ma_channel_map_init_standard(ma_standard_channel_map_default, pChannelMap, channelMapCap, pMP3->dr.channels); - } - - return MA_SUCCESS; - } - #else - { - /* mp3 is disabled. Should never hit this since initialization would have failed. */ - MA_ASSERT(MA_FALSE); - return MA_NOT_IMPLEMENTED; - } - #endif -} - -MA_API ma_result ma_mp3_get_cursor_in_pcm_frames(ma_mp3* pMP3, ma_uint64* pCursor) -{ - if (pCursor == NULL) { - return MA_INVALID_ARGS; - } - - *pCursor = 0; /* Safety. */ - - if (pMP3 == NULL) { - return MA_INVALID_ARGS; - } - - #if !defined(MA_NO_MP3) - { - *pCursor = pMP3->dr.currentPCMFrame; - - return MA_SUCCESS; - } - #else - { - /* mp3 is disabled. Should never hit this since initialization would have failed. */ - MA_ASSERT(MA_FALSE); - return MA_NOT_IMPLEMENTED; - } - #endif -} - -MA_API ma_result ma_mp3_get_length_in_pcm_frames(ma_mp3* pMP3, ma_uint64* pLength) -{ - if (pLength == NULL) { - return MA_INVALID_ARGS; - } - - *pLength = 0; /* Safety. */ - - if (pMP3 == NULL) { - return MA_INVALID_ARGS; - } - - #if !defined(MA_NO_MP3) - { - *pLength = ma_dr_mp3_get_pcm_frame_count(&pMP3->dr); - - return MA_SUCCESS; - } - #else - { - /* mp3 is disabled. Should never hit this since initialization would have failed. */ - MA_ASSERT(MA_FALSE); - return MA_NOT_IMPLEMENTED; - } - #endif -} - - -static ma_result ma_decoding_backend_init__mp3(void* pUserData, ma_read_proc onRead, ma_seek_proc onSeek, ma_tell_proc onTell, void* pReadSeekTellUserData, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_data_source** ppBackend) -{ - ma_result result; - ma_mp3* pMP3; - - (void)pUserData; /* For now not using pUserData, but once we start storing the vorbis decoder state within the ma_decoder structure this will be set to the decoder so we can avoid a malloc. */ - - /* For now we're just allocating the decoder backend on the heap. */ - pMP3 = (ma_mp3*)ma_malloc(sizeof(*pMP3), pAllocationCallbacks); - if (pMP3 == NULL) { - return MA_OUT_OF_MEMORY; - } - - result = ma_mp3_init(onRead, onSeek, onTell, pReadSeekTellUserData, pConfig, pAllocationCallbacks, pMP3); - if (result != MA_SUCCESS) { - ma_free(pMP3, pAllocationCallbacks); - return result; - } - - *ppBackend = pMP3; - - return MA_SUCCESS; -} - -static ma_result ma_decoding_backend_init_file__mp3(void* pUserData, const char* pFilePath, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_data_source** ppBackend) -{ - ma_result result; - ma_mp3* pMP3; - - (void)pUserData; /* For now not using pUserData, but once we start storing the vorbis decoder state within the ma_decoder structure this will be set to the decoder so we can avoid a malloc. */ - - /* For now we're just allocating the decoder backend on the heap. */ - pMP3 = (ma_mp3*)ma_malloc(sizeof(*pMP3), pAllocationCallbacks); - if (pMP3 == NULL) { - return MA_OUT_OF_MEMORY; - } - - result = ma_mp3_init_file(pFilePath, pConfig, pAllocationCallbacks, pMP3); - if (result != MA_SUCCESS) { - ma_free(pMP3, pAllocationCallbacks); - return result; - } - - *ppBackend = pMP3; - - return MA_SUCCESS; -} - -static ma_result ma_decoding_backend_init_file_w__mp3(void* pUserData, const wchar_t* pFilePath, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_data_source** ppBackend) -{ - ma_result result; - ma_mp3* pMP3; - - (void)pUserData; /* For now not using pUserData, but once we start storing the vorbis decoder state within the ma_decoder structure this will be set to the decoder so we can avoid a malloc. */ - - /* For now we're just allocating the decoder backend on the heap. */ - pMP3 = (ma_mp3*)ma_malloc(sizeof(*pMP3), pAllocationCallbacks); - if (pMP3 == NULL) { - return MA_OUT_OF_MEMORY; - } - - result = ma_mp3_init_file_w(pFilePath, pConfig, pAllocationCallbacks, pMP3); - if (result != MA_SUCCESS) { - ma_free(pMP3, pAllocationCallbacks); - return result; - } - - *ppBackend = pMP3; - - return MA_SUCCESS; -} - -static ma_result ma_decoding_backend_init_memory__mp3(void* pUserData, const void* pData, size_t dataSize, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_data_source** ppBackend) -{ - ma_result result; - ma_mp3* pMP3; - - (void)pUserData; /* For now not using pUserData, but once we start storing the vorbis decoder state within the ma_decoder structure this will be set to the decoder so we can avoid a malloc. */ - - /* For now we're just allocating the decoder backend on the heap. */ - pMP3 = (ma_mp3*)ma_malloc(sizeof(*pMP3), pAllocationCallbacks); - if (pMP3 == NULL) { - return MA_OUT_OF_MEMORY; - } - - result = ma_mp3_init_memory(pData, dataSize, pConfig, pAllocationCallbacks, pMP3); - if (result != MA_SUCCESS) { - ma_free(pMP3, pAllocationCallbacks); - return result; - } - - *ppBackend = pMP3; - - return MA_SUCCESS; -} - -static void ma_decoding_backend_uninit__mp3(void* pUserData, ma_data_source* pBackend, const ma_allocation_callbacks* pAllocationCallbacks) -{ - ma_mp3* pMP3 = (ma_mp3*)pBackend; - - (void)pUserData; - - ma_mp3_uninit(pMP3, pAllocationCallbacks); - ma_free(pMP3, pAllocationCallbacks); -} - -static ma_decoding_backend_vtable g_ma_decoding_backend_vtable_mp3 = -{ - ma_decoding_backend_init__mp3, - ma_decoding_backend_init_file__mp3, - ma_decoding_backend_init_file_w__mp3, - ma_decoding_backend_init_memory__mp3, - ma_decoding_backend_uninit__mp3 -}; - -static ma_result ma_decoder_init_mp3__internal(const ma_decoder_config* pConfig, ma_decoder* pDecoder) -{ - return ma_decoder_init_from_vtable__internal(&g_ma_decoding_backend_vtable_mp3, NULL, pConfig, pDecoder); -} - -static ma_result ma_decoder_init_mp3_from_file__internal(const char* pFilePath, const ma_decoder_config* pConfig, ma_decoder* pDecoder) -{ - return ma_decoder_init_from_file__internal(&g_ma_decoding_backend_vtable_mp3, NULL, pFilePath, pConfig, pDecoder); -} - -static ma_result ma_decoder_init_mp3_from_file_w__internal(const wchar_t* pFilePath, const ma_decoder_config* pConfig, ma_decoder* pDecoder) -{ - return ma_decoder_init_from_file_w__internal(&g_ma_decoding_backend_vtable_mp3, NULL, pFilePath, pConfig, pDecoder); -} - -static ma_result ma_decoder_init_mp3_from_memory__internal(const void* pData, size_t dataSize, const ma_decoder_config* pConfig, ma_decoder* pDecoder) -{ - return ma_decoder_init_from_memory__internal(&g_ma_decoding_backend_vtable_mp3, NULL, pData, dataSize, pConfig, pDecoder); -} -#endif /* ma_dr_mp3_h */ - -/* Vorbis */ -#ifdef STB_VORBIS_INCLUDE_STB_VORBIS_H -#define MA_HAS_VORBIS - -/* The size in bytes of each chunk of data to read from the Vorbis stream. */ -#define MA_VORBIS_DATA_CHUNK_SIZE 4096 - -typedef struct -{ - ma_data_source_base ds; - ma_read_proc onRead; - ma_seek_proc onSeek; - ma_tell_proc onTell; - void* pReadSeekTellUserData; - ma_allocation_callbacks allocationCallbacks; /* Store the allocation callbacks within the structure because we may need to dynamically expand a buffer in ma_stbvorbis_read_pcm_frames() when using push mode. */ - ma_format format; /* Only f32 is allowed with stb_vorbis. */ - ma_uint32 channels; - ma_uint32 sampleRate; - ma_uint64 cursor; -#if !defined(MA_NO_VORBIS) - stb_vorbis* stb; - ma_bool32 usingPushMode; - struct - { - ma_uint8* pData; - size_t dataSize; - size_t dataCapacity; - size_t audioStartOffsetInBytes; - ma_uint32 framesConsumed; /* The number of frames consumed in ppPacketData. */ - ma_uint32 framesRemaining; /* The number of frames remaining in ppPacketData. */ - float** ppPacketData; - } push; -#endif -} ma_stbvorbis; - -MA_API ma_result ma_stbvorbis_init(ma_read_proc onRead, ma_seek_proc onSeek, ma_tell_proc onTell, void* pReadSeekTellUserData, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_stbvorbis* pVorbis); -MA_API ma_result ma_stbvorbis_init_file(const char* pFilePath, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_stbvorbis* pVorbis); -MA_API ma_result ma_stbvorbis_init_memory(const void* pData, size_t dataSize, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_stbvorbis* pVorbis); -MA_API void ma_stbvorbis_uninit(ma_stbvorbis* pVorbis, const ma_allocation_callbacks* pAllocationCallbacks); -MA_API ma_result ma_stbvorbis_read_pcm_frames(ma_stbvorbis* pVorbis, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead); -MA_API ma_result ma_stbvorbis_seek_to_pcm_frame(ma_stbvorbis* pVorbis, ma_uint64 frameIndex); -MA_API ma_result ma_stbvorbis_get_data_format(ma_stbvorbis* pVorbis, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate, ma_channel* pChannelMap, size_t channelMapCap); -MA_API ma_result ma_stbvorbis_get_cursor_in_pcm_frames(ma_stbvorbis* pVorbis, ma_uint64* pCursor); -MA_API ma_result ma_stbvorbis_get_length_in_pcm_frames(ma_stbvorbis* pVorbis, ma_uint64* pLength); - - -static ma_result ma_stbvorbis_ds_read(ma_data_source* pDataSource, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead) -{ - return ma_stbvorbis_read_pcm_frames((ma_stbvorbis*)pDataSource, pFramesOut, frameCount, pFramesRead); -} - -static ma_result ma_stbvorbis_ds_seek(ma_data_source* pDataSource, ma_uint64 frameIndex) -{ - return ma_stbvorbis_seek_to_pcm_frame((ma_stbvorbis*)pDataSource, frameIndex); -} - -static ma_result ma_stbvorbis_ds_get_data_format(ma_data_source* pDataSource, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate, ma_channel* pChannelMap, size_t channelMapCap) -{ - return ma_stbvorbis_get_data_format((ma_stbvorbis*)pDataSource, pFormat, pChannels, pSampleRate, pChannelMap, channelMapCap); -} - -static ma_result ma_stbvorbis_ds_get_cursor(ma_data_source* pDataSource, ma_uint64* pCursor) -{ - return ma_stbvorbis_get_cursor_in_pcm_frames((ma_stbvorbis*)pDataSource, pCursor); -} - -static ma_result ma_stbvorbis_ds_get_length(ma_data_source* pDataSource, ma_uint64* pLength) -{ - return ma_stbvorbis_get_length_in_pcm_frames((ma_stbvorbis*)pDataSource, pLength); -} - -static ma_data_source_vtable g_ma_stbvorbis_ds_vtable = -{ - ma_stbvorbis_ds_read, - ma_stbvorbis_ds_seek, - ma_stbvorbis_ds_get_data_format, - ma_stbvorbis_ds_get_cursor, - ma_stbvorbis_ds_get_length, - NULL, /* onSetLooping */ - 0 -}; - - -static ma_result ma_stbvorbis_init_internal(const ma_decoding_backend_config* pConfig, ma_stbvorbis* pVorbis) -{ - ma_result result; - ma_data_source_config dataSourceConfig; - - (void)pConfig; - - if (pVorbis == NULL) { - return MA_INVALID_ARGS; - } - - MA_ZERO_OBJECT(pVorbis); - pVorbis->format = ma_format_f32; /* Only supporting f32. */ - - dataSourceConfig = ma_data_source_config_init(); - dataSourceConfig.vtable = &g_ma_stbvorbis_ds_vtable; - - result = ma_data_source_init(&dataSourceConfig, &pVorbis->ds); - if (result != MA_SUCCESS) { - return result; /* Failed to initialize the base data source. */ - } - - return MA_SUCCESS; -} - -#if !defined(MA_NO_VORBIS) -static ma_result ma_stbvorbis_post_init(ma_stbvorbis* pVorbis) -{ - stb_vorbis_info info; - - MA_ASSERT(pVorbis != NULL); - - info = stb_vorbis_get_info(pVorbis->stb); - - pVorbis->channels = info.channels; - pVorbis->sampleRate = info.sample_rate; - - return MA_SUCCESS; -} - -static ma_result ma_stbvorbis_init_internal_decoder_push(ma_stbvorbis* pVorbis) -{ - ma_result result; - stb_vorbis* stb; - size_t dataSize = 0; - size_t dataCapacity = 0; - ma_uint8* pData = NULL; /* <-- Must be initialized to NULL. */ - - for (;;) { - int vorbisError; - int consumedDataSize; /* <-- Fill by stb_vorbis_open_pushdata(). */ - size_t bytesRead; - ma_uint8* pNewData; - - /* Allocate memory for the new chunk. */ - dataCapacity += MA_VORBIS_DATA_CHUNK_SIZE; - pNewData = (ma_uint8*)ma_realloc(pData, dataCapacity, &pVorbis->allocationCallbacks); - if (pNewData == NULL) { - ma_free(pData, &pVorbis->allocationCallbacks); - return MA_OUT_OF_MEMORY; - } - - pData = pNewData; - - /* Read in the next chunk. */ - result = pVorbis->onRead(pVorbis->pReadSeekTellUserData, ma_offset_ptr(pData, dataSize), (dataCapacity - dataSize), &bytesRead); - dataSize += bytesRead; - - if (result != MA_SUCCESS) { - ma_free(pData, &pVorbis->allocationCallbacks); - return result; - } - - /* We have a maximum of 31 bits with stb_vorbis. */ - if (dataSize > INT_MAX) { - ma_free(pData, &pVorbis->allocationCallbacks); - return MA_TOO_BIG; - } - - stb = stb_vorbis_open_pushdata(pData, (int)dataSize, &consumedDataSize, &vorbisError, NULL); - if (stb != NULL) { - /* - Successfully opened the Vorbis decoder. We might have some leftover unprocessed - data so we'll need to move that down to the front. - */ - dataSize -= (size_t)consumedDataSize; /* Consume the data. */ - MA_MOVE_MEMORY(pData, ma_offset_ptr(pData, consumedDataSize), dataSize); - - /* - We need to track the start point so we can seek back to the start of the audio - data when seeking. - */ - pVorbis->push.audioStartOffsetInBytes = consumedDataSize; - - break; - } else { - /* Failed to open the decoder. */ - if (vorbisError == VORBIS_need_more_data) { - continue; - } else { - ma_free(pData, &pVorbis->allocationCallbacks); - return MA_ERROR; /* Failed to open the stb_vorbis decoder. */ - } - } - } - - MA_ASSERT(stb != NULL); - pVorbis->stb = stb; - pVorbis->push.pData = pData; - pVorbis->push.dataSize = dataSize; - pVorbis->push.dataCapacity = dataCapacity; - - return MA_SUCCESS; -} -#endif - -MA_API ma_result ma_stbvorbis_init(ma_read_proc onRead, ma_seek_proc onSeek, ma_tell_proc onTell, void* pReadSeekTellUserData, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_stbvorbis* pVorbis) -{ - ma_result result; - - result = ma_stbvorbis_init_internal(pConfig, pVorbis); - if (result != MA_SUCCESS) { - return result; - } - - if (onRead == NULL || onSeek == NULL) { - return MA_INVALID_ARGS; /* onRead and onSeek are mandatory. */ - } - - pVorbis->onRead = onRead; - pVorbis->onSeek = onSeek; - pVorbis->onTell = onTell; - pVorbis->pReadSeekTellUserData = pReadSeekTellUserData; - ma_allocation_callbacks_init_copy(&pVorbis->allocationCallbacks, pAllocationCallbacks); - - #if !defined(MA_NO_VORBIS) - { - /* - stb_vorbis lacks a callback based API for its pulling API which means we're stuck with the - pushing API. In order for us to be able to successfully initialize the decoder we need to - supply it with enough data. We need to keep loading data until we have enough. - */ - result = ma_stbvorbis_init_internal_decoder_push(pVorbis); - if (result != MA_SUCCESS) { - return result; - } - - pVorbis->usingPushMode = MA_TRUE; - - result = ma_stbvorbis_post_init(pVorbis); - if (result != MA_SUCCESS) { - stb_vorbis_close(pVorbis->stb); - ma_free(pVorbis->push.pData, pAllocationCallbacks); - return result; - } - - return MA_SUCCESS; - } - #else - { - /* vorbis is disabled. */ - (void)pAllocationCallbacks; - return MA_NOT_IMPLEMENTED; - } - #endif -} - -MA_API ma_result ma_stbvorbis_init_file(const char* pFilePath, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_stbvorbis* pVorbis) -{ - ma_result result; - - result = ma_stbvorbis_init_internal(pConfig, pVorbis); - if (result != MA_SUCCESS) { - return result; - } - - #if !defined(MA_NO_VORBIS) - { - (void)pAllocationCallbacks; /* Don't know how to make use of this with stb_vorbis. */ - - /* We can use stb_vorbis' pull mode for file based streams. */ - pVorbis->stb = stb_vorbis_open_filename(pFilePath, NULL, NULL); - if (pVorbis->stb == NULL) { - return MA_INVALID_FILE; - } - - pVorbis->usingPushMode = MA_FALSE; - - result = ma_stbvorbis_post_init(pVorbis); - if (result != MA_SUCCESS) { - stb_vorbis_close(pVorbis->stb); - return result; - } - - return MA_SUCCESS; - } - #else - { - /* vorbis is disabled. */ - (void)pFilePath; - (void)pAllocationCallbacks; - return MA_NOT_IMPLEMENTED; - } - #endif -} - -MA_API ma_result ma_stbvorbis_init_memory(const void* pData, size_t dataSize, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_stbvorbis* pVorbis) -{ - ma_result result; - - result = ma_stbvorbis_init_internal(pConfig, pVorbis); - if (result != MA_SUCCESS) { - return result; - } - - #if !defined(MA_NO_VORBIS) - { - (void)pAllocationCallbacks; - - /* stb_vorbis uses an int as its size specifier, restricting it to 32-bit even on 64-bit systems. *sigh*. */ - if (dataSize > INT_MAX) { - return MA_TOO_BIG; - } - - pVorbis->stb = stb_vorbis_open_memory((const unsigned char*)pData, (int)dataSize, NULL, NULL); - if (pVorbis->stb == NULL) { - return MA_INVALID_FILE; - } - - pVorbis->usingPushMode = MA_FALSE; - - result = ma_stbvorbis_post_init(pVorbis); - if (result != MA_SUCCESS) { - stb_vorbis_close(pVorbis->stb); - return result; - } - - return MA_SUCCESS; - } - #else - { - /* vorbis is disabled. */ - (void)pData; - (void)dataSize; - (void)pAllocationCallbacks; - return MA_NOT_IMPLEMENTED; - } - #endif -} - -MA_API void ma_stbvorbis_uninit(ma_stbvorbis* pVorbis, const ma_allocation_callbacks* pAllocationCallbacks) -{ - if (pVorbis == NULL) { - return; - } - - #if !defined(MA_NO_VORBIS) - { - stb_vorbis_close(pVorbis->stb); - - /* We'll have to clear some memory if we're using push mode. */ - if (pVorbis->usingPushMode) { - ma_free(pVorbis->push.pData, pAllocationCallbacks); - } - } - #else - { - /* vorbis is disabled. Should never hit this since initialization would have failed. */ - MA_ASSERT(MA_FALSE); - } - #endif - - ma_data_source_uninit(&pVorbis->ds); -} - -MA_API ma_result ma_stbvorbis_read_pcm_frames(ma_stbvorbis* pVorbis, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead) -{ - if (pFramesRead != NULL) { - *pFramesRead = 0; - } - - if (frameCount == 0) { - return MA_INVALID_ARGS; - } - - if (pVorbis == NULL) { - return MA_INVALID_ARGS; - } - - #if !defined(MA_NO_VORBIS) - { - /* We always use floating point format. */ - ma_result result = MA_SUCCESS; /* Must be initialized to MA_SUCCESS. */ - ma_uint64 totalFramesRead = 0; - ma_format format; - ma_uint32 channels; - - ma_stbvorbis_get_data_format(pVorbis, &format, &channels, NULL, NULL, 0); - - if (format == ma_format_f32) { - /* We read differently depending on whether or not we're using push mode. */ - if (pVorbis->usingPushMode) { - /* Push mode. This is the complex case. */ - float* pFramesOutF32 = (float*)pFramesOut; - - while (totalFramesRead < frameCount) { - /* The first thing to do is read from any already-cached frames. */ - ma_uint32 framesToReadFromCache = (ma_uint32)ma_min(pVorbis->push.framesRemaining, (frameCount - totalFramesRead)); /* Safe cast because pVorbis->framesRemaining is 32-bit. */ - - /* The output pointer can be null in which case we just treat it as a seek. */ - if (pFramesOut != NULL) { - ma_uint64 iFrame; - for (iFrame = 0; iFrame < framesToReadFromCache; iFrame += 1) { - ma_uint32 iChannel; - for (iChannel = 0; iChannel < pVorbis->channels; iChannel += 1) { - pFramesOutF32[iChannel] = pVorbis->push.ppPacketData[iChannel][pVorbis->push.framesConsumed + iFrame]; - } - - pFramesOutF32 += pVorbis->channels; - } - } - - /* Update pointers and counters. */ - pVorbis->push.framesConsumed += framesToReadFromCache; - pVorbis->push.framesRemaining -= framesToReadFromCache; - totalFramesRead += framesToReadFromCache; - - /* Don't bother reading any more frames right now if we've just finished loading. */ - if (totalFramesRead == frameCount) { - break; - } - - MA_ASSERT(pVorbis->push.framesRemaining == 0); - - /* Getting here means we've run out of cached frames. We'll need to load some more. */ - for (;;) { - int samplesRead = 0; - int consumedDataSize; - - /* We need to case dataSize to an int, so make sure we can do it safely. */ - if (pVorbis->push.dataSize > INT_MAX) { - break; /* Too big. */ - } - - consumedDataSize = stb_vorbis_decode_frame_pushdata(pVorbis->stb, pVorbis->push.pData, (int)pVorbis->push.dataSize, NULL, &pVorbis->push.ppPacketData, &samplesRead); - if (consumedDataSize != 0) { - /* Successfully decoded a Vorbis frame. Consume the data. */ - pVorbis->push.dataSize -= (size_t)consumedDataSize; - MA_MOVE_MEMORY(pVorbis->push.pData, ma_offset_ptr(pVorbis->push.pData, consumedDataSize), pVorbis->push.dataSize); - - pVorbis->push.framesConsumed = 0; - pVorbis->push.framesRemaining = samplesRead; - - break; - } else { - /* Not enough data. Read more. */ - size_t bytesRead; - - /* Expand the data buffer if necessary. */ - if (pVorbis->push.dataCapacity == pVorbis->push.dataSize) { - size_t newCap = pVorbis->push.dataCapacity + MA_VORBIS_DATA_CHUNK_SIZE; - ma_uint8* pNewData; - - pNewData = (ma_uint8*)ma_realloc(pVorbis->push.pData, newCap, &pVorbis->allocationCallbacks); - if (pNewData == NULL) { - result = MA_OUT_OF_MEMORY; - break; - } - - pVorbis->push.pData = pNewData; - pVorbis->push.dataCapacity = newCap; - } - - /* We should have enough room to load some data. */ - result = pVorbis->onRead(pVorbis->pReadSeekTellUserData, ma_offset_ptr(pVorbis->push.pData, pVorbis->push.dataSize), (pVorbis->push.dataCapacity - pVorbis->push.dataSize), &bytesRead); - pVorbis->push.dataSize += bytesRead; - - if (result != MA_SUCCESS) { - break; /* Failed to read any data. Get out. */ - } - } - } - - /* If we don't have a success code at this point it means we've encountered an error or the end of the file has been reached (probably the latter). */ - if (result != MA_SUCCESS) { - break; - } - } - } else { - /* Pull mode. This is the simple case, but we still need to run in a loop because stb_vorbis loves using 32-bit instead of 64-bit. */ - while (totalFramesRead < frameCount) { - ma_uint64 framesRemaining = (frameCount - totalFramesRead); - int framesRead; - - if (framesRemaining > INT_MAX) { - framesRemaining = INT_MAX; - } - - framesRead = stb_vorbis_get_samples_float_interleaved(pVorbis->stb, channels, (float*)ma_offset_pcm_frames_ptr(pFramesOut, totalFramesRead, format, channels), (int)framesRemaining * channels); /* Safe cast. */ - totalFramesRead += framesRead; - - if (framesRead < (int)framesRemaining) { - break; /* Nothing left to read. Get out. */ - } - } - } - } else { - result = MA_INVALID_ARGS; - } - - pVorbis->cursor += totalFramesRead; - - if (totalFramesRead == 0) { - result = MA_AT_END; - } - - if (pFramesRead != NULL) { - *pFramesRead = totalFramesRead; - } - - if (result == MA_SUCCESS && totalFramesRead == 0) { - result = MA_AT_END; - } - - return result; - } - #else - { - /* vorbis is disabled. Should never hit this since initialization would have failed. */ - MA_ASSERT(MA_FALSE); - - (void)pFramesOut; - (void)frameCount; - (void)pFramesRead; - - return MA_NOT_IMPLEMENTED; - } - #endif -} - -MA_API ma_result ma_stbvorbis_seek_to_pcm_frame(ma_stbvorbis* pVorbis, ma_uint64 frameIndex) -{ - if (pVorbis == NULL) { - return MA_INVALID_ARGS; - } - - #if !defined(MA_NO_VORBIS) - { - /* Different seeking methods depending on whether or not we're using push mode. */ - if (pVorbis->usingPushMode) { - /* Push mode. This is the complex case. */ - ma_result result; - float buffer[4096]; - - /* If we're seeking backwards, we need to seek back to the start and then brute-force forward. */ - if (frameIndex < pVorbis->cursor) { - if (frameIndex > 0x7FFFFFFF) { - return MA_INVALID_ARGS; /* Trying to seek beyond the 32-bit maximum of stb_vorbis. */ - } - - /* - This is wildly inefficient due to me having trouble getting sample exact seeking working - robustly with stb_vorbis_flush_pushdata(). The only way I can think to make this work - perfectly is to reinitialize the decoder. Note that we only enter this path when seeking - backwards. This will hopefully be removed once we get our own Vorbis decoder implemented. - */ - stb_vorbis_close(pVorbis->stb); - ma_free(pVorbis->push.pData, &pVorbis->allocationCallbacks); - - MA_ZERO_OBJECT(&pVorbis->push); - - /* Seek to the start of the file. */ - result = pVorbis->onSeek(pVorbis->pReadSeekTellUserData, 0, ma_seek_origin_start); - if (result != MA_SUCCESS) { - return result; - } - - result = ma_stbvorbis_init_internal_decoder_push(pVorbis); - if (result != MA_SUCCESS) { - return result; - } - - /* At this point we should be sitting on the first frame. */ - pVorbis->cursor = 0; - } - - /* We're just brute-forcing this for now. */ - while (pVorbis->cursor < frameIndex) { - ma_uint64 framesRead; - ma_uint64 framesToRead = ma_countof(buffer)/pVorbis->channels; - if (framesToRead > (frameIndex - pVorbis->cursor)) { - framesToRead = (frameIndex - pVorbis->cursor); - } - - result = ma_stbvorbis_read_pcm_frames(pVorbis, buffer, framesToRead, &framesRead); - if (result != MA_SUCCESS) { - return result; - } - } - } else { - /* Pull mode. This is the simple case. */ - int vorbisResult; - - if (frameIndex > UINT_MAX) { - return MA_INVALID_ARGS; /* Trying to seek beyond the 32-bit maximum of stb_vorbis. */ - } - - vorbisResult = stb_vorbis_seek(pVorbis->stb, (unsigned int)frameIndex); /* Safe cast. */ - if (vorbisResult == 0) { - return MA_ERROR; /* See failed. */ - } - - pVorbis->cursor = frameIndex; - } - - return MA_SUCCESS; - } - #else - { - /* vorbis is disabled. Should never hit this since initialization would have failed. */ - MA_ASSERT(MA_FALSE); - - (void)frameIndex; - - return MA_NOT_IMPLEMENTED; - } - #endif -} - -MA_API ma_result ma_stbvorbis_get_data_format(ma_stbvorbis* pVorbis, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate, ma_channel* pChannelMap, size_t channelMapCap) -{ - /* Defaults for safety. */ - if (pFormat != NULL) { - *pFormat = ma_format_unknown; - } - if (pChannels != NULL) { - *pChannels = 0; - } - if (pSampleRate != NULL) { - *pSampleRate = 0; - } - if (pChannelMap != NULL) { - MA_ZERO_MEMORY(pChannelMap, sizeof(*pChannelMap) * channelMapCap); - } - - if (pVorbis == NULL) { - return MA_INVALID_OPERATION; - } - - if (pFormat != NULL) { - *pFormat = pVorbis->format; - } - - #if !defined(MA_NO_VORBIS) - { - if (pChannels != NULL) { - *pChannels = pVorbis->channels; - } - - if (pSampleRate != NULL) { - *pSampleRate = pVorbis->sampleRate; - } - - if (pChannelMap != NULL) { - ma_channel_map_init_standard(ma_standard_channel_map_vorbis, pChannelMap, channelMapCap, pVorbis->channels); - } - - return MA_SUCCESS; - } - #else - { - /* vorbis is disabled. Should never hit this since initialization would have failed. */ - MA_ASSERT(MA_FALSE); - return MA_NOT_IMPLEMENTED; - } - #endif -} - -MA_API ma_result ma_stbvorbis_get_cursor_in_pcm_frames(ma_stbvorbis* pVorbis, ma_uint64* pCursor) -{ - if (pCursor == NULL) { - return MA_INVALID_ARGS; - } - - *pCursor = 0; /* Safety. */ - - if (pVorbis == NULL) { - return MA_INVALID_ARGS; - } - - #if !defined(MA_NO_VORBIS) - { - *pCursor = pVorbis->cursor; - - return MA_SUCCESS; - } - #else - { - /* vorbis is disabled. Should never hit this since initialization would have failed. */ - MA_ASSERT(MA_FALSE); - return MA_NOT_IMPLEMENTED; - } - #endif -} - -MA_API ma_result ma_stbvorbis_get_length_in_pcm_frames(ma_stbvorbis* pVorbis, ma_uint64* pLength) -{ - if (pLength == NULL) { - return MA_INVALID_ARGS; - } - - *pLength = 0; /* Safety. */ - - if (pVorbis == NULL) { - return MA_INVALID_ARGS; - } - - #if !defined(MA_NO_VORBIS) - { - if (pVorbis->usingPushMode) { - *pLength = 0; /* I don't know of a good way to determine this reliably with stb_vorbis and push mode. */ - } else { - *pLength = stb_vorbis_stream_length_in_samples(pVorbis->stb); - } - - return MA_SUCCESS; - } - #else - { - /* vorbis is disabled. Should never hit this since initialization would have failed. */ - MA_ASSERT(MA_FALSE); - return MA_NOT_IMPLEMENTED; - } - #endif -} - - -static ma_result ma_decoding_backend_init__stbvorbis(void* pUserData, ma_read_proc onRead, ma_seek_proc onSeek, ma_tell_proc onTell, void* pReadSeekTellUserData, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_data_source** ppBackend) -{ - ma_result result; - ma_stbvorbis* pVorbis; - - (void)pUserData; /* For now not using pUserData, but once we start storing the vorbis decoder state within the ma_decoder structure this will be set to the decoder so we can avoid a malloc. */ - - /* For now we're just allocating the decoder backend on the heap. */ - pVorbis = (ma_stbvorbis*)ma_malloc(sizeof(*pVorbis), pAllocationCallbacks); - if (pVorbis == NULL) { - return MA_OUT_OF_MEMORY; - } - - result = ma_stbvorbis_init(onRead, onSeek, onTell, pReadSeekTellUserData, pConfig, pAllocationCallbacks, pVorbis); - if (result != MA_SUCCESS) { - ma_free(pVorbis, pAllocationCallbacks); - return result; - } - - *ppBackend = pVorbis; - - return MA_SUCCESS; -} - -static ma_result ma_decoding_backend_init_file__stbvorbis(void* pUserData, const char* pFilePath, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_data_source** ppBackend) -{ - ma_result result; - ma_stbvorbis* pVorbis; - - (void)pUserData; /* For now not using pUserData, but once we start storing the vorbis decoder state within the ma_decoder structure this will be set to the decoder so we can avoid a malloc. */ - - /* For now we're just allocating the decoder backend on the heap. */ - pVorbis = (ma_stbvorbis*)ma_malloc(sizeof(*pVorbis), pAllocationCallbacks); - if (pVorbis == NULL) { - return MA_OUT_OF_MEMORY; - } - - result = ma_stbvorbis_init_file(pFilePath, pConfig, pAllocationCallbacks, pVorbis); - if (result != MA_SUCCESS) { - ma_free(pVorbis, pAllocationCallbacks); - return result; - } - - *ppBackend = pVorbis; - - return MA_SUCCESS; -} - -static ma_result ma_decoding_backend_init_memory__stbvorbis(void* pUserData, const void* pData, size_t dataSize, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_data_source** ppBackend) -{ - ma_result result; - ma_stbvorbis* pVorbis; - - (void)pUserData; /* For now not using pUserData, but once we start storing the vorbis decoder state within the ma_decoder structure this will be set to the decoder so we can avoid a malloc. */ - - /* For now we're just allocating the decoder backend on the heap. */ - pVorbis = (ma_stbvorbis*)ma_malloc(sizeof(*pVorbis), pAllocationCallbacks); - if (pVorbis == NULL) { - return MA_OUT_OF_MEMORY; - } - - result = ma_stbvorbis_init_memory(pData, dataSize, pConfig, pAllocationCallbacks, pVorbis); - if (result != MA_SUCCESS) { - ma_free(pVorbis, pAllocationCallbacks); - return result; - } - - *ppBackend = pVorbis; - - return MA_SUCCESS; -} - -static void ma_decoding_backend_uninit__stbvorbis(void* pUserData, ma_data_source* pBackend, const ma_allocation_callbacks* pAllocationCallbacks) -{ - ma_stbvorbis* pVorbis = (ma_stbvorbis*)pBackend; - - (void)pUserData; - - ma_stbvorbis_uninit(pVorbis, pAllocationCallbacks); - ma_free(pVorbis, pAllocationCallbacks); -} - -static ma_decoding_backend_vtable g_ma_decoding_backend_vtable_stbvorbis = -{ - ma_decoding_backend_init__stbvorbis, - ma_decoding_backend_init_file__stbvorbis, - NULL, /* onInitFileW() */ - ma_decoding_backend_init_memory__stbvorbis, - ma_decoding_backend_uninit__stbvorbis -}; - -static ma_result ma_decoder_init_vorbis__internal(const ma_decoder_config* pConfig, ma_decoder* pDecoder) -{ - return ma_decoder_init_from_vtable__internal(&g_ma_decoding_backend_vtable_stbvorbis, NULL, pConfig, pDecoder); -} - -static ma_result ma_decoder_init_vorbis_from_file__internal(const char* pFilePath, const ma_decoder_config* pConfig, ma_decoder* pDecoder) -{ - return ma_decoder_init_from_file__internal(&g_ma_decoding_backend_vtable_stbvorbis, NULL, pFilePath, pConfig, pDecoder); -} - -static ma_result ma_decoder_init_vorbis_from_file_w__internal(const wchar_t* pFilePath, const ma_decoder_config* pConfig, ma_decoder* pDecoder) -{ - return ma_decoder_init_from_file_w__internal(&g_ma_decoding_backend_vtable_stbvorbis, NULL, pFilePath, pConfig, pDecoder); -} - -static ma_result ma_decoder_init_vorbis_from_memory__internal(const void* pData, size_t dataSize, const ma_decoder_config* pConfig, ma_decoder* pDecoder) -{ - return ma_decoder_init_from_memory__internal(&g_ma_decoding_backend_vtable_stbvorbis, NULL, pData, dataSize, pConfig, pDecoder); -} -#endif /* STB_VORBIS_INCLUDE_STB_VORBIS_H */ - - - -static ma_result ma_decoder__init_allocation_callbacks(const ma_decoder_config* pConfig, ma_decoder* pDecoder) -{ - MA_ASSERT(pDecoder != NULL); - - if (pConfig != NULL) { - return ma_allocation_callbacks_init_copy(&pDecoder->allocationCallbacks, &pConfig->allocationCallbacks); - } else { - pDecoder->allocationCallbacks = ma_allocation_callbacks_init_default(); - return MA_SUCCESS; - } -} - -static ma_result ma_decoder__data_source_on_read(ma_data_source* pDataSource, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead) -{ - return ma_decoder_read_pcm_frames((ma_decoder*)pDataSource, pFramesOut, frameCount, pFramesRead); -} - -static ma_result ma_decoder__data_source_on_seek(ma_data_source* pDataSource, ma_uint64 frameIndex) -{ - return ma_decoder_seek_to_pcm_frame((ma_decoder*)pDataSource, frameIndex); -} - -static ma_result ma_decoder__data_source_on_get_data_format(ma_data_source* pDataSource, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate, ma_channel* pChannelMap, size_t channelMapCap) -{ - return ma_decoder_get_data_format((ma_decoder*)pDataSource, pFormat, pChannels, pSampleRate, pChannelMap, channelMapCap); -} - -static ma_result ma_decoder__data_source_on_get_cursor(ma_data_source* pDataSource, ma_uint64* pCursor) -{ - return ma_decoder_get_cursor_in_pcm_frames((ma_decoder*)pDataSource, pCursor); -} - -static ma_result ma_decoder__data_source_on_get_length(ma_data_source* pDataSource, ma_uint64* pLength) -{ - return ma_decoder_get_length_in_pcm_frames((ma_decoder*)pDataSource, pLength); -} - -static ma_data_source_vtable g_ma_decoder_data_source_vtable = -{ - ma_decoder__data_source_on_read, - ma_decoder__data_source_on_seek, - ma_decoder__data_source_on_get_data_format, - ma_decoder__data_source_on_get_cursor, - ma_decoder__data_source_on_get_length, - NULL, /* onSetLooping */ - 0 -}; - -static ma_result ma_decoder__preinit(ma_decoder_read_proc onRead, ma_decoder_seek_proc onSeek, ma_decoder_tell_proc onTell, void* pUserData, const ma_decoder_config* pConfig, ma_decoder* pDecoder) -{ - ma_result result; - ma_data_source_config dataSourceConfig; - - MA_ASSERT(pConfig != NULL); - - if (pDecoder == NULL) { - return MA_INVALID_ARGS; - } - - MA_ZERO_OBJECT(pDecoder); - - dataSourceConfig = ma_data_source_config_init(); - dataSourceConfig.vtable = &g_ma_decoder_data_source_vtable; - - result = ma_data_source_init(&dataSourceConfig, &pDecoder->ds); - if (result != MA_SUCCESS) { - return result; - } - - pDecoder->onRead = onRead; - pDecoder->onSeek = onSeek; - pDecoder->onTell = onTell; - pDecoder->pUserData = pUserData; - - result = ma_decoder__init_allocation_callbacks(pConfig, pDecoder); - if (result != MA_SUCCESS) { - ma_data_source_uninit(&pDecoder->ds); - return result; - } - - return MA_SUCCESS; -} - -static ma_result ma_decoder__postinit(const ma_decoder_config* pConfig, ma_decoder* pDecoder) -{ - ma_result result; - - result = ma_decoder__init_data_converter(pDecoder, pConfig); - - /* If we failed post initialization we need to uninitialize the decoder before returning to prevent a memory leak. */ - if (result != MA_SUCCESS) { - ma_decoder_uninit(pDecoder); - return result; - } - - return result; -} - - -static ma_result ma_decoder_init__internal(ma_decoder_read_proc onRead, ma_decoder_seek_proc onSeek, void* pUserData, const ma_decoder_config* pConfig, ma_decoder* pDecoder) -{ - ma_result result = MA_NO_BACKEND; - - MA_ASSERT(pConfig != NULL); - MA_ASSERT(pDecoder != NULL); - - /* Silence some warnings in the case that we don't have any decoder backends enabled. */ - (void)onRead; - (void)onSeek; - (void)pUserData; - - - /* If we've specified a specific encoding type, try that first. */ - if (pConfig->encodingFormat != ma_encoding_format_unknown) { - #ifdef MA_HAS_WAV - if (pConfig->encodingFormat == ma_encoding_format_wav) { - result = ma_decoder_init_wav__internal(pConfig, pDecoder); - } - #endif - #ifdef MA_HAS_FLAC - if (pConfig->encodingFormat == ma_encoding_format_flac) { - result = ma_decoder_init_flac__internal(pConfig, pDecoder); - } - #endif - #ifdef MA_HAS_MP3 - if (pConfig->encodingFormat == ma_encoding_format_mp3) { - result = ma_decoder_init_mp3__internal(pConfig, pDecoder); - } - #endif - #ifdef MA_HAS_VORBIS - if (pConfig->encodingFormat == ma_encoding_format_vorbis) { - result = ma_decoder_init_vorbis__internal(pConfig, pDecoder); - } - #endif - - /* If we weren't able to initialize the decoder, seek back to the start to give the next attempts a clean start. */ - if (result != MA_SUCCESS) { - onSeek(pDecoder, 0, ma_seek_origin_start); - } - } - - if (result != MA_SUCCESS) { - /* Getting here means we couldn't load a specific decoding backend based on the encoding format. */ - - /* - We use trial and error to open a decoder. We prioritize custom decoders so that if they - implement the same encoding format they take priority over the built-in decoders. - */ - if (result != MA_SUCCESS) { - result = ma_decoder_init_custom__internal(pConfig, pDecoder); - if (result != MA_SUCCESS) { - onSeek(pDecoder, 0, ma_seek_origin_start); - } - } - - /* - If we get to this point and we still haven't found a decoder, and the caller has requested a - specific encoding format, there's no hope for it. Abort. - */ - if (pConfig->encodingFormat != ma_encoding_format_unknown) { - return MA_NO_BACKEND; - } - - #ifdef MA_HAS_WAV - if (result != MA_SUCCESS) { - result = ma_decoder_init_wav__internal(pConfig, pDecoder); - if (result != MA_SUCCESS) { - onSeek(pDecoder, 0, ma_seek_origin_start); - } - } - #endif - #ifdef MA_HAS_FLAC - if (result != MA_SUCCESS) { - result = ma_decoder_init_flac__internal(pConfig, pDecoder); - if (result != MA_SUCCESS) { - onSeek(pDecoder, 0, ma_seek_origin_start); - } - } - #endif - #ifdef MA_HAS_MP3 - if (result != MA_SUCCESS) { - result = ma_decoder_init_mp3__internal(pConfig, pDecoder); - if (result != MA_SUCCESS) { - onSeek(pDecoder, 0, ma_seek_origin_start); - } - } - #endif - #ifdef MA_HAS_VORBIS - if (result != MA_SUCCESS) { - result = ma_decoder_init_vorbis__internal(pConfig, pDecoder); - if (result != MA_SUCCESS) { - onSeek(pDecoder, 0, ma_seek_origin_start); - } - } - #endif - } - - if (result != MA_SUCCESS) { - return result; - } - - return ma_decoder__postinit(pConfig, pDecoder); -} - -MA_API ma_result ma_decoder_init(ma_decoder_read_proc onRead, ma_decoder_seek_proc onSeek, void* pUserData, const ma_decoder_config* pConfig, ma_decoder* pDecoder) -{ - ma_decoder_config config; - ma_result result; - - config = ma_decoder_config_init_copy(pConfig); - - result = ma_decoder__preinit(onRead, onSeek, NULL, pUserData, &config, pDecoder); - if (result != MA_SUCCESS) { - return result; - } - - return ma_decoder_init__internal(onRead, onSeek, pUserData, &config, pDecoder); -} - - -static ma_result ma_decoder__on_read_memory(ma_decoder* pDecoder, void* pBufferOut, size_t bytesToRead, size_t* pBytesRead) -{ - size_t bytesRemaining; - - MA_ASSERT(pDecoder->data.memory.dataSize >= pDecoder->data.memory.currentReadPos); - - if (pBytesRead != NULL) { - *pBytesRead = 0; - } - - bytesRemaining = pDecoder->data.memory.dataSize - pDecoder->data.memory.currentReadPos; - if (bytesToRead > bytesRemaining) { - bytesToRead = bytesRemaining; - } - - if (bytesRemaining == 0) { - return MA_AT_END; - } - - if (bytesToRead > 0) { - MA_COPY_MEMORY(pBufferOut, pDecoder->data.memory.pData + pDecoder->data.memory.currentReadPos, bytesToRead); - pDecoder->data.memory.currentReadPos += bytesToRead; - } - - if (pBytesRead != NULL) { - *pBytesRead = bytesToRead; - } - - return MA_SUCCESS; -} - -static ma_result ma_decoder__on_seek_memory(ma_decoder* pDecoder, ma_int64 byteOffset, ma_seek_origin origin) -{ - if (byteOffset > 0 && (ma_uint64)byteOffset > MA_SIZE_MAX) { - return MA_BAD_SEEK; - } - - if (origin == ma_seek_origin_current) { - if (byteOffset > 0) { - if (pDecoder->data.memory.currentReadPos + byteOffset > pDecoder->data.memory.dataSize) { - byteOffset = (ma_int64)(pDecoder->data.memory.dataSize - pDecoder->data.memory.currentReadPos); /* Trying to seek too far forward. */ - } - - pDecoder->data.memory.currentReadPos += (size_t)byteOffset; - } else { - if (pDecoder->data.memory.currentReadPos < (size_t)-byteOffset) { - byteOffset = -(ma_int64)pDecoder->data.memory.currentReadPos; /* Trying to seek too far backwards. */ - } - - pDecoder->data.memory.currentReadPos -= (size_t)-byteOffset; - } - } else { - if (origin == ma_seek_origin_end) { - if (byteOffset < 0) { - byteOffset = -byteOffset; - } - - if (byteOffset > (ma_int64)pDecoder->data.memory.dataSize) { - pDecoder->data.memory.currentReadPos = 0; /* Trying to seek too far back. */ - } else { - pDecoder->data.memory.currentReadPos = pDecoder->data.memory.dataSize - (size_t)byteOffset; - } - } else { - if ((size_t)byteOffset <= pDecoder->data.memory.dataSize) { - pDecoder->data.memory.currentReadPos = (size_t)byteOffset; - } else { - pDecoder->data.memory.currentReadPos = pDecoder->data.memory.dataSize; /* Trying to seek too far forward. */ - } - } - } - - return MA_SUCCESS; -} - -static ma_result ma_decoder__on_tell_memory(ma_decoder* pDecoder, ma_int64* pCursor) -{ - MA_ASSERT(pDecoder != NULL); - MA_ASSERT(pCursor != NULL); - - *pCursor = (ma_int64)pDecoder->data.memory.currentReadPos; - - return MA_SUCCESS; -} - -static ma_result ma_decoder__preinit_memory_wrapper(const void* pData, size_t dataSize, const ma_decoder_config* pConfig, ma_decoder* pDecoder) -{ - ma_result result = ma_decoder__preinit(ma_decoder__on_read_memory, ma_decoder__on_seek_memory, ma_decoder__on_tell_memory, NULL, pConfig, pDecoder); - if (result != MA_SUCCESS) { - return result; - } - - if (pData == NULL || dataSize == 0) { - return MA_INVALID_ARGS; - } - - pDecoder->data.memory.pData = (const ma_uint8*)pData; - pDecoder->data.memory.dataSize = dataSize; - pDecoder->data.memory.currentReadPos = 0; - - (void)pConfig; - return MA_SUCCESS; -} - -MA_API ma_result ma_decoder_init_memory(const void* pData, size_t dataSize, const ma_decoder_config* pConfig, ma_decoder* pDecoder) -{ - ma_result result; - ma_decoder_config config; - - config = ma_decoder_config_init_copy(pConfig); - - result = ma_decoder__preinit(NULL, NULL, NULL, NULL, &config, pDecoder); - if (result != MA_SUCCESS) { - return result; - } - - if (pData == NULL || dataSize == 0) { - return MA_INVALID_ARGS; - } - - /* If the backend has support for loading from a file path we'll want to use that. If that all fails we'll fall back to the VFS path. */ - result = MA_NO_BACKEND; - - if (config.encodingFormat != ma_encoding_format_unknown) { - #ifdef MA_HAS_WAV - if (config.encodingFormat == ma_encoding_format_wav) { - result = ma_decoder_init_wav_from_memory__internal(pData, dataSize, &config, pDecoder); - } - #endif - #ifdef MA_HAS_FLAC - if (config.encodingFormat == ma_encoding_format_flac) { - result = ma_decoder_init_flac_from_memory__internal(pData, dataSize, &config, pDecoder); - } - #endif - #ifdef MA_HAS_MP3 - if (config.encodingFormat == ma_encoding_format_mp3) { - result = ma_decoder_init_mp3_from_memory__internal(pData, dataSize, &config, pDecoder); - } - #endif - #ifdef MA_HAS_VORBIS - if (config.encodingFormat == ma_encoding_format_vorbis) { - result = ma_decoder_init_vorbis_from_memory__internal(pData, dataSize, &config, pDecoder); - } - #endif - } - - if (result != MA_SUCCESS) { - /* Getting here means we weren't able to initialize a decoder of a specific encoding format. */ - - /* - We use trial and error to open a decoder. We prioritize custom decoders so that if they - implement the same encoding format they take priority over the built-in decoders. - */ - result = ma_decoder_init_custom_from_memory__internal(pData, dataSize, &config, pDecoder); - - /* - If we get to this point and we still haven't found a decoder, and the caller has requested a - specific encoding format, there's no hope for it. Abort. - */ - if (result != MA_SUCCESS && config.encodingFormat != ma_encoding_format_unknown) { - return MA_NO_BACKEND; - } - - /* Use trial and error for stock decoders. */ - if (result != MA_SUCCESS) { - #ifdef MA_HAS_WAV - if (result != MA_SUCCESS) { - result = ma_decoder_init_wav_from_memory__internal(pData, dataSize, &config, pDecoder); - } - #endif - #ifdef MA_HAS_FLAC - if (result != MA_SUCCESS) { - result = ma_decoder_init_flac_from_memory__internal(pData, dataSize, &config, pDecoder); - } - #endif - #ifdef MA_HAS_MP3 - if (result != MA_SUCCESS) { - result = ma_decoder_init_mp3_from_memory__internal(pData, dataSize, &config, pDecoder); - } - #endif - #ifdef MA_HAS_VORBIS - if (result != MA_SUCCESS) { - result = ma_decoder_init_vorbis_from_memory__internal(pData, dataSize, &config, pDecoder); - } - #endif - } - } - - /* - If at this point we still haven't successfully initialized the decoder it most likely means - the backend doesn't have an implementation for loading from a file path. We'll try using - miniaudio's built-in file IO for loading file. - */ - if (result == MA_SUCCESS) { - /* Initialization was successful. Finish up. */ - result = ma_decoder__postinit(&config, pDecoder); - if (result != MA_SUCCESS) { - /* - The backend was initialized successfully, but for some reason post-initialization failed. This is most likely - due to an out of memory error. We're going to abort with an error here and not try to recover. - */ - if (pDecoder->pBackendVTable != NULL && pDecoder->pBackendVTable->onUninit != NULL) { - pDecoder->pBackendVTable->onUninit(pDecoder->pBackendUserData, &pDecoder->pBackend, &pDecoder->allocationCallbacks); - } - - return result; - } - } else { - /* Probably no implementation for loading from a block of memory. Use miniaudio's abstraction instead. */ - result = ma_decoder__preinit_memory_wrapper(pData, dataSize, &config, pDecoder); - if (result != MA_SUCCESS) { - return result; - } - - result = ma_decoder_init__internal(ma_decoder__on_read_memory, ma_decoder__on_seek_memory, NULL, &config, pDecoder); - if (result != MA_SUCCESS) { - return result; - } - } - - return MA_SUCCESS; -} - - -#if defined(MA_HAS_WAV) || \ - defined(MA_HAS_MP3) || \ - defined(MA_HAS_FLAC) || \ - defined(MA_HAS_VORBIS) -#define MA_HAS_PATH_API -#endif - -#if defined(MA_HAS_PATH_API) -static const char* ma_path_file_name(const char* path) -{ - const char* fileName; - - if (path == NULL) { - return NULL; - } - - fileName = path; - - /* We just loop through the path until we find the last slash. */ - while (path[0] != '\0') { - if (path[0] == '/' || path[0] == '\\') { - fileName = path; - } - - path += 1; - } - - /* At this point the file name is sitting on a slash, so just move forward. */ - while (fileName[0] != '\0' && (fileName[0] == '/' || fileName[0] == '\\')) { - fileName += 1; - } - - return fileName; -} - -static const wchar_t* ma_path_file_name_w(const wchar_t* path) -{ - const wchar_t* fileName; - - if (path == NULL) { - return NULL; - } - - fileName = path; - - /* We just loop through the path until we find the last slash. */ - while (path[0] != '\0') { - if (path[0] == '/' || path[0] == '\\') { - fileName = path; - } - - path += 1; - } - - /* At this point the file name is sitting on a slash, so just move forward. */ - while (fileName[0] != '\0' && (fileName[0] == '/' || fileName[0] == '\\')) { - fileName += 1; - } - - return fileName; -} - - -static const char* ma_path_extension(const char* path) -{ - const char* extension; - const char* lastOccurance; - - if (path == NULL) { - path = ""; - } - - extension = ma_path_file_name(path); - lastOccurance = NULL; - - /* Just find the last '.' and return. */ - while (extension[0] != '\0') { - if (extension[0] == '.') { - extension += 1; - lastOccurance = extension; - } - - extension += 1; - } - - return (lastOccurance != NULL) ? lastOccurance : extension; -} - -static const wchar_t* ma_path_extension_w(const wchar_t* path) -{ - const wchar_t* extension; - const wchar_t* lastOccurance; - - if (path == NULL) { - path = L""; - } - - extension = ma_path_file_name_w(path); - lastOccurance = NULL; - - /* Just find the last '.' and return. */ - while (extension[0] != '\0') { - if (extension[0] == '.') { - extension += 1; - lastOccurance = extension; - } - - extension += 1; - } - - return (lastOccurance != NULL) ? lastOccurance : extension; -} - - -static ma_bool32 ma_path_extension_equal(const char* path, const char* extension) -{ - const char* ext1; - const char* ext2; - - if (path == NULL || extension == NULL) { - return MA_FALSE; - } - - ext1 = extension; - ext2 = ma_path_extension(path); - -#if defined(_MSC_VER) || defined(__DMC__) - return _stricmp(ext1, ext2) == 0; -#else - return strcasecmp(ext1, ext2) == 0; -#endif -} - -static ma_bool32 ma_path_extension_equal_w(const wchar_t* path, const wchar_t* extension) -{ - const wchar_t* ext1; - const wchar_t* ext2; - - if (path == NULL || extension == NULL) { - return MA_FALSE; - } - - ext1 = extension; - ext2 = ma_path_extension_w(path); - -#if defined(_MSC_VER) || defined(__WATCOMC__) || defined(__DMC__) - return _wcsicmp(ext1, ext2) == 0; -#else - /* - I'm not aware of a wide character version of strcasecmp(). I'm therefore converting the extensions to multibyte strings and comparing those. This - isn't the most efficient way to do it, but it should work OK. - */ - { - char ext1MB[4096]; - char ext2MB[4096]; - const wchar_t* pext1 = ext1; - const wchar_t* pext2 = ext2; - mbstate_t mbs1; - mbstate_t mbs2; - - MA_ZERO_OBJECT(&mbs1); - MA_ZERO_OBJECT(&mbs2); - - if (wcsrtombs(ext1MB, &pext1, sizeof(ext1MB), &mbs1) == (size_t)-1) { - return MA_FALSE; - } - if (wcsrtombs(ext2MB, &pext2, sizeof(ext2MB), &mbs2) == (size_t)-1) { - return MA_FALSE; - } - - return strcasecmp(ext1MB, ext2MB) == 0; - } -#endif -} -#endif /* MA_HAS_PATH_API */ - - - -static ma_result ma_decoder__on_read_vfs(ma_decoder* pDecoder, void* pBufferOut, size_t bytesToRead, size_t* pBytesRead) -{ - MA_ASSERT(pDecoder != NULL); - MA_ASSERT(pBufferOut != NULL); - - return ma_vfs_or_default_read(pDecoder->data.vfs.pVFS, pDecoder->data.vfs.file, pBufferOut, bytesToRead, pBytesRead); -} - -static ma_result ma_decoder__on_seek_vfs(ma_decoder* pDecoder, ma_int64 offset, ma_seek_origin origin) -{ - MA_ASSERT(pDecoder != NULL); - - return ma_vfs_or_default_seek(pDecoder->data.vfs.pVFS, pDecoder->data.vfs.file, offset, origin); -} - -static ma_result ma_decoder__on_tell_vfs(ma_decoder* pDecoder, ma_int64* pCursor) -{ - MA_ASSERT(pDecoder != NULL); - - return ma_vfs_or_default_tell(pDecoder->data.vfs.pVFS, pDecoder->data.vfs.file, pCursor); -} - -static ma_result ma_decoder__preinit_vfs(ma_vfs* pVFS, const char* pFilePath, const ma_decoder_config* pConfig, ma_decoder* pDecoder) -{ - ma_result result; - ma_vfs_file file; - - result = ma_decoder__preinit(ma_decoder__on_read_vfs, ma_decoder__on_seek_vfs, ma_decoder__on_tell_vfs, NULL, pConfig, pDecoder); - if (result != MA_SUCCESS) { - return result; - } - - if (pFilePath == NULL || pFilePath[0] == '\0') { - return MA_INVALID_ARGS; - } - - result = ma_vfs_or_default_open(pVFS, pFilePath, MA_OPEN_MODE_READ, &file); - if (result != MA_SUCCESS) { - return result; - } - - pDecoder->data.vfs.pVFS = pVFS; - pDecoder->data.vfs.file = file; - - return MA_SUCCESS; -} - -MA_API ma_result ma_decoder_init_vfs(ma_vfs* pVFS, const char* pFilePath, const ma_decoder_config* pConfig, ma_decoder* pDecoder) -{ - ma_result result; - ma_decoder_config config; - - config = ma_decoder_config_init_copy(pConfig); - result = ma_decoder__preinit_vfs(pVFS, pFilePath, &config, pDecoder); - if (result != MA_SUCCESS) { - return result; - } - - result = MA_NO_BACKEND; - - if (config.encodingFormat != ma_encoding_format_unknown) { - #ifdef MA_HAS_WAV - if (config.encodingFormat == ma_encoding_format_wav) { - result = ma_decoder_init_wav__internal(&config, pDecoder); - } - #endif - #ifdef MA_HAS_FLAC - if (config.encodingFormat == ma_encoding_format_flac) { - result = ma_decoder_init_flac__internal(&config, pDecoder); - } - #endif - #ifdef MA_HAS_MP3 - if (config.encodingFormat == ma_encoding_format_mp3) { - result = ma_decoder_init_mp3__internal(&config, pDecoder); - } - #endif - #ifdef MA_HAS_VORBIS - if (config.encodingFormat == ma_encoding_format_vorbis) { - result = ma_decoder_init_vorbis__internal(&config, pDecoder); - } - #endif - - /* Make sure we seek back to the start if we didn't initialize a decoder successfully so the next attempts have a fresh start. */ - if (result != MA_SUCCESS) { - ma_decoder__on_seek_vfs(pDecoder, 0, ma_seek_origin_start); - } - } - - if (result != MA_SUCCESS) { - /* Getting here means we weren't able to initialize a decoder of a specific encoding format. */ - - /* - We use trial and error to open a decoder. We prioritize custom decoders so that if they - implement the same encoding format they take priority over the built-in decoders. - */ - if (result != MA_SUCCESS) { - result = ma_decoder_init_custom__internal(&config, pDecoder); - if (result != MA_SUCCESS) { - ma_decoder__on_seek_vfs(pDecoder, 0, ma_seek_origin_start); - } - } - - /* - If we get to this point and we still haven't found a decoder, and the caller has requested a - specific encoding format, there's no hope for it. Abort. - */ - if (config.encodingFormat != ma_encoding_format_unknown) { - return MA_NO_BACKEND; - } - - #ifdef MA_HAS_WAV - if (result != MA_SUCCESS && ma_path_extension_equal(pFilePath, "wav")) { - result = ma_decoder_init_wav__internal(&config, pDecoder); - if (result != MA_SUCCESS) { - ma_decoder__on_seek_vfs(pDecoder, 0, ma_seek_origin_start); - } - } - #endif - #ifdef MA_HAS_FLAC - if (result != MA_SUCCESS && ma_path_extension_equal(pFilePath, "flac")) { - result = ma_decoder_init_flac__internal(&config, pDecoder); - if (result != MA_SUCCESS) { - ma_decoder__on_seek_vfs(pDecoder, 0, ma_seek_origin_start); - } - } - #endif - #ifdef MA_HAS_MP3 - if (result != MA_SUCCESS && ma_path_extension_equal(pFilePath, "mp3")) { - result = ma_decoder_init_mp3__internal(&config, pDecoder); - if (result != MA_SUCCESS) { - ma_decoder__on_seek_vfs(pDecoder, 0, ma_seek_origin_start); - } - } - #endif - } - - /* If we still haven't got a result just use trial and error. Otherwise we can finish up. */ - if (result != MA_SUCCESS) { - result = ma_decoder_init__internal(ma_decoder__on_read_vfs, ma_decoder__on_seek_vfs, NULL, &config, pDecoder); - } else { - result = ma_decoder__postinit(&config, pDecoder); - } - - if (result != MA_SUCCESS) { - if (pDecoder->data.vfs.file != NULL) { /* <-- Will be reset to NULL if ma_decoder_uninit() is called in one of the steps above which allows us to avoid a double close of the file. */ - ma_vfs_or_default_close(pVFS, pDecoder->data.vfs.file); - } - - return result; - } - - return MA_SUCCESS; -} - - -static ma_result ma_decoder__preinit_vfs_w(ma_vfs* pVFS, const wchar_t* pFilePath, const ma_decoder_config* pConfig, ma_decoder* pDecoder) -{ - ma_result result; - ma_vfs_file file; - - result = ma_decoder__preinit(ma_decoder__on_read_vfs, ma_decoder__on_seek_vfs, ma_decoder__on_tell_vfs, NULL, pConfig, pDecoder); - if (result != MA_SUCCESS) { - return result; - } - - if (pFilePath == NULL || pFilePath[0] == '\0') { - return MA_INVALID_ARGS; - } - - result = ma_vfs_or_default_open_w(pVFS, pFilePath, MA_OPEN_MODE_READ, &file); - if (result != MA_SUCCESS) { - return result; - } - - pDecoder->data.vfs.pVFS = pVFS; - pDecoder->data.vfs.file = file; - - return MA_SUCCESS; -} - -MA_API ma_result ma_decoder_init_vfs_w(ma_vfs* pVFS, const wchar_t* pFilePath, const ma_decoder_config* pConfig, ma_decoder* pDecoder) -{ - ma_result result; - ma_decoder_config config; - - config = ma_decoder_config_init_copy(pConfig); - result = ma_decoder__preinit_vfs_w(pVFS, pFilePath, &config, pDecoder); - if (result != MA_SUCCESS) { - return result; - } - - result = MA_NO_BACKEND; - - if (config.encodingFormat != ma_encoding_format_unknown) { - #ifdef MA_HAS_WAV - if (config.encodingFormat == ma_encoding_format_wav) { - result = ma_decoder_init_wav__internal(&config, pDecoder); - } - #endif - #ifdef MA_HAS_FLAC - if (config.encodingFormat == ma_encoding_format_flac) { - result = ma_decoder_init_flac__internal(&config, pDecoder); - } - #endif - #ifdef MA_HAS_MP3 - if (config.encodingFormat == ma_encoding_format_mp3) { - result = ma_decoder_init_mp3__internal(&config, pDecoder); - } - #endif - #ifdef MA_HAS_VORBIS - if (config.encodingFormat == ma_encoding_format_vorbis) { - result = ma_decoder_init_vorbis__internal(&config, pDecoder); - } - #endif - - /* Make sure we seek back to the start if we didn't initialize a decoder successfully so the next attempts have a fresh start. */ - if (result != MA_SUCCESS) { - ma_decoder__on_seek_vfs(pDecoder, 0, ma_seek_origin_start); - } - } - - if (result != MA_SUCCESS) { - /* Getting here means we weren't able to initialize a decoder of a specific encoding format. */ - - /* - We use trial and error to open a decoder. We prioritize custom decoders so that if they - implement the same encoding format they take priority over the built-in decoders. - */ - if (result != MA_SUCCESS) { - result = ma_decoder_init_custom__internal(&config, pDecoder); - if (result != MA_SUCCESS) { - ma_decoder__on_seek_vfs(pDecoder, 0, ma_seek_origin_start); - } - } - - /* - If we get to this point and we still haven't found a decoder, and the caller has requested a - specific encoding format, there's no hope for it. Abort. - */ - if (config.encodingFormat != ma_encoding_format_unknown) { - return MA_NO_BACKEND; - } - - #ifdef MA_HAS_WAV - if (result != MA_SUCCESS && ma_path_extension_equal_w(pFilePath, L"wav")) { - result = ma_decoder_init_wav__internal(&config, pDecoder); - if (result != MA_SUCCESS) { - ma_decoder__on_seek_vfs(pDecoder, 0, ma_seek_origin_start); - } - } - #endif - #ifdef MA_HAS_FLAC - if (result != MA_SUCCESS && ma_path_extension_equal_w(pFilePath, L"flac")) { - result = ma_decoder_init_flac__internal(&config, pDecoder); - if (result != MA_SUCCESS) { - ma_decoder__on_seek_vfs(pDecoder, 0, ma_seek_origin_start); - } - } - #endif - #ifdef MA_HAS_MP3 - if (result != MA_SUCCESS && ma_path_extension_equal_w(pFilePath, L"mp3")) { - result = ma_decoder_init_mp3__internal(&config, pDecoder); - if (result != MA_SUCCESS) { - ma_decoder__on_seek_vfs(pDecoder, 0, ma_seek_origin_start); - } - } - #endif - } - - /* If we still haven't got a result just use trial and error. Otherwise we can finish up. */ - if (result != MA_SUCCESS) { - result = ma_decoder_init__internal(ma_decoder__on_read_vfs, ma_decoder__on_seek_vfs, NULL, &config, pDecoder); - } else { - result = ma_decoder__postinit(&config, pDecoder); - } - - if (result != MA_SUCCESS) { - ma_vfs_or_default_close(pVFS, pDecoder->data.vfs.file); - return result; - } - - return MA_SUCCESS; -} - - -static ma_result ma_decoder__preinit_file(const char* pFilePath, const ma_decoder_config* pConfig, ma_decoder* pDecoder) -{ - ma_result result; - - result = ma_decoder__preinit(NULL, NULL, NULL, NULL, pConfig, pDecoder); - if (result != MA_SUCCESS) { - return result; - } - - if (pFilePath == NULL || pFilePath[0] == '\0') { - return MA_INVALID_ARGS; - } - - return MA_SUCCESS; -} - -MA_API ma_result ma_decoder_init_file(const char* pFilePath, const ma_decoder_config* pConfig, ma_decoder* pDecoder) -{ - ma_result result; - ma_decoder_config config; - - config = ma_decoder_config_init_copy(pConfig); - result = ma_decoder__preinit_file(pFilePath, &config, pDecoder); - if (result != MA_SUCCESS) { - return result; - } - - /* If the backend has support for loading from a file path we'll want to use that. If that all fails we'll fall back to the VFS path. */ - result = MA_NO_BACKEND; - - if (config.encodingFormat != ma_encoding_format_unknown) { - #ifdef MA_HAS_WAV - if (config.encodingFormat == ma_encoding_format_wav) { - result = ma_decoder_init_wav_from_file__internal(pFilePath, &config, pDecoder); - } - #endif - #ifdef MA_HAS_FLAC - if (config.encodingFormat == ma_encoding_format_flac) { - result = ma_decoder_init_flac_from_file__internal(pFilePath, &config, pDecoder); - } - #endif - #ifdef MA_HAS_MP3 - if (config.encodingFormat == ma_encoding_format_mp3) { - result = ma_decoder_init_mp3_from_file__internal(pFilePath, &config, pDecoder); - } - #endif - #ifdef MA_HAS_VORBIS - if (config.encodingFormat == ma_encoding_format_vorbis) { - result = ma_decoder_init_vorbis_from_file__internal(pFilePath, &config, pDecoder); - } - #endif - } - - if (result != MA_SUCCESS) { - /* Getting here means we weren't able to initialize a decoder of a specific encoding format. */ - - /* - We use trial and error to open a decoder. We prioritize custom decoders so that if they - implement the same encoding format they take priority over the built-in decoders. - */ - result = ma_decoder_init_custom_from_file__internal(pFilePath, &config, pDecoder); - - /* - If we get to this point and we still haven't found a decoder, and the caller has requested a - specific encoding format, there's no hope for it. Abort. - */ - if (result != MA_SUCCESS && config.encodingFormat != ma_encoding_format_unknown) { - return MA_NO_BACKEND; - } - - /* First try loading based on the file extension so we don't waste time opening and closing files. */ - #ifdef MA_HAS_WAV - if (result != MA_SUCCESS && ma_path_extension_equal(pFilePath, "wav")) { - result = ma_decoder_init_wav_from_file__internal(pFilePath, &config, pDecoder); - } - #endif - #ifdef MA_HAS_FLAC - if (result != MA_SUCCESS && ma_path_extension_equal(pFilePath, "flac")) { - result = ma_decoder_init_flac_from_file__internal(pFilePath, &config, pDecoder); - } - #endif - #ifdef MA_HAS_MP3 - if (result != MA_SUCCESS && ma_path_extension_equal(pFilePath, "mp3")) { - result = ma_decoder_init_mp3_from_file__internal(pFilePath, &config, pDecoder); - } - #endif - #ifdef MA_HAS_VORBIS - if (result != MA_SUCCESS && ma_path_extension_equal(pFilePath, "ogg")) { - result = ma_decoder_init_vorbis_from_file__internal(pFilePath, &config, pDecoder); - } - #endif - - /* - If we still haven't got a result just use trial and error. Custom decoders have already been attempted, so here we - need only iterate over our stock decoders. - */ - if (result != MA_SUCCESS) { - #ifdef MA_HAS_WAV - if (result != MA_SUCCESS) { - result = ma_decoder_init_wav_from_file__internal(pFilePath, &config, pDecoder); - } - #endif - #ifdef MA_HAS_FLAC - if (result != MA_SUCCESS) { - result = ma_decoder_init_flac_from_file__internal(pFilePath, &config, pDecoder); - } - #endif - #ifdef MA_HAS_MP3 - if (result != MA_SUCCESS) { - result = ma_decoder_init_mp3_from_file__internal(pFilePath, &config, pDecoder); - } - #endif - #ifdef MA_HAS_VORBIS - if (result != MA_SUCCESS) { - result = ma_decoder_init_vorbis_from_file__internal(pFilePath, &config, pDecoder); - } - #endif - } - } - - /* - If at this point we still haven't successfully initialized the decoder it most likely means - the backend doesn't have an implementation for loading from a file path. We'll try using - miniaudio's built-in file IO for loading file. - */ - if (result == MA_SUCCESS) { - /* Initialization was successful. Finish up. */ - result = ma_decoder__postinit(&config, pDecoder); - if (result != MA_SUCCESS) { - /* - The backend was initialized successfully, but for some reason post-initialization failed. This is most likely - due to an out of memory error. We're going to abort with an error here and not try to recover. - */ - if (pDecoder->pBackendVTable != NULL && pDecoder->pBackendVTable->onUninit != NULL) { - pDecoder->pBackendVTable->onUninit(pDecoder->pBackendUserData, &pDecoder->pBackend, &pDecoder->allocationCallbacks); - } - - return result; - } - } else { - /* Probably no implementation for loading from a file path. Use miniaudio's file IO instead. */ - result = ma_decoder_init_vfs(NULL, pFilePath, pConfig, pDecoder); - if (result != MA_SUCCESS) { - return result; - } - } - - return MA_SUCCESS; -} - -static ma_result ma_decoder__preinit_file_w(const wchar_t* pFilePath, const ma_decoder_config* pConfig, ma_decoder* pDecoder) -{ - ma_result result; - - result = ma_decoder__preinit(NULL, NULL, NULL, NULL, pConfig, pDecoder); - if (result != MA_SUCCESS) { - return result; - } - - if (pFilePath == NULL || pFilePath[0] == '\0') { - return MA_INVALID_ARGS; - } - - return MA_SUCCESS; -} - -MA_API ma_result ma_decoder_init_file_w(const wchar_t* pFilePath, const ma_decoder_config* pConfig, ma_decoder* pDecoder) -{ - ma_result result; - ma_decoder_config config; - - config = ma_decoder_config_init_copy(pConfig); - result = ma_decoder__preinit_file_w(pFilePath, &config, pDecoder); - if (result != MA_SUCCESS) { - return result; - } - - /* If the backend has support for loading from a file path we'll want to use that. If that all fails we'll fall back to the VFS path. */ - result = MA_NO_BACKEND; - - if (config.encodingFormat != ma_encoding_format_unknown) { - #ifdef MA_HAS_WAV - if (config.encodingFormat == ma_encoding_format_wav) { - result = ma_decoder_init_wav_from_file_w__internal(pFilePath, &config, pDecoder); - } - #endif - #ifdef MA_HAS_FLAC - if (config.encodingFormat == ma_encoding_format_flac) { - result = ma_decoder_init_flac_from_file_w__internal(pFilePath, &config, pDecoder); - } - #endif - #ifdef MA_HAS_MP3 - if (config.encodingFormat == ma_encoding_format_mp3) { - result = ma_decoder_init_mp3_from_file_w__internal(pFilePath, &config, pDecoder); - } - #endif - #ifdef MA_HAS_VORBIS - if (config.encodingFormat == ma_encoding_format_vorbis) { - result = ma_decoder_init_vorbis_from_file_w__internal(pFilePath, &config, pDecoder); - } - #endif - } - - if (result != MA_SUCCESS) { - /* Getting here means we weren't able to initialize a decoder of a specific encoding format. */ - - /* - We use trial and error to open a decoder. We prioritize custom decoders so that if they - implement the same encoding format they take priority over the built-in decoders. - */ - result = ma_decoder_init_custom_from_file_w__internal(pFilePath, &config, pDecoder); - - /* - If we get to this point and we still haven't found a decoder, and the caller has requested a - specific encoding format, there's no hope for it. Abort. - */ - if (result != MA_SUCCESS && config.encodingFormat != ma_encoding_format_unknown) { - return MA_NO_BACKEND; - } - - /* First try loading based on the file extension so we don't waste time opening and closing files. */ - #ifdef MA_HAS_WAV - if (result != MA_SUCCESS && ma_path_extension_equal_w(pFilePath, L"wav")) { - result = ma_decoder_init_wav_from_file_w__internal(pFilePath, &config, pDecoder); - } - #endif - #ifdef MA_HAS_FLAC - if (result != MA_SUCCESS && ma_path_extension_equal_w(pFilePath, L"flac")) { - result = ma_decoder_init_flac_from_file_w__internal(pFilePath, &config, pDecoder); - } - #endif - #ifdef MA_HAS_MP3 - if (result != MA_SUCCESS && ma_path_extension_equal_w(pFilePath, L"mp3")) { - result = ma_decoder_init_mp3_from_file_w__internal(pFilePath, &config, pDecoder); - } - #endif - #ifdef MA_HAS_VORBIS - if (result != MA_SUCCESS && ma_path_extension_equal_w(pFilePath, L"ogg")) { - result = ma_decoder_init_vorbis_from_file_w__internal(pFilePath, &config, pDecoder); - } - #endif - - /* - If we still haven't got a result just use trial and error. Custom decoders have already been attempted, so here we - need only iterate over our stock decoders. - */ - if (result != MA_SUCCESS) { - #ifdef MA_HAS_WAV - if (result != MA_SUCCESS) { - result = ma_decoder_init_wav_from_file_w__internal(pFilePath, &config, pDecoder); - } - #endif - #ifdef MA_HAS_FLAC - if (result != MA_SUCCESS) { - result = ma_decoder_init_flac_from_file_w__internal(pFilePath, &config, pDecoder); - } - #endif - #ifdef MA_HAS_MP3 - if (result != MA_SUCCESS) { - result = ma_decoder_init_mp3_from_file_w__internal(pFilePath, &config, pDecoder); - } - #endif - #ifdef MA_HAS_VORBIS - if (result != MA_SUCCESS) { - result = ma_decoder_init_vorbis_from_file_w__internal(pFilePath, &config, pDecoder); - } - #endif - } - } - - /* - If at this point we still haven't successfully initialized the decoder it most likely means - the backend doesn't have an implementation for loading from a file path. We'll try using - miniaudio's built-in file IO for loading file. - */ - if (result == MA_SUCCESS) { - /* Initialization was successful. Finish up. */ - result = ma_decoder__postinit(&config, pDecoder); - if (result != MA_SUCCESS) { - /* - The backend was initialized successfully, but for some reason post-initialization failed. This is most likely - due to an out of memory error. We're going to abort with an error here and not try to recover. - */ - if (pDecoder->pBackendVTable != NULL && pDecoder->pBackendVTable->onUninit != NULL) { - pDecoder->pBackendVTable->onUninit(pDecoder->pBackendUserData, &pDecoder->pBackend, &pDecoder->allocationCallbacks); - } - - return result; - } - } else { - /* Probably no implementation for loading from a file path. Use miniaudio's file IO instead. */ - result = ma_decoder_init_vfs_w(NULL, pFilePath, pConfig, pDecoder); - if (result != MA_SUCCESS) { - return result; - } - } - - return MA_SUCCESS; -} - -MA_API ma_result ma_decoder_uninit(ma_decoder* pDecoder) -{ - if (pDecoder == NULL) { - return MA_INVALID_ARGS; - } - - if (pDecoder->pBackend != NULL) { - if (pDecoder->pBackendVTable != NULL && pDecoder->pBackendVTable->onUninit != NULL) { - pDecoder->pBackendVTable->onUninit(pDecoder->pBackendUserData, pDecoder->pBackend, &pDecoder->allocationCallbacks); - } - } - - if (pDecoder->onRead == ma_decoder__on_read_vfs) { - ma_vfs_or_default_close(pDecoder->data.vfs.pVFS, pDecoder->data.vfs.file); - pDecoder->data.vfs.file = NULL; - } - - ma_data_converter_uninit(&pDecoder->converter, &pDecoder->allocationCallbacks); - ma_data_source_uninit(&pDecoder->ds); - - if (pDecoder->pInputCache != NULL) { - ma_free(pDecoder->pInputCache, &pDecoder->allocationCallbacks); - } - - return MA_SUCCESS; -} - -MA_API ma_result ma_decoder_read_pcm_frames(ma_decoder* pDecoder, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead) -{ - ma_result result = MA_SUCCESS; - ma_uint64 totalFramesReadOut; - void* pRunningFramesOut; - - if (pFramesRead != NULL) { - *pFramesRead = 0; /* Safety. */ - } - - if (frameCount == 0) { - return MA_INVALID_ARGS; - } - - if (pDecoder == NULL) { - return MA_INVALID_ARGS; - } - - if (pDecoder->pBackend == NULL) { - return MA_INVALID_OPERATION; - } - - /* Fast path. */ - if (pDecoder->converter.isPassthrough) { - result = ma_data_source_read_pcm_frames(pDecoder->pBackend, pFramesOut, frameCount, &totalFramesReadOut); - } else { - /* - Getting here means we need to do data conversion. If we're seeking forward and are _not_ doing resampling we can run this in a fast path. If we're doing resampling we - need to run through each sample because we need to ensure its internal cache is updated. - */ - if (pFramesOut == NULL && pDecoder->converter.hasResampler == MA_FALSE) { - result = ma_data_source_read_pcm_frames(pDecoder->pBackend, NULL, frameCount, &totalFramesReadOut); - } else { - /* Slow path. Need to run everything through the data converter. */ - ma_format internalFormat; - ma_uint32 internalChannels; - - totalFramesReadOut = 0; - pRunningFramesOut = pFramesOut; - - result = ma_data_source_get_data_format(pDecoder->pBackend, &internalFormat, &internalChannels, NULL, NULL, 0); - if (result != MA_SUCCESS) { - return result; /* Failed to retrieve the internal format and channel count. */ - } - - /* - We run a different path depending on whether or not we are using a heap-allocated - intermediary buffer or not. If the data converter does not support the calculation of - the required number of input frames, we'll use the heap-allocated path. Otherwise we'll - use the stack-allocated path. - */ - if (pDecoder->pInputCache != NULL) { - /* We don't have a way of determining the required number of input frames, so need to persistently store input data in a cache. */ - while (totalFramesReadOut < frameCount) { - ma_uint64 framesToReadThisIterationIn; - ma_uint64 framesToReadThisIterationOut; - - /* If there's any data available in the cache, that needs to get processed first. */ - if (pDecoder->inputCacheRemaining > 0) { - framesToReadThisIterationOut = (frameCount - totalFramesReadOut); - framesToReadThisIterationIn = framesToReadThisIterationOut; - if (framesToReadThisIterationIn > pDecoder->inputCacheRemaining) { - framesToReadThisIterationIn = pDecoder->inputCacheRemaining; - } - - result = ma_data_converter_process_pcm_frames(&pDecoder->converter, ma_offset_pcm_frames_ptr(pDecoder->pInputCache, pDecoder->inputCacheConsumed, internalFormat, internalChannels), &framesToReadThisIterationIn, pRunningFramesOut, &framesToReadThisIterationOut); - if (result != MA_SUCCESS) { - break; - } - - pDecoder->inputCacheConsumed += framesToReadThisIterationIn; - pDecoder->inputCacheRemaining -= framesToReadThisIterationIn; - - totalFramesReadOut += framesToReadThisIterationOut; - - if (pRunningFramesOut != NULL) { - pRunningFramesOut = ma_offset_ptr(pRunningFramesOut, framesToReadThisIterationOut * ma_get_bytes_per_frame(pDecoder->outputFormat, pDecoder->outputChannels)); - } - - if (framesToReadThisIterationIn == 0 && framesToReadThisIterationOut == 0) { - break; /* We're done. */ - } - } - - /* Getting here means there's no data in the cache and we need to fill it up from the data source. */ - if (pDecoder->inputCacheRemaining == 0) { - pDecoder->inputCacheConsumed = 0; - - result = ma_data_source_read_pcm_frames(pDecoder->pBackend, pDecoder->pInputCache, pDecoder->inputCacheCap, &pDecoder->inputCacheRemaining); - if (result != MA_SUCCESS) { - break; - } - } - } - } else { - /* We have a way of determining the required number of input frames so just use the stack. */ - while (totalFramesReadOut < frameCount) { - ma_uint8 pIntermediaryBuffer[MA_DATA_CONVERTER_STACK_BUFFER_SIZE]; /* In internal format. */ - ma_uint64 intermediaryBufferCap = sizeof(pIntermediaryBuffer) / ma_get_bytes_per_frame(internalFormat, internalChannels); - ma_uint64 framesToReadThisIterationIn; - ma_uint64 framesReadThisIterationIn; - ma_uint64 framesToReadThisIterationOut; - ma_uint64 framesReadThisIterationOut; - ma_uint64 requiredInputFrameCount; - - framesToReadThisIterationOut = (frameCount - totalFramesReadOut); - framesToReadThisIterationIn = framesToReadThisIterationOut; - if (framesToReadThisIterationIn > intermediaryBufferCap) { - framesToReadThisIterationIn = intermediaryBufferCap; - } - - ma_data_converter_get_required_input_frame_count(&pDecoder->converter, framesToReadThisIterationOut, &requiredInputFrameCount); - if (framesToReadThisIterationIn > requiredInputFrameCount) { - framesToReadThisIterationIn = requiredInputFrameCount; - } - - if (requiredInputFrameCount > 0) { - result = ma_data_source_read_pcm_frames(pDecoder->pBackend, pIntermediaryBuffer, framesToReadThisIterationIn, &framesReadThisIterationIn); - - /* - Note here that even if we've reached the end, we don't want to abort because there might be more output frames needing to be - generated from cached input data, which might happen if resampling is being performed. - */ - if (result != MA_SUCCESS && result != MA_AT_END) { - break; - } - } else { - framesReadThisIterationIn = 0; - pIntermediaryBuffer[0] = 0; /* <-- This is just to silence a static analysis warning. */ - } - - /* - At this point we have our decoded data in input format and now we need to convert to output format. Note that even if we didn't read any - input frames, we still want to try processing frames because there may some output frames generated from cached input data. - */ - framesReadThisIterationOut = framesToReadThisIterationOut; - result = ma_data_converter_process_pcm_frames(&pDecoder->converter, pIntermediaryBuffer, &framesReadThisIterationIn, pRunningFramesOut, &framesReadThisIterationOut); - if (result != MA_SUCCESS) { - break; - } - - totalFramesReadOut += framesReadThisIterationOut; - - if (pRunningFramesOut != NULL) { - pRunningFramesOut = ma_offset_ptr(pRunningFramesOut, framesReadThisIterationOut * ma_get_bytes_per_frame(pDecoder->outputFormat, pDecoder->outputChannels)); - } - - if (framesReadThisIterationIn == 0 && framesReadThisIterationOut == 0) { - break; /* We're done. */ - } - } - } - } - } - - pDecoder->readPointerInPCMFrames += totalFramesReadOut; - - if (pFramesRead != NULL) { - *pFramesRead = totalFramesReadOut; - } - - if (result == MA_SUCCESS && totalFramesReadOut == 0) { - result = MA_AT_END; - } - - return result; -} - -MA_API ma_result ma_decoder_seek_to_pcm_frame(ma_decoder* pDecoder, ma_uint64 frameIndex) -{ - if (pDecoder == NULL) { - return MA_INVALID_ARGS; - } - - if (pDecoder->pBackend != NULL) { - ma_result result; - ma_uint64 internalFrameIndex; - ma_uint32 internalSampleRate; - ma_uint64 currentFrameIndex; - - result = ma_data_source_get_data_format(pDecoder->pBackend, NULL, NULL, &internalSampleRate, NULL, 0); - if (result != MA_SUCCESS) { - return result; /* Failed to retrieve the internal sample rate. */ - } - - if (internalSampleRate == pDecoder->outputSampleRate) { - internalFrameIndex = frameIndex; - } else { - internalFrameIndex = ma_calculate_frame_count_after_resampling(internalSampleRate, pDecoder->outputSampleRate, frameIndex); - } - - /* Only seek if we're requesting a different frame to what we're currently sitting on. */ - ma_data_source_get_cursor_in_pcm_frames(pDecoder->pBackend, ¤tFrameIndex); - if (currentFrameIndex != internalFrameIndex) { - result = ma_data_source_seek_to_pcm_frame(pDecoder->pBackend, internalFrameIndex); - if (result == MA_SUCCESS) { - pDecoder->readPointerInPCMFrames = frameIndex; - } - - /* Reset the data converter so that any cached data in the resampler is cleared. */ - ma_data_converter_reset(&pDecoder->converter); - } - - return result; - } - - /* Should never get here, but if we do it means onSeekToPCMFrame was not set by the backend. */ - return MA_INVALID_ARGS; -} - -MA_API ma_result ma_decoder_get_data_format(ma_decoder* pDecoder, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate, ma_channel* pChannelMap, size_t channelMapCap) -{ - if (pDecoder == NULL) { - return MA_INVALID_ARGS; - } - - if (pFormat != NULL) { - *pFormat = pDecoder->outputFormat; - } - - if (pChannels != NULL) { - *pChannels = pDecoder->outputChannels; - } - - if (pSampleRate != NULL) { - *pSampleRate = pDecoder->outputSampleRate; - } - - if (pChannelMap != NULL) { - ma_data_converter_get_output_channel_map(&pDecoder->converter, pChannelMap, channelMapCap); - } - - return MA_SUCCESS; -} - -MA_API ma_result ma_decoder_get_cursor_in_pcm_frames(ma_decoder* pDecoder, ma_uint64* pCursor) -{ - if (pCursor == NULL) { - return MA_INVALID_ARGS; - } - - *pCursor = 0; - - if (pDecoder == NULL) { - return MA_INVALID_ARGS; - } - - *pCursor = pDecoder->readPointerInPCMFrames; - - return MA_SUCCESS; -} - -MA_API ma_result ma_decoder_get_length_in_pcm_frames(ma_decoder* pDecoder, ma_uint64* pLength) -{ - if (pLength == NULL) { - return MA_INVALID_ARGS; - } - - *pLength = 0; - - if (pDecoder == NULL) { - return MA_INVALID_ARGS; - } - - if (pDecoder->pBackend != NULL) { - ma_result result; - ma_uint64 internalLengthInPCMFrames; - ma_uint32 internalSampleRate; - - result = ma_data_source_get_length_in_pcm_frames(pDecoder->pBackend, &internalLengthInPCMFrames); - if (result != MA_SUCCESS) { - return result; /* Failed to retrieve the internal length. */ - } - - result = ma_data_source_get_data_format(pDecoder->pBackend, NULL, NULL, &internalSampleRate, NULL, 0); - if (result != MA_SUCCESS) { - return result; /* Failed to retrieve the internal sample rate. */ - } - - if (internalSampleRate == pDecoder->outputSampleRate) { - *pLength = internalLengthInPCMFrames; - } else { - *pLength = ma_calculate_frame_count_after_resampling(pDecoder->outputSampleRate, internalSampleRate, internalLengthInPCMFrames); - } - - return MA_SUCCESS; - } else { - return MA_NO_BACKEND; - } -} - -MA_API ma_result ma_decoder_get_available_frames(ma_decoder* pDecoder, ma_uint64* pAvailableFrames) -{ - ma_result result; - ma_uint64 totalFrameCount; - - if (pAvailableFrames == NULL) { - return MA_INVALID_ARGS; - } - - *pAvailableFrames = 0; - - if (pDecoder == NULL) { - return MA_INVALID_ARGS; - } - - result = ma_decoder_get_length_in_pcm_frames(pDecoder, &totalFrameCount); - if (result != MA_SUCCESS) { - return result; - } - - if (totalFrameCount <= pDecoder->readPointerInPCMFrames) { - *pAvailableFrames = 0; - } else { - *pAvailableFrames = totalFrameCount - pDecoder->readPointerInPCMFrames; - } - - return MA_SUCCESS; -} - - -static ma_result ma_decoder__full_decode_and_uninit(ma_decoder* pDecoder, ma_decoder_config* pConfigOut, ma_uint64* pFrameCountOut, void** ppPCMFramesOut) -{ - ma_result result; - ma_uint64 totalFrameCount; - ma_uint64 bpf; - ma_uint64 dataCapInFrames; - void* pPCMFramesOut; - - MA_ASSERT(pDecoder != NULL); - - totalFrameCount = 0; - bpf = ma_get_bytes_per_frame(pDecoder->outputFormat, pDecoder->outputChannels); - - /* The frame count is unknown until we try reading. Thus, we just run in a loop. */ - dataCapInFrames = 0; - pPCMFramesOut = NULL; - for (;;) { - ma_uint64 frameCountToTryReading; - ma_uint64 framesJustRead; - - /* Make room if there's not enough. */ - if (totalFrameCount == dataCapInFrames) { - void* pNewPCMFramesOut; - ma_uint64 newDataCapInFrames = dataCapInFrames*2; - if (newDataCapInFrames == 0) { - newDataCapInFrames = 4096; - } - - if ((newDataCapInFrames * bpf) > MA_SIZE_MAX) { - ma_free(pPCMFramesOut, &pDecoder->allocationCallbacks); - return MA_TOO_BIG; - } - - pNewPCMFramesOut = (void*)ma_realloc(pPCMFramesOut, (size_t)(newDataCapInFrames * bpf), &pDecoder->allocationCallbacks); - if (pNewPCMFramesOut == NULL) { - ma_free(pPCMFramesOut, &pDecoder->allocationCallbacks); - return MA_OUT_OF_MEMORY; - } - - dataCapInFrames = newDataCapInFrames; - pPCMFramesOut = pNewPCMFramesOut; - } - - frameCountToTryReading = dataCapInFrames - totalFrameCount; - MA_ASSERT(frameCountToTryReading > 0); - - result = ma_decoder_read_pcm_frames(pDecoder, (ma_uint8*)pPCMFramesOut + (totalFrameCount * bpf), frameCountToTryReading, &framesJustRead); - totalFrameCount += framesJustRead; - - if (result != MA_SUCCESS) { - break; - } - - if (framesJustRead < frameCountToTryReading) { - break; - } - } - - - if (pConfigOut != NULL) { - pConfigOut->format = pDecoder->outputFormat; - pConfigOut->channels = pDecoder->outputChannels; - pConfigOut->sampleRate = pDecoder->outputSampleRate; - } - - if (ppPCMFramesOut != NULL) { - *ppPCMFramesOut = pPCMFramesOut; - } else { - ma_free(pPCMFramesOut, &pDecoder->allocationCallbacks); - } - - if (pFrameCountOut != NULL) { - *pFrameCountOut = totalFrameCount; - } - - ma_decoder_uninit(pDecoder); - return MA_SUCCESS; -} - -MA_API ma_result ma_decode_from_vfs(ma_vfs* pVFS, const char* pFilePath, ma_decoder_config* pConfig, ma_uint64* pFrameCountOut, void** ppPCMFramesOut) -{ - ma_result result; - ma_decoder_config config; - ma_decoder decoder; - - if (pFrameCountOut != NULL) { - *pFrameCountOut = 0; - } - if (ppPCMFramesOut != NULL) { - *ppPCMFramesOut = NULL; - } - - config = ma_decoder_config_init_copy(pConfig); - - result = ma_decoder_init_vfs(pVFS, pFilePath, &config, &decoder); - if (result != MA_SUCCESS) { - return result; - } - - result = ma_decoder__full_decode_and_uninit(&decoder, pConfig, pFrameCountOut, ppPCMFramesOut); - - return result; -} - -MA_API ma_result ma_decode_file(const char* pFilePath, ma_decoder_config* pConfig, ma_uint64* pFrameCountOut, void** ppPCMFramesOut) -{ - return ma_decode_from_vfs(NULL, pFilePath, pConfig, pFrameCountOut, ppPCMFramesOut); -} - -MA_API ma_result ma_decode_memory(const void* pData, size_t dataSize, ma_decoder_config* pConfig, ma_uint64* pFrameCountOut, void** ppPCMFramesOut) -{ - ma_decoder_config config; - ma_decoder decoder; - ma_result result; - - if (pFrameCountOut != NULL) { - *pFrameCountOut = 0; - } - if (ppPCMFramesOut != NULL) { - *ppPCMFramesOut = NULL; - } - - if (pData == NULL || dataSize == 0) { - return MA_INVALID_ARGS; - } - - config = ma_decoder_config_init_copy(pConfig); - - result = ma_decoder_init_memory(pData, dataSize, &config, &decoder); - if (result != MA_SUCCESS) { - return result; - } - - return ma_decoder__full_decode_and_uninit(&decoder, pConfig, pFrameCountOut, ppPCMFramesOut); -} -#endif /* MA_NO_DECODING */ - - -#ifndef MA_NO_ENCODING - -#if defined(MA_HAS_WAV) -static size_t ma_encoder__internal_on_write_wav(void* pUserData, const void* pData, size_t bytesToWrite) -{ - ma_encoder* pEncoder = (ma_encoder*)pUserData; - size_t bytesWritten = 0; - - MA_ASSERT(pEncoder != NULL); - - pEncoder->onWrite(pEncoder, pData, bytesToWrite, &bytesWritten); - return bytesWritten; -} - -static ma_bool32 ma_encoder__internal_on_seek_wav(void* pUserData, int offset, ma_dr_wav_seek_origin origin) -{ - ma_encoder* pEncoder = (ma_encoder*)pUserData; - ma_result result; - - MA_ASSERT(pEncoder != NULL); - - result = pEncoder->onSeek(pEncoder, offset, (origin == ma_dr_wav_seek_origin_start) ? ma_seek_origin_start : ma_seek_origin_current); - if (result != MA_SUCCESS) { - return MA_FALSE; - } else { - return MA_TRUE; - } -} - -static ma_result ma_encoder__on_init_wav(ma_encoder* pEncoder) -{ - ma_dr_wav_data_format wavFormat; - ma_allocation_callbacks allocationCallbacks; - ma_dr_wav* pWav; - - MA_ASSERT(pEncoder != NULL); - - pWav = (ma_dr_wav*)ma_malloc(sizeof(*pWav), &pEncoder->config.allocationCallbacks); - if (pWav == NULL) { - return MA_OUT_OF_MEMORY; - } - - wavFormat.container = ma_dr_wav_container_riff; - wavFormat.channels = pEncoder->config.channels; - wavFormat.sampleRate = pEncoder->config.sampleRate; - wavFormat.bitsPerSample = ma_get_bytes_per_sample(pEncoder->config.format) * 8; - if (pEncoder->config.format == ma_format_f32) { - wavFormat.format = MA_DR_WAVE_FORMAT_IEEE_FLOAT; - } else { - wavFormat.format = MA_DR_WAVE_FORMAT_PCM; - } - - allocationCallbacks.pUserData = pEncoder->config.allocationCallbacks.pUserData; - allocationCallbacks.onMalloc = pEncoder->config.allocationCallbacks.onMalloc; - allocationCallbacks.onRealloc = pEncoder->config.allocationCallbacks.onRealloc; - allocationCallbacks.onFree = pEncoder->config.allocationCallbacks.onFree; - - if (!ma_dr_wav_init_write(pWav, &wavFormat, ma_encoder__internal_on_write_wav, ma_encoder__internal_on_seek_wav, pEncoder, &allocationCallbacks)) { - return MA_ERROR; - } - - pEncoder->pInternalEncoder = pWav; - - return MA_SUCCESS; -} - -static void ma_encoder__on_uninit_wav(ma_encoder* pEncoder) -{ - ma_dr_wav* pWav; - - MA_ASSERT(pEncoder != NULL); - - pWav = (ma_dr_wav*)pEncoder->pInternalEncoder; - MA_ASSERT(pWav != NULL); - - ma_dr_wav_uninit(pWav); - ma_free(pWav, &pEncoder->config.allocationCallbacks); -} - -static ma_result ma_encoder__on_write_pcm_frames_wav(ma_encoder* pEncoder, const void* pFramesIn, ma_uint64 frameCount, ma_uint64* pFramesWritten) -{ - ma_dr_wav* pWav; - ma_uint64 framesWritten; - - MA_ASSERT(pEncoder != NULL); - - pWav = (ma_dr_wav*)pEncoder->pInternalEncoder; - MA_ASSERT(pWav != NULL); - - framesWritten = ma_dr_wav_write_pcm_frames(pWav, frameCount, pFramesIn); - - if (pFramesWritten != NULL) { - *pFramesWritten = framesWritten; - } - - return MA_SUCCESS; -} -#endif - -MA_API ma_encoder_config ma_encoder_config_init(ma_encoding_format encodingFormat, ma_format format, ma_uint32 channels, ma_uint32 sampleRate) -{ - ma_encoder_config config; - - MA_ZERO_OBJECT(&config); - config.encodingFormat = encodingFormat; - config.format = format; - config.channels = channels; - config.sampleRate = sampleRate; - - return config; -} - -MA_API ma_result ma_encoder_preinit(const ma_encoder_config* pConfig, ma_encoder* pEncoder) -{ - ma_result result; - - if (pEncoder == NULL) { - return MA_INVALID_ARGS; - } - - MA_ZERO_OBJECT(pEncoder); - - if (pConfig == NULL) { - return MA_INVALID_ARGS; - } - - if (pConfig->format == ma_format_unknown || pConfig->channels == 0 || pConfig->sampleRate == 0) { - return MA_INVALID_ARGS; - } - - pEncoder->config = *pConfig; - - result = ma_allocation_callbacks_init_copy(&pEncoder->config.allocationCallbacks, &pConfig->allocationCallbacks); - if (result != MA_SUCCESS) { - return result; - } - - return MA_SUCCESS; -} - -MA_API ma_result ma_encoder_init__internal(ma_encoder_write_proc onWrite, ma_encoder_seek_proc onSeek, void* pUserData, ma_encoder* pEncoder) -{ - ma_result result = MA_SUCCESS; - - /* This assumes ma_encoder_preinit() has been called prior. */ - MA_ASSERT(pEncoder != NULL); - - if (onWrite == NULL || onSeek == NULL) { - return MA_INVALID_ARGS; - } - - pEncoder->onWrite = onWrite; - pEncoder->onSeek = onSeek; - pEncoder->pUserData = pUserData; - - switch (pEncoder->config.encodingFormat) - { - case ma_encoding_format_wav: - { - #if defined(MA_HAS_WAV) - pEncoder->onInit = ma_encoder__on_init_wav; - pEncoder->onUninit = ma_encoder__on_uninit_wav; - pEncoder->onWritePCMFrames = ma_encoder__on_write_pcm_frames_wav; - #else - result = MA_NO_BACKEND; - #endif - } break; - - default: - { - result = MA_INVALID_ARGS; - } break; - } - - /* Getting here means we should have our backend callbacks set up. */ - if (result == MA_SUCCESS) { - result = pEncoder->onInit(pEncoder); - } - - return result; -} - -static ma_result ma_encoder__on_write_vfs(ma_encoder* pEncoder, const void* pBufferIn, size_t bytesToWrite, size_t* pBytesWritten) -{ - return ma_vfs_or_default_write(pEncoder->data.vfs.pVFS, pEncoder->data.vfs.file, pBufferIn, bytesToWrite, pBytesWritten); -} - -static ma_result ma_encoder__on_seek_vfs(ma_encoder* pEncoder, ma_int64 offset, ma_seek_origin origin) -{ - return ma_vfs_or_default_seek(pEncoder->data.vfs.pVFS, pEncoder->data.vfs.file, offset, origin); -} - -MA_API ma_result ma_encoder_init_vfs(ma_vfs* pVFS, const char* pFilePath, const ma_encoder_config* pConfig, ma_encoder* pEncoder) -{ - ma_result result; - ma_vfs_file file; - - result = ma_encoder_preinit(pConfig, pEncoder); - if (result != MA_SUCCESS) { - return result; - } - - /* Now open the file. If this fails we don't need to uninitialize the encoder. */ - result = ma_vfs_or_default_open(pVFS, pFilePath, MA_OPEN_MODE_WRITE, &file); - if (result != MA_SUCCESS) { - return result; - } - - pEncoder->data.vfs.pVFS = pVFS; - pEncoder->data.vfs.file = file; - - result = ma_encoder_init__internal(ma_encoder__on_write_vfs, ma_encoder__on_seek_vfs, NULL, pEncoder); - if (result != MA_SUCCESS) { - ma_vfs_or_default_close(pVFS, file); - return result; - } - - return MA_SUCCESS; -} - -MA_API ma_result ma_encoder_init_vfs_w(ma_vfs* pVFS, const wchar_t* pFilePath, const ma_encoder_config* pConfig, ma_encoder* pEncoder) -{ - ma_result result; - ma_vfs_file file; - - result = ma_encoder_preinit(pConfig, pEncoder); - if (result != MA_SUCCESS) { - return result; - } - - /* Now open the file. If this fails we don't need to uninitialize the encoder. */ - result = ma_vfs_or_default_open_w(pVFS, pFilePath, MA_OPEN_MODE_WRITE, &file); - if (result != MA_SUCCESS) { - return result; - } - - pEncoder->data.vfs.pVFS = pVFS; - pEncoder->data.vfs.file = file; - - result = ma_encoder_init__internal(ma_encoder__on_write_vfs, ma_encoder__on_seek_vfs, NULL, pEncoder); - if (result != MA_SUCCESS) { - ma_vfs_or_default_close(pVFS, file); - return result; - } - - return MA_SUCCESS; -} - -MA_API ma_result ma_encoder_init_file(const char* pFilePath, const ma_encoder_config* pConfig, ma_encoder* pEncoder) -{ - return ma_encoder_init_vfs(NULL, pFilePath, pConfig, pEncoder); -} - -MA_API ma_result ma_encoder_init_file_w(const wchar_t* pFilePath, const ma_encoder_config* pConfig, ma_encoder* pEncoder) -{ - return ma_encoder_init_vfs_w(NULL, pFilePath, pConfig, pEncoder); -} - -MA_API ma_result ma_encoder_init(ma_encoder_write_proc onWrite, ma_encoder_seek_proc onSeek, void* pUserData, const ma_encoder_config* pConfig, ma_encoder* pEncoder) -{ - ma_result result; - - result = ma_encoder_preinit(pConfig, pEncoder); - if (result != MA_SUCCESS) { - return result; - } - - return ma_encoder_init__internal(onWrite, onSeek, pUserData, pEncoder); -} - - -MA_API void ma_encoder_uninit(ma_encoder* pEncoder) -{ - if (pEncoder == NULL) { - return; - } - - if (pEncoder->onUninit) { - pEncoder->onUninit(pEncoder); - } - - /* If we have a file handle, close it. */ - if (pEncoder->onWrite == ma_encoder__on_write_vfs) { - ma_vfs_or_default_close(pEncoder->data.vfs.pVFS, pEncoder->data.vfs.file); - pEncoder->data.vfs.file = NULL; - } -} - - -MA_API ma_result ma_encoder_write_pcm_frames(ma_encoder* pEncoder, const void* pFramesIn, ma_uint64 frameCount, ma_uint64* pFramesWritten) -{ - if (pFramesWritten != NULL) { - *pFramesWritten = 0; - } - - if (pEncoder == NULL || pFramesIn == NULL) { - return MA_INVALID_ARGS; - } - - return pEncoder->onWritePCMFrames(pEncoder, pFramesIn, frameCount, pFramesWritten); -} -#endif /* MA_NO_ENCODING */ - - - -/************************************************************************************************************************************************************** - -Generation - -**************************************************************************************************************************************************************/ -#ifndef MA_NO_GENERATION -MA_API ma_waveform_config ma_waveform_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRate, ma_waveform_type type, double amplitude, double frequency) -{ - ma_waveform_config config; - - MA_ZERO_OBJECT(&config); - config.format = format; - config.channels = channels; - config.sampleRate = sampleRate; - config.type = type; - config.amplitude = amplitude; - config.frequency = frequency; - - return config; -} - -static ma_result ma_waveform__data_source_on_read(ma_data_source* pDataSource, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead) -{ - return ma_waveform_read_pcm_frames((ma_waveform*)pDataSource, pFramesOut, frameCount, pFramesRead); -} - -static ma_result ma_waveform__data_source_on_seek(ma_data_source* pDataSource, ma_uint64 frameIndex) -{ - return ma_waveform_seek_to_pcm_frame((ma_waveform*)pDataSource, frameIndex); -} - -static ma_result ma_waveform__data_source_on_get_data_format(ma_data_source* pDataSource, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate, ma_channel* pChannelMap, size_t channelMapCap) -{ - ma_waveform* pWaveform = (ma_waveform*)pDataSource; - - *pFormat = pWaveform->config.format; - *pChannels = pWaveform->config.channels; - *pSampleRate = pWaveform->config.sampleRate; - ma_channel_map_init_standard(ma_standard_channel_map_default, pChannelMap, channelMapCap, pWaveform->config.channels); - - return MA_SUCCESS; -} - -static ma_result ma_waveform__data_source_on_get_cursor(ma_data_source* pDataSource, ma_uint64* pCursor) -{ - ma_waveform* pWaveform = (ma_waveform*)pDataSource; - - *pCursor = (ma_uint64)(pWaveform->time / pWaveform->advance); - - return MA_SUCCESS; -} - -static double ma_waveform__calculate_advance(ma_uint32 sampleRate, double frequency) -{ - return (1.0 / (sampleRate / frequency)); -} - -static void ma_waveform__update_advance(ma_waveform* pWaveform) -{ - pWaveform->advance = ma_waveform__calculate_advance(pWaveform->config.sampleRate, pWaveform->config.frequency); -} - -static ma_data_source_vtable g_ma_waveform_data_source_vtable = -{ - ma_waveform__data_source_on_read, - ma_waveform__data_source_on_seek, - ma_waveform__data_source_on_get_data_format, - ma_waveform__data_source_on_get_cursor, - NULL, /* onGetLength. There's no notion of a length in waveforms. */ - NULL, /* onSetLooping */ - 0 -}; - -MA_API ma_result ma_waveform_init(const ma_waveform_config* pConfig, ma_waveform* pWaveform) -{ - ma_result result; - ma_data_source_config dataSourceConfig; - - if (pWaveform == NULL) { - return MA_INVALID_ARGS; - } - - MA_ZERO_OBJECT(pWaveform); - - dataSourceConfig = ma_data_source_config_init(); - dataSourceConfig.vtable = &g_ma_waveform_data_source_vtable; - - result = ma_data_source_init(&dataSourceConfig, &pWaveform->ds); - if (result != MA_SUCCESS) { - return result; - } - - pWaveform->config = *pConfig; - pWaveform->advance = ma_waveform__calculate_advance(pWaveform->config.sampleRate, pWaveform->config.frequency); - pWaveform->time = 0; - - return MA_SUCCESS; -} - -MA_API void ma_waveform_uninit(ma_waveform* pWaveform) -{ - if (pWaveform == NULL) { - return; - } - - ma_data_source_uninit(&pWaveform->ds); -} - -MA_API ma_result ma_waveform_set_amplitude(ma_waveform* pWaveform, double amplitude) -{ - if (pWaveform == NULL) { - return MA_INVALID_ARGS; - } - - pWaveform->config.amplitude = amplitude; - return MA_SUCCESS; -} - -MA_API ma_result ma_waveform_set_frequency(ma_waveform* pWaveform, double frequency) -{ - if (pWaveform == NULL) { - return MA_INVALID_ARGS; - } - - pWaveform->config.frequency = frequency; - ma_waveform__update_advance(pWaveform); - - return MA_SUCCESS; -} - -MA_API ma_result ma_waveform_set_type(ma_waveform* pWaveform, ma_waveform_type type) -{ - if (pWaveform == NULL) { - return MA_INVALID_ARGS; - } - - pWaveform->config.type = type; - return MA_SUCCESS; -} - -MA_API ma_result ma_waveform_set_sample_rate(ma_waveform* pWaveform, ma_uint32 sampleRate) -{ - if (pWaveform == NULL) { - return MA_INVALID_ARGS; - } - - pWaveform->config.sampleRate = sampleRate; - ma_waveform__update_advance(pWaveform); - - return MA_SUCCESS; -} - -static float ma_waveform_sine_f32(double time, double amplitude) -{ - return (float)(ma_sind(MA_TAU_D * time) * amplitude); -} - -static ma_int16 ma_waveform_sine_s16(double time, double amplitude) -{ - return ma_pcm_sample_f32_to_s16(ma_waveform_sine_f32(time, amplitude)); -} - -static float ma_waveform_square_f32(double time, double dutyCycle, double amplitude) -{ - double f = time - (ma_int64)time; - double r; - - if (f < dutyCycle) { - r = amplitude; - } else { - r = -amplitude; - } - - return (float)r; -} - -static ma_int16 ma_waveform_square_s16(double time, double dutyCycle, double amplitude) -{ - return ma_pcm_sample_f32_to_s16(ma_waveform_square_f32(time, dutyCycle, amplitude)); -} - -static float ma_waveform_triangle_f32(double time, double amplitude) -{ - double f = time - (ma_int64)time; - double r; - - r = 2 * ma_abs(2 * (f - 0.5)) - 1; - - return (float)(r * amplitude); -} - -static ma_int16 ma_waveform_triangle_s16(double time, double amplitude) -{ - return ma_pcm_sample_f32_to_s16(ma_waveform_triangle_f32(time, amplitude)); -} - -static float ma_waveform_sawtooth_f32(double time, double amplitude) -{ - double f = time - (ma_int64)time; - double r; - - r = 2 * (f - 0.5); - - return (float)(r * amplitude); -} - -static ma_int16 ma_waveform_sawtooth_s16(double time, double amplitude) -{ - return ma_pcm_sample_f32_to_s16(ma_waveform_sawtooth_f32(time, amplitude)); -} - -static void ma_waveform_read_pcm_frames__sine(ma_waveform* pWaveform, void* pFramesOut, ma_uint64 frameCount) -{ - ma_uint64 iFrame; - ma_uint64 iChannel; - ma_uint32 bps = ma_get_bytes_per_sample(pWaveform->config.format); - ma_uint32 bpf = bps * pWaveform->config.channels; - - MA_ASSERT(pWaveform != NULL); - MA_ASSERT(pFramesOut != NULL); - - if (pWaveform->config.format == ma_format_f32) { - float* pFramesOutF32 = (float*)pFramesOut; - for (iFrame = 0; iFrame < frameCount; iFrame += 1) { - float s = ma_waveform_sine_f32(pWaveform->time, pWaveform->config.amplitude); - pWaveform->time += pWaveform->advance; - - for (iChannel = 0; iChannel < pWaveform->config.channels; iChannel += 1) { - pFramesOutF32[iFrame*pWaveform->config.channels + iChannel] = s; - } - } - } else if (pWaveform->config.format == ma_format_s16) { - ma_int16* pFramesOutS16 = (ma_int16*)pFramesOut; - for (iFrame = 0; iFrame < frameCount; iFrame += 1) { - ma_int16 s = ma_waveform_sine_s16(pWaveform->time, pWaveform->config.amplitude); - pWaveform->time += pWaveform->advance; - - for (iChannel = 0; iChannel < pWaveform->config.channels; iChannel += 1) { - pFramesOutS16[iFrame*pWaveform->config.channels + iChannel] = s; - } - } - } else { - for (iFrame = 0; iFrame < frameCount; iFrame += 1) { - float s = ma_waveform_sine_f32(pWaveform->time, pWaveform->config.amplitude); - pWaveform->time += pWaveform->advance; - - for (iChannel = 0; iChannel < pWaveform->config.channels; iChannel += 1) { - ma_pcm_convert(ma_offset_ptr(pFramesOut, iFrame*bpf + iChannel*bps), pWaveform->config.format, &s, ma_format_f32, 1, ma_dither_mode_none); - } - } - } -} - -static void ma_waveform_read_pcm_frames__square(ma_waveform* pWaveform, double dutyCycle, void* pFramesOut, ma_uint64 frameCount) -{ - ma_uint64 iFrame; - ma_uint64 iChannel; - ma_uint32 bps = ma_get_bytes_per_sample(pWaveform->config.format); - ma_uint32 bpf = bps * pWaveform->config.channels; - - MA_ASSERT(pWaveform != NULL); - MA_ASSERT(pFramesOut != NULL); - - if (pWaveform->config.format == ma_format_f32) { - float* pFramesOutF32 = (float*)pFramesOut; - for (iFrame = 0; iFrame < frameCount; iFrame += 1) { - float s = ma_waveform_square_f32(pWaveform->time, dutyCycle, pWaveform->config.amplitude); - pWaveform->time += pWaveform->advance; - - for (iChannel = 0; iChannel < pWaveform->config.channels; iChannel += 1) { - pFramesOutF32[iFrame*pWaveform->config.channels + iChannel] = s; - } - } - } else if (pWaveform->config.format == ma_format_s16) { - ma_int16* pFramesOutS16 = (ma_int16*)pFramesOut; - for (iFrame = 0; iFrame < frameCount; iFrame += 1) { - ma_int16 s = ma_waveform_square_s16(pWaveform->time, dutyCycle, pWaveform->config.amplitude); - pWaveform->time += pWaveform->advance; - - for (iChannel = 0; iChannel < pWaveform->config.channels; iChannel += 1) { - pFramesOutS16[iFrame*pWaveform->config.channels + iChannel] = s; - } - } - } else { - for (iFrame = 0; iFrame < frameCount; iFrame += 1) { - float s = ma_waveform_square_f32(pWaveform->time, dutyCycle, pWaveform->config.amplitude); - pWaveform->time += pWaveform->advance; - - for (iChannel = 0; iChannel < pWaveform->config.channels; iChannel += 1) { - ma_pcm_convert(ma_offset_ptr(pFramesOut, iFrame*bpf + iChannel*bps), pWaveform->config.format, &s, ma_format_f32, 1, ma_dither_mode_none); - } - } - } -} - -static void ma_waveform_read_pcm_frames__triangle(ma_waveform* pWaveform, void* pFramesOut, ma_uint64 frameCount) -{ - ma_uint64 iFrame; - ma_uint64 iChannel; - ma_uint32 bps = ma_get_bytes_per_sample(pWaveform->config.format); - ma_uint32 bpf = bps * pWaveform->config.channels; - - MA_ASSERT(pWaveform != NULL); - MA_ASSERT(pFramesOut != NULL); - - if (pWaveform->config.format == ma_format_f32) { - float* pFramesOutF32 = (float*)pFramesOut; - for (iFrame = 0; iFrame < frameCount; iFrame += 1) { - float s = ma_waveform_triangle_f32(pWaveform->time, pWaveform->config.amplitude); - pWaveform->time += pWaveform->advance; - - for (iChannel = 0; iChannel < pWaveform->config.channels; iChannel += 1) { - pFramesOutF32[iFrame*pWaveform->config.channels + iChannel] = s; - } - } - } else if (pWaveform->config.format == ma_format_s16) { - ma_int16* pFramesOutS16 = (ma_int16*)pFramesOut; - for (iFrame = 0; iFrame < frameCount; iFrame += 1) { - ma_int16 s = ma_waveform_triangle_s16(pWaveform->time, pWaveform->config.amplitude); - pWaveform->time += pWaveform->advance; - - for (iChannel = 0; iChannel < pWaveform->config.channels; iChannel += 1) { - pFramesOutS16[iFrame*pWaveform->config.channels + iChannel] = s; - } - } - } else { - for (iFrame = 0; iFrame < frameCount; iFrame += 1) { - float s = ma_waveform_triangle_f32(pWaveform->time, pWaveform->config.amplitude); - pWaveform->time += pWaveform->advance; - - for (iChannel = 0; iChannel < pWaveform->config.channels; iChannel += 1) { - ma_pcm_convert(ma_offset_ptr(pFramesOut, iFrame*bpf + iChannel*bps), pWaveform->config.format, &s, ma_format_f32, 1, ma_dither_mode_none); - } - } - } -} - -static void ma_waveform_read_pcm_frames__sawtooth(ma_waveform* pWaveform, void* pFramesOut, ma_uint64 frameCount) -{ - ma_uint64 iFrame; - ma_uint64 iChannel; - ma_uint32 bps = ma_get_bytes_per_sample(pWaveform->config.format); - ma_uint32 bpf = bps * pWaveform->config.channels; - - MA_ASSERT(pWaveform != NULL); - MA_ASSERT(pFramesOut != NULL); - - if (pWaveform->config.format == ma_format_f32) { - float* pFramesOutF32 = (float*)pFramesOut; - for (iFrame = 0; iFrame < frameCount; iFrame += 1) { - float s = ma_waveform_sawtooth_f32(pWaveform->time, pWaveform->config.amplitude); - pWaveform->time += pWaveform->advance; - - for (iChannel = 0; iChannel < pWaveform->config.channels; iChannel += 1) { - pFramesOutF32[iFrame*pWaveform->config.channels + iChannel] = s; - } - } - } else if (pWaveform->config.format == ma_format_s16) { - ma_int16* pFramesOutS16 = (ma_int16*)pFramesOut; - for (iFrame = 0; iFrame < frameCount; iFrame += 1) { - ma_int16 s = ma_waveform_sawtooth_s16(pWaveform->time, pWaveform->config.amplitude); - pWaveform->time += pWaveform->advance; - - for (iChannel = 0; iChannel < pWaveform->config.channels; iChannel += 1) { - pFramesOutS16[iFrame*pWaveform->config.channels + iChannel] = s; - } - } - } else { - for (iFrame = 0; iFrame < frameCount; iFrame += 1) { - float s = ma_waveform_sawtooth_f32(pWaveform->time, pWaveform->config.amplitude); - pWaveform->time += pWaveform->advance; - - for (iChannel = 0; iChannel < pWaveform->config.channels; iChannel += 1) { - ma_pcm_convert(ma_offset_ptr(pFramesOut, iFrame*bpf + iChannel*bps), pWaveform->config.format, &s, ma_format_f32, 1, ma_dither_mode_none); - } - } - } -} - -MA_API ma_result ma_waveform_read_pcm_frames(ma_waveform* pWaveform, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead) -{ - if (pFramesRead != NULL) { - *pFramesRead = 0; - } - - if (frameCount == 0) { - return MA_INVALID_ARGS; - } - - if (pWaveform == NULL) { - return MA_INVALID_ARGS; - } - - if (pFramesOut != NULL) { - switch (pWaveform->config.type) - { - case ma_waveform_type_sine: - { - ma_waveform_read_pcm_frames__sine(pWaveform, pFramesOut, frameCount); - } break; - - case ma_waveform_type_square: - { - ma_waveform_read_pcm_frames__square(pWaveform, 0.5, pFramesOut, frameCount); - } break; - - case ma_waveform_type_triangle: - { - ma_waveform_read_pcm_frames__triangle(pWaveform, pFramesOut, frameCount); - } break; - - case ma_waveform_type_sawtooth: - { - ma_waveform_read_pcm_frames__sawtooth(pWaveform, pFramesOut, frameCount); - } break; - - default: return MA_INVALID_OPERATION; /* Unknown waveform type. */ - } - } else { - pWaveform->time += pWaveform->advance * (ma_int64)frameCount; /* Cast to int64 required for VC6. Won't affect anything in practice. */ - } - - if (pFramesRead != NULL) { - *pFramesRead = frameCount; - } - - return MA_SUCCESS; -} - -MA_API ma_result ma_waveform_seek_to_pcm_frame(ma_waveform* pWaveform, ma_uint64 frameIndex) -{ - if (pWaveform == NULL) { - return MA_INVALID_ARGS; - } - - pWaveform->time = pWaveform->advance * (ma_int64)frameIndex; /* Casting for VC6. Won't be an issue in practice. */ - - return MA_SUCCESS; -} - -MA_API ma_pulsewave_config ma_pulsewave_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRate, double dutyCycle, double amplitude, double frequency) -{ - ma_pulsewave_config config; - - MA_ZERO_OBJECT(&config); - config.format = format; - config.channels = channels; - config.sampleRate = sampleRate; - config.dutyCycle = dutyCycle; - config.amplitude = amplitude; - config.frequency = frequency; - - return config; -} - -MA_API ma_result ma_pulsewave_init(const ma_pulsewave_config* pConfig, ma_pulsewave* pWaveform) -{ - ma_result result; - ma_waveform_config config; - - if (pWaveform == NULL) { - return MA_INVALID_ARGS; - } - - MA_ZERO_OBJECT(pWaveform); - - config = ma_waveform_config_init( - pConfig->format, - pConfig->channels, - pConfig->sampleRate, - ma_waveform_type_square, - pConfig->amplitude, - pConfig->frequency - ); - - result = ma_waveform_init(&config, &pWaveform->waveform); - ma_pulsewave_set_duty_cycle(pWaveform, pConfig->dutyCycle); - - return result; -} - -MA_API void ma_pulsewave_uninit(ma_pulsewave* pWaveform) -{ - if (pWaveform == NULL) { - return; - } - - ma_waveform_uninit(&pWaveform->waveform); -} - -MA_API ma_result ma_pulsewave_read_pcm_frames(ma_pulsewave* pWaveform, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead) -{ - if (pFramesRead != NULL) { - *pFramesRead = 0; - } - - if (frameCount == 0) { - return MA_INVALID_ARGS; - } - - if (pWaveform == NULL) { - return MA_INVALID_ARGS; - } - - if (pFramesOut != NULL) { - ma_waveform_read_pcm_frames__square(&pWaveform->waveform, pWaveform->config.dutyCycle, pFramesOut, frameCount); - } else { - pWaveform->waveform.time += pWaveform->waveform.advance * (ma_int64)frameCount; /* Cast to int64 required for VC6. Won't affect anything in practice. */ - } - - if (pFramesRead != NULL) { - *pFramesRead = frameCount; - } - - return MA_SUCCESS; -} - -MA_API ma_result ma_pulsewave_seek_to_pcm_frame(ma_pulsewave* pWaveform, ma_uint64 frameIndex) -{ - if (pWaveform == NULL) { - return MA_INVALID_ARGS; - } - - ma_waveform_seek_to_pcm_frame(&pWaveform->waveform, frameIndex); - - return MA_SUCCESS; -} - -MA_API ma_result ma_pulsewave_set_amplitude(ma_pulsewave* pWaveform, double amplitude) -{ - if (pWaveform == NULL) { - return MA_INVALID_ARGS; - } - - pWaveform->config.amplitude = amplitude; - ma_waveform_set_amplitude(&pWaveform->waveform, amplitude); - - return MA_SUCCESS; -} - -MA_API ma_result ma_pulsewave_set_frequency(ma_pulsewave* pWaveform, double frequency) -{ - if (pWaveform == NULL) { - return MA_INVALID_ARGS; - } - - pWaveform->config.frequency = frequency; - ma_waveform_set_frequency(&pWaveform->waveform, frequency); - - return MA_SUCCESS; -} - -MA_API ma_result ma_pulsewave_set_sample_rate(ma_pulsewave* pWaveform, ma_uint32 sampleRate) -{ - if (pWaveform == NULL) { - return MA_INVALID_ARGS; - } - - pWaveform->config.sampleRate = sampleRate; - ma_waveform_set_sample_rate(&pWaveform->waveform, sampleRate); - - return MA_SUCCESS; -} - -MA_API ma_result ma_pulsewave_set_duty_cycle(ma_pulsewave* pWaveform, double dutyCycle) -{ - if (pWaveform == NULL) { - return MA_INVALID_ARGS; - } - - pWaveform->config.dutyCycle = dutyCycle; - - return MA_SUCCESS; -} - - - -MA_API ma_noise_config ma_noise_config_init(ma_format format, ma_uint32 channels, ma_noise_type type, ma_int32 seed, double amplitude) -{ - ma_noise_config config; - MA_ZERO_OBJECT(&config); - - config.format = format; - config.channels = channels; - config.type = type; - config.seed = seed; - config.amplitude = amplitude; - - if (config.seed == 0) { - config.seed = MA_DEFAULT_LCG_SEED; - } - - return config; -} - - -static ma_result ma_noise__data_source_on_read(ma_data_source* pDataSource, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead) -{ - return ma_noise_read_pcm_frames((ma_noise*)pDataSource, pFramesOut, frameCount, pFramesRead); -} - -static ma_result ma_noise__data_source_on_seek(ma_data_source* pDataSource, ma_uint64 frameIndex) -{ - /* No-op. Just pretend to be successful. */ - (void)pDataSource; - (void)frameIndex; - return MA_SUCCESS; -} - -static ma_result ma_noise__data_source_on_get_data_format(ma_data_source* pDataSource, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate, ma_channel* pChannelMap, size_t channelMapCap) -{ - ma_noise* pNoise = (ma_noise*)pDataSource; - - *pFormat = pNoise->config.format; - *pChannels = pNoise->config.channels; - *pSampleRate = 0; /* There is no notion of sample rate with noise generation. */ - ma_channel_map_init_standard(ma_standard_channel_map_default, pChannelMap, channelMapCap, pNoise->config.channels); - - return MA_SUCCESS; -} - -static ma_data_source_vtable g_ma_noise_data_source_vtable = -{ - ma_noise__data_source_on_read, - ma_noise__data_source_on_seek, /* No-op for noise. */ - ma_noise__data_source_on_get_data_format, - NULL, /* onGetCursor. No notion of a cursor for noise. */ - NULL, /* onGetLength. No notion of a length for noise. */ - NULL, /* onSetLooping */ - 0 -}; - - -#ifndef MA_PINK_NOISE_BIN_SIZE -#define MA_PINK_NOISE_BIN_SIZE 16 -#endif - -typedef struct -{ - size_t sizeInBytes; - struct - { - size_t binOffset; - size_t accumulationOffset; - size_t counterOffset; - } pink; - struct - { - size_t accumulationOffset; - } brownian; -} ma_noise_heap_layout; - -static ma_result ma_noise_get_heap_layout(const ma_noise_config* pConfig, ma_noise_heap_layout* pHeapLayout) -{ - MA_ASSERT(pHeapLayout != NULL); - - MA_ZERO_OBJECT(pHeapLayout); - - if (pConfig == NULL) { - return MA_INVALID_ARGS; - } - - if (pConfig->channels == 0) { - return MA_INVALID_ARGS; - } - - pHeapLayout->sizeInBytes = 0; - - /* Pink. */ - if (pConfig->type == ma_noise_type_pink) { - /* bin */ - pHeapLayout->pink.binOffset = pHeapLayout->sizeInBytes; - pHeapLayout->sizeInBytes += sizeof(double*) * pConfig->channels; - pHeapLayout->sizeInBytes += sizeof(double ) * pConfig->channels * MA_PINK_NOISE_BIN_SIZE; - - /* accumulation */ - pHeapLayout->pink.accumulationOffset = pHeapLayout->sizeInBytes; - pHeapLayout->sizeInBytes += sizeof(double) * pConfig->channels; - - /* counter */ - pHeapLayout->pink.counterOffset = pHeapLayout->sizeInBytes; - pHeapLayout->sizeInBytes += sizeof(ma_uint32) * pConfig->channels; - } - - /* Brownian. */ - if (pConfig->type == ma_noise_type_brownian) { - /* accumulation */ - pHeapLayout->brownian.accumulationOffset = pHeapLayout->sizeInBytes; - pHeapLayout->sizeInBytes += sizeof(double) * pConfig->channels; - } - - /* Make sure allocation size is aligned. */ - pHeapLayout->sizeInBytes = ma_align_64(pHeapLayout->sizeInBytes); - - return MA_SUCCESS; -} - -MA_API ma_result ma_noise_get_heap_size(const ma_noise_config* pConfig, size_t* pHeapSizeInBytes) -{ - ma_result result; - ma_noise_heap_layout heapLayout; - - if (pHeapSizeInBytes == NULL) { - return MA_INVALID_ARGS; - } - - *pHeapSizeInBytes = 0; - - result = ma_noise_get_heap_layout(pConfig, &heapLayout); - if (result != MA_SUCCESS) { - return result; - } - - *pHeapSizeInBytes = heapLayout.sizeInBytes; - - return MA_SUCCESS; -} - -MA_API ma_result ma_noise_init_preallocated(const ma_noise_config* pConfig, void* pHeap, ma_noise* pNoise) -{ - ma_result result; - ma_noise_heap_layout heapLayout; - ma_data_source_config dataSourceConfig; - ma_uint32 iChannel; - - if (pNoise == NULL) { - return MA_INVALID_ARGS; - } - - MA_ZERO_OBJECT(pNoise); - - result = ma_noise_get_heap_layout(pConfig, &heapLayout); - if (result != MA_SUCCESS) { - return result; - } - - pNoise->_pHeap = pHeap; - MA_ZERO_MEMORY(pNoise->_pHeap, heapLayout.sizeInBytes); - - dataSourceConfig = ma_data_source_config_init(); - dataSourceConfig.vtable = &g_ma_noise_data_source_vtable; - - result = ma_data_source_init(&dataSourceConfig, &pNoise->ds); - if (result != MA_SUCCESS) { - return result; - } - - pNoise->config = *pConfig; - ma_lcg_seed(&pNoise->lcg, pConfig->seed); - - if (pNoise->config.type == ma_noise_type_pink) { - pNoise->state.pink.bin = (double** )ma_offset_ptr(pHeap, heapLayout.pink.binOffset); - pNoise->state.pink.accumulation = (double* )ma_offset_ptr(pHeap, heapLayout.pink.accumulationOffset); - pNoise->state.pink.counter = (ma_uint32*)ma_offset_ptr(pHeap, heapLayout.pink.counterOffset); - - for (iChannel = 0; iChannel < pConfig->channels; iChannel += 1) { - pNoise->state.pink.bin[iChannel] = (double*)ma_offset_ptr(pHeap, heapLayout.pink.binOffset + (sizeof(double*) * pConfig->channels) + (sizeof(double) * MA_PINK_NOISE_BIN_SIZE * iChannel)); - pNoise->state.pink.accumulation[iChannel] = 0; - pNoise->state.pink.counter[iChannel] = 1; - } - } - - if (pNoise->config.type == ma_noise_type_brownian) { - pNoise->state.brownian.accumulation = (double*)ma_offset_ptr(pHeap, heapLayout.brownian.accumulationOffset); - - for (iChannel = 0; iChannel < pConfig->channels; iChannel += 1) { - pNoise->state.brownian.accumulation[iChannel] = 0; - } - } - - return MA_SUCCESS; -} - -MA_API ma_result ma_noise_init(const ma_noise_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_noise* pNoise) -{ - ma_result result; - size_t heapSizeInBytes; - void* pHeap; - - result = ma_noise_get_heap_size(pConfig, &heapSizeInBytes); - if (result != MA_SUCCESS) { - return result; - } - - if (heapSizeInBytes > 0) { - pHeap = ma_malloc(heapSizeInBytes, pAllocationCallbacks); - if (pHeap == NULL) { - return MA_OUT_OF_MEMORY; - } - } else { - pHeap = NULL; - } - - result = ma_noise_init_preallocated(pConfig, pHeap, pNoise); - if (result != MA_SUCCESS) { - ma_free(pHeap, pAllocationCallbacks); - return result; - } - - pNoise->_ownsHeap = MA_TRUE; - return MA_SUCCESS; -} - -MA_API void ma_noise_uninit(ma_noise* pNoise, const ma_allocation_callbacks* pAllocationCallbacks) -{ - if (pNoise == NULL) { - return; - } - - ma_data_source_uninit(&pNoise->ds); - - if (pNoise->_ownsHeap) { - ma_free(pNoise->_pHeap, pAllocationCallbacks); - } -} - -MA_API ma_result ma_noise_set_amplitude(ma_noise* pNoise, double amplitude) -{ - if (pNoise == NULL) { - return MA_INVALID_ARGS; - } - - pNoise->config.amplitude = amplitude; - return MA_SUCCESS; -} - -MA_API ma_result ma_noise_set_seed(ma_noise* pNoise, ma_int32 seed) -{ - if (pNoise == NULL) { - return MA_INVALID_ARGS; - } - - pNoise->lcg.state = seed; - return MA_SUCCESS; -} - - -MA_API ma_result ma_noise_set_type(ma_noise* pNoise, ma_noise_type type) -{ - if (pNoise == NULL) { - return MA_INVALID_ARGS; - } - - /* - This function should never have been implemented in the first place. Changing the type dynamically is not - supported. Instead you need to uninitialize and reinitialize a fresh `ma_noise` object. This function - will be removed in version 0.12. - */ - MA_ASSERT(MA_FALSE); - (void)type; - - return MA_INVALID_OPERATION; -} - -static MA_INLINE float ma_noise_f32_white(ma_noise* pNoise) -{ - return (float)(ma_lcg_rand_f64(&pNoise->lcg) * pNoise->config.amplitude); -} - -static MA_INLINE ma_int16 ma_noise_s16_white(ma_noise* pNoise) -{ - return ma_pcm_sample_f32_to_s16(ma_noise_f32_white(pNoise)); -} - -static MA_INLINE ma_uint64 ma_noise_read_pcm_frames__white(ma_noise* pNoise, void* pFramesOut, ma_uint64 frameCount) -{ - ma_uint64 iFrame; - ma_uint32 iChannel; - const ma_uint32 channels = pNoise->config.channels; - MA_ASSUME(channels > 0); - - if (pNoise->config.format == ma_format_f32) { - float* pFramesOutF32 = (float*)pFramesOut; - if (pNoise->config.duplicateChannels) { - for (iFrame = 0; iFrame < frameCount; iFrame += 1) { - float s = ma_noise_f32_white(pNoise); - for (iChannel = 0; iChannel < channels; iChannel += 1) { - pFramesOutF32[iFrame*channels + iChannel] = s; - } - } - } else { - for (iFrame = 0; iFrame < frameCount; iFrame += 1) { - for (iChannel = 0; iChannel < channels; iChannel += 1) { - pFramesOutF32[iFrame*channels + iChannel] = ma_noise_f32_white(pNoise); - } - } - } - } else if (pNoise->config.format == ma_format_s16) { - ma_int16* pFramesOutS16 = (ma_int16*)pFramesOut; - if (pNoise->config.duplicateChannels) { - for (iFrame = 0; iFrame < frameCount; iFrame += 1) { - ma_int16 s = ma_noise_s16_white(pNoise); - for (iChannel = 0; iChannel < channels; iChannel += 1) { - pFramesOutS16[iFrame*channels + iChannel] = s; - } - } - } else { - for (iFrame = 0; iFrame < frameCount; iFrame += 1) { - for (iChannel = 0; iChannel < channels; iChannel += 1) { - pFramesOutS16[iFrame*channels + iChannel] = ma_noise_s16_white(pNoise); - } - } - } - } else { - const ma_uint32 bps = ma_get_bytes_per_sample(pNoise->config.format); - const ma_uint32 bpf = bps * channels; - - if (pNoise->config.duplicateChannels) { - for (iFrame = 0; iFrame < frameCount; iFrame += 1) { - float s = ma_noise_f32_white(pNoise); - for (iChannel = 0; iChannel < channels; iChannel += 1) { - ma_pcm_convert(ma_offset_ptr(pFramesOut, iFrame*bpf + iChannel*bps), pNoise->config.format, &s, ma_format_f32, 1, ma_dither_mode_none); - } - } - } else { - for (iFrame = 0; iFrame < frameCount; iFrame += 1) { - for (iChannel = 0; iChannel < channels; iChannel += 1) { - float s = ma_noise_f32_white(pNoise); - ma_pcm_convert(ma_offset_ptr(pFramesOut, iFrame*bpf + iChannel*bps), pNoise->config.format, &s, ma_format_f32, 1, ma_dither_mode_none); - } - } - } - } - - return frameCount; -} - - -static MA_INLINE unsigned int ma_tzcnt32(unsigned int x) -{ - unsigned int n; - - /* Special case for odd numbers since they should happen about half the time. */ - if (x & 0x1) { - return 0; - } - - if (x == 0) { - return sizeof(x) << 3; - } - - n = 1; - if ((x & 0x0000FFFF) == 0) { x >>= 16; n += 16; } - if ((x & 0x000000FF) == 0) { x >>= 8; n += 8; } - if ((x & 0x0000000F) == 0) { x >>= 4; n += 4; } - if ((x & 0x00000003) == 0) { x >>= 2; n += 2; } - n -= x & 0x00000001; - - return n; -} - -/* -Pink noise generation based on Tonic (public domain) with modifications. https://github.com/TonicAudio/Tonic/blob/master/src/Tonic/Noise.h - -This is basically _the_ reference for pink noise from what I've found: http://www.firstpr.com.au/dsp/pink-noise/ -*/ -static MA_INLINE float ma_noise_f32_pink(ma_noise* pNoise, ma_uint32 iChannel) -{ - double result; - double binPrev; - double binNext; - unsigned int ibin; - - ibin = ma_tzcnt32(pNoise->state.pink.counter[iChannel]) & (MA_PINK_NOISE_BIN_SIZE - 1); - - binPrev = pNoise->state.pink.bin[iChannel][ibin]; - binNext = ma_lcg_rand_f64(&pNoise->lcg); - pNoise->state.pink.bin[iChannel][ibin] = binNext; - - pNoise->state.pink.accumulation[iChannel] += (binNext - binPrev); - pNoise->state.pink.counter[iChannel] += 1; - - result = (ma_lcg_rand_f64(&pNoise->lcg) + pNoise->state.pink.accumulation[iChannel]); - result /= 10; - - return (float)(result * pNoise->config.amplitude); -} - -static MA_INLINE ma_int16 ma_noise_s16_pink(ma_noise* pNoise, ma_uint32 iChannel) -{ - return ma_pcm_sample_f32_to_s16(ma_noise_f32_pink(pNoise, iChannel)); -} - -static MA_INLINE ma_uint64 ma_noise_read_pcm_frames__pink(ma_noise* pNoise, void* pFramesOut, ma_uint64 frameCount) -{ - ma_uint64 iFrame; - ma_uint32 iChannel; - const ma_uint32 channels = pNoise->config.channels; - MA_ASSUME(channels > 0); - - if (pNoise->config.format == ma_format_f32) { - float* pFramesOutF32 = (float*)pFramesOut; - if (pNoise->config.duplicateChannels) { - for (iFrame = 0; iFrame < frameCount; iFrame += 1) { - float s = ma_noise_f32_pink(pNoise, 0); - for (iChannel = 0; iChannel < channels; iChannel += 1) { - pFramesOutF32[iFrame*channels + iChannel] = s; - } - } - } else { - for (iFrame = 0; iFrame < frameCount; iFrame += 1) { - for (iChannel = 0; iChannel < channels; iChannel += 1) { - pFramesOutF32[iFrame*channels + iChannel] = ma_noise_f32_pink(pNoise, iChannel); - } - } - } - } else if (pNoise->config.format == ma_format_s16) { - ma_int16* pFramesOutS16 = (ma_int16*)pFramesOut; - if (pNoise->config.duplicateChannels) { - for (iFrame = 0; iFrame < frameCount; iFrame += 1) { - ma_int16 s = ma_noise_s16_pink(pNoise, 0); - for (iChannel = 0; iChannel < channels; iChannel += 1) { - pFramesOutS16[iFrame*channels + iChannel] = s; - } - } - } else { - for (iFrame = 0; iFrame < frameCount; iFrame += 1) { - for (iChannel = 0; iChannel < channels; iChannel += 1) { - pFramesOutS16[iFrame*channels + iChannel] = ma_noise_s16_pink(pNoise, iChannel); - } - } - } - } else { - const ma_uint32 bps = ma_get_bytes_per_sample(pNoise->config.format); - const ma_uint32 bpf = bps * channels; - - if (pNoise->config.duplicateChannels) { - for (iFrame = 0; iFrame < frameCount; iFrame += 1) { - float s = ma_noise_f32_pink(pNoise, 0); - for (iChannel = 0; iChannel < channels; iChannel += 1) { - ma_pcm_convert(ma_offset_ptr(pFramesOut, iFrame*bpf + iChannel*bps), pNoise->config.format, &s, ma_format_f32, 1, ma_dither_mode_none); - } - } - } else { - for (iFrame = 0; iFrame < frameCount; iFrame += 1) { - for (iChannel = 0; iChannel < channels; iChannel += 1) { - float s = ma_noise_f32_pink(pNoise, iChannel); - ma_pcm_convert(ma_offset_ptr(pFramesOut, iFrame*bpf + iChannel*bps), pNoise->config.format, &s, ma_format_f32, 1, ma_dither_mode_none); - } - } - } - } - - return frameCount; -} - - -static MA_INLINE float ma_noise_f32_brownian(ma_noise* pNoise, ma_uint32 iChannel) -{ - double result; - - result = (ma_lcg_rand_f64(&pNoise->lcg) + pNoise->state.brownian.accumulation[iChannel]); - result /= 1.005; /* Don't escape the -1..1 range on average. */ - - pNoise->state.brownian.accumulation[iChannel] = result; - result /= 20; - - return (float)(result * pNoise->config.amplitude); -} - -static MA_INLINE ma_int16 ma_noise_s16_brownian(ma_noise* pNoise, ma_uint32 iChannel) -{ - return ma_pcm_sample_f32_to_s16(ma_noise_f32_brownian(pNoise, iChannel)); -} - -static MA_INLINE ma_uint64 ma_noise_read_pcm_frames__brownian(ma_noise* pNoise, void* pFramesOut, ma_uint64 frameCount) -{ - ma_uint64 iFrame; - ma_uint32 iChannel; - const ma_uint32 channels = pNoise->config.channels; - MA_ASSUME(channels > 0); - - if (pNoise->config.format == ma_format_f32) { - float* pFramesOutF32 = (float*)pFramesOut; - if (pNoise->config.duplicateChannels) { - for (iFrame = 0; iFrame < frameCount; iFrame += 1) { - float s = ma_noise_f32_brownian(pNoise, 0); - for (iChannel = 0; iChannel < channels; iChannel += 1) { - pFramesOutF32[iFrame*channels + iChannel] = s; - } - } - } else { - for (iFrame = 0; iFrame < frameCount; iFrame += 1) { - for (iChannel = 0; iChannel < channels; iChannel += 1) { - pFramesOutF32[iFrame*channels + iChannel] = ma_noise_f32_brownian(pNoise, iChannel); - } - } - } - } else if (pNoise->config.format == ma_format_s16) { - ma_int16* pFramesOutS16 = (ma_int16*)pFramesOut; - if (pNoise->config.duplicateChannels) { - for (iFrame = 0; iFrame < frameCount; iFrame += 1) { - ma_int16 s = ma_noise_s16_brownian(pNoise, 0); - for (iChannel = 0; iChannel < channels; iChannel += 1) { - pFramesOutS16[iFrame*channels + iChannel] = s; - } - } - } else { - for (iFrame = 0; iFrame < frameCount; iFrame += 1) { - for (iChannel = 0; iChannel < channels; iChannel += 1) { - pFramesOutS16[iFrame*channels + iChannel] = ma_noise_s16_brownian(pNoise, iChannel); - } - } - } - } else { - const ma_uint32 bps = ma_get_bytes_per_sample(pNoise->config.format); - const ma_uint32 bpf = bps * channels; - - if (pNoise->config.duplicateChannels) { - for (iFrame = 0; iFrame < frameCount; iFrame += 1) { - float s = ma_noise_f32_brownian(pNoise, 0); - for (iChannel = 0; iChannel < channels; iChannel += 1) { - ma_pcm_convert(ma_offset_ptr(pFramesOut, iFrame*bpf + iChannel*bps), pNoise->config.format, &s, ma_format_f32, 1, ma_dither_mode_none); - } - } - } else { - for (iFrame = 0; iFrame < frameCount; iFrame += 1) { - for (iChannel = 0; iChannel < channels; iChannel += 1) { - float s = ma_noise_f32_brownian(pNoise, iChannel); - ma_pcm_convert(ma_offset_ptr(pFramesOut, iFrame*bpf + iChannel*bps), pNoise->config.format, &s, ma_format_f32, 1, ma_dither_mode_none); - } - } - } - } - - return frameCount; -} - -MA_API ma_result ma_noise_read_pcm_frames(ma_noise* pNoise, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead) -{ - ma_uint64 framesRead = 0; - - if (pFramesRead != NULL) { - *pFramesRead = 0; - } - - if (frameCount == 0) { - return MA_INVALID_ARGS; - } - - if (pNoise == NULL) { - return MA_INVALID_ARGS; - } - - /* The output buffer is allowed to be NULL. Since we aren't tracking cursors or anything we can just do nothing and pretend to be successful. */ - if (pFramesOut == NULL) { - framesRead = frameCount; - } else { - switch (pNoise->config.type) { - case ma_noise_type_white: framesRead = ma_noise_read_pcm_frames__white (pNoise, pFramesOut, frameCount); break; - case ma_noise_type_pink: framesRead = ma_noise_read_pcm_frames__pink (pNoise, pFramesOut, frameCount); break; - case ma_noise_type_brownian: framesRead = ma_noise_read_pcm_frames__brownian(pNoise, pFramesOut, frameCount); break; - default: return MA_INVALID_OPERATION; /* Unknown noise type. */ - } - } - - if (pFramesRead != NULL) { - *pFramesRead = framesRead; - } - - return MA_SUCCESS; -} -#endif /* MA_NO_GENERATION */ - - - -#ifndef MA_NO_RESOURCE_MANAGER -#ifndef MA_RESOURCE_MANAGER_PAGE_SIZE_IN_MILLISECONDS -#define MA_RESOURCE_MANAGER_PAGE_SIZE_IN_MILLISECONDS 1000 -#endif - -#ifndef MA_JOB_TYPE_RESOURCE_MANAGER_QUEUE_CAPACITY -#define MA_JOB_TYPE_RESOURCE_MANAGER_QUEUE_CAPACITY 1024 -#endif - -MA_API ma_resource_manager_pipeline_notifications ma_resource_manager_pipeline_notifications_init(void) -{ - ma_resource_manager_pipeline_notifications notifications; - - MA_ZERO_OBJECT(¬ifications); - - return notifications; -} - -static void ma_resource_manager_pipeline_notifications_signal_all_notifications(const ma_resource_manager_pipeline_notifications* pPipelineNotifications) -{ - if (pPipelineNotifications == NULL) { - return; - } - - if (pPipelineNotifications->init.pNotification) { ma_async_notification_signal(pPipelineNotifications->init.pNotification); } - if (pPipelineNotifications->done.pNotification) { ma_async_notification_signal(pPipelineNotifications->done.pNotification); } -} - -static void ma_resource_manager_pipeline_notifications_acquire_all_fences(const ma_resource_manager_pipeline_notifications* pPipelineNotifications) -{ - if (pPipelineNotifications == NULL) { - return; - } - - if (pPipelineNotifications->init.pFence != NULL) { ma_fence_acquire(pPipelineNotifications->init.pFence); } - if (pPipelineNotifications->done.pFence != NULL) { ma_fence_acquire(pPipelineNotifications->done.pFence); } -} - -static void ma_resource_manager_pipeline_notifications_release_all_fences(const ma_resource_manager_pipeline_notifications* pPipelineNotifications) -{ - if (pPipelineNotifications == NULL) { - return; - } - - if (pPipelineNotifications->init.pFence != NULL) { ma_fence_release(pPipelineNotifications->init.pFence); } - if (pPipelineNotifications->done.pFence != NULL) { ma_fence_release(pPipelineNotifications->done.pFence); } -} - - - -#ifndef MA_DEFAULT_HASH_SEED -#define MA_DEFAULT_HASH_SEED 42 -#endif - -/* MurmurHash3. Based on code from https://github.com/PeterScott/murmur3/blob/master/murmur3.c (public domain). */ -#if defined(__clang__) || (defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6))) - #pragma GCC diagnostic push - #if __GNUC__ >= 7 - #pragma GCC diagnostic ignored "-Wimplicit-fallthrough" - #endif -#endif - -static MA_INLINE ma_uint32 ma_rotl32(ma_uint32 x, ma_int8 r) -{ - return (x << r) | (x >> (32 - r)); -} - -static MA_INLINE ma_uint32 ma_hash_getblock(const ma_uint32* blocks, int i) -{ - ma_uint32 block; - - /* Try silencing a sanitization warning about unaligned access by doing a memcpy() instead of assignment. */ - MA_COPY_MEMORY(&block, ma_offset_ptr(blocks, i * sizeof(block)), sizeof(block)); - - if (ma_is_little_endian()) { - return block; - } else { - return ma_swap_endian_uint32(block); - } -} - -static MA_INLINE ma_uint32 ma_hash_fmix32(ma_uint32 h) -{ - h ^= h >> 16; - h *= 0x85ebca6b; - h ^= h >> 13; - h *= 0xc2b2ae35; - h ^= h >> 16; - - return h; -} - -static ma_uint32 ma_hash_32(const void* key, int len, ma_uint32 seed) -{ - const ma_uint8* data = (const ma_uint8*)key; - const ma_uint32* blocks; - const ma_uint8* tail; - const int nblocks = len / 4; - ma_uint32 h1 = seed; - ma_uint32 c1 = 0xcc9e2d51; - ma_uint32 c2 = 0x1b873593; - ma_uint32 k1; - int i; - - blocks = (const ma_uint32 *)(data + nblocks*4); - - for(i = -nblocks; i; i++) { - k1 = ma_hash_getblock(blocks,i); - - k1 *= c1; - k1 = ma_rotl32(k1, 15); - k1 *= c2; - - h1 ^= k1; - h1 = ma_rotl32(h1, 13); - h1 = h1*5 + 0xe6546b64; - } - - - tail = (const ma_uint8*)(data + nblocks*4); - - k1 = 0; - switch(len & 3) { - case 3: k1 ^= tail[2] << 16; - case 2: k1 ^= tail[1] << 8; - case 1: k1 ^= tail[0]; - k1 *= c1; k1 = ma_rotl32(k1, 15); k1 *= c2; h1 ^= k1; - }; - - - h1 ^= len; - h1 = ma_hash_fmix32(h1); - - return h1; -} - -#if defined(__clang__) || (defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6))) - #pragma GCC diagnostic push -#endif -/* End MurmurHash3 */ - -static ma_uint32 ma_hash_string_32(const char* str) -{ - return ma_hash_32(str, (int)strlen(str), MA_DEFAULT_HASH_SEED); -} - -static ma_uint32 ma_hash_string_w_32(const wchar_t* str) -{ - return ma_hash_32(str, (int)wcslen(str) * sizeof(*str), MA_DEFAULT_HASH_SEED); -} - - - - -/* -Basic BST Functions -*/ -static ma_result ma_resource_manager_data_buffer_node_search(ma_resource_manager* pResourceManager, ma_uint32 hashedName32, ma_resource_manager_data_buffer_node** ppDataBufferNode) -{ - ma_resource_manager_data_buffer_node* pCurrentNode; - - MA_ASSERT(pResourceManager != NULL); - MA_ASSERT(ppDataBufferNode != NULL); - - pCurrentNode = pResourceManager->pRootDataBufferNode; - while (pCurrentNode != NULL) { - if (hashedName32 == pCurrentNode->hashedName32) { - break; /* Found. */ - } else if (hashedName32 < pCurrentNode->hashedName32) { - pCurrentNode = pCurrentNode->pChildLo; - } else { - pCurrentNode = pCurrentNode->pChildHi; - } - } - - *ppDataBufferNode = pCurrentNode; - - if (pCurrentNode == NULL) { - return MA_DOES_NOT_EXIST; - } else { - return MA_SUCCESS; - } -} - -static ma_result ma_resource_manager_data_buffer_node_insert_point(ma_resource_manager* pResourceManager, ma_uint32 hashedName32, ma_resource_manager_data_buffer_node** ppInsertPoint) -{ - ma_result result = MA_SUCCESS; - ma_resource_manager_data_buffer_node* pCurrentNode; - - MA_ASSERT(pResourceManager != NULL); - MA_ASSERT(ppInsertPoint != NULL); - - *ppInsertPoint = NULL; - - if (pResourceManager->pRootDataBufferNode == NULL) { - return MA_SUCCESS; /* No items. */ - } - - /* We need to find the node that will become the parent of the new node. If a node is found that already has the same hashed name we need to return MA_ALREADY_EXISTS. */ - pCurrentNode = pResourceManager->pRootDataBufferNode; - while (pCurrentNode != NULL) { - if (hashedName32 == pCurrentNode->hashedName32) { - result = MA_ALREADY_EXISTS; - break; - } else { - if (hashedName32 < pCurrentNode->hashedName32) { - if (pCurrentNode->pChildLo == NULL) { - result = MA_SUCCESS; - break; - } else { - pCurrentNode = pCurrentNode->pChildLo; - } - } else { - if (pCurrentNode->pChildHi == NULL) { - result = MA_SUCCESS; - break; - } else { - pCurrentNode = pCurrentNode->pChildHi; - } - } - } - } - - *ppInsertPoint = pCurrentNode; - return result; -} - -static ma_result ma_resource_manager_data_buffer_node_insert_at(ma_resource_manager* pResourceManager, ma_resource_manager_data_buffer_node* pDataBufferNode, ma_resource_manager_data_buffer_node* pInsertPoint) -{ - MA_ASSERT(pResourceManager != NULL); - MA_ASSERT(pDataBufferNode != NULL); - - /* The key must have been set before calling this function. */ - MA_ASSERT(pDataBufferNode->hashedName32 != 0); - - if (pInsertPoint == NULL) { - /* It's the first node. */ - pResourceManager->pRootDataBufferNode = pDataBufferNode; - } else { - /* It's not the first node. It needs to be inserted. */ - if (pDataBufferNode->hashedName32 < pInsertPoint->hashedName32) { - MA_ASSERT(pInsertPoint->pChildLo == NULL); - pInsertPoint->pChildLo = pDataBufferNode; - } else { - MA_ASSERT(pInsertPoint->pChildHi == NULL); - pInsertPoint->pChildHi = pDataBufferNode; - } - } - - pDataBufferNode->pParent = pInsertPoint; - - return MA_SUCCESS; -} - -#if 0 /* Unused for now. */ -static ma_result ma_resource_manager_data_buffer_node_insert(ma_resource_manager* pResourceManager, ma_resource_manager_data_buffer_node* pDataBufferNode) -{ - ma_result result; - ma_resource_manager_data_buffer_node* pInsertPoint; - - MA_ASSERT(pResourceManager != NULL); - MA_ASSERT(pDataBufferNode != NULL); - - result = ma_resource_manager_data_buffer_node_insert_point(pResourceManager, pDataBufferNode->hashedName32, &pInsertPoint); - if (result != MA_SUCCESS) { - return MA_INVALID_ARGS; - } - - return ma_resource_manager_data_buffer_node_insert_at(pResourceManager, pDataBufferNode, pInsertPoint); -} -#endif - -static MA_INLINE ma_resource_manager_data_buffer_node* ma_resource_manager_data_buffer_node_find_min(ma_resource_manager_data_buffer_node* pDataBufferNode) -{ - ma_resource_manager_data_buffer_node* pCurrentNode; - - MA_ASSERT(pDataBufferNode != NULL); - - pCurrentNode = pDataBufferNode; - while (pCurrentNode->pChildLo != NULL) { - pCurrentNode = pCurrentNode->pChildLo; - } - - return pCurrentNode; -} - -static MA_INLINE ma_resource_manager_data_buffer_node* ma_resource_manager_data_buffer_node_find_max(ma_resource_manager_data_buffer_node* pDataBufferNode) -{ - ma_resource_manager_data_buffer_node* pCurrentNode; - - MA_ASSERT(pDataBufferNode != NULL); - - pCurrentNode = pDataBufferNode; - while (pCurrentNode->pChildHi != NULL) { - pCurrentNode = pCurrentNode->pChildHi; - } - - return pCurrentNode; -} - -static MA_INLINE ma_resource_manager_data_buffer_node* ma_resource_manager_data_buffer_node_find_inorder_successor(ma_resource_manager_data_buffer_node* pDataBufferNode) -{ - MA_ASSERT(pDataBufferNode != NULL); - MA_ASSERT(pDataBufferNode->pChildHi != NULL); - - return ma_resource_manager_data_buffer_node_find_min(pDataBufferNode->pChildHi); -} - -static MA_INLINE ma_resource_manager_data_buffer_node* ma_resource_manager_data_buffer_node_find_inorder_predecessor(ma_resource_manager_data_buffer_node* pDataBufferNode) -{ - MA_ASSERT(pDataBufferNode != NULL); - MA_ASSERT(pDataBufferNode->pChildLo != NULL); - - return ma_resource_manager_data_buffer_node_find_max(pDataBufferNode->pChildLo); -} - -static ma_result ma_resource_manager_data_buffer_node_remove(ma_resource_manager* pResourceManager, ma_resource_manager_data_buffer_node* pDataBufferNode) -{ - MA_ASSERT(pResourceManager != NULL); - MA_ASSERT(pDataBufferNode != NULL); - - if (pDataBufferNode->pChildLo == NULL) { - if (pDataBufferNode->pChildHi == NULL) { - /* Simple case - deleting a buffer with no children. */ - if (pDataBufferNode->pParent == NULL) { - MA_ASSERT(pResourceManager->pRootDataBufferNode == pDataBufferNode); /* There is only a single buffer in the tree which should be equal to the root node. */ - pResourceManager->pRootDataBufferNode = NULL; - } else { - if (pDataBufferNode->pParent->pChildLo == pDataBufferNode) { - pDataBufferNode->pParent->pChildLo = NULL; - } else { - pDataBufferNode->pParent->pChildHi = NULL; - } - } - } else { - /* Node has one child - pChildHi != NULL. */ - pDataBufferNode->pChildHi->pParent = pDataBufferNode->pParent; - - if (pDataBufferNode->pParent == NULL) { - MA_ASSERT(pResourceManager->pRootDataBufferNode == pDataBufferNode); - pResourceManager->pRootDataBufferNode = pDataBufferNode->pChildHi; - } else { - if (pDataBufferNode->pParent->pChildLo == pDataBufferNode) { - pDataBufferNode->pParent->pChildLo = pDataBufferNode->pChildHi; - } else { - pDataBufferNode->pParent->pChildHi = pDataBufferNode->pChildHi; - } - } - } - } else { - if (pDataBufferNode->pChildHi == NULL) { - /* Node has one child - pChildLo != NULL. */ - pDataBufferNode->pChildLo->pParent = pDataBufferNode->pParent; - - if (pDataBufferNode->pParent == NULL) { - MA_ASSERT(pResourceManager->pRootDataBufferNode == pDataBufferNode); - pResourceManager->pRootDataBufferNode = pDataBufferNode->pChildLo; - } else { - if (pDataBufferNode->pParent->pChildLo == pDataBufferNode) { - pDataBufferNode->pParent->pChildLo = pDataBufferNode->pChildLo; - } else { - pDataBufferNode->pParent->pChildHi = pDataBufferNode->pChildLo; - } - } - } else { - /* Complex case - deleting a node with two children. */ - ma_resource_manager_data_buffer_node* pReplacementDataBufferNode; - - /* For now we are just going to use the in-order successor as the replacement, but we may want to try to keep this balanced by switching between the two. */ - pReplacementDataBufferNode = ma_resource_manager_data_buffer_node_find_inorder_successor(pDataBufferNode); - MA_ASSERT(pReplacementDataBufferNode != NULL); - - /* - Now that we have our replacement node we can make the change. The simple way to do this would be to just exchange the values, and then remove the replacement - node, however we track specific nodes via pointers which means we can't just swap out the values. We need to instead just change the pointers around. The - replacement node should have at most 1 child. Therefore, we can detach it in terms of our simpler cases above. What we're essentially doing is detaching the - replacement node and reinserting it into the same position as the deleted node. - */ - MA_ASSERT(pReplacementDataBufferNode->pParent != NULL); /* The replacement node should never be the root which means it should always have a parent. */ - MA_ASSERT(pReplacementDataBufferNode->pChildLo == NULL); /* Because we used in-order successor. This would be pChildHi == NULL if we used in-order predecessor. */ - - if (pReplacementDataBufferNode->pChildHi == NULL) { - if (pReplacementDataBufferNode->pParent->pChildLo == pReplacementDataBufferNode) { - pReplacementDataBufferNode->pParent->pChildLo = NULL; - } else { - pReplacementDataBufferNode->pParent->pChildHi = NULL; - } - } else { - pReplacementDataBufferNode->pChildHi->pParent = pReplacementDataBufferNode->pParent; - if (pReplacementDataBufferNode->pParent->pChildLo == pReplacementDataBufferNode) { - pReplacementDataBufferNode->pParent->pChildLo = pReplacementDataBufferNode->pChildHi; - } else { - pReplacementDataBufferNode->pParent->pChildHi = pReplacementDataBufferNode->pChildHi; - } - } - - - /* The replacement node has essentially been detached from the binary tree, so now we need to replace the old data buffer with it. The first thing to update is the parent */ - if (pDataBufferNode->pParent != NULL) { - if (pDataBufferNode->pParent->pChildLo == pDataBufferNode) { - pDataBufferNode->pParent->pChildLo = pReplacementDataBufferNode; - } else { - pDataBufferNode->pParent->pChildHi = pReplacementDataBufferNode; - } - } - - /* Now need to update the replacement node's pointers. */ - pReplacementDataBufferNode->pParent = pDataBufferNode->pParent; - pReplacementDataBufferNode->pChildLo = pDataBufferNode->pChildLo; - pReplacementDataBufferNode->pChildHi = pDataBufferNode->pChildHi; - - /* Now the children of the replacement node need to have their parent pointers updated. */ - if (pReplacementDataBufferNode->pChildLo != NULL) { - pReplacementDataBufferNode->pChildLo->pParent = pReplacementDataBufferNode; - } - if (pReplacementDataBufferNode->pChildHi != NULL) { - pReplacementDataBufferNode->pChildHi->pParent = pReplacementDataBufferNode; - } - - /* Now the root node needs to be updated. */ - if (pResourceManager->pRootDataBufferNode == pDataBufferNode) { - pResourceManager->pRootDataBufferNode = pReplacementDataBufferNode; - } - } - } - - return MA_SUCCESS; -} - -#if 0 /* Unused for now. */ -static ma_result ma_resource_manager_data_buffer_node_remove_by_key(ma_resource_manager* pResourceManager, ma_uint32 hashedName32) -{ - ma_result result; - ma_resource_manager_data_buffer_node* pDataBufferNode; - - result = ma_resource_manager_data_buffer_search(pResourceManager, hashedName32, &pDataBufferNode); - if (result != MA_SUCCESS) { - return result; /* Could not find the data buffer. */ - } - - return ma_resource_manager_data_buffer_remove(pResourceManager, pDataBufferNode); -} -#endif - -static ma_resource_manager_data_supply_type ma_resource_manager_data_buffer_node_get_data_supply_type(ma_resource_manager_data_buffer_node* pDataBufferNode) -{ - return (ma_resource_manager_data_supply_type)ma_atomic_load_i32(&pDataBufferNode->data.type); -} - -static void ma_resource_manager_data_buffer_node_set_data_supply_type(ma_resource_manager_data_buffer_node* pDataBufferNode, ma_resource_manager_data_supply_type supplyType) -{ - ma_atomic_exchange_i32(&pDataBufferNode->data.type, supplyType); -} - -static ma_result ma_resource_manager_data_buffer_node_increment_ref(ma_resource_manager* pResourceManager, ma_resource_manager_data_buffer_node* pDataBufferNode, ma_uint32* pNewRefCount) -{ - ma_uint32 refCount; - - MA_ASSERT(pResourceManager != NULL); - MA_ASSERT(pDataBufferNode != NULL); - - (void)pResourceManager; - - refCount = ma_atomic_fetch_add_32(&pDataBufferNode->refCount, 1) + 1; - - if (pNewRefCount != NULL) { - *pNewRefCount = refCount; - } - - return MA_SUCCESS; -} - -static ma_result ma_resource_manager_data_buffer_node_decrement_ref(ma_resource_manager* pResourceManager, ma_resource_manager_data_buffer_node* pDataBufferNode, ma_uint32* pNewRefCount) -{ - ma_uint32 refCount; - - MA_ASSERT(pResourceManager != NULL); - MA_ASSERT(pDataBufferNode != NULL); - - (void)pResourceManager; - - refCount = ma_atomic_fetch_sub_32(&pDataBufferNode->refCount, 1) - 1; - - if (pNewRefCount != NULL) { - *pNewRefCount = refCount; - } - - return MA_SUCCESS; -} - -static void ma_resource_manager_data_buffer_node_free(ma_resource_manager* pResourceManager, ma_resource_manager_data_buffer_node* pDataBufferNode) -{ - MA_ASSERT(pResourceManager != NULL); - MA_ASSERT(pDataBufferNode != NULL); - - if (pDataBufferNode->isDataOwnedByResourceManager) { - if (ma_resource_manager_data_buffer_node_get_data_supply_type(pDataBufferNode) == ma_resource_manager_data_supply_type_encoded) { - ma_free((void*)pDataBufferNode->data.backend.encoded.pData, &pResourceManager->config.allocationCallbacks); - pDataBufferNode->data.backend.encoded.pData = NULL; - pDataBufferNode->data.backend.encoded.sizeInBytes = 0; - } else if (ma_resource_manager_data_buffer_node_get_data_supply_type(pDataBufferNode) == ma_resource_manager_data_supply_type_decoded) { - ma_free((void*)pDataBufferNode->data.backend.decoded.pData, &pResourceManager->config.allocationCallbacks); - pDataBufferNode->data.backend.decoded.pData = NULL; - pDataBufferNode->data.backend.decoded.totalFrameCount = 0; - } else if (ma_resource_manager_data_buffer_node_get_data_supply_type(pDataBufferNode) == ma_resource_manager_data_supply_type_decoded_paged) { - ma_paged_audio_buffer_data_uninit(&pDataBufferNode->data.backend.decodedPaged.data, &pResourceManager->config.allocationCallbacks); - } else { - /* Should never hit this if the node was successfully initialized. */ - MA_ASSERT(pDataBufferNode->result != MA_SUCCESS); - } - } - - /* The data buffer itself needs to be freed. */ - ma_free(pDataBufferNode, &pResourceManager->config.allocationCallbacks); -} - -static ma_result ma_resource_manager_data_buffer_node_result(const ma_resource_manager_data_buffer_node* pDataBufferNode) -{ - MA_ASSERT(pDataBufferNode != NULL); - - return (ma_result)ma_atomic_load_i32((ma_result*)&pDataBufferNode->result); /* Need a naughty const-cast here. */ -} - - -static ma_bool32 ma_resource_manager_is_threading_enabled(const ma_resource_manager* pResourceManager) -{ - MA_ASSERT(pResourceManager != NULL); - - return (pResourceManager->config.flags & MA_RESOURCE_MANAGER_FLAG_NO_THREADING) == 0; -} - - -typedef struct -{ - union - { - ma_async_notification_event e; - ma_async_notification_poll p; - } backend; /* Must be the first member. */ - ma_resource_manager* pResourceManager; -} ma_resource_manager_inline_notification; - -static ma_result ma_resource_manager_inline_notification_init(ma_resource_manager* pResourceManager, ma_resource_manager_inline_notification* pNotification) -{ - MA_ASSERT(pResourceManager != NULL); - MA_ASSERT(pNotification != NULL); - - pNotification->pResourceManager = pResourceManager; - - if (ma_resource_manager_is_threading_enabled(pResourceManager)) { - return ma_async_notification_event_init(&pNotification->backend.e); - } else { - return ma_async_notification_poll_init(&pNotification->backend.p); - } -} - -static void ma_resource_manager_inline_notification_uninit(ma_resource_manager_inline_notification* pNotification) -{ - MA_ASSERT(pNotification != NULL); - - if (ma_resource_manager_is_threading_enabled(pNotification->pResourceManager)) { - ma_async_notification_event_uninit(&pNotification->backend.e); - } else { - /* No need to uninitialize a polling notification. */ - } -} - -static void ma_resource_manager_inline_notification_wait(ma_resource_manager_inline_notification* pNotification) -{ - MA_ASSERT(pNotification != NULL); - - if (ma_resource_manager_is_threading_enabled(pNotification->pResourceManager)) { - ma_async_notification_event_wait(&pNotification->backend.e); - } else { - while (ma_async_notification_poll_is_signalled(&pNotification->backend.p) == MA_FALSE) { - ma_result result = ma_resource_manager_process_next_job(pNotification->pResourceManager); - if (result == MA_NO_DATA_AVAILABLE || result == MA_CANCELLED) { - break; - } - } - } -} - -static void ma_resource_manager_inline_notification_wait_and_uninit(ma_resource_manager_inline_notification* pNotification) -{ - ma_resource_manager_inline_notification_wait(pNotification); - ma_resource_manager_inline_notification_uninit(pNotification); -} - - -static void ma_resource_manager_data_buffer_bst_lock(ma_resource_manager* pResourceManager) -{ - MA_ASSERT(pResourceManager != NULL); - - if (ma_resource_manager_is_threading_enabled(pResourceManager)) { - #ifndef MA_NO_THREADING - { - ma_mutex_lock(&pResourceManager->dataBufferBSTLock); - } - #else - { - MA_ASSERT(MA_FALSE); /* Should never hit this. */ - } - #endif - } else { - /* Threading not enabled. Do nothing. */ - } -} - -static void ma_resource_manager_data_buffer_bst_unlock(ma_resource_manager* pResourceManager) -{ - MA_ASSERT(pResourceManager != NULL); - - if (ma_resource_manager_is_threading_enabled(pResourceManager)) { - #ifndef MA_NO_THREADING - { - ma_mutex_unlock(&pResourceManager->dataBufferBSTLock); - } - #else - { - MA_ASSERT(MA_FALSE); /* Should never hit this. */ - } - #endif - } else { - /* Threading not enabled. Do nothing. */ - } -} - -#ifndef MA_NO_THREADING -static ma_thread_result MA_THREADCALL ma_resource_manager_job_thread(void* pUserData) -{ - ma_resource_manager* pResourceManager = (ma_resource_manager*)pUserData; - MA_ASSERT(pResourceManager != NULL); - - for (;;) { - ma_result result; - ma_job job; - - result = ma_resource_manager_next_job(pResourceManager, &job); - if (result != MA_SUCCESS) { - break; - } - - /* Terminate if we got a quit message. */ - if (job.toc.breakup.code == MA_JOB_TYPE_QUIT) { - break; - } - - ma_job_process(&job); - } - - return (ma_thread_result)0; -} -#endif - -MA_API ma_resource_manager_config ma_resource_manager_config_init(void) -{ - ma_resource_manager_config config; - - MA_ZERO_OBJECT(&config); - config.decodedFormat = ma_format_unknown; - config.decodedChannels = 0; - config.decodedSampleRate = 0; - config.jobThreadCount = 1; /* A single miniaudio-managed job thread by default. */ - config.jobQueueCapacity = MA_JOB_TYPE_RESOURCE_MANAGER_QUEUE_CAPACITY; - - /* Flags. */ - config.flags = 0; - #ifdef MA_NO_THREADING - { - /* Threading is disabled at compile time so disable threading at runtime as well by default. */ - config.flags |= MA_RESOURCE_MANAGER_FLAG_NO_THREADING; - config.jobThreadCount = 0; - } - #endif - - return config; -} - - -MA_API ma_result ma_resource_manager_init(const ma_resource_manager_config* pConfig, ma_resource_manager* pResourceManager) -{ - ma_result result; - ma_job_queue_config jobQueueConfig; - - if (pResourceManager == NULL) { - return MA_INVALID_ARGS; - } - - MA_ZERO_OBJECT(pResourceManager); - - if (pConfig == NULL) { - return MA_INVALID_ARGS; - } - - #ifndef MA_NO_THREADING - { - if (pConfig->jobThreadCount > ma_countof(pResourceManager->jobThreads)) { - return MA_INVALID_ARGS; /* Requesting too many job threads. */ - } - } - #endif - - pResourceManager->config = *pConfig; - ma_allocation_callbacks_init_copy(&pResourceManager->config.allocationCallbacks, &pConfig->allocationCallbacks); - - /* Get the log set up early so we can start using it as soon as possible. */ - if (pResourceManager->config.pLog == NULL) { - result = ma_log_init(&pResourceManager->config.allocationCallbacks, &pResourceManager->log); - if (result == MA_SUCCESS) { - pResourceManager->config.pLog = &pResourceManager->log; - } else { - pResourceManager->config.pLog = NULL; /* Logging is unavailable. */ - } - } - - if (pResourceManager->config.pVFS == NULL) { - result = ma_default_vfs_init(&pResourceManager->defaultVFS, &pResourceManager->config.allocationCallbacks); - if (result != MA_SUCCESS) { - return result; /* Failed to initialize the default file system. */ - } - - pResourceManager->config.pVFS = &pResourceManager->defaultVFS; - } - - /* If threading has been disabled at compile time, enforce it at run time as well. */ - #ifdef MA_NO_THREADING - { - pResourceManager->config.flags |= MA_RESOURCE_MANAGER_FLAG_NO_THREADING; - } - #endif - - /* We need to force MA_RESOURCE_MANAGER_FLAG_NON_BLOCKING if MA_RESOURCE_MANAGER_FLAG_NO_THREADING is set. */ - if ((pResourceManager->config.flags & MA_RESOURCE_MANAGER_FLAG_NO_THREADING) != 0) { - pResourceManager->config.flags |= MA_RESOURCE_MANAGER_FLAG_NON_BLOCKING; - - /* We cannot allow job threads when MA_RESOURCE_MANAGER_FLAG_NO_THREADING has been set. This is an invalid use case. */ - if (pResourceManager->config.jobThreadCount > 0) { - return MA_INVALID_ARGS; - } - } - - /* Job queue. */ - jobQueueConfig.capacity = pResourceManager->config.jobQueueCapacity; - jobQueueConfig.flags = 0; - if ((pResourceManager->config.flags & MA_RESOURCE_MANAGER_FLAG_NON_BLOCKING) != 0) { - if (pResourceManager->config.jobThreadCount > 0) { - return MA_INVALID_ARGS; /* Non-blocking mode is only valid for self-managed job threads. */ - } - - jobQueueConfig.flags |= MA_JOB_QUEUE_FLAG_NON_BLOCKING; - } - - result = ma_job_queue_init(&jobQueueConfig, &pResourceManager->config.allocationCallbacks, &pResourceManager->jobQueue); - if (result != MA_SUCCESS) { - return result; - } - - - /* Custom decoding backends. */ - if (pConfig->ppCustomDecodingBackendVTables != NULL && pConfig->customDecodingBackendCount > 0) { - size_t sizeInBytes = sizeof(*pResourceManager->config.ppCustomDecodingBackendVTables) * pConfig->customDecodingBackendCount; - ma_decoding_backend_vtable** ppCustomDecodingBackendVTables; - - ppCustomDecodingBackendVTables = (ma_decoding_backend_vtable**)ma_malloc(sizeInBytes, &pResourceManager->config.allocationCallbacks); - if (pResourceManager->config.ppCustomDecodingBackendVTables == NULL) { - ma_job_queue_uninit(&pResourceManager->jobQueue, &pResourceManager->config.allocationCallbacks); - return MA_OUT_OF_MEMORY; - } - - MA_COPY_MEMORY(ppCustomDecodingBackendVTables, pConfig->ppCustomDecodingBackendVTables, sizeInBytes); - - pResourceManager->config.ppCustomDecodingBackendVTables = ppCustomDecodingBackendVTables; - pResourceManager->config.customDecodingBackendCount = pConfig->customDecodingBackendCount; - pResourceManager->config.pCustomDecodingBackendUserData = pConfig->pCustomDecodingBackendUserData; - } - - - - /* Here is where we initialize our threading stuff. We don't do this if we don't support threading. */ - if (ma_resource_manager_is_threading_enabled(pResourceManager)) { - #ifndef MA_NO_THREADING - { - ma_uint32 iJobThread; - - /* Data buffer lock. */ - result = ma_mutex_init(&pResourceManager->dataBufferBSTLock); - if (result != MA_SUCCESS) { - ma_job_queue_uninit(&pResourceManager->jobQueue, &pResourceManager->config.allocationCallbacks); - return result; - } - - /* Create the job threads last to ensure the threads has access to valid data. */ - for (iJobThread = 0; iJobThread < pResourceManager->config.jobThreadCount; iJobThread += 1) { - result = ma_thread_create(&pResourceManager->jobThreads[iJobThread], ma_thread_priority_normal, pResourceManager->config.jobThreadStackSize, ma_resource_manager_job_thread, pResourceManager, &pResourceManager->config.allocationCallbacks); - if (result != MA_SUCCESS) { - ma_mutex_uninit(&pResourceManager->dataBufferBSTLock); - ma_job_queue_uninit(&pResourceManager->jobQueue, &pResourceManager->config.allocationCallbacks); - return result; - } - } - } - #else - { - /* Threading is disabled at compile time. We should never get here because validation checks should have already been performed. */ - MA_ASSERT(MA_FALSE); - } - #endif - } - - return MA_SUCCESS; -} - - -static void ma_resource_manager_delete_all_data_buffer_nodes(ma_resource_manager* pResourceManager) -{ - MA_ASSERT(pResourceManager); - - /* If everything was done properly, there shouldn't be any active data buffers. */ - while (pResourceManager->pRootDataBufferNode != NULL) { - ma_resource_manager_data_buffer_node* pDataBufferNode = pResourceManager->pRootDataBufferNode; - ma_resource_manager_data_buffer_node_remove(pResourceManager, pDataBufferNode); - - /* The data buffer has been removed from the BST, so now we need to free its data. */ - ma_resource_manager_data_buffer_node_free(pResourceManager, pDataBufferNode); - } -} - -MA_API void ma_resource_manager_uninit(ma_resource_manager* pResourceManager) -{ - if (pResourceManager == NULL) { - return; - } - - /* - Job threads need to be killed first. To do this we need to post a quit message to the message queue and then wait for the thread. The quit message will never be removed from the - queue which means it will never not be returned after being encountered for the first time which means all threads will eventually receive it. - */ - ma_resource_manager_post_job_quit(pResourceManager); - - /* Wait for every job to finish before continuing to ensure nothing is sill trying to access any of our objects below. */ - if (ma_resource_manager_is_threading_enabled(pResourceManager)) { - #ifndef MA_NO_THREADING - { - ma_uint32 iJobThread; - - for (iJobThread = 0; iJobThread < pResourceManager->config.jobThreadCount; iJobThread += 1) { - ma_thread_wait(&pResourceManager->jobThreads[iJobThread]); - } - } - #else - { - MA_ASSERT(MA_FALSE); /* Should never hit this. */ - } - #endif - } - - /* At this point the thread should have returned and no other thread should be accessing our data. We can now delete all data buffers. */ - ma_resource_manager_delete_all_data_buffer_nodes(pResourceManager); - - /* The job queue is no longer needed. */ - ma_job_queue_uninit(&pResourceManager->jobQueue, &pResourceManager->config.allocationCallbacks); - - /* We're no longer doing anything with data buffers so the lock can now be uninitialized. */ - if (ma_resource_manager_is_threading_enabled(pResourceManager)) { - #ifndef MA_NO_THREADING - { - ma_mutex_uninit(&pResourceManager->dataBufferBSTLock); - } - #else - { - MA_ASSERT(MA_FALSE); /* Should never hit this. */ - } - #endif - } - - ma_free((ma_decoding_backend_vtable**)pResourceManager->config.ppCustomDecodingBackendVTables, &pResourceManager->config.allocationCallbacks); /* <-- Naughty const-cast, but this is safe. */ - - if (pResourceManager->config.pLog == &pResourceManager->log) { - ma_log_uninit(&pResourceManager->log); - } -} - -MA_API ma_log* ma_resource_manager_get_log(ma_resource_manager* pResourceManager) -{ - if (pResourceManager == NULL) { - return NULL; - } - - return pResourceManager->config.pLog; -} - - - -MA_API ma_resource_manager_data_source_config ma_resource_manager_data_source_config_init(void) -{ - ma_resource_manager_data_source_config config; - - MA_ZERO_OBJECT(&config); - config.rangeBegInPCMFrames = MA_DATA_SOURCE_DEFAULT_RANGE_BEG; - config.rangeEndInPCMFrames = MA_DATA_SOURCE_DEFAULT_RANGE_END; - config.loopPointBegInPCMFrames = MA_DATA_SOURCE_DEFAULT_LOOP_POINT_BEG; - config.loopPointEndInPCMFrames = MA_DATA_SOURCE_DEFAULT_LOOP_POINT_END; - config.isLooping = MA_FALSE; - - return config; -} - - -static ma_decoder_config ma_resource_manager__init_decoder_config(ma_resource_manager* pResourceManager) -{ - ma_decoder_config config; - - config = ma_decoder_config_init(pResourceManager->config.decodedFormat, pResourceManager->config.decodedChannels, pResourceManager->config.decodedSampleRate); - config.allocationCallbacks = pResourceManager->config.allocationCallbacks; - config.ppCustomBackendVTables = pResourceManager->config.ppCustomDecodingBackendVTables; - config.customBackendCount = pResourceManager->config.customDecodingBackendCount; - config.pCustomBackendUserData = pResourceManager->config.pCustomDecodingBackendUserData; - - return config; -} - -static ma_result ma_resource_manager__init_decoder(ma_resource_manager* pResourceManager, const char* pFilePath, const wchar_t* pFilePathW, ma_decoder* pDecoder) -{ - ma_result result; - ma_decoder_config config; - - MA_ASSERT(pResourceManager != NULL); - MA_ASSERT(pFilePath != NULL || pFilePathW != NULL); - MA_ASSERT(pDecoder != NULL); - - config = ma_resource_manager__init_decoder_config(pResourceManager); - - if (pFilePath != NULL) { - result = ma_decoder_init_vfs(pResourceManager->config.pVFS, pFilePath, &config, pDecoder); - if (result != MA_SUCCESS) { - ma_log_postf(ma_resource_manager_get_log(pResourceManager), MA_LOG_LEVEL_WARNING, "Failed to load file \"%s\". %s.\n", pFilePath, ma_result_description(result)); - return result; - } - } else { - result = ma_decoder_init_vfs_w(pResourceManager->config.pVFS, pFilePathW, &config, pDecoder); - if (result != MA_SUCCESS) { - #if (defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L) || defined(_MSC_VER) - ma_log_postf(ma_resource_manager_get_log(pResourceManager), MA_LOG_LEVEL_WARNING, "Failed to load file \"%ls\". %s.\n", pFilePathW, ma_result_description(result)); - #endif - return result; - } - } - - return MA_SUCCESS; -} - -static ma_bool32 ma_resource_manager_data_buffer_has_connector(ma_resource_manager_data_buffer* pDataBuffer) -{ - return ma_atomic_bool32_get(&pDataBuffer->isConnectorInitialized); -} - -static ma_data_source* ma_resource_manager_data_buffer_get_connector(ma_resource_manager_data_buffer* pDataBuffer) -{ - if (ma_resource_manager_data_buffer_has_connector(pDataBuffer) == MA_FALSE) { - return NULL; /* Connector not yet initialized. */ - } - - switch (pDataBuffer->pNode->data.type) - { - case ma_resource_manager_data_supply_type_encoded: return &pDataBuffer->connector.decoder; - case ma_resource_manager_data_supply_type_decoded: return &pDataBuffer->connector.buffer; - case ma_resource_manager_data_supply_type_decoded_paged: return &pDataBuffer->connector.pagedBuffer; - - case ma_resource_manager_data_supply_type_unknown: - default: - { - ma_log_postf(ma_resource_manager_get_log(pDataBuffer->pResourceManager), MA_LOG_LEVEL_ERROR, "Failed to retrieve data buffer connector. Unknown data supply type.\n"); - return NULL; - }; - }; -} - -static ma_result ma_resource_manager_data_buffer_init_connector(ma_resource_manager_data_buffer* pDataBuffer, const ma_resource_manager_data_source_config* pConfig, ma_async_notification* pInitNotification, ma_fence* pInitFence) -{ - ma_result result; - - MA_ASSERT(pDataBuffer != NULL); - MA_ASSERT(pConfig != NULL); - MA_ASSERT(ma_resource_manager_data_buffer_has_connector(pDataBuffer) == MA_FALSE); - - /* The underlying data buffer must be initialized before we'll be able to know how to initialize the backend. */ - result = ma_resource_manager_data_buffer_node_result(pDataBuffer->pNode); - if (result != MA_SUCCESS && result != MA_BUSY) { - return result; /* The data buffer is in an erroneous state. */ - } - - /* - We need to initialize either a ma_decoder or an ma_audio_buffer depending on whether or not the backing data is encoded or decoded. These act as the - "instance" to the data and are used to form the connection between underlying data buffer and the data source. If the data buffer is decoded, we can use - an ma_audio_buffer. This enables us to use memory mapping when mixing which saves us a bit of data movement overhead. - */ - switch (ma_resource_manager_data_buffer_node_get_data_supply_type(pDataBuffer->pNode)) - { - case ma_resource_manager_data_supply_type_encoded: /* Connector is a decoder. */ - { - ma_decoder_config config; - config = ma_resource_manager__init_decoder_config(pDataBuffer->pResourceManager); - result = ma_decoder_init_memory(pDataBuffer->pNode->data.backend.encoded.pData, pDataBuffer->pNode->data.backend.encoded.sizeInBytes, &config, &pDataBuffer->connector.decoder); - } break; - - case ma_resource_manager_data_supply_type_decoded: /* Connector is an audio buffer. */ - { - ma_audio_buffer_config config; - config = ma_audio_buffer_config_init(pDataBuffer->pNode->data.backend.decoded.format, pDataBuffer->pNode->data.backend.decoded.channels, pDataBuffer->pNode->data.backend.decoded.totalFrameCount, pDataBuffer->pNode->data.backend.decoded.pData, NULL); - result = ma_audio_buffer_init(&config, &pDataBuffer->connector.buffer); - } break; - - case ma_resource_manager_data_supply_type_decoded_paged: /* Connector is a paged audio buffer. */ - { - ma_paged_audio_buffer_config config; - config = ma_paged_audio_buffer_config_init(&pDataBuffer->pNode->data.backend.decodedPaged.data); - result = ma_paged_audio_buffer_init(&config, &pDataBuffer->connector.pagedBuffer); - } break; - - case ma_resource_manager_data_supply_type_unknown: - default: - { - /* Unknown data supply type. Should never happen. Need to post an error here. */ - return MA_INVALID_ARGS; - }; - } - - /* - Initialization of the connector is when we can fire the init notification. This will give the application access to - the format/channels/rate of the data source. - */ - if (result == MA_SUCCESS) { - /* - The resource manager supports the ability to set the range and loop settings via a config at - initialization time. This results in an case where the ranges could be set explicitly via - ma_data_source_set_*() before we get to this point here. If this happens, we'll end up - hitting a case where we just override those settings which results in what feels like a bug. - - To address this we only change the relevant properties if they're not equal to defaults. If - they're equal to defaults there's no need to change them anyway. If they're *not* set to the - default values, we can assume the user has set the range and loop settings via the config. If - they're doing their own calls to ma_data_source_set_*() in addition to setting them via the - config, that's entirely on the caller and any synchronization issue becomes their problem. - */ - if (pConfig->rangeBegInPCMFrames != MA_DATA_SOURCE_DEFAULT_RANGE_BEG || pConfig->rangeEndInPCMFrames != MA_DATA_SOURCE_DEFAULT_RANGE_END) { - ma_data_source_set_range_in_pcm_frames(pDataBuffer, pConfig->rangeBegInPCMFrames, pConfig->rangeEndInPCMFrames); - } - - if (pConfig->loopPointBegInPCMFrames != MA_DATA_SOURCE_DEFAULT_LOOP_POINT_BEG || pConfig->loopPointEndInPCMFrames != MA_DATA_SOURCE_DEFAULT_LOOP_POINT_END) { - ma_data_source_set_loop_point_in_pcm_frames(pDataBuffer, pConfig->loopPointBegInPCMFrames, pConfig->loopPointEndInPCMFrames); - } - - if (pConfig->isLooping != MA_FALSE) { - ma_data_source_set_looping(pDataBuffer, pConfig->isLooping); - } - - ma_atomic_bool32_set(&pDataBuffer->isConnectorInitialized, MA_TRUE); - - if (pInitNotification != NULL) { - ma_async_notification_signal(pInitNotification); - } - - if (pInitFence != NULL) { - ma_fence_release(pInitFence); - } - } - - /* At this point the backend should be initialized. We do *not* want to set pDataSource->result here - that needs to be done at a higher level to ensure it's done as the last step. */ - return result; -} - -static ma_result ma_resource_manager_data_buffer_uninit_connector(ma_resource_manager* pResourceManager, ma_resource_manager_data_buffer* pDataBuffer) -{ - MA_ASSERT(pResourceManager != NULL); - MA_ASSERT(pDataBuffer != NULL); - - (void)pResourceManager; - - switch (ma_resource_manager_data_buffer_node_get_data_supply_type(pDataBuffer->pNode)) - { - case ma_resource_manager_data_supply_type_encoded: /* Connector is a decoder. */ - { - ma_decoder_uninit(&pDataBuffer->connector.decoder); - } break; - - case ma_resource_manager_data_supply_type_decoded: /* Connector is an audio buffer. */ - { - ma_audio_buffer_uninit(&pDataBuffer->connector.buffer); - } break; - - case ma_resource_manager_data_supply_type_decoded_paged: /* Connector is a paged audio buffer. */ - { - ma_paged_audio_buffer_uninit(&pDataBuffer->connector.pagedBuffer); - } break; - - case ma_resource_manager_data_supply_type_unknown: - default: - { - /* Unknown data supply type. Should never happen. Need to post an error here. */ - return MA_INVALID_ARGS; - }; - } - - return MA_SUCCESS; -} - -static ma_uint32 ma_resource_manager_data_buffer_node_next_execution_order(ma_resource_manager_data_buffer_node* pDataBufferNode) -{ - MA_ASSERT(pDataBufferNode != NULL); - return ma_atomic_fetch_add_32(&pDataBufferNode->executionCounter, 1); -} - -static ma_result ma_resource_manager_data_buffer_node_init_supply_encoded(ma_resource_manager* pResourceManager, ma_resource_manager_data_buffer_node* pDataBufferNode, const char* pFilePath, const wchar_t* pFilePathW) -{ - ma_result result; - size_t dataSizeInBytes; - void* pData; - - MA_ASSERT(pResourceManager != NULL); - MA_ASSERT(pDataBufferNode != NULL); - MA_ASSERT(pFilePath != NULL || pFilePathW != NULL); - - result = ma_vfs_open_and_read_file_ex(pResourceManager->config.pVFS, pFilePath, pFilePathW, &pData, &dataSizeInBytes, &pResourceManager->config.allocationCallbacks); - if (result != MA_SUCCESS) { - if (pFilePath != NULL) { - ma_log_postf(ma_resource_manager_get_log(pResourceManager), MA_LOG_LEVEL_WARNING, "Failed to load file \"%s\". %s.\n", pFilePath, ma_result_description(result)); - } else { - #if (defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L) || defined(_MSC_VER) - ma_log_postf(ma_resource_manager_get_log(pResourceManager), MA_LOG_LEVEL_WARNING, "Failed to load file \"%ls\". %s.\n", pFilePathW, ma_result_description(result)); - #endif - } - - return result; - } - - pDataBufferNode->data.backend.encoded.pData = pData; - pDataBufferNode->data.backend.encoded.sizeInBytes = dataSizeInBytes; - ma_resource_manager_data_buffer_node_set_data_supply_type(pDataBufferNode, ma_resource_manager_data_supply_type_encoded); /* <-- Must be set last. */ - - return MA_SUCCESS; -} - -static ma_result ma_resource_manager_data_buffer_node_init_supply_decoded(ma_resource_manager* pResourceManager, ma_resource_manager_data_buffer_node* pDataBufferNode, const char* pFilePath, const wchar_t* pFilePathW, ma_uint32 flags, ma_decoder** ppDecoder) -{ - ma_result result = MA_SUCCESS; - ma_decoder* pDecoder; - ma_uint64 totalFrameCount; - - MA_ASSERT(pResourceManager != NULL); - MA_ASSERT(pDataBufferNode != NULL); - MA_ASSERT(ppDecoder != NULL); - MA_ASSERT(pFilePath != NULL || pFilePathW != NULL); - - *ppDecoder = NULL; /* For safety. */ - - pDecoder = (ma_decoder*)ma_malloc(sizeof(*pDecoder), &pResourceManager->config.allocationCallbacks); - if (pDecoder == NULL) { - return MA_OUT_OF_MEMORY; - } - - result = ma_resource_manager__init_decoder(pResourceManager, pFilePath, pFilePathW, pDecoder); - if (result != MA_SUCCESS) { - ma_free(pDecoder, &pResourceManager->config.allocationCallbacks); - return result; - } - - /* - At this point we have the decoder and we now need to initialize the data supply. This will - be either a decoded buffer, or a decoded paged buffer. A regular buffer is just one big heap - allocated buffer, whereas a paged buffer is a linked list of paged-sized buffers. The latter - is used when the length of a sound is unknown until a full decode has been performed. - */ - if ((flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_UNKNOWN_LENGTH) == 0) { - result = ma_decoder_get_length_in_pcm_frames(pDecoder, &totalFrameCount); - if (result != MA_SUCCESS) { - return result; - } - } else { - totalFrameCount = 0; - } - - if (totalFrameCount > 0) { - /* It's a known length. The data supply is a regular decoded buffer. */ - ma_uint64 dataSizeInBytes; - void* pData; - - dataSizeInBytes = totalFrameCount * ma_get_bytes_per_frame(pDecoder->outputFormat, pDecoder->outputChannels); - if (dataSizeInBytes > MA_SIZE_MAX) { - ma_decoder_uninit(pDecoder); - ma_free(pDecoder, &pResourceManager->config.allocationCallbacks); - return MA_TOO_BIG; - } - - pData = ma_malloc((size_t)dataSizeInBytes, &pResourceManager->config.allocationCallbacks); - if (pData == NULL) { - ma_decoder_uninit(pDecoder); - ma_free(pDecoder, &pResourceManager->config.allocationCallbacks); - return MA_OUT_OF_MEMORY; - } - - /* The buffer needs to be initialized to silence in case the caller reads from it. */ - ma_silence_pcm_frames(pData, totalFrameCount, pDecoder->outputFormat, pDecoder->outputChannels); - - /* Data has been allocated and the data supply can now be initialized. */ - pDataBufferNode->data.backend.decoded.pData = pData; - pDataBufferNode->data.backend.decoded.totalFrameCount = totalFrameCount; - pDataBufferNode->data.backend.decoded.format = pDecoder->outputFormat; - pDataBufferNode->data.backend.decoded.channels = pDecoder->outputChannels; - pDataBufferNode->data.backend.decoded.sampleRate = pDecoder->outputSampleRate; - pDataBufferNode->data.backend.decoded.decodedFrameCount = 0; - ma_resource_manager_data_buffer_node_set_data_supply_type(pDataBufferNode, ma_resource_manager_data_supply_type_decoded); /* <-- Must be set last. */ - } else { - /* - It's an unknown length. The data supply is a paged decoded buffer. Setting this up is - actually easier than the non-paged decoded buffer because we just need to initialize - a ma_paged_audio_buffer object. - */ - result = ma_paged_audio_buffer_data_init(pDecoder->outputFormat, pDecoder->outputChannels, &pDataBufferNode->data.backend.decodedPaged.data); - if (result != MA_SUCCESS) { - ma_decoder_uninit(pDecoder); - ma_free(pDecoder, &pResourceManager->config.allocationCallbacks); - return result; - } - - pDataBufferNode->data.backend.decodedPaged.sampleRate = pDecoder->outputSampleRate; - pDataBufferNode->data.backend.decodedPaged.decodedFrameCount = 0; - ma_resource_manager_data_buffer_node_set_data_supply_type(pDataBufferNode, ma_resource_manager_data_supply_type_decoded_paged); /* <-- Must be set last. */ - } - - *ppDecoder = pDecoder; - - return MA_SUCCESS; -} - -static ma_result ma_resource_manager_data_buffer_node_decode_next_page(ma_resource_manager* pResourceManager, ma_resource_manager_data_buffer_node* pDataBufferNode, ma_decoder* pDecoder) -{ - ma_result result = MA_SUCCESS; - ma_uint64 pageSizeInFrames; - ma_uint64 framesToTryReading; - ma_uint64 framesRead; - - MA_ASSERT(pResourceManager != NULL); - MA_ASSERT(pDataBufferNode != NULL); - MA_ASSERT(pDecoder != NULL); - - /* We need to know the size of a page in frames to know how many frames to decode. */ - pageSizeInFrames = MA_RESOURCE_MANAGER_PAGE_SIZE_IN_MILLISECONDS * (pDecoder->outputSampleRate/1000); - framesToTryReading = pageSizeInFrames; - - /* - Here is where we do the decoding of the next page. We'll run a slightly different path depending - on whether or not we're using a flat or paged buffer because the allocation of the page differs - between the two. For a flat buffer it's an offset to an already-allocated buffer. For a paged - buffer, we need to allocate a new page and attach it to the linked list. - */ - switch (ma_resource_manager_data_buffer_node_get_data_supply_type(pDataBufferNode)) - { - case ma_resource_manager_data_supply_type_decoded: - { - /* The destination buffer is an offset to the existing buffer. Don't read more than we originally retrieved when we first initialized the decoder. */ - void* pDst; - ma_uint64 framesRemaining = pDataBufferNode->data.backend.decoded.totalFrameCount - pDataBufferNode->data.backend.decoded.decodedFrameCount; - if (framesToTryReading > framesRemaining) { - framesToTryReading = framesRemaining; - } - - if (framesToTryReading > 0) { - pDst = ma_offset_ptr( - pDataBufferNode->data.backend.decoded.pData, - pDataBufferNode->data.backend.decoded.decodedFrameCount * ma_get_bytes_per_frame(pDataBufferNode->data.backend.decoded.format, pDataBufferNode->data.backend.decoded.channels) - ); - MA_ASSERT(pDst != NULL); - - result = ma_decoder_read_pcm_frames(pDecoder, pDst, framesToTryReading, &framesRead); - if (framesRead > 0) { - pDataBufferNode->data.backend.decoded.decodedFrameCount += framesRead; - } - } else { - framesRead = 0; - } - } break; - - case ma_resource_manager_data_supply_type_decoded_paged: - { - /* The destination buffer is a freshly allocated page. */ - ma_paged_audio_buffer_page* pPage; - - result = ma_paged_audio_buffer_data_allocate_page(&pDataBufferNode->data.backend.decodedPaged.data, framesToTryReading, NULL, &pResourceManager->config.allocationCallbacks, &pPage); - if (result != MA_SUCCESS) { - return result; - } - - result = ma_decoder_read_pcm_frames(pDecoder, pPage->pAudioData, framesToTryReading, &framesRead); - if (result == MA_SUCCESS && framesRead > 0) { - pPage->sizeInFrames = framesRead; - - result = ma_paged_audio_buffer_data_append_page(&pDataBufferNode->data.backend.decodedPaged.data, pPage); - if (result == MA_SUCCESS) { - pDataBufferNode->data.backend.decodedPaged.decodedFrameCount += framesRead; - } else { - /* Failed to append the page. Just abort and set the status to MA_AT_END. */ - ma_paged_audio_buffer_data_free_page(&pDataBufferNode->data.backend.decodedPaged.data, pPage, &pResourceManager->config.allocationCallbacks); - result = MA_AT_END; - } - } else { - /* No frames were read. Free the page and just set the status to MA_AT_END. */ - ma_paged_audio_buffer_data_free_page(&pDataBufferNode->data.backend.decodedPaged.data, pPage, &pResourceManager->config.allocationCallbacks); - result = MA_AT_END; - } - } break; - - case ma_resource_manager_data_supply_type_encoded: - case ma_resource_manager_data_supply_type_unknown: - default: - { - /* Unexpected data supply type. */ - ma_log_postf(ma_resource_manager_get_log(pResourceManager), MA_LOG_LEVEL_ERROR, "Unexpected data supply type (%d) when decoding page.", ma_resource_manager_data_buffer_node_get_data_supply_type(pDataBufferNode)); - return MA_ERROR; - }; - } - - if (result == MA_SUCCESS && framesRead == 0) { - result = MA_AT_END; - } - - return result; -} - -static ma_result ma_resource_manager_data_buffer_node_acquire_critical_section(ma_resource_manager* pResourceManager, const char* pFilePath, const wchar_t* pFilePathW, ma_uint32 hashedName32, ma_uint32 flags, const ma_resource_manager_data_supply* pExistingData, ma_fence* pInitFence, ma_fence* pDoneFence, ma_resource_manager_inline_notification* pInitNotification, ma_resource_manager_data_buffer_node** ppDataBufferNode) -{ - ma_result result = MA_SUCCESS; - ma_resource_manager_data_buffer_node* pDataBufferNode = NULL; - ma_resource_manager_data_buffer_node* pInsertPoint; - - if (ppDataBufferNode != NULL) { - *ppDataBufferNode = NULL; - } - - result = ma_resource_manager_data_buffer_node_insert_point(pResourceManager, hashedName32, &pInsertPoint); - if (result == MA_ALREADY_EXISTS) { - /* The node already exists. We just need to increment the reference count. */ - pDataBufferNode = pInsertPoint; - - result = ma_resource_manager_data_buffer_node_increment_ref(pResourceManager, pDataBufferNode, NULL); - if (result != MA_SUCCESS) { - return result; /* Should never happen. Failed to increment the reference count. */ - } - - result = MA_ALREADY_EXISTS; - goto done; - } else { - /* - The node does not already exist. We need to post a LOAD_DATA_BUFFER_NODE job here. This - needs to be done inside the critical section to ensure an uninitialization of the node - does not occur before initialization on another thread. - */ - pDataBufferNode = (ma_resource_manager_data_buffer_node*)ma_malloc(sizeof(*pDataBufferNode), &pResourceManager->config.allocationCallbacks); - if (pDataBufferNode == NULL) { - return MA_OUT_OF_MEMORY; - } - - MA_ZERO_OBJECT(pDataBufferNode); - pDataBufferNode->hashedName32 = hashedName32; - pDataBufferNode->refCount = 1; /* Always set to 1 by default (this is our first reference). */ - - if (pExistingData == NULL) { - pDataBufferNode->data.type = ma_resource_manager_data_supply_type_unknown; /* <-- We won't know this until we start decoding. */ - pDataBufferNode->result = MA_BUSY; /* Must be set to MA_BUSY before we leave the critical section, so might as well do it now. */ - pDataBufferNode->isDataOwnedByResourceManager = MA_TRUE; - } else { - pDataBufferNode->data = *pExistingData; - pDataBufferNode->result = MA_SUCCESS; /* Not loading asynchronously, so just set the status */ - pDataBufferNode->isDataOwnedByResourceManager = MA_FALSE; - } - - result = ma_resource_manager_data_buffer_node_insert_at(pResourceManager, pDataBufferNode, pInsertPoint); - if (result != MA_SUCCESS) { - ma_free(pDataBufferNode, &pResourceManager->config.allocationCallbacks); - return result; /* Should never happen. Failed to insert the data buffer into the BST. */ - } - - /* - Here is where we'll post the job, but only if we're loading asynchronously. If we're - loading synchronously we'll defer loading to a later stage, outside of the critical - section. - */ - if (pDataBufferNode->isDataOwnedByResourceManager && (flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_ASYNC) != 0) { - /* Loading asynchronously. Post the job. */ - ma_job job; - char* pFilePathCopy = NULL; - wchar_t* pFilePathWCopy = NULL; - - /* We need a copy of the file path. We should probably make this more efficient, but for now we'll do a transient memory allocation. */ - if (pFilePath != NULL) { - pFilePathCopy = ma_copy_string(pFilePath, &pResourceManager->config.allocationCallbacks); - } else { - pFilePathWCopy = ma_copy_string_w(pFilePathW, &pResourceManager->config.allocationCallbacks); - } - - if (pFilePathCopy == NULL && pFilePathWCopy == NULL) { - ma_resource_manager_data_buffer_node_remove(pResourceManager, pDataBufferNode); - ma_free(pDataBufferNode, &pResourceManager->config.allocationCallbacks); - return MA_OUT_OF_MEMORY; - } - - if ((flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_WAIT_INIT) != 0) { - ma_resource_manager_inline_notification_init(pResourceManager, pInitNotification); - } - - /* Acquire init and done fences before posting the job. These will be unacquired by the job thread. */ - if (pInitFence != NULL) { ma_fence_acquire(pInitFence); } - if (pDoneFence != NULL) { ma_fence_acquire(pDoneFence); } - - /* We now have everything we need to post the job to the job thread. */ - job = ma_job_init(MA_JOB_TYPE_RESOURCE_MANAGER_LOAD_DATA_BUFFER_NODE); - job.order = ma_resource_manager_data_buffer_node_next_execution_order(pDataBufferNode); - job.data.resourceManager.loadDataBufferNode.pResourceManager = pResourceManager; - job.data.resourceManager.loadDataBufferNode.pDataBufferNode = pDataBufferNode; - job.data.resourceManager.loadDataBufferNode.pFilePath = pFilePathCopy; - job.data.resourceManager.loadDataBufferNode.pFilePathW = pFilePathWCopy; - job.data.resourceManager.loadDataBufferNode.flags = flags; - job.data.resourceManager.loadDataBufferNode.pInitNotification = ((flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_WAIT_INIT) != 0) ? pInitNotification : NULL; - job.data.resourceManager.loadDataBufferNode.pDoneNotification = NULL; - job.data.resourceManager.loadDataBufferNode.pInitFence = pInitFence; - job.data.resourceManager.loadDataBufferNode.pDoneFence = pDoneFence; - - if ((flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_WAIT_INIT) != 0) { - result = ma_job_process(&job); - } else { - result = ma_resource_manager_post_job(pResourceManager, &job); - } - - if (result != MA_SUCCESS) { - /* Failed to post job. Probably ran out of memory. */ - ma_log_postf(ma_resource_manager_get_log(pResourceManager), MA_LOG_LEVEL_ERROR, "Failed to post MA_JOB_TYPE_RESOURCE_MANAGER_LOAD_DATA_BUFFER_NODE job. %s.\n", ma_result_description(result)); - - /* - Fences were acquired before posting the job, but since the job was not able to - be posted, we need to make sure we release them so nothing gets stuck waiting. - */ - if (pInitFence != NULL) { ma_fence_release(pInitFence); } - if (pDoneFence != NULL) { ma_fence_release(pDoneFence); } - - if ((flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_WAIT_INIT) != 0) { - ma_resource_manager_inline_notification_uninit(pInitNotification); - } else { - /* These will have been freed by the job thread, but with WAIT_INIT they will already have happened since the job has already been handled. */ - ma_free(pFilePathCopy, &pResourceManager->config.allocationCallbacks); - ma_free(pFilePathWCopy, &pResourceManager->config.allocationCallbacks); - } - - ma_resource_manager_data_buffer_node_remove(pResourceManager, pDataBufferNode); - ma_free(pDataBufferNode, &pResourceManager->config.allocationCallbacks); - - return result; - } - } - } - -done: - if (ppDataBufferNode != NULL) { - *ppDataBufferNode = pDataBufferNode; - } - - return result; -} - -static ma_result ma_resource_manager_data_buffer_node_acquire(ma_resource_manager* pResourceManager, const char* pFilePath, const wchar_t* pFilePathW, ma_uint32 hashedName32, ma_uint32 flags, const ma_resource_manager_data_supply* pExistingData, ma_fence* pInitFence, ma_fence* pDoneFence, ma_resource_manager_data_buffer_node** ppDataBufferNode) -{ - ma_result result = MA_SUCCESS; - ma_bool32 nodeAlreadyExists = MA_FALSE; - ma_resource_manager_data_buffer_node* pDataBufferNode = NULL; - ma_resource_manager_inline_notification initNotification; /* Used when the WAIT_INIT flag is set. */ - - if (ppDataBufferNode != NULL) { - *ppDataBufferNode = NULL; /* Safety. */ - } - - if (pResourceManager == NULL || (pFilePath == NULL && pFilePathW == NULL && hashedName32 == 0)) { - return MA_INVALID_ARGS; - } - - /* If we're specifying existing data, it must be valid. */ - if (pExistingData != NULL && pExistingData->type == ma_resource_manager_data_supply_type_unknown) { - return MA_INVALID_ARGS; - } - - /* If we don't support threading, remove the ASYNC flag to make the rest of this a bit simpler. */ - if (ma_resource_manager_is_threading_enabled(pResourceManager) == MA_FALSE) { - flags &= ~MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_ASYNC; - } - - if (hashedName32 == 0) { - if (pFilePath != NULL) { - hashedName32 = ma_hash_string_32(pFilePath); - } else { - hashedName32 = ma_hash_string_w_32(pFilePathW); - } - } - - /* - Here is where we either increment the node's reference count or allocate a new one and add it - to the BST. When allocating a new node, we need to make sure the LOAD_DATA_BUFFER_NODE job is - posted inside the critical section just in case the caller immediately uninitializes the node - as this will ensure the FREE_DATA_BUFFER_NODE job is given an execution order such that the - node is not uninitialized before initialization. - */ - ma_resource_manager_data_buffer_bst_lock(pResourceManager); - { - result = ma_resource_manager_data_buffer_node_acquire_critical_section(pResourceManager, pFilePath, pFilePathW, hashedName32, flags, pExistingData, pInitFence, pDoneFence, &initNotification, &pDataBufferNode); - } - ma_resource_manager_data_buffer_bst_unlock(pResourceManager); - - if (result == MA_ALREADY_EXISTS) { - nodeAlreadyExists = MA_TRUE; - result = MA_SUCCESS; - } else { - if (result != MA_SUCCESS) { - return result; - } - } - - /* - If we're loading synchronously, we'll need to load everything now. When loading asynchronously, - a job will have been posted inside the BST critical section so that an uninitialization can be - allocated an appropriate execution order thereby preventing it from being uninitialized before - the node is initialized by the decoding thread(s). - */ - if (nodeAlreadyExists == MA_FALSE) { /* Don't need to try loading anything if the node already exists. */ - if (pFilePath == NULL && pFilePathW == NULL) { - /* - If this path is hit, it means a buffer is being copied (i.e. initialized from only the - hashed name), but that node has been freed in the meantime, probably from some other - thread. This is an invalid operation. - */ - ma_log_postf(ma_resource_manager_get_log(pResourceManager), MA_LOG_LEVEL_WARNING, "Cloning data buffer node failed because the source node was released. The source node must remain valid until the cloning has completed.\n"); - result = MA_INVALID_OPERATION; - goto done; - } - - if (pDataBufferNode->isDataOwnedByResourceManager) { - if ((flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_ASYNC) == 0) { - /* Loading synchronously. Load the sound in it's entirety here. */ - if ((flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_DECODE) == 0) { - /* No decoding. This is the simple case - just store the file contents in memory. */ - result = ma_resource_manager_data_buffer_node_init_supply_encoded(pResourceManager, pDataBufferNode, pFilePath, pFilePathW); - if (result != MA_SUCCESS) { - goto done; - } - } else { - /* Decoding. We do this the same way as we do when loading asynchronously. */ - ma_decoder* pDecoder; - result = ma_resource_manager_data_buffer_node_init_supply_decoded(pResourceManager, pDataBufferNode, pFilePath, pFilePathW, flags, &pDecoder); - if (result != MA_SUCCESS) { - goto done; - } - - /* We have the decoder, now decode page by page just like we do when loading asynchronously. */ - for (;;) { - /* Decode next page. */ - result = ma_resource_manager_data_buffer_node_decode_next_page(pResourceManager, pDataBufferNode, pDecoder); - if (result != MA_SUCCESS) { - break; /* Will return MA_AT_END when the last page has been decoded. */ - } - } - - /* Reaching the end needs to be considered successful. */ - if (result == MA_AT_END) { - result = MA_SUCCESS; - } - - /* - At this point the data buffer is either fully decoded or some error occurred. Either - way, the decoder is no longer necessary. - */ - ma_decoder_uninit(pDecoder); - ma_free(pDecoder, &pResourceManager->config.allocationCallbacks); - } - - /* Getting here means we were successful. Make sure the status of the node is updated accordingly. */ - ma_atomic_exchange_i32(&pDataBufferNode->result, result); - } else { - /* Loading asynchronously. We may need to wait for initialization. */ - if ((flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_WAIT_INIT) != 0) { - ma_resource_manager_inline_notification_wait(&initNotification); - } - } - } else { - /* The data is not managed by the resource manager so there's nothing else to do. */ - MA_ASSERT(pExistingData != NULL); - } - } - -done: - /* If we failed to initialize the data buffer we need to free it. */ - if (result != MA_SUCCESS) { - if (nodeAlreadyExists == MA_FALSE) { - ma_resource_manager_data_buffer_node_remove(pResourceManager, pDataBufferNode); - ma_free(pDataBufferNode, &pResourceManager->config.allocationCallbacks); - } - } - - /* - The init notification needs to be uninitialized. This will be used if the node does not already - exist, and we've specified ASYNC | WAIT_INIT. - */ - if (nodeAlreadyExists == MA_FALSE && pDataBufferNode->isDataOwnedByResourceManager && (flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_ASYNC) != 0) { - if ((flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_WAIT_INIT) != 0) { - ma_resource_manager_inline_notification_uninit(&initNotification); - } - } - - if (ppDataBufferNode != NULL) { - *ppDataBufferNode = pDataBufferNode; - } - - return result; -} - -static ma_result ma_resource_manager_data_buffer_node_unacquire(ma_resource_manager* pResourceManager, ma_resource_manager_data_buffer_node* pDataBufferNode, const char* pName, const wchar_t* pNameW) -{ - ma_result result = MA_SUCCESS; - ma_uint32 refCount = 0xFFFFFFFF; /* The new reference count of the node after decrementing. Initialize to non-0 to be safe we don't fall into the freeing path. */ - ma_uint32 hashedName32 = 0; - - if (pResourceManager == NULL) { - return MA_INVALID_ARGS; - } - - if (pDataBufferNode == NULL) { - if (pName == NULL && pNameW == NULL) { - return MA_INVALID_ARGS; - } - - if (pName != NULL) { - hashedName32 = ma_hash_string_32(pName); - } else { - hashedName32 = ma_hash_string_w_32(pNameW); - } - } - - /* - The first thing to do is decrement the reference counter of the node. Then, if the reference - count is zero, we need to free the node. If the node is still in the process of loading, we'll - need to post a job to the job queue to free the node. Otherwise we'll just do it here. - */ - ma_resource_manager_data_buffer_bst_lock(pResourceManager); - { - /* Might need to find the node. Must be done inside the critical section. */ - if (pDataBufferNode == NULL) { - result = ma_resource_manager_data_buffer_node_search(pResourceManager, hashedName32, &pDataBufferNode); - if (result != MA_SUCCESS) { - goto stage2; /* Couldn't find the node. */ - } - } - - result = ma_resource_manager_data_buffer_node_decrement_ref(pResourceManager, pDataBufferNode, &refCount); - if (result != MA_SUCCESS) { - goto stage2; /* Should never happen. */ - } - - if (refCount == 0) { - result = ma_resource_manager_data_buffer_node_remove(pResourceManager, pDataBufferNode); - if (result != MA_SUCCESS) { - goto stage2; /* An error occurred when trying to remove the data buffer. This should never happen. */ - } - } - } - ma_resource_manager_data_buffer_bst_unlock(pResourceManager); - -stage2: - if (result != MA_SUCCESS) { - return result; - } - - /* - Here is where we need to free the node. We don't want to do this inside the critical section - above because we want to keep that as small as possible for multi-threaded efficiency. - */ - if (refCount == 0) { - if (ma_resource_manager_data_buffer_node_result(pDataBufferNode) == MA_BUSY) { - /* The sound is still loading. We need to delay the freeing of the node to a safe time. */ - ma_job job; - - /* We need to mark the node as unavailable for the sake of the resource manager worker threads. */ - ma_atomic_exchange_i32(&pDataBufferNode->result, MA_UNAVAILABLE); - - job = ma_job_init(MA_JOB_TYPE_RESOURCE_MANAGER_FREE_DATA_BUFFER_NODE); - job.order = ma_resource_manager_data_buffer_node_next_execution_order(pDataBufferNode); - job.data.resourceManager.freeDataBufferNode.pResourceManager = pResourceManager; - job.data.resourceManager.freeDataBufferNode.pDataBufferNode = pDataBufferNode; - - result = ma_resource_manager_post_job(pResourceManager, &job); - if (result != MA_SUCCESS) { - ma_log_postf(ma_resource_manager_get_log(pResourceManager), MA_LOG_LEVEL_ERROR, "Failed to post MA_JOB_TYPE_RESOURCE_MANAGER_FREE_DATA_BUFFER_NODE job. %s.\n", ma_result_description(result)); - return result; - } - - /* If we don't support threading, process the job queue here. */ - if (ma_resource_manager_is_threading_enabled(pResourceManager) == MA_FALSE) { - while (ma_resource_manager_data_buffer_node_result(pDataBufferNode) == MA_BUSY) { - result = ma_resource_manager_process_next_job(pResourceManager); - if (result == MA_NO_DATA_AVAILABLE || result == MA_CANCELLED) { - result = MA_SUCCESS; - break; - } - } - } else { - /* Threading is enabled. The job queue will deal with the rest of the cleanup from here. */ - } - } else { - /* The sound isn't loading so we can just free the node here. */ - ma_resource_manager_data_buffer_node_free(pResourceManager, pDataBufferNode); - } - } - - return result; -} - - - -static ma_uint32 ma_resource_manager_data_buffer_next_execution_order(ma_resource_manager_data_buffer* pDataBuffer) -{ - MA_ASSERT(pDataBuffer != NULL); - return ma_atomic_fetch_add_32(&pDataBuffer->executionCounter, 1); -} - -static ma_result ma_resource_manager_data_buffer_cb__read_pcm_frames(ma_data_source* pDataSource, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead) -{ - return ma_resource_manager_data_buffer_read_pcm_frames((ma_resource_manager_data_buffer*)pDataSource, pFramesOut, frameCount, pFramesRead); -} - -static ma_result ma_resource_manager_data_buffer_cb__seek_to_pcm_frame(ma_data_source* pDataSource, ma_uint64 frameIndex) -{ - return ma_resource_manager_data_buffer_seek_to_pcm_frame((ma_resource_manager_data_buffer*)pDataSource, frameIndex); -} - -static ma_result ma_resource_manager_data_buffer_cb__get_data_format(ma_data_source* pDataSource, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate, ma_channel* pChannelMap, size_t channelMapCap) -{ - return ma_resource_manager_data_buffer_get_data_format((ma_resource_manager_data_buffer*)pDataSource, pFormat, pChannels, pSampleRate, pChannelMap, channelMapCap); -} - -static ma_result ma_resource_manager_data_buffer_cb__get_cursor_in_pcm_frames(ma_data_source* pDataSource, ma_uint64* pCursor) -{ - return ma_resource_manager_data_buffer_get_cursor_in_pcm_frames((ma_resource_manager_data_buffer*)pDataSource, pCursor); -} - -static ma_result ma_resource_manager_data_buffer_cb__get_length_in_pcm_frames(ma_data_source* pDataSource, ma_uint64* pLength) -{ - return ma_resource_manager_data_buffer_get_length_in_pcm_frames((ma_resource_manager_data_buffer*)pDataSource, pLength); -} - -static ma_result ma_resource_manager_data_buffer_cb__set_looping(ma_data_source* pDataSource, ma_bool32 isLooping) -{ - ma_resource_manager_data_buffer* pDataBuffer = (ma_resource_manager_data_buffer*)pDataSource; - MA_ASSERT(pDataBuffer != NULL); - - ma_atomic_exchange_32(&pDataBuffer->isLooping, isLooping); - - /* The looping state needs to be set on the connector as well or else looping won't work when we read audio data. */ - ma_data_source_set_looping(ma_resource_manager_data_buffer_get_connector(pDataBuffer), isLooping); - - return MA_SUCCESS; -} - -static ma_data_source_vtable g_ma_resource_manager_data_buffer_vtable = -{ - ma_resource_manager_data_buffer_cb__read_pcm_frames, - ma_resource_manager_data_buffer_cb__seek_to_pcm_frame, - ma_resource_manager_data_buffer_cb__get_data_format, - ma_resource_manager_data_buffer_cb__get_cursor_in_pcm_frames, - ma_resource_manager_data_buffer_cb__get_length_in_pcm_frames, - ma_resource_manager_data_buffer_cb__set_looping, - 0 -}; - -static ma_result ma_resource_manager_data_buffer_init_ex_internal(ma_resource_manager* pResourceManager, const ma_resource_manager_data_source_config* pConfig, ma_uint32 hashedName32, ma_resource_manager_data_buffer* pDataBuffer) -{ - ma_result result = MA_SUCCESS; - ma_resource_manager_data_buffer_node* pDataBufferNode; - ma_data_source_config dataSourceConfig; - ma_bool32 async; - ma_uint32 flags; - ma_resource_manager_pipeline_notifications notifications; - - if (pDataBuffer == NULL) { - if (pConfig != NULL && pConfig->pNotifications != NULL) { - ma_resource_manager_pipeline_notifications_signal_all_notifications(pConfig->pNotifications); - } - - return MA_INVALID_ARGS; - } - - MA_ZERO_OBJECT(pDataBuffer); - - if (pConfig == NULL) { - return MA_INVALID_ARGS; - } - - if (pConfig->pNotifications != NULL) { - notifications = *pConfig->pNotifications; /* From here on out we should be referencing `notifications` instead of `pNotifications`. Set this to NULL to catch errors at testing time. */ - } else { - MA_ZERO_OBJECT(¬ifications); - } - - /* For safety, always remove the ASYNC flag if threading is disabled on the resource manager. */ - flags = pConfig->flags; - if (ma_resource_manager_is_threading_enabled(pResourceManager) == MA_FALSE) { - flags &= ~MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_ASYNC; - } - - if (pConfig->isLooping) { - flags |= MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_LOOPING; - } - - async = (flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_ASYNC) != 0; - - /* - Fences need to be acquired before doing anything. These must be acquired and released outside of - the node to ensure there's no holes where ma_fence_wait() could prematurely return before the - data buffer has completed initialization. - - When loading asynchronously, the node acquisition routine below will acquire the fences on this - thread and then release them on the async thread when the operation is complete. - - These fences are always released at the "done" tag at the end of this function. They'll be - acquired a second if loading asynchronously. This double acquisition system is just done to - simplify code maintenance. - */ - ma_resource_manager_pipeline_notifications_acquire_all_fences(¬ifications); - { - /* We first need to acquire a node. If ASYNC is not set, this will not return until the entire sound has been loaded. */ - result = ma_resource_manager_data_buffer_node_acquire(pResourceManager, pConfig->pFilePath, pConfig->pFilePathW, hashedName32, flags, NULL, notifications.init.pFence, notifications.done.pFence, &pDataBufferNode); - if (result != MA_SUCCESS) { - ma_resource_manager_pipeline_notifications_signal_all_notifications(¬ifications); - goto done; - } - - dataSourceConfig = ma_data_source_config_init(); - dataSourceConfig.vtable = &g_ma_resource_manager_data_buffer_vtable; - - result = ma_data_source_init(&dataSourceConfig, &pDataBuffer->ds); - if (result != MA_SUCCESS) { - ma_resource_manager_data_buffer_node_unacquire(pResourceManager, pDataBufferNode, NULL, NULL); - ma_resource_manager_pipeline_notifications_signal_all_notifications(¬ifications); - goto done; - } - - pDataBuffer->pResourceManager = pResourceManager; - pDataBuffer->pNode = pDataBufferNode; - pDataBuffer->flags = flags; - pDataBuffer->result = MA_BUSY; /* Always default to MA_BUSY for safety. It'll be overwritten when loading completes or an error occurs. */ - - /* If we're loading asynchronously we need to post a job to the job queue to initialize the connector. */ - if (async == MA_FALSE || ma_resource_manager_data_buffer_node_result(pDataBufferNode) == MA_SUCCESS) { - /* Loading synchronously or the data has already been fully loaded. We can just initialize the connector from here without a job. */ - result = ma_resource_manager_data_buffer_init_connector(pDataBuffer, pConfig, NULL, NULL); - ma_atomic_exchange_i32(&pDataBuffer->result, result); - - ma_resource_manager_pipeline_notifications_signal_all_notifications(¬ifications); - goto done; - } else { - /* The node's data supply isn't initialized yet. The caller has requested that we load asynchronously so we need to post a job to do this. */ - ma_job job; - ma_resource_manager_inline_notification initNotification; /* Used when the WAIT_INIT flag is set. */ - - if ((flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_WAIT_INIT) != 0) { - ma_resource_manager_inline_notification_init(pResourceManager, &initNotification); - } - - /* - The status of the data buffer needs to be set to MA_BUSY before posting the job so that the - worker thread is aware of its busy state. If the LOAD_DATA_BUFFER job sees a status other - than MA_BUSY, it'll assume an error and fall through to an early exit. - */ - ma_atomic_exchange_i32(&pDataBuffer->result, MA_BUSY); - - /* Acquire fences a second time. These will be released by the async thread. */ - ma_resource_manager_pipeline_notifications_acquire_all_fences(¬ifications); - - job = ma_job_init(MA_JOB_TYPE_RESOURCE_MANAGER_LOAD_DATA_BUFFER); - job.order = ma_resource_manager_data_buffer_next_execution_order(pDataBuffer); - job.data.resourceManager.loadDataBuffer.pDataBuffer = pDataBuffer; - job.data.resourceManager.loadDataBuffer.pInitNotification = ((flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_WAIT_INIT) != 0) ? &initNotification : notifications.init.pNotification; - job.data.resourceManager.loadDataBuffer.pDoneNotification = notifications.done.pNotification; - job.data.resourceManager.loadDataBuffer.pInitFence = notifications.init.pFence; - job.data.resourceManager.loadDataBuffer.pDoneFence = notifications.done.pFence; - job.data.resourceManager.loadDataBuffer.rangeBegInPCMFrames = pConfig->rangeBegInPCMFrames; - job.data.resourceManager.loadDataBuffer.rangeEndInPCMFrames = pConfig->rangeEndInPCMFrames; - job.data.resourceManager.loadDataBuffer.loopPointBegInPCMFrames = pConfig->loopPointBegInPCMFrames; - job.data.resourceManager.loadDataBuffer.loopPointEndInPCMFrames = pConfig->loopPointEndInPCMFrames; - job.data.resourceManager.loadDataBuffer.isLooping = (flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_LOOPING) != 0; - - /* If we need to wait for initialization to complete we can just process the job in place. */ - if ((flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_WAIT_INIT) != 0) { - result = ma_job_process(&job); - } else { - result = ma_resource_manager_post_job(pResourceManager, &job); - } - - if (result != MA_SUCCESS) { - /* We failed to post the job. Most likely there isn't enough room in the queue's buffer. */ - ma_log_postf(ma_resource_manager_get_log(pResourceManager), MA_LOG_LEVEL_ERROR, "Failed to post MA_JOB_TYPE_RESOURCE_MANAGER_LOAD_DATA_BUFFER job. %s.\n", ma_result_description(result)); - ma_atomic_exchange_i32(&pDataBuffer->result, result); - - /* Release the fences after the result has been set on the data buffer. */ - ma_resource_manager_pipeline_notifications_release_all_fences(¬ifications); - } else { - if ((flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_WAIT_INIT) != 0) { - ma_resource_manager_inline_notification_wait(&initNotification); - - if (notifications.init.pNotification != NULL) { - ma_async_notification_signal(notifications.init.pNotification); - } - - /* NOTE: Do not release the init fence here. It will have been done by the job. */ - - /* Make sure we return an error if initialization failed on the async thread. */ - result = ma_resource_manager_data_buffer_result(pDataBuffer); - if (result == MA_BUSY) { - result = MA_SUCCESS; - } - } - } - - if ((flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_WAIT_INIT) != 0) { - ma_resource_manager_inline_notification_uninit(&initNotification); - } - } - - if (result != MA_SUCCESS) { - ma_resource_manager_data_buffer_node_unacquire(pResourceManager, pDataBufferNode, NULL, NULL); - goto done; - } - } -done: - if (result == MA_SUCCESS) { - if (pConfig->initialSeekPointInPCMFrames > 0) { - ma_resource_manager_data_buffer_seek_to_pcm_frame(pDataBuffer, pConfig->initialSeekPointInPCMFrames); - } - } - - ma_resource_manager_pipeline_notifications_release_all_fences(¬ifications); - - return result; -} - -MA_API ma_result ma_resource_manager_data_buffer_init_ex(ma_resource_manager* pResourceManager, const ma_resource_manager_data_source_config* pConfig, ma_resource_manager_data_buffer* pDataBuffer) -{ - return ma_resource_manager_data_buffer_init_ex_internal(pResourceManager, pConfig, 0, pDataBuffer); -} - -MA_API ma_result ma_resource_manager_data_buffer_init(ma_resource_manager* pResourceManager, const char* pFilePath, ma_uint32 flags, const ma_resource_manager_pipeline_notifications* pNotifications, ma_resource_manager_data_buffer* pDataBuffer) -{ - ma_resource_manager_data_source_config config; - - config = ma_resource_manager_data_source_config_init(); - config.pFilePath = pFilePath; - config.flags = flags; - config.pNotifications = pNotifications; - - return ma_resource_manager_data_buffer_init_ex(pResourceManager, &config, pDataBuffer); -} - -MA_API ma_result ma_resource_manager_data_buffer_init_w(ma_resource_manager* pResourceManager, const wchar_t* pFilePath, ma_uint32 flags, const ma_resource_manager_pipeline_notifications* pNotifications, ma_resource_manager_data_buffer* pDataBuffer) -{ - ma_resource_manager_data_source_config config; - - config = ma_resource_manager_data_source_config_init(); - config.pFilePathW = pFilePath; - config.flags = flags; - config.pNotifications = pNotifications; - - return ma_resource_manager_data_buffer_init_ex(pResourceManager, &config, pDataBuffer); -} - -MA_API ma_result ma_resource_manager_data_buffer_init_copy(ma_resource_manager* pResourceManager, const ma_resource_manager_data_buffer* pExistingDataBuffer, ma_resource_manager_data_buffer* pDataBuffer) -{ - ma_resource_manager_data_source_config config; - - if (pExistingDataBuffer == NULL) { - return MA_INVALID_ARGS; - } - - MA_ASSERT(pExistingDataBuffer->pNode != NULL); /* <-- If you've triggered this, you've passed in an invalid existing data buffer. */ - - config = ma_resource_manager_data_source_config_init(); - config.flags = pExistingDataBuffer->flags; - - return ma_resource_manager_data_buffer_init_ex_internal(pResourceManager, &config, pExistingDataBuffer->pNode->hashedName32, pDataBuffer); -} - -static ma_result ma_resource_manager_data_buffer_uninit_internal(ma_resource_manager_data_buffer* pDataBuffer) -{ - MA_ASSERT(pDataBuffer != NULL); - - /* The connector should be uninitialized first. */ - ma_resource_manager_data_buffer_uninit_connector(pDataBuffer->pResourceManager, pDataBuffer); - - /* With the connector uninitialized we can unacquire the node. */ - ma_resource_manager_data_buffer_node_unacquire(pDataBuffer->pResourceManager, pDataBuffer->pNode, NULL, NULL); - - /* The base data source needs to be uninitialized as well. */ - ma_data_source_uninit(&pDataBuffer->ds); - - return MA_SUCCESS; -} - -MA_API ma_result ma_resource_manager_data_buffer_uninit(ma_resource_manager_data_buffer* pDataBuffer) -{ - ma_result result; - - if (pDataBuffer == NULL) { - return MA_INVALID_ARGS; - } - - if (ma_resource_manager_data_buffer_result(pDataBuffer) == MA_SUCCESS) { - /* The data buffer can be deleted synchronously. */ - return ma_resource_manager_data_buffer_uninit_internal(pDataBuffer); - } else { - /* - The data buffer needs to be deleted asynchronously because it's still loading. With the status set to MA_UNAVAILABLE, no more pages will - be loaded and the uninitialization should happen fairly quickly. Since the caller owns the data buffer, we need to wait for this event - to get processed before returning. - */ - ma_resource_manager_inline_notification notification; - ma_job job; - - /* - We need to mark the node as unavailable so we don't try reading from it anymore, but also to - let the loading thread know that it needs to abort it's loading procedure. - */ - ma_atomic_exchange_i32(&pDataBuffer->result, MA_UNAVAILABLE); - - result = ma_resource_manager_inline_notification_init(pDataBuffer->pResourceManager, ¬ification); - if (result != MA_SUCCESS) { - return result; /* Failed to create the notification. This should rarely, if ever, happen. */ - } - - job = ma_job_init(MA_JOB_TYPE_RESOURCE_MANAGER_FREE_DATA_BUFFER); - job.order = ma_resource_manager_data_buffer_next_execution_order(pDataBuffer); - job.data.resourceManager.freeDataBuffer.pDataBuffer = pDataBuffer; - job.data.resourceManager.freeDataBuffer.pDoneNotification = ¬ification; - job.data.resourceManager.freeDataBuffer.pDoneFence = NULL; - - result = ma_resource_manager_post_job(pDataBuffer->pResourceManager, &job); - if (result != MA_SUCCESS) { - ma_resource_manager_inline_notification_uninit(¬ification); - return result; - } - - ma_resource_manager_inline_notification_wait_and_uninit(¬ification); - } - - return result; -} - -MA_API ma_result ma_resource_manager_data_buffer_read_pcm_frames(ma_resource_manager_data_buffer* pDataBuffer, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead) -{ - ma_result result = MA_SUCCESS; - ma_uint64 framesRead = 0; - ma_bool32 isDecodedBufferBusy = MA_FALSE; - - /* Safety. */ - if (pFramesRead != NULL) { - *pFramesRead = 0; - } - - if (frameCount == 0) { - return MA_INVALID_ARGS; - } - - /* - We cannot be using the data buffer after it's been uninitialized. If you trigger this assert it means you're trying to read from the data buffer after - it's been uninitialized or is in the process of uninitializing. - */ - MA_ASSERT(ma_resource_manager_data_buffer_node_result(pDataBuffer->pNode) != MA_UNAVAILABLE); - - /* If the node is not initialized we need to abort with a busy code. */ - if (ma_resource_manager_data_buffer_has_connector(pDataBuffer) == MA_FALSE) { - return MA_BUSY; /* Still loading. */ - } - - /* - If we've got a seek scheduled we'll want to do that before reading. However, for paged buffers, there's - a chance that the sound hasn't yet been decoded up to the seek point will result in the seek failing. If - this happens, we need to keep the seek scheduled and return MA_BUSY. - */ - if (pDataBuffer->seekToCursorOnNextRead) { - pDataBuffer->seekToCursorOnNextRead = MA_FALSE; - - result = ma_data_source_seek_to_pcm_frame(ma_resource_manager_data_buffer_get_connector(pDataBuffer), pDataBuffer->seekTargetInPCMFrames); - if (result != MA_SUCCESS) { - if (result == MA_BAD_SEEK && ma_resource_manager_data_buffer_node_get_data_supply_type(pDataBuffer->pNode) == ma_resource_manager_data_supply_type_decoded_paged) { - pDataBuffer->seekToCursorOnNextRead = MA_TRUE; /* Keep the seek scheduled. We just haven't loaded enough data yet to do the seek properly. */ - return MA_BUSY; - } - - return result; - } - } - - /* - For decoded buffers (not paged) we need to check beforehand how many frames we have available. We cannot - exceed this amount. We'll read as much as we can, and then return MA_BUSY. - */ - if (ma_resource_manager_data_buffer_node_get_data_supply_type(pDataBuffer->pNode) == ma_resource_manager_data_supply_type_decoded) { - ma_uint64 availableFrames; - - isDecodedBufferBusy = (ma_resource_manager_data_buffer_node_result(pDataBuffer->pNode) == MA_BUSY); - - if (ma_resource_manager_data_buffer_get_available_frames(pDataBuffer, &availableFrames) == MA_SUCCESS) { - /* Don't try reading more than the available frame count if the data buffer node is still loading. */ - if (isDecodedBufferBusy) { - if (frameCount > availableFrames) { - frameCount = availableFrames; - - /* - If there's no frames available we want to set the status to MA_AT_END. The logic below - will check if the node is busy, and if so, change it to MA_BUSY. The reason we do this - is because we don't want to call `ma_data_source_read_pcm_frames()` if the frame count - is 0 because that'll result in a situation where it's possible MA_AT_END won't get - returned. - */ - if (frameCount == 0) { - result = MA_AT_END; - } - } else { - isDecodedBufferBusy = MA_FALSE; /* We have enough frames available in the buffer to avoid a MA_BUSY status. */ - } - } else { - /* - Getting here means the buffer has been fully loaded. We can just pass the frame count straight - into ma_data_source_read_pcm_frames() below and let ma_data_source handle it. - */ - } - } - } - - /* Don't attempt to read anything if we've got no frames available. */ - if (frameCount > 0) { - result = ma_data_source_read_pcm_frames(ma_resource_manager_data_buffer_get_connector(pDataBuffer), pFramesOut, frameCount, &framesRead); - } - - /* - If we returned MA_AT_END, but the node is still loading, we don't want to return that code or else the caller will interpret the sound - as at the end and terminate decoding. - */ - if (result == MA_AT_END) { - if (ma_resource_manager_data_buffer_node_result(pDataBuffer->pNode) == MA_BUSY) { - result = MA_BUSY; - } - } - - if (isDecodedBufferBusy) { - result = MA_BUSY; - } - - if (pFramesRead != NULL) { - *pFramesRead = framesRead; - } - - if (result == MA_SUCCESS && framesRead == 0) { - result = MA_AT_END; - } - - return result; -} - -MA_API ma_result ma_resource_manager_data_buffer_seek_to_pcm_frame(ma_resource_manager_data_buffer* pDataBuffer, ma_uint64 frameIndex) -{ - ma_result result; - - /* We cannot be using the data source after it's been uninitialized. */ - MA_ASSERT(ma_resource_manager_data_buffer_node_result(pDataBuffer->pNode) != MA_UNAVAILABLE); - - /* If we haven't yet got a connector we need to abort. */ - if (ma_resource_manager_data_buffer_has_connector(pDataBuffer) == MA_FALSE) { - pDataBuffer->seekTargetInPCMFrames = frameIndex; - pDataBuffer->seekToCursorOnNextRead = MA_TRUE; - return MA_BUSY; /* Still loading. */ - } - - result = ma_data_source_seek_to_pcm_frame(ma_resource_manager_data_buffer_get_connector(pDataBuffer), frameIndex); - if (result != MA_SUCCESS) { - return result; - } - - pDataBuffer->seekTargetInPCMFrames = ~(ma_uint64)0; /* <-- For identification purposes. */ - pDataBuffer->seekToCursorOnNextRead = MA_FALSE; - - return MA_SUCCESS; -} - -MA_API ma_result ma_resource_manager_data_buffer_get_data_format(ma_resource_manager_data_buffer* pDataBuffer, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate, ma_channel* pChannelMap, size_t channelMapCap) -{ - /* We cannot be using the data source after it's been uninitialized. */ - MA_ASSERT(ma_resource_manager_data_buffer_node_result(pDataBuffer->pNode) != MA_UNAVAILABLE); - - switch (ma_resource_manager_data_buffer_node_get_data_supply_type(pDataBuffer->pNode)) - { - case ma_resource_manager_data_supply_type_encoded: - { - return ma_data_source_get_data_format(&pDataBuffer->connector.decoder, pFormat, pChannels, pSampleRate, pChannelMap, channelMapCap); - }; - - case ma_resource_manager_data_supply_type_decoded: - { - *pFormat = pDataBuffer->pNode->data.backend.decoded.format; - *pChannels = pDataBuffer->pNode->data.backend.decoded.channels; - *pSampleRate = pDataBuffer->pNode->data.backend.decoded.sampleRate; - ma_channel_map_init_standard(ma_standard_channel_map_default, pChannelMap, channelMapCap, pDataBuffer->pNode->data.backend.decoded.channels); - return MA_SUCCESS; - }; - - case ma_resource_manager_data_supply_type_decoded_paged: - { - *pFormat = pDataBuffer->pNode->data.backend.decodedPaged.data.format; - *pChannels = pDataBuffer->pNode->data.backend.decodedPaged.data.channels; - *pSampleRate = pDataBuffer->pNode->data.backend.decodedPaged.sampleRate; - ma_channel_map_init_standard(ma_standard_channel_map_default, pChannelMap, channelMapCap, pDataBuffer->pNode->data.backend.decoded.channels); - return MA_SUCCESS; - }; - - case ma_resource_manager_data_supply_type_unknown: - { - return MA_BUSY; /* Still loading. */ - }; - - default: - { - /* Unknown supply type. Should never hit this. */ - return MA_INVALID_ARGS; - } - } -} - -MA_API ma_result ma_resource_manager_data_buffer_get_cursor_in_pcm_frames(ma_resource_manager_data_buffer* pDataBuffer, ma_uint64* pCursor) -{ - /* We cannot be using the data source after it's been uninitialized. */ - MA_ASSERT(ma_resource_manager_data_buffer_node_result(pDataBuffer->pNode) != MA_UNAVAILABLE); - - if (pDataBuffer == NULL || pCursor == NULL) { - return MA_INVALID_ARGS; - } - - *pCursor = 0; - - switch (ma_resource_manager_data_buffer_node_get_data_supply_type(pDataBuffer->pNode)) - { - case ma_resource_manager_data_supply_type_encoded: - { - return ma_decoder_get_cursor_in_pcm_frames(&pDataBuffer->connector.decoder, pCursor); - }; - - case ma_resource_manager_data_supply_type_decoded: - { - return ma_audio_buffer_get_cursor_in_pcm_frames(&pDataBuffer->connector.buffer, pCursor); - }; - - case ma_resource_manager_data_supply_type_decoded_paged: - { - return ma_paged_audio_buffer_get_cursor_in_pcm_frames(&pDataBuffer->connector.pagedBuffer, pCursor); - }; - - case ma_resource_manager_data_supply_type_unknown: - { - return MA_BUSY; - }; - - default: - { - return MA_INVALID_ARGS; - } - } -} - -MA_API ma_result ma_resource_manager_data_buffer_get_length_in_pcm_frames(ma_resource_manager_data_buffer* pDataBuffer, ma_uint64* pLength) -{ - /* We cannot be using the data source after it's been uninitialized. */ - MA_ASSERT(ma_resource_manager_data_buffer_node_result(pDataBuffer->pNode) != MA_UNAVAILABLE); - - if (pDataBuffer == NULL || pLength == NULL) { - return MA_INVALID_ARGS; - } - - if (ma_resource_manager_data_buffer_node_get_data_supply_type(pDataBuffer->pNode) == ma_resource_manager_data_supply_type_unknown) { - return MA_BUSY; /* Still loading. */ - } - - return ma_data_source_get_length_in_pcm_frames(ma_resource_manager_data_buffer_get_connector(pDataBuffer), pLength); -} - -MA_API ma_result ma_resource_manager_data_buffer_result(const ma_resource_manager_data_buffer* pDataBuffer) -{ - if (pDataBuffer == NULL) { - return MA_INVALID_ARGS; - } - - return (ma_result)ma_atomic_load_i32((ma_result*)&pDataBuffer->result); /* Need a naughty const-cast here. */ -} - -MA_API ma_result ma_resource_manager_data_buffer_set_looping(ma_resource_manager_data_buffer* pDataBuffer, ma_bool32 isLooping) -{ - return ma_data_source_set_looping(pDataBuffer, isLooping); -} - -MA_API ma_bool32 ma_resource_manager_data_buffer_is_looping(const ma_resource_manager_data_buffer* pDataBuffer) -{ - return ma_data_source_is_looping(pDataBuffer); -} - -MA_API ma_result ma_resource_manager_data_buffer_get_available_frames(ma_resource_manager_data_buffer* pDataBuffer, ma_uint64* pAvailableFrames) -{ - if (pAvailableFrames == NULL) { - return MA_INVALID_ARGS; - } - - *pAvailableFrames = 0; - - if (pDataBuffer == NULL) { - return MA_INVALID_ARGS; - } - - if (ma_resource_manager_data_buffer_node_get_data_supply_type(pDataBuffer->pNode) == ma_resource_manager_data_supply_type_unknown) { - if (ma_resource_manager_data_buffer_node_result(pDataBuffer->pNode) == MA_BUSY) { - return MA_BUSY; - } else { - return MA_INVALID_OPERATION; /* No connector. */ - } - } - - switch (ma_resource_manager_data_buffer_node_get_data_supply_type(pDataBuffer->pNode)) - { - case ma_resource_manager_data_supply_type_encoded: - { - return ma_decoder_get_available_frames(&pDataBuffer->connector.decoder, pAvailableFrames); - }; - - case ma_resource_manager_data_supply_type_decoded: - { - return ma_audio_buffer_get_available_frames(&pDataBuffer->connector.buffer, pAvailableFrames); - }; - - case ma_resource_manager_data_supply_type_decoded_paged: - { - ma_uint64 cursor; - ma_paged_audio_buffer_get_cursor_in_pcm_frames(&pDataBuffer->connector.pagedBuffer, &cursor); - - if (pDataBuffer->pNode->data.backend.decodedPaged.decodedFrameCount > cursor) { - *pAvailableFrames = pDataBuffer->pNode->data.backend.decodedPaged.decodedFrameCount - cursor; - } else { - *pAvailableFrames = 0; - } - - return MA_SUCCESS; - }; - - case ma_resource_manager_data_supply_type_unknown: - default: - { - /* Unknown supply type. Should never hit this. */ - return MA_INVALID_ARGS; - } - } -} - -MA_API ma_result ma_resource_manager_register_file(ma_resource_manager* pResourceManager, const char* pFilePath, ma_uint32 flags) -{ - return ma_resource_manager_data_buffer_node_acquire(pResourceManager, pFilePath, NULL, 0, flags, NULL, NULL, NULL, NULL); -} - -MA_API ma_result ma_resource_manager_register_file_w(ma_resource_manager* pResourceManager, const wchar_t* pFilePath, ma_uint32 flags) -{ - return ma_resource_manager_data_buffer_node_acquire(pResourceManager, NULL, pFilePath, 0, flags, NULL, NULL, NULL, NULL); -} - - -static ma_result ma_resource_manager_register_data(ma_resource_manager* pResourceManager, const char* pName, const wchar_t* pNameW, ma_resource_manager_data_supply* pExistingData) -{ - return ma_resource_manager_data_buffer_node_acquire(pResourceManager, pName, pNameW, 0, 0, pExistingData, NULL, NULL, NULL); -} - -static ma_result ma_resource_manager_register_decoded_data_internal(ma_resource_manager* pResourceManager, const char* pName, const wchar_t* pNameW, const void* pData, ma_uint64 frameCount, ma_format format, ma_uint32 channels, ma_uint32 sampleRate) -{ - ma_resource_manager_data_supply data; - data.type = ma_resource_manager_data_supply_type_decoded; - data.backend.decoded.pData = pData; - data.backend.decoded.totalFrameCount = frameCount; - data.backend.decoded.format = format; - data.backend.decoded.channels = channels; - data.backend.decoded.sampleRate = sampleRate; - - return ma_resource_manager_register_data(pResourceManager, pName, pNameW, &data); -} - -MA_API ma_result ma_resource_manager_register_decoded_data(ma_resource_manager* pResourceManager, const char* pName, const void* pData, ma_uint64 frameCount, ma_format format, ma_uint32 channels, ma_uint32 sampleRate) -{ - return ma_resource_manager_register_decoded_data_internal(pResourceManager, pName, NULL, pData, frameCount, format, channels, sampleRate); -} - -MA_API ma_result ma_resource_manager_register_decoded_data_w(ma_resource_manager* pResourceManager, const wchar_t* pName, const void* pData, ma_uint64 frameCount, ma_format format, ma_uint32 channels, ma_uint32 sampleRate) -{ - return ma_resource_manager_register_decoded_data_internal(pResourceManager, NULL, pName, pData, frameCount, format, channels, sampleRate); -} - - -static ma_result ma_resource_manager_register_encoded_data_internal(ma_resource_manager* pResourceManager, const char* pName, const wchar_t* pNameW, const void* pData, size_t sizeInBytes) -{ - ma_resource_manager_data_supply data; - data.type = ma_resource_manager_data_supply_type_encoded; - data.backend.encoded.pData = pData; - data.backend.encoded.sizeInBytes = sizeInBytes; - - return ma_resource_manager_register_data(pResourceManager, pName, pNameW, &data); -} - -MA_API ma_result ma_resource_manager_register_encoded_data(ma_resource_manager* pResourceManager, const char* pName, const void* pData, size_t sizeInBytes) -{ - return ma_resource_manager_register_encoded_data_internal(pResourceManager, pName, NULL, pData, sizeInBytes); -} - -MA_API ma_result ma_resource_manager_register_encoded_data_w(ma_resource_manager* pResourceManager, const wchar_t* pName, const void* pData, size_t sizeInBytes) -{ - return ma_resource_manager_register_encoded_data_internal(pResourceManager, NULL, pName, pData, sizeInBytes); -} - - -MA_API ma_result ma_resource_manager_unregister_file(ma_resource_manager* pResourceManager, const char* pFilePath) -{ - return ma_resource_manager_unregister_data(pResourceManager, pFilePath); -} - -MA_API ma_result ma_resource_manager_unregister_file_w(ma_resource_manager* pResourceManager, const wchar_t* pFilePath) -{ - return ma_resource_manager_unregister_data_w(pResourceManager, pFilePath); -} - -MA_API ma_result ma_resource_manager_unregister_data(ma_resource_manager* pResourceManager, const char* pName) -{ - return ma_resource_manager_data_buffer_node_unacquire(pResourceManager, NULL, pName, NULL); -} - -MA_API ma_result ma_resource_manager_unregister_data_w(ma_resource_manager* pResourceManager, const wchar_t* pName) -{ - return ma_resource_manager_data_buffer_node_unacquire(pResourceManager, NULL, NULL, pName); -} - - -static ma_uint32 ma_resource_manager_data_stream_next_execution_order(ma_resource_manager_data_stream* pDataStream) -{ - MA_ASSERT(pDataStream != NULL); - return ma_atomic_fetch_add_32(&pDataStream->executionCounter, 1); -} - -static ma_bool32 ma_resource_manager_data_stream_is_decoder_at_end(const ma_resource_manager_data_stream* pDataStream) -{ - MA_ASSERT(pDataStream != NULL); - return ma_atomic_load_32((ma_bool32*)&pDataStream->isDecoderAtEnd); -} - -static ma_uint32 ma_resource_manager_data_stream_seek_counter(const ma_resource_manager_data_stream* pDataStream) -{ - MA_ASSERT(pDataStream != NULL); - return ma_atomic_load_32((ma_uint32*)&pDataStream->seekCounter); -} - - -static ma_result ma_resource_manager_data_stream_cb__read_pcm_frames(ma_data_source* pDataSource, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead) -{ - return ma_resource_manager_data_stream_read_pcm_frames((ma_resource_manager_data_stream*)pDataSource, pFramesOut, frameCount, pFramesRead); -} - -static ma_result ma_resource_manager_data_stream_cb__seek_to_pcm_frame(ma_data_source* pDataSource, ma_uint64 frameIndex) -{ - return ma_resource_manager_data_stream_seek_to_pcm_frame((ma_resource_manager_data_stream*)pDataSource, frameIndex); -} - -static ma_result ma_resource_manager_data_stream_cb__get_data_format(ma_data_source* pDataSource, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate, ma_channel* pChannelMap, size_t channelMapCap) -{ - return ma_resource_manager_data_stream_get_data_format((ma_resource_manager_data_stream*)pDataSource, pFormat, pChannels, pSampleRate, pChannelMap, channelMapCap); -} - -static ma_result ma_resource_manager_data_stream_cb__get_cursor_in_pcm_frames(ma_data_source* pDataSource, ma_uint64* pCursor) -{ - return ma_resource_manager_data_stream_get_cursor_in_pcm_frames((ma_resource_manager_data_stream*)pDataSource, pCursor); -} - -static ma_result ma_resource_manager_data_stream_cb__get_length_in_pcm_frames(ma_data_source* pDataSource, ma_uint64* pLength) -{ - return ma_resource_manager_data_stream_get_length_in_pcm_frames((ma_resource_manager_data_stream*)pDataSource, pLength); -} - -static ma_result ma_resource_manager_data_stream_cb__set_looping(ma_data_source* pDataSource, ma_bool32 isLooping) -{ - ma_resource_manager_data_stream* pDataStream = (ma_resource_manager_data_stream*)pDataSource; - MA_ASSERT(pDataStream != NULL); - - ma_atomic_exchange_32(&pDataStream->isLooping, isLooping); - - return MA_SUCCESS; -} - -static ma_data_source_vtable g_ma_resource_manager_data_stream_vtable = -{ - ma_resource_manager_data_stream_cb__read_pcm_frames, - ma_resource_manager_data_stream_cb__seek_to_pcm_frame, - ma_resource_manager_data_stream_cb__get_data_format, - ma_resource_manager_data_stream_cb__get_cursor_in_pcm_frames, - ma_resource_manager_data_stream_cb__get_length_in_pcm_frames, - ma_resource_manager_data_stream_cb__set_looping, - 0 /*MA_DATA_SOURCE_SELF_MANAGED_RANGE_AND_LOOP_POINT*/ -}; - -static void ma_resource_manager_data_stream_set_absolute_cursor(ma_resource_manager_data_stream* pDataStream, ma_uint64 absoluteCursor) -{ - /* Loop if possible. */ - if (absoluteCursor > pDataStream->totalLengthInPCMFrames && pDataStream->totalLengthInPCMFrames > 0) { - absoluteCursor = absoluteCursor % pDataStream->totalLengthInPCMFrames; - } - - ma_atomic_exchange_64(&pDataStream->absoluteCursor, absoluteCursor); -} - -MA_API ma_result ma_resource_manager_data_stream_init_ex(ma_resource_manager* pResourceManager, const ma_resource_manager_data_source_config* pConfig, ma_resource_manager_data_stream* pDataStream) -{ - ma_result result; - ma_data_source_config dataSourceConfig; - char* pFilePathCopy = NULL; - wchar_t* pFilePathWCopy = NULL; - ma_job job; - ma_bool32 waitBeforeReturning = MA_FALSE; - ma_resource_manager_inline_notification waitNotification; - ma_resource_manager_pipeline_notifications notifications; - ma_uint32 flags; - - if (pDataStream == NULL) { - if (pConfig != NULL && pConfig->pNotifications != NULL) { - ma_resource_manager_pipeline_notifications_signal_all_notifications(pConfig->pNotifications); - } - - return MA_INVALID_ARGS; - } - - MA_ZERO_OBJECT(pDataStream); - - if (pConfig == NULL) { - return MA_INVALID_ARGS; - } - - if (pConfig->pNotifications != NULL) { - notifications = *pConfig->pNotifications; /* From here on out, `notifications` should be used instead of `pNotifications`. Setting this to NULL to catch any errors at testing time. */ - } else { - MA_ZERO_OBJECT(¬ifications); - } - - dataSourceConfig = ma_data_source_config_init(); - dataSourceConfig.vtable = &g_ma_resource_manager_data_stream_vtable; - - result = ma_data_source_init(&dataSourceConfig, &pDataStream->ds); - if (result != MA_SUCCESS) { - ma_resource_manager_pipeline_notifications_signal_all_notifications(¬ifications); - return result; - } - - flags = pConfig->flags; - if (pConfig->isLooping) { - flags |= MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_LOOPING; - } - - pDataStream->pResourceManager = pResourceManager; - pDataStream->flags = pConfig->flags; - pDataStream->result = MA_BUSY; - - ma_data_source_set_range_in_pcm_frames(pDataStream, pConfig->rangeBegInPCMFrames, pConfig->rangeEndInPCMFrames); - ma_data_source_set_loop_point_in_pcm_frames(pDataStream, pConfig->loopPointBegInPCMFrames, pConfig->loopPointEndInPCMFrames); - ma_data_source_set_looping(pDataStream, (flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_LOOPING) != 0); - - if (pResourceManager == NULL || (pConfig->pFilePath == NULL && pConfig->pFilePathW == NULL)) { - ma_resource_manager_pipeline_notifications_signal_all_notifications(¬ifications); - return MA_INVALID_ARGS; - } - - /* We want all access to the VFS and the internal decoder to happen on the job thread just to keep things easier to manage for the VFS. */ - - /* We need a copy of the file path. We should probably make this more efficient, but for now we'll do a transient memory allocation. */ - if (pConfig->pFilePath != NULL) { - pFilePathCopy = ma_copy_string(pConfig->pFilePath, &pResourceManager->config.allocationCallbacks); - } else { - pFilePathWCopy = ma_copy_string_w(pConfig->pFilePathW, &pResourceManager->config.allocationCallbacks); - } - - if (pFilePathCopy == NULL && pFilePathWCopy == NULL) { - ma_resource_manager_pipeline_notifications_signal_all_notifications(¬ifications); - return MA_OUT_OF_MEMORY; - } - - /* - We need to check for the presence of MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_ASYNC. If it's not set, we need to wait before returning. Otherwise we - can return immediately. Likewise, we'll also check for MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_WAIT_INIT and do the same. - */ - if ((pConfig->flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_ASYNC) == 0 || (pConfig->flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_WAIT_INIT) != 0) { - waitBeforeReturning = MA_TRUE; - ma_resource_manager_inline_notification_init(pResourceManager, &waitNotification); - } - - ma_resource_manager_pipeline_notifications_acquire_all_fences(¬ifications); - - /* Set the absolute cursor to our initial seek position so retrieval of the cursor returns a good value. */ - ma_resource_manager_data_stream_set_absolute_cursor(pDataStream, pConfig->initialSeekPointInPCMFrames); - - /* We now have everything we need to post the job. This is the last thing we need to do from here. The rest will be done by the job thread. */ - job = ma_job_init(MA_JOB_TYPE_RESOURCE_MANAGER_LOAD_DATA_STREAM); - job.order = ma_resource_manager_data_stream_next_execution_order(pDataStream); - job.data.resourceManager.loadDataStream.pDataStream = pDataStream; - job.data.resourceManager.loadDataStream.pFilePath = pFilePathCopy; - job.data.resourceManager.loadDataStream.pFilePathW = pFilePathWCopy; - job.data.resourceManager.loadDataStream.initialSeekPoint = pConfig->initialSeekPointInPCMFrames; - job.data.resourceManager.loadDataStream.pInitNotification = (waitBeforeReturning == MA_TRUE) ? &waitNotification : notifications.init.pNotification; - job.data.resourceManager.loadDataStream.pInitFence = notifications.init.pFence; - result = ma_resource_manager_post_job(pResourceManager, &job); - if (result != MA_SUCCESS) { - ma_resource_manager_pipeline_notifications_signal_all_notifications(¬ifications); - ma_resource_manager_pipeline_notifications_release_all_fences(¬ifications); - - if (waitBeforeReturning) { - ma_resource_manager_inline_notification_uninit(&waitNotification); - } - - ma_free(pFilePathCopy, &pResourceManager->config.allocationCallbacks); - ma_free(pFilePathWCopy, &pResourceManager->config.allocationCallbacks); - return result; - } - - /* Wait if needed. */ - if (waitBeforeReturning) { - ma_resource_manager_inline_notification_wait_and_uninit(&waitNotification); - - if (notifications.init.pNotification != NULL) { - ma_async_notification_signal(notifications.init.pNotification); - } - - /* - If there was an error during initialization make sure we return that result here. We don't want to do this - if we're not waiting because it will most likely be in a busy state. - */ - if (pDataStream->result != MA_SUCCESS) { - return pDataStream->result; - } - - /* NOTE: Do not release pInitFence here. That will be done by the job. */ - } - - return MA_SUCCESS; -} - -MA_API ma_result ma_resource_manager_data_stream_init(ma_resource_manager* pResourceManager, const char* pFilePath, ma_uint32 flags, const ma_resource_manager_pipeline_notifications* pNotifications, ma_resource_manager_data_stream* pDataStream) -{ - ma_resource_manager_data_source_config config; - - config = ma_resource_manager_data_source_config_init(); - config.pFilePath = pFilePath; - config.flags = flags; - config.pNotifications = pNotifications; - - return ma_resource_manager_data_stream_init_ex(pResourceManager, &config, pDataStream); -} - -MA_API ma_result ma_resource_manager_data_stream_init_w(ma_resource_manager* pResourceManager, const wchar_t* pFilePath, ma_uint32 flags, const ma_resource_manager_pipeline_notifications* pNotifications, ma_resource_manager_data_stream* pDataStream) -{ - ma_resource_manager_data_source_config config; - - config = ma_resource_manager_data_source_config_init(); - config.pFilePathW = pFilePath; - config.flags = flags; - config.pNotifications = pNotifications; - - return ma_resource_manager_data_stream_init_ex(pResourceManager, &config, pDataStream); -} - -MA_API ma_result ma_resource_manager_data_stream_uninit(ma_resource_manager_data_stream* pDataStream) -{ - ma_resource_manager_inline_notification freeEvent; - ma_job job; - - if (pDataStream == NULL) { - return MA_INVALID_ARGS; - } - - /* The first thing to do is set the result to unavailable. This will prevent future page decoding. */ - ma_atomic_exchange_i32(&pDataStream->result, MA_UNAVAILABLE); - - /* - We need to post a job to ensure we're not in the middle or decoding or anything. Because the object is owned by the caller, we'll need - to wait for it to complete before returning which means we need an event. - */ - ma_resource_manager_inline_notification_init(pDataStream->pResourceManager, &freeEvent); - - job = ma_job_init(MA_JOB_TYPE_RESOURCE_MANAGER_FREE_DATA_STREAM); - job.order = ma_resource_manager_data_stream_next_execution_order(pDataStream); - job.data.resourceManager.freeDataStream.pDataStream = pDataStream; - job.data.resourceManager.freeDataStream.pDoneNotification = &freeEvent; - job.data.resourceManager.freeDataStream.pDoneFence = NULL; - ma_resource_manager_post_job(pDataStream->pResourceManager, &job); - - /* We need to wait for the job to finish processing before we return. */ - ma_resource_manager_inline_notification_wait_and_uninit(&freeEvent); - - return MA_SUCCESS; -} - - -static ma_uint32 ma_resource_manager_data_stream_get_page_size_in_frames(ma_resource_manager_data_stream* pDataStream) -{ - MA_ASSERT(pDataStream != NULL); - MA_ASSERT(pDataStream->isDecoderInitialized == MA_TRUE); - - return MA_RESOURCE_MANAGER_PAGE_SIZE_IN_MILLISECONDS * (pDataStream->decoder.outputSampleRate/1000); -} - -static void* ma_resource_manager_data_stream_get_page_data_pointer(ma_resource_manager_data_stream* pDataStream, ma_uint32 pageIndex, ma_uint32 relativeCursor) -{ - MA_ASSERT(pDataStream != NULL); - MA_ASSERT(pDataStream->isDecoderInitialized == MA_TRUE); - MA_ASSERT(pageIndex == 0 || pageIndex == 1); - - return ma_offset_ptr(pDataStream->pPageData, ((ma_resource_manager_data_stream_get_page_size_in_frames(pDataStream) * pageIndex) + relativeCursor) * ma_get_bytes_per_frame(pDataStream->decoder.outputFormat, pDataStream->decoder.outputChannels)); -} - -static void ma_resource_manager_data_stream_fill_page(ma_resource_manager_data_stream* pDataStream, ma_uint32 pageIndex) -{ - ma_result result = MA_SUCCESS; - ma_uint64 pageSizeInFrames; - ma_uint64 totalFramesReadForThisPage = 0; - void* pPageData = ma_resource_manager_data_stream_get_page_data_pointer(pDataStream, pageIndex, 0); - - pageSizeInFrames = ma_resource_manager_data_stream_get_page_size_in_frames(pDataStream); - - /* The decoder needs to inherit the stream's looping and range state. */ - { - ma_uint64 rangeBeg; - ma_uint64 rangeEnd; - ma_uint64 loopPointBeg; - ma_uint64 loopPointEnd; - - ma_data_source_set_looping(&pDataStream->decoder, ma_resource_manager_data_stream_is_looping(pDataStream)); - - ma_data_source_get_range_in_pcm_frames(pDataStream, &rangeBeg, &rangeEnd); - ma_data_source_set_range_in_pcm_frames(&pDataStream->decoder, rangeBeg, rangeEnd); - - ma_data_source_get_loop_point_in_pcm_frames(pDataStream, &loopPointBeg, &loopPointEnd); - ma_data_source_set_loop_point_in_pcm_frames(&pDataStream->decoder, loopPointBeg, loopPointEnd); - } - - /* Just read straight from the decoder. It will deal with ranges and looping for us. */ - result = ma_data_source_read_pcm_frames(&pDataStream->decoder, pPageData, pageSizeInFrames, &totalFramesReadForThisPage); - if (result == MA_AT_END || totalFramesReadForThisPage < pageSizeInFrames) { - ma_atomic_exchange_32(&pDataStream->isDecoderAtEnd, MA_TRUE); - } - - ma_atomic_exchange_32(&pDataStream->pageFrameCount[pageIndex], (ma_uint32)totalFramesReadForThisPage); - ma_atomic_exchange_32(&pDataStream->isPageValid[pageIndex], MA_TRUE); -} - -static void ma_resource_manager_data_stream_fill_pages(ma_resource_manager_data_stream* pDataStream) -{ - ma_uint32 iPage; - - MA_ASSERT(pDataStream != NULL); - - for (iPage = 0; iPage < 2; iPage += 1) { - ma_resource_manager_data_stream_fill_page(pDataStream, iPage); - } -} - - -static ma_result ma_resource_manager_data_stream_map(ma_resource_manager_data_stream* pDataStream, void** ppFramesOut, ma_uint64* pFrameCount) -{ - ma_uint64 framesAvailable; - ma_uint64 frameCount = 0; - - /* We cannot be using the data source after it's been uninitialized. */ - MA_ASSERT(ma_resource_manager_data_stream_result(pDataStream) != MA_UNAVAILABLE); - - if (pFrameCount != NULL) { - frameCount = *pFrameCount; - *pFrameCount = 0; - } - if (ppFramesOut != NULL) { - *ppFramesOut = NULL; - } - - if (pDataStream == NULL || ppFramesOut == NULL || pFrameCount == NULL) { - return MA_INVALID_ARGS; - } - - if (ma_resource_manager_data_stream_result(pDataStream) != MA_SUCCESS) { - return MA_INVALID_OPERATION; - } - - /* Don't attempt to read while we're in the middle of seeking. Tell the caller that we're busy. */ - if (ma_resource_manager_data_stream_seek_counter(pDataStream) > 0) { - return MA_BUSY; - } - - /* If the page we're on is invalid it means we've caught up to the job thread. */ - if (ma_atomic_load_32(&pDataStream->isPageValid[pDataStream->currentPageIndex]) == MA_FALSE) { - framesAvailable = 0; - } else { - /* - The page we're on is valid so we must have some frames available. We need to make sure that we don't overflow into the next page, even if it's valid. The reason is - that the unmap process will only post an update for one page at a time. Keeping mapping tied to page boundaries makes this simpler. - */ - ma_uint32 currentPageFrameCount = ma_atomic_load_32(&pDataStream->pageFrameCount[pDataStream->currentPageIndex]); - MA_ASSERT(currentPageFrameCount >= pDataStream->relativeCursor); - - framesAvailable = currentPageFrameCount - pDataStream->relativeCursor; - } - - /* If there's no frames available and the result is set to MA_AT_END we need to return MA_AT_END. */ - if (framesAvailable == 0) { - if (ma_resource_manager_data_stream_is_decoder_at_end(pDataStream)) { - return MA_AT_END; - } else { - return MA_BUSY; /* There are no frames available, but we're not marked as EOF so we might have caught up to the job thread. Need to return MA_BUSY and wait for more data. */ - } - } - - MA_ASSERT(framesAvailable > 0); - - if (frameCount > framesAvailable) { - frameCount = framesAvailable; - } - - *ppFramesOut = ma_resource_manager_data_stream_get_page_data_pointer(pDataStream, pDataStream->currentPageIndex, pDataStream->relativeCursor); - *pFrameCount = frameCount; - - return MA_SUCCESS; -} - -static ma_result ma_resource_manager_data_stream_unmap(ma_resource_manager_data_stream* pDataStream, ma_uint64 frameCount) -{ - ma_uint32 newRelativeCursor; - ma_uint32 pageSizeInFrames; - ma_job job; - - /* We cannot be using the data source after it's been uninitialized. */ - MA_ASSERT(ma_resource_manager_data_stream_result(pDataStream) != MA_UNAVAILABLE); - - if (pDataStream == NULL) { - return MA_INVALID_ARGS; - } - - if (ma_resource_manager_data_stream_result(pDataStream) != MA_SUCCESS) { - return MA_INVALID_OPERATION; - } - - /* The frame count should always fit inside a 32-bit integer. */ - if (frameCount > 0xFFFFFFFF) { - return MA_INVALID_ARGS; - } - - pageSizeInFrames = ma_resource_manager_data_stream_get_page_size_in_frames(pDataStream); - - /* The absolute cursor needs to be updated for ma_resource_manager_data_stream_get_cursor_in_pcm_frames(). */ - ma_resource_manager_data_stream_set_absolute_cursor(pDataStream, ma_atomic_load_64(&pDataStream->absoluteCursor) + frameCount); - - /* Here is where we need to check if we need to load a new page, and if so, post a job to load it. */ - newRelativeCursor = pDataStream->relativeCursor + (ma_uint32)frameCount; - - /* If the new cursor has flowed over to the next page we need to mark the old one as invalid and post an event for it. */ - if (newRelativeCursor >= pageSizeInFrames) { - newRelativeCursor -= pageSizeInFrames; - - /* Here is where we post the job start decoding. */ - job = ma_job_init(MA_JOB_TYPE_RESOURCE_MANAGER_PAGE_DATA_STREAM); - job.order = ma_resource_manager_data_stream_next_execution_order(pDataStream); - job.data.resourceManager.pageDataStream.pDataStream = pDataStream; - job.data.resourceManager.pageDataStream.pageIndex = pDataStream->currentPageIndex; - - /* The page needs to be marked as invalid so that the public API doesn't try reading from it. */ - ma_atomic_exchange_32(&pDataStream->isPageValid[pDataStream->currentPageIndex], MA_FALSE); - - /* Before posting the job we need to make sure we set some state. */ - pDataStream->relativeCursor = newRelativeCursor; - pDataStream->currentPageIndex = (pDataStream->currentPageIndex + 1) & 0x01; - return ma_resource_manager_post_job(pDataStream->pResourceManager, &job); - } else { - /* We haven't moved into a new page so we can just move the cursor forward. */ - pDataStream->relativeCursor = newRelativeCursor; - return MA_SUCCESS; - } -} - - -MA_API ma_result ma_resource_manager_data_stream_read_pcm_frames(ma_resource_manager_data_stream* pDataStream, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead) -{ - ma_result result = MA_SUCCESS; - ma_uint64 totalFramesProcessed; - ma_format format; - ma_uint32 channels; - - /* Safety. */ - if (pFramesRead != NULL) { - *pFramesRead = 0; - } - - if (frameCount == 0) { - return MA_INVALID_ARGS; - } - - /* We cannot be using the data source after it's been uninitialized. */ - MA_ASSERT(ma_resource_manager_data_stream_result(pDataStream) != MA_UNAVAILABLE); - - if (pDataStream == NULL) { - return MA_INVALID_ARGS; - } - - if (ma_resource_manager_data_stream_result(pDataStream) != MA_SUCCESS) { - return MA_INVALID_OPERATION; - } - - /* Don't attempt to read while we're in the middle of seeking. Tell the caller that we're busy. */ - if (ma_resource_manager_data_stream_seek_counter(pDataStream) > 0) { - return MA_BUSY; - } - - ma_resource_manager_data_stream_get_data_format(pDataStream, &format, &channels, NULL, NULL, 0); - - /* Reading is implemented in terms of map/unmap. We need to run this in a loop because mapping is clamped against page boundaries. */ - totalFramesProcessed = 0; - while (totalFramesProcessed < frameCount) { - void* pMappedFrames; - ma_uint64 mappedFrameCount; - - mappedFrameCount = frameCount - totalFramesProcessed; - result = ma_resource_manager_data_stream_map(pDataStream, &pMappedFrames, &mappedFrameCount); - if (result != MA_SUCCESS) { - break; - } - - /* Copy the mapped data to the output buffer if we have one. It's allowed for pFramesOut to be NULL in which case a relative forward seek is performed. */ - if (pFramesOut != NULL) { - ma_copy_pcm_frames(ma_offset_pcm_frames_ptr(pFramesOut, totalFramesProcessed, format, channels), pMappedFrames, mappedFrameCount, format, channels); - } - - totalFramesProcessed += mappedFrameCount; - - result = ma_resource_manager_data_stream_unmap(pDataStream, mappedFrameCount); - if (result != MA_SUCCESS) { - break; /* This is really bad - will only get an error here if we failed to post a job to the queue for loading the next page. */ - } - } - - if (pFramesRead != NULL) { - *pFramesRead = totalFramesProcessed; - } - - if (result == MA_SUCCESS && totalFramesProcessed == 0) { - result = MA_AT_END; - } - - return result; -} - -MA_API ma_result ma_resource_manager_data_stream_seek_to_pcm_frame(ma_resource_manager_data_stream* pDataStream, ma_uint64 frameIndex) -{ - ma_job job; - ma_result streamResult; - - streamResult = ma_resource_manager_data_stream_result(pDataStream); - - /* We cannot be using the data source after it's been uninitialized. */ - MA_ASSERT(streamResult != MA_UNAVAILABLE); - - if (pDataStream == NULL) { - return MA_INVALID_ARGS; - } - - if (streamResult != MA_SUCCESS && streamResult != MA_BUSY) { - return MA_INVALID_OPERATION; - } - - /* If we're not already seeking and we're sitting on the same frame, just make this a no-op. */ - if (ma_atomic_load_32(&pDataStream->seekCounter) == 0) { - if (ma_atomic_load_64(&pDataStream->absoluteCursor) == frameIndex) { - return MA_SUCCESS; - } - } - - - /* Increment the seek counter first to indicate to read_paged_pcm_frames() and map_paged_pcm_frames() that we are in the middle of a seek and MA_BUSY should be returned. */ - ma_atomic_fetch_add_32(&pDataStream->seekCounter, 1); - - /* Update the absolute cursor so that ma_resource_manager_data_stream_get_cursor_in_pcm_frames() returns the new position. */ - ma_resource_manager_data_stream_set_absolute_cursor(pDataStream, frameIndex); - - /* - We need to clear our currently loaded pages so that the stream starts playback from the new seek point as soon as possible. These are for the purpose of the public - API and will be ignored by the seek job. The seek job will operate on the assumption that both pages have been marked as invalid and the cursor is at the start of - the first page. - */ - pDataStream->relativeCursor = 0; - pDataStream->currentPageIndex = 0; - ma_atomic_exchange_32(&pDataStream->isPageValid[0], MA_FALSE); - ma_atomic_exchange_32(&pDataStream->isPageValid[1], MA_FALSE); - - /* Make sure the data stream is not marked as at the end or else if we seek in response to hitting the end, we won't be able to read any more data. */ - ma_atomic_exchange_32(&pDataStream->isDecoderAtEnd, MA_FALSE); - - /* - The public API is not allowed to touch the internal decoder so we need to use a job to perform the seek. When seeking, the job thread will assume both pages - are invalid and any content contained within them will be discarded and replaced with newly decoded data. - */ - job = ma_job_init(MA_JOB_TYPE_RESOURCE_MANAGER_SEEK_DATA_STREAM); - job.order = ma_resource_manager_data_stream_next_execution_order(pDataStream); - job.data.resourceManager.seekDataStream.pDataStream = pDataStream; - job.data.resourceManager.seekDataStream.frameIndex = frameIndex; - return ma_resource_manager_post_job(pDataStream->pResourceManager, &job); -} - -MA_API ma_result ma_resource_manager_data_stream_get_data_format(ma_resource_manager_data_stream* pDataStream, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate, ma_channel* pChannelMap, size_t channelMapCap) -{ - /* We cannot be using the data source after it's been uninitialized. */ - MA_ASSERT(ma_resource_manager_data_stream_result(pDataStream) != MA_UNAVAILABLE); - - if (pFormat != NULL) { - *pFormat = ma_format_unknown; - } - - if (pChannels != NULL) { - *pChannels = 0; - } - - if (pSampleRate != NULL) { - *pSampleRate = 0; - } - - if (pChannelMap != NULL) { - MA_ZERO_MEMORY(pChannelMap, sizeof(*pChannelMap) * channelMapCap); - } - - if (pDataStream == NULL) { - return MA_INVALID_ARGS; - } - - if (ma_resource_manager_data_stream_result(pDataStream) != MA_SUCCESS) { - return MA_INVALID_OPERATION; - } - - /* - We're being a little bit naughty here and accessing the internal decoder from the public API. The output data format is constant, and we've defined this function - such that the application is responsible for ensuring it's not called while uninitializing so it should be safe. - */ - return ma_data_source_get_data_format(&pDataStream->decoder, pFormat, pChannels, pSampleRate, pChannelMap, channelMapCap); -} - -MA_API ma_result ma_resource_manager_data_stream_get_cursor_in_pcm_frames(ma_resource_manager_data_stream* pDataStream, ma_uint64* pCursor) -{ - ma_result result; - - if (pCursor == NULL) { - return MA_INVALID_ARGS; - } - - *pCursor = 0; - - /* We cannot be using the data source after it's been uninitialized. */ - MA_ASSERT(ma_resource_manager_data_stream_result(pDataStream) != MA_UNAVAILABLE); - - if (pDataStream == NULL) { - return MA_INVALID_ARGS; - } - - /* - If the stream is in an erroneous state we need to return an invalid operation. We can allow - this to be called when the data stream is in a busy state because the caller may have asked - for an initial seek position and it's convenient to return that as the cursor position. - */ - result = ma_resource_manager_data_stream_result(pDataStream); - if (result != MA_SUCCESS && result != MA_BUSY) { - return MA_INVALID_OPERATION; - } - - *pCursor = ma_atomic_load_64(&pDataStream->absoluteCursor); - - return MA_SUCCESS; -} - -MA_API ma_result ma_resource_manager_data_stream_get_length_in_pcm_frames(ma_resource_manager_data_stream* pDataStream, ma_uint64* pLength) -{ - ma_result streamResult; - - if (pLength == NULL) { - return MA_INVALID_ARGS; - } - - *pLength = 0; - - streamResult = ma_resource_manager_data_stream_result(pDataStream); - - /* We cannot be using the data source after it's been uninitialized. */ - MA_ASSERT(streamResult != MA_UNAVAILABLE); - - if (pDataStream == NULL) { - return MA_INVALID_ARGS; - } - - if (streamResult != MA_SUCCESS) { - return streamResult; - } - - /* - We most definitely do not want to be calling ma_decoder_get_length_in_pcm_frames() directly. Instead we want to use a cached value that we - calculated when we initialized it on the job thread. - */ - *pLength = pDataStream->totalLengthInPCMFrames; - if (*pLength == 0) { - return MA_NOT_IMPLEMENTED; /* Some decoders may not have a known length. */ - } - - return MA_SUCCESS; -} - -MA_API ma_result ma_resource_manager_data_stream_result(const ma_resource_manager_data_stream* pDataStream) -{ - if (pDataStream == NULL) { - return MA_INVALID_ARGS; - } - - return (ma_result)ma_atomic_load_i32(&pDataStream->result); -} - -MA_API ma_result ma_resource_manager_data_stream_set_looping(ma_resource_manager_data_stream* pDataStream, ma_bool32 isLooping) -{ - return ma_data_source_set_looping(pDataStream, isLooping); -} - -MA_API ma_bool32 ma_resource_manager_data_stream_is_looping(const ma_resource_manager_data_stream* pDataStream) -{ - if (pDataStream == NULL) { - return MA_FALSE; - } - - return ma_atomic_load_32((ma_bool32*)&pDataStream->isLooping); /* Naughty const-cast. Value won't change from here in practice (maybe from another thread). */ -} - -MA_API ma_result ma_resource_manager_data_stream_get_available_frames(ma_resource_manager_data_stream* pDataStream, ma_uint64* pAvailableFrames) -{ - ma_uint32 pageIndex0; - ma_uint32 pageIndex1; - ma_uint32 relativeCursor; - ma_uint64 availableFrames; - - if (pAvailableFrames == NULL) { - return MA_INVALID_ARGS; - } - - *pAvailableFrames = 0; - - if (pDataStream == NULL) { - return MA_INVALID_ARGS; - } - - pageIndex0 = pDataStream->currentPageIndex; - pageIndex1 = (pDataStream->currentPageIndex + 1) & 0x01; - relativeCursor = pDataStream->relativeCursor; - - availableFrames = 0; - if (ma_atomic_load_32(&pDataStream->isPageValid[pageIndex0])) { - availableFrames += ma_atomic_load_32(&pDataStream->pageFrameCount[pageIndex0]) - relativeCursor; - if (ma_atomic_load_32(&pDataStream->isPageValid[pageIndex1])) { - availableFrames += ma_atomic_load_32(&pDataStream->pageFrameCount[pageIndex1]); - } - } - - *pAvailableFrames = availableFrames; - return MA_SUCCESS; -} - - -static ma_result ma_resource_manager_data_source_preinit(ma_resource_manager* pResourceManager, const ma_resource_manager_data_source_config* pConfig, ma_resource_manager_data_source* pDataSource) -{ - if (pDataSource == NULL) { - return MA_INVALID_ARGS; - } - - MA_ZERO_OBJECT(pDataSource); - - if (pConfig == NULL) { - return MA_INVALID_ARGS; - } - - if (pResourceManager == NULL) { - return MA_INVALID_ARGS; - } - - pDataSource->flags = pConfig->flags; - if (pConfig->isLooping) { - pDataSource->flags |= MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_LOOPING; - } - - return MA_SUCCESS; -} - -MA_API ma_result ma_resource_manager_data_source_init_ex(ma_resource_manager* pResourceManager, const ma_resource_manager_data_source_config* pConfig, ma_resource_manager_data_source* pDataSource) -{ - ma_result result; - - result = ma_resource_manager_data_source_preinit(pResourceManager, pConfig, pDataSource); - if (result != MA_SUCCESS) { - return result; - } - - /* The data source itself is just a data stream or a data buffer. */ - if ((pConfig->flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_STREAM) != 0) { - return ma_resource_manager_data_stream_init_ex(pResourceManager, pConfig, &pDataSource->backend.stream); - } else { - return ma_resource_manager_data_buffer_init_ex(pResourceManager, pConfig, &pDataSource->backend.buffer); - } -} - -MA_API ma_result ma_resource_manager_data_source_init(ma_resource_manager* pResourceManager, const char* pName, ma_uint32 flags, const ma_resource_manager_pipeline_notifications* pNotifications, ma_resource_manager_data_source* pDataSource) -{ - ma_resource_manager_data_source_config config; - - config = ma_resource_manager_data_source_config_init(); - config.pFilePath = pName; - config.flags = flags; - config.pNotifications = pNotifications; - - return ma_resource_manager_data_source_init_ex(pResourceManager, &config, pDataSource); -} - -MA_API ma_result ma_resource_manager_data_source_init_w(ma_resource_manager* pResourceManager, const wchar_t* pName, ma_uint32 flags, const ma_resource_manager_pipeline_notifications* pNotifications, ma_resource_manager_data_source* pDataSource) -{ - ma_resource_manager_data_source_config config; - - config = ma_resource_manager_data_source_config_init(); - config.pFilePathW = pName; - config.flags = flags; - config.pNotifications = pNotifications; - - return ma_resource_manager_data_source_init_ex(pResourceManager, &config, pDataSource); -} - -MA_API ma_result ma_resource_manager_data_source_init_copy(ma_resource_manager* pResourceManager, const ma_resource_manager_data_source* pExistingDataSource, ma_resource_manager_data_source* pDataSource) -{ - ma_result result; - ma_resource_manager_data_source_config config; - - if (pExistingDataSource == NULL) { - return MA_INVALID_ARGS; - } - - config = ma_resource_manager_data_source_config_init(); - config.flags = pExistingDataSource->flags; - - result = ma_resource_manager_data_source_preinit(pResourceManager, &config, pDataSource); - if (result != MA_SUCCESS) { - return result; - } - - /* Copying can only be done from data buffers. Streams cannot be copied. */ - if ((pExistingDataSource->flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_STREAM) != 0) { - return MA_INVALID_OPERATION; - } - - return ma_resource_manager_data_buffer_init_copy(pResourceManager, &pExistingDataSource->backend.buffer, &pDataSource->backend.buffer); -} - -MA_API ma_result ma_resource_manager_data_source_uninit(ma_resource_manager_data_source* pDataSource) -{ - if (pDataSource == NULL) { - return MA_INVALID_ARGS; - } - - /* All we need to is uninitialize the underlying data buffer or data stream. */ - if ((pDataSource->flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_STREAM) != 0) { - return ma_resource_manager_data_stream_uninit(&pDataSource->backend.stream); - } else { - return ma_resource_manager_data_buffer_uninit(&pDataSource->backend.buffer); - } -} - -MA_API ma_result ma_resource_manager_data_source_read_pcm_frames(ma_resource_manager_data_source* pDataSource, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead) -{ - /* Safety. */ - if (pFramesRead != NULL) { - *pFramesRead = 0; - } - - if (pDataSource == NULL) { - return MA_INVALID_ARGS; - } - - if ((pDataSource->flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_STREAM) != 0) { - return ma_resource_manager_data_stream_read_pcm_frames(&pDataSource->backend.stream, pFramesOut, frameCount, pFramesRead); - } else { - return ma_resource_manager_data_buffer_read_pcm_frames(&pDataSource->backend.buffer, pFramesOut, frameCount, pFramesRead); - } -} - -MA_API ma_result ma_resource_manager_data_source_seek_to_pcm_frame(ma_resource_manager_data_source* pDataSource, ma_uint64 frameIndex) -{ - if (pDataSource == NULL) { - return MA_INVALID_ARGS; - } - - if ((pDataSource->flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_STREAM) != 0) { - return ma_resource_manager_data_stream_seek_to_pcm_frame(&pDataSource->backend.stream, frameIndex); - } else { - return ma_resource_manager_data_buffer_seek_to_pcm_frame(&pDataSource->backend.buffer, frameIndex); - } -} - -MA_API ma_result ma_resource_manager_data_source_map(ma_resource_manager_data_source* pDataSource, void** ppFramesOut, ma_uint64* pFrameCount) -{ - if (pDataSource == NULL) { - return MA_INVALID_ARGS; - } - - if ((pDataSource->flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_STREAM) != 0) { - return ma_resource_manager_data_stream_map(&pDataSource->backend.stream, ppFramesOut, pFrameCount); - } else { - return MA_NOT_IMPLEMENTED; /* Mapping not supported with data buffers. */ - } -} - -MA_API ma_result ma_resource_manager_data_source_unmap(ma_resource_manager_data_source* pDataSource, ma_uint64 frameCount) -{ - if (pDataSource == NULL) { - return MA_INVALID_ARGS; - } - - if ((pDataSource->flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_STREAM) != 0) { - return ma_resource_manager_data_stream_unmap(&pDataSource->backend.stream, frameCount); - } else { - return MA_NOT_IMPLEMENTED; /* Mapping not supported with data buffers. */ - } -} - -MA_API ma_result ma_resource_manager_data_source_get_data_format(ma_resource_manager_data_source* pDataSource, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate, ma_channel* pChannelMap, size_t channelMapCap) -{ - if (pDataSource == NULL) { - return MA_INVALID_ARGS; - } - - if ((pDataSource->flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_STREAM) != 0) { - return ma_resource_manager_data_stream_get_data_format(&pDataSource->backend.stream, pFormat, pChannels, pSampleRate, pChannelMap, channelMapCap); - } else { - return ma_resource_manager_data_buffer_get_data_format(&pDataSource->backend.buffer, pFormat, pChannels, pSampleRate, pChannelMap, channelMapCap); - } -} - -MA_API ma_result ma_resource_manager_data_source_get_cursor_in_pcm_frames(ma_resource_manager_data_source* pDataSource, ma_uint64* pCursor) -{ - if (pDataSource == NULL) { - return MA_INVALID_ARGS; - } - - if ((pDataSource->flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_STREAM) != 0) { - return ma_resource_manager_data_stream_get_cursor_in_pcm_frames(&pDataSource->backend.stream, pCursor); - } else { - return ma_resource_manager_data_buffer_get_cursor_in_pcm_frames(&pDataSource->backend.buffer, pCursor); - } -} - -MA_API ma_result ma_resource_manager_data_source_get_length_in_pcm_frames(ma_resource_manager_data_source* pDataSource, ma_uint64* pLength) -{ - if (pDataSource == NULL) { - return MA_INVALID_ARGS; - } - - if ((pDataSource->flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_STREAM) != 0) { - return ma_resource_manager_data_stream_get_length_in_pcm_frames(&pDataSource->backend.stream, pLength); - } else { - return ma_resource_manager_data_buffer_get_length_in_pcm_frames(&pDataSource->backend.buffer, pLength); - } -} - -MA_API ma_result ma_resource_manager_data_source_result(const ma_resource_manager_data_source* pDataSource) -{ - if (pDataSource == NULL) { - return MA_INVALID_ARGS; - } - - if ((pDataSource->flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_STREAM) != 0) { - return ma_resource_manager_data_stream_result(&pDataSource->backend.stream); - } else { - return ma_resource_manager_data_buffer_result(&pDataSource->backend.buffer); - } -} - -MA_API ma_result ma_resource_manager_data_source_set_looping(ma_resource_manager_data_source* pDataSource, ma_bool32 isLooping) -{ - if (pDataSource == NULL) { - return MA_INVALID_ARGS; - } - - if ((pDataSource->flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_STREAM) != 0) { - return ma_resource_manager_data_stream_set_looping(&pDataSource->backend.stream, isLooping); - } else { - return ma_resource_manager_data_buffer_set_looping(&pDataSource->backend.buffer, isLooping); - } -} - -MA_API ma_bool32 ma_resource_manager_data_source_is_looping(const ma_resource_manager_data_source* pDataSource) -{ - if (pDataSource == NULL) { - return MA_FALSE; - } - - if ((pDataSource->flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_STREAM) != 0) { - return ma_resource_manager_data_stream_is_looping(&pDataSource->backend.stream); - } else { - return ma_resource_manager_data_buffer_is_looping(&pDataSource->backend.buffer); - } -} - -MA_API ma_result ma_resource_manager_data_source_get_available_frames(ma_resource_manager_data_source* pDataSource, ma_uint64* pAvailableFrames) -{ - if (pAvailableFrames == NULL) { - return MA_INVALID_ARGS; - } - - *pAvailableFrames = 0; - - if (pDataSource == NULL) { - return MA_INVALID_ARGS; - } - - if ((pDataSource->flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_STREAM) != 0) { - return ma_resource_manager_data_stream_get_available_frames(&pDataSource->backend.stream, pAvailableFrames); - } else { - return ma_resource_manager_data_buffer_get_available_frames(&pDataSource->backend.buffer, pAvailableFrames); - } -} - - -MA_API ma_result ma_resource_manager_post_job(ma_resource_manager* pResourceManager, const ma_job* pJob) -{ - if (pResourceManager == NULL) { - return MA_INVALID_ARGS; - } - - return ma_job_queue_post(&pResourceManager->jobQueue, pJob); -} - -MA_API ma_result ma_resource_manager_post_job_quit(ma_resource_manager* pResourceManager) -{ - ma_job job = ma_job_init(MA_JOB_TYPE_QUIT); - return ma_resource_manager_post_job(pResourceManager, &job); -} - -MA_API ma_result ma_resource_manager_next_job(ma_resource_manager* pResourceManager, ma_job* pJob) -{ - if (pResourceManager == NULL) { - return MA_INVALID_ARGS; - } - - return ma_job_queue_next(&pResourceManager->jobQueue, pJob); -} - - -static ma_result ma_job_process__resource_manager__load_data_buffer_node(ma_job* pJob) -{ - ma_result result = MA_SUCCESS; - ma_resource_manager* pResourceManager; - ma_resource_manager_data_buffer_node* pDataBufferNode; - - MA_ASSERT(pJob != NULL); - - pResourceManager = (ma_resource_manager*)pJob->data.resourceManager.loadDataBufferNode.pResourceManager; - MA_ASSERT(pResourceManager != NULL); - - pDataBufferNode = (ma_resource_manager_data_buffer_node*)pJob->data.resourceManager.loadDataBufferNode.pDataBufferNode; - MA_ASSERT(pDataBufferNode != NULL); - MA_ASSERT(pDataBufferNode->isDataOwnedByResourceManager == MA_TRUE); /* The data should always be owned by the resource manager. */ - - /* The data buffer is not getting deleted, but we may be getting executed out of order. If so, we need to push the job back onto the queue and return. */ - if (pJob->order != ma_atomic_load_32(&pDataBufferNode->executionPointer)) { - return ma_resource_manager_post_job(pResourceManager, pJob); /* Attempting to execute out of order. Probably interleaved with a MA_JOB_TYPE_RESOURCE_MANAGER_FREE_DATA_BUFFER job. */ - } - - /* First thing we need to do is check whether or not the data buffer is getting deleted. If so we just abort. */ - if (ma_resource_manager_data_buffer_node_result(pDataBufferNode) != MA_BUSY) { - result = ma_resource_manager_data_buffer_node_result(pDataBufferNode); /* The data buffer may be getting deleted before it's even been loaded. */ - goto done; - } - - /* - We're ready to start loading. Essentially what we're doing here is initializing the data supply - of the node. Once this is complete, data buffers can have their connectors initialized which - will allow then to have audio data read from them. - - Note that when the data supply type has been moved away from "unknown", that is when other threads - will determine that the node is available for data delivery and the data buffer connectors can be - initialized. Therefore, it's important that it is set after the data supply has been initialized. - */ - if ((pJob->data.resourceManager.loadDataBufferNode.flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_DECODE) != 0) { - /* - Decoding. This is the complex case because we're not going to be doing the entire decoding - process here. Instead it's going to be split of multiple jobs and loaded in pages. The - reason for this is to evenly distribute decoding time across multiple sounds, rather than - having one huge sound hog all the available processing resources. - - The first thing we do is initialize a decoder. This is allocated on the heap and is passed - around to the paging jobs. When the last paging job has completed it's processing, it'll - free the decoder for us. - - This job does not do any actual decoding. It instead just posts a PAGE_DATA_BUFFER_NODE job - which is where the actual decoding work will be done. However, once this job is complete, - the node will be in a state where data buffer connectors can be initialized. - */ - ma_decoder* pDecoder; /* <-- Free'd on the last page decode. */ - ma_job pageDataBufferNodeJob; - - /* Allocate the decoder by initializing a decoded data supply. */ - result = ma_resource_manager_data_buffer_node_init_supply_decoded(pResourceManager, pDataBufferNode, pJob->data.resourceManager.loadDataBufferNode.pFilePath, pJob->data.resourceManager.loadDataBufferNode.pFilePathW, pJob->data.resourceManager.loadDataBufferNode.flags, &pDecoder); - - /* - Don't ever propagate an MA_BUSY result code or else the resource manager will think the - node is just busy decoding rather than in an error state. This should never happen, but - including this logic for safety just in case. - */ - if (result == MA_BUSY) { - result = MA_ERROR; - } - - if (result != MA_SUCCESS) { - if (pJob->data.resourceManager.loadDataBufferNode.pFilePath != NULL) { - ma_log_postf(ma_resource_manager_get_log(pResourceManager), MA_LOG_LEVEL_WARNING, "Failed to initialize data supply for \"%s\". %s.\n", pJob->data.resourceManager.loadDataBufferNode.pFilePath, ma_result_description(result)); - } else { - #if (defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L) || defined(_MSC_VER) - ma_log_postf(ma_resource_manager_get_log(pResourceManager), MA_LOG_LEVEL_WARNING, "Failed to initialize data supply for \"%ls\", %s.\n", pJob->data.resourceManager.loadDataBufferNode.pFilePathW, ma_result_description(result)); - #endif - } - - goto done; - } - - /* - At this point the node's data supply is initialized and other threads can start initializing - their data buffer connectors. However, no data will actually be available until we start to - actually decode it. To do this, we need to post a paging job which is where the decoding - work is done. - - Note that if an error occurred at an earlier point, this section will have been skipped. - */ - pageDataBufferNodeJob = ma_job_init(MA_JOB_TYPE_RESOURCE_MANAGER_PAGE_DATA_BUFFER_NODE); - pageDataBufferNodeJob.order = ma_resource_manager_data_buffer_node_next_execution_order(pDataBufferNode); - pageDataBufferNodeJob.data.resourceManager.pageDataBufferNode.pResourceManager = pResourceManager; - pageDataBufferNodeJob.data.resourceManager.pageDataBufferNode.pDataBufferNode = pDataBufferNode; - pageDataBufferNodeJob.data.resourceManager.pageDataBufferNode.pDecoder = pDecoder; - pageDataBufferNodeJob.data.resourceManager.pageDataBufferNode.pDoneNotification = pJob->data.resourceManager.loadDataBufferNode.pDoneNotification; - pageDataBufferNodeJob.data.resourceManager.pageDataBufferNode.pDoneFence = pJob->data.resourceManager.loadDataBufferNode.pDoneFence; - - /* The job has been set up so it can now be posted. */ - result = ma_resource_manager_post_job(pResourceManager, &pageDataBufferNodeJob); - - /* - When we get here, we want to make sure the result code is set to MA_BUSY. The reason for - this is that the result will be copied over to the node's internal result variable. In - this case, since the decoding is still in-progress, we need to make sure the result code - is set to MA_BUSY. - */ - if (result != MA_SUCCESS) { - ma_log_postf(ma_resource_manager_get_log(pResourceManager), MA_LOG_LEVEL_ERROR, "Failed to post MA_JOB_TYPE_RESOURCE_MANAGER_PAGE_DATA_BUFFER_NODE job. %s\n", ma_result_description(result)); - ma_decoder_uninit(pDecoder); - ma_free(pDecoder, &pResourceManager->config.allocationCallbacks); - } else { - result = MA_BUSY; - } - } else { - /* No decoding. This is the simple case. We need only read the file content into memory and we're done. */ - result = ma_resource_manager_data_buffer_node_init_supply_encoded(pResourceManager, pDataBufferNode, pJob->data.resourceManager.loadDataBufferNode.pFilePath, pJob->data.resourceManager.loadDataBufferNode.pFilePathW); - } - - -done: - /* File paths are no longer needed. */ - ma_free(pJob->data.resourceManager.loadDataBufferNode.pFilePath, &pResourceManager->config.allocationCallbacks); - ma_free(pJob->data.resourceManager.loadDataBufferNode.pFilePathW, &pResourceManager->config.allocationCallbacks); - - /* - We need to set the result to at the very end to ensure no other threads try reading the data before we've fully initialized the object. Other threads - are going to be inspecting this variable to determine whether or not they're ready to read data. We can only change the result if it's set to MA_BUSY - because otherwise we may be changing away from an error code which would be bad. An example is if the application creates a data buffer, but then - immediately deletes it before we've got to this point. In this case, pDataBuffer->result will be MA_UNAVAILABLE, and setting it to MA_SUCCESS or any - other error code would cause the buffer to look like it's in a state that it's not. - */ - ma_atomic_compare_and_swap_i32(&pDataBufferNode->result, MA_BUSY, result); - - /* At this point initialization is complete and we can signal the notification if any. */ - if (pJob->data.resourceManager.loadDataBufferNode.pInitNotification != NULL) { - ma_async_notification_signal(pJob->data.resourceManager.loadDataBufferNode.pInitNotification); - } - if (pJob->data.resourceManager.loadDataBufferNode.pInitFence != NULL) { - ma_fence_release(pJob->data.resourceManager.loadDataBufferNode.pInitFence); - } - - /* If we have a success result it means we've fully loaded the buffer. This will happen in the non-decoding case. */ - if (result != MA_BUSY) { - if (pJob->data.resourceManager.loadDataBufferNode.pDoneNotification != NULL) { - ma_async_notification_signal(pJob->data.resourceManager.loadDataBufferNode.pDoneNotification); - } - if (pJob->data.resourceManager.loadDataBufferNode.pDoneFence != NULL) { - ma_fence_release(pJob->data.resourceManager.loadDataBufferNode.pDoneFence); - } - } - - /* Increment the node's execution pointer so that the next jobs can be processed. This is how we keep decoding of pages in-order. */ - ma_atomic_fetch_add_32(&pDataBufferNode->executionPointer, 1); - - /* A busy result should be considered successful from the point of view of the job system. */ - if (result == MA_BUSY) { - result = MA_SUCCESS; - } - - return result; -} - -static ma_result ma_job_process__resource_manager__free_data_buffer_node(ma_job* pJob) -{ - ma_resource_manager* pResourceManager; - ma_resource_manager_data_buffer_node* pDataBufferNode; - - MA_ASSERT(pJob != NULL); - - pResourceManager = (ma_resource_manager*)pJob->data.resourceManager.freeDataBufferNode.pResourceManager; - MA_ASSERT(pResourceManager != NULL); - - pDataBufferNode = (ma_resource_manager_data_buffer_node*)pJob->data.resourceManager.freeDataBufferNode.pDataBufferNode; - MA_ASSERT(pDataBufferNode != NULL); - - if (pJob->order != ma_atomic_load_32(&pDataBufferNode->executionPointer)) { - return ma_resource_manager_post_job(pResourceManager, pJob); /* Out of order. */ - } - - ma_resource_manager_data_buffer_node_free(pResourceManager, pDataBufferNode); - - /* The event needs to be signalled last. */ - if (pJob->data.resourceManager.freeDataBufferNode.pDoneNotification != NULL) { - ma_async_notification_signal(pJob->data.resourceManager.freeDataBufferNode.pDoneNotification); - } - - if (pJob->data.resourceManager.freeDataBufferNode.pDoneFence != NULL) { - ma_fence_release(pJob->data.resourceManager.freeDataBufferNode.pDoneFence); - } - - ma_atomic_fetch_add_32(&pDataBufferNode->executionPointer, 1); - return MA_SUCCESS; -} - -static ma_result ma_job_process__resource_manager__page_data_buffer_node(ma_job* pJob) -{ - ma_result result = MA_SUCCESS; - ma_resource_manager* pResourceManager; - ma_resource_manager_data_buffer_node* pDataBufferNode; - - MA_ASSERT(pJob != NULL); - - pResourceManager = (ma_resource_manager*)pJob->data.resourceManager.pageDataBufferNode.pResourceManager; - MA_ASSERT(pResourceManager != NULL); - - pDataBufferNode = (ma_resource_manager_data_buffer_node*)pJob->data.resourceManager.pageDataBufferNode.pDataBufferNode; - MA_ASSERT(pDataBufferNode != NULL); - - if (pJob->order != ma_atomic_load_32(&pDataBufferNode->executionPointer)) { - return ma_resource_manager_post_job(pResourceManager, pJob); /* Out of order. */ - } - - /* Don't do any more decoding if the data buffer has started the uninitialization process. */ - result = ma_resource_manager_data_buffer_node_result(pDataBufferNode); - if (result != MA_BUSY) { - goto done; - } - - /* We're ready to decode the next page. */ - result = ma_resource_manager_data_buffer_node_decode_next_page(pResourceManager, pDataBufferNode, (ma_decoder*)pJob->data.resourceManager.pageDataBufferNode.pDecoder); - - /* - If we have a success code by this point, we want to post another job. We're going to set the - result back to MA_BUSY to make it clear that there's still more to load. - */ - if (result == MA_SUCCESS) { - ma_job newJob; - newJob = *pJob; /* Everything is the same as the input job, except the execution order. */ - newJob.order = ma_resource_manager_data_buffer_node_next_execution_order(pDataBufferNode); /* We need a fresh execution order. */ - - result = ma_resource_manager_post_job(pResourceManager, &newJob); - - /* Since the sound isn't yet fully decoded we want the status to be set to busy. */ - if (result == MA_SUCCESS) { - result = MA_BUSY; - } - } - -done: - /* If there's still more to decode the result will be set to MA_BUSY. Otherwise we can free the decoder. */ - if (result != MA_BUSY) { - ma_decoder_uninit((ma_decoder*)pJob->data.resourceManager.pageDataBufferNode.pDecoder); - ma_free(pJob->data.resourceManager.pageDataBufferNode.pDecoder, &pResourceManager->config.allocationCallbacks); - } - - /* If we reached the end we need to treat it as successful. */ - if (result == MA_AT_END) { - result = MA_SUCCESS; - } - - /* Make sure we set the result of node in case some error occurred. */ - ma_atomic_compare_and_swap_i32(&pDataBufferNode->result, MA_BUSY, result); - - /* Signal the notification after setting the result in case the notification callback wants to inspect the result code. */ - if (result != MA_BUSY) { - if (pJob->data.resourceManager.pageDataBufferNode.pDoneNotification != NULL) { - ma_async_notification_signal(pJob->data.resourceManager.pageDataBufferNode.pDoneNotification); - } - - if (pJob->data.resourceManager.pageDataBufferNode.pDoneFence != NULL) { - ma_fence_release(pJob->data.resourceManager.pageDataBufferNode.pDoneFence); - } - } - - ma_atomic_fetch_add_32(&pDataBufferNode->executionPointer, 1); - return result; -} - - -static ma_result ma_job_process__resource_manager__load_data_buffer(ma_job* pJob) -{ - ma_result result = MA_SUCCESS; - ma_resource_manager* pResourceManager; - ma_resource_manager_data_buffer* pDataBuffer; - ma_resource_manager_data_supply_type dataSupplyType = ma_resource_manager_data_supply_type_unknown; - ma_bool32 isConnectorInitialized = MA_FALSE; - - /* - All we're doing here is checking if the node has finished loading. If not, we just re-post the job - and keep waiting. Otherwise we increment the execution counter and set the buffer's result code. - */ - MA_ASSERT(pJob != NULL); - - pDataBuffer = (ma_resource_manager_data_buffer*)pJob->data.resourceManager.loadDataBuffer.pDataBuffer; - MA_ASSERT(pDataBuffer != NULL); - - pResourceManager = pDataBuffer->pResourceManager; - - if (pJob->order != ma_atomic_load_32(&pDataBuffer->executionPointer)) { - return ma_resource_manager_post_job(pResourceManager, pJob); /* Attempting to execute out of order. Probably interleaved with a MA_JOB_TYPE_RESOURCE_MANAGER_FREE_DATA_BUFFER job. */ - } - - /* - First thing we need to do is check whether or not the data buffer is getting deleted. If so we - just abort, but making sure we increment the execution pointer. - */ - result = ma_resource_manager_data_buffer_result(pDataBuffer); - if (result != MA_BUSY) { - goto done; /* <-- This will ensure the execution pointer is incremented. */ - } else { - result = MA_SUCCESS; /* <-- Make sure this is reset. */ - (void)result; /* <-- This is to suppress a static analysis diagnostic about "result" not being used. But for safety when I do future maintenance I don't want to delete that assignment. */ - } - - /* Try initializing the connector if we haven't already. */ - isConnectorInitialized = ma_resource_manager_data_buffer_has_connector(pDataBuffer); - if (isConnectorInitialized == MA_FALSE) { - dataSupplyType = ma_resource_manager_data_buffer_node_get_data_supply_type(pDataBuffer->pNode); - - if (dataSupplyType != ma_resource_manager_data_supply_type_unknown) { - /* We can now initialize the connector. If this fails, we need to abort. It's very rare for this to fail. */ - ma_resource_manager_data_source_config dataSourceConfig; /* For setting initial looping state and range. */ - dataSourceConfig = ma_resource_manager_data_source_config_init(); - dataSourceConfig.rangeBegInPCMFrames = pJob->data.resourceManager.loadDataBuffer.rangeBegInPCMFrames; - dataSourceConfig.rangeEndInPCMFrames = pJob->data.resourceManager.loadDataBuffer.rangeEndInPCMFrames; - dataSourceConfig.loopPointBegInPCMFrames = pJob->data.resourceManager.loadDataBuffer.loopPointBegInPCMFrames; - dataSourceConfig.loopPointEndInPCMFrames = pJob->data.resourceManager.loadDataBuffer.loopPointEndInPCMFrames; - dataSourceConfig.isLooping = pJob->data.resourceManager.loadDataBuffer.isLooping; - - result = ma_resource_manager_data_buffer_init_connector(pDataBuffer, &dataSourceConfig, pJob->data.resourceManager.loadDataBuffer.pInitNotification, pJob->data.resourceManager.loadDataBuffer.pInitFence); - if (result != MA_SUCCESS) { - ma_log_postf(ma_resource_manager_get_log(pResourceManager), MA_LOG_LEVEL_ERROR, "Failed to initialize connector for data buffer. %s.\n", ma_result_description(result)); - goto done; - } - } else { - /* Don't have a known data supply type. Most likely the data buffer node is still loading, but it could be that an error occurred. */ - } - } else { - /* The connector is already initialized. Nothing to do here. */ - } - - /* - If the data node is still loading, we need to repost the job and *not* increment the execution - pointer (i.e. we need to not fall through to the "done" label). - - There is a hole between here and the where the data connector is initialized where the data - buffer node may have finished initializing. We need to check for this by checking the result of - the data buffer node and whether or not we had an unknown data supply type at the time of - trying to initialize the data connector. - */ - result = ma_resource_manager_data_buffer_node_result(pDataBuffer->pNode); - if (result == MA_BUSY || (result == MA_SUCCESS && isConnectorInitialized == MA_FALSE && dataSupplyType == ma_resource_manager_data_supply_type_unknown)) { - return ma_resource_manager_post_job(pResourceManager, pJob); - } - -done: - /* Only move away from a busy code so that we don't trash any existing error codes. */ - ma_atomic_compare_and_swap_i32(&pDataBuffer->result, MA_BUSY, result); - - /* Only signal the other threads after the result has been set just for cleanliness sake. */ - if (pJob->data.resourceManager.loadDataBuffer.pDoneNotification != NULL) { - ma_async_notification_signal(pJob->data.resourceManager.loadDataBuffer.pDoneNotification); - } - if (pJob->data.resourceManager.loadDataBuffer.pDoneFence != NULL) { - ma_fence_release(pJob->data.resourceManager.loadDataBuffer.pDoneFence); - } - - /* - If at this point the data buffer has not had it's connector initialized, it means the - notification event was never signalled which means we need to signal it here. - */ - if (ma_resource_manager_data_buffer_has_connector(pDataBuffer) == MA_FALSE && result != MA_SUCCESS) { - if (pJob->data.resourceManager.loadDataBuffer.pInitNotification != NULL) { - ma_async_notification_signal(pJob->data.resourceManager.loadDataBuffer.pInitNotification); - } - if (pJob->data.resourceManager.loadDataBuffer.pInitFence != NULL) { - ma_fence_release(pJob->data.resourceManager.loadDataBuffer.pInitFence); - } - } - - ma_atomic_fetch_add_32(&pDataBuffer->executionPointer, 1); - return result; -} - -static ma_result ma_job_process__resource_manager__free_data_buffer(ma_job* pJob) -{ - ma_resource_manager* pResourceManager; - ma_resource_manager_data_buffer* pDataBuffer; - - MA_ASSERT(pJob != NULL); - - pDataBuffer = (ma_resource_manager_data_buffer*)pJob->data.resourceManager.freeDataBuffer.pDataBuffer; - MA_ASSERT(pDataBuffer != NULL); - - pResourceManager = pDataBuffer->pResourceManager; - - if (pJob->order != ma_atomic_load_32(&pDataBuffer->executionPointer)) { - return ma_resource_manager_post_job(pResourceManager, pJob); /* Out of order. */ - } - - ma_resource_manager_data_buffer_uninit_internal(pDataBuffer); - - /* The event needs to be signalled last. */ - if (pJob->data.resourceManager.freeDataBuffer.pDoneNotification != NULL) { - ma_async_notification_signal(pJob->data.resourceManager.freeDataBuffer.pDoneNotification); - } - - if (pJob->data.resourceManager.freeDataBuffer.pDoneFence != NULL) { - ma_fence_release(pJob->data.resourceManager.freeDataBuffer.pDoneFence); - } - - ma_atomic_fetch_add_32(&pDataBuffer->executionPointer, 1); - return MA_SUCCESS; -} - -static ma_result ma_job_process__resource_manager__load_data_stream(ma_job* pJob) -{ - ma_result result = MA_SUCCESS; - ma_decoder_config decoderConfig; - ma_uint32 pageBufferSizeInBytes; - ma_resource_manager* pResourceManager; - ma_resource_manager_data_stream* pDataStream; - - MA_ASSERT(pJob != NULL); - - pDataStream = (ma_resource_manager_data_stream*)pJob->data.resourceManager.loadDataStream.pDataStream; - MA_ASSERT(pDataStream != NULL); - - pResourceManager = pDataStream->pResourceManager; - - if (pJob->order != ma_atomic_load_32(&pDataStream->executionPointer)) { - return ma_resource_manager_post_job(pResourceManager, pJob); /* Out of order. */ - } - - if (ma_resource_manager_data_stream_result(pDataStream) != MA_BUSY) { - result = MA_INVALID_OPERATION; /* Most likely the data stream is being uninitialized. */ - goto done; - } - - /* We need to initialize the decoder first so we can determine the size of the pages. */ - decoderConfig = ma_resource_manager__init_decoder_config(pResourceManager); - - if (pJob->data.resourceManager.loadDataStream.pFilePath != NULL) { - result = ma_decoder_init_vfs(pResourceManager->config.pVFS, pJob->data.resourceManager.loadDataStream.pFilePath, &decoderConfig, &pDataStream->decoder); - } else { - result = ma_decoder_init_vfs_w(pResourceManager->config.pVFS, pJob->data.resourceManager.loadDataStream.pFilePathW, &decoderConfig, &pDataStream->decoder); - } - if (result != MA_SUCCESS) { - goto done; - } - - /* Retrieve the total length of the file before marking the decoder as loaded. */ - if ((pDataStream->flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_UNKNOWN_LENGTH) == 0) { - result = ma_decoder_get_length_in_pcm_frames(&pDataStream->decoder, &pDataStream->totalLengthInPCMFrames); - if (result != MA_SUCCESS) { - goto done; /* Failed to retrieve the length. */ - } - } else { - pDataStream->totalLengthInPCMFrames = 0; - } - - /* - Only mark the decoder as initialized when the length of the decoder has been retrieved because that can possibly require a scan over the whole file - and we don't want to have another thread trying to access the decoder while it's scanning. - */ - pDataStream->isDecoderInitialized = MA_TRUE; - - /* We have the decoder so we can now initialize our page buffer. */ - pageBufferSizeInBytes = ma_resource_manager_data_stream_get_page_size_in_frames(pDataStream) * 2 * ma_get_bytes_per_frame(pDataStream->decoder.outputFormat, pDataStream->decoder.outputChannels); - - pDataStream->pPageData = ma_malloc(pageBufferSizeInBytes, &pResourceManager->config.allocationCallbacks); - if (pDataStream->pPageData == NULL) { - ma_decoder_uninit(&pDataStream->decoder); - result = MA_OUT_OF_MEMORY; - goto done; - } - - /* Seek to our initial seek point before filling the initial pages. */ - ma_decoder_seek_to_pcm_frame(&pDataStream->decoder, pJob->data.resourceManager.loadDataStream.initialSeekPoint); - - /* We have our decoder and our page buffer, so now we need to fill our pages. */ - ma_resource_manager_data_stream_fill_pages(pDataStream); - - /* And now we're done. We want to make sure the result is MA_SUCCESS. */ - result = MA_SUCCESS; - -done: - ma_free(pJob->data.resourceManager.loadDataStream.pFilePath, &pResourceManager->config.allocationCallbacks); - ma_free(pJob->data.resourceManager.loadDataStream.pFilePathW, &pResourceManager->config.allocationCallbacks); - - /* We can only change the status away from MA_BUSY. If it's set to anything else it means an error has occurred somewhere or the uninitialization process has started (most likely). */ - ma_atomic_compare_and_swap_i32(&pDataStream->result, MA_BUSY, result); - - /* Only signal the other threads after the result has been set just for cleanliness sake. */ - if (pJob->data.resourceManager.loadDataStream.pInitNotification != NULL) { - ma_async_notification_signal(pJob->data.resourceManager.loadDataStream.pInitNotification); - } - if (pJob->data.resourceManager.loadDataStream.pInitFence != NULL) { - ma_fence_release(pJob->data.resourceManager.loadDataStream.pInitFence); - } - - ma_atomic_fetch_add_32(&pDataStream->executionPointer, 1); - return result; -} - -static ma_result ma_job_process__resource_manager__free_data_stream(ma_job* pJob) -{ - ma_resource_manager* pResourceManager; - ma_resource_manager_data_stream* pDataStream; - - MA_ASSERT(pJob != NULL); - - pDataStream = (ma_resource_manager_data_stream*)pJob->data.resourceManager.freeDataStream.pDataStream; - MA_ASSERT(pDataStream != NULL); - - pResourceManager = pDataStream->pResourceManager; - - if (pJob->order != ma_atomic_load_32(&pDataStream->executionPointer)) { - return ma_resource_manager_post_job(pResourceManager, pJob); /* Out of order. */ - } - - /* If our status is not MA_UNAVAILABLE we have a bug somewhere. */ - MA_ASSERT(ma_resource_manager_data_stream_result(pDataStream) == MA_UNAVAILABLE); - - if (pDataStream->isDecoderInitialized) { - ma_decoder_uninit(&pDataStream->decoder); - } - - if (pDataStream->pPageData != NULL) { - ma_free(pDataStream->pPageData, &pResourceManager->config.allocationCallbacks); - pDataStream->pPageData = NULL; /* Just in case... */ - } - - ma_data_source_uninit(&pDataStream->ds); - - /* The event needs to be signalled last. */ - if (pJob->data.resourceManager.freeDataStream.pDoneNotification != NULL) { - ma_async_notification_signal(pJob->data.resourceManager.freeDataStream.pDoneNotification); - } - if (pJob->data.resourceManager.freeDataStream.pDoneFence != NULL) { - ma_fence_release(pJob->data.resourceManager.freeDataStream.pDoneFence); - } - - /*ma_atomic_fetch_add_32(&pDataStream->executionPointer, 1);*/ - return MA_SUCCESS; -} - -static ma_result ma_job_process__resource_manager__page_data_stream(ma_job* pJob) -{ - ma_result result = MA_SUCCESS; - ma_resource_manager* pResourceManager; - ma_resource_manager_data_stream* pDataStream; - - MA_ASSERT(pJob != NULL); - - pDataStream = (ma_resource_manager_data_stream*)pJob->data.resourceManager.pageDataStream.pDataStream; - MA_ASSERT(pDataStream != NULL); - - pResourceManager = pDataStream->pResourceManager; - - if (pJob->order != ma_atomic_load_32(&pDataStream->executionPointer)) { - return ma_resource_manager_post_job(pResourceManager, pJob); /* Out of order. */ - } - - /* For streams, the status should be MA_SUCCESS. */ - if (ma_resource_manager_data_stream_result(pDataStream) != MA_SUCCESS) { - result = MA_INVALID_OPERATION; - goto done; - } - - ma_resource_manager_data_stream_fill_page(pDataStream, pJob->data.resourceManager.pageDataStream.pageIndex); - -done: - ma_atomic_fetch_add_32(&pDataStream->executionPointer, 1); - return result; -} - -static ma_result ma_job_process__resource_manager__seek_data_stream(ma_job* pJob) -{ - ma_result result = MA_SUCCESS; - ma_resource_manager* pResourceManager; - ma_resource_manager_data_stream* pDataStream; - - MA_ASSERT(pJob != NULL); - - pDataStream = (ma_resource_manager_data_stream*)pJob->data.resourceManager.seekDataStream.pDataStream; - MA_ASSERT(pDataStream != NULL); - - pResourceManager = pDataStream->pResourceManager; - - if (pJob->order != ma_atomic_load_32(&pDataStream->executionPointer)) { - return ma_resource_manager_post_job(pResourceManager, pJob); /* Out of order. */ - } - - /* For streams the status should be MA_SUCCESS for this to do anything. */ - if (ma_resource_manager_data_stream_result(pDataStream) != MA_SUCCESS || pDataStream->isDecoderInitialized == MA_FALSE) { - result = MA_INVALID_OPERATION; - goto done; - } - - /* - With seeking we just assume both pages are invalid and the relative frame cursor at position 0. This is basically exactly the same as loading, except - instead of initializing the decoder, we seek to a frame. - */ - ma_decoder_seek_to_pcm_frame(&pDataStream->decoder, pJob->data.resourceManager.seekDataStream.frameIndex); - - /* After seeking we'll need to reload the pages. */ - ma_resource_manager_data_stream_fill_pages(pDataStream); - - /* We need to let the public API know that we're done seeking. */ - ma_atomic_fetch_sub_32(&pDataStream->seekCounter, 1); - -done: - ma_atomic_fetch_add_32(&pDataStream->executionPointer, 1); - return result; -} - -MA_API ma_result ma_resource_manager_process_job(ma_resource_manager* pResourceManager, ma_job* pJob) -{ - if (pResourceManager == NULL || pJob == NULL) { - return MA_INVALID_ARGS; - } - - return ma_job_process(pJob); -} - -MA_API ma_result ma_resource_manager_process_next_job(ma_resource_manager* pResourceManager) -{ - ma_result result; - ma_job job; - - if (pResourceManager == NULL) { - return MA_INVALID_ARGS; - } - - /* This will return MA_CANCELLED if the next job is a quit job. */ - result = ma_resource_manager_next_job(pResourceManager, &job); - if (result != MA_SUCCESS) { - return result; - } - - return ma_job_process(&job); -} -#else -/* We'll get here if the resource manager is being excluded from the build. We need to define the job processing callbacks as no-ops. */ -static ma_result ma_job_process__resource_manager__load_data_buffer_node(ma_job* pJob) { return ma_job_process__noop(pJob); } -static ma_result ma_job_process__resource_manager__free_data_buffer_node(ma_job* pJob) { return ma_job_process__noop(pJob); } -static ma_result ma_job_process__resource_manager__page_data_buffer_node(ma_job* pJob) { return ma_job_process__noop(pJob); } -static ma_result ma_job_process__resource_manager__load_data_buffer(ma_job* pJob) { return ma_job_process__noop(pJob); } -static ma_result ma_job_process__resource_manager__free_data_buffer(ma_job* pJob) { return ma_job_process__noop(pJob); } -static ma_result ma_job_process__resource_manager__load_data_stream(ma_job* pJob) { return ma_job_process__noop(pJob); } -static ma_result ma_job_process__resource_manager__free_data_stream(ma_job* pJob) { return ma_job_process__noop(pJob); } -static ma_result ma_job_process__resource_manager__page_data_stream(ma_job* pJob) { return ma_job_process__noop(pJob); } -static ma_result ma_job_process__resource_manager__seek_data_stream(ma_job* pJob) { return ma_job_process__noop(pJob); } -#endif /* MA_NO_RESOURCE_MANAGER */ - - -#ifndef MA_NO_NODE_GRAPH - -static ma_stack* ma_stack_init(size_t sizeInBytes, const ma_allocation_callbacks* pAllocationCallbacks) -{ - ma_stack* pStack; - - if (sizeInBytes == 0) { - return NULL; - } - - pStack = (ma_stack*)ma_malloc(sizeof(*pStack) - sizeof(pStack->_data) + sizeInBytes, pAllocationCallbacks); - if (pStack == NULL) { - return NULL; - } - - pStack->offset = 0; - pStack->sizeInBytes = sizeInBytes; - - return pStack; -} - -static void ma_stack_uninit(ma_stack* pStack, const ma_allocation_callbacks* pAllocationCallbacks) -{ - if (pStack == NULL) { - return; - } - - ma_free(pStack, pAllocationCallbacks); -} - -static void* ma_stack_alloc(ma_stack* pStack, size_t sz) -{ - /* The size of the allocation is stored in the memory directly before the pointer. This needs to include padding to keep it aligned to ma_uintptr */ - void* p = (void*)((char*)pStack->_data + pStack->offset); - size_t* pSize = (size_t*)p; - - sz = (sz + (sizeof(ma_uintptr) - 1)) & ~(sizeof(ma_uintptr) - 1); /* Padding. */ - if (pStack->offset + sz + sizeof(size_t) > pStack->sizeInBytes) { - return NULL; /* Out of memory. */ - } - - pStack->offset += sz + sizeof(size_t); - - *pSize = sz; - return (void*)((char*)p + sizeof(size_t)); -} - -static void ma_stack_free(ma_stack* pStack, void* p) -{ - size_t* pSize; - - if (p == NULL) { - return; - } - - pSize = (size_t*)p - 1; - pStack->offset -= *pSize + sizeof(size_t); -} - - - -/* 10ms @ 48K = 480. Must never exceed 65535. */ -#ifndef MA_DEFAULT_NODE_CACHE_CAP_IN_FRAMES_PER_BUS -#define MA_DEFAULT_NODE_CACHE_CAP_IN_FRAMES_PER_BUS 480 -#endif - -#ifndef MA_DEFAULT_PREMIX_STACK_SIZE_PER_CHANNEL -#define MA_DEFAULT_PREMIX_STACK_SIZE_PER_CHANNEL 524288 -#endif - -static ma_result ma_node_read_pcm_frames(ma_node* pNode, ma_uint32 outputBusIndex, float* pFramesOut, ma_uint32 frameCount, ma_uint32* pFramesRead, ma_uint64 globalTime); - -MA_API void ma_debug_fill_pcm_frames_with_sine_wave(float* pFramesOut, ma_uint32 frameCount, ma_format format, ma_uint32 channels, ma_uint32 sampleRate) -{ - #ifndef MA_NO_GENERATION - { - ma_waveform_config waveformConfig; - ma_waveform waveform; - - waveformConfig = ma_waveform_config_init(format, channels, sampleRate, ma_waveform_type_sine, 1.0, 400); - ma_waveform_init(&waveformConfig, &waveform); - ma_waveform_read_pcm_frames(&waveform, pFramesOut, frameCount, NULL); - } - #else - { - (void)pFramesOut; - (void)frameCount; - (void)format; - (void)channels; - (void)sampleRate; - #if defined(MA_DEBUG_OUTPUT) - { - #if _MSC_VER - #pragma message ("ma_debug_fill_pcm_frames_with_sine_wave() will do nothing because MA_NO_GENERATION is enabled.") - #endif - } - #endif - } - #endif -} - - - -MA_API ma_node_graph_config ma_node_graph_config_init(ma_uint32 channels) -{ - ma_node_graph_config config; - - MA_ZERO_OBJECT(&config); - config.channels = channels; - config.processingSizeInFrames = 0; - - return config; -} - - -static void ma_node_graph_set_is_reading(ma_node_graph* pNodeGraph, ma_bool32 isReading) -{ - MA_ASSERT(pNodeGraph != NULL); - ma_atomic_exchange_32(&pNodeGraph->isReading, isReading); -} - -#if 0 -static ma_bool32 ma_node_graph_is_reading(ma_node_graph* pNodeGraph) -{ - MA_ASSERT(pNodeGraph != NULL); - return ma_atomic_load_32(&pNodeGraph->isReading); -} -#endif - - -static void ma_node_graph_node_process_pcm_frames(ma_node* pNode, const float** ppFramesIn, ma_uint32* pFrameCountIn, float** ppFramesOut, ma_uint32* pFrameCountOut) -{ - ma_node_graph* pNodeGraph = (ma_node_graph*)pNode; - ma_uint64 framesRead; - - ma_node_graph_read_pcm_frames(pNodeGraph, ppFramesOut[0], *pFrameCountOut, &framesRead); - - *pFrameCountOut = (ma_uint32)framesRead; /* Safe cast. */ - - (void)ppFramesIn; - (void)pFrameCountIn; -} - -static ma_node_vtable g_node_graph_node_vtable = -{ - ma_node_graph_node_process_pcm_frames, - NULL, /* onGetRequiredInputFrameCount */ - 0, /* 0 input buses. */ - 1, /* 1 output bus. */ - 0 /* Flags. */ -}; - -static void ma_node_graph_endpoint_process_pcm_frames(ma_node* pNode, const float** ppFramesIn, ma_uint32* pFrameCountIn, float** ppFramesOut, ma_uint32* pFrameCountOut) -{ - MA_ASSERT(pNode != NULL); - MA_ASSERT(ma_node_get_input_bus_count(pNode) == 1); - MA_ASSERT(ma_node_get_output_bus_count(pNode) == 1); - - /* Input channel count needs to be the same as the output channel count. */ - MA_ASSERT(ma_node_get_input_channels(pNode, 0) == ma_node_get_output_channels(pNode, 0)); - - /* We don't need to do anything here because it's a passthrough. */ - (void)pNode; - (void)ppFramesIn; - (void)pFrameCountIn; - (void)ppFramesOut; - (void)pFrameCountOut; - -#if 0 - /* The data has already been mixed. We just need to move it to the output buffer. */ - if (ppFramesIn != NULL) { - ma_copy_pcm_frames(ppFramesOut[0], ppFramesIn[0], *pFrameCountOut, ma_format_f32, ma_node_get_output_channels(pNode, 0)); - } -#endif -} - -static ma_node_vtable g_node_graph_endpoint_vtable = -{ - ma_node_graph_endpoint_process_pcm_frames, - NULL, /* onGetRequiredInputFrameCount */ - 1, /* 1 input bus. */ - 1, /* 1 output bus. */ - MA_NODE_FLAG_PASSTHROUGH /* Flags. The endpoint is a passthrough. */ -}; - -MA_API ma_result ma_node_graph_init(const ma_node_graph_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_node_graph* pNodeGraph) -{ - ma_result result; - ma_node_config baseConfig; - ma_node_config endpointConfig; - - if (pNodeGraph == NULL) { - return MA_INVALID_ARGS; - } - - MA_ZERO_OBJECT(pNodeGraph); - pNodeGraph->processingSizeInFrames = pConfig->processingSizeInFrames; - - /* Base node so we can use the node graph as a node into another graph. */ - baseConfig = ma_node_config_init(); - baseConfig.vtable = &g_node_graph_node_vtable; - baseConfig.pOutputChannels = &pConfig->channels; - - result = ma_node_init(pNodeGraph, &baseConfig, pAllocationCallbacks, &pNodeGraph->base); - if (result != MA_SUCCESS) { - return result; - } - - - /* Endpoint. */ - endpointConfig = ma_node_config_init(); - endpointConfig.vtable = &g_node_graph_endpoint_vtable; - endpointConfig.pInputChannels = &pConfig->channels; - endpointConfig.pOutputChannels = &pConfig->channels; - - result = ma_node_init(pNodeGraph, &endpointConfig, pAllocationCallbacks, &pNodeGraph->endpoint); - if (result != MA_SUCCESS) { - ma_node_uninit(&pNodeGraph->base, pAllocationCallbacks); - return result; - } - - - /* Processing cache. */ - if (pConfig->processingSizeInFrames > 0) { - pNodeGraph->pProcessingCache = (float*)ma_malloc(pConfig->processingSizeInFrames * pConfig->channels * sizeof(float), pAllocationCallbacks); - if (pNodeGraph->pProcessingCache == NULL) { - ma_node_uninit(&pNodeGraph->endpoint, pAllocationCallbacks); - ma_node_uninit(&pNodeGraph->base, pAllocationCallbacks); - return MA_OUT_OF_MEMORY; - } - } - - - /* - We need a pre-mix stack. The size of this stack is configurable via the config. The default value depends on the channel count. - */ - { - size_t preMixStackSizeInBytes = pConfig->preMixStackSizeInBytes; - if (preMixStackSizeInBytes == 0) { - preMixStackSizeInBytes = pConfig->channels * MA_DEFAULT_PREMIX_STACK_SIZE_PER_CHANNEL; - } - - pNodeGraph->pPreMixStack = ma_stack_init(preMixStackSizeInBytes, pAllocationCallbacks); - if (pNodeGraph->pPreMixStack == NULL) { - ma_node_uninit(&pNodeGraph->endpoint, pAllocationCallbacks); - ma_node_uninit(&pNodeGraph->base, pAllocationCallbacks); - if (pNodeGraph->pProcessingCache != NULL) { - ma_free(pNodeGraph->pProcessingCache, pAllocationCallbacks); - } - - return MA_OUT_OF_MEMORY; - } - } - - - return MA_SUCCESS; -} - -MA_API void ma_node_graph_uninit(ma_node_graph* pNodeGraph, const ma_allocation_callbacks* pAllocationCallbacks) -{ - if (pNodeGraph == NULL) { - return; - } - - ma_node_uninit(&pNodeGraph->endpoint, pAllocationCallbacks); - ma_node_uninit(&pNodeGraph->base, pAllocationCallbacks); - - if (pNodeGraph->pProcessingCache != NULL) { - ma_free(pNodeGraph->pProcessingCache, pAllocationCallbacks); - pNodeGraph->pProcessingCache = NULL; - } - - if (pNodeGraph->pPreMixStack != NULL) { - ma_stack_uninit(pNodeGraph->pPreMixStack, pAllocationCallbacks); - pNodeGraph->pPreMixStack = NULL; - } -} - -MA_API ma_node* ma_node_graph_get_endpoint(ma_node_graph* pNodeGraph) -{ - if (pNodeGraph == NULL) { - return NULL; - } - - return &pNodeGraph->endpoint; -} - -MA_API ma_result ma_node_graph_read_pcm_frames(ma_node_graph* pNodeGraph, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead) -{ - ma_result result = MA_SUCCESS; - ma_uint64 totalFramesRead; - ma_uint32 channels; - - if (pFramesRead != NULL) { - *pFramesRead = 0; /* Safety. */ - } - - if (pNodeGraph == NULL) { - return MA_INVALID_ARGS; - } - - channels = ma_node_get_output_channels(&pNodeGraph->endpoint, 0); - - - /* We'll be nice and try to do a full read of all frameCount frames. */ - totalFramesRead = 0; - while (totalFramesRead < frameCount) { - ma_uint32 framesJustRead; - ma_uint64 framesToRead; - float* pRunningFramesOut; - - framesToRead = frameCount - totalFramesRead; - if (framesToRead > 0xFFFFFFFF) { - framesToRead = 0xFFFFFFFF; - } - - pRunningFramesOut = (float*)ma_offset_pcm_frames_ptr(pFramesOut, totalFramesRead, ma_format_f32, channels); - - /* If there's anything in the cache, consume that first. */ - if (pNodeGraph->processingCacheFramesRemaining > 0) { - ma_uint32 framesToReadFromCache; - - framesToReadFromCache = (ma_uint32)framesToRead; - if (framesToReadFromCache > pNodeGraph->processingCacheFramesRemaining) { - framesToReadFromCache = pNodeGraph->processingCacheFramesRemaining; - } - - MA_COPY_MEMORY(pRunningFramesOut, pNodeGraph->pProcessingCache, framesToReadFromCache * channels * sizeof(float)); - MA_MOVE_MEMORY(pNodeGraph->pProcessingCache, pNodeGraph->pProcessingCache + (framesToReadFromCache * channels), (pNodeGraph->processingCacheFramesRemaining - framesToReadFromCache) * channels * sizeof(float)); - pNodeGraph->processingCacheFramesRemaining -= framesToReadFromCache; - - totalFramesRead += framesToReadFromCache; - continue; - } else { - /* - If processingSizeInFrames is non-zero, we need to make sure we always read in chunks of that size. If the frame count is less than - that, we need to read into the cache and then continue on. - */ - float* pReadDst = pRunningFramesOut; - - if (pNodeGraph->processingSizeInFrames > 0) { - if (framesToRead < pNodeGraph->processingSizeInFrames) { - pReadDst = pNodeGraph->pProcessingCache; /* We need to read into the cache because otherwise we'll overflow the output buffer. */ - } - - framesToRead = pNodeGraph->processingSizeInFrames; - } - - ma_node_graph_set_is_reading(pNodeGraph, MA_TRUE); - { - result = ma_node_read_pcm_frames(&pNodeGraph->endpoint, 0, pReadDst, (ma_uint32)framesToRead, &framesJustRead, ma_node_get_time(&pNodeGraph->endpoint)); - } - ma_node_graph_set_is_reading(pNodeGraph, MA_FALSE); - - /* - Do not increment the total frames read counter if we read into the cache. We use this to determine how many frames have - been written to the final output buffer. - */ - if (pReadDst == pNodeGraph->pProcessingCache) { - /* We read into the cache. */ - pNodeGraph->processingCacheFramesRemaining = framesJustRead; - } else { - /* We read straight into the output buffer. */ - totalFramesRead += framesJustRead; - } - - if (result != MA_SUCCESS) { - break; - } - - /* Abort if we weren't able to read any frames or else we risk getting stuck in a loop. */ - if (framesJustRead == 0) { - break; - } - } - } - - /* Let's go ahead and silence any leftover frames just for some added safety to ensure the caller doesn't try emitting garbage out of the speakers. */ - if (totalFramesRead < frameCount) { - ma_silence_pcm_frames(ma_offset_pcm_frames_ptr(pFramesOut, totalFramesRead, ma_format_f32, channels), (frameCount - totalFramesRead), ma_format_f32, channels); - } - - if (pFramesRead != NULL) { - *pFramesRead = totalFramesRead; - } - - return result; -} - -MA_API ma_uint32 ma_node_graph_get_channels(const ma_node_graph* pNodeGraph) -{ - if (pNodeGraph == NULL) { - return 0; - } - - return ma_node_get_output_channels(&pNodeGraph->endpoint, 0); -} - -MA_API ma_uint64 ma_node_graph_get_time(const ma_node_graph* pNodeGraph) -{ - if (pNodeGraph == NULL) { - return 0; - } - - return ma_node_get_time(&pNodeGraph->endpoint); /* Global time is just the local time of the endpoint. */ -} - -MA_API ma_result ma_node_graph_set_time(ma_node_graph* pNodeGraph, ma_uint64 globalTime) -{ - if (pNodeGraph == NULL) { - return MA_INVALID_ARGS; - } - - return ma_node_set_time(&pNodeGraph->endpoint, globalTime); /* Global time is just the local time of the endpoint. */ -} - - -#define MA_NODE_OUTPUT_BUS_FLAG_HAS_READ 0x01 /* Whether or not this bus ready to read more data. Only used on nodes with multiple output buses. */ - -static ma_result ma_node_output_bus_init(ma_node* pNode, ma_uint32 outputBusIndex, ma_uint32 channels, ma_node_output_bus* pOutputBus) -{ - MA_ASSERT(pOutputBus != NULL); - MA_ASSERT(outputBusIndex < MA_MAX_NODE_BUS_COUNT); - MA_ASSERT(outputBusIndex < ma_node_get_output_bus_count(pNode)); - MA_ASSERT(channels < 256); - - MA_ZERO_OBJECT(pOutputBus); - - if (channels == 0) { - return MA_INVALID_ARGS; - } - - pOutputBus->pNode = pNode; - pOutputBus->outputBusIndex = (ma_uint8)outputBusIndex; - pOutputBus->channels = (ma_uint8)channels; - pOutputBus->flags = MA_NODE_OUTPUT_BUS_FLAG_HAS_READ; /* <-- Important that this flag is set by default. */ - pOutputBus->volume = 1; - - return MA_SUCCESS; -} - -static void ma_node_output_bus_lock(ma_node_output_bus* pOutputBus) -{ - ma_spinlock_lock(&pOutputBus->lock); -} - -static void ma_node_output_bus_unlock(ma_node_output_bus* pOutputBus) -{ - ma_spinlock_unlock(&pOutputBus->lock); -} - - -static ma_uint32 ma_node_output_bus_get_channels(const ma_node_output_bus* pOutputBus) -{ - return pOutputBus->channels; -} - - -static void ma_node_output_bus_set_has_read(ma_node_output_bus* pOutputBus, ma_bool32 hasRead) -{ - if (hasRead) { - ma_atomic_fetch_or_32(&pOutputBus->flags, MA_NODE_OUTPUT_BUS_FLAG_HAS_READ); - } else { - ma_atomic_fetch_and_32(&pOutputBus->flags, (ma_uint32)~MA_NODE_OUTPUT_BUS_FLAG_HAS_READ); - } -} - -static ma_bool32 ma_node_output_bus_has_read(ma_node_output_bus* pOutputBus) -{ - return (ma_atomic_load_32(&pOutputBus->flags) & MA_NODE_OUTPUT_BUS_FLAG_HAS_READ) != 0; -} - - -static void ma_node_output_bus_set_is_attached(ma_node_output_bus* pOutputBus, ma_bool32 isAttached) -{ - ma_atomic_exchange_32(&pOutputBus->isAttached, isAttached); -} - -static ma_bool32 ma_node_output_bus_is_attached(ma_node_output_bus* pOutputBus) -{ - return ma_atomic_load_32(&pOutputBus->isAttached); -} - - -static ma_result ma_node_output_bus_set_volume(ma_node_output_bus* pOutputBus, float volume) -{ - MA_ASSERT(pOutputBus != NULL); - - if (volume < 0.0f) { - volume = 0.0f; - } - - ma_atomic_exchange_f32(&pOutputBus->volume, volume); - - return MA_SUCCESS; -} - -static float ma_node_output_bus_get_volume(const ma_node_output_bus* pOutputBus) -{ - return ma_atomic_load_f32((float*)&pOutputBus->volume); -} - - -static ma_result ma_node_input_bus_init(ma_uint32 channels, ma_node_input_bus* pInputBus) -{ - MA_ASSERT(pInputBus != NULL); - MA_ASSERT(channels < 256); - - MA_ZERO_OBJECT(pInputBus); - - if (channels == 0) { - return MA_INVALID_ARGS; - } - - pInputBus->channels = (ma_uint8)channels; - - return MA_SUCCESS; -} - -static void ma_node_input_bus_lock(ma_node_input_bus* pInputBus) -{ - MA_ASSERT(pInputBus != NULL); - - ma_spinlock_lock(&pInputBus->lock); -} - -static void ma_node_input_bus_unlock(ma_node_input_bus* pInputBus) -{ - MA_ASSERT(pInputBus != NULL); - - ma_spinlock_unlock(&pInputBus->lock); -} - - -static void ma_node_input_bus_next_begin(ma_node_input_bus* pInputBus) -{ - ma_atomic_fetch_add_32(&pInputBus->nextCounter, 1); -} - -static void ma_node_input_bus_next_end(ma_node_input_bus* pInputBus) -{ - ma_atomic_fetch_sub_32(&pInputBus->nextCounter, 1); -} - -static ma_uint32 ma_node_input_bus_get_next_counter(ma_node_input_bus* pInputBus) -{ - return ma_atomic_load_32(&pInputBus->nextCounter); -} - - -static ma_uint32 ma_node_input_bus_get_channels(const ma_node_input_bus* pInputBus) -{ - return pInputBus->channels; -} - - -static void ma_node_input_bus_detach__no_output_bus_lock(ma_node_input_bus* pInputBus, ma_node_output_bus* pOutputBus) -{ - MA_ASSERT(pInputBus != NULL); - MA_ASSERT(pOutputBus != NULL); - - /* - Mark the output bus as detached first. This will prevent future iterations on the audio thread - from iterating this output bus. - */ - ma_node_output_bus_set_is_attached(pOutputBus, MA_FALSE); - - /* - We cannot use the output bus lock here since it'll be getting used at a higher level, but we do - still need to use the input bus lock since we'll be updating pointers on two different output - buses. The same rules apply here as the attaching case. Although we're using a lock here, we're - *not* using a lock when iterating over the list in the audio thread. We therefore need to craft - this in a way such that the iteration on the audio thread doesn't break. - - The first thing to do is swap out the "next" pointer of the previous output bus with the - new "next" output bus. This is the operation that matters for iteration on the audio thread. - After that, the previous pointer on the new "next" pointer needs to be updated, after which - point the linked list will be in a good state. - */ - ma_node_input_bus_lock(pInputBus); - { - ma_node_output_bus* pOldPrev = (ma_node_output_bus*)ma_atomic_load_ptr(&pOutputBus->pPrev); - ma_node_output_bus* pOldNext = (ma_node_output_bus*)ma_atomic_load_ptr(&pOutputBus->pNext); - - if (pOldPrev != NULL) { - ma_atomic_exchange_ptr(&pOldPrev->pNext, pOldNext); /* <-- This is where the output bus is detached from the list. */ - } - if (pOldNext != NULL) { - ma_atomic_exchange_ptr(&pOldNext->pPrev, pOldPrev); /* <-- This is required for detachment. */ - } - } - ma_node_input_bus_unlock(pInputBus); - - /* At this point the output bus is detached and the linked list is completely unaware of it. Reset some data for safety. */ - ma_atomic_exchange_ptr(&pOutputBus->pNext, NULL); /* Using atomic exchanges here, mainly for the benefit of analysis tools which don't always recognize spinlocks. */ - ma_atomic_exchange_ptr(&pOutputBus->pPrev, NULL); /* As above. */ - pOutputBus->pInputNode = NULL; - pOutputBus->inputNodeInputBusIndex = 0; - - - /* - For thread-safety reasons, we don't want to be returning from this straight away. We need to - wait for the audio thread to finish with the output bus. There's two things we need to wait - for. The first is the part that selects the next output bus in the list, and the other is the - part that reads from the output bus. Basically all we're doing is waiting for the input bus - to stop referencing the output bus. - - We're doing this part last because we want the section above to run while the audio thread - is finishing up with the output bus, just for efficiency reasons. We marked the output bus as - detached right at the top of this function which is going to prevent the audio thread from - iterating the output bus again. - */ - - /* Part 1: Wait for the current iteration to complete. */ - while (ma_node_input_bus_get_next_counter(pInputBus) > 0) { - ma_yield(); - } - - /* Part 2: Wait for any reads to complete. */ - while (ma_atomic_load_32(&pOutputBus->refCount) > 0) { - ma_yield(); - } - - /* - At this point we're done detaching and we can be guaranteed that the audio thread is not going - to attempt to reference this output bus again (until attached again). - */ -} - -#if 0 /* Not used at the moment, but leaving here in case I need it later. */ -static void ma_node_input_bus_detach(ma_node_input_bus* pInputBus, ma_node_output_bus* pOutputBus) -{ - MA_ASSERT(pInputBus != NULL); - MA_ASSERT(pOutputBus != NULL); - - ma_node_output_bus_lock(pOutputBus); - { - ma_node_input_bus_detach__no_output_bus_lock(pInputBus, pOutputBus); - } - ma_node_output_bus_unlock(pOutputBus); -} -#endif - -static void ma_node_input_bus_attach(ma_node_input_bus* pInputBus, ma_node_output_bus* pOutputBus, ma_node* pNewInputNode, ma_uint32 inputNodeInputBusIndex) -{ - MA_ASSERT(pInputBus != NULL); - MA_ASSERT(pOutputBus != NULL); - - ma_node_output_bus_lock(pOutputBus); - { - ma_node_output_bus* pOldInputNode = (ma_node_output_bus*)ma_atomic_load_ptr(&pOutputBus->pInputNode); - - /* Detach from any existing attachment first if necessary. */ - if (pOldInputNode != NULL) { - ma_node_input_bus_detach__no_output_bus_lock(pInputBus, pOutputBus); - } - - /* - At this point we can be sure the output bus is not attached to anything. The linked list in the - old input bus has been updated so that pOutputBus will not get iterated again. - */ - pOutputBus->pInputNode = pNewInputNode; /* No need for an atomic assignment here because modification of this variable always happens within a lock. */ - pOutputBus->inputNodeInputBusIndex = (ma_uint8)inputNodeInputBusIndex; - - /* - Now we need to attach the output bus to the linked list. This involves updating two pointers on - two different output buses so I'm going to go ahead and keep this simple and just use a lock. - There are ways to do this without a lock, but it's just too hard to maintain for its value. - - Although we're locking here, it's important to remember that we're *not* locking when iterating - and reading audio data since that'll be running on the audio thread. As a result we need to be - careful how we craft this so that we don't break iteration. What we're going to do is always - attach the new item so that it becomes the first item in the list. That way, as we're iterating - we won't break any links in the list and iteration will continue safely. The detaching case will - also be crafted in a way as to not break list iteration. It's important to remember to use - atomic exchanges here since no locking is happening on the audio thread during iteration. - */ - ma_node_input_bus_lock(pInputBus); - { - ma_node_output_bus* pNewPrev = &pInputBus->head; - ma_node_output_bus* pNewNext = (ma_node_output_bus*)ma_atomic_load_ptr(&pInputBus->head.pNext); - - /* Update the local output bus. */ - ma_atomic_exchange_ptr(&pOutputBus->pPrev, pNewPrev); - ma_atomic_exchange_ptr(&pOutputBus->pNext, pNewNext); - - /* Update the other output buses to point back to the local output bus. */ - ma_atomic_exchange_ptr(&pInputBus->head.pNext, pOutputBus); /* <-- This is where the output bus is actually attached to the input bus. */ - - /* Do the previous pointer last. This is only used for detachment. */ - if (pNewNext != NULL) { - ma_atomic_exchange_ptr(&pNewNext->pPrev, pOutputBus); - } - } - ma_node_input_bus_unlock(pInputBus); - - /* - Mark the node as attached last. This is used to controlling whether or the output bus will be - iterated on the audio thread. Mainly required for detachment purposes. - */ - ma_node_output_bus_set_is_attached(pOutputBus, MA_TRUE); - } - ma_node_output_bus_unlock(pOutputBus); -} - -static ma_node_output_bus* ma_node_input_bus_next(ma_node_input_bus* pInputBus, ma_node_output_bus* pOutputBus) -{ - ma_node_output_bus* pNext; - - MA_ASSERT(pInputBus != NULL); - - if (pOutputBus == NULL) { - return NULL; - } - - ma_node_input_bus_next_begin(pInputBus); - { - pNext = pOutputBus; - for (;;) { - pNext = (ma_node_output_bus*)ma_atomic_load_ptr(&pNext->pNext); - if (pNext == NULL) { - break; /* Reached the end. */ - } - - if (ma_node_output_bus_is_attached(pNext) == MA_FALSE) { - continue; /* The node is not attached. Keep checking. */ - } - - /* The next node has been selected. */ - break; - } - - /* We need to increment the reference count of the selected node. */ - if (pNext != NULL) { - ma_atomic_fetch_add_32(&pNext->refCount, 1); - } - - /* The previous node is no longer being referenced. */ - ma_atomic_fetch_sub_32(&pOutputBus->refCount, 1); - } - ma_node_input_bus_next_end(pInputBus); - - return pNext; -} - -static ma_node_output_bus* ma_node_input_bus_first(ma_node_input_bus* pInputBus) -{ - return ma_node_input_bus_next(pInputBus, &pInputBus->head); -} - - - -static ma_result ma_node_input_bus_read_pcm_frames(ma_node* pInputNode, ma_node_input_bus* pInputBus, float* pFramesOut, ma_uint32 frameCount, ma_uint32* pFramesRead, ma_uint64 globalTime) -{ - ma_result result = MA_SUCCESS; - ma_node_output_bus* pOutputBus; - ma_node_output_bus* pFirst; - ma_uint32 inputChannels; - ma_bool32 doesOutputBufferHaveContent = MA_FALSE; - - /* - This will be called from the audio thread which means we can't be doing any locking. Basically, - this function will not perform any locking, whereas attaching and detaching will, but crafted in - such a way that we don't need to perform any locking here. The important thing to remember is - to always iterate in a forward direction. - - In order to process any data we need to first read from all input buses. That's where this - function comes in. This iterates over each of the attachments and accumulates/mixes them. We - also convert the channels to the nodes output channel count before mixing. We want to do this - channel conversion so that the caller of this function can invoke the processing callback - without having to do it themselves. - - When we iterate over each of the attachments on the input bus, we need to read as much data as - we can from each of them so that we don't end up with holes between each of the attachments. To - do this, we need to read from each attachment in a loop and read as many frames as we can, up - to `frameCount`. - */ - MA_ASSERT(pInputNode != NULL); - MA_ASSERT(pFramesRead != NULL); /* pFramesRead is critical and must always be specified. On input it's undefined and on output it'll be set to the number of frames actually read. */ - - *pFramesRead = 0; /* Safety. */ - - inputChannels = ma_node_input_bus_get_channels(pInputBus); - - /* - We need to be careful with how we call ma_node_input_bus_first() and ma_node_input_bus_next(). They - are both critical to our lock-free thread-safety system. We can only call ma_node_input_bus_first() - once per iteration, however we have an optimization to checks whether or not it's the first item in - the list. We therefore need to store a pointer to the first item rather than repeatedly calling - ma_node_input_bus_first(). It's safe to keep hold of this pointer, so long as we don't dereference it - after calling ma_node_input_bus_next(), which we won't be. - */ - pFirst = ma_node_input_bus_first(pInputBus); - if (pFirst == NULL) { - return MA_SUCCESS; /* No attachments. Read nothing. */ - } - - for (pOutputBus = pFirst; pOutputBus != NULL; pOutputBus = ma_node_input_bus_next(pInputBus, pOutputBus)) { - ma_uint32 framesProcessed = 0; - ma_bool32 isSilentOutput = MA_FALSE; - - MA_ASSERT(pOutputBus->pNode != NULL); - MA_ASSERT(((ma_node_base*)pOutputBus->pNode)->vtable != NULL); - - isSilentOutput = (((ma_node_base*)pOutputBus->pNode)->vtable->flags & MA_NODE_FLAG_SILENT_OUTPUT) != 0; - - if (pFramesOut != NULL) { - /* Read. */ - while (framesProcessed < frameCount) { - float* pRunningFramesOut; - ma_uint32 framesToRead; - ma_uint32 framesJustRead = 0; - - framesToRead = frameCount - framesProcessed; - pRunningFramesOut = ma_offset_pcm_frames_ptr_f32(pFramesOut, framesProcessed, inputChannels); - - if (doesOutputBufferHaveContent == MA_FALSE) { - /* Fast path. First attachment. We just read straight into the output buffer (no mixing required). */ - result = ma_node_read_pcm_frames(pOutputBus->pNode, pOutputBus->outputBusIndex, pRunningFramesOut, framesToRead, &framesJustRead, globalTime + framesProcessed); - } else { - /* Slow path. Not the first attachment. Mixing required. */ - ma_uint32 preMixBufferCapInFrames = ((ma_node_base*)pInputNode)->cachedDataCapInFramesPerBus; - float* pPreMixBuffer = (float*)ma_stack_alloc(((ma_node_base*)pInputNode)->pNodeGraph->pPreMixStack, preMixBufferCapInFrames * inputChannels * sizeof(float)); - - if (pPreMixBuffer == NULL) { - /* - If you're hitting this assert it means you've got an unusually deep chain of nodes, you've got an excessively large processing - size, or you have a combination of both, and as a result have run out of stack space. You can increase this using the - preMixStackSizeInBytes variable in ma_node_graph_config. If you're using ma_engine, you can do it via the preMixStackSizeInBytes - variable in ma_engine_config. It defaults to 512KB per output channel. - */ - MA_ASSERT(MA_FALSE); - } else { - if (framesToRead > preMixBufferCapInFrames) { - framesToRead = preMixBufferCapInFrames; - } - - result = ma_node_read_pcm_frames(pOutputBus->pNode, pOutputBus->outputBusIndex, pPreMixBuffer, framesToRead, &framesJustRead, globalTime + framesProcessed); - if (result == MA_SUCCESS || result == MA_AT_END) { - if (isSilentOutput == MA_FALSE) { /* Don't mix if the node outputs silence. */ - ma_mix_pcm_frames_f32(pRunningFramesOut, pPreMixBuffer, framesJustRead, inputChannels, /*volume*/1); - } - } - - /* The pre-mix buffer is no longer required. */ - ma_stack_free(((ma_node_base*)pInputNode)->pNodeGraph->pPreMixStack, pPreMixBuffer); - pPreMixBuffer = NULL; - } - } - - framesProcessed += framesJustRead; - - /* If we reached the end or otherwise failed to read any data we need to finish up with this output node. */ - if (result != MA_SUCCESS) { - break; - } - - /* If we didn't read anything, abort so we don't get stuck in a loop. */ - if (framesJustRead == 0) { - break; - } - } - - /* If it's the first attachment we didn't do any mixing. Any leftover samples need to be silenced. */ - if (pOutputBus == pFirst && framesProcessed < frameCount) { - ma_silence_pcm_frames(ma_offset_pcm_frames_ptr(pFramesOut, framesProcessed, ma_format_f32, inputChannels), (frameCount - framesProcessed), ma_format_f32, inputChannels); - } - - if (isSilentOutput == MA_FALSE) { - doesOutputBufferHaveContent = MA_TRUE; - } - } else { - /* Seek. */ - ma_node_read_pcm_frames(pOutputBus->pNode, pOutputBus->outputBusIndex, NULL, frameCount, &framesProcessed, globalTime); - } - } - - /* If we didn't output anything, output silence. */ - if (doesOutputBufferHaveContent == MA_FALSE && pFramesOut != NULL) { - ma_silence_pcm_frames(pFramesOut, frameCount, ma_format_f32, inputChannels); - } - - /* In this path we always "process" the entire amount. */ - *pFramesRead = frameCount; - - return result; -} - - -MA_API ma_node_config ma_node_config_init(void) -{ - ma_node_config config; - - MA_ZERO_OBJECT(&config); - config.initialState = ma_node_state_started; /* Nodes are started by default. */ - config.inputBusCount = MA_NODE_BUS_COUNT_UNKNOWN; - config.outputBusCount = MA_NODE_BUS_COUNT_UNKNOWN; - - return config; -} - -static ma_uint16 ma_node_config_get_cache_size_in_frames(const ma_node_config* pConfig, const ma_node_graph* pNodeGraph) -{ - ma_uint32 cacheSizeInFrames; - - (void)pConfig; - - if (pNodeGraph->processingSizeInFrames > 0) { - cacheSizeInFrames = pNodeGraph->processingSizeInFrames; - } else { - cacheSizeInFrames = MA_DEFAULT_NODE_CACHE_CAP_IN_FRAMES_PER_BUS; - } - - if (cacheSizeInFrames > 0xFFFF) { - cacheSizeInFrames = 0xFFFF; - } - - return (ma_uint16)cacheSizeInFrames; -} - - - -static ma_result ma_node_detach_full(ma_node* pNode); - -static float* ma_node_get_cached_input_ptr(ma_node* pNode, ma_uint32 inputBusIndex) -{ - ma_node_base* pNodeBase = (ma_node_base*)pNode; - ma_uint32 iInputBus; - float* pBasePtr; - - MA_ASSERT(pNodeBase != NULL); - - /* Input data is stored at the front of the buffer. */ - pBasePtr = pNodeBase->pCachedData; - for (iInputBus = 0; iInputBus < inputBusIndex; iInputBus += 1) { - pBasePtr += pNodeBase->cachedDataCapInFramesPerBus * ma_node_input_bus_get_channels(&pNodeBase->pInputBuses[iInputBus]); - } - - return pBasePtr; -} - -static float* ma_node_get_cached_output_ptr(ma_node* pNode, ma_uint32 outputBusIndex) -{ - ma_node_base* pNodeBase = (ma_node_base*)pNode; - ma_uint32 iInputBus; - ma_uint32 iOutputBus; - float* pBasePtr; - - MA_ASSERT(pNodeBase != NULL); - - /* Cached output data starts after the input data. */ - pBasePtr = pNodeBase->pCachedData; - for (iInputBus = 0; iInputBus < ma_node_get_input_bus_count(pNodeBase); iInputBus += 1) { - pBasePtr += pNodeBase->cachedDataCapInFramesPerBus * ma_node_input_bus_get_channels(&pNodeBase->pInputBuses[iInputBus]); - } - - for (iOutputBus = 0; iOutputBus < outputBusIndex; iOutputBus += 1) { - pBasePtr += pNodeBase->cachedDataCapInFramesPerBus * ma_node_output_bus_get_channels(&pNodeBase->pOutputBuses[iOutputBus]); - } - - return pBasePtr; -} - - -typedef struct -{ - size_t sizeInBytes; - size_t inputBusOffset; - size_t outputBusOffset; - size_t cachedDataOffset; - ma_uint32 inputBusCount; /* So it doesn't have to be calculated twice. */ - ma_uint32 outputBusCount; /* So it doesn't have to be calculated twice. */ -} ma_node_heap_layout; - -static ma_result ma_node_translate_bus_counts(const ma_node_config* pConfig, ma_uint32* pInputBusCount, ma_uint32* pOutputBusCount) -{ - ma_uint32 inputBusCount; - ma_uint32 outputBusCount; - - MA_ASSERT(pConfig != NULL); - MA_ASSERT(pInputBusCount != NULL); - MA_ASSERT(pOutputBusCount != NULL); - - /* Bus counts are determined by the vtable, unless they're set to `MA_NODE_BUS_COUNT_UNKNWON`, in which case they're taken from the config. */ - if (pConfig->vtable->inputBusCount == MA_NODE_BUS_COUNT_UNKNOWN) { - inputBusCount = pConfig->inputBusCount; - } else { - inputBusCount = pConfig->vtable->inputBusCount; - - if (pConfig->inputBusCount != MA_NODE_BUS_COUNT_UNKNOWN && pConfig->inputBusCount != pConfig->vtable->inputBusCount) { - return MA_INVALID_ARGS; /* Invalid configuration. You must not specify a conflicting bus count between the node's config and the vtable. */ - } - } - - if (pConfig->vtable->outputBusCount == MA_NODE_BUS_COUNT_UNKNOWN) { - outputBusCount = pConfig->outputBusCount; - } else { - outputBusCount = pConfig->vtable->outputBusCount; - - if (pConfig->outputBusCount != MA_NODE_BUS_COUNT_UNKNOWN && pConfig->outputBusCount != pConfig->vtable->outputBusCount) { - return MA_INVALID_ARGS; /* Invalid configuration. You must not specify a conflicting bus count between the node's config and the vtable. */ - } - } - - /* Bus counts must be within limits. */ - if (inputBusCount > MA_MAX_NODE_BUS_COUNT || outputBusCount > MA_MAX_NODE_BUS_COUNT) { - return MA_INVALID_ARGS; - } - - - /* We must have channel counts for each bus. */ - if ((inputBusCount > 0 && pConfig->pInputChannels == NULL) || (outputBusCount > 0 && pConfig->pOutputChannels == NULL)) { - return MA_INVALID_ARGS; /* You must specify channel counts for each input and output bus. */ - } - - - /* Some special rules for passthrough nodes. */ - if ((pConfig->vtable->flags & MA_NODE_FLAG_PASSTHROUGH) != 0) { - if ((pConfig->vtable->inputBusCount != 0 && pConfig->vtable->inputBusCount != 1) || pConfig->vtable->outputBusCount != 1) { - return MA_INVALID_ARGS; /* Passthrough nodes must have exactly 1 output bus and either 0 or 1 input bus. */ - } - - if (pConfig->pInputChannels[0] != pConfig->pOutputChannels[0]) { - return MA_INVALID_ARGS; /* Passthrough nodes must have the same number of channels between input and output nodes. */ - } - } - - - *pInputBusCount = inputBusCount; - *pOutputBusCount = outputBusCount; - - return MA_SUCCESS; -} - -static ma_result ma_node_get_heap_layout(ma_node_graph* pNodeGraph, const ma_node_config* pConfig, ma_node_heap_layout* pHeapLayout) -{ - ma_result result; - ma_uint32 inputBusCount; - ma_uint32 outputBusCount; - - MA_ASSERT(pHeapLayout != NULL); - - MA_ZERO_OBJECT(pHeapLayout); - - if (pConfig == NULL || pConfig->vtable == NULL || pConfig->vtable->onProcess == NULL) { - return MA_INVALID_ARGS; - } - - result = ma_node_translate_bus_counts(pConfig, &inputBusCount, &outputBusCount); - if (result != MA_SUCCESS) { - return result; - } - - pHeapLayout->sizeInBytes = 0; - - /* Input buses. */ - if (inputBusCount > MA_MAX_NODE_LOCAL_BUS_COUNT) { - pHeapLayout->inputBusOffset = pHeapLayout->sizeInBytes; - pHeapLayout->sizeInBytes += ma_align_64(sizeof(ma_node_input_bus) * inputBusCount); - } else { - pHeapLayout->inputBusOffset = MA_SIZE_MAX; /* MA_SIZE_MAX indicates that no heap allocation is required for the input bus. */ - } - - /* Output buses. */ - if (outputBusCount > MA_MAX_NODE_LOCAL_BUS_COUNT) { - pHeapLayout->outputBusOffset = pHeapLayout->sizeInBytes; - pHeapLayout->sizeInBytes += ma_align_64(sizeof(ma_node_output_bus) * outputBusCount); - } else { - pHeapLayout->outputBusOffset = MA_SIZE_MAX; - } - - /* - Cached audio data. - - We need to allocate memory for caching both input and output data. We have an optimization - where no caching is necessary for specific conditions: - - - The node has 0 inputs and 1 output. - - When a node meets the above conditions, no cache is allocated. - - The size choice for this buffer is a little bit finicky. We don't want to be too wasteful by - allocating too much, but at the same time we want it be large enough so that enough frames can - be processed for each call to ma_node_read_pcm_frames() so that it keeps things efficient. For - now I'm going with 10ms @ 48K which is 480 frames per bus. This is configurable at compile - time. It might also be worth investigating whether or not this can be configured at run time. - */ - if (inputBusCount == 0 && outputBusCount == 1) { - /* Fast path. No cache needed. */ - pHeapLayout->cachedDataOffset = MA_SIZE_MAX; - } else { - /* Slow path. Cache needed. */ - size_t cachedDataSizeInBytes = 0; - ma_uint32 cacheCapInFrames; - ma_uint32 iBus; - - /* The capacity of the cache is based on our callback processing size. */ - cacheCapInFrames = ma_node_config_get_cache_size_in_frames(pConfig, pNodeGraph); - - for (iBus = 0; iBus < inputBusCount; iBus += 1) { - cachedDataSizeInBytes += cacheCapInFrames * ma_get_bytes_per_frame(ma_format_f32, pConfig->pInputChannels[iBus]); - } - - for (iBus = 0; iBus < outputBusCount; iBus += 1) { - cachedDataSizeInBytes += cacheCapInFrames * ma_get_bytes_per_frame(ma_format_f32, pConfig->pOutputChannels[iBus]); - } - - pHeapLayout->cachedDataOffset = pHeapLayout->sizeInBytes; - pHeapLayout->sizeInBytes += ma_align_64(cachedDataSizeInBytes); - } - - - /* - Not technically part of the heap, but we can output the input and output bus counts so we can - avoid a redundant call to ma_node_translate_bus_counts(). - */ - pHeapLayout->inputBusCount = inputBusCount; - pHeapLayout->outputBusCount = outputBusCount; - - /* Make sure allocation size is aligned. */ - pHeapLayout->sizeInBytes = ma_align_64(pHeapLayout->sizeInBytes); - - return MA_SUCCESS; -} - -MA_API ma_result ma_node_get_heap_size(ma_node_graph* pNodeGraph, const ma_node_config* pConfig, size_t* pHeapSizeInBytes) -{ - ma_result result; - ma_node_heap_layout heapLayout; - - if (pHeapSizeInBytes == NULL) { - return MA_INVALID_ARGS; - } - - *pHeapSizeInBytes = 0; - - result = ma_node_get_heap_layout(pNodeGraph, pConfig, &heapLayout); - if (result != MA_SUCCESS) { - return result; - } - - *pHeapSizeInBytes = heapLayout.sizeInBytes; - - return MA_SUCCESS; -} - -MA_API ma_result ma_node_init_preallocated(ma_node_graph* pNodeGraph, const ma_node_config* pConfig, void* pHeap, ma_node* pNode) -{ - ma_node_base* pNodeBase = (ma_node_base*)pNode; - ma_result result; - ma_node_heap_layout heapLayout; - ma_uint32 iInputBus; - ma_uint32 iOutputBus; - - if (pNodeBase == NULL) { - return MA_INVALID_ARGS; - } - - MA_ZERO_OBJECT(pNodeBase); - - result = ma_node_get_heap_layout(pNodeGraph, pConfig, &heapLayout); - if (result != MA_SUCCESS) { - return result; - } - - pNodeBase->_pHeap = pHeap; - MA_ZERO_MEMORY(pHeap, heapLayout.sizeInBytes); - - pNodeBase->pNodeGraph = pNodeGraph; - pNodeBase->vtable = pConfig->vtable; - pNodeBase->state = pConfig->initialState; - pNodeBase->stateTimes[ma_node_state_started] = 0; - pNodeBase->stateTimes[ma_node_state_stopped] = (ma_uint64)(ma_int64)-1; /* Weird casting for VC6 compatibility. */ - pNodeBase->inputBusCount = heapLayout.inputBusCount; - pNodeBase->outputBusCount = heapLayout.outputBusCount; - - if (heapLayout.inputBusOffset != MA_SIZE_MAX) { - pNodeBase->pInputBuses = (ma_node_input_bus*)ma_offset_ptr(pHeap, heapLayout.inputBusOffset); - } else { - pNodeBase->pInputBuses = pNodeBase->_inputBuses; - } - - if (heapLayout.outputBusOffset != MA_SIZE_MAX) { - pNodeBase->pOutputBuses = (ma_node_output_bus*)ma_offset_ptr(pHeap, heapLayout.outputBusOffset); - } else { - pNodeBase->pOutputBuses = pNodeBase->_outputBuses; - } - - if (heapLayout.cachedDataOffset != MA_SIZE_MAX) { - pNodeBase->pCachedData = (float*)ma_offset_ptr(pHeap, heapLayout.cachedDataOffset); - pNodeBase->cachedDataCapInFramesPerBus = ma_node_config_get_cache_size_in_frames(pConfig, pNodeGraph); - } else { - pNodeBase->pCachedData = NULL; - } - - - /* We need to run an initialization step for each input and output bus. */ - for (iInputBus = 0; iInputBus < ma_node_get_input_bus_count(pNodeBase); iInputBus += 1) { - result = ma_node_input_bus_init(pConfig->pInputChannels[iInputBus], &pNodeBase->pInputBuses[iInputBus]); - if (result != MA_SUCCESS) { - return result; - } - } - - for (iOutputBus = 0; iOutputBus < ma_node_get_output_bus_count(pNodeBase); iOutputBus += 1) { - result = ma_node_output_bus_init(pNodeBase, iOutputBus, pConfig->pOutputChannels[iOutputBus], &pNodeBase->pOutputBuses[iOutputBus]); - if (result != MA_SUCCESS) { - return result; - } - } - - - /* The cached data needs to be initialized to silence (or a sine wave tone if we're debugging). */ - if (pNodeBase->pCachedData != NULL) { - ma_uint32 iBus; - - #if 1 /* Toggle this between 0 and 1 to turn debugging on or off. 1 = fill with a sine wave for debugging; 0 = fill with silence. */ - /* For safety we'll go ahead and default the buffer to silence. */ - for (iBus = 0; iBus < ma_node_get_input_bus_count(pNodeBase); iBus += 1) { - ma_silence_pcm_frames(ma_node_get_cached_input_ptr(pNode, iBus), pNodeBase->cachedDataCapInFramesPerBus, ma_format_f32, ma_node_input_bus_get_channels(&pNodeBase->pInputBuses[iBus])); - } - for (iBus = 0; iBus < ma_node_get_output_bus_count(pNodeBase); iBus += 1) { - ma_silence_pcm_frames(ma_node_get_cached_output_ptr(pNode, iBus), pNodeBase->cachedDataCapInFramesPerBus, ma_format_f32, ma_node_output_bus_get_channels(&pNodeBase->pOutputBuses[iBus])); - } - #else - /* For debugging. Default to a sine wave. */ - for (iBus = 0; iBus < ma_node_get_input_bus_count(pNodeBase); iBus += 1) { - ma_debug_fill_pcm_frames_with_sine_wave(ma_node_get_cached_input_ptr(pNode, iBus), pNodeBase->cachedDataCapInFramesPerBus, ma_format_f32, ma_node_input_bus_get_channels(&pNodeBase->pInputBuses[iBus]), 48000); - } - for (iBus = 0; iBus < ma_node_get_output_bus_count(pNodeBase); iBus += 1) { - ma_debug_fill_pcm_frames_with_sine_wave(ma_node_get_cached_output_ptr(pNode, iBus), pNodeBase->cachedDataCapInFramesPerBus, ma_format_f32, ma_node_output_bus_get_channels(&pNodeBase->pOutputBuses[iBus]), 48000); - } - #endif - } - - return MA_SUCCESS; -} - -MA_API ma_result ma_node_init(ma_node_graph* pNodeGraph, const ma_node_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_node* pNode) -{ - ma_result result; - size_t heapSizeInBytes; - void* pHeap; - - result = ma_node_get_heap_size(pNodeGraph, pConfig, &heapSizeInBytes); - if (result != MA_SUCCESS) { - return result; - } - - if (heapSizeInBytes > 0) { - pHeap = ma_malloc(heapSizeInBytes, pAllocationCallbacks); - if (pHeap == NULL) { - return MA_OUT_OF_MEMORY; - } - } else { - pHeap = NULL; - } - - result = ma_node_init_preallocated(pNodeGraph, pConfig, pHeap, pNode); - if (result != MA_SUCCESS) { - ma_free(pHeap, pAllocationCallbacks); - return result; - } - - ((ma_node_base*)pNode)->_ownsHeap = MA_TRUE; - return MA_SUCCESS; -} - -MA_API void ma_node_uninit(ma_node* pNode, const ma_allocation_callbacks* pAllocationCallbacks) -{ - ma_node_base* pNodeBase = (ma_node_base*)pNode; - - if (pNodeBase == NULL) { - return; - } - - /* - The first thing we need to do is fully detach the node. This will detach all inputs and - outputs. We need to do this first because it will sever the connection with the node graph and - allow us to complete uninitialization without needing to worry about thread-safety with the - audio thread. The detachment process will wait for any local processing of the node to finish. - */ - ma_node_detach_full(pNode); - - /* - At this point the node should be completely unreferenced by the node graph and we can finish up - the uninitialization process without needing to worry about thread-safety. - */ - if (pNodeBase->_ownsHeap) { - ma_free(pNodeBase->_pHeap, pAllocationCallbacks); - } -} - -MA_API ma_node_graph* ma_node_get_node_graph(const ma_node* pNode) -{ - if (pNode == NULL) { - return NULL; - } - - return ((const ma_node_base*)pNode)->pNodeGraph; -} - -MA_API ma_uint32 ma_node_get_input_bus_count(const ma_node* pNode) -{ - if (pNode == NULL) { - return 0; - } - - return ((ma_node_base*)pNode)->inputBusCount; -} - -MA_API ma_uint32 ma_node_get_output_bus_count(const ma_node* pNode) -{ - if (pNode == NULL) { - return 0; - } - - return ((ma_node_base*)pNode)->outputBusCount; -} - - -MA_API ma_uint32 ma_node_get_input_channels(const ma_node* pNode, ma_uint32 inputBusIndex) -{ - const ma_node_base* pNodeBase = (const ma_node_base*)pNode; - - if (pNode == NULL) { - return 0; - } - - if (inputBusIndex >= ma_node_get_input_bus_count(pNode)) { - return 0; /* Invalid bus index. */ - } - - return ma_node_input_bus_get_channels(&pNodeBase->pInputBuses[inputBusIndex]); -} - -MA_API ma_uint32 ma_node_get_output_channels(const ma_node* pNode, ma_uint32 outputBusIndex) -{ - const ma_node_base* pNodeBase = (const ma_node_base*)pNode; - - if (pNode == NULL) { - return 0; - } - - if (outputBusIndex >= ma_node_get_output_bus_count(pNode)) { - return 0; /* Invalid bus index. */ - } - - return ma_node_output_bus_get_channels(&pNodeBase->pOutputBuses[outputBusIndex]); -} - - -static ma_result ma_node_detach_full(ma_node* pNode) -{ - ma_node_base* pNodeBase = (ma_node_base*)pNode; - ma_uint32 iInputBus; - - if (pNodeBase == NULL) { - return MA_INVALID_ARGS; - } - - /* - Make sure the node is completely detached first. This will not return until the output bus is - guaranteed to no longer be referenced by the audio thread. - */ - ma_node_detach_all_output_buses(pNode); - - /* - At this point all output buses will have been detached from the graph and we can be guaranteed - that none of its input nodes will be getting processed by the graph. We can detach these - without needing to worry about the audio thread touching them. - */ - for (iInputBus = 0; iInputBus < ma_node_get_input_bus_count(pNode); iInputBus += 1) { - ma_node_input_bus* pInputBus; - ma_node_output_bus* pOutputBus; - - pInputBus = &pNodeBase->pInputBuses[iInputBus]; - - /* - This is important. We cannot be using ma_node_input_bus_first() or ma_node_input_bus_next(). Those - functions are specifically for the audio thread. We'll instead just manually iterate using standard - linked list logic. We don't need to worry about the audio thread referencing these because the step - above severed the connection to the graph. - */ - for (pOutputBus = (ma_node_output_bus*)ma_atomic_load_ptr(&pInputBus->head.pNext); pOutputBus != NULL; pOutputBus = (ma_node_output_bus*)ma_atomic_load_ptr(&pInputBus->head.pNext)) { - ma_node_detach_output_bus(pOutputBus->pNode, pOutputBus->outputBusIndex); /* This won't do any waiting in practice and should be efficient. */ - } - } - - return MA_SUCCESS; -} - -MA_API ma_result ma_node_detach_output_bus(ma_node* pNode, ma_uint32 outputBusIndex) -{ - ma_result result = MA_SUCCESS; - ma_node_base* pNodeBase = (ma_node_base*)pNode; - ma_node_base* pInputNodeBase; - - if (pNode == NULL) { - return MA_INVALID_ARGS; - } - - if (outputBusIndex >= ma_node_get_output_bus_count(pNode)) { - return MA_INVALID_ARGS; /* Invalid output bus index. */ - } - - /* We need to lock the output bus because we need to inspect the input node and grab its input bus. */ - ma_node_output_bus_lock(&pNodeBase->pOutputBuses[outputBusIndex]); - { - pInputNodeBase = (ma_node_base*)pNodeBase->pOutputBuses[outputBusIndex].pInputNode; - if (pInputNodeBase != NULL) { - ma_node_input_bus_detach__no_output_bus_lock(&pInputNodeBase->pInputBuses[pNodeBase->pOutputBuses[outputBusIndex].inputNodeInputBusIndex], &pNodeBase->pOutputBuses[outputBusIndex]); - } - } - ma_node_output_bus_unlock(&pNodeBase->pOutputBuses[outputBusIndex]); - - return result; -} - -MA_API ma_result ma_node_detach_all_output_buses(ma_node* pNode) -{ - ma_uint32 iOutputBus; - - if (pNode == NULL) { - return MA_INVALID_ARGS; - } - - for (iOutputBus = 0; iOutputBus < ma_node_get_output_bus_count(pNode); iOutputBus += 1) { - ma_node_detach_output_bus(pNode, iOutputBus); - } - - return MA_SUCCESS; -} - -MA_API ma_result ma_node_attach_output_bus(ma_node* pNode, ma_uint32 outputBusIndex, ma_node* pOtherNode, ma_uint32 otherNodeInputBusIndex) -{ - ma_node_base* pNodeBase = (ma_node_base*)pNode; - ma_node_base* pOtherNodeBase = (ma_node_base*)pOtherNode; - - if (pNodeBase == NULL || pOtherNodeBase == NULL) { - return MA_INVALID_ARGS; - } - - if (pNodeBase == pOtherNodeBase) { - return MA_INVALID_OPERATION; /* Cannot attach a node to itself. */ - } - - if (outputBusIndex >= ma_node_get_output_bus_count(pNode) || otherNodeInputBusIndex >= ma_node_get_input_bus_count(pOtherNode)) { - return MA_INVALID_OPERATION; /* Invalid bus index. */ - } - - /* The output channel count of the output node must be the same as the input channel count of the input node. */ - if (ma_node_get_output_channels(pNode, outputBusIndex) != ma_node_get_input_channels(pOtherNode, otherNodeInputBusIndex)) { - return MA_INVALID_OPERATION; /* Channel count is incompatible. */ - } - - /* This will deal with detaching if the output bus is already attached to something. */ - ma_node_input_bus_attach(&pOtherNodeBase->pInputBuses[otherNodeInputBusIndex], &pNodeBase->pOutputBuses[outputBusIndex], pOtherNode, otherNodeInputBusIndex); - - return MA_SUCCESS; -} - -MA_API ma_result ma_node_set_output_bus_volume(ma_node* pNode, ma_uint32 outputBusIndex, float volume) -{ - ma_node_base* pNodeBase = (ma_node_base*)pNode; - - if (pNodeBase == NULL) { - return MA_INVALID_ARGS; - } - - if (outputBusIndex >= ma_node_get_output_bus_count(pNode)) { - return MA_INVALID_ARGS; /* Invalid bus index. */ - } - - return ma_node_output_bus_set_volume(&pNodeBase->pOutputBuses[outputBusIndex], volume); -} - -MA_API float ma_node_get_output_bus_volume(const ma_node* pNode, ma_uint32 outputBusIndex) -{ - const ma_node_base* pNodeBase = (const ma_node_base*)pNode; - - if (pNodeBase == NULL) { - return 0; - } - - if (outputBusIndex >= ma_node_get_output_bus_count(pNode)) { - return 0; /* Invalid bus index. */ - } - - return ma_node_output_bus_get_volume(&pNodeBase->pOutputBuses[outputBusIndex]); -} - -MA_API ma_result ma_node_set_state(ma_node* pNode, ma_node_state state) -{ - ma_node_base* pNodeBase = (ma_node_base*)pNode; - - if (pNodeBase == NULL) { - return MA_INVALID_ARGS; - } - - ma_atomic_exchange_i32(&pNodeBase->state, state); - - return MA_SUCCESS; -} - -MA_API ma_node_state ma_node_get_state(const ma_node* pNode) -{ - const ma_node_base* pNodeBase = (const ma_node_base*)pNode; - - if (pNodeBase == NULL) { - return ma_node_state_stopped; - } - - return (ma_node_state)ma_atomic_load_i32(&pNodeBase->state); -} - -MA_API ma_result ma_node_set_state_time(ma_node* pNode, ma_node_state state, ma_uint64 globalTime) -{ - if (pNode == NULL) { - return MA_INVALID_ARGS; - } - - /* Validation check for safety since we'll be using this as an index into stateTimes[]. */ - if (state != ma_node_state_started && state != ma_node_state_stopped) { - return MA_INVALID_ARGS; - } - - ma_atomic_exchange_64(&((ma_node_base*)pNode)->stateTimes[state], globalTime); - - return MA_SUCCESS; -} - -MA_API ma_uint64 ma_node_get_state_time(const ma_node* pNode, ma_node_state state) -{ - if (pNode == NULL) { - return 0; - } - - /* Validation check for safety since we'll be using this as an index into stateTimes[]. */ - if (state != ma_node_state_started && state != ma_node_state_stopped) { - return 0; - } - - return ma_atomic_load_64(&((ma_node_base*)pNode)->stateTimes[state]); -} - -MA_API ma_node_state ma_node_get_state_by_time(const ma_node* pNode, ma_uint64 globalTime) -{ - if (pNode == NULL) { - return ma_node_state_stopped; - } - - return ma_node_get_state_by_time_range(pNode, globalTime, globalTime); -} - -MA_API ma_node_state ma_node_get_state_by_time_range(const ma_node* pNode, ma_uint64 globalTimeBeg, ma_uint64 globalTimeEnd) -{ - ma_node_state state; - - if (pNode == NULL) { - return ma_node_state_stopped; - } - - state = ma_node_get_state(pNode); - - /* An explicitly stopped node is always stopped. */ - if (state == ma_node_state_stopped) { - return ma_node_state_stopped; - } - - /* - Getting here means the node is marked as started, but it may still not be truly started due to - its start time not having been reached yet. Also, the stop time may have also been reached in - which case it'll be considered stopped. - */ - if (ma_node_get_state_time(pNode, ma_node_state_started) > globalTimeBeg) { - return ma_node_state_stopped; /* Start time has not yet been reached. */ - } - - if (ma_node_get_state_time(pNode, ma_node_state_stopped) <= globalTimeEnd) { - return ma_node_state_stopped; /* Stop time has been reached. */ - } - - /* Getting here means the node is marked as started and is within its start/stop times. */ - return ma_node_state_started; -} - -MA_API ma_uint64 ma_node_get_time(const ma_node* pNode) -{ - if (pNode == NULL) { - return 0; - } - - return ma_atomic_load_64(&((ma_node_base*)pNode)->localTime); -} - -MA_API ma_result ma_node_set_time(ma_node* pNode, ma_uint64 localTime) -{ - if (pNode == NULL) { - return MA_INVALID_ARGS; - } - - ma_atomic_exchange_64(&((ma_node_base*)pNode)->localTime, localTime); - - return MA_SUCCESS; -} - - - -static void ma_node_process_pcm_frames_internal(ma_node* pNode, const float** ppFramesIn, ma_uint32* pFrameCountIn, float** ppFramesOut, ma_uint32* pFrameCountOut) -{ - ma_node_base* pNodeBase = (ma_node_base*)pNode; - - MA_ASSERT(pNode != NULL); - - if (pNodeBase->vtable->onProcess) { - pNodeBase->vtable->onProcess(pNode, ppFramesIn, pFrameCountIn, ppFramesOut, pFrameCountOut); - } -} - -static ma_result ma_node_read_pcm_frames(ma_node* pNode, ma_uint32 outputBusIndex, float* pFramesOut, ma_uint32 frameCount, ma_uint32* pFramesRead, ma_uint64 globalTime) -{ - ma_node_base* pNodeBase = (ma_node_base*)pNode; - ma_result result = MA_SUCCESS; - ma_uint32 iInputBus; - ma_uint32 iOutputBus; - ma_uint32 inputBusCount; - ma_uint32 outputBusCount; - ma_uint32 totalFramesRead = 0; - float* ppFramesIn[MA_MAX_NODE_BUS_COUNT]; - float* ppFramesOut[MA_MAX_NODE_BUS_COUNT]; - ma_uint64 globalTimeBeg; - ma_uint64 globalTimeEnd; - ma_uint64 startTime; - ma_uint64 stopTime; - ma_uint32 timeOffsetBeg; - ma_uint32 timeOffsetEnd; - ma_uint32 frameCountIn; - ma_uint32 frameCountOut; - - /* - pFramesRead is mandatory. It must be used to determine how many frames were read. It's normal and - expected that the number of frames read may be different to that requested. Therefore, the caller - must look at this value to correctly determine how many frames were read. - */ - MA_ASSERT(pFramesRead != NULL); /* <-- If you've triggered this assert, you're using this function wrong. You *must* use this variable and inspect it after the call returns. */ - if (pFramesRead == NULL) { - return MA_INVALID_ARGS; - } - - *pFramesRead = 0; /* Safety. */ - - if (pNodeBase == NULL) { - return MA_INVALID_ARGS; - } - - if (outputBusIndex >= ma_node_get_output_bus_count(pNodeBase)) { - return MA_INVALID_ARGS; /* Invalid output bus index. */ - } - - /* Don't do anything if we're in a stopped state. */ - if (ma_node_get_state_by_time_range(pNode, globalTime, globalTime + frameCount) != ma_node_state_started) { - return MA_SUCCESS; /* We're in a stopped state. This is not an error - we just need to not read anything. */ - } - - - globalTimeBeg = globalTime; - globalTimeEnd = globalTime + frameCount; - startTime = ma_node_get_state_time(pNode, ma_node_state_started); - stopTime = ma_node_get_state_time(pNode, ma_node_state_stopped); - - /* - At this point we know that we are inside our start/stop times. However, we may need to adjust - our frame count and output pointer to accommodate since we could be straddling the time period - that this function is getting called for. - - It's possible (and likely) that the start time does not line up with the output buffer. We - therefore need to offset it by a number of frames to accommodate. The same thing applies for - the stop time. - */ - timeOffsetBeg = (globalTimeBeg < startTime) ? (ma_uint32)(globalTimeEnd - startTime) : 0; - timeOffsetEnd = (globalTimeEnd > stopTime) ? (ma_uint32)(globalTimeEnd - stopTime) : 0; - - /* Trim based on the start offset. We need to silence the start of the buffer. */ - if (timeOffsetBeg > 0) { - ma_silence_pcm_frames(pFramesOut, timeOffsetBeg, ma_format_f32, ma_node_get_output_channels(pNode, outputBusIndex)); - pFramesOut += timeOffsetBeg * ma_node_get_output_channels(pNode, outputBusIndex); - frameCount -= timeOffsetBeg; - } - - /* Trim based on the end offset. We don't need to silence the tail section because we'll just have a reduced value written to pFramesRead. */ - if (timeOffsetEnd > 0) { - frameCount -= timeOffsetEnd; - } - - - /* We run on different paths depending on the bus counts. */ - inputBusCount = ma_node_get_input_bus_count(pNode); - outputBusCount = ma_node_get_output_bus_count(pNode); - - /* - Run a simplified path when there are no inputs and one output. In this case there's nothing to - actually read and we can go straight to output. This is a very common scenario because the vast - majority of data source nodes will use this setup so this optimization I think is worthwhile. - */ - if (inputBusCount == 0 && outputBusCount == 1) { - /* Fast path. No need to read from input and no need for any caching. */ - frameCountIn = 0; - frameCountOut = frameCount; /* Just read as much as we can. The callback will return what was actually read. */ - - ppFramesOut[0] = pFramesOut; - - /* - If it's a passthrough we won't be expecting the callback to output anything, so we'll - need to pre-silence the output buffer. - */ - if ((pNodeBase->vtable->flags & MA_NODE_FLAG_PASSTHROUGH) != 0) { - ma_silence_pcm_frames(pFramesOut, frameCount, ma_format_f32, ma_node_get_output_channels(pNode, outputBusIndex)); - } - - ma_node_process_pcm_frames_internal(pNode, NULL, &frameCountIn, ppFramesOut, &frameCountOut); - totalFramesRead = frameCountOut; - } else { - /* Slow path. Need to read input data. */ - if ((pNodeBase->vtable->flags & MA_NODE_FLAG_PASSTHROUGH) != 0) { - /* - Fast path. We're running a passthrough. We need to read directly into the output buffer, but - still fire the callback so that event handling and trigger nodes can do their thing. Since - it's a passthrough there's no need for any kind of caching logic. - */ - MA_ASSERT(outputBusCount == inputBusCount); - MA_ASSERT(outputBusCount == 1); - MA_ASSERT(outputBusIndex == 0); - - /* We just read directly from input bus to output buffer, and then afterwards fire the callback. */ - ppFramesOut[0] = pFramesOut; - ppFramesIn[0] = ppFramesOut[0]; - - result = ma_node_input_bus_read_pcm_frames(pNodeBase, &pNodeBase->pInputBuses[0], ppFramesIn[0], frameCount, &totalFramesRead, globalTime); - if (result == MA_SUCCESS) { - /* Even though it's a passthrough, we still need to fire the callback. */ - frameCountIn = totalFramesRead; - frameCountOut = totalFramesRead; - - if (totalFramesRead > 0) { - ma_node_process_pcm_frames_internal(pNode, (const float**)ppFramesIn, &frameCountIn, ppFramesOut, &frameCountOut); /* From GCC: expected 'const float **' but argument is of type 'float **'. Shouldn't this be implicit? Explicit cast to silence the warning. */ - } - - /* - A passthrough should never have modified the input and output frame counts. If you're - triggering these asserts you need to fix your processing callback. - */ - MA_ASSERT(frameCountIn == totalFramesRead); - MA_ASSERT(frameCountOut == totalFramesRead); - } - } else { - /* Slow path. Need to do caching. */ - ma_uint32 framesToProcessIn; - ma_uint32 framesToProcessOut; - ma_bool32 consumeNullInput = MA_FALSE; - - /* - We use frameCount as a basis for the number of frames to read since that's what's being - requested, however we still need to clamp it to whatever can fit in the cache. - - This will also be used as the basis for determining how many input frames to read. This is - not ideal because it can result in too many input frames being read which introduces latency. - To solve this, nodes can implement an optional callback called onGetRequiredInputFrameCount - which is used as hint to miniaudio as to how many input frames it needs to read at a time. This - callback is completely optional, and if it's not set, miniaudio will assume `frameCount`. - - This function will be called multiple times for each period of time, once for each output node. - We cannot read from each input node each time this function is called. Instead we need to check - whether or not this is first output bus to be read from for this time period, and if so, read - from our input data. - - To determine whether or not we're ready to read data, we check a flag. There will be one flag - for each output. When the flag is set, it means data has been read previously and that we're - ready to advance time forward for our input nodes by reading fresh data. - */ - framesToProcessOut = frameCount; - if (framesToProcessOut > pNodeBase->cachedDataCapInFramesPerBus) { - framesToProcessOut = pNodeBase->cachedDataCapInFramesPerBus; - } - - framesToProcessIn = frameCount; - if (pNodeBase->vtable->onGetRequiredInputFrameCount) { - pNodeBase->vtable->onGetRequiredInputFrameCount(pNode, framesToProcessOut, &framesToProcessIn); /* <-- It does not matter if this fails. */ - } - if (framesToProcessIn > pNodeBase->cachedDataCapInFramesPerBus) { - framesToProcessIn = pNodeBase->cachedDataCapInFramesPerBus; - } - - - MA_ASSERT(framesToProcessIn <= 0xFFFF); - MA_ASSERT(framesToProcessOut <= 0xFFFF); - - if (ma_node_output_bus_has_read(&pNodeBase->pOutputBuses[outputBusIndex])) { - /* Getting here means we need to do another round of processing. */ - pNodeBase->cachedFrameCountOut = 0; - - for (;;) { - frameCountOut = 0; - - /* - We need to prepare our output frame pointers for processing. In the same iteration we need - to mark every output bus as unread so that future calls to this function for different buses - for the current time period don't pull in data when they should instead be reading from cache. - */ - for (iOutputBus = 0; iOutputBus < outputBusCount; iOutputBus += 1) { - ma_node_output_bus_set_has_read(&pNodeBase->pOutputBuses[iOutputBus], MA_FALSE); /* <-- This is what tells the next calls to this function for other output buses for this time period to read from cache instead of pulling in more data. */ - ppFramesOut[iOutputBus] = ma_node_get_cached_output_ptr(pNode, iOutputBus); - } - - /* We only need to read from input buses if there isn't already some data in the cache. */ - if (pNodeBase->cachedFrameCountIn == 0) { - ma_uint32 maxFramesReadIn = 0; - - /* Here is where we pull in data from the input buses. This is what will trigger an advance in time. */ - for (iInputBus = 0; iInputBus < inputBusCount; iInputBus += 1) { - ma_uint32 framesRead; - - /* The first thing to do is get the offset within our bulk allocation to store this input data. */ - ppFramesIn[iInputBus] = ma_node_get_cached_input_ptr(pNode, iInputBus); - - /* Once we've determined our destination pointer we can read. Note that we must inspect the number of frames read and fill any leftovers with silence for safety. */ - result = ma_node_input_bus_read_pcm_frames(pNodeBase, &pNodeBase->pInputBuses[iInputBus], ppFramesIn[iInputBus], framesToProcessIn, &framesRead, globalTime); - if (result != MA_SUCCESS) { - /* It doesn't really matter if we fail because we'll just fill with silence. */ - framesRead = 0; /* Just for safety, but I don't think it's really needed. */ - } - - /* TODO: Minor optimization opportunity here. If no frames were read and the buffer is already filled with silence, no need to re-silence it. */ - /* Any leftover frames need to silenced for safety. */ - if (framesRead < framesToProcessIn) { - ma_silence_pcm_frames(ppFramesIn[iInputBus] + (framesRead * ma_node_get_input_channels(pNodeBase, iInputBus)), (framesToProcessIn - framesRead), ma_format_f32, ma_node_get_input_channels(pNodeBase, iInputBus)); - } - - maxFramesReadIn = ma_max(maxFramesReadIn, framesRead); - } - - /* This was a fresh load of input data so reset our consumption counter. */ - pNodeBase->consumedFrameCountIn = 0; - - /* - We don't want to keep processing if there's nothing to process, so set the number of cached - input frames to the maximum number we read from each attachment (the lesser will be padded - with silence). If we didn't read anything, this will be set to 0 and the entire buffer will - have been assigned to silence. This being equal to 0 is an important property for us because - it allows us to detect when NULL can be passed into the processing callback for the input - buffer for the purpose of continuous processing. - */ - pNodeBase->cachedFrameCountIn = (ma_uint16)maxFramesReadIn; - } else { - /* We don't need to read anything, but we do need to prepare our input frame pointers. */ - for (iInputBus = 0; iInputBus < inputBusCount; iInputBus += 1) { - ppFramesIn[iInputBus] = ma_node_get_cached_input_ptr(pNode, iInputBus) + (pNodeBase->consumedFrameCountIn * ma_node_get_input_channels(pNodeBase, iInputBus)); - } - } - - /* - At this point we have our input data so now we need to do some processing. Sneaky little - optimization here - we can set the pointer to the output buffer for this output bus so - that the final copy into the output buffer is done directly by onProcess(). - */ - if (pFramesOut != NULL) { - ppFramesOut[outputBusIndex] = ma_offset_pcm_frames_ptr_f32(pFramesOut, pNodeBase->cachedFrameCountOut, ma_node_get_output_channels(pNode, outputBusIndex)); - } - - - /* Give the processing function the entire capacity of the output buffer. */ - frameCountOut = (framesToProcessOut - pNodeBase->cachedFrameCountOut); - - /* - We need to treat nodes with continuous processing a little differently. For these ones, - we always want to fire the callback with the requested number of frames, regardless of - pNodeBase->cachedFrameCountIn, which could be 0. Also, we want to check if we can pass - in NULL for the input buffer to the callback. - */ - if ((pNodeBase->vtable->flags & MA_NODE_FLAG_CONTINUOUS_PROCESSING) != 0) { - /* We're using continuous processing. Make sure we specify the whole frame count at all times. */ - frameCountIn = framesToProcessIn; /* Give the processing function as much input data as we've got in the buffer, including any silenced padding from short reads. */ - - if ((pNodeBase->vtable->flags & MA_NODE_FLAG_ALLOW_NULL_INPUT) != 0 && pNodeBase->consumedFrameCountIn == 0 && pNodeBase->cachedFrameCountIn == 0) { - consumeNullInput = MA_TRUE; - } else { - consumeNullInput = MA_FALSE; - } - - /* - Since we're using continuous processing we're always passing in a full frame count - regardless of how much input data was read. If this is greater than what we read as - input, we'll end up with an underflow. We instead need to make sure our cached frame - count is set to the number of frames we'll be passing to the data callback. Not - doing this will result in an underflow when we "consume" the cached data later on. - - Note that this check needs to be done after the "consumeNullInput" check above because - we use the property of cachedFrameCountIn being 0 to determine whether or not we - should be passing in a null pointer to the processing callback for when the node is - configured with MA_NODE_FLAG_ALLOW_NULL_INPUT. - */ - if (pNodeBase->cachedFrameCountIn < (ma_uint16)frameCountIn) { - pNodeBase->cachedFrameCountIn = (ma_uint16)frameCountIn; - } - } else { - frameCountIn = pNodeBase->cachedFrameCountIn; /* Give the processing function as much valid input data as we've got. */ - consumeNullInput = MA_FALSE; - } - - /* - Process data slightly differently depending on whether or not we're consuming NULL - input (checked just above). - */ - if (consumeNullInput) { - ma_node_process_pcm_frames_internal(pNode, NULL, &frameCountIn, ppFramesOut, &frameCountOut); - } else { - /* - We want to skip processing if there's no input data, but we can only do that safely if - we know that there is no chance of any output frames being produced. If continuous - processing is being used, this won't be a problem because the input frame count will - always be non-0. However, if continuous processing is *not* enabled and input and output - data is processed at different rates, we still need to process that last input frame - because there could be a few excess output frames needing to be produced from cached - data. The `MA_NODE_FLAG_DIFFERENT_PROCESSING_RATES` flag is used as the indicator for - determining whether or not we need to process the node even when there are no input - frames available right now. - */ - if (frameCountIn > 0 || (pNodeBase->vtable->flags & MA_NODE_FLAG_DIFFERENT_PROCESSING_RATES) != 0) { - ma_node_process_pcm_frames_internal(pNode, (const float**)ppFramesIn, &frameCountIn, ppFramesOut, &frameCountOut); /* From GCC: expected 'const float **' but argument is of type 'float **'. Shouldn't this be implicit? Explicit cast to silence the warning. */ - } else { - frameCountOut = 0; /* No data was processed. */ - } - } - - /* - Thanks to our sneaky optimization above we don't need to do any data copying directly into - the output buffer - the onProcess() callback just did that for us. We do, however, need to - apply the number of input and output frames that were processed. Note that due to continuous - processing above, we need to do explicit checks here. If we just consumed a NULL input - buffer it means that no actual input data was processed from the internal buffers and we - don't want to be modifying any counters. - */ - if (consumeNullInput == MA_FALSE) { - pNodeBase->consumedFrameCountIn += (ma_uint16)frameCountIn; - pNodeBase->cachedFrameCountIn -= (ma_uint16)frameCountIn; - } - - /* The cached output frame count is always equal to what we just read. */ - pNodeBase->cachedFrameCountOut += (ma_uint16)frameCountOut; - - /* If we couldn't process any data, we're done. The loop needs to be terminated here or else we'll get stuck in a loop. */ - if (pNodeBase->cachedFrameCountOut == framesToProcessOut || (frameCountOut == 0 && frameCountIn == 0)) { - break; - } - } - } else { - /* - We're not needing to read anything from the input buffer so just read directly from our - already-processed data. - */ - if (pFramesOut != NULL) { - ma_copy_pcm_frames(pFramesOut, ma_node_get_cached_output_ptr(pNodeBase, outputBusIndex), pNodeBase->cachedFrameCountOut, ma_format_f32, ma_node_get_output_channels(pNodeBase, outputBusIndex)); - } - } - - /* The number of frames read is always equal to the number of cached output frames. */ - totalFramesRead = pNodeBase->cachedFrameCountOut; - - /* Now that we've read the data, make sure our read flag is set. */ - ma_node_output_bus_set_has_read(&pNodeBase->pOutputBuses[outputBusIndex], MA_TRUE); - } - } - - /* Apply volume, if necessary. */ - ma_apply_volume_factor_f32(pFramesOut, totalFramesRead * ma_node_get_output_channels(pNodeBase, outputBusIndex), ma_node_output_bus_get_volume(&pNodeBase->pOutputBuses[outputBusIndex])); - - /* Advance our local time forward. */ - ma_atomic_fetch_add_64(&pNodeBase->localTime, (ma_uint64)totalFramesRead); - - *pFramesRead = totalFramesRead + timeOffsetBeg; /* Must include the silenced section at the start of the buffer. */ - return result; -} - - - - -/* Data source node. */ -MA_API ma_data_source_node_config ma_data_source_node_config_init(ma_data_source* pDataSource) -{ - ma_data_source_node_config config; - - MA_ZERO_OBJECT(&config); - config.nodeConfig = ma_node_config_init(); - config.pDataSource = pDataSource; - - return config; -} - - -static void ma_data_source_node_process_pcm_frames(ma_node* pNode, const float** ppFramesIn, ma_uint32* pFrameCountIn, float** ppFramesOut, ma_uint32* pFrameCountOut) -{ - ma_data_source_node* pDataSourceNode = (ma_data_source_node*)pNode; - ma_format format; - ma_uint32 channels; - ma_uint32 frameCount; - ma_uint64 framesRead = 0; - - MA_ASSERT(pDataSourceNode != NULL); - MA_ASSERT(pDataSourceNode->pDataSource != NULL); - MA_ASSERT(ma_node_get_input_bus_count(pDataSourceNode) == 0); - MA_ASSERT(ma_node_get_output_bus_count(pDataSourceNode) == 1); - - /* We don't want to read from ppFramesIn at all. Instead we read from the data source. */ - (void)ppFramesIn; - (void)pFrameCountIn; - - frameCount = *pFrameCountOut; - - /* miniaudio should never be calling this with a frame count of zero. */ - MA_ASSERT(frameCount > 0); - - if (ma_data_source_get_data_format(pDataSourceNode->pDataSource, &format, &channels, NULL, NULL, 0) == MA_SUCCESS) { /* <-- Don't care about sample rate here. */ - /* The node graph system requires samples be in floating point format. This is checked in ma_data_source_node_init(). */ - MA_ASSERT(format == ma_format_f32); - (void)format; /* Just to silence some static analysis tools. */ - - ma_data_source_read_pcm_frames(pDataSourceNode->pDataSource, ppFramesOut[0], frameCount, &framesRead); - } - - *pFrameCountOut = (ma_uint32)framesRead; -} - -static ma_node_vtable g_ma_data_source_node_vtable = -{ - ma_data_source_node_process_pcm_frames, - NULL, /* onGetRequiredInputFrameCount */ - 0, /* 0 input buses. */ - 1, /* 1 output bus. */ - 0 -}; - -MA_API ma_result ma_data_source_node_init(ma_node_graph* pNodeGraph, const ma_data_source_node_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_data_source_node* pDataSourceNode) -{ - ma_result result; - ma_format format; /* For validating the format, which must be ma_format_f32. */ - ma_uint32 channels; /* For specifying the channel count of the output bus. */ - ma_node_config baseConfig; - - if (pDataSourceNode == NULL) { - return MA_INVALID_ARGS; - } - - MA_ZERO_OBJECT(pDataSourceNode); - - if (pConfig == NULL) { - return MA_INVALID_ARGS; - } - - result = ma_data_source_get_data_format(pConfig->pDataSource, &format, &channels, NULL, NULL, 0); /* Don't care about sample rate. This will check pDataSource for NULL. */ - if (result != MA_SUCCESS) { - return result; - } - - MA_ASSERT(format == ma_format_f32); /* <-- If you've triggered this it means your data source is not outputting floating-point samples. You must configure your data source to use ma_format_f32. */ - if (format != ma_format_f32) { - return MA_INVALID_ARGS; /* Invalid format. */ - } - - /* The channel count is defined by the data source. If the caller has manually changed the channels we just ignore it. */ - baseConfig = pConfig->nodeConfig; - baseConfig.vtable = &g_ma_data_source_node_vtable; /* Explicitly set the vtable here to prevent callers from setting it incorrectly. */ - - /* - The channel count is defined by the data source. It is invalid for the caller to manually set - the channel counts in the config. `ma_data_source_node_config_init()` will have defaulted the - channel count pointer to NULL which is how it must remain. If you trigger any of these asserts - it means you're explicitly setting the channel count. Instead, configure the output channel - count of your data source to be the necessary channel count. - */ - if (baseConfig.pOutputChannels != NULL) { - return MA_INVALID_ARGS; - } - - baseConfig.pOutputChannels = &channels; - - result = ma_node_init(pNodeGraph, &baseConfig, pAllocationCallbacks, &pDataSourceNode->base); - if (result != MA_SUCCESS) { - return result; - } - - pDataSourceNode->pDataSource = pConfig->pDataSource; - - return MA_SUCCESS; -} - -MA_API void ma_data_source_node_uninit(ma_data_source_node* pDataSourceNode, const ma_allocation_callbacks* pAllocationCallbacks) -{ - ma_node_uninit(&pDataSourceNode->base, pAllocationCallbacks); -} - -MA_API ma_result ma_data_source_node_set_looping(ma_data_source_node* pDataSourceNode, ma_bool32 isLooping) -{ - if (pDataSourceNode == NULL) { - return MA_INVALID_ARGS; - } - - return ma_data_source_set_looping(pDataSourceNode->pDataSource, isLooping); -} - -MA_API ma_bool32 ma_data_source_node_is_looping(ma_data_source_node* pDataSourceNode) -{ - if (pDataSourceNode == NULL) { - return MA_FALSE; - } - - return ma_data_source_is_looping(pDataSourceNode->pDataSource); -} - - - -/* Splitter Node. */ -MA_API ma_splitter_node_config ma_splitter_node_config_init(ma_uint32 channels) -{ - ma_splitter_node_config config; - - MA_ZERO_OBJECT(&config); - config.nodeConfig = ma_node_config_init(); - config.channels = channels; - config.outputBusCount = 2; - - return config; -} - - -static void ma_splitter_node_process_pcm_frames(ma_node* pNode, const float** ppFramesIn, ma_uint32* pFrameCountIn, float** ppFramesOut, ma_uint32* pFrameCountOut) -{ - ma_node_base* pNodeBase = (ma_node_base*)pNode; - ma_uint32 iOutputBus; - ma_uint32 channels; - - MA_ASSERT(pNodeBase != NULL); - MA_ASSERT(ma_node_get_input_bus_count(pNodeBase) == 1); - - /* We don't need to consider the input frame count - it'll be the same as the output frame count and we process everything. */ - (void)pFrameCountIn; - - /* NOTE: This assumes the same number of channels for all inputs and outputs. This was checked in ma_splitter_node_init(). */ - channels = ma_node_get_input_channels(pNodeBase, 0); - - /* Splitting is just copying the first input bus and copying it over to each output bus. */ - for (iOutputBus = 0; iOutputBus < ma_node_get_output_bus_count(pNodeBase); iOutputBus += 1) { - ma_copy_pcm_frames(ppFramesOut[iOutputBus], ppFramesIn[0], *pFrameCountOut, ma_format_f32, channels); - } -} - -static ma_node_vtable g_ma_splitter_node_vtable = -{ - ma_splitter_node_process_pcm_frames, - NULL, /* onGetRequiredInputFrameCount */ - 1, /* 1 input bus. */ - MA_NODE_BUS_COUNT_UNKNOWN, /* The output bus count is specified on a per-node basis. */ - 0 -}; - -MA_API ma_result ma_splitter_node_init(ma_node_graph* pNodeGraph, const ma_splitter_node_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_splitter_node* pSplitterNode) -{ - ma_result result; - ma_node_config baseConfig; - ma_uint32 pInputChannels[1]; - ma_uint32 pOutputChannels[MA_MAX_NODE_BUS_COUNT]; - ma_uint32 iOutputBus; - - if (pSplitterNode == NULL) { - return MA_INVALID_ARGS; - } - - MA_ZERO_OBJECT(pSplitterNode); - - if (pConfig == NULL) { - return MA_INVALID_ARGS; - } - - if (pConfig->outputBusCount > MA_MAX_NODE_BUS_COUNT) { - return MA_INVALID_ARGS; /* Too many output buses. */ - } - - /* Splitters require the same number of channels between inputs and outputs. */ - pInputChannels[0] = pConfig->channels; - for (iOutputBus = 0; iOutputBus < pConfig->outputBusCount; iOutputBus += 1) { - pOutputChannels[iOutputBus] = pConfig->channels; - } - - baseConfig = pConfig->nodeConfig; - baseConfig.vtable = &g_ma_splitter_node_vtable; - baseConfig.pInputChannels = pInputChannels; - baseConfig.pOutputChannels = pOutputChannels; - baseConfig.outputBusCount = pConfig->outputBusCount; - - result = ma_node_init(pNodeGraph, &baseConfig, pAllocationCallbacks, &pSplitterNode->base); - if (result != MA_SUCCESS) { - return result; /* Failed to initialize the base node. */ - } - - return MA_SUCCESS; -} - -MA_API void ma_splitter_node_uninit(ma_splitter_node* pSplitterNode, const ma_allocation_callbacks* pAllocationCallbacks) -{ - ma_node_uninit(pSplitterNode, pAllocationCallbacks); -} - - -/* -Biquad Node -*/ -MA_API ma_biquad_node_config ma_biquad_node_config_init(ma_uint32 channels, float b0, float b1, float b2, float a0, float a1, float a2) -{ - ma_biquad_node_config config; - - config.nodeConfig = ma_node_config_init(); - config.biquad = ma_biquad_config_init(ma_format_f32, channels, b0, b1, b2, a0, a1, a2); - - return config; -} - -static void ma_biquad_node_process_pcm_frames(ma_node* pNode, const float** ppFramesIn, ma_uint32* pFrameCountIn, float** ppFramesOut, ma_uint32* pFrameCountOut) -{ - ma_biquad_node* pLPFNode = (ma_biquad_node*)pNode; - - MA_ASSERT(pNode != NULL); - (void)pFrameCountIn; - - ma_biquad_process_pcm_frames(&pLPFNode->biquad, ppFramesOut[0], ppFramesIn[0], *pFrameCountOut); -} - -static ma_node_vtable g_ma_biquad_node_vtable = -{ - ma_biquad_node_process_pcm_frames, - NULL, /* onGetRequiredInputFrameCount */ - 1, /* One input. */ - 1, /* One output. */ - 0 /* Default flags. */ -}; - -MA_API ma_result ma_biquad_node_init(ma_node_graph* pNodeGraph, const ma_biquad_node_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_biquad_node* pNode) -{ - ma_result result; - ma_node_config baseNodeConfig; - - if (pNode == NULL) { - return MA_INVALID_ARGS; - } - - MA_ZERO_OBJECT(pNode); - - if (pConfig == NULL) { - return MA_INVALID_ARGS; - } - - if (pConfig->biquad.format != ma_format_f32) { - return MA_INVALID_ARGS; /* The format must be f32. */ - } - - result = ma_biquad_init(&pConfig->biquad, pAllocationCallbacks, &pNode->biquad); - if (result != MA_SUCCESS) { - return result; - } - - baseNodeConfig = ma_node_config_init(); - baseNodeConfig.vtable = &g_ma_biquad_node_vtable; - baseNodeConfig.pInputChannels = &pConfig->biquad.channels; - baseNodeConfig.pOutputChannels = &pConfig->biquad.channels; - - result = ma_node_init(pNodeGraph, &baseNodeConfig, pAllocationCallbacks, pNode); - if (result != MA_SUCCESS) { - return result; - } - - return result; -} - -MA_API ma_result ma_biquad_node_reinit(const ma_biquad_config* pConfig, ma_biquad_node* pNode) -{ - ma_biquad_node* pLPFNode = (ma_biquad_node*)pNode; - - MA_ASSERT(pNode != NULL); - - return ma_biquad_reinit(pConfig, &pLPFNode->biquad); -} - -MA_API void ma_biquad_node_uninit(ma_biquad_node* pNode, const ma_allocation_callbacks* pAllocationCallbacks) -{ - ma_biquad_node* pLPFNode = (ma_biquad_node*)pNode; - - if (pNode == NULL) { - return; - } - - ma_node_uninit(pNode, pAllocationCallbacks); - ma_biquad_uninit(&pLPFNode->biquad, pAllocationCallbacks); -} - - - -/* -Low Pass Filter Node -*/ -MA_API ma_lpf_node_config ma_lpf_node_config_init(ma_uint32 channels, ma_uint32 sampleRate, double cutoffFrequency, ma_uint32 order) -{ - ma_lpf_node_config config; - - config.nodeConfig = ma_node_config_init(); - config.lpf = ma_lpf_config_init(ma_format_f32, channels, sampleRate, cutoffFrequency, order); - - return config; -} - -static void ma_lpf_node_process_pcm_frames(ma_node* pNode, const float** ppFramesIn, ma_uint32* pFrameCountIn, float** ppFramesOut, ma_uint32* pFrameCountOut) -{ - ma_lpf_node* pLPFNode = (ma_lpf_node*)pNode; - - MA_ASSERT(pNode != NULL); - (void)pFrameCountIn; - - ma_lpf_process_pcm_frames(&pLPFNode->lpf, ppFramesOut[0], ppFramesIn[0], *pFrameCountOut); -} - -static ma_node_vtable g_ma_lpf_node_vtable = -{ - ma_lpf_node_process_pcm_frames, - NULL, /* onGetRequiredInputFrameCount */ - 1, /* One input. */ - 1, /* One output. */ - 0 /* Default flags. */ -}; - -MA_API ma_result ma_lpf_node_init(ma_node_graph* pNodeGraph, const ma_lpf_node_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_lpf_node* pNode) -{ - ma_result result; - ma_node_config baseNodeConfig; - - if (pNode == NULL) { - return MA_INVALID_ARGS; - } - - MA_ZERO_OBJECT(pNode); - - if (pConfig == NULL) { - return MA_INVALID_ARGS; - } - - if (pConfig->lpf.format != ma_format_f32) { - return MA_INVALID_ARGS; /* The format must be f32. */ - } - - result = ma_lpf_init(&pConfig->lpf, pAllocationCallbacks, &pNode->lpf); - if (result != MA_SUCCESS) { - return result; - } - - baseNodeConfig = ma_node_config_init(); - baseNodeConfig.vtable = &g_ma_lpf_node_vtable; - baseNodeConfig.pInputChannels = &pConfig->lpf.channels; - baseNodeConfig.pOutputChannels = &pConfig->lpf.channels; - - result = ma_node_init(pNodeGraph, &baseNodeConfig, pAllocationCallbacks, pNode); - if (result != MA_SUCCESS) { - return result; - } - - return result; -} - -MA_API ma_result ma_lpf_node_reinit(const ma_lpf_config* pConfig, ma_lpf_node* pNode) -{ - ma_lpf_node* pLPFNode = (ma_lpf_node*)pNode; - - if (pNode == NULL) { - return MA_INVALID_ARGS; - } - - return ma_lpf_reinit(pConfig, &pLPFNode->lpf); -} - -MA_API void ma_lpf_node_uninit(ma_lpf_node* pNode, const ma_allocation_callbacks* pAllocationCallbacks) -{ - ma_lpf_node* pLPFNode = (ma_lpf_node*)pNode; - - if (pNode == NULL) { - return; - } - - ma_node_uninit(pNode, pAllocationCallbacks); - ma_lpf_uninit(&pLPFNode->lpf, pAllocationCallbacks); -} - - - -/* -High Pass Filter Node -*/ -MA_API ma_hpf_node_config ma_hpf_node_config_init(ma_uint32 channels, ma_uint32 sampleRate, double cutoffFrequency, ma_uint32 order) -{ - ma_hpf_node_config config; - - config.nodeConfig = ma_node_config_init(); - config.hpf = ma_hpf_config_init(ma_format_f32, channels, sampleRate, cutoffFrequency, order); - - return config; -} - -static void ma_hpf_node_process_pcm_frames(ma_node* pNode, const float** ppFramesIn, ma_uint32* pFrameCountIn, float** ppFramesOut, ma_uint32* pFrameCountOut) -{ - ma_hpf_node* pHPFNode = (ma_hpf_node*)pNode; - - MA_ASSERT(pNode != NULL); - (void)pFrameCountIn; - - ma_hpf_process_pcm_frames(&pHPFNode->hpf, ppFramesOut[0], ppFramesIn[0], *pFrameCountOut); -} - -static ma_node_vtable g_ma_hpf_node_vtable = -{ - ma_hpf_node_process_pcm_frames, - NULL, /* onGetRequiredInputFrameCount */ - 1, /* One input. */ - 1, /* One output. */ - 0 /* Default flags. */ -}; - -MA_API ma_result ma_hpf_node_init(ma_node_graph* pNodeGraph, const ma_hpf_node_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_hpf_node* pNode) -{ - ma_result result; - ma_node_config baseNodeConfig; - - if (pNode == NULL) { - return MA_INVALID_ARGS; - } - - MA_ZERO_OBJECT(pNode); - - if (pConfig == NULL) { - return MA_INVALID_ARGS; - } - - if (pConfig->hpf.format != ma_format_f32) { - return MA_INVALID_ARGS; /* The format must be f32. */ - } - - result = ma_hpf_init(&pConfig->hpf, pAllocationCallbacks, &pNode->hpf); - if (result != MA_SUCCESS) { - return result; - } - - baseNodeConfig = ma_node_config_init(); - baseNodeConfig.vtable = &g_ma_hpf_node_vtable; - baseNodeConfig.pInputChannels = &pConfig->hpf.channels; - baseNodeConfig.pOutputChannels = &pConfig->hpf.channels; - - result = ma_node_init(pNodeGraph, &baseNodeConfig, pAllocationCallbacks, pNode); - if (result != MA_SUCCESS) { - return result; - } - - return result; -} - -MA_API ma_result ma_hpf_node_reinit(const ma_hpf_config* pConfig, ma_hpf_node* pNode) -{ - ma_hpf_node* pHPFNode = (ma_hpf_node*)pNode; - - if (pNode == NULL) { - return MA_INVALID_ARGS; - } - - return ma_hpf_reinit(pConfig, &pHPFNode->hpf); -} - -MA_API void ma_hpf_node_uninit(ma_hpf_node* pNode, const ma_allocation_callbacks* pAllocationCallbacks) -{ - ma_hpf_node* pHPFNode = (ma_hpf_node*)pNode; - - if (pNode == NULL) { - return; - } - - ma_node_uninit(pNode, pAllocationCallbacks); - ma_hpf_uninit(&pHPFNode->hpf, pAllocationCallbacks); -} - - - - -/* -Band Pass Filter Node -*/ -MA_API ma_bpf_node_config ma_bpf_node_config_init(ma_uint32 channels, ma_uint32 sampleRate, double cutoffFrequency, ma_uint32 order) -{ - ma_bpf_node_config config; - - config.nodeConfig = ma_node_config_init(); - config.bpf = ma_bpf_config_init(ma_format_f32, channels, sampleRate, cutoffFrequency, order); - - return config; -} - -static void ma_bpf_node_process_pcm_frames(ma_node* pNode, const float** ppFramesIn, ma_uint32* pFrameCountIn, float** ppFramesOut, ma_uint32* pFrameCountOut) -{ - ma_bpf_node* pBPFNode = (ma_bpf_node*)pNode; - - MA_ASSERT(pNode != NULL); - (void)pFrameCountIn; - - ma_bpf_process_pcm_frames(&pBPFNode->bpf, ppFramesOut[0], ppFramesIn[0], *pFrameCountOut); -} - -static ma_node_vtable g_ma_bpf_node_vtable = -{ - ma_bpf_node_process_pcm_frames, - NULL, /* onGetRequiredInputFrameCount */ - 1, /* One input. */ - 1, /* One output. */ - 0 /* Default flags. */ -}; - -MA_API ma_result ma_bpf_node_init(ma_node_graph* pNodeGraph, const ma_bpf_node_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_bpf_node* pNode) -{ - ma_result result; - ma_node_config baseNodeConfig; - - if (pNode == NULL) { - return MA_INVALID_ARGS; - } - - MA_ZERO_OBJECT(pNode); - - if (pConfig == NULL) { - return MA_INVALID_ARGS; - } - - if (pConfig->bpf.format != ma_format_f32) { - return MA_INVALID_ARGS; /* The format must be f32. */ - } - - result = ma_bpf_init(&pConfig->bpf, pAllocationCallbacks, &pNode->bpf); - if (result != MA_SUCCESS) { - return result; - } - - baseNodeConfig = ma_node_config_init(); - baseNodeConfig.vtable = &g_ma_bpf_node_vtable; - baseNodeConfig.pInputChannels = &pConfig->bpf.channels; - baseNodeConfig.pOutputChannels = &pConfig->bpf.channels; - - result = ma_node_init(pNodeGraph, &baseNodeConfig, pAllocationCallbacks, pNode); - if (result != MA_SUCCESS) { - return result; - } - - return result; -} - -MA_API ma_result ma_bpf_node_reinit(const ma_bpf_config* pConfig, ma_bpf_node* pNode) -{ - ma_bpf_node* pBPFNode = (ma_bpf_node*)pNode; - - if (pNode == NULL) { - return MA_INVALID_ARGS; - } - - return ma_bpf_reinit(pConfig, &pBPFNode->bpf); -} - -MA_API void ma_bpf_node_uninit(ma_bpf_node* pNode, const ma_allocation_callbacks* pAllocationCallbacks) -{ - ma_bpf_node* pBPFNode = (ma_bpf_node*)pNode; - - if (pNode == NULL) { - return; - } - - ma_node_uninit(pNode, pAllocationCallbacks); - ma_bpf_uninit(&pBPFNode->bpf, pAllocationCallbacks); -} - - - -/* -Notching Filter Node -*/ -MA_API ma_notch_node_config ma_notch_node_config_init(ma_uint32 channels, ma_uint32 sampleRate, double q, double frequency) -{ - ma_notch_node_config config; - - config.nodeConfig = ma_node_config_init(); - config.notch = ma_notch2_config_init(ma_format_f32, channels, sampleRate, q, frequency); - - return config; -} - -static void ma_notch_node_process_pcm_frames(ma_node* pNode, const float** ppFramesIn, ma_uint32* pFrameCountIn, float** ppFramesOut, ma_uint32* pFrameCountOut) -{ - ma_notch_node* pBPFNode = (ma_notch_node*)pNode; - - MA_ASSERT(pNode != NULL); - (void)pFrameCountIn; - - ma_notch2_process_pcm_frames(&pBPFNode->notch, ppFramesOut[0], ppFramesIn[0], *pFrameCountOut); -} - -static ma_node_vtable g_ma_notch_node_vtable = -{ - ma_notch_node_process_pcm_frames, - NULL, /* onGetRequiredInputFrameCount */ - 1, /* One input. */ - 1, /* One output. */ - 0 /* Default flags. */ -}; - -MA_API ma_result ma_notch_node_init(ma_node_graph* pNodeGraph, const ma_notch_node_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_notch_node* pNode) -{ - ma_result result; - ma_node_config baseNodeConfig; - - if (pNode == NULL) { - return MA_INVALID_ARGS; - } - - MA_ZERO_OBJECT(pNode); - - if (pConfig == NULL) { - return MA_INVALID_ARGS; - } - - if (pConfig->notch.format != ma_format_f32) { - return MA_INVALID_ARGS; /* The format must be f32. */ - } - - result = ma_notch2_init(&pConfig->notch, pAllocationCallbacks, &pNode->notch); - if (result != MA_SUCCESS) { - return result; - } - - baseNodeConfig = ma_node_config_init(); - baseNodeConfig.vtable = &g_ma_notch_node_vtable; - baseNodeConfig.pInputChannels = &pConfig->notch.channels; - baseNodeConfig.pOutputChannels = &pConfig->notch.channels; - - result = ma_node_init(pNodeGraph, &baseNodeConfig, pAllocationCallbacks, pNode); - if (result != MA_SUCCESS) { - return result; - } - - return result; -} - -MA_API ma_result ma_notch_node_reinit(const ma_notch_config* pConfig, ma_notch_node* pNode) -{ - ma_notch_node* pNotchNode = (ma_notch_node*)pNode; - - if (pNode == NULL) { - return MA_INVALID_ARGS; - } - - return ma_notch2_reinit(pConfig, &pNotchNode->notch); -} - -MA_API void ma_notch_node_uninit(ma_notch_node* pNode, const ma_allocation_callbacks* pAllocationCallbacks) -{ - ma_notch_node* pNotchNode = (ma_notch_node*)pNode; - - if (pNode == NULL) { - return; - } - - ma_node_uninit(pNode, pAllocationCallbacks); - ma_notch2_uninit(&pNotchNode->notch, pAllocationCallbacks); -} - - - -/* -Peaking Filter Node -*/ -MA_API ma_peak_node_config ma_peak_node_config_init(ma_uint32 channels, ma_uint32 sampleRate, double gainDB, double q, double frequency) -{ - ma_peak_node_config config; - - config.nodeConfig = ma_node_config_init(); - config.peak = ma_peak2_config_init(ma_format_f32, channels, sampleRate, gainDB, q, frequency); - - return config; -} - -static void ma_peak_node_process_pcm_frames(ma_node* pNode, const float** ppFramesIn, ma_uint32* pFrameCountIn, float** ppFramesOut, ma_uint32* pFrameCountOut) -{ - ma_peak_node* pBPFNode = (ma_peak_node*)pNode; - - MA_ASSERT(pNode != NULL); - (void)pFrameCountIn; - - ma_peak2_process_pcm_frames(&pBPFNode->peak, ppFramesOut[0], ppFramesIn[0], *pFrameCountOut); -} - -static ma_node_vtable g_ma_peak_node_vtable = -{ - ma_peak_node_process_pcm_frames, - NULL, /* onGetRequiredInputFrameCount */ - 1, /* One input. */ - 1, /* One output. */ - 0 /* Default flags. */ -}; - -MA_API ma_result ma_peak_node_init(ma_node_graph* pNodeGraph, const ma_peak_node_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_peak_node* pNode) -{ - ma_result result; - ma_node_config baseNodeConfig; - - if (pNode == NULL) { - return MA_INVALID_ARGS; - } - - MA_ZERO_OBJECT(pNode); - - if (pConfig == NULL) { - return MA_INVALID_ARGS; - } - - if (pConfig->peak.format != ma_format_f32) { - return MA_INVALID_ARGS; /* The format must be f32. */ - } - - result = ma_peak2_init(&pConfig->peak, pAllocationCallbacks, &pNode->peak); - if (result != MA_SUCCESS) { - ma_node_uninit(pNode, pAllocationCallbacks); - return result; - } - - baseNodeConfig = ma_node_config_init(); - baseNodeConfig.vtable = &g_ma_peak_node_vtable; - baseNodeConfig.pInputChannels = &pConfig->peak.channels; - baseNodeConfig.pOutputChannels = &pConfig->peak.channels; - - result = ma_node_init(pNodeGraph, &baseNodeConfig, pAllocationCallbacks, pNode); - if (result != MA_SUCCESS) { - return result; - } - - return result; -} - -MA_API ma_result ma_peak_node_reinit(const ma_peak_config* pConfig, ma_peak_node* pNode) -{ - ma_peak_node* pPeakNode = (ma_peak_node*)pNode; - - if (pNode == NULL) { - return MA_INVALID_ARGS; - } - - return ma_peak2_reinit(pConfig, &pPeakNode->peak); -} - -MA_API void ma_peak_node_uninit(ma_peak_node* pNode, const ma_allocation_callbacks* pAllocationCallbacks) -{ - ma_peak_node* pPeakNode = (ma_peak_node*)pNode; - - if (pNode == NULL) { - return; - } - - ma_node_uninit(pNode, pAllocationCallbacks); - ma_peak2_uninit(&pPeakNode->peak, pAllocationCallbacks); -} - - - -/* -Low Shelf Filter Node -*/ -MA_API ma_loshelf_node_config ma_loshelf_node_config_init(ma_uint32 channels, ma_uint32 sampleRate, double gainDB, double q, double frequency) -{ - ma_loshelf_node_config config; - - config.nodeConfig = ma_node_config_init(); - config.loshelf = ma_loshelf2_config_init(ma_format_f32, channels, sampleRate, gainDB, q, frequency); - - return config; -} - -static void ma_loshelf_node_process_pcm_frames(ma_node* pNode, const float** ppFramesIn, ma_uint32* pFrameCountIn, float** ppFramesOut, ma_uint32* pFrameCountOut) -{ - ma_loshelf_node* pBPFNode = (ma_loshelf_node*)pNode; - - MA_ASSERT(pNode != NULL); - (void)pFrameCountIn; - - ma_loshelf2_process_pcm_frames(&pBPFNode->loshelf, ppFramesOut[0], ppFramesIn[0], *pFrameCountOut); -} - -static ma_node_vtable g_ma_loshelf_node_vtable = -{ - ma_loshelf_node_process_pcm_frames, - NULL, /* onGetRequiredInputFrameCount */ - 1, /* One input. */ - 1, /* One output. */ - 0 /* Default flags. */ -}; - -MA_API ma_result ma_loshelf_node_init(ma_node_graph* pNodeGraph, const ma_loshelf_node_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_loshelf_node* pNode) -{ - ma_result result; - ma_node_config baseNodeConfig; - - if (pNode == NULL) { - return MA_INVALID_ARGS; - } - - MA_ZERO_OBJECT(pNode); - - if (pConfig == NULL) { - return MA_INVALID_ARGS; - } - - if (pConfig->loshelf.format != ma_format_f32) { - return MA_INVALID_ARGS; /* The format must be f32. */ - } - - result = ma_loshelf2_init(&pConfig->loshelf, pAllocationCallbacks, &pNode->loshelf); - if (result != MA_SUCCESS) { - return result; - } - - baseNodeConfig = ma_node_config_init(); - baseNodeConfig.vtable = &g_ma_loshelf_node_vtable; - baseNodeConfig.pInputChannels = &pConfig->loshelf.channels; - baseNodeConfig.pOutputChannels = &pConfig->loshelf.channels; - - result = ma_node_init(pNodeGraph, &baseNodeConfig, pAllocationCallbacks, pNode); - if (result != MA_SUCCESS) { - return result; - } - - return result; -} - -MA_API ma_result ma_loshelf_node_reinit(const ma_loshelf_config* pConfig, ma_loshelf_node* pNode) -{ - ma_loshelf_node* pLoshelfNode = (ma_loshelf_node*)pNode; - - if (pNode == NULL) { - return MA_INVALID_ARGS; - } - - return ma_loshelf2_reinit(pConfig, &pLoshelfNode->loshelf); -} - -MA_API void ma_loshelf_node_uninit(ma_loshelf_node* pNode, const ma_allocation_callbacks* pAllocationCallbacks) -{ - ma_loshelf_node* pLoshelfNode = (ma_loshelf_node*)pNode; - - if (pNode == NULL) { - return; - } - - ma_node_uninit(pNode, pAllocationCallbacks); - ma_loshelf2_uninit(&pLoshelfNode->loshelf, pAllocationCallbacks); -} - - - -/* -High Shelf Filter Node -*/ -MA_API ma_hishelf_node_config ma_hishelf_node_config_init(ma_uint32 channels, ma_uint32 sampleRate, double gainDB, double q, double frequency) -{ - ma_hishelf_node_config config; - - config.nodeConfig = ma_node_config_init(); - config.hishelf = ma_hishelf2_config_init(ma_format_f32, channels, sampleRate, gainDB, q, frequency); - - return config; -} - -static void ma_hishelf_node_process_pcm_frames(ma_node* pNode, const float** ppFramesIn, ma_uint32* pFrameCountIn, float** ppFramesOut, ma_uint32* pFrameCountOut) -{ - ma_hishelf_node* pBPFNode = (ma_hishelf_node*)pNode; - - MA_ASSERT(pNode != NULL); - (void)pFrameCountIn; - - ma_hishelf2_process_pcm_frames(&pBPFNode->hishelf, ppFramesOut[0], ppFramesIn[0], *pFrameCountOut); -} - -static ma_node_vtable g_ma_hishelf_node_vtable = -{ - ma_hishelf_node_process_pcm_frames, - NULL, /* onGetRequiredInputFrameCount */ - 1, /* One input. */ - 1, /* One output. */ - 0 /* Default flags. */ -}; - -MA_API ma_result ma_hishelf_node_init(ma_node_graph* pNodeGraph, const ma_hishelf_node_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_hishelf_node* pNode) -{ - ma_result result; - ma_node_config baseNodeConfig; - - if (pNode == NULL) { - return MA_INVALID_ARGS; - } - - MA_ZERO_OBJECT(pNode); - - if (pConfig == NULL) { - return MA_INVALID_ARGS; - } - - if (pConfig->hishelf.format != ma_format_f32) { - return MA_INVALID_ARGS; /* The format must be f32. */ - } - - result = ma_hishelf2_init(&pConfig->hishelf, pAllocationCallbacks, &pNode->hishelf); - if (result != MA_SUCCESS) { - return result; - } - - baseNodeConfig = ma_node_config_init(); - baseNodeConfig.vtable = &g_ma_hishelf_node_vtable; - baseNodeConfig.pInputChannels = &pConfig->hishelf.channels; - baseNodeConfig.pOutputChannels = &pConfig->hishelf.channels; - - result = ma_node_init(pNodeGraph, &baseNodeConfig, pAllocationCallbacks, pNode); - if (result != MA_SUCCESS) { - return result; - } - - return result; -} - -MA_API ma_result ma_hishelf_node_reinit(const ma_hishelf_config* pConfig, ma_hishelf_node* pNode) -{ - ma_hishelf_node* pHishelfNode = (ma_hishelf_node*)pNode; - - if (pNode == NULL) { - return MA_INVALID_ARGS; - } - - return ma_hishelf2_reinit(pConfig, &pHishelfNode->hishelf); -} - -MA_API void ma_hishelf_node_uninit(ma_hishelf_node* pNode, const ma_allocation_callbacks* pAllocationCallbacks) -{ - ma_hishelf_node* pHishelfNode = (ma_hishelf_node*)pNode; - - if (pNode == NULL) { - return; - } - - ma_node_uninit(pNode, pAllocationCallbacks); - ma_hishelf2_uninit(&pHishelfNode->hishelf, pAllocationCallbacks); -} - - - - -MA_API ma_delay_node_config ma_delay_node_config_init(ma_uint32 channels, ma_uint32 sampleRate, ma_uint32 delayInFrames, float decay) -{ - ma_delay_node_config config; - - config.nodeConfig = ma_node_config_init(); - config.delay = ma_delay_config_init(channels, sampleRate, delayInFrames, decay); - - return config; -} - - -static void ma_delay_node_process_pcm_frames(ma_node* pNode, const float** ppFramesIn, ma_uint32* pFrameCountIn, float** ppFramesOut, ma_uint32* pFrameCountOut) -{ - ma_delay_node* pDelayNode = (ma_delay_node*)pNode; - - (void)pFrameCountIn; - - ma_delay_process_pcm_frames(&pDelayNode->delay, ppFramesOut[0], ppFramesIn[0], *pFrameCountOut); -} - -static ma_node_vtable g_ma_delay_node_vtable = -{ - ma_delay_node_process_pcm_frames, - NULL, - 1, /* 1 input channels. */ - 1, /* 1 output channel. */ - MA_NODE_FLAG_CONTINUOUS_PROCESSING /* Delay requires continuous processing to ensure the tail get's processed. */ -}; - -MA_API ma_result ma_delay_node_init(ma_node_graph* pNodeGraph, const ma_delay_node_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_delay_node* pDelayNode) -{ - ma_result result; - ma_node_config baseConfig; - - if (pDelayNode == NULL) { - return MA_INVALID_ARGS; - } - - MA_ZERO_OBJECT(pDelayNode); - - result = ma_delay_init(&pConfig->delay, pAllocationCallbacks, &pDelayNode->delay); - if (result != MA_SUCCESS) { - return result; - } - - baseConfig = pConfig->nodeConfig; - baseConfig.vtable = &g_ma_delay_node_vtable; - baseConfig.pInputChannels = &pConfig->delay.channels; - baseConfig.pOutputChannels = &pConfig->delay.channels; - - result = ma_node_init(pNodeGraph, &baseConfig, pAllocationCallbacks, &pDelayNode->baseNode); - if (result != MA_SUCCESS) { - ma_delay_uninit(&pDelayNode->delay, pAllocationCallbacks); - return result; - } - - return result; -} - -MA_API void ma_delay_node_uninit(ma_delay_node* pDelayNode, const ma_allocation_callbacks* pAllocationCallbacks) -{ - if (pDelayNode == NULL) { - return; - } - - /* The base node is always uninitialized first. */ - ma_node_uninit(pDelayNode, pAllocationCallbacks); - ma_delay_uninit(&pDelayNode->delay, pAllocationCallbacks); -} - -MA_API void ma_delay_node_set_wet(ma_delay_node* pDelayNode, float value) -{ - if (pDelayNode == NULL) { - return; - } - - ma_delay_set_wet(&pDelayNode->delay, value); -} - -MA_API float ma_delay_node_get_wet(const ma_delay_node* pDelayNode) -{ - if (pDelayNode == NULL) { - return 0; - } - - return ma_delay_get_wet(&pDelayNode->delay); -} - -MA_API void ma_delay_node_set_dry(ma_delay_node* pDelayNode, float value) -{ - if (pDelayNode == NULL) { - return; - } - - ma_delay_set_dry(&pDelayNode->delay, value); -} - -MA_API float ma_delay_node_get_dry(const ma_delay_node* pDelayNode) -{ - if (pDelayNode == NULL) { - return 0; - } - - return ma_delay_get_dry(&pDelayNode->delay); -} - -MA_API void ma_delay_node_set_decay(ma_delay_node* pDelayNode, float value) -{ - if (pDelayNode == NULL) { - return; - } - - ma_delay_set_decay(&pDelayNode->delay, value); -} - -MA_API float ma_delay_node_get_decay(const ma_delay_node* pDelayNode) -{ - if (pDelayNode == NULL) { - return 0; - } - - return ma_delay_get_decay(&pDelayNode->delay); -} -#endif /* MA_NO_NODE_GRAPH */ - - -/* SECTION: miniaudio_engine.c */ -#if !defined(MA_NO_ENGINE) && !defined(MA_NO_NODE_GRAPH) -/************************************************************************************************************************************************************** - -Engine - -**************************************************************************************************************************************************************/ -#define MA_SEEK_TARGET_NONE (~(ma_uint64)0) - - -static void ma_sound_set_at_end(ma_sound* pSound, ma_bool32 atEnd) -{ - MA_ASSERT(pSound != NULL); - ma_atomic_exchange_32(&pSound->atEnd, atEnd); - - /* Fire any callbacks or events. */ - if (atEnd) { - if (pSound->endCallback != NULL) { - pSound->endCallback(pSound->pEndCallbackUserData, pSound); - } - } -} - -static ma_bool32 ma_sound_get_at_end(const ma_sound* pSound) -{ - MA_ASSERT(pSound != NULL); - return ma_atomic_load_32(&pSound->atEnd); -} - - -MA_API ma_engine_node_config ma_engine_node_config_init(ma_engine* pEngine, ma_engine_node_type type, ma_uint32 flags) -{ - ma_engine_node_config config; - - MA_ZERO_OBJECT(&config); - config.pEngine = pEngine; - config.type = type; - config.isPitchDisabled = (flags & MA_SOUND_FLAG_NO_PITCH) != 0; - config.isSpatializationDisabled = (flags & MA_SOUND_FLAG_NO_SPATIALIZATION) != 0; - config.monoExpansionMode = pEngine->monoExpansionMode; - - return config; -} - - -static void ma_engine_node_update_pitch_if_required(ma_engine_node* pEngineNode) -{ - ma_bool32 isUpdateRequired = MA_FALSE; - float newPitch; - - MA_ASSERT(pEngineNode != NULL); - - newPitch = ma_atomic_load_explicit_f32(&pEngineNode->pitch, ma_atomic_memory_order_acquire); - - if (pEngineNode->oldPitch != newPitch) { - pEngineNode->oldPitch = newPitch; - isUpdateRequired = MA_TRUE; - } - - if (pEngineNode->oldDopplerPitch != pEngineNode->spatializer.dopplerPitch) { - pEngineNode->oldDopplerPitch = pEngineNode->spatializer.dopplerPitch; - isUpdateRequired = MA_TRUE; - } - - if (isUpdateRequired) { - float basePitch = (float)pEngineNode->sampleRate / ma_engine_get_sample_rate(pEngineNode->pEngine); - ma_linear_resampler_set_rate_ratio(&pEngineNode->resampler, basePitch * pEngineNode->oldPitch * pEngineNode->oldDopplerPitch); - } -} - -static ma_bool32 ma_engine_node_is_pitching_enabled(const ma_engine_node* pEngineNode) -{ - MA_ASSERT(pEngineNode != NULL); - - /* Don't try to be clever by skipping resampling in the pitch=1 case or else you'll glitch when moving away from 1. */ - return !ma_atomic_load_explicit_32(&pEngineNode->isPitchDisabled, ma_atomic_memory_order_acquire); -} - -static ma_bool32 ma_engine_node_is_spatialization_enabled(const ma_engine_node* pEngineNode) -{ - MA_ASSERT(pEngineNode != NULL); - - return !ma_atomic_load_explicit_32(&pEngineNode->isSpatializationDisabled, ma_atomic_memory_order_acquire); -} - -static ma_uint64 ma_engine_node_get_required_input_frame_count(const ma_engine_node* pEngineNode, ma_uint64 outputFrameCount) -{ - ma_uint64 inputFrameCount = 0; - - if (ma_engine_node_is_pitching_enabled(pEngineNode)) { - ma_result result = ma_linear_resampler_get_required_input_frame_count(&pEngineNode->resampler, outputFrameCount, &inputFrameCount); - if (result != MA_SUCCESS) { - inputFrameCount = 0; - } - } else { - inputFrameCount = outputFrameCount; /* No resampling, so 1:1. */ - } - - return inputFrameCount; -} - -static ma_result ma_engine_node_set_volume(ma_engine_node* pEngineNode, float volume) -{ - if (pEngineNode == NULL) { - return MA_INVALID_ARGS; - } - - ma_atomic_float_set(&pEngineNode->volume, volume); - - /* If we're not smoothing we should bypass the volume gainer entirely. */ - if (pEngineNode->volumeSmoothTimeInPCMFrames == 0) { - /* We should always have an active spatializer because it can be enabled and disabled dynamically. We can just use that for holding our volume. */ - ma_spatializer_set_master_volume(&pEngineNode->spatializer, volume); - } else { - /* We're using volume smoothing, so apply the master volume to the gainer. */ - ma_gainer_set_gain(&pEngineNode->volumeGainer, volume); - } - - return MA_SUCCESS; -} - -static ma_result ma_engine_node_get_volume(const ma_engine_node* pEngineNode, float* pVolume) -{ - if (pVolume == NULL) { - return MA_INVALID_ARGS; - } - - *pVolume = 0.0f; - - if (pEngineNode == NULL) { - return MA_INVALID_ARGS; - } - - *pVolume = ma_atomic_float_get((ma_atomic_float*)&pEngineNode->volume); - - return MA_SUCCESS; -} - - -static void ma_engine_node_process_pcm_frames__general(ma_engine_node* pEngineNode, const float** ppFramesIn, ma_uint32* pFrameCountIn, float** ppFramesOut, ma_uint32* pFrameCountOut) -{ - ma_uint32 frameCountIn; - ma_uint32 frameCountOut; - ma_uint32 totalFramesProcessedIn; - ma_uint32 totalFramesProcessedOut; - ma_uint32 channelsIn; - ma_uint32 channelsOut; - ma_bool32 isPitchingEnabled; - ma_bool32 isFadingEnabled; - ma_bool32 isSpatializationEnabled; - ma_bool32 isPanningEnabled; - ma_bool32 isVolumeSmoothingEnabled; - - frameCountIn = *pFrameCountIn; - frameCountOut = *pFrameCountOut; - - channelsIn = ma_spatializer_get_input_channels(&pEngineNode->spatializer); - channelsOut = ma_spatializer_get_output_channels(&pEngineNode->spatializer); - - totalFramesProcessedIn = 0; - totalFramesProcessedOut = 0; - - /* Update the fader if applicable. */ - { - ma_uint64 fadeLengthInFrames = ma_atomic_uint64_get(&pEngineNode->fadeSettings.fadeLengthInFrames); - if (fadeLengthInFrames != ~(ma_uint64)0) { - float fadeVolumeBeg = ma_atomic_float_get(&pEngineNode->fadeSettings.volumeBeg); - float fadeVolumeEnd = ma_atomic_float_get(&pEngineNode->fadeSettings.volumeEnd); - ma_int64 fadeStartOffsetInFrames = (ma_int64)ma_atomic_uint64_get(&pEngineNode->fadeSettings.absoluteGlobalTimeInFrames); - if (fadeStartOffsetInFrames == (ma_int64)(~(ma_uint64)0)) { - fadeStartOffsetInFrames = 0; - } else { - fadeStartOffsetInFrames -= ma_engine_get_time_in_pcm_frames(pEngineNode->pEngine); - } - - ma_fader_set_fade_ex(&pEngineNode->fader, fadeVolumeBeg, fadeVolumeEnd, fadeLengthInFrames, fadeStartOffsetInFrames); - - /* Reset the fade length so we don't erroneously apply it again. */ - ma_atomic_uint64_set(&pEngineNode->fadeSettings.fadeLengthInFrames, ~(ma_uint64)0); - } - } - - isPitchingEnabled = ma_engine_node_is_pitching_enabled(pEngineNode); - isFadingEnabled = pEngineNode->fader.volumeBeg != 1 || pEngineNode->fader.volumeEnd != 1; - isSpatializationEnabled = ma_engine_node_is_spatialization_enabled(pEngineNode); - isPanningEnabled = pEngineNode->panner.pan != 0 && channelsOut != 1; - isVolumeSmoothingEnabled = pEngineNode->volumeSmoothTimeInPCMFrames > 0; - - /* Keep going while we've still got data available for processing. */ - while (totalFramesProcessedOut < frameCountOut) { - /* - We need to process in a specific order. We always do resampling first because it's likely - we're going to be increasing the channel count after spatialization. Also, I want to do - fading based on the output sample rate. - - We'll first read into a buffer from the resampler. Then we'll do all processing that - operates on the on the input channel count. We'll then get the spatializer to output to - the output buffer and then do all effects from that point directly in the output buffer - in-place. - - Note that we're always running the resampler if pitching is enabled, even when the pitch - is 1. If we try to be clever and skip resampling when the pitch is 1, we'll get a glitch - when we move away from 1, back to 1, and then away from 1 again. We'll want to implement - any pitch=1 optimizations in the resampler itself. - - There's a small optimization here that we'll utilize since it might be a fairly common - case. When the input and output channel counts are the same, we'll read straight into the - output buffer from the resampler and do everything in-place. - */ - const float* pRunningFramesIn; - float* pRunningFramesOut; - float* pWorkingBuffer; /* This is the buffer that we'll be processing frames in. This is in input channels. */ - float temp[MA_DATA_CONVERTER_STACK_BUFFER_SIZE / sizeof(float)]; - ma_uint32 tempCapInFrames = ma_countof(temp) / channelsIn; - ma_uint32 framesAvailableIn; - ma_uint32 framesAvailableOut; - ma_uint32 framesJustProcessedIn; - ma_uint32 framesJustProcessedOut; - ma_bool32 isWorkingBufferValid = MA_FALSE; - - framesAvailableIn = frameCountIn - totalFramesProcessedIn; - framesAvailableOut = frameCountOut - totalFramesProcessedOut; - - pRunningFramesIn = ma_offset_pcm_frames_const_ptr_f32(ppFramesIn[0], totalFramesProcessedIn, channelsIn); - pRunningFramesOut = ma_offset_pcm_frames_ptr_f32(ppFramesOut[0], totalFramesProcessedOut, channelsOut); - - if (channelsIn == channelsOut) { - /* Fast path. Channel counts are the same. No need for an intermediary input buffer. */ - pWorkingBuffer = pRunningFramesOut; - } else { - /* Slow path. Channel counts are different. Need to use an intermediary input buffer. */ - pWorkingBuffer = temp; - if (framesAvailableOut > tempCapInFrames) { - framesAvailableOut = tempCapInFrames; - } - } - - /* First is resampler. */ - if (isPitchingEnabled) { - ma_uint64 resampleFrameCountIn = framesAvailableIn; - ma_uint64 resampleFrameCountOut = framesAvailableOut; - - ma_linear_resampler_process_pcm_frames(&pEngineNode->resampler, pRunningFramesIn, &resampleFrameCountIn, pWorkingBuffer, &resampleFrameCountOut); - isWorkingBufferValid = MA_TRUE; - - framesJustProcessedIn = (ma_uint32)resampleFrameCountIn; - framesJustProcessedOut = (ma_uint32)resampleFrameCountOut; - } else { - framesJustProcessedIn = ma_min(framesAvailableIn, framesAvailableOut); - framesJustProcessedOut = framesJustProcessedIn; /* When no resampling is being performed, the number of output frames is the same as input frames. */ - } - - /* Fading. */ - if (isFadingEnabled) { - if (isWorkingBufferValid) { - ma_fader_process_pcm_frames(&pEngineNode->fader, pWorkingBuffer, pWorkingBuffer, framesJustProcessedOut); /* In-place processing. */ - } else { - ma_fader_process_pcm_frames(&pEngineNode->fader, pWorkingBuffer, pRunningFramesIn, framesJustProcessedOut); - isWorkingBufferValid = MA_TRUE; - } - } - - /* - If we're using smoothing, we won't be applying volume via the spatializer, but instead from a ma_gainer. In this case - we'll want to apply our volume now. - */ - if (isVolumeSmoothingEnabled) { - if (isWorkingBufferValid) { - ma_gainer_process_pcm_frames(&pEngineNode->volumeGainer, pWorkingBuffer, pWorkingBuffer, framesJustProcessedOut); - } else { - ma_gainer_process_pcm_frames(&pEngineNode->volumeGainer, pWorkingBuffer, pRunningFramesIn, framesJustProcessedOut); - isWorkingBufferValid = MA_TRUE; - } - } - - /* - If at this point we still haven't actually done anything with the working buffer we need - to just read straight from the input buffer. - */ - if (isWorkingBufferValid == MA_FALSE) { - pWorkingBuffer = (float*)pRunningFramesIn; /* Naughty const cast, but it's safe at this point because we won't ever be writing to it from this point out. */ - } - - /* Spatialization. */ - if (isSpatializationEnabled) { - ma_uint32 iListener; - - /* - When determining the listener to use, we first check to see if the sound is pinned to a - specific listener. If so, we use that. Otherwise we just use the closest listener. - */ - if (pEngineNode->pinnedListenerIndex != MA_LISTENER_INDEX_CLOSEST && pEngineNode->pinnedListenerIndex < ma_engine_get_listener_count(pEngineNode->pEngine)) { - iListener = pEngineNode->pinnedListenerIndex; - } else { - ma_vec3f spatializerPosition = ma_spatializer_get_position(&pEngineNode->spatializer); - iListener = ma_engine_find_closest_listener(pEngineNode->pEngine, spatializerPosition.x, spatializerPosition.y, spatializerPosition.z); - } - - ma_spatializer_process_pcm_frames(&pEngineNode->spatializer, &pEngineNode->pEngine->listeners[iListener], pRunningFramesOut, pWorkingBuffer, framesJustProcessedOut); - } else { - /* No spatialization, but we still need to do channel conversion and master volume. */ - float volume; - ma_engine_node_get_volume(pEngineNode, &volume); /* Should never fail. */ - - if (channelsIn == channelsOut) { - /* No channel conversion required. Just copy straight to the output buffer. */ - if (isVolumeSmoothingEnabled) { - /* Volume has already been applied. Just copy straight to the output buffer. */ - ma_copy_pcm_frames(pRunningFramesOut, pWorkingBuffer, framesJustProcessedOut * channelsOut, ma_format_f32, channelsOut); - } else { - /* Volume has not been applied yet. Copy and apply volume in the same pass. */ - ma_copy_and_apply_volume_factor_f32(pRunningFramesOut, pWorkingBuffer, framesJustProcessedOut * channelsOut, volume); - } - } else { - /* Channel conversion required. TODO: Add support for channel maps here. */ - ma_channel_map_apply_f32(pRunningFramesOut, NULL, channelsOut, pWorkingBuffer, NULL, channelsIn, framesJustProcessedOut, ma_channel_mix_mode_simple, pEngineNode->monoExpansionMode); - - /* If we're using smoothing, the volume will have already been applied. */ - if (!isVolumeSmoothingEnabled) { - ma_apply_volume_factor_f32(pRunningFramesOut, framesJustProcessedOut * channelsOut, volume); - } - } - } - - /* At this point we can guarantee that the output buffer contains valid data. We can process everything in place now. */ - - /* Panning. */ - if (isPanningEnabled) { - ma_panner_process_pcm_frames(&pEngineNode->panner, pRunningFramesOut, pRunningFramesOut, framesJustProcessedOut); /* In-place processing. */ - } - - /* We're done for this chunk. */ - totalFramesProcessedIn += framesJustProcessedIn; - totalFramesProcessedOut += framesJustProcessedOut; - - /* If we didn't process any output frames this iteration it means we've either run out of input data, or run out of room in the output buffer. */ - if (framesJustProcessedOut == 0) { - break; - } - } - - /* At this point we're done processing. */ - *pFrameCountIn = totalFramesProcessedIn; - *pFrameCountOut = totalFramesProcessedOut; -} - -static void ma_engine_node_process_pcm_frames__sound(ma_node* pNode, const float** ppFramesIn, ma_uint32* pFrameCountIn, float** ppFramesOut, ma_uint32* pFrameCountOut) -{ - /* For sounds, we need to first read from the data source. Then we need to apply the engine effects (pan, pitch, fades, etc.). */ - ma_result result = MA_SUCCESS; - ma_sound* pSound = (ma_sound*)pNode; - ma_uint32 frameCount = *pFrameCountOut; - ma_uint32 totalFramesRead = 0; - ma_format dataSourceFormat; - ma_uint32 dataSourceChannels; - ma_uint8 temp[MA_DATA_CONVERTER_STACK_BUFFER_SIZE]; - ma_uint32 tempCapInFrames; - ma_uint64 seekTarget; - - /* This is a data source node which means no input buses. */ - (void)ppFramesIn; - (void)pFrameCountIn; - - /* If we're marked at the end we need to stop the sound and do nothing. */ - if (ma_sound_at_end(pSound)) { - ma_sound_stop(pSound); - *pFrameCountOut = 0; - return; - } - - /* If we're seeking, do so now before reading. */ - seekTarget = ma_atomic_load_64(&pSound->seekTarget); - if (seekTarget != MA_SEEK_TARGET_NONE) { - ma_data_source_seek_to_pcm_frame(pSound->pDataSource, seekTarget); - - /* Any time-dependant effects need to have their times updated. */ - ma_node_set_time(pSound, seekTarget); - - ma_atomic_exchange_64(&pSound->seekTarget, MA_SEEK_TARGET_NONE); - } - - /* - We want to update the pitch once. For sounds, this can be either at the start or at the end. If - we don't force this to only ever be updating once, we could end up in a situation where - retrieving the required input frame count ends up being different to what we actually retrieve. - What could happen is that the required input frame count is calculated, the pitch is update, - and then this processing function is called resulting in a different number of input frames - being processed. Do not call this in ma_engine_node_process_pcm_frames__general() or else - you'll hit the aforementioned bug. - */ - ma_engine_node_update_pitch_if_required(&pSound->engineNode); - - /* - For the convenience of the caller, we're doing to allow data sources to use non-floating-point formats and channel counts that differ - from the main engine. - */ - result = ma_data_source_get_data_format(pSound->pDataSource, &dataSourceFormat, &dataSourceChannels, NULL, NULL, 0); - if (result == MA_SUCCESS) { - tempCapInFrames = sizeof(temp) / ma_get_bytes_per_frame(dataSourceFormat, dataSourceChannels); - - /* Keep reading until we've read as much as was requested or we reach the end of the data source. */ - while (totalFramesRead < frameCount) { - ma_uint32 framesRemaining = frameCount - totalFramesRead; - ma_uint32 framesToRead; - ma_uint64 framesJustRead; - ma_uint32 frameCountIn; - ma_uint32 frameCountOut; - const float* pRunningFramesIn; - float* pRunningFramesOut; - - /* - The first thing we need to do is read into the temporary buffer. We can calculate exactly - how many input frames we'll need after resampling. - */ - framesToRead = (ma_uint32)ma_engine_node_get_required_input_frame_count(&pSound->engineNode, framesRemaining); - if (framesToRead > tempCapInFrames) { - framesToRead = tempCapInFrames; - } - - result = ma_data_source_read_pcm_frames(pSound->pDataSource, temp, framesToRead, &framesJustRead); - - /* If we reached the end of the sound we'll want to mark it as at the end and stop it. This should never be returned for looping sounds. */ - if (result == MA_AT_END) { - ma_sound_set_at_end(pSound, MA_TRUE); /* This will be set to false in ma_sound_start(). */ - } - - pRunningFramesOut = ma_offset_pcm_frames_ptr_f32(ppFramesOut[0], totalFramesRead, ma_node_get_output_channels(pNode, 0)); - - frameCountIn = (ma_uint32)framesJustRead; - frameCountOut = framesRemaining; - - /* Convert if necessary. */ - if (dataSourceFormat == ma_format_f32) { - /* Fast path. No data conversion necessary. */ - pRunningFramesIn = (float*)temp; - ma_engine_node_process_pcm_frames__general(&pSound->engineNode, &pRunningFramesIn, &frameCountIn, &pRunningFramesOut, &frameCountOut); - } else { - /* Slow path. Need to do sample format conversion to f32. If we give the f32 buffer the same count as the first temp buffer, we're guaranteed it'll be large enough. */ - float tempf32[MA_DATA_CONVERTER_STACK_BUFFER_SIZE]; /* Do not do `MA_DATA_CONVERTER_STACK_BUFFER_SIZE/sizeof(float)` here like we've done in other places. */ - ma_convert_pcm_frames_format(tempf32, ma_format_f32, temp, dataSourceFormat, framesJustRead, dataSourceChannels, ma_dither_mode_none); - - /* Now that we have our samples in f32 format we can process like normal. */ - pRunningFramesIn = tempf32; - ma_engine_node_process_pcm_frames__general(&pSound->engineNode, &pRunningFramesIn, &frameCountIn, &pRunningFramesOut, &frameCountOut); - } - - /* We should have processed all of our input frames since we calculated the required number of input frames at the top. */ - MA_ASSERT(frameCountIn == framesJustRead); - totalFramesRead += (ma_uint32)frameCountOut; /* Safe cast. */ - - if (result != MA_SUCCESS || ma_sound_at_end(pSound)) { - break; /* Might have reached the end. */ - } - } - } - - *pFrameCountOut = totalFramesRead; -} - -static void ma_engine_node_process_pcm_frames__group(ma_node* pNode, const float** ppFramesIn, ma_uint32* pFrameCountIn, float** ppFramesOut, ma_uint32* pFrameCountOut) -{ - /* - Make sure the pitch is updated before trying to read anything. It's important that this is done - only once and not in ma_engine_node_process_pcm_frames__general(). The reason for this is that - ma_engine_node_process_pcm_frames__general() will call ma_engine_node_get_required_input_frame_count(), - and if another thread modifies the pitch just after that call it can result in a glitch due to - the input rate changing. - */ - ma_engine_node_update_pitch_if_required((ma_engine_node*)pNode); - - /* For groups, the input data has already been read and we just need to apply the effect. */ - ma_engine_node_process_pcm_frames__general((ma_engine_node*)pNode, ppFramesIn, pFrameCountIn, ppFramesOut, pFrameCountOut); -} - -static ma_result ma_engine_node_get_required_input_frame_count__group(ma_node* pNode, ma_uint32 outputFrameCount, ma_uint32* pInputFrameCount) -{ - ma_uint64 inputFrameCount; - - MA_ASSERT(pInputFrameCount != NULL); - - /* Our pitch will affect this calculation. We need to update it. */ - ma_engine_node_update_pitch_if_required((ma_engine_node*)pNode); - - inputFrameCount = ma_engine_node_get_required_input_frame_count((ma_engine_node*)pNode, outputFrameCount); - if (inputFrameCount > 0xFFFFFFFF) { - inputFrameCount = 0xFFFFFFFF; /* Will never happen because miniaudio will only ever process in relatively small chunks. */ - } - - *pInputFrameCount = (ma_uint32)inputFrameCount; - - return MA_SUCCESS; -} - - -static ma_node_vtable g_ma_engine_node_vtable__sound = -{ - ma_engine_node_process_pcm_frames__sound, - NULL, /* onGetRequiredInputFrameCount */ - 0, /* Sounds are data source nodes which means they have zero inputs (their input is drawn from the data source itself). */ - 1, /* Sounds have one output bus. */ - 0 /* Default flags. */ -}; - -static ma_node_vtable g_ma_engine_node_vtable__group = -{ - ma_engine_node_process_pcm_frames__group, - ma_engine_node_get_required_input_frame_count__group, - 1, /* Groups have one input bus. */ - 1, /* Groups have one output bus. */ - MA_NODE_FLAG_DIFFERENT_PROCESSING_RATES /* The engine node does resampling so should let miniaudio know about it. */ -}; - - - -static ma_node_config ma_engine_node_base_node_config_init(const ma_engine_node_config* pConfig) -{ - ma_node_config baseNodeConfig; - - if (pConfig->type == ma_engine_node_type_sound) { - /* Sound. */ - baseNodeConfig = ma_node_config_init(); - baseNodeConfig.vtable = &g_ma_engine_node_vtable__sound; - baseNodeConfig.initialState = ma_node_state_stopped; /* Sounds are stopped by default. */ - } else { - /* Group. */ - baseNodeConfig = ma_node_config_init(); - baseNodeConfig.vtable = &g_ma_engine_node_vtable__group; - baseNodeConfig.initialState = ma_node_state_started; /* Groups are started by default. */ - } - - return baseNodeConfig; -} - -static ma_spatializer_config ma_engine_node_spatializer_config_init(const ma_node_config* pBaseNodeConfig) -{ - return ma_spatializer_config_init(pBaseNodeConfig->pInputChannels[0], pBaseNodeConfig->pOutputChannels[0]); -} - -typedef struct -{ - size_t sizeInBytes; - size_t baseNodeOffset; - size_t resamplerOffset; - size_t spatializerOffset; - size_t gainerOffset; -} ma_engine_node_heap_layout; - -static ma_result ma_engine_node_get_heap_layout(const ma_engine_node_config* pConfig, ma_engine_node_heap_layout* pHeapLayout) -{ - ma_result result; - size_t tempHeapSize; - ma_node_config baseNodeConfig; - ma_linear_resampler_config resamplerConfig; - ma_spatializer_config spatializerConfig; - ma_gainer_config gainerConfig; - ma_uint32 channelsIn; - ma_uint32 channelsOut; - ma_channel defaultStereoChannelMap[2] = {MA_CHANNEL_SIDE_LEFT, MA_CHANNEL_SIDE_RIGHT}; /* <-- Consistent with the default channel map of a stereo listener. Means channel conversion can run on a fast path. */ - - MA_ASSERT(pHeapLayout); - - MA_ZERO_OBJECT(pHeapLayout); - - if (pConfig == NULL) { - return MA_INVALID_ARGS; - } - - if (pConfig->pEngine == NULL) { - return MA_INVALID_ARGS; /* An engine must be specified. */ - } - - pHeapLayout->sizeInBytes = 0; - - channelsIn = (pConfig->channelsIn != 0) ? pConfig->channelsIn : ma_engine_get_channels(pConfig->pEngine); - channelsOut = (pConfig->channelsOut != 0) ? pConfig->channelsOut : ma_engine_get_channels(pConfig->pEngine); - - - /* Base node. */ - baseNodeConfig = ma_engine_node_base_node_config_init(pConfig); - baseNodeConfig.pInputChannels = &channelsIn; - baseNodeConfig.pOutputChannels = &channelsOut; - - result = ma_node_get_heap_size(ma_engine_get_node_graph(pConfig->pEngine), &baseNodeConfig, &tempHeapSize); - if (result != MA_SUCCESS) { - return result; /* Failed to retrieve the size of the heap for the base node. */ - } - - pHeapLayout->baseNodeOffset = pHeapLayout->sizeInBytes; - pHeapLayout->sizeInBytes += ma_align_64(tempHeapSize); - - - /* Resmapler. */ - resamplerConfig = ma_linear_resampler_config_init(ma_format_f32, channelsIn, 1, 1); /* Input and output sample rates don't affect the calculation of the heap size. */ - resamplerConfig.lpfOrder = 0; - - result = ma_linear_resampler_get_heap_size(&resamplerConfig, &tempHeapSize); - if (result != MA_SUCCESS) { - return result; /* Failed to retrieve the size of the heap for the resampler. */ - } - - pHeapLayout->resamplerOffset = pHeapLayout->sizeInBytes; - pHeapLayout->sizeInBytes += ma_align_64(tempHeapSize); - - - /* Spatializer. */ - spatializerConfig = ma_engine_node_spatializer_config_init(&baseNodeConfig); - - if (spatializerConfig.channelsIn == 2) { - spatializerConfig.pChannelMapIn = defaultStereoChannelMap; - } - - result = ma_spatializer_get_heap_size(&spatializerConfig, &tempHeapSize); - if (result != MA_SUCCESS) { - return result; /* Failed to retrieve the size of the heap for the spatializer. */ - } - - pHeapLayout->spatializerOffset = pHeapLayout->sizeInBytes; - pHeapLayout->sizeInBytes += ma_align_64(tempHeapSize); - - - /* Gainer. Will not be used if we are not using smoothing. */ - if (pConfig->volumeSmoothTimeInPCMFrames > 0) { - gainerConfig = ma_gainer_config_init(channelsIn, pConfig->volumeSmoothTimeInPCMFrames); - - result = ma_gainer_get_heap_size(&gainerConfig, &tempHeapSize); - if (result != MA_SUCCESS) { - return result; - } - - pHeapLayout->gainerOffset = pHeapLayout->sizeInBytes; - pHeapLayout->sizeInBytes += ma_align_64(tempHeapSize); - } - - - return MA_SUCCESS; -} - -MA_API ma_result ma_engine_node_get_heap_size(const ma_engine_node_config* pConfig, size_t* pHeapSizeInBytes) -{ - ma_result result; - ma_engine_node_heap_layout heapLayout; - - if (pHeapSizeInBytes == NULL) { - return MA_INVALID_ARGS; - } - - *pHeapSizeInBytes = 0; - - result = ma_engine_node_get_heap_layout(pConfig, &heapLayout); - if (result != MA_SUCCESS) { - return result; - } - - *pHeapSizeInBytes = heapLayout.sizeInBytes; - - return MA_SUCCESS; -} - -MA_API ma_result ma_engine_node_init_preallocated(const ma_engine_node_config* pConfig, void* pHeap, ma_engine_node* pEngineNode) -{ - ma_result result; - ma_engine_node_heap_layout heapLayout; - ma_node_config baseNodeConfig; - ma_linear_resampler_config resamplerConfig; - ma_fader_config faderConfig; - ma_spatializer_config spatializerConfig; - ma_panner_config pannerConfig; - ma_gainer_config gainerConfig; - ma_uint32 channelsIn; - ma_uint32 channelsOut; - ma_channel defaultStereoChannelMap[2] = {MA_CHANNEL_SIDE_LEFT, MA_CHANNEL_SIDE_RIGHT}; /* <-- Consistent with the default channel map of a stereo listener. Means channel conversion can run on a fast path. */ - - if (pEngineNode == NULL) { - return MA_INVALID_ARGS; - } - - MA_ZERO_OBJECT(pEngineNode); - - result = ma_engine_node_get_heap_layout(pConfig, &heapLayout); - if (result != MA_SUCCESS) { - return result; - } - - if (pConfig->pinnedListenerIndex != MA_LISTENER_INDEX_CLOSEST && pConfig->pinnedListenerIndex >= ma_engine_get_listener_count(pConfig->pEngine)) { - return MA_INVALID_ARGS; /* Invalid listener. */ - } - - pEngineNode->_pHeap = pHeap; - MA_ZERO_MEMORY(pHeap, heapLayout.sizeInBytes); - - pEngineNode->pEngine = pConfig->pEngine; - pEngineNode->sampleRate = (pConfig->sampleRate > 0) ? pConfig->sampleRate : ma_engine_get_sample_rate(pEngineNode->pEngine); - pEngineNode->volumeSmoothTimeInPCMFrames = pConfig->volumeSmoothTimeInPCMFrames; - pEngineNode->monoExpansionMode = pConfig->monoExpansionMode; - ma_atomic_float_set(&pEngineNode->volume, 1); - pEngineNode->pitch = 1; - pEngineNode->oldPitch = 1; - pEngineNode->oldDopplerPitch = 1; - pEngineNode->isPitchDisabled = pConfig->isPitchDisabled; - pEngineNode->isSpatializationDisabled = pConfig->isSpatializationDisabled; - pEngineNode->pinnedListenerIndex = pConfig->pinnedListenerIndex; - ma_atomic_float_set(&pEngineNode->fadeSettings.volumeBeg, 1); - ma_atomic_float_set(&pEngineNode->fadeSettings.volumeEnd, 1); - ma_atomic_uint64_set(&pEngineNode->fadeSettings.fadeLengthInFrames, (~(ma_uint64)0)); - ma_atomic_uint64_set(&pEngineNode->fadeSettings.absoluteGlobalTimeInFrames, (~(ma_uint64)0)); /* <-- Indicates that the fade should start immediately. */ - - channelsIn = (pConfig->channelsIn != 0) ? pConfig->channelsIn : ma_engine_get_channels(pConfig->pEngine); - channelsOut = (pConfig->channelsOut != 0) ? pConfig->channelsOut : ma_engine_get_channels(pConfig->pEngine); - - /* - If the sample rate of the sound is different to the engine, make sure pitching is enabled so that the resampler - is activated. Not doing this will result in the sound not being resampled if MA_SOUND_FLAG_NO_PITCH is used. - */ - if (pEngineNode->sampleRate != ma_engine_get_sample_rate(pEngineNode->pEngine)) { - pEngineNode->isPitchDisabled = MA_FALSE; - } - - - /* Base node. */ - baseNodeConfig = ma_engine_node_base_node_config_init(pConfig); - baseNodeConfig.pInputChannels = &channelsIn; - baseNodeConfig.pOutputChannels = &channelsOut; - - result = ma_node_init_preallocated(&pConfig->pEngine->nodeGraph, &baseNodeConfig, ma_offset_ptr(pHeap, heapLayout.baseNodeOffset), &pEngineNode->baseNode); - if (result != MA_SUCCESS) { - goto error0; - } - - - /* - We can now initialize the effects we need in order to implement the engine node. There's a - defined order of operations here, mainly centered around when we convert our channels from the - data source's native channel count to the engine's channel count. As a rule, we want to do as - much computation as possible before spatialization because there's a chance that will increase - the channel count, thereby increasing the amount of work needing to be done to process. - */ - - /* We'll always do resampling first. */ - resamplerConfig = ma_linear_resampler_config_init(ma_format_f32, baseNodeConfig.pInputChannels[0], pEngineNode->sampleRate, ma_engine_get_sample_rate(pEngineNode->pEngine)); - resamplerConfig.lpfOrder = 0; /* <-- Need to disable low-pass filtering for pitch shifting for now because there's cases where the biquads are becoming unstable. Need to figure out a better fix for this. */ - - result = ma_linear_resampler_init_preallocated(&resamplerConfig, ma_offset_ptr(pHeap, heapLayout.resamplerOffset), &pEngineNode->resampler); - if (result != MA_SUCCESS) { - goto error1; - } - - - /* After resampling will come the fader. */ - faderConfig = ma_fader_config_init(ma_format_f32, baseNodeConfig.pInputChannels[0], ma_engine_get_sample_rate(pEngineNode->pEngine)); - - result = ma_fader_init(&faderConfig, &pEngineNode->fader); - if (result != MA_SUCCESS) { - goto error2; - } - - - /* - Spatialization comes next. We spatialize based on the node's output channel count. It's up the caller to - ensure channels counts link up correctly in the node graph. - */ - spatializerConfig = ma_engine_node_spatializer_config_init(&baseNodeConfig); - spatializerConfig.gainSmoothTimeInFrames = pEngineNode->pEngine->gainSmoothTimeInFrames; - - if (spatializerConfig.channelsIn == 2) { - spatializerConfig.pChannelMapIn = defaultStereoChannelMap; - } - - result = ma_spatializer_init_preallocated(&spatializerConfig, ma_offset_ptr(pHeap, heapLayout.spatializerOffset), &pEngineNode->spatializer); - if (result != MA_SUCCESS) { - goto error2; - } - - - /* - After spatialization comes panning. We need to do this after spatialization because otherwise we wouldn't - be able to pan mono sounds. - */ - pannerConfig = ma_panner_config_init(ma_format_f32, baseNodeConfig.pOutputChannels[0]); - - result = ma_panner_init(&pannerConfig, &pEngineNode->panner); - if (result != MA_SUCCESS) { - goto error3; - } - - - /* We'll need a gainer for smoothing out volume changes if we have a non-zero smooth time. We apply this before converting to the output channel count. */ - if (pConfig->volumeSmoothTimeInPCMFrames > 0) { - gainerConfig = ma_gainer_config_init(channelsIn, pConfig->volumeSmoothTimeInPCMFrames); - - result = ma_gainer_init_preallocated(&gainerConfig, ma_offset_ptr(pHeap, heapLayout.gainerOffset), &pEngineNode->volumeGainer); - if (result != MA_SUCCESS) { - goto error3; - } - } - - - return MA_SUCCESS; - - /* No need for allocation callbacks here because we use a preallocated heap. */ -error3: ma_spatializer_uninit(&pEngineNode->spatializer, NULL); -error2: ma_linear_resampler_uninit(&pEngineNode->resampler, NULL); -error1: ma_node_uninit(&pEngineNode->baseNode, NULL); -error0: return result; -} - -MA_API ma_result ma_engine_node_init(const ma_engine_node_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_engine_node* pEngineNode) -{ - ma_result result; - size_t heapSizeInBytes; - void* pHeap; - - result = ma_engine_node_get_heap_size(pConfig, &heapSizeInBytes); - if (result != MA_SUCCESS) { - return result; - } - - if (heapSizeInBytes > 0) { - pHeap = ma_malloc(heapSizeInBytes, pAllocationCallbacks); - if (pHeap == NULL) { - return MA_OUT_OF_MEMORY; - } - } else { - pHeap = NULL; - } - - result = ma_engine_node_init_preallocated(pConfig, pHeap, pEngineNode); - if (result != MA_SUCCESS) { - ma_free(pHeap, pAllocationCallbacks); - return result; - } - - pEngineNode->_ownsHeap = MA_TRUE; - return MA_SUCCESS; -} - -MA_API void ma_engine_node_uninit(ma_engine_node* pEngineNode, const ma_allocation_callbacks* pAllocationCallbacks) -{ - /* - The base node always needs to be uninitialized first to ensure it's detached from the graph completely before we - destroy anything that might be in the middle of being used by the processing function. - */ - ma_node_uninit(&pEngineNode->baseNode, pAllocationCallbacks); - - /* Now that the node has been uninitialized we can safely uninitialize the rest. */ - if (pEngineNode->volumeSmoothTimeInPCMFrames > 0) { - ma_gainer_uninit(&pEngineNode->volumeGainer, pAllocationCallbacks); - } - - ma_spatializer_uninit(&pEngineNode->spatializer, pAllocationCallbacks); - ma_linear_resampler_uninit(&pEngineNode->resampler, pAllocationCallbacks); - - /* Free the heap last. */ - if (pEngineNode->_ownsHeap) { - ma_free(pEngineNode->_pHeap, pAllocationCallbacks); - } -} - - -MA_API ma_sound_config ma_sound_config_init(void) -{ - return ma_sound_config_init_2(NULL); -} - -MA_API ma_sound_config ma_sound_config_init_2(ma_engine* pEngine) -{ - ma_sound_config config; - - MA_ZERO_OBJECT(&config); - - if (pEngine != NULL) { - config.monoExpansionMode = pEngine->monoExpansionMode; - } else { - config.monoExpansionMode = ma_mono_expansion_mode_default; - } - - config.rangeEndInPCMFrames = ~((ma_uint64)0); - config.loopPointEndInPCMFrames = ~((ma_uint64)0); - - return config; -} - -MA_API ma_sound_group_config ma_sound_group_config_init(void) -{ - return ma_sound_group_config_init_2(NULL); -} - -MA_API ma_sound_group_config ma_sound_group_config_init_2(ma_engine* pEngine) -{ - ma_sound_group_config config; - - MA_ZERO_OBJECT(&config); - - if (pEngine != NULL) { - config.monoExpansionMode = pEngine->monoExpansionMode; - } else { - config.monoExpansionMode = ma_mono_expansion_mode_default; - } - - return config; -} - - -MA_API ma_engine_config ma_engine_config_init(void) -{ - ma_engine_config config; - - MA_ZERO_OBJECT(&config); - config.listenerCount = 1; /* Always want at least one listener. */ - config.monoExpansionMode = ma_mono_expansion_mode_default; - - return config; -} - - -#if !defined(MA_NO_DEVICE_IO) -static void ma_engine_data_callback_internal(ma_device* pDevice, void* pFramesOut, const void* pFramesIn, ma_uint32 frameCount) -{ - ma_engine* pEngine = (ma_engine*)pDevice->pUserData; - - (void)pFramesIn; - - /* - Experiment: Try processing a resource manager job if we're on the Emscripten build. - - This serves two purposes: - - 1) It ensures jobs are actually processed at some point since we cannot guarantee that the - caller is doing the right thing and calling ma_resource_manager_process_next_job(); and - - 2) It's an attempt at working around an issue where processing jobs on the Emscripten main - loop doesn't work as well as it should. When trying to load sounds without the `DECODE` - flag or with the `ASYNC` flag, the sound data is just not able to be loaded in time - before the callback is processed. I think it's got something to do with the single- - threaded nature of Web, but I'm not entirely sure. - */ - #if !defined(MA_NO_RESOURCE_MANAGER) && defined(MA_EMSCRIPTEN) - { - if (pEngine->pResourceManager != NULL) { - if ((pEngine->pResourceManager->config.flags & MA_RESOURCE_MANAGER_FLAG_NO_THREADING) != 0) { - ma_resource_manager_process_next_job(pEngine->pResourceManager); - } - } - } - #endif - - ma_engine_read_pcm_frames(pEngine, pFramesOut, frameCount, NULL); -} - -static ma_uint32 ma_device__get_processing_size_in_frames(ma_device* pDevice) -{ - /* - The processing size is the period size. The device can have a fixed sized processing size, or - it can be decided by the backend in which case it can be variable. - */ - if (pDevice->playback.intermediaryBufferCap > 0) { - /* Using a fixed sized processing callback. */ - return pDevice->playback.intermediaryBufferCap; - } else { - /* Not using a fixed sized processing callback. Need to estimate the processing size based on the backend. */ - return pDevice->playback.internalPeriodSizeInFrames; - } -} -#endif - -MA_API ma_result ma_engine_init(const ma_engine_config* pConfig, ma_engine* pEngine) -{ - ma_result result; - ma_node_graph_config nodeGraphConfig; - ma_engine_config engineConfig; - ma_spatializer_listener_config listenerConfig; - ma_uint32 iListener; - - if (pEngine == NULL) { - return MA_INVALID_ARGS; - } - - MA_ZERO_OBJECT(pEngine); - - /* The config is allowed to be NULL in which case we use defaults for everything. */ - if (pConfig != NULL) { - engineConfig = *pConfig; - } else { - engineConfig = ma_engine_config_init(); - } - - pEngine->monoExpansionMode = engineConfig.monoExpansionMode; - pEngine->defaultVolumeSmoothTimeInPCMFrames = engineConfig.defaultVolumeSmoothTimeInPCMFrames; - pEngine->onProcess = engineConfig.onProcess; - pEngine->pProcessUserData = engineConfig.pProcessUserData; - ma_allocation_callbacks_init_copy(&pEngine->allocationCallbacks, &engineConfig.allocationCallbacks); - - #if !defined(MA_NO_RESOURCE_MANAGER) - { - pEngine->pResourceManager = engineConfig.pResourceManager; - } - #endif - - #if !defined(MA_NO_DEVICE_IO) - { - pEngine->pDevice = engineConfig.pDevice; - - /* If we don't have a device, we need one. */ - if (pEngine->pDevice == NULL && engineConfig.noDevice == MA_FALSE) { - ma_device_config deviceConfig; - - pEngine->pDevice = (ma_device*)ma_malloc(sizeof(*pEngine->pDevice), &pEngine->allocationCallbacks); - if (pEngine->pDevice == NULL) { - return MA_OUT_OF_MEMORY; - } - - deviceConfig = ma_device_config_init(ma_device_type_playback); - deviceConfig.playback.pDeviceID = engineConfig.pPlaybackDeviceID; - deviceConfig.playback.format = ma_format_f32; - deviceConfig.playback.channels = engineConfig.channels; - deviceConfig.sampleRate = engineConfig.sampleRate; - deviceConfig.dataCallback = (engineConfig.dataCallback != NULL) ? engineConfig.dataCallback : ma_engine_data_callback_internal; - deviceConfig.pUserData = pEngine; - deviceConfig.notificationCallback = engineConfig.notificationCallback; - deviceConfig.periodSizeInFrames = engineConfig.periodSizeInFrames; - deviceConfig.periodSizeInMilliseconds = engineConfig.periodSizeInMilliseconds; - deviceConfig.noPreSilencedOutputBuffer = MA_TRUE; /* We'll always be outputting to every frame in the callback so there's no need for a pre-silenced buffer. */ - deviceConfig.noClip = MA_TRUE; /* The engine will do clipping itself. */ - - if (engineConfig.pContext == NULL) { - ma_context_config contextConfig = ma_context_config_init(); - contextConfig.allocationCallbacks = pEngine->allocationCallbacks; - contextConfig.pLog = engineConfig.pLog; - - /* If the engine config does not specify a log, use the resource manager's if we have one. */ - #ifndef MA_NO_RESOURCE_MANAGER - { - if (contextConfig.pLog == NULL && engineConfig.pResourceManager != NULL) { - contextConfig.pLog = ma_resource_manager_get_log(engineConfig.pResourceManager); - } - } - #endif - - result = ma_device_init_ex(NULL, 0, &contextConfig, &deviceConfig, pEngine->pDevice); - } else { - result = ma_device_init(engineConfig.pContext, &deviceConfig, pEngine->pDevice); - } - - if (result != MA_SUCCESS) { - ma_free(pEngine->pDevice, &pEngine->allocationCallbacks); - pEngine->pDevice = NULL; - return result; - } - - pEngine->ownsDevice = MA_TRUE; - } - - /* Update the channel count and sample rate of the engine config so we can reference it below. */ - if (pEngine->pDevice != NULL) { - engineConfig.channels = pEngine->pDevice->playback.channels; - engineConfig.sampleRate = pEngine->pDevice->sampleRate; - - /* - The processing size used by the engine is determined by engineConfig.periodSizeInFrames. We want - to make this equal to what the device is using for it's period size. If we don't do that, it's - possible that the node graph will split it's processing into multiple passes which can introduce - glitching. - */ - engineConfig.periodSizeInFrames = ma_device__get_processing_size_in_frames(pEngine->pDevice); - } - } - #endif - - if (engineConfig.channels == 0 || engineConfig.sampleRate == 0) { - return MA_INVALID_ARGS; - } - - pEngine->sampleRate = engineConfig.sampleRate; - - /* The engine always uses either the log that was passed into the config, or the context's log is available. */ - if (engineConfig.pLog != NULL) { - pEngine->pLog = engineConfig.pLog; - } else { - #if !defined(MA_NO_DEVICE_IO) - { - pEngine->pLog = ma_device_get_log(pEngine->pDevice); - } - #else - { - pEngine->pLog = NULL; - } - #endif - } - - - /* The engine is a node graph. This needs to be initialized after we have the device so we can determine the channel count. */ - nodeGraphConfig = ma_node_graph_config_init(engineConfig.channels); - nodeGraphConfig.processingSizeInFrames = engineConfig.periodSizeInFrames; - nodeGraphConfig.preMixStackSizeInBytes = engineConfig.preMixStackSizeInBytes; - - result = ma_node_graph_init(&nodeGraphConfig, &pEngine->allocationCallbacks, &pEngine->nodeGraph); - if (result != MA_SUCCESS) { - goto on_error_1; - } - - - /* We need at least one listener. */ - if (engineConfig.listenerCount == 0) { - engineConfig.listenerCount = 1; - } - - if (engineConfig.listenerCount > MA_ENGINE_MAX_LISTENERS) { - result = MA_INVALID_ARGS; /* Too many listeners. */ - goto on_error_1; - } - - for (iListener = 0; iListener < engineConfig.listenerCount; iListener += 1) { - listenerConfig = ma_spatializer_listener_config_init(ma_node_graph_get_channels(&pEngine->nodeGraph)); - - /* - If we're using a device, use the device's channel map for the listener. Otherwise just use - miniaudio's default channel map. - */ - #if !defined(MA_NO_DEVICE_IO) - { - if (pEngine->pDevice != NULL) { - /* - Temporarily disabled. There is a subtle bug here where front-left and front-right - will be used by the device's channel map, but this is not what we want to use for - spatialization. Instead we want to use side-left and side-right. I need to figure - out a better solution for this. For now, disabling the use of device channel maps. - */ - /*listenerConfig.pChannelMapOut = pEngine->pDevice->playback.channelMap;*/ - } - } - #endif - - result = ma_spatializer_listener_init(&listenerConfig, &pEngine->allocationCallbacks, &pEngine->listeners[iListener]); /* TODO: Change this to a pre-allocated heap. */ - if (result != MA_SUCCESS) { - goto on_error_2; - } - - pEngine->listenerCount += 1; - } - - - /* Gain smoothing for spatialized sounds. */ - pEngine->gainSmoothTimeInFrames = engineConfig.gainSmoothTimeInFrames; - if (pEngine->gainSmoothTimeInFrames == 0) { - ma_uint32 gainSmoothTimeInMilliseconds = engineConfig.gainSmoothTimeInMilliseconds; - if (gainSmoothTimeInMilliseconds == 0) { - gainSmoothTimeInMilliseconds = 8; - } - - pEngine->gainSmoothTimeInFrames = (gainSmoothTimeInMilliseconds * ma_engine_get_sample_rate(pEngine)) / 1000; /* 8ms by default. */ - } - - - /* We need a resource manager. */ - #ifndef MA_NO_RESOURCE_MANAGER - { - if (pEngine->pResourceManager == NULL) { - ma_resource_manager_config resourceManagerConfig; - - pEngine->pResourceManager = (ma_resource_manager*)ma_malloc(sizeof(*pEngine->pResourceManager), &pEngine->allocationCallbacks); - if (pEngine->pResourceManager == NULL) { - result = MA_OUT_OF_MEMORY; - goto on_error_2; - } - - resourceManagerConfig = ma_resource_manager_config_init(); - resourceManagerConfig.pLog = pEngine->pLog; /* Always use the engine's log for internally-managed resource managers. */ - resourceManagerConfig.decodedFormat = ma_format_f32; - resourceManagerConfig.decodedChannels = 0; /* Leave the decoded channel count as 0 so we can get good spatialization. */ - resourceManagerConfig.decodedSampleRate = ma_engine_get_sample_rate(pEngine); - ma_allocation_callbacks_init_copy(&resourceManagerConfig.allocationCallbacks, &pEngine->allocationCallbacks); - resourceManagerConfig.pVFS = engineConfig.pResourceManagerVFS; - - /* The Emscripten build cannot use threads unless it's targeting pthreads. */ - #if defined(MA_EMSCRIPTEN) && !defined(__EMSCRIPTEN_PTHREADS__) - { - resourceManagerConfig.jobThreadCount = 0; - resourceManagerConfig.flags |= MA_RESOURCE_MANAGER_FLAG_NO_THREADING; - } - #endif - - result = ma_resource_manager_init(&resourceManagerConfig, pEngine->pResourceManager); - if (result != MA_SUCCESS) { - goto on_error_3; - } - - pEngine->ownsResourceManager = MA_TRUE; - } - } - #endif - - /* Setup some stuff for inlined sounds. That is sounds played with ma_engine_play_sound(). */ - pEngine->inlinedSoundLock = 0; - pEngine->pInlinedSoundHead = NULL; - - /* Start the engine if required. This should always be the last step. */ - #if !defined(MA_NO_DEVICE_IO) - { - if (engineConfig.noAutoStart == MA_FALSE && pEngine->pDevice != NULL) { - result = ma_engine_start(pEngine); - if (result != MA_SUCCESS) { - goto on_error_4; /* Failed to start the engine. */ - } - } - } - #endif - - return MA_SUCCESS; - -#if !defined(MA_NO_DEVICE_IO) -on_error_4: -#endif -#if !defined(MA_NO_RESOURCE_MANAGER) -on_error_3: - if (pEngine->ownsResourceManager) { - ma_free(pEngine->pResourceManager, &pEngine->allocationCallbacks); - } -#endif /* MA_NO_RESOURCE_MANAGER */ -on_error_2: - for (iListener = 0; iListener < pEngine->listenerCount; iListener += 1) { - ma_spatializer_listener_uninit(&pEngine->listeners[iListener], &pEngine->allocationCallbacks); - } - - ma_node_graph_uninit(&pEngine->nodeGraph, &pEngine->allocationCallbacks); -on_error_1: - #if !defined(MA_NO_DEVICE_IO) - { - if (pEngine->ownsDevice) { - ma_device_uninit(pEngine->pDevice); - ma_free(pEngine->pDevice, &pEngine->allocationCallbacks); - } - } - #endif - - return result; -} - -MA_API void ma_engine_uninit(ma_engine* pEngine) -{ - ma_uint32 iListener; - - if (pEngine == NULL) { - return; - } - - /* The device must be uninitialized before the node graph to ensure the audio thread doesn't try accessing it. */ - #if !defined(MA_NO_DEVICE_IO) - { - if (pEngine->ownsDevice) { - ma_device_uninit(pEngine->pDevice); - ma_free(pEngine->pDevice, &pEngine->allocationCallbacks); - } else { - if (pEngine->pDevice != NULL) { - ma_device_stop(pEngine->pDevice); - } - } - } - #endif - - /* - All inlined sounds need to be deleted. I'm going to use a lock here just to future proof in case - I want to do some kind of garbage collection later on. - */ - ma_spinlock_lock(&pEngine->inlinedSoundLock); - { - for (;;) { - ma_sound_inlined* pSoundToDelete = pEngine->pInlinedSoundHead; - if (pSoundToDelete == NULL) { - break; /* Done. */ - } - - pEngine->pInlinedSoundHead = pSoundToDelete->pNext; - - ma_sound_uninit(&pSoundToDelete->sound); - ma_free(pSoundToDelete, &pEngine->allocationCallbacks); - } - } - ma_spinlock_unlock(&pEngine->inlinedSoundLock); - - for (iListener = 0; iListener < pEngine->listenerCount; iListener += 1) { - ma_spatializer_listener_uninit(&pEngine->listeners[iListener], &pEngine->allocationCallbacks); - } - - /* Make sure the node graph is uninitialized after the audio thread has been shutdown to prevent accessing of the node graph after being uninitialized. */ - ma_node_graph_uninit(&pEngine->nodeGraph, &pEngine->allocationCallbacks); - - /* Uninitialize the resource manager last to ensure we don't have a thread still trying to access it. */ -#ifndef MA_NO_RESOURCE_MANAGER - if (pEngine->ownsResourceManager) { - ma_resource_manager_uninit(pEngine->pResourceManager); - ma_free(pEngine->pResourceManager, &pEngine->allocationCallbacks); - } -#endif -} - -MA_API ma_result ma_engine_read_pcm_frames(ma_engine* pEngine, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead) -{ - ma_result result; - ma_uint64 framesRead = 0; - - if (pFramesRead != NULL) { - *pFramesRead = 0; - } - - result = ma_node_graph_read_pcm_frames(&pEngine->nodeGraph, pFramesOut, frameCount, &framesRead); - if (result != MA_SUCCESS) { - return result; - } - - if (pFramesRead != NULL) { - *pFramesRead = framesRead; - } - - if (pEngine->onProcess) { - pEngine->onProcess(pEngine->pProcessUserData, (float*)pFramesOut, framesRead); /* Safe cast to float* because the engine always works on floating point samples. */ - } - - return MA_SUCCESS; -} - -MA_API ma_node_graph* ma_engine_get_node_graph(ma_engine* pEngine) -{ - if (pEngine == NULL) { - return NULL; - } - - return &pEngine->nodeGraph; -} - -#if !defined(MA_NO_RESOURCE_MANAGER) -MA_API ma_resource_manager* ma_engine_get_resource_manager(ma_engine* pEngine) -{ - if (pEngine == NULL) { - return NULL; - } - - #if !defined(MA_NO_RESOURCE_MANAGER) - { - return pEngine->pResourceManager; - } - #else - { - return NULL; - } - #endif -} -#endif - -MA_API ma_device* ma_engine_get_device(ma_engine* pEngine) -{ - if (pEngine == NULL) { - return NULL; - } - - #if !defined(MA_NO_DEVICE_IO) - { - return pEngine->pDevice; - } - #else - { - return NULL; - } - #endif -} - -MA_API ma_log* ma_engine_get_log(ma_engine* pEngine) -{ - if (pEngine == NULL) { - return NULL; - } - - if (pEngine->pLog != NULL) { - return pEngine->pLog; - } else { - #if !defined(MA_NO_DEVICE_IO) - { - return ma_device_get_log(ma_engine_get_device(pEngine)); - } - #else - { - return NULL; - } - #endif - } -} - -MA_API ma_node* ma_engine_get_endpoint(ma_engine* pEngine) -{ - return ma_node_graph_get_endpoint(&pEngine->nodeGraph); -} - -MA_API ma_uint64 ma_engine_get_time_in_pcm_frames(const ma_engine* pEngine) -{ - return ma_node_graph_get_time(&pEngine->nodeGraph); -} - -MA_API ma_uint64 ma_engine_get_time_in_milliseconds(const ma_engine* pEngine) -{ - return ma_engine_get_time_in_pcm_frames(pEngine) * 1000 / ma_engine_get_sample_rate(pEngine); -} - -MA_API ma_result ma_engine_set_time_in_pcm_frames(ma_engine* pEngine, ma_uint64 globalTime) -{ - return ma_node_graph_set_time(&pEngine->nodeGraph, globalTime); -} - -MA_API ma_result ma_engine_set_time_in_milliseconds(ma_engine* pEngine, ma_uint64 globalTime) -{ - return ma_engine_set_time_in_pcm_frames(pEngine, globalTime * ma_engine_get_sample_rate(pEngine) / 1000); -} - -MA_API ma_uint64 ma_engine_get_time(const ma_engine* pEngine) -{ - return ma_engine_get_time_in_pcm_frames(pEngine); -} - -MA_API ma_result ma_engine_set_time(ma_engine* pEngine, ma_uint64 globalTime) -{ - return ma_engine_set_time_in_pcm_frames(pEngine, globalTime); -} - -MA_API ma_uint32 ma_engine_get_channels(const ma_engine* pEngine) -{ - return ma_node_graph_get_channels(&pEngine->nodeGraph); -} - -MA_API ma_uint32 ma_engine_get_sample_rate(const ma_engine* pEngine) -{ - if (pEngine == NULL) { - return 0; - } - - return pEngine->sampleRate; -} - - -MA_API ma_result ma_engine_start(ma_engine* pEngine) -{ - ma_result result; - - if (pEngine == NULL) { - return MA_INVALID_ARGS; - } - - #if !defined(MA_NO_DEVICE_IO) - { - if (pEngine->pDevice != NULL) { - result = ma_device_start(pEngine->pDevice); - } else { - result = MA_INVALID_OPERATION; /* The engine is running without a device which means there's no real notion of "starting" the engine. */ - } - } - #else - { - result = MA_INVALID_OPERATION; /* Device IO is disabled, so there's no real notion of "starting" the engine. */ - } - #endif - - if (result != MA_SUCCESS) { - return result; - } - - return MA_SUCCESS; -} - -MA_API ma_result ma_engine_stop(ma_engine* pEngine) -{ - ma_result result; - - if (pEngine == NULL) { - return MA_INVALID_ARGS; - } - - #if !defined(MA_NO_DEVICE_IO) - { - if (pEngine->pDevice != NULL) { - result = ma_device_stop(pEngine->pDevice); - } else { - result = MA_INVALID_OPERATION; /* The engine is running without a device which means there's no real notion of "stopping" the engine. */ - } - } - #else - { - result = MA_INVALID_OPERATION; /* Device IO is disabled, so there's no real notion of "stopping" the engine. */ - } - #endif - - if (result != MA_SUCCESS) { - return result; - } - - return MA_SUCCESS; -} - -MA_API ma_result ma_engine_set_volume(ma_engine* pEngine, float volume) -{ - if (pEngine == NULL) { - return MA_INVALID_ARGS; - } - - return ma_node_set_output_bus_volume(ma_node_graph_get_endpoint(&pEngine->nodeGraph), 0, volume); -} - -MA_API float ma_engine_get_volume(ma_engine* pEngine) -{ - if (pEngine == NULL) { - return 0; - } - - return ma_node_get_output_bus_volume(ma_node_graph_get_endpoint(&pEngine->nodeGraph), 0); -} - -MA_API ma_result ma_engine_set_gain_db(ma_engine* pEngine, float gainDB) -{ - return ma_engine_set_volume(pEngine, ma_volume_db_to_linear(gainDB)); -} - -MA_API float ma_engine_get_gain_db(ma_engine* pEngine) -{ - return ma_volume_linear_to_db(ma_engine_get_volume(pEngine)); -} - - -MA_API ma_uint32 ma_engine_get_listener_count(const ma_engine* pEngine) -{ - if (pEngine == NULL) { - return 0; - } - - return pEngine->listenerCount; -} - -MA_API ma_uint32 ma_engine_find_closest_listener(const ma_engine* pEngine, float absolutePosX, float absolutePosY, float absolutePosZ) -{ - ma_uint32 iListener; - ma_uint32 iListenerClosest; - float closestLen2 = MA_FLT_MAX; - - if (pEngine == NULL || pEngine->listenerCount == 1) { - return 0; - } - - iListenerClosest = 0; - for (iListener = 0; iListener < pEngine->listenerCount; iListener += 1) { - if (ma_engine_listener_is_enabled(pEngine, iListener)) { - float len2 = ma_vec3f_len2(ma_vec3f_sub(ma_spatializer_listener_get_position(&pEngine->listeners[iListener]), ma_vec3f_init_3f(absolutePosX, absolutePosY, absolutePosZ))); - if (closestLen2 > len2) { - closestLen2 = len2; - iListenerClosest = iListener; - } - } - } - - MA_ASSERT(iListenerClosest < 255); - return iListenerClosest; -} - -MA_API void ma_engine_listener_set_position(ma_engine* pEngine, ma_uint32 listenerIndex, float x, float y, float z) -{ - if (pEngine == NULL || listenerIndex >= pEngine->listenerCount) { - return; - } - - ma_spatializer_listener_set_position(&pEngine->listeners[listenerIndex], x, y, z); -} - -MA_API ma_vec3f ma_engine_listener_get_position(const ma_engine* pEngine, ma_uint32 listenerIndex) -{ - if (pEngine == NULL || listenerIndex >= pEngine->listenerCount) { - return ma_vec3f_init_3f(0, 0, 0); - } - - return ma_spatializer_listener_get_position(&pEngine->listeners[listenerIndex]); -} - -MA_API void ma_engine_listener_set_direction(ma_engine* pEngine, ma_uint32 listenerIndex, float x, float y, float z) -{ - if (pEngine == NULL || listenerIndex >= pEngine->listenerCount) { - return; - } - - ma_spatializer_listener_set_direction(&pEngine->listeners[listenerIndex], x, y, z); -} - -MA_API ma_vec3f ma_engine_listener_get_direction(const ma_engine* pEngine, ma_uint32 listenerIndex) -{ - if (pEngine == NULL || listenerIndex >= pEngine->listenerCount) { - return ma_vec3f_init_3f(0, 0, -1); - } - - return ma_spatializer_listener_get_direction(&pEngine->listeners[listenerIndex]); -} - -MA_API void ma_engine_listener_set_velocity(ma_engine* pEngine, ma_uint32 listenerIndex, float x, float y, float z) -{ - if (pEngine == NULL || listenerIndex >= pEngine->listenerCount) { - return; - } - - ma_spatializer_listener_set_velocity(&pEngine->listeners[listenerIndex], x, y, z); -} - -MA_API ma_vec3f ma_engine_listener_get_velocity(const ma_engine* pEngine, ma_uint32 listenerIndex) -{ - if (pEngine == NULL || listenerIndex >= pEngine->listenerCount) { - return ma_vec3f_init_3f(0, 0, 0); - } - - return ma_spatializer_listener_get_velocity(&pEngine->listeners[listenerIndex]); -} - -MA_API void ma_engine_listener_set_cone(ma_engine* pEngine, ma_uint32 listenerIndex, float innerAngleInRadians, float outerAngleInRadians, float outerGain) -{ - if (pEngine == NULL || listenerIndex >= pEngine->listenerCount) { - return; - } - - ma_spatializer_listener_set_cone(&pEngine->listeners[listenerIndex], innerAngleInRadians, outerAngleInRadians, outerGain); -} - -MA_API void ma_engine_listener_get_cone(const ma_engine* pEngine, ma_uint32 listenerIndex, float* pInnerAngleInRadians, float* pOuterAngleInRadians, float* pOuterGain) -{ - if (pInnerAngleInRadians != NULL) { - *pInnerAngleInRadians = 0; - } - - if (pOuterAngleInRadians != NULL) { - *pOuterAngleInRadians = 0; - } - - if (pOuterGain != NULL) { - *pOuterGain = 0; - } - - if (pEngine == NULL || listenerIndex >= pEngine->listenerCount) { - return; - } - - ma_spatializer_listener_get_cone(&pEngine->listeners[listenerIndex], pInnerAngleInRadians, pOuterAngleInRadians, pOuterGain); -} - -MA_API void ma_engine_listener_set_world_up(ma_engine* pEngine, ma_uint32 listenerIndex, float x, float y, float z) -{ - if (pEngine == NULL || listenerIndex >= pEngine->listenerCount) { - return; - } - - ma_spatializer_listener_set_world_up(&pEngine->listeners[listenerIndex], x, y, z); -} - -MA_API ma_vec3f ma_engine_listener_get_world_up(const ma_engine* pEngine, ma_uint32 listenerIndex) -{ - if (pEngine == NULL || listenerIndex >= pEngine->listenerCount) { - return ma_vec3f_init_3f(0, 1, 0); - } - - return ma_spatializer_listener_get_world_up(&pEngine->listeners[listenerIndex]); -} - -MA_API void ma_engine_listener_set_enabled(ma_engine* pEngine, ma_uint32 listenerIndex, ma_bool32 isEnabled) -{ - if (pEngine == NULL || listenerIndex >= pEngine->listenerCount) { - return; - } - - ma_spatializer_listener_set_enabled(&pEngine->listeners[listenerIndex], isEnabled); -} - -MA_API ma_bool32 ma_engine_listener_is_enabled(const ma_engine* pEngine, ma_uint32 listenerIndex) -{ - if (pEngine == NULL || listenerIndex >= pEngine->listenerCount) { - return MA_FALSE; - } - - return ma_spatializer_listener_is_enabled(&pEngine->listeners[listenerIndex]); -} - - -#ifndef MA_NO_RESOURCE_MANAGER -MA_API ma_result ma_engine_play_sound_ex(ma_engine* pEngine, const char* pFilePath, ma_node* pNode, ma_uint32 nodeInputBusIndex) -{ - ma_result result = MA_SUCCESS; - ma_sound_inlined* pSound = NULL; - ma_sound_inlined* pNextSound = NULL; - - if (pEngine == NULL || pFilePath == NULL) { - return MA_INVALID_ARGS; - } - - /* Attach to the endpoint node if nothing is specified. */ - if (pNode == NULL) { - pNode = ma_node_graph_get_endpoint(&pEngine->nodeGraph); - nodeInputBusIndex = 0; - } - - /* - We want to check if we can recycle an already-allocated inlined sound. Since this is just a - helper I'm not *too* concerned about performance here and I'm happy to use a lock to keep - the implementation simple. Maybe this can be optimized later if there's enough demand, but - if this function is being used it probably means the caller doesn't really care too much. - - What we do is check the atEnd flag. When this is true, we can recycle the sound. Otherwise - we just keep iterating. If we reach the end without finding a sound to recycle we just - allocate a new one. This doesn't scale well for a massive number of sounds being played - simultaneously as we don't ever actually free the sound objects. Some kind of garbage - collection routine might be valuable for this which I'll think about. - */ - ma_spinlock_lock(&pEngine->inlinedSoundLock); - { - ma_uint32 soundFlags = 0; - - for (pNextSound = pEngine->pInlinedSoundHead; pNextSound != NULL; pNextSound = pNextSound->pNext) { - if (ma_sound_at_end(&pNextSound->sound)) { - /* - The sound is at the end which means it's available for recycling. All we need to do - is uninitialize it and reinitialize it. All we're doing is recycling memory. - */ - pSound = pNextSound; - ma_atomic_fetch_sub_32(&pEngine->inlinedSoundCount, 1); - break; - } - } - - if (pSound != NULL) { - /* - We actually want to detach the sound from the list here. The reason is because we want the sound - to be in a consistent state at the non-recycled case to simplify the logic below. - */ - if (pEngine->pInlinedSoundHead == pSound) { - pEngine->pInlinedSoundHead = pSound->pNext; - } - - if (pSound->pPrev != NULL) { - pSound->pPrev->pNext = pSound->pNext; - } - if (pSound->pNext != NULL) { - pSound->pNext->pPrev = pSound->pPrev; - } - - /* Now the previous sound needs to be uninitialized. */ - ma_sound_uninit(&pNextSound->sound); - } else { - /* No sound available for recycling. Allocate one now. */ - pSound = (ma_sound_inlined*)ma_malloc(sizeof(*pSound), &pEngine->allocationCallbacks); - } - - if (pSound != NULL) { /* Safety check for the allocation above. */ - /* - At this point we should have memory allocated for the inlined sound. We just need - to initialize it like a normal sound now. - */ - soundFlags |= MA_SOUND_FLAG_ASYNC; /* For inlined sounds we don't want to be sitting around waiting for stuff to load so force an async load. */ - soundFlags |= MA_SOUND_FLAG_NO_DEFAULT_ATTACHMENT; /* We want specific control over where the sound is attached in the graph. We'll attach it manually just before playing the sound. */ - soundFlags |= MA_SOUND_FLAG_NO_PITCH; /* Pitching isn't usable with inlined sounds, so disable it to save on speed. */ - soundFlags |= MA_SOUND_FLAG_NO_SPATIALIZATION; /* Not currently doing spatialization with inlined sounds, but this might actually change later. For now disable spatialization. Will be removed if we ever add support for spatialization here. */ - - result = ma_sound_init_from_file(pEngine, pFilePath, soundFlags, NULL, NULL, &pSound->sound); - if (result == MA_SUCCESS) { - /* Now attach the sound to the graph. */ - result = ma_node_attach_output_bus(pSound, 0, pNode, nodeInputBusIndex); - if (result == MA_SUCCESS) { - /* At this point the sound should be loaded and we can go ahead and add it to the list. The new item becomes the new head. */ - pSound->pNext = pEngine->pInlinedSoundHead; - pSound->pPrev = NULL; - - pEngine->pInlinedSoundHead = pSound; /* <-- This is what attaches the sound to the list. */ - if (pSound->pNext != NULL) { - pSound->pNext->pPrev = pSound; - } - } else { - ma_free(pSound, &pEngine->allocationCallbacks); - } - } else { - ma_free(pSound, &pEngine->allocationCallbacks); - } - } else { - result = MA_OUT_OF_MEMORY; - } - } - ma_spinlock_unlock(&pEngine->inlinedSoundLock); - - if (result != MA_SUCCESS) { - return result; - } - - /* Finally we can start playing the sound. */ - result = ma_sound_start(&pSound->sound); - if (result != MA_SUCCESS) { - /* Failed to start the sound. We need to mark it for recycling and return an error. */ - ma_atomic_exchange_32(&pSound->sound.atEnd, MA_TRUE); - return result; - } - - ma_atomic_fetch_add_32(&pEngine->inlinedSoundCount, 1); - return result; -} - -MA_API ma_result ma_engine_play_sound(ma_engine* pEngine, const char* pFilePath, ma_sound_group* pGroup) -{ - return ma_engine_play_sound_ex(pEngine, pFilePath, pGroup, 0); -} -#endif - - -static ma_result ma_sound_preinit(ma_engine* pEngine, ma_sound* pSound) -{ - if (pSound == NULL) { - return MA_INVALID_ARGS; - } - - MA_ZERO_OBJECT(pSound); - pSound->seekTarget = MA_SEEK_TARGET_NONE; - - if (pEngine == NULL) { - return MA_INVALID_ARGS; - } - - return MA_SUCCESS; -} - -static ma_result ma_sound_init_from_data_source_internal(ma_engine* pEngine, const ma_sound_config* pConfig, ma_sound* pSound) -{ - ma_result result; - ma_engine_node_config engineNodeConfig; - ma_engine_node_type type; /* Will be set to ma_engine_node_type_group if no data source is specified. */ - - /* Do not clear pSound to zero here - that's done at a higher level with ma_sound_preinit(). */ - MA_ASSERT(pEngine != NULL); - MA_ASSERT(pSound != NULL); - - if (pConfig == NULL) { - return MA_INVALID_ARGS; - } - - pSound->pDataSource = pConfig->pDataSource; - - if (pConfig->pDataSource != NULL) { - type = ma_engine_node_type_sound; - } else { - type = ma_engine_node_type_group; - } - - /* - Sounds are engine nodes. Before we can initialize this we need to determine the channel count. - If we can't do this we need to abort. It's up to the caller to ensure they're using a data - source that provides this information upfront. - */ - engineNodeConfig = ma_engine_node_config_init(pEngine, type, pConfig->flags); - engineNodeConfig.channelsIn = pConfig->channelsIn; - engineNodeConfig.channelsOut = pConfig->channelsOut; - engineNodeConfig.volumeSmoothTimeInPCMFrames = pConfig->volumeSmoothTimeInPCMFrames; - engineNodeConfig.monoExpansionMode = pConfig->monoExpansionMode; - - if (engineNodeConfig.volumeSmoothTimeInPCMFrames == 0) { - engineNodeConfig.volumeSmoothTimeInPCMFrames = pEngine->defaultVolumeSmoothTimeInPCMFrames; - } - - /* If we're loading from a data source the input channel count needs to be the data source's native channel count. */ - if (pConfig->pDataSource != NULL) { - result = ma_data_source_get_data_format(pConfig->pDataSource, NULL, &engineNodeConfig.channelsIn, &engineNodeConfig.sampleRate, NULL, 0); - if (result != MA_SUCCESS) { - return result; /* Failed to retrieve the channel count. */ - } - - if (engineNodeConfig.channelsIn == 0) { - return MA_INVALID_OPERATION; /* Invalid channel count. */ - } - - if (engineNodeConfig.channelsOut == MA_SOUND_SOURCE_CHANNEL_COUNT) { - engineNodeConfig.channelsOut = engineNodeConfig.channelsIn; - } - } - - - /* Getting here means we should have a valid channel count and we can initialize the engine node. */ - result = ma_engine_node_init(&engineNodeConfig, &pEngine->allocationCallbacks, &pSound->engineNode); - if (result != MA_SUCCESS) { - return result; - } - - /* If no attachment is specified, attach the sound straight to the endpoint. */ - if (pConfig->pInitialAttachment == NULL) { - /* No group. Attach straight to the endpoint by default, unless the caller has requested that it not. */ - if ((pConfig->flags & MA_SOUND_FLAG_NO_DEFAULT_ATTACHMENT) == 0) { - result = ma_node_attach_output_bus(pSound, 0, ma_node_graph_get_endpoint(&pEngine->nodeGraph), 0); - } - } else { - /* An attachment is specified. Attach to it by default. The sound has only a single output bus, and the config will specify which input bus to attach to. */ - result = ma_node_attach_output_bus(pSound, 0, pConfig->pInitialAttachment, pConfig->initialAttachmentInputBusIndex); - } - - if (result != MA_SUCCESS) { - ma_engine_node_uninit(&pSound->engineNode, &pEngine->allocationCallbacks); - return result; - } - - - /* Apply initial range and looping state to the data source if applicable. */ - if (pConfig->rangeBegInPCMFrames != 0 || pConfig->rangeEndInPCMFrames != ~((ma_uint64)0)) { - ma_data_source_set_range_in_pcm_frames(ma_sound_get_data_source(pSound), pConfig->rangeBegInPCMFrames, pConfig->rangeEndInPCMFrames); - } - - if (pConfig->loopPointBegInPCMFrames != 0 || pConfig->loopPointEndInPCMFrames != ~((ma_uint64)0)) { - ma_data_source_set_range_in_pcm_frames(ma_sound_get_data_source(pSound), pConfig->loopPointBegInPCMFrames, pConfig->loopPointEndInPCMFrames); - } - - ma_sound_set_looping(pSound, pConfig->isLooping || ((pConfig->flags & MA_SOUND_FLAG_LOOPING) != 0)); - - return MA_SUCCESS; -} - -#ifndef MA_NO_RESOURCE_MANAGER -MA_API ma_result ma_sound_init_from_file_internal(ma_engine* pEngine, const ma_sound_config* pConfig, ma_sound* pSound) -{ - ma_result result = MA_SUCCESS; - ma_uint32 flags; - ma_sound_config config; - ma_resource_manager_pipeline_notifications notifications; - - /* - The engine requires knowledge of the channel count of the underlying data source before it can - initialize the sound. Therefore, we need to make the resource manager wait until initialization - of the underlying data source to be initialized so we can get access to the channel count. To - do this, the MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_WAIT_INIT is forced. - - Because we're initializing the data source before the sound, there's a chance the notification - will get triggered before this function returns. This is OK, so long as the caller is aware of - it and can avoid accessing the sound from within the notification. - */ - flags = pConfig->flags | MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_WAIT_INIT; - if (pConfig->isLooping) { - flags |= MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_LOOPING; - } - - pSound->pResourceManagerDataSource = (ma_resource_manager_data_source*)ma_malloc(sizeof(*pSound->pResourceManagerDataSource), &pEngine->allocationCallbacks); - if (pSound->pResourceManagerDataSource == NULL) { - return MA_OUT_OF_MEMORY; - } - - /* Removed in 0.12. Set pDoneFence on the notifications. */ - notifications = pConfig->initNotifications; - if (pConfig->pDoneFence != NULL && notifications.done.pFence == NULL) { - notifications.done.pFence = pConfig->pDoneFence; - } - - /* - We must wrap everything around the fence if one was specified. This ensures ma_fence_wait() does - not return prematurely before the sound has finished initializing. - */ - if (notifications.done.pFence) { ma_fence_acquire(notifications.done.pFence); } - { - ma_resource_manager_data_source_config resourceManagerDataSourceConfig = ma_resource_manager_data_source_config_init(); - resourceManagerDataSourceConfig.pFilePath = pConfig->pFilePath; - resourceManagerDataSourceConfig.pFilePathW = pConfig->pFilePathW; - resourceManagerDataSourceConfig.flags = flags; - resourceManagerDataSourceConfig.pNotifications = ¬ifications; - resourceManagerDataSourceConfig.initialSeekPointInPCMFrames = pConfig->initialSeekPointInPCMFrames; - resourceManagerDataSourceConfig.rangeBegInPCMFrames = pConfig->rangeBegInPCMFrames; - resourceManagerDataSourceConfig.rangeEndInPCMFrames = pConfig->rangeEndInPCMFrames; - resourceManagerDataSourceConfig.loopPointBegInPCMFrames = pConfig->loopPointBegInPCMFrames; - resourceManagerDataSourceConfig.loopPointEndInPCMFrames = pConfig->loopPointEndInPCMFrames; - resourceManagerDataSourceConfig.isLooping = (flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_LOOPING) != 0; - - result = ma_resource_manager_data_source_init_ex(pEngine->pResourceManager, &resourceManagerDataSourceConfig, pSound->pResourceManagerDataSource); - if (result != MA_SUCCESS) { - goto done; - } - - pSound->ownsDataSource = MA_TRUE; /* <-- Important. Not setting this will result in the resource manager data source never getting uninitialized. */ - - /* We need to use a slightly customized version of the config so we'll need to make a copy. */ - config = *pConfig; - config.pFilePath = NULL; - config.pFilePathW = NULL; - config.pDataSource = pSound->pResourceManagerDataSource; - - result = ma_sound_init_from_data_source_internal(pEngine, &config, pSound); - if (result != MA_SUCCESS) { - ma_resource_manager_data_source_uninit(pSound->pResourceManagerDataSource); - ma_free(pSound->pResourceManagerDataSource, &pEngine->allocationCallbacks); - MA_ZERO_OBJECT(pSound); - goto done; - } - } -done: - if (notifications.done.pFence) { ma_fence_release(notifications.done.pFence); } - return result; -} - -MA_API ma_result ma_sound_init_from_file(ma_engine* pEngine, const char* pFilePath, ma_uint32 flags, ma_sound_group* pGroup, ma_fence* pDoneFence, ma_sound* pSound) -{ - ma_sound_config config; - - if (pFilePath == NULL) { - return MA_INVALID_ARGS; - } - - config = ma_sound_config_init_2(pEngine); - config.pFilePath = pFilePath; - config.flags = flags; - config.pInitialAttachment = pGroup; - config.pDoneFence = pDoneFence; - - return ma_sound_init_ex(pEngine, &config, pSound); -} - -MA_API ma_result ma_sound_init_from_file_w(ma_engine* pEngine, const wchar_t* pFilePath, ma_uint32 flags, ma_sound_group* pGroup, ma_fence* pDoneFence, ma_sound* pSound) -{ - ma_sound_config config; - - if (pFilePath == NULL) { - return MA_INVALID_ARGS; - } - - config = ma_sound_config_init_2(pEngine); - config.pFilePathW = pFilePath; - config.flags = flags; - config.pInitialAttachment = pGroup; - config.pDoneFence = pDoneFence; - - return ma_sound_init_ex(pEngine, &config, pSound); -} - -MA_API ma_result ma_sound_init_copy(ma_engine* pEngine, const ma_sound* pExistingSound, ma_uint32 flags, ma_sound_group* pGroup, ma_sound* pSound) -{ - ma_result result; - ma_sound_config config; - - result = ma_sound_preinit(pEngine, pSound); - if (result != MA_SUCCESS) { - return result; - } - - if (pExistingSound == NULL) { - return MA_INVALID_ARGS; - } - - /* Cloning only works for data buffers (not streams) that are loaded from the resource manager. */ - if (pExistingSound->pResourceManagerDataSource == NULL) { - return MA_INVALID_OPERATION; - } - - /* - We need to make a clone of the data source. If the data source is not a data buffer (i.e. a stream) - this will fail. - */ - pSound->pResourceManagerDataSource = (ma_resource_manager_data_source*)ma_malloc(sizeof(*pSound->pResourceManagerDataSource), &pEngine->allocationCallbacks); - if (pSound->pResourceManagerDataSource == NULL) { - return MA_OUT_OF_MEMORY; - } - - result = ma_resource_manager_data_source_init_copy(pEngine->pResourceManager, pExistingSound->pResourceManagerDataSource, pSound->pResourceManagerDataSource); - if (result != MA_SUCCESS) { - ma_free(pSound->pResourceManagerDataSource, &pEngine->allocationCallbacks); - return result; - } - - config = ma_sound_config_init_2(pEngine); - config.pDataSource = pSound->pResourceManagerDataSource; - config.flags = flags; - config.pInitialAttachment = pGroup; - config.monoExpansionMode = pExistingSound->engineNode.monoExpansionMode; - config.volumeSmoothTimeInPCMFrames = pExistingSound->engineNode.volumeSmoothTimeInPCMFrames; - - result = ma_sound_init_from_data_source_internal(pEngine, &config, pSound); - if (result != MA_SUCCESS) { - ma_resource_manager_data_source_uninit(pSound->pResourceManagerDataSource); - ma_free(pSound->pResourceManagerDataSource, &pEngine->allocationCallbacks); - MA_ZERO_OBJECT(pSound); - return result; - } - - /* Make sure the sound is marked as the owner of the data source or else it will never get uninitialized. */ - pSound->ownsDataSource = MA_TRUE; - - return MA_SUCCESS; -} -#endif - -MA_API ma_result ma_sound_init_from_data_source(ma_engine* pEngine, ma_data_source* pDataSource, ma_uint32 flags, ma_sound_group* pGroup, ma_sound* pSound) -{ - ma_sound_config config = ma_sound_config_init_2(pEngine); - config.pDataSource = pDataSource; - config.flags = flags; - config.pInitialAttachment = pGroup; - return ma_sound_init_ex(pEngine, &config, pSound); -} - -MA_API ma_result ma_sound_init_ex(ma_engine* pEngine, const ma_sound_config* pConfig, ma_sound* pSound) -{ - ma_result result; - - result = ma_sound_preinit(pEngine, pSound); - if (result != MA_SUCCESS) { - return result; - } - - if (pConfig == NULL) { - return MA_INVALID_ARGS; - } - - pSound->endCallback = pConfig->endCallback; - pSound->pEndCallbackUserData = pConfig->pEndCallbackUserData; - - /* We need to load the sound differently depending on whether or not we're loading from a file. */ -#ifndef MA_NO_RESOURCE_MANAGER - if (pConfig->pFilePath != NULL || pConfig->pFilePathW != NULL) { - return ma_sound_init_from_file_internal(pEngine, pConfig, pSound); - } else -#endif - { - /* - Getting here means we're not loading from a file. We may be loading from an already-initialized - data source, or none at all. If we aren't specifying any data source, we'll be initializing - the equivalent to a group. ma_data_source_init_from_data_source_internal() will deal with this - for us, so no special treatment required here. - */ - return ma_sound_init_from_data_source_internal(pEngine, pConfig, pSound); - } -} - -MA_API void ma_sound_uninit(ma_sound* pSound) -{ - if (pSound == NULL) { - return; - } - - /* - Always uninitialize the node first. This ensures it's detached from the graph and does not return until it has done - so which makes thread safety beyond this point trivial. - */ - ma_engine_node_uninit(&pSound->engineNode, &pSound->engineNode.pEngine->allocationCallbacks); - - /* Once the sound is detached from the group we can guarantee that it won't be referenced by the mixer thread which means it's safe for us to destroy the data source. */ -#ifndef MA_NO_RESOURCE_MANAGER - if (pSound->ownsDataSource) { - ma_resource_manager_data_source_uninit(pSound->pResourceManagerDataSource); - ma_free(pSound->pResourceManagerDataSource, &pSound->engineNode.pEngine->allocationCallbacks); - pSound->pDataSource = NULL; - } -#else - MA_ASSERT(pSound->ownsDataSource == MA_FALSE); -#endif -} - -MA_API ma_engine* ma_sound_get_engine(const ma_sound* pSound) -{ - if (pSound == NULL) { - return NULL; - } - - return pSound->engineNode.pEngine; -} - -MA_API ma_data_source* ma_sound_get_data_source(const ma_sound* pSound) -{ - if (pSound == NULL) { - return NULL; - } - - return pSound->pDataSource; -} - -MA_API ma_result ma_sound_start(ma_sound* pSound) -{ - if (pSound == NULL) { - return MA_INVALID_ARGS; - } - - /* If the sound is already playing, do nothing. */ - if (ma_sound_is_playing(pSound)) { - return MA_SUCCESS; - } - - /* If the sound is at the end it means we want to start from the start again. */ - if (ma_sound_at_end(pSound)) { - ma_result result = ma_data_source_seek_to_pcm_frame(pSound->pDataSource, 0); - if (result != MA_SUCCESS && result != MA_NOT_IMPLEMENTED) { - return result; /* Failed to seek back to the start. */ - } - - /* Make sure we clear the end indicator. */ - ma_atomic_exchange_32(&pSound->atEnd, MA_FALSE); - } - - /* Make sure the sound is started. If there's a start delay, the sound won't actually start until the start time is reached. */ - ma_node_set_state(pSound, ma_node_state_started); - - return MA_SUCCESS; -} - -MA_API ma_result ma_sound_stop(ma_sound* pSound) -{ - if (pSound == NULL) { - return MA_INVALID_ARGS; - } - - /* This will stop the sound immediately. Use ma_sound_set_stop_time() to stop the sound at a specific time. */ - ma_node_set_state(pSound, ma_node_state_stopped); - - return MA_SUCCESS; -} - -MA_API ma_result ma_sound_stop_with_fade_in_pcm_frames(ma_sound* pSound, ma_uint64 fadeLengthInFrames) -{ - if (pSound == NULL) { - return MA_INVALID_ARGS; - } - - /* Stopping with a fade out requires us to schedule the stop into the future by the fade length. */ - ma_sound_set_stop_time_with_fade_in_pcm_frames(pSound, ma_engine_get_time_in_pcm_frames(ma_sound_get_engine(pSound)) + fadeLengthInFrames, fadeLengthInFrames); - - return MA_SUCCESS; -} - -MA_API ma_result ma_sound_stop_with_fade_in_milliseconds(ma_sound* pSound, ma_uint64 fadeLengthInMilliseconds) -{ - ma_uint64 sampleRate; - - if (pSound == NULL) { - return MA_INVALID_ARGS; - } - - sampleRate = ma_engine_get_sample_rate(ma_sound_get_engine(pSound)); - - return ma_sound_stop_with_fade_in_pcm_frames(pSound, (fadeLengthInMilliseconds * sampleRate) / 1000); -} - -MA_API void ma_sound_set_volume(ma_sound* pSound, float volume) -{ - if (pSound == NULL) { - return; - } - - ma_engine_node_set_volume(&pSound->engineNode, volume); -} - -MA_API float ma_sound_get_volume(const ma_sound* pSound) -{ - float volume = 0; - - if (pSound == NULL) { - return 0; - } - - ma_engine_node_get_volume(&pSound->engineNode, &volume); - - return volume; -} - -MA_API void ma_sound_set_pan(ma_sound* pSound, float pan) -{ - if (pSound == NULL) { - return; - } - - ma_panner_set_pan(&pSound->engineNode.panner, pan); -} - -MA_API float ma_sound_get_pan(const ma_sound* pSound) -{ - if (pSound == NULL) { - return 0; - } - - return ma_panner_get_pan(&pSound->engineNode.panner); -} - -MA_API void ma_sound_set_pan_mode(ma_sound* pSound, ma_pan_mode panMode) -{ - if (pSound == NULL) { - return; - } - - ma_panner_set_mode(&pSound->engineNode.panner, panMode); -} - -MA_API ma_pan_mode ma_sound_get_pan_mode(const ma_sound* pSound) -{ - if (pSound == NULL) { - return ma_pan_mode_balance; - } - - return ma_panner_get_mode(&pSound->engineNode.panner); -} - -MA_API void ma_sound_set_pitch(ma_sound* pSound, float pitch) -{ - if (pSound == NULL) { - return; - } - - if (pitch <= 0) { - return; - } - - ma_atomic_exchange_explicit_f32(&pSound->engineNode.pitch, pitch, ma_atomic_memory_order_release); -} - -MA_API float ma_sound_get_pitch(const ma_sound* pSound) -{ - if (pSound == NULL) { - return 0; - } - - return ma_atomic_load_f32(&pSound->engineNode.pitch); /* Naughty const-cast for this. */ -} - -MA_API void ma_sound_set_spatialization_enabled(ma_sound* pSound, ma_bool32 enabled) -{ - if (pSound == NULL) { - return; - } - - ma_atomic_exchange_explicit_32(&pSound->engineNode.isSpatializationDisabled, !enabled, ma_atomic_memory_order_release); -} - -MA_API ma_bool32 ma_sound_is_spatialization_enabled(const ma_sound* pSound) -{ - if (pSound == NULL) { - return MA_FALSE; - } - - return ma_engine_node_is_spatialization_enabled(&pSound->engineNode); -} - -MA_API void ma_sound_set_pinned_listener_index(ma_sound* pSound, ma_uint32 listenerIndex) -{ - if (pSound == NULL || listenerIndex >= ma_engine_get_listener_count(ma_sound_get_engine(pSound))) { - return; - } - - ma_atomic_exchange_explicit_32(&pSound->engineNode.pinnedListenerIndex, listenerIndex, ma_atomic_memory_order_release); -} - -MA_API ma_uint32 ma_sound_get_pinned_listener_index(const ma_sound* pSound) -{ - if (pSound == NULL) { - return MA_LISTENER_INDEX_CLOSEST; - } - - return ma_atomic_load_explicit_32(&pSound->engineNode.pinnedListenerIndex, ma_atomic_memory_order_acquire); -} - -MA_API ma_uint32 ma_sound_get_listener_index(const ma_sound* pSound) -{ - ma_uint32 listenerIndex; - - if (pSound == NULL) { - return 0; - } - - listenerIndex = ma_sound_get_pinned_listener_index(pSound); - if (listenerIndex == MA_LISTENER_INDEX_CLOSEST) { - ma_vec3f position = ma_sound_get_position(pSound); - return ma_engine_find_closest_listener(ma_sound_get_engine(pSound), position.x, position.y, position.z); - } - - return listenerIndex; -} - -MA_API ma_vec3f ma_sound_get_direction_to_listener(const ma_sound* pSound) -{ - ma_vec3f relativePos; - ma_engine* pEngine; - - if (pSound == NULL) { - return ma_vec3f_init_3f(0, 0, -1); - } - - pEngine = ma_sound_get_engine(pSound); - if (pEngine == NULL) { - return ma_vec3f_init_3f(0, 0, -1); - } - - ma_spatializer_get_relative_position_and_direction(&pSound->engineNode.spatializer, &pEngine->listeners[ma_sound_get_listener_index(pSound)], &relativePos, NULL); - - return ma_vec3f_normalize(ma_vec3f_neg(relativePos)); -} - -MA_API void ma_sound_set_position(ma_sound* pSound, float x, float y, float z) -{ - if (pSound == NULL) { - return; - } - - ma_spatializer_set_position(&pSound->engineNode.spatializer, x, y, z); -} - -MA_API ma_vec3f ma_sound_get_position(const ma_sound* pSound) -{ - if (pSound == NULL) { - return ma_vec3f_init_3f(0, 0, 0); - } - - return ma_spatializer_get_position(&pSound->engineNode.spatializer); -} - -MA_API void ma_sound_set_direction(ma_sound* pSound, float x, float y, float z) -{ - if (pSound == NULL) { - return; - } - - ma_spatializer_set_direction(&pSound->engineNode.spatializer, x, y, z); -} - -MA_API ma_vec3f ma_sound_get_direction(const ma_sound* pSound) -{ - if (pSound == NULL) { - return ma_vec3f_init_3f(0, 0, 0); - } - - return ma_spatializer_get_direction(&pSound->engineNode.spatializer); -} - -MA_API void ma_sound_set_velocity(ma_sound* pSound, float x, float y, float z) -{ - if (pSound == NULL) { - return; - } - - ma_spatializer_set_velocity(&pSound->engineNode.spatializer, x, y, z); -} - -MA_API ma_vec3f ma_sound_get_velocity(const ma_sound* pSound) -{ - if (pSound == NULL) { - return ma_vec3f_init_3f(0, 0, 0); - } - - return ma_spatializer_get_velocity(&pSound->engineNode.spatializer); -} - -MA_API void ma_sound_set_attenuation_model(ma_sound* pSound, ma_attenuation_model attenuationModel) -{ - if (pSound == NULL) { - return; - } - - ma_spatializer_set_attenuation_model(&pSound->engineNode.spatializer, attenuationModel); -} - -MA_API ma_attenuation_model ma_sound_get_attenuation_model(const ma_sound* pSound) -{ - if (pSound == NULL) { - return ma_attenuation_model_none; - } - - return ma_spatializer_get_attenuation_model(&pSound->engineNode.spatializer); -} - -MA_API void ma_sound_set_positioning(ma_sound* pSound, ma_positioning positioning) -{ - if (pSound == NULL) { - return; - } - - ma_spatializer_set_positioning(&pSound->engineNode.spatializer, positioning); -} - -MA_API ma_positioning ma_sound_get_positioning(const ma_sound* pSound) -{ - if (pSound == NULL) { - return ma_positioning_absolute; - } - - return ma_spatializer_get_positioning(&pSound->engineNode.spatializer); -} - -MA_API void ma_sound_set_rolloff(ma_sound* pSound, float rolloff) -{ - if (pSound == NULL) { - return; - } - - ma_spatializer_set_rolloff(&pSound->engineNode.spatializer, rolloff); -} - -MA_API float ma_sound_get_rolloff(const ma_sound* pSound) -{ - if (pSound == NULL) { - return 0; - } - - return ma_spatializer_get_rolloff(&pSound->engineNode.spatializer); -} - -MA_API void ma_sound_set_min_gain(ma_sound* pSound, float minGain) -{ - if (pSound == NULL) { - return; - } - - ma_spatializer_set_min_gain(&pSound->engineNode.spatializer, minGain); -} - -MA_API float ma_sound_get_min_gain(const ma_sound* pSound) -{ - if (pSound == NULL) { - return 0; - } - - return ma_spatializer_get_min_gain(&pSound->engineNode.spatializer); -} - -MA_API void ma_sound_set_max_gain(ma_sound* pSound, float maxGain) -{ - if (pSound == NULL) { - return; - } - - ma_spatializer_set_max_gain(&pSound->engineNode.spatializer, maxGain); -} - -MA_API float ma_sound_get_max_gain(const ma_sound* pSound) -{ - if (pSound == NULL) { - return 0; - } - - return ma_spatializer_get_max_gain(&pSound->engineNode.spatializer); -} - -MA_API void ma_sound_set_min_distance(ma_sound* pSound, float minDistance) -{ - if (pSound == NULL) { - return; - } - - ma_spatializer_set_min_distance(&pSound->engineNode.spatializer, minDistance); -} - -MA_API float ma_sound_get_min_distance(const ma_sound* pSound) -{ - if (pSound == NULL) { - return 0; - } - - return ma_spatializer_get_min_distance(&pSound->engineNode.spatializer); -} - -MA_API void ma_sound_set_max_distance(ma_sound* pSound, float maxDistance) -{ - if (pSound == NULL) { - return; - } - - ma_spatializer_set_max_distance(&pSound->engineNode.spatializer, maxDistance); -} - -MA_API float ma_sound_get_max_distance(const ma_sound* pSound) -{ - if (pSound == NULL) { - return 0; - } - - return ma_spatializer_get_max_distance(&pSound->engineNode.spatializer); -} - -MA_API void ma_sound_set_cone(ma_sound* pSound, float innerAngleInRadians, float outerAngleInRadians, float outerGain) -{ - if (pSound == NULL) { - return; - } - - ma_spatializer_set_cone(&pSound->engineNode.spatializer, innerAngleInRadians, outerAngleInRadians, outerGain); -} - -MA_API void ma_sound_get_cone(const ma_sound* pSound, float* pInnerAngleInRadians, float* pOuterAngleInRadians, float* pOuterGain) -{ - if (pInnerAngleInRadians != NULL) { - *pInnerAngleInRadians = 0; - } - - if (pOuterAngleInRadians != NULL) { - *pOuterAngleInRadians = 0; - } - - if (pOuterGain != NULL) { - *pOuterGain = 0; - } - - if (pSound == NULL) { - return; - } - - ma_spatializer_get_cone(&pSound->engineNode.spatializer, pInnerAngleInRadians, pOuterAngleInRadians, pOuterGain); -} - -MA_API void ma_sound_set_doppler_factor(ma_sound* pSound, float dopplerFactor) -{ - if (pSound == NULL) { - return; - } - - ma_spatializer_set_doppler_factor(&pSound->engineNode.spatializer, dopplerFactor); -} - -MA_API float ma_sound_get_doppler_factor(const ma_sound* pSound) -{ - if (pSound == NULL) { - return 0; - } - - return ma_spatializer_get_doppler_factor(&pSound->engineNode.spatializer); -} - -MA_API void ma_sound_set_directional_attenuation_factor(ma_sound* pSound, float directionalAttenuationFactor) -{ - if (pSound == NULL) { - return; - } - - ma_spatializer_set_directional_attenuation_factor(&pSound->engineNode.spatializer, directionalAttenuationFactor); -} - -MA_API float ma_sound_get_directional_attenuation_factor(const ma_sound* pSound) -{ - if (pSound == NULL) { - return 1; - } - - return ma_spatializer_get_directional_attenuation_factor(&pSound->engineNode.spatializer); -} - - -MA_API void ma_sound_set_fade_in_pcm_frames(ma_sound* pSound, float volumeBeg, float volumeEnd, ma_uint64 fadeLengthInFrames) -{ - if (pSound == NULL) { - return; - } - - ma_sound_set_fade_start_in_pcm_frames(pSound, volumeBeg, volumeEnd, fadeLengthInFrames, (~(ma_uint64)0)); -} - -MA_API void ma_sound_set_fade_in_milliseconds(ma_sound* pSound, float volumeBeg, float volumeEnd, ma_uint64 fadeLengthInMilliseconds) -{ - if (pSound == NULL) { - return; - } - - ma_sound_set_fade_in_pcm_frames(pSound, volumeBeg, volumeEnd, (fadeLengthInMilliseconds * pSound->engineNode.fader.config.sampleRate) / 1000); -} - -MA_API void ma_sound_set_fade_start_in_pcm_frames(ma_sound* pSound, float volumeBeg, float volumeEnd, ma_uint64 fadeLengthInFrames, ma_uint64 absoluteGlobalTimeInFrames) -{ - if (pSound == NULL) { - return; - } - - /* - We don't want to update the fader at this point because we need to use the engine's current time - to derive the fader's start offset. The timer is being updated on the audio thread so in order to - do this as accurately as possible we'll need to defer this to the audio thread. - */ - ma_atomic_float_set(&pSound->engineNode.fadeSettings.volumeBeg, volumeBeg); - ma_atomic_float_set(&pSound->engineNode.fadeSettings.volumeEnd, volumeEnd); - ma_atomic_uint64_set(&pSound->engineNode.fadeSettings.fadeLengthInFrames, fadeLengthInFrames); - ma_atomic_uint64_set(&pSound->engineNode.fadeSettings.absoluteGlobalTimeInFrames, absoluteGlobalTimeInFrames); -} - -MA_API void ma_sound_set_fade_start_in_milliseconds(ma_sound* pSound, float volumeBeg, float volumeEnd, ma_uint64 fadeLengthInMilliseconds, ma_uint64 absoluteGlobalTimeInMilliseconds) -{ - ma_uint32 sampleRate; - - if (pSound == NULL) { - return; - } - - sampleRate = ma_engine_get_sample_rate(ma_sound_get_engine(pSound)); - - ma_sound_set_fade_start_in_pcm_frames(pSound, volumeBeg, volumeEnd, (fadeLengthInMilliseconds * sampleRate) / 1000, (absoluteGlobalTimeInMilliseconds * sampleRate) / 1000); -} - -MA_API float ma_sound_get_current_fade_volume(const ma_sound* pSound) -{ - if (pSound == NULL) { - return MA_INVALID_ARGS; - } - - return ma_fader_get_current_volume(&pSound->engineNode.fader); -} - -MA_API void ma_sound_set_start_time_in_pcm_frames(ma_sound* pSound, ma_uint64 absoluteGlobalTimeInFrames) -{ - if (pSound == NULL) { - return; - } - - ma_node_set_state_time(pSound, ma_node_state_started, absoluteGlobalTimeInFrames); -} - -MA_API void ma_sound_set_start_time_in_milliseconds(ma_sound* pSound, ma_uint64 absoluteGlobalTimeInMilliseconds) -{ - if (pSound == NULL) { - return; - } - - ma_sound_set_start_time_in_pcm_frames(pSound, absoluteGlobalTimeInMilliseconds * ma_engine_get_sample_rate(ma_sound_get_engine(pSound)) / 1000); -} - -MA_API void ma_sound_set_stop_time_in_pcm_frames(ma_sound* pSound, ma_uint64 absoluteGlobalTimeInFrames) -{ - if (pSound == NULL) { - return; - } - - ma_sound_set_stop_time_with_fade_in_pcm_frames(pSound, absoluteGlobalTimeInFrames, 0); -} - -MA_API void ma_sound_set_stop_time_in_milliseconds(ma_sound* pSound, ma_uint64 absoluteGlobalTimeInMilliseconds) -{ - if (pSound == NULL) { - return; - } - - ma_sound_set_stop_time_in_pcm_frames(pSound, absoluteGlobalTimeInMilliseconds * ma_engine_get_sample_rate(ma_sound_get_engine(pSound)) / 1000); -} - -MA_API void ma_sound_set_stop_time_with_fade_in_pcm_frames(ma_sound* pSound, ma_uint64 stopAbsoluteGlobalTimeInFrames, ma_uint64 fadeLengthInFrames) -{ - if (pSound == NULL) { - return; - } - - if (fadeLengthInFrames > 0) { - if (fadeLengthInFrames > stopAbsoluteGlobalTimeInFrames) { - fadeLengthInFrames = stopAbsoluteGlobalTimeInFrames; - } - - ma_sound_set_fade_start_in_pcm_frames(pSound, -1, 0, fadeLengthInFrames, stopAbsoluteGlobalTimeInFrames - fadeLengthInFrames); - } - - ma_node_set_state_time(pSound, ma_node_state_stopped, stopAbsoluteGlobalTimeInFrames); -} - -MA_API void ma_sound_set_stop_time_with_fade_in_milliseconds(ma_sound* pSound, ma_uint64 stopAbsoluteGlobalTimeInMilliseconds, ma_uint64 fadeLengthInMilliseconds) -{ - ma_uint32 sampleRate; - - if (pSound == NULL) { - return; - } - - sampleRate = ma_engine_get_sample_rate(ma_sound_get_engine(pSound)); - - ma_sound_set_stop_time_with_fade_in_pcm_frames(pSound, (stopAbsoluteGlobalTimeInMilliseconds * sampleRate) / 1000, (fadeLengthInMilliseconds * sampleRate) / 1000); -} - -MA_API ma_bool32 ma_sound_is_playing(const ma_sound* pSound) -{ - if (pSound == NULL) { - return MA_FALSE; - } - - return ma_node_get_state_by_time(pSound, ma_engine_get_time_in_pcm_frames(ma_sound_get_engine(pSound))) == ma_node_state_started; -} - -MA_API ma_uint64 ma_sound_get_time_in_pcm_frames(const ma_sound* pSound) -{ - if (pSound == NULL) { - return 0; - } - - return ma_node_get_time(pSound); -} - -MA_API ma_uint64 ma_sound_get_time_in_milliseconds(const ma_sound* pSound) -{ - return ma_sound_get_time_in_pcm_frames(pSound) * 1000 / ma_engine_get_sample_rate(ma_sound_get_engine(pSound)); -} - -MA_API void ma_sound_set_looping(ma_sound* pSound, ma_bool32 isLooping) -{ - if (pSound == NULL) { - return; - } - - /* Looping is only a valid concept if the sound is backed by a data source. */ - if (pSound->pDataSource == NULL) { - return; - } - - /* The looping state needs to be applied to the data source in order for any looping to actually happen. */ - ma_data_source_set_looping(pSound->pDataSource, isLooping); -} - -MA_API ma_bool32 ma_sound_is_looping(const ma_sound* pSound) -{ - if (pSound == NULL) { - return MA_FALSE; - } - - /* There is no notion of looping for sounds that are not backed by a data source. */ - if (pSound->pDataSource == NULL) { - return MA_FALSE; - } - - return ma_data_source_is_looping(pSound->pDataSource); -} - -MA_API ma_bool32 ma_sound_at_end(const ma_sound* pSound) -{ - if (pSound == NULL) { - return MA_FALSE; - } - - /* There is no notion of an end of a sound if it's not backed by a data source. */ - if (pSound->pDataSource == NULL) { - return MA_FALSE; - } - - return ma_sound_get_at_end(pSound); -} - -MA_API ma_result ma_sound_seek_to_pcm_frame(ma_sound* pSound, ma_uint64 frameIndex) -{ - if (pSound == NULL) { - return MA_INVALID_ARGS; - } - - /* Seeking is only valid for sounds that are backed by a data source. */ - if (pSound->pDataSource == NULL) { - return MA_INVALID_OPERATION; - } - - /* We can't be seeking while reading at the same time. We just set the seek target and get the mixing thread to do the actual seek. */ - ma_atomic_exchange_64(&pSound->seekTarget, frameIndex); - - return MA_SUCCESS; -} - -MA_API ma_result ma_sound_seek_to_second(ma_sound* pSound, float seekPointInSeconds) -{ - ma_uint64 frameIndex; - ma_uint32 sampleRate; - ma_result result; - - if (pSound == NULL) { - return MA_INVALID_ARGS; - } - - result = ma_sound_get_data_format(pSound, NULL, NULL, &sampleRate, NULL, 0); - if (result != MA_SUCCESS) { - return result; - } - - /* We need PCM frames. We need to convert first */ - frameIndex = (ma_uint64)(seekPointInSeconds * sampleRate); - - return ma_sound_seek_to_pcm_frame(pSound, frameIndex); -} - -MA_API ma_result ma_sound_get_data_format(ma_sound* pSound, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate, ma_channel* pChannelMap, size_t channelMapCap) -{ - if (pSound == NULL) { - return MA_INVALID_ARGS; - } - - /* The data format is retrieved directly from the data source if the sound is backed by one. Otherwise we pull it from the node. */ - if (pSound->pDataSource == NULL) { - ma_uint32 channels; - - if (pFormat != NULL) { - *pFormat = ma_format_f32; - } - - channels = ma_node_get_input_channels(&pSound->engineNode, 0); - if (pChannels != NULL) { - *pChannels = channels; - } - - if (pSampleRate != NULL) { - *pSampleRate = pSound->engineNode.resampler.config.sampleRateIn; - } - - if (pChannelMap != NULL) { - ma_channel_map_init_standard(ma_standard_channel_map_default, pChannelMap, channelMapCap, channels); - } - - return MA_SUCCESS; - } else { - return ma_data_source_get_data_format(pSound->pDataSource, pFormat, pChannels, pSampleRate, pChannelMap, channelMapCap); - } -} - -MA_API ma_result ma_sound_get_cursor_in_pcm_frames(ma_sound* pSound, ma_uint64* pCursor) -{ - ma_uint64 seekTarget; - - if (pSound == NULL) { - return MA_INVALID_ARGS; - } - - /* The notion of a cursor is only valid for sounds that are backed by a data source. */ - if (pSound->pDataSource == NULL) { - return MA_INVALID_OPERATION; - } - - seekTarget = ma_atomic_load_64(&pSound->seekTarget); - if (seekTarget != MA_SEEK_TARGET_NONE) { - *pCursor = seekTarget; - return MA_SUCCESS; - } else { - return ma_data_source_get_cursor_in_pcm_frames(pSound->pDataSource, pCursor); - } -} - -MA_API ma_result ma_sound_get_length_in_pcm_frames(ma_sound* pSound, ma_uint64* pLength) -{ - if (pSound == NULL) { - return MA_INVALID_ARGS; - } - - /* The notion of a sound length is only valid for sounds that are backed by a data source. */ - if (pSound->pDataSource == NULL) { - return MA_INVALID_OPERATION; - } - - return ma_data_source_get_length_in_pcm_frames(pSound->pDataSource, pLength); -} - -MA_API ma_result ma_sound_get_cursor_in_seconds(ma_sound* pSound, float* pCursor) -{ - ma_result result; - ma_uint64 cursorInPCMFrames; - ma_uint32 sampleRate; - - if (pCursor != NULL) { - *pCursor = 0; - } - - result = ma_sound_get_cursor_in_pcm_frames(pSound, &cursorInPCMFrames); - if (result != MA_SUCCESS) { - return result; - } - - result = ma_sound_get_data_format(pSound, NULL, NULL, &sampleRate, NULL, 0); - if (result != MA_SUCCESS) { - return result; - } - - /* VC6 does not support division of unsigned 64-bit integers with floating point numbers. Need to use a signed number. This shouldn't effect anything in practice. */ - *pCursor = (ma_int64)cursorInPCMFrames / (float)sampleRate; - - return MA_SUCCESS; -} - -MA_API ma_result ma_sound_get_length_in_seconds(ma_sound* pSound, float* pLength) -{ - if (pSound == NULL) { - return MA_INVALID_ARGS; - } - - /* The notion of a sound length is only valid for sounds that are backed by a data source. */ - if (pSound->pDataSource == NULL) { - return MA_INVALID_OPERATION; - } - - return ma_data_source_get_length_in_seconds(pSound->pDataSource, pLength); -} - -MA_API ma_result ma_sound_set_end_callback(ma_sound* pSound, ma_sound_end_proc callback, void* pUserData) -{ - if (pSound == NULL) { - return MA_INVALID_ARGS; - } - - /* The notion of an end is only valid for sounds that are backed by a data source. */ - if (pSound->pDataSource == NULL) { - return MA_INVALID_OPERATION; - } - - pSound->endCallback = callback; - pSound->pEndCallbackUserData = pUserData; - - return MA_SUCCESS; -} - - -MA_API ma_result ma_sound_group_init(ma_engine* pEngine, ma_uint32 flags, ma_sound_group* pParentGroup, ma_sound_group* pGroup) -{ - ma_sound_group_config config = ma_sound_group_config_init_2(pEngine); - config.flags = flags; - config.pInitialAttachment = pParentGroup; - return ma_sound_group_init_ex(pEngine, &config, pGroup); -} - -MA_API ma_result ma_sound_group_init_ex(ma_engine* pEngine, const ma_sound_group_config* pConfig, ma_sound_group* pGroup) -{ - ma_sound_config soundConfig; - - if (pGroup == NULL) { - return MA_INVALID_ARGS; - } - - MA_ZERO_OBJECT(pGroup); - - if (pConfig == NULL) { - return MA_INVALID_ARGS; - } - - /* A sound group is just a sound without a data source. */ - soundConfig = *pConfig; - soundConfig.pFilePath = NULL; - soundConfig.pFilePathW = NULL; - soundConfig.pDataSource = NULL; - - /* - Groups need to have spatialization disabled by default because I think it'll be pretty rare - that programs will want to spatialize groups (but not unheard of). Certainly it feels like - disabling this by default feels like the right option. Spatialization can be enabled with a - call to ma_sound_group_set_spatialization_enabled(). - */ - soundConfig.flags |= MA_SOUND_FLAG_NO_SPATIALIZATION; - - return ma_sound_init_ex(pEngine, &soundConfig, pGroup); -} - -MA_API void ma_sound_group_uninit(ma_sound_group* pGroup) -{ - ma_sound_uninit(pGroup); -} - -MA_API ma_engine* ma_sound_group_get_engine(const ma_sound_group* pGroup) -{ - return ma_sound_get_engine(pGroup); -} - -MA_API ma_result ma_sound_group_start(ma_sound_group* pGroup) -{ - return ma_sound_start(pGroup); -} - -MA_API ma_result ma_sound_group_stop(ma_sound_group* pGroup) -{ - return ma_sound_stop(pGroup); -} - -MA_API void ma_sound_group_set_volume(ma_sound_group* pGroup, float volume) -{ - ma_sound_set_volume(pGroup, volume); -} - -MA_API float ma_sound_group_get_volume(const ma_sound_group* pGroup) -{ - return ma_sound_get_volume(pGroup); -} - -MA_API void ma_sound_group_set_pan(ma_sound_group* pGroup, float pan) -{ - ma_sound_set_pan(pGroup, pan); -} - -MA_API float ma_sound_group_get_pan(const ma_sound_group* pGroup) -{ - return ma_sound_get_pan(pGroup); -} - -MA_API void ma_sound_group_set_pan_mode(ma_sound_group* pGroup, ma_pan_mode panMode) -{ - ma_sound_set_pan_mode(pGroup, panMode); -} - -MA_API ma_pan_mode ma_sound_group_get_pan_mode(const ma_sound_group* pGroup) -{ - return ma_sound_get_pan_mode(pGroup); -} - -MA_API void ma_sound_group_set_pitch(ma_sound_group* pGroup, float pitch) -{ - ma_sound_set_pitch(pGroup, pitch); -} - -MA_API float ma_sound_group_get_pitch(const ma_sound_group* pGroup) -{ - return ma_sound_get_pitch(pGroup); -} - -MA_API void ma_sound_group_set_spatialization_enabled(ma_sound_group* pGroup, ma_bool32 enabled) -{ - ma_sound_set_spatialization_enabled(pGroup, enabled); -} - -MA_API ma_bool32 ma_sound_group_is_spatialization_enabled(const ma_sound_group* pGroup) -{ - return ma_sound_is_spatialization_enabled(pGroup); -} - -MA_API void ma_sound_group_set_pinned_listener_index(ma_sound_group* pGroup, ma_uint32 listenerIndex) -{ - ma_sound_set_pinned_listener_index(pGroup, listenerIndex); -} - -MA_API ma_uint32 ma_sound_group_get_pinned_listener_index(const ma_sound_group* pGroup) -{ - return ma_sound_get_pinned_listener_index(pGroup); -} - -MA_API ma_uint32 ma_sound_group_get_listener_index(const ma_sound_group* pGroup) -{ - return ma_sound_get_listener_index(pGroup); -} - -MA_API ma_vec3f ma_sound_group_get_direction_to_listener(const ma_sound_group* pGroup) -{ - return ma_sound_get_direction_to_listener(pGroup); -} - -MA_API void ma_sound_group_set_position(ma_sound_group* pGroup, float x, float y, float z) -{ - ma_sound_set_position(pGroup, x, y, z); -} - -MA_API ma_vec3f ma_sound_group_get_position(const ma_sound_group* pGroup) -{ - return ma_sound_get_position(pGroup); -} - -MA_API void ma_sound_group_set_direction(ma_sound_group* pGroup, float x, float y, float z) -{ - ma_sound_set_direction(pGroup, x, y, z); -} - -MA_API ma_vec3f ma_sound_group_get_direction(const ma_sound_group* pGroup) -{ - return ma_sound_get_direction(pGroup); -} - -MA_API void ma_sound_group_set_velocity(ma_sound_group* pGroup, float x, float y, float z) -{ - ma_sound_set_velocity(pGroup, x, y, z); -} - -MA_API ma_vec3f ma_sound_group_get_velocity(const ma_sound_group* pGroup) -{ - return ma_sound_get_velocity(pGroup); -} - -MA_API void ma_sound_group_set_attenuation_model(ma_sound_group* pGroup, ma_attenuation_model attenuationModel) -{ - ma_sound_set_attenuation_model(pGroup, attenuationModel); -} - -MA_API ma_attenuation_model ma_sound_group_get_attenuation_model(const ma_sound_group* pGroup) -{ - return ma_sound_get_attenuation_model(pGroup); -} - -MA_API void ma_sound_group_set_positioning(ma_sound_group* pGroup, ma_positioning positioning) -{ - ma_sound_set_positioning(pGroup, positioning); -} - -MA_API ma_positioning ma_sound_group_get_positioning(const ma_sound_group* pGroup) -{ - return ma_sound_get_positioning(pGroup); -} - -MA_API void ma_sound_group_set_rolloff(ma_sound_group* pGroup, float rolloff) -{ - ma_sound_set_rolloff(pGroup, rolloff); -} - -MA_API float ma_sound_group_get_rolloff(const ma_sound_group* pGroup) -{ - return ma_sound_get_rolloff(pGroup); -} - -MA_API void ma_sound_group_set_min_gain(ma_sound_group* pGroup, float minGain) -{ - ma_sound_set_min_gain(pGroup, minGain); -} - -MA_API float ma_sound_group_get_min_gain(const ma_sound_group* pGroup) -{ - return ma_sound_get_min_gain(pGroup); -} - -MA_API void ma_sound_group_set_max_gain(ma_sound_group* pGroup, float maxGain) -{ - ma_sound_set_max_gain(pGroup, maxGain); -} - -MA_API float ma_sound_group_get_max_gain(const ma_sound_group* pGroup) -{ - return ma_sound_get_max_gain(pGroup); -} - -MA_API void ma_sound_group_set_min_distance(ma_sound_group* pGroup, float minDistance) -{ - ma_sound_set_min_distance(pGroup, minDistance); -} - -MA_API float ma_sound_group_get_min_distance(const ma_sound_group* pGroup) -{ - return ma_sound_get_min_distance(pGroup); -} - -MA_API void ma_sound_group_set_max_distance(ma_sound_group* pGroup, float maxDistance) -{ - ma_sound_set_max_distance(pGroup, maxDistance); -} - -MA_API float ma_sound_group_get_max_distance(const ma_sound_group* pGroup) -{ - return ma_sound_get_max_distance(pGroup); -} - -MA_API void ma_sound_group_set_cone(ma_sound_group* pGroup, float innerAngleInRadians, float outerAngleInRadians, float outerGain) -{ - ma_sound_set_cone(pGroup, innerAngleInRadians, outerAngleInRadians, outerGain); -} - -MA_API void ma_sound_group_get_cone(const ma_sound_group* pGroup, float* pInnerAngleInRadians, float* pOuterAngleInRadians, float* pOuterGain) -{ - ma_sound_get_cone(pGroup, pInnerAngleInRadians, pOuterAngleInRadians, pOuterGain); -} - -MA_API void ma_sound_group_set_doppler_factor(ma_sound_group* pGroup, float dopplerFactor) -{ - ma_sound_set_doppler_factor(pGroup, dopplerFactor); -} - -MA_API float ma_sound_group_get_doppler_factor(const ma_sound_group* pGroup) -{ - return ma_sound_get_doppler_factor(pGroup); -} - -MA_API void ma_sound_group_set_directional_attenuation_factor(ma_sound_group* pGroup, float directionalAttenuationFactor) -{ - ma_sound_set_directional_attenuation_factor(pGroup, directionalAttenuationFactor); -} - -MA_API float ma_sound_group_get_directional_attenuation_factor(const ma_sound_group* pGroup) -{ - return ma_sound_get_directional_attenuation_factor(pGroup); -} - -MA_API void ma_sound_group_set_fade_in_pcm_frames(ma_sound_group* pGroup, float volumeBeg, float volumeEnd, ma_uint64 fadeLengthInFrames) -{ - ma_sound_set_fade_in_pcm_frames(pGroup, volumeBeg, volumeEnd, fadeLengthInFrames); -} - -MA_API void ma_sound_group_set_fade_in_milliseconds(ma_sound_group* pGroup, float volumeBeg, float volumeEnd, ma_uint64 fadeLengthInMilliseconds) -{ - ma_sound_set_fade_in_milliseconds(pGroup, volumeBeg, volumeEnd, fadeLengthInMilliseconds); -} - -MA_API float ma_sound_group_get_current_fade_volume(ma_sound_group* pGroup) -{ - return ma_sound_get_current_fade_volume(pGroup); -} - -MA_API void ma_sound_group_set_start_time_in_pcm_frames(ma_sound_group* pGroup, ma_uint64 absoluteGlobalTimeInFrames) -{ - ma_sound_set_start_time_in_pcm_frames(pGroup, absoluteGlobalTimeInFrames); -} - -MA_API void ma_sound_group_set_start_time_in_milliseconds(ma_sound_group* pGroup, ma_uint64 absoluteGlobalTimeInMilliseconds) -{ - ma_sound_set_start_time_in_milliseconds(pGroup, absoluteGlobalTimeInMilliseconds); -} - -MA_API void ma_sound_group_set_stop_time_in_pcm_frames(ma_sound_group* pGroup, ma_uint64 absoluteGlobalTimeInFrames) -{ - ma_sound_set_stop_time_in_pcm_frames(pGroup, absoluteGlobalTimeInFrames); -} - -MA_API void ma_sound_group_set_stop_time_in_milliseconds(ma_sound_group* pGroup, ma_uint64 absoluteGlobalTimeInMilliseconds) -{ - ma_sound_set_stop_time_in_milliseconds(pGroup, absoluteGlobalTimeInMilliseconds); -} - -MA_API ma_bool32 ma_sound_group_is_playing(const ma_sound_group* pGroup) -{ - return ma_sound_is_playing(pGroup); -} - -MA_API ma_uint64 ma_sound_group_get_time_in_pcm_frames(const ma_sound_group* pGroup) -{ - return ma_sound_get_time_in_pcm_frames(pGroup); -} -#endif /* MA_NO_ENGINE */ -/* END SECTION: miniaudio_engine.c */ - - - -/************************************************************************************************************************************************************** -*************************************************************************************************************************************************************** - -Auto Generated -============== -All code below is auto-generated from a tool. This mostly consists of decoding backend implementations such as ma_dr_wav, ma_dr_flac, etc. If you find a bug in the -code below please report the bug to the respective repository for the relevant project (probably dr_libs). - -*************************************************************************************************************************************************************** -**************************************************************************************************************************************************************/ -#if !defined(MA_NO_WAV) && (!defined(MA_NO_DECODING) || !defined(MA_NO_ENCODING)) -#if !defined(MA_DR_WAV_IMPLEMENTATION) -/* dr_wav_c begin */ -#ifndef ma_dr_wav_c -#define ma_dr_wav_c -#ifdef __MRC__ -#pragma options opt off -#endif -#include -#include -#include -#ifndef MA_DR_WAV_NO_STDIO -#include -#ifndef MA_DR_WAV_NO_WCHAR -#include -#endif -#endif -#ifndef MA_DR_WAV_ASSERT -#include -#define MA_DR_WAV_ASSERT(expression) assert(expression) -#endif -#ifndef MA_DR_WAV_MALLOC -#define MA_DR_WAV_MALLOC(sz) malloc((sz)) -#endif -#ifndef MA_DR_WAV_REALLOC -#define MA_DR_WAV_REALLOC(p, sz) realloc((p), (sz)) -#endif -#ifndef MA_DR_WAV_FREE -#define MA_DR_WAV_FREE(p) free((p)) -#endif -#ifndef MA_DR_WAV_COPY_MEMORY -#define MA_DR_WAV_COPY_MEMORY(dst, src, sz) memcpy((dst), (src), (sz)) -#endif -#ifndef MA_DR_WAV_ZERO_MEMORY -#define MA_DR_WAV_ZERO_MEMORY(p, sz) memset((p), 0, (sz)) -#endif -#ifndef MA_DR_WAV_ZERO_OBJECT -#define MA_DR_WAV_ZERO_OBJECT(p) MA_DR_WAV_ZERO_MEMORY((p), sizeof(*p)) -#endif -#define ma_dr_wav_countof(x) (sizeof(x) / sizeof(x[0])) -#define ma_dr_wav_align(x, a) ((((x) + (a) - 1) / (a)) * (a)) -#define ma_dr_wav_min(a, b) (((a) < (b)) ? (a) : (b)) -#define ma_dr_wav_max(a, b) (((a) > (b)) ? (a) : (b)) -#define ma_dr_wav_clamp(x, lo, hi) (ma_dr_wav_max((lo), ma_dr_wav_min((hi), (x)))) -#define ma_dr_wav_offset_ptr(p, offset) (((ma_uint8*)(p)) + (offset)) -#define MA_DR_WAV_MAX_SIMD_VECTOR_SIZE 32 -#define MA_DR_WAV_INT64_MIN ((ma_int64) ((ma_uint64)0x80000000 << 32)) -#define MA_DR_WAV_INT64_MAX ((ma_int64)(((ma_uint64)0x7FFFFFFF << 32) | 0xFFFFFFFF)) -#if defined(_MSC_VER) && _MSC_VER >= 1400 - #define MA_DR_WAV_HAS_BYTESWAP16_INTRINSIC - #define MA_DR_WAV_HAS_BYTESWAP32_INTRINSIC - #define MA_DR_WAV_HAS_BYTESWAP64_INTRINSIC -#elif defined(__clang__) - #if defined(__has_builtin) - #if __has_builtin(__builtin_bswap16) - #define MA_DR_WAV_HAS_BYTESWAP16_INTRINSIC - #endif - #if __has_builtin(__builtin_bswap32) - #define MA_DR_WAV_HAS_BYTESWAP32_INTRINSIC - #endif - #if __has_builtin(__builtin_bswap64) - #define MA_DR_WAV_HAS_BYTESWAP64_INTRINSIC - #endif - #endif -#elif defined(__GNUC__) - #if ((__GNUC__ > 4) || (__GNUC__ == 4 && __GNUC_MINOR__ >= 3)) - #define MA_DR_WAV_HAS_BYTESWAP32_INTRINSIC - #define MA_DR_WAV_HAS_BYTESWAP64_INTRINSIC - #endif - #if ((__GNUC__ > 4) || (__GNUC__ == 4 && __GNUC_MINOR__ >= 8)) - #define MA_DR_WAV_HAS_BYTESWAP16_INTRINSIC - #endif -#endif -MA_API void ma_dr_wav_version(ma_uint32* pMajor, ma_uint32* pMinor, ma_uint32* pRevision) -{ - if (pMajor) { - *pMajor = MA_DR_WAV_VERSION_MAJOR; - } - if (pMinor) { - *pMinor = MA_DR_WAV_VERSION_MINOR; - } - if (pRevision) { - *pRevision = MA_DR_WAV_VERSION_REVISION; - } -} -MA_API const char* ma_dr_wav_version_string(void) -{ - return MA_DR_WAV_VERSION_STRING; -} -#ifndef MA_DR_WAV_MAX_SAMPLE_RATE -#define MA_DR_WAV_MAX_SAMPLE_RATE 384000 -#endif -#ifndef MA_DR_WAV_MAX_CHANNELS -#define MA_DR_WAV_MAX_CHANNELS 256 -#endif -#ifndef MA_DR_WAV_MAX_BITS_PER_SAMPLE -#define MA_DR_WAV_MAX_BITS_PER_SAMPLE 64 -#endif -static const ma_uint8 ma_dr_wavGUID_W64_RIFF[16] = {0x72,0x69,0x66,0x66, 0x2E,0x91, 0xCF,0x11, 0xA5,0xD6, 0x28,0xDB,0x04,0xC1,0x00,0x00}; -static const ma_uint8 ma_dr_wavGUID_W64_WAVE[16] = {0x77,0x61,0x76,0x65, 0xF3,0xAC, 0xD3,0x11, 0x8C,0xD1, 0x00,0xC0,0x4F,0x8E,0xDB,0x8A}; -static const ma_uint8 ma_dr_wavGUID_W64_FMT [16] = {0x66,0x6D,0x74,0x20, 0xF3,0xAC, 0xD3,0x11, 0x8C,0xD1, 0x00,0xC0,0x4F,0x8E,0xDB,0x8A}; -static const ma_uint8 ma_dr_wavGUID_W64_FACT[16] = {0x66,0x61,0x63,0x74, 0xF3,0xAC, 0xD3,0x11, 0x8C,0xD1, 0x00,0xC0,0x4F,0x8E,0xDB,0x8A}; -static const ma_uint8 ma_dr_wavGUID_W64_DATA[16] = {0x64,0x61,0x74,0x61, 0xF3,0xAC, 0xD3,0x11, 0x8C,0xD1, 0x00,0xC0,0x4F,0x8E,0xDB,0x8A}; -static MA_INLINE int ma_dr_wav__is_little_endian(void) -{ -#if defined(MA_X86) || defined(MA_X64) - return MA_TRUE; -#elif defined(__BYTE_ORDER) && defined(__LITTLE_ENDIAN) && __BYTE_ORDER == __LITTLE_ENDIAN - return MA_TRUE; -#else - int n = 1; - return (*(char*)&n) == 1; -#endif -} -static MA_INLINE void ma_dr_wav_bytes_to_guid(const ma_uint8* data, ma_uint8* guid) -{ - int i; - for (i = 0; i < 16; ++i) { - guid[i] = data[i]; - } -} -static MA_INLINE ma_uint16 ma_dr_wav__bswap16(ma_uint16 n) -{ -#ifdef MA_DR_WAV_HAS_BYTESWAP16_INTRINSIC - #if defined(_MSC_VER) - return _byteswap_ushort(n); - #elif defined(__GNUC__) || defined(__clang__) - return __builtin_bswap16(n); - #else - #error "This compiler does not support the byte swap intrinsic." - #endif -#else - return ((n & 0xFF00) >> 8) | - ((n & 0x00FF) << 8); -#endif -} -static MA_INLINE ma_uint32 ma_dr_wav__bswap32(ma_uint32 n) -{ -#ifdef MA_DR_WAV_HAS_BYTESWAP32_INTRINSIC - #if defined(_MSC_VER) - return _byteswap_ulong(n); - #elif defined(__GNUC__) || defined(__clang__) - #if defined(MA_ARM) && (defined(__ARM_ARCH) && __ARM_ARCH >= 6) && !defined(MA_64BIT) - ma_uint32 r; - __asm__ __volatile__ ( - #if defined(MA_64BIT) - "rev %w[out], %w[in]" : [out]"=r"(r) : [in]"r"(n) - #else - "rev %[out], %[in]" : [out]"=r"(r) : [in]"r"(n) - #endif - ); - return r; - #else - return __builtin_bswap32(n); - #endif - #else - #error "This compiler does not support the byte swap intrinsic." - #endif -#else - return ((n & 0xFF000000) >> 24) | - ((n & 0x00FF0000) >> 8) | - ((n & 0x0000FF00) << 8) | - ((n & 0x000000FF) << 24); -#endif -} -static MA_INLINE ma_uint64 ma_dr_wav__bswap64(ma_uint64 n) -{ -#ifdef MA_DR_WAV_HAS_BYTESWAP64_INTRINSIC - #if defined(_MSC_VER) - return _byteswap_uint64(n); - #elif defined(__GNUC__) || defined(__clang__) - return __builtin_bswap64(n); - #else - #error "This compiler does not support the byte swap intrinsic." - #endif -#else - return ((n & ((ma_uint64)0xFF000000 << 32)) >> 56) | - ((n & ((ma_uint64)0x00FF0000 << 32)) >> 40) | - ((n & ((ma_uint64)0x0000FF00 << 32)) >> 24) | - ((n & ((ma_uint64)0x000000FF << 32)) >> 8) | - ((n & ((ma_uint64)0xFF000000 )) << 8) | - ((n & ((ma_uint64)0x00FF0000 )) << 24) | - ((n & ((ma_uint64)0x0000FF00 )) << 40) | - ((n & ((ma_uint64)0x000000FF )) << 56); -#endif -} -static MA_INLINE ma_int16 ma_dr_wav__bswap_s16(ma_int16 n) -{ - return (ma_int16)ma_dr_wav__bswap16((ma_uint16)n); -} -static MA_INLINE void ma_dr_wav__bswap_samples_s16(ma_int16* pSamples, ma_uint64 sampleCount) -{ - ma_uint64 iSample; - for (iSample = 0; iSample < sampleCount; iSample += 1) { - pSamples[iSample] = ma_dr_wav__bswap_s16(pSamples[iSample]); - } -} -static MA_INLINE void ma_dr_wav__bswap_s24(ma_uint8* p) -{ - ma_uint8 t; - t = p[0]; - p[0] = p[2]; - p[2] = t; -} -static MA_INLINE void ma_dr_wav__bswap_samples_s24(ma_uint8* pSamples, ma_uint64 sampleCount) -{ - ma_uint64 iSample; - for (iSample = 0; iSample < sampleCount; iSample += 1) { - ma_uint8* pSample = pSamples + (iSample*3); - ma_dr_wav__bswap_s24(pSample); - } -} -static MA_INLINE ma_int32 ma_dr_wav__bswap_s32(ma_int32 n) -{ - return (ma_int32)ma_dr_wav__bswap32((ma_uint32)n); -} -static MA_INLINE void ma_dr_wav__bswap_samples_s32(ma_int32* pSamples, ma_uint64 sampleCount) -{ - ma_uint64 iSample; - for (iSample = 0; iSample < sampleCount; iSample += 1) { - pSamples[iSample] = ma_dr_wav__bswap_s32(pSamples[iSample]); - } -} -static MA_INLINE ma_int64 ma_dr_wav__bswap_s64(ma_int64 n) -{ - return (ma_int64)ma_dr_wav__bswap64((ma_uint64)n); -} -static MA_INLINE void ma_dr_wav__bswap_samples_s64(ma_int64* pSamples, ma_uint64 sampleCount) -{ - ma_uint64 iSample; - for (iSample = 0; iSample < sampleCount; iSample += 1) { - pSamples[iSample] = ma_dr_wav__bswap_s64(pSamples[iSample]); - } -} -static MA_INLINE float ma_dr_wav__bswap_f32(float n) -{ - union { - ma_uint32 i; - float f; - } x; - x.f = n; - x.i = ma_dr_wav__bswap32(x.i); - return x.f; -} -static MA_INLINE void ma_dr_wav__bswap_samples_f32(float* pSamples, ma_uint64 sampleCount) -{ - ma_uint64 iSample; - for (iSample = 0; iSample < sampleCount; iSample += 1) { - pSamples[iSample] = ma_dr_wav__bswap_f32(pSamples[iSample]); - } -} -static MA_INLINE void ma_dr_wav__bswap_samples(void* pSamples, ma_uint64 sampleCount, ma_uint32 bytesPerSample) -{ - switch (bytesPerSample) - { - case 1: - { - } break; - case 2: - { - ma_dr_wav__bswap_samples_s16((ma_int16*)pSamples, sampleCount); - } break; - case 3: - { - ma_dr_wav__bswap_samples_s24((ma_uint8*)pSamples, sampleCount); - } break; - case 4: - { - ma_dr_wav__bswap_samples_s32((ma_int32*)pSamples, sampleCount); - } break; - case 8: - { - ma_dr_wav__bswap_samples_s64((ma_int64*)pSamples, sampleCount); - } break; - default: - { - MA_DR_WAV_ASSERT(MA_FALSE); - } break; - } -} -MA_PRIVATE MA_INLINE ma_bool32 ma_dr_wav_is_container_be(ma_dr_wav_container container) -{ - if (container == ma_dr_wav_container_rifx || container == ma_dr_wav_container_aiff) { - return MA_TRUE; - } else { - return MA_FALSE; - } -} -MA_PRIVATE MA_INLINE ma_uint16 ma_dr_wav_bytes_to_u16_le(const ma_uint8* data) -{ - return ((ma_uint16)data[0] << 0) | ((ma_uint16)data[1] << 8); -} -MA_PRIVATE MA_INLINE ma_uint16 ma_dr_wav_bytes_to_u16_be(const ma_uint8* data) -{ - return ((ma_uint16)data[1] << 0) | ((ma_uint16)data[0] << 8); -} -MA_PRIVATE MA_INLINE ma_uint16 ma_dr_wav_bytes_to_u16_ex(const ma_uint8* data, ma_dr_wav_container container) -{ - if (ma_dr_wav_is_container_be(container)) { - return ma_dr_wav_bytes_to_u16_be(data); - } else { - return ma_dr_wav_bytes_to_u16_le(data); - } -} -MA_PRIVATE MA_INLINE ma_uint32 ma_dr_wav_bytes_to_u32_le(const ma_uint8* data) -{ - return ((ma_uint32)data[0] << 0) | ((ma_uint32)data[1] << 8) | ((ma_uint32)data[2] << 16) | ((ma_uint32)data[3] << 24); -} -MA_PRIVATE MA_INLINE ma_uint32 ma_dr_wav_bytes_to_u32_be(const ma_uint8* data) -{ - return ((ma_uint32)data[3] << 0) | ((ma_uint32)data[2] << 8) | ((ma_uint32)data[1] << 16) | ((ma_uint32)data[0] << 24); -} -MA_PRIVATE MA_INLINE ma_uint32 ma_dr_wav_bytes_to_u32_ex(const ma_uint8* data, ma_dr_wav_container container) -{ - if (ma_dr_wav_is_container_be(container)) { - return ma_dr_wav_bytes_to_u32_be(data); - } else { - return ma_dr_wav_bytes_to_u32_le(data); - } -} -MA_PRIVATE ma_int64 ma_dr_wav_aiff_extented_to_s64(const ma_uint8* data) -{ - ma_uint32 exponent = ((ma_uint32)data[0] << 8) | data[1]; - ma_uint64 hi = ((ma_uint64)data[2] << 24) | ((ma_uint64)data[3] << 16) | ((ma_uint64)data[4] << 8) | ((ma_uint64)data[5] << 0); - ma_uint64 lo = ((ma_uint64)data[6] << 24) | ((ma_uint64)data[7] << 16) | ((ma_uint64)data[8] << 8) | ((ma_uint64)data[9] << 0); - ma_uint64 significand = (hi << 32) | lo; - int sign = exponent >> 15; - exponent &= 0x7FFF; - if (exponent == 0 && significand == 0) { - return 0; - } else if (exponent == 0x7FFF) { - return sign ? MA_DR_WAV_INT64_MIN : MA_DR_WAV_INT64_MAX; - } - exponent -= 16383; - if (exponent > 63) { - return sign ? MA_DR_WAV_INT64_MIN : MA_DR_WAV_INT64_MAX; - } else if (exponent < 1) { - return 0; - } - significand >>= (63 - exponent); - if (sign) { - return -(ma_int64)significand; - } else { - return (ma_int64)significand; - } -} -MA_PRIVATE void* ma_dr_wav__malloc_default(size_t sz, void* pUserData) -{ - (void)pUserData; - return MA_DR_WAV_MALLOC(sz); -} -MA_PRIVATE void* ma_dr_wav__realloc_default(void* p, size_t sz, void* pUserData) -{ - (void)pUserData; - return MA_DR_WAV_REALLOC(p, sz); -} -MA_PRIVATE void ma_dr_wav__free_default(void* p, void* pUserData) -{ - (void)pUserData; - MA_DR_WAV_FREE(p); -} -MA_PRIVATE void* ma_dr_wav__malloc_from_callbacks(size_t sz, const ma_allocation_callbacks* pAllocationCallbacks) -{ - if (pAllocationCallbacks == NULL) { - return NULL; - } - if (pAllocationCallbacks->onMalloc != NULL) { - return pAllocationCallbacks->onMalloc(sz, pAllocationCallbacks->pUserData); - } - if (pAllocationCallbacks->onRealloc != NULL) { - return pAllocationCallbacks->onRealloc(NULL, sz, pAllocationCallbacks->pUserData); - } - return NULL; -} -MA_PRIVATE void* ma_dr_wav__realloc_from_callbacks(void* p, size_t szNew, size_t szOld, const ma_allocation_callbacks* pAllocationCallbacks) -{ - if (pAllocationCallbacks == NULL) { - return NULL; - } - if (pAllocationCallbacks->onRealloc != NULL) { - return pAllocationCallbacks->onRealloc(p, szNew, pAllocationCallbacks->pUserData); - } - if (pAllocationCallbacks->onMalloc != NULL && pAllocationCallbacks->onFree != NULL) { - void* p2; - p2 = pAllocationCallbacks->onMalloc(szNew, pAllocationCallbacks->pUserData); - if (p2 == NULL) { - return NULL; - } - if (p != NULL) { - MA_DR_WAV_COPY_MEMORY(p2, p, szOld); - pAllocationCallbacks->onFree(p, pAllocationCallbacks->pUserData); - } - return p2; - } - return NULL; -} -MA_PRIVATE void ma_dr_wav__free_from_callbacks(void* p, const ma_allocation_callbacks* pAllocationCallbacks) -{ - if (p == NULL || pAllocationCallbacks == NULL) { - return; - } - if (pAllocationCallbacks->onFree != NULL) { - pAllocationCallbacks->onFree(p, pAllocationCallbacks->pUserData); - } -} -MA_PRIVATE ma_allocation_callbacks ma_dr_wav_copy_allocation_callbacks_or_defaults(const ma_allocation_callbacks* pAllocationCallbacks) -{ - if (pAllocationCallbacks != NULL) { - return *pAllocationCallbacks; - } else { - ma_allocation_callbacks allocationCallbacks; - allocationCallbacks.pUserData = NULL; - allocationCallbacks.onMalloc = ma_dr_wav__malloc_default; - allocationCallbacks.onRealloc = ma_dr_wav__realloc_default; - allocationCallbacks.onFree = ma_dr_wav__free_default; - return allocationCallbacks; - } -} -static MA_INLINE ma_bool32 ma_dr_wav__is_compressed_format_tag(ma_uint16 formatTag) -{ - return - formatTag == MA_DR_WAVE_FORMAT_ADPCM || - formatTag == MA_DR_WAVE_FORMAT_DVI_ADPCM; -} -MA_PRIVATE unsigned int ma_dr_wav__chunk_padding_size_riff(ma_uint64 chunkSize) -{ - return (unsigned int)(chunkSize % 2); -} -MA_PRIVATE unsigned int ma_dr_wav__chunk_padding_size_w64(ma_uint64 chunkSize) -{ - return (unsigned int)(chunkSize % 8); -} -MA_PRIVATE ma_uint64 ma_dr_wav_read_pcm_frames_s16__msadpcm(ma_dr_wav* pWav, ma_uint64 samplesToRead, ma_int16* pBufferOut); -MA_PRIVATE ma_uint64 ma_dr_wav_read_pcm_frames_s16__ima(ma_dr_wav* pWav, ma_uint64 samplesToRead, ma_int16* pBufferOut); -MA_PRIVATE ma_bool32 ma_dr_wav_init_write__internal(ma_dr_wav* pWav, const ma_dr_wav_data_format* pFormat, ma_uint64 totalSampleCount); -MA_PRIVATE ma_result ma_dr_wav__read_chunk_header(ma_dr_wav_read_proc onRead, void* pUserData, ma_dr_wav_container container, ma_uint64* pRunningBytesReadOut, ma_dr_wav_chunk_header* pHeaderOut) -{ - if (container == ma_dr_wav_container_riff || container == ma_dr_wav_container_rifx || container == ma_dr_wav_container_rf64 || container == ma_dr_wav_container_aiff) { - ma_uint8 sizeInBytes[4]; - if (onRead(pUserData, pHeaderOut->id.fourcc, 4) != 4) { - return MA_AT_END; - } - if (onRead(pUserData, sizeInBytes, 4) != 4) { - return MA_INVALID_FILE; - } - pHeaderOut->sizeInBytes = ma_dr_wav_bytes_to_u32_ex(sizeInBytes, container); - pHeaderOut->paddingSize = ma_dr_wav__chunk_padding_size_riff(pHeaderOut->sizeInBytes); - *pRunningBytesReadOut += 8; - } else if (container == ma_dr_wav_container_w64) { - ma_uint8 sizeInBytes[8]; - if (onRead(pUserData, pHeaderOut->id.guid, 16) != 16) { - return MA_AT_END; - } - if (onRead(pUserData, sizeInBytes, 8) != 8) { - return MA_INVALID_FILE; - } - pHeaderOut->sizeInBytes = ma_dr_wav_bytes_to_u64(sizeInBytes) - 24; - pHeaderOut->paddingSize = ma_dr_wav__chunk_padding_size_w64(pHeaderOut->sizeInBytes); - *pRunningBytesReadOut += 24; - } else { - return MA_INVALID_FILE; - } - return MA_SUCCESS; -} -MA_PRIVATE ma_bool32 ma_dr_wav__seek_forward(ma_dr_wav_seek_proc onSeek, ma_uint64 offset, void* pUserData) -{ - ma_uint64 bytesRemainingToSeek = offset; - while (bytesRemainingToSeek > 0) { - if (bytesRemainingToSeek > 0x7FFFFFFF) { - if (!onSeek(pUserData, 0x7FFFFFFF, ma_dr_wav_seek_origin_current)) { - return MA_FALSE; - } - bytesRemainingToSeek -= 0x7FFFFFFF; - } else { - if (!onSeek(pUserData, (int)bytesRemainingToSeek, ma_dr_wav_seek_origin_current)) { - return MA_FALSE; - } - bytesRemainingToSeek = 0; - } - } - return MA_TRUE; -} -MA_PRIVATE ma_bool32 ma_dr_wav__seek_from_start(ma_dr_wav_seek_proc onSeek, ma_uint64 offset, void* pUserData) -{ - if (offset <= 0x7FFFFFFF) { - return onSeek(pUserData, (int)offset, ma_dr_wav_seek_origin_start); - } - if (!onSeek(pUserData, 0x7FFFFFFF, ma_dr_wav_seek_origin_start)) { - return MA_FALSE; - } - offset -= 0x7FFFFFFF; - for (;;) { - if (offset <= 0x7FFFFFFF) { - return onSeek(pUserData, (int)offset, ma_dr_wav_seek_origin_current); - } - if (!onSeek(pUserData, 0x7FFFFFFF, ma_dr_wav_seek_origin_current)) { - return MA_FALSE; - } - offset -= 0x7FFFFFFF; - } -} -MA_PRIVATE size_t ma_dr_wav__on_read(ma_dr_wav_read_proc onRead, void* pUserData, void* pBufferOut, size_t bytesToRead, ma_uint64* pCursor) -{ - size_t bytesRead; - MA_DR_WAV_ASSERT(onRead != NULL); - MA_DR_WAV_ASSERT(pCursor != NULL); - bytesRead = onRead(pUserData, pBufferOut, bytesToRead); - *pCursor += bytesRead; - return bytesRead; -} -#if 0 -MA_PRIVATE ma_bool32 ma_dr_wav__on_seek(ma_dr_wav_seek_proc onSeek, void* pUserData, int offset, ma_dr_wav_seek_origin origin, ma_uint64* pCursor) -{ - MA_DR_WAV_ASSERT(onSeek != NULL); - MA_DR_WAV_ASSERT(pCursor != NULL); - if (!onSeek(pUserData, offset, origin)) { - return MA_FALSE; - } - if (origin == ma_dr_wav_seek_origin_start) { - *pCursor = offset; - } else { - *pCursor += offset; - } - return MA_TRUE; -} -#endif -#define MA_DR_WAV_SMPL_BYTES 36 -#define MA_DR_WAV_SMPL_LOOP_BYTES 24 -#define MA_DR_WAV_INST_BYTES 7 -#define MA_DR_WAV_ACID_BYTES 24 -#define MA_DR_WAV_CUE_BYTES 4 -#define MA_DR_WAV_BEXT_BYTES 602 -#define MA_DR_WAV_BEXT_DESCRIPTION_BYTES 256 -#define MA_DR_WAV_BEXT_ORIGINATOR_NAME_BYTES 32 -#define MA_DR_WAV_BEXT_ORIGINATOR_REF_BYTES 32 -#define MA_DR_WAV_BEXT_RESERVED_BYTES 180 -#define MA_DR_WAV_BEXT_UMID_BYTES 64 -#define MA_DR_WAV_CUE_POINT_BYTES 24 -#define MA_DR_WAV_LIST_LABEL_OR_NOTE_BYTES 4 -#define MA_DR_WAV_LIST_LABELLED_TEXT_BYTES 20 -#define MA_DR_WAV_METADATA_ALIGNMENT 8 -typedef enum -{ - ma_dr_wav__metadata_parser_stage_count, - ma_dr_wav__metadata_parser_stage_read -} ma_dr_wav__metadata_parser_stage; -typedef struct -{ - ma_dr_wav_read_proc onRead; - ma_dr_wav_seek_proc onSeek; - void *pReadSeekUserData; - ma_dr_wav__metadata_parser_stage stage; - ma_dr_wav_metadata *pMetadata; - ma_uint32 metadataCount; - ma_uint8 *pData; - ma_uint8 *pDataCursor; - ma_uint64 metadataCursor; - ma_uint64 extraCapacity; -} ma_dr_wav__metadata_parser; -MA_PRIVATE size_t ma_dr_wav__metadata_memory_capacity(ma_dr_wav__metadata_parser* pParser) -{ - ma_uint64 cap = sizeof(ma_dr_wav_metadata) * (ma_uint64)pParser->metadataCount + pParser->extraCapacity; - if (cap > MA_SIZE_MAX) { - return 0; - } - return (size_t)cap; -} -MA_PRIVATE ma_uint8* ma_dr_wav__metadata_get_memory(ma_dr_wav__metadata_parser* pParser, size_t size, size_t align) -{ - ma_uint8* pResult; - if (align) { - ma_uintptr modulo = (ma_uintptr)pParser->pDataCursor % align; - if (modulo != 0) { - pParser->pDataCursor += align - modulo; - } - } - pResult = pParser->pDataCursor; - MA_DR_WAV_ASSERT((pResult + size) <= (pParser->pData + ma_dr_wav__metadata_memory_capacity(pParser))); - pParser->pDataCursor += size; - return pResult; -} -MA_PRIVATE void ma_dr_wav__metadata_request_extra_memory_for_stage_2(ma_dr_wav__metadata_parser* pParser, size_t bytes, size_t align) -{ - size_t extra = bytes + (align ? (align - 1) : 0); - pParser->extraCapacity += extra; -} -MA_PRIVATE ma_result ma_dr_wav__metadata_alloc(ma_dr_wav__metadata_parser* pParser, ma_allocation_callbacks* pAllocationCallbacks) -{ - if (pParser->extraCapacity != 0 || pParser->metadataCount != 0) { - pAllocationCallbacks->onFree(pParser->pData, pAllocationCallbacks->pUserData); - pParser->pData = (ma_uint8*)pAllocationCallbacks->onMalloc(ma_dr_wav__metadata_memory_capacity(pParser), pAllocationCallbacks->pUserData); - pParser->pDataCursor = pParser->pData; - if (pParser->pData == NULL) { - return MA_OUT_OF_MEMORY; - } - pParser->pMetadata = (ma_dr_wav_metadata*)ma_dr_wav__metadata_get_memory(pParser, sizeof(ma_dr_wav_metadata) * pParser->metadataCount, 1); - pParser->metadataCursor = 0; - } - return MA_SUCCESS; -} -MA_PRIVATE size_t ma_dr_wav__metadata_parser_read(ma_dr_wav__metadata_parser* pParser, void* pBufferOut, size_t bytesToRead, ma_uint64* pCursor) -{ - if (pCursor != NULL) { - return ma_dr_wav__on_read(pParser->onRead, pParser->pReadSeekUserData, pBufferOut, bytesToRead, pCursor); - } else { - return pParser->onRead(pParser->pReadSeekUserData, pBufferOut, bytesToRead); - } -} -MA_PRIVATE ma_uint64 ma_dr_wav__read_smpl_to_metadata_obj(ma_dr_wav__metadata_parser* pParser, const ma_dr_wav_chunk_header* pChunkHeader, ma_dr_wav_metadata* pMetadata) -{ - ma_uint8 smplHeaderData[MA_DR_WAV_SMPL_BYTES]; - ma_uint64 totalBytesRead = 0; - size_t bytesJustRead; - if (pMetadata == NULL) { - return 0; - } - bytesJustRead = ma_dr_wav__metadata_parser_read(pParser, smplHeaderData, sizeof(smplHeaderData), &totalBytesRead); - MA_DR_WAV_ASSERT(pParser->stage == ma_dr_wav__metadata_parser_stage_read); - MA_DR_WAV_ASSERT(pChunkHeader != NULL); - if (pMetadata != NULL && bytesJustRead == sizeof(smplHeaderData)) { - ma_uint32 iSampleLoop; - pMetadata->type = ma_dr_wav_metadata_type_smpl; - pMetadata->data.smpl.manufacturerId = ma_dr_wav_bytes_to_u32(smplHeaderData + 0); - pMetadata->data.smpl.productId = ma_dr_wav_bytes_to_u32(smplHeaderData + 4); - pMetadata->data.smpl.samplePeriodNanoseconds = ma_dr_wav_bytes_to_u32(smplHeaderData + 8); - pMetadata->data.smpl.midiUnityNote = ma_dr_wav_bytes_to_u32(smplHeaderData + 12); - pMetadata->data.smpl.midiPitchFraction = ma_dr_wav_bytes_to_u32(smplHeaderData + 16); - pMetadata->data.smpl.smpteFormat = ma_dr_wav_bytes_to_u32(smplHeaderData + 20); - pMetadata->data.smpl.smpteOffset = ma_dr_wav_bytes_to_u32(smplHeaderData + 24); - pMetadata->data.smpl.sampleLoopCount = ma_dr_wav_bytes_to_u32(smplHeaderData + 28); - pMetadata->data.smpl.samplerSpecificDataSizeInBytes = ma_dr_wav_bytes_to_u32(smplHeaderData + 32); - if (pMetadata->data.smpl.sampleLoopCount == (pChunkHeader->sizeInBytes - MA_DR_WAV_SMPL_BYTES) / MA_DR_WAV_SMPL_LOOP_BYTES) { - pMetadata->data.smpl.pLoops = (ma_dr_wav_smpl_loop*)ma_dr_wav__metadata_get_memory(pParser, sizeof(ma_dr_wav_smpl_loop) * pMetadata->data.smpl.sampleLoopCount, MA_DR_WAV_METADATA_ALIGNMENT); - for (iSampleLoop = 0; iSampleLoop < pMetadata->data.smpl.sampleLoopCount; ++iSampleLoop) { - ma_uint8 smplLoopData[MA_DR_WAV_SMPL_LOOP_BYTES]; - bytesJustRead = ma_dr_wav__metadata_parser_read(pParser, smplLoopData, sizeof(smplLoopData), &totalBytesRead); - if (bytesJustRead == sizeof(smplLoopData)) { - pMetadata->data.smpl.pLoops[iSampleLoop].cuePointId = ma_dr_wav_bytes_to_u32(smplLoopData + 0); - pMetadata->data.smpl.pLoops[iSampleLoop].type = ma_dr_wav_bytes_to_u32(smplLoopData + 4); - pMetadata->data.smpl.pLoops[iSampleLoop].firstSampleByteOffset = ma_dr_wav_bytes_to_u32(smplLoopData + 8); - pMetadata->data.smpl.pLoops[iSampleLoop].lastSampleByteOffset = ma_dr_wav_bytes_to_u32(smplLoopData + 12); - pMetadata->data.smpl.pLoops[iSampleLoop].sampleFraction = ma_dr_wav_bytes_to_u32(smplLoopData + 16); - pMetadata->data.smpl.pLoops[iSampleLoop].playCount = ma_dr_wav_bytes_to_u32(smplLoopData + 20); - } else { - break; - } - } - if (pMetadata->data.smpl.samplerSpecificDataSizeInBytes > 0) { - pMetadata->data.smpl.pSamplerSpecificData = ma_dr_wav__metadata_get_memory(pParser, pMetadata->data.smpl.samplerSpecificDataSizeInBytes, 1); - MA_DR_WAV_ASSERT(pMetadata->data.smpl.pSamplerSpecificData != NULL); - ma_dr_wav__metadata_parser_read(pParser, pMetadata->data.smpl.pSamplerSpecificData, pMetadata->data.smpl.samplerSpecificDataSizeInBytes, &totalBytesRead); - } - } - } - return totalBytesRead; -} -MA_PRIVATE ma_uint64 ma_dr_wav__read_cue_to_metadata_obj(ma_dr_wav__metadata_parser* pParser, const ma_dr_wav_chunk_header* pChunkHeader, ma_dr_wav_metadata* pMetadata) -{ - ma_uint8 cueHeaderSectionData[MA_DR_WAV_CUE_BYTES]; - ma_uint64 totalBytesRead = 0; - size_t bytesJustRead; - if (pMetadata == NULL) { - return 0; - } - bytesJustRead = ma_dr_wav__metadata_parser_read(pParser, cueHeaderSectionData, sizeof(cueHeaderSectionData), &totalBytesRead); - MA_DR_WAV_ASSERT(pParser->stage == ma_dr_wav__metadata_parser_stage_read); - if (bytesJustRead == sizeof(cueHeaderSectionData)) { - pMetadata->type = ma_dr_wav_metadata_type_cue; - pMetadata->data.cue.cuePointCount = ma_dr_wav_bytes_to_u32(cueHeaderSectionData); - if (pMetadata->data.cue.cuePointCount == (pChunkHeader->sizeInBytes - MA_DR_WAV_CUE_BYTES) / MA_DR_WAV_CUE_POINT_BYTES) { - pMetadata->data.cue.pCuePoints = (ma_dr_wav_cue_point*)ma_dr_wav__metadata_get_memory(pParser, sizeof(ma_dr_wav_cue_point) * pMetadata->data.cue.cuePointCount, MA_DR_WAV_METADATA_ALIGNMENT); - MA_DR_WAV_ASSERT(pMetadata->data.cue.pCuePoints != NULL); - if (pMetadata->data.cue.cuePointCount > 0) { - ma_uint32 iCuePoint; - for (iCuePoint = 0; iCuePoint < pMetadata->data.cue.cuePointCount; ++iCuePoint) { - ma_uint8 cuePointData[MA_DR_WAV_CUE_POINT_BYTES]; - bytesJustRead = ma_dr_wav__metadata_parser_read(pParser, cuePointData, sizeof(cuePointData), &totalBytesRead); - if (bytesJustRead == sizeof(cuePointData)) { - pMetadata->data.cue.pCuePoints[iCuePoint].id = ma_dr_wav_bytes_to_u32(cuePointData + 0); - pMetadata->data.cue.pCuePoints[iCuePoint].playOrderPosition = ma_dr_wav_bytes_to_u32(cuePointData + 4); - pMetadata->data.cue.pCuePoints[iCuePoint].dataChunkId[0] = cuePointData[8]; - pMetadata->data.cue.pCuePoints[iCuePoint].dataChunkId[1] = cuePointData[9]; - pMetadata->data.cue.pCuePoints[iCuePoint].dataChunkId[2] = cuePointData[10]; - pMetadata->data.cue.pCuePoints[iCuePoint].dataChunkId[3] = cuePointData[11]; - pMetadata->data.cue.pCuePoints[iCuePoint].chunkStart = ma_dr_wav_bytes_to_u32(cuePointData + 12); - pMetadata->data.cue.pCuePoints[iCuePoint].blockStart = ma_dr_wav_bytes_to_u32(cuePointData + 16); - pMetadata->data.cue.pCuePoints[iCuePoint].sampleByteOffset = ma_dr_wav_bytes_to_u32(cuePointData + 20); - } else { - break; - } - } - } - } - } - return totalBytesRead; -} -MA_PRIVATE ma_uint64 ma_dr_wav__read_inst_to_metadata_obj(ma_dr_wav__metadata_parser* pParser, ma_dr_wav_metadata* pMetadata) -{ - ma_uint8 instData[MA_DR_WAV_INST_BYTES]; - ma_uint64 bytesRead; - if (pMetadata == NULL) { - return 0; - } - bytesRead = ma_dr_wav__metadata_parser_read(pParser, instData, sizeof(instData), NULL); - MA_DR_WAV_ASSERT(pParser->stage == ma_dr_wav__metadata_parser_stage_read); - if (bytesRead == sizeof(instData)) { - pMetadata->type = ma_dr_wav_metadata_type_inst; - pMetadata->data.inst.midiUnityNote = (ma_int8)instData[0]; - pMetadata->data.inst.fineTuneCents = (ma_int8)instData[1]; - pMetadata->data.inst.gainDecibels = (ma_int8)instData[2]; - pMetadata->data.inst.lowNote = (ma_int8)instData[3]; - pMetadata->data.inst.highNote = (ma_int8)instData[4]; - pMetadata->data.inst.lowVelocity = (ma_int8)instData[5]; - pMetadata->data.inst.highVelocity = (ma_int8)instData[6]; - } - return bytesRead; -} -MA_PRIVATE ma_uint64 ma_dr_wav__read_acid_to_metadata_obj(ma_dr_wav__metadata_parser* pParser, ma_dr_wav_metadata* pMetadata) -{ - ma_uint8 acidData[MA_DR_WAV_ACID_BYTES]; - ma_uint64 bytesRead; - if (pMetadata == NULL) { - return 0; - } - bytesRead = ma_dr_wav__metadata_parser_read(pParser, acidData, sizeof(acidData), NULL); - MA_DR_WAV_ASSERT(pParser->stage == ma_dr_wav__metadata_parser_stage_read); - if (bytesRead == sizeof(acidData)) { - pMetadata->type = ma_dr_wav_metadata_type_acid; - pMetadata->data.acid.flags = ma_dr_wav_bytes_to_u32(acidData + 0); - pMetadata->data.acid.midiUnityNote = ma_dr_wav_bytes_to_u16(acidData + 4); - pMetadata->data.acid.reserved1 = ma_dr_wav_bytes_to_u16(acidData + 6); - pMetadata->data.acid.reserved2 = ma_dr_wav_bytes_to_f32(acidData + 8); - pMetadata->data.acid.numBeats = ma_dr_wav_bytes_to_u32(acidData + 12); - pMetadata->data.acid.meterDenominator = ma_dr_wav_bytes_to_u16(acidData + 16); - pMetadata->data.acid.meterNumerator = ma_dr_wav_bytes_to_u16(acidData + 18); - pMetadata->data.acid.tempo = ma_dr_wav_bytes_to_f32(acidData + 20); - } - return bytesRead; -} -MA_PRIVATE size_t ma_dr_wav__strlen(const char* str) -{ - size_t result = 0; - while (*str++) { - result += 1; - } - return result; -} -MA_PRIVATE size_t ma_dr_wav__strlen_clamped(const char* str, size_t maxToRead) -{ - size_t result = 0; - while (*str++ && result < maxToRead) { - result += 1; - } - return result; -} -MA_PRIVATE char* ma_dr_wav__metadata_copy_string(ma_dr_wav__metadata_parser* pParser, const char* str, size_t maxToRead) -{ - size_t len = ma_dr_wav__strlen_clamped(str, maxToRead); - if (len) { - char* result = (char*)ma_dr_wav__metadata_get_memory(pParser, len + 1, 1); - MA_DR_WAV_ASSERT(result != NULL); - MA_DR_WAV_COPY_MEMORY(result, str, len); - result[len] = '\0'; - return result; - } else { - return NULL; - } -} -typedef struct -{ - const void* pBuffer; - size_t sizeInBytes; - size_t cursor; -} ma_dr_wav_buffer_reader; -MA_PRIVATE ma_result ma_dr_wav_buffer_reader_init(const void* pBuffer, size_t sizeInBytes, ma_dr_wav_buffer_reader* pReader) -{ - MA_DR_WAV_ASSERT(pBuffer != NULL); - MA_DR_WAV_ASSERT(pReader != NULL); - MA_DR_WAV_ZERO_OBJECT(pReader); - pReader->pBuffer = pBuffer; - pReader->sizeInBytes = sizeInBytes; - pReader->cursor = 0; - return MA_SUCCESS; -} -MA_PRIVATE const void* ma_dr_wav_buffer_reader_ptr(const ma_dr_wav_buffer_reader* pReader) -{ - MA_DR_WAV_ASSERT(pReader != NULL); - return ma_dr_wav_offset_ptr(pReader->pBuffer, pReader->cursor); -} -MA_PRIVATE ma_result ma_dr_wav_buffer_reader_seek(ma_dr_wav_buffer_reader* pReader, size_t bytesToSeek) -{ - MA_DR_WAV_ASSERT(pReader != NULL); - if (pReader->cursor + bytesToSeek > pReader->sizeInBytes) { - return MA_BAD_SEEK; - } - pReader->cursor += bytesToSeek; - return MA_SUCCESS; -} -MA_PRIVATE ma_result ma_dr_wav_buffer_reader_read(ma_dr_wav_buffer_reader* pReader, void* pDst, size_t bytesToRead, size_t* pBytesRead) -{ - ma_result result = MA_SUCCESS; - size_t bytesRemaining; - MA_DR_WAV_ASSERT(pReader != NULL); - if (pBytesRead != NULL) { - *pBytesRead = 0; - } - bytesRemaining = (pReader->sizeInBytes - pReader->cursor); - if (bytesToRead > bytesRemaining) { - bytesToRead = bytesRemaining; - } - if (pDst == NULL) { - result = ma_dr_wav_buffer_reader_seek(pReader, bytesToRead); - } else { - MA_DR_WAV_COPY_MEMORY(pDst, ma_dr_wav_buffer_reader_ptr(pReader), bytesToRead); - pReader->cursor += bytesToRead; - } - MA_DR_WAV_ASSERT(pReader->cursor <= pReader->sizeInBytes); - if (result == MA_SUCCESS) { - if (pBytesRead != NULL) { - *pBytesRead = bytesToRead; - } - } - return MA_SUCCESS; -} -MA_PRIVATE ma_result ma_dr_wav_buffer_reader_read_u16(ma_dr_wav_buffer_reader* pReader, ma_uint16* pDst) -{ - ma_result result; - size_t bytesRead; - ma_uint8 data[2]; - MA_DR_WAV_ASSERT(pReader != NULL); - MA_DR_WAV_ASSERT(pDst != NULL); - *pDst = 0; - result = ma_dr_wav_buffer_reader_read(pReader, data, sizeof(*pDst), &bytesRead); - if (result != MA_SUCCESS || bytesRead != sizeof(*pDst)) { - return result; - } - *pDst = ma_dr_wav_bytes_to_u16(data); - return MA_SUCCESS; -} -MA_PRIVATE ma_result ma_dr_wav_buffer_reader_read_u32(ma_dr_wav_buffer_reader* pReader, ma_uint32* pDst) -{ - ma_result result; - size_t bytesRead; - ma_uint8 data[4]; - MA_DR_WAV_ASSERT(pReader != NULL); - MA_DR_WAV_ASSERT(pDst != NULL); - *pDst = 0; - result = ma_dr_wav_buffer_reader_read(pReader, data, sizeof(*pDst), &bytesRead); - if (result != MA_SUCCESS || bytesRead != sizeof(*pDst)) { - return result; - } - *pDst = ma_dr_wav_bytes_to_u32(data); - return MA_SUCCESS; -} -MA_PRIVATE ma_uint64 ma_dr_wav__read_bext_to_metadata_obj(ma_dr_wav__metadata_parser* pParser, ma_dr_wav_metadata* pMetadata, ma_uint64 chunkSize) -{ - ma_uint8 bextData[MA_DR_WAV_BEXT_BYTES]; - size_t bytesRead = ma_dr_wav__metadata_parser_read(pParser, bextData, sizeof(bextData), NULL); - MA_DR_WAV_ASSERT(pParser->stage == ma_dr_wav__metadata_parser_stage_read); - if (bytesRead == sizeof(bextData)) { - ma_dr_wav_buffer_reader reader; - ma_uint32 timeReferenceLow; - ma_uint32 timeReferenceHigh; - size_t extraBytes; - pMetadata->type = ma_dr_wav_metadata_type_bext; - if (ma_dr_wav_buffer_reader_init(bextData, bytesRead, &reader) == MA_SUCCESS) { - pMetadata->data.bext.pDescription = ma_dr_wav__metadata_copy_string(pParser, (const char*)ma_dr_wav_buffer_reader_ptr(&reader), MA_DR_WAV_BEXT_DESCRIPTION_BYTES); - ma_dr_wav_buffer_reader_seek(&reader, MA_DR_WAV_BEXT_DESCRIPTION_BYTES); - pMetadata->data.bext.pOriginatorName = ma_dr_wav__metadata_copy_string(pParser, (const char*)ma_dr_wav_buffer_reader_ptr(&reader), MA_DR_WAV_BEXT_ORIGINATOR_NAME_BYTES); - ma_dr_wav_buffer_reader_seek(&reader, MA_DR_WAV_BEXT_ORIGINATOR_NAME_BYTES); - pMetadata->data.bext.pOriginatorReference = ma_dr_wav__metadata_copy_string(pParser, (const char*)ma_dr_wav_buffer_reader_ptr(&reader), MA_DR_WAV_BEXT_ORIGINATOR_REF_BYTES); - ma_dr_wav_buffer_reader_seek(&reader, MA_DR_WAV_BEXT_ORIGINATOR_REF_BYTES); - ma_dr_wav_buffer_reader_read(&reader, pMetadata->data.bext.pOriginationDate, sizeof(pMetadata->data.bext.pOriginationDate), NULL); - ma_dr_wav_buffer_reader_read(&reader, pMetadata->data.bext.pOriginationTime, sizeof(pMetadata->data.bext.pOriginationTime), NULL); - ma_dr_wav_buffer_reader_read_u32(&reader, &timeReferenceLow); - ma_dr_wav_buffer_reader_read_u32(&reader, &timeReferenceHigh); - pMetadata->data.bext.timeReference = ((ma_uint64)timeReferenceHigh << 32) + timeReferenceLow; - ma_dr_wav_buffer_reader_read_u16(&reader, &pMetadata->data.bext.version); - pMetadata->data.bext.pUMID = ma_dr_wav__metadata_get_memory(pParser, MA_DR_WAV_BEXT_UMID_BYTES, 1); - ma_dr_wav_buffer_reader_read(&reader, pMetadata->data.bext.pUMID, MA_DR_WAV_BEXT_UMID_BYTES, NULL); - ma_dr_wav_buffer_reader_read_u16(&reader, &pMetadata->data.bext.loudnessValue); - ma_dr_wav_buffer_reader_read_u16(&reader, &pMetadata->data.bext.loudnessRange); - ma_dr_wav_buffer_reader_read_u16(&reader, &pMetadata->data.bext.maxTruePeakLevel); - ma_dr_wav_buffer_reader_read_u16(&reader, &pMetadata->data.bext.maxMomentaryLoudness); - ma_dr_wav_buffer_reader_read_u16(&reader, &pMetadata->data.bext.maxShortTermLoudness); - MA_DR_WAV_ASSERT((ma_dr_wav_offset_ptr(ma_dr_wav_buffer_reader_ptr(&reader), MA_DR_WAV_BEXT_RESERVED_BYTES)) == (bextData + MA_DR_WAV_BEXT_BYTES)); - extraBytes = (size_t)(chunkSize - MA_DR_WAV_BEXT_BYTES); - if (extraBytes > 0) { - pMetadata->data.bext.pCodingHistory = (char*)ma_dr_wav__metadata_get_memory(pParser, extraBytes + 1, 1); - MA_DR_WAV_ASSERT(pMetadata->data.bext.pCodingHistory != NULL); - bytesRead += ma_dr_wav__metadata_parser_read(pParser, pMetadata->data.bext.pCodingHistory, extraBytes, NULL); - pMetadata->data.bext.codingHistorySize = (ma_uint32)ma_dr_wav__strlen(pMetadata->data.bext.pCodingHistory); - } else { - pMetadata->data.bext.pCodingHistory = NULL; - pMetadata->data.bext.codingHistorySize = 0; - } - } - } - return bytesRead; -} -MA_PRIVATE ma_uint64 ma_dr_wav__read_list_label_or_note_to_metadata_obj(ma_dr_wav__metadata_parser* pParser, ma_dr_wav_metadata* pMetadata, ma_uint64 chunkSize, ma_dr_wav_metadata_type type) -{ - ma_uint8 cueIDBuffer[MA_DR_WAV_LIST_LABEL_OR_NOTE_BYTES]; - ma_uint64 totalBytesRead = 0; - size_t bytesJustRead = ma_dr_wav__metadata_parser_read(pParser, cueIDBuffer, sizeof(cueIDBuffer), &totalBytesRead); - MA_DR_WAV_ASSERT(pParser->stage == ma_dr_wav__metadata_parser_stage_read); - if (bytesJustRead == sizeof(cueIDBuffer)) { - ma_uint32 sizeIncludingNullTerminator; - pMetadata->type = type; - pMetadata->data.labelOrNote.cuePointId = ma_dr_wav_bytes_to_u32(cueIDBuffer); - sizeIncludingNullTerminator = (ma_uint32)chunkSize - MA_DR_WAV_LIST_LABEL_OR_NOTE_BYTES; - if (sizeIncludingNullTerminator > 0) { - pMetadata->data.labelOrNote.stringLength = sizeIncludingNullTerminator - 1; - pMetadata->data.labelOrNote.pString = (char*)ma_dr_wav__metadata_get_memory(pParser, sizeIncludingNullTerminator, 1); - MA_DR_WAV_ASSERT(pMetadata->data.labelOrNote.pString != NULL); - ma_dr_wav__metadata_parser_read(pParser, pMetadata->data.labelOrNote.pString, sizeIncludingNullTerminator, &totalBytesRead); - } else { - pMetadata->data.labelOrNote.stringLength = 0; - pMetadata->data.labelOrNote.pString = NULL; - } - } - return totalBytesRead; -} -MA_PRIVATE ma_uint64 ma_dr_wav__read_list_labelled_cue_region_to_metadata_obj(ma_dr_wav__metadata_parser* pParser, ma_dr_wav_metadata* pMetadata, ma_uint64 chunkSize) -{ - ma_uint8 buffer[MA_DR_WAV_LIST_LABELLED_TEXT_BYTES]; - ma_uint64 totalBytesRead = 0; - size_t bytesJustRead = ma_dr_wav__metadata_parser_read(pParser, buffer, sizeof(buffer), &totalBytesRead); - MA_DR_WAV_ASSERT(pParser->stage == ma_dr_wav__metadata_parser_stage_read); - if (bytesJustRead == sizeof(buffer)) { - ma_uint32 sizeIncludingNullTerminator; - pMetadata->type = ma_dr_wav_metadata_type_list_labelled_cue_region; - pMetadata->data.labelledCueRegion.cuePointId = ma_dr_wav_bytes_to_u32(buffer + 0); - pMetadata->data.labelledCueRegion.sampleLength = ma_dr_wav_bytes_to_u32(buffer + 4); - pMetadata->data.labelledCueRegion.purposeId[0] = buffer[8]; - pMetadata->data.labelledCueRegion.purposeId[1] = buffer[9]; - pMetadata->data.labelledCueRegion.purposeId[2] = buffer[10]; - pMetadata->data.labelledCueRegion.purposeId[3] = buffer[11]; - pMetadata->data.labelledCueRegion.country = ma_dr_wav_bytes_to_u16(buffer + 12); - pMetadata->data.labelledCueRegion.language = ma_dr_wav_bytes_to_u16(buffer + 14); - pMetadata->data.labelledCueRegion.dialect = ma_dr_wav_bytes_to_u16(buffer + 16); - pMetadata->data.labelledCueRegion.codePage = ma_dr_wav_bytes_to_u16(buffer + 18); - sizeIncludingNullTerminator = (ma_uint32)chunkSize - MA_DR_WAV_LIST_LABELLED_TEXT_BYTES; - if (sizeIncludingNullTerminator > 0) { - pMetadata->data.labelledCueRegion.stringLength = sizeIncludingNullTerminator - 1; - pMetadata->data.labelledCueRegion.pString = (char*)ma_dr_wav__metadata_get_memory(pParser, sizeIncludingNullTerminator, 1); - MA_DR_WAV_ASSERT(pMetadata->data.labelledCueRegion.pString != NULL); - ma_dr_wav__metadata_parser_read(pParser, pMetadata->data.labelledCueRegion.pString, sizeIncludingNullTerminator, &totalBytesRead); - } else { - pMetadata->data.labelledCueRegion.stringLength = 0; - pMetadata->data.labelledCueRegion.pString = NULL; - } - } - return totalBytesRead; -} -MA_PRIVATE ma_uint64 ma_dr_wav__metadata_process_info_text_chunk(ma_dr_wav__metadata_parser* pParser, ma_uint64 chunkSize, ma_dr_wav_metadata_type type) -{ - ma_uint64 bytesRead = 0; - ma_uint32 stringSizeWithNullTerminator = (ma_uint32)chunkSize; - if (pParser->stage == ma_dr_wav__metadata_parser_stage_count) { - pParser->metadataCount += 1; - ma_dr_wav__metadata_request_extra_memory_for_stage_2(pParser, stringSizeWithNullTerminator, 1); - } else { - ma_dr_wav_metadata* pMetadata = &pParser->pMetadata[pParser->metadataCursor]; - pMetadata->type = type; - if (stringSizeWithNullTerminator > 0) { - pMetadata->data.infoText.stringLength = stringSizeWithNullTerminator - 1; - pMetadata->data.infoText.pString = (char*)ma_dr_wav__metadata_get_memory(pParser, stringSizeWithNullTerminator, 1); - MA_DR_WAV_ASSERT(pMetadata->data.infoText.pString != NULL); - bytesRead = ma_dr_wav__metadata_parser_read(pParser, pMetadata->data.infoText.pString, (size_t)stringSizeWithNullTerminator, NULL); - if (bytesRead == chunkSize) { - pParser->metadataCursor += 1; - } else { - } - } else { - pMetadata->data.infoText.stringLength = 0; - pMetadata->data.infoText.pString = NULL; - pParser->metadataCursor += 1; - } - } - return bytesRead; -} -MA_PRIVATE ma_uint64 ma_dr_wav__metadata_process_unknown_chunk(ma_dr_wav__metadata_parser* pParser, const ma_uint8* pChunkId, ma_uint64 chunkSize, ma_dr_wav_metadata_location location) -{ - ma_uint64 bytesRead = 0; - if (location == ma_dr_wav_metadata_location_invalid) { - return 0; - } - if (ma_dr_wav_fourcc_equal(pChunkId, "data") || ma_dr_wav_fourcc_equal(pChunkId, "fmt ") || ma_dr_wav_fourcc_equal(pChunkId, "fact")) { - return 0; - } - if (pParser->stage == ma_dr_wav__metadata_parser_stage_count) { - pParser->metadataCount += 1; - ma_dr_wav__metadata_request_extra_memory_for_stage_2(pParser, (size_t)chunkSize, 1); - } else { - ma_dr_wav_metadata* pMetadata = &pParser->pMetadata[pParser->metadataCursor]; - pMetadata->type = ma_dr_wav_metadata_type_unknown; - pMetadata->data.unknown.chunkLocation = location; - pMetadata->data.unknown.id[0] = pChunkId[0]; - pMetadata->data.unknown.id[1] = pChunkId[1]; - pMetadata->data.unknown.id[2] = pChunkId[2]; - pMetadata->data.unknown.id[3] = pChunkId[3]; - pMetadata->data.unknown.dataSizeInBytes = (ma_uint32)chunkSize; - pMetadata->data.unknown.pData = (ma_uint8 *)ma_dr_wav__metadata_get_memory(pParser, (size_t)chunkSize, 1); - MA_DR_WAV_ASSERT(pMetadata->data.unknown.pData != NULL); - bytesRead = ma_dr_wav__metadata_parser_read(pParser, pMetadata->data.unknown.pData, pMetadata->data.unknown.dataSizeInBytes, NULL); - if (bytesRead == pMetadata->data.unknown.dataSizeInBytes) { - pParser->metadataCursor += 1; - } else { - } - } - return bytesRead; -} -MA_PRIVATE ma_bool32 ma_dr_wav__chunk_matches(ma_dr_wav_metadata_type allowedMetadataTypes, const ma_uint8* pChunkID, ma_dr_wav_metadata_type type, const char* pID) -{ - return (allowedMetadataTypes & type) && ma_dr_wav_fourcc_equal(pChunkID, pID); -} -MA_PRIVATE ma_uint64 ma_dr_wav__metadata_process_chunk(ma_dr_wav__metadata_parser* pParser, const ma_dr_wav_chunk_header* pChunkHeader, ma_dr_wav_metadata_type allowedMetadataTypes) -{ - const ma_uint8 *pChunkID = pChunkHeader->id.fourcc; - ma_uint64 bytesRead = 0; - if (ma_dr_wav__chunk_matches(allowedMetadataTypes, pChunkID, ma_dr_wav_metadata_type_smpl, "smpl")) { - if (pChunkHeader->sizeInBytes >= MA_DR_WAV_SMPL_BYTES) { - if (pParser->stage == ma_dr_wav__metadata_parser_stage_count) { - ma_uint8 buffer[4]; - size_t bytesJustRead; - if (!pParser->onSeek(pParser->pReadSeekUserData, 28, ma_dr_wav_seek_origin_current)) { - return bytesRead; - } - bytesRead += 28; - bytesJustRead = ma_dr_wav__metadata_parser_read(pParser, buffer, sizeof(buffer), &bytesRead); - if (bytesJustRead == sizeof(buffer)) { - ma_uint32 loopCount = ma_dr_wav_bytes_to_u32(buffer); - ma_uint64 calculatedLoopCount; - calculatedLoopCount = (pChunkHeader->sizeInBytes - MA_DR_WAV_SMPL_BYTES) / MA_DR_WAV_SMPL_LOOP_BYTES; - if (calculatedLoopCount == loopCount) { - bytesJustRead = ma_dr_wav__metadata_parser_read(pParser, buffer, sizeof(buffer), &bytesRead); - if (bytesJustRead == sizeof(buffer)) { - ma_uint32 samplerSpecificDataSizeInBytes = ma_dr_wav_bytes_to_u32(buffer); - pParser->metadataCount += 1; - ma_dr_wav__metadata_request_extra_memory_for_stage_2(pParser, sizeof(ma_dr_wav_smpl_loop) * loopCount, MA_DR_WAV_METADATA_ALIGNMENT); - ma_dr_wav__metadata_request_extra_memory_for_stage_2(pParser, samplerSpecificDataSizeInBytes, 1); - } - } else { - } - } - } else { - bytesRead = ma_dr_wav__read_smpl_to_metadata_obj(pParser, pChunkHeader, &pParser->pMetadata[pParser->metadataCursor]); - if (bytesRead == pChunkHeader->sizeInBytes) { - pParser->metadataCursor += 1; - } else { - } - } - } else { - } - } else if (ma_dr_wav__chunk_matches(allowedMetadataTypes, pChunkID, ma_dr_wav_metadata_type_inst, "inst")) { - if (pChunkHeader->sizeInBytes == MA_DR_WAV_INST_BYTES) { - if (pParser->stage == ma_dr_wav__metadata_parser_stage_count) { - pParser->metadataCount += 1; - } else { - bytesRead = ma_dr_wav__read_inst_to_metadata_obj(pParser, &pParser->pMetadata[pParser->metadataCursor]); - if (bytesRead == pChunkHeader->sizeInBytes) { - pParser->metadataCursor += 1; - } else { - } - } - } else { - } - } else if (ma_dr_wav__chunk_matches(allowedMetadataTypes, pChunkID, ma_dr_wav_metadata_type_acid, "acid")) { - if (pChunkHeader->sizeInBytes == MA_DR_WAV_ACID_BYTES) { - if (pParser->stage == ma_dr_wav__metadata_parser_stage_count) { - pParser->metadataCount += 1; - } else { - bytesRead = ma_dr_wav__read_acid_to_metadata_obj(pParser, &pParser->pMetadata[pParser->metadataCursor]); - if (bytesRead == pChunkHeader->sizeInBytes) { - pParser->metadataCursor += 1; - } else { - } - } - } else { - } - } else if (ma_dr_wav__chunk_matches(allowedMetadataTypes, pChunkID, ma_dr_wav_metadata_type_cue, "cue ")) { - if (pChunkHeader->sizeInBytes >= MA_DR_WAV_CUE_BYTES) { - if (pParser->stage == ma_dr_wav__metadata_parser_stage_count) { - size_t cueCount; - pParser->metadataCount += 1; - cueCount = (size_t)(pChunkHeader->sizeInBytes - MA_DR_WAV_CUE_BYTES) / MA_DR_WAV_CUE_POINT_BYTES; - ma_dr_wav__metadata_request_extra_memory_for_stage_2(pParser, sizeof(ma_dr_wav_cue_point) * cueCount, MA_DR_WAV_METADATA_ALIGNMENT); - } else { - bytesRead = ma_dr_wav__read_cue_to_metadata_obj(pParser, pChunkHeader, &pParser->pMetadata[pParser->metadataCursor]); - if (bytesRead == pChunkHeader->sizeInBytes) { - pParser->metadataCursor += 1; - } else { - } - } - } else { - } - } else if (ma_dr_wav__chunk_matches(allowedMetadataTypes, pChunkID, ma_dr_wav_metadata_type_bext, "bext")) { - if (pChunkHeader->sizeInBytes >= MA_DR_WAV_BEXT_BYTES) { - if (pParser->stage == ma_dr_wav__metadata_parser_stage_count) { - char buffer[MA_DR_WAV_BEXT_DESCRIPTION_BYTES + 1]; - size_t allocSizeNeeded = MA_DR_WAV_BEXT_UMID_BYTES; - size_t bytesJustRead; - buffer[MA_DR_WAV_BEXT_DESCRIPTION_BYTES] = '\0'; - bytesJustRead = ma_dr_wav__metadata_parser_read(pParser, buffer, MA_DR_WAV_BEXT_DESCRIPTION_BYTES, &bytesRead); - if (bytesJustRead != MA_DR_WAV_BEXT_DESCRIPTION_BYTES) { - return bytesRead; - } - allocSizeNeeded += ma_dr_wav__strlen(buffer) + 1; - buffer[MA_DR_WAV_BEXT_ORIGINATOR_NAME_BYTES] = '\0'; - bytesJustRead = ma_dr_wav__metadata_parser_read(pParser, buffer, MA_DR_WAV_BEXT_ORIGINATOR_NAME_BYTES, &bytesRead); - if (bytesJustRead != MA_DR_WAV_BEXT_ORIGINATOR_NAME_BYTES) { - return bytesRead; - } - allocSizeNeeded += ma_dr_wav__strlen(buffer) + 1; - buffer[MA_DR_WAV_BEXT_ORIGINATOR_REF_BYTES] = '\0'; - bytesJustRead = ma_dr_wav__metadata_parser_read(pParser, buffer, MA_DR_WAV_BEXT_ORIGINATOR_REF_BYTES, &bytesRead); - if (bytesJustRead != MA_DR_WAV_BEXT_ORIGINATOR_REF_BYTES) { - return bytesRead; - } - allocSizeNeeded += ma_dr_wav__strlen(buffer) + 1; - allocSizeNeeded += (size_t)pChunkHeader->sizeInBytes - MA_DR_WAV_BEXT_BYTES; - ma_dr_wav__metadata_request_extra_memory_for_stage_2(pParser, allocSizeNeeded, 1); - pParser->metadataCount += 1; - } else { - bytesRead = ma_dr_wav__read_bext_to_metadata_obj(pParser, &pParser->pMetadata[pParser->metadataCursor], pChunkHeader->sizeInBytes); - if (bytesRead == pChunkHeader->sizeInBytes) { - pParser->metadataCursor += 1; - } else { - } - } - } else { - } - } else if (ma_dr_wav_fourcc_equal(pChunkID, "LIST") || ma_dr_wav_fourcc_equal(pChunkID, "list")) { - ma_dr_wav_metadata_location listType = ma_dr_wav_metadata_location_invalid; - while (bytesRead < pChunkHeader->sizeInBytes) { - ma_uint8 subchunkId[4]; - ma_uint8 subchunkSizeBuffer[4]; - ma_uint64 subchunkDataSize; - ma_uint64 subchunkBytesRead = 0; - ma_uint64 bytesJustRead = ma_dr_wav__metadata_parser_read(pParser, subchunkId, sizeof(subchunkId), &bytesRead); - if (bytesJustRead != sizeof(subchunkId)) { - break; - } - if (ma_dr_wav_fourcc_equal(subchunkId, "adtl")) { - listType = ma_dr_wav_metadata_location_inside_adtl_list; - continue; - } else if (ma_dr_wav_fourcc_equal(subchunkId, "INFO")) { - listType = ma_dr_wav_metadata_location_inside_info_list; - continue; - } - bytesJustRead = ma_dr_wav__metadata_parser_read(pParser, subchunkSizeBuffer, sizeof(subchunkSizeBuffer), &bytesRead); - if (bytesJustRead != sizeof(subchunkSizeBuffer)) { - break; - } - subchunkDataSize = ma_dr_wav_bytes_to_u32(subchunkSizeBuffer); - if (ma_dr_wav__chunk_matches(allowedMetadataTypes, subchunkId, ma_dr_wav_metadata_type_list_label, "labl") || ma_dr_wav__chunk_matches(allowedMetadataTypes, subchunkId, ma_dr_wav_metadata_type_list_note, "note")) { - if (subchunkDataSize >= MA_DR_WAV_LIST_LABEL_OR_NOTE_BYTES) { - ma_uint64 stringSizeWithNullTerm = subchunkDataSize - MA_DR_WAV_LIST_LABEL_OR_NOTE_BYTES; - if (pParser->stage == ma_dr_wav__metadata_parser_stage_count) { - pParser->metadataCount += 1; - ma_dr_wav__metadata_request_extra_memory_for_stage_2(pParser, (size_t)stringSizeWithNullTerm, 1); - } else { - subchunkBytesRead = ma_dr_wav__read_list_label_or_note_to_metadata_obj(pParser, &pParser->pMetadata[pParser->metadataCursor], subchunkDataSize, ma_dr_wav_fourcc_equal(subchunkId, "labl") ? ma_dr_wav_metadata_type_list_label : ma_dr_wav_metadata_type_list_note); - if (subchunkBytesRead == subchunkDataSize) { - pParser->metadataCursor += 1; - } else { - } - } - } else { - } - } else if (ma_dr_wav__chunk_matches(allowedMetadataTypes, subchunkId, ma_dr_wav_metadata_type_list_labelled_cue_region, "ltxt")) { - if (subchunkDataSize >= MA_DR_WAV_LIST_LABELLED_TEXT_BYTES) { - ma_uint64 stringSizeWithNullTerminator = subchunkDataSize - MA_DR_WAV_LIST_LABELLED_TEXT_BYTES; - if (pParser->stage == ma_dr_wav__metadata_parser_stage_count) { - pParser->metadataCount += 1; - ma_dr_wav__metadata_request_extra_memory_for_stage_2(pParser, (size_t)stringSizeWithNullTerminator, 1); - } else { - subchunkBytesRead = ma_dr_wav__read_list_labelled_cue_region_to_metadata_obj(pParser, &pParser->pMetadata[pParser->metadataCursor], subchunkDataSize); - if (subchunkBytesRead == subchunkDataSize) { - pParser->metadataCursor += 1; - } else { - } - } - } else { - } - } else if (ma_dr_wav__chunk_matches(allowedMetadataTypes, subchunkId, ma_dr_wav_metadata_type_list_info_software, "ISFT")) { - subchunkBytesRead = ma_dr_wav__metadata_process_info_text_chunk(pParser, subchunkDataSize, ma_dr_wav_metadata_type_list_info_software); - } else if (ma_dr_wav__chunk_matches(allowedMetadataTypes, subchunkId, ma_dr_wav_metadata_type_list_info_copyright, "ICOP")) { - subchunkBytesRead = ma_dr_wav__metadata_process_info_text_chunk(pParser, subchunkDataSize, ma_dr_wav_metadata_type_list_info_copyright); - } else if (ma_dr_wav__chunk_matches(allowedMetadataTypes, subchunkId, ma_dr_wav_metadata_type_list_info_title, "INAM")) { - subchunkBytesRead = ma_dr_wav__metadata_process_info_text_chunk(pParser, subchunkDataSize, ma_dr_wav_metadata_type_list_info_title); - } else if (ma_dr_wav__chunk_matches(allowedMetadataTypes, subchunkId, ma_dr_wav_metadata_type_list_info_artist, "IART")) { - subchunkBytesRead = ma_dr_wav__metadata_process_info_text_chunk(pParser, subchunkDataSize, ma_dr_wav_metadata_type_list_info_artist); - } else if (ma_dr_wav__chunk_matches(allowedMetadataTypes, subchunkId, ma_dr_wav_metadata_type_list_info_comment, "ICMT")) { - subchunkBytesRead = ma_dr_wav__metadata_process_info_text_chunk(pParser, subchunkDataSize, ma_dr_wav_metadata_type_list_info_comment); - } else if (ma_dr_wav__chunk_matches(allowedMetadataTypes, subchunkId, ma_dr_wav_metadata_type_list_info_date, "ICRD")) { - subchunkBytesRead = ma_dr_wav__metadata_process_info_text_chunk(pParser, subchunkDataSize, ma_dr_wav_metadata_type_list_info_date); - } else if (ma_dr_wav__chunk_matches(allowedMetadataTypes, subchunkId, ma_dr_wav_metadata_type_list_info_genre, "IGNR")) { - subchunkBytesRead = ma_dr_wav__metadata_process_info_text_chunk(pParser, subchunkDataSize, ma_dr_wav_metadata_type_list_info_genre); - } else if (ma_dr_wav__chunk_matches(allowedMetadataTypes, subchunkId, ma_dr_wav_metadata_type_list_info_album, "IPRD")) { - subchunkBytesRead = ma_dr_wav__metadata_process_info_text_chunk(pParser, subchunkDataSize, ma_dr_wav_metadata_type_list_info_album); - } else if (ma_dr_wav__chunk_matches(allowedMetadataTypes, subchunkId, ma_dr_wav_metadata_type_list_info_tracknumber, "ITRK")) { - subchunkBytesRead = ma_dr_wav__metadata_process_info_text_chunk(pParser, subchunkDataSize, ma_dr_wav_metadata_type_list_info_tracknumber); - } else if ((allowedMetadataTypes & ma_dr_wav_metadata_type_unknown) != 0) { - subchunkBytesRead = ma_dr_wav__metadata_process_unknown_chunk(pParser, subchunkId, subchunkDataSize, listType); - } - bytesRead += subchunkBytesRead; - MA_DR_WAV_ASSERT(subchunkBytesRead <= subchunkDataSize); - if (subchunkBytesRead < subchunkDataSize) { - ma_uint64 bytesToSeek = subchunkDataSize - subchunkBytesRead; - if (!pParser->onSeek(pParser->pReadSeekUserData, (int)bytesToSeek, ma_dr_wav_seek_origin_current)) { - break; - } - bytesRead += bytesToSeek; - } - if ((subchunkDataSize % 2) == 1) { - if (!pParser->onSeek(pParser->pReadSeekUserData, 1, ma_dr_wav_seek_origin_current)) { - break; - } - bytesRead += 1; - } - } - } else if ((allowedMetadataTypes & ma_dr_wav_metadata_type_unknown) != 0) { - bytesRead = ma_dr_wav__metadata_process_unknown_chunk(pParser, pChunkID, pChunkHeader->sizeInBytes, ma_dr_wav_metadata_location_top_level); - } - return bytesRead; -} -MA_PRIVATE ma_uint32 ma_dr_wav_get_bytes_per_pcm_frame(ma_dr_wav* pWav) -{ - ma_uint32 bytesPerFrame; - if ((pWav->bitsPerSample & 0x7) == 0) { - bytesPerFrame = (pWav->bitsPerSample * pWav->fmt.channels) >> 3; - } else { - bytesPerFrame = pWav->fmt.blockAlign; - } - if (pWav->translatedFormatTag == MA_DR_WAVE_FORMAT_ALAW || pWav->translatedFormatTag == MA_DR_WAVE_FORMAT_MULAW) { - if (bytesPerFrame != pWav->fmt.channels) { - return 0; - } - } - return bytesPerFrame; -} -MA_API ma_uint16 ma_dr_wav_fmt_get_format(const ma_dr_wav_fmt* pFMT) -{ - if (pFMT == NULL) { - return 0; - } - if (pFMT->formatTag != MA_DR_WAVE_FORMAT_EXTENSIBLE) { - return pFMT->formatTag; - } else { - return ma_dr_wav_bytes_to_u16(pFMT->subFormat); - } -} -MA_PRIVATE ma_bool32 ma_dr_wav_preinit(ma_dr_wav* pWav, ma_dr_wav_read_proc onRead, ma_dr_wav_seek_proc onSeek, void* pReadSeekUserData, const ma_allocation_callbacks* pAllocationCallbacks) -{ - if (pWav == NULL || onRead == NULL || onSeek == NULL) { - return MA_FALSE; - } - MA_DR_WAV_ZERO_MEMORY(pWav, sizeof(*pWav)); - pWav->onRead = onRead; - pWav->onSeek = onSeek; - pWav->pUserData = pReadSeekUserData; - pWav->allocationCallbacks = ma_dr_wav_copy_allocation_callbacks_or_defaults(pAllocationCallbacks); - if (pWav->allocationCallbacks.onFree == NULL || (pWav->allocationCallbacks.onMalloc == NULL && pWav->allocationCallbacks.onRealloc == NULL)) { - return MA_FALSE; - } - return MA_TRUE; -} -MA_PRIVATE ma_bool32 ma_dr_wav_init__internal(ma_dr_wav* pWav, ma_dr_wav_chunk_proc onChunk, void* pChunkUserData, ma_uint32 flags) -{ - ma_result result; - ma_uint64 cursor; - ma_bool32 sequential; - ma_uint8 riff[4]; - ma_dr_wav_fmt fmt; - unsigned short translatedFormatTag; - ma_uint64 dataChunkSize = 0; - ma_uint64 sampleCountFromFactChunk = 0; - ma_uint64 metadataStartPos; - ma_dr_wav__metadata_parser metadataParser; - ma_bool8 isProcessingMetadata = MA_FALSE; - ma_bool8 foundChunk_fmt = MA_FALSE; - ma_bool8 foundChunk_data = MA_FALSE; - ma_bool8 isAIFCFormType = MA_FALSE; - ma_uint64 aiffFrameCount = 0; - cursor = 0; - sequential = (flags & MA_DR_WAV_SEQUENTIAL) != 0; - MA_DR_WAV_ZERO_OBJECT(&fmt); - if (ma_dr_wav__on_read(pWav->onRead, pWav->pUserData, riff, sizeof(riff), &cursor) != sizeof(riff)) { - return MA_FALSE; - } - if (ma_dr_wav_fourcc_equal(riff, "RIFF")) { - pWav->container = ma_dr_wav_container_riff; - } else if (ma_dr_wav_fourcc_equal(riff, "RIFX")) { - pWav->container = ma_dr_wav_container_rifx; - } else if (ma_dr_wav_fourcc_equal(riff, "riff")) { - int i; - ma_uint8 riff2[12]; - pWav->container = ma_dr_wav_container_w64; - if (ma_dr_wav__on_read(pWav->onRead, pWav->pUserData, riff2, sizeof(riff2), &cursor) != sizeof(riff2)) { - return MA_FALSE; - } - for (i = 0; i < 12; ++i) { - if (riff2[i] != ma_dr_wavGUID_W64_RIFF[i+4]) { - return MA_FALSE; - } - } - } else if (ma_dr_wav_fourcc_equal(riff, "RF64")) { - pWav->container = ma_dr_wav_container_rf64; - } else if (ma_dr_wav_fourcc_equal(riff, "FORM")) { - pWav->container = ma_dr_wav_container_aiff; - } else { - return MA_FALSE; - } - if (pWav->container == ma_dr_wav_container_riff || pWav->container == ma_dr_wav_container_rifx || pWav->container == ma_dr_wav_container_rf64) { - ma_uint8 chunkSizeBytes[4]; - ma_uint8 wave[4]; - if (ma_dr_wav__on_read(pWav->onRead, pWav->pUserData, chunkSizeBytes, sizeof(chunkSizeBytes), &cursor) != sizeof(chunkSizeBytes)) { - return MA_FALSE; - } - if (pWav->container == ma_dr_wav_container_riff || pWav->container == ma_dr_wav_container_rifx) { - if (ma_dr_wav_bytes_to_u32_ex(chunkSizeBytes, pWav->container) < 36) { - } - } else if (pWav->container == ma_dr_wav_container_rf64) { - if (ma_dr_wav_bytes_to_u32_le(chunkSizeBytes) != 0xFFFFFFFF) { - return MA_FALSE; - } - } else { - return MA_FALSE; - } - if (ma_dr_wav__on_read(pWav->onRead, pWav->pUserData, wave, sizeof(wave), &cursor) != sizeof(wave)) { - return MA_FALSE; - } - if (!ma_dr_wav_fourcc_equal(wave, "WAVE")) { - return MA_FALSE; - } - } else if (pWav->container == ma_dr_wav_container_w64) { - ma_uint8 chunkSizeBytes[8]; - ma_uint8 wave[16]; - if (ma_dr_wav__on_read(pWav->onRead, pWav->pUserData, chunkSizeBytes, sizeof(chunkSizeBytes), &cursor) != sizeof(chunkSizeBytes)) { - return MA_FALSE; - } - if (ma_dr_wav_bytes_to_u64(chunkSizeBytes) < 80) { - return MA_FALSE; - } - if (ma_dr_wav__on_read(pWav->onRead, pWav->pUserData, wave, sizeof(wave), &cursor) != sizeof(wave)) { - return MA_FALSE; - } - if (!ma_dr_wav_guid_equal(wave, ma_dr_wavGUID_W64_WAVE)) { - return MA_FALSE; - } - } else if (pWav->container == ma_dr_wav_container_aiff) { - ma_uint8 chunkSizeBytes[4]; - ma_uint8 aiff[4]; - if (ma_dr_wav__on_read(pWav->onRead, pWav->pUserData, chunkSizeBytes, sizeof(chunkSizeBytes), &cursor) != sizeof(chunkSizeBytes)) { - return MA_FALSE; - } - if (ma_dr_wav_bytes_to_u32_be(chunkSizeBytes) < 18) { - return MA_FALSE; - } - if (ma_dr_wav__on_read(pWav->onRead, pWav->pUserData, aiff, sizeof(aiff), &cursor) != sizeof(aiff)) { - return MA_FALSE; - } - if (ma_dr_wav_fourcc_equal(aiff, "AIFF")) { - isAIFCFormType = MA_FALSE; - } else if (ma_dr_wav_fourcc_equal(aiff, "AIFC")) { - isAIFCFormType = MA_TRUE; - } else { - return MA_FALSE; - } - } else { - return MA_FALSE; - } - if (pWav->container == ma_dr_wav_container_rf64) { - ma_uint8 sizeBytes[8]; - ma_uint64 bytesRemainingInChunk; - ma_dr_wav_chunk_header header; - result = ma_dr_wav__read_chunk_header(pWav->onRead, pWav->pUserData, pWav->container, &cursor, &header); - if (result != MA_SUCCESS) { - return MA_FALSE; - } - if (!ma_dr_wav_fourcc_equal(header.id.fourcc, "ds64")) { - return MA_FALSE; - } - bytesRemainingInChunk = header.sizeInBytes + header.paddingSize; - if (!ma_dr_wav__seek_forward(pWav->onSeek, 8, pWav->pUserData)) { - return MA_FALSE; - } - bytesRemainingInChunk -= 8; - cursor += 8; - if (ma_dr_wav__on_read(pWav->onRead, pWav->pUserData, sizeBytes, sizeof(sizeBytes), &cursor) != sizeof(sizeBytes)) { - return MA_FALSE; - } - bytesRemainingInChunk -= 8; - dataChunkSize = ma_dr_wav_bytes_to_u64(sizeBytes); - if (ma_dr_wav__on_read(pWav->onRead, pWav->pUserData, sizeBytes, sizeof(sizeBytes), &cursor) != sizeof(sizeBytes)) { - return MA_FALSE; - } - bytesRemainingInChunk -= 8; - sampleCountFromFactChunk = ma_dr_wav_bytes_to_u64(sizeBytes); - if (!ma_dr_wav__seek_forward(pWav->onSeek, bytesRemainingInChunk, pWav->pUserData)) { - return MA_FALSE; - } - cursor += bytesRemainingInChunk; - } - metadataStartPos = cursor; - isProcessingMetadata = !sequential && ((flags & MA_DR_WAV_WITH_METADATA) != 0); - if (pWav->container != ma_dr_wav_container_riff && pWav->container != ma_dr_wav_container_rf64) { - isProcessingMetadata = MA_FALSE; - } - MA_DR_WAV_ZERO_MEMORY(&metadataParser, sizeof(metadataParser)); - if (isProcessingMetadata) { - metadataParser.onRead = pWav->onRead; - metadataParser.onSeek = pWav->onSeek; - metadataParser.pReadSeekUserData = pWav->pUserData; - metadataParser.stage = ma_dr_wav__metadata_parser_stage_count; - } - for (;;) { - ma_dr_wav_chunk_header header; - ma_uint64 chunkSize; - result = ma_dr_wav__read_chunk_header(pWav->onRead, pWav->pUserData, pWav->container, &cursor, &header); - if (result != MA_SUCCESS) { - break; - } - chunkSize = header.sizeInBytes; - if (!sequential && onChunk != NULL) { - ma_uint64 callbackBytesRead = onChunk(pChunkUserData, pWav->onRead, pWav->onSeek, pWav->pUserData, &header, pWav->container, &fmt); - if (callbackBytesRead > 0) { - if (ma_dr_wav__seek_from_start(pWav->onSeek, cursor, pWav->pUserData) == MA_FALSE) { - return MA_FALSE; - } - } - } - if (((pWav->container == ma_dr_wav_container_riff || pWav->container == ma_dr_wav_container_rifx || pWav->container == ma_dr_wav_container_rf64) && ma_dr_wav_fourcc_equal(header.id.fourcc, "fmt ")) || - ((pWav->container == ma_dr_wav_container_w64) && ma_dr_wav_guid_equal(header.id.guid, ma_dr_wavGUID_W64_FMT))) { - ma_uint8 fmtData[16]; - foundChunk_fmt = MA_TRUE; - if (pWav->onRead(pWav->pUserData, fmtData, sizeof(fmtData)) != sizeof(fmtData)) { - return MA_FALSE; - } - cursor += sizeof(fmtData); - fmt.formatTag = ma_dr_wav_bytes_to_u16_ex(fmtData + 0, pWav->container); - fmt.channels = ma_dr_wav_bytes_to_u16_ex(fmtData + 2, pWav->container); - fmt.sampleRate = ma_dr_wav_bytes_to_u32_ex(fmtData + 4, pWav->container); - fmt.avgBytesPerSec = ma_dr_wav_bytes_to_u32_ex(fmtData + 8, pWav->container); - fmt.blockAlign = ma_dr_wav_bytes_to_u16_ex(fmtData + 12, pWav->container); - fmt.bitsPerSample = ma_dr_wav_bytes_to_u16_ex(fmtData + 14, pWav->container); - fmt.extendedSize = 0; - fmt.validBitsPerSample = 0; - fmt.channelMask = 0; - MA_DR_WAV_ZERO_MEMORY(fmt.subFormat, sizeof(fmt.subFormat)); - if (header.sizeInBytes > 16) { - ma_uint8 fmt_cbSize[2]; - int bytesReadSoFar = 0; - if (pWav->onRead(pWav->pUserData, fmt_cbSize, sizeof(fmt_cbSize)) != sizeof(fmt_cbSize)) { - return MA_FALSE; - } - cursor += sizeof(fmt_cbSize); - bytesReadSoFar = 18; - fmt.extendedSize = ma_dr_wav_bytes_to_u16_ex(fmt_cbSize, pWav->container); - if (fmt.extendedSize > 0) { - if (fmt.formatTag == MA_DR_WAVE_FORMAT_EXTENSIBLE) { - if (fmt.extendedSize != 22) { - return MA_FALSE; - } - } - if (fmt.formatTag == MA_DR_WAVE_FORMAT_EXTENSIBLE) { - ma_uint8 fmtext[22]; - if (pWav->onRead(pWav->pUserData, fmtext, fmt.extendedSize) != fmt.extendedSize) { - return MA_FALSE; - } - fmt.validBitsPerSample = ma_dr_wav_bytes_to_u16_ex(fmtext + 0, pWav->container); - fmt.channelMask = ma_dr_wav_bytes_to_u32_ex(fmtext + 2, pWav->container); - ma_dr_wav_bytes_to_guid(fmtext + 6, fmt.subFormat); - } else { - if (pWav->onSeek(pWav->pUserData, fmt.extendedSize, ma_dr_wav_seek_origin_current) == MA_FALSE) { - return MA_FALSE; - } - } - cursor += fmt.extendedSize; - bytesReadSoFar += fmt.extendedSize; - } - if (pWav->onSeek(pWav->pUserData, (int)(header.sizeInBytes - bytesReadSoFar), ma_dr_wav_seek_origin_current) == MA_FALSE) { - return MA_FALSE; - } - cursor += (header.sizeInBytes - bytesReadSoFar); - } - if (header.paddingSize > 0) { - if (ma_dr_wav__seek_forward(pWav->onSeek, header.paddingSize, pWav->pUserData) == MA_FALSE) { - break; - } - cursor += header.paddingSize; - } - continue; - } - if (((pWav->container == ma_dr_wav_container_riff || pWav->container == ma_dr_wav_container_rifx || pWav->container == ma_dr_wav_container_rf64) && ma_dr_wav_fourcc_equal(header.id.fourcc, "data")) || - ((pWav->container == ma_dr_wav_container_w64) && ma_dr_wav_guid_equal(header.id.guid, ma_dr_wavGUID_W64_DATA))) { - foundChunk_data = MA_TRUE; - pWav->dataChunkDataPos = cursor; - if (pWav->container != ma_dr_wav_container_rf64) { - dataChunkSize = chunkSize; - } - if (sequential || !isProcessingMetadata) { - break; - } else { - chunkSize += header.paddingSize; - if (ma_dr_wav__seek_forward(pWav->onSeek, chunkSize, pWav->pUserData) == MA_FALSE) { - break; - } - cursor += chunkSize; - continue; - } - } - if (((pWav->container == ma_dr_wav_container_riff || pWav->container == ma_dr_wav_container_rifx || pWav->container == ma_dr_wav_container_rf64) && ma_dr_wav_fourcc_equal(header.id.fourcc, "fact")) || - ((pWav->container == ma_dr_wav_container_w64) && ma_dr_wav_guid_equal(header.id.guid, ma_dr_wavGUID_W64_FACT))) { - if (pWav->container == ma_dr_wav_container_riff || pWav->container == ma_dr_wav_container_rifx) { - ma_uint8 sampleCount[4]; - if (ma_dr_wav__on_read(pWav->onRead, pWav->pUserData, &sampleCount, 4, &cursor) != 4) { - return MA_FALSE; - } - chunkSize -= 4; - if (pWav->translatedFormatTag == MA_DR_WAVE_FORMAT_ADPCM) { - sampleCountFromFactChunk = ma_dr_wav_bytes_to_u32_ex(sampleCount, pWav->container); - } else { - sampleCountFromFactChunk = 0; - } - } else if (pWav->container == ma_dr_wav_container_w64) { - if (ma_dr_wav__on_read(pWav->onRead, pWav->pUserData, &sampleCountFromFactChunk, 8, &cursor) != 8) { - return MA_FALSE; - } - chunkSize -= 8; - } else if (pWav->container == ma_dr_wav_container_rf64) { - } - chunkSize += header.paddingSize; - if (ma_dr_wav__seek_forward(pWav->onSeek, chunkSize, pWav->pUserData) == MA_FALSE) { - break; - } - cursor += chunkSize; - continue; - } - if (pWav->container == ma_dr_wav_container_aiff && ma_dr_wav_fourcc_equal(header.id.fourcc, "COMM")) { - ma_uint8 commData[24]; - ma_uint32 commDataBytesToRead; - ma_uint16 channels; - ma_uint32 frameCount; - ma_uint16 sampleSizeInBits; - ma_int64 sampleRate; - ma_uint16 compressionFormat; - foundChunk_fmt = MA_TRUE; - if (isAIFCFormType) { - commDataBytesToRead = 24; - if (header.sizeInBytes < commDataBytesToRead) { - return MA_FALSE; - } - } else { - commDataBytesToRead = 18; - if (header.sizeInBytes != commDataBytesToRead) { - return MA_FALSE; - } - } - if (ma_dr_wav__on_read(pWav->onRead, pWav->pUserData, commData, commDataBytesToRead, &cursor) != commDataBytesToRead) { - return MA_FALSE; - } - channels = ma_dr_wav_bytes_to_u16_ex (commData + 0, pWav->container); - frameCount = ma_dr_wav_bytes_to_u32_ex (commData + 2, pWav->container); - sampleSizeInBits = ma_dr_wav_bytes_to_u16_ex (commData + 6, pWav->container); - sampleRate = ma_dr_wav_aiff_extented_to_s64(commData + 8); - if (sampleRate < 0 || sampleRate > 0xFFFFFFFF) { - return MA_FALSE; - } - if (isAIFCFormType) { - const ma_uint8* type = commData + 18; - if (ma_dr_wav_fourcc_equal(type, "NONE")) { - compressionFormat = MA_DR_WAVE_FORMAT_PCM; - } else if (ma_dr_wav_fourcc_equal(type, "raw ")) { - compressionFormat = MA_DR_WAVE_FORMAT_PCM; - if (sampleSizeInBits == 8) { - pWav->aiff.isUnsigned = MA_TRUE; - } - } else if (ma_dr_wav_fourcc_equal(type, "sowt")) { - compressionFormat = MA_DR_WAVE_FORMAT_PCM; - pWav->aiff.isLE = MA_TRUE; - } else if (ma_dr_wav_fourcc_equal(type, "fl32") || ma_dr_wav_fourcc_equal(type, "fl64") || ma_dr_wav_fourcc_equal(type, "FL32") || ma_dr_wav_fourcc_equal(type, "FL64")) { - compressionFormat = MA_DR_WAVE_FORMAT_IEEE_FLOAT; - } else if (ma_dr_wav_fourcc_equal(type, "alaw") || ma_dr_wav_fourcc_equal(type, "ALAW")) { - compressionFormat = MA_DR_WAVE_FORMAT_ALAW; - } else if (ma_dr_wav_fourcc_equal(type, "ulaw") || ma_dr_wav_fourcc_equal(type, "ULAW")) { - compressionFormat = MA_DR_WAVE_FORMAT_MULAW; - } else if (ma_dr_wav_fourcc_equal(type, "ima4")) { - compressionFormat = MA_DR_WAVE_FORMAT_DVI_ADPCM; - sampleSizeInBits = 4; - (void)compressionFormat; - (void)sampleSizeInBits; - return MA_FALSE; - } else { - return MA_FALSE; - } - } else { - compressionFormat = MA_DR_WAVE_FORMAT_PCM; - } - aiffFrameCount = frameCount; - fmt.formatTag = compressionFormat; - fmt.channels = channels; - fmt.sampleRate = (ma_uint32)sampleRate; - fmt.bitsPerSample = sampleSizeInBits; - fmt.blockAlign = (ma_uint16)(fmt.channels * fmt.bitsPerSample / 8); - fmt.avgBytesPerSec = fmt.blockAlign * fmt.sampleRate; - if (fmt.blockAlign == 0 && compressionFormat == MA_DR_WAVE_FORMAT_DVI_ADPCM) { - fmt.blockAlign = 34 * fmt.channels; - } - if (compressionFormat == MA_DR_WAVE_FORMAT_ALAW || compressionFormat == MA_DR_WAVE_FORMAT_MULAW) { - if (fmt.bitsPerSample > 8) { - fmt.bitsPerSample = 8; - fmt.blockAlign = fmt.channels; - } - } - fmt.bitsPerSample += (fmt.bitsPerSample & 7); - if (isAIFCFormType) { - if (ma_dr_wav__seek_forward(pWav->onSeek, (chunkSize - commDataBytesToRead), pWav->pUserData) == MA_FALSE) { - return MA_FALSE; - } - cursor += (chunkSize - commDataBytesToRead); - } - continue; - } - if (pWav->container == ma_dr_wav_container_aiff && ma_dr_wav_fourcc_equal(header.id.fourcc, "SSND")) { - ma_uint8 offsetAndBlockSizeData[8]; - ma_uint32 offset; - foundChunk_data = MA_TRUE; - if (ma_dr_wav__on_read(pWav->onRead, pWav->pUserData, offsetAndBlockSizeData, sizeof(offsetAndBlockSizeData), &cursor) != sizeof(offsetAndBlockSizeData)) { - return MA_FALSE; - } - offset = ma_dr_wav_bytes_to_u32_ex(offsetAndBlockSizeData + 0, pWav->container); - if (ma_dr_wav__seek_forward(pWav->onSeek, offset, pWav->pUserData) == MA_FALSE) { - return MA_FALSE; - } - cursor += offset; - pWav->dataChunkDataPos = cursor; - dataChunkSize = chunkSize; - if (sequential || !isProcessingMetadata) { - break; - } else { - if (ma_dr_wav__seek_forward(pWav->onSeek, chunkSize, pWav->pUserData) == MA_FALSE) { - break; - } - cursor += chunkSize; - continue; - } - } - if (isProcessingMetadata) { - ma_dr_wav__metadata_process_chunk(&metadataParser, &header, ma_dr_wav_metadata_type_all_including_unknown); - if (ma_dr_wav__seek_from_start(pWav->onSeek, cursor, pWav->pUserData) == MA_FALSE) { - break; - } - } - chunkSize += header.paddingSize; - if (ma_dr_wav__seek_forward(pWav->onSeek, chunkSize, pWav->pUserData) == MA_FALSE) { - break; - } - cursor += chunkSize; - } - if (!foundChunk_fmt || !foundChunk_data) { - return MA_FALSE; - } - if ((fmt.sampleRate == 0 || fmt.sampleRate > MA_DR_WAV_MAX_SAMPLE_RATE ) || - (fmt.channels == 0 || fmt.channels > MA_DR_WAV_MAX_CHANNELS ) || - (fmt.bitsPerSample == 0 || fmt.bitsPerSample > MA_DR_WAV_MAX_BITS_PER_SAMPLE) || - fmt.blockAlign == 0) { - return MA_FALSE; - } - translatedFormatTag = fmt.formatTag; - if (translatedFormatTag == MA_DR_WAVE_FORMAT_EXTENSIBLE) { - translatedFormatTag = ma_dr_wav_bytes_to_u16_ex(fmt.subFormat + 0, pWav->container); - } - if (!sequential) { - if (!ma_dr_wav__seek_from_start(pWav->onSeek, pWav->dataChunkDataPos, pWav->pUserData)) { - return MA_FALSE; - } - cursor = pWav->dataChunkDataPos; - } - if (isProcessingMetadata && metadataParser.metadataCount > 0) { - if (ma_dr_wav__seek_from_start(pWav->onSeek, metadataStartPos, pWav->pUserData) == MA_FALSE) { - return MA_FALSE; - } - result = ma_dr_wav__metadata_alloc(&metadataParser, &pWav->allocationCallbacks); - if (result != MA_SUCCESS) { - return MA_FALSE; - } - metadataParser.stage = ma_dr_wav__metadata_parser_stage_read; - for (;;) { - ma_dr_wav_chunk_header header; - ma_uint64 metadataBytesRead; - result = ma_dr_wav__read_chunk_header(pWav->onRead, pWav->pUserData, pWav->container, &cursor, &header); - if (result != MA_SUCCESS) { - break; - } - metadataBytesRead = ma_dr_wav__metadata_process_chunk(&metadataParser, &header, ma_dr_wav_metadata_type_all_including_unknown); - if (ma_dr_wav__seek_forward(pWav->onSeek, (header.sizeInBytes + header.paddingSize) - metadataBytesRead, pWav->pUserData) == MA_FALSE) { - ma_dr_wav_free(metadataParser.pMetadata, &pWav->allocationCallbacks); - return MA_FALSE; - } - } - pWav->pMetadata = metadataParser.pMetadata; - pWav->metadataCount = metadataParser.metadataCount; - } - if (dataChunkSize == 0xFFFFFFFF && (pWav->container == ma_dr_wav_container_riff || pWav->container == ma_dr_wav_container_rifx) && pWav->isSequentialWrite == MA_FALSE) { - dataChunkSize = 0; - for (;;) { - ma_uint8 temp[4096]; - size_t bytesRead = pWav->onRead(pWav->pUserData, temp, sizeof(temp)); - dataChunkSize += bytesRead; - if (bytesRead < sizeof(temp)) { - break; - } - } - } - if (ma_dr_wav__seek_from_start(pWav->onSeek, pWav->dataChunkDataPos, pWav->pUserData) == MA_FALSE) { - ma_dr_wav_free(pWav->pMetadata, &pWav->allocationCallbacks); - return MA_FALSE; - } - pWav->fmt = fmt; - pWav->sampleRate = fmt.sampleRate; - pWav->channels = fmt.channels; - pWav->bitsPerSample = fmt.bitsPerSample; - pWav->bytesRemaining = dataChunkSize; - pWav->translatedFormatTag = translatedFormatTag; - pWav->dataChunkDataSize = dataChunkSize; - if (sampleCountFromFactChunk != 0) { - pWav->totalPCMFrameCount = sampleCountFromFactChunk; - } else if (aiffFrameCount != 0) { - pWav->totalPCMFrameCount = aiffFrameCount; - } else { - ma_uint32 bytesPerFrame = ma_dr_wav_get_bytes_per_pcm_frame(pWav); - if (bytesPerFrame == 0) { - ma_dr_wav_free(pWav->pMetadata, &pWav->allocationCallbacks); - return MA_FALSE; - } - pWav->totalPCMFrameCount = dataChunkSize / bytesPerFrame; - if (pWav->translatedFormatTag == MA_DR_WAVE_FORMAT_ADPCM) { - ma_uint64 totalBlockHeaderSizeInBytes; - ma_uint64 blockCount = dataChunkSize / fmt.blockAlign; - if ((blockCount * fmt.blockAlign) < dataChunkSize) { - blockCount += 1; - } - totalBlockHeaderSizeInBytes = blockCount * (6*fmt.channels); - pWav->totalPCMFrameCount = ((dataChunkSize - totalBlockHeaderSizeInBytes) * 2) / fmt.channels; - } - if (pWav->translatedFormatTag == MA_DR_WAVE_FORMAT_DVI_ADPCM) { - ma_uint64 totalBlockHeaderSizeInBytes; - ma_uint64 blockCount = dataChunkSize / fmt.blockAlign; - if ((blockCount * fmt.blockAlign) < dataChunkSize) { - blockCount += 1; - } - totalBlockHeaderSizeInBytes = blockCount * (4*fmt.channels); - pWav->totalPCMFrameCount = ((dataChunkSize - totalBlockHeaderSizeInBytes) * 2) / fmt.channels; - pWav->totalPCMFrameCount += blockCount; - } - } - if (pWav->translatedFormatTag == MA_DR_WAVE_FORMAT_ADPCM || pWav->translatedFormatTag == MA_DR_WAVE_FORMAT_DVI_ADPCM) { - if (pWav->channels > 2) { - ma_dr_wav_free(pWav->pMetadata, &pWav->allocationCallbacks); - return MA_FALSE; - } - } - if (ma_dr_wav_get_bytes_per_pcm_frame(pWav) == 0) { - ma_dr_wav_free(pWav->pMetadata, &pWav->allocationCallbacks); - return MA_FALSE; - } -#ifdef MA_DR_WAV_LIBSNDFILE_COMPAT - if (pWav->translatedFormatTag == MA_DR_WAVE_FORMAT_ADPCM) { - ma_uint64 blockCount = dataChunkSize / fmt.blockAlign; - pWav->totalPCMFrameCount = (((blockCount * (fmt.blockAlign - (6*pWav->channels))) * 2)) / fmt.channels; - } - if (pWav->translatedFormatTag == MA_DR_WAVE_FORMAT_DVI_ADPCM) { - ma_uint64 blockCount = dataChunkSize / fmt.blockAlign; - pWav->totalPCMFrameCount = (((blockCount * (fmt.blockAlign - (4*pWav->channels))) * 2) + (blockCount * pWav->channels)) / fmt.channels; - } -#endif - return MA_TRUE; -} -MA_API ma_bool32 ma_dr_wav_init(ma_dr_wav* pWav, ma_dr_wav_read_proc onRead, ma_dr_wav_seek_proc onSeek, void* pUserData, const ma_allocation_callbacks* pAllocationCallbacks) -{ - return ma_dr_wav_init_ex(pWav, onRead, onSeek, NULL, pUserData, NULL, 0, pAllocationCallbacks); -} -MA_API ma_bool32 ma_dr_wav_init_ex(ma_dr_wav* pWav, ma_dr_wav_read_proc onRead, ma_dr_wav_seek_proc onSeek, ma_dr_wav_chunk_proc onChunk, void* pReadSeekUserData, void* pChunkUserData, ma_uint32 flags, const ma_allocation_callbacks* pAllocationCallbacks) -{ - if (!ma_dr_wav_preinit(pWav, onRead, onSeek, pReadSeekUserData, pAllocationCallbacks)) { - return MA_FALSE; - } - return ma_dr_wav_init__internal(pWav, onChunk, pChunkUserData, flags); -} -MA_API ma_bool32 ma_dr_wav_init_with_metadata(ma_dr_wav* pWav, ma_dr_wav_read_proc onRead, ma_dr_wav_seek_proc onSeek, void* pUserData, ma_uint32 flags, const ma_allocation_callbacks* pAllocationCallbacks) -{ - if (!ma_dr_wav_preinit(pWav, onRead, onSeek, pUserData, pAllocationCallbacks)) { - return MA_FALSE; - } - return ma_dr_wav_init__internal(pWav, NULL, NULL, flags | MA_DR_WAV_WITH_METADATA); -} -MA_API ma_dr_wav_metadata* ma_dr_wav_take_ownership_of_metadata(ma_dr_wav* pWav) -{ - ma_dr_wav_metadata *result = pWav->pMetadata; - pWav->pMetadata = NULL; - pWav->metadataCount = 0; - return result; -} -MA_PRIVATE size_t ma_dr_wav__write(ma_dr_wav* pWav, const void* pData, size_t dataSize) -{ - MA_DR_WAV_ASSERT(pWav != NULL); - MA_DR_WAV_ASSERT(pWav->onWrite != NULL); - return pWav->onWrite(pWav->pUserData, pData, dataSize); -} -MA_PRIVATE size_t ma_dr_wav__write_byte(ma_dr_wav* pWav, ma_uint8 byte) -{ - MA_DR_WAV_ASSERT(pWav != NULL); - MA_DR_WAV_ASSERT(pWav->onWrite != NULL); - return pWav->onWrite(pWav->pUserData, &byte, 1); -} -MA_PRIVATE size_t ma_dr_wav__write_u16ne_to_le(ma_dr_wav* pWav, ma_uint16 value) -{ - MA_DR_WAV_ASSERT(pWav != NULL); - MA_DR_WAV_ASSERT(pWav->onWrite != NULL); - if (!ma_dr_wav__is_little_endian()) { - value = ma_dr_wav__bswap16(value); - } - return ma_dr_wav__write(pWav, &value, 2); -} -MA_PRIVATE size_t ma_dr_wav__write_u32ne_to_le(ma_dr_wav* pWav, ma_uint32 value) -{ - MA_DR_WAV_ASSERT(pWav != NULL); - MA_DR_WAV_ASSERT(pWav->onWrite != NULL); - if (!ma_dr_wav__is_little_endian()) { - value = ma_dr_wav__bswap32(value); - } - return ma_dr_wav__write(pWav, &value, 4); -} -MA_PRIVATE size_t ma_dr_wav__write_u64ne_to_le(ma_dr_wav* pWav, ma_uint64 value) -{ - MA_DR_WAV_ASSERT(pWav != NULL); - MA_DR_WAV_ASSERT(pWav->onWrite != NULL); - if (!ma_dr_wav__is_little_endian()) { - value = ma_dr_wav__bswap64(value); - } - return ma_dr_wav__write(pWav, &value, 8); -} -MA_PRIVATE size_t ma_dr_wav__write_f32ne_to_le(ma_dr_wav* pWav, float value) -{ - union { - ma_uint32 u32; - float f32; - } u; - MA_DR_WAV_ASSERT(pWav != NULL); - MA_DR_WAV_ASSERT(pWav->onWrite != NULL); - u.f32 = value; - if (!ma_dr_wav__is_little_endian()) { - u.u32 = ma_dr_wav__bswap32(u.u32); - } - return ma_dr_wav__write(pWav, &u.u32, 4); -} -MA_PRIVATE size_t ma_dr_wav__write_or_count(ma_dr_wav* pWav, const void* pData, size_t dataSize) -{ - if (pWav == NULL) { - return dataSize; - } - return ma_dr_wav__write(pWav, pData, dataSize); -} -MA_PRIVATE size_t ma_dr_wav__write_or_count_byte(ma_dr_wav* pWav, ma_uint8 byte) -{ - if (pWav == NULL) { - return 1; - } - return ma_dr_wav__write_byte(pWav, byte); -} -MA_PRIVATE size_t ma_dr_wav__write_or_count_u16ne_to_le(ma_dr_wav* pWav, ma_uint16 value) -{ - if (pWav == NULL) { - return 2; - } - return ma_dr_wav__write_u16ne_to_le(pWav, value); -} -MA_PRIVATE size_t ma_dr_wav__write_or_count_u32ne_to_le(ma_dr_wav* pWav, ma_uint32 value) -{ - if (pWav == NULL) { - return 4; - } - return ma_dr_wav__write_u32ne_to_le(pWav, value); -} -#if 0 -MA_PRIVATE size_t ma_dr_wav__write_or_count_u64ne_to_le(ma_dr_wav* pWav, ma_uint64 value) -{ - if (pWav == NULL) { - return 8; - } - return ma_dr_wav__write_u64ne_to_le(pWav, value); -} -#endif -MA_PRIVATE size_t ma_dr_wav__write_or_count_f32ne_to_le(ma_dr_wav* pWav, float value) -{ - if (pWav == NULL) { - return 4; - } - return ma_dr_wav__write_f32ne_to_le(pWav, value); -} -MA_PRIVATE size_t ma_dr_wav__write_or_count_string_to_fixed_size_buf(ma_dr_wav* pWav, char* str, size_t bufFixedSize) -{ - size_t len; - if (pWav == NULL) { - return bufFixedSize; - } - len = ma_dr_wav__strlen_clamped(str, bufFixedSize); - ma_dr_wav__write_or_count(pWav, str, len); - if (len < bufFixedSize) { - size_t i; - for (i = 0; i < bufFixedSize - len; ++i) { - ma_dr_wav__write_byte(pWav, 0); - } - } - return bufFixedSize; -} -MA_PRIVATE size_t ma_dr_wav__write_or_count_metadata(ma_dr_wav* pWav, ma_dr_wav_metadata* pMetadatas, ma_uint32 metadataCount) -{ - size_t bytesWritten = 0; - ma_bool32 hasListAdtl = MA_FALSE; - ma_bool32 hasListInfo = MA_FALSE; - ma_uint32 iMetadata; - if (pMetadatas == NULL || metadataCount == 0) { - return 0; - } - for (iMetadata = 0; iMetadata < metadataCount; ++iMetadata) { - ma_dr_wav_metadata* pMetadata = &pMetadatas[iMetadata]; - ma_uint32 chunkSize = 0; - if ((pMetadata->type & ma_dr_wav_metadata_type_list_all_info_strings) || (pMetadata->type == ma_dr_wav_metadata_type_unknown && pMetadata->data.unknown.chunkLocation == ma_dr_wav_metadata_location_inside_info_list)) { - hasListInfo = MA_TRUE; - } - if ((pMetadata->type & ma_dr_wav_metadata_type_list_all_adtl) || (pMetadata->type == ma_dr_wav_metadata_type_unknown && pMetadata->data.unknown.chunkLocation == ma_dr_wav_metadata_location_inside_adtl_list)) { - hasListAdtl = MA_TRUE; - } - switch (pMetadata->type) { - case ma_dr_wav_metadata_type_smpl: - { - ma_uint32 iLoop; - chunkSize = MA_DR_WAV_SMPL_BYTES + MA_DR_WAV_SMPL_LOOP_BYTES * pMetadata->data.smpl.sampleLoopCount + pMetadata->data.smpl.samplerSpecificDataSizeInBytes; - bytesWritten += ma_dr_wav__write_or_count(pWav, "smpl", 4); - bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, chunkSize); - bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, pMetadata->data.smpl.manufacturerId); - bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, pMetadata->data.smpl.productId); - bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, pMetadata->data.smpl.samplePeriodNanoseconds); - bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, pMetadata->data.smpl.midiUnityNote); - bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, pMetadata->data.smpl.midiPitchFraction); - bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, pMetadata->data.smpl.smpteFormat); - bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, pMetadata->data.smpl.smpteOffset); - bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, pMetadata->data.smpl.sampleLoopCount); - bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, pMetadata->data.smpl.samplerSpecificDataSizeInBytes); - for (iLoop = 0; iLoop < pMetadata->data.smpl.sampleLoopCount; ++iLoop) { - bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, pMetadata->data.smpl.pLoops[iLoop].cuePointId); - bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, pMetadata->data.smpl.pLoops[iLoop].type); - bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, pMetadata->data.smpl.pLoops[iLoop].firstSampleByteOffset); - bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, pMetadata->data.smpl.pLoops[iLoop].lastSampleByteOffset); - bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, pMetadata->data.smpl.pLoops[iLoop].sampleFraction); - bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, pMetadata->data.smpl.pLoops[iLoop].playCount); - } - if (pMetadata->data.smpl.samplerSpecificDataSizeInBytes > 0) { - bytesWritten += ma_dr_wav__write_or_count(pWav, pMetadata->data.smpl.pSamplerSpecificData, pMetadata->data.smpl.samplerSpecificDataSizeInBytes); - } - } break; - case ma_dr_wav_metadata_type_inst: - { - chunkSize = MA_DR_WAV_INST_BYTES; - bytesWritten += ma_dr_wav__write_or_count(pWav, "inst", 4); - bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, chunkSize); - bytesWritten += ma_dr_wav__write_or_count(pWav, &pMetadata->data.inst.midiUnityNote, 1); - bytesWritten += ma_dr_wav__write_or_count(pWav, &pMetadata->data.inst.fineTuneCents, 1); - bytesWritten += ma_dr_wav__write_or_count(pWav, &pMetadata->data.inst.gainDecibels, 1); - bytesWritten += ma_dr_wav__write_or_count(pWav, &pMetadata->data.inst.lowNote, 1); - bytesWritten += ma_dr_wav__write_or_count(pWav, &pMetadata->data.inst.highNote, 1); - bytesWritten += ma_dr_wav__write_or_count(pWav, &pMetadata->data.inst.lowVelocity, 1); - bytesWritten += ma_dr_wav__write_or_count(pWav, &pMetadata->data.inst.highVelocity, 1); - } break; - case ma_dr_wav_metadata_type_cue: - { - ma_uint32 iCuePoint; - chunkSize = MA_DR_WAV_CUE_BYTES + MA_DR_WAV_CUE_POINT_BYTES * pMetadata->data.cue.cuePointCount; - bytesWritten += ma_dr_wav__write_or_count(pWav, "cue ", 4); - bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, chunkSize); - bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, pMetadata->data.cue.cuePointCount); - for (iCuePoint = 0; iCuePoint < pMetadata->data.cue.cuePointCount; ++iCuePoint) { - bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, pMetadata->data.cue.pCuePoints[iCuePoint].id); - bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, pMetadata->data.cue.pCuePoints[iCuePoint].playOrderPosition); - bytesWritten += ma_dr_wav__write_or_count(pWav, pMetadata->data.cue.pCuePoints[iCuePoint].dataChunkId, 4); - bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, pMetadata->data.cue.pCuePoints[iCuePoint].chunkStart); - bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, pMetadata->data.cue.pCuePoints[iCuePoint].blockStart); - bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, pMetadata->data.cue.pCuePoints[iCuePoint].sampleByteOffset); - } - } break; - case ma_dr_wav_metadata_type_acid: - { - chunkSize = MA_DR_WAV_ACID_BYTES; - bytesWritten += ma_dr_wav__write_or_count(pWav, "acid", 4); - bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, chunkSize); - bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, pMetadata->data.acid.flags); - bytesWritten += ma_dr_wav__write_or_count_u16ne_to_le(pWav, pMetadata->data.acid.midiUnityNote); - bytesWritten += ma_dr_wav__write_or_count_u16ne_to_le(pWav, pMetadata->data.acid.reserved1); - bytesWritten += ma_dr_wav__write_or_count_f32ne_to_le(pWav, pMetadata->data.acid.reserved2); - bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, pMetadata->data.acid.numBeats); - bytesWritten += ma_dr_wav__write_or_count_u16ne_to_le(pWav, pMetadata->data.acid.meterDenominator); - bytesWritten += ma_dr_wav__write_or_count_u16ne_to_le(pWav, pMetadata->data.acid.meterNumerator); - bytesWritten += ma_dr_wav__write_or_count_f32ne_to_le(pWav, pMetadata->data.acid.tempo); - } break; - case ma_dr_wav_metadata_type_bext: - { - char reservedBuf[MA_DR_WAV_BEXT_RESERVED_BYTES]; - ma_uint32 timeReferenceLow; - ma_uint32 timeReferenceHigh; - chunkSize = MA_DR_WAV_BEXT_BYTES + pMetadata->data.bext.codingHistorySize; - bytesWritten += ma_dr_wav__write_or_count(pWav, "bext", 4); - bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, chunkSize); - bytesWritten += ma_dr_wav__write_or_count_string_to_fixed_size_buf(pWav, pMetadata->data.bext.pDescription, MA_DR_WAV_BEXT_DESCRIPTION_BYTES); - bytesWritten += ma_dr_wav__write_or_count_string_to_fixed_size_buf(pWav, pMetadata->data.bext.pOriginatorName, MA_DR_WAV_BEXT_ORIGINATOR_NAME_BYTES); - bytesWritten += ma_dr_wav__write_or_count_string_to_fixed_size_buf(pWav, pMetadata->data.bext.pOriginatorReference, MA_DR_WAV_BEXT_ORIGINATOR_REF_BYTES); - bytesWritten += ma_dr_wav__write_or_count(pWav, pMetadata->data.bext.pOriginationDate, sizeof(pMetadata->data.bext.pOriginationDate)); - bytesWritten += ma_dr_wav__write_or_count(pWav, pMetadata->data.bext.pOriginationTime, sizeof(pMetadata->data.bext.pOriginationTime)); - timeReferenceLow = (ma_uint32)(pMetadata->data.bext.timeReference & 0xFFFFFFFF); - timeReferenceHigh = (ma_uint32)(pMetadata->data.bext.timeReference >> 32); - bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, timeReferenceLow); - bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, timeReferenceHigh); - bytesWritten += ma_dr_wav__write_or_count_u16ne_to_le(pWav, pMetadata->data.bext.version); - bytesWritten += ma_dr_wav__write_or_count(pWav, pMetadata->data.bext.pUMID, MA_DR_WAV_BEXT_UMID_BYTES); - bytesWritten += ma_dr_wav__write_or_count_u16ne_to_le(pWav, pMetadata->data.bext.loudnessValue); - bytesWritten += ma_dr_wav__write_or_count_u16ne_to_le(pWav, pMetadata->data.bext.loudnessRange); - bytesWritten += ma_dr_wav__write_or_count_u16ne_to_le(pWav, pMetadata->data.bext.maxTruePeakLevel); - bytesWritten += ma_dr_wav__write_or_count_u16ne_to_le(pWav, pMetadata->data.bext.maxMomentaryLoudness); - bytesWritten += ma_dr_wav__write_or_count_u16ne_to_le(pWav, pMetadata->data.bext.maxShortTermLoudness); - MA_DR_WAV_ZERO_MEMORY(reservedBuf, sizeof(reservedBuf)); - bytesWritten += ma_dr_wav__write_or_count(pWav, reservedBuf, sizeof(reservedBuf)); - if (pMetadata->data.bext.codingHistorySize > 0) { - bytesWritten += ma_dr_wav__write_or_count(pWav, pMetadata->data.bext.pCodingHistory, pMetadata->data.bext.codingHistorySize); - } - } break; - case ma_dr_wav_metadata_type_unknown: - { - if (pMetadata->data.unknown.chunkLocation == ma_dr_wav_metadata_location_top_level) { - chunkSize = pMetadata->data.unknown.dataSizeInBytes; - bytesWritten += ma_dr_wav__write_or_count(pWav, pMetadata->data.unknown.id, 4); - bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, chunkSize); - bytesWritten += ma_dr_wav__write_or_count(pWav, pMetadata->data.unknown.pData, pMetadata->data.unknown.dataSizeInBytes); - } - } break; - default: break; - } - if ((chunkSize % 2) != 0) { - bytesWritten += ma_dr_wav__write_or_count_byte(pWav, 0); - } - } - if (hasListInfo) { - ma_uint32 chunkSize = 4; - for (iMetadata = 0; iMetadata < metadataCount; ++iMetadata) { - ma_dr_wav_metadata* pMetadata = &pMetadatas[iMetadata]; - if ((pMetadata->type & ma_dr_wav_metadata_type_list_all_info_strings)) { - chunkSize += 8; - chunkSize += pMetadata->data.infoText.stringLength + 1; - } else if (pMetadata->type == ma_dr_wav_metadata_type_unknown && pMetadata->data.unknown.chunkLocation == ma_dr_wav_metadata_location_inside_info_list) { - chunkSize += 8; - chunkSize += pMetadata->data.unknown.dataSizeInBytes; - } - if ((chunkSize % 2) != 0) { - chunkSize += 1; - } - } - bytesWritten += ma_dr_wav__write_or_count(pWav, "LIST", 4); - bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, chunkSize); - bytesWritten += ma_dr_wav__write_or_count(pWav, "INFO", 4); - for (iMetadata = 0; iMetadata < metadataCount; ++iMetadata) { - ma_dr_wav_metadata* pMetadata = &pMetadatas[iMetadata]; - ma_uint32 subchunkSize = 0; - if (pMetadata->type & ma_dr_wav_metadata_type_list_all_info_strings) { - const char* pID = NULL; - switch (pMetadata->type) { - case ma_dr_wav_metadata_type_list_info_software: pID = "ISFT"; break; - case ma_dr_wav_metadata_type_list_info_copyright: pID = "ICOP"; break; - case ma_dr_wav_metadata_type_list_info_title: pID = "INAM"; break; - case ma_dr_wav_metadata_type_list_info_artist: pID = "IART"; break; - case ma_dr_wav_metadata_type_list_info_comment: pID = "ICMT"; break; - case ma_dr_wav_metadata_type_list_info_date: pID = "ICRD"; break; - case ma_dr_wav_metadata_type_list_info_genre: pID = "IGNR"; break; - case ma_dr_wav_metadata_type_list_info_album: pID = "IPRD"; break; - case ma_dr_wav_metadata_type_list_info_tracknumber: pID = "ITRK"; break; - default: break; - } - MA_DR_WAV_ASSERT(pID != NULL); - if (pMetadata->data.infoText.stringLength) { - subchunkSize = pMetadata->data.infoText.stringLength + 1; - bytesWritten += ma_dr_wav__write_or_count(pWav, pID, 4); - bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, subchunkSize); - bytesWritten += ma_dr_wav__write_or_count(pWav, pMetadata->data.infoText.pString, pMetadata->data.infoText.stringLength); - bytesWritten += ma_dr_wav__write_or_count_byte(pWav, '\0'); - } - } else if (pMetadata->type == ma_dr_wav_metadata_type_unknown && pMetadata->data.unknown.chunkLocation == ma_dr_wav_metadata_location_inside_info_list) { - if (pMetadata->data.unknown.dataSizeInBytes) { - subchunkSize = pMetadata->data.unknown.dataSizeInBytes; - bytesWritten += ma_dr_wav__write_or_count(pWav, pMetadata->data.unknown.id, 4); - bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, pMetadata->data.unknown.dataSizeInBytes); - bytesWritten += ma_dr_wav__write_or_count(pWav, pMetadata->data.unknown.pData, subchunkSize); - } - } - if ((subchunkSize % 2) != 0) { - bytesWritten += ma_dr_wav__write_or_count_byte(pWav, 0); - } - } - } - if (hasListAdtl) { - ma_uint32 chunkSize = 4; - for (iMetadata = 0; iMetadata < metadataCount; ++iMetadata) { - ma_dr_wav_metadata* pMetadata = &pMetadatas[iMetadata]; - switch (pMetadata->type) - { - case ma_dr_wav_metadata_type_list_label: - case ma_dr_wav_metadata_type_list_note: - { - chunkSize += 8; - chunkSize += MA_DR_WAV_LIST_LABEL_OR_NOTE_BYTES; - if (pMetadata->data.labelOrNote.stringLength > 0) { - chunkSize += pMetadata->data.labelOrNote.stringLength + 1; - } - } break; - case ma_dr_wav_metadata_type_list_labelled_cue_region: - { - chunkSize += 8; - chunkSize += MA_DR_WAV_LIST_LABELLED_TEXT_BYTES; - if (pMetadata->data.labelledCueRegion.stringLength > 0) { - chunkSize += pMetadata->data.labelledCueRegion.stringLength + 1; - } - } break; - case ma_dr_wav_metadata_type_unknown: - { - if (pMetadata->data.unknown.chunkLocation == ma_dr_wav_metadata_location_inside_adtl_list) { - chunkSize += 8; - chunkSize += pMetadata->data.unknown.dataSizeInBytes; - } - } break; - default: break; - } - if ((chunkSize % 2) != 0) { - chunkSize += 1; - } - } - bytesWritten += ma_dr_wav__write_or_count(pWav, "LIST", 4); - bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, chunkSize); - bytesWritten += ma_dr_wav__write_or_count(pWav, "adtl", 4); - for (iMetadata = 0; iMetadata < metadataCount; ++iMetadata) { - ma_dr_wav_metadata* pMetadata = &pMetadatas[iMetadata]; - ma_uint32 subchunkSize = 0; - switch (pMetadata->type) - { - case ma_dr_wav_metadata_type_list_label: - case ma_dr_wav_metadata_type_list_note: - { - if (pMetadata->data.labelOrNote.stringLength > 0) { - const char *pID = NULL; - if (pMetadata->type == ma_dr_wav_metadata_type_list_label) { - pID = "labl"; - } - else if (pMetadata->type == ma_dr_wav_metadata_type_list_note) { - pID = "note"; - } - MA_DR_WAV_ASSERT(pID != NULL); - MA_DR_WAV_ASSERT(pMetadata->data.labelOrNote.pString != NULL); - subchunkSize = MA_DR_WAV_LIST_LABEL_OR_NOTE_BYTES; - bytesWritten += ma_dr_wav__write_or_count(pWav, pID, 4); - subchunkSize += pMetadata->data.labelOrNote.stringLength + 1; - bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, subchunkSize); - bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, pMetadata->data.labelOrNote.cuePointId); - bytesWritten += ma_dr_wav__write_or_count(pWav, pMetadata->data.labelOrNote.pString, pMetadata->data.labelOrNote.stringLength); - bytesWritten += ma_dr_wav__write_or_count_byte(pWav, '\0'); - } - } break; - case ma_dr_wav_metadata_type_list_labelled_cue_region: - { - subchunkSize = MA_DR_WAV_LIST_LABELLED_TEXT_BYTES; - bytesWritten += ma_dr_wav__write_or_count(pWav, "ltxt", 4); - if (pMetadata->data.labelledCueRegion.stringLength > 0) { - subchunkSize += pMetadata->data.labelledCueRegion.stringLength + 1; - } - bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, subchunkSize); - bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, pMetadata->data.labelledCueRegion.cuePointId); - bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, pMetadata->data.labelledCueRegion.sampleLength); - bytesWritten += ma_dr_wav__write_or_count(pWav, pMetadata->data.labelledCueRegion.purposeId, 4); - bytesWritten += ma_dr_wav__write_or_count_u16ne_to_le(pWav, pMetadata->data.labelledCueRegion.country); - bytesWritten += ma_dr_wav__write_or_count_u16ne_to_le(pWav, pMetadata->data.labelledCueRegion.language); - bytesWritten += ma_dr_wav__write_or_count_u16ne_to_le(pWav, pMetadata->data.labelledCueRegion.dialect); - bytesWritten += ma_dr_wav__write_or_count_u16ne_to_le(pWav, pMetadata->data.labelledCueRegion.codePage); - if (pMetadata->data.labelledCueRegion.stringLength > 0) { - MA_DR_WAV_ASSERT(pMetadata->data.labelledCueRegion.pString != NULL); - bytesWritten += ma_dr_wav__write_or_count(pWav, pMetadata->data.labelledCueRegion.pString, pMetadata->data.labelledCueRegion.stringLength); - bytesWritten += ma_dr_wav__write_or_count_byte(pWav, '\0'); - } - } break; - case ma_dr_wav_metadata_type_unknown: - { - if (pMetadata->data.unknown.chunkLocation == ma_dr_wav_metadata_location_inside_adtl_list) { - subchunkSize = pMetadata->data.unknown.dataSizeInBytes; - MA_DR_WAV_ASSERT(pMetadata->data.unknown.pData != NULL); - bytesWritten += ma_dr_wav__write_or_count(pWav, pMetadata->data.unknown.id, 4); - bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, subchunkSize); - bytesWritten += ma_dr_wav__write_or_count(pWav, pMetadata->data.unknown.pData, subchunkSize); - } - } break; - default: break; - } - if ((subchunkSize % 2) != 0) { - bytesWritten += ma_dr_wav__write_or_count_byte(pWav, 0); - } - } - } - MA_DR_WAV_ASSERT((bytesWritten % 2) == 0); - return bytesWritten; -} -MA_PRIVATE ma_uint32 ma_dr_wav__riff_chunk_size_riff(ma_uint64 dataChunkSize, ma_dr_wav_metadata* pMetadata, ma_uint32 metadataCount) -{ - ma_uint64 chunkSize = 4 + 24 + (ma_uint64)ma_dr_wav__write_or_count_metadata(NULL, pMetadata, metadataCount) + 8 + dataChunkSize + ma_dr_wav__chunk_padding_size_riff(dataChunkSize); - if (chunkSize > 0xFFFFFFFFUL) { - chunkSize = 0xFFFFFFFFUL; - } - return (ma_uint32)chunkSize; -} -MA_PRIVATE ma_uint32 ma_dr_wav__data_chunk_size_riff(ma_uint64 dataChunkSize) -{ - if (dataChunkSize <= 0xFFFFFFFFUL) { - return (ma_uint32)dataChunkSize; - } else { - return 0xFFFFFFFFUL; - } -} -MA_PRIVATE ma_uint64 ma_dr_wav__riff_chunk_size_w64(ma_uint64 dataChunkSize) -{ - ma_uint64 dataSubchunkPaddingSize = ma_dr_wav__chunk_padding_size_w64(dataChunkSize); - return 80 + 24 + dataChunkSize + dataSubchunkPaddingSize; -} -MA_PRIVATE ma_uint64 ma_dr_wav__data_chunk_size_w64(ma_uint64 dataChunkSize) -{ - return 24 + dataChunkSize; -} -MA_PRIVATE ma_uint64 ma_dr_wav__riff_chunk_size_rf64(ma_uint64 dataChunkSize, ma_dr_wav_metadata *metadata, ma_uint32 numMetadata) -{ - ma_uint64 chunkSize = 4 + 36 + 24 + (ma_uint64)ma_dr_wav__write_or_count_metadata(NULL, metadata, numMetadata) + 8 + dataChunkSize + ma_dr_wav__chunk_padding_size_riff(dataChunkSize); - if (chunkSize > 0xFFFFFFFFUL) { - chunkSize = 0xFFFFFFFFUL; - } - return chunkSize; -} -MA_PRIVATE ma_uint64 ma_dr_wav__data_chunk_size_rf64(ma_uint64 dataChunkSize) -{ - return dataChunkSize; -} -MA_PRIVATE ma_bool32 ma_dr_wav_preinit_write(ma_dr_wav* pWav, const ma_dr_wav_data_format* pFormat, ma_bool32 isSequential, ma_dr_wav_write_proc onWrite, ma_dr_wav_seek_proc onSeek, void* pUserData, const ma_allocation_callbacks* pAllocationCallbacks) -{ - if (pWav == NULL || onWrite == NULL) { - return MA_FALSE; - } - if (!isSequential && onSeek == NULL) { - return MA_FALSE; - } - if (pFormat->format == MA_DR_WAVE_FORMAT_EXTENSIBLE) { - return MA_FALSE; - } - if (pFormat->format == MA_DR_WAVE_FORMAT_ADPCM || pFormat->format == MA_DR_WAVE_FORMAT_DVI_ADPCM) { - return MA_FALSE; - } - MA_DR_WAV_ZERO_MEMORY(pWav, sizeof(*pWav)); - pWav->onWrite = onWrite; - pWav->onSeek = onSeek; - pWav->pUserData = pUserData; - pWav->allocationCallbacks = ma_dr_wav_copy_allocation_callbacks_or_defaults(pAllocationCallbacks); - if (pWav->allocationCallbacks.onFree == NULL || (pWav->allocationCallbacks.onMalloc == NULL && pWav->allocationCallbacks.onRealloc == NULL)) { - return MA_FALSE; - } - pWav->fmt.formatTag = (ma_uint16)pFormat->format; - pWav->fmt.channels = (ma_uint16)pFormat->channels; - pWav->fmt.sampleRate = pFormat->sampleRate; - pWav->fmt.avgBytesPerSec = (ma_uint32)((pFormat->bitsPerSample * pFormat->sampleRate * pFormat->channels) / 8); - pWav->fmt.blockAlign = (ma_uint16)((pFormat->channels * pFormat->bitsPerSample) / 8); - pWav->fmt.bitsPerSample = (ma_uint16)pFormat->bitsPerSample; - pWav->fmt.extendedSize = 0; - pWav->isSequentialWrite = isSequential; - return MA_TRUE; -} -MA_PRIVATE ma_bool32 ma_dr_wav_init_write__internal(ma_dr_wav* pWav, const ma_dr_wav_data_format* pFormat, ma_uint64 totalSampleCount) -{ - size_t runningPos = 0; - ma_uint64 initialDataChunkSize = 0; - ma_uint64 chunkSizeFMT; - if (pWav->isSequentialWrite) { - initialDataChunkSize = (totalSampleCount * pWav->fmt.bitsPerSample) / 8; - if (pFormat->container == ma_dr_wav_container_riff) { - if (initialDataChunkSize > (0xFFFFFFFFUL - 36)) { - return MA_FALSE; - } - } - } - pWav->dataChunkDataSizeTargetWrite = initialDataChunkSize; - if (pFormat->container == ma_dr_wav_container_riff) { - ma_uint32 chunkSizeRIFF = 28 + (ma_uint32)initialDataChunkSize; - runningPos += ma_dr_wav__write(pWav, "RIFF", 4); - runningPos += ma_dr_wav__write_u32ne_to_le(pWav, chunkSizeRIFF); - runningPos += ma_dr_wav__write(pWav, "WAVE", 4); - } else if (pFormat->container == ma_dr_wav_container_w64) { - ma_uint64 chunkSizeRIFF = 80 + 24 + initialDataChunkSize; - runningPos += ma_dr_wav__write(pWav, ma_dr_wavGUID_W64_RIFF, 16); - runningPos += ma_dr_wav__write_u64ne_to_le(pWav, chunkSizeRIFF); - runningPos += ma_dr_wav__write(pWav, ma_dr_wavGUID_W64_WAVE, 16); - } else if (pFormat->container == ma_dr_wav_container_rf64) { - runningPos += ma_dr_wav__write(pWav, "RF64", 4); - runningPos += ma_dr_wav__write_u32ne_to_le(pWav, 0xFFFFFFFF); - runningPos += ma_dr_wav__write(pWav, "WAVE", 4); - } else { - return MA_FALSE; - } - if (pFormat->container == ma_dr_wav_container_rf64) { - ma_uint32 initialds64ChunkSize = 28; - ma_uint64 initialRiffChunkSize = 8 + initialds64ChunkSize + initialDataChunkSize; - runningPos += ma_dr_wav__write(pWav, "ds64", 4); - runningPos += ma_dr_wav__write_u32ne_to_le(pWav, initialds64ChunkSize); - runningPos += ma_dr_wav__write_u64ne_to_le(pWav, initialRiffChunkSize); - runningPos += ma_dr_wav__write_u64ne_to_le(pWav, initialDataChunkSize); - runningPos += ma_dr_wav__write_u64ne_to_le(pWav, totalSampleCount); - runningPos += ma_dr_wav__write_u32ne_to_le(pWav, 0); - } - if (pFormat->container == ma_dr_wav_container_riff || pFormat->container == ma_dr_wav_container_rf64) { - chunkSizeFMT = 16; - runningPos += ma_dr_wav__write(pWav, "fmt ", 4); - runningPos += ma_dr_wav__write_u32ne_to_le(pWav, (ma_uint32)chunkSizeFMT); - } else if (pFormat->container == ma_dr_wav_container_w64) { - chunkSizeFMT = 40; - runningPos += ma_dr_wav__write(pWav, ma_dr_wavGUID_W64_FMT, 16); - runningPos += ma_dr_wav__write_u64ne_to_le(pWav, chunkSizeFMT); - } - runningPos += ma_dr_wav__write_u16ne_to_le(pWav, pWav->fmt.formatTag); - runningPos += ma_dr_wav__write_u16ne_to_le(pWav, pWav->fmt.channels); - runningPos += ma_dr_wav__write_u32ne_to_le(pWav, pWav->fmt.sampleRate); - runningPos += ma_dr_wav__write_u32ne_to_le(pWav, pWav->fmt.avgBytesPerSec); - runningPos += ma_dr_wav__write_u16ne_to_le(pWav, pWav->fmt.blockAlign); - runningPos += ma_dr_wav__write_u16ne_to_le(pWav, pWav->fmt.bitsPerSample); - if (!pWav->isSequentialWrite && pWav->pMetadata != NULL && pWav->metadataCount > 0 && (pFormat->container == ma_dr_wav_container_riff || pFormat->container == ma_dr_wav_container_rf64)) { - runningPos += ma_dr_wav__write_or_count_metadata(pWav, pWav->pMetadata, pWav->metadataCount); - } - pWav->dataChunkDataPos = runningPos; - if (pFormat->container == ma_dr_wav_container_riff) { - ma_uint32 chunkSizeDATA = (ma_uint32)initialDataChunkSize; - runningPos += ma_dr_wav__write(pWav, "data", 4); - runningPos += ma_dr_wav__write_u32ne_to_le(pWav, chunkSizeDATA); - } else if (pFormat->container == ma_dr_wav_container_w64) { - ma_uint64 chunkSizeDATA = 24 + initialDataChunkSize; - runningPos += ma_dr_wav__write(pWav, ma_dr_wavGUID_W64_DATA, 16); - runningPos += ma_dr_wav__write_u64ne_to_le(pWav, chunkSizeDATA); - } else if (pFormat->container == ma_dr_wav_container_rf64) { - runningPos += ma_dr_wav__write(pWav, "data", 4); - runningPos += ma_dr_wav__write_u32ne_to_le(pWav, 0xFFFFFFFF); - } - pWav->container = pFormat->container; - pWav->channels = (ma_uint16)pFormat->channels; - pWav->sampleRate = pFormat->sampleRate; - pWav->bitsPerSample = (ma_uint16)pFormat->bitsPerSample; - pWav->translatedFormatTag = (ma_uint16)pFormat->format; - pWav->dataChunkDataPos = runningPos; - return MA_TRUE; -} -MA_API ma_bool32 ma_dr_wav_init_write(ma_dr_wav* pWav, const ma_dr_wav_data_format* pFormat, ma_dr_wav_write_proc onWrite, ma_dr_wav_seek_proc onSeek, void* pUserData, const ma_allocation_callbacks* pAllocationCallbacks) -{ - if (!ma_dr_wav_preinit_write(pWav, pFormat, MA_FALSE, onWrite, onSeek, pUserData, pAllocationCallbacks)) { - return MA_FALSE; - } - return ma_dr_wav_init_write__internal(pWav, pFormat, 0); -} -MA_API ma_bool32 ma_dr_wav_init_write_sequential(ma_dr_wav* pWav, const ma_dr_wav_data_format* pFormat, ma_uint64 totalSampleCount, ma_dr_wav_write_proc onWrite, void* pUserData, const ma_allocation_callbacks* pAllocationCallbacks) -{ - if (!ma_dr_wav_preinit_write(pWav, pFormat, MA_TRUE, onWrite, NULL, pUserData, pAllocationCallbacks)) { - return MA_FALSE; - } - return ma_dr_wav_init_write__internal(pWav, pFormat, totalSampleCount); -} -MA_API ma_bool32 ma_dr_wav_init_write_sequential_pcm_frames(ma_dr_wav* pWav, const ma_dr_wav_data_format* pFormat, ma_uint64 totalPCMFrameCount, ma_dr_wav_write_proc onWrite, void* pUserData, const ma_allocation_callbacks* pAllocationCallbacks) -{ - if (pFormat == NULL) { - return MA_FALSE; - } - return ma_dr_wav_init_write_sequential(pWav, pFormat, totalPCMFrameCount*pFormat->channels, onWrite, pUserData, pAllocationCallbacks); -} -MA_API ma_bool32 ma_dr_wav_init_write_with_metadata(ma_dr_wav* pWav, const ma_dr_wav_data_format* pFormat, ma_dr_wav_write_proc onWrite, ma_dr_wav_seek_proc onSeek, void* pUserData, const ma_allocation_callbacks* pAllocationCallbacks, ma_dr_wav_metadata* pMetadata, ma_uint32 metadataCount) -{ - if (!ma_dr_wav_preinit_write(pWav, pFormat, MA_FALSE, onWrite, onSeek, pUserData, pAllocationCallbacks)) { - return MA_FALSE; - } - pWav->pMetadata = pMetadata; - pWav->metadataCount = metadataCount; - return ma_dr_wav_init_write__internal(pWav, pFormat, 0); -} -MA_API ma_uint64 ma_dr_wav_target_write_size_bytes(const ma_dr_wav_data_format* pFormat, ma_uint64 totalFrameCount, ma_dr_wav_metadata* pMetadata, ma_uint32 metadataCount) -{ - ma_uint64 targetDataSizeBytes = (ma_uint64)((ma_int64)totalFrameCount * pFormat->channels * pFormat->bitsPerSample/8.0); - ma_uint64 riffChunkSizeBytes; - ma_uint64 fileSizeBytes = 0; - if (pFormat->container == ma_dr_wav_container_riff) { - riffChunkSizeBytes = ma_dr_wav__riff_chunk_size_riff(targetDataSizeBytes, pMetadata, metadataCount); - fileSizeBytes = (8 + riffChunkSizeBytes); - } else if (pFormat->container == ma_dr_wav_container_w64) { - riffChunkSizeBytes = ma_dr_wav__riff_chunk_size_w64(targetDataSizeBytes); - fileSizeBytes = riffChunkSizeBytes; - } else if (pFormat->container == ma_dr_wav_container_rf64) { - riffChunkSizeBytes = ma_dr_wav__riff_chunk_size_rf64(targetDataSizeBytes, pMetadata, metadataCount); - fileSizeBytes = (8 + riffChunkSizeBytes); - } - return fileSizeBytes; -} -#ifndef MA_DR_WAV_NO_STDIO -MA_PRIVATE size_t ma_dr_wav__on_read_stdio(void* pUserData, void* pBufferOut, size_t bytesToRead) -{ - return fread(pBufferOut, 1, bytesToRead, (FILE*)pUserData); -} -MA_PRIVATE size_t ma_dr_wav__on_write_stdio(void* pUserData, const void* pData, size_t bytesToWrite) -{ - return fwrite(pData, 1, bytesToWrite, (FILE*)pUserData); -} -MA_PRIVATE ma_bool32 ma_dr_wav__on_seek_stdio(void* pUserData, int offset, ma_dr_wav_seek_origin origin) -{ - return fseek((FILE*)pUserData, offset, (origin == ma_dr_wav_seek_origin_current) ? SEEK_CUR : SEEK_SET) == 0; -} -MA_API ma_bool32 ma_dr_wav_init_file(ma_dr_wav* pWav, const char* filename, const ma_allocation_callbacks* pAllocationCallbacks) -{ - return ma_dr_wav_init_file_ex(pWav, filename, NULL, NULL, 0, pAllocationCallbacks); -} -MA_PRIVATE ma_bool32 ma_dr_wav_init_file__internal_FILE(ma_dr_wav* pWav, FILE* pFile, ma_dr_wav_chunk_proc onChunk, void* pChunkUserData, ma_uint32 flags, const ma_allocation_callbacks* pAllocationCallbacks) -{ - ma_bool32 result; - result = ma_dr_wav_preinit(pWav, ma_dr_wav__on_read_stdio, ma_dr_wav__on_seek_stdio, (void*)pFile, pAllocationCallbacks); - if (result != MA_TRUE) { - fclose(pFile); - return result; - } - result = ma_dr_wav_init__internal(pWav, onChunk, pChunkUserData, flags); - if (result != MA_TRUE) { - fclose(pFile); - return result; - } - return MA_TRUE; -} -MA_API ma_bool32 ma_dr_wav_init_file_ex(ma_dr_wav* pWav, const char* filename, ma_dr_wav_chunk_proc onChunk, void* pChunkUserData, ma_uint32 flags, const ma_allocation_callbacks* pAllocationCallbacks) -{ - FILE* pFile; - if (ma_fopen(&pFile, filename, "rb") != MA_SUCCESS) { - return MA_FALSE; - } - return ma_dr_wav_init_file__internal_FILE(pWav, pFile, onChunk, pChunkUserData, flags, pAllocationCallbacks); -} -#ifndef MA_DR_WAV_NO_WCHAR -MA_API ma_bool32 ma_dr_wav_init_file_w(ma_dr_wav* pWav, const wchar_t* filename, const ma_allocation_callbacks* pAllocationCallbacks) -{ - return ma_dr_wav_init_file_ex_w(pWav, filename, NULL, NULL, 0, pAllocationCallbacks); -} -MA_API ma_bool32 ma_dr_wav_init_file_ex_w(ma_dr_wav* pWav, const wchar_t* filename, ma_dr_wav_chunk_proc onChunk, void* pChunkUserData, ma_uint32 flags, const ma_allocation_callbacks* pAllocationCallbacks) -{ - FILE* pFile; - if (ma_wfopen(&pFile, filename, L"rb", pAllocationCallbacks) != MA_SUCCESS) { - return MA_FALSE; - } - return ma_dr_wav_init_file__internal_FILE(pWav, pFile, onChunk, pChunkUserData, flags, pAllocationCallbacks); -} -#endif -MA_API ma_bool32 ma_dr_wav_init_file_with_metadata(ma_dr_wav* pWav, const char* filename, ma_uint32 flags, const ma_allocation_callbacks* pAllocationCallbacks) -{ - FILE* pFile; - if (ma_fopen(&pFile, filename, "rb") != MA_SUCCESS) { - return MA_FALSE; - } - return ma_dr_wav_init_file__internal_FILE(pWav, pFile, NULL, NULL, flags | MA_DR_WAV_WITH_METADATA, pAllocationCallbacks); -} -#ifndef MA_DR_WAV_NO_WCHAR -MA_API ma_bool32 ma_dr_wav_init_file_with_metadata_w(ma_dr_wav* pWav, const wchar_t* filename, ma_uint32 flags, const ma_allocation_callbacks* pAllocationCallbacks) -{ - FILE* pFile; - if (ma_wfopen(&pFile, filename, L"rb", pAllocationCallbacks) != MA_SUCCESS) { - return MA_FALSE; - } - return ma_dr_wav_init_file__internal_FILE(pWav, pFile, NULL, NULL, flags | MA_DR_WAV_WITH_METADATA, pAllocationCallbacks); -} -#endif -MA_PRIVATE ma_bool32 ma_dr_wav_init_file_write__internal_FILE(ma_dr_wav* pWav, FILE* pFile, const ma_dr_wav_data_format* pFormat, ma_uint64 totalSampleCount, ma_bool32 isSequential, const ma_allocation_callbacks* pAllocationCallbacks) -{ - ma_bool32 result; - result = ma_dr_wav_preinit_write(pWav, pFormat, isSequential, ma_dr_wav__on_write_stdio, ma_dr_wav__on_seek_stdio, (void*)pFile, pAllocationCallbacks); - if (result != MA_TRUE) { - fclose(pFile); - return result; - } - result = ma_dr_wav_init_write__internal(pWav, pFormat, totalSampleCount); - if (result != MA_TRUE) { - fclose(pFile); - return result; - } - return MA_TRUE; -} -MA_PRIVATE ma_bool32 ma_dr_wav_init_file_write__internal(ma_dr_wav* pWav, const char* filename, const ma_dr_wav_data_format* pFormat, ma_uint64 totalSampleCount, ma_bool32 isSequential, const ma_allocation_callbacks* pAllocationCallbacks) -{ - FILE* pFile; - if (ma_fopen(&pFile, filename, "wb") != MA_SUCCESS) { - return MA_FALSE; - } - return ma_dr_wav_init_file_write__internal_FILE(pWav, pFile, pFormat, totalSampleCount, isSequential, pAllocationCallbacks); -} -#ifndef MA_DR_WAV_NO_WCHAR -MA_PRIVATE ma_bool32 ma_dr_wav_init_file_write_w__internal(ma_dr_wav* pWav, const wchar_t* filename, const ma_dr_wav_data_format* pFormat, ma_uint64 totalSampleCount, ma_bool32 isSequential, const ma_allocation_callbacks* pAllocationCallbacks) -{ - FILE* pFile; - if (ma_wfopen(&pFile, filename, L"wb", pAllocationCallbacks) != MA_SUCCESS) { - return MA_FALSE; - } - return ma_dr_wav_init_file_write__internal_FILE(pWav, pFile, pFormat, totalSampleCount, isSequential, pAllocationCallbacks); -} -#endif -MA_API ma_bool32 ma_dr_wav_init_file_write(ma_dr_wav* pWav, const char* filename, const ma_dr_wav_data_format* pFormat, const ma_allocation_callbacks* pAllocationCallbacks) -{ - return ma_dr_wav_init_file_write__internal(pWav, filename, pFormat, 0, MA_FALSE, pAllocationCallbacks); -} -MA_API ma_bool32 ma_dr_wav_init_file_write_sequential(ma_dr_wav* pWav, const char* filename, const ma_dr_wav_data_format* pFormat, ma_uint64 totalSampleCount, const ma_allocation_callbacks* pAllocationCallbacks) -{ - return ma_dr_wav_init_file_write__internal(pWav, filename, pFormat, totalSampleCount, MA_TRUE, pAllocationCallbacks); -} -MA_API ma_bool32 ma_dr_wav_init_file_write_sequential_pcm_frames(ma_dr_wav* pWav, const char* filename, const ma_dr_wav_data_format* pFormat, ma_uint64 totalPCMFrameCount, const ma_allocation_callbacks* pAllocationCallbacks) -{ - if (pFormat == NULL) { - return MA_FALSE; - } - return ma_dr_wav_init_file_write_sequential(pWav, filename, pFormat, totalPCMFrameCount*pFormat->channels, pAllocationCallbacks); -} -#ifndef MA_DR_WAV_NO_WCHAR -MA_API ma_bool32 ma_dr_wav_init_file_write_w(ma_dr_wav* pWav, const wchar_t* filename, const ma_dr_wav_data_format* pFormat, const ma_allocation_callbacks* pAllocationCallbacks) -{ - return ma_dr_wav_init_file_write_w__internal(pWav, filename, pFormat, 0, MA_FALSE, pAllocationCallbacks); -} -MA_API ma_bool32 ma_dr_wav_init_file_write_sequential_w(ma_dr_wav* pWav, const wchar_t* filename, const ma_dr_wav_data_format* pFormat, ma_uint64 totalSampleCount, const ma_allocation_callbacks* pAllocationCallbacks) -{ - return ma_dr_wav_init_file_write_w__internal(pWav, filename, pFormat, totalSampleCount, MA_TRUE, pAllocationCallbacks); -} -MA_API ma_bool32 ma_dr_wav_init_file_write_sequential_pcm_frames_w(ma_dr_wav* pWav, const wchar_t* filename, const ma_dr_wav_data_format* pFormat, ma_uint64 totalPCMFrameCount, const ma_allocation_callbacks* pAllocationCallbacks) -{ - if (pFormat == NULL) { - return MA_FALSE; - } - return ma_dr_wav_init_file_write_sequential_w(pWav, filename, pFormat, totalPCMFrameCount*pFormat->channels, pAllocationCallbacks); -} -#endif -#endif -MA_PRIVATE size_t ma_dr_wav__on_read_memory(void* pUserData, void* pBufferOut, size_t bytesToRead) -{ - ma_dr_wav* pWav = (ma_dr_wav*)pUserData; - size_t bytesRemaining; - MA_DR_WAV_ASSERT(pWav != NULL); - MA_DR_WAV_ASSERT(pWav->memoryStream.dataSize >= pWav->memoryStream.currentReadPos); - bytesRemaining = pWav->memoryStream.dataSize - pWav->memoryStream.currentReadPos; - if (bytesToRead > bytesRemaining) { - bytesToRead = bytesRemaining; - } - if (bytesToRead > 0) { - MA_DR_WAV_COPY_MEMORY(pBufferOut, pWav->memoryStream.data + pWav->memoryStream.currentReadPos, bytesToRead); - pWav->memoryStream.currentReadPos += bytesToRead; - } - return bytesToRead; -} -MA_PRIVATE ma_bool32 ma_dr_wav__on_seek_memory(void* pUserData, int offset, ma_dr_wav_seek_origin origin) -{ - ma_dr_wav* pWav = (ma_dr_wav*)pUserData; - MA_DR_WAV_ASSERT(pWav != NULL); - if (origin == ma_dr_wav_seek_origin_current) { - if (offset > 0) { - if (pWav->memoryStream.currentReadPos + offset > pWav->memoryStream.dataSize) { - return MA_FALSE; - } - } else { - if (pWav->memoryStream.currentReadPos < (size_t)-offset) { - return MA_FALSE; - } - } - pWav->memoryStream.currentReadPos += offset; - } else { - if ((ma_uint32)offset <= pWav->memoryStream.dataSize) { - pWav->memoryStream.currentReadPos = offset; - } else { - return MA_FALSE; - } - } - return MA_TRUE; -} -MA_PRIVATE size_t ma_dr_wav__on_write_memory(void* pUserData, const void* pDataIn, size_t bytesToWrite) -{ - ma_dr_wav* pWav = (ma_dr_wav*)pUserData; - size_t bytesRemaining; - MA_DR_WAV_ASSERT(pWav != NULL); - MA_DR_WAV_ASSERT(pWav->memoryStreamWrite.dataCapacity >= pWav->memoryStreamWrite.currentWritePos); - bytesRemaining = pWav->memoryStreamWrite.dataCapacity - pWav->memoryStreamWrite.currentWritePos; - if (bytesRemaining < bytesToWrite) { - void* pNewData; - size_t newDataCapacity = (pWav->memoryStreamWrite.dataCapacity == 0) ? 256 : pWav->memoryStreamWrite.dataCapacity * 2; - if ((newDataCapacity - pWav->memoryStreamWrite.currentWritePos) < bytesToWrite) { - newDataCapacity = pWav->memoryStreamWrite.currentWritePos + bytesToWrite; - } - pNewData = ma_dr_wav__realloc_from_callbacks(*pWav->memoryStreamWrite.ppData, newDataCapacity, pWav->memoryStreamWrite.dataCapacity, &pWav->allocationCallbacks); - if (pNewData == NULL) { - return 0; - } - *pWav->memoryStreamWrite.ppData = pNewData; - pWav->memoryStreamWrite.dataCapacity = newDataCapacity; - } - MA_DR_WAV_COPY_MEMORY(((ma_uint8*)(*pWav->memoryStreamWrite.ppData)) + pWav->memoryStreamWrite.currentWritePos, pDataIn, bytesToWrite); - pWav->memoryStreamWrite.currentWritePos += bytesToWrite; - if (pWav->memoryStreamWrite.dataSize < pWav->memoryStreamWrite.currentWritePos) { - pWav->memoryStreamWrite.dataSize = pWav->memoryStreamWrite.currentWritePos; - } - *pWav->memoryStreamWrite.pDataSize = pWav->memoryStreamWrite.dataSize; - return bytesToWrite; -} -MA_PRIVATE ma_bool32 ma_dr_wav__on_seek_memory_write(void* pUserData, int offset, ma_dr_wav_seek_origin origin) -{ - ma_dr_wav* pWav = (ma_dr_wav*)pUserData; - MA_DR_WAV_ASSERT(pWav != NULL); - if (origin == ma_dr_wav_seek_origin_current) { - if (offset > 0) { - if (pWav->memoryStreamWrite.currentWritePos + offset > pWav->memoryStreamWrite.dataSize) { - offset = (int)(pWav->memoryStreamWrite.dataSize - pWav->memoryStreamWrite.currentWritePos); - } - } else { - if (pWav->memoryStreamWrite.currentWritePos < (size_t)-offset) { - offset = -(int)pWav->memoryStreamWrite.currentWritePos; - } - } - pWav->memoryStreamWrite.currentWritePos += offset; - } else { - if ((ma_uint32)offset <= pWav->memoryStreamWrite.dataSize) { - pWav->memoryStreamWrite.currentWritePos = offset; - } else { - pWav->memoryStreamWrite.currentWritePos = pWav->memoryStreamWrite.dataSize; - } - } - return MA_TRUE; -} -MA_API ma_bool32 ma_dr_wav_init_memory(ma_dr_wav* pWav, const void* data, size_t dataSize, const ma_allocation_callbacks* pAllocationCallbacks) -{ - return ma_dr_wav_init_memory_ex(pWav, data, dataSize, NULL, NULL, 0, pAllocationCallbacks); -} -MA_API ma_bool32 ma_dr_wav_init_memory_ex(ma_dr_wav* pWav, const void* data, size_t dataSize, ma_dr_wav_chunk_proc onChunk, void* pChunkUserData, ma_uint32 flags, const ma_allocation_callbacks* pAllocationCallbacks) -{ - if (data == NULL || dataSize == 0) { - return MA_FALSE; - } - if (!ma_dr_wav_preinit(pWav, ma_dr_wav__on_read_memory, ma_dr_wav__on_seek_memory, pWav, pAllocationCallbacks)) { - return MA_FALSE; - } - pWav->memoryStream.data = (const ma_uint8*)data; - pWav->memoryStream.dataSize = dataSize; - pWav->memoryStream.currentReadPos = 0; - return ma_dr_wav_init__internal(pWav, onChunk, pChunkUserData, flags); -} -MA_API ma_bool32 ma_dr_wav_init_memory_with_metadata(ma_dr_wav* pWav, const void* data, size_t dataSize, ma_uint32 flags, const ma_allocation_callbacks* pAllocationCallbacks) -{ - if (data == NULL || dataSize == 0) { - return MA_FALSE; - } - if (!ma_dr_wav_preinit(pWav, ma_dr_wav__on_read_memory, ma_dr_wav__on_seek_memory, pWav, pAllocationCallbacks)) { - return MA_FALSE; - } - pWav->memoryStream.data = (const ma_uint8*)data; - pWav->memoryStream.dataSize = dataSize; - pWav->memoryStream.currentReadPos = 0; - return ma_dr_wav_init__internal(pWav, NULL, NULL, flags | MA_DR_WAV_WITH_METADATA); -} -MA_PRIVATE ma_bool32 ma_dr_wav_init_memory_write__internal(ma_dr_wav* pWav, void** ppData, size_t* pDataSize, const ma_dr_wav_data_format* pFormat, ma_uint64 totalSampleCount, ma_bool32 isSequential, const ma_allocation_callbacks* pAllocationCallbacks) -{ - if (ppData == NULL || pDataSize == NULL) { - return MA_FALSE; - } - *ppData = NULL; - *pDataSize = 0; - if (!ma_dr_wav_preinit_write(pWav, pFormat, isSequential, ma_dr_wav__on_write_memory, ma_dr_wav__on_seek_memory_write, pWav, pAllocationCallbacks)) { - return MA_FALSE; - } - pWav->memoryStreamWrite.ppData = ppData; - pWav->memoryStreamWrite.pDataSize = pDataSize; - pWav->memoryStreamWrite.dataSize = 0; - pWav->memoryStreamWrite.dataCapacity = 0; - pWav->memoryStreamWrite.currentWritePos = 0; - return ma_dr_wav_init_write__internal(pWav, pFormat, totalSampleCount); -} -MA_API ma_bool32 ma_dr_wav_init_memory_write(ma_dr_wav* pWav, void** ppData, size_t* pDataSize, const ma_dr_wav_data_format* pFormat, const ma_allocation_callbacks* pAllocationCallbacks) -{ - return ma_dr_wav_init_memory_write__internal(pWav, ppData, pDataSize, pFormat, 0, MA_FALSE, pAllocationCallbacks); -} -MA_API ma_bool32 ma_dr_wav_init_memory_write_sequential(ma_dr_wav* pWav, void** ppData, size_t* pDataSize, const ma_dr_wav_data_format* pFormat, ma_uint64 totalSampleCount, const ma_allocation_callbacks* pAllocationCallbacks) -{ - return ma_dr_wav_init_memory_write__internal(pWav, ppData, pDataSize, pFormat, totalSampleCount, MA_TRUE, pAllocationCallbacks); -} -MA_API ma_bool32 ma_dr_wav_init_memory_write_sequential_pcm_frames(ma_dr_wav* pWav, void** ppData, size_t* pDataSize, const ma_dr_wav_data_format* pFormat, ma_uint64 totalPCMFrameCount, const ma_allocation_callbacks* pAllocationCallbacks) -{ - if (pFormat == NULL) { - return MA_FALSE; - } - return ma_dr_wav_init_memory_write_sequential(pWav, ppData, pDataSize, pFormat, totalPCMFrameCount*pFormat->channels, pAllocationCallbacks); -} -MA_API ma_result ma_dr_wav_uninit(ma_dr_wav* pWav) -{ - ma_result result = MA_SUCCESS; - if (pWav == NULL) { - return MA_INVALID_ARGS; - } - if (pWav->onWrite != NULL) { - ma_uint32 paddingSize = 0; - if (pWav->container == ma_dr_wav_container_riff || pWav->container == ma_dr_wav_container_rf64) { - paddingSize = ma_dr_wav__chunk_padding_size_riff(pWav->dataChunkDataSize); - } else { - paddingSize = ma_dr_wav__chunk_padding_size_w64(pWav->dataChunkDataSize); - } - if (paddingSize > 0) { - ma_uint64 paddingData = 0; - ma_dr_wav__write(pWav, &paddingData, paddingSize); - } - if (pWav->onSeek && !pWav->isSequentialWrite) { - if (pWav->container == ma_dr_wav_container_riff) { - if (pWav->onSeek(pWav->pUserData, 4, ma_dr_wav_seek_origin_start)) { - ma_uint32 riffChunkSize = ma_dr_wav__riff_chunk_size_riff(pWav->dataChunkDataSize, pWav->pMetadata, pWav->metadataCount); - ma_dr_wav__write_u32ne_to_le(pWav, riffChunkSize); - } - if (pWav->onSeek(pWav->pUserData, (int)pWav->dataChunkDataPos - 4, ma_dr_wav_seek_origin_start)) { - ma_uint32 dataChunkSize = ma_dr_wav__data_chunk_size_riff(pWav->dataChunkDataSize); - ma_dr_wav__write_u32ne_to_le(pWav, dataChunkSize); - } - } else if (pWav->container == ma_dr_wav_container_w64) { - if (pWav->onSeek(pWav->pUserData, 16, ma_dr_wav_seek_origin_start)) { - ma_uint64 riffChunkSize = ma_dr_wav__riff_chunk_size_w64(pWav->dataChunkDataSize); - ma_dr_wav__write_u64ne_to_le(pWav, riffChunkSize); - } - if (pWav->onSeek(pWav->pUserData, (int)pWav->dataChunkDataPos - 8, ma_dr_wav_seek_origin_start)) { - ma_uint64 dataChunkSize = ma_dr_wav__data_chunk_size_w64(pWav->dataChunkDataSize); - ma_dr_wav__write_u64ne_to_le(pWav, dataChunkSize); - } - } else if (pWav->container == ma_dr_wav_container_rf64) { - int ds64BodyPos = 12 + 8; - if (pWav->onSeek(pWav->pUserData, ds64BodyPos + 0, ma_dr_wav_seek_origin_start)) { - ma_uint64 riffChunkSize = ma_dr_wav__riff_chunk_size_rf64(pWav->dataChunkDataSize, pWav->pMetadata, pWav->metadataCount); - ma_dr_wav__write_u64ne_to_le(pWav, riffChunkSize); - } - if (pWav->onSeek(pWav->pUserData, ds64BodyPos + 8, ma_dr_wav_seek_origin_start)) { - ma_uint64 dataChunkSize = ma_dr_wav__data_chunk_size_rf64(pWav->dataChunkDataSize); - ma_dr_wav__write_u64ne_to_le(pWav, dataChunkSize); - } - } - } - if (pWav->isSequentialWrite) { - if (pWav->dataChunkDataSize != pWav->dataChunkDataSizeTargetWrite) { - result = MA_INVALID_FILE; - } - } - } else { - ma_dr_wav_free(pWav->pMetadata, &pWav->allocationCallbacks); - } -#ifndef MA_DR_WAV_NO_STDIO - if (pWav->onRead == ma_dr_wav__on_read_stdio || pWav->onWrite == ma_dr_wav__on_write_stdio) { - fclose((FILE*)pWav->pUserData); - } -#endif - return result; -} -MA_API size_t ma_dr_wav_read_raw(ma_dr_wav* pWav, size_t bytesToRead, void* pBufferOut) -{ - size_t bytesRead; - ma_uint32 bytesPerFrame; - if (pWav == NULL || bytesToRead == 0) { - return 0; - } - if (bytesToRead > pWav->bytesRemaining) { - bytesToRead = (size_t)pWav->bytesRemaining; - } - if (bytesToRead == 0) { - return 0; - } - bytesPerFrame = ma_dr_wav_get_bytes_per_pcm_frame(pWav); - if (bytesPerFrame == 0) { - return 0; - } - if (pBufferOut != NULL) { - bytesRead = pWav->onRead(pWav->pUserData, pBufferOut, bytesToRead); - } else { - bytesRead = 0; - while (bytesRead < bytesToRead) { - size_t bytesToSeek = (bytesToRead - bytesRead); - if (bytesToSeek > 0x7FFFFFFF) { - bytesToSeek = 0x7FFFFFFF; - } - if (pWav->onSeek(pWav->pUserData, (int)bytesToSeek, ma_dr_wav_seek_origin_current) == MA_FALSE) { - break; - } - bytesRead += bytesToSeek; - } - while (bytesRead < bytesToRead) { - ma_uint8 buffer[4096]; - size_t bytesSeeked; - size_t bytesToSeek = (bytesToRead - bytesRead); - if (bytesToSeek > sizeof(buffer)) { - bytesToSeek = sizeof(buffer); - } - bytesSeeked = pWav->onRead(pWav->pUserData, buffer, bytesToSeek); - bytesRead += bytesSeeked; - if (bytesSeeked < bytesToSeek) { - break; - } - } - } - pWav->readCursorInPCMFrames += bytesRead / bytesPerFrame; - pWav->bytesRemaining -= bytesRead; - return bytesRead; -} -MA_API ma_uint64 ma_dr_wav_read_pcm_frames_le(ma_dr_wav* pWav, ma_uint64 framesToRead, void* pBufferOut) -{ - ma_uint32 bytesPerFrame; - ma_uint64 bytesToRead; - ma_uint64 framesRemainingInFile; - if (pWav == NULL || framesToRead == 0) { - return 0; - } - if (ma_dr_wav__is_compressed_format_tag(pWav->translatedFormatTag)) { - return 0; - } - framesRemainingInFile = pWav->totalPCMFrameCount - pWav->readCursorInPCMFrames; - if (framesToRead > framesRemainingInFile) { - framesToRead = framesRemainingInFile; - } - bytesPerFrame = ma_dr_wav_get_bytes_per_pcm_frame(pWav); - if (bytesPerFrame == 0) { - return 0; - } - bytesToRead = framesToRead * bytesPerFrame; - if (bytesToRead > MA_SIZE_MAX) { - bytesToRead = (MA_SIZE_MAX / bytesPerFrame) * bytesPerFrame; - } - if (bytesToRead == 0) { - return 0; - } - return ma_dr_wav_read_raw(pWav, (size_t)bytesToRead, pBufferOut) / bytesPerFrame; -} -MA_API ma_uint64 ma_dr_wav_read_pcm_frames_be(ma_dr_wav* pWav, ma_uint64 framesToRead, void* pBufferOut) -{ - ma_uint64 framesRead = ma_dr_wav_read_pcm_frames_le(pWav, framesToRead, pBufferOut); - if (pBufferOut != NULL) { - ma_uint32 bytesPerFrame = ma_dr_wav_get_bytes_per_pcm_frame(pWav); - if (bytesPerFrame == 0) { - return 0; - } - ma_dr_wav__bswap_samples(pBufferOut, framesRead*pWav->channels, bytesPerFrame/pWav->channels); - } - return framesRead; -} -MA_API ma_uint64 ma_dr_wav_read_pcm_frames(ma_dr_wav* pWav, ma_uint64 framesToRead, void* pBufferOut) -{ - ma_uint64 framesRead = 0; - if (ma_dr_wav_is_container_be(pWav->container)) { - if (pWav->container != ma_dr_wav_container_aiff || pWav->aiff.isLE == MA_FALSE) { - if (ma_dr_wav__is_little_endian()) { - framesRead = ma_dr_wav_read_pcm_frames_be(pWav, framesToRead, pBufferOut); - } else { - framesRead = ma_dr_wav_read_pcm_frames_le(pWav, framesToRead, pBufferOut); - } - goto post_process; - } - } - if (ma_dr_wav__is_little_endian()) { - framesRead = ma_dr_wav_read_pcm_frames_le(pWav, framesToRead, pBufferOut); - } else { - framesRead = ma_dr_wav_read_pcm_frames_be(pWav, framesToRead, pBufferOut); - } - post_process: - { - if (pWav->container == ma_dr_wav_container_aiff && pWav->bitsPerSample == 8 && pWav->aiff.isUnsigned == MA_FALSE) { - if (pBufferOut != NULL) { - ma_uint64 iSample; - for (iSample = 0; iSample < framesRead * pWav->channels; iSample += 1) { - ((ma_uint8*)pBufferOut)[iSample] += 128; - } - } - } - } - return framesRead; -} -MA_PRIVATE ma_bool32 ma_dr_wav_seek_to_first_pcm_frame(ma_dr_wav* pWav) -{ - if (pWav->onWrite != NULL) { - return MA_FALSE; - } - if (!pWav->onSeek(pWav->pUserData, (int)pWav->dataChunkDataPos, ma_dr_wav_seek_origin_start)) { - return MA_FALSE; - } - if (ma_dr_wav__is_compressed_format_tag(pWav->translatedFormatTag)) { - if (pWav->translatedFormatTag == MA_DR_WAVE_FORMAT_ADPCM) { - MA_DR_WAV_ZERO_OBJECT(&pWav->msadpcm); - } else if (pWav->translatedFormatTag == MA_DR_WAVE_FORMAT_DVI_ADPCM) { - MA_DR_WAV_ZERO_OBJECT(&pWav->ima); - } else { - MA_DR_WAV_ASSERT(MA_FALSE); - } - } - pWav->readCursorInPCMFrames = 0; - pWav->bytesRemaining = pWav->dataChunkDataSize; - return MA_TRUE; -} -MA_API ma_bool32 ma_dr_wav_seek_to_pcm_frame(ma_dr_wav* pWav, ma_uint64 targetFrameIndex) -{ - if (pWav == NULL || pWav->onSeek == NULL) { - return MA_FALSE; - } - if (pWav->onWrite != NULL) { - return MA_FALSE; - } - if (pWav->totalPCMFrameCount == 0) { - return MA_TRUE; - } - if (targetFrameIndex > pWav->totalPCMFrameCount) { - targetFrameIndex = pWav->totalPCMFrameCount; - } - if (ma_dr_wav__is_compressed_format_tag(pWav->translatedFormatTag)) { - if (targetFrameIndex < pWav->readCursorInPCMFrames) { - if (!ma_dr_wav_seek_to_first_pcm_frame(pWav)) { - return MA_FALSE; - } - } - if (targetFrameIndex > pWav->readCursorInPCMFrames) { - ma_uint64 offsetInFrames = targetFrameIndex - pWav->readCursorInPCMFrames; - ma_int16 devnull[2048]; - while (offsetInFrames > 0) { - ma_uint64 framesRead = 0; - ma_uint64 framesToRead = offsetInFrames; - if (framesToRead > ma_dr_wav_countof(devnull)/pWav->channels) { - framesToRead = ma_dr_wav_countof(devnull)/pWav->channels; - } - if (pWav->translatedFormatTag == MA_DR_WAVE_FORMAT_ADPCM) { - framesRead = ma_dr_wav_read_pcm_frames_s16__msadpcm(pWav, framesToRead, devnull); - } else if (pWav->translatedFormatTag == MA_DR_WAVE_FORMAT_DVI_ADPCM) { - framesRead = ma_dr_wav_read_pcm_frames_s16__ima(pWav, framesToRead, devnull); - } else { - MA_DR_WAV_ASSERT(MA_FALSE); - } - if (framesRead != framesToRead) { - return MA_FALSE; - } - offsetInFrames -= framesRead; - } - } - } else { - ma_uint64 totalSizeInBytes; - ma_uint64 currentBytePos; - ma_uint64 targetBytePos; - ma_uint64 offset; - ma_uint32 bytesPerFrame; - bytesPerFrame = ma_dr_wav_get_bytes_per_pcm_frame(pWav); - if (bytesPerFrame == 0) { - return MA_FALSE; - } - totalSizeInBytes = pWav->totalPCMFrameCount * bytesPerFrame; - currentBytePos = totalSizeInBytes - pWav->bytesRemaining; - targetBytePos = targetFrameIndex * bytesPerFrame; - if (currentBytePos < targetBytePos) { - offset = (targetBytePos - currentBytePos); - } else { - if (!ma_dr_wav_seek_to_first_pcm_frame(pWav)) { - return MA_FALSE; - } - offset = targetBytePos; - } - while (offset > 0) { - int offset32 = ((offset > INT_MAX) ? INT_MAX : (int)offset); - if (!pWav->onSeek(pWav->pUserData, offset32, ma_dr_wav_seek_origin_current)) { - return MA_FALSE; - } - pWav->readCursorInPCMFrames += offset32 / bytesPerFrame; - pWav->bytesRemaining -= offset32; - offset -= offset32; - } - } - return MA_TRUE; -} -MA_API ma_result ma_dr_wav_get_cursor_in_pcm_frames(ma_dr_wav* pWav, ma_uint64* pCursor) -{ - if (pCursor == NULL) { - return MA_INVALID_ARGS; - } - *pCursor = 0; - if (pWav == NULL) { - return MA_INVALID_ARGS; - } - *pCursor = pWav->readCursorInPCMFrames; - return MA_SUCCESS; -} -MA_API ma_result ma_dr_wav_get_length_in_pcm_frames(ma_dr_wav* pWav, ma_uint64* pLength) -{ - if (pLength == NULL) { - return MA_INVALID_ARGS; - } - *pLength = 0; - if (pWav == NULL) { - return MA_INVALID_ARGS; - } - *pLength = pWav->totalPCMFrameCount; - return MA_SUCCESS; -} -MA_API size_t ma_dr_wav_write_raw(ma_dr_wav* pWav, size_t bytesToWrite, const void* pData) -{ - size_t bytesWritten; - if (pWav == NULL || bytesToWrite == 0 || pData == NULL) { - return 0; - } - bytesWritten = pWav->onWrite(pWav->pUserData, pData, bytesToWrite); - pWav->dataChunkDataSize += bytesWritten; - return bytesWritten; -} -MA_API ma_uint64 ma_dr_wav_write_pcm_frames_le(ma_dr_wav* pWav, ma_uint64 framesToWrite, const void* pData) -{ - ma_uint64 bytesToWrite; - ma_uint64 bytesWritten; - const ma_uint8* pRunningData; - if (pWav == NULL || framesToWrite == 0 || pData == NULL) { - return 0; - } - bytesToWrite = ((framesToWrite * pWav->channels * pWav->bitsPerSample) / 8); - if (bytesToWrite > MA_SIZE_MAX) { - return 0; - } - bytesWritten = 0; - pRunningData = (const ma_uint8*)pData; - while (bytesToWrite > 0) { - size_t bytesJustWritten; - ma_uint64 bytesToWriteThisIteration; - bytesToWriteThisIteration = bytesToWrite; - MA_DR_WAV_ASSERT(bytesToWriteThisIteration <= MA_SIZE_MAX); - bytesJustWritten = ma_dr_wav_write_raw(pWav, (size_t)bytesToWriteThisIteration, pRunningData); - if (bytesJustWritten == 0) { - break; - } - bytesToWrite -= bytesJustWritten; - bytesWritten += bytesJustWritten; - pRunningData += bytesJustWritten; - } - return (bytesWritten * 8) / pWav->bitsPerSample / pWav->channels; -} -MA_API ma_uint64 ma_dr_wav_write_pcm_frames_be(ma_dr_wav* pWav, ma_uint64 framesToWrite, const void* pData) -{ - ma_uint64 bytesToWrite; - ma_uint64 bytesWritten; - ma_uint32 bytesPerSample; - const ma_uint8* pRunningData; - if (pWav == NULL || framesToWrite == 0 || pData == NULL) { - return 0; - } - bytesToWrite = ((framesToWrite * pWav->channels * pWav->bitsPerSample) / 8); - if (bytesToWrite > MA_SIZE_MAX) { - return 0; - } - bytesWritten = 0; - pRunningData = (const ma_uint8*)pData; - bytesPerSample = ma_dr_wav_get_bytes_per_pcm_frame(pWav) / pWav->channels; - if (bytesPerSample == 0) { - return 0; - } - while (bytesToWrite > 0) { - ma_uint8 temp[4096]; - ma_uint32 sampleCount; - size_t bytesJustWritten; - ma_uint64 bytesToWriteThisIteration; - bytesToWriteThisIteration = bytesToWrite; - MA_DR_WAV_ASSERT(bytesToWriteThisIteration <= MA_SIZE_MAX); - sampleCount = sizeof(temp)/bytesPerSample; - if (bytesToWriteThisIteration > ((ma_uint64)sampleCount)*bytesPerSample) { - bytesToWriteThisIteration = ((ma_uint64)sampleCount)*bytesPerSample; - } - MA_DR_WAV_COPY_MEMORY(temp, pRunningData, (size_t)bytesToWriteThisIteration); - ma_dr_wav__bswap_samples(temp, sampleCount, bytesPerSample); - bytesJustWritten = ma_dr_wav_write_raw(pWav, (size_t)bytesToWriteThisIteration, temp); - if (bytesJustWritten == 0) { - break; - } - bytesToWrite -= bytesJustWritten; - bytesWritten += bytesJustWritten; - pRunningData += bytesJustWritten; - } - return (bytesWritten * 8) / pWav->bitsPerSample / pWav->channels; -} -MA_API ma_uint64 ma_dr_wav_write_pcm_frames(ma_dr_wav* pWav, ma_uint64 framesToWrite, const void* pData) -{ - if (ma_dr_wav__is_little_endian()) { - return ma_dr_wav_write_pcm_frames_le(pWav, framesToWrite, pData); - } else { - return ma_dr_wav_write_pcm_frames_be(pWav, framesToWrite, pData); - } -} -MA_PRIVATE ma_uint64 ma_dr_wav_read_pcm_frames_s16__msadpcm(ma_dr_wav* pWav, ma_uint64 framesToRead, ma_int16* pBufferOut) -{ - ma_uint64 totalFramesRead = 0; - static ma_int32 adaptationTable[] = { - 230, 230, 230, 230, 307, 409, 512, 614, - 768, 614, 512, 409, 307, 230, 230, 230 - }; - static ma_int32 coeff1Table[] = { 256, 512, 0, 192, 240, 460, 392 }; - static ma_int32 coeff2Table[] = { 0, -256, 0, 64, 0, -208, -232 }; - MA_DR_WAV_ASSERT(pWav != NULL); - MA_DR_WAV_ASSERT(framesToRead > 0); - while (pWav->readCursorInPCMFrames < pWav->totalPCMFrameCount) { - MA_DR_WAV_ASSERT(framesToRead > 0); - if (pWav->msadpcm.cachedFrameCount == 0 && pWav->msadpcm.bytesRemainingInBlock == 0) { - if (pWav->channels == 1) { - ma_uint8 header[7]; - if (pWav->onRead(pWav->pUserData, header, sizeof(header)) != sizeof(header)) { - return totalFramesRead; - } - pWav->msadpcm.bytesRemainingInBlock = pWav->fmt.blockAlign - sizeof(header); - pWav->msadpcm.predictor[0] = header[0]; - pWav->msadpcm.delta[0] = ma_dr_wav_bytes_to_s16(header + 1); - pWav->msadpcm.prevFrames[0][1] = (ma_int32)ma_dr_wav_bytes_to_s16(header + 3); - pWav->msadpcm.prevFrames[0][0] = (ma_int32)ma_dr_wav_bytes_to_s16(header + 5); - pWav->msadpcm.cachedFrames[2] = pWav->msadpcm.prevFrames[0][0]; - pWav->msadpcm.cachedFrames[3] = pWav->msadpcm.prevFrames[0][1]; - pWav->msadpcm.cachedFrameCount = 2; - if (pWav->msadpcm.predictor[0] >= ma_dr_wav_countof(coeff1Table)) { - return totalFramesRead; - } - } else { - ma_uint8 header[14]; - if (pWav->onRead(pWav->pUserData, header, sizeof(header)) != sizeof(header)) { - return totalFramesRead; - } - pWav->msadpcm.bytesRemainingInBlock = pWav->fmt.blockAlign - sizeof(header); - pWav->msadpcm.predictor[0] = header[0]; - pWav->msadpcm.predictor[1] = header[1]; - pWav->msadpcm.delta[0] = ma_dr_wav_bytes_to_s16(header + 2); - pWav->msadpcm.delta[1] = ma_dr_wav_bytes_to_s16(header + 4); - pWav->msadpcm.prevFrames[0][1] = (ma_int32)ma_dr_wav_bytes_to_s16(header + 6); - pWav->msadpcm.prevFrames[1][1] = (ma_int32)ma_dr_wav_bytes_to_s16(header + 8); - pWav->msadpcm.prevFrames[0][0] = (ma_int32)ma_dr_wav_bytes_to_s16(header + 10); - pWav->msadpcm.prevFrames[1][0] = (ma_int32)ma_dr_wav_bytes_to_s16(header + 12); - pWav->msadpcm.cachedFrames[0] = pWav->msadpcm.prevFrames[0][0]; - pWav->msadpcm.cachedFrames[1] = pWav->msadpcm.prevFrames[1][0]; - pWav->msadpcm.cachedFrames[2] = pWav->msadpcm.prevFrames[0][1]; - pWav->msadpcm.cachedFrames[3] = pWav->msadpcm.prevFrames[1][1]; - pWav->msadpcm.cachedFrameCount = 2; - if (pWav->msadpcm.predictor[0] >= ma_dr_wav_countof(coeff1Table) || pWav->msadpcm.predictor[1] >= ma_dr_wav_countof(coeff2Table)) { - return totalFramesRead; - } - } - } - while (framesToRead > 0 && pWav->msadpcm.cachedFrameCount > 0 && pWav->readCursorInPCMFrames < pWav->totalPCMFrameCount) { - if (pBufferOut != NULL) { - ma_uint32 iSample = 0; - for (iSample = 0; iSample < pWav->channels; iSample += 1) { - pBufferOut[iSample] = (ma_int16)pWav->msadpcm.cachedFrames[(ma_dr_wav_countof(pWav->msadpcm.cachedFrames) - (pWav->msadpcm.cachedFrameCount*pWav->channels)) + iSample]; - } - pBufferOut += pWav->channels; - } - framesToRead -= 1; - totalFramesRead += 1; - pWav->readCursorInPCMFrames += 1; - pWav->msadpcm.cachedFrameCount -= 1; - } - if (framesToRead == 0) { - break; - } - if (pWav->msadpcm.cachedFrameCount == 0) { - if (pWav->msadpcm.bytesRemainingInBlock == 0) { - continue; - } else { - ma_uint8 nibbles; - ma_int32 nibble0; - ma_int32 nibble1; - if (pWav->onRead(pWav->pUserData, &nibbles, 1) != 1) { - return totalFramesRead; - } - pWav->msadpcm.bytesRemainingInBlock -= 1; - nibble0 = ((nibbles & 0xF0) >> 4); if ((nibbles & 0x80)) { nibble0 |= 0xFFFFFFF0UL; } - nibble1 = ((nibbles & 0x0F) >> 0); if ((nibbles & 0x08)) { nibble1 |= 0xFFFFFFF0UL; } - if (pWav->channels == 1) { - ma_int32 newSample0; - ma_int32 newSample1; - newSample0 = ((pWav->msadpcm.prevFrames[0][1] * coeff1Table[pWav->msadpcm.predictor[0]]) + (pWav->msadpcm.prevFrames[0][0] * coeff2Table[pWav->msadpcm.predictor[0]])) >> 8; - newSample0 += nibble0 * pWav->msadpcm.delta[0]; - newSample0 = ma_dr_wav_clamp(newSample0, -32768, 32767); - pWav->msadpcm.delta[0] = (adaptationTable[((nibbles & 0xF0) >> 4)] * pWav->msadpcm.delta[0]) >> 8; - if (pWav->msadpcm.delta[0] < 16) { - pWav->msadpcm.delta[0] = 16; - } - pWav->msadpcm.prevFrames[0][0] = pWav->msadpcm.prevFrames[0][1]; - pWav->msadpcm.prevFrames[0][1] = newSample0; - newSample1 = ((pWav->msadpcm.prevFrames[0][1] * coeff1Table[pWav->msadpcm.predictor[0]]) + (pWav->msadpcm.prevFrames[0][0] * coeff2Table[pWav->msadpcm.predictor[0]])) >> 8; - newSample1 += nibble1 * pWav->msadpcm.delta[0]; - newSample1 = ma_dr_wav_clamp(newSample1, -32768, 32767); - pWav->msadpcm.delta[0] = (adaptationTable[((nibbles & 0x0F) >> 0)] * pWav->msadpcm.delta[0]) >> 8; - if (pWav->msadpcm.delta[0] < 16) { - pWav->msadpcm.delta[0] = 16; - } - pWav->msadpcm.prevFrames[0][0] = pWav->msadpcm.prevFrames[0][1]; - pWav->msadpcm.prevFrames[0][1] = newSample1; - pWav->msadpcm.cachedFrames[2] = newSample0; - pWav->msadpcm.cachedFrames[3] = newSample1; - pWav->msadpcm.cachedFrameCount = 2; - } else { - ma_int32 newSample0; - ma_int32 newSample1; - newSample0 = ((pWav->msadpcm.prevFrames[0][1] * coeff1Table[pWav->msadpcm.predictor[0]]) + (pWav->msadpcm.prevFrames[0][0] * coeff2Table[pWav->msadpcm.predictor[0]])) >> 8; - newSample0 += nibble0 * pWav->msadpcm.delta[0]; - newSample0 = ma_dr_wav_clamp(newSample0, -32768, 32767); - pWav->msadpcm.delta[0] = (adaptationTable[((nibbles & 0xF0) >> 4)] * pWav->msadpcm.delta[0]) >> 8; - if (pWav->msadpcm.delta[0] < 16) { - pWav->msadpcm.delta[0] = 16; - } - pWav->msadpcm.prevFrames[0][0] = pWav->msadpcm.prevFrames[0][1]; - pWav->msadpcm.prevFrames[0][1] = newSample0; - newSample1 = ((pWav->msadpcm.prevFrames[1][1] * coeff1Table[pWav->msadpcm.predictor[1]]) + (pWav->msadpcm.prevFrames[1][0] * coeff2Table[pWav->msadpcm.predictor[1]])) >> 8; - newSample1 += nibble1 * pWav->msadpcm.delta[1]; - newSample1 = ma_dr_wav_clamp(newSample1, -32768, 32767); - pWav->msadpcm.delta[1] = (adaptationTable[((nibbles & 0x0F) >> 0)] * pWav->msadpcm.delta[1]) >> 8; - if (pWav->msadpcm.delta[1] < 16) { - pWav->msadpcm.delta[1] = 16; - } - pWav->msadpcm.prevFrames[1][0] = pWav->msadpcm.prevFrames[1][1]; - pWav->msadpcm.prevFrames[1][1] = newSample1; - pWav->msadpcm.cachedFrames[2] = newSample0; - pWav->msadpcm.cachedFrames[3] = newSample1; - pWav->msadpcm.cachedFrameCount = 1; - } - } - } - } - return totalFramesRead; -} -MA_PRIVATE ma_uint64 ma_dr_wav_read_pcm_frames_s16__ima(ma_dr_wav* pWav, ma_uint64 framesToRead, ma_int16* pBufferOut) -{ - ma_uint64 totalFramesRead = 0; - ma_uint32 iChannel; - static ma_int32 indexTable[16] = { - -1, -1, -1, -1, 2, 4, 6, 8, - -1, -1, -1, -1, 2, 4, 6, 8 - }; - static ma_int32 stepTable[89] = { - 7, 8, 9, 10, 11, 12, 13, 14, 16, 17, - 19, 21, 23, 25, 28, 31, 34, 37, 41, 45, - 50, 55, 60, 66, 73, 80, 88, 97, 107, 118, - 130, 143, 157, 173, 190, 209, 230, 253, 279, 307, - 337, 371, 408, 449, 494, 544, 598, 658, 724, 796, - 876, 963, 1060, 1166, 1282, 1411, 1552, 1707, 1878, 2066, - 2272, 2499, 2749, 3024, 3327, 3660, 4026, 4428, 4871, 5358, - 5894, 6484, 7132, 7845, 8630, 9493, 10442, 11487, 12635, 13899, - 15289, 16818, 18500, 20350, 22385, 24623, 27086, 29794, 32767 - }; - MA_DR_WAV_ASSERT(pWav != NULL); - MA_DR_WAV_ASSERT(framesToRead > 0); - while (pWav->readCursorInPCMFrames < pWav->totalPCMFrameCount) { - MA_DR_WAV_ASSERT(framesToRead > 0); - if (pWav->ima.cachedFrameCount == 0 && pWav->ima.bytesRemainingInBlock == 0) { - if (pWav->channels == 1) { - ma_uint8 header[4]; - if (pWav->onRead(pWav->pUserData, header, sizeof(header)) != sizeof(header)) { - return totalFramesRead; - } - pWav->ima.bytesRemainingInBlock = pWav->fmt.blockAlign - sizeof(header); - if (header[2] >= ma_dr_wav_countof(stepTable)) { - pWav->onSeek(pWav->pUserData, pWav->ima.bytesRemainingInBlock, ma_dr_wav_seek_origin_current); - pWav->ima.bytesRemainingInBlock = 0; - return totalFramesRead; - } - pWav->ima.predictor[0] = (ma_int16)ma_dr_wav_bytes_to_u16(header + 0); - pWav->ima.stepIndex[0] = ma_dr_wav_clamp(header[2], 0, (ma_int32)ma_dr_wav_countof(stepTable)-1); - pWav->ima.cachedFrames[ma_dr_wav_countof(pWav->ima.cachedFrames) - 1] = pWav->ima.predictor[0]; - pWav->ima.cachedFrameCount = 1; - } else { - ma_uint8 header[8]; - if (pWav->onRead(pWav->pUserData, header, sizeof(header)) != sizeof(header)) { - return totalFramesRead; - } - pWav->ima.bytesRemainingInBlock = pWav->fmt.blockAlign - sizeof(header); - if (header[2] >= ma_dr_wav_countof(stepTable) || header[6] >= ma_dr_wav_countof(stepTable)) { - pWav->onSeek(pWav->pUserData, pWav->ima.bytesRemainingInBlock, ma_dr_wav_seek_origin_current); - pWav->ima.bytesRemainingInBlock = 0; - return totalFramesRead; - } - pWav->ima.predictor[0] = ma_dr_wav_bytes_to_s16(header + 0); - pWav->ima.stepIndex[0] = ma_dr_wav_clamp(header[2], 0, (ma_int32)ma_dr_wav_countof(stepTable)-1); - pWav->ima.predictor[1] = ma_dr_wav_bytes_to_s16(header + 4); - pWav->ima.stepIndex[1] = ma_dr_wav_clamp(header[6], 0, (ma_int32)ma_dr_wav_countof(stepTable)-1); - pWav->ima.cachedFrames[ma_dr_wav_countof(pWav->ima.cachedFrames) - 2] = pWav->ima.predictor[0]; - pWav->ima.cachedFrames[ma_dr_wav_countof(pWav->ima.cachedFrames) - 1] = pWav->ima.predictor[1]; - pWav->ima.cachedFrameCount = 1; - } - } - while (framesToRead > 0 && pWav->ima.cachedFrameCount > 0 && pWav->readCursorInPCMFrames < pWav->totalPCMFrameCount) { - if (pBufferOut != NULL) { - ma_uint32 iSample; - for (iSample = 0; iSample < pWav->channels; iSample += 1) { - pBufferOut[iSample] = (ma_int16)pWav->ima.cachedFrames[(ma_dr_wav_countof(pWav->ima.cachedFrames) - (pWav->ima.cachedFrameCount*pWav->channels)) + iSample]; - } - pBufferOut += pWav->channels; - } - framesToRead -= 1; - totalFramesRead += 1; - pWav->readCursorInPCMFrames += 1; - pWav->ima.cachedFrameCount -= 1; - } - if (framesToRead == 0) { - break; - } - if (pWav->ima.cachedFrameCount == 0) { - if (pWav->ima.bytesRemainingInBlock == 0) { - continue; - } else { - pWav->ima.cachedFrameCount = 8; - for (iChannel = 0; iChannel < pWav->channels; ++iChannel) { - ma_uint32 iByte; - ma_uint8 nibbles[4]; - if (pWav->onRead(pWav->pUserData, &nibbles, 4) != 4) { - pWav->ima.cachedFrameCount = 0; - return totalFramesRead; - } - pWav->ima.bytesRemainingInBlock -= 4; - for (iByte = 0; iByte < 4; ++iByte) { - ma_uint8 nibble0 = ((nibbles[iByte] & 0x0F) >> 0); - ma_uint8 nibble1 = ((nibbles[iByte] & 0xF0) >> 4); - ma_int32 step = stepTable[pWav->ima.stepIndex[iChannel]]; - ma_int32 predictor = pWav->ima.predictor[iChannel]; - ma_int32 diff = step >> 3; - if (nibble0 & 1) diff += step >> 2; - if (nibble0 & 2) diff += step >> 1; - if (nibble0 & 4) diff += step; - if (nibble0 & 8) diff = -diff; - predictor = ma_dr_wav_clamp(predictor + diff, -32768, 32767); - pWav->ima.predictor[iChannel] = predictor; - pWav->ima.stepIndex[iChannel] = ma_dr_wav_clamp(pWav->ima.stepIndex[iChannel] + indexTable[nibble0], 0, (ma_int32)ma_dr_wav_countof(stepTable)-1); - pWav->ima.cachedFrames[(ma_dr_wav_countof(pWav->ima.cachedFrames) - (pWav->ima.cachedFrameCount*pWav->channels)) + (iByte*2+0)*pWav->channels + iChannel] = predictor; - step = stepTable[pWav->ima.stepIndex[iChannel]]; - predictor = pWav->ima.predictor[iChannel]; - diff = step >> 3; - if (nibble1 & 1) diff += step >> 2; - if (nibble1 & 2) diff += step >> 1; - if (nibble1 & 4) diff += step; - if (nibble1 & 8) diff = -diff; - predictor = ma_dr_wav_clamp(predictor + diff, -32768, 32767); - pWav->ima.predictor[iChannel] = predictor; - pWav->ima.stepIndex[iChannel] = ma_dr_wav_clamp(pWav->ima.stepIndex[iChannel] + indexTable[nibble1], 0, (ma_int32)ma_dr_wav_countof(stepTable)-1); - pWav->ima.cachedFrames[(ma_dr_wav_countof(pWav->ima.cachedFrames) - (pWav->ima.cachedFrameCount*pWav->channels)) + (iByte*2+1)*pWav->channels + iChannel] = predictor; - } - } - } - } - } - return totalFramesRead; -} -#ifndef MA_DR_WAV_NO_CONVERSION_API -static unsigned short g_ma_dr_wavAlawTable[256] = { - 0xEA80, 0xEB80, 0xE880, 0xE980, 0xEE80, 0xEF80, 0xEC80, 0xED80, 0xE280, 0xE380, 0xE080, 0xE180, 0xE680, 0xE780, 0xE480, 0xE580, - 0xF540, 0xF5C0, 0xF440, 0xF4C0, 0xF740, 0xF7C0, 0xF640, 0xF6C0, 0xF140, 0xF1C0, 0xF040, 0xF0C0, 0xF340, 0xF3C0, 0xF240, 0xF2C0, - 0xAA00, 0xAE00, 0xA200, 0xA600, 0xBA00, 0xBE00, 0xB200, 0xB600, 0x8A00, 0x8E00, 0x8200, 0x8600, 0x9A00, 0x9E00, 0x9200, 0x9600, - 0xD500, 0xD700, 0xD100, 0xD300, 0xDD00, 0xDF00, 0xD900, 0xDB00, 0xC500, 0xC700, 0xC100, 0xC300, 0xCD00, 0xCF00, 0xC900, 0xCB00, - 0xFEA8, 0xFEB8, 0xFE88, 0xFE98, 0xFEE8, 0xFEF8, 0xFEC8, 0xFED8, 0xFE28, 0xFE38, 0xFE08, 0xFE18, 0xFE68, 0xFE78, 0xFE48, 0xFE58, - 0xFFA8, 0xFFB8, 0xFF88, 0xFF98, 0xFFE8, 0xFFF8, 0xFFC8, 0xFFD8, 0xFF28, 0xFF38, 0xFF08, 0xFF18, 0xFF68, 0xFF78, 0xFF48, 0xFF58, - 0xFAA0, 0xFAE0, 0xFA20, 0xFA60, 0xFBA0, 0xFBE0, 0xFB20, 0xFB60, 0xF8A0, 0xF8E0, 0xF820, 0xF860, 0xF9A0, 0xF9E0, 0xF920, 0xF960, - 0xFD50, 0xFD70, 0xFD10, 0xFD30, 0xFDD0, 0xFDF0, 0xFD90, 0xFDB0, 0xFC50, 0xFC70, 0xFC10, 0xFC30, 0xFCD0, 0xFCF0, 0xFC90, 0xFCB0, - 0x1580, 0x1480, 0x1780, 0x1680, 0x1180, 0x1080, 0x1380, 0x1280, 0x1D80, 0x1C80, 0x1F80, 0x1E80, 0x1980, 0x1880, 0x1B80, 0x1A80, - 0x0AC0, 0x0A40, 0x0BC0, 0x0B40, 0x08C0, 0x0840, 0x09C0, 0x0940, 0x0EC0, 0x0E40, 0x0FC0, 0x0F40, 0x0CC0, 0x0C40, 0x0DC0, 0x0D40, - 0x5600, 0x5200, 0x5E00, 0x5A00, 0x4600, 0x4200, 0x4E00, 0x4A00, 0x7600, 0x7200, 0x7E00, 0x7A00, 0x6600, 0x6200, 0x6E00, 0x6A00, - 0x2B00, 0x2900, 0x2F00, 0x2D00, 0x2300, 0x2100, 0x2700, 0x2500, 0x3B00, 0x3900, 0x3F00, 0x3D00, 0x3300, 0x3100, 0x3700, 0x3500, - 0x0158, 0x0148, 0x0178, 0x0168, 0x0118, 0x0108, 0x0138, 0x0128, 0x01D8, 0x01C8, 0x01F8, 0x01E8, 0x0198, 0x0188, 0x01B8, 0x01A8, - 0x0058, 0x0048, 0x0078, 0x0068, 0x0018, 0x0008, 0x0038, 0x0028, 0x00D8, 0x00C8, 0x00F8, 0x00E8, 0x0098, 0x0088, 0x00B8, 0x00A8, - 0x0560, 0x0520, 0x05E0, 0x05A0, 0x0460, 0x0420, 0x04E0, 0x04A0, 0x0760, 0x0720, 0x07E0, 0x07A0, 0x0660, 0x0620, 0x06E0, 0x06A0, - 0x02B0, 0x0290, 0x02F0, 0x02D0, 0x0230, 0x0210, 0x0270, 0x0250, 0x03B0, 0x0390, 0x03F0, 0x03D0, 0x0330, 0x0310, 0x0370, 0x0350 -}; -static unsigned short g_ma_dr_wavMulawTable[256] = { - 0x8284, 0x8684, 0x8A84, 0x8E84, 0x9284, 0x9684, 0x9A84, 0x9E84, 0xA284, 0xA684, 0xAA84, 0xAE84, 0xB284, 0xB684, 0xBA84, 0xBE84, - 0xC184, 0xC384, 0xC584, 0xC784, 0xC984, 0xCB84, 0xCD84, 0xCF84, 0xD184, 0xD384, 0xD584, 0xD784, 0xD984, 0xDB84, 0xDD84, 0xDF84, - 0xE104, 0xE204, 0xE304, 0xE404, 0xE504, 0xE604, 0xE704, 0xE804, 0xE904, 0xEA04, 0xEB04, 0xEC04, 0xED04, 0xEE04, 0xEF04, 0xF004, - 0xF0C4, 0xF144, 0xF1C4, 0xF244, 0xF2C4, 0xF344, 0xF3C4, 0xF444, 0xF4C4, 0xF544, 0xF5C4, 0xF644, 0xF6C4, 0xF744, 0xF7C4, 0xF844, - 0xF8A4, 0xF8E4, 0xF924, 0xF964, 0xF9A4, 0xF9E4, 0xFA24, 0xFA64, 0xFAA4, 0xFAE4, 0xFB24, 0xFB64, 0xFBA4, 0xFBE4, 0xFC24, 0xFC64, - 0xFC94, 0xFCB4, 0xFCD4, 0xFCF4, 0xFD14, 0xFD34, 0xFD54, 0xFD74, 0xFD94, 0xFDB4, 0xFDD4, 0xFDF4, 0xFE14, 0xFE34, 0xFE54, 0xFE74, - 0xFE8C, 0xFE9C, 0xFEAC, 0xFEBC, 0xFECC, 0xFEDC, 0xFEEC, 0xFEFC, 0xFF0C, 0xFF1C, 0xFF2C, 0xFF3C, 0xFF4C, 0xFF5C, 0xFF6C, 0xFF7C, - 0xFF88, 0xFF90, 0xFF98, 0xFFA0, 0xFFA8, 0xFFB0, 0xFFB8, 0xFFC0, 0xFFC8, 0xFFD0, 0xFFD8, 0xFFE0, 0xFFE8, 0xFFF0, 0xFFF8, 0x0000, - 0x7D7C, 0x797C, 0x757C, 0x717C, 0x6D7C, 0x697C, 0x657C, 0x617C, 0x5D7C, 0x597C, 0x557C, 0x517C, 0x4D7C, 0x497C, 0x457C, 0x417C, - 0x3E7C, 0x3C7C, 0x3A7C, 0x387C, 0x367C, 0x347C, 0x327C, 0x307C, 0x2E7C, 0x2C7C, 0x2A7C, 0x287C, 0x267C, 0x247C, 0x227C, 0x207C, - 0x1EFC, 0x1DFC, 0x1CFC, 0x1BFC, 0x1AFC, 0x19FC, 0x18FC, 0x17FC, 0x16FC, 0x15FC, 0x14FC, 0x13FC, 0x12FC, 0x11FC, 0x10FC, 0x0FFC, - 0x0F3C, 0x0EBC, 0x0E3C, 0x0DBC, 0x0D3C, 0x0CBC, 0x0C3C, 0x0BBC, 0x0B3C, 0x0ABC, 0x0A3C, 0x09BC, 0x093C, 0x08BC, 0x083C, 0x07BC, - 0x075C, 0x071C, 0x06DC, 0x069C, 0x065C, 0x061C, 0x05DC, 0x059C, 0x055C, 0x051C, 0x04DC, 0x049C, 0x045C, 0x041C, 0x03DC, 0x039C, - 0x036C, 0x034C, 0x032C, 0x030C, 0x02EC, 0x02CC, 0x02AC, 0x028C, 0x026C, 0x024C, 0x022C, 0x020C, 0x01EC, 0x01CC, 0x01AC, 0x018C, - 0x0174, 0x0164, 0x0154, 0x0144, 0x0134, 0x0124, 0x0114, 0x0104, 0x00F4, 0x00E4, 0x00D4, 0x00C4, 0x00B4, 0x00A4, 0x0094, 0x0084, - 0x0078, 0x0070, 0x0068, 0x0060, 0x0058, 0x0050, 0x0048, 0x0040, 0x0038, 0x0030, 0x0028, 0x0020, 0x0018, 0x0010, 0x0008, 0x0000 -}; -static MA_INLINE ma_int16 ma_dr_wav__alaw_to_s16(ma_uint8 sampleIn) -{ - return (short)g_ma_dr_wavAlawTable[sampleIn]; -} -static MA_INLINE ma_int16 ma_dr_wav__mulaw_to_s16(ma_uint8 sampleIn) -{ - return (short)g_ma_dr_wavMulawTable[sampleIn]; -} -MA_PRIVATE void ma_dr_wav__pcm_to_s16(ma_int16* pOut, const ma_uint8* pIn, size_t totalSampleCount, unsigned int bytesPerSample) -{ - size_t i; - if (bytesPerSample == 1) { - ma_dr_wav_u8_to_s16(pOut, pIn, totalSampleCount); - return; - } - if (bytesPerSample == 2) { - for (i = 0; i < totalSampleCount; ++i) { - *pOut++ = ((const ma_int16*)pIn)[i]; - } - return; - } - if (bytesPerSample == 3) { - ma_dr_wav_s24_to_s16(pOut, pIn, totalSampleCount); - return; - } - if (bytesPerSample == 4) { - ma_dr_wav_s32_to_s16(pOut, (const ma_int32*)pIn, totalSampleCount); - return; - } - if (bytesPerSample > 8) { - MA_DR_WAV_ZERO_MEMORY(pOut, totalSampleCount * sizeof(*pOut)); - return; - } - for (i = 0; i < totalSampleCount; ++i) { - ma_uint64 sample = 0; - unsigned int shift = (8 - bytesPerSample) * 8; - unsigned int j; - for (j = 0; j < bytesPerSample; j += 1) { - MA_DR_WAV_ASSERT(j < 8); - sample |= (ma_uint64)(pIn[j]) << shift; - shift += 8; - } - pIn += j; - *pOut++ = (ma_int16)((ma_int64)sample >> 48); - } -} -MA_PRIVATE void ma_dr_wav__ieee_to_s16(ma_int16* pOut, const ma_uint8* pIn, size_t totalSampleCount, unsigned int bytesPerSample) -{ - if (bytesPerSample == 4) { - ma_dr_wav_f32_to_s16(pOut, (const float*)pIn, totalSampleCount); - return; - } else if (bytesPerSample == 8) { - ma_dr_wav_f64_to_s16(pOut, (const double*)pIn, totalSampleCount); - return; - } else { - MA_DR_WAV_ZERO_MEMORY(pOut, totalSampleCount * sizeof(*pOut)); - return; - } -} -MA_PRIVATE ma_uint64 ma_dr_wav_read_pcm_frames_s16__pcm(ma_dr_wav* pWav, ma_uint64 framesToRead, ma_int16* pBufferOut) -{ - ma_uint64 totalFramesRead; - ma_uint8 sampleData[4096] = {0}; - ma_uint32 bytesPerFrame; - ma_uint32 bytesPerSample; - ma_uint64 samplesRead; - if ((pWav->translatedFormatTag == MA_DR_WAVE_FORMAT_PCM && pWav->bitsPerSample == 16) || pBufferOut == NULL) { - return ma_dr_wav_read_pcm_frames(pWav, framesToRead, pBufferOut); - } - bytesPerFrame = ma_dr_wav_get_bytes_per_pcm_frame(pWav); - if (bytesPerFrame == 0) { - return 0; - } - bytesPerSample = bytesPerFrame / pWav->channels; - if (bytesPerSample == 0 || (bytesPerFrame % pWav->channels) != 0) { - return 0; - } - totalFramesRead = 0; - while (framesToRead > 0) { - ma_uint64 framesToReadThisIteration = ma_dr_wav_min(framesToRead, sizeof(sampleData)/bytesPerFrame); - ma_uint64 framesRead = ma_dr_wav_read_pcm_frames(pWav, framesToReadThisIteration, sampleData); - if (framesRead == 0) { - break; - } - MA_DR_WAV_ASSERT(framesRead <= framesToReadThisIteration); - samplesRead = framesRead * pWav->channels; - if ((samplesRead * bytesPerSample) > sizeof(sampleData)) { - MA_DR_WAV_ASSERT(MA_FALSE); - break; - } - ma_dr_wav__pcm_to_s16(pBufferOut, sampleData, (size_t)samplesRead, bytesPerSample); - pBufferOut += samplesRead; - framesToRead -= framesRead; - totalFramesRead += framesRead; - } - return totalFramesRead; -} -MA_PRIVATE ma_uint64 ma_dr_wav_read_pcm_frames_s16__ieee(ma_dr_wav* pWav, ma_uint64 framesToRead, ma_int16* pBufferOut) -{ - ma_uint64 totalFramesRead; - ma_uint8 sampleData[4096] = {0}; - ma_uint32 bytesPerFrame; - ma_uint32 bytesPerSample; - ma_uint64 samplesRead; - if (pBufferOut == NULL) { - return ma_dr_wav_read_pcm_frames(pWav, framesToRead, NULL); - } - bytesPerFrame = ma_dr_wav_get_bytes_per_pcm_frame(pWav); - if (bytesPerFrame == 0) { - return 0; - } - bytesPerSample = bytesPerFrame / pWav->channels; - if (bytesPerSample == 0 || (bytesPerFrame % pWav->channels) != 0) { - return 0; - } - totalFramesRead = 0; - while (framesToRead > 0) { - ma_uint64 framesToReadThisIteration = ma_dr_wav_min(framesToRead, sizeof(sampleData)/bytesPerFrame); - ma_uint64 framesRead = ma_dr_wav_read_pcm_frames(pWav, framesToReadThisIteration, sampleData); - if (framesRead == 0) { - break; - } - MA_DR_WAV_ASSERT(framesRead <= framesToReadThisIteration); - samplesRead = framesRead * pWav->channels; - if ((samplesRead * bytesPerSample) > sizeof(sampleData)) { - MA_DR_WAV_ASSERT(MA_FALSE); - break; - } - ma_dr_wav__ieee_to_s16(pBufferOut, sampleData, (size_t)samplesRead, bytesPerSample); - pBufferOut += samplesRead; - framesToRead -= framesRead; - totalFramesRead += framesRead; - } - return totalFramesRead; -} -MA_PRIVATE ma_uint64 ma_dr_wav_read_pcm_frames_s16__alaw(ma_dr_wav* pWav, ma_uint64 framesToRead, ma_int16* pBufferOut) -{ - ma_uint64 totalFramesRead; - ma_uint8 sampleData[4096] = {0}; - ma_uint32 bytesPerFrame; - ma_uint32 bytesPerSample; - ma_uint64 samplesRead; - if (pBufferOut == NULL) { - return ma_dr_wav_read_pcm_frames(pWav, framesToRead, NULL); - } - bytesPerFrame = ma_dr_wav_get_bytes_per_pcm_frame(pWav); - if (bytesPerFrame == 0) { - return 0; - } - bytesPerSample = bytesPerFrame / pWav->channels; - if (bytesPerSample == 0 || (bytesPerFrame % pWav->channels) != 0) { - return 0; - } - totalFramesRead = 0; - while (framesToRead > 0) { - ma_uint64 framesToReadThisIteration = ma_dr_wav_min(framesToRead, sizeof(sampleData)/bytesPerFrame); - ma_uint64 framesRead = ma_dr_wav_read_pcm_frames(pWav, framesToReadThisIteration, sampleData); - if (framesRead == 0) { - break; - } - MA_DR_WAV_ASSERT(framesRead <= framesToReadThisIteration); - samplesRead = framesRead * pWav->channels; - if ((samplesRead * bytesPerSample) > sizeof(sampleData)) { - MA_DR_WAV_ASSERT(MA_FALSE); - break; - } - ma_dr_wav_alaw_to_s16(pBufferOut, sampleData, (size_t)samplesRead); - #ifdef MA_DR_WAV_LIBSNDFILE_COMPAT - { - if (pWav->container == ma_dr_wav_container_aiff) { - ma_uint64 iSample; - for (iSample = 0; iSample < samplesRead; iSample += 1) { - pBufferOut[iSample] = -pBufferOut[iSample]; - } - } - } - #endif - pBufferOut += samplesRead; - framesToRead -= framesRead; - totalFramesRead += framesRead; - } - return totalFramesRead; -} -MA_PRIVATE ma_uint64 ma_dr_wav_read_pcm_frames_s16__mulaw(ma_dr_wav* pWav, ma_uint64 framesToRead, ma_int16* pBufferOut) -{ - ma_uint64 totalFramesRead; - ma_uint8 sampleData[4096] = {0}; - ma_uint32 bytesPerFrame; - ma_uint32 bytesPerSample; - ma_uint64 samplesRead; - if (pBufferOut == NULL) { - return ma_dr_wav_read_pcm_frames(pWav, framesToRead, NULL); - } - bytesPerFrame = ma_dr_wav_get_bytes_per_pcm_frame(pWav); - if (bytesPerFrame == 0) { - return 0; - } - bytesPerSample = bytesPerFrame / pWav->channels; - if (bytesPerSample == 0 || (bytesPerFrame % pWav->channels) != 0) { - return 0; - } - totalFramesRead = 0; - while (framesToRead > 0) { - ma_uint64 framesToReadThisIteration = ma_dr_wav_min(framesToRead, sizeof(sampleData)/bytesPerFrame); - ma_uint64 framesRead = ma_dr_wav_read_pcm_frames(pWav, framesToReadThisIteration, sampleData); - if (framesRead == 0) { - break; - } - MA_DR_WAV_ASSERT(framesRead <= framesToReadThisIteration); - samplesRead = framesRead * pWav->channels; - if ((samplesRead * bytesPerSample) > sizeof(sampleData)) { - MA_DR_WAV_ASSERT(MA_FALSE); - break; - } - ma_dr_wav_mulaw_to_s16(pBufferOut, sampleData, (size_t)samplesRead); - #ifdef MA_DR_WAV_LIBSNDFILE_COMPAT - { - if (pWav->container == ma_dr_wav_container_aiff) { - ma_uint64 iSample; - for (iSample = 0; iSample < samplesRead; iSample += 1) { - pBufferOut[iSample] = -pBufferOut[iSample]; - } - } - } - #endif - pBufferOut += samplesRead; - framesToRead -= framesRead; - totalFramesRead += framesRead; - } - return totalFramesRead; -} -MA_API ma_uint64 ma_dr_wav_read_pcm_frames_s16(ma_dr_wav* pWav, ma_uint64 framesToRead, ma_int16* pBufferOut) -{ - if (pWav == NULL || framesToRead == 0) { - return 0; - } - if (pBufferOut == NULL) { - return ma_dr_wav_read_pcm_frames(pWav, framesToRead, NULL); - } - if (framesToRead * pWav->channels * sizeof(ma_int16) > MA_SIZE_MAX) { - framesToRead = MA_SIZE_MAX / sizeof(ma_int16) / pWav->channels; - } - if (pWav->translatedFormatTag == MA_DR_WAVE_FORMAT_PCM) { - return ma_dr_wav_read_pcm_frames_s16__pcm(pWav, framesToRead, pBufferOut); - } - if (pWav->translatedFormatTag == MA_DR_WAVE_FORMAT_IEEE_FLOAT) { - return ma_dr_wav_read_pcm_frames_s16__ieee(pWav, framesToRead, pBufferOut); - } - if (pWav->translatedFormatTag == MA_DR_WAVE_FORMAT_ALAW) { - return ma_dr_wav_read_pcm_frames_s16__alaw(pWav, framesToRead, pBufferOut); - } - if (pWav->translatedFormatTag == MA_DR_WAVE_FORMAT_MULAW) { - return ma_dr_wav_read_pcm_frames_s16__mulaw(pWav, framesToRead, pBufferOut); - } - if (pWav->translatedFormatTag == MA_DR_WAVE_FORMAT_ADPCM) { - return ma_dr_wav_read_pcm_frames_s16__msadpcm(pWav, framesToRead, pBufferOut); - } - if (pWav->translatedFormatTag == MA_DR_WAVE_FORMAT_DVI_ADPCM) { - return ma_dr_wav_read_pcm_frames_s16__ima(pWav, framesToRead, pBufferOut); - } - return 0; -} -MA_API ma_uint64 ma_dr_wav_read_pcm_frames_s16le(ma_dr_wav* pWav, ma_uint64 framesToRead, ma_int16* pBufferOut) -{ - ma_uint64 framesRead = ma_dr_wav_read_pcm_frames_s16(pWav, framesToRead, pBufferOut); - if (pBufferOut != NULL && ma_dr_wav__is_little_endian() == MA_FALSE) { - ma_dr_wav__bswap_samples_s16(pBufferOut, framesRead*pWav->channels); - } - return framesRead; -} -MA_API ma_uint64 ma_dr_wav_read_pcm_frames_s16be(ma_dr_wav* pWav, ma_uint64 framesToRead, ma_int16* pBufferOut) -{ - ma_uint64 framesRead = ma_dr_wav_read_pcm_frames_s16(pWav, framesToRead, pBufferOut); - if (pBufferOut != NULL && ma_dr_wav__is_little_endian() == MA_TRUE) { - ma_dr_wav__bswap_samples_s16(pBufferOut, framesRead*pWav->channels); - } - return framesRead; -} -MA_API void ma_dr_wav_u8_to_s16(ma_int16* pOut, const ma_uint8* pIn, size_t sampleCount) -{ - int r; - size_t i; - for (i = 0; i < sampleCount; ++i) { - int x = pIn[i]; - r = x << 8; - r = r - 32768; - pOut[i] = (short)r; - } -} -MA_API void ma_dr_wav_s24_to_s16(ma_int16* pOut, const ma_uint8* pIn, size_t sampleCount) -{ - int r; - size_t i; - for (i = 0; i < sampleCount; ++i) { - int x = ((int)(((unsigned int)(((const ma_uint8*)pIn)[i*3+0]) << 8) | ((unsigned int)(((const ma_uint8*)pIn)[i*3+1]) << 16) | ((unsigned int)(((const ma_uint8*)pIn)[i*3+2])) << 24)) >> 8; - r = x >> 8; - pOut[i] = (short)r; - } -} -MA_API void ma_dr_wav_s32_to_s16(ma_int16* pOut, const ma_int32* pIn, size_t sampleCount) -{ - int r; - size_t i; - for (i = 0; i < sampleCount; ++i) { - int x = pIn[i]; - r = x >> 16; - pOut[i] = (short)r; - } -} -MA_API void ma_dr_wav_f32_to_s16(ma_int16* pOut, const float* pIn, size_t sampleCount) -{ - int r; - size_t i; - for (i = 0; i < sampleCount; ++i) { - float x = pIn[i]; - float c; - c = ((x < -1) ? -1 : ((x > 1) ? 1 : x)); - c = c + 1; - r = (int)(c * 32767.5f); - r = r - 32768; - pOut[i] = (short)r; - } -} -MA_API void ma_dr_wav_f64_to_s16(ma_int16* pOut, const double* pIn, size_t sampleCount) -{ - int r; - size_t i; - for (i = 0; i < sampleCount; ++i) { - double x = pIn[i]; - double c; - c = ((x < -1) ? -1 : ((x > 1) ? 1 : x)); - c = c + 1; - r = (int)(c * 32767.5); - r = r - 32768; - pOut[i] = (short)r; - } -} -MA_API void ma_dr_wav_alaw_to_s16(ma_int16* pOut, const ma_uint8* pIn, size_t sampleCount) -{ - size_t i; - for (i = 0; i < sampleCount; ++i) { - pOut[i] = ma_dr_wav__alaw_to_s16(pIn[i]); - } -} -MA_API void ma_dr_wav_mulaw_to_s16(ma_int16* pOut, const ma_uint8* pIn, size_t sampleCount) -{ - size_t i; - for (i = 0; i < sampleCount; ++i) { - pOut[i] = ma_dr_wav__mulaw_to_s16(pIn[i]); - } -} -MA_PRIVATE void ma_dr_wav__pcm_to_f32(float* pOut, const ma_uint8* pIn, size_t sampleCount, unsigned int bytesPerSample) -{ - unsigned int i; - if (bytesPerSample == 1) { - ma_dr_wav_u8_to_f32(pOut, pIn, sampleCount); - return; - } - if (bytesPerSample == 2) { - ma_dr_wav_s16_to_f32(pOut, (const ma_int16*)pIn, sampleCount); - return; - } - if (bytesPerSample == 3) { - ma_dr_wav_s24_to_f32(pOut, pIn, sampleCount); - return; - } - if (bytesPerSample == 4) { - ma_dr_wav_s32_to_f32(pOut, (const ma_int32*)pIn, sampleCount); - return; - } - if (bytesPerSample > 8) { - MA_DR_WAV_ZERO_MEMORY(pOut, sampleCount * sizeof(*pOut)); - return; - } - for (i = 0; i < sampleCount; ++i) { - ma_uint64 sample = 0; - unsigned int shift = (8 - bytesPerSample) * 8; - unsigned int j; - for (j = 0; j < bytesPerSample; j += 1) { - MA_DR_WAV_ASSERT(j < 8); - sample |= (ma_uint64)(pIn[j]) << shift; - shift += 8; - } - pIn += j; - *pOut++ = (float)((ma_int64)sample / 9223372036854775807.0); - } -} -MA_PRIVATE void ma_dr_wav__ieee_to_f32(float* pOut, const ma_uint8* pIn, size_t sampleCount, unsigned int bytesPerSample) -{ - if (bytesPerSample == 4) { - unsigned int i; - for (i = 0; i < sampleCount; ++i) { - *pOut++ = ((const float*)pIn)[i]; - } - return; - } else if (bytesPerSample == 8) { - ma_dr_wav_f64_to_f32(pOut, (const double*)pIn, sampleCount); - return; - } else { - MA_DR_WAV_ZERO_MEMORY(pOut, sampleCount * sizeof(*pOut)); - return; - } -} -MA_PRIVATE ma_uint64 ma_dr_wav_read_pcm_frames_f32__pcm(ma_dr_wav* pWav, ma_uint64 framesToRead, float* pBufferOut) -{ - ma_uint64 totalFramesRead; - ma_uint8 sampleData[4096] = {0}; - ma_uint32 bytesPerFrame; - ma_uint32 bytesPerSample; - ma_uint64 samplesRead; - bytesPerFrame = ma_dr_wav_get_bytes_per_pcm_frame(pWav); - if (bytesPerFrame == 0) { - return 0; - } - bytesPerSample = bytesPerFrame / pWav->channels; - if (bytesPerSample == 0 || (bytesPerFrame % pWav->channels) != 0) { - return 0; - } - totalFramesRead = 0; - while (framesToRead > 0) { - ma_uint64 framesToReadThisIteration = ma_dr_wav_min(framesToRead, sizeof(sampleData)/bytesPerFrame); - ma_uint64 framesRead = ma_dr_wav_read_pcm_frames(pWav, framesToReadThisIteration, sampleData); - if (framesRead == 0) { - break; - } - MA_DR_WAV_ASSERT(framesRead <= framesToReadThisIteration); - samplesRead = framesRead * pWav->channels; - if ((samplesRead * bytesPerSample) > sizeof(sampleData)) { - MA_DR_WAV_ASSERT(MA_FALSE); - break; - } - ma_dr_wav__pcm_to_f32(pBufferOut, sampleData, (size_t)samplesRead, bytesPerSample); - pBufferOut += samplesRead; - framesToRead -= framesRead; - totalFramesRead += framesRead; - } - return totalFramesRead; -} -MA_PRIVATE ma_uint64 ma_dr_wav_read_pcm_frames_f32__msadpcm_ima(ma_dr_wav* pWav, ma_uint64 framesToRead, float* pBufferOut) -{ - ma_uint64 totalFramesRead; - ma_int16 samples16[2048]; - totalFramesRead = 0; - while (framesToRead > 0) { - ma_uint64 framesToReadThisIteration = ma_dr_wav_min(framesToRead, ma_dr_wav_countof(samples16)/pWav->channels); - ma_uint64 framesRead = ma_dr_wav_read_pcm_frames_s16(pWav, framesToReadThisIteration, samples16); - if (framesRead == 0) { - break; - } - MA_DR_WAV_ASSERT(framesRead <= framesToReadThisIteration); - ma_dr_wav_s16_to_f32(pBufferOut, samples16, (size_t)(framesRead*pWav->channels)); - pBufferOut += framesRead*pWav->channels; - framesToRead -= framesRead; - totalFramesRead += framesRead; - } - return totalFramesRead; -} -MA_PRIVATE ma_uint64 ma_dr_wav_read_pcm_frames_f32__ieee(ma_dr_wav* pWav, ma_uint64 framesToRead, float* pBufferOut) -{ - ma_uint64 totalFramesRead; - ma_uint8 sampleData[4096] = {0}; - ma_uint32 bytesPerFrame; - ma_uint32 bytesPerSample; - ma_uint64 samplesRead; - if (pWav->translatedFormatTag == MA_DR_WAVE_FORMAT_IEEE_FLOAT && pWav->bitsPerSample == 32) { - return ma_dr_wav_read_pcm_frames(pWav, framesToRead, pBufferOut); - } - bytesPerFrame = ma_dr_wav_get_bytes_per_pcm_frame(pWav); - if (bytesPerFrame == 0) { - return 0; - } - bytesPerSample = bytesPerFrame / pWav->channels; - if (bytesPerSample == 0 || (bytesPerFrame % pWav->channels) != 0) { - return 0; - } - totalFramesRead = 0; - while (framesToRead > 0) { - ma_uint64 framesToReadThisIteration = ma_dr_wav_min(framesToRead, sizeof(sampleData)/bytesPerFrame); - ma_uint64 framesRead = ma_dr_wav_read_pcm_frames(pWav, framesToReadThisIteration, sampleData); - if (framesRead == 0) { - break; - } - MA_DR_WAV_ASSERT(framesRead <= framesToReadThisIteration); - samplesRead = framesRead * pWav->channels; - if ((samplesRead * bytesPerSample) > sizeof(sampleData)) { - MA_DR_WAV_ASSERT(MA_FALSE); - break; - } - ma_dr_wav__ieee_to_f32(pBufferOut, sampleData, (size_t)samplesRead, bytesPerSample); - pBufferOut += samplesRead; - framesToRead -= framesRead; - totalFramesRead += framesRead; - } - return totalFramesRead; -} -MA_PRIVATE ma_uint64 ma_dr_wav_read_pcm_frames_f32__alaw(ma_dr_wav* pWav, ma_uint64 framesToRead, float* pBufferOut) -{ - ma_uint64 totalFramesRead; - ma_uint8 sampleData[4096] = {0}; - ma_uint32 bytesPerFrame; - ma_uint32 bytesPerSample; - ma_uint64 samplesRead; - bytesPerFrame = ma_dr_wav_get_bytes_per_pcm_frame(pWav); - if (bytesPerFrame == 0) { - return 0; - } - bytesPerSample = bytesPerFrame / pWav->channels; - if (bytesPerSample == 0 || (bytesPerFrame % pWav->channels) != 0) { - return 0; - } - totalFramesRead = 0; - while (framesToRead > 0) { - ma_uint64 framesToReadThisIteration = ma_dr_wav_min(framesToRead, sizeof(sampleData)/bytesPerFrame); - ma_uint64 framesRead = ma_dr_wav_read_pcm_frames(pWav, framesToReadThisIteration, sampleData); - if (framesRead == 0) { - break; - } - MA_DR_WAV_ASSERT(framesRead <= framesToReadThisIteration); - samplesRead = framesRead * pWav->channels; - if ((samplesRead * bytesPerSample) > sizeof(sampleData)) { - MA_DR_WAV_ASSERT(MA_FALSE); - break; - } - ma_dr_wav_alaw_to_f32(pBufferOut, sampleData, (size_t)samplesRead); - #ifdef MA_DR_WAV_LIBSNDFILE_COMPAT - { - if (pWav->container == ma_dr_wav_container_aiff) { - ma_uint64 iSample; - for (iSample = 0; iSample < samplesRead; iSample += 1) { - pBufferOut[iSample] = -pBufferOut[iSample]; - } - } - } - #endif - pBufferOut += samplesRead; - framesToRead -= framesRead; - totalFramesRead += framesRead; - } - return totalFramesRead; -} -MA_PRIVATE ma_uint64 ma_dr_wav_read_pcm_frames_f32__mulaw(ma_dr_wav* pWav, ma_uint64 framesToRead, float* pBufferOut) -{ - ma_uint64 totalFramesRead; - ma_uint8 sampleData[4096] = {0}; - ma_uint32 bytesPerFrame; - ma_uint32 bytesPerSample; - ma_uint64 samplesRead; - bytesPerFrame = ma_dr_wav_get_bytes_per_pcm_frame(pWav); - if (bytesPerFrame == 0) { - return 0; - } - bytesPerSample = bytesPerFrame / pWav->channels; - if (bytesPerSample == 0 || (bytesPerFrame % pWav->channels) != 0) { - return 0; - } - totalFramesRead = 0; - while (framesToRead > 0) { - ma_uint64 framesToReadThisIteration = ma_dr_wav_min(framesToRead, sizeof(sampleData)/bytesPerFrame); - ma_uint64 framesRead = ma_dr_wav_read_pcm_frames(pWav, framesToReadThisIteration, sampleData); - if (framesRead == 0) { - break; - } - MA_DR_WAV_ASSERT(framesRead <= framesToReadThisIteration); - samplesRead = framesRead * pWav->channels; - if ((samplesRead * bytesPerSample) > sizeof(sampleData)) { - MA_DR_WAV_ASSERT(MA_FALSE); - break; - } - ma_dr_wav_mulaw_to_f32(pBufferOut, sampleData, (size_t)samplesRead); - #ifdef MA_DR_WAV_LIBSNDFILE_COMPAT - { - if (pWav->container == ma_dr_wav_container_aiff) { - ma_uint64 iSample; - for (iSample = 0; iSample < samplesRead; iSample += 1) { - pBufferOut[iSample] = -pBufferOut[iSample]; - } - } - } - #endif - pBufferOut += samplesRead; - framesToRead -= framesRead; - totalFramesRead += framesRead; - } - return totalFramesRead; -} -MA_API ma_uint64 ma_dr_wav_read_pcm_frames_f32(ma_dr_wav* pWav, ma_uint64 framesToRead, float* pBufferOut) -{ - if (pWav == NULL || framesToRead == 0) { - return 0; - } - if (pBufferOut == NULL) { - return ma_dr_wav_read_pcm_frames(pWav, framesToRead, NULL); - } - if (framesToRead * pWav->channels * sizeof(float) > MA_SIZE_MAX) { - framesToRead = MA_SIZE_MAX / sizeof(float) / pWav->channels; - } - if (pWav->translatedFormatTag == MA_DR_WAVE_FORMAT_PCM) { - return ma_dr_wav_read_pcm_frames_f32__pcm(pWav, framesToRead, pBufferOut); - } - if (pWav->translatedFormatTag == MA_DR_WAVE_FORMAT_ADPCM || pWav->translatedFormatTag == MA_DR_WAVE_FORMAT_DVI_ADPCM) { - return ma_dr_wav_read_pcm_frames_f32__msadpcm_ima(pWav, framesToRead, pBufferOut); - } - if (pWav->translatedFormatTag == MA_DR_WAVE_FORMAT_IEEE_FLOAT) { - return ma_dr_wav_read_pcm_frames_f32__ieee(pWav, framesToRead, pBufferOut); - } - if (pWav->translatedFormatTag == MA_DR_WAVE_FORMAT_ALAW) { - return ma_dr_wav_read_pcm_frames_f32__alaw(pWav, framesToRead, pBufferOut); - } - if (pWav->translatedFormatTag == MA_DR_WAVE_FORMAT_MULAW) { - return ma_dr_wav_read_pcm_frames_f32__mulaw(pWav, framesToRead, pBufferOut); - } - return 0; -} -MA_API ma_uint64 ma_dr_wav_read_pcm_frames_f32le(ma_dr_wav* pWav, ma_uint64 framesToRead, float* pBufferOut) -{ - ma_uint64 framesRead = ma_dr_wav_read_pcm_frames_f32(pWav, framesToRead, pBufferOut); - if (pBufferOut != NULL && ma_dr_wav__is_little_endian() == MA_FALSE) { - ma_dr_wav__bswap_samples_f32(pBufferOut, framesRead*pWav->channels); - } - return framesRead; -} -MA_API ma_uint64 ma_dr_wav_read_pcm_frames_f32be(ma_dr_wav* pWav, ma_uint64 framesToRead, float* pBufferOut) -{ - ma_uint64 framesRead = ma_dr_wav_read_pcm_frames_f32(pWav, framesToRead, pBufferOut); - if (pBufferOut != NULL && ma_dr_wav__is_little_endian() == MA_TRUE) { - ma_dr_wav__bswap_samples_f32(pBufferOut, framesRead*pWav->channels); - } - return framesRead; -} -MA_API void ma_dr_wav_u8_to_f32(float* pOut, const ma_uint8* pIn, size_t sampleCount) -{ - size_t i; - if (pOut == NULL || pIn == NULL) { - return; - } -#ifdef MA_DR_WAV_LIBSNDFILE_COMPAT - for (i = 0; i < sampleCount; ++i) { - *pOut++ = (pIn[i] / 256.0f) * 2 - 1; - } -#else - for (i = 0; i < sampleCount; ++i) { - float x = pIn[i]; - x = x * 0.00784313725490196078f; - x = x - 1; - *pOut++ = x; - } -#endif -} -MA_API void ma_dr_wav_s16_to_f32(float* pOut, const ma_int16* pIn, size_t sampleCount) -{ - size_t i; - if (pOut == NULL || pIn == NULL) { - return; - } - for (i = 0; i < sampleCount; ++i) { - *pOut++ = pIn[i] * 0.000030517578125f; - } -} -MA_API void ma_dr_wav_s24_to_f32(float* pOut, const ma_uint8* pIn, size_t sampleCount) -{ - size_t i; - if (pOut == NULL || pIn == NULL) { - return; - } - for (i = 0; i < sampleCount; ++i) { - double x; - ma_uint32 a = ((ma_uint32)(pIn[i*3+0]) << 8); - ma_uint32 b = ((ma_uint32)(pIn[i*3+1]) << 16); - ma_uint32 c = ((ma_uint32)(pIn[i*3+2]) << 24); - x = (double)((ma_int32)(a | b | c) >> 8); - *pOut++ = (float)(x * 0.00000011920928955078125); - } -} -MA_API void ma_dr_wav_s32_to_f32(float* pOut, const ma_int32* pIn, size_t sampleCount) -{ - size_t i; - if (pOut == NULL || pIn == NULL) { - return; - } - for (i = 0; i < sampleCount; ++i) { - *pOut++ = (float)(pIn[i] / 2147483648.0); - } -} -MA_API void ma_dr_wav_f64_to_f32(float* pOut, const double* pIn, size_t sampleCount) -{ - size_t i; - if (pOut == NULL || pIn == NULL) { - return; - } - for (i = 0; i < sampleCount; ++i) { - *pOut++ = (float)pIn[i]; - } -} -MA_API void ma_dr_wav_alaw_to_f32(float* pOut, const ma_uint8* pIn, size_t sampleCount) -{ - size_t i; - if (pOut == NULL || pIn == NULL) { - return; - } - for (i = 0; i < sampleCount; ++i) { - *pOut++ = ma_dr_wav__alaw_to_s16(pIn[i]) / 32768.0f; - } -} -MA_API void ma_dr_wav_mulaw_to_f32(float* pOut, const ma_uint8* pIn, size_t sampleCount) -{ - size_t i; - if (pOut == NULL || pIn == NULL) { - return; - } - for (i = 0; i < sampleCount; ++i) { - *pOut++ = ma_dr_wav__mulaw_to_s16(pIn[i]) / 32768.0f; - } -} -MA_PRIVATE void ma_dr_wav__pcm_to_s32(ma_int32* pOut, const ma_uint8* pIn, size_t totalSampleCount, unsigned int bytesPerSample) -{ - unsigned int i; - if (bytesPerSample == 1) { - ma_dr_wav_u8_to_s32(pOut, pIn, totalSampleCount); - return; - } - if (bytesPerSample == 2) { - ma_dr_wav_s16_to_s32(pOut, (const ma_int16*)pIn, totalSampleCount); - return; - } - if (bytesPerSample == 3) { - ma_dr_wav_s24_to_s32(pOut, pIn, totalSampleCount); - return; - } - if (bytesPerSample == 4) { - for (i = 0; i < totalSampleCount; ++i) { - *pOut++ = ((const ma_int32*)pIn)[i]; - } - return; - } - if (bytesPerSample > 8) { - MA_DR_WAV_ZERO_MEMORY(pOut, totalSampleCount * sizeof(*pOut)); - return; - } - for (i = 0; i < totalSampleCount; ++i) { - ma_uint64 sample = 0; - unsigned int shift = (8 - bytesPerSample) * 8; - unsigned int j; - for (j = 0; j < bytesPerSample; j += 1) { - MA_DR_WAV_ASSERT(j < 8); - sample |= (ma_uint64)(pIn[j]) << shift; - shift += 8; - } - pIn += j; - *pOut++ = (ma_int32)((ma_int64)sample >> 32); - } -} -MA_PRIVATE void ma_dr_wav__ieee_to_s32(ma_int32* pOut, const ma_uint8* pIn, size_t totalSampleCount, unsigned int bytesPerSample) -{ - if (bytesPerSample == 4) { - ma_dr_wav_f32_to_s32(pOut, (const float*)pIn, totalSampleCount); - return; - } else if (bytesPerSample == 8) { - ma_dr_wav_f64_to_s32(pOut, (const double*)pIn, totalSampleCount); - return; - } else { - MA_DR_WAV_ZERO_MEMORY(pOut, totalSampleCount * sizeof(*pOut)); - return; - } -} -MA_PRIVATE ma_uint64 ma_dr_wav_read_pcm_frames_s32__pcm(ma_dr_wav* pWav, ma_uint64 framesToRead, ma_int32* pBufferOut) -{ - ma_uint64 totalFramesRead; - ma_uint8 sampleData[4096] = {0}; - ma_uint32 bytesPerFrame; - ma_uint32 bytesPerSample; - ma_uint64 samplesRead; - if (pWav->translatedFormatTag == MA_DR_WAVE_FORMAT_PCM && pWav->bitsPerSample == 32) { - return ma_dr_wav_read_pcm_frames(pWav, framesToRead, pBufferOut); - } - bytesPerFrame = ma_dr_wav_get_bytes_per_pcm_frame(pWav); - if (bytesPerFrame == 0) { - return 0; - } - bytesPerSample = bytesPerFrame / pWav->channels; - if (bytesPerSample == 0 || (bytesPerFrame % pWav->channels) != 0) { - return 0; - } - totalFramesRead = 0; - while (framesToRead > 0) { - ma_uint64 framesToReadThisIteration = ma_dr_wav_min(framesToRead, sizeof(sampleData)/bytesPerFrame); - ma_uint64 framesRead = ma_dr_wav_read_pcm_frames(pWav, framesToReadThisIteration, sampleData); - if (framesRead == 0) { - break; - } - MA_DR_WAV_ASSERT(framesRead <= framesToReadThisIteration); - samplesRead = framesRead * pWav->channels; - if ((samplesRead * bytesPerSample) > sizeof(sampleData)) { - MA_DR_WAV_ASSERT(MA_FALSE); - break; - } - ma_dr_wav__pcm_to_s32(pBufferOut, sampleData, (size_t)samplesRead, bytesPerSample); - pBufferOut += samplesRead; - framesToRead -= framesRead; - totalFramesRead += framesRead; - } - return totalFramesRead; -} -MA_PRIVATE ma_uint64 ma_dr_wav_read_pcm_frames_s32__msadpcm_ima(ma_dr_wav* pWav, ma_uint64 framesToRead, ma_int32* pBufferOut) -{ - ma_uint64 totalFramesRead = 0; - ma_int16 samples16[2048]; - while (framesToRead > 0) { - ma_uint64 framesToReadThisIteration = ma_dr_wav_min(framesToRead, ma_dr_wav_countof(samples16)/pWav->channels); - ma_uint64 framesRead = ma_dr_wav_read_pcm_frames_s16(pWav, framesToReadThisIteration, samples16); - if (framesRead == 0) { - break; - } - MA_DR_WAV_ASSERT(framesRead <= framesToReadThisIteration); - ma_dr_wav_s16_to_s32(pBufferOut, samples16, (size_t)(framesRead*pWav->channels)); - pBufferOut += framesRead*pWav->channels; - framesToRead -= framesRead; - totalFramesRead += framesRead; - } - return totalFramesRead; -} -MA_PRIVATE ma_uint64 ma_dr_wav_read_pcm_frames_s32__ieee(ma_dr_wav* pWav, ma_uint64 framesToRead, ma_int32* pBufferOut) -{ - ma_uint64 totalFramesRead; - ma_uint8 sampleData[4096] = {0}; - ma_uint32 bytesPerFrame; - ma_uint32 bytesPerSample; - ma_uint64 samplesRead; - bytesPerFrame = ma_dr_wav_get_bytes_per_pcm_frame(pWav); - if (bytesPerFrame == 0) { - return 0; - } - bytesPerSample = bytesPerFrame / pWav->channels; - if (bytesPerSample == 0 || (bytesPerFrame % pWav->channels) != 0) { - return 0; - } - totalFramesRead = 0; - while (framesToRead > 0) { - ma_uint64 framesToReadThisIteration = ma_dr_wav_min(framesToRead, sizeof(sampleData)/bytesPerFrame); - ma_uint64 framesRead = ma_dr_wav_read_pcm_frames(pWav, framesToReadThisIteration, sampleData); - if (framesRead == 0) { - break; - } - MA_DR_WAV_ASSERT(framesRead <= framesToReadThisIteration); - samplesRead = framesRead * pWav->channels; - if ((samplesRead * bytesPerSample) > sizeof(sampleData)) { - MA_DR_WAV_ASSERT(MA_FALSE); - break; - } - ma_dr_wav__ieee_to_s32(pBufferOut, sampleData, (size_t)samplesRead, bytesPerSample); - pBufferOut += samplesRead; - framesToRead -= framesRead; - totalFramesRead += framesRead; - } - return totalFramesRead; -} -MA_PRIVATE ma_uint64 ma_dr_wav_read_pcm_frames_s32__alaw(ma_dr_wav* pWav, ma_uint64 framesToRead, ma_int32* pBufferOut) -{ - ma_uint64 totalFramesRead; - ma_uint8 sampleData[4096] = {0}; - ma_uint32 bytesPerFrame; - ma_uint32 bytesPerSample; - ma_uint64 samplesRead; - bytesPerFrame = ma_dr_wav_get_bytes_per_pcm_frame(pWav); - if (bytesPerFrame == 0) { - return 0; - } - bytesPerSample = bytesPerFrame / pWav->channels; - if (bytesPerSample == 0 || (bytesPerFrame % pWav->channels) != 0) { - return 0; - } - totalFramesRead = 0; - while (framesToRead > 0) { - ma_uint64 framesToReadThisIteration = ma_dr_wav_min(framesToRead, sizeof(sampleData)/bytesPerFrame); - ma_uint64 framesRead = ma_dr_wav_read_pcm_frames(pWav, framesToReadThisIteration, sampleData); - if (framesRead == 0) { - break; - } - MA_DR_WAV_ASSERT(framesRead <= framesToReadThisIteration); - samplesRead = framesRead * pWav->channels; - if ((samplesRead * bytesPerSample) > sizeof(sampleData)) { - MA_DR_WAV_ASSERT(MA_FALSE); - break; - } - ma_dr_wav_alaw_to_s32(pBufferOut, sampleData, (size_t)samplesRead); - #ifdef MA_DR_WAV_LIBSNDFILE_COMPAT - { - if (pWav->container == ma_dr_wav_container_aiff) { - ma_uint64 iSample; - for (iSample = 0; iSample < samplesRead; iSample += 1) { - pBufferOut[iSample] = -pBufferOut[iSample]; - } - } - } - #endif - pBufferOut += samplesRead; - framesToRead -= framesRead; - totalFramesRead += framesRead; - } - return totalFramesRead; -} -MA_PRIVATE ma_uint64 ma_dr_wav_read_pcm_frames_s32__mulaw(ma_dr_wav* pWav, ma_uint64 framesToRead, ma_int32* pBufferOut) -{ - ma_uint64 totalFramesRead; - ma_uint8 sampleData[4096] = {0}; - ma_uint32 bytesPerFrame; - ma_uint32 bytesPerSample; - ma_uint64 samplesRead; - bytesPerFrame = ma_dr_wav_get_bytes_per_pcm_frame(pWav); - if (bytesPerFrame == 0) { - return 0; - } - bytesPerSample = bytesPerFrame / pWav->channels; - if (bytesPerSample == 0 || (bytesPerFrame % pWav->channels) != 0) { - return 0; - } - totalFramesRead = 0; - while (framesToRead > 0) { - ma_uint64 framesToReadThisIteration = ma_dr_wav_min(framesToRead, sizeof(sampleData)/bytesPerFrame); - ma_uint64 framesRead = ma_dr_wav_read_pcm_frames(pWav, framesToReadThisIteration, sampleData); - if (framesRead == 0) { - break; - } - MA_DR_WAV_ASSERT(framesRead <= framesToReadThisIteration); - samplesRead = framesRead * pWav->channels; - if ((samplesRead * bytesPerSample) > sizeof(sampleData)) { - MA_DR_WAV_ASSERT(MA_FALSE); - break; - } - ma_dr_wav_mulaw_to_s32(pBufferOut, sampleData, (size_t)samplesRead); - #ifdef MA_DR_WAV_LIBSNDFILE_COMPAT - { - if (pWav->container == ma_dr_wav_container_aiff) { - ma_uint64 iSample; - for (iSample = 0; iSample < samplesRead; iSample += 1) { - pBufferOut[iSample] = -pBufferOut[iSample]; - } - } - } - #endif - pBufferOut += samplesRead; - framesToRead -= framesRead; - totalFramesRead += framesRead; - } - return totalFramesRead; -} -MA_API ma_uint64 ma_dr_wav_read_pcm_frames_s32(ma_dr_wav* pWav, ma_uint64 framesToRead, ma_int32* pBufferOut) -{ - if (pWav == NULL || framesToRead == 0) { - return 0; - } - if (pBufferOut == NULL) { - return ma_dr_wav_read_pcm_frames(pWav, framesToRead, NULL); - } - if (framesToRead * pWav->channels * sizeof(ma_int32) > MA_SIZE_MAX) { - framesToRead = MA_SIZE_MAX / sizeof(ma_int32) / pWav->channels; - } - if (pWav->translatedFormatTag == MA_DR_WAVE_FORMAT_PCM) { - return ma_dr_wav_read_pcm_frames_s32__pcm(pWav, framesToRead, pBufferOut); - } - if (pWav->translatedFormatTag == MA_DR_WAVE_FORMAT_ADPCM || pWav->translatedFormatTag == MA_DR_WAVE_FORMAT_DVI_ADPCM) { - return ma_dr_wav_read_pcm_frames_s32__msadpcm_ima(pWav, framesToRead, pBufferOut); - } - if (pWav->translatedFormatTag == MA_DR_WAVE_FORMAT_IEEE_FLOAT) { - return ma_dr_wav_read_pcm_frames_s32__ieee(pWav, framesToRead, pBufferOut); - } - if (pWav->translatedFormatTag == MA_DR_WAVE_FORMAT_ALAW) { - return ma_dr_wav_read_pcm_frames_s32__alaw(pWav, framesToRead, pBufferOut); - } - if (pWav->translatedFormatTag == MA_DR_WAVE_FORMAT_MULAW) { - return ma_dr_wav_read_pcm_frames_s32__mulaw(pWav, framesToRead, pBufferOut); - } - return 0; -} -MA_API ma_uint64 ma_dr_wav_read_pcm_frames_s32le(ma_dr_wav* pWav, ma_uint64 framesToRead, ma_int32* pBufferOut) -{ - ma_uint64 framesRead = ma_dr_wav_read_pcm_frames_s32(pWav, framesToRead, pBufferOut); - if (pBufferOut != NULL && ma_dr_wav__is_little_endian() == MA_FALSE) { - ma_dr_wav__bswap_samples_s32(pBufferOut, framesRead*pWav->channels); - } - return framesRead; -} -MA_API ma_uint64 ma_dr_wav_read_pcm_frames_s32be(ma_dr_wav* pWav, ma_uint64 framesToRead, ma_int32* pBufferOut) -{ - ma_uint64 framesRead = ma_dr_wav_read_pcm_frames_s32(pWav, framesToRead, pBufferOut); - if (pBufferOut != NULL && ma_dr_wav__is_little_endian() == MA_TRUE) { - ma_dr_wav__bswap_samples_s32(pBufferOut, framesRead*pWav->channels); - } - return framesRead; -} -MA_API void ma_dr_wav_u8_to_s32(ma_int32* pOut, const ma_uint8* pIn, size_t sampleCount) -{ - size_t i; - if (pOut == NULL || pIn == NULL) { - return; - } - for (i = 0; i < sampleCount; ++i) { - *pOut++ = ((int)pIn[i] - 128) << 24; - } -} -MA_API void ma_dr_wav_s16_to_s32(ma_int32* pOut, const ma_int16* pIn, size_t sampleCount) -{ - size_t i; - if (pOut == NULL || pIn == NULL) { - return; - } - for (i = 0; i < sampleCount; ++i) { - *pOut++ = pIn[i] << 16; - } -} -MA_API void ma_dr_wav_s24_to_s32(ma_int32* pOut, const ma_uint8* pIn, size_t sampleCount) -{ - size_t i; - if (pOut == NULL || pIn == NULL) { - return; - } - for (i = 0; i < sampleCount; ++i) { - unsigned int s0 = pIn[i*3 + 0]; - unsigned int s1 = pIn[i*3 + 1]; - unsigned int s2 = pIn[i*3 + 2]; - ma_int32 sample32 = (ma_int32)((s0 << 8) | (s1 << 16) | (s2 << 24)); - *pOut++ = sample32; - } -} -MA_API void ma_dr_wav_f32_to_s32(ma_int32* pOut, const float* pIn, size_t sampleCount) -{ - size_t i; - if (pOut == NULL || pIn == NULL) { - return; - } - for (i = 0; i < sampleCount; ++i) { - *pOut++ = (ma_int32)(2147483648.0f * pIn[i]); - } -} -MA_API void ma_dr_wav_f64_to_s32(ma_int32* pOut, const double* pIn, size_t sampleCount) -{ - size_t i; - if (pOut == NULL || pIn == NULL) { - return; - } - for (i = 0; i < sampleCount; ++i) { - *pOut++ = (ma_int32)(2147483648.0 * pIn[i]); - } -} -MA_API void ma_dr_wav_alaw_to_s32(ma_int32* pOut, const ma_uint8* pIn, size_t sampleCount) -{ - size_t i; - if (pOut == NULL || pIn == NULL) { - return; - } - for (i = 0; i < sampleCount; ++i) { - *pOut++ = ((ma_int32)ma_dr_wav__alaw_to_s16(pIn[i])) << 16; - } -} -MA_API void ma_dr_wav_mulaw_to_s32(ma_int32* pOut, const ma_uint8* pIn, size_t sampleCount) -{ - size_t i; - if (pOut == NULL || pIn == NULL) { - return; - } - for (i= 0; i < sampleCount; ++i) { - *pOut++ = ((ma_int32)ma_dr_wav__mulaw_to_s16(pIn[i])) << 16; - } -} -MA_PRIVATE ma_int16* ma_dr_wav__read_pcm_frames_and_close_s16(ma_dr_wav* pWav, unsigned int* channels, unsigned int* sampleRate, ma_uint64* totalFrameCount) -{ - ma_uint64 sampleDataSize; - ma_int16* pSampleData; - ma_uint64 framesRead; - MA_DR_WAV_ASSERT(pWav != NULL); - sampleDataSize = pWav->totalPCMFrameCount * pWav->channels * sizeof(ma_int16); - if (sampleDataSize > MA_SIZE_MAX) { - ma_dr_wav_uninit(pWav); - return NULL; - } - pSampleData = (ma_int16*)ma_dr_wav__malloc_from_callbacks((size_t)sampleDataSize, &pWav->allocationCallbacks); - if (pSampleData == NULL) { - ma_dr_wav_uninit(pWav); - return NULL; - } - framesRead = ma_dr_wav_read_pcm_frames_s16(pWav, (size_t)pWav->totalPCMFrameCount, pSampleData); - if (framesRead != pWav->totalPCMFrameCount) { - ma_dr_wav__free_from_callbacks(pSampleData, &pWav->allocationCallbacks); - ma_dr_wav_uninit(pWav); - return NULL; - } - ma_dr_wav_uninit(pWav); - if (sampleRate) { - *sampleRate = pWav->sampleRate; - } - if (channels) { - *channels = pWav->channels; - } - if (totalFrameCount) { - *totalFrameCount = pWav->totalPCMFrameCount; - } - return pSampleData; -} -MA_PRIVATE float* ma_dr_wav__read_pcm_frames_and_close_f32(ma_dr_wav* pWav, unsigned int* channels, unsigned int* sampleRate, ma_uint64* totalFrameCount) -{ - ma_uint64 sampleDataSize; - float* pSampleData; - ma_uint64 framesRead; - MA_DR_WAV_ASSERT(pWav != NULL); - sampleDataSize = pWav->totalPCMFrameCount * pWav->channels * sizeof(float); - if (sampleDataSize > MA_SIZE_MAX) { - ma_dr_wav_uninit(pWav); - return NULL; - } - pSampleData = (float*)ma_dr_wav__malloc_from_callbacks((size_t)sampleDataSize, &pWav->allocationCallbacks); - if (pSampleData == NULL) { - ma_dr_wav_uninit(pWav); - return NULL; - } - framesRead = ma_dr_wav_read_pcm_frames_f32(pWav, (size_t)pWav->totalPCMFrameCount, pSampleData); - if (framesRead != pWav->totalPCMFrameCount) { - ma_dr_wav__free_from_callbacks(pSampleData, &pWav->allocationCallbacks); - ma_dr_wav_uninit(pWav); - return NULL; - } - ma_dr_wav_uninit(pWav); - if (sampleRate) { - *sampleRate = pWav->sampleRate; - } - if (channels) { - *channels = pWav->channels; - } - if (totalFrameCount) { - *totalFrameCount = pWav->totalPCMFrameCount; - } - return pSampleData; -} -MA_PRIVATE ma_int32* ma_dr_wav__read_pcm_frames_and_close_s32(ma_dr_wav* pWav, unsigned int* channels, unsigned int* sampleRate, ma_uint64* totalFrameCount) -{ - ma_uint64 sampleDataSize; - ma_int32* pSampleData; - ma_uint64 framesRead; - MA_DR_WAV_ASSERT(pWav != NULL); - sampleDataSize = pWav->totalPCMFrameCount * pWav->channels * sizeof(ma_int32); - if (sampleDataSize > MA_SIZE_MAX) { - ma_dr_wav_uninit(pWav); - return NULL; - } - pSampleData = (ma_int32*)ma_dr_wav__malloc_from_callbacks((size_t)sampleDataSize, &pWav->allocationCallbacks); - if (pSampleData == NULL) { - ma_dr_wav_uninit(pWav); - return NULL; - } - framesRead = ma_dr_wav_read_pcm_frames_s32(pWav, (size_t)pWav->totalPCMFrameCount, pSampleData); - if (framesRead != pWav->totalPCMFrameCount) { - ma_dr_wav__free_from_callbacks(pSampleData, &pWav->allocationCallbacks); - ma_dr_wav_uninit(pWav); - return NULL; - } - ma_dr_wav_uninit(pWav); - if (sampleRate) { - *sampleRate = pWav->sampleRate; - } - if (channels) { - *channels = pWav->channels; - } - if (totalFrameCount) { - *totalFrameCount = pWav->totalPCMFrameCount; - } - return pSampleData; -} -MA_API ma_int16* ma_dr_wav_open_and_read_pcm_frames_s16(ma_dr_wav_read_proc onRead, ma_dr_wav_seek_proc onSeek, void* pUserData, unsigned int* channelsOut, unsigned int* sampleRateOut, ma_uint64* totalFrameCountOut, const ma_allocation_callbacks* pAllocationCallbacks) -{ - ma_dr_wav wav; - if (channelsOut) { - *channelsOut = 0; - } - if (sampleRateOut) { - *sampleRateOut = 0; - } - if (totalFrameCountOut) { - *totalFrameCountOut = 0; - } - if (!ma_dr_wav_init(&wav, onRead, onSeek, pUserData, pAllocationCallbacks)) { - return NULL; - } - return ma_dr_wav__read_pcm_frames_and_close_s16(&wav, channelsOut, sampleRateOut, totalFrameCountOut); -} -MA_API float* ma_dr_wav_open_and_read_pcm_frames_f32(ma_dr_wav_read_proc onRead, ma_dr_wav_seek_proc onSeek, void* pUserData, unsigned int* channelsOut, unsigned int* sampleRateOut, ma_uint64* totalFrameCountOut, const ma_allocation_callbacks* pAllocationCallbacks) -{ - ma_dr_wav wav; - if (channelsOut) { - *channelsOut = 0; - } - if (sampleRateOut) { - *sampleRateOut = 0; - } - if (totalFrameCountOut) { - *totalFrameCountOut = 0; - } - if (!ma_dr_wav_init(&wav, onRead, onSeek, pUserData, pAllocationCallbacks)) { - return NULL; - } - return ma_dr_wav__read_pcm_frames_and_close_f32(&wav, channelsOut, sampleRateOut, totalFrameCountOut); -} -MA_API ma_int32* ma_dr_wav_open_and_read_pcm_frames_s32(ma_dr_wav_read_proc onRead, ma_dr_wav_seek_proc onSeek, void* pUserData, unsigned int* channelsOut, unsigned int* sampleRateOut, ma_uint64* totalFrameCountOut, const ma_allocation_callbacks* pAllocationCallbacks) -{ - ma_dr_wav wav; - if (channelsOut) { - *channelsOut = 0; - } - if (sampleRateOut) { - *sampleRateOut = 0; - } - if (totalFrameCountOut) { - *totalFrameCountOut = 0; - } - if (!ma_dr_wav_init(&wav, onRead, onSeek, pUserData, pAllocationCallbacks)) { - return NULL; - } - return ma_dr_wav__read_pcm_frames_and_close_s32(&wav, channelsOut, sampleRateOut, totalFrameCountOut); -} -#ifndef MA_DR_WAV_NO_STDIO -MA_API ma_int16* ma_dr_wav_open_file_and_read_pcm_frames_s16(const char* filename, unsigned int* channelsOut, unsigned int* sampleRateOut, ma_uint64* totalFrameCountOut, const ma_allocation_callbacks* pAllocationCallbacks) -{ - ma_dr_wav wav; - if (channelsOut) { - *channelsOut = 0; - } - if (sampleRateOut) { - *sampleRateOut = 0; - } - if (totalFrameCountOut) { - *totalFrameCountOut = 0; - } - if (!ma_dr_wav_init_file(&wav, filename, pAllocationCallbacks)) { - return NULL; - } - return ma_dr_wav__read_pcm_frames_and_close_s16(&wav, channelsOut, sampleRateOut, totalFrameCountOut); -} -MA_API float* ma_dr_wav_open_file_and_read_pcm_frames_f32(const char* filename, unsigned int* channelsOut, unsigned int* sampleRateOut, ma_uint64* totalFrameCountOut, const ma_allocation_callbacks* pAllocationCallbacks) -{ - ma_dr_wav wav; - if (channelsOut) { - *channelsOut = 0; - } - if (sampleRateOut) { - *sampleRateOut = 0; - } - if (totalFrameCountOut) { - *totalFrameCountOut = 0; - } - if (!ma_dr_wav_init_file(&wav, filename, pAllocationCallbacks)) { - return NULL; - } - return ma_dr_wav__read_pcm_frames_and_close_f32(&wav, channelsOut, sampleRateOut, totalFrameCountOut); -} -MA_API ma_int32* ma_dr_wav_open_file_and_read_pcm_frames_s32(const char* filename, unsigned int* channelsOut, unsigned int* sampleRateOut, ma_uint64* totalFrameCountOut, const ma_allocation_callbacks* pAllocationCallbacks) -{ - ma_dr_wav wav; - if (channelsOut) { - *channelsOut = 0; - } - if (sampleRateOut) { - *sampleRateOut = 0; - } - if (totalFrameCountOut) { - *totalFrameCountOut = 0; - } - if (!ma_dr_wav_init_file(&wav, filename, pAllocationCallbacks)) { - return NULL; - } - return ma_dr_wav__read_pcm_frames_and_close_s32(&wav, channelsOut, sampleRateOut, totalFrameCountOut); -} -#ifndef MA_DR_WAV_NO_WCHAR -MA_API ma_int16* ma_dr_wav_open_file_and_read_pcm_frames_s16_w(const wchar_t* filename, unsigned int* channelsOut, unsigned int* sampleRateOut, ma_uint64* totalFrameCountOut, const ma_allocation_callbacks* pAllocationCallbacks) -{ - ma_dr_wav wav; - if (sampleRateOut) { - *sampleRateOut = 0; - } - if (channelsOut) { - *channelsOut = 0; - } - if (totalFrameCountOut) { - *totalFrameCountOut = 0; - } - if (!ma_dr_wav_init_file_w(&wav, filename, pAllocationCallbacks)) { - return NULL; - } - return ma_dr_wav__read_pcm_frames_and_close_s16(&wav, channelsOut, sampleRateOut, totalFrameCountOut); -} -MA_API float* ma_dr_wav_open_file_and_read_pcm_frames_f32_w(const wchar_t* filename, unsigned int* channelsOut, unsigned int* sampleRateOut, ma_uint64* totalFrameCountOut, const ma_allocation_callbacks* pAllocationCallbacks) -{ - ma_dr_wav wav; - if (sampleRateOut) { - *sampleRateOut = 0; - } - if (channelsOut) { - *channelsOut = 0; - } - if (totalFrameCountOut) { - *totalFrameCountOut = 0; - } - if (!ma_dr_wav_init_file_w(&wav, filename, pAllocationCallbacks)) { - return NULL; - } - return ma_dr_wav__read_pcm_frames_and_close_f32(&wav, channelsOut, sampleRateOut, totalFrameCountOut); -} -MA_API ma_int32* ma_dr_wav_open_file_and_read_pcm_frames_s32_w(const wchar_t* filename, unsigned int* channelsOut, unsigned int* sampleRateOut, ma_uint64* totalFrameCountOut, const ma_allocation_callbacks* pAllocationCallbacks) -{ - ma_dr_wav wav; - if (sampleRateOut) { - *sampleRateOut = 0; - } - if (channelsOut) { - *channelsOut = 0; - } - if (totalFrameCountOut) { - *totalFrameCountOut = 0; - } - if (!ma_dr_wav_init_file_w(&wav, filename, pAllocationCallbacks)) { - return NULL; - } - return ma_dr_wav__read_pcm_frames_and_close_s32(&wav, channelsOut, sampleRateOut, totalFrameCountOut); -} -#endif -#endif -MA_API ma_int16* ma_dr_wav_open_memory_and_read_pcm_frames_s16(const void* data, size_t dataSize, unsigned int* channelsOut, unsigned int* sampleRateOut, ma_uint64* totalFrameCountOut, const ma_allocation_callbacks* pAllocationCallbacks) -{ - ma_dr_wav wav; - if (channelsOut) { - *channelsOut = 0; - } - if (sampleRateOut) { - *sampleRateOut = 0; - } - if (totalFrameCountOut) { - *totalFrameCountOut = 0; - } - if (!ma_dr_wav_init_memory(&wav, data, dataSize, pAllocationCallbacks)) { - return NULL; - } - return ma_dr_wav__read_pcm_frames_and_close_s16(&wav, channelsOut, sampleRateOut, totalFrameCountOut); -} -MA_API float* ma_dr_wav_open_memory_and_read_pcm_frames_f32(const void* data, size_t dataSize, unsigned int* channelsOut, unsigned int* sampleRateOut, ma_uint64* totalFrameCountOut, const ma_allocation_callbacks* pAllocationCallbacks) -{ - ma_dr_wav wav; - if (channelsOut) { - *channelsOut = 0; - } - if (sampleRateOut) { - *sampleRateOut = 0; - } - if (totalFrameCountOut) { - *totalFrameCountOut = 0; - } - if (!ma_dr_wav_init_memory(&wav, data, dataSize, pAllocationCallbacks)) { - return NULL; - } - return ma_dr_wav__read_pcm_frames_and_close_f32(&wav, channelsOut, sampleRateOut, totalFrameCountOut); -} -MA_API ma_int32* ma_dr_wav_open_memory_and_read_pcm_frames_s32(const void* data, size_t dataSize, unsigned int* channelsOut, unsigned int* sampleRateOut, ma_uint64* totalFrameCountOut, const ma_allocation_callbacks* pAllocationCallbacks) -{ - ma_dr_wav wav; - if (channelsOut) { - *channelsOut = 0; - } - if (sampleRateOut) { - *sampleRateOut = 0; - } - if (totalFrameCountOut) { - *totalFrameCountOut = 0; - } - if (!ma_dr_wav_init_memory(&wav, data, dataSize, pAllocationCallbacks)) { - return NULL; - } - return ma_dr_wav__read_pcm_frames_and_close_s32(&wav, channelsOut, sampleRateOut, totalFrameCountOut); -} -#endif -MA_API void ma_dr_wav_free(void* p, const ma_allocation_callbacks* pAllocationCallbacks) -{ - if (pAllocationCallbacks != NULL) { - ma_dr_wav__free_from_callbacks(p, pAllocationCallbacks); - } else { - ma_dr_wav__free_default(p, NULL); - } -} -MA_API ma_uint16 ma_dr_wav_bytes_to_u16(const ma_uint8* data) -{ - return ((ma_uint16)data[0] << 0) | ((ma_uint16)data[1] << 8); -} -MA_API ma_int16 ma_dr_wav_bytes_to_s16(const ma_uint8* data) -{ - return (ma_int16)ma_dr_wav_bytes_to_u16(data); -} -MA_API ma_uint32 ma_dr_wav_bytes_to_u32(const ma_uint8* data) -{ - return ma_dr_wav_bytes_to_u32_le(data); -} -MA_API float ma_dr_wav_bytes_to_f32(const ma_uint8* data) -{ - union { - ma_uint32 u32; - float f32; - } value; - value.u32 = ma_dr_wav_bytes_to_u32(data); - return value.f32; -} -MA_API ma_int32 ma_dr_wav_bytes_to_s32(const ma_uint8* data) -{ - return (ma_int32)ma_dr_wav_bytes_to_u32(data); -} -MA_API ma_uint64 ma_dr_wav_bytes_to_u64(const ma_uint8* data) -{ - return - ((ma_uint64)data[0] << 0) | ((ma_uint64)data[1] << 8) | ((ma_uint64)data[2] << 16) | ((ma_uint64)data[3] << 24) | - ((ma_uint64)data[4] << 32) | ((ma_uint64)data[5] << 40) | ((ma_uint64)data[6] << 48) | ((ma_uint64)data[7] << 56); -} -MA_API ma_int64 ma_dr_wav_bytes_to_s64(const ma_uint8* data) -{ - return (ma_int64)ma_dr_wav_bytes_to_u64(data); -} -MA_API ma_bool32 ma_dr_wav_guid_equal(const ma_uint8 a[16], const ma_uint8 b[16]) -{ - int i; - for (i = 0; i < 16; i += 1) { - if (a[i] != b[i]) { - return MA_FALSE; - } - } - return MA_TRUE; -} -MA_API ma_bool32 ma_dr_wav_fourcc_equal(const ma_uint8* a, const char* b) -{ - return - a[0] == b[0] && - a[1] == b[1] && - a[2] == b[2] && - a[3] == b[3]; -} -#ifdef __MRC__ -#pragma options opt reset -#endif -#endif -/* dr_wav_c end */ -#endif /* MA_DR_WAV_IMPLEMENTATION */ -#endif /* MA_NO_WAV */ - -#if !defined(MA_NO_FLAC) && !defined(MA_NO_DECODING) -#if !defined(MA_DR_FLAC_IMPLEMENTATION) -/* dr_flac_c begin */ -#ifndef ma_dr_flac_c -#define ma_dr_flac_c -#if defined(__clang__) || (defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6))) - #pragma GCC diagnostic push - #if __GNUC__ >= 7 - #pragma GCC diagnostic ignored "-Wimplicit-fallthrough" - #endif -#endif -#ifdef __linux__ - #ifndef _BSD_SOURCE - #define _BSD_SOURCE - #endif - #ifndef _DEFAULT_SOURCE - #define _DEFAULT_SOURCE - #endif - #ifndef __USE_BSD - #define __USE_BSD - #endif - #include -#endif -#include -#include -#if !defined(MA_DR_FLAC_NO_SIMD) - #if defined(MA_X64) || defined(MA_X86) - #if defined(_MSC_VER) && !defined(__clang__) - #if _MSC_VER >= 1400 && !defined(MA_DR_FLAC_NO_SSE2) - #define MA_DR_FLAC_SUPPORT_SSE2 - #endif - #if _MSC_VER >= 1600 && !defined(MA_DR_FLAC_NO_SSE41) - #define MA_DR_FLAC_SUPPORT_SSE41 - #endif - #elif defined(__clang__) || (defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 3))) - #if defined(__SSE2__) && !defined(MA_DR_FLAC_NO_SSE2) - #define MA_DR_FLAC_SUPPORT_SSE2 - #endif - #if defined(__SSE4_1__) && !defined(MA_DR_FLAC_NO_SSE41) - #define MA_DR_FLAC_SUPPORT_SSE41 - #endif - #endif - #if !defined(__GNUC__) && !defined(__clang__) && defined(__has_include) - #if !defined(MA_DR_FLAC_SUPPORT_SSE2) && !defined(MA_DR_FLAC_NO_SSE2) && __has_include() - #define MA_DR_FLAC_SUPPORT_SSE2 - #endif - #if !defined(MA_DR_FLAC_SUPPORT_SSE41) && !defined(MA_DR_FLAC_NO_SSE41) && __has_include() - #define MA_DR_FLAC_SUPPORT_SSE41 - #endif - #endif - #if defined(MA_DR_FLAC_SUPPORT_SSE41) - #include - #elif defined(MA_DR_FLAC_SUPPORT_SSE2) - #include - #endif - #endif - #if defined(MA_ARM) - #if !defined(MA_DR_FLAC_NO_NEON) && (defined(__ARM_NEON) || defined(__aarch64__) || defined(_M_ARM64)) - #define MA_DR_FLAC_SUPPORT_NEON - #include - #endif - #endif -#endif -#if !defined(MA_DR_FLAC_NO_SIMD) && (defined(MA_X86) || defined(MA_X64)) - #if defined(_MSC_VER) && !defined(__clang__) - #if _MSC_VER >= 1400 - #include - static void ma_dr_flac__cpuid(int info[4], int fid) - { - __cpuid(info, fid); - } - #else - #define MA_DR_FLAC_NO_CPUID - #endif - #else - #if defined(__GNUC__) || defined(__clang__) - static void ma_dr_flac__cpuid(int info[4], int fid) - { - #if defined(MA_X86) && defined(__PIC__) - __asm__ __volatile__ ( - "xchg{l} {%%}ebx, %k1;" - "cpuid;" - "xchg{l} {%%}ebx, %k1;" - : "=a"(info[0]), "=&r"(info[1]), "=c"(info[2]), "=d"(info[3]) : "a"(fid), "c"(0) - ); - #else - __asm__ __volatile__ ( - "cpuid" : "=a"(info[0]), "=b"(info[1]), "=c"(info[2]), "=d"(info[3]) : "a"(fid), "c"(0) - ); - #endif - } - #else - #define MA_DR_FLAC_NO_CPUID - #endif - #endif -#else - #define MA_DR_FLAC_NO_CPUID -#endif -static MA_INLINE ma_bool32 ma_dr_flac_has_sse2(void) -{ -#if defined(MA_DR_FLAC_SUPPORT_SSE2) - #if (defined(MA_X64) || defined(MA_X86)) && !defined(MA_DR_FLAC_NO_SSE2) - #if defined(MA_X64) - return MA_TRUE; - #elif (defined(_M_IX86_FP) && _M_IX86_FP == 2) || defined(__SSE2__) - return MA_TRUE; - #else - #if defined(MA_DR_FLAC_NO_CPUID) - return MA_FALSE; - #else - int info[4]; - ma_dr_flac__cpuid(info, 1); - return (info[3] & (1 << 26)) != 0; - #endif - #endif - #else - return MA_FALSE; - #endif -#else - return MA_FALSE; -#endif -} -static MA_INLINE ma_bool32 ma_dr_flac_has_sse41(void) -{ -#if defined(MA_DR_FLAC_SUPPORT_SSE41) - #if (defined(MA_X64) || defined(MA_X86)) && !defined(MA_DR_FLAC_NO_SSE41) - #if defined(__SSE4_1__) || defined(__AVX__) - return MA_TRUE; - #else - #if defined(MA_DR_FLAC_NO_CPUID) - return MA_FALSE; - #else - int info[4]; - ma_dr_flac__cpuid(info, 1); - return (info[2] & (1 << 19)) != 0; - #endif - #endif - #else - return MA_FALSE; - #endif -#else - return MA_FALSE; -#endif -} -#if defined(_MSC_VER) && _MSC_VER >= 1500 && (defined(MA_X86) || defined(MA_X64)) && !defined(__clang__) - #define MA_DR_FLAC_HAS_LZCNT_INTRINSIC -#elif (defined(__GNUC__) && ((__GNUC__ > 4) || (__GNUC__ == 4 && __GNUC_MINOR__ >= 7))) - #define MA_DR_FLAC_HAS_LZCNT_INTRINSIC -#elif defined(__clang__) - #if defined(__has_builtin) - #if __has_builtin(__builtin_clzll) || __has_builtin(__builtin_clzl) - #define MA_DR_FLAC_HAS_LZCNT_INTRINSIC - #endif - #endif -#endif -#if defined(_MSC_VER) && _MSC_VER >= 1400 && !defined(__clang__) - #define MA_DR_FLAC_HAS_BYTESWAP16_INTRINSIC - #define MA_DR_FLAC_HAS_BYTESWAP32_INTRINSIC - #define MA_DR_FLAC_HAS_BYTESWAP64_INTRINSIC -#elif defined(__clang__) - #if defined(__has_builtin) - #if __has_builtin(__builtin_bswap16) - #define MA_DR_FLAC_HAS_BYTESWAP16_INTRINSIC - #endif - #if __has_builtin(__builtin_bswap32) - #define MA_DR_FLAC_HAS_BYTESWAP32_INTRINSIC - #endif - #if __has_builtin(__builtin_bswap64) - #define MA_DR_FLAC_HAS_BYTESWAP64_INTRINSIC - #endif - #endif -#elif defined(__GNUC__) - #if ((__GNUC__ > 4) || (__GNUC__ == 4 && __GNUC_MINOR__ >= 3)) - #define MA_DR_FLAC_HAS_BYTESWAP32_INTRINSIC - #define MA_DR_FLAC_HAS_BYTESWAP64_INTRINSIC - #endif - #if ((__GNUC__ > 4) || (__GNUC__ == 4 && __GNUC_MINOR__ >= 8)) - #define MA_DR_FLAC_HAS_BYTESWAP16_INTRINSIC - #endif -#elif defined(__WATCOMC__) && defined(__386__) - #define MA_DR_FLAC_HAS_BYTESWAP16_INTRINSIC - #define MA_DR_FLAC_HAS_BYTESWAP32_INTRINSIC - #define MA_DR_FLAC_HAS_BYTESWAP64_INTRINSIC - extern __inline ma_uint16 _watcom_bswap16(ma_uint16); - extern __inline ma_uint32 _watcom_bswap32(ma_uint32); - extern __inline ma_uint64 _watcom_bswap64(ma_uint64); -#pragma aux _watcom_bswap16 = \ - "xchg al, ah" \ - parm [ax] \ - value [ax] \ - modify nomemory; -#pragma aux _watcom_bswap32 = \ - "bswap eax" \ - parm [eax] \ - value [eax] \ - modify nomemory; -#pragma aux _watcom_bswap64 = \ - "bswap eax" \ - "bswap edx" \ - "xchg eax,edx" \ - parm [eax edx] \ - value [eax edx] \ - modify nomemory; -#endif -#ifndef MA_DR_FLAC_ASSERT -#include -#define MA_DR_FLAC_ASSERT(expression) assert(expression) -#endif -#ifndef MA_DR_FLAC_MALLOC -#define MA_DR_FLAC_MALLOC(sz) malloc((sz)) -#endif -#ifndef MA_DR_FLAC_REALLOC -#define MA_DR_FLAC_REALLOC(p, sz) realloc((p), (sz)) -#endif -#ifndef MA_DR_FLAC_FREE -#define MA_DR_FLAC_FREE(p) free((p)) -#endif -#ifndef MA_DR_FLAC_COPY_MEMORY -#define MA_DR_FLAC_COPY_MEMORY(dst, src, sz) memcpy((dst), (src), (sz)) -#endif -#ifndef MA_DR_FLAC_ZERO_MEMORY -#define MA_DR_FLAC_ZERO_MEMORY(p, sz) memset((p), 0, (sz)) -#endif -#ifndef MA_DR_FLAC_ZERO_OBJECT -#define MA_DR_FLAC_ZERO_OBJECT(p) MA_DR_FLAC_ZERO_MEMORY((p), sizeof(*(p))) -#endif -#define MA_DR_FLAC_MAX_SIMD_VECTOR_SIZE 64 -#define MA_DR_FLAC_SUBFRAME_CONSTANT 0 -#define MA_DR_FLAC_SUBFRAME_VERBATIM 1 -#define MA_DR_FLAC_SUBFRAME_FIXED 8 -#define MA_DR_FLAC_SUBFRAME_LPC 32 -#define MA_DR_FLAC_SUBFRAME_RESERVED 255 -#define MA_DR_FLAC_RESIDUAL_CODING_METHOD_PARTITIONED_RICE 0 -#define MA_DR_FLAC_RESIDUAL_CODING_METHOD_PARTITIONED_RICE2 1 -#define MA_DR_FLAC_CHANNEL_ASSIGNMENT_INDEPENDENT 0 -#define MA_DR_FLAC_CHANNEL_ASSIGNMENT_LEFT_SIDE 8 -#define MA_DR_FLAC_CHANNEL_ASSIGNMENT_RIGHT_SIDE 9 -#define MA_DR_FLAC_CHANNEL_ASSIGNMENT_MID_SIDE 10 -#define MA_DR_FLAC_SEEKPOINT_SIZE_IN_BYTES 18 -#define MA_DR_FLAC_CUESHEET_TRACK_SIZE_IN_BYTES 36 -#define MA_DR_FLAC_CUESHEET_TRACK_INDEX_SIZE_IN_BYTES 12 -#define ma_dr_flac_align(x, a) ((((x) + (a) - 1) / (a)) * (a)) -MA_API void ma_dr_flac_version(ma_uint32* pMajor, ma_uint32* pMinor, ma_uint32* pRevision) -{ - if (pMajor) { - *pMajor = MA_DR_FLAC_VERSION_MAJOR; - } - if (pMinor) { - *pMinor = MA_DR_FLAC_VERSION_MINOR; - } - if (pRevision) { - *pRevision = MA_DR_FLAC_VERSION_REVISION; - } -} -MA_API const char* ma_dr_flac_version_string(void) -{ - return MA_DR_FLAC_VERSION_STRING; -} -#if defined(__has_feature) - #if __has_feature(thread_sanitizer) - #define MA_DR_FLAC_NO_THREAD_SANITIZE __attribute__((no_sanitize("thread"))) - #else - #define MA_DR_FLAC_NO_THREAD_SANITIZE - #endif -#else - #define MA_DR_FLAC_NO_THREAD_SANITIZE -#endif -#if defined(MA_DR_FLAC_HAS_LZCNT_INTRINSIC) -static ma_bool32 ma_dr_flac__gIsLZCNTSupported = MA_FALSE; -#endif -#ifndef MA_DR_FLAC_NO_CPUID -static ma_bool32 ma_dr_flac__gIsSSE2Supported = MA_FALSE; -static ma_bool32 ma_dr_flac__gIsSSE41Supported = MA_FALSE; -MA_DR_FLAC_NO_THREAD_SANITIZE static void ma_dr_flac__init_cpu_caps(void) -{ - static ma_bool32 isCPUCapsInitialized = MA_FALSE; - if (!isCPUCapsInitialized) { -#if defined(MA_DR_FLAC_HAS_LZCNT_INTRINSIC) - int info[4] = {0}; - ma_dr_flac__cpuid(info, 0x80000001); - ma_dr_flac__gIsLZCNTSupported = (info[2] & (1 << 5)) != 0; -#endif - ma_dr_flac__gIsSSE2Supported = ma_dr_flac_has_sse2(); - ma_dr_flac__gIsSSE41Supported = ma_dr_flac_has_sse41(); - isCPUCapsInitialized = MA_TRUE; - } -} -#else -static ma_bool32 ma_dr_flac__gIsNEONSupported = MA_FALSE; -static MA_INLINE ma_bool32 ma_dr_flac__has_neon(void) -{ -#if defined(MA_DR_FLAC_SUPPORT_NEON) - #if defined(MA_ARM) && !defined(MA_DR_FLAC_NO_NEON) - #if (defined(__ARM_NEON) || defined(__aarch64__) || defined(_M_ARM64)) - return MA_TRUE; - #else - return MA_FALSE; - #endif - #else - return MA_FALSE; - #endif -#else - return MA_FALSE; -#endif -} -MA_DR_FLAC_NO_THREAD_SANITIZE static void ma_dr_flac__init_cpu_caps(void) -{ - ma_dr_flac__gIsNEONSupported = ma_dr_flac__has_neon(); -#if defined(MA_DR_FLAC_HAS_LZCNT_INTRINSIC) && defined(MA_ARM) && (defined(__ARM_ARCH) && __ARM_ARCH >= 5) - ma_dr_flac__gIsLZCNTSupported = MA_TRUE; -#endif -} -#endif -static MA_INLINE ma_bool32 ma_dr_flac__is_little_endian(void) -{ -#if defined(MA_X86) || defined(MA_X64) - return MA_TRUE; -#elif defined(__BYTE_ORDER) && defined(__LITTLE_ENDIAN) && __BYTE_ORDER == __LITTLE_ENDIAN - return MA_TRUE; -#else - int n = 1; - return (*(char*)&n) == 1; -#endif -} -static MA_INLINE ma_uint16 ma_dr_flac__swap_endian_uint16(ma_uint16 n) -{ -#ifdef MA_DR_FLAC_HAS_BYTESWAP16_INTRINSIC - #if defined(_MSC_VER) && !defined(__clang__) - return _byteswap_ushort(n); - #elif defined(__GNUC__) || defined(__clang__) - return __builtin_bswap16(n); - #elif defined(__WATCOMC__) && defined(__386__) - return _watcom_bswap16(n); - #else - #error "This compiler does not support the byte swap intrinsic." - #endif -#else - return ((n & 0xFF00) >> 8) | - ((n & 0x00FF) << 8); -#endif -} -static MA_INLINE ma_uint32 ma_dr_flac__swap_endian_uint32(ma_uint32 n) -{ -#ifdef MA_DR_FLAC_HAS_BYTESWAP32_INTRINSIC - #if defined(_MSC_VER) && !defined(__clang__) - return _byteswap_ulong(n); - #elif defined(__GNUC__) || defined(__clang__) - #if defined(MA_ARM) && (defined(__ARM_ARCH) && __ARM_ARCH >= 6) && !defined(__ARM_ARCH_6M__) && !defined(MA_64BIT) - ma_uint32 r; - __asm__ __volatile__ ( - #if defined(MA_64BIT) - "rev %w[out], %w[in]" : [out]"=r"(r) : [in]"r"(n) - #else - "rev %[out], %[in]" : [out]"=r"(r) : [in]"r"(n) - #endif - ); - return r; - #else - return __builtin_bswap32(n); - #endif - #elif defined(__WATCOMC__) && defined(__386__) - return _watcom_bswap32(n); - #else - #error "This compiler does not support the byte swap intrinsic." - #endif -#else - return ((n & 0xFF000000) >> 24) | - ((n & 0x00FF0000) >> 8) | - ((n & 0x0000FF00) << 8) | - ((n & 0x000000FF) << 24); -#endif -} -static MA_INLINE ma_uint64 ma_dr_flac__swap_endian_uint64(ma_uint64 n) -{ -#ifdef MA_DR_FLAC_HAS_BYTESWAP64_INTRINSIC - #if defined(_MSC_VER) && !defined(__clang__) - return _byteswap_uint64(n); - #elif defined(__GNUC__) || defined(__clang__) - return __builtin_bswap64(n); - #elif defined(__WATCOMC__) && defined(__386__) - return _watcom_bswap64(n); - #else - #error "This compiler does not support the byte swap intrinsic." - #endif -#else - return ((n & ((ma_uint64)0xFF000000 << 32)) >> 56) | - ((n & ((ma_uint64)0x00FF0000 << 32)) >> 40) | - ((n & ((ma_uint64)0x0000FF00 << 32)) >> 24) | - ((n & ((ma_uint64)0x000000FF << 32)) >> 8) | - ((n & ((ma_uint64)0xFF000000 )) << 8) | - ((n & ((ma_uint64)0x00FF0000 )) << 24) | - ((n & ((ma_uint64)0x0000FF00 )) << 40) | - ((n & ((ma_uint64)0x000000FF )) << 56); -#endif -} -static MA_INLINE ma_uint16 ma_dr_flac__be2host_16(ma_uint16 n) -{ - if (ma_dr_flac__is_little_endian()) { - return ma_dr_flac__swap_endian_uint16(n); - } - return n; -} -static MA_INLINE ma_uint32 ma_dr_flac__be2host_32(ma_uint32 n) -{ - if (ma_dr_flac__is_little_endian()) { - return ma_dr_flac__swap_endian_uint32(n); - } - return n; -} -static MA_INLINE ma_uint32 ma_dr_flac__be2host_32_ptr_unaligned(const void* pData) -{ - const ma_uint8* pNum = (ma_uint8*)pData; - return *(pNum) << 24 | *(pNum+1) << 16 | *(pNum+2) << 8 | *(pNum+3); -} -static MA_INLINE ma_uint64 ma_dr_flac__be2host_64(ma_uint64 n) -{ - if (ma_dr_flac__is_little_endian()) { - return ma_dr_flac__swap_endian_uint64(n); - } - return n; -} -static MA_INLINE ma_uint32 ma_dr_flac__le2host_32(ma_uint32 n) -{ - if (!ma_dr_flac__is_little_endian()) { - return ma_dr_flac__swap_endian_uint32(n); - } - return n; -} -static MA_INLINE ma_uint32 ma_dr_flac__le2host_32_ptr_unaligned(const void* pData) -{ - const ma_uint8* pNum = (ma_uint8*)pData; - return *pNum | *(pNum+1) << 8 | *(pNum+2) << 16 | *(pNum+3) << 24; -} -static MA_INLINE ma_uint32 ma_dr_flac__unsynchsafe_32(ma_uint32 n) -{ - ma_uint32 result = 0; - result |= (n & 0x7F000000) >> 3; - result |= (n & 0x007F0000) >> 2; - result |= (n & 0x00007F00) >> 1; - result |= (n & 0x0000007F) >> 0; - return result; -} -static ma_uint8 ma_dr_flac__crc8_table[] = { - 0x00, 0x07, 0x0E, 0x09, 0x1C, 0x1B, 0x12, 0x15, 0x38, 0x3F, 0x36, 0x31, 0x24, 0x23, 0x2A, 0x2D, - 0x70, 0x77, 0x7E, 0x79, 0x6C, 0x6B, 0x62, 0x65, 0x48, 0x4F, 0x46, 0x41, 0x54, 0x53, 0x5A, 0x5D, - 0xE0, 0xE7, 0xEE, 0xE9, 0xFC, 0xFB, 0xF2, 0xF5, 0xD8, 0xDF, 0xD6, 0xD1, 0xC4, 0xC3, 0xCA, 0xCD, - 0x90, 0x97, 0x9E, 0x99, 0x8C, 0x8B, 0x82, 0x85, 0xA8, 0xAF, 0xA6, 0xA1, 0xB4, 0xB3, 0xBA, 0xBD, - 0xC7, 0xC0, 0xC9, 0xCE, 0xDB, 0xDC, 0xD5, 0xD2, 0xFF, 0xF8, 0xF1, 0xF6, 0xE3, 0xE4, 0xED, 0xEA, - 0xB7, 0xB0, 0xB9, 0xBE, 0xAB, 0xAC, 0xA5, 0xA2, 0x8F, 0x88, 0x81, 0x86, 0x93, 0x94, 0x9D, 0x9A, - 0x27, 0x20, 0x29, 0x2E, 0x3B, 0x3C, 0x35, 0x32, 0x1F, 0x18, 0x11, 0x16, 0x03, 0x04, 0x0D, 0x0A, - 0x57, 0x50, 0x59, 0x5E, 0x4B, 0x4C, 0x45, 0x42, 0x6F, 0x68, 0x61, 0x66, 0x73, 0x74, 0x7D, 0x7A, - 0x89, 0x8E, 0x87, 0x80, 0x95, 0x92, 0x9B, 0x9C, 0xB1, 0xB6, 0xBF, 0xB8, 0xAD, 0xAA, 0xA3, 0xA4, - 0xF9, 0xFE, 0xF7, 0xF0, 0xE5, 0xE2, 0xEB, 0xEC, 0xC1, 0xC6, 0xCF, 0xC8, 0xDD, 0xDA, 0xD3, 0xD4, - 0x69, 0x6E, 0x67, 0x60, 0x75, 0x72, 0x7B, 0x7C, 0x51, 0x56, 0x5F, 0x58, 0x4D, 0x4A, 0x43, 0x44, - 0x19, 0x1E, 0x17, 0x10, 0x05, 0x02, 0x0B, 0x0C, 0x21, 0x26, 0x2F, 0x28, 0x3D, 0x3A, 0x33, 0x34, - 0x4E, 0x49, 0x40, 0x47, 0x52, 0x55, 0x5C, 0x5B, 0x76, 0x71, 0x78, 0x7F, 0x6A, 0x6D, 0x64, 0x63, - 0x3E, 0x39, 0x30, 0x37, 0x22, 0x25, 0x2C, 0x2B, 0x06, 0x01, 0x08, 0x0F, 0x1A, 0x1D, 0x14, 0x13, - 0xAE, 0xA9, 0xA0, 0xA7, 0xB2, 0xB5, 0xBC, 0xBB, 0x96, 0x91, 0x98, 0x9F, 0x8A, 0x8D, 0x84, 0x83, - 0xDE, 0xD9, 0xD0, 0xD7, 0xC2, 0xC5, 0xCC, 0xCB, 0xE6, 0xE1, 0xE8, 0xEF, 0xFA, 0xFD, 0xF4, 0xF3 -}; -static ma_uint16 ma_dr_flac__crc16_table[] = { - 0x0000, 0x8005, 0x800F, 0x000A, 0x801B, 0x001E, 0x0014, 0x8011, - 0x8033, 0x0036, 0x003C, 0x8039, 0x0028, 0x802D, 0x8027, 0x0022, - 0x8063, 0x0066, 0x006C, 0x8069, 0x0078, 0x807D, 0x8077, 0x0072, - 0x0050, 0x8055, 0x805F, 0x005A, 0x804B, 0x004E, 0x0044, 0x8041, - 0x80C3, 0x00C6, 0x00CC, 0x80C9, 0x00D8, 0x80DD, 0x80D7, 0x00D2, - 0x00F0, 0x80F5, 0x80FF, 0x00FA, 0x80EB, 0x00EE, 0x00E4, 0x80E1, - 0x00A0, 0x80A5, 0x80AF, 0x00AA, 0x80BB, 0x00BE, 0x00B4, 0x80B1, - 0x8093, 0x0096, 0x009C, 0x8099, 0x0088, 0x808D, 0x8087, 0x0082, - 0x8183, 0x0186, 0x018C, 0x8189, 0x0198, 0x819D, 0x8197, 0x0192, - 0x01B0, 0x81B5, 0x81BF, 0x01BA, 0x81AB, 0x01AE, 0x01A4, 0x81A1, - 0x01E0, 0x81E5, 0x81EF, 0x01EA, 0x81FB, 0x01FE, 0x01F4, 0x81F1, - 0x81D3, 0x01D6, 0x01DC, 0x81D9, 0x01C8, 0x81CD, 0x81C7, 0x01C2, - 0x0140, 0x8145, 0x814F, 0x014A, 0x815B, 0x015E, 0x0154, 0x8151, - 0x8173, 0x0176, 0x017C, 0x8179, 0x0168, 0x816D, 0x8167, 0x0162, - 0x8123, 0x0126, 0x012C, 0x8129, 0x0138, 0x813D, 0x8137, 0x0132, - 0x0110, 0x8115, 0x811F, 0x011A, 0x810B, 0x010E, 0x0104, 0x8101, - 0x8303, 0x0306, 0x030C, 0x8309, 0x0318, 0x831D, 0x8317, 0x0312, - 0x0330, 0x8335, 0x833F, 0x033A, 0x832B, 0x032E, 0x0324, 0x8321, - 0x0360, 0x8365, 0x836F, 0x036A, 0x837B, 0x037E, 0x0374, 0x8371, - 0x8353, 0x0356, 0x035C, 0x8359, 0x0348, 0x834D, 0x8347, 0x0342, - 0x03C0, 0x83C5, 0x83CF, 0x03CA, 0x83DB, 0x03DE, 0x03D4, 0x83D1, - 0x83F3, 0x03F6, 0x03FC, 0x83F9, 0x03E8, 0x83ED, 0x83E7, 0x03E2, - 0x83A3, 0x03A6, 0x03AC, 0x83A9, 0x03B8, 0x83BD, 0x83B7, 0x03B2, - 0x0390, 0x8395, 0x839F, 0x039A, 0x838B, 0x038E, 0x0384, 0x8381, - 0x0280, 0x8285, 0x828F, 0x028A, 0x829B, 0x029E, 0x0294, 0x8291, - 0x82B3, 0x02B6, 0x02BC, 0x82B9, 0x02A8, 0x82AD, 0x82A7, 0x02A2, - 0x82E3, 0x02E6, 0x02EC, 0x82E9, 0x02F8, 0x82FD, 0x82F7, 0x02F2, - 0x02D0, 0x82D5, 0x82DF, 0x02DA, 0x82CB, 0x02CE, 0x02C4, 0x82C1, - 0x8243, 0x0246, 0x024C, 0x8249, 0x0258, 0x825D, 0x8257, 0x0252, - 0x0270, 0x8275, 0x827F, 0x027A, 0x826B, 0x026E, 0x0264, 0x8261, - 0x0220, 0x8225, 0x822F, 0x022A, 0x823B, 0x023E, 0x0234, 0x8231, - 0x8213, 0x0216, 0x021C, 0x8219, 0x0208, 0x820D, 0x8207, 0x0202 -}; -static MA_INLINE ma_uint8 ma_dr_flac_crc8_byte(ma_uint8 crc, ma_uint8 data) -{ - return ma_dr_flac__crc8_table[crc ^ data]; -} -static MA_INLINE ma_uint8 ma_dr_flac_crc8(ma_uint8 crc, ma_uint32 data, ma_uint32 count) -{ -#ifdef MA_DR_FLAC_NO_CRC - (void)crc; - (void)data; - (void)count; - return 0; -#else -#if 0 - ma_uint8 p = 0x07; - for (int i = count-1; i >= 0; --i) { - ma_uint8 bit = (data & (1 << i)) >> i; - if (crc & 0x80) { - crc = ((crc << 1) | bit) ^ p; - } else { - crc = ((crc << 1) | bit); - } - } - return crc; -#else - ma_uint32 wholeBytes; - ma_uint32 leftoverBits; - ma_uint64 leftoverDataMask; - static ma_uint64 leftoverDataMaskTable[8] = { - 0x00, 0x01, 0x03, 0x07, 0x0F, 0x1F, 0x3F, 0x7F - }; - MA_DR_FLAC_ASSERT(count <= 32); - wholeBytes = count >> 3; - leftoverBits = count - (wholeBytes*8); - leftoverDataMask = leftoverDataMaskTable[leftoverBits]; - switch (wholeBytes) { - case 4: crc = ma_dr_flac_crc8_byte(crc, (ma_uint8)((data & (0xFF000000UL << leftoverBits)) >> (24 + leftoverBits))); - case 3: crc = ma_dr_flac_crc8_byte(crc, (ma_uint8)((data & (0x00FF0000UL << leftoverBits)) >> (16 + leftoverBits))); - case 2: crc = ma_dr_flac_crc8_byte(crc, (ma_uint8)((data & (0x0000FF00UL << leftoverBits)) >> ( 8 + leftoverBits))); - case 1: crc = ma_dr_flac_crc8_byte(crc, (ma_uint8)((data & (0x000000FFUL << leftoverBits)) >> ( 0 + leftoverBits))); - case 0: if (leftoverBits > 0) crc = (ma_uint8)((crc << leftoverBits) ^ ma_dr_flac__crc8_table[(crc >> (8 - leftoverBits)) ^ (data & leftoverDataMask)]); - } - return crc; -#endif -#endif -} -static MA_INLINE ma_uint16 ma_dr_flac_crc16_byte(ma_uint16 crc, ma_uint8 data) -{ - return (crc << 8) ^ ma_dr_flac__crc16_table[(ma_uint8)(crc >> 8) ^ data]; -} -static MA_INLINE ma_uint16 ma_dr_flac_crc16_cache(ma_uint16 crc, ma_dr_flac_cache_t data) -{ -#ifdef MA_64BIT - crc = ma_dr_flac_crc16_byte(crc, (ma_uint8)((data >> 56) & 0xFF)); - crc = ma_dr_flac_crc16_byte(crc, (ma_uint8)((data >> 48) & 0xFF)); - crc = ma_dr_flac_crc16_byte(crc, (ma_uint8)((data >> 40) & 0xFF)); - crc = ma_dr_flac_crc16_byte(crc, (ma_uint8)((data >> 32) & 0xFF)); -#endif - crc = ma_dr_flac_crc16_byte(crc, (ma_uint8)((data >> 24) & 0xFF)); - crc = ma_dr_flac_crc16_byte(crc, (ma_uint8)((data >> 16) & 0xFF)); - crc = ma_dr_flac_crc16_byte(crc, (ma_uint8)((data >> 8) & 0xFF)); - crc = ma_dr_flac_crc16_byte(crc, (ma_uint8)((data >> 0) & 0xFF)); - return crc; -} -static MA_INLINE ma_uint16 ma_dr_flac_crc16_bytes(ma_uint16 crc, ma_dr_flac_cache_t data, ma_uint32 byteCount) -{ - switch (byteCount) - { -#ifdef MA_64BIT - case 8: crc = ma_dr_flac_crc16_byte(crc, (ma_uint8)((data >> 56) & 0xFF)); - case 7: crc = ma_dr_flac_crc16_byte(crc, (ma_uint8)((data >> 48) & 0xFF)); - case 6: crc = ma_dr_flac_crc16_byte(crc, (ma_uint8)((data >> 40) & 0xFF)); - case 5: crc = ma_dr_flac_crc16_byte(crc, (ma_uint8)((data >> 32) & 0xFF)); -#endif - case 4: crc = ma_dr_flac_crc16_byte(crc, (ma_uint8)((data >> 24) & 0xFF)); - case 3: crc = ma_dr_flac_crc16_byte(crc, (ma_uint8)((data >> 16) & 0xFF)); - case 2: crc = ma_dr_flac_crc16_byte(crc, (ma_uint8)((data >> 8) & 0xFF)); - case 1: crc = ma_dr_flac_crc16_byte(crc, (ma_uint8)((data >> 0) & 0xFF)); - } - return crc; -} -#if 0 -static MA_INLINE ma_uint16 ma_dr_flac_crc16__32bit(ma_uint16 crc, ma_uint32 data, ma_uint32 count) -{ -#ifdef MA_DR_FLAC_NO_CRC - (void)crc; - (void)data; - (void)count; - return 0; -#else -#if 0 - ma_uint16 p = 0x8005; - for (int i = count-1; i >= 0; --i) { - ma_uint16 bit = (data & (1ULL << i)) >> i; - if (r & 0x8000) { - r = ((r << 1) | bit) ^ p; - } else { - r = ((r << 1) | bit); - } - } - return crc; -#else - ma_uint32 wholeBytes; - ma_uint32 leftoverBits; - ma_uint64 leftoverDataMask; - static ma_uint64 leftoverDataMaskTable[8] = { - 0x00, 0x01, 0x03, 0x07, 0x0F, 0x1F, 0x3F, 0x7F - }; - MA_DR_FLAC_ASSERT(count <= 64); - wholeBytes = count >> 3; - leftoverBits = count & 7; - leftoverDataMask = leftoverDataMaskTable[leftoverBits]; - switch (wholeBytes) { - default: - case 4: crc = ma_dr_flac_crc16_byte(crc, (ma_uint8)((data & (0xFF000000UL << leftoverBits)) >> (24 + leftoverBits))); - case 3: crc = ma_dr_flac_crc16_byte(crc, (ma_uint8)((data & (0x00FF0000UL << leftoverBits)) >> (16 + leftoverBits))); - case 2: crc = ma_dr_flac_crc16_byte(crc, (ma_uint8)((data & (0x0000FF00UL << leftoverBits)) >> ( 8 + leftoverBits))); - case 1: crc = ma_dr_flac_crc16_byte(crc, (ma_uint8)((data & (0x000000FFUL << leftoverBits)) >> ( 0 + leftoverBits))); - case 0: if (leftoverBits > 0) crc = (crc << leftoverBits) ^ ma_dr_flac__crc16_table[(crc >> (16 - leftoverBits)) ^ (data & leftoverDataMask)]; - } - return crc; -#endif -#endif -} -static MA_INLINE ma_uint16 ma_dr_flac_crc16__64bit(ma_uint16 crc, ma_uint64 data, ma_uint32 count) -{ -#ifdef MA_DR_FLAC_NO_CRC - (void)crc; - (void)data; - (void)count; - return 0; -#else - ma_uint32 wholeBytes; - ma_uint32 leftoverBits; - ma_uint64 leftoverDataMask; - static ma_uint64 leftoverDataMaskTable[8] = { - 0x00, 0x01, 0x03, 0x07, 0x0F, 0x1F, 0x3F, 0x7F - }; - MA_DR_FLAC_ASSERT(count <= 64); - wholeBytes = count >> 3; - leftoverBits = count & 7; - leftoverDataMask = leftoverDataMaskTable[leftoverBits]; - switch (wholeBytes) { - default: - case 8: crc = ma_dr_flac_crc16_byte(crc, (ma_uint8)((data & (((ma_uint64)0xFF000000 << 32) << leftoverBits)) >> (56 + leftoverBits))); - case 7: crc = ma_dr_flac_crc16_byte(crc, (ma_uint8)((data & (((ma_uint64)0x00FF0000 << 32) << leftoverBits)) >> (48 + leftoverBits))); - case 6: crc = ma_dr_flac_crc16_byte(crc, (ma_uint8)((data & (((ma_uint64)0x0000FF00 << 32) << leftoverBits)) >> (40 + leftoverBits))); - case 5: crc = ma_dr_flac_crc16_byte(crc, (ma_uint8)((data & (((ma_uint64)0x000000FF << 32) << leftoverBits)) >> (32 + leftoverBits))); - case 4: crc = ma_dr_flac_crc16_byte(crc, (ma_uint8)((data & (((ma_uint64)0xFF000000 ) << leftoverBits)) >> (24 + leftoverBits))); - case 3: crc = ma_dr_flac_crc16_byte(crc, (ma_uint8)((data & (((ma_uint64)0x00FF0000 ) << leftoverBits)) >> (16 + leftoverBits))); - case 2: crc = ma_dr_flac_crc16_byte(crc, (ma_uint8)((data & (((ma_uint64)0x0000FF00 ) << leftoverBits)) >> ( 8 + leftoverBits))); - case 1: crc = ma_dr_flac_crc16_byte(crc, (ma_uint8)((data & (((ma_uint64)0x000000FF ) << leftoverBits)) >> ( 0 + leftoverBits))); - case 0: if (leftoverBits > 0) crc = (crc << leftoverBits) ^ ma_dr_flac__crc16_table[(crc >> (16 - leftoverBits)) ^ (data & leftoverDataMask)]; - } - return crc; -#endif -} -static MA_INLINE ma_uint16 ma_dr_flac_crc16(ma_uint16 crc, ma_dr_flac_cache_t data, ma_uint32 count) -{ -#ifdef MA_64BIT - return ma_dr_flac_crc16__64bit(crc, data, count); -#else - return ma_dr_flac_crc16__32bit(crc, data, count); -#endif -} -#endif -#ifdef MA_64BIT -#define ma_dr_flac__be2host__cache_line ma_dr_flac__be2host_64 -#else -#define ma_dr_flac__be2host__cache_line ma_dr_flac__be2host_32 -#endif -#define MA_DR_FLAC_CACHE_L1_SIZE_BYTES(bs) (sizeof((bs)->cache)) -#define MA_DR_FLAC_CACHE_L1_SIZE_BITS(bs) (sizeof((bs)->cache)*8) -#define MA_DR_FLAC_CACHE_L1_BITS_REMAINING(bs) (MA_DR_FLAC_CACHE_L1_SIZE_BITS(bs) - (bs)->consumedBits) -#define MA_DR_FLAC_CACHE_L1_SELECTION_MASK(_bitCount) (~((~(ma_dr_flac_cache_t)0) >> (_bitCount))) -#define MA_DR_FLAC_CACHE_L1_SELECTION_SHIFT(bs, _bitCount) (MA_DR_FLAC_CACHE_L1_SIZE_BITS(bs) - (_bitCount)) -#define MA_DR_FLAC_CACHE_L1_SELECT(bs, _bitCount) (((bs)->cache) & MA_DR_FLAC_CACHE_L1_SELECTION_MASK(_bitCount)) -#define MA_DR_FLAC_CACHE_L1_SELECT_AND_SHIFT(bs, _bitCount) (MA_DR_FLAC_CACHE_L1_SELECT((bs), (_bitCount)) >> MA_DR_FLAC_CACHE_L1_SELECTION_SHIFT((bs), (_bitCount))) -#define MA_DR_FLAC_CACHE_L1_SELECT_AND_SHIFT_SAFE(bs, _bitCount)(MA_DR_FLAC_CACHE_L1_SELECT((bs), (_bitCount)) >> (MA_DR_FLAC_CACHE_L1_SELECTION_SHIFT((bs), (_bitCount)) & (MA_DR_FLAC_CACHE_L1_SIZE_BITS(bs)-1))) -#define MA_DR_FLAC_CACHE_L2_SIZE_BYTES(bs) (sizeof((bs)->cacheL2)) -#define MA_DR_FLAC_CACHE_L2_LINE_COUNT(bs) (MA_DR_FLAC_CACHE_L2_SIZE_BYTES(bs) / sizeof((bs)->cacheL2[0])) -#define MA_DR_FLAC_CACHE_L2_LINES_REMAINING(bs) (MA_DR_FLAC_CACHE_L2_LINE_COUNT(bs) - (bs)->nextL2Line) -#ifndef MA_DR_FLAC_NO_CRC -static MA_INLINE void ma_dr_flac__reset_crc16(ma_dr_flac_bs* bs) -{ - bs->crc16 = 0; - bs->crc16CacheIgnoredBytes = bs->consumedBits >> 3; -} -static MA_INLINE void ma_dr_flac__update_crc16(ma_dr_flac_bs* bs) -{ - if (bs->crc16CacheIgnoredBytes == 0) { - bs->crc16 = ma_dr_flac_crc16_cache(bs->crc16, bs->crc16Cache); - } else { - bs->crc16 = ma_dr_flac_crc16_bytes(bs->crc16, bs->crc16Cache, MA_DR_FLAC_CACHE_L1_SIZE_BYTES(bs) - bs->crc16CacheIgnoredBytes); - bs->crc16CacheIgnoredBytes = 0; - } -} -static MA_INLINE ma_uint16 ma_dr_flac__flush_crc16(ma_dr_flac_bs* bs) -{ - MA_DR_FLAC_ASSERT((MA_DR_FLAC_CACHE_L1_BITS_REMAINING(bs) & 7) == 0); - if (MA_DR_FLAC_CACHE_L1_BITS_REMAINING(bs) == 0) { - ma_dr_flac__update_crc16(bs); - } else { - bs->crc16 = ma_dr_flac_crc16_bytes(bs->crc16, bs->crc16Cache >> MA_DR_FLAC_CACHE_L1_BITS_REMAINING(bs), (bs->consumedBits >> 3) - bs->crc16CacheIgnoredBytes); - bs->crc16CacheIgnoredBytes = bs->consumedBits >> 3; - } - return bs->crc16; -} -#endif -static MA_INLINE ma_bool32 ma_dr_flac__reload_l1_cache_from_l2(ma_dr_flac_bs* bs) -{ - size_t bytesRead; - size_t alignedL1LineCount; - if (bs->nextL2Line < MA_DR_FLAC_CACHE_L2_LINE_COUNT(bs)) { - bs->cache = bs->cacheL2[bs->nextL2Line++]; - return MA_TRUE; - } - if (bs->unalignedByteCount > 0) { - return MA_FALSE; - } - bytesRead = bs->onRead(bs->pUserData, bs->cacheL2, MA_DR_FLAC_CACHE_L2_SIZE_BYTES(bs)); - bs->nextL2Line = 0; - if (bytesRead == MA_DR_FLAC_CACHE_L2_SIZE_BYTES(bs)) { - bs->cache = bs->cacheL2[bs->nextL2Line++]; - return MA_TRUE; - } - alignedL1LineCount = bytesRead / MA_DR_FLAC_CACHE_L1_SIZE_BYTES(bs); - bs->unalignedByteCount = bytesRead - (alignedL1LineCount * MA_DR_FLAC_CACHE_L1_SIZE_BYTES(bs)); - if (bs->unalignedByteCount > 0) { - bs->unalignedCache = bs->cacheL2[alignedL1LineCount]; - } - if (alignedL1LineCount > 0) { - size_t offset = MA_DR_FLAC_CACHE_L2_LINE_COUNT(bs) - alignedL1LineCount; - size_t i; - for (i = alignedL1LineCount; i > 0; --i) { - bs->cacheL2[i-1 + offset] = bs->cacheL2[i-1]; - } - bs->nextL2Line = (ma_uint32)offset; - bs->cache = bs->cacheL2[bs->nextL2Line++]; - return MA_TRUE; - } else { - bs->nextL2Line = MA_DR_FLAC_CACHE_L2_LINE_COUNT(bs); - return MA_FALSE; - } -} -static ma_bool32 ma_dr_flac__reload_cache(ma_dr_flac_bs* bs) -{ - size_t bytesRead; -#ifndef MA_DR_FLAC_NO_CRC - ma_dr_flac__update_crc16(bs); -#endif - if (ma_dr_flac__reload_l1_cache_from_l2(bs)) { - bs->cache = ma_dr_flac__be2host__cache_line(bs->cache); - bs->consumedBits = 0; -#ifndef MA_DR_FLAC_NO_CRC - bs->crc16Cache = bs->cache; -#endif - return MA_TRUE; - } - bytesRead = bs->unalignedByteCount; - if (bytesRead == 0) { - bs->consumedBits = MA_DR_FLAC_CACHE_L1_SIZE_BITS(bs); - return MA_FALSE; - } - MA_DR_FLAC_ASSERT(bytesRead < MA_DR_FLAC_CACHE_L1_SIZE_BYTES(bs)); - bs->consumedBits = (ma_uint32)(MA_DR_FLAC_CACHE_L1_SIZE_BYTES(bs) - bytesRead) * 8; - bs->cache = ma_dr_flac__be2host__cache_line(bs->unalignedCache); - bs->cache &= MA_DR_FLAC_CACHE_L1_SELECTION_MASK(MA_DR_FLAC_CACHE_L1_BITS_REMAINING(bs)); - bs->unalignedByteCount = 0; -#ifndef MA_DR_FLAC_NO_CRC - bs->crc16Cache = bs->cache >> bs->consumedBits; - bs->crc16CacheIgnoredBytes = bs->consumedBits >> 3; -#endif - return MA_TRUE; -} -static void ma_dr_flac__reset_cache(ma_dr_flac_bs* bs) -{ - bs->nextL2Line = MA_DR_FLAC_CACHE_L2_LINE_COUNT(bs); - bs->consumedBits = MA_DR_FLAC_CACHE_L1_SIZE_BITS(bs); - bs->cache = 0; - bs->unalignedByteCount = 0; - bs->unalignedCache = 0; -#ifndef MA_DR_FLAC_NO_CRC - bs->crc16Cache = 0; - bs->crc16CacheIgnoredBytes = 0; -#endif -} -static MA_INLINE ma_bool32 ma_dr_flac__read_uint32(ma_dr_flac_bs* bs, unsigned int bitCount, ma_uint32* pResultOut) -{ - MA_DR_FLAC_ASSERT(bs != NULL); - MA_DR_FLAC_ASSERT(pResultOut != NULL); - MA_DR_FLAC_ASSERT(bitCount > 0); - MA_DR_FLAC_ASSERT(bitCount <= 32); - if (bs->consumedBits == MA_DR_FLAC_CACHE_L1_SIZE_BITS(bs)) { - if (!ma_dr_flac__reload_cache(bs)) { - return MA_FALSE; - } - } - if (bitCount <= MA_DR_FLAC_CACHE_L1_BITS_REMAINING(bs)) { -#ifdef MA_64BIT - *pResultOut = (ma_uint32)MA_DR_FLAC_CACHE_L1_SELECT_AND_SHIFT(bs, bitCount); - bs->consumedBits += bitCount; - bs->cache <<= bitCount; -#else - if (bitCount < MA_DR_FLAC_CACHE_L1_SIZE_BITS(bs)) { - *pResultOut = (ma_uint32)MA_DR_FLAC_CACHE_L1_SELECT_AND_SHIFT(bs, bitCount); - bs->consumedBits += bitCount; - bs->cache <<= bitCount; - } else { - *pResultOut = (ma_uint32)bs->cache; - bs->consumedBits = MA_DR_FLAC_CACHE_L1_SIZE_BITS(bs); - bs->cache = 0; - } -#endif - return MA_TRUE; - } else { - ma_uint32 bitCountHi = MA_DR_FLAC_CACHE_L1_BITS_REMAINING(bs); - ma_uint32 bitCountLo = bitCount - bitCountHi; - ma_uint32 resultHi; - MA_DR_FLAC_ASSERT(bitCountHi > 0); - MA_DR_FLAC_ASSERT(bitCountHi < 32); - resultHi = (ma_uint32)MA_DR_FLAC_CACHE_L1_SELECT_AND_SHIFT(bs, bitCountHi); - if (!ma_dr_flac__reload_cache(bs)) { - return MA_FALSE; - } - if (bitCountLo > MA_DR_FLAC_CACHE_L1_BITS_REMAINING(bs)) { - return MA_FALSE; - } - *pResultOut = (resultHi << bitCountLo) | (ma_uint32)MA_DR_FLAC_CACHE_L1_SELECT_AND_SHIFT(bs, bitCountLo); - bs->consumedBits += bitCountLo; - bs->cache <<= bitCountLo; - return MA_TRUE; - } -} -static ma_bool32 ma_dr_flac__read_int32(ma_dr_flac_bs* bs, unsigned int bitCount, ma_int32* pResult) -{ - ma_uint32 result; - MA_DR_FLAC_ASSERT(bs != NULL); - MA_DR_FLAC_ASSERT(pResult != NULL); - MA_DR_FLAC_ASSERT(bitCount > 0); - MA_DR_FLAC_ASSERT(bitCount <= 32); - if (!ma_dr_flac__read_uint32(bs, bitCount, &result)) { - return MA_FALSE; - } - if (bitCount < 32) { - ma_uint32 signbit; - signbit = ((result >> (bitCount-1)) & 0x01); - result |= (~signbit + 1) << bitCount; - } - *pResult = (ma_int32)result; - return MA_TRUE; -} -#ifdef MA_64BIT -static ma_bool32 ma_dr_flac__read_uint64(ma_dr_flac_bs* bs, unsigned int bitCount, ma_uint64* pResultOut) -{ - ma_uint32 resultHi; - ma_uint32 resultLo; - MA_DR_FLAC_ASSERT(bitCount <= 64); - MA_DR_FLAC_ASSERT(bitCount > 32); - if (!ma_dr_flac__read_uint32(bs, bitCount - 32, &resultHi)) { - return MA_FALSE; - } - if (!ma_dr_flac__read_uint32(bs, 32, &resultLo)) { - return MA_FALSE; - } - *pResultOut = (((ma_uint64)resultHi) << 32) | ((ma_uint64)resultLo); - return MA_TRUE; -} -#endif -#if 0 -static ma_bool32 ma_dr_flac__read_int64(ma_dr_flac_bs* bs, unsigned int bitCount, ma_int64* pResultOut) -{ - ma_uint64 result; - ma_uint64 signbit; - MA_DR_FLAC_ASSERT(bitCount <= 64); - if (!ma_dr_flac__read_uint64(bs, bitCount, &result)) { - return MA_FALSE; - } - signbit = ((result >> (bitCount-1)) & 0x01); - result |= (~signbit + 1) << bitCount; - *pResultOut = (ma_int64)result; - return MA_TRUE; -} -#endif -static ma_bool32 ma_dr_flac__read_uint16(ma_dr_flac_bs* bs, unsigned int bitCount, ma_uint16* pResult) -{ - ma_uint32 result; - MA_DR_FLAC_ASSERT(bs != NULL); - MA_DR_FLAC_ASSERT(pResult != NULL); - MA_DR_FLAC_ASSERT(bitCount > 0); - MA_DR_FLAC_ASSERT(bitCount <= 16); - if (!ma_dr_flac__read_uint32(bs, bitCount, &result)) { - return MA_FALSE; - } - *pResult = (ma_uint16)result; - return MA_TRUE; -} -#if 0 -static ma_bool32 ma_dr_flac__read_int16(ma_dr_flac_bs* bs, unsigned int bitCount, ma_int16* pResult) -{ - ma_int32 result; - MA_DR_FLAC_ASSERT(bs != NULL); - MA_DR_FLAC_ASSERT(pResult != NULL); - MA_DR_FLAC_ASSERT(bitCount > 0); - MA_DR_FLAC_ASSERT(bitCount <= 16); - if (!ma_dr_flac__read_int32(bs, bitCount, &result)) { - return MA_FALSE; - } - *pResult = (ma_int16)result; - return MA_TRUE; -} -#endif -static ma_bool32 ma_dr_flac__read_uint8(ma_dr_flac_bs* bs, unsigned int bitCount, ma_uint8* pResult) -{ - ma_uint32 result; - MA_DR_FLAC_ASSERT(bs != NULL); - MA_DR_FLAC_ASSERT(pResult != NULL); - MA_DR_FLAC_ASSERT(bitCount > 0); - MA_DR_FLAC_ASSERT(bitCount <= 8); - if (!ma_dr_flac__read_uint32(bs, bitCount, &result)) { - return MA_FALSE; - } - *pResult = (ma_uint8)result; - return MA_TRUE; -} -static ma_bool32 ma_dr_flac__read_int8(ma_dr_flac_bs* bs, unsigned int bitCount, ma_int8* pResult) -{ - ma_int32 result; - MA_DR_FLAC_ASSERT(bs != NULL); - MA_DR_FLAC_ASSERT(pResult != NULL); - MA_DR_FLAC_ASSERT(bitCount > 0); - MA_DR_FLAC_ASSERT(bitCount <= 8); - if (!ma_dr_flac__read_int32(bs, bitCount, &result)) { - return MA_FALSE; - } - *pResult = (ma_int8)result; - return MA_TRUE; -} -static ma_bool32 ma_dr_flac__seek_bits(ma_dr_flac_bs* bs, size_t bitsToSeek) -{ - if (bitsToSeek <= MA_DR_FLAC_CACHE_L1_BITS_REMAINING(bs)) { - bs->consumedBits += (ma_uint32)bitsToSeek; - bs->cache <<= bitsToSeek; - return MA_TRUE; - } else { - bitsToSeek -= MA_DR_FLAC_CACHE_L1_BITS_REMAINING(bs); - bs->consumedBits += MA_DR_FLAC_CACHE_L1_BITS_REMAINING(bs); - bs->cache = 0; -#ifdef MA_64BIT - while (bitsToSeek >= MA_DR_FLAC_CACHE_L1_SIZE_BITS(bs)) { - ma_uint64 bin; - if (!ma_dr_flac__read_uint64(bs, MA_DR_FLAC_CACHE_L1_SIZE_BITS(bs), &bin)) { - return MA_FALSE; - } - bitsToSeek -= MA_DR_FLAC_CACHE_L1_SIZE_BITS(bs); - } -#else - while (bitsToSeek >= MA_DR_FLAC_CACHE_L1_SIZE_BITS(bs)) { - ma_uint32 bin; - if (!ma_dr_flac__read_uint32(bs, MA_DR_FLAC_CACHE_L1_SIZE_BITS(bs), &bin)) { - return MA_FALSE; - } - bitsToSeek -= MA_DR_FLAC_CACHE_L1_SIZE_BITS(bs); - } -#endif - while (bitsToSeek >= 8) { - ma_uint8 bin; - if (!ma_dr_flac__read_uint8(bs, 8, &bin)) { - return MA_FALSE; - } - bitsToSeek -= 8; - } - if (bitsToSeek > 0) { - ma_uint8 bin; - if (!ma_dr_flac__read_uint8(bs, (ma_uint32)bitsToSeek, &bin)) { - return MA_FALSE; - } - bitsToSeek = 0; - } - MA_DR_FLAC_ASSERT(bitsToSeek == 0); - return MA_TRUE; - } -} -static ma_bool32 ma_dr_flac__find_and_seek_to_next_sync_code(ma_dr_flac_bs* bs) -{ - MA_DR_FLAC_ASSERT(bs != NULL); - if (!ma_dr_flac__seek_bits(bs, MA_DR_FLAC_CACHE_L1_BITS_REMAINING(bs) & 7)) { - return MA_FALSE; - } - for (;;) { - ma_uint8 hi; -#ifndef MA_DR_FLAC_NO_CRC - ma_dr_flac__reset_crc16(bs); -#endif - if (!ma_dr_flac__read_uint8(bs, 8, &hi)) { - return MA_FALSE; - } - if (hi == 0xFF) { - ma_uint8 lo; - if (!ma_dr_flac__read_uint8(bs, 6, &lo)) { - return MA_FALSE; - } - if (lo == 0x3E) { - return MA_TRUE; - } else { - if (!ma_dr_flac__seek_bits(bs, MA_DR_FLAC_CACHE_L1_BITS_REMAINING(bs) & 7)) { - return MA_FALSE; - } - } - } - } -} -#if defined(MA_DR_FLAC_HAS_LZCNT_INTRINSIC) -#define MA_DR_FLAC_IMPLEMENT_CLZ_LZCNT -#endif -#if defined(_MSC_VER) && _MSC_VER >= 1400 && (defined(MA_X64) || defined(MA_X86)) && !defined(__clang__) -#define MA_DR_FLAC_IMPLEMENT_CLZ_MSVC -#endif -#if defined(__WATCOMC__) && defined(__386__) -#define MA_DR_FLAC_IMPLEMENT_CLZ_WATCOM -#endif -#ifdef __MRC__ -#include -#define MA_DR_FLAC_IMPLEMENT_CLZ_MRC -#endif -static MA_INLINE ma_uint32 ma_dr_flac__clz_software(ma_dr_flac_cache_t x) -{ - ma_uint32 n; - static ma_uint32 clz_table_4[] = { - 0, - 4, - 3, 3, - 2, 2, 2, 2, - 1, 1, 1, 1, 1, 1, 1, 1 - }; - if (x == 0) { - return sizeof(x)*8; - } - n = clz_table_4[x >> (sizeof(x)*8 - 4)]; - if (n == 0) { -#ifdef MA_64BIT - if ((x & ((ma_uint64)0xFFFFFFFF << 32)) == 0) { n = 32; x <<= 32; } - if ((x & ((ma_uint64)0xFFFF0000 << 32)) == 0) { n += 16; x <<= 16; } - if ((x & ((ma_uint64)0xFF000000 << 32)) == 0) { n += 8; x <<= 8; } - if ((x & ((ma_uint64)0xF0000000 << 32)) == 0) { n += 4; x <<= 4; } -#else - if ((x & 0xFFFF0000) == 0) { n = 16; x <<= 16; } - if ((x & 0xFF000000) == 0) { n += 8; x <<= 8; } - if ((x & 0xF0000000) == 0) { n += 4; x <<= 4; } -#endif - n += clz_table_4[x >> (sizeof(x)*8 - 4)]; - } - return n - 1; -} -#ifdef MA_DR_FLAC_IMPLEMENT_CLZ_LZCNT -static MA_INLINE ma_bool32 ma_dr_flac__is_lzcnt_supported(void) -{ -#if defined(MA_DR_FLAC_HAS_LZCNT_INTRINSIC) && defined(MA_ARM) && (defined(__ARM_ARCH) && __ARM_ARCH >= 5) - return MA_TRUE; -#elif defined(__MRC__) - return MA_TRUE; -#else - #ifdef MA_DR_FLAC_HAS_LZCNT_INTRINSIC - return ma_dr_flac__gIsLZCNTSupported; - #else - return MA_FALSE; - #endif -#endif -} -static MA_INLINE ma_uint32 ma_dr_flac__clz_lzcnt(ma_dr_flac_cache_t x) -{ -#if defined(_MSC_VER) - #ifdef MA_64BIT - return (ma_uint32)__lzcnt64(x); - #else - return (ma_uint32)__lzcnt(x); - #endif -#else - #if defined(__GNUC__) || defined(__clang__) - #if defined(MA_X64) - { - ma_uint64 r; - __asm__ __volatile__ ( - "lzcnt{ %1, %0| %0, %1}" : "=r"(r) : "r"(x) : "cc" - ); - return (ma_uint32)r; - } - #elif defined(MA_X86) - { - ma_uint32 r; - __asm__ __volatile__ ( - "lzcnt{l %1, %0| %0, %1}" : "=r"(r) : "r"(x) : "cc" - ); - return r; - } - #elif defined(MA_ARM) && (defined(__ARM_ARCH) && __ARM_ARCH >= 5) && !defined(__ARM_ARCH_6M__) && !defined(MA_64BIT) - { - unsigned int r; - __asm__ __volatile__ ( - #if defined(MA_64BIT) - "clz %w[out], %w[in]" : [out]"=r"(r) : [in]"r"(x) - #else - "clz %[out], %[in]" : [out]"=r"(r) : [in]"r"(x) - #endif - ); - return r; - } - #else - if (x == 0) { - return sizeof(x)*8; - } - #ifdef MA_64BIT - return (ma_uint32)__builtin_clzll((ma_uint64)x); - #else - return (ma_uint32)__builtin_clzl((ma_uint32)x); - #endif - #endif - #else - #error "This compiler does not support the lzcnt intrinsic." - #endif -#endif -} -#endif -#ifdef MA_DR_FLAC_IMPLEMENT_CLZ_MSVC -#include -static MA_INLINE ma_uint32 ma_dr_flac__clz_msvc(ma_dr_flac_cache_t x) -{ - ma_uint32 n; - if (x == 0) { - return sizeof(x)*8; - } -#ifdef MA_64BIT - _BitScanReverse64((unsigned long*)&n, x); -#else - _BitScanReverse((unsigned long*)&n, x); -#endif - return sizeof(x)*8 - n - 1; -} -#endif -#ifdef MA_DR_FLAC_IMPLEMENT_CLZ_WATCOM -static __inline ma_uint32 ma_dr_flac__clz_watcom (ma_uint32); -#ifdef MA_DR_FLAC_IMPLEMENT_CLZ_WATCOM_LZCNT -#pragma aux ma_dr_flac__clz_watcom_lzcnt = \ - "db 0F3h, 0Fh, 0BDh, 0C0h" \ - parm [eax] \ - value [eax] \ - modify nomemory; -#else -#pragma aux ma_dr_flac__clz_watcom = \ - "bsr eax, eax" \ - "xor eax, 31" \ - parm [eax] nomemory \ - value [eax] \ - modify exact [eax] nomemory; -#endif -#endif -static MA_INLINE ma_uint32 ma_dr_flac__clz(ma_dr_flac_cache_t x) -{ -#ifdef MA_DR_FLAC_IMPLEMENT_CLZ_LZCNT - if (ma_dr_flac__is_lzcnt_supported()) { - return ma_dr_flac__clz_lzcnt(x); - } else -#endif - { -#ifdef MA_DR_FLAC_IMPLEMENT_CLZ_MSVC - return ma_dr_flac__clz_msvc(x); -#elif defined(MA_DR_FLAC_IMPLEMENT_CLZ_WATCOM_LZCNT) - return ma_dr_flac__clz_watcom_lzcnt(x); -#elif defined(MA_DR_FLAC_IMPLEMENT_CLZ_WATCOM) - return (x == 0) ? sizeof(x)*8 : ma_dr_flac__clz_watcom(x); -#elif defined(__MRC__) - return __cntlzw(x); -#else - return ma_dr_flac__clz_software(x); -#endif - } -} -static MA_INLINE ma_bool32 ma_dr_flac__seek_past_next_set_bit(ma_dr_flac_bs* bs, unsigned int* pOffsetOut) -{ - ma_uint32 zeroCounter = 0; - ma_uint32 setBitOffsetPlus1; - while (bs->cache == 0) { - zeroCounter += (ma_uint32)MA_DR_FLAC_CACHE_L1_BITS_REMAINING(bs); - if (!ma_dr_flac__reload_cache(bs)) { - return MA_FALSE; - } - } - if (bs->cache == 1) { - *pOffsetOut = zeroCounter + (ma_uint32)MA_DR_FLAC_CACHE_L1_BITS_REMAINING(bs) - 1; - if (!ma_dr_flac__reload_cache(bs)) { - return MA_FALSE; - } - return MA_TRUE; - } - setBitOffsetPlus1 = ma_dr_flac__clz(bs->cache); - setBitOffsetPlus1 += 1; - if (setBitOffsetPlus1 > MA_DR_FLAC_CACHE_L1_BITS_REMAINING(bs)) { - return MA_FALSE; - } - bs->consumedBits += setBitOffsetPlus1; - bs->cache <<= setBitOffsetPlus1; - *pOffsetOut = zeroCounter + setBitOffsetPlus1 - 1; - return MA_TRUE; -} -static ma_bool32 ma_dr_flac__seek_to_byte(ma_dr_flac_bs* bs, ma_uint64 offsetFromStart) -{ - MA_DR_FLAC_ASSERT(bs != NULL); - MA_DR_FLAC_ASSERT(offsetFromStart > 0); - if (offsetFromStart > 0x7FFFFFFF) { - ma_uint64 bytesRemaining = offsetFromStart; - if (!bs->onSeek(bs->pUserData, 0x7FFFFFFF, ma_dr_flac_seek_origin_start)) { - return MA_FALSE; - } - bytesRemaining -= 0x7FFFFFFF; - while (bytesRemaining > 0x7FFFFFFF) { - if (!bs->onSeek(bs->pUserData, 0x7FFFFFFF, ma_dr_flac_seek_origin_current)) { - return MA_FALSE; - } - bytesRemaining -= 0x7FFFFFFF; - } - if (bytesRemaining > 0) { - if (!bs->onSeek(bs->pUserData, (int)bytesRemaining, ma_dr_flac_seek_origin_current)) { - return MA_FALSE; - } - } - } else { - if (!bs->onSeek(bs->pUserData, (int)offsetFromStart, ma_dr_flac_seek_origin_start)) { - return MA_FALSE; - } - } - ma_dr_flac__reset_cache(bs); - return MA_TRUE; -} -static ma_result ma_dr_flac__read_utf8_coded_number(ma_dr_flac_bs* bs, ma_uint64* pNumberOut, ma_uint8* pCRCOut) -{ - ma_uint8 crc; - ma_uint64 result; - ma_uint8 utf8[7] = {0}; - int byteCount; - int i; - MA_DR_FLAC_ASSERT(bs != NULL); - MA_DR_FLAC_ASSERT(pNumberOut != NULL); - MA_DR_FLAC_ASSERT(pCRCOut != NULL); - crc = *pCRCOut; - if (!ma_dr_flac__read_uint8(bs, 8, utf8)) { - *pNumberOut = 0; - return MA_AT_END; - } - crc = ma_dr_flac_crc8(crc, utf8[0], 8); - if ((utf8[0] & 0x80) == 0) { - *pNumberOut = utf8[0]; - *pCRCOut = crc; - return MA_SUCCESS; - } - if ((utf8[0] & 0xE0) == 0xC0) { - byteCount = 2; - } else if ((utf8[0] & 0xF0) == 0xE0) { - byteCount = 3; - } else if ((utf8[0] & 0xF8) == 0xF0) { - byteCount = 4; - } else if ((utf8[0] & 0xFC) == 0xF8) { - byteCount = 5; - } else if ((utf8[0] & 0xFE) == 0xFC) { - byteCount = 6; - } else if ((utf8[0] & 0xFF) == 0xFE) { - byteCount = 7; - } else { - *pNumberOut = 0; - return MA_CRC_MISMATCH; - } - MA_DR_FLAC_ASSERT(byteCount > 1); - result = (ma_uint64)(utf8[0] & (0xFF >> (byteCount + 1))); - for (i = 1; i < byteCount; ++i) { - if (!ma_dr_flac__read_uint8(bs, 8, utf8 + i)) { - *pNumberOut = 0; - return MA_AT_END; - } - crc = ma_dr_flac_crc8(crc, utf8[i], 8); - result = (result << 6) | (utf8[i] & 0x3F); - } - *pNumberOut = result; - *pCRCOut = crc; - return MA_SUCCESS; -} -static MA_INLINE ma_uint32 ma_dr_flac__ilog2_u32(ma_uint32 x) -{ -#if 1 - ma_uint32 result = 0; - while (x > 0) { - result += 1; - x >>= 1; - } - return result; -#endif -} -static MA_INLINE ma_bool32 ma_dr_flac__use_64_bit_prediction(ma_uint32 bitsPerSample, ma_uint32 order, ma_uint32 precision) -{ - return bitsPerSample + precision + ma_dr_flac__ilog2_u32(order) > 32; -} -#if defined(__clang__) -__attribute__((no_sanitize("signed-integer-overflow"))) -#endif -static MA_INLINE ma_int32 ma_dr_flac__calculate_prediction_32(ma_uint32 order, ma_int32 shift, const ma_int32* coefficients, ma_int32* pDecodedSamples) -{ - ma_int32 prediction = 0; - MA_DR_FLAC_ASSERT(order <= 32); - switch (order) - { - case 32: prediction += coefficients[31] * pDecodedSamples[-32]; - case 31: prediction += coefficients[30] * pDecodedSamples[-31]; - case 30: prediction += coefficients[29] * pDecodedSamples[-30]; - case 29: prediction += coefficients[28] * pDecodedSamples[-29]; - case 28: prediction += coefficients[27] * pDecodedSamples[-28]; - case 27: prediction += coefficients[26] * pDecodedSamples[-27]; - case 26: prediction += coefficients[25] * pDecodedSamples[-26]; - case 25: prediction += coefficients[24] * pDecodedSamples[-25]; - case 24: prediction += coefficients[23] * pDecodedSamples[-24]; - case 23: prediction += coefficients[22] * pDecodedSamples[-23]; - case 22: prediction += coefficients[21] * pDecodedSamples[-22]; - case 21: prediction += coefficients[20] * pDecodedSamples[-21]; - case 20: prediction += coefficients[19] * pDecodedSamples[-20]; - case 19: prediction += coefficients[18] * pDecodedSamples[-19]; - case 18: prediction += coefficients[17] * pDecodedSamples[-18]; - case 17: prediction += coefficients[16] * pDecodedSamples[-17]; - case 16: prediction += coefficients[15] * pDecodedSamples[-16]; - case 15: prediction += coefficients[14] * pDecodedSamples[-15]; - case 14: prediction += coefficients[13] * pDecodedSamples[-14]; - case 13: prediction += coefficients[12] * pDecodedSamples[-13]; - case 12: prediction += coefficients[11] * pDecodedSamples[-12]; - case 11: prediction += coefficients[10] * pDecodedSamples[-11]; - case 10: prediction += coefficients[ 9] * pDecodedSamples[-10]; - case 9: prediction += coefficients[ 8] * pDecodedSamples[- 9]; - case 8: prediction += coefficients[ 7] * pDecodedSamples[- 8]; - case 7: prediction += coefficients[ 6] * pDecodedSamples[- 7]; - case 6: prediction += coefficients[ 5] * pDecodedSamples[- 6]; - case 5: prediction += coefficients[ 4] * pDecodedSamples[- 5]; - case 4: prediction += coefficients[ 3] * pDecodedSamples[- 4]; - case 3: prediction += coefficients[ 2] * pDecodedSamples[- 3]; - case 2: prediction += coefficients[ 1] * pDecodedSamples[- 2]; - case 1: prediction += coefficients[ 0] * pDecodedSamples[- 1]; - } - return (ma_int32)(prediction >> shift); -} -static MA_INLINE ma_int32 ma_dr_flac__calculate_prediction_64(ma_uint32 order, ma_int32 shift, const ma_int32* coefficients, ma_int32* pDecodedSamples) -{ - ma_int64 prediction; - MA_DR_FLAC_ASSERT(order <= 32); -#ifndef MA_64BIT - if (order == 8) - { - prediction = coefficients[0] * (ma_int64)pDecodedSamples[-1]; - prediction += coefficients[1] * (ma_int64)pDecodedSamples[-2]; - prediction += coefficients[2] * (ma_int64)pDecodedSamples[-3]; - prediction += coefficients[3] * (ma_int64)pDecodedSamples[-4]; - prediction += coefficients[4] * (ma_int64)pDecodedSamples[-5]; - prediction += coefficients[5] * (ma_int64)pDecodedSamples[-6]; - prediction += coefficients[6] * (ma_int64)pDecodedSamples[-7]; - prediction += coefficients[7] * (ma_int64)pDecodedSamples[-8]; - } - else if (order == 7) - { - prediction = coefficients[0] * (ma_int64)pDecodedSamples[-1]; - prediction += coefficients[1] * (ma_int64)pDecodedSamples[-2]; - prediction += coefficients[2] * (ma_int64)pDecodedSamples[-3]; - prediction += coefficients[3] * (ma_int64)pDecodedSamples[-4]; - prediction += coefficients[4] * (ma_int64)pDecodedSamples[-5]; - prediction += coefficients[5] * (ma_int64)pDecodedSamples[-6]; - prediction += coefficients[6] * (ma_int64)pDecodedSamples[-7]; - } - else if (order == 3) - { - prediction = coefficients[0] * (ma_int64)pDecodedSamples[-1]; - prediction += coefficients[1] * (ma_int64)pDecodedSamples[-2]; - prediction += coefficients[2] * (ma_int64)pDecodedSamples[-3]; - } - else if (order == 6) - { - prediction = coefficients[0] * (ma_int64)pDecodedSamples[-1]; - prediction += coefficients[1] * (ma_int64)pDecodedSamples[-2]; - prediction += coefficients[2] * (ma_int64)pDecodedSamples[-3]; - prediction += coefficients[3] * (ma_int64)pDecodedSamples[-4]; - prediction += coefficients[4] * (ma_int64)pDecodedSamples[-5]; - prediction += coefficients[5] * (ma_int64)pDecodedSamples[-6]; - } - else if (order == 5) - { - prediction = coefficients[0] * (ma_int64)pDecodedSamples[-1]; - prediction += coefficients[1] * (ma_int64)pDecodedSamples[-2]; - prediction += coefficients[2] * (ma_int64)pDecodedSamples[-3]; - prediction += coefficients[3] * (ma_int64)pDecodedSamples[-4]; - prediction += coefficients[4] * (ma_int64)pDecodedSamples[-5]; - } - else if (order == 4) - { - prediction = coefficients[0] * (ma_int64)pDecodedSamples[-1]; - prediction += coefficients[1] * (ma_int64)pDecodedSamples[-2]; - prediction += coefficients[2] * (ma_int64)pDecodedSamples[-3]; - prediction += coefficients[3] * (ma_int64)pDecodedSamples[-4]; - } - else if (order == 12) - { - prediction = coefficients[0] * (ma_int64)pDecodedSamples[-1]; - prediction += coefficients[1] * (ma_int64)pDecodedSamples[-2]; - prediction += coefficients[2] * (ma_int64)pDecodedSamples[-3]; - prediction += coefficients[3] * (ma_int64)pDecodedSamples[-4]; - prediction += coefficients[4] * (ma_int64)pDecodedSamples[-5]; - prediction += coefficients[5] * (ma_int64)pDecodedSamples[-6]; - prediction += coefficients[6] * (ma_int64)pDecodedSamples[-7]; - prediction += coefficients[7] * (ma_int64)pDecodedSamples[-8]; - prediction += coefficients[8] * (ma_int64)pDecodedSamples[-9]; - prediction += coefficients[9] * (ma_int64)pDecodedSamples[-10]; - prediction += coefficients[10] * (ma_int64)pDecodedSamples[-11]; - prediction += coefficients[11] * (ma_int64)pDecodedSamples[-12]; - } - else if (order == 2) - { - prediction = coefficients[0] * (ma_int64)pDecodedSamples[-1]; - prediction += coefficients[1] * (ma_int64)pDecodedSamples[-2]; - } - else if (order == 1) - { - prediction = coefficients[0] * (ma_int64)pDecodedSamples[-1]; - } - else if (order == 10) - { - prediction = coefficients[0] * (ma_int64)pDecodedSamples[-1]; - prediction += coefficients[1] * (ma_int64)pDecodedSamples[-2]; - prediction += coefficients[2] * (ma_int64)pDecodedSamples[-3]; - prediction += coefficients[3] * (ma_int64)pDecodedSamples[-4]; - prediction += coefficients[4] * (ma_int64)pDecodedSamples[-5]; - prediction += coefficients[5] * (ma_int64)pDecodedSamples[-6]; - prediction += coefficients[6] * (ma_int64)pDecodedSamples[-7]; - prediction += coefficients[7] * (ma_int64)pDecodedSamples[-8]; - prediction += coefficients[8] * (ma_int64)pDecodedSamples[-9]; - prediction += coefficients[9] * (ma_int64)pDecodedSamples[-10]; - } - else if (order == 9) - { - prediction = coefficients[0] * (ma_int64)pDecodedSamples[-1]; - prediction += coefficients[1] * (ma_int64)pDecodedSamples[-2]; - prediction += coefficients[2] * (ma_int64)pDecodedSamples[-3]; - prediction += coefficients[3] * (ma_int64)pDecodedSamples[-4]; - prediction += coefficients[4] * (ma_int64)pDecodedSamples[-5]; - prediction += coefficients[5] * (ma_int64)pDecodedSamples[-6]; - prediction += coefficients[6] * (ma_int64)pDecodedSamples[-7]; - prediction += coefficients[7] * (ma_int64)pDecodedSamples[-8]; - prediction += coefficients[8] * (ma_int64)pDecodedSamples[-9]; - } - else if (order == 11) - { - prediction = coefficients[0] * (ma_int64)pDecodedSamples[-1]; - prediction += coefficients[1] * (ma_int64)pDecodedSamples[-2]; - prediction += coefficients[2] * (ma_int64)pDecodedSamples[-3]; - prediction += coefficients[3] * (ma_int64)pDecodedSamples[-4]; - prediction += coefficients[4] * (ma_int64)pDecodedSamples[-5]; - prediction += coefficients[5] * (ma_int64)pDecodedSamples[-6]; - prediction += coefficients[6] * (ma_int64)pDecodedSamples[-7]; - prediction += coefficients[7] * (ma_int64)pDecodedSamples[-8]; - prediction += coefficients[8] * (ma_int64)pDecodedSamples[-9]; - prediction += coefficients[9] * (ma_int64)pDecodedSamples[-10]; - prediction += coefficients[10] * (ma_int64)pDecodedSamples[-11]; - } - else - { - int j; - prediction = 0; - for (j = 0; j < (int)order; ++j) { - prediction += coefficients[j] * (ma_int64)pDecodedSamples[-j-1]; - } - } -#endif -#ifdef MA_64BIT - prediction = 0; - switch (order) - { - case 32: prediction += coefficients[31] * (ma_int64)pDecodedSamples[-32]; - case 31: prediction += coefficients[30] * (ma_int64)pDecodedSamples[-31]; - case 30: prediction += coefficients[29] * (ma_int64)pDecodedSamples[-30]; - case 29: prediction += coefficients[28] * (ma_int64)pDecodedSamples[-29]; - case 28: prediction += coefficients[27] * (ma_int64)pDecodedSamples[-28]; - case 27: prediction += coefficients[26] * (ma_int64)pDecodedSamples[-27]; - case 26: prediction += coefficients[25] * (ma_int64)pDecodedSamples[-26]; - case 25: prediction += coefficients[24] * (ma_int64)pDecodedSamples[-25]; - case 24: prediction += coefficients[23] * (ma_int64)pDecodedSamples[-24]; - case 23: prediction += coefficients[22] * (ma_int64)pDecodedSamples[-23]; - case 22: prediction += coefficients[21] * (ma_int64)pDecodedSamples[-22]; - case 21: prediction += coefficients[20] * (ma_int64)pDecodedSamples[-21]; - case 20: prediction += coefficients[19] * (ma_int64)pDecodedSamples[-20]; - case 19: prediction += coefficients[18] * (ma_int64)pDecodedSamples[-19]; - case 18: prediction += coefficients[17] * (ma_int64)pDecodedSamples[-18]; - case 17: prediction += coefficients[16] * (ma_int64)pDecodedSamples[-17]; - case 16: prediction += coefficients[15] * (ma_int64)pDecodedSamples[-16]; - case 15: prediction += coefficients[14] * (ma_int64)pDecodedSamples[-15]; - case 14: prediction += coefficients[13] * (ma_int64)pDecodedSamples[-14]; - case 13: prediction += coefficients[12] * (ma_int64)pDecodedSamples[-13]; - case 12: prediction += coefficients[11] * (ma_int64)pDecodedSamples[-12]; - case 11: prediction += coefficients[10] * (ma_int64)pDecodedSamples[-11]; - case 10: prediction += coefficients[ 9] * (ma_int64)pDecodedSamples[-10]; - case 9: prediction += coefficients[ 8] * (ma_int64)pDecodedSamples[- 9]; - case 8: prediction += coefficients[ 7] * (ma_int64)pDecodedSamples[- 8]; - case 7: prediction += coefficients[ 6] * (ma_int64)pDecodedSamples[- 7]; - case 6: prediction += coefficients[ 5] * (ma_int64)pDecodedSamples[- 6]; - case 5: prediction += coefficients[ 4] * (ma_int64)pDecodedSamples[- 5]; - case 4: prediction += coefficients[ 3] * (ma_int64)pDecodedSamples[- 4]; - case 3: prediction += coefficients[ 2] * (ma_int64)pDecodedSamples[- 3]; - case 2: prediction += coefficients[ 1] * (ma_int64)pDecodedSamples[- 2]; - case 1: prediction += coefficients[ 0] * (ma_int64)pDecodedSamples[- 1]; - } -#endif - return (ma_int32)(prediction >> shift); -} -#if 0 -static ma_bool32 ma_dr_flac__decode_samples_with_residual__rice__reference(ma_dr_flac_bs* bs, ma_uint32 bitsPerSample, ma_uint32 count, ma_uint8 riceParam, ma_uint32 lpcOrder, ma_int32 lpcShift, ma_uint32 lpcPrecision, const ma_int32* coefficients, ma_int32* pSamplesOut) -{ - ma_uint32 i; - MA_DR_FLAC_ASSERT(bs != NULL); - MA_DR_FLAC_ASSERT(pSamplesOut != NULL); - for (i = 0; i < count; ++i) { - ma_uint32 zeroCounter = 0; - for (;;) { - ma_uint8 bit; - if (!ma_dr_flac__read_uint8(bs, 1, &bit)) { - return MA_FALSE; - } - if (bit == 0) { - zeroCounter += 1; - } else { - break; - } - } - ma_uint32 decodedRice; - if (riceParam > 0) { - if (!ma_dr_flac__read_uint32(bs, riceParam, &decodedRice)) { - return MA_FALSE; - } - } else { - decodedRice = 0; - } - decodedRice |= (zeroCounter << riceParam); - if ((decodedRice & 0x01)) { - decodedRice = ~(decodedRice >> 1); - } else { - decodedRice = (decodedRice >> 1); - } - if (ma_dr_flac__use_64_bit_prediction(bitsPerSample, lpcOrder, lpcPrecision)) { - pSamplesOut[i] = decodedRice + ma_dr_flac__calculate_prediction_64(lpcOrder, lpcShift, coefficients, pSamplesOut + i); - } else { - pSamplesOut[i] = decodedRice + ma_dr_flac__calculate_prediction_32(lpcOrder, lpcShift, coefficients, pSamplesOut + i); - } - } - return MA_TRUE; -} -#endif -#if 0 -static ma_bool32 ma_dr_flac__read_rice_parts__reference(ma_dr_flac_bs* bs, ma_uint8 riceParam, ma_uint32* pZeroCounterOut, ma_uint32* pRiceParamPartOut) -{ - ma_uint32 zeroCounter = 0; - ma_uint32 decodedRice; - for (;;) { - ma_uint8 bit; - if (!ma_dr_flac__read_uint8(bs, 1, &bit)) { - return MA_FALSE; - } - if (bit == 0) { - zeroCounter += 1; - } else { - break; - } - } - if (riceParam > 0) { - if (!ma_dr_flac__read_uint32(bs, riceParam, &decodedRice)) { - return MA_FALSE; - } - } else { - decodedRice = 0; - } - *pZeroCounterOut = zeroCounter; - *pRiceParamPartOut = decodedRice; - return MA_TRUE; -} -#endif -#if 0 -static MA_INLINE ma_bool32 ma_dr_flac__read_rice_parts(ma_dr_flac_bs* bs, ma_uint8 riceParam, ma_uint32* pZeroCounterOut, ma_uint32* pRiceParamPartOut) -{ - ma_dr_flac_cache_t riceParamMask; - ma_uint32 zeroCounter; - ma_uint32 setBitOffsetPlus1; - ma_uint32 riceParamPart; - ma_uint32 riceLength; - MA_DR_FLAC_ASSERT(riceParam > 0); - riceParamMask = MA_DR_FLAC_CACHE_L1_SELECTION_MASK(riceParam); - zeroCounter = 0; - while (bs->cache == 0) { - zeroCounter += (ma_uint32)MA_DR_FLAC_CACHE_L1_BITS_REMAINING(bs); - if (!ma_dr_flac__reload_cache(bs)) { - return MA_FALSE; - } - } - setBitOffsetPlus1 = ma_dr_flac__clz(bs->cache); - zeroCounter += setBitOffsetPlus1; - setBitOffsetPlus1 += 1; - riceLength = setBitOffsetPlus1 + riceParam; - if (riceLength < MA_DR_FLAC_CACHE_L1_BITS_REMAINING(bs)) { - riceParamPart = (ma_uint32)((bs->cache & (riceParamMask >> setBitOffsetPlus1)) >> MA_DR_FLAC_CACHE_L1_SELECTION_SHIFT(bs, riceLength)); - bs->consumedBits += riceLength; - bs->cache <<= riceLength; - } else { - ma_uint32 bitCountLo; - ma_dr_flac_cache_t resultHi; - bs->consumedBits += riceLength; - bs->cache <<= setBitOffsetPlus1 & (MA_DR_FLAC_CACHE_L1_SIZE_BITS(bs)-1); - bitCountLo = bs->consumedBits - MA_DR_FLAC_CACHE_L1_SIZE_BITS(bs); - resultHi = MA_DR_FLAC_CACHE_L1_SELECT_AND_SHIFT(bs, riceParam); - if (bs->nextL2Line < MA_DR_FLAC_CACHE_L2_LINE_COUNT(bs)) { -#ifndef MA_DR_FLAC_NO_CRC - ma_dr_flac__update_crc16(bs); -#endif - bs->cache = ma_dr_flac__be2host__cache_line(bs->cacheL2[bs->nextL2Line++]); - bs->consumedBits = 0; -#ifndef MA_DR_FLAC_NO_CRC - bs->crc16Cache = bs->cache; -#endif - } else { - if (!ma_dr_flac__reload_cache(bs)) { - return MA_FALSE; - } - if (bitCountLo > MA_DR_FLAC_CACHE_L1_BITS_REMAINING(bs)) { - return MA_FALSE; - } - } - riceParamPart = (ma_uint32)(resultHi | MA_DR_FLAC_CACHE_L1_SELECT_AND_SHIFT_SAFE(bs, bitCountLo)); - bs->consumedBits += bitCountLo; - bs->cache <<= bitCountLo; - } - pZeroCounterOut[0] = zeroCounter; - pRiceParamPartOut[0] = riceParamPart; - return MA_TRUE; -} -#endif -static MA_INLINE ma_bool32 ma_dr_flac__read_rice_parts_x1(ma_dr_flac_bs* bs, ma_uint8 riceParam, ma_uint32* pZeroCounterOut, ma_uint32* pRiceParamPartOut) -{ - ma_uint32 riceParamPlus1 = riceParam + 1; - ma_uint32 riceParamPlus1Shift = MA_DR_FLAC_CACHE_L1_SELECTION_SHIFT(bs, riceParamPlus1); - ma_uint32 riceParamPlus1MaxConsumedBits = MA_DR_FLAC_CACHE_L1_SIZE_BITS(bs) - riceParamPlus1; - ma_dr_flac_cache_t bs_cache = bs->cache; - ma_uint32 bs_consumedBits = bs->consumedBits; - ma_uint32 lzcount = ma_dr_flac__clz(bs_cache); - if (lzcount < sizeof(bs_cache)*8) { - pZeroCounterOut[0] = lzcount; - extract_rice_param_part: - bs_cache <<= lzcount; - bs_consumedBits += lzcount; - if (bs_consumedBits <= riceParamPlus1MaxConsumedBits) { - pRiceParamPartOut[0] = (ma_uint32)(bs_cache >> riceParamPlus1Shift); - bs_cache <<= riceParamPlus1; - bs_consumedBits += riceParamPlus1; - } else { - ma_uint32 riceParamPartHi; - ma_uint32 riceParamPartLo; - ma_uint32 riceParamPartLoBitCount; - riceParamPartHi = (ma_uint32)(bs_cache >> riceParamPlus1Shift); - riceParamPartLoBitCount = bs_consumedBits - riceParamPlus1MaxConsumedBits; - MA_DR_FLAC_ASSERT(riceParamPartLoBitCount > 0 && riceParamPartLoBitCount < 32); - if (bs->nextL2Line < MA_DR_FLAC_CACHE_L2_LINE_COUNT(bs)) { - #ifndef MA_DR_FLAC_NO_CRC - ma_dr_flac__update_crc16(bs); - #endif - bs_cache = ma_dr_flac__be2host__cache_line(bs->cacheL2[bs->nextL2Line++]); - bs_consumedBits = riceParamPartLoBitCount; - #ifndef MA_DR_FLAC_NO_CRC - bs->crc16Cache = bs_cache; - #endif - } else { - if (!ma_dr_flac__reload_cache(bs)) { - return MA_FALSE; - } - if (riceParamPartLoBitCount > MA_DR_FLAC_CACHE_L1_BITS_REMAINING(bs)) { - return MA_FALSE; - } - bs_cache = bs->cache; - bs_consumedBits = bs->consumedBits + riceParamPartLoBitCount; - } - riceParamPartLo = (ma_uint32)(bs_cache >> (MA_DR_FLAC_CACHE_L1_SELECTION_SHIFT(bs, riceParamPartLoBitCount))); - pRiceParamPartOut[0] = riceParamPartHi | riceParamPartLo; - bs_cache <<= riceParamPartLoBitCount; - } - } else { - ma_uint32 zeroCounter = (ma_uint32)(MA_DR_FLAC_CACHE_L1_SIZE_BITS(bs) - bs_consumedBits); - for (;;) { - if (bs->nextL2Line < MA_DR_FLAC_CACHE_L2_LINE_COUNT(bs)) { - #ifndef MA_DR_FLAC_NO_CRC - ma_dr_flac__update_crc16(bs); - #endif - bs_cache = ma_dr_flac__be2host__cache_line(bs->cacheL2[bs->nextL2Line++]); - bs_consumedBits = 0; - #ifndef MA_DR_FLAC_NO_CRC - bs->crc16Cache = bs_cache; - #endif - } else { - if (!ma_dr_flac__reload_cache(bs)) { - return MA_FALSE; - } - bs_cache = bs->cache; - bs_consumedBits = bs->consumedBits; - } - lzcount = ma_dr_flac__clz(bs_cache); - zeroCounter += lzcount; - if (lzcount < sizeof(bs_cache)*8) { - break; - } - } - pZeroCounterOut[0] = zeroCounter; - goto extract_rice_param_part; - } - bs->cache = bs_cache; - bs->consumedBits = bs_consumedBits; - return MA_TRUE; -} -static MA_INLINE ma_bool32 ma_dr_flac__seek_rice_parts(ma_dr_flac_bs* bs, ma_uint8 riceParam) -{ - ma_uint32 riceParamPlus1 = riceParam + 1; - ma_uint32 riceParamPlus1MaxConsumedBits = MA_DR_FLAC_CACHE_L1_SIZE_BITS(bs) - riceParamPlus1; - ma_dr_flac_cache_t bs_cache = bs->cache; - ma_uint32 bs_consumedBits = bs->consumedBits; - ma_uint32 lzcount = ma_dr_flac__clz(bs_cache); - if (lzcount < sizeof(bs_cache)*8) { - extract_rice_param_part: - bs_cache <<= lzcount; - bs_consumedBits += lzcount; - if (bs_consumedBits <= riceParamPlus1MaxConsumedBits) { - bs_cache <<= riceParamPlus1; - bs_consumedBits += riceParamPlus1; - } else { - ma_uint32 riceParamPartLoBitCount = bs_consumedBits - riceParamPlus1MaxConsumedBits; - MA_DR_FLAC_ASSERT(riceParamPartLoBitCount > 0 && riceParamPartLoBitCount < 32); - if (bs->nextL2Line < MA_DR_FLAC_CACHE_L2_LINE_COUNT(bs)) { - #ifndef MA_DR_FLAC_NO_CRC - ma_dr_flac__update_crc16(bs); - #endif - bs_cache = ma_dr_flac__be2host__cache_line(bs->cacheL2[bs->nextL2Line++]); - bs_consumedBits = riceParamPartLoBitCount; - #ifndef MA_DR_FLAC_NO_CRC - bs->crc16Cache = bs_cache; - #endif - } else { - if (!ma_dr_flac__reload_cache(bs)) { - return MA_FALSE; - } - if (riceParamPartLoBitCount > MA_DR_FLAC_CACHE_L1_BITS_REMAINING(bs)) { - return MA_FALSE; - } - bs_cache = bs->cache; - bs_consumedBits = bs->consumedBits + riceParamPartLoBitCount; - } - bs_cache <<= riceParamPartLoBitCount; - } - } else { - for (;;) { - if (bs->nextL2Line < MA_DR_FLAC_CACHE_L2_LINE_COUNT(bs)) { - #ifndef MA_DR_FLAC_NO_CRC - ma_dr_flac__update_crc16(bs); - #endif - bs_cache = ma_dr_flac__be2host__cache_line(bs->cacheL2[bs->nextL2Line++]); - bs_consumedBits = 0; - #ifndef MA_DR_FLAC_NO_CRC - bs->crc16Cache = bs_cache; - #endif - } else { - if (!ma_dr_flac__reload_cache(bs)) { - return MA_FALSE; - } - bs_cache = bs->cache; - bs_consumedBits = bs->consumedBits; - } - lzcount = ma_dr_flac__clz(bs_cache); - if (lzcount < sizeof(bs_cache)*8) { - break; - } - } - goto extract_rice_param_part; - } - bs->cache = bs_cache; - bs->consumedBits = bs_consumedBits; - return MA_TRUE; -} -static ma_bool32 ma_dr_flac__decode_samples_with_residual__rice__scalar_zeroorder(ma_dr_flac_bs* bs, ma_uint32 bitsPerSample, ma_uint32 count, ma_uint8 riceParam, ma_uint32 order, ma_int32 shift, const ma_int32* coefficients, ma_int32* pSamplesOut) -{ - ma_uint32 t[2] = {0x00000000, 0xFFFFFFFF}; - ma_uint32 zeroCountPart0; - ma_uint32 riceParamPart0; - ma_uint32 riceParamMask; - ma_uint32 i; - MA_DR_FLAC_ASSERT(bs != NULL); - MA_DR_FLAC_ASSERT(pSamplesOut != NULL); - (void)bitsPerSample; - (void)order; - (void)shift; - (void)coefficients; - riceParamMask = (ma_uint32)~((~0UL) << riceParam); - i = 0; - while (i < count) { - if (!ma_dr_flac__read_rice_parts_x1(bs, riceParam, &zeroCountPart0, &riceParamPart0)) { - return MA_FALSE; - } - riceParamPart0 &= riceParamMask; - riceParamPart0 |= (zeroCountPart0 << riceParam); - riceParamPart0 = (riceParamPart0 >> 1) ^ t[riceParamPart0 & 0x01]; - pSamplesOut[i] = riceParamPart0; - i += 1; - } - return MA_TRUE; -} -static ma_bool32 ma_dr_flac__decode_samples_with_residual__rice__scalar(ma_dr_flac_bs* bs, ma_uint32 bitsPerSample, ma_uint32 count, ma_uint8 riceParam, ma_uint32 lpcOrder, ma_int32 lpcShift, ma_uint32 lpcPrecision, const ma_int32* coefficients, ma_int32* pSamplesOut) -{ - ma_uint32 t[2] = {0x00000000, 0xFFFFFFFF}; - ma_uint32 zeroCountPart0 = 0; - ma_uint32 zeroCountPart1 = 0; - ma_uint32 zeroCountPart2 = 0; - ma_uint32 zeroCountPart3 = 0; - ma_uint32 riceParamPart0 = 0; - ma_uint32 riceParamPart1 = 0; - ma_uint32 riceParamPart2 = 0; - ma_uint32 riceParamPart3 = 0; - ma_uint32 riceParamMask; - const ma_int32* pSamplesOutEnd; - ma_uint32 i; - MA_DR_FLAC_ASSERT(bs != NULL); - MA_DR_FLAC_ASSERT(pSamplesOut != NULL); - if (lpcOrder == 0) { - return ma_dr_flac__decode_samples_with_residual__rice__scalar_zeroorder(bs, bitsPerSample, count, riceParam, lpcOrder, lpcShift, coefficients, pSamplesOut); - } - riceParamMask = (ma_uint32)~((~0UL) << riceParam); - pSamplesOutEnd = pSamplesOut + (count & ~3); - if (ma_dr_flac__use_64_bit_prediction(bitsPerSample, lpcOrder, lpcPrecision)) { - while (pSamplesOut < pSamplesOutEnd) { - if (!ma_dr_flac__read_rice_parts_x1(bs, riceParam, &zeroCountPart0, &riceParamPart0) || - !ma_dr_flac__read_rice_parts_x1(bs, riceParam, &zeroCountPart1, &riceParamPart1) || - !ma_dr_flac__read_rice_parts_x1(bs, riceParam, &zeroCountPart2, &riceParamPart2) || - !ma_dr_flac__read_rice_parts_x1(bs, riceParam, &zeroCountPart3, &riceParamPart3)) { - return MA_FALSE; - } - riceParamPart0 &= riceParamMask; - riceParamPart1 &= riceParamMask; - riceParamPart2 &= riceParamMask; - riceParamPart3 &= riceParamMask; - riceParamPart0 |= (zeroCountPart0 << riceParam); - riceParamPart1 |= (zeroCountPart1 << riceParam); - riceParamPart2 |= (zeroCountPart2 << riceParam); - riceParamPart3 |= (zeroCountPart3 << riceParam); - riceParamPart0 = (riceParamPart0 >> 1) ^ t[riceParamPart0 & 0x01]; - riceParamPart1 = (riceParamPart1 >> 1) ^ t[riceParamPart1 & 0x01]; - riceParamPart2 = (riceParamPart2 >> 1) ^ t[riceParamPart2 & 0x01]; - riceParamPart3 = (riceParamPart3 >> 1) ^ t[riceParamPart3 & 0x01]; - pSamplesOut[0] = riceParamPart0 + ma_dr_flac__calculate_prediction_64(lpcOrder, lpcShift, coefficients, pSamplesOut + 0); - pSamplesOut[1] = riceParamPart1 + ma_dr_flac__calculate_prediction_64(lpcOrder, lpcShift, coefficients, pSamplesOut + 1); - pSamplesOut[2] = riceParamPart2 + ma_dr_flac__calculate_prediction_64(lpcOrder, lpcShift, coefficients, pSamplesOut + 2); - pSamplesOut[3] = riceParamPart3 + ma_dr_flac__calculate_prediction_64(lpcOrder, lpcShift, coefficients, pSamplesOut + 3); - pSamplesOut += 4; - } - } else { - while (pSamplesOut < pSamplesOutEnd) { - if (!ma_dr_flac__read_rice_parts_x1(bs, riceParam, &zeroCountPart0, &riceParamPart0) || - !ma_dr_flac__read_rice_parts_x1(bs, riceParam, &zeroCountPart1, &riceParamPart1) || - !ma_dr_flac__read_rice_parts_x1(bs, riceParam, &zeroCountPart2, &riceParamPart2) || - !ma_dr_flac__read_rice_parts_x1(bs, riceParam, &zeroCountPart3, &riceParamPart3)) { - return MA_FALSE; - } - riceParamPart0 &= riceParamMask; - riceParamPart1 &= riceParamMask; - riceParamPart2 &= riceParamMask; - riceParamPart3 &= riceParamMask; - riceParamPart0 |= (zeroCountPart0 << riceParam); - riceParamPart1 |= (zeroCountPart1 << riceParam); - riceParamPart2 |= (zeroCountPart2 << riceParam); - riceParamPart3 |= (zeroCountPart3 << riceParam); - riceParamPart0 = (riceParamPart0 >> 1) ^ t[riceParamPart0 & 0x01]; - riceParamPart1 = (riceParamPart1 >> 1) ^ t[riceParamPart1 & 0x01]; - riceParamPart2 = (riceParamPart2 >> 1) ^ t[riceParamPart2 & 0x01]; - riceParamPart3 = (riceParamPart3 >> 1) ^ t[riceParamPart3 & 0x01]; - pSamplesOut[0] = riceParamPart0 + ma_dr_flac__calculate_prediction_32(lpcOrder, lpcShift, coefficients, pSamplesOut + 0); - pSamplesOut[1] = riceParamPart1 + ma_dr_flac__calculate_prediction_32(lpcOrder, lpcShift, coefficients, pSamplesOut + 1); - pSamplesOut[2] = riceParamPart2 + ma_dr_flac__calculate_prediction_32(lpcOrder, lpcShift, coefficients, pSamplesOut + 2); - pSamplesOut[3] = riceParamPart3 + ma_dr_flac__calculate_prediction_32(lpcOrder, lpcShift, coefficients, pSamplesOut + 3); - pSamplesOut += 4; - } - } - i = (count & ~3); - while (i < count) { - if (!ma_dr_flac__read_rice_parts_x1(bs, riceParam, &zeroCountPart0, &riceParamPart0)) { - return MA_FALSE; - } - riceParamPart0 &= riceParamMask; - riceParamPart0 |= (zeroCountPart0 << riceParam); - riceParamPart0 = (riceParamPart0 >> 1) ^ t[riceParamPart0 & 0x01]; - if (ma_dr_flac__use_64_bit_prediction(bitsPerSample, lpcOrder, lpcPrecision)) { - pSamplesOut[0] = riceParamPart0 + ma_dr_flac__calculate_prediction_64(lpcOrder, lpcShift, coefficients, pSamplesOut + 0); - } else { - pSamplesOut[0] = riceParamPart0 + ma_dr_flac__calculate_prediction_32(lpcOrder, lpcShift, coefficients, pSamplesOut + 0); - } - i += 1; - pSamplesOut += 1; - } - return MA_TRUE; -} -#if defined(MA_DR_FLAC_SUPPORT_SSE2) -static MA_INLINE __m128i ma_dr_flac__mm_packs_interleaved_epi32(__m128i a, __m128i b) -{ - __m128i r; - r = _mm_packs_epi32(a, b); - r = _mm_shuffle_epi32(r, _MM_SHUFFLE(3, 1, 2, 0)); - r = _mm_shufflehi_epi16(r, _MM_SHUFFLE(3, 1, 2, 0)); - r = _mm_shufflelo_epi16(r, _MM_SHUFFLE(3, 1, 2, 0)); - return r; -} -#endif -#if defined(MA_DR_FLAC_SUPPORT_SSE41) -static MA_INLINE __m128i ma_dr_flac__mm_not_si128(__m128i a) -{ - return _mm_xor_si128(a, _mm_cmpeq_epi32(_mm_setzero_si128(), _mm_setzero_si128())); -} -static MA_INLINE __m128i ma_dr_flac__mm_hadd_epi32(__m128i x) -{ - __m128i x64 = _mm_add_epi32(x, _mm_shuffle_epi32(x, _MM_SHUFFLE(1, 0, 3, 2))); - __m128i x32 = _mm_shufflelo_epi16(x64, _MM_SHUFFLE(1, 0, 3, 2)); - return _mm_add_epi32(x64, x32); -} -static MA_INLINE __m128i ma_dr_flac__mm_hadd_epi64(__m128i x) -{ - return _mm_add_epi64(x, _mm_shuffle_epi32(x, _MM_SHUFFLE(1, 0, 3, 2))); -} -static MA_INLINE __m128i ma_dr_flac__mm_srai_epi64(__m128i x, int count) -{ - __m128i lo = _mm_srli_epi64(x, count); - __m128i hi = _mm_srai_epi32(x, count); - hi = _mm_and_si128(hi, _mm_set_epi32(0xFFFFFFFF, 0, 0xFFFFFFFF, 0)); - return _mm_or_si128(lo, hi); -} -static ma_bool32 ma_dr_flac__decode_samples_with_residual__rice__sse41_32(ma_dr_flac_bs* bs, ma_uint32 count, ma_uint8 riceParam, ma_uint32 order, ma_int32 shift, const ma_int32* coefficients, ma_int32* pSamplesOut) -{ - int i; - ma_uint32 riceParamMask; - ma_int32* pDecodedSamples = pSamplesOut; - ma_int32* pDecodedSamplesEnd = pSamplesOut + (count & ~3); - ma_uint32 zeroCountParts0 = 0; - ma_uint32 zeroCountParts1 = 0; - ma_uint32 zeroCountParts2 = 0; - ma_uint32 zeroCountParts3 = 0; - ma_uint32 riceParamParts0 = 0; - ma_uint32 riceParamParts1 = 0; - ma_uint32 riceParamParts2 = 0; - ma_uint32 riceParamParts3 = 0; - __m128i coefficients128_0; - __m128i coefficients128_4; - __m128i coefficients128_8; - __m128i samples128_0; - __m128i samples128_4; - __m128i samples128_8; - __m128i riceParamMask128; - const ma_uint32 t[2] = {0x00000000, 0xFFFFFFFF}; - riceParamMask = (ma_uint32)~((~0UL) << riceParam); - riceParamMask128 = _mm_set1_epi32(riceParamMask); - coefficients128_0 = _mm_setzero_si128(); - coefficients128_4 = _mm_setzero_si128(); - coefficients128_8 = _mm_setzero_si128(); - samples128_0 = _mm_setzero_si128(); - samples128_4 = _mm_setzero_si128(); - samples128_8 = _mm_setzero_si128(); -#if 1 - { - int runningOrder = order; - if (runningOrder >= 4) { - coefficients128_0 = _mm_loadu_si128((const __m128i*)(coefficients + 0)); - samples128_0 = _mm_loadu_si128((const __m128i*)(pSamplesOut - 4)); - runningOrder -= 4; - } else { - switch (runningOrder) { - case 3: coefficients128_0 = _mm_set_epi32(0, coefficients[2], coefficients[1], coefficients[0]); samples128_0 = _mm_set_epi32(pSamplesOut[-1], pSamplesOut[-2], pSamplesOut[-3], 0); break; - case 2: coefficients128_0 = _mm_set_epi32(0, 0, coefficients[1], coefficients[0]); samples128_0 = _mm_set_epi32(pSamplesOut[-1], pSamplesOut[-2], 0, 0); break; - case 1: coefficients128_0 = _mm_set_epi32(0, 0, 0, coefficients[0]); samples128_0 = _mm_set_epi32(pSamplesOut[-1], 0, 0, 0); break; - } - runningOrder = 0; - } - if (runningOrder >= 4) { - coefficients128_4 = _mm_loadu_si128((const __m128i*)(coefficients + 4)); - samples128_4 = _mm_loadu_si128((const __m128i*)(pSamplesOut - 8)); - runningOrder -= 4; - } else { - switch (runningOrder) { - case 3: coefficients128_4 = _mm_set_epi32(0, coefficients[6], coefficients[5], coefficients[4]); samples128_4 = _mm_set_epi32(pSamplesOut[-5], pSamplesOut[-6], pSamplesOut[-7], 0); break; - case 2: coefficients128_4 = _mm_set_epi32(0, 0, coefficients[5], coefficients[4]); samples128_4 = _mm_set_epi32(pSamplesOut[-5], pSamplesOut[-6], 0, 0); break; - case 1: coefficients128_4 = _mm_set_epi32(0, 0, 0, coefficients[4]); samples128_4 = _mm_set_epi32(pSamplesOut[-5], 0, 0, 0); break; - } - runningOrder = 0; - } - if (runningOrder == 4) { - coefficients128_8 = _mm_loadu_si128((const __m128i*)(coefficients + 8)); - samples128_8 = _mm_loadu_si128((const __m128i*)(pSamplesOut - 12)); - runningOrder -= 4; - } else { - switch (runningOrder) { - case 3: coefficients128_8 = _mm_set_epi32(0, coefficients[10], coefficients[9], coefficients[8]); samples128_8 = _mm_set_epi32(pSamplesOut[-9], pSamplesOut[-10], pSamplesOut[-11], 0); break; - case 2: coefficients128_8 = _mm_set_epi32(0, 0, coefficients[9], coefficients[8]); samples128_8 = _mm_set_epi32(pSamplesOut[-9], pSamplesOut[-10], 0, 0); break; - case 1: coefficients128_8 = _mm_set_epi32(0, 0, 0, coefficients[8]); samples128_8 = _mm_set_epi32(pSamplesOut[-9], 0, 0, 0); break; - } - runningOrder = 0; - } - coefficients128_0 = _mm_shuffle_epi32(coefficients128_0, _MM_SHUFFLE(0, 1, 2, 3)); - coefficients128_4 = _mm_shuffle_epi32(coefficients128_4, _MM_SHUFFLE(0, 1, 2, 3)); - coefficients128_8 = _mm_shuffle_epi32(coefficients128_8, _MM_SHUFFLE(0, 1, 2, 3)); - } -#else - switch (order) - { - case 12: ((ma_int32*)&coefficients128_8)[0] = coefficients[11]; ((ma_int32*)&samples128_8)[0] = pDecodedSamples[-12]; - case 11: ((ma_int32*)&coefficients128_8)[1] = coefficients[10]; ((ma_int32*)&samples128_8)[1] = pDecodedSamples[-11]; - case 10: ((ma_int32*)&coefficients128_8)[2] = coefficients[ 9]; ((ma_int32*)&samples128_8)[2] = pDecodedSamples[-10]; - case 9: ((ma_int32*)&coefficients128_8)[3] = coefficients[ 8]; ((ma_int32*)&samples128_8)[3] = pDecodedSamples[- 9]; - case 8: ((ma_int32*)&coefficients128_4)[0] = coefficients[ 7]; ((ma_int32*)&samples128_4)[0] = pDecodedSamples[- 8]; - case 7: ((ma_int32*)&coefficients128_4)[1] = coefficients[ 6]; ((ma_int32*)&samples128_4)[1] = pDecodedSamples[- 7]; - case 6: ((ma_int32*)&coefficients128_4)[2] = coefficients[ 5]; ((ma_int32*)&samples128_4)[2] = pDecodedSamples[- 6]; - case 5: ((ma_int32*)&coefficients128_4)[3] = coefficients[ 4]; ((ma_int32*)&samples128_4)[3] = pDecodedSamples[- 5]; - case 4: ((ma_int32*)&coefficients128_0)[0] = coefficients[ 3]; ((ma_int32*)&samples128_0)[0] = pDecodedSamples[- 4]; - case 3: ((ma_int32*)&coefficients128_0)[1] = coefficients[ 2]; ((ma_int32*)&samples128_0)[1] = pDecodedSamples[- 3]; - case 2: ((ma_int32*)&coefficients128_0)[2] = coefficients[ 1]; ((ma_int32*)&samples128_0)[2] = pDecodedSamples[- 2]; - case 1: ((ma_int32*)&coefficients128_0)[3] = coefficients[ 0]; ((ma_int32*)&samples128_0)[3] = pDecodedSamples[- 1]; - } -#endif - while (pDecodedSamples < pDecodedSamplesEnd) { - __m128i prediction128; - __m128i zeroCountPart128; - __m128i riceParamPart128; - if (!ma_dr_flac__read_rice_parts_x1(bs, riceParam, &zeroCountParts0, &riceParamParts0) || - !ma_dr_flac__read_rice_parts_x1(bs, riceParam, &zeroCountParts1, &riceParamParts1) || - !ma_dr_flac__read_rice_parts_x1(bs, riceParam, &zeroCountParts2, &riceParamParts2) || - !ma_dr_flac__read_rice_parts_x1(bs, riceParam, &zeroCountParts3, &riceParamParts3)) { - return MA_FALSE; - } - zeroCountPart128 = _mm_set_epi32(zeroCountParts3, zeroCountParts2, zeroCountParts1, zeroCountParts0); - riceParamPart128 = _mm_set_epi32(riceParamParts3, riceParamParts2, riceParamParts1, riceParamParts0); - riceParamPart128 = _mm_and_si128(riceParamPart128, riceParamMask128); - riceParamPart128 = _mm_or_si128(riceParamPart128, _mm_slli_epi32(zeroCountPart128, riceParam)); - riceParamPart128 = _mm_xor_si128(_mm_srli_epi32(riceParamPart128, 1), _mm_add_epi32(ma_dr_flac__mm_not_si128(_mm_and_si128(riceParamPart128, _mm_set1_epi32(0x01))), _mm_set1_epi32(0x01))); - if (order <= 4) { - for (i = 0; i < 4; i += 1) { - prediction128 = _mm_mullo_epi32(coefficients128_0, samples128_0); - prediction128 = ma_dr_flac__mm_hadd_epi32(prediction128); - prediction128 = _mm_srai_epi32(prediction128, shift); - prediction128 = _mm_add_epi32(riceParamPart128, prediction128); - samples128_0 = _mm_alignr_epi8(prediction128, samples128_0, 4); - riceParamPart128 = _mm_alignr_epi8(_mm_setzero_si128(), riceParamPart128, 4); - } - } else if (order <= 8) { - for (i = 0; i < 4; i += 1) { - prediction128 = _mm_mullo_epi32(coefficients128_4, samples128_4); - prediction128 = _mm_add_epi32(prediction128, _mm_mullo_epi32(coefficients128_0, samples128_0)); - prediction128 = ma_dr_flac__mm_hadd_epi32(prediction128); - prediction128 = _mm_srai_epi32(prediction128, shift); - prediction128 = _mm_add_epi32(riceParamPart128, prediction128); - samples128_4 = _mm_alignr_epi8(samples128_0, samples128_4, 4); - samples128_0 = _mm_alignr_epi8(prediction128, samples128_0, 4); - riceParamPart128 = _mm_alignr_epi8(_mm_setzero_si128(), riceParamPart128, 4); - } - } else { - for (i = 0; i < 4; i += 1) { - prediction128 = _mm_mullo_epi32(coefficients128_8, samples128_8); - prediction128 = _mm_add_epi32(prediction128, _mm_mullo_epi32(coefficients128_4, samples128_4)); - prediction128 = _mm_add_epi32(prediction128, _mm_mullo_epi32(coefficients128_0, samples128_0)); - prediction128 = ma_dr_flac__mm_hadd_epi32(prediction128); - prediction128 = _mm_srai_epi32(prediction128, shift); - prediction128 = _mm_add_epi32(riceParamPart128, prediction128); - samples128_8 = _mm_alignr_epi8(samples128_4, samples128_8, 4); - samples128_4 = _mm_alignr_epi8(samples128_0, samples128_4, 4); - samples128_0 = _mm_alignr_epi8(prediction128, samples128_0, 4); - riceParamPart128 = _mm_alignr_epi8(_mm_setzero_si128(), riceParamPart128, 4); - } - } - _mm_storeu_si128((__m128i*)pDecodedSamples, samples128_0); - pDecodedSamples += 4; - } - i = (count & ~3); - while (i < (int)count) { - if (!ma_dr_flac__read_rice_parts_x1(bs, riceParam, &zeroCountParts0, &riceParamParts0)) { - return MA_FALSE; - } - riceParamParts0 &= riceParamMask; - riceParamParts0 |= (zeroCountParts0 << riceParam); - riceParamParts0 = (riceParamParts0 >> 1) ^ t[riceParamParts0 & 0x01]; - pDecodedSamples[0] = riceParamParts0 + ma_dr_flac__calculate_prediction_32(order, shift, coefficients, pDecodedSamples); - i += 1; - pDecodedSamples += 1; - } - return MA_TRUE; -} -static ma_bool32 ma_dr_flac__decode_samples_with_residual__rice__sse41_64(ma_dr_flac_bs* bs, ma_uint32 count, ma_uint8 riceParam, ma_uint32 order, ma_int32 shift, const ma_int32* coefficients, ma_int32* pSamplesOut) -{ - int i; - ma_uint32 riceParamMask; - ma_int32* pDecodedSamples = pSamplesOut; - ma_int32* pDecodedSamplesEnd = pSamplesOut + (count & ~3); - ma_uint32 zeroCountParts0 = 0; - ma_uint32 zeroCountParts1 = 0; - ma_uint32 zeroCountParts2 = 0; - ma_uint32 zeroCountParts3 = 0; - ma_uint32 riceParamParts0 = 0; - ma_uint32 riceParamParts1 = 0; - ma_uint32 riceParamParts2 = 0; - ma_uint32 riceParamParts3 = 0; - __m128i coefficients128_0; - __m128i coefficients128_4; - __m128i coefficients128_8; - __m128i samples128_0; - __m128i samples128_4; - __m128i samples128_8; - __m128i prediction128; - __m128i riceParamMask128; - const ma_uint32 t[2] = {0x00000000, 0xFFFFFFFF}; - MA_DR_FLAC_ASSERT(order <= 12); - riceParamMask = (ma_uint32)~((~0UL) << riceParam); - riceParamMask128 = _mm_set1_epi32(riceParamMask); - prediction128 = _mm_setzero_si128(); - coefficients128_0 = _mm_setzero_si128(); - coefficients128_4 = _mm_setzero_si128(); - coefficients128_8 = _mm_setzero_si128(); - samples128_0 = _mm_setzero_si128(); - samples128_4 = _mm_setzero_si128(); - samples128_8 = _mm_setzero_si128(); -#if 1 - { - int runningOrder = order; - if (runningOrder >= 4) { - coefficients128_0 = _mm_loadu_si128((const __m128i*)(coefficients + 0)); - samples128_0 = _mm_loadu_si128((const __m128i*)(pSamplesOut - 4)); - runningOrder -= 4; - } else { - switch (runningOrder) { - case 3: coefficients128_0 = _mm_set_epi32(0, coefficients[2], coefficients[1], coefficients[0]); samples128_0 = _mm_set_epi32(pSamplesOut[-1], pSamplesOut[-2], pSamplesOut[-3], 0); break; - case 2: coefficients128_0 = _mm_set_epi32(0, 0, coefficients[1], coefficients[0]); samples128_0 = _mm_set_epi32(pSamplesOut[-1], pSamplesOut[-2], 0, 0); break; - case 1: coefficients128_0 = _mm_set_epi32(0, 0, 0, coefficients[0]); samples128_0 = _mm_set_epi32(pSamplesOut[-1], 0, 0, 0); break; - } - runningOrder = 0; - } - if (runningOrder >= 4) { - coefficients128_4 = _mm_loadu_si128((const __m128i*)(coefficients + 4)); - samples128_4 = _mm_loadu_si128((const __m128i*)(pSamplesOut - 8)); - runningOrder -= 4; - } else { - switch (runningOrder) { - case 3: coefficients128_4 = _mm_set_epi32(0, coefficients[6], coefficients[5], coefficients[4]); samples128_4 = _mm_set_epi32(pSamplesOut[-5], pSamplesOut[-6], pSamplesOut[-7], 0); break; - case 2: coefficients128_4 = _mm_set_epi32(0, 0, coefficients[5], coefficients[4]); samples128_4 = _mm_set_epi32(pSamplesOut[-5], pSamplesOut[-6], 0, 0); break; - case 1: coefficients128_4 = _mm_set_epi32(0, 0, 0, coefficients[4]); samples128_4 = _mm_set_epi32(pSamplesOut[-5], 0, 0, 0); break; - } - runningOrder = 0; - } - if (runningOrder == 4) { - coefficients128_8 = _mm_loadu_si128((const __m128i*)(coefficients + 8)); - samples128_8 = _mm_loadu_si128((const __m128i*)(pSamplesOut - 12)); - runningOrder -= 4; - } else { - switch (runningOrder) { - case 3: coefficients128_8 = _mm_set_epi32(0, coefficients[10], coefficients[9], coefficients[8]); samples128_8 = _mm_set_epi32(pSamplesOut[-9], pSamplesOut[-10], pSamplesOut[-11], 0); break; - case 2: coefficients128_8 = _mm_set_epi32(0, 0, coefficients[9], coefficients[8]); samples128_8 = _mm_set_epi32(pSamplesOut[-9], pSamplesOut[-10], 0, 0); break; - case 1: coefficients128_8 = _mm_set_epi32(0, 0, 0, coefficients[8]); samples128_8 = _mm_set_epi32(pSamplesOut[-9], 0, 0, 0); break; - } - runningOrder = 0; - } - coefficients128_0 = _mm_shuffle_epi32(coefficients128_0, _MM_SHUFFLE(0, 1, 2, 3)); - coefficients128_4 = _mm_shuffle_epi32(coefficients128_4, _MM_SHUFFLE(0, 1, 2, 3)); - coefficients128_8 = _mm_shuffle_epi32(coefficients128_8, _MM_SHUFFLE(0, 1, 2, 3)); - } -#else - switch (order) - { - case 12: ((ma_int32*)&coefficients128_8)[0] = coefficients[11]; ((ma_int32*)&samples128_8)[0] = pDecodedSamples[-12]; - case 11: ((ma_int32*)&coefficients128_8)[1] = coefficients[10]; ((ma_int32*)&samples128_8)[1] = pDecodedSamples[-11]; - case 10: ((ma_int32*)&coefficients128_8)[2] = coefficients[ 9]; ((ma_int32*)&samples128_8)[2] = pDecodedSamples[-10]; - case 9: ((ma_int32*)&coefficients128_8)[3] = coefficients[ 8]; ((ma_int32*)&samples128_8)[3] = pDecodedSamples[- 9]; - case 8: ((ma_int32*)&coefficients128_4)[0] = coefficients[ 7]; ((ma_int32*)&samples128_4)[0] = pDecodedSamples[- 8]; - case 7: ((ma_int32*)&coefficients128_4)[1] = coefficients[ 6]; ((ma_int32*)&samples128_4)[1] = pDecodedSamples[- 7]; - case 6: ((ma_int32*)&coefficients128_4)[2] = coefficients[ 5]; ((ma_int32*)&samples128_4)[2] = pDecodedSamples[- 6]; - case 5: ((ma_int32*)&coefficients128_4)[3] = coefficients[ 4]; ((ma_int32*)&samples128_4)[3] = pDecodedSamples[- 5]; - case 4: ((ma_int32*)&coefficients128_0)[0] = coefficients[ 3]; ((ma_int32*)&samples128_0)[0] = pDecodedSamples[- 4]; - case 3: ((ma_int32*)&coefficients128_0)[1] = coefficients[ 2]; ((ma_int32*)&samples128_0)[1] = pDecodedSamples[- 3]; - case 2: ((ma_int32*)&coefficients128_0)[2] = coefficients[ 1]; ((ma_int32*)&samples128_0)[2] = pDecodedSamples[- 2]; - case 1: ((ma_int32*)&coefficients128_0)[3] = coefficients[ 0]; ((ma_int32*)&samples128_0)[3] = pDecodedSamples[- 1]; - } -#endif - while (pDecodedSamples < pDecodedSamplesEnd) { - __m128i zeroCountPart128; - __m128i riceParamPart128; - if (!ma_dr_flac__read_rice_parts_x1(bs, riceParam, &zeroCountParts0, &riceParamParts0) || - !ma_dr_flac__read_rice_parts_x1(bs, riceParam, &zeroCountParts1, &riceParamParts1) || - !ma_dr_flac__read_rice_parts_x1(bs, riceParam, &zeroCountParts2, &riceParamParts2) || - !ma_dr_flac__read_rice_parts_x1(bs, riceParam, &zeroCountParts3, &riceParamParts3)) { - return MA_FALSE; - } - zeroCountPart128 = _mm_set_epi32(zeroCountParts3, zeroCountParts2, zeroCountParts1, zeroCountParts0); - riceParamPart128 = _mm_set_epi32(riceParamParts3, riceParamParts2, riceParamParts1, riceParamParts0); - riceParamPart128 = _mm_and_si128(riceParamPart128, riceParamMask128); - riceParamPart128 = _mm_or_si128(riceParamPart128, _mm_slli_epi32(zeroCountPart128, riceParam)); - riceParamPart128 = _mm_xor_si128(_mm_srli_epi32(riceParamPart128, 1), _mm_add_epi32(ma_dr_flac__mm_not_si128(_mm_and_si128(riceParamPart128, _mm_set1_epi32(1))), _mm_set1_epi32(1))); - for (i = 0; i < 4; i += 1) { - prediction128 = _mm_xor_si128(prediction128, prediction128); - switch (order) - { - case 12: - case 11: prediction128 = _mm_add_epi64(prediction128, _mm_mul_epi32(_mm_shuffle_epi32(coefficients128_8, _MM_SHUFFLE(1, 1, 0, 0)), _mm_shuffle_epi32(samples128_8, _MM_SHUFFLE(1, 1, 0, 0)))); - case 10: - case 9: prediction128 = _mm_add_epi64(prediction128, _mm_mul_epi32(_mm_shuffle_epi32(coefficients128_8, _MM_SHUFFLE(3, 3, 2, 2)), _mm_shuffle_epi32(samples128_8, _MM_SHUFFLE(3, 3, 2, 2)))); - case 8: - case 7: prediction128 = _mm_add_epi64(prediction128, _mm_mul_epi32(_mm_shuffle_epi32(coefficients128_4, _MM_SHUFFLE(1, 1, 0, 0)), _mm_shuffle_epi32(samples128_4, _MM_SHUFFLE(1, 1, 0, 0)))); - case 6: - case 5: prediction128 = _mm_add_epi64(prediction128, _mm_mul_epi32(_mm_shuffle_epi32(coefficients128_4, _MM_SHUFFLE(3, 3, 2, 2)), _mm_shuffle_epi32(samples128_4, _MM_SHUFFLE(3, 3, 2, 2)))); - case 4: - case 3: prediction128 = _mm_add_epi64(prediction128, _mm_mul_epi32(_mm_shuffle_epi32(coefficients128_0, _MM_SHUFFLE(1, 1, 0, 0)), _mm_shuffle_epi32(samples128_0, _MM_SHUFFLE(1, 1, 0, 0)))); - case 2: - case 1: prediction128 = _mm_add_epi64(prediction128, _mm_mul_epi32(_mm_shuffle_epi32(coefficients128_0, _MM_SHUFFLE(3, 3, 2, 2)), _mm_shuffle_epi32(samples128_0, _MM_SHUFFLE(3, 3, 2, 2)))); - } - prediction128 = ma_dr_flac__mm_hadd_epi64(prediction128); - prediction128 = ma_dr_flac__mm_srai_epi64(prediction128, shift); - prediction128 = _mm_add_epi32(riceParamPart128, prediction128); - samples128_8 = _mm_alignr_epi8(samples128_4, samples128_8, 4); - samples128_4 = _mm_alignr_epi8(samples128_0, samples128_4, 4); - samples128_0 = _mm_alignr_epi8(prediction128, samples128_0, 4); - riceParamPart128 = _mm_alignr_epi8(_mm_setzero_si128(), riceParamPart128, 4); - } - _mm_storeu_si128((__m128i*)pDecodedSamples, samples128_0); - pDecodedSamples += 4; - } - i = (count & ~3); - while (i < (int)count) { - if (!ma_dr_flac__read_rice_parts_x1(bs, riceParam, &zeroCountParts0, &riceParamParts0)) { - return MA_FALSE; - } - riceParamParts0 &= riceParamMask; - riceParamParts0 |= (zeroCountParts0 << riceParam); - riceParamParts0 = (riceParamParts0 >> 1) ^ t[riceParamParts0 & 0x01]; - pDecodedSamples[0] = riceParamParts0 + ma_dr_flac__calculate_prediction_64(order, shift, coefficients, pDecodedSamples); - i += 1; - pDecodedSamples += 1; - } - return MA_TRUE; -} -static ma_bool32 ma_dr_flac__decode_samples_with_residual__rice__sse41(ma_dr_flac_bs* bs, ma_uint32 bitsPerSample, ma_uint32 count, ma_uint8 riceParam, ma_uint32 lpcOrder, ma_int32 lpcShift, ma_uint32 lpcPrecision, const ma_int32* coefficients, ma_int32* pSamplesOut) -{ - MA_DR_FLAC_ASSERT(bs != NULL); - MA_DR_FLAC_ASSERT(pSamplesOut != NULL); - if (lpcOrder > 0 && lpcOrder <= 12) { - if (ma_dr_flac__use_64_bit_prediction(bitsPerSample, lpcOrder, lpcPrecision)) { - return ma_dr_flac__decode_samples_with_residual__rice__sse41_64(bs, count, riceParam, lpcOrder, lpcShift, coefficients, pSamplesOut); - } else { - return ma_dr_flac__decode_samples_with_residual__rice__sse41_32(bs, count, riceParam, lpcOrder, lpcShift, coefficients, pSamplesOut); - } - } else { - return ma_dr_flac__decode_samples_with_residual__rice__scalar(bs, bitsPerSample, count, riceParam, lpcOrder, lpcShift, lpcPrecision, coefficients, pSamplesOut); - } -} -#endif -#if defined(MA_DR_FLAC_SUPPORT_NEON) -static MA_INLINE void ma_dr_flac__vst2q_s32(ma_int32* p, int32x4x2_t x) -{ - vst1q_s32(p+0, x.val[0]); - vst1q_s32(p+4, x.val[1]); -} -static MA_INLINE void ma_dr_flac__vst2q_u32(ma_uint32* p, uint32x4x2_t x) -{ - vst1q_u32(p+0, x.val[0]); - vst1q_u32(p+4, x.val[1]); -} -static MA_INLINE void ma_dr_flac__vst2q_f32(float* p, float32x4x2_t x) -{ - vst1q_f32(p+0, x.val[0]); - vst1q_f32(p+4, x.val[1]); -} -static MA_INLINE void ma_dr_flac__vst2q_s16(ma_int16* p, int16x4x2_t x) -{ - vst1q_s16(p, vcombine_s16(x.val[0], x.val[1])); -} -static MA_INLINE void ma_dr_flac__vst2q_u16(ma_uint16* p, uint16x4x2_t x) -{ - vst1q_u16(p, vcombine_u16(x.val[0], x.val[1])); -} -static MA_INLINE int32x4_t ma_dr_flac__vdupq_n_s32x4(ma_int32 x3, ma_int32 x2, ma_int32 x1, ma_int32 x0) -{ - ma_int32 x[4]; - x[3] = x3; - x[2] = x2; - x[1] = x1; - x[0] = x0; - return vld1q_s32(x); -} -static MA_INLINE int32x4_t ma_dr_flac__valignrq_s32_1(int32x4_t a, int32x4_t b) -{ - return vextq_s32(b, a, 1); -} -static MA_INLINE uint32x4_t ma_dr_flac__valignrq_u32_1(uint32x4_t a, uint32x4_t b) -{ - return vextq_u32(b, a, 1); -} -static MA_INLINE int32x2_t ma_dr_flac__vhaddq_s32(int32x4_t x) -{ - int32x2_t r = vadd_s32(vget_high_s32(x), vget_low_s32(x)); - return vpadd_s32(r, r); -} -static MA_INLINE int64x1_t ma_dr_flac__vhaddq_s64(int64x2_t x) -{ - return vadd_s64(vget_high_s64(x), vget_low_s64(x)); -} -static MA_INLINE int32x4_t ma_dr_flac__vrevq_s32(int32x4_t x) -{ - return vrev64q_s32(vcombine_s32(vget_high_s32(x), vget_low_s32(x))); -} -static MA_INLINE int32x4_t ma_dr_flac__vnotq_s32(int32x4_t x) -{ - return veorq_s32(x, vdupq_n_s32(0xFFFFFFFF)); -} -static MA_INLINE uint32x4_t ma_dr_flac__vnotq_u32(uint32x4_t x) -{ - return veorq_u32(x, vdupq_n_u32(0xFFFFFFFF)); -} -static ma_bool32 ma_dr_flac__decode_samples_with_residual__rice__neon_32(ma_dr_flac_bs* bs, ma_uint32 count, ma_uint8 riceParam, ma_uint32 order, ma_int32 shift, const ma_int32* coefficients, ma_int32* pSamplesOut) -{ - int i; - ma_uint32 riceParamMask; - ma_int32* pDecodedSamples = pSamplesOut; - ma_int32* pDecodedSamplesEnd = pSamplesOut + (count & ~3); - ma_uint32 zeroCountParts[4]; - ma_uint32 riceParamParts[4]; - int32x4_t coefficients128_0; - int32x4_t coefficients128_4; - int32x4_t coefficients128_8; - int32x4_t samples128_0; - int32x4_t samples128_4; - int32x4_t samples128_8; - uint32x4_t riceParamMask128; - int32x4_t riceParam128; - int32x2_t shift64; - uint32x4_t one128; - const ma_uint32 t[2] = {0x00000000, 0xFFFFFFFF}; - riceParamMask = (ma_uint32)~((~0UL) << riceParam); - riceParamMask128 = vdupq_n_u32(riceParamMask); - riceParam128 = vdupq_n_s32(riceParam); - shift64 = vdup_n_s32(-shift); - one128 = vdupq_n_u32(1); - { - int runningOrder = order; - ma_int32 tempC[4] = {0, 0, 0, 0}; - ma_int32 tempS[4] = {0, 0, 0, 0}; - if (runningOrder >= 4) { - coefficients128_0 = vld1q_s32(coefficients + 0); - samples128_0 = vld1q_s32(pSamplesOut - 4); - runningOrder -= 4; - } else { - switch (runningOrder) { - case 3: tempC[2] = coefficients[2]; tempS[1] = pSamplesOut[-3]; - case 2: tempC[1] = coefficients[1]; tempS[2] = pSamplesOut[-2]; - case 1: tempC[0] = coefficients[0]; tempS[3] = pSamplesOut[-1]; - } - coefficients128_0 = vld1q_s32(tempC); - samples128_0 = vld1q_s32(tempS); - runningOrder = 0; - } - if (runningOrder >= 4) { - coefficients128_4 = vld1q_s32(coefficients + 4); - samples128_4 = vld1q_s32(pSamplesOut - 8); - runningOrder -= 4; - } else { - switch (runningOrder) { - case 3: tempC[2] = coefficients[6]; tempS[1] = pSamplesOut[-7]; - case 2: tempC[1] = coefficients[5]; tempS[2] = pSamplesOut[-6]; - case 1: tempC[0] = coefficients[4]; tempS[3] = pSamplesOut[-5]; - } - coefficients128_4 = vld1q_s32(tempC); - samples128_4 = vld1q_s32(tempS); - runningOrder = 0; - } - if (runningOrder == 4) { - coefficients128_8 = vld1q_s32(coefficients + 8); - samples128_8 = vld1q_s32(pSamplesOut - 12); - runningOrder -= 4; - } else { - switch (runningOrder) { - case 3: tempC[2] = coefficients[10]; tempS[1] = pSamplesOut[-11]; - case 2: tempC[1] = coefficients[ 9]; tempS[2] = pSamplesOut[-10]; - case 1: tempC[0] = coefficients[ 8]; tempS[3] = pSamplesOut[- 9]; - } - coefficients128_8 = vld1q_s32(tempC); - samples128_8 = vld1q_s32(tempS); - runningOrder = 0; - } - coefficients128_0 = ma_dr_flac__vrevq_s32(coefficients128_0); - coefficients128_4 = ma_dr_flac__vrevq_s32(coefficients128_4); - coefficients128_8 = ma_dr_flac__vrevq_s32(coefficients128_8); - } - while (pDecodedSamples < pDecodedSamplesEnd) { - int32x4_t prediction128; - int32x2_t prediction64; - uint32x4_t zeroCountPart128; - uint32x4_t riceParamPart128; - if (!ma_dr_flac__read_rice_parts_x1(bs, riceParam, &zeroCountParts[0], &riceParamParts[0]) || - !ma_dr_flac__read_rice_parts_x1(bs, riceParam, &zeroCountParts[1], &riceParamParts[1]) || - !ma_dr_flac__read_rice_parts_x1(bs, riceParam, &zeroCountParts[2], &riceParamParts[2]) || - !ma_dr_flac__read_rice_parts_x1(bs, riceParam, &zeroCountParts[3], &riceParamParts[3])) { - return MA_FALSE; - } - zeroCountPart128 = vld1q_u32(zeroCountParts); - riceParamPart128 = vld1q_u32(riceParamParts); - riceParamPart128 = vandq_u32(riceParamPart128, riceParamMask128); - riceParamPart128 = vorrq_u32(riceParamPart128, vshlq_u32(zeroCountPart128, riceParam128)); - riceParamPart128 = veorq_u32(vshrq_n_u32(riceParamPart128, 1), vaddq_u32(ma_dr_flac__vnotq_u32(vandq_u32(riceParamPart128, one128)), one128)); - if (order <= 4) { - for (i = 0; i < 4; i += 1) { - prediction128 = vmulq_s32(coefficients128_0, samples128_0); - prediction64 = ma_dr_flac__vhaddq_s32(prediction128); - prediction64 = vshl_s32(prediction64, shift64); - prediction64 = vadd_s32(prediction64, vget_low_s32(vreinterpretq_s32_u32(riceParamPart128))); - samples128_0 = ma_dr_flac__valignrq_s32_1(vcombine_s32(prediction64, vdup_n_s32(0)), samples128_0); - riceParamPart128 = ma_dr_flac__valignrq_u32_1(vdupq_n_u32(0), riceParamPart128); - } - } else if (order <= 8) { - for (i = 0; i < 4; i += 1) { - prediction128 = vmulq_s32(coefficients128_4, samples128_4); - prediction128 = vmlaq_s32(prediction128, coefficients128_0, samples128_0); - prediction64 = ma_dr_flac__vhaddq_s32(prediction128); - prediction64 = vshl_s32(prediction64, shift64); - prediction64 = vadd_s32(prediction64, vget_low_s32(vreinterpretq_s32_u32(riceParamPart128))); - samples128_4 = ma_dr_flac__valignrq_s32_1(samples128_0, samples128_4); - samples128_0 = ma_dr_flac__valignrq_s32_1(vcombine_s32(prediction64, vdup_n_s32(0)), samples128_0); - riceParamPart128 = ma_dr_flac__valignrq_u32_1(vdupq_n_u32(0), riceParamPart128); - } - } else { - for (i = 0; i < 4; i += 1) { - prediction128 = vmulq_s32(coefficients128_8, samples128_8); - prediction128 = vmlaq_s32(prediction128, coefficients128_4, samples128_4); - prediction128 = vmlaq_s32(prediction128, coefficients128_0, samples128_0); - prediction64 = ma_dr_flac__vhaddq_s32(prediction128); - prediction64 = vshl_s32(prediction64, shift64); - prediction64 = vadd_s32(prediction64, vget_low_s32(vreinterpretq_s32_u32(riceParamPart128))); - samples128_8 = ma_dr_flac__valignrq_s32_1(samples128_4, samples128_8); - samples128_4 = ma_dr_flac__valignrq_s32_1(samples128_0, samples128_4); - samples128_0 = ma_dr_flac__valignrq_s32_1(vcombine_s32(prediction64, vdup_n_s32(0)), samples128_0); - riceParamPart128 = ma_dr_flac__valignrq_u32_1(vdupq_n_u32(0), riceParamPart128); - } - } - vst1q_s32(pDecodedSamples, samples128_0); - pDecodedSamples += 4; - } - i = (count & ~3); - while (i < (int)count) { - if (!ma_dr_flac__read_rice_parts_x1(bs, riceParam, &zeroCountParts[0], &riceParamParts[0])) { - return MA_FALSE; - } - riceParamParts[0] &= riceParamMask; - riceParamParts[0] |= (zeroCountParts[0] << riceParam); - riceParamParts[0] = (riceParamParts[0] >> 1) ^ t[riceParamParts[0] & 0x01]; - pDecodedSamples[0] = riceParamParts[0] + ma_dr_flac__calculate_prediction_32(order, shift, coefficients, pDecodedSamples); - i += 1; - pDecodedSamples += 1; - } - return MA_TRUE; -} -static ma_bool32 ma_dr_flac__decode_samples_with_residual__rice__neon_64(ma_dr_flac_bs* bs, ma_uint32 count, ma_uint8 riceParam, ma_uint32 order, ma_int32 shift, const ma_int32* coefficients, ma_int32* pSamplesOut) -{ - int i; - ma_uint32 riceParamMask; - ma_int32* pDecodedSamples = pSamplesOut; - ma_int32* pDecodedSamplesEnd = pSamplesOut + (count & ~3); - ma_uint32 zeroCountParts[4]; - ma_uint32 riceParamParts[4]; - int32x4_t coefficients128_0; - int32x4_t coefficients128_4; - int32x4_t coefficients128_8; - int32x4_t samples128_0; - int32x4_t samples128_4; - int32x4_t samples128_8; - uint32x4_t riceParamMask128; - int32x4_t riceParam128; - int64x1_t shift64; - uint32x4_t one128; - int64x2_t prediction128 = { 0 }; - uint32x4_t zeroCountPart128; - uint32x4_t riceParamPart128; - const ma_uint32 t[2] = {0x00000000, 0xFFFFFFFF}; - riceParamMask = (ma_uint32)~((~0UL) << riceParam); - riceParamMask128 = vdupq_n_u32(riceParamMask); - riceParam128 = vdupq_n_s32(riceParam); - shift64 = vdup_n_s64(-shift); - one128 = vdupq_n_u32(1); - { - int runningOrder = order; - ma_int32 tempC[4] = {0, 0, 0, 0}; - ma_int32 tempS[4] = {0, 0, 0, 0}; - if (runningOrder >= 4) { - coefficients128_0 = vld1q_s32(coefficients + 0); - samples128_0 = vld1q_s32(pSamplesOut - 4); - runningOrder -= 4; - } else { - switch (runningOrder) { - case 3: tempC[2] = coefficients[2]; tempS[1] = pSamplesOut[-3]; - case 2: tempC[1] = coefficients[1]; tempS[2] = pSamplesOut[-2]; - case 1: tempC[0] = coefficients[0]; tempS[3] = pSamplesOut[-1]; - } - coefficients128_0 = vld1q_s32(tempC); - samples128_0 = vld1q_s32(tempS); - runningOrder = 0; - } - if (runningOrder >= 4) { - coefficients128_4 = vld1q_s32(coefficients + 4); - samples128_4 = vld1q_s32(pSamplesOut - 8); - runningOrder -= 4; - } else { - switch (runningOrder) { - case 3: tempC[2] = coefficients[6]; tempS[1] = pSamplesOut[-7]; - case 2: tempC[1] = coefficients[5]; tempS[2] = pSamplesOut[-6]; - case 1: tempC[0] = coefficients[4]; tempS[3] = pSamplesOut[-5]; - } - coefficients128_4 = vld1q_s32(tempC); - samples128_4 = vld1q_s32(tempS); - runningOrder = 0; - } - if (runningOrder == 4) { - coefficients128_8 = vld1q_s32(coefficients + 8); - samples128_8 = vld1q_s32(pSamplesOut - 12); - runningOrder -= 4; - } else { - switch (runningOrder) { - case 3: tempC[2] = coefficients[10]; tempS[1] = pSamplesOut[-11]; - case 2: tempC[1] = coefficients[ 9]; tempS[2] = pSamplesOut[-10]; - case 1: tempC[0] = coefficients[ 8]; tempS[3] = pSamplesOut[- 9]; - } - coefficients128_8 = vld1q_s32(tempC); - samples128_8 = vld1q_s32(tempS); - runningOrder = 0; - } - coefficients128_0 = ma_dr_flac__vrevq_s32(coefficients128_0); - coefficients128_4 = ma_dr_flac__vrevq_s32(coefficients128_4); - coefficients128_8 = ma_dr_flac__vrevq_s32(coefficients128_8); - } - while (pDecodedSamples < pDecodedSamplesEnd) { - if (!ma_dr_flac__read_rice_parts_x1(bs, riceParam, &zeroCountParts[0], &riceParamParts[0]) || - !ma_dr_flac__read_rice_parts_x1(bs, riceParam, &zeroCountParts[1], &riceParamParts[1]) || - !ma_dr_flac__read_rice_parts_x1(bs, riceParam, &zeroCountParts[2], &riceParamParts[2]) || - !ma_dr_flac__read_rice_parts_x1(bs, riceParam, &zeroCountParts[3], &riceParamParts[3])) { - return MA_FALSE; - } - zeroCountPart128 = vld1q_u32(zeroCountParts); - riceParamPart128 = vld1q_u32(riceParamParts); - riceParamPart128 = vandq_u32(riceParamPart128, riceParamMask128); - riceParamPart128 = vorrq_u32(riceParamPart128, vshlq_u32(zeroCountPart128, riceParam128)); - riceParamPart128 = veorq_u32(vshrq_n_u32(riceParamPart128, 1), vaddq_u32(ma_dr_flac__vnotq_u32(vandq_u32(riceParamPart128, one128)), one128)); - for (i = 0; i < 4; i += 1) { - int64x1_t prediction64; - prediction128 = veorq_s64(prediction128, prediction128); - switch (order) - { - case 12: - case 11: prediction128 = vaddq_s64(prediction128, vmull_s32(vget_low_s32(coefficients128_8), vget_low_s32(samples128_8))); - case 10: - case 9: prediction128 = vaddq_s64(prediction128, vmull_s32(vget_high_s32(coefficients128_8), vget_high_s32(samples128_8))); - case 8: - case 7: prediction128 = vaddq_s64(prediction128, vmull_s32(vget_low_s32(coefficients128_4), vget_low_s32(samples128_4))); - case 6: - case 5: prediction128 = vaddq_s64(prediction128, vmull_s32(vget_high_s32(coefficients128_4), vget_high_s32(samples128_4))); - case 4: - case 3: prediction128 = vaddq_s64(prediction128, vmull_s32(vget_low_s32(coefficients128_0), vget_low_s32(samples128_0))); - case 2: - case 1: prediction128 = vaddq_s64(prediction128, vmull_s32(vget_high_s32(coefficients128_0), vget_high_s32(samples128_0))); - } - prediction64 = ma_dr_flac__vhaddq_s64(prediction128); - prediction64 = vshl_s64(prediction64, shift64); - prediction64 = vadd_s64(prediction64, vdup_n_s64(vgetq_lane_u32(riceParamPart128, 0))); - samples128_8 = ma_dr_flac__valignrq_s32_1(samples128_4, samples128_8); - samples128_4 = ma_dr_flac__valignrq_s32_1(samples128_0, samples128_4); - samples128_0 = ma_dr_flac__valignrq_s32_1(vcombine_s32(vreinterpret_s32_s64(prediction64), vdup_n_s32(0)), samples128_0); - riceParamPart128 = ma_dr_flac__valignrq_u32_1(vdupq_n_u32(0), riceParamPart128); - } - vst1q_s32(pDecodedSamples, samples128_0); - pDecodedSamples += 4; - } - i = (count & ~3); - while (i < (int)count) { - if (!ma_dr_flac__read_rice_parts_x1(bs, riceParam, &zeroCountParts[0], &riceParamParts[0])) { - return MA_FALSE; - } - riceParamParts[0] &= riceParamMask; - riceParamParts[0] |= (zeroCountParts[0] << riceParam); - riceParamParts[0] = (riceParamParts[0] >> 1) ^ t[riceParamParts[0] & 0x01]; - pDecodedSamples[0] = riceParamParts[0] + ma_dr_flac__calculate_prediction_64(order, shift, coefficients, pDecodedSamples); - i += 1; - pDecodedSamples += 1; - } - return MA_TRUE; -} -static ma_bool32 ma_dr_flac__decode_samples_with_residual__rice__neon(ma_dr_flac_bs* bs, ma_uint32 bitsPerSample, ma_uint32 count, ma_uint8 riceParam, ma_uint32 lpcOrder, ma_int32 lpcShift, ma_uint32 lpcPrecision, const ma_int32* coefficients, ma_int32* pSamplesOut) -{ - MA_DR_FLAC_ASSERT(bs != NULL); - MA_DR_FLAC_ASSERT(pSamplesOut != NULL); - if (lpcOrder > 0 && lpcOrder <= 12) { - if (ma_dr_flac__use_64_bit_prediction(bitsPerSample, lpcOrder, lpcPrecision)) { - return ma_dr_flac__decode_samples_with_residual__rice__neon_64(bs, count, riceParam, lpcOrder, lpcShift, coefficients, pSamplesOut); - } else { - return ma_dr_flac__decode_samples_with_residual__rice__neon_32(bs, count, riceParam, lpcOrder, lpcShift, coefficients, pSamplesOut); - } - } else { - return ma_dr_flac__decode_samples_with_residual__rice__scalar(bs, bitsPerSample, count, riceParam, lpcOrder, lpcShift, lpcPrecision, coefficients, pSamplesOut); - } -} -#endif -static ma_bool32 ma_dr_flac__decode_samples_with_residual__rice(ma_dr_flac_bs* bs, ma_uint32 bitsPerSample, ma_uint32 count, ma_uint8 riceParam, ma_uint32 lpcOrder, ma_int32 lpcShift, ma_uint32 lpcPrecision, const ma_int32* coefficients, ma_int32* pSamplesOut) -{ -#if defined(MA_DR_FLAC_SUPPORT_SSE41) - if (ma_dr_flac__gIsSSE41Supported) { - return ma_dr_flac__decode_samples_with_residual__rice__sse41(bs, bitsPerSample, count, riceParam, lpcOrder, lpcShift, lpcPrecision, coefficients, pSamplesOut); - } else -#elif defined(MA_DR_FLAC_SUPPORT_NEON) - if (ma_dr_flac__gIsNEONSupported) { - return ma_dr_flac__decode_samples_with_residual__rice__neon(bs, bitsPerSample, count, riceParam, lpcOrder, lpcShift, lpcPrecision, coefficients, pSamplesOut); - } else -#endif - { - #if 0 - return ma_dr_flac__decode_samples_with_residual__rice__reference(bs, bitsPerSample, count, riceParam, lpcOrder, lpcShift, lpcPrecision, coefficients, pSamplesOut); - #else - return ma_dr_flac__decode_samples_with_residual__rice__scalar(bs, bitsPerSample, count, riceParam, lpcOrder, lpcShift, lpcPrecision, coefficients, pSamplesOut); - #endif - } -} -static ma_bool32 ma_dr_flac__read_and_seek_residual__rice(ma_dr_flac_bs* bs, ma_uint32 count, ma_uint8 riceParam) -{ - ma_uint32 i; - MA_DR_FLAC_ASSERT(bs != NULL); - for (i = 0; i < count; ++i) { - if (!ma_dr_flac__seek_rice_parts(bs, riceParam)) { - return MA_FALSE; - } - } - return MA_TRUE; -} -#if defined(__clang__) -__attribute__((no_sanitize("signed-integer-overflow"))) -#endif -static ma_bool32 ma_dr_flac__decode_samples_with_residual__unencoded(ma_dr_flac_bs* bs, ma_uint32 bitsPerSample, ma_uint32 count, ma_uint8 unencodedBitsPerSample, ma_uint32 lpcOrder, ma_int32 lpcShift, ma_uint32 lpcPrecision, const ma_int32* coefficients, ma_int32* pSamplesOut) -{ - ma_uint32 i; - MA_DR_FLAC_ASSERT(bs != NULL); - MA_DR_FLAC_ASSERT(unencodedBitsPerSample <= 31); - MA_DR_FLAC_ASSERT(pSamplesOut != NULL); - for (i = 0; i < count; ++i) { - if (unencodedBitsPerSample > 0) { - if (!ma_dr_flac__read_int32(bs, unencodedBitsPerSample, pSamplesOut + i)) { - return MA_FALSE; - } - } else { - pSamplesOut[i] = 0; - } - if (ma_dr_flac__use_64_bit_prediction(bitsPerSample, lpcOrder, lpcPrecision)) { - pSamplesOut[i] += ma_dr_flac__calculate_prediction_64(lpcOrder, lpcShift, coefficients, pSamplesOut + i); - } else { - pSamplesOut[i] += ma_dr_flac__calculate_prediction_32(lpcOrder, lpcShift, coefficients, pSamplesOut + i); - } - } - return MA_TRUE; -} -static ma_bool32 ma_dr_flac__decode_samples_with_residual(ma_dr_flac_bs* bs, ma_uint32 bitsPerSample, ma_uint32 blockSize, ma_uint32 lpcOrder, ma_int32 lpcShift, ma_uint32 lpcPrecision, const ma_int32* coefficients, ma_int32* pDecodedSamples) -{ - ma_uint8 residualMethod; - ma_uint8 partitionOrder; - ma_uint32 samplesInPartition; - ma_uint32 partitionsRemaining; - MA_DR_FLAC_ASSERT(bs != NULL); - MA_DR_FLAC_ASSERT(blockSize != 0); - MA_DR_FLAC_ASSERT(pDecodedSamples != NULL); - if (!ma_dr_flac__read_uint8(bs, 2, &residualMethod)) { - return MA_FALSE; - } - if (residualMethod != MA_DR_FLAC_RESIDUAL_CODING_METHOD_PARTITIONED_RICE && residualMethod != MA_DR_FLAC_RESIDUAL_CODING_METHOD_PARTITIONED_RICE2) { - return MA_FALSE; - } - pDecodedSamples += lpcOrder; - if (!ma_dr_flac__read_uint8(bs, 4, &partitionOrder)) { - return MA_FALSE; - } - if (partitionOrder > 8) { - return MA_FALSE; - } - if ((blockSize / (1 << partitionOrder)) < lpcOrder) { - return MA_FALSE; - } - samplesInPartition = (blockSize / (1 << partitionOrder)) - lpcOrder; - partitionsRemaining = (1 << partitionOrder); - for (;;) { - ma_uint8 riceParam = 0; - if (residualMethod == MA_DR_FLAC_RESIDUAL_CODING_METHOD_PARTITIONED_RICE) { - if (!ma_dr_flac__read_uint8(bs, 4, &riceParam)) { - return MA_FALSE; - } - if (riceParam == 15) { - riceParam = 0xFF; - } - } else if (residualMethod == MA_DR_FLAC_RESIDUAL_CODING_METHOD_PARTITIONED_RICE2) { - if (!ma_dr_flac__read_uint8(bs, 5, &riceParam)) { - return MA_FALSE; - } - if (riceParam == 31) { - riceParam = 0xFF; - } - } - if (riceParam != 0xFF) { - if (!ma_dr_flac__decode_samples_with_residual__rice(bs, bitsPerSample, samplesInPartition, riceParam, lpcOrder, lpcShift, lpcPrecision, coefficients, pDecodedSamples)) { - return MA_FALSE; - } - } else { - ma_uint8 unencodedBitsPerSample = 0; - if (!ma_dr_flac__read_uint8(bs, 5, &unencodedBitsPerSample)) { - return MA_FALSE; - } - if (!ma_dr_flac__decode_samples_with_residual__unencoded(bs, bitsPerSample, samplesInPartition, unencodedBitsPerSample, lpcOrder, lpcShift, lpcPrecision, coefficients, pDecodedSamples)) { - return MA_FALSE; - } - } - pDecodedSamples += samplesInPartition; - if (partitionsRemaining == 1) { - break; - } - partitionsRemaining -= 1; - if (partitionOrder != 0) { - samplesInPartition = blockSize / (1 << partitionOrder); - } - } - return MA_TRUE; -} -static ma_bool32 ma_dr_flac__read_and_seek_residual(ma_dr_flac_bs* bs, ma_uint32 blockSize, ma_uint32 order) -{ - ma_uint8 residualMethod; - ma_uint8 partitionOrder; - ma_uint32 samplesInPartition; - ma_uint32 partitionsRemaining; - MA_DR_FLAC_ASSERT(bs != NULL); - MA_DR_FLAC_ASSERT(blockSize != 0); - if (!ma_dr_flac__read_uint8(bs, 2, &residualMethod)) { - return MA_FALSE; - } - if (residualMethod != MA_DR_FLAC_RESIDUAL_CODING_METHOD_PARTITIONED_RICE && residualMethod != MA_DR_FLAC_RESIDUAL_CODING_METHOD_PARTITIONED_RICE2) { - return MA_FALSE; - } - if (!ma_dr_flac__read_uint8(bs, 4, &partitionOrder)) { - return MA_FALSE; - } - if (partitionOrder > 8) { - return MA_FALSE; - } - if ((blockSize / (1 << partitionOrder)) <= order) { - return MA_FALSE; - } - samplesInPartition = (blockSize / (1 << partitionOrder)) - order; - partitionsRemaining = (1 << partitionOrder); - for (;;) - { - ma_uint8 riceParam = 0; - if (residualMethod == MA_DR_FLAC_RESIDUAL_CODING_METHOD_PARTITIONED_RICE) { - if (!ma_dr_flac__read_uint8(bs, 4, &riceParam)) { - return MA_FALSE; - } - if (riceParam == 15) { - riceParam = 0xFF; - } - } else if (residualMethod == MA_DR_FLAC_RESIDUAL_CODING_METHOD_PARTITIONED_RICE2) { - if (!ma_dr_flac__read_uint8(bs, 5, &riceParam)) { - return MA_FALSE; - } - if (riceParam == 31) { - riceParam = 0xFF; - } - } - if (riceParam != 0xFF) { - if (!ma_dr_flac__read_and_seek_residual__rice(bs, samplesInPartition, riceParam)) { - return MA_FALSE; - } - } else { - ma_uint8 unencodedBitsPerSample = 0; - if (!ma_dr_flac__read_uint8(bs, 5, &unencodedBitsPerSample)) { - return MA_FALSE; - } - if (!ma_dr_flac__seek_bits(bs, unencodedBitsPerSample * samplesInPartition)) { - return MA_FALSE; - } - } - if (partitionsRemaining == 1) { - break; - } - partitionsRemaining -= 1; - samplesInPartition = blockSize / (1 << partitionOrder); - } - return MA_TRUE; -} -static ma_bool32 ma_dr_flac__decode_samples__constant(ma_dr_flac_bs* bs, ma_uint32 blockSize, ma_uint32 subframeBitsPerSample, ma_int32* pDecodedSamples) -{ - ma_uint32 i; - ma_int32 sample; - if (!ma_dr_flac__read_int32(bs, subframeBitsPerSample, &sample)) { - return MA_FALSE; - } - for (i = 0; i < blockSize; ++i) { - pDecodedSamples[i] = sample; - } - return MA_TRUE; -} -static ma_bool32 ma_dr_flac__decode_samples__verbatim(ma_dr_flac_bs* bs, ma_uint32 blockSize, ma_uint32 subframeBitsPerSample, ma_int32* pDecodedSamples) -{ - ma_uint32 i; - for (i = 0; i < blockSize; ++i) { - ma_int32 sample; - if (!ma_dr_flac__read_int32(bs, subframeBitsPerSample, &sample)) { - return MA_FALSE; - } - pDecodedSamples[i] = sample; - } - return MA_TRUE; -} -static ma_bool32 ma_dr_flac__decode_samples__fixed(ma_dr_flac_bs* bs, ma_uint32 blockSize, ma_uint32 subframeBitsPerSample, ma_uint8 lpcOrder, ma_int32* pDecodedSamples) -{ - ma_uint32 i; - static ma_int32 lpcCoefficientsTable[5][4] = { - {0, 0, 0, 0}, - {1, 0, 0, 0}, - {2, -1, 0, 0}, - {3, -3, 1, 0}, - {4, -6, 4, -1} - }; - for (i = 0; i < lpcOrder; ++i) { - ma_int32 sample; - if (!ma_dr_flac__read_int32(bs, subframeBitsPerSample, &sample)) { - return MA_FALSE; - } - pDecodedSamples[i] = sample; - } - if (!ma_dr_flac__decode_samples_with_residual(bs, subframeBitsPerSample, blockSize, lpcOrder, 0, 4, lpcCoefficientsTable[lpcOrder], pDecodedSamples)) { - return MA_FALSE; - } - return MA_TRUE; -} -static ma_bool32 ma_dr_flac__decode_samples__lpc(ma_dr_flac_bs* bs, ma_uint32 blockSize, ma_uint32 bitsPerSample, ma_uint8 lpcOrder, ma_int32* pDecodedSamples) -{ - ma_uint8 i; - ma_uint8 lpcPrecision; - ma_int8 lpcShift; - ma_int32 coefficients[32]; - for (i = 0; i < lpcOrder; ++i) { - ma_int32 sample; - if (!ma_dr_flac__read_int32(bs, bitsPerSample, &sample)) { - return MA_FALSE; - } - pDecodedSamples[i] = sample; - } - if (!ma_dr_flac__read_uint8(bs, 4, &lpcPrecision)) { - return MA_FALSE; - } - if (lpcPrecision == 15) { - return MA_FALSE; - } - lpcPrecision += 1; - if (!ma_dr_flac__read_int8(bs, 5, &lpcShift)) { - return MA_FALSE; - } - if (lpcShift < 0) { - return MA_FALSE; - } - MA_DR_FLAC_ZERO_MEMORY(coefficients, sizeof(coefficients)); - for (i = 0; i < lpcOrder; ++i) { - if (!ma_dr_flac__read_int32(bs, lpcPrecision, coefficients + i)) { - return MA_FALSE; - } - } - if (!ma_dr_flac__decode_samples_with_residual(bs, bitsPerSample, blockSize, lpcOrder, lpcShift, lpcPrecision, coefficients, pDecodedSamples)) { - return MA_FALSE; - } - return MA_TRUE; -} -static ma_bool32 ma_dr_flac__read_next_flac_frame_header(ma_dr_flac_bs* bs, ma_uint8 streaminfoBitsPerSample, ma_dr_flac_frame_header* header) -{ - const ma_uint32 sampleRateTable[12] = {0, 88200, 176400, 192000, 8000, 16000, 22050, 24000, 32000, 44100, 48000, 96000}; - const ma_uint8 bitsPerSampleTable[8] = {0, 8, 12, (ma_uint8)-1, 16, 20, 24, (ma_uint8)-1}; - MA_DR_FLAC_ASSERT(bs != NULL); - MA_DR_FLAC_ASSERT(header != NULL); - for (;;) { - ma_uint8 crc8 = 0xCE; - ma_uint8 reserved = 0; - ma_uint8 blockingStrategy = 0; - ma_uint8 blockSize = 0; - ma_uint8 sampleRate = 0; - ma_uint8 channelAssignment = 0; - ma_uint8 bitsPerSample = 0; - ma_bool32 isVariableBlockSize; - if (!ma_dr_flac__find_and_seek_to_next_sync_code(bs)) { - return MA_FALSE; - } - if (!ma_dr_flac__read_uint8(bs, 1, &reserved)) { - return MA_FALSE; - } - if (reserved == 1) { - continue; - } - crc8 = ma_dr_flac_crc8(crc8, reserved, 1); - if (!ma_dr_flac__read_uint8(bs, 1, &blockingStrategy)) { - return MA_FALSE; - } - crc8 = ma_dr_flac_crc8(crc8, blockingStrategy, 1); - if (!ma_dr_flac__read_uint8(bs, 4, &blockSize)) { - return MA_FALSE; - } - if (blockSize == 0) { - continue; - } - crc8 = ma_dr_flac_crc8(crc8, blockSize, 4); - if (!ma_dr_flac__read_uint8(bs, 4, &sampleRate)) { - return MA_FALSE; - } - crc8 = ma_dr_flac_crc8(crc8, sampleRate, 4); - if (!ma_dr_flac__read_uint8(bs, 4, &channelAssignment)) { - return MA_FALSE; - } - if (channelAssignment > 10) { - continue; - } - crc8 = ma_dr_flac_crc8(crc8, channelAssignment, 4); - if (!ma_dr_flac__read_uint8(bs, 3, &bitsPerSample)) { - return MA_FALSE; - } - if (bitsPerSample == 3 || bitsPerSample == 7) { - continue; - } - crc8 = ma_dr_flac_crc8(crc8, bitsPerSample, 3); - if (!ma_dr_flac__read_uint8(bs, 1, &reserved)) { - return MA_FALSE; - } - if (reserved == 1) { - continue; - } - crc8 = ma_dr_flac_crc8(crc8, reserved, 1); - isVariableBlockSize = blockingStrategy == 1; - if (isVariableBlockSize) { - ma_uint64 pcmFrameNumber; - ma_result result = ma_dr_flac__read_utf8_coded_number(bs, &pcmFrameNumber, &crc8); - if (result != MA_SUCCESS) { - if (result == MA_AT_END) { - return MA_FALSE; - } else { - continue; - } - } - header->flacFrameNumber = 0; - header->pcmFrameNumber = pcmFrameNumber; - } else { - ma_uint64 flacFrameNumber = 0; - ma_result result = ma_dr_flac__read_utf8_coded_number(bs, &flacFrameNumber, &crc8); - if (result != MA_SUCCESS) { - if (result == MA_AT_END) { - return MA_FALSE; - } else { - continue; - } - } - header->flacFrameNumber = (ma_uint32)flacFrameNumber; - header->pcmFrameNumber = 0; - } - MA_DR_FLAC_ASSERT(blockSize > 0); - if (blockSize == 1) { - header->blockSizeInPCMFrames = 192; - } else if (blockSize <= 5) { - MA_DR_FLAC_ASSERT(blockSize >= 2); - header->blockSizeInPCMFrames = 576 * (1 << (blockSize - 2)); - } else if (blockSize == 6) { - if (!ma_dr_flac__read_uint16(bs, 8, &header->blockSizeInPCMFrames)) { - return MA_FALSE; - } - crc8 = ma_dr_flac_crc8(crc8, header->blockSizeInPCMFrames, 8); - header->blockSizeInPCMFrames += 1; - } else if (blockSize == 7) { - if (!ma_dr_flac__read_uint16(bs, 16, &header->blockSizeInPCMFrames)) { - return MA_FALSE; - } - crc8 = ma_dr_flac_crc8(crc8, header->blockSizeInPCMFrames, 16); - if (header->blockSizeInPCMFrames == 0xFFFF) { - return MA_FALSE; - } - header->blockSizeInPCMFrames += 1; - } else { - MA_DR_FLAC_ASSERT(blockSize >= 8); - header->blockSizeInPCMFrames = 256 * (1 << (blockSize - 8)); - } - if (sampleRate <= 11) { - header->sampleRate = sampleRateTable[sampleRate]; - } else if (sampleRate == 12) { - if (!ma_dr_flac__read_uint32(bs, 8, &header->sampleRate)) { - return MA_FALSE; - } - crc8 = ma_dr_flac_crc8(crc8, header->sampleRate, 8); - header->sampleRate *= 1000; - } else if (sampleRate == 13) { - if (!ma_dr_flac__read_uint32(bs, 16, &header->sampleRate)) { - return MA_FALSE; - } - crc8 = ma_dr_flac_crc8(crc8, header->sampleRate, 16); - } else if (sampleRate == 14) { - if (!ma_dr_flac__read_uint32(bs, 16, &header->sampleRate)) { - return MA_FALSE; - } - crc8 = ma_dr_flac_crc8(crc8, header->sampleRate, 16); - header->sampleRate *= 10; - } else { - continue; - } - header->channelAssignment = channelAssignment; - header->bitsPerSample = bitsPerSampleTable[bitsPerSample]; - if (header->bitsPerSample == 0) { - header->bitsPerSample = streaminfoBitsPerSample; - } - if (header->bitsPerSample != streaminfoBitsPerSample) { - return MA_FALSE; - } - if (!ma_dr_flac__read_uint8(bs, 8, &header->crc8)) { - return MA_FALSE; - } -#ifndef MA_DR_FLAC_NO_CRC - if (header->crc8 != crc8) { - continue; - } -#endif - return MA_TRUE; - } -} -static ma_bool32 ma_dr_flac__read_subframe_header(ma_dr_flac_bs* bs, ma_dr_flac_subframe* pSubframe) -{ - ma_uint8 header; - int type; - if (!ma_dr_flac__read_uint8(bs, 8, &header)) { - return MA_FALSE; - } - if ((header & 0x80) != 0) { - return MA_FALSE; - } - pSubframe->lpcOrder = 0; - type = (header & 0x7E) >> 1; - if (type == 0) { - pSubframe->subframeType = MA_DR_FLAC_SUBFRAME_CONSTANT; - } else if (type == 1) { - pSubframe->subframeType = MA_DR_FLAC_SUBFRAME_VERBATIM; - } else { - if ((type & 0x20) != 0) { - pSubframe->subframeType = MA_DR_FLAC_SUBFRAME_LPC; - pSubframe->lpcOrder = (ma_uint8)(type & 0x1F) + 1; - } else if ((type & 0x08) != 0) { - pSubframe->subframeType = MA_DR_FLAC_SUBFRAME_FIXED; - pSubframe->lpcOrder = (ma_uint8)(type & 0x07); - if (pSubframe->lpcOrder > 4) { - pSubframe->subframeType = MA_DR_FLAC_SUBFRAME_RESERVED; - pSubframe->lpcOrder = 0; - } - } else { - pSubframe->subframeType = MA_DR_FLAC_SUBFRAME_RESERVED; - } - } - if (pSubframe->subframeType == MA_DR_FLAC_SUBFRAME_RESERVED) { - return MA_FALSE; - } - pSubframe->wastedBitsPerSample = 0; - if ((header & 0x01) == 1) { - unsigned int wastedBitsPerSample; - if (!ma_dr_flac__seek_past_next_set_bit(bs, &wastedBitsPerSample)) { - return MA_FALSE; - } - pSubframe->wastedBitsPerSample = (ma_uint8)wastedBitsPerSample + 1; - } - return MA_TRUE; -} -static ma_bool32 ma_dr_flac__decode_subframe(ma_dr_flac_bs* bs, ma_dr_flac_frame* frame, int subframeIndex, ma_int32* pDecodedSamplesOut) -{ - ma_dr_flac_subframe* pSubframe; - ma_uint32 subframeBitsPerSample; - MA_DR_FLAC_ASSERT(bs != NULL); - MA_DR_FLAC_ASSERT(frame != NULL); - pSubframe = frame->subframes + subframeIndex; - if (!ma_dr_flac__read_subframe_header(bs, pSubframe)) { - return MA_FALSE; - } - subframeBitsPerSample = frame->header.bitsPerSample; - if ((frame->header.channelAssignment == MA_DR_FLAC_CHANNEL_ASSIGNMENT_LEFT_SIDE || frame->header.channelAssignment == MA_DR_FLAC_CHANNEL_ASSIGNMENT_MID_SIDE) && subframeIndex == 1) { - subframeBitsPerSample += 1; - } else if (frame->header.channelAssignment == MA_DR_FLAC_CHANNEL_ASSIGNMENT_RIGHT_SIDE && subframeIndex == 0) { - subframeBitsPerSample += 1; - } - if (subframeBitsPerSample > 32) { - return MA_FALSE; - } - if (pSubframe->wastedBitsPerSample >= subframeBitsPerSample) { - return MA_FALSE; - } - subframeBitsPerSample -= pSubframe->wastedBitsPerSample; - pSubframe->pSamplesS32 = pDecodedSamplesOut; - if (frame->header.blockSizeInPCMFrames < pSubframe->lpcOrder) { - return MA_FALSE; - } - switch (pSubframe->subframeType) - { - case MA_DR_FLAC_SUBFRAME_CONSTANT: - { - ma_dr_flac__decode_samples__constant(bs, frame->header.blockSizeInPCMFrames, subframeBitsPerSample, pSubframe->pSamplesS32); - } break; - case MA_DR_FLAC_SUBFRAME_VERBATIM: - { - ma_dr_flac__decode_samples__verbatim(bs, frame->header.blockSizeInPCMFrames, subframeBitsPerSample, pSubframe->pSamplesS32); - } break; - case MA_DR_FLAC_SUBFRAME_FIXED: - { - ma_dr_flac__decode_samples__fixed(bs, frame->header.blockSizeInPCMFrames, subframeBitsPerSample, pSubframe->lpcOrder, pSubframe->pSamplesS32); - } break; - case MA_DR_FLAC_SUBFRAME_LPC: - { - ma_dr_flac__decode_samples__lpc(bs, frame->header.blockSizeInPCMFrames, subframeBitsPerSample, pSubframe->lpcOrder, pSubframe->pSamplesS32); - } break; - default: return MA_FALSE; - } - return MA_TRUE; -} -static ma_bool32 ma_dr_flac__seek_subframe(ma_dr_flac_bs* bs, ma_dr_flac_frame* frame, int subframeIndex) -{ - ma_dr_flac_subframe* pSubframe; - ma_uint32 subframeBitsPerSample; - MA_DR_FLAC_ASSERT(bs != NULL); - MA_DR_FLAC_ASSERT(frame != NULL); - pSubframe = frame->subframes + subframeIndex; - if (!ma_dr_flac__read_subframe_header(bs, pSubframe)) { - return MA_FALSE; - } - subframeBitsPerSample = frame->header.bitsPerSample; - if ((frame->header.channelAssignment == MA_DR_FLAC_CHANNEL_ASSIGNMENT_LEFT_SIDE || frame->header.channelAssignment == MA_DR_FLAC_CHANNEL_ASSIGNMENT_MID_SIDE) && subframeIndex == 1) { - subframeBitsPerSample += 1; - } else if (frame->header.channelAssignment == MA_DR_FLAC_CHANNEL_ASSIGNMENT_RIGHT_SIDE && subframeIndex == 0) { - subframeBitsPerSample += 1; - } - if (pSubframe->wastedBitsPerSample >= subframeBitsPerSample) { - return MA_FALSE; - } - subframeBitsPerSample -= pSubframe->wastedBitsPerSample; - pSubframe->pSamplesS32 = NULL; - switch (pSubframe->subframeType) - { - case MA_DR_FLAC_SUBFRAME_CONSTANT: - { - if (!ma_dr_flac__seek_bits(bs, subframeBitsPerSample)) { - return MA_FALSE; - } - } break; - case MA_DR_FLAC_SUBFRAME_VERBATIM: - { - unsigned int bitsToSeek = frame->header.blockSizeInPCMFrames * subframeBitsPerSample; - if (!ma_dr_flac__seek_bits(bs, bitsToSeek)) { - return MA_FALSE; - } - } break; - case MA_DR_FLAC_SUBFRAME_FIXED: - { - unsigned int bitsToSeek = pSubframe->lpcOrder * subframeBitsPerSample; - if (!ma_dr_flac__seek_bits(bs, bitsToSeek)) { - return MA_FALSE; - } - if (!ma_dr_flac__read_and_seek_residual(bs, frame->header.blockSizeInPCMFrames, pSubframe->lpcOrder)) { - return MA_FALSE; - } - } break; - case MA_DR_FLAC_SUBFRAME_LPC: - { - ma_uint8 lpcPrecision; - unsigned int bitsToSeek = pSubframe->lpcOrder * subframeBitsPerSample; - if (!ma_dr_flac__seek_bits(bs, bitsToSeek)) { - return MA_FALSE; - } - if (!ma_dr_flac__read_uint8(bs, 4, &lpcPrecision)) { - return MA_FALSE; - } - if (lpcPrecision == 15) { - return MA_FALSE; - } - lpcPrecision += 1; - bitsToSeek = (pSubframe->lpcOrder * lpcPrecision) + 5; - if (!ma_dr_flac__seek_bits(bs, bitsToSeek)) { - return MA_FALSE; - } - if (!ma_dr_flac__read_and_seek_residual(bs, frame->header.blockSizeInPCMFrames, pSubframe->lpcOrder)) { - return MA_FALSE; - } - } break; - default: return MA_FALSE; - } - return MA_TRUE; -} -static MA_INLINE ma_uint8 ma_dr_flac__get_channel_count_from_channel_assignment(ma_int8 channelAssignment) -{ - ma_uint8 lookup[] = {1, 2, 3, 4, 5, 6, 7, 8, 2, 2, 2}; - MA_DR_FLAC_ASSERT(channelAssignment <= 10); - return lookup[channelAssignment]; -} -static ma_result ma_dr_flac__decode_flac_frame(ma_dr_flac* pFlac) -{ - int channelCount; - int i; - ma_uint8 paddingSizeInBits; - ma_uint16 desiredCRC16; -#ifndef MA_DR_FLAC_NO_CRC - ma_uint16 actualCRC16; -#endif - MA_DR_FLAC_ZERO_MEMORY(pFlac->currentFLACFrame.subframes, sizeof(pFlac->currentFLACFrame.subframes)); - if (pFlac->currentFLACFrame.header.blockSizeInPCMFrames > pFlac->maxBlockSizeInPCMFrames) { - return MA_ERROR; - } - channelCount = ma_dr_flac__get_channel_count_from_channel_assignment(pFlac->currentFLACFrame.header.channelAssignment); - if (channelCount != (int)pFlac->channels) { - return MA_ERROR; - } - for (i = 0; i < channelCount; ++i) { - if (!ma_dr_flac__decode_subframe(&pFlac->bs, &pFlac->currentFLACFrame, i, pFlac->pDecodedSamples + (pFlac->currentFLACFrame.header.blockSizeInPCMFrames * i))) { - return MA_ERROR; - } - } - paddingSizeInBits = (ma_uint8)(MA_DR_FLAC_CACHE_L1_BITS_REMAINING(&pFlac->bs) & 7); - if (paddingSizeInBits > 0) { - ma_uint8 padding = 0; - if (!ma_dr_flac__read_uint8(&pFlac->bs, paddingSizeInBits, &padding)) { - return MA_AT_END; - } - } -#ifndef MA_DR_FLAC_NO_CRC - actualCRC16 = ma_dr_flac__flush_crc16(&pFlac->bs); -#endif - if (!ma_dr_flac__read_uint16(&pFlac->bs, 16, &desiredCRC16)) { - return MA_AT_END; - } -#ifndef MA_DR_FLAC_NO_CRC - if (actualCRC16 != desiredCRC16) { - return MA_CRC_MISMATCH; - } -#endif - pFlac->currentFLACFrame.pcmFramesRemaining = pFlac->currentFLACFrame.header.blockSizeInPCMFrames; - return MA_SUCCESS; -} -static ma_result ma_dr_flac__seek_flac_frame(ma_dr_flac* pFlac) -{ - int channelCount; - int i; - ma_uint16 desiredCRC16; -#ifndef MA_DR_FLAC_NO_CRC - ma_uint16 actualCRC16; -#endif - channelCount = ma_dr_flac__get_channel_count_from_channel_assignment(pFlac->currentFLACFrame.header.channelAssignment); - for (i = 0; i < channelCount; ++i) { - if (!ma_dr_flac__seek_subframe(&pFlac->bs, &pFlac->currentFLACFrame, i)) { - return MA_ERROR; - } - } - if (!ma_dr_flac__seek_bits(&pFlac->bs, MA_DR_FLAC_CACHE_L1_BITS_REMAINING(&pFlac->bs) & 7)) { - return MA_ERROR; - } -#ifndef MA_DR_FLAC_NO_CRC - actualCRC16 = ma_dr_flac__flush_crc16(&pFlac->bs); -#endif - if (!ma_dr_flac__read_uint16(&pFlac->bs, 16, &desiredCRC16)) { - return MA_AT_END; - } -#ifndef MA_DR_FLAC_NO_CRC - if (actualCRC16 != desiredCRC16) { - return MA_CRC_MISMATCH; - } -#endif - return MA_SUCCESS; -} -static ma_bool32 ma_dr_flac__read_and_decode_next_flac_frame(ma_dr_flac* pFlac) -{ - MA_DR_FLAC_ASSERT(pFlac != NULL); - for (;;) { - ma_result result; - if (!ma_dr_flac__read_next_flac_frame_header(&pFlac->bs, pFlac->bitsPerSample, &pFlac->currentFLACFrame.header)) { - return MA_FALSE; - } - result = ma_dr_flac__decode_flac_frame(pFlac); - if (result != MA_SUCCESS) { - if (result == MA_CRC_MISMATCH) { - continue; - } else { - return MA_FALSE; - } - } - return MA_TRUE; - } -} -static void ma_dr_flac__get_pcm_frame_range_of_current_flac_frame(ma_dr_flac* pFlac, ma_uint64* pFirstPCMFrame, ma_uint64* pLastPCMFrame) -{ - ma_uint64 firstPCMFrame; - ma_uint64 lastPCMFrame; - MA_DR_FLAC_ASSERT(pFlac != NULL); - firstPCMFrame = pFlac->currentFLACFrame.header.pcmFrameNumber; - if (firstPCMFrame == 0) { - firstPCMFrame = ((ma_uint64)pFlac->currentFLACFrame.header.flacFrameNumber) * pFlac->maxBlockSizeInPCMFrames; - } - lastPCMFrame = firstPCMFrame + pFlac->currentFLACFrame.header.blockSizeInPCMFrames; - if (lastPCMFrame > 0) { - lastPCMFrame -= 1; - } - if (pFirstPCMFrame) { - *pFirstPCMFrame = firstPCMFrame; - } - if (pLastPCMFrame) { - *pLastPCMFrame = lastPCMFrame; - } -} -static ma_bool32 ma_dr_flac__seek_to_first_frame(ma_dr_flac* pFlac) -{ - ma_bool32 result; - MA_DR_FLAC_ASSERT(pFlac != NULL); - result = ma_dr_flac__seek_to_byte(&pFlac->bs, pFlac->firstFLACFramePosInBytes); - MA_DR_FLAC_ZERO_MEMORY(&pFlac->currentFLACFrame, sizeof(pFlac->currentFLACFrame)); - pFlac->currentPCMFrame = 0; - return result; -} -static MA_INLINE ma_result ma_dr_flac__seek_to_next_flac_frame(ma_dr_flac* pFlac) -{ - MA_DR_FLAC_ASSERT(pFlac != NULL); - return ma_dr_flac__seek_flac_frame(pFlac); -} -static ma_uint64 ma_dr_flac__seek_forward_by_pcm_frames(ma_dr_flac* pFlac, ma_uint64 pcmFramesToSeek) -{ - ma_uint64 pcmFramesRead = 0; - while (pcmFramesToSeek > 0) { - if (pFlac->currentFLACFrame.pcmFramesRemaining == 0) { - if (!ma_dr_flac__read_and_decode_next_flac_frame(pFlac)) { - break; - } - } else { - if (pFlac->currentFLACFrame.pcmFramesRemaining > pcmFramesToSeek) { - pcmFramesRead += pcmFramesToSeek; - pFlac->currentFLACFrame.pcmFramesRemaining -= (ma_uint32)pcmFramesToSeek; - pcmFramesToSeek = 0; - } else { - pcmFramesRead += pFlac->currentFLACFrame.pcmFramesRemaining; - pcmFramesToSeek -= pFlac->currentFLACFrame.pcmFramesRemaining; - pFlac->currentFLACFrame.pcmFramesRemaining = 0; - } - } - } - pFlac->currentPCMFrame += pcmFramesRead; - return pcmFramesRead; -} -static ma_bool32 ma_dr_flac__seek_to_pcm_frame__brute_force(ma_dr_flac* pFlac, ma_uint64 pcmFrameIndex) -{ - ma_bool32 isMidFrame = MA_FALSE; - ma_uint64 runningPCMFrameCount; - MA_DR_FLAC_ASSERT(pFlac != NULL); - if (pcmFrameIndex >= pFlac->currentPCMFrame) { - runningPCMFrameCount = pFlac->currentPCMFrame; - if (pFlac->currentPCMFrame == 0 && pFlac->currentFLACFrame.pcmFramesRemaining == 0) { - if (!ma_dr_flac__read_next_flac_frame_header(&pFlac->bs, pFlac->bitsPerSample, &pFlac->currentFLACFrame.header)) { - return MA_FALSE; - } - } else { - isMidFrame = MA_TRUE; - } - } else { - runningPCMFrameCount = 0; - if (!ma_dr_flac__seek_to_first_frame(pFlac)) { - return MA_FALSE; - } - if (!ma_dr_flac__read_next_flac_frame_header(&pFlac->bs, pFlac->bitsPerSample, &pFlac->currentFLACFrame.header)) { - return MA_FALSE; - } - } - for (;;) { - ma_uint64 pcmFrameCountInThisFLACFrame; - ma_uint64 firstPCMFrameInFLACFrame = 0; - ma_uint64 lastPCMFrameInFLACFrame = 0; - ma_dr_flac__get_pcm_frame_range_of_current_flac_frame(pFlac, &firstPCMFrameInFLACFrame, &lastPCMFrameInFLACFrame); - pcmFrameCountInThisFLACFrame = (lastPCMFrameInFLACFrame - firstPCMFrameInFLACFrame) + 1; - if (pcmFrameIndex < (runningPCMFrameCount + pcmFrameCountInThisFLACFrame)) { - ma_uint64 pcmFramesToDecode = pcmFrameIndex - runningPCMFrameCount; - if (!isMidFrame) { - ma_result result = ma_dr_flac__decode_flac_frame(pFlac); - if (result == MA_SUCCESS) { - return ma_dr_flac__seek_forward_by_pcm_frames(pFlac, pcmFramesToDecode) == pcmFramesToDecode; - } else { - if (result == MA_CRC_MISMATCH) { - goto next_iteration; - } else { - return MA_FALSE; - } - } - } else { - return ma_dr_flac__seek_forward_by_pcm_frames(pFlac, pcmFramesToDecode) == pcmFramesToDecode; - } - } else { - if (!isMidFrame) { - ma_result result = ma_dr_flac__seek_to_next_flac_frame(pFlac); - if (result == MA_SUCCESS) { - runningPCMFrameCount += pcmFrameCountInThisFLACFrame; - } else { - if (result == MA_CRC_MISMATCH) { - goto next_iteration; - } else { - return MA_FALSE; - } - } - } else { - runningPCMFrameCount += pFlac->currentFLACFrame.pcmFramesRemaining; - pFlac->currentFLACFrame.pcmFramesRemaining = 0; - isMidFrame = MA_FALSE; - } - if (pcmFrameIndex == pFlac->totalPCMFrameCount && runningPCMFrameCount == pFlac->totalPCMFrameCount) { - return MA_TRUE; - } - } - next_iteration: - if (!ma_dr_flac__read_next_flac_frame_header(&pFlac->bs, pFlac->bitsPerSample, &pFlac->currentFLACFrame.header)) { - return MA_FALSE; - } - } -} -#if !defined(MA_DR_FLAC_NO_CRC) -#define MA_DR_FLAC_BINARY_SEARCH_APPROX_COMPRESSION_RATIO 0.6f -static ma_bool32 ma_dr_flac__seek_to_approximate_flac_frame_to_byte(ma_dr_flac* pFlac, ma_uint64 targetByte, ma_uint64 rangeLo, ma_uint64 rangeHi, ma_uint64* pLastSuccessfulSeekOffset) -{ - MA_DR_FLAC_ASSERT(pFlac != NULL); - MA_DR_FLAC_ASSERT(pLastSuccessfulSeekOffset != NULL); - MA_DR_FLAC_ASSERT(targetByte >= rangeLo); - MA_DR_FLAC_ASSERT(targetByte <= rangeHi); - *pLastSuccessfulSeekOffset = pFlac->firstFLACFramePosInBytes; - for (;;) { - ma_uint64 lastTargetByte = targetByte; - if (!ma_dr_flac__seek_to_byte(&pFlac->bs, targetByte)) { - if (targetByte == 0) { - ma_dr_flac__seek_to_first_frame(pFlac); - return MA_FALSE; - } - targetByte = rangeLo + ((rangeHi - rangeLo)/2); - rangeHi = targetByte; - } else { - MA_DR_FLAC_ZERO_MEMORY(&pFlac->currentFLACFrame, sizeof(pFlac->currentFLACFrame)); -#if 1 - if (!ma_dr_flac__read_and_decode_next_flac_frame(pFlac)) { - targetByte = rangeLo + ((rangeHi - rangeLo)/2); - rangeHi = targetByte; - } else { - break; - } -#else - if (!ma_dr_flac__read_next_flac_frame_header(&pFlac->bs, pFlac->bitsPerSample, &pFlac->currentFLACFrame.header)) { - targetByte = rangeLo + ((rangeHi - rangeLo)/2); - rangeHi = targetByte; - } else { - break; - } -#endif - } - if(targetByte == lastTargetByte) { - return MA_FALSE; - } - } - ma_dr_flac__get_pcm_frame_range_of_current_flac_frame(pFlac, &pFlac->currentPCMFrame, NULL); - MA_DR_FLAC_ASSERT(targetByte <= rangeHi); - *pLastSuccessfulSeekOffset = targetByte; - return MA_TRUE; -} -static ma_bool32 ma_dr_flac__decode_flac_frame_and_seek_forward_by_pcm_frames(ma_dr_flac* pFlac, ma_uint64 offset) -{ -#if 0 - if (ma_dr_flac__decode_flac_frame(pFlac) != MA_SUCCESS) { - if (ma_dr_flac__read_and_decode_next_flac_frame(pFlac) == MA_FALSE) { - return MA_FALSE; - } - } -#endif - return ma_dr_flac__seek_forward_by_pcm_frames(pFlac, offset) == offset; -} -static ma_bool32 ma_dr_flac__seek_to_pcm_frame__binary_search_internal(ma_dr_flac* pFlac, ma_uint64 pcmFrameIndex, ma_uint64 byteRangeLo, ma_uint64 byteRangeHi) -{ - ma_uint64 targetByte; - ma_uint64 pcmRangeLo = pFlac->totalPCMFrameCount; - ma_uint64 pcmRangeHi = 0; - ma_uint64 lastSuccessfulSeekOffset = (ma_uint64)-1; - ma_uint64 closestSeekOffsetBeforeTargetPCMFrame = byteRangeLo; - ma_uint32 seekForwardThreshold = (pFlac->maxBlockSizeInPCMFrames != 0) ? pFlac->maxBlockSizeInPCMFrames*2 : 4096; - targetByte = byteRangeLo + (ma_uint64)(((ma_int64)((pcmFrameIndex - pFlac->currentPCMFrame) * pFlac->channels * pFlac->bitsPerSample)/8.0f) * MA_DR_FLAC_BINARY_SEARCH_APPROX_COMPRESSION_RATIO); - if (targetByte > byteRangeHi) { - targetByte = byteRangeHi; - } - for (;;) { - if (ma_dr_flac__seek_to_approximate_flac_frame_to_byte(pFlac, targetByte, byteRangeLo, byteRangeHi, &lastSuccessfulSeekOffset)) { - ma_uint64 newPCMRangeLo; - ma_uint64 newPCMRangeHi; - ma_dr_flac__get_pcm_frame_range_of_current_flac_frame(pFlac, &newPCMRangeLo, &newPCMRangeHi); - if (pcmRangeLo == newPCMRangeLo) { - if (!ma_dr_flac__seek_to_approximate_flac_frame_to_byte(pFlac, closestSeekOffsetBeforeTargetPCMFrame, closestSeekOffsetBeforeTargetPCMFrame, byteRangeHi, &lastSuccessfulSeekOffset)) { - break; - } - if (ma_dr_flac__decode_flac_frame_and_seek_forward_by_pcm_frames(pFlac, pcmFrameIndex - pFlac->currentPCMFrame)) { - return MA_TRUE; - } else { - break; - } - } - pcmRangeLo = newPCMRangeLo; - pcmRangeHi = newPCMRangeHi; - if (pcmRangeLo <= pcmFrameIndex && pcmRangeHi >= pcmFrameIndex) { - if (ma_dr_flac__decode_flac_frame_and_seek_forward_by_pcm_frames(pFlac, pcmFrameIndex - pFlac->currentPCMFrame) ) { - return MA_TRUE; - } else { - break; - } - } else { - const float approxCompressionRatio = (ma_int64)(lastSuccessfulSeekOffset - pFlac->firstFLACFramePosInBytes) / ((ma_int64)(pcmRangeLo * pFlac->channels * pFlac->bitsPerSample)/8.0f); - if (pcmRangeLo > pcmFrameIndex) { - byteRangeHi = lastSuccessfulSeekOffset; - if (byteRangeLo > byteRangeHi) { - byteRangeLo = byteRangeHi; - } - targetByte = byteRangeLo + ((byteRangeHi - byteRangeLo) / 2); - if (targetByte < byteRangeLo) { - targetByte = byteRangeLo; - } - } else { - if ((pcmFrameIndex - pcmRangeLo) < seekForwardThreshold) { - if (ma_dr_flac__decode_flac_frame_and_seek_forward_by_pcm_frames(pFlac, pcmFrameIndex - pFlac->currentPCMFrame)) { - return MA_TRUE; - } else { - break; - } - } else { - byteRangeLo = lastSuccessfulSeekOffset; - if (byteRangeHi < byteRangeLo) { - byteRangeHi = byteRangeLo; - } - targetByte = lastSuccessfulSeekOffset + (ma_uint64)(((ma_int64)((pcmFrameIndex-pcmRangeLo) * pFlac->channels * pFlac->bitsPerSample)/8.0f) * approxCompressionRatio); - if (targetByte > byteRangeHi) { - targetByte = byteRangeHi; - } - if (closestSeekOffsetBeforeTargetPCMFrame < lastSuccessfulSeekOffset) { - closestSeekOffsetBeforeTargetPCMFrame = lastSuccessfulSeekOffset; - } - } - } - } - } else { - break; - } - } - ma_dr_flac__seek_to_first_frame(pFlac); - return MA_FALSE; -} -static ma_bool32 ma_dr_flac__seek_to_pcm_frame__binary_search(ma_dr_flac* pFlac, ma_uint64 pcmFrameIndex) -{ - ma_uint64 byteRangeLo; - ma_uint64 byteRangeHi; - ma_uint32 seekForwardThreshold = (pFlac->maxBlockSizeInPCMFrames != 0) ? pFlac->maxBlockSizeInPCMFrames*2 : 4096; - if (ma_dr_flac__seek_to_first_frame(pFlac) == MA_FALSE) { - return MA_FALSE; - } - if (pcmFrameIndex < seekForwardThreshold) { - return ma_dr_flac__seek_forward_by_pcm_frames(pFlac, pcmFrameIndex) == pcmFrameIndex; - } - byteRangeLo = pFlac->firstFLACFramePosInBytes; - byteRangeHi = pFlac->firstFLACFramePosInBytes + (ma_uint64)((ma_int64)(pFlac->totalPCMFrameCount * pFlac->channels * pFlac->bitsPerSample)/8.0f); - return ma_dr_flac__seek_to_pcm_frame__binary_search_internal(pFlac, pcmFrameIndex, byteRangeLo, byteRangeHi); -} -#endif -static ma_bool32 ma_dr_flac__seek_to_pcm_frame__seek_table(ma_dr_flac* pFlac, ma_uint64 pcmFrameIndex) -{ - ma_uint32 iClosestSeekpoint = 0; - ma_bool32 isMidFrame = MA_FALSE; - ma_uint64 runningPCMFrameCount; - ma_uint32 iSeekpoint; - MA_DR_FLAC_ASSERT(pFlac != NULL); - if (pFlac->pSeekpoints == NULL || pFlac->seekpointCount == 0) { - return MA_FALSE; - } - if (pFlac->pSeekpoints[0].firstPCMFrame > pcmFrameIndex) { - return MA_FALSE; - } - for (iSeekpoint = 0; iSeekpoint < pFlac->seekpointCount; ++iSeekpoint) { - if (pFlac->pSeekpoints[iSeekpoint].firstPCMFrame >= pcmFrameIndex) { - break; - } - iClosestSeekpoint = iSeekpoint; - } - if (pFlac->pSeekpoints[iClosestSeekpoint].pcmFrameCount == 0 || pFlac->pSeekpoints[iClosestSeekpoint].pcmFrameCount > pFlac->maxBlockSizeInPCMFrames) { - return MA_FALSE; - } - if (pFlac->pSeekpoints[iClosestSeekpoint].firstPCMFrame > pFlac->totalPCMFrameCount && pFlac->totalPCMFrameCount > 0) { - return MA_FALSE; - } -#if !defined(MA_DR_FLAC_NO_CRC) - if (pFlac->totalPCMFrameCount > 0) { - ma_uint64 byteRangeLo; - ma_uint64 byteRangeHi; - byteRangeHi = pFlac->firstFLACFramePosInBytes + (ma_uint64)((ma_int64)(pFlac->totalPCMFrameCount * pFlac->channels * pFlac->bitsPerSample)/8.0f); - byteRangeLo = pFlac->firstFLACFramePosInBytes + pFlac->pSeekpoints[iClosestSeekpoint].flacFrameOffset; - if (iClosestSeekpoint < pFlac->seekpointCount-1) { - ma_uint32 iNextSeekpoint = iClosestSeekpoint + 1; - if (pFlac->pSeekpoints[iClosestSeekpoint].flacFrameOffset >= pFlac->pSeekpoints[iNextSeekpoint].flacFrameOffset || pFlac->pSeekpoints[iNextSeekpoint].pcmFrameCount == 0) { - return MA_FALSE; - } - if (pFlac->pSeekpoints[iNextSeekpoint].firstPCMFrame != (((ma_uint64)0xFFFFFFFF << 32) | 0xFFFFFFFF)) { - byteRangeHi = pFlac->firstFLACFramePosInBytes + pFlac->pSeekpoints[iNextSeekpoint].flacFrameOffset - 1; - } - } - if (ma_dr_flac__seek_to_byte(&pFlac->bs, pFlac->firstFLACFramePosInBytes + pFlac->pSeekpoints[iClosestSeekpoint].flacFrameOffset)) { - if (ma_dr_flac__read_next_flac_frame_header(&pFlac->bs, pFlac->bitsPerSample, &pFlac->currentFLACFrame.header)) { - ma_dr_flac__get_pcm_frame_range_of_current_flac_frame(pFlac, &pFlac->currentPCMFrame, NULL); - if (ma_dr_flac__seek_to_pcm_frame__binary_search_internal(pFlac, pcmFrameIndex, byteRangeLo, byteRangeHi)) { - return MA_TRUE; - } - } - } - } -#endif - if (pcmFrameIndex >= pFlac->currentPCMFrame && pFlac->pSeekpoints[iClosestSeekpoint].firstPCMFrame <= pFlac->currentPCMFrame) { - runningPCMFrameCount = pFlac->currentPCMFrame; - if (pFlac->currentPCMFrame == 0 && pFlac->currentFLACFrame.pcmFramesRemaining == 0) { - if (!ma_dr_flac__read_next_flac_frame_header(&pFlac->bs, pFlac->bitsPerSample, &pFlac->currentFLACFrame.header)) { - return MA_FALSE; - } - } else { - isMidFrame = MA_TRUE; - } - } else { - runningPCMFrameCount = pFlac->pSeekpoints[iClosestSeekpoint].firstPCMFrame; - if (!ma_dr_flac__seek_to_byte(&pFlac->bs, pFlac->firstFLACFramePosInBytes + pFlac->pSeekpoints[iClosestSeekpoint].flacFrameOffset)) { - return MA_FALSE; - } - if (!ma_dr_flac__read_next_flac_frame_header(&pFlac->bs, pFlac->bitsPerSample, &pFlac->currentFLACFrame.header)) { - return MA_FALSE; - } - } - for (;;) { - ma_uint64 pcmFrameCountInThisFLACFrame; - ma_uint64 firstPCMFrameInFLACFrame = 0; - ma_uint64 lastPCMFrameInFLACFrame = 0; - ma_dr_flac__get_pcm_frame_range_of_current_flac_frame(pFlac, &firstPCMFrameInFLACFrame, &lastPCMFrameInFLACFrame); - pcmFrameCountInThisFLACFrame = (lastPCMFrameInFLACFrame - firstPCMFrameInFLACFrame) + 1; - if (pcmFrameIndex < (runningPCMFrameCount + pcmFrameCountInThisFLACFrame)) { - ma_uint64 pcmFramesToDecode = pcmFrameIndex - runningPCMFrameCount; - if (!isMidFrame) { - ma_result result = ma_dr_flac__decode_flac_frame(pFlac); - if (result == MA_SUCCESS) { - return ma_dr_flac__seek_forward_by_pcm_frames(pFlac, pcmFramesToDecode) == pcmFramesToDecode; - } else { - if (result == MA_CRC_MISMATCH) { - goto next_iteration; - } else { - return MA_FALSE; - } - } - } else { - return ma_dr_flac__seek_forward_by_pcm_frames(pFlac, pcmFramesToDecode) == pcmFramesToDecode; - } - } else { - if (!isMidFrame) { - ma_result result = ma_dr_flac__seek_to_next_flac_frame(pFlac); - if (result == MA_SUCCESS) { - runningPCMFrameCount += pcmFrameCountInThisFLACFrame; - } else { - if (result == MA_CRC_MISMATCH) { - goto next_iteration; - } else { - return MA_FALSE; - } - } - } else { - runningPCMFrameCount += pFlac->currentFLACFrame.pcmFramesRemaining; - pFlac->currentFLACFrame.pcmFramesRemaining = 0; - isMidFrame = MA_FALSE; - } - if (pcmFrameIndex == pFlac->totalPCMFrameCount && runningPCMFrameCount == pFlac->totalPCMFrameCount) { - return MA_TRUE; - } - } - next_iteration: - if (!ma_dr_flac__read_next_flac_frame_header(&pFlac->bs, pFlac->bitsPerSample, &pFlac->currentFLACFrame.header)) { - return MA_FALSE; - } - } -} -#ifndef MA_DR_FLAC_NO_OGG -typedef struct -{ - ma_uint8 capturePattern[4]; - ma_uint8 structureVersion; - ma_uint8 headerType; - ma_uint64 granulePosition; - ma_uint32 serialNumber; - ma_uint32 sequenceNumber; - ma_uint32 checksum; - ma_uint8 segmentCount; - ma_uint8 segmentTable[255]; -} ma_dr_flac_ogg_page_header; -#endif -typedef struct -{ - ma_dr_flac_read_proc onRead; - ma_dr_flac_seek_proc onSeek; - ma_dr_flac_meta_proc onMeta; - ma_dr_flac_container container; - void* pUserData; - void* pUserDataMD; - ma_uint32 sampleRate; - ma_uint8 channels; - ma_uint8 bitsPerSample; - ma_uint64 totalPCMFrameCount; - ma_uint16 maxBlockSizeInPCMFrames; - ma_uint64 runningFilePos; - ma_bool32 hasStreamInfoBlock; - ma_bool32 hasMetadataBlocks; - ma_dr_flac_bs bs; - ma_dr_flac_frame_header firstFrameHeader; -#ifndef MA_DR_FLAC_NO_OGG - ma_uint32 oggSerial; - ma_uint64 oggFirstBytePos; - ma_dr_flac_ogg_page_header oggBosHeader; -#endif -} ma_dr_flac_init_info; -static MA_INLINE void ma_dr_flac__decode_block_header(ma_uint32 blockHeader, ma_uint8* isLastBlock, ma_uint8* blockType, ma_uint32* blockSize) -{ - blockHeader = ma_dr_flac__be2host_32(blockHeader); - *isLastBlock = (ma_uint8)((blockHeader & 0x80000000UL) >> 31); - *blockType = (ma_uint8)((blockHeader & 0x7F000000UL) >> 24); - *blockSize = (blockHeader & 0x00FFFFFFUL); -} -static MA_INLINE ma_bool32 ma_dr_flac__read_and_decode_block_header(ma_dr_flac_read_proc onRead, void* pUserData, ma_uint8* isLastBlock, ma_uint8* blockType, ma_uint32* blockSize) -{ - ma_uint32 blockHeader; - *blockSize = 0; - if (onRead(pUserData, &blockHeader, 4) != 4) { - return MA_FALSE; - } - ma_dr_flac__decode_block_header(blockHeader, isLastBlock, blockType, blockSize); - return MA_TRUE; -} -static ma_bool32 ma_dr_flac__read_streaminfo(ma_dr_flac_read_proc onRead, void* pUserData, ma_dr_flac_streaminfo* pStreamInfo) -{ - ma_uint32 blockSizes; - ma_uint64 frameSizes = 0; - ma_uint64 importantProps; - ma_uint8 md5[16]; - if (onRead(pUserData, &blockSizes, 4) != 4) { - return MA_FALSE; - } - if (onRead(pUserData, &frameSizes, 6) != 6) { - return MA_FALSE; - } - if (onRead(pUserData, &importantProps, 8) != 8) { - return MA_FALSE; - } - if (onRead(pUserData, md5, sizeof(md5)) != sizeof(md5)) { - return MA_FALSE; - } - blockSizes = ma_dr_flac__be2host_32(blockSizes); - frameSizes = ma_dr_flac__be2host_64(frameSizes); - importantProps = ma_dr_flac__be2host_64(importantProps); - pStreamInfo->minBlockSizeInPCMFrames = (ma_uint16)((blockSizes & 0xFFFF0000) >> 16); - pStreamInfo->maxBlockSizeInPCMFrames = (ma_uint16) (blockSizes & 0x0000FFFF); - pStreamInfo->minFrameSizeInPCMFrames = (ma_uint32)((frameSizes & (((ma_uint64)0x00FFFFFF << 16) << 24)) >> 40); - pStreamInfo->maxFrameSizeInPCMFrames = (ma_uint32)((frameSizes & (((ma_uint64)0x00FFFFFF << 16) << 0)) >> 16); - pStreamInfo->sampleRate = (ma_uint32)((importantProps & (((ma_uint64)0x000FFFFF << 16) << 28)) >> 44); - pStreamInfo->channels = (ma_uint8 )((importantProps & (((ma_uint64)0x0000000E << 16) << 24)) >> 41) + 1; - pStreamInfo->bitsPerSample = (ma_uint8 )((importantProps & (((ma_uint64)0x0000001F << 16) << 20)) >> 36) + 1; - pStreamInfo->totalPCMFrameCount = ((importantProps & ((((ma_uint64)0x0000000F << 16) << 16) | 0xFFFFFFFF))); - MA_DR_FLAC_COPY_MEMORY(pStreamInfo->md5, md5, sizeof(md5)); - return MA_TRUE; -} -static void* ma_dr_flac__malloc_default(size_t sz, void* pUserData) -{ - (void)pUserData; - return MA_DR_FLAC_MALLOC(sz); -} -static void* ma_dr_flac__realloc_default(void* p, size_t sz, void* pUserData) -{ - (void)pUserData; - return MA_DR_FLAC_REALLOC(p, sz); -} -static void ma_dr_flac__free_default(void* p, void* pUserData) -{ - (void)pUserData; - MA_DR_FLAC_FREE(p); -} -static void* ma_dr_flac__malloc_from_callbacks(size_t sz, const ma_allocation_callbacks* pAllocationCallbacks) -{ - if (pAllocationCallbacks == NULL) { - return NULL; - } - if (pAllocationCallbacks->onMalloc != NULL) { - return pAllocationCallbacks->onMalloc(sz, pAllocationCallbacks->pUserData); - } - if (pAllocationCallbacks->onRealloc != NULL) { - return pAllocationCallbacks->onRealloc(NULL, sz, pAllocationCallbacks->pUserData); - } - return NULL; -} -static void* ma_dr_flac__realloc_from_callbacks(void* p, size_t szNew, size_t szOld, const ma_allocation_callbacks* pAllocationCallbacks) -{ - if (pAllocationCallbacks == NULL) { - return NULL; - } - if (pAllocationCallbacks->onRealloc != NULL) { - return pAllocationCallbacks->onRealloc(p, szNew, pAllocationCallbacks->pUserData); - } - if (pAllocationCallbacks->onMalloc != NULL && pAllocationCallbacks->onFree != NULL) { - void* p2; - p2 = pAllocationCallbacks->onMalloc(szNew, pAllocationCallbacks->pUserData); - if (p2 == NULL) { - return NULL; - } - if (p != NULL) { - MA_DR_FLAC_COPY_MEMORY(p2, p, szOld); - pAllocationCallbacks->onFree(p, pAllocationCallbacks->pUserData); - } - return p2; - } - return NULL; -} -static void ma_dr_flac__free_from_callbacks(void* p, const ma_allocation_callbacks* pAllocationCallbacks) -{ - if (p == NULL || pAllocationCallbacks == NULL) { - return; - } - if (pAllocationCallbacks->onFree != NULL) { - pAllocationCallbacks->onFree(p, pAllocationCallbacks->pUserData); - } -} -static ma_bool32 ma_dr_flac__read_and_decode_metadata(ma_dr_flac_read_proc onRead, ma_dr_flac_seek_proc onSeek, ma_dr_flac_meta_proc onMeta, void* pUserData, void* pUserDataMD, ma_uint64* pFirstFramePos, ma_uint64* pSeektablePos, ma_uint32* pSeekpointCount, ma_allocation_callbacks* pAllocationCallbacks) -{ - ma_uint64 runningFilePos = 42; - ma_uint64 seektablePos = 0; - ma_uint32 seektableSize = 0; - for (;;) { - ma_dr_flac_metadata metadata; - ma_uint8 isLastBlock = 0; - ma_uint8 blockType = 0; - ma_uint32 blockSize; - if (ma_dr_flac__read_and_decode_block_header(onRead, pUserData, &isLastBlock, &blockType, &blockSize) == MA_FALSE) { - return MA_FALSE; - } - runningFilePos += 4; - metadata.type = blockType; - metadata.pRawData = NULL; - metadata.rawDataSize = 0; - switch (blockType) - { - case MA_DR_FLAC_METADATA_BLOCK_TYPE_APPLICATION: - { - if (blockSize < 4) { - return MA_FALSE; - } - if (onMeta) { - void* pRawData = ma_dr_flac__malloc_from_callbacks(blockSize, pAllocationCallbacks); - if (pRawData == NULL) { - return MA_FALSE; - } - if (onRead(pUserData, pRawData, blockSize) != blockSize) { - ma_dr_flac__free_from_callbacks(pRawData, pAllocationCallbacks); - return MA_FALSE; - } - metadata.pRawData = pRawData; - metadata.rawDataSize = blockSize; - metadata.data.application.id = ma_dr_flac__be2host_32(*(ma_uint32*)pRawData); - metadata.data.application.pData = (const void*)((ma_uint8*)pRawData + sizeof(ma_uint32)); - metadata.data.application.dataSize = blockSize - sizeof(ma_uint32); - onMeta(pUserDataMD, &metadata); - ma_dr_flac__free_from_callbacks(pRawData, pAllocationCallbacks); - } - } break; - case MA_DR_FLAC_METADATA_BLOCK_TYPE_SEEKTABLE: - { - seektablePos = runningFilePos; - seektableSize = blockSize; - if (onMeta) { - ma_uint32 seekpointCount; - ma_uint32 iSeekpoint; - void* pRawData; - seekpointCount = blockSize/MA_DR_FLAC_SEEKPOINT_SIZE_IN_BYTES; - pRawData = ma_dr_flac__malloc_from_callbacks(seekpointCount * sizeof(ma_dr_flac_seekpoint), pAllocationCallbacks); - if (pRawData == NULL) { - return MA_FALSE; - } - for (iSeekpoint = 0; iSeekpoint < seekpointCount; ++iSeekpoint) { - ma_dr_flac_seekpoint* pSeekpoint = (ma_dr_flac_seekpoint*)pRawData + iSeekpoint; - if (onRead(pUserData, pSeekpoint, MA_DR_FLAC_SEEKPOINT_SIZE_IN_BYTES) != MA_DR_FLAC_SEEKPOINT_SIZE_IN_BYTES) { - ma_dr_flac__free_from_callbacks(pRawData, pAllocationCallbacks); - return MA_FALSE; - } - pSeekpoint->firstPCMFrame = ma_dr_flac__be2host_64(pSeekpoint->firstPCMFrame); - pSeekpoint->flacFrameOffset = ma_dr_flac__be2host_64(pSeekpoint->flacFrameOffset); - pSeekpoint->pcmFrameCount = ma_dr_flac__be2host_16(pSeekpoint->pcmFrameCount); - } - metadata.pRawData = pRawData; - metadata.rawDataSize = blockSize; - metadata.data.seektable.seekpointCount = seekpointCount; - metadata.data.seektable.pSeekpoints = (const ma_dr_flac_seekpoint*)pRawData; - onMeta(pUserDataMD, &metadata); - ma_dr_flac__free_from_callbacks(pRawData, pAllocationCallbacks); - } - } break; - case MA_DR_FLAC_METADATA_BLOCK_TYPE_VORBIS_COMMENT: - { - if (blockSize < 8) { - return MA_FALSE; - } - if (onMeta) { - void* pRawData; - const char* pRunningData; - const char* pRunningDataEnd; - ma_uint32 i; - pRawData = ma_dr_flac__malloc_from_callbacks(blockSize, pAllocationCallbacks); - if (pRawData == NULL) { - return MA_FALSE; - } - if (onRead(pUserData, pRawData, blockSize) != blockSize) { - ma_dr_flac__free_from_callbacks(pRawData, pAllocationCallbacks); - return MA_FALSE; - } - metadata.pRawData = pRawData; - metadata.rawDataSize = blockSize; - pRunningData = (const char*)pRawData; - pRunningDataEnd = (const char*)pRawData + blockSize; - metadata.data.vorbis_comment.vendorLength = ma_dr_flac__le2host_32_ptr_unaligned(pRunningData); pRunningData += 4; - if ((pRunningDataEnd - pRunningData) - 4 < (ma_int64)metadata.data.vorbis_comment.vendorLength) { - ma_dr_flac__free_from_callbacks(pRawData, pAllocationCallbacks); - return MA_FALSE; - } - metadata.data.vorbis_comment.vendor = pRunningData; pRunningData += metadata.data.vorbis_comment.vendorLength; - metadata.data.vorbis_comment.commentCount = ma_dr_flac__le2host_32_ptr_unaligned(pRunningData); pRunningData += 4; - if ((pRunningDataEnd - pRunningData) / sizeof(ma_uint32) < metadata.data.vorbis_comment.commentCount) { - ma_dr_flac__free_from_callbacks(pRawData, pAllocationCallbacks); - return MA_FALSE; - } - metadata.data.vorbis_comment.pComments = pRunningData; - for (i = 0; i < metadata.data.vorbis_comment.commentCount; ++i) { - ma_uint32 commentLength; - if (pRunningDataEnd - pRunningData < 4) { - ma_dr_flac__free_from_callbacks(pRawData, pAllocationCallbacks); - return MA_FALSE; - } - commentLength = ma_dr_flac__le2host_32_ptr_unaligned(pRunningData); pRunningData += 4; - if (pRunningDataEnd - pRunningData < (ma_int64)commentLength) { - ma_dr_flac__free_from_callbacks(pRawData, pAllocationCallbacks); - return MA_FALSE; - } - pRunningData += commentLength; - } - onMeta(pUserDataMD, &metadata); - ma_dr_flac__free_from_callbacks(pRawData, pAllocationCallbacks); - } - } break; - case MA_DR_FLAC_METADATA_BLOCK_TYPE_CUESHEET: - { - if (blockSize < 396) { - return MA_FALSE; - } - if (onMeta) { - void* pRawData; - const char* pRunningData; - const char* pRunningDataEnd; - size_t bufferSize; - ma_uint8 iTrack; - ma_uint8 iIndex; - void* pTrackData; - pRawData = ma_dr_flac__malloc_from_callbacks(blockSize, pAllocationCallbacks); - if (pRawData == NULL) { - return MA_FALSE; - } - if (onRead(pUserData, pRawData, blockSize) != blockSize) { - ma_dr_flac__free_from_callbacks(pRawData, pAllocationCallbacks); - return MA_FALSE; - } - metadata.pRawData = pRawData; - metadata.rawDataSize = blockSize; - pRunningData = (const char*)pRawData; - pRunningDataEnd = (const char*)pRawData + blockSize; - MA_DR_FLAC_COPY_MEMORY(metadata.data.cuesheet.catalog, pRunningData, 128); pRunningData += 128; - metadata.data.cuesheet.leadInSampleCount = ma_dr_flac__be2host_64(*(const ma_uint64*)pRunningData); pRunningData += 8; - metadata.data.cuesheet.isCD = (pRunningData[0] & 0x80) != 0; pRunningData += 259; - metadata.data.cuesheet.trackCount = pRunningData[0]; pRunningData += 1; - metadata.data.cuesheet.pTrackData = NULL; - { - const char* pRunningDataSaved = pRunningData; - bufferSize = metadata.data.cuesheet.trackCount * MA_DR_FLAC_CUESHEET_TRACK_SIZE_IN_BYTES; - for (iTrack = 0; iTrack < metadata.data.cuesheet.trackCount; ++iTrack) { - ma_uint8 indexCount; - ma_uint32 indexPointSize; - if (pRunningDataEnd - pRunningData < MA_DR_FLAC_CUESHEET_TRACK_SIZE_IN_BYTES) { - ma_dr_flac__free_from_callbacks(pRawData, pAllocationCallbacks); - return MA_FALSE; - } - pRunningData += 35; - indexCount = pRunningData[0]; - pRunningData += 1; - bufferSize += indexCount * sizeof(ma_dr_flac_cuesheet_track_index); - indexPointSize = indexCount * MA_DR_FLAC_CUESHEET_TRACK_INDEX_SIZE_IN_BYTES; - if (pRunningDataEnd - pRunningData < (ma_int64)indexPointSize) { - ma_dr_flac__free_from_callbacks(pRawData, pAllocationCallbacks); - return MA_FALSE; - } - pRunningData += indexPointSize; - } - pRunningData = pRunningDataSaved; - } - { - char* pRunningTrackData; - pTrackData = ma_dr_flac__malloc_from_callbacks(bufferSize, pAllocationCallbacks); - if (pTrackData == NULL) { - ma_dr_flac__free_from_callbacks(pRawData, pAllocationCallbacks); - return MA_FALSE; - } - pRunningTrackData = (char*)pTrackData; - for (iTrack = 0; iTrack < metadata.data.cuesheet.trackCount; ++iTrack) { - ma_uint8 indexCount; - MA_DR_FLAC_COPY_MEMORY(pRunningTrackData, pRunningData, MA_DR_FLAC_CUESHEET_TRACK_SIZE_IN_BYTES); - pRunningData += MA_DR_FLAC_CUESHEET_TRACK_SIZE_IN_BYTES-1; - pRunningTrackData += MA_DR_FLAC_CUESHEET_TRACK_SIZE_IN_BYTES-1; - indexCount = pRunningData[0]; - pRunningData += 1; - pRunningTrackData += 1; - for (iIndex = 0; iIndex < indexCount; ++iIndex) { - ma_dr_flac_cuesheet_track_index* pTrackIndex = (ma_dr_flac_cuesheet_track_index*)pRunningTrackData; - MA_DR_FLAC_COPY_MEMORY(pRunningTrackData, pRunningData, MA_DR_FLAC_CUESHEET_TRACK_INDEX_SIZE_IN_BYTES); - pRunningData += MA_DR_FLAC_CUESHEET_TRACK_INDEX_SIZE_IN_BYTES; - pRunningTrackData += sizeof(ma_dr_flac_cuesheet_track_index); - pTrackIndex->offset = ma_dr_flac__be2host_64(pTrackIndex->offset); - } - } - metadata.data.cuesheet.pTrackData = pTrackData; - } - ma_dr_flac__free_from_callbacks(pRawData, pAllocationCallbacks); - pRawData = NULL; - onMeta(pUserDataMD, &metadata); - ma_dr_flac__free_from_callbacks(pTrackData, pAllocationCallbacks); - pTrackData = NULL; - } - } break; - case MA_DR_FLAC_METADATA_BLOCK_TYPE_PICTURE: - { - if (blockSize < 32) { - return MA_FALSE; - } - if (onMeta) { - void* pRawData; - const char* pRunningData; - const char* pRunningDataEnd; - pRawData = ma_dr_flac__malloc_from_callbacks(blockSize, pAllocationCallbacks); - if (pRawData == NULL) { - return MA_FALSE; - } - if (onRead(pUserData, pRawData, blockSize) != blockSize) { - ma_dr_flac__free_from_callbacks(pRawData, pAllocationCallbacks); - return MA_FALSE; - } - metadata.pRawData = pRawData; - metadata.rawDataSize = blockSize; - pRunningData = (const char*)pRawData; - pRunningDataEnd = (const char*)pRawData + blockSize; - metadata.data.picture.type = ma_dr_flac__be2host_32_ptr_unaligned(pRunningData); pRunningData += 4; - metadata.data.picture.mimeLength = ma_dr_flac__be2host_32_ptr_unaligned(pRunningData); pRunningData += 4; - if ((pRunningDataEnd - pRunningData) - 24 < (ma_int64)metadata.data.picture.mimeLength) { - ma_dr_flac__free_from_callbacks(pRawData, pAllocationCallbacks); - return MA_FALSE; - } - metadata.data.picture.mime = pRunningData; pRunningData += metadata.data.picture.mimeLength; - metadata.data.picture.descriptionLength = ma_dr_flac__be2host_32_ptr_unaligned(pRunningData); pRunningData += 4; - if ((pRunningDataEnd - pRunningData) - 20 < (ma_int64)metadata.data.picture.descriptionLength) { - ma_dr_flac__free_from_callbacks(pRawData, pAllocationCallbacks); - return MA_FALSE; - } - metadata.data.picture.description = pRunningData; pRunningData += metadata.data.picture.descriptionLength; - metadata.data.picture.width = ma_dr_flac__be2host_32_ptr_unaligned(pRunningData); pRunningData += 4; - metadata.data.picture.height = ma_dr_flac__be2host_32_ptr_unaligned(pRunningData); pRunningData += 4; - metadata.data.picture.colorDepth = ma_dr_flac__be2host_32_ptr_unaligned(pRunningData); pRunningData += 4; - metadata.data.picture.indexColorCount = ma_dr_flac__be2host_32_ptr_unaligned(pRunningData); pRunningData += 4; - metadata.data.picture.pictureDataSize = ma_dr_flac__be2host_32_ptr_unaligned(pRunningData); pRunningData += 4; - metadata.data.picture.pPictureData = (const ma_uint8*)pRunningData; - if (pRunningDataEnd - pRunningData < (ma_int64)metadata.data.picture.pictureDataSize) { - ma_dr_flac__free_from_callbacks(pRawData, pAllocationCallbacks); - return MA_FALSE; - } - onMeta(pUserDataMD, &metadata); - ma_dr_flac__free_from_callbacks(pRawData, pAllocationCallbacks); - } - } break; - case MA_DR_FLAC_METADATA_BLOCK_TYPE_PADDING: - { - if (onMeta) { - metadata.data.padding.unused = 0; - if (!onSeek(pUserData, blockSize, ma_dr_flac_seek_origin_current)) { - isLastBlock = MA_TRUE; - } else { - onMeta(pUserDataMD, &metadata); - } - } - } break; - case MA_DR_FLAC_METADATA_BLOCK_TYPE_INVALID: - { - if (onMeta) { - if (!onSeek(pUserData, blockSize, ma_dr_flac_seek_origin_current)) { - isLastBlock = MA_TRUE; - } - } - } break; - default: - { - if (onMeta) { - void* pRawData = ma_dr_flac__malloc_from_callbacks(blockSize, pAllocationCallbacks); - if (pRawData == NULL) { - return MA_FALSE; - } - if (onRead(pUserData, pRawData, blockSize) != blockSize) { - ma_dr_flac__free_from_callbacks(pRawData, pAllocationCallbacks); - return MA_FALSE; - } - metadata.pRawData = pRawData; - metadata.rawDataSize = blockSize; - onMeta(pUserDataMD, &metadata); - ma_dr_flac__free_from_callbacks(pRawData, pAllocationCallbacks); - } - } break; - } - if (onMeta == NULL && blockSize > 0) { - if (!onSeek(pUserData, blockSize, ma_dr_flac_seek_origin_current)) { - isLastBlock = MA_TRUE; - } - } - runningFilePos += blockSize; - if (isLastBlock) { - break; - } - } - *pSeektablePos = seektablePos; - *pSeekpointCount = seektableSize / MA_DR_FLAC_SEEKPOINT_SIZE_IN_BYTES; - *pFirstFramePos = runningFilePos; - return MA_TRUE; -} -static ma_bool32 ma_dr_flac__init_private__native(ma_dr_flac_init_info* pInit, ma_dr_flac_read_proc onRead, ma_dr_flac_seek_proc onSeek, ma_dr_flac_meta_proc onMeta, void* pUserData, void* pUserDataMD, ma_bool32 relaxed) -{ - ma_uint8 isLastBlock; - ma_uint8 blockType; - ma_uint32 blockSize; - (void)onSeek; - pInit->container = ma_dr_flac_container_native; - if (!ma_dr_flac__read_and_decode_block_header(onRead, pUserData, &isLastBlock, &blockType, &blockSize)) { - return MA_FALSE; - } - if (blockType != MA_DR_FLAC_METADATA_BLOCK_TYPE_STREAMINFO || blockSize != 34) { - if (!relaxed) { - return MA_FALSE; - } else { - pInit->hasStreamInfoBlock = MA_FALSE; - pInit->hasMetadataBlocks = MA_FALSE; - if (!ma_dr_flac__read_next_flac_frame_header(&pInit->bs, 0, &pInit->firstFrameHeader)) { - return MA_FALSE; - } - if (pInit->firstFrameHeader.bitsPerSample == 0) { - return MA_FALSE; - } - pInit->sampleRate = pInit->firstFrameHeader.sampleRate; - pInit->channels = ma_dr_flac__get_channel_count_from_channel_assignment(pInit->firstFrameHeader.channelAssignment); - pInit->bitsPerSample = pInit->firstFrameHeader.bitsPerSample; - pInit->maxBlockSizeInPCMFrames = 65535; - return MA_TRUE; - } - } else { - ma_dr_flac_streaminfo streaminfo; - if (!ma_dr_flac__read_streaminfo(onRead, pUserData, &streaminfo)) { - return MA_FALSE; - } - pInit->hasStreamInfoBlock = MA_TRUE; - pInit->sampleRate = streaminfo.sampleRate; - pInit->channels = streaminfo.channels; - pInit->bitsPerSample = streaminfo.bitsPerSample; - pInit->totalPCMFrameCount = streaminfo.totalPCMFrameCount; - pInit->maxBlockSizeInPCMFrames = streaminfo.maxBlockSizeInPCMFrames; - pInit->hasMetadataBlocks = !isLastBlock; - if (onMeta) { - ma_dr_flac_metadata metadata; - metadata.type = MA_DR_FLAC_METADATA_BLOCK_TYPE_STREAMINFO; - metadata.pRawData = NULL; - metadata.rawDataSize = 0; - metadata.data.streaminfo = streaminfo; - onMeta(pUserDataMD, &metadata); - } - return MA_TRUE; - } -} -#ifndef MA_DR_FLAC_NO_OGG -#define MA_DR_FLAC_OGG_MAX_PAGE_SIZE 65307 -#define MA_DR_FLAC_OGG_CAPTURE_PATTERN_CRC32 1605413199 -typedef enum -{ - ma_dr_flac_ogg_recover_on_crc_mismatch, - ma_dr_flac_ogg_fail_on_crc_mismatch -} ma_dr_flac_ogg_crc_mismatch_recovery; -#ifndef MA_DR_FLAC_NO_CRC -static ma_uint32 ma_dr_flac__crc32_table[] = { - 0x00000000L, 0x04C11DB7L, 0x09823B6EL, 0x0D4326D9L, - 0x130476DCL, 0x17C56B6BL, 0x1A864DB2L, 0x1E475005L, - 0x2608EDB8L, 0x22C9F00FL, 0x2F8AD6D6L, 0x2B4BCB61L, - 0x350C9B64L, 0x31CD86D3L, 0x3C8EA00AL, 0x384FBDBDL, - 0x4C11DB70L, 0x48D0C6C7L, 0x4593E01EL, 0x4152FDA9L, - 0x5F15ADACL, 0x5BD4B01BL, 0x569796C2L, 0x52568B75L, - 0x6A1936C8L, 0x6ED82B7FL, 0x639B0DA6L, 0x675A1011L, - 0x791D4014L, 0x7DDC5DA3L, 0x709F7B7AL, 0x745E66CDL, - 0x9823B6E0L, 0x9CE2AB57L, 0x91A18D8EL, 0x95609039L, - 0x8B27C03CL, 0x8FE6DD8BL, 0x82A5FB52L, 0x8664E6E5L, - 0xBE2B5B58L, 0xBAEA46EFL, 0xB7A96036L, 0xB3687D81L, - 0xAD2F2D84L, 0xA9EE3033L, 0xA4AD16EAL, 0xA06C0B5DL, - 0xD4326D90L, 0xD0F37027L, 0xDDB056FEL, 0xD9714B49L, - 0xC7361B4CL, 0xC3F706FBL, 0xCEB42022L, 0xCA753D95L, - 0xF23A8028L, 0xF6FB9D9FL, 0xFBB8BB46L, 0xFF79A6F1L, - 0xE13EF6F4L, 0xE5FFEB43L, 0xE8BCCD9AL, 0xEC7DD02DL, - 0x34867077L, 0x30476DC0L, 0x3D044B19L, 0x39C556AEL, - 0x278206ABL, 0x23431B1CL, 0x2E003DC5L, 0x2AC12072L, - 0x128E9DCFL, 0x164F8078L, 0x1B0CA6A1L, 0x1FCDBB16L, - 0x018AEB13L, 0x054BF6A4L, 0x0808D07DL, 0x0CC9CDCAL, - 0x7897AB07L, 0x7C56B6B0L, 0x71159069L, 0x75D48DDEL, - 0x6B93DDDBL, 0x6F52C06CL, 0x6211E6B5L, 0x66D0FB02L, - 0x5E9F46BFL, 0x5A5E5B08L, 0x571D7DD1L, 0x53DC6066L, - 0x4D9B3063L, 0x495A2DD4L, 0x44190B0DL, 0x40D816BAL, - 0xACA5C697L, 0xA864DB20L, 0xA527FDF9L, 0xA1E6E04EL, - 0xBFA1B04BL, 0xBB60ADFCL, 0xB6238B25L, 0xB2E29692L, - 0x8AAD2B2FL, 0x8E6C3698L, 0x832F1041L, 0x87EE0DF6L, - 0x99A95DF3L, 0x9D684044L, 0x902B669DL, 0x94EA7B2AL, - 0xE0B41DE7L, 0xE4750050L, 0xE9362689L, 0xEDF73B3EL, - 0xF3B06B3BL, 0xF771768CL, 0xFA325055L, 0xFEF34DE2L, - 0xC6BCF05FL, 0xC27DEDE8L, 0xCF3ECB31L, 0xCBFFD686L, - 0xD5B88683L, 0xD1799B34L, 0xDC3ABDEDL, 0xD8FBA05AL, - 0x690CE0EEL, 0x6DCDFD59L, 0x608EDB80L, 0x644FC637L, - 0x7A089632L, 0x7EC98B85L, 0x738AAD5CL, 0x774BB0EBL, - 0x4F040D56L, 0x4BC510E1L, 0x46863638L, 0x42472B8FL, - 0x5C007B8AL, 0x58C1663DL, 0x558240E4L, 0x51435D53L, - 0x251D3B9EL, 0x21DC2629L, 0x2C9F00F0L, 0x285E1D47L, - 0x36194D42L, 0x32D850F5L, 0x3F9B762CL, 0x3B5A6B9BL, - 0x0315D626L, 0x07D4CB91L, 0x0A97ED48L, 0x0E56F0FFL, - 0x1011A0FAL, 0x14D0BD4DL, 0x19939B94L, 0x1D528623L, - 0xF12F560EL, 0xF5EE4BB9L, 0xF8AD6D60L, 0xFC6C70D7L, - 0xE22B20D2L, 0xE6EA3D65L, 0xEBA91BBCL, 0xEF68060BL, - 0xD727BBB6L, 0xD3E6A601L, 0xDEA580D8L, 0xDA649D6FL, - 0xC423CD6AL, 0xC0E2D0DDL, 0xCDA1F604L, 0xC960EBB3L, - 0xBD3E8D7EL, 0xB9FF90C9L, 0xB4BCB610L, 0xB07DABA7L, - 0xAE3AFBA2L, 0xAAFBE615L, 0xA7B8C0CCL, 0xA379DD7BL, - 0x9B3660C6L, 0x9FF77D71L, 0x92B45BA8L, 0x9675461FL, - 0x8832161AL, 0x8CF30BADL, 0x81B02D74L, 0x857130C3L, - 0x5D8A9099L, 0x594B8D2EL, 0x5408ABF7L, 0x50C9B640L, - 0x4E8EE645L, 0x4A4FFBF2L, 0x470CDD2BL, 0x43CDC09CL, - 0x7B827D21L, 0x7F436096L, 0x7200464FL, 0x76C15BF8L, - 0x68860BFDL, 0x6C47164AL, 0x61043093L, 0x65C52D24L, - 0x119B4BE9L, 0x155A565EL, 0x18197087L, 0x1CD86D30L, - 0x029F3D35L, 0x065E2082L, 0x0B1D065BL, 0x0FDC1BECL, - 0x3793A651L, 0x3352BBE6L, 0x3E119D3FL, 0x3AD08088L, - 0x2497D08DL, 0x2056CD3AL, 0x2D15EBE3L, 0x29D4F654L, - 0xC5A92679L, 0xC1683BCEL, 0xCC2B1D17L, 0xC8EA00A0L, - 0xD6AD50A5L, 0xD26C4D12L, 0xDF2F6BCBL, 0xDBEE767CL, - 0xE3A1CBC1L, 0xE760D676L, 0xEA23F0AFL, 0xEEE2ED18L, - 0xF0A5BD1DL, 0xF464A0AAL, 0xF9278673L, 0xFDE69BC4L, - 0x89B8FD09L, 0x8D79E0BEL, 0x803AC667L, 0x84FBDBD0L, - 0x9ABC8BD5L, 0x9E7D9662L, 0x933EB0BBL, 0x97FFAD0CL, - 0xAFB010B1L, 0xAB710D06L, 0xA6322BDFL, 0xA2F33668L, - 0xBCB4666DL, 0xB8757BDAL, 0xB5365D03L, 0xB1F740B4L -}; -#endif -static MA_INLINE ma_uint32 ma_dr_flac_crc32_byte(ma_uint32 crc32, ma_uint8 data) -{ -#ifndef MA_DR_FLAC_NO_CRC - return (crc32 << 8) ^ ma_dr_flac__crc32_table[(ma_uint8)((crc32 >> 24) & 0xFF) ^ data]; -#else - (void)data; - return crc32; -#endif -} -#if 0 -static MA_INLINE ma_uint32 ma_dr_flac_crc32_uint32(ma_uint32 crc32, ma_uint32 data) -{ - crc32 = ma_dr_flac_crc32_byte(crc32, (ma_uint8)((data >> 24) & 0xFF)); - crc32 = ma_dr_flac_crc32_byte(crc32, (ma_uint8)((data >> 16) & 0xFF)); - crc32 = ma_dr_flac_crc32_byte(crc32, (ma_uint8)((data >> 8) & 0xFF)); - crc32 = ma_dr_flac_crc32_byte(crc32, (ma_uint8)((data >> 0) & 0xFF)); - return crc32; -} -static MA_INLINE ma_uint32 ma_dr_flac_crc32_uint64(ma_uint32 crc32, ma_uint64 data) -{ - crc32 = ma_dr_flac_crc32_uint32(crc32, (ma_uint32)((data >> 32) & 0xFFFFFFFF)); - crc32 = ma_dr_flac_crc32_uint32(crc32, (ma_uint32)((data >> 0) & 0xFFFFFFFF)); - return crc32; -} -#endif -static MA_INLINE ma_uint32 ma_dr_flac_crc32_buffer(ma_uint32 crc32, ma_uint8* pData, ma_uint32 dataSize) -{ - ma_uint32 i; - for (i = 0; i < dataSize; ++i) { - crc32 = ma_dr_flac_crc32_byte(crc32, pData[i]); - } - return crc32; -} -static MA_INLINE ma_bool32 ma_dr_flac_ogg__is_capture_pattern(ma_uint8 pattern[4]) -{ - return pattern[0] == 'O' && pattern[1] == 'g' && pattern[2] == 'g' && pattern[3] == 'S'; -} -static MA_INLINE ma_uint32 ma_dr_flac_ogg__get_page_header_size(ma_dr_flac_ogg_page_header* pHeader) -{ - return 27 + pHeader->segmentCount; -} -static MA_INLINE ma_uint32 ma_dr_flac_ogg__get_page_body_size(ma_dr_flac_ogg_page_header* pHeader) -{ - ma_uint32 pageBodySize = 0; - int i; - for (i = 0; i < pHeader->segmentCount; ++i) { - pageBodySize += pHeader->segmentTable[i]; - } - return pageBodySize; -} -static ma_result ma_dr_flac_ogg__read_page_header_after_capture_pattern(ma_dr_flac_read_proc onRead, void* pUserData, ma_dr_flac_ogg_page_header* pHeader, ma_uint32* pBytesRead, ma_uint32* pCRC32) -{ - ma_uint8 data[23]; - ma_uint32 i; - MA_DR_FLAC_ASSERT(*pCRC32 == MA_DR_FLAC_OGG_CAPTURE_PATTERN_CRC32); - if (onRead(pUserData, data, 23) != 23) { - return MA_AT_END; - } - *pBytesRead += 23; - pHeader->capturePattern[0] = 'O'; - pHeader->capturePattern[1] = 'g'; - pHeader->capturePattern[2] = 'g'; - pHeader->capturePattern[3] = 'S'; - pHeader->structureVersion = data[0]; - pHeader->headerType = data[1]; - MA_DR_FLAC_COPY_MEMORY(&pHeader->granulePosition, &data[ 2], 8); - MA_DR_FLAC_COPY_MEMORY(&pHeader->serialNumber, &data[10], 4); - MA_DR_FLAC_COPY_MEMORY(&pHeader->sequenceNumber, &data[14], 4); - MA_DR_FLAC_COPY_MEMORY(&pHeader->checksum, &data[18], 4); - pHeader->segmentCount = data[22]; - data[18] = 0; - data[19] = 0; - data[20] = 0; - data[21] = 0; - for (i = 0; i < 23; ++i) { - *pCRC32 = ma_dr_flac_crc32_byte(*pCRC32, data[i]); - } - if (onRead(pUserData, pHeader->segmentTable, pHeader->segmentCount) != pHeader->segmentCount) { - return MA_AT_END; - } - *pBytesRead += pHeader->segmentCount; - for (i = 0; i < pHeader->segmentCount; ++i) { - *pCRC32 = ma_dr_flac_crc32_byte(*pCRC32, pHeader->segmentTable[i]); - } - return MA_SUCCESS; -} -static ma_result ma_dr_flac_ogg__read_page_header(ma_dr_flac_read_proc onRead, void* pUserData, ma_dr_flac_ogg_page_header* pHeader, ma_uint32* pBytesRead, ma_uint32* pCRC32) -{ - ma_uint8 id[4]; - *pBytesRead = 0; - if (onRead(pUserData, id, 4) != 4) { - return MA_AT_END; - } - *pBytesRead += 4; - for (;;) { - if (ma_dr_flac_ogg__is_capture_pattern(id)) { - ma_result result; - *pCRC32 = MA_DR_FLAC_OGG_CAPTURE_PATTERN_CRC32; - result = ma_dr_flac_ogg__read_page_header_after_capture_pattern(onRead, pUserData, pHeader, pBytesRead, pCRC32); - if (result == MA_SUCCESS) { - return MA_SUCCESS; - } else { - if (result == MA_CRC_MISMATCH) { - continue; - } else { - return result; - } - } - } else { - id[0] = id[1]; - id[1] = id[2]; - id[2] = id[3]; - if (onRead(pUserData, &id[3], 1) != 1) { - return MA_AT_END; - } - *pBytesRead += 1; - } - } -} -typedef struct -{ - ma_dr_flac_read_proc onRead; - ma_dr_flac_seek_proc onSeek; - void* pUserData; - ma_uint64 currentBytePos; - ma_uint64 firstBytePos; - ma_uint32 serialNumber; - ma_dr_flac_ogg_page_header bosPageHeader; - ma_dr_flac_ogg_page_header currentPageHeader; - ma_uint32 bytesRemainingInPage; - ma_uint32 pageDataSize; - ma_uint8 pageData[MA_DR_FLAC_OGG_MAX_PAGE_SIZE]; -} ma_dr_flac_oggbs; -static size_t ma_dr_flac_oggbs__read_physical(ma_dr_flac_oggbs* oggbs, void* bufferOut, size_t bytesToRead) -{ - size_t bytesActuallyRead = oggbs->onRead(oggbs->pUserData, bufferOut, bytesToRead); - oggbs->currentBytePos += bytesActuallyRead; - return bytesActuallyRead; -} -static ma_bool32 ma_dr_flac_oggbs__seek_physical(ma_dr_flac_oggbs* oggbs, ma_uint64 offset, ma_dr_flac_seek_origin origin) -{ - if (origin == ma_dr_flac_seek_origin_start) { - if (offset <= 0x7FFFFFFF) { - if (!oggbs->onSeek(oggbs->pUserData, (int)offset, ma_dr_flac_seek_origin_start)) { - return MA_FALSE; - } - oggbs->currentBytePos = offset; - return MA_TRUE; - } else { - if (!oggbs->onSeek(oggbs->pUserData, 0x7FFFFFFF, ma_dr_flac_seek_origin_start)) { - return MA_FALSE; - } - oggbs->currentBytePos = offset; - return ma_dr_flac_oggbs__seek_physical(oggbs, offset - 0x7FFFFFFF, ma_dr_flac_seek_origin_current); - } - } else { - while (offset > 0x7FFFFFFF) { - if (!oggbs->onSeek(oggbs->pUserData, 0x7FFFFFFF, ma_dr_flac_seek_origin_current)) { - return MA_FALSE; - } - oggbs->currentBytePos += 0x7FFFFFFF; - offset -= 0x7FFFFFFF; - } - if (!oggbs->onSeek(oggbs->pUserData, (int)offset, ma_dr_flac_seek_origin_current)) { - return MA_FALSE; - } - oggbs->currentBytePos += offset; - return MA_TRUE; - } -} -static ma_bool32 ma_dr_flac_oggbs__goto_next_page(ma_dr_flac_oggbs* oggbs, ma_dr_flac_ogg_crc_mismatch_recovery recoveryMethod) -{ - ma_dr_flac_ogg_page_header header; - for (;;) { - ma_uint32 crc32 = 0; - ma_uint32 bytesRead; - ma_uint32 pageBodySize; -#ifndef MA_DR_FLAC_NO_CRC - ma_uint32 actualCRC32; -#endif - if (ma_dr_flac_ogg__read_page_header(oggbs->onRead, oggbs->pUserData, &header, &bytesRead, &crc32) != MA_SUCCESS) { - return MA_FALSE; - } - oggbs->currentBytePos += bytesRead; - pageBodySize = ma_dr_flac_ogg__get_page_body_size(&header); - if (pageBodySize > MA_DR_FLAC_OGG_MAX_PAGE_SIZE) { - continue; - } - if (header.serialNumber != oggbs->serialNumber) { - if (pageBodySize > 0 && !ma_dr_flac_oggbs__seek_physical(oggbs, pageBodySize, ma_dr_flac_seek_origin_current)) { - return MA_FALSE; - } - continue; - } - if (ma_dr_flac_oggbs__read_physical(oggbs, oggbs->pageData, pageBodySize) != pageBodySize) { - return MA_FALSE; - } - oggbs->pageDataSize = pageBodySize; -#ifndef MA_DR_FLAC_NO_CRC - actualCRC32 = ma_dr_flac_crc32_buffer(crc32, oggbs->pageData, oggbs->pageDataSize); - if (actualCRC32 != header.checksum) { - if (recoveryMethod == ma_dr_flac_ogg_recover_on_crc_mismatch) { - continue; - } else { - ma_dr_flac_oggbs__goto_next_page(oggbs, ma_dr_flac_ogg_recover_on_crc_mismatch); - return MA_FALSE; - } - } -#else - (void)recoveryMethod; -#endif - oggbs->currentPageHeader = header; - oggbs->bytesRemainingInPage = pageBodySize; - return MA_TRUE; - } -} -#if 0 -static ma_uint8 ma_dr_flac_oggbs__get_current_segment_index(ma_dr_flac_oggbs* oggbs, ma_uint8* pBytesRemainingInSeg) -{ - ma_uint32 bytesConsumedInPage = ma_dr_flac_ogg__get_page_body_size(&oggbs->currentPageHeader) - oggbs->bytesRemainingInPage; - ma_uint8 iSeg = 0; - ma_uint32 iByte = 0; - while (iByte < bytesConsumedInPage) { - ma_uint8 segmentSize = oggbs->currentPageHeader.segmentTable[iSeg]; - if (iByte + segmentSize > bytesConsumedInPage) { - break; - } else { - iSeg += 1; - iByte += segmentSize; - } - } - *pBytesRemainingInSeg = oggbs->currentPageHeader.segmentTable[iSeg] - (ma_uint8)(bytesConsumedInPage - iByte); - return iSeg; -} -static ma_bool32 ma_dr_flac_oggbs__seek_to_next_packet(ma_dr_flac_oggbs* oggbs) -{ - for (;;) { - ma_bool32 atEndOfPage = MA_FALSE; - ma_uint8 bytesRemainingInSeg; - ma_uint8 iFirstSeg = ma_dr_flac_oggbs__get_current_segment_index(oggbs, &bytesRemainingInSeg); - ma_uint32 bytesToEndOfPacketOrPage = bytesRemainingInSeg; - for (ma_uint8 iSeg = iFirstSeg; iSeg < oggbs->currentPageHeader.segmentCount; ++iSeg) { - ma_uint8 segmentSize = oggbs->currentPageHeader.segmentTable[iSeg]; - if (segmentSize < 255) { - if (iSeg == oggbs->currentPageHeader.segmentCount-1) { - atEndOfPage = MA_TRUE; - } - break; - } - bytesToEndOfPacketOrPage += segmentSize; - } - ma_dr_flac_oggbs__seek_physical(oggbs, bytesToEndOfPacketOrPage, ma_dr_flac_seek_origin_current); - oggbs->bytesRemainingInPage -= bytesToEndOfPacketOrPage; - if (atEndOfPage) { - if (!ma_dr_flac_oggbs__goto_next_page(oggbs)) { - return MA_FALSE; - } - if ((oggbs->currentPageHeader.headerType & 0x01) == 0) { - return MA_TRUE; - } - } else { - return MA_TRUE; - } - } -} -static ma_bool32 ma_dr_flac_oggbs__seek_to_next_frame(ma_dr_flac_oggbs* oggbs) -{ - return ma_dr_flac_oggbs__seek_to_next_packet(oggbs); -} -#endif -static size_t ma_dr_flac__on_read_ogg(void* pUserData, void* bufferOut, size_t bytesToRead) -{ - ma_dr_flac_oggbs* oggbs = (ma_dr_flac_oggbs*)pUserData; - ma_uint8* pRunningBufferOut = (ma_uint8*)bufferOut; - size_t bytesRead = 0; - MA_DR_FLAC_ASSERT(oggbs != NULL); - MA_DR_FLAC_ASSERT(pRunningBufferOut != NULL); - while (bytesRead < bytesToRead) { - size_t bytesRemainingToRead = bytesToRead - bytesRead; - if (oggbs->bytesRemainingInPage >= bytesRemainingToRead) { - MA_DR_FLAC_COPY_MEMORY(pRunningBufferOut, oggbs->pageData + (oggbs->pageDataSize - oggbs->bytesRemainingInPage), bytesRemainingToRead); - bytesRead += bytesRemainingToRead; - oggbs->bytesRemainingInPage -= (ma_uint32)bytesRemainingToRead; - break; - } - if (oggbs->bytesRemainingInPage > 0) { - MA_DR_FLAC_COPY_MEMORY(pRunningBufferOut, oggbs->pageData + (oggbs->pageDataSize - oggbs->bytesRemainingInPage), oggbs->bytesRemainingInPage); - bytesRead += oggbs->bytesRemainingInPage; - pRunningBufferOut += oggbs->bytesRemainingInPage; - oggbs->bytesRemainingInPage = 0; - } - MA_DR_FLAC_ASSERT(bytesRemainingToRead > 0); - if (!ma_dr_flac_oggbs__goto_next_page(oggbs, ma_dr_flac_ogg_recover_on_crc_mismatch)) { - break; - } - } - return bytesRead; -} -static ma_bool32 ma_dr_flac__on_seek_ogg(void* pUserData, int offset, ma_dr_flac_seek_origin origin) -{ - ma_dr_flac_oggbs* oggbs = (ma_dr_flac_oggbs*)pUserData; - int bytesSeeked = 0; - MA_DR_FLAC_ASSERT(oggbs != NULL); - MA_DR_FLAC_ASSERT(offset >= 0); - if (origin == ma_dr_flac_seek_origin_start) { - if (!ma_dr_flac_oggbs__seek_physical(oggbs, (int)oggbs->firstBytePos, ma_dr_flac_seek_origin_start)) { - return MA_FALSE; - } - if (!ma_dr_flac_oggbs__goto_next_page(oggbs, ma_dr_flac_ogg_fail_on_crc_mismatch)) { - return MA_FALSE; - } - return ma_dr_flac__on_seek_ogg(pUserData, offset, ma_dr_flac_seek_origin_current); - } - MA_DR_FLAC_ASSERT(origin == ma_dr_flac_seek_origin_current); - while (bytesSeeked < offset) { - int bytesRemainingToSeek = offset - bytesSeeked; - MA_DR_FLAC_ASSERT(bytesRemainingToSeek >= 0); - if (oggbs->bytesRemainingInPage >= (size_t)bytesRemainingToSeek) { - bytesSeeked += bytesRemainingToSeek; - (void)bytesSeeked; - oggbs->bytesRemainingInPage -= bytesRemainingToSeek; - break; - } - if (oggbs->bytesRemainingInPage > 0) { - bytesSeeked += (int)oggbs->bytesRemainingInPage; - oggbs->bytesRemainingInPage = 0; - } - MA_DR_FLAC_ASSERT(bytesRemainingToSeek > 0); - if (!ma_dr_flac_oggbs__goto_next_page(oggbs, ma_dr_flac_ogg_fail_on_crc_mismatch)) { - return MA_FALSE; - } - } - return MA_TRUE; -} -static ma_bool32 ma_dr_flac_ogg__seek_to_pcm_frame(ma_dr_flac* pFlac, ma_uint64 pcmFrameIndex) -{ - ma_dr_flac_oggbs* oggbs = (ma_dr_flac_oggbs*)pFlac->_oggbs; - ma_uint64 originalBytePos; - ma_uint64 runningGranulePosition; - ma_uint64 runningFrameBytePos; - ma_uint64 runningPCMFrameCount; - MA_DR_FLAC_ASSERT(oggbs != NULL); - originalBytePos = oggbs->currentBytePos; - if (!ma_dr_flac__seek_to_byte(&pFlac->bs, pFlac->firstFLACFramePosInBytes)) { - return MA_FALSE; - } - oggbs->bytesRemainingInPage = 0; - runningGranulePosition = 0; - for (;;) { - if (!ma_dr_flac_oggbs__goto_next_page(oggbs, ma_dr_flac_ogg_recover_on_crc_mismatch)) { - ma_dr_flac_oggbs__seek_physical(oggbs, originalBytePos, ma_dr_flac_seek_origin_start); - return MA_FALSE; - } - runningFrameBytePos = oggbs->currentBytePos - ma_dr_flac_ogg__get_page_header_size(&oggbs->currentPageHeader) - oggbs->pageDataSize; - if (oggbs->currentPageHeader.granulePosition >= pcmFrameIndex) { - break; - } - if ((oggbs->currentPageHeader.headerType & 0x01) == 0) { - if (oggbs->currentPageHeader.segmentTable[0] >= 2) { - ma_uint8 firstBytesInPage[2]; - firstBytesInPage[0] = oggbs->pageData[0]; - firstBytesInPage[1] = oggbs->pageData[1]; - if ((firstBytesInPage[0] == 0xFF) && (firstBytesInPage[1] & 0xFC) == 0xF8) { - runningGranulePosition = oggbs->currentPageHeader.granulePosition; - } - continue; - } - } - } - if (!ma_dr_flac_oggbs__seek_physical(oggbs, runningFrameBytePos, ma_dr_flac_seek_origin_start)) { - return MA_FALSE; - } - if (!ma_dr_flac_oggbs__goto_next_page(oggbs, ma_dr_flac_ogg_recover_on_crc_mismatch)) { - return MA_FALSE; - } - runningPCMFrameCount = runningGranulePosition; - for (;;) { - ma_uint64 firstPCMFrameInFLACFrame = 0; - ma_uint64 lastPCMFrameInFLACFrame = 0; - ma_uint64 pcmFrameCountInThisFrame; - if (!ma_dr_flac__read_next_flac_frame_header(&pFlac->bs, pFlac->bitsPerSample, &pFlac->currentFLACFrame.header)) { - return MA_FALSE; - } - ma_dr_flac__get_pcm_frame_range_of_current_flac_frame(pFlac, &firstPCMFrameInFLACFrame, &lastPCMFrameInFLACFrame); - pcmFrameCountInThisFrame = (lastPCMFrameInFLACFrame - firstPCMFrameInFLACFrame) + 1; - if (pcmFrameIndex == pFlac->totalPCMFrameCount && (runningPCMFrameCount + pcmFrameCountInThisFrame) == pFlac->totalPCMFrameCount) { - ma_result result = ma_dr_flac__decode_flac_frame(pFlac); - if (result == MA_SUCCESS) { - pFlac->currentPCMFrame = pcmFrameIndex; - pFlac->currentFLACFrame.pcmFramesRemaining = 0; - return MA_TRUE; - } else { - return MA_FALSE; - } - } - if (pcmFrameIndex < (runningPCMFrameCount + pcmFrameCountInThisFrame)) { - ma_result result = ma_dr_flac__decode_flac_frame(pFlac); - if (result == MA_SUCCESS) { - ma_uint64 pcmFramesToDecode = (size_t)(pcmFrameIndex - runningPCMFrameCount); - if (pcmFramesToDecode == 0) { - return MA_TRUE; - } - pFlac->currentPCMFrame = runningPCMFrameCount; - return ma_dr_flac__seek_forward_by_pcm_frames(pFlac, pcmFramesToDecode) == pcmFramesToDecode; - } else { - if (result == MA_CRC_MISMATCH) { - continue; - } else { - return MA_FALSE; - } - } - } else { - ma_result result = ma_dr_flac__seek_to_next_flac_frame(pFlac); - if (result == MA_SUCCESS) { - runningPCMFrameCount += pcmFrameCountInThisFrame; - } else { - if (result == MA_CRC_MISMATCH) { - continue; - } else { - return MA_FALSE; - } - } - } - } -} -static ma_bool32 ma_dr_flac__init_private__ogg(ma_dr_flac_init_info* pInit, ma_dr_flac_read_proc onRead, ma_dr_flac_seek_proc onSeek, ma_dr_flac_meta_proc onMeta, void* pUserData, void* pUserDataMD, ma_bool32 relaxed) -{ - ma_dr_flac_ogg_page_header header; - ma_uint32 crc32 = MA_DR_FLAC_OGG_CAPTURE_PATTERN_CRC32; - ma_uint32 bytesRead = 0; - (void)relaxed; - pInit->container = ma_dr_flac_container_ogg; - pInit->oggFirstBytePos = 0; - if (ma_dr_flac_ogg__read_page_header_after_capture_pattern(onRead, pUserData, &header, &bytesRead, &crc32) != MA_SUCCESS) { - return MA_FALSE; - } - pInit->runningFilePos += bytesRead; - for (;;) { - int pageBodySize; - if ((header.headerType & 0x02) == 0) { - return MA_FALSE; - } - pageBodySize = ma_dr_flac_ogg__get_page_body_size(&header); - if (pageBodySize == 51) { - ma_uint32 bytesRemainingInPage = pageBodySize; - ma_uint8 packetType; - if (onRead(pUserData, &packetType, 1) != 1) { - return MA_FALSE; - } - bytesRemainingInPage -= 1; - if (packetType == 0x7F) { - ma_uint8 sig[4]; - if (onRead(pUserData, sig, 4) != 4) { - return MA_FALSE; - } - bytesRemainingInPage -= 4; - if (sig[0] == 'F' && sig[1] == 'L' && sig[2] == 'A' && sig[3] == 'C') { - ma_uint8 mappingVersion[2]; - if (onRead(pUserData, mappingVersion, 2) != 2) { - return MA_FALSE; - } - if (mappingVersion[0] != 1) { - return MA_FALSE; - } - if (!onSeek(pUserData, 2, ma_dr_flac_seek_origin_current)) { - return MA_FALSE; - } - if (onRead(pUserData, sig, 4) != 4) { - return MA_FALSE; - } - if (sig[0] == 'f' && sig[1] == 'L' && sig[2] == 'a' && sig[3] == 'C') { - ma_dr_flac_streaminfo streaminfo; - ma_uint8 isLastBlock; - ma_uint8 blockType; - ma_uint32 blockSize; - if (!ma_dr_flac__read_and_decode_block_header(onRead, pUserData, &isLastBlock, &blockType, &blockSize)) { - return MA_FALSE; - } - if (blockType != MA_DR_FLAC_METADATA_BLOCK_TYPE_STREAMINFO || blockSize != 34) { - return MA_FALSE; - } - if (ma_dr_flac__read_streaminfo(onRead, pUserData, &streaminfo)) { - pInit->hasStreamInfoBlock = MA_TRUE; - pInit->sampleRate = streaminfo.sampleRate; - pInit->channels = streaminfo.channels; - pInit->bitsPerSample = streaminfo.bitsPerSample; - pInit->totalPCMFrameCount = streaminfo.totalPCMFrameCount; - pInit->maxBlockSizeInPCMFrames = streaminfo.maxBlockSizeInPCMFrames; - pInit->hasMetadataBlocks = !isLastBlock; - if (onMeta) { - ma_dr_flac_metadata metadata; - metadata.type = MA_DR_FLAC_METADATA_BLOCK_TYPE_STREAMINFO; - metadata.pRawData = NULL; - metadata.rawDataSize = 0; - metadata.data.streaminfo = streaminfo; - onMeta(pUserDataMD, &metadata); - } - pInit->runningFilePos += pageBodySize; - pInit->oggFirstBytePos = pInit->runningFilePos - 79; - pInit->oggSerial = header.serialNumber; - pInit->oggBosHeader = header; - break; - } else { - return MA_FALSE; - } - } else { - return MA_FALSE; - } - } else { - if (!onSeek(pUserData, bytesRemainingInPage, ma_dr_flac_seek_origin_current)) { - return MA_FALSE; - } - } - } else { - if (!onSeek(pUserData, bytesRemainingInPage, ma_dr_flac_seek_origin_current)) { - return MA_FALSE; - } - } - } else { - if (!onSeek(pUserData, pageBodySize, ma_dr_flac_seek_origin_current)) { - return MA_FALSE; - } - } - pInit->runningFilePos += pageBodySize; - if (ma_dr_flac_ogg__read_page_header(onRead, pUserData, &header, &bytesRead, &crc32) != MA_SUCCESS) { - return MA_FALSE; - } - pInit->runningFilePos += bytesRead; - } - pInit->hasMetadataBlocks = MA_TRUE; - return MA_TRUE; -} -#endif -static ma_bool32 ma_dr_flac__init_private(ma_dr_flac_init_info* pInit, ma_dr_flac_read_proc onRead, ma_dr_flac_seek_proc onSeek, ma_dr_flac_meta_proc onMeta, ma_dr_flac_container container, void* pUserData, void* pUserDataMD) -{ - ma_bool32 relaxed; - ma_uint8 id[4]; - if (pInit == NULL || onRead == NULL || onSeek == NULL) { - return MA_FALSE; - } - MA_DR_FLAC_ZERO_MEMORY(pInit, sizeof(*pInit)); - pInit->onRead = onRead; - pInit->onSeek = onSeek; - pInit->onMeta = onMeta; - pInit->container = container; - pInit->pUserData = pUserData; - pInit->pUserDataMD = pUserDataMD; - pInit->bs.onRead = onRead; - pInit->bs.onSeek = onSeek; - pInit->bs.pUserData = pUserData; - ma_dr_flac__reset_cache(&pInit->bs); - relaxed = container != ma_dr_flac_container_unknown; - for (;;) { - if (onRead(pUserData, id, 4) != 4) { - return MA_FALSE; - } - pInit->runningFilePos += 4; - if (id[0] == 'I' && id[1] == 'D' && id[2] == '3') { - ma_uint8 header[6]; - ma_uint8 flags; - ma_uint32 headerSize; - if (onRead(pUserData, header, 6) != 6) { - return MA_FALSE; - } - pInit->runningFilePos += 6; - flags = header[1]; - MA_DR_FLAC_COPY_MEMORY(&headerSize, header+2, 4); - headerSize = ma_dr_flac__unsynchsafe_32(ma_dr_flac__be2host_32(headerSize)); - if (flags & 0x10) { - headerSize += 10; - } - if (!onSeek(pUserData, headerSize, ma_dr_flac_seek_origin_current)) { - return MA_FALSE; - } - pInit->runningFilePos += headerSize; - } else { - break; - } - } - if (id[0] == 'f' && id[1] == 'L' && id[2] == 'a' && id[3] == 'C') { - return ma_dr_flac__init_private__native(pInit, onRead, onSeek, onMeta, pUserData, pUserDataMD, relaxed); - } -#ifndef MA_DR_FLAC_NO_OGG - if (id[0] == 'O' && id[1] == 'g' && id[2] == 'g' && id[3] == 'S') { - return ma_dr_flac__init_private__ogg(pInit, onRead, onSeek, onMeta, pUserData, pUserDataMD, relaxed); - } -#endif - if (relaxed) { - if (container == ma_dr_flac_container_native) { - return ma_dr_flac__init_private__native(pInit, onRead, onSeek, onMeta, pUserData, pUserDataMD, relaxed); - } -#ifndef MA_DR_FLAC_NO_OGG - if (container == ma_dr_flac_container_ogg) { - return ma_dr_flac__init_private__ogg(pInit, onRead, onSeek, onMeta, pUserData, pUserDataMD, relaxed); - } -#endif - } - return MA_FALSE; -} -static void ma_dr_flac__init_from_info(ma_dr_flac* pFlac, const ma_dr_flac_init_info* pInit) -{ - MA_DR_FLAC_ASSERT(pFlac != NULL); - MA_DR_FLAC_ASSERT(pInit != NULL); - MA_DR_FLAC_ZERO_MEMORY(pFlac, sizeof(*pFlac)); - pFlac->bs = pInit->bs; - pFlac->onMeta = pInit->onMeta; - pFlac->pUserDataMD = pInit->pUserDataMD; - pFlac->maxBlockSizeInPCMFrames = pInit->maxBlockSizeInPCMFrames; - pFlac->sampleRate = pInit->sampleRate; - pFlac->channels = (ma_uint8)pInit->channels; - pFlac->bitsPerSample = (ma_uint8)pInit->bitsPerSample; - pFlac->totalPCMFrameCount = pInit->totalPCMFrameCount; - pFlac->container = pInit->container; -} -static ma_dr_flac* ma_dr_flac_open_with_metadata_private(ma_dr_flac_read_proc onRead, ma_dr_flac_seek_proc onSeek, ma_dr_flac_meta_proc onMeta, ma_dr_flac_container container, void* pUserData, void* pUserDataMD, const ma_allocation_callbacks* pAllocationCallbacks) -{ - ma_dr_flac_init_info init; - ma_uint32 allocationSize; - ma_uint32 wholeSIMDVectorCountPerChannel; - ma_uint32 decodedSamplesAllocationSize; -#ifndef MA_DR_FLAC_NO_OGG - ma_dr_flac_oggbs* pOggbs = NULL; -#endif - ma_uint64 firstFramePos; - ma_uint64 seektablePos; - ma_uint32 seekpointCount; - ma_allocation_callbacks allocationCallbacks; - ma_dr_flac* pFlac; - ma_dr_flac__init_cpu_caps(); - if (!ma_dr_flac__init_private(&init, onRead, onSeek, onMeta, container, pUserData, pUserDataMD)) { - return NULL; - } - if (pAllocationCallbacks != NULL) { - allocationCallbacks = *pAllocationCallbacks; - if (allocationCallbacks.onFree == NULL || (allocationCallbacks.onMalloc == NULL && allocationCallbacks.onRealloc == NULL)) { - return NULL; - } - } else { - allocationCallbacks.pUserData = NULL; - allocationCallbacks.onMalloc = ma_dr_flac__malloc_default; - allocationCallbacks.onRealloc = ma_dr_flac__realloc_default; - allocationCallbacks.onFree = ma_dr_flac__free_default; - } - allocationSize = sizeof(ma_dr_flac); - if ((init.maxBlockSizeInPCMFrames % (MA_DR_FLAC_MAX_SIMD_VECTOR_SIZE / sizeof(ma_int32))) == 0) { - wholeSIMDVectorCountPerChannel = (init.maxBlockSizeInPCMFrames / (MA_DR_FLAC_MAX_SIMD_VECTOR_SIZE / sizeof(ma_int32))); - } else { - wholeSIMDVectorCountPerChannel = (init.maxBlockSizeInPCMFrames / (MA_DR_FLAC_MAX_SIMD_VECTOR_SIZE / sizeof(ma_int32))) + 1; - } - decodedSamplesAllocationSize = wholeSIMDVectorCountPerChannel * MA_DR_FLAC_MAX_SIMD_VECTOR_SIZE * init.channels; - allocationSize += decodedSamplesAllocationSize; - allocationSize += MA_DR_FLAC_MAX_SIMD_VECTOR_SIZE; -#ifndef MA_DR_FLAC_NO_OGG - if (init.container == ma_dr_flac_container_ogg) { - allocationSize += sizeof(ma_dr_flac_oggbs); - pOggbs = (ma_dr_flac_oggbs*)ma_dr_flac__malloc_from_callbacks(sizeof(*pOggbs), &allocationCallbacks); - if (pOggbs == NULL) { - return NULL; - } - MA_DR_FLAC_ZERO_MEMORY(pOggbs, sizeof(*pOggbs)); - pOggbs->onRead = onRead; - pOggbs->onSeek = onSeek; - pOggbs->pUserData = pUserData; - pOggbs->currentBytePos = init.oggFirstBytePos; - pOggbs->firstBytePos = init.oggFirstBytePos; - pOggbs->serialNumber = init.oggSerial; - pOggbs->bosPageHeader = init.oggBosHeader; - pOggbs->bytesRemainingInPage = 0; - } -#endif - firstFramePos = 42; - seektablePos = 0; - seekpointCount = 0; - if (init.hasMetadataBlocks) { - ma_dr_flac_read_proc onReadOverride = onRead; - ma_dr_flac_seek_proc onSeekOverride = onSeek; - void* pUserDataOverride = pUserData; -#ifndef MA_DR_FLAC_NO_OGG - if (init.container == ma_dr_flac_container_ogg) { - onReadOverride = ma_dr_flac__on_read_ogg; - onSeekOverride = ma_dr_flac__on_seek_ogg; - pUserDataOverride = (void*)pOggbs; - } -#endif - if (!ma_dr_flac__read_and_decode_metadata(onReadOverride, onSeekOverride, onMeta, pUserDataOverride, pUserDataMD, &firstFramePos, &seektablePos, &seekpointCount, &allocationCallbacks)) { - #ifndef MA_DR_FLAC_NO_OGG - ma_dr_flac__free_from_callbacks(pOggbs, &allocationCallbacks); - #endif - return NULL; - } - allocationSize += seekpointCount * sizeof(ma_dr_flac_seekpoint); - } - pFlac = (ma_dr_flac*)ma_dr_flac__malloc_from_callbacks(allocationSize, &allocationCallbacks); - if (pFlac == NULL) { - #ifndef MA_DR_FLAC_NO_OGG - ma_dr_flac__free_from_callbacks(pOggbs, &allocationCallbacks); - #endif - return NULL; - } - ma_dr_flac__init_from_info(pFlac, &init); - pFlac->allocationCallbacks = allocationCallbacks; - pFlac->pDecodedSamples = (ma_int32*)ma_dr_flac_align((size_t)pFlac->pExtraData, MA_DR_FLAC_MAX_SIMD_VECTOR_SIZE); -#ifndef MA_DR_FLAC_NO_OGG - if (init.container == ma_dr_flac_container_ogg) { - ma_dr_flac_oggbs* pInternalOggbs = (ma_dr_flac_oggbs*)((ma_uint8*)pFlac->pDecodedSamples + decodedSamplesAllocationSize + (seekpointCount * sizeof(ma_dr_flac_seekpoint))); - MA_DR_FLAC_COPY_MEMORY(pInternalOggbs, pOggbs, sizeof(*pOggbs)); - ma_dr_flac__free_from_callbacks(pOggbs, &allocationCallbacks); - pOggbs = NULL; - pFlac->bs.onRead = ma_dr_flac__on_read_ogg; - pFlac->bs.onSeek = ma_dr_flac__on_seek_ogg; - pFlac->bs.pUserData = (void*)pInternalOggbs; - pFlac->_oggbs = (void*)pInternalOggbs; - } -#endif - pFlac->firstFLACFramePosInBytes = firstFramePos; -#ifndef MA_DR_FLAC_NO_OGG - if (init.container == ma_dr_flac_container_ogg) - { - pFlac->pSeekpoints = NULL; - pFlac->seekpointCount = 0; - } - else -#endif - { - if (seektablePos != 0) { - pFlac->seekpointCount = seekpointCount; - pFlac->pSeekpoints = (ma_dr_flac_seekpoint*)((ma_uint8*)pFlac->pDecodedSamples + decodedSamplesAllocationSize); - MA_DR_FLAC_ASSERT(pFlac->bs.onSeek != NULL); - MA_DR_FLAC_ASSERT(pFlac->bs.onRead != NULL); - if (pFlac->bs.onSeek(pFlac->bs.pUserData, (int)seektablePos, ma_dr_flac_seek_origin_start)) { - ma_uint32 iSeekpoint; - for (iSeekpoint = 0; iSeekpoint < seekpointCount; iSeekpoint += 1) { - if (pFlac->bs.onRead(pFlac->bs.pUserData, pFlac->pSeekpoints + iSeekpoint, MA_DR_FLAC_SEEKPOINT_SIZE_IN_BYTES) == MA_DR_FLAC_SEEKPOINT_SIZE_IN_BYTES) { - pFlac->pSeekpoints[iSeekpoint].firstPCMFrame = ma_dr_flac__be2host_64(pFlac->pSeekpoints[iSeekpoint].firstPCMFrame); - pFlac->pSeekpoints[iSeekpoint].flacFrameOffset = ma_dr_flac__be2host_64(pFlac->pSeekpoints[iSeekpoint].flacFrameOffset); - pFlac->pSeekpoints[iSeekpoint].pcmFrameCount = ma_dr_flac__be2host_16(pFlac->pSeekpoints[iSeekpoint].pcmFrameCount); - } else { - pFlac->pSeekpoints = NULL; - pFlac->seekpointCount = 0; - break; - } - } - if (!pFlac->bs.onSeek(pFlac->bs.pUserData, (int)pFlac->firstFLACFramePosInBytes, ma_dr_flac_seek_origin_start)) { - ma_dr_flac__free_from_callbacks(pFlac, &allocationCallbacks); - return NULL; - } - } else { - pFlac->pSeekpoints = NULL; - pFlac->seekpointCount = 0; - } - } - } - if (!init.hasStreamInfoBlock) { - pFlac->currentFLACFrame.header = init.firstFrameHeader; - for (;;) { - ma_result result = ma_dr_flac__decode_flac_frame(pFlac); - if (result == MA_SUCCESS) { - break; - } else { - if (result == MA_CRC_MISMATCH) { - if (!ma_dr_flac__read_next_flac_frame_header(&pFlac->bs, pFlac->bitsPerSample, &pFlac->currentFLACFrame.header)) { - ma_dr_flac__free_from_callbacks(pFlac, &allocationCallbacks); - return NULL; - } - continue; - } else { - ma_dr_flac__free_from_callbacks(pFlac, &allocationCallbacks); - return NULL; - } - } - } - } - return pFlac; -} -#ifndef MA_DR_FLAC_NO_STDIO -#include -#ifndef MA_DR_FLAC_NO_WCHAR -#include -#endif -static size_t ma_dr_flac__on_read_stdio(void* pUserData, void* bufferOut, size_t bytesToRead) -{ - return fread(bufferOut, 1, bytesToRead, (FILE*)pUserData); -} -static ma_bool32 ma_dr_flac__on_seek_stdio(void* pUserData, int offset, ma_dr_flac_seek_origin origin) -{ - MA_DR_FLAC_ASSERT(offset >= 0); - return fseek((FILE*)pUserData, offset, (origin == ma_dr_flac_seek_origin_current) ? SEEK_CUR : SEEK_SET) == 0; -} -MA_API ma_dr_flac* ma_dr_flac_open_file(const char* pFileName, const ma_allocation_callbacks* pAllocationCallbacks) -{ - ma_dr_flac* pFlac; - FILE* pFile; - if (ma_fopen(&pFile, pFileName, "rb") != MA_SUCCESS) { - return NULL; - } - pFlac = ma_dr_flac_open(ma_dr_flac__on_read_stdio, ma_dr_flac__on_seek_stdio, (void*)pFile, pAllocationCallbacks); - if (pFlac == NULL) { - fclose(pFile); - return NULL; - } - return pFlac; -} -#ifndef MA_DR_FLAC_NO_WCHAR -MA_API ma_dr_flac* ma_dr_flac_open_file_w(const wchar_t* pFileName, const ma_allocation_callbacks* pAllocationCallbacks) -{ - ma_dr_flac* pFlac; - FILE* pFile; - if (ma_wfopen(&pFile, pFileName, L"rb", pAllocationCallbacks) != MA_SUCCESS) { - return NULL; - } - pFlac = ma_dr_flac_open(ma_dr_flac__on_read_stdio, ma_dr_flac__on_seek_stdio, (void*)pFile, pAllocationCallbacks); - if (pFlac == NULL) { - fclose(pFile); - return NULL; - } - return pFlac; -} -#endif -MA_API ma_dr_flac* ma_dr_flac_open_file_with_metadata(const char* pFileName, ma_dr_flac_meta_proc onMeta, void* pUserData, const ma_allocation_callbacks* pAllocationCallbacks) -{ - ma_dr_flac* pFlac; - FILE* pFile; - if (ma_fopen(&pFile, pFileName, "rb") != MA_SUCCESS) { - return NULL; - } - pFlac = ma_dr_flac_open_with_metadata_private(ma_dr_flac__on_read_stdio, ma_dr_flac__on_seek_stdio, onMeta, ma_dr_flac_container_unknown, (void*)pFile, pUserData, pAllocationCallbacks); - if (pFlac == NULL) { - fclose(pFile); - return pFlac; - } - return pFlac; -} -#ifndef MA_DR_FLAC_NO_WCHAR -MA_API ma_dr_flac* ma_dr_flac_open_file_with_metadata_w(const wchar_t* pFileName, ma_dr_flac_meta_proc onMeta, void* pUserData, const ma_allocation_callbacks* pAllocationCallbacks) -{ - ma_dr_flac* pFlac; - FILE* pFile; - if (ma_wfopen(&pFile, pFileName, L"rb", pAllocationCallbacks) != MA_SUCCESS) { - return NULL; - } - pFlac = ma_dr_flac_open_with_metadata_private(ma_dr_flac__on_read_stdio, ma_dr_flac__on_seek_stdio, onMeta, ma_dr_flac_container_unknown, (void*)pFile, pUserData, pAllocationCallbacks); - if (pFlac == NULL) { - fclose(pFile); - return pFlac; - } - return pFlac; -} -#endif -#endif -static size_t ma_dr_flac__on_read_memory(void* pUserData, void* bufferOut, size_t bytesToRead) -{ - ma_dr_flac__memory_stream* memoryStream = (ma_dr_flac__memory_stream*)pUserData; - size_t bytesRemaining; - MA_DR_FLAC_ASSERT(memoryStream != NULL); - MA_DR_FLAC_ASSERT(memoryStream->dataSize >= memoryStream->currentReadPos); - bytesRemaining = memoryStream->dataSize - memoryStream->currentReadPos; - if (bytesToRead > bytesRemaining) { - bytesToRead = bytesRemaining; - } - if (bytesToRead > 0) { - MA_DR_FLAC_COPY_MEMORY(bufferOut, memoryStream->data + memoryStream->currentReadPos, bytesToRead); - memoryStream->currentReadPos += bytesToRead; - } - return bytesToRead; -} -static ma_bool32 ma_dr_flac__on_seek_memory(void* pUserData, int offset, ma_dr_flac_seek_origin origin) -{ - ma_dr_flac__memory_stream* memoryStream = (ma_dr_flac__memory_stream*)pUserData; - MA_DR_FLAC_ASSERT(memoryStream != NULL); - MA_DR_FLAC_ASSERT(offset >= 0); - if (offset > (ma_int64)memoryStream->dataSize) { - return MA_FALSE; - } - if (origin == ma_dr_flac_seek_origin_current) { - if (memoryStream->currentReadPos + offset <= memoryStream->dataSize) { - memoryStream->currentReadPos += offset; - } else { - return MA_FALSE; - } - } else { - if ((ma_uint32)offset <= memoryStream->dataSize) { - memoryStream->currentReadPos = offset; - } else { - return MA_FALSE; - } - } - return MA_TRUE; -} -MA_API ma_dr_flac* ma_dr_flac_open_memory(const void* pData, size_t dataSize, const ma_allocation_callbacks* pAllocationCallbacks) -{ - ma_dr_flac__memory_stream memoryStream; - ma_dr_flac* pFlac; - memoryStream.data = (const ma_uint8*)pData; - memoryStream.dataSize = dataSize; - memoryStream.currentReadPos = 0; - pFlac = ma_dr_flac_open(ma_dr_flac__on_read_memory, ma_dr_flac__on_seek_memory, &memoryStream, pAllocationCallbacks); - if (pFlac == NULL) { - return NULL; - } - pFlac->memoryStream = memoryStream; -#ifndef MA_DR_FLAC_NO_OGG - if (pFlac->container == ma_dr_flac_container_ogg) - { - ma_dr_flac_oggbs* oggbs = (ma_dr_flac_oggbs*)pFlac->_oggbs; - oggbs->pUserData = &pFlac->memoryStream; - } - else -#endif - { - pFlac->bs.pUserData = &pFlac->memoryStream; - } - return pFlac; -} -MA_API ma_dr_flac* ma_dr_flac_open_memory_with_metadata(const void* pData, size_t dataSize, ma_dr_flac_meta_proc onMeta, void* pUserData, const ma_allocation_callbacks* pAllocationCallbacks) -{ - ma_dr_flac__memory_stream memoryStream; - ma_dr_flac* pFlac; - memoryStream.data = (const ma_uint8*)pData; - memoryStream.dataSize = dataSize; - memoryStream.currentReadPos = 0; - pFlac = ma_dr_flac_open_with_metadata_private(ma_dr_flac__on_read_memory, ma_dr_flac__on_seek_memory, onMeta, ma_dr_flac_container_unknown, &memoryStream, pUserData, pAllocationCallbacks); - if (pFlac == NULL) { - return NULL; - } - pFlac->memoryStream = memoryStream; -#ifndef MA_DR_FLAC_NO_OGG - if (pFlac->container == ma_dr_flac_container_ogg) - { - ma_dr_flac_oggbs* oggbs = (ma_dr_flac_oggbs*)pFlac->_oggbs; - oggbs->pUserData = &pFlac->memoryStream; - } - else -#endif - { - pFlac->bs.pUserData = &pFlac->memoryStream; - } - return pFlac; -} -MA_API ma_dr_flac* ma_dr_flac_open(ma_dr_flac_read_proc onRead, ma_dr_flac_seek_proc onSeek, void* pUserData, const ma_allocation_callbacks* pAllocationCallbacks) -{ - return ma_dr_flac_open_with_metadata_private(onRead, onSeek, NULL, ma_dr_flac_container_unknown, pUserData, pUserData, pAllocationCallbacks); -} -MA_API ma_dr_flac* ma_dr_flac_open_relaxed(ma_dr_flac_read_proc onRead, ma_dr_flac_seek_proc onSeek, ma_dr_flac_container container, void* pUserData, const ma_allocation_callbacks* pAllocationCallbacks) -{ - return ma_dr_flac_open_with_metadata_private(onRead, onSeek, NULL, container, pUserData, pUserData, pAllocationCallbacks); -} -MA_API ma_dr_flac* ma_dr_flac_open_with_metadata(ma_dr_flac_read_proc onRead, ma_dr_flac_seek_proc onSeek, ma_dr_flac_meta_proc onMeta, void* pUserData, const ma_allocation_callbacks* pAllocationCallbacks) -{ - return ma_dr_flac_open_with_metadata_private(onRead, onSeek, onMeta, ma_dr_flac_container_unknown, pUserData, pUserData, pAllocationCallbacks); -} -MA_API ma_dr_flac* ma_dr_flac_open_with_metadata_relaxed(ma_dr_flac_read_proc onRead, ma_dr_flac_seek_proc onSeek, ma_dr_flac_meta_proc onMeta, ma_dr_flac_container container, void* pUserData, const ma_allocation_callbacks* pAllocationCallbacks) -{ - return ma_dr_flac_open_with_metadata_private(onRead, onSeek, onMeta, container, pUserData, pUserData, pAllocationCallbacks); -} -MA_API void ma_dr_flac_close(ma_dr_flac* pFlac) -{ - if (pFlac == NULL) { - return; - } -#ifndef MA_DR_FLAC_NO_STDIO - if (pFlac->bs.onRead == ma_dr_flac__on_read_stdio) { - fclose((FILE*)pFlac->bs.pUserData); - } -#ifndef MA_DR_FLAC_NO_OGG - if (pFlac->container == ma_dr_flac_container_ogg) { - ma_dr_flac_oggbs* oggbs = (ma_dr_flac_oggbs*)pFlac->_oggbs; - MA_DR_FLAC_ASSERT(pFlac->bs.onRead == ma_dr_flac__on_read_ogg); - if (oggbs->onRead == ma_dr_flac__on_read_stdio) { - fclose((FILE*)oggbs->pUserData); - } - } -#endif -#endif - ma_dr_flac__free_from_callbacks(pFlac, &pFlac->allocationCallbacks); -} -#if 0 -static MA_INLINE void ma_dr_flac_read_pcm_frames_s32__decode_left_side__reference(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, ma_int32* pOutputSamples) -{ - ma_uint64 i; - for (i = 0; i < frameCount; ++i) { - ma_uint32 left = (ma_uint32)pInputSamples0[i] << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample); - ma_uint32 side = (ma_uint32)pInputSamples1[i] << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample); - ma_uint32 right = left - side; - pOutputSamples[i*2+0] = (ma_int32)left; - pOutputSamples[i*2+1] = (ma_int32)right; - } -} -#endif -static MA_INLINE void ma_dr_flac_read_pcm_frames_s32__decode_left_side__scalar(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, ma_int32* pOutputSamples) -{ - ma_uint64 i; - ma_uint64 frameCount4 = frameCount >> 2; - const ma_uint32* pInputSamples0U32 = (const ma_uint32*)pInputSamples0; - const ma_uint32* pInputSamples1U32 = (const ma_uint32*)pInputSamples1; - ma_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; - ma_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; - for (i = 0; i < frameCount4; ++i) { - ma_uint32 left0 = pInputSamples0U32[i*4+0] << shift0; - ma_uint32 left1 = pInputSamples0U32[i*4+1] << shift0; - ma_uint32 left2 = pInputSamples0U32[i*4+2] << shift0; - ma_uint32 left3 = pInputSamples0U32[i*4+3] << shift0; - ma_uint32 side0 = pInputSamples1U32[i*4+0] << shift1; - ma_uint32 side1 = pInputSamples1U32[i*4+1] << shift1; - ma_uint32 side2 = pInputSamples1U32[i*4+2] << shift1; - ma_uint32 side3 = pInputSamples1U32[i*4+3] << shift1; - ma_uint32 right0 = left0 - side0; - ma_uint32 right1 = left1 - side1; - ma_uint32 right2 = left2 - side2; - ma_uint32 right3 = left3 - side3; - pOutputSamples[i*8+0] = (ma_int32)left0; - pOutputSamples[i*8+1] = (ma_int32)right0; - pOutputSamples[i*8+2] = (ma_int32)left1; - pOutputSamples[i*8+3] = (ma_int32)right1; - pOutputSamples[i*8+4] = (ma_int32)left2; - pOutputSamples[i*8+5] = (ma_int32)right2; - pOutputSamples[i*8+6] = (ma_int32)left3; - pOutputSamples[i*8+7] = (ma_int32)right3; - } - for (i = (frameCount4 << 2); i < frameCount; ++i) { - ma_uint32 left = pInputSamples0U32[i] << shift0; - ma_uint32 side = pInputSamples1U32[i] << shift1; - ma_uint32 right = left - side; - pOutputSamples[i*2+0] = (ma_int32)left; - pOutputSamples[i*2+1] = (ma_int32)right; - } -} -#if defined(MA_DR_FLAC_SUPPORT_SSE2) -static MA_INLINE void ma_dr_flac_read_pcm_frames_s32__decode_left_side__sse2(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, ma_int32* pOutputSamples) -{ - ma_uint64 i; - ma_uint64 frameCount4 = frameCount >> 2; - const ma_uint32* pInputSamples0U32 = (const ma_uint32*)pInputSamples0; - const ma_uint32* pInputSamples1U32 = (const ma_uint32*)pInputSamples1; - ma_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; - ma_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; - MA_DR_FLAC_ASSERT(pFlac->bitsPerSample <= 24); - for (i = 0; i < frameCount4; ++i) { - __m128i left = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples0 + i), shift0); - __m128i side = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples1 + i), shift1); - __m128i right = _mm_sub_epi32(left, side); - _mm_storeu_si128((__m128i*)(pOutputSamples + i*8 + 0), _mm_unpacklo_epi32(left, right)); - _mm_storeu_si128((__m128i*)(pOutputSamples + i*8 + 4), _mm_unpackhi_epi32(left, right)); - } - for (i = (frameCount4 << 2); i < frameCount; ++i) { - ma_uint32 left = pInputSamples0U32[i] << shift0; - ma_uint32 side = pInputSamples1U32[i] << shift1; - ma_uint32 right = left - side; - pOutputSamples[i*2+0] = (ma_int32)left; - pOutputSamples[i*2+1] = (ma_int32)right; - } -} -#endif -#if defined(MA_DR_FLAC_SUPPORT_NEON) -static MA_INLINE void ma_dr_flac_read_pcm_frames_s32__decode_left_side__neon(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, ma_int32* pOutputSamples) -{ - ma_uint64 i; - ma_uint64 frameCount4 = frameCount >> 2; - const ma_uint32* pInputSamples0U32 = (const ma_uint32*)pInputSamples0; - const ma_uint32* pInputSamples1U32 = (const ma_uint32*)pInputSamples1; - ma_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; - ma_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; - int32x4_t shift0_4; - int32x4_t shift1_4; - MA_DR_FLAC_ASSERT(pFlac->bitsPerSample <= 24); - shift0_4 = vdupq_n_s32(shift0); - shift1_4 = vdupq_n_s32(shift1); - for (i = 0; i < frameCount4; ++i) { - uint32x4_t left; - uint32x4_t side; - uint32x4_t right; - left = vshlq_u32(vld1q_u32(pInputSamples0U32 + i*4), shift0_4); - side = vshlq_u32(vld1q_u32(pInputSamples1U32 + i*4), shift1_4); - right = vsubq_u32(left, side); - ma_dr_flac__vst2q_u32((ma_uint32*)pOutputSamples + i*8, vzipq_u32(left, right)); - } - for (i = (frameCount4 << 2); i < frameCount; ++i) { - ma_uint32 left = pInputSamples0U32[i] << shift0; - ma_uint32 side = pInputSamples1U32[i] << shift1; - ma_uint32 right = left - side; - pOutputSamples[i*2+0] = (ma_int32)left; - pOutputSamples[i*2+1] = (ma_int32)right; - } -} -#endif -static MA_INLINE void ma_dr_flac_read_pcm_frames_s32__decode_left_side(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, ma_int32* pOutputSamples) -{ -#if defined(MA_DR_FLAC_SUPPORT_SSE2) - if (ma_dr_flac__gIsSSE2Supported && pFlac->bitsPerSample <= 24) { - ma_dr_flac_read_pcm_frames_s32__decode_left_side__sse2(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); - } else -#elif defined(MA_DR_FLAC_SUPPORT_NEON) - if (ma_dr_flac__gIsNEONSupported && pFlac->bitsPerSample <= 24) { - ma_dr_flac_read_pcm_frames_s32__decode_left_side__neon(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); - } else -#endif - { -#if 0 - ma_dr_flac_read_pcm_frames_s32__decode_left_side__reference(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); -#else - ma_dr_flac_read_pcm_frames_s32__decode_left_side__scalar(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); -#endif - } -} -#if 0 -static MA_INLINE void ma_dr_flac_read_pcm_frames_s32__decode_right_side__reference(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, ma_int32* pOutputSamples) -{ - ma_uint64 i; - for (i = 0; i < frameCount; ++i) { - ma_uint32 side = (ma_uint32)pInputSamples0[i] << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample); - ma_uint32 right = (ma_uint32)pInputSamples1[i] << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample); - ma_uint32 left = right + side; - pOutputSamples[i*2+0] = (ma_int32)left; - pOutputSamples[i*2+1] = (ma_int32)right; - } -} -#endif -static MA_INLINE void ma_dr_flac_read_pcm_frames_s32__decode_right_side__scalar(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, ma_int32* pOutputSamples) -{ - ma_uint64 i; - ma_uint64 frameCount4 = frameCount >> 2; - const ma_uint32* pInputSamples0U32 = (const ma_uint32*)pInputSamples0; - const ma_uint32* pInputSamples1U32 = (const ma_uint32*)pInputSamples1; - ma_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; - ma_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; - for (i = 0; i < frameCount4; ++i) { - ma_uint32 side0 = pInputSamples0U32[i*4+0] << shift0; - ma_uint32 side1 = pInputSamples0U32[i*4+1] << shift0; - ma_uint32 side2 = pInputSamples0U32[i*4+2] << shift0; - ma_uint32 side3 = pInputSamples0U32[i*4+3] << shift0; - ma_uint32 right0 = pInputSamples1U32[i*4+0] << shift1; - ma_uint32 right1 = pInputSamples1U32[i*4+1] << shift1; - ma_uint32 right2 = pInputSamples1U32[i*4+2] << shift1; - ma_uint32 right3 = pInputSamples1U32[i*4+3] << shift1; - ma_uint32 left0 = right0 + side0; - ma_uint32 left1 = right1 + side1; - ma_uint32 left2 = right2 + side2; - ma_uint32 left3 = right3 + side3; - pOutputSamples[i*8+0] = (ma_int32)left0; - pOutputSamples[i*8+1] = (ma_int32)right0; - pOutputSamples[i*8+2] = (ma_int32)left1; - pOutputSamples[i*8+3] = (ma_int32)right1; - pOutputSamples[i*8+4] = (ma_int32)left2; - pOutputSamples[i*8+5] = (ma_int32)right2; - pOutputSamples[i*8+6] = (ma_int32)left3; - pOutputSamples[i*8+7] = (ma_int32)right3; - } - for (i = (frameCount4 << 2); i < frameCount; ++i) { - ma_uint32 side = pInputSamples0U32[i] << shift0; - ma_uint32 right = pInputSamples1U32[i] << shift1; - ma_uint32 left = right + side; - pOutputSamples[i*2+0] = (ma_int32)left; - pOutputSamples[i*2+1] = (ma_int32)right; - } -} -#if defined(MA_DR_FLAC_SUPPORT_SSE2) -static MA_INLINE void ma_dr_flac_read_pcm_frames_s32__decode_right_side__sse2(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, ma_int32* pOutputSamples) -{ - ma_uint64 i; - ma_uint64 frameCount4 = frameCount >> 2; - const ma_uint32* pInputSamples0U32 = (const ma_uint32*)pInputSamples0; - const ma_uint32* pInputSamples1U32 = (const ma_uint32*)pInputSamples1; - ma_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; - ma_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; - MA_DR_FLAC_ASSERT(pFlac->bitsPerSample <= 24); - for (i = 0; i < frameCount4; ++i) { - __m128i side = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples0 + i), shift0); - __m128i right = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples1 + i), shift1); - __m128i left = _mm_add_epi32(right, side); - _mm_storeu_si128((__m128i*)(pOutputSamples + i*8 + 0), _mm_unpacklo_epi32(left, right)); - _mm_storeu_si128((__m128i*)(pOutputSamples + i*8 + 4), _mm_unpackhi_epi32(left, right)); - } - for (i = (frameCount4 << 2); i < frameCount; ++i) { - ma_uint32 side = pInputSamples0U32[i] << shift0; - ma_uint32 right = pInputSamples1U32[i] << shift1; - ma_uint32 left = right + side; - pOutputSamples[i*2+0] = (ma_int32)left; - pOutputSamples[i*2+1] = (ma_int32)right; - } -} -#endif -#if defined(MA_DR_FLAC_SUPPORT_NEON) -static MA_INLINE void ma_dr_flac_read_pcm_frames_s32__decode_right_side__neon(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, ma_int32* pOutputSamples) -{ - ma_uint64 i; - ma_uint64 frameCount4 = frameCount >> 2; - const ma_uint32* pInputSamples0U32 = (const ma_uint32*)pInputSamples0; - const ma_uint32* pInputSamples1U32 = (const ma_uint32*)pInputSamples1; - ma_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; - ma_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; - int32x4_t shift0_4; - int32x4_t shift1_4; - MA_DR_FLAC_ASSERT(pFlac->bitsPerSample <= 24); - shift0_4 = vdupq_n_s32(shift0); - shift1_4 = vdupq_n_s32(shift1); - for (i = 0; i < frameCount4; ++i) { - uint32x4_t side; - uint32x4_t right; - uint32x4_t left; - side = vshlq_u32(vld1q_u32(pInputSamples0U32 + i*4), shift0_4); - right = vshlq_u32(vld1q_u32(pInputSamples1U32 + i*4), shift1_4); - left = vaddq_u32(right, side); - ma_dr_flac__vst2q_u32((ma_uint32*)pOutputSamples + i*8, vzipq_u32(left, right)); - } - for (i = (frameCount4 << 2); i < frameCount; ++i) { - ma_uint32 side = pInputSamples0U32[i] << shift0; - ma_uint32 right = pInputSamples1U32[i] << shift1; - ma_uint32 left = right + side; - pOutputSamples[i*2+0] = (ma_int32)left; - pOutputSamples[i*2+1] = (ma_int32)right; - } -} -#endif -static MA_INLINE void ma_dr_flac_read_pcm_frames_s32__decode_right_side(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, ma_int32* pOutputSamples) -{ -#if defined(MA_DR_FLAC_SUPPORT_SSE2) - if (ma_dr_flac__gIsSSE2Supported && pFlac->bitsPerSample <= 24) { - ma_dr_flac_read_pcm_frames_s32__decode_right_side__sse2(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); - } else -#elif defined(MA_DR_FLAC_SUPPORT_NEON) - if (ma_dr_flac__gIsNEONSupported && pFlac->bitsPerSample <= 24) { - ma_dr_flac_read_pcm_frames_s32__decode_right_side__neon(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); - } else -#endif - { -#if 0 - ma_dr_flac_read_pcm_frames_s32__decode_right_side__reference(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); -#else - ma_dr_flac_read_pcm_frames_s32__decode_right_side__scalar(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); -#endif - } -} -#if 0 -static MA_INLINE void ma_dr_flac_read_pcm_frames_s32__decode_mid_side__reference(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, ma_int32* pOutputSamples) -{ - for (ma_uint64 i = 0; i < frameCount; ++i) { - ma_uint32 mid = pInputSamples0U32[i] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; - ma_uint32 side = pInputSamples1U32[i] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; - mid = (mid << 1) | (side & 0x01); - pOutputSamples[i*2+0] = (ma_int32)((ma_uint32)((ma_int32)(mid + side) >> 1) << unusedBitsPerSample); - pOutputSamples[i*2+1] = (ma_int32)((ma_uint32)((ma_int32)(mid - side) >> 1) << unusedBitsPerSample); - } -} -#endif -static MA_INLINE void ma_dr_flac_read_pcm_frames_s32__decode_mid_side__scalar(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, ma_int32* pOutputSamples) -{ - ma_uint64 i; - ma_uint64 frameCount4 = frameCount >> 2; - const ma_uint32* pInputSamples0U32 = (const ma_uint32*)pInputSamples0; - const ma_uint32* pInputSamples1U32 = (const ma_uint32*)pInputSamples1; - ma_int32 shift = unusedBitsPerSample; - if (shift > 0) { - shift -= 1; - for (i = 0; i < frameCount4; ++i) { - ma_uint32 temp0L; - ma_uint32 temp1L; - ma_uint32 temp2L; - ma_uint32 temp3L; - ma_uint32 temp0R; - ma_uint32 temp1R; - ma_uint32 temp2R; - ma_uint32 temp3R; - ma_uint32 mid0 = pInputSamples0U32[i*4+0] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; - ma_uint32 mid1 = pInputSamples0U32[i*4+1] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; - ma_uint32 mid2 = pInputSamples0U32[i*4+2] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; - ma_uint32 mid3 = pInputSamples0U32[i*4+3] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; - ma_uint32 side0 = pInputSamples1U32[i*4+0] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; - ma_uint32 side1 = pInputSamples1U32[i*4+1] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; - ma_uint32 side2 = pInputSamples1U32[i*4+2] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; - ma_uint32 side3 = pInputSamples1U32[i*4+3] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; - mid0 = (mid0 << 1) | (side0 & 0x01); - mid1 = (mid1 << 1) | (side1 & 0x01); - mid2 = (mid2 << 1) | (side2 & 0x01); - mid3 = (mid3 << 1) | (side3 & 0x01); - temp0L = (mid0 + side0) << shift; - temp1L = (mid1 + side1) << shift; - temp2L = (mid2 + side2) << shift; - temp3L = (mid3 + side3) << shift; - temp0R = (mid0 - side0) << shift; - temp1R = (mid1 - side1) << shift; - temp2R = (mid2 - side2) << shift; - temp3R = (mid3 - side3) << shift; - pOutputSamples[i*8+0] = (ma_int32)temp0L; - pOutputSamples[i*8+1] = (ma_int32)temp0R; - pOutputSamples[i*8+2] = (ma_int32)temp1L; - pOutputSamples[i*8+3] = (ma_int32)temp1R; - pOutputSamples[i*8+4] = (ma_int32)temp2L; - pOutputSamples[i*8+5] = (ma_int32)temp2R; - pOutputSamples[i*8+6] = (ma_int32)temp3L; - pOutputSamples[i*8+7] = (ma_int32)temp3R; - } - } else { - for (i = 0; i < frameCount4; ++i) { - ma_uint32 temp0L; - ma_uint32 temp1L; - ma_uint32 temp2L; - ma_uint32 temp3L; - ma_uint32 temp0R; - ma_uint32 temp1R; - ma_uint32 temp2R; - ma_uint32 temp3R; - ma_uint32 mid0 = pInputSamples0U32[i*4+0] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; - ma_uint32 mid1 = pInputSamples0U32[i*4+1] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; - ma_uint32 mid2 = pInputSamples0U32[i*4+2] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; - ma_uint32 mid3 = pInputSamples0U32[i*4+3] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; - ma_uint32 side0 = pInputSamples1U32[i*4+0] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; - ma_uint32 side1 = pInputSamples1U32[i*4+1] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; - ma_uint32 side2 = pInputSamples1U32[i*4+2] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; - ma_uint32 side3 = pInputSamples1U32[i*4+3] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; - mid0 = (mid0 << 1) | (side0 & 0x01); - mid1 = (mid1 << 1) | (side1 & 0x01); - mid2 = (mid2 << 1) | (side2 & 0x01); - mid3 = (mid3 << 1) | (side3 & 0x01); - temp0L = (ma_uint32)((ma_int32)(mid0 + side0) >> 1); - temp1L = (ma_uint32)((ma_int32)(mid1 + side1) >> 1); - temp2L = (ma_uint32)((ma_int32)(mid2 + side2) >> 1); - temp3L = (ma_uint32)((ma_int32)(mid3 + side3) >> 1); - temp0R = (ma_uint32)((ma_int32)(mid0 - side0) >> 1); - temp1R = (ma_uint32)((ma_int32)(mid1 - side1) >> 1); - temp2R = (ma_uint32)((ma_int32)(mid2 - side2) >> 1); - temp3R = (ma_uint32)((ma_int32)(mid3 - side3) >> 1); - pOutputSamples[i*8+0] = (ma_int32)temp0L; - pOutputSamples[i*8+1] = (ma_int32)temp0R; - pOutputSamples[i*8+2] = (ma_int32)temp1L; - pOutputSamples[i*8+3] = (ma_int32)temp1R; - pOutputSamples[i*8+4] = (ma_int32)temp2L; - pOutputSamples[i*8+5] = (ma_int32)temp2R; - pOutputSamples[i*8+6] = (ma_int32)temp3L; - pOutputSamples[i*8+7] = (ma_int32)temp3R; - } - } - for (i = (frameCount4 << 2); i < frameCount; ++i) { - ma_uint32 mid = pInputSamples0U32[i] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; - ma_uint32 side = pInputSamples1U32[i] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; - mid = (mid << 1) | (side & 0x01); - pOutputSamples[i*2+0] = (ma_int32)((ma_uint32)((ma_int32)(mid + side) >> 1) << unusedBitsPerSample); - pOutputSamples[i*2+1] = (ma_int32)((ma_uint32)((ma_int32)(mid - side) >> 1) << unusedBitsPerSample); - } -} -#if defined(MA_DR_FLAC_SUPPORT_SSE2) -static MA_INLINE void ma_dr_flac_read_pcm_frames_s32__decode_mid_side__sse2(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, ma_int32* pOutputSamples) -{ - ma_uint64 i; - ma_uint64 frameCount4 = frameCount >> 2; - const ma_uint32* pInputSamples0U32 = (const ma_uint32*)pInputSamples0; - const ma_uint32* pInputSamples1U32 = (const ma_uint32*)pInputSamples1; - ma_int32 shift = unusedBitsPerSample; - MA_DR_FLAC_ASSERT(pFlac->bitsPerSample <= 24); - if (shift == 0) { - for (i = 0; i < frameCount4; ++i) { - __m128i mid; - __m128i side; - __m128i left; - __m128i right; - mid = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples0 + i), pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample); - side = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples1 + i), pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample); - mid = _mm_or_si128(_mm_slli_epi32(mid, 1), _mm_and_si128(side, _mm_set1_epi32(0x01))); - left = _mm_srai_epi32(_mm_add_epi32(mid, side), 1); - right = _mm_srai_epi32(_mm_sub_epi32(mid, side), 1); - _mm_storeu_si128((__m128i*)(pOutputSamples + i*8 + 0), _mm_unpacklo_epi32(left, right)); - _mm_storeu_si128((__m128i*)(pOutputSamples + i*8 + 4), _mm_unpackhi_epi32(left, right)); - } - for (i = (frameCount4 << 2); i < frameCount; ++i) { - ma_uint32 mid = pInputSamples0U32[i] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; - ma_uint32 side = pInputSamples1U32[i] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; - mid = (mid << 1) | (side & 0x01); - pOutputSamples[i*2+0] = (ma_int32)(mid + side) >> 1; - pOutputSamples[i*2+1] = (ma_int32)(mid - side) >> 1; - } - } else { - shift -= 1; - for (i = 0; i < frameCount4; ++i) { - __m128i mid; - __m128i side; - __m128i left; - __m128i right; - mid = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples0 + i), pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample); - side = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples1 + i), pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample); - mid = _mm_or_si128(_mm_slli_epi32(mid, 1), _mm_and_si128(side, _mm_set1_epi32(0x01))); - left = _mm_slli_epi32(_mm_add_epi32(mid, side), shift); - right = _mm_slli_epi32(_mm_sub_epi32(mid, side), shift); - _mm_storeu_si128((__m128i*)(pOutputSamples + i*8 + 0), _mm_unpacklo_epi32(left, right)); - _mm_storeu_si128((__m128i*)(pOutputSamples + i*8 + 4), _mm_unpackhi_epi32(left, right)); - } - for (i = (frameCount4 << 2); i < frameCount; ++i) { - ma_uint32 mid = pInputSamples0U32[i] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; - ma_uint32 side = pInputSamples1U32[i] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; - mid = (mid << 1) | (side & 0x01); - pOutputSamples[i*2+0] = (ma_int32)((mid + side) << shift); - pOutputSamples[i*2+1] = (ma_int32)((mid - side) << shift); - } - } -} -#endif -#if defined(MA_DR_FLAC_SUPPORT_NEON) -static MA_INLINE void ma_dr_flac_read_pcm_frames_s32__decode_mid_side__neon(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, ma_int32* pOutputSamples) -{ - ma_uint64 i; - ma_uint64 frameCount4 = frameCount >> 2; - const ma_uint32* pInputSamples0U32 = (const ma_uint32*)pInputSamples0; - const ma_uint32* pInputSamples1U32 = (const ma_uint32*)pInputSamples1; - ma_int32 shift = unusedBitsPerSample; - int32x4_t wbpsShift0_4; - int32x4_t wbpsShift1_4; - uint32x4_t one4; - MA_DR_FLAC_ASSERT(pFlac->bitsPerSample <= 24); - wbpsShift0_4 = vdupq_n_s32(pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample); - wbpsShift1_4 = vdupq_n_s32(pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample); - one4 = vdupq_n_u32(1); - if (shift == 0) { - for (i = 0; i < frameCount4; ++i) { - uint32x4_t mid; - uint32x4_t side; - int32x4_t left; - int32x4_t right; - mid = vshlq_u32(vld1q_u32(pInputSamples0U32 + i*4), wbpsShift0_4); - side = vshlq_u32(vld1q_u32(pInputSamples1U32 + i*4), wbpsShift1_4); - mid = vorrq_u32(vshlq_n_u32(mid, 1), vandq_u32(side, one4)); - left = vshrq_n_s32(vreinterpretq_s32_u32(vaddq_u32(mid, side)), 1); - right = vshrq_n_s32(vreinterpretq_s32_u32(vsubq_u32(mid, side)), 1); - ma_dr_flac__vst2q_s32(pOutputSamples + i*8, vzipq_s32(left, right)); - } - for (i = (frameCount4 << 2); i < frameCount; ++i) { - ma_uint32 mid = pInputSamples0U32[i] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; - ma_uint32 side = pInputSamples1U32[i] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; - mid = (mid << 1) | (side & 0x01); - pOutputSamples[i*2+0] = (ma_int32)(mid + side) >> 1; - pOutputSamples[i*2+1] = (ma_int32)(mid - side) >> 1; - } - } else { - int32x4_t shift4; - shift -= 1; - shift4 = vdupq_n_s32(shift); - for (i = 0; i < frameCount4; ++i) { - uint32x4_t mid; - uint32x4_t side; - int32x4_t left; - int32x4_t right; - mid = vshlq_u32(vld1q_u32(pInputSamples0U32 + i*4), wbpsShift0_4); - side = vshlq_u32(vld1q_u32(pInputSamples1U32 + i*4), wbpsShift1_4); - mid = vorrq_u32(vshlq_n_u32(mid, 1), vandq_u32(side, one4)); - left = vreinterpretq_s32_u32(vshlq_u32(vaddq_u32(mid, side), shift4)); - right = vreinterpretq_s32_u32(vshlq_u32(vsubq_u32(mid, side), shift4)); - ma_dr_flac__vst2q_s32(pOutputSamples + i*8, vzipq_s32(left, right)); - } - for (i = (frameCount4 << 2); i < frameCount; ++i) { - ma_uint32 mid = pInputSamples0U32[i] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; - ma_uint32 side = pInputSamples1U32[i] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; - mid = (mid << 1) | (side & 0x01); - pOutputSamples[i*2+0] = (ma_int32)((mid + side) << shift); - pOutputSamples[i*2+1] = (ma_int32)((mid - side) << shift); - } - } -} -#endif -static MA_INLINE void ma_dr_flac_read_pcm_frames_s32__decode_mid_side(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, ma_int32* pOutputSamples) -{ -#if defined(MA_DR_FLAC_SUPPORT_SSE2) - if (ma_dr_flac__gIsSSE2Supported && pFlac->bitsPerSample <= 24) { - ma_dr_flac_read_pcm_frames_s32__decode_mid_side__sse2(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); - } else -#elif defined(MA_DR_FLAC_SUPPORT_NEON) - if (ma_dr_flac__gIsNEONSupported && pFlac->bitsPerSample <= 24) { - ma_dr_flac_read_pcm_frames_s32__decode_mid_side__neon(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); - } else -#endif - { -#if 0 - ma_dr_flac_read_pcm_frames_s32__decode_mid_side__reference(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); -#else - ma_dr_flac_read_pcm_frames_s32__decode_mid_side__scalar(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); -#endif - } -} -#if 0 -static MA_INLINE void ma_dr_flac_read_pcm_frames_s32__decode_independent_stereo__reference(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, ma_int32* pOutputSamples) -{ - for (ma_uint64 i = 0; i < frameCount; ++i) { - pOutputSamples[i*2+0] = (ma_int32)((ma_uint32)pInputSamples0[i] << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample)); - pOutputSamples[i*2+1] = (ma_int32)((ma_uint32)pInputSamples1[i] << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample)); - } -} -#endif -static MA_INLINE void ma_dr_flac_read_pcm_frames_s32__decode_independent_stereo__scalar(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, ma_int32* pOutputSamples) -{ - ma_uint64 i; - ma_uint64 frameCount4 = frameCount >> 2; - const ma_uint32* pInputSamples0U32 = (const ma_uint32*)pInputSamples0; - const ma_uint32* pInputSamples1U32 = (const ma_uint32*)pInputSamples1; - ma_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; - ma_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; - for (i = 0; i < frameCount4; ++i) { - ma_uint32 tempL0 = pInputSamples0U32[i*4+0] << shift0; - ma_uint32 tempL1 = pInputSamples0U32[i*4+1] << shift0; - ma_uint32 tempL2 = pInputSamples0U32[i*4+2] << shift0; - ma_uint32 tempL3 = pInputSamples0U32[i*4+3] << shift0; - ma_uint32 tempR0 = pInputSamples1U32[i*4+0] << shift1; - ma_uint32 tempR1 = pInputSamples1U32[i*4+1] << shift1; - ma_uint32 tempR2 = pInputSamples1U32[i*4+2] << shift1; - ma_uint32 tempR3 = pInputSamples1U32[i*4+3] << shift1; - pOutputSamples[i*8+0] = (ma_int32)tempL0; - pOutputSamples[i*8+1] = (ma_int32)tempR0; - pOutputSamples[i*8+2] = (ma_int32)tempL1; - pOutputSamples[i*8+3] = (ma_int32)tempR1; - pOutputSamples[i*8+4] = (ma_int32)tempL2; - pOutputSamples[i*8+5] = (ma_int32)tempR2; - pOutputSamples[i*8+6] = (ma_int32)tempL3; - pOutputSamples[i*8+7] = (ma_int32)tempR3; - } - for (i = (frameCount4 << 2); i < frameCount; ++i) { - pOutputSamples[i*2+0] = (ma_int32)(pInputSamples0U32[i] << shift0); - pOutputSamples[i*2+1] = (ma_int32)(pInputSamples1U32[i] << shift1); - } -} -#if defined(MA_DR_FLAC_SUPPORT_SSE2) -static MA_INLINE void ma_dr_flac_read_pcm_frames_s32__decode_independent_stereo__sse2(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, ma_int32* pOutputSamples) -{ - ma_uint64 i; - ma_uint64 frameCount4 = frameCount >> 2; - const ma_uint32* pInputSamples0U32 = (const ma_uint32*)pInputSamples0; - const ma_uint32* pInputSamples1U32 = (const ma_uint32*)pInputSamples1; - ma_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; - ma_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; - for (i = 0; i < frameCount4; ++i) { - __m128i left = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples0 + i), shift0); - __m128i right = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples1 + i), shift1); - _mm_storeu_si128((__m128i*)(pOutputSamples + i*8 + 0), _mm_unpacklo_epi32(left, right)); - _mm_storeu_si128((__m128i*)(pOutputSamples + i*8 + 4), _mm_unpackhi_epi32(left, right)); - } - for (i = (frameCount4 << 2); i < frameCount; ++i) { - pOutputSamples[i*2+0] = (ma_int32)(pInputSamples0U32[i] << shift0); - pOutputSamples[i*2+1] = (ma_int32)(pInputSamples1U32[i] << shift1); - } -} -#endif -#if defined(MA_DR_FLAC_SUPPORT_NEON) -static MA_INLINE void ma_dr_flac_read_pcm_frames_s32__decode_independent_stereo__neon(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, ma_int32* pOutputSamples) -{ - ma_uint64 i; - ma_uint64 frameCount4 = frameCount >> 2; - const ma_uint32* pInputSamples0U32 = (const ma_uint32*)pInputSamples0; - const ma_uint32* pInputSamples1U32 = (const ma_uint32*)pInputSamples1; - ma_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; - ma_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; - int32x4_t shift4_0 = vdupq_n_s32(shift0); - int32x4_t shift4_1 = vdupq_n_s32(shift1); - for (i = 0; i < frameCount4; ++i) { - int32x4_t left; - int32x4_t right; - left = vreinterpretq_s32_u32(vshlq_u32(vld1q_u32(pInputSamples0U32 + i*4), shift4_0)); - right = vreinterpretq_s32_u32(vshlq_u32(vld1q_u32(pInputSamples1U32 + i*4), shift4_1)); - ma_dr_flac__vst2q_s32(pOutputSamples + i*8, vzipq_s32(left, right)); - } - for (i = (frameCount4 << 2); i < frameCount; ++i) { - pOutputSamples[i*2+0] = (ma_int32)(pInputSamples0U32[i] << shift0); - pOutputSamples[i*2+1] = (ma_int32)(pInputSamples1U32[i] << shift1); - } -} -#endif -static MA_INLINE void ma_dr_flac_read_pcm_frames_s32__decode_independent_stereo(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, ma_int32* pOutputSamples) -{ -#if defined(MA_DR_FLAC_SUPPORT_SSE2) - if (ma_dr_flac__gIsSSE2Supported && pFlac->bitsPerSample <= 24) { - ma_dr_flac_read_pcm_frames_s32__decode_independent_stereo__sse2(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); - } else -#elif defined(MA_DR_FLAC_SUPPORT_NEON) - if (ma_dr_flac__gIsNEONSupported && pFlac->bitsPerSample <= 24) { - ma_dr_flac_read_pcm_frames_s32__decode_independent_stereo__neon(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); - } else -#endif - { -#if 0 - ma_dr_flac_read_pcm_frames_s32__decode_independent_stereo__reference(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); -#else - ma_dr_flac_read_pcm_frames_s32__decode_independent_stereo__scalar(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); -#endif - } -} -MA_API ma_uint64 ma_dr_flac_read_pcm_frames_s32(ma_dr_flac* pFlac, ma_uint64 framesToRead, ma_int32* pBufferOut) -{ - ma_uint64 framesRead; - ma_uint32 unusedBitsPerSample; - if (pFlac == NULL || framesToRead == 0) { - return 0; - } - if (pBufferOut == NULL) { - return ma_dr_flac__seek_forward_by_pcm_frames(pFlac, framesToRead); - } - MA_DR_FLAC_ASSERT(pFlac->bitsPerSample <= 32); - unusedBitsPerSample = 32 - pFlac->bitsPerSample; - framesRead = 0; - while (framesToRead > 0) { - if (pFlac->currentFLACFrame.pcmFramesRemaining == 0) { - if (!ma_dr_flac__read_and_decode_next_flac_frame(pFlac)) { - break; - } - } else { - unsigned int channelCount = ma_dr_flac__get_channel_count_from_channel_assignment(pFlac->currentFLACFrame.header.channelAssignment); - ma_uint64 iFirstPCMFrame = pFlac->currentFLACFrame.header.blockSizeInPCMFrames - pFlac->currentFLACFrame.pcmFramesRemaining; - ma_uint64 frameCountThisIteration = framesToRead; - if (frameCountThisIteration > pFlac->currentFLACFrame.pcmFramesRemaining) { - frameCountThisIteration = pFlac->currentFLACFrame.pcmFramesRemaining; - } - if (channelCount == 2) { - const ma_int32* pDecodedSamples0 = pFlac->currentFLACFrame.subframes[0].pSamplesS32 + iFirstPCMFrame; - const ma_int32* pDecodedSamples1 = pFlac->currentFLACFrame.subframes[1].pSamplesS32 + iFirstPCMFrame; - switch (pFlac->currentFLACFrame.header.channelAssignment) - { - case MA_DR_FLAC_CHANNEL_ASSIGNMENT_LEFT_SIDE: - { - ma_dr_flac_read_pcm_frames_s32__decode_left_side(pFlac, frameCountThisIteration, unusedBitsPerSample, pDecodedSamples0, pDecodedSamples1, pBufferOut); - } break; - case MA_DR_FLAC_CHANNEL_ASSIGNMENT_RIGHT_SIDE: - { - ma_dr_flac_read_pcm_frames_s32__decode_right_side(pFlac, frameCountThisIteration, unusedBitsPerSample, pDecodedSamples0, pDecodedSamples1, pBufferOut); - } break; - case MA_DR_FLAC_CHANNEL_ASSIGNMENT_MID_SIDE: - { - ma_dr_flac_read_pcm_frames_s32__decode_mid_side(pFlac, frameCountThisIteration, unusedBitsPerSample, pDecodedSamples0, pDecodedSamples1, pBufferOut); - } break; - case MA_DR_FLAC_CHANNEL_ASSIGNMENT_INDEPENDENT: - default: - { - ma_dr_flac_read_pcm_frames_s32__decode_independent_stereo(pFlac, frameCountThisIteration, unusedBitsPerSample, pDecodedSamples0, pDecodedSamples1, pBufferOut); - } break; - } - } else { - ma_uint64 i; - for (i = 0; i < frameCountThisIteration; ++i) { - unsigned int j; - for (j = 0; j < channelCount; ++j) { - pBufferOut[(i*channelCount)+j] = (ma_int32)((ma_uint32)(pFlac->currentFLACFrame.subframes[j].pSamplesS32[iFirstPCMFrame + i]) << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[j].wastedBitsPerSample)); - } - } - } - framesRead += frameCountThisIteration; - pBufferOut += frameCountThisIteration * channelCount; - framesToRead -= frameCountThisIteration; - pFlac->currentPCMFrame += frameCountThisIteration; - pFlac->currentFLACFrame.pcmFramesRemaining -= (ma_uint32)frameCountThisIteration; - } - } - return framesRead; -} -#if 0 -static MA_INLINE void ma_dr_flac_read_pcm_frames_s16__decode_left_side__reference(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, ma_int16* pOutputSamples) -{ - ma_uint64 i; - for (i = 0; i < frameCount; ++i) { - ma_uint32 left = (ma_uint32)pInputSamples0[i] << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample); - ma_uint32 side = (ma_uint32)pInputSamples1[i] << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample); - ma_uint32 right = left - side; - left >>= 16; - right >>= 16; - pOutputSamples[i*2+0] = (ma_int16)left; - pOutputSamples[i*2+1] = (ma_int16)right; - } -} -#endif -static MA_INLINE void ma_dr_flac_read_pcm_frames_s16__decode_left_side__scalar(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, ma_int16* pOutputSamples) -{ - ma_uint64 i; - ma_uint64 frameCount4 = frameCount >> 2; - const ma_uint32* pInputSamples0U32 = (const ma_uint32*)pInputSamples0; - const ma_uint32* pInputSamples1U32 = (const ma_uint32*)pInputSamples1; - ma_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; - ma_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; - for (i = 0; i < frameCount4; ++i) { - ma_uint32 left0 = pInputSamples0U32[i*4+0] << shift0; - ma_uint32 left1 = pInputSamples0U32[i*4+1] << shift0; - ma_uint32 left2 = pInputSamples0U32[i*4+2] << shift0; - ma_uint32 left3 = pInputSamples0U32[i*4+3] << shift0; - ma_uint32 side0 = pInputSamples1U32[i*4+0] << shift1; - ma_uint32 side1 = pInputSamples1U32[i*4+1] << shift1; - ma_uint32 side2 = pInputSamples1U32[i*4+2] << shift1; - ma_uint32 side3 = pInputSamples1U32[i*4+3] << shift1; - ma_uint32 right0 = left0 - side0; - ma_uint32 right1 = left1 - side1; - ma_uint32 right2 = left2 - side2; - ma_uint32 right3 = left3 - side3; - left0 >>= 16; - left1 >>= 16; - left2 >>= 16; - left3 >>= 16; - right0 >>= 16; - right1 >>= 16; - right2 >>= 16; - right3 >>= 16; - pOutputSamples[i*8+0] = (ma_int16)left0; - pOutputSamples[i*8+1] = (ma_int16)right0; - pOutputSamples[i*8+2] = (ma_int16)left1; - pOutputSamples[i*8+3] = (ma_int16)right1; - pOutputSamples[i*8+4] = (ma_int16)left2; - pOutputSamples[i*8+5] = (ma_int16)right2; - pOutputSamples[i*8+6] = (ma_int16)left3; - pOutputSamples[i*8+7] = (ma_int16)right3; - } - for (i = (frameCount4 << 2); i < frameCount; ++i) { - ma_uint32 left = pInputSamples0U32[i] << shift0; - ma_uint32 side = pInputSamples1U32[i] << shift1; - ma_uint32 right = left - side; - left >>= 16; - right >>= 16; - pOutputSamples[i*2+0] = (ma_int16)left; - pOutputSamples[i*2+1] = (ma_int16)right; - } -} -#if defined(MA_DR_FLAC_SUPPORT_SSE2) -static MA_INLINE void ma_dr_flac_read_pcm_frames_s16__decode_left_side__sse2(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, ma_int16* pOutputSamples) -{ - ma_uint64 i; - ma_uint64 frameCount4 = frameCount >> 2; - const ma_uint32* pInputSamples0U32 = (const ma_uint32*)pInputSamples0; - const ma_uint32* pInputSamples1U32 = (const ma_uint32*)pInputSamples1; - ma_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; - ma_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; - MA_DR_FLAC_ASSERT(pFlac->bitsPerSample <= 24); - for (i = 0; i < frameCount4; ++i) { - __m128i left = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples0 + i), shift0); - __m128i side = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples1 + i), shift1); - __m128i right = _mm_sub_epi32(left, side); - left = _mm_srai_epi32(left, 16); - right = _mm_srai_epi32(right, 16); - _mm_storeu_si128((__m128i*)(pOutputSamples + i*8), ma_dr_flac__mm_packs_interleaved_epi32(left, right)); - } - for (i = (frameCount4 << 2); i < frameCount; ++i) { - ma_uint32 left = pInputSamples0U32[i] << shift0; - ma_uint32 side = pInputSamples1U32[i] << shift1; - ma_uint32 right = left - side; - left >>= 16; - right >>= 16; - pOutputSamples[i*2+0] = (ma_int16)left; - pOutputSamples[i*2+1] = (ma_int16)right; - } -} -#endif -#if defined(MA_DR_FLAC_SUPPORT_NEON) -static MA_INLINE void ma_dr_flac_read_pcm_frames_s16__decode_left_side__neon(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, ma_int16* pOutputSamples) -{ - ma_uint64 i; - ma_uint64 frameCount4 = frameCount >> 2; - const ma_uint32* pInputSamples0U32 = (const ma_uint32*)pInputSamples0; - const ma_uint32* pInputSamples1U32 = (const ma_uint32*)pInputSamples1; - ma_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; - ma_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; - int32x4_t shift0_4; - int32x4_t shift1_4; - MA_DR_FLAC_ASSERT(pFlac->bitsPerSample <= 24); - shift0_4 = vdupq_n_s32(shift0); - shift1_4 = vdupq_n_s32(shift1); - for (i = 0; i < frameCount4; ++i) { - uint32x4_t left; - uint32x4_t side; - uint32x4_t right; - left = vshlq_u32(vld1q_u32(pInputSamples0U32 + i*4), shift0_4); - side = vshlq_u32(vld1q_u32(pInputSamples1U32 + i*4), shift1_4); - right = vsubq_u32(left, side); - left = vshrq_n_u32(left, 16); - right = vshrq_n_u32(right, 16); - ma_dr_flac__vst2q_u16((ma_uint16*)pOutputSamples + i*8, vzip_u16(vmovn_u32(left), vmovn_u32(right))); - } - for (i = (frameCount4 << 2); i < frameCount; ++i) { - ma_uint32 left = pInputSamples0U32[i] << shift0; - ma_uint32 side = pInputSamples1U32[i] << shift1; - ma_uint32 right = left - side; - left >>= 16; - right >>= 16; - pOutputSamples[i*2+0] = (ma_int16)left; - pOutputSamples[i*2+1] = (ma_int16)right; - } -} -#endif -static MA_INLINE void ma_dr_flac_read_pcm_frames_s16__decode_left_side(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, ma_int16* pOutputSamples) -{ -#if defined(MA_DR_FLAC_SUPPORT_SSE2) - if (ma_dr_flac__gIsSSE2Supported && pFlac->bitsPerSample <= 24) { - ma_dr_flac_read_pcm_frames_s16__decode_left_side__sse2(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); - } else -#elif defined(MA_DR_FLAC_SUPPORT_NEON) - if (ma_dr_flac__gIsNEONSupported && pFlac->bitsPerSample <= 24) { - ma_dr_flac_read_pcm_frames_s16__decode_left_side__neon(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); - } else -#endif - { -#if 0 - ma_dr_flac_read_pcm_frames_s16__decode_left_side__reference(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); -#else - ma_dr_flac_read_pcm_frames_s16__decode_left_side__scalar(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); -#endif - } -} -#if 0 -static MA_INLINE void ma_dr_flac_read_pcm_frames_s16__decode_right_side__reference(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, ma_int16* pOutputSamples) -{ - ma_uint64 i; - for (i = 0; i < frameCount; ++i) { - ma_uint32 side = (ma_uint32)pInputSamples0[i] << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample); - ma_uint32 right = (ma_uint32)pInputSamples1[i] << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample); - ma_uint32 left = right + side; - left >>= 16; - right >>= 16; - pOutputSamples[i*2+0] = (ma_int16)left; - pOutputSamples[i*2+1] = (ma_int16)right; - } -} -#endif -static MA_INLINE void ma_dr_flac_read_pcm_frames_s16__decode_right_side__scalar(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, ma_int16* pOutputSamples) -{ - ma_uint64 i; - ma_uint64 frameCount4 = frameCount >> 2; - const ma_uint32* pInputSamples0U32 = (const ma_uint32*)pInputSamples0; - const ma_uint32* pInputSamples1U32 = (const ma_uint32*)pInputSamples1; - ma_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; - ma_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; - for (i = 0; i < frameCount4; ++i) { - ma_uint32 side0 = pInputSamples0U32[i*4+0] << shift0; - ma_uint32 side1 = pInputSamples0U32[i*4+1] << shift0; - ma_uint32 side2 = pInputSamples0U32[i*4+2] << shift0; - ma_uint32 side3 = pInputSamples0U32[i*4+3] << shift0; - ma_uint32 right0 = pInputSamples1U32[i*4+0] << shift1; - ma_uint32 right1 = pInputSamples1U32[i*4+1] << shift1; - ma_uint32 right2 = pInputSamples1U32[i*4+2] << shift1; - ma_uint32 right3 = pInputSamples1U32[i*4+3] << shift1; - ma_uint32 left0 = right0 + side0; - ma_uint32 left1 = right1 + side1; - ma_uint32 left2 = right2 + side2; - ma_uint32 left3 = right3 + side3; - left0 >>= 16; - left1 >>= 16; - left2 >>= 16; - left3 >>= 16; - right0 >>= 16; - right1 >>= 16; - right2 >>= 16; - right3 >>= 16; - pOutputSamples[i*8+0] = (ma_int16)left0; - pOutputSamples[i*8+1] = (ma_int16)right0; - pOutputSamples[i*8+2] = (ma_int16)left1; - pOutputSamples[i*8+3] = (ma_int16)right1; - pOutputSamples[i*8+4] = (ma_int16)left2; - pOutputSamples[i*8+5] = (ma_int16)right2; - pOutputSamples[i*8+6] = (ma_int16)left3; - pOutputSamples[i*8+7] = (ma_int16)right3; - } - for (i = (frameCount4 << 2); i < frameCount; ++i) { - ma_uint32 side = pInputSamples0U32[i] << shift0; - ma_uint32 right = pInputSamples1U32[i] << shift1; - ma_uint32 left = right + side; - left >>= 16; - right >>= 16; - pOutputSamples[i*2+0] = (ma_int16)left; - pOutputSamples[i*2+1] = (ma_int16)right; - } -} -#if defined(MA_DR_FLAC_SUPPORT_SSE2) -static MA_INLINE void ma_dr_flac_read_pcm_frames_s16__decode_right_side__sse2(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, ma_int16* pOutputSamples) -{ - ma_uint64 i; - ma_uint64 frameCount4 = frameCount >> 2; - const ma_uint32* pInputSamples0U32 = (const ma_uint32*)pInputSamples0; - const ma_uint32* pInputSamples1U32 = (const ma_uint32*)pInputSamples1; - ma_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; - ma_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; - MA_DR_FLAC_ASSERT(pFlac->bitsPerSample <= 24); - for (i = 0; i < frameCount4; ++i) { - __m128i side = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples0 + i), shift0); - __m128i right = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples1 + i), shift1); - __m128i left = _mm_add_epi32(right, side); - left = _mm_srai_epi32(left, 16); - right = _mm_srai_epi32(right, 16); - _mm_storeu_si128((__m128i*)(pOutputSamples + i*8), ma_dr_flac__mm_packs_interleaved_epi32(left, right)); - } - for (i = (frameCount4 << 2); i < frameCount; ++i) { - ma_uint32 side = pInputSamples0U32[i] << shift0; - ma_uint32 right = pInputSamples1U32[i] << shift1; - ma_uint32 left = right + side; - left >>= 16; - right >>= 16; - pOutputSamples[i*2+0] = (ma_int16)left; - pOutputSamples[i*2+1] = (ma_int16)right; - } -} -#endif -#if defined(MA_DR_FLAC_SUPPORT_NEON) -static MA_INLINE void ma_dr_flac_read_pcm_frames_s16__decode_right_side__neon(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, ma_int16* pOutputSamples) -{ - ma_uint64 i; - ma_uint64 frameCount4 = frameCount >> 2; - const ma_uint32* pInputSamples0U32 = (const ma_uint32*)pInputSamples0; - const ma_uint32* pInputSamples1U32 = (const ma_uint32*)pInputSamples1; - ma_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; - ma_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; - int32x4_t shift0_4; - int32x4_t shift1_4; - MA_DR_FLAC_ASSERT(pFlac->bitsPerSample <= 24); - shift0_4 = vdupq_n_s32(shift0); - shift1_4 = vdupq_n_s32(shift1); - for (i = 0; i < frameCount4; ++i) { - uint32x4_t side; - uint32x4_t right; - uint32x4_t left; - side = vshlq_u32(vld1q_u32(pInputSamples0U32 + i*4), shift0_4); - right = vshlq_u32(vld1q_u32(pInputSamples1U32 + i*4), shift1_4); - left = vaddq_u32(right, side); - left = vshrq_n_u32(left, 16); - right = vshrq_n_u32(right, 16); - ma_dr_flac__vst2q_u16((ma_uint16*)pOutputSamples + i*8, vzip_u16(vmovn_u32(left), vmovn_u32(right))); - } - for (i = (frameCount4 << 2); i < frameCount; ++i) { - ma_uint32 side = pInputSamples0U32[i] << shift0; - ma_uint32 right = pInputSamples1U32[i] << shift1; - ma_uint32 left = right + side; - left >>= 16; - right >>= 16; - pOutputSamples[i*2+0] = (ma_int16)left; - pOutputSamples[i*2+1] = (ma_int16)right; - } -} -#endif -static MA_INLINE void ma_dr_flac_read_pcm_frames_s16__decode_right_side(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, ma_int16* pOutputSamples) -{ -#if defined(MA_DR_FLAC_SUPPORT_SSE2) - if (ma_dr_flac__gIsSSE2Supported && pFlac->bitsPerSample <= 24) { - ma_dr_flac_read_pcm_frames_s16__decode_right_side__sse2(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); - } else -#elif defined(MA_DR_FLAC_SUPPORT_NEON) - if (ma_dr_flac__gIsNEONSupported && pFlac->bitsPerSample <= 24) { - ma_dr_flac_read_pcm_frames_s16__decode_right_side__neon(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); - } else -#endif - { -#if 0 - ma_dr_flac_read_pcm_frames_s16__decode_right_side__reference(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); -#else - ma_dr_flac_read_pcm_frames_s16__decode_right_side__scalar(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); -#endif - } -} -#if 0 -static MA_INLINE void ma_dr_flac_read_pcm_frames_s16__decode_mid_side__reference(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, ma_int16* pOutputSamples) -{ - for (ma_uint64 i = 0; i < frameCount; ++i) { - ma_uint32 mid = (ma_uint32)pInputSamples0[i] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; - ma_uint32 side = (ma_uint32)pInputSamples1[i] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; - mid = (mid << 1) | (side & 0x01); - pOutputSamples[i*2+0] = (ma_int16)(((ma_uint32)((ma_int32)(mid + side) >> 1) << unusedBitsPerSample) >> 16); - pOutputSamples[i*2+1] = (ma_int16)(((ma_uint32)((ma_int32)(mid - side) >> 1) << unusedBitsPerSample) >> 16); - } -} -#endif -static MA_INLINE void ma_dr_flac_read_pcm_frames_s16__decode_mid_side__scalar(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, ma_int16* pOutputSamples) -{ - ma_uint64 i; - ma_uint64 frameCount4 = frameCount >> 2; - const ma_uint32* pInputSamples0U32 = (const ma_uint32*)pInputSamples0; - const ma_uint32* pInputSamples1U32 = (const ma_uint32*)pInputSamples1; - ma_uint32 shift = unusedBitsPerSample; - if (shift > 0) { - shift -= 1; - for (i = 0; i < frameCount4; ++i) { - ma_uint32 temp0L; - ma_uint32 temp1L; - ma_uint32 temp2L; - ma_uint32 temp3L; - ma_uint32 temp0R; - ma_uint32 temp1R; - ma_uint32 temp2R; - ma_uint32 temp3R; - ma_uint32 mid0 = pInputSamples0U32[i*4+0] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; - ma_uint32 mid1 = pInputSamples0U32[i*4+1] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; - ma_uint32 mid2 = pInputSamples0U32[i*4+2] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; - ma_uint32 mid3 = pInputSamples0U32[i*4+3] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; - ma_uint32 side0 = pInputSamples1U32[i*4+0] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; - ma_uint32 side1 = pInputSamples1U32[i*4+1] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; - ma_uint32 side2 = pInputSamples1U32[i*4+2] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; - ma_uint32 side3 = pInputSamples1U32[i*4+3] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; - mid0 = (mid0 << 1) | (side0 & 0x01); - mid1 = (mid1 << 1) | (side1 & 0x01); - mid2 = (mid2 << 1) | (side2 & 0x01); - mid3 = (mid3 << 1) | (side3 & 0x01); - temp0L = (mid0 + side0) << shift; - temp1L = (mid1 + side1) << shift; - temp2L = (mid2 + side2) << shift; - temp3L = (mid3 + side3) << shift; - temp0R = (mid0 - side0) << shift; - temp1R = (mid1 - side1) << shift; - temp2R = (mid2 - side2) << shift; - temp3R = (mid3 - side3) << shift; - temp0L >>= 16; - temp1L >>= 16; - temp2L >>= 16; - temp3L >>= 16; - temp0R >>= 16; - temp1R >>= 16; - temp2R >>= 16; - temp3R >>= 16; - pOutputSamples[i*8+0] = (ma_int16)temp0L; - pOutputSamples[i*8+1] = (ma_int16)temp0R; - pOutputSamples[i*8+2] = (ma_int16)temp1L; - pOutputSamples[i*8+3] = (ma_int16)temp1R; - pOutputSamples[i*8+4] = (ma_int16)temp2L; - pOutputSamples[i*8+5] = (ma_int16)temp2R; - pOutputSamples[i*8+6] = (ma_int16)temp3L; - pOutputSamples[i*8+7] = (ma_int16)temp3R; - } - } else { - for (i = 0; i < frameCount4; ++i) { - ma_uint32 temp0L; - ma_uint32 temp1L; - ma_uint32 temp2L; - ma_uint32 temp3L; - ma_uint32 temp0R; - ma_uint32 temp1R; - ma_uint32 temp2R; - ma_uint32 temp3R; - ma_uint32 mid0 = pInputSamples0U32[i*4+0] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; - ma_uint32 mid1 = pInputSamples0U32[i*4+1] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; - ma_uint32 mid2 = pInputSamples0U32[i*4+2] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; - ma_uint32 mid3 = pInputSamples0U32[i*4+3] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; - ma_uint32 side0 = pInputSamples1U32[i*4+0] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; - ma_uint32 side1 = pInputSamples1U32[i*4+1] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; - ma_uint32 side2 = pInputSamples1U32[i*4+2] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; - ma_uint32 side3 = pInputSamples1U32[i*4+3] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; - mid0 = (mid0 << 1) | (side0 & 0x01); - mid1 = (mid1 << 1) | (side1 & 0x01); - mid2 = (mid2 << 1) | (side2 & 0x01); - mid3 = (mid3 << 1) | (side3 & 0x01); - temp0L = ((ma_int32)(mid0 + side0) >> 1); - temp1L = ((ma_int32)(mid1 + side1) >> 1); - temp2L = ((ma_int32)(mid2 + side2) >> 1); - temp3L = ((ma_int32)(mid3 + side3) >> 1); - temp0R = ((ma_int32)(mid0 - side0) >> 1); - temp1R = ((ma_int32)(mid1 - side1) >> 1); - temp2R = ((ma_int32)(mid2 - side2) >> 1); - temp3R = ((ma_int32)(mid3 - side3) >> 1); - temp0L >>= 16; - temp1L >>= 16; - temp2L >>= 16; - temp3L >>= 16; - temp0R >>= 16; - temp1R >>= 16; - temp2R >>= 16; - temp3R >>= 16; - pOutputSamples[i*8+0] = (ma_int16)temp0L; - pOutputSamples[i*8+1] = (ma_int16)temp0R; - pOutputSamples[i*8+2] = (ma_int16)temp1L; - pOutputSamples[i*8+3] = (ma_int16)temp1R; - pOutputSamples[i*8+4] = (ma_int16)temp2L; - pOutputSamples[i*8+5] = (ma_int16)temp2R; - pOutputSamples[i*8+6] = (ma_int16)temp3L; - pOutputSamples[i*8+7] = (ma_int16)temp3R; - } - } - for (i = (frameCount4 << 2); i < frameCount; ++i) { - ma_uint32 mid = pInputSamples0U32[i] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; - ma_uint32 side = pInputSamples1U32[i] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; - mid = (mid << 1) | (side & 0x01); - pOutputSamples[i*2+0] = (ma_int16)(((ma_uint32)((ma_int32)(mid + side) >> 1) << unusedBitsPerSample) >> 16); - pOutputSamples[i*2+1] = (ma_int16)(((ma_uint32)((ma_int32)(mid - side) >> 1) << unusedBitsPerSample) >> 16); - } -} -#if defined(MA_DR_FLAC_SUPPORT_SSE2) -static MA_INLINE void ma_dr_flac_read_pcm_frames_s16__decode_mid_side__sse2(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, ma_int16* pOutputSamples) -{ - ma_uint64 i; - ma_uint64 frameCount4 = frameCount >> 2; - const ma_uint32* pInputSamples0U32 = (const ma_uint32*)pInputSamples0; - const ma_uint32* pInputSamples1U32 = (const ma_uint32*)pInputSamples1; - ma_uint32 shift = unusedBitsPerSample; - MA_DR_FLAC_ASSERT(pFlac->bitsPerSample <= 24); - if (shift == 0) { - for (i = 0; i < frameCount4; ++i) { - __m128i mid; - __m128i side; - __m128i left; - __m128i right; - mid = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples0 + i), pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample); - side = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples1 + i), pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample); - mid = _mm_or_si128(_mm_slli_epi32(mid, 1), _mm_and_si128(side, _mm_set1_epi32(0x01))); - left = _mm_srai_epi32(_mm_add_epi32(mid, side), 1); - right = _mm_srai_epi32(_mm_sub_epi32(mid, side), 1); - left = _mm_srai_epi32(left, 16); - right = _mm_srai_epi32(right, 16); - _mm_storeu_si128((__m128i*)(pOutputSamples + i*8), ma_dr_flac__mm_packs_interleaved_epi32(left, right)); - } - for (i = (frameCount4 << 2); i < frameCount; ++i) { - ma_uint32 mid = pInputSamples0U32[i] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; - ma_uint32 side = pInputSamples1U32[i] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; - mid = (mid << 1) | (side & 0x01); - pOutputSamples[i*2+0] = (ma_int16)(((ma_int32)(mid + side) >> 1) >> 16); - pOutputSamples[i*2+1] = (ma_int16)(((ma_int32)(mid - side) >> 1) >> 16); - } - } else { - shift -= 1; - for (i = 0; i < frameCount4; ++i) { - __m128i mid; - __m128i side; - __m128i left; - __m128i right; - mid = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples0 + i), pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample); - side = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples1 + i), pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample); - mid = _mm_or_si128(_mm_slli_epi32(mid, 1), _mm_and_si128(side, _mm_set1_epi32(0x01))); - left = _mm_slli_epi32(_mm_add_epi32(mid, side), shift); - right = _mm_slli_epi32(_mm_sub_epi32(mid, side), shift); - left = _mm_srai_epi32(left, 16); - right = _mm_srai_epi32(right, 16); - _mm_storeu_si128((__m128i*)(pOutputSamples + i*8), ma_dr_flac__mm_packs_interleaved_epi32(left, right)); - } - for (i = (frameCount4 << 2); i < frameCount; ++i) { - ma_uint32 mid = pInputSamples0U32[i] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; - ma_uint32 side = pInputSamples1U32[i] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; - mid = (mid << 1) | (side & 0x01); - pOutputSamples[i*2+0] = (ma_int16)(((mid + side) << shift) >> 16); - pOutputSamples[i*2+1] = (ma_int16)(((mid - side) << shift) >> 16); - } - } -} -#endif -#if defined(MA_DR_FLAC_SUPPORT_NEON) -static MA_INLINE void ma_dr_flac_read_pcm_frames_s16__decode_mid_side__neon(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, ma_int16* pOutputSamples) -{ - ma_uint64 i; - ma_uint64 frameCount4 = frameCount >> 2; - const ma_uint32* pInputSamples0U32 = (const ma_uint32*)pInputSamples0; - const ma_uint32* pInputSamples1U32 = (const ma_uint32*)pInputSamples1; - ma_uint32 shift = unusedBitsPerSample; - int32x4_t wbpsShift0_4; - int32x4_t wbpsShift1_4; - MA_DR_FLAC_ASSERT(pFlac->bitsPerSample <= 24); - wbpsShift0_4 = vdupq_n_s32(pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample); - wbpsShift1_4 = vdupq_n_s32(pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample); - if (shift == 0) { - for (i = 0; i < frameCount4; ++i) { - uint32x4_t mid; - uint32x4_t side; - int32x4_t left; - int32x4_t right; - mid = vshlq_u32(vld1q_u32(pInputSamples0U32 + i*4), wbpsShift0_4); - side = vshlq_u32(vld1q_u32(pInputSamples1U32 + i*4), wbpsShift1_4); - mid = vorrq_u32(vshlq_n_u32(mid, 1), vandq_u32(side, vdupq_n_u32(1))); - left = vshrq_n_s32(vreinterpretq_s32_u32(vaddq_u32(mid, side)), 1); - right = vshrq_n_s32(vreinterpretq_s32_u32(vsubq_u32(mid, side)), 1); - left = vshrq_n_s32(left, 16); - right = vshrq_n_s32(right, 16); - ma_dr_flac__vst2q_s16(pOutputSamples + i*8, vzip_s16(vmovn_s32(left), vmovn_s32(right))); - } - for (i = (frameCount4 << 2); i < frameCount; ++i) { - ma_uint32 mid = pInputSamples0U32[i] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; - ma_uint32 side = pInputSamples1U32[i] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; - mid = (mid << 1) | (side & 0x01); - pOutputSamples[i*2+0] = (ma_int16)(((ma_int32)(mid + side) >> 1) >> 16); - pOutputSamples[i*2+1] = (ma_int16)(((ma_int32)(mid - side) >> 1) >> 16); - } - } else { - int32x4_t shift4; - shift -= 1; - shift4 = vdupq_n_s32(shift); - for (i = 0; i < frameCount4; ++i) { - uint32x4_t mid; - uint32x4_t side; - int32x4_t left; - int32x4_t right; - mid = vshlq_u32(vld1q_u32(pInputSamples0U32 + i*4), wbpsShift0_4); - side = vshlq_u32(vld1q_u32(pInputSamples1U32 + i*4), wbpsShift1_4); - mid = vorrq_u32(vshlq_n_u32(mid, 1), vandq_u32(side, vdupq_n_u32(1))); - left = vreinterpretq_s32_u32(vshlq_u32(vaddq_u32(mid, side), shift4)); - right = vreinterpretq_s32_u32(vshlq_u32(vsubq_u32(mid, side), shift4)); - left = vshrq_n_s32(left, 16); - right = vshrq_n_s32(right, 16); - ma_dr_flac__vst2q_s16(pOutputSamples + i*8, vzip_s16(vmovn_s32(left), vmovn_s32(right))); - } - for (i = (frameCount4 << 2); i < frameCount; ++i) { - ma_uint32 mid = pInputSamples0U32[i] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; - ma_uint32 side = pInputSamples1U32[i] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; - mid = (mid << 1) | (side & 0x01); - pOutputSamples[i*2+0] = (ma_int16)(((mid + side) << shift) >> 16); - pOutputSamples[i*2+1] = (ma_int16)(((mid - side) << shift) >> 16); - } - } -} -#endif -static MA_INLINE void ma_dr_flac_read_pcm_frames_s16__decode_mid_side(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, ma_int16* pOutputSamples) -{ -#if defined(MA_DR_FLAC_SUPPORT_SSE2) - if (ma_dr_flac__gIsSSE2Supported && pFlac->bitsPerSample <= 24) { - ma_dr_flac_read_pcm_frames_s16__decode_mid_side__sse2(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); - } else -#elif defined(MA_DR_FLAC_SUPPORT_NEON) - if (ma_dr_flac__gIsNEONSupported && pFlac->bitsPerSample <= 24) { - ma_dr_flac_read_pcm_frames_s16__decode_mid_side__neon(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); - } else -#endif - { -#if 0 - ma_dr_flac_read_pcm_frames_s16__decode_mid_side__reference(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); -#else - ma_dr_flac_read_pcm_frames_s16__decode_mid_side__scalar(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); -#endif - } -} -#if 0 -static MA_INLINE void ma_dr_flac_read_pcm_frames_s16__decode_independent_stereo__reference(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, ma_int16* pOutputSamples) -{ - for (ma_uint64 i = 0; i < frameCount; ++i) { - pOutputSamples[i*2+0] = (ma_int16)((ma_int32)((ma_uint32)pInputSamples0[i] << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample)) >> 16); - pOutputSamples[i*2+1] = (ma_int16)((ma_int32)((ma_uint32)pInputSamples1[i] << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample)) >> 16); - } -} -#endif -static MA_INLINE void ma_dr_flac_read_pcm_frames_s16__decode_independent_stereo__scalar(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, ma_int16* pOutputSamples) -{ - ma_uint64 i; - ma_uint64 frameCount4 = frameCount >> 2; - const ma_uint32* pInputSamples0U32 = (const ma_uint32*)pInputSamples0; - const ma_uint32* pInputSamples1U32 = (const ma_uint32*)pInputSamples1; - ma_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; - ma_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; - for (i = 0; i < frameCount4; ++i) { - ma_uint32 tempL0 = pInputSamples0U32[i*4+0] << shift0; - ma_uint32 tempL1 = pInputSamples0U32[i*4+1] << shift0; - ma_uint32 tempL2 = pInputSamples0U32[i*4+2] << shift0; - ma_uint32 tempL3 = pInputSamples0U32[i*4+3] << shift0; - ma_uint32 tempR0 = pInputSamples1U32[i*4+0] << shift1; - ma_uint32 tempR1 = pInputSamples1U32[i*4+1] << shift1; - ma_uint32 tempR2 = pInputSamples1U32[i*4+2] << shift1; - ma_uint32 tempR3 = pInputSamples1U32[i*4+3] << shift1; - tempL0 >>= 16; - tempL1 >>= 16; - tempL2 >>= 16; - tempL3 >>= 16; - tempR0 >>= 16; - tempR1 >>= 16; - tempR2 >>= 16; - tempR3 >>= 16; - pOutputSamples[i*8+0] = (ma_int16)tempL0; - pOutputSamples[i*8+1] = (ma_int16)tempR0; - pOutputSamples[i*8+2] = (ma_int16)tempL1; - pOutputSamples[i*8+3] = (ma_int16)tempR1; - pOutputSamples[i*8+4] = (ma_int16)tempL2; - pOutputSamples[i*8+5] = (ma_int16)tempR2; - pOutputSamples[i*8+6] = (ma_int16)tempL3; - pOutputSamples[i*8+7] = (ma_int16)tempR3; - } - for (i = (frameCount4 << 2); i < frameCount; ++i) { - pOutputSamples[i*2+0] = (ma_int16)((pInputSamples0U32[i] << shift0) >> 16); - pOutputSamples[i*2+1] = (ma_int16)((pInputSamples1U32[i] << shift1) >> 16); - } -} -#if defined(MA_DR_FLAC_SUPPORT_SSE2) -static MA_INLINE void ma_dr_flac_read_pcm_frames_s16__decode_independent_stereo__sse2(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, ma_int16* pOutputSamples) -{ - ma_uint64 i; - ma_uint64 frameCount4 = frameCount >> 2; - const ma_uint32* pInputSamples0U32 = (const ma_uint32*)pInputSamples0; - const ma_uint32* pInputSamples1U32 = (const ma_uint32*)pInputSamples1; - ma_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; - ma_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; - for (i = 0; i < frameCount4; ++i) { - __m128i left = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples0 + i), shift0); - __m128i right = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples1 + i), shift1); - left = _mm_srai_epi32(left, 16); - right = _mm_srai_epi32(right, 16); - _mm_storeu_si128((__m128i*)(pOutputSamples + i*8), ma_dr_flac__mm_packs_interleaved_epi32(left, right)); - } - for (i = (frameCount4 << 2); i < frameCount; ++i) { - pOutputSamples[i*2+0] = (ma_int16)((pInputSamples0U32[i] << shift0) >> 16); - pOutputSamples[i*2+1] = (ma_int16)((pInputSamples1U32[i] << shift1) >> 16); - } -} -#endif -#if defined(MA_DR_FLAC_SUPPORT_NEON) -static MA_INLINE void ma_dr_flac_read_pcm_frames_s16__decode_independent_stereo__neon(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, ma_int16* pOutputSamples) -{ - ma_uint64 i; - ma_uint64 frameCount4 = frameCount >> 2; - const ma_uint32* pInputSamples0U32 = (const ma_uint32*)pInputSamples0; - const ma_uint32* pInputSamples1U32 = (const ma_uint32*)pInputSamples1; - ma_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; - ma_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; - int32x4_t shift0_4 = vdupq_n_s32(shift0); - int32x4_t shift1_4 = vdupq_n_s32(shift1); - for (i = 0; i < frameCount4; ++i) { - int32x4_t left; - int32x4_t right; - left = vreinterpretq_s32_u32(vshlq_u32(vld1q_u32(pInputSamples0U32 + i*4), shift0_4)); - right = vreinterpretq_s32_u32(vshlq_u32(vld1q_u32(pInputSamples1U32 + i*4), shift1_4)); - left = vshrq_n_s32(left, 16); - right = vshrq_n_s32(right, 16); - ma_dr_flac__vst2q_s16(pOutputSamples + i*8, vzip_s16(vmovn_s32(left), vmovn_s32(right))); - } - for (i = (frameCount4 << 2); i < frameCount; ++i) { - pOutputSamples[i*2+0] = (ma_int16)((pInputSamples0U32[i] << shift0) >> 16); - pOutputSamples[i*2+1] = (ma_int16)((pInputSamples1U32[i] << shift1) >> 16); - } -} -#endif -static MA_INLINE void ma_dr_flac_read_pcm_frames_s16__decode_independent_stereo(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, ma_int16* pOutputSamples) -{ -#if defined(MA_DR_FLAC_SUPPORT_SSE2) - if (ma_dr_flac__gIsSSE2Supported && pFlac->bitsPerSample <= 24) { - ma_dr_flac_read_pcm_frames_s16__decode_independent_stereo__sse2(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); - } else -#elif defined(MA_DR_FLAC_SUPPORT_NEON) - if (ma_dr_flac__gIsNEONSupported && pFlac->bitsPerSample <= 24) { - ma_dr_flac_read_pcm_frames_s16__decode_independent_stereo__neon(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); - } else -#endif - { -#if 0 - ma_dr_flac_read_pcm_frames_s16__decode_independent_stereo__reference(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); -#else - ma_dr_flac_read_pcm_frames_s16__decode_independent_stereo__scalar(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); -#endif - } -} -MA_API ma_uint64 ma_dr_flac_read_pcm_frames_s16(ma_dr_flac* pFlac, ma_uint64 framesToRead, ma_int16* pBufferOut) -{ - ma_uint64 framesRead; - ma_uint32 unusedBitsPerSample; - if (pFlac == NULL || framesToRead == 0) { - return 0; - } - if (pBufferOut == NULL) { - return ma_dr_flac__seek_forward_by_pcm_frames(pFlac, framesToRead); - } - MA_DR_FLAC_ASSERT(pFlac->bitsPerSample <= 32); - unusedBitsPerSample = 32 - pFlac->bitsPerSample; - framesRead = 0; - while (framesToRead > 0) { - if (pFlac->currentFLACFrame.pcmFramesRemaining == 0) { - if (!ma_dr_flac__read_and_decode_next_flac_frame(pFlac)) { - break; - } - } else { - unsigned int channelCount = ma_dr_flac__get_channel_count_from_channel_assignment(pFlac->currentFLACFrame.header.channelAssignment); - ma_uint64 iFirstPCMFrame = pFlac->currentFLACFrame.header.blockSizeInPCMFrames - pFlac->currentFLACFrame.pcmFramesRemaining; - ma_uint64 frameCountThisIteration = framesToRead; - if (frameCountThisIteration > pFlac->currentFLACFrame.pcmFramesRemaining) { - frameCountThisIteration = pFlac->currentFLACFrame.pcmFramesRemaining; - } - if (channelCount == 2) { - const ma_int32* pDecodedSamples0 = pFlac->currentFLACFrame.subframes[0].pSamplesS32 + iFirstPCMFrame; - const ma_int32* pDecodedSamples1 = pFlac->currentFLACFrame.subframes[1].pSamplesS32 + iFirstPCMFrame; - switch (pFlac->currentFLACFrame.header.channelAssignment) - { - case MA_DR_FLAC_CHANNEL_ASSIGNMENT_LEFT_SIDE: - { - ma_dr_flac_read_pcm_frames_s16__decode_left_side(pFlac, frameCountThisIteration, unusedBitsPerSample, pDecodedSamples0, pDecodedSamples1, pBufferOut); - } break; - case MA_DR_FLAC_CHANNEL_ASSIGNMENT_RIGHT_SIDE: - { - ma_dr_flac_read_pcm_frames_s16__decode_right_side(pFlac, frameCountThisIteration, unusedBitsPerSample, pDecodedSamples0, pDecodedSamples1, pBufferOut); - } break; - case MA_DR_FLAC_CHANNEL_ASSIGNMENT_MID_SIDE: - { - ma_dr_flac_read_pcm_frames_s16__decode_mid_side(pFlac, frameCountThisIteration, unusedBitsPerSample, pDecodedSamples0, pDecodedSamples1, pBufferOut); - } break; - case MA_DR_FLAC_CHANNEL_ASSIGNMENT_INDEPENDENT: - default: - { - ma_dr_flac_read_pcm_frames_s16__decode_independent_stereo(pFlac, frameCountThisIteration, unusedBitsPerSample, pDecodedSamples0, pDecodedSamples1, pBufferOut); - } break; - } - } else { - ma_uint64 i; - for (i = 0; i < frameCountThisIteration; ++i) { - unsigned int j; - for (j = 0; j < channelCount; ++j) { - ma_int32 sampleS32 = (ma_int32)((ma_uint32)(pFlac->currentFLACFrame.subframes[j].pSamplesS32[iFirstPCMFrame + i]) << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[j].wastedBitsPerSample)); - pBufferOut[(i*channelCount)+j] = (ma_int16)(sampleS32 >> 16); - } - } - } - framesRead += frameCountThisIteration; - pBufferOut += frameCountThisIteration * channelCount; - framesToRead -= frameCountThisIteration; - pFlac->currentPCMFrame += frameCountThisIteration; - pFlac->currentFLACFrame.pcmFramesRemaining -= (ma_uint32)frameCountThisIteration; - } - } - return framesRead; -} -#if 0 -static MA_INLINE void ma_dr_flac_read_pcm_frames_f32__decode_left_side__reference(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, float* pOutputSamples) -{ - ma_uint64 i; - for (i = 0; i < frameCount; ++i) { - ma_uint32 left = (ma_uint32)pInputSamples0[i] << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample); - ma_uint32 side = (ma_uint32)pInputSamples1[i] << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample); - ma_uint32 right = left - side; - pOutputSamples[i*2+0] = (float)((ma_int32)left / 2147483648.0); - pOutputSamples[i*2+1] = (float)((ma_int32)right / 2147483648.0); - } -} -#endif -static MA_INLINE void ma_dr_flac_read_pcm_frames_f32__decode_left_side__scalar(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, float* pOutputSamples) -{ - ma_uint64 i; - ma_uint64 frameCount4 = frameCount >> 2; - const ma_uint32* pInputSamples0U32 = (const ma_uint32*)pInputSamples0; - const ma_uint32* pInputSamples1U32 = (const ma_uint32*)pInputSamples1; - ma_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; - ma_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; - float factor = 1 / 2147483648.0; - for (i = 0; i < frameCount4; ++i) { - ma_uint32 left0 = pInputSamples0U32[i*4+0] << shift0; - ma_uint32 left1 = pInputSamples0U32[i*4+1] << shift0; - ma_uint32 left2 = pInputSamples0U32[i*4+2] << shift0; - ma_uint32 left3 = pInputSamples0U32[i*4+3] << shift0; - ma_uint32 side0 = pInputSamples1U32[i*4+0] << shift1; - ma_uint32 side1 = pInputSamples1U32[i*4+1] << shift1; - ma_uint32 side2 = pInputSamples1U32[i*4+2] << shift1; - ma_uint32 side3 = pInputSamples1U32[i*4+3] << shift1; - ma_uint32 right0 = left0 - side0; - ma_uint32 right1 = left1 - side1; - ma_uint32 right2 = left2 - side2; - ma_uint32 right3 = left3 - side3; - pOutputSamples[i*8+0] = (ma_int32)left0 * factor; - pOutputSamples[i*8+1] = (ma_int32)right0 * factor; - pOutputSamples[i*8+2] = (ma_int32)left1 * factor; - pOutputSamples[i*8+3] = (ma_int32)right1 * factor; - pOutputSamples[i*8+4] = (ma_int32)left2 * factor; - pOutputSamples[i*8+5] = (ma_int32)right2 * factor; - pOutputSamples[i*8+6] = (ma_int32)left3 * factor; - pOutputSamples[i*8+7] = (ma_int32)right3 * factor; - } - for (i = (frameCount4 << 2); i < frameCount; ++i) { - ma_uint32 left = pInputSamples0U32[i] << shift0; - ma_uint32 side = pInputSamples1U32[i] << shift1; - ma_uint32 right = left - side; - pOutputSamples[i*2+0] = (ma_int32)left * factor; - pOutputSamples[i*2+1] = (ma_int32)right * factor; - } -} -#if defined(MA_DR_FLAC_SUPPORT_SSE2) -static MA_INLINE void ma_dr_flac_read_pcm_frames_f32__decode_left_side__sse2(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, float* pOutputSamples) -{ - ma_uint64 i; - ma_uint64 frameCount4 = frameCount >> 2; - const ma_uint32* pInputSamples0U32 = (const ma_uint32*)pInputSamples0; - const ma_uint32* pInputSamples1U32 = (const ma_uint32*)pInputSamples1; - ma_uint32 shift0 = (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample) - 8; - ma_uint32 shift1 = (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample) - 8; - __m128 factor; - MA_DR_FLAC_ASSERT(pFlac->bitsPerSample <= 24); - factor = _mm_set1_ps(1.0f / 8388608.0f); - for (i = 0; i < frameCount4; ++i) { - __m128i left = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples0 + i), shift0); - __m128i side = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples1 + i), shift1); - __m128i right = _mm_sub_epi32(left, side); - __m128 leftf = _mm_mul_ps(_mm_cvtepi32_ps(left), factor); - __m128 rightf = _mm_mul_ps(_mm_cvtepi32_ps(right), factor); - _mm_storeu_ps(pOutputSamples + i*8 + 0, _mm_unpacklo_ps(leftf, rightf)); - _mm_storeu_ps(pOutputSamples + i*8 + 4, _mm_unpackhi_ps(leftf, rightf)); - } - for (i = (frameCount4 << 2); i < frameCount; ++i) { - ma_uint32 left = pInputSamples0U32[i] << shift0; - ma_uint32 side = pInputSamples1U32[i] << shift1; - ma_uint32 right = left - side; - pOutputSamples[i*2+0] = (ma_int32)left / 8388608.0f; - pOutputSamples[i*2+1] = (ma_int32)right / 8388608.0f; - } -} -#endif -#if defined(MA_DR_FLAC_SUPPORT_NEON) -static MA_INLINE void ma_dr_flac_read_pcm_frames_f32__decode_left_side__neon(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, float* pOutputSamples) -{ - ma_uint64 i; - ma_uint64 frameCount4 = frameCount >> 2; - const ma_uint32* pInputSamples0U32 = (const ma_uint32*)pInputSamples0; - const ma_uint32* pInputSamples1U32 = (const ma_uint32*)pInputSamples1; - ma_uint32 shift0 = (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample) - 8; - ma_uint32 shift1 = (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample) - 8; - float32x4_t factor4; - int32x4_t shift0_4; - int32x4_t shift1_4; - MA_DR_FLAC_ASSERT(pFlac->bitsPerSample <= 24); - factor4 = vdupq_n_f32(1.0f / 8388608.0f); - shift0_4 = vdupq_n_s32(shift0); - shift1_4 = vdupq_n_s32(shift1); - for (i = 0; i < frameCount4; ++i) { - uint32x4_t left; - uint32x4_t side; - uint32x4_t right; - float32x4_t leftf; - float32x4_t rightf; - left = vshlq_u32(vld1q_u32(pInputSamples0U32 + i*4), shift0_4); - side = vshlq_u32(vld1q_u32(pInputSamples1U32 + i*4), shift1_4); - right = vsubq_u32(left, side); - leftf = vmulq_f32(vcvtq_f32_s32(vreinterpretq_s32_u32(left)), factor4); - rightf = vmulq_f32(vcvtq_f32_s32(vreinterpretq_s32_u32(right)), factor4); - ma_dr_flac__vst2q_f32(pOutputSamples + i*8, vzipq_f32(leftf, rightf)); - } - for (i = (frameCount4 << 2); i < frameCount; ++i) { - ma_uint32 left = pInputSamples0U32[i] << shift0; - ma_uint32 side = pInputSamples1U32[i] << shift1; - ma_uint32 right = left - side; - pOutputSamples[i*2+0] = (ma_int32)left / 8388608.0f; - pOutputSamples[i*2+1] = (ma_int32)right / 8388608.0f; - } -} -#endif -static MA_INLINE void ma_dr_flac_read_pcm_frames_f32__decode_left_side(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, float* pOutputSamples) -{ -#if defined(MA_DR_FLAC_SUPPORT_SSE2) - if (ma_dr_flac__gIsSSE2Supported && pFlac->bitsPerSample <= 24) { - ma_dr_flac_read_pcm_frames_f32__decode_left_side__sse2(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); - } else -#elif defined(MA_DR_FLAC_SUPPORT_NEON) - if (ma_dr_flac__gIsNEONSupported && pFlac->bitsPerSample <= 24) { - ma_dr_flac_read_pcm_frames_f32__decode_left_side__neon(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); - } else -#endif - { -#if 0 - ma_dr_flac_read_pcm_frames_f32__decode_left_side__reference(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); -#else - ma_dr_flac_read_pcm_frames_f32__decode_left_side__scalar(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); -#endif - } -} -#if 0 -static MA_INLINE void ma_dr_flac_read_pcm_frames_f32__decode_right_side__reference(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, float* pOutputSamples) -{ - ma_uint64 i; - for (i = 0; i < frameCount; ++i) { - ma_uint32 side = (ma_uint32)pInputSamples0[i] << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample); - ma_uint32 right = (ma_uint32)pInputSamples1[i] << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample); - ma_uint32 left = right + side; - pOutputSamples[i*2+0] = (float)((ma_int32)left / 2147483648.0); - pOutputSamples[i*2+1] = (float)((ma_int32)right / 2147483648.0); - } -} -#endif -static MA_INLINE void ma_dr_flac_read_pcm_frames_f32__decode_right_side__scalar(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, float* pOutputSamples) -{ - ma_uint64 i; - ma_uint64 frameCount4 = frameCount >> 2; - const ma_uint32* pInputSamples0U32 = (const ma_uint32*)pInputSamples0; - const ma_uint32* pInputSamples1U32 = (const ma_uint32*)pInputSamples1; - ma_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; - ma_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; - float factor = 1 / 2147483648.0; - for (i = 0; i < frameCount4; ++i) { - ma_uint32 side0 = pInputSamples0U32[i*4+0] << shift0; - ma_uint32 side1 = pInputSamples0U32[i*4+1] << shift0; - ma_uint32 side2 = pInputSamples0U32[i*4+2] << shift0; - ma_uint32 side3 = pInputSamples0U32[i*4+3] << shift0; - ma_uint32 right0 = pInputSamples1U32[i*4+0] << shift1; - ma_uint32 right1 = pInputSamples1U32[i*4+1] << shift1; - ma_uint32 right2 = pInputSamples1U32[i*4+2] << shift1; - ma_uint32 right3 = pInputSamples1U32[i*4+3] << shift1; - ma_uint32 left0 = right0 + side0; - ma_uint32 left1 = right1 + side1; - ma_uint32 left2 = right2 + side2; - ma_uint32 left3 = right3 + side3; - pOutputSamples[i*8+0] = (ma_int32)left0 * factor; - pOutputSamples[i*8+1] = (ma_int32)right0 * factor; - pOutputSamples[i*8+2] = (ma_int32)left1 * factor; - pOutputSamples[i*8+3] = (ma_int32)right1 * factor; - pOutputSamples[i*8+4] = (ma_int32)left2 * factor; - pOutputSamples[i*8+5] = (ma_int32)right2 * factor; - pOutputSamples[i*8+6] = (ma_int32)left3 * factor; - pOutputSamples[i*8+7] = (ma_int32)right3 * factor; - } - for (i = (frameCount4 << 2); i < frameCount; ++i) { - ma_uint32 side = pInputSamples0U32[i] << shift0; - ma_uint32 right = pInputSamples1U32[i] << shift1; - ma_uint32 left = right + side; - pOutputSamples[i*2+0] = (ma_int32)left * factor; - pOutputSamples[i*2+1] = (ma_int32)right * factor; - } -} -#if defined(MA_DR_FLAC_SUPPORT_SSE2) -static MA_INLINE void ma_dr_flac_read_pcm_frames_f32__decode_right_side__sse2(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, float* pOutputSamples) -{ - ma_uint64 i; - ma_uint64 frameCount4 = frameCount >> 2; - const ma_uint32* pInputSamples0U32 = (const ma_uint32*)pInputSamples0; - const ma_uint32* pInputSamples1U32 = (const ma_uint32*)pInputSamples1; - ma_uint32 shift0 = (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample) - 8; - ma_uint32 shift1 = (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample) - 8; - __m128 factor; - MA_DR_FLAC_ASSERT(pFlac->bitsPerSample <= 24); - factor = _mm_set1_ps(1.0f / 8388608.0f); - for (i = 0; i < frameCount4; ++i) { - __m128i side = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples0 + i), shift0); - __m128i right = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples1 + i), shift1); - __m128i left = _mm_add_epi32(right, side); - __m128 leftf = _mm_mul_ps(_mm_cvtepi32_ps(left), factor); - __m128 rightf = _mm_mul_ps(_mm_cvtepi32_ps(right), factor); - _mm_storeu_ps(pOutputSamples + i*8 + 0, _mm_unpacklo_ps(leftf, rightf)); - _mm_storeu_ps(pOutputSamples + i*8 + 4, _mm_unpackhi_ps(leftf, rightf)); - } - for (i = (frameCount4 << 2); i < frameCount; ++i) { - ma_uint32 side = pInputSamples0U32[i] << shift0; - ma_uint32 right = pInputSamples1U32[i] << shift1; - ma_uint32 left = right + side; - pOutputSamples[i*2+0] = (ma_int32)left / 8388608.0f; - pOutputSamples[i*2+1] = (ma_int32)right / 8388608.0f; - } -} -#endif -#if defined(MA_DR_FLAC_SUPPORT_NEON) -static MA_INLINE void ma_dr_flac_read_pcm_frames_f32__decode_right_side__neon(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, float* pOutputSamples) -{ - ma_uint64 i; - ma_uint64 frameCount4 = frameCount >> 2; - const ma_uint32* pInputSamples0U32 = (const ma_uint32*)pInputSamples0; - const ma_uint32* pInputSamples1U32 = (const ma_uint32*)pInputSamples1; - ma_uint32 shift0 = (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample) - 8; - ma_uint32 shift1 = (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample) - 8; - float32x4_t factor4; - int32x4_t shift0_4; - int32x4_t shift1_4; - MA_DR_FLAC_ASSERT(pFlac->bitsPerSample <= 24); - factor4 = vdupq_n_f32(1.0f / 8388608.0f); - shift0_4 = vdupq_n_s32(shift0); - shift1_4 = vdupq_n_s32(shift1); - for (i = 0; i < frameCount4; ++i) { - uint32x4_t side; - uint32x4_t right; - uint32x4_t left; - float32x4_t leftf; - float32x4_t rightf; - side = vshlq_u32(vld1q_u32(pInputSamples0U32 + i*4), shift0_4); - right = vshlq_u32(vld1q_u32(pInputSamples1U32 + i*4), shift1_4); - left = vaddq_u32(right, side); - leftf = vmulq_f32(vcvtq_f32_s32(vreinterpretq_s32_u32(left)), factor4); - rightf = vmulq_f32(vcvtq_f32_s32(vreinterpretq_s32_u32(right)), factor4); - ma_dr_flac__vst2q_f32(pOutputSamples + i*8, vzipq_f32(leftf, rightf)); - } - for (i = (frameCount4 << 2); i < frameCount; ++i) { - ma_uint32 side = pInputSamples0U32[i] << shift0; - ma_uint32 right = pInputSamples1U32[i] << shift1; - ma_uint32 left = right + side; - pOutputSamples[i*2+0] = (ma_int32)left / 8388608.0f; - pOutputSamples[i*2+1] = (ma_int32)right / 8388608.0f; - } -} -#endif -static MA_INLINE void ma_dr_flac_read_pcm_frames_f32__decode_right_side(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, float* pOutputSamples) -{ -#if defined(MA_DR_FLAC_SUPPORT_SSE2) - if (ma_dr_flac__gIsSSE2Supported && pFlac->bitsPerSample <= 24) { - ma_dr_flac_read_pcm_frames_f32__decode_right_side__sse2(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); - } else -#elif defined(MA_DR_FLAC_SUPPORT_NEON) - if (ma_dr_flac__gIsNEONSupported && pFlac->bitsPerSample <= 24) { - ma_dr_flac_read_pcm_frames_f32__decode_right_side__neon(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); - } else -#endif - { -#if 0 - ma_dr_flac_read_pcm_frames_f32__decode_right_side__reference(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); -#else - ma_dr_flac_read_pcm_frames_f32__decode_right_side__scalar(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); -#endif - } -} -#if 0 -static MA_INLINE void ma_dr_flac_read_pcm_frames_f32__decode_mid_side__reference(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, float* pOutputSamples) -{ - for (ma_uint64 i = 0; i < frameCount; ++i) { - ma_uint32 mid = (ma_uint32)pInputSamples0[i] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; - ma_uint32 side = (ma_uint32)pInputSamples1[i] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; - mid = (mid << 1) | (side & 0x01); - pOutputSamples[i*2+0] = (float)((((ma_int32)(mid + side) >> 1) << (unusedBitsPerSample)) / 2147483648.0); - pOutputSamples[i*2+1] = (float)((((ma_int32)(mid - side) >> 1) << (unusedBitsPerSample)) / 2147483648.0); - } -} -#endif -static MA_INLINE void ma_dr_flac_read_pcm_frames_f32__decode_mid_side__scalar(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, float* pOutputSamples) -{ - ma_uint64 i; - ma_uint64 frameCount4 = frameCount >> 2; - const ma_uint32* pInputSamples0U32 = (const ma_uint32*)pInputSamples0; - const ma_uint32* pInputSamples1U32 = (const ma_uint32*)pInputSamples1; - ma_uint32 shift = unusedBitsPerSample; - float factor = 1 / 2147483648.0; - if (shift > 0) { - shift -= 1; - for (i = 0; i < frameCount4; ++i) { - ma_uint32 temp0L; - ma_uint32 temp1L; - ma_uint32 temp2L; - ma_uint32 temp3L; - ma_uint32 temp0R; - ma_uint32 temp1R; - ma_uint32 temp2R; - ma_uint32 temp3R; - ma_uint32 mid0 = pInputSamples0U32[i*4+0] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; - ma_uint32 mid1 = pInputSamples0U32[i*4+1] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; - ma_uint32 mid2 = pInputSamples0U32[i*4+2] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; - ma_uint32 mid3 = pInputSamples0U32[i*4+3] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; - ma_uint32 side0 = pInputSamples1U32[i*4+0] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; - ma_uint32 side1 = pInputSamples1U32[i*4+1] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; - ma_uint32 side2 = pInputSamples1U32[i*4+2] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; - ma_uint32 side3 = pInputSamples1U32[i*4+3] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; - mid0 = (mid0 << 1) | (side0 & 0x01); - mid1 = (mid1 << 1) | (side1 & 0x01); - mid2 = (mid2 << 1) | (side2 & 0x01); - mid3 = (mid3 << 1) | (side3 & 0x01); - temp0L = (mid0 + side0) << shift; - temp1L = (mid1 + side1) << shift; - temp2L = (mid2 + side2) << shift; - temp3L = (mid3 + side3) << shift; - temp0R = (mid0 - side0) << shift; - temp1R = (mid1 - side1) << shift; - temp2R = (mid2 - side2) << shift; - temp3R = (mid3 - side3) << shift; - pOutputSamples[i*8+0] = (ma_int32)temp0L * factor; - pOutputSamples[i*8+1] = (ma_int32)temp0R * factor; - pOutputSamples[i*8+2] = (ma_int32)temp1L * factor; - pOutputSamples[i*8+3] = (ma_int32)temp1R * factor; - pOutputSamples[i*8+4] = (ma_int32)temp2L * factor; - pOutputSamples[i*8+5] = (ma_int32)temp2R * factor; - pOutputSamples[i*8+6] = (ma_int32)temp3L * factor; - pOutputSamples[i*8+7] = (ma_int32)temp3R * factor; - } - } else { - for (i = 0; i < frameCount4; ++i) { - ma_uint32 temp0L; - ma_uint32 temp1L; - ma_uint32 temp2L; - ma_uint32 temp3L; - ma_uint32 temp0R; - ma_uint32 temp1R; - ma_uint32 temp2R; - ma_uint32 temp3R; - ma_uint32 mid0 = pInputSamples0U32[i*4+0] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; - ma_uint32 mid1 = pInputSamples0U32[i*4+1] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; - ma_uint32 mid2 = pInputSamples0U32[i*4+2] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; - ma_uint32 mid3 = pInputSamples0U32[i*4+3] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; - ma_uint32 side0 = pInputSamples1U32[i*4+0] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; - ma_uint32 side1 = pInputSamples1U32[i*4+1] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; - ma_uint32 side2 = pInputSamples1U32[i*4+2] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; - ma_uint32 side3 = pInputSamples1U32[i*4+3] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; - mid0 = (mid0 << 1) | (side0 & 0x01); - mid1 = (mid1 << 1) | (side1 & 0x01); - mid2 = (mid2 << 1) | (side2 & 0x01); - mid3 = (mid3 << 1) | (side3 & 0x01); - temp0L = (ma_uint32)((ma_int32)(mid0 + side0) >> 1); - temp1L = (ma_uint32)((ma_int32)(mid1 + side1) >> 1); - temp2L = (ma_uint32)((ma_int32)(mid2 + side2) >> 1); - temp3L = (ma_uint32)((ma_int32)(mid3 + side3) >> 1); - temp0R = (ma_uint32)((ma_int32)(mid0 - side0) >> 1); - temp1R = (ma_uint32)((ma_int32)(mid1 - side1) >> 1); - temp2R = (ma_uint32)((ma_int32)(mid2 - side2) >> 1); - temp3R = (ma_uint32)((ma_int32)(mid3 - side3) >> 1); - pOutputSamples[i*8+0] = (ma_int32)temp0L * factor; - pOutputSamples[i*8+1] = (ma_int32)temp0R * factor; - pOutputSamples[i*8+2] = (ma_int32)temp1L * factor; - pOutputSamples[i*8+3] = (ma_int32)temp1R * factor; - pOutputSamples[i*8+4] = (ma_int32)temp2L * factor; - pOutputSamples[i*8+5] = (ma_int32)temp2R * factor; - pOutputSamples[i*8+6] = (ma_int32)temp3L * factor; - pOutputSamples[i*8+7] = (ma_int32)temp3R * factor; - } - } - for (i = (frameCount4 << 2); i < frameCount; ++i) { - ma_uint32 mid = pInputSamples0U32[i] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; - ma_uint32 side = pInputSamples1U32[i] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; - mid = (mid << 1) | (side & 0x01); - pOutputSamples[i*2+0] = (ma_int32)((ma_uint32)((ma_int32)(mid + side) >> 1) << unusedBitsPerSample) * factor; - pOutputSamples[i*2+1] = (ma_int32)((ma_uint32)((ma_int32)(mid - side) >> 1) << unusedBitsPerSample) * factor; - } -} -#if defined(MA_DR_FLAC_SUPPORT_SSE2) -static MA_INLINE void ma_dr_flac_read_pcm_frames_f32__decode_mid_side__sse2(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, float* pOutputSamples) -{ - ma_uint64 i; - ma_uint64 frameCount4 = frameCount >> 2; - const ma_uint32* pInputSamples0U32 = (const ma_uint32*)pInputSamples0; - const ma_uint32* pInputSamples1U32 = (const ma_uint32*)pInputSamples1; - ma_uint32 shift = unusedBitsPerSample - 8; - float factor; - __m128 factor128; - MA_DR_FLAC_ASSERT(pFlac->bitsPerSample <= 24); - factor = 1.0f / 8388608.0f; - factor128 = _mm_set1_ps(factor); - if (shift == 0) { - for (i = 0; i < frameCount4; ++i) { - __m128i mid; - __m128i side; - __m128i tempL; - __m128i tempR; - __m128 leftf; - __m128 rightf; - mid = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples0 + i), pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample); - side = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples1 + i), pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample); - mid = _mm_or_si128(_mm_slli_epi32(mid, 1), _mm_and_si128(side, _mm_set1_epi32(0x01))); - tempL = _mm_srai_epi32(_mm_add_epi32(mid, side), 1); - tempR = _mm_srai_epi32(_mm_sub_epi32(mid, side), 1); - leftf = _mm_mul_ps(_mm_cvtepi32_ps(tempL), factor128); - rightf = _mm_mul_ps(_mm_cvtepi32_ps(tempR), factor128); - _mm_storeu_ps(pOutputSamples + i*8 + 0, _mm_unpacklo_ps(leftf, rightf)); - _mm_storeu_ps(pOutputSamples + i*8 + 4, _mm_unpackhi_ps(leftf, rightf)); - } - for (i = (frameCount4 << 2); i < frameCount; ++i) { - ma_uint32 mid = pInputSamples0U32[i] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; - ma_uint32 side = pInputSamples1U32[i] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; - mid = (mid << 1) | (side & 0x01); - pOutputSamples[i*2+0] = ((ma_int32)(mid + side) >> 1) * factor; - pOutputSamples[i*2+1] = ((ma_int32)(mid - side) >> 1) * factor; - } - } else { - shift -= 1; - for (i = 0; i < frameCount4; ++i) { - __m128i mid; - __m128i side; - __m128i tempL; - __m128i tempR; - __m128 leftf; - __m128 rightf; - mid = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples0 + i), pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample); - side = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples1 + i), pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample); - mid = _mm_or_si128(_mm_slli_epi32(mid, 1), _mm_and_si128(side, _mm_set1_epi32(0x01))); - tempL = _mm_slli_epi32(_mm_add_epi32(mid, side), shift); - tempR = _mm_slli_epi32(_mm_sub_epi32(mid, side), shift); - leftf = _mm_mul_ps(_mm_cvtepi32_ps(tempL), factor128); - rightf = _mm_mul_ps(_mm_cvtepi32_ps(tempR), factor128); - _mm_storeu_ps(pOutputSamples + i*8 + 0, _mm_unpacklo_ps(leftf, rightf)); - _mm_storeu_ps(pOutputSamples + i*8 + 4, _mm_unpackhi_ps(leftf, rightf)); - } - for (i = (frameCount4 << 2); i < frameCount; ++i) { - ma_uint32 mid = pInputSamples0U32[i] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; - ma_uint32 side = pInputSamples1U32[i] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; - mid = (mid << 1) | (side & 0x01); - pOutputSamples[i*2+0] = (ma_int32)((mid + side) << shift) * factor; - pOutputSamples[i*2+1] = (ma_int32)((mid - side) << shift) * factor; - } - } -} -#endif -#if defined(MA_DR_FLAC_SUPPORT_NEON) -static MA_INLINE void ma_dr_flac_read_pcm_frames_f32__decode_mid_side__neon(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, float* pOutputSamples) -{ - ma_uint64 i; - ma_uint64 frameCount4 = frameCount >> 2; - const ma_uint32* pInputSamples0U32 = (const ma_uint32*)pInputSamples0; - const ma_uint32* pInputSamples1U32 = (const ma_uint32*)pInputSamples1; - ma_uint32 shift = unusedBitsPerSample - 8; - float factor; - float32x4_t factor4; - int32x4_t shift4; - int32x4_t wbps0_4; - int32x4_t wbps1_4; - MA_DR_FLAC_ASSERT(pFlac->bitsPerSample <= 24); - factor = 1.0f / 8388608.0f; - factor4 = vdupq_n_f32(factor); - wbps0_4 = vdupq_n_s32(pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample); - wbps1_4 = vdupq_n_s32(pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample); - if (shift == 0) { - for (i = 0; i < frameCount4; ++i) { - int32x4_t lefti; - int32x4_t righti; - float32x4_t leftf; - float32x4_t rightf; - uint32x4_t mid = vshlq_u32(vld1q_u32(pInputSamples0U32 + i*4), wbps0_4); - uint32x4_t side = vshlq_u32(vld1q_u32(pInputSamples1U32 + i*4), wbps1_4); - mid = vorrq_u32(vshlq_n_u32(mid, 1), vandq_u32(side, vdupq_n_u32(1))); - lefti = vshrq_n_s32(vreinterpretq_s32_u32(vaddq_u32(mid, side)), 1); - righti = vshrq_n_s32(vreinterpretq_s32_u32(vsubq_u32(mid, side)), 1); - leftf = vmulq_f32(vcvtq_f32_s32(lefti), factor4); - rightf = vmulq_f32(vcvtq_f32_s32(righti), factor4); - ma_dr_flac__vst2q_f32(pOutputSamples + i*8, vzipq_f32(leftf, rightf)); - } - for (i = (frameCount4 << 2); i < frameCount; ++i) { - ma_uint32 mid = pInputSamples0U32[i] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; - ma_uint32 side = pInputSamples1U32[i] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; - mid = (mid << 1) | (side & 0x01); - pOutputSamples[i*2+0] = ((ma_int32)(mid + side) >> 1) * factor; - pOutputSamples[i*2+1] = ((ma_int32)(mid - side) >> 1) * factor; - } - } else { - shift -= 1; - shift4 = vdupq_n_s32(shift); - for (i = 0; i < frameCount4; ++i) { - uint32x4_t mid; - uint32x4_t side; - int32x4_t lefti; - int32x4_t righti; - float32x4_t leftf; - float32x4_t rightf; - mid = vshlq_u32(vld1q_u32(pInputSamples0U32 + i*4), wbps0_4); - side = vshlq_u32(vld1q_u32(pInputSamples1U32 + i*4), wbps1_4); - mid = vorrq_u32(vshlq_n_u32(mid, 1), vandq_u32(side, vdupq_n_u32(1))); - lefti = vreinterpretq_s32_u32(vshlq_u32(vaddq_u32(mid, side), shift4)); - righti = vreinterpretq_s32_u32(vshlq_u32(vsubq_u32(mid, side), shift4)); - leftf = vmulq_f32(vcvtq_f32_s32(lefti), factor4); - rightf = vmulq_f32(vcvtq_f32_s32(righti), factor4); - ma_dr_flac__vst2q_f32(pOutputSamples + i*8, vzipq_f32(leftf, rightf)); - } - for (i = (frameCount4 << 2); i < frameCount; ++i) { - ma_uint32 mid = pInputSamples0U32[i] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; - ma_uint32 side = pInputSamples1U32[i] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; - mid = (mid << 1) | (side & 0x01); - pOutputSamples[i*2+0] = (ma_int32)((mid + side) << shift) * factor; - pOutputSamples[i*2+1] = (ma_int32)((mid - side) << shift) * factor; - } - } -} -#endif -static MA_INLINE void ma_dr_flac_read_pcm_frames_f32__decode_mid_side(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, float* pOutputSamples) -{ -#if defined(MA_DR_FLAC_SUPPORT_SSE2) - if (ma_dr_flac__gIsSSE2Supported && pFlac->bitsPerSample <= 24) { - ma_dr_flac_read_pcm_frames_f32__decode_mid_side__sse2(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); - } else -#elif defined(MA_DR_FLAC_SUPPORT_NEON) - if (ma_dr_flac__gIsNEONSupported && pFlac->bitsPerSample <= 24) { - ma_dr_flac_read_pcm_frames_f32__decode_mid_side__neon(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); - } else -#endif - { -#if 0 - ma_dr_flac_read_pcm_frames_f32__decode_mid_side__reference(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); -#else - ma_dr_flac_read_pcm_frames_f32__decode_mid_side__scalar(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); -#endif - } -} -#if 0 -static MA_INLINE void ma_dr_flac_read_pcm_frames_f32__decode_independent_stereo__reference(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, float* pOutputSamples) -{ - for (ma_uint64 i = 0; i < frameCount; ++i) { - pOutputSamples[i*2+0] = (float)((ma_int32)((ma_uint32)pInputSamples0[i] << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample)) / 2147483648.0); - pOutputSamples[i*2+1] = (float)((ma_int32)((ma_uint32)pInputSamples1[i] << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample)) / 2147483648.0); - } -} -#endif -static MA_INLINE void ma_dr_flac_read_pcm_frames_f32__decode_independent_stereo__scalar(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, float* pOutputSamples) -{ - ma_uint64 i; - ma_uint64 frameCount4 = frameCount >> 2; - const ma_uint32* pInputSamples0U32 = (const ma_uint32*)pInputSamples0; - const ma_uint32* pInputSamples1U32 = (const ma_uint32*)pInputSamples1; - ma_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; - ma_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; - float factor = 1 / 2147483648.0; - for (i = 0; i < frameCount4; ++i) { - ma_uint32 tempL0 = pInputSamples0U32[i*4+0] << shift0; - ma_uint32 tempL1 = pInputSamples0U32[i*4+1] << shift0; - ma_uint32 tempL2 = pInputSamples0U32[i*4+2] << shift0; - ma_uint32 tempL3 = pInputSamples0U32[i*4+3] << shift0; - ma_uint32 tempR0 = pInputSamples1U32[i*4+0] << shift1; - ma_uint32 tempR1 = pInputSamples1U32[i*4+1] << shift1; - ma_uint32 tempR2 = pInputSamples1U32[i*4+2] << shift1; - ma_uint32 tempR3 = pInputSamples1U32[i*4+3] << shift1; - pOutputSamples[i*8+0] = (ma_int32)tempL0 * factor; - pOutputSamples[i*8+1] = (ma_int32)tempR0 * factor; - pOutputSamples[i*8+2] = (ma_int32)tempL1 * factor; - pOutputSamples[i*8+3] = (ma_int32)tempR1 * factor; - pOutputSamples[i*8+4] = (ma_int32)tempL2 * factor; - pOutputSamples[i*8+5] = (ma_int32)tempR2 * factor; - pOutputSamples[i*8+6] = (ma_int32)tempL3 * factor; - pOutputSamples[i*8+7] = (ma_int32)tempR3 * factor; - } - for (i = (frameCount4 << 2); i < frameCount; ++i) { - pOutputSamples[i*2+0] = (ma_int32)(pInputSamples0U32[i] << shift0) * factor; - pOutputSamples[i*2+1] = (ma_int32)(pInputSamples1U32[i] << shift1) * factor; - } -} -#if defined(MA_DR_FLAC_SUPPORT_SSE2) -static MA_INLINE void ma_dr_flac_read_pcm_frames_f32__decode_independent_stereo__sse2(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, float* pOutputSamples) -{ - ma_uint64 i; - ma_uint64 frameCount4 = frameCount >> 2; - const ma_uint32* pInputSamples0U32 = (const ma_uint32*)pInputSamples0; - const ma_uint32* pInputSamples1U32 = (const ma_uint32*)pInputSamples1; - ma_uint32 shift0 = (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample) - 8; - ma_uint32 shift1 = (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample) - 8; - float factor = 1.0f / 8388608.0f; - __m128 factor128 = _mm_set1_ps(factor); - for (i = 0; i < frameCount4; ++i) { - __m128i lefti; - __m128i righti; - __m128 leftf; - __m128 rightf; - lefti = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples0 + i), shift0); - righti = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples1 + i), shift1); - leftf = _mm_mul_ps(_mm_cvtepi32_ps(lefti), factor128); - rightf = _mm_mul_ps(_mm_cvtepi32_ps(righti), factor128); - _mm_storeu_ps(pOutputSamples + i*8 + 0, _mm_unpacklo_ps(leftf, rightf)); - _mm_storeu_ps(pOutputSamples + i*8 + 4, _mm_unpackhi_ps(leftf, rightf)); - } - for (i = (frameCount4 << 2); i < frameCount; ++i) { - pOutputSamples[i*2+0] = (ma_int32)(pInputSamples0U32[i] << shift0) * factor; - pOutputSamples[i*2+1] = (ma_int32)(pInputSamples1U32[i] << shift1) * factor; - } -} -#endif -#if defined(MA_DR_FLAC_SUPPORT_NEON) -static MA_INLINE void ma_dr_flac_read_pcm_frames_f32__decode_independent_stereo__neon(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, float* pOutputSamples) -{ - ma_uint64 i; - ma_uint64 frameCount4 = frameCount >> 2; - const ma_uint32* pInputSamples0U32 = (const ma_uint32*)pInputSamples0; - const ma_uint32* pInputSamples1U32 = (const ma_uint32*)pInputSamples1; - ma_uint32 shift0 = (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample) - 8; - ma_uint32 shift1 = (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample) - 8; - float factor = 1.0f / 8388608.0f; - float32x4_t factor4 = vdupq_n_f32(factor); - int32x4_t shift0_4 = vdupq_n_s32(shift0); - int32x4_t shift1_4 = vdupq_n_s32(shift1); - for (i = 0; i < frameCount4; ++i) { - int32x4_t lefti; - int32x4_t righti; - float32x4_t leftf; - float32x4_t rightf; - lefti = vreinterpretq_s32_u32(vshlq_u32(vld1q_u32(pInputSamples0U32 + i*4), shift0_4)); - righti = vreinterpretq_s32_u32(vshlq_u32(vld1q_u32(pInputSamples1U32 + i*4), shift1_4)); - leftf = vmulq_f32(vcvtq_f32_s32(lefti), factor4); - rightf = vmulq_f32(vcvtq_f32_s32(righti), factor4); - ma_dr_flac__vst2q_f32(pOutputSamples + i*8, vzipq_f32(leftf, rightf)); - } - for (i = (frameCount4 << 2); i < frameCount; ++i) { - pOutputSamples[i*2+0] = (ma_int32)(pInputSamples0U32[i] << shift0) * factor; - pOutputSamples[i*2+1] = (ma_int32)(pInputSamples1U32[i] << shift1) * factor; - } -} -#endif -static MA_INLINE void ma_dr_flac_read_pcm_frames_f32__decode_independent_stereo(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, float* pOutputSamples) -{ -#if defined(MA_DR_FLAC_SUPPORT_SSE2) - if (ma_dr_flac__gIsSSE2Supported && pFlac->bitsPerSample <= 24) { - ma_dr_flac_read_pcm_frames_f32__decode_independent_stereo__sse2(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); - } else -#elif defined(MA_DR_FLAC_SUPPORT_NEON) - if (ma_dr_flac__gIsNEONSupported && pFlac->bitsPerSample <= 24) { - ma_dr_flac_read_pcm_frames_f32__decode_independent_stereo__neon(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); - } else -#endif - { -#if 0 - ma_dr_flac_read_pcm_frames_f32__decode_independent_stereo__reference(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); -#else - ma_dr_flac_read_pcm_frames_f32__decode_independent_stereo__scalar(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); -#endif - } -} -MA_API ma_uint64 ma_dr_flac_read_pcm_frames_f32(ma_dr_flac* pFlac, ma_uint64 framesToRead, float* pBufferOut) -{ - ma_uint64 framesRead; - ma_uint32 unusedBitsPerSample; - if (pFlac == NULL || framesToRead == 0) { - return 0; - } - if (pBufferOut == NULL) { - return ma_dr_flac__seek_forward_by_pcm_frames(pFlac, framesToRead); - } - MA_DR_FLAC_ASSERT(pFlac->bitsPerSample <= 32); - unusedBitsPerSample = 32 - pFlac->bitsPerSample; - framesRead = 0; - while (framesToRead > 0) { - if (pFlac->currentFLACFrame.pcmFramesRemaining == 0) { - if (!ma_dr_flac__read_and_decode_next_flac_frame(pFlac)) { - break; - } - } else { - unsigned int channelCount = ma_dr_flac__get_channel_count_from_channel_assignment(pFlac->currentFLACFrame.header.channelAssignment); - ma_uint64 iFirstPCMFrame = pFlac->currentFLACFrame.header.blockSizeInPCMFrames - pFlac->currentFLACFrame.pcmFramesRemaining; - ma_uint64 frameCountThisIteration = framesToRead; - if (frameCountThisIteration > pFlac->currentFLACFrame.pcmFramesRemaining) { - frameCountThisIteration = pFlac->currentFLACFrame.pcmFramesRemaining; - } - if (channelCount == 2) { - const ma_int32* pDecodedSamples0 = pFlac->currentFLACFrame.subframes[0].pSamplesS32 + iFirstPCMFrame; - const ma_int32* pDecodedSamples1 = pFlac->currentFLACFrame.subframes[1].pSamplesS32 + iFirstPCMFrame; - switch (pFlac->currentFLACFrame.header.channelAssignment) - { - case MA_DR_FLAC_CHANNEL_ASSIGNMENT_LEFT_SIDE: - { - ma_dr_flac_read_pcm_frames_f32__decode_left_side(pFlac, frameCountThisIteration, unusedBitsPerSample, pDecodedSamples0, pDecodedSamples1, pBufferOut); - } break; - case MA_DR_FLAC_CHANNEL_ASSIGNMENT_RIGHT_SIDE: - { - ma_dr_flac_read_pcm_frames_f32__decode_right_side(pFlac, frameCountThisIteration, unusedBitsPerSample, pDecodedSamples0, pDecodedSamples1, pBufferOut); - } break; - case MA_DR_FLAC_CHANNEL_ASSIGNMENT_MID_SIDE: - { - ma_dr_flac_read_pcm_frames_f32__decode_mid_side(pFlac, frameCountThisIteration, unusedBitsPerSample, pDecodedSamples0, pDecodedSamples1, pBufferOut); - } break; - case MA_DR_FLAC_CHANNEL_ASSIGNMENT_INDEPENDENT: - default: - { - ma_dr_flac_read_pcm_frames_f32__decode_independent_stereo(pFlac, frameCountThisIteration, unusedBitsPerSample, pDecodedSamples0, pDecodedSamples1, pBufferOut); - } break; - } - } else { - ma_uint64 i; - for (i = 0; i < frameCountThisIteration; ++i) { - unsigned int j; - for (j = 0; j < channelCount; ++j) { - ma_int32 sampleS32 = (ma_int32)((ma_uint32)(pFlac->currentFLACFrame.subframes[j].pSamplesS32[iFirstPCMFrame + i]) << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[j].wastedBitsPerSample)); - pBufferOut[(i*channelCount)+j] = (float)(sampleS32 / 2147483648.0); - } - } - } - framesRead += frameCountThisIteration; - pBufferOut += frameCountThisIteration * channelCount; - framesToRead -= frameCountThisIteration; - pFlac->currentPCMFrame += frameCountThisIteration; - pFlac->currentFLACFrame.pcmFramesRemaining -= (unsigned int)frameCountThisIteration; - } - } - return framesRead; -} -MA_API ma_bool32 ma_dr_flac_seek_to_pcm_frame(ma_dr_flac* pFlac, ma_uint64 pcmFrameIndex) -{ - if (pFlac == NULL) { - return MA_FALSE; - } - if (pFlac->currentPCMFrame == pcmFrameIndex) { - return MA_TRUE; - } - if (pFlac->firstFLACFramePosInBytes == 0) { - return MA_FALSE; - } - if (pcmFrameIndex == 0) { - pFlac->currentPCMFrame = 0; - return ma_dr_flac__seek_to_first_frame(pFlac); - } else { - ma_bool32 wasSuccessful = MA_FALSE; - ma_uint64 originalPCMFrame = pFlac->currentPCMFrame; - if (pcmFrameIndex > pFlac->totalPCMFrameCount) { - pcmFrameIndex = pFlac->totalPCMFrameCount; - } - if (pcmFrameIndex > pFlac->currentPCMFrame) { - ma_uint32 offset = (ma_uint32)(pcmFrameIndex - pFlac->currentPCMFrame); - if (pFlac->currentFLACFrame.pcmFramesRemaining > offset) { - pFlac->currentFLACFrame.pcmFramesRemaining -= offset; - pFlac->currentPCMFrame = pcmFrameIndex; - return MA_TRUE; - } - } else { - ma_uint32 offsetAbs = (ma_uint32)(pFlac->currentPCMFrame - pcmFrameIndex); - ma_uint32 currentFLACFramePCMFrameCount = pFlac->currentFLACFrame.header.blockSizeInPCMFrames; - ma_uint32 currentFLACFramePCMFramesConsumed = currentFLACFramePCMFrameCount - pFlac->currentFLACFrame.pcmFramesRemaining; - if (currentFLACFramePCMFramesConsumed > offsetAbs) { - pFlac->currentFLACFrame.pcmFramesRemaining += offsetAbs; - pFlac->currentPCMFrame = pcmFrameIndex; - return MA_TRUE; - } - } -#ifndef MA_DR_FLAC_NO_OGG - if (pFlac->container == ma_dr_flac_container_ogg) - { - wasSuccessful = ma_dr_flac_ogg__seek_to_pcm_frame(pFlac, pcmFrameIndex); - } - else -#endif - { - if (!pFlac->_noSeekTableSeek) { - wasSuccessful = ma_dr_flac__seek_to_pcm_frame__seek_table(pFlac, pcmFrameIndex); - } -#if !defined(MA_DR_FLAC_NO_CRC) - if (!wasSuccessful && !pFlac->_noBinarySearchSeek && pFlac->totalPCMFrameCount > 0) { - wasSuccessful = ma_dr_flac__seek_to_pcm_frame__binary_search(pFlac, pcmFrameIndex); - } -#endif - if (!wasSuccessful && !pFlac->_noBruteForceSeek) { - wasSuccessful = ma_dr_flac__seek_to_pcm_frame__brute_force(pFlac, pcmFrameIndex); - } - } - if (wasSuccessful) { - pFlac->currentPCMFrame = pcmFrameIndex; - } else { - if (ma_dr_flac_seek_to_pcm_frame(pFlac, originalPCMFrame) == MA_FALSE) { - ma_dr_flac_seek_to_pcm_frame(pFlac, 0); - } - } - return wasSuccessful; - } -} -#define MA_DR_FLAC_DEFINE_FULL_READ_AND_CLOSE(extension, type) \ -static type* ma_dr_flac__full_read_and_close_ ## extension (ma_dr_flac* pFlac, unsigned int* channelsOut, unsigned int* sampleRateOut, ma_uint64* totalPCMFrameCountOut)\ -{ \ - type* pSampleData = NULL; \ - ma_uint64 totalPCMFrameCount; \ - \ - MA_DR_FLAC_ASSERT(pFlac != NULL); \ - \ - totalPCMFrameCount = pFlac->totalPCMFrameCount; \ - \ - if (totalPCMFrameCount == 0) { \ - type buffer[4096]; \ - ma_uint64 pcmFramesRead; \ - size_t sampleDataBufferSize = sizeof(buffer); \ - \ - pSampleData = (type*)ma_dr_flac__malloc_from_callbacks(sampleDataBufferSize, &pFlac->allocationCallbacks); \ - if (pSampleData == NULL) { \ - goto on_error; \ - } \ - \ - while ((pcmFramesRead = (ma_uint64)ma_dr_flac_read_pcm_frames_##extension(pFlac, sizeof(buffer)/sizeof(buffer[0])/pFlac->channels, buffer)) > 0) { \ - if (((totalPCMFrameCount + pcmFramesRead) * pFlac->channels * sizeof(type)) > sampleDataBufferSize) { \ - type* pNewSampleData; \ - size_t newSampleDataBufferSize; \ - \ - newSampleDataBufferSize = sampleDataBufferSize * 2; \ - pNewSampleData = (type*)ma_dr_flac__realloc_from_callbacks(pSampleData, newSampleDataBufferSize, sampleDataBufferSize, &pFlac->allocationCallbacks); \ - if (pNewSampleData == NULL) { \ - ma_dr_flac__free_from_callbacks(pSampleData, &pFlac->allocationCallbacks); \ - goto on_error; \ - } \ - \ - sampleDataBufferSize = newSampleDataBufferSize; \ - pSampleData = pNewSampleData; \ - } \ - \ - MA_DR_FLAC_COPY_MEMORY(pSampleData + (totalPCMFrameCount*pFlac->channels), buffer, (size_t)(pcmFramesRead*pFlac->channels*sizeof(type))); \ - totalPCMFrameCount += pcmFramesRead; \ - } \ - \ - \ - MA_DR_FLAC_ZERO_MEMORY(pSampleData + (totalPCMFrameCount*pFlac->channels), (size_t)(sampleDataBufferSize - totalPCMFrameCount*pFlac->channels*sizeof(type))); \ - } else { \ - ma_uint64 dataSize = totalPCMFrameCount*pFlac->channels*sizeof(type); \ - if (dataSize > (ma_uint64)MA_SIZE_MAX) { \ - goto on_error; \ - } \ - \ - pSampleData = (type*)ma_dr_flac__malloc_from_callbacks((size_t)dataSize, &pFlac->allocationCallbacks); \ - if (pSampleData == NULL) { \ - goto on_error; \ - } \ - \ - totalPCMFrameCount = ma_dr_flac_read_pcm_frames_##extension(pFlac, pFlac->totalPCMFrameCount, pSampleData); \ - } \ - \ - if (sampleRateOut) *sampleRateOut = pFlac->sampleRate; \ - if (channelsOut) *channelsOut = pFlac->channels; \ - if (totalPCMFrameCountOut) *totalPCMFrameCountOut = totalPCMFrameCount; \ - \ - ma_dr_flac_close(pFlac); \ - return pSampleData; \ - \ -on_error: \ - ma_dr_flac_close(pFlac); \ - return NULL; \ -} -MA_DR_FLAC_DEFINE_FULL_READ_AND_CLOSE(s32, ma_int32) -MA_DR_FLAC_DEFINE_FULL_READ_AND_CLOSE(s16, ma_int16) -MA_DR_FLAC_DEFINE_FULL_READ_AND_CLOSE(f32, float) -MA_API ma_int32* ma_dr_flac_open_and_read_pcm_frames_s32(ma_dr_flac_read_proc onRead, ma_dr_flac_seek_proc onSeek, void* pUserData, unsigned int* channelsOut, unsigned int* sampleRateOut, ma_uint64* totalPCMFrameCountOut, const ma_allocation_callbacks* pAllocationCallbacks) -{ - ma_dr_flac* pFlac; - if (channelsOut) { - *channelsOut = 0; - } - if (sampleRateOut) { - *sampleRateOut = 0; - } - if (totalPCMFrameCountOut) { - *totalPCMFrameCountOut = 0; - } - pFlac = ma_dr_flac_open(onRead, onSeek, pUserData, pAllocationCallbacks); - if (pFlac == NULL) { - return NULL; - } - return ma_dr_flac__full_read_and_close_s32(pFlac, channelsOut, sampleRateOut, totalPCMFrameCountOut); -} -MA_API ma_int16* ma_dr_flac_open_and_read_pcm_frames_s16(ma_dr_flac_read_proc onRead, ma_dr_flac_seek_proc onSeek, void* pUserData, unsigned int* channelsOut, unsigned int* sampleRateOut, ma_uint64* totalPCMFrameCountOut, const ma_allocation_callbacks* pAllocationCallbacks) -{ - ma_dr_flac* pFlac; - if (channelsOut) { - *channelsOut = 0; - } - if (sampleRateOut) { - *sampleRateOut = 0; - } - if (totalPCMFrameCountOut) { - *totalPCMFrameCountOut = 0; - } - pFlac = ma_dr_flac_open(onRead, onSeek, pUserData, pAllocationCallbacks); - if (pFlac == NULL) { - return NULL; - } - return ma_dr_flac__full_read_and_close_s16(pFlac, channelsOut, sampleRateOut, totalPCMFrameCountOut); -} -MA_API float* ma_dr_flac_open_and_read_pcm_frames_f32(ma_dr_flac_read_proc onRead, ma_dr_flac_seek_proc onSeek, void* pUserData, unsigned int* channelsOut, unsigned int* sampleRateOut, ma_uint64* totalPCMFrameCountOut, const ma_allocation_callbacks* pAllocationCallbacks) -{ - ma_dr_flac* pFlac; - if (channelsOut) { - *channelsOut = 0; - } - if (sampleRateOut) { - *sampleRateOut = 0; - } - if (totalPCMFrameCountOut) { - *totalPCMFrameCountOut = 0; - } - pFlac = ma_dr_flac_open(onRead, onSeek, pUserData, pAllocationCallbacks); - if (pFlac == NULL) { - return NULL; - } - return ma_dr_flac__full_read_and_close_f32(pFlac, channelsOut, sampleRateOut, totalPCMFrameCountOut); -} -#ifndef MA_DR_FLAC_NO_STDIO -MA_API ma_int32* ma_dr_flac_open_file_and_read_pcm_frames_s32(const char* filename, unsigned int* channels, unsigned int* sampleRate, ma_uint64* totalPCMFrameCount, const ma_allocation_callbacks* pAllocationCallbacks) -{ - ma_dr_flac* pFlac; - if (sampleRate) { - *sampleRate = 0; - } - if (channels) { - *channels = 0; - } - if (totalPCMFrameCount) { - *totalPCMFrameCount = 0; - } - pFlac = ma_dr_flac_open_file(filename, pAllocationCallbacks); - if (pFlac == NULL) { - return NULL; - } - return ma_dr_flac__full_read_and_close_s32(pFlac, channels, sampleRate, totalPCMFrameCount); -} -MA_API ma_int16* ma_dr_flac_open_file_and_read_pcm_frames_s16(const char* filename, unsigned int* channels, unsigned int* sampleRate, ma_uint64* totalPCMFrameCount, const ma_allocation_callbacks* pAllocationCallbacks) -{ - ma_dr_flac* pFlac; - if (sampleRate) { - *sampleRate = 0; - } - if (channels) { - *channels = 0; - } - if (totalPCMFrameCount) { - *totalPCMFrameCount = 0; - } - pFlac = ma_dr_flac_open_file(filename, pAllocationCallbacks); - if (pFlac == NULL) { - return NULL; - } - return ma_dr_flac__full_read_and_close_s16(pFlac, channels, sampleRate, totalPCMFrameCount); -} -MA_API float* ma_dr_flac_open_file_and_read_pcm_frames_f32(const char* filename, unsigned int* channels, unsigned int* sampleRate, ma_uint64* totalPCMFrameCount, const ma_allocation_callbacks* pAllocationCallbacks) -{ - ma_dr_flac* pFlac; - if (sampleRate) { - *sampleRate = 0; - } - if (channels) { - *channels = 0; - } - if (totalPCMFrameCount) { - *totalPCMFrameCount = 0; - } - pFlac = ma_dr_flac_open_file(filename, pAllocationCallbacks); - if (pFlac == NULL) { - return NULL; - } - return ma_dr_flac__full_read_and_close_f32(pFlac, channels, sampleRate, totalPCMFrameCount); -} -#endif -MA_API ma_int32* ma_dr_flac_open_memory_and_read_pcm_frames_s32(const void* data, size_t dataSize, unsigned int* channels, unsigned int* sampleRate, ma_uint64* totalPCMFrameCount, const ma_allocation_callbacks* pAllocationCallbacks) -{ - ma_dr_flac* pFlac; - if (sampleRate) { - *sampleRate = 0; - } - if (channels) { - *channels = 0; - } - if (totalPCMFrameCount) { - *totalPCMFrameCount = 0; - } - pFlac = ma_dr_flac_open_memory(data, dataSize, pAllocationCallbacks); - if (pFlac == NULL) { - return NULL; - } - return ma_dr_flac__full_read_and_close_s32(pFlac, channels, sampleRate, totalPCMFrameCount); -} -MA_API ma_int16* ma_dr_flac_open_memory_and_read_pcm_frames_s16(const void* data, size_t dataSize, unsigned int* channels, unsigned int* sampleRate, ma_uint64* totalPCMFrameCount, const ma_allocation_callbacks* pAllocationCallbacks) -{ - ma_dr_flac* pFlac; - if (sampleRate) { - *sampleRate = 0; - } - if (channels) { - *channels = 0; - } - if (totalPCMFrameCount) { - *totalPCMFrameCount = 0; - } - pFlac = ma_dr_flac_open_memory(data, dataSize, pAllocationCallbacks); - if (pFlac == NULL) { - return NULL; - } - return ma_dr_flac__full_read_and_close_s16(pFlac, channels, sampleRate, totalPCMFrameCount); -} -MA_API float* ma_dr_flac_open_memory_and_read_pcm_frames_f32(const void* data, size_t dataSize, unsigned int* channels, unsigned int* sampleRate, ma_uint64* totalPCMFrameCount, const ma_allocation_callbacks* pAllocationCallbacks) -{ - ma_dr_flac* pFlac; - if (sampleRate) { - *sampleRate = 0; - } - if (channels) { - *channels = 0; - } - if (totalPCMFrameCount) { - *totalPCMFrameCount = 0; - } - pFlac = ma_dr_flac_open_memory(data, dataSize, pAllocationCallbacks); - if (pFlac == NULL) { - return NULL; - } - return ma_dr_flac__full_read_and_close_f32(pFlac, channels, sampleRate, totalPCMFrameCount); -} -MA_API void ma_dr_flac_free(void* p, const ma_allocation_callbacks* pAllocationCallbacks) -{ - if (pAllocationCallbacks != NULL) { - ma_dr_flac__free_from_callbacks(p, pAllocationCallbacks); - } else { - ma_dr_flac__free_default(p, NULL); - } -} -MA_API void ma_dr_flac_init_vorbis_comment_iterator(ma_dr_flac_vorbis_comment_iterator* pIter, ma_uint32 commentCount, const void* pComments) -{ - if (pIter == NULL) { - return; - } - pIter->countRemaining = commentCount; - pIter->pRunningData = (const char*)pComments; -} -MA_API const char* ma_dr_flac_next_vorbis_comment(ma_dr_flac_vorbis_comment_iterator* pIter, ma_uint32* pCommentLengthOut) -{ - ma_int32 length; - const char* pComment; - if (pCommentLengthOut) { - *pCommentLengthOut = 0; - } - if (pIter == NULL || pIter->countRemaining == 0 || pIter->pRunningData == NULL) { - return NULL; - } - length = ma_dr_flac__le2host_32_ptr_unaligned(pIter->pRunningData); - pIter->pRunningData += 4; - pComment = pIter->pRunningData; - pIter->pRunningData += length; - pIter->countRemaining -= 1; - if (pCommentLengthOut) { - *pCommentLengthOut = length; - } - return pComment; -} -MA_API void ma_dr_flac_init_cuesheet_track_iterator(ma_dr_flac_cuesheet_track_iterator* pIter, ma_uint32 trackCount, const void* pTrackData) -{ - if (pIter == NULL) { - return; - } - pIter->countRemaining = trackCount; - pIter->pRunningData = (const char*)pTrackData; -} -MA_API ma_bool32 ma_dr_flac_next_cuesheet_track(ma_dr_flac_cuesheet_track_iterator* pIter, ma_dr_flac_cuesheet_track* pCuesheetTrack) -{ - ma_dr_flac_cuesheet_track cuesheetTrack; - const char* pRunningData; - ma_uint64 offsetHi; - ma_uint64 offsetLo; - if (pIter == NULL || pIter->countRemaining == 0 || pIter->pRunningData == NULL) { - return MA_FALSE; - } - pRunningData = pIter->pRunningData; - offsetHi = ma_dr_flac__be2host_32(*(const ma_uint32*)pRunningData); pRunningData += 4; - offsetLo = ma_dr_flac__be2host_32(*(const ma_uint32*)pRunningData); pRunningData += 4; - cuesheetTrack.offset = offsetLo | (offsetHi << 32); - cuesheetTrack.trackNumber = pRunningData[0]; pRunningData += 1; - MA_DR_FLAC_COPY_MEMORY(cuesheetTrack.ISRC, pRunningData, sizeof(cuesheetTrack.ISRC)); pRunningData += 12; - cuesheetTrack.isAudio = (pRunningData[0] & 0x80) != 0; - cuesheetTrack.preEmphasis = (pRunningData[0] & 0x40) != 0; pRunningData += 14; - cuesheetTrack.indexCount = pRunningData[0]; pRunningData += 1; - cuesheetTrack.pIndexPoints = (const ma_dr_flac_cuesheet_track_index*)pRunningData; pRunningData += cuesheetTrack.indexCount * sizeof(ma_dr_flac_cuesheet_track_index); - pIter->pRunningData = pRunningData; - pIter->countRemaining -= 1; - if (pCuesheetTrack) { - *pCuesheetTrack = cuesheetTrack; - } - return MA_TRUE; -} -#if defined(__clang__) || (defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6))) - #pragma GCC diagnostic pop -#endif -#endif -/* dr_flac_c end */ -#endif /* MA_DR_FLAC_IMPLEMENTATION */ -#endif /* MA_NO_FLAC */ - -#if !defined(MA_NO_MP3) && !defined(MA_NO_DECODING) -#if !defined(MA_DR_MP3_IMPLEMENTATION) -/* dr_mp3_c begin */ -#ifndef ma_dr_mp3_c -#define ma_dr_mp3_c -#include -#include -#include -MA_API void ma_dr_mp3_version(ma_uint32* pMajor, ma_uint32* pMinor, ma_uint32* pRevision) -{ - if (pMajor) { - *pMajor = MA_DR_MP3_VERSION_MAJOR; - } - if (pMinor) { - *pMinor = MA_DR_MP3_VERSION_MINOR; - } - if (pRevision) { - *pRevision = MA_DR_MP3_VERSION_REVISION; - } -} -MA_API const char* ma_dr_mp3_version_string(void) -{ - return MA_DR_MP3_VERSION_STRING; -} -#if defined(__TINYC__) -#define MA_DR_MP3_NO_SIMD -#endif -#define MA_DR_MP3_OFFSET_PTR(p, offset) ((void*)((ma_uint8*)(p) + (offset))) -#define MA_DR_MP3_MAX_FREE_FORMAT_FRAME_SIZE 2304 -#ifndef MA_DR_MP3_MAX_FRAME_SYNC_MATCHES -#define MA_DR_MP3_MAX_FRAME_SYNC_MATCHES 10 -#endif -#define MA_DR_MP3_MAX_L3_FRAME_PAYLOAD_BYTES MA_DR_MP3_MAX_FREE_FORMAT_FRAME_SIZE -#define MA_DR_MP3_MAX_BITRESERVOIR_BYTES 511 -#define MA_DR_MP3_SHORT_BLOCK_TYPE 2 -#define MA_DR_MP3_STOP_BLOCK_TYPE 3 -#define MA_DR_MP3_MODE_MONO 3 -#define MA_DR_MP3_MODE_JOINT_STEREO 1 -#define MA_DR_MP3_HDR_SIZE 4 -#define MA_DR_MP3_HDR_IS_MONO(h) (((h[3]) & 0xC0) == 0xC0) -#define MA_DR_MP3_HDR_IS_MS_STEREO(h) (((h[3]) & 0xE0) == 0x60) -#define MA_DR_MP3_HDR_IS_FREE_FORMAT(h) (((h[2]) & 0xF0) == 0) -#define MA_DR_MP3_HDR_IS_CRC(h) (!((h[1]) & 1)) -#define MA_DR_MP3_HDR_TEST_PADDING(h) ((h[2]) & 0x2) -#define MA_DR_MP3_HDR_TEST_MPEG1(h) ((h[1]) & 0x8) -#define MA_DR_MP3_HDR_TEST_NOT_MPEG25(h) ((h[1]) & 0x10) -#define MA_DR_MP3_HDR_TEST_I_STEREO(h) ((h[3]) & 0x10) -#define MA_DR_MP3_HDR_TEST_MS_STEREO(h) ((h[3]) & 0x20) -#define MA_DR_MP3_HDR_GET_STEREO_MODE(h) (((h[3]) >> 6) & 3) -#define MA_DR_MP3_HDR_GET_STEREO_MODE_EXT(h) (((h[3]) >> 4) & 3) -#define MA_DR_MP3_HDR_GET_LAYER(h) (((h[1]) >> 1) & 3) -#define MA_DR_MP3_HDR_GET_BITRATE(h) ((h[2]) >> 4) -#define MA_DR_MP3_HDR_GET_SAMPLE_RATE(h) (((h[2]) >> 2) & 3) -#define MA_DR_MP3_HDR_GET_MY_SAMPLE_RATE(h) (MA_DR_MP3_HDR_GET_SAMPLE_RATE(h) + (((h[1] >> 3) & 1) + ((h[1] >> 4) & 1))*3) -#define MA_DR_MP3_HDR_IS_FRAME_576(h) ((h[1] & 14) == 2) -#define MA_DR_MP3_HDR_IS_LAYER_1(h) ((h[1] & 6) == 6) -#define MA_DR_MP3_BITS_DEQUANTIZER_OUT -1 -#define MA_DR_MP3_MAX_SCF (255 + MA_DR_MP3_BITS_DEQUANTIZER_OUT*4 - 210) -#define MA_DR_MP3_MAX_SCFI ((MA_DR_MP3_MAX_SCF + 3) & ~3) -#define MA_DR_MP3_MIN(a, b) ((a) > (b) ? (b) : (a)) -#define MA_DR_MP3_MAX(a, b) ((a) < (b) ? (b) : (a)) -#if !defined(MA_DR_MP3_NO_SIMD) -#if !defined(MA_DR_MP3_ONLY_SIMD) && (defined(_M_X64) || defined(__x86_64__) || defined(__aarch64__) || defined(_M_ARM64) || defined(_M_ARM64EC)) -#define MA_DR_MP3_ONLY_SIMD -#endif -#if ((defined(_MSC_VER) && _MSC_VER >= 1400) && defined(_M_X64)) || ((defined(__i386) || defined(_M_IX86) || defined(__i386__) || defined(__x86_64__)) && ((defined(_M_IX86_FP) && _M_IX86_FP == 2) || defined(__SSE2__))) -#if defined(_MSC_VER) -#include -#endif -#include -#define MA_DR_MP3_HAVE_SSE 1 -#define MA_DR_MP3_HAVE_SIMD 1 -#define MA_DR_MP3_VSTORE _mm_storeu_ps -#define MA_DR_MP3_VLD _mm_loadu_ps -#define MA_DR_MP3_VSET _mm_set1_ps -#define MA_DR_MP3_VADD _mm_add_ps -#define MA_DR_MP3_VSUB _mm_sub_ps -#define MA_DR_MP3_VMUL _mm_mul_ps -#define MA_DR_MP3_VMAC(a, x, y) _mm_add_ps(a, _mm_mul_ps(x, y)) -#define MA_DR_MP3_VMSB(a, x, y) _mm_sub_ps(a, _mm_mul_ps(x, y)) -#define MA_DR_MP3_VMUL_S(x, s) _mm_mul_ps(x, _mm_set1_ps(s)) -#define MA_DR_MP3_VREV(x) _mm_shuffle_ps(x, x, _MM_SHUFFLE(0, 1, 2, 3)) -typedef __m128 ma_dr_mp3_f4; -#if defined(_MSC_VER) || defined(MA_DR_MP3_ONLY_SIMD) -#define ma_dr_mp3_cpuid __cpuid -#else -static __inline__ __attribute__((always_inline)) void ma_dr_mp3_cpuid(int CPUInfo[], const int InfoType) -{ -#if defined(__PIC__) - __asm__ __volatile__( -#if defined(__x86_64__) - "push %%rbx\n" - "cpuid\n" - "xchgl %%ebx, %1\n" - "pop %%rbx\n" -#else - "xchgl %%ebx, %1\n" - "cpuid\n" - "xchgl %%ebx, %1\n" -#endif - : "=a" (CPUInfo[0]), "=r" (CPUInfo[1]), "=c" (CPUInfo[2]), "=d" (CPUInfo[3]) - : "a" (InfoType)); -#else - __asm__ __volatile__( - "cpuid" - : "=a" (CPUInfo[0]), "=b" (CPUInfo[1]), "=c" (CPUInfo[2]), "=d" (CPUInfo[3]) - : "a" (InfoType)); -#endif -} -#endif -static int ma_dr_mp3_have_simd(void) -{ -#ifdef MA_DR_MP3_ONLY_SIMD - return 1; -#else - static int g_have_simd; - int CPUInfo[4]; -#ifdef MINIMP3_TEST - static int g_counter; - if (g_counter++ > 100) - return 0; -#endif - if (g_have_simd) - goto end; - ma_dr_mp3_cpuid(CPUInfo, 0); - if (CPUInfo[0] > 0) - { - ma_dr_mp3_cpuid(CPUInfo, 1); - g_have_simd = (CPUInfo[3] & (1 << 26)) + 1; - return g_have_simd - 1; - } -end: - return g_have_simd - 1; -#endif -} -#elif defined(__ARM_NEON) || defined(__aarch64__) || defined(_M_ARM64) || defined(_M_ARM64EC) -#include -#define MA_DR_MP3_HAVE_SSE 0 -#define MA_DR_MP3_HAVE_SIMD 1 -#define MA_DR_MP3_VSTORE vst1q_f32 -#define MA_DR_MP3_VLD vld1q_f32 -#define MA_DR_MP3_VSET vmovq_n_f32 -#define MA_DR_MP3_VADD vaddq_f32 -#define MA_DR_MP3_VSUB vsubq_f32 -#define MA_DR_MP3_VMUL vmulq_f32 -#define MA_DR_MP3_VMAC(a, x, y) vmlaq_f32(a, x, y) -#define MA_DR_MP3_VMSB(a, x, y) vmlsq_f32(a, x, y) -#define MA_DR_MP3_VMUL_S(x, s) vmulq_f32(x, vmovq_n_f32(s)) -#define MA_DR_MP3_VREV(x) vcombine_f32(vget_high_f32(vrev64q_f32(x)), vget_low_f32(vrev64q_f32(x))) -typedef float32x4_t ma_dr_mp3_f4; -static int ma_dr_mp3_have_simd(void) -{ - return 1; -} -#else -#define MA_DR_MP3_HAVE_SSE 0 -#define MA_DR_MP3_HAVE_SIMD 0 -#ifdef MA_DR_MP3_ONLY_SIMD -#error MA_DR_MP3_ONLY_SIMD used, but SSE/NEON not enabled -#endif -#endif -#else -#define MA_DR_MP3_HAVE_SIMD 0 -#endif -#if defined(__ARM_ARCH) && (__ARM_ARCH >= 6) && !defined(__aarch64__) && !defined(_M_ARM64) && !defined(_M_ARM64EC) && !defined(__ARM_ARCH_6M__) -#define MA_DR_MP3_HAVE_ARMV6 1 -static __inline__ __attribute__((always_inline)) ma_int32 ma_dr_mp3_clip_int16_arm(ma_int32 a) -{ - ma_int32 x = 0; - __asm__ ("ssat %0, #16, %1" : "=r"(x) : "r"(a)); - return x; -} -#else -#define MA_DR_MP3_HAVE_ARMV6 0 -#endif -#ifndef MA_DR_MP3_ASSERT -#include -#define MA_DR_MP3_ASSERT(expression) assert(expression) -#endif -#ifndef MA_DR_MP3_COPY_MEMORY -#define MA_DR_MP3_COPY_MEMORY(dst, src, sz) memcpy((dst), (src), (sz)) -#endif -#ifndef MA_DR_MP3_MOVE_MEMORY -#define MA_DR_MP3_MOVE_MEMORY(dst, src, sz) memmove((dst), (src), (sz)) -#endif -#ifndef MA_DR_MP3_ZERO_MEMORY -#define MA_DR_MP3_ZERO_MEMORY(p, sz) memset((p), 0, (sz)) -#endif -#define MA_DR_MP3_ZERO_OBJECT(p) MA_DR_MP3_ZERO_MEMORY((p), sizeof(*(p))) -#ifndef MA_DR_MP3_MALLOC -#define MA_DR_MP3_MALLOC(sz) malloc((sz)) -#endif -#ifndef MA_DR_MP3_REALLOC -#define MA_DR_MP3_REALLOC(p, sz) realloc((p), (sz)) -#endif -#ifndef MA_DR_MP3_FREE -#define MA_DR_MP3_FREE(p) free((p)) -#endif -typedef struct -{ - const ma_uint8 *buf; - int pos, limit; -} ma_dr_mp3_bs; -typedef struct -{ - float scf[3*64]; - ma_uint8 total_bands, stereo_bands, bitalloc[64], scfcod[64]; -} ma_dr_mp3_L12_scale_info; -typedef struct -{ - ma_uint8 tab_offset, code_tab_width, band_count; -} ma_dr_mp3_L12_subband_alloc; -typedef struct -{ - const ma_uint8 *sfbtab; - ma_uint16 part_23_length, big_values, scalefac_compress; - ma_uint8 global_gain, block_type, mixed_block_flag, n_long_sfb, n_short_sfb; - ma_uint8 table_select[3], region_count[3], subblock_gain[3]; - ma_uint8 preflag, scalefac_scale, count1_table, scfsi; -} ma_dr_mp3_L3_gr_info; -typedef struct -{ - ma_dr_mp3_bs bs; - ma_uint8 maindata[MA_DR_MP3_MAX_BITRESERVOIR_BYTES + MA_DR_MP3_MAX_L3_FRAME_PAYLOAD_BYTES]; - ma_dr_mp3_L3_gr_info gr_info[4]; - float grbuf[2][576], scf[40], syn[18 + 15][2*32]; - ma_uint8 ist_pos[2][39]; -} ma_dr_mp3dec_scratch; -static void ma_dr_mp3_bs_init(ma_dr_mp3_bs *bs, const ma_uint8 *data, int bytes) -{ - bs->buf = data; - bs->pos = 0; - bs->limit = bytes*8; -} -static ma_uint32 ma_dr_mp3_bs_get_bits(ma_dr_mp3_bs *bs, int n) -{ - ma_uint32 next, cache = 0, s = bs->pos & 7; - int shl = n + s; - const ma_uint8 *p = bs->buf + (bs->pos >> 3); - if ((bs->pos += n) > bs->limit) - return 0; - next = *p++ & (255 >> s); - while ((shl -= 8) > 0) - { - cache |= next << shl; - next = *p++; - } - return cache | (next >> -shl); -} -static int ma_dr_mp3_hdr_valid(const ma_uint8 *h) -{ - return h[0] == 0xff && - ((h[1] & 0xF0) == 0xf0 || (h[1] & 0xFE) == 0xe2) && - (MA_DR_MP3_HDR_GET_LAYER(h) != 0) && - (MA_DR_MP3_HDR_GET_BITRATE(h) != 15) && - (MA_DR_MP3_HDR_GET_SAMPLE_RATE(h) != 3); -} -static int ma_dr_mp3_hdr_compare(const ma_uint8 *h1, const ma_uint8 *h2) -{ - return ma_dr_mp3_hdr_valid(h2) && - ((h1[1] ^ h2[1]) & 0xFE) == 0 && - ((h1[2] ^ h2[2]) & 0x0C) == 0 && - !(MA_DR_MP3_HDR_IS_FREE_FORMAT(h1) ^ MA_DR_MP3_HDR_IS_FREE_FORMAT(h2)); -} -static unsigned ma_dr_mp3_hdr_bitrate_kbps(const ma_uint8 *h) -{ - static const ma_uint8 halfrate[2][3][15] = { - { { 0,4,8,12,16,20,24,28,32,40,48,56,64,72,80 }, { 0,4,8,12,16,20,24,28,32,40,48,56,64,72,80 }, { 0,16,24,28,32,40,48,56,64,72,80,88,96,112,128 } }, - { { 0,16,20,24,28,32,40,48,56,64,80,96,112,128,160 }, { 0,16,24,28,32,40,48,56,64,80,96,112,128,160,192 }, { 0,16,32,48,64,80,96,112,128,144,160,176,192,208,224 } }, - }; - return 2*halfrate[!!MA_DR_MP3_HDR_TEST_MPEG1(h)][MA_DR_MP3_HDR_GET_LAYER(h) - 1][MA_DR_MP3_HDR_GET_BITRATE(h)]; -} -static unsigned ma_dr_mp3_hdr_sample_rate_hz(const ma_uint8 *h) -{ - static const unsigned g_hz[3] = { 44100, 48000, 32000 }; - return g_hz[MA_DR_MP3_HDR_GET_SAMPLE_RATE(h)] >> (int)!MA_DR_MP3_HDR_TEST_MPEG1(h) >> (int)!MA_DR_MP3_HDR_TEST_NOT_MPEG25(h); -} -static unsigned ma_dr_mp3_hdr_frame_samples(const ma_uint8 *h) -{ - return MA_DR_MP3_HDR_IS_LAYER_1(h) ? 384 : (1152 >> (int)MA_DR_MP3_HDR_IS_FRAME_576(h)); -} -static int ma_dr_mp3_hdr_frame_bytes(const ma_uint8 *h, int free_format_size) -{ - int frame_bytes = ma_dr_mp3_hdr_frame_samples(h)*ma_dr_mp3_hdr_bitrate_kbps(h)*125/ma_dr_mp3_hdr_sample_rate_hz(h); - if (MA_DR_MP3_HDR_IS_LAYER_1(h)) - { - frame_bytes &= ~3; - } - return frame_bytes ? frame_bytes : free_format_size; -} -static int ma_dr_mp3_hdr_padding(const ma_uint8 *h) -{ - return MA_DR_MP3_HDR_TEST_PADDING(h) ? (MA_DR_MP3_HDR_IS_LAYER_1(h) ? 4 : 1) : 0; -} -#ifndef MA_DR_MP3_ONLY_MP3 -static const ma_dr_mp3_L12_subband_alloc *ma_dr_mp3_L12_subband_alloc_table(const ma_uint8 *hdr, ma_dr_mp3_L12_scale_info *sci) -{ - const ma_dr_mp3_L12_subband_alloc *alloc; - int mode = MA_DR_MP3_HDR_GET_STEREO_MODE(hdr); - int nbands, stereo_bands = (mode == MA_DR_MP3_MODE_MONO) ? 0 : (mode == MA_DR_MP3_MODE_JOINT_STEREO) ? (MA_DR_MP3_HDR_GET_STEREO_MODE_EXT(hdr) << 2) + 4 : 32; - if (MA_DR_MP3_HDR_IS_LAYER_1(hdr)) - { - static const ma_dr_mp3_L12_subband_alloc g_alloc_L1[] = { { 76, 4, 32 } }; - alloc = g_alloc_L1; - nbands = 32; - } else if (!MA_DR_MP3_HDR_TEST_MPEG1(hdr)) - { - static const ma_dr_mp3_L12_subband_alloc g_alloc_L2M2[] = { { 60, 4, 4 }, { 44, 3, 7 }, { 44, 2, 19 } }; - alloc = g_alloc_L2M2; - nbands = 30; - } else - { - static const ma_dr_mp3_L12_subband_alloc g_alloc_L2M1[] = { { 0, 4, 3 }, { 16, 4, 8 }, { 32, 3, 12 }, { 40, 2, 7 } }; - int sample_rate_idx = MA_DR_MP3_HDR_GET_SAMPLE_RATE(hdr); - unsigned kbps = ma_dr_mp3_hdr_bitrate_kbps(hdr) >> (int)(mode != MA_DR_MP3_MODE_MONO); - if (!kbps) - { - kbps = 192; - } - alloc = g_alloc_L2M1; - nbands = 27; - if (kbps < 56) - { - static const ma_dr_mp3_L12_subband_alloc g_alloc_L2M1_lowrate[] = { { 44, 4, 2 }, { 44, 3, 10 } }; - alloc = g_alloc_L2M1_lowrate; - nbands = sample_rate_idx == 2 ? 12 : 8; - } else if (kbps >= 96 && sample_rate_idx != 1) - { - nbands = 30; - } - } - sci->total_bands = (ma_uint8)nbands; - sci->stereo_bands = (ma_uint8)MA_DR_MP3_MIN(stereo_bands, nbands); - return alloc; -} -static void ma_dr_mp3_L12_read_scalefactors(ma_dr_mp3_bs *bs, ma_uint8 *pba, ma_uint8 *scfcod, int bands, float *scf) -{ - static const float g_deq_L12[18*3] = { -#define MA_DR_MP3_DQ(x) 9.53674316e-07f/x, 7.56931807e-07f/x, 6.00777173e-07f/x - MA_DR_MP3_DQ(3),MA_DR_MP3_DQ(7),MA_DR_MP3_DQ(15),MA_DR_MP3_DQ(31),MA_DR_MP3_DQ(63),MA_DR_MP3_DQ(127),MA_DR_MP3_DQ(255),MA_DR_MP3_DQ(511),MA_DR_MP3_DQ(1023),MA_DR_MP3_DQ(2047),MA_DR_MP3_DQ(4095),MA_DR_MP3_DQ(8191),MA_DR_MP3_DQ(16383),MA_DR_MP3_DQ(32767),MA_DR_MP3_DQ(65535),MA_DR_MP3_DQ(3),MA_DR_MP3_DQ(5),MA_DR_MP3_DQ(9) - }; - int i, m; - for (i = 0; i < bands; i++) - { - float s = 0; - int ba = *pba++; - int mask = ba ? 4 + ((19 >> scfcod[i]) & 3) : 0; - for (m = 4; m; m >>= 1) - { - if (mask & m) - { - int b = ma_dr_mp3_bs_get_bits(bs, 6); - s = g_deq_L12[ba*3 - 6 + b % 3]*(int)(1 << 21 >> b/3); - } - *scf++ = s; - } - } -} -static void ma_dr_mp3_L12_read_scale_info(const ma_uint8 *hdr, ma_dr_mp3_bs *bs, ma_dr_mp3_L12_scale_info *sci) -{ - static const ma_uint8 g_bitalloc_code_tab[] = { - 0,17, 3, 4, 5,6,7, 8,9,10,11,12,13,14,15,16, - 0,17,18, 3,19,4,5, 6,7, 8, 9,10,11,12,13,16, - 0,17,18, 3,19,4,5,16, - 0,17,18,16, - 0,17,18,19, 4,5,6, 7,8, 9,10,11,12,13,14,15, - 0,17,18, 3,19,4,5, 6,7, 8, 9,10,11,12,13,14, - 0, 2, 3, 4, 5,6,7, 8,9,10,11,12,13,14,15,16 - }; - const ma_dr_mp3_L12_subband_alloc *subband_alloc = ma_dr_mp3_L12_subband_alloc_table(hdr, sci); - int i, k = 0, ba_bits = 0; - const ma_uint8 *ba_code_tab = g_bitalloc_code_tab; - for (i = 0; i < sci->total_bands; i++) - { - ma_uint8 ba; - if (i == k) - { - k += subband_alloc->band_count; - ba_bits = subband_alloc->code_tab_width; - ba_code_tab = g_bitalloc_code_tab + subband_alloc->tab_offset; - subband_alloc++; - } - ba = ba_code_tab[ma_dr_mp3_bs_get_bits(bs, ba_bits)]; - sci->bitalloc[2*i] = ba; - if (i < sci->stereo_bands) - { - ba = ba_code_tab[ma_dr_mp3_bs_get_bits(bs, ba_bits)]; - } - sci->bitalloc[2*i + 1] = sci->stereo_bands ? ba : 0; - } - for (i = 0; i < 2*sci->total_bands; i++) - { - sci->scfcod[i] = (ma_uint8)(sci->bitalloc[i] ? MA_DR_MP3_HDR_IS_LAYER_1(hdr) ? 2 : ma_dr_mp3_bs_get_bits(bs, 2) : 6); - } - ma_dr_mp3_L12_read_scalefactors(bs, sci->bitalloc, sci->scfcod, sci->total_bands*2, sci->scf); - for (i = sci->stereo_bands; i < sci->total_bands; i++) - { - sci->bitalloc[2*i + 1] = 0; - } -} -static int ma_dr_mp3_L12_dequantize_granule(float *grbuf, ma_dr_mp3_bs *bs, ma_dr_mp3_L12_scale_info *sci, int group_size) -{ - int i, j, k, choff = 576; - for (j = 0; j < 4; j++) - { - float *dst = grbuf + group_size*j; - for (i = 0; i < 2*sci->total_bands; i++) - { - int ba = sci->bitalloc[i]; - if (ba != 0) - { - if (ba < 17) - { - int half = (1 << (ba - 1)) - 1; - for (k = 0; k < group_size; k++) - { - dst[k] = (float)((int)ma_dr_mp3_bs_get_bits(bs, ba) - half); - } - } else - { - unsigned mod = (2 << (ba - 17)) + 1; - unsigned code = ma_dr_mp3_bs_get_bits(bs, mod + 2 - (mod >> 3)); - for (k = 0; k < group_size; k++, code /= mod) - { - dst[k] = (float)((int)(code % mod - mod/2)); - } - } - } - dst += choff; - choff = 18 - choff; - } - } - return group_size*4; -} -static void ma_dr_mp3_L12_apply_scf_384(ma_dr_mp3_L12_scale_info *sci, const float *scf, float *dst) -{ - int i, k; - MA_DR_MP3_COPY_MEMORY(dst + 576 + sci->stereo_bands*18, dst + sci->stereo_bands*18, (sci->total_bands - sci->stereo_bands)*18*sizeof(float)); - for (i = 0; i < sci->total_bands; i++, dst += 18, scf += 6) - { - for (k = 0; k < 12; k++) - { - dst[k + 0] *= scf[0]; - dst[k + 576] *= scf[3]; - } - } -} -#endif -static int ma_dr_mp3_L3_read_side_info(ma_dr_mp3_bs *bs, ma_dr_mp3_L3_gr_info *gr, const ma_uint8 *hdr) -{ - static const ma_uint8 g_scf_long[8][23] = { - { 6,6,6,6,6,6,8,10,12,14,16,20,24,28,32,38,46,52,60,68,58,54,0 }, - { 12,12,12,12,12,12,16,20,24,28,32,40,48,56,64,76,90,2,2,2,2,2,0 }, - { 6,6,6,6,6,6,8,10,12,14,16,20,24,28,32,38,46,52,60,68,58,54,0 }, - { 6,6,6,6,6,6,8,10,12,14,16,18,22,26,32,38,46,54,62,70,76,36,0 }, - { 6,6,6,6,6,6,8,10,12,14,16,20,24,28,32,38,46,52,60,68,58,54,0 }, - { 4,4,4,4,4,4,6,6,8,8,10,12,16,20,24,28,34,42,50,54,76,158,0 }, - { 4,4,4,4,4,4,6,6,6,8,10,12,16,18,22,28,34,40,46,54,54,192,0 }, - { 4,4,4,4,4,4,6,6,8,10,12,16,20,24,30,38,46,56,68,84,102,26,0 } - }; - static const ma_uint8 g_scf_short[8][40] = { - { 4,4,4,4,4,4,4,4,4,6,6,6,8,8,8,10,10,10,12,12,12,14,14,14,18,18,18,24,24,24,30,30,30,40,40,40,18,18,18,0 }, - { 8,8,8,8,8,8,8,8,8,12,12,12,16,16,16,20,20,20,24,24,24,28,28,28,36,36,36,2,2,2,2,2,2,2,2,2,26,26,26,0 }, - { 4,4,4,4,4,4,4,4,4,6,6,6,6,6,6,8,8,8,10,10,10,14,14,14,18,18,18,26,26,26,32,32,32,42,42,42,18,18,18,0 }, - { 4,4,4,4,4,4,4,4,4,6,6,6,8,8,8,10,10,10,12,12,12,14,14,14,18,18,18,24,24,24,32,32,32,44,44,44,12,12,12,0 }, - { 4,4,4,4,4,4,4,4,4,6,6,6,8,8,8,10,10,10,12,12,12,14,14,14,18,18,18,24,24,24,30,30,30,40,40,40,18,18,18,0 }, - { 4,4,4,4,4,4,4,4,4,4,4,4,6,6,6,8,8,8,10,10,10,12,12,12,14,14,14,18,18,18,22,22,22,30,30,30,56,56,56,0 }, - { 4,4,4,4,4,4,4,4,4,4,4,4,6,6,6,6,6,6,10,10,10,12,12,12,14,14,14,16,16,16,20,20,20,26,26,26,66,66,66,0 }, - { 4,4,4,4,4,4,4,4,4,4,4,4,6,6,6,8,8,8,12,12,12,16,16,16,20,20,20,26,26,26,34,34,34,42,42,42,12,12,12,0 } - }; - static const ma_uint8 g_scf_mixed[8][40] = { - { 6,6,6,6,6,6,6,6,6,8,8,8,10,10,10,12,12,12,14,14,14,18,18,18,24,24,24,30,30,30,40,40,40,18,18,18,0 }, - { 12,12,12,4,4,4,8,8,8,12,12,12,16,16,16,20,20,20,24,24,24,28,28,28,36,36,36,2,2,2,2,2,2,2,2,2,26,26,26,0 }, - { 6,6,6,6,6,6,6,6,6,6,6,6,8,8,8,10,10,10,14,14,14,18,18,18,26,26,26,32,32,32,42,42,42,18,18,18,0 }, - { 6,6,6,6,6,6,6,6,6,8,8,8,10,10,10,12,12,12,14,14,14,18,18,18,24,24,24,32,32,32,44,44,44,12,12,12,0 }, - { 6,6,6,6,6,6,6,6,6,8,8,8,10,10,10,12,12,12,14,14,14,18,18,18,24,24,24,30,30,30,40,40,40,18,18,18,0 }, - { 4,4,4,4,4,4,6,6,4,4,4,6,6,6,8,8,8,10,10,10,12,12,12,14,14,14,18,18,18,22,22,22,30,30,30,56,56,56,0 }, - { 4,4,4,4,4,4,6,6,4,4,4,6,6,6,6,6,6,10,10,10,12,12,12,14,14,14,16,16,16,20,20,20,26,26,26,66,66,66,0 }, - { 4,4,4,4,4,4,6,6,4,4,4,6,6,6,8,8,8,12,12,12,16,16,16,20,20,20,26,26,26,34,34,34,42,42,42,12,12,12,0 } - }; - unsigned tables, scfsi = 0; - int main_data_begin, part_23_sum = 0; - int gr_count = MA_DR_MP3_HDR_IS_MONO(hdr) ? 1 : 2; - int sr_idx = MA_DR_MP3_HDR_GET_MY_SAMPLE_RATE(hdr); sr_idx -= (sr_idx != 0); - if (MA_DR_MP3_HDR_TEST_MPEG1(hdr)) - { - gr_count *= 2; - main_data_begin = ma_dr_mp3_bs_get_bits(bs, 9); - scfsi = ma_dr_mp3_bs_get_bits(bs, 7 + gr_count); - } else - { - main_data_begin = ma_dr_mp3_bs_get_bits(bs, 8 + gr_count) >> gr_count; - } - do - { - if (MA_DR_MP3_HDR_IS_MONO(hdr)) - { - scfsi <<= 4; - } - gr->part_23_length = (ma_uint16)ma_dr_mp3_bs_get_bits(bs, 12); - part_23_sum += gr->part_23_length; - gr->big_values = (ma_uint16)ma_dr_mp3_bs_get_bits(bs, 9); - if (gr->big_values > 288) - { - return -1; - } - gr->global_gain = (ma_uint8)ma_dr_mp3_bs_get_bits(bs, 8); - gr->scalefac_compress = (ma_uint16)ma_dr_mp3_bs_get_bits(bs, MA_DR_MP3_HDR_TEST_MPEG1(hdr) ? 4 : 9); - gr->sfbtab = g_scf_long[sr_idx]; - gr->n_long_sfb = 22; - gr->n_short_sfb = 0; - if (ma_dr_mp3_bs_get_bits(bs, 1)) - { - gr->block_type = (ma_uint8)ma_dr_mp3_bs_get_bits(bs, 2); - if (!gr->block_type) - { - return -1; - } - gr->mixed_block_flag = (ma_uint8)ma_dr_mp3_bs_get_bits(bs, 1); - gr->region_count[0] = 7; - gr->region_count[1] = 255; - if (gr->block_type == MA_DR_MP3_SHORT_BLOCK_TYPE) - { - scfsi &= 0x0F0F; - if (!gr->mixed_block_flag) - { - gr->region_count[0] = 8; - gr->sfbtab = g_scf_short[sr_idx]; - gr->n_long_sfb = 0; - gr->n_short_sfb = 39; - } else - { - gr->sfbtab = g_scf_mixed[sr_idx]; - gr->n_long_sfb = MA_DR_MP3_HDR_TEST_MPEG1(hdr) ? 8 : 6; - gr->n_short_sfb = 30; - } - } - tables = ma_dr_mp3_bs_get_bits(bs, 10); - tables <<= 5; - gr->subblock_gain[0] = (ma_uint8)ma_dr_mp3_bs_get_bits(bs, 3); - gr->subblock_gain[1] = (ma_uint8)ma_dr_mp3_bs_get_bits(bs, 3); - gr->subblock_gain[2] = (ma_uint8)ma_dr_mp3_bs_get_bits(bs, 3); - } else - { - gr->block_type = 0; - gr->mixed_block_flag = 0; - tables = ma_dr_mp3_bs_get_bits(bs, 15); - gr->region_count[0] = (ma_uint8)ma_dr_mp3_bs_get_bits(bs, 4); - gr->region_count[1] = (ma_uint8)ma_dr_mp3_bs_get_bits(bs, 3); - gr->region_count[2] = 255; - } - gr->table_select[0] = (ma_uint8)(tables >> 10); - gr->table_select[1] = (ma_uint8)((tables >> 5) & 31); - gr->table_select[2] = (ma_uint8)((tables) & 31); - gr->preflag = (ma_uint8)(MA_DR_MP3_HDR_TEST_MPEG1(hdr) ? ma_dr_mp3_bs_get_bits(bs, 1) : (gr->scalefac_compress >= 500)); - gr->scalefac_scale = (ma_uint8)ma_dr_mp3_bs_get_bits(bs, 1); - gr->count1_table = (ma_uint8)ma_dr_mp3_bs_get_bits(bs, 1); - gr->scfsi = (ma_uint8)((scfsi >> 12) & 15); - scfsi <<= 4; - gr++; - } while(--gr_count); - if (part_23_sum + bs->pos > bs->limit + main_data_begin*8) - { - return -1; - } - return main_data_begin; -} -static void ma_dr_mp3_L3_read_scalefactors(ma_uint8 *scf, ma_uint8 *ist_pos, const ma_uint8 *scf_size, const ma_uint8 *scf_count, ma_dr_mp3_bs *bitbuf, int scfsi) -{ - int i, k; - for (i = 0; i < 4 && scf_count[i]; i++, scfsi *= 2) - { - int cnt = scf_count[i]; - if (scfsi & 8) - { - MA_DR_MP3_COPY_MEMORY(scf, ist_pos, cnt); - } else - { - int bits = scf_size[i]; - if (!bits) - { - MA_DR_MP3_ZERO_MEMORY(scf, cnt); - MA_DR_MP3_ZERO_MEMORY(ist_pos, cnt); - } else - { - int max_scf = (scfsi < 0) ? (1 << bits) - 1 : -1; - for (k = 0; k < cnt; k++) - { - int s = ma_dr_mp3_bs_get_bits(bitbuf, bits); - ist_pos[k] = (ma_uint8)(s == max_scf ? -1 : s); - scf[k] = (ma_uint8)s; - } - } - } - ist_pos += cnt; - scf += cnt; - } - scf[0] = scf[1] = scf[2] = 0; -} -static float ma_dr_mp3_L3_ldexp_q2(float y, int exp_q2) -{ - static const float g_expfrac[4] = { 9.31322575e-10f,7.83145814e-10f,6.58544508e-10f,5.53767716e-10f }; - int e; - do - { - e = MA_DR_MP3_MIN(30*4, exp_q2); - y *= g_expfrac[e & 3]*(1 << 30 >> (e >> 2)); - } while ((exp_q2 -= e) > 0); - return y; -} -static void ma_dr_mp3_L3_decode_scalefactors(const ma_uint8 *hdr, ma_uint8 *ist_pos, ma_dr_mp3_bs *bs, const ma_dr_mp3_L3_gr_info *gr, float *scf, int ch) -{ - static const ma_uint8 g_scf_partitions[3][28] = { - { 6,5,5, 5,6,5,5,5,6,5, 7,3,11,10,0,0, 7, 7, 7,0, 6, 6,6,3, 8, 8,5,0 }, - { 8,9,6,12,6,9,9,9,6,9,12,6,15,18,0,0, 6,15,12,0, 6,12,9,6, 6,18,9,0 }, - { 9,9,6,12,9,9,9,9,9,9,12,6,18,18,0,0,12,12,12,0,12, 9,9,6,15,12,9,0 } - }; - const ma_uint8 *scf_partition = g_scf_partitions[!!gr->n_short_sfb + !gr->n_long_sfb]; - ma_uint8 scf_size[4], iscf[40]; - int i, scf_shift = gr->scalefac_scale + 1, gain_exp, scfsi = gr->scfsi; - float gain; - if (MA_DR_MP3_HDR_TEST_MPEG1(hdr)) - { - static const ma_uint8 g_scfc_decode[16] = { 0,1,2,3, 12,5,6,7, 9,10,11,13, 14,15,18,19 }; - int part = g_scfc_decode[gr->scalefac_compress]; - scf_size[1] = scf_size[0] = (ma_uint8)(part >> 2); - scf_size[3] = scf_size[2] = (ma_uint8)(part & 3); - } else - { - static const ma_uint8 g_mod[6*4] = { 5,5,4,4,5,5,4,1,4,3,1,1,5,6,6,1,4,4,4,1,4,3,1,1 }; - int k, modprod, sfc, ist = MA_DR_MP3_HDR_TEST_I_STEREO(hdr) && ch; - sfc = gr->scalefac_compress >> ist; - for (k = ist*3*4; sfc >= 0; sfc -= modprod, k += 4) - { - for (modprod = 1, i = 3; i >= 0; i--) - { - scf_size[i] = (ma_uint8)(sfc / modprod % g_mod[k + i]); - modprod *= g_mod[k + i]; - } - } - scf_partition += k; - scfsi = -16; - } - ma_dr_mp3_L3_read_scalefactors(iscf, ist_pos, scf_size, scf_partition, bs, scfsi); - if (gr->n_short_sfb) - { - int sh = 3 - scf_shift; - for (i = 0; i < gr->n_short_sfb; i += 3) - { - iscf[gr->n_long_sfb + i + 0] = (ma_uint8)(iscf[gr->n_long_sfb + i + 0] + (gr->subblock_gain[0] << sh)); - iscf[gr->n_long_sfb + i + 1] = (ma_uint8)(iscf[gr->n_long_sfb + i + 1] + (gr->subblock_gain[1] << sh)); - iscf[gr->n_long_sfb + i + 2] = (ma_uint8)(iscf[gr->n_long_sfb + i + 2] + (gr->subblock_gain[2] << sh)); - } - } else if (gr->preflag) - { - static const ma_uint8 g_preamp[10] = { 1,1,1,1,2,2,3,3,3,2 }; - for (i = 0; i < 10; i++) - { - iscf[11 + i] = (ma_uint8)(iscf[11 + i] + g_preamp[i]); - } - } - gain_exp = gr->global_gain + MA_DR_MP3_BITS_DEQUANTIZER_OUT*4 - 210 - (MA_DR_MP3_HDR_IS_MS_STEREO(hdr) ? 2 : 0); - gain = ma_dr_mp3_L3_ldexp_q2(1 << (MA_DR_MP3_MAX_SCFI/4), MA_DR_MP3_MAX_SCFI - gain_exp); - for (i = 0; i < (int)(gr->n_long_sfb + gr->n_short_sfb); i++) - { - scf[i] = ma_dr_mp3_L3_ldexp_q2(gain, iscf[i] << scf_shift); - } -} -static const float g_ma_dr_mp3_pow43[129 + 16] = { - 0,-1,-2.519842f,-4.326749f,-6.349604f,-8.549880f,-10.902724f,-13.390518f,-16.000000f,-18.720754f,-21.544347f,-24.463781f,-27.473142f,-30.567351f,-33.741992f,-36.993181f, - 0,1,2.519842f,4.326749f,6.349604f,8.549880f,10.902724f,13.390518f,16.000000f,18.720754f,21.544347f,24.463781f,27.473142f,30.567351f,33.741992f,36.993181f,40.317474f,43.711787f,47.173345f,50.699631f,54.288352f,57.937408f,61.644865f,65.408941f,69.227979f,73.100443f,77.024898f,81.000000f,85.024491f,89.097188f,93.216975f,97.382800f,101.593667f,105.848633f,110.146801f,114.487321f,118.869381f,123.292209f,127.755065f,132.257246f,136.798076f,141.376907f,145.993119f,150.646117f,155.335327f,160.060199f,164.820202f,169.614826f,174.443577f,179.305980f,184.201575f,189.129918f,194.090580f,199.083145f,204.107210f,209.162385f,214.248292f,219.364564f,224.510845f,229.686789f,234.892058f,240.126328f,245.389280f,250.680604f,256.000000f,261.347174f,266.721841f,272.123723f,277.552547f,283.008049f,288.489971f,293.998060f,299.532071f,305.091761f,310.676898f,316.287249f,321.922592f,327.582707f,333.267377f,338.976394f,344.709550f,350.466646f,356.247482f,362.051866f,367.879608f,373.730522f,379.604427f,385.501143f,391.420496f,397.362314f,403.326427f,409.312672f,415.320884f,421.350905f,427.402579f,433.475750f,439.570269f,445.685987f,451.822757f,457.980436f,464.158883f,470.357960f,476.577530f,482.817459f,489.077615f,495.357868f,501.658090f,507.978156f,514.317941f,520.677324f,527.056184f,533.454404f,539.871867f,546.308458f,552.764065f,559.238575f,565.731879f,572.243870f,578.774440f,585.323483f,591.890898f,598.476581f,605.080431f,611.702349f,618.342238f,625.000000f,631.675540f,638.368763f,645.079578f -}; -static float ma_dr_mp3_L3_pow_43(int x) -{ - float frac; - int sign, mult = 256; - if (x < 129) - { - return g_ma_dr_mp3_pow43[16 + x]; - } - if (x < 1024) - { - mult = 16; - x <<= 3; - } - sign = 2*x & 64; - frac = (float)((x & 63) - sign) / ((x & ~63) + sign); - return g_ma_dr_mp3_pow43[16 + ((x + sign) >> 6)]*(1.f + frac*((4.f/3) + frac*(2.f/9)))*mult; -} -static void ma_dr_mp3_L3_huffman(float *dst, ma_dr_mp3_bs *bs, const ma_dr_mp3_L3_gr_info *gr_info, const float *scf, int layer3gr_limit) -{ - static const ma_int16 tabs[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 785,785,785,785,784,784,784,784,513,513,513,513,513,513,513,513,256,256,256,256,256,256,256,256,256,256,256,256,256,256,256,256, - -255,1313,1298,1282,785,785,785,785,784,784,784,784,769,769,769,769,256,256,256,256,256,256,256,256,256,256,256,256,256,256,256,256,290,288, - -255,1313,1298,1282,769,769,769,769,529,529,529,529,529,529,529,529,528,528,528,528,528,528,528,528,512,512,512,512,512,512,512,512,290,288, - -253,-318,-351,-367,785,785,785,785,784,784,784,784,769,769,769,769,256,256,256,256,256,256,256,256,256,256,256,256,256,256,256,256,819,818,547,547,275,275,275,275,561,560,515,546,289,274,288,258, - -254,-287,1329,1299,1314,1312,1057,1057,1042,1042,1026,1026,784,784,784,784,529,529,529,529,529,529,529,529,769,769,769,769,768,768,768,768,563,560,306,306,291,259, - -252,-413,-477,-542,1298,-575,1041,1041,784,784,784,784,769,769,769,769,256,256,256,256,256,256,256,256,256,256,256,256,256,256,256,256,-383,-399,1107,1092,1106,1061,849,849,789,789,1104,1091,773,773,1076,1075,341,340,325,309,834,804,577,577,532,532,516,516,832,818,803,816,561,561,531,531,515,546,289,289,288,258, - -252,-429,-493,-559,1057,1057,1042,1042,529,529,529,529,529,529,529,529,784,784,784,784,769,769,769,769,512,512,512,512,512,512,512,512,-382,1077,-415,1106,1061,1104,849,849,789,789,1091,1076,1029,1075,834,834,597,581,340,340,339,324,804,833,532,532,832,772,818,803,817,787,816,771,290,290,290,290,288,258, - -253,-349,-414,-447,-463,1329,1299,-479,1314,1312,1057,1057,1042,1042,1026,1026,785,785,785,785,784,784,784,784,769,769,769,769,768,768,768,768,-319,851,821,-335,836,850,805,849,341,340,325,336,533,533,579,579,564,564,773,832,578,548,563,516,321,276,306,291,304,259, - -251,-572,-733,-830,-863,-879,1041,1041,784,784,784,784,769,769,769,769,256,256,256,256,256,256,256,256,256,256,256,256,256,256,256,256,-511,-527,-543,1396,1351,1381,1366,1395,1335,1380,-559,1334,1138,1138,1063,1063,1350,1392,1031,1031,1062,1062,1364,1363,1120,1120,1333,1348,881,881,881,881,375,374,359,373,343,358,341,325,791,791,1123,1122,-703,1105,1045,-719,865,865,790,790,774,774,1104,1029,338,293,323,308,-799,-815,833,788,772,818,803,816,322,292,307,320,561,531,515,546,289,274,288,258, - -251,-525,-605,-685,-765,-831,-846,1298,1057,1057,1312,1282,785,785,785,785,784,784,784,784,769,769,769,769,512,512,512,512,512,512,512,512,1399,1398,1383,1367,1382,1396,1351,-511,1381,1366,1139,1139,1079,1079,1124,1124,1364,1349,1363,1333,882,882,882,882,807,807,807,807,1094,1094,1136,1136,373,341,535,535,881,775,867,822,774,-591,324,338,-671,849,550,550,866,864,609,609,293,336,534,534,789,835,773,-751,834,804,308,307,833,788,832,772,562,562,547,547,305,275,560,515,290,290, - -252,-397,-477,-557,-622,-653,-719,-735,-750,1329,1299,1314,1057,1057,1042,1042,1312,1282,1024,1024,785,785,785,785,784,784,784,784,769,769,769,769,-383,1127,1141,1111,1126,1140,1095,1110,869,869,883,883,1079,1109,882,882,375,374,807,868,838,881,791,-463,867,822,368,263,852,837,836,-543,610,610,550,550,352,336,534,534,865,774,851,821,850,805,593,533,579,564,773,832,578,578,548,548,577,577,307,276,306,291,516,560,259,259, - -250,-2107,-2507,-2764,-2909,-2974,-3007,-3023,1041,1041,1040,1040,769,769,769,769,256,256,256,256,256,256,256,256,256,256,256,256,256,256,256,256,-767,-1052,-1213,-1277,-1358,-1405,-1469,-1535,-1550,-1582,-1614,-1647,-1662,-1694,-1726,-1759,-1774,-1807,-1822,-1854,-1886,1565,-1919,-1935,-1951,-1967,1731,1730,1580,1717,-1983,1729,1564,-1999,1548,-2015,-2031,1715,1595,-2047,1714,-2063,1610,-2079,1609,-2095,1323,1323,1457,1457,1307,1307,1712,1547,1641,1700,1699,1594,1685,1625,1442,1442,1322,1322,-780,-973,-910,1279,1278,1277,1262,1276,1261,1275,1215,1260,1229,-959,974,974,989,989,-943,735,478,478,495,463,506,414,-1039,1003,958,1017,927,942,987,957,431,476,1272,1167,1228,-1183,1256,-1199,895,895,941,941,1242,1227,1212,1135,1014,1014,490,489,503,487,910,1013,985,925,863,894,970,955,1012,847,-1343,831,755,755,984,909,428,366,754,559,-1391,752,486,457,924,997,698,698,983,893,740,740,908,877,739,739,667,667,953,938,497,287,271,271,683,606,590,712,726,574,302,302,738,736,481,286,526,725,605,711,636,724,696,651,589,681,666,710,364,467,573,695,466,466,301,465,379,379,709,604,665,679,316,316,634,633,436,436,464,269,424,394,452,332,438,363,347,408,393,448,331,422,362,407,392,421,346,406,391,376,375,359,1441,1306,-2367,1290,-2383,1337,-2399,-2415,1426,1321,-2431,1411,1336,-2447,-2463,-2479,1169,1169,1049,1049,1424,1289,1412,1352,1319,-2495,1154,1154,1064,1064,1153,1153,416,390,360,404,403,389,344,374,373,343,358,372,327,357,342,311,356,326,1395,1394,1137,1137,1047,1047,1365,1392,1287,1379,1334,1364,1349,1378,1318,1363,792,792,792,792,1152,1152,1032,1032,1121,1121,1046,1046,1120,1120,1030,1030,-2895,1106,1061,1104,849,849,789,789,1091,1076,1029,1090,1060,1075,833,833,309,324,532,532,832,772,818,803,561,561,531,560,515,546,289,274,288,258, - -250,-1179,-1579,-1836,-1996,-2124,-2253,-2333,-2413,-2477,-2542,-2574,-2607,-2622,-2655,1314,1313,1298,1312,1282,785,785,785,785,1040,1040,1025,1025,768,768,768,768,-766,-798,-830,-862,-895,-911,-927,-943,-959,-975,-991,-1007,-1023,-1039,-1055,-1070,1724,1647,-1103,-1119,1631,1767,1662,1738,1708,1723,-1135,1780,1615,1779,1599,1677,1646,1778,1583,-1151,1777,1567,1737,1692,1765,1722,1707,1630,1751,1661,1764,1614,1736,1676,1763,1750,1645,1598,1721,1691,1762,1706,1582,1761,1566,-1167,1749,1629,767,766,751,765,494,494,735,764,719,749,734,763,447,447,748,718,477,506,431,491,446,476,461,505,415,430,475,445,504,399,460,489,414,503,383,474,429,459,502,502,746,752,488,398,501,473,413,472,486,271,480,270,-1439,-1455,1357,-1471,-1487,-1503,1341,1325,-1519,1489,1463,1403,1309,-1535,1372,1448,1418,1476,1356,1462,1387,-1551,1475,1340,1447,1402,1386,-1567,1068,1068,1474,1461,455,380,468,440,395,425,410,454,364,467,466,464,453,269,409,448,268,432,1371,1473,1432,1417,1308,1460,1355,1446,1459,1431,1083,1083,1401,1416,1458,1445,1067,1067,1370,1457,1051,1051,1291,1430,1385,1444,1354,1415,1400,1443,1082,1082,1173,1113,1186,1066,1185,1050,-1967,1158,1128,1172,1097,1171,1081,-1983,1157,1112,416,266,375,400,1170,1142,1127,1065,793,793,1169,1033,1156,1096,1141,1111,1155,1080,1126,1140,898,898,808,808,897,897,792,792,1095,1152,1032,1125,1110,1139,1079,1124,882,807,838,881,853,791,-2319,867,368,263,822,852,837,866,806,865,-2399,851,352,262,534,534,821,836,594,594,549,549,593,593,533,533,848,773,579,579,564,578,548,563,276,276,577,576,306,291,516,560,305,305,275,259, - -251,-892,-2058,-2620,-2828,-2957,-3023,-3039,1041,1041,1040,1040,769,769,769,769,256,256,256,256,256,256,256,256,256,256,256,256,256,256,256,256,-511,-527,-543,-559,1530,-575,-591,1528,1527,1407,1526,1391,1023,1023,1023,1023,1525,1375,1268,1268,1103,1103,1087,1087,1039,1039,1523,-604,815,815,815,815,510,495,509,479,508,463,507,447,431,505,415,399,-734,-782,1262,-815,1259,1244,-831,1258,1228,-847,-863,1196,-879,1253,987,987,748,-767,493,493,462,477,414,414,686,669,478,446,461,445,474,429,487,458,412,471,1266,1264,1009,1009,799,799,-1019,-1276,-1452,-1581,-1677,-1757,-1821,-1886,-1933,-1997,1257,1257,1483,1468,1512,1422,1497,1406,1467,1496,1421,1510,1134,1134,1225,1225,1466,1451,1374,1405,1252,1252,1358,1480,1164,1164,1251,1251,1238,1238,1389,1465,-1407,1054,1101,-1423,1207,-1439,830,830,1248,1038,1237,1117,1223,1148,1236,1208,411,426,395,410,379,269,1193,1222,1132,1235,1221,1116,976,976,1192,1162,1177,1220,1131,1191,963,963,-1647,961,780,-1663,558,558,994,993,437,408,393,407,829,978,813,797,947,-1743,721,721,377,392,844,950,828,890,706,706,812,859,796,960,948,843,934,874,571,571,-1919,690,555,689,421,346,539,539,944,779,918,873,932,842,903,888,570,570,931,917,674,674,-2575,1562,-2591,1609,-2607,1654,1322,1322,1441,1441,1696,1546,1683,1593,1669,1624,1426,1426,1321,1321,1639,1680,1425,1425,1305,1305,1545,1668,1608,1623,1667,1592,1638,1666,1320,1320,1652,1607,1409,1409,1304,1304,1288,1288,1664,1637,1395,1395,1335,1335,1622,1636,1394,1394,1319,1319,1606,1621,1392,1392,1137,1137,1137,1137,345,390,360,375,404,373,1047,-2751,-2767,-2783,1062,1121,1046,-2799,1077,-2815,1106,1061,789,789,1105,1104,263,355,310,340,325,354,352,262,339,324,1091,1076,1029,1090,1060,1075,833,833,788,788,1088,1028,818,818,803,803,561,561,531,531,816,771,546,546,289,274,288,258, - -253,-317,-381,-446,-478,-509,1279,1279,-811,-1179,-1451,-1756,-1900,-2028,-2189,-2253,-2333,-2414,-2445,-2511,-2526,1313,1298,-2559,1041,1041,1040,1040,1025,1025,1024,1024,1022,1007,1021,991,1020,975,1019,959,687,687,1018,1017,671,671,655,655,1016,1015,639,639,758,758,623,623,757,607,756,591,755,575,754,559,543,543,1009,783,-575,-621,-685,-749,496,-590,750,749,734,748,974,989,1003,958,988,973,1002,942,987,957,972,1001,926,986,941,971,956,1000,910,985,925,999,894,970,-1071,-1087,-1102,1390,-1135,1436,1509,1451,1374,-1151,1405,1358,1480,1420,-1167,1507,1494,1389,1342,1465,1435,1450,1326,1505,1310,1493,1373,1479,1404,1492,1464,1419,428,443,472,397,736,526,464,464,486,457,442,471,484,482,1357,1449,1434,1478,1388,1491,1341,1490,1325,1489,1463,1403,1309,1477,1372,1448,1418,1433,1476,1356,1462,1387,-1439,1475,1340,1447,1402,1474,1324,1461,1371,1473,269,448,1432,1417,1308,1460,-1711,1459,-1727,1441,1099,1099,1446,1386,1431,1401,-1743,1289,1083,1083,1160,1160,1458,1445,1067,1067,1370,1457,1307,1430,1129,1129,1098,1098,268,432,267,416,266,400,-1887,1144,1187,1082,1173,1113,1186,1066,1050,1158,1128,1143,1172,1097,1171,1081,420,391,1157,1112,1170,1142,1127,1065,1169,1049,1156,1096,1141,1111,1155,1080,1126,1154,1064,1153,1140,1095,1048,-2159,1125,1110,1137,-2175,823,823,1139,1138,807,807,384,264,368,263,868,838,853,791,867,822,852,837,866,806,865,790,-2319,851,821,836,352,262,850,805,849,-2399,533,533,835,820,336,261,578,548,563,577,532,532,832,772,562,562,547,547,305,275,560,515,290,290,288,258 }; - static const ma_uint8 tab32[] = { 130,162,193,209,44,28,76,140,9,9,9,9,9,9,9,9,190,254,222,238,126,94,157,157,109,61,173,205}; - static const ma_uint8 tab33[] = { 252,236,220,204,188,172,156,140,124,108,92,76,60,44,28,12 }; - static const ma_int16 tabindex[2*16] = { 0,32,64,98,0,132,180,218,292,364,426,538,648,746,0,1126,1460,1460,1460,1460,1460,1460,1460,1460,1842,1842,1842,1842,1842,1842,1842,1842 }; - static const ma_uint8 g_linbits[] = { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,2,3,4,6,8,10,13,4,5,6,7,8,9,11,13 }; -#define MA_DR_MP3_PEEK_BITS(n) (bs_cache >> (32 - (n))) -#define MA_DR_MP3_FLUSH_BITS(n) { bs_cache <<= (n); bs_sh += (n); } -#define MA_DR_MP3_CHECK_BITS while (bs_sh >= 0) { bs_cache |= (ma_uint32)*bs_next_ptr++ << bs_sh; bs_sh -= 8; } -#define MA_DR_MP3_BSPOS ((bs_next_ptr - bs->buf)*8 - 24 + bs_sh) - float one = 0.0f; - int ireg = 0, big_val_cnt = gr_info->big_values; - const ma_uint8 *sfb = gr_info->sfbtab; - const ma_uint8 *bs_next_ptr = bs->buf + bs->pos/8; - ma_uint32 bs_cache = (((bs_next_ptr[0]*256u + bs_next_ptr[1])*256u + bs_next_ptr[2])*256u + bs_next_ptr[3]) << (bs->pos & 7); - int pairs_to_decode, np, bs_sh = (bs->pos & 7) - 8; - bs_next_ptr += 4; - while (big_val_cnt > 0) - { - int tab_num = gr_info->table_select[ireg]; - int sfb_cnt = gr_info->region_count[ireg++]; - const ma_int16 *codebook = tabs + tabindex[tab_num]; - int linbits = g_linbits[tab_num]; - if (linbits) - { - do - { - np = *sfb++ / 2; - pairs_to_decode = MA_DR_MP3_MIN(big_val_cnt, np); - one = *scf++; - do - { - int j, w = 5; - int leaf = codebook[MA_DR_MP3_PEEK_BITS(w)]; - while (leaf < 0) - { - MA_DR_MP3_FLUSH_BITS(w); - w = leaf & 7; - leaf = codebook[MA_DR_MP3_PEEK_BITS(w) - (leaf >> 3)]; - } - MA_DR_MP3_FLUSH_BITS(leaf >> 8); - for (j = 0; j < 2; j++, dst++, leaf >>= 4) - { - int lsb = leaf & 0x0F; - if (lsb == 15) - { - lsb += MA_DR_MP3_PEEK_BITS(linbits); - MA_DR_MP3_FLUSH_BITS(linbits); - MA_DR_MP3_CHECK_BITS; - *dst = one*ma_dr_mp3_L3_pow_43(lsb)*((ma_int32)bs_cache < 0 ? -1: 1); - } else - { - *dst = g_ma_dr_mp3_pow43[16 + lsb - 16*(bs_cache >> 31)]*one; - } - MA_DR_MP3_FLUSH_BITS(lsb ? 1 : 0); - } - MA_DR_MP3_CHECK_BITS; - } while (--pairs_to_decode); - } while ((big_val_cnt -= np) > 0 && --sfb_cnt >= 0); - } else - { - do - { - np = *sfb++ / 2; - pairs_to_decode = MA_DR_MP3_MIN(big_val_cnt, np); - one = *scf++; - do - { - int j, w = 5; - int leaf = codebook[MA_DR_MP3_PEEK_BITS(w)]; - while (leaf < 0) - { - MA_DR_MP3_FLUSH_BITS(w); - w = leaf & 7; - leaf = codebook[MA_DR_MP3_PEEK_BITS(w) - (leaf >> 3)]; - } - MA_DR_MP3_FLUSH_BITS(leaf >> 8); - for (j = 0; j < 2; j++, dst++, leaf >>= 4) - { - int lsb = leaf & 0x0F; - *dst = g_ma_dr_mp3_pow43[16 + lsb - 16*(bs_cache >> 31)]*one; - MA_DR_MP3_FLUSH_BITS(lsb ? 1 : 0); - } - MA_DR_MP3_CHECK_BITS; - } while (--pairs_to_decode); - } while ((big_val_cnt -= np) > 0 && --sfb_cnt >= 0); - } - } - for (np = 1 - big_val_cnt;; dst += 4) - { - const ma_uint8 *codebook_count1 = (gr_info->count1_table) ? tab33 : tab32; - int leaf = codebook_count1[MA_DR_MP3_PEEK_BITS(4)]; - if (!(leaf & 8)) - { - leaf = codebook_count1[(leaf >> 3) + (bs_cache << 4 >> (32 - (leaf & 3)))]; - } - MA_DR_MP3_FLUSH_BITS(leaf & 7); - if (MA_DR_MP3_BSPOS > layer3gr_limit) - { - break; - } -#define MA_DR_MP3_RELOAD_SCALEFACTOR if (!--np) { np = *sfb++/2; if (!np) break; one = *scf++; } -#define MA_DR_MP3_DEQ_COUNT1(s) if (leaf & (128 >> s)) { dst[s] = ((ma_int32)bs_cache < 0) ? -one : one; MA_DR_MP3_FLUSH_BITS(1) } - MA_DR_MP3_RELOAD_SCALEFACTOR; - MA_DR_MP3_DEQ_COUNT1(0); - MA_DR_MP3_DEQ_COUNT1(1); - MA_DR_MP3_RELOAD_SCALEFACTOR; - MA_DR_MP3_DEQ_COUNT1(2); - MA_DR_MP3_DEQ_COUNT1(3); - MA_DR_MP3_CHECK_BITS; - } - bs->pos = layer3gr_limit; -} -static void ma_dr_mp3_L3_midside_stereo(float *left, int n) -{ - int i = 0; - float *right = left + 576; -#if MA_DR_MP3_HAVE_SIMD - if (ma_dr_mp3_have_simd()) - { - for (; i < n - 3; i += 4) - { - ma_dr_mp3_f4 vl = MA_DR_MP3_VLD(left + i); - ma_dr_mp3_f4 vr = MA_DR_MP3_VLD(right + i); - MA_DR_MP3_VSTORE(left + i, MA_DR_MP3_VADD(vl, vr)); - MA_DR_MP3_VSTORE(right + i, MA_DR_MP3_VSUB(vl, vr)); - } -#ifdef __GNUC__ - if (__builtin_constant_p(n % 4 == 0) && n % 4 == 0) - return; -#endif - } -#endif - for (; i < n; i++) - { - float a = left[i]; - float b = right[i]; - left[i] = a + b; - right[i] = a - b; - } -} -static void ma_dr_mp3_L3_intensity_stereo_band(float *left, int n, float kl, float kr) -{ - int i; - for (i = 0; i < n; i++) - { - left[i + 576] = left[i]*kr; - left[i] = left[i]*kl; - } -} -static void ma_dr_mp3_L3_stereo_top_band(const float *right, const ma_uint8 *sfb, int nbands, int max_band[3]) -{ - int i, k; - max_band[0] = max_band[1] = max_band[2] = -1; - for (i = 0; i < nbands; i++) - { - for (k = 0; k < sfb[i]; k += 2) - { - if (right[k] != 0 || right[k + 1] != 0) - { - max_band[i % 3] = i; - break; - } - } - right += sfb[i]; - } -} -static void ma_dr_mp3_L3_stereo_process(float *left, const ma_uint8 *ist_pos, const ma_uint8 *sfb, const ma_uint8 *hdr, int max_band[3], int mpeg2_sh) -{ - static const float g_pan[7*2] = { 0,1,0.21132487f,0.78867513f,0.36602540f,0.63397460f,0.5f,0.5f,0.63397460f,0.36602540f,0.78867513f,0.21132487f,1,0 }; - unsigned i, max_pos = MA_DR_MP3_HDR_TEST_MPEG1(hdr) ? 7 : 64; - for (i = 0; sfb[i]; i++) - { - unsigned ipos = ist_pos[i]; - if ((int)i > max_band[i % 3] && ipos < max_pos) - { - float kl, kr, s = MA_DR_MP3_HDR_TEST_MS_STEREO(hdr) ? 1.41421356f : 1; - if (MA_DR_MP3_HDR_TEST_MPEG1(hdr)) - { - kl = g_pan[2*ipos]; - kr = g_pan[2*ipos + 1]; - } else - { - kl = 1; - kr = ma_dr_mp3_L3_ldexp_q2(1, (ipos + 1) >> 1 << mpeg2_sh); - if (ipos & 1) - { - kl = kr; - kr = 1; - } - } - ma_dr_mp3_L3_intensity_stereo_band(left, sfb[i], kl*s, kr*s); - } else if (MA_DR_MP3_HDR_TEST_MS_STEREO(hdr)) - { - ma_dr_mp3_L3_midside_stereo(left, sfb[i]); - } - left += sfb[i]; - } -} -static void ma_dr_mp3_L3_intensity_stereo(float *left, ma_uint8 *ist_pos, const ma_dr_mp3_L3_gr_info *gr, const ma_uint8 *hdr) -{ - int max_band[3], n_sfb = gr->n_long_sfb + gr->n_short_sfb; - int i, max_blocks = gr->n_short_sfb ? 3 : 1; - ma_dr_mp3_L3_stereo_top_band(left + 576, gr->sfbtab, n_sfb, max_band); - if (gr->n_long_sfb) - { - max_band[0] = max_band[1] = max_band[2] = MA_DR_MP3_MAX(MA_DR_MP3_MAX(max_band[0], max_band[1]), max_band[2]); - } - for (i = 0; i < max_blocks; i++) - { - int default_pos = MA_DR_MP3_HDR_TEST_MPEG1(hdr) ? 3 : 0; - int itop = n_sfb - max_blocks + i; - int prev = itop - max_blocks; - ist_pos[itop] = (ma_uint8)(max_band[i] >= prev ? default_pos : ist_pos[prev]); - } - ma_dr_mp3_L3_stereo_process(left, ist_pos, gr->sfbtab, hdr, max_band, gr[1].scalefac_compress & 1); -} -static void ma_dr_mp3_L3_reorder(float *grbuf, float *scratch, const ma_uint8 *sfb) -{ - int i, len; - float *src = grbuf, *dst = scratch; - for (;0 != (len = *sfb); sfb += 3, src += 2*len) - { - for (i = 0; i < len; i++, src++) - { - *dst++ = src[0*len]; - *dst++ = src[1*len]; - *dst++ = src[2*len]; - } - } - MA_DR_MP3_COPY_MEMORY(grbuf, scratch, (dst - scratch)*sizeof(float)); -} -static void ma_dr_mp3_L3_antialias(float *grbuf, int nbands) -{ - static const float g_aa[2][8] = { - {0.85749293f,0.88174200f,0.94962865f,0.98331459f,0.99551782f,0.99916056f,0.99989920f,0.99999316f}, - {0.51449576f,0.47173197f,0.31337745f,0.18191320f,0.09457419f,0.04096558f,0.01419856f,0.00369997f} - }; - for (; nbands > 0; nbands--, grbuf += 18) - { - int i = 0; -#if MA_DR_MP3_HAVE_SIMD - if (ma_dr_mp3_have_simd()) for (; i < 8; i += 4) - { - ma_dr_mp3_f4 vu = MA_DR_MP3_VLD(grbuf + 18 + i); - ma_dr_mp3_f4 vd = MA_DR_MP3_VLD(grbuf + 14 - i); - ma_dr_mp3_f4 vc0 = MA_DR_MP3_VLD(g_aa[0] + i); - ma_dr_mp3_f4 vc1 = MA_DR_MP3_VLD(g_aa[1] + i); - vd = MA_DR_MP3_VREV(vd); - MA_DR_MP3_VSTORE(grbuf + 18 + i, MA_DR_MP3_VSUB(MA_DR_MP3_VMUL(vu, vc0), MA_DR_MP3_VMUL(vd, vc1))); - vd = MA_DR_MP3_VADD(MA_DR_MP3_VMUL(vu, vc1), MA_DR_MP3_VMUL(vd, vc0)); - MA_DR_MP3_VSTORE(grbuf + 14 - i, MA_DR_MP3_VREV(vd)); - } -#endif -#ifndef MA_DR_MP3_ONLY_SIMD - for(; i < 8; i++) - { - float u = grbuf[18 + i]; - float d = grbuf[17 - i]; - grbuf[18 + i] = u*g_aa[0][i] - d*g_aa[1][i]; - grbuf[17 - i] = u*g_aa[1][i] + d*g_aa[0][i]; - } -#endif - } -} -static void ma_dr_mp3_L3_dct3_9(float *y) -{ - float s0, s1, s2, s3, s4, s5, s6, s7, s8, t0, t2, t4; - s0 = y[0]; s2 = y[2]; s4 = y[4]; s6 = y[6]; s8 = y[8]; - t0 = s0 + s6*0.5f; - s0 -= s6; - t4 = (s4 + s2)*0.93969262f; - t2 = (s8 + s2)*0.76604444f; - s6 = (s4 - s8)*0.17364818f; - s4 += s8 - s2; - s2 = s0 - s4*0.5f; - y[4] = s4 + s0; - s8 = t0 - t2 + s6; - s0 = t0 - t4 + t2; - s4 = t0 + t4 - s6; - s1 = y[1]; s3 = y[3]; s5 = y[5]; s7 = y[7]; - s3 *= 0.86602540f; - t0 = (s5 + s1)*0.98480775f; - t4 = (s5 - s7)*0.34202014f; - t2 = (s1 + s7)*0.64278761f; - s1 = (s1 - s5 - s7)*0.86602540f; - s5 = t0 - s3 - t2; - s7 = t4 - s3 - t0; - s3 = t4 + s3 - t2; - y[0] = s4 - s7; - y[1] = s2 + s1; - y[2] = s0 - s3; - y[3] = s8 + s5; - y[5] = s8 - s5; - y[6] = s0 + s3; - y[7] = s2 - s1; - y[8] = s4 + s7; -} -static void ma_dr_mp3_L3_imdct36(float *grbuf, float *overlap, const float *window, int nbands) -{ - int i, j; - static const float g_twid9[18] = { - 0.73727734f,0.79335334f,0.84339145f,0.88701083f,0.92387953f,0.95371695f,0.97629601f,0.99144486f,0.99904822f,0.67559021f,0.60876143f,0.53729961f,0.46174861f,0.38268343f,0.30070580f,0.21643961f,0.13052619f,0.04361938f - }; - for (j = 0; j < nbands; j++, grbuf += 18, overlap += 9) - { - float co[9], si[9]; - co[0] = -grbuf[0]; - si[0] = grbuf[17]; - for (i = 0; i < 4; i++) - { - si[8 - 2*i] = grbuf[4*i + 1] - grbuf[4*i + 2]; - co[1 + 2*i] = grbuf[4*i + 1] + grbuf[4*i + 2]; - si[7 - 2*i] = grbuf[4*i + 4] - grbuf[4*i + 3]; - co[2 + 2*i] = -(grbuf[4*i + 3] + grbuf[4*i + 4]); - } - ma_dr_mp3_L3_dct3_9(co); - ma_dr_mp3_L3_dct3_9(si); - si[1] = -si[1]; - si[3] = -si[3]; - si[5] = -si[5]; - si[7] = -si[7]; - i = 0; -#if MA_DR_MP3_HAVE_SIMD - if (ma_dr_mp3_have_simd()) for (; i < 8; i += 4) - { - ma_dr_mp3_f4 vovl = MA_DR_MP3_VLD(overlap + i); - ma_dr_mp3_f4 vc = MA_DR_MP3_VLD(co + i); - ma_dr_mp3_f4 vs = MA_DR_MP3_VLD(si + i); - ma_dr_mp3_f4 vr0 = MA_DR_MP3_VLD(g_twid9 + i); - ma_dr_mp3_f4 vr1 = MA_DR_MP3_VLD(g_twid9 + 9 + i); - ma_dr_mp3_f4 vw0 = MA_DR_MP3_VLD(window + i); - ma_dr_mp3_f4 vw1 = MA_DR_MP3_VLD(window + 9 + i); - ma_dr_mp3_f4 vsum = MA_DR_MP3_VADD(MA_DR_MP3_VMUL(vc, vr1), MA_DR_MP3_VMUL(vs, vr0)); - MA_DR_MP3_VSTORE(overlap + i, MA_DR_MP3_VSUB(MA_DR_MP3_VMUL(vc, vr0), MA_DR_MP3_VMUL(vs, vr1))); - MA_DR_MP3_VSTORE(grbuf + i, MA_DR_MP3_VSUB(MA_DR_MP3_VMUL(vovl, vw0), MA_DR_MP3_VMUL(vsum, vw1))); - vsum = MA_DR_MP3_VADD(MA_DR_MP3_VMUL(vovl, vw1), MA_DR_MP3_VMUL(vsum, vw0)); - MA_DR_MP3_VSTORE(grbuf + 14 - i, MA_DR_MP3_VREV(vsum)); - } -#endif - for (; i < 9; i++) - { - float ovl = overlap[i]; - float sum = co[i]*g_twid9[9 + i] + si[i]*g_twid9[0 + i]; - overlap[i] = co[i]*g_twid9[0 + i] - si[i]*g_twid9[9 + i]; - grbuf[i] = ovl*window[0 + i] - sum*window[9 + i]; - grbuf[17 - i] = ovl*window[9 + i] + sum*window[0 + i]; - } - } -} -static void ma_dr_mp3_L3_idct3(float x0, float x1, float x2, float *dst) -{ - float m1 = x1*0.86602540f; - float a1 = x0 - x2*0.5f; - dst[1] = x0 + x2; - dst[0] = a1 + m1; - dst[2] = a1 - m1; -} -static void ma_dr_mp3_L3_imdct12(float *x, float *dst, float *overlap) -{ - static const float g_twid3[6] = { 0.79335334f,0.92387953f,0.99144486f, 0.60876143f,0.38268343f,0.13052619f }; - float co[3], si[3]; - int i; - ma_dr_mp3_L3_idct3(-x[0], x[6] + x[3], x[12] + x[9], co); - ma_dr_mp3_L3_idct3(x[15], x[12] - x[9], x[6] - x[3], si); - si[1] = -si[1]; - for (i = 0; i < 3; i++) - { - float ovl = overlap[i]; - float sum = co[i]*g_twid3[3 + i] + si[i]*g_twid3[0 + i]; - overlap[i] = co[i]*g_twid3[0 + i] - si[i]*g_twid3[3 + i]; - dst[i] = ovl*g_twid3[2 - i] - sum*g_twid3[5 - i]; - dst[5 - i] = ovl*g_twid3[5 - i] + sum*g_twid3[2 - i]; - } -} -static void ma_dr_mp3_L3_imdct_short(float *grbuf, float *overlap, int nbands) -{ - for (;nbands > 0; nbands--, overlap += 9, grbuf += 18) - { - float tmp[18]; - MA_DR_MP3_COPY_MEMORY(tmp, grbuf, sizeof(tmp)); - MA_DR_MP3_COPY_MEMORY(grbuf, overlap, 6*sizeof(float)); - ma_dr_mp3_L3_imdct12(tmp, grbuf + 6, overlap + 6); - ma_dr_mp3_L3_imdct12(tmp + 1, grbuf + 12, overlap + 6); - ma_dr_mp3_L3_imdct12(tmp + 2, overlap, overlap + 6); - } -} -static void ma_dr_mp3_L3_change_sign(float *grbuf) -{ - int b, i; - for (b = 0, grbuf += 18; b < 32; b += 2, grbuf += 36) - for (i = 1; i < 18; i += 2) - grbuf[i] = -grbuf[i]; -} -static void ma_dr_mp3_L3_imdct_gr(float *grbuf, float *overlap, unsigned block_type, unsigned n_long_bands) -{ - static const float g_mdct_window[2][18] = { - { 0.99904822f,0.99144486f,0.97629601f,0.95371695f,0.92387953f,0.88701083f,0.84339145f,0.79335334f,0.73727734f,0.04361938f,0.13052619f,0.21643961f,0.30070580f,0.38268343f,0.46174861f,0.53729961f,0.60876143f,0.67559021f }, - { 1,1,1,1,1,1,0.99144486f,0.92387953f,0.79335334f,0,0,0,0,0,0,0.13052619f,0.38268343f,0.60876143f } - }; - if (n_long_bands) - { - ma_dr_mp3_L3_imdct36(grbuf, overlap, g_mdct_window[0], n_long_bands); - grbuf += 18*n_long_bands; - overlap += 9*n_long_bands; - } - if (block_type == MA_DR_MP3_SHORT_BLOCK_TYPE) - ma_dr_mp3_L3_imdct_short(grbuf, overlap, 32 - n_long_bands); - else - ma_dr_mp3_L3_imdct36(grbuf, overlap, g_mdct_window[block_type == MA_DR_MP3_STOP_BLOCK_TYPE], 32 - n_long_bands); -} -static void ma_dr_mp3_L3_save_reservoir(ma_dr_mp3dec *h, ma_dr_mp3dec_scratch *s) -{ - int pos = (s->bs.pos + 7)/8u; - int remains = s->bs.limit/8u - pos; - if (remains > MA_DR_MP3_MAX_BITRESERVOIR_BYTES) - { - pos += remains - MA_DR_MP3_MAX_BITRESERVOIR_BYTES; - remains = MA_DR_MP3_MAX_BITRESERVOIR_BYTES; - } - if (remains > 0) - { - MA_DR_MP3_MOVE_MEMORY(h->reserv_buf, s->maindata + pos, remains); - } - h->reserv = remains; -} -static int ma_dr_mp3_L3_restore_reservoir(ma_dr_mp3dec *h, ma_dr_mp3_bs *bs, ma_dr_mp3dec_scratch *s, int main_data_begin) -{ - int frame_bytes = (bs->limit - bs->pos)/8; - int bytes_have = MA_DR_MP3_MIN(h->reserv, main_data_begin); - MA_DR_MP3_COPY_MEMORY(s->maindata, h->reserv_buf + MA_DR_MP3_MAX(0, h->reserv - main_data_begin), MA_DR_MP3_MIN(h->reserv, main_data_begin)); - MA_DR_MP3_COPY_MEMORY(s->maindata + bytes_have, bs->buf + bs->pos/8, frame_bytes); - ma_dr_mp3_bs_init(&s->bs, s->maindata, bytes_have + frame_bytes); - return h->reserv >= main_data_begin; -} -static void ma_dr_mp3_L3_decode(ma_dr_mp3dec *h, ma_dr_mp3dec_scratch *s, ma_dr_mp3_L3_gr_info *gr_info, int nch) -{ - int ch; - for (ch = 0; ch < nch; ch++) - { - int layer3gr_limit = s->bs.pos + gr_info[ch].part_23_length; - ma_dr_mp3_L3_decode_scalefactors(h->header, s->ist_pos[ch], &s->bs, gr_info + ch, s->scf, ch); - ma_dr_mp3_L3_huffman(s->grbuf[ch], &s->bs, gr_info + ch, s->scf, layer3gr_limit); - } - if (MA_DR_MP3_HDR_TEST_I_STEREO(h->header)) - { - ma_dr_mp3_L3_intensity_stereo(s->grbuf[0], s->ist_pos[1], gr_info, h->header); - } else if (MA_DR_MP3_HDR_IS_MS_STEREO(h->header)) - { - ma_dr_mp3_L3_midside_stereo(s->grbuf[0], 576); - } - for (ch = 0; ch < nch; ch++, gr_info++) - { - int aa_bands = 31; - int n_long_bands = (gr_info->mixed_block_flag ? 2 : 0) << (int)(MA_DR_MP3_HDR_GET_MY_SAMPLE_RATE(h->header) == 2); - if (gr_info->n_short_sfb) - { - aa_bands = n_long_bands - 1; - ma_dr_mp3_L3_reorder(s->grbuf[ch] + n_long_bands*18, s->syn[0], gr_info->sfbtab + gr_info->n_long_sfb); - } - ma_dr_mp3_L3_antialias(s->grbuf[ch], aa_bands); - ma_dr_mp3_L3_imdct_gr(s->grbuf[ch], h->mdct_overlap[ch], gr_info->block_type, n_long_bands); - ma_dr_mp3_L3_change_sign(s->grbuf[ch]); - } -} -static void ma_dr_mp3d_DCT_II(float *grbuf, int n) -{ - static const float g_sec[24] = { - 10.19000816f,0.50060302f,0.50241929f,3.40760851f,0.50547093f,0.52249861f,2.05778098f,0.51544732f,0.56694406f,1.48416460f,0.53104258f,0.64682180f,1.16943991f,0.55310392f,0.78815460f,0.97256821f,0.58293498f,1.06067765f,0.83934963f,0.62250412f,1.72244716f,0.74453628f,0.67480832f,5.10114861f - }; - int i, k = 0; -#if MA_DR_MP3_HAVE_SIMD - if (ma_dr_mp3_have_simd()) for (; k < n; k += 4) - { - ma_dr_mp3_f4 t[4][8], *x; - float *y = grbuf + k; - for (x = t[0], i = 0; i < 8; i++, x++) - { - ma_dr_mp3_f4 x0 = MA_DR_MP3_VLD(&y[i*18]); - ma_dr_mp3_f4 x1 = MA_DR_MP3_VLD(&y[(15 - i)*18]); - ma_dr_mp3_f4 x2 = MA_DR_MP3_VLD(&y[(16 + i)*18]); - ma_dr_mp3_f4 x3 = MA_DR_MP3_VLD(&y[(31 - i)*18]); - ma_dr_mp3_f4 t0 = MA_DR_MP3_VADD(x0, x3); - ma_dr_mp3_f4 t1 = MA_DR_MP3_VADD(x1, x2); - ma_dr_mp3_f4 t2 = MA_DR_MP3_VMUL_S(MA_DR_MP3_VSUB(x1, x2), g_sec[3*i + 0]); - ma_dr_mp3_f4 t3 = MA_DR_MP3_VMUL_S(MA_DR_MP3_VSUB(x0, x3), g_sec[3*i + 1]); - x[0] = MA_DR_MP3_VADD(t0, t1); - x[8] = MA_DR_MP3_VMUL_S(MA_DR_MP3_VSUB(t0, t1), g_sec[3*i + 2]); - x[16] = MA_DR_MP3_VADD(t3, t2); - x[24] = MA_DR_MP3_VMUL_S(MA_DR_MP3_VSUB(t3, t2), g_sec[3*i + 2]); - } - for (x = t[0], i = 0; i < 4; i++, x += 8) - { - ma_dr_mp3_f4 x0 = x[0], x1 = x[1], x2 = x[2], x3 = x[3], x4 = x[4], x5 = x[5], x6 = x[6], x7 = x[7], xt; - xt = MA_DR_MP3_VSUB(x0, x7); x0 = MA_DR_MP3_VADD(x0, x7); - x7 = MA_DR_MP3_VSUB(x1, x6); x1 = MA_DR_MP3_VADD(x1, x6); - x6 = MA_DR_MP3_VSUB(x2, x5); x2 = MA_DR_MP3_VADD(x2, x5); - x5 = MA_DR_MP3_VSUB(x3, x4); x3 = MA_DR_MP3_VADD(x3, x4); - x4 = MA_DR_MP3_VSUB(x0, x3); x0 = MA_DR_MP3_VADD(x0, x3); - x3 = MA_DR_MP3_VSUB(x1, x2); x1 = MA_DR_MP3_VADD(x1, x2); - x[0] = MA_DR_MP3_VADD(x0, x1); - x[4] = MA_DR_MP3_VMUL_S(MA_DR_MP3_VSUB(x0, x1), 0.70710677f); - x5 = MA_DR_MP3_VADD(x5, x6); - x6 = MA_DR_MP3_VMUL_S(MA_DR_MP3_VADD(x6, x7), 0.70710677f); - x7 = MA_DR_MP3_VADD(x7, xt); - x3 = MA_DR_MP3_VMUL_S(MA_DR_MP3_VADD(x3, x4), 0.70710677f); - x5 = MA_DR_MP3_VSUB(x5, MA_DR_MP3_VMUL_S(x7, 0.198912367f)); - x7 = MA_DR_MP3_VADD(x7, MA_DR_MP3_VMUL_S(x5, 0.382683432f)); - x5 = MA_DR_MP3_VSUB(x5, MA_DR_MP3_VMUL_S(x7, 0.198912367f)); - x0 = MA_DR_MP3_VSUB(xt, x6); xt = MA_DR_MP3_VADD(xt, x6); - x[1] = MA_DR_MP3_VMUL_S(MA_DR_MP3_VADD(xt, x7), 0.50979561f); - x[2] = MA_DR_MP3_VMUL_S(MA_DR_MP3_VADD(x4, x3), 0.54119611f); - x[3] = MA_DR_MP3_VMUL_S(MA_DR_MP3_VSUB(x0, x5), 0.60134488f); - x[5] = MA_DR_MP3_VMUL_S(MA_DR_MP3_VADD(x0, x5), 0.89997619f); - x[6] = MA_DR_MP3_VMUL_S(MA_DR_MP3_VSUB(x4, x3), 1.30656302f); - x[7] = MA_DR_MP3_VMUL_S(MA_DR_MP3_VSUB(xt, x7), 2.56291556f); - } - if (k > n - 3) - { -#if MA_DR_MP3_HAVE_SSE -#define MA_DR_MP3_VSAVE2(i, v) _mm_storel_pi((__m64 *)(void*)&y[i*18], v) -#else -#define MA_DR_MP3_VSAVE2(i, v) vst1_f32((float32_t *)&y[(i)*18], vget_low_f32(v)) -#endif - for (i = 0; i < 7; i++, y += 4*18) - { - ma_dr_mp3_f4 s = MA_DR_MP3_VADD(t[3][i], t[3][i + 1]); - MA_DR_MP3_VSAVE2(0, t[0][i]); - MA_DR_MP3_VSAVE2(1, MA_DR_MP3_VADD(t[2][i], s)); - MA_DR_MP3_VSAVE2(2, MA_DR_MP3_VADD(t[1][i], t[1][i + 1])); - MA_DR_MP3_VSAVE2(3, MA_DR_MP3_VADD(t[2][1 + i], s)); - } - MA_DR_MP3_VSAVE2(0, t[0][7]); - MA_DR_MP3_VSAVE2(1, MA_DR_MP3_VADD(t[2][7], t[3][7])); - MA_DR_MP3_VSAVE2(2, t[1][7]); - MA_DR_MP3_VSAVE2(3, t[3][7]); - } else - { -#define MA_DR_MP3_VSAVE4(i, v) MA_DR_MP3_VSTORE(&y[(i)*18], v) - for (i = 0; i < 7; i++, y += 4*18) - { - ma_dr_mp3_f4 s = MA_DR_MP3_VADD(t[3][i], t[3][i + 1]); - MA_DR_MP3_VSAVE4(0, t[0][i]); - MA_DR_MP3_VSAVE4(1, MA_DR_MP3_VADD(t[2][i], s)); - MA_DR_MP3_VSAVE4(2, MA_DR_MP3_VADD(t[1][i], t[1][i + 1])); - MA_DR_MP3_VSAVE4(3, MA_DR_MP3_VADD(t[2][1 + i], s)); - } - MA_DR_MP3_VSAVE4(0, t[0][7]); - MA_DR_MP3_VSAVE4(1, MA_DR_MP3_VADD(t[2][7], t[3][7])); - MA_DR_MP3_VSAVE4(2, t[1][7]); - MA_DR_MP3_VSAVE4(3, t[3][7]); - } - } else -#endif -#ifdef MA_DR_MP3_ONLY_SIMD - {} -#else - for (; k < n; k++) - { - float t[4][8], *x, *y = grbuf + k; - for (x = t[0], i = 0; i < 8; i++, x++) - { - float x0 = y[i*18]; - float x1 = y[(15 - i)*18]; - float x2 = y[(16 + i)*18]; - float x3 = y[(31 - i)*18]; - float t0 = x0 + x3; - float t1 = x1 + x2; - float t2 = (x1 - x2)*g_sec[3*i + 0]; - float t3 = (x0 - x3)*g_sec[3*i + 1]; - x[0] = t0 + t1; - x[8] = (t0 - t1)*g_sec[3*i + 2]; - x[16] = t3 + t2; - x[24] = (t3 - t2)*g_sec[3*i + 2]; - } - for (x = t[0], i = 0; i < 4; i++, x += 8) - { - float x0 = x[0], x1 = x[1], x2 = x[2], x3 = x[3], x4 = x[4], x5 = x[5], x6 = x[6], x7 = x[7], xt; - xt = x0 - x7; x0 += x7; - x7 = x1 - x6; x1 += x6; - x6 = x2 - x5; x2 += x5; - x5 = x3 - x4; x3 += x4; - x4 = x0 - x3; x0 += x3; - x3 = x1 - x2; x1 += x2; - x[0] = x0 + x1; - x[4] = (x0 - x1)*0.70710677f; - x5 = x5 + x6; - x6 = (x6 + x7)*0.70710677f; - x7 = x7 + xt; - x3 = (x3 + x4)*0.70710677f; - x5 -= x7*0.198912367f; - x7 += x5*0.382683432f; - x5 -= x7*0.198912367f; - x0 = xt - x6; xt += x6; - x[1] = (xt + x7)*0.50979561f; - x[2] = (x4 + x3)*0.54119611f; - x[3] = (x0 - x5)*0.60134488f; - x[5] = (x0 + x5)*0.89997619f; - x[6] = (x4 - x3)*1.30656302f; - x[7] = (xt - x7)*2.56291556f; - } - for (i = 0; i < 7; i++, y += 4*18) - { - y[0*18] = t[0][i]; - y[1*18] = t[2][i] + t[3][i] + t[3][i + 1]; - y[2*18] = t[1][i] + t[1][i + 1]; - y[3*18] = t[2][i + 1] + t[3][i] + t[3][i + 1]; - } - y[0*18] = t[0][7]; - y[1*18] = t[2][7] + t[3][7]; - y[2*18] = t[1][7]; - y[3*18] = t[3][7]; - } -#endif -} -#ifndef MA_DR_MP3_FLOAT_OUTPUT -typedef ma_int16 ma_dr_mp3d_sample_t; -static ma_int16 ma_dr_mp3d_scale_pcm(float sample) -{ - ma_int16 s; -#if MA_DR_MP3_HAVE_ARMV6 - ma_int32 s32 = (ma_int32)(sample + .5f); - s32 -= (s32 < 0); - s = (ma_int16)ma_dr_mp3_clip_int16_arm(s32); -#else - if (sample >= 32766.5f) return (ma_int16) 32767; - if (sample <= -32767.5f) return (ma_int16)-32768; - s = (ma_int16)(sample + .5f); - s -= (s < 0); -#endif - return s; -} -#else -typedef float ma_dr_mp3d_sample_t; -static float ma_dr_mp3d_scale_pcm(float sample) -{ - return sample*(1.f/32768.f); -} -#endif -static void ma_dr_mp3d_synth_pair(ma_dr_mp3d_sample_t *pcm, int nch, const float *z) -{ - float a; - a = (z[14*64] - z[ 0]) * 29; - a += (z[ 1*64] + z[13*64]) * 213; - a += (z[12*64] - z[ 2*64]) * 459; - a += (z[ 3*64] + z[11*64]) * 2037; - a += (z[10*64] - z[ 4*64]) * 5153; - a += (z[ 5*64] + z[ 9*64]) * 6574; - a += (z[ 8*64] - z[ 6*64]) * 37489; - a += z[ 7*64] * 75038; - pcm[0] = ma_dr_mp3d_scale_pcm(a); - z += 2; - a = z[14*64] * 104; - a += z[12*64] * 1567; - a += z[10*64] * 9727; - a += z[ 8*64] * 64019; - a += z[ 6*64] * -9975; - a += z[ 4*64] * -45; - a += z[ 2*64] * 146; - a += z[ 0*64] * -5; - pcm[16*nch] = ma_dr_mp3d_scale_pcm(a); -} -static void ma_dr_mp3d_synth(float *xl, ma_dr_mp3d_sample_t *dstl, int nch, float *lins) -{ - int i; - float *xr = xl + 576*(nch - 1); - ma_dr_mp3d_sample_t *dstr = dstl + (nch - 1); - static const float g_win[] = { - -1,26,-31,208,218,401,-519,2063,2000,4788,-5517,7134,5959,35640,-39336,74992, - -1,24,-35,202,222,347,-581,2080,1952,4425,-5879,7640,5288,33791,-41176,74856, - -1,21,-38,196,225,294,-645,2087,1893,4063,-6237,8092,4561,31947,-43006,74630, - -1,19,-41,190,227,244,-711,2085,1822,3705,-6589,8492,3776,30112,-44821,74313, - -1,17,-45,183,228,197,-779,2075,1739,3351,-6935,8840,2935,28289,-46617,73908, - -1,16,-49,176,228,153,-848,2057,1644,3004,-7271,9139,2037,26482,-48390,73415, - -2,14,-53,169,227,111,-919,2032,1535,2663,-7597,9389,1082,24694,-50137,72835, - -2,13,-58,161,224,72,-991,2001,1414,2330,-7910,9592,70,22929,-51853,72169, - -2,11,-63,154,221,36,-1064,1962,1280,2006,-8209,9750,-998,21189,-53534,71420, - -2,10,-68,147,215,2,-1137,1919,1131,1692,-8491,9863,-2122,19478,-55178,70590, - -3,9,-73,139,208,-29,-1210,1870,970,1388,-8755,9935,-3300,17799,-56778,69679, - -3,8,-79,132,200,-57,-1283,1817,794,1095,-8998,9966,-4533,16155,-58333,68692, - -4,7,-85,125,189,-83,-1356,1759,605,814,-9219,9959,-5818,14548,-59838,67629, - -4,7,-91,117,177,-106,-1428,1698,402,545,-9416,9916,-7154,12980,-61289,66494, - -5,6,-97,111,163,-127,-1498,1634,185,288,-9585,9838,-8540,11455,-62684,65290 - }; - float *zlin = lins + 15*64; - const float *w = g_win; - zlin[4*15] = xl[18*16]; - zlin[4*15 + 1] = xr[18*16]; - zlin[4*15 + 2] = xl[0]; - zlin[4*15 + 3] = xr[0]; - zlin[4*31] = xl[1 + 18*16]; - zlin[4*31 + 1] = xr[1 + 18*16]; - zlin[4*31 + 2] = xl[1]; - zlin[4*31 + 3] = xr[1]; - ma_dr_mp3d_synth_pair(dstr, nch, lins + 4*15 + 1); - ma_dr_mp3d_synth_pair(dstr + 32*nch, nch, lins + 4*15 + 64 + 1); - ma_dr_mp3d_synth_pair(dstl, nch, lins + 4*15); - ma_dr_mp3d_synth_pair(dstl + 32*nch, nch, lins + 4*15 + 64); -#if MA_DR_MP3_HAVE_SIMD - if (ma_dr_mp3_have_simd()) for (i = 14; i >= 0; i--) - { -#define MA_DR_MP3_VLOAD(k) ma_dr_mp3_f4 w0 = MA_DR_MP3_VSET(*w++); ma_dr_mp3_f4 w1 = MA_DR_MP3_VSET(*w++); ma_dr_mp3_f4 vz = MA_DR_MP3_VLD(&zlin[4*i - 64*k]); ma_dr_mp3_f4 vy = MA_DR_MP3_VLD(&zlin[4*i - 64*(15 - k)]); -#define MA_DR_MP3_V0(k) { MA_DR_MP3_VLOAD(k) b = MA_DR_MP3_VADD(MA_DR_MP3_VMUL(vz, w1), MA_DR_MP3_VMUL(vy, w0)) ; a = MA_DR_MP3_VSUB(MA_DR_MP3_VMUL(vz, w0), MA_DR_MP3_VMUL(vy, w1)); } -#define MA_DR_MP3_V1(k) { MA_DR_MP3_VLOAD(k) b = MA_DR_MP3_VADD(b, MA_DR_MP3_VADD(MA_DR_MP3_VMUL(vz, w1), MA_DR_MP3_VMUL(vy, w0))); a = MA_DR_MP3_VADD(a, MA_DR_MP3_VSUB(MA_DR_MP3_VMUL(vz, w0), MA_DR_MP3_VMUL(vy, w1))); } -#define MA_DR_MP3_V2(k) { MA_DR_MP3_VLOAD(k) b = MA_DR_MP3_VADD(b, MA_DR_MP3_VADD(MA_DR_MP3_VMUL(vz, w1), MA_DR_MP3_VMUL(vy, w0))); a = MA_DR_MP3_VADD(a, MA_DR_MP3_VSUB(MA_DR_MP3_VMUL(vy, w1), MA_DR_MP3_VMUL(vz, w0))); } - ma_dr_mp3_f4 a, b; - zlin[4*i] = xl[18*(31 - i)]; - zlin[4*i + 1] = xr[18*(31 - i)]; - zlin[4*i + 2] = xl[1 + 18*(31 - i)]; - zlin[4*i + 3] = xr[1 + 18*(31 - i)]; - zlin[4*i + 64] = xl[1 + 18*(1 + i)]; - zlin[4*i + 64 + 1] = xr[1 + 18*(1 + i)]; - zlin[4*i - 64 + 2] = xl[18*(1 + i)]; - zlin[4*i - 64 + 3] = xr[18*(1 + i)]; - MA_DR_MP3_V0(0) MA_DR_MP3_V2(1) MA_DR_MP3_V1(2) MA_DR_MP3_V2(3) MA_DR_MP3_V1(4) MA_DR_MP3_V2(5) MA_DR_MP3_V1(6) MA_DR_MP3_V2(7) - { -#ifndef MA_DR_MP3_FLOAT_OUTPUT -#if MA_DR_MP3_HAVE_SSE - static const ma_dr_mp3_f4 g_max = { 32767.0f, 32767.0f, 32767.0f, 32767.0f }; - static const ma_dr_mp3_f4 g_min = { -32768.0f, -32768.0f, -32768.0f, -32768.0f }; - __m128i pcm8 = _mm_packs_epi32(_mm_cvtps_epi32(_mm_max_ps(_mm_min_ps(a, g_max), g_min)), - _mm_cvtps_epi32(_mm_max_ps(_mm_min_ps(b, g_max), g_min))); - dstr[(15 - i)*nch] = (ma_int16)_mm_extract_epi16(pcm8, 1); - dstr[(17 + i)*nch] = (ma_int16)_mm_extract_epi16(pcm8, 5); - dstl[(15 - i)*nch] = (ma_int16)_mm_extract_epi16(pcm8, 0); - dstl[(17 + i)*nch] = (ma_int16)_mm_extract_epi16(pcm8, 4); - dstr[(47 - i)*nch] = (ma_int16)_mm_extract_epi16(pcm8, 3); - dstr[(49 + i)*nch] = (ma_int16)_mm_extract_epi16(pcm8, 7); - dstl[(47 - i)*nch] = (ma_int16)_mm_extract_epi16(pcm8, 2); - dstl[(49 + i)*nch] = (ma_int16)_mm_extract_epi16(pcm8, 6); -#else - int16x4_t pcma, pcmb; - a = MA_DR_MP3_VADD(a, MA_DR_MP3_VSET(0.5f)); - b = MA_DR_MP3_VADD(b, MA_DR_MP3_VSET(0.5f)); - pcma = vqmovn_s32(vqaddq_s32(vcvtq_s32_f32(a), vreinterpretq_s32_u32(vcltq_f32(a, MA_DR_MP3_VSET(0))))); - pcmb = vqmovn_s32(vqaddq_s32(vcvtq_s32_f32(b), vreinterpretq_s32_u32(vcltq_f32(b, MA_DR_MP3_VSET(0))))); - vst1_lane_s16(dstr + (15 - i)*nch, pcma, 1); - vst1_lane_s16(dstr + (17 + i)*nch, pcmb, 1); - vst1_lane_s16(dstl + (15 - i)*nch, pcma, 0); - vst1_lane_s16(dstl + (17 + i)*nch, pcmb, 0); - vst1_lane_s16(dstr + (47 - i)*nch, pcma, 3); - vst1_lane_s16(dstr + (49 + i)*nch, pcmb, 3); - vst1_lane_s16(dstl + (47 - i)*nch, pcma, 2); - vst1_lane_s16(dstl + (49 + i)*nch, pcmb, 2); -#endif -#else - #if MA_DR_MP3_HAVE_SSE - static const ma_dr_mp3_f4 g_scale = { 1.0f/32768.0f, 1.0f/32768.0f, 1.0f/32768.0f, 1.0f/32768.0f }; - #else - const ma_dr_mp3_f4 g_scale = vdupq_n_f32(1.0f/32768.0f); - #endif - a = MA_DR_MP3_VMUL(a, g_scale); - b = MA_DR_MP3_VMUL(b, g_scale); -#if MA_DR_MP3_HAVE_SSE - _mm_store_ss(dstr + (15 - i)*nch, _mm_shuffle_ps(a, a, _MM_SHUFFLE(1, 1, 1, 1))); - _mm_store_ss(dstr + (17 + i)*nch, _mm_shuffle_ps(b, b, _MM_SHUFFLE(1, 1, 1, 1))); - _mm_store_ss(dstl + (15 - i)*nch, _mm_shuffle_ps(a, a, _MM_SHUFFLE(0, 0, 0, 0))); - _mm_store_ss(dstl + (17 + i)*nch, _mm_shuffle_ps(b, b, _MM_SHUFFLE(0, 0, 0, 0))); - _mm_store_ss(dstr + (47 - i)*nch, _mm_shuffle_ps(a, a, _MM_SHUFFLE(3, 3, 3, 3))); - _mm_store_ss(dstr + (49 + i)*nch, _mm_shuffle_ps(b, b, _MM_SHUFFLE(3, 3, 3, 3))); - _mm_store_ss(dstl + (47 - i)*nch, _mm_shuffle_ps(a, a, _MM_SHUFFLE(2, 2, 2, 2))); - _mm_store_ss(dstl + (49 + i)*nch, _mm_shuffle_ps(b, b, _MM_SHUFFLE(2, 2, 2, 2))); -#else - vst1q_lane_f32(dstr + (15 - i)*nch, a, 1); - vst1q_lane_f32(dstr + (17 + i)*nch, b, 1); - vst1q_lane_f32(dstl + (15 - i)*nch, a, 0); - vst1q_lane_f32(dstl + (17 + i)*nch, b, 0); - vst1q_lane_f32(dstr + (47 - i)*nch, a, 3); - vst1q_lane_f32(dstr + (49 + i)*nch, b, 3); - vst1q_lane_f32(dstl + (47 - i)*nch, a, 2); - vst1q_lane_f32(dstl + (49 + i)*nch, b, 2); -#endif -#endif - } - } else -#endif -#ifdef MA_DR_MP3_ONLY_SIMD - {} -#else - for (i = 14; i >= 0; i--) - { -#define MA_DR_MP3_LOAD(k) float w0 = *w++; float w1 = *w++; float *vz = &zlin[4*i - k*64]; float *vy = &zlin[4*i - (15 - k)*64]; -#define MA_DR_MP3_S0(k) { int j; MA_DR_MP3_LOAD(k); for (j = 0; j < 4; j++) b[j] = vz[j]*w1 + vy[j]*w0, a[j] = vz[j]*w0 - vy[j]*w1; } -#define MA_DR_MP3_S1(k) { int j; MA_DR_MP3_LOAD(k); for (j = 0; j < 4; j++) b[j] += vz[j]*w1 + vy[j]*w0, a[j] += vz[j]*w0 - vy[j]*w1; } -#define MA_DR_MP3_S2(k) { int j; MA_DR_MP3_LOAD(k); for (j = 0; j < 4; j++) b[j] += vz[j]*w1 + vy[j]*w0, a[j] += vy[j]*w1 - vz[j]*w0; } - float a[4], b[4]; - zlin[4*i] = xl[18*(31 - i)]; - zlin[4*i + 1] = xr[18*(31 - i)]; - zlin[4*i + 2] = xl[1 + 18*(31 - i)]; - zlin[4*i + 3] = xr[1 + 18*(31 - i)]; - zlin[4*(i + 16)] = xl[1 + 18*(1 + i)]; - zlin[4*(i + 16) + 1] = xr[1 + 18*(1 + i)]; - zlin[4*(i - 16) + 2] = xl[18*(1 + i)]; - zlin[4*(i - 16) + 3] = xr[18*(1 + i)]; - MA_DR_MP3_S0(0) MA_DR_MP3_S2(1) MA_DR_MP3_S1(2) MA_DR_MP3_S2(3) MA_DR_MP3_S1(4) MA_DR_MP3_S2(5) MA_DR_MP3_S1(6) MA_DR_MP3_S2(7) - dstr[(15 - i)*nch] = ma_dr_mp3d_scale_pcm(a[1]); - dstr[(17 + i)*nch] = ma_dr_mp3d_scale_pcm(b[1]); - dstl[(15 - i)*nch] = ma_dr_mp3d_scale_pcm(a[0]); - dstl[(17 + i)*nch] = ma_dr_mp3d_scale_pcm(b[0]); - dstr[(47 - i)*nch] = ma_dr_mp3d_scale_pcm(a[3]); - dstr[(49 + i)*nch] = ma_dr_mp3d_scale_pcm(b[3]); - dstl[(47 - i)*nch] = ma_dr_mp3d_scale_pcm(a[2]); - dstl[(49 + i)*nch] = ma_dr_mp3d_scale_pcm(b[2]); - } -#endif -} -static void ma_dr_mp3d_synth_granule(float *qmf_state, float *grbuf, int nbands, int nch, ma_dr_mp3d_sample_t *pcm, float *lins) -{ - int i; - for (i = 0; i < nch; i++) - { - ma_dr_mp3d_DCT_II(grbuf + 576*i, nbands); - } - MA_DR_MP3_COPY_MEMORY(lins, qmf_state, sizeof(float)*15*64); - for (i = 0; i < nbands; i += 2) - { - ma_dr_mp3d_synth(grbuf + i, pcm + 32*nch*i, nch, lins + i*64); - } -#ifndef MA_DR_MP3_NONSTANDARD_BUT_LOGICAL - if (nch == 1) - { - for (i = 0; i < 15*64; i += 2) - { - qmf_state[i] = lins[nbands*64 + i]; - } - } else -#endif - { - MA_DR_MP3_COPY_MEMORY(qmf_state, lins + nbands*64, sizeof(float)*15*64); - } -} -static int ma_dr_mp3d_match_frame(const ma_uint8 *hdr, int mp3_bytes, int frame_bytes) -{ - int i, nmatch; - for (i = 0, nmatch = 0; nmatch < MA_DR_MP3_MAX_FRAME_SYNC_MATCHES; nmatch++) - { - i += ma_dr_mp3_hdr_frame_bytes(hdr + i, frame_bytes) + ma_dr_mp3_hdr_padding(hdr + i); - if (i + MA_DR_MP3_HDR_SIZE > mp3_bytes) - return nmatch > 0; - if (!ma_dr_mp3_hdr_compare(hdr, hdr + i)) - return 0; - } - return 1; -} -static int ma_dr_mp3d_find_frame(const ma_uint8 *mp3, int mp3_bytes, int *free_format_bytes, int *ptr_frame_bytes) -{ - int i, k; - for (i = 0; i < mp3_bytes - MA_DR_MP3_HDR_SIZE; i++, mp3++) - { - if (ma_dr_mp3_hdr_valid(mp3)) - { - int frame_bytes = ma_dr_mp3_hdr_frame_bytes(mp3, *free_format_bytes); - int frame_and_padding = frame_bytes + ma_dr_mp3_hdr_padding(mp3); - for (k = MA_DR_MP3_HDR_SIZE; !frame_bytes && k < MA_DR_MP3_MAX_FREE_FORMAT_FRAME_SIZE && i + 2*k < mp3_bytes - MA_DR_MP3_HDR_SIZE; k++) - { - if (ma_dr_mp3_hdr_compare(mp3, mp3 + k)) - { - int fb = k - ma_dr_mp3_hdr_padding(mp3); - int nextfb = fb + ma_dr_mp3_hdr_padding(mp3 + k); - if (i + k + nextfb + MA_DR_MP3_HDR_SIZE > mp3_bytes || !ma_dr_mp3_hdr_compare(mp3, mp3 + k + nextfb)) - continue; - frame_and_padding = k; - frame_bytes = fb; - *free_format_bytes = fb; - } - } - if ((frame_bytes && i + frame_and_padding <= mp3_bytes && - ma_dr_mp3d_match_frame(mp3, mp3_bytes - i, frame_bytes)) || - (!i && frame_and_padding == mp3_bytes)) - { - *ptr_frame_bytes = frame_and_padding; - return i; - } - *free_format_bytes = 0; - } - } - *ptr_frame_bytes = 0; - return mp3_bytes; -} -MA_API void ma_dr_mp3dec_init(ma_dr_mp3dec *dec) -{ - dec->header[0] = 0; -} -MA_API int ma_dr_mp3dec_decode_frame(ma_dr_mp3dec *dec, const ma_uint8 *mp3, int mp3_bytes, void *pcm, ma_dr_mp3dec_frame_info *info) -{ - int i = 0, igr, frame_size = 0, success = 1; - const ma_uint8 *hdr; - ma_dr_mp3_bs bs_frame[1]; - ma_dr_mp3dec_scratch scratch; - if (mp3_bytes > 4 && dec->header[0] == 0xff && ma_dr_mp3_hdr_compare(dec->header, mp3)) - { - frame_size = ma_dr_mp3_hdr_frame_bytes(mp3, dec->free_format_bytes) + ma_dr_mp3_hdr_padding(mp3); - if (frame_size != mp3_bytes && (frame_size + MA_DR_MP3_HDR_SIZE > mp3_bytes || !ma_dr_mp3_hdr_compare(mp3, mp3 + frame_size))) - { - frame_size = 0; - } - } - if (!frame_size) - { - MA_DR_MP3_ZERO_MEMORY(dec, sizeof(ma_dr_mp3dec)); - i = ma_dr_mp3d_find_frame(mp3, mp3_bytes, &dec->free_format_bytes, &frame_size); - if (!frame_size || i + frame_size > mp3_bytes) - { - info->frame_bytes = i; - return 0; - } - } - hdr = mp3 + i; - MA_DR_MP3_COPY_MEMORY(dec->header, hdr, MA_DR_MP3_HDR_SIZE); - info->frame_bytes = i + frame_size; - info->channels = MA_DR_MP3_HDR_IS_MONO(hdr) ? 1 : 2; - info->hz = ma_dr_mp3_hdr_sample_rate_hz(hdr); - info->layer = 4 - MA_DR_MP3_HDR_GET_LAYER(hdr); - info->bitrate_kbps = ma_dr_mp3_hdr_bitrate_kbps(hdr); - ma_dr_mp3_bs_init(bs_frame, hdr + MA_DR_MP3_HDR_SIZE, frame_size - MA_DR_MP3_HDR_SIZE); - if (MA_DR_MP3_HDR_IS_CRC(hdr)) - { - ma_dr_mp3_bs_get_bits(bs_frame, 16); - } - if (info->layer == 3) - { - int main_data_begin = ma_dr_mp3_L3_read_side_info(bs_frame, scratch.gr_info, hdr); - if (main_data_begin < 0 || bs_frame->pos > bs_frame->limit) - { - ma_dr_mp3dec_init(dec); - return 0; - } - success = ma_dr_mp3_L3_restore_reservoir(dec, bs_frame, &scratch, main_data_begin); - if (success && pcm != NULL) - { - for (igr = 0; igr < (MA_DR_MP3_HDR_TEST_MPEG1(hdr) ? 2 : 1); igr++, pcm = MA_DR_MP3_OFFSET_PTR(pcm, sizeof(ma_dr_mp3d_sample_t)*576*info->channels)) - { - MA_DR_MP3_ZERO_MEMORY(scratch.grbuf[0], 576*2*sizeof(float)); - ma_dr_mp3_L3_decode(dec, &scratch, scratch.gr_info + igr*info->channels, info->channels); - ma_dr_mp3d_synth_granule(dec->qmf_state, scratch.grbuf[0], 18, info->channels, (ma_dr_mp3d_sample_t*)pcm, scratch.syn[0]); - } - } - ma_dr_mp3_L3_save_reservoir(dec, &scratch); - } else - { -#ifdef MA_DR_MP3_ONLY_MP3 - return 0; -#else - ma_dr_mp3_L12_scale_info sci[1]; - if (pcm == NULL) { - return ma_dr_mp3_hdr_frame_samples(hdr); - } - ma_dr_mp3_L12_read_scale_info(hdr, bs_frame, sci); - MA_DR_MP3_ZERO_MEMORY(scratch.grbuf[0], 576*2*sizeof(float)); - for (i = 0, igr = 0; igr < 3; igr++) - { - if (12 == (i += ma_dr_mp3_L12_dequantize_granule(scratch.grbuf[0] + i, bs_frame, sci, info->layer | 1))) - { - i = 0; - ma_dr_mp3_L12_apply_scf_384(sci, sci->scf + igr, scratch.grbuf[0]); - ma_dr_mp3d_synth_granule(dec->qmf_state, scratch.grbuf[0], 12, info->channels, (ma_dr_mp3d_sample_t*)pcm, scratch.syn[0]); - MA_DR_MP3_ZERO_MEMORY(scratch.grbuf[0], 576*2*sizeof(float)); - pcm = MA_DR_MP3_OFFSET_PTR(pcm, sizeof(ma_dr_mp3d_sample_t)*384*info->channels); - } - if (bs_frame->pos > bs_frame->limit) - { - ma_dr_mp3dec_init(dec); - return 0; - } - } -#endif - } - return success*ma_dr_mp3_hdr_frame_samples(dec->header); -} -MA_API void ma_dr_mp3dec_f32_to_s16(const float *in, ma_int16 *out, size_t num_samples) -{ - size_t i = 0; -#if MA_DR_MP3_HAVE_SIMD - size_t aligned_count = num_samples & ~7; - for(; i < aligned_count; i+=8) - { - ma_dr_mp3_f4 scale = MA_DR_MP3_VSET(32768.0f); - ma_dr_mp3_f4 a = MA_DR_MP3_VMUL(MA_DR_MP3_VLD(&in[i ]), scale); - ma_dr_mp3_f4 b = MA_DR_MP3_VMUL(MA_DR_MP3_VLD(&in[i+4]), scale); -#if MA_DR_MP3_HAVE_SSE - ma_dr_mp3_f4 s16max = MA_DR_MP3_VSET( 32767.0f); - ma_dr_mp3_f4 s16min = MA_DR_MP3_VSET(-32768.0f); - __m128i pcm8 = _mm_packs_epi32(_mm_cvtps_epi32(_mm_max_ps(_mm_min_ps(a, s16max), s16min)), - _mm_cvtps_epi32(_mm_max_ps(_mm_min_ps(b, s16max), s16min))); - out[i ] = (ma_int16)_mm_extract_epi16(pcm8, 0); - out[i+1] = (ma_int16)_mm_extract_epi16(pcm8, 1); - out[i+2] = (ma_int16)_mm_extract_epi16(pcm8, 2); - out[i+3] = (ma_int16)_mm_extract_epi16(pcm8, 3); - out[i+4] = (ma_int16)_mm_extract_epi16(pcm8, 4); - out[i+5] = (ma_int16)_mm_extract_epi16(pcm8, 5); - out[i+6] = (ma_int16)_mm_extract_epi16(pcm8, 6); - out[i+7] = (ma_int16)_mm_extract_epi16(pcm8, 7); -#else - int16x4_t pcma, pcmb; - a = MA_DR_MP3_VADD(a, MA_DR_MP3_VSET(0.5f)); - b = MA_DR_MP3_VADD(b, MA_DR_MP3_VSET(0.5f)); - pcma = vqmovn_s32(vqaddq_s32(vcvtq_s32_f32(a), vreinterpretq_s32_u32(vcltq_f32(a, MA_DR_MP3_VSET(0))))); - pcmb = vqmovn_s32(vqaddq_s32(vcvtq_s32_f32(b), vreinterpretq_s32_u32(vcltq_f32(b, MA_DR_MP3_VSET(0))))); - vst1_lane_s16(out+i , pcma, 0); - vst1_lane_s16(out+i+1, pcma, 1); - vst1_lane_s16(out+i+2, pcma, 2); - vst1_lane_s16(out+i+3, pcma, 3); - vst1_lane_s16(out+i+4, pcmb, 0); - vst1_lane_s16(out+i+5, pcmb, 1); - vst1_lane_s16(out+i+6, pcmb, 2); - vst1_lane_s16(out+i+7, pcmb, 3); -#endif - } -#endif - for(; i < num_samples; i++) - { - float sample = in[i] * 32768.0f; - if (sample >= 32766.5f) - out[i] = (ma_int16) 32767; - else if (sample <= -32767.5f) - out[i] = (ma_int16)-32768; - else - { - short s = (ma_int16)(sample + .5f); - s -= (s < 0); - out[i] = s; - } - } -} -#ifndef MA_DR_MP3_SEEK_LEADING_MP3_FRAMES -#define MA_DR_MP3_SEEK_LEADING_MP3_FRAMES 2 -#endif -#define MA_DR_MP3_MIN_DATA_CHUNK_SIZE 16384 -#ifndef MA_DR_MP3_DATA_CHUNK_SIZE -#define MA_DR_MP3_DATA_CHUNK_SIZE (MA_DR_MP3_MIN_DATA_CHUNK_SIZE*4) -#endif -#define MA_DR_MP3_COUNTOF(x) (sizeof(x) / sizeof(x[0])) -#define MA_DR_MP3_CLAMP(x, lo, hi) (MA_DR_MP3_MAX(lo, MA_DR_MP3_MIN(x, hi))) -#ifndef MA_DR_MP3_PI_D -#define MA_DR_MP3_PI_D 3.14159265358979323846264 -#endif -#define MA_DR_MP3_DEFAULT_RESAMPLER_LPF_ORDER 2 -static MA_INLINE float ma_dr_mp3_mix_f32(float x, float y, float a) -{ - return x*(1-a) + y*a; -} -static MA_INLINE float ma_dr_mp3_mix_f32_fast(float x, float y, float a) -{ - float r0 = (y - x); - float r1 = r0*a; - return x + r1; -} -static MA_INLINE ma_uint32 ma_dr_mp3_gcf_u32(ma_uint32 a, ma_uint32 b) -{ - for (;;) { - if (b == 0) { - break; - } else { - ma_uint32 t = a; - a = b; - b = t % a; - } - } - return a; -} -static void* ma_dr_mp3__malloc_default(size_t sz, void* pUserData) -{ - (void)pUserData; - return MA_DR_MP3_MALLOC(sz); -} -static void* ma_dr_mp3__realloc_default(void* p, size_t sz, void* pUserData) -{ - (void)pUserData; - return MA_DR_MP3_REALLOC(p, sz); -} -static void ma_dr_mp3__free_default(void* p, void* pUserData) -{ - (void)pUserData; - MA_DR_MP3_FREE(p); -} -static void* ma_dr_mp3__malloc_from_callbacks(size_t sz, const ma_allocation_callbacks* pAllocationCallbacks) -{ - if (pAllocationCallbacks == NULL) { - return NULL; - } - if (pAllocationCallbacks->onMalloc != NULL) { - return pAllocationCallbacks->onMalloc(sz, pAllocationCallbacks->pUserData); - } - if (pAllocationCallbacks->onRealloc != NULL) { - return pAllocationCallbacks->onRealloc(NULL, sz, pAllocationCallbacks->pUserData); - } - return NULL; -} -static void* ma_dr_mp3__realloc_from_callbacks(void* p, size_t szNew, size_t szOld, const ma_allocation_callbacks* pAllocationCallbacks) -{ - if (pAllocationCallbacks == NULL) { - return NULL; - } - if (pAllocationCallbacks->onRealloc != NULL) { - return pAllocationCallbacks->onRealloc(p, szNew, pAllocationCallbacks->pUserData); - } - if (pAllocationCallbacks->onMalloc != NULL && pAllocationCallbacks->onFree != NULL) { - void* p2; - p2 = pAllocationCallbacks->onMalloc(szNew, pAllocationCallbacks->pUserData); - if (p2 == NULL) { - return NULL; - } - if (p != NULL) { - MA_DR_MP3_COPY_MEMORY(p2, p, szOld); - pAllocationCallbacks->onFree(p, pAllocationCallbacks->pUserData); - } - return p2; - } - return NULL; -} -static void ma_dr_mp3__free_from_callbacks(void* p, const ma_allocation_callbacks* pAllocationCallbacks) -{ - if (p == NULL || pAllocationCallbacks == NULL) { - return; - } - if (pAllocationCallbacks->onFree != NULL) { - pAllocationCallbacks->onFree(p, pAllocationCallbacks->pUserData); - } -} -static ma_allocation_callbacks ma_dr_mp3_copy_allocation_callbacks_or_defaults(const ma_allocation_callbacks* pAllocationCallbacks) -{ - if (pAllocationCallbacks != NULL) { - return *pAllocationCallbacks; - } else { - ma_allocation_callbacks allocationCallbacks; - allocationCallbacks.pUserData = NULL; - allocationCallbacks.onMalloc = ma_dr_mp3__malloc_default; - allocationCallbacks.onRealloc = ma_dr_mp3__realloc_default; - allocationCallbacks.onFree = ma_dr_mp3__free_default; - return allocationCallbacks; - } -} -static size_t ma_dr_mp3__on_read(ma_dr_mp3* pMP3, void* pBufferOut, size_t bytesToRead) -{ - size_t bytesRead = pMP3->onRead(pMP3->pUserData, pBufferOut, bytesToRead); - pMP3->streamCursor += bytesRead; - return bytesRead; -} -static ma_bool32 ma_dr_mp3__on_seek(ma_dr_mp3* pMP3, int offset, ma_dr_mp3_seek_origin origin) -{ - MA_DR_MP3_ASSERT(offset >= 0); - if (!pMP3->onSeek(pMP3->pUserData, offset, origin)) { - return MA_FALSE; - } - if (origin == ma_dr_mp3_seek_origin_start) { - pMP3->streamCursor = (ma_uint64)offset; - } else { - pMP3->streamCursor += offset; - } - return MA_TRUE; -} -static ma_bool32 ma_dr_mp3__on_seek_64(ma_dr_mp3* pMP3, ma_uint64 offset, ma_dr_mp3_seek_origin origin) -{ - if (offset <= 0x7FFFFFFF) { - return ma_dr_mp3__on_seek(pMP3, (int)offset, origin); - } - if (!ma_dr_mp3__on_seek(pMP3, 0x7FFFFFFF, ma_dr_mp3_seek_origin_start)) { - return MA_FALSE; - } - offset -= 0x7FFFFFFF; - while (offset > 0) { - if (offset <= 0x7FFFFFFF) { - if (!ma_dr_mp3__on_seek(pMP3, (int)offset, ma_dr_mp3_seek_origin_current)) { - return MA_FALSE; - } - offset = 0; - } else { - if (!ma_dr_mp3__on_seek(pMP3, 0x7FFFFFFF, ma_dr_mp3_seek_origin_current)) { - return MA_FALSE; - } - offset -= 0x7FFFFFFF; - } - } - return MA_TRUE; -} -static ma_uint32 ma_dr_mp3_decode_next_frame_ex__callbacks(ma_dr_mp3* pMP3, ma_dr_mp3d_sample_t* pPCMFrames) -{ - ma_uint32 pcmFramesRead = 0; - MA_DR_MP3_ASSERT(pMP3 != NULL); - MA_DR_MP3_ASSERT(pMP3->onRead != NULL); - if (pMP3->atEnd) { - return 0; - } - for (;;) { - ma_dr_mp3dec_frame_info info; - if (pMP3->dataSize < MA_DR_MP3_MIN_DATA_CHUNK_SIZE) { - size_t bytesRead; - if (pMP3->pData != NULL) { - MA_DR_MP3_MOVE_MEMORY(pMP3->pData, pMP3->pData + pMP3->dataConsumed, pMP3->dataSize); - } - pMP3->dataConsumed = 0; - if (pMP3->dataCapacity < MA_DR_MP3_DATA_CHUNK_SIZE) { - ma_uint8* pNewData; - size_t newDataCap; - newDataCap = MA_DR_MP3_DATA_CHUNK_SIZE; - pNewData = (ma_uint8*)ma_dr_mp3__realloc_from_callbacks(pMP3->pData, newDataCap, pMP3->dataCapacity, &pMP3->allocationCallbacks); - if (pNewData == NULL) { - return 0; - } - pMP3->pData = pNewData; - pMP3->dataCapacity = newDataCap; - } - bytesRead = ma_dr_mp3__on_read(pMP3, pMP3->pData + pMP3->dataSize, (pMP3->dataCapacity - pMP3->dataSize)); - if (bytesRead == 0) { - if (pMP3->dataSize == 0) { - pMP3->atEnd = MA_TRUE; - return 0; - } - } - pMP3->dataSize += bytesRead; - } - if (pMP3->dataSize > INT_MAX) { - pMP3->atEnd = MA_TRUE; - return 0; - } - MA_DR_MP3_ASSERT(pMP3->pData != NULL); - MA_DR_MP3_ASSERT(pMP3->dataCapacity > 0); - if (pMP3->pData == NULL) { - return 0; - } - pcmFramesRead = ma_dr_mp3dec_decode_frame(&pMP3->decoder, pMP3->pData + pMP3->dataConsumed, (int)pMP3->dataSize, pPCMFrames, &info); - if (info.frame_bytes > 0) { - pMP3->dataConsumed += (size_t)info.frame_bytes; - pMP3->dataSize -= (size_t)info.frame_bytes; - } - if (pcmFramesRead > 0) { - pcmFramesRead = ma_dr_mp3_hdr_frame_samples(pMP3->decoder.header); - pMP3->pcmFramesConsumedInMP3Frame = 0; - pMP3->pcmFramesRemainingInMP3Frame = pcmFramesRead; - pMP3->mp3FrameChannels = info.channels; - pMP3->mp3FrameSampleRate = info.hz; - break; - } else if (info.frame_bytes == 0) { - size_t bytesRead; - MA_DR_MP3_MOVE_MEMORY(pMP3->pData, pMP3->pData + pMP3->dataConsumed, pMP3->dataSize); - pMP3->dataConsumed = 0; - if (pMP3->dataCapacity == pMP3->dataSize) { - ma_uint8* pNewData; - size_t newDataCap; - newDataCap = pMP3->dataCapacity + MA_DR_MP3_DATA_CHUNK_SIZE; - pNewData = (ma_uint8*)ma_dr_mp3__realloc_from_callbacks(pMP3->pData, newDataCap, pMP3->dataCapacity, &pMP3->allocationCallbacks); - if (pNewData == NULL) { - return 0; - } - pMP3->pData = pNewData; - pMP3->dataCapacity = newDataCap; - } - bytesRead = ma_dr_mp3__on_read(pMP3, pMP3->pData + pMP3->dataSize, (pMP3->dataCapacity - pMP3->dataSize)); - if (bytesRead == 0) { - pMP3->atEnd = MA_TRUE; - return 0; - } - pMP3->dataSize += bytesRead; - } - }; - return pcmFramesRead; -} -static ma_uint32 ma_dr_mp3_decode_next_frame_ex__memory(ma_dr_mp3* pMP3, ma_dr_mp3d_sample_t* pPCMFrames) -{ - ma_uint32 pcmFramesRead = 0; - ma_dr_mp3dec_frame_info info; - MA_DR_MP3_ASSERT(pMP3 != NULL); - MA_DR_MP3_ASSERT(pMP3->memory.pData != NULL); - if (pMP3->atEnd) { - return 0; - } - for (;;) { - pcmFramesRead = ma_dr_mp3dec_decode_frame(&pMP3->decoder, pMP3->memory.pData + pMP3->memory.currentReadPos, (int)(pMP3->memory.dataSize - pMP3->memory.currentReadPos), pPCMFrames, &info); - if (pcmFramesRead > 0) { - pcmFramesRead = ma_dr_mp3_hdr_frame_samples(pMP3->decoder.header); - pMP3->pcmFramesConsumedInMP3Frame = 0; - pMP3->pcmFramesRemainingInMP3Frame = pcmFramesRead; - pMP3->mp3FrameChannels = info.channels; - pMP3->mp3FrameSampleRate = info.hz; - break; - } else if (info.frame_bytes > 0) { - pMP3->memory.currentReadPos += (size_t)info.frame_bytes; - } else { - break; - } - } - pMP3->memory.currentReadPos += (size_t)info.frame_bytes; - return pcmFramesRead; -} -static ma_uint32 ma_dr_mp3_decode_next_frame_ex(ma_dr_mp3* pMP3, ma_dr_mp3d_sample_t* pPCMFrames) -{ - if (pMP3->memory.pData != NULL && pMP3->memory.dataSize > 0) { - return ma_dr_mp3_decode_next_frame_ex__memory(pMP3, pPCMFrames); - } else { - return ma_dr_mp3_decode_next_frame_ex__callbacks(pMP3, pPCMFrames); - } -} -static ma_uint32 ma_dr_mp3_decode_next_frame(ma_dr_mp3* pMP3) -{ - MA_DR_MP3_ASSERT(pMP3 != NULL); - return ma_dr_mp3_decode_next_frame_ex(pMP3, (ma_dr_mp3d_sample_t*)pMP3->pcmFrames); -} -#if 0 -static ma_uint32 ma_dr_mp3_seek_next_frame(ma_dr_mp3* pMP3) -{ - ma_uint32 pcmFrameCount; - MA_DR_MP3_ASSERT(pMP3 != NULL); - pcmFrameCount = ma_dr_mp3_decode_next_frame_ex(pMP3, NULL); - if (pcmFrameCount == 0) { - return 0; - } - pMP3->currentPCMFrame += pcmFrameCount; - pMP3->pcmFramesConsumedInMP3Frame = pcmFrameCount; - pMP3->pcmFramesRemainingInMP3Frame = 0; - return pcmFrameCount; -} -#endif -static ma_bool32 ma_dr_mp3_init_internal(ma_dr_mp3* pMP3, ma_dr_mp3_read_proc onRead, ma_dr_mp3_seek_proc onSeek, void* pUserData, const ma_allocation_callbacks* pAllocationCallbacks) -{ - MA_DR_MP3_ASSERT(pMP3 != NULL); - MA_DR_MP3_ASSERT(onRead != NULL); - ma_dr_mp3dec_init(&pMP3->decoder); - pMP3->onRead = onRead; - pMP3->onSeek = onSeek; - pMP3->pUserData = pUserData; - pMP3->allocationCallbacks = ma_dr_mp3_copy_allocation_callbacks_or_defaults(pAllocationCallbacks); - if (pMP3->allocationCallbacks.onFree == NULL || (pMP3->allocationCallbacks.onMalloc == NULL && pMP3->allocationCallbacks.onRealloc == NULL)) { - return MA_FALSE; - } - if (ma_dr_mp3_decode_next_frame(pMP3) == 0) { - ma_dr_mp3__free_from_callbacks(pMP3->pData, &pMP3->allocationCallbacks); - return MA_FALSE; - } - pMP3->channels = pMP3->mp3FrameChannels; - pMP3->sampleRate = pMP3->mp3FrameSampleRate; - return MA_TRUE; -} -MA_API ma_bool32 ma_dr_mp3_init(ma_dr_mp3* pMP3, ma_dr_mp3_read_proc onRead, ma_dr_mp3_seek_proc onSeek, void* pUserData, const ma_allocation_callbacks* pAllocationCallbacks) -{ - if (pMP3 == NULL || onRead == NULL) { - return MA_FALSE; - } - MA_DR_MP3_ZERO_OBJECT(pMP3); - return ma_dr_mp3_init_internal(pMP3, onRead, onSeek, pUserData, pAllocationCallbacks); -} -static size_t ma_dr_mp3__on_read_memory(void* pUserData, void* pBufferOut, size_t bytesToRead) -{ - ma_dr_mp3* pMP3 = (ma_dr_mp3*)pUserData; - size_t bytesRemaining; - MA_DR_MP3_ASSERT(pMP3 != NULL); - MA_DR_MP3_ASSERT(pMP3->memory.dataSize >= pMP3->memory.currentReadPos); - bytesRemaining = pMP3->memory.dataSize - pMP3->memory.currentReadPos; - if (bytesToRead > bytesRemaining) { - bytesToRead = bytesRemaining; - } - if (bytesToRead > 0) { - MA_DR_MP3_COPY_MEMORY(pBufferOut, pMP3->memory.pData + pMP3->memory.currentReadPos, bytesToRead); - pMP3->memory.currentReadPos += bytesToRead; - } - return bytesToRead; -} -static ma_bool32 ma_dr_mp3__on_seek_memory(void* pUserData, int byteOffset, ma_dr_mp3_seek_origin origin) -{ - ma_dr_mp3* pMP3 = (ma_dr_mp3*)pUserData; - MA_DR_MP3_ASSERT(pMP3 != NULL); - if (origin == ma_dr_mp3_seek_origin_current) { - if (byteOffset > 0) { - if (pMP3->memory.currentReadPos + byteOffset > pMP3->memory.dataSize) { - byteOffset = (int)(pMP3->memory.dataSize - pMP3->memory.currentReadPos); - } - } else { - if (pMP3->memory.currentReadPos < (size_t)-byteOffset) { - byteOffset = -(int)pMP3->memory.currentReadPos; - } - } - pMP3->memory.currentReadPos += byteOffset; - } else { - if ((ma_uint32)byteOffset <= pMP3->memory.dataSize) { - pMP3->memory.currentReadPos = byteOffset; - } else { - pMP3->memory.currentReadPos = pMP3->memory.dataSize; - } - } - return MA_TRUE; -} -MA_API ma_bool32 ma_dr_mp3_init_memory(ma_dr_mp3* pMP3, const void* pData, size_t dataSize, const ma_allocation_callbacks* pAllocationCallbacks) -{ - if (pMP3 == NULL) { - return MA_FALSE; - } - MA_DR_MP3_ZERO_OBJECT(pMP3); - if (pData == NULL || dataSize == 0) { - return MA_FALSE; - } - pMP3->memory.pData = (const ma_uint8*)pData; - pMP3->memory.dataSize = dataSize; - pMP3->memory.currentReadPos = 0; - return ma_dr_mp3_init_internal(pMP3, ma_dr_mp3__on_read_memory, ma_dr_mp3__on_seek_memory, pMP3, pAllocationCallbacks); -} -#ifndef MA_DR_MP3_NO_STDIO -#include -#include -static size_t ma_dr_mp3__on_read_stdio(void* pUserData, void* pBufferOut, size_t bytesToRead) -{ - return fread(pBufferOut, 1, bytesToRead, (FILE*)pUserData); -} -static ma_bool32 ma_dr_mp3__on_seek_stdio(void* pUserData, int offset, ma_dr_mp3_seek_origin origin) -{ - return fseek((FILE*)pUserData, offset, (origin == ma_dr_mp3_seek_origin_current) ? SEEK_CUR : SEEK_SET) == 0; -} -MA_API ma_bool32 ma_dr_mp3_init_file(ma_dr_mp3* pMP3, const char* pFilePath, const ma_allocation_callbacks* pAllocationCallbacks) -{ - ma_bool32 result; - FILE* pFile; - if (ma_fopen(&pFile, pFilePath, "rb") != MA_SUCCESS) { - return MA_FALSE; - } - result = ma_dr_mp3_init(pMP3, ma_dr_mp3__on_read_stdio, ma_dr_mp3__on_seek_stdio, (void*)pFile, pAllocationCallbacks); - if (result != MA_TRUE) { - fclose(pFile); - return result; - } - return MA_TRUE; -} -MA_API ma_bool32 ma_dr_mp3_init_file_w(ma_dr_mp3* pMP3, const wchar_t* pFilePath, const ma_allocation_callbacks* pAllocationCallbacks) -{ - ma_bool32 result; - FILE* pFile; - if (ma_wfopen(&pFile, pFilePath, L"rb", pAllocationCallbacks) != MA_SUCCESS) { - return MA_FALSE; - } - result = ma_dr_mp3_init(pMP3, ma_dr_mp3__on_read_stdio, ma_dr_mp3__on_seek_stdio, (void*)pFile, pAllocationCallbacks); - if (result != MA_TRUE) { - fclose(pFile); - return result; - } - return MA_TRUE; -} -#endif -MA_API void ma_dr_mp3_uninit(ma_dr_mp3* pMP3) -{ - if (pMP3 == NULL) { - return; - } -#ifndef MA_DR_MP3_NO_STDIO - if (pMP3->onRead == ma_dr_mp3__on_read_stdio) { - FILE* pFile = (FILE*)pMP3->pUserData; - if (pFile != NULL) { - fclose(pFile); - pMP3->pUserData = NULL; - } - } -#endif - ma_dr_mp3__free_from_callbacks(pMP3->pData, &pMP3->allocationCallbacks); -} -#if defined(MA_DR_MP3_FLOAT_OUTPUT) -static void ma_dr_mp3_f32_to_s16(ma_int16* dst, const float* src, ma_uint64 sampleCount) -{ - ma_uint64 i; - ma_uint64 i4; - ma_uint64 sampleCount4; - i = 0; - sampleCount4 = sampleCount >> 2; - for (i4 = 0; i4 < sampleCount4; i4 += 1) { - float x0 = src[i+0]; - float x1 = src[i+1]; - float x2 = src[i+2]; - float x3 = src[i+3]; - x0 = ((x0 < -1) ? -1 : ((x0 > 1) ? 1 : x0)); - x1 = ((x1 < -1) ? -1 : ((x1 > 1) ? 1 : x1)); - x2 = ((x2 < -1) ? -1 : ((x2 > 1) ? 1 : x2)); - x3 = ((x3 < -1) ? -1 : ((x3 > 1) ? 1 : x3)); - x0 = x0 * 32767.0f; - x1 = x1 * 32767.0f; - x2 = x2 * 32767.0f; - x3 = x3 * 32767.0f; - dst[i+0] = (ma_int16)x0; - dst[i+1] = (ma_int16)x1; - dst[i+2] = (ma_int16)x2; - dst[i+3] = (ma_int16)x3; - i += 4; - } - for (; i < sampleCount; i += 1) { - float x = src[i]; - x = ((x < -1) ? -1 : ((x > 1) ? 1 : x)); - x = x * 32767.0f; - dst[i] = (ma_int16)x; - } -} -#endif -#if !defined(MA_DR_MP3_FLOAT_OUTPUT) -static void ma_dr_mp3_s16_to_f32(float* dst, const ma_int16* src, ma_uint64 sampleCount) -{ - ma_uint64 i; - for (i = 0; i < sampleCount; i += 1) { - float x = (float)src[i]; - x = x * 0.000030517578125f; - dst[i] = x; - } -} -#endif -static ma_uint64 ma_dr_mp3_read_pcm_frames_raw(ma_dr_mp3* pMP3, ma_uint64 framesToRead, void* pBufferOut) -{ - ma_uint64 totalFramesRead = 0; - MA_DR_MP3_ASSERT(pMP3 != NULL); - MA_DR_MP3_ASSERT(pMP3->onRead != NULL); - while (framesToRead > 0) { - ma_uint32 framesToConsume = (ma_uint32)MA_DR_MP3_MIN(pMP3->pcmFramesRemainingInMP3Frame, framesToRead); - if (pBufferOut != NULL) { - #if defined(MA_DR_MP3_FLOAT_OUTPUT) - float* pFramesOutF32 = (float*)MA_DR_MP3_OFFSET_PTR(pBufferOut, sizeof(float) * totalFramesRead * pMP3->channels); - float* pFramesInF32 = (float*)MA_DR_MP3_OFFSET_PTR(&pMP3->pcmFrames[0], sizeof(float) * pMP3->pcmFramesConsumedInMP3Frame * pMP3->mp3FrameChannels); - MA_DR_MP3_COPY_MEMORY(pFramesOutF32, pFramesInF32, sizeof(float) * framesToConsume * pMP3->channels); - #else - ma_int16* pFramesOutS16 = (ma_int16*)MA_DR_MP3_OFFSET_PTR(pBufferOut, sizeof(ma_int16) * totalFramesRead * pMP3->channels); - ma_int16* pFramesInS16 = (ma_int16*)MA_DR_MP3_OFFSET_PTR(&pMP3->pcmFrames[0], sizeof(ma_int16) * pMP3->pcmFramesConsumedInMP3Frame * pMP3->mp3FrameChannels); - MA_DR_MP3_COPY_MEMORY(pFramesOutS16, pFramesInS16, sizeof(ma_int16) * framesToConsume * pMP3->channels); - #endif - } - pMP3->currentPCMFrame += framesToConsume; - pMP3->pcmFramesConsumedInMP3Frame += framesToConsume; - pMP3->pcmFramesRemainingInMP3Frame -= framesToConsume; - totalFramesRead += framesToConsume; - framesToRead -= framesToConsume; - if (framesToRead == 0) { - break; - } - MA_DR_MP3_ASSERT(pMP3->pcmFramesRemainingInMP3Frame == 0); - if (ma_dr_mp3_decode_next_frame(pMP3) == 0) { - break; - } - } - return totalFramesRead; -} -MA_API ma_uint64 ma_dr_mp3_read_pcm_frames_f32(ma_dr_mp3* pMP3, ma_uint64 framesToRead, float* pBufferOut) -{ - if (pMP3 == NULL || pMP3->onRead == NULL) { - return 0; - } -#if defined(MA_DR_MP3_FLOAT_OUTPUT) - return ma_dr_mp3_read_pcm_frames_raw(pMP3, framesToRead, pBufferOut); -#else - { - ma_int16 pTempS16[8192]; - ma_uint64 totalPCMFramesRead = 0; - while (totalPCMFramesRead < framesToRead) { - ma_uint64 framesJustRead; - ma_uint64 framesRemaining = framesToRead - totalPCMFramesRead; - ma_uint64 framesToReadNow = MA_DR_MP3_COUNTOF(pTempS16) / pMP3->channels; - if (framesToReadNow > framesRemaining) { - framesToReadNow = framesRemaining; - } - framesJustRead = ma_dr_mp3_read_pcm_frames_raw(pMP3, framesToReadNow, pTempS16); - if (framesJustRead == 0) { - break; - } - ma_dr_mp3_s16_to_f32((float*)MA_DR_MP3_OFFSET_PTR(pBufferOut, sizeof(float) * totalPCMFramesRead * pMP3->channels), pTempS16, framesJustRead * pMP3->channels); - totalPCMFramesRead += framesJustRead; - } - return totalPCMFramesRead; - } -#endif -} -MA_API ma_uint64 ma_dr_mp3_read_pcm_frames_s16(ma_dr_mp3* pMP3, ma_uint64 framesToRead, ma_int16* pBufferOut) -{ - if (pMP3 == NULL || pMP3->onRead == NULL) { - return 0; - } -#if !defined(MA_DR_MP3_FLOAT_OUTPUT) - return ma_dr_mp3_read_pcm_frames_raw(pMP3, framesToRead, pBufferOut); -#else - { - float pTempF32[4096]; - ma_uint64 totalPCMFramesRead = 0; - while (totalPCMFramesRead < framesToRead) { - ma_uint64 framesJustRead; - ma_uint64 framesRemaining = framesToRead - totalPCMFramesRead; - ma_uint64 framesToReadNow = MA_DR_MP3_COUNTOF(pTempF32) / pMP3->channels; - if (framesToReadNow > framesRemaining) { - framesToReadNow = framesRemaining; - } - framesJustRead = ma_dr_mp3_read_pcm_frames_raw(pMP3, framesToReadNow, pTempF32); - if (framesJustRead == 0) { - break; - } - ma_dr_mp3_f32_to_s16((ma_int16*)MA_DR_MP3_OFFSET_PTR(pBufferOut, sizeof(ma_int16) * totalPCMFramesRead * pMP3->channels), pTempF32, framesJustRead * pMP3->channels); - totalPCMFramesRead += framesJustRead; - } - return totalPCMFramesRead; - } -#endif -} -static void ma_dr_mp3_reset(ma_dr_mp3* pMP3) -{ - MA_DR_MP3_ASSERT(pMP3 != NULL); - pMP3->pcmFramesConsumedInMP3Frame = 0; - pMP3->pcmFramesRemainingInMP3Frame = 0; - pMP3->currentPCMFrame = 0; - pMP3->dataSize = 0; - pMP3->atEnd = MA_FALSE; - ma_dr_mp3dec_init(&pMP3->decoder); -} -static ma_bool32 ma_dr_mp3_seek_to_start_of_stream(ma_dr_mp3* pMP3) -{ - MA_DR_MP3_ASSERT(pMP3 != NULL); - MA_DR_MP3_ASSERT(pMP3->onSeek != NULL); - if (!ma_dr_mp3__on_seek(pMP3, 0, ma_dr_mp3_seek_origin_start)) { - return MA_FALSE; - } - ma_dr_mp3_reset(pMP3); - return MA_TRUE; -} -static ma_bool32 ma_dr_mp3_seek_forward_by_pcm_frames__brute_force(ma_dr_mp3* pMP3, ma_uint64 frameOffset) -{ - ma_uint64 framesRead; -#if defined(MA_DR_MP3_FLOAT_OUTPUT) - framesRead = ma_dr_mp3_read_pcm_frames_f32(pMP3, frameOffset, NULL); -#else - framesRead = ma_dr_mp3_read_pcm_frames_s16(pMP3, frameOffset, NULL); -#endif - if (framesRead != frameOffset) { - return MA_FALSE; - } - return MA_TRUE; -} -static ma_bool32 ma_dr_mp3_seek_to_pcm_frame__brute_force(ma_dr_mp3* pMP3, ma_uint64 frameIndex) -{ - MA_DR_MP3_ASSERT(pMP3 != NULL); - if (frameIndex == pMP3->currentPCMFrame) { - return MA_TRUE; - } - if (frameIndex < pMP3->currentPCMFrame) { - if (!ma_dr_mp3_seek_to_start_of_stream(pMP3)) { - return MA_FALSE; - } - } - MA_DR_MP3_ASSERT(frameIndex >= pMP3->currentPCMFrame); - return ma_dr_mp3_seek_forward_by_pcm_frames__brute_force(pMP3, (frameIndex - pMP3->currentPCMFrame)); -} -static ma_bool32 ma_dr_mp3_find_closest_seek_point(ma_dr_mp3* pMP3, ma_uint64 frameIndex, ma_uint32* pSeekPointIndex) -{ - ma_uint32 iSeekPoint; - MA_DR_MP3_ASSERT(pSeekPointIndex != NULL); - *pSeekPointIndex = 0; - if (frameIndex < pMP3->pSeekPoints[0].pcmFrameIndex) { - return MA_FALSE; - } - for (iSeekPoint = 0; iSeekPoint < pMP3->seekPointCount; ++iSeekPoint) { - if (pMP3->pSeekPoints[iSeekPoint].pcmFrameIndex > frameIndex) { - break; - } - *pSeekPointIndex = iSeekPoint; - } - return MA_TRUE; -} -static ma_bool32 ma_dr_mp3_seek_to_pcm_frame__seek_table(ma_dr_mp3* pMP3, ma_uint64 frameIndex) -{ - ma_dr_mp3_seek_point seekPoint; - ma_uint32 priorSeekPointIndex; - ma_uint16 iMP3Frame; - ma_uint64 leftoverFrames; - MA_DR_MP3_ASSERT(pMP3 != NULL); - MA_DR_MP3_ASSERT(pMP3->pSeekPoints != NULL); - MA_DR_MP3_ASSERT(pMP3->seekPointCount > 0); - if (ma_dr_mp3_find_closest_seek_point(pMP3, frameIndex, &priorSeekPointIndex)) { - seekPoint = pMP3->pSeekPoints[priorSeekPointIndex]; - } else { - seekPoint.seekPosInBytes = 0; - seekPoint.pcmFrameIndex = 0; - seekPoint.mp3FramesToDiscard = 0; - seekPoint.pcmFramesToDiscard = 0; - } - if (!ma_dr_mp3__on_seek_64(pMP3, seekPoint.seekPosInBytes, ma_dr_mp3_seek_origin_start)) { - return MA_FALSE; - } - ma_dr_mp3_reset(pMP3); - for (iMP3Frame = 0; iMP3Frame < seekPoint.mp3FramesToDiscard; ++iMP3Frame) { - ma_uint32 pcmFramesRead; - ma_dr_mp3d_sample_t* pPCMFrames; - pPCMFrames = NULL; - if (iMP3Frame == seekPoint.mp3FramesToDiscard-1) { - pPCMFrames = (ma_dr_mp3d_sample_t*)pMP3->pcmFrames; - } - pcmFramesRead = ma_dr_mp3_decode_next_frame_ex(pMP3, pPCMFrames); - if (pcmFramesRead == 0) { - return MA_FALSE; - } - } - pMP3->currentPCMFrame = seekPoint.pcmFrameIndex - seekPoint.pcmFramesToDiscard; - leftoverFrames = frameIndex - pMP3->currentPCMFrame; - return ma_dr_mp3_seek_forward_by_pcm_frames__brute_force(pMP3, leftoverFrames); -} -MA_API ma_bool32 ma_dr_mp3_seek_to_pcm_frame(ma_dr_mp3* pMP3, ma_uint64 frameIndex) -{ - if (pMP3 == NULL || pMP3->onSeek == NULL) { - return MA_FALSE; - } - if (frameIndex == 0) { - return ma_dr_mp3_seek_to_start_of_stream(pMP3); - } - if (pMP3->pSeekPoints != NULL && pMP3->seekPointCount > 0) { - return ma_dr_mp3_seek_to_pcm_frame__seek_table(pMP3, frameIndex); - } else { - return ma_dr_mp3_seek_to_pcm_frame__brute_force(pMP3, frameIndex); - } -} -MA_API ma_bool32 ma_dr_mp3_get_mp3_and_pcm_frame_count(ma_dr_mp3* pMP3, ma_uint64* pMP3FrameCount, ma_uint64* pPCMFrameCount) -{ - ma_uint64 currentPCMFrame; - ma_uint64 totalPCMFrameCount; - ma_uint64 totalMP3FrameCount; - if (pMP3 == NULL) { - return MA_FALSE; - } - if (pMP3->onSeek == NULL) { - return MA_FALSE; - } - currentPCMFrame = pMP3->currentPCMFrame; - if (!ma_dr_mp3_seek_to_start_of_stream(pMP3)) { - return MA_FALSE; - } - totalPCMFrameCount = 0; - totalMP3FrameCount = 0; - for (;;) { - ma_uint32 pcmFramesInCurrentMP3Frame; - pcmFramesInCurrentMP3Frame = ma_dr_mp3_decode_next_frame_ex(pMP3, NULL); - if (pcmFramesInCurrentMP3Frame == 0) { - break; - } - totalPCMFrameCount += pcmFramesInCurrentMP3Frame; - totalMP3FrameCount += 1; - } - if (!ma_dr_mp3_seek_to_start_of_stream(pMP3)) { - return MA_FALSE; - } - if (!ma_dr_mp3_seek_to_pcm_frame(pMP3, currentPCMFrame)) { - return MA_FALSE; - } - if (pMP3FrameCount != NULL) { - *pMP3FrameCount = totalMP3FrameCount; - } - if (pPCMFrameCount != NULL) { - *pPCMFrameCount = totalPCMFrameCount; - } - return MA_TRUE; -} -MA_API ma_uint64 ma_dr_mp3_get_pcm_frame_count(ma_dr_mp3* pMP3) -{ - ma_uint64 totalPCMFrameCount; - if (!ma_dr_mp3_get_mp3_and_pcm_frame_count(pMP3, NULL, &totalPCMFrameCount)) { - return 0; - } - return totalPCMFrameCount; -} -MA_API ma_uint64 ma_dr_mp3_get_mp3_frame_count(ma_dr_mp3* pMP3) -{ - ma_uint64 totalMP3FrameCount; - if (!ma_dr_mp3_get_mp3_and_pcm_frame_count(pMP3, &totalMP3FrameCount, NULL)) { - return 0; - } - return totalMP3FrameCount; -} -static void ma_dr_mp3__accumulate_running_pcm_frame_count(ma_dr_mp3* pMP3, ma_uint32 pcmFrameCountIn, ma_uint64* pRunningPCMFrameCount, float* pRunningPCMFrameCountFractionalPart) -{ - float srcRatio; - float pcmFrameCountOutF; - ma_uint32 pcmFrameCountOut; - srcRatio = (float)pMP3->mp3FrameSampleRate / (float)pMP3->sampleRate; - MA_DR_MP3_ASSERT(srcRatio > 0); - pcmFrameCountOutF = *pRunningPCMFrameCountFractionalPart + (pcmFrameCountIn / srcRatio); - pcmFrameCountOut = (ma_uint32)pcmFrameCountOutF; - *pRunningPCMFrameCountFractionalPart = pcmFrameCountOutF - pcmFrameCountOut; - *pRunningPCMFrameCount += pcmFrameCountOut; -} -typedef struct -{ - ma_uint64 bytePos; - ma_uint64 pcmFrameIndex; -} ma_dr_mp3__seeking_mp3_frame_info; -MA_API ma_bool32 ma_dr_mp3_calculate_seek_points(ma_dr_mp3* pMP3, ma_uint32* pSeekPointCount, ma_dr_mp3_seek_point* pSeekPoints) -{ - ma_uint32 seekPointCount; - ma_uint64 currentPCMFrame; - ma_uint64 totalMP3FrameCount; - ma_uint64 totalPCMFrameCount; - if (pMP3 == NULL || pSeekPointCount == NULL || pSeekPoints == NULL) { - return MA_FALSE; - } - seekPointCount = *pSeekPointCount; - if (seekPointCount == 0) { - return MA_FALSE; - } - currentPCMFrame = pMP3->currentPCMFrame; - if (!ma_dr_mp3_get_mp3_and_pcm_frame_count(pMP3, &totalMP3FrameCount, &totalPCMFrameCount)) { - return MA_FALSE; - } - if (totalMP3FrameCount < MA_DR_MP3_SEEK_LEADING_MP3_FRAMES+1) { - seekPointCount = 1; - pSeekPoints[0].seekPosInBytes = 0; - pSeekPoints[0].pcmFrameIndex = 0; - pSeekPoints[0].mp3FramesToDiscard = 0; - pSeekPoints[0].pcmFramesToDiscard = 0; - } else { - ma_uint64 pcmFramesBetweenSeekPoints; - ma_dr_mp3__seeking_mp3_frame_info mp3FrameInfo[MA_DR_MP3_SEEK_LEADING_MP3_FRAMES+1]; - ma_uint64 runningPCMFrameCount = 0; - float runningPCMFrameCountFractionalPart = 0; - ma_uint64 nextTargetPCMFrame; - ma_uint32 iMP3Frame; - ma_uint32 iSeekPoint; - if (seekPointCount > totalMP3FrameCount-1) { - seekPointCount = (ma_uint32)totalMP3FrameCount-1; - } - pcmFramesBetweenSeekPoints = totalPCMFrameCount / (seekPointCount+1); - if (!ma_dr_mp3_seek_to_start_of_stream(pMP3)) { - return MA_FALSE; - } - for (iMP3Frame = 0; iMP3Frame < MA_DR_MP3_SEEK_LEADING_MP3_FRAMES+1; ++iMP3Frame) { - ma_uint32 pcmFramesInCurrentMP3FrameIn; - MA_DR_MP3_ASSERT(pMP3->streamCursor >= pMP3->dataSize); - mp3FrameInfo[iMP3Frame].bytePos = pMP3->streamCursor - pMP3->dataSize; - mp3FrameInfo[iMP3Frame].pcmFrameIndex = runningPCMFrameCount; - pcmFramesInCurrentMP3FrameIn = ma_dr_mp3_decode_next_frame_ex(pMP3, NULL); - if (pcmFramesInCurrentMP3FrameIn == 0) { - return MA_FALSE; - } - ma_dr_mp3__accumulate_running_pcm_frame_count(pMP3, pcmFramesInCurrentMP3FrameIn, &runningPCMFrameCount, &runningPCMFrameCountFractionalPart); - } - nextTargetPCMFrame = 0; - for (iSeekPoint = 0; iSeekPoint < seekPointCount; ++iSeekPoint) { - nextTargetPCMFrame += pcmFramesBetweenSeekPoints; - for (;;) { - if (nextTargetPCMFrame < runningPCMFrameCount) { - pSeekPoints[iSeekPoint].seekPosInBytes = mp3FrameInfo[0].bytePos; - pSeekPoints[iSeekPoint].pcmFrameIndex = nextTargetPCMFrame; - pSeekPoints[iSeekPoint].mp3FramesToDiscard = MA_DR_MP3_SEEK_LEADING_MP3_FRAMES; - pSeekPoints[iSeekPoint].pcmFramesToDiscard = (ma_uint16)(nextTargetPCMFrame - mp3FrameInfo[MA_DR_MP3_SEEK_LEADING_MP3_FRAMES-1].pcmFrameIndex); - break; - } else { - size_t i; - ma_uint32 pcmFramesInCurrentMP3FrameIn; - for (i = 0; i < MA_DR_MP3_COUNTOF(mp3FrameInfo)-1; ++i) { - mp3FrameInfo[i] = mp3FrameInfo[i+1]; - } - mp3FrameInfo[MA_DR_MP3_COUNTOF(mp3FrameInfo)-1].bytePos = pMP3->streamCursor - pMP3->dataSize; - mp3FrameInfo[MA_DR_MP3_COUNTOF(mp3FrameInfo)-1].pcmFrameIndex = runningPCMFrameCount; - pcmFramesInCurrentMP3FrameIn = ma_dr_mp3_decode_next_frame_ex(pMP3, NULL); - if (pcmFramesInCurrentMP3FrameIn == 0) { - pSeekPoints[iSeekPoint].seekPosInBytes = mp3FrameInfo[0].bytePos; - pSeekPoints[iSeekPoint].pcmFrameIndex = nextTargetPCMFrame; - pSeekPoints[iSeekPoint].mp3FramesToDiscard = MA_DR_MP3_SEEK_LEADING_MP3_FRAMES; - pSeekPoints[iSeekPoint].pcmFramesToDiscard = (ma_uint16)(nextTargetPCMFrame - mp3FrameInfo[MA_DR_MP3_SEEK_LEADING_MP3_FRAMES-1].pcmFrameIndex); - break; - } - ma_dr_mp3__accumulate_running_pcm_frame_count(pMP3, pcmFramesInCurrentMP3FrameIn, &runningPCMFrameCount, &runningPCMFrameCountFractionalPart); - } - } - } - if (!ma_dr_mp3_seek_to_start_of_stream(pMP3)) { - return MA_FALSE; - } - if (!ma_dr_mp3_seek_to_pcm_frame(pMP3, currentPCMFrame)) { - return MA_FALSE; - } - } - *pSeekPointCount = seekPointCount; - return MA_TRUE; -} -MA_API ma_bool32 ma_dr_mp3_bind_seek_table(ma_dr_mp3* pMP3, ma_uint32 seekPointCount, ma_dr_mp3_seek_point* pSeekPoints) -{ - if (pMP3 == NULL) { - return MA_FALSE; - } - if (seekPointCount == 0 || pSeekPoints == NULL) { - pMP3->seekPointCount = 0; - pMP3->pSeekPoints = NULL; - } else { - pMP3->seekPointCount = seekPointCount; - pMP3->pSeekPoints = pSeekPoints; - } - return MA_TRUE; -} -static float* ma_dr_mp3__full_read_and_close_f32(ma_dr_mp3* pMP3, ma_dr_mp3_config* pConfig, ma_uint64* pTotalFrameCount) -{ - ma_uint64 totalFramesRead = 0; - ma_uint64 framesCapacity = 0; - float* pFrames = NULL; - float temp[4096]; - MA_DR_MP3_ASSERT(pMP3 != NULL); - for (;;) { - ma_uint64 framesToReadRightNow = MA_DR_MP3_COUNTOF(temp) / pMP3->channels; - ma_uint64 framesJustRead = ma_dr_mp3_read_pcm_frames_f32(pMP3, framesToReadRightNow, temp); - if (framesJustRead == 0) { - break; - } - if (framesCapacity < totalFramesRead + framesJustRead) { - ma_uint64 oldFramesBufferSize; - ma_uint64 newFramesBufferSize; - ma_uint64 newFramesCap; - float* pNewFrames; - newFramesCap = framesCapacity * 2; - if (newFramesCap < totalFramesRead + framesJustRead) { - newFramesCap = totalFramesRead + framesJustRead; - } - oldFramesBufferSize = framesCapacity * pMP3->channels * sizeof(float); - newFramesBufferSize = newFramesCap * pMP3->channels * sizeof(float); - if (newFramesBufferSize > (ma_uint64)MA_SIZE_MAX) { - break; - } - pNewFrames = (float*)ma_dr_mp3__realloc_from_callbacks(pFrames, (size_t)newFramesBufferSize, (size_t)oldFramesBufferSize, &pMP3->allocationCallbacks); - if (pNewFrames == NULL) { - ma_dr_mp3__free_from_callbacks(pFrames, &pMP3->allocationCallbacks); - break; - } - pFrames = pNewFrames; - framesCapacity = newFramesCap; - } - MA_DR_MP3_COPY_MEMORY(pFrames + totalFramesRead*pMP3->channels, temp, (size_t)(framesJustRead*pMP3->channels*sizeof(float))); - totalFramesRead += framesJustRead; - if (framesJustRead != framesToReadRightNow) { - break; - } - } - if (pConfig != NULL) { - pConfig->channels = pMP3->channels; - pConfig->sampleRate = pMP3->sampleRate; - } - ma_dr_mp3_uninit(pMP3); - if (pTotalFrameCount) { - *pTotalFrameCount = totalFramesRead; - } - return pFrames; -} -static ma_int16* ma_dr_mp3__full_read_and_close_s16(ma_dr_mp3* pMP3, ma_dr_mp3_config* pConfig, ma_uint64* pTotalFrameCount) -{ - ma_uint64 totalFramesRead = 0; - ma_uint64 framesCapacity = 0; - ma_int16* pFrames = NULL; - ma_int16 temp[4096]; - MA_DR_MP3_ASSERT(pMP3 != NULL); - for (;;) { - ma_uint64 framesToReadRightNow = MA_DR_MP3_COUNTOF(temp) / pMP3->channels; - ma_uint64 framesJustRead = ma_dr_mp3_read_pcm_frames_s16(pMP3, framesToReadRightNow, temp); - if (framesJustRead == 0) { - break; - } - if (framesCapacity < totalFramesRead + framesJustRead) { - ma_uint64 newFramesBufferSize; - ma_uint64 oldFramesBufferSize; - ma_uint64 newFramesCap; - ma_int16* pNewFrames; - newFramesCap = framesCapacity * 2; - if (newFramesCap < totalFramesRead + framesJustRead) { - newFramesCap = totalFramesRead + framesJustRead; - } - oldFramesBufferSize = framesCapacity * pMP3->channels * sizeof(ma_int16); - newFramesBufferSize = newFramesCap * pMP3->channels * sizeof(ma_int16); - if (newFramesBufferSize > (ma_uint64)MA_SIZE_MAX) { - break; - } - pNewFrames = (ma_int16*)ma_dr_mp3__realloc_from_callbacks(pFrames, (size_t)newFramesBufferSize, (size_t)oldFramesBufferSize, &pMP3->allocationCallbacks); - if (pNewFrames == NULL) { - ma_dr_mp3__free_from_callbacks(pFrames, &pMP3->allocationCallbacks); - break; - } - pFrames = pNewFrames; - framesCapacity = newFramesCap; - } - MA_DR_MP3_COPY_MEMORY(pFrames + totalFramesRead*pMP3->channels, temp, (size_t)(framesJustRead*pMP3->channels*sizeof(ma_int16))); - totalFramesRead += framesJustRead; - if (framesJustRead != framesToReadRightNow) { - break; - } - } - if (pConfig != NULL) { - pConfig->channels = pMP3->channels; - pConfig->sampleRate = pMP3->sampleRate; - } - ma_dr_mp3_uninit(pMP3); - if (pTotalFrameCount) { - *pTotalFrameCount = totalFramesRead; - } - return pFrames; -} -MA_API float* ma_dr_mp3_open_and_read_pcm_frames_f32(ma_dr_mp3_read_proc onRead, ma_dr_mp3_seek_proc onSeek, void* pUserData, ma_dr_mp3_config* pConfig, ma_uint64* pTotalFrameCount, const ma_allocation_callbacks* pAllocationCallbacks) -{ - ma_dr_mp3 mp3; - if (!ma_dr_mp3_init(&mp3, onRead, onSeek, pUserData, pAllocationCallbacks)) { - return NULL; - } - return ma_dr_mp3__full_read_and_close_f32(&mp3, pConfig, pTotalFrameCount); -} -MA_API ma_int16* ma_dr_mp3_open_and_read_pcm_frames_s16(ma_dr_mp3_read_proc onRead, ma_dr_mp3_seek_proc onSeek, void* pUserData, ma_dr_mp3_config* pConfig, ma_uint64* pTotalFrameCount, const ma_allocation_callbacks* pAllocationCallbacks) -{ - ma_dr_mp3 mp3; - if (!ma_dr_mp3_init(&mp3, onRead, onSeek, pUserData, pAllocationCallbacks)) { - return NULL; - } - return ma_dr_mp3__full_read_and_close_s16(&mp3, pConfig, pTotalFrameCount); -} -MA_API float* ma_dr_mp3_open_memory_and_read_pcm_frames_f32(const void* pData, size_t dataSize, ma_dr_mp3_config* pConfig, ma_uint64* pTotalFrameCount, const ma_allocation_callbacks* pAllocationCallbacks) -{ - ma_dr_mp3 mp3; - if (!ma_dr_mp3_init_memory(&mp3, pData, dataSize, pAllocationCallbacks)) { - return NULL; - } - return ma_dr_mp3__full_read_and_close_f32(&mp3, pConfig, pTotalFrameCount); -} -MA_API ma_int16* ma_dr_mp3_open_memory_and_read_pcm_frames_s16(const void* pData, size_t dataSize, ma_dr_mp3_config* pConfig, ma_uint64* pTotalFrameCount, const ma_allocation_callbacks* pAllocationCallbacks) -{ - ma_dr_mp3 mp3; - if (!ma_dr_mp3_init_memory(&mp3, pData, dataSize, pAllocationCallbacks)) { - return NULL; - } - return ma_dr_mp3__full_read_and_close_s16(&mp3, pConfig, pTotalFrameCount); -} -#ifndef MA_DR_MP3_NO_STDIO -MA_API float* ma_dr_mp3_open_file_and_read_pcm_frames_f32(const char* filePath, ma_dr_mp3_config* pConfig, ma_uint64* pTotalFrameCount, const ma_allocation_callbacks* pAllocationCallbacks) -{ - ma_dr_mp3 mp3; - if (!ma_dr_mp3_init_file(&mp3, filePath, pAllocationCallbacks)) { - return NULL; - } - return ma_dr_mp3__full_read_and_close_f32(&mp3, pConfig, pTotalFrameCount); -} -MA_API ma_int16* ma_dr_mp3_open_file_and_read_pcm_frames_s16(const char* filePath, ma_dr_mp3_config* pConfig, ma_uint64* pTotalFrameCount, const ma_allocation_callbacks* pAllocationCallbacks) -{ - ma_dr_mp3 mp3; - if (!ma_dr_mp3_init_file(&mp3, filePath, pAllocationCallbacks)) { - return NULL; - } - return ma_dr_mp3__full_read_and_close_s16(&mp3, pConfig, pTotalFrameCount); -} -#endif -MA_API void* ma_dr_mp3_malloc(size_t sz, const ma_allocation_callbacks* pAllocationCallbacks) -{ - if (pAllocationCallbacks != NULL) { - return ma_dr_mp3__malloc_from_callbacks(sz, pAllocationCallbacks); - } else { - return ma_dr_mp3__malloc_default(sz, NULL); - } -} -MA_API void ma_dr_mp3_free(void* p, const ma_allocation_callbacks* pAllocationCallbacks) -{ - if (pAllocationCallbacks != NULL) { - ma_dr_mp3__free_from_callbacks(p, pAllocationCallbacks); - } else { - ma_dr_mp3__free_default(p, NULL); - } -} -#endif -/* dr_mp3_c end */ -#endif /* MA_DR_MP3_IMPLEMENTATION */ -#endif /* MA_NO_MP3 */ - - -/* End globally disabled warnings. */ -#if defined(_MSC_VER) - #pragma warning(pop) -#endif - -#endif /* miniaudio_c */ -#endif /* MINIAUDIO_IMPLEMENTATION */ - - -/* -This software is available as a choice of the following licenses. Choose -whichever you prefer. - -=============================================================================== -ALTERNATIVE 1 - Public Domain (www.unlicense.org) -=============================================================================== -This is free and unencumbered software released into the public domain. - -Anyone is free to copy, modify, publish, use, compile, sell, or distribute this -software, either in source code form or as a compiled binary, for any purpose, -commercial or non-commercial, and by any means. - -In jurisdictions that recognize copyright laws, the author or authors of this -software dedicate any and all copyright interest in the software to the public -domain. We make this dedication for the benefit of the public at large and to -the detriment of our heirs and successors. We intend this dedication to be an -overt act of relinquishment in perpetuity of all present and future rights to -this software under copyright law. - -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 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. - -For more information, please refer to - -=============================================================================== -ALTERNATIVE 2 - MIT No Attribution -=============================================================================== -Copyright 2025 David Reid - -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. - -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. -*/ diff --git a/tests-cmake/fft/CMakeLists.txt b/tests-cmake/fft/CMakeLists.txt deleted file mode 100644 index 77f8119a2c..0000000000 --- a/tests-cmake/fft/CMakeLists.txt +++ /dev/null @@ -1,35 +0,0 @@ -cmake_minimum_required(VERSION 3.20) - - -# set the project name -project(fft) -set (CMAKE_CXX_STANDARD 11) -set (DCMAKE_CXX_FLAGS "-Werror") -set (CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -O0 -ldl -lpthread -lm") -set (CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} -O0 -ldl -lpthread -lm") - -# Emulator is not necessary for -DIS_MIN_DESKTOP -set(ADD_ARDUINO_EMULATOR OFF CACHE BOOL "Add Arduino Emulator Library") -set(ADD_PORTAUDIO OFF CACHE BOOL "No Portaudio") - -# Build with arduino-audio-tools -if(CMAKE_SOURCE_DIR STREQUAL CMAKE_CURRENT_SOURCE_DIR) - add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/../.. ${CMAKE_CURRENT_BINARY_DIR}/arduino-audio-tools ) -endif() - -# Download miniaudio.h -file(DOWNLOAD https://raw.githubusercontent.com/mackron/miniaudio/master/miniaudio.h - ${CMAKE_CURRENT_SOURCE_DIR}/miniaudio.h) - - -# build sketch as executable -add_executable (fft fft.cpp) - -# set preprocessor defines -target_compile_definitions(fft PUBLIC -DIS_MIN_DESKTOP) - -# specify libraries -target_link_libraries(fft arduino-audio-tools) - -# access to miniaudio in sketch directory -target_include_directories(fft PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}) diff --git a/tests-cmake/fft/fft.cpp b/tests-cmake/fft/fft.cpp deleted file mode 100644 index f4cdfbb3f1..0000000000 --- a/tests-cmake/fft/fft.cpp +++ /dev/null @@ -1,48 +0,0 @@ -#include "AudioTools.h" -#include "AudioTools/AudioLibs/AudioRealFFT.h" // using RealFFT -#include "AudioTools/AudioLibs/MiniAudioStream.h" - -AudioInfo info(44100, 2, 16); -AudioRealFFT afft; // or AudioKissFFT -Hann hann; -BufferedWindow buffered(&hann); -SineWaveGenerator sineWave(32000); -GeneratedSoundStream in(sineWave); -StreamCopy copier(afft, in); -//CsvOutput out(Serial); -MiniAudioStream out; -StreamCopy copierIFFT(out, afft); - -// process fft result -void fftResult(AudioFFTBase &fft) { - // copy ifft result to output - while (copierIFFT.copy()); -} - -void setup() { - AudioToolsLogger.begin(Serial, AudioToolsLogLevel::Warning); - - // set the frequency - sineWave.setFrequency(N_B4); - - // Setup sine wave - auto cfg = in.defaultConfig(); - cfg.copyFrom(info); - in.begin(cfg); - - // Setup FFT - auto tcfg = afft.defaultConfig(RXTX_MODE); - tcfg.copyFrom(info); - tcfg.length = 1024; - tcfg.window_function = &buffered; - tcfg.stride = 512; - tcfg.callback = fftResult; - afft.begin(tcfg); - - // setup output - auto ocfg = out.defaultConfig(TX_MODE); - ocfg.copyFrom(info); - out.begin(ocfg); -} - -void loop() { copier.copy(); } \ No newline at end of file diff --git a/tests-cmake/fft/miniaudio.h b/tests-cmake/fft/miniaudio.h deleted file mode 100644 index c74bebeb3c..0000000000 --- a/tests-cmake/fft/miniaudio.h +++ /dev/null @@ -1,93468 +0,0 @@ -/* -Audio playback and capture library. Choice of public domain or MIT-0. See license statements at the end of this file. -miniaudio - v0.11.22 - 2025-02-24 - -David Reid - mackron@gmail.com - -Website: https://miniaud.io -Documentation: https://miniaud.io/docs -GitHub: https://github.com/mackron/miniaudio -*/ - -/* -1. Introduction -=============== -To use miniaudio, include "miniaudio.h": - - ```c - #include "miniaudio.h" - ``` - -The implementation is contained in "miniaudio.c". Just compile this like any other source file. You -can include miniaudio.c if you want to compile your project as a single translation unit: - - ```c - #include "miniaudio.c" - ``` - -miniaudio includes both low level and high level APIs. The low level API is good for those who want -to do all of their mixing themselves and only require a light weight interface to the underlying -audio device. The high level API is good for those who have complex mixing and effect requirements. - -In miniaudio, objects are transparent structures. Unlike many other libraries, there are no handles -to opaque objects which means you need to allocate memory for objects yourself. In the examples -presented in this documentation you will often see objects declared on the stack. You need to be -careful when translating these examples to your own code so that you don't accidentally declare -your objects on the stack and then cause them to become invalid once the function returns. In -addition, you must ensure the memory address of your objects remain the same throughout their -lifetime. You therefore cannot be making copies of your objects. - -A config/init pattern is used throughout the entire library. The idea is that you set up a config -object and pass that into the initialization routine. The advantage to this system is that the -config object can be initialized with logical defaults and new properties added to it without -breaking the API. The config object can be allocated on the stack and does not need to be -maintained after initialization of the corresponding object. - - -1.1. Low Level API ------------------- -The low level API gives you access to the raw audio data of an audio device. It supports playback, -capture, full-duplex and loopback (WASAPI only). You can enumerate over devices to determine which -physical device(s) you want to connect to. - -The low level API uses the concept of a "device" as the abstraction for physical devices. The idea -is that you choose a physical device to emit or capture audio from, and then move data to/from the -device when miniaudio tells you to. Data is delivered to and from devices asynchronously via a -callback which you specify when initializing the device. - -When initializing the device you first need to configure it. The device configuration allows you to -specify things like the format of the data delivered via the callback, the size of the internal -buffer and the ID of the device you want to emit or capture audio from. - -Once you have the device configuration set up you can initialize the device. When initializing a -device you need to allocate memory for the device object beforehand. This gives the application -complete control over how the memory is allocated. In the example below we initialize a playback -device on the stack, but you could allocate it on the heap if that suits your situation better. - - ```c - void data_callback(ma_device* pDevice, void* pOutput, const void* pInput, ma_uint32 frameCount) - { - // In playback mode copy data to pOutput. In capture mode read data from pInput. In full-duplex mode, both - // pOutput and pInput will be valid and you can move data from pInput into pOutput. Never process more than - // frameCount frames. - } - - int main() - { - ma_device_config config = ma_device_config_init(ma_device_type_playback); - config.playback.format = ma_format_f32; // Set to ma_format_unknown to use the device's native format. - config.playback.channels = 2; // Set to 0 to use the device's native channel count. - config.sampleRate = 48000; // Set to 0 to use the device's native sample rate. - config.dataCallback = data_callback; // This function will be called when miniaudio needs more data. - config.pUserData = pMyCustomData; // Can be accessed from the device object (device.pUserData). - - ma_device device; - if (ma_device_init(NULL, &config, &device) != MA_SUCCESS) { - return -1; // Failed to initialize the device. - } - - ma_device_start(&device); // The device is sleeping by default so you'll need to start it manually. - - // Do something here. Probably your program's main loop. - - ma_device_uninit(&device); - return 0; - } - ``` - -In the example above, `data_callback()` is where audio data is written and read from the device. -The idea is in playback mode you cause sound to be emitted from the speakers by writing audio data -to the output buffer (`pOutput` in the example). In capture mode you read data from the input -buffer (`pInput`) to extract sound captured by the microphone. The `frameCount` parameter tells you -how many frames can be written to the output buffer and read from the input buffer. A "frame" is -one sample for each channel. For example, in a stereo stream (2 channels), one frame is 2 -samples: one for the left, one for the right. The channel count is defined by the device config. -The size in bytes of an individual sample is defined by the sample format which is also specified -in the device config. Multi-channel audio data is always interleaved, which means the samples for -each frame are stored next to each other in memory. For example, in a stereo stream the first pair -of samples will be the left and right samples for the first frame, the second pair of samples will -be the left and right samples for the second frame, etc. - -The configuration of the device is defined by the `ma_device_config` structure. The config object -is always initialized with `ma_device_config_init()`. It's important to always initialize the -config with this function as it initializes it with logical defaults and ensures your program -doesn't break when new members are added to the `ma_device_config` structure. The example above -uses a fairly simple and standard device configuration. The call to `ma_device_config_init()` takes -a single parameter, which is whether or not the device is a playback, capture, duplex or loopback -device (loopback devices are not supported on all backends). The `config.playback.format` member -sets the sample format which can be one of the following (all formats are native-endian): - - +---------------+----------------------------------------+---------------------------+ - | Symbol | Description | Range | - +---------------+----------------------------------------+---------------------------+ - | ma_format_f32 | 32-bit floating point | [-1, 1] | - | ma_format_s16 | 16-bit signed integer | [-32768, 32767] | - | ma_format_s24 | 24-bit signed integer (tightly packed) | [-8388608, 8388607] | - | ma_format_s32 | 32-bit signed integer | [-2147483648, 2147483647] | - | ma_format_u8 | 8-bit unsigned integer | [0, 255] | - +---------------+----------------------------------------+---------------------------+ - -The `config.playback.channels` member sets the number of channels to use with the device. The -channel count cannot exceed MA_MAX_CHANNELS. The `config.sampleRate` member sets the sample rate -(which must be the same for both playback and capture in full-duplex configurations). This is -usually set to 44100 or 48000, but can be set to anything. It's recommended to keep this between -8000 and 384000, however. - -Note that leaving the format, channel count and/or sample rate at their default values will result -in the internal device's native configuration being used which is useful if you want to avoid the -overhead of miniaudio's automatic data conversion. - -In addition to the sample format, channel count and sample rate, the data callback and user data -pointer are also set via the config. The user data pointer is not passed into the callback as a -parameter, but is instead set to the `pUserData` member of `ma_device` which you can access -directly since all miniaudio structures are transparent. - -Initializing the device is done with `ma_device_init()`. This will return a result code telling you -what went wrong, if anything. On success it will return `MA_SUCCESS`. After initialization is -complete the device will be in a stopped state. To start it, use `ma_device_start()`. -Uninitializing the device will stop it, which is what the example above does, but you can also stop -the device with `ma_device_stop()`. To resume the device simply call `ma_device_start()` again. -Note that it's important to never stop or start the device from inside the callback. This will -result in a deadlock. Instead you set a variable or signal an event indicating that the device -needs to stop and handle it in a different thread. The following APIs must never be called inside -the callback: - - ```c - ma_device_init() - ma_device_init_ex() - ma_device_uninit() - ma_device_start() - ma_device_stop() - ``` - -You must never try uninitializing and reinitializing a device inside the callback. You must also -never try to stop and start it from inside the callback. There are a few other things you shouldn't -do in the callback depending on your requirements, however this isn't so much a thread-safety -thing, but rather a real-time processing thing which is beyond the scope of this introduction. - -The example above demonstrates the initialization of a playback device, but it works exactly the -same for capture. All you need to do is change the device type from `ma_device_type_playback` to -`ma_device_type_capture` when setting up the config, like so: - - ```c - ma_device_config config = ma_device_config_init(ma_device_type_capture); - config.capture.format = MY_FORMAT; - config.capture.channels = MY_CHANNEL_COUNT; - ``` - -In the data callback you just read from the input buffer (`pInput` in the example above) and leave -the output buffer alone (it will be set to NULL when the device type is set to -`ma_device_type_capture`). - -These are the available device types and how you should handle the buffers in the callback: - - +-------------------------+--------------------------------------------------------+ - | Device Type | Callback Behavior | - +-------------------------+--------------------------------------------------------+ - | ma_device_type_playback | Write to output buffer, leave input buffer untouched. | - | ma_device_type_capture | Read from input buffer, leave output buffer untouched. | - | ma_device_type_duplex | Read from input buffer, write to output buffer. | - | ma_device_type_loopback | Read from input buffer, leave output buffer untouched. | - +-------------------------+--------------------------------------------------------+ - -You will notice in the example above that the sample format and channel count is specified -separately for playback and capture. This is to support different data formats between the playback -and capture devices in a full-duplex system. An example may be that you want to capture audio data -as a monaural stream (one channel), but output sound to a stereo speaker system. Note that if you -use different formats between playback and capture in a full-duplex configuration you will need to -convert the data yourself. There are functions available to help you do this which will be -explained later. - -The example above did not specify a physical device to connect to which means it will use the -operating system's default device. If you have multiple physical devices connected and you want to -use a specific one you will need to specify the device ID in the configuration, like so: - - ```c - config.playback.pDeviceID = pMyPlaybackDeviceID; // Only if requesting a playback or duplex device. - config.capture.pDeviceID = pMyCaptureDeviceID; // Only if requesting a capture, duplex or loopback device. - ``` - -To retrieve the device ID you will need to perform device enumeration, however this requires the -use of a new concept called the "context". Conceptually speaking the context sits above the device. -There is one context to many devices. The purpose of the context is to represent the backend at a -more global level and to perform operations outside the scope of an individual device. Mainly it is -used for performing run-time linking against backend libraries, initializing backends and -enumerating devices. The example below shows how to enumerate devices. - - ```c - ma_context context; - if (ma_context_init(NULL, 0, NULL, &context) != MA_SUCCESS) { - // Error. - } - - ma_device_info* pPlaybackInfos; - ma_uint32 playbackCount; - ma_device_info* pCaptureInfos; - ma_uint32 captureCount; - if (ma_context_get_devices(&context, &pPlaybackInfos, &playbackCount, &pCaptureInfos, &captureCount) != MA_SUCCESS) { - // Error. - } - - // Loop over each device info and do something with it. Here we just print the name with their index. You may want - // to give the user the opportunity to choose which device they'd prefer. - for (ma_uint32 iDevice = 0; iDevice < playbackCount; iDevice += 1) { - printf("%d - %s\n", iDevice, pPlaybackInfos[iDevice].name); - } - - ma_device_config config = ma_device_config_init(ma_device_type_playback); - config.playback.pDeviceID = &pPlaybackInfos[chosenPlaybackDeviceIndex].id; - config.playback.format = MY_FORMAT; - config.playback.channels = MY_CHANNEL_COUNT; - config.sampleRate = MY_SAMPLE_RATE; - config.dataCallback = data_callback; - config.pUserData = pMyCustomData; - - ma_device device; - if (ma_device_init(&context, &config, &device) != MA_SUCCESS) { - // Error - } - - ... - - ma_device_uninit(&device); - ma_context_uninit(&context); - ``` - -The first thing we do in this example is initialize a `ma_context` object with `ma_context_init()`. -The first parameter is a pointer to a list of `ma_backend` values which are used to override the -default backend priorities. When this is NULL, as in this example, miniaudio's default priorities -are used. The second parameter is the number of backends listed in the array pointed to by the -first parameter. The third parameter is a pointer to a `ma_context_config` object which can be -NULL, in which case defaults are used. The context configuration is used for setting the logging -callback, custom memory allocation callbacks, user-defined data and some backend-specific -configurations. - -Once the context has been initialized you can enumerate devices. In the example above we use the -simpler `ma_context_get_devices()`, however you can also use a callback for handling devices by -using `ma_context_enumerate_devices()`. When using `ma_context_get_devices()` you provide a pointer -to a pointer that will, upon output, be set to a pointer to a buffer containing a list of -`ma_device_info` structures. You also provide a pointer to an unsigned integer that will receive -the number of items in the returned buffer. Do not free the returned buffers as their memory is -managed internally by miniaudio. - -The `ma_device_info` structure contains an `id` member which is the ID you pass to the device -config. It also contains the name of the device which is useful for presenting a list of devices -to the user via the UI. - -When creating your own context you will want to pass it to `ma_device_init()` when initializing the -device. Passing in NULL, like we do in the first example, will result in miniaudio creating the -context for you, which you don't want to do since you've already created a context. Note that -internally the context is only tracked by it's pointer which means you must not change the location -of the `ma_context` object. If this is an issue, consider using `malloc()` to allocate memory for -the context. - - -1.2. High Level API -------------------- -The high level API consists of three main parts: - - * Resource management for loading and streaming sounds. - * A node graph for advanced mixing and effect processing. - * A high level "engine" that wraps around the resource manager and node graph. - -The resource manager (`ma_resource_manager`) is used for loading sounds. It supports loading sounds -fully into memory and also streaming. It will also deal with reference counting for you which -avoids the same sound being loaded multiple times. - -The node graph is used for mixing and effect processing. The idea is that you connect a number of -nodes into the graph by connecting each node's outputs to another node's inputs. Each node can -implement its own effect. By chaining nodes together, advanced mixing and effect processing can -be achieved. - -The engine encapsulates both the resource manager and the node graph to create a simple, easy to -use high level API. The resource manager and node graph APIs are covered in more later sections of -this manual. - -The code below shows how you can initialize an engine using it's default configuration. - - ```c - ma_result result; - ma_engine engine; - - result = ma_engine_init(NULL, &engine); - if (result != MA_SUCCESS) { - return result; // Failed to initialize the engine. - } - ``` - -This creates an engine instance which will initialize a device internally which you can access with -`ma_engine_get_device()`. It will also initialize a resource manager for you which can be accessed -with `ma_engine_get_resource_manager()`. The engine itself is a node graph (`ma_node_graph`) which -means you can pass a pointer to the engine object into any of the `ma_node_graph` APIs (with a -cast). Alternatively, you can use `ma_engine_get_node_graph()` instead of a cast. - -Note that all objects in miniaudio, including the `ma_engine` object in the example above, are -transparent structures. There are no handles to opaque structures in miniaudio which means you need -to be mindful of how you declare them. In the example above we are declaring it on the stack, but -this will result in the struct being invalidated once the function encapsulating it returns. If -allocating the engine on the heap is more appropriate, you can easily do so with a standard call -to `malloc()` or whatever heap allocation routine you like: - - ```c - ma_engine* pEngine = malloc(sizeof(*pEngine)); - ``` - -The `ma_engine` API uses the same config/init pattern used all throughout miniaudio. To configure -an engine, you can fill out a `ma_engine_config` object and pass it into the first parameter of -`ma_engine_init()`: - - ```c - ma_result result; - ma_engine engine; - ma_engine_config engineConfig; - - engineConfig = ma_engine_config_init(); - engineConfig.pResourceManager = &myCustomResourceManager; // <-- Initialized as some earlier stage. - - result = ma_engine_init(&engineConfig, &engine); - if (result != MA_SUCCESS) { - return result; - } - ``` - -This creates an engine instance using a custom config. In this particular example it's showing how -you can specify a custom resource manager rather than having the engine initialize one internally. -This is particularly useful if you want to have multiple engine's share the same resource manager. - -The engine must be uninitialized with `ma_engine_uninit()` when it's no longer needed. - -By default the engine will be started, but nothing will be playing because no sounds have been -initialized. The easiest but least flexible way of playing a sound is like so: - - ```c - ma_engine_play_sound(&engine, "my_sound.wav", NULL); - ``` - -This plays what miniaudio calls an "inline" sound. It plays the sound once, and then puts the -internal sound up for recycling. The last parameter is used to specify which sound group the sound -should be associated with which will be explained later. This particular way of playing a sound is -simple, but lacks flexibility and features. A more flexible way of playing a sound is to first -initialize a sound: - - ```c - ma_result result; - ma_sound sound; - - result = ma_sound_init_from_file(&engine, "my_sound.wav", 0, NULL, NULL, &sound); - if (result != MA_SUCCESS) { - return result; - } - - ma_sound_start(&sound); - ``` - -This returns a `ma_sound` object which represents a single instance of the specified sound file. If -you want to play the same file multiple times simultaneously, you need to create one sound for each -instance. - -Sounds should be uninitialized with `ma_sound_uninit()`. - -Sounds are not started by default. Start a sound with `ma_sound_start()` and stop it with -`ma_sound_stop()`. When a sound is stopped, it is not rewound to the start. Use -`ma_sound_seek_to_pcm_frame(&sound, 0)` to seek back to the start of a sound. By default, starting -and stopping sounds happens immediately, but sometimes it might be convenient to schedule the sound -the be started and/or stopped at a specific time. This can be done with the following functions: - - ```c - ma_sound_set_start_time_in_pcm_frames() - ma_sound_set_start_time_in_milliseconds() - ma_sound_set_stop_time_in_pcm_frames() - ma_sound_set_stop_time_in_milliseconds() - ``` - -The start/stop time needs to be specified based on the absolute timer which is controlled by the -engine. The current global time in PCM frames can be retrieved with -`ma_engine_get_time_in_pcm_frames()`. The engine's global time can be changed with -`ma_engine_set_time_in_pcm_frames()` for synchronization purposes if required. Note that scheduling -a start time still requires an explicit call to `ma_sound_start()` before anything will play: - - ```c - ma_sound_set_start_time_in_pcm_frames(&sound, ma_engine_get_time_in_pcm_frames(&engine) + (ma_engine_get_sample_rate(&engine) * 2); - ma_sound_start(&sound); - ``` - -The third parameter of `ma_sound_init_from_file()` is a set of flags that control how the sound be -loaded and a few options on which features should be enabled for that sound. By default, the sound -is synchronously loaded fully into memory straight from the file system without any kind of -decoding. If you want to decode the sound before storing it in memory, you need to specify the -`MA_SOUND_FLAG_DECODE` flag. This is useful if you want to incur the cost of decoding at an earlier -stage, such as a loading stage. Without this option, decoding will happen dynamically at mixing -time which might be too expensive on the audio thread. - -If you want to load the sound asynchronously, you can specify the `MA_SOUND_FLAG_ASYNC` flag. This -will result in `ma_sound_init_from_file()` returning quickly, but the sound will not start playing -until the sound has had some audio decoded. - -The fourth parameter is a pointer to sound group. A sound group is used as a mechanism to organise -sounds into groups which have their own effect processing and volume control. An example is a game -which might have separate groups for sfx, voice and music. Each of these groups have their own -independent volume control. Use `ma_sound_group_init()` or `ma_sound_group_init_ex()` to initialize -a sound group. - -Sounds and sound groups are nodes in the engine's node graph and can be plugged into any `ma_node` -API. This makes it possible to connect sounds and sound groups to effect nodes to produce complex -effect chains. - -A sound can have its volume changed with `ma_sound_set_volume()`. If you prefer decibel volume -control you can use `ma_volume_db_to_linear()` to convert from decibel representation to linear. - -Panning and pitching is supported with `ma_sound_set_pan()` and `ma_sound_set_pitch()`. If you know -a sound will never have its pitch changed with `ma_sound_set_pitch()` or via the doppler effect, -you can specify the `MA_SOUND_FLAG_NO_PITCH` flag when initializing the sound for an optimization. - -By default, sounds and sound groups have spatialization enabled. If you don't ever want to -spatialize your sounds, initialize the sound with the `MA_SOUND_FLAG_NO_SPATIALIZATION` flag. The -spatialization model is fairly simple and is roughly on feature parity with OpenAL. HRTF and -environmental occlusion are not currently supported, but planned for the future. The supported -features include: - - * Sound and listener positioning and orientation with cones - * Attenuation models: none, inverse, linear and exponential - * Doppler effect - -Sounds can be faded in and out with `ma_sound_set_fade_in_pcm_frames()`. - -To check if a sound is currently playing, you can use `ma_sound_is_playing()`. To check if a sound -is at the end, use `ma_sound_at_end()`. Looping of a sound can be controlled with -`ma_sound_set_looping()`. Use `ma_sound_is_looping()` to check whether or not the sound is looping. - - - -2. Building -=========== -miniaudio should work cleanly out of the box without the need to download or install any -dependencies. See below for platform-specific details. - -Note that GCC and Clang require `-msse2`, `-mavx2`, etc. for SIMD optimizations. - -If you get errors about undefined references to `__sync_val_compare_and_swap_8`, `__atomic_load_8`, -etc. you need to link with `-latomic`. - - -2.1. Windows ------------- -The Windows build should compile cleanly on all popular compilers without the need to configure any -include paths nor link to any libraries. - -The UWP build may require linking to mmdevapi.lib if you get errors about an unresolved external -symbol for `ActivateAudioInterfaceAsync()`. - - -2.2. macOS and iOS ------------------- -The macOS build should compile cleanly without the need to download any dependencies nor link to -any libraries or frameworks. The iOS build needs to be compiled as Objective-C and will need to -link the relevant frameworks but should compile cleanly out of the box with Xcode. Compiling -through the command line requires linking to `-lpthread` and `-lm`. - -Due to the way miniaudio links to frameworks at runtime, your application may not pass Apple's -notarization process. To fix this there are two options. The first is to compile with -`-DMA_NO_RUNTIME_LINKING` which in turn will require linking with -`-framework CoreFoundation -framework CoreAudio -framework AudioToolbox`. If you get errors about -AudioToolbox, try with `-framework AudioUnit` instead. You may get this when using older versions -of iOS. Alternatively, if you would rather keep using runtime linking you can add the following to -your entitlements.xcent file: - - ``` - com.apple.security.cs.allow-dyld-environment-variables - - com.apple.security.cs.allow-unsigned-executable-memory - - ``` - -See this discussion for more info: https://github.com/mackron/miniaudio/issues/203. - - -2.3. Linux ----------- -The Linux build only requires linking to `-ldl`, `-lpthread` and `-lm`. You do not need any -development packages. You may need to link with `-latomic` if you're compiling for 32-bit ARM. - - -2.4. BSD --------- -The BSD build only requires linking to `-lpthread` and `-lm`. NetBSD uses audio(4), OpenBSD uses -sndio and FreeBSD uses OSS. You may need to link with `-latomic` if you're compiling for 32-bit -ARM. - - -2.5. Android ------------- -AAudio is the highest priority backend on Android. This should work out of the box without needing -any kind of compiler configuration. Support for AAudio starts with Android 8 which means older -versions will fall back to OpenSL|ES which requires API level 16+. - -There have been reports that the OpenSL|ES backend fails to initialize on some Android based -devices due to `dlopen()` failing to open "libOpenSLES.so". If this happens on your platform -you'll need to disable run-time linking with `MA_NO_RUNTIME_LINKING` and link with -lOpenSLES. - - -2.6. Emscripten ---------------- -The Emscripten build emits Web Audio JavaScript directly and should compile cleanly out of the box. -You cannot use `-std=c*` compiler flags, nor `-ansi`. - -You can enable the use of AudioWorkets by defining `MA_ENABLE_AUDIO_WORKLETS` and then compiling -with the following options: - - -sAUDIO_WORKLET=1 -sWASM_WORKERS=1 -sASYNCIFY - -An example for compiling with AudioWorklet support might look like this: - - emcc program.c -o bin/program.html -DMA_ENABLE_AUDIO_WORKLETS -sAUDIO_WORKLET=1 -sWASM_WORKERS=1 -sASYNCIFY - -To run locally, you'll need to use emrun: - - emrun bin/program.html - - - -2.7. Build Options ------------------- -`#define` these options before including miniaudio.c, or pass them as compiler flags: - - +----------------------------------+--------------------------------------------------------------------+ - | Option | Description | - +----------------------------------+--------------------------------------------------------------------+ - | MA_NO_WASAPI | Disables the WASAPI backend. | - +----------------------------------+--------------------------------------------------------------------+ - | MA_NO_DSOUND | Disables the DirectSound backend. | - +----------------------------------+--------------------------------------------------------------------+ - | MA_NO_WINMM | Disables the WinMM backend. | - +----------------------------------+--------------------------------------------------------------------+ - | MA_NO_ALSA | Disables the ALSA backend. | - +----------------------------------+--------------------------------------------------------------------+ - | MA_NO_PULSEAUDIO | Disables the PulseAudio backend. | - +----------------------------------+--------------------------------------------------------------------+ - | MA_NO_JACK | Disables the JACK backend. | - +----------------------------------+--------------------------------------------------------------------+ - | MA_NO_COREAUDIO | Disables the Core Audio backend. | - +----------------------------------+--------------------------------------------------------------------+ - | MA_NO_SNDIO | Disables the sndio backend. | - +----------------------------------+--------------------------------------------------------------------+ - | MA_NO_AUDIO4 | Disables the audio(4) backend. | - +----------------------------------+--------------------------------------------------------------------+ - | MA_NO_OSS | Disables the OSS backend. | - +----------------------------------+--------------------------------------------------------------------+ - | MA_NO_AAUDIO | Disables the AAudio backend. | - +----------------------------------+--------------------------------------------------------------------+ - | MA_NO_OPENSL | Disables the OpenSL|ES backend. | - +----------------------------------+--------------------------------------------------------------------+ - | MA_NO_WEBAUDIO | Disables the Web Audio backend. | - +----------------------------------+--------------------------------------------------------------------+ - | MA_NO_CUSTOM | Disables support for custom backends. | - +----------------------------------+--------------------------------------------------------------------+ - | MA_NO_NULL | Disables the null backend. | - +----------------------------------+--------------------------------------------------------------------+ - | MA_ENABLE_ONLY_SPECIFIC_BACKENDS | Disables all backends by default and requires `MA_ENABLE_*` to | - | | enable specific backends. | - +----------------------------------+--------------------------------------------------------------------+ - | MA_ENABLE_WASAPI | Used in conjunction with MA_ENABLE_ONLY_SPECIFIC_BACKENDS to | - | | enable the WASAPI backend. | - +----------------------------------+--------------------------------------------------------------------+ - | MA_ENABLE_DSOUND | Used in conjunction with MA_ENABLE_ONLY_SPECIFIC_BACKENDS to | - | | enable the DirectSound backend. | - +----------------------------------+--------------------------------------------------------------------+ - | MA_ENABLE_WINMM | Used in conjunction with MA_ENABLE_ONLY_SPECIFIC_BACKENDS to | - | | enable the WinMM backend. | - +----------------------------------+--------------------------------------------------------------------+ - | MA_ENABLE_ALSA | Used in conjunction with MA_ENABLE_ONLY_SPECIFIC_BACKENDS to | - | | enable the ALSA backend. | - +----------------------------------+--------------------------------------------------------------------+ - | MA_ENABLE_PULSEAUDIO | Used in conjunction with MA_ENABLE_ONLY_SPECIFIC_BACKENDS to | - | | enable the PulseAudio backend. | - +----------------------------------+--------------------------------------------------------------------+ - | MA_ENABLE_JACK | Used in conjunction with MA_ENABLE_ONLY_SPECIFIC_BACKENDS to | - | | enable the JACK backend. | - +----------------------------------+--------------------------------------------------------------------+ - | MA_ENABLE_COREAUDIO | Used in conjunction with MA_ENABLE_ONLY_SPECIFIC_BACKENDS to | - | | enable the Core Audio backend. | - +----------------------------------+--------------------------------------------------------------------+ - | MA_ENABLE_SNDIO | Used in conjunction with MA_ENABLE_ONLY_SPECIFIC_BACKENDS to | - | | enable the sndio backend. | - +----------------------------------+--------------------------------------------------------------------+ - | MA_ENABLE_AUDIO4 | Used in conjunction with MA_ENABLE_ONLY_SPECIFIC_BACKENDS to | - | | enable the audio(4) backend. | - +----------------------------------+--------------------------------------------------------------------+ - | MA_ENABLE_OSS | Used in conjunction with MA_ENABLE_ONLY_SPECIFIC_BACKENDS to | - | | enable the OSS backend. | - +----------------------------------+--------------------------------------------------------------------+ - | MA_ENABLE_AAUDIO | Used in conjunction with MA_ENABLE_ONLY_SPECIFIC_BACKENDS to | - | | enable the AAudio backend. | - +----------------------------------+--------------------------------------------------------------------+ - | MA_ENABLE_OPENSL | Used in conjunction with MA_ENABLE_ONLY_SPECIFIC_BACKENDS to | - | | enable the OpenSL|ES backend. | - +----------------------------------+--------------------------------------------------------------------+ - | MA_ENABLE_WEBAUDIO | Used in conjunction with MA_ENABLE_ONLY_SPECIFIC_BACKENDS to | - | | enable the Web Audio backend. | - +----------------------------------+--------------------------------------------------------------------+ - | MA_ENABLE_CUSTOM | Used in conjunction with MA_ENABLE_ONLY_SPECIFIC_BACKENDS to | - | | enable custom backends. | - +----------------------------------+--------------------------------------------------------------------+ - | MA_ENABLE_NULL | Used in conjunction with MA_ENABLE_ONLY_SPECIFIC_BACKENDS to | - | | enable the null backend. | - +----------------------------------+--------------------------------------------------------------------+ - | MA_NO_DECODING | Disables decoding APIs. | - +----------------------------------+--------------------------------------------------------------------+ - | MA_NO_ENCODING | Disables encoding APIs. | - +----------------------------------+--------------------------------------------------------------------+ - | MA_NO_WAV | Disables the built-in WAV decoder and encoder. | - +----------------------------------+--------------------------------------------------------------------+ - | MA_NO_FLAC | Disables the built-in FLAC decoder. | - +----------------------------------+--------------------------------------------------------------------+ - | MA_NO_MP3 | Disables the built-in MP3 decoder. | - +----------------------------------+--------------------------------------------------------------------+ - | MA_NO_DEVICE_IO | Disables playback and recording. This will disable `ma_context` | - | | and `ma_device` APIs. This is useful if you only want to use | - | | miniaudio's data conversion and/or decoding APIs. | - +----------------------------------+--------------------------------------------------------------------+ - | MA_NO_RESOURCE_MANAGER | Disables the resource manager. When using the engine this will | - | | also disable the following functions: | - | | | - | | ``` | - | | ma_sound_init_from_file() | - | | ma_sound_init_from_file_w() | - | | ma_sound_init_copy() | - | | ma_engine_play_sound_ex() | - | | ma_engine_play_sound() | - | | ``` | - | | | - | | The only way to initialize a `ma_sound` object is to initialize it | - | | from a data source. | - +----------------------------------+--------------------------------------------------------------------+ - | MA_NO_NODE_GRAPH | Disables the node graph API. This will also disable the engine API | - | | because it depends on the node graph. | - +----------------------------------+--------------------------------------------------------------------+ - | MA_NO_ENGINE | Disables the engine API. | - +----------------------------------+--------------------------------------------------------------------+ - | MA_NO_THREADING | Disables the `ma_thread`, `ma_mutex`, `ma_semaphore` and | - | | `ma_event` APIs. This option is useful if you only need to use | - | | miniaudio for data conversion, decoding and/or encoding. Some | - | | families of APIs require threading which means the following | - | | options must also be set: | - | | | - | | ``` | - | | MA_NO_DEVICE_IO | - | | ``` | - +----------------------------------+--------------------------------------------------------------------+ - | MA_NO_GENERATION | Disables generation APIs such a `ma_waveform` and `ma_noise`. | - +----------------------------------+--------------------------------------------------------------------+ - | MA_NO_SSE2 | Disables SSE2 optimizations. | - +----------------------------------+--------------------------------------------------------------------+ - | MA_NO_AVX2 | Disables AVX2 optimizations. | - +----------------------------------+--------------------------------------------------------------------+ - | MA_NO_NEON | Disables NEON optimizations. | - +----------------------------------+--------------------------------------------------------------------+ - | MA_NO_RUNTIME_LINKING | Disables runtime linking. This is useful for passing Apple's | - | | notarization process. When enabling this, you may need to avoid | - | | using `-std=c89` or `-std=c99` on Linux builds or else you may end | - | | up with compilation errors due to conflicts with `timespec` and | - | | `timeval` data types. | - | | | - | | You may need to enable this if your target platform does not allow | - | | runtime linking via `dlopen()`. | - +----------------------------------+--------------------------------------------------------------------+ - | MA_USE_STDINT | (Pass this in as a compiler flag. Do not `#define` this before | - | | miniaudio.c) Forces the use of stdint.h for sized types. | - +----------------------------------+--------------------------------------------------------------------+ - | MA_DEBUG_OUTPUT | Enable `printf()` output of debug logs (`MA_LOG_LEVEL_DEBUG`). | - +----------------------------------+--------------------------------------------------------------------+ - | MA_COINIT_VALUE | Windows only. The value to pass to internal calls to | - | | `CoInitializeEx()`. Defaults to `COINIT_MULTITHREADED`. | - +----------------------------------+--------------------------------------------------------------------+ - | MA_FORCE_UWP | Windows only. Affects only the WASAPI backend. Will force the | - | | WASAPI backend to use the UWP code path instead of the regular | - | | desktop path. This is normally auto-detected and should rarely be | - | | needed to be used explicitly, but can be useful for debugging. | - +----------------------------------+--------------------------------------------------------------------+ - | MA_ON_THREAD_ENTRY | Defines some code that will be executed as soon as an internal | - | | miniaudio-managed thread is created. This will be the first thing | - | | to be executed by the thread entry point. | - +----------------------------------+--------------------------------------------------------------------+ - | MA_ON_THREAD_EXIT | Defines some code that will be executed from the entry point of an | - | | internal miniaudio-managed thread upon exit. This will be the last | - | | thing to be executed before the thread's entry point exits. | - +----------------------------------+--------------------------------------------------------------------+ - | MA_THREAD_DEFAULT_STACK_SIZE | If set, specifies the default stack size used by miniaudio-managed | - | | threads. | - +----------------------------------+--------------------------------------------------------------------+ - | MA_API | Controls how public APIs should be decorated. Default is `extern`. | - +----------------------------------+--------------------------------------------------------------------+ - - -3. Definitions -============== -This section defines common terms used throughout miniaudio. Unfortunately there is often ambiguity -in the use of terms throughout the audio space, so this section is intended to clarify how miniaudio -uses each term. - -3.1. Sample ------------ -A sample is a single unit of audio data. If the sample format is f32, then one sample is one 32-bit -floating point number. - -3.2. Frame / PCM Frame ----------------------- -A frame is a group of samples equal to the number of channels. For a stereo stream a frame is 2 -samples, a mono frame is 1 sample, a 5.1 surround sound frame is 6 samples, etc. The terms "frame" -and "PCM frame" are the same thing in miniaudio. Note that this is different to a compressed frame. -If ever miniaudio needs to refer to a compressed frame, such as a FLAC frame, it will always -clarify what it's referring to with something like "FLAC frame". - -3.3. Channel ------------- -A stream of monaural audio that is emitted from an individual speaker in a speaker system, or -received from an individual microphone in a microphone system. A stereo stream has two channels (a -left channel, and a right channel), a 5.1 surround sound system has 6 channels, etc. Some audio -systems refer to a channel as a complex audio stream that's mixed with other channels to produce -the final mix - this is completely different to miniaudio's use of the term "channel" and should -not be confused. - -3.4. Sample Rate ----------------- -The sample rate in miniaudio is always expressed in Hz, such as 44100, 48000, etc. It's the number -of PCM frames that are processed per second. - -3.5. Formats ------------- -Throughout miniaudio you will see references to different sample formats: - - +---------------+----------------------------------------+---------------------------+ - | Symbol | Description | Range | - +---------------+----------------------------------------+---------------------------+ - | ma_format_f32 | 32-bit floating point | [-1, 1] | - | ma_format_s16 | 16-bit signed integer | [-32768, 32767] | - | ma_format_s24 | 24-bit signed integer (tightly packed) | [-8388608, 8388607] | - | ma_format_s32 | 32-bit signed integer | [-2147483648, 2147483647] | - | ma_format_u8 | 8-bit unsigned integer | [0, 255] | - +---------------+----------------------------------------+---------------------------+ - -All formats are native-endian. - - - -4. Data Sources -=============== -The data source abstraction in miniaudio is used for retrieving audio data from some source. A few -examples include `ma_decoder`, `ma_noise` and `ma_waveform`. You will need to be familiar with data -sources in order to make sense of some of the higher level concepts in miniaudio. - -The `ma_data_source` API is a generic interface for reading from a data source. Any object that -implements the data source interface can be plugged into any `ma_data_source` function. - -To read data from a data source: - - ```c - ma_result result; - ma_uint64 framesRead; - - result = ma_data_source_read_pcm_frames(pDataSource, pFramesOut, frameCount, &framesRead); - if (result != MA_SUCCESS) { - return result; // Failed to read data from the data source. - } - ``` - -If you don't need the number of frames that were successfully read you can pass in `NULL` to the -`pFramesRead` parameter. If this returns a value less than the number of frames requested it means -the end of the file has been reached. `MA_AT_END` will be returned only when the number of frames -read is 0. - -When calling any data source function, with the exception of `ma_data_source_init()` and -`ma_data_source_uninit()`, you can pass in any object that implements a data source. For example, -you could plug in a decoder like so: - - ```c - ma_result result; - ma_uint64 framesRead; - ma_decoder decoder; // <-- This would be initialized with `ma_decoder_init_*()`. - - result = ma_data_source_read_pcm_frames(&decoder, pFramesOut, frameCount, &framesRead); - if (result != MA_SUCCESS) { - return result; // Failed to read data from the decoder. - } - ``` - -If you want to seek forward you can pass in `NULL` to the `pFramesOut` parameter. Alternatively you -can use `ma_data_source_seek_pcm_frames()`. - -To seek to a specific PCM frame: - - ```c - result = ma_data_source_seek_to_pcm_frame(pDataSource, frameIndex); - if (result != MA_SUCCESS) { - return result; // Failed to seek to PCM frame. - } - ``` - -You can retrieve the total length of a data source in PCM frames, but note that some data sources -may not have the notion of a length, such as noise and waveforms, and others may just not have a -way of determining the length such as some decoders. To retrieve the length: - - ```c - ma_uint64 length; - - result = ma_data_source_get_length_in_pcm_frames(pDataSource, &length); - if (result != MA_SUCCESS) { - return result; // Failed to retrieve the length. - } - ``` - -Care should be taken when retrieving the length of a data source where the underlying decoder is -pulling data from a data stream with an undefined length, such as internet radio or some kind of -broadcast. If you do this, `ma_data_source_get_length_in_pcm_frames()` may never return. - -The current position of the cursor in PCM frames can also be retrieved: - - ```c - ma_uint64 cursor; - - result = ma_data_source_get_cursor_in_pcm_frames(pDataSource, &cursor); - if (result != MA_SUCCESS) { - return result; // Failed to retrieve the cursor. - } - ``` - -You will often need to know the data format that will be returned after reading. This can be -retrieved like so: - - ```c - ma_format format; - ma_uint32 channels; - ma_uint32 sampleRate; - ma_channel channelMap[MA_MAX_CHANNELS]; - - result = ma_data_source_get_data_format(pDataSource, &format, &channels, &sampleRate, channelMap, MA_MAX_CHANNELS); - if (result != MA_SUCCESS) { - return result; // Failed to retrieve data format. - } - ``` - -If you do not need a specific data format property, just pass in NULL to the respective parameter. - -There may be cases where you want to implement something like a sound bank where you only want to -read data within a certain range of the underlying data. To do this you can use a range: - - ```c - result = ma_data_source_set_range_in_pcm_frames(pDataSource, rangeBegInFrames, rangeEndInFrames); - if (result != MA_SUCCESS) { - return result; // Failed to set the range. - } - ``` - -This is useful if you have a sound bank where many sounds are stored in the same file and you want -the data source to only play one of those sub-sounds. Note that once the range is set, everything -that takes a position, such as cursors and loop points, should always be relatvie to the start of -the range. When the range is set, any previously defined loop point will be reset. - -Custom loop points can also be used with data sources. By default, data sources will loop after -they reach the end of the data source, but if you need to loop at a specific location, you can do -the following: - - ```c - result = ma_data_set_loop_point_in_pcm_frames(pDataSource, loopBegInFrames, loopEndInFrames); - if (result != MA_SUCCESS) { - return result; // Failed to set the loop point. - } - ``` - -The loop point is relative to the current range. - -It's sometimes useful to chain data sources together so that a seamless transition can be achieved. -To do this, you can use chaining: - - ```c - ma_decoder decoder1; - ma_decoder decoder2; - - // ... initialize decoders with ma_decoder_init_*() ... - - result = ma_data_source_set_next(&decoder1, &decoder2); - if (result != MA_SUCCESS) { - return result; // Failed to set the next data source. - } - - result = ma_data_source_read_pcm_frames(&decoder1, pFramesOut, frameCount, pFramesRead); - if (result != MA_SUCCESS) { - return result; // Failed to read from the decoder. - } - ``` - -In the example above we're using decoders. When reading from a chain, you always want to read from -the top level data source in the chain. In the example above, `decoder1` is the top level data -source in the chain. When `decoder1` reaches the end, `decoder2` will start seamlessly without any -gaps. - -Note that when looping is enabled, only the current data source will be looped. You can loop the -entire chain by linking in a loop like so: - - ```c - ma_data_source_set_next(&decoder1, &decoder2); // decoder1 -> decoder2 - ma_data_source_set_next(&decoder2, &decoder1); // decoder2 -> decoder1 (loop back to the start). - ``` - -Note that setting up chaining is not thread safe, so care needs to be taken if you're dynamically -changing links while the audio thread is in the middle of reading. - -Do not use `ma_decoder_seek_to_pcm_frame()` as a means to reuse a data source to play multiple -instances of the same sound simultaneously. This can be extremely inefficient depending on the type -of data source and can result in glitching due to subtle changes to the state of internal filters. -Instead, initialize multiple data sources for each instance. - - -4.1. Custom Data Sources ------------------------- -You can implement a custom data source by implementing the functions in `ma_data_source_vtable`. -Your custom object must have `ma_data_source_base` as it's first member: - - ```c - struct my_data_source - { - ma_data_source_base base; - ... - }; - ``` - -In your initialization routine, you need to call `ma_data_source_init()` in order to set up the -base object (`ma_data_source_base`): - - ```c - static ma_result my_data_source_read(ma_data_source* pDataSource, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead) - { - // Read data here. Output in the same format returned by my_data_source_get_data_format(). - } - - static ma_result my_data_source_seek(ma_data_source* pDataSource, ma_uint64 frameIndex) - { - // Seek to a specific PCM frame here. Return MA_NOT_IMPLEMENTED if seeking is not supported. - } - - static ma_result my_data_source_get_data_format(ma_data_source* pDataSource, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate, ma_channel* pChannelMap, size_t channelMapCap) - { - // Return the format of the data here. - } - - static ma_result my_data_source_get_cursor(ma_data_source* pDataSource, ma_uint64* pCursor) - { - // Retrieve the current position of the cursor here. Return MA_NOT_IMPLEMENTED and set *pCursor to 0 if there is no notion of a cursor. - } - - static ma_result my_data_source_get_length(ma_data_source* pDataSource, ma_uint64* pLength) - { - // Retrieve the length in PCM frames here. Return MA_NOT_IMPLEMENTED and set *pLength to 0 if there is no notion of a length or if the length is unknown. - } - - static ma_data_source_vtable g_my_data_source_vtable = - { - my_data_source_read, - my_data_source_seek, - my_data_source_get_data_format, - my_data_source_get_cursor, - my_data_source_get_length - }; - - ma_result my_data_source_init(my_data_source* pMyDataSource) - { - ma_result result; - ma_data_source_config baseConfig; - - baseConfig = ma_data_source_config_init(); - baseConfig.vtable = &g_my_data_source_vtable; - - result = ma_data_source_init(&baseConfig, &pMyDataSource->base); - if (result != MA_SUCCESS) { - return result; - } - - // ... do the initialization of your custom data source here ... - - return MA_SUCCESS; - } - - void my_data_source_uninit(my_data_source* pMyDataSource) - { - // ... do the uninitialization of your custom data source here ... - - // You must uninitialize the base data source. - ma_data_source_uninit(&pMyDataSource->base); - } - ``` - -Note that `ma_data_source_init()` and `ma_data_source_uninit()` are never called directly outside -of the custom data source. It's up to the custom data source itself to call these within their own -init/uninit functions. - - - -5. Engine -========= -The `ma_engine` API is a high level API for managing and mixing sounds and effect processing. The -`ma_engine` object encapsulates a resource manager and a node graph, both of which will be -explained in more detail later. - -Sounds are called `ma_sound` and are created from an engine. Sounds can be associated with a mixing -group called `ma_sound_group` which are also created from the engine. Both `ma_sound` and -`ma_sound_group` objects are nodes within the engine's node graph. - -When the engine is initialized, it will normally create a device internally. If you would rather -manage the device yourself, you can do so and just pass a pointer to it via the engine config when -you initialize the engine. You can also just use the engine without a device, which again can be -configured via the engine config. - -The most basic way to initialize the engine is with a default config, like so: - - ```c - ma_result result; - ma_engine engine; - - result = ma_engine_init(NULL, &engine); - if (result != MA_SUCCESS) { - return result; // Failed to initialize the engine. - } - ``` - -This will result in the engine initializing a playback device using the operating system's default -device. This will be sufficient for many use cases, but if you need more flexibility you'll want to -configure the engine with an engine config: - - ```c - ma_result result; - ma_engine engine; - ma_engine_config engineConfig; - - engineConfig = ma_engine_config_init(); - engineConfig.pDevice = &myDevice; - - result = ma_engine_init(&engineConfig, &engine); - if (result != MA_SUCCESS) { - return result; // Failed to initialize the engine. - } - ``` - -In the example above we're passing in a pre-initialized device. Since the caller is the one in -control of the device's data callback, it's their responsibility to manually call -`ma_engine_read_pcm_frames()` from inside their data callback: - - ```c - void playback_data_callback(ma_device* pDevice, void* pOutput, const void* pInput, ma_uint32 frameCount) - { - ma_engine_read_pcm_frames(&g_Engine, pOutput, frameCount, NULL); - } - ``` - -You can also use the engine independent of a device entirely: - - ```c - ma_result result; - ma_engine engine; - ma_engine_config engineConfig; - - engineConfig = ma_engine_config_init(); - engineConfig.noDevice = MA_TRUE; - engineConfig.channels = 2; // Must be set when not using a device. - engineConfig.sampleRate = 48000; // Must be set when not using a device. - - result = ma_engine_init(&engineConfig, &engine); - if (result != MA_SUCCESS) { - return result; // Failed to initialize the engine. - } - ``` - -Note that when you're not using a device, you must set the channel count and sample rate in the -config or else miniaudio won't know what to use (miniaudio will use the device to determine this -normally). When not using a device, you need to use `ma_engine_read_pcm_frames()` to process audio -data from the engine. This kind of setup is useful if you want to do something like offline -processing or want to use a different audio system for playback such as SDL. - -When a sound is loaded it goes through a resource manager. By default the engine will initialize a -resource manager internally, but you can also specify a pre-initialized resource manager: - - ```c - ma_result result; - ma_engine engine1; - ma_engine engine2; - ma_engine_config engineConfig; - - engineConfig = ma_engine_config_init(); - engineConfig.pResourceManager = &myResourceManager; - - ma_engine_init(&engineConfig, &engine1); - ma_engine_init(&engineConfig, &engine2); - ``` - -In this example we are initializing two engines, both of which are sharing the same resource -manager. This is especially useful for saving memory when loading the same file across multiple -engines. If you were not to use a shared resource manager, each engine instance would use their own -which would result in any sounds that are used between both engine's being loaded twice. By using -a shared resource manager, it would only be loaded once. Using multiple engine's is useful when you -need to output to multiple playback devices, such as in a local multiplayer game where each player -is using their own set of headphones. - -By default an engine will be in a started state. To make it so the engine is not automatically -started you can configure it as such: - - ```c - engineConfig.noAutoStart = MA_TRUE; - - // The engine will need to be started manually. - ma_engine_start(&engine); - - // Later on the engine can be stopped with ma_engine_stop(). - ma_engine_stop(&engine); - ``` - -The concept of starting or stopping an engine is only relevant when using the engine with a -device. Attempting to start or stop an engine that is not associated with a device will result in -`MA_INVALID_OPERATION`. - -The master volume of the engine can be controlled with `ma_engine_set_volume()` which takes a -linear scale, with 0 resulting in silence and anything above 1 resulting in amplification. If you -prefer decibel based volume control, use `ma_volume_db_to_linear()` to convert from dB to linear. - -When a sound is spatialized, it is done so relative to a listener. An engine can be configured to -have multiple listeners which can be configured via the config: - - ```c - engineConfig.listenerCount = 2; - ``` - -The maximum number of listeners is restricted to `MA_ENGINE_MAX_LISTENERS`. By default, when a -sound is spatialized, it will be done so relative to the closest listener. You can also pin a sound -to a specific listener which will be explained later. Listener's have a position, direction, cone, -and velocity (for doppler effect). A listener is referenced by an index, the meaning of which is up -to the caller (the index is 0 based and cannot go beyond the listener count, minus 1). The -position, direction and velocity are all specified in absolute terms: - - ```c - ma_engine_listener_set_position(&engine, listenerIndex, worldPosX, worldPosY, worldPosZ); - ``` - -The direction of the listener represents it's forward vector. The listener's up vector can also be -specified and defaults to +1 on the Y axis. - - ```c - ma_engine_listener_set_direction(&engine, listenerIndex, forwardX, forwardY, forwardZ); - ma_engine_listener_set_world_up(&engine, listenerIndex, 0, 1, 0); - ``` - -The engine supports directional attenuation. The listener can have a cone the controls how sound is -attenuated based on the listener's direction. When a sound is between the inner and outer cones, it -will be attenuated between 1 and the cone's outer gain: - - ```c - ma_engine_listener_set_cone(&engine, listenerIndex, innerAngleInRadians, outerAngleInRadians, outerGain); - ``` - -When a sound is inside the inner code, no directional attenuation is applied. When the sound is -outside of the outer cone, the attenuation will be set to `outerGain` in the example above. When -the sound is in between the inner and outer cones, the attenuation will be interpolated between 1 -and the outer gain. - -The engine's coordinate system follows the OpenGL coordinate system where positive X points right, -positive Y points up and negative Z points forward. - -The simplest and least flexible way to play a sound is like so: - - ```c - ma_engine_play_sound(&engine, "my_sound.wav", pGroup); - ``` - -This is a "fire and forget" style of function. The engine will manage the `ma_sound` object -internally. When the sound finishes playing, it'll be put up for recycling. For more flexibility -you'll want to initialize a sound object: - - ```c - ma_sound sound; - - result = ma_sound_init_from_file(&engine, "my_sound.wav", flags, pGroup, NULL, &sound); - if (result != MA_SUCCESS) { - return result; // Failed to load sound. - } - ``` - -Sounds need to be uninitialized with `ma_sound_uninit()`. - -The example above loads a sound from a file. If the resource manager has been disabled you will not -be able to use this function and instead you'll need to initialize a sound directly from a data -source: - - ```c - ma_sound sound; - - result = ma_sound_init_from_data_source(&engine, &dataSource, flags, pGroup, &sound); - if (result != MA_SUCCESS) { - return result; - } - ``` - -Each `ma_sound` object represents a single instance of the sound. If you want to play the same -sound multiple times at the same time, you need to initialize a separate `ma_sound` object. - -For the most flexibility when initializing sounds, use `ma_sound_init_ex()`. This uses miniaudio's -standard config/init pattern: - - ```c - ma_sound sound; - ma_sound_config soundConfig; - - soundConfig = ma_sound_config_init(); - soundConfig.pFilePath = NULL; // Set this to load from a file path. - soundConfig.pDataSource = NULL; // Set this to initialize from an existing data source. - soundConfig.pInitialAttachment = &someNodeInTheNodeGraph; - soundConfig.initialAttachmentInputBusIndex = 0; - soundConfig.channelsIn = 1; - soundConfig.channelsOut = 0; // Set to 0 to use the engine's native channel count. - - result = ma_sound_init_ex(&soundConfig, &sound); - if (result != MA_SUCCESS) { - return result; - } - ``` - -In the example above, the sound is being initialized without a file nor a data source. This is -valid, in which case the sound acts as a node in the middle of the node graph. This means you can -connect other sounds to this sound and allow it to act like a sound group. Indeed, this is exactly -what a `ma_sound_group` is. - -When loading a sound, you specify a set of flags that control how the sound is loaded and what -features are enabled for that sound. When no flags are set, the sound will be fully loaded into -memory in exactly the same format as how it's stored on the file system. The resource manager will -allocate a block of memory and then load the file directly into it. When reading audio data, it -will be decoded dynamically on the fly. In order to save processing time on the audio thread, it -might be beneficial to pre-decode the sound. You can do this with the `MA_SOUND_FLAG_DECODE` flag: - - ```c - ma_sound_init_from_file(&engine, "my_sound.wav", MA_SOUND_FLAG_DECODE, pGroup, NULL, &sound); - ``` - -By default, sounds will be loaded synchronously, meaning `ma_sound_init_*()` will not return until -the sound has been fully loaded. If this is prohibitive you can instead load sounds asynchronously -by specifying the `MA_SOUND_FLAG_ASYNC` flag: - - ```c - ma_sound_init_from_file(&engine, "my_sound.wav", MA_SOUND_FLAG_DECODE | MA_SOUND_FLAG_ASYNC, pGroup, NULL, &sound); - ``` - -This will result in `ma_sound_init_*()` returning quickly, but the sound won't yet have been fully -loaded. When you start the sound, it won't output anything until some sound is available. The sound -will start outputting audio before the sound has been fully decoded when the `MA_SOUND_FLAG_DECODE` -is specified. - -If you need to wait for an asynchronously loaded sound to be fully loaded, you can use a fence. A -fence in miniaudio is a simple synchronization mechanism which simply blocks until it's internal -counter hit's zero. You can specify a fence like so: - - ```c - ma_result result; - ma_fence fence; - ma_sound sounds[4]; - - result = ma_fence_init(&fence); - if (result != MA_SUCCESS) { - return result; - } - - // Load some sounds asynchronously. - for (int iSound = 0; iSound < 4; iSound += 1) { - ma_sound_init_from_file(&engine, mySoundFilesPaths[iSound], MA_SOUND_FLAG_DECODE | MA_SOUND_FLAG_ASYNC, pGroup, &fence, &sounds[iSound]); - } - - // ... do some other stuff here in the mean time ... - - // Wait for all sounds to finish loading. - ma_fence_wait(&fence); - ``` - -If loading the entire sound into memory is prohibitive, you can also configure the engine to stream -the audio data: - - ```c - ma_sound_init_from_file(&engine, "my_sound.wav", MA_SOUND_FLAG_STREAM, pGroup, NULL, &sound); - ``` - -When streaming sounds, 2 seconds worth of audio data is stored in memory. Although it should work -fine, it's inefficient to use streaming for short sounds. Streaming is useful for things like music -tracks in games. - -When loading a sound from a file path, the engine will reference count the file to prevent it from -being loaded if it's already in memory. When you uninitialize a sound, the reference count will be -decremented, and if it hits zero, the sound will be unloaded from memory. This reference counting -system is not used for streams. The engine will use a 64-bit hash of the file name when comparing -file paths which means there's a small chance you might encounter a name collision. If this is an -issue, you'll need to use a different name for one of the colliding file paths, or just not load -from files and instead load from a data source. - -You can use `ma_sound_init_copy()` to initialize a copy of another sound. Note, however, that this -only works for sounds that were initialized with `ma_sound_init_from_file()` and without the -`MA_SOUND_FLAG_STREAM` flag. - -When you initialize a sound, if you specify a sound group the sound will be attached to that group -automatically. If you set it to NULL, it will be automatically attached to the engine's endpoint. -If you would instead rather leave the sound unattached by default, you can specify the -`MA_SOUND_FLAG_NO_DEFAULT_ATTACHMENT` flag. This is useful if you want to set up a complex node -graph. - -Sounds are not started by default. To start a sound, use `ma_sound_start()`. Stop a sound with -`ma_sound_stop()`. - -Sounds can have their volume controlled with `ma_sound_set_volume()` in the same way as the -engine's master volume. - -Sounds support stereo panning and pitching. Set the pan with `ma_sound_set_pan()`. Setting the pan -to 0 will result in an unpanned sound. Setting it to -1 will shift everything to the left, whereas -+1 will shift it to the right. The pitch can be controlled with `ma_sound_set_pitch()`. A larger -value will result in a higher pitch. The pitch must be greater than 0. - -The engine supports 3D spatialization of sounds. By default sounds will have spatialization -enabled, but if a sound does not need to be spatialized it's best to disable it. There are two ways -to disable spatialization of a sound: - - ```c - // Disable spatialization at initialization time via a flag: - ma_sound_init_from_file(&engine, "my_sound.wav", MA_SOUND_FLAG_NO_SPATIALIZATION, NULL, NULL, &sound); - - // Dynamically disable or enable spatialization post-initialization: - ma_sound_set_spatialization_enabled(&sound, isSpatializationEnabled); - ``` - -By default sounds will be spatialized based on the closest listener. If a sound should always be -spatialized relative to a specific listener it can be pinned to one: - - ```c - ma_sound_set_pinned_listener_index(&sound, listenerIndex); - ``` - -Like listeners, sounds have a position. By default, the position of a sound is in absolute space, -but it can be changed to be relative to a listener: - - ```c - ma_sound_set_positioning(&sound, ma_positioning_relative); - ``` - -Note that relative positioning of a sound only makes sense if there is either only one listener, or -the sound is pinned to a specific listener. To set the position of a sound: - - ```c - ma_sound_set_position(&sound, posX, posY, posZ); - ``` - -The direction works the same way as a listener and represents the sound's forward direction: - - ```c - ma_sound_set_direction(&sound, forwardX, forwardY, forwardZ); - ``` - -Sound's also have a cone for controlling directional attenuation. This works exactly the same as -listeners: - - ```c - ma_sound_set_cone(&sound, innerAngleInRadians, outerAngleInRadians, outerGain); - ``` - -The velocity of a sound is used for doppler effect and can be set as such: - - ```c - ma_sound_set_velocity(&sound, velocityX, velocityY, velocityZ); - ``` - -The engine supports different attenuation models which can be configured on a per-sound basis. By -default the attenuation model is set to `ma_attenuation_model_inverse` which is the equivalent to -OpenAL's `AL_INVERSE_DISTANCE_CLAMPED`. Configure the attenuation model like so: - - ```c - ma_sound_set_attenuation_model(&sound, ma_attenuation_model_inverse); - ``` - -The supported attenuation models include the following: - - +----------------------------------+----------------------------------------------+ - | ma_attenuation_model_none | No distance attenuation. | - +----------------------------------+----------------------------------------------+ - | ma_attenuation_model_inverse | Equivalent to `AL_INVERSE_DISTANCE_CLAMPED`. | - +----------------------------------+----------------------------------------------+ - | ma_attenuation_model_linear | Linear attenuation. | - +----------------------------------+----------------------------------------------+ - | ma_attenuation_model_exponential | Exponential attenuation. | - +----------------------------------+----------------------------------------------+ - -To control how quickly a sound rolls off as it moves away from the listener, you need to configure -the rolloff: - - ```c - ma_sound_set_rolloff(&sound, rolloff); - ``` - -You can control the minimum and maximum gain to apply from spatialization: - - ```c - ma_sound_set_min_gain(&sound, minGain); - ma_sound_set_max_gain(&sound, maxGain); - ``` - -Likewise, in the calculation of attenuation, you can control the minimum and maximum distances for -the attenuation calculation. This is useful if you want to ensure sounds don't drop below a certain -volume after the listener moves further away and to have sounds play a maximum volume when the -listener is within a certain distance: - - ```c - ma_sound_set_min_distance(&sound, minDistance); - ma_sound_set_max_distance(&sound, maxDistance); - ``` - -The engine's spatialization system supports doppler effect. The doppler factor can be configure on -a per-sound basis like so: - - ```c - ma_sound_set_doppler_factor(&sound, dopplerFactor); - ``` - -You can fade sounds in and out with `ma_sound_set_fade_in_pcm_frames()` and -`ma_sound_set_fade_in_milliseconds()`. Set the volume to -1 to use the current volume as the -starting volume: - - ```c - // Fade in over 1 second. - ma_sound_set_fade_in_milliseconds(&sound, 0, 1, 1000); - - // ... sometime later ... - - // Fade out over 1 second, starting from the current volume. - ma_sound_set_fade_in_milliseconds(&sound, -1, 0, 1000); - ``` - -By default sounds will start immediately, but sometimes for timing and synchronization purposes it -can be useful to schedule a sound to start or stop: - - ```c - // Start the sound in 1 second from now. - ma_sound_set_start_time_in_pcm_frames(&sound, ma_engine_get_time_in_pcm_frames(&engine) + (ma_engine_get_sample_rate(&engine) * 1)); - - // Stop the sound in 2 seconds from now. - ma_sound_set_stop_time_in_pcm_frames(&sound, ma_engine_get_time_in_pcm_frames(&engine) + (ma_engine_get_sample_rate(&engine) * 2)); - ``` - -Note that scheduling a start time still requires an explicit call to `ma_sound_start()` before -anything will play. - -The time is specified in global time which is controlled by the engine. You can get the engine's -current time with `ma_engine_get_time_in_pcm_frames()`. The engine's global time is incremented -automatically as audio data is read, but it can be reset with `ma_engine_set_time_in_pcm_frames()` -in case it needs to be resynchronized for some reason. - -To determine whether or not a sound is currently playing, use `ma_sound_is_playing()`. This will -take the scheduled start and stop times into account. - -Whether or not a sound should loop can be controlled with `ma_sound_set_looping()`. Sounds will not -be looping by default. Use `ma_sound_is_looping()` to determine whether or not a sound is looping. - -Use `ma_sound_at_end()` to determine whether or not a sound is currently at the end. For a looping -sound this should never return true. Alternatively, you can configure a callback that will be fired -when the sound reaches the end. Note that the callback is fired from the audio thread which means -you cannot be uninitializing sound from the callback. To set the callback you can use -`ma_sound_set_end_callback()`. Alternatively, if you're using `ma_sound_init_ex()`, you can pass it -into the config like so: - - ```c - soundConfig.endCallback = my_end_callback; - soundConfig.pEndCallbackUserData = pMyEndCallbackUserData; - ``` - -The end callback is declared like so: - - ```c - void my_end_callback(void* pUserData, ma_sound* pSound) - { - ... - } - ``` - -Internally a sound wraps around a data source. Some APIs exist to control the underlying data -source, mainly for convenience: - - ```c - ma_sound_seek_to_pcm_frame(&sound, frameIndex); - ma_sound_get_data_format(&sound, &format, &channels, &sampleRate, pChannelMap, channelMapCapacity); - ma_sound_get_cursor_in_pcm_frames(&sound, &cursor); - ma_sound_get_length_in_pcm_frames(&sound, &length); - ``` - -Sound groups have the same API as sounds, only they are called `ma_sound_group`, and since they do -not have any notion of a data source, anything relating to a data source is unavailable. - -Internally, sound data is loaded via the `ma_decoder` API which means by default it only supports -file formats that have built-in support in miniaudio. You can extend this to support any kind of -file format through the use of custom decoders. To do this you'll need to use a self-managed -resource manager and configure it appropriately. See the "Resource Management" section below for -details on how to set this up. - - -6. Resource Management -====================== -Many programs will want to manage sound resources for things such as reference counting and -streaming. This is supported by miniaudio via the `ma_resource_manager` API. - -The resource manager is mainly responsible for the following: - - * Loading of sound files into memory with reference counting. - * Streaming of sound data. - -When loading a sound file, the resource manager will give you back a `ma_data_source` compatible -object called `ma_resource_manager_data_source`. This object can be passed into any -`ma_data_source` API which is how you can read and seek audio data. When loading a sound file, you -specify whether or not you want the sound to be fully loaded into memory (and optionally -pre-decoded) or streamed. When loading into memory, you can also specify whether or not you want -the data to be loaded asynchronously. - -The example below is how you can initialize a resource manager using it's default configuration: - - ```c - ma_resource_manager_config config; - ma_resource_manager resourceManager; - - config = ma_resource_manager_config_init(); - result = ma_resource_manager_init(&config, &resourceManager); - if (result != MA_SUCCESS) { - ma_device_uninit(&device); - printf("Failed to initialize the resource manager."); - return -1; - } - ``` - -You can configure the format, channels and sample rate of the decoded audio data. By default it -will use the file's native data format, but you can configure it to use a consistent format. This -is useful for offloading the cost of data conversion to load time rather than dynamically -converting at mixing time. To do this, you configure the decoded format, channels and sample rate -like the code below: - - ```c - config = ma_resource_manager_config_init(); - config.decodedFormat = device.playback.format; - config.decodedChannels = device.playback.channels; - config.decodedSampleRate = device.sampleRate; - ``` - -In the code above, the resource manager will be configured so that any decoded audio data will be -pre-converted at load time to the device's native data format. If instead you used defaults and -the data format of the file did not match the device's data format, you would need to convert the -data at mixing time which may be prohibitive in high-performance and large scale scenarios like -games. - -Internally the resource manager uses the `ma_decoder` API to load sounds. This means by default it -only supports decoders that are built into miniaudio. It's possible to support additional encoding -formats through the use of custom decoders. To do so, pass in your `ma_decoding_backend_vtable` -vtables into the resource manager config: - - ```c - ma_decoding_backend_vtable* pCustomBackendVTables[] = - { - &g_ma_decoding_backend_vtable_libvorbis, - &g_ma_decoding_backend_vtable_libopus - }; - - ... - - resourceManagerConfig.ppCustomDecodingBackendVTables = pCustomBackendVTables; - resourceManagerConfig.customDecodingBackendCount = sizeof(pCustomBackendVTables) / sizeof(pCustomBackendVTables[0]); - resourceManagerConfig.pCustomDecodingBackendUserData = NULL; - ``` - -This system can allow you to support any kind of file format. See the "Decoding" section for -details on how to implement custom decoders. The miniaudio repository includes examples for Opus -via libopus and libopusfile and Vorbis via libvorbis and libvorbisfile. - -Asynchronicity is achieved via a job system. When an operation needs to be performed, such as the -decoding of a page, a job will be posted to a queue which will then be processed by a job thread. -By default there will be only one job thread running, but this can be configured, like so: - - ```c - config = ma_resource_manager_config_init(); - config.jobThreadCount = MY_JOB_THREAD_COUNT; - ``` - -By default job threads are managed internally by the resource manager, however you can also self -manage your job threads if, for example, you want to integrate the job processing into your -existing job infrastructure, or if you simply don't like the way the resource manager does it. To -do this, just set the job thread count to 0 and process jobs manually. To process jobs, you first -need to retrieve a job using `ma_resource_manager_next_job()` and then process it using -`ma_job_process()`: - - ```c - config = ma_resource_manager_config_init(); - config.jobThreadCount = 0; // Don't manage any job threads internally. - config.flags = MA_RESOURCE_MANAGER_FLAG_NON_BLOCKING; // Optional. Makes `ma_resource_manager_next_job()` non-blocking. - - // ... Initialize your custom job threads ... - - void my_custom_job_thread(...) - { - for (;;) { - ma_job job; - ma_result result = ma_resource_manager_next_job(pMyResourceManager, &job); - if (result != MA_SUCCESS) { - if (result == MA_NO_DATA_AVAILABLE) { - // No jobs are available. Keep going. Will only get this if the resource manager was initialized - // with MA_RESOURCE_MANAGER_FLAG_NON_BLOCKING. - continue; - } else if (result == MA_CANCELLED) { - // MA_JOB_TYPE_QUIT was posted. Exit. - break; - } else { - // Some other error occurred. - break; - } - } - - ma_job_process(&job); - } - } - ``` - -In the example above, the `MA_JOB_TYPE_QUIT` event is the used as the termination -indicator, but you can use whatever you would like to terminate the thread. The call to -`ma_resource_manager_next_job()` is blocking by default, but can be configured to be non-blocking -by initializing the resource manager with the `MA_RESOURCE_MANAGER_FLAG_NON_BLOCKING` configuration -flag. Note that the `MA_JOB_TYPE_QUIT` will never be removed from the job queue. This -is to give every thread the opportunity to catch the event and terminate naturally. - -When loading a file, it's sometimes convenient to be able to customize how files are opened and -read instead of using standard `fopen()`, `fclose()`, etc. which is what miniaudio will use by -default. This can be done by setting `pVFS` member of the resource manager's config: - - ```c - // Initialize your custom VFS object. See documentation for VFS for information on how to do this. - my_custom_vfs vfs = my_custom_vfs_init(); - - config = ma_resource_manager_config_init(); - config.pVFS = &vfs; - ``` - -This is particularly useful in programs like games where you want to read straight from an archive -rather than the normal file system. If you do not specify a custom VFS, the resource manager will -use the operating system's normal file operations. - -To load a sound file and create a data source, call `ma_resource_manager_data_source_init()`. When -loading a sound you need to specify the file path and options for how the sounds should be loaded. -By default a sound will be loaded synchronously. The returned data source is owned by the caller -which means the caller is responsible for the allocation and freeing of the data source. Below is -an example for initializing a data source: - - ```c - ma_resource_manager_data_source dataSource; - ma_result result = ma_resource_manager_data_source_init(pResourceManager, pFilePath, flags, &dataSource); - if (result != MA_SUCCESS) { - // Error. - } - - // ... - - // A ma_resource_manager_data_source object is compatible with the `ma_data_source` API. To read data, just call - // the `ma_data_source_read_pcm_frames()` like you would with any normal data source. - result = ma_data_source_read_pcm_frames(&dataSource, pDecodedData, frameCount, &framesRead); - if (result != MA_SUCCESS) { - // Failed to read PCM frames. - } - - // ... - - ma_resource_manager_data_source_uninit(&dataSource); - ``` - -The `flags` parameter specifies how you want to perform loading of the sound file. It can be a -combination of the following flags: - - ``` - MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_STREAM - MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_DECODE - MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_ASYNC - MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_WAIT_INIT - MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_LOOPING - ``` - -When no flags are specified (set to 0), the sound will be fully loaded into memory, but not -decoded, meaning the raw file data will be stored in memory, and then dynamically decoded when -`ma_data_source_read_pcm_frames()` is called. To instead decode the audio data before storing it in -memory, use the `MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_DECODE` flag. By default, the sound file will -be loaded synchronously, meaning `ma_resource_manager_data_source_init()` will only return after -the entire file has been loaded. This is good for simplicity, but can be prohibitively slow. You -can instead load the sound asynchronously using the `MA_RESOURCE_MANAGER_DATA_SOURCE_ASYNC` flag. -This will result in `ma_resource_manager_data_source_init()` returning quickly, but no data will be -returned by `ma_data_source_read_pcm_frames()` until some data is available. When no data is -available because the asynchronous decoding hasn't caught up, `MA_BUSY` will be returned by -`ma_data_source_read_pcm_frames()`. - -For large sounds, it's often prohibitive to store the entire file in memory. To mitigate this, you -can instead stream audio data which you can do by specifying the -`MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_STREAM` flag. When streaming, data will be decoded in 1 -second pages. When a new page needs to be decoded, a job will be posted to the job queue and then -subsequently processed in a job thread. - -The `MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_LOOPING` flag can be used so that the sound will loop -when it reaches the end by default. It's recommended you use this flag when you want to have a -looping streaming sound. If you try loading a very short sound as a stream, you will get a glitch. -This is because the resource manager needs to pre-fill the initial buffer at initialization time, -and if you don't specify the `MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_LOOPING` flag, the resource -manager will assume the sound is not looping and will stop filling the buffer when it reaches the -end, therefore resulting in a discontinuous buffer. - -For in-memory sounds, reference counting is used to ensure the data is loaded only once. This means -multiple calls to `ma_resource_manager_data_source_init()` with the same file path will result in -the file data only being loaded once. Each call to `ma_resource_manager_data_source_init()` must be -matched up with a call to `ma_resource_manager_data_source_uninit()`. Sometimes it can be useful -for a program to register self-managed raw audio data and associate it with a file path. Use the -`ma_resource_manager_register_*()` and `ma_resource_manager_unregister_*()` APIs to do this. -`ma_resource_manager_register_decoded_data()` is used to associate a pointer to raw, self-managed -decoded audio data in the specified data format with the specified name. Likewise, -`ma_resource_manager_register_encoded_data()` is used to associate a pointer to raw self-managed -encoded audio data (the raw file data) with the specified name. Note that these names need not be -actual file paths. When `ma_resource_manager_data_source_init()` is called (without the -`MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_STREAM` flag), the resource manager will look for these -explicitly registered data buffers and, if found, will use it as the backing data for the data -source. Note that the resource manager does *not* make a copy of this data so it is up to the -caller to ensure the pointer stays valid for its lifetime. Use -`ma_resource_manager_unregister_data()` to unregister the self-managed data. You can also use -`ma_resource_manager_register_file()` and `ma_resource_manager_unregister_file()` to register and -unregister a file. It does not make sense to use the `MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_STREAM` -flag with a self-managed data pointer. - - -6.1. Asynchronous Loading and Synchronization ---------------------------------------------- -When loading asynchronously, it can be useful to poll whether or not loading has finished. Use -`ma_resource_manager_data_source_result()` to determine this. For in-memory sounds, this will -return `MA_SUCCESS` when the file has been *entirely* decoded. If the sound is still being decoded, -`MA_BUSY` will be returned. Otherwise, some other error code will be returned if the sound failed -to load. For streaming data sources, `MA_SUCCESS` will be returned when the first page has been -decoded and the sound is ready to be played. If the first page is still being decoded, `MA_BUSY` -will be returned. Otherwise, some other error code will be returned if the sound failed to load. - -In addition to polling, you can also use a simple synchronization object called a "fence" to wait -for asynchronously loaded sounds to finish. This is called `ma_fence`. The advantage to using a -fence is that it can be used to wait for a group of sounds to finish loading rather than waiting -for sounds on an individual basis. There are two stages to loading a sound: - - * Initialization of the internal decoder; and - * Completion of decoding of the file (the file is fully decoded) - -You can specify separate fences for each of the different stages. Waiting for the initialization -of the internal decoder is important for when you need to know the sample format, channels and -sample rate of the file. - -The example below shows how you could use a fence when loading a number of sounds: - - ```c - // This fence will be released when all sounds are finished loading entirely. - ma_fence fence; - ma_fence_init(&fence); - - // This will be passed into the initialization routine for each sound. - ma_resource_manager_pipeline_notifications notifications = ma_resource_manager_pipeline_notifications_init(); - notifications.done.pFence = &fence; - - // Now load a bunch of sounds: - for (iSound = 0; iSound < soundCount; iSound += 1) { - ma_resource_manager_data_source_init(pResourceManager, pSoundFilePaths[iSound], flags, ¬ifications, &pSoundSources[iSound]); - } - - // ... DO SOMETHING ELSE WHILE SOUNDS ARE LOADING ... - - // Wait for loading of sounds to finish. - ma_fence_wait(&fence); - ``` - -In the example above we used a fence for waiting until the entire file has been fully decoded. If -you only need to wait for the initialization of the internal decoder to complete, you can use the -`init` member of the `ma_resource_manager_pipeline_notifications` object: - - ```c - notifications.init.pFence = &fence; - ``` - -If a fence is not appropriate for your situation, you can instead use a callback that is fired on -an individual sound basis. This is done in a very similar way to fences: - - ```c - typedef struct - { - ma_async_notification_callbacks cb; - void* pMyData; - } my_notification; - - void my_notification_callback(ma_async_notification* pNotification) - { - my_notification* pMyNotification = (my_notification*)pNotification; - - // Do something in response to the sound finishing loading. - } - - ... - - my_notification myCallback; - myCallback.cb.onSignal = my_notification_callback; - myCallback.pMyData = pMyData; - - ma_resource_manager_pipeline_notifications notifications = ma_resource_manager_pipeline_notifications_init(); - notifications.done.pNotification = &myCallback; - - ma_resource_manager_data_source_init(pResourceManager, "my_sound.wav", flags, ¬ifications, &mySound); - ``` - -In the example above we just extend the `ma_async_notification_callbacks` object and pass an -instantiation into the `ma_resource_manager_pipeline_notifications` in the same way as we did with -the fence, only we set `pNotification` instead of `pFence`. You can set both of these at the same -time and they should both work as expected. If using the `pNotification` system, you need to ensure -your `ma_async_notification_callbacks` object stays valid. - - - -6.2. Resource Manager Implementation Details --------------------------------------------- -Resources are managed in two main ways: - - * By storing the entire sound inside an in-memory buffer (referred to as a data buffer) - * By streaming audio data on the fly (referred to as a data stream) - -A resource managed data source (`ma_resource_manager_data_source`) encapsulates a data buffer or -data stream, depending on whether or not the data source was initialized with the -`MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_STREAM` flag. If so, it will make use of a -`ma_resource_manager_data_stream` object. Otherwise it will use a `ma_resource_manager_data_buffer` -object. Both of these objects are data sources which means they can be used with any -`ma_data_source_*()` API. - -Another major feature of the resource manager is the ability to asynchronously decode audio files. -This relieves the audio thread of time-consuming decoding which can negatively affect scalability -due to the audio thread needing to complete it's work extremely quickly to avoid glitching. -Asynchronous decoding is achieved through a job system. There is a central multi-producer, -multi-consumer, fixed-capacity job queue. When some asynchronous work needs to be done, a job is -posted to the queue which is then read by a job thread. The number of job threads can be -configured for improved scalability, and job threads can all run in parallel without needing to -worry about the order of execution (how this is achieved is explained below). - -When a sound is being loaded asynchronously, playback can begin before the sound has been fully -decoded. This enables the application to start playback of the sound quickly, while at the same -time allowing to resource manager to keep loading in the background. Since there may be less -threads than the number of sounds being loaded at a given time, a simple scheduling system is used -to keep decoding time balanced and fair. The resource manager solves this by splitting decoding -into chunks called pages. By default, each page is 1 second long. When a page has been decoded, a -new job will be posted to start decoding the next page. By dividing up decoding into pages, an -individual sound shouldn't ever delay every other sound from having their first page decoded. Of -course, when loading many sounds at the same time, there will always be an amount of time required -to process jobs in the queue so in heavy load situations there will still be some delay. To -determine if a data source is ready to have some frames read, use -`ma_resource_manager_data_source_get_available_frames()`. This will return the number of frames -available starting from the current position. - - -6.2.1. Job Queue ----------------- -The resource manager uses a job queue which is multi-producer, multi-consumer, and fixed-capacity. -This job queue is not currently lock-free, and instead uses a spinlock to achieve thread-safety. -Only a fixed number of jobs can be allocated and inserted into the queue which is done through a -lock-free data structure for allocating an index into a fixed sized array, with reference counting -for mitigation of the ABA problem. The reference count is 32-bit. - -For many types of jobs it's important that they execute in a specific order. In these cases, jobs -are executed serially. For the resource manager, serial execution of jobs is only required on a -per-object basis (per data buffer or per data stream). Each of these objects stores an execution -counter. When a job is posted it is associated with an execution counter. When the job is -processed, it checks if the execution counter of the job equals the execution counter of the -owning object and if so, processes the job. If the counters are not equal, the job will be posted -back onto the job queue for later processing. When the job finishes processing the execution order -of the main object is incremented. This system means the no matter how many job threads are -executing, decoding of an individual sound will always get processed serially. The advantage to -having multiple threads comes into play when loading multiple sounds at the same time. - -The resource manager's job queue is not 100% lock-free and will use a spinlock to achieve -thread-safety for a very small section of code. This is only relevant when the resource manager -uses more than one job thread. If only using a single job thread, which is the default, the -lock should never actually wait in practice. The amount of time spent locking should be quite -short, but it's something to be aware of for those who have pedantic lock-free requirements and -need to use more than one job thread. There are plans to remove this lock in a future version. - -In addition, posting a job will release a semaphore, which on Win32 is implemented with -`ReleaseSemaphore` and on POSIX platforms via a condition variable: - - ```c - pthread_mutex_lock(&pSemaphore->lock); - { - pSemaphore->value += 1; - pthread_cond_signal(&pSemaphore->cond); - } - pthread_mutex_unlock(&pSemaphore->lock); - ``` - -Again, this is relevant for those with strict lock-free requirements in the audio thread. To avoid -this, you can use non-blocking mode (via the `MA_JOB_QUEUE_FLAG_NON_BLOCKING` -flag) and implement your own job processing routine (see the "Resource Manager" section above for -details on how to do this). - - - -6.2.2. Data Buffers -------------------- -When the `MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_STREAM` flag is excluded at initialization time, the -resource manager will try to load the data into an in-memory data buffer. Before doing so, however, -it will first check if the specified file is already loaded. If so, it will increment a reference -counter and just use the already loaded data. This saves both time and memory. When the data buffer -is uninitialized, the reference counter will be decremented. If the counter hits zero, the file -will be unloaded. This is a detail to keep in mind because it could result in excessive loading and -unloading of a sound. For example, the following sequence will result in a file be loaded twice, -once after the other: - - ```c - ma_resource_manager_data_source_init(pResourceManager, "my_file", ..., &myDataBuffer0); // Refcount = 1. Initial load. - ma_resource_manager_data_source_uninit(&myDataBuffer0); // Refcount = 0. Unloaded. - - ma_resource_manager_data_source_init(pResourceManager, "my_file", ..., &myDataBuffer1); // Refcount = 1. Reloaded because previous uninit() unloaded it. - ma_resource_manager_data_source_uninit(&myDataBuffer1); // Refcount = 0. Unloaded. - ``` - -A binary search tree (BST) is used for storing data buffers as it has good balance between -efficiency and simplicity. The key of the BST is a 64-bit hash of the file path that was passed -into `ma_resource_manager_data_source_init()`. The advantage of using a hash is that it saves -memory over storing the entire path, has faster comparisons, and results in a mostly balanced BST -due to the random nature of the hash. The disadvantages are that file names are case-sensitive and -there's a small chance of name collisions. If case-sensitivity is an issue, you should normalize -your file names to upper- or lower-case before initializing your data sources. If name collisions -become an issue, you'll need to change the name of one of the colliding names or just not use the -resource manager. - -When a sound file has not already been loaded and the `MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_ASYNC` -flag is excluded, the file will be decoded synchronously by the calling thread. There are two -options for controlling how the audio is stored in the data buffer - encoded or decoded. When the -`MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_DECODE` option is excluded, the raw file data will be stored -in memory. Otherwise the sound will be decoded before storing it in memory. Synchronous loading is -a very simple and standard process of simply adding an item to the BST, allocating a block of -memory and then decoding (if `MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_DECODE` is specified). - -When the `MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_ASYNC` flag is specified, loading of the data buffer -is done asynchronously. In this case, a job is posted to the queue to start loading and then the -function immediately returns, setting an internal result code to `MA_BUSY`. This result code is -returned when the program calls `ma_resource_manager_data_source_result()`. When decoding has fully -completed `MA_SUCCESS` will be returned. This can be used to know if loading has fully completed. - -When loading asynchronously, a single job is posted to the queue of the type -`MA_JOB_TYPE_RESOURCE_MANAGER_LOAD_DATA_BUFFER_NODE`. This involves making a copy of the file path and -associating it with job. When the job is processed by the job thread, it will first load the file -using the VFS associated with the resource manager. When using a custom VFS, it's important that it -be completely thread-safe because it will be used from one or more job threads at the same time. -Individual files should only ever be accessed by one thread at a time, however. After opening the -file via the VFS, the job will determine whether or not the file is being decoded. If not, it -simply allocates a block of memory and loads the raw file contents into it and returns. On the -other hand, when the file is being decoded, it will first allocate a decoder on the heap and -initialize it. Then it will check if the length of the file is known. If so it will allocate a -block of memory to store the decoded output and initialize it to silence. If the size is unknown, -it will allocate room for one page. After memory has been allocated, the first page will be -decoded. If the sound is shorter than a page, the result code will be set to `MA_SUCCESS` and the -completion event will be signalled and loading is now complete. If, however, there is more to -decode, a job with the code `MA_JOB_TYPE_RESOURCE_MANAGER_PAGE_DATA_BUFFER_NODE` is posted. This job -will decode the next page and perform the same process if it reaches the end. If there is more to -decode, the job will post another `MA_JOB_TYPE_RESOURCE_MANAGER_PAGE_DATA_BUFFER_NODE` job which will -keep on happening until the sound has been fully decoded. For sounds of an unknown length, each -page will be linked together as a linked list. Internally this is implemented via the -`ma_paged_audio_buffer` object. - - -6.2.3. Data Streams -------------------- -Data streams only ever store two pages worth of data for each instance. They are most useful for -large sounds like music tracks in games that would consume too much memory if fully decoded in -memory. After every frame from a page has been read, a job will be posted to load the next page -which is done from the VFS. - -For data streams, the `MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_ASYNC` flag will determine whether or -not initialization of the data source waits until the two pages have been decoded. When unset, -`ma_resource_manager_data_source_init()` will wait until the two pages have been loaded, otherwise -it will return immediately. - -When frames are read from a data stream using `ma_resource_manager_data_source_read_pcm_frames()`, -`MA_BUSY` will be returned if there are no frames available. If there are some frames available, -but less than the number requested, `MA_SUCCESS` will be returned, but the actual number of frames -read will be less than the number requested. Due to the asynchronous nature of data streams, -seeking is also asynchronous. If the data stream is in the middle of a seek, `MA_BUSY` will be -returned when trying to read frames. - -When `ma_resource_manager_data_source_read_pcm_frames()` results in a page getting fully consumed -a job is posted to load the next page. This will be posted from the same thread that called -`ma_resource_manager_data_source_read_pcm_frames()`. - -Data streams are uninitialized by posting a job to the queue, but the function won't return until -that job has been processed. The reason for this is that the caller owns the data stream object and -therefore miniaudio needs to ensure everything completes before handing back control to the caller. -Also, if the data stream is uninitialized while pages are in the middle of decoding, they must -complete before destroying any underlying object and the job system handles this cleanly. - -Note that when a new page needs to be loaded, a job will be posted to the resource manager's job -thread from the audio thread. You must keep in mind the details mentioned in the "Job Queue" -section above regarding locking when posting an event if you require a strictly lock-free audio -thread. - - - -7. Node Graph -============= -miniaudio's routing infrastructure follows a node graph paradigm. The idea is that you create a -node whose outputs are attached to inputs of another node, thereby creating a graph. There are -different types of nodes, with each node in the graph processing input data to produce output, -which is then fed through the chain. Each node in the graph can apply their own custom effects. At -the start of the graph will usually be one or more data source nodes which have no inputs and -instead pull their data from a data source. At the end of the graph is an endpoint which represents -the end of the chain and is where the final output is ultimately extracted from. - -Each node has a number of input buses and a number of output buses. An output bus from a node is -attached to an input bus of another. Multiple nodes can connect their output buses to another -node's input bus, in which case their outputs will be mixed before processing by the node. Below is -a diagram that illustrates a hypothetical node graph setup: - - ``` - >>>>>>>>>>>>>>>>>>>>>>>>>>>>>> Data flows left to right >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> - - +---------------+ +-----------------+ - | Data Source 1 =----+ +----------+ +----= Low Pass Filter =----+ - +---------------+ | | =----+ +-----------------+ | +----------+ - +----= Splitter | +----= ENDPOINT | - +---------------+ | | =----+ +-----------------+ | +----------+ - | Data Source 2 =----+ +----------+ +----= Echo / Delay =----+ - +---------------+ +-----------------+ - ``` - -In the above graph, it starts with two data sources whose outputs are attached to the input of a -splitter node. It's at this point that the two data sources are mixed. After mixing, the splitter -performs it's processing routine and produces two outputs which is simply a duplication of the -input stream. One output is attached to a low pass filter, whereas the other output is attached to -a echo/delay. The outputs of the low pass filter and the echo are attached to the endpoint, and -since they're both connected to the same input bus, they'll be mixed. - -Each input bus must be configured to accept the same number of channels, but the number of channels -used by input buses can be different to the number of channels for output buses in which case -miniaudio will automatically convert the input data to the output channel count before processing. -The number of channels of an output bus of one node must match the channel count of the input bus -it's attached to. The channel counts cannot be changed after the node has been initialized. If you -attempt to attach an output bus to an input bus with a different channel count, attachment will -fail. - -To use a node graph, you first need to initialize a `ma_node_graph` object. This is essentially a -container around the entire graph. The `ma_node_graph` object is required for some thread-safety -issues which will be explained later. A `ma_node_graph` object is initialized using miniaudio's -standard config/init system: - - ```c - ma_node_graph_config nodeGraphConfig = ma_node_graph_config_init(myChannelCount); - - result = ma_node_graph_init(&nodeGraphConfig, NULL, &nodeGraph); // Second parameter is a pointer to allocation callbacks. - if (result != MA_SUCCESS) { - // Failed to initialize node graph. - } - ``` - -When you initialize the node graph, you're specifying the channel count of the endpoint. The -endpoint is a special node which has one input bus and one output bus, both of which have the -same channel count, which is specified in the config. Any nodes that connect directly to the -endpoint must be configured such that their output buses have the same channel count. When you read -audio data from the node graph, it'll have the channel count you specified in the config. To read -data from the graph: - - ```c - ma_uint32 framesRead; - result = ma_node_graph_read_pcm_frames(&nodeGraph, pFramesOut, frameCount, &framesRead); - if (result != MA_SUCCESS) { - // Failed to read data from the node graph. - } - ``` - -When you read audio data, miniaudio starts at the node graph's endpoint node which then pulls in -data from its input attachments, which in turn recursively pull in data from their inputs, and so -on. At the start of the graph there will be some kind of data source node which will have zero -inputs and will instead read directly from a data source. The base nodes don't literally need to -read from a `ma_data_source` object, but they will always have some kind of underlying object that -sources some kind of audio. The `ma_data_source_node` node can be used to read from a -`ma_data_source`. Data is always in floating-point format and in the number of channels you -specified when the graph was initialized. The sample rate is defined by the underlying data sources. -It's up to you to ensure they use a consistent and appropriate sample rate. - -The `ma_node` API is designed to allow custom nodes to be implemented with relative ease, but -miniaudio includes a few stock nodes for common functionality. This is how you would initialize a -node which reads directly from a data source (`ma_data_source_node`) which is an example of one -of the stock nodes that comes with miniaudio: - - ```c - ma_data_source_node_config config = ma_data_source_node_config_init(pMyDataSource); - - ma_data_source_node dataSourceNode; - result = ma_data_source_node_init(&nodeGraph, &config, NULL, &dataSourceNode); - if (result != MA_SUCCESS) { - // Failed to create data source node. - } - ``` - -The data source node will use the output channel count to determine the channel count of the output -bus. There will be 1 output bus and 0 input buses (data will be drawn directly from the data -source). The data source must output to floating-point (`ma_format_f32`) or else an error will be -returned from `ma_data_source_node_init()`. - -By default the node will not be attached to the graph. To do so, use `ma_node_attach_output_bus()`: - - ```c - result = ma_node_attach_output_bus(&dataSourceNode, 0, ma_node_graph_get_endpoint(&nodeGraph), 0); - if (result != MA_SUCCESS) { - // Failed to attach node. - } - ``` - -The code above connects the data source node directly to the endpoint. Since the data source node -has only a single output bus, the index will always be 0. Likewise, the endpoint only has a single -input bus which means the input bus index will also always be 0. - -To detach a specific output bus, use `ma_node_detach_output_bus()`. To detach all output buses, use -`ma_node_detach_all_output_buses()`. If you want to just move the output bus from one attachment to -another, you do not need to detach first. You can just call `ma_node_attach_output_bus()` and it'll -deal with it for you. - -Less frequently you may want to create a specialized node. This will be a node where you implement -your own processing callback to apply a custom effect of some kind. This is similar to initializing -one of the stock node types, only this time you need to specify a pointer to a vtable containing a -pointer to the processing function and the number of input and output buses. Example: - - ```c - static void my_custom_node_process_pcm_frames(ma_node* pNode, const float** ppFramesIn, ma_uint32* pFrameCountIn, float** ppFramesOut, ma_uint32* pFrameCountOut) - { - // Do some processing of ppFramesIn (one stream of audio data per input bus) - const float* pFramesIn_0 = ppFramesIn[0]; // Input bus @ index 0. - const float* pFramesIn_1 = ppFramesIn[1]; // Input bus @ index 1. - float* pFramesOut_0 = ppFramesOut[0]; // Output bus @ index 0. - - // Do some processing. On input, `pFrameCountIn` will be the number of input frames in each - // buffer in `ppFramesIn` and `pFrameCountOut` will be the capacity of each of the buffers - // in `ppFramesOut`. On output, `pFrameCountIn` should be set to the number of input frames - // your node consumed and `pFrameCountOut` should be set the number of output frames that - // were produced. - // - // You should process as many frames as you can. If your effect consumes input frames at the - // same rate as output frames (always the case, unless you're doing resampling), you need - // only look at `ppFramesOut` and process that exact number of frames. If you're doing - // resampling, you'll need to be sure to set both `pFrameCountIn` and `pFrameCountOut` - // properly. - } - - static ma_node_vtable my_custom_node_vtable = - { - my_custom_node_process_pcm_frames, // The function that will be called to process your custom node. This is where you'd implement your effect processing. - NULL, // Optional. A callback for calculating the number of input frames that are required to process a specified number of output frames. - 2, // 2 input buses. - 1, // 1 output bus. - 0 // Default flags. - }; - - ... - - // Each bus needs to have a channel count specified. To do this you need to specify the channel - // counts in an array and then pass that into the node config. - ma_uint32 inputChannels[2]; // Equal in size to the number of input channels specified in the vtable. - ma_uint32 outputChannels[1]; // Equal in size to the number of output channels specified in the vtable. - - inputChannels[0] = channelsIn; - inputChannels[1] = channelsIn; - outputChannels[0] = channelsOut; - - ma_node_config nodeConfig = ma_node_config_init(); - nodeConfig.vtable = &my_custom_node_vtable; - nodeConfig.pInputChannels = inputChannels; - nodeConfig.pOutputChannels = outputChannels; - - ma_node_base node; - result = ma_node_init(&nodeGraph, &nodeConfig, NULL, &node); - if (result != MA_SUCCESS) { - // Failed to initialize node. - } - ``` - -When initializing a custom node, as in the code above, you'll normally just place your vtable in -static space. The number of input and output buses are specified as part of the vtable. If you need -a variable number of buses on a per-node bases, the vtable should have the relevant bus count set -to `MA_NODE_BUS_COUNT_UNKNOWN`. In this case, the bus count should be set in the node config: - - ```c - static ma_node_vtable my_custom_node_vtable = - { - my_custom_node_process_pcm_frames, // The function that will be called process your custom node. This is where you'd implement your effect processing. - NULL, // Optional. A callback for calculating the number of input frames that are required to process a specified number of output frames. - MA_NODE_BUS_COUNT_UNKNOWN, // The number of input buses is determined on a per-node basis. - 1, // 1 output bus. - 0 // Default flags. - }; - - ... - - ma_node_config nodeConfig = ma_node_config_init(); - nodeConfig.vtable = &my_custom_node_vtable; - nodeConfig.inputBusCount = myBusCount; // <-- Since the vtable specifies MA_NODE_BUS_COUNT_UNKNOWN, the input bus count should be set here. - nodeConfig.pInputChannels = inputChannels; // <-- Make sure there are nodeConfig.inputBusCount elements in this array. - nodeConfig.pOutputChannels = outputChannels; // <-- The vtable specifies 1 output bus, so there must be 1 element in this array. - ``` - -In the above example it's important to never set the `inputBusCount` and `outputBusCount` members -to anything other than their defaults if the vtable specifies an explicit count. They can only be -set if the vtable specifies MA_NODE_BUS_COUNT_UNKNOWN in the relevant bus count. - -Most often you'll want to create a structure to encapsulate your node with some extra data. You -need to make sure the `ma_node_base` object is your first member of the structure: - - ```c - typedef struct - { - ma_node_base base; // <-- Make sure this is always the first member. - float someCustomData; - } my_custom_node; - ``` - -By doing this, your object will be compatible with all `ma_node` APIs and you can attach it to the -graph just like any other node. - -In the custom processing callback (`my_custom_node_process_pcm_frames()` in the example above), the -number of channels for each bus is what was specified by the config when the node was initialized -with `ma_node_init()`. In addition, all attachments to each of the input buses will have been -pre-mixed by miniaudio. The config allows you to specify different channel counts for each -individual input and output bus. It's up to the effect to handle it appropriate, and if it can't, -return an error in it's initialization routine. - -Custom nodes can be assigned some flags to describe their behaviour. These are set via the vtable -and include the following: - - +-----------------------------------------+---------------------------------------------------+ - | Flag Name | Description | - +-----------------------------------------+---------------------------------------------------+ - | MA_NODE_FLAG_PASSTHROUGH | Useful for nodes that do not do any kind of audio | - | | processing, but are instead used for tracking | - | | time, handling events, etc. Also used by the | - | | internal endpoint node. It reads directly from | - | | the input bus to the output bus. Nodes with this | - | | flag must have exactly 1 input bus and 1 output | - | | bus, and both buses must have the same channel | - | | counts. | - +-----------------------------------------+---------------------------------------------------+ - | MA_NODE_FLAG_CONTINUOUS_PROCESSING | Causes the processing callback to be called even | - | | when no data is available to be read from input | - | | attachments. When a node has at least one input | - | | bus, but there are no inputs attached or the | - | | inputs do not deliver any data, the node's | - | | processing callback will not get fired. This flag | - | | will make it so the callback is always fired | - | | regardless of whether or not any input data is | - | | received. This is useful for effects like | - | | echos where there will be a tail of audio data | - | | that still needs to be processed even when the | - | | original data sources have reached their ends. It | - | | may also be useful for nodes that must always | - | | have their processing callback fired when there | - | | are no inputs attached. | - +-----------------------------------------+---------------------------------------------------+ - | MA_NODE_FLAG_ALLOW_NULL_INPUT | Used in conjunction with | - | | `MA_NODE_FLAG_CONTINUOUS_PROCESSING`. When this | - | | is set, the `ppFramesIn` parameter of the | - | | processing callback will be set to NULL when | - | | there are no input frames are available. When | - | | this is unset, silence will be posted to the | - | | processing callback. | - +-----------------------------------------+---------------------------------------------------+ - | MA_NODE_FLAG_DIFFERENT_PROCESSING_RATES | Used to tell miniaudio that input and output | - | | frames are processed at different rates. You | - | | should set this for any nodes that perform | - | | resampling. | - +-----------------------------------------+---------------------------------------------------+ - | MA_NODE_FLAG_SILENT_OUTPUT | Used to tell miniaudio that a node produces only | - | | silent output. This is useful for nodes where you | - | | don't want the output to contribute to the final | - | | mix. An example might be if you want split your | - | | stream and have one branch be output to a file. | - | | When using this flag, you should avoid writing to | - | | the output buffer of the node's processing | - | | callback because miniaudio will ignore it anyway. | - +-----------------------------------------+---------------------------------------------------+ - - -If you need to make a copy of an audio stream for effect processing you can use a splitter node -called `ma_splitter_node`. This takes has 1 input bus and splits the stream into 2 output buses. -You can use it like this: - - ```c - ma_splitter_node_config splitterNodeConfig = ma_splitter_node_config_init(channels); - - ma_splitter_node splitterNode; - result = ma_splitter_node_init(&nodeGraph, &splitterNodeConfig, NULL, &splitterNode); - if (result != MA_SUCCESS) { - // Failed to create node. - } - - // Attach your output buses to two different input buses (can be on two different nodes). - ma_node_attach_output_bus(&splitterNode, 0, ma_node_graph_get_endpoint(&nodeGraph), 0); // Attach directly to the endpoint. - ma_node_attach_output_bus(&splitterNode, 1, &myEffectNode, 0); // Attach to input bus 0 of some effect node. - ``` - -The volume of an output bus can be configured on a per-bus basis: - - ```c - ma_node_set_output_bus_volume(&splitterNode, 0, 0.5f); - ma_node_set_output_bus_volume(&splitterNode, 1, 0.5f); - ``` - -In the code above we're using the splitter node from before and changing the volume of each of the -copied streams. - -You can start and stop a node with the following: - - ```c - ma_node_set_state(&splitterNode, ma_node_state_started); // The default state. - ma_node_set_state(&splitterNode, ma_node_state_stopped); - ``` - -By default the node is in a started state, but since it won't be connected to anything won't -actually be invoked by the node graph until it's connected. When you stop a node, data will not be -read from any of its input connections. You can use this property to stop a group of sounds -atomically. - -You can configure the initial state of a node in it's config: - - ```c - nodeConfig.initialState = ma_node_state_stopped; - ``` - -Note that for the stock specialized nodes, all of their configs will have a `nodeConfig` member -which is the config to use with the base node. This is where the initial state can be configured -for specialized nodes: - - ```c - dataSourceNodeConfig.nodeConfig.initialState = ma_node_state_stopped; - ``` - -When using a specialized node like `ma_data_source_node` or `ma_splitter_node`, be sure to not -modify the `vtable` member of the `nodeConfig` object. - - -7.1. Timing ------------ -The node graph supports starting and stopping nodes at scheduled times. This is especially useful -for data source nodes where you want to get the node set up, but only start playback at a specific -time. There are two clocks: local and global. - -A local clock is per-node, whereas the global clock is per graph. Scheduling starts and stops can -only be done based on the global clock because the local clock will not be running while the node -is stopped. The global clocks advances whenever `ma_node_graph_read_pcm_frames()` is called. On the -other hand, the local clock only advances when the node's processing callback is fired, and is -advanced based on the output frame count. - -To retrieve the global time, use `ma_node_graph_get_time()`. The global time can be set with -`ma_node_graph_set_time()` which might be useful if you want to do seeking on a global timeline. -Getting and setting the local time is similar. Use `ma_node_get_time()` to retrieve the local time, -and `ma_node_set_time()` to set the local time. The global and local times will be advanced by the -audio thread, so care should be taken to avoid data races. Ideally you should avoid calling these -outside of the node processing callbacks which are always run on the audio thread. - -There is basic support for scheduling the starting and stopping of nodes. You can only schedule one -start and one stop at a time. This is mainly intended for putting nodes into a started or stopped -state in a frame-exact manner. Without this mechanism, starting and stopping of a node is limited -to the resolution of a call to `ma_node_graph_read_pcm_frames()` which would typically be in blocks -of several milliseconds. The following APIs can be used for scheduling node states: - - ```c - ma_node_set_state_time() - ma_node_get_state_time() - ``` - -The time is absolute and must be based on the global clock. An example is below: - - ```c - ma_node_set_state_time(&myNode, ma_node_state_started, sampleRate*1); // Delay starting to 1 second. - ma_node_set_state_time(&myNode, ma_node_state_stopped, sampleRate*5); // Delay stopping to 5 seconds. - ``` - -An example for changing the state using a relative time. - - ```c - ma_node_set_state_time(&myNode, ma_node_state_started, sampleRate*1 + ma_node_graph_get_time(&myNodeGraph)); - ma_node_set_state_time(&myNode, ma_node_state_stopped, sampleRate*5 + ma_node_graph_get_time(&myNodeGraph)); - ``` - -Note that due to the nature of multi-threading the times may not be 100% exact. If this is an -issue, consider scheduling state changes from within a processing callback. An idea might be to -have some kind of passthrough trigger node that is used specifically for tracking time and handling -events. - - - -7.2. Thread Safety and Locking ------------------------------- -When processing audio, it's ideal not to have any kind of locking in the audio thread. Since it's -expected that `ma_node_graph_read_pcm_frames()` would be run on the audio thread, it does so -without the use of any locks. This section discusses the implementation used by miniaudio and goes -over some of the compromises employed by miniaudio to achieve this goal. Note that the current -implementation may not be ideal - feedback and critiques are most welcome. - -The node graph API is not *entirely* lock-free. Only `ma_node_graph_read_pcm_frames()` is expected -to be lock-free. Attachment, detachment and uninitialization of nodes use locks to simplify the -implementation, but are crafted in a way such that such locking is not required when reading audio -data from the graph. Locking in these areas are achieved by means of spinlocks. - -The main complication with keeping `ma_node_graph_read_pcm_frames()` lock-free stems from the fact -that a node can be uninitialized, and it's memory potentially freed, while in the middle of being -processed on the audio thread. There are times when the audio thread will be referencing a node, -which means the uninitialization process of a node needs to make sure it delays returning until the -audio thread is finished so that control is not handed back to the caller thereby giving them a -chance to free the node's memory. - -When the audio thread is processing a node, it does so by reading from each of the output buses of -the node. In order for a node to process data for one of its output buses, it needs to read from -each of its input buses, and so on an so forth. It follows that once all output buses of a node -are detached, the node as a whole will be disconnected and no further processing will occur unless -it's output buses are reattached, which won't be happening when the node is being uninitialized. -By having `ma_node_detach_output_bus()` wait until the audio thread is finished with it, we can -simplify a few things, at the expense of making `ma_node_detach_output_bus()` a bit slower. By -doing this, the implementation of `ma_node_uninit()` becomes trivial - just detach all output -nodes, followed by each of the attachments to each of its input nodes, and then do any final clean -up. - -With the above design, the worst-case scenario is `ma_node_detach_output_bus()` taking as long as -it takes to process the output bus being detached. This will happen if it's called at just the -wrong moment where the audio thread has just iterated it and has just started processing. The -caller of `ma_node_detach_output_bus()` will stall until the audio thread is finished, which -includes the cost of recursively processing its inputs. This is the biggest compromise made with -the approach taken by miniaudio for its lock-free processing system. The cost of detaching nodes -earlier in the pipeline (data sources, for example) will be cheaper than the cost of detaching -higher level nodes, such as some kind of final post-processing endpoint. If you need to do mass -detachments, detach starting from the lowest level nodes and work your way towards the final -endpoint node (but don't try detaching the node graph's endpoint). If the audio thread is not -running, detachment will be fast and detachment in any order will be the same. The reason nodes -need to wait for their input attachments to complete is due to the potential for desyncs between -data sources. If the node was to terminate processing mid way through processing its inputs, -there's a chance that some of the underlying data sources will have been read, but then others not. -That will then result in a potential desynchronization when detaching and reattaching higher-level -nodes. A possible solution to this is to have an option when detaching to terminate processing -before processing all input attachments which should be fairly simple. - -Another compromise, albeit less significant, is locking when attaching and detaching nodes. This -locking is achieved by means of a spinlock in order to reduce memory overhead. A lock is present -for each input bus and output bus. When an output bus is connected to an input bus, both the output -bus and input bus is locked. This locking is specifically for attaching and detaching across -different threads and does not affect `ma_node_graph_read_pcm_frames()` in any way. The locking and -unlocking is mostly self-explanatory, but a slightly less intuitive aspect comes into it when -considering that iterating over attachments must not break as a result of attaching or detaching a -node while iteration is occurring. - -Attaching and detaching are both quite simple. When an output bus of a node is attached to an input -bus of another node, it's added to a linked list. Basically, an input bus is a linked list, where -each item in the list is and output bus. We have some intentional (and convenient) restrictions on -what can done with the linked list in order to simplify the implementation. First of all, whenever -something needs to iterate over the list, it must do so in a forward direction. Backwards iteration -is not supported. Also, items can only be added to the start of the list. - -The linked list is a doubly-linked list where each item in the list (an output bus) holds a pointer -to the next item in the list, and another to the previous item. A pointer to the previous item is -only required for fast detachment of the node - it is never used in iteration. This is an -important property because it means from the perspective of iteration, attaching and detaching of -an item can be done with a single atomic assignment. This is exploited by both the attachment and -detachment process. When attaching the node, the first thing that is done is the setting of the -local "next" and "previous" pointers of the node. After that, the item is "attached" to the list -by simply performing an atomic exchange with the head pointer. After that, the node is "attached" -to the list from the perspective of iteration. Even though the "previous" pointer of the next item -hasn't yet been set, from the perspective of iteration it's been attached because iteration will -only be happening in a forward direction which means the "previous" pointer won't actually ever get -used. The same general process applies to detachment. See `ma_node_attach_output_bus()` and -`ma_node_detach_output_bus()` for the implementation of this mechanism. - - - -8. Decoding -=========== -The `ma_decoder` API is used for reading audio files. Decoders are completely decoupled from -devices and can be used independently. Built-in support is included for the following formats: - - +---------+ - | Format | - +---------+ - | WAV | - | MP3 | - | FLAC | - +---------+ - -You can disable the built-in decoders by specifying one or more of the following options before the -miniaudio implementation: - - ```c - #define MA_NO_WAV - #define MA_NO_MP3 - #define MA_NO_FLAC - ``` - -miniaudio supports the ability to plug in custom decoders. See the section below for details on how -to use custom decoders. - -A decoder can be initialized from a file with `ma_decoder_init_file()`, a block of memory with -`ma_decoder_init_memory()`, or from data delivered via callbacks with `ma_decoder_init()`. Here is -an example for loading a decoder from a file: - - ```c - ma_decoder decoder; - ma_result result = ma_decoder_init_file("MySong.mp3", NULL, &decoder); - if (result != MA_SUCCESS) { - return false; // An error occurred. - } - - ... - - ma_decoder_uninit(&decoder); - ``` - -When initializing a decoder, you can optionally pass in a pointer to a `ma_decoder_config` object -(the `NULL` argument in the example above) which allows you to configure the output format, channel -count, sample rate and channel map: - - ```c - ma_decoder_config config = ma_decoder_config_init(ma_format_f32, 2, 48000); - ``` - -When passing in `NULL` for decoder config in `ma_decoder_init*()`, the output format will be the -same as that defined by the decoding backend. - -Data is read from the decoder as PCM frames. This will output the number of PCM frames actually -read. If this is less than the requested number of PCM frames it means you've reached the end. The -return value will be `MA_AT_END` if no samples have been read and the end has been reached. - - ```c - ma_result result = ma_decoder_read_pcm_frames(pDecoder, pFrames, framesToRead, &framesRead); - if (framesRead < framesToRead) { - // Reached the end. - } - ``` - -You can also seek to a specific frame like so: - - ```c - ma_result result = ma_decoder_seek_to_pcm_frame(pDecoder, targetFrame); - if (result != MA_SUCCESS) { - return false; // An error occurred. - } - ``` - -If you want to loop back to the start, you can simply seek back to the first PCM frame: - - ```c - ma_decoder_seek_to_pcm_frame(pDecoder, 0); - ``` - -When loading a decoder, miniaudio uses a trial and error technique to find the appropriate decoding -backend. This can be unnecessarily inefficient if the type is already known. In this case you can -use `encodingFormat` variable in the device config to specify a specific encoding format you want -to decode: - - ```c - decoderConfig.encodingFormat = ma_encoding_format_wav; - ``` - -See the `ma_encoding_format` enum for possible encoding formats. - -The `ma_decoder_init_file()` API will try using the file extension to determine which decoding -backend to prefer. - - -8.1. Custom Decoders --------------------- -It's possible to implement a custom decoder and plug it into miniaudio. This is extremely useful -when you want to use the `ma_decoder` API, but need to support an encoding format that's not one of -the stock formats supported by miniaudio. This can be put to particularly good use when using the -`ma_engine` and/or `ma_resource_manager` APIs because they use `ma_decoder` internally. If, for -example, you wanted to support Opus, you can do so with a custom decoder (there if a reference -Opus decoder in the "extras" folder of the miniaudio repository which uses libopus + libopusfile). - -A custom decoder must implement a data source. A vtable called `ma_decoding_backend_vtable` needs -to be implemented which is then passed into the decoder config: - - ```c - ma_decoding_backend_vtable* pCustomBackendVTables[] = - { - &g_ma_decoding_backend_vtable_libvorbis, - &g_ma_decoding_backend_vtable_libopus - }; - - ... - - decoderConfig = ma_decoder_config_init_default(); - decoderConfig.pCustomBackendUserData = NULL; - decoderConfig.ppCustomBackendVTables = pCustomBackendVTables; - decoderConfig.customBackendCount = sizeof(pCustomBackendVTables) / sizeof(pCustomBackendVTables[0]); - ``` - -The `ma_decoding_backend_vtable` vtable has the following functions: - - ``` - onInit - onInitFile - onInitFileW - onInitMemory - onUninit - ``` - -There are only two functions that must be implemented - `onInit` and `onUninit`. The other -functions can be implemented for a small optimization for loading from a file path or memory. If -these are not specified, miniaudio will deal with it for you via a generic implementation. - -When you initialize a custom data source (by implementing the `onInit` function in the vtable) you -will need to output a pointer to a `ma_data_source` which implements your custom decoder. See the -section about data sources for details on how to implement this. Alternatively, see the -"custom_decoders" example in the miniaudio repository. - -The `onInit` function takes a pointer to some callbacks for the purpose of reading raw audio data -from some arbitrary source. You'll use these functions to read from the raw data and perform the -decoding. When you call them, you will pass in the `pReadSeekTellUserData` pointer to the relevant -parameter. - -The `pConfig` parameter in `onInit` can be used to configure the backend if appropriate. It's only -used as a hint and can be ignored. However, if any of the properties are relevant to your decoder, -an optimal implementation will handle the relevant properties appropriately. - -If memory allocation is required, it should be done so via the specified allocation callbacks if -possible (the `pAllocationCallbacks` parameter). - -If an error occurs when initializing the decoder, you should leave `ppBackend` unset, or set to -NULL, and make sure everything is cleaned up appropriately and an appropriate result code returned. -When multiple custom backends are specified, miniaudio will cycle through the vtables in the order -they're listed in the array that's passed into the decoder config so it's important that your -initialization routine is clean. - -When a decoder is uninitialized, the `onUninit` callback will be fired which will give you an -opportunity to clean up and internal data. - - - -9. Encoding -=========== -The `ma_encoding` API is used for writing audio files. The only supported output format is WAV. -This can be disabled by specifying the following option before the implementation of miniaudio: - - ```c - #define MA_NO_WAV - ``` - -An encoder can be initialized to write to a file with `ma_encoder_init_file()` or from data -delivered via callbacks with `ma_encoder_init()`. Below is an example for initializing an encoder -to output to a file. - - ```c - ma_encoder_config config = ma_encoder_config_init(ma_encoding_format_wav, FORMAT, CHANNELS, SAMPLE_RATE); - ma_encoder encoder; - ma_result result = ma_encoder_init_file("my_file.wav", &config, &encoder); - if (result != MA_SUCCESS) { - // Error - } - - ... - - ma_encoder_uninit(&encoder); - ``` - -When initializing an encoder you must specify a config which is initialized with -`ma_encoder_config_init()`. Here you must specify the file type, the output sample format, output -channel count and output sample rate. The following file types are supported: - - +------------------------+-------------+ - | Enum | Description | - +------------------------+-------------+ - | ma_encoding_format_wav | WAV | - +------------------------+-------------+ - -If the format, channel count or sample rate is not supported by the output file type an error will -be returned. The encoder will not perform data conversion so you will need to convert it before -outputting any audio data. To output audio data, use `ma_encoder_write_pcm_frames()`, like in the -example below: - - ```c - ma_uint64 framesWritten; - result = ma_encoder_write_pcm_frames(&encoder, pPCMFramesToWrite, framesToWrite, &framesWritten); - if (result != MA_SUCCESS) { - ... handle error ... - } - ``` - -The `framesWritten` variable will contain the number of PCM frames that were actually written. This -is optionally and you can pass in `NULL` if you need this. - -Encoders must be uninitialized with `ma_encoder_uninit()`. - - - -10. Data Conversion -=================== -A data conversion API is included with miniaudio which supports the majority of data conversion -requirements. This supports conversion between sample formats, channel counts (with channel -mapping) and sample rates. - - -10.1. Sample Format Conversion ------------------------------- -Conversion between sample formats is achieved with the `ma_pcm_*_to_*()`, `ma_pcm_convert()` and -`ma_convert_pcm_frames_format()` APIs. Use `ma_pcm_*_to_*()` to convert between two specific -formats. Use `ma_pcm_convert()` to convert based on a `ma_format` variable. Use -`ma_convert_pcm_frames_format()` to convert PCM frames where you want to specify the frame count -and channel count as a variable instead of the total sample count. - - -10.1.1. Dithering ------------------ -Dithering can be set using the ditherMode parameter. - -The different dithering modes include the following, in order of efficiency: - - +-----------+--------------------------+ - | Type | Enum Token | - +-----------+--------------------------+ - | None | ma_dither_mode_none | - | Rectangle | ma_dither_mode_rectangle | - | Triangle | ma_dither_mode_triangle | - +-----------+--------------------------+ - -Note that even if the dither mode is set to something other than `ma_dither_mode_none`, it will be -ignored for conversions where dithering is not needed. Dithering is available for the following -conversions: - - ``` - s16 -> u8 - s24 -> u8 - s32 -> u8 - f32 -> u8 - s24 -> s16 - s32 -> s16 - f32 -> s16 - ``` - -Note that it is not an error to pass something other than ma_dither_mode_none for conversions where -dither is not used. It will just be ignored. - - - -10.2. Channel Conversion ------------------------- -Channel conversion is used for channel rearrangement and conversion from one channel count to -another. The `ma_channel_converter` API is used for channel conversion. Below is an example of -initializing a simple channel converter which converts from mono to stereo. - - ```c - ma_channel_converter_config config = ma_channel_converter_config_init( - ma_format, // Sample format - 1, // Input channels - NULL, // Input channel map - 2, // Output channels - NULL, // Output channel map - ma_channel_mix_mode_default); // The mixing algorithm to use when combining channels. - - result = ma_channel_converter_init(&config, NULL, &converter); - if (result != MA_SUCCESS) { - // Error. - } - ``` - -To perform the conversion simply call `ma_channel_converter_process_pcm_frames()` like so: - - ```c - ma_result result = ma_channel_converter_process_pcm_frames(&converter, pFramesOut, pFramesIn, frameCount); - if (result != MA_SUCCESS) { - // Error. - } - ``` - -It is up to the caller to ensure the output buffer is large enough to accommodate the new PCM -frames. - -Input and output PCM frames are always interleaved. Deinterleaved layouts are not supported. - - -10.2.1. Channel Mapping ------------------------ -In addition to converting from one channel count to another, like the example above, the channel -converter can also be used to rearrange channels. When initializing the channel converter, you can -optionally pass in channel maps for both the input and output frames. If the channel counts are the -same, and each channel map contains the same channel positions with the exception that they're in -a different order, a simple shuffling of the channels will be performed. If, however, there is not -a 1:1 mapping of channel positions, or the channel counts differ, the input channels will be mixed -based on a mixing mode which is specified when initializing the `ma_channel_converter_config` -object. - -When converting from mono to multi-channel, the mono channel is simply copied to each output -channel. When going the other way around, the audio of each output channel is simply averaged and -copied to the mono channel. - -In more complicated cases blending is used. The `ma_channel_mix_mode_simple` mode will drop excess -channels and silence extra channels. For example, converting from 4 to 2 channels, the 3rd and 4th -channels will be dropped, whereas converting from 2 to 4 channels will put silence into the 3rd and -4th channels. - -The `ma_channel_mix_mode_rectangle` mode uses spacial locality based on a rectangle to compute a -simple distribution between input and output. Imagine sitting in the middle of a room, with -speakers on the walls representing channel positions. The `MA_CHANNEL_FRONT_LEFT` position can be -thought of as being in the corner of the front and left walls. - -Finally, the `ma_channel_mix_mode_custom_weights` mode can be used to use custom user-defined -weights. Custom weights can be passed in as the last parameter of -`ma_channel_converter_config_init()`. - -Predefined channel maps can be retrieved with `ma_channel_map_init_standard()`. This takes a -`ma_standard_channel_map` enum as its first parameter, which can be one of the following: - - +-----------------------------------+-----------------------------------------------------------+ - | Name | Description | - +-----------------------------------+-----------------------------------------------------------+ - | ma_standard_channel_map_default | Default channel map used by miniaudio. See below. | - | ma_standard_channel_map_microsoft | Channel map used by Microsoft's bitfield channel maps. | - | ma_standard_channel_map_alsa | Default ALSA channel map. | - | ma_standard_channel_map_rfc3551 | RFC 3551. Based on AIFF. | - | ma_standard_channel_map_flac | FLAC channel map. | - | ma_standard_channel_map_vorbis | Vorbis channel map. | - | ma_standard_channel_map_sound4 | FreeBSD's sound(4). | - | ma_standard_channel_map_sndio | sndio channel map. http://www.sndio.org/tips.html. | - | ma_standard_channel_map_webaudio | https://webaudio.github.io/web-audio-api/#ChannelOrdering | - +-----------------------------------+-----------------------------------------------------------+ - -Below are the channel maps used by default in miniaudio (`ma_standard_channel_map_default`): - - +---------------+---------------------------------+ - | Channel Count | Mapping | - +---------------+---------------------------------+ - | 1 (Mono) | 0: MA_CHANNEL_MONO | - +---------------+---------------------------------+ - | 2 (Stereo) | 0: MA_CHANNEL_FRONT_LEFT
| - | | 1: MA_CHANNEL_FRONT_RIGHT | - +---------------+---------------------------------+ - | 3 | 0: MA_CHANNEL_FRONT_LEFT
| - | | 1: MA_CHANNEL_FRONT_RIGHT
| - | | 2: MA_CHANNEL_FRONT_CENTER | - +---------------+---------------------------------+ - | 4 (Surround) | 0: MA_CHANNEL_FRONT_LEFT
| - | | 1: MA_CHANNEL_FRONT_RIGHT
| - | | 2: MA_CHANNEL_FRONT_CENTER
| - | | 3: MA_CHANNEL_BACK_CENTER | - +---------------+---------------------------------+ - | 5 | 0: MA_CHANNEL_FRONT_LEFT
| - | | 1: MA_CHANNEL_FRONT_RIGHT
| - | | 2: MA_CHANNEL_FRONT_CENTER
| - | | 3: MA_CHANNEL_BACK_LEFT
| - | | 4: MA_CHANNEL_BACK_RIGHT | - +---------------+---------------------------------+ - | 6 (5.1) | 0: MA_CHANNEL_FRONT_LEFT
| - | | 1: MA_CHANNEL_FRONT_RIGHT
| - | | 2: MA_CHANNEL_FRONT_CENTER
| - | | 3: MA_CHANNEL_LFE
| - | | 4: MA_CHANNEL_SIDE_LEFT
| - | | 5: MA_CHANNEL_SIDE_RIGHT | - +---------------+---------------------------------+ - | 7 | 0: MA_CHANNEL_FRONT_LEFT
| - | | 1: MA_CHANNEL_FRONT_RIGHT
| - | | 2: MA_CHANNEL_FRONT_CENTER
| - | | 3: MA_CHANNEL_LFE
| - | | 4: MA_CHANNEL_BACK_CENTER
| - | | 4: MA_CHANNEL_SIDE_LEFT
| - | | 5: MA_CHANNEL_SIDE_RIGHT | - +---------------+---------------------------------+ - | 8 (7.1) | 0: MA_CHANNEL_FRONT_LEFT
| - | | 1: MA_CHANNEL_FRONT_RIGHT
| - | | 2: MA_CHANNEL_FRONT_CENTER
| - | | 3: MA_CHANNEL_LFE
| - | | 4: MA_CHANNEL_BACK_LEFT
| - | | 5: MA_CHANNEL_BACK_RIGHT
| - | | 6: MA_CHANNEL_SIDE_LEFT
| - | | 7: MA_CHANNEL_SIDE_RIGHT | - +---------------+---------------------------------+ - | Other | All channels set to 0. This | - | | is equivalent to the same | - | | mapping as the device. | - +---------------+---------------------------------+ - - - -10.3. Resampling ----------------- -Resampling is achieved with the `ma_resampler` object. To create a resampler object, do something -like the following: - - ```c - ma_resampler_config config = ma_resampler_config_init( - ma_format_s16, - channels, - sampleRateIn, - sampleRateOut, - ma_resample_algorithm_linear); - - ma_resampler resampler; - ma_result result = ma_resampler_init(&config, NULL, &resampler); - if (result != MA_SUCCESS) { - // An error occurred... - } - ``` - -Do the following to uninitialize the resampler: - - ```c - ma_resampler_uninit(&resampler); - ``` - -The following example shows how data can be processed - - ```c - ma_uint64 frameCountIn = 1000; - ma_uint64 frameCountOut = 2000; - ma_result result = ma_resampler_process_pcm_frames(&resampler, pFramesIn, &frameCountIn, pFramesOut, &frameCountOut); - if (result != MA_SUCCESS) { - // An error occurred... - } - - // At this point, frameCountIn contains the number of input frames that were consumed and frameCountOut contains the - // number of output frames written. - ``` - -To initialize the resampler you first need to set up a config (`ma_resampler_config`) with -`ma_resampler_config_init()`. You need to specify the sample format you want to use, the number of -channels, the input and output sample rate, and the algorithm. - -The sample format can be either `ma_format_s16` or `ma_format_f32`. If you need a different format -you will need to perform pre- and post-conversions yourself where necessary. Note that the format -is the same for both input and output. The format cannot be changed after initialization. - -The resampler supports multiple channels and is always interleaved (both input and output). The -channel count cannot be changed after initialization. - -The sample rates can be anything other than zero, and are always specified in hertz. They should be -set to something like 44100, etc. The sample rate is the only configuration property that can be -changed after initialization. - -The miniaudio resampler has built-in support for the following algorithms: - - +-----------+------------------------------+ - | Algorithm | Enum Token | - +-----------+------------------------------+ - | Linear | ma_resample_algorithm_linear | - | Custom | ma_resample_algorithm_custom | - +-----------+------------------------------+ - -The algorithm cannot be changed after initialization. - -Processing always happens on a per PCM frame basis and always assumes interleaved input and output. -De-interleaved processing is not supported. To process frames, use -`ma_resampler_process_pcm_frames()`. On input, this function takes the number of output frames you -can fit in the output buffer and the number of input frames contained in the input buffer. On -output these variables contain the number of output frames that were written to the output buffer -and the number of input frames that were consumed in the process. You can pass in NULL for the -input buffer in which case it will be treated as an infinitely large buffer of zeros. The output -buffer can also be NULL, in which case the processing will be treated as seek. - -The sample rate can be changed dynamically on the fly. You can change this with explicit sample -rates with `ma_resampler_set_rate()` and also with a decimal ratio with -`ma_resampler_set_rate_ratio()`. The ratio is in/out. - -Sometimes it's useful to know exactly how many input frames will be required to output a specific -number of frames. You can calculate this with `ma_resampler_get_required_input_frame_count()`. -Likewise, it's sometimes useful to know exactly how many frames would be output given a certain -number of input frames. You can do this with `ma_resampler_get_expected_output_frame_count()`. - -Due to the nature of how resampling works, the resampler introduces some latency. This can be -retrieved in terms of both the input rate and the output rate with -`ma_resampler_get_input_latency()` and `ma_resampler_get_output_latency()`. - - -10.3.1. Resampling Algorithms ------------------------------ -The choice of resampling algorithm depends on your situation and requirements. - - -10.3.1.1. Linear Resampling ---------------------------- -The linear resampler is the fastest, but comes at the expense of poorer quality. There is, however, -some control over the quality of the linear resampler which may make it a suitable option depending -on your requirements. - -The linear resampler performs low-pass filtering before or after downsampling or upsampling, -depending on the sample rates you're converting between. When decreasing the sample rate, the -low-pass filter will be applied before downsampling. When increasing the rate it will be performed -after upsampling. By default a fourth order low-pass filter will be applied. This can be configured -via the `lpfOrder` configuration variable. Setting this to 0 will disable filtering. - -The low-pass filter has a cutoff frequency which defaults to half the sample rate of the lowest of -the input and output sample rates (Nyquist Frequency). - -The API for the linear resampler is the same as the main resampler API, only it's called -`ma_linear_resampler`. - - -10.3.2. Custom Resamplers -------------------------- -You can implement a custom resampler by using the `ma_resample_algorithm_custom` resampling -algorithm and setting a vtable in the resampler config: - - ```c - ma_resampler_config config = ma_resampler_config_init(..., ma_resample_algorithm_custom); - config.pBackendVTable = &g_customResamplerVTable; - ``` - -Custom resamplers are useful if the stock algorithms are not appropriate for your use case. You -need to implement the required functions in `ma_resampling_backend_vtable`. Note that not all -functions in the vtable need to be implemented, but if it's possible to implement, they should be. - -You can use the `ma_linear_resampler` object for an example on how to implement the vtable. The -`onGetHeapSize` callback is used to calculate the size of any internal heap allocation the custom -resampler will need to make given the supplied config. When you initialize the resampler via the -`onInit` callback, you'll be given a pointer to a heap allocation which is where you should store -the heap allocated data. You should not free this data in `onUninit` because miniaudio will manage -it for you. - -The `onProcess` callback is where the actual resampling takes place. On input, `pFrameCountIn` -points to a variable containing the number of frames in the `pFramesIn` buffer and -`pFrameCountOut` points to a variable containing the capacity in frames of the `pFramesOut` buffer. -On output, `pFrameCountIn` should be set to the number of input frames that were fully consumed, -whereas `pFrameCountOut` should be set to the number of frames that were written to `pFramesOut`. - -The `onSetRate` callback is optional and is used for dynamically changing the sample rate. If -dynamic rate changes are not supported, you can set this callback to NULL. - -The `onGetInputLatency` and `onGetOutputLatency` functions are used for retrieving the latency in -input and output rates respectively. These can be NULL in which case latency calculations will be -assumed to be NULL. - -The `onGetRequiredInputFrameCount` callback is used to give miniaudio a hint as to how many input -frames are required to be available to produce the given number of output frames. Likewise, the -`onGetExpectedOutputFrameCount` callback is used to determine how many output frames will be -produced given the specified number of input frames. miniaudio will use these as a hint, but they -are optional and can be set to NULL if you're unable to implement them. - - - -10.4. General Data Conversion ------------------------------ -The `ma_data_converter` API can be used to wrap sample format conversion, channel conversion and -resampling into one operation. This is what miniaudio uses internally to convert between the format -requested when the device was initialized and the format of the backend's native device. The API -for general data conversion is very similar to the resampling API. Create a `ma_data_converter` -object like this: - - ```c - ma_data_converter_config config = ma_data_converter_config_init( - inputFormat, - outputFormat, - inputChannels, - outputChannels, - inputSampleRate, - outputSampleRate - ); - - ma_data_converter converter; - ma_result result = ma_data_converter_init(&config, NULL, &converter); - if (result != MA_SUCCESS) { - // An error occurred... - } - ``` - -In the example above we use `ma_data_converter_config_init()` to initialize the config, however -there's many more properties that can be configured, such as channel maps and resampling quality. -Something like the following may be more suitable depending on your requirements: - - ```c - ma_data_converter_config config = ma_data_converter_config_init_default(); - config.formatIn = inputFormat; - config.formatOut = outputFormat; - config.channelsIn = inputChannels; - config.channelsOut = outputChannels; - config.sampleRateIn = inputSampleRate; - config.sampleRateOut = outputSampleRate; - ma_channel_map_init_standard(ma_standard_channel_map_flac, config.channelMapIn, sizeof(config.channelMapIn)/sizeof(config.channelMapIn[0]), config.channelCountIn); - config.resampling.linear.lpfOrder = MA_MAX_FILTER_ORDER; - ``` - -Do the following to uninitialize the data converter: - - ```c - ma_data_converter_uninit(&converter, NULL); - ``` - -The following example shows how data can be processed - - ```c - ma_uint64 frameCountIn = 1000; - ma_uint64 frameCountOut = 2000; - ma_result result = ma_data_converter_process_pcm_frames(&converter, pFramesIn, &frameCountIn, pFramesOut, &frameCountOut); - if (result != MA_SUCCESS) { - // An error occurred... - } - - // At this point, frameCountIn contains the number of input frames that were consumed and frameCountOut contains the number - // of output frames written. - ``` - -The data converter supports multiple channels and is always interleaved (both input and output). -The channel count cannot be changed after initialization. - -Sample rates can be anything other than zero, and are always specified in hertz. They should be set -to something like 44100, etc. The sample rate is the only configuration property that can be -changed after initialization, but only if the `resampling.allowDynamicSampleRate` member of -`ma_data_converter_config` is set to `MA_TRUE`. To change the sample rate, use -`ma_data_converter_set_rate()` or `ma_data_converter_set_rate_ratio()`. The ratio must be in/out. -The resampling algorithm cannot be changed after initialization. - -Processing always happens on a per PCM frame basis and always assumes interleaved input and output. -De-interleaved processing is not supported. To process frames, use -`ma_data_converter_process_pcm_frames()`. On input, this function takes the number of output frames -you can fit in the output buffer and the number of input frames contained in the input buffer. On -output these variables contain the number of output frames that were written to the output buffer -and the number of input frames that were consumed in the process. You can pass in NULL for the -input buffer in which case it will be treated as an infinitely large -buffer of zeros. The output buffer can also be NULL, in which case the processing will be treated -as seek. - -Sometimes it's useful to know exactly how many input frames will be required to output a specific -number of frames. You can calculate this with `ma_data_converter_get_required_input_frame_count()`. -Likewise, it's sometimes useful to know exactly how many frames would be output given a certain -number of input frames. You can do this with `ma_data_converter_get_expected_output_frame_count()`. - -Due to the nature of how resampling works, the data converter introduces some latency if resampling -is required. This can be retrieved in terms of both the input rate and the output rate with -`ma_data_converter_get_input_latency()` and `ma_data_converter_get_output_latency()`. - - - -11. Filtering -============= - -11.1. Biquad Filtering ----------------------- -Biquad filtering is achieved with the `ma_biquad` API. Example: - - ```c - ma_biquad_config config = ma_biquad_config_init(ma_format_f32, channels, b0, b1, b2, a0, a1, a2); - ma_result result = ma_biquad_init(&config, NULL, &biquad); - if (result != MA_SUCCESS) { - // Error. - } - - ... - - ma_biquad_process_pcm_frames(&biquad, pFramesOut, pFramesIn, frameCount); - ``` - -Biquad filtering is implemented using transposed direct form 2. The numerator coefficients are b0, -b1 and b2, and the denominator coefficients are a0, a1 and a2. The a0 coefficient is required and -coefficients must not be pre-normalized. - -Supported formats are `ma_format_s16` and `ma_format_f32`. If you need to use a different format -you need to convert it yourself beforehand. When using `ma_format_s16` the biquad filter will use -fixed point arithmetic. When using `ma_format_f32`, floating point arithmetic will be used. - -Input and output frames are always interleaved. - -Filtering can be applied in-place by passing in the same pointer for both the input and output -buffers, like so: - - ```c - ma_biquad_process_pcm_frames(&biquad, pMyData, pMyData, frameCount); - ``` - -If you need to change the values of the coefficients, but maintain the values in the registers you -can do so with `ma_biquad_reinit()`. This is useful if you need to change the properties of the -filter while keeping the values of registers valid to avoid glitching. Do not use -`ma_biquad_init()` for this as it will do a full initialization which involves clearing the -registers to 0. Note that changing the format or channel count after initialization is invalid and -will result in an error. - - -11.2. Low-Pass Filtering ------------------------- -Low-pass filtering is achieved with the following APIs: - - +---------+------------------------------------------+ - | API | Description | - +---------+------------------------------------------+ - | ma_lpf1 | First order low-pass filter | - | ma_lpf2 | Second order low-pass filter | - | ma_lpf | High order low-pass filter (Butterworth) | - +---------+------------------------------------------+ - -Low-pass filter example: - - ```c - ma_lpf_config config = ma_lpf_config_init(ma_format_f32, channels, sampleRate, cutoffFrequency, order); - ma_result result = ma_lpf_init(&config, &lpf); - if (result != MA_SUCCESS) { - // Error. - } - - ... - - ma_lpf_process_pcm_frames(&lpf, pFramesOut, pFramesIn, frameCount); - ``` - -Supported formats are `ma_format_s16` and` ma_format_f32`. If you need to use a different format -you need to convert it yourself beforehand. Input and output frames are always interleaved. - -Filtering can be applied in-place by passing in the same pointer for both the input and output -buffers, like so: - - ```c - ma_lpf_process_pcm_frames(&lpf, pMyData, pMyData, frameCount); - ``` - -The maximum filter order is limited to `MA_MAX_FILTER_ORDER` which is set to 8. If you need more, -you can chain first and second order filters together. - - ```c - for (iFilter = 0; iFilter < filterCount; iFilter += 1) { - ma_lpf2_process_pcm_frames(&lpf2[iFilter], pMyData, pMyData, frameCount); - } - ``` - -If you need to change the configuration of the filter, but need to maintain the state of internal -registers you can do so with `ma_lpf_reinit()`. This may be useful if you need to change the sample -rate and/or cutoff frequency dynamically while maintaining smooth transitions. Note that changing the -format or channel count after initialization is invalid and will result in an error. - -The `ma_lpf` object supports a configurable order, but if you only need a first order filter you -may want to consider using `ma_lpf1`. Likewise, if you only need a second order filter you can use -`ma_lpf2`. The advantage of this is that they're lighter weight and a bit more efficient. - -If an even filter order is specified, a series of second order filters will be processed in a -chain. If an odd filter order is specified, a first order filter will be applied, followed by a -series of second order filters in a chain. - - -11.3. High-Pass Filtering -------------------------- -High-pass filtering is achieved with the following APIs: - - +---------+-------------------------------------------+ - | API | Description | - +---------+-------------------------------------------+ - | ma_hpf1 | First order high-pass filter | - | ma_hpf2 | Second order high-pass filter | - | ma_hpf | High order high-pass filter (Butterworth) | - +---------+-------------------------------------------+ - -High-pass filters work exactly the same as low-pass filters, only the APIs are called `ma_hpf1`, -`ma_hpf2` and `ma_hpf`. See example code for low-pass filters for example usage. - - -11.4. Band-Pass Filtering -------------------------- -Band-pass filtering is achieved with the following APIs: - - +---------+-------------------------------+ - | API | Description | - +---------+-------------------------------+ - | ma_bpf2 | Second order band-pass filter | - | ma_bpf | High order band-pass filter | - +---------+-------------------------------+ - -Band-pass filters work exactly the same as low-pass filters, only the APIs are called `ma_bpf2` and -`ma_hpf`. See example code for low-pass filters for example usage. Note that the order for -band-pass filters must be an even number which means there is no first order band-pass filter, -unlike low-pass and high-pass filters. - - -11.5. Notch Filtering ---------------------- -Notch filtering is achieved with the following APIs: - - +-----------+------------------------------------------+ - | API | Description | - +-----------+------------------------------------------+ - | ma_notch2 | Second order notching filter | - +-----------+------------------------------------------+ - - -11.6. Peaking EQ Filtering -------------------------- -Peaking filtering is achieved with the following APIs: - - +----------+------------------------------------------+ - | API | Description | - +----------+------------------------------------------+ - | ma_peak2 | Second order peaking filter | - +----------+------------------------------------------+ - - -11.7. Low Shelf Filtering -------------------------- -Low shelf filtering is achieved with the following APIs: - - +-------------+------------------------------------------+ - | API | Description | - +-------------+------------------------------------------+ - | ma_loshelf2 | Second order low shelf filter | - +-------------+------------------------------------------+ - -Where a high-pass filter is used to eliminate lower frequencies, a low shelf filter can be used to -just turn them down rather than eliminate them entirely. - - -11.8. High Shelf Filtering --------------------------- -High shelf filtering is achieved with the following APIs: - - +-------------+------------------------------------------+ - | API | Description | - +-------------+------------------------------------------+ - | ma_hishelf2 | Second order high shelf filter | - +-------------+------------------------------------------+ - -The high shelf filter has the same API as the low shelf filter, only you would use `ma_hishelf` -instead of `ma_loshelf`. Where a low shelf filter is used to adjust the volume of low frequencies, -the high shelf filter does the same thing for high frequencies. - - - - -12. Waveform and Noise Generation -================================= - -12.1. Waveforms ---------------- -miniaudio supports generation of sine, square, triangle and sawtooth waveforms. This is achieved -with the `ma_waveform` API. Example: - - ```c - ma_waveform_config config = ma_waveform_config_init( - FORMAT, - CHANNELS, - SAMPLE_RATE, - ma_waveform_type_sine, - amplitude, - frequency); - - ma_waveform waveform; - ma_result result = ma_waveform_init(&config, &waveform); - if (result != MA_SUCCESS) { - // Error. - } - - ... - - ma_waveform_read_pcm_frames(&waveform, pOutput, frameCount); - ``` - -The amplitude, frequency, type, and sample rate can be changed dynamically with -`ma_waveform_set_amplitude()`, `ma_waveform_set_frequency()`, `ma_waveform_set_type()`, and -`ma_waveform_set_sample_rate()` respectively. - -You can invert the waveform by setting the amplitude to a negative value. You can use this to -control whether or not a sawtooth has a positive or negative ramp, for example. - -Below are the supported waveform types: - - +---------------------------+ - | Enum Name | - +---------------------------+ - | ma_waveform_type_sine | - | ma_waveform_type_square | - | ma_waveform_type_triangle | - | ma_waveform_type_sawtooth | - +---------------------------+ - - - -12.2. Noise ------------ -miniaudio supports generation of white, pink and Brownian noise via the `ma_noise` API. Example: - - ```c - ma_noise_config config = ma_noise_config_init( - FORMAT, - CHANNELS, - ma_noise_type_white, - SEED, - amplitude); - - ma_noise noise; - ma_result result = ma_noise_init(&config, &noise); - if (result != MA_SUCCESS) { - // Error. - } - - ... - - ma_noise_read_pcm_frames(&noise, pOutput, frameCount); - ``` - -The noise API uses simple LCG random number generation. It supports a custom seed which is useful -for things like automated testing requiring reproducibility. Setting the seed to zero will default -to `MA_DEFAULT_LCG_SEED`. - -The amplitude and seed can be changed dynamically with `ma_noise_set_amplitude()` and -`ma_noise_set_seed()` respectively. - -By default, the noise API will use different values for different channels. So, for example, the -left side in a stereo stream will be different to the right side. To instead have each channel use -the same random value, set the `duplicateChannels` member of the noise config to true, like so: - - ```c - config.duplicateChannels = MA_TRUE; - ``` - -Below are the supported noise types. - - +------------------------+ - | Enum Name | - +------------------------+ - | ma_noise_type_white | - | ma_noise_type_pink | - | ma_noise_type_brownian | - +------------------------+ - - - -13. Audio Buffers -================= -miniaudio supports reading from a buffer of raw audio data via the `ma_audio_buffer` API. This can -read from memory that's managed by the application, but can also handle the memory management for -you internally. Memory management is flexible and should support most use cases. - -Audio buffers are initialized using the standard configuration system used everywhere in miniaudio: - - ```c - ma_audio_buffer_config config = ma_audio_buffer_config_init( - format, - channels, - sizeInFrames, - pExistingData, - &allocationCallbacks); - - ma_audio_buffer buffer; - result = ma_audio_buffer_init(&config, &buffer); - if (result != MA_SUCCESS) { - // Error. - } - - ... - - ma_audio_buffer_uninit(&buffer); - ``` - -In the example above, the memory pointed to by `pExistingData` will *not* be copied and is how an -application can do self-managed memory allocation. If you would rather make a copy of the data, use -`ma_audio_buffer_init_copy()`. To uninitialize the buffer, use `ma_audio_buffer_uninit()`. - -Sometimes it can be convenient to allocate the memory for the `ma_audio_buffer` structure and the -raw audio data in a contiguous block of memory. That is, the raw audio data will be located -immediately after the `ma_audio_buffer` structure. To do this, use -`ma_audio_buffer_alloc_and_init()`: - - ```c - ma_audio_buffer_config config = ma_audio_buffer_config_init( - format, - channels, - sizeInFrames, - pExistingData, - &allocationCallbacks); - - ma_audio_buffer* pBuffer - result = ma_audio_buffer_alloc_and_init(&config, &pBuffer); - if (result != MA_SUCCESS) { - // Error - } - - ... - - ma_audio_buffer_uninit_and_free(&buffer); - ``` - -If you initialize the buffer with `ma_audio_buffer_alloc_and_init()` you should uninitialize it -with `ma_audio_buffer_uninit_and_free()`. In the example above, the memory pointed to by -`pExistingData` will be copied into the buffer, which is contrary to the behavior of -`ma_audio_buffer_init()`. - -An audio buffer has a playback cursor just like a decoder. As you read frames from the buffer, the -cursor moves forward. The last parameter (`loop`) can be used to determine if the buffer should -loop. The return value is the number of frames actually read. If this is less than the number of -frames requested it means the end has been reached. This should never happen if the `loop` -parameter is set to true. If you want to manually loop back to the start, you can do so with with -`ma_audio_buffer_seek_to_pcm_frame(pAudioBuffer, 0)`. Below is an example for reading data from an -audio buffer. - - ```c - ma_uint64 framesRead = ma_audio_buffer_read_pcm_frames(pAudioBuffer, pFramesOut, desiredFrameCount, isLooping); - if (framesRead < desiredFrameCount) { - // If not looping, this means the end has been reached. This should never happen in looping mode with valid input. - } - ``` - -Sometimes you may want to avoid the cost of data movement between the internal buffer and the -output buffer. Instead you can use memory mapping to retrieve a pointer to a segment of data: - - ```c - void* pMappedFrames; - ma_uint64 frameCount = frameCountToTryMapping; - ma_result result = ma_audio_buffer_map(pAudioBuffer, &pMappedFrames, &frameCount); - if (result == MA_SUCCESS) { - // Map was successful. The value in frameCount will be how many frames were _actually_ mapped, which may be - // less due to the end of the buffer being reached. - ma_copy_pcm_frames(pFramesOut, pMappedFrames, frameCount, pAudioBuffer->format, pAudioBuffer->channels); - - // You must unmap the buffer. - ma_audio_buffer_unmap(pAudioBuffer, frameCount); - } - ``` - -When you use memory mapping, the read cursor is increment by the frame count passed in to -`ma_audio_buffer_unmap()`. If you decide not to process every frame you can pass in a value smaller -than the value returned by `ma_audio_buffer_map()`. The disadvantage to using memory mapping is -that it does not handle looping for you. You can determine if the buffer is at the end for the -purpose of looping with `ma_audio_buffer_at_end()` or by inspecting the return value of -`ma_audio_buffer_unmap()` and checking if it equals `MA_AT_END`. You should not treat `MA_AT_END` -as an error when returned by `ma_audio_buffer_unmap()`. - - - -14. Ring Buffers -================ -miniaudio supports lock free (single producer, single consumer) ring buffers which are exposed via -the `ma_rb` and `ma_pcm_rb` APIs. The `ma_rb` API operates on bytes, whereas the `ma_pcm_rb` -operates on PCM frames. They are otherwise identical as `ma_pcm_rb` is just a wrapper around -`ma_rb`. - -Unlike most other APIs in miniaudio, ring buffers support both interleaved and deinterleaved -streams. The caller can also allocate their own backing memory for the ring buffer to use -internally for added flexibility. Otherwise the ring buffer will manage it's internal memory for -you. - -The examples below use the PCM frame variant of the ring buffer since that's most likely the one -you will want to use. To initialize a ring buffer, do something like the following: - - ```c - ma_pcm_rb rb; - ma_result result = ma_pcm_rb_init(FORMAT, CHANNELS, BUFFER_SIZE_IN_FRAMES, NULL, NULL, &rb); - if (result != MA_SUCCESS) { - // Error - } - ``` - -The `ma_pcm_rb_init()` function takes the sample format and channel count as parameters because -it's the PCM variant of the ring buffer API. For the regular ring buffer that operates on bytes you -would call `ma_rb_init()` which leaves these out and just takes the size of the buffer in bytes -instead of frames. The fourth parameter is an optional pre-allocated buffer and the fifth parameter -is a pointer to a `ma_allocation_callbacks` structure for custom memory allocation routines. -Passing in `NULL` for this results in `MA_MALLOC()` and `MA_FREE()` being used. - -Use `ma_pcm_rb_init_ex()` if you need a deinterleaved buffer. The data for each sub-buffer is -offset from each other based on the stride. To manage your sub-buffers you can use -`ma_pcm_rb_get_subbuffer_stride()`, `ma_pcm_rb_get_subbuffer_offset()` and -`ma_pcm_rb_get_subbuffer_ptr()`. - -Use `ma_pcm_rb_acquire_read()` and `ma_pcm_rb_acquire_write()` to retrieve a pointer to a section -of the ring buffer. You specify the number of frames you need, and on output it will set to what -was actually acquired. If the read or write pointer is positioned such that the number of frames -requested will require a loop, it will be clamped to the end of the buffer. Therefore, the number -of frames you're given may be less than the number you requested. - -After calling `ma_pcm_rb_acquire_read()` or `ma_pcm_rb_acquire_write()`, you do your work on the -buffer and then "commit" it with `ma_pcm_rb_commit_read()` or `ma_pcm_rb_commit_write()`. This is -where the read/write pointers are updated. When you commit you need to pass in the buffer that was -returned by the earlier call to `ma_pcm_rb_acquire_read()` or `ma_pcm_rb_acquire_write()` and is -only used for validation. The number of frames passed to `ma_pcm_rb_commit_read()` and -`ma_pcm_rb_commit_write()` is what's used to increment the pointers, and can be less that what was -originally requested. - -If you want to correct for drift between the write pointer and the read pointer you can use a -combination of `ma_pcm_rb_pointer_distance()`, `ma_pcm_rb_seek_read()` and -`ma_pcm_rb_seek_write()`. Note that you can only move the pointers forward, and you should only -move the read pointer forward via the consumer thread, and the write pointer forward by the -producer thread. If there is too much space between the pointers, move the read pointer forward. If -there is too little space between the pointers, move the write pointer forward. - -You can use a ring buffer at the byte level instead of the PCM frame level by using the `ma_rb` -API. This is exactly the same, only you will use the `ma_rb` functions instead of `ma_pcm_rb` and -instead of frame counts you will pass around byte counts. - -The maximum size of the buffer in bytes is `0x7FFFFFFF-(MA_SIMD_ALIGNMENT-1)` due to the most -significant bit being used to encode a loop flag and the internally managed buffers always being -aligned to `MA_SIMD_ALIGNMENT`. - -Note that the ring buffer is only thread safe when used by a single consumer thread and single -producer thread. - - - -15. Backends -============ -The following backends are supported by miniaudio. These are listed in order of default priority. -When no backend is specified when initializing a context or device, miniaudio will attempt to use -each of these backends in the order listed in the table below. - -Note that backends that are not usable by the build target will not be included in the build. For -example, ALSA, which is specific to Linux, will not be included in the Windows build. - - +-------------+-----------------------+--------------------------------------------------------+ - | Name | Enum Name | Supported Operating Systems | - +-------------+-----------------------+--------------------------------------------------------+ - | WASAPI | ma_backend_wasapi | Windows Vista+ | - | DirectSound | ma_backend_dsound | Windows XP+ | - | WinMM | ma_backend_winmm | Windows 95+ | - | Core Audio | ma_backend_coreaudio | macOS, iOS | - | sndio | ma_backend_sndio | OpenBSD | - | audio(4) | ma_backend_audio4 | NetBSD, OpenBSD | - | OSS | ma_backend_oss | FreeBSD | - | PulseAudio | ma_backend_pulseaudio | Cross Platform (disabled on Windows, BSD and Android) | - | ALSA | ma_backend_alsa | Linux | - | JACK | ma_backend_jack | Cross Platform (disabled on BSD and Android) | - | AAudio | ma_backend_aaudio | Android 8+ | - | OpenSL ES | ma_backend_opensl | Android (API level 16+) | - | Web Audio | ma_backend_webaudio | Web (via Emscripten) | - | Custom | ma_backend_custom | Cross Platform | - | Null | ma_backend_null | Cross Platform (not used on Web) | - +-------------+-----------------------+--------------------------------------------------------+ - -Some backends have some nuance details you may want to be aware of. - -15.1. WASAPI ------------- -- Low-latency shared mode will be disabled when using an application-defined sample rate which is - different to the device's native sample rate. To work around this, set `wasapi.noAutoConvertSRC` - to true in the device config. This is due to IAudioClient3_InitializeSharedAudioStream() failing - when the `AUDCLNT_STREAMFLAGS_AUTOCONVERTPCM` flag is specified. Setting wasapi.noAutoConvertSRC - will result in miniaudio's internal resampler being used instead which will in turn enable the - use of low-latency shared mode. - -15.2. PulseAudio ----------------- -- If you experience bad glitching/noise on Arch Linux, consider this fix from the Arch wiki: - https://wiki.archlinux.org/index.php/PulseAudio/Troubleshooting#Glitches,_skips_or_crackling. - Alternatively, consider using a different backend such as ALSA. - -15.3. Android -------------- -- To capture audio on Android, remember to add the RECORD_AUDIO permission to your manifest: - `` -- With OpenSL|ES, only a single ma_context can be active at any given time. This is due to a - limitation with OpenSL|ES. -- With AAudio, only default devices are enumerated. This is due to AAudio not having an enumeration - API (devices are enumerated through Java). You can however perform your own device enumeration - through Java and then set the ID in the ma_device_id structure (ma_device_id.aaudio) and pass it - to ma_device_init(). -- The backend API will perform resampling where possible. The reason for this as opposed to using - miniaudio's built-in resampler is to take advantage of any potential device-specific - optimizations the driver may implement. - -BSD ---- -- The sndio backend is currently only enabled on OpenBSD builds. -- The audio(4) backend is supported on OpenBSD, but you may need to disable sndiod before you can - use it. - -15.4. UWP ---------- -- UWP only supports default playback and capture devices. -- UWP requires the Microphone capability to be enabled in the application's manifest (Package.appxmanifest): - - ``` - - ... - - - - - ``` - -15.5. Web Audio / Emscripten ----------------------------- -- You cannot use `-std=c*` compiler flags, nor `-ansi`. This only applies to the Emscripten build. -- The first time a context is initialized it will create a global object called "miniaudio" whose - primary purpose is to act as a factory for device objects. -- Currently the Web Audio backend uses ScriptProcessorNode's, but this may need to change later as - they've been deprecated. -- Google has implemented a policy in their browsers that prevent automatic media output without - first receiving some kind of user input. The following web page has additional details: - https://developers.google.com/web/updates/2017/09/autoplay-policy-changes. Starting the device - may fail if you try to start playback without first handling some kind of user input. - - - -16. Optimization Tips -===================== -See below for some tips on improving performance. - -16.1. Low Level API -------------------- -- In the data callback, if your data is already clipped prior to copying it into the output buffer, - set the `noClip` config option in the device config to true. This will disable miniaudio's built - in clipping function. -- By default, miniaudio will pre-silence the data callback's output buffer. If you know that you - will always write valid data to the output buffer you can disable pre-silencing by setting the - `noPreSilence` config option in the device config to true. - -16.2. High Level API --------------------- -- If a sound does not require doppler or pitch shifting, consider disabling pitching by - initializing the sound with the `MA_SOUND_FLAG_NO_PITCH` flag. -- If a sound does not require spatialization, disable it by initializing the sound with the - `MA_SOUND_FLAG_NO_SPATIALIZATION` flag. It can be re-enabled again post-initialization with - `ma_sound_set_spatialization_enabled()`. -- If you know all of your sounds will always be the same sample rate, set the engine's sample - rate to match that of the sounds. Likewise, if you're using a self-managed resource manager, - consider setting the decoded sample rate to match your sounds. By configuring everything to - use a consistent sample rate, sample rate conversion can be avoided. - - - -17. Miscellaneous Notes -======================= -- Automatic stream routing is enabled on a per-backend basis. Support is explicitly enabled for - WASAPI and Core Audio, however other backends such as PulseAudio may naturally support it, though - not all have been tested. -- When compiling with VC6 and earlier, decoding is restricted to files less than 2GB in size. This - is due to 64-bit file APIs not being available. -*/ - -#ifndef miniaudio_h -#define miniaudio_h - -#ifdef __cplusplus -extern "C" { -#endif - -#define MA_STRINGIFY(x) #x -#define MA_XSTRINGIFY(x) MA_STRINGIFY(x) - -#define MA_VERSION_MAJOR 0 -#define MA_VERSION_MINOR 11 -#define MA_VERSION_REVISION 22 -#define MA_VERSION_STRING MA_XSTRINGIFY(MA_VERSION_MAJOR) "." MA_XSTRINGIFY(MA_VERSION_MINOR) "." MA_XSTRINGIFY(MA_VERSION_REVISION) - -#if defined(_MSC_VER) && !defined(__clang__) - #pragma warning(push) - #pragma warning(disable:4201) /* nonstandard extension used: nameless struct/union */ - #pragma warning(disable:4214) /* nonstandard extension used: bit field types other than int */ - #pragma warning(disable:4324) /* structure was padded due to alignment specifier */ -#elif defined(__clang__) || (defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 8))) - #pragma GCC diagnostic push - #pragma GCC diagnostic ignored "-Wpedantic" /* For ISO C99 doesn't support unnamed structs/unions [-Wpedantic] */ - #if defined(__clang__) - #pragma GCC diagnostic ignored "-Wc11-extensions" /* anonymous unions are a C11 extension */ - #endif -#endif - - -#if defined(__LP64__) || defined(_WIN64) || (defined(__x86_64__) && !defined(__ILP32__)) || defined(_M_X64) || defined(__ia64) || defined(_M_IA64) || defined(__aarch64__) || defined(_M_ARM64) || defined(__powerpc64__) || defined(__ppc64__) - #define MA_SIZEOF_PTR 8 -#else - #define MA_SIZEOF_PTR 4 -#endif - -#include /* For size_t. */ - -/* Sized types. */ -#if defined(MA_USE_STDINT) - #include - typedef int8_t ma_int8; - typedef uint8_t ma_uint8; - typedef int16_t ma_int16; - typedef uint16_t ma_uint16; - typedef int32_t ma_int32; - typedef uint32_t ma_uint32; - typedef int64_t ma_int64; - typedef uint64_t ma_uint64; -#else - typedef signed char ma_int8; - typedef unsigned char ma_uint8; - typedef signed short ma_int16; - typedef unsigned short ma_uint16; - typedef signed int ma_int32; - typedef unsigned int ma_uint32; - #if defined(_MSC_VER) && !defined(__clang__) - typedef signed __int64 ma_int64; - typedef unsigned __int64 ma_uint64; - #else - #if defined(__clang__) || (defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6))) - #pragma GCC diagnostic push - #pragma GCC diagnostic ignored "-Wlong-long" - #if defined(__clang__) - #pragma GCC diagnostic ignored "-Wc++11-long-long" - #endif - #endif - typedef signed long long ma_int64; - typedef unsigned long long ma_uint64; - #if defined(__clang__) || (defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6))) - #pragma GCC diagnostic pop - #endif - #endif -#endif /* MA_USE_STDINT */ - -#if MA_SIZEOF_PTR == 8 - typedef ma_uint64 ma_uintptr; -#else - typedef ma_uint32 ma_uintptr; -#endif - -typedef ma_uint8 ma_bool8; -typedef ma_uint32 ma_bool32; -#define MA_TRUE 1 -#define MA_FALSE 0 - -/* These float types are not used universally by miniaudio. It's to simplify some macro expansion for atomic types. */ -typedef float ma_float; -typedef double ma_double; - -typedef void* ma_handle; -typedef void* ma_ptr; - -/* -ma_proc is annoying because when compiling with GCC we get pedantic warnings about converting -between `void*` and `void (*)()`. We can't use `void (*)()` with MSVC however, because we'll get -warning C4191 about "type cast between incompatible function types". To work around this I'm going -to use a different data type depending on the compiler. -*/ -#if defined(__GNUC__) -typedef void (*ma_proc)(void); -#else -typedef void* ma_proc; -#endif - -#if defined(_MSC_VER) && !defined(_WCHAR_T_DEFINED) -typedef ma_uint16 wchar_t; -#endif - -/* Define NULL for some compilers. */ -#ifndef NULL -#define NULL 0 -#endif - -#if defined(SIZE_MAX) - #define MA_SIZE_MAX SIZE_MAX -#else - #define MA_SIZE_MAX 0xFFFFFFFF /* When SIZE_MAX is not defined by the standard library just default to the maximum 32-bit unsigned integer. */ -#endif - - -/* Platform/backend detection. */ -#if defined(_WIN32) || defined(__COSMOPOLITAN__) - #define MA_WIN32 - #if defined(MA_FORCE_UWP) || (defined(WINAPI_FAMILY) && ((defined(WINAPI_FAMILY_PC_APP) && WINAPI_FAMILY == WINAPI_FAMILY_PC_APP) || (defined(WINAPI_FAMILY_PHONE_APP) && WINAPI_FAMILY == WINAPI_FAMILY_PHONE_APP))) - #define MA_WIN32_UWP - #elif defined(WINAPI_FAMILY) && (defined(WINAPI_FAMILY_GAMES) && WINAPI_FAMILY == WINAPI_FAMILY_GAMES) - #define MA_WIN32_GDK - #else - #define MA_WIN32_DESKTOP - #endif -#endif -#if !defined(_WIN32) /* If it's not Win32, assume POSIX. */ - #define MA_POSIX - - /* - Use the MA_NO_PTHREAD_IN_HEADER option at your own risk. This is intentionally undocumented. - You can use this to avoid including pthread.h in the header section. The downside is that it - results in some fixed sized structures being declared for the various types that are used in - miniaudio. The risk here is that these types might be too small for a given platform. This - risk is yours to take and no support will be offered if you enable this option. - */ - #ifndef MA_NO_PTHREAD_IN_HEADER - #include /* Unfortunate #include, but needed for pthread_t, pthread_mutex_t and pthread_cond_t types. */ - typedef pthread_t ma_pthread_t; - typedef pthread_mutex_t ma_pthread_mutex_t; - typedef pthread_cond_t ma_pthread_cond_t; - #else - typedef ma_uintptr ma_pthread_t; - typedef union ma_pthread_mutex_t { char __data[40]; ma_uint64 __alignment; } ma_pthread_mutex_t; - typedef union ma_pthread_cond_t { char __data[48]; ma_uint64 __alignment; } ma_pthread_cond_t; - #endif - - #if defined(__unix__) - #define MA_UNIX - #endif - #if defined(__linux__) - #define MA_LINUX - #endif - #if defined(__APPLE__) - #define MA_APPLE - #endif - #if defined(__DragonFly__) || defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__) - #define MA_BSD - #endif - #if defined(__ANDROID__) - #define MA_ANDROID - #endif - #if defined(__EMSCRIPTEN__) - #define MA_EMSCRIPTEN - #endif - #if defined(__ORBIS__) - #define MA_ORBIS - #endif - #if defined(__PROSPERO__) - #define MA_PROSPERO - #endif - #if defined(__NX__) - #define MA_NX - #endif - #if defined(__BEOS__) || defined(__HAIKU__) - #define MA_BEOS - #endif - #if defined(__HAIKU__) - #define MA_HAIKU - #endif -#endif - -#if defined(__has_c_attribute) - #if __has_c_attribute(fallthrough) - #define MA_FALLTHROUGH [[fallthrough]] - #endif -#endif -#if !defined(MA_FALLTHROUGH) && defined(__has_attribute) && (defined(__clang__) || defined(__GNUC__)) - #if __has_attribute(fallthrough) - #define MA_FALLTHROUGH __attribute__((fallthrough)) - #endif -#endif -#if !defined(MA_FALLTHROUGH) - #define MA_FALLTHROUGH ((void)0) -#endif - -#ifdef _MSC_VER - #define MA_INLINE __forceinline - - /* noinline was introduced in Visual Studio 2005. */ - #if _MSC_VER >= 1400 - #define MA_NO_INLINE __declspec(noinline) - #else - #define MA_NO_INLINE - #endif -#elif defined(__GNUC__) - /* - I've had a bug report where GCC is emitting warnings about functions possibly not being inlineable. This warning happens when - the __attribute__((always_inline)) attribute is defined without an "inline" statement. I think therefore there must be some - case where "__inline__" is not always defined, thus the compiler emitting these warnings. When using -std=c89 or -ansi on the - command line, we cannot use the "inline" keyword and instead need to use "__inline__". In an attempt to work around this issue - I am using "__inline__" only when we're compiling in strict ANSI mode. - */ - #if defined(__STRICT_ANSI__) - #define MA_GNUC_INLINE_HINT __inline__ - #else - #define MA_GNUC_INLINE_HINT inline - #endif - - #if (__GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 2)) || defined(__clang__) - #define MA_INLINE MA_GNUC_INLINE_HINT __attribute__((always_inline)) - #define MA_NO_INLINE __attribute__((noinline)) - #else - #define MA_INLINE MA_GNUC_INLINE_HINT - #define MA_NO_INLINE __attribute__((noinline)) - #endif -#elif defined(__WATCOMC__) - #define MA_INLINE __inline - #define MA_NO_INLINE -#else - #define MA_INLINE - #define MA_NO_INLINE -#endif - -/* MA_DLL is not officially supported. You're on your own if you want to use this. */ -#if defined(MA_DLL) - #if defined(_WIN32) - #define MA_DLL_IMPORT __declspec(dllimport) - #define MA_DLL_EXPORT __declspec(dllexport) - #define MA_DLL_PRIVATE static - #else - #if defined(__GNUC__) && __GNUC__ >= 4 - #define MA_DLL_IMPORT __attribute__((visibility("default"))) - #define MA_DLL_EXPORT __attribute__((visibility("default"))) - #define MA_DLL_PRIVATE __attribute__((visibility("hidden"))) - #else - #define MA_DLL_IMPORT - #define MA_DLL_EXPORT - #define MA_DLL_PRIVATE static - #endif - #endif -#endif - -#if !defined(MA_API) - #if defined(MA_DLL) - #if defined(MINIAUDIO_IMPLEMENTATION) || defined(MA_IMPLEMENTATION) - #define MA_API MA_DLL_EXPORT - #else - #define MA_API MA_DLL_IMPORT - #endif - #else - #define MA_API extern - #endif -#endif - -#if !defined(MA_STATIC) - #if defined(MA_DLL) - #define MA_PRIVATE MA_DLL_PRIVATE - #else - #define MA_PRIVATE static - #endif -#endif - - -/* SIMD alignment in bytes. Currently set to 32 bytes in preparation for future AVX optimizations. */ -#define MA_SIMD_ALIGNMENT 32 - -/* -Special wchar_t type to ensure any structures in the public sections that reference it have a -consistent size across all platforms. - -On Windows, wchar_t is 2 bytes, whereas everywhere else it's 4 bytes. Since Windows likes to use -wchar_t for its IDs, we need a special explicitly sized wchar type that is always 2 bytes on all -platforms. -*/ -#if !defined(MA_POSIX) && defined(MA_WIN32) -typedef wchar_t ma_wchar_win32; -#else -typedef ma_uint16 ma_wchar_win32; -#endif - - - -/* -Logging Levels -============== -Log levels are only used to give logging callbacks some context as to the severity of a log message -so they can do filtering. All log levels will be posted to registered logging callbacks. If you -don't want to output a certain log level you can discriminate against the log level in the callback. - -MA_LOG_LEVEL_DEBUG - Used for debugging. Useful for debug and test builds, but should be disabled in release builds. - -MA_LOG_LEVEL_INFO - Informational logging. Useful for debugging. This will never be called from within the data - callback. - -MA_LOG_LEVEL_WARNING - Warnings. You should enable this in you development builds and action them when encountered. These - logs usually indicate a potential problem or misconfiguration, but still allow you to keep - running. This will never be called from within the data callback. - -MA_LOG_LEVEL_ERROR - Error logging. This will be fired when an operation fails and is subsequently aborted. This can - be fired from within the data callback, in which case the device will be stopped. You should - always have this log level enabled. -*/ -typedef enum -{ - MA_LOG_LEVEL_DEBUG = 4, - MA_LOG_LEVEL_INFO = 3, - MA_LOG_LEVEL_WARNING = 2, - MA_LOG_LEVEL_ERROR = 1 -} ma_log_level; - -/* -Variables needing to be accessed atomically should be declared with this macro for two reasons: - - 1) It allows people who read the code to identify a variable as such; and - 2) It forces alignment on platforms where it's required or optimal. - -Note that for x86/64, alignment is not strictly necessary, but does have some performance -implications. Where supported by the compiler, alignment will be used, but otherwise if the CPU -architecture does not require it, it will simply leave it unaligned. This is the case with old -versions of Visual Studio, which I've confirmed with at least VC6. -*/ -#if !defined(_MSC_VER) && defined (__STDC_VERSION__) && (__STDC_VERSION__ >= 201112L) - #include - #define MA_ATOMIC(alignment, type) _Alignas(alignment) type -#else - #if defined(__GNUC__) - /* GCC-style compilers. */ - #define MA_ATOMIC(alignment, type) type __attribute__((aligned(alignment))) - #elif defined(_MSC_VER) && _MSC_VER > 1200 /* 1200 = VC6. Alignment not supported, but not necessary because x86 is the only supported target. */ - /* MSVC. */ - #define MA_ATOMIC(alignment, type) __declspec(align(alignment)) type - #else - /* Other compilers. */ - #define MA_ATOMIC(alignment, type) type - #endif -#endif - -typedef struct ma_context ma_context; -typedef struct ma_device ma_device; - -typedef ma_uint8 ma_channel; -typedef enum -{ - MA_CHANNEL_NONE = 0, - MA_CHANNEL_MONO = 1, - MA_CHANNEL_FRONT_LEFT = 2, - MA_CHANNEL_FRONT_RIGHT = 3, - MA_CHANNEL_FRONT_CENTER = 4, - MA_CHANNEL_LFE = 5, - MA_CHANNEL_BACK_LEFT = 6, - MA_CHANNEL_BACK_RIGHT = 7, - MA_CHANNEL_FRONT_LEFT_CENTER = 8, - MA_CHANNEL_FRONT_RIGHT_CENTER = 9, - MA_CHANNEL_BACK_CENTER = 10, - MA_CHANNEL_SIDE_LEFT = 11, - MA_CHANNEL_SIDE_RIGHT = 12, - MA_CHANNEL_TOP_CENTER = 13, - MA_CHANNEL_TOP_FRONT_LEFT = 14, - MA_CHANNEL_TOP_FRONT_CENTER = 15, - MA_CHANNEL_TOP_FRONT_RIGHT = 16, - MA_CHANNEL_TOP_BACK_LEFT = 17, - MA_CHANNEL_TOP_BACK_CENTER = 18, - MA_CHANNEL_TOP_BACK_RIGHT = 19, - MA_CHANNEL_AUX_0 = 20, - MA_CHANNEL_AUX_1 = 21, - MA_CHANNEL_AUX_2 = 22, - MA_CHANNEL_AUX_3 = 23, - MA_CHANNEL_AUX_4 = 24, - MA_CHANNEL_AUX_5 = 25, - MA_CHANNEL_AUX_6 = 26, - MA_CHANNEL_AUX_7 = 27, - MA_CHANNEL_AUX_8 = 28, - MA_CHANNEL_AUX_9 = 29, - MA_CHANNEL_AUX_10 = 30, - MA_CHANNEL_AUX_11 = 31, - MA_CHANNEL_AUX_12 = 32, - MA_CHANNEL_AUX_13 = 33, - MA_CHANNEL_AUX_14 = 34, - MA_CHANNEL_AUX_15 = 35, - MA_CHANNEL_AUX_16 = 36, - MA_CHANNEL_AUX_17 = 37, - MA_CHANNEL_AUX_18 = 38, - MA_CHANNEL_AUX_19 = 39, - MA_CHANNEL_AUX_20 = 40, - MA_CHANNEL_AUX_21 = 41, - MA_CHANNEL_AUX_22 = 42, - MA_CHANNEL_AUX_23 = 43, - MA_CHANNEL_AUX_24 = 44, - MA_CHANNEL_AUX_25 = 45, - MA_CHANNEL_AUX_26 = 46, - MA_CHANNEL_AUX_27 = 47, - MA_CHANNEL_AUX_28 = 48, - MA_CHANNEL_AUX_29 = 49, - MA_CHANNEL_AUX_30 = 50, - MA_CHANNEL_AUX_31 = 51, - MA_CHANNEL_LEFT = MA_CHANNEL_FRONT_LEFT, - MA_CHANNEL_RIGHT = MA_CHANNEL_FRONT_RIGHT, - MA_CHANNEL_POSITION_COUNT = (MA_CHANNEL_AUX_31 + 1) -} _ma_channel_position; /* Do not use `_ma_channel_position` directly. Use `ma_channel` instead. */ - -typedef enum -{ - MA_SUCCESS = 0, - MA_ERROR = -1, /* A generic error. */ - MA_INVALID_ARGS = -2, - MA_INVALID_OPERATION = -3, - MA_OUT_OF_MEMORY = -4, - MA_OUT_OF_RANGE = -5, - MA_ACCESS_DENIED = -6, - MA_DOES_NOT_EXIST = -7, - MA_ALREADY_EXISTS = -8, - MA_TOO_MANY_OPEN_FILES = -9, - MA_INVALID_FILE = -10, - MA_TOO_BIG = -11, - MA_PATH_TOO_LONG = -12, - MA_NAME_TOO_LONG = -13, - MA_NOT_DIRECTORY = -14, - MA_IS_DIRECTORY = -15, - MA_DIRECTORY_NOT_EMPTY = -16, - MA_AT_END = -17, - MA_NO_SPACE = -18, - MA_BUSY = -19, - MA_IO_ERROR = -20, - MA_INTERRUPT = -21, - MA_UNAVAILABLE = -22, - MA_ALREADY_IN_USE = -23, - MA_BAD_ADDRESS = -24, - MA_BAD_SEEK = -25, - MA_BAD_PIPE = -26, - MA_DEADLOCK = -27, - MA_TOO_MANY_LINKS = -28, - MA_NOT_IMPLEMENTED = -29, - MA_NO_MESSAGE = -30, - MA_BAD_MESSAGE = -31, - MA_NO_DATA_AVAILABLE = -32, - MA_INVALID_DATA = -33, - MA_TIMEOUT = -34, - MA_NO_NETWORK = -35, - MA_NOT_UNIQUE = -36, - MA_NOT_SOCKET = -37, - MA_NO_ADDRESS = -38, - MA_BAD_PROTOCOL = -39, - MA_PROTOCOL_UNAVAILABLE = -40, - MA_PROTOCOL_NOT_SUPPORTED = -41, - MA_PROTOCOL_FAMILY_NOT_SUPPORTED = -42, - MA_ADDRESS_FAMILY_NOT_SUPPORTED = -43, - MA_SOCKET_NOT_SUPPORTED = -44, - MA_CONNECTION_RESET = -45, - MA_ALREADY_CONNECTED = -46, - MA_NOT_CONNECTED = -47, - MA_CONNECTION_REFUSED = -48, - MA_NO_HOST = -49, - MA_IN_PROGRESS = -50, - MA_CANCELLED = -51, - MA_MEMORY_ALREADY_MAPPED = -52, - - /* General non-standard errors. */ - MA_CRC_MISMATCH = -100, - - /* General miniaudio-specific errors. */ - MA_FORMAT_NOT_SUPPORTED = -200, - MA_DEVICE_TYPE_NOT_SUPPORTED = -201, - MA_SHARE_MODE_NOT_SUPPORTED = -202, - MA_NO_BACKEND = -203, - MA_NO_DEVICE = -204, - MA_API_NOT_FOUND = -205, - MA_INVALID_DEVICE_CONFIG = -206, - MA_LOOP = -207, - MA_BACKEND_NOT_ENABLED = -208, - - /* State errors. */ - MA_DEVICE_NOT_INITIALIZED = -300, - MA_DEVICE_ALREADY_INITIALIZED = -301, - MA_DEVICE_NOT_STARTED = -302, - MA_DEVICE_NOT_STOPPED = -303, - - /* Operation errors. */ - MA_FAILED_TO_INIT_BACKEND = -400, - MA_FAILED_TO_OPEN_BACKEND_DEVICE = -401, - MA_FAILED_TO_START_BACKEND_DEVICE = -402, - MA_FAILED_TO_STOP_BACKEND_DEVICE = -403 -} ma_result; - - -#define MA_MIN_CHANNELS 1 -#ifndef MA_MAX_CHANNELS -#define MA_MAX_CHANNELS 254 -#endif - -#ifndef MA_MAX_FILTER_ORDER -#define MA_MAX_FILTER_ORDER 8 -#endif - -typedef enum -{ - ma_stream_format_pcm = 0 -} ma_stream_format; - -typedef enum -{ - ma_stream_layout_interleaved = 0, - ma_stream_layout_deinterleaved -} ma_stream_layout; - -typedef enum -{ - ma_dither_mode_none = 0, - ma_dither_mode_rectangle, - ma_dither_mode_triangle -} ma_dither_mode; - -typedef enum -{ - /* - I like to keep these explicitly defined because they're used as a key into a lookup table. When items are - added to this, make sure there are no gaps and that they're added to the lookup table in ma_get_bytes_per_sample(). - */ - ma_format_unknown = 0, /* Mainly used for indicating an error, but also used as the default for the output format for decoders. */ - ma_format_u8 = 1, - ma_format_s16 = 2, /* Seems to be the most widely supported format. */ - ma_format_s24 = 3, /* Tightly packed. 3 bytes per sample. */ - ma_format_s32 = 4, - ma_format_f32 = 5, - ma_format_count -} ma_format; - -typedef enum -{ - /* Standard rates need to be in priority order. */ - ma_standard_sample_rate_48000 = 48000, /* Most common */ - ma_standard_sample_rate_44100 = 44100, - - ma_standard_sample_rate_32000 = 32000, /* Lows */ - ma_standard_sample_rate_24000 = 24000, - ma_standard_sample_rate_22050 = 22050, - - ma_standard_sample_rate_88200 = 88200, /* Highs */ - ma_standard_sample_rate_96000 = 96000, - ma_standard_sample_rate_176400 = 176400, - ma_standard_sample_rate_192000 = 192000, - - ma_standard_sample_rate_16000 = 16000, /* Extreme lows */ - ma_standard_sample_rate_11025 = 11025, - ma_standard_sample_rate_8000 = 8000, - - ma_standard_sample_rate_352800 = 352800, /* Extreme highs */ - ma_standard_sample_rate_384000 = 384000, - - ma_standard_sample_rate_min = ma_standard_sample_rate_8000, - ma_standard_sample_rate_max = ma_standard_sample_rate_384000, - ma_standard_sample_rate_count = 14 /* Need to maintain the count manually. Make sure this is updated if items are added to enum. */ -} ma_standard_sample_rate; - - -typedef enum -{ - ma_channel_mix_mode_rectangular = 0, /* Simple averaging based on the plane(s) the channel is sitting on. */ - ma_channel_mix_mode_simple, /* Drop excess channels; zeroed out extra channels. */ - ma_channel_mix_mode_custom_weights, /* Use custom weights specified in ma_channel_converter_config. */ - ma_channel_mix_mode_default = ma_channel_mix_mode_rectangular -} ma_channel_mix_mode; - -typedef enum -{ - ma_standard_channel_map_microsoft, - ma_standard_channel_map_alsa, - ma_standard_channel_map_rfc3551, /* Based off AIFF. */ - ma_standard_channel_map_flac, - ma_standard_channel_map_vorbis, - ma_standard_channel_map_sound4, /* FreeBSD's sound(4). */ - ma_standard_channel_map_sndio, /* www.sndio.org/tips.html */ - ma_standard_channel_map_webaudio = ma_standard_channel_map_flac, /* https://webaudio.github.io/web-audio-api/#ChannelOrdering. Only 1, 2, 4 and 6 channels are defined, but can fill in the gaps with logical assumptions. */ - ma_standard_channel_map_default = ma_standard_channel_map_microsoft -} ma_standard_channel_map; - -typedef enum -{ - ma_performance_profile_low_latency = 0, - ma_performance_profile_conservative -} ma_performance_profile; - - -typedef struct -{ - void* pUserData; - void* (* onMalloc)(size_t sz, void* pUserData); - void* (* onRealloc)(void* p, size_t sz, void* pUserData); - void (* onFree)(void* p, void* pUserData); -} ma_allocation_callbacks; - -typedef struct -{ - ma_int32 state; -} ma_lcg; - - -/* -Atomics. - -These are typesafe structures to prevent errors as a result of forgetting to reference variables atomically. It's too -easy to introduce subtle bugs where you accidentally do a regular assignment instead of an atomic load/store, etc. By -using a struct we can enforce the use of atomics at compile time. - -These types are declared in the header section because we need to reference them in structs below, but functions for -using them are only exposed in the implementation section. I do not want these to be part of the public API. - -There's a few downsides to this system. The first is that you need to declare a new struct for each type. Below are -some macros to help with the declarations. They will be named like so: - - ma_atomic_uint32 - atomic ma_uint32 - ma_atomic_int32 - atomic ma_int32 - ma_atomic_uint64 - atomic ma_uint64 - ma_atomic_float - atomic float - ma_atomic_bool32 - atomic ma_bool32 - -The other downside is that atomic pointers are extremely messy. You need to declare a new struct for each specific -type of pointer you need to make atomic. For example, an atomic ma_node* will look like this: - - MA_ATOMIC_SAFE_TYPE_IMPL_PTR(node) - -Which will declare a type struct that's named like so: - - ma_atomic_ptr_node - -Functions to use the atomic types are declared in the implementation section. All atomic functions are prefixed with -the name of the struct. For example: - - ma_atomic_uint32_set() - Atomic store of ma_uint32 - ma_atomic_uint32_get() - Atomic load of ma_uint32 - etc. - -For pointer types it's the same, which makes them a bit messy to use due to the length of each function name, but in -return you get type safety and enforcement of atomic operations. -*/ -#define MA_ATOMIC_SAFE_TYPE_DECL(c89TypeExtension, typeSize, type) \ - typedef struct \ - { \ - MA_ATOMIC(typeSize, ma_##type) value; \ - } ma_atomic_##type; \ - -#define MA_ATOMIC_SAFE_TYPE_DECL_PTR(type) \ - typedef struct \ - { \ - MA_ATOMIC(MA_SIZEOF_PTR, ma_##type*) value; \ - } ma_atomic_ptr_##type; \ - -MA_ATOMIC_SAFE_TYPE_DECL(32, 4, uint32) -MA_ATOMIC_SAFE_TYPE_DECL(i32, 4, int32) -MA_ATOMIC_SAFE_TYPE_DECL(64, 8, uint64) -MA_ATOMIC_SAFE_TYPE_DECL(f32, 4, float) -MA_ATOMIC_SAFE_TYPE_DECL(32, 4, bool32) - - -/* Spinlocks are 32-bit for compatibility reasons. */ -typedef ma_uint32 ma_spinlock; - -#ifndef MA_NO_THREADING - /* Thread priorities should be ordered such that the default priority of the worker thread is 0. */ - typedef enum - { - ma_thread_priority_idle = -5, - ma_thread_priority_lowest = -4, - ma_thread_priority_low = -3, - ma_thread_priority_normal = -2, - ma_thread_priority_high = -1, - ma_thread_priority_highest = 0, - ma_thread_priority_realtime = 1, - ma_thread_priority_default = 0 - } ma_thread_priority; - - #if defined(MA_POSIX) - typedef ma_pthread_t ma_thread; - #elif defined(MA_WIN32) - typedef ma_handle ma_thread; - #endif - - #if defined(MA_POSIX) - typedef ma_pthread_mutex_t ma_mutex; - #elif defined(MA_WIN32) - typedef ma_handle ma_mutex; - #endif - - #if defined(MA_POSIX) - typedef struct - { - ma_uint32 value; - ma_pthread_mutex_t lock; - ma_pthread_cond_t cond; - } ma_event; - #elif defined(MA_WIN32) - typedef ma_handle ma_event; - #endif - - #if defined(MA_POSIX) - typedef struct - { - int value; - ma_pthread_mutex_t lock; - ma_pthread_cond_t cond; - } ma_semaphore; - #elif defined(MA_WIN32) - typedef ma_handle ma_semaphore; - #endif -#else - /* MA_NO_THREADING is set which means threading is disabled. Threading is required by some API families. If any of these are enabled we need to throw an error. */ - #ifndef MA_NO_DEVICE_IO - #error "MA_NO_THREADING cannot be used without MA_NO_DEVICE_IO"; - #endif -#endif /* MA_NO_THREADING */ - - -/* -Retrieves the version of miniaudio as separated integers. Each component can be NULL if it's not required. -*/ -MA_API void ma_version(ma_uint32* pMajor, ma_uint32* pMinor, ma_uint32* pRevision); - -/* -Retrieves the version of miniaudio as a string which can be useful for logging purposes. -*/ -MA_API const char* ma_version_string(void); - - -/************************************************************************************************************************************************************** - -Logging - -**************************************************************************************************************************************************************/ -#include /* For va_list. */ - -#if defined(__has_attribute) - #if __has_attribute(format) - #define MA_ATTRIBUTE_FORMAT(fmt, va) __attribute__((format(printf, fmt, va))) - #endif -#endif -#ifndef MA_ATTRIBUTE_FORMAT -#define MA_ATTRIBUTE_FORMAT(fmt, va) -#endif - -#ifndef MA_MAX_LOG_CALLBACKS -#define MA_MAX_LOG_CALLBACKS 4 -#endif - - -/* -The callback for handling log messages. - - -Parameters ----------- -pUserData (in) - The user data pointer that was passed into ma_log_register_callback(). - -logLevel (in) - The log level. This can be one of the following: - - +----------------------+ - | Log Level | - +----------------------+ - | MA_LOG_LEVEL_DEBUG | - | MA_LOG_LEVEL_INFO | - | MA_LOG_LEVEL_WARNING | - | MA_LOG_LEVEL_ERROR | - +----------------------+ - -pMessage (in) - The log message. -*/ -typedef void (* ma_log_callback_proc)(void* pUserData, ma_uint32 level, const char* pMessage); - -typedef struct -{ - ma_log_callback_proc onLog; - void* pUserData; -} ma_log_callback; - -MA_API ma_log_callback ma_log_callback_init(ma_log_callback_proc onLog, void* pUserData); - - -typedef struct -{ - ma_log_callback callbacks[MA_MAX_LOG_CALLBACKS]; - ma_uint32 callbackCount; - ma_allocation_callbacks allocationCallbacks; /* Need to store these persistently because ma_log_postv() might need to allocate a buffer on the heap. */ -#ifndef MA_NO_THREADING - ma_mutex lock; /* For thread safety just to make it easier and safer for the logging implementation. */ -#endif -} ma_log; - -MA_API ma_result ma_log_init(const ma_allocation_callbacks* pAllocationCallbacks, ma_log* pLog); -MA_API void ma_log_uninit(ma_log* pLog); -MA_API ma_result ma_log_register_callback(ma_log* pLog, ma_log_callback callback); -MA_API ma_result ma_log_unregister_callback(ma_log* pLog, ma_log_callback callback); -MA_API ma_result ma_log_post(ma_log* pLog, ma_uint32 level, const char* pMessage); -MA_API ma_result ma_log_postv(ma_log* pLog, ma_uint32 level, const char* pFormat, va_list args); -MA_API ma_result ma_log_postf(ma_log* pLog, ma_uint32 level, const char* pFormat, ...) MA_ATTRIBUTE_FORMAT(3, 4); - - -/************************************************************************************************************************************************************** - -Biquad Filtering - -**************************************************************************************************************************************************************/ -typedef union -{ - float f32; - ma_int32 s32; -} ma_biquad_coefficient; - -typedef struct -{ - ma_format format; - ma_uint32 channels; - double b0; - double b1; - double b2; - double a0; - double a1; - double a2; -} ma_biquad_config; - -MA_API ma_biquad_config ma_biquad_config_init(ma_format format, ma_uint32 channels, double b0, double b1, double b2, double a0, double a1, double a2); - -typedef struct -{ - ma_format format; - ma_uint32 channels; - ma_biquad_coefficient b0; - ma_biquad_coefficient b1; - ma_biquad_coefficient b2; - ma_biquad_coefficient a1; - ma_biquad_coefficient a2; - ma_biquad_coefficient* pR1; - ma_biquad_coefficient* pR2; - - /* Memory management. */ - void* _pHeap; - ma_bool32 _ownsHeap; -} ma_biquad; - -MA_API ma_result ma_biquad_get_heap_size(const ma_biquad_config* pConfig, size_t* pHeapSizeInBytes); -MA_API ma_result ma_biquad_init_preallocated(const ma_biquad_config* pConfig, void* pHeap, ma_biquad* pBQ); -MA_API ma_result ma_biquad_init(const ma_biquad_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_biquad* pBQ); -MA_API void ma_biquad_uninit(ma_biquad* pBQ, const ma_allocation_callbacks* pAllocationCallbacks); -MA_API ma_result ma_biquad_reinit(const ma_biquad_config* pConfig, ma_biquad* pBQ); -MA_API ma_result ma_biquad_clear_cache(ma_biquad* pBQ); -MA_API ma_result ma_biquad_process_pcm_frames(ma_biquad* pBQ, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount); -MA_API ma_uint32 ma_biquad_get_latency(const ma_biquad* pBQ); - - -/************************************************************************************************************************************************************** - -Low-Pass Filtering - -**************************************************************************************************************************************************************/ -typedef struct -{ - ma_format format; - ma_uint32 channels; - ma_uint32 sampleRate; - double cutoffFrequency; - double q; -} ma_lpf1_config, ma_lpf2_config; - -MA_API ma_lpf1_config ma_lpf1_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRate, double cutoffFrequency); -MA_API ma_lpf2_config ma_lpf2_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRate, double cutoffFrequency, double q); - -typedef struct -{ - ma_format format; - ma_uint32 channels; - ma_biquad_coefficient a; - ma_biquad_coefficient* pR1; - - /* Memory management. */ - void* _pHeap; - ma_bool32 _ownsHeap; -} ma_lpf1; - -MA_API ma_result ma_lpf1_get_heap_size(const ma_lpf1_config* pConfig, size_t* pHeapSizeInBytes); -MA_API ma_result ma_lpf1_init_preallocated(const ma_lpf1_config* pConfig, void* pHeap, ma_lpf1* pLPF); -MA_API ma_result ma_lpf1_init(const ma_lpf1_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_lpf1* pLPF); -MA_API void ma_lpf1_uninit(ma_lpf1* pLPF, const ma_allocation_callbacks* pAllocationCallbacks); -MA_API ma_result ma_lpf1_reinit(const ma_lpf1_config* pConfig, ma_lpf1* pLPF); -MA_API ma_result ma_lpf1_clear_cache(ma_lpf1* pLPF); -MA_API ma_result ma_lpf1_process_pcm_frames(ma_lpf1* pLPF, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount); -MA_API ma_uint32 ma_lpf1_get_latency(const ma_lpf1* pLPF); - -typedef struct -{ - ma_biquad bq; /* The second order low-pass filter is implemented as a biquad filter. */ -} ma_lpf2; - -MA_API ma_result ma_lpf2_get_heap_size(const ma_lpf2_config* pConfig, size_t* pHeapSizeInBytes); -MA_API ma_result ma_lpf2_init_preallocated(const ma_lpf2_config* pConfig, void* pHeap, ma_lpf2* pHPF); -MA_API ma_result ma_lpf2_init(const ma_lpf2_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_lpf2* pLPF); -MA_API void ma_lpf2_uninit(ma_lpf2* pLPF, const ma_allocation_callbacks* pAllocationCallbacks); -MA_API ma_result ma_lpf2_reinit(const ma_lpf2_config* pConfig, ma_lpf2* pLPF); -MA_API ma_result ma_lpf2_clear_cache(ma_lpf2* pLPF); -MA_API ma_result ma_lpf2_process_pcm_frames(ma_lpf2* pLPF, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount); -MA_API ma_uint32 ma_lpf2_get_latency(const ma_lpf2* pLPF); - - -typedef struct -{ - ma_format format; - ma_uint32 channels; - ma_uint32 sampleRate; - double cutoffFrequency; - ma_uint32 order; /* If set to 0, will be treated as a passthrough (no filtering will be applied). */ -} ma_lpf_config; - -MA_API ma_lpf_config ma_lpf_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRate, double cutoffFrequency, ma_uint32 order); - -typedef struct -{ - ma_format format; - ma_uint32 channels; - ma_uint32 sampleRate; - ma_uint32 lpf1Count; - ma_uint32 lpf2Count; - ma_lpf1* pLPF1; - ma_lpf2* pLPF2; - - /* Memory management. */ - void* _pHeap; - ma_bool32 _ownsHeap; -} ma_lpf; - -MA_API ma_result ma_lpf_get_heap_size(const ma_lpf_config* pConfig, size_t* pHeapSizeInBytes); -MA_API ma_result ma_lpf_init_preallocated(const ma_lpf_config* pConfig, void* pHeap, ma_lpf* pLPF); -MA_API ma_result ma_lpf_init(const ma_lpf_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_lpf* pLPF); -MA_API void ma_lpf_uninit(ma_lpf* pLPF, const ma_allocation_callbacks* pAllocationCallbacks); -MA_API ma_result ma_lpf_reinit(const ma_lpf_config* pConfig, ma_lpf* pLPF); -MA_API ma_result ma_lpf_clear_cache(ma_lpf* pLPF); -MA_API ma_result ma_lpf_process_pcm_frames(ma_lpf* pLPF, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount); -MA_API ma_uint32 ma_lpf_get_latency(const ma_lpf* pLPF); - - -/************************************************************************************************************************************************************** - -High-Pass Filtering - -**************************************************************************************************************************************************************/ -typedef struct -{ - ma_format format; - ma_uint32 channels; - ma_uint32 sampleRate; - double cutoffFrequency; - double q; -} ma_hpf1_config, ma_hpf2_config; - -MA_API ma_hpf1_config ma_hpf1_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRate, double cutoffFrequency); -MA_API ma_hpf2_config ma_hpf2_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRate, double cutoffFrequency, double q); - -typedef struct -{ - ma_format format; - ma_uint32 channels; - ma_biquad_coefficient a; - ma_biquad_coefficient* pR1; - - /* Memory management. */ - void* _pHeap; - ma_bool32 _ownsHeap; -} ma_hpf1; - -MA_API ma_result ma_hpf1_get_heap_size(const ma_hpf1_config* pConfig, size_t* pHeapSizeInBytes); -MA_API ma_result ma_hpf1_init_preallocated(const ma_hpf1_config* pConfig, void* pHeap, ma_hpf1* pLPF); -MA_API ma_result ma_hpf1_init(const ma_hpf1_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_hpf1* pHPF); -MA_API void ma_hpf1_uninit(ma_hpf1* pHPF, const ma_allocation_callbacks* pAllocationCallbacks); -MA_API ma_result ma_hpf1_reinit(const ma_hpf1_config* pConfig, ma_hpf1* pHPF); -MA_API ma_result ma_hpf1_process_pcm_frames(ma_hpf1* pHPF, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount); -MA_API ma_uint32 ma_hpf1_get_latency(const ma_hpf1* pHPF); - -typedef struct -{ - ma_biquad bq; /* The second order high-pass filter is implemented as a biquad filter. */ -} ma_hpf2; - -MA_API ma_result ma_hpf2_get_heap_size(const ma_hpf2_config* pConfig, size_t* pHeapSizeInBytes); -MA_API ma_result ma_hpf2_init_preallocated(const ma_hpf2_config* pConfig, void* pHeap, ma_hpf2* pHPF); -MA_API ma_result ma_hpf2_init(const ma_hpf2_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_hpf2* pHPF); -MA_API void ma_hpf2_uninit(ma_hpf2* pHPF, const ma_allocation_callbacks* pAllocationCallbacks); -MA_API ma_result ma_hpf2_reinit(const ma_hpf2_config* pConfig, ma_hpf2* pHPF); -MA_API ma_result ma_hpf2_process_pcm_frames(ma_hpf2* pHPF, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount); -MA_API ma_uint32 ma_hpf2_get_latency(const ma_hpf2* pHPF); - - -typedef struct -{ - ma_format format; - ma_uint32 channels; - ma_uint32 sampleRate; - double cutoffFrequency; - ma_uint32 order; /* If set to 0, will be treated as a passthrough (no filtering will be applied). */ -} ma_hpf_config; - -MA_API ma_hpf_config ma_hpf_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRate, double cutoffFrequency, ma_uint32 order); - -typedef struct -{ - ma_format format; - ma_uint32 channels; - ma_uint32 sampleRate; - ma_uint32 hpf1Count; - ma_uint32 hpf2Count; - ma_hpf1* pHPF1; - ma_hpf2* pHPF2; - - /* Memory management. */ - void* _pHeap; - ma_bool32 _ownsHeap; -} ma_hpf; - -MA_API ma_result ma_hpf_get_heap_size(const ma_hpf_config* pConfig, size_t* pHeapSizeInBytes); -MA_API ma_result ma_hpf_init_preallocated(const ma_hpf_config* pConfig, void* pHeap, ma_hpf* pLPF); -MA_API ma_result ma_hpf_init(const ma_hpf_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_hpf* pHPF); -MA_API void ma_hpf_uninit(ma_hpf* pHPF, const ma_allocation_callbacks* pAllocationCallbacks); -MA_API ma_result ma_hpf_reinit(const ma_hpf_config* pConfig, ma_hpf* pHPF); -MA_API ma_result ma_hpf_process_pcm_frames(ma_hpf* pHPF, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount); -MA_API ma_uint32 ma_hpf_get_latency(const ma_hpf* pHPF); - - -/************************************************************************************************************************************************************** - -Band-Pass Filtering - -**************************************************************************************************************************************************************/ -typedef struct -{ - ma_format format; - ma_uint32 channels; - ma_uint32 sampleRate; - double cutoffFrequency; - double q; -} ma_bpf2_config; - -MA_API ma_bpf2_config ma_bpf2_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRate, double cutoffFrequency, double q); - -typedef struct -{ - ma_biquad bq; /* The second order band-pass filter is implemented as a biquad filter. */ -} ma_bpf2; - -MA_API ma_result ma_bpf2_get_heap_size(const ma_bpf2_config* pConfig, size_t* pHeapSizeInBytes); -MA_API ma_result ma_bpf2_init_preallocated(const ma_bpf2_config* pConfig, void* pHeap, ma_bpf2* pBPF); -MA_API ma_result ma_bpf2_init(const ma_bpf2_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_bpf2* pBPF); -MA_API void ma_bpf2_uninit(ma_bpf2* pBPF, const ma_allocation_callbacks* pAllocationCallbacks); -MA_API ma_result ma_bpf2_reinit(const ma_bpf2_config* pConfig, ma_bpf2* pBPF); -MA_API ma_result ma_bpf2_process_pcm_frames(ma_bpf2* pBPF, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount); -MA_API ma_uint32 ma_bpf2_get_latency(const ma_bpf2* pBPF); - - -typedef struct -{ - ma_format format; - ma_uint32 channels; - ma_uint32 sampleRate; - double cutoffFrequency; - ma_uint32 order; /* If set to 0, will be treated as a passthrough (no filtering will be applied). */ -} ma_bpf_config; - -MA_API ma_bpf_config ma_bpf_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRate, double cutoffFrequency, ma_uint32 order); - -typedef struct -{ - ma_format format; - ma_uint32 channels; - ma_uint32 bpf2Count; - ma_bpf2* pBPF2; - - /* Memory management. */ - void* _pHeap; - ma_bool32 _ownsHeap; -} ma_bpf; - -MA_API ma_result ma_bpf_get_heap_size(const ma_bpf_config* pConfig, size_t* pHeapSizeInBytes); -MA_API ma_result ma_bpf_init_preallocated(const ma_bpf_config* pConfig, void* pHeap, ma_bpf* pBPF); -MA_API ma_result ma_bpf_init(const ma_bpf_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_bpf* pBPF); -MA_API void ma_bpf_uninit(ma_bpf* pBPF, const ma_allocation_callbacks* pAllocationCallbacks); -MA_API ma_result ma_bpf_reinit(const ma_bpf_config* pConfig, ma_bpf* pBPF); -MA_API ma_result ma_bpf_process_pcm_frames(ma_bpf* pBPF, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount); -MA_API ma_uint32 ma_bpf_get_latency(const ma_bpf* pBPF); - - -/************************************************************************************************************************************************************** - -Notching Filter - -**************************************************************************************************************************************************************/ -typedef struct -{ - ma_format format; - ma_uint32 channels; - ma_uint32 sampleRate; - double q; - double frequency; -} ma_notch2_config, ma_notch_config; - -MA_API ma_notch2_config ma_notch2_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRate, double q, double frequency); - -typedef struct -{ - ma_biquad bq; -} ma_notch2; - -MA_API ma_result ma_notch2_get_heap_size(const ma_notch2_config* pConfig, size_t* pHeapSizeInBytes); -MA_API ma_result ma_notch2_init_preallocated(const ma_notch2_config* pConfig, void* pHeap, ma_notch2* pFilter); -MA_API ma_result ma_notch2_init(const ma_notch2_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_notch2* pFilter); -MA_API void ma_notch2_uninit(ma_notch2* pFilter, const ma_allocation_callbacks* pAllocationCallbacks); -MA_API ma_result ma_notch2_reinit(const ma_notch2_config* pConfig, ma_notch2* pFilter); -MA_API ma_result ma_notch2_process_pcm_frames(ma_notch2* pFilter, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount); -MA_API ma_uint32 ma_notch2_get_latency(const ma_notch2* pFilter); - - -/************************************************************************************************************************************************************** - -Peaking EQ Filter - -**************************************************************************************************************************************************************/ -typedef struct -{ - ma_format format; - ma_uint32 channels; - ma_uint32 sampleRate; - double gainDB; - double q; - double frequency; -} ma_peak2_config, ma_peak_config; - -MA_API ma_peak2_config ma_peak2_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRate, double gainDB, double q, double frequency); - -typedef struct -{ - ma_biquad bq; -} ma_peak2; - -MA_API ma_result ma_peak2_get_heap_size(const ma_peak2_config* pConfig, size_t* pHeapSizeInBytes); -MA_API ma_result ma_peak2_init_preallocated(const ma_peak2_config* pConfig, void* pHeap, ma_peak2* pFilter); -MA_API ma_result ma_peak2_init(const ma_peak2_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_peak2* pFilter); -MA_API void ma_peak2_uninit(ma_peak2* pFilter, const ma_allocation_callbacks* pAllocationCallbacks); -MA_API ma_result ma_peak2_reinit(const ma_peak2_config* pConfig, ma_peak2* pFilter); -MA_API ma_result ma_peak2_process_pcm_frames(ma_peak2* pFilter, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount); -MA_API ma_uint32 ma_peak2_get_latency(const ma_peak2* pFilter); - - -/************************************************************************************************************************************************************** - -Low Shelf Filter - -**************************************************************************************************************************************************************/ -typedef struct -{ - ma_format format; - ma_uint32 channels; - ma_uint32 sampleRate; - double gainDB; - double shelfSlope; - double frequency; -} ma_loshelf2_config, ma_loshelf_config; - -MA_API ma_loshelf2_config ma_loshelf2_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRate, double gainDB, double shelfSlope, double frequency); - -typedef struct -{ - ma_biquad bq; -} ma_loshelf2; - -MA_API ma_result ma_loshelf2_get_heap_size(const ma_loshelf2_config* pConfig, size_t* pHeapSizeInBytes); -MA_API ma_result ma_loshelf2_init_preallocated(const ma_loshelf2_config* pConfig, void* pHeap, ma_loshelf2* pFilter); -MA_API ma_result ma_loshelf2_init(const ma_loshelf2_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_loshelf2* pFilter); -MA_API void ma_loshelf2_uninit(ma_loshelf2* pFilter, const ma_allocation_callbacks* pAllocationCallbacks); -MA_API ma_result ma_loshelf2_reinit(const ma_loshelf2_config* pConfig, ma_loshelf2* pFilter); -MA_API ma_result ma_loshelf2_process_pcm_frames(ma_loshelf2* pFilter, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount); -MA_API ma_uint32 ma_loshelf2_get_latency(const ma_loshelf2* pFilter); - - -/************************************************************************************************************************************************************** - -High Shelf Filter - -**************************************************************************************************************************************************************/ -typedef struct -{ - ma_format format; - ma_uint32 channels; - ma_uint32 sampleRate; - double gainDB; - double shelfSlope; - double frequency; -} ma_hishelf2_config, ma_hishelf_config; - -MA_API ma_hishelf2_config ma_hishelf2_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRate, double gainDB, double shelfSlope, double frequency); - -typedef struct -{ - ma_biquad bq; -} ma_hishelf2; - -MA_API ma_result ma_hishelf2_get_heap_size(const ma_hishelf2_config* pConfig, size_t* pHeapSizeInBytes); -MA_API ma_result ma_hishelf2_init_preallocated(const ma_hishelf2_config* pConfig, void* pHeap, ma_hishelf2* pFilter); -MA_API ma_result ma_hishelf2_init(const ma_hishelf2_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_hishelf2* pFilter); -MA_API void ma_hishelf2_uninit(ma_hishelf2* pFilter, const ma_allocation_callbacks* pAllocationCallbacks); -MA_API ma_result ma_hishelf2_reinit(const ma_hishelf2_config* pConfig, ma_hishelf2* pFilter); -MA_API ma_result ma_hishelf2_process_pcm_frames(ma_hishelf2* pFilter, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount); -MA_API ma_uint32 ma_hishelf2_get_latency(const ma_hishelf2* pFilter); - - - -/* -Delay -*/ -typedef struct -{ - ma_uint32 channels; - ma_uint32 sampleRate; - ma_uint32 delayInFrames; - ma_bool32 delayStart; /* Set to true to delay the start of the output; false otherwise. */ - float wet; /* 0..1. Default = 1. */ - float dry; /* 0..1. Default = 1. */ - float decay; /* 0..1. Default = 0 (no feedback). Feedback decay. Use this for echo. */ -} ma_delay_config; - -MA_API ma_delay_config ma_delay_config_init(ma_uint32 channels, ma_uint32 sampleRate, ma_uint32 delayInFrames, float decay); - - -typedef struct -{ - ma_delay_config config; - ma_uint32 cursor; /* Feedback is written to this cursor. Always equal or in front of the read cursor. */ - ma_uint32 bufferSizeInFrames; - float* pBuffer; -} ma_delay; - -MA_API ma_result ma_delay_init(const ma_delay_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_delay* pDelay); -MA_API void ma_delay_uninit(ma_delay* pDelay, const ma_allocation_callbacks* pAllocationCallbacks); -MA_API ma_result ma_delay_process_pcm_frames(ma_delay* pDelay, void* pFramesOut, const void* pFramesIn, ma_uint32 frameCount); -MA_API void ma_delay_set_wet(ma_delay* pDelay, float value); -MA_API float ma_delay_get_wet(const ma_delay* pDelay); -MA_API void ma_delay_set_dry(ma_delay* pDelay, float value); -MA_API float ma_delay_get_dry(const ma_delay* pDelay); -MA_API void ma_delay_set_decay(ma_delay* pDelay, float value); -MA_API float ma_delay_get_decay(const ma_delay* pDelay); - - -/* Gainer for smooth volume changes. */ -typedef struct -{ - ma_uint32 channels; - ma_uint32 smoothTimeInFrames; -} ma_gainer_config; - -MA_API ma_gainer_config ma_gainer_config_init(ma_uint32 channels, ma_uint32 smoothTimeInFrames); - - -typedef struct -{ - ma_gainer_config config; - ma_uint32 t; - float masterVolume; - float* pOldGains; - float* pNewGains; - - /* Memory management. */ - void* _pHeap; - ma_bool32 _ownsHeap; -} ma_gainer; - -MA_API ma_result ma_gainer_get_heap_size(const ma_gainer_config* pConfig, size_t* pHeapSizeInBytes); -MA_API ma_result ma_gainer_init_preallocated(const ma_gainer_config* pConfig, void* pHeap, ma_gainer* pGainer); -MA_API ma_result ma_gainer_init(const ma_gainer_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_gainer* pGainer); -MA_API void ma_gainer_uninit(ma_gainer* pGainer, const ma_allocation_callbacks* pAllocationCallbacks); -MA_API ma_result ma_gainer_process_pcm_frames(ma_gainer* pGainer, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount); -MA_API ma_result ma_gainer_set_gain(ma_gainer* pGainer, float newGain); -MA_API ma_result ma_gainer_set_gains(ma_gainer* pGainer, float* pNewGains); -MA_API ma_result ma_gainer_set_master_volume(ma_gainer* pGainer, float volume); -MA_API ma_result ma_gainer_get_master_volume(const ma_gainer* pGainer, float* pVolume); - - - -/* Stereo panner. */ -typedef enum -{ - ma_pan_mode_balance = 0, /* Does not blend one side with the other. Technically just a balance. Compatible with other popular audio engines and therefore the default. */ - ma_pan_mode_pan /* A true pan. The sound from one side will "move" to the other side and blend with it. */ -} ma_pan_mode; - -typedef struct -{ - ma_format format; - ma_uint32 channels; - ma_pan_mode mode; - float pan; -} ma_panner_config; - -MA_API ma_panner_config ma_panner_config_init(ma_format format, ma_uint32 channels); - - -typedef struct -{ - ma_format format; - ma_uint32 channels; - ma_pan_mode mode; - float pan; /* -1..1 where 0 is no pan, -1 is left side, +1 is right side. Defaults to 0. */ -} ma_panner; - -MA_API ma_result ma_panner_init(const ma_panner_config* pConfig, ma_panner* pPanner); -MA_API ma_result ma_panner_process_pcm_frames(ma_panner* pPanner, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount); -MA_API void ma_panner_set_mode(ma_panner* pPanner, ma_pan_mode mode); -MA_API ma_pan_mode ma_panner_get_mode(const ma_panner* pPanner); -MA_API void ma_panner_set_pan(ma_panner* pPanner, float pan); -MA_API float ma_panner_get_pan(const ma_panner* pPanner); - - - -/* Fader. */ -typedef struct -{ - ma_format format; - ma_uint32 channels; - ma_uint32 sampleRate; -} ma_fader_config; - -MA_API ma_fader_config ma_fader_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRate); - -typedef struct -{ - ma_fader_config config; - float volumeBeg; /* If volumeBeg and volumeEnd is equal to 1, no fading happens (ma_fader_process_pcm_frames() will run as a passthrough). */ - float volumeEnd; - ma_uint64 lengthInFrames; /* The total length of the fade. */ - ma_int64 cursorInFrames; /* The current time in frames. Incremented by ma_fader_process_pcm_frames(). Signed because it'll be offset by startOffsetInFrames in set_fade_ex(). */ -} ma_fader; - -MA_API ma_result ma_fader_init(const ma_fader_config* pConfig, ma_fader* pFader); -MA_API ma_result ma_fader_process_pcm_frames(ma_fader* pFader, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount); -MA_API void ma_fader_get_data_format(const ma_fader* pFader, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate); -MA_API void ma_fader_set_fade(ma_fader* pFader, float volumeBeg, float volumeEnd, ma_uint64 lengthInFrames); -MA_API void ma_fader_set_fade_ex(ma_fader* pFader, float volumeBeg, float volumeEnd, ma_uint64 lengthInFrames, ma_int64 startOffsetInFrames); -MA_API float ma_fader_get_current_volume(const ma_fader* pFader); - - - -/* Spatializer. */ -typedef struct -{ - float x; - float y; - float z; -} ma_vec3f; - -typedef struct -{ - ma_vec3f v; - ma_spinlock lock; -} ma_atomic_vec3f; - -typedef enum -{ - ma_attenuation_model_none, /* No distance attenuation and no spatialization. */ - ma_attenuation_model_inverse, /* Equivalent to OpenAL's AL_INVERSE_DISTANCE_CLAMPED. */ - ma_attenuation_model_linear, /* Linear attenuation. Equivalent to OpenAL's AL_LINEAR_DISTANCE_CLAMPED. */ - ma_attenuation_model_exponential /* Exponential attenuation. Equivalent to OpenAL's AL_EXPONENT_DISTANCE_CLAMPED. */ -} ma_attenuation_model; - -typedef enum -{ - ma_positioning_absolute, - ma_positioning_relative -} ma_positioning; - -typedef enum -{ - ma_handedness_right, - ma_handedness_left -} ma_handedness; - - -typedef struct -{ - ma_uint32 channelsOut; - ma_channel* pChannelMapOut; - ma_handedness handedness; /* Defaults to right. Forward is -1 on the Z axis. In a left handed system, forward is +1 on the Z axis. */ - float coneInnerAngleInRadians; - float coneOuterAngleInRadians; - float coneOuterGain; - float speedOfSound; - ma_vec3f worldUp; -} ma_spatializer_listener_config; - -MA_API ma_spatializer_listener_config ma_spatializer_listener_config_init(ma_uint32 channelsOut); - - -typedef struct -{ - ma_spatializer_listener_config config; - ma_atomic_vec3f position; /* The absolute position of the listener. */ - ma_atomic_vec3f direction; /* The direction the listener is facing. The world up vector is config.worldUp. */ - ma_atomic_vec3f velocity; - ma_bool32 isEnabled; - - /* Memory management. */ - ma_bool32 _ownsHeap; - void* _pHeap; -} ma_spatializer_listener; - -MA_API ma_result ma_spatializer_listener_get_heap_size(const ma_spatializer_listener_config* pConfig, size_t* pHeapSizeInBytes); -MA_API ma_result ma_spatializer_listener_init_preallocated(const ma_spatializer_listener_config* pConfig, void* pHeap, ma_spatializer_listener* pListener); -MA_API ma_result ma_spatializer_listener_init(const ma_spatializer_listener_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_spatializer_listener* pListener); -MA_API void ma_spatializer_listener_uninit(ma_spatializer_listener* pListener, const ma_allocation_callbacks* pAllocationCallbacks); -MA_API ma_channel* ma_spatializer_listener_get_channel_map(ma_spatializer_listener* pListener); -MA_API void ma_spatializer_listener_set_cone(ma_spatializer_listener* pListener, float innerAngleInRadians, float outerAngleInRadians, float outerGain); -MA_API void ma_spatializer_listener_get_cone(const ma_spatializer_listener* pListener, float* pInnerAngleInRadians, float* pOuterAngleInRadians, float* pOuterGain); -MA_API void ma_spatializer_listener_set_position(ma_spatializer_listener* pListener, float x, float y, float z); -MA_API ma_vec3f ma_spatializer_listener_get_position(const ma_spatializer_listener* pListener); -MA_API void ma_spatializer_listener_set_direction(ma_spatializer_listener* pListener, float x, float y, float z); -MA_API ma_vec3f ma_spatializer_listener_get_direction(const ma_spatializer_listener* pListener); -MA_API void ma_spatializer_listener_set_velocity(ma_spatializer_listener* pListener, float x, float y, float z); -MA_API ma_vec3f ma_spatializer_listener_get_velocity(const ma_spatializer_listener* pListener); -MA_API void ma_spatializer_listener_set_speed_of_sound(ma_spatializer_listener* pListener, float speedOfSound); -MA_API float ma_spatializer_listener_get_speed_of_sound(const ma_spatializer_listener* pListener); -MA_API void ma_spatializer_listener_set_world_up(ma_spatializer_listener* pListener, float x, float y, float z); -MA_API ma_vec3f ma_spatializer_listener_get_world_up(const ma_spatializer_listener* pListener); -MA_API void ma_spatializer_listener_set_enabled(ma_spatializer_listener* pListener, ma_bool32 isEnabled); -MA_API ma_bool32 ma_spatializer_listener_is_enabled(const ma_spatializer_listener* pListener); - - -typedef struct -{ - ma_uint32 channelsIn; - ma_uint32 channelsOut; - ma_channel* pChannelMapIn; - ma_attenuation_model attenuationModel; - ma_positioning positioning; - ma_handedness handedness; /* Defaults to right. Forward is -1 on the Z axis. In a left handed system, forward is +1 on the Z axis. */ - float minGain; - float maxGain; - float minDistance; - float maxDistance; - float rolloff; - float coneInnerAngleInRadians; - float coneOuterAngleInRadians; - float coneOuterGain; - float dopplerFactor; /* Set to 0 to disable doppler effect. */ - float directionalAttenuationFactor; /* Set to 0 to disable directional attenuation. */ - float minSpatializationChannelGain; /* The minimal scaling factor to apply to channel gains when accounting for the direction of the sound relative to the listener. Must be in the range of 0..1. Smaller values means more aggressive directional panning, larger values means more subtle directional panning. */ - ma_uint32 gainSmoothTimeInFrames; /* When the gain of a channel changes during spatialization, the transition will be linearly interpolated over this number of frames. */ -} ma_spatializer_config; - -MA_API ma_spatializer_config ma_spatializer_config_init(ma_uint32 channelsIn, ma_uint32 channelsOut); - - -typedef struct -{ - ma_uint32 channelsIn; - ma_uint32 channelsOut; - ma_channel* pChannelMapIn; - ma_attenuation_model attenuationModel; - ma_positioning positioning; - ma_handedness handedness; /* Defaults to right. Forward is -1 on the Z axis. In a left handed system, forward is +1 on the Z axis. */ - float minGain; - float maxGain; - float minDistance; - float maxDistance; - float rolloff; - float coneInnerAngleInRadians; - float coneOuterAngleInRadians; - float coneOuterGain; - float dopplerFactor; /* Set to 0 to disable doppler effect. */ - float directionalAttenuationFactor; /* Set to 0 to disable directional attenuation. */ - ma_uint32 gainSmoothTimeInFrames; /* When the gain of a channel changes during spatialization, the transition will be linearly interpolated over this number of frames. */ - ma_atomic_vec3f position; - ma_atomic_vec3f direction; - ma_atomic_vec3f velocity; /* For doppler effect. */ - float dopplerPitch; /* Will be updated by ma_spatializer_process_pcm_frames() and can be used by higher level functions to apply a pitch shift for doppler effect. */ - float minSpatializationChannelGain; - ma_gainer gainer; /* For smooth gain transitions. */ - float* pNewChannelGainsOut; /* An offset of _pHeap. Used by ma_spatializer_process_pcm_frames() to store new channel gains. The number of elements in this array is equal to config.channelsOut. */ - - /* Memory management. */ - void* _pHeap; - ma_bool32 _ownsHeap; -} ma_spatializer; - -MA_API ma_result ma_spatializer_get_heap_size(const ma_spatializer_config* pConfig, size_t* pHeapSizeInBytes); -MA_API ma_result ma_spatializer_init_preallocated(const ma_spatializer_config* pConfig, void* pHeap, ma_spatializer* pSpatializer); -MA_API ma_result ma_spatializer_init(const ma_spatializer_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_spatializer* pSpatializer); -MA_API void ma_spatializer_uninit(ma_spatializer* pSpatializer, const ma_allocation_callbacks* pAllocationCallbacks); -MA_API ma_result ma_spatializer_process_pcm_frames(ma_spatializer* pSpatializer, ma_spatializer_listener* pListener, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount); -MA_API ma_result ma_spatializer_set_master_volume(ma_spatializer* pSpatializer, float volume); -MA_API ma_result ma_spatializer_get_master_volume(const ma_spatializer* pSpatializer, float* pVolume); -MA_API ma_uint32 ma_spatializer_get_input_channels(const ma_spatializer* pSpatializer); -MA_API ma_uint32 ma_spatializer_get_output_channels(const ma_spatializer* pSpatializer); -MA_API void ma_spatializer_set_attenuation_model(ma_spatializer* pSpatializer, ma_attenuation_model attenuationModel); -MA_API ma_attenuation_model ma_spatializer_get_attenuation_model(const ma_spatializer* pSpatializer); -MA_API void ma_spatializer_set_positioning(ma_spatializer* pSpatializer, ma_positioning positioning); -MA_API ma_positioning ma_spatializer_get_positioning(const ma_spatializer* pSpatializer); -MA_API void ma_spatializer_set_rolloff(ma_spatializer* pSpatializer, float rolloff); -MA_API float ma_spatializer_get_rolloff(const ma_spatializer* pSpatializer); -MA_API void ma_spatializer_set_min_gain(ma_spatializer* pSpatializer, float minGain); -MA_API float ma_spatializer_get_min_gain(const ma_spatializer* pSpatializer); -MA_API void ma_spatializer_set_max_gain(ma_spatializer* pSpatializer, float maxGain); -MA_API float ma_spatializer_get_max_gain(const ma_spatializer* pSpatializer); -MA_API void ma_spatializer_set_min_distance(ma_spatializer* pSpatializer, float minDistance); -MA_API float ma_spatializer_get_min_distance(const ma_spatializer* pSpatializer); -MA_API void ma_spatializer_set_max_distance(ma_spatializer* pSpatializer, float maxDistance); -MA_API float ma_spatializer_get_max_distance(const ma_spatializer* pSpatializer); -MA_API void ma_spatializer_set_cone(ma_spatializer* pSpatializer, float innerAngleInRadians, float outerAngleInRadians, float outerGain); -MA_API void ma_spatializer_get_cone(const ma_spatializer* pSpatializer, float* pInnerAngleInRadians, float* pOuterAngleInRadians, float* pOuterGain); -MA_API void ma_spatializer_set_doppler_factor(ma_spatializer* pSpatializer, float dopplerFactor); -MA_API float ma_spatializer_get_doppler_factor(const ma_spatializer* pSpatializer); -MA_API void ma_spatializer_set_directional_attenuation_factor(ma_spatializer* pSpatializer, float directionalAttenuationFactor); -MA_API float ma_spatializer_get_directional_attenuation_factor(const ma_spatializer* pSpatializer); -MA_API void ma_spatializer_set_position(ma_spatializer* pSpatializer, float x, float y, float z); -MA_API ma_vec3f ma_spatializer_get_position(const ma_spatializer* pSpatializer); -MA_API void ma_spatializer_set_direction(ma_spatializer* pSpatializer, float x, float y, float z); -MA_API ma_vec3f ma_spatializer_get_direction(const ma_spatializer* pSpatializer); -MA_API void ma_spatializer_set_velocity(ma_spatializer* pSpatializer, float x, float y, float z); -MA_API ma_vec3f ma_spatializer_get_velocity(const ma_spatializer* pSpatializer); -MA_API void ma_spatializer_get_relative_position_and_direction(const ma_spatializer* pSpatializer, const ma_spatializer_listener* pListener, ma_vec3f* pRelativePos, ma_vec3f* pRelativeDir); - - - -/************************************************************************************************************************************************************ -************************************************************************************************************************************************************* - -DATA CONVERSION -=============== - -This section contains the APIs for data conversion. You will find everything here for channel mapping, sample format conversion, resampling, etc. - -************************************************************************************************************************************************************* -************************************************************************************************************************************************************/ - -/************************************************************************************************************************************************************** - -Resampling - -**************************************************************************************************************************************************************/ -typedef struct -{ - ma_format format; - ma_uint32 channels; - ma_uint32 sampleRateIn; - ma_uint32 sampleRateOut; - ma_uint32 lpfOrder; /* The low-pass filter order. Setting this to 0 will disable low-pass filtering. */ - double lpfNyquistFactor; /* 0..1. Defaults to 1. 1 = Half the sampling frequency (Nyquist Frequency), 0.5 = Quarter the sampling frequency (half Nyquest Frequency), etc. */ -} ma_linear_resampler_config; - -MA_API ma_linear_resampler_config ma_linear_resampler_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRateIn, ma_uint32 sampleRateOut); - -typedef struct -{ - ma_linear_resampler_config config; - ma_uint32 inAdvanceInt; - ma_uint32 inAdvanceFrac; - ma_uint32 inTimeInt; - ma_uint32 inTimeFrac; - union - { - float* f32; - ma_int16* s16; - } x0; /* The previous input frame. */ - union - { - float* f32; - ma_int16* s16; - } x1; /* The next input frame. */ - ma_lpf lpf; - - /* Memory management. */ - void* _pHeap; - ma_bool32 _ownsHeap; -} ma_linear_resampler; - -MA_API ma_result ma_linear_resampler_get_heap_size(const ma_linear_resampler_config* pConfig, size_t* pHeapSizeInBytes); -MA_API ma_result ma_linear_resampler_init_preallocated(const ma_linear_resampler_config* pConfig, void* pHeap, ma_linear_resampler* pResampler); -MA_API ma_result ma_linear_resampler_init(const ma_linear_resampler_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_linear_resampler* pResampler); -MA_API void ma_linear_resampler_uninit(ma_linear_resampler* pResampler, const ma_allocation_callbacks* pAllocationCallbacks); -MA_API ma_result ma_linear_resampler_process_pcm_frames(ma_linear_resampler* pResampler, const void* pFramesIn, ma_uint64* pFrameCountIn, void* pFramesOut, ma_uint64* pFrameCountOut); -MA_API ma_result ma_linear_resampler_set_rate(ma_linear_resampler* pResampler, ma_uint32 sampleRateIn, ma_uint32 sampleRateOut); -MA_API ma_result ma_linear_resampler_set_rate_ratio(ma_linear_resampler* pResampler, float ratioInOut); -MA_API ma_uint64 ma_linear_resampler_get_input_latency(const ma_linear_resampler* pResampler); -MA_API ma_uint64 ma_linear_resampler_get_output_latency(const ma_linear_resampler* pResampler); -MA_API ma_result ma_linear_resampler_get_required_input_frame_count(const ma_linear_resampler* pResampler, ma_uint64 outputFrameCount, ma_uint64* pInputFrameCount); -MA_API ma_result ma_linear_resampler_get_expected_output_frame_count(const ma_linear_resampler* pResampler, ma_uint64 inputFrameCount, ma_uint64* pOutputFrameCount); -MA_API ma_result ma_linear_resampler_reset(ma_linear_resampler* pResampler); - - -typedef struct ma_resampler_config ma_resampler_config; - -typedef void ma_resampling_backend; -typedef struct -{ - ma_result (* onGetHeapSize )(void* pUserData, const ma_resampler_config* pConfig, size_t* pHeapSizeInBytes); - ma_result (* onInit )(void* pUserData, const ma_resampler_config* pConfig, void* pHeap, ma_resampling_backend** ppBackend); - void (* onUninit )(void* pUserData, ma_resampling_backend* pBackend, const ma_allocation_callbacks* pAllocationCallbacks); - ma_result (* onProcess )(void* pUserData, ma_resampling_backend* pBackend, const void* pFramesIn, ma_uint64* pFrameCountIn, void* pFramesOut, ma_uint64* pFrameCountOut); - ma_result (* onSetRate )(void* pUserData, ma_resampling_backend* pBackend, ma_uint32 sampleRateIn, ma_uint32 sampleRateOut); /* Optional. Rate changes will be disabled. */ - ma_uint64 (* onGetInputLatency )(void* pUserData, const ma_resampling_backend* pBackend); /* Optional. Latency will be reported as 0. */ - ma_uint64 (* onGetOutputLatency )(void* pUserData, const ma_resampling_backend* pBackend); /* Optional. Latency will be reported as 0. */ - ma_result (* onGetRequiredInputFrameCount )(void* pUserData, const ma_resampling_backend* pBackend, ma_uint64 outputFrameCount, ma_uint64* pInputFrameCount); /* Optional. Latency mitigation will be disabled. */ - ma_result (* onGetExpectedOutputFrameCount)(void* pUserData, const ma_resampling_backend* pBackend, ma_uint64 inputFrameCount, ma_uint64* pOutputFrameCount); /* Optional. Latency mitigation will be disabled. */ - ma_result (* onReset )(void* pUserData, ma_resampling_backend* pBackend); -} ma_resampling_backend_vtable; - -typedef enum -{ - ma_resample_algorithm_linear = 0, /* Fastest, lowest quality. Optional low-pass filtering. Default. */ - ma_resample_algorithm_custom, -} ma_resample_algorithm; - -struct ma_resampler_config -{ - ma_format format; /* Must be either ma_format_f32 or ma_format_s16. */ - ma_uint32 channels; - ma_uint32 sampleRateIn; - ma_uint32 sampleRateOut; - ma_resample_algorithm algorithm; /* When set to ma_resample_algorithm_custom, pBackendVTable will be used. */ - ma_resampling_backend_vtable* pBackendVTable; - void* pBackendUserData; - struct - { - ma_uint32 lpfOrder; - } linear; -}; - -MA_API ma_resampler_config ma_resampler_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRateIn, ma_uint32 sampleRateOut, ma_resample_algorithm algorithm); - -typedef struct -{ - ma_resampling_backend* pBackend; - ma_resampling_backend_vtable* pBackendVTable; - void* pBackendUserData; - ma_format format; - ma_uint32 channels; - ma_uint32 sampleRateIn; - ma_uint32 sampleRateOut; - union - { - ma_linear_resampler linear; - } state; /* State for stock resamplers so we can avoid a malloc. For stock resamplers, pBackend will point here. */ - - /* Memory management. */ - void* _pHeap; - ma_bool32 _ownsHeap; -} ma_resampler; - -MA_API ma_result ma_resampler_get_heap_size(const ma_resampler_config* pConfig, size_t* pHeapSizeInBytes); -MA_API ma_result ma_resampler_init_preallocated(const ma_resampler_config* pConfig, void* pHeap, ma_resampler* pResampler); - -/* -Initializes a new resampler object from a config. -*/ -MA_API ma_result ma_resampler_init(const ma_resampler_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_resampler* pResampler); - -/* -Uninitializes a resampler. -*/ -MA_API void ma_resampler_uninit(ma_resampler* pResampler, const ma_allocation_callbacks* pAllocationCallbacks); - -/* -Converts the given input data. - -Both the input and output frames must be in the format specified in the config when the resampler was initialized. - -On input, [pFrameCountOut] contains the number of output frames to process. On output it contains the number of output frames that -were actually processed, which may be less than the requested amount which will happen if there's not enough input data. You can use -ma_resampler_get_expected_output_frame_count() to know how many output frames will be processed for a given number of input frames. - -On input, [pFrameCountIn] contains the number of input frames contained in [pFramesIn]. On output it contains the number of whole -input frames that were actually processed. You can use ma_resampler_get_required_input_frame_count() to know how many input frames -you should provide for a given number of output frames. [pFramesIn] can be NULL, in which case zeroes will be used instead. - -If [pFramesOut] is NULL, a seek is performed. In this case, if [pFrameCountOut] is not NULL it will seek by the specified number of -output frames. Otherwise, if [pFramesCountOut] is NULL and [pFrameCountIn] is not NULL, it will seek by the specified number of input -frames. When seeking, [pFramesIn] is allowed to NULL, in which case the internal timing state will be updated, but no input will be -processed. In this case, any internal filter state will be updated as if zeroes were passed in. - -It is an error for [pFramesOut] to be non-NULL and [pFrameCountOut] to be NULL. - -It is an error for both [pFrameCountOut] and [pFrameCountIn] to be NULL. -*/ -MA_API ma_result ma_resampler_process_pcm_frames(ma_resampler* pResampler, const void* pFramesIn, ma_uint64* pFrameCountIn, void* pFramesOut, ma_uint64* pFrameCountOut); - - -/* -Sets the input and output sample rate. -*/ -MA_API ma_result ma_resampler_set_rate(ma_resampler* pResampler, ma_uint32 sampleRateIn, ma_uint32 sampleRateOut); - -/* -Sets the input and output sample rate as a ratio. - -The ration is in/out. -*/ -MA_API ma_result ma_resampler_set_rate_ratio(ma_resampler* pResampler, float ratio); - -/* -Retrieves the latency introduced by the resampler in input frames. -*/ -MA_API ma_uint64 ma_resampler_get_input_latency(const ma_resampler* pResampler); - -/* -Retrieves the latency introduced by the resampler in output frames. -*/ -MA_API ma_uint64 ma_resampler_get_output_latency(const ma_resampler* pResampler); - -/* -Calculates the number of whole input frames that would need to be read from the client in order to output the specified -number of output frames. - -The returned value does not include cached input frames. It only returns the number of extra frames that would need to be -read from the input buffer in order to output the specified number of output frames. -*/ -MA_API ma_result ma_resampler_get_required_input_frame_count(const ma_resampler* pResampler, ma_uint64 outputFrameCount, ma_uint64* pInputFrameCount); - -/* -Calculates the number of whole output frames that would be output after fully reading and consuming the specified number of -input frames. -*/ -MA_API ma_result ma_resampler_get_expected_output_frame_count(const ma_resampler* pResampler, ma_uint64 inputFrameCount, ma_uint64* pOutputFrameCount); - -/* -Resets the resampler's timer and clears its internal cache. -*/ -MA_API ma_result ma_resampler_reset(ma_resampler* pResampler); - - -/************************************************************************************************************************************************************** - -Channel Conversion - -**************************************************************************************************************************************************************/ -typedef enum -{ - ma_channel_conversion_path_unknown, - ma_channel_conversion_path_passthrough, - ma_channel_conversion_path_mono_out, /* Converting to mono. */ - ma_channel_conversion_path_mono_in, /* Converting from mono. */ - ma_channel_conversion_path_shuffle, /* Simple shuffle. Will use this when all channels are present in both input and output channel maps, but just in a different order. */ - ma_channel_conversion_path_weights /* Blended based on weights. */ -} ma_channel_conversion_path; - -typedef enum -{ - ma_mono_expansion_mode_duplicate = 0, /* The default. */ - ma_mono_expansion_mode_average, /* Average the mono channel across all channels. */ - ma_mono_expansion_mode_stereo_only, /* Duplicate to the left and right channels only and ignore the others. */ - ma_mono_expansion_mode_default = ma_mono_expansion_mode_duplicate -} ma_mono_expansion_mode; - -typedef struct -{ - ma_format format; - ma_uint32 channelsIn; - ma_uint32 channelsOut; - const ma_channel* pChannelMapIn; - const ma_channel* pChannelMapOut; - ma_channel_mix_mode mixingMode; - ma_bool32 calculateLFEFromSpatialChannels; /* When an output LFE channel is present, but no input LFE, set to true to set the output LFE to the average of all spatial channels (LR, FR, etc.). Ignored when an input LFE is present. */ - float** ppWeights; /* [in][out]. Only used when mixingMode is set to ma_channel_mix_mode_custom_weights. */ -} ma_channel_converter_config; - -MA_API ma_channel_converter_config ma_channel_converter_config_init(ma_format format, ma_uint32 channelsIn, const ma_channel* pChannelMapIn, ma_uint32 channelsOut, const ma_channel* pChannelMapOut, ma_channel_mix_mode mixingMode); - -typedef struct -{ - ma_format format; - ma_uint32 channelsIn; - ma_uint32 channelsOut; - ma_channel_mix_mode mixingMode; - ma_channel_conversion_path conversionPath; - ma_channel* pChannelMapIn; - ma_channel* pChannelMapOut; - ma_uint8* pShuffleTable; /* Indexed by output channel index. */ - union - { - float** f32; - ma_int32** s16; - } weights; /* [in][out] */ - - /* Memory management. */ - void* _pHeap; - ma_bool32 _ownsHeap; -} ma_channel_converter; - -MA_API ma_result ma_channel_converter_get_heap_size(const ma_channel_converter_config* pConfig, size_t* pHeapSizeInBytes); -MA_API ma_result ma_channel_converter_init_preallocated(const ma_channel_converter_config* pConfig, void* pHeap, ma_channel_converter* pConverter); -MA_API ma_result ma_channel_converter_init(const ma_channel_converter_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_channel_converter* pConverter); -MA_API void ma_channel_converter_uninit(ma_channel_converter* pConverter, const ma_allocation_callbacks* pAllocationCallbacks); -MA_API ma_result ma_channel_converter_process_pcm_frames(ma_channel_converter* pConverter, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount); -MA_API ma_result ma_channel_converter_get_input_channel_map(const ma_channel_converter* pConverter, ma_channel* pChannelMap, size_t channelMapCap); -MA_API ma_result ma_channel_converter_get_output_channel_map(const ma_channel_converter* pConverter, ma_channel* pChannelMap, size_t channelMapCap); - - -/************************************************************************************************************************************************************** - -Data Conversion - -**************************************************************************************************************************************************************/ -typedef struct -{ - ma_format formatIn; - ma_format formatOut; - ma_uint32 channelsIn; - ma_uint32 channelsOut; - ma_uint32 sampleRateIn; - ma_uint32 sampleRateOut; - ma_channel* pChannelMapIn; - ma_channel* pChannelMapOut; - ma_dither_mode ditherMode; - ma_channel_mix_mode channelMixMode; - ma_bool32 calculateLFEFromSpatialChannels; /* When an output LFE channel is present, but no input LFE, set to true to set the output LFE to the average of all spatial channels (LR, FR, etc.). Ignored when an input LFE is present. */ - float** ppChannelWeights; /* [in][out]. Only used when mixingMode is set to ma_channel_mix_mode_custom_weights. */ - ma_bool32 allowDynamicSampleRate; - ma_resampler_config resampling; -} ma_data_converter_config; - -MA_API ma_data_converter_config ma_data_converter_config_init_default(void); -MA_API ma_data_converter_config ma_data_converter_config_init(ma_format formatIn, ma_format formatOut, ma_uint32 channelsIn, ma_uint32 channelsOut, ma_uint32 sampleRateIn, ma_uint32 sampleRateOut); - - -typedef enum -{ - ma_data_converter_execution_path_passthrough, /* No conversion. */ - ma_data_converter_execution_path_format_only, /* Only format conversion. */ - ma_data_converter_execution_path_channels_only, /* Only channel conversion. */ - ma_data_converter_execution_path_resample_only, /* Only resampling. */ - ma_data_converter_execution_path_resample_first, /* All conversions, but resample as the first step. */ - ma_data_converter_execution_path_channels_first /* All conversions, but channels as the first step. */ -} ma_data_converter_execution_path; - -typedef struct -{ - ma_format formatIn; - ma_format formatOut; - ma_uint32 channelsIn; - ma_uint32 channelsOut; - ma_uint32 sampleRateIn; - ma_uint32 sampleRateOut; - ma_dither_mode ditherMode; - ma_data_converter_execution_path executionPath; /* The execution path the data converter will follow when processing. */ - ma_channel_converter channelConverter; - ma_resampler resampler; - ma_bool8 hasPreFormatConversion; - ma_bool8 hasPostFormatConversion; - ma_bool8 hasChannelConverter; - ma_bool8 hasResampler; - ma_bool8 isPassthrough; - - /* Memory management. */ - ma_bool8 _ownsHeap; - void* _pHeap; -} ma_data_converter; - -MA_API ma_result ma_data_converter_get_heap_size(const ma_data_converter_config* pConfig, size_t* pHeapSizeInBytes); -MA_API ma_result ma_data_converter_init_preallocated(const ma_data_converter_config* pConfig, void* pHeap, ma_data_converter* pConverter); -MA_API ma_result ma_data_converter_init(const ma_data_converter_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_data_converter* pConverter); -MA_API void ma_data_converter_uninit(ma_data_converter* pConverter, const ma_allocation_callbacks* pAllocationCallbacks); -MA_API ma_result ma_data_converter_process_pcm_frames(ma_data_converter* pConverter, const void* pFramesIn, ma_uint64* pFrameCountIn, void* pFramesOut, ma_uint64* pFrameCountOut); -MA_API ma_result ma_data_converter_set_rate(ma_data_converter* pConverter, ma_uint32 sampleRateIn, ma_uint32 sampleRateOut); -MA_API ma_result ma_data_converter_set_rate_ratio(ma_data_converter* pConverter, float ratioInOut); -MA_API ma_uint64 ma_data_converter_get_input_latency(const ma_data_converter* pConverter); -MA_API ma_uint64 ma_data_converter_get_output_latency(const ma_data_converter* pConverter); -MA_API ma_result ma_data_converter_get_required_input_frame_count(const ma_data_converter* pConverter, ma_uint64 outputFrameCount, ma_uint64* pInputFrameCount); -MA_API ma_result ma_data_converter_get_expected_output_frame_count(const ma_data_converter* pConverter, ma_uint64 inputFrameCount, ma_uint64* pOutputFrameCount); -MA_API ma_result ma_data_converter_get_input_channel_map(const ma_data_converter* pConverter, ma_channel* pChannelMap, size_t channelMapCap); -MA_API ma_result ma_data_converter_get_output_channel_map(const ma_data_converter* pConverter, ma_channel* pChannelMap, size_t channelMapCap); -MA_API ma_result ma_data_converter_reset(ma_data_converter* pConverter); - - -/************************************************************************************************************************************************************ - -Format Conversion - -************************************************************************************************************************************************************/ -MA_API void ma_pcm_u8_to_s16(void* pOut, const void* pIn, ma_uint64 count, ma_dither_mode ditherMode); -MA_API void ma_pcm_u8_to_s24(void* pOut, const void* pIn, ma_uint64 count, ma_dither_mode ditherMode); -MA_API void ma_pcm_u8_to_s32(void* pOut, const void* pIn, ma_uint64 count, ma_dither_mode ditherMode); -MA_API void ma_pcm_u8_to_f32(void* pOut, const void* pIn, ma_uint64 count, ma_dither_mode ditherMode); -MA_API void ma_pcm_s16_to_u8(void* pOut, const void* pIn, ma_uint64 count, ma_dither_mode ditherMode); -MA_API void ma_pcm_s16_to_s24(void* pOut, const void* pIn, ma_uint64 count, ma_dither_mode ditherMode); -MA_API void ma_pcm_s16_to_s32(void* pOut, const void* pIn, ma_uint64 count, ma_dither_mode ditherMode); -MA_API void ma_pcm_s16_to_f32(void* pOut, const void* pIn, ma_uint64 count, ma_dither_mode ditherMode); -MA_API void ma_pcm_s24_to_u8(void* pOut, const void* pIn, ma_uint64 count, ma_dither_mode ditherMode); -MA_API void ma_pcm_s24_to_s16(void* pOut, const void* pIn, ma_uint64 count, ma_dither_mode ditherMode); -MA_API void ma_pcm_s24_to_s32(void* pOut, const void* pIn, ma_uint64 count, ma_dither_mode ditherMode); -MA_API void ma_pcm_s24_to_f32(void* pOut, const void* pIn, ma_uint64 count, ma_dither_mode ditherMode); -MA_API void ma_pcm_s32_to_u8(void* pOut, const void* pIn, ma_uint64 count, ma_dither_mode ditherMode); -MA_API void ma_pcm_s32_to_s16(void* pOut, const void* pIn, ma_uint64 count, ma_dither_mode ditherMode); -MA_API void ma_pcm_s32_to_s24(void* pOut, const void* pIn, ma_uint64 count, ma_dither_mode ditherMode); -MA_API void ma_pcm_s32_to_f32(void* pOut, const void* pIn, ma_uint64 count, ma_dither_mode ditherMode); -MA_API void ma_pcm_f32_to_u8(void* pOut, const void* pIn, ma_uint64 count, ma_dither_mode ditherMode); -MA_API void ma_pcm_f32_to_s16(void* pOut, const void* pIn, ma_uint64 count, ma_dither_mode ditherMode); -MA_API void ma_pcm_f32_to_s24(void* pOut, const void* pIn, ma_uint64 count, ma_dither_mode ditherMode); -MA_API void ma_pcm_f32_to_s32(void* pOut, const void* pIn, ma_uint64 count, ma_dither_mode ditherMode); -MA_API void ma_pcm_convert(void* pOut, ma_format formatOut, const void* pIn, ma_format formatIn, ma_uint64 sampleCount, ma_dither_mode ditherMode); -MA_API void ma_convert_pcm_frames_format(void* pOut, ma_format formatOut, const void* pIn, ma_format formatIn, ma_uint64 frameCount, ma_uint32 channels, ma_dither_mode ditherMode); - -/* -Deinterleaves an interleaved buffer. -*/ -MA_API void ma_deinterleave_pcm_frames(ma_format format, ma_uint32 channels, ma_uint64 frameCount, const void* pInterleavedPCMFrames, void** ppDeinterleavedPCMFrames); - -/* -Interleaves a group of deinterleaved buffers. -*/ -MA_API void ma_interleave_pcm_frames(ma_format format, ma_uint32 channels, ma_uint64 frameCount, const void** ppDeinterleavedPCMFrames, void* pInterleavedPCMFrames); - - -/************************************************************************************************************************************************************ - -Channel Maps - -************************************************************************************************************************************************************/ -/* -This is used in the shuffle table to indicate that the channel index is undefined and should be ignored. -*/ -#define MA_CHANNEL_INDEX_NULL 255 - -/* -Retrieves the channel position of the specified channel in the given channel map. - -The pChannelMap parameter can be null, in which case miniaudio's default channel map will be assumed. -*/ -MA_API ma_channel ma_channel_map_get_channel(const ma_channel* pChannelMap, ma_uint32 channelCount, ma_uint32 channelIndex); - -/* -Initializes a blank channel map. - -When a blank channel map is specified anywhere it indicates that the native channel map should be used. -*/ -MA_API void ma_channel_map_init_blank(ma_channel* pChannelMap, ma_uint32 channels); - -/* -Helper for retrieving a standard channel map. - -The output channel map buffer must have a capacity of at least `channelMapCap`. -*/ -MA_API void ma_channel_map_init_standard(ma_standard_channel_map standardChannelMap, ma_channel* pChannelMap, size_t channelMapCap, ma_uint32 channels); - -/* -Copies a channel map. - -Both input and output channel map buffers must have a capacity of at least `channels`. -*/ -MA_API void ma_channel_map_copy(ma_channel* pOut, const ma_channel* pIn, ma_uint32 channels); - -/* -Copies a channel map if one is specified, otherwise copies the default channel map. - -The output buffer must have a capacity of at least `channels`. If not NULL, the input channel map must also have a capacity of at least `channels`. -*/ -MA_API void ma_channel_map_copy_or_default(ma_channel* pOut, size_t channelMapCapOut, const ma_channel* pIn, ma_uint32 channels); - - -/* -Determines whether or not a channel map is valid. - -A blank channel map is valid (all channels set to MA_CHANNEL_NONE). The way a blank channel map is handled is context specific, but -is usually treated as a passthrough. - -Invalid channel maps: - - A channel map with no channels - - A channel map with more than one channel and a mono channel - -The channel map buffer must have a capacity of at least `channels`. -*/ -MA_API ma_bool32 ma_channel_map_is_valid(const ma_channel* pChannelMap, ma_uint32 channels); - -/* -Helper for comparing two channel maps for equality. - -This assumes the channel count is the same between the two. - -Both channels map buffers must have a capacity of at least `channels`. -*/ -MA_API ma_bool32 ma_channel_map_is_equal(const ma_channel* pChannelMapA, const ma_channel* pChannelMapB, ma_uint32 channels); - -/* -Helper for determining if a channel map is blank (all channels set to MA_CHANNEL_NONE). - -The channel map buffer must have a capacity of at least `channels`. -*/ -MA_API ma_bool32 ma_channel_map_is_blank(const ma_channel* pChannelMap, ma_uint32 channels); - -/* -Helper for determining whether or not a channel is present in the given channel map. - -The channel map buffer must have a capacity of at least `channels`. -*/ -MA_API ma_bool32 ma_channel_map_contains_channel_position(ma_uint32 channels, const ma_channel* pChannelMap, ma_channel channelPosition); - -/* -Find a channel position in the given channel map. Returns MA_TRUE if the channel is found; MA_FALSE otherwise. The -index of the channel is output to `pChannelIndex`. - -The channel map buffer must have a capacity of at least `channels`. -*/ -MA_API ma_bool32 ma_channel_map_find_channel_position(ma_uint32 channels, const ma_channel* pChannelMap, ma_channel channelPosition, ma_uint32* pChannelIndex); - -/* -Generates a string representing the given channel map. - -This is for printing and debugging purposes, not serialization/deserialization. - -Returns the length of the string, not including the null terminator. -*/ -MA_API size_t ma_channel_map_to_string(const ma_channel* pChannelMap, ma_uint32 channels, char* pBufferOut, size_t bufferCap); - -/* -Retrieves a human readable version of a channel position. -*/ -MA_API const char* ma_channel_position_to_string(ma_channel channel); - - -/************************************************************************************************************************************************************ - -Conversion Helpers - -************************************************************************************************************************************************************/ - -/* -High-level helper for doing a full format conversion in one go. Returns the number of output frames. Call this with pOut set to NULL to -determine the required size of the output buffer. frameCountOut should be set to the capacity of pOut. If pOut is NULL, frameCountOut is -ignored. - -A return value of 0 indicates an error. - -This function is useful for one-off bulk conversions, but if you're streaming data you should use the ma_data_converter APIs instead. -*/ -MA_API ma_uint64 ma_convert_frames(void* pOut, ma_uint64 frameCountOut, ma_format formatOut, ma_uint32 channelsOut, ma_uint32 sampleRateOut, const void* pIn, ma_uint64 frameCountIn, ma_format formatIn, ma_uint32 channelsIn, ma_uint32 sampleRateIn); -MA_API ma_uint64 ma_convert_frames_ex(void* pOut, ma_uint64 frameCountOut, const void* pIn, ma_uint64 frameCountIn, const ma_data_converter_config* pConfig); - - -/************************************************************************************************************************************************************ - -Data Source - -************************************************************************************************************************************************************/ -typedef void ma_data_source; - -#define MA_DATA_SOURCE_SELF_MANAGED_RANGE_AND_LOOP_POINT 0x00000001 - -typedef struct -{ - ma_result (* onRead)(ma_data_source* pDataSource, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead); - ma_result (* onSeek)(ma_data_source* pDataSource, ma_uint64 frameIndex); - ma_result (* onGetDataFormat)(ma_data_source* pDataSource, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate, ma_channel* pChannelMap, size_t channelMapCap); - ma_result (* onGetCursor)(ma_data_source* pDataSource, ma_uint64* pCursor); - ma_result (* onGetLength)(ma_data_source* pDataSource, ma_uint64* pLength); - ma_result (* onSetLooping)(ma_data_source* pDataSource, ma_bool32 isLooping); - ma_uint32 flags; -} ma_data_source_vtable; - -typedef ma_data_source* (* ma_data_source_get_next_proc)(ma_data_source* pDataSource); - -typedef struct -{ - const ma_data_source_vtable* vtable; -} ma_data_source_config; - -MA_API ma_data_source_config ma_data_source_config_init(void); - - -typedef struct -{ - const ma_data_source_vtable* vtable; - ma_uint64 rangeBegInFrames; - ma_uint64 rangeEndInFrames; /* Set to -1 for unranged (default). */ - ma_uint64 loopBegInFrames; /* Relative to rangeBegInFrames. */ - ma_uint64 loopEndInFrames; /* Relative to rangeBegInFrames. Set to -1 for the end of the range. */ - ma_data_source* pCurrent; /* When non-NULL, the data source being initialized will act as a proxy and will route all operations to pCurrent. Used in conjunction with pNext/onGetNext for seamless chaining. */ - ma_data_source* pNext; /* When set to NULL, onGetNext will be used. */ - ma_data_source_get_next_proc onGetNext; /* Will be used when pNext is NULL. If both are NULL, no next will be used. */ - MA_ATOMIC(4, ma_bool32) isLooping; -} ma_data_source_base; - -MA_API ma_result ma_data_source_init(const ma_data_source_config* pConfig, ma_data_source* pDataSource); -MA_API void ma_data_source_uninit(ma_data_source* pDataSource); -MA_API ma_result ma_data_source_read_pcm_frames(ma_data_source* pDataSource, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead); /* Must support pFramesOut = NULL in which case a forward seek should be performed. */ -MA_API ma_result ma_data_source_seek_pcm_frames(ma_data_source* pDataSource, ma_uint64 frameCount, ma_uint64* pFramesSeeked); /* Can only seek forward. Equivalent to ma_data_source_read_pcm_frames(pDataSource, NULL, frameCount, &framesRead); */ -MA_API ma_result ma_data_source_seek_to_pcm_frame(ma_data_source* pDataSource, ma_uint64 frameIndex); -MA_API ma_result ma_data_source_seek_seconds(ma_data_source* pDataSource, float secondCount, float* pSecondsSeeked); /* Can only seek forward. Abstraction to ma_data_source_seek_pcm_frames() */ -MA_API ma_result ma_data_source_seek_to_second(ma_data_source* pDataSource, float seekPointInSeconds); /* Abstraction to ma_data_source_seek_to_pcm_frame() */ -MA_API ma_result ma_data_source_get_data_format(ma_data_source* pDataSource, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate, ma_channel* pChannelMap, size_t channelMapCap); -MA_API ma_result ma_data_source_get_cursor_in_pcm_frames(ma_data_source* pDataSource, ma_uint64* pCursor); -MA_API ma_result ma_data_source_get_length_in_pcm_frames(ma_data_source* pDataSource, ma_uint64* pLength); /* Returns MA_NOT_IMPLEMENTED if the length is unknown or cannot be determined. Decoders can return this. */ -MA_API ma_result ma_data_source_get_cursor_in_seconds(ma_data_source* pDataSource, float* pCursor); -MA_API ma_result ma_data_source_get_length_in_seconds(ma_data_source* pDataSource, float* pLength); -MA_API ma_result ma_data_source_set_looping(ma_data_source* pDataSource, ma_bool32 isLooping); -MA_API ma_bool32 ma_data_source_is_looping(const ma_data_source* pDataSource); -MA_API ma_result ma_data_source_set_range_in_pcm_frames(ma_data_source* pDataSource, ma_uint64 rangeBegInFrames, ma_uint64 rangeEndInFrames); -MA_API void ma_data_source_get_range_in_pcm_frames(const ma_data_source* pDataSource, ma_uint64* pRangeBegInFrames, ma_uint64* pRangeEndInFrames); -MA_API ma_result ma_data_source_set_loop_point_in_pcm_frames(ma_data_source* pDataSource, ma_uint64 loopBegInFrames, ma_uint64 loopEndInFrames); -MA_API void ma_data_source_get_loop_point_in_pcm_frames(const ma_data_source* pDataSource, ma_uint64* pLoopBegInFrames, ma_uint64* pLoopEndInFrames); -MA_API ma_result ma_data_source_set_current(ma_data_source* pDataSource, ma_data_source* pCurrentDataSource); -MA_API ma_data_source* ma_data_source_get_current(const ma_data_source* pDataSource); -MA_API ma_result ma_data_source_set_next(ma_data_source* pDataSource, ma_data_source* pNextDataSource); -MA_API ma_data_source* ma_data_source_get_next(const ma_data_source* pDataSource); -MA_API ma_result ma_data_source_set_next_callback(ma_data_source* pDataSource, ma_data_source_get_next_proc onGetNext); -MA_API ma_data_source_get_next_proc ma_data_source_get_next_callback(const ma_data_source* pDataSource); - - -typedef struct -{ - ma_data_source_base ds; - ma_format format; - ma_uint32 channels; - ma_uint32 sampleRate; - ma_uint64 cursor; - ma_uint64 sizeInFrames; - const void* pData; -} ma_audio_buffer_ref; - -MA_API ma_result ma_audio_buffer_ref_init(ma_format format, ma_uint32 channels, const void* pData, ma_uint64 sizeInFrames, ma_audio_buffer_ref* pAudioBufferRef); -MA_API void ma_audio_buffer_ref_uninit(ma_audio_buffer_ref* pAudioBufferRef); -MA_API ma_result ma_audio_buffer_ref_set_data(ma_audio_buffer_ref* pAudioBufferRef, const void* pData, ma_uint64 sizeInFrames); -MA_API ma_uint64 ma_audio_buffer_ref_read_pcm_frames(ma_audio_buffer_ref* pAudioBufferRef, void* pFramesOut, ma_uint64 frameCount, ma_bool32 loop); -MA_API ma_result ma_audio_buffer_ref_seek_to_pcm_frame(ma_audio_buffer_ref* pAudioBufferRef, ma_uint64 frameIndex); -MA_API ma_result ma_audio_buffer_ref_map(ma_audio_buffer_ref* pAudioBufferRef, void** ppFramesOut, ma_uint64* pFrameCount); -MA_API ma_result ma_audio_buffer_ref_unmap(ma_audio_buffer_ref* pAudioBufferRef, ma_uint64 frameCount); /* Returns MA_AT_END if the end has been reached. This should be considered successful. */ -MA_API ma_bool32 ma_audio_buffer_ref_at_end(const ma_audio_buffer_ref* pAudioBufferRef); -MA_API ma_result ma_audio_buffer_ref_get_cursor_in_pcm_frames(const ma_audio_buffer_ref* pAudioBufferRef, ma_uint64* pCursor); -MA_API ma_result ma_audio_buffer_ref_get_length_in_pcm_frames(const ma_audio_buffer_ref* pAudioBufferRef, ma_uint64* pLength); -MA_API ma_result ma_audio_buffer_ref_get_available_frames(const ma_audio_buffer_ref* pAudioBufferRef, ma_uint64* pAvailableFrames); - - - -typedef struct -{ - ma_format format; - ma_uint32 channels; - ma_uint32 sampleRate; - ma_uint64 sizeInFrames; - const void* pData; /* If set to NULL, will allocate a block of memory for you. */ - ma_allocation_callbacks allocationCallbacks; -} ma_audio_buffer_config; - -MA_API ma_audio_buffer_config ma_audio_buffer_config_init(ma_format format, ma_uint32 channels, ma_uint64 sizeInFrames, const void* pData, const ma_allocation_callbacks* pAllocationCallbacks); - -typedef struct -{ - ma_audio_buffer_ref ref; - ma_allocation_callbacks allocationCallbacks; - ma_bool32 ownsData; /* Used to control whether or not miniaudio owns the data buffer. If set to true, pData will be freed in ma_audio_buffer_uninit(). */ - ma_uint8 _pExtraData[1]; /* For allocating a buffer with the memory located directly after the other memory of the structure. */ -} ma_audio_buffer; - -MA_API ma_result ma_audio_buffer_init(const ma_audio_buffer_config* pConfig, ma_audio_buffer* pAudioBuffer); -MA_API ma_result ma_audio_buffer_init_copy(const ma_audio_buffer_config* pConfig, ma_audio_buffer* pAudioBuffer); -MA_API ma_result ma_audio_buffer_alloc_and_init(const ma_audio_buffer_config* pConfig, ma_audio_buffer** ppAudioBuffer); /* Always copies the data. Doesn't make sense to use this otherwise. Use ma_audio_buffer_uninit_and_free() to uninit. */ -MA_API void ma_audio_buffer_uninit(ma_audio_buffer* pAudioBuffer); -MA_API void ma_audio_buffer_uninit_and_free(ma_audio_buffer* pAudioBuffer); -MA_API ma_uint64 ma_audio_buffer_read_pcm_frames(ma_audio_buffer* pAudioBuffer, void* pFramesOut, ma_uint64 frameCount, ma_bool32 loop); -MA_API ma_result ma_audio_buffer_seek_to_pcm_frame(ma_audio_buffer* pAudioBuffer, ma_uint64 frameIndex); -MA_API ma_result ma_audio_buffer_map(ma_audio_buffer* pAudioBuffer, void** ppFramesOut, ma_uint64* pFrameCount); -MA_API ma_result ma_audio_buffer_unmap(ma_audio_buffer* pAudioBuffer, ma_uint64 frameCount); /* Returns MA_AT_END if the end has been reached. This should be considered successful. */ -MA_API ma_bool32 ma_audio_buffer_at_end(const ma_audio_buffer* pAudioBuffer); -MA_API ma_result ma_audio_buffer_get_cursor_in_pcm_frames(const ma_audio_buffer* pAudioBuffer, ma_uint64* pCursor); -MA_API ma_result ma_audio_buffer_get_length_in_pcm_frames(const ma_audio_buffer* pAudioBuffer, ma_uint64* pLength); -MA_API ma_result ma_audio_buffer_get_available_frames(const ma_audio_buffer* pAudioBuffer, ma_uint64* pAvailableFrames); - - -/* -Paged Audio Buffer -================== -A paged audio buffer is made up of a linked list of pages. It's expandable, but not shrinkable. It -can be used for cases where audio data is streamed in asynchronously while allowing data to be read -at the same time. - -This is lock-free, but not 100% thread safe. You can append a page and read from the buffer across -simultaneously across different threads, however only one thread at a time can append, and only one -thread at a time can read and seek. -*/ -typedef struct ma_paged_audio_buffer_page ma_paged_audio_buffer_page; -struct ma_paged_audio_buffer_page -{ - MA_ATOMIC(MA_SIZEOF_PTR, ma_paged_audio_buffer_page*) pNext; - ma_uint64 sizeInFrames; - ma_uint8 pAudioData[1]; -}; - -typedef struct -{ - ma_format format; - ma_uint32 channels; - ma_paged_audio_buffer_page head; /* Dummy head for the lock-free algorithm. Always has a size of 0. */ - MA_ATOMIC(MA_SIZEOF_PTR, ma_paged_audio_buffer_page*) pTail; /* Never null. Initially set to &head. */ -} ma_paged_audio_buffer_data; - -MA_API ma_result ma_paged_audio_buffer_data_init(ma_format format, ma_uint32 channels, ma_paged_audio_buffer_data* pData); -MA_API void ma_paged_audio_buffer_data_uninit(ma_paged_audio_buffer_data* pData, const ma_allocation_callbacks* pAllocationCallbacks); -MA_API ma_paged_audio_buffer_page* ma_paged_audio_buffer_data_get_head(ma_paged_audio_buffer_data* pData); -MA_API ma_paged_audio_buffer_page* ma_paged_audio_buffer_data_get_tail(ma_paged_audio_buffer_data* pData); -MA_API ma_result ma_paged_audio_buffer_data_get_length_in_pcm_frames(ma_paged_audio_buffer_data* pData, ma_uint64* pLength); -MA_API ma_result ma_paged_audio_buffer_data_allocate_page(ma_paged_audio_buffer_data* pData, ma_uint64 pageSizeInFrames, const void* pInitialData, const ma_allocation_callbacks* pAllocationCallbacks, ma_paged_audio_buffer_page** ppPage); -MA_API ma_result ma_paged_audio_buffer_data_free_page(ma_paged_audio_buffer_data* pData, ma_paged_audio_buffer_page* pPage, const ma_allocation_callbacks* pAllocationCallbacks); -MA_API ma_result ma_paged_audio_buffer_data_append_page(ma_paged_audio_buffer_data* pData, ma_paged_audio_buffer_page* pPage); -MA_API ma_result ma_paged_audio_buffer_data_allocate_and_append_page(ma_paged_audio_buffer_data* pData, ma_uint32 pageSizeInFrames, const void* pInitialData, const ma_allocation_callbacks* pAllocationCallbacks); - - -typedef struct -{ - ma_paged_audio_buffer_data* pData; /* Must not be null. */ -} ma_paged_audio_buffer_config; - -MA_API ma_paged_audio_buffer_config ma_paged_audio_buffer_config_init(ma_paged_audio_buffer_data* pData); - - -typedef struct -{ - ma_data_source_base ds; - ma_paged_audio_buffer_data* pData; /* Audio data is read from here. Cannot be null. */ - ma_paged_audio_buffer_page* pCurrent; - ma_uint64 relativeCursor; /* Relative to the current page. */ - ma_uint64 absoluteCursor; -} ma_paged_audio_buffer; - -MA_API ma_result ma_paged_audio_buffer_init(const ma_paged_audio_buffer_config* pConfig, ma_paged_audio_buffer* pPagedAudioBuffer); -MA_API void ma_paged_audio_buffer_uninit(ma_paged_audio_buffer* pPagedAudioBuffer); -MA_API ma_result ma_paged_audio_buffer_read_pcm_frames(ma_paged_audio_buffer* pPagedAudioBuffer, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead); /* Returns MA_AT_END if no more pages available. */ -MA_API ma_result ma_paged_audio_buffer_seek_to_pcm_frame(ma_paged_audio_buffer* pPagedAudioBuffer, ma_uint64 frameIndex); -MA_API ma_result ma_paged_audio_buffer_get_cursor_in_pcm_frames(ma_paged_audio_buffer* pPagedAudioBuffer, ma_uint64* pCursor); -MA_API ma_result ma_paged_audio_buffer_get_length_in_pcm_frames(ma_paged_audio_buffer* pPagedAudioBuffer, ma_uint64* pLength); - - - -/************************************************************************************************************************************************************ - -Ring Buffer - -************************************************************************************************************************************************************/ -typedef struct -{ - void* pBuffer; - ma_uint32 subbufferSizeInBytes; - ma_uint32 subbufferCount; - ma_uint32 subbufferStrideInBytes; - MA_ATOMIC(4, ma_uint32) encodedReadOffset; /* Most significant bit is the loop flag. Lower 31 bits contains the actual offset in bytes. Must be used atomically. */ - MA_ATOMIC(4, ma_uint32) encodedWriteOffset; /* Most significant bit is the loop flag. Lower 31 bits contains the actual offset in bytes. Must be used atomically. */ - ma_bool8 ownsBuffer; /* Used to know whether or not miniaudio is responsible for free()-ing the buffer. */ - ma_bool8 clearOnWriteAcquire; /* When set, clears the acquired write buffer before returning from ma_rb_acquire_write(). */ - ma_allocation_callbacks allocationCallbacks; -} ma_rb; - -MA_API ma_result ma_rb_init_ex(size_t subbufferSizeInBytes, size_t subbufferCount, size_t subbufferStrideInBytes, void* pOptionalPreallocatedBuffer, const ma_allocation_callbacks* pAllocationCallbacks, ma_rb* pRB); -MA_API ma_result ma_rb_init(size_t bufferSizeInBytes, void* pOptionalPreallocatedBuffer, const ma_allocation_callbacks* pAllocationCallbacks, ma_rb* pRB); -MA_API void ma_rb_uninit(ma_rb* pRB); -MA_API void ma_rb_reset(ma_rb* pRB); -MA_API ma_result ma_rb_acquire_read(ma_rb* pRB, size_t* pSizeInBytes, void** ppBufferOut); -MA_API ma_result ma_rb_commit_read(ma_rb* pRB, size_t sizeInBytes); -MA_API ma_result ma_rb_acquire_write(ma_rb* pRB, size_t* pSizeInBytes, void** ppBufferOut); -MA_API ma_result ma_rb_commit_write(ma_rb* pRB, size_t sizeInBytes); -MA_API ma_result ma_rb_seek_read(ma_rb* pRB, size_t offsetInBytes); -MA_API ma_result ma_rb_seek_write(ma_rb* pRB, size_t offsetInBytes); -MA_API ma_int32 ma_rb_pointer_distance(ma_rb* pRB); /* Returns the distance between the write pointer and the read pointer. Should never be negative for a correct program. Will return the number of bytes that can be read before the read pointer hits the write pointer. */ -MA_API ma_uint32 ma_rb_available_read(ma_rb* pRB); -MA_API ma_uint32 ma_rb_available_write(ma_rb* pRB); -MA_API size_t ma_rb_get_subbuffer_size(ma_rb* pRB); -MA_API size_t ma_rb_get_subbuffer_stride(ma_rb* pRB); -MA_API size_t ma_rb_get_subbuffer_offset(ma_rb* pRB, size_t subbufferIndex); -MA_API void* ma_rb_get_subbuffer_ptr(ma_rb* pRB, size_t subbufferIndex, void* pBuffer); - - -typedef struct -{ - ma_data_source_base ds; - ma_rb rb; - ma_format format; - ma_uint32 channels; - ma_uint32 sampleRate; /* Not required for the ring buffer itself, but useful for associating the data with some sample rate, particularly for data sources. */ -} ma_pcm_rb; - -MA_API ma_result ma_pcm_rb_init_ex(ma_format format, ma_uint32 channels, ma_uint32 subbufferSizeInFrames, ma_uint32 subbufferCount, ma_uint32 subbufferStrideInFrames, void* pOptionalPreallocatedBuffer, const ma_allocation_callbacks* pAllocationCallbacks, ma_pcm_rb* pRB); -MA_API ma_result ma_pcm_rb_init(ma_format format, ma_uint32 channels, ma_uint32 bufferSizeInFrames, void* pOptionalPreallocatedBuffer, const ma_allocation_callbacks* pAllocationCallbacks, ma_pcm_rb* pRB); -MA_API void ma_pcm_rb_uninit(ma_pcm_rb* pRB); -MA_API void ma_pcm_rb_reset(ma_pcm_rb* pRB); -MA_API ma_result ma_pcm_rb_acquire_read(ma_pcm_rb* pRB, ma_uint32* pSizeInFrames, void** ppBufferOut); -MA_API ma_result ma_pcm_rb_commit_read(ma_pcm_rb* pRB, ma_uint32 sizeInFrames); -MA_API ma_result ma_pcm_rb_acquire_write(ma_pcm_rb* pRB, ma_uint32* pSizeInFrames, void** ppBufferOut); -MA_API ma_result ma_pcm_rb_commit_write(ma_pcm_rb* pRB, ma_uint32 sizeInFrames); -MA_API ma_result ma_pcm_rb_seek_read(ma_pcm_rb* pRB, ma_uint32 offsetInFrames); -MA_API ma_result ma_pcm_rb_seek_write(ma_pcm_rb* pRB, ma_uint32 offsetInFrames); -MA_API ma_int32 ma_pcm_rb_pointer_distance(ma_pcm_rb* pRB); /* Return value is in frames. */ -MA_API ma_uint32 ma_pcm_rb_available_read(ma_pcm_rb* pRB); -MA_API ma_uint32 ma_pcm_rb_available_write(ma_pcm_rb* pRB); -MA_API ma_uint32 ma_pcm_rb_get_subbuffer_size(ma_pcm_rb* pRB); -MA_API ma_uint32 ma_pcm_rb_get_subbuffer_stride(ma_pcm_rb* pRB); -MA_API ma_uint32 ma_pcm_rb_get_subbuffer_offset(ma_pcm_rb* pRB, ma_uint32 subbufferIndex); -MA_API void* ma_pcm_rb_get_subbuffer_ptr(ma_pcm_rb* pRB, ma_uint32 subbufferIndex, void* pBuffer); -MA_API ma_format ma_pcm_rb_get_format(const ma_pcm_rb* pRB); -MA_API ma_uint32 ma_pcm_rb_get_channels(const ma_pcm_rb* pRB); -MA_API ma_uint32 ma_pcm_rb_get_sample_rate(const ma_pcm_rb* pRB); -MA_API void ma_pcm_rb_set_sample_rate(ma_pcm_rb* pRB, ma_uint32 sampleRate); - - -/* -The idea of the duplex ring buffer is to act as the intermediary buffer when running two asynchronous devices in a duplex set up. The -capture device writes to it, and then a playback device reads from it. - -At the moment this is just a simple naive implementation, but in the future I want to implement some dynamic resampling to seamlessly -handle desyncs. Note that the API is work in progress and may change at any time in any version. - -The size of the buffer is based on the capture side since that's what'll be written to the buffer. It is based on the capture period size -in frames. The internal sample rate of the capture device is also needed in order to calculate the size. -*/ -typedef struct -{ - ma_pcm_rb rb; -} ma_duplex_rb; - -MA_API ma_result ma_duplex_rb_init(ma_format captureFormat, ma_uint32 captureChannels, ma_uint32 sampleRate, ma_uint32 captureInternalSampleRate, ma_uint32 captureInternalPeriodSizeInFrames, const ma_allocation_callbacks* pAllocationCallbacks, ma_duplex_rb* pRB); -MA_API ma_result ma_duplex_rb_uninit(ma_duplex_rb* pRB); - - -/************************************************************************************************************************************************************ - -Miscellaneous Helpers - -************************************************************************************************************************************************************/ -/* -Retrieves a human readable description of the given result code. -*/ -MA_API const char* ma_result_description(ma_result result); - -/* -malloc() -*/ -MA_API void* ma_malloc(size_t sz, const ma_allocation_callbacks* pAllocationCallbacks); - -/* -calloc() -*/ -MA_API void* ma_calloc(size_t sz, const ma_allocation_callbacks* pAllocationCallbacks); - -/* -realloc() -*/ -MA_API void* ma_realloc(void* p, size_t sz, const ma_allocation_callbacks* pAllocationCallbacks); - -/* -free() -*/ -MA_API void ma_free(void* p, const ma_allocation_callbacks* pAllocationCallbacks); - -/* -Performs an aligned malloc, with the assumption that the alignment is a power of 2. -*/ -MA_API void* ma_aligned_malloc(size_t sz, size_t alignment, const ma_allocation_callbacks* pAllocationCallbacks); - -/* -Free's an aligned malloc'd buffer. -*/ -MA_API void ma_aligned_free(void* p, const ma_allocation_callbacks* pAllocationCallbacks); - -/* -Retrieves a friendly name for a format. -*/ -MA_API const char* ma_get_format_name(ma_format format); - -/* -Blends two frames in floating point format. -*/ -MA_API void ma_blend_f32(float* pOut, float* pInA, float* pInB, float factor, ma_uint32 channels); - -/* -Retrieves the size of a sample in bytes for the given format. - -This API is efficient and is implemented using a lookup table. - -Thread Safety: SAFE - This API is pure. -*/ -MA_API ma_uint32 ma_get_bytes_per_sample(ma_format format); -static MA_INLINE ma_uint32 ma_get_bytes_per_frame(ma_format format, ma_uint32 channels) { return ma_get_bytes_per_sample(format) * channels; } - -/* -Converts a log level to a string. -*/ -MA_API const char* ma_log_level_to_string(ma_uint32 logLevel); - - - - -/************************************************************************************************************************************************************ - -Synchronization - -************************************************************************************************************************************************************/ -/* -Locks a spinlock. -*/ -MA_API ma_result ma_spinlock_lock(volatile ma_spinlock* pSpinlock); - -/* -Locks a spinlock, but does not yield() when looping. -*/ -MA_API ma_result ma_spinlock_lock_noyield(volatile ma_spinlock* pSpinlock); - -/* -Unlocks a spinlock. -*/ -MA_API ma_result ma_spinlock_unlock(volatile ma_spinlock* pSpinlock); - - -#ifndef MA_NO_THREADING - -/* -Creates a mutex. - -A mutex must be created from a valid context. A mutex is initially unlocked. -*/ -MA_API ma_result ma_mutex_init(ma_mutex* pMutex); - -/* -Deletes a mutex. -*/ -MA_API void ma_mutex_uninit(ma_mutex* pMutex); - -/* -Locks a mutex with an infinite timeout. -*/ -MA_API void ma_mutex_lock(ma_mutex* pMutex); - -/* -Unlocks a mutex. -*/ -MA_API void ma_mutex_unlock(ma_mutex* pMutex); - - -/* -Initializes an auto-reset event. -*/ -MA_API ma_result ma_event_init(ma_event* pEvent); - -/* -Uninitializes an auto-reset event. -*/ -MA_API void ma_event_uninit(ma_event* pEvent); - -/* -Waits for the specified auto-reset event to become signalled. -*/ -MA_API ma_result ma_event_wait(ma_event* pEvent); - -/* -Signals the specified auto-reset event. -*/ -MA_API ma_result ma_event_signal(ma_event* pEvent); - - -MA_API ma_result ma_semaphore_init(int initialValue, ma_semaphore* pSemaphore); -MA_API void ma_semaphore_uninit(ma_semaphore* pSemaphore); -MA_API ma_result ma_semaphore_wait(ma_semaphore* pSemaphore); -MA_API ma_result ma_semaphore_release(ma_semaphore* pSemaphore); -#endif /* MA_NO_THREADING */ - - -/* -Fence -===== -This locks while the counter is larger than 0. Counter can be incremented and decremented by any -thread, but care needs to be taken when waiting. It is possible for one thread to acquire the -fence just as another thread returns from ma_fence_wait(). - -The idea behind a fence is to allow you to wait for a group of operations to complete. When an -operation starts, the counter is incremented which locks the fence. When the operation completes, -the fence will be released which decrements the counter. ma_fence_wait() will block until the -counter hits zero. - -If threading is disabled, ma_fence_wait() will spin on the counter. -*/ -typedef struct -{ -#ifndef MA_NO_THREADING - ma_event e; -#endif - ma_uint32 counter; -} ma_fence; - -MA_API ma_result ma_fence_init(ma_fence* pFence); -MA_API void ma_fence_uninit(ma_fence* pFence); -MA_API ma_result ma_fence_acquire(ma_fence* pFence); /* Increment counter. */ -MA_API ma_result ma_fence_release(ma_fence* pFence); /* Decrement counter. */ -MA_API ma_result ma_fence_wait(ma_fence* pFence); /* Wait for counter to reach 0. */ - - - -/* -Notification callback for asynchronous operations. -*/ -typedef void ma_async_notification; - -typedef struct -{ - void (* onSignal)(ma_async_notification* pNotification); -} ma_async_notification_callbacks; - -MA_API ma_result ma_async_notification_signal(ma_async_notification* pNotification); - - -/* -Simple polling notification. - -This just sets a variable when the notification has been signalled which is then polled with ma_async_notification_poll_is_signalled() -*/ -typedef struct -{ - ma_async_notification_callbacks cb; - ma_bool32 signalled; -} ma_async_notification_poll; - -MA_API ma_result ma_async_notification_poll_init(ma_async_notification_poll* pNotificationPoll); -MA_API ma_bool32 ma_async_notification_poll_is_signalled(const ma_async_notification_poll* pNotificationPoll); - - -/* -Event Notification - -This uses an ma_event. If threading is disabled (MA_NO_THREADING), initialization will fail. -*/ -typedef struct -{ - ma_async_notification_callbacks cb; -#ifndef MA_NO_THREADING - ma_event e; -#endif -} ma_async_notification_event; - -MA_API ma_result ma_async_notification_event_init(ma_async_notification_event* pNotificationEvent); -MA_API ma_result ma_async_notification_event_uninit(ma_async_notification_event* pNotificationEvent); -MA_API ma_result ma_async_notification_event_wait(ma_async_notification_event* pNotificationEvent); -MA_API ma_result ma_async_notification_event_signal(ma_async_notification_event* pNotificationEvent); - - - - -/************************************************************************************************************************************************************ - -Job Queue - -************************************************************************************************************************************************************/ - -/* -Slot Allocator --------------- -The idea of the slot allocator is for it to be used in conjunction with a fixed sized buffer. You use the slot allocator to allocate an index that can be used -as the insertion point for an object. - -Slots are reference counted to help mitigate the ABA problem in the lock-free queue we use for tracking jobs. - -The slot index is stored in the low 32 bits. The reference counter is stored in the high 32 bits: - - +-----------------+-----------------+ - | 32 Bits | 32 Bits | - +-----------------+-----------------+ - | Reference Count | Slot Index | - +-----------------+-----------------+ -*/ -typedef struct -{ - ma_uint32 capacity; /* The number of slots to make available. */ -} ma_slot_allocator_config; - -MA_API ma_slot_allocator_config ma_slot_allocator_config_init(ma_uint32 capacity); - - -typedef struct -{ - MA_ATOMIC(4, ma_uint32) bitfield; /* Must be used atomically because the allocation and freeing routines need to make copies of this which must never be optimized away by the compiler. */ -} ma_slot_allocator_group; - -typedef struct -{ - ma_slot_allocator_group* pGroups; /* Slots are grouped in chunks of 32. */ - ma_uint32* pSlots; /* 32 bits for reference counting for ABA mitigation. */ - ma_uint32 count; /* Allocation count. */ - ma_uint32 capacity; - - /* Memory management. */ - ma_bool32 _ownsHeap; - void* _pHeap; -} ma_slot_allocator; - -MA_API ma_result ma_slot_allocator_get_heap_size(const ma_slot_allocator_config* pConfig, size_t* pHeapSizeInBytes); -MA_API ma_result ma_slot_allocator_init_preallocated(const ma_slot_allocator_config* pConfig, void* pHeap, ma_slot_allocator* pAllocator); -MA_API ma_result ma_slot_allocator_init(const ma_slot_allocator_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_slot_allocator* pAllocator); -MA_API void ma_slot_allocator_uninit(ma_slot_allocator* pAllocator, const ma_allocation_callbacks* pAllocationCallbacks); -MA_API ma_result ma_slot_allocator_alloc(ma_slot_allocator* pAllocator, ma_uint64* pSlot); -MA_API ma_result ma_slot_allocator_free(ma_slot_allocator* pAllocator, ma_uint64 slot); - - -typedef struct ma_job ma_job; - -/* -Callback for processing a job. Each job type will have their own processing callback which will be -called by ma_job_process(). -*/ -typedef ma_result (* ma_job_proc)(ma_job* pJob); - -/* When a job type is added here an callback needs to be added go "g_jobVTable" in the implementation section. */ -typedef enum -{ - /* Miscellaneous. */ - MA_JOB_TYPE_QUIT = 0, - MA_JOB_TYPE_CUSTOM, - - /* Resource Manager. */ - MA_JOB_TYPE_RESOURCE_MANAGER_LOAD_DATA_BUFFER_NODE, - MA_JOB_TYPE_RESOURCE_MANAGER_FREE_DATA_BUFFER_NODE, - MA_JOB_TYPE_RESOURCE_MANAGER_PAGE_DATA_BUFFER_NODE, - MA_JOB_TYPE_RESOURCE_MANAGER_LOAD_DATA_BUFFER, - MA_JOB_TYPE_RESOURCE_MANAGER_FREE_DATA_BUFFER, - MA_JOB_TYPE_RESOURCE_MANAGER_LOAD_DATA_STREAM, - MA_JOB_TYPE_RESOURCE_MANAGER_FREE_DATA_STREAM, - MA_JOB_TYPE_RESOURCE_MANAGER_PAGE_DATA_STREAM, - MA_JOB_TYPE_RESOURCE_MANAGER_SEEK_DATA_STREAM, - - /* Device. */ - MA_JOB_TYPE_DEVICE_AAUDIO_REROUTE, - - /* Count. Must always be last. */ - MA_JOB_TYPE_COUNT -} ma_job_type; - -struct ma_job -{ - union - { - struct - { - ma_uint16 code; /* Job type. */ - ma_uint16 slot; /* Index into a ma_slot_allocator. */ - ma_uint32 refcount; - } breakup; - ma_uint64 allocation; - } toc; /* 8 bytes. We encode the job code into the slot allocation data to save space. */ - MA_ATOMIC(8, ma_uint64) next; /* refcount + slot for the next item. Does not include the job code. */ - ma_uint32 order; /* Execution order. Used to create a data dependency and ensure a job is executed in order. Usage is contextual depending on the job type. */ - - union - { - /* Miscellaneous. */ - struct - { - ma_job_proc proc; - ma_uintptr data0; - ma_uintptr data1; - } custom; - - /* Resource Manager */ - union - { - struct - { - /*ma_resource_manager**/ void* pResourceManager; - /*ma_resource_manager_data_buffer_node**/ void* pDataBufferNode; - char* pFilePath; - wchar_t* pFilePathW; - ma_uint32 flags; /* Resource manager data source flags that were used when initializing the data buffer. */ - ma_async_notification* pInitNotification; /* Signalled when the data buffer has been initialized and the format/channels/rate can be retrieved. */ - ma_async_notification* pDoneNotification; /* Signalled when the data buffer has been fully decoded. Will be passed through to MA_JOB_TYPE_RESOURCE_MANAGER_PAGE_DATA_BUFFER_NODE when decoding. */ - ma_fence* pInitFence; /* Released when initialization of the decoder is complete. */ - ma_fence* pDoneFence; /* Released if initialization of the decoder fails. Passed through to PAGE_DATA_BUFFER_NODE untouched if init is successful. */ - } loadDataBufferNode; - struct - { - /*ma_resource_manager**/ void* pResourceManager; - /*ma_resource_manager_data_buffer_node**/ void* pDataBufferNode; - ma_async_notification* pDoneNotification; - ma_fence* pDoneFence; - } freeDataBufferNode; - struct - { - /*ma_resource_manager**/ void* pResourceManager; - /*ma_resource_manager_data_buffer_node**/ void* pDataBufferNode; - /*ma_decoder**/ void* pDecoder; - ma_async_notification* pDoneNotification; /* Signalled when the data buffer has been fully decoded. */ - ma_fence* pDoneFence; /* Passed through from LOAD_DATA_BUFFER_NODE and released when the data buffer completes decoding or an error occurs. */ - } pageDataBufferNode; - - struct - { - /*ma_resource_manager_data_buffer**/ void* pDataBuffer; - ma_async_notification* pInitNotification; /* Signalled when the data buffer has been initialized and the format/channels/rate can be retrieved. */ - ma_async_notification* pDoneNotification; /* Signalled when the data buffer has been fully decoded. */ - ma_fence* pInitFence; /* Released when the data buffer has been initialized and the format/channels/rate can be retrieved. */ - ma_fence* pDoneFence; /* Released when the data buffer has been fully decoded. */ - ma_uint64 rangeBegInPCMFrames; - ma_uint64 rangeEndInPCMFrames; - ma_uint64 loopPointBegInPCMFrames; - ma_uint64 loopPointEndInPCMFrames; - ma_uint32 isLooping; - } loadDataBuffer; - struct - { - /*ma_resource_manager_data_buffer**/ void* pDataBuffer; - ma_async_notification* pDoneNotification; - ma_fence* pDoneFence; - } freeDataBuffer; - - struct - { - /*ma_resource_manager_data_stream**/ void* pDataStream; - char* pFilePath; /* Allocated when the job is posted, freed by the job thread after loading. */ - wchar_t* pFilePathW; /* ^ As above ^. Only used if pFilePath is NULL. */ - ma_uint64 initialSeekPoint; - ma_async_notification* pInitNotification; /* Signalled after the first two pages have been decoded and frames can be read from the stream. */ - ma_fence* pInitFence; - } loadDataStream; - struct - { - /*ma_resource_manager_data_stream**/ void* pDataStream; - ma_async_notification* pDoneNotification; - ma_fence* pDoneFence; - } freeDataStream; - struct - { - /*ma_resource_manager_data_stream**/ void* pDataStream; - ma_uint32 pageIndex; /* The index of the page to decode into. */ - } pageDataStream; - struct - { - /*ma_resource_manager_data_stream**/ void* pDataStream; - ma_uint64 frameIndex; - } seekDataStream; - } resourceManager; - - /* Device. */ - union - { - union - { - struct - { - /*ma_device**/ void* pDevice; - /*ma_device_type*/ ma_uint32 deviceType; - } reroute; - } aaudio; - } device; - } data; -}; - -MA_API ma_job ma_job_init(ma_uint16 code); -MA_API ma_result ma_job_process(ma_job* pJob); - - -/* -When set, ma_job_queue_next() will not wait and no semaphore will be signaled in -ma_job_queue_post(). ma_job_queue_next() will return MA_NO_DATA_AVAILABLE if nothing is available. - -This flag should always be used for platforms that do not support multithreading. -*/ -typedef enum -{ - MA_JOB_QUEUE_FLAG_NON_BLOCKING = 0x00000001 -} ma_job_queue_flags; - -typedef struct -{ - ma_uint32 flags; - ma_uint32 capacity; /* The maximum number of jobs that can fit in the queue at a time. */ -} ma_job_queue_config; - -MA_API ma_job_queue_config ma_job_queue_config_init(ma_uint32 flags, ma_uint32 capacity); - - -typedef struct -{ - ma_uint32 flags; /* Flags passed in at initialization time. */ - ma_uint32 capacity; /* The maximum number of jobs that can fit in the queue at a time. Set by the config. */ - MA_ATOMIC(8, ma_uint64) head; /* The first item in the list. Required for removing from the top of the list. */ - MA_ATOMIC(8, ma_uint64) tail; /* The last item in the list. Required for appending to the end of the list. */ -#ifndef MA_NO_THREADING - ma_semaphore sem; /* Only used when MA_JOB_QUEUE_FLAG_NON_BLOCKING is unset. */ -#endif - ma_slot_allocator allocator; - ma_job* pJobs; -#ifndef MA_USE_EXPERIMENTAL_LOCK_FREE_JOB_QUEUE - ma_spinlock lock; -#endif - - /* Memory management. */ - void* _pHeap; - ma_bool32 _ownsHeap; -} ma_job_queue; - -MA_API ma_result ma_job_queue_get_heap_size(const ma_job_queue_config* pConfig, size_t* pHeapSizeInBytes); -MA_API ma_result ma_job_queue_init_preallocated(const ma_job_queue_config* pConfig, void* pHeap, ma_job_queue* pQueue); -MA_API ma_result ma_job_queue_init(const ma_job_queue_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_job_queue* pQueue); -MA_API void ma_job_queue_uninit(ma_job_queue* pQueue, const ma_allocation_callbacks* pAllocationCallbacks); -MA_API ma_result ma_job_queue_post(ma_job_queue* pQueue, const ma_job* pJob); -MA_API ma_result ma_job_queue_next(ma_job_queue* pQueue, ma_job* pJob); /* Returns MA_CANCELLED if the next job is a quit job. */ - - - -/************************************************************************************************************************************************************ -************************************************************************************************************************************************************* - -DEVICE I/O -========== - -This section contains the APIs for device playback and capture. Here is where you'll find ma_device_init(), etc. - -************************************************************************************************************************************************************* -************************************************************************************************************************************************************/ -#ifndef MA_NO_DEVICE_IO -/* Some backends are only supported on certain platforms. */ -#if defined(MA_WIN32) - #define MA_SUPPORT_WASAPI - - #if defined(MA_WIN32_DESKTOP) /* DirectSound and WinMM backends are only supported on desktops. */ - #define MA_SUPPORT_DSOUND - #define MA_SUPPORT_WINMM - - /* Don't enable JACK here if compiling with Cosmopolitan. It'll be enabled in the Linux section below. */ - #if !defined(__COSMOPOLITAN__) - #define MA_SUPPORT_JACK /* JACK is technically supported on Windows, but I don't know how many people use it in practice... */ - #endif - #endif -#endif -#if defined(MA_UNIX) && !defined(MA_ORBIS) && !defined(MA_PROSPERO) - #if defined(MA_LINUX) - #if !defined(MA_ANDROID) && !defined(__COSMOPOLITAN__) /* ALSA is not supported on Android. */ - #define MA_SUPPORT_ALSA - #endif - #endif - #if !defined(MA_BSD) && !defined(MA_ANDROID) && !defined(MA_EMSCRIPTEN) - #define MA_SUPPORT_PULSEAUDIO - #define MA_SUPPORT_JACK - #endif - #if defined(__OpenBSD__) /* <-- Change this to "#if defined(MA_BSD)" to enable sndio on all BSD flavors. */ - #define MA_SUPPORT_SNDIO /* sndio is only supported on OpenBSD for now. May be expanded later if there's demand. */ - #endif - #if defined(__NetBSD__) || defined(__OpenBSD__) - #define MA_SUPPORT_AUDIO4 /* Only support audio(4) on platforms with known support. */ - #endif - #if defined(__FreeBSD__) || defined(__DragonFly__) - #define MA_SUPPORT_OSS /* Only support OSS on specific platforms with known support. */ - #endif -#endif -#if defined(MA_ANDROID) - #define MA_SUPPORT_AAUDIO - #define MA_SUPPORT_OPENSL -#endif -#if defined(MA_APPLE) - #define MA_SUPPORT_COREAUDIO -#endif -#if defined(MA_EMSCRIPTEN) - #define MA_SUPPORT_WEBAUDIO -#endif - -/* All platforms should support custom backends. */ -#define MA_SUPPORT_CUSTOM - -/* Explicitly disable the Null backend for Emscripten because it uses a background thread which is not properly supported right now. */ -#if !defined(MA_EMSCRIPTEN) -#define MA_SUPPORT_NULL -#endif - - -#if defined(MA_SUPPORT_WASAPI) && !defined(MA_NO_WASAPI) && (!defined(MA_ENABLE_ONLY_SPECIFIC_BACKENDS) || defined(MA_ENABLE_WASAPI)) - #define MA_HAS_WASAPI -#endif -#if defined(MA_SUPPORT_DSOUND) && !defined(MA_NO_DSOUND) && (!defined(MA_ENABLE_ONLY_SPECIFIC_BACKENDS) || defined(MA_ENABLE_DSOUND)) - #define MA_HAS_DSOUND -#endif -#if defined(MA_SUPPORT_WINMM) && !defined(MA_NO_WINMM) && (!defined(MA_ENABLE_ONLY_SPECIFIC_BACKENDS) || defined(MA_ENABLE_WINMM)) - #define MA_HAS_WINMM -#endif -#if defined(MA_SUPPORT_ALSA) && !defined(MA_NO_ALSA) && (!defined(MA_ENABLE_ONLY_SPECIFIC_BACKENDS) || defined(MA_ENABLE_ALSA)) - #define MA_HAS_ALSA -#endif -#if defined(MA_SUPPORT_PULSEAUDIO) && !defined(MA_NO_PULSEAUDIO) && (!defined(MA_ENABLE_ONLY_SPECIFIC_BACKENDS) || defined(MA_ENABLE_PULSEAUDIO)) - #define MA_HAS_PULSEAUDIO -#endif -#if defined(MA_SUPPORT_JACK) && !defined(MA_NO_JACK) && (!defined(MA_ENABLE_ONLY_SPECIFIC_BACKENDS) || defined(MA_ENABLE_JACK)) - #define MA_HAS_JACK -#endif -#if defined(MA_SUPPORT_COREAUDIO) && !defined(MA_NO_COREAUDIO) && (!defined(MA_ENABLE_ONLY_SPECIFIC_BACKENDS) || defined(MA_ENABLE_COREAUDIO)) - #define MA_HAS_COREAUDIO -#endif -#if defined(MA_SUPPORT_SNDIO) && !defined(MA_NO_SNDIO) && (!defined(MA_ENABLE_ONLY_SPECIFIC_BACKENDS) || defined(MA_ENABLE_SNDIO)) - #define MA_HAS_SNDIO -#endif -#if defined(MA_SUPPORT_AUDIO4) && !defined(MA_NO_AUDIO4) && (!defined(MA_ENABLE_ONLY_SPECIFIC_BACKENDS) || defined(MA_ENABLE_AUDIO4)) - #define MA_HAS_AUDIO4 -#endif -#if defined(MA_SUPPORT_OSS) && !defined(MA_NO_OSS) && (!defined(MA_ENABLE_ONLY_SPECIFIC_BACKENDS) || defined(MA_ENABLE_OSS)) - #define MA_HAS_OSS -#endif -#if defined(MA_SUPPORT_AAUDIO) && !defined(MA_NO_AAUDIO) && (!defined(MA_ENABLE_ONLY_SPECIFIC_BACKENDS) || defined(MA_ENABLE_AAUDIO)) - #define MA_HAS_AAUDIO -#endif -#if defined(MA_SUPPORT_OPENSL) && !defined(MA_NO_OPENSL) && (!defined(MA_ENABLE_ONLY_SPECIFIC_BACKENDS) || defined(MA_ENABLE_OPENSL)) - #define MA_HAS_OPENSL -#endif -#if defined(MA_SUPPORT_WEBAUDIO) && !defined(MA_NO_WEBAUDIO) && (!defined(MA_ENABLE_ONLY_SPECIFIC_BACKENDS) || defined(MA_ENABLE_WEBAUDIO)) - #define MA_HAS_WEBAUDIO -#endif -#if defined(MA_SUPPORT_CUSTOM) && !defined(MA_NO_CUSTOM) && (!defined(MA_ENABLE_ONLY_SPECIFIC_BACKENDS) || defined(MA_ENABLE_CUSTOM)) - #define MA_HAS_CUSTOM -#endif -#if defined(MA_SUPPORT_NULL) && !defined(MA_NO_NULL) && (!defined(MA_ENABLE_ONLY_SPECIFIC_BACKENDS) || defined(MA_ENABLE_NULL)) - #define MA_HAS_NULL -#endif - -typedef enum -{ - ma_device_state_uninitialized = 0, - ma_device_state_stopped = 1, /* The device's default state after initialization. */ - ma_device_state_started = 2, /* The device is started and is requesting and/or delivering audio data. */ - ma_device_state_starting = 3, /* Transitioning from a stopped state to started. */ - ma_device_state_stopping = 4 /* Transitioning from a started state to stopped. */ -} ma_device_state; - -MA_ATOMIC_SAFE_TYPE_DECL(i32, 4, device_state) - - -#ifdef MA_SUPPORT_WASAPI -/* We need a IMMNotificationClient object for WASAPI. */ -typedef struct -{ - void* lpVtbl; - ma_uint32 counter; - ma_device* pDevice; -} ma_IMMNotificationClient; -#endif - -/* Backend enums must be in priority order. */ -typedef enum -{ - ma_backend_wasapi, - ma_backend_dsound, - ma_backend_winmm, - ma_backend_coreaudio, - ma_backend_sndio, - ma_backend_audio4, - ma_backend_oss, - ma_backend_pulseaudio, - ma_backend_alsa, - ma_backend_jack, - ma_backend_aaudio, - ma_backend_opensl, - ma_backend_webaudio, - ma_backend_custom, /* <-- Custom backend, with callbacks defined by the context config. */ - ma_backend_null /* <-- Must always be the last item. Lowest priority, and used as the terminator for backend enumeration. */ -} ma_backend; - -#define MA_BACKEND_COUNT (ma_backend_null+1) - - -/* -Device job thread. This is used by backends that require asynchronous processing of certain -operations. It is not used by all backends. - -The device job thread is made up of a thread and a job queue. You can post a job to the thread with -ma_device_job_thread_post(). The thread will do the processing of the job. -*/ -typedef struct -{ - ma_bool32 noThread; /* Set this to true if you want to process jobs yourself. */ - ma_uint32 jobQueueCapacity; - ma_uint32 jobQueueFlags; -} ma_device_job_thread_config; - -MA_API ma_device_job_thread_config ma_device_job_thread_config_init(void); - -typedef struct -{ - ma_thread thread; - ma_job_queue jobQueue; - ma_bool32 _hasThread; -} ma_device_job_thread; - -MA_API ma_result ma_device_job_thread_init(const ma_device_job_thread_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_device_job_thread* pJobThread); -MA_API void ma_device_job_thread_uninit(ma_device_job_thread* pJobThread, const ma_allocation_callbacks* pAllocationCallbacks); -MA_API ma_result ma_device_job_thread_post(ma_device_job_thread* pJobThread, const ma_job* pJob); -MA_API ma_result ma_device_job_thread_next(ma_device_job_thread* pJobThread, ma_job* pJob); - - - -/* Device notification types. */ -typedef enum -{ - ma_device_notification_type_started, - ma_device_notification_type_stopped, - ma_device_notification_type_rerouted, - ma_device_notification_type_interruption_began, - ma_device_notification_type_interruption_ended, - ma_device_notification_type_unlocked -} ma_device_notification_type; - -typedef struct -{ - ma_device* pDevice; - ma_device_notification_type type; - union - { - struct - { - int _unused; - } started; - struct - { - int _unused; - } stopped; - struct - { - int _unused; - } rerouted; - struct - { - int _unused; - } interruption; - } data; -} ma_device_notification; - -/* -The notification callback for when the application should be notified of a change to the device. - -This callback is used for notifying the application of changes such as when the device has started, -stopped, rerouted or an interruption has occurred. Note that not all backends will post all -notification types. For example, some backends will perform automatic stream routing without any -kind of notification to the host program which means miniaudio will never know about it and will -never be able to fire the rerouted notification. You should keep this in mind when designing your -program. - -The stopped notification will *not* get fired when a device is rerouted. - - -Parameters ----------- -pNotification (in) - A pointer to a structure containing information about the event. Use the `pDevice` member of - this object to retrieve the relevant device. The `type` member can be used to discriminate - against each of the notification types. - - -Remarks -------- -Do not restart or uninitialize the device from the callback. - -Not all notifications will be triggered by all backends, however the started and stopped events -should be reliable for all backends. Some backends do not have a good way to detect device -stoppages due to unplugging the device which may result in the stopped callback not getting -fired. This has been observed with at least one BSD variant. - -The rerouted notification is fired *after* the reroute has occurred. The stopped notification will -*not* get fired when a device is rerouted. The following backends are known to do automatic stream -rerouting, but do not have a way to be notified of the change: - - * DirectSound - -The interruption notifications are used on mobile platforms for detecting when audio is interrupted -due to things like an incoming phone call. Currently this is only implemented on iOS. None of the -Android backends will report this notification. -*/ -typedef void (* ma_device_notification_proc)(const ma_device_notification* pNotification); - - -/* -The callback for processing audio data from the device. - -The data callback is fired by miniaudio whenever the device needs to have more data delivered to a playback device, or when a capture device has some data -available. This is called as soon as the backend asks for more data which means it may be called with inconsistent frame counts. You cannot assume the -callback will be fired with a consistent frame count. - - -Parameters ----------- -pDevice (in) - A pointer to the relevant device. - -pOutput (out) - A pointer to the output buffer that will receive audio data that will later be played back through the speakers. This will be non-null for a playback or - full-duplex device and null for a capture and loopback device. - -pInput (in) - A pointer to the buffer containing input data from a recording device. This will be non-null for a capture, full-duplex or loopback device and null for a - playback device. - -frameCount (in) - The number of PCM frames to process. Note that this will not necessarily be equal to what you requested when you initialized the device. The - `periodSizeInFrames` and `periodSizeInMilliseconds` members of the device config are just hints, and are not necessarily exactly what you'll get. You must - not assume this will always be the same value each time the callback is fired. - - -Remarks -------- -You cannot stop and start the device from inside the callback or else you'll get a deadlock. You must also not uninitialize the device from inside the -callback. The following APIs cannot be called from inside the callback: - - ma_device_init() - ma_device_init_ex() - ma_device_uninit() - ma_device_start() - ma_device_stop() - -The proper way to stop the device is to call `ma_device_stop()` from a different thread, normally the main application thread. -*/ -typedef void (* ma_device_data_proc)(ma_device* pDevice, void* pOutput, const void* pInput, ma_uint32 frameCount); - - - - -/* -DEPRECATED. Use ma_device_notification_proc instead. - -The callback for when the device has been stopped. - -This will be called when the device is stopped explicitly with `ma_device_stop()` and also called implicitly when the device is stopped through external forces -such as being unplugged or an internal error occurring. - - -Parameters ----------- -pDevice (in) - A pointer to the device that has just stopped. - - -Remarks -------- -Do not restart or uninitialize the device from the callback. -*/ -typedef void (* ma_stop_proc)(ma_device* pDevice); /* DEPRECATED. Use ma_device_notification_proc instead. */ - -typedef enum -{ - ma_device_type_playback = 1, - ma_device_type_capture = 2, - ma_device_type_duplex = ma_device_type_playback | ma_device_type_capture, /* 3 */ - ma_device_type_loopback = 4 -} ma_device_type; - -typedef enum -{ - ma_share_mode_shared = 0, - ma_share_mode_exclusive -} ma_share_mode; - -/* iOS/tvOS/watchOS session categories. */ -typedef enum -{ - ma_ios_session_category_default = 0, /* AVAudioSessionCategoryPlayAndRecord. */ - ma_ios_session_category_none, /* Leave the session category unchanged. */ - ma_ios_session_category_ambient, /* AVAudioSessionCategoryAmbient */ - ma_ios_session_category_solo_ambient, /* AVAudioSessionCategorySoloAmbient */ - ma_ios_session_category_playback, /* AVAudioSessionCategoryPlayback */ - ma_ios_session_category_record, /* AVAudioSessionCategoryRecord */ - ma_ios_session_category_play_and_record, /* AVAudioSessionCategoryPlayAndRecord */ - ma_ios_session_category_multi_route /* AVAudioSessionCategoryMultiRoute */ -} ma_ios_session_category; - -/* iOS/tvOS/watchOS session category options */ -typedef enum -{ - ma_ios_session_category_option_mix_with_others = 0x01, /* AVAudioSessionCategoryOptionMixWithOthers */ - ma_ios_session_category_option_duck_others = 0x02, /* AVAudioSessionCategoryOptionDuckOthers */ - ma_ios_session_category_option_allow_bluetooth = 0x04, /* AVAudioSessionCategoryOptionAllowBluetooth */ - ma_ios_session_category_option_default_to_speaker = 0x08, /* AVAudioSessionCategoryOptionDefaultToSpeaker */ - ma_ios_session_category_option_interrupt_spoken_audio_and_mix_with_others = 0x11, /* AVAudioSessionCategoryOptionInterruptSpokenAudioAndMixWithOthers */ - ma_ios_session_category_option_allow_bluetooth_a2dp = 0x20, /* AVAudioSessionCategoryOptionAllowBluetoothA2DP */ - ma_ios_session_category_option_allow_air_play = 0x40, /* AVAudioSessionCategoryOptionAllowAirPlay */ -} ma_ios_session_category_option; - -/* OpenSL stream types. */ -typedef enum -{ - ma_opensl_stream_type_default = 0, /* Leaves the stream type unset. */ - ma_opensl_stream_type_voice, /* SL_ANDROID_STREAM_VOICE */ - ma_opensl_stream_type_system, /* SL_ANDROID_STREAM_SYSTEM */ - ma_opensl_stream_type_ring, /* SL_ANDROID_STREAM_RING */ - ma_opensl_stream_type_media, /* SL_ANDROID_STREAM_MEDIA */ - ma_opensl_stream_type_alarm, /* SL_ANDROID_STREAM_ALARM */ - ma_opensl_stream_type_notification /* SL_ANDROID_STREAM_NOTIFICATION */ -} ma_opensl_stream_type; - -/* OpenSL recording presets. */ -typedef enum -{ - ma_opensl_recording_preset_default = 0, /* Leaves the input preset unset. */ - ma_opensl_recording_preset_generic, /* SL_ANDROID_RECORDING_PRESET_GENERIC */ - ma_opensl_recording_preset_camcorder, /* SL_ANDROID_RECORDING_PRESET_CAMCORDER */ - ma_opensl_recording_preset_voice_recognition, /* SL_ANDROID_RECORDING_PRESET_VOICE_RECOGNITION */ - ma_opensl_recording_preset_voice_communication, /* SL_ANDROID_RECORDING_PRESET_VOICE_COMMUNICATION */ - ma_opensl_recording_preset_voice_unprocessed /* SL_ANDROID_RECORDING_PRESET_UNPROCESSED */ -} ma_opensl_recording_preset; - -/* WASAPI audio thread priority characteristics. */ -typedef enum -{ - ma_wasapi_usage_default = 0, - ma_wasapi_usage_games, - ma_wasapi_usage_pro_audio, -} ma_wasapi_usage; - -/* AAudio usage types. */ -typedef enum -{ - ma_aaudio_usage_default = 0, /* Leaves the usage type unset. */ - ma_aaudio_usage_media, /* AAUDIO_USAGE_MEDIA */ - ma_aaudio_usage_voice_communication, /* AAUDIO_USAGE_VOICE_COMMUNICATION */ - ma_aaudio_usage_voice_communication_signalling, /* AAUDIO_USAGE_VOICE_COMMUNICATION_SIGNALLING */ - ma_aaudio_usage_alarm, /* AAUDIO_USAGE_ALARM */ - ma_aaudio_usage_notification, /* AAUDIO_USAGE_NOTIFICATION */ - ma_aaudio_usage_notification_ringtone, /* AAUDIO_USAGE_NOTIFICATION_RINGTONE */ - ma_aaudio_usage_notification_event, /* AAUDIO_USAGE_NOTIFICATION_EVENT */ - ma_aaudio_usage_assistance_accessibility, /* AAUDIO_USAGE_ASSISTANCE_ACCESSIBILITY */ - ma_aaudio_usage_assistance_navigation_guidance, /* AAUDIO_USAGE_ASSISTANCE_NAVIGATION_GUIDANCE */ - ma_aaudio_usage_assistance_sonification, /* AAUDIO_USAGE_ASSISTANCE_SONIFICATION */ - ma_aaudio_usage_game, /* AAUDIO_USAGE_GAME */ - ma_aaudio_usage_assitant, /* AAUDIO_USAGE_ASSISTANT */ - ma_aaudio_usage_emergency, /* AAUDIO_SYSTEM_USAGE_EMERGENCY */ - ma_aaudio_usage_safety, /* AAUDIO_SYSTEM_USAGE_SAFETY */ - ma_aaudio_usage_vehicle_status, /* AAUDIO_SYSTEM_USAGE_VEHICLE_STATUS */ - ma_aaudio_usage_announcement /* AAUDIO_SYSTEM_USAGE_ANNOUNCEMENT */ -} ma_aaudio_usage; - -/* AAudio content types. */ -typedef enum -{ - ma_aaudio_content_type_default = 0, /* Leaves the content type unset. */ - ma_aaudio_content_type_speech, /* AAUDIO_CONTENT_TYPE_SPEECH */ - ma_aaudio_content_type_music, /* AAUDIO_CONTENT_TYPE_MUSIC */ - ma_aaudio_content_type_movie, /* AAUDIO_CONTENT_TYPE_MOVIE */ - ma_aaudio_content_type_sonification /* AAUDIO_CONTENT_TYPE_SONIFICATION */ -} ma_aaudio_content_type; - -/* AAudio input presets. */ -typedef enum -{ - ma_aaudio_input_preset_default = 0, /* Leaves the input preset unset. */ - ma_aaudio_input_preset_generic, /* AAUDIO_INPUT_PRESET_GENERIC */ - ma_aaudio_input_preset_camcorder, /* AAUDIO_INPUT_PRESET_CAMCORDER */ - ma_aaudio_input_preset_voice_recognition, /* AAUDIO_INPUT_PRESET_VOICE_RECOGNITION */ - ma_aaudio_input_preset_voice_communication, /* AAUDIO_INPUT_PRESET_VOICE_COMMUNICATION */ - ma_aaudio_input_preset_unprocessed, /* AAUDIO_INPUT_PRESET_UNPROCESSED */ - ma_aaudio_input_preset_voice_performance /* AAUDIO_INPUT_PRESET_VOICE_PERFORMANCE */ -} ma_aaudio_input_preset; - -typedef enum -{ - ma_aaudio_allow_capture_default = 0, /* Leaves the allowed capture policy unset. */ - ma_aaudio_allow_capture_by_all, /* AAUDIO_ALLOW_CAPTURE_BY_ALL */ - ma_aaudio_allow_capture_by_system, /* AAUDIO_ALLOW_CAPTURE_BY_SYSTEM */ - ma_aaudio_allow_capture_by_none /* AAUDIO_ALLOW_CAPTURE_BY_NONE */ -} ma_aaudio_allowed_capture_policy; - -typedef union -{ - ma_int64 counter; - double counterD; -} ma_timer; - -typedef union -{ - ma_wchar_win32 wasapi[64]; /* WASAPI uses a wchar_t string for identification. */ - ma_uint8 dsound[16]; /* DirectSound uses a GUID for identification. */ - /*UINT_PTR*/ ma_uint32 winmm; /* When creating a device, WinMM expects a Win32 UINT_PTR for device identification. In practice it's actually just a UINT. */ - char alsa[256]; /* ALSA uses a name string for identification. */ - char pulse[256]; /* PulseAudio uses a name string for identification. */ - int jack; /* JACK always uses default devices. */ - char coreaudio[256]; /* Core Audio uses a string for identification. */ - char sndio[256]; /* "snd/0", etc. */ - char audio4[256]; /* "/dev/audio", etc. */ - char oss[64]; /* "dev/dsp0", etc. "dev/dsp" for the default device. */ - ma_int32 aaudio; /* AAudio uses a 32-bit integer for identification. */ - ma_uint32 opensl; /* OpenSL|ES uses a 32-bit unsigned integer for identification. */ - char webaudio[32]; /* Web Audio always uses default devices for now, but if this changes it'll be a GUID. */ - union - { - int i; - char s[256]; - void* p; - } custom; /* The custom backend could be anything. Give them a few options. */ - int nullbackend; /* The null backend uses an integer for device IDs. */ -} ma_device_id; - -MA_API ma_bool32 ma_device_id_equal(const ma_device_id* pA, const ma_device_id* pB); - - -typedef struct ma_context_config ma_context_config; -typedef struct ma_device_config ma_device_config; -typedef struct ma_backend_callbacks ma_backend_callbacks; - -#define MA_DATA_FORMAT_FLAG_EXCLUSIVE_MODE (1U << 1) /* If set, this is supported in exclusive mode. Otherwise not natively supported by exclusive mode. */ - -#ifndef MA_MAX_DEVICE_NAME_LENGTH -#define MA_MAX_DEVICE_NAME_LENGTH 255 -#endif - -typedef struct -{ - /* Basic info. This is the only information guaranteed to be filled in during device enumeration. */ - ma_device_id id; - char name[MA_MAX_DEVICE_NAME_LENGTH + 1]; /* +1 for null terminator. */ - ma_bool32 isDefault; - - ma_uint32 nativeDataFormatCount; - struct - { - ma_format format; /* Sample format. If set to ma_format_unknown, all sample formats are supported. */ - ma_uint32 channels; /* If set to 0, all channels are supported. */ - ma_uint32 sampleRate; /* If set to 0, all sample rates are supported. */ - ma_uint32 flags; /* A combination of MA_DATA_FORMAT_FLAG_* flags. */ - } nativeDataFormats[/*ma_format_count * ma_standard_sample_rate_count * MA_MAX_CHANNELS*/ 64]; /* Not sure how big to make this. There can be *many* permutations for virtual devices which can support anything. */ -} ma_device_info; - -struct ma_device_config -{ - ma_device_type deviceType; - ma_uint32 sampleRate; - ma_uint32 periodSizeInFrames; - ma_uint32 periodSizeInMilliseconds; - ma_uint32 periods; - ma_performance_profile performanceProfile; - ma_bool8 noPreSilencedOutputBuffer; /* When set to true, the contents of the output buffer passed into the data callback will be left undefined rather than initialized to silence. */ - ma_bool8 noClip; /* When set to true, the contents of the output buffer passed into the data callback will not be clipped after returning. Only applies when the playback sample format is f32. */ - ma_bool8 noDisableDenormals; /* Do not disable denormals when firing the data callback. */ - ma_bool8 noFixedSizedCallback; /* Disables strict fixed-sized data callbacks. Setting this to true will result in the period size being treated only as a hint to the backend. This is an optimization for those who don't need fixed sized callbacks. */ - ma_device_data_proc dataCallback; - ma_device_notification_proc notificationCallback; - ma_stop_proc stopCallback; - void* pUserData; - ma_resampler_config resampling; - struct - { - const ma_device_id* pDeviceID; - ma_format format; - ma_uint32 channels; - ma_channel* pChannelMap; - ma_channel_mix_mode channelMixMode; - ma_bool32 calculateLFEFromSpatialChannels; /* When an output LFE channel is present, but no input LFE, set to true to set the output LFE to the average of all spatial channels (LR, FR, etc.). Ignored when an input LFE is present. */ - ma_share_mode shareMode; - } playback; - struct - { - const ma_device_id* pDeviceID; - ma_format format; - ma_uint32 channels; - ma_channel* pChannelMap; - ma_channel_mix_mode channelMixMode; - ma_bool32 calculateLFEFromSpatialChannels; /* When an output LFE channel is present, but no input LFE, set to true to set the output LFE to the average of all spatial channels (LR, FR, etc.). Ignored when an input LFE is present. */ - ma_share_mode shareMode; - } capture; - - struct - { - ma_wasapi_usage usage; /* When configured, uses Avrt APIs to set the thread characteristics. */ - ma_bool8 noAutoConvertSRC; /* When set to true, disables the use of AUDCLNT_STREAMFLAGS_AUTOCONVERTPCM. */ - ma_bool8 noDefaultQualitySRC; /* When set to true, disables the use of AUDCLNT_STREAMFLAGS_SRC_DEFAULT_QUALITY. */ - ma_bool8 noAutoStreamRouting; /* Disables automatic stream routing. */ - ma_bool8 noHardwareOffloading; /* Disables WASAPI's hardware offloading feature. */ - ma_uint32 loopbackProcessID; /* The process ID to include or exclude for loopback mode. Set to 0 to capture audio from all processes. Ignored when an explicit device ID is specified. */ - ma_bool8 loopbackProcessExclude; /* When set to true, excludes the process specified by loopbackProcessID. By default, the process will be included. */ - } wasapi; - struct - { - ma_bool32 noMMap; /* Disables MMap mode. */ - ma_bool32 noAutoFormat; /* Opens the ALSA device with SND_PCM_NO_AUTO_FORMAT. */ - ma_bool32 noAutoChannels; /* Opens the ALSA device with SND_PCM_NO_AUTO_CHANNELS. */ - ma_bool32 noAutoResample; /* Opens the ALSA device with SND_PCM_NO_AUTO_RESAMPLE. */ - } alsa; - struct - { - const char* pStreamNamePlayback; - const char* pStreamNameCapture; - int channelMap; - } pulse; - struct - { - ma_bool32 allowNominalSampleRateChange; /* Desktop only. When enabled, allows changing of the sample rate at the operating system level. */ - } coreaudio; - struct - { - ma_opensl_stream_type streamType; - ma_opensl_recording_preset recordingPreset; - ma_bool32 enableCompatibilityWorkarounds; - } opensl; - struct - { - ma_aaudio_usage usage; - ma_aaudio_content_type contentType; - ma_aaudio_input_preset inputPreset; - ma_aaudio_allowed_capture_policy allowedCapturePolicy; - ma_bool32 noAutoStartAfterReroute; - ma_bool32 enableCompatibilityWorkarounds; - ma_bool32 allowSetBufferCapacity; - } aaudio; -}; - - -/* -The callback for handling device enumeration. This is fired from `ma_context_enumerate_devices()`. - - -Parameters ----------- -pContext (in) - A pointer to the context performing the enumeration. - -deviceType (in) - The type of the device being enumerated. This will always be either `ma_device_type_playback` or `ma_device_type_capture`. - -pInfo (in) - A pointer to a `ma_device_info` containing the ID and name of the enumerated device. Note that this will not include detailed information about the device, - only basic information (ID and name). The reason for this is that it would otherwise require opening the backend device to probe for the information which - is too inefficient. - -pUserData (in) - The user data pointer passed into `ma_context_enumerate_devices()`. -*/ -typedef ma_bool32 (* ma_enum_devices_callback_proc)(ma_context* pContext, ma_device_type deviceType, const ma_device_info* pInfo, void* pUserData); - - -/* -Describes some basic details about a playback or capture device. -*/ -typedef struct -{ - const ma_device_id* pDeviceID; - ma_share_mode shareMode; - ma_format format; - ma_uint32 channels; - ma_uint32 sampleRate; - ma_channel channelMap[MA_MAX_CHANNELS]; - ma_uint32 periodSizeInFrames; - ma_uint32 periodSizeInMilliseconds; - ma_uint32 periodCount; -} ma_device_descriptor; - -/* -These are the callbacks required to be implemented for a backend. These callbacks are grouped into two parts: context and device. There is one context -to many devices. A device is created from a context. - -The general flow goes like this: - - 1) A context is created with `onContextInit()` - 1a) Available devices can be enumerated with `onContextEnumerateDevices()` if required. - 1b) Detailed information about a device can be queried with `onContextGetDeviceInfo()` if required. - 2) A device is created from the context that was created in the first step using `onDeviceInit()`, and optionally a device ID that was - selected from device enumeration via `onContextEnumerateDevices()`. - 3) A device is started or stopped with `onDeviceStart()` / `onDeviceStop()` - 4) Data is delivered to and from the device by the backend. This is always done based on the native format returned by the prior call - to `onDeviceInit()`. Conversion between the device's native format and the format requested by the application will be handled by - miniaudio internally. - -Initialization of the context is quite simple. You need to do any necessary initialization of internal objects and then output the -callbacks defined in this structure. - -Once the context has been initialized you can initialize a device. Before doing so, however, the application may want to know which -physical devices are available. This is where `onContextEnumerateDevices()` comes in. This is fairly simple. For each device, fire the -given callback with, at a minimum, the basic information filled out in `ma_device_info`. When the callback returns `MA_FALSE`, enumeration -needs to stop and the `onContextEnumerateDevices()` function returns with a success code. - -Detailed device information can be retrieved from a device ID using `onContextGetDeviceInfo()`. This takes as input the device type and ID, -and on output returns detailed information about the device in `ma_device_info`. The `onContextGetDeviceInfo()` callback must handle the -case when the device ID is NULL, in which case information about the default device needs to be retrieved. - -Once the context has been created and the device ID retrieved (if using anything other than the default device), the device can be created. -This is a little bit more complicated than initialization of the context due to its more complicated configuration. When initializing a -device, a duplex device may be requested. This means a separate data format needs to be specified for both playback and capture. On input, -the data format is set to what the application wants. On output it's set to the native format which should match as closely as possible to -the requested format. The conversion between the format requested by the application and the device's native format will be handled -internally by miniaudio. - -On input, if the sample format is set to `ma_format_unknown`, the backend is free to use whatever sample format it desires, so long as it's -supported by miniaudio. When the channel count is set to 0, the backend should use the device's native channel count. The same applies for -sample rate. For the channel map, the default should be used when `ma_channel_map_is_blank()` returns true (all channels set to -`MA_CHANNEL_NONE`). On input, the `periodSizeInFrames` or `periodSizeInMilliseconds` option should always be set. The backend should -inspect both of these variables. If `periodSizeInFrames` is set, it should take priority, otherwise it needs to be derived from the period -size in milliseconds (`periodSizeInMilliseconds`) and the sample rate, keeping in mind that the sample rate may be 0, in which case the -sample rate will need to be determined before calculating the period size in frames. On output, all members of the `ma_device_descriptor` -object should be set to a valid value, except for `periodSizeInMilliseconds` which is optional (`periodSizeInFrames` *must* be set). - -Starting and stopping of the device is done with `onDeviceStart()` and `onDeviceStop()` and should be self-explanatory. If the backend uses -asynchronous reading and writing, `onDeviceStart()` and `onDeviceStop()` should always be implemented. - -The handling of data delivery between the application and the device is the most complicated part of the process. To make this a bit -easier, some helper callbacks are available. If the backend uses a blocking read/write style of API, the `onDeviceRead()` and -`onDeviceWrite()` callbacks can optionally be implemented. These are blocking and work just like reading and writing from a file. If the -backend uses a callback for data delivery, that callback must call `ma_device_handle_backend_data_callback()` from within its callback. -This allows miniaudio to then process any necessary data conversion and then pass it to the miniaudio data callback. - -If the backend requires absolute flexibility with its data delivery, it can optionally implement the `onDeviceDataLoop()` callback -which will allow it to implement the logic that will run on the audio thread. This is much more advanced and is completely optional. - -The audio thread should run data delivery logic in a loop while `ma_device_get_state() == ma_device_state_started` and no errors have been -encountered. Do not start or stop the device here. That will be handled from outside the `onDeviceDataLoop()` callback. - -The invocation of the `onDeviceDataLoop()` callback will be handled by miniaudio. When you start the device, miniaudio will fire this -callback. When the device is stopped, the `ma_device_get_state() == ma_device_state_started` condition will fail and the loop will be terminated -which will then fall through to the part that stops the device. For an example on how to implement the `onDeviceDataLoop()` callback, -look at `ma_device_audio_thread__default_read_write()`. Implement the `onDeviceDataLoopWakeup()` callback if you need a mechanism to -wake up the audio thread. - -If the backend supports an optimized retrieval of device information from an initialized `ma_device` object, it should implement the -`onDeviceGetInfo()` callback. This is optional, in which case it will fall back to `onContextGetDeviceInfo()` which is less efficient. -*/ -struct ma_backend_callbacks -{ - ma_result (* onContextInit)(ma_context* pContext, const ma_context_config* pConfig, ma_backend_callbacks* pCallbacks); - ma_result (* onContextUninit)(ma_context* pContext); - ma_result (* onContextEnumerateDevices)(ma_context* pContext, ma_enum_devices_callback_proc callback, void* pUserData); - ma_result (* onContextGetDeviceInfo)(ma_context* pContext, ma_device_type deviceType, const ma_device_id* pDeviceID, ma_device_info* pDeviceInfo); - ma_result (* onDeviceInit)(ma_device* pDevice, const ma_device_config* pConfig, ma_device_descriptor* pDescriptorPlayback, ma_device_descriptor* pDescriptorCapture); - ma_result (* onDeviceUninit)(ma_device* pDevice); - ma_result (* onDeviceStart)(ma_device* pDevice); - ma_result (* onDeviceStop)(ma_device* pDevice); - ma_result (* onDeviceRead)(ma_device* pDevice, void* pFrames, ma_uint32 frameCount, ma_uint32* pFramesRead); - ma_result (* onDeviceWrite)(ma_device* pDevice, const void* pFrames, ma_uint32 frameCount, ma_uint32* pFramesWritten); - ma_result (* onDeviceDataLoop)(ma_device* pDevice); - ma_result (* onDeviceDataLoopWakeup)(ma_device* pDevice); - ma_result (* onDeviceGetInfo)(ma_device* pDevice, ma_device_type type, ma_device_info* pDeviceInfo); -}; - -struct ma_context_config -{ - ma_log* pLog; - ma_thread_priority threadPriority; - size_t threadStackSize; - void* pUserData; - ma_allocation_callbacks allocationCallbacks; - struct - { - ma_handle hWnd; /* HWND. Optional window handle to pass into SetCooperativeLevel(). Will default to the foreground window, and if that fails, the desktop window. */ - } dsound; - struct - { - ma_bool32 useVerboseDeviceEnumeration; - } alsa; - struct - { - const char* pApplicationName; - const char* pServerName; - ma_bool32 tryAutoSpawn; /* Enables autospawning of the PulseAudio daemon if necessary. */ - } pulse; - struct - { - ma_ios_session_category sessionCategory; - ma_uint32 sessionCategoryOptions; - ma_bool32 noAudioSessionActivate; /* iOS only. When set to true, does not perform an explicit [[AVAudioSession sharedInstace] setActive:true] on initialization. */ - ma_bool32 noAudioSessionDeactivate; /* iOS only. When set to true, does not perform an explicit [[AVAudioSession sharedInstace] setActive:false] on uninitialization. */ - } coreaudio; - struct - { - const char* pClientName; - ma_bool32 tryStartServer; - } jack; - ma_backend_callbacks custom; -}; - -/* WASAPI specific structure for some commands which must run on a common thread due to bugs in WASAPI. */ -typedef struct -{ - int code; - ma_event* pEvent; /* This will be signalled when the event is complete. */ - union - { - struct - { - int _unused; - } quit; - struct - { - ma_device_type deviceType; - void* pAudioClient; - void** ppAudioClientService; - ma_result* pResult; /* The result from creating the audio client service. */ - } createAudioClient; - struct - { - ma_device* pDevice; - ma_device_type deviceType; - } releaseAudioClient; - } data; -} ma_context_command__wasapi; - -struct ma_context -{ - ma_backend_callbacks callbacks; - ma_backend backend; /* DirectSound, ALSA, etc. */ - ma_log* pLog; - ma_log log; /* Only used if the log is owned by the context. The pLog member will be set to &log in this case. */ - ma_thread_priority threadPriority; - size_t threadStackSize; - void* pUserData; - ma_allocation_callbacks allocationCallbacks; - ma_mutex deviceEnumLock; /* Used to make ma_context_get_devices() thread safe. */ - ma_mutex deviceInfoLock; /* Used to make ma_context_get_device_info() thread safe. */ - ma_uint32 deviceInfoCapacity; /* Total capacity of pDeviceInfos. */ - ma_uint32 playbackDeviceInfoCount; - ma_uint32 captureDeviceInfoCount; - ma_device_info* pDeviceInfos; /* Playback devices first, then capture. */ - - union - { -#ifdef MA_SUPPORT_WASAPI - struct - { - ma_thread commandThread; - ma_mutex commandLock; - ma_semaphore commandSem; - ma_uint32 commandIndex; - ma_uint32 commandCount; - ma_context_command__wasapi commands[4]; - ma_handle hAvrt; - ma_proc AvSetMmThreadCharacteristicsA; - ma_proc AvRevertMmThreadcharacteristics; - ma_handle hMMDevapi; - ma_proc ActivateAudioInterfaceAsync; - } wasapi; -#endif -#ifdef MA_SUPPORT_DSOUND - struct - { - ma_handle hWnd; /* Can be null. */ - ma_handle hDSoundDLL; - ma_proc DirectSoundCreate; - ma_proc DirectSoundEnumerateA; - ma_proc DirectSoundCaptureCreate; - ma_proc DirectSoundCaptureEnumerateA; - } dsound; -#endif -#ifdef MA_SUPPORT_WINMM - struct - { - ma_handle hWinMM; - ma_proc waveOutGetNumDevs; - ma_proc waveOutGetDevCapsA; - ma_proc waveOutOpen; - ma_proc waveOutClose; - ma_proc waveOutPrepareHeader; - ma_proc waveOutUnprepareHeader; - ma_proc waveOutWrite; - ma_proc waveOutReset; - ma_proc waveInGetNumDevs; - ma_proc waveInGetDevCapsA; - ma_proc waveInOpen; - ma_proc waveInClose; - ma_proc waveInPrepareHeader; - ma_proc waveInUnprepareHeader; - ma_proc waveInAddBuffer; - ma_proc waveInStart; - ma_proc waveInReset; - } winmm; -#endif -#ifdef MA_SUPPORT_ALSA - struct - { - ma_handle asoundSO; - ma_proc snd_pcm_open; - ma_proc snd_pcm_close; - ma_proc snd_pcm_hw_params_sizeof; - ma_proc snd_pcm_hw_params_any; - ma_proc snd_pcm_hw_params_set_format; - ma_proc snd_pcm_hw_params_set_format_first; - ma_proc snd_pcm_hw_params_get_format_mask; - ma_proc snd_pcm_hw_params_set_channels; - ma_proc snd_pcm_hw_params_set_channels_near; - ma_proc snd_pcm_hw_params_set_channels_minmax; - ma_proc snd_pcm_hw_params_set_rate_resample; - ma_proc snd_pcm_hw_params_set_rate; - ma_proc snd_pcm_hw_params_set_rate_near; - ma_proc snd_pcm_hw_params_set_buffer_size_near; - ma_proc snd_pcm_hw_params_set_periods_near; - ma_proc snd_pcm_hw_params_set_access; - ma_proc snd_pcm_hw_params_get_format; - ma_proc snd_pcm_hw_params_get_channels; - ma_proc snd_pcm_hw_params_get_channels_min; - ma_proc snd_pcm_hw_params_get_channels_max; - ma_proc snd_pcm_hw_params_get_rate; - ma_proc snd_pcm_hw_params_get_rate_min; - ma_proc snd_pcm_hw_params_get_rate_max; - ma_proc snd_pcm_hw_params_get_buffer_size; - ma_proc snd_pcm_hw_params_get_periods; - ma_proc snd_pcm_hw_params_get_access; - ma_proc snd_pcm_hw_params_test_format; - ma_proc snd_pcm_hw_params_test_channels; - ma_proc snd_pcm_hw_params_test_rate; - ma_proc snd_pcm_hw_params; - ma_proc snd_pcm_sw_params_sizeof; - ma_proc snd_pcm_sw_params_current; - ma_proc snd_pcm_sw_params_get_boundary; - ma_proc snd_pcm_sw_params_set_avail_min; - ma_proc snd_pcm_sw_params_set_start_threshold; - ma_proc snd_pcm_sw_params_set_stop_threshold; - ma_proc snd_pcm_sw_params; - ma_proc snd_pcm_format_mask_sizeof; - ma_proc snd_pcm_format_mask_test; - ma_proc snd_pcm_get_chmap; - ma_proc snd_pcm_state; - ma_proc snd_pcm_prepare; - ma_proc snd_pcm_start; - ma_proc snd_pcm_drop; - ma_proc snd_pcm_drain; - ma_proc snd_pcm_reset; - ma_proc snd_device_name_hint; - ma_proc snd_device_name_get_hint; - ma_proc snd_card_get_index; - ma_proc snd_device_name_free_hint; - ma_proc snd_pcm_mmap_begin; - ma_proc snd_pcm_mmap_commit; - ma_proc snd_pcm_recover; - ma_proc snd_pcm_readi; - ma_proc snd_pcm_writei; - ma_proc snd_pcm_avail; - ma_proc snd_pcm_avail_update; - ma_proc snd_pcm_wait; - ma_proc snd_pcm_nonblock; - ma_proc snd_pcm_info; - ma_proc snd_pcm_info_sizeof; - ma_proc snd_pcm_info_get_name; - ma_proc snd_pcm_poll_descriptors; - ma_proc snd_pcm_poll_descriptors_count; - ma_proc snd_pcm_poll_descriptors_revents; - ma_proc snd_config_update_free_global; - - ma_mutex internalDeviceEnumLock; - ma_bool32 useVerboseDeviceEnumeration; - } alsa; -#endif -#ifdef MA_SUPPORT_PULSEAUDIO - struct - { - ma_handle pulseSO; - ma_proc pa_mainloop_new; - ma_proc pa_mainloop_free; - ma_proc pa_mainloop_quit; - ma_proc pa_mainloop_get_api; - ma_proc pa_mainloop_iterate; - ma_proc pa_mainloop_wakeup; - ma_proc pa_threaded_mainloop_new; - ma_proc pa_threaded_mainloop_free; - ma_proc pa_threaded_mainloop_start; - ma_proc pa_threaded_mainloop_stop; - ma_proc pa_threaded_mainloop_lock; - ma_proc pa_threaded_mainloop_unlock; - ma_proc pa_threaded_mainloop_wait; - ma_proc pa_threaded_mainloop_signal; - ma_proc pa_threaded_mainloop_accept; - ma_proc pa_threaded_mainloop_get_retval; - ma_proc pa_threaded_mainloop_get_api; - ma_proc pa_threaded_mainloop_in_thread; - ma_proc pa_threaded_mainloop_set_name; - ma_proc pa_context_new; - ma_proc pa_context_unref; - ma_proc pa_context_connect; - ma_proc pa_context_disconnect; - ma_proc pa_context_set_state_callback; - ma_proc pa_context_get_state; - ma_proc pa_context_get_sink_info_list; - ma_proc pa_context_get_source_info_list; - ma_proc pa_context_get_sink_info_by_name; - ma_proc pa_context_get_source_info_by_name; - ma_proc pa_operation_unref; - ma_proc pa_operation_get_state; - ma_proc pa_channel_map_init_extend; - ma_proc pa_channel_map_valid; - ma_proc pa_channel_map_compatible; - ma_proc pa_stream_new; - ma_proc pa_stream_unref; - ma_proc pa_stream_connect_playback; - ma_proc pa_stream_connect_record; - ma_proc pa_stream_disconnect; - ma_proc pa_stream_get_state; - ma_proc pa_stream_get_sample_spec; - ma_proc pa_stream_get_channel_map; - ma_proc pa_stream_get_buffer_attr; - ma_proc pa_stream_set_buffer_attr; - ma_proc pa_stream_get_device_name; - ma_proc pa_stream_set_write_callback; - ma_proc pa_stream_set_read_callback; - ma_proc pa_stream_set_suspended_callback; - ma_proc pa_stream_set_moved_callback; - ma_proc pa_stream_is_suspended; - ma_proc pa_stream_flush; - ma_proc pa_stream_drain; - ma_proc pa_stream_is_corked; - ma_proc pa_stream_cork; - ma_proc pa_stream_trigger; - ma_proc pa_stream_begin_write; - ma_proc pa_stream_write; - ma_proc pa_stream_peek; - ma_proc pa_stream_drop; - ma_proc pa_stream_writable_size; - ma_proc pa_stream_readable_size; - - /*pa_mainloop**/ ma_ptr pMainLoop; - /*pa_context**/ ma_ptr pPulseContext; - char* pApplicationName; /* Set when the context is initialized. Used by devices for their local pa_context objects. */ - char* pServerName; /* Set when the context is initialized. Used by devices for their local pa_context objects. */ - } pulse; -#endif -#ifdef MA_SUPPORT_JACK - struct - { - ma_handle jackSO; - ma_proc jack_client_open; - ma_proc jack_client_close; - ma_proc jack_client_name_size; - ma_proc jack_set_process_callback; - ma_proc jack_set_buffer_size_callback; - ma_proc jack_on_shutdown; - ma_proc jack_get_sample_rate; - ma_proc jack_get_buffer_size; - ma_proc jack_get_ports; - ma_proc jack_activate; - ma_proc jack_deactivate; - ma_proc jack_connect; - ma_proc jack_port_register; - ma_proc jack_port_name; - ma_proc jack_port_get_buffer; - ma_proc jack_free; - - char* pClientName; - ma_bool32 tryStartServer; - } jack; -#endif -#ifdef MA_SUPPORT_COREAUDIO - struct - { - ma_handle hCoreFoundation; - ma_proc CFStringGetCString; - ma_proc CFRelease; - - ma_handle hCoreAudio; - ma_proc AudioObjectGetPropertyData; - ma_proc AudioObjectGetPropertyDataSize; - ma_proc AudioObjectSetPropertyData; - ma_proc AudioObjectAddPropertyListener; - ma_proc AudioObjectRemovePropertyListener; - - ma_handle hAudioUnit; /* Could possibly be set to AudioToolbox on later versions of macOS. */ - ma_proc AudioComponentFindNext; - ma_proc AudioComponentInstanceDispose; - ma_proc AudioComponentInstanceNew; - ma_proc AudioOutputUnitStart; - ma_proc AudioOutputUnitStop; - ma_proc AudioUnitAddPropertyListener; - ma_proc AudioUnitGetPropertyInfo; - ma_proc AudioUnitGetProperty; - ma_proc AudioUnitSetProperty; - ma_proc AudioUnitInitialize; - ma_proc AudioUnitRender; - - /*AudioComponent*/ ma_ptr component; - ma_bool32 noAudioSessionDeactivate; /* For tracking whether or not the iOS audio session should be explicitly deactivated. Set from the config in ma_context_init__coreaudio(). */ - } coreaudio; -#endif -#ifdef MA_SUPPORT_SNDIO - struct - { - ma_handle sndioSO; - ma_proc sio_open; - ma_proc sio_close; - ma_proc sio_setpar; - ma_proc sio_getpar; - ma_proc sio_getcap; - ma_proc sio_start; - ma_proc sio_stop; - ma_proc sio_read; - ma_proc sio_write; - ma_proc sio_onmove; - ma_proc sio_nfds; - ma_proc sio_pollfd; - ma_proc sio_revents; - ma_proc sio_eof; - ma_proc sio_setvol; - ma_proc sio_onvol; - ma_proc sio_initpar; - } sndio; -#endif -#ifdef MA_SUPPORT_AUDIO4 - struct - { - int _unused; - } audio4; -#endif -#ifdef MA_SUPPORT_OSS - struct - { - int versionMajor; - int versionMinor; - } oss; -#endif -#ifdef MA_SUPPORT_AAUDIO - struct - { - ma_handle hAAudio; /* libaaudio.so */ - ma_proc AAudio_createStreamBuilder; - ma_proc AAudioStreamBuilder_delete; - ma_proc AAudioStreamBuilder_setDeviceId; - ma_proc AAudioStreamBuilder_setDirection; - ma_proc AAudioStreamBuilder_setSharingMode; - ma_proc AAudioStreamBuilder_setFormat; - ma_proc AAudioStreamBuilder_setChannelCount; - ma_proc AAudioStreamBuilder_setSampleRate; - ma_proc AAudioStreamBuilder_setBufferCapacityInFrames; - ma_proc AAudioStreamBuilder_setFramesPerDataCallback; - ma_proc AAudioStreamBuilder_setDataCallback; - ma_proc AAudioStreamBuilder_setErrorCallback; - ma_proc AAudioStreamBuilder_setPerformanceMode; - ma_proc AAudioStreamBuilder_setUsage; - ma_proc AAudioStreamBuilder_setContentType; - ma_proc AAudioStreamBuilder_setInputPreset; - ma_proc AAudioStreamBuilder_setAllowedCapturePolicy; - ma_proc AAudioStreamBuilder_openStream; - ma_proc AAudioStream_close; - ma_proc AAudioStream_getState; - ma_proc AAudioStream_waitForStateChange; - ma_proc AAudioStream_getFormat; - ma_proc AAudioStream_getChannelCount; - ma_proc AAudioStream_getSampleRate; - ma_proc AAudioStream_getBufferCapacityInFrames; - ma_proc AAudioStream_getFramesPerDataCallback; - ma_proc AAudioStream_getFramesPerBurst; - ma_proc AAudioStream_requestStart; - ma_proc AAudioStream_requestStop; - ma_device_job_thread jobThread; /* For processing operations outside of the error callback, specifically device disconnections and rerouting. */ - } aaudio; -#endif -#ifdef MA_SUPPORT_OPENSL - struct - { - ma_handle libOpenSLES; - ma_handle SL_IID_ENGINE; - ma_handle SL_IID_AUDIOIODEVICECAPABILITIES; - ma_handle SL_IID_ANDROIDSIMPLEBUFFERQUEUE; - ma_handle SL_IID_RECORD; - ma_handle SL_IID_PLAY; - ma_handle SL_IID_OUTPUTMIX; - ma_handle SL_IID_ANDROIDCONFIGURATION; - ma_proc slCreateEngine; - } opensl; -#endif -#ifdef MA_SUPPORT_WEBAUDIO - struct - { - int _unused; - } webaudio; -#endif -#ifdef MA_SUPPORT_NULL - struct - { - int _unused; - } null_backend; -#endif - }; - - union - { -#if defined(MA_WIN32) - struct - { - /*HMODULE*/ ma_handle hOle32DLL; - ma_proc CoInitialize; - ma_proc CoInitializeEx; - ma_proc CoUninitialize; - ma_proc CoCreateInstance; - ma_proc CoTaskMemFree; - ma_proc PropVariantClear; - ma_proc StringFromGUID2; - - /*HMODULE*/ ma_handle hUser32DLL; - ma_proc GetForegroundWindow; - ma_proc GetDesktopWindow; - - /*HMODULE*/ ma_handle hAdvapi32DLL; - ma_proc RegOpenKeyExA; - ma_proc RegCloseKey; - ma_proc RegQueryValueExA; - - /*HRESULT*/ long CoInitializeResult; - } win32; -#endif -#ifdef MA_POSIX - struct - { - int _unused; - } posix; -#endif - int _unused; - }; -}; - -struct ma_device -{ - ma_context* pContext; - ma_device_type type; - ma_uint32 sampleRate; - ma_atomic_device_state state; /* The state of the device is variable and can change at any time on any thread. Must be used atomically. */ - ma_device_data_proc onData; /* Set once at initialization time and should not be changed after. */ - ma_device_notification_proc onNotification; /* Set once at initialization time and should not be changed after. */ - ma_stop_proc onStop; /* DEPRECATED. Use the notification callback instead. Set once at initialization time and should not be changed after. */ - void* pUserData; /* Application defined data. */ - ma_mutex startStopLock; - ma_event wakeupEvent; - ma_event startEvent; - ma_event stopEvent; - ma_thread thread; - ma_result workResult; /* This is set by the worker thread after it's finished doing a job. */ - ma_bool8 isOwnerOfContext; /* When set to true, uninitializing the device will also uninitialize the context. Set to true when NULL is passed into ma_device_init(). */ - ma_bool8 noPreSilencedOutputBuffer; - ma_bool8 noClip; - ma_bool8 noDisableDenormals; - ma_bool8 noFixedSizedCallback; - ma_atomic_float masterVolumeFactor; /* Linear 0..1. Can be read and written simultaneously by different threads. Must be used atomically. */ - ma_duplex_rb duplexRB; /* Intermediary buffer for duplex device on asynchronous backends. */ - struct - { - ma_resample_algorithm algorithm; - ma_resampling_backend_vtable* pBackendVTable; - void* pBackendUserData; - struct - { - ma_uint32 lpfOrder; - } linear; - } resampling; - struct - { - ma_device_id* pID; /* Set to NULL if using default ID, otherwise set to the address of "id". */ - ma_device_id id; /* If using an explicit device, will be set to a copy of the ID used for initialization. Otherwise cleared to 0. */ - char name[MA_MAX_DEVICE_NAME_LENGTH + 1]; /* Maybe temporary. Likely to be replaced with a query API. */ - ma_share_mode shareMode; /* Set to whatever was passed in when the device was initialized. */ - ma_format format; - ma_uint32 channels; - ma_channel channelMap[MA_MAX_CHANNELS]; - ma_format internalFormat; - ma_uint32 internalChannels; - ma_uint32 internalSampleRate; - ma_channel internalChannelMap[MA_MAX_CHANNELS]; - ma_uint32 internalPeriodSizeInFrames; - ma_uint32 internalPeriods; - ma_channel_mix_mode channelMixMode; - ma_bool32 calculateLFEFromSpatialChannels; - ma_data_converter converter; - void* pIntermediaryBuffer; /* For implementing fixed sized buffer callbacks. Will be null if using variable sized callbacks. */ - ma_uint32 intermediaryBufferCap; - ma_uint32 intermediaryBufferLen; /* How many valid frames are sitting in the intermediary buffer. */ - void* pInputCache; /* In external format. Can be null. */ - ma_uint64 inputCacheCap; - ma_uint64 inputCacheConsumed; - ma_uint64 inputCacheRemaining; - } playback; - struct - { - ma_device_id* pID; /* Set to NULL if using default ID, otherwise set to the address of "id". */ - ma_device_id id; /* If using an explicit device, will be set to a copy of the ID used for initialization. Otherwise cleared to 0. */ - char name[MA_MAX_DEVICE_NAME_LENGTH + 1]; /* Maybe temporary. Likely to be replaced with a query API. */ - ma_share_mode shareMode; /* Set to whatever was passed in when the device was initialized. */ - ma_format format; - ma_uint32 channels; - ma_channel channelMap[MA_MAX_CHANNELS]; - ma_format internalFormat; - ma_uint32 internalChannels; - ma_uint32 internalSampleRate; - ma_channel internalChannelMap[MA_MAX_CHANNELS]; - ma_uint32 internalPeriodSizeInFrames; - ma_uint32 internalPeriods; - ma_channel_mix_mode channelMixMode; - ma_bool32 calculateLFEFromSpatialChannels; - ma_data_converter converter; - void* pIntermediaryBuffer; /* For implementing fixed sized buffer callbacks. Will be null if using variable sized callbacks. */ - ma_uint32 intermediaryBufferCap; - ma_uint32 intermediaryBufferLen; /* How many valid frames are sitting in the intermediary buffer. */ - } capture; - - union - { -#ifdef MA_SUPPORT_WASAPI - struct - { - /*IAudioClient**/ ma_ptr pAudioClientPlayback; - /*IAudioClient**/ ma_ptr pAudioClientCapture; - /*IAudioRenderClient**/ ma_ptr pRenderClient; - /*IAudioCaptureClient**/ ma_ptr pCaptureClient; - /*IMMDeviceEnumerator**/ ma_ptr pDeviceEnumerator; /* Used for IMMNotificationClient notifications. Required for detecting default device changes. */ - ma_IMMNotificationClient notificationClient; - /*HANDLE*/ ma_handle hEventPlayback; /* Auto reset. Initialized to signaled. */ - /*HANDLE*/ ma_handle hEventCapture; /* Auto reset. Initialized to unsignaled. */ - ma_uint32 actualBufferSizeInFramesPlayback; /* Value from GetBufferSize(). internalPeriodSizeInFrames is not set to the _actual_ buffer size when low-latency shared mode is being used due to the way the IAudioClient3 API works. */ - ma_uint32 actualBufferSizeInFramesCapture; - ma_uint32 originalPeriodSizeInFrames; - ma_uint32 originalPeriodSizeInMilliseconds; - ma_uint32 originalPeriods; - ma_performance_profile originalPerformanceProfile; - ma_uint32 periodSizeInFramesPlayback; - ma_uint32 periodSizeInFramesCapture; - void* pMappedBufferCapture; - ma_uint32 mappedBufferCaptureCap; - ma_uint32 mappedBufferCaptureLen; - void* pMappedBufferPlayback; - ma_uint32 mappedBufferPlaybackCap; - ma_uint32 mappedBufferPlaybackLen; - ma_atomic_bool32 isStartedCapture; /* Can be read and written simultaneously across different threads. Must be used atomically, and must be 32-bit. */ - ma_atomic_bool32 isStartedPlayback; /* Can be read and written simultaneously across different threads. Must be used atomically, and must be 32-bit. */ - ma_uint32 loopbackProcessID; - ma_bool8 loopbackProcessExclude; - ma_bool8 noAutoConvertSRC; /* When set to true, disables the use of AUDCLNT_STREAMFLAGS_AUTOCONVERTPCM. */ - ma_bool8 noDefaultQualitySRC; /* When set to true, disables the use of AUDCLNT_STREAMFLAGS_SRC_DEFAULT_QUALITY. */ - ma_bool8 noHardwareOffloading; - ma_bool8 allowCaptureAutoStreamRouting; - ma_bool8 allowPlaybackAutoStreamRouting; - ma_bool8 isDetachedPlayback; - ma_bool8 isDetachedCapture; - ma_wasapi_usage usage; - void* hAvrtHandle; - ma_mutex rerouteLock; - } wasapi; -#endif -#ifdef MA_SUPPORT_DSOUND - struct - { - /*LPDIRECTSOUND*/ ma_ptr pPlayback; - /*LPDIRECTSOUNDBUFFER*/ ma_ptr pPlaybackPrimaryBuffer; - /*LPDIRECTSOUNDBUFFER*/ ma_ptr pPlaybackBuffer; - /*LPDIRECTSOUNDCAPTURE*/ ma_ptr pCapture; - /*LPDIRECTSOUNDCAPTUREBUFFER*/ ma_ptr pCaptureBuffer; - } dsound; -#endif -#ifdef MA_SUPPORT_WINMM - struct - { - /*HWAVEOUT*/ ma_handle hDevicePlayback; - /*HWAVEIN*/ ma_handle hDeviceCapture; - /*HANDLE*/ ma_handle hEventPlayback; - /*HANDLE*/ ma_handle hEventCapture; - ma_uint32 fragmentSizeInFrames; - ma_uint32 iNextHeaderPlayback; /* [0,periods). Used as an index into pWAVEHDRPlayback. */ - ma_uint32 iNextHeaderCapture; /* [0,periods). Used as an index into pWAVEHDRCapture. */ - ma_uint32 headerFramesConsumedPlayback; /* The number of PCM frames consumed in the buffer in pWAVEHEADER[iNextHeader]. */ - ma_uint32 headerFramesConsumedCapture; /* ^^^ */ - /*WAVEHDR**/ ma_uint8* pWAVEHDRPlayback; /* One instantiation for each period. */ - /*WAVEHDR**/ ma_uint8* pWAVEHDRCapture; /* One instantiation for each period. */ - ma_uint8* pIntermediaryBufferPlayback; - ma_uint8* pIntermediaryBufferCapture; - ma_uint8* _pHeapData; /* Used internally and is used for the heap allocated data for the intermediary buffer and the WAVEHDR structures. */ - } winmm; -#endif -#ifdef MA_SUPPORT_ALSA - struct - { - /*snd_pcm_t**/ ma_ptr pPCMPlayback; - /*snd_pcm_t**/ ma_ptr pPCMCapture; - /*struct pollfd**/ void* pPollDescriptorsPlayback; - /*struct pollfd**/ void* pPollDescriptorsCapture; - int pollDescriptorCountPlayback; - int pollDescriptorCountCapture; - int wakeupfdPlayback; /* eventfd for waking up from poll() when the playback device is stopped. */ - int wakeupfdCapture; /* eventfd for waking up from poll() when the capture device is stopped. */ - ma_bool8 isUsingMMapPlayback; - ma_bool8 isUsingMMapCapture; - } alsa; -#endif -#ifdef MA_SUPPORT_PULSEAUDIO - struct - { - /*pa_mainloop**/ ma_ptr pMainLoop; - /*pa_context**/ ma_ptr pPulseContext; - /*pa_stream**/ ma_ptr pStreamPlayback; - /*pa_stream**/ ma_ptr pStreamCapture; - } pulse; -#endif -#ifdef MA_SUPPORT_JACK - struct - { - /*jack_client_t**/ ma_ptr pClient; - /*jack_port_t**/ ma_ptr* ppPortsPlayback; - /*jack_port_t**/ ma_ptr* ppPortsCapture; - float* pIntermediaryBufferPlayback; /* Typed as a float because JACK is always floating point. */ - float* pIntermediaryBufferCapture; - } jack; -#endif -#ifdef MA_SUPPORT_COREAUDIO - struct - { - ma_uint32 deviceObjectIDPlayback; - ma_uint32 deviceObjectIDCapture; - /*AudioUnit*/ ma_ptr audioUnitPlayback; - /*AudioUnit*/ ma_ptr audioUnitCapture; - /*AudioBufferList**/ ma_ptr pAudioBufferList; /* Only used for input devices. */ - ma_uint32 audioBufferCapInFrames; /* Only used for input devices. The capacity in frames of each buffer in pAudioBufferList. */ - ma_event stopEvent; - ma_uint32 originalPeriodSizeInFrames; - ma_uint32 originalPeriodSizeInMilliseconds; - ma_uint32 originalPeriods; - ma_performance_profile originalPerformanceProfile; - ma_bool32 isDefaultPlaybackDevice; - ma_bool32 isDefaultCaptureDevice; - ma_bool32 isSwitchingPlaybackDevice; /* <-- Set to true when the default device has changed and miniaudio is in the process of switching. */ - ma_bool32 isSwitchingCaptureDevice; /* <-- Set to true when the default device has changed and miniaudio is in the process of switching. */ - void* pNotificationHandler; /* Only used on mobile platforms. Obj-C object for handling route changes. */ - } coreaudio; -#endif -#ifdef MA_SUPPORT_SNDIO - struct - { - ma_ptr handlePlayback; - ma_ptr handleCapture; - ma_bool32 isStartedPlayback; - ma_bool32 isStartedCapture; - } sndio; -#endif -#ifdef MA_SUPPORT_AUDIO4 - struct - { - int fdPlayback; - int fdCapture; - } audio4; -#endif -#ifdef MA_SUPPORT_OSS - struct - { - int fdPlayback; - int fdCapture; - } oss; -#endif -#ifdef MA_SUPPORT_AAUDIO - struct - { - /*AAudioStream**/ ma_ptr pStreamPlayback; - /*AAudioStream**/ ma_ptr pStreamCapture; - ma_mutex rerouteLock; - ma_aaudio_usage usage; - ma_aaudio_content_type contentType; - ma_aaudio_input_preset inputPreset; - ma_aaudio_allowed_capture_policy allowedCapturePolicy; - ma_bool32 noAutoStartAfterReroute; - } aaudio; -#endif -#ifdef MA_SUPPORT_OPENSL - struct - { - /*SLObjectItf*/ ma_ptr pOutputMixObj; - /*SLOutputMixItf*/ ma_ptr pOutputMix; - /*SLObjectItf*/ ma_ptr pAudioPlayerObj; - /*SLPlayItf*/ ma_ptr pAudioPlayer; - /*SLObjectItf*/ ma_ptr pAudioRecorderObj; - /*SLRecordItf*/ ma_ptr pAudioRecorder; - /*SLAndroidSimpleBufferQueueItf*/ ma_ptr pBufferQueuePlayback; - /*SLAndroidSimpleBufferQueueItf*/ ma_ptr pBufferQueueCapture; - ma_bool32 isDrainingCapture; - ma_bool32 isDrainingPlayback; - ma_uint32 currentBufferIndexPlayback; - ma_uint32 currentBufferIndexCapture; - ma_uint8* pBufferPlayback; /* This is malloc()'d and is used for storing audio data. Typed as ma_uint8 for easy offsetting. */ - ma_uint8* pBufferCapture; - } opensl; -#endif -#ifdef MA_SUPPORT_WEBAUDIO - struct - { - /* AudioWorklets path. */ - /* EMSCRIPTEN_WEBAUDIO_T */ int audioContext; - /* EMSCRIPTEN_WEBAUDIO_T */ int audioWorklet; - float* pIntermediaryBuffer; - void* pStackBuffer; - ma_result initResult; /* Set to MA_BUSY while initialization is in progress. */ - int deviceIndex; /* We store the device in a list on the JavaScript side. This is used to map our C object to the JS object. */ - } webaudio; -#endif -#ifdef MA_SUPPORT_NULL - struct - { - ma_thread deviceThread; - ma_event operationEvent; - ma_event operationCompletionEvent; - ma_semaphore operationSemaphore; - ma_uint32 operation; - ma_result operationResult; - ma_timer timer; - double priorRunTime; - ma_uint32 currentPeriodFramesRemainingPlayback; - ma_uint32 currentPeriodFramesRemainingCapture; - ma_uint64 lastProcessedFramePlayback; - ma_uint64 lastProcessedFrameCapture; - ma_atomic_bool32 isStarted; /* Read and written by multiple threads. Must be used atomically, and must be 32-bit for compiler compatibility. */ - } null_device; -#endif - }; -}; -#if defined(_MSC_VER) && !defined(__clang__) - #pragma warning(pop) -#elif defined(__clang__) || (defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 8))) - #pragma GCC diagnostic pop /* For ISO C99 doesn't support unnamed structs/unions [-Wpedantic] */ -#endif - -/* -Initializes a `ma_context_config` object. - - -Return Value ------------- -A `ma_context_config` initialized to defaults. - - -Remarks -------- -You must always use this to initialize the default state of the `ma_context_config` object. Not using this will result in your program breaking when miniaudio -is updated and new members are added to `ma_context_config`. It also sets logical defaults. - -You can override members of the returned object by changing it's members directly. - - -See Also --------- -ma_context_init() -*/ -MA_API ma_context_config ma_context_config_init(void); - -/* -Initializes a context. - -The context is used for selecting and initializing an appropriate backend and to represent the backend at a more global level than that of an individual -device. There is one context to many devices, and a device is created from a context. A context is required to enumerate devices. - - -Parameters ----------- -backends (in, optional) - A list of backends to try initializing, in priority order. Can be NULL, in which case it uses default priority order. - -backendCount (in, optional) - The number of items in `backend`. Ignored if `backend` is NULL. - -pConfig (in, optional) - The context configuration. - -pContext (in) - A pointer to the context object being initialized. - - -Return Value ------------- -MA_SUCCESS if successful; any other error code otherwise. - - -Thread Safety -------------- -Unsafe. Do not call this function across multiple threads as some backends read and write to global state. - - -Remarks -------- -When `backends` is NULL, the default priority order will be used. Below is a list of backends in priority order: - - |-------------|-----------------------|--------------------------------------------------------| - | Name | Enum Name | Supported Operating Systems | - |-------------|-----------------------|--------------------------------------------------------| - | WASAPI | ma_backend_wasapi | Windows Vista+ | - | DirectSound | ma_backend_dsound | Windows XP+ | - | WinMM | ma_backend_winmm | Windows XP+ (may work on older versions, but untested) | - | Core Audio | ma_backend_coreaudio | macOS, iOS | - | ALSA | ma_backend_alsa | Linux | - | PulseAudio | ma_backend_pulseaudio | Cross Platform (disabled on Windows, BSD and Android) | - | JACK | ma_backend_jack | Cross Platform (disabled on BSD and Android) | - | sndio | ma_backend_sndio | OpenBSD | - | audio(4) | ma_backend_audio4 | NetBSD, OpenBSD | - | OSS | ma_backend_oss | FreeBSD | - | AAudio | ma_backend_aaudio | Android 8+ | - | OpenSL|ES | ma_backend_opensl | Android (API level 16+) | - | Web Audio | ma_backend_webaudio | Web (via Emscripten) | - | Null | ma_backend_null | Cross Platform (not used on Web) | - |-------------|-----------------------|--------------------------------------------------------| - -The context can be configured via the `pConfig` argument. The config object is initialized with `ma_context_config_init()`. Individual configuration settings -can then be set directly on the structure. Below are the members of the `ma_context_config` object. - - pLog - A pointer to the `ma_log` to post log messages to. Can be NULL if the application does not - require logging. See the `ma_log` API for details on how to use the logging system. - - threadPriority - The desired priority to use for the audio thread. Allowable values include the following: - - |--------------------------------------| - | Thread Priority | - |--------------------------------------| - | ma_thread_priority_idle | - | ma_thread_priority_lowest | - | ma_thread_priority_low | - | ma_thread_priority_normal | - | ma_thread_priority_high | - | ma_thread_priority_highest (default) | - | ma_thread_priority_realtime | - | ma_thread_priority_default | - |--------------------------------------| - - threadStackSize - The desired size of the stack for the audio thread. Defaults to the operating system's default. - - pUserData - A pointer to application-defined data. This can be accessed from the context object directly such as `context.pUserData`. - - allocationCallbacks - Structure containing custom allocation callbacks. Leaving this at defaults will cause it to use MA_MALLOC, MA_REALLOC and MA_FREE. These allocation - callbacks will be used for anything tied to the context, including devices. - - alsa.useVerboseDeviceEnumeration - ALSA will typically enumerate many different devices which can be intrusive and not user-friendly. To combat this, miniaudio will enumerate only unique - card/device pairs by default. The problem with this is that you lose a bit of flexibility and control. Setting alsa.useVerboseDeviceEnumeration makes - it so the ALSA backend includes all devices. Defaults to false. - - pulse.pApplicationName - PulseAudio only. The application name to use when initializing the PulseAudio context with `pa_context_new()`. - - pulse.pServerName - PulseAudio only. The name of the server to connect to with `pa_context_connect()`. - - pulse.tryAutoSpawn - PulseAudio only. Whether or not to try automatically starting the PulseAudio daemon. Defaults to false. If you set this to true, keep in mind that - miniaudio uses a trial and error method to find the most appropriate backend, and this will result in the PulseAudio daemon starting which may be - intrusive for the end user. - - coreaudio.sessionCategory - iOS only. The session category to use for the shared AudioSession instance. Below is a list of allowable values and their Core Audio equivalents. - - |-----------------------------------------|-------------------------------------| - | miniaudio Token | Core Audio Token | - |-----------------------------------------|-------------------------------------| - | ma_ios_session_category_ambient | AVAudioSessionCategoryAmbient | - | ma_ios_session_category_solo_ambient | AVAudioSessionCategorySoloAmbient | - | ma_ios_session_category_playback | AVAudioSessionCategoryPlayback | - | ma_ios_session_category_record | AVAudioSessionCategoryRecord | - | ma_ios_session_category_play_and_record | AVAudioSessionCategoryPlayAndRecord | - | ma_ios_session_category_multi_route | AVAudioSessionCategoryMultiRoute | - | ma_ios_session_category_none | AVAudioSessionCategoryAmbient | - | ma_ios_session_category_default | AVAudioSessionCategoryAmbient | - |-----------------------------------------|-------------------------------------| - - coreaudio.sessionCategoryOptions - iOS only. Session category options to use with the shared AudioSession instance. Below is a list of allowable values and their Core Audio equivalents. - - |---------------------------------------------------------------------------|------------------------------------------------------------------| - | miniaudio Token | Core Audio Token | - |---------------------------------------------------------------------------|------------------------------------------------------------------| - | ma_ios_session_category_option_mix_with_others | AVAudioSessionCategoryOptionMixWithOthers | - | ma_ios_session_category_option_duck_others | AVAudioSessionCategoryOptionDuckOthers | - | ma_ios_session_category_option_allow_bluetooth | AVAudioSessionCategoryOptionAllowBluetooth | - | ma_ios_session_category_option_default_to_speaker | AVAudioSessionCategoryOptionDefaultToSpeaker | - | ma_ios_session_category_option_interrupt_spoken_audio_and_mix_with_others | AVAudioSessionCategoryOptionInterruptSpokenAudioAndMixWithOthers | - | ma_ios_session_category_option_allow_bluetooth_a2dp | AVAudioSessionCategoryOptionAllowBluetoothA2DP | - | ma_ios_session_category_option_allow_air_play | AVAudioSessionCategoryOptionAllowAirPlay | - |---------------------------------------------------------------------------|------------------------------------------------------------------| - - coreaudio.noAudioSessionActivate - iOS only. When set to true, does not perform an explicit [[AVAudioSession sharedInstace] setActive:true] on initialization. - - coreaudio.noAudioSessionDeactivate - iOS only. When set to true, does not perform an explicit [[AVAudioSession sharedInstace] setActive:false] on uninitialization. - - jack.pClientName - The name of the client to pass to `jack_client_open()`. - - jack.tryStartServer - Whether or not to try auto-starting the JACK server. Defaults to false. - - -It is recommended that only a single context is active at any given time because it's a bulky data structure which performs run-time linking for the -relevant backends every time it's initialized. - -The location of the context cannot change throughout it's lifetime. Consider allocating the `ma_context` object with `malloc()` if this is an issue. The -reason for this is that a pointer to the context is stored in the `ma_device` structure. - - -Example 1 - Default Initialization ----------------------------------- -The example below shows how to initialize the context using the default configuration. - -```c -ma_context context; -ma_result result = ma_context_init(NULL, 0, NULL, &context); -if (result != MA_SUCCESS) { - // Error. -} -``` - - -Example 2 - Custom Configuration --------------------------------- -The example below shows how to initialize the context using custom backend priorities and a custom configuration. In this hypothetical example, the program -wants to prioritize ALSA over PulseAudio on Linux. They also want to avoid using the WinMM backend on Windows because it's latency is too high. They also -want an error to be returned if no valid backend is available which they achieve by excluding the Null backend. - -For the configuration, the program wants to capture any log messages so they can, for example, route it to a log file and user interface. - -```c -ma_backend backends[] = { - ma_backend_alsa, - ma_backend_pulseaudio, - ma_backend_wasapi, - ma_backend_dsound -}; - -ma_log log; -ma_log_init(&log); -ma_log_register_callback(&log, ma_log_callback_init(my_log_callbac, pMyLogUserData)); - -ma_context_config config = ma_context_config_init(); -config.pLog = &log; // Specify a custom log object in the config so any logs that are posted from ma_context_init() are captured. - -ma_context context; -ma_result result = ma_context_init(backends, sizeof(backends)/sizeof(backends[0]), &config, &context); -if (result != MA_SUCCESS) { - // Error. - if (result == MA_NO_BACKEND) { - // Couldn't find an appropriate backend. - } -} - -// You could also attach a log callback post-initialization: -ma_log_register_callback(ma_context_get_log(&context), ma_log_callback_init(my_log_callback, pMyLogUserData)); -``` - - -See Also --------- -ma_context_config_init() -ma_context_uninit() -*/ -MA_API ma_result ma_context_init(const ma_backend backends[], ma_uint32 backendCount, const ma_context_config* pConfig, ma_context* pContext); - -/* -Uninitializes a context. - - -Return Value ------------- -MA_SUCCESS if successful; any other error code otherwise. - - -Thread Safety -------------- -Unsafe. Do not call this function across multiple threads as some backends read and write to global state. - - -Remarks -------- -Results are undefined if you call this while any device created by this context is still active. - - -See Also --------- -ma_context_init() -*/ -MA_API ma_result ma_context_uninit(ma_context* pContext); - -/* -Retrieves the size of the ma_context object. - -This is mainly for the purpose of bindings to know how much memory to allocate. -*/ -MA_API size_t ma_context_sizeof(void); - -/* -Retrieves a pointer to the log object associated with this context. - - -Remarks -------- -Pass the returned pointer to `ma_log_post()`, `ma_log_postv()` or `ma_log_postf()` to post a log -message. - -You can attach your own logging callback to the log with `ma_log_register_callback()` - - -Return Value ------------- -A pointer to the `ma_log` object that the context uses to post log messages. If some error occurs, -NULL will be returned. -*/ -MA_API ma_log* ma_context_get_log(ma_context* pContext); - -/* -Enumerates over every device (both playback and capture). - -This is a lower-level enumeration function to the easier to use `ma_context_get_devices()`. Use `ma_context_enumerate_devices()` if you would rather not incur -an internal heap allocation, or it simply suits your code better. - -Note that this only retrieves the ID and name/description of the device. The reason for only retrieving basic information is that it would otherwise require -opening the backend device in order to probe it for more detailed information which can be inefficient. Consider using `ma_context_get_device_info()` for this, -but don't call it from within the enumeration callback. - -Returning false from the callback will stop enumeration. Returning true will continue enumeration. - - -Parameters ----------- -pContext (in) - A pointer to the context performing the enumeration. - -callback (in) - The callback to fire for each enumerated device. - -pUserData (in) - A pointer to application-defined data passed to the callback. - - -Return Value ------------- -MA_SUCCESS if successful; any other error code otherwise. - - -Thread Safety -------------- -Safe. This is guarded using a simple mutex lock. - - -Remarks -------- -Do _not_ assume the first enumerated device of a given type is the default device. - -Some backends and platforms may only support default playback and capture devices. - -In general, you should not do anything complicated from within the callback. In particular, do not try initializing a device from within the callback. Also, -do not try to call `ma_context_get_device_info()` from within the callback. - -Consider using `ma_context_get_devices()` for a simpler and safer API, albeit at the expense of an internal heap allocation. - - -Example 1 - Simple Enumeration ------------------------------- -ma_bool32 ma_device_enum_callback(ma_context* pContext, ma_device_type deviceType, const ma_device_info* pInfo, void* pUserData) -{ - printf("Device Name: %s\n", pInfo->name); - return MA_TRUE; -} - -ma_result result = ma_context_enumerate_devices(&context, my_device_enum_callback, pMyUserData); -if (result != MA_SUCCESS) { - // Error. -} - - -See Also --------- -ma_context_get_devices() -*/ -MA_API ma_result ma_context_enumerate_devices(ma_context* pContext, ma_enum_devices_callback_proc callback, void* pUserData); - -/* -Retrieves basic information about every active playback and/or capture device. - -This function will allocate memory internally for the device lists and return a pointer to them through the `ppPlaybackDeviceInfos` and `ppCaptureDeviceInfos` -parameters. If you do not want to incur the overhead of these allocations consider using `ma_context_enumerate_devices()` which will instead use a callback. - -Note that this only retrieves the ID and name/description of the device. The reason for only retrieving basic information is that it would otherwise require -opening the backend device in order to probe it for more detailed information which can be inefficient. Consider using `ma_context_get_device_info()` for this, -but don't call it from within the enumeration callback. - - -Parameters ----------- -pContext (in) - A pointer to the context performing the enumeration. - -ppPlaybackDeviceInfos (out) - A pointer to a pointer that will receive the address of a buffer containing the list of `ma_device_info` structures for playback devices. - -pPlaybackDeviceCount (out) - A pointer to an unsigned integer that will receive the number of playback devices. - -ppCaptureDeviceInfos (out) - A pointer to a pointer that will receive the address of a buffer containing the list of `ma_device_info` structures for capture devices. - -pCaptureDeviceCount (out) - A pointer to an unsigned integer that will receive the number of capture devices. - - -Return Value ------------- -MA_SUCCESS if successful; any other error code otherwise. - - -Thread Safety -------------- -Unsafe. Since each call to this function invalidates the pointers from the previous call, you should not be calling this simultaneously across multiple -threads. Instead, you need to make a copy of the returned data with your own higher level synchronization. - - -Remarks -------- -It is _not_ safe to assume the first device in the list is the default device. - -You can pass in NULL for the playback or capture lists in which case they'll be ignored. - -The returned pointers will become invalid upon the next call this this function, or when the context is uninitialized. Do not free the returned pointers. - - -See Also --------- -ma_context_enumerate_devices() -*/ -MA_API ma_result ma_context_get_devices(ma_context* pContext, ma_device_info** ppPlaybackDeviceInfos, ma_uint32* pPlaybackDeviceCount, ma_device_info** ppCaptureDeviceInfos, ma_uint32* pCaptureDeviceCount); - -/* -Retrieves information about a device of the given type, with the specified ID and share mode. - - -Parameters ----------- -pContext (in) - A pointer to the context performing the query. - -deviceType (in) - The type of the device being queried. Must be either `ma_device_type_playback` or `ma_device_type_capture`. - -pDeviceID (in) - The ID of the device being queried. - -pDeviceInfo (out) - A pointer to the `ma_device_info` structure that will receive the device information. - - -Return Value ------------- -MA_SUCCESS if successful; any other error code otherwise. - - -Thread Safety -------------- -Safe. This is guarded using a simple mutex lock. - - -Remarks -------- -Do _not_ call this from within the `ma_context_enumerate_devices()` callback. - -It's possible for a device to have different information and capabilities depending on whether or not it's opened in shared or exclusive mode. For example, in -shared mode, WASAPI always uses floating point samples for mixing, but in exclusive mode it can be anything. Therefore, this function allows you to specify -which share mode you want information for. Note that not all backends and devices support shared or exclusive mode, in which case this function will fail if -the requested share mode is unsupported. - -This leaves pDeviceInfo unmodified in the result of an error. -*/ -MA_API ma_result ma_context_get_device_info(ma_context* pContext, ma_device_type deviceType, const ma_device_id* pDeviceID, ma_device_info* pDeviceInfo); - -/* -Determines if the given context supports loopback mode. - - -Parameters ----------- -pContext (in) - A pointer to the context getting queried. - - -Return Value ------------- -MA_TRUE if the context supports loopback mode; MA_FALSE otherwise. -*/ -MA_API ma_bool32 ma_context_is_loopback_supported(ma_context* pContext); - - - -/* -Initializes a device config with default settings. - - -Parameters ----------- -deviceType (in) - The type of the device this config is being initialized for. This must set to one of the following: - - |-------------------------| - | Device Type | - |-------------------------| - | ma_device_type_playback | - | ma_device_type_capture | - | ma_device_type_duplex | - | ma_device_type_loopback | - |-------------------------| - - -Return Value ------------- -A new device config object with default settings. You will typically want to adjust the config after this function returns. See remarks. - - -Thread Safety -------------- -Safe. - - -Callback Safety ---------------- -Safe, but don't try initializing a device in a callback. - - -Remarks -------- -The returned config will be initialized to defaults. You will normally want to customize a few variables before initializing the device. See Example 1 for a -typical configuration which sets the sample format, channel count, sample rate, data callback and user data. These are usually things you will want to change -before initializing the device. - -See `ma_device_init()` for details on specific configuration options. - - -Example 1 - Simple Configuration --------------------------------- -The example below is what a program will typically want to configure for each device at a minimum. Notice how `ma_device_config_init()` is called first, and -then the returned object is modified directly. This is important because it ensures that your program continues to work as new configuration options are added -to the `ma_device_config` structure. - -```c -ma_device_config config = ma_device_config_init(ma_device_type_playback); -config.playback.format = ma_format_f32; -config.playback.channels = 2; -config.sampleRate = 48000; -config.dataCallback = ma_data_callback; -config.pUserData = pMyUserData; -``` - - -See Also --------- -ma_device_init() -ma_device_init_ex() -*/ -MA_API ma_device_config ma_device_config_init(ma_device_type deviceType); - - -/* -Initializes a device. - -A device represents a physical audio device. The idea is you send or receive audio data from the device to either play it back through a speaker, or capture it -from a microphone. Whether or not you should send or receive data from the device (or both) depends on the type of device you are initializing which can be -playback, capture, full-duplex or loopback. (Note that loopback mode is only supported on select backends.) Sending and receiving audio data to and from the -device is done via a callback which is fired by miniaudio at periodic time intervals. - -The frequency at which data is delivered to and from a device depends on the size of its period. The size of the period can be defined in terms of PCM frames -or milliseconds, whichever is more convenient. Generally speaking, the smaller the period, the lower the latency at the expense of higher CPU usage and -increased risk of glitching due to the more frequent and granular data deliver intervals. The size of a period will depend on your requirements, but -miniaudio's defaults should work fine for most scenarios. If you're building a game you should leave this fairly small, whereas if you're building a simple -media player you can make it larger. Note that the period size you request is actually just a hint - miniaudio will tell the backend what you want, but the -backend is ultimately responsible for what it gives you. You cannot assume you will get exactly what you ask for. - -When delivering data to and from a device you need to make sure it's in the correct format which you can set through the device configuration. You just set the -format that you want to use and miniaudio will perform all of the necessary conversion for you internally. When delivering data to and from the callback you -can assume the format is the same as what you requested when you initialized the device. See Remarks for more details on miniaudio's data conversion pipeline. - - -Parameters ----------- -pContext (in, optional) - A pointer to the context that owns the device. This can be null, in which case it creates a default context internally. - -pConfig (in) - A pointer to the device configuration. Cannot be null. See remarks for details. - -pDevice (out) - A pointer to the device object being initialized. - - -Return Value ------------- -MA_SUCCESS if successful; any other error code otherwise. - - -Thread Safety -------------- -Unsafe. It is not safe to call this function simultaneously for different devices because some backends depend on and mutate global state. The same applies to -calling this at the same time as `ma_device_uninit()`. - - -Callback Safety ---------------- -Unsafe. It is not safe to call this inside any callback. - - -Remarks -------- -Setting `pContext` to NULL will result in miniaudio creating a default context internally and is equivalent to passing in a context initialized like so: - - ```c - ma_context_init(NULL, 0, NULL, &context); - ``` - -Do not set `pContext` to NULL if you are needing to open multiple devices. You can, however, use NULL when initializing the first device, and then use -device.pContext for the initialization of other devices. - -The device can be configured via the `pConfig` argument. The config object is initialized with `ma_device_config_init()`. Individual configuration settings can -then be set directly on the structure. Below are the members of the `ma_device_config` object. - - deviceType - Must be `ma_device_type_playback`, `ma_device_type_capture`, `ma_device_type_duplex` of `ma_device_type_loopback`. - - sampleRate - The sample rate, in hertz. The most common sample rates are 48000 and 44100. Setting this to 0 will use the device's native sample rate. - - periodSizeInFrames - The desired size of a period in PCM frames. If this is 0, `periodSizeInMilliseconds` will be used instead. If both are 0 the default buffer size will - be used depending on the selected performance profile. This value affects latency. See below for details. - - periodSizeInMilliseconds - The desired size of a period in milliseconds. If this is 0, `periodSizeInFrames` will be used instead. If both are 0 the default buffer size will be - used depending on the selected performance profile. The value affects latency. See below for details. - - periods - The number of periods making up the device's entire buffer. The total buffer size is `periodSizeInFrames` or `periodSizeInMilliseconds` multiplied by - this value. This is just a hint as backends will be the ones who ultimately decide how your periods will be configured. - - performanceProfile - A hint to miniaudio as to the performance requirements of your program. Can be either `ma_performance_profile_low_latency` (default) or - `ma_performance_profile_conservative`. This mainly affects the size of default buffers and can usually be left at its default value. - - noPreSilencedOutputBuffer - When set to true, the contents of the output buffer passed into the data callback will be left undefined. When set to false (default), the contents of - the output buffer will be cleared the zero. You can use this to avoid the overhead of zeroing out the buffer if you can guarantee that your data - callback will write to every sample in the output buffer, or if you are doing your own clearing. - - noClip - When set to true, the contents of the output buffer are left alone after returning and it will be left up to the backend itself to decide whether or - not to clip. When set to false (default), the contents of the output buffer passed into the data callback will be clipped after returning. This only - applies when the playback sample format is f32. - - noDisableDenormals - By default, miniaudio will disable denormals when the data callback is called. Setting this to true will prevent the disabling of denormals. - - noFixedSizedCallback - Allows miniaudio to fire the data callback with any frame count. When this is set to false (the default), the data callback will be fired with a - consistent frame count as specified by `periodSizeInFrames` or `periodSizeInMilliseconds`. When set to true, miniaudio will fire the callback with - whatever the backend requests, which could be anything. - - dataCallback - The callback to fire whenever data is ready to be delivered to or from the device. - - notificationCallback - The callback to fire when something has changed with the device, such as whether or not it has been started or stopped. - - pUserData - The user data pointer to use with the device. You can access this directly from the device object like `device.pUserData`. - - resampling.algorithm - The resampling algorithm to use when miniaudio needs to perform resampling between the rate specified by `sampleRate` and the device's native rate. The - default value is `ma_resample_algorithm_linear`, and the quality can be configured with `resampling.linear.lpfOrder`. - - resampling.pBackendVTable - A pointer to an optional vtable that can be used for plugging in a custom resampler. - - resampling.pBackendUserData - A pointer that will passed to callbacks in pBackendVTable. - - resampling.linear.lpfOrder - The linear resampler applies a low-pass filter as part of its processing for anti-aliasing. This setting controls the order of the filter. The higher - the value, the better the quality, in general. Setting this to 0 will disable low-pass filtering altogether. The maximum value is - `MA_MAX_FILTER_ORDER`. The default value is `min(4, MA_MAX_FILTER_ORDER)`. - - playback.pDeviceID - A pointer to a `ma_device_id` structure containing the ID of the playback device to initialize. Setting this NULL (default) will use the system's - default playback device. Retrieve the device ID from the `ma_device_info` structure, which can be retrieved using device enumeration. - - playback.format - The sample format to use for playback. When set to `ma_format_unknown` the device's native format will be used. This can be retrieved after - initialization from the device object directly with `device.playback.format`. - - playback.channels - The number of channels to use for playback. When set to 0 the device's native channel count will be used. This can be retrieved after initialization - from the device object directly with `device.playback.channels`. - - playback.pChannelMap - The channel map to use for playback. When left empty, the device's native channel map will be used. This can be retrieved after initialization from the - device object direct with `device.playback.pChannelMap`. When set, the buffer should contain `channels` items. - - playback.shareMode - The preferred share mode to use for playback. Can be either `ma_share_mode_shared` (default) or `ma_share_mode_exclusive`. Note that if you specify - exclusive mode, but it's not supported by the backend, initialization will fail. You can then fall back to shared mode if desired by changing this to - ma_share_mode_shared and reinitializing. - - capture.pDeviceID - A pointer to a `ma_device_id` structure containing the ID of the capture device to initialize. Setting this NULL (default) will use the system's - default capture device. Retrieve the device ID from the `ma_device_info` structure, which can be retrieved using device enumeration. - - capture.format - The sample format to use for capture. When set to `ma_format_unknown` the device's native format will be used. This can be retrieved after - initialization from the device object directly with `device.capture.format`. - - capture.channels - The number of channels to use for capture. When set to 0 the device's native channel count will be used. This can be retrieved after initialization - from the device object directly with `device.capture.channels`. - - capture.pChannelMap - The channel map to use for capture. When left empty, the device's native channel map will be used. This can be retrieved after initialization from the - device object direct with `device.capture.pChannelMap`. When set, the buffer should contain `channels` items. - - capture.shareMode - The preferred share mode to use for capture. Can be either `ma_share_mode_shared` (default) or `ma_share_mode_exclusive`. Note that if you specify - exclusive mode, but it's not supported by the backend, initialization will fail. You can then fall back to shared mode if desired by changing this to - ma_share_mode_shared and reinitializing. - - wasapi.noAutoConvertSRC - WASAPI only. When set to true, disables WASAPI's automatic resampling and forces the use of miniaudio's resampler. Defaults to false. - - wasapi.noDefaultQualitySRC - WASAPI only. Only used when `wasapi.noAutoConvertSRC` is set to false. When set to true, disables the use of `AUDCLNT_STREAMFLAGS_SRC_DEFAULT_QUALITY`. - You should usually leave this set to false, which is the default. - - wasapi.noAutoStreamRouting - WASAPI only. When set to true, disables automatic stream routing on the WASAPI backend. Defaults to false. - - wasapi.noHardwareOffloading - WASAPI only. When set to true, disables the use of WASAPI's hardware offloading feature. Defaults to false. - - alsa.noMMap - ALSA only. When set to true, disables MMap mode. Defaults to false. - - alsa.noAutoFormat - ALSA only. When set to true, disables ALSA's automatic format conversion by including the SND_PCM_NO_AUTO_FORMAT flag. Defaults to false. - - alsa.noAutoChannels - ALSA only. When set to true, disables ALSA's automatic channel conversion by including the SND_PCM_NO_AUTO_CHANNELS flag. Defaults to false. - - alsa.noAutoResample - ALSA only. When set to true, disables ALSA's automatic resampling by including the SND_PCM_NO_AUTO_RESAMPLE flag. Defaults to false. - - pulse.pStreamNamePlayback - PulseAudio only. Sets the stream name for playback. - - pulse.pStreamNameCapture - PulseAudio only. Sets the stream name for capture. - - pulse.channelMap - PulseAudio only. Sets the channel map that is requested from PulseAudio. See MA_PA_CHANNEL_MAP_* constants. Defaults to MA_PA_CHANNEL_MAP_AIFF. - - coreaudio.allowNominalSampleRateChange - Core Audio only. Desktop only. When enabled, allows the sample rate of the device to be changed at the operating system level. This - is disabled by default in order to prevent intrusive changes to the user's system. This is useful if you want to use a sample rate - that is known to be natively supported by the hardware thereby avoiding the cost of resampling. When set to true, miniaudio will - find the closest match between the sample rate requested in the device config and the sample rates natively supported by the - hardware. When set to false, the sample rate currently set by the operating system will always be used. - - opensl.streamType - OpenSL only. Explicitly sets the stream type. If left unset (`ma_opensl_stream_type_default`), the - stream type will be left unset. Think of this as the type of audio you're playing. - - opensl.recordingPreset - OpenSL only. Explicitly sets the type of recording your program will be doing. When left - unset, the recording preset will be left unchanged. - - aaudio.usage - AAudio only. Explicitly sets the nature of the audio the program will be consuming. When - left unset, the usage will be left unchanged. - - aaudio.contentType - AAudio only. Sets the content type. When left unset, the content type will be left unchanged. - - aaudio.inputPreset - AAudio only. Explicitly sets the type of recording your program will be doing. When left - unset, the input preset will be left unchanged. - - aaudio.noAutoStartAfterReroute - AAudio only. Controls whether or not the device should be automatically restarted after a - stream reroute. When set to false (default) the device will be restarted automatically; - otherwise the device will be stopped. - - -Once initialized, the device's config is immutable. If you need to change the config you will need to initialize a new device. - -After initializing the device it will be in a stopped state. To start it, use `ma_device_start()`. - -If both `periodSizeInFrames` and `periodSizeInMilliseconds` are set to zero, it will default to `MA_DEFAULT_PERIOD_SIZE_IN_MILLISECONDS_LOW_LATENCY` or -`MA_DEFAULT_PERIOD_SIZE_IN_MILLISECONDS_CONSERVATIVE`, depending on whether or not `performanceProfile` is set to `ma_performance_profile_low_latency` or -`ma_performance_profile_conservative`. - -If you request exclusive mode and the backend does not support it an error will be returned. For robustness, you may want to first try initializing the device -in exclusive mode, and then fall back to shared mode if required. Alternatively you can just request shared mode (the default if you leave it unset in the -config) which is the most reliable option. Some backends do not have a practical way of choosing whether or not the device should be exclusive or not (ALSA, -for example) in which case it just acts as a hint. Unless you have special requirements you should try avoiding exclusive mode as it's intrusive to the user. -Starting with Windows 10, miniaudio will use low-latency shared mode where possible which may make exclusive mode unnecessary. - -When sending or receiving data to/from a device, miniaudio will internally perform a format conversion to convert between the format specified by the config -and the format used internally by the backend. If you pass in 0 for the sample format, channel count, sample rate _and_ channel map, data transmission will run -on an optimized pass-through fast path. You can retrieve the format, channel count and sample rate by inspecting the `playback/capture.format`, -`playback/capture.channels` and `sampleRate` members of the device object. - -When compiling for UWP you must ensure you call this function on the main UI thread because the operating system may need to present the user with a message -asking for permissions. Please refer to the official documentation for ActivateAudioInterfaceAsync() for more information. - -ALSA Specific: When initializing the default device, requesting shared mode will try using the "dmix" device for playback and the "dsnoop" device for capture. -If these fail it will try falling back to the "hw" device. - - -Example 1 - Simple Initialization ---------------------------------- -This example shows how to initialize a simple playback device using a standard configuration. If you are just needing to do simple playback from the default -playback device this is usually all you need. - -```c -ma_device_config config = ma_device_config_init(ma_device_type_playback); -config.playback.format = ma_format_f32; -config.playback.channels = 2; -config.sampleRate = 48000; -config.dataCallback = ma_data_callback; -config.pMyUserData = pMyUserData; - -ma_device device; -ma_result result = ma_device_init(NULL, &config, &device); -if (result != MA_SUCCESS) { - // Error -} -``` - - -Example 2 - Advanced Initialization ------------------------------------ -This example shows how you might do some more advanced initialization. In this hypothetical example we want to control the latency by setting the buffer size -and period count. We also want to allow the user to be able to choose which device to output from which means we need a context so we can perform device -enumeration. - -```c -ma_context context; -ma_result result = ma_context_init(NULL, 0, NULL, &context); -if (result != MA_SUCCESS) { - // Error -} - -ma_device_info* pPlaybackDeviceInfos; -ma_uint32 playbackDeviceCount; -result = ma_context_get_devices(&context, &pPlaybackDeviceInfos, &playbackDeviceCount, NULL, NULL); -if (result != MA_SUCCESS) { - // Error -} - -// ... choose a device from pPlaybackDeviceInfos ... - -ma_device_config config = ma_device_config_init(ma_device_type_playback); -config.playback.pDeviceID = pMyChosenDeviceID; // <-- Get this from the `id` member of one of the `ma_device_info` objects returned by ma_context_get_devices(). -config.playback.format = ma_format_f32; -config.playback.channels = 2; -config.sampleRate = 48000; -config.dataCallback = ma_data_callback; -config.pUserData = pMyUserData; -config.periodSizeInMilliseconds = 10; -config.periods = 3; - -ma_device device; -result = ma_device_init(&context, &config, &device); -if (result != MA_SUCCESS) { - // Error -} -``` - - -See Also --------- -ma_device_config_init() -ma_device_uninit() -ma_device_start() -ma_context_init() -ma_context_get_devices() -ma_context_enumerate_devices() -*/ -MA_API ma_result ma_device_init(ma_context* pContext, const ma_device_config* pConfig, ma_device* pDevice); - -/* -Initializes a device without a context, with extra parameters for controlling the configuration of the internal self-managed context. - -This is the same as `ma_device_init()`, only instead of a context being passed in, the parameters from `ma_context_init()` are passed in instead. This function -allows you to configure the internally created context. - - -Parameters ----------- -backends (in, optional) - A list of backends to try initializing, in priority order. Can be NULL, in which case it uses default priority order. - -backendCount (in, optional) - The number of items in `backend`. Ignored if `backend` is NULL. - -pContextConfig (in, optional) - The context configuration. - -pConfig (in) - A pointer to the device configuration. Cannot be null. See remarks for details. - -pDevice (out) - A pointer to the device object being initialized. - - -Return Value ------------- -MA_SUCCESS if successful; any other error code otherwise. - - -Thread Safety -------------- -Unsafe. It is not safe to call this function simultaneously for different devices because some backends depend on and mutate global state. The same applies to -calling this at the same time as `ma_device_uninit()`. - - -Callback Safety ---------------- -Unsafe. It is not safe to call this inside any callback. - - -Remarks -------- -You only need to use this function if you want to configure the context differently to its defaults. You should never use this function if you want to manage -your own context. - -See the documentation for `ma_context_init()` for information on the different context configuration options. - - -See Also --------- -ma_device_init() -ma_device_uninit() -ma_device_config_init() -ma_context_init() -*/ -MA_API ma_result ma_device_init_ex(const ma_backend backends[], ma_uint32 backendCount, const ma_context_config* pContextConfig, const ma_device_config* pConfig, ma_device* pDevice); - -/* -Uninitializes a device. - -This will explicitly stop the device. You do not need to call `ma_device_stop()` beforehand, but it's harmless if you do. - - -Parameters ----------- -pDevice (in) - A pointer to the device to stop. - - -Return Value ------------- -Nothing - - -Thread Safety -------------- -Unsafe. As soon as this API is called the device should be considered undefined. - - -Callback Safety ---------------- -Unsafe. It is not safe to call this inside any callback. Doing this will result in a deadlock. - - -See Also --------- -ma_device_init() -ma_device_stop() -*/ -MA_API void ma_device_uninit(ma_device* pDevice); - - -/* -Retrieves a pointer to the context that owns the given device. -*/ -MA_API ma_context* ma_device_get_context(ma_device* pDevice); - -/* -Helper function for retrieving the log object associated with the context that owns this device. -*/ -MA_API ma_log* ma_device_get_log(ma_device* pDevice); - - -/* -Retrieves information about the device. - - -Parameters ----------- -pDevice (in) - A pointer to the device whose information is being retrieved. - -type (in) - The device type. This parameter is required for duplex devices. When retrieving device - information, you are doing so for an individual playback or capture device. - -pDeviceInfo (out) - A pointer to the `ma_device_info` that will receive the device information. - - -Return Value ------------- -MA_SUCCESS if successful; any other error code otherwise. - - -Thread Safety -------------- -Unsafe. This should be considered unsafe because it may be calling into the backend which may or -may not be safe. - - -Callback Safety ---------------- -Unsafe. You should avoid calling this in the data callback because it may call into the backend -which may or may not be safe. -*/ -MA_API ma_result ma_device_get_info(ma_device* pDevice, ma_device_type type, ma_device_info* pDeviceInfo); - - -/* -Retrieves the name of the device. - - -Parameters ----------- -pDevice (in) - A pointer to the device whose information is being retrieved. - -type (in) - The device type. This parameter is required for duplex devices. When retrieving device - information, you are doing so for an individual playback or capture device. - -pName (out) - A pointer to the buffer that will receive the name. - -nameCap (in) - The capacity of the output buffer, including space for the null terminator. - -pLengthNotIncludingNullTerminator (out, optional) - A pointer to the variable that will receive the length of the name, not including the null - terminator. - - -Return Value ------------- -MA_SUCCESS if successful; any other error code otherwise. - - -Thread Safety -------------- -Unsafe. This should be considered unsafe because it may be calling into the backend which may or -may not be safe. - - -Callback Safety ---------------- -Unsafe. You should avoid calling this in the data callback because it may call into the backend -which may or may not be safe. - - -Remarks -------- -If the name does not fully fit into the output buffer, it'll be truncated. You can pass in NULL to -`pName` if you want to first get the length of the name for the purpose of memory allocation of the -output buffer. Allocating a buffer of size `MA_MAX_DEVICE_NAME_LENGTH + 1` should be enough for -most cases and will avoid the need for the inefficiency of calling this function twice. - -This is implemented in terms of `ma_device_get_info()`. -*/ -MA_API ma_result ma_device_get_name(ma_device* pDevice, ma_device_type type, char* pName, size_t nameCap, size_t* pLengthNotIncludingNullTerminator); - - -/* -Starts the device. For playback devices this begins playback. For capture devices it begins recording. - -Use `ma_device_stop()` to stop the device. - - -Parameters ----------- -pDevice (in) - A pointer to the device to start. - - -Return Value ------------- -MA_SUCCESS if successful; any other error code otherwise. - - -Thread Safety -------------- -Safe. It's safe to call this from any thread with the exception of the callback thread. - - -Callback Safety ---------------- -Unsafe. It is not safe to call this inside any callback. - - -Remarks -------- -For a playback device, this will retrieve an initial chunk of audio data from the client before returning. The reason for this is to ensure there is valid -audio data in the buffer, which needs to be done before the device begins playback. - -This API waits until the backend device has been started for real by the worker thread. It also waits on a mutex for thread-safety. - -Do not call this in any callback. - - -See Also --------- -ma_device_stop() -*/ -MA_API ma_result ma_device_start(ma_device* pDevice); - -/* -Stops the device. For playback devices this stops playback. For capture devices it stops recording. - -Use `ma_device_start()` to start the device again. - - -Parameters ----------- -pDevice (in) - A pointer to the device to stop. - - -Return Value ------------- -MA_SUCCESS if successful; any other error code otherwise. - - -Thread Safety -------------- -Safe. It's safe to call this from any thread with the exception of the callback thread. - - -Callback Safety ---------------- -Unsafe. It is not safe to call this inside any callback. Doing this will result in a deadlock. - - -Remarks -------- -This API needs to wait on the worker thread to stop the backend device properly before returning. It also waits on a mutex for thread-safety. In addition, some -backends need to wait for the device to finish playback/recording of the current fragment which can take some time (usually proportionate to the buffer size -that was specified at initialization time). - -Backends are required to either pause the stream in-place or drain the buffer if pausing is not possible. The reason for this is that stopping the device and -the resuming it with ma_device_start() (which you might do when your program loses focus) may result in a situation where those samples are never output to the -speakers or received from the microphone which can in turn result in de-syncs. - -Do not call this in any callback. - - -See Also --------- -ma_device_start() -*/ -MA_API ma_result ma_device_stop(ma_device* pDevice); - -/* -Determines whether or not the device is started. - - -Parameters ----------- -pDevice (in) - A pointer to the device whose start state is being retrieved. - - -Return Value ------------- -True if the device is started, false otherwise. - - -Thread Safety -------------- -Safe. If another thread calls `ma_device_start()` or `ma_device_stop()` at this same time as this function is called, there's a very small chance the return -value will be out of sync. - - -Callback Safety ---------------- -Safe. This is implemented as a simple accessor. - - -See Also --------- -ma_device_start() -ma_device_stop() -*/ -MA_API ma_bool32 ma_device_is_started(const ma_device* pDevice); - - -/* -Retrieves the state of the device. - - -Parameters ----------- -pDevice (in) - A pointer to the device whose state is being retrieved. - - -Return Value ------------- -The current state of the device. The return value will be one of the following: - - +-------------------------------+------------------------------------------------------------------------------+ - | ma_device_state_uninitialized | Will only be returned if the device is in the middle of initialization. | - +-------------------------------+------------------------------------------------------------------------------+ - | ma_device_state_stopped | The device is stopped. The initial state of the device after initialization. | - +-------------------------------+------------------------------------------------------------------------------+ - | ma_device_state_started | The device started and requesting and/or delivering audio data. | - +-------------------------------+------------------------------------------------------------------------------+ - | ma_device_state_starting | The device is in the process of starting. | - +-------------------------------+------------------------------------------------------------------------------+ - | ma_device_state_stopping | The device is in the process of stopping. | - +-------------------------------+------------------------------------------------------------------------------+ - - -Thread Safety -------------- -Safe. This is implemented as a simple accessor. Note that if the device is started or stopped at the same time as this function is called, -there's a possibility the return value could be out of sync. See remarks. - - -Callback Safety ---------------- -Safe. This is implemented as a simple accessor. - - -Remarks -------- -The general flow of a devices state goes like this: - - ``` - ma_device_init() -> ma_device_state_uninitialized -> ma_device_state_stopped - ma_device_start() -> ma_device_state_starting -> ma_device_state_started - ma_device_stop() -> ma_device_state_stopping -> ma_device_state_stopped - ``` - -When the state of the device is changed with `ma_device_start()` or `ma_device_stop()` at this same time as this function is called, the -value returned by this function could potentially be out of sync. If this is significant to your program you need to implement your own -synchronization. -*/ -MA_API ma_device_state ma_device_get_state(const ma_device* pDevice); - - -/* -Performs post backend initialization routines for setting up internal data conversion. - -This should be called whenever the backend is initialized. The only time this should be called from -outside of miniaudio is if you're implementing a custom backend, and you would only do it if you -are reinitializing the backend due to rerouting or reinitializing for some reason. - - -Parameters ----------- -pDevice [in] - A pointer to the device. - -deviceType [in] - The type of the device that was just reinitialized. - -pPlaybackDescriptor [in] - The descriptor of the playback device containing the internal data format and buffer sizes. - -pPlaybackDescriptor [in] - The descriptor of the capture device containing the internal data format and buffer sizes. - - -Return Value ------------- -MA_SUCCESS if successful; any other error otherwise. - - -Thread Safety -------------- -Unsafe. This will be reinitializing internal data converters which may be in use by another thread. - - -Callback Safety ---------------- -Unsafe. This will be reinitializing internal data converters which may be in use by the callback. - - -Remarks -------- -For a duplex device, you can call this for only one side of the system. This is why the deviceType -is specified as a parameter rather than deriving it from the device. - -You do not need to call this manually unless you are doing a custom backend, in which case you need -only do it if you're manually performing rerouting or reinitialization. -*/ -MA_API ma_result ma_device_post_init(ma_device* pDevice, ma_device_type deviceType, const ma_device_descriptor* pPlaybackDescriptor, const ma_device_descriptor* pCaptureDescriptor); - - -/* -Sets the master volume factor for the device. - -The volume factor must be between 0 (silence) and 1 (full volume). Use `ma_device_set_master_volume_db()` to use decibel notation, where 0 is full volume and -values less than 0 decreases the volume. - - -Parameters ----------- -pDevice (in) - A pointer to the device whose volume is being set. - -volume (in) - The new volume factor. Must be >= 0. - - -Return Value ------------- -MA_SUCCESS if the volume was set successfully. -MA_INVALID_ARGS if pDevice is NULL. -MA_INVALID_ARGS if volume is negative. - - -Thread Safety -------------- -Safe. This just sets a local member of the device object. - - -Callback Safety ---------------- -Safe. If you set the volume in the data callback, that data written to the output buffer will have the new volume applied. - - -Remarks -------- -This applies the volume factor across all channels. - -This does not change the operating system's volume. It only affects the volume for the given `ma_device` object's audio stream. - - -See Also --------- -ma_device_get_master_volume() -ma_device_set_master_volume_db() -ma_device_get_master_volume_db() -*/ -MA_API ma_result ma_device_set_master_volume(ma_device* pDevice, float volume); - -/* -Retrieves the master volume factor for the device. - - -Parameters ----------- -pDevice (in) - A pointer to the device whose volume factor is being retrieved. - -pVolume (in) - A pointer to the variable that will receive the volume factor. The returned value will be in the range of [0, 1]. - - -Return Value ------------- -MA_SUCCESS if successful. -MA_INVALID_ARGS if pDevice is NULL. -MA_INVALID_ARGS if pVolume is NULL. - - -Thread Safety -------------- -Safe. This just a simple member retrieval. - - -Callback Safety ---------------- -Safe. - - -Remarks -------- -If an error occurs, `*pVolume` will be set to 0. - - -See Also --------- -ma_device_set_master_volume() -ma_device_set_master_volume_gain_db() -ma_device_get_master_volume_gain_db() -*/ -MA_API ma_result ma_device_get_master_volume(ma_device* pDevice, float* pVolume); - -/* -Sets the master volume for the device as gain in decibels. - -A gain of 0 is full volume, whereas a gain of < 0 will decrease the volume. - - -Parameters ----------- -pDevice (in) - A pointer to the device whose gain is being set. - -gainDB (in) - The new volume as gain in decibels. Must be less than or equal to 0, where 0 is full volume and anything less than 0 decreases the volume. - - -Return Value ------------- -MA_SUCCESS if the volume was set successfully. -MA_INVALID_ARGS if pDevice is NULL. -MA_INVALID_ARGS if the gain is > 0. - - -Thread Safety -------------- -Safe. This just sets a local member of the device object. - - -Callback Safety ---------------- -Safe. If you set the volume in the data callback, that data written to the output buffer will have the new volume applied. - - -Remarks -------- -This applies the gain across all channels. - -This does not change the operating system's volume. It only affects the volume for the given `ma_device` object's audio stream. - - -See Also --------- -ma_device_get_master_volume_gain_db() -ma_device_set_master_volume() -ma_device_get_master_volume() -*/ -MA_API ma_result ma_device_set_master_volume_db(ma_device* pDevice, float gainDB); - -/* -Retrieves the master gain in decibels. - - -Parameters ----------- -pDevice (in) - A pointer to the device whose gain is being retrieved. - -pGainDB (in) - A pointer to the variable that will receive the gain in decibels. The returned value will be <= 0. - - -Return Value ------------- -MA_SUCCESS if successful. -MA_INVALID_ARGS if pDevice is NULL. -MA_INVALID_ARGS if pGainDB is NULL. - - -Thread Safety -------------- -Safe. This just a simple member retrieval. - - -Callback Safety ---------------- -Safe. - - -Remarks -------- -If an error occurs, `*pGainDB` will be set to 0. - - -See Also --------- -ma_device_set_master_volume_db() -ma_device_set_master_volume() -ma_device_get_master_volume() -*/ -MA_API ma_result ma_device_get_master_volume_db(ma_device* pDevice, float* pGainDB); - - -/* -Called from the data callback of asynchronous backends to allow miniaudio to process the data and fire the miniaudio data callback. - - -Parameters ----------- -pDevice (in) - A pointer to device whose processing the data callback. - -pOutput (out) - A pointer to the buffer that will receive the output PCM frame data. On a playback device this must not be NULL. On a duplex device - this can be NULL, in which case pInput must not be NULL. - -pInput (in) - A pointer to the buffer containing input PCM frame data. On a capture device this must not be NULL. On a duplex device this can be - NULL, in which case `pOutput` must not be NULL. - -frameCount (in) - The number of frames being processed. - - -Return Value ------------- -MA_SUCCESS if successful; any other result code otherwise. - - -Thread Safety -------------- -This function should only ever be called from the internal data callback of the backend. It is safe to call this simultaneously between a -playback and capture device in duplex setups. - - -Callback Safety ---------------- -Do not call this from the miniaudio data callback. It should only ever be called from the internal data callback of the backend. - - -Remarks -------- -If both `pOutput` and `pInput` are NULL, and error will be returned. In duplex scenarios, both `pOutput` and `pInput` can be non-NULL, in -which case `pInput` will be processed first, followed by `pOutput`. - -If you are implementing a custom backend, and that backend uses a callback for data delivery, you'll need to call this from inside that -callback. -*/ -MA_API ma_result ma_device_handle_backend_data_callback(ma_device* pDevice, void* pOutput, const void* pInput, ma_uint32 frameCount); - - -/* -Calculates an appropriate buffer size from a descriptor, native sample rate and performance profile. - -This function is used by backends for helping determine an appropriately sized buffer to use with -the device depending on the values of `periodSizeInFrames` and `periodSizeInMilliseconds` in the -`pDescriptor` object. Since buffer size calculations based on time depends on the sample rate, a -best guess at the device's native sample rate is also required which is where `nativeSampleRate` -comes in. In addition, the performance profile is also needed for cases where both the period size -in frames and milliseconds are both zero. - - -Parameters ----------- -pDescriptor (in) - A pointer to device descriptor whose `periodSizeInFrames` and `periodSizeInMilliseconds` members - will be used for the calculation of the buffer size. - -nativeSampleRate (in) - The device's native sample rate. This is only ever used when the `periodSizeInFrames` member of - `pDescriptor` is zero. In this case, `periodSizeInMilliseconds` will be used instead, in which - case a sample rate is required to convert to a size in frames. - -performanceProfile (in) - When both the `periodSizeInFrames` and `periodSizeInMilliseconds` members of `pDescriptor` are - zero, miniaudio will fall back to a buffer size based on the performance profile. The profile - to use for this calculation is determine by this parameter. - - -Return Value ------------- -The calculated buffer size in frames. - - -Thread Safety -------------- -This is safe so long as nothing modifies `pDescriptor` at the same time. However, this function -should only ever be called from within the backend's device initialization routine and therefore -shouldn't have any multithreading concerns. - - -Callback Safety ---------------- -This is safe to call within the data callback, but there is no reason to ever do this. - - -Remarks -------- -If `nativeSampleRate` is zero, this function will fall back to `pDescriptor->sampleRate`. If that -is also zero, `MA_DEFAULT_SAMPLE_RATE` will be used instead. -*/ -MA_API ma_uint32 ma_calculate_buffer_size_in_frames_from_descriptor(const ma_device_descriptor* pDescriptor, ma_uint32 nativeSampleRate, ma_performance_profile performanceProfile); - - - -/* -Retrieves a friendly name for a backend. -*/ -MA_API const char* ma_get_backend_name(ma_backend backend); - -/* -Retrieves the backend enum from the given name. -*/ -MA_API ma_result ma_get_backend_from_name(const char* pBackendName, ma_backend* pBackend); - -/* -Determines whether or not the given backend is available by the compilation environment. -*/ -MA_API ma_bool32 ma_is_backend_enabled(ma_backend backend); - -/* -Retrieves compile-time enabled backends. - - -Parameters ----------- -pBackends (out, optional) - A pointer to the buffer that will receive the enabled backends. Set to NULL to retrieve the backend count. Setting - the capacity of the buffer to `MA_BUFFER_COUNT` will guarantee it's large enough for all backends. - -backendCap (in) - The capacity of the `pBackends` buffer. - -pBackendCount (out) - A pointer to the variable that will receive the enabled backend count. - - -Return Value ------------- -MA_SUCCESS if successful. -MA_INVALID_ARGS if `pBackendCount` is NULL. -MA_NO_SPACE if the capacity of `pBackends` is not large enough. - -If `MA_NO_SPACE` is returned, the `pBackends` buffer will be filled with `*pBackendCount` values. - - -Thread Safety -------------- -Safe. - - -Callback Safety ---------------- -Safe. - - -Remarks -------- -If you want to retrieve the number of backends so you can determine the capacity of `pBackends` buffer, you can call -this function with `pBackends` set to NULL. - -This will also enumerate the null backend. If you don't want to include this you need to check for `ma_backend_null` -when you enumerate over the returned backends and handle it appropriately. Alternatively, you can disable it at -compile time with `MA_NO_NULL`. - -The returned backends are determined based on compile time settings, not the platform it's currently running on. For -example, PulseAudio will be returned if it was enabled at compile time, even when the user doesn't actually have -PulseAudio installed. - - -Example 1 ---------- -The example below retrieves the enabled backend count using a fixed sized buffer allocated on the stack. The buffer is -given a capacity of `MA_BACKEND_COUNT` which will guarantee it'll be large enough to store all available backends. -Since `MA_BACKEND_COUNT` is always a relatively small value, this should be suitable for most scenarios. - -``` -ma_backend enabledBackends[MA_BACKEND_COUNT]; -size_t enabledBackendCount; - -result = ma_get_enabled_backends(enabledBackends, MA_BACKEND_COUNT, &enabledBackendCount); -if (result != MA_SUCCESS) { - // Failed to retrieve enabled backends. Should never happen in this example since all inputs are valid. -} -``` - - -See Also --------- -ma_is_backend_enabled() -*/ -MA_API ma_result ma_get_enabled_backends(ma_backend* pBackends, size_t backendCap, size_t* pBackendCount); - -/* -Determines whether or not loopback mode is support by a backend. -*/ -MA_API ma_bool32 ma_is_loopback_supported(ma_backend backend); - -#endif /* MA_NO_DEVICE_IO */ - - - -/************************************************************************************************************************************************************ - -Utilities - -************************************************************************************************************************************************************/ - -/* -Calculates a buffer size in milliseconds (rounded up) from the specified number of frames and sample rate. -*/ -MA_API ma_uint32 ma_calculate_buffer_size_in_milliseconds_from_frames(ma_uint32 bufferSizeInFrames, ma_uint32 sampleRate); - -/* -Calculates a buffer size in frames from the specified number of milliseconds and sample rate. -*/ -MA_API ma_uint32 ma_calculate_buffer_size_in_frames_from_milliseconds(ma_uint32 bufferSizeInMilliseconds, ma_uint32 sampleRate); - -/* -Copies PCM frames from one buffer to another. -*/ -MA_API void ma_copy_pcm_frames(void* dst, const void* src, ma_uint64 frameCount, ma_format format, ma_uint32 channels); - -/* -Copies silent frames into the given buffer. - -Remarks -------- -For all formats except `ma_format_u8`, the output buffer will be filled with 0. For `ma_format_u8` it will be filled with 128. The reason for this is that it -makes more sense for the purpose of mixing to initialize it to the center point. -*/ -MA_API void ma_silence_pcm_frames(void* p, ma_uint64 frameCount, ma_format format, ma_uint32 channels); - - -/* -Offsets a pointer by the specified number of PCM frames. -*/ -MA_API void* ma_offset_pcm_frames_ptr(void* p, ma_uint64 offsetInFrames, ma_format format, ma_uint32 channels); -MA_API const void* ma_offset_pcm_frames_const_ptr(const void* p, ma_uint64 offsetInFrames, ma_format format, ma_uint32 channels); -static MA_INLINE float* ma_offset_pcm_frames_ptr_f32(float* p, ma_uint64 offsetInFrames, ma_uint32 channels) { return (float*)ma_offset_pcm_frames_ptr((void*)p, offsetInFrames, ma_format_f32, channels); } -static MA_INLINE const float* ma_offset_pcm_frames_const_ptr_f32(const float* p, ma_uint64 offsetInFrames, ma_uint32 channels) { return (const float*)ma_offset_pcm_frames_const_ptr((const void*)p, offsetInFrames, ma_format_f32, channels); } - - -/* -Clips samples. -*/ -MA_API void ma_clip_samples_u8(ma_uint8* pDst, const ma_int16* pSrc, ma_uint64 count); -MA_API void ma_clip_samples_s16(ma_int16* pDst, const ma_int32* pSrc, ma_uint64 count); -MA_API void ma_clip_samples_s24(ma_uint8* pDst, const ma_int64* pSrc, ma_uint64 count); -MA_API void ma_clip_samples_s32(ma_int32* pDst, const ma_int64* pSrc, ma_uint64 count); -MA_API void ma_clip_samples_f32(float* pDst, const float* pSrc, ma_uint64 count); -MA_API void ma_clip_pcm_frames(void* pDst, const void* pSrc, ma_uint64 frameCount, ma_format format, ma_uint32 channels); - -/* -Helper for applying a volume factor to samples. - -Note that the source and destination buffers can be the same, in which case it'll perform the operation in-place. -*/ -MA_API void ma_copy_and_apply_volume_factor_u8(ma_uint8* pSamplesOut, const ma_uint8* pSamplesIn, ma_uint64 sampleCount, float factor); -MA_API void ma_copy_and_apply_volume_factor_s16(ma_int16* pSamplesOut, const ma_int16* pSamplesIn, ma_uint64 sampleCount, float factor); -MA_API void ma_copy_and_apply_volume_factor_s24(void* pSamplesOut, const void* pSamplesIn, ma_uint64 sampleCount, float factor); -MA_API void ma_copy_and_apply_volume_factor_s32(ma_int32* pSamplesOut, const ma_int32* pSamplesIn, ma_uint64 sampleCount, float factor); -MA_API void ma_copy_and_apply_volume_factor_f32(float* pSamplesOut, const float* pSamplesIn, ma_uint64 sampleCount, float factor); - -MA_API void ma_apply_volume_factor_u8(ma_uint8* pSamples, ma_uint64 sampleCount, float factor); -MA_API void ma_apply_volume_factor_s16(ma_int16* pSamples, ma_uint64 sampleCount, float factor); -MA_API void ma_apply_volume_factor_s24(void* pSamples, ma_uint64 sampleCount, float factor); -MA_API void ma_apply_volume_factor_s32(ma_int32* pSamples, ma_uint64 sampleCount, float factor); -MA_API void ma_apply_volume_factor_f32(float* pSamples, ma_uint64 sampleCount, float factor); - -MA_API void ma_copy_and_apply_volume_factor_pcm_frames_u8(ma_uint8* pFramesOut, const ma_uint8* pFramesIn, ma_uint64 frameCount, ma_uint32 channels, float factor); -MA_API void ma_copy_and_apply_volume_factor_pcm_frames_s16(ma_int16* pFramesOut, const ma_int16* pFramesIn, ma_uint64 frameCount, ma_uint32 channels, float factor); -MA_API void ma_copy_and_apply_volume_factor_pcm_frames_s24(void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount, ma_uint32 channels, float factor); -MA_API void ma_copy_and_apply_volume_factor_pcm_frames_s32(ma_int32* pFramesOut, const ma_int32* pFramesIn, ma_uint64 frameCount, ma_uint32 channels, float factor); -MA_API void ma_copy_and_apply_volume_factor_pcm_frames_f32(float* pFramesOut, const float* pFramesIn, ma_uint64 frameCount, ma_uint32 channels, float factor); -MA_API void ma_copy_and_apply_volume_factor_pcm_frames(void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount, ma_format format, ma_uint32 channels, float factor); - -MA_API void ma_apply_volume_factor_pcm_frames_u8(ma_uint8* pFrames, ma_uint64 frameCount, ma_uint32 channels, float factor); -MA_API void ma_apply_volume_factor_pcm_frames_s16(ma_int16* pFrames, ma_uint64 frameCount, ma_uint32 channels, float factor); -MA_API void ma_apply_volume_factor_pcm_frames_s24(void* pFrames, ma_uint64 frameCount, ma_uint32 channels, float factor); -MA_API void ma_apply_volume_factor_pcm_frames_s32(ma_int32* pFrames, ma_uint64 frameCount, ma_uint32 channels, float factor); -MA_API void ma_apply_volume_factor_pcm_frames_f32(float* pFrames, ma_uint64 frameCount, ma_uint32 channels, float factor); -MA_API void ma_apply_volume_factor_pcm_frames(void* pFrames, ma_uint64 frameCount, ma_format format, ma_uint32 channels, float factor); - -MA_API void ma_copy_and_apply_volume_factor_per_channel_f32(float* pFramesOut, const float* pFramesIn, ma_uint64 frameCount, ma_uint32 channels, float* pChannelGains); - - -MA_API void ma_copy_and_apply_volume_and_clip_samples_u8(ma_uint8* pDst, const ma_int16* pSrc, ma_uint64 count, float volume); -MA_API void ma_copy_and_apply_volume_and_clip_samples_s16(ma_int16* pDst, const ma_int32* pSrc, ma_uint64 count, float volume); -MA_API void ma_copy_and_apply_volume_and_clip_samples_s24(ma_uint8* pDst, const ma_int64* pSrc, ma_uint64 count, float volume); -MA_API void ma_copy_and_apply_volume_and_clip_samples_s32(ma_int32* pDst, const ma_int64* pSrc, ma_uint64 count, float volume); -MA_API void ma_copy_and_apply_volume_and_clip_samples_f32(float* pDst, const float* pSrc, ma_uint64 count, float volume); -MA_API void ma_copy_and_apply_volume_and_clip_pcm_frames(void* pDst, const void* pSrc, ma_uint64 frameCount, ma_format format, ma_uint32 channels, float volume); - - -/* -Helper for converting a linear factor to gain in decibels. -*/ -MA_API float ma_volume_linear_to_db(float factor); - -/* -Helper for converting gain in decibels to a linear factor. -*/ -MA_API float ma_volume_db_to_linear(float gain); - - -/* -Mixes the specified number of frames in floating point format with a volume factor. - -This will run on an optimized path when the volume is equal to 1. -*/ -MA_API ma_result ma_mix_pcm_frames_f32(float* pDst, const float* pSrc, ma_uint64 frameCount, ma_uint32 channels, float volume); - - - - -/************************************************************************************************************************************************************ - -VFS -=== - -The VFS object (virtual file system) is what's used to customize file access. This is useful in cases where stdio FILE* based APIs may not be entirely -appropriate for a given situation. - -************************************************************************************************************************************************************/ -typedef void ma_vfs; -typedef ma_handle ma_vfs_file; - -typedef enum -{ - MA_OPEN_MODE_READ = 0x00000001, - MA_OPEN_MODE_WRITE = 0x00000002 -} ma_open_mode_flags; - -typedef enum -{ - ma_seek_origin_start, - ma_seek_origin_current, - ma_seek_origin_end /* Not used by decoders. */ -} ma_seek_origin; - -typedef struct -{ - ma_uint64 sizeInBytes; -} ma_file_info; - -typedef struct -{ - ma_result (* onOpen) (ma_vfs* pVFS, const char* pFilePath, ma_uint32 openMode, ma_vfs_file* pFile); - ma_result (* onOpenW)(ma_vfs* pVFS, const wchar_t* pFilePath, ma_uint32 openMode, ma_vfs_file* pFile); - ma_result (* onClose)(ma_vfs* pVFS, ma_vfs_file file); - ma_result (* onRead) (ma_vfs* pVFS, ma_vfs_file file, void* pDst, size_t sizeInBytes, size_t* pBytesRead); - ma_result (* onWrite)(ma_vfs* pVFS, ma_vfs_file file, const void* pSrc, size_t sizeInBytes, size_t* pBytesWritten); - ma_result (* onSeek) (ma_vfs* pVFS, ma_vfs_file file, ma_int64 offset, ma_seek_origin origin); - ma_result (* onTell) (ma_vfs* pVFS, ma_vfs_file file, ma_int64* pCursor); - ma_result (* onInfo) (ma_vfs* pVFS, ma_vfs_file file, ma_file_info* pInfo); -} ma_vfs_callbacks; - -MA_API ma_result ma_vfs_open(ma_vfs* pVFS, const char* pFilePath, ma_uint32 openMode, ma_vfs_file* pFile); -MA_API ma_result ma_vfs_open_w(ma_vfs* pVFS, const wchar_t* pFilePath, ma_uint32 openMode, ma_vfs_file* pFile); -MA_API ma_result ma_vfs_close(ma_vfs* pVFS, ma_vfs_file file); -MA_API ma_result ma_vfs_read(ma_vfs* pVFS, ma_vfs_file file, void* pDst, size_t sizeInBytes, size_t* pBytesRead); -MA_API ma_result ma_vfs_write(ma_vfs* pVFS, ma_vfs_file file, const void* pSrc, size_t sizeInBytes, size_t* pBytesWritten); -MA_API ma_result ma_vfs_seek(ma_vfs* pVFS, ma_vfs_file file, ma_int64 offset, ma_seek_origin origin); -MA_API ma_result ma_vfs_tell(ma_vfs* pVFS, ma_vfs_file file, ma_int64* pCursor); -MA_API ma_result ma_vfs_info(ma_vfs* pVFS, ma_vfs_file file, ma_file_info* pInfo); -MA_API ma_result ma_vfs_open_and_read_file(ma_vfs* pVFS, const char* pFilePath, void** ppData, size_t* pSize, const ma_allocation_callbacks* pAllocationCallbacks); - -typedef struct -{ - ma_vfs_callbacks cb; - ma_allocation_callbacks allocationCallbacks; /* Only used for the wchar_t version of open() on non-Windows platforms. */ -} ma_default_vfs; - -MA_API ma_result ma_default_vfs_init(ma_default_vfs* pVFS, const ma_allocation_callbacks* pAllocationCallbacks); - - - -typedef ma_result (* ma_read_proc)(void* pUserData, void* pBufferOut, size_t bytesToRead, size_t* pBytesRead); -typedef ma_result (* ma_seek_proc)(void* pUserData, ma_int64 offset, ma_seek_origin origin); -typedef ma_result (* ma_tell_proc)(void* pUserData, ma_int64* pCursor); - - - -#if !defined(MA_NO_DECODING) || !defined(MA_NO_ENCODING) -typedef enum -{ - ma_encoding_format_unknown = 0, - ma_encoding_format_wav, - ma_encoding_format_flac, - ma_encoding_format_mp3, - ma_encoding_format_vorbis -} ma_encoding_format; -#endif - -/************************************************************************************************************************************************************ - -Decoding -======== - -Decoders are independent of the main device API. Decoding APIs can be called freely inside the device's data callback, but they are not thread safe unless -you do your own synchronization. - -************************************************************************************************************************************************************/ -#ifndef MA_NO_DECODING -typedef struct ma_decoder ma_decoder; - - -typedef struct -{ - ma_format preferredFormat; - ma_uint32 seekPointCount; /* Set to > 0 to generate a seektable if the decoding backend supports it. */ -} ma_decoding_backend_config; - -MA_API ma_decoding_backend_config ma_decoding_backend_config_init(ma_format preferredFormat, ma_uint32 seekPointCount); - - -typedef struct -{ - ma_result (* onInit )(void* pUserData, ma_read_proc onRead, ma_seek_proc onSeek, ma_tell_proc onTell, void* pReadSeekTellUserData, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_data_source** ppBackend); - ma_result (* onInitFile )(void* pUserData, const char* pFilePath, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_data_source** ppBackend); /* Optional. */ - ma_result (* onInitFileW )(void* pUserData, const wchar_t* pFilePath, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_data_source** ppBackend); /* Optional. */ - ma_result (* onInitMemory)(void* pUserData, const void* pData, size_t dataSize, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_data_source** ppBackend); /* Optional. */ - void (* onUninit )(void* pUserData, ma_data_source* pBackend, const ma_allocation_callbacks* pAllocationCallbacks); -} ma_decoding_backend_vtable; - - -typedef ma_result (* ma_decoder_read_proc)(ma_decoder* pDecoder, void* pBufferOut, size_t bytesToRead, size_t* pBytesRead); /* Returns the number of bytes read. */ -typedef ma_result (* ma_decoder_seek_proc)(ma_decoder* pDecoder, ma_int64 byteOffset, ma_seek_origin origin); -typedef ma_result (* ma_decoder_tell_proc)(ma_decoder* pDecoder, ma_int64* pCursor); - -typedef struct -{ - ma_format format; /* Set to 0 or ma_format_unknown to use the stream's internal format. */ - ma_uint32 channels; /* Set to 0 to use the stream's internal channels. */ - ma_uint32 sampleRate; /* Set to 0 to use the stream's internal sample rate. */ - ma_channel* pChannelMap; - ma_channel_mix_mode channelMixMode; - ma_dither_mode ditherMode; - ma_resampler_config resampling; - ma_allocation_callbacks allocationCallbacks; - ma_encoding_format encodingFormat; - ma_uint32 seekPointCount; /* When set to > 0, specifies the number of seek points to use for the generation of a seek table. Not all decoding backends support this. */ - ma_decoding_backend_vtable** ppCustomBackendVTables; - ma_uint32 customBackendCount; - void* pCustomBackendUserData; -} ma_decoder_config; - -struct ma_decoder -{ - ma_data_source_base ds; - ma_data_source* pBackend; /* The decoding backend we'll be pulling data from. */ - const ma_decoding_backend_vtable* pBackendVTable; /* The vtable for the decoding backend. This needs to be stored so we can access the onUninit() callback. */ - void* pBackendUserData; - ma_decoder_read_proc onRead; - ma_decoder_seek_proc onSeek; - ma_decoder_tell_proc onTell; - void* pUserData; - ma_uint64 readPointerInPCMFrames; /* In output sample rate. Used for keeping track of how many frames are available for decoding. */ - ma_format outputFormat; - ma_uint32 outputChannels; - ma_uint32 outputSampleRate; - ma_data_converter converter; /* Data conversion is achieved by running frames through this. */ - void* pInputCache; /* In input format. Can be null if it's not needed. */ - ma_uint64 inputCacheCap; /* The capacity of the input cache. */ - ma_uint64 inputCacheConsumed; /* The number of frames that have been consumed in the cache. Used for determining the next valid frame. */ - ma_uint64 inputCacheRemaining; /* The number of valid frames remaining in the cache. */ - ma_allocation_callbacks allocationCallbacks; - union - { - struct - { - ma_vfs* pVFS; - ma_vfs_file file; - } vfs; - struct - { - const ma_uint8* pData; - size_t dataSize; - size_t currentReadPos; - } memory; /* Only used for decoders that were opened against a block of memory. */ - } data; -}; - -MA_API ma_decoder_config ma_decoder_config_init(ma_format outputFormat, ma_uint32 outputChannels, ma_uint32 outputSampleRate); -MA_API ma_decoder_config ma_decoder_config_init_default(void); - -MA_API ma_result ma_decoder_init(ma_decoder_read_proc onRead, ma_decoder_seek_proc onSeek, void* pUserData, const ma_decoder_config* pConfig, ma_decoder* pDecoder); -MA_API ma_result ma_decoder_init_memory(const void* pData, size_t dataSize, const ma_decoder_config* pConfig, ma_decoder* pDecoder); -MA_API ma_result ma_decoder_init_vfs(ma_vfs* pVFS, const char* pFilePath, const ma_decoder_config* pConfig, ma_decoder* pDecoder); -MA_API ma_result ma_decoder_init_vfs_w(ma_vfs* pVFS, const wchar_t* pFilePath, const ma_decoder_config* pConfig, ma_decoder* pDecoder); -MA_API ma_result ma_decoder_init_file(const char* pFilePath, const ma_decoder_config* pConfig, ma_decoder* pDecoder); -MA_API ma_result ma_decoder_init_file_w(const wchar_t* pFilePath, const ma_decoder_config* pConfig, ma_decoder* pDecoder); - -/* -Uninitializes a decoder. -*/ -MA_API ma_result ma_decoder_uninit(ma_decoder* pDecoder); - -/* -Reads PCM frames from the given decoder. - -This is not thread safe without your own synchronization. -*/ -MA_API ma_result ma_decoder_read_pcm_frames(ma_decoder* pDecoder, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead); - -/* -Seeks to a PCM frame based on its absolute index. - -This is not thread safe without your own synchronization. -*/ -MA_API ma_result ma_decoder_seek_to_pcm_frame(ma_decoder* pDecoder, ma_uint64 frameIndex); - -/* -Retrieves the decoder's output data format. -*/ -MA_API ma_result ma_decoder_get_data_format(ma_decoder* pDecoder, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate, ma_channel* pChannelMap, size_t channelMapCap); - -/* -Retrieves the current position of the read cursor in PCM frames. -*/ -MA_API ma_result ma_decoder_get_cursor_in_pcm_frames(ma_decoder* pDecoder, ma_uint64* pCursor); - -/* -Retrieves the length of the decoder in PCM frames. - -Do not call this on streams of an undefined length, such as internet radio. - -If the length is unknown or an error occurs, 0 will be returned. - -This will always return 0 for Vorbis decoders. This is due to a limitation with stb_vorbis in push mode which is what miniaudio -uses internally. - -For MP3's, this will decode the entire file. Do not call this in time critical scenarios. - -This function is not thread safe without your own synchronization. -*/ -MA_API ma_result ma_decoder_get_length_in_pcm_frames(ma_decoder* pDecoder, ma_uint64* pLength); - -/* -Retrieves the number of frames that can be read before reaching the end. - -This calls `ma_decoder_get_length_in_pcm_frames()` so you need to be aware of the rules for that function, in -particular ensuring you do not call it on streams of an undefined length, such as internet radio. - -If the total length of the decoder cannot be retrieved, such as with Vorbis decoders, `MA_NOT_IMPLEMENTED` will be -returned. -*/ -MA_API ma_result ma_decoder_get_available_frames(ma_decoder* pDecoder, ma_uint64* pAvailableFrames); - -/* -Helper for opening and decoding a file into a heap allocated block of memory. Free the returned pointer with ma_free(). On input, -pConfig should be set to what you want. On output it will be set to what you got. -*/ -MA_API ma_result ma_decode_from_vfs(ma_vfs* pVFS, const char* pFilePath, ma_decoder_config* pConfig, ma_uint64* pFrameCountOut, void** ppPCMFramesOut); -MA_API ma_result ma_decode_file(const char* pFilePath, ma_decoder_config* pConfig, ma_uint64* pFrameCountOut, void** ppPCMFramesOut); -MA_API ma_result ma_decode_memory(const void* pData, size_t dataSize, ma_decoder_config* pConfig, ma_uint64* pFrameCountOut, void** ppPCMFramesOut); - -#endif /* MA_NO_DECODING */ - - -/************************************************************************************************************************************************************ - -Encoding -======== - -Encoders do not perform any format conversion for you. If your target format does not support the format, and error will be returned. - -************************************************************************************************************************************************************/ -#ifndef MA_NO_ENCODING -typedef struct ma_encoder ma_encoder; - -typedef ma_result (* ma_encoder_write_proc) (ma_encoder* pEncoder, const void* pBufferIn, size_t bytesToWrite, size_t* pBytesWritten); -typedef ma_result (* ma_encoder_seek_proc) (ma_encoder* pEncoder, ma_int64 offset, ma_seek_origin origin); -typedef ma_result (* ma_encoder_init_proc) (ma_encoder* pEncoder); -typedef void (* ma_encoder_uninit_proc) (ma_encoder* pEncoder); -typedef ma_result (* ma_encoder_write_pcm_frames_proc)(ma_encoder* pEncoder, const void* pFramesIn, ma_uint64 frameCount, ma_uint64* pFramesWritten); - -typedef struct -{ - ma_encoding_format encodingFormat; - ma_format format; - ma_uint32 channels; - ma_uint32 sampleRate; - ma_allocation_callbacks allocationCallbacks; -} ma_encoder_config; - -MA_API ma_encoder_config ma_encoder_config_init(ma_encoding_format encodingFormat, ma_format format, ma_uint32 channels, ma_uint32 sampleRate); - -struct ma_encoder -{ - ma_encoder_config config; - ma_encoder_write_proc onWrite; - ma_encoder_seek_proc onSeek; - ma_encoder_init_proc onInit; - ma_encoder_uninit_proc onUninit; - ma_encoder_write_pcm_frames_proc onWritePCMFrames; - void* pUserData; - void* pInternalEncoder; - union - { - struct - { - ma_vfs* pVFS; - ma_vfs_file file; - } vfs; - } data; -}; - -MA_API ma_result ma_encoder_init(ma_encoder_write_proc onWrite, ma_encoder_seek_proc onSeek, void* pUserData, const ma_encoder_config* pConfig, ma_encoder* pEncoder); -MA_API ma_result ma_encoder_init_vfs(ma_vfs* pVFS, const char* pFilePath, const ma_encoder_config* pConfig, ma_encoder* pEncoder); -MA_API ma_result ma_encoder_init_vfs_w(ma_vfs* pVFS, const wchar_t* pFilePath, const ma_encoder_config* pConfig, ma_encoder* pEncoder); -MA_API ma_result ma_encoder_init_file(const char* pFilePath, const ma_encoder_config* pConfig, ma_encoder* pEncoder); -MA_API ma_result ma_encoder_init_file_w(const wchar_t* pFilePath, const ma_encoder_config* pConfig, ma_encoder* pEncoder); -MA_API void ma_encoder_uninit(ma_encoder* pEncoder); -MA_API ma_result ma_encoder_write_pcm_frames(ma_encoder* pEncoder, const void* pFramesIn, ma_uint64 frameCount, ma_uint64* pFramesWritten); - -#endif /* MA_NO_ENCODING */ - - -/************************************************************************************************************************************************************ - -Generation - -************************************************************************************************************************************************************/ -#ifndef MA_NO_GENERATION -typedef enum -{ - ma_waveform_type_sine, - ma_waveform_type_square, - ma_waveform_type_triangle, - ma_waveform_type_sawtooth -} ma_waveform_type; - -typedef struct -{ - ma_format format; - ma_uint32 channels; - ma_uint32 sampleRate; - ma_waveform_type type; - double amplitude; - double frequency; -} ma_waveform_config; - -MA_API ma_waveform_config ma_waveform_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRate, ma_waveform_type type, double amplitude, double frequency); - -typedef struct -{ - ma_data_source_base ds; - ma_waveform_config config; - double advance; - double time; -} ma_waveform; - -MA_API ma_result ma_waveform_init(const ma_waveform_config* pConfig, ma_waveform* pWaveform); -MA_API void ma_waveform_uninit(ma_waveform* pWaveform); -MA_API ma_result ma_waveform_read_pcm_frames(ma_waveform* pWaveform, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead); -MA_API ma_result ma_waveform_seek_to_pcm_frame(ma_waveform* pWaveform, ma_uint64 frameIndex); -MA_API ma_result ma_waveform_set_amplitude(ma_waveform* pWaveform, double amplitude); -MA_API ma_result ma_waveform_set_frequency(ma_waveform* pWaveform, double frequency); -MA_API ma_result ma_waveform_set_type(ma_waveform* pWaveform, ma_waveform_type type); -MA_API ma_result ma_waveform_set_sample_rate(ma_waveform* pWaveform, ma_uint32 sampleRate); - -typedef struct -{ - ma_format format; - ma_uint32 channels; - ma_uint32 sampleRate; - double dutyCycle; - double amplitude; - double frequency; -} ma_pulsewave_config; - -MA_API ma_pulsewave_config ma_pulsewave_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRate, double dutyCycle, double amplitude, double frequency); - -typedef struct -{ - ma_waveform waveform; - ma_pulsewave_config config; -} ma_pulsewave; - -MA_API ma_result ma_pulsewave_init(const ma_pulsewave_config* pConfig, ma_pulsewave* pWaveform); -MA_API void ma_pulsewave_uninit(ma_pulsewave* pWaveform); -MA_API ma_result ma_pulsewave_read_pcm_frames(ma_pulsewave* pWaveform, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead); -MA_API ma_result ma_pulsewave_seek_to_pcm_frame(ma_pulsewave* pWaveform, ma_uint64 frameIndex); -MA_API ma_result ma_pulsewave_set_amplitude(ma_pulsewave* pWaveform, double amplitude); -MA_API ma_result ma_pulsewave_set_frequency(ma_pulsewave* pWaveform, double frequency); -MA_API ma_result ma_pulsewave_set_sample_rate(ma_pulsewave* pWaveform, ma_uint32 sampleRate); -MA_API ma_result ma_pulsewave_set_duty_cycle(ma_pulsewave* pWaveform, double dutyCycle); - -typedef enum -{ - ma_noise_type_white, - ma_noise_type_pink, - ma_noise_type_brownian -} ma_noise_type; - - -typedef struct -{ - ma_format format; - ma_uint32 channels; - ma_noise_type type; - ma_int32 seed; - double amplitude; - ma_bool32 duplicateChannels; -} ma_noise_config; - -MA_API ma_noise_config ma_noise_config_init(ma_format format, ma_uint32 channels, ma_noise_type type, ma_int32 seed, double amplitude); - -typedef struct -{ - ma_data_source_base ds; - ma_noise_config config; - ma_lcg lcg; - union - { - struct - { - double** bin; - double* accumulation; - ma_uint32* counter; - } pink; - struct - { - double* accumulation; - } brownian; - } state; - - /* Memory management. */ - void* _pHeap; - ma_bool32 _ownsHeap; -} ma_noise; - -MA_API ma_result ma_noise_get_heap_size(const ma_noise_config* pConfig, size_t* pHeapSizeInBytes); -MA_API ma_result ma_noise_init_preallocated(const ma_noise_config* pConfig, void* pHeap, ma_noise* pNoise); -MA_API ma_result ma_noise_init(const ma_noise_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_noise* pNoise); -MA_API void ma_noise_uninit(ma_noise* pNoise, const ma_allocation_callbacks* pAllocationCallbacks); -MA_API ma_result ma_noise_read_pcm_frames(ma_noise* pNoise, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead); -MA_API ma_result ma_noise_set_amplitude(ma_noise* pNoise, double amplitude); -MA_API ma_result ma_noise_set_seed(ma_noise* pNoise, ma_int32 seed); -MA_API ma_result ma_noise_set_type(ma_noise* pNoise, ma_noise_type type); - -#endif /* MA_NO_GENERATION */ - - - -/************************************************************************************************************************************************************ - -Resource Manager - -************************************************************************************************************************************************************/ -/* The resource manager cannot be enabled if there is no decoder. */ -#if !defined(MA_NO_RESOURCE_MANAGER) && defined(MA_NO_DECODING) -#define MA_NO_RESOURCE_MANAGER -#endif - -#ifndef MA_NO_RESOURCE_MANAGER -typedef struct ma_resource_manager ma_resource_manager; -typedef struct ma_resource_manager_data_buffer_node ma_resource_manager_data_buffer_node; -typedef struct ma_resource_manager_data_buffer ma_resource_manager_data_buffer; -typedef struct ma_resource_manager_data_stream ma_resource_manager_data_stream; -typedef struct ma_resource_manager_data_source ma_resource_manager_data_source; - -typedef enum -{ - MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_STREAM = 0x00000001, /* When set, does not load the entire data source in memory. Disk I/O will happen on job threads. */ - MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_DECODE = 0x00000002, /* Decode data before storing in memory. When set, decoding is done at the resource manager level rather than the mixing thread. Results in faster mixing, but higher memory usage. */ - MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_ASYNC = 0x00000004, /* When set, the resource manager will load the data source asynchronously. */ - MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_WAIT_INIT = 0x00000008, /* When set, waits for initialization of the underlying data source before returning from ma_resource_manager_data_source_init(). */ - MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_UNKNOWN_LENGTH = 0x00000010, /* Gives the resource manager a hint that the length of the data source is unknown and calling `ma_data_source_get_length_in_pcm_frames()` should be avoided. */ - MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_LOOPING = 0x00000020 /* When set, configures the data source to loop by default. */ -} ma_resource_manager_data_source_flags; - - -/* -Pipeline notifications used by the resource manager. Made up of both an async notification and a fence, both of which are optional. -*/ -typedef struct -{ - ma_async_notification* pNotification; - ma_fence* pFence; -} ma_resource_manager_pipeline_stage_notification; - -typedef struct -{ - ma_resource_manager_pipeline_stage_notification init; /* Initialization of the decoder. */ - ma_resource_manager_pipeline_stage_notification done; /* Decoding fully completed. */ -} ma_resource_manager_pipeline_notifications; - -MA_API ma_resource_manager_pipeline_notifications ma_resource_manager_pipeline_notifications_init(void); - - - -/* BEGIN BACKWARDS COMPATIBILITY */ -/* TODO: Remove this block in version 0.12. */ -#if 1 -#define ma_resource_manager_job ma_job -#define ma_resource_manager_job_init ma_job_init -#define MA_JOB_TYPE_RESOURCE_MANAGER_QUEUE_FLAG_NON_BLOCKING MA_JOB_QUEUE_FLAG_NON_BLOCKING -#define ma_resource_manager_job_queue_config ma_job_queue_config -#define ma_resource_manager_job_queue_config_init ma_job_queue_config_init -#define ma_resource_manager_job_queue ma_job_queue -#define ma_resource_manager_job_queue_get_heap_size ma_job_queue_get_heap_size -#define ma_resource_manager_job_queue_init_preallocated ma_job_queue_init_preallocated -#define ma_resource_manager_job_queue_init ma_job_queue_init -#define ma_resource_manager_job_queue_uninit ma_job_queue_uninit -#define ma_resource_manager_job_queue_post ma_job_queue_post -#define ma_resource_manager_job_queue_next ma_job_queue_next -#endif -/* END BACKWARDS COMPATIBILITY */ - - - - -/* Maximum job thread count will be restricted to this, but this may be removed later and replaced with a heap allocation thereby removing any limitation. */ -#ifndef MA_RESOURCE_MANAGER_MAX_JOB_THREAD_COUNT -#define MA_RESOURCE_MANAGER_MAX_JOB_THREAD_COUNT 64 -#endif - -typedef enum -{ - /* Indicates ma_resource_manager_next_job() should not block. Only valid when the job thread count is 0. */ - MA_RESOURCE_MANAGER_FLAG_NON_BLOCKING = 0x00000001, - - /* Disables any kind of multithreading. Implicitly enables MA_RESOURCE_MANAGER_FLAG_NON_BLOCKING. */ - MA_RESOURCE_MANAGER_FLAG_NO_THREADING = 0x00000002 -} ma_resource_manager_flags; - -typedef struct -{ - const char* pFilePath; - const wchar_t* pFilePathW; - const ma_resource_manager_pipeline_notifications* pNotifications; - ma_uint64 initialSeekPointInPCMFrames; - ma_uint64 rangeBegInPCMFrames; - ma_uint64 rangeEndInPCMFrames; - ma_uint64 loopPointBegInPCMFrames; - ma_uint64 loopPointEndInPCMFrames; - ma_uint32 flags; - ma_bool32 isLooping; /* Deprecated. Use the MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_LOOPING flag in `flags` instead. */ -} ma_resource_manager_data_source_config; - -MA_API ma_resource_manager_data_source_config ma_resource_manager_data_source_config_init(void); - - -typedef enum -{ - ma_resource_manager_data_supply_type_unknown = 0, /* Used for determining whether or the data supply has been initialized. */ - ma_resource_manager_data_supply_type_encoded, /* Data supply is an encoded buffer. Connector is ma_decoder. */ - ma_resource_manager_data_supply_type_decoded, /* Data supply is a decoded buffer. Connector is ma_audio_buffer. */ - ma_resource_manager_data_supply_type_decoded_paged /* Data supply is a linked list of decoded buffers. Connector is ma_paged_audio_buffer. */ -} ma_resource_manager_data_supply_type; - -typedef struct -{ - MA_ATOMIC(4, ma_resource_manager_data_supply_type) type; /* Read and written from different threads so needs to be accessed atomically. */ - union - { - struct - { - const void* pData; - size_t sizeInBytes; - } encoded; - struct - { - const void* pData; - ma_uint64 totalFrameCount; - ma_uint64 decodedFrameCount; - ma_format format; - ma_uint32 channels; - ma_uint32 sampleRate; - } decoded; - struct - { - ma_paged_audio_buffer_data data; - ma_uint64 decodedFrameCount; - ma_uint32 sampleRate; - } decodedPaged; - } backend; -} ma_resource_manager_data_supply; - -struct ma_resource_manager_data_buffer_node -{ - ma_uint32 hashedName32; /* The hashed name. This is the key. */ - ma_uint32 refCount; - MA_ATOMIC(4, ma_result) result; /* Result from asynchronous loading. When loading set to MA_BUSY. When fully loaded set to MA_SUCCESS. When deleting set to MA_UNAVAILABLE. */ - MA_ATOMIC(4, ma_uint32) executionCounter; /* For allocating execution orders for jobs. */ - MA_ATOMIC(4, ma_uint32) executionPointer; /* For managing the order of execution for asynchronous jobs relating to this object. Incremented as jobs complete processing. */ - ma_bool32 isDataOwnedByResourceManager; /* Set to true when the underlying data buffer was allocated the resource manager. Set to false if it is owned by the application (via ma_resource_manager_register_*()). */ - ma_resource_manager_data_supply data; - ma_resource_manager_data_buffer_node* pParent; - ma_resource_manager_data_buffer_node* pChildLo; - ma_resource_manager_data_buffer_node* pChildHi; -}; - -struct ma_resource_manager_data_buffer -{ - ma_data_source_base ds; /* Base data source. A data buffer is a data source. */ - ma_resource_manager* pResourceManager; /* A pointer to the resource manager that owns this buffer. */ - ma_resource_manager_data_buffer_node* pNode; /* The data node. This is reference counted and is what supplies the data. */ - ma_uint32 flags; /* The flags that were passed used to initialize the buffer. */ - MA_ATOMIC(4, ma_uint32) executionCounter; /* For allocating execution orders for jobs. */ - MA_ATOMIC(4, ma_uint32) executionPointer; /* For managing the order of execution for asynchronous jobs relating to this object. Incremented as jobs complete processing. */ - ma_uint64 seekTargetInPCMFrames; /* Only updated by the public API. Never written nor read from the job thread. */ - ma_bool32 seekToCursorOnNextRead; /* On the next read we need to seek to the frame cursor. */ - MA_ATOMIC(4, ma_result) result; /* Keeps track of a result of decoding. Set to MA_BUSY while the buffer is still loading. Set to MA_SUCCESS when loading is finished successfully. Otherwise set to some other code. */ - MA_ATOMIC(4, ma_bool32) isLooping; /* Can be read and written by different threads at the same time. Must be used atomically. */ - ma_atomic_bool32 isConnectorInitialized; /* Used for asynchronous loading to ensure we don't try to initialize the connector multiple times while waiting for the node to fully load. */ - union - { - ma_decoder decoder; /* Supply type is ma_resource_manager_data_supply_type_encoded */ - ma_audio_buffer buffer; /* Supply type is ma_resource_manager_data_supply_type_decoded */ - ma_paged_audio_buffer pagedBuffer; /* Supply type is ma_resource_manager_data_supply_type_decoded_paged */ - } connector; /* Connects this object to the node's data supply. */ -}; - -struct ma_resource_manager_data_stream -{ - ma_data_source_base ds; /* Base data source. A data stream is a data source. */ - ma_resource_manager* pResourceManager; /* A pointer to the resource manager that owns this data stream. */ - ma_uint32 flags; /* The flags that were passed used to initialize the stream. */ - ma_decoder decoder; /* Used for filling pages with data. This is only ever accessed by the job thread. The public API should never touch this. */ - ma_bool32 isDecoderInitialized; /* Required for determining whether or not the decoder should be uninitialized in MA_JOB_TYPE_RESOURCE_MANAGER_FREE_DATA_STREAM. */ - ma_uint64 totalLengthInPCMFrames; /* This is calculated when first loaded by the MA_JOB_TYPE_RESOURCE_MANAGER_LOAD_DATA_STREAM. */ - ma_uint32 relativeCursor; /* The playback cursor, relative to the current page. Only ever accessed by the public API. Never accessed by the job thread. */ - MA_ATOMIC(8, ma_uint64) absoluteCursor; /* The playback cursor, in absolute position starting from the start of the file. */ - ma_uint32 currentPageIndex; /* Toggles between 0 and 1. Index 0 is the first half of pPageData. Index 1 is the second half. Only ever accessed by the public API. Never accessed by the job thread. */ - MA_ATOMIC(4, ma_uint32) executionCounter; /* For allocating execution orders for jobs. */ - MA_ATOMIC(4, ma_uint32) executionPointer; /* For managing the order of execution for asynchronous jobs relating to this object. Incremented as jobs complete processing. */ - - /* Written by the public API, read by the job thread. */ - MA_ATOMIC(4, ma_bool32) isLooping; /* Whether or not the stream is looping. It's important to set the looping flag at the data stream level for smooth loop transitions. */ - - /* Written by the job thread, read by the public API. */ - void* pPageData; /* Buffer containing the decoded data of each page. Allocated once at initialization time. */ - MA_ATOMIC(4, ma_uint32) pageFrameCount[2]; /* The number of valid PCM frames in each page. Used to determine the last valid frame. */ - - /* Written and read by both the public API and the job thread. These must be atomic. */ - MA_ATOMIC(4, ma_result) result; /* Result from asynchronous loading. When loading set to MA_BUSY. When initialized set to MA_SUCCESS. When deleting set to MA_UNAVAILABLE. If an error occurs when loading, set to an error code. */ - MA_ATOMIC(4, ma_bool32) isDecoderAtEnd; /* Whether or not the decoder has reached the end. */ - MA_ATOMIC(4, ma_bool32) isPageValid[2]; /* Booleans to indicate whether or not a page is valid. Set to false by the public API, set to true by the job thread. Set to false as the pages are consumed, true when they are filled. */ - MA_ATOMIC(4, ma_bool32) seekCounter; /* When 0, no seeking is being performed. When > 0, a seek is being performed and reading should be delayed with MA_BUSY. */ -}; - -struct ma_resource_manager_data_source -{ - union - { - ma_resource_manager_data_buffer buffer; - ma_resource_manager_data_stream stream; - } backend; /* Must be the first item because we need the first item to be the data source callbacks for the buffer or stream. */ - - ma_uint32 flags; /* The flags that were passed in to ma_resource_manager_data_source_init(). */ - MA_ATOMIC(4, ma_uint32) executionCounter; /* For allocating execution orders for jobs. */ - MA_ATOMIC(4, ma_uint32) executionPointer; /* For managing the order of execution for asynchronous jobs relating to this object. Incremented as jobs complete processing. */ -}; - -typedef struct -{ - ma_allocation_callbacks allocationCallbacks; - ma_log* pLog; - ma_format decodedFormat; /* The decoded format to use. Set to ma_format_unknown (default) to use the file's native format. */ - ma_uint32 decodedChannels; /* The decoded channel count to use. Set to 0 (default) to use the file's native channel count. */ - ma_uint32 decodedSampleRate; /* the decoded sample rate to use. Set to 0 (default) to use the file's native sample rate. */ - ma_uint32 jobThreadCount; /* Set to 0 if you want to self-manage your job threads. Defaults to 1. */ - size_t jobThreadStackSize; - ma_uint32 jobQueueCapacity; /* The maximum number of jobs that can fit in the queue at a time. Defaults to MA_JOB_TYPE_RESOURCE_MANAGER_QUEUE_CAPACITY. Cannot be zero. */ - ma_uint32 flags; - ma_vfs* pVFS; /* Can be NULL in which case defaults will be used. */ - ma_decoding_backend_vtable** ppCustomDecodingBackendVTables; - ma_uint32 customDecodingBackendCount; - void* pCustomDecodingBackendUserData; -} ma_resource_manager_config; - -MA_API ma_resource_manager_config ma_resource_manager_config_init(void); - -struct ma_resource_manager -{ - ma_resource_manager_config config; - ma_resource_manager_data_buffer_node* pRootDataBufferNode; /* The root buffer in the binary tree. */ -#ifndef MA_NO_THREADING - ma_mutex dataBufferBSTLock; /* For synchronizing access to the data buffer binary tree. */ - ma_thread jobThreads[MA_RESOURCE_MANAGER_MAX_JOB_THREAD_COUNT]; /* The threads for executing jobs. */ -#endif - ma_job_queue jobQueue; /* Multi-consumer, multi-producer job queue for managing jobs for asynchronous decoding and streaming. */ - ma_default_vfs defaultVFS; /* Only used if a custom VFS is not specified. */ - ma_log log; /* Only used if no log was specified in the config. */ -}; - -/* Init. */ -MA_API ma_result ma_resource_manager_init(const ma_resource_manager_config* pConfig, ma_resource_manager* pResourceManager); -MA_API void ma_resource_manager_uninit(ma_resource_manager* pResourceManager); -MA_API ma_log* ma_resource_manager_get_log(ma_resource_manager* pResourceManager); - -/* Registration. */ -MA_API ma_result ma_resource_manager_register_file(ma_resource_manager* pResourceManager, const char* pFilePath, ma_uint32 flags); -MA_API ma_result ma_resource_manager_register_file_w(ma_resource_manager* pResourceManager, const wchar_t* pFilePath, ma_uint32 flags); -MA_API ma_result ma_resource_manager_register_decoded_data(ma_resource_manager* pResourceManager, const char* pName, const void* pData, ma_uint64 frameCount, ma_format format, ma_uint32 channels, ma_uint32 sampleRate); /* Does not copy. Increments the reference count if already exists and returns MA_SUCCESS. */ -MA_API ma_result ma_resource_manager_register_decoded_data_w(ma_resource_manager* pResourceManager, const wchar_t* pName, const void* pData, ma_uint64 frameCount, ma_format format, ma_uint32 channels, ma_uint32 sampleRate); -MA_API ma_result ma_resource_manager_register_encoded_data(ma_resource_manager* pResourceManager, const char* pName, const void* pData, size_t sizeInBytes); /* Does not copy. Increments the reference count if already exists and returns MA_SUCCESS. */ -MA_API ma_result ma_resource_manager_register_encoded_data_w(ma_resource_manager* pResourceManager, const wchar_t* pName, const void* pData, size_t sizeInBytes); -MA_API ma_result ma_resource_manager_unregister_file(ma_resource_manager* pResourceManager, const char* pFilePath); -MA_API ma_result ma_resource_manager_unregister_file_w(ma_resource_manager* pResourceManager, const wchar_t* pFilePath); -MA_API ma_result ma_resource_manager_unregister_data(ma_resource_manager* pResourceManager, const char* pName); -MA_API ma_result ma_resource_manager_unregister_data_w(ma_resource_manager* pResourceManager, const wchar_t* pName); - -/* Data Buffers. */ -MA_API ma_result ma_resource_manager_data_buffer_init_ex(ma_resource_manager* pResourceManager, const ma_resource_manager_data_source_config* pConfig, ma_resource_manager_data_buffer* pDataBuffer); -MA_API ma_result ma_resource_manager_data_buffer_init(ma_resource_manager* pResourceManager, const char* pFilePath, ma_uint32 flags, const ma_resource_manager_pipeline_notifications* pNotifications, ma_resource_manager_data_buffer* pDataBuffer); -MA_API ma_result ma_resource_manager_data_buffer_init_w(ma_resource_manager* pResourceManager, const wchar_t* pFilePath, ma_uint32 flags, const ma_resource_manager_pipeline_notifications* pNotifications, ma_resource_manager_data_buffer* pDataBuffer); -MA_API ma_result ma_resource_manager_data_buffer_init_copy(ma_resource_manager* pResourceManager, const ma_resource_manager_data_buffer* pExistingDataBuffer, ma_resource_manager_data_buffer* pDataBuffer); -MA_API ma_result ma_resource_manager_data_buffer_uninit(ma_resource_manager_data_buffer* pDataBuffer); -MA_API ma_result ma_resource_manager_data_buffer_read_pcm_frames(ma_resource_manager_data_buffer* pDataBuffer, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead); -MA_API ma_result ma_resource_manager_data_buffer_seek_to_pcm_frame(ma_resource_manager_data_buffer* pDataBuffer, ma_uint64 frameIndex); -MA_API ma_result ma_resource_manager_data_buffer_get_data_format(ma_resource_manager_data_buffer* pDataBuffer, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate, ma_channel* pChannelMap, size_t channelMapCap); -MA_API ma_result ma_resource_manager_data_buffer_get_cursor_in_pcm_frames(ma_resource_manager_data_buffer* pDataBuffer, ma_uint64* pCursor); -MA_API ma_result ma_resource_manager_data_buffer_get_length_in_pcm_frames(ma_resource_manager_data_buffer* pDataBuffer, ma_uint64* pLength); -MA_API ma_result ma_resource_manager_data_buffer_result(const ma_resource_manager_data_buffer* pDataBuffer); -MA_API ma_result ma_resource_manager_data_buffer_set_looping(ma_resource_manager_data_buffer* pDataBuffer, ma_bool32 isLooping); -MA_API ma_bool32 ma_resource_manager_data_buffer_is_looping(const ma_resource_manager_data_buffer* pDataBuffer); -MA_API ma_result ma_resource_manager_data_buffer_get_available_frames(ma_resource_manager_data_buffer* pDataBuffer, ma_uint64* pAvailableFrames); - -/* Data Streams. */ -MA_API ma_result ma_resource_manager_data_stream_init_ex(ma_resource_manager* pResourceManager, const ma_resource_manager_data_source_config* pConfig, ma_resource_manager_data_stream* pDataStream); -MA_API ma_result ma_resource_manager_data_stream_init(ma_resource_manager* pResourceManager, const char* pFilePath, ma_uint32 flags, const ma_resource_manager_pipeline_notifications* pNotifications, ma_resource_manager_data_stream* pDataStream); -MA_API ma_result ma_resource_manager_data_stream_init_w(ma_resource_manager* pResourceManager, const wchar_t* pFilePath, ma_uint32 flags, const ma_resource_manager_pipeline_notifications* pNotifications, ma_resource_manager_data_stream* pDataStream); -MA_API ma_result ma_resource_manager_data_stream_uninit(ma_resource_manager_data_stream* pDataStream); -MA_API ma_result ma_resource_manager_data_stream_read_pcm_frames(ma_resource_manager_data_stream* pDataStream, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead); -MA_API ma_result ma_resource_manager_data_stream_seek_to_pcm_frame(ma_resource_manager_data_stream* pDataStream, ma_uint64 frameIndex); -MA_API ma_result ma_resource_manager_data_stream_get_data_format(ma_resource_manager_data_stream* pDataStream, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate, ma_channel* pChannelMap, size_t channelMapCap); -MA_API ma_result ma_resource_manager_data_stream_get_cursor_in_pcm_frames(ma_resource_manager_data_stream* pDataStream, ma_uint64* pCursor); -MA_API ma_result ma_resource_manager_data_stream_get_length_in_pcm_frames(ma_resource_manager_data_stream* pDataStream, ma_uint64* pLength); -MA_API ma_result ma_resource_manager_data_stream_result(const ma_resource_manager_data_stream* pDataStream); -MA_API ma_result ma_resource_manager_data_stream_set_looping(ma_resource_manager_data_stream* pDataStream, ma_bool32 isLooping); -MA_API ma_bool32 ma_resource_manager_data_stream_is_looping(const ma_resource_manager_data_stream* pDataStream); -MA_API ma_result ma_resource_manager_data_stream_get_available_frames(ma_resource_manager_data_stream* pDataStream, ma_uint64* pAvailableFrames); - -/* Data Sources. */ -MA_API ma_result ma_resource_manager_data_source_init_ex(ma_resource_manager* pResourceManager, const ma_resource_manager_data_source_config* pConfig, ma_resource_manager_data_source* pDataSource); -MA_API ma_result ma_resource_manager_data_source_init(ma_resource_manager* pResourceManager, const char* pName, ma_uint32 flags, const ma_resource_manager_pipeline_notifications* pNotifications, ma_resource_manager_data_source* pDataSource); -MA_API ma_result ma_resource_manager_data_source_init_w(ma_resource_manager* pResourceManager, const wchar_t* pName, ma_uint32 flags, const ma_resource_manager_pipeline_notifications* pNotifications, ma_resource_manager_data_source* pDataSource); -MA_API ma_result ma_resource_manager_data_source_init_copy(ma_resource_manager* pResourceManager, const ma_resource_manager_data_source* pExistingDataSource, ma_resource_manager_data_source* pDataSource); -MA_API ma_result ma_resource_manager_data_source_uninit(ma_resource_manager_data_source* pDataSource); -MA_API ma_result ma_resource_manager_data_source_read_pcm_frames(ma_resource_manager_data_source* pDataSource, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead); -MA_API ma_result ma_resource_manager_data_source_seek_to_pcm_frame(ma_resource_manager_data_source* pDataSource, ma_uint64 frameIndex); -MA_API ma_result ma_resource_manager_data_source_get_data_format(ma_resource_manager_data_source* pDataSource, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate, ma_channel* pChannelMap, size_t channelMapCap); -MA_API ma_result ma_resource_manager_data_source_get_cursor_in_pcm_frames(ma_resource_manager_data_source* pDataSource, ma_uint64* pCursor); -MA_API ma_result ma_resource_manager_data_source_get_length_in_pcm_frames(ma_resource_manager_data_source* pDataSource, ma_uint64* pLength); -MA_API ma_result ma_resource_manager_data_source_result(const ma_resource_manager_data_source* pDataSource); -MA_API ma_result ma_resource_manager_data_source_set_looping(ma_resource_manager_data_source* pDataSource, ma_bool32 isLooping); -MA_API ma_bool32 ma_resource_manager_data_source_is_looping(const ma_resource_manager_data_source* pDataSource); -MA_API ma_result ma_resource_manager_data_source_get_available_frames(ma_resource_manager_data_source* pDataSource, ma_uint64* pAvailableFrames); - -/* Job management. */ -MA_API ma_result ma_resource_manager_post_job(ma_resource_manager* pResourceManager, const ma_job* pJob); -MA_API ma_result ma_resource_manager_post_job_quit(ma_resource_manager* pResourceManager); /* Helper for posting a quit job. */ -MA_API ma_result ma_resource_manager_next_job(ma_resource_manager* pResourceManager, ma_job* pJob); -MA_API ma_result ma_resource_manager_process_job(ma_resource_manager* pResourceManager, ma_job* pJob); /* DEPRECATED. Use ma_job_process(). Will be removed in version 0.12. */ -MA_API ma_result ma_resource_manager_process_next_job(ma_resource_manager* pResourceManager); /* Returns MA_CANCELLED if a MA_JOB_TYPE_QUIT job is found. In non-blocking mode, returns MA_NO_DATA_AVAILABLE if no jobs are available. */ -#endif /* MA_NO_RESOURCE_MANAGER */ - - - -/************************************************************************************************************************************************************ - -Node Graph - -************************************************************************************************************************************************************/ -#ifndef MA_NO_NODE_GRAPH -/* Must never exceed 254. */ -#ifndef MA_MAX_NODE_BUS_COUNT -#define MA_MAX_NODE_BUS_COUNT 254 -#endif - -/* Used internally by miniaudio for memory management. Must never exceed MA_MAX_NODE_BUS_COUNT. */ -#ifndef MA_MAX_NODE_LOCAL_BUS_COUNT -#define MA_MAX_NODE_LOCAL_BUS_COUNT 2 -#endif - -/* Use this when the bus count is determined by the node instance rather than the vtable. */ -#define MA_NODE_BUS_COUNT_UNKNOWN 255 - - -/* For some internal memory management of ma_node_graph. */ -typedef struct -{ - size_t offset; - size_t sizeInBytes; - unsigned char _data[1]; -} ma_stack; - - -typedef struct ma_node_graph ma_node_graph; -typedef void ma_node; - - -/* Node flags. */ -typedef enum -{ - MA_NODE_FLAG_PASSTHROUGH = 0x00000001, - MA_NODE_FLAG_CONTINUOUS_PROCESSING = 0x00000002, - MA_NODE_FLAG_ALLOW_NULL_INPUT = 0x00000004, - MA_NODE_FLAG_DIFFERENT_PROCESSING_RATES = 0x00000008, - MA_NODE_FLAG_SILENT_OUTPUT = 0x00000010 -} ma_node_flags; - - -/* The playback state of a node. Either started or stopped. */ -typedef enum -{ - ma_node_state_started = 0, - ma_node_state_stopped = 1 -} ma_node_state; - - -typedef struct -{ - /* - Extended processing callback. This callback is used for effects that process input and output - at different rates (i.e. they perform resampling). This is similar to the simple version, only - they take two separate frame counts: one for input, and one for output. - - On input, `pFrameCountOut` is equal to the capacity of the output buffer for each bus, whereas - `pFrameCountIn` will be equal to the number of PCM frames in each of the buffers in `ppFramesIn`. - - On output, set `pFrameCountOut` to the number of PCM frames that were actually output and set - `pFrameCountIn` to the number of input frames that were consumed. - */ - void (* onProcess)(ma_node* pNode, const float** ppFramesIn, ma_uint32* pFrameCountIn, float** ppFramesOut, ma_uint32* pFrameCountOut); - - /* - A callback for retrieving the number of input frames that are required to output the - specified number of output frames. You would only want to implement this when the node performs - resampling. This is optional, even for nodes that perform resampling, but it does offer a - small reduction in latency as it allows miniaudio to calculate the exact number of input frames - to read at a time instead of having to estimate. - */ - ma_result (* onGetRequiredInputFrameCount)(ma_node* pNode, ma_uint32 outputFrameCount, ma_uint32* pInputFrameCount); - - /* - The number of input buses. This is how many sub-buffers will be contained in the `ppFramesIn` - parameters of the callbacks above. - */ - ma_uint8 inputBusCount; - - /* - The number of output buses. This is how many sub-buffers will be contained in the `ppFramesOut` - parameters of the callbacks above. - */ - ma_uint8 outputBusCount; - - /* - Flags describing characteristics of the node. This is currently just a placeholder for some - ideas for later on. - */ - ma_uint32 flags; -} ma_node_vtable; - -typedef struct -{ - const ma_node_vtable* vtable; /* Should never be null. Initialization of the node will fail if so. */ - ma_node_state initialState; /* Defaults to ma_node_state_started. */ - ma_uint32 inputBusCount; /* Only used if the vtable specifies an input bus count of `MA_NODE_BUS_COUNT_UNKNOWN`, otherwise must be set to `MA_NODE_BUS_COUNT_UNKNOWN` (default). */ - ma_uint32 outputBusCount; /* Only used if the vtable specifies an output bus count of `MA_NODE_BUS_COUNT_UNKNOWN`, otherwise be set to `MA_NODE_BUS_COUNT_UNKNOWN` (default). */ - const ma_uint32* pInputChannels; /* The number of elements are determined by the input bus count as determined by the vtable, or `inputBusCount` if the vtable specifies `MA_NODE_BUS_COUNT_UNKNOWN`. */ - const ma_uint32* pOutputChannels; /* The number of elements are determined by the output bus count as determined by the vtable, or `outputBusCount` if the vtable specifies `MA_NODE_BUS_COUNT_UNKNOWN`. */ -} ma_node_config; - -MA_API ma_node_config ma_node_config_init(void); - - -/* -A node has multiple output buses. An output bus is attached to an input bus as an item in a linked -list. Think of the input bus as a linked list, with the output bus being an item in that list. -*/ -typedef struct ma_node_output_bus ma_node_output_bus; -struct ma_node_output_bus -{ - /* Immutable. */ - ma_node* pNode; /* The node that owns this output bus. The input node. Will be null for dummy head and tail nodes. */ - ma_uint8 outputBusIndex; /* The index of the output bus on pNode that this output bus represents. */ - ma_uint8 channels; /* The number of channels in the audio stream for this bus. */ - - /* Mutable via multiple threads. Must be used atomically. The weird ordering here is for packing reasons. */ - ma_uint8 inputNodeInputBusIndex; /* The index of the input bus on the input. Required for detaching. Will only be used within the spinlock so does not need to be atomic. */ - MA_ATOMIC(4, ma_uint32) flags; /* Some state flags for tracking the read state of the output buffer. A combination of MA_NODE_OUTPUT_BUS_FLAG_*. */ - MA_ATOMIC(4, ma_uint32) refCount; /* Reference count for some thread-safety when detaching. */ - MA_ATOMIC(4, ma_bool32) isAttached; /* This is used to prevent iteration of nodes that are in the middle of being detached. Used for thread safety. */ - MA_ATOMIC(4, ma_spinlock) lock; /* Unfortunate lock, but significantly simplifies the implementation. Required for thread-safe attaching and detaching. */ - MA_ATOMIC(4, float) volume; /* Linear. */ - MA_ATOMIC(MA_SIZEOF_PTR, ma_node_output_bus*) pNext; /* If null, it's the tail node or detached. */ - MA_ATOMIC(MA_SIZEOF_PTR, ma_node_output_bus*) pPrev; /* If null, it's the head node or detached. */ - MA_ATOMIC(MA_SIZEOF_PTR, ma_node*) pInputNode; /* The node that this output bus is attached to. Required for detaching. */ -}; - -/* -A node has multiple input buses. The output buses of a node are connecting to the input busses of -another. An input bus is essentially just a linked list of output buses. -*/ -typedef struct ma_node_input_bus ma_node_input_bus; -struct ma_node_input_bus -{ - /* Mutable via multiple threads. */ - ma_node_output_bus head; /* Dummy head node for simplifying some lock-free thread-safety stuff. */ - MA_ATOMIC(4, ma_uint32) nextCounter; /* This is used to determine whether or not the input bus is finding the next node in the list. Used for thread safety when detaching output buses. */ - MA_ATOMIC(4, ma_spinlock) lock; /* Unfortunate lock, but significantly simplifies the implementation. Required for thread-safe attaching and detaching. */ - - /* Set once at startup. */ - ma_uint8 channels; /* The number of channels in the audio stream for this bus. */ -}; - - -typedef struct ma_node_base ma_node_base; -struct ma_node_base -{ - /* These variables are set once at startup. */ - ma_node_graph* pNodeGraph; /* The graph this node belongs to. */ - const ma_node_vtable* vtable; - ma_uint32 inputBusCount; - ma_uint32 outputBusCount; - ma_node_input_bus* pInputBuses; - ma_node_output_bus* pOutputBuses; - float* pCachedData; /* Allocated on the heap. Fixed size. Needs to be stored on the heap because reading from output buses is done in separate function calls. */ - ma_uint16 cachedDataCapInFramesPerBus; /* The capacity of the input data cache in frames, per bus. */ - - /* These variables are read and written only from the audio thread. */ - ma_uint16 cachedFrameCountOut; - ma_uint16 cachedFrameCountIn; - ma_uint16 consumedFrameCountIn; - - /* These variables are read and written between different threads. */ - MA_ATOMIC(4, ma_node_state) state; /* When set to stopped, nothing will be read, regardless of the times in stateTimes. */ - MA_ATOMIC(8, ma_uint64) stateTimes[2]; /* Indexed by ma_node_state. Specifies the time based on the global clock that a node should be considered to be in the relevant state. */ - MA_ATOMIC(8, ma_uint64) localTime; /* The node's local clock. This is just a running sum of the number of output frames that have been processed. Can be modified by any thread with `ma_node_set_time()`. */ - - /* Memory management. */ - ma_node_input_bus _inputBuses[MA_MAX_NODE_LOCAL_BUS_COUNT]; - ma_node_output_bus _outputBuses[MA_MAX_NODE_LOCAL_BUS_COUNT]; - void* _pHeap; /* A heap allocation for internal use only. pInputBuses and/or pOutputBuses will point to this if the bus count exceeds MA_MAX_NODE_LOCAL_BUS_COUNT. */ - ma_bool32 _ownsHeap; /* If set to true, the node owns the heap allocation and _pHeap will be freed in ma_node_uninit(). */ -}; - -MA_API ma_result ma_node_get_heap_size(ma_node_graph* pNodeGraph, const ma_node_config* pConfig, size_t* pHeapSizeInBytes); -MA_API ma_result ma_node_init_preallocated(ma_node_graph* pNodeGraph, const ma_node_config* pConfig, void* pHeap, ma_node* pNode); -MA_API ma_result ma_node_init(ma_node_graph* pNodeGraph, const ma_node_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_node* pNode); -MA_API void ma_node_uninit(ma_node* pNode, const ma_allocation_callbacks* pAllocationCallbacks); -MA_API ma_node_graph* ma_node_get_node_graph(const ma_node* pNode); -MA_API ma_uint32 ma_node_get_input_bus_count(const ma_node* pNode); -MA_API ma_uint32 ma_node_get_output_bus_count(const ma_node* pNode); -MA_API ma_uint32 ma_node_get_input_channels(const ma_node* pNode, ma_uint32 inputBusIndex); -MA_API ma_uint32 ma_node_get_output_channels(const ma_node* pNode, ma_uint32 outputBusIndex); -MA_API ma_result ma_node_attach_output_bus(ma_node* pNode, ma_uint32 outputBusIndex, ma_node* pOtherNode, ma_uint32 otherNodeInputBusIndex); -MA_API ma_result ma_node_detach_output_bus(ma_node* pNode, ma_uint32 outputBusIndex); -MA_API ma_result ma_node_detach_all_output_buses(ma_node* pNode); -MA_API ma_result ma_node_set_output_bus_volume(ma_node* pNode, ma_uint32 outputBusIndex, float volume); -MA_API float ma_node_get_output_bus_volume(const ma_node* pNode, ma_uint32 outputBusIndex); -MA_API ma_result ma_node_set_state(ma_node* pNode, ma_node_state state); -MA_API ma_node_state ma_node_get_state(const ma_node* pNode); -MA_API ma_result ma_node_set_state_time(ma_node* pNode, ma_node_state state, ma_uint64 globalTime); -MA_API ma_uint64 ma_node_get_state_time(const ma_node* pNode, ma_node_state state); -MA_API ma_node_state ma_node_get_state_by_time(const ma_node* pNode, ma_uint64 globalTime); -MA_API ma_node_state ma_node_get_state_by_time_range(const ma_node* pNode, ma_uint64 globalTimeBeg, ma_uint64 globalTimeEnd); -MA_API ma_uint64 ma_node_get_time(const ma_node* pNode); -MA_API ma_result ma_node_set_time(ma_node* pNode, ma_uint64 localTime); - - -typedef struct -{ - ma_uint32 channels; - ma_uint32 processingSizeInFrames; /* This is the preferred processing size for node processing callbacks unless overridden by a node itself. Can be 0 in which case it will be based on the frame count passed into ma_node_graph_read_pcm_frames(), but will not be well defined. */ - size_t preMixStackSizeInBytes; /* Defaults to 512KB per channel. Reducing this will save memory, but the depth of your node graph will be more restricted. */ -} ma_node_graph_config; - -MA_API ma_node_graph_config ma_node_graph_config_init(ma_uint32 channels); - - -struct ma_node_graph -{ - /* Immutable. */ - ma_node_base base; /* The node graph itself is a node so it can be connected as an input to different node graph. This has zero inputs and calls ma_node_graph_read_pcm_frames() to generate it's output. */ - ma_node_base endpoint; /* Special node that all nodes eventually connect to. Data is read from this node in ma_node_graph_read_pcm_frames(). */ - float* pProcessingCache; /* This will be allocated when processingSizeInFrames is non-zero. This is needed because ma_node_graph_read_pcm_frames() can be called with a variable number of frames, and we may need to do some buffering in situations where the caller requests a frame count that's not a multiple of processingSizeInFrames. */ - ma_uint32 processingCacheFramesRemaining; - ma_uint32 processingSizeInFrames; - - /* Read and written by multiple threads. */ - MA_ATOMIC(4, ma_bool32) isReading; - - /* Modified only by the audio thread. */ - ma_stack* pPreMixStack; -}; - -MA_API ma_result ma_node_graph_init(const ma_node_graph_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_node_graph* pNodeGraph); -MA_API void ma_node_graph_uninit(ma_node_graph* pNodeGraph, const ma_allocation_callbacks* pAllocationCallbacks); -MA_API ma_node* ma_node_graph_get_endpoint(ma_node_graph* pNodeGraph); -MA_API ma_result ma_node_graph_read_pcm_frames(ma_node_graph* pNodeGraph, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead); -MA_API ma_uint32 ma_node_graph_get_channels(const ma_node_graph* pNodeGraph); -MA_API ma_uint64 ma_node_graph_get_time(const ma_node_graph* pNodeGraph); -MA_API ma_result ma_node_graph_set_time(ma_node_graph* pNodeGraph, ma_uint64 globalTime); - - - -/* Data source node. 0 input buses, 1 output bus. Used for reading from a data source. */ -typedef struct -{ - ma_node_config nodeConfig; - ma_data_source* pDataSource; -} ma_data_source_node_config; - -MA_API ma_data_source_node_config ma_data_source_node_config_init(ma_data_source* pDataSource); - - -typedef struct -{ - ma_node_base base; - ma_data_source* pDataSource; -} ma_data_source_node; - -MA_API ma_result ma_data_source_node_init(ma_node_graph* pNodeGraph, const ma_data_source_node_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_data_source_node* pDataSourceNode); -MA_API void ma_data_source_node_uninit(ma_data_source_node* pDataSourceNode, const ma_allocation_callbacks* pAllocationCallbacks); -MA_API ma_result ma_data_source_node_set_looping(ma_data_source_node* pDataSourceNode, ma_bool32 isLooping); -MA_API ma_bool32 ma_data_source_node_is_looping(ma_data_source_node* pDataSourceNode); - - -/* Splitter Node. 1 input, many outputs. Used for splitting/copying a stream so it can be as input into two separate output nodes. */ -typedef struct -{ - ma_node_config nodeConfig; - ma_uint32 channels; - ma_uint32 outputBusCount; -} ma_splitter_node_config; - -MA_API ma_splitter_node_config ma_splitter_node_config_init(ma_uint32 channels); - - -typedef struct -{ - ma_node_base base; -} ma_splitter_node; - -MA_API ma_result ma_splitter_node_init(ma_node_graph* pNodeGraph, const ma_splitter_node_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_splitter_node* pSplitterNode); -MA_API void ma_splitter_node_uninit(ma_splitter_node* pSplitterNode, const ma_allocation_callbacks* pAllocationCallbacks); - - -/* -Biquad Node -*/ -typedef struct -{ - ma_node_config nodeConfig; - ma_biquad_config biquad; -} ma_biquad_node_config; - -MA_API ma_biquad_node_config ma_biquad_node_config_init(ma_uint32 channels, float b0, float b1, float b2, float a0, float a1, float a2); - - -typedef struct -{ - ma_node_base baseNode; - ma_biquad biquad; -} ma_biquad_node; - -MA_API ma_result ma_biquad_node_init(ma_node_graph* pNodeGraph, const ma_biquad_node_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_biquad_node* pNode); -MA_API ma_result ma_biquad_node_reinit(const ma_biquad_config* pConfig, ma_biquad_node* pNode); -MA_API void ma_biquad_node_uninit(ma_biquad_node* pNode, const ma_allocation_callbacks* pAllocationCallbacks); - - -/* -Low Pass Filter Node -*/ -typedef struct -{ - ma_node_config nodeConfig; - ma_lpf_config lpf; -} ma_lpf_node_config; - -MA_API ma_lpf_node_config ma_lpf_node_config_init(ma_uint32 channels, ma_uint32 sampleRate, double cutoffFrequency, ma_uint32 order); - - -typedef struct -{ - ma_node_base baseNode; - ma_lpf lpf; -} ma_lpf_node; - -MA_API ma_result ma_lpf_node_init(ma_node_graph* pNodeGraph, const ma_lpf_node_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_lpf_node* pNode); -MA_API ma_result ma_lpf_node_reinit(const ma_lpf_config* pConfig, ma_lpf_node* pNode); -MA_API void ma_lpf_node_uninit(ma_lpf_node* pNode, const ma_allocation_callbacks* pAllocationCallbacks); - - -/* -High Pass Filter Node -*/ -typedef struct -{ - ma_node_config nodeConfig; - ma_hpf_config hpf; -} ma_hpf_node_config; - -MA_API ma_hpf_node_config ma_hpf_node_config_init(ma_uint32 channels, ma_uint32 sampleRate, double cutoffFrequency, ma_uint32 order); - - -typedef struct -{ - ma_node_base baseNode; - ma_hpf hpf; -} ma_hpf_node; - -MA_API ma_result ma_hpf_node_init(ma_node_graph* pNodeGraph, const ma_hpf_node_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_hpf_node* pNode); -MA_API ma_result ma_hpf_node_reinit(const ma_hpf_config* pConfig, ma_hpf_node* pNode); -MA_API void ma_hpf_node_uninit(ma_hpf_node* pNode, const ma_allocation_callbacks* pAllocationCallbacks); - - -/* -Band Pass Filter Node -*/ -typedef struct -{ - ma_node_config nodeConfig; - ma_bpf_config bpf; -} ma_bpf_node_config; - -MA_API ma_bpf_node_config ma_bpf_node_config_init(ma_uint32 channels, ma_uint32 sampleRate, double cutoffFrequency, ma_uint32 order); - - -typedef struct -{ - ma_node_base baseNode; - ma_bpf bpf; -} ma_bpf_node; - -MA_API ma_result ma_bpf_node_init(ma_node_graph* pNodeGraph, const ma_bpf_node_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_bpf_node* pNode); -MA_API ma_result ma_bpf_node_reinit(const ma_bpf_config* pConfig, ma_bpf_node* pNode); -MA_API void ma_bpf_node_uninit(ma_bpf_node* pNode, const ma_allocation_callbacks* pAllocationCallbacks); - - -/* -Notching Filter Node -*/ -typedef struct -{ - ma_node_config nodeConfig; - ma_notch_config notch; -} ma_notch_node_config; - -MA_API ma_notch_node_config ma_notch_node_config_init(ma_uint32 channels, ma_uint32 sampleRate, double q, double frequency); - - -typedef struct -{ - ma_node_base baseNode; - ma_notch2 notch; -} ma_notch_node; - -MA_API ma_result ma_notch_node_init(ma_node_graph* pNodeGraph, const ma_notch_node_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_notch_node* pNode); -MA_API ma_result ma_notch_node_reinit(const ma_notch_config* pConfig, ma_notch_node* pNode); -MA_API void ma_notch_node_uninit(ma_notch_node* pNode, const ma_allocation_callbacks* pAllocationCallbacks); - - -/* -Peaking Filter Node -*/ -typedef struct -{ - ma_node_config nodeConfig; - ma_peak_config peak; -} ma_peak_node_config; - -MA_API ma_peak_node_config ma_peak_node_config_init(ma_uint32 channels, ma_uint32 sampleRate, double gainDB, double q, double frequency); - - -typedef struct -{ - ma_node_base baseNode; - ma_peak2 peak; -} ma_peak_node; - -MA_API ma_result ma_peak_node_init(ma_node_graph* pNodeGraph, const ma_peak_node_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_peak_node* pNode); -MA_API ma_result ma_peak_node_reinit(const ma_peak_config* pConfig, ma_peak_node* pNode); -MA_API void ma_peak_node_uninit(ma_peak_node* pNode, const ma_allocation_callbacks* pAllocationCallbacks); - - -/* -Low Shelf Filter Node -*/ -typedef struct -{ - ma_node_config nodeConfig; - ma_loshelf_config loshelf; -} ma_loshelf_node_config; - -MA_API ma_loshelf_node_config ma_loshelf_node_config_init(ma_uint32 channels, ma_uint32 sampleRate, double gainDB, double q, double frequency); - - -typedef struct -{ - ma_node_base baseNode; - ma_loshelf2 loshelf; -} ma_loshelf_node; - -MA_API ma_result ma_loshelf_node_init(ma_node_graph* pNodeGraph, const ma_loshelf_node_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_loshelf_node* pNode); -MA_API ma_result ma_loshelf_node_reinit(const ma_loshelf_config* pConfig, ma_loshelf_node* pNode); -MA_API void ma_loshelf_node_uninit(ma_loshelf_node* pNode, const ma_allocation_callbacks* pAllocationCallbacks); - - -/* -High Shelf Filter Node -*/ -typedef struct -{ - ma_node_config nodeConfig; - ma_hishelf_config hishelf; -} ma_hishelf_node_config; - -MA_API ma_hishelf_node_config ma_hishelf_node_config_init(ma_uint32 channels, ma_uint32 sampleRate, double gainDB, double q, double frequency); - - -typedef struct -{ - ma_node_base baseNode; - ma_hishelf2 hishelf; -} ma_hishelf_node; - -MA_API ma_result ma_hishelf_node_init(ma_node_graph* pNodeGraph, const ma_hishelf_node_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_hishelf_node* pNode); -MA_API ma_result ma_hishelf_node_reinit(const ma_hishelf_config* pConfig, ma_hishelf_node* pNode); -MA_API void ma_hishelf_node_uninit(ma_hishelf_node* pNode, const ma_allocation_callbacks* pAllocationCallbacks); - - -typedef struct -{ - ma_node_config nodeConfig; - ma_delay_config delay; -} ma_delay_node_config; - -MA_API ma_delay_node_config ma_delay_node_config_init(ma_uint32 channels, ma_uint32 sampleRate, ma_uint32 delayInFrames, float decay); - - -typedef struct -{ - ma_node_base baseNode; - ma_delay delay; -} ma_delay_node; - -MA_API ma_result ma_delay_node_init(ma_node_graph* pNodeGraph, const ma_delay_node_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_delay_node* pDelayNode); -MA_API void ma_delay_node_uninit(ma_delay_node* pDelayNode, const ma_allocation_callbacks* pAllocationCallbacks); -MA_API void ma_delay_node_set_wet(ma_delay_node* pDelayNode, float value); -MA_API float ma_delay_node_get_wet(const ma_delay_node* pDelayNode); -MA_API void ma_delay_node_set_dry(ma_delay_node* pDelayNode, float value); -MA_API float ma_delay_node_get_dry(const ma_delay_node* pDelayNode); -MA_API void ma_delay_node_set_decay(ma_delay_node* pDelayNode, float value); -MA_API float ma_delay_node_get_decay(const ma_delay_node* pDelayNode); -#endif /* MA_NO_NODE_GRAPH */ - - -/* SECTION: miniaudio_engine.h */ -/************************************************************************************************************************************************************ - -Engine - -************************************************************************************************************************************************************/ -#if !defined(MA_NO_ENGINE) && !defined(MA_NO_NODE_GRAPH) -typedef struct ma_engine ma_engine; -typedef struct ma_sound ma_sound; - - -/* Sound flags. */ -typedef enum -{ - /* Resource manager flags. */ - MA_SOUND_FLAG_STREAM = 0x00000001, /* MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_STREAM */ - MA_SOUND_FLAG_DECODE = 0x00000002, /* MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_DECODE */ - MA_SOUND_FLAG_ASYNC = 0x00000004, /* MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_ASYNC */ - MA_SOUND_FLAG_WAIT_INIT = 0x00000008, /* MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_WAIT_INIT */ - MA_SOUND_FLAG_UNKNOWN_LENGTH = 0x00000010, /* MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_UNKNOWN_LENGTH */ - MA_SOUND_FLAG_LOOPING = 0x00000020, /* MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_LOOPING */ - - /* ma_sound specific flags. */ - MA_SOUND_FLAG_NO_DEFAULT_ATTACHMENT = 0x00001000, /* Do not attach to the endpoint by default. Useful for when setting up nodes in a complex graph system. */ - MA_SOUND_FLAG_NO_PITCH = 0x00002000, /* Disable pitch shifting with ma_sound_set_pitch() and ma_sound_group_set_pitch(). This is an optimization. */ - MA_SOUND_FLAG_NO_SPATIALIZATION = 0x00004000 /* Disable spatialization. */ -} ma_sound_flags; - -#ifndef MA_ENGINE_MAX_LISTENERS -#define MA_ENGINE_MAX_LISTENERS 4 -#endif - -#define MA_LISTENER_INDEX_CLOSEST ((ma_uint8)-1) - -typedef enum -{ - ma_engine_node_type_sound, - ma_engine_node_type_group -} ma_engine_node_type; - -typedef struct -{ - ma_engine* pEngine; - ma_engine_node_type type; - ma_uint32 channelsIn; - ma_uint32 channelsOut; - ma_uint32 sampleRate; /* Only used when the type is set to ma_engine_node_type_sound. */ - ma_uint32 volumeSmoothTimeInPCMFrames; /* The number of frames to smooth over volume changes. Defaults to 0 in which case no smoothing is used. */ - ma_mono_expansion_mode monoExpansionMode; - ma_bool8 isPitchDisabled; /* Pitching can be explicitly disabled with MA_SOUND_FLAG_NO_PITCH to optimize processing. */ - ma_bool8 isSpatializationDisabled; /* Spatialization can be explicitly disabled with MA_SOUND_FLAG_NO_SPATIALIZATION. */ - ma_uint8 pinnedListenerIndex; /* The index of the listener this node should always use for spatialization. If set to MA_LISTENER_INDEX_CLOSEST the engine will use the closest listener. */ -} ma_engine_node_config; - -MA_API ma_engine_node_config ma_engine_node_config_init(ma_engine* pEngine, ma_engine_node_type type, ma_uint32 flags); - - -/* Base node object for both ma_sound and ma_sound_group. */ -typedef struct -{ - ma_node_base baseNode; /* Must be the first member for compatibility with the ma_node API. */ - ma_engine* pEngine; /* A pointer to the engine. Set based on the value from the config. */ - ma_uint32 sampleRate; /* The sample rate of the input data. For sounds backed by a data source, this will be the data source's sample rate. Otherwise it'll be the engine's sample rate. */ - ma_uint32 volumeSmoothTimeInPCMFrames; - ma_mono_expansion_mode monoExpansionMode; - ma_fader fader; - ma_linear_resampler resampler; /* For pitch shift. */ - ma_spatializer spatializer; - ma_panner panner; - ma_gainer volumeGainer; /* This will only be used if volumeSmoothTimeInPCMFrames is > 0. */ - ma_atomic_float volume; /* Defaults to 1. */ - MA_ATOMIC(4, float) pitch; - float oldPitch; /* For determining whether or not the resampler needs to be updated to reflect the new pitch. The resampler will be updated on the mixing thread. */ - float oldDopplerPitch; /* For determining whether or not the resampler needs to be updated to take a new doppler pitch into account. */ - MA_ATOMIC(4, ma_bool32) isPitchDisabled; /* When set to true, pitching will be disabled which will allow the resampler to be bypassed to save some computation. */ - MA_ATOMIC(4, ma_bool32) isSpatializationDisabled; /* Set to false by default. When set to false, will not have spatialisation applied. */ - MA_ATOMIC(4, ma_uint32) pinnedListenerIndex; /* The index of the listener this node should always use for spatialization. If set to MA_LISTENER_INDEX_CLOSEST the engine will use the closest listener. */ - - /* When setting a fade, it's not done immediately in ma_sound_set_fade(). It's deferred to the audio thread which means we need to store the settings here. */ - struct - { - ma_atomic_float volumeBeg; - ma_atomic_float volumeEnd; - ma_atomic_uint64 fadeLengthInFrames; /* <-- Defaults to (~(ma_uint64)0) which is used to indicate that no fade should be applied. */ - ma_atomic_uint64 absoluteGlobalTimeInFrames; /* <-- The time to start the fade. */ - } fadeSettings; - - /* Memory management. */ - ma_bool8 _ownsHeap; - void* _pHeap; -} ma_engine_node; - -MA_API ma_result ma_engine_node_get_heap_size(const ma_engine_node_config* pConfig, size_t* pHeapSizeInBytes); -MA_API ma_result ma_engine_node_init_preallocated(const ma_engine_node_config* pConfig, void* pHeap, ma_engine_node* pEngineNode); -MA_API ma_result ma_engine_node_init(const ma_engine_node_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_engine_node* pEngineNode); -MA_API void ma_engine_node_uninit(ma_engine_node* pEngineNode, const ma_allocation_callbacks* pAllocationCallbacks); - - -#define MA_SOUND_SOURCE_CHANNEL_COUNT 0xFFFFFFFF - -/* Callback for when a sound reaches the end. */ -typedef void (* ma_sound_end_proc)(void* pUserData, ma_sound* pSound); - -typedef struct -{ - const char* pFilePath; /* Set this to load from the resource manager. */ - const wchar_t* pFilePathW; /* Set this to load from the resource manager. */ - ma_data_source* pDataSource; /* Set this to load from an existing data source. */ - ma_node* pInitialAttachment; /* If set, the sound will be attached to an input of this node. This can be set to a ma_sound. If set to NULL, the sound will be attached directly to the endpoint unless MA_SOUND_FLAG_NO_DEFAULT_ATTACHMENT is set in `flags`. */ - ma_uint32 initialAttachmentInputBusIndex; /* The index of the input bus of pInitialAttachment to attach the sound to. */ - ma_uint32 channelsIn; /* Ignored if using a data source as input (the data source's channel count will be used always). Otherwise, setting to 0 will cause the engine's channel count to be used. */ - ma_uint32 channelsOut; /* Set this to 0 (default) to use the engine's channel count. Set to MA_SOUND_SOURCE_CHANNEL_COUNT to use the data source's channel count (only used if using a data source as input). */ - ma_mono_expansion_mode monoExpansionMode; /* Controls how the mono channel should be expanded to other channels when spatialization is disabled on a sound. */ - ma_uint32 flags; /* A combination of MA_SOUND_FLAG_* flags. */ - ma_uint32 volumeSmoothTimeInPCMFrames; /* The number of frames to smooth over volume changes. Defaults to 0 in which case no smoothing is used. */ - ma_uint64 initialSeekPointInPCMFrames; /* Initializes the sound such that it's seeked to this location by default. */ - ma_uint64 rangeBegInPCMFrames; - ma_uint64 rangeEndInPCMFrames; - ma_uint64 loopPointBegInPCMFrames; - ma_uint64 loopPointEndInPCMFrames; - ma_sound_end_proc endCallback; /* Fired when the sound reaches the end. Will be fired from the audio thread. Do not restart, uninitialize or otherwise change the state of the sound from here. Instead fire an event or set a variable to indicate to a different thread to change the start of the sound. Will not be fired in response to a scheduled stop with ma_sound_set_stop_time_*(). */ - void* pEndCallbackUserData; -#ifndef MA_NO_RESOURCE_MANAGER - ma_resource_manager_pipeline_notifications initNotifications; -#endif - ma_fence* pDoneFence; /* Deprecated. Use initNotifications instead. Released when the resource manager has finished decoding the entire sound. Not used with streams. */ - ma_bool32 isLooping; /* Deprecated. Use the MA_SOUND_FLAG_LOOPING flag in `flags` instead. */ -} ma_sound_config; - -MA_API ma_sound_config ma_sound_config_init(void); /* Deprecated. Will be removed in version 0.12. Use ma_sound_config_2() instead. */ -MA_API ma_sound_config ma_sound_config_init_2(ma_engine* pEngine); /* Will be renamed to ma_sound_config_init() in version 0.12. */ - -struct ma_sound -{ - ma_engine_node engineNode; /* Must be the first member for compatibility with the ma_node API. */ - ma_data_source* pDataSource; - MA_ATOMIC(8, ma_uint64) seekTarget; /* The PCM frame index to seek to in the mixing thread. Set to (~(ma_uint64)0) to not perform any seeking. */ - MA_ATOMIC(4, ma_bool32) atEnd; - ma_sound_end_proc endCallback; - void* pEndCallbackUserData; - ma_bool8 ownsDataSource; - - /* - We're declaring a resource manager data source object here to save us a malloc when loading a - sound via the resource manager, which I *think* will be the most common scenario. - */ -#ifndef MA_NO_RESOURCE_MANAGER - ma_resource_manager_data_source* pResourceManagerDataSource; -#endif -}; - -/* Structure specifically for sounds played with ma_engine_play_sound(). Making this a separate structure to reduce overhead. */ -typedef struct ma_sound_inlined ma_sound_inlined; -struct ma_sound_inlined -{ - ma_sound sound; - ma_sound_inlined* pNext; - ma_sound_inlined* pPrev; -}; - -/* A sound group is just a sound. */ -typedef ma_sound_config ma_sound_group_config; -typedef ma_sound ma_sound_group; - -MA_API ma_sound_group_config ma_sound_group_config_init(void); /* Deprecated. Will be removed in version 0.12. Use ma_sound_config_2() instead. */ -MA_API ma_sound_group_config ma_sound_group_config_init_2(ma_engine* pEngine); /* Will be renamed to ma_sound_config_init() in version 0.12. */ - -typedef void (* ma_engine_process_proc)(void* pUserData, float* pFramesOut, ma_uint64 frameCount); - -typedef struct -{ -#if !defined(MA_NO_RESOURCE_MANAGER) - ma_resource_manager* pResourceManager; /* Can be null in which case a resource manager will be created for you. */ -#endif -#if !defined(MA_NO_DEVICE_IO) - ma_context* pContext; - ma_device* pDevice; /* If set, the caller is responsible for calling ma_engine_data_callback() in the device's data callback. */ - ma_device_id* pPlaybackDeviceID; /* The ID of the playback device to use with the default listener. */ - ma_device_data_proc dataCallback; /* Can be null. Can be used to provide a custom device data callback. */ - ma_device_notification_proc notificationCallback; -#endif - ma_log* pLog; /* When set to NULL, will use the context's log. */ - ma_uint32 listenerCount; /* Must be between 1 and MA_ENGINE_MAX_LISTENERS. */ - ma_uint32 channels; /* The number of channels to use when mixing and spatializing. When set to 0, will use the native channel count of the device. */ - ma_uint32 sampleRate; /* The sample rate. When set to 0 will use the native channel count of the device. */ - ma_uint32 periodSizeInFrames; /* If set to something other than 0, updates will always be exactly this size. The underlying device may be a different size, but from the perspective of the mixer that won't matter.*/ - ma_uint32 periodSizeInMilliseconds; /* Used if periodSizeInFrames is unset. */ - ma_uint32 gainSmoothTimeInFrames; /* The number of frames to interpolate the gain of spatialized sounds across. If set to 0, will use gainSmoothTimeInMilliseconds. */ - ma_uint32 gainSmoothTimeInMilliseconds; /* When set to 0, gainSmoothTimeInFrames will be used. If both are set to 0, a default value will be used. */ - ma_uint32 defaultVolumeSmoothTimeInPCMFrames; /* Defaults to 0. Controls the default amount of smoothing to apply to volume changes to sounds. High values means more smoothing at the expense of high latency (will take longer to reach the new volume). */ - ma_uint32 preMixStackSizeInBytes; /* A stack is used for internal processing in the node graph. This allows you to configure the size of this stack. Smaller values will reduce the maximum depth of your node graph. You should rarely need to modify this. */ - ma_allocation_callbacks allocationCallbacks; - ma_bool32 noAutoStart; /* When set to true, requires an explicit call to ma_engine_start(). This is false by default, meaning the engine will be started automatically in ma_engine_init(). */ - ma_bool32 noDevice; /* When set to true, don't create a default device. ma_engine_read_pcm_frames() can be called manually to read data. */ - ma_mono_expansion_mode monoExpansionMode; /* Controls how the mono channel should be expanded to other channels when spatialization is disabled on a sound. */ - ma_vfs* pResourceManagerVFS; /* A pointer to a pre-allocated VFS object to use with the resource manager. This is ignored if pResourceManager is not NULL. */ - ma_engine_process_proc onProcess; /* Fired at the end of each call to ma_engine_read_pcm_frames(). For engine's that manage their own internal device (the default configuration), this will be fired from the audio thread, and you do not need to call ma_engine_read_pcm_frames() manually in order to trigger this. */ - void* pProcessUserData; /* User data that's passed into onProcess. */ -} ma_engine_config; - -MA_API ma_engine_config ma_engine_config_init(void); - - -struct ma_engine -{ - ma_node_graph nodeGraph; /* An engine is a node graph. It should be able to be plugged into any ma_node_graph API (with a cast) which means this must be the first member of this struct. */ -#if !defined(MA_NO_RESOURCE_MANAGER) - ma_resource_manager* pResourceManager; -#endif -#if !defined(MA_NO_DEVICE_IO) - ma_device* pDevice; /* Optionally set via the config, otherwise allocated by the engine in ma_engine_init(). */ -#endif - ma_log* pLog; - ma_uint32 sampleRate; - ma_uint32 listenerCount; - ma_spatializer_listener listeners[MA_ENGINE_MAX_LISTENERS]; - ma_allocation_callbacks allocationCallbacks; - ma_bool8 ownsResourceManager; - ma_bool8 ownsDevice; - ma_spinlock inlinedSoundLock; /* For synchronizing access to the inlined sound list. */ - ma_sound_inlined* pInlinedSoundHead; /* The first inlined sound. Inlined sounds are tracked in a linked list. */ - MA_ATOMIC(4, ma_uint32) inlinedSoundCount; /* The total number of allocated inlined sound objects. Used for debugging. */ - ma_uint32 gainSmoothTimeInFrames; /* The number of frames to interpolate the gain of spatialized sounds across. */ - ma_uint32 defaultVolumeSmoothTimeInPCMFrames; - ma_mono_expansion_mode monoExpansionMode; - ma_engine_process_proc onProcess; - void* pProcessUserData; -}; - -MA_API ma_result ma_engine_init(const ma_engine_config* pConfig, ma_engine* pEngine); -MA_API void ma_engine_uninit(ma_engine* pEngine); -MA_API ma_result ma_engine_read_pcm_frames(ma_engine* pEngine, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead); -MA_API ma_node_graph* ma_engine_get_node_graph(ma_engine* pEngine); -#if !defined(MA_NO_RESOURCE_MANAGER) -MA_API ma_resource_manager* ma_engine_get_resource_manager(ma_engine* pEngine); -#endif -MA_API ma_device* ma_engine_get_device(ma_engine* pEngine); -MA_API ma_log* ma_engine_get_log(ma_engine* pEngine); -MA_API ma_node* ma_engine_get_endpoint(ma_engine* pEngine); -MA_API ma_uint64 ma_engine_get_time_in_pcm_frames(const ma_engine* pEngine); -MA_API ma_uint64 ma_engine_get_time_in_milliseconds(const ma_engine* pEngine); -MA_API ma_result ma_engine_set_time_in_pcm_frames(ma_engine* pEngine, ma_uint64 globalTime); -MA_API ma_result ma_engine_set_time_in_milliseconds(ma_engine* pEngine, ma_uint64 globalTime); -MA_API ma_uint64 ma_engine_get_time(const ma_engine* pEngine); /* Deprecated. Use ma_engine_get_time_in_pcm_frames(). Will be removed in version 0.12. */ -MA_API ma_result ma_engine_set_time(ma_engine* pEngine, ma_uint64 globalTime); /* Deprecated. Use ma_engine_set_time_in_pcm_frames(). Will be removed in version 0.12. */ -MA_API ma_uint32 ma_engine_get_channels(const ma_engine* pEngine); -MA_API ma_uint32 ma_engine_get_sample_rate(const ma_engine* pEngine); - -MA_API ma_result ma_engine_start(ma_engine* pEngine); -MA_API ma_result ma_engine_stop(ma_engine* pEngine); -MA_API ma_result ma_engine_set_volume(ma_engine* pEngine, float volume); -MA_API float ma_engine_get_volume(ma_engine* pEngine); -MA_API ma_result ma_engine_set_gain_db(ma_engine* pEngine, float gainDB); -MA_API float ma_engine_get_gain_db(ma_engine* pEngine); - -MA_API ma_uint32 ma_engine_get_listener_count(const ma_engine* pEngine); -MA_API ma_uint32 ma_engine_find_closest_listener(const ma_engine* pEngine, float absolutePosX, float absolutePosY, float absolutePosZ); -MA_API void ma_engine_listener_set_position(ma_engine* pEngine, ma_uint32 listenerIndex, float x, float y, float z); -MA_API ma_vec3f ma_engine_listener_get_position(const ma_engine* pEngine, ma_uint32 listenerIndex); -MA_API void ma_engine_listener_set_direction(ma_engine* pEngine, ma_uint32 listenerIndex, float x, float y, float z); -MA_API ma_vec3f ma_engine_listener_get_direction(const ma_engine* pEngine, ma_uint32 listenerIndex); -MA_API void ma_engine_listener_set_velocity(ma_engine* pEngine, ma_uint32 listenerIndex, float x, float y, float z); -MA_API ma_vec3f ma_engine_listener_get_velocity(const ma_engine* pEngine, ma_uint32 listenerIndex); -MA_API void ma_engine_listener_set_cone(ma_engine* pEngine, ma_uint32 listenerIndex, float innerAngleInRadians, float outerAngleInRadians, float outerGain); -MA_API void ma_engine_listener_get_cone(const ma_engine* pEngine, ma_uint32 listenerIndex, float* pInnerAngleInRadians, float* pOuterAngleInRadians, float* pOuterGain); -MA_API void ma_engine_listener_set_world_up(ma_engine* pEngine, ma_uint32 listenerIndex, float x, float y, float z); -MA_API ma_vec3f ma_engine_listener_get_world_up(const ma_engine* pEngine, ma_uint32 listenerIndex); -MA_API void ma_engine_listener_set_enabled(ma_engine* pEngine, ma_uint32 listenerIndex, ma_bool32 isEnabled); -MA_API ma_bool32 ma_engine_listener_is_enabled(const ma_engine* pEngine, ma_uint32 listenerIndex); - -#ifndef MA_NO_RESOURCE_MANAGER -MA_API ma_result ma_engine_play_sound_ex(ma_engine* pEngine, const char* pFilePath, ma_node* pNode, ma_uint32 nodeInputBusIndex); -MA_API ma_result ma_engine_play_sound(ma_engine* pEngine, const char* pFilePath, ma_sound_group* pGroup); /* Fire and forget. */ -#endif - -#ifndef MA_NO_RESOURCE_MANAGER -MA_API ma_result ma_sound_init_from_file(ma_engine* pEngine, const char* pFilePath, ma_uint32 flags, ma_sound_group* pGroup, ma_fence* pDoneFence, ma_sound* pSound); -MA_API ma_result ma_sound_init_from_file_w(ma_engine* pEngine, const wchar_t* pFilePath, ma_uint32 flags, ma_sound_group* pGroup, ma_fence* pDoneFence, ma_sound* pSound); -MA_API ma_result ma_sound_init_copy(ma_engine* pEngine, const ma_sound* pExistingSound, ma_uint32 flags, ma_sound_group* pGroup, ma_sound* pSound); -#endif -MA_API ma_result ma_sound_init_from_data_source(ma_engine* pEngine, ma_data_source* pDataSource, ma_uint32 flags, ma_sound_group* pGroup, ma_sound* pSound); -MA_API ma_result ma_sound_init_ex(ma_engine* pEngine, const ma_sound_config* pConfig, ma_sound* pSound); -MA_API void ma_sound_uninit(ma_sound* pSound); -MA_API ma_engine* ma_sound_get_engine(const ma_sound* pSound); -MA_API ma_data_source* ma_sound_get_data_source(const ma_sound* pSound); -MA_API ma_result ma_sound_start(ma_sound* pSound); -MA_API ma_result ma_sound_stop(ma_sound* pSound); -MA_API ma_result ma_sound_stop_with_fade_in_pcm_frames(ma_sound* pSound, ma_uint64 fadeLengthInFrames); /* Will overwrite any scheduled stop and fade. */ -MA_API ma_result ma_sound_stop_with_fade_in_milliseconds(ma_sound* pSound, ma_uint64 fadeLengthInFrames); /* Will overwrite any scheduled stop and fade. */ -MA_API void ma_sound_set_volume(ma_sound* pSound, float volume); -MA_API float ma_sound_get_volume(const ma_sound* pSound); -MA_API void ma_sound_set_pan(ma_sound* pSound, float pan); -MA_API float ma_sound_get_pan(const ma_sound* pSound); -MA_API void ma_sound_set_pan_mode(ma_sound* pSound, ma_pan_mode panMode); -MA_API ma_pan_mode ma_sound_get_pan_mode(const ma_sound* pSound); -MA_API void ma_sound_set_pitch(ma_sound* pSound, float pitch); -MA_API float ma_sound_get_pitch(const ma_sound* pSound); -MA_API void ma_sound_set_spatialization_enabled(ma_sound* pSound, ma_bool32 enabled); -MA_API ma_bool32 ma_sound_is_spatialization_enabled(const ma_sound* pSound); -MA_API void ma_sound_set_pinned_listener_index(ma_sound* pSound, ma_uint32 listenerIndex); -MA_API ma_uint32 ma_sound_get_pinned_listener_index(const ma_sound* pSound); -MA_API ma_uint32 ma_sound_get_listener_index(const ma_sound* pSound); -MA_API ma_vec3f ma_sound_get_direction_to_listener(const ma_sound* pSound); -MA_API void ma_sound_set_position(ma_sound* pSound, float x, float y, float z); -MA_API ma_vec3f ma_sound_get_position(const ma_sound* pSound); -MA_API void ma_sound_set_direction(ma_sound* pSound, float x, float y, float z); -MA_API ma_vec3f ma_sound_get_direction(const ma_sound* pSound); -MA_API void ma_sound_set_velocity(ma_sound* pSound, float x, float y, float z); -MA_API ma_vec3f ma_sound_get_velocity(const ma_sound* pSound); -MA_API void ma_sound_set_attenuation_model(ma_sound* pSound, ma_attenuation_model attenuationModel); -MA_API ma_attenuation_model ma_sound_get_attenuation_model(const ma_sound* pSound); -MA_API void ma_sound_set_positioning(ma_sound* pSound, ma_positioning positioning); -MA_API ma_positioning ma_sound_get_positioning(const ma_sound* pSound); -MA_API void ma_sound_set_rolloff(ma_sound* pSound, float rolloff); -MA_API float ma_sound_get_rolloff(const ma_sound* pSound); -MA_API void ma_sound_set_min_gain(ma_sound* pSound, float minGain); -MA_API float ma_sound_get_min_gain(const ma_sound* pSound); -MA_API void ma_sound_set_max_gain(ma_sound* pSound, float maxGain); -MA_API float ma_sound_get_max_gain(const ma_sound* pSound); -MA_API void ma_sound_set_min_distance(ma_sound* pSound, float minDistance); -MA_API float ma_sound_get_min_distance(const ma_sound* pSound); -MA_API void ma_sound_set_max_distance(ma_sound* pSound, float maxDistance); -MA_API float ma_sound_get_max_distance(const ma_sound* pSound); -MA_API void ma_sound_set_cone(ma_sound* pSound, float innerAngleInRadians, float outerAngleInRadians, float outerGain); -MA_API void ma_sound_get_cone(const ma_sound* pSound, float* pInnerAngleInRadians, float* pOuterAngleInRadians, float* pOuterGain); -MA_API void ma_sound_set_doppler_factor(ma_sound* pSound, float dopplerFactor); -MA_API float ma_sound_get_doppler_factor(const ma_sound* pSound); -MA_API void ma_sound_set_directional_attenuation_factor(ma_sound* pSound, float directionalAttenuationFactor); -MA_API float ma_sound_get_directional_attenuation_factor(const ma_sound* pSound); -MA_API void ma_sound_set_fade_in_pcm_frames(ma_sound* pSound, float volumeBeg, float volumeEnd, ma_uint64 fadeLengthInFrames); -MA_API void ma_sound_set_fade_in_milliseconds(ma_sound* pSound, float volumeBeg, float volumeEnd, ma_uint64 fadeLengthInMilliseconds); -MA_API void ma_sound_set_fade_start_in_pcm_frames(ma_sound* pSound, float volumeBeg, float volumeEnd, ma_uint64 fadeLengthInFrames, ma_uint64 absoluteGlobalTimeInFrames); -MA_API void ma_sound_set_fade_start_in_milliseconds(ma_sound* pSound, float volumeBeg, float volumeEnd, ma_uint64 fadeLengthInMilliseconds, ma_uint64 absoluteGlobalTimeInMilliseconds); -MA_API float ma_sound_get_current_fade_volume(const ma_sound* pSound); -MA_API void ma_sound_set_start_time_in_pcm_frames(ma_sound* pSound, ma_uint64 absoluteGlobalTimeInFrames); -MA_API void ma_sound_set_start_time_in_milliseconds(ma_sound* pSound, ma_uint64 absoluteGlobalTimeInMilliseconds); -MA_API void ma_sound_set_stop_time_in_pcm_frames(ma_sound* pSound, ma_uint64 absoluteGlobalTimeInFrames); -MA_API void ma_sound_set_stop_time_in_milliseconds(ma_sound* pSound, ma_uint64 absoluteGlobalTimeInMilliseconds); -MA_API void ma_sound_set_stop_time_with_fade_in_pcm_frames(ma_sound* pSound, ma_uint64 stopAbsoluteGlobalTimeInFrames, ma_uint64 fadeLengthInFrames); -MA_API void ma_sound_set_stop_time_with_fade_in_milliseconds(ma_sound* pSound, ma_uint64 stopAbsoluteGlobalTimeInMilliseconds, ma_uint64 fadeLengthInMilliseconds); -MA_API ma_bool32 ma_sound_is_playing(const ma_sound* pSound); -MA_API ma_uint64 ma_sound_get_time_in_pcm_frames(const ma_sound* pSound); -MA_API ma_uint64 ma_sound_get_time_in_milliseconds(const ma_sound* pSound); -MA_API void ma_sound_set_looping(ma_sound* pSound, ma_bool32 isLooping); -MA_API ma_bool32 ma_sound_is_looping(const ma_sound* pSound); -MA_API ma_bool32 ma_sound_at_end(const ma_sound* pSound); -MA_API ma_result ma_sound_seek_to_pcm_frame(ma_sound* pSound, ma_uint64 frameIndex); /* Just a wrapper around ma_data_source_seek_to_pcm_frame(). */ -MA_API ma_result ma_sound_seek_to_second(ma_sound* pSound, float seekPointInSeconds); /* Abstraction to ma_sound_seek_to_pcm_frame() */ -MA_API ma_result ma_sound_get_data_format(ma_sound* pSound, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate, ma_channel* pChannelMap, size_t channelMapCap); -MA_API ma_result ma_sound_get_cursor_in_pcm_frames(ma_sound* pSound, ma_uint64* pCursor); -MA_API ma_result ma_sound_get_length_in_pcm_frames(ma_sound* pSound, ma_uint64* pLength); -MA_API ma_result ma_sound_get_cursor_in_seconds(ma_sound* pSound, float* pCursor); -MA_API ma_result ma_sound_get_length_in_seconds(ma_sound* pSound, float* pLength); -MA_API ma_result ma_sound_set_end_callback(ma_sound* pSound, ma_sound_end_proc callback, void* pUserData); - -MA_API ma_result ma_sound_group_init(ma_engine* pEngine, ma_uint32 flags, ma_sound_group* pParentGroup, ma_sound_group* pGroup); -MA_API ma_result ma_sound_group_init_ex(ma_engine* pEngine, const ma_sound_group_config* pConfig, ma_sound_group* pGroup); -MA_API void ma_sound_group_uninit(ma_sound_group* pGroup); -MA_API ma_engine* ma_sound_group_get_engine(const ma_sound_group* pGroup); -MA_API ma_result ma_sound_group_start(ma_sound_group* pGroup); -MA_API ma_result ma_sound_group_stop(ma_sound_group* pGroup); -MA_API void ma_sound_group_set_volume(ma_sound_group* pGroup, float volume); -MA_API float ma_sound_group_get_volume(const ma_sound_group* pGroup); -MA_API void ma_sound_group_set_pan(ma_sound_group* pGroup, float pan); -MA_API float ma_sound_group_get_pan(const ma_sound_group* pGroup); -MA_API void ma_sound_group_set_pan_mode(ma_sound_group* pGroup, ma_pan_mode panMode); -MA_API ma_pan_mode ma_sound_group_get_pan_mode(const ma_sound_group* pGroup); -MA_API void ma_sound_group_set_pitch(ma_sound_group* pGroup, float pitch); -MA_API float ma_sound_group_get_pitch(const ma_sound_group* pGroup); -MA_API void ma_sound_group_set_spatialization_enabled(ma_sound_group* pGroup, ma_bool32 enabled); -MA_API ma_bool32 ma_sound_group_is_spatialization_enabled(const ma_sound_group* pGroup); -MA_API void ma_sound_group_set_pinned_listener_index(ma_sound_group* pGroup, ma_uint32 listenerIndex); -MA_API ma_uint32 ma_sound_group_get_pinned_listener_index(const ma_sound_group* pGroup); -MA_API ma_uint32 ma_sound_group_get_listener_index(const ma_sound_group* pGroup); -MA_API ma_vec3f ma_sound_group_get_direction_to_listener(const ma_sound_group* pGroup); -MA_API void ma_sound_group_set_position(ma_sound_group* pGroup, float x, float y, float z); -MA_API ma_vec3f ma_sound_group_get_position(const ma_sound_group* pGroup); -MA_API void ma_sound_group_set_direction(ma_sound_group* pGroup, float x, float y, float z); -MA_API ma_vec3f ma_sound_group_get_direction(const ma_sound_group* pGroup); -MA_API void ma_sound_group_set_velocity(ma_sound_group* pGroup, float x, float y, float z); -MA_API ma_vec3f ma_sound_group_get_velocity(const ma_sound_group* pGroup); -MA_API void ma_sound_group_set_attenuation_model(ma_sound_group* pGroup, ma_attenuation_model attenuationModel); -MA_API ma_attenuation_model ma_sound_group_get_attenuation_model(const ma_sound_group* pGroup); -MA_API void ma_sound_group_set_positioning(ma_sound_group* pGroup, ma_positioning positioning); -MA_API ma_positioning ma_sound_group_get_positioning(const ma_sound_group* pGroup); -MA_API void ma_sound_group_set_rolloff(ma_sound_group* pGroup, float rolloff); -MA_API float ma_sound_group_get_rolloff(const ma_sound_group* pGroup); -MA_API void ma_sound_group_set_min_gain(ma_sound_group* pGroup, float minGain); -MA_API float ma_sound_group_get_min_gain(const ma_sound_group* pGroup); -MA_API void ma_sound_group_set_max_gain(ma_sound_group* pGroup, float maxGain); -MA_API float ma_sound_group_get_max_gain(const ma_sound_group* pGroup); -MA_API void ma_sound_group_set_min_distance(ma_sound_group* pGroup, float minDistance); -MA_API float ma_sound_group_get_min_distance(const ma_sound_group* pGroup); -MA_API void ma_sound_group_set_max_distance(ma_sound_group* pGroup, float maxDistance); -MA_API float ma_sound_group_get_max_distance(const ma_sound_group* pGroup); -MA_API void ma_sound_group_set_cone(ma_sound_group* pGroup, float innerAngleInRadians, float outerAngleInRadians, float outerGain); -MA_API void ma_sound_group_get_cone(const ma_sound_group* pGroup, float* pInnerAngleInRadians, float* pOuterAngleInRadians, float* pOuterGain); -MA_API void ma_sound_group_set_doppler_factor(ma_sound_group* pGroup, float dopplerFactor); -MA_API float ma_sound_group_get_doppler_factor(const ma_sound_group* pGroup); -MA_API void ma_sound_group_set_directional_attenuation_factor(ma_sound_group* pGroup, float directionalAttenuationFactor); -MA_API float ma_sound_group_get_directional_attenuation_factor(const ma_sound_group* pGroup); -MA_API void ma_sound_group_set_fade_in_pcm_frames(ma_sound_group* pGroup, float volumeBeg, float volumeEnd, ma_uint64 fadeLengthInFrames); -MA_API void ma_sound_group_set_fade_in_milliseconds(ma_sound_group* pGroup, float volumeBeg, float volumeEnd, ma_uint64 fadeLengthInMilliseconds); -MA_API float ma_sound_group_get_current_fade_volume(ma_sound_group* pGroup); -MA_API void ma_sound_group_set_start_time_in_pcm_frames(ma_sound_group* pGroup, ma_uint64 absoluteGlobalTimeInFrames); -MA_API void ma_sound_group_set_start_time_in_milliseconds(ma_sound_group* pGroup, ma_uint64 absoluteGlobalTimeInMilliseconds); -MA_API void ma_sound_group_set_stop_time_in_pcm_frames(ma_sound_group* pGroup, ma_uint64 absoluteGlobalTimeInFrames); -MA_API void ma_sound_group_set_stop_time_in_milliseconds(ma_sound_group* pGroup, ma_uint64 absoluteGlobalTimeInMilliseconds); -MA_API ma_bool32 ma_sound_group_is_playing(const ma_sound_group* pGroup); -MA_API ma_uint64 ma_sound_group_get_time_in_pcm_frames(const ma_sound_group* pGroup); -#endif /* MA_NO_ENGINE */ -/* END SECTION: miniaudio_engine.h */ - -#ifdef __cplusplus -} -#endif -#endif /* miniaudio_h */ - - -/* -This is for preventing greying out of the implementation section. -*/ -#if defined(Q_CREATOR_RUN) || defined(__INTELLISENSE__) || defined(__CDT_PARSER__) -#define MINIAUDIO_IMPLEMENTATION -#endif - -/************************************************************************************************************************************************************ -************************************************************************************************************************************************************* - -IMPLEMENTATION - -************************************************************************************************************************************************************* -************************************************************************************************************************************************************/ -#if defined(MINIAUDIO_IMPLEMENTATION) || defined(MA_IMPLEMENTATION) -#ifndef miniaudio_c -#define miniaudio_c - -#include -#include /* For INT_MAX */ -#include /* sin(), etc. */ -#include /* For malloc(), free(), wcstombs(). */ -#include /* For memset() */ - -#include -#include -#if !defined(_MSC_VER) && !defined(__DMC__) - #include /* For strcasecmp(). */ - #include /* For wcslen(), wcsrtombs() */ -#endif -#ifdef _MSC_VER - #include /* For _controlfp_s constants */ -#endif - -#if defined(MA_WIN32) - #include - - /* - There's a possibility that WIN32_LEAN_AND_MEAN has been defined which will exclude some symbols - such as STGM_READ and CLSCTL_ALL. We need to check these and define them ourselves if they're - unavailable. - */ - #ifndef STGM_READ - #define STGM_READ 0x00000000L - #endif - #ifndef CLSCTX_ALL - #define CLSCTX_ALL 23 - #endif - - /* IUnknown is used by both the WASAPI and DirectSound backends. It easier to just declare our version here. */ - typedef struct ma_IUnknown ma_IUnknown; -#endif - -#if !defined(MA_WIN32) -#include -#include /* select() (used for ma_sleep()). */ -#include -#endif - -#ifdef MA_NX -#include /* For nanosleep() */ -#endif - -#include /* For fstat(), etc. */ - -#ifdef MA_EMSCRIPTEN -#include -#endif - - -/* Architecture Detection */ -#if !defined(MA_64BIT) && !defined(MA_32BIT) -#ifdef _WIN32 -#ifdef _WIN64 -#define MA_64BIT -#else -#define MA_32BIT -#endif -#endif -#endif - -#if !defined(MA_64BIT) && !defined(MA_32BIT) -#ifdef __GNUC__ -#ifdef __LP64__ -#define MA_64BIT -#else -#define MA_32BIT -#endif -#endif -#endif - -#if !defined(MA_64BIT) && !defined(MA_32BIT) -#include -#if INTPTR_MAX == INT64_MAX -#define MA_64BIT -#else -#define MA_32BIT -#endif -#endif - -#if defined(__arm__) || defined(_M_ARM) -#define MA_ARM32 -#endif -#if defined(__arm64) || defined(__arm64__) || defined(__aarch64__) || defined(_M_ARM64) -#define MA_ARM64 -#endif - -#if defined(__x86_64__) || defined(_M_X64) -#define MA_X64 -#elif defined(__i386) || defined(_M_IX86) -#define MA_X86 -#elif defined(MA_ARM32) || defined(MA_ARM64) -#define MA_ARM -#endif - -/* Intrinsics Support */ -#if (defined(MA_X64) || defined(MA_X86)) && !defined(__COSMOPOLITAN__) - #if defined(_MSC_VER) && !defined(__clang__) - /* MSVC. */ - #if _MSC_VER >= 1400 && !defined(MA_NO_SSE2) /* 2005 */ - #define MA_SUPPORT_SSE2 - #endif - /*#if _MSC_VER >= 1600 && !defined(MA_NO_AVX)*/ /* 2010 */ - /* #define MA_SUPPORT_AVX*/ - /*#endif*/ - #if _MSC_VER >= 1700 && !defined(MA_NO_AVX2) /* 2012 */ - #define MA_SUPPORT_AVX2 - #endif - #else - /* Assume GNUC-style. */ - #if defined(__SSE2__) && !defined(MA_NO_SSE2) - #define MA_SUPPORT_SSE2 - #endif - /*#if defined(__AVX__) && !defined(MA_NO_AVX)*/ - /* #define MA_SUPPORT_AVX*/ - /*#endif*/ - #if defined(__AVX2__) && !defined(MA_NO_AVX2) - #define MA_SUPPORT_AVX2 - #endif - #endif - - /* If at this point we still haven't determined compiler support for the intrinsics just fall back to __has_include. */ - #if !defined(__GNUC__) && !defined(__clang__) && defined(__has_include) - #if !defined(MA_SUPPORT_SSE2) && !defined(MA_NO_SSE2) && __has_include() - #define MA_SUPPORT_SSE2 - #endif - /*#if !defined(MA_SUPPORT_AVX) && !defined(MA_NO_AVX) && __has_include()*/ - /* #define MA_SUPPORT_AVX*/ - /*#endif*/ - #if !defined(MA_SUPPORT_AVX2) && !defined(MA_NO_AVX2) && __has_include() - #define MA_SUPPORT_AVX2 - #endif - #endif - - #if defined(MA_SUPPORT_AVX2) || defined(MA_SUPPORT_AVX) - #include - #elif defined(MA_SUPPORT_SSE2) - #include - #endif -#endif - -#if defined(MA_ARM) - #if !defined(MA_NO_NEON) && (defined(__ARM_NEON) || defined(__aarch64__) || defined(_M_ARM64)) - #define MA_SUPPORT_NEON - #include - #endif -#endif - -/* Begin globally disabled warnings. */ -#if defined(_MSC_VER) - #pragma warning(push) - #pragma warning(disable:4752) /* found Intel(R) Advanced Vector Extensions; consider using /arch:AVX */ - #pragma warning(disable:4049) /* compiler limit : terminating line number emission */ -#endif - -#if defined(MA_X64) || defined(MA_X86) - #if defined(_MSC_VER) && !defined(__clang__) - #if _MSC_VER >= 1400 - #include - static MA_INLINE void ma_cpuid(int info[4], int fid) - { - __cpuid(info, fid); - } - #else - #define MA_NO_CPUID - #endif - - #if _MSC_VER >= 1600 && (defined(_MSC_FULL_VER) && _MSC_FULL_VER >= 160040219) - static MA_INLINE unsigned __int64 ma_xgetbv(int reg) - { - return _xgetbv(reg); - } - #else - #define MA_NO_XGETBV - #endif - #elif (defined(__GNUC__) || defined(__clang__)) && !defined(MA_ANDROID) - static MA_INLINE void ma_cpuid(int info[4], int fid) - { - /* - It looks like the -fPIC option uses the ebx register which GCC complains about. We can work around this by just using a different register, the - specific register of which I'm letting the compiler decide on. The "k" prefix is used to specify a 32-bit register. The {...} syntax is for - supporting different assembly dialects. - - What's basically happening is that we're saving and restoring the ebx register manually. - */ - #if defined(MA_X86) && defined(__PIC__) - __asm__ __volatile__ ( - "xchg{l} {%%}ebx, %k1;" - "cpuid;" - "xchg{l} {%%}ebx, %k1;" - : "=a"(info[0]), "=&r"(info[1]), "=c"(info[2]), "=d"(info[3]) : "a"(fid), "c"(0) - ); - #else - __asm__ __volatile__ ( - "cpuid" : "=a"(info[0]), "=b"(info[1]), "=c"(info[2]), "=d"(info[3]) : "a"(fid), "c"(0) - ); - #endif - } - - static MA_INLINE ma_uint64 ma_xgetbv(int reg) - { - unsigned int hi; - unsigned int lo; - - __asm__ __volatile__ ( - "xgetbv" : "=a"(lo), "=d"(hi) : "c"(reg) - ); - - return ((ma_uint64)hi << 32) | (ma_uint64)lo; - } - #else - #define MA_NO_CPUID - #define MA_NO_XGETBV - #endif -#else - #define MA_NO_CPUID - #define MA_NO_XGETBV -#endif - -static MA_INLINE ma_bool32 ma_has_sse2(void) -{ -#if defined(MA_SUPPORT_SSE2) - #if (defined(MA_X64) || defined(MA_X86)) && !defined(MA_NO_SSE2) - #if defined(MA_X64) - return MA_TRUE; /* 64-bit targets always support SSE2. */ - #elif (defined(_M_IX86_FP) && _M_IX86_FP == 2) || defined(__SSE2__) - return MA_TRUE; /* If the compiler is allowed to freely generate SSE2 code we can assume support. */ - #else - #if defined(MA_NO_CPUID) - return MA_FALSE; - #else - int info[4]; - ma_cpuid(info, 1); - return (info[3] & (1 << 26)) != 0; - #endif - #endif - #else - return MA_FALSE; /* SSE2 is only supported on x86 and x64 architectures. */ - #endif -#else - return MA_FALSE; /* No compiler support. */ -#endif -} - -#if 0 -static MA_INLINE ma_bool32 ma_has_avx() -{ -#if defined(MA_SUPPORT_AVX) - #if (defined(MA_X64) || defined(MA_X86)) && !defined(MA_NO_AVX) - #if defined(_AVX_) || defined(__AVX__) - return MA_TRUE; /* If the compiler is allowed to freely generate AVX code we can assume support. */ - #else - /* AVX requires both CPU and OS support. */ - #if defined(MA_NO_CPUID) || defined(MA_NO_XGETBV) - return MA_FALSE; - #else - int info[4]; - ma_cpuid(info, 1); - if (((info[2] & (1 << 27)) != 0) && ((info[2] & (1 << 28)) != 0)) { - ma_uint64 xrc = ma_xgetbv(0); - if ((xrc & 0x06) == 0x06) { - return MA_TRUE; - } else { - return MA_FALSE; - } - } else { - return MA_FALSE; - } - #endif - #endif - #else - return MA_FALSE; /* AVX is only supported on x86 and x64 architectures. */ - #endif -#else - return MA_FALSE; /* No compiler support. */ -#endif -} -#endif - -static MA_INLINE ma_bool32 ma_has_avx2(void) -{ -#if defined(MA_SUPPORT_AVX2) - #if (defined(MA_X64) || defined(MA_X86)) && !defined(MA_NO_AVX2) - #if defined(_AVX2_) || defined(__AVX2__) - return MA_TRUE; /* If the compiler is allowed to freely generate AVX2 code we can assume support. */ - #else - /* AVX2 requires both CPU and OS support. */ - #if defined(MA_NO_CPUID) || defined(MA_NO_XGETBV) - return MA_FALSE; - #else - int info1[4]; - int info7[4]; - ma_cpuid(info1, 1); - ma_cpuid(info7, 7); - if (((info1[2] & (1 << 27)) != 0) && ((info7[1] & (1 << 5)) != 0)) { - ma_uint64 xrc = ma_xgetbv(0); - if ((xrc & 0x06) == 0x06) { - return MA_TRUE; - } else { - return MA_FALSE; - } - } else { - return MA_FALSE; - } - #endif - #endif - #else - return MA_FALSE; /* AVX2 is only supported on x86 and x64 architectures. */ - #endif -#else - return MA_FALSE; /* No compiler support. */ -#endif -} - -static MA_INLINE ma_bool32 ma_has_neon(void) -{ -#if defined(MA_SUPPORT_NEON) - #if defined(MA_ARM) && !defined(MA_NO_NEON) - #if (defined(__ARM_NEON) || defined(__aarch64__) || defined(_M_ARM64)) - return MA_TRUE; /* If the compiler is allowed to freely generate NEON code we can assume support. */ - #else - /* TODO: Runtime check. */ - return MA_FALSE; - #endif - #else - return MA_FALSE; /* NEON is only supported on ARM architectures. */ - #endif -#else - return MA_FALSE; /* No compiler support. */ -#endif -} - -#if defined(__has_builtin) - #define MA_COMPILER_HAS_BUILTIN(x) __has_builtin(x) -#else - #define MA_COMPILER_HAS_BUILTIN(x) 0 -#endif - -#ifndef MA_ASSUME - #if MA_COMPILER_HAS_BUILTIN(__builtin_assume) - #define MA_ASSUME(x) __builtin_assume(x) - #elif MA_COMPILER_HAS_BUILTIN(__builtin_unreachable) - #define MA_ASSUME(x) do { if (!(x)) __builtin_unreachable(); } while (0) - #elif defined(_MSC_VER) - #define MA_ASSUME(x) __assume(x) - #else - #define MA_ASSUME(x) (void)(x) - #endif -#endif - -#ifndef MA_RESTRICT - #if defined(__clang__) || defined(__GNUC__) || defined(_MSC_VER) - #define MA_RESTRICT __restrict - #else - #define MA_RESTRICT - #endif -#endif - -#if defined(_MSC_VER) && _MSC_VER >= 1400 - #define MA_HAS_BYTESWAP16_INTRINSIC - #define MA_HAS_BYTESWAP32_INTRINSIC - #define MA_HAS_BYTESWAP64_INTRINSIC -#elif defined(__clang__) - #if MA_COMPILER_HAS_BUILTIN(__builtin_bswap16) - #define MA_HAS_BYTESWAP16_INTRINSIC - #endif - #if MA_COMPILER_HAS_BUILTIN(__builtin_bswap32) - #define MA_HAS_BYTESWAP32_INTRINSIC - #endif - #if MA_COMPILER_HAS_BUILTIN(__builtin_bswap64) - #define MA_HAS_BYTESWAP64_INTRINSIC - #endif -#elif defined(__GNUC__) - #if ((__GNUC__ > 4) || (__GNUC__ == 4 && __GNUC_MINOR__ >= 3)) - #define MA_HAS_BYTESWAP32_INTRINSIC - #define MA_HAS_BYTESWAP64_INTRINSIC - #endif - #if ((__GNUC__ > 4) || (__GNUC__ == 4 && __GNUC_MINOR__ >= 8)) - #define MA_HAS_BYTESWAP16_INTRINSIC - #endif -#endif - - -static MA_INLINE ma_bool32 ma_is_little_endian(void) -{ -#if defined(MA_X86) || defined(MA_X64) - return MA_TRUE; -#else - int n = 1; - return (*(char*)&n) == 1; -#endif -} - -static MA_INLINE ma_bool32 ma_is_big_endian(void) -{ - return !ma_is_little_endian(); -} - - -static MA_INLINE ma_uint32 ma_swap_endian_uint32(ma_uint32 n) -{ -#ifdef MA_HAS_BYTESWAP32_INTRINSIC - #if defined(_MSC_VER) - return _byteswap_ulong(n); - #elif defined(__GNUC__) || defined(__clang__) - #if defined(MA_ARM) && (defined(__ARM_ARCH) && __ARM_ARCH >= 6) && !defined(MA_64BIT) /* <-- 64-bit inline assembly has not been tested, so disabling for now. */ - /* Inline assembly optimized implementation for ARM. In my testing, GCC does not generate optimized code with __builtin_bswap32(). */ - ma_uint32 r; - __asm__ __volatile__ ( - #if defined(MA_64BIT) - "rev %w[out], %w[in]" : [out]"=r"(r) : [in]"r"(n) /* <-- This is untested. If someone in the community could test this, that would be appreciated! */ - #else - "rev %[out], %[in]" : [out]"=r"(r) : [in]"r"(n) - #endif - ); - return r; - #else - return __builtin_bswap32(n); - #endif - #else - #error "This compiler does not support the byte swap intrinsic." - #endif -#else - return ((n & 0xFF000000) >> 24) | - ((n & 0x00FF0000) >> 8) | - ((n & 0x0000FF00) << 8) | - ((n & 0x000000FF) << 24); -#endif -} - - -#if !defined(MA_EMSCRIPTEN) -#ifdef MA_WIN32 -static void ma_sleep__win32(ma_uint32 milliseconds) -{ - Sleep((DWORD)milliseconds); -} -#endif -#ifdef MA_POSIX -static void ma_sleep__posix(ma_uint32 milliseconds) -{ -#ifdef MA_EMSCRIPTEN - (void)milliseconds; - MA_ASSERT(MA_FALSE); /* The Emscripten build should never sleep. */ -#else - #if (defined(_POSIX_C_SOURCE) && _POSIX_C_SOURCE >= 199309L) || defined(MA_NX) - struct timespec ts; - ts.tv_sec = milliseconds / 1000; - ts.tv_nsec = milliseconds % 1000 * 1000000; - nanosleep(&ts, NULL); - #else - struct timeval tv; - tv.tv_sec = milliseconds / 1000; - tv.tv_usec = milliseconds % 1000 * 1000; - select(0, NULL, NULL, NULL, &tv); - #endif -#endif -} -#endif - -static MA_INLINE void ma_sleep(ma_uint32 milliseconds) -{ -#ifdef MA_WIN32 - ma_sleep__win32(milliseconds); -#endif -#ifdef MA_POSIX - ma_sleep__posix(milliseconds); -#endif -} -#endif - -static MA_INLINE void ma_yield(void) -{ -#if defined(__i386) || defined(_M_IX86) || defined(__x86_64__) || defined(_M_X64) - /* x86/x64 */ - #if (defined(_MSC_VER) || defined(__WATCOMC__) || defined(__DMC__)) && !defined(__clang__) - #if _MSC_VER >= 1400 - _mm_pause(); - #else - #if defined(__DMC__) - /* Digital Mars does not recognize the PAUSE opcode. Fall back to NOP. */ - __asm nop; - #else - __asm pause; - #endif - #endif - #else - __asm__ __volatile__ ("pause"); - #endif -#elif (defined(__arm__) && defined(__ARM_ARCH) && __ARM_ARCH >= 7) || defined(_M_ARM64) || (defined(_M_ARM) && _M_ARM >= 7) || defined(__ARM_ARCH_6K__) || defined(__ARM_ARCH_6T2__) - /* ARM */ - #if defined(_MSC_VER) - /* Apparently there is a __yield() intrinsic that's compatible with ARM, but I cannot find documentation for it nor can I find where it's declared. */ - __yield(); - #else - __asm__ __volatile__ ("yield"); /* ARMv6K/ARMv6T2 and above. */ - #endif -#else - /* Unknown or unsupported architecture. No-op. */ -#endif -} - - -#define MA_MM_DENORMALS_ZERO_MASK 0x0040 -#define MA_MM_FLUSH_ZERO_MASK 0x8000 - -static MA_INLINE unsigned int ma_disable_denormals(void) -{ - unsigned int prevState; - - #if defined(_MSC_VER) - { - /* - Older versions of Visual Studio don't support the "safe" versions of _controlfp_s(). I don't - know which version of Visual Studio first added support for _controlfp_s(), but I do know - that VC6 lacks support. _MSC_VER = 1200 is VC6, but if you get compilation errors on older - versions of Visual Studio, let me know and I'll make the necessary adjustment. - */ - #if _MSC_VER <= 1200 - { - prevState = _statusfp(); - _controlfp(prevState | _DN_FLUSH, _MCW_DN); - } - #else - { - unsigned int unused; - _controlfp_s(&prevState, 0, 0); - _controlfp_s(&unused, prevState | _DN_FLUSH, _MCW_DN); - } - #endif - } - #elif defined(MA_X86) || defined(MA_X64) - { - #if defined(__SSE2__) && !(defined(__TINYC__) || defined(__WATCOMC__) || defined(__COSMOPOLITAN__)) /* <-- Add compilers that lack support for _mm_getcsr() and _mm_setcsr() to this list. */ - { - prevState = _mm_getcsr(); - _mm_setcsr(prevState | MA_MM_DENORMALS_ZERO_MASK | MA_MM_FLUSH_ZERO_MASK); - } - #else - { - /* x88/64, but no support for _mm_getcsr()/_mm_setcsr(). May need to fall back to inlined assembly here. */ - prevState = 0; - } - #endif - } - #else - { - /* Unknown or unsupported architecture. No-op. */ - prevState = 0; - } - #endif - - return prevState; -} - -static MA_INLINE void ma_restore_denormals(unsigned int prevState) -{ - #if defined(_MSC_VER) - { - /* Older versions of Visual Studio do not support _controlfp_s(). See ma_disable_denormals(). */ - #if _MSC_VER <= 1200 - { - _controlfp(prevState, _MCW_DN); - } - #else - { - unsigned int unused; - _controlfp_s(&unused, prevState, _MCW_DN); - } - #endif - } - #elif defined(MA_X86) || defined(MA_X64) - { - #if defined(__SSE2__) && !(defined(__TINYC__) || defined(__WATCOMC__) || defined(__COSMOPOLITAN__)) /* <-- Add compilers that lack support for _mm_getcsr() and _mm_setcsr() to this list. */ - { - _mm_setcsr(prevState); - } - #else - { - /* x88/64, but no support for _mm_getcsr()/_mm_setcsr(). May need to fall back to inlined assembly here. */ - (void)prevState; - } - #endif - } - #else - { - /* Unknown or unsupported architecture. No-op. */ - (void)prevState; - } - #endif -} - - -#ifdef MA_ANDROID -#include - -int ma_android_sdk_version() -{ - char sdkVersion[PROP_VALUE_MAX + 1] = {0, }; - if (__system_property_get("ro.build.version.sdk", sdkVersion)) { - return atoi(sdkVersion); - } - - return 0; -} -#endif - - -#ifndef MA_COINIT_VALUE -#define MA_COINIT_VALUE 0 /* 0 = COINIT_MULTITHREADED */ -#endif - - -#ifndef MA_FLT_MAX - #ifdef FLT_MAX - #define MA_FLT_MAX FLT_MAX - #else - #define MA_FLT_MAX 3.402823466e+38F - #endif -#endif - - -#ifndef MA_PI -#define MA_PI 3.14159265358979323846264f -#endif -#ifndef MA_PI_D -#define MA_PI_D 3.14159265358979323846264 -#endif -#ifndef MA_TAU -#define MA_TAU 6.28318530717958647693f -#endif -#ifndef MA_TAU_D -#define MA_TAU_D 6.28318530717958647693 -#endif - - -/* The default format when ma_format_unknown (0) is requested when initializing a device. */ -#ifndef MA_DEFAULT_FORMAT -#define MA_DEFAULT_FORMAT ma_format_f32 -#endif - -/* The default channel count to use when 0 is used when initializing a device. */ -#ifndef MA_DEFAULT_CHANNELS -#define MA_DEFAULT_CHANNELS 2 -#endif - -/* The default sample rate to use when 0 is used when initializing a device. */ -#ifndef MA_DEFAULT_SAMPLE_RATE -#define MA_DEFAULT_SAMPLE_RATE 48000 -#endif - -/* Default periods when none is specified in ma_device_init(). More periods means more work on the CPU. */ -#ifndef MA_DEFAULT_PERIODS -#define MA_DEFAULT_PERIODS 3 -#endif - -/* The default period size in milliseconds for low latency mode. */ -#ifndef MA_DEFAULT_PERIOD_SIZE_IN_MILLISECONDS_LOW_LATENCY -#define MA_DEFAULT_PERIOD_SIZE_IN_MILLISECONDS_LOW_LATENCY 10 -#endif - -/* The default buffer size in milliseconds for conservative mode. */ -#ifndef MA_DEFAULT_PERIOD_SIZE_IN_MILLISECONDS_CONSERVATIVE -#define MA_DEFAULT_PERIOD_SIZE_IN_MILLISECONDS_CONSERVATIVE 100 -#endif - -/* The default LPF filter order for linear resampling. Note that this is clamped to MA_MAX_FILTER_ORDER. */ -#ifndef MA_DEFAULT_RESAMPLER_LPF_ORDER - #if MA_MAX_FILTER_ORDER >= 4 - #define MA_DEFAULT_RESAMPLER_LPF_ORDER 4 - #else - #define MA_DEFAULT_RESAMPLER_LPF_ORDER MA_MAX_FILTER_ORDER - #endif -#endif - - -#if defined(__clang__) || (defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6))) - #pragma GCC diagnostic push - #pragma GCC diagnostic ignored "-Wunused-variable" -#endif - -/* Standard sample rates, in order of priority. */ -static ma_uint32 g_maStandardSampleRatePriorities[] = { - (ma_uint32)ma_standard_sample_rate_48000, - (ma_uint32)ma_standard_sample_rate_44100, - - (ma_uint32)ma_standard_sample_rate_32000, - (ma_uint32)ma_standard_sample_rate_24000, - (ma_uint32)ma_standard_sample_rate_22050, - - (ma_uint32)ma_standard_sample_rate_88200, - (ma_uint32)ma_standard_sample_rate_96000, - (ma_uint32)ma_standard_sample_rate_176400, - (ma_uint32)ma_standard_sample_rate_192000, - - (ma_uint32)ma_standard_sample_rate_16000, - (ma_uint32)ma_standard_sample_rate_11025, - (ma_uint32)ma_standard_sample_rate_8000, - - (ma_uint32)ma_standard_sample_rate_352800, - (ma_uint32)ma_standard_sample_rate_384000 -}; - -static MA_INLINE ma_bool32 ma_is_standard_sample_rate(ma_uint32 sampleRate) -{ - ma_uint32 iSampleRate; - - for (iSampleRate = 0; iSampleRate < sizeof(g_maStandardSampleRatePriorities) / sizeof(g_maStandardSampleRatePriorities[0]); iSampleRate += 1) { - if (g_maStandardSampleRatePriorities[iSampleRate] == sampleRate) { - return MA_TRUE; - } - } - - /* Getting here means the sample rate is not supported. */ - return MA_FALSE; -} - - -static ma_format g_maFormatPriorities[] = { - ma_format_s16, /* Most common */ - ma_format_f32, - - /*ma_format_s24_32,*/ /* Clean alignment */ - ma_format_s32, - - ma_format_s24, /* Unclean alignment */ - - ma_format_u8 /* Low quality */ -}; -#if defined(__clang__) || (defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6))) - #pragma GCC diagnostic pop -#endif - - -MA_API void ma_version(ma_uint32* pMajor, ma_uint32* pMinor, ma_uint32* pRevision) -{ - if (pMajor) { - *pMajor = MA_VERSION_MAJOR; - } - - if (pMinor) { - *pMinor = MA_VERSION_MINOR; - } - - if (pRevision) { - *pRevision = MA_VERSION_REVISION; - } -} - -MA_API const char* ma_version_string(void) -{ - return MA_VERSION_STRING; -} - - -/****************************************************************************** - -Standard Library Stuff - -******************************************************************************/ -#ifndef MA_ASSERT -#define MA_ASSERT(condition) assert(condition) -#endif - -#ifndef MA_MALLOC -#define MA_MALLOC(sz) malloc((sz)) -#endif -#ifndef MA_REALLOC -#define MA_REALLOC(p, sz) realloc((p), (sz)) -#endif -#ifndef MA_FREE -#define MA_FREE(p) free((p)) -#endif - -static MA_INLINE void ma_zero_memory_default(void* p, size_t sz) -{ - if (p == NULL) { - MA_ASSERT(sz == 0); /* If this is triggered there's an error with the calling code. */ - return; - } - - if (sz > 0) { - memset(p, 0, sz); - } -} - - -#ifndef MA_ZERO_MEMORY -#define MA_ZERO_MEMORY(p, sz) ma_zero_memory_default((p), (sz)) -#endif -#ifndef MA_COPY_MEMORY -#define MA_COPY_MEMORY(dst, src, sz) memcpy((dst), (src), (sz)) -#endif -#ifndef MA_MOVE_MEMORY -#define MA_MOVE_MEMORY(dst, src, sz) memmove((dst), (src), (sz)) -#endif - -#define MA_ZERO_OBJECT(p) MA_ZERO_MEMORY((p), sizeof(*(p))) - -#define ma_countof(x) (sizeof(x) / sizeof(x[0])) -#define ma_max(x, y) (((x) > (y)) ? (x) : (y)) -#define ma_min(x, y) (((x) < (y)) ? (x) : (y)) -#define ma_abs(x) (((x) > 0) ? (x) : -(x)) -#define ma_clamp(x, lo, hi) (ma_max(lo, ma_min(x, hi))) -#define ma_offset_ptr(p, offset) (((ma_uint8*)(p)) + (offset)) -#define ma_align(x, a) (((x) + ((a)-1)) & ~((a)-1)) -#define ma_align_64(x) ma_align(x, 8) - -#define ma_buffer_frame_capacity(buffer, channels, format) (sizeof(buffer) / ma_get_bytes_per_sample(format) / (channels)) - -static MA_INLINE double ma_sind(double x) -{ - /* TODO: Implement custom sin(x). */ - return sin(x); -} - -static MA_INLINE double ma_expd(double x) -{ - /* TODO: Implement custom exp(x). */ - return exp(x); -} - -static MA_INLINE double ma_logd(double x) -{ - /* TODO: Implement custom log(x). */ - return log(x); -} - -static MA_INLINE double ma_powd(double x, double y) -{ - /* TODO: Implement custom pow(x, y). */ - return pow(x, y); -} - -static MA_INLINE double ma_sqrtd(double x) -{ - /* TODO: Implement custom sqrt(x). */ - return sqrt(x); -} - - -static MA_INLINE float ma_rsqrtf(float x) -{ - #if defined(MA_SUPPORT_SSE2) && !defined(MA_NO_SSE2) && (defined(MA_X64) || (defined(_M_IX86_FP) && _M_IX86_FP == 2) || defined(__SSE2__)) - { - /* - For SSE we can use RSQRTSS. - - This Stack Overflow post suggests that compilers don't necessarily generate optimal code - when using intrinsics: - - https://web.archive.org/web/20221211012522/https://stackoverflow.com/questions/32687079/getting-fewest-instructions-for-rsqrtss-wrapper - - I'm going to do something similar here, but a bit simpler. - */ - #if defined(__GNUC__) || defined(__clang__) - { - float result; - __asm__ __volatile__("rsqrtss %1, %0" : "=x"(result) : "x"(x)); - return result; - } - #else - { - return _mm_cvtss_f32(_mm_rsqrt_ss(_mm_set_ps1(x))); - } - #endif - } - #else - { - return 1 / (float)ma_sqrtd(x); - } - #endif -} - - -static MA_INLINE float ma_sinf(float x) -{ - return (float)ma_sind((float)x); -} - -static MA_INLINE double ma_cosd(double x) -{ - return ma_sind((MA_PI_D*0.5) - x); -} - -static MA_INLINE float ma_cosf(float x) -{ - return (float)ma_cosd((float)x); -} - -static MA_INLINE double ma_log10d(double x) -{ - return ma_logd(x) * 0.43429448190325182765; -} - -static MA_INLINE float ma_powf(float x, float y) -{ - return (float)ma_powd((double)x, (double)y); -} - -static MA_INLINE float ma_log10f(float x) -{ - return (float)ma_log10d((double)x); -} - - -static MA_INLINE double ma_degrees_to_radians(double degrees) -{ - return degrees * 0.01745329252; -} - -static MA_INLINE double ma_radians_to_degrees(double radians) -{ - return radians * 57.295779512896; -} - -static MA_INLINE float ma_degrees_to_radians_f(float degrees) -{ - return degrees * 0.01745329252f; -} - -static MA_INLINE float ma_radians_to_degrees_f(float radians) -{ - return radians * 57.295779512896f; -} - - -/* -Return Values: - 0: Success - 22: EINVAL - 34: ERANGE - -Not using symbolic constants for errors because I want to avoid #including errno.h - -These are marked as no-inline because of some bad code generation by Clang. None of these functions -are used in any performance-critical code within miniaudio. -*/ -MA_API MA_NO_INLINE int ma_strcpy_s(char* dst, size_t dstSizeInBytes, const char* src) -{ - size_t i; - - if (dst == 0) { - return 22; - } - if (dstSizeInBytes == 0) { - return 34; - } - if (src == 0) { - dst[0] = '\0'; - return 22; - } - - for (i = 0; i < dstSizeInBytes && src[i] != '\0'; ++i) { - dst[i] = src[i]; - } - - if (i < dstSizeInBytes) { - dst[i] = '\0'; - return 0; - } - - dst[0] = '\0'; - return 34; -} - -MA_API MA_NO_INLINE int ma_wcscpy_s(wchar_t* dst, size_t dstCap, const wchar_t* src) -{ - size_t i; - - if (dst == 0) { - return 22; - } - if (dstCap == 0) { - return 34; - } - if (src == 0) { - dst[0] = '\0'; - return 22; - } - - for (i = 0; i < dstCap && src[i] != '\0'; ++i) { - dst[i] = src[i]; - } - - if (i < dstCap) { - dst[i] = '\0'; - return 0; - } - - dst[0] = '\0'; - return 34; -} - - -MA_API MA_NO_INLINE int ma_strncpy_s(char* dst, size_t dstSizeInBytes, const char* src, size_t count) -{ - size_t maxcount; - size_t i; - - if (dst == 0) { - return 22; - } - if (dstSizeInBytes == 0) { - return 34; - } - if (src == 0) { - dst[0] = '\0'; - return 22; - } - - maxcount = count; - if (count == ((size_t)-1) || count >= dstSizeInBytes) { /* -1 = _TRUNCATE */ - maxcount = dstSizeInBytes - 1; - } - - for (i = 0; i < maxcount && src[i] != '\0'; ++i) { - dst[i] = src[i]; - } - - if (src[i] == '\0' || i == count || count == ((size_t)-1)) { - dst[i] = '\0'; - return 0; - } - - dst[0] = '\0'; - return 34; -} - -MA_API MA_NO_INLINE int ma_strcat_s(char* dst, size_t dstSizeInBytes, const char* src) -{ - char* dstorig; - - if (dst == 0) { - return 22; - } - if (dstSizeInBytes == 0) { - return 34; - } - if (src == 0) { - dst[0] = '\0'; - return 22; - } - - dstorig = dst; - - while (dstSizeInBytes > 0 && dst[0] != '\0') { - dst += 1; - dstSizeInBytes -= 1; - } - - if (dstSizeInBytes == 0) { - return 22; /* Unterminated. */ - } - - - while (dstSizeInBytes > 0 && src[0] != '\0') { - *dst++ = *src++; - dstSizeInBytes -= 1; - } - - if (dstSizeInBytes > 0) { - dst[0] = '\0'; - } else { - dstorig[0] = '\0'; - return 34; - } - - return 0; -} - -MA_API MA_NO_INLINE int ma_strncat_s(char* dst, size_t dstSizeInBytes, const char* src, size_t count) -{ - char* dstorig; - - if (dst == 0) { - return 22; - } - if (dstSizeInBytes == 0) { - return 34; - } - if (src == 0) { - return 22; - } - - dstorig = dst; - - while (dstSizeInBytes > 0 && dst[0] != '\0') { - dst += 1; - dstSizeInBytes -= 1; - } - - if (dstSizeInBytes == 0) { - return 22; /* Unterminated. */ - } - - - if (count == ((size_t)-1)) { /* _TRUNCATE */ - count = dstSizeInBytes - 1; - } - - while (dstSizeInBytes > 0 && src[0] != '\0' && count > 0) { - *dst++ = *src++; - dstSizeInBytes -= 1; - count -= 1; - } - - if (dstSizeInBytes > 0) { - dst[0] = '\0'; - } else { - dstorig[0] = '\0'; - return 34; - } - - return 0; -} - -MA_API MA_NO_INLINE int ma_itoa_s(int value, char* dst, size_t dstSizeInBytes, int radix) -{ - int sign; - unsigned int valueU; - char* dstEnd; - - if (dst == NULL || dstSizeInBytes == 0) { - return 22; - } - if (radix < 2 || radix > 36) { - dst[0] = '\0'; - return 22; - } - - sign = (value < 0 && radix == 10) ? -1 : 1; /* The negative sign is only used when the base is 10. */ - - if (value < 0) { - valueU = -value; - } else { - valueU = value; - } - - dstEnd = dst; - do - { - int remainder = valueU % radix; - if (remainder > 9) { - *dstEnd = (char)((remainder - 10) + 'a'); - } else { - *dstEnd = (char)(remainder + '0'); - } - - dstEnd += 1; - dstSizeInBytes -= 1; - valueU /= radix; - } while (dstSizeInBytes > 0 && valueU > 0); - - if (dstSizeInBytes == 0) { - dst[0] = '\0'; - return 22; /* Ran out of room in the output buffer. */ - } - - if (sign < 0) { - *dstEnd++ = '-'; - dstSizeInBytes -= 1; - } - - if (dstSizeInBytes == 0) { - dst[0] = '\0'; - return 22; /* Ran out of room in the output buffer. */ - } - - *dstEnd = '\0'; - - - /* At this point the string will be reversed. */ - dstEnd -= 1; - while (dst < dstEnd) { - char temp = *dst; - *dst = *dstEnd; - *dstEnd = temp; - - dst += 1; - dstEnd -= 1; - } - - return 0; -} - -MA_API MA_NO_INLINE int ma_strcmp(const char* str1, const char* str2) -{ - if (str1 == str2) return 0; - - /* These checks differ from the standard implementation. It's not important, but I prefer it just for sanity. */ - if (str1 == NULL) return -1; - if (str2 == NULL) return 1; - - for (;;) { - if (str1[0] == '\0') { - break; - } - if (str1[0] != str2[0]) { - break; - } - - str1 += 1; - str2 += 1; - } - - return ((unsigned char*)str1)[0] - ((unsigned char*)str2)[0]; -} - -MA_API MA_NO_INLINE int ma_strappend(char* dst, size_t dstSize, const char* srcA, const char* srcB) -{ - int result; - - result = ma_strncpy_s(dst, dstSize, srcA, (size_t)-1); - if (result != 0) { - return result; - } - - result = ma_strncat_s(dst, dstSize, srcB, (size_t)-1); - if (result != 0) { - return result; - } - - return result; -} - -MA_API MA_NO_INLINE char* ma_copy_string(const char* src, const ma_allocation_callbacks* pAllocationCallbacks) -{ - size_t sz; - char* dst; - - if (src == NULL) { - return NULL; - } - - sz = strlen(src)+1; - dst = (char*)ma_malloc(sz, pAllocationCallbacks); - if (dst == NULL) { - return NULL; - } - - ma_strcpy_s(dst, sz, src); - - return dst; -} - -MA_API MA_NO_INLINE wchar_t* ma_copy_string_w(const wchar_t* src, const ma_allocation_callbacks* pAllocationCallbacks) -{ - size_t sz = wcslen(src)+1; - wchar_t* dst = (wchar_t*)ma_malloc(sz * sizeof(*dst), pAllocationCallbacks); - if (dst == NULL) { - return NULL; - } - - ma_wcscpy_s(dst, sz, src); - - return dst; -} - - - -#include -static ma_result ma_result_from_errno(int e) -{ - if (e == 0) { - return MA_SUCCESS; - } -#ifdef EPERM - else if (e == EPERM) { return MA_INVALID_OPERATION; } -#endif -#ifdef ENOENT - else if (e == ENOENT) { return MA_DOES_NOT_EXIST; } -#endif -#ifdef ESRCH - else if (e == ESRCH) { return MA_DOES_NOT_EXIST; } -#endif -#ifdef EINTR - else if (e == EINTR) { return MA_INTERRUPT; } -#endif -#ifdef EIO - else if (e == EIO) { return MA_IO_ERROR; } -#endif -#ifdef ENXIO - else if (e == ENXIO) { return MA_DOES_NOT_EXIST; } -#endif -#ifdef E2BIG - else if (e == E2BIG) { return MA_INVALID_ARGS; } -#endif -#ifdef ENOEXEC - else if (e == ENOEXEC) { return MA_INVALID_FILE; } -#endif -#ifdef EBADF - else if (e == EBADF) { return MA_INVALID_FILE; } -#endif -#ifdef ECHILD - else if (e == ECHILD) { return MA_ERROR; } -#endif -#ifdef EAGAIN - else if (e == EAGAIN) { return MA_UNAVAILABLE; } -#endif -#ifdef ENOMEM - else if (e == ENOMEM) { return MA_OUT_OF_MEMORY; } -#endif -#ifdef EACCES - else if (e == EACCES) { return MA_ACCESS_DENIED; } -#endif -#ifdef EFAULT - else if (e == EFAULT) { return MA_BAD_ADDRESS; } -#endif -#ifdef ENOTBLK - else if (e == ENOTBLK) { return MA_ERROR; } -#endif -#ifdef EBUSY - else if (e == EBUSY) { return MA_BUSY; } -#endif -#ifdef EEXIST - else if (e == EEXIST) { return MA_ALREADY_EXISTS; } -#endif -#ifdef EXDEV - else if (e == EXDEV) { return MA_ERROR; } -#endif -#ifdef ENODEV - else if (e == ENODEV) { return MA_DOES_NOT_EXIST; } -#endif -#ifdef ENOTDIR - else if (e == ENOTDIR) { return MA_NOT_DIRECTORY; } -#endif -#ifdef EISDIR - else if (e == EISDIR) { return MA_IS_DIRECTORY; } -#endif -#ifdef EINVAL - else if (e == EINVAL) { return MA_INVALID_ARGS; } -#endif -#ifdef ENFILE - else if (e == ENFILE) { return MA_TOO_MANY_OPEN_FILES; } -#endif -#ifdef EMFILE - else if (e == EMFILE) { return MA_TOO_MANY_OPEN_FILES; } -#endif -#ifdef ENOTTY - else if (e == ENOTTY) { return MA_INVALID_OPERATION; } -#endif -#ifdef ETXTBSY - else if (e == ETXTBSY) { return MA_BUSY; } -#endif -#ifdef EFBIG - else if (e == EFBIG) { return MA_TOO_BIG; } -#endif -#ifdef ENOSPC - else if (e == ENOSPC) { return MA_NO_SPACE; } -#endif -#ifdef ESPIPE - else if (e == ESPIPE) { return MA_BAD_SEEK; } -#endif -#ifdef EROFS - else if (e == EROFS) { return MA_ACCESS_DENIED; } -#endif -#ifdef EMLINK - else if (e == EMLINK) { return MA_TOO_MANY_LINKS; } -#endif -#ifdef EPIPE - else if (e == EPIPE) { return MA_BAD_PIPE; } -#endif -#ifdef EDOM - else if (e == EDOM) { return MA_OUT_OF_RANGE; } -#endif -#ifdef ERANGE - else if (e == ERANGE) { return MA_OUT_OF_RANGE; } -#endif -#ifdef EDEADLK - else if (e == EDEADLK) { return MA_DEADLOCK; } -#endif -#ifdef ENAMETOOLONG - else if (e == ENAMETOOLONG) { return MA_PATH_TOO_LONG; } -#endif -#ifdef ENOLCK - else if (e == ENOLCK) { return MA_ERROR; } -#endif -#ifdef ENOSYS - else if (e == ENOSYS) { return MA_NOT_IMPLEMENTED; } -#endif -#ifdef ENOTEMPTY - else if (e == ENOTEMPTY) { return MA_DIRECTORY_NOT_EMPTY; } -#endif -#ifdef ELOOP - else if (e == ELOOP) { return MA_TOO_MANY_LINKS; } -#endif -#ifdef ENOMSG - else if (e == ENOMSG) { return MA_NO_MESSAGE; } -#endif -#ifdef EIDRM - else if (e == EIDRM) { return MA_ERROR; } -#endif -#ifdef ECHRNG - else if (e == ECHRNG) { return MA_ERROR; } -#endif -#ifdef EL2NSYNC - else if (e == EL2NSYNC) { return MA_ERROR; } -#endif -#ifdef EL3HLT - else if (e == EL3HLT) { return MA_ERROR; } -#endif -#ifdef EL3RST - else if (e == EL3RST) { return MA_ERROR; } -#endif -#ifdef ELNRNG - else if (e == ELNRNG) { return MA_OUT_OF_RANGE; } -#endif -#ifdef EUNATCH - else if (e == EUNATCH) { return MA_ERROR; } -#endif -#ifdef ENOCSI - else if (e == ENOCSI) { return MA_ERROR; } -#endif -#ifdef EL2HLT - else if (e == EL2HLT) { return MA_ERROR; } -#endif -#ifdef EBADE - else if (e == EBADE) { return MA_ERROR; } -#endif -#ifdef EBADR - else if (e == EBADR) { return MA_ERROR; } -#endif -#ifdef EXFULL - else if (e == EXFULL) { return MA_ERROR; } -#endif -#ifdef ENOANO - else if (e == ENOANO) { return MA_ERROR; } -#endif -#ifdef EBADRQC - else if (e == EBADRQC) { return MA_ERROR; } -#endif -#ifdef EBADSLT - else if (e == EBADSLT) { return MA_ERROR; } -#endif -#ifdef EBFONT - else if (e == EBFONT) { return MA_INVALID_FILE; } -#endif -#ifdef ENOSTR - else if (e == ENOSTR) { return MA_ERROR; } -#endif -#ifdef ENODATA - else if (e == ENODATA) { return MA_NO_DATA_AVAILABLE; } -#endif -#ifdef ETIME - else if (e == ETIME) { return MA_TIMEOUT; } -#endif -#ifdef ENOSR - else if (e == ENOSR) { return MA_NO_DATA_AVAILABLE; } -#endif -#ifdef ENONET - else if (e == ENONET) { return MA_NO_NETWORK; } -#endif -#ifdef ENOPKG - else if (e == ENOPKG) { return MA_ERROR; } -#endif -#ifdef EREMOTE - else if (e == EREMOTE) { return MA_ERROR; } -#endif -#ifdef ENOLINK - else if (e == ENOLINK) { return MA_ERROR; } -#endif -#ifdef EADV - else if (e == EADV) { return MA_ERROR; } -#endif -#ifdef ESRMNT - else if (e == ESRMNT) { return MA_ERROR; } -#endif -#ifdef ECOMM - else if (e == ECOMM) { return MA_ERROR; } -#endif -#ifdef EPROTO - else if (e == EPROTO) { return MA_ERROR; } -#endif -#ifdef EMULTIHOP - else if (e == EMULTIHOP) { return MA_ERROR; } -#endif -#ifdef EDOTDOT - else if (e == EDOTDOT) { return MA_ERROR; } -#endif -#ifdef EBADMSG - else if (e == EBADMSG) { return MA_BAD_MESSAGE; } -#endif -#ifdef EOVERFLOW - else if (e == EOVERFLOW) { return MA_TOO_BIG; } -#endif -#ifdef ENOTUNIQ - else if (e == ENOTUNIQ) { return MA_NOT_UNIQUE; } -#endif -#ifdef EBADFD - else if (e == EBADFD) { return MA_ERROR; } -#endif -#ifdef EREMCHG - else if (e == EREMCHG) { return MA_ERROR; } -#endif -#ifdef ELIBACC - else if (e == ELIBACC) { return MA_ACCESS_DENIED; } -#endif -#ifdef ELIBBAD - else if (e == ELIBBAD) { return MA_INVALID_FILE; } -#endif -#ifdef ELIBSCN - else if (e == ELIBSCN) { return MA_INVALID_FILE; } -#endif -#ifdef ELIBMAX - else if (e == ELIBMAX) { return MA_ERROR; } -#endif -#ifdef ELIBEXEC - else if (e == ELIBEXEC) { return MA_ERROR; } -#endif -#ifdef EILSEQ - else if (e == EILSEQ) { return MA_INVALID_DATA; } -#endif -#ifdef ERESTART - else if (e == ERESTART) { return MA_ERROR; } -#endif -#ifdef ESTRPIPE - else if (e == ESTRPIPE) { return MA_ERROR; } -#endif -#ifdef EUSERS - else if (e == EUSERS) { return MA_ERROR; } -#endif -#ifdef ENOTSOCK - else if (e == ENOTSOCK) { return MA_NOT_SOCKET; } -#endif -#ifdef EDESTADDRREQ - else if (e == EDESTADDRREQ) { return MA_NO_ADDRESS; } -#endif -#ifdef EMSGSIZE - else if (e == EMSGSIZE) { return MA_TOO_BIG; } -#endif -#ifdef EPROTOTYPE - else if (e == EPROTOTYPE) { return MA_BAD_PROTOCOL; } -#endif -#ifdef ENOPROTOOPT - else if (e == ENOPROTOOPT) { return MA_PROTOCOL_UNAVAILABLE; } -#endif -#ifdef EPROTONOSUPPORT - else if (e == EPROTONOSUPPORT) { return MA_PROTOCOL_NOT_SUPPORTED; } -#endif -#ifdef ESOCKTNOSUPPORT - else if (e == ESOCKTNOSUPPORT) { return MA_SOCKET_NOT_SUPPORTED; } -#endif -#ifdef EOPNOTSUPP - else if (e == EOPNOTSUPP) { return MA_INVALID_OPERATION; } -#endif -#ifdef EPFNOSUPPORT - else if (e == EPFNOSUPPORT) { return MA_PROTOCOL_FAMILY_NOT_SUPPORTED; } -#endif -#ifdef EAFNOSUPPORT - else if (e == EAFNOSUPPORT) { return MA_ADDRESS_FAMILY_NOT_SUPPORTED; } -#endif -#ifdef EADDRINUSE - else if (e == EADDRINUSE) { return MA_ALREADY_IN_USE; } -#endif -#ifdef EADDRNOTAVAIL - else if (e == EADDRNOTAVAIL) { return MA_ERROR; } -#endif -#ifdef ENETDOWN - else if (e == ENETDOWN) { return MA_NO_NETWORK; } -#endif -#ifdef ENETUNREACH - else if (e == ENETUNREACH) { return MA_NO_NETWORK; } -#endif -#ifdef ENETRESET - else if (e == ENETRESET) { return MA_NO_NETWORK; } -#endif -#ifdef ECONNABORTED - else if (e == ECONNABORTED) { return MA_NO_NETWORK; } -#endif -#ifdef ECONNRESET - else if (e == ECONNRESET) { return MA_CONNECTION_RESET; } -#endif -#ifdef ENOBUFS - else if (e == ENOBUFS) { return MA_NO_SPACE; } -#endif -#ifdef EISCONN - else if (e == EISCONN) { return MA_ALREADY_CONNECTED; } -#endif -#ifdef ENOTCONN - else if (e == ENOTCONN) { return MA_NOT_CONNECTED; } -#endif -#ifdef ESHUTDOWN - else if (e == ESHUTDOWN) { return MA_ERROR; } -#endif -#ifdef ETOOMANYREFS - else if (e == ETOOMANYREFS) { return MA_ERROR; } -#endif -#ifdef ETIMEDOUT - else if (e == ETIMEDOUT) { return MA_TIMEOUT; } -#endif -#ifdef ECONNREFUSED - else if (e == ECONNREFUSED) { return MA_CONNECTION_REFUSED; } -#endif -#ifdef EHOSTDOWN - else if (e == EHOSTDOWN) { return MA_NO_HOST; } -#endif -#ifdef EHOSTUNREACH - else if (e == EHOSTUNREACH) { return MA_NO_HOST; } -#endif -#ifdef EALREADY - else if (e == EALREADY) { return MA_IN_PROGRESS; } -#endif -#ifdef EINPROGRESS - else if (e == EINPROGRESS) { return MA_IN_PROGRESS; } -#endif -#ifdef ESTALE - else if (e == ESTALE) { return MA_INVALID_FILE; } -#endif -#ifdef EUCLEAN - else if (e == EUCLEAN) { return MA_ERROR; } -#endif -#ifdef ENOTNAM - else if (e == ENOTNAM) { return MA_ERROR; } -#endif -#ifdef ENAVAIL - else if (e == ENAVAIL) { return MA_ERROR; } -#endif -#ifdef EISNAM - else if (e == EISNAM) { return MA_ERROR; } -#endif -#ifdef EREMOTEIO - else if (e == EREMOTEIO) { return MA_IO_ERROR; } -#endif -#ifdef EDQUOT - else if (e == EDQUOT) { return MA_NO_SPACE; } -#endif -#ifdef ENOMEDIUM - else if (e == ENOMEDIUM) { return MA_DOES_NOT_EXIST; } -#endif -#ifdef EMEDIUMTYPE - else if (e == EMEDIUMTYPE) { return MA_ERROR; } -#endif -#ifdef ECANCELED - else if (e == ECANCELED) { return MA_CANCELLED; } -#endif -#ifdef ENOKEY - else if (e == ENOKEY) { return MA_ERROR; } -#endif -#ifdef EKEYEXPIRED - else if (e == EKEYEXPIRED) { return MA_ERROR; } -#endif -#ifdef EKEYREVOKED - else if (e == EKEYREVOKED) { return MA_ERROR; } -#endif -#ifdef EKEYREJECTED - else if (e == EKEYREJECTED) { return MA_ERROR; } -#endif -#ifdef EOWNERDEAD - else if (e == EOWNERDEAD) { return MA_ERROR; } -#endif -#ifdef ENOTRECOVERABLE - else if (e == ENOTRECOVERABLE) { return MA_ERROR; } -#endif -#ifdef ERFKILL - else if (e == ERFKILL) { return MA_ERROR; } -#endif -#ifdef EHWPOISON - else if (e == EHWPOISON) { return MA_ERROR; } -#endif - else { - return MA_ERROR; - } -} - -MA_API ma_result ma_fopen(FILE** ppFile, const char* pFilePath, const char* pOpenMode) -{ -#if defined(_MSC_VER) && _MSC_VER >= 1400 - errno_t err; -#endif - - if (ppFile != NULL) { - *ppFile = NULL; /* Safety. */ - } - - if (pFilePath == NULL || pOpenMode == NULL || ppFile == NULL) { - return MA_INVALID_ARGS; - } - -#if defined(_MSC_VER) && _MSC_VER >= 1400 - err = fopen_s(ppFile, pFilePath, pOpenMode); - if (err != 0) { - return ma_result_from_errno(err); - } -#else -#if defined(_WIN32) || defined(__APPLE__) - *ppFile = fopen(pFilePath, pOpenMode); -#else - #if defined(_FILE_OFFSET_BITS) && _FILE_OFFSET_BITS == 64 && defined(_LARGEFILE64_SOURCE) - *ppFile = fopen64(pFilePath, pOpenMode); - #else - *ppFile = fopen(pFilePath, pOpenMode); - #endif -#endif - if (*ppFile == NULL) { - ma_result result = ma_result_from_errno(errno); - if (result == MA_SUCCESS) { - result = MA_ERROR; /* Just a safety check to make sure we never ever return success when pFile == NULL. */ - } - - return result; - } -#endif - - return MA_SUCCESS; -} - - - -/* -_wfopen() isn't always available in all compilation environments. - - * Windows only. - * MSVC seems to support it universally as far back as VC6 from what I can tell (haven't checked further back). - * MinGW-64 (both 32- and 64-bit) seems to support it. - * MinGW wraps it in !defined(__STRICT_ANSI__). - * OpenWatcom wraps it in !defined(_NO_EXT_KEYS). - -This can be reviewed as compatibility issues arise. The preference is to use _wfopen_s() and _wfopen() as opposed to the wcsrtombs() -fallback, so if you notice your compiler not detecting this properly I'm happy to look at adding support. -*/ -#if defined(_WIN32) - #if defined(_MSC_VER) || defined(__MINGW64__) || (!defined(__STRICT_ANSI__) && !defined(_NO_EXT_KEYS)) - #define MA_HAS_WFOPEN - #endif -#endif - -MA_API ma_result ma_wfopen(FILE** ppFile, const wchar_t* pFilePath, const wchar_t* pOpenMode, const ma_allocation_callbacks* pAllocationCallbacks) -{ - if (ppFile != NULL) { - *ppFile = NULL; /* Safety. */ - } - - if (pFilePath == NULL || pOpenMode == NULL || ppFile == NULL) { - return MA_INVALID_ARGS; - } - -#if defined(MA_HAS_WFOPEN) - { - /* Use _wfopen() on Windows. */ - #if defined(_MSC_VER) && _MSC_VER >= 1400 - errno_t err = _wfopen_s(ppFile, pFilePath, pOpenMode); - if (err != 0) { - return ma_result_from_errno(err); - } - #else - *ppFile = _wfopen(pFilePath, pOpenMode); - if (*ppFile == NULL) { - return ma_result_from_errno(errno); - } - #endif - (void)pAllocationCallbacks; - } -#else - /* - Use fopen() on anything other than Windows. Requires a conversion. This is annoying because fopen() is locale specific. The only real way I can - think of to do this is with wcsrtombs(). Note that wcstombs() is apparently not thread-safe because it uses a static global mbstate_t object for - maintaining state. I've checked this with -std=c89 and it works, but if somebody get's a compiler error I'll look into improving compatibility. - */ - { - mbstate_t mbs; - size_t lenMB; - const wchar_t* pFilePathTemp = pFilePath; - char* pFilePathMB = NULL; - char pOpenModeMB[32] = {0}; - - /* Get the length first. */ - MA_ZERO_OBJECT(&mbs); - lenMB = wcsrtombs(NULL, &pFilePathTemp, 0, &mbs); - if (lenMB == (size_t)-1) { - return ma_result_from_errno(errno); - } - - pFilePathMB = (char*)ma_malloc(lenMB + 1, pAllocationCallbacks); - if (pFilePathMB == NULL) { - return MA_OUT_OF_MEMORY; - } - - pFilePathTemp = pFilePath; - MA_ZERO_OBJECT(&mbs); - wcsrtombs(pFilePathMB, &pFilePathTemp, lenMB + 1, &mbs); - - /* The open mode should always consist of ASCII characters so we should be able to do a trivial conversion. */ - { - size_t i = 0; - for (;;) { - if (pOpenMode[i] == 0) { - pOpenModeMB[i] = '\0'; - break; - } - - pOpenModeMB[i] = (char)pOpenMode[i]; - i += 1; - } - } - - *ppFile = fopen(pFilePathMB, pOpenModeMB); - - ma_free(pFilePathMB, pAllocationCallbacks); - } - - if (*ppFile == NULL) { - return MA_ERROR; - } -#endif - - return MA_SUCCESS; -} - - - -static MA_INLINE void ma_copy_memory_64(void* dst, const void* src, ma_uint64 sizeInBytes) -{ -#if 0xFFFFFFFFFFFFFFFF <= MA_SIZE_MAX - MA_COPY_MEMORY(dst, src, (size_t)sizeInBytes); -#else - while (sizeInBytes > 0) { - ma_uint64 bytesToCopyNow = sizeInBytes; - if (bytesToCopyNow > MA_SIZE_MAX) { - bytesToCopyNow = MA_SIZE_MAX; - } - - MA_COPY_MEMORY(dst, src, (size_t)bytesToCopyNow); /* Safe cast to size_t. */ - - sizeInBytes -= bytesToCopyNow; - dst = ( void*)(( ma_uint8*)dst + bytesToCopyNow); - src = (const void*)((const ma_uint8*)src + bytesToCopyNow); - } -#endif -} - -static MA_INLINE void ma_zero_memory_64(void* dst, ma_uint64 sizeInBytes) -{ -#if 0xFFFFFFFFFFFFFFFF <= MA_SIZE_MAX - MA_ZERO_MEMORY(dst, (size_t)sizeInBytes); -#else - while (sizeInBytes > 0) { - ma_uint64 bytesToZeroNow = sizeInBytes; - if (bytesToZeroNow > MA_SIZE_MAX) { - bytesToZeroNow = MA_SIZE_MAX; - } - - MA_ZERO_MEMORY(dst, (size_t)bytesToZeroNow); /* Safe cast to size_t. */ - - sizeInBytes -= bytesToZeroNow; - dst = (void*)((ma_uint8*)dst + bytesToZeroNow); - } -#endif -} - - -/* Thanks to good old Bit Twiddling Hacks for this one: http://graphics.stanford.edu/~seander/bithacks.html#RoundUpPowerOf2 */ -static MA_INLINE unsigned int ma_next_power_of_2(unsigned int x) -{ - x--; - x |= x >> 1; - x |= x >> 2; - x |= x >> 4; - x |= x >> 8; - x |= x >> 16; - x++; - - return x; -} - -static MA_INLINE unsigned int ma_prev_power_of_2(unsigned int x) -{ - return ma_next_power_of_2(x) >> 1; -} - -static MA_INLINE unsigned int ma_round_to_power_of_2(unsigned int x) -{ - unsigned int prev = ma_prev_power_of_2(x); - unsigned int next = ma_next_power_of_2(x); - if ((next - x) > (x - prev)) { - return prev; - } else { - return next; - } -} - -static MA_INLINE unsigned int ma_count_set_bits(unsigned int x) -{ - unsigned int count = 0; - while (x != 0) { - if (x & 1) { - count += 1; - } - - x = x >> 1; - } - - return count; -} - - - -/************************************************************************************************************************************************************** - -Allocation Callbacks - -**************************************************************************************************************************************************************/ -static void* ma__malloc_default(size_t sz, void* pUserData) -{ - (void)pUserData; - return MA_MALLOC(sz); -} - -static void* ma__realloc_default(void* p, size_t sz, void* pUserData) -{ - (void)pUserData; - return MA_REALLOC(p, sz); -} - -static void ma__free_default(void* p, void* pUserData) -{ - (void)pUserData; - MA_FREE(p); -} - -static ma_allocation_callbacks ma_allocation_callbacks_init_default(void) -{ - ma_allocation_callbacks callbacks; - callbacks.pUserData = NULL; - callbacks.onMalloc = ma__malloc_default; - callbacks.onRealloc = ma__realloc_default; - callbacks.onFree = ma__free_default; - - return callbacks; -} - -static ma_result ma_allocation_callbacks_init_copy(ma_allocation_callbacks* pDst, const ma_allocation_callbacks* pSrc) -{ - if (pDst == NULL) { - return MA_INVALID_ARGS; - } - - if (pSrc == NULL) { - *pDst = ma_allocation_callbacks_init_default(); - } else { - if (pSrc->pUserData == NULL && pSrc->onFree == NULL && pSrc->onMalloc == NULL && pSrc->onRealloc == NULL) { - *pDst = ma_allocation_callbacks_init_default(); - } else { - if (pSrc->onFree == NULL || (pSrc->onMalloc == NULL && pSrc->onRealloc == NULL)) { - return MA_INVALID_ARGS; /* Invalid allocation callbacks. */ - } else { - *pDst = *pSrc; - } - } - } - - return MA_SUCCESS; -} - - - - -/************************************************************************************************************************************************************** - -Logging - -**************************************************************************************************************************************************************/ -MA_API const char* ma_log_level_to_string(ma_uint32 logLevel) -{ - switch (logLevel) - { - case MA_LOG_LEVEL_DEBUG: return "DEBUG"; - case MA_LOG_LEVEL_INFO: return "INFO"; - case MA_LOG_LEVEL_WARNING: return "WARNING"; - case MA_LOG_LEVEL_ERROR: return "ERROR"; - default: return "ERROR"; - } -} - -#if defined(MA_DEBUG_OUTPUT) -#if defined(MA_ANDROID) - #include -#endif - -/* Customize this to use a specific tag in __android_log_print() for debug output messages. */ -#ifndef MA_ANDROID_LOG_TAG -#define MA_ANDROID_LOG_TAG "miniaudio" -#endif - -void ma_log_callback_debug(void* pUserData, ma_uint32 level, const char* pMessage) -{ - (void)pUserData; - - /* Special handling for some platforms. */ - #if defined(MA_ANDROID) - { - /* Android. */ - __android_log_print(ANDROID_LOG_DEBUG, MA_ANDROID_LOG_TAG, "%s: %s", ma_log_level_to_string(level), pMessage); - } - #else - { - /* Everything else. */ - printf("%s: %s", ma_log_level_to_string(level), pMessage); - } - #endif -} -#endif - -MA_API ma_log_callback ma_log_callback_init(ma_log_callback_proc onLog, void* pUserData) -{ - ma_log_callback callback; - - MA_ZERO_OBJECT(&callback); - callback.onLog = onLog; - callback.pUserData = pUserData; - - return callback; -} - - -MA_API ma_result ma_log_init(const ma_allocation_callbacks* pAllocationCallbacks, ma_log* pLog) -{ - if (pLog == NULL) { - return MA_INVALID_ARGS; - } - - MA_ZERO_OBJECT(pLog); - ma_allocation_callbacks_init_copy(&pLog->allocationCallbacks, pAllocationCallbacks); - - /* We need a mutex for thread safety. */ - #ifndef MA_NO_THREADING - { - ma_result result = ma_mutex_init(&pLog->lock); - if (result != MA_SUCCESS) { - return result; - } - } - #endif - - /* If we're using debug output, enable it. */ - #if defined(MA_DEBUG_OUTPUT) - { - ma_log_register_callback(pLog, ma_log_callback_init(ma_log_callback_debug, NULL)); /* Doesn't really matter if this fails. */ - } - #endif - - return MA_SUCCESS; -} - -MA_API void ma_log_uninit(ma_log* pLog) -{ - if (pLog == NULL) { - return; - } - -#ifndef MA_NO_THREADING - ma_mutex_uninit(&pLog->lock); -#endif -} - -static void ma_log_lock(ma_log* pLog) -{ -#ifndef MA_NO_THREADING - ma_mutex_lock(&pLog->lock); -#else - (void)pLog; -#endif -} - -static void ma_log_unlock(ma_log* pLog) -{ -#ifndef MA_NO_THREADING - ma_mutex_unlock(&pLog->lock); -#else - (void)pLog; -#endif -} - -MA_API ma_result ma_log_register_callback(ma_log* pLog, ma_log_callback callback) -{ - ma_result result = MA_SUCCESS; - - if (pLog == NULL || callback.onLog == NULL) { - return MA_INVALID_ARGS; - } - - ma_log_lock(pLog); - { - if (pLog->callbackCount == ma_countof(pLog->callbacks)) { - result = MA_OUT_OF_MEMORY; /* Reached the maximum allowed log callbacks. */ - } else { - pLog->callbacks[pLog->callbackCount] = callback; - pLog->callbackCount += 1; - } - } - ma_log_unlock(pLog); - - return result; -} - -MA_API ma_result ma_log_unregister_callback(ma_log* pLog, ma_log_callback callback) -{ - if (pLog == NULL) { - return MA_INVALID_ARGS; - } - - ma_log_lock(pLog); - { - ma_uint32 iLog; - for (iLog = 0; iLog < pLog->callbackCount; ) { - if (pLog->callbacks[iLog].onLog == callback.onLog) { - /* Found. Move everything down a slot. */ - ma_uint32 jLog; - for (jLog = iLog; jLog < pLog->callbackCount-1; jLog += 1) { - pLog->callbacks[jLog] = pLog->callbacks[jLog + 1]; - } - - pLog->callbackCount -= 1; - } else { - /* Not found. */ - iLog += 1; - } - } - } - ma_log_unlock(pLog); - - return MA_SUCCESS; -} - -MA_API ma_result ma_log_post(ma_log* pLog, ma_uint32 level, const char* pMessage) -{ - if (pLog == NULL || pMessage == NULL) { - return MA_INVALID_ARGS; - } - - ma_log_lock(pLog); - { - ma_uint32 iLog; - for (iLog = 0; iLog < pLog->callbackCount; iLog += 1) { - if (pLog->callbacks[iLog].onLog) { - pLog->callbacks[iLog].onLog(pLog->callbacks[iLog].pUserData, level, pMessage); - } - } - } - ma_log_unlock(pLog); - - return MA_SUCCESS; -} - - -/* -We need to emulate _vscprintf() for the VC6 build. This can be more efficient, but since it's only VC6, and it's just a -logging function, I'm happy to keep this simple. In the VC6 build we can implement this in terms of _vsnprintf(). -*/ -#if defined(_MSC_VER) && _MSC_VER < 1900 -static int ma_vscprintf(const ma_allocation_callbacks* pAllocationCallbacks, const char* format, va_list args) -{ -#if _MSC_VER > 1200 - return _vscprintf(format, args); -#else - int result; - char* pTempBuffer = NULL; - size_t tempBufferCap = 1024; - - if (format == NULL) { - errno = EINVAL; - return -1; - } - - for (;;) { - char* pNewTempBuffer = (char*)ma_realloc(pTempBuffer, tempBufferCap, pAllocationCallbacks); - if (pNewTempBuffer == NULL) { - ma_free(pTempBuffer, pAllocationCallbacks); - errno = ENOMEM; - return -1; /* Out of memory. */ - } - - pTempBuffer = pNewTempBuffer; - - result = _vsnprintf(pTempBuffer, tempBufferCap, format, args); - ma_free(pTempBuffer, NULL); - - if (result != -1) { - break; /* Got it. */ - } - - /* Buffer wasn't big enough. Ideally it'd be nice to use an error code to know the reason for sure, but this is reliable enough. */ - tempBufferCap *= 2; - } - - return result; -#endif -} -#endif - -MA_API ma_result ma_log_postv(ma_log* pLog, ma_uint32 level, const char* pFormat, va_list args) -{ - if (pLog == NULL || pFormat == NULL) { - return MA_INVALID_ARGS; - } - - #if (defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L) || ((!defined(_MSC_VER) || _MSC_VER >= 1900) && !defined(__STRICT_ANSI__) && !defined(_NO_EXT_KEYS)) || (defined(__cplusplus) && __cplusplus >= 201103L) - { - ma_result result; - int length; - char pFormattedMessageStack[1024]; - char* pFormattedMessageHeap = NULL; - - /* First try formatting into our fixed sized stack allocated buffer. If this is too small we'll fallback to a heap allocation. */ - length = vsnprintf(pFormattedMessageStack, sizeof(pFormattedMessageStack), pFormat, args); - if (length < 0) { - return MA_INVALID_OPERATION; /* An error occurred when trying to convert the buffer. */ - } - - if ((size_t)length < sizeof(pFormattedMessageStack)) { - /* The string was written to the stack. */ - result = ma_log_post(pLog, level, pFormattedMessageStack); - } else { - /* The stack buffer was too small, try the heap. */ - pFormattedMessageHeap = (char*)ma_malloc(length + 1, &pLog->allocationCallbacks); - if (pFormattedMessageHeap == NULL) { - return MA_OUT_OF_MEMORY; - } - - length = vsnprintf(pFormattedMessageHeap, length + 1, pFormat, args); - if (length < 0) { - ma_free(pFormattedMessageHeap, &pLog->allocationCallbacks); - return MA_INVALID_OPERATION; - } - - result = ma_log_post(pLog, level, pFormattedMessageHeap); - ma_free(pFormattedMessageHeap, &pLog->allocationCallbacks); - } - - return result; - } - #else - { - /* - Without snprintf() we need to first measure the string and then heap allocate it. I'm only aware of Visual Studio having support for this without snprintf(), so we'll - need to restrict this branch to Visual Studio. For other compilers we need to just not support formatted logging because I don't want the security risk of overflowing - a fixed sized stack allocated buffer. - */ - #if defined(_MSC_VER) && _MSC_VER >= 1200 /* 1200 = VC6 */ - { - ma_result result; - int formattedLen; - char* pFormattedMessage = NULL; - va_list args2; - - #if _MSC_VER >= 1800 - { - va_copy(args2, args); - } - #else - { - args2 = args; - } - #endif - - formattedLen = ma_vscprintf(&pLog->allocationCallbacks, pFormat, args2); - va_end(args2); - - if (formattedLen <= 0) { - return MA_INVALID_OPERATION; - } - - pFormattedMessage = (char*)ma_malloc(formattedLen + 1, &pLog->allocationCallbacks); - if (pFormattedMessage == NULL) { - return MA_OUT_OF_MEMORY; - } - - /* We'll get errors on newer versions of Visual Studio if we try to use vsprintf(). */ - #if _MSC_VER >= 1400 /* 1400 = Visual Studio 2005 */ - { - vsprintf_s(pFormattedMessage, formattedLen + 1, pFormat, args); - } - #else - { - vsprintf(pFormattedMessage, pFormat, args); - } - #endif - - result = ma_log_post(pLog, level, pFormattedMessage); - ma_free(pFormattedMessage, &pLog->allocationCallbacks); - - return result; - } - #else - { - /* Can't do anything because we don't have a safe way of to emulate vsnprintf() without a manual solution. */ - (void)level; - (void)args; - - return MA_INVALID_OPERATION; - } - #endif - } - #endif -} - -MA_API ma_result ma_log_postf(ma_log* pLog, ma_uint32 level, const char* pFormat, ...) -{ - ma_result result; - va_list args; - - if (pLog == NULL || pFormat == NULL) { - return MA_INVALID_ARGS; - } - - va_start(args, pFormat); - { - result = ma_log_postv(pLog, level, pFormat, args); - } - va_end(args); - - return result; -} - - - -static MA_INLINE ma_uint8 ma_clip_u8(ma_int32 x) -{ - return (ma_uint8)(ma_clamp(x, -128, 127) + 128); -} - -static MA_INLINE ma_int16 ma_clip_s16(ma_int32 x) -{ - return (ma_int16)ma_clamp(x, -32768, 32767); -} - -static MA_INLINE ma_int64 ma_clip_s24(ma_int64 x) -{ - return (ma_int64)ma_clamp(x, -8388608, 8388607); -} - -static MA_INLINE ma_int32 ma_clip_s32(ma_int64 x) -{ - /* This dance is to silence warnings with -std=c89. A good compiler should be able to optimize this away. */ - ma_int64 clipMin; - ma_int64 clipMax; - clipMin = -((ma_int64)2147483647 + 1); - clipMax = (ma_int64)2147483647; - - return (ma_int32)ma_clamp(x, clipMin, clipMax); -} - -static MA_INLINE float ma_clip_f32(float x) -{ - if (x < -1) return -1; - if (x > +1) return +1; - return x; -} - - -static MA_INLINE float ma_mix_f32(float x, float y, float a) -{ - return x*(1-a) + y*a; -} -static MA_INLINE float ma_mix_f32_fast(float x, float y, float a) -{ - float r0 = (y - x); - float r1 = r0*a; - return x + r1; - /*return x + (y - x)*a;*/ -} - -#if defined(MA_SUPPORT_SSE2) -static MA_INLINE __m128 ma_mix_f32_fast__sse2(__m128 x, __m128 y, __m128 a) -{ - return _mm_add_ps(x, _mm_mul_ps(_mm_sub_ps(y, x), a)); -} -#endif -#if defined(MA_SUPPORT_AVX2) -static MA_INLINE __m256 ma_mix_f32_fast__avx2(__m256 x, __m256 y, __m256 a) -{ - return _mm256_add_ps(x, _mm256_mul_ps(_mm256_sub_ps(y, x), a)); -} -#endif -#if defined(MA_SUPPORT_NEON) -static MA_INLINE float32x4_t ma_mix_f32_fast__neon(float32x4_t x, float32x4_t y, float32x4_t a) -{ - return vaddq_f32(x, vmulq_f32(vsubq_f32(y, x), a)); -} -#endif - - -static MA_INLINE double ma_mix_f64(double x, double y, double a) -{ - return x*(1-a) + y*a; -} -static MA_INLINE double ma_mix_f64_fast(double x, double y, double a) -{ - return x + (y - x)*a; -} - -static MA_INLINE float ma_scale_to_range_f32(float x, float lo, float hi) -{ - return lo + x*(hi-lo); -} - - -/* -Greatest common factor using Euclid's algorithm iteratively. -*/ -static MA_INLINE ma_uint32 ma_gcf_u32(ma_uint32 a, ma_uint32 b) -{ - for (;;) { - if (b == 0) { - break; - } else { - ma_uint32 t = a; - a = b; - b = t % a; - } - } - - return a; -} - - -static ma_uint32 ma_ffs_32(ma_uint32 x) -{ - ma_uint32 i; - - /* Just a naive implementation just to get things working for now. Will optimize this later. */ - for (i = 0; i < 32; i += 1) { - if ((x & (1U << i)) != 0) { - return i; - } - } - - return i; -} - -static MA_INLINE ma_int16 ma_float_to_fixed_16(float x) -{ - return (ma_int16)(x * (1 << 8)); -} - - - -/* -Random Number Generation - -miniaudio uses the LCG random number generation algorithm. This is good enough for audio. - -Note that miniaudio's global LCG implementation uses global state which is _not_ thread-local. When this is called across -multiple threads, results will be unpredictable. However, it won't crash and results will still be random enough for -miniaudio's purposes. -*/ -#ifndef MA_DEFAULT_LCG_SEED -#define MA_DEFAULT_LCG_SEED 4321 -#endif - -#define MA_LCG_M 2147483647 -#define MA_LCG_A 48271 -#define MA_LCG_C 0 - -static ma_lcg g_maLCG = {MA_DEFAULT_LCG_SEED}; /* Non-zero initial seed. Use ma_seed() to use an explicit seed. */ - -static MA_INLINE void ma_lcg_seed(ma_lcg* pLCG, ma_int32 seed) -{ - MA_ASSERT(pLCG != NULL); - pLCG->state = seed; -} - -static MA_INLINE ma_int32 ma_lcg_rand_s32(ma_lcg* pLCG) -{ - pLCG->state = (MA_LCG_A * pLCG->state + MA_LCG_C) % MA_LCG_M; - return pLCG->state; -} - -static MA_INLINE ma_uint32 ma_lcg_rand_u32(ma_lcg* pLCG) -{ - return (ma_uint32)ma_lcg_rand_s32(pLCG); -} - -static MA_INLINE ma_int16 ma_lcg_rand_s16(ma_lcg* pLCG) -{ - return (ma_int16)(ma_lcg_rand_s32(pLCG) & 0xFFFF); -} - -static MA_INLINE double ma_lcg_rand_f64(ma_lcg* pLCG) -{ - return ma_lcg_rand_s32(pLCG) / (double)0x7FFFFFFF; -} - -static MA_INLINE float ma_lcg_rand_f32(ma_lcg* pLCG) -{ - return (float)ma_lcg_rand_f64(pLCG); -} - -static MA_INLINE float ma_lcg_rand_range_f32(ma_lcg* pLCG, float lo, float hi) -{ - return ma_scale_to_range_f32(ma_lcg_rand_f32(pLCG), lo, hi); -} - -static MA_INLINE ma_int32 ma_lcg_rand_range_s32(ma_lcg* pLCG, ma_int32 lo, ma_int32 hi) -{ - if (lo == hi) { - return lo; - } - - return lo + ma_lcg_rand_u32(pLCG) / (0xFFFFFFFF / (hi - lo + 1) + 1); -} - - - -static MA_INLINE void ma_seed(ma_int32 seed) -{ - ma_lcg_seed(&g_maLCG, seed); -} - -static MA_INLINE ma_int32 ma_rand_s32(void) -{ - return ma_lcg_rand_s32(&g_maLCG); -} - -static MA_INLINE ma_uint32 ma_rand_u32(void) -{ - return ma_lcg_rand_u32(&g_maLCG); -} - -static MA_INLINE double ma_rand_f64(void) -{ - return ma_lcg_rand_f64(&g_maLCG); -} - -static MA_INLINE float ma_rand_f32(void) -{ - return ma_lcg_rand_f32(&g_maLCG); -} - -static MA_INLINE float ma_rand_range_f32(float lo, float hi) -{ - return ma_lcg_rand_range_f32(&g_maLCG, lo, hi); -} - -static MA_INLINE ma_int32 ma_rand_range_s32(ma_int32 lo, ma_int32 hi) -{ - return ma_lcg_rand_range_s32(&g_maLCG, lo, hi); -} - - -static MA_INLINE float ma_dither_f32_rectangle(float ditherMin, float ditherMax) -{ - return ma_rand_range_f32(ditherMin, ditherMax); -} - -static MA_INLINE float ma_dither_f32_triangle(float ditherMin, float ditherMax) -{ - float a = ma_rand_range_f32(ditherMin, 0); - float b = ma_rand_range_f32(0, ditherMax); - return a + b; -} - -static MA_INLINE float ma_dither_f32(ma_dither_mode ditherMode, float ditherMin, float ditherMax) -{ - if (ditherMode == ma_dither_mode_rectangle) { - return ma_dither_f32_rectangle(ditherMin, ditherMax); - } - if (ditherMode == ma_dither_mode_triangle) { - return ma_dither_f32_triangle(ditherMin, ditherMax); - } - - return 0; -} - -static MA_INLINE ma_int32 ma_dither_s32(ma_dither_mode ditherMode, ma_int32 ditherMin, ma_int32 ditherMax) -{ - if (ditherMode == ma_dither_mode_rectangle) { - ma_int32 a = ma_rand_range_s32(ditherMin, ditherMax); - return a; - } - if (ditherMode == ma_dither_mode_triangle) { - ma_int32 a = ma_rand_range_s32(ditherMin, 0); - ma_int32 b = ma_rand_range_s32(0, ditherMax); - return a + b; - } - - return 0; -} - - -/************************************************************************************************************************************************************** - -Atomics - -**************************************************************************************************************************************************************/ -/* c89atomic.h begin */ -#ifndef ma_atomic_h -#if defined(__cplusplus) -extern "C" { -#endif -#if defined(__clang__) || (defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6))) - #pragma GCC diagnostic push - #pragma GCC diagnostic ignored "-Wlong-long" - #if defined(__clang__) - #pragma GCC diagnostic ignored "-Wc++11-long-long" - #endif -#endif -typedef int ma_atomic_memory_order; -#define MA_ATOMIC_HAS_8 -#define MA_ATOMIC_HAS_16 -#define MA_ATOMIC_HAS_32 -#define MA_ATOMIC_HAS_64 -#if (defined(_MSC_VER) ) || defined(__WATCOMC__) || defined(__DMC__) - #define MA_ATOMIC_MSVC_ARM_INTRINSIC(dst, src, order, intrin, ma_atomicType, msvcType) \ - ma_atomicType result; \ - switch (order) \ - { \ - case ma_atomic_memory_order_relaxed: \ - { \ - result = (ma_atomicType)intrin##_nf((volatile msvcType*)dst, (msvcType)src); \ - } break; \ - case ma_atomic_memory_order_consume: \ - case ma_atomic_memory_order_acquire: \ - { \ - result = (ma_atomicType)intrin##_acq((volatile msvcType*)dst, (msvcType)src); \ - } break; \ - case ma_atomic_memory_order_release: \ - { \ - result = (ma_atomicType)intrin##_rel((volatile msvcType*)dst, (msvcType)src); \ - } break; \ - case ma_atomic_memory_order_acq_rel: \ - case ma_atomic_memory_order_seq_cst: \ - default: \ - { \ - result = (ma_atomicType)intrin((volatile msvcType*)dst, (msvcType)src); \ - } break; \ - } \ - return result; - #define MA_ATOMIC_MSVC_ARM_INTRINSIC_COMPARE_EXCHANGE(ptr, expected, desired, order, intrin, ma_atomicType, msvcType) \ - ma_atomicType result; \ - switch (order) \ - { \ - case ma_atomic_memory_order_relaxed: \ - { \ - result = (ma_atomicType)intrin##_nf((volatile msvcType*)ptr, (msvcType)expected, (msvcType)desired); \ - } break; \ - case ma_atomic_memory_order_consume: \ - case ma_atomic_memory_order_acquire: \ - { \ - result = (ma_atomicType)intrin##_acq((volatile msvcType*)ptr, (msvcType)expected, (msvcType)desired); \ - } break; \ - case ma_atomic_memory_order_release: \ - { \ - result = (ma_atomicType)intrin##_rel((volatile msvcType*)ptr, (msvcType)expected, (msvcType)desired); \ - } break; \ - case ma_atomic_memory_order_acq_rel: \ - case ma_atomic_memory_order_seq_cst: \ - default: \ - { \ - result = (ma_atomicType)intrin((volatile msvcType*)ptr, (msvcType)expected, (msvcType)desired); \ - } break; \ - } \ - return result; - #define ma_atomic_memory_order_relaxed 0 - #define ma_atomic_memory_order_consume 1 - #define ma_atomic_memory_order_acquire 2 - #define ma_atomic_memory_order_release 3 - #define ma_atomic_memory_order_acq_rel 4 - #define ma_atomic_memory_order_seq_cst 5 - #if _MSC_VER < 1600 && defined(MA_X86) - #define MA_ATOMIC_MSVC_USE_INLINED_ASSEMBLY - #endif - #if _MSC_VER < 1600 - #undef MA_ATOMIC_HAS_8 - #undef MA_ATOMIC_HAS_16 - #endif - #if !defined(MA_ATOMIC_MSVC_USE_INLINED_ASSEMBLY) - #include - #endif - #if defined(MA_ATOMIC_MSVC_USE_INLINED_ASSEMBLY) - #if defined(MA_ATOMIC_HAS_8) - static MA_INLINE ma_uint8 __stdcall ma_atomic_compare_and_swap_8(volatile ma_uint8* dst, ma_uint8 expected, ma_uint8 desired) - { - ma_uint8 result = 0; - __asm { - mov ecx, dst - mov al, expected - mov dl, desired - lock cmpxchg [ecx], dl - mov result, al - } - return result; - } - #endif - #if defined(MA_ATOMIC_HAS_16) - static MA_INLINE ma_uint16 __stdcall ma_atomic_compare_and_swap_16(volatile ma_uint16* dst, ma_uint16 expected, ma_uint16 desired) - { - ma_uint16 result = 0; - __asm { - mov ecx, dst - mov ax, expected - mov dx, desired - lock cmpxchg [ecx], dx - mov result, ax - } - return result; - } - #endif - #if defined(MA_ATOMIC_HAS_32) - static MA_INLINE ma_uint32 __stdcall ma_atomic_compare_and_swap_32(volatile ma_uint32* dst, ma_uint32 expected, ma_uint32 desired) - { - ma_uint32 result = 0; - __asm { - mov ecx, dst - mov eax, expected - mov edx, desired - lock cmpxchg [ecx], edx - mov result, eax - } - return result; - } - #endif - #if defined(MA_ATOMIC_HAS_64) - static MA_INLINE ma_uint64 __stdcall ma_atomic_compare_and_swap_64(volatile ma_uint64* dst, ma_uint64 expected, ma_uint64 desired) - { - ma_uint32 resultEAX = 0; - ma_uint32 resultEDX = 0; - __asm { - mov esi, dst - mov eax, dword ptr expected - mov edx, dword ptr expected + 4 - mov ebx, dword ptr desired - mov ecx, dword ptr desired + 4 - lock cmpxchg8b qword ptr [esi] - mov resultEAX, eax - mov resultEDX, edx - } - return ((ma_uint64)resultEDX << 32) | resultEAX; - } - #endif - #else - #if defined(MA_ATOMIC_HAS_8) - #define ma_atomic_compare_and_swap_8( dst, expected, desired) (ma_uint8 )_InterlockedCompareExchange8((volatile char*)dst, (char)desired, (char)expected) - #endif - #if defined(MA_ATOMIC_HAS_16) - #define ma_atomic_compare_and_swap_16(dst, expected, desired) (ma_uint16)_InterlockedCompareExchange16((volatile short*)dst, (short)desired, (short)expected) - #endif - #if defined(MA_ATOMIC_HAS_32) - #define ma_atomic_compare_and_swap_32(dst, expected, desired) (ma_uint32)_InterlockedCompareExchange((volatile long*)dst, (long)desired, (long)expected) - #endif - #if defined(MA_ATOMIC_HAS_64) - #define ma_atomic_compare_and_swap_64(dst, expected, desired) (ma_uint64)_InterlockedCompareExchange64((volatile ma_int64*)dst, (ma_int64)desired, (ma_int64)expected) - #endif - #endif - #if defined(MA_ATOMIC_MSVC_USE_INLINED_ASSEMBLY) - #if defined(MA_ATOMIC_HAS_8) - static MA_INLINE ma_uint8 __stdcall ma_atomic_exchange_explicit_8(volatile ma_uint8* dst, ma_uint8 src, ma_atomic_memory_order order) - { - ma_uint8 result = 0; - (void)order; - __asm { - mov ecx, dst - mov al, src - lock xchg [ecx], al - mov result, al - } - return result; - } - #endif - #if defined(MA_ATOMIC_HAS_16) - static MA_INLINE ma_uint16 __stdcall ma_atomic_exchange_explicit_16(volatile ma_uint16* dst, ma_uint16 src, ma_atomic_memory_order order) - { - ma_uint16 result = 0; - (void)order; - __asm { - mov ecx, dst - mov ax, src - lock xchg [ecx], ax - mov result, ax - } - return result; - } - #endif - #if defined(MA_ATOMIC_HAS_32) - static MA_INLINE ma_uint32 __stdcall ma_atomic_exchange_explicit_32(volatile ma_uint32* dst, ma_uint32 src, ma_atomic_memory_order order) - { - ma_uint32 result = 0; - (void)order; - __asm { - mov ecx, dst - mov eax, src - lock xchg [ecx], eax - mov result, eax - } - return result; - } - #endif - #else - #if defined(MA_ATOMIC_HAS_8) - static MA_INLINE ma_uint8 __stdcall ma_atomic_exchange_explicit_8(volatile ma_uint8* dst, ma_uint8 src, ma_atomic_memory_order order) - { - #if defined(MA_ARM) - MA_ATOMIC_MSVC_ARM_INTRINSIC(dst, src, order, _InterlockedExchange8, ma_uint8, char); - #else - (void)order; - return (ma_uint8)_InterlockedExchange8((volatile char*)dst, (char)src); - #endif - } - #endif - #if defined(MA_ATOMIC_HAS_16) - static MA_INLINE ma_uint16 __stdcall ma_atomic_exchange_explicit_16(volatile ma_uint16* dst, ma_uint16 src, ma_atomic_memory_order order) - { - #if defined(MA_ARM) - MA_ATOMIC_MSVC_ARM_INTRINSIC(dst, src, order, _InterlockedExchange16, ma_uint16, short); - #else - (void)order; - return (ma_uint16)_InterlockedExchange16((volatile short*)dst, (short)src); - #endif - } - #endif - #if defined(MA_ATOMIC_HAS_32) - static MA_INLINE ma_uint32 __stdcall ma_atomic_exchange_explicit_32(volatile ma_uint32* dst, ma_uint32 src, ma_atomic_memory_order order) - { - #if defined(MA_ARM) - MA_ATOMIC_MSVC_ARM_INTRINSIC(dst, src, order, _InterlockedExchange, ma_uint32, long); - #else - (void)order; - return (ma_uint32)_InterlockedExchange((volatile long*)dst, (long)src); - #endif - } - #endif - #if defined(MA_ATOMIC_HAS_64) && defined(MA_64BIT) - static MA_INLINE ma_uint64 __stdcall ma_atomic_exchange_explicit_64(volatile ma_uint64* dst, ma_uint64 src, ma_atomic_memory_order order) - { - #if defined(MA_ARM) - MA_ATOMIC_MSVC_ARM_INTRINSIC(dst, src, order, _InterlockedExchange64, ma_uint64, long long); - #else - (void)order; - return (ma_uint64)_InterlockedExchange64((volatile long long*)dst, (long long)src); - #endif - } - #else - #endif - #endif - #if defined(MA_ATOMIC_HAS_64) && !defined(MA_64BIT) - static MA_INLINE ma_uint64 __stdcall ma_atomic_exchange_explicit_64(volatile ma_uint64* dst, ma_uint64 src, ma_atomic_memory_order order) - { - ma_uint64 oldValue; - do { - oldValue = *dst; - } while (ma_atomic_compare_and_swap_64(dst, oldValue, src) != oldValue); - (void)order; - return oldValue; - } - #endif - #if defined(MA_ATOMIC_MSVC_USE_INLINED_ASSEMBLY) - #if defined(MA_ATOMIC_HAS_8) - static MA_INLINE ma_uint8 __stdcall ma_atomic_fetch_add_explicit_8(volatile ma_uint8* dst, ma_uint8 src, ma_atomic_memory_order order) - { - ma_uint8 result = 0; - (void)order; - __asm { - mov ecx, dst - mov al, src - lock xadd [ecx], al - mov result, al - } - return result; - } - #endif - #if defined(MA_ATOMIC_HAS_16) - static MA_INLINE ma_uint16 __stdcall ma_atomic_fetch_add_explicit_16(volatile ma_uint16* dst, ma_uint16 src, ma_atomic_memory_order order) - { - ma_uint16 result = 0; - (void)order; - __asm { - mov ecx, dst - mov ax, src - lock xadd [ecx], ax - mov result, ax - } - return result; - } - #endif - #if defined(MA_ATOMIC_HAS_32) - static MA_INLINE ma_uint32 __stdcall ma_atomic_fetch_add_explicit_32(volatile ma_uint32* dst, ma_uint32 src, ma_atomic_memory_order order) - { - ma_uint32 result = 0; - (void)order; - __asm { - mov ecx, dst - mov eax, src - lock xadd [ecx], eax - mov result, eax - } - return result; - } - #endif - #else - #if defined(MA_ATOMIC_HAS_8) - static MA_INLINE ma_uint8 __stdcall ma_atomic_fetch_add_explicit_8(volatile ma_uint8* dst, ma_uint8 src, ma_atomic_memory_order order) - { - #if defined(MA_ARM) - MA_ATOMIC_MSVC_ARM_INTRINSIC(dst, src, order, _InterlockedExchangeAdd8, ma_uint8, char); - #else - (void)order; - return (ma_uint8)_InterlockedExchangeAdd8((volatile char*)dst, (char)src); - #endif - } - #endif - #if defined(MA_ATOMIC_HAS_16) - static MA_INLINE ma_uint16 __stdcall ma_atomic_fetch_add_explicit_16(volatile ma_uint16* dst, ma_uint16 src, ma_atomic_memory_order order) - { - #if defined(MA_ARM) - MA_ATOMIC_MSVC_ARM_INTRINSIC(dst, src, order, _InterlockedExchangeAdd16, ma_uint16, short); - #else - (void)order; - return (ma_uint16)_InterlockedExchangeAdd16((volatile short*)dst, (short)src); - #endif - } - #endif - #if defined(MA_ATOMIC_HAS_32) - static MA_INLINE ma_uint32 __stdcall ma_atomic_fetch_add_explicit_32(volatile ma_uint32* dst, ma_uint32 src, ma_atomic_memory_order order) - { - #if defined(MA_ARM) - MA_ATOMIC_MSVC_ARM_INTRINSIC(dst, src, order, _InterlockedExchangeAdd, ma_uint32, long); - #else - (void)order; - return (ma_uint32)_InterlockedExchangeAdd((volatile long*)dst, (long)src); - #endif - } - #endif - #if defined(MA_ATOMIC_HAS_64) && defined(MA_64BIT) - static MA_INLINE ma_uint64 __stdcall ma_atomic_fetch_add_explicit_64(volatile ma_uint64* dst, ma_uint64 src, ma_atomic_memory_order order) - { - #if defined(MA_ARM) - MA_ATOMIC_MSVC_ARM_INTRINSIC(dst, src, order, _InterlockedExchangeAdd64, ma_uint64, long long); - #else - (void)order; - return (ma_uint64)_InterlockedExchangeAdd64((volatile long long*)dst, (long long)src); - #endif - } - #else - #endif - #endif - #if defined(MA_ATOMIC_HAS_64) && !defined(MA_64BIT) - static MA_INLINE ma_uint64 __stdcall ma_atomic_fetch_add_explicit_64(volatile ma_uint64* dst, ma_uint64 src, ma_atomic_memory_order order) - { - ma_uint64 oldValue; - ma_uint64 newValue; - do { - oldValue = *dst; - newValue = oldValue + src; - } while (ma_atomic_compare_and_swap_64(dst, oldValue, newValue) != oldValue); - (void)order; - return oldValue; - } - #endif - #if defined(MA_ATOMIC_MSVC_USE_INLINED_ASSEMBLY) - static MA_INLINE void __stdcall ma_atomic_thread_fence(ma_atomic_memory_order order) - { - (void)order; - __asm { - lock add [esp], 0 - } - } - #else - #if defined(MA_X64) - #define ma_atomic_thread_fence(order) __faststorefence(), (void)order - #elif defined(MA_ARM64) - #define ma_atomic_thread_fence(order) __dmb(_ARM64_BARRIER_ISH), (void)order - #else - static MA_INLINE void ma_atomic_thread_fence(ma_atomic_memory_order order) - { - volatile ma_uint32 barrier = 0; - ma_atomic_fetch_add_explicit_32(&barrier, 0, order); - } - #endif - #endif - #define ma_atomic_compiler_fence() ma_atomic_thread_fence(ma_atomic_memory_order_seq_cst) - #define ma_atomic_signal_fence(order) ma_atomic_thread_fence(order) - #if defined(MA_ATOMIC_HAS_8) - static MA_INLINE ma_uint8 ma_atomic_load_explicit_8(volatile const ma_uint8* ptr, ma_atomic_memory_order order) - { - #if defined(MA_ARM) - MA_ATOMIC_MSVC_ARM_INTRINSIC_COMPARE_EXCHANGE(ptr, 0, 0, order, _InterlockedCompareExchange8, ma_uint8, char); - #else - (void)order; - return ma_atomic_compare_and_swap_8((volatile ma_uint8*)ptr, 0, 0); - #endif - } - #endif - #if defined(MA_ATOMIC_HAS_16) - static MA_INLINE ma_uint16 ma_atomic_load_explicit_16(volatile const ma_uint16* ptr, ma_atomic_memory_order order) - { - #if defined(MA_ARM) - MA_ATOMIC_MSVC_ARM_INTRINSIC_COMPARE_EXCHANGE(ptr, 0, 0, order, _InterlockedCompareExchange16, ma_uint16, short); - #else - (void)order; - return ma_atomic_compare_and_swap_16((volatile ma_uint16*)ptr, 0, 0); - #endif - } - #endif - #if defined(MA_ATOMIC_HAS_32) - static MA_INLINE ma_uint32 ma_atomic_load_explicit_32(volatile const ma_uint32* ptr, ma_atomic_memory_order order) - { - #if defined(MA_ARM) - MA_ATOMIC_MSVC_ARM_INTRINSIC_COMPARE_EXCHANGE(ptr, 0, 0, order, _InterlockedCompareExchange, ma_uint32, long); - #else - (void)order; - return ma_atomic_compare_and_swap_32((volatile ma_uint32*)ptr, 0, 0); - #endif - } - #endif - #if defined(MA_ATOMIC_HAS_64) - static MA_INLINE ma_uint64 ma_atomic_load_explicit_64(volatile const ma_uint64* ptr, ma_atomic_memory_order order) - { - #if defined(MA_ARM) - MA_ATOMIC_MSVC_ARM_INTRINSIC_COMPARE_EXCHANGE(ptr, 0, 0, order, _InterlockedCompareExchange64, ma_uint64, long long); - #else - (void)order; - return ma_atomic_compare_and_swap_64((volatile ma_uint64*)ptr, 0, 0); - #endif - } - #endif - #if defined(MA_ATOMIC_HAS_8) - #define ma_atomic_store_explicit_8( dst, src, order) (void)ma_atomic_exchange_explicit_8 (dst, src, order) - #endif - #if defined(MA_ATOMIC_HAS_16) - #define ma_atomic_store_explicit_16(dst, src, order) (void)ma_atomic_exchange_explicit_16(dst, src, order) - #endif - #if defined(MA_ATOMIC_HAS_32) - #define ma_atomic_store_explicit_32(dst, src, order) (void)ma_atomic_exchange_explicit_32(dst, src, order) - #endif - #if defined(MA_ATOMIC_HAS_64) - #define ma_atomic_store_explicit_64(dst, src, order) (void)ma_atomic_exchange_explicit_64(dst, src, order) - #endif - #if defined(MA_ATOMIC_HAS_8) - static MA_INLINE ma_uint8 __stdcall ma_atomic_fetch_sub_explicit_8(volatile ma_uint8* dst, ma_uint8 src, ma_atomic_memory_order order) - { - ma_uint8 oldValue; - ma_uint8 newValue; - do { - oldValue = *dst; - newValue = (ma_uint8)(oldValue - src); - } while (ma_atomic_compare_and_swap_8(dst, oldValue, newValue) != oldValue); - (void)order; - return oldValue; - } - #endif - #if defined(MA_ATOMIC_HAS_16) - static MA_INLINE ma_uint16 __stdcall ma_atomic_fetch_sub_explicit_16(volatile ma_uint16* dst, ma_uint16 src, ma_atomic_memory_order order) - { - ma_uint16 oldValue; - ma_uint16 newValue; - do { - oldValue = *dst; - newValue = (ma_uint16)(oldValue - src); - } while (ma_atomic_compare_and_swap_16(dst, oldValue, newValue) != oldValue); - (void)order; - return oldValue; - } - #endif - #if defined(MA_ATOMIC_HAS_32) - static MA_INLINE ma_uint32 __stdcall ma_atomic_fetch_sub_explicit_32(volatile ma_uint32* dst, ma_uint32 src, ma_atomic_memory_order order) - { - ma_uint32 oldValue; - ma_uint32 newValue; - do { - oldValue = *dst; - newValue = oldValue - src; - } while (ma_atomic_compare_and_swap_32(dst, oldValue, newValue) != oldValue); - (void)order; - return oldValue; - } - #endif - #if defined(MA_ATOMIC_HAS_64) - static MA_INLINE ma_uint64 __stdcall ma_atomic_fetch_sub_explicit_64(volatile ma_uint64* dst, ma_uint64 src, ma_atomic_memory_order order) - { - ma_uint64 oldValue; - ma_uint64 newValue; - do { - oldValue = *dst; - newValue = oldValue - src; - } while (ma_atomic_compare_and_swap_64(dst, oldValue, newValue) != oldValue); - (void)order; - return oldValue; - } - #endif - #if defined(MA_ATOMIC_HAS_8) - static MA_INLINE ma_uint8 __stdcall ma_atomic_fetch_and_explicit_8(volatile ma_uint8* dst, ma_uint8 src, ma_atomic_memory_order order) - { - #if defined(MA_ARM) - MA_ATOMIC_MSVC_ARM_INTRINSIC(dst, src, order, _InterlockedAnd8, ma_uint8, char); - #else - ma_uint8 oldValue; - ma_uint8 newValue; - do { - oldValue = *dst; - newValue = (ma_uint8)(oldValue & src); - } while (ma_atomic_compare_and_swap_8(dst, oldValue, newValue) != oldValue); - (void)order; - return oldValue; - #endif - } - #endif - #if defined(MA_ATOMIC_HAS_16) - static MA_INLINE ma_uint16 __stdcall ma_atomic_fetch_and_explicit_16(volatile ma_uint16* dst, ma_uint16 src, ma_atomic_memory_order order) - { - #if defined(MA_ARM) - MA_ATOMIC_MSVC_ARM_INTRINSIC(dst, src, order, _InterlockedAnd16, ma_uint16, short); - #else - ma_uint16 oldValue; - ma_uint16 newValue; - do { - oldValue = *dst; - newValue = (ma_uint16)(oldValue & src); - } while (ma_atomic_compare_and_swap_16(dst, oldValue, newValue) != oldValue); - (void)order; - return oldValue; - #endif - } - #endif - #if defined(MA_ATOMIC_HAS_32) - static MA_INLINE ma_uint32 __stdcall ma_atomic_fetch_and_explicit_32(volatile ma_uint32* dst, ma_uint32 src, ma_atomic_memory_order order) - { - #if defined(MA_ARM) - MA_ATOMIC_MSVC_ARM_INTRINSIC(dst, src, order, _InterlockedAnd, ma_uint32, long); - #else - ma_uint32 oldValue; - ma_uint32 newValue; - do { - oldValue = *dst; - newValue = oldValue & src; - } while (ma_atomic_compare_and_swap_32(dst, oldValue, newValue) != oldValue); - (void)order; - return oldValue; - #endif - } - #endif - #if defined(MA_ATOMIC_HAS_64) - static MA_INLINE ma_uint64 __stdcall ma_atomic_fetch_and_explicit_64(volatile ma_uint64* dst, ma_uint64 src, ma_atomic_memory_order order) - { - #if defined(MA_ARM) - MA_ATOMIC_MSVC_ARM_INTRINSIC(dst, src, order, _InterlockedAnd64, ma_uint64, long long); - #else - ma_uint64 oldValue; - ma_uint64 newValue; - do { - oldValue = *dst; - newValue = oldValue & src; - } while (ma_atomic_compare_and_swap_64(dst, oldValue, newValue) != oldValue); - (void)order; - return oldValue; - #endif - } - #endif - #if defined(MA_ATOMIC_HAS_8) - static MA_INLINE ma_uint8 __stdcall ma_atomic_fetch_xor_explicit_8(volatile ma_uint8* dst, ma_uint8 src, ma_atomic_memory_order order) - { - #if defined(MA_ARM) - MA_ATOMIC_MSVC_ARM_INTRINSIC(dst, src, order, _InterlockedXor8, ma_uint8, char); - #else - ma_uint8 oldValue; - ma_uint8 newValue; - do { - oldValue = *dst; - newValue = (ma_uint8)(oldValue ^ src); - } while (ma_atomic_compare_and_swap_8(dst, oldValue, newValue) != oldValue); - (void)order; - return oldValue; - #endif - } - #endif - #if defined(MA_ATOMIC_HAS_16) - static MA_INLINE ma_uint16 __stdcall ma_atomic_fetch_xor_explicit_16(volatile ma_uint16* dst, ma_uint16 src, ma_atomic_memory_order order) - { - #if defined(MA_ARM) - MA_ATOMIC_MSVC_ARM_INTRINSIC(dst, src, order, _InterlockedXor16, ma_uint16, short); - #else - ma_uint16 oldValue; - ma_uint16 newValue; - do { - oldValue = *dst; - newValue = (ma_uint16)(oldValue ^ src); - } while (ma_atomic_compare_and_swap_16(dst, oldValue, newValue) != oldValue); - (void)order; - return oldValue; - #endif - } - #endif - #if defined(MA_ATOMIC_HAS_32) - static MA_INLINE ma_uint32 __stdcall ma_atomic_fetch_xor_explicit_32(volatile ma_uint32* dst, ma_uint32 src, ma_atomic_memory_order order) - { - #if defined(MA_ARM) - MA_ATOMIC_MSVC_ARM_INTRINSIC(dst, src, order, _InterlockedXor, ma_uint32, long); - #else - ma_uint32 oldValue; - ma_uint32 newValue; - do { - oldValue = *dst; - newValue = oldValue ^ src; - } while (ma_atomic_compare_and_swap_32(dst, oldValue, newValue) != oldValue); - (void)order; - return oldValue; - #endif - } - #endif - #if defined(MA_ATOMIC_HAS_64) - static MA_INLINE ma_uint64 __stdcall ma_atomic_fetch_xor_explicit_64(volatile ma_uint64* dst, ma_uint64 src, ma_atomic_memory_order order) - { - #if defined(MA_ARM) - MA_ATOMIC_MSVC_ARM_INTRINSIC(dst, src, order, _InterlockedXor64, ma_uint64, long long); - #else - ma_uint64 oldValue; - ma_uint64 newValue; - do { - oldValue = *dst; - newValue = oldValue ^ src; - } while (ma_atomic_compare_and_swap_64(dst, oldValue, newValue) != oldValue); - (void)order; - return oldValue; - #endif - } - #endif - #if defined(MA_ATOMIC_HAS_8) - static MA_INLINE ma_uint8 __stdcall ma_atomic_fetch_or_explicit_8(volatile ma_uint8* dst, ma_uint8 src, ma_atomic_memory_order order) - { - #if defined(MA_ARM) - MA_ATOMIC_MSVC_ARM_INTRINSIC(dst, src, order, _InterlockedOr8, ma_uint8, char); - #else - ma_uint8 oldValue; - ma_uint8 newValue; - do { - oldValue = *dst; - newValue = (ma_uint8)(oldValue | src); - } while (ma_atomic_compare_and_swap_8(dst, oldValue, newValue) != oldValue); - (void)order; - return oldValue; - #endif - } - #endif - #if defined(MA_ATOMIC_HAS_16) - static MA_INLINE ma_uint16 __stdcall ma_atomic_fetch_or_explicit_16(volatile ma_uint16* dst, ma_uint16 src, ma_atomic_memory_order order) - { - #if defined(MA_ARM) - MA_ATOMIC_MSVC_ARM_INTRINSIC(dst, src, order, _InterlockedOr16, ma_uint16, short); - #else - ma_uint16 oldValue; - ma_uint16 newValue; - do { - oldValue = *dst; - newValue = (ma_uint16)(oldValue | src); - } while (ma_atomic_compare_and_swap_16(dst, oldValue, newValue) != oldValue); - (void)order; - return oldValue; - #endif - } - #endif - #if defined(MA_ATOMIC_HAS_32) - static MA_INLINE ma_uint32 __stdcall ma_atomic_fetch_or_explicit_32(volatile ma_uint32* dst, ma_uint32 src, ma_atomic_memory_order order) - { - #if defined(MA_ARM) - MA_ATOMIC_MSVC_ARM_INTRINSIC(dst, src, order, _InterlockedOr, ma_uint32, long); - #else - ma_uint32 oldValue; - ma_uint32 newValue; - do { - oldValue = *dst; - newValue = oldValue | src; - } while (ma_atomic_compare_and_swap_32(dst, oldValue, newValue) != oldValue); - (void)order; - return oldValue; - #endif - } - #endif - #if defined(MA_ATOMIC_HAS_64) - static MA_INLINE ma_uint64 __stdcall ma_atomic_fetch_or_explicit_64(volatile ma_uint64* dst, ma_uint64 src, ma_atomic_memory_order order) - { - #if defined(MA_ARM) - MA_ATOMIC_MSVC_ARM_INTRINSIC(dst, src, order, _InterlockedOr64, ma_uint64, long long); - #else - ma_uint64 oldValue; - ma_uint64 newValue; - do { - oldValue = *dst; - newValue = oldValue | src; - } while (ma_atomic_compare_and_swap_64(dst, oldValue, newValue) != oldValue); - (void)order; - return oldValue; - #endif - } - #endif - #if defined(MA_ATOMIC_HAS_8) - #define ma_atomic_test_and_set_explicit_8( dst, order) ma_atomic_exchange_explicit_8 (dst, 1, order) - #endif - #if defined(MA_ATOMIC_HAS_16) - #define ma_atomic_test_and_set_explicit_16(dst, order) ma_atomic_exchange_explicit_16(dst, 1, order) - #endif - #if defined(MA_ATOMIC_HAS_32) - #define ma_atomic_test_and_set_explicit_32(dst, order) ma_atomic_exchange_explicit_32(dst, 1, order) - #endif - #if defined(MA_ATOMIC_HAS_64) - #define ma_atomic_test_and_set_explicit_64(dst, order) ma_atomic_exchange_explicit_64(dst, 1, order) - #endif - #if defined(MA_ATOMIC_HAS_8) - #define ma_atomic_clear_explicit_8( dst, order) ma_atomic_store_explicit_8 (dst, 0, order) - #endif - #if defined(MA_ATOMIC_HAS_16) - #define ma_atomic_clear_explicit_16(dst, order) ma_atomic_store_explicit_16(dst, 0, order) - #endif - #if defined(MA_ATOMIC_HAS_32) - #define ma_atomic_clear_explicit_32(dst, order) ma_atomic_store_explicit_32(dst, 0, order) - #endif - #if defined(MA_ATOMIC_HAS_64) - #define ma_atomic_clear_explicit_64(dst, order) ma_atomic_store_explicit_64(dst, 0, order) - #endif - #if defined(MA_ATOMIC_HAS_8) - typedef ma_uint8 ma_atomic_flag; - #define ma_atomic_flag_test_and_set_explicit(ptr, order) (ma_bool32)ma_atomic_test_and_set_explicit_8(ptr, order) - #define ma_atomic_flag_clear_explicit(ptr, order) ma_atomic_clear_explicit_8(ptr, order) - #define ma_atomic_flag_load_explicit(ptr, order) ma_atomic_load_explicit_8(ptr, order) - #else - typedef ma_uint32 ma_atomic_flag; - #define ma_atomic_flag_test_and_set_explicit(ptr, order) (ma_bool32)ma_atomic_test_and_set_explicit_32(ptr, order) - #define ma_atomic_flag_clear_explicit(ptr, order) ma_atomic_clear_explicit_32(ptr, order) - #define ma_atomic_flag_load_explicit(ptr, order) ma_atomic_load_explicit_32(ptr, order) - #endif -#elif defined(__clang__) || (defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 7))) - #define MA_ATOMIC_HAS_NATIVE_COMPARE_EXCHANGE - #define MA_ATOMIC_HAS_NATIVE_IS_LOCK_FREE - #define ma_atomic_memory_order_relaxed __ATOMIC_RELAXED - #define ma_atomic_memory_order_consume __ATOMIC_CONSUME - #define ma_atomic_memory_order_acquire __ATOMIC_ACQUIRE - #define ma_atomic_memory_order_release __ATOMIC_RELEASE - #define ma_atomic_memory_order_acq_rel __ATOMIC_ACQ_REL - #define ma_atomic_memory_order_seq_cst __ATOMIC_SEQ_CST - #define ma_atomic_compiler_fence() __asm__ __volatile__("":::"memory") - #define ma_atomic_thread_fence(order) __atomic_thread_fence(order) - #define ma_atomic_signal_fence(order) __atomic_signal_fence(order) - #define ma_atomic_is_lock_free_8(ptr) __atomic_is_lock_free(1, ptr) - #define ma_atomic_is_lock_free_16(ptr) __atomic_is_lock_free(2, ptr) - #define ma_atomic_is_lock_free_32(ptr) __atomic_is_lock_free(4, ptr) - #define ma_atomic_is_lock_free_64(ptr) __atomic_is_lock_free(8, ptr) - #define ma_atomic_test_and_set_explicit_8( dst, order) __atomic_exchange_n(dst, 1, order) - #define ma_atomic_test_and_set_explicit_16(dst, order) __atomic_exchange_n(dst, 1, order) - #define ma_atomic_test_and_set_explicit_32(dst, order) __atomic_exchange_n(dst, 1, order) - #define ma_atomic_test_and_set_explicit_64(dst, order) __atomic_exchange_n(dst, 1, order) - #define ma_atomic_clear_explicit_8( dst, order) __atomic_store_n(dst, 0, order) - #define ma_atomic_clear_explicit_16(dst, order) __atomic_store_n(dst, 0, order) - #define ma_atomic_clear_explicit_32(dst, order) __atomic_store_n(dst, 0, order) - #define ma_atomic_clear_explicit_64(dst, order) __atomic_store_n(dst, 0, order) - #define ma_atomic_store_explicit_8( dst, src, order) __atomic_store_n(dst, src, order) - #define ma_atomic_store_explicit_16(dst, src, order) __atomic_store_n(dst, src, order) - #define ma_atomic_store_explicit_32(dst, src, order) __atomic_store_n(dst, src, order) - #define ma_atomic_store_explicit_64(dst, src, order) __atomic_store_n(dst, src, order) - #define ma_atomic_load_explicit_8( dst, order) __atomic_load_n(dst, order) - #define ma_atomic_load_explicit_16(dst, order) __atomic_load_n(dst, order) - #define ma_atomic_load_explicit_32(dst, order) __atomic_load_n(dst, order) - #define ma_atomic_load_explicit_64(dst, order) __atomic_load_n(dst, order) - #define ma_atomic_exchange_explicit_8( dst, src, order) __atomic_exchange_n(dst, src, order) - #define ma_atomic_exchange_explicit_16(dst, src, order) __atomic_exchange_n(dst, src, order) - #define ma_atomic_exchange_explicit_32(dst, src, order) __atomic_exchange_n(dst, src, order) - #define ma_atomic_exchange_explicit_64(dst, src, order) __atomic_exchange_n(dst, src, order) - #define ma_atomic_compare_exchange_strong_explicit_8( dst, expected, desired, successOrder, failureOrder) __atomic_compare_exchange_n(dst, expected, desired, 0, successOrder, failureOrder) - #define ma_atomic_compare_exchange_strong_explicit_16(dst, expected, desired, successOrder, failureOrder) __atomic_compare_exchange_n(dst, expected, desired, 0, successOrder, failureOrder) - #define ma_atomic_compare_exchange_strong_explicit_32(dst, expected, desired, successOrder, failureOrder) __atomic_compare_exchange_n(dst, expected, desired, 0, successOrder, failureOrder) - #define ma_atomic_compare_exchange_strong_explicit_64(dst, expected, desired, successOrder, failureOrder) __atomic_compare_exchange_n(dst, expected, desired, 0, successOrder, failureOrder) - #define ma_atomic_compare_exchange_weak_explicit_8( dst, expected, desired, successOrder, failureOrder) __atomic_compare_exchange_n(dst, expected, desired, 1, successOrder, failureOrder) - #define ma_atomic_compare_exchange_weak_explicit_16(dst, expected, desired, successOrder, failureOrder) __atomic_compare_exchange_n(dst, expected, desired, 1, successOrder, failureOrder) - #define ma_atomic_compare_exchange_weak_explicit_32(dst, expected, desired, successOrder, failureOrder) __atomic_compare_exchange_n(dst, expected, desired, 1, successOrder, failureOrder) - #define ma_atomic_compare_exchange_weak_explicit_64(dst, expected, desired, successOrder, failureOrder) __atomic_compare_exchange_n(dst, expected, desired, 1, successOrder, failureOrder) - #define ma_atomic_fetch_add_explicit_8( dst, src, order) __atomic_fetch_add(dst, src, order) - #define ma_atomic_fetch_add_explicit_16(dst, src, order) __atomic_fetch_add(dst, src, order) - #define ma_atomic_fetch_add_explicit_32(dst, src, order) __atomic_fetch_add(dst, src, order) - #define ma_atomic_fetch_add_explicit_64(dst, src, order) __atomic_fetch_add(dst, src, order) - #define ma_atomic_fetch_sub_explicit_8( dst, src, order) __atomic_fetch_sub(dst, src, order) - #define ma_atomic_fetch_sub_explicit_16(dst, src, order) __atomic_fetch_sub(dst, src, order) - #define ma_atomic_fetch_sub_explicit_32(dst, src, order) __atomic_fetch_sub(dst, src, order) - #define ma_atomic_fetch_sub_explicit_64(dst, src, order) __atomic_fetch_sub(dst, src, order) - #define ma_atomic_fetch_or_explicit_8( dst, src, order) __atomic_fetch_or(dst, src, order) - #define ma_atomic_fetch_or_explicit_16(dst, src, order) __atomic_fetch_or(dst, src, order) - #define ma_atomic_fetch_or_explicit_32(dst, src, order) __atomic_fetch_or(dst, src, order) - #define ma_atomic_fetch_or_explicit_64(dst, src, order) __atomic_fetch_or(dst, src, order) - #define ma_atomic_fetch_xor_explicit_8( dst, src, order) __atomic_fetch_xor(dst, src, order) - #define ma_atomic_fetch_xor_explicit_16(dst, src, order) __atomic_fetch_xor(dst, src, order) - #define ma_atomic_fetch_xor_explicit_32(dst, src, order) __atomic_fetch_xor(dst, src, order) - #define ma_atomic_fetch_xor_explicit_64(dst, src, order) __atomic_fetch_xor(dst, src, order) - #define ma_atomic_fetch_and_explicit_8( dst, src, order) __atomic_fetch_and(dst, src, order) - #define ma_atomic_fetch_and_explicit_16(dst, src, order) __atomic_fetch_and(dst, src, order) - #define ma_atomic_fetch_and_explicit_32(dst, src, order) __atomic_fetch_and(dst, src, order) - #define ma_atomic_fetch_and_explicit_64(dst, src, order) __atomic_fetch_and(dst, src, order) - static MA_INLINE ma_uint8 ma_atomic_compare_and_swap_8(volatile ma_uint8* dst, ma_uint8 expected, ma_uint8 desired) - { - __atomic_compare_exchange_n(dst, &expected, desired, 0, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST); - return expected; - } - static MA_INLINE ma_uint16 ma_atomic_compare_and_swap_16(volatile ma_uint16* dst, ma_uint16 expected, ma_uint16 desired) - { - __atomic_compare_exchange_n(dst, &expected, desired, 0, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST); - return expected; - } - static MA_INLINE ma_uint32 ma_atomic_compare_and_swap_32(volatile ma_uint32* dst, ma_uint32 expected, ma_uint32 desired) - { - __atomic_compare_exchange_n(dst, &expected, desired, 0, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST); - return expected; - } - #if defined(__clang__) - #pragma clang diagnostic push - #if __clang_major__ >= 8 - #pragma clang diagnostic ignored "-Watomic-alignment" - #endif - #endif - static MA_INLINE ma_uint64 ma_atomic_compare_and_swap_64(volatile ma_uint64* dst, ma_uint64 expected, ma_uint64 desired) - { - __atomic_compare_exchange_n(dst, &expected, desired, 0, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST); - return expected; - } - #if defined(__clang__) - #pragma clang diagnostic pop - #endif - typedef ma_uint8 ma_atomic_flag; - #define ma_atomic_flag_test_and_set_explicit(dst, order) (ma_bool32)__atomic_test_and_set(dst, order) - #define ma_atomic_flag_clear_explicit(dst, order) __atomic_clear(dst, order) - #define ma_atomic_flag_load_explicit(ptr, order) ma_atomic_load_explicit_8(ptr, order) -#else - #define ma_atomic_memory_order_relaxed 1 - #define ma_atomic_memory_order_consume 2 - #define ma_atomic_memory_order_acquire 3 - #define ma_atomic_memory_order_release 4 - #define ma_atomic_memory_order_acq_rel 5 - #define ma_atomic_memory_order_seq_cst 6 - #define ma_atomic_compiler_fence() __asm__ __volatile__("":::"memory") - #if defined(__GNUC__) - #define ma_atomic_thread_fence(order) __sync_synchronize(), (void)order - static MA_INLINE ma_uint8 ma_atomic_exchange_explicit_8(volatile ma_uint8* dst, ma_uint8 src, ma_atomic_memory_order order) - { - if (order > ma_atomic_memory_order_acquire) { - __sync_synchronize(); - } - return __sync_lock_test_and_set(dst, src); - } - static MA_INLINE ma_uint16 ma_atomic_exchange_explicit_16(volatile ma_uint16* dst, ma_uint16 src, ma_atomic_memory_order order) - { - ma_uint16 oldValue; - do { - oldValue = *dst; - } while (__sync_val_compare_and_swap(dst, oldValue, src) != oldValue); - (void)order; - return oldValue; - } - static MA_INLINE ma_uint32 ma_atomic_exchange_explicit_32(volatile ma_uint32* dst, ma_uint32 src, ma_atomic_memory_order order) - { - ma_uint32 oldValue; - do { - oldValue = *dst; - } while (__sync_val_compare_and_swap(dst, oldValue, src) != oldValue); - (void)order; - return oldValue; - } - static MA_INLINE ma_uint64 ma_atomic_exchange_explicit_64(volatile ma_uint64* dst, ma_uint64 src, ma_atomic_memory_order order) - { - ma_uint64 oldValue; - do { - oldValue = *dst; - } while (__sync_val_compare_and_swap(dst, oldValue, src) != oldValue); - (void)order; - return oldValue; - } - static MA_INLINE ma_uint8 ma_atomic_fetch_add_explicit_8(volatile ma_uint8* dst, ma_uint8 src, ma_atomic_memory_order order) - { - (void)order; - return __sync_fetch_and_add(dst, src); - } - static MA_INLINE ma_uint16 ma_atomic_fetch_add_explicit_16(volatile ma_uint16* dst, ma_uint16 src, ma_atomic_memory_order order) - { - (void)order; - return __sync_fetch_and_add(dst, src); - } - static MA_INLINE ma_uint32 ma_atomic_fetch_add_explicit_32(volatile ma_uint32* dst, ma_uint32 src, ma_atomic_memory_order order) - { - (void)order; - return __sync_fetch_and_add(dst, src); - } - static MA_INLINE ma_uint64 ma_atomic_fetch_add_explicit_64(volatile ma_uint64* dst, ma_uint64 src, ma_atomic_memory_order order) - { - (void)order; - return __sync_fetch_and_add(dst, src); - } - static MA_INLINE ma_uint8 ma_atomic_fetch_sub_explicit_8(volatile ma_uint8* dst, ma_uint8 src, ma_atomic_memory_order order) - { - (void)order; - return __sync_fetch_and_sub(dst, src); - } - static MA_INLINE ma_uint16 ma_atomic_fetch_sub_explicit_16(volatile ma_uint16* dst, ma_uint16 src, ma_atomic_memory_order order) - { - (void)order; - return __sync_fetch_and_sub(dst, src); - } - static MA_INLINE ma_uint32 ma_atomic_fetch_sub_explicit_32(volatile ma_uint32* dst, ma_uint32 src, ma_atomic_memory_order order) - { - (void)order; - return __sync_fetch_and_sub(dst, src); - } - static MA_INLINE ma_uint64 ma_atomic_fetch_sub_explicit_64(volatile ma_uint64* dst, ma_uint64 src, ma_atomic_memory_order order) - { - (void)order; - return __sync_fetch_and_sub(dst, src); - } - static MA_INLINE ma_uint8 ma_atomic_fetch_or_explicit_8(volatile ma_uint8* dst, ma_uint8 src, ma_atomic_memory_order order) - { - (void)order; - return __sync_fetch_and_or(dst, src); - } - static MA_INLINE ma_uint16 ma_atomic_fetch_or_explicit_16(volatile ma_uint16* dst, ma_uint16 src, ma_atomic_memory_order order) - { - (void)order; - return __sync_fetch_and_or(dst, src); - } - static MA_INLINE ma_uint32 ma_atomic_fetch_or_explicit_32(volatile ma_uint32* dst, ma_uint32 src, ma_atomic_memory_order order) - { - (void)order; - return __sync_fetch_and_or(dst, src); - } - static MA_INLINE ma_uint64 ma_atomic_fetch_or_explicit_64(volatile ma_uint64* dst, ma_uint64 src, ma_atomic_memory_order order) - { - (void)order; - return __sync_fetch_and_or(dst, src); - } - static MA_INLINE ma_uint8 ma_atomic_fetch_xor_explicit_8(volatile ma_uint8* dst, ma_uint8 src, ma_atomic_memory_order order) - { - (void)order; - return __sync_fetch_and_xor(dst, src); - } - static MA_INLINE ma_uint16 ma_atomic_fetch_xor_explicit_16(volatile ma_uint16* dst, ma_uint16 src, ma_atomic_memory_order order) - { - (void)order; - return __sync_fetch_and_xor(dst, src); - } - static MA_INLINE ma_uint32 ma_atomic_fetch_xor_explicit_32(volatile ma_uint32* dst, ma_uint32 src, ma_atomic_memory_order order) - { - (void)order; - return __sync_fetch_and_xor(dst, src); - } - static MA_INLINE ma_uint64 ma_atomic_fetch_xor_explicit_64(volatile ma_uint64* dst, ma_uint64 src, ma_atomic_memory_order order) - { - (void)order; - return __sync_fetch_and_xor(dst, src); - } - static MA_INLINE ma_uint8 ma_atomic_fetch_and_explicit_8(volatile ma_uint8* dst, ma_uint8 src, ma_atomic_memory_order order) - { - (void)order; - return __sync_fetch_and_and(dst, src); - } - static MA_INLINE ma_uint16 ma_atomic_fetch_and_explicit_16(volatile ma_uint16* dst, ma_uint16 src, ma_atomic_memory_order order) - { - (void)order; - return __sync_fetch_and_and(dst, src); - } - static MA_INLINE ma_uint32 ma_atomic_fetch_and_explicit_32(volatile ma_uint32* dst, ma_uint32 src, ma_atomic_memory_order order) - { - (void)order; - return __sync_fetch_and_and(dst, src); - } - static MA_INLINE ma_uint64 ma_atomic_fetch_and_explicit_64(volatile ma_uint64* dst, ma_uint64 src, ma_atomic_memory_order order) - { - (void)order; - return __sync_fetch_and_and(dst, src); - } - #define ma_atomic_compare_and_swap_8( dst, expected, desired) __sync_val_compare_and_swap(dst, expected, desired) - #define ma_atomic_compare_and_swap_16(dst, expected, desired) __sync_val_compare_and_swap(dst, expected, desired) - #define ma_atomic_compare_and_swap_32(dst, expected, desired) __sync_val_compare_and_swap(dst, expected, desired) - #define ma_atomic_compare_and_swap_64(dst, expected, desired) __sync_val_compare_and_swap(dst, expected, desired) - #else - #if defined(MA_X86) - #define ma_atomic_thread_fence(order) __asm__ __volatile__("lock; addl $0, (%%esp)" ::: "memory", "cc") - #elif defined(MA_X64) - #define ma_atomic_thread_fence(order) __asm__ __volatile__("lock; addq $0, (%%rsp)" ::: "memory", "cc") - #else - #error Unsupported architecture. Please submit a feature request. - #endif - static MA_INLINE ma_uint8 ma_atomic_compare_and_swap_8(volatile ma_uint8* dst, ma_uint8 expected, ma_uint8 desired) - { - ma_uint8 result; - #if defined(MA_X86) || defined(MA_X64) - __asm__ __volatile__("lock; cmpxchg %3, %0" : "+m"(*dst), "=a"(result) : "a"(expected), "d"(desired) : "cc"); - #else - #error Unsupported architecture. Please submit a feature request. - #endif - return result; - } - static MA_INLINE ma_uint16 ma_atomic_compare_and_swap_16(volatile ma_uint16* dst, ma_uint16 expected, ma_uint16 desired) - { - ma_uint16 result; - #if defined(MA_X86) || defined(MA_X64) - __asm__ __volatile__("lock; cmpxchg %3, %0" : "+m"(*dst), "=a"(result) : "a"(expected), "d"(desired) : "cc"); - #else - #error Unsupported architecture. Please submit a feature request. - #endif - return result; - } - static MA_INLINE ma_uint32 ma_atomic_compare_and_swap_32(volatile ma_uint32* dst, ma_uint32 expected, ma_uint32 desired) - { - ma_uint32 result; - #if defined(MA_X86) || defined(MA_X64) - __asm__ __volatile__("lock; cmpxchg %3, %0" : "+m"(*dst), "=a"(result) : "a"(expected), "d"(desired) : "cc"); - #else - #error Unsupported architecture. Please submit a feature request. - #endif - return result; - } - static MA_INLINE ma_uint64 ma_atomic_compare_and_swap_64(volatile ma_uint64* dst, ma_uint64 expected, ma_uint64 desired) - { - volatile ma_uint64 result; - #if defined(MA_X86) - ma_uint32 resultEAX; - ma_uint32 resultEDX; - __asm__ __volatile__("push %%ebx; xchg %5, %%ebx; lock; cmpxchg8b %0; pop %%ebx" : "+m"(*dst), "=a"(resultEAX), "=d"(resultEDX) : "a"(expected & 0xFFFFFFFF), "d"(expected >> 32), "r"(desired & 0xFFFFFFFF), "c"(desired >> 32) : "cc"); - result = ((ma_uint64)resultEDX << 32) | resultEAX; - #elif defined(MA_X64) - __asm__ __volatile__("lock; cmpxchg %3, %0" : "+m"(*dst), "=a"(result) : "a"(expected), "d"(desired) : "cc"); - #else - #error Unsupported architecture. Please submit a feature request. - #endif - return result; - } - static MA_INLINE ma_uint8 ma_atomic_exchange_explicit_8(volatile ma_uint8* dst, ma_uint8 src, ma_atomic_memory_order order) - { - ma_uint8 result = 0; - (void)order; - #if defined(MA_X86) || defined(MA_X64) - __asm__ __volatile__("lock; xchg %1, %0" : "+m"(*dst), "=a"(result) : "a"(src)); - #else - #error Unsupported architecture. Please submit a feature request. - #endif - return result; - } - static MA_INLINE ma_uint16 ma_atomic_exchange_explicit_16(volatile ma_uint16* dst, ma_uint16 src, ma_atomic_memory_order order) - { - ma_uint16 result = 0; - (void)order; - #if defined(MA_X86) || defined(MA_X64) - __asm__ __volatile__("lock; xchg %1, %0" : "+m"(*dst), "=a"(result) : "a"(src)); - #else - #error Unsupported architecture. Please submit a feature request. - #endif - return result; - } - static MA_INLINE ma_uint32 ma_atomic_exchange_explicit_32(volatile ma_uint32* dst, ma_uint32 src, ma_atomic_memory_order order) - { - ma_uint32 result; - (void)order; - #if defined(MA_X86) || defined(MA_X64) - __asm__ __volatile__("lock; xchg %1, %0" : "+m"(*dst), "=a"(result) : "a"(src)); - #else - #error Unsupported architecture. Please submit a feature request. - #endif - return result; - } - static MA_INLINE ma_uint64 ma_atomic_exchange_explicit_64(volatile ma_uint64* dst, ma_uint64 src, ma_atomic_memory_order order) - { - ma_uint64 result; - (void)order; - #if defined(MA_X86) - do { - result = *dst; - } while (ma_atomic_compare_and_swap_64(dst, result, src) != result); - #elif defined(MA_X64) - __asm__ __volatile__("lock; xchg %1, %0" : "+m"(*dst), "=a"(result) : "a"(src)); - #else - #error Unsupported architecture. Please submit a feature request. - #endif - return result; - } - static MA_INLINE ma_uint8 ma_atomic_fetch_add_explicit_8(volatile ma_uint8* dst, ma_uint8 src, ma_atomic_memory_order order) - { - ma_uint8 result; - (void)order; - #if defined(MA_X86) || defined(MA_X64) - __asm__ __volatile__("lock; xadd %1, %0" : "+m"(*dst), "=a"(result) : "a"(src) : "cc"); - #else - #error Unsupported architecture. Please submit a feature request. - #endif - return result; - } - static MA_INLINE ma_uint16 ma_atomic_fetch_add_explicit_16(volatile ma_uint16* dst, ma_uint16 src, ma_atomic_memory_order order) - { - ma_uint16 result; - (void)order; - #if defined(MA_X86) || defined(MA_X64) - __asm__ __volatile__("lock; xadd %1, %0" : "+m"(*dst), "=a"(result) : "a"(src) : "cc"); - #else - #error Unsupported architecture. Please submit a feature request. - #endif - return result; - } - static MA_INLINE ma_uint32 ma_atomic_fetch_add_explicit_32(volatile ma_uint32* dst, ma_uint32 src, ma_atomic_memory_order order) - { - ma_uint32 result; - (void)order; - #if defined(MA_X86) || defined(MA_X64) - __asm__ __volatile__("lock; xadd %1, %0" : "+m"(*dst), "=a"(result) : "a"(src) : "cc"); - #else - #error Unsupported architecture. Please submit a feature request. - #endif - return result; - } - static MA_INLINE ma_uint64 ma_atomic_fetch_add_explicit_64(volatile ma_uint64* dst, ma_uint64 src, ma_atomic_memory_order order) - { - #if defined(MA_X86) - ma_uint64 oldValue; - ma_uint64 newValue; - (void)order; - do { - oldValue = *dst; - newValue = oldValue + src; - } while (ma_atomic_compare_and_swap_64(dst, oldValue, newValue) != oldValue); - return oldValue; - #elif defined(MA_X64) - ma_uint64 result; - (void)order; - __asm__ __volatile__("lock; xadd %1, %0" : "+m"(*dst), "=a"(result) : "a"(src) : "cc"); - return result; - #endif - } - static MA_INLINE ma_uint8 ma_atomic_fetch_sub_explicit_8(volatile ma_uint8* dst, ma_uint8 src, ma_atomic_memory_order order) - { - ma_uint8 oldValue; - ma_uint8 newValue; - do { - oldValue = *dst; - newValue = (ma_uint8)(oldValue - src); - } while (ma_atomic_compare_and_swap_8(dst, oldValue, newValue) != oldValue); - (void)order; - return oldValue; - } - static MA_INLINE ma_uint16 ma_atomic_fetch_sub_explicit_16(volatile ma_uint16* dst, ma_uint16 src, ma_atomic_memory_order order) - { - ma_uint16 oldValue; - ma_uint16 newValue; - do { - oldValue = *dst; - newValue = (ma_uint16)(oldValue - src); - } while (ma_atomic_compare_and_swap_16(dst, oldValue, newValue) != oldValue); - (void)order; - return oldValue; - } - static MA_INLINE ma_uint32 ma_atomic_fetch_sub_explicit_32(volatile ma_uint32* dst, ma_uint32 src, ma_atomic_memory_order order) - { - ma_uint32 oldValue; - ma_uint32 newValue; - do { - oldValue = *dst; - newValue = oldValue - src; - } while (ma_atomic_compare_and_swap_32(dst, oldValue, newValue) != oldValue); - (void)order; - return oldValue; - } - static MA_INLINE ma_uint64 ma_atomic_fetch_sub_explicit_64(volatile ma_uint64* dst, ma_uint64 src, ma_atomic_memory_order order) - { - ma_uint64 oldValue; - ma_uint64 newValue; - do { - oldValue = *dst; - newValue = oldValue - src; - } while (ma_atomic_compare_and_swap_64(dst, oldValue, newValue) != oldValue); - (void)order; - return oldValue; - } - static MA_INLINE ma_uint8 ma_atomic_fetch_and_explicit_8(volatile ma_uint8* dst, ma_uint8 src, ma_atomic_memory_order order) - { - ma_uint8 oldValue; - ma_uint8 newValue; - do { - oldValue = *dst; - newValue = (ma_uint8)(oldValue & src); - } while (ma_atomic_compare_and_swap_8(dst, oldValue, newValue) != oldValue); - (void)order; - return oldValue; - } - static MA_INLINE ma_uint16 ma_atomic_fetch_and_explicit_16(volatile ma_uint16* dst, ma_uint16 src, ma_atomic_memory_order order) - { - ma_uint16 oldValue; - ma_uint16 newValue; - do { - oldValue = *dst; - newValue = (ma_uint16)(oldValue & src); - } while (ma_atomic_compare_and_swap_16(dst, oldValue, newValue) != oldValue); - (void)order; - return oldValue; - } - static MA_INLINE ma_uint32 ma_atomic_fetch_and_explicit_32(volatile ma_uint32* dst, ma_uint32 src, ma_atomic_memory_order order) - { - ma_uint32 oldValue; - ma_uint32 newValue; - do { - oldValue = *dst; - newValue = oldValue & src; - } while (ma_atomic_compare_and_swap_32(dst, oldValue, newValue) != oldValue); - (void)order; - return oldValue; - } - static MA_INLINE ma_uint64 ma_atomic_fetch_and_explicit_64(volatile ma_uint64* dst, ma_uint64 src, ma_atomic_memory_order order) - { - ma_uint64 oldValue; - ma_uint64 newValue; - do { - oldValue = *dst; - newValue = oldValue & src; - } while (ma_atomic_compare_and_swap_64(dst, oldValue, newValue) != oldValue); - (void)order; - return oldValue; - } - static MA_INLINE ma_uint8 ma_atomic_fetch_xor_explicit_8(volatile ma_uint8* dst, ma_uint8 src, ma_atomic_memory_order order) - { - ma_uint8 oldValue; - ma_uint8 newValue; - do { - oldValue = *dst; - newValue = (ma_uint8)(oldValue ^ src); - } while (ma_atomic_compare_and_swap_8(dst, oldValue, newValue) != oldValue); - (void)order; - return oldValue; - } - static MA_INLINE ma_uint16 ma_atomic_fetch_xor_explicit_16(volatile ma_uint16* dst, ma_uint16 src, ma_atomic_memory_order order) - { - ma_uint16 oldValue; - ma_uint16 newValue; - do { - oldValue = *dst; - newValue = (ma_uint16)(oldValue ^ src); - } while (ma_atomic_compare_and_swap_16(dst, oldValue, newValue) != oldValue); - (void)order; - return oldValue; - } - static MA_INLINE ma_uint32 ma_atomic_fetch_xor_explicit_32(volatile ma_uint32* dst, ma_uint32 src, ma_atomic_memory_order order) - { - ma_uint32 oldValue; - ma_uint32 newValue; - do { - oldValue = *dst; - newValue = oldValue ^ src; - } while (ma_atomic_compare_and_swap_32(dst, oldValue, newValue) != oldValue); - (void)order; - return oldValue; - } - static MA_INLINE ma_uint64 ma_atomic_fetch_xor_explicit_64(volatile ma_uint64* dst, ma_uint64 src, ma_atomic_memory_order order) - { - ma_uint64 oldValue; - ma_uint64 newValue; - do { - oldValue = *dst; - newValue = oldValue ^ src; - } while (ma_atomic_compare_and_swap_64(dst, oldValue, newValue) != oldValue); - (void)order; - return oldValue; - } - static MA_INLINE ma_uint8 ma_atomic_fetch_or_explicit_8(volatile ma_uint8* dst, ma_uint8 src, ma_atomic_memory_order order) - { - ma_uint8 oldValue; - ma_uint8 newValue; - do { - oldValue = *dst; - newValue = (ma_uint8)(oldValue | src); - } while (ma_atomic_compare_and_swap_8(dst, oldValue, newValue) != oldValue); - (void)order; - return oldValue; - } - static MA_INLINE ma_uint16 ma_atomic_fetch_or_explicit_16(volatile ma_uint16* dst, ma_uint16 src, ma_atomic_memory_order order) - { - ma_uint16 oldValue; - ma_uint16 newValue; - do { - oldValue = *dst; - newValue = (ma_uint16)(oldValue | src); - } while (ma_atomic_compare_and_swap_16(dst, oldValue, newValue) != oldValue); - (void)order; - return oldValue; - } - static MA_INLINE ma_uint32 ma_atomic_fetch_or_explicit_32(volatile ma_uint32* dst, ma_uint32 src, ma_atomic_memory_order order) - { - ma_uint32 oldValue; - ma_uint32 newValue; - do { - oldValue = *dst; - newValue = oldValue | src; - } while (ma_atomic_compare_and_swap_32(dst, oldValue, newValue) != oldValue); - (void)order; - return oldValue; - } - static MA_INLINE ma_uint64 ma_atomic_fetch_or_explicit_64(volatile ma_uint64* dst, ma_uint64 src, ma_atomic_memory_order order) - { - ma_uint64 oldValue; - ma_uint64 newValue; - do { - oldValue = *dst; - newValue = oldValue | src; - } while (ma_atomic_compare_and_swap_64(dst, oldValue, newValue) != oldValue); - (void)order; - return oldValue; - } - #endif - #define ma_atomic_signal_fence(order) ma_atomic_thread_fence(order) - static MA_INLINE ma_uint8 ma_atomic_load_explicit_8(volatile const ma_uint8* ptr, ma_atomic_memory_order order) - { - (void)order; - return ma_atomic_compare_and_swap_8((ma_uint8*)ptr, 0, 0); - } - static MA_INLINE ma_uint16 ma_atomic_load_explicit_16(volatile const ma_uint16* ptr, ma_atomic_memory_order order) - { - (void)order; - return ma_atomic_compare_and_swap_16((ma_uint16*)ptr, 0, 0); - } - static MA_INLINE ma_uint32 ma_atomic_load_explicit_32(volatile const ma_uint32* ptr, ma_atomic_memory_order order) - { - (void)order; - return ma_atomic_compare_and_swap_32((ma_uint32*)ptr, 0, 0); - } - static MA_INLINE ma_uint64 ma_atomic_load_explicit_64(volatile const ma_uint64* ptr, ma_atomic_memory_order order) - { - (void)order; - return ma_atomic_compare_and_swap_64((ma_uint64*)ptr, 0, 0); - } - #define ma_atomic_store_explicit_8( dst, src, order) (void)ma_atomic_exchange_explicit_8 (dst, src, order) - #define ma_atomic_store_explicit_16(dst, src, order) (void)ma_atomic_exchange_explicit_16(dst, src, order) - #define ma_atomic_store_explicit_32(dst, src, order) (void)ma_atomic_exchange_explicit_32(dst, src, order) - #define ma_atomic_store_explicit_64(dst, src, order) (void)ma_atomic_exchange_explicit_64(dst, src, order) - #define ma_atomic_test_and_set_explicit_8( dst, order) ma_atomic_exchange_explicit_8 (dst, 1, order) - #define ma_atomic_test_and_set_explicit_16(dst, order) ma_atomic_exchange_explicit_16(dst, 1, order) - #define ma_atomic_test_and_set_explicit_32(dst, order) ma_atomic_exchange_explicit_32(dst, 1, order) - #define ma_atomic_test_and_set_explicit_64(dst, order) ma_atomic_exchange_explicit_64(dst, 1, order) - #define ma_atomic_clear_explicit_8( dst, order) ma_atomic_store_explicit_8 (dst, 0, order) - #define ma_atomic_clear_explicit_16(dst, order) ma_atomic_store_explicit_16(dst, 0, order) - #define ma_atomic_clear_explicit_32(dst, order) ma_atomic_store_explicit_32(dst, 0, order) - #define ma_atomic_clear_explicit_64(dst, order) ma_atomic_store_explicit_64(dst, 0, order) - typedef ma_uint8 ma_atomic_flag; - #define ma_atomic_flag_test_and_set_explicit(ptr, order) (ma_bool32)ma_atomic_test_and_set_explicit_8(ptr, order) - #define ma_atomic_flag_clear_explicit(ptr, order) ma_atomic_clear_explicit_8(ptr, order) - #define ma_atomic_flag_load_explicit(ptr, order) ma_atomic_load_explicit_8(ptr, order) -#endif -#if !defined(MA_ATOMIC_HAS_NATIVE_COMPARE_EXCHANGE) - #if defined(MA_ATOMIC_HAS_8) - static MA_INLINE ma_bool32 ma_atomic_compare_exchange_strong_explicit_8(volatile ma_uint8* dst, ma_uint8* expected, ma_uint8 desired, ma_atomic_memory_order successOrder, ma_atomic_memory_order failureOrder) - { - ma_uint8 expectedValue; - ma_uint8 result; - (void)successOrder; - (void)failureOrder; - expectedValue = ma_atomic_load_explicit_8(expected, ma_atomic_memory_order_seq_cst); - result = ma_atomic_compare_and_swap_8(dst, expectedValue, desired); - if (result == expectedValue) { - return 1; - } else { - ma_atomic_store_explicit_8(expected, result, failureOrder); - return 0; - } - } - #endif - #if defined(MA_ATOMIC_HAS_16) - static MA_INLINE ma_bool32 ma_atomic_compare_exchange_strong_explicit_16(volatile ma_uint16* dst, ma_uint16* expected, ma_uint16 desired, ma_atomic_memory_order successOrder, ma_atomic_memory_order failureOrder) - { - ma_uint16 expectedValue; - ma_uint16 result; - (void)successOrder; - (void)failureOrder; - expectedValue = ma_atomic_load_explicit_16(expected, ma_atomic_memory_order_seq_cst); - result = ma_atomic_compare_and_swap_16(dst, expectedValue, desired); - if (result == expectedValue) { - return 1; - } else { - ma_atomic_store_explicit_16(expected, result, failureOrder); - return 0; - } - } - #endif - #if defined(MA_ATOMIC_HAS_32) - static MA_INLINE ma_bool32 ma_atomic_compare_exchange_strong_explicit_32(volatile ma_uint32* dst, ma_uint32* expected, ma_uint32 desired, ma_atomic_memory_order successOrder, ma_atomic_memory_order failureOrder) - { - ma_uint32 expectedValue; - ma_uint32 result; - (void)successOrder; - (void)failureOrder; - expectedValue = ma_atomic_load_explicit_32(expected, ma_atomic_memory_order_seq_cst); - result = ma_atomic_compare_and_swap_32(dst, expectedValue, desired); - if (result == expectedValue) { - return 1; - } else { - ma_atomic_store_explicit_32(expected, result, failureOrder); - return 0; - } - } - #endif - #if defined(MA_ATOMIC_HAS_64) - static MA_INLINE ma_bool32 ma_atomic_compare_exchange_strong_explicit_64(volatile ma_uint64* dst, volatile ma_uint64* expected, ma_uint64 desired, ma_atomic_memory_order successOrder, ma_atomic_memory_order failureOrder) - { - ma_uint64 expectedValue; - ma_uint64 result; - (void)successOrder; - (void)failureOrder; - expectedValue = ma_atomic_load_explicit_64(expected, ma_atomic_memory_order_seq_cst); - result = ma_atomic_compare_and_swap_64(dst, expectedValue, desired); - if (result == expectedValue) { - return 1; - } else { - ma_atomic_store_explicit_64(expected, result, failureOrder); - return 0; - } - } - #endif - #define ma_atomic_compare_exchange_weak_explicit_8( dst, expected, desired, successOrder, failureOrder) ma_atomic_compare_exchange_strong_explicit_8 (dst, expected, desired, successOrder, failureOrder) - #define ma_atomic_compare_exchange_weak_explicit_16(dst, expected, desired, successOrder, failureOrder) ma_atomic_compare_exchange_strong_explicit_16(dst, expected, desired, successOrder, failureOrder) - #define ma_atomic_compare_exchange_weak_explicit_32(dst, expected, desired, successOrder, failureOrder) ma_atomic_compare_exchange_strong_explicit_32(dst, expected, desired, successOrder, failureOrder) - #define ma_atomic_compare_exchange_weak_explicit_64(dst, expected, desired, successOrder, failureOrder) ma_atomic_compare_exchange_strong_explicit_64(dst, expected, desired, successOrder, failureOrder) -#endif -#if !defined(MA_ATOMIC_HAS_NATIVE_IS_LOCK_FREE) - static MA_INLINE ma_bool32 ma_atomic_is_lock_free_8(volatile void* ptr) - { - (void)ptr; - return 1; - } - static MA_INLINE ma_bool32 ma_atomic_is_lock_free_16(volatile void* ptr) - { - (void)ptr; - return 1; - } - static MA_INLINE ma_bool32 ma_atomic_is_lock_free_32(volatile void* ptr) - { - (void)ptr; - return 1; - } - static MA_INLINE ma_bool32 ma_atomic_is_lock_free_64(volatile void* ptr) - { - (void)ptr; - #if defined(MA_64BIT) - return 1; - #else - #if defined(MA_X86) || defined(MA_X64) - return 1; - #else - return 0; - #endif - #endif - } -#endif -#if defined(MA_64BIT) - static MA_INLINE ma_bool32 ma_atomic_is_lock_free_ptr(volatile void** ptr) - { - return ma_atomic_is_lock_free_64((volatile ma_uint64*)ptr); - } - static MA_INLINE void* ma_atomic_load_explicit_ptr(volatile void** ptr, ma_atomic_memory_order order) - { - return (void*)ma_atomic_load_explicit_64((volatile ma_uint64*)ptr, order); - } - static MA_INLINE void ma_atomic_store_explicit_ptr(volatile void** dst, void* src, ma_atomic_memory_order order) - { - ma_atomic_store_explicit_64((volatile ma_uint64*)dst, (ma_uint64)src, order); - } - static MA_INLINE void* ma_atomic_exchange_explicit_ptr(volatile void** dst, void* src, ma_atomic_memory_order order) - { - return (void*)ma_atomic_exchange_explicit_64((volatile ma_uint64*)dst, (ma_uint64)src, order); - } - static MA_INLINE ma_bool32 ma_atomic_compare_exchange_strong_explicit_ptr(volatile void** dst, void** expected, void* desired, ma_atomic_memory_order successOrder, ma_atomic_memory_order failureOrder) - { - return ma_atomic_compare_exchange_strong_explicit_64((volatile ma_uint64*)dst, (ma_uint64*)expected, (ma_uint64)desired, successOrder, failureOrder); - } - static MA_INLINE ma_bool32 ma_atomic_compare_exchange_weak_explicit_ptr(volatile void** dst, void** expected, void* desired, ma_atomic_memory_order successOrder, ma_atomic_memory_order failureOrder) - { - return ma_atomic_compare_exchange_weak_explicit_64((volatile ma_uint64*)dst, (ma_uint64*)expected, (ma_uint64)desired, successOrder, failureOrder); - } - static MA_INLINE void* ma_atomic_compare_and_swap_ptr(volatile void** dst, void* expected, void* desired) - { - return (void*)ma_atomic_compare_and_swap_64((volatile ma_uint64*)dst, (ma_uint64)expected, (ma_uint64)desired); - } -#elif defined(MA_32BIT) - static MA_INLINE ma_bool32 ma_atomic_is_lock_free_ptr(volatile void** ptr) - { - return ma_atomic_is_lock_free_32((volatile ma_uint32*)ptr); - } - static MA_INLINE void* ma_atomic_load_explicit_ptr(volatile void** ptr, ma_atomic_memory_order order) - { - return (void*)ma_atomic_load_explicit_32((volatile ma_uint32*)ptr, order); - } - static MA_INLINE void ma_atomic_store_explicit_ptr(volatile void** dst, void* src, ma_atomic_memory_order order) - { - ma_atomic_store_explicit_32((volatile ma_uint32*)dst, (ma_uint32)src, order); - } - static MA_INLINE void* ma_atomic_exchange_explicit_ptr(volatile void** dst, void* src, ma_atomic_memory_order order) - { - return (void*)ma_atomic_exchange_explicit_32((volatile ma_uint32*)dst, (ma_uint32)src, order); - } - static MA_INLINE ma_bool32 ma_atomic_compare_exchange_strong_explicit_ptr(volatile void** dst, void** expected, void* desired, ma_atomic_memory_order successOrder, ma_atomic_memory_order failureOrder) - { - return ma_atomic_compare_exchange_strong_explicit_32((volatile ma_uint32*)dst, (ma_uint32*)expected, (ma_uint32)desired, successOrder, failureOrder); - } - static MA_INLINE ma_bool32 ma_atomic_compare_exchange_weak_explicit_ptr(volatile void** dst, void** expected, void* desired, ma_atomic_memory_order successOrder, ma_atomic_memory_order failureOrder) - { - return ma_atomic_compare_exchange_weak_explicit_32((volatile ma_uint32*)dst, (ma_uint32*)expected, (ma_uint32)desired, successOrder, failureOrder); - } - static MA_INLINE void* ma_atomic_compare_and_swap_ptr(volatile void** dst, void* expected, void* desired) - { - return (void*)ma_atomic_compare_and_swap_32((volatile ma_uint32*)dst, (ma_uint32)expected, (ma_uint32)desired); - } -#else - #error Unsupported architecture. -#endif -#define ma_atomic_flag_test_and_set(ptr) ma_atomic_flag_test_and_set_explicit(ptr, ma_atomic_memory_order_seq_cst) -#define ma_atomic_flag_clear(ptr) ma_atomic_flag_clear_explicit(ptr, ma_atomic_memory_order_seq_cst) -#define ma_atomic_store_ptr(dst, src) ma_atomic_store_explicit_ptr((volatile void**)dst, (void*)src, ma_atomic_memory_order_seq_cst) -#define ma_atomic_load_ptr(ptr) ma_atomic_load_explicit_ptr((volatile void**)ptr, ma_atomic_memory_order_seq_cst) -#define ma_atomic_exchange_ptr(dst, src) ma_atomic_exchange_explicit_ptr((volatile void**)dst, (void*)src, ma_atomic_memory_order_seq_cst) -#define ma_atomic_compare_exchange_strong_ptr(dst, expected, desired) ma_atomic_compare_exchange_strong_explicit_ptr((volatile void**)dst, (void**)expected, (void*)desired, ma_atomic_memory_order_seq_cst, ma_atomic_memory_order_seq_cst) -#define ma_atomic_compare_exchange_weak_ptr(dst, expected, desired) ma_atomic_compare_exchange_weak_explicit_ptr((volatile void**)dst, (void**)expected, (void*)desired, ma_atomic_memory_order_seq_cst, ma_atomic_memory_order_seq_cst) -#define ma_atomic_test_and_set_8( ptr) ma_atomic_test_and_set_explicit_8( ptr, ma_atomic_memory_order_seq_cst) -#define ma_atomic_test_and_set_16(ptr) ma_atomic_test_and_set_explicit_16(ptr, ma_atomic_memory_order_seq_cst) -#define ma_atomic_test_and_set_32(ptr) ma_atomic_test_and_set_explicit_32(ptr, ma_atomic_memory_order_seq_cst) -#define ma_atomic_test_and_set_64(ptr) ma_atomic_test_and_set_explicit_64(ptr, ma_atomic_memory_order_seq_cst) -#define ma_atomic_clear_8( ptr) ma_atomic_clear_explicit_8( ptr, ma_atomic_memory_order_seq_cst) -#define ma_atomic_clear_16(ptr) ma_atomic_clear_explicit_16(ptr, ma_atomic_memory_order_seq_cst) -#define ma_atomic_clear_32(ptr) ma_atomic_clear_explicit_32(ptr, ma_atomic_memory_order_seq_cst) -#define ma_atomic_clear_64(ptr) ma_atomic_clear_explicit_64(ptr, ma_atomic_memory_order_seq_cst) -#define ma_atomic_store_8( dst, src) ma_atomic_store_explicit_8( dst, src, ma_atomic_memory_order_seq_cst) -#define ma_atomic_store_16(dst, src) ma_atomic_store_explicit_16(dst, src, ma_atomic_memory_order_seq_cst) -#define ma_atomic_store_32(dst, src) ma_atomic_store_explicit_32(dst, src, ma_atomic_memory_order_seq_cst) -#define ma_atomic_store_64(dst, src) ma_atomic_store_explicit_64(dst, src, ma_atomic_memory_order_seq_cst) -#define ma_atomic_load_8( ptr) ma_atomic_load_explicit_8( ptr, ma_atomic_memory_order_seq_cst) -#define ma_atomic_load_16(ptr) ma_atomic_load_explicit_16(ptr, ma_atomic_memory_order_seq_cst) -#define ma_atomic_load_32(ptr) ma_atomic_load_explicit_32(ptr, ma_atomic_memory_order_seq_cst) -#define ma_atomic_load_64(ptr) ma_atomic_load_explicit_64(ptr, ma_atomic_memory_order_seq_cst) -#define ma_atomic_exchange_8( dst, src) ma_atomic_exchange_explicit_8( dst, src, ma_atomic_memory_order_seq_cst) -#define ma_atomic_exchange_16(dst, src) ma_atomic_exchange_explicit_16(dst, src, ma_atomic_memory_order_seq_cst) -#define ma_atomic_exchange_32(dst, src) ma_atomic_exchange_explicit_32(dst, src, ma_atomic_memory_order_seq_cst) -#define ma_atomic_exchange_64(dst, src) ma_atomic_exchange_explicit_64(dst, src, ma_atomic_memory_order_seq_cst) -#define ma_atomic_compare_exchange_strong_8( dst, expected, desired) ma_atomic_compare_exchange_strong_explicit_8( dst, expected, desired, ma_atomic_memory_order_seq_cst, ma_atomic_memory_order_seq_cst) -#define ma_atomic_compare_exchange_strong_16(dst, expected, desired) ma_atomic_compare_exchange_strong_explicit_16(dst, expected, desired, ma_atomic_memory_order_seq_cst, ma_atomic_memory_order_seq_cst) -#define ma_atomic_compare_exchange_strong_32(dst, expected, desired) ma_atomic_compare_exchange_strong_explicit_32(dst, expected, desired, ma_atomic_memory_order_seq_cst, ma_atomic_memory_order_seq_cst) -#define ma_atomic_compare_exchange_strong_64(dst, expected, desired) ma_atomic_compare_exchange_strong_explicit_64(dst, expected, desired, ma_atomic_memory_order_seq_cst, ma_atomic_memory_order_seq_cst) -#define ma_atomic_compare_exchange_weak_8( dst, expected, desired) ma_atomic_compare_exchange_weak_explicit_8( dst, expected, desired, ma_atomic_memory_order_seq_cst, ma_atomic_memory_order_seq_cst) -#define ma_atomic_compare_exchange_weak_16( dst, expected, desired) ma_atomic_compare_exchange_weak_explicit_16(dst, expected, desired, ma_atomic_memory_order_seq_cst, ma_atomic_memory_order_seq_cst) -#define ma_atomic_compare_exchange_weak_32( dst, expected, desired) ma_atomic_compare_exchange_weak_explicit_32(dst, expected, desired, ma_atomic_memory_order_seq_cst, ma_atomic_memory_order_seq_cst) -#define ma_atomic_compare_exchange_weak_64( dst, expected, desired) ma_atomic_compare_exchange_weak_explicit_64(dst, expected, desired, ma_atomic_memory_order_seq_cst, ma_atomic_memory_order_seq_cst) -#define ma_atomic_fetch_add_8( dst, src) ma_atomic_fetch_add_explicit_8( dst, src, ma_atomic_memory_order_seq_cst) -#define ma_atomic_fetch_add_16(dst, src) ma_atomic_fetch_add_explicit_16(dst, src, ma_atomic_memory_order_seq_cst) -#define ma_atomic_fetch_add_32(dst, src) ma_atomic_fetch_add_explicit_32(dst, src, ma_atomic_memory_order_seq_cst) -#define ma_atomic_fetch_add_64(dst, src) ma_atomic_fetch_add_explicit_64(dst, src, ma_atomic_memory_order_seq_cst) -#define ma_atomic_fetch_sub_8( dst, src) ma_atomic_fetch_sub_explicit_8( dst, src, ma_atomic_memory_order_seq_cst) -#define ma_atomic_fetch_sub_16(dst, src) ma_atomic_fetch_sub_explicit_16(dst, src, ma_atomic_memory_order_seq_cst) -#define ma_atomic_fetch_sub_32(dst, src) ma_atomic_fetch_sub_explicit_32(dst, src, ma_atomic_memory_order_seq_cst) -#define ma_atomic_fetch_sub_64(dst, src) ma_atomic_fetch_sub_explicit_64(dst, src, ma_atomic_memory_order_seq_cst) -#define ma_atomic_fetch_or_8( dst, src) ma_atomic_fetch_or_explicit_8( dst, src, ma_atomic_memory_order_seq_cst) -#define ma_atomic_fetch_or_16(dst, src) ma_atomic_fetch_or_explicit_16(dst, src, ma_atomic_memory_order_seq_cst) -#define ma_atomic_fetch_or_32(dst, src) ma_atomic_fetch_or_explicit_32(dst, src, ma_atomic_memory_order_seq_cst) -#define ma_atomic_fetch_or_64(dst, src) ma_atomic_fetch_or_explicit_64(dst, src, ma_atomic_memory_order_seq_cst) -#define ma_atomic_fetch_xor_8( dst, src) ma_atomic_fetch_xor_explicit_8( dst, src, ma_atomic_memory_order_seq_cst) -#define ma_atomic_fetch_xor_16(dst, src) ma_atomic_fetch_xor_explicit_16(dst, src, ma_atomic_memory_order_seq_cst) -#define ma_atomic_fetch_xor_32(dst, src) ma_atomic_fetch_xor_explicit_32(dst, src, ma_atomic_memory_order_seq_cst) -#define ma_atomic_fetch_xor_64(dst, src) ma_atomic_fetch_xor_explicit_64(dst, src, ma_atomic_memory_order_seq_cst) -#define ma_atomic_fetch_and_8( dst, src) ma_atomic_fetch_and_explicit_8 (dst, src, ma_atomic_memory_order_seq_cst) -#define ma_atomic_fetch_and_16(dst, src) ma_atomic_fetch_and_explicit_16(dst, src, ma_atomic_memory_order_seq_cst) -#define ma_atomic_fetch_and_32(dst, src) ma_atomic_fetch_and_explicit_32(dst, src, ma_atomic_memory_order_seq_cst) -#define ma_atomic_fetch_and_64(dst, src) ma_atomic_fetch_and_explicit_64(dst, src, ma_atomic_memory_order_seq_cst) -#define ma_atomic_test_and_set_explicit_i8( ptr, order) (ma_int8 )ma_atomic_test_and_set_explicit_8( (ma_uint8* )ptr, order) -#define ma_atomic_test_and_set_explicit_i16(ptr, order) (ma_int16)ma_atomic_test_and_set_explicit_16((ma_uint16*)ptr, order) -#define ma_atomic_test_and_set_explicit_i32(ptr, order) (ma_int32)ma_atomic_test_and_set_explicit_32((ma_uint32*)ptr, order) -#define ma_atomic_test_and_set_explicit_i64(ptr, order) (ma_int64)ma_atomic_test_and_set_explicit_64((ma_uint64*)ptr, order) -#define ma_atomic_clear_explicit_i8( ptr, order) ma_atomic_clear_explicit_8( (ma_uint8* )ptr, order) -#define ma_atomic_clear_explicit_i16(ptr, order) ma_atomic_clear_explicit_16((ma_uint16*)ptr, order) -#define ma_atomic_clear_explicit_i32(ptr, order) ma_atomic_clear_explicit_32((ma_uint32*)ptr, order) -#define ma_atomic_clear_explicit_i64(ptr, order) ma_atomic_clear_explicit_64((ma_uint64*)ptr, order) -#define ma_atomic_store_explicit_i8( dst, src, order) ma_atomic_store_explicit_8( (ma_uint8* )dst, (ma_uint8 )src, order) -#define ma_atomic_store_explicit_i16(dst, src, order) ma_atomic_store_explicit_16((ma_uint16*)dst, (ma_uint16)src, order) -#define ma_atomic_store_explicit_i32(dst, src, order) ma_atomic_store_explicit_32((ma_uint32*)dst, (ma_uint32)src, order) -#define ma_atomic_store_explicit_i64(dst, src, order) ma_atomic_store_explicit_64((ma_uint64*)dst, (ma_uint64)src, order) -#define ma_atomic_load_explicit_i8( ptr, order) (ma_int8 )ma_atomic_load_explicit_8( (ma_uint8* )ptr, order) -#define ma_atomic_load_explicit_i16(ptr, order) (ma_int16)ma_atomic_load_explicit_16((ma_uint16*)ptr, order) -#define ma_atomic_load_explicit_i32(ptr, order) (ma_int32)ma_atomic_load_explicit_32((ma_uint32*)ptr, order) -#define ma_atomic_load_explicit_i64(ptr, order) (ma_int64)ma_atomic_load_explicit_64((ma_uint64*)ptr, order) -#define ma_atomic_exchange_explicit_i8( dst, src, order) (ma_int8 )ma_atomic_exchange_explicit_8 ((ma_uint8* )dst, (ma_uint8 )src, order) -#define ma_atomic_exchange_explicit_i16(dst, src, order) (ma_int16)ma_atomic_exchange_explicit_16((ma_uint16*)dst, (ma_uint16)src, order) -#define ma_atomic_exchange_explicit_i32(dst, src, order) (ma_int32)ma_atomic_exchange_explicit_32((ma_uint32*)dst, (ma_uint32)src, order) -#define ma_atomic_exchange_explicit_i64(dst, src, order) (ma_int64)ma_atomic_exchange_explicit_64((ma_uint64*)dst, (ma_uint64)src, order) -#define ma_atomic_compare_exchange_strong_explicit_i8( dst, expected, desired, successOrder, failureOrder) ma_atomic_compare_exchange_strong_explicit_8( (ma_uint8* )dst, (ma_uint8* )expected, (ma_uint8 )desired, successOrder, failureOrder) -#define ma_atomic_compare_exchange_strong_explicit_i16(dst, expected, desired, successOrder, failureOrder) ma_atomic_compare_exchange_strong_explicit_16((ma_uint16*)dst, (ma_uint16*)expected, (ma_uint16)desired, successOrder, failureOrder) -#define ma_atomic_compare_exchange_strong_explicit_i32(dst, expected, desired, successOrder, failureOrder) ma_atomic_compare_exchange_strong_explicit_32((ma_uint32*)dst, (ma_uint32*)expected, (ma_uint32)desired, successOrder, failureOrder) -#define ma_atomic_compare_exchange_strong_explicit_i64(dst, expected, desired, successOrder, failureOrder) ma_atomic_compare_exchange_strong_explicit_64((ma_uint64*)dst, (ma_uint64*)expected, (ma_uint64)desired, successOrder, failureOrder) -#define ma_atomic_compare_exchange_weak_explicit_i8( dst, expected, desired, successOrder, failureOrder) ma_atomic_compare_exchange_weak_explicit_8( (ma_uint8* )dst, (ma_uint8* )expected, (ma_uint8 )desired, successOrder, failureOrder) -#define ma_atomic_compare_exchange_weak_explicit_i16(dst, expected, desired, successOrder, failureOrder) ma_atomic_compare_exchange_weak_explicit_16((ma_uint16*)dst, (ma_uint16*)expected, (ma_uint16)desired, successOrder, failureOrder) -#define ma_atomic_compare_exchange_weak_explicit_i32(dst, expected, desired, successOrder, failureOrder) ma_atomic_compare_exchange_weak_explicit_32((ma_uint32*)dst, (ma_uint32*)expected, (ma_uint32)desired, successOrder, failureOrder) -#define ma_atomic_compare_exchange_weak_explicit_i64(dst, expected, desired, successOrder, failureOrder) ma_atomic_compare_exchange_weak_explicit_64((ma_uint64*)dst, (ma_uint64*)expected, (ma_uint64)desired, successOrder, failureOrder) -#define ma_atomic_fetch_add_explicit_i8( dst, src, order) (ma_int8 )ma_atomic_fetch_add_explicit_8( (ma_uint8* )dst, (ma_uint8 )src, order) -#define ma_atomic_fetch_add_explicit_i16(dst, src, order) (ma_int16)ma_atomic_fetch_add_explicit_16((ma_uint16*)dst, (ma_uint16)src, order) -#define ma_atomic_fetch_add_explicit_i32(dst, src, order) (ma_int32)ma_atomic_fetch_add_explicit_32((ma_uint32*)dst, (ma_uint32)src, order) -#define ma_atomic_fetch_add_explicit_i64(dst, src, order) (ma_int64)ma_atomic_fetch_add_explicit_64((ma_uint64*)dst, (ma_uint64)src, order) -#define ma_atomic_fetch_sub_explicit_i8( dst, src, order) (ma_int8 )ma_atomic_fetch_sub_explicit_8( (ma_uint8* )dst, (ma_uint8 )src, order) -#define ma_atomic_fetch_sub_explicit_i16(dst, src, order) (ma_int16)ma_atomic_fetch_sub_explicit_16((ma_uint16*)dst, (ma_uint16)src, order) -#define ma_atomic_fetch_sub_explicit_i32(dst, src, order) (ma_int32)ma_atomic_fetch_sub_explicit_32((ma_uint32*)dst, (ma_uint32)src, order) -#define ma_atomic_fetch_sub_explicit_i64(dst, src, order) (ma_int64)ma_atomic_fetch_sub_explicit_64((ma_uint64*)dst, (ma_uint64)src, order) -#define ma_atomic_fetch_or_explicit_i8( dst, src, order) (ma_int8 )ma_atomic_fetch_or_explicit_8( (ma_uint8* )dst, (ma_uint8 )src, order) -#define ma_atomic_fetch_or_explicit_i16(dst, src, order) (ma_int16)ma_atomic_fetch_or_explicit_16((ma_uint16*)dst, (ma_uint16)src, order) -#define ma_atomic_fetch_or_explicit_i32(dst, src, order) (ma_int32)ma_atomic_fetch_or_explicit_32((ma_uint32*)dst, (ma_uint32)src, order) -#define ma_atomic_fetch_or_explicit_i64(dst, src, order) (ma_int64)ma_atomic_fetch_or_explicit_64((ma_uint64*)dst, (ma_uint64)src, order) -#define ma_atomic_fetch_xor_explicit_i8( dst, src, order) (ma_int8 )ma_atomic_fetch_xor_explicit_8( (ma_uint8* )dst, (ma_uint8 )src, order) -#define ma_atomic_fetch_xor_explicit_i16(dst, src, order) (ma_int16)ma_atomic_fetch_xor_explicit_16((ma_uint16*)dst, (ma_uint16)src, order) -#define ma_atomic_fetch_xor_explicit_i32(dst, src, order) (ma_int32)ma_atomic_fetch_xor_explicit_32((ma_uint32*)dst, (ma_uint32)src, order) -#define ma_atomic_fetch_xor_explicit_i64(dst, src, order) (ma_int64)ma_atomic_fetch_xor_explicit_64((ma_uint64*)dst, (ma_uint64)src, order) -#define ma_atomic_fetch_and_explicit_i8( dst, src, order) (ma_int8 )ma_atomic_fetch_and_explicit_8( (ma_uint8* )dst, (ma_uint8 )src, order) -#define ma_atomic_fetch_and_explicit_i16(dst, src, order) (ma_int16)ma_atomic_fetch_and_explicit_16((ma_uint16*)dst, (ma_uint16)src, order) -#define ma_atomic_fetch_and_explicit_i32(dst, src, order) (ma_int32)ma_atomic_fetch_and_explicit_32((ma_uint32*)dst, (ma_uint32)src, order) -#define ma_atomic_fetch_and_explicit_i64(dst, src, order) (ma_int64)ma_atomic_fetch_and_explicit_64((ma_uint64*)dst, (ma_uint64)src, order) -#define ma_atomic_test_and_set_i8( ptr) ma_atomic_test_and_set_explicit_i8( ptr, ma_atomic_memory_order_seq_cst) -#define ma_atomic_test_and_set_i16(ptr) ma_atomic_test_and_set_explicit_i16(ptr, ma_atomic_memory_order_seq_cst) -#define ma_atomic_test_and_set_i32(ptr) ma_atomic_test_and_set_explicit_i32(ptr, ma_atomic_memory_order_seq_cst) -#define ma_atomic_test_and_set_i64(ptr) ma_atomic_test_and_set_explicit_i64(ptr, ma_atomic_memory_order_seq_cst) -#define ma_atomic_clear_i8( ptr) ma_atomic_clear_explicit_i8( ptr, ma_atomic_memory_order_seq_cst) -#define ma_atomic_clear_i16(ptr) ma_atomic_clear_explicit_i16(ptr, ma_atomic_memory_order_seq_cst) -#define ma_atomic_clear_i32(ptr) ma_atomic_clear_explicit_i32(ptr, ma_atomic_memory_order_seq_cst) -#define ma_atomic_clear_i64(ptr) ma_atomic_clear_explicit_i64(ptr, ma_atomic_memory_order_seq_cst) -#define ma_atomic_store_i8( dst, src) ma_atomic_store_explicit_i8( dst, src, ma_atomic_memory_order_seq_cst) -#define ma_atomic_store_i16(dst, src) ma_atomic_store_explicit_i16(dst, src, ma_atomic_memory_order_seq_cst) -#define ma_atomic_store_i32(dst, src) ma_atomic_store_explicit_i32(dst, src, ma_atomic_memory_order_seq_cst) -#define ma_atomic_store_i64(dst, src) ma_atomic_store_explicit_i64(dst, src, ma_atomic_memory_order_seq_cst) -#define ma_atomic_load_i8( ptr) ma_atomic_load_explicit_i8( ptr, ma_atomic_memory_order_seq_cst) -#define ma_atomic_load_i16(ptr) ma_atomic_load_explicit_i16(ptr, ma_atomic_memory_order_seq_cst) -#define ma_atomic_load_i32(ptr) ma_atomic_load_explicit_i32(ptr, ma_atomic_memory_order_seq_cst) -#define ma_atomic_load_i64(ptr) ma_atomic_load_explicit_i64(ptr, ma_atomic_memory_order_seq_cst) -#define ma_atomic_exchange_i8( dst, src) ma_atomic_exchange_explicit_i8( dst, src, ma_atomic_memory_order_seq_cst) -#define ma_atomic_exchange_i16(dst, src) ma_atomic_exchange_explicit_i16(dst, src, ma_atomic_memory_order_seq_cst) -#define ma_atomic_exchange_i32(dst, src) ma_atomic_exchange_explicit_i32(dst, src, ma_atomic_memory_order_seq_cst) -#define ma_atomic_exchange_i64(dst, src) ma_atomic_exchange_explicit_i64(dst, src, ma_atomic_memory_order_seq_cst) -#define ma_atomic_compare_exchange_strong_i8( dst, expected, desired) ma_atomic_compare_exchange_strong_explicit_i8( dst, expected, desired, ma_atomic_memory_order_seq_cst, ma_atomic_memory_order_seq_cst) -#define ma_atomic_compare_exchange_strong_i16(dst, expected, desired) ma_atomic_compare_exchange_strong_explicit_i16(dst, expected, desired, ma_atomic_memory_order_seq_cst, ma_atomic_memory_order_seq_cst) -#define ma_atomic_compare_exchange_strong_i32(dst, expected, desired) ma_atomic_compare_exchange_strong_explicit_i32(dst, expected, desired, ma_atomic_memory_order_seq_cst, ma_atomic_memory_order_seq_cst) -#define ma_atomic_compare_exchange_strong_i64(dst, expected, desired) ma_atomic_compare_exchange_strong_explicit_i64(dst, expected, desired, ma_atomic_memory_order_seq_cst, ma_atomic_memory_order_seq_cst) -#define ma_atomic_compare_exchange_weak_i8( dst, expected, desired) ma_atomic_compare_exchange_weak_explicit_i8( dst, expected, desired, ma_atomic_memory_order_seq_cst, ma_atomic_memory_order_seq_cst) -#define ma_atomic_compare_exchange_weak_i16(dst, expected, desired) ma_atomic_compare_exchange_weak_explicit_i16(dst, expected, desired, ma_atomic_memory_order_seq_cst, ma_atomic_memory_order_seq_cst) -#define ma_atomic_compare_exchange_weak_i32(dst, expected, desired) ma_atomic_compare_exchange_weak_explicit_i32(dst, expected, desired, ma_atomic_memory_order_seq_cst, ma_atomic_memory_order_seq_cst) -#define ma_atomic_compare_exchange_weak_i64(dst, expected, desired) ma_atomic_compare_exchange_weak_explicit_i64(dst, expected, desired, ma_atomic_memory_order_seq_cst, ma_atomic_memory_order_seq_cst) -#define ma_atomic_fetch_add_i8( dst, src) ma_atomic_fetch_add_explicit_i8( dst, src, ma_atomic_memory_order_seq_cst) -#define ma_atomic_fetch_add_i16(dst, src) ma_atomic_fetch_add_explicit_i16(dst, src, ma_atomic_memory_order_seq_cst) -#define ma_atomic_fetch_add_i32(dst, src) ma_atomic_fetch_add_explicit_i32(dst, src, ma_atomic_memory_order_seq_cst) -#define ma_atomic_fetch_add_i64(dst, src) ma_atomic_fetch_add_explicit_i64(dst, src, ma_atomic_memory_order_seq_cst) -#define ma_atomic_fetch_sub_i8( dst, src) ma_atomic_fetch_sub_explicit_i8( dst, src, ma_atomic_memory_order_seq_cst) -#define ma_atomic_fetch_sub_i16(dst, src) ma_atomic_fetch_sub_explicit_i16(dst, src, ma_atomic_memory_order_seq_cst) -#define ma_atomic_fetch_sub_i32(dst, src) ma_atomic_fetch_sub_explicit_i32(dst, src, ma_atomic_memory_order_seq_cst) -#define ma_atomic_fetch_sub_i64(dst, src) ma_atomic_fetch_sub_explicit_i64(dst, src, ma_atomic_memory_order_seq_cst) -#define ma_atomic_fetch_or_i8( dst, src) ma_atomic_fetch_or_explicit_i8( dst, src, ma_atomic_memory_order_seq_cst) -#define ma_atomic_fetch_or_i16(dst, src) ma_atomic_fetch_or_explicit_i16(dst, src, ma_atomic_memory_order_seq_cst) -#define ma_atomic_fetch_or_i32(dst, src) ma_atomic_fetch_or_explicit_i32(dst, src, ma_atomic_memory_order_seq_cst) -#define ma_atomic_fetch_or_i64(dst, src) ma_atomic_fetch_or_explicit_i64(dst, src, ma_atomic_memory_order_seq_cst) -#define ma_atomic_fetch_xor_i8( dst, src) ma_atomic_fetch_xor_explicit_i8( dst, src, ma_atomic_memory_order_seq_cst) -#define ma_atomic_fetch_xor_i16(dst, src) ma_atomic_fetch_xor_explicit_i16(dst, src, ma_atomic_memory_order_seq_cst) -#define ma_atomic_fetch_xor_i32(dst, src) ma_atomic_fetch_xor_explicit_i32(dst, src, ma_atomic_memory_order_seq_cst) -#define ma_atomic_fetch_xor_i64(dst, src) ma_atomic_fetch_xor_explicit_i64(dst, src, ma_atomic_memory_order_seq_cst) -#define ma_atomic_fetch_and_i8( dst, src) ma_atomic_fetch_and_explicit_i8( dst, src, ma_atomic_memory_order_seq_cst) -#define ma_atomic_fetch_and_i16(dst, src) ma_atomic_fetch_and_explicit_i16(dst, src, ma_atomic_memory_order_seq_cst) -#define ma_atomic_fetch_and_i32(dst, src) ma_atomic_fetch_and_explicit_i32(dst, src, ma_atomic_memory_order_seq_cst) -#define ma_atomic_fetch_and_i64(dst, src) ma_atomic_fetch_and_explicit_i64(dst, src, ma_atomic_memory_order_seq_cst) -#define ma_atomic_compare_and_swap_i8( dst, expected, dedsired) (ma_int8 )ma_atomic_compare_and_swap_8( (ma_uint8* )dst, (ma_uint8 )expected, (ma_uint8 )dedsired) -#define ma_atomic_compare_and_swap_i16(dst, expected, dedsired) (ma_int16)ma_atomic_compare_and_swap_16((ma_uint16*)dst, (ma_uint16)expected, (ma_uint16)dedsired) -#define ma_atomic_compare_and_swap_i32(dst, expected, dedsired) (ma_int32)ma_atomic_compare_and_swap_32((ma_uint32*)dst, (ma_uint32)expected, (ma_uint32)dedsired) -#define ma_atomic_compare_and_swap_i64(dst, expected, dedsired) (ma_int64)ma_atomic_compare_and_swap_64((ma_uint64*)dst, (ma_uint64)expected, (ma_uint64)dedsired) -typedef union -{ - ma_uint32 i; - float f; -} ma_atomic_if32; -typedef union -{ - ma_uint64 i; - double f; -} ma_atomic_if64; -#define ma_atomic_clear_explicit_f32(ptr, order) ma_atomic_clear_explicit_32((ma_uint32*)ptr, order) -#define ma_atomic_clear_explicit_f64(ptr, order) ma_atomic_clear_explicit_64((ma_uint64*)ptr, order) -static MA_INLINE void ma_atomic_store_explicit_f32(volatile float* dst, float src, ma_atomic_memory_order order) -{ - ma_atomic_if32 x; - x.f = src; - ma_atomic_store_explicit_32((volatile ma_uint32*)dst, x.i, order); -} -static MA_INLINE void ma_atomic_store_explicit_f64(volatile double* dst, double src, ma_atomic_memory_order order) -{ - ma_atomic_if64 x; - x.f = src; - ma_atomic_store_explicit_64((volatile ma_uint64*)dst, x.i, order); -} -static MA_INLINE float ma_atomic_load_explicit_f32(volatile const float* ptr, ma_atomic_memory_order order) -{ - ma_atomic_if32 r; - r.i = ma_atomic_load_explicit_32((volatile const ma_uint32*)ptr, order); - return r.f; -} -static MA_INLINE double ma_atomic_load_explicit_f64(volatile const double* ptr, ma_atomic_memory_order order) -{ - ma_atomic_if64 r; - r.i = ma_atomic_load_explicit_64((volatile const ma_uint64*)ptr, order); - return r.f; -} -static MA_INLINE float ma_atomic_exchange_explicit_f32(volatile float* dst, float src, ma_atomic_memory_order order) -{ - ma_atomic_if32 r; - ma_atomic_if32 x; - x.f = src; - r.i = ma_atomic_exchange_explicit_32((volatile ma_uint32*)dst, x.i, order); - return r.f; -} -static MA_INLINE double ma_atomic_exchange_explicit_f64(volatile double* dst, double src, ma_atomic_memory_order order) -{ - ma_atomic_if64 r; - ma_atomic_if64 x; - x.f = src; - r.i = ma_atomic_exchange_explicit_64((volatile ma_uint64*)dst, x.i, order); - return r.f; -} -static MA_INLINE ma_bool32 ma_atomic_compare_exchange_strong_explicit_f32(volatile float* dst, float* expected, float desired, ma_atomic_memory_order successOrder, ma_atomic_memory_order failureOrder) -{ - ma_atomic_if32 d; - d.f = desired; - return ma_atomic_compare_exchange_strong_explicit_32((volatile ma_uint32*)dst, (ma_uint32*)expected, d.i, successOrder, failureOrder); -} -static MA_INLINE ma_bool32 ma_atomic_compare_exchange_strong_explicit_f64(volatile double* dst, double* expected, double desired, ma_atomic_memory_order successOrder, ma_atomic_memory_order failureOrder) -{ - ma_atomic_if64 d; - d.f = desired; - return ma_atomic_compare_exchange_strong_explicit_64((volatile ma_uint64*)dst, (ma_uint64*)expected, d.i, successOrder, failureOrder); -} -static MA_INLINE ma_bool32 ma_atomic_compare_exchange_weak_explicit_f32(volatile float* dst, float* expected, float desired, ma_atomic_memory_order successOrder, ma_atomic_memory_order failureOrder) -{ - ma_atomic_if32 d; - d.f = desired; - return ma_atomic_compare_exchange_weak_explicit_32((volatile ma_uint32*)dst, (ma_uint32*)expected, d.i, successOrder, failureOrder); -} -static MA_INLINE ma_bool32 ma_atomic_compare_exchange_weak_explicit_f64(volatile double* dst, double* expected, double desired, ma_atomic_memory_order successOrder, ma_atomic_memory_order failureOrder) -{ - ma_atomic_if64 d; - d.f = desired; - return ma_atomic_compare_exchange_weak_explicit_64((volatile ma_uint64*)dst, (ma_uint64*)expected, d.i, successOrder, failureOrder); -} -static MA_INLINE float ma_atomic_fetch_add_explicit_f32(volatile float* dst, float src, ma_atomic_memory_order order) -{ - ma_atomic_if32 r; - ma_atomic_if32 x; - x.f = src; - r.i = ma_atomic_fetch_add_explicit_32((volatile ma_uint32*)dst, x.i, order); - return r.f; -} -static MA_INLINE double ma_atomic_fetch_add_explicit_f64(volatile double* dst, double src, ma_atomic_memory_order order) -{ - ma_atomic_if64 r; - ma_atomic_if64 x; - x.f = src; - r.i = ma_atomic_fetch_add_explicit_64((volatile ma_uint64*)dst, x.i, order); - return r.f; -} -static MA_INLINE float ma_atomic_fetch_sub_explicit_f32(volatile float* dst, float src, ma_atomic_memory_order order) -{ - ma_atomic_if32 r; - ma_atomic_if32 x; - x.f = src; - r.i = ma_atomic_fetch_sub_explicit_32((volatile ma_uint32*)dst, x.i, order); - return r.f; -} -static MA_INLINE double ma_atomic_fetch_sub_explicit_f64(volatile double* dst, double src, ma_atomic_memory_order order) -{ - ma_atomic_if64 r; - ma_atomic_if64 x; - x.f = src; - r.i = ma_atomic_fetch_sub_explicit_64((volatile ma_uint64*)dst, x.i, order); - return r.f; -} -static MA_INLINE float ma_atomic_fetch_or_explicit_f32(volatile float* dst, float src, ma_atomic_memory_order order) -{ - ma_atomic_if32 r; - ma_atomic_if32 x; - x.f = src; - r.i = ma_atomic_fetch_or_explicit_32((volatile ma_uint32*)dst, x.i, order); - return r.f; -} -static MA_INLINE double ma_atomic_fetch_or_explicit_f64(volatile double* dst, double src, ma_atomic_memory_order order) -{ - ma_atomic_if64 r; - ma_atomic_if64 x; - x.f = src; - r.i = ma_atomic_fetch_or_explicit_64((volatile ma_uint64*)dst, x.i, order); - return r.f; -} -static MA_INLINE float ma_atomic_fetch_xor_explicit_f32(volatile float* dst, float src, ma_atomic_memory_order order) -{ - ma_atomic_if32 r; - ma_atomic_if32 x; - x.f = src; - r.i = ma_atomic_fetch_xor_explicit_32((volatile ma_uint32*)dst, x.i, order); - return r.f; -} -static MA_INLINE double ma_atomic_fetch_xor_explicit_f64(volatile double* dst, double src, ma_atomic_memory_order order) -{ - ma_atomic_if64 r; - ma_atomic_if64 x; - x.f = src; - r.i = ma_atomic_fetch_xor_explicit_64((volatile ma_uint64*)dst, x.i, order); - return r.f; -} -static MA_INLINE float ma_atomic_fetch_and_explicit_f32(volatile float* dst, float src, ma_atomic_memory_order order) -{ - ma_atomic_if32 r; - ma_atomic_if32 x; - x.f = src; - r.i = ma_atomic_fetch_and_explicit_32((volatile ma_uint32*)dst, x.i, order); - return r.f; -} -static MA_INLINE double ma_atomic_fetch_and_explicit_f64(volatile double* dst, double src, ma_atomic_memory_order order) -{ - ma_atomic_if64 r; - ma_atomic_if64 x; - x.f = src; - r.i = ma_atomic_fetch_and_explicit_64((volatile ma_uint64*)dst, x.i, order); - return r.f; -} -#define ma_atomic_clear_f32(ptr) (float )ma_atomic_clear_explicit_f32(ptr, ma_atomic_memory_order_seq_cst) -#define ma_atomic_clear_f64(ptr) (double)ma_atomic_clear_explicit_f64(ptr, ma_atomic_memory_order_seq_cst) -#define ma_atomic_store_f32(dst, src) ma_atomic_store_explicit_f32(dst, src, ma_atomic_memory_order_seq_cst) -#define ma_atomic_store_f64(dst, src) ma_atomic_store_explicit_f64(dst, src, ma_atomic_memory_order_seq_cst) -#define ma_atomic_load_f32(ptr) (float )ma_atomic_load_explicit_f32(ptr, ma_atomic_memory_order_seq_cst) -#define ma_atomic_load_f64(ptr) (double)ma_atomic_load_explicit_f64(ptr, ma_atomic_memory_order_seq_cst) -#define ma_atomic_exchange_f32(dst, src) (float )ma_atomic_exchange_explicit_f32(dst, src, ma_atomic_memory_order_seq_cst) -#define ma_atomic_exchange_f64(dst, src) (double)ma_atomic_exchange_explicit_f64(dst, src, ma_atomic_memory_order_seq_cst) -#define ma_atomic_compare_exchange_strong_f32(dst, expected, desired) ma_atomic_compare_exchange_strong_explicit_f32(dst, expected, desired, ma_atomic_memory_order_seq_cst, ma_atomic_memory_order_seq_cst) -#define ma_atomic_compare_exchange_strong_f64(dst, expected, desired) ma_atomic_compare_exchange_strong_explicit_f64(dst, expected, desired, ma_atomic_memory_order_seq_cst, ma_atomic_memory_order_seq_cst) -#define ma_atomic_compare_exchange_weak_f32(dst, expected, desired) ma_atomic_compare_exchange_weak_explicit_f32(dst, expected, desired, ma_atomic_memory_order_seq_cst, ma_atomic_memory_order_seq_cst) -#define ma_atomic_compare_exchange_weak_f64(dst, expected, desired) ma_atomic_compare_exchange_weak_explicit_f64(dst, expected, desired, ma_atomic_memory_order_seq_cst, ma_atomic_memory_order_seq_cst) -#define ma_atomic_fetch_add_f32(dst, src) ma_atomic_fetch_add_explicit_f32(dst, src, ma_atomic_memory_order_seq_cst) -#define ma_atomic_fetch_add_f64(dst, src) ma_atomic_fetch_add_explicit_f64(dst, src, ma_atomic_memory_order_seq_cst) -#define ma_atomic_fetch_sub_f32(dst, src) ma_atomic_fetch_sub_explicit_f32(dst, src, ma_atomic_memory_order_seq_cst) -#define ma_atomic_fetch_sub_f64(dst, src) ma_atomic_fetch_sub_explicit_f64(dst, src, ma_atomic_memory_order_seq_cst) -#define ma_atomic_fetch_or_f32(dst, src) ma_atomic_fetch_or_explicit_f32(dst, src, ma_atomic_memory_order_seq_cst) -#define ma_atomic_fetch_or_f64(dst, src) ma_atomic_fetch_or_explicit_f64(dst, src, ma_atomic_memory_order_seq_cst) -#define ma_atomic_fetch_xor_f32(dst, src) ma_atomic_fetch_xor_explicit_f32(dst, src, ma_atomic_memory_order_seq_cst) -#define ma_atomic_fetch_xor_f64(dst, src) ma_atomic_fetch_xor_explicit_f64(dst, src, ma_atomic_memory_order_seq_cst) -#define ma_atomic_fetch_and_f32(dst, src) ma_atomic_fetch_and_explicit_f32(dst, src, ma_atomic_memory_order_seq_cst) -#define ma_atomic_fetch_and_f64(dst, src) ma_atomic_fetch_and_explicit_f64(dst, src, ma_atomic_memory_order_seq_cst) -static MA_INLINE float ma_atomic_compare_and_swap_f32(volatile float* dst, float expected, float desired) -{ - ma_atomic_if32 r; - ma_atomic_if32 e, d; - e.f = expected; - d.f = desired; - r.i = ma_atomic_compare_and_swap_32((volatile ma_uint32*)dst, e.i, d.i); - return r.f; -} -static MA_INLINE double ma_atomic_compare_and_swap_f64(volatile double* dst, double expected, double desired) -{ - ma_atomic_if64 r; - ma_atomic_if64 e, d; - e.f = expected; - d.f = desired; - r.i = ma_atomic_compare_and_swap_64((volatile ma_uint64*)dst, e.i, d.i); - return r.f; -} -typedef ma_atomic_flag ma_atomic_spinlock; -static MA_INLINE void ma_atomic_spinlock_lock(volatile ma_atomic_spinlock* pSpinlock) -{ - for (;;) { - if (ma_atomic_flag_test_and_set_explicit(pSpinlock, ma_atomic_memory_order_acquire) == 0) { - break; - } - while (ma_atomic_flag_load_explicit(pSpinlock, ma_atomic_memory_order_relaxed) == 1) { - } - } -} -static MA_INLINE void ma_atomic_spinlock_unlock(volatile ma_atomic_spinlock* pSpinlock) -{ - ma_atomic_flag_clear_explicit(pSpinlock, ma_atomic_memory_order_release); -} -#if defined(__clang__) || (defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6))) - #pragma GCC diagnostic pop -#endif -#if defined(__cplusplus) -} -#endif -#endif -/* c89atomic.h end */ - -#define MA_ATOMIC_SAFE_TYPE_IMPL(c89TypeExtension, type) \ - static MA_INLINE ma_##type ma_atomic_##type##_get(ma_atomic_##type* x) \ - { \ - return (ma_##type)ma_atomic_load_##c89TypeExtension(&x->value); \ - } \ - static MA_INLINE void ma_atomic_##type##_set(ma_atomic_##type* x, ma_##type value) \ - { \ - ma_atomic_store_##c89TypeExtension(&x->value, value); \ - } \ - static MA_INLINE ma_##type ma_atomic_##type##_exchange(ma_atomic_##type* x, ma_##type value) \ - { \ - return (ma_##type)ma_atomic_exchange_##c89TypeExtension(&x->value, value); \ - } \ - static MA_INLINE ma_bool32 ma_atomic_##type##_compare_exchange(ma_atomic_##type* x, ma_##type* expected, ma_##type desired) \ - { \ - return ma_atomic_compare_exchange_weak_##c89TypeExtension(&x->value, expected, desired); \ - } \ - static MA_INLINE ma_##type ma_atomic_##type##_fetch_add(ma_atomic_##type* x, ma_##type y) \ - { \ - return (ma_##type)ma_atomic_fetch_add_##c89TypeExtension(&x->value, y); \ - } \ - static MA_INLINE ma_##type ma_atomic_##type##_fetch_sub(ma_atomic_##type* x, ma_##type y) \ - { \ - return (ma_##type)ma_atomic_fetch_sub_##c89TypeExtension(&x->value, y); \ - } \ - static MA_INLINE ma_##type ma_atomic_##type##_fetch_or(ma_atomic_##type* x, ma_##type y) \ - { \ - return (ma_##type)ma_atomic_fetch_or_##c89TypeExtension(&x->value, y); \ - } \ - static MA_INLINE ma_##type ma_atomic_##type##_fetch_xor(ma_atomic_##type* x, ma_##type y) \ - { \ - return (ma_##type)ma_atomic_fetch_xor_##c89TypeExtension(&x->value, y); \ - } \ - static MA_INLINE ma_##type ma_atomic_##type##_fetch_and(ma_atomic_##type* x, ma_##type y) \ - { \ - return (ma_##type)ma_atomic_fetch_and_##c89TypeExtension(&x->value, y); \ - } \ - static MA_INLINE ma_##type ma_atomic_##type##_compare_and_swap(ma_atomic_##type* x, ma_##type expected, ma_##type desired) \ - { \ - return (ma_##type)ma_atomic_compare_and_swap_##c89TypeExtension(&x->value, expected, desired); \ - } \ - -#define MA_ATOMIC_SAFE_TYPE_IMPL_PTR(type) \ - static MA_INLINE ma_##type* ma_atomic_ptr_##type##_get(ma_atomic_ptr_##type* x) \ - { \ - return ma_atomic_load_ptr((void**)&x->value); \ - } \ - static MA_INLINE void ma_atomic_ptr_##type##_set(ma_atomic_ptr_##type* x, ma_##type* value) \ - { \ - ma_atomic_store_ptr((void**)&x->value, (void*)value); \ - } \ - static MA_INLINE ma_##type* ma_atomic_ptr_##type##_exchange(ma_atomic_ptr_##type* x, ma_##type* value) \ - { \ - return ma_atomic_exchange_ptr((void**)&x->value, (void*)value); \ - } \ - static MA_INLINE ma_bool32 ma_atomic_ptr_##type##_compare_exchange(ma_atomic_ptr_##type* x, ma_##type** expected, ma_##type* desired) \ - { \ - return ma_atomic_compare_exchange_weak_ptr((void**)&x->value, (void*)expected, (void*)desired); \ - } \ - static MA_INLINE ma_##type* ma_atomic_ptr_##type##_compare_and_swap(ma_atomic_ptr_##type* x, ma_##type* expected, ma_##type* desired) \ - { \ - return (ma_##type*)ma_atomic_compare_and_swap_ptr((void**)&x->value, (void*)expected, (void*)desired); \ - } \ - -MA_ATOMIC_SAFE_TYPE_IMPL(32, uint32) -MA_ATOMIC_SAFE_TYPE_IMPL(i32, int32) -MA_ATOMIC_SAFE_TYPE_IMPL(64, uint64) -MA_ATOMIC_SAFE_TYPE_IMPL(f32, float) -MA_ATOMIC_SAFE_TYPE_IMPL(32, bool32) - -#if !defined(MA_NO_DEVICE_IO) -MA_ATOMIC_SAFE_TYPE_IMPL(i32, device_state) -#endif - - -MA_API ma_uint64 ma_calculate_frame_count_after_resampling(ma_uint32 sampleRateOut, ma_uint32 sampleRateIn, ma_uint64 frameCountIn) -{ - /* This is based on the calculation in ma_linear_resampler_get_expected_output_frame_count(). */ - ma_uint64 outputFrameCount; - ma_uint64 preliminaryInputFrameCountFromFrac; - ma_uint64 preliminaryInputFrameCount; - - if (sampleRateIn == 0 || sampleRateOut == 0 || frameCountIn == 0) { - return 0; - } - - if (sampleRateOut == sampleRateIn) { - return frameCountIn; - } - - outputFrameCount = (frameCountIn * sampleRateOut) / sampleRateIn; - - preliminaryInputFrameCountFromFrac = (outputFrameCount * (sampleRateIn / sampleRateOut)) / sampleRateOut; - preliminaryInputFrameCount = (outputFrameCount * (sampleRateIn % sampleRateOut)) + preliminaryInputFrameCountFromFrac; - - if (preliminaryInputFrameCount <= frameCountIn) { - outputFrameCount += 1; - } - - return outputFrameCount; -} - -#ifndef MA_DATA_CONVERTER_STACK_BUFFER_SIZE -#define MA_DATA_CONVERTER_STACK_BUFFER_SIZE 4096 -#endif - - - -#if defined(MA_WIN32) -static ma_result ma_result_from_GetLastError(DWORD error) -{ - switch (error) - { - case ERROR_SUCCESS: return MA_SUCCESS; - case ERROR_PATH_NOT_FOUND: return MA_DOES_NOT_EXIST; - case ERROR_TOO_MANY_OPEN_FILES: return MA_TOO_MANY_OPEN_FILES; - case ERROR_NOT_ENOUGH_MEMORY: return MA_OUT_OF_MEMORY; - case ERROR_DISK_FULL: return MA_NO_SPACE; - case ERROR_HANDLE_EOF: return MA_AT_END; - case ERROR_NEGATIVE_SEEK: return MA_BAD_SEEK; - case ERROR_INVALID_PARAMETER: return MA_INVALID_ARGS; - case ERROR_ACCESS_DENIED: return MA_ACCESS_DENIED; - case ERROR_SEM_TIMEOUT: return MA_TIMEOUT; - case ERROR_FILE_NOT_FOUND: return MA_DOES_NOT_EXIST; - default: break; - } - - return MA_ERROR; -} -#endif /* MA_WIN32 */ - - -/******************************************************************************* - -Threading - -*******************************************************************************/ -static MA_INLINE ma_result ma_spinlock_lock_ex(volatile ma_spinlock* pSpinlock, ma_bool32 yield) -{ - if (pSpinlock == NULL) { - return MA_INVALID_ARGS; - } - - for (;;) { - if (ma_atomic_exchange_explicit_32(pSpinlock, 1, ma_atomic_memory_order_acquire) == 0) { - break; - } - - while (ma_atomic_load_explicit_32(pSpinlock, ma_atomic_memory_order_relaxed) == 1) { - if (yield) { - ma_yield(); - } - } - } - - return MA_SUCCESS; -} - -MA_API ma_result ma_spinlock_lock(volatile ma_spinlock* pSpinlock) -{ - return ma_spinlock_lock_ex(pSpinlock, MA_TRUE); -} - -MA_API ma_result ma_spinlock_lock_noyield(volatile ma_spinlock* pSpinlock) -{ - return ma_spinlock_lock_ex(pSpinlock, MA_FALSE); -} - -MA_API ma_result ma_spinlock_unlock(volatile ma_spinlock* pSpinlock) -{ - if (pSpinlock == NULL) { - return MA_INVALID_ARGS; - } - - ma_atomic_store_explicit_32(pSpinlock, 0, ma_atomic_memory_order_release); - return MA_SUCCESS; -} - - -#ifndef MA_NO_THREADING -#if defined(MA_POSIX) - #define MA_THREADCALL - typedef void* ma_thread_result; -#elif defined(MA_WIN32) - #define MA_THREADCALL WINAPI - typedef unsigned long ma_thread_result; -#endif - -typedef ma_thread_result (MA_THREADCALL * ma_thread_entry_proc)(void* pData); - -#ifdef MA_POSIX -static ma_result ma_thread_create__posix(ma_thread* pThread, ma_thread_priority priority, size_t stackSize, ma_thread_entry_proc entryProc, void* pData) -{ - int result; - pthread_attr_t* pAttr = NULL; - -#if !defined(__EMSCRIPTEN__) && !defined(__3DS__) - /* Try setting the thread priority. It's not critical if anything fails here. */ - pthread_attr_t attr; - if (pthread_attr_init(&attr) == 0) { - int scheduler = -1; - - /* We successfully initialized our attributes object so we can assign the pointer so it's passed into pthread_create(). */ - pAttr = &attr; - - /* We need to set the scheduler policy. Only do this if the OS supports pthread_attr_setschedpolicy() */ - #if !defined(MA_BEOS) - { - if (priority == ma_thread_priority_idle) { - #ifdef SCHED_IDLE - if (pthread_attr_setschedpolicy(&attr, SCHED_IDLE) == 0) { - scheduler = SCHED_IDLE; - } - #endif - } else if (priority == ma_thread_priority_realtime) { - #ifdef SCHED_FIFO - if (pthread_attr_setschedpolicy(&attr, SCHED_FIFO) == 0) { - scheduler = SCHED_FIFO; - } - #endif - #ifdef MA_LINUX - } else { - scheduler = sched_getscheduler(0); - #endif - } - } - #endif - - if (stackSize > 0) { - pthread_attr_setstacksize(&attr, stackSize); - } - - if (scheduler != -1) { - int priorityMin = sched_get_priority_min(scheduler); - int priorityMax = sched_get_priority_max(scheduler); - int priorityStep = (priorityMax - priorityMin) / 7; /* 7 = number of priorities supported by miniaudio. */ - - struct sched_param sched; - if (pthread_attr_getschedparam(&attr, &sched) == 0) { - if (priority == ma_thread_priority_idle) { - sched.sched_priority = priorityMin; - } else if (priority == ma_thread_priority_realtime) { - #if defined(MA_PTHREAD_REALTIME_THREAD_PRIORITY) - { - sched.sched_priority = MA_PTHREAD_REALTIME_THREAD_PRIORITY; - } - #else - { - sched.sched_priority = priorityMax; - } - #endif - } else { - sched.sched_priority += ((int)priority + 5) * priorityStep; /* +5 because the lowest priority is -5. */ - } - - if (sched.sched_priority < priorityMin) { - sched.sched_priority = priorityMin; - } - if (sched.sched_priority > priorityMax) { - sched.sched_priority = priorityMax; - } - - /* I'm not treating a failure of setting the priority as a critical error so not aborting on failure here. */ - if (pthread_attr_setschedparam(&attr, &sched) == 0) { - #if !defined(MA_ANDROID) || (defined(__ANDROID_API__) && __ANDROID_API__ >= 28) - { - pthread_attr_setinheritsched(&attr, PTHREAD_EXPLICIT_SCHED); - } - #endif - } - } - } - } -#else - /* It's the emscripten build. We'll have a few unused parameters. */ - (void)priority; - (void)stackSize; -#endif - - result = pthread_create((pthread_t*)pThread, pAttr, entryProc, pData); - - /* The thread attributes object is no longer required. */ - if (pAttr != NULL) { - pthread_attr_destroy(pAttr); - } - - if (result != 0) { - return ma_result_from_errno(result); - } - - return MA_SUCCESS; -} - -static void ma_thread_wait__posix(ma_thread* pThread) -{ - pthread_join((pthread_t)*pThread, NULL); -} - - -static ma_result ma_mutex_init__posix(ma_mutex* pMutex) -{ - int result; - - if (pMutex == NULL) { - return MA_INVALID_ARGS; - } - - MA_ZERO_OBJECT(pMutex); - - result = pthread_mutex_init((pthread_mutex_t*)pMutex, NULL); - if (result != 0) { - return ma_result_from_errno(result); - } - - return MA_SUCCESS; -} - -static void ma_mutex_uninit__posix(ma_mutex* pMutex) -{ - pthread_mutex_destroy((pthread_mutex_t*)pMutex); -} - -static void ma_mutex_lock__posix(ma_mutex* pMutex) -{ - pthread_mutex_lock((pthread_mutex_t*)pMutex); -} - -static void ma_mutex_unlock__posix(ma_mutex* pMutex) -{ - pthread_mutex_unlock((pthread_mutex_t*)pMutex); -} - - -static ma_result ma_event_init__posix(ma_event* pEvent) -{ - int result; - - result = pthread_mutex_init((pthread_mutex_t*)&pEvent->lock, NULL); - if (result != 0) { - return ma_result_from_errno(result); - } - - result = pthread_cond_init((pthread_cond_t*)&pEvent->cond, NULL); - if (result != 0) { - pthread_mutex_destroy((pthread_mutex_t*)&pEvent->lock); - return ma_result_from_errno(result); - } - - pEvent->value = 0; - return MA_SUCCESS; -} - -static void ma_event_uninit__posix(ma_event* pEvent) -{ - pthread_cond_destroy((pthread_cond_t*)&pEvent->cond); - pthread_mutex_destroy((pthread_mutex_t*)&pEvent->lock); -} - -static ma_result ma_event_wait__posix(ma_event* pEvent) -{ - pthread_mutex_lock((pthread_mutex_t*)&pEvent->lock); - { - while (pEvent->value == 0) { - pthread_cond_wait((pthread_cond_t*)&pEvent->cond, (pthread_mutex_t*)&pEvent->lock); - } - pEvent->value = 0; /* Auto-reset. */ - } - pthread_mutex_unlock((pthread_mutex_t*)&pEvent->lock); - - return MA_SUCCESS; -} - -static ma_result ma_event_signal__posix(ma_event* pEvent) -{ - pthread_mutex_lock((pthread_mutex_t*)&pEvent->lock); - { - pEvent->value = 1; - pthread_cond_signal((pthread_cond_t*)&pEvent->cond); - } - pthread_mutex_unlock((pthread_mutex_t*)&pEvent->lock); - - return MA_SUCCESS; -} - - -static ma_result ma_semaphore_init__posix(int initialValue, ma_semaphore* pSemaphore) -{ - int result; - - if (pSemaphore == NULL) { - return MA_INVALID_ARGS; - } - - pSemaphore->value = initialValue; - - result = pthread_mutex_init((pthread_mutex_t*)&pSemaphore->lock, NULL); - if (result != 0) { - return ma_result_from_errno(result); /* Failed to create mutex. */ - } - - result = pthread_cond_init((pthread_cond_t*)&pSemaphore->cond, NULL); - if (result != 0) { - pthread_mutex_destroy((pthread_mutex_t*)&pSemaphore->lock); - return ma_result_from_errno(result); /* Failed to create condition variable. */ - } - - return MA_SUCCESS; -} - -static void ma_semaphore_uninit__posix(ma_semaphore* pSemaphore) -{ - if (pSemaphore == NULL) { - return; - } - - pthread_cond_destroy((pthread_cond_t*)&pSemaphore->cond); - pthread_mutex_destroy((pthread_mutex_t*)&pSemaphore->lock); -} - -static ma_result ma_semaphore_wait__posix(ma_semaphore* pSemaphore) -{ - if (pSemaphore == NULL) { - return MA_INVALID_ARGS; - } - - pthread_mutex_lock((pthread_mutex_t*)&pSemaphore->lock); - { - /* We need to wait on a condition variable before escaping. We can't return from this function until the semaphore has been signaled. */ - while (pSemaphore->value == 0) { - pthread_cond_wait((pthread_cond_t*)&pSemaphore->cond, (pthread_mutex_t*)&pSemaphore->lock); - } - - pSemaphore->value -= 1; - } - pthread_mutex_unlock((pthread_mutex_t*)&pSemaphore->lock); - - return MA_SUCCESS; -} - -static ma_result ma_semaphore_release__posix(ma_semaphore* pSemaphore) -{ - if (pSemaphore == NULL) { - return MA_INVALID_ARGS; - } - - pthread_mutex_lock((pthread_mutex_t*)&pSemaphore->lock); - { - pSemaphore->value += 1; - pthread_cond_signal((pthread_cond_t*)&pSemaphore->cond); - } - pthread_mutex_unlock((pthread_mutex_t*)&pSemaphore->lock); - - return MA_SUCCESS; -} -#elif defined(MA_WIN32) -static int ma_thread_priority_to_win32(ma_thread_priority priority) -{ - switch (priority) { - case ma_thread_priority_idle: return THREAD_PRIORITY_IDLE; - case ma_thread_priority_lowest: return THREAD_PRIORITY_LOWEST; - case ma_thread_priority_low: return THREAD_PRIORITY_BELOW_NORMAL; - case ma_thread_priority_normal: return THREAD_PRIORITY_NORMAL; - case ma_thread_priority_high: return THREAD_PRIORITY_ABOVE_NORMAL; - case ma_thread_priority_highest: return THREAD_PRIORITY_HIGHEST; - case ma_thread_priority_realtime: return THREAD_PRIORITY_TIME_CRITICAL; - default: return THREAD_PRIORITY_NORMAL; - } -} - -static ma_result ma_thread_create__win32(ma_thread* pThread, ma_thread_priority priority, size_t stackSize, ma_thread_entry_proc entryProc, void* pData) -{ - DWORD threadID; /* Not used. Only used for passing into CreateThread() so it doesn't fail on Windows 98. */ - - *pThread = CreateThread(NULL, stackSize, entryProc, pData, 0, &threadID); - if (*pThread == NULL) { - return ma_result_from_GetLastError(GetLastError()); - } - - SetThreadPriority((HANDLE)*pThread, ma_thread_priority_to_win32(priority)); - - return MA_SUCCESS; -} - -static void ma_thread_wait__win32(ma_thread* pThread) -{ - WaitForSingleObject((HANDLE)*pThread, INFINITE); - CloseHandle((HANDLE)*pThread); -} - - -static ma_result ma_mutex_init__win32(ma_mutex* pMutex) -{ - *pMutex = CreateEventA(NULL, FALSE, TRUE, NULL); - if (*pMutex == NULL) { - return ma_result_from_GetLastError(GetLastError()); - } - - return MA_SUCCESS; -} - -static void ma_mutex_uninit__win32(ma_mutex* pMutex) -{ - CloseHandle((HANDLE)*pMutex); -} - -static void ma_mutex_lock__win32(ma_mutex* pMutex) -{ - WaitForSingleObject((HANDLE)*pMutex, INFINITE); -} - -static void ma_mutex_unlock__win32(ma_mutex* pMutex) -{ - SetEvent((HANDLE)*pMutex); -} - - -static ma_result ma_event_init__win32(ma_event* pEvent) -{ - *pEvent = CreateEventA(NULL, FALSE, FALSE, NULL); - if (*pEvent == NULL) { - return ma_result_from_GetLastError(GetLastError()); - } - - return MA_SUCCESS; -} - -static void ma_event_uninit__win32(ma_event* pEvent) -{ - CloseHandle((HANDLE)*pEvent); -} - -static ma_result ma_event_wait__win32(ma_event* pEvent) -{ - DWORD result = WaitForSingleObject((HANDLE)*pEvent, INFINITE); - if (result == WAIT_OBJECT_0) { - return MA_SUCCESS; - } - - if (result == WAIT_TIMEOUT) { - return MA_TIMEOUT; - } - - return ma_result_from_GetLastError(GetLastError()); -} - -static ma_result ma_event_signal__win32(ma_event* pEvent) -{ - BOOL result = SetEvent((HANDLE)*pEvent); - if (result == 0) { - return ma_result_from_GetLastError(GetLastError()); - } - - return MA_SUCCESS; -} - - -static ma_result ma_semaphore_init__win32(int initialValue, ma_semaphore* pSemaphore) -{ - *pSemaphore = CreateSemaphoreW(NULL, (LONG)initialValue, LONG_MAX, NULL); - if (*pSemaphore == NULL) { - return ma_result_from_GetLastError(GetLastError()); - } - - return MA_SUCCESS; -} - -static void ma_semaphore_uninit__win32(ma_semaphore* pSemaphore) -{ - CloseHandle((HANDLE)*pSemaphore); -} - -static ma_result ma_semaphore_wait__win32(ma_semaphore* pSemaphore) -{ - DWORD result = WaitForSingleObject((HANDLE)*pSemaphore, INFINITE); - if (result == WAIT_OBJECT_0) { - return MA_SUCCESS; - } - - if (result == WAIT_TIMEOUT) { - return MA_TIMEOUT; - } - - return ma_result_from_GetLastError(GetLastError()); -} - -static ma_result ma_semaphore_release__win32(ma_semaphore* pSemaphore) -{ - BOOL result = ReleaseSemaphore((HANDLE)*pSemaphore, 1, NULL); - if (result == 0) { - return ma_result_from_GetLastError(GetLastError()); - } - - return MA_SUCCESS; -} -#endif - -typedef struct -{ - ma_thread_entry_proc entryProc; - void* pData; - ma_allocation_callbacks allocationCallbacks; -} ma_thread_proxy_data; - -static ma_thread_result MA_THREADCALL ma_thread_entry_proxy(void* pData) -{ - ma_thread_proxy_data* pProxyData = (ma_thread_proxy_data*)pData; - ma_thread_entry_proc entryProc; - void* pEntryProcData; - ma_thread_result result; - - #if defined(MA_ON_THREAD_ENTRY) - MA_ON_THREAD_ENTRY - #endif - - entryProc = pProxyData->entryProc; - pEntryProcData = pProxyData->pData; - - /* Free the proxy data before getting into the real thread entry proc. */ - ma_free(pProxyData, &pProxyData->allocationCallbacks); - - result = entryProc(pEntryProcData); - - #if defined(MA_ON_THREAD_EXIT) - MA_ON_THREAD_EXIT - #endif - - return result; -} - -static ma_result ma_thread_create(ma_thread* pThread, ma_thread_priority priority, size_t stackSize, ma_thread_entry_proc entryProc, void* pData, const ma_allocation_callbacks* pAllocationCallbacks) -{ - ma_result result; - ma_thread_proxy_data* pProxyData; - - if (pThread == NULL || entryProc == NULL) { - return MA_INVALID_ARGS; - } - - pProxyData = (ma_thread_proxy_data*)ma_malloc(sizeof(*pProxyData), pAllocationCallbacks); /* Will be freed by the proxy entry proc. */ - if (pProxyData == NULL) { - return MA_OUT_OF_MEMORY; - } - -#if defined(MA_THREAD_DEFAULT_STACK_SIZE) - if (stackSize == 0) { - stackSize = MA_THREAD_DEFAULT_STACK_SIZE; - } -#endif - - pProxyData->entryProc = entryProc; - pProxyData->pData = pData; - ma_allocation_callbacks_init_copy(&pProxyData->allocationCallbacks, pAllocationCallbacks); - -#if defined(MA_POSIX) - result = ma_thread_create__posix(pThread, priority, stackSize, ma_thread_entry_proxy, pProxyData); -#elif defined(MA_WIN32) - result = ma_thread_create__win32(pThread, priority, stackSize, ma_thread_entry_proxy, pProxyData); -#endif - - if (result != MA_SUCCESS) { - ma_free(pProxyData, pAllocationCallbacks); - return result; - } - - return MA_SUCCESS; -} - -static void ma_thread_wait(ma_thread* pThread) -{ - if (pThread == NULL) { - return; - } - -#if defined(MA_POSIX) - ma_thread_wait__posix(pThread); -#elif defined(MA_WIN32) - ma_thread_wait__win32(pThread); -#endif -} - - -MA_API ma_result ma_mutex_init(ma_mutex* pMutex) -{ - if (pMutex == NULL) { - MA_ASSERT(MA_FALSE); /* Fire an assert so the caller is aware of this bug. */ - return MA_INVALID_ARGS; - } - -#if defined(MA_POSIX) - return ma_mutex_init__posix(pMutex); -#elif defined(MA_WIN32) - return ma_mutex_init__win32(pMutex); -#endif -} - -MA_API void ma_mutex_uninit(ma_mutex* pMutex) -{ - if (pMutex == NULL) { - return; - } - -#if defined(MA_POSIX) - ma_mutex_uninit__posix(pMutex); -#elif defined(MA_WIN32) - ma_mutex_uninit__win32(pMutex); -#endif -} - -MA_API void ma_mutex_lock(ma_mutex* pMutex) -{ - if (pMutex == NULL) { - MA_ASSERT(MA_FALSE); /* Fire an assert so the caller is aware of this bug. */ - return; - } - -#if defined(MA_POSIX) - ma_mutex_lock__posix(pMutex); -#elif defined(MA_WIN32) - ma_mutex_lock__win32(pMutex); -#endif -} - -MA_API void ma_mutex_unlock(ma_mutex* pMutex) -{ - if (pMutex == NULL) { - MA_ASSERT(MA_FALSE); /* Fire an assert so the caller is aware of this bug. */ - return; - } - -#if defined(MA_POSIX) - ma_mutex_unlock__posix(pMutex); -#elif defined(MA_WIN32) - ma_mutex_unlock__win32(pMutex); -#endif -} - - -MA_API ma_result ma_event_init(ma_event* pEvent) -{ - if (pEvent == NULL) { - MA_ASSERT(MA_FALSE); /* Fire an assert so the caller is aware of this bug. */ - return MA_INVALID_ARGS; - } - -#if defined(MA_POSIX) - return ma_event_init__posix(pEvent); -#elif defined(MA_WIN32) - return ma_event_init__win32(pEvent); -#endif -} - -#if 0 -static ma_result ma_event_alloc_and_init(ma_event** ppEvent, ma_allocation_callbacks* pAllocationCallbacks) -{ - ma_result result; - ma_event* pEvent; - - if (ppEvent == NULL) { - return MA_INVALID_ARGS; - } - - *ppEvent = NULL; - - pEvent = ma_malloc(sizeof(*pEvent), pAllocationCallbacks); - if (pEvent == NULL) { - return MA_OUT_OF_MEMORY; - } - - result = ma_event_init(pEvent); - if (result != MA_SUCCESS) { - ma_free(pEvent, pAllocationCallbacks); - return result; - } - - *ppEvent = pEvent; - return result; -} -#endif - -MA_API void ma_event_uninit(ma_event* pEvent) -{ - if (pEvent == NULL) { - return; - } - -#if defined(MA_POSIX) - ma_event_uninit__posix(pEvent); -#elif defined(MA_WIN32) - ma_event_uninit__win32(pEvent); -#endif -} - -#if 0 -static void ma_event_uninit_and_free(ma_event* pEvent, ma_allocation_callbacks* pAllocationCallbacks) -{ - if (pEvent == NULL) { - return; - } - - ma_event_uninit(pEvent); - ma_free(pEvent, pAllocationCallbacks); -} -#endif - -MA_API ma_result ma_event_wait(ma_event* pEvent) -{ - if (pEvent == NULL) { - MA_ASSERT(MA_FALSE); /* Fire an assert to the caller is aware of this bug. */ - return MA_INVALID_ARGS; - } - -#if defined(MA_POSIX) - return ma_event_wait__posix(pEvent); -#elif defined(MA_WIN32) - return ma_event_wait__win32(pEvent); -#endif -} - -MA_API ma_result ma_event_signal(ma_event* pEvent) -{ - if (pEvent == NULL) { - MA_ASSERT(MA_FALSE); /* Fire an assert to the caller is aware of this bug. */ - return MA_INVALID_ARGS; - } - -#if defined(MA_POSIX) - return ma_event_signal__posix(pEvent); -#elif defined(MA_WIN32) - return ma_event_signal__win32(pEvent); -#endif -} - - -MA_API ma_result ma_semaphore_init(int initialValue, ma_semaphore* pSemaphore) -{ - if (pSemaphore == NULL) { - MA_ASSERT(MA_FALSE); /* Fire an assert so the caller is aware of this bug. */ - return MA_INVALID_ARGS; - } - -#if defined(MA_POSIX) - return ma_semaphore_init__posix(initialValue, pSemaphore); -#elif defined(MA_WIN32) - return ma_semaphore_init__win32(initialValue, pSemaphore); -#endif -} - -MA_API void ma_semaphore_uninit(ma_semaphore* pSemaphore) -{ - if (pSemaphore == NULL) { - MA_ASSERT(MA_FALSE); /* Fire an assert so the caller is aware of this bug. */ - return; - } - -#if defined(MA_POSIX) - ma_semaphore_uninit__posix(pSemaphore); -#elif defined(MA_WIN32) - ma_semaphore_uninit__win32(pSemaphore); -#endif -} - -MA_API ma_result ma_semaphore_wait(ma_semaphore* pSemaphore) -{ - if (pSemaphore == NULL) { - MA_ASSERT(MA_FALSE); /* Fire an assert so the caller is aware of this bug. */ - return MA_INVALID_ARGS; - } - -#if defined(MA_POSIX) - return ma_semaphore_wait__posix(pSemaphore); -#elif defined(MA_WIN32) - return ma_semaphore_wait__win32(pSemaphore); -#endif -} - -MA_API ma_result ma_semaphore_release(ma_semaphore* pSemaphore) -{ - if (pSemaphore == NULL) { - MA_ASSERT(MA_FALSE); /* Fire an assert so the caller is aware of this bug. */ - return MA_INVALID_ARGS; - } - -#if defined(MA_POSIX) - return ma_semaphore_release__posix(pSemaphore); -#elif defined(MA_WIN32) - return ma_semaphore_release__win32(pSemaphore); -#endif -} -#else -/* MA_NO_THREADING is set which means threading is disabled. Threading is required by some API families. If any of these are enabled we need to throw an error. */ -#ifndef MA_NO_DEVICE_IO -#error "MA_NO_THREADING cannot be used without MA_NO_DEVICE_IO"; -#endif -#endif /* MA_NO_THREADING */ - - - -#define MA_FENCE_COUNTER_MAX 0x7FFFFFFF - -MA_API ma_result ma_fence_init(ma_fence* pFence) -{ - if (pFence == NULL) { - return MA_INVALID_ARGS; - } - - MA_ZERO_OBJECT(pFence); - pFence->counter = 0; - - #ifndef MA_NO_THREADING - { - ma_result result; - - result = ma_event_init(&pFence->e); - if (result != MA_SUCCESS) { - return result; - } - } - #endif - - return MA_SUCCESS; -} - -MA_API void ma_fence_uninit(ma_fence* pFence) -{ - if (pFence == NULL) { - return; - } - - #ifndef MA_NO_THREADING - { - ma_event_uninit(&pFence->e); - } - #endif - - MA_ZERO_OBJECT(pFence); -} - -MA_API ma_result ma_fence_acquire(ma_fence* pFence) -{ - if (pFence == NULL) { - return MA_INVALID_ARGS; - } - - for (;;) { - ma_uint32 oldCounter = ma_atomic_load_32(&pFence->counter); - ma_uint32 newCounter = oldCounter + 1; - - /* Make sure we're not about to exceed our maximum value. */ - if (newCounter > MA_FENCE_COUNTER_MAX) { - MA_ASSERT(MA_FALSE); - return MA_OUT_OF_RANGE; - } - - if (ma_atomic_compare_exchange_weak_32(&pFence->counter, &oldCounter, newCounter)) { - return MA_SUCCESS; - } else { - if (oldCounter == MA_FENCE_COUNTER_MAX) { - MA_ASSERT(MA_FALSE); - return MA_OUT_OF_RANGE; /* The other thread took the last available slot. Abort. */ - } - } - } - - /* Should never get here. */ - /*return MA_SUCCESS;*/ -} - -MA_API ma_result ma_fence_release(ma_fence* pFence) -{ - if (pFence == NULL) { - return MA_INVALID_ARGS; - } - - for (;;) { - ma_uint32 oldCounter = ma_atomic_load_32(&pFence->counter); - ma_uint32 newCounter = oldCounter - 1; - - if (oldCounter == 0) { - MA_ASSERT(MA_FALSE); - return MA_INVALID_OPERATION; /* Acquire/release mismatch. */ - } - - if (ma_atomic_compare_exchange_weak_32(&pFence->counter, &oldCounter, newCounter)) { - #ifndef MA_NO_THREADING - { - if (newCounter == 0) { - ma_event_signal(&pFence->e); /* <-- ma_fence_wait() will be waiting on this. */ - } - } - #endif - - return MA_SUCCESS; - } else { - if (oldCounter == 0) { - MA_ASSERT(MA_FALSE); - return MA_INVALID_OPERATION; /* Another thread has taken the 0 slot. Acquire/release mismatch. */ - } - } - } - - /* Should never get here. */ - /*return MA_SUCCESS;*/ -} - -MA_API ma_result ma_fence_wait(ma_fence* pFence) -{ - if (pFence == NULL) { - return MA_INVALID_ARGS; - } - - for (;;) { - ma_uint32 counter; - - counter = ma_atomic_load_32(&pFence->counter); - if (counter == 0) { - /* - Counter has hit zero. By the time we get here some other thread may have acquired the - fence again, but that is where the caller needs to take care with how they se the fence. - */ - return MA_SUCCESS; - } - - /* Getting here means the counter is > 0. We'll need to wait for something to happen. */ - #ifndef MA_NO_THREADING - { - ma_result result; - - result = ma_event_wait(&pFence->e); - if (result != MA_SUCCESS) { - return result; - } - } - #endif - } - - /* Should never get here. */ - /*return MA_INVALID_OPERATION;*/ -} - - -MA_API ma_result ma_async_notification_signal(ma_async_notification* pNotification) -{ - ma_async_notification_callbacks* pNotificationCallbacks = (ma_async_notification_callbacks*)pNotification; - - if (pNotification == NULL) { - return MA_INVALID_ARGS; - } - - if (pNotificationCallbacks->onSignal == NULL) { - return MA_NOT_IMPLEMENTED; - } - - pNotificationCallbacks->onSignal(pNotification); - return MA_INVALID_ARGS; -} - - -static void ma_async_notification_poll__on_signal(ma_async_notification* pNotification) -{ - ((ma_async_notification_poll*)pNotification)->signalled = MA_TRUE; -} - -MA_API ma_result ma_async_notification_poll_init(ma_async_notification_poll* pNotificationPoll) -{ - if (pNotificationPoll == NULL) { - return MA_INVALID_ARGS; - } - - pNotificationPoll->cb.onSignal = ma_async_notification_poll__on_signal; - pNotificationPoll->signalled = MA_FALSE; - - return MA_SUCCESS; -} - -MA_API ma_bool32 ma_async_notification_poll_is_signalled(const ma_async_notification_poll* pNotificationPoll) -{ - if (pNotificationPoll == NULL) { - return MA_FALSE; - } - - return pNotificationPoll->signalled; -} - - -static void ma_async_notification_event__on_signal(ma_async_notification* pNotification) -{ - ma_async_notification_event_signal((ma_async_notification_event*)pNotification); -} - -MA_API ma_result ma_async_notification_event_init(ma_async_notification_event* pNotificationEvent) -{ - if (pNotificationEvent == NULL) { - return MA_INVALID_ARGS; - } - - pNotificationEvent->cb.onSignal = ma_async_notification_event__on_signal; - - #ifndef MA_NO_THREADING - { - ma_result result; - - result = ma_event_init(&pNotificationEvent->e); - if (result != MA_SUCCESS) { - return result; - } - - return MA_SUCCESS; - } - #else - { - return MA_NOT_IMPLEMENTED; /* Threading is disabled. */ - } - #endif -} - -MA_API ma_result ma_async_notification_event_uninit(ma_async_notification_event* pNotificationEvent) -{ - if (pNotificationEvent == NULL) { - return MA_INVALID_ARGS; - } - - #ifndef MA_NO_THREADING - { - ma_event_uninit(&pNotificationEvent->e); - return MA_SUCCESS; - } - #else - { - return MA_NOT_IMPLEMENTED; /* Threading is disabled. */ - } - #endif -} - -MA_API ma_result ma_async_notification_event_wait(ma_async_notification_event* pNotificationEvent) -{ - if (pNotificationEvent == NULL) { - return MA_INVALID_ARGS; - } - - #ifndef MA_NO_THREADING - { - return ma_event_wait(&pNotificationEvent->e); - } - #else - { - return MA_NOT_IMPLEMENTED; /* Threading is disabled. */ - } - #endif -} - -MA_API ma_result ma_async_notification_event_signal(ma_async_notification_event* pNotificationEvent) -{ - if (pNotificationEvent == NULL) { - return MA_INVALID_ARGS; - } - - #ifndef MA_NO_THREADING - { - return ma_event_signal(&pNotificationEvent->e); - } - #else - { - return MA_NOT_IMPLEMENTED; /* Threading is disabled. */ - } - #endif -} - - - -/************************************************************************************************************************************************************ - -Job Queue - -************************************************************************************************************************************************************/ -MA_API ma_slot_allocator_config ma_slot_allocator_config_init(ma_uint32 capacity) -{ - ma_slot_allocator_config config; - - MA_ZERO_OBJECT(&config); - config.capacity = capacity; - - return config; -} - - -static MA_INLINE ma_uint32 ma_slot_allocator_calculate_group_capacity(ma_uint32 slotCapacity) -{ - ma_uint32 cap = slotCapacity / 32; - if ((slotCapacity % 32) != 0) { - cap += 1; - } - - return cap; -} - -static MA_INLINE ma_uint32 ma_slot_allocator_group_capacity(const ma_slot_allocator* pAllocator) -{ - return ma_slot_allocator_calculate_group_capacity(pAllocator->capacity); -} - - -typedef struct -{ - size_t sizeInBytes; - size_t groupsOffset; - size_t slotsOffset; -} ma_slot_allocator_heap_layout; - -static ma_result ma_slot_allocator_get_heap_layout(const ma_slot_allocator_config* pConfig, ma_slot_allocator_heap_layout* pHeapLayout) -{ - MA_ASSERT(pHeapLayout != NULL); - - MA_ZERO_OBJECT(pHeapLayout); - - if (pConfig == NULL) { - return MA_INVALID_ARGS; - } - - if (pConfig->capacity == 0) { - return MA_INVALID_ARGS; - } - - pHeapLayout->sizeInBytes = 0; - - /* Groups. */ - pHeapLayout->groupsOffset = pHeapLayout->sizeInBytes; - pHeapLayout->sizeInBytes += ma_align_64(ma_slot_allocator_calculate_group_capacity(pConfig->capacity) * sizeof(ma_slot_allocator_group)); - - /* Slots. */ - pHeapLayout->slotsOffset = pHeapLayout->sizeInBytes; - pHeapLayout->sizeInBytes += ma_align_64(pConfig->capacity * sizeof(ma_uint32)); - - return MA_SUCCESS; -} - -MA_API ma_result ma_slot_allocator_get_heap_size(const ma_slot_allocator_config* pConfig, size_t* pHeapSizeInBytes) -{ - ma_result result; - ma_slot_allocator_heap_layout layout; - - if (pHeapSizeInBytes == NULL) { - return MA_INVALID_ARGS; - } - - *pHeapSizeInBytes = 0; - - result = ma_slot_allocator_get_heap_layout(pConfig, &layout); - if (result != MA_SUCCESS) { - return result; - } - - *pHeapSizeInBytes = layout.sizeInBytes; - - return result; -} - -MA_API ma_result ma_slot_allocator_init_preallocated(const ma_slot_allocator_config* pConfig, void* pHeap, ma_slot_allocator* pAllocator) -{ - ma_result result; - ma_slot_allocator_heap_layout heapLayout; - - if (pAllocator == NULL) { - return MA_INVALID_ARGS; - } - - MA_ZERO_OBJECT(pAllocator); - - if (pHeap == NULL) { - return MA_INVALID_ARGS; - } - - result = ma_slot_allocator_get_heap_layout(pConfig, &heapLayout); - if (result != MA_SUCCESS) { - return result; - } - - pAllocator->_pHeap = pHeap; - MA_ZERO_MEMORY(pHeap, heapLayout.sizeInBytes); - - pAllocator->pGroups = (ma_slot_allocator_group*)ma_offset_ptr(pHeap, heapLayout.groupsOffset); - pAllocator->pSlots = (ma_uint32*)ma_offset_ptr(pHeap, heapLayout.slotsOffset); - pAllocator->capacity = pConfig->capacity; - - return MA_SUCCESS; -} - -MA_API ma_result ma_slot_allocator_init(const ma_slot_allocator_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_slot_allocator* pAllocator) -{ - ma_result result; - size_t heapSizeInBytes; - void* pHeap; - - result = ma_slot_allocator_get_heap_size(pConfig, &heapSizeInBytes); - if (result != MA_SUCCESS) { - return result; /* Failed to retrieve the size of the heap allocation. */ - } - - if (heapSizeInBytes > 0) { - pHeap = ma_malloc(heapSizeInBytes, pAllocationCallbacks); - if (pHeap == NULL) { - return MA_OUT_OF_MEMORY; - } - } else { - pHeap = NULL; - } - - result = ma_slot_allocator_init_preallocated(pConfig, pHeap, pAllocator); - if (result != MA_SUCCESS) { - ma_free(pHeap, pAllocationCallbacks); - return result; - } - - pAllocator->_ownsHeap = MA_TRUE; - return MA_SUCCESS; -} - -MA_API void ma_slot_allocator_uninit(ma_slot_allocator* pAllocator, const ma_allocation_callbacks* pAllocationCallbacks) -{ - if (pAllocator == NULL) { - return; - } - - if (pAllocator->_ownsHeap) { - ma_free(pAllocator->_pHeap, pAllocationCallbacks); - } -} - -MA_API ma_result ma_slot_allocator_alloc(ma_slot_allocator* pAllocator, ma_uint64* pSlot) -{ - ma_uint32 iAttempt; - const ma_uint32 maxAttempts = 2; /* The number of iterations to perform until returning MA_OUT_OF_MEMORY if no slots can be found. */ - - if (pAllocator == NULL || pSlot == NULL) { - return MA_INVALID_ARGS; - } - - for (iAttempt = 0; iAttempt < maxAttempts; iAttempt += 1) { - /* We need to acquire a suitable bitfield first. This is a bitfield that's got an available slot within it. */ - ma_uint32 iGroup; - for (iGroup = 0; iGroup < ma_slot_allocator_group_capacity(pAllocator); iGroup += 1) { - /* CAS */ - for (;;) { - ma_uint32 oldBitfield; - ma_uint32 newBitfield; - ma_uint32 bitOffset; - - oldBitfield = ma_atomic_load_32(&pAllocator->pGroups[iGroup].bitfield); /* <-- This copy must happen. The compiler must not optimize this away. */ - - /* Fast check to see if anything is available. */ - if (oldBitfield == 0xFFFFFFFF) { - break; /* No available bits in this bitfield. */ - } - - bitOffset = ma_ffs_32(~oldBitfield); - MA_ASSERT(bitOffset < 32); - - newBitfield = oldBitfield | (1 << bitOffset); - - if (ma_atomic_compare_and_swap_32(&pAllocator->pGroups[iGroup].bitfield, oldBitfield, newBitfield) == oldBitfield) { - ma_uint32 slotIndex; - - /* Increment the counter as soon as possible to have other threads report out-of-memory sooner than later. */ - ma_atomic_fetch_add_32(&pAllocator->count, 1); - - /* The slot index is required for constructing the output value. */ - slotIndex = (iGroup << 5) + bitOffset; /* iGroup << 5 = iGroup * 32 */ - if (slotIndex >= pAllocator->capacity) { - return MA_OUT_OF_MEMORY; - } - - /* Increment the reference count before constructing the output value. */ - pAllocator->pSlots[slotIndex] += 1; - - /* Construct the output value. */ - *pSlot = (((ma_uint64)pAllocator->pSlots[slotIndex] << 32) | slotIndex); - - return MA_SUCCESS; - } - } - } - - /* We weren't able to find a slot. If it's because we've reached our capacity we need to return MA_OUT_OF_MEMORY. Otherwise we need to do another iteration and try again. */ - if (pAllocator->count < pAllocator->capacity) { - ma_yield(); - } else { - return MA_OUT_OF_MEMORY; - } - } - - /* We couldn't find a slot within the maximum number of attempts. */ - return MA_OUT_OF_MEMORY; -} - -MA_API ma_result ma_slot_allocator_free(ma_slot_allocator* pAllocator, ma_uint64 slot) -{ - ma_uint32 iGroup; - ma_uint32 iBit; - - if (pAllocator == NULL) { - return MA_INVALID_ARGS; - } - - iGroup = (ma_uint32)((slot & 0xFFFFFFFF) >> 5); /* slot / 32 */ - iBit = (ma_uint32)((slot & 0xFFFFFFFF) & 31); /* slot % 32 */ - - if (iGroup >= ma_slot_allocator_group_capacity(pAllocator)) { - return MA_INVALID_ARGS; - } - - MA_ASSERT(iBit < 32); /* This must be true due to the logic we used to actually calculate it. */ - - while (ma_atomic_load_32(&pAllocator->count) > 0) { - /* CAS */ - ma_uint32 oldBitfield; - ma_uint32 newBitfield; - - oldBitfield = ma_atomic_load_32(&pAllocator->pGroups[iGroup].bitfield); /* <-- This copy must happen. The compiler must not optimize this away. */ - newBitfield = oldBitfield & ~(1 << iBit); - - /* Debugging for checking for double-frees. */ - #if defined(MA_DEBUG_OUTPUT) - { - if ((oldBitfield & (1 << iBit)) == 0) { - MA_ASSERT(MA_FALSE); /* Double free detected.*/ - } - } - #endif - - if (ma_atomic_compare_and_swap_32(&pAllocator->pGroups[iGroup].bitfield, oldBitfield, newBitfield) == oldBitfield) { - ma_atomic_fetch_sub_32(&pAllocator->count, 1); - return MA_SUCCESS; - } - } - - /* Getting here means there are no allocations available for freeing. */ - return MA_INVALID_OPERATION; -} - - -#define MA_JOB_ID_NONE ~((ma_uint64)0) -#define MA_JOB_SLOT_NONE (ma_uint16)(~0) - -static MA_INLINE ma_uint32 ma_job_extract_refcount(ma_uint64 toc) -{ - return (ma_uint32)(toc >> 32); -} - -static MA_INLINE ma_uint16 ma_job_extract_slot(ma_uint64 toc) -{ - return (ma_uint16)(toc & 0x0000FFFF); -} - -static MA_INLINE ma_uint16 ma_job_extract_code(ma_uint64 toc) -{ - return (ma_uint16)((toc & 0xFFFF0000) >> 16); -} - -static MA_INLINE ma_uint64 ma_job_toc_to_allocation(ma_uint64 toc) -{ - return ((ma_uint64)ma_job_extract_refcount(toc) << 32) | (ma_uint64)ma_job_extract_slot(toc); -} - -static MA_INLINE ma_uint64 ma_job_set_refcount(ma_uint64 toc, ma_uint32 refcount) -{ - /* Clear the reference count first. */ - toc = toc & ~((ma_uint64)0xFFFFFFFF << 32); - toc = toc | ((ma_uint64)refcount << 32); - - return toc; -} - - -MA_API ma_job ma_job_init(ma_uint16 code) -{ - ma_job job; - - MA_ZERO_OBJECT(&job); - job.toc.breakup.code = code; - job.toc.breakup.slot = MA_JOB_SLOT_NONE; /* Temp value. Will be allocated when posted to a queue. */ - job.next = MA_JOB_ID_NONE; - - return job; -} - - -static ma_result ma_job_process__noop(ma_job* pJob); -static ma_result ma_job_process__quit(ma_job* pJob); -static ma_result ma_job_process__custom(ma_job* pJob); -static ma_result ma_job_process__resource_manager__load_data_buffer_node(ma_job* pJob); -static ma_result ma_job_process__resource_manager__free_data_buffer_node(ma_job* pJob); -static ma_result ma_job_process__resource_manager__page_data_buffer_node(ma_job* pJob); -static ma_result ma_job_process__resource_manager__load_data_buffer(ma_job* pJob); -static ma_result ma_job_process__resource_manager__free_data_buffer(ma_job* pJob); -static ma_result ma_job_process__resource_manager__load_data_stream(ma_job* pJob); -static ma_result ma_job_process__resource_manager__free_data_stream(ma_job* pJob); -static ma_result ma_job_process__resource_manager__page_data_stream(ma_job* pJob); -static ma_result ma_job_process__resource_manager__seek_data_stream(ma_job* pJob); - -#if !defined(MA_NO_DEVICE_IO) -static ma_result ma_job_process__device__aaudio_reroute(ma_job* pJob); -#endif - -static ma_job_proc g_jobVTable[MA_JOB_TYPE_COUNT] = -{ - /* Miscellaneous. */ - ma_job_process__quit, /* MA_JOB_TYPE_QUIT */ - ma_job_process__custom, /* MA_JOB_TYPE_CUSTOM */ - - /* Resource Manager. */ - ma_job_process__resource_manager__load_data_buffer_node, /* MA_JOB_TYPE_RESOURCE_MANAGER_LOAD_DATA_BUFFER_NODE */ - ma_job_process__resource_manager__free_data_buffer_node, /* MA_JOB_TYPE_RESOURCE_MANAGER_FREE_DATA_BUFFER_NODE */ - ma_job_process__resource_manager__page_data_buffer_node, /* MA_JOB_TYPE_RESOURCE_MANAGER_PAGE_DATA_BUFFER_NODE */ - ma_job_process__resource_manager__load_data_buffer, /* MA_JOB_TYPE_RESOURCE_MANAGER_LOAD_DATA_BUFFER */ - ma_job_process__resource_manager__free_data_buffer, /* MA_JOB_TYPE_RESOURCE_MANAGER_FREE_DATA_BUFFER */ - ma_job_process__resource_manager__load_data_stream, /* MA_JOB_TYPE_RESOURCE_MANAGER_LOAD_DATA_STREAM */ - ma_job_process__resource_manager__free_data_stream, /* MA_JOB_TYPE_RESOURCE_MANAGER_FREE_DATA_STREAM */ - ma_job_process__resource_manager__page_data_stream, /* MA_JOB_TYPE_RESOURCE_MANAGER_PAGE_DATA_STREAM */ - ma_job_process__resource_manager__seek_data_stream, /* MA_JOB_TYPE_RESOURCE_MANAGER_SEEK_DATA_STREAM */ - - /* Device. */ -#if !defined(MA_NO_DEVICE_IO) - ma_job_process__device__aaudio_reroute /* MA_JOB_TYPE_DEVICE_AAUDIO_REROUTE */ -#endif -}; - -MA_API ma_result ma_job_process(ma_job* pJob) -{ - if (pJob == NULL) { - return MA_INVALID_ARGS; - } - - if (pJob->toc.breakup.code >= MA_JOB_TYPE_COUNT) { - return MA_INVALID_OPERATION; - } - - return g_jobVTable[pJob->toc.breakup.code](pJob); -} - -static ma_result ma_job_process__noop(ma_job* pJob) -{ - MA_ASSERT(pJob != NULL); - - /* No-op. */ - (void)pJob; - - return MA_SUCCESS; -} - -static ma_result ma_job_process__quit(ma_job* pJob) -{ - return ma_job_process__noop(pJob); -} - -static ma_result ma_job_process__custom(ma_job* pJob) -{ - MA_ASSERT(pJob != NULL); - - /* No-op if there's no callback. */ - if (pJob->data.custom.proc == NULL) { - return MA_SUCCESS; - } - - return pJob->data.custom.proc(pJob); -} - - - -MA_API ma_job_queue_config ma_job_queue_config_init(ma_uint32 flags, ma_uint32 capacity) -{ - ma_job_queue_config config; - - config.flags = flags; - config.capacity = capacity; - - return config; -} - - -typedef struct -{ - size_t sizeInBytes; - size_t allocatorOffset; - size_t jobsOffset; -} ma_job_queue_heap_layout; - -static ma_result ma_job_queue_get_heap_layout(const ma_job_queue_config* pConfig, ma_job_queue_heap_layout* pHeapLayout) -{ - ma_result result; - - MA_ASSERT(pHeapLayout != NULL); - - MA_ZERO_OBJECT(pHeapLayout); - - if (pConfig == NULL) { - return MA_INVALID_ARGS; - } - - if (pConfig->capacity == 0) { - return MA_INVALID_ARGS; - } - - pHeapLayout->sizeInBytes = 0; - - /* Allocator. */ - { - ma_slot_allocator_config allocatorConfig; - size_t allocatorHeapSizeInBytes; - - allocatorConfig = ma_slot_allocator_config_init(pConfig->capacity); - result = ma_slot_allocator_get_heap_size(&allocatorConfig, &allocatorHeapSizeInBytes); - if (result != MA_SUCCESS) { - return result; - } - - pHeapLayout->allocatorOffset = pHeapLayout->sizeInBytes; - pHeapLayout->sizeInBytes += allocatorHeapSizeInBytes; - } - - /* Jobs. */ - pHeapLayout->jobsOffset = pHeapLayout->sizeInBytes; - pHeapLayout->sizeInBytes += ma_align_64(pConfig->capacity * sizeof(ma_job)); - - return MA_SUCCESS; -} - -MA_API ma_result ma_job_queue_get_heap_size(const ma_job_queue_config* pConfig, size_t* pHeapSizeInBytes) -{ - ma_result result; - ma_job_queue_heap_layout layout; - - if (pHeapSizeInBytes == NULL) { - return MA_INVALID_ARGS; - } - - *pHeapSizeInBytes = 0; - - result = ma_job_queue_get_heap_layout(pConfig, &layout); - if (result != MA_SUCCESS) { - return result; - } - - *pHeapSizeInBytes = layout.sizeInBytes; - - return MA_SUCCESS; -} - -MA_API ma_result ma_job_queue_init_preallocated(const ma_job_queue_config* pConfig, void* pHeap, ma_job_queue* pQueue) -{ - ma_result result; - ma_job_queue_heap_layout heapLayout; - ma_slot_allocator_config allocatorConfig; - - if (pQueue == NULL) { - return MA_INVALID_ARGS; - } - - MA_ZERO_OBJECT(pQueue); - - result = ma_job_queue_get_heap_layout(pConfig, &heapLayout); - if (result != MA_SUCCESS) { - return result; - } - - pQueue->_pHeap = pHeap; - MA_ZERO_MEMORY(pHeap, heapLayout.sizeInBytes); - - pQueue->flags = pConfig->flags; - pQueue->capacity = pConfig->capacity; - pQueue->pJobs = (ma_job*)ma_offset_ptr(pHeap, heapLayout.jobsOffset); - - allocatorConfig = ma_slot_allocator_config_init(pConfig->capacity); - result = ma_slot_allocator_init_preallocated(&allocatorConfig, ma_offset_ptr(pHeap, heapLayout.allocatorOffset), &pQueue->allocator); - if (result != MA_SUCCESS) { - return result; - } - - /* We need a semaphore if we're running in non-blocking mode. If threading is disabled we need to return an error. */ - if ((pQueue->flags & MA_JOB_QUEUE_FLAG_NON_BLOCKING) == 0) { - #ifndef MA_NO_THREADING - { - ma_semaphore_init(0, &pQueue->sem); - } - #else - { - /* Threading is disabled and we've requested non-blocking mode. */ - return MA_INVALID_OPERATION; - } - #endif - } - - /* - Our queue needs to be initialized with a free standing node. This should always be slot 0. Required for the lock free algorithm. The first job in the queue is - just a dummy item for giving us the first item in the list which is stored in the "next" member. - */ - ma_slot_allocator_alloc(&pQueue->allocator, &pQueue->head); /* Will never fail. */ - pQueue->pJobs[ma_job_extract_slot(pQueue->head)].next = MA_JOB_ID_NONE; - pQueue->tail = pQueue->head; - - return MA_SUCCESS; -} - -MA_API ma_result ma_job_queue_init(const ma_job_queue_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_job_queue* pQueue) -{ - ma_result result; - size_t heapSizeInBytes; - void* pHeap; - - result = ma_job_queue_get_heap_size(pConfig, &heapSizeInBytes); - if (result != MA_SUCCESS) { - return result; - } - - if (heapSizeInBytes > 0) { - pHeap = ma_malloc(heapSizeInBytes, pAllocationCallbacks); - if (pHeap == NULL) { - return MA_OUT_OF_MEMORY; - } - } else { - pHeap = NULL; - } - - result = ma_job_queue_init_preallocated(pConfig, pHeap, pQueue); - if (result != MA_SUCCESS) { - ma_free(pHeap, pAllocationCallbacks); - return result; - } - - pQueue->_ownsHeap = MA_TRUE; - return MA_SUCCESS; -} - -MA_API void ma_job_queue_uninit(ma_job_queue* pQueue, const ma_allocation_callbacks* pAllocationCallbacks) -{ - if (pQueue == NULL) { - return; - } - - /* All we need to do is uninitialize the semaphore. */ - if ((pQueue->flags & MA_JOB_QUEUE_FLAG_NON_BLOCKING) == 0) { - #ifndef MA_NO_THREADING - { - ma_semaphore_uninit(&pQueue->sem); - } - #else - { - MA_ASSERT(MA_FALSE); /* Should never get here. Should have been checked at initialization time. */ - } - #endif - } - - ma_slot_allocator_uninit(&pQueue->allocator, pAllocationCallbacks); - - if (pQueue->_ownsHeap) { - ma_free(pQueue->_pHeap, pAllocationCallbacks); - } -} - -static ma_bool32 ma_job_queue_cas(volatile ma_uint64* dst, ma_uint64 expected, ma_uint64 desired) -{ - /* The new counter is taken from the expected value. */ - return ma_atomic_compare_and_swap_64(dst, expected, ma_job_set_refcount(desired, ma_job_extract_refcount(expected) + 1)) == expected; -} - -MA_API ma_result ma_job_queue_post(ma_job_queue* pQueue, const ma_job* pJob) -{ - /* - Lock free queue implementation based on the paper by Michael and Scott: Nonblocking Algorithms and Preemption-Safe Locking on Multiprogrammed Shared Memory Multiprocessors - */ - ma_result result; - ma_uint64 slot; - ma_uint64 tail; - ma_uint64 next; - - if (pQueue == NULL || pJob == NULL) { - return MA_INVALID_ARGS; - } - - /* We need a new slot. */ - result = ma_slot_allocator_alloc(&pQueue->allocator, &slot); - if (result != MA_SUCCESS) { - return result; /* Probably ran out of slots. If so, MA_OUT_OF_MEMORY will be returned. */ - } - - /* At this point we should have a slot to place the job. */ - MA_ASSERT(ma_job_extract_slot(slot) < pQueue->capacity); - - /* We need to put the job into memory before we do anything. */ - pQueue->pJobs[ma_job_extract_slot(slot)] = *pJob; - pQueue->pJobs[ma_job_extract_slot(slot)].toc.allocation = slot; /* This will overwrite the job code. */ - pQueue->pJobs[ma_job_extract_slot(slot)].toc.breakup.code = pJob->toc.breakup.code; /* The job code needs to be applied again because the line above overwrote it. */ - pQueue->pJobs[ma_job_extract_slot(slot)].next = MA_JOB_ID_NONE; /* Reset for safety. */ - - #ifndef MA_USE_EXPERIMENTAL_LOCK_FREE_JOB_QUEUE - ma_spinlock_lock(&pQueue->lock); - #endif - { - /* The job is stored in memory so now we need to add it to our linked list. We only ever add items to the end of the list. */ - for (;;) { - tail = ma_atomic_load_64(&pQueue->tail); - next = ma_atomic_load_64(&pQueue->pJobs[ma_job_extract_slot(tail)].next); - - if (ma_job_toc_to_allocation(tail) == ma_job_toc_to_allocation(ma_atomic_load_64(&pQueue->tail))) { - if (ma_job_extract_slot(next) == 0xFFFF) { - if (ma_job_queue_cas(&pQueue->pJobs[ma_job_extract_slot(tail)].next, next, slot)) { - break; - } - } else { - ma_job_queue_cas(&pQueue->tail, tail, ma_job_extract_slot(next)); - } - } - } - ma_job_queue_cas(&pQueue->tail, tail, slot); - } - #ifndef MA_USE_EXPERIMENTAL_LOCK_FREE_JOB_QUEUE - ma_spinlock_unlock(&pQueue->lock); - #endif - - - /* Signal the semaphore as the last step if we're using synchronous mode. */ - if ((pQueue->flags & MA_JOB_QUEUE_FLAG_NON_BLOCKING) == 0) { - #ifndef MA_NO_THREADING - { - ma_semaphore_release(&pQueue->sem); - } - #else - { - MA_ASSERT(MA_FALSE); /* Should never get here. Should have been checked at initialization time. */ - } - #endif - } - - return MA_SUCCESS; -} - -MA_API ma_result ma_job_queue_next(ma_job_queue* pQueue, ma_job* pJob) -{ - ma_uint64 head; - ma_uint64 tail; - ma_uint64 next; - - if (pQueue == NULL || pJob == NULL) { - return MA_INVALID_ARGS; - } - - /* If we're running in synchronous mode we'll need to wait on a semaphore. */ - if ((pQueue->flags & MA_JOB_QUEUE_FLAG_NON_BLOCKING) == 0) { - #ifndef MA_NO_THREADING - { - ma_semaphore_wait(&pQueue->sem); - } - #else - { - MA_ASSERT(MA_FALSE); /* Should never get here. Should have been checked at initialization time. */ - } - #endif - } - - #ifndef MA_USE_EXPERIMENTAL_LOCK_FREE_JOB_QUEUE - ma_spinlock_lock(&pQueue->lock); - #endif - { - /* - BUG: In lock-free mode, multiple threads can be in this section of code. The "head" variable in the loop below - is stored. One thread can fall through to the freeing of this item while another is still using "head" for the - retrieval of the "next" variable. - - The slot allocator might need to make use of some reference counting to ensure it's only truly freed when - there are no more references to the item. This must be fixed before removing these locks. - */ - - /* Now we need to remove the root item from the list. */ - for (;;) { - head = ma_atomic_load_64(&pQueue->head); - tail = ma_atomic_load_64(&pQueue->tail); - next = ma_atomic_load_64(&pQueue->pJobs[ma_job_extract_slot(head)].next); - - if (ma_job_toc_to_allocation(head) == ma_job_toc_to_allocation(ma_atomic_load_64(&pQueue->head))) { - if (ma_job_extract_slot(head) == ma_job_extract_slot(tail)) { - if (ma_job_extract_slot(next) == 0xFFFF) { - #ifndef MA_USE_EXPERIMENTAL_LOCK_FREE_JOB_QUEUE - ma_spinlock_unlock(&pQueue->lock); - #endif - return MA_NO_DATA_AVAILABLE; - } - ma_job_queue_cas(&pQueue->tail, tail, ma_job_extract_slot(next)); - } else { - *pJob = pQueue->pJobs[ma_job_extract_slot(next)]; - if (ma_job_queue_cas(&pQueue->head, head, ma_job_extract_slot(next))) { - break; - } - } - } - } - } - #ifndef MA_USE_EXPERIMENTAL_LOCK_FREE_JOB_QUEUE - ma_spinlock_unlock(&pQueue->lock); - #endif - - ma_slot_allocator_free(&pQueue->allocator, head); - - /* - If it's a quit job make sure it's put back on the queue to ensure other threads have an opportunity to detect it and terminate naturally. We - could instead just leave it on the queue, but that would involve fiddling with the lock-free code above and I want to keep that as simple as - possible. - */ - if (pJob->toc.breakup.code == MA_JOB_TYPE_QUIT) { - ma_job_queue_post(pQueue, pJob); - return MA_CANCELLED; /* Return a cancelled status just in case the thread is checking return codes and not properly checking for a quit job. */ - } - - return MA_SUCCESS; -} - - - -/******************************************************************************* - -Dynamic Linking - -*******************************************************************************/ -#ifdef MA_POSIX - /* No need for dlfcn.h if we're not using runtime linking. */ - #ifndef MA_NO_RUNTIME_LINKING - #include - #endif -#endif - -MA_API ma_handle ma_dlopen(ma_log* pLog, const char* filename) -{ -#ifndef MA_NO_RUNTIME_LINKING - ma_handle handle; - - ma_log_postf(pLog, MA_LOG_LEVEL_DEBUG, "Loading library: %s\n", filename); - - #ifdef MA_WIN32 - /* From MSDN: Desktop applications cannot use LoadPackagedLibrary; if a desktop application calls this function it fails with APPMODEL_ERROR_NO_PACKAGE.*/ - #if !defined(MA_WIN32_UWP) || !(defined(WINAPI_FAMILY) && ((defined(WINAPI_FAMILY_PHONE_APP) && WINAPI_FAMILY == WINAPI_FAMILY_PHONE_APP))) - handle = (ma_handle)LoadLibraryA(filename); - #else - /* *sigh* It appears there is no ANSI version of LoadPackagedLibrary()... */ - WCHAR filenameW[4096]; - if (MultiByteToWideChar(CP_UTF8, 0, filename, -1, filenameW, sizeof(filenameW)) == 0) { - handle = NULL; - } else { - handle = (ma_handle)LoadPackagedLibrary(filenameW, 0); - } - #endif - #else - handle = (ma_handle)dlopen(filename, RTLD_NOW); - #endif - - /* - I'm not considering failure to load a library an error nor a warning because seamlessly falling through to a lower-priority - backend is a deliberate design choice. Instead I'm logging it as an informational message. - */ - if (handle == NULL) { - ma_log_postf(pLog, MA_LOG_LEVEL_INFO, "Failed to load library: %s\n", filename); - } - - return handle; -#else - /* Runtime linking is disabled. */ - (void)pLog; - (void)filename; - return NULL; -#endif -} - -MA_API void ma_dlclose(ma_log* pLog, ma_handle handle) -{ -#ifndef MA_NO_RUNTIME_LINKING - #ifdef MA_WIN32 - FreeLibrary((HMODULE)handle); - #else - /* Hack for Android bug (see https://github.com/android/ndk/issues/360). Calling dlclose() pre-API 28 may segfault. */ - #if !defined(MA_ANDROID) || (defined(__ANDROID_API__) && __ANDROID_API__ >= 28) - { - dlclose((void*)handle); - } - #else - { - (void)handle; - } - #endif - #endif - - (void)pLog; -#else - /* Runtime linking is disabled. */ - (void)pLog; - (void)handle; -#endif -} - -MA_API ma_proc ma_dlsym(ma_log* pLog, ma_handle handle, const char* symbol) -{ -#ifndef MA_NO_RUNTIME_LINKING - ma_proc proc; - - ma_log_postf(pLog, MA_LOG_LEVEL_DEBUG, "Loading symbol: %s\n", symbol); - -#ifdef _WIN32 - proc = (ma_proc)GetProcAddress((HMODULE)handle, symbol); -#else -#if (defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 8))) || defined(__clang__) - #pragma GCC diagnostic push - #pragma GCC diagnostic ignored "-Wpedantic" -#endif - proc = (ma_proc)dlsym((void*)handle, symbol); -#if (defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 8))) || defined(__clang__) - #pragma GCC diagnostic pop -#endif -#endif - - if (proc == NULL) { - ma_log_postf(pLog, MA_LOG_LEVEL_WARNING, "Failed to load symbol: %s\n", symbol); - } - - (void)pLog; /* It's possible for pContext to be unused. */ - return proc; -#else - /* Runtime linking is disabled. */ - (void)pLog; - (void)handle; - (void)symbol; - return NULL; -#endif -} - - - -/************************************************************************************************************************************************************ -************************************************************************************************************************************************************* - -DEVICE I/O -========== - -************************************************************************************************************************************************************* -************************************************************************************************************************************************************/ - -/* Disable run-time linking on certain backends and platforms. */ -#ifndef MA_NO_RUNTIME_LINKING - #if defined(MA_EMSCRIPTEN) || defined(MA_ORBIS) || defined(MA_PROSPERO) - #define MA_NO_RUNTIME_LINKING - #endif -#endif - -#ifdef MA_APPLE - #include -#endif - -#ifndef MA_NO_DEVICE_IO - -#if defined(MA_APPLE) && (MAC_OS_X_VERSION_MIN_REQUIRED < 101200) - #include /* For mach_absolute_time() */ -#endif - -#ifdef MA_POSIX - #include - #include - - /* No need for dlfcn.h if we're not using runtime linking. */ - #ifndef MA_NO_RUNTIME_LINKING - #include - #endif -#endif - -/* This must be set to at least 26. */ -#ifndef MA_AAUDIO_MIN_ANDROID_SDK_VERSION -#define MA_AAUDIO_MIN_ANDROID_SDK_VERSION 27 -#endif - - -MA_API void ma_device_info_add_native_data_format(ma_device_info* pDeviceInfo, ma_format format, ma_uint32 channels, ma_uint32 sampleRate, ma_uint32 flags) -{ - if (pDeviceInfo == NULL) { - return; - } - - if (pDeviceInfo->nativeDataFormatCount < ma_countof(pDeviceInfo->nativeDataFormats)) { - pDeviceInfo->nativeDataFormats[pDeviceInfo->nativeDataFormatCount].format = format; - pDeviceInfo->nativeDataFormats[pDeviceInfo->nativeDataFormatCount].channels = channels; - pDeviceInfo->nativeDataFormats[pDeviceInfo->nativeDataFormatCount].sampleRate = sampleRate; - pDeviceInfo->nativeDataFormats[pDeviceInfo->nativeDataFormatCount].flags = flags; - pDeviceInfo->nativeDataFormatCount += 1; - } -} - - -typedef struct -{ - ma_backend backend; - const char* pName; -} ma_backend_info; - -static ma_backend_info gBackendInfo[] = /* Indexed by the backend enum. Must be in the order backends are declared in the ma_backend enum. */ -{ - {ma_backend_wasapi, "WASAPI"}, - {ma_backend_dsound, "DirectSound"}, - {ma_backend_winmm, "WinMM"}, - {ma_backend_coreaudio, "Core Audio"}, - {ma_backend_sndio, "sndio"}, - {ma_backend_audio4, "audio(4)"}, - {ma_backend_oss, "OSS"}, - {ma_backend_pulseaudio, "PulseAudio"}, - {ma_backend_alsa, "ALSA"}, - {ma_backend_jack, "JACK"}, - {ma_backend_aaudio, "AAudio"}, - {ma_backend_opensl, "OpenSL|ES"}, - {ma_backend_webaudio, "Web Audio"}, - {ma_backend_custom, "Custom"}, - {ma_backend_null, "Null"} -}; - -MA_API const char* ma_get_backend_name(ma_backend backend) -{ - if (backend < 0 || backend >= (int)ma_countof(gBackendInfo)) { - return "Unknown"; - } - - return gBackendInfo[backend].pName; -} - -MA_API ma_result ma_get_backend_from_name(const char* pBackendName, ma_backend* pBackend) -{ - size_t iBackend; - - if (pBackendName == NULL) { - return MA_INVALID_ARGS; - } - - for (iBackend = 0; iBackend < ma_countof(gBackendInfo); iBackend += 1) { - if (ma_strcmp(pBackendName, gBackendInfo[iBackend].pName) == 0) { - if (pBackend != NULL) { - *pBackend = gBackendInfo[iBackend].backend; - } - - return MA_SUCCESS; - } - } - - /* Getting here means the backend name is unknown. */ - return MA_INVALID_ARGS; -} - -MA_API ma_bool32 ma_is_backend_enabled(ma_backend backend) -{ - /* - This looks a little bit gross, but we want all backends to be included in the switch to avoid warnings on some compilers - about some enums not being handled by the switch statement. - */ - switch (backend) - { - case ma_backend_wasapi: - #if defined(MA_HAS_WASAPI) - return MA_TRUE; - #else - return MA_FALSE; - #endif - case ma_backend_dsound: - #if defined(MA_HAS_DSOUND) - return MA_TRUE; - #else - return MA_FALSE; - #endif - case ma_backend_winmm: - #if defined(MA_HAS_WINMM) - return MA_TRUE; - #else - return MA_FALSE; - #endif - case ma_backend_coreaudio: - #if defined(MA_HAS_COREAUDIO) - return MA_TRUE; - #else - return MA_FALSE; - #endif - case ma_backend_sndio: - #if defined(MA_HAS_SNDIO) - return MA_TRUE; - #else - return MA_FALSE; - #endif - case ma_backend_audio4: - #if defined(MA_HAS_AUDIO4) - return MA_TRUE; - #else - return MA_FALSE; - #endif - case ma_backend_oss: - #if defined(MA_HAS_OSS) - return MA_TRUE; - #else - return MA_FALSE; - #endif - case ma_backend_pulseaudio: - #if defined(MA_HAS_PULSEAUDIO) - return MA_TRUE; - #else - return MA_FALSE; - #endif - case ma_backend_alsa: - #if defined(MA_HAS_ALSA) - return MA_TRUE; - #else - return MA_FALSE; - #endif - case ma_backend_jack: - #if defined(MA_HAS_JACK) - return MA_TRUE; - #else - return MA_FALSE; - #endif - case ma_backend_aaudio: - #if defined(MA_HAS_AAUDIO) - #if defined(MA_ANDROID) - { - return ma_android_sdk_version() >= MA_AAUDIO_MIN_ANDROID_SDK_VERSION; - } - #else - return MA_FALSE; - #endif - #else - return MA_FALSE; - #endif - case ma_backend_opensl: - #if defined(MA_HAS_OPENSL) - #if defined(MA_ANDROID) - { - return ma_android_sdk_version() >= 9; - } - #else - return MA_TRUE; - #endif - #else - return MA_FALSE; - #endif - case ma_backend_webaudio: - #if defined(MA_HAS_WEBAUDIO) - return MA_TRUE; - #else - return MA_FALSE; - #endif - case ma_backend_custom: - #if defined(MA_HAS_CUSTOM) - return MA_TRUE; - #else - return MA_FALSE; - #endif - case ma_backend_null: - #if defined(MA_HAS_NULL) - return MA_TRUE; - #else - return MA_FALSE; - #endif - - default: return MA_FALSE; - } -} - -MA_API ma_result ma_get_enabled_backends(ma_backend* pBackends, size_t backendCap, size_t* pBackendCount) -{ - size_t backendCount; - size_t iBackend; - ma_result result = MA_SUCCESS; - - if (pBackendCount == NULL) { - return MA_INVALID_ARGS; - } - - backendCount = 0; - - for (iBackend = 0; iBackend <= ma_backend_null; iBackend += 1) { - ma_backend backend = (ma_backend)iBackend; - - if (ma_is_backend_enabled(backend)) { - /* The backend is enabled. Try adding it to the list. If there's no room, MA_NO_SPACE needs to be returned. */ - if (backendCount == backendCap) { - result = MA_NO_SPACE; - break; - } else { - pBackends[backendCount] = backend; - backendCount += 1; - } - } - } - - if (pBackendCount != NULL) { - *pBackendCount = backendCount; - } - - return result; -} - -MA_API ma_bool32 ma_is_loopback_supported(ma_backend backend) -{ - switch (backend) - { - case ma_backend_wasapi: return MA_TRUE; - case ma_backend_dsound: return MA_FALSE; - case ma_backend_winmm: return MA_FALSE; - case ma_backend_coreaudio: return MA_FALSE; - case ma_backend_sndio: return MA_FALSE; - case ma_backend_audio4: return MA_FALSE; - case ma_backend_oss: return MA_FALSE; - case ma_backend_pulseaudio: return MA_FALSE; - case ma_backend_alsa: return MA_FALSE; - case ma_backend_jack: return MA_FALSE; - case ma_backend_aaudio: return MA_FALSE; - case ma_backend_opensl: return MA_FALSE; - case ma_backend_webaudio: return MA_FALSE; - case ma_backend_custom: return MA_FALSE; /* <-- Will depend on the implementation of the backend. */ - case ma_backend_null: return MA_FALSE; - default: return MA_FALSE; - } -} - - - -#if defined(MA_WIN32) -/* WASAPI error codes. */ -#define MA_AUDCLNT_E_NOT_INITIALIZED ((HRESULT)0x88890001) -#define MA_AUDCLNT_E_ALREADY_INITIALIZED ((HRESULT)0x88890002) -#define MA_AUDCLNT_E_WRONG_ENDPOINT_TYPE ((HRESULT)0x88890003) -#define MA_AUDCLNT_E_DEVICE_INVALIDATED ((HRESULT)0x88890004) -#define MA_AUDCLNT_E_NOT_STOPPED ((HRESULT)0x88890005) -#define MA_AUDCLNT_E_BUFFER_TOO_LARGE ((HRESULT)0x88890006) -#define MA_AUDCLNT_E_OUT_OF_ORDER ((HRESULT)0x88890007) -#define MA_AUDCLNT_E_UNSUPPORTED_FORMAT ((HRESULT)0x88890008) -#define MA_AUDCLNT_E_INVALID_SIZE ((HRESULT)0x88890009) -#define MA_AUDCLNT_E_DEVICE_IN_USE ((HRESULT)0x8889000A) -#define MA_AUDCLNT_E_BUFFER_OPERATION_PENDING ((HRESULT)0x8889000B) -#define MA_AUDCLNT_E_THREAD_NOT_REGISTERED ((HRESULT)0x8889000C) -#define MA_AUDCLNT_E_NO_SINGLE_PROCESS ((HRESULT)0x8889000D) -#define MA_AUDCLNT_E_EXCLUSIVE_MODE_NOT_ALLOWED ((HRESULT)0x8889000E) -#define MA_AUDCLNT_E_ENDPOINT_CREATE_FAILED ((HRESULT)0x8889000F) -#define MA_AUDCLNT_E_SERVICE_NOT_RUNNING ((HRESULT)0x88890010) -#define MA_AUDCLNT_E_EVENTHANDLE_NOT_EXPECTED ((HRESULT)0x88890011) -#define MA_AUDCLNT_E_EXCLUSIVE_MODE_ONLY ((HRESULT)0x88890012) -#define MA_AUDCLNT_E_BUFDURATION_PERIOD_NOT_EQUAL ((HRESULT)0x88890013) -#define MA_AUDCLNT_E_EVENTHANDLE_NOT_SET ((HRESULT)0x88890014) -#define MA_AUDCLNT_E_INCORRECT_BUFFER_SIZE ((HRESULT)0x88890015) -#define MA_AUDCLNT_E_BUFFER_SIZE_ERROR ((HRESULT)0x88890016) -#define MA_AUDCLNT_E_CPUUSAGE_EXCEEDED ((HRESULT)0x88890017) -#define MA_AUDCLNT_E_BUFFER_ERROR ((HRESULT)0x88890018) -#define MA_AUDCLNT_E_BUFFER_SIZE_NOT_ALIGNED ((HRESULT)0x88890019) -#define MA_AUDCLNT_E_INVALID_DEVICE_PERIOD ((HRESULT)0x88890020) -#define MA_AUDCLNT_E_INVALID_STREAM_FLAG ((HRESULT)0x88890021) -#define MA_AUDCLNT_E_ENDPOINT_OFFLOAD_NOT_CAPABLE ((HRESULT)0x88890022) -#define MA_AUDCLNT_E_OUT_OF_OFFLOAD_RESOURCES ((HRESULT)0x88890023) -#define MA_AUDCLNT_E_OFFLOAD_MODE_ONLY ((HRESULT)0x88890024) -#define MA_AUDCLNT_E_NONOFFLOAD_MODE_ONLY ((HRESULT)0x88890025) -#define MA_AUDCLNT_E_RESOURCES_INVALIDATED ((HRESULT)0x88890026) -#define MA_AUDCLNT_E_RAW_MODE_UNSUPPORTED ((HRESULT)0x88890027) -#define MA_AUDCLNT_E_ENGINE_PERIODICITY_LOCKED ((HRESULT)0x88890028) -#define MA_AUDCLNT_E_ENGINE_FORMAT_LOCKED ((HRESULT)0x88890029) -#define MA_AUDCLNT_E_HEADTRACKING_ENABLED ((HRESULT)0x88890030) -#define MA_AUDCLNT_E_HEADTRACKING_UNSUPPORTED ((HRESULT)0x88890040) -#define MA_AUDCLNT_S_BUFFER_EMPTY ((HRESULT)0x08890001) -#define MA_AUDCLNT_S_THREAD_ALREADY_REGISTERED ((HRESULT)0x08890002) -#define MA_AUDCLNT_S_POSITION_STALLED ((HRESULT)0x08890003) - -#define MA_DS_OK ((HRESULT)0) -#define MA_DS_NO_VIRTUALIZATION ((HRESULT)0x0878000A) -#define MA_DSERR_ALLOCATED ((HRESULT)0x8878000A) -#define MA_DSERR_CONTROLUNAVAIL ((HRESULT)0x8878001E) -#define MA_DSERR_INVALIDPARAM ((HRESULT)0x80070057) /*E_INVALIDARG*/ -#define MA_DSERR_INVALIDCALL ((HRESULT)0x88780032) -#define MA_DSERR_GENERIC ((HRESULT)0x80004005) /*E_FAIL*/ -#define MA_DSERR_PRIOLEVELNEEDED ((HRESULT)0x88780046) -#define MA_DSERR_OUTOFMEMORY ((HRESULT)0x8007000E) /*E_OUTOFMEMORY*/ -#define MA_DSERR_BADFORMAT ((HRESULT)0x88780064) -#define MA_DSERR_UNSUPPORTED ((HRESULT)0x80004001) /*E_NOTIMPL*/ -#define MA_DSERR_NODRIVER ((HRESULT)0x88780078) -#define MA_DSERR_ALREADYINITIALIZED ((HRESULT)0x88780082) -#define MA_DSERR_NOAGGREGATION ((HRESULT)0x80040110) /*CLASS_E_NOAGGREGATION*/ -#define MA_DSERR_BUFFERLOST ((HRESULT)0x88780096) -#define MA_DSERR_OTHERAPPHASPRIO ((HRESULT)0x887800A0) -#define MA_DSERR_UNINITIALIZED ((HRESULT)0x887800AA) -#define MA_DSERR_NOINTERFACE ((HRESULT)0x80004002) /*E_NOINTERFACE*/ -#define MA_DSERR_ACCESSDENIED ((HRESULT)0x80070005) /*E_ACCESSDENIED*/ -#define MA_DSERR_BUFFERTOOSMALL ((HRESULT)0x887800B4) -#define MA_DSERR_DS8_REQUIRED ((HRESULT)0x887800BE) -#define MA_DSERR_SENDLOOP ((HRESULT)0x887800C8) -#define MA_DSERR_BADSENDBUFFERGUID ((HRESULT)0x887800D2) -#define MA_DSERR_OBJECTNOTFOUND ((HRESULT)0x88781161) -#define MA_DSERR_FXUNAVAILABLE ((HRESULT)0x887800DC) - -static ma_result ma_result_from_HRESULT(HRESULT hr) -{ - switch (hr) - { - case NOERROR: return MA_SUCCESS; - /*case S_OK: return MA_SUCCESS;*/ - - case E_POINTER: return MA_INVALID_ARGS; - case E_UNEXPECTED: return MA_ERROR; - case E_NOTIMPL: return MA_NOT_IMPLEMENTED; - case E_OUTOFMEMORY: return MA_OUT_OF_MEMORY; - case E_INVALIDARG: return MA_INVALID_ARGS; - case E_NOINTERFACE: return MA_API_NOT_FOUND; - case E_HANDLE: return MA_INVALID_ARGS; - case E_ABORT: return MA_ERROR; - case E_FAIL: return MA_ERROR; - case E_ACCESSDENIED: return MA_ACCESS_DENIED; - - /* WASAPI */ - case MA_AUDCLNT_E_NOT_INITIALIZED: return MA_DEVICE_NOT_INITIALIZED; - case MA_AUDCLNT_E_ALREADY_INITIALIZED: return MA_DEVICE_ALREADY_INITIALIZED; - case MA_AUDCLNT_E_WRONG_ENDPOINT_TYPE: return MA_INVALID_ARGS; - case MA_AUDCLNT_E_DEVICE_INVALIDATED: return MA_UNAVAILABLE; - case MA_AUDCLNT_E_NOT_STOPPED: return MA_DEVICE_NOT_STOPPED; - case MA_AUDCLNT_E_BUFFER_TOO_LARGE: return MA_TOO_BIG; - case MA_AUDCLNT_E_OUT_OF_ORDER: return MA_INVALID_OPERATION; - case MA_AUDCLNT_E_UNSUPPORTED_FORMAT: return MA_FORMAT_NOT_SUPPORTED; - case MA_AUDCLNT_E_INVALID_SIZE: return MA_INVALID_ARGS; - case MA_AUDCLNT_E_DEVICE_IN_USE: return MA_BUSY; - case MA_AUDCLNT_E_BUFFER_OPERATION_PENDING: return MA_INVALID_OPERATION; - case MA_AUDCLNT_E_THREAD_NOT_REGISTERED: return MA_DOES_NOT_EXIST; - case MA_AUDCLNT_E_NO_SINGLE_PROCESS: return MA_INVALID_OPERATION; - case MA_AUDCLNT_E_EXCLUSIVE_MODE_NOT_ALLOWED: return MA_SHARE_MODE_NOT_SUPPORTED; - case MA_AUDCLNT_E_ENDPOINT_CREATE_FAILED: return MA_FAILED_TO_OPEN_BACKEND_DEVICE; - case MA_AUDCLNT_E_SERVICE_NOT_RUNNING: return MA_NOT_CONNECTED; - case MA_AUDCLNT_E_EVENTHANDLE_NOT_EXPECTED: return MA_INVALID_ARGS; - case MA_AUDCLNT_E_EXCLUSIVE_MODE_ONLY: return MA_SHARE_MODE_NOT_SUPPORTED; - case MA_AUDCLNT_E_BUFDURATION_PERIOD_NOT_EQUAL: return MA_INVALID_ARGS; - case MA_AUDCLNT_E_EVENTHANDLE_NOT_SET: return MA_INVALID_ARGS; - case MA_AUDCLNT_E_INCORRECT_BUFFER_SIZE: return MA_INVALID_ARGS; - case MA_AUDCLNT_E_BUFFER_SIZE_ERROR: return MA_INVALID_ARGS; - case MA_AUDCLNT_E_CPUUSAGE_EXCEEDED: return MA_ERROR; - case MA_AUDCLNT_E_BUFFER_ERROR: return MA_ERROR; - case MA_AUDCLNT_E_BUFFER_SIZE_NOT_ALIGNED: return MA_INVALID_ARGS; - case MA_AUDCLNT_E_INVALID_DEVICE_PERIOD: return MA_INVALID_ARGS; - case MA_AUDCLNT_E_INVALID_STREAM_FLAG: return MA_INVALID_ARGS; - case MA_AUDCLNT_E_ENDPOINT_OFFLOAD_NOT_CAPABLE: return MA_INVALID_OPERATION; - case MA_AUDCLNT_E_OUT_OF_OFFLOAD_RESOURCES: return MA_OUT_OF_MEMORY; - case MA_AUDCLNT_E_OFFLOAD_MODE_ONLY: return MA_INVALID_OPERATION; - case MA_AUDCLNT_E_NONOFFLOAD_MODE_ONLY: return MA_INVALID_OPERATION; - case MA_AUDCLNT_E_RESOURCES_INVALIDATED: return MA_INVALID_DATA; - case MA_AUDCLNT_E_RAW_MODE_UNSUPPORTED: return MA_INVALID_OPERATION; - case MA_AUDCLNT_E_ENGINE_PERIODICITY_LOCKED: return MA_INVALID_OPERATION; - case MA_AUDCLNT_E_ENGINE_FORMAT_LOCKED: return MA_INVALID_OPERATION; - case MA_AUDCLNT_E_HEADTRACKING_ENABLED: return MA_INVALID_OPERATION; - case MA_AUDCLNT_E_HEADTRACKING_UNSUPPORTED: return MA_INVALID_OPERATION; - case MA_AUDCLNT_S_BUFFER_EMPTY: return MA_NO_SPACE; - case MA_AUDCLNT_S_THREAD_ALREADY_REGISTERED: return MA_ALREADY_EXISTS; - case MA_AUDCLNT_S_POSITION_STALLED: return MA_ERROR; - - /* DirectSound */ - /*case MA_DS_OK: return MA_SUCCESS;*/ /* S_OK */ - case MA_DS_NO_VIRTUALIZATION: return MA_SUCCESS; - case MA_DSERR_ALLOCATED: return MA_ALREADY_IN_USE; - case MA_DSERR_CONTROLUNAVAIL: return MA_INVALID_OPERATION; - /*case MA_DSERR_INVALIDPARAM: return MA_INVALID_ARGS;*/ /* E_INVALIDARG */ - case MA_DSERR_INVALIDCALL: return MA_INVALID_OPERATION; - /*case MA_DSERR_GENERIC: return MA_ERROR;*/ /* E_FAIL */ - case MA_DSERR_PRIOLEVELNEEDED: return MA_INVALID_OPERATION; - /*case MA_DSERR_OUTOFMEMORY: return MA_OUT_OF_MEMORY;*/ /* E_OUTOFMEMORY */ - case MA_DSERR_BADFORMAT: return MA_FORMAT_NOT_SUPPORTED; - /*case MA_DSERR_UNSUPPORTED: return MA_NOT_IMPLEMENTED;*/ /* E_NOTIMPL */ - case MA_DSERR_NODRIVER: return MA_FAILED_TO_INIT_BACKEND; - case MA_DSERR_ALREADYINITIALIZED: return MA_DEVICE_ALREADY_INITIALIZED; - case MA_DSERR_NOAGGREGATION: return MA_ERROR; - case MA_DSERR_BUFFERLOST: return MA_UNAVAILABLE; - case MA_DSERR_OTHERAPPHASPRIO: return MA_ACCESS_DENIED; - case MA_DSERR_UNINITIALIZED: return MA_DEVICE_NOT_INITIALIZED; - /*case MA_DSERR_NOINTERFACE: return MA_API_NOT_FOUND;*/ /* E_NOINTERFACE */ - /*case MA_DSERR_ACCESSDENIED: return MA_ACCESS_DENIED;*/ /* E_ACCESSDENIED */ - case MA_DSERR_BUFFERTOOSMALL: return MA_NO_SPACE; - case MA_DSERR_DS8_REQUIRED: return MA_INVALID_OPERATION; - case MA_DSERR_SENDLOOP: return MA_DEADLOCK; - case MA_DSERR_BADSENDBUFFERGUID: return MA_INVALID_ARGS; - case MA_DSERR_OBJECTNOTFOUND: return MA_NO_DEVICE; - case MA_DSERR_FXUNAVAILABLE: return MA_UNAVAILABLE; - - default: return MA_ERROR; - } -} - -/* PROPVARIANT */ -#define MA_VT_LPWSTR 31 -#define MA_VT_BLOB 65 - -#if defined(_MSC_VER) && !defined(__clang__) - #pragma warning(push) - #pragma warning(disable:4201) /* nonstandard extension used: nameless struct/union */ -#elif defined(__clang__) || (defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 8))) - #pragma GCC diagnostic push - #pragma GCC diagnostic ignored "-Wpedantic" /* For ISO C99 doesn't support unnamed structs/unions [-Wpedantic] */ - #if defined(__clang__) - #pragma GCC diagnostic ignored "-Wc11-extensions" /* anonymous unions are a C11 extension */ - #endif -#endif -typedef struct -{ - WORD vt; - WORD wReserved1; - WORD wReserved2; - WORD wReserved3; - union - { - struct - { - ULONG cbSize; - BYTE* pBlobData; - } blob; - WCHAR* pwszVal; - char pad[16]; /* Just to ensure the size of the struct matches the official version. */ - }; -} MA_PROPVARIANT; -#if defined(_MSC_VER) && !defined(__clang__) - #pragma warning(pop) -#elif defined(__clang__) || (defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 8))) - #pragma GCC diagnostic pop -#endif - -typedef HRESULT (WINAPI * MA_PFN_CoInitialize)(void* pvReserved); -typedef HRESULT (WINAPI * MA_PFN_CoInitializeEx)(void* pvReserved, DWORD dwCoInit); -typedef void (WINAPI * MA_PFN_CoUninitialize)(void); -typedef HRESULT (WINAPI * MA_PFN_CoCreateInstance)(const IID* rclsid, void* pUnkOuter, DWORD dwClsContext, const IID* riid, void* ppv); -typedef void (WINAPI * MA_PFN_CoTaskMemFree)(void* pv); -typedef HRESULT (WINAPI * MA_PFN_PropVariantClear)(MA_PROPVARIANT *pvar); -typedef int (WINAPI * MA_PFN_StringFromGUID2)(const GUID* const rguid, WCHAR* lpsz, int cchMax); - -typedef HWND (WINAPI * MA_PFN_GetForegroundWindow)(void); -typedef HWND (WINAPI * MA_PFN_GetDesktopWindow)(void); - -#if defined(MA_WIN32_DESKTOP) -/* Microsoft documents these APIs as returning LSTATUS, but the Win32 API shipping with some compilers do not define it. It's just a LONG. */ -typedef LONG (WINAPI * MA_PFN_RegOpenKeyExA)(HKEY hKey, const char* lpSubKey, DWORD ulOptions, DWORD samDesired, HKEY* phkResult); -typedef LONG (WINAPI * MA_PFN_RegCloseKey)(HKEY hKey); -typedef LONG (WINAPI * MA_PFN_RegQueryValueExA)(HKEY hKey, const char* lpValueName, DWORD* lpReserved, DWORD* lpType, BYTE* lpData, DWORD* lpcbData); -#endif /* MA_WIN32_DESKTOP */ - -MA_API size_t ma_strlen_WCHAR(const WCHAR* str) -{ - size_t len = 0; - while (str[len] != '\0') { - len += 1; - } - - return len; -} - -MA_API int ma_strcmp_WCHAR(const WCHAR *s1, const WCHAR *s2) -{ - while (*s1 != '\0' && *s1 == *s2) { - s1 += 1; - s2 += 1; - } - - return *s1 - *s2; -} - -MA_API int ma_strcpy_s_WCHAR(WCHAR* dst, size_t dstCap, const WCHAR* src) -{ - size_t i; - - if (dst == 0) { - return 22; - } - if (dstCap == 0) { - return 34; - } - if (src == 0) { - dst[0] = '\0'; - return 22; - } - - for (i = 0; i < dstCap && src[i] != '\0'; ++i) { - dst[i] = src[i]; - } - - if (i < dstCap) { - dst[i] = '\0'; - return 0; - } - - dst[0] = '\0'; - return 34; -} -#endif /* MA_WIN32 */ - - -#define MA_DEFAULT_PLAYBACK_DEVICE_NAME "Default Playback Device" -#define MA_DEFAULT_CAPTURE_DEVICE_NAME "Default Capture Device" - - - - -/******************************************************************************* - -Timing - -*******************************************************************************/ -#if defined(MA_WIN32) && !defined(MA_POSIX) - static LARGE_INTEGER g_ma_TimerFrequency; /* <-- Initialized to zero since it's static. */ - static void ma_timer_init(ma_timer* pTimer) - { - LARGE_INTEGER counter; - - if (g_ma_TimerFrequency.QuadPart == 0) { - QueryPerformanceFrequency(&g_ma_TimerFrequency); - } - - QueryPerformanceCounter(&counter); - pTimer->counter = counter.QuadPart; - } - - static double ma_timer_get_time_in_seconds(ma_timer* pTimer) - { - LARGE_INTEGER counter; - if (!QueryPerformanceCounter(&counter)) { - return 0; - } - - return (double)(counter.QuadPart - pTimer->counter) / g_ma_TimerFrequency.QuadPart; - } -#elif defined(MA_APPLE) && (MAC_OS_X_VERSION_MIN_REQUIRED < 101200) - static ma_uint64 g_ma_TimerFrequency = 0; - static void ma_timer_init(ma_timer* pTimer) - { - mach_timebase_info_data_t baseTime; - mach_timebase_info(&baseTime); - g_ma_TimerFrequency = (baseTime.denom * 1e9) / baseTime.numer; - - pTimer->counter = mach_absolute_time(); - } - - static double ma_timer_get_time_in_seconds(ma_timer* pTimer) - { - ma_uint64 newTimeCounter = mach_absolute_time(); - ma_uint64 oldTimeCounter = pTimer->counter; - - return (newTimeCounter - oldTimeCounter) / g_ma_TimerFrequency; - } -#elif defined(MA_EMSCRIPTEN) - static MA_INLINE void ma_timer_init(ma_timer* pTimer) - { - pTimer->counterD = emscripten_get_now(); - } - - static MA_INLINE double ma_timer_get_time_in_seconds(ma_timer* pTimer) - { - return (emscripten_get_now() - pTimer->counterD) / 1000; /* Emscripten is in milliseconds. */ - } -#else - #if defined(_POSIX_C_SOURCE) && _POSIX_C_SOURCE >= 199309L - #if defined(CLOCK_MONOTONIC) - #define MA_CLOCK_ID CLOCK_MONOTONIC - #else - #define MA_CLOCK_ID CLOCK_REALTIME - #endif - - static void ma_timer_init(ma_timer* pTimer) - { - struct timespec newTime; - clock_gettime(MA_CLOCK_ID, &newTime); - - pTimer->counter = (newTime.tv_sec * 1000000000) + newTime.tv_nsec; - } - - static double ma_timer_get_time_in_seconds(ma_timer* pTimer) - { - ma_uint64 newTimeCounter; - ma_uint64 oldTimeCounter; - - struct timespec newTime; - clock_gettime(MA_CLOCK_ID, &newTime); - - newTimeCounter = (newTime.tv_sec * 1000000000) + newTime.tv_nsec; - oldTimeCounter = pTimer->counter; - - return (newTimeCounter - oldTimeCounter) / 1000000000.0; - } - #else - static void ma_timer_init(ma_timer* pTimer) - { - struct timeval newTime; - gettimeofday(&newTime, NULL); - - pTimer->counter = (newTime.tv_sec * 1000000) + newTime.tv_usec; - } - - static double ma_timer_get_time_in_seconds(ma_timer* pTimer) - { - ma_uint64 newTimeCounter; - ma_uint64 oldTimeCounter; - - struct timeval newTime; - gettimeofday(&newTime, NULL); - - newTimeCounter = (newTime.tv_sec * 1000000) + newTime.tv_usec; - oldTimeCounter = pTimer->counter; - - return (newTimeCounter - oldTimeCounter) / 1000000.0; - } - #endif -#endif - - - -#if 0 -static ma_uint32 ma_get_closest_standard_sample_rate(ma_uint32 sampleRateIn) -{ - ma_uint32 closestRate = 0; - ma_uint32 closestDiff = 0xFFFFFFFF; - size_t iStandardRate; - - for (iStandardRate = 0; iStandardRate < ma_countof(g_maStandardSampleRatePriorities); ++iStandardRate) { - ma_uint32 standardRate = g_maStandardSampleRatePriorities[iStandardRate]; - ma_uint32 diff; - - if (sampleRateIn > standardRate) { - diff = sampleRateIn - standardRate; - } else { - diff = standardRate - sampleRateIn; - } - - if (diff == 0) { - return standardRate; /* The input sample rate is a standard rate. */ - } - - if (closestDiff > diff) { - closestDiff = diff; - closestRate = standardRate; - } - } - - return closestRate; -} -#endif - - -static MA_INLINE unsigned int ma_device_disable_denormals(ma_device* pDevice) -{ - MA_ASSERT(pDevice != NULL); - - if (!pDevice->noDisableDenormals) { - return ma_disable_denormals(); - } else { - return 0; - } -} - -static MA_INLINE void ma_device_restore_denormals(ma_device* pDevice, unsigned int prevState) -{ - MA_ASSERT(pDevice != NULL); - - if (!pDevice->noDisableDenormals) { - ma_restore_denormals(prevState); - } else { - /* Do nothing. */ - (void)prevState; - } -} - -static ma_device_notification ma_device_notification_init(ma_device* pDevice, ma_device_notification_type type) -{ - ma_device_notification notification; - - MA_ZERO_OBJECT(¬ification); - notification.pDevice = pDevice; - notification.type = type; - - return notification; -} - -static void ma_device__on_notification(ma_device_notification notification) -{ - MA_ASSERT(notification.pDevice != NULL); - - if (notification.pDevice->onNotification != NULL) { - notification.pDevice->onNotification(¬ification); - } - - /* TEMP FOR COMPATIBILITY: If it's a stopped notification, fire the onStop callback as well. This is only for backwards compatibility and will be removed. */ - if (notification.pDevice->onStop != NULL && notification.type == ma_device_notification_type_stopped) { - notification.pDevice->onStop(notification.pDevice); - } -} - -static void ma_device__on_notification_started(ma_device* pDevice) -{ - ma_device__on_notification(ma_device_notification_init(pDevice, ma_device_notification_type_started)); -} - -static void ma_device__on_notification_stopped(ma_device* pDevice) -{ - ma_device__on_notification(ma_device_notification_init(pDevice, ma_device_notification_type_stopped)); -} - -/* Not all platforms support reroute notifications. */ -#if !defined(MA_EMSCRIPTEN) -static void ma_device__on_notification_rerouted(ma_device* pDevice) -{ - ma_device__on_notification(ma_device_notification_init(pDevice, ma_device_notification_type_rerouted)); -} -#endif - -#if defined(MA_EMSCRIPTEN) -#ifdef __cplusplus -extern "C" { -#endif -void EMSCRIPTEN_KEEPALIVE ma_device__on_notification_unlocked(ma_device* pDevice) -{ - ma_device__on_notification(ma_device_notification_init(pDevice, ma_device_notification_type_unlocked)); -} -#ifdef __cplusplus -} -#endif -#endif - - -static void ma_device__on_data_inner(ma_device* pDevice, void* pFramesOut, const void* pFramesIn, ma_uint32 frameCount) -{ - MA_ASSERT(pDevice != NULL); - MA_ASSERT(pDevice->onData != NULL); - - if (!pDevice->noPreSilencedOutputBuffer && pFramesOut != NULL) { - ma_silence_pcm_frames(pFramesOut, frameCount, pDevice->playback.format, pDevice->playback.channels); - } - - pDevice->onData(pDevice, pFramesOut, pFramesIn, frameCount); -} - -static void ma_device__on_data(ma_device* pDevice, void* pFramesOut, const void* pFramesIn, ma_uint32 frameCount) -{ - MA_ASSERT(pDevice != NULL); - - /* Don't read more data from the client if we're in the process of stopping. */ - if (ma_device_get_state(pDevice) == ma_device_state_stopping) { - return; - } - - if (pDevice->noFixedSizedCallback) { - /* Fast path. Not using a fixed sized callback. Process directly from the specified buffers. */ - ma_device__on_data_inner(pDevice, pFramesOut, pFramesIn, frameCount); - } else { - /* Slow path. Using a fixed sized callback. Need to use the intermediary buffer. */ - ma_uint32 totalFramesProcessed = 0; - - while (totalFramesProcessed < frameCount) { - ma_uint32 totalFramesRemaining = frameCount - totalFramesProcessed; - ma_uint32 framesToProcessThisIteration = 0; - - if (pFramesIn != NULL) { - /* Capturing. Write to the intermediary buffer. If there's no room, fire the callback to empty it. */ - if (pDevice->capture.intermediaryBufferLen < pDevice->capture.intermediaryBufferCap) { - /* There's some room left in the intermediary buffer. Write to it without firing the callback. */ - framesToProcessThisIteration = totalFramesRemaining; - if (framesToProcessThisIteration > pDevice->capture.intermediaryBufferCap - pDevice->capture.intermediaryBufferLen) { - framesToProcessThisIteration = pDevice->capture.intermediaryBufferCap - pDevice->capture.intermediaryBufferLen; - } - - ma_copy_pcm_frames( - ma_offset_pcm_frames_ptr(pDevice->capture.pIntermediaryBuffer, pDevice->capture.intermediaryBufferLen, pDevice->capture.format, pDevice->capture.channels), - ma_offset_pcm_frames_const_ptr(pFramesIn, totalFramesProcessed, pDevice->capture.format, pDevice->capture.channels), - framesToProcessThisIteration, - pDevice->capture.format, pDevice->capture.channels); - - pDevice->capture.intermediaryBufferLen += framesToProcessThisIteration; - } - - if (pDevice->capture.intermediaryBufferLen == pDevice->capture.intermediaryBufferCap) { - /* No room left in the intermediary buffer. Fire the data callback. */ - if (pDevice->type == ma_device_type_duplex) { - /* We'll do the duplex data callback later after we've processed the playback data. */ - } else { - ma_device__on_data_inner(pDevice, NULL, pDevice->capture.pIntermediaryBuffer, pDevice->capture.intermediaryBufferCap); - - /* The intermediary buffer has just been drained. */ - pDevice->capture.intermediaryBufferLen = 0; - } - } - } - - if (pFramesOut != NULL) { - /* Playing back. Read from the intermediary buffer. If there's nothing in it, fire the callback to fill it. */ - if (pDevice->playback.intermediaryBufferLen > 0) { - /* There's some content in the intermediary buffer. Read from that without firing the callback. */ - if (pDevice->type == ma_device_type_duplex) { - /* The frames processed this iteration for a duplex device will always be based on the capture side. Leave it unmodified. */ - } else { - framesToProcessThisIteration = totalFramesRemaining; - if (framesToProcessThisIteration > pDevice->playback.intermediaryBufferLen) { - framesToProcessThisIteration = pDevice->playback.intermediaryBufferLen; - } - } - - ma_copy_pcm_frames( - ma_offset_pcm_frames_ptr(pFramesOut, totalFramesProcessed, pDevice->playback.format, pDevice->playback.channels), - ma_offset_pcm_frames_ptr(pDevice->playback.pIntermediaryBuffer, pDevice->playback.intermediaryBufferCap - pDevice->playback.intermediaryBufferLen, pDevice->playback.format, pDevice->playback.channels), - framesToProcessThisIteration, - pDevice->playback.format, pDevice->playback.channels); - - pDevice->playback.intermediaryBufferLen -= framesToProcessThisIteration; - } - - if (pDevice->playback.intermediaryBufferLen == 0) { - /* There's nothing in the intermediary buffer. Fire the data callback to fill it. */ - if (pDevice->type == ma_device_type_duplex) { - /* In duplex mode, the data callback will be fired later. Nothing to do here. */ - } else { - ma_device__on_data_inner(pDevice, pDevice->playback.pIntermediaryBuffer, NULL, pDevice->playback.intermediaryBufferCap); - - /* The intermediary buffer has just been filled. */ - pDevice->playback.intermediaryBufferLen = pDevice->playback.intermediaryBufferCap; - } - } - } - - /* If we're in duplex mode we might need to do a refill of the data. */ - if (pDevice->type == ma_device_type_duplex) { - if (pDevice->capture.intermediaryBufferLen == pDevice->capture.intermediaryBufferCap) { - ma_device__on_data_inner(pDevice, pDevice->playback.pIntermediaryBuffer, pDevice->capture.pIntermediaryBuffer, pDevice->capture.intermediaryBufferCap); - - pDevice->playback.intermediaryBufferLen = pDevice->playback.intermediaryBufferCap; /* The playback buffer will have just been filled. */ - pDevice->capture.intermediaryBufferLen = 0; /* The intermediary buffer has just been drained. */ - } - } - - /* Make sure this is only incremented once in the duplex case. */ - totalFramesProcessed += framesToProcessThisIteration; - } - } -} - -static void ma_device__handle_data_callback(ma_device* pDevice, void* pFramesOut, const void* pFramesIn, ma_uint32 frameCount) -{ - float masterVolumeFactor; - - ma_device_get_master_volume(pDevice, &masterVolumeFactor); /* Use ma_device_get_master_volume() to ensure the volume is loaded atomically. */ - - if (pDevice->onData) { - unsigned int prevDenormalState = ma_device_disable_denormals(pDevice); - { - /* Volume control of input makes things a bit awkward because the input buffer is read-only. We'll need to use a temp buffer and loop in this case. */ - if (pFramesIn != NULL && masterVolumeFactor != 1) { - ma_uint8 tempFramesIn[MA_DATA_CONVERTER_STACK_BUFFER_SIZE]; - ma_uint32 bpfCapture = ma_get_bytes_per_frame(pDevice->capture.format, pDevice->capture.channels); - ma_uint32 bpfPlayback = ma_get_bytes_per_frame(pDevice->playback.format, pDevice->playback.channels); - ma_uint32 totalFramesProcessed = 0; - while (totalFramesProcessed < frameCount) { - ma_uint32 framesToProcessThisIteration = frameCount - totalFramesProcessed; - if (framesToProcessThisIteration > sizeof(tempFramesIn)/bpfCapture) { - framesToProcessThisIteration = sizeof(tempFramesIn)/bpfCapture; - } - - ma_copy_and_apply_volume_factor_pcm_frames(tempFramesIn, ma_offset_ptr(pFramesIn, totalFramesProcessed*bpfCapture), framesToProcessThisIteration, pDevice->capture.format, pDevice->capture.channels, masterVolumeFactor); - - ma_device__on_data(pDevice, ma_offset_ptr(pFramesOut, totalFramesProcessed*bpfPlayback), tempFramesIn, framesToProcessThisIteration); - - totalFramesProcessed += framesToProcessThisIteration; - } - } else { - ma_device__on_data(pDevice, pFramesOut, pFramesIn, frameCount); - } - - /* Volume control and clipping for playback devices. */ - if (pFramesOut != NULL) { - if (masterVolumeFactor != 1) { - if (pFramesIn == NULL) { /* <-- In full-duplex situations, the volume will have been applied to the input samples before the data callback. Applying it again post-callback will incorrectly compound it. */ - ma_apply_volume_factor_pcm_frames(pFramesOut, frameCount, pDevice->playback.format, pDevice->playback.channels, masterVolumeFactor); - } - } - - if (!pDevice->noClip && pDevice->playback.format == ma_format_f32) { - ma_clip_samples_f32((float*)pFramesOut, (const float*)pFramesOut, frameCount * pDevice->playback.channels); /* Intentionally specifying the same pointer for both input and output for in-place processing. */ - } - } - } - ma_device_restore_denormals(pDevice, prevDenormalState); - } else { - /* No data callback. Just silence the output. */ - if (pFramesOut != NULL) { - ma_silence_pcm_frames(pFramesOut, frameCount, pDevice->playback.format, pDevice->playback.channels); - } - } -} - - - -/* A helper function for reading sample data from the client. */ -static void ma_device__read_frames_from_client(ma_device* pDevice, ma_uint32 frameCount, void* pFramesOut) -{ - MA_ASSERT(pDevice != NULL); - MA_ASSERT(frameCount > 0); - MA_ASSERT(pFramesOut != NULL); - - if (pDevice->playback.converter.isPassthrough) { - ma_device__handle_data_callback(pDevice, pFramesOut, NULL, frameCount); - } else { - ma_result result; - ma_uint64 totalFramesReadOut; - void* pRunningFramesOut; - - totalFramesReadOut = 0; - pRunningFramesOut = pFramesOut; - - /* - We run slightly different logic depending on whether or not we're using a heap-allocated - buffer for caching input data. This will be the case if the data converter does not have - the ability to retrieve the required input frame count for a given output frame count. - */ - if (pDevice->playback.pInputCache != NULL) { - while (totalFramesReadOut < frameCount) { - ma_uint64 framesToReadThisIterationIn; - ma_uint64 framesToReadThisIterationOut; - - /* If there's any data available in the cache, that needs to get processed first. */ - if (pDevice->playback.inputCacheRemaining > 0) { - framesToReadThisIterationOut = (frameCount - totalFramesReadOut); - framesToReadThisIterationIn = framesToReadThisIterationOut; - if (framesToReadThisIterationIn > pDevice->playback.inputCacheRemaining) { - framesToReadThisIterationIn = pDevice->playback.inputCacheRemaining; - } - - result = ma_data_converter_process_pcm_frames(&pDevice->playback.converter, ma_offset_pcm_frames_ptr(pDevice->playback.pInputCache, pDevice->playback.inputCacheConsumed, pDevice->playback.format, pDevice->playback.channels), &framesToReadThisIterationIn, pRunningFramesOut, &framesToReadThisIterationOut); - if (result != MA_SUCCESS) { - break; - } - - pDevice->playback.inputCacheConsumed += framesToReadThisIterationIn; - pDevice->playback.inputCacheRemaining -= framesToReadThisIterationIn; - - totalFramesReadOut += framesToReadThisIterationOut; - pRunningFramesOut = ma_offset_ptr(pRunningFramesOut, framesToReadThisIterationOut * ma_get_bytes_per_frame(pDevice->playback.internalFormat, pDevice->playback.internalChannels)); - - if (framesToReadThisIterationIn == 0 && framesToReadThisIterationOut == 0) { - break; /* We're done. */ - } - } - - /* Getting here means there's no data in the cache and we need to fill it up with data from the client. */ - if (pDevice->playback.inputCacheRemaining == 0) { - ma_device__handle_data_callback(pDevice, pDevice->playback.pInputCache, NULL, (ma_uint32)pDevice->playback.inputCacheCap); - - pDevice->playback.inputCacheConsumed = 0; - pDevice->playback.inputCacheRemaining = pDevice->playback.inputCacheCap; - } - } - } else { - while (totalFramesReadOut < frameCount) { - ma_uint8 pIntermediaryBuffer[MA_DATA_CONVERTER_STACK_BUFFER_SIZE]; /* In client format. */ - ma_uint64 intermediaryBufferCap = sizeof(pIntermediaryBuffer) / ma_get_bytes_per_frame(pDevice->playback.format, pDevice->playback.channels); - ma_uint64 framesToReadThisIterationIn; - ma_uint64 framesReadThisIterationIn; - ma_uint64 framesToReadThisIterationOut; - ma_uint64 framesReadThisIterationOut; - ma_uint64 requiredInputFrameCount; - - framesToReadThisIterationOut = (frameCount - totalFramesReadOut); - framesToReadThisIterationIn = framesToReadThisIterationOut; - if (framesToReadThisIterationIn > intermediaryBufferCap) { - framesToReadThisIterationIn = intermediaryBufferCap; - } - - ma_data_converter_get_required_input_frame_count(&pDevice->playback.converter, framesToReadThisIterationOut, &requiredInputFrameCount); - if (framesToReadThisIterationIn > requiredInputFrameCount) { - framesToReadThisIterationIn = requiredInputFrameCount; - } - - ma_device__handle_data_callback(pDevice, pIntermediaryBuffer, NULL, (ma_uint32)framesToReadThisIterationIn); - - /* - At this point we have our decoded data in input format and now we need to convert to output format. Note that even if we didn't read any - input frames, we still want to try processing frames because there may some output frames generated from cached input data. - */ - framesReadThisIterationIn = framesToReadThisIterationIn; - framesReadThisIterationOut = framesToReadThisIterationOut; - result = ma_data_converter_process_pcm_frames(&pDevice->playback.converter, pIntermediaryBuffer, &framesReadThisIterationIn, pRunningFramesOut, &framesReadThisIterationOut); - if (result != MA_SUCCESS) { - break; - } - - totalFramesReadOut += framesReadThisIterationOut; - pRunningFramesOut = ma_offset_ptr(pRunningFramesOut, framesReadThisIterationOut * ma_get_bytes_per_frame(pDevice->playback.internalFormat, pDevice->playback.internalChannels)); - - if (framesReadThisIterationIn == 0 && framesReadThisIterationOut == 0) { - break; /* We're done. */ - } - } - } - } -} - -/* A helper for sending sample data to the client. */ -static void ma_device__send_frames_to_client(ma_device* pDevice, ma_uint32 frameCountInDeviceFormat, const void* pFramesInDeviceFormat) -{ - MA_ASSERT(pDevice != NULL); - MA_ASSERT(frameCountInDeviceFormat > 0); - MA_ASSERT(pFramesInDeviceFormat != NULL); - - if (pDevice->capture.converter.isPassthrough) { - ma_device__handle_data_callback(pDevice, NULL, pFramesInDeviceFormat, frameCountInDeviceFormat); - } else { - ma_result result; - ma_uint8 pFramesInClientFormat[MA_DATA_CONVERTER_STACK_BUFFER_SIZE]; - ma_uint64 framesInClientFormatCap = sizeof(pFramesInClientFormat) / ma_get_bytes_per_frame(pDevice->capture.format, pDevice->capture.channels); - ma_uint64 totalDeviceFramesProcessed = 0; - ma_uint64 totalClientFramesProcessed = 0; - const void* pRunningFramesInDeviceFormat = pFramesInDeviceFormat; - - /* We just keep going until we've exhausted all of our input frames and cannot generate any more output frames. */ - for (;;) { - ma_uint64 deviceFramesProcessedThisIteration; - ma_uint64 clientFramesProcessedThisIteration; - - deviceFramesProcessedThisIteration = (frameCountInDeviceFormat - totalDeviceFramesProcessed); - clientFramesProcessedThisIteration = framesInClientFormatCap; - - result = ma_data_converter_process_pcm_frames(&pDevice->capture.converter, pRunningFramesInDeviceFormat, &deviceFramesProcessedThisIteration, pFramesInClientFormat, &clientFramesProcessedThisIteration); - if (result != MA_SUCCESS) { - break; - } - - if (clientFramesProcessedThisIteration > 0) { - ma_device__handle_data_callback(pDevice, NULL, pFramesInClientFormat, (ma_uint32)clientFramesProcessedThisIteration); /* Safe cast. */ - } - - pRunningFramesInDeviceFormat = ma_offset_ptr(pRunningFramesInDeviceFormat, deviceFramesProcessedThisIteration * ma_get_bytes_per_frame(pDevice->capture.internalFormat, pDevice->capture.internalChannels)); - totalDeviceFramesProcessed += deviceFramesProcessedThisIteration; - totalClientFramesProcessed += clientFramesProcessedThisIteration; - - /* This is just to silence a warning. I might want to use this variable later so leaving in place for now. */ - (void)totalClientFramesProcessed; - - if (deviceFramesProcessedThisIteration == 0 && clientFramesProcessedThisIteration == 0) { - break; /* We're done. */ - } - } - } -} - -static ma_result ma_device__handle_duplex_callback_capture(ma_device* pDevice, ma_uint32 frameCountInDeviceFormat, const void* pFramesInDeviceFormat, ma_pcm_rb* pRB) -{ - ma_result result; - ma_uint32 totalDeviceFramesProcessed = 0; - const void* pRunningFramesInDeviceFormat = pFramesInDeviceFormat; - - MA_ASSERT(pDevice != NULL); - MA_ASSERT(frameCountInDeviceFormat > 0); - MA_ASSERT(pFramesInDeviceFormat != NULL); - MA_ASSERT(pRB != NULL); - - /* Write to the ring buffer. The ring buffer is in the client format which means we need to convert. */ - for (;;) { - ma_uint32 framesToProcessInDeviceFormat = (frameCountInDeviceFormat - totalDeviceFramesProcessed); - ma_uint32 framesToProcessInClientFormat = MA_DATA_CONVERTER_STACK_BUFFER_SIZE / ma_get_bytes_per_frame(pDevice->capture.format, pDevice->capture.channels); - ma_uint64 framesProcessedInDeviceFormat; - ma_uint64 framesProcessedInClientFormat; - void* pFramesInClientFormat; - - result = ma_pcm_rb_acquire_write(pRB, &framesToProcessInClientFormat, &pFramesInClientFormat); - if (result != MA_SUCCESS) { - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "Failed to acquire capture PCM frames from ring buffer."); - break; - } - - if (framesToProcessInClientFormat == 0) { - if (ma_pcm_rb_pointer_distance(pRB) == (ma_int32)ma_pcm_rb_get_subbuffer_size(pRB)) { - break; /* Overrun. Not enough room in the ring buffer for input frame. Excess frames are dropped. */ - } - } - - /* Convert. */ - framesProcessedInDeviceFormat = framesToProcessInDeviceFormat; - framesProcessedInClientFormat = framesToProcessInClientFormat; - result = ma_data_converter_process_pcm_frames(&pDevice->capture.converter, pRunningFramesInDeviceFormat, &framesProcessedInDeviceFormat, pFramesInClientFormat, &framesProcessedInClientFormat); - if (result != MA_SUCCESS) { - break; - } - - result = ma_pcm_rb_commit_write(pRB, (ma_uint32)framesProcessedInClientFormat); /* Safe cast. */ - if (result != MA_SUCCESS) { - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "Failed to commit capture PCM frames to ring buffer."); - break; - } - - pRunningFramesInDeviceFormat = ma_offset_ptr(pRunningFramesInDeviceFormat, framesProcessedInDeviceFormat * ma_get_bytes_per_frame(pDevice->capture.internalFormat, pDevice->capture.internalChannels)); - totalDeviceFramesProcessed += (ma_uint32)framesProcessedInDeviceFormat; /* Safe cast. */ - - /* We're done when we're unable to process any client nor device frames. */ - if (framesProcessedInClientFormat == 0 && framesProcessedInDeviceFormat == 0) { - break; /* Done. */ - } - } - - return MA_SUCCESS; -} - -static ma_result ma_device__handle_duplex_callback_playback(ma_device* pDevice, ma_uint32 frameCount, void* pFramesInInternalFormat, ma_pcm_rb* pRB) -{ - ma_result result; - ma_uint8 silentInputFrames[MA_DATA_CONVERTER_STACK_BUFFER_SIZE]; - ma_uint32 totalFramesReadOut = 0; - - MA_ASSERT(pDevice != NULL); - MA_ASSERT(frameCount > 0); - MA_ASSERT(pFramesInInternalFormat != NULL); - MA_ASSERT(pRB != NULL); - MA_ASSERT(pDevice->playback.pInputCache != NULL); - - /* - Sitting in the ring buffer should be captured data from the capture callback in external format. If there's not enough data in there for - the whole frameCount frames we just use silence instead for the input data. - */ - MA_ZERO_MEMORY(silentInputFrames, sizeof(silentInputFrames)); - - while (totalFramesReadOut < frameCount && ma_device_is_started(pDevice)) { - /* - We should have a buffer allocated on the heap. Any playback frames still sitting in there - need to be sent to the internal device before we process any more data from the client. - */ - if (pDevice->playback.inputCacheRemaining > 0) { - ma_uint64 framesConvertedIn = pDevice->playback.inputCacheRemaining; - ma_uint64 framesConvertedOut = (frameCount - totalFramesReadOut); - ma_data_converter_process_pcm_frames(&pDevice->playback.converter, ma_offset_pcm_frames_ptr(pDevice->playback.pInputCache, pDevice->playback.inputCacheConsumed, pDevice->playback.format, pDevice->playback.channels), &framesConvertedIn, pFramesInInternalFormat, &framesConvertedOut); - - pDevice->playback.inputCacheConsumed += framesConvertedIn; - pDevice->playback.inputCacheRemaining -= framesConvertedIn; - - totalFramesReadOut += (ma_uint32)framesConvertedOut; /* Safe cast. */ - pFramesInInternalFormat = ma_offset_ptr(pFramesInInternalFormat, framesConvertedOut * ma_get_bytes_per_frame(pDevice->playback.internalFormat, pDevice->playback.internalChannels)); - } - - /* If there's no more data in the cache we'll need to fill it with some. */ - if (totalFramesReadOut < frameCount && pDevice->playback.inputCacheRemaining == 0) { - ma_uint32 inputFrameCount; - void* pInputFrames; - - inputFrameCount = (ma_uint32)pDevice->playback.inputCacheCap; - result = ma_pcm_rb_acquire_read(pRB, &inputFrameCount, &pInputFrames); - if (result == MA_SUCCESS) { - if (inputFrameCount > 0) { - ma_device__handle_data_callback(pDevice, pDevice->playback.pInputCache, pInputFrames, inputFrameCount); - } else { - if (ma_pcm_rb_pointer_distance(pRB) == 0) { - break; /* Underrun. */ - } - } - } else { - /* No capture data available. Feed in silence. */ - inputFrameCount = (ma_uint32)ma_min(pDevice->playback.inputCacheCap, sizeof(silentInputFrames) / ma_get_bytes_per_frame(pDevice->capture.format, pDevice->capture.channels)); - ma_device__handle_data_callback(pDevice, pDevice->playback.pInputCache, silentInputFrames, inputFrameCount); - } - - pDevice->playback.inputCacheConsumed = 0; - pDevice->playback.inputCacheRemaining = inputFrameCount; - - result = ma_pcm_rb_commit_read(pRB, inputFrameCount); - if (result != MA_SUCCESS) { - return result; /* Should never happen. */ - } - } - } - - return MA_SUCCESS; -} - -/* A helper for changing the state of the device. */ -static MA_INLINE void ma_device__set_state(ma_device* pDevice, ma_device_state newState) -{ - ma_atomic_device_state_set(&pDevice->state, newState); -} - - -#if defined(MA_WIN32) - static GUID MA_GUID_KSDATAFORMAT_SUBTYPE_PCM = {0x00000001, 0x0000, 0x0010, {0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71}}; - static GUID MA_GUID_KSDATAFORMAT_SUBTYPE_IEEE_FLOAT = {0x00000003, 0x0000, 0x0010, {0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71}}; - /*static GUID MA_GUID_KSDATAFORMAT_SUBTYPE_ALAW = {0x00000006, 0x0000, 0x0010, {0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71}};*/ - /*static GUID MA_GUID_KSDATAFORMAT_SUBTYPE_MULAW = {0x00000007, 0x0000, 0x0010, {0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71}};*/ -#endif - - - -MA_API ma_uint32 ma_get_format_priority_index(ma_format format) /* Lower = better. */ -{ - ma_uint32 i; - for (i = 0; i < ma_countof(g_maFormatPriorities); ++i) { - if (g_maFormatPriorities[i] == format) { - return i; - } - } - - /* Getting here means the format could not be found or is equal to ma_format_unknown. */ - return (ma_uint32)-1; -} - -static ma_result ma_device__post_init_setup(ma_device* pDevice, ma_device_type deviceType); - -static ma_bool32 ma_device_descriptor_is_valid(const ma_device_descriptor* pDeviceDescriptor) -{ - if (pDeviceDescriptor == NULL) { - return MA_FALSE; - } - - if (pDeviceDescriptor->format == ma_format_unknown) { - return MA_FALSE; - } - - if (pDeviceDescriptor->channels == 0 || pDeviceDescriptor->channels > MA_MAX_CHANNELS) { - return MA_FALSE; - } - - if (pDeviceDescriptor->sampleRate == 0) { - return MA_FALSE; - } - - return MA_TRUE; -} - - -static ma_result ma_device_audio_thread__default_read_write(ma_device* pDevice) -{ - ma_result result = MA_SUCCESS; - ma_bool32 exitLoop = MA_FALSE; - ma_uint8 capturedDeviceData[MA_DATA_CONVERTER_STACK_BUFFER_SIZE]; - ma_uint8 playbackDeviceData[MA_DATA_CONVERTER_STACK_BUFFER_SIZE]; - ma_uint32 capturedDeviceDataCapInFrames = 0; - ma_uint32 playbackDeviceDataCapInFrames = 0; - - MA_ASSERT(pDevice != NULL); - - /* Just some quick validation on the device type and the available callbacks. */ - if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex || pDevice->type == ma_device_type_loopback) { - if (pDevice->pContext->callbacks.onDeviceRead == NULL) { - return MA_NOT_IMPLEMENTED; - } - - capturedDeviceDataCapInFrames = sizeof(capturedDeviceData) / ma_get_bytes_per_frame(pDevice->capture.internalFormat, pDevice->capture.internalChannels); - } - - if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) { - if (pDevice->pContext->callbacks.onDeviceWrite == NULL) { - return MA_NOT_IMPLEMENTED; - } - - playbackDeviceDataCapInFrames = sizeof(playbackDeviceData) / ma_get_bytes_per_frame(pDevice->playback.internalFormat, pDevice->playback.internalChannels); - } - - /* NOTE: The device was started outside of this function, in the worker thread. */ - - while (ma_device_get_state(pDevice) == ma_device_state_started && !exitLoop) { - switch (pDevice->type) { - case ma_device_type_duplex: - { - /* The process is: onDeviceRead() -> convert -> callback -> convert -> onDeviceWrite() */ - ma_uint32 totalCapturedDeviceFramesProcessed = 0; - ma_uint32 capturedDevicePeriodSizeInFrames = ma_min(pDevice->capture.internalPeriodSizeInFrames, pDevice->playback.internalPeriodSizeInFrames); - - while (totalCapturedDeviceFramesProcessed < capturedDevicePeriodSizeInFrames) { - ma_uint32 capturedDeviceFramesRemaining; - ma_uint32 capturedDeviceFramesProcessed; - ma_uint32 capturedDeviceFramesToProcess; - ma_uint32 capturedDeviceFramesToTryProcessing = capturedDevicePeriodSizeInFrames - totalCapturedDeviceFramesProcessed; - if (capturedDeviceFramesToTryProcessing > capturedDeviceDataCapInFrames) { - capturedDeviceFramesToTryProcessing = capturedDeviceDataCapInFrames; - } - - result = pDevice->pContext->callbacks.onDeviceRead(pDevice, capturedDeviceData, capturedDeviceFramesToTryProcessing, &capturedDeviceFramesToProcess); - if (result != MA_SUCCESS) { - exitLoop = MA_TRUE; - break; - } - - capturedDeviceFramesRemaining = capturedDeviceFramesToProcess; - capturedDeviceFramesProcessed = 0; - - /* At this point we have our captured data in device format and we now need to convert it to client format. */ - for (;;) { - ma_uint8 capturedClientData[MA_DATA_CONVERTER_STACK_BUFFER_SIZE]; - ma_uint8 playbackClientData[MA_DATA_CONVERTER_STACK_BUFFER_SIZE]; - ma_uint32 capturedClientDataCapInFrames = sizeof(capturedClientData) / ma_get_bytes_per_frame(pDevice->capture.format, pDevice->capture.channels); - ma_uint32 playbackClientDataCapInFrames = sizeof(playbackClientData) / ma_get_bytes_per_frame(pDevice->playback.format, pDevice->playback.channels); - ma_uint64 capturedClientFramesToProcessThisIteration = ma_min(capturedClientDataCapInFrames, playbackClientDataCapInFrames); - ma_uint64 capturedDeviceFramesToProcessThisIteration = capturedDeviceFramesRemaining; - ma_uint8* pRunningCapturedDeviceFrames = ma_offset_ptr(capturedDeviceData, capturedDeviceFramesProcessed * ma_get_bytes_per_frame(pDevice->capture.internalFormat, pDevice->capture.internalChannels)); - - /* Convert capture data from device format to client format. */ - result = ma_data_converter_process_pcm_frames(&pDevice->capture.converter, pRunningCapturedDeviceFrames, &capturedDeviceFramesToProcessThisIteration, capturedClientData, &capturedClientFramesToProcessThisIteration); - if (result != MA_SUCCESS) { - break; - } - - /* - If we weren't able to generate any output frames it must mean we've exhausted all of our input. The only time this would not be the case is if capturedClientData was too small - which should never be the case when it's of the size MA_DATA_CONVERTER_STACK_BUFFER_SIZE. - */ - if (capturedClientFramesToProcessThisIteration == 0) { - break; - } - - ma_device__handle_data_callback(pDevice, playbackClientData, capturedClientData, (ma_uint32)capturedClientFramesToProcessThisIteration); /* Safe cast .*/ - - capturedDeviceFramesProcessed += (ma_uint32)capturedDeviceFramesToProcessThisIteration; /* Safe cast. */ - capturedDeviceFramesRemaining -= (ma_uint32)capturedDeviceFramesToProcessThisIteration; /* Safe cast. */ - - /* At this point the playbackClientData buffer should be holding data that needs to be written to the device. */ - for (;;) { - ma_uint64 convertedClientFrameCount = capturedClientFramesToProcessThisIteration; - ma_uint64 convertedDeviceFrameCount = playbackDeviceDataCapInFrames; - result = ma_data_converter_process_pcm_frames(&pDevice->playback.converter, playbackClientData, &convertedClientFrameCount, playbackDeviceData, &convertedDeviceFrameCount); - if (result != MA_SUCCESS) { - break; - } - - result = pDevice->pContext->callbacks.onDeviceWrite(pDevice, playbackDeviceData, (ma_uint32)convertedDeviceFrameCount, NULL); /* Safe cast. */ - if (result != MA_SUCCESS) { - exitLoop = MA_TRUE; - break; - } - - capturedClientFramesToProcessThisIteration -= (ma_uint32)convertedClientFrameCount; /* Safe cast. */ - if (capturedClientFramesToProcessThisIteration == 0) { - break; - } - } - - /* In case an error happened from ma_device_write__null()... */ - if (result != MA_SUCCESS) { - exitLoop = MA_TRUE; - break; - } - } - - /* Make sure we don't get stuck in the inner loop. */ - if (capturedDeviceFramesProcessed == 0) { - break; - } - - totalCapturedDeviceFramesProcessed += capturedDeviceFramesProcessed; - } - } break; - - case ma_device_type_capture: - case ma_device_type_loopback: - { - ma_uint32 periodSizeInFrames = pDevice->capture.internalPeriodSizeInFrames; - ma_uint32 framesReadThisPeriod = 0; - while (framesReadThisPeriod < periodSizeInFrames) { - ma_uint32 framesRemainingInPeriod = periodSizeInFrames - framesReadThisPeriod; - ma_uint32 framesProcessed; - ma_uint32 framesToReadThisIteration = framesRemainingInPeriod; - if (framesToReadThisIteration > capturedDeviceDataCapInFrames) { - framesToReadThisIteration = capturedDeviceDataCapInFrames; - } - - result = pDevice->pContext->callbacks.onDeviceRead(pDevice, capturedDeviceData, framesToReadThisIteration, &framesProcessed); - if (result != MA_SUCCESS) { - exitLoop = MA_TRUE; - break; - } - - /* Make sure we don't get stuck in the inner loop. */ - if (framesProcessed == 0) { - break; - } - - ma_device__send_frames_to_client(pDevice, framesProcessed, capturedDeviceData); - - framesReadThisPeriod += framesProcessed; - } - } break; - - case ma_device_type_playback: - { - /* We write in chunks of the period size, but use a stack allocated buffer for the intermediary. */ - ma_uint32 periodSizeInFrames = pDevice->playback.internalPeriodSizeInFrames; - ma_uint32 framesWrittenThisPeriod = 0; - while (framesWrittenThisPeriod < periodSizeInFrames) { - ma_uint32 framesRemainingInPeriod = periodSizeInFrames - framesWrittenThisPeriod; - ma_uint32 framesProcessed; - ma_uint32 framesToWriteThisIteration = framesRemainingInPeriod; - if (framesToWriteThisIteration > playbackDeviceDataCapInFrames) { - framesToWriteThisIteration = playbackDeviceDataCapInFrames; - } - - ma_device__read_frames_from_client(pDevice, framesToWriteThisIteration, playbackDeviceData); - - result = pDevice->pContext->callbacks.onDeviceWrite(pDevice, playbackDeviceData, framesToWriteThisIteration, &framesProcessed); - if (result != MA_SUCCESS) { - exitLoop = MA_TRUE; - break; - } - - /* Make sure we don't get stuck in the inner loop. */ - if (framesProcessed == 0) { - break; - } - - framesWrittenThisPeriod += framesProcessed; - } - } break; - - /* Should never get here. */ - default: break; - } - } - - return result; -} - - - -/******************************************************************************* - -Null Backend - -*******************************************************************************/ -#ifdef MA_HAS_NULL - -#define MA_DEVICE_OP_NONE__NULL 0 -#define MA_DEVICE_OP_START__NULL 1 -#define MA_DEVICE_OP_SUSPEND__NULL 2 -#define MA_DEVICE_OP_KILL__NULL 3 - -static ma_thread_result MA_THREADCALL ma_device_thread__null(void* pData) -{ - ma_device* pDevice = (ma_device*)pData; - MA_ASSERT(pDevice != NULL); - - for (;;) { /* Keep the thread alive until the device is uninitialized. */ - ma_uint32 operation; - - /* Wait for an operation to be requested. */ - ma_event_wait(&pDevice->null_device.operationEvent); - - /* At this point an event should have been triggered. */ - operation = pDevice->null_device.operation; - - /* Starting the device needs to put the thread into a loop. */ - if (operation == MA_DEVICE_OP_START__NULL) { - /* Reset the timer just in case. */ - ma_timer_init(&pDevice->null_device.timer); - - /* Getting here means a suspend or kill operation has been requested. */ - pDevice->null_device.operationResult = MA_SUCCESS; - ma_event_signal(&pDevice->null_device.operationCompletionEvent); - ma_semaphore_release(&pDevice->null_device.operationSemaphore); - continue; - } - - /* Suspending the device means we need to stop the timer and just continue the loop. */ - if (operation == MA_DEVICE_OP_SUSPEND__NULL) { - /* We need to add the current run time to the prior run time, then reset the timer. */ - pDevice->null_device.priorRunTime += ma_timer_get_time_in_seconds(&pDevice->null_device.timer); - ma_timer_init(&pDevice->null_device.timer); - - /* We're done. */ - pDevice->null_device.operationResult = MA_SUCCESS; - ma_event_signal(&pDevice->null_device.operationCompletionEvent); - ma_semaphore_release(&pDevice->null_device.operationSemaphore); - continue; - } - - /* Killing the device means we need to get out of this loop so that this thread can terminate. */ - if (operation == MA_DEVICE_OP_KILL__NULL) { - pDevice->null_device.operationResult = MA_SUCCESS; - ma_event_signal(&pDevice->null_device.operationCompletionEvent); - ma_semaphore_release(&pDevice->null_device.operationSemaphore); - break; - } - - /* Getting a signal on a "none" operation probably means an error. Return invalid operation. */ - if (operation == MA_DEVICE_OP_NONE__NULL) { - MA_ASSERT(MA_FALSE); /* <-- Trigger this in debug mode to ensure developers are aware they're doing something wrong (or there's a bug in a miniaudio). */ - pDevice->null_device.operationResult = MA_INVALID_OPERATION; - ma_event_signal(&pDevice->null_device.operationCompletionEvent); - ma_semaphore_release(&pDevice->null_device.operationSemaphore); - continue; /* Continue the loop. Don't terminate. */ - } - } - - return (ma_thread_result)0; -} - -static ma_result ma_device_do_operation__null(ma_device* pDevice, ma_uint32 operation) -{ - ma_result result; - - /* - TODO: Need to review this and consider just using mutual exclusion. I think the original motivation - for this was to just post the event to a queue and return immediately, but that has since changed - and now this function is synchronous. I think this can be simplified to just use a mutex. - */ - - /* - The first thing to do is wait for an operation slot to become available. We only have a single slot for this, but we could extend this later - to support queuing of operations. - */ - result = ma_semaphore_wait(&pDevice->null_device.operationSemaphore); - if (result != MA_SUCCESS) { - return result; /* Failed to wait for the event. */ - } - - /* - When we get here it means the background thread is not referencing the operation code and it can be changed. After changing this we need to - signal an event to the worker thread to let it know that it can start work. - */ - pDevice->null_device.operation = operation; - - /* Once the operation code has been set, the worker thread can start work. */ - if (ma_event_signal(&pDevice->null_device.operationEvent) != MA_SUCCESS) { - return MA_ERROR; - } - - /* We want everything to be synchronous so we're going to wait for the worker thread to complete it's operation. */ - if (ma_event_wait(&pDevice->null_device.operationCompletionEvent) != MA_SUCCESS) { - return MA_ERROR; - } - - return pDevice->null_device.operationResult; -} - -static ma_uint64 ma_device_get_total_run_time_in_frames__null(ma_device* pDevice) -{ - ma_uint32 internalSampleRate; - if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) { - internalSampleRate = pDevice->capture.internalSampleRate; - } else { - internalSampleRate = pDevice->playback.internalSampleRate; - } - - return (ma_uint64)((pDevice->null_device.priorRunTime + ma_timer_get_time_in_seconds(&pDevice->null_device.timer)) * internalSampleRate); -} - -static ma_result ma_context_enumerate_devices__null(ma_context* pContext, ma_enum_devices_callback_proc callback, void* pUserData) -{ - ma_bool32 cbResult = MA_TRUE; - - MA_ASSERT(pContext != NULL); - MA_ASSERT(callback != NULL); - - /* Playback. */ - if (cbResult) { - ma_device_info deviceInfo; - MA_ZERO_OBJECT(&deviceInfo); - ma_strncpy_s(deviceInfo.name, sizeof(deviceInfo.name), "NULL Playback Device", (size_t)-1); - deviceInfo.isDefault = MA_TRUE; /* Only one playback and capture device for the null backend, so might as well mark as default. */ - cbResult = callback(pContext, ma_device_type_playback, &deviceInfo, pUserData); - } - - /* Capture. */ - if (cbResult) { - ma_device_info deviceInfo; - MA_ZERO_OBJECT(&deviceInfo); - ma_strncpy_s(deviceInfo.name, sizeof(deviceInfo.name), "NULL Capture Device", (size_t)-1); - deviceInfo.isDefault = MA_TRUE; /* Only one playback and capture device for the null backend, so might as well mark as default. */ - cbResult = callback(pContext, ma_device_type_capture, &deviceInfo, pUserData); - } - - (void)cbResult; /* Silence a static analysis warning. */ - - return MA_SUCCESS; -} - -static ma_result ma_context_get_device_info__null(ma_context* pContext, ma_device_type deviceType, const ma_device_id* pDeviceID, ma_device_info* pDeviceInfo) -{ - MA_ASSERT(pContext != NULL); - - if (pDeviceID != NULL && pDeviceID->nullbackend != 0) { - return MA_NO_DEVICE; /* Don't know the device. */ - } - - /* Name / Description */ - if (deviceType == ma_device_type_playback) { - ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), "NULL Playback Device", (size_t)-1); - } else { - ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), "NULL Capture Device", (size_t)-1); - } - - pDeviceInfo->isDefault = MA_TRUE; /* Only one playback and capture device for the null backend, so might as well mark as default. */ - - /* Support everything on the null backend. */ - pDeviceInfo->nativeDataFormats[0].format = ma_format_unknown; - pDeviceInfo->nativeDataFormats[0].channels = 0; - pDeviceInfo->nativeDataFormats[0].sampleRate = 0; - pDeviceInfo->nativeDataFormats[0].flags = 0; - pDeviceInfo->nativeDataFormatCount = 1; - - (void)pContext; - return MA_SUCCESS; -} - - -static ma_result ma_device_uninit__null(ma_device* pDevice) -{ - MA_ASSERT(pDevice != NULL); - - /* Keep it clean and wait for the device thread to finish before returning. */ - ma_device_do_operation__null(pDevice, MA_DEVICE_OP_KILL__NULL); - - /* Wait for the thread to finish before continuing. */ - ma_thread_wait(&pDevice->null_device.deviceThread); - - /* At this point the loop in the device thread is as good as terminated so we can uninitialize our events. */ - ma_semaphore_uninit(&pDevice->null_device.operationSemaphore); - ma_event_uninit(&pDevice->null_device.operationCompletionEvent); - ma_event_uninit(&pDevice->null_device.operationEvent); - - return MA_SUCCESS; -} - -static ma_result ma_device_init__null(ma_device* pDevice, const ma_device_config* pConfig, ma_device_descriptor* pDescriptorPlayback, ma_device_descriptor* pDescriptorCapture) -{ - ma_result result; - - MA_ASSERT(pDevice != NULL); - - MA_ZERO_OBJECT(&pDevice->null_device); - - if (pConfig->deviceType == ma_device_type_loopback) { - return MA_DEVICE_TYPE_NOT_SUPPORTED; - } - - /* The null backend supports everything exactly as we specify it. */ - if (pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) { - pDescriptorCapture->format = (pDescriptorCapture->format != ma_format_unknown) ? pDescriptorCapture->format : MA_DEFAULT_FORMAT; - pDescriptorCapture->channels = (pDescriptorCapture->channels != 0) ? pDescriptorCapture->channels : MA_DEFAULT_CHANNELS; - pDescriptorCapture->sampleRate = (pDescriptorCapture->sampleRate != 0) ? pDescriptorCapture->sampleRate : MA_DEFAULT_SAMPLE_RATE; - - if (pDescriptorCapture->channelMap[0] == MA_CHANNEL_NONE) { - ma_channel_map_init_standard(ma_standard_channel_map_default, pDescriptorCapture->channelMap, ma_countof(pDescriptorCapture->channelMap), pDescriptorCapture->channels); - } - - pDescriptorCapture->periodSizeInFrames = ma_calculate_buffer_size_in_frames_from_descriptor(pDescriptorCapture, pDescriptorCapture->sampleRate, pConfig->performanceProfile); - } - - if (pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) { - pDescriptorPlayback->format = (pDescriptorPlayback->format != ma_format_unknown) ? pDescriptorPlayback->format : MA_DEFAULT_FORMAT; - pDescriptorPlayback->channels = (pDescriptorPlayback->channels != 0) ? pDescriptorPlayback->channels : MA_DEFAULT_CHANNELS; - pDescriptorPlayback->sampleRate = (pDescriptorPlayback->sampleRate != 0) ? pDescriptorPlayback->sampleRate : MA_DEFAULT_SAMPLE_RATE; - - if (pDescriptorPlayback->channelMap[0] == MA_CHANNEL_NONE) { - ma_channel_map_init_standard(ma_standard_channel_map_default, pDescriptorPlayback->channelMap, ma_countof(pDescriptorCapture->channelMap), pDescriptorPlayback->channels); - } - - pDescriptorPlayback->periodSizeInFrames = ma_calculate_buffer_size_in_frames_from_descriptor(pDescriptorPlayback, pDescriptorPlayback->sampleRate, pConfig->performanceProfile); - } - - /* - In order to get timing right, we need to create a thread that does nothing but keeps track of the timer. This timer is started when the - first period is "written" to it, and then stopped in ma_device_stop__null(). - */ - result = ma_event_init(&pDevice->null_device.operationEvent); - if (result != MA_SUCCESS) { - return result; - } - - result = ma_event_init(&pDevice->null_device.operationCompletionEvent); - if (result != MA_SUCCESS) { - return result; - } - - result = ma_semaphore_init(1, &pDevice->null_device.operationSemaphore); /* <-- It's important that the initial value is set to 1. */ - if (result != MA_SUCCESS) { - return result; - } - - result = ma_thread_create(&pDevice->null_device.deviceThread, pDevice->pContext->threadPriority, 0, ma_device_thread__null, pDevice, &pDevice->pContext->allocationCallbacks); - if (result != MA_SUCCESS) { - return result; - } - - return MA_SUCCESS; -} - -static ma_result ma_device_start__null(ma_device* pDevice) -{ - MA_ASSERT(pDevice != NULL); - - ma_device_do_operation__null(pDevice, MA_DEVICE_OP_START__NULL); - - ma_atomic_bool32_set(&pDevice->null_device.isStarted, MA_TRUE); - return MA_SUCCESS; -} - -static ma_result ma_device_stop__null(ma_device* pDevice) -{ - MA_ASSERT(pDevice != NULL); - - ma_device_do_operation__null(pDevice, MA_DEVICE_OP_SUSPEND__NULL); - - ma_atomic_bool32_set(&pDevice->null_device.isStarted, MA_FALSE); - return MA_SUCCESS; -} - -static ma_bool32 ma_device_is_started__null(ma_device* pDevice) -{ - MA_ASSERT(pDevice != NULL); - - return ma_atomic_bool32_get(&pDevice->null_device.isStarted); -} - -static ma_result ma_device_write__null(ma_device* pDevice, const void* pPCMFrames, ma_uint32 frameCount, ma_uint32* pFramesWritten) -{ - ma_result result = MA_SUCCESS; - ma_uint32 totalPCMFramesProcessed; - ma_bool32 wasStartedOnEntry; - - if (pFramesWritten != NULL) { - *pFramesWritten = 0; - } - - wasStartedOnEntry = ma_device_is_started__null(pDevice); - - /* Keep going until everything has been read. */ - totalPCMFramesProcessed = 0; - while (totalPCMFramesProcessed < frameCount) { - ma_uint64 targetFrame; - - /* If there are any frames remaining in the current period, consume those first. */ - if (pDevice->null_device.currentPeriodFramesRemainingPlayback > 0) { - ma_uint32 framesRemaining = (frameCount - totalPCMFramesProcessed); - ma_uint32 framesToProcess = pDevice->null_device.currentPeriodFramesRemainingPlayback; - if (framesToProcess > framesRemaining) { - framesToProcess = framesRemaining; - } - - /* We don't actually do anything with pPCMFrames, so just mark it as unused to prevent a warning. */ - (void)pPCMFrames; - - pDevice->null_device.currentPeriodFramesRemainingPlayback -= framesToProcess; - totalPCMFramesProcessed += framesToProcess; - } - - /* If we've consumed the current period we'll need to mark it as such an ensure the device is started if it's not already. */ - if (pDevice->null_device.currentPeriodFramesRemainingPlayback == 0) { - pDevice->null_device.currentPeriodFramesRemainingPlayback = 0; - - if (!ma_device_is_started__null(pDevice) && !wasStartedOnEntry) { - result = ma_device_start__null(pDevice); - if (result != MA_SUCCESS) { - break; - } - } - } - - /* If we've consumed the whole buffer we can return now. */ - MA_ASSERT(totalPCMFramesProcessed <= frameCount); - if (totalPCMFramesProcessed == frameCount) { - break; - } - - /* Getting here means we've still got more frames to consume, we but need to wait for it to become available. */ - targetFrame = pDevice->null_device.lastProcessedFramePlayback; - for (;;) { - ma_uint64 currentFrame; - - /* Stop waiting if the device has been stopped. */ - if (!ma_device_is_started__null(pDevice)) { - break; - } - - currentFrame = ma_device_get_total_run_time_in_frames__null(pDevice); - if (currentFrame >= targetFrame) { - break; - } - - /* Getting here means we haven't yet reached the target sample, so continue waiting. */ - ma_sleep(10); - } - - pDevice->null_device.lastProcessedFramePlayback += pDevice->playback.internalPeriodSizeInFrames; - pDevice->null_device.currentPeriodFramesRemainingPlayback = pDevice->playback.internalPeriodSizeInFrames; - } - - if (pFramesWritten != NULL) { - *pFramesWritten = totalPCMFramesProcessed; - } - - return result; -} - -static ma_result ma_device_read__null(ma_device* pDevice, void* pPCMFrames, ma_uint32 frameCount, ma_uint32* pFramesRead) -{ - ma_result result = MA_SUCCESS; - ma_uint32 totalPCMFramesProcessed; - - if (pFramesRead != NULL) { - *pFramesRead = 0; - } - - /* Keep going until everything has been read. */ - totalPCMFramesProcessed = 0; - while (totalPCMFramesProcessed < frameCount) { - ma_uint64 targetFrame; - - /* If there are any frames remaining in the current period, consume those first. */ - if (pDevice->null_device.currentPeriodFramesRemainingCapture > 0) { - ma_uint32 bpf = ma_get_bytes_per_frame(pDevice->capture.internalFormat, pDevice->capture.internalChannels); - ma_uint32 framesRemaining = (frameCount - totalPCMFramesProcessed); - ma_uint32 framesToProcess = pDevice->null_device.currentPeriodFramesRemainingCapture; - if (framesToProcess > framesRemaining) { - framesToProcess = framesRemaining; - } - - /* We need to ensure the output buffer is zeroed. */ - MA_ZERO_MEMORY(ma_offset_ptr(pPCMFrames, totalPCMFramesProcessed*bpf), framesToProcess*bpf); - - pDevice->null_device.currentPeriodFramesRemainingCapture -= framesToProcess; - totalPCMFramesProcessed += framesToProcess; - } - - /* If we've consumed the current period we'll need to mark it as such an ensure the device is started if it's not already. */ - if (pDevice->null_device.currentPeriodFramesRemainingCapture == 0) { - pDevice->null_device.currentPeriodFramesRemainingCapture = 0; - } - - /* If we've consumed the whole buffer we can return now. */ - MA_ASSERT(totalPCMFramesProcessed <= frameCount); - if (totalPCMFramesProcessed == frameCount) { - break; - } - - /* Getting here means we've still got more frames to consume, we but need to wait for it to become available. */ - targetFrame = pDevice->null_device.lastProcessedFrameCapture + pDevice->capture.internalPeriodSizeInFrames; - for (;;) { - ma_uint64 currentFrame; - - /* Stop waiting if the device has been stopped. */ - if (!ma_device_is_started__null(pDevice)) { - break; - } - - currentFrame = ma_device_get_total_run_time_in_frames__null(pDevice); - if (currentFrame >= targetFrame) { - break; - } - - /* Getting here means we haven't yet reached the target sample, so continue waiting. */ - ma_sleep(10); - } - - pDevice->null_device.lastProcessedFrameCapture += pDevice->capture.internalPeriodSizeInFrames; - pDevice->null_device.currentPeriodFramesRemainingCapture = pDevice->capture.internalPeriodSizeInFrames; - } - - if (pFramesRead != NULL) { - *pFramesRead = totalPCMFramesProcessed; - } - - return result; -} - -static ma_result ma_context_uninit__null(ma_context* pContext) -{ - MA_ASSERT(pContext != NULL); - MA_ASSERT(pContext->backend == ma_backend_null); - - (void)pContext; - return MA_SUCCESS; -} - -static ma_result ma_context_init__null(ma_context* pContext, const ma_context_config* pConfig, ma_backend_callbacks* pCallbacks) -{ - MA_ASSERT(pContext != NULL); - - (void)pConfig; - (void)pContext; - - pCallbacks->onContextInit = ma_context_init__null; - pCallbacks->onContextUninit = ma_context_uninit__null; - pCallbacks->onContextEnumerateDevices = ma_context_enumerate_devices__null; - pCallbacks->onContextGetDeviceInfo = ma_context_get_device_info__null; - pCallbacks->onDeviceInit = ma_device_init__null; - pCallbacks->onDeviceUninit = ma_device_uninit__null; - pCallbacks->onDeviceStart = ma_device_start__null; - pCallbacks->onDeviceStop = ma_device_stop__null; - pCallbacks->onDeviceRead = ma_device_read__null; - pCallbacks->onDeviceWrite = ma_device_write__null; - pCallbacks->onDeviceDataLoop = NULL; /* Our backend is asynchronous with a blocking read-write API which means we can get miniaudio to deal with the audio thread. */ - - /* The null backend always works. */ - return MA_SUCCESS; -} -#endif - - - -/******************************************************************************* - -WIN32 COMMON - -*******************************************************************************/ -#if defined(MA_WIN32) -#if defined(MA_WIN32_DESKTOP) || defined(MA_WIN32_GDK) - #define ma_CoInitializeEx(pContext, pvReserved, dwCoInit) ((pContext->win32.CoInitializeEx) ? ((MA_PFN_CoInitializeEx)pContext->win32.CoInitializeEx)(pvReserved, dwCoInit) : ((MA_PFN_CoInitialize)pContext->win32.CoInitialize)(pvReserved)) - #define ma_CoUninitialize(pContext) ((MA_PFN_CoUninitialize)pContext->win32.CoUninitialize)() - #define ma_CoCreateInstance(pContext, rclsid, pUnkOuter, dwClsContext, riid, ppv) ((MA_PFN_CoCreateInstance)pContext->win32.CoCreateInstance)(rclsid, pUnkOuter, dwClsContext, riid, ppv) - #define ma_CoTaskMemFree(pContext, pv) ((MA_PFN_CoTaskMemFree)pContext->win32.CoTaskMemFree)(pv) - #define ma_PropVariantClear(pContext, pvar) ((MA_PFN_PropVariantClear)pContext->win32.PropVariantClear)(pvar) -#else - #define ma_CoInitializeEx(pContext, pvReserved, dwCoInit) CoInitializeEx(pvReserved, dwCoInit) - #define ma_CoUninitialize(pContext) CoUninitialize() - #define ma_CoCreateInstance(pContext, rclsid, pUnkOuter, dwClsContext, riid, ppv) CoCreateInstance(rclsid, pUnkOuter, dwClsContext, riid, ppv) - #define ma_CoTaskMemFree(pContext, pv) CoTaskMemFree(pv) - #define ma_PropVariantClear(pContext, pvar) PropVariantClear(pvar) -#endif - -#if !defined(MAXULONG_PTR) && !defined(__WATCOMC__) -typedef size_t DWORD_PTR; -#endif - -#if !defined(WAVE_FORMAT_1M08) -#define WAVE_FORMAT_1M08 0x00000001 -#define WAVE_FORMAT_1S08 0x00000002 -#define WAVE_FORMAT_1M16 0x00000004 -#define WAVE_FORMAT_1S16 0x00000008 -#define WAVE_FORMAT_2M08 0x00000010 -#define WAVE_FORMAT_2S08 0x00000020 -#define WAVE_FORMAT_2M16 0x00000040 -#define WAVE_FORMAT_2S16 0x00000080 -#define WAVE_FORMAT_4M08 0x00000100 -#define WAVE_FORMAT_4S08 0x00000200 -#define WAVE_FORMAT_4M16 0x00000400 -#define WAVE_FORMAT_4S16 0x00000800 -#endif - -#if !defined(WAVE_FORMAT_44M08) -#define WAVE_FORMAT_44M08 0x00000100 -#define WAVE_FORMAT_44S08 0x00000200 -#define WAVE_FORMAT_44M16 0x00000400 -#define WAVE_FORMAT_44S16 0x00000800 -#define WAVE_FORMAT_48M08 0x00001000 -#define WAVE_FORMAT_48S08 0x00002000 -#define WAVE_FORMAT_48M16 0x00004000 -#define WAVE_FORMAT_48S16 0x00008000 -#define WAVE_FORMAT_96M08 0x00010000 -#define WAVE_FORMAT_96S08 0x00020000 -#define WAVE_FORMAT_96M16 0x00040000 -#define WAVE_FORMAT_96S16 0x00080000 -#endif - -#ifndef SPEAKER_FRONT_LEFT -#define SPEAKER_FRONT_LEFT 0x1 -#define SPEAKER_FRONT_RIGHT 0x2 -#define SPEAKER_FRONT_CENTER 0x4 -#define SPEAKER_LOW_FREQUENCY 0x8 -#define SPEAKER_BACK_LEFT 0x10 -#define SPEAKER_BACK_RIGHT 0x20 -#define SPEAKER_FRONT_LEFT_OF_CENTER 0x40 -#define SPEAKER_FRONT_RIGHT_OF_CENTER 0x80 -#define SPEAKER_BACK_CENTER 0x100 -#define SPEAKER_SIDE_LEFT 0x200 -#define SPEAKER_SIDE_RIGHT 0x400 -#define SPEAKER_TOP_CENTER 0x800 -#define SPEAKER_TOP_FRONT_LEFT 0x1000 -#define SPEAKER_TOP_FRONT_CENTER 0x2000 -#define SPEAKER_TOP_FRONT_RIGHT 0x4000 -#define SPEAKER_TOP_BACK_LEFT 0x8000 -#define SPEAKER_TOP_BACK_CENTER 0x10000 -#define SPEAKER_TOP_BACK_RIGHT 0x20000 -#endif - -/* -Implement our own version of MA_WAVEFORMATEXTENSIBLE so we can avoid a header. Be careful with this -because MA_WAVEFORMATEX has an extra two bytes over standard WAVEFORMATEX due to padding. The -standard version uses tight packing, but for compiler compatibility we're not doing that with ours. -*/ -typedef struct -{ - WORD wFormatTag; - WORD nChannels; - DWORD nSamplesPerSec; - DWORD nAvgBytesPerSec; - WORD nBlockAlign; - WORD wBitsPerSample; - WORD cbSize; -} MA_WAVEFORMATEX; - -typedef struct -{ - WORD wFormatTag; - WORD nChannels; - DWORD nSamplesPerSec; - DWORD nAvgBytesPerSec; - WORD nBlockAlign; - WORD wBitsPerSample; - WORD cbSize; - union - { - WORD wValidBitsPerSample; - WORD wSamplesPerBlock; - WORD wReserved; - } Samples; - DWORD dwChannelMask; - GUID SubFormat; -} MA_WAVEFORMATEXTENSIBLE; - - - -#ifndef WAVE_FORMAT_EXTENSIBLE -#define WAVE_FORMAT_EXTENSIBLE 0xFFFE -#endif - -#ifndef WAVE_FORMAT_PCM -#define WAVE_FORMAT_PCM 1 -#endif - -#ifndef WAVE_FORMAT_IEEE_FLOAT -#define WAVE_FORMAT_IEEE_FLOAT 0x0003 -#endif - -/* Converts an individual Win32-style channel identifier (SPEAKER_FRONT_LEFT, etc.) to miniaudio. */ -static ma_uint8 ma_channel_id_to_ma__win32(DWORD id) -{ - switch (id) - { - case SPEAKER_FRONT_LEFT: return MA_CHANNEL_FRONT_LEFT; - case SPEAKER_FRONT_RIGHT: return MA_CHANNEL_FRONT_RIGHT; - case SPEAKER_FRONT_CENTER: return MA_CHANNEL_FRONT_CENTER; - case SPEAKER_LOW_FREQUENCY: return MA_CHANNEL_LFE; - case SPEAKER_BACK_LEFT: return MA_CHANNEL_BACK_LEFT; - case SPEAKER_BACK_RIGHT: return MA_CHANNEL_BACK_RIGHT; - case SPEAKER_FRONT_LEFT_OF_CENTER: return MA_CHANNEL_FRONT_LEFT_CENTER; - case SPEAKER_FRONT_RIGHT_OF_CENTER: return MA_CHANNEL_FRONT_RIGHT_CENTER; - case SPEAKER_BACK_CENTER: return MA_CHANNEL_BACK_CENTER; - case SPEAKER_SIDE_LEFT: return MA_CHANNEL_SIDE_LEFT; - case SPEAKER_SIDE_RIGHT: return MA_CHANNEL_SIDE_RIGHT; - case SPEAKER_TOP_CENTER: return MA_CHANNEL_TOP_CENTER; - case SPEAKER_TOP_FRONT_LEFT: return MA_CHANNEL_TOP_FRONT_LEFT; - case SPEAKER_TOP_FRONT_CENTER: return MA_CHANNEL_TOP_FRONT_CENTER; - case SPEAKER_TOP_FRONT_RIGHT: return MA_CHANNEL_TOP_FRONT_RIGHT; - case SPEAKER_TOP_BACK_LEFT: return MA_CHANNEL_TOP_BACK_LEFT; - case SPEAKER_TOP_BACK_CENTER: return MA_CHANNEL_TOP_BACK_CENTER; - case SPEAKER_TOP_BACK_RIGHT: return MA_CHANNEL_TOP_BACK_RIGHT; - default: return 0; - } -} - -/* Converts an individual miniaudio channel identifier (MA_CHANNEL_FRONT_LEFT, etc.) to Win32-style. */ -static DWORD ma_channel_id_to_win32(DWORD id) -{ - switch (id) - { - case MA_CHANNEL_MONO: return SPEAKER_FRONT_CENTER; - case MA_CHANNEL_FRONT_LEFT: return SPEAKER_FRONT_LEFT; - case MA_CHANNEL_FRONT_RIGHT: return SPEAKER_FRONT_RIGHT; - case MA_CHANNEL_FRONT_CENTER: return SPEAKER_FRONT_CENTER; - case MA_CHANNEL_LFE: return SPEAKER_LOW_FREQUENCY; - case MA_CHANNEL_BACK_LEFT: return SPEAKER_BACK_LEFT; - case MA_CHANNEL_BACK_RIGHT: return SPEAKER_BACK_RIGHT; - case MA_CHANNEL_FRONT_LEFT_CENTER: return SPEAKER_FRONT_LEFT_OF_CENTER; - case MA_CHANNEL_FRONT_RIGHT_CENTER: return SPEAKER_FRONT_RIGHT_OF_CENTER; - case MA_CHANNEL_BACK_CENTER: return SPEAKER_BACK_CENTER; - case MA_CHANNEL_SIDE_LEFT: return SPEAKER_SIDE_LEFT; - case MA_CHANNEL_SIDE_RIGHT: return SPEAKER_SIDE_RIGHT; - case MA_CHANNEL_TOP_CENTER: return SPEAKER_TOP_CENTER; - case MA_CHANNEL_TOP_FRONT_LEFT: return SPEAKER_TOP_FRONT_LEFT; - case MA_CHANNEL_TOP_FRONT_CENTER: return SPEAKER_TOP_FRONT_CENTER; - case MA_CHANNEL_TOP_FRONT_RIGHT: return SPEAKER_TOP_FRONT_RIGHT; - case MA_CHANNEL_TOP_BACK_LEFT: return SPEAKER_TOP_BACK_LEFT; - case MA_CHANNEL_TOP_BACK_CENTER: return SPEAKER_TOP_BACK_CENTER; - case MA_CHANNEL_TOP_BACK_RIGHT: return SPEAKER_TOP_BACK_RIGHT; - default: return 0; - } -} - -/* Converts a channel mapping to a Win32-style channel mask. */ -static DWORD ma_channel_map_to_channel_mask__win32(const ma_channel* pChannelMap, ma_uint32 channels) -{ - DWORD dwChannelMask = 0; - ma_uint32 iChannel; - - for (iChannel = 0; iChannel < channels; ++iChannel) { - dwChannelMask |= ma_channel_id_to_win32(pChannelMap[iChannel]); - } - - return dwChannelMask; -} - -/* Converts a Win32-style channel mask to a miniaudio channel map. */ -static void ma_channel_mask_to_channel_map__win32(DWORD dwChannelMask, ma_uint32 channels, ma_channel* pChannelMap) -{ - /* If the channel mask is set to 0, just assume a default Win32 channel map. */ - if (dwChannelMask == 0) { - ma_channel_map_init_standard(ma_standard_channel_map_microsoft, pChannelMap, channels, channels); - } else { - if (channels == 1 && (dwChannelMask & SPEAKER_FRONT_CENTER) != 0) { - pChannelMap[0] = MA_CHANNEL_MONO; - } else { - /* Just iterate over each bit. */ - ma_uint32 iChannel = 0; - ma_uint32 iBit; - - for (iBit = 0; iBit < 32 && iChannel < channels; ++iBit) { - DWORD bitValue = (dwChannelMask & (1UL << iBit)); - if (bitValue != 0) { - /* The bit is set. */ - pChannelMap[iChannel] = ma_channel_id_to_ma__win32(bitValue); - iChannel += 1; - } - } - } - } -} - -#ifdef __cplusplus -static ma_bool32 ma_is_guid_equal(const void* a, const void* b) -{ - return IsEqualGUID(*(const GUID*)a, *(const GUID*)b); -} -#else -#define ma_is_guid_equal(a, b) IsEqualGUID((const GUID*)a, (const GUID*)b) -#endif - -static MA_INLINE ma_bool32 ma_is_guid_null(const void* guid) -{ - static GUID nullguid = {0x00000000, 0x0000, 0x0000, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}; - return ma_is_guid_equal(guid, &nullguid); -} - -static ma_format ma_format_from_WAVEFORMATEX(const MA_WAVEFORMATEX* pWF) -{ - MA_ASSERT(pWF != NULL); - - if (pWF->wFormatTag == WAVE_FORMAT_EXTENSIBLE) { - const MA_WAVEFORMATEXTENSIBLE* pWFEX = (const MA_WAVEFORMATEXTENSIBLE*)pWF; - if (ma_is_guid_equal(&pWFEX->SubFormat, &MA_GUID_KSDATAFORMAT_SUBTYPE_PCM)) { - if (pWFEX->Samples.wValidBitsPerSample == 32) { - return ma_format_s32; - } - if (pWFEX->Samples.wValidBitsPerSample == 24) { - if (pWFEX->wBitsPerSample == 32) { - return ma_format_s32; - } - if (pWFEX->wBitsPerSample == 24) { - return ma_format_s24; - } - } - if (pWFEX->Samples.wValidBitsPerSample == 16) { - return ma_format_s16; - } - if (pWFEX->Samples.wValidBitsPerSample == 8) { - return ma_format_u8; - } - } - if (ma_is_guid_equal(&pWFEX->SubFormat, &MA_GUID_KSDATAFORMAT_SUBTYPE_IEEE_FLOAT)) { - if (pWFEX->Samples.wValidBitsPerSample == 32) { - return ma_format_f32; - } - /* - if (pWFEX->Samples.wValidBitsPerSample == 64) { - return ma_format_f64; - } - */ - } - } else { - if (pWF->wFormatTag == WAVE_FORMAT_PCM) { - if (pWF->wBitsPerSample == 32) { - return ma_format_s32; - } - if (pWF->wBitsPerSample == 24) { - return ma_format_s24; - } - if (pWF->wBitsPerSample == 16) { - return ma_format_s16; - } - if (pWF->wBitsPerSample == 8) { - return ma_format_u8; - } - } - if (pWF->wFormatTag == WAVE_FORMAT_IEEE_FLOAT) { - if (pWF->wBitsPerSample == 32) { - return ma_format_f32; - } - if (pWF->wBitsPerSample == 64) { - /*return ma_format_f64;*/ - } - } - } - - return ma_format_unknown; -} -#endif - - -/******************************************************************************* - -WASAPI Backend - -*******************************************************************************/ -#ifdef MA_HAS_WASAPI -#if 0 -#if defined(_MSC_VER) - #pragma warning(push) - #pragma warning(disable:4091) /* 'typedef ': ignored on left of '' when no variable is declared */ -#endif -#include -#include -#if defined(_MSC_VER) - #pragma warning(pop) -#endif -#endif /* 0 */ - -static ma_result ma_device_reroute__wasapi(ma_device* pDevice, ma_device_type deviceType); - -/* Some compilers don't define VerifyVersionInfoW. Need to write this ourselves. */ -#define MA_WIN32_WINNT_VISTA 0x0600 -#define MA_VER_MINORVERSION 0x01 -#define MA_VER_MAJORVERSION 0x02 -#define MA_VER_SERVICEPACKMAJOR 0x20 -#define MA_VER_GREATER_EQUAL 0x03 - -typedef struct { - DWORD dwOSVersionInfoSize; - DWORD dwMajorVersion; - DWORD dwMinorVersion; - DWORD dwBuildNumber; - DWORD dwPlatformId; - WCHAR szCSDVersion[128]; - WORD wServicePackMajor; - WORD wServicePackMinor; - WORD wSuiteMask; - BYTE wProductType; - BYTE wReserved; -} ma_OSVERSIONINFOEXW; - -typedef BOOL (WINAPI * ma_PFNVerifyVersionInfoW) (ma_OSVERSIONINFOEXW* lpVersionInfo, DWORD dwTypeMask, DWORDLONG dwlConditionMask); -typedef ULONGLONG (WINAPI * ma_PFNVerSetConditionMask)(ULONGLONG dwlConditionMask, DWORD dwTypeBitMask, BYTE dwConditionMask); - - -#ifndef PROPERTYKEY_DEFINED -#define PROPERTYKEY_DEFINED -#ifndef __WATCOMC__ -typedef struct -{ - GUID fmtid; - DWORD pid; -} PROPERTYKEY; -#endif -#endif - -/* Some compilers don't define PropVariantInit(). We just do this ourselves since it's just a memset(). */ -static MA_INLINE void ma_PropVariantInit(MA_PROPVARIANT* pProp) -{ - MA_ZERO_OBJECT(pProp); -} - - -static const PROPERTYKEY MA_PKEY_Device_FriendlyName = {{0xA45C254E, 0xDF1C, 0x4EFD, {0x80, 0x20, 0x67, 0xD1, 0x46, 0xA8, 0x50, 0xE0}}, 14}; -static const PROPERTYKEY MA_PKEY_AudioEngine_DeviceFormat = {{0xF19F064D, 0x82C, 0x4E27, {0xBC, 0x73, 0x68, 0x82, 0xA1, 0xBB, 0x8E, 0x4C}}, 0}; - -static const IID MA_IID_IUnknown = {0x00000000, 0x0000, 0x0000, {0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46}}; /* 00000000-0000-0000-C000-000000000046 */ -#if !defined(MA_WIN32_DESKTOP) && !defined(MA_WIN32_GDK) -static const IID MA_IID_IAgileObject = {0x94EA2B94, 0xE9CC, 0x49E0, {0xC0, 0xFF, 0xEE, 0x64, 0xCA, 0x8F, 0x5B, 0x90}}; /* 94EA2B94-E9CC-49E0-C0FF-EE64CA8F5B90 */ -#endif - -static const IID MA_IID_IAudioClient = {0x1CB9AD4C, 0xDBFA, 0x4C32, {0xB1, 0x78, 0xC2, 0xF5, 0x68, 0xA7, 0x03, 0xB2}}; /* 1CB9AD4C-DBFA-4C32-B178-C2F568A703B2 = __uuidof(IAudioClient) */ -static const IID MA_IID_IAudioClient2 = {0x726778CD, 0xF60A, 0x4EDA, {0x82, 0xDE, 0xE4, 0x76, 0x10, 0xCD, 0x78, 0xAA}}; /* 726778CD-F60A-4EDA-82DE-E47610CD78AA = __uuidof(IAudioClient2) */ -static const IID MA_IID_IAudioClient3 = {0x7ED4EE07, 0x8E67, 0x4CD4, {0x8C, 0x1A, 0x2B, 0x7A, 0x59, 0x87, 0xAD, 0x42}}; /* 7ED4EE07-8E67-4CD4-8C1A-2B7A5987AD42 = __uuidof(IAudioClient3) */ -static const IID MA_IID_IAudioRenderClient = {0xF294ACFC, 0x3146, 0x4483, {0xA7, 0xBF, 0xAD, 0xDC, 0xA7, 0xC2, 0x60, 0xE2}}; /* F294ACFC-3146-4483-A7BF-ADDCA7C260E2 = __uuidof(IAudioRenderClient) */ -static const IID MA_IID_IAudioCaptureClient = {0xC8ADBD64, 0xE71E, 0x48A0, {0xA4, 0xDE, 0x18, 0x5C, 0x39, 0x5C, 0xD3, 0x17}}; /* C8ADBD64-E71E-48A0-A4DE-185C395CD317 = __uuidof(IAudioCaptureClient) */ -static const IID MA_IID_IMMNotificationClient = {0x7991EEC9, 0x7E89, 0x4D85, {0x83, 0x90, 0x6C, 0x70, 0x3C, 0xEC, 0x60, 0xC0}}; /* 7991EEC9-7E89-4D85-8390-6C703CEC60C0 = __uuidof(IMMNotificationClient) */ -#if !defined(MA_WIN32_DESKTOP) && !defined(MA_WIN32_GDK) -static const IID MA_IID_DEVINTERFACE_AUDIO_RENDER = {0xE6327CAD, 0xDCEC, 0x4949, {0xAE, 0x8A, 0x99, 0x1E, 0x97, 0x6A, 0x79, 0xD2}}; /* E6327CAD-DCEC-4949-AE8A-991E976A79D2 */ -static const IID MA_IID_DEVINTERFACE_AUDIO_CAPTURE = {0x2EEF81BE, 0x33FA, 0x4800, {0x96, 0x70, 0x1C, 0xD4, 0x74, 0x97, 0x2C, 0x3F}}; /* 2EEF81BE-33FA-4800-9670-1CD474972C3F */ -static const IID MA_IID_IActivateAudioInterfaceCompletionHandler = {0x41D949AB, 0x9862, 0x444A, {0x80, 0xF6, 0xC2, 0x61, 0x33, 0x4D, 0xA5, 0xEB}}; /* 41D949AB-9862-444A-80F6-C261334DA5EB */ -#endif - -static const IID MA_CLSID_MMDeviceEnumerator = {0xBCDE0395, 0xE52F, 0x467C, {0x8E, 0x3D, 0xC4, 0x57, 0x92, 0x91, 0x69, 0x2E}}; /* BCDE0395-E52F-467C-8E3D-C4579291692E = __uuidof(MMDeviceEnumerator) */ -static const IID MA_IID_IMMDeviceEnumerator = {0xA95664D2, 0x9614, 0x4F35, {0xA7, 0x46, 0xDE, 0x8D, 0xB6, 0x36, 0x17, 0xE6}}; /* A95664D2-9614-4F35-A746-DE8DB63617E6 = __uuidof(IMMDeviceEnumerator) */ - -#if defined(MA_WIN32_DESKTOP) || defined(MA_WIN32_GDK) -#define MA_MM_DEVICE_STATE_ACTIVE 1 -#define MA_MM_DEVICE_STATE_DISABLED 2 -#define MA_MM_DEVICE_STATE_NOTPRESENT 4 -#define MA_MM_DEVICE_STATE_UNPLUGGED 8 - -typedef struct ma_IMMDeviceEnumerator ma_IMMDeviceEnumerator; -typedef struct ma_IMMDeviceCollection ma_IMMDeviceCollection; -typedef struct ma_IMMDevice ma_IMMDevice; -#else -typedef struct ma_IActivateAudioInterfaceCompletionHandler ma_IActivateAudioInterfaceCompletionHandler; -typedef struct ma_IActivateAudioInterfaceAsyncOperation ma_IActivateAudioInterfaceAsyncOperation; -#endif -typedef struct ma_IPropertyStore ma_IPropertyStore; -typedef struct ma_IAudioClient ma_IAudioClient; -typedef struct ma_IAudioClient2 ma_IAudioClient2; -typedef struct ma_IAudioClient3 ma_IAudioClient3; -typedef struct ma_IAudioRenderClient ma_IAudioRenderClient; -typedef struct ma_IAudioCaptureClient ma_IAudioCaptureClient; - -typedef ma_int64 MA_REFERENCE_TIME; - -#define MA_AUDCLNT_STREAMFLAGS_CROSSPROCESS 0x00010000 -#define MA_AUDCLNT_STREAMFLAGS_LOOPBACK 0x00020000 -#define MA_AUDCLNT_STREAMFLAGS_EVENTCALLBACK 0x00040000 -#define MA_AUDCLNT_STREAMFLAGS_NOPERSIST 0x00080000 -#define MA_AUDCLNT_STREAMFLAGS_RATEADJUST 0x00100000 -#define MA_AUDCLNT_STREAMFLAGS_SRC_DEFAULT_QUALITY 0x08000000 -#define MA_AUDCLNT_STREAMFLAGS_AUTOCONVERTPCM 0x80000000 -#define MA_AUDCLNT_SESSIONFLAGS_EXPIREWHENUNOWNED 0x10000000 -#define MA_AUDCLNT_SESSIONFLAGS_DISPLAY_HIDE 0x20000000 -#define MA_AUDCLNT_SESSIONFLAGS_DISPLAY_HIDEWHENEXPIRED 0x40000000 - -/* Buffer flags. */ -#define MA_AUDCLNT_BUFFERFLAGS_DATA_DISCONTINUITY 1 -#define MA_AUDCLNT_BUFFERFLAGS_SILENT 2 -#define MA_AUDCLNT_BUFFERFLAGS_TIMESTAMP_ERROR 4 - -typedef enum -{ - ma_eRender = 0, - ma_eCapture = 1, - ma_eAll = 2 -} ma_EDataFlow; - -typedef enum -{ - ma_eConsole = 0, - ma_eMultimedia = 1, - ma_eCommunications = 2 -} ma_ERole; - -typedef enum -{ - MA_AUDCLNT_SHAREMODE_SHARED, - MA_AUDCLNT_SHAREMODE_EXCLUSIVE -} MA_AUDCLNT_SHAREMODE; - -typedef enum -{ - MA_AudioCategory_Other = 0 /* <-- miniaudio is only caring about Other. */ -} MA_AUDIO_STREAM_CATEGORY; - -typedef struct -{ - ma_uint32 cbSize; - BOOL bIsOffload; - MA_AUDIO_STREAM_CATEGORY eCategory; -} ma_AudioClientProperties; - -/* IUnknown */ -typedef struct -{ - /* IUnknown */ - HRESULT (STDMETHODCALLTYPE * QueryInterface)(ma_IUnknown* pThis, const IID* const riid, void** ppObject); - ULONG (STDMETHODCALLTYPE * AddRef) (ma_IUnknown* pThis); - ULONG (STDMETHODCALLTYPE * Release) (ma_IUnknown* pThis); -} ma_IUnknownVtbl; -struct ma_IUnknown -{ - ma_IUnknownVtbl* lpVtbl; -}; -static MA_INLINE HRESULT ma_IUnknown_QueryInterface(ma_IUnknown* pThis, const IID* const riid, void** ppObject) { return pThis->lpVtbl->QueryInterface(pThis, riid, ppObject); } -static MA_INLINE ULONG ma_IUnknown_AddRef(ma_IUnknown* pThis) { return pThis->lpVtbl->AddRef(pThis); } -static MA_INLINE ULONG ma_IUnknown_Release(ma_IUnknown* pThis) { return pThis->lpVtbl->Release(pThis); } - -#if defined(MA_WIN32_DESKTOP) || defined(MA_WIN32_GDK) - /* IMMNotificationClient */ - typedef struct - { - /* IUnknown */ - HRESULT (STDMETHODCALLTYPE * QueryInterface)(ma_IMMNotificationClient* pThis, const IID* const riid, void** ppObject); - ULONG (STDMETHODCALLTYPE * AddRef) (ma_IMMNotificationClient* pThis); - ULONG (STDMETHODCALLTYPE * Release) (ma_IMMNotificationClient* pThis); - - /* IMMNotificationClient */ - HRESULT (STDMETHODCALLTYPE * OnDeviceStateChanged) (ma_IMMNotificationClient* pThis, const WCHAR* pDeviceID, DWORD dwNewState); - HRESULT (STDMETHODCALLTYPE * OnDeviceAdded) (ma_IMMNotificationClient* pThis, const WCHAR* pDeviceID); - HRESULT (STDMETHODCALLTYPE * OnDeviceRemoved) (ma_IMMNotificationClient* pThis, const WCHAR* pDeviceID); - HRESULT (STDMETHODCALLTYPE * OnDefaultDeviceChanged)(ma_IMMNotificationClient* pThis, ma_EDataFlow dataFlow, ma_ERole role, const WCHAR* pDefaultDeviceID); - HRESULT (STDMETHODCALLTYPE * OnPropertyValueChanged)(ma_IMMNotificationClient* pThis, const WCHAR* pDeviceID, const PROPERTYKEY key); - } ma_IMMNotificationClientVtbl; - - /* IMMDeviceEnumerator */ - typedef struct - { - /* IUnknown */ - HRESULT (STDMETHODCALLTYPE * QueryInterface)(ma_IMMDeviceEnumerator* pThis, const IID* const riid, void** ppObject); - ULONG (STDMETHODCALLTYPE * AddRef) (ma_IMMDeviceEnumerator* pThis); - ULONG (STDMETHODCALLTYPE * Release) (ma_IMMDeviceEnumerator* pThis); - - /* IMMDeviceEnumerator */ - HRESULT (STDMETHODCALLTYPE * EnumAudioEndpoints) (ma_IMMDeviceEnumerator* pThis, ma_EDataFlow dataFlow, DWORD dwStateMask, ma_IMMDeviceCollection** ppDevices); - HRESULT (STDMETHODCALLTYPE * GetDefaultAudioEndpoint) (ma_IMMDeviceEnumerator* pThis, ma_EDataFlow dataFlow, ma_ERole role, ma_IMMDevice** ppEndpoint); - HRESULT (STDMETHODCALLTYPE * GetDevice) (ma_IMMDeviceEnumerator* pThis, const WCHAR* pID, ma_IMMDevice** ppDevice); - HRESULT (STDMETHODCALLTYPE * RegisterEndpointNotificationCallback) (ma_IMMDeviceEnumerator* pThis, ma_IMMNotificationClient* pClient); - HRESULT (STDMETHODCALLTYPE * UnregisterEndpointNotificationCallback)(ma_IMMDeviceEnumerator* pThis, ma_IMMNotificationClient* pClient); - } ma_IMMDeviceEnumeratorVtbl; - struct ma_IMMDeviceEnumerator - { - ma_IMMDeviceEnumeratorVtbl* lpVtbl; - }; - static MA_INLINE HRESULT ma_IMMDeviceEnumerator_QueryInterface(ma_IMMDeviceEnumerator* pThis, const IID* const riid, void** ppObject) { return pThis->lpVtbl->QueryInterface(pThis, riid, ppObject); } - static MA_INLINE ULONG ma_IMMDeviceEnumerator_AddRef(ma_IMMDeviceEnumerator* pThis) { return pThis->lpVtbl->AddRef(pThis); } - static MA_INLINE ULONG ma_IMMDeviceEnumerator_Release(ma_IMMDeviceEnumerator* pThis) { return pThis->lpVtbl->Release(pThis); } - static MA_INLINE HRESULT ma_IMMDeviceEnumerator_EnumAudioEndpoints(ma_IMMDeviceEnumerator* pThis, ma_EDataFlow dataFlow, DWORD dwStateMask, ma_IMMDeviceCollection** ppDevices) { return pThis->lpVtbl->EnumAudioEndpoints(pThis, dataFlow, dwStateMask, ppDevices); } - static MA_INLINE HRESULT ma_IMMDeviceEnumerator_GetDefaultAudioEndpoint(ma_IMMDeviceEnumerator* pThis, ma_EDataFlow dataFlow, ma_ERole role, ma_IMMDevice** ppEndpoint) { return pThis->lpVtbl->GetDefaultAudioEndpoint(pThis, dataFlow, role, ppEndpoint); } - static MA_INLINE HRESULT ma_IMMDeviceEnumerator_GetDevice(ma_IMMDeviceEnumerator* pThis, const WCHAR* pID, ma_IMMDevice** ppDevice) { return pThis->lpVtbl->GetDevice(pThis, pID, ppDevice); } - static MA_INLINE HRESULT ma_IMMDeviceEnumerator_RegisterEndpointNotificationCallback(ma_IMMDeviceEnumerator* pThis, ma_IMMNotificationClient* pClient) { return pThis->lpVtbl->RegisterEndpointNotificationCallback(pThis, pClient); } - static MA_INLINE HRESULT ma_IMMDeviceEnumerator_UnregisterEndpointNotificationCallback(ma_IMMDeviceEnumerator* pThis, ma_IMMNotificationClient* pClient) { return pThis->lpVtbl->UnregisterEndpointNotificationCallback(pThis, pClient); } - - - /* IMMDeviceCollection */ - typedef struct - { - /* IUnknown */ - HRESULT (STDMETHODCALLTYPE * QueryInterface)(ma_IMMDeviceCollection* pThis, const IID* const riid, void** ppObject); - ULONG (STDMETHODCALLTYPE * AddRef) (ma_IMMDeviceCollection* pThis); - ULONG (STDMETHODCALLTYPE * Release) (ma_IMMDeviceCollection* pThis); - - /* IMMDeviceCollection */ - HRESULT (STDMETHODCALLTYPE * GetCount)(ma_IMMDeviceCollection* pThis, UINT* pDevices); - HRESULT (STDMETHODCALLTYPE * Item) (ma_IMMDeviceCollection* pThis, UINT nDevice, ma_IMMDevice** ppDevice); - } ma_IMMDeviceCollectionVtbl; - struct ma_IMMDeviceCollection - { - ma_IMMDeviceCollectionVtbl* lpVtbl; - }; - static MA_INLINE HRESULT ma_IMMDeviceCollection_QueryInterface(ma_IMMDeviceCollection* pThis, const IID* const riid, void** ppObject) { return pThis->lpVtbl->QueryInterface(pThis, riid, ppObject); } - static MA_INLINE ULONG ma_IMMDeviceCollection_AddRef(ma_IMMDeviceCollection* pThis) { return pThis->lpVtbl->AddRef(pThis); } - static MA_INLINE ULONG ma_IMMDeviceCollection_Release(ma_IMMDeviceCollection* pThis) { return pThis->lpVtbl->Release(pThis); } - static MA_INLINE HRESULT ma_IMMDeviceCollection_GetCount(ma_IMMDeviceCollection* pThis, UINT* pDevices) { return pThis->lpVtbl->GetCount(pThis, pDevices); } - static MA_INLINE HRESULT ma_IMMDeviceCollection_Item(ma_IMMDeviceCollection* pThis, UINT nDevice, ma_IMMDevice** ppDevice) { return pThis->lpVtbl->Item(pThis, nDevice, ppDevice); } - - - /* IMMDevice */ - typedef struct - { - /* IUnknown */ - HRESULT (STDMETHODCALLTYPE * QueryInterface)(ma_IMMDevice* pThis, const IID* const riid, void** ppObject); - ULONG (STDMETHODCALLTYPE * AddRef) (ma_IMMDevice* pThis); - ULONG (STDMETHODCALLTYPE * Release) (ma_IMMDevice* pThis); - - /* IMMDevice */ - HRESULT (STDMETHODCALLTYPE * Activate) (ma_IMMDevice* pThis, const IID* const iid, DWORD dwClsCtx, MA_PROPVARIANT* pActivationParams, void** ppInterface); - HRESULT (STDMETHODCALLTYPE * OpenPropertyStore)(ma_IMMDevice* pThis, DWORD stgmAccess, ma_IPropertyStore** ppProperties); - HRESULT (STDMETHODCALLTYPE * GetId) (ma_IMMDevice* pThis, WCHAR** pID); - HRESULT (STDMETHODCALLTYPE * GetState) (ma_IMMDevice* pThis, DWORD *pState); - } ma_IMMDeviceVtbl; - struct ma_IMMDevice - { - ma_IMMDeviceVtbl* lpVtbl; - }; - static MA_INLINE HRESULT ma_IMMDevice_QueryInterface(ma_IMMDevice* pThis, const IID* const riid, void** ppObject) { return pThis->lpVtbl->QueryInterface(pThis, riid, ppObject); } - static MA_INLINE ULONG ma_IMMDevice_AddRef(ma_IMMDevice* pThis) { return pThis->lpVtbl->AddRef(pThis); } - static MA_INLINE ULONG ma_IMMDevice_Release(ma_IMMDevice* pThis) { return pThis->lpVtbl->Release(pThis); } - static MA_INLINE HRESULT ma_IMMDevice_Activate(ma_IMMDevice* pThis, const IID* const iid, DWORD dwClsCtx, MA_PROPVARIANT* pActivationParams, void** ppInterface) { return pThis->lpVtbl->Activate(pThis, iid, dwClsCtx, pActivationParams, ppInterface); } - static MA_INLINE HRESULT ma_IMMDevice_OpenPropertyStore(ma_IMMDevice* pThis, DWORD stgmAccess, ma_IPropertyStore** ppProperties) { return pThis->lpVtbl->OpenPropertyStore(pThis, stgmAccess, ppProperties); } - static MA_INLINE HRESULT ma_IMMDevice_GetId(ma_IMMDevice* pThis, WCHAR** pID) { return pThis->lpVtbl->GetId(pThis, pID); } - static MA_INLINE HRESULT ma_IMMDevice_GetState(ma_IMMDevice* pThis, DWORD *pState) { return pThis->lpVtbl->GetState(pThis, pState); } -#else - /* IActivateAudioInterfaceAsyncOperation */ - typedef struct - { - /* IUnknown */ - HRESULT (STDMETHODCALLTYPE * QueryInterface)(ma_IActivateAudioInterfaceAsyncOperation* pThis, const IID* const riid, void** ppObject); - ULONG (STDMETHODCALLTYPE * AddRef) (ma_IActivateAudioInterfaceAsyncOperation* pThis); - ULONG (STDMETHODCALLTYPE * Release) (ma_IActivateAudioInterfaceAsyncOperation* pThis); - - /* IActivateAudioInterfaceAsyncOperation */ - HRESULT (STDMETHODCALLTYPE * GetActivateResult)(ma_IActivateAudioInterfaceAsyncOperation* pThis, HRESULT *pActivateResult, ma_IUnknown** ppActivatedInterface); - } ma_IActivateAudioInterfaceAsyncOperationVtbl; - struct ma_IActivateAudioInterfaceAsyncOperation - { - ma_IActivateAudioInterfaceAsyncOperationVtbl* lpVtbl; - }; - static MA_INLINE HRESULT ma_IActivateAudioInterfaceAsyncOperation_QueryInterface(ma_IActivateAudioInterfaceAsyncOperation* pThis, const IID* const riid, void** ppObject) { return pThis->lpVtbl->QueryInterface(pThis, riid, ppObject); } - static MA_INLINE ULONG ma_IActivateAudioInterfaceAsyncOperation_AddRef(ma_IActivateAudioInterfaceAsyncOperation* pThis) { return pThis->lpVtbl->AddRef(pThis); } - static MA_INLINE ULONG ma_IActivateAudioInterfaceAsyncOperation_Release(ma_IActivateAudioInterfaceAsyncOperation* pThis) { return pThis->lpVtbl->Release(pThis); } - static MA_INLINE HRESULT ma_IActivateAudioInterfaceAsyncOperation_GetActivateResult(ma_IActivateAudioInterfaceAsyncOperation* pThis, HRESULT *pActivateResult, ma_IUnknown** ppActivatedInterface) { return pThis->lpVtbl->GetActivateResult(pThis, pActivateResult, ppActivatedInterface); } -#endif - -/* IPropertyStore */ -typedef struct -{ - /* IUnknown */ - HRESULT (STDMETHODCALLTYPE * QueryInterface)(ma_IPropertyStore* pThis, const IID* const riid, void** ppObject); - ULONG (STDMETHODCALLTYPE * AddRef) (ma_IPropertyStore* pThis); - ULONG (STDMETHODCALLTYPE * Release) (ma_IPropertyStore* pThis); - - /* IPropertyStore */ - HRESULT (STDMETHODCALLTYPE * GetCount)(ma_IPropertyStore* pThis, DWORD* pPropCount); - HRESULT (STDMETHODCALLTYPE * GetAt) (ma_IPropertyStore* pThis, DWORD propIndex, PROPERTYKEY* pPropKey); - HRESULT (STDMETHODCALLTYPE * GetValue)(ma_IPropertyStore* pThis, const PROPERTYKEY* const pKey, MA_PROPVARIANT* pPropVar); - HRESULT (STDMETHODCALLTYPE * SetValue)(ma_IPropertyStore* pThis, const PROPERTYKEY* const pKey, const MA_PROPVARIANT* const pPropVar); - HRESULT (STDMETHODCALLTYPE * Commit) (ma_IPropertyStore* pThis); -} ma_IPropertyStoreVtbl; -struct ma_IPropertyStore -{ - ma_IPropertyStoreVtbl* lpVtbl; -}; -static MA_INLINE HRESULT ma_IPropertyStore_QueryInterface(ma_IPropertyStore* pThis, const IID* const riid, void** ppObject) { return pThis->lpVtbl->QueryInterface(pThis, riid, ppObject); } -static MA_INLINE ULONG ma_IPropertyStore_AddRef(ma_IPropertyStore* pThis) { return pThis->lpVtbl->AddRef(pThis); } -static MA_INLINE ULONG ma_IPropertyStore_Release(ma_IPropertyStore* pThis) { return pThis->lpVtbl->Release(pThis); } -static MA_INLINE HRESULT ma_IPropertyStore_GetCount(ma_IPropertyStore* pThis, DWORD* pPropCount) { return pThis->lpVtbl->GetCount(pThis, pPropCount); } -static MA_INLINE HRESULT ma_IPropertyStore_GetAt(ma_IPropertyStore* pThis, DWORD propIndex, PROPERTYKEY* pPropKey) { return pThis->lpVtbl->GetAt(pThis, propIndex, pPropKey); } -static MA_INLINE HRESULT ma_IPropertyStore_GetValue(ma_IPropertyStore* pThis, const PROPERTYKEY* const pKey, MA_PROPVARIANT* pPropVar) { return pThis->lpVtbl->GetValue(pThis, pKey, pPropVar); } -static MA_INLINE HRESULT ma_IPropertyStore_SetValue(ma_IPropertyStore* pThis, const PROPERTYKEY* const pKey, const MA_PROPVARIANT* const pPropVar) { return pThis->lpVtbl->SetValue(pThis, pKey, pPropVar); } -static MA_INLINE HRESULT ma_IPropertyStore_Commit(ma_IPropertyStore* pThis) { return pThis->lpVtbl->Commit(pThis); } - - -/* IAudioClient */ -typedef struct -{ - /* IUnknown */ - HRESULT (STDMETHODCALLTYPE * QueryInterface)(ma_IAudioClient* pThis, const IID* const riid, void** ppObject); - ULONG (STDMETHODCALLTYPE * AddRef) (ma_IAudioClient* pThis); - ULONG (STDMETHODCALLTYPE * Release) (ma_IAudioClient* pThis); - - /* IAudioClient */ - HRESULT (STDMETHODCALLTYPE * Initialize) (ma_IAudioClient* pThis, MA_AUDCLNT_SHAREMODE shareMode, DWORD streamFlags, MA_REFERENCE_TIME bufferDuration, MA_REFERENCE_TIME periodicity, const MA_WAVEFORMATEX* pFormat, const GUID* pAudioSessionGuid); - HRESULT (STDMETHODCALLTYPE * GetBufferSize) (ma_IAudioClient* pThis, ma_uint32* pNumBufferFrames); - HRESULT (STDMETHODCALLTYPE * GetStreamLatency) (ma_IAudioClient* pThis, MA_REFERENCE_TIME* pLatency); - HRESULT (STDMETHODCALLTYPE * GetCurrentPadding)(ma_IAudioClient* pThis, ma_uint32* pNumPaddingFrames); - HRESULT (STDMETHODCALLTYPE * IsFormatSupported)(ma_IAudioClient* pThis, MA_AUDCLNT_SHAREMODE shareMode, const MA_WAVEFORMATEX* pFormat, MA_WAVEFORMATEX** ppClosestMatch); - HRESULT (STDMETHODCALLTYPE * GetMixFormat) (ma_IAudioClient* pThis, MA_WAVEFORMATEX** ppDeviceFormat); - HRESULT (STDMETHODCALLTYPE * GetDevicePeriod) (ma_IAudioClient* pThis, MA_REFERENCE_TIME* pDefaultDevicePeriod, MA_REFERENCE_TIME* pMinimumDevicePeriod); - HRESULT (STDMETHODCALLTYPE * Start) (ma_IAudioClient* pThis); - HRESULT (STDMETHODCALLTYPE * Stop) (ma_IAudioClient* pThis); - HRESULT (STDMETHODCALLTYPE * Reset) (ma_IAudioClient* pThis); - HRESULT (STDMETHODCALLTYPE * SetEventHandle) (ma_IAudioClient* pThis, HANDLE eventHandle); - HRESULT (STDMETHODCALLTYPE * GetService) (ma_IAudioClient* pThis, const IID* const riid, void** pp); -} ma_IAudioClientVtbl; -struct ma_IAudioClient -{ - ma_IAudioClientVtbl* lpVtbl; -}; -static MA_INLINE HRESULT ma_IAudioClient_QueryInterface(ma_IAudioClient* pThis, const IID* const riid, void** ppObject) { return pThis->lpVtbl->QueryInterface(pThis, riid, ppObject); } -static MA_INLINE ULONG ma_IAudioClient_AddRef(ma_IAudioClient* pThis) { return pThis->lpVtbl->AddRef(pThis); } -static MA_INLINE ULONG ma_IAudioClient_Release(ma_IAudioClient* pThis) { return pThis->lpVtbl->Release(pThis); } -static MA_INLINE HRESULT ma_IAudioClient_Initialize(ma_IAudioClient* pThis, MA_AUDCLNT_SHAREMODE shareMode, DWORD streamFlags, MA_REFERENCE_TIME bufferDuration, MA_REFERENCE_TIME periodicity, const MA_WAVEFORMATEX* pFormat, const GUID* pAudioSessionGuid) { return pThis->lpVtbl->Initialize(pThis, shareMode, streamFlags, bufferDuration, periodicity, pFormat, pAudioSessionGuid); } -static MA_INLINE HRESULT ma_IAudioClient_GetBufferSize(ma_IAudioClient* pThis, ma_uint32* pNumBufferFrames) { return pThis->lpVtbl->GetBufferSize(pThis, pNumBufferFrames); } -static MA_INLINE HRESULT ma_IAudioClient_GetStreamLatency(ma_IAudioClient* pThis, MA_REFERENCE_TIME* pLatency) { return pThis->lpVtbl->GetStreamLatency(pThis, pLatency); } -static MA_INLINE HRESULT ma_IAudioClient_GetCurrentPadding(ma_IAudioClient* pThis, ma_uint32* pNumPaddingFrames) { return pThis->lpVtbl->GetCurrentPadding(pThis, pNumPaddingFrames); } -static MA_INLINE HRESULT ma_IAudioClient_IsFormatSupported(ma_IAudioClient* pThis, MA_AUDCLNT_SHAREMODE shareMode, const MA_WAVEFORMATEX* pFormat, MA_WAVEFORMATEX** ppClosestMatch) { return pThis->lpVtbl->IsFormatSupported(pThis, shareMode, pFormat, ppClosestMatch); } -static MA_INLINE HRESULT ma_IAudioClient_GetMixFormat(ma_IAudioClient* pThis, MA_WAVEFORMATEX** ppDeviceFormat) { return pThis->lpVtbl->GetMixFormat(pThis, ppDeviceFormat); } -static MA_INLINE HRESULT ma_IAudioClient_GetDevicePeriod(ma_IAudioClient* pThis, MA_REFERENCE_TIME* pDefaultDevicePeriod, MA_REFERENCE_TIME* pMinimumDevicePeriod) { return pThis->lpVtbl->GetDevicePeriod(pThis, pDefaultDevicePeriod, pMinimumDevicePeriod); } -static MA_INLINE HRESULT ma_IAudioClient_Start(ma_IAudioClient* pThis) { return pThis->lpVtbl->Start(pThis); } -static MA_INLINE HRESULT ma_IAudioClient_Stop(ma_IAudioClient* pThis) { return pThis->lpVtbl->Stop(pThis); } -static MA_INLINE HRESULT ma_IAudioClient_Reset(ma_IAudioClient* pThis) { return pThis->lpVtbl->Reset(pThis); } -static MA_INLINE HRESULT ma_IAudioClient_SetEventHandle(ma_IAudioClient* pThis, HANDLE eventHandle) { return pThis->lpVtbl->SetEventHandle(pThis, eventHandle); } -static MA_INLINE HRESULT ma_IAudioClient_GetService(ma_IAudioClient* pThis, const IID* const riid, void** pp) { return pThis->lpVtbl->GetService(pThis, riid, pp); } - -/* IAudioClient2 */ -typedef struct -{ - /* IUnknown */ - HRESULT (STDMETHODCALLTYPE * QueryInterface)(ma_IAudioClient2* pThis, const IID* const riid, void** ppObject); - ULONG (STDMETHODCALLTYPE * AddRef) (ma_IAudioClient2* pThis); - ULONG (STDMETHODCALLTYPE * Release) (ma_IAudioClient2* pThis); - - /* IAudioClient */ - HRESULT (STDMETHODCALLTYPE * Initialize) (ma_IAudioClient2* pThis, MA_AUDCLNT_SHAREMODE shareMode, DWORD streamFlags, MA_REFERENCE_TIME bufferDuration, MA_REFERENCE_TIME periodicity, const MA_WAVEFORMATEX* pFormat, const GUID* pAudioSessionGuid); - HRESULT (STDMETHODCALLTYPE * GetBufferSize) (ma_IAudioClient2* pThis, ma_uint32* pNumBufferFrames); - HRESULT (STDMETHODCALLTYPE * GetStreamLatency) (ma_IAudioClient2* pThis, MA_REFERENCE_TIME* pLatency); - HRESULT (STDMETHODCALLTYPE * GetCurrentPadding)(ma_IAudioClient2* pThis, ma_uint32* pNumPaddingFrames); - HRESULT (STDMETHODCALLTYPE * IsFormatSupported)(ma_IAudioClient2* pThis, MA_AUDCLNT_SHAREMODE shareMode, const MA_WAVEFORMATEX* pFormat, MA_WAVEFORMATEX** ppClosestMatch); - HRESULT (STDMETHODCALLTYPE * GetMixFormat) (ma_IAudioClient2* pThis, MA_WAVEFORMATEX** ppDeviceFormat); - HRESULT (STDMETHODCALLTYPE * GetDevicePeriod) (ma_IAudioClient2* pThis, MA_REFERENCE_TIME* pDefaultDevicePeriod, MA_REFERENCE_TIME* pMinimumDevicePeriod); - HRESULT (STDMETHODCALLTYPE * Start) (ma_IAudioClient2* pThis); - HRESULT (STDMETHODCALLTYPE * Stop) (ma_IAudioClient2* pThis); - HRESULT (STDMETHODCALLTYPE * Reset) (ma_IAudioClient2* pThis); - HRESULT (STDMETHODCALLTYPE * SetEventHandle) (ma_IAudioClient2* pThis, HANDLE eventHandle); - HRESULT (STDMETHODCALLTYPE * GetService) (ma_IAudioClient2* pThis, const IID* const riid, void** pp); - - /* IAudioClient2 */ - HRESULT (STDMETHODCALLTYPE * IsOffloadCapable) (ma_IAudioClient2* pThis, MA_AUDIO_STREAM_CATEGORY category, BOOL* pOffloadCapable); - HRESULT (STDMETHODCALLTYPE * SetClientProperties)(ma_IAudioClient2* pThis, const ma_AudioClientProperties* pProperties); - HRESULT (STDMETHODCALLTYPE * GetBufferSizeLimits)(ma_IAudioClient2* pThis, const MA_WAVEFORMATEX* pFormat, BOOL eventDriven, MA_REFERENCE_TIME* pMinBufferDuration, MA_REFERENCE_TIME* pMaxBufferDuration); -} ma_IAudioClient2Vtbl; -struct ma_IAudioClient2 -{ - ma_IAudioClient2Vtbl* lpVtbl; -}; -static MA_INLINE HRESULT ma_IAudioClient2_QueryInterface(ma_IAudioClient2* pThis, const IID* const riid, void** ppObject) { return pThis->lpVtbl->QueryInterface(pThis, riid, ppObject); } -static MA_INLINE ULONG ma_IAudioClient2_AddRef(ma_IAudioClient2* pThis) { return pThis->lpVtbl->AddRef(pThis); } -static MA_INLINE ULONG ma_IAudioClient2_Release(ma_IAudioClient2* pThis) { return pThis->lpVtbl->Release(pThis); } -static MA_INLINE HRESULT ma_IAudioClient2_Initialize(ma_IAudioClient2* pThis, MA_AUDCLNT_SHAREMODE shareMode, DWORD streamFlags, MA_REFERENCE_TIME bufferDuration, MA_REFERENCE_TIME periodicity, const MA_WAVEFORMATEX* pFormat, const GUID* pAudioSessionGuid) { return pThis->lpVtbl->Initialize(pThis, shareMode, streamFlags, bufferDuration, periodicity, pFormat, pAudioSessionGuid); } -static MA_INLINE HRESULT ma_IAudioClient2_GetBufferSize(ma_IAudioClient2* pThis, ma_uint32* pNumBufferFrames) { return pThis->lpVtbl->GetBufferSize(pThis, pNumBufferFrames); } -static MA_INLINE HRESULT ma_IAudioClient2_GetStreamLatency(ma_IAudioClient2* pThis, MA_REFERENCE_TIME* pLatency) { return pThis->lpVtbl->GetStreamLatency(pThis, pLatency); } -static MA_INLINE HRESULT ma_IAudioClient2_GetCurrentPadding(ma_IAudioClient2* pThis, ma_uint32* pNumPaddingFrames) { return pThis->lpVtbl->GetCurrentPadding(pThis, pNumPaddingFrames); } -static MA_INLINE HRESULT ma_IAudioClient2_IsFormatSupported(ma_IAudioClient2* pThis, MA_AUDCLNT_SHAREMODE shareMode, const MA_WAVEFORMATEX* pFormat, MA_WAVEFORMATEX** ppClosestMatch) { return pThis->lpVtbl->IsFormatSupported(pThis, shareMode, pFormat, ppClosestMatch); } -static MA_INLINE HRESULT ma_IAudioClient2_GetMixFormat(ma_IAudioClient2* pThis, MA_WAVEFORMATEX** ppDeviceFormat) { return pThis->lpVtbl->GetMixFormat(pThis, ppDeviceFormat); } -static MA_INLINE HRESULT ma_IAudioClient2_GetDevicePeriod(ma_IAudioClient2* pThis, MA_REFERENCE_TIME* pDefaultDevicePeriod, MA_REFERENCE_TIME* pMinimumDevicePeriod) { return pThis->lpVtbl->GetDevicePeriod(pThis, pDefaultDevicePeriod, pMinimumDevicePeriod); } -static MA_INLINE HRESULT ma_IAudioClient2_Start(ma_IAudioClient2* pThis) { return pThis->lpVtbl->Start(pThis); } -static MA_INLINE HRESULT ma_IAudioClient2_Stop(ma_IAudioClient2* pThis) { return pThis->lpVtbl->Stop(pThis); } -static MA_INLINE HRESULT ma_IAudioClient2_Reset(ma_IAudioClient2* pThis) { return pThis->lpVtbl->Reset(pThis); } -static MA_INLINE HRESULT ma_IAudioClient2_SetEventHandle(ma_IAudioClient2* pThis, HANDLE eventHandle) { return pThis->lpVtbl->SetEventHandle(pThis, eventHandle); } -static MA_INLINE HRESULT ma_IAudioClient2_GetService(ma_IAudioClient2* pThis, const IID* const riid, void** pp) { return pThis->lpVtbl->GetService(pThis, riid, pp); } -static MA_INLINE HRESULT ma_IAudioClient2_IsOffloadCapable(ma_IAudioClient2* pThis, MA_AUDIO_STREAM_CATEGORY category, BOOL* pOffloadCapable) { return pThis->lpVtbl->IsOffloadCapable(pThis, category, pOffloadCapable); } -static MA_INLINE HRESULT ma_IAudioClient2_SetClientProperties(ma_IAudioClient2* pThis, const ma_AudioClientProperties* pProperties) { return pThis->lpVtbl->SetClientProperties(pThis, pProperties); } -static MA_INLINE HRESULT ma_IAudioClient2_GetBufferSizeLimits(ma_IAudioClient2* pThis, const MA_WAVEFORMATEX* pFormat, BOOL eventDriven, MA_REFERENCE_TIME* pMinBufferDuration, MA_REFERENCE_TIME* pMaxBufferDuration) { return pThis->lpVtbl->GetBufferSizeLimits(pThis, pFormat, eventDriven, pMinBufferDuration, pMaxBufferDuration); } - - -/* IAudioClient3 */ -typedef struct -{ - /* IUnknown */ - HRESULT (STDMETHODCALLTYPE * QueryInterface)(ma_IAudioClient3* pThis, const IID* const riid, void** ppObject); - ULONG (STDMETHODCALLTYPE * AddRef) (ma_IAudioClient3* pThis); - ULONG (STDMETHODCALLTYPE * Release) (ma_IAudioClient3* pThis); - - /* IAudioClient */ - HRESULT (STDMETHODCALLTYPE * Initialize) (ma_IAudioClient3* pThis, MA_AUDCLNT_SHAREMODE shareMode, DWORD streamFlags, MA_REFERENCE_TIME bufferDuration, MA_REFERENCE_TIME periodicity, const MA_WAVEFORMATEX* pFormat, const GUID* pAudioSessionGuid); - HRESULT (STDMETHODCALLTYPE * GetBufferSize) (ma_IAudioClient3* pThis, ma_uint32* pNumBufferFrames); - HRESULT (STDMETHODCALLTYPE * GetStreamLatency) (ma_IAudioClient3* pThis, MA_REFERENCE_TIME* pLatency); - HRESULT (STDMETHODCALLTYPE * GetCurrentPadding)(ma_IAudioClient3* pThis, ma_uint32* pNumPaddingFrames); - HRESULT (STDMETHODCALLTYPE * IsFormatSupported)(ma_IAudioClient3* pThis, MA_AUDCLNT_SHAREMODE shareMode, const MA_WAVEFORMATEX* pFormat, MA_WAVEFORMATEX** ppClosestMatch); - HRESULT (STDMETHODCALLTYPE * GetMixFormat) (ma_IAudioClient3* pThis, MA_WAVEFORMATEX** ppDeviceFormat); - HRESULT (STDMETHODCALLTYPE * GetDevicePeriod) (ma_IAudioClient3* pThis, MA_REFERENCE_TIME* pDefaultDevicePeriod, MA_REFERENCE_TIME* pMinimumDevicePeriod); - HRESULT (STDMETHODCALLTYPE * Start) (ma_IAudioClient3* pThis); - HRESULT (STDMETHODCALLTYPE * Stop) (ma_IAudioClient3* pThis); - HRESULT (STDMETHODCALLTYPE * Reset) (ma_IAudioClient3* pThis); - HRESULT (STDMETHODCALLTYPE * SetEventHandle) (ma_IAudioClient3* pThis, HANDLE eventHandle); - HRESULT (STDMETHODCALLTYPE * GetService) (ma_IAudioClient3* pThis, const IID* const riid, void** pp); - - /* IAudioClient2 */ - HRESULT (STDMETHODCALLTYPE * IsOffloadCapable) (ma_IAudioClient3* pThis, MA_AUDIO_STREAM_CATEGORY category, BOOL* pOffloadCapable); - HRESULT (STDMETHODCALLTYPE * SetClientProperties)(ma_IAudioClient3* pThis, const ma_AudioClientProperties* pProperties); - HRESULT (STDMETHODCALLTYPE * GetBufferSizeLimits)(ma_IAudioClient3* pThis, const MA_WAVEFORMATEX* pFormat, BOOL eventDriven, MA_REFERENCE_TIME* pMinBufferDuration, MA_REFERENCE_TIME* pMaxBufferDuration); - - /* IAudioClient3 */ - HRESULT (STDMETHODCALLTYPE * GetSharedModeEnginePeriod) (ma_IAudioClient3* pThis, const MA_WAVEFORMATEX* pFormat, ma_uint32* pDefaultPeriodInFrames, ma_uint32* pFundamentalPeriodInFrames, ma_uint32* pMinPeriodInFrames, ma_uint32* pMaxPeriodInFrames); - HRESULT (STDMETHODCALLTYPE * GetCurrentSharedModeEnginePeriod)(ma_IAudioClient3* pThis, MA_WAVEFORMATEX** ppFormat, ma_uint32* pCurrentPeriodInFrames); - HRESULT (STDMETHODCALLTYPE * InitializeSharedAudioStream) (ma_IAudioClient3* pThis, DWORD streamFlags, ma_uint32 periodInFrames, const MA_WAVEFORMATEX* pFormat, const GUID* pAudioSessionGuid); -} ma_IAudioClient3Vtbl; -struct ma_IAudioClient3 -{ - ma_IAudioClient3Vtbl* lpVtbl; -}; -static MA_INLINE HRESULT ma_IAudioClient3_QueryInterface(ma_IAudioClient3* pThis, const IID* const riid, void** ppObject) { return pThis->lpVtbl->QueryInterface(pThis, riid, ppObject); } -static MA_INLINE ULONG ma_IAudioClient3_AddRef(ma_IAudioClient3* pThis) { return pThis->lpVtbl->AddRef(pThis); } -static MA_INLINE ULONG ma_IAudioClient3_Release(ma_IAudioClient3* pThis) { return pThis->lpVtbl->Release(pThis); } -static MA_INLINE HRESULT ma_IAudioClient3_Initialize(ma_IAudioClient3* pThis, MA_AUDCLNT_SHAREMODE shareMode, DWORD streamFlags, MA_REFERENCE_TIME bufferDuration, MA_REFERENCE_TIME periodicity, const MA_WAVEFORMATEX* pFormat, const GUID* pAudioSessionGuid) { return pThis->lpVtbl->Initialize(pThis, shareMode, streamFlags, bufferDuration, periodicity, pFormat, pAudioSessionGuid); } -static MA_INLINE HRESULT ma_IAudioClient3_GetBufferSize(ma_IAudioClient3* pThis, ma_uint32* pNumBufferFrames) { return pThis->lpVtbl->GetBufferSize(pThis, pNumBufferFrames); } -static MA_INLINE HRESULT ma_IAudioClient3_GetStreamLatency(ma_IAudioClient3* pThis, MA_REFERENCE_TIME* pLatency) { return pThis->lpVtbl->GetStreamLatency(pThis, pLatency); } -static MA_INLINE HRESULT ma_IAudioClient3_GetCurrentPadding(ma_IAudioClient3* pThis, ma_uint32* pNumPaddingFrames) { return pThis->lpVtbl->GetCurrentPadding(pThis, pNumPaddingFrames); } -static MA_INLINE HRESULT ma_IAudioClient3_IsFormatSupported(ma_IAudioClient3* pThis, MA_AUDCLNT_SHAREMODE shareMode, const MA_WAVEFORMATEX* pFormat, MA_WAVEFORMATEX** ppClosestMatch) { return pThis->lpVtbl->IsFormatSupported(pThis, shareMode, pFormat, ppClosestMatch); } -static MA_INLINE HRESULT ma_IAudioClient3_GetMixFormat(ma_IAudioClient3* pThis, MA_WAVEFORMATEX** ppDeviceFormat) { return pThis->lpVtbl->GetMixFormat(pThis, ppDeviceFormat); } -static MA_INLINE HRESULT ma_IAudioClient3_GetDevicePeriod(ma_IAudioClient3* pThis, MA_REFERENCE_TIME* pDefaultDevicePeriod, MA_REFERENCE_TIME* pMinimumDevicePeriod) { return pThis->lpVtbl->GetDevicePeriod(pThis, pDefaultDevicePeriod, pMinimumDevicePeriod); } -static MA_INLINE HRESULT ma_IAudioClient3_Start(ma_IAudioClient3* pThis) { return pThis->lpVtbl->Start(pThis); } -static MA_INLINE HRESULT ma_IAudioClient3_Stop(ma_IAudioClient3* pThis) { return pThis->lpVtbl->Stop(pThis); } -static MA_INLINE HRESULT ma_IAudioClient3_Reset(ma_IAudioClient3* pThis) { return pThis->lpVtbl->Reset(pThis); } -static MA_INLINE HRESULT ma_IAudioClient3_SetEventHandle(ma_IAudioClient3* pThis, HANDLE eventHandle) { return pThis->lpVtbl->SetEventHandle(pThis, eventHandle); } -static MA_INLINE HRESULT ma_IAudioClient3_GetService(ma_IAudioClient3* pThis, const IID* const riid, void** pp) { return pThis->lpVtbl->GetService(pThis, riid, pp); } -static MA_INLINE HRESULT ma_IAudioClient3_IsOffloadCapable(ma_IAudioClient3* pThis, MA_AUDIO_STREAM_CATEGORY category, BOOL* pOffloadCapable) { return pThis->lpVtbl->IsOffloadCapable(pThis, category, pOffloadCapable); } -static MA_INLINE HRESULT ma_IAudioClient3_SetClientProperties(ma_IAudioClient3* pThis, const ma_AudioClientProperties* pProperties) { return pThis->lpVtbl->SetClientProperties(pThis, pProperties); } -static MA_INLINE HRESULT ma_IAudioClient3_GetBufferSizeLimits(ma_IAudioClient3* pThis, const MA_WAVEFORMATEX* pFormat, BOOL eventDriven, MA_REFERENCE_TIME* pMinBufferDuration, MA_REFERENCE_TIME* pMaxBufferDuration) { return pThis->lpVtbl->GetBufferSizeLimits(pThis, pFormat, eventDriven, pMinBufferDuration, pMaxBufferDuration); } -static MA_INLINE HRESULT ma_IAudioClient3_GetSharedModeEnginePeriod(ma_IAudioClient3* pThis, const MA_WAVEFORMATEX* pFormat, ma_uint32* pDefaultPeriodInFrames, ma_uint32* pFundamentalPeriodInFrames, ma_uint32* pMinPeriodInFrames, ma_uint32* pMaxPeriodInFrames) { return pThis->lpVtbl->GetSharedModeEnginePeriod(pThis, pFormat, pDefaultPeriodInFrames, pFundamentalPeriodInFrames, pMinPeriodInFrames, pMaxPeriodInFrames); } -static MA_INLINE HRESULT ma_IAudioClient3_GetCurrentSharedModeEnginePeriod(ma_IAudioClient3* pThis, MA_WAVEFORMATEX** ppFormat, ma_uint32* pCurrentPeriodInFrames) { return pThis->lpVtbl->GetCurrentSharedModeEnginePeriod(pThis, ppFormat, pCurrentPeriodInFrames); } -static MA_INLINE HRESULT ma_IAudioClient3_InitializeSharedAudioStream(ma_IAudioClient3* pThis, DWORD streamFlags, ma_uint32 periodInFrames, const MA_WAVEFORMATEX* pFormat, const GUID* pAudioSessionGUID) { return pThis->lpVtbl->InitializeSharedAudioStream(pThis, streamFlags, periodInFrames, pFormat, pAudioSessionGUID); } - - -/* IAudioRenderClient */ -typedef struct -{ - /* IUnknown */ - HRESULT (STDMETHODCALLTYPE * QueryInterface)(ma_IAudioRenderClient* pThis, const IID* const riid, void** ppObject); - ULONG (STDMETHODCALLTYPE * AddRef) (ma_IAudioRenderClient* pThis); - ULONG (STDMETHODCALLTYPE * Release) (ma_IAudioRenderClient* pThis); - - /* IAudioRenderClient */ - HRESULT (STDMETHODCALLTYPE * GetBuffer) (ma_IAudioRenderClient* pThis, ma_uint32 numFramesRequested, BYTE** ppData); - HRESULT (STDMETHODCALLTYPE * ReleaseBuffer)(ma_IAudioRenderClient* pThis, ma_uint32 numFramesWritten, DWORD dwFlags); -} ma_IAudioRenderClientVtbl; -struct ma_IAudioRenderClient -{ - ma_IAudioRenderClientVtbl* lpVtbl; -}; -static MA_INLINE HRESULT ma_IAudioRenderClient_QueryInterface(ma_IAudioRenderClient* pThis, const IID* const riid, void** ppObject) { return pThis->lpVtbl->QueryInterface(pThis, riid, ppObject); } -static MA_INLINE ULONG ma_IAudioRenderClient_AddRef(ma_IAudioRenderClient* pThis) { return pThis->lpVtbl->AddRef(pThis); } -static MA_INLINE ULONG ma_IAudioRenderClient_Release(ma_IAudioRenderClient* pThis) { return pThis->lpVtbl->Release(pThis); } -static MA_INLINE HRESULT ma_IAudioRenderClient_GetBuffer(ma_IAudioRenderClient* pThis, ma_uint32 numFramesRequested, BYTE** ppData) { return pThis->lpVtbl->GetBuffer(pThis, numFramesRequested, ppData); } -static MA_INLINE HRESULT ma_IAudioRenderClient_ReleaseBuffer(ma_IAudioRenderClient* pThis, ma_uint32 numFramesWritten, DWORD dwFlags) { return pThis->lpVtbl->ReleaseBuffer(pThis, numFramesWritten, dwFlags); } - - -/* IAudioCaptureClient */ -typedef struct -{ - /* IUnknown */ - HRESULT (STDMETHODCALLTYPE * QueryInterface)(ma_IAudioCaptureClient* pThis, const IID* const riid, void** ppObject); - ULONG (STDMETHODCALLTYPE * AddRef) (ma_IAudioCaptureClient* pThis); - ULONG (STDMETHODCALLTYPE * Release) (ma_IAudioCaptureClient* pThis); - - /* IAudioRenderClient */ - HRESULT (STDMETHODCALLTYPE * GetBuffer) (ma_IAudioCaptureClient* pThis, BYTE** ppData, ma_uint32* pNumFramesToRead, DWORD* pFlags, ma_uint64* pDevicePosition, ma_uint64* pQPCPosition); - HRESULT (STDMETHODCALLTYPE * ReleaseBuffer) (ma_IAudioCaptureClient* pThis, ma_uint32 numFramesRead); - HRESULT (STDMETHODCALLTYPE * GetNextPacketSize)(ma_IAudioCaptureClient* pThis, ma_uint32* pNumFramesInNextPacket); -} ma_IAudioCaptureClientVtbl; -struct ma_IAudioCaptureClient -{ - ma_IAudioCaptureClientVtbl* lpVtbl; -}; -static MA_INLINE HRESULT ma_IAudioCaptureClient_QueryInterface(ma_IAudioCaptureClient* pThis, const IID* const riid, void** ppObject) { return pThis->lpVtbl->QueryInterface(pThis, riid, ppObject); } -static MA_INLINE ULONG ma_IAudioCaptureClient_AddRef(ma_IAudioCaptureClient* pThis) { return pThis->lpVtbl->AddRef(pThis); } -static MA_INLINE ULONG ma_IAudioCaptureClient_Release(ma_IAudioCaptureClient* pThis) { return pThis->lpVtbl->Release(pThis); } -static MA_INLINE HRESULT ma_IAudioCaptureClient_GetBuffer(ma_IAudioCaptureClient* pThis, BYTE** ppData, ma_uint32* pNumFramesToRead, DWORD* pFlags, ma_uint64* pDevicePosition, ma_uint64* pQPCPosition) { return pThis->lpVtbl->GetBuffer(pThis, ppData, pNumFramesToRead, pFlags, pDevicePosition, pQPCPosition); } -static MA_INLINE HRESULT ma_IAudioCaptureClient_ReleaseBuffer(ma_IAudioCaptureClient* pThis, ma_uint32 numFramesRead) { return pThis->lpVtbl->ReleaseBuffer(pThis, numFramesRead); } -static MA_INLINE HRESULT ma_IAudioCaptureClient_GetNextPacketSize(ma_IAudioCaptureClient* pThis, ma_uint32* pNumFramesInNextPacket) { return pThis->lpVtbl->GetNextPacketSize(pThis, pNumFramesInNextPacket); } - -#if defined(MA_WIN32_UWP) -/* mmdevapi Functions */ -typedef HRESULT (WINAPI * MA_PFN_ActivateAudioInterfaceAsync)(const wchar_t* deviceInterfacePath, const IID* riid, MA_PROPVARIANT* activationParams, ma_IActivateAudioInterfaceCompletionHandler* completionHandler, ma_IActivateAudioInterfaceAsyncOperation** activationOperation); -#endif - -/* Avrt Functions */ -typedef HANDLE (WINAPI * MA_PFN_AvSetMmThreadCharacteristicsA)(const char* TaskName, DWORD* TaskIndex); -typedef BOOL (WINAPI * MA_PFN_AvRevertMmThreadCharacteristics)(HANDLE AvrtHandle); - -#if !defined(MA_WIN32_DESKTOP) && !defined(MA_WIN32_GDK) -typedef struct ma_completion_handler_uwp ma_completion_handler_uwp; - -typedef struct -{ - /* IUnknown */ - HRESULT (STDMETHODCALLTYPE * QueryInterface)(ma_completion_handler_uwp* pThis, const IID* const riid, void** ppObject); - ULONG (STDMETHODCALLTYPE * AddRef) (ma_completion_handler_uwp* pThis); - ULONG (STDMETHODCALLTYPE * Release) (ma_completion_handler_uwp* pThis); - - /* IActivateAudioInterfaceCompletionHandler */ - HRESULT (STDMETHODCALLTYPE * ActivateCompleted)(ma_completion_handler_uwp* pThis, ma_IActivateAudioInterfaceAsyncOperation* pActivateOperation); -} ma_completion_handler_uwp_vtbl; -struct ma_completion_handler_uwp -{ - ma_completion_handler_uwp_vtbl* lpVtbl; - MA_ATOMIC(4, ma_uint32) counter; - HANDLE hEvent; -}; - -static HRESULT STDMETHODCALLTYPE ma_completion_handler_uwp_QueryInterface(ma_completion_handler_uwp* pThis, const IID* const riid, void** ppObject) -{ - /* - We need to "implement" IAgileObject which is just an indicator that's used internally by WASAPI for some multithreading management. To - "implement" this, we just make sure we return pThis when the IAgileObject is requested. - */ - if (!ma_is_guid_equal(riid, &MA_IID_IUnknown) && !ma_is_guid_equal(riid, &MA_IID_IActivateAudioInterfaceCompletionHandler) && !ma_is_guid_equal(riid, &MA_IID_IAgileObject)) { - *ppObject = NULL; - return E_NOINTERFACE; - } - - /* Getting here means the IID is IUnknown or IMMNotificationClient. */ - *ppObject = (void*)pThis; - ((ma_completion_handler_uwp_vtbl*)pThis->lpVtbl)->AddRef(pThis); - return S_OK; -} - -static ULONG STDMETHODCALLTYPE ma_completion_handler_uwp_AddRef(ma_completion_handler_uwp* pThis) -{ - return (ULONG)ma_atomic_fetch_add_32(&pThis->counter, 1) + 1; -} - -static ULONG STDMETHODCALLTYPE ma_completion_handler_uwp_Release(ma_completion_handler_uwp* pThis) -{ - ma_uint32 newRefCount = ma_atomic_fetch_sub_32(&pThis->counter, 1) - 1; - if (newRefCount == 0) { - return 0; /* We don't free anything here because we never allocate the object on the heap. */ - } - - return (ULONG)newRefCount; -} - -static HRESULT STDMETHODCALLTYPE ma_completion_handler_uwp_ActivateCompleted(ma_completion_handler_uwp* pThis, ma_IActivateAudioInterfaceAsyncOperation* pActivateOperation) -{ - (void)pActivateOperation; - SetEvent(pThis->hEvent); - return S_OK; -} - - -static ma_completion_handler_uwp_vtbl g_maCompletionHandlerVtblInstance = { - ma_completion_handler_uwp_QueryInterface, - ma_completion_handler_uwp_AddRef, - ma_completion_handler_uwp_Release, - ma_completion_handler_uwp_ActivateCompleted -}; - -static ma_result ma_completion_handler_uwp_init(ma_completion_handler_uwp* pHandler) -{ - MA_ASSERT(pHandler != NULL); - MA_ZERO_OBJECT(pHandler); - - pHandler->lpVtbl = &g_maCompletionHandlerVtblInstance; - pHandler->counter = 1; - pHandler->hEvent = CreateEventA(NULL, FALSE, FALSE, NULL); - if (pHandler->hEvent == NULL) { - return ma_result_from_GetLastError(GetLastError()); - } - - return MA_SUCCESS; -} - -static void ma_completion_handler_uwp_uninit(ma_completion_handler_uwp* pHandler) -{ - if (pHandler->hEvent != NULL) { - CloseHandle(pHandler->hEvent); - } -} - -static void ma_completion_handler_uwp_wait(ma_completion_handler_uwp* pHandler) -{ - WaitForSingleObject((HANDLE)pHandler->hEvent, INFINITE); -} -#endif /* !MA_WIN32_DESKTOP */ - -/* We need a virtual table for our notification client object that's used for detecting changes to the default device. */ -#if defined(MA_WIN32_DESKTOP) || defined(MA_WIN32_GDK) -static HRESULT STDMETHODCALLTYPE ma_IMMNotificationClient_QueryInterface(ma_IMMNotificationClient* pThis, const IID* const riid, void** ppObject) -{ - /* - We care about two interfaces - IUnknown and IMMNotificationClient. If the requested IID is something else - we just return E_NOINTERFACE. Otherwise we need to increment the reference counter and return S_OK. - */ - if (!ma_is_guid_equal(riid, &MA_IID_IUnknown) && !ma_is_guid_equal(riid, &MA_IID_IMMNotificationClient)) { - *ppObject = NULL; - return E_NOINTERFACE; - } - - /* Getting here means the IID is IUnknown or IMMNotificationClient. */ - *ppObject = (void*)pThis; - ((ma_IMMNotificationClientVtbl*)pThis->lpVtbl)->AddRef(pThis); - return S_OK; -} - -static ULONG STDMETHODCALLTYPE ma_IMMNotificationClient_AddRef(ma_IMMNotificationClient* pThis) -{ - return (ULONG)ma_atomic_fetch_add_32(&pThis->counter, 1) + 1; -} - -static ULONG STDMETHODCALLTYPE ma_IMMNotificationClient_Release(ma_IMMNotificationClient* pThis) -{ - ma_uint32 newRefCount = ma_atomic_fetch_sub_32(&pThis->counter, 1) - 1; - if (newRefCount == 0) { - return 0; /* We don't free anything here because we never allocate the object on the heap. */ - } - - return (ULONG)newRefCount; -} - -static HRESULT STDMETHODCALLTYPE ma_IMMNotificationClient_OnDeviceStateChanged(ma_IMMNotificationClient* pThis, const WCHAR* pDeviceID, DWORD dwNewState) -{ - ma_bool32 isThisDevice = MA_FALSE; - ma_bool32 isCapture = MA_FALSE; - ma_bool32 isPlayback = MA_FALSE; - -#ifdef MA_DEBUG_OUTPUT - /*ma_log_postf(ma_device_get_log(pThis->pDevice), MA_LOG_LEVEL_DEBUG, "IMMNotificationClient_OnDeviceStateChanged(pDeviceID=%S, dwNewState=%u)\n", (pDeviceID != NULL) ? pDeviceID : L"(NULL)", (unsigned int)dwNewState);*/ -#endif - - /* - There have been reports of a hang when a playback device is disconnected. The idea with this code is to explicitly stop the device if we detect - that the device is disabled or has been unplugged. - */ - if (pThis->pDevice->wasapi.allowCaptureAutoStreamRouting && (pThis->pDevice->type == ma_device_type_capture || pThis->pDevice->type == ma_device_type_duplex || pThis->pDevice->type == ma_device_type_loopback)) { - isCapture = MA_TRUE; - if (ma_strcmp_WCHAR(pThis->pDevice->capture.id.wasapi, pDeviceID) == 0) { - isThisDevice = MA_TRUE; - } - } - - if (pThis->pDevice->wasapi.allowPlaybackAutoStreamRouting && (pThis->pDevice->type == ma_device_type_playback || pThis->pDevice->type == ma_device_type_duplex)) { - isPlayback = MA_TRUE; - if (ma_strcmp_WCHAR(pThis->pDevice->playback.id.wasapi, pDeviceID) == 0) { - isThisDevice = MA_TRUE; - } - } - - - /* - If the device ID matches our device we need to mark our device as detached and stop it. When a - device is added in OnDeviceAdded(), we'll restart it. We only mark it as detached if the device - was started at the time of being removed. - */ - if (isThisDevice) { - if ((dwNewState & MA_MM_DEVICE_STATE_ACTIVE) == 0) { - /* - Unplugged or otherwise unavailable. Mark as detached if we were in a playing state. We'll - use this to determine whether or not we need to automatically start the device when it's - plugged back in again. - */ - if (ma_device_get_state(pThis->pDevice) == ma_device_state_started) { - if (isPlayback) { - pThis->pDevice->wasapi.isDetachedPlayback = MA_TRUE; - } - if (isCapture) { - pThis->pDevice->wasapi.isDetachedCapture = MA_TRUE; - } - - ma_device_stop(pThis->pDevice); - } - } - - if ((dwNewState & MA_MM_DEVICE_STATE_ACTIVE) != 0) { - /* The device was activated. If we were detached, we need to start it again. */ - ma_bool8 tryRestartingDevice = MA_FALSE; - - if (isPlayback) { - if (pThis->pDevice->wasapi.isDetachedPlayback) { - pThis->pDevice->wasapi.isDetachedPlayback = MA_FALSE; - ma_device_reroute__wasapi(pThis->pDevice, ma_device_type_playback); - tryRestartingDevice = MA_TRUE; - } - } - - if (isCapture) { - if (pThis->pDevice->wasapi.isDetachedCapture) { - pThis->pDevice->wasapi.isDetachedCapture = MA_FALSE; - ma_device_reroute__wasapi(pThis->pDevice, (pThis->pDevice->type == ma_device_type_loopback) ? ma_device_type_loopback : ma_device_type_capture); - tryRestartingDevice = MA_TRUE; - } - } - - if (tryRestartingDevice) { - if (pThis->pDevice->wasapi.isDetachedPlayback == MA_FALSE && pThis->pDevice->wasapi.isDetachedCapture == MA_FALSE) { - ma_device_start(pThis->pDevice); - } - } - } - } - - return S_OK; -} - -static HRESULT STDMETHODCALLTYPE ma_IMMNotificationClient_OnDeviceAdded(ma_IMMNotificationClient* pThis, const WCHAR* pDeviceID) -{ -#ifdef MA_DEBUG_OUTPUT - /*ma_log_postf(ma_device_get_log(pThis->pDevice), MA_LOG_LEVEL_DEBUG, "IMMNotificationClient_OnDeviceAdded(pDeviceID=%S)\n", (pDeviceID != NULL) ? pDeviceID : L"(NULL)");*/ -#endif - - /* We don't need to worry about this event for our purposes. */ - (void)pThis; - (void)pDeviceID; - return S_OK; -} - -static HRESULT STDMETHODCALLTYPE ma_IMMNotificationClient_OnDeviceRemoved(ma_IMMNotificationClient* pThis, const WCHAR* pDeviceID) -{ -#ifdef MA_DEBUG_OUTPUT - /*ma_log_postf(ma_device_get_log(pThis->pDevice), MA_LOG_LEVEL_DEBUG, "IMMNotificationClient_OnDeviceRemoved(pDeviceID=%S)\n", (pDeviceID != NULL) ? pDeviceID : L"(NULL)");*/ -#endif - - /* We don't need to worry about this event for our purposes. */ - (void)pThis; - (void)pDeviceID; - return S_OK; -} - -static HRESULT STDMETHODCALLTYPE ma_IMMNotificationClient_OnDefaultDeviceChanged(ma_IMMNotificationClient* pThis, ma_EDataFlow dataFlow, ma_ERole role, const WCHAR* pDefaultDeviceID) -{ -#ifdef MA_DEBUG_OUTPUT - /*ma_log_postf(ma_device_get_log(pThis->pDevice), MA_LOG_LEVEL_DEBUG, "IMMNotificationClient_OnDefaultDeviceChanged(dataFlow=%d, role=%d, pDefaultDeviceID=%S)\n", dataFlow, role, (pDefaultDeviceID != NULL) ? pDefaultDeviceID : L"(NULL)");*/ -#endif - - (void)role; - - /* We only care about devices with the same data flow as the current device. */ - if ((pThis->pDevice->type == ma_device_type_playback && dataFlow != ma_eRender) || - (pThis->pDevice->type == ma_device_type_capture && dataFlow != ma_eCapture) || - (pThis->pDevice->type == ma_device_type_loopback && dataFlow != ma_eRender)) { - ma_log_postf(ma_device_get_log(pThis->pDevice), MA_LOG_LEVEL_DEBUG, "[WASAPI] Stream rerouting abandoned because dataFlow does match device type.\n"); - return S_OK; - } - - /* We need to consider dataFlow as ma_eCapture if device is ma_device_type_loopback */ - if (pThis->pDevice->type == ma_device_type_loopback) { - dataFlow = ma_eCapture; - } - - /* Don't do automatic stream routing if we're not allowed. */ - if ((dataFlow == ma_eRender && pThis->pDevice->wasapi.allowPlaybackAutoStreamRouting == MA_FALSE) || - (dataFlow == ma_eCapture && pThis->pDevice->wasapi.allowCaptureAutoStreamRouting == MA_FALSE)) { - ma_log_postf(ma_device_get_log(pThis->pDevice), MA_LOG_LEVEL_DEBUG, "[WASAPI] Stream rerouting abandoned because automatic stream routing has been disabled by the device config.\n"); - return S_OK; - } - - /* - Not currently supporting automatic stream routing in exclusive mode. This is not working correctly on my machine due to - AUDCLNT_E_DEVICE_IN_USE errors when reinitializing the device. If this is a bug in miniaudio, we can try re-enabling this once - it's fixed. - */ - if ((dataFlow == ma_eRender && pThis->pDevice->playback.shareMode == ma_share_mode_exclusive) || - (dataFlow == ma_eCapture && pThis->pDevice->capture.shareMode == ma_share_mode_exclusive)) { - ma_log_postf(ma_device_get_log(pThis->pDevice), MA_LOG_LEVEL_DEBUG, "[WASAPI] Stream rerouting abandoned because the device shared mode is exclusive.\n"); - return S_OK; - } - - - - /* - Second attempt at device rerouting. We're going to retrieve the device's state at the time of - the route change. We're then going to stop the device, reinitialize the device, and then start - it again if the state before stopping was ma_device_state_started. - */ - { - ma_uint32 previousState = ma_device_get_state(pThis->pDevice); - ma_bool8 restartDevice = MA_FALSE; - - if (previousState == ma_device_state_uninitialized || previousState == ma_device_state_starting) { - ma_log_postf(ma_device_get_log(pThis->pDevice), MA_LOG_LEVEL_DEBUG, "[WASAPI] Stream rerouting abandoned because the device is in the process of starting.\n"); - return S_OK; - } - - if (previousState == ma_device_state_started) { - ma_device_stop(pThis->pDevice); - restartDevice = MA_TRUE; - } - - if (pDefaultDeviceID != NULL) { /* <-- The input device ID will be null if there's no other device available. */ - ma_mutex_lock(&pThis->pDevice->wasapi.rerouteLock); - { - if (dataFlow == ma_eRender) { - ma_device_reroute__wasapi(pThis->pDevice, ma_device_type_playback); - - if (pThis->pDevice->wasapi.isDetachedPlayback) { - pThis->pDevice->wasapi.isDetachedPlayback = MA_FALSE; - - if (pThis->pDevice->type == ma_device_type_duplex && pThis->pDevice->wasapi.isDetachedCapture) { - restartDevice = MA_FALSE; /* It's a duplex device and the capture side is detached. We cannot be restarting the device just yet. */ - } - else { - restartDevice = MA_TRUE; /* It's not a duplex device, or the capture side is also attached so we can go ahead and restart the device. */ - } - } - } - else { - ma_device_reroute__wasapi(pThis->pDevice, (pThis->pDevice->type == ma_device_type_loopback) ? ma_device_type_loopback : ma_device_type_capture); - - if (pThis->pDevice->wasapi.isDetachedCapture) { - pThis->pDevice->wasapi.isDetachedCapture = MA_FALSE; - - if (pThis->pDevice->type == ma_device_type_duplex && pThis->pDevice->wasapi.isDetachedPlayback) { - restartDevice = MA_FALSE; /* It's a duplex device and the playback side is detached. We cannot be restarting the device just yet. */ - } - else { - restartDevice = MA_TRUE; /* It's not a duplex device, or the playback side is also attached so we can go ahead and restart the device. */ - } - } - } - } - ma_mutex_unlock(&pThis->pDevice->wasapi.rerouteLock); - - if (restartDevice) { - ma_device_start(pThis->pDevice); - } - } - } - - return S_OK; -} - -static HRESULT STDMETHODCALLTYPE ma_IMMNotificationClient_OnPropertyValueChanged(ma_IMMNotificationClient* pThis, const WCHAR* pDeviceID, const PROPERTYKEY key) -{ -#ifdef MA_DEBUG_OUTPUT - /*ma_log_postf(ma_device_get_log(pThis->pDevice), MA_LOG_LEVEL_DEBUG, "IMMNotificationClient_OnPropertyValueChanged(pDeviceID=%S)\n", (pDeviceID != NULL) ? pDeviceID : L"(NULL)");*/ -#endif - - (void)pThis; - (void)pDeviceID; - (void)key; - return S_OK; -} - -static ma_IMMNotificationClientVtbl g_maNotificationCientVtbl = { - ma_IMMNotificationClient_QueryInterface, - ma_IMMNotificationClient_AddRef, - ma_IMMNotificationClient_Release, - ma_IMMNotificationClient_OnDeviceStateChanged, - ma_IMMNotificationClient_OnDeviceAdded, - ma_IMMNotificationClient_OnDeviceRemoved, - ma_IMMNotificationClient_OnDefaultDeviceChanged, - ma_IMMNotificationClient_OnPropertyValueChanged -}; -#endif /* MA_WIN32_DESKTOP */ - -static const char* ma_to_usage_string__wasapi(ma_wasapi_usage usage) -{ - switch (usage) - { - case ma_wasapi_usage_default: return NULL; - case ma_wasapi_usage_games: return "Games"; - case ma_wasapi_usage_pro_audio: return "Pro Audio"; - default: break; - } - - return NULL; -} - -#if defined(MA_WIN32_DESKTOP) || defined(MA_WIN32_GDK) -typedef ma_IMMDevice ma_WASAPIDeviceInterface; -#else -typedef ma_IUnknown ma_WASAPIDeviceInterface; -#endif - - -#define MA_CONTEXT_COMMAND_QUIT__WASAPI 1 -#define MA_CONTEXT_COMMAND_CREATE_IAUDIOCLIENT__WASAPI 2 -#define MA_CONTEXT_COMMAND_RELEASE_IAUDIOCLIENT__WASAPI 3 - -static ma_context_command__wasapi ma_context_init_command__wasapi(int code) -{ - ma_context_command__wasapi cmd; - - MA_ZERO_OBJECT(&cmd); - cmd.code = code; - - return cmd; -} - -static ma_result ma_context_post_command__wasapi(ma_context* pContext, const ma_context_command__wasapi* pCmd) -{ - /* For now we are doing everything synchronously, but I might relax this later if the need arises. */ - ma_result result; - ma_bool32 isUsingLocalEvent = MA_FALSE; - ma_event localEvent; - - MA_ASSERT(pContext != NULL); - MA_ASSERT(pCmd != NULL); - - if (pCmd->pEvent == NULL) { - isUsingLocalEvent = MA_TRUE; - - result = ma_event_init(&localEvent); - if (result != MA_SUCCESS) { - return result; /* Failed to create the event for this command. */ - } - } - - /* Here is where we add the command to the list. If there's not enough room we'll spin until there is. */ - ma_mutex_lock(&pContext->wasapi.commandLock); - { - ma_uint32 index; - - /* Spin until we've got some space available. */ - while (pContext->wasapi.commandCount == ma_countof(pContext->wasapi.commands)) { - ma_yield(); - } - - /* Space is now available. Can safely add to the list. */ - index = (pContext->wasapi.commandIndex + pContext->wasapi.commandCount) % ma_countof(pContext->wasapi.commands); - pContext->wasapi.commands[index] = *pCmd; - pContext->wasapi.commands[index].pEvent = &localEvent; - pContext->wasapi.commandCount += 1; - - /* Now that the command has been added, release the semaphore so ma_context_next_command__wasapi() can return. */ - ma_semaphore_release(&pContext->wasapi.commandSem); - } - ma_mutex_unlock(&pContext->wasapi.commandLock); - - if (isUsingLocalEvent) { - ma_event_wait(&localEvent); - ma_event_uninit(&localEvent); - } - - return MA_SUCCESS; -} - -static ma_result ma_context_next_command__wasapi(ma_context* pContext, ma_context_command__wasapi* pCmd) -{ - ma_result result = MA_SUCCESS; - - MA_ASSERT(pContext != NULL); - MA_ASSERT(pCmd != NULL); - - result = ma_semaphore_wait(&pContext->wasapi.commandSem); - if (result == MA_SUCCESS) { - ma_mutex_lock(&pContext->wasapi.commandLock); - { - *pCmd = pContext->wasapi.commands[pContext->wasapi.commandIndex]; - pContext->wasapi.commandIndex = (pContext->wasapi.commandIndex + 1) % ma_countof(pContext->wasapi.commands); - pContext->wasapi.commandCount -= 1; - } - ma_mutex_unlock(&pContext->wasapi.commandLock); - } - - return result; -} - -static ma_thread_result MA_THREADCALL ma_context_command_thread__wasapi(void* pUserData) -{ - ma_result result; - ma_context* pContext = (ma_context*)pUserData; - MA_ASSERT(pContext != NULL); - - for (;;) { - ma_context_command__wasapi cmd; - result = ma_context_next_command__wasapi(pContext, &cmd); - if (result != MA_SUCCESS) { - break; - } - - switch (cmd.code) - { - case MA_CONTEXT_COMMAND_QUIT__WASAPI: - { - /* Do nothing. Handled after the switch. */ - } break; - - case MA_CONTEXT_COMMAND_CREATE_IAUDIOCLIENT__WASAPI: - { - if (cmd.data.createAudioClient.deviceType == ma_device_type_playback) { - *cmd.data.createAudioClient.pResult = ma_result_from_HRESULT(ma_IAudioClient_GetService((ma_IAudioClient*)cmd.data.createAudioClient.pAudioClient, &MA_IID_IAudioRenderClient, cmd.data.createAudioClient.ppAudioClientService)); - } else { - *cmd.data.createAudioClient.pResult = ma_result_from_HRESULT(ma_IAudioClient_GetService((ma_IAudioClient*)cmd.data.createAudioClient.pAudioClient, &MA_IID_IAudioCaptureClient, cmd.data.createAudioClient.ppAudioClientService)); - } - } break; - - case MA_CONTEXT_COMMAND_RELEASE_IAUDIOCLIENT__WASAPI: - { - if (cmd.data.releaseAudioClient.deviceType == ma_device_type_playback) { - if (cmd.data.releaseAudioClient.pDevice->wasapi.pAudioClientPlayback != NULL) { - ma_IAudioClient_Release((ma_IAudioClient*)cmd.data.releaseAudioClient.pDevice->wasapi.pAudioClientPlayback); - cmd.data.releaseAudioClient.pDevice->wasapi.pAudioClientPlayback = NULL; - } - } - - if (cmd.data.releaseAudioClient.deviceType == ma_device_type_capture) { - if (cmd.data.releaseAudioClient.pDevice->wasapi.pAudioClientCapture != NULL) { - ma_IAudioClient_Release((ma_IAudioClient*)cmd.data.releaseAudioClient.pDevice->wasapi.pAudioClientCapture); - cmd.data.releaseAudioClient.pDevice->wasapi.pAudioClientCapture = NULL; - } - } - } break; - - default: - { - /* Unknown command. Ignore it, but trigger an assert in debug mode so we're aware of it. */ - MA_ASSERT(MA_FALSE); - } break; - } - - if (cmd.pEvent != NULL) { - ma_event_signal(cmd.pEvent); - } - - if (cmd.code == MA_CONTEXT_COMMAND_QUIT__WASAPI) { - break; /* Received a quit message. Get out of here. */ - } - } - - return (ma_thread_result)0; -} - -static ma_result ma_device_create_IAudioClient_service__wasapi(ma_context* pContext, ma_device_type deviceType, ma_IAudioClient* pAudioClient, void** ppAudioClientService) -{ - ma_result result; - ma_result cmdResult; - ma_context_command__wasapi cmd = ma_context_init_command__wasapi(MA_CONTEXT_COMMAND_CREATE_IAUDIOCLIENT__WASAPI); - cmd.data.createAudioClient.deviceType = deviceType; - cmd.data.createAudioClient.pAudioClient = (void*)pAudioClient; - cmd.data.createAudioClient.ppAudioClientService = ppAudioClientService; - cmd.data.createAudioClient.pResult = &cmdResult; /* Declared locally, but won't be dereferenced after this function returns since execution of the command will wait here. */ - - result = ma_context_post_command__wasapi(pContext, &cmd); /* This will not return until the command has actually been run. */ - if (result != MA_SUCCESS) { - return result; - } - - return *cmd.data.createAudioClient.pResult; -} - -#if 0 /* Not used at the moment, but leaving here for future use. */ -static ma_result ma_device_release_IAudioClient_service__wasapi(ma_device* pDevice, ma_device_type deviceType) -{ - ma_result result; - ma_context_command__wasapi cmd = ma_context_init_command__wasapi(MA_CONTEXT_COMMAND_RELEASE_IAUDIOCLIENT__WASAPI); - cmd.data.releaseAudioClient.pDevice = pDevice; - cmd.data.releaseAudioClient.deviceType = deviceType; - - result = ma_context_post_command__wasapi(pDevice->pContext, &cmd); /* This will not return until the command has actually been run. */ - if (result != MA_SUCCESS) { - return result; - } - - return MA_SUCCESS; -} -#endif - - -static void ma_add_native_data_format_to_device_info_from_WAVEFORMATEX(const MA_WAVEFORMATEX* pWF, ma_share_mode shareMode, ma_device_info* pInfo) -{ - MA_ASSERT(pWF != NULL); - MA_ASSERT(pInfo != NULL); - - if (pInfo->nativeDataFormatCount >= ma_countof(pInfo->nativeDataFormats)) { - return; /* Too many data formats. Need to ignore this one. Don't think this should ever happen with WASAPI. */ - } - - pInfo->nativeDataFormats[pInfo->nativeDataFormatCount].format = ma_format_from_WAVEFORMATEX(pWF); - pInfo->nativeDataFormats[pInfo->nativeDataFormatCount].channels = pWF->nChannels; - pInfo->nativeDataFormats[pInfo->nativeDataFormatCount].sampleRate = pWF->nSamplesPerSec; - pInfo->nativeDataFormats[pInfo->nativeDataFormatCount].flags = (shareMode == ma_share_mode_exclusive) ? MA_DATA_FORMAT_FLAG_EXCLUSIVE_MODE : 0; - pInfo->nativeDataFormatCount += 1; -} - -static ma_result ma_context_get_device_info_from_IAudioClient__wasapi(ma_context* pContext, /*ma_IMMDevice**/void* pMMDevice, ma_IAudioClient* pAudioClient, ma_device_info* pInfo) -{ - HRESULT hr; - MA_WAVEFORMATEX* pWF = NULL; - - MA_ASSERT(pAudioClient != NULL); - MA_ASSERT(pInfo != NULL); - - /* Shared Mode. We use GetMixFormat() here. */ - hr = ma_IAudioClient_GetMixFormat((ma_IAudioClient*)pAudioClient, (MA_WAVEFORMATEX**)&pWF); - if (SUCCEEDED(hr)) { - ma_add_native_data_format_to_device_info_from_WAVEFORMATEX(pWF, ma_share_mode_shared, pInfo); - } else { - ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to retrieve mix format for device info retrieval."); - return ma_result_from_HRESULT(hr); - } - - /* - Exclusive Mode. We repeatedly call IsFormatSupported() here. This is not currently supported on - UWP. Failure to retrieve the exclusive mode format is not considered an error, so from here on - out, MA_SUCCESS is guaranteed to be returned. - */ - #if defined(MA_WIN32_DESKTOP) || defined(MA_WIN32_GDK) - { - ma_IPropertyStore *pProperties; - - /* - The first thing to do is get the format from PKEY_AudioEngine_DeviceFormat. This should give us a channel count we assume is - correct which will simplify our searching. - */ - hr = ma_IMMDevice_OpenPropertyStore((ma_IMMDevice*)pMMDevice, STGM_READ, &pProperties); - if (SUCCEEDED(hr)) { - MA_PROPVARIANT var; - ma_PropVariantInit(&var); - - hr = ma_IPropertyStore_GetValue(pProperties, &MA_PKEY_AudioEngine_DeviceFormat, &var); - if (SUCCEEDED(hr)) { - pWF = (MA_WAVEFORMATEX*)var.blob.pBlobData; - - /* - In my testing, the format returned by PKEY_AudioEngine_DeviceFormat is suitable for exclusive mode so we check this format - first. If this fails, fall back to a search. - */ - hr = ma_IAudioClient_IsFormatSupported((ma_IAudioClient*)pAudioClient, MA_AUDCLNT_SHAREMODE_EXCLUSIVE, pWF, NULL); - if (SUCCEEDED(hr)) { - /* The format returned by PKEY_AudioEngine_DeviceFormat is supported. */ - ma_add_native_data_format_to_device_info_from_WAVEFORMATEX(pWF, ma_share_mode_exclusive, pInfo); - } else { - /* - The format returned by PKEY_AudioEngine_DeviceFormat is not supported, so fall back to a search. We assume the channel - count returned by MA_PKEY_AudioEngine_DeviceFormat is valid and correct. For simplicity we're only returning one format. - */ - ma_uint32 channels = pWF->nChannels; - ma_channel defaultChannelMap[MA_MAX_CHANNELS]; - MA_WAVEFORMATEXTENSIBLE wf; - ma_bool32 found; - ma_uint32 iFormat; - - /* Make sure we don't overflow the channel map. */ - if (channels > MA_MAX_CHANNELS) { - channels = MA_MAX_CHANNELS; - } - - ma_channel_map_init_standard(ma_standard_channel_map_microsoft, defaultChannelMap, ma_countof(defaultChannelMap), channels); - - MA_ZERO_OBJECT(&wf); - wf.cbSize = sizeof(wf); - wf.wFormatTag = WAVE_FORMAT_EXTENSIBLE; - wf.nChannels = (WORD)channels; - wf.dwChannelMask = ma_channel_map_to_channel_mask__win32(defaultChannelMap, channels); - - found = MA_FALSE; - for (iFormat = 0; iFormat < ma_countof(g_maFormatPriorities); ++iFormat) { - ma_format format = g_maFormatPriorities[iFormat]; - ma_uint32 iSampleRate; - - wf.wBitsPerSample = (WORD)(ma_get_bytes_per_sample(format)*8); - wf.nBlockAlign = (WORD)(wf.nChannels * wf.wBitsPerSample / 8); - wf.nAvgBytesPerSec = wf.nBlockAlign * wf.nSamplesPerSec; - wf.Samples.wValidBitsPerSample = /*(format == ma_format_s24_32) ? 24 :*/ wf.wBitsPerSample; - if (format == ma_format_f32) { - wf.SubFormat = MA_GUID_KSDATAFORMAT_SUBTYPE_IEEE_FLOAT; - } else { - wf.SubFormat = MA_GUID_KSDATAFORMAT_SUBTYPE_PCM; - } - - for (iSampleRate = 0; iSampleRate < ma_countof(g_maStandardSampleRatePriorities); ++iSampleRate) { - wf.nSamplesPerSec = g_maStandardSampleRatePriorities[iSampleRate]; - - hr = ma_IAudioClient_IsFormatSupported((ma_IAudioClient*)pAudioClient, MA_AUDCLNT_SHAREMODE_EXCLUSIVE, (MA_WAVEFORMATEX*)&wf, NULL); - if (SUCCEEDED(hr)) { - ma_add_native_data_format_to_device_info_from_WAVEFORMATEX((MA_WAVEFORMATEX*)&wf, ma_share_mode_exclusive, pInfo); - found = MA_TRUE; - break; - } - } - - if (found) { - break; - } - } - - ma_PropVariantClear(pContext, &var); - - if (!found) { - ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_WARNING, "[WASAPI] Failed to find suitable device format for device info retrieval."); - } - } - } else { - ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_WARNING, "[WASAPI] Failed to retrieve device format for device info retrieval."); - } - - ma_IPropertyStore_Release(pProperties); - } else { - ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_WARNING, "[WASAPI] Failed to open property store for device info retrieval."); - } - } - #else - { - (void)pMMDevice; /* Unused. */ - } - #endif - - return MA_SUCCESS; -} - -#if defined(MA_WIN32_DESKTOP) || defined(MA_WIN32_GDK) -static ma_EDataFlow ma_device_type_to_EDataFlow(ma_device_type deviceType) -{ - if (deviceType == ma_device_type_playback) { - return ma_eRender; - } else if (deviceType == ma_device_type_capture) { - return ma_eCapture; - } else { - MA_ASSERT(MA_FALSE); - return ma_eRender; /* Should never hit this. */ - } -} - -static ma_result ma_context_create_IMMDeviceEnumerator__wasapi(ma_context* pContext, ma_IMMDeviceEnumerator** ppDeviceEnumerator) -{ - HRESULT hr; - ma_IMMDeviceEnumerator* pDeviceEnumerator; - - MA_ASSERT(pContext != NULL); - MA_ASSERT(ppDeviceEnumerator != NULL); - - *ppDeviceEnumerator = NULL; /* Safety. */ - - hr = ma_CoCreateInstance(pContext, &MA_CLSID_MMDeviceEnumerator, NULL, CLSCTX_ALL, &MA_IID_IMMDeviceEnumerator, (void**)&pDeviceEnumerator); - if (FAILED(hr)) { - ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to create device enumerator."); - return ma_result_from_HRESULT(hr); - } - - *ppDeviceEnumerator = pDeviceEnumerator; - - return MA_SUCCESS; -} - -static WCHAR* ma_context_get_default_device_id_from_IMMDeviceEnumerator__wasapi(ma_context* pContext, ma_IMMDeviceEnumerator* pDeviceEnumerator, ma_device_type deviceType) -{ - HRESULT hr; - ma_IMMDevice* pMMDefaultDevice = NULL; - WCHAR* pDefaultDeviceID = NULL; - ma_EDataFlow dataFlow; - ma_ERole role; - - MA_ASSERT(pContext != NULL); - MA_ASSERT(pDeviceEnumerator != NULL); - - (void)pContext; - - /* Grab the EDataFlow type from the device type. */ - dataFlow = ma_device_type_to_EDataFlow(deviceType); - - /* The role is always eConsole, but we may make this configurable later. */ - role = ma_eConsole; - - hr = ma_IMMDeviceEnumerator_GetDefaultAudioEndpoint(pDeviceEnumerator, dataFlow, role, &pMMDefaultDevice); - if (FAILED(hr)) { - return NULL; - } - - hr = ma_IMMDevice_GetId(pMMDefaultDevice, &pDefaultDeviceID); - - ma_IMMDevice_Release(pMMDefaultDevice); - pMMDefaultDevice = NULL; - - if (FAILED(hr)) { - return NULL; - } - - return pDefaultDeviceID; -} - -static WCHAR* ma_context_get_default_device_id__wasapi(ma_context* pContext, ma_device_type deviceType) /* Free the returned pointer with ma_CoTaskMemFree() */ -{ - ma_result result; - ma_IMMDeviceEnumerator* pDeviceEnumerator; - WCHAR* pDefaultDeviceID = NULL; - - MA_ASSERT(pContext != NULL); - - result = ma_context_create_IMMDeviceEnumerator__wasapi(pContext, &pDeviceEnumerator); - if (result != MA_SUCCESS) { - return NULL; - } - - pDefaultDeviceID = ma_context_get_default_device_id_from_IMMDeviceEnumerator__wasapi(pContext, pDeviceEnumerator, deviceType); - - ma_IMMDeviceEnumerator_Release(pDeviceEnumerator); - return pDefaultDeviceID; -} - -static ma_result ma_context_get_MMDevice__wasapi(ma_context* pContext, ma_device_type deviceType, const ma_device_id* pDeviceID, ma_IMMDevice** ppMMDevice) -{ - ma_IMMDeviceEnumerator* pDeviceEnumerator; - HRESULT hr; - - MA_ASSERT(pContext != NULL); - MA_ASSERT(ppMMDevice != NULL); - - /* - This weird COM init/uninit here is a hack to work around a crash when changing devices. What is happening is - WASAPI fires a callback from another thread when the device is changed. It's from that thread where this - function is getting called. What I'm suspecting is that the other thread is not initializing COM which in turn - results in CoCreateInstance() failing. - - The community has reported that this seems to fix the crash. There are future plans to move all WASAPI operation - over to a single thread to make everything safer, but in the meantime while we wait for that to come online I'm - happy enough to use this hack instead. - */ - ma_CoInitializeEx(pContext, NULL, MA_COINIT_VALUE); - { - hr = ma_CoCreateInstance(pContext, &MA_CLSID_MMDeviceEnumerator, NULL, CLSCTX_ALL, &MA_IID_IMMDeviceEnumerator, (void**)&pDeviceEnumerator); - } - ma_CoUninitialize(pContext); - - if (FAILED(hr)) { /* <-- This is checking the call above to ma_CoCreateInstance(). */ - ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to create IMMDeviceEnumerator.\n"); - return ma_result_from_HRESULT(hr); - } - - if (pDeviceID == NULL) { - hr = ma_IMMDeviceEnumerator_GetDefaultAudioEndpoint(pDeviceEnumerator, (deviceType == ma_device_type_capture) ? ma_eCapture : ma_eRender, ma_eConsole, ppMMDevice); - } else { - hr = ma_IMMDeviceEnumerator_GetDevice(pDeviceEnumerator, pDeviceID->wasapi, ppMMDevice); - } - - ma_IMMDeviceEnumerator_Release(pDeviceEnumerator); - if (FAILED(hr)) { - ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to retrieve IMMDevice.\n"); - return ma_result_from_HRESULT(hr); - } - - return MA_SUCCESS; -} - -static ma_result ma_context_get_device_id_from_MMDevice__wasapi(ma_context* pContext, ma_IMMDevice* pMMDevice, ma_device_id* pDeviceID) -{ - WCHAR* pDeviceIDString; - HRESULT hr; - - MA_ASSERT(pDeviceID != NULL); - - hr = ma_IMMDevice_GetId(pMMDevice, &pDeviceIDString); - if (SUCCEEDED(hr)) { - size_t idlen = ma_strlen_WCHAR(pDeviceIDString); - if (idlen+1 > ma_countof(pDeviceID->wasapi)) { - ma_CoTaskMemFree(pContext, pDeviceIDString); - MA_ASSERT(MA_FALSE); /* NOTE: If this is triggered, please report it. It means the format of the ID must have changed and is too long to fit in our fixed sized buffer. */ - return MA_ERROR; - } - - MA_COPY_MEMORY(pDeviceID->wasapi, pDeviceIDString, idlen * sizeof(wchar_t)); - pDeviceID->wasapi[idlen] = '\0'; - - ma_CoTaskMemFree(pContext, pDeviceIDString); - - return MA_SUCCESS; - } - - return MA_ERROR; -} - -static ma_result ma_context_get_device_info_from_MMDevice__wasapi(ma_context* pContext, ma_IMMDevice* pMMDevice, WCHAR* pDefaultDeviceID, ma_bool32 onlySimpleInfo, ma_device_info* pInfo) -{ - ma_result result; - HRESULT hr; - - MA_ASSERT(pContext != NULL); - MA_ASSERT(pMMDevice != NULL); - MA_ASSERT(pInfo != NULL); - - /* ID. */ - result = ma_context_get_device_id_from_MMDevice__wasapi(pContext, pMMDevice, &pInfo->id); - if (result == MA_SUCCESS) { - if (pDefaultDeviceID != NULL) { - if (ma_strcmp_WCHAR(pInfo->id.wasapi, pDefaultDeviceID) == 0) { - pInfo->isDefault = MA_TRUE; - } - } - } - - /* Description / Friendly Name */ - { - ma_IPropertyStore *pProperties; - hr = ma_IMMDevice_OpenPropertyStore(pMMDevice, STGM_READ, &pProperties); - if (SUCCEEDED(hr)) { - MA_PROPVARIANT var; - - ma_PropVariantInit(&var); - hr = ma_IPropertyStore_GetValue(pProperties, &MA_PKEY_Device_FriendlyName, &var); - if (SUCCEEDED(hr)) { - WideCharToMultiByte(CP_UTF8, 0, var.pwszVal, -1, pInfo->name, sizeof(pInfo->name), 0, FALSE); - ma_PropVariantClear(pContext, &var); - } - - ma_IPropertyStore_Release(pProperties); - } - } - - /* Format */ - if (!onlySimpleInfo) { - ma_IAudioClient* pAudioClient; - hr = ma_IMMDevice_Activate(pMMDevice, &MA_IID_IAudioClient, CLSCTX_ALL, NULL, (void**)&pAudioClient); - if (SUCCEEDED(hr)) { - result = ma_context_get_device_info_from_IAudioClient__wasapi(pContext, pMMDevice, pAudioClient, pInfo); - - ma_IAudioClient_Release(pAudioClient); - return result; - } else { - ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to activate audio client for device info retrieval."); - return ma_result_from_HRESULT(hr); - } - } - - return MA_SUCCESS; -} - -static ma_result ma_context_enumerate_devices_by_type__wasapi(ma_context* pContext, ma_IMMDeviceEnumerator* pDeviceEnumerator, ma_device_type deviceType, ma_enum_devices_callback_proc callback, void* pUserData) -{ - ma_result result = MA_SUCCESS; - UINT deviceCount; - HRESULT hr; - ma_uint32 iDevice; - WCHAR* pDefaultDeviceID = NULL; - ma_IMMDeviceCollection* pDeviceCollection = NULL; - - MA_ASSERT(pContext != NULL); - MA_ASSERT(callback != NULL); - - /* Grab the default device. We use this to know whether or not flag the returned device info as being the default. */ - pDefaultDeviceID = ma_context_get_default_device_id_from_IMMDeviceEnumerator__wasapi(pContext, pDeviceEnumerator, deviceType); - - /* We need to enumerate the devices which returns a device collection. */ - hr = ma_IMMDeviceEnumerator_EnumAudioEndpoints(pDeviceEnumerator, ma_device_type_to_EDataFlow(deviceType), MA_MM_DEVICE_STATE_ACTIVE, &pDeviceCollection); - if (SUCCEEDED(hr)) { - hr = ma_IMMDeviceCollection_GetCount(pDeviceCollection, &deviceCount); - if (FAILED(hr)) { - ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to get device count.\n"); - result = ma_result_from_HRESULT(hr); - goto done; - } - - for (iDevice = 0; iDevice < deviceCount; ++iDevice) { - ma_device_info deviceInfo; - ma_IMMDevice* pMMDevice; - - MA_ZERO_OBJECT(&deviceInfo); - - hr = ma_IMMDeviceCollection_Item(pDeviceCollection, iDevice, &pMMDevice); - if (SUCCEEDED(hr)) { - result = ma_context_get_device_info_from_MMDevice__wasapi(pContext, pMMDevice, pDefaultDeviceID, MA_TRUE, &deviceInfo); /* MA_TRUE = onlySimpleInfo. */ - - ma_IMMDevice_Release(pMMDevice); - if (result == MA_SUCCESS) { - ma_bool32 cbResult = callback(pContext, deviceType, &deviceInfo, pUserData); - if (cbResult == MA_FALSE) { - break; - } - } - } - } - } - -done: - if (pDefaultDeviceID != NULL) { - ma_CoTaskMemFree(pContext, pDefaultDeviceID); - pDefaultDeviceID = NULL; - } - - if (pDeviceCollection != NULL) { - ma_IMMDeviceCollection_Release(pDeviceCollection); - pDeviceCollection = NULL; - } - - return result; -} - -static ma_result ma_context_get_IAudioClient_Desktop__wasapi(ma_context* pContext, ma_device_type deviceType, const ma_device_id* pDeviceID, MA_PROPVARIANT* pActivationParams, ma_IAudioClient** ppAudioClient, ma_IMMDevice** ppMMDevice) -{ - ma_result result; - HRESULT hr; - - MA_ASSERT(pContext != NULL); - MA_ASSERT(ppAudioClient != NULL); - MA_ASSERT(ppMMDevice != NULL); - - result = ma_context_get_MMDevice__wasapi(pContext, deviceType, pDeviceID, ppMMDevice); - if (result != MA_SUCCESS) { - return result; - } - - hr = ma_IMMDevice_Activate(*ppMMDevice, &MA_IID_IAudioClient, CLSCTX_ALL, pActivationParams, (void**)ppAudioClient); - if (FAILED(hr)) { - return ma_result_from_HRESULT(hr); - } - - return MA_SUCCESS; -} -#else -static ma_result ma_context_get_IAudioClient_UWP__wasapi(ma_context* pContext, ma_device_type deviceType, const ma_device_id* pDeviceID, MA_PROPVARIANT* pActivationParams, ma_IAudioClient** ppAudioClient, ma_IUnknown** ppActivatedInterface) -{ - ma_IActivateAudioInterfaceAsyncOperation *pAsyncOp = NULL; - ma_completion_handler_uwp completionHandler; - IID iid; - WCHAR* iidStr; - HRESULT hr; - ma_result result; - HRESULT activateResult; - ma_IUnknown* pActivatedInterface; - - MA_ASSERT(pContext != NULL); - MA_ASSERT(ppAudioClient != NULL); - - if (pDeviceID != NULL) { - iidStr = (WCHAR*)pDeviceID->wasapi; - } else { - if (deviceType == ma_device_type_capture) { - iid = MA_IID_DEVINTERFACE_AUDIO_CAPTURE; - } else { - iid = MA_IID_DEVINTERFACE_AUDIO_RENDER; - } - - #if defined(__cplusplus) - hr = StringFromIID(iid, &iidStr); - #else - hr = StringFromIID(&iid, &iidStr); - #endif - if (FAILED(hr)) { - ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to convert device IID to string for ActivateAudioInterfaceAsync(). Out of memory.\n"); - return ma_result_from_HRESULT(hr); - } - } - - result = ma_completion_handler_uwp_init(&completionHandler); - if (result != MA_SUCCESS) { - ma_CoTaskMemFree(pContext, iidStr); - ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to create event for waiting for ActivateAudioInterfaceAsync().\n"); - return result; - } - - hr = ((MA_PFN_ActivateAudioInterfaceAsync)pContext->wasapi.ActivateAudioInterfaceAsync)(iidStr, &MA_IID_IAudioClient, pActivationParams, (ma_IActivateAudioInterfaceCompletionHandler*)&completionHandler, (ma_IActivateAudioInterfaceAsyncOperation**)&pAsyncOp); - if (FAILED(hr)) { - ma_completion_handler_uwp_uninit(&completionHandler); - ma_CoTaskMemFree(pContext, iidStr); - ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[WASAPI] ActivateAudioInterfaceAsync() failed.\n"); - return ma_result_from_HRESULT(hr); - } - - if (pDeviceID == NULL) { - ma_CoTaskMemFree(pContext, iidStr); - } - - /* Wait for the async operation for finish. */ - ma_completion_handler_uwp_wait(&completionHandler); - ma_completion_handler_uwp_uninit(&completionHandler); - - hr = ma_IActivateAudioInterfaceAsyncOperation_GetActivateResult(pAsyncOp, &activateResult, &pActivatedInterface); - ma_IActivateAudioInterfaceAsyncOperation_Release(pAsyncOp); - - if (FAILED(hr) || FAILED(activateResult)) { - ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to activate device.\n"); - return FAILED(hr) ? ma_result_from_HRESULT(hr) : ma_result_from_HRESULT(activateResult); - } - - /* Here is where we grab the IAudioClient interface. */ - hr = ma_IUnknown_QueryInterface(pActivatedInterface, &MA_IID_IAudioClient, (void**)ppAudioClient); - if (FAILED(hr)) { - ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to query IAudioClient interface.\n"); - return ma_result_from_HRESULT(hr); - } - - if (ppActivatedInterface) { - *ppActivatedInterface = pActivatedInterface; - } else { - ma_IUnknown_Release(pActivatedInterface); - } - - return MA_SUCCESS; -} -#endif - - -/* https://docs.microsoft.com/en-us/windows/win32/api/audioclientactivationparams/ne-audioclientactivationparams-audioclient_activation_type */ -typedef enum -{ - MA_AUDIOCLIENT_ACTIVATION_TYPE_DEFAULT, - MA_AUDIOCLIENT_ACTIVATION_TYPE_PROCESS_LOOPBACK -} MA_AUDIOCLIENT_ACTIVATION_TYPE; - -/* https://docs.microsoft.com/en-us/windows/win32/api/audioclientactivationparams/ne-audioclientactivationparams-process_loopback_mode */ -typedef enum -{ - MA_PROCESS_LOOPBACK_MODE_INCLUDE_TARGET_PROCESS_TREE, - MA_PROCESS_LOOPBACK_MODE_EXCLUDE_TARGET_PROCESS_TREE -} MA_PROCESS_LOOPBACK_MODE; - -/* https://docs.microsoft.com/en-us/windows/win32/api/audioclientactivationparams/ns-audioclientactivationparams-audioclient_process_loopback_params */ -typedef struct -{ - DWORD TargetProcessId; - MA_PROCESS_LOOPBACK_MODE ProcessLoopbackMode; -} MA_AUDIOCLIENT_PROCESS_LOOPBACK_PARAMS; - -#if defined(_MSC_VER) && !defined(__clang__) - #pragma warning(push) - #pragma warning(disable:4201) /* nonstandard extension used: nameless struct/union */ -#elif defined(__clang__) || (defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 8))) - #pragma GCC diagnostic push - #pragma GCC diagnostic ignored "-Wpedantic" /* For ISO C99 doesn't support unnamed structs/unions [-Wpedantic] */ - #if defined(__clang__) - #pragma GCC diagnostic ignored "-Wc11-extensions" /* anonymous unions are a C11 extension */ - #endif -#endif -/* https://docs.microsoft.com/en-us/windows/win32/api/audioclientactivationparams/ns-audioclientactivationparams-audioclient_activation_params */ -typedef struct -{ - MA_AUDIOCLIENT_ACTIVATION_TYPE ActivationType; - union - { - MA_AUDIOCLIENT_PROCESS_LOOPBACK_PARAMS ProcessLoopbackParams; - }; -} MA_AUDIOCLIENT_ACTIVATION_PARAMS; -#if defined(_MSC_VER) && !defined(__clang__) - #pragma warning(pop) -#elif defined(__clang__) || (defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 8))) - #pragma GCC diagnostic pop -#endif - -#define MA_VIRTUAL_AUDIO_DEVICE_PROCESS_LOOPBACK L"VAD\\Process_Loopback" - -static ma_result ma_context_get_IAudioClient__wasapi(ma_context* pContext, ma_device_type deviceType, const ma_device_id* pDeviceID, ma_uint32 loopbackProcessID, ma_bool32 loopbackProcessExclude, ma_IAudioClient** ppAudioClient, ma_WASAPIDeviceInterface** ppDeviceInterface) -{ - ma_result result; - ma_bool32 usingProcessLoopback = MA_FALSE; - MA_AUDIOCLIENT_ACTIVATION_PARAMS audioclientActivationParams; - MA_PROPVARIANT activationParams; - MA_PROPVARIANT* pActivationParams = NULL; - ma_device_id virtualDeviceID; - - /* Activation parameters specific to loopback mode. Note that process-specific loopback will only work when a default device ID is specified. */ - if (deviceType == ma_device_type_loopback && loopbackProcessID != 0 && pDeviceID == NULL) { - usingProcessLoopback = MA_TRUE; - } - - if (usingProcessLoopback) { - MA_ZERO_OBJECT(&audioclientActivationParams); - audioclientActivationParams.ActivationType = MA_AUDIOCLIENT_ACTIVATION_TYPE_PROCESS_LOOPBACK; - audioclientActivationParams.ProcessLoopbackParams.ProcessLoopbackMode = (loopbackProcessExclude) ? MA_PROCESS_LOOPBACK_MODE_EXCLUDE_TARGET_PROCESS_TREE : MA_PROCESS_LOOPBACK_MODE_INCLUDE_TARGET_PROCESS_TREE; - audioclientActivationParams.ProcessLoopbackParams.TargetProcessId = (DWORD)loopbackProcessID; - - ma_PropVariantInit(&activationParams); - activationParams.vt = MA_VT_BLOB; - activationParams.blob.cbSize = sizeof(audioclientActivationParams); - activationParams.blob.pBlobData = (BYTE*)&audioclientActivationParams; - pActivationParams = &activationParams; - - /* When requesting a specific device ID we need to use a special device ID. */ - MA_COPY_MEMORY(virtualDeviceID.wasapi, MA_VIRTUAL_AUDIO_DEVICE_PROCESS_LOOPBACK, (wcslen(MA_VIRTUAL_AUDIO_DEVICE_PROCESS_LOOPBACK) + 1) * sizeof(wchar_t)); /* +1 for the null terminator. */ - pDeviceID = &virtualDeviceID; - } else { - pActivationParams = NULL; /* No activation parameters required. */ - } - -#if defined(MA_WIN32_DESKTOP) || defined(MA_WIN32_GDK) - result = ma_context_get_IAudioClient_Desktop__wasapi(pContext, deviceType, pDeviceID, pActivationParams, ppAudioClient, ppDeviceInterface); -#else - result = ma_context_get_IAudioClient_UWP__wasapi(pContext, deviceType, pDeviceID, pActivationParams, ppAudioClient, ppDeviceInterface); -#endif - - /* - If loopback mode was requested with a process ID and initialization failed, it could be because it's - trying to run on an older version of Windows where it's not supported. We need to let the caller - know about this with a log message. - */ - if (result != MA_SUCCESS) { - if (usingProcessLoopback) { - ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[WASAPI] Loopback mode requested to %s process ID %u, but initialization failed. Support for this feature begins with Windows 10 Build 20348. Confirm your version of Windows or consider not using process-specific loopback.\n", (loopbackProcessExclude) ? "exclude" : "include", loopbackProcessID); - } - } - - return result; -} - - -static ma_result ma_context_enumerate_devices__wasapi(ma_context* pContext, ma_enum_devices_callback_proc callback, void* pUserData) -{ - /* Different enumeration for desktop and UWP. */ -#if defined(MA_WIN32_DESKTOP) || defined(MA_WIN32_GDK) - /* Desktop */ - HRESULT hr; - ma_IMMDeviceEnumerator* pDeviceEnumerator; - - hr = ma_CoCreateInstance(pContext, &MA_CLSID_MMDeviceEnumerator, NULL, CLSCTX_ALL, &MA_IID_IMMDeviceEnumerator, (void**)&pDeviceEnumerator); - if (FAILED(hr)) { - ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to create device enumerator."); - return ma_result_from_HRESULT(hr); - } - - ma_context_enumerate_devices_by_type__wasapi(pContext, pDeviceEnumerator, ma_device_type_playback, callback, pUserData); - ma_context_enumerate_devices_by_type__wasapi(pContext, pDeviceEnumerator, ma_device_type_capture, callback, pUserData); - - ma_IMMDeviceEnumerator_Release(pDeviceEnumerator); -#else - /* - UWP - - The MMDevice API is only supported on desktop applications. For now, while I'm still figuring out how to properly enumerate - over devices without using MMDevice, I'm restricting devices to defaults. - - Hint: DeviceInformation::FindAllAsync() with DeviceClass.AudioCapture/AudioRender. https://blogs.windows.com/buildingapps/2014/05/15/real-time-audio-in-windows-store-and-windows-phone-apps/ - */ - if (callback) { - ma_bool32 cbResult = MA_TRUE; - - /* Playback. */ - if (cbResult) { - ma_device_info deviceInfo; - MA_ZERO_OBJECT(&deviceInfo); - ma_strncpy_s(deviceInfo.name, sizeof(deviceInfo.name), MA_DEFAULT_PLAYBACK_DEVICE_NAME, (size_t)-1); - deviceInfo.isDefault = MA_TRUE; - cbResult = callback(pContext, ma_device_type_playback, &deviceInfo, pUserData); - } - - /* Capture. */ - if (cbResult) { - ma_device_info deviceInfo; - MA_ZERO_OBJECT(&deviceInfo); - ma_strncpy_s(deviceInfo.name, sizeof(deviceInfo.name), MA_DEFAULT_CAPTURE_DEVICE_NAME, (size_t)-1); - deviceInfo.isDefault = MA_TRUE; - cbResult = callback(pContext, ma_device_type_capture, &deviceInfo, pUserData); - } - } -#endif - - return MA_SUCCESS; -} - -static ma_result ma_context_get_device_info__wasapi(ma_context* pContext, ma_device_type deviceType, const ma_device_id* pDeviceID, ma_device_info* pDeviceInfo) -{ -#if defined(MA_WIN32_DESKTOP) || defined(MA_WIN32_GDK) - ma_result result; - ma_IMMDevice* pMMDevice = NULL; - WCHAR* pDefaultDeviceID = NULL; - - result = ma_context_get_MMDevice__wasapi(pContext, deviceType, pDeviceID, &pMMDevice); - if (result != MA_SUCCESS) { - return result; - } - - /* We need the default device ID so we can set the isDefault flag in the device info. */ - pDefaultDeviceID = ma_context_get_default_device_id__wasapi(pContext, deviceType); - - result = ma_context_get_device_info_from_MMDevice__wasapi(pContext, pMMDevice, pDefaultDeviceID, MA_FALSE, pDeviceInfo); /* MA_FALSE = !onlySimpleInfo. */ - - if (pDefaultDeviceID != NULL) { - ma_CoTaskMemFree(pContext, pDefaultDeviceID); - pDefaultDeviceID = NULL; - } - - ma_IMMDevice_Release(pMMDevice); - - return result; -#else - ma_IAudioClient* pAudioClient; - ma_result result; - - /* UWP currently only uses default devices. */ - if (deviceType == ma_device_type_playback) { - ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), MA_DEFAULT_PLAYBACK_DEVICE_NAME, (size_t)-1); - } else { - ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), MA_DEFAULT_CAPTURE_DEVICE_NAME, (size_t)-1); - } - - result = ma_context_get_IAudioClient_UWP__wasapi(pContext, deviceType, pDeviceID, NULL, &pAudioClient, NULL); - if (result != MA_SUCCESS) { - return result; - } - - result = ma_context_get_device_info_from_IAudioClient__wasapi(pContext, NULL, pAudioClient, pDeviceInfo); - - pDeviceInfo->isDefault = MA_TRUE; /* UWP only supports default devices. */ - - ma_IAudioClient_Release(pAudioClient); - return result; -#endif -} - -static ma_result ma_device_uninit__wasapi(ma_device* pDevice) -{ - MA_ASSERT(pDevice != NULL); - - #if defined(MA_WIN32_DESKTOP) || defined(MA_WIN32_GDK) - { - if (pDevice->wasapi.pDeviceEnumerator) { - ((ma_IMMDeviceEnumerator*)pDevice->wasapi.pDeviceEnumerator)->lpVtbl->UnregisterEndpointNotificationCallback((ma_IMMDeviceEnumerator*)pDevice->wasapi.pDeviceEnumerator, &pDevice->wasapi.notificationClient); - ma_IMMDeviceEnumerator_Release((ma_IMMDeviceEnumerator*)pDevice->wasapi.pDeviceEnumerator); - } - - ma_mutex_uninit(&pDevice->wasapi.rerouteLock); - } - #endif - - if (pDevice->wasapi.pRenderClient) { - if (pDevice->wasapi.pMappedBufferPlayback != NULL) { - ma_IAudioRenderClient_ReleaseBuffer((ma_IAudioRenderClient*)pDevice->wasapi.pRenderClient, pDevice->wasapi.mappedBufferPlaybackCap, 0); - pDevice->wasapi.pMappedBufferPlayback = NULL; - pDevice->wasapi.mappedBufferPlaybackCap = 0; - pDevice->wasapi.mappedBufferPlaybackLen = 0; - } - - ma_IAudioRenderClient_Release((ma_IAudioRenderClient*)pDevice->wasapi.pRenderClient); - } - if (pDevice->wasapi.pCaptureClient) { - if (pDevice->wasapi.pMappedBufferCapture != NULL) { - ma_IAudioCaptureClient_ReleaseBuffer((ma_IAudioCaptureClient*)pDevice->wasapi.pCaptureClient, pDevice->wasapi.mappedBufferCaptureCap); - pDevice->wasapi.pMappedBufferCapture = NULL; - pDevice->wasapi.mappedBufferCaptureCap = 0; - pDevice->wasapi.mappedBufferCaptureLen = 0; - } - - ma_IAudioCaptureClient_Release((ma_IAudioCaptureClient*)pDevice->wasapi.pCaptureClient); - } - - if (pDevice->wasapi.pAudioClientPlayback) { - ma_IAudioClient_Release((ma_IAudioClient*)pDevice->wasapi.pAudioClientPlayback); - } - if (pDevice->wasapi.pAudioClientCapture) { - ma_IAudioClient_Release((ma_IAudioClient*)pDevice->wasapi.pAudioClientCapture); - } - - if (pDevice->wasapi.hEventPlayback) { - CloseHandle((HANDLE)pDevice->wasapi.hEventPlayback); - } - if (pDevice->wasapi.hEventCapture) { - CloseHandle((HANDLE)pDevice->wasapi.hEventCapture); - } - - return MA_SUCCESS; -} - - -typedef struct -{ - /* Input. */ - ma_format formatIn; - ma_uint32 channelsIn; - ma_uint32 sampleRateIn; - ma_channel channelMapIn[MA_MAX_CHANNELS]; - ma_uint32 periodSizeInFramesIn; - ma_uint32 periodSizeInMillisecondsIn; - ma_uint32 periodsIn; - ma_share_mode shareMode; - ma_performance_profile performanceProfile; - ma_bool32 noAutoConvertSRC; - ma_bool32 noDefaultQualitySRC; - ma_bool32 noHardwareOffloading; - ma_uint32 loopbackProcessID; - ma_bool32 loopbackProcessExclude; - - /* Output. */ - ma_IAudioClient* pAudioClient; - ma_IAudioRenderClient* pRenderClient; - ma_IAudioCaptureClient* pCaptureClient; - ma_format formatOut; - ma_uint32 channelsOut; - ma_uint32 sampleRateOut; - ma_channel channelMapOut[MA_MAX_CHANNELS]; - ma_uint32 periodSizeInFramesOut; - ma_uint32 periodsOut; - ma_bool32 usingAudioClient3; - char deviceName[256]; - ma_device_id id; -} ma_device_init_internal_data__wasapi; - -static ma_result ma_device_init_internal__wasapi(ma_context* pContext, ma_device_type deviceType, const ma_device_id* pDeviceID, ma_device_init_internal_data__wasapi* pData) -{ - HRESULT hr; - ma_result result = MA_SUCCESS; - const char* errorMsg = ""; - MA_AUDCLNT_SHAREMODE shareMode = MA_AUDCLNT_SHAREMODE_SHARED; - DWORD streamFlags = 0; - MA_REFERENCE_TIME periodDurationInMicroseconds; - ma_bool32 wasInitializedUsingIAudioClient3 = MA_FALSE; - MA_WAVEFORMATEXTENSIBLE wf; - ma_WASAPIDeviceInterface* pDeviceInterface = NULL; - ma_IAudioClient2* pAudioClient2; - ma_uint32 nativeSampleRate; - ma_bool32 usingProcessLoopback = MA_FALSE; - - MA_ASSERT(pContext != NULL); - MA_ASSERT(pData != NULL); - - /* This function is only used to initialize one device type: either playback, capture or loopback. Never full-duplex. */ - if (deviceType == ma_device_type_duplex) { - return MA_INVALID_ARGS; - } - - usingProcessLoopback = deviceType == ma_device_type_loopback && pData->loopbackProcessID != 0 && pDeviceID == NULL; - - pData->pAudioClient = NULL; - pData->pRenderClient = NULL; - pData->pCaptureClient = NULL; - - streamFlags = MA_AUDCLNT_STREAMFLAGS_EVENTCALLBACK; - if (!pData->noAutoConvertSRC && pData->sampleRateIn != 0 && pData->shareMode != ma_share_mode_exclusive) { /* <-- Exclusive streams must use the native sample rate. */ - streamFlags |= MA_AUDCLNT_STREAMFLAGS_AUTOCONVERTPCM; - } - if (!pData->noDefaultQualitySRC && pData->sampleRateIn != 0 && (streamFlags & MA_AUDCLNT_STREAMFLAGS_AUTOCONVERTPCM) != 0) { - streamFlags |= MA_AUDCLNT_STREAMFLAGS_SRC_DEFAULT_QUALITY; - } - if (deviceType == ma_device_type_loopback) { - streamFlags |= MA_AUDCLNT_STREAMFLAGS_LOOPBACK; - } - - result = ma_context_get_IAudioClient__wasapi(pContext, deviceType, pDeviceID, pData->loopbackProcessID, pData->loopbackProcessExclude, &pData->pAudioClient, &pDeviceInterface); - if (result != MA_SUCCESS) { - goto done; - } - - MA_ZERO_OBJECT(&wf); - - /* Try enabling hardware offloading. */ - if (!pData->noHardwareOffloading) { - hr = ma_IAudioClient_QueryInterface(pData->pAudioClient, &MA_IID_IAudioClient2, (void**)&pAudioClient2); - if (SUCCEEDED(hr)) { - BOOL isHardwareOffloadingSupported = 0; - hr = ma_IAudioClient2_IsOffloadCapable(pAudioClient2, MA_AudioCategory_Other, &isHardwareOffloadingSupported); - if (SUCCEEDED(hr) && isHardwareOffloadingSupported) { - ma_AudioClientProperties clientProperties; - MA_ZERO_OBJECT(&clientProperties); - clientProperties.cbSize = sizeof(clientProperties); - clientProperties.bIsOffload = 1; - clientProperties.eCategory = MA_AudioCategory_Other; - ma_IAudioClient2_SetClientProperties(pAudioClient2, &clientProperties); - } - - pAudioClient2->lpVtbl->Release(pAudioClient2); - } - } - - /* Here is where we try to determine the best format to use with the device. If the client if wanting exclusive mode, first try finding the best format for that. If this fails, fall back to shared mode. */ - result = MA_FORMAT_NOT_SUPPORTED; - if (pData->shareMode == ma_share_mode_exclusive) { - #if defined(MA_WIN32_DESKTOP) || defined(MA_WIN32_GDK) - /* In exclusive mode on desktop we always use the backend's native format. */ - ma_IPropertyStore* pStore = NULL; - hr = ma_IMMDevice_OpenPropertyStore(pDeviceInterface, STGM_READ, &pStore); - if (SUCCEEDED(hr)) { - MA_PROPVARIANT prop; - ma_PropVariantInit(&prop); - hr = ma_IPropertyStore_GetValue(pStore, &MA_PKEY_AudioEngine_DeviceFormat, &prop); - if (SUCCEEDED(hr)) { - MA_WAVEFORMATEX* pActualFormat = (MA_WAVEFORMATEX*)prop.blob.pBlobData; - hr = ma_IAudioClient_IsFormatSupported((ma_IAudioClient*)pData->pAudioClient, MA_AUDCLNT_SHAREMODE_EXCLUSIVE, pActualFormat, NULL); - if (SUCCEEDED(hr)) { - MA_COPY_MEMORY(&wf, pActualFormat, sizeof(MA_WAVEFORMATEXTENSIBLE)); - } - - ma_PropVariantClear(pContext, &prop); - } - - ma_IPropertyStore_Release(pStore); - } - #else - /* - I do not know how to query the device's native format on UWP so for now I'm just disabling support for - exclusive mode. The alternative is to enumerate over different formats and check IsFormatSupported() - until you find one that works. - - TODO: Add support for exclusive mode to UWP. - */ - hr = S_FALSE; - #endif - - if (hr == S_OK) { - shareMode = MA_AUDCLNT_SHAREMODE_EXCLUSIVE; - result = MA_SUCCESS; - } else { - result = MA_SHARE_MODE_NOT_SUPPORTED; - } - } else { - /* In shared mode we are always using the format reported by the operating system. */ - MA_WAVEFORMATEXTENSIBLE* pNativeFormat = NULL; - hr = ma_IAudioClient_GetMixFormat((ma_IAudioClient*)pData->pAudioClient, (MA_WAVEFORMATEX**)&pNativeFormat); - if (hr != S_OK) { - /* When using process-specific loopback, GetMixFormat() seems to always fail. */ - if (usingProcessLoopback) { - wf.wFormatTag = WAVE_FORMAT_IEEE_FLOAT; - wf.nChannels = 2; - wf.nSamplesPerSec = 44100; - wf.wBitsPerSample = 32; - wf.nBlockAlign = wf.nChannels * wf.wBitsPerSample / 8; - wf.nAvgBytesPerSec = wf.nSamplesPerSec * wf.nBlockAlign; - wf.cbSize = sizeof(MA_WAVEFORMATEX); - - result = MA_SUCCESS; - } else { - result = MA_FORMAT_NOT_SUPPORTED; - } - } else { - /* - I've seen cases where cbSize will be set to sizeof(WAVEFORMATEX) even though the structure itself - is given the format tag of WAVE_FORMAT_EXTENSIBLE. If the format tag is WAVE_FORMAT_EXTENSIBLE - want to make sure we copy the whole WAVEFORMATEXTENSIBLE structure. Otherwise we'll have to be - safe and only copy the WAVEFORMATEX part. - */ - if (pNativeFormat->wFormatTag == WAVE_FORMAT_EXTENSIBLE) { - MA_COPY_MEMORY(&wf, pNativeFormat, sizeof(MA_WAVEFORMATEXTENSIBLE)); - } else { - /* I've seen a case where cbSize was set to 0. Assume sizeof(WAVEFORMATEX) in this case. */ - size_t cbSize = pNativeFormat->cbSize; - if (cbSize == 0) { - cbSize = sizeof(MA_WAVEFORMATEX); - } - - /* Make sure we don't copy more than the capacity of `wf`. */ - if (cbSize > sizeof(wf)) { - cbSize = sizeof(wf); - } - - MA_COPY_MEMORY(&wf, pNativeFormat, cbSize); - } - - result = MA_SUCCESS; - } - - ma_CoTaskMemFree(pContext, pNativeFormat); - - shareMode = MA_AUDCLNT_SHAREMODE_SHARED; - } - - /* Return an error if we still haven't found a format. */ - if (result != MA_SUCCESS) { - errorMsg = "[WASAPI] Failed to find best device mix format."; - goto done; - } - - /* - Override the native sample rate with the one requested by the caller, but only if we're not using the default sample rate. We'll use - WASAPI to perform the sample rate conversion. - */ - nativeSampleRate = wf.nSamplesPerSec; - if (streamFlags & MA_AUDCLNT_STREAMFLAGS_AUTOCONVERTPCM) { - wf.nSamplesPerSec = (pData->sampleRateIn != 0) ? pData->sampleRateIn : MA_DEFAULT_SAMPLE_RATE; - wf.nAvgBytesPerSec = wf.nSamplesPerSec * wf.nBlockAlign; - } - - pData->formatOut = ma_format_from_WAVEFORMATEX((MA_WAVEFORMATEX*)&wf); - if (pData->formatOut == ma_format_unknown) { - /* - The format isn't supported. This is almost certainly because the exclusive mode format isn't supported by miniaudio. We need to return MA_SHARE_MODE_NOT_SUPPORTED - in this case so that the caller can detect it and fall back to shared mode if desired. We should never get here if shared mode was requested, but just for - completeness we'll check for it and return MA_FORMAT_NOT_SUPPORTED. - */ - if (shareMode == MA_AUDCLNT_SHAREMODE_EXCLUSIVE) { - result = MA_SHARE_MODE_NOT_SUPPORTED; - } else { - result = MA_FORMAT_NOT_SUPPORTED; - } - - errorMsg = "[WASAPI] Native format not supported."; - goto done; - } - - pData->channelsOut = wf.nChannels; - pData->sampleRateOut = wf.nSamplesPerSec; - - /* - Get the internal channel map based on the channel mask. There is a possibility that GetMixFormat() returns - a WAVEFORMATEX instead of a WAVEFORMATEXTENSIBLE, in which case the channel mask will be undefined. In this - case we'll just use the default channel map. - */ - if (wf.wFormatTag == WAVE_FORMAT_EXTENSIBLE || wf.cbSize >= sizeof(MA_WAVEFORMATEXTENSIBLE)) { - ma_channel_mask_to_channel_map__win32(wf.dwChannelMask, pData->channelsOut, pData->channelMapOut); - } else { - ma_channel_map_init_standard(ma_standard_channel_map_microsoft, pData->channelMapOut, ma_countof(pData->channelMapOut), pData->channelsOut); - } - - /* Period size. */ - pData->periodsOut = (pData->periodsIn != 0) ? pData->periodsIn : MA_DEFAULT_PERIODS; - pData->periodSizeInFramesOut = pData->periodSizeInFramesIn; - if (pData->periodSizeInFramesOut == 0) { - if (pData->periodSizeInMillisecondsIn == 0) { - if (pData->performanceProfile == ma_performance_profile_low_latency) { - pData->periodSizeInFramesOut = ma_calculate_buffer_size_in_frames_from_milliseconds(MA_DEFAULT_PERIOD_SIZE_IN_MILLISECONDS_LOW_LATENCY, wf.nSamplesPerSec); - } else { - pData->periodSizeInFramesOut = ma_calculate_buffer_size_in_frames_from_milliseconds(MA_DEFAULT_PERIOD_SIZE_IN_MILLISECONDS_CONSERVATIVE, wf.nSamplesPerSec); - } - } else { - pData->periodSizeInFramesOut = ma_calculate_buffer_size_in_frames_from_milliseconds(pData->periodSizeInMillisecondsIn, wf.nSamplesPerSec); - } - } - - periodDurationInMicroseconds = ((ma_uint64)pData->periodSizeInFramesOut * 1000 * 1000) / wf.nSamplesPerSec; - - - /* Slightly different initialization for shared and exclusive modes. We try exclusive mode first, and if it fails, fall back to shared mode. */ - if (shareMode == MA_AUDCLNT_SHAREMODE_EXCLUSIVE) { - MA_REFERENCE_TIME bufferDuration = periodDurationInMicroseconds * pData->periodsOut * 10; - - /* - If the periodicity is too small, Initialize() will fail with AUDCLNT_E_INVALID_DEVICE_PERIOD. In this case we should just keep increasing - it and trying it again. - */ - hr = E_FAIL; - for (;;) { - hr = ma_IAudioClient_Initialize((ma_IAudioClient*)pData->pAudioClient, shareMode, streamFlags, bufferDuration, bufferDuration, (MA_WAVEFORMATEX*)&wf, NULL); - if (hr == MA_AUDCLNT_E_INVALID_DEVICE_PERIOD) { - if (bufferDuration > 500*10000) { - break; - } else { - if (bufferDuration == 0) { /* <-- Just a sanity check to prevent an infinite loop. Should never happen, but it makes me feel better. */ - break; - } - - bufferDuration = bufferDuration * 2; - continue; - } - } else { - break; - } - } - - if (hr == MA_AUDCLNT_E_BUFFER_SIZE_NOT_ALIGNED) { - ma_uint32 bufferSizeInFrames; - hr = ma_IAudioClient_GetBufferSize((ma_IAudioClient*)pData->pAudioClient, &bufferSizeInFrames); - if (SUCCEEDED(hr)) { - bufferDuration = (MA_REFERENCE_TIME)((10000.0 * 1000 / wf.nSamplesPerSec * bufferSizeInFrames) + 0.5); - - /* Unfortunately we need to release and re-acquire the audio client according to MSDN. Seems silly - why not just call IAudioClient_Initialize() again?! */ - ma_IAudioClient_Release((ma_IAudioClient*)pData->pAudioClient); - - #if defined(MA_WIN32_DESKTOP) || defined(MA_WIN32_GDK) - hr = ma_IMMDevice_Activate(pDeviceInterface, &MA_IID_IAudioClient, CLSCTX_ALL, NULL, (void**)&pData->pAudioClient); - #else - hr = ma_IUnknown_QueryInterface(pDeviceInterface, &MA_IID_IAudioClient, (void**)&pData->pAudioClient); - #endif - - if (SUCCEEDED(hr)) { - hr = ma_IAudioClient_Initialize((ma_IAudioClient*)pData->pAudioClient, shareMode, streamFlags, bufferDuration, bufferDuration, (MA_WAVEFORMATEX*)&wf, NULL); - } - } - } - - if (FAILED(hr)) { - /* Failed to initialize in exclusive mode. Don't fall back to shared mode - instead tell the client about it. They can reinitialize in shared mode if they want. */ - if (hr == E_ACCESSDENIED) { - errorMsg = "[WASAPI] Failed to initialize device in exclusive mode. Access denied.", result = MA_ACCESS_DENIED; - } else if (hr == MA_AUDCLNT_E_DEVICE_IN_USE) { - errorMsg = "[WASAPI] Failed to initialize device in exclusive mode. Device in use.", result = MA_BUSY; - } else { - errorMsg = "[WASAPI] Failed to initialize device in exclusive mode."; result = ma_result_from_HRESULT(hr); - } - goto done; - } - } - - if (shareMode == MA_AUDCLNT_SHAREMODE_SHARED) { - /* - Low latency shared mode via IAudioClient3. - - NOTE - ==== - Contrary to the documentation on MSDN (https://docs.microsoft.com/en-us/windows/win32/api/audioclient/nf-audioclient-iaudioclient3-initializesharedaudiostream), the - use of AUDCLNT_STREAMFLAGS_AUTOCONVERTPCM and AUDCLNT_STREAMFLAGS_SRC_DEFAULT_QUALITY with IAudioClient3_InitializeSharedAudioStream() absolutely does not work. Using - any of these flags will result in HRESULT code 0x88890021. The other problem is that calling IAudioClient3_GetSharedModeEnginePeriod() with a sample rate different to - that returned by IAudioClient_GetMixFormat() also results in an error. I'm therefore disabling low-latency shared mode with AUDCLNT_STREAMFLAGS_AUTOCONVERTPCM. - */ - #ifndef MA_WASAPI_NO_LOW_LATENCY_SHARED_MODE - { - if ((streamFlags & MA_AUDCLNT_STREAMFLAGS_AUTOCONVERTPCM) == 0 || nativeSampleRate == wf.nSamplesPerSec) { - ma_IAudioClient3* pAudioClient3 = NULL; - hr = ma_IAudioClient_QueryInterface(pData->pAudioClient, &MA_IID_IAudioClient3, (void**)&pAudioClient3); - if (SUCCEEDED(hr)) { - ma_uint32 defaultPeriodInFrames; - ma_uint32 fundamentalPeriodInFrames; - ma_uint32 minPeriodInFrames; - ma_uint32 maxPeriodInFrames; - hr = ma_IAudioClient3_GetSharedModeEnginePeriod(pAudioClient3, (MA_WAVEFORMATEX*)&wf, &defaultPeriodInFrames, &fundamentalPeriodInFrames, &minPeriodInFrames, &maxPeriodInFrames); - if (SUCCEEDED(hr)) { - ma_uint32 desiredPeriodInFrames = pData->periodSizeInFramesOut; - ma_uint32 actualPeriodInFrames = desiredPeriodInFrames; - - /* Make sure the period size is a multiple of fundamentalPeriodInFrames. */ - actualPeriodInFrames = actualPeriodInFrames / fundamentalPeriodInFrames; - actualPeriodInFrames = actualPeriodInFrames * fundamentalPeriodInFrames; - - /* The period needs to be clamped between minPeriodInFrames and maxPeriodInFrames. */ - actualPeriodInFrames = ma_clamp(actualPeriodInFrames, minPeriodInFrames, maxPeriodInFrames); - - ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_DEBUG, "[WASAPI] Trying IAudioClient3_InitializeSharedAudioStream(actualPeriodInFrames=%d)\n", actualPeriodInFrames); - ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_DEBUG, " defaultPeriodInFrames=%d\n", defaultPeriodInFrames); - ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_DEBUG, " fundamentalPeriodInFrames=%d\n", fundamentalPeriodInFrames); - ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_DEBUG, " minPeriodInFrames=%d\n", minPeriodInFrames); - ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_DEBUG, " maxPeriodInFrames=%d\n", maxPeriodInFrames); - - /* If the client requested a largish buffer than we don't actually want to use low latency shared mode because it forces small buffers. */ - if (actualPeriodInFrames >= desiredPeriodInFrames) { - /* - MA_AUDCLNT_STREAMFLAGS_AUTOCONVERTPCM | MA_AUDCLNT_STREAMFLAGS_SRC_DEFAULT_QUALITY must not be in the stream flags. If either of these are specified, - IAudioClient3_InitializeSharedAudioStream() will fail. - */ - hr = ma_IAudioClient3_InitializeSharedAudioStream(pAudioClient3, streamFlags & ~(MA_AUDCLNT_STREAMFLAGS_AUTOCONVERTPCM | MA_AUDCLNT_STREAMFLAGS_SRC_DEFAULT_QUALITY), actualPeriodInFrames, (MA_WAVEFORMATEX*)&wf, NULL); - if (SUCCEEDED(hr)) { - wasInitializedUsingIAudioClient3 = MA_TRUE; - pData->periodSizeInFramesOut = actualPeriodInFrames; - - ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_DEBUG, "[WASAPI] Using IAudioClient3\n"); - ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_DEBUG, " periodSizeInFramesOut=%d\n", pData->periodSizeInFramesOut); - } else { - ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_DEBUG, "[WASAPI] IAudioClient3_InitializeSharedAudioStream failed. Falling back to IAudioClient.\n"); - } - } else { - ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_DEBUG, "[WASAPI] Not using IAudioClient3 because the desired period size is larger than the maximum supported by IAudioClient3.\n"); - } - } else { - ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_DEBUG, "[WASAPI] IAudioClient3_GetSharedModeEnginePeriod failed. Falling back to IAudioClient.\n"); - } - - ma_IAudioClient3_Release(pAudioClient3); - pAudioClient3 = NULL; - } - } - } - #else - { - ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_DEBUG, "[WASAPI] Not using IAudioClient3 because MA_WASAPI_NO_LOW_LATENCY_SHARED_MODE is enabled.\n"); - } - #endif - - /* If we don't have an IAudioClient3 then we need to use the normal initialization routine. */ - if (!wasInitializedUsingIAudioClient3) { - MA_REFERENCE_TIME bufferDuration = periodDurationInMicroseconds * pData->periodsOut * 10; /* <-- Multiply by 10 for microseconds to 100-nanoseconds. */ - hr = ma_IAudioClient_Initialize((ma_IAudioClient*)pData->pAudioClient, shareMode, streamFlags, bufferDuration, 0, (const MA_WAVEFORMATEX*)&wf, NULL); - if (FAILED(hr)) { - if (hr == E_ACCESSDENIED) { - errorMsg = "[WASAPI] Failed to initialize device. Access denied.", result = MA_ACCESS_DENIED; - } else if (hr == MA_AUDCLNT_E_DEVICE_IN_USE) { - errorMsg = "[WASAPI] Failed to initialize device. Device in use.", result = MA_BUSY; - } else { - errorMsg = "[WASAPI] Failed to initialize device.", result = ma_result_from_HRESULT(hr); - } - - goto done; - } - } - } - - if (!wasInitializedUsingIAudioClient3) { - ma_uint32 bufferSizeInFrames = 0; - hr = ma_IAudioClient_GetBufferSize((ma_IAudioClient*)pData->pAudioClient, &bufferSizeInFrames); - if (FAILED(hr)) { - errorMsg = "[WASAPI] Failed to get audio client's actual buffer size.", result = ma_result_from_HRESULT(hr); - goto done; - } - - /* - When using process loopback mode, retrieval of the buffer size seems to result in totally - incorrect values. In this case we'll just assume it's the same size as what we requested - when we initialized the client. - */ - if (usingProcessLoopback) { - bufferSizeInFrames = (ma_uint32)((periodDurationInMicroseconds * pData->periodsOut) * pData->sampleRateOut / 1000000); - } - - pData->periodSizeInFramesOut = bufferSizeInFrames / pData->periodsOut; - } - - pData->usingAudioClient3 = wasInitializedUsingIAudioClient3; - - - if (deviceType == ma_device_type_playback) { - result = ma_device_create_IAudioClient_service__wasapi(pContext, deviceType, (ma_IAudioClient*)pData->pAudioClient, (void**)&pData->pRenderClient); - } else { - result = ma_device_create_IAudioClient_service__wasapi(pContext, deviceType, (ma_IAudioClient*)pData->pAudioClient, (void**)&pData->pCaptureClient); - } - - /*if (FAILED(hr)) {*/ - if (result != MA_SUCCESS) { - errorMsg = "[WASAPI] Failed to get audio client service."; - goto done; - } - - - /* Grab the name of the device. */ - #if defined(MA_WIN32_DESKTOP) || defined(MA_WIN32_GDK) - { - ma_IPropertyStore *pProperties; - hr = ma_IMMDevice_OpenPropertyStore(pDeviceInterface, STGM_READ, &pProperties); - if (SUCCEEDED(hr)) { - MA_PROPVARIANT varName; - ma_PropVariantInit(&varName); - hr = ma_IPropertyStore_GetValue(pProperties, &MA_PKEY_Device_FriendlyName, &varName); - if (SUCCEEDED(hr)) { - WideCharToMultiByte(CP_UTF8, 0, varName.pwszVal, -1, pData->deviceName, sizeof(pData->deviceName), 0, FALSE); - ma_PropVariantClear(pContext, &varName); - } - - ma_IPropertyStore_Release(pProperties); - } - } - #endif - - /* - For the WASAPI backend we need to know the actual IDs of the device in order to do automatic - stream routing so that IDs can be compared and we can determine which device has been detached - and whether or not it matches with our ma_device. - */ - #if defined(MA_WIN32_DESKTOP) || defined(MA_WIN32_GDK) - { - /* Desktop */ - ma_context_get_device_id_from_MMDevice__wasapi(pContext, pDeviceInterface, &pData->id); - } - #else - { - /* UWP */ - /* TODO: Implement me. Need to figure out how to get the ID of the default device. */ - } - #endif - -done: - /* Clean up. */ -#if defined(MA_WIN32_DESKTOP) || defined(MA_WIN32_GDK) - if (pDeviceInterface != NULL) { - ma_IMMDevice_Release(pDeviceInterface); - } -#else - if (pDeviceInterface != NULL) { - ma_IUnknown_Release(pDeviceInterface); - } -#endif - - if (result != MA_SUCCESS) { - if (pData->pRenderClient) { - ma_IAudioRenderClient_Release((ma_IAudioRenderClient*)pData->pRenderClient); - pData->pRenderClient = NULL; - } - if (pData->pCaptureClient) { - ma_IAudioCaptureClient_Release((ma_IAudioCaptureClient*)pData->pCaptureClient); - pData->pCaptureClient = NULL; - } - if (pData->pAudioClient) { - ma_IAudioClient_Release((ma_IAudioClient*)pData->pAudioClient); - pData->pAudioClient = NULL; - } - - if (errorMsg != NULL && errorMsg[0] != '\0') { - ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "%s\n", errorMsg); - } - - return result; - } else { - return MA_SUCCESS; - } -} - -static ma_result ma_device_reinit__wasapi(ma_device* pDevice, ma_device_type deviceType) -{ - ma_device_init_internal_data__wasapi data; - ma_result result; - - MA_ASSERT(pDevice != NULL); - - /* We only re-initialize the playback or capture device. Never a full-duplex device. */ - if (deviceType == ma_device_type_duplex) { - return MA_INVALID_ARGS; - } - - - /* - Before reinitializing the device we need to free the previous audio clients. - - There's a known memory leak here. We will be calling this from the routing change callback that - is fired by WASAPI. If we attempt to release the IAudioClient we will deadlock. In my opinion - this is a bug. I'm not sure what I need to do to handle this cleanly, but I think we'll probably - need some system where we post an event, but delay the execution of it until the callback has - returned. I'm not sure how to do this reliably, however. I have set up some infrastructure for - a command thread which might be useful for this. - */ - if (deviceType == ma_device_type_capture || deviceType == ma_device_type_loopback) { - if (pDevice->wasapi.pCaptureClient) { - ma_IAudioCaptureClient_Release((ma_IAudioCaptureClient*)pDevice->wasapi.pCaptureClient); - pDevice->wasapi.pCaptureClient = NULL; - } - - if (pDevice->wasapi.pAudioClientCapture) { - /*ma_device_release_IAudioClient_service__wasapi(pDevice, ma_device_type_capture);*/ - pDevice->wasapi.pAudioClientCapture = NULL; - } - } - - if (deviceType == ma_device_type_playback) { - if (pDevice->wasapi.pRenderClient) { - ma_IAudioRenderClient_Release((ma_IAudioRenderClient*)pDevice->wasapi.pRenderClient); - pDevice->wasapi.pRenderClient = NULL; - } - - if (pDevice->wasapi.pAudioClientPlayback) { - /*ma_device_release_IAudioClient_service__wasapi(pDevice, ma_device_type_playback);*/ - pDevice->wasapi.pAudioClientPlayback = NULL; - } - } - - - if (deviceType == ma_device_type_playback) { - data.formatIn = pDevice->playback.format; - data.channelsIn = pDevice->playback.channels; - MA_COPY_MEMORY(data.channelMapIn, pDevice->playback.channelMap, sizeof(pDevice->playback.channelMap)); - data.shareMode = pDevice->playback.shareMode; - } else { - data.formatIn = pDevice->capture.format; - data.channelsIn = pDevice->capture.channels; - MA_COPY_MEMORY(data.channelMapIn, pDevice->capture.channelMap, sizeof(pDevice->capture.channelMap)); - data.shareMode = pDevice->capture.shareMode; - } - - data.sampleRateIn = pDevice->sampleRate; - data.periodSizeInFramesIn = pDevice->wasapi.originalPeriodSizeInFrames; - data.periodSizeInMillisecondsIn = pDevice->wasapi.originalPeriodSizeInMilliseconds; - data.periodsIn = pDevice->wasapi.originalPeriods; - data.performanceProfile = pDevice->wasapi.originalPerformanceProfile; - data.noAutoConvertSRC = pDevice->wasapi.noAutoConvertSRC; - data.noDefaultQualitySRC = pDevice->wasapi.noDefaultQualitySRC; - data.noHardwareOffloading = pDevice->wasapi.noHardwareOffloading; - data.loopbackProcessID = pDevice->wasapi.loopbackProcessID; - data.loopbackProcessExclude = pDevice->wasapi.loopbackProcessExclude; - result = ma_device_init_internal__wasapi(pDevice->pContext, deviceType, NULL, &data); - if (result != MA_SUCCESS) { - return result; - } - - /* At this point we have some new objects ready to go. We need to uninitialize the previous ones and then set the new ones. */ - if (deviceType == ma_device_type_capture || deviceType == ma_device_type_loopback) { - pDevice->wasapi.pAudioClientCapture = data.pAudioClient; - pDevice->wasapi.pCaptureClient = data.pCaptureClient; - - pDevice->capture.internalFormat = data.formatOut; - pDevice->capture.internalChannels = data.channelsOut; - pDevice->capture.internalSampleRate = data.sampleRateOut; - MA_COPY_MEMORY(pDevice->capture.internalChannelMap, data.channelMapOut, sizeof(data.channelMapOut)); - pDevice->capture.internalPeriodSizeInFrames = data.periodSizeInFramesOut; - pDevice->capture.internalPeriods = data.periodsOut; - ma_strcpy_s(pDevice->capture.name, sizeof(pDevice->capture.name), data.deviceName); - - ma_IAudioClient_SetEventHandle((ma_IAudioClient*)pDevice->wasapi.pAudioClientCapture, (HANDLE)pDevice->wasapi.hEventCapture); - - pDevice->wasapi.periodSizeInFramesCapture = data.periodSizeInFramesOut; - ma_IAudioClient_GetBufferSize((ma_IAudioClient*)pDevice->wasapi.pAudioClientCapture, &pDevice->wasapi.actualBufferSizeInFramesCapture); - - /* We must always have a valid ID. */ - ma_strcpy_s_WCHAR(pDevice->capture.id.wasapi, sizeof(pDevice->capture.id.wasapi), data.id.wasapi); - } - - if (deviceType == ma_device_type_playback) { - pDevice->wasapi.pAudioClientPlayback = data.pAudioClient; - pDevice->wasapi.pRenderClient = data.pRenderClient; - - pDevice->playback.internalFormat = data.formatOut; - pDevice->playback.internalChannels = data.channelsOut; - pDevice->playback.internalSampleRate = data.sampleRateOut; - MA_COPY_MEMORY(pDevice->playback.internalChannelMap, data.channelMapOut, sizeof(data.channelMapOut)); - pDevice->playback.internalPeriodSizeInFrames = data.periodSizeInFramesOut; - pDevice->playback.internalPeriods = data.periodsOut; - ma_strcpy_s(pDevice->playback.name, sizeof(pDevice->playback.name), data.deviceName); - - ma_IAudioClient_SetEventHandle((ma_IAudioClient*)pDevice->wasapi.pAudioClientPlayback, (HANDLE)pDevice->wasapi.hEventPlayback); - - pDevice->wasapi.periodSizeInFramesPlayback = data.periodSizeInFramesOut; - ma_IAudioClient_GetBufferSize((ma_IAudioClient*)pDevice->wasapi.pAudioClientPlayback, &pDevice->wasapi.actualBufferSizeInFramesPlayback); - - /* We must always have a valid ID because rerouting will look at it. */ - ma_strcpy_s_WCHAR(pDevice->playback.id.wasapi, sizeof(pDevice->playback.id.wasapi), data.id.wasapi); - } - - return MA_SUCCESS; -} - -static ma_result ma_device_init__wasapi(ma_device* pDevice, const ma_device_config* pConfig, ma_device_descriptor* pDescriptorPlayback, ma_device_descriptor* pDescriptorCapture) -{ - ma_result result = MA_SUCCESS; - -#if defined(MA_WIN32_DESKTOP) || defined(MA_WIN32_GDK) - HRESULT hr; - ma_IMMDeviceEnumerator* pDeviceEnumerator; -#endif - - MA_ASSERT(pDevice != NULL); - - MA_ZERO_OBJECT(&pDevice->wasapi); - pDevice->wasapi.usage = pConfig->wasapi.usage; - pDevice->wasapi.noAutoConvertSRC = pConfig->wasapi.noAutoConvertSRC; - pDevice->wasapi.noDefaultQualitySRC = pConfig->wasapi.noDefaultQualitySRC; - pDevice->wasapi.noHardwareOffloading = pConfig->wasapi.noHardwareOffloading; - pDevice->wasapi.loopbackProcessID = pConfig->wasapi.loopbackProcessID; - pDevice->wasapi.loopbackProcessExclude = pConfig->wasapi.loopbackProcessExclude; - - /* Exclusive mode is not allowed with loopback. */ - if (pConfig->deviceType == ma_device_type_loopback && pConfig->playback.shareMode == ma_share_mode_exclusive) { - return MA_INVALID_DEVICE_CONFIG; - } - - if (pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex || pConfig->deviceType == ma_device_type_loopback) { - ma_device_init_internal_data__wasapi data; - data.formatIn = pDescriptorCapture->format; - data.channelsIn = pDescriptorCapture->channels; - data.sampleRateIn = pDescriptorCapture->sampleRate; - MA_COPY_MEMORY(data.channelMapIn, pDescriptorCapture->channelMap, sizeof(pDescriptorCapture->channelMap)); - data.periodSizeInFramesIn = pDescriptorCapture->periodSizeInFrames; - data.periodSizeInMillisecondsIn = pDescriptorCapture->periodSizeInMilliseconds; - data.periodsIn = pDescriptorCapture->periodCount; - data.shareMode = pDescriptorCapture->shareMode; - data.performanceProfile = pConfig->performanceProfile; - data.noAutoConvertSRC = pConfig->wasapi.noAutoConvertSRC; - data.noDefaultQualitySRC = pConfig->wasapi.noDefaultQualitySRC; - data.noHardwareOffloading = pConfig->wasapi.noHardwareOffloading; - data.loopbackProcessID = pConfig->wasapi.loopbackProcessID; - data.loopbackProcessExclude = pConfig->wasapi.loopbackProcessExclude; - - result = ma_device_init_internal__wasapi(pDevice->pContext, (pConfig->deviceType == ma_device_type_loopback) ? ma_device_type_loopback : ma_device_type_capture, pDescriptorCapture->pDeviceID, &data); - if (result != MA_SUCCESS) { - return result; - } - - pDevice->wasapi.pAudioClientCapture = data.pAudioClient; - pDevice->wasapi.pCaptureClient = data.pCaptureClient; - pDevice->wasapi.originalPeriodSizeInMilliseconds = pDescriptorCapture->periodSizeInMilliseconds; - pDevice->wasapi.originalPeriodSizeInFrames = pDescriptorCapture->periodSizeInFrames; - pDevice->wasapi.originalPeriods = pDescriptorCapture->periodCount; - pDevice->wasapi.originalPerformanceProfile = pConfig->performanceProfile; - - /* - The event for capture needs to be manual reset for the same reason as playback. We keep the initial state set to unsignaled, - however, because we want to block until we actually have something for the first call to ma_device_read(). - */ - pDevice->wasapi.hEventCapture = (ma_handle)CreateEventA(NULL, FALSE, FALSE, NULL); /* Auto reset, unsignaled by default. */ - if (pDevice->wasapi.hEventCapture == NULL) { - result = ma_result_from_GetLastError(GetLastError()); - - if (pDevice->wasapi.pCaptureClient != NULL) { - ma_IAudioCaptureClient_Release((ma_IAudioCaptureClient*)pDevice->wasapi.pCaptureClient); - pDevice->wasapi.pCaptureClient = NULL; - } - if (pDevice->wasapi.pAudioClientCapture != NULL) { - ma_IAudioClient_Release((ma_IAudioClient*)pDevice->wasapi.pAudioClientCapture); - pDevice->wasapi.pAudioClientCapture = NULL; - } - - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to create event for capture."); - return result; - } - ma_IAudioClient_SetEventHandle((ma_IAudioClient*)pDevice->wasapi.pAudioClientCapture, (HANDLE)pDevice->wasapi.hEventCapture); - - pDevice->wasapi.periodSizeInFramesCapture = data.periodSizeInFramesOut; - ma_IAudioClient_GetBufferSize((ma_IAudioClient*)pDevice->wasapi.pAudioClientCapture, &pDevice->wasapi.actualBufferSizeInFramesCapture); - - /* We must always have a valid ID. */ - ma_strcpy_s_WCHAR(pDevice->capture.id.wasapi, sizeof(pDevice->capture.id.wasapi), data.id.wasapi); - - /* The descriptor needs to be updated with actual values. */ - pDescriptorCapture->format = data.formatOut; - pDescriptorCapture->channels = data.channelsOut; - pDescriptorCapture->sampleRate = data.sampleRateOut; - MA_COPY_MEMORY(pDescriptorCapture->channelMap, data.channelMapOut, sizeof(data.channelMapOut)); - pDescriptorCapture->periodSizeInFrames = data.periodSizeInFramesOut; - pDescriptorCapture->periodCount = data.periodsOut; - } - - if (pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) { - ma_device_init_internal_data__wasapi data; - data.formatIn = pDescriptorPlayback->format; - data.channelsIn = pDescriptorPlayback->channels; - data.sampleRateIn = pDescriptorPlayback->sampleRate; - MA_COPY_MEMORY(data.channelMapIn, pDescriptorPlayback->channelMap, sizeof(pDescriptorPlayback->channelMap)); - data.periodSizeInFramesIn = pDescriptorPlayback->periodSizeInFrames; - data.periodSizeInMillisecondsIn = pDescriptorPlayback->periodSizeInMilliseconds; - data.periodsIn = pDescriptorPlayback->periodCount; - data.shareMode = pDescriptorPlayback->shareMode; - data.performanceProfile = pConfig->performanceProfile; - data.noAutoConvertSRC = pConfig->wasapi.noAutoConvertSRC; - data.noDefaultQualitySRC = pConfig->wasapi.noDefaultQualitySRC; - data.noHardwareOffloading = pConfig->wasapi.noHardwareOffloading; - data.loopbackProcessID = pConfig->wasapi.loopbackProcessID; - data.loopbackProcessExclude = pConfig->wasapi.loopbackProcessExclude; - - result = ma_device_init_internal__wasapi(pDevice->pContext, ma_device_type_playback, pDescriptorPlayback->pDeviceID, &data); - if (result != MA_SUCCESS) { - if (pConfig->deviceType == ma_device_type_duplex) { - if (pDevice->wasapi.pCaptureClient != NULL) { - ma_IAudioCaptureClient_Release((ma_IAudioCaptureClient*)pDevice->wasapi.pCaptureClient); - pDevice->wasapi.pCaptureClient = NULL; - } - if (pDevice->wasapi.pAudioClientCapture != NULL) { - ma_IAudioClient_Release((ma_IAudioClient*)pDevice->wasapi.pAudioClientCapture); - pDevice->wasapi.pAudioClientCapture = NULL; - } - - CloseHandle((HANDLE)pDevice->wasapi.hEventCapture); - pDevice->wasapi.hEventCapture = NULL; - } - return result; - } - - pDevice->wasapi.pAudioClientPlayback = data.pAudioClient; - pDevice->wasapi.pRenderClient = data.pRenderClient; - pDevice->wasapi.originalPeriodSizeInMilliseconds = pDescriptorPlayback->periodSizeInMilliseconds; - pDevice->wasapi.originalPeriodSizeInFrames = pDescriptorPlayback->periodSizeInFrames; - pDevice->wasapi.originalPeriods = pDescriptorPlayback->periodCount; - pDevice->wasapi.originalPerformanceProfile = pConfig->performanceProfile; - - /* - The event for playback is needs to be manual reset because we want to explicitly control the fact that it becomes signalled - only after the whole available space has been filled, never before. - - The playback event also needs to be initially set to a signaled state so that the first call to ma_device_write() is able - to get passed WaitForMultipleObjects(). - */ - pDevice->wasapi.hEventPlayback = (ma_handle)CreateEventA(NULL, FALSE, TRUE, NULL); /* Auto reset, signaled by default. */ - if (pDevice->wasapi.hEventPlayback == NULL) { - result = ma_result_from_GetLastError(GetLastError()); - - if (pConfig->deviceType == ma_device_type_duplex) { - if (pDevice->wasapi.pCaptureClient != NULL) { - ma_IAudioCaptureClient_Release((ma_IAudioCaptureClient*)pDevice->wasapi.pCaptureClient); - pDevice->wasapi.pCaptureClient = NULL; - } - if (pDevice->wasapi.pAudioClientCapture != NULL) { - ma_IAudioClient_Release((ma_IAudioClient*)pDevice->wasapi.pAudioClientCapture); - pDevice->wasapi.pAudioClientCapture = NULL; - } - - CloseHandle((HANDLE)pDevice->wasapi.hEventCapture); - pDevice->wasapi.hEventCapture = NULL; - } - - if (pDevice->wasapi.pRenderClient != NULL) { - ma_IAudioRenderClient_Release((ma_IAudioRenderClient*)pDevice->wasapi.pRenderClient); - pDevice->wasapi.pRenderClient = NULL; - } - if (pDevice->wasapi.pAudioClientPlayback != NULL) { - ma_IAudioClient_Release((ma_IAudioClient*)pDevice->wasapi.pAudioClientPlayback); - pDevice->wasapi.pAudioClientPlayback = NULL; - } - - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to create event for playback."); - return result; - } - ma_IAudioClient_SetEventHandle((ma_IAudioClient*)pDevice->wasapi.pAudioClientPlayback, (HANDLE)pDevice->wasapi.hEventPlayback); - - pDevice->wasapi.periodSizeInFramesPlayback = data.periodSizeInFramesOut; - ma_IAudioClient_GetBufferSize((ma_IAudioClient*)pDevice->wasapi.pAudioClientPlayback, &pDevice->wasapi.actualBufferSizeInFramesPlayback); - - /* We must always have a valid ID because rerouting will look at it. */ - ma_strcpy_s_WCHAR(pDevice->playback.id.wasapi, sizeof(pDevice->playback.id.wasapi), data.id.wasapi); - - /* The descriptor needs to be updated with actual values. */ - pDescriptorPlayback->format = data.formatOut; - pDescriptorPlayback->channels = data.channelsOut; - pDescriptorPlayback->sampleRate = data.sampleRateOut; - MA_COPY_MEMORY(pDescriptorPlayback->channelMap, data.channelMapOut, sizeof(data.channelMapOut)); - pDescriptorPlayback->periodSizeInFrames = data.periodSizeInFramesOut; - pDescriptorPlayback->periodCount = data.periodsOut; - } - - /* - We need to register a notification client to detect when the device has been disabled, unplugged or re-routed (when the default device changes). When - we are connecting to the default device we want to do automatic stream routing when the device is disabled or unplugged. Otherwise we want to just - stop the device outright and let the application handle it. - */ -#if defined(MA_WIN32_DESKTOP) || defined(MA_WIN32_GDK) - if (pConfig->wasapi.noAutoStreamRouting == MA_FALSE) { - if ((pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex || pConfig->deviceType == ma_device_type_loopback) && pConfig->capture.pDeviceID == NULL) { - pDevice->wasapi.allowCaptureAutoStreamRouting = MA_TRUE; - } - if ((pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) && pConfig->playback.pDeviceID == NULL) { - pDevice->wasapi.allowPlaybackAutoStreamRouting = MA_TRUE; - } - } - - ma_mutex_init(&pDevice->wasapi.rerouteLock); - - hr = ma_CoCreateInstance(pDevice->pContext, &MA_CLSID_MMDeviceEnumerator, NULL, CLSCTX_ALL, &MA_IID_IMMDeviceEnumerator, (void**)&pDeviceEnumerator); - if (FAILED(hr)) { - ma_device_uninit__wasapi(pDevice); - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to create device enumerator."); - return ma_result_from_HRESULT(hr); - } - - pDevice->wasapi.notificationClient.lpVtbl = (void*)&g_maNotificationCientVtbl; - pDevice->wasapi.notificationClient.counter = 1; - pDevice->wasapi.notificationClient.pDevice = pDevice; - - hr = pDeviceEnumerator->lpVtbl->RegisterEndpointNotificationCallback(pDeviceEnumerator, &pDevice->wasapi.notificationClient); - if (SUCCEEDED(hr)) { - pDevice->wasapi.pDeviceEnumerator = (ma_ptr)pDeviceEnumerator; - } else { - /* Not the end of the world if we fail to register the notification callback. We just won't support automatic stream routing. */ - ma_IMMDeviceEnumerator_Release(pDeviceEnumerator); - } -#endif - - ma_atomic_bool32_set(&pDevice->wasapi.isStartedCapture, MA_FALSE); - ma_atomic_bool32_set(&pDevice->wasapi.isStartedPlayback, MA_FALSE); - - return MA_SUCCESS; -} - -static ma_result ma_device__get_available_frames__wasapi(ma_device* pDevice, ma_IAudioClient* pAudioClient, ma_uint32* pFrameCount) -{ - ma_uint32 paddingFramesCount; - HRESULT hr; - ma_share_mode shareMode; - - MA_ASSERT(pDevice != NULL); - MA_ASSERT(pFrameCount != NULL); - - *pFrameCount = 0; - - if ((ma_ptr)pAudioClient != pDevice->wasapi.pAudioClientPlayback && (ma_ptr)pAudioClient != pDevice->wasapi.pAudioClientCapture) { - return MA_INVALID_OPERATION; - } - - /* - I've had a report that GetCurrentPadding() is returning a frame count of 0 which is preventing - higher level function calls from doing anything because it thinks nothing is available. I have - taken a look at the documentation and it looks like this is unnecessary in exclusive mode. - - From Microsoft's documentation: - - For an exclusive-mode rendering or capture stream that was initialized with the - AUDCLNT_STREAMFLAGS_EVENTCALLBACK flag, the client typically has no use for the padding - value reported by GetCurrentPadding. Instead, the client accesses an entire buffer during - each processing pass. - - Considering this, I'm going to skip GetCurrentPadding() for exclusive mode and just report the - entire buffer. This depends on the caller making sure they wait on the event handler. - */ - shareMode = ((ma_ptr)pAudioClient == pDevice->wasapi.pAudioClientPlayback) ? pDevice->playback.shareMode : pDevice->capture.shareMode; - if (shareMode == ma_share_mode_shared) { - /* Shared mode. */ - hr = ma_IAudioClient_GetCurrentPadding(pAudioClient, &paddingFramesCount); - if (FAILED(hr)) { - return ma_result_from_HRESULT(hr); - } - - if ((ma_ptr)pAudioClient == pDevice->wasapi.pAudioClientPlayback) { - *pFrameCount = pDevice->wasapi.actualBufferSizeInFramesPlayback - paddingFramesCount; - } else { - *pFrameCount = paddingFramesCount; - } - } else { - /* Exclusive mode. */ - if ((ma_ptr)pAudioClient == pDevice->wasapi.pAudioClientPlayback) { - *pFrameCount = pDevice->wasapi.actualBufferSizeInFramesPlayback; - } else { - *pFrameCount = pDevice->wasapi.actualBufferSizeInFramesCapture; - } - } - - return MA_SUCCESS; -} - - -static ma_result ma_device_reroute__wasapi(ma_device* pDevice, ma_device_type deviceType) -{ - ma_result result; - - if (deviceType == ma_device_type_duplex) { - return MA_INVALID_ARGS; - } - - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "=== CHANGING DEVICE ===\n"); - - result = ma_device_reinit__wasapi(pDevice, deviceType); - if (result != MA_SUCCESS) { - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_WARNING, "[WASAPI] Reinitializing device after route change failed.\n"); - return result; - } - - ma_device__post_init_setup(pDevice, deviceType); - ma_device__on_notification_rerouted(pDevice); - - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "=== DEVICE CHANGED ===\n"); - - return MA_SUCCESS; -} - -static ma_result ma_device_start__wasapi_nolock(ma_device* pDevice) -{ - HRESULT hr; - - if (pDevice->pContext->wasapi.hAvrt) { - const char* pTaskName = ma_to_usage_string__wasapi(pDevice->wasapi.usage); - if (pTaskName) { - DWORD idx = 0; - pDevice->wasapi.hAvrtHandle = (ma_handle)((MA_PFN_AvSetMmThreadCharacteristicsA)pDevice->pContext->wasapi.AvSetMmThreadCharacteristicsA)(pTaskName, &idx); - } - } - - if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex || pDevice->type == ma_device_type_loopback) { - hr = ma_IAudioClient_Start((ma_IAudioClient*)pDevice->wasapi.pAudioClientCapture); - if (FAILED(hr)) { - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to start internal capture device. HRESULT = %d.", (int)hr); - return ma_result_from_HRESULT(hr); - } - - ma_atomic_bool32_set(&pDevice->wasapi.isStartedCapture, MA_TRUE); - } - - if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) { - hr = ma_IAudioClient_Start((ma_IAudioClient*)pDevice->wasapi.pAudioClientPlayback); - if (FAILED(hr)) { - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to start internal playback device. HRESULT = %d.", (int)hr); - return ma_result_from_HRESULT(hr); - } - - ma_atomic_bool32_set(&pDevice->wasapi.isStartedPlayback, MA_TRUE); - } - - return MA_SUCCESS; -} - -static ma_result ma_device_start__wasapi(ma_device* pDevice) -{ - ma_result result; - - MA_ASSERT(pDevice != NULL); - - /* Wait for any rerouting to finish before attempting to start the device. */ - ma_mutex_lock(&pDevice->wasapi.rerouteLock); - { - result = ma_device_start__wasapi_nolock(pDevice); - } - ma_mutex_unlock(&pDevice->wasapi.rerouteLock); - - return result; -} - -static ma_result ma_device_stop__wasapi_nolock(ma_device* pDevice) -{ - ma_result result; - HRESULT hr; - - MA_ASSERT(pDevice != NULL); - - if (pDevice->wasapi.hAvrtHandle) { - ((MA_PFN_AvRevertMmThreadCharacteristics)pDevice->pContext->wasapi.AvRevertMmThreadcharacteristics)((HANDLE)pDevice->wasapi.hAvrtHandle); - pDevice->wasapi.hAvrtHandle = NULL; - } - - if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex || pDevice->type == ma_device_type_loopback) { - /* If we have a mapped buffer we need to release it. */ - if (pDevice->wasapi.pMappedBufferCapture != NULL) { - ma_IAudioCaptureClient_ReleaseBuffer((ma_IAudioCaptureClient*)pDevice->wasapi.pCaptureClient, pDevice->wasapi.mappedBufferCaptureCap); - pDevice->wasapi.pMappedBufferCapture = NULL; - pDevice->wasapi.mappedBufferCaptureCap = 0; - pDevice->wasapi.mappedBufferCaptureLen = 0; - } - - hr = ma_IAudioClient_Stop((ma_IAudioClient*)pDevice->wasapi.pAudioClientCapture); - if (FAILED(hr)) { - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to stop internal capture device."); - return ma_result_from_HRESULT(hr); - } - - /* The audio client needs to be reset otherwise restarting will fail. */ - hr = ma_IAudioClient_Reset((ma_IAudioClient*)pDevice->wasapi.pAudioClientCapture); - if (FAILED(hr)) { - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to reset internal capture device."); - return ma_result_from_HRESULT(hr); - } - - ma_atomic_bool32_set(&pDevice->wasapi.isStartedCapture, MA_FALSE); - } - - if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) { - if (pDevice->wasapi.pMappedBufferPlayback != NULL) { - ma_silence_pcm_frames( - ma_offset_pcm_frames_ptr(pDevice->wasapi.pMappedBufferPlayback, pDevice->wasapi.mappedBufferPlaybackLen, pDevice->playback.internalFormat, pDevice->playback.internalChannels), - pDevice->wasapi.mappedBufferPlaybackCap - pDevice->wasapi.mappedBufferPlaybackLen, - pDevice->playback.internalFormat, pDevice->playback.internalChannels - ); - ma_IAudioRenderClient_ReleaseBuffer((ma_IAudioRenderClient*)pDevice->wasapi.pRenderClient, pDevice->wasapi.mappedBufferPlaybackCap, 0); - pDevice->wasapi.pMappedBufferPlayback = NULL; - pDevice->wasapi.mappedBufferPlaybackCap = 0; - pDevice->wasapi.mappedBufferPlaybackLen = 0; - } - - /* - The buffer needs to be drained before stopping the device. Not doing this will result in the last few frames not getting output to - the speakers. This is a problem for very short sounds because it'll result in a significant portion of it not getting played. - */ - if (ma_atomic_bool32_get(&pDevice->wasapi.isStartedPlayback)) { - /* We need to make sure we put a timeout here or else we'll risk getting stuck in a deadlock in some cases. */ - DWORD waitTime = (pDevice->wasapi.actualBufferSizeInFramesPlayback * 1000) / pDevice->playback.internalSampleRate; - - if (pDevice->playback.shareMode == ma_share_mode_exclusive) { - WaitForSingleObject((HANDLE)pDevice->wasapi.hEventPlayback, waitTime); - } else { - ma_uint32 prevFramesAvailablePlayback = (ma_uint32)-1; - ma_uint32 framesAvailablePlayback; - for (;;) { - result = ma_device__get_available_frames__wasapi(pDevice, (ma_IAudioClient*)pDevice->wasapi.pAudioClientPlayback, &framesAvailablePlayback); - if (result != MA_SUCCESS) { - break; - } - - if (framesAvailablePlayback >= pDevice->wasapi.actualBufferSizeInFramesPlayback) { - break; - } - - /* - Just a safety check to avoid an infinite loop. If this iteration results in a situation where the number of available frames - has not changed, get out of the loop. I don't think this should ever happen, but I think it's nice to have just in case. - */ - if (framesAvailablePlayback == prevFramesAvailablePlayback) { - break; - } - prevFramesAvailablePlayback = framesAvailablePlayback; - - ResetEvent((HANDLE)pDevice->wasapi.hEventPlayback); /* Manual reset. */ - WaitForSingleObject((HANDLE)pDevice->wasapi.hEventPlayback, waitTime); - } - } - } - - hr = ma_IAudioClient_Stop((ma_IAudioClient*)pDevice->wasapi.pAudioClientPlayback); - if (FAILED(hr)) { - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to stop internal playback device."); - return ma_result_from_HRESULT(hr); - } - - /* The audio client needs to be reset otherwise restarting will fail. */ - { - ma_int32 retries = 5; - - while ((hr = ma_IAudioClient_Reset((ma_IAudioClient*)pDevice->wasapi.pAudioClientPlayback)) == MA_AUDCLNT_E_BUFFER_OPERATION_PENDING && retries > 0) { - ma_sleep(10); - retries -= 1; - } - } - - if (FAILED(hr)) { - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to reset internal playback device."); - return ma_result_from_HRESULT(hr); - } - - ma_atomic_bool32_set(&pDevice->wasapi.isStartedPlayback, MA_FALSE); - } - - return MA_SUCCESS; -} - -static ma_result ma_device_stop__wasapi(ma_device* pDevice) -{ - ma_result result; - - MA_ASSERT(pDevice != NULL); - - /* Wait for any rerouting to finish before attempting to stop the device. */ - ma_mutex_lock(&pDevice->wasapi.rerouteLock); - { - result = ma_device_stop__wasapi_nolock(pDevice); - } - ma_mutex_unlock(&pDevice->wasapi.rerouteLock); - - return result; -} - - -#ifndef MA_WASAPI_WAIT_TIMEOUT_MILLISECONDS -#define MA_WASAPI_WAIT_TIMEOUT_MILLISECONDS 5000 -#endif - -static ma_result ma_device_read__wasapi(ma_device* pDevice, void* pFrames, ma_uint32 frameCount, ma_uint32* pFramesRead) -{ - ma_result result = MA_SUCCESS; - ma_uint32 totalFramesProcessed = 0; - - /* - When reading, we need to get a buffer and process all of it before releasing it. Because the - frame count (frameCount) can be different to the size of the buffer, we'll need to cache the - pointer to the buffer. - */ - - /* Keep running until we've processed the requested number of frames. */ - while (ma_device_get_state(pDevice) == ma_device_state_started && totalFramesProcessed < frameCount) { - ma_uint32 framesRemaining = frameCount - totalFramesProcessed; - - /* If we have a mapped data buffer, consume that first. */ - if (pDevice->wasapi.pMappedBufferCapture != NULL) { - /* We have a cached data pointer so consume that before grabbing another one from WASAPI. */ - ma_uint32 framesToProcessNow = framesRemaining; - if (framesToProcessNow > pDevice->wasapi.mappedBufferCaptureLen) { - framesToProcessNow = pDevice->wasapi.mappedBufferCaptureLen; - } - - /* Now just copy the data over to the output buffer. */ - ma_copy_pcm_frames( - ma_offset_pcm_frames_ptr(pFrames, totalFramesProcessed, pDevice->capture.internalFormat, pDevice->capture.internalChannels), - ma_offset_pcm_frames_const_ptr(pDevice->wasapi.pMappedBufferCapture, pDevice->wasapi.mappedBufferCaptureCap - pDevice->wasapi.mappedBufferCaptureLen, pDevice->capture.internalFormat, pDevice->capture.internalChannels), - framesToProcessNow, - pDevice->capture.internalFormat, pDevice->capture.internalChannels - ); - - totalFramesProcessed += framesToProcessNow; - pDevice->wasapi.mappedBufferCaptureLen -= framesToProcessNow; - - /* If the data buffer has been fully consumed we need to release it. */ - if (pDevice->wasapi.mappedBufferCaptureLen == 0) { - ma_IAudioCaptureClient_ReleaseBuffer((ma_IAudioCaptureClient*)pDevice->wasapi.pCaptureClient, pDevice->wasapi.mappedBufferCaptureCap); - pDevice->wasapi.pMappedBufferCapture = NULL; - pDevice->wasapi.mappedBufferCaptureCap = 0; - } - } else { - /* We don't have any cached data pointer, so grab another one. */ - HRESULT hr; - DWORD flags = 0; - - /* First just ask WASAPI for a data buffer. If it's not available, we'll wait for more. */ - hr = ma_IAudioCaptureClient_GetBuffer((ma_IAudioCaptureClient*)pDevice->wasapi.pCaptureClient, (BYTE**)&pDevice->wasapi.pMappedBufferCapture, &pDevice->wasapi.mappedBufferCaptureCap, &flags, NULL, NULL); - if (hr == S_OK) { - /* We got a data buffer. Continue to the next loop iteration which will then read from the mapped pointer. */ - pDevice->wasapi.mappedBufferCaptureLen = pDevice->wasapi.mappedBufferCaptureCap; - - /* - There have been reports that indicate that at times the AUDCLNT_BUFFERFLAGS_DATA_DISCONTINUITY is reported for every - call to IAudioCaptureClient_GetBuffer() above which results in spamming of the debug messages below. To partially - work around this, I'm only outputting these messages when MA_DEBUG_OUTPUT is explicitly defined. The better solution - would be to figure out why the flag is always getting reported. - */ - #if defined(MA_DEBUG_OUTPUT) - { - if (flags != 0) { - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "[WASAPI] Capture Flags: %ld\n", flags); - - if ((flags & MA_AUDCLNT_BUFFERFLAGS_DATA_DISCONTINUITY) != 0) { - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "[WASAPI] Data discontinuity (possible overrun). Attempting recovery. mappedBufferCaptureCap=%d\n", pDevice->wasapi.mappedBufferCaptureCap); - } - } - } - #endif - - /* Overrun detection. */ - if ((flags & MA_AUDCLNT_BUFFERFLAGS_DATA_DISCONTINUITY) != 0) { - /* Glitched. Probably due to an overrun. */ - - /* - If we got an overrun it probably means we're straddling the end of the buffer. In normal capture - mode this is the fault of the client application because they're responsible for ensuring data is - processed fast enough. In duplex mode, however, the processing of audio is tied to the playback - device, so this can possibly be the result of a timing de-sync. - - In capture mode we're not going to do any kind of recovery because the real fix is for the client - application to process faster. In duplex mode, we'll treat this as a desync and reset the buffers - to prevent a never-ending sequence of glitches due to straddling the end of the buffer. - */ - if (pDevice->type == ma_device_type_duplex) { - /* - Experiment: - - If we empty out the *entire* buffer we may end up putting ourselves into an underrun position - which isn't really any better than the overrun we're probably in right now. Instead we'll just - empty out about half. - */ - ma_uint32 i; - ma_uint32 periodCount = (pDevice->wasapi.actualBufferSizeInFramesCapture / pDevice->wasapi.periodSizeInFramesCapture); - ma_uint32 iterationCount = periodCount / 2; - if ((periodCount % 2) > 0) { - iterationCount += 1; - } - - for (i = 0; i < iterationCount; i += 1) { - hr = ma_IAudioCaptureClient_ReleaseBuffer((ma_IAudioCaptureClient*)pDevice->wasapi.pCaptureClient, pDevice->wasapi.mappedBufferCaptureCap); - if (FAILED(hr)) { - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "[WASAPI] Data discontinuity recovery: IAudioCaptureClient_ReleaseBuffer() failed with %ld.\n", hr); - break; - } - - flags = 0; - hr = ma_IAudioCaptureClient_GetBuffer((ma_IAudioCaptureClient*)pDevice->wasapi.pCaptureClient, (BYTE**)&pDevice->wasapi.pMappedBufferCapture, &pDevice->wasapi.mappedBufferCaptureCap, &flags, NULL, NULL); - if (hr == MA_AUDCLNT_S_BUFFER_EMPTY || FAILED(hr)) { - /* - The buffer has been completely emptied or an error occurred. In this case we'll need - to reset the state of the mapped buffer which will trigger the next iteration to get - a fresh buffer from WASAPI. - */ - pDevice->wasapi.pMappedBufferCapture = NULL; - pDevice->wasapi.mappedBufferCaptureCap = 0; - pDevice->wasapi.mappedBufferCaptureLen = 0; - - if (hr == MA_AUDCLNT_S_BUFFER_EMPTY) { - if ((flags & MA_AUDCLNT_BUFFERFLAGS_DATA_DISCONTINUITY) != 0) { - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "[WASAPI] Data discontinuity recovery: Buffer emptied, and data discontinuity still reported.\n"); - } else { - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "[WASAPI] Data discontinuity recovery: Buffer emptied.\n"); - } - } - - if (FAILED(hr)) { - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "[WASAPI] Data discontinuity recovery: IAudioCaptureClient_GetBuffer() failed with %ld.\n", hr); - } - - break; - } - } - - /* If at this point we have a valid buffer mapped, make sure the buffer length is set appropriately. */ - if (pDevice->wasapi.pMappedBufferCapture != NULL) { - pDevice->wasapi.mappedBufferCaptureLen = pDevice->wasapi.mappedBufferCaptureCap; - } - } - } - - continue; - } else { - if (hr == MA_AUDCLNT_S_BUFFER_EMPTY || hr == MA_AUDCLNT_E_BUFFER_ERROR) { - /* - No data is available. We need to wait for more. There's two situations to consider - here. The first is normal capture mode. If this times out it probably means the - microphone isn't delivering data for whatever reason. In this case we'll just - abort the read and return whatever we were able to get. The other situations is - loopback mode, in which case a timeout probably just means the nothing is playing - through the speakers. - */ - - /* Experiment: Use a shorter timeout for loopback mode. */ - DWORD timeoutInMilliseconds = MA_WASAPI_WAIT_TIMEOUT_MILLISECONDS; - if (pDevice->type == ma_device_type_loopback) { - timeoutInMilliseconds = 10; - } - - if (WaitForSingleObject((HANDLE)pDevice->wasapi.hEventCapture, timeoutInMilliseconds) != WAIT_OBJECT_0) { - if (pDevice->type == ma_device_type_loopback) { - continue; /* Keep waiting in loopback mode. */ - } else { - result = MA_ERROR; - break; /* Wait failed. */ - } - } - - /* At this point we should be able to loop back to the start of the loop and try retrieving a data buffer again. */ - } else { - /* An error occurred and we need to abort. */ - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to retrieve internal buffer from capture device in preparation for reading from the device. HRESULT = %d. Stopping device.\n", (int)hr); - result = ma_result_from_HRESULT(hr); - break; - } - } - } - } - - /* - If we were unable to process the entire requested frame count, but we still have a mapped buffer, - there's a good chance either an error occurred or the device was stopped mid-read. In this case - we'll need to make sure the buffer is released. - */ - if (totalFramesProcessed < frameCount && pDevice->wasapi.pMappedBufferCapture != NULL) { - ma_IAudioCaptureClient_ReleaseBuffer((ma_IAudioCaptureClient*)pDevice->wasapi.pCaptureClient, pDevice->wasapi.mappedBufferCaptureCap); - pDevice->wasapi.pMappedBufferCapture = NULL; - pDevice->wasapi.mappedBufferCaptureCap = 0; - pDevice->wasapi.mappedBufferCaptureLen = 0; - } - - if (pFramesRead != NULL) { - *pFramesRead = totalFramesProcessed; - } - - return result; -} - -static ma_result ma_device_write__wasapi(ma_device* pDevice, const void* pFrames, ma_uint32 frameCount, ma_uint32* pFramesWritten) -{ - ma_result result = MA_SUCCESS; - ma_uint32 totalFramesProcessed = 0; - - /* Keep writing to the device until it's stopped or we've consumed all of our input. */ - while (ma_device_get_state(pDevice) == ma_device_state_started && totalFramesProcessed < frameCount) { - ma_uint32 framesRemaining = frameCount - totalFramesProcessed; - - /* - We're going to do this in a similar way to capture. We'll first check if the cached data pointer - is valid, and if so, read from that. Otherwise We will call IAudioRenderClient_GetBuffer() with - a requested buffer size equal to our actual period size. If it returns AUDCLNT_E_BUFFER_TOO_LARGE - it means we need to wait for some data to become available. - */ - if (pDevice->wasapi.pMappedBufferPlayback != NULL) { - /* We still have some space available in the mapped data buffer. Write to it. */ - ma_uint32 framesToProcessNow = framesRemaining; - if (framesToProcessNow > (pDevice->wasapi.mappedBufferPlaybackCap - pDevice->wasapi.mappedBufferPlaybackLen)) { - framesToProcessNow = (pDevice->wasapi.mappedBufferPlaybackCap - pDevice->wasapi.mappedBufferPlaybackLen); - } - - /* Now just copy the data over to the output buffer. */ - ma_copy_pcm_frames( - ma_offset_pcm_frames_ptr(pDevice->wasapi.pMappedBufferPlayback, pDevice->wasapi.mappedBufferPlaybackLen, pDevice->playback.internalFormat, pDevice->playback.internalChannels), - ma_offset_pcm_frames_const_ptr(pFrames, totalFramesProcessed, pDevice->playback.internalFormat, pDevice->playback.internalChannels), - framesToProcessNow, - pDevice->playback.internalFormat, pDevice->playback.internalChannels - ); - - totalFramesProcessed += framesToProcessNow; - pDevice->wasapi.mappedBufferPlaybackLen += framesToProcessNow; - - /* If the data buffer has been fully consumed we need to release it. */ - if (pDevice->wasapi.mappedBufferPlaybackLen == pDevice->wasapi.mappedBufferPlaybackCap) { - ma_IAudioRenderClient_ReleaseBuffer((ma_IAudioRenderClient*)pDevice->wasapi.pRenderClient, pDevice->wasapi.mappedBufferPlaybackCap, 0); - pDevice->wasapi.pMappedBufferPlayback = NULL; - pDevice->wasapi.mappedBufferPlaybackCap = 0; - pDevice->wasapi.mappedBufferPlaybackLen = 0; - - /* - In exclusive mode we need to wait here. Exclusive mode is weird because GetBuffer() never - seems to return AUDCLNT_E_BUFFER_TOO_LARGE, which is what we normally use to determine - whether or not we need to wait for more data. - */ - if (pDevice->playback.shareMode == ma_share_mode_exclusive) { - if (WaitForSingleObject((HANDLE)pDevice->wasapi.hEventPlayback, MA_WASAPI_WAIT_TIMEOUT_MILLISECONDS) != WAIT_OBJECT_0) { - result = MA_ERROR; - break; /* Wait failed. Probably timed out. */ - } - } - } - } else { - /* We don't have a mapped data buffer so we'll need to get one. */ - HRESULT hr; - ma_uint32 bufferSizeInFrames; - - /* Special rules for exclusive mode. */ - if (pDevice->playback.shareMode == ma_share_mode_exclusive) { - bufferSizeInFrames = pDevice->wasapi.actualBufferSizeInFramesPlayback; - } else { - bufferSizeInFrames = pDevice->wasapi.periodSizeInFramesPlayback; - } - - hr = ma_IAudioRenderClient_GetBuffer((ma_IAudioRenderClient*)pDevice->wasapi.pRenderClient, bufferSizeInFrames, (BYTE**)&pDevice->wasapi.pMappedBufferPlayback); - if (hr == S_OK) { - /* We have data available. */ - pDevice->wasapi.mappedBufferPlaybackCap = bufferSizeInFrames; - pDevice->wasapi.mappedBufferPlaybackLen = 0; - } else { - if (hr == MA_AUDCLNT_E_BUFFER_TOO_LARGE || hr == MA_AUDCLNT_E_BUFFER_ERROR) { - /* Not enough data available. We need to wait for more. */ - if (WaitForSingleObject((HANDLE)pDevice->wasapi.hEventPlayback, MA_WASAPI_WAIT_TIMEOUT_MILLISECONDS) != WAIT_OBJECT_0) { - result = MA_ERROR; - break; /* Wait failed. Probably timed out. */ - } - } else { - /* Some error occurred. We'll need to abort. */ - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to retrieve internal buffer from playback device in preparation for writing to the device. HRESULT = %d. Stopping device.\n", (int)hr); - result = ma_result_from_HRESULT(hr); - break; - } - } - } - } - - if (pFramesWritten != NULL) { - *pFramesWritten = totalFramesProcessed; - } - - return result; -} - -static ma_result ma_device_data_loop_wakeup__wasapi(ma_device* pDevice) -{ - MA_ASSERT(pDevice != NULL); - - if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex || pDevice->type == ma_device_type_loopback) { - SetEvent((HANDLE)pDevice->wasapi.hEventCapture); - } - - if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) { - SetEvent((HANDLE)pDevice->wasapi.hEventPlayback); - } - - return MA_SUCCESS; -} - - -static ma_result ma_context_uninit__wasapi(ma_context* pContext) -{ - ma_context_command__wasapi cmd = ma_context_init_command__wasapi(MA_CONTEXT_COMMAND_QUIT__WASAPI); - - MA_ASSERT(pContext != NULL); - MA_ASSERT(pContext->backend == ma_backend_wasapi); - - ma_context_post_command__wasapi(pContext, &cmd); - ma_thread_wait(&pContext->wasapi.commandThread); - - if (pContext->wasapi.hAvrt) { - ma_dlclose(ma_context_get_log(pContext), pContext->wasapi.hAvrt); - pContext->wasapi.hAvrt = NULL; - } - - #if defined(MA_WIN32_UWP) - { - if (pContext->wasapi.hMMDevapi) { - ma_dlclose(ma_context_get_log(pContext), pContext->wasapi.hMMDevapi); - pContext->wasapi.hMMDevapi = NULL; - } - } - #endif - - /* Only after the thread has been terminated can we uninitialize the sync objects for the command thread. */ - ma_semaphore_uninit(&pContext->wasapi.commandSem); - ma_mutex_uninit(&pContext->wasapi.commandLock); - - return MA_SUCCESS; -} - -static ma_result ma_context_init__wasapi(ma_context* pContext, const ma_context_config* pConfig, ma_backend_callbacks* pCallbacks) -{ - ma_result result = MA_SUCCESS; - - MA_ASSERT(pContext != NULL); - - (void)pConfig; - -#ifdef MA_WIN32_DESKTOP - /* - WASAPI is only supported in Vista SP1 and newer. The reason for SP1 and not the base version of Vista is that event-driven - exclusive mode does not work until SP1. - - Unfortunately older compilers don't define these functions so we need to dynamically load them in order to avoid a link error. - */ - { - ma_OSVERSIONINFOEXW osvi; - ma_handle kernel32DLL; - ma_PFNVerifyVersionInfoW _VerifyVersionInfoW; - ma_PFNVerSetConditionMask _VerSetConditionMask; - - kernel32DLL = ma_dlopen(ma_context_get_log(pContext), "kernel32.dll"); - if (kernel32DLL == NULL) { - return MA_NO_BACKEND; - } - - _VerifyVersionInfoW = (ma_PFNVerifyVersionInfoW )ma_dlsym(ma_context_get_log(pContext), kernel32DLL, "VerifyVersionInfoW"); - _VerSetConditionMask = (ma_PFNVerSetConditionMask)ma_dlsym(ma_context_get_log(pContext), kernel32DLL, "VerSetConditionMask"); - if (_VerifyVersionInfoW == NULL || _VerSetConditionMask == NULL) { - ma_dlclose(ma_context_get_log(pContext), kernel32DLL); - return MA_NO_BACKEND; - } - - MA_ZERO_OBJECT(&osvi); - osvi.dwOSVersionInfoSize = sizeof(osvi); - osvi.dwMajorVersion = ((MA_WIN32_WINNT_VISTA >> 8) & 0xFF); - osvi.dwMinorVersion = ((MA_WIN32_WINNT_VISTA >> 0) & 0xFF); - osvi.wServicePackMajor = 1; - if (_VerifyVersionInfoW(&osvi, MA_VER_MAJORVERSION | MA_VER_MINORVERSION | MA_VER_SERVICEPACKMAJOR, _VerSetConditionMask(_VerSetConditionMask(_VerSetConditionMask(0, MA_VER_MAJORVERSION, MA_VER_GREATER_EQUAL), MA_VER_MINORVERSION, MA_VER_GREATER_EQUAL), MA_VER_SERVICEPACKMAJOR, MA_VER_GREATER_EQUAL))) { - result = MA_SUCCESS; - } else { - result = MA_NO_BACKEND; - } - - ma_dlclose(ma_context_get_log(pContext), kernel32DLL); - } -#endif - - if (result != MA_SUCCESS) { - return result; - } - - MA_ZERO_OBJECT(&pContext->wasapi); - - - #if defined(MA_WIN32_UWP) - { - /* Link to mmdevapi so we can get access to ActivateAudioInterfaceAsync(). */ - pContext->wasapi.hMMDevapi = ma_dlopen(ma_context_get_log(pContext), "mmdevapi.dll"); - if (pContext->wasapi.hMMDevapi) { - pContext->wasapi.ActivateAudioInterfaceAsync = ma_dlsym(ma_context_get_log(pContext), pContext->wasapi.hMMDevapi, "ActivateAudioInterfaceAsync"); - if (pContext->wasapi.ActivateAudioInterfaceAsync == NULL) { - ma_dlclose(ma_context_get_log(pContext), pContext->wasapi.hMMDevapi); - return MA_NO_BACKEND; /* ActivateAudioInterfaceAsync() could not be loaded. */ - } - } else { - return MA_NO_BACKEND; /* Failed to load mmdevapi.dll which is required for ActivateAudioInterfaceAsync() */ - } - } - #endif - - /* Optionally use the Avrt API to specify the audio thread's latency sensitivity requirements */ - pContext->wasapi.hAvrt = ma_dlopen(ma_context_get_log(pContext), "avrt.dll"); - if (pContext->wasapi.hAvrt) { - pContext->wasapi.AvSetMmThreadCharacteristicsA = ma_dlsym(ma_context_get_log(pContext), pContext->wasapi.hAvrt, "AvSetMmThreadCharacteristicsA"); - pContext->wasapi.AvRevertMmThreadcharacteristics = ma_dlsym(ma_context_get_log(pContext), pContext->wasapi.hAvrt, "AvRevertMmThreadCharacteristics"); - - /* If either function could not be found, disable use of avrt entirely. */ - if (!pContext->wasapi.AvSetMmThreadCharacteristicsA || !pContext->wasapi.AvRevertMmThreadcharacteristics) { - pContext->wasapi.AvSetMmThreadCharacteristicsA = NULL; - pContext->wasapi.AvRevertMmThreadcharacteristics = NULL; - ma_dlclose(ma_context_get_log(pContext), pContext->wasapi.hAvrt); - pContext->wasapi.hAvrt = NULL; - } - } - - - /* - Annoyingly, WASAPI does not allow you to release an IAudioClient object from a different thread - than the one that retrieved it with GetService(). This can result in a deadlock in two - situations: - - 1) When calling ma_device_uninit() from a different thread to ma_device_init(); and - 2) When uninitializing and reinitializing the internal IAudioClient object in response to - automatic stream routing. - - We could define ma_device_uninit() such that it must be called on the same thread as - ma_device_init(). We could also just not release the IAudioClient when performing automatic - stream routing to avoid the deadlock. Neither of these are acceptable solutions in my view so - we're going to have to work around this with a worker thread. This is not ideal, but I can't - think of a better way to do this. - - More information about this can be found here: - - https://docs.microsoft.com/en-us/windows/win32/api/audioclient/nn-audioclient-iaudiorenderclient - - Note this section: - - When releasing an IAudioRenderClient interface instance, the client must call the interface's - Release method from the same thread as the call to IAudioClient::GetService that created the - object. - */ - { - result = ma_mutex_init(&pContext->wasapi.commandLock); - if (result != MA_SUCCESS) { - return result; - } - - result = ma_semaphore_init(0, &pContext->wasapi.commandSem); - if (result != MA_SUCCESS) { - ma_mutex_uninit(&pContext->wasapi.commandLock); - return result; - } - - result = ma_thread_create(&pContext->wasapi.commandThread, ma_thread_priority_normal, 0, ma_context_command_thread__wasapi, pContext, &pContext->allocationCallbacks); - if (result != MA_SUCCESS) { - ma_semaphore_uninit(&pContext->wasapi.commandSem); - ma_mutex_uninit(&pContext->wasapi.commandLock); - return result; - } - } - - - pCallbacks->onContextInit = ma_context_init__wasapi; - pCallbacks->onContextUninit = ma_context_uninit__wasapi; - pCallbacks->onContextEnumerateDevices = ma_context_enumerate_devices__wasapi; - pCallbacks->onContextGetDeviceInfo = ma_context_get_device_info__wasapi; - pCallbacks->onDeviceInit = ma_device_init__wasapi; - pCallbacks->onDeviceUninit = ma_device_uninit__wasapi; - pCallbacks->onDeviceStart = ma_device_start__wasapi; - pCallbacks->onDeviceStop = ma_device_stop__wasapi; - pCallbacks->onDeviceRead = ma_device_read__wasapi; - pCallbacks->onDeviceWrite = ma_device_write__wasapi; - pCallbacks->onDeviceDataLoop = NULL; - pCallbacks->onDeviceDataLoopWakeup = ma_device_data_loop_wakeup__wasapi; - - return MA_SUCCESS; -} -#endif - -/****************************************************************************** - -DirectSound Backend - -******************************************************************************/ -#ifdef MA_HAS_DSOUND -/*#include */ - -/*static const GUID MA_GUID_IID_DirectSoundNotify = {0xb0210783, 0x89cd, 0x11d0, {0xaf, 0x08, 0x00, 0xa0, 0xc9, 0x25, 0xcd, 0x16}};*/ - -/* miniaudio only uses priority or exclusive modes. */ -#define MA_DSSCL_NORMAL 1 -#define MA_DSSCL_PRIORITY 2 -#define MA_DSSCL_EXCLUSIVE 3 -#define MA_DSSCL_WRITEPRIMARY 4 - -#define MA_DSCAPS_PRIMARYMONO 0x00000001 -#define MA_DSCAPS_PRIMARYSTEREO 0x00000002 -#define MA_DSCAPS_PRIMARY8BIT 0x00000004 -#define MA_DSCAPS_PRIMARY16BIT 0x00000008 -#define MA_DSCAPS_CONTINUOUSRATE 0x00000010 -#define MA_DSCAPS_EMULDRIVER 0x00000020 -#define MA_DSCAPS_CERTIFIED 0x00000040 -#define MA_DSCAPS_SECONDARYMONO 0x00000100 -#define MA_DSCAPS_SECONDARYSTEREO 0x00000200 -#define MA_DSCAPS_SECONDARY8BIT 0x00000400 -#define MA_DSCAPS_SECONDARY16BIT 0x00000800 - -#define MA_DSBCAPS_PRIMARYBUFFER 0x00000001 -#define MA_DSBCAPS_STATIC 0x00000002 -#define MA_DSBCAPS_LOCHARDWARE 0x00000004 -#define MA_DSBCAPS_LOCSOFTWARE 0x00000008 -#define MA_DSBCAPS_CTRL3D 0x00000010 -#define MA_DSBCAPS_CTRLFREQUENCY 0x00000020 -#define MA_DSBCAPS_CTRLPAN 0x00000040 -#define MA_DSBCAPS_CTRLVOLUME 0x00000080 -#define MA_DSBCAPS_CTRLPOSITIONNOTIFY 0x00000100 -#define MA_DSBCAPS_CTRLFX 0x00000200 -#define MA_DSBCAPS_STICKYFOCUS 0x00004000 -#define MA_DSBCAPS_GLOBALFOCUS 0x00008000 -#define MA_DSBCAPS_GETCURRENTPOSITION2 0x00010000 -#define MA_DSBCAPS_MUTE3DATMAXDISTANCE 0x00020000 -#define MA_DSBCAPS_LOCDEFER 0x00040000 -#define MA_DSBCAPS_TRUEPLAYPOSITION 0x00080000 - -#define MA_DSBPLAY_LOOPING 0x00000001 -#define MA_DSBPLAY_LOCHARDWARE 0x00000002 -#define MA_DSBPLAY_LOCSOFTWARE 0x00000004 -#define MA_DSBPLAY_TERMINATEBY_TIME 0x00000008 -#define MA_DSBPLAY_TERMINATEBY_DISTANCE 0x00000010 -#define MA_DSBPLAY_TERMINATEBY_PRIORITY 0x00000020 - -#define MA_DSBSTATUS_PLAYING 0x00000001 -#define MA_DSBSTATUS_BUFFERLOST 0x00000002 -#define MA_DSBSTATUS_LOOPING 0x00000004 -#define MA_DSBSTATUS_LOCHARDWARE 0x00000008 -#define MA_DSBSTATUS_LOCSOFTWARE 0x00000010 -#define MA_DSBSTATUS_TERMINATED 0x00000020 - -#define MA_DSCBSTART_LOOPING 0x00000001 - -typedef struct -{ - DWORD dwSize; - DWORD dwFlags; - DWORD dwBufferBytes; - DWORD dwReserved; - MA_WAVEFORMATEX* lpwfxFormat; - GUID guid3DAlgorithm; -} MA_DSBUFFERDESC; - -typedef struct -{ - DWORD dwSize; - DWORD dwFlags; - DWORD dwBufferBytes; - DWORD dwReserved; - MA_WAVEFORMATEX* lpwfxFormat; - DWORD dwFXCount; - void* lpDSCFXDesc; /* <-- miniaudio doesn't use this, so set to void*. */ -} MA_DSCBUFFERDESC; - -typedef struct -{ - DWORD dwSize; - DWORD dwFlags; - DWORD dwMinSecondarySampleRate; - DWORD dwMaxSecondarySampleRate; - DWORD dwPrimaryBuffers; - DWORD dwMaxHwMixingAllBuffers; - DWORD dwMaxHwMixingStaticBuffers; - DWORD dwMaxHwMixingStreamingBuffers; - DWORD dwFreeHwMixingAllBuffers; - DWORD dwFreeHwMixingStaticBuffers; - DWORD dwFreeHwMixingStreamingBuffers; - DWORD dwMaxHw3DAllBuffers; - DWORD dwMaxHw3DStaticBuffers; - DWORD dwMaxHw3DStreamingBuffers; - DWORD dwFreeHw3DAllBuffers; - DWORD dwFreeHw3DStaticBuffers; - DWORD dwFreeHw3DStreamingBuffers; - DWORD dwTotalHwMemBytes; - DWORD dwFreeHwMemBytes; - DWORD dwMaxContigFreeHwMemBytes; - DWORD dwUnlockTransferRateHwBuffers; - DWORD dwPlayCpuOverheadSwBuffers; - DWORD dwReserved1; - DWORD dwReserved2; -} MA_DSCAPS; - -typedef struct -{ - DWORD dwSize; - DWORD dwFlags; - DWORD dwBufferBytes; - DWORD dwUnlockTransferRate; - DWORD dwPlayCpuOverhead; -} MA_DSBCAPS; - -typedef struct -{ - DWORD dwSize; - DWORD dwFlags; - DWORD dwFormats; - DWORD dwChannels; -} MA_DSCCAPS; - -typedef struct -{ - DWORD dwSize; - DWORD dwFlags; - DWORD dwBufferBytes; - DWORD dwReserved; -} MA_DSCBCAPS; - -typedef struct -{ - DWORD dwOffset; - HANDLE hEventNotify; -} MA_DSBPOSITIONNOTIFY; - -typedef struct ma_IDirectSound ma_IDirectSound; -typedef struct ma_IDirectSoundBuffer ma_IDirectSoundBuffer; -typedef struct ma_IDirectSoundCapture ma_IDirectSoundCapture; -typedef struct ma_IDirectSoundCaptureBuffer ma_IDirectSoundCaptureBuffer; -typedef struct ma_IDirectSoundNotify ma_IDirectSoundNotify; - - -/* -COM objects. The way these work is that you have a vtable (a list of function pointers, kind of -like how C++ works internally), and then you have a structure with a single member, which is a -pointer to the vtable. The vtable is where the methods of the object are defined. Methods need -to be in a specific order, and parent classes need to have their methods declared first. -*/ - -/* IDirectSound */ -typedef struct -{ - /* IUnknown */ - HRESULT (STDMETHODCALLTYPE * QueryInterface)(ma_IDirectSound* pThis, const IID* const riid, void** ppObject); - ULONG (STDMETHODCALLTYPE * AddRef) (ma_IDirectSound* pThis); - ULONG (STDMETHODCALLTYPE * Release) (ma_IDirectSound* pThis); - - /* IDirectSound */ - HRESULT (STDMETHODCALLTYPE * CreateSoundBuffer) (ma_IDirectSound* pThis, const MA_DSBUFFERDESC* pDSBufferDesc, ma_IDirectSoundBuffer** ppDSBuffer, void* pUnkOuter); - HRESULT (STDMETHODCALLTYPE * GetCaps) (ma_IDirectSound* pThis, MA_DSCAPS* pDSCaps); - HRESULT (STDMETHODCALLTYPE * DuplicateSoundBuffer)(ma_IDirectSound* pThis, ma_IDirectSoundBuffer* pDSBufferOriginal, ma_IDirectSoundBuffer** ppDSBufferDuplicate); - HRESULT (STDMETHODCALLTYPE * SetCooperativeLevel) (ma_IDirectSound* pThis, HWND hwnd, DWORD dwLevel); - HRESULT (STDMETHODCALLTYPE * Compact) (ma_IDirectSound* pThis); - HRESULT (STDMETHODCALLTYPE * GetSpeakerConfig) (ma_IDirectSound* pThis, DWORD* pSpeakerConfig); - HRESULT (STDMETHODCALLTYPE * SetSpeakerConfig) (ma_IDirectSound* pThis, DWORD dwSpeakerConfig); - HRESULT (STDMETHODCALLTYPE * Initialize) (ma_IDirectSound* pThis, const GUID* pGuidDevice); -} ma_IDirectSoundVtbl; -struct ma_IDirectSound -{ - ma_IDirectSoundVtbl* lpVtbl; -}; -static MA_INLINE HRESULT ma_IDirectSound_QueryInterface(ma_IDirectSound* pThis, const IID* const riid, void** ppObject) { return pThis->lpVtbl->QueryInterface(pThis, riid, ppObject); } -static MA_INLINE ULONG ma_IDirectSound_AddRef(ma_IDirectSound* pThis) { return pThis->lpVtbl->AddRef(pThis); } -static MA_INLINE ULONG ma_IDirectSound_Release(ma_IDirectSound* pThis) { return pThis->lpVtbl->Release(pThis); } -static MA_INLINE HRESULT ma_IDirectSound_CreateSoundBuffer(ma_IDirectSound* pThis, const MA_DSBUFFERDESC* pDSBufferDesc, ma_IDirectSoundBuffer** ppDSBuffer, void* pUnkOuter) { return pThis->lpVtbl->CreateSoundBuffer(pThis, pDSBufferDesc, ppDSBuffer, pUnkOuter); } -static MA_INLINE HRESULT ma_IDirectSound_GetCaps(ma_IDirectSound* pThis, MA_DSCAPS* pDSCaps) { return pThis->lpVtbl->GetCaps(pThis, pDSCaps); } -static MA_INLINE HRESULT ma_IDirectSound_DuplicateSoundBuffer(ma_IDirectSound* pThis, ma_IDirectSoundBuffer* pDSBufferOriginal, ma_IDirectSoundBuffer** ppDSBufferDuplicate) { return pThis->lpVtbl->DuplicateSoundBuffer(pThis, pDSBufferOriginal, ppDSBufferDuplicate); } -static MA_INLINE HRESULT ma_IDirectSound_SetCooperativeLevel(ma_IDirectSound* pThis, HWND hwnd, DWORD dwLevel) { return pThis->lpVtbl->SetCooperativeLevel(pThis, hwnd, dwLevel); } -static MA_INLINE HRESULT ma_IDirectSound_Compact(ma_IDirectSound* pThis) { return pThis->lpVtbl->Compact(pThis); } -static MA_INLINE HRESULT ma_IDirectSound_GetSpeakerConfig(ma_IDirectSound* pThis, DWORD* pSpeakerConfig) { return pThis->lpVtbl->GetSpeakerConfig(pThis, pSpeakerConfig); } -static MA_INLINE HRESULT ma_IDirectSound_SetSpeakerConfig(ma_IDirectSound* pThis, DWORD dwSpeakerConfig) { return pThis->lpVtbl->SetSpeakerConfig(pThis, dwSpeakerConfig); } -static MA_INLINE HRESULT ma_IDirectSound_Initialize(ma_IDirectSound* pThis, const GUID* pGuidDevice) { return pThis->lpVtbl->Initialize(pThis, pGuidDevice); } - - -/* IDirectSoundBuffer */ -typedef struct -{ - /* IUnknown */ - HRESULT (STDMETHODCALLTYPE * QueryInterface)(ma_IDirectSoundBuffer* pThis, const IID* const riid, void** ppObject); - ULONG (STDMETHODCALLTYPE * AddRef) (ma_IDirectSoundBuffer* pThis); - ULONG (STDMETHODCALLTYPE * Release) (ma_IDirectSoundBuffer* pThis); - - /* IDirectSoundBuffer */ - HRESULT (STDMETHODCALLTYPE * GetCaps) (ma_IDirectSoundBuffer* pThis, MA_DSBCAPS* pDSBufferCaps); - HRESULT (STDMETHODCALLTYPE * GetCurrentPosition)(ma_IDirectSoundBuffer* pThis, DWORD* pCurrentPlayCursor, DWORD* pCurrentWriteCursor); - HRESULT (STDMETHODCALLTYPE * GetFormat) (ma_IDirectSoundBuffer* pThis, MA_WAVEFORMATEX* pFormat, DWORD dwSizeAllocated, DWORD* pSizeWritten); - HRESULT (STDMETHODCALLTYPE * GetVolume) (ma_IDirectSoundBuffer* pThis, LONG* pVolume); - HRESULT (STDMETHODCALLTYPE * GetPan) (ma_IDirectSoundBuffer* pThis, LONG* pPan); - HRESULT (STDMETHODCALLTYPE * GetFrequency) (ma_IDirectSoundBuffer* pThis, DWORD* pFrequency); - HRESULT (STDMETHODCALLTYPE * GetStatus) (ma_IDirectSoundBuffer* pThis, DWORD* pStatus); - HRESULT (STDMETHODCALLTYPE * Initialize) (ma_IDirectSoundBuffer* pThis, ma_IDirectSound* pDirectSound, const MA_DSBUFFERDESC* pDSBufferDesc); - HRESULT (STDMETHODCALLTYPE * Lock) (ma_IDirectSoundBuffer* pThis, DWORD dwOffset, DWORD dwBytes, void** ppAudioPtr1, DWORD* pAudioBytes1, void** ppAudioPtr2, DWORD* pAudioBytes2, DWORD dwFlags); - HRESULT (STDMETHODCALLTYPE * Play) (ma_IDirectSoundBuffer* pThis, DWORD dwReserved1, DWORD dwPriority, DWORD dwFlags); - HRESULT (STDMETHODCALLTYPE * SetCurrentPosition)(ma_IDirectSoundBuffer* pThis, DWORD dwNewPosition); - HRESULT (STDMETHODCALLTYPE * SetFormat) (ma_IDirectSoundBuffer* pThis, const MA_WAVEFORMATEX* pFormat); - HRESULT (STDMETHODCALLTYPE * SetVolume) (ma_IDirectSoundBuffer* pThis, LONG volume); - HRESULT (STDMETHODCALLTYPE * SetPan) (ma_IDirectSoundBuffer* pThis, LONG pan); - HRESULT (STDMETHODCALLTYPE * SetFrequency) (ma_IDirectSoundBuffer* pThis, DWORD dwFrequency); - HRESULT (STDMETHODCALLTYPE * Stop) (ma_IDirectSoundBuffer* pThis); - HRESULT (STDMETHODCALLTYPE * Unlock) (ma_IDirectSoundBuffer* pThis, void* pAudioPtr1, DWORD dwAudioBytes1, void* pAudioPtr2, DWORD dwAudioBytes2); - HRESULT (STDMETHODCALLTYPE * Restore) (ma_IDirectSoundBuffer* pThis); -} ma_IDirectSoundBufferVtbl; -struct ma_IDirectSoundBuffer -{ - ma_IDirectSoundBufferVtbl* lpVtbl; -}; -static MA_INLINE HRESULT ma_IDirectSoundBuffer_QueryInterface(ma_IDirectSoundBuffer* pThis, const IID* const riid, void** ppObject) { return pThis->lpVtbl->QueryInterface(pThis, riid, ppObject); } -static MA_INLINE ULONG ma_IDirectSoundBuffer_AddRef(ma_IDirectSoundBuffer* pThis) { return pThis->lpVtbl->AddRef(pThis); } -static MA_INLINE ULONG ma_IDirectSoundBuffer_Release(ma_IDirectSoundBuffer* pThis) { return pThis->lpVtbl->Release(pThis); } -static MA_INLINE HRESULT ma_IDirectSoundBuffer_GetCaps(ma_IDirectSoundBuffer* pThis, MA_DSBCAPS* pDSBufferCaps) { return pThis->lpVtbl->GetCaps(pThis, pDSBufferCaps); } -static MA_INLINE HRESULT ma_IDirectSoundBuffer_GetCurrentPosition(ma_IDirectSoundBuffer* pThis, DWORD* pCurrentPlayCursor, DWORD* pCurrentWriteCursor) { return pThis->lpVtbl->GetCurrentPosition(pThis, pCurrentPlayCursor, pCurrentWriteCursor); } -static MA_INLINE HRESULT ma_IDirectSoundBuffer_GetFormat(ma_IDirectSoundBuffer* pThis, MA_WAVEFORMATEX* pFormat, DWORD dwSizeAllocated, DWORD* pSizeWritten) { return pThis->lpVtbl->GetFormat(pThis, pFormat, dwSizeAllocated, pSizeWritten); } -static MA_INLINE HRESULT ma_IDirectSoundBuffer_GetVolume(ma_IDirectSoundBuffer* pThis, LONG* pVolume) { return pThis->lpVtbl->GetVolume(pThis, pVolume); } -static MA_INLINE HRESULT ma_IDirectSoundBuffer_GetPan(ma_IDirectSoundBuffer* pThis, LONG* pPan) { return pThis->lpVtbl->GetPan(pThis, pPan); } -static MA_INLINE HRESULT ma_IDirectSoundBuffer_GetFrequency(ma_IDirectSoundBuffer* pThis, DWORD* pFrequency) { return pThis->lpVtbl->GetFrequency(pThis, pFrequency); } -static MA_INLINE HRESULT ma_IDirectSoundBuffer_GetStatus(ma_IDirectSoundBuffer* pThis, DWORD* pStatus) { return pThis->lpVtbl->GetStatus(pThis, pStatus); } -static MA_INLINE HRESULT ma_IDirectSoundBuffer_Initialize(ma_IDirectSoundBuffer* pThis, ma_IDirectSound* pDirectSound, const MA_DSBUFFERDESC* pDSBufferDesc) { return pThis->lpVtbl->Initialize(pThis, pDirectSound, pDSBufferDesc); } -static MA_INLINE HRESULT ma_IDirectSoundBuffer_Lock(ma_IDirectSoundBuffer* pThis, DWORD dwOffset, DWORD dwBytes, void** ppAudioPtr1, DWORD* pAudioBytes1, void** ppAudioPtr2, DWORD* pAudioBytes2, DWORD dwFlags) { return pThis->lpVtbl->Lock(pThis, dwOffset, dwBytes, ppAudioPtr1, pAudioBytes1, ppAudioPtr2, pAudioBytes2, dwFlags); } -static MA_INLINE HRESULT ma_IDirectSoundBuffer_Play(ma_IDirectSoundBuffer* pThis, DWORD dwReserved1, DWORD dwPriority, DWORD dwFlags) { return pThis->lpVtbl->Play(pThis, dwReserved1, dwPriority, dwFlags); } -static MA_INLINE HRESULT ma_IDirectSoundBuffer_SetCurrentPosition(ma_IDirectSoundBuffer* pThis, DWORD dwNewPosition) { return pThis->lpVtbl->SetCurrentPosition(pThis, dwNewPosition); } -static MA_INLINE HRESULT ma_IDirectSoundBuffer_SetFormat(ma_IDirectSoundBuffer* pThis, const MA_WAVEFORMATEX* pFormat) { return pThis->lpVtbl->SetFormat(pThis, pFormat); } -static MA_INLINE HRESULT ma_IDirectSoundBuffer_SetVolume(ma_IDirectSoundBuffer* pThis, LONG volume) { return pThis->lpVtbl->SetVolume(pThis, volume); } -static MA_INLINE HRESULT ma_IDirectSoundBuffer_SetPan(ma_IDirectSoundBuffer* pThis, LONG pan) { return pThis->lpVtbl->SetPan(pThis, pan); } -static MA_INLINE HRESULT ma_IDirectSoundBuffer_SetFrequency(ma_IDirectSoundBuffer* pThis, DWORD dwFrequency) { return pThis->lpVtbl->SetFrequency(pThis, dwFrequency); } -static MA_INLINE HRESULT ma_IDirectSoundBuffer_Stop(ma_IDirectSoundBuffer* pThis) { return pThis->lpVtbl->Stop(pThis); } -static MA_INLINE HRESULT ma_IDirectSoundBuffer_Unlock(ma_IDirectSoundBuffer* pThis, void* pAudioPtr1, DWORD dwAudioBytes1, void* pAudioPtr2, DWORD dwAudioBytes2) { return pThis->lpVtbl->Unlock(pThis, pAudioPtr1, dwAudioBytes1, pAudioPtr2, dwAudioBytes2); } -static MA_INLINE HRESULT ma_IDirectSoundBuffer_Restore(ma_IDirectSoundBuffer* pThis) { return pThis->lpVtbl->Restore(pThis); } - - -/* IDirectSoundCapture */ -typedef struct -{ - /* IUnknown */ - HRESULT (STDMETHODCALLTYPE * QueryInterface)(ma_IDirectSoundCapture* pThis, const IID* const riid, void** ppObject); - ULONG (STDMETHODCALLTYPE * AddRef) (ma_IDirectSoundCapture* pThis); - ULONG (STDMETHODCALLTYPE * Release) (ma_IDirectSoundCapture* pThis); - - /* IDirectSoundCapture */ - HRESULT (STDMETHODCALLTYPE * CreateCaptureBuffer)(ma_IDirectSoundCapture* pThis, const MA_DSCBUFFERDESC* pDSCBufferDesc, ma_IDirectSoundCaptureBuffer** ppDSCBuffer, void* pUnkOuter); - HRESULT (STDMETHODCALLTYPE * GetCaps) (ma_IDirectSoundCapture* pThis, MA_DSCCAPS* pDSCCaps); - HRESULT (STDMETHODCALLTYPE * Initialize) (ma_IDirectSoundCapture* pThis, const GUID* pGuidDevice); -} ma_IDirectSoundCaptureVtbl; -struct ma_IDirectSoundCapture -{ - ma_IDirectSoundCaptureVtbl* lpVtbl; -}; -static MA_INLINE HRESULT ma_IDirectSoundCapture_QueryInterface (ma_IDirectSoundCapture* pThis, const IID* const riid, void** ppObject) { return pThis->lpVtbl->QueryInterface(pThis, riid, ppObject); } -static MA_INLINE ULONG ma_IDirectSoundCapture_AddRef (ma_IDirectSoundCapture* pThis) { return pThis->lpVtbl->AddRef(pThis); } -static MA_INLINE ULONG ma_IDirectSoundCapture_Release (ma_IDirectSoundCapture* pThis) { return pThis->lpVtbl->Release(pThis); } -static MA_INLINE HRESULT ma_IDirectSoundCapture_CreateCaptureBuffer(ma_IDirectSoundCapture* pThis, const MA_DSCBUFFERDESC* pDSCBufferDesc, ma_IDirectSoundCaptureBuffer** ppDSCBuffer, void* pUnkOuter) { return pThis->lpVtbl->CreateCaptureBuffer(pThis, pDSCBufferDesc, ppDSCBuffer, pUnkOuter); } -static MA_INLINE HRESULT ma_IDirectSoundCapture_GetCaps (ma_IDirectSoundCapture* pThis, MA_DSCCAPS* pDSCCaps) { return pThis->lpVtbl->GetCaps(pThis, pDSCCaps); } -static MA_INLINE HRESULT ma_IDirectSoundCapture_Initialize (ma_IDirectSoundCapture* pThis, const GUID* pGuidDevice) { return pThis->lpVtbl->Initialize(pThis, pGuidDevice); } - - -/* IDirectSoundCaptureBuffer */ -typedef struct -{ - /* IUnknown */ - HRESULT (STDMETHODCALLTYPE * QueryInterface)(ma_IDirectSoundCaptureBuffer* pThis, const IID* const riid, void** ppObject); - ULONG (STDMETHODCALLTYPE * AddRef) (ma_IDirectSoundCaptureBuffer* pThis); - ULONG (STDMETHODCALLTYPE * Release) (ma_IDirectSoundCaptureBuffer* pThis); - - /* IDirectSoundCaptureBuffer */ - HRESULT (STDMETHODCALLTYPE * GetCaps) (ma_IDirectSoundCaptureBuffer* pThis, MA_DSCBCAPS* pDSCBCaps); - HRESULT (STDMETHODCALLTYPE * GetCurrentPosition)(ma_IDirectSoundCaptureBuffer* pThis, DWORD* pCapturePosition, DWORD* pReadPosition); - HRESULT (STDMETHODCALLTYPE * GetFormat) (ma_IDirectSoundCaptureBuffer* pThis, MA_WAVEFORMATEX* pFormat, DWORD dwSizeAllocated, DWORD* pSizeWritten); - HRESULT (STDMETHODCALLTYPE * GetStatus) (ma_IDirectSoundCaptureBuffer* pThis, DWORD* pStatus); - HRESULT (STDMETHODCALLTYPE * Initialize) (ma_IDirectSoundCaptureBuffer* pThis, ma_IDirectSoundCapture* pDirectSoundCapture, const MA_DSCBUFFERDESC* pDSCBufferDesc); - HRESULT (STDMETHODCALLTYPE * Lock) (ma_IDirectSoundCaptureBuffer* pThis, DWORD dwOffset, DWORD dwBytes, void** ppAudioPtr1, DWORD* pAudioBytes1, void** ppAudioPtr2, DWORD* pAudioBytes2, DWORD dwFlags); - HRESULT (STDMETHODCALLTYPE * Start) (ma_IDirectSoundCaptureBuffer* pThis, DWORD dwFlags); - HRESULT (STDMETHODCALLTYPE * Stop) (ma_IDirectSoundCaptureBuffer* pThis); - HRESULT (STDMETHODCALLTYPE * Unlock) (ma_IDirectSoundCaptureBuffer* pThis, void* pAudioPtr1, DWORD dwAudioBytes1, void* pAudioPtr2, DWORD dwAudioBytes2); -} ma_IDirectSoundCaptureBufferVtbl; -struct ma_IDirectSoundCaptureBuffer -{ - ma_IDirectSoundCaptureBufferVtbl* lpVtbl; -}; -static MA_INLINE HRESULT ma_IDirectSoundCaptureBuffer_QueryInterface(ma_IDirectSoundCaptureBuffer* pThis, const IID* const riid, void** ppObject) { return pThis->lpVtbl->QueryInterface(pThis, riid, ppObject); } -static MA_INLINE ULONG ma_IDirectSoundCaptureBuffer_AddRef(ma_IDirectSoundCaptureBuffer* pThis) { return pThis->lpVtbl->AddRef(pThis); } -static MA_INLINE ULONG ma_IDirectSoundCaptureBuffer_Release(ma_IDirectSoundCaptureBuffer* pThis) { return pThis->lpVtbl->Release(pThis); } -static MA_INLINE HRESULT ma_IDirectSoundCaptureBuffer_GetCaps(ma_IDirectSoundCaptureBuffer* pThis, MA_DSCBCAPS* pDSCBCaps) { return pThis->lpVtbl->GetCaps(pThis, pDSCBCaps); } -static MA_INLINE HRESULT ma_IDirectSoundCaptureBuffer_GetCurrentPosition(ma_IDirectSoundCaptureBuffer* pThis, DWORD* pCapturePosition, DWORD* pReadPosition) { return pThis->lpVtbl->GetCurrentPosition(pThis, pCapturePosition, pReadPosition); } -static MA_INLINE HRESULT ma_IDirectSoundCaptureBuffer_GetFormat(ma_IDirectSoundCaptureBuffer* pThis, MA_WAVEFORMATEX* pFormat, DWORD dwSizeAllocated, DWORD* pSizeWritten) { return pThis->lpVtbl->GetFormat(pThis, pFormat, dwSizeAllocated, pSizeWritten); } -static MA_INLINE HRESULT ma_IDirectSoundCaptureBuffer_GetStatus(ma_IDirectSoundCaptureBuffer* pThis, DWORD* pStatus) { return pThis->lpVtbl->GetStatus(pThis, pStatus); } -static MA_INLINE HRESULT ma_IDirectSoundCaptureBuffer_Initialize(ma_IDirectSoundCaptureBuffer* pThis, ma_IDirectSoundCapture* pDirectSoundCapture, const MA_DSCBUFFERDESC* pDSCBufferDesc) { return pThis->lpVtbl->Initialize(pThis, pDirectSoundCapture, pDSCBufferDesc); } -static MA_INLINE HRESULT ma_IDirectSoundCaptureBuffer_Lock(ma_IDirectSoundCaptureBuffer* pThis, DWORD dwOffset, DWORD dwBytes, void** ppAudioPtr1, DWORD* pAudioBytes1, void** ppAudioPtr2, DWORD* pAudioBytes2, DWORD dwFlags) { return pThis->lpVtbl->Lock(pThis, dwOffset, dwBytes, ppAudioPtr1, pAudioBytes1, ppAudioPtr2, pAudioBytes2, dwFlags); } -static MA_INLINE HRESULT ma_IDirectSoundCaptureBuffer_Start(ma_IDirectSoundCaptureBuffer* pThis, DWORD dwFlags) { return pThis->lpVtbl->Start(pThis, dwFlags); } -static MA_INLINE HRESULT ma_IDirectSoundCaptureBuffer_Stop(ma_IDirectSoundCaptureBuffer* pThis) { return pThis->lpVtbl->Stop(pThis); } -static MA_INLINE HRESULT ma_IDirectSoundCaptureBuffer_Unlock(ma_IDirectSoundCaptureBuffer* pThis, void* pAudioPtr1, DWORD dwAudioBytes1, void* pAudioPtr2, DWORD dwAudioBytes2) { return pThis->lpVtbl->Unlock(pThis, pAudioPtr1, dwAudioBytes1, pAudioPtr2, dwAudioBytes2); } - - -/* IDirectSoundNotify */ -typedef struct -{ - /* IUnknown */ - HRESULT (STDMETHODCALLTYPE * QueryInterface)(ma_IDirectSoundNotify* pThis, const IID* const riid, void** ppObject); - ULONG (STDMETHODCALLTYPE * AddRef) (ma_IDirectSoundNotify* pThis); - ULONG (STDMETHODCALLTYPE * Release) (ma_IDirectSoundNotify* pThis); - - /* IDirectSoundNotify */ - HRESULT (STDMETHODCALLTYPE * SetNotificationPositions)(ma_IDirectSoundNotify* pThis, DWORD dwPositionNotifies, const MA_DSBPOSITIONNOTIFY* pPositionNotifies); -} ma_IDirectSoundNotifyVtbl; -struct ma_IDirectSoundNotify -{ - ma_IDirectSoundNotifyVtbl* lpVtbl; -}; -static MA_INLINE HRESULT ma_IDirectSoundNotify_QueryInterface(ma_IDirectSoundNotify* pThis, const IID* const riid, void** ppObject) { return pThis->lpVtbl->QueryInterface(pThis, riid, ppObject); } -static MA_INLINE ULONG ma_IDirectSoundNotify_AddRef(ma_IDirectSoundNotify* pThis) { return pThis->lpVtbl->AddRef(pThis); } -static MA_INLINE ULONG ma_IDirectSoundNotify_Release(ma_IDirectSoundNotify* pThis) { return pThis->lpVtbl->Release(pThis); } -static MA_INLINE HRESULT ma_IDirectSoundNotify_SetNotificationPositions(ma_IDirectSoundNotify* pThis, DWORD dwPositionNotifies, const MA_DSBPOSITIONNOTIFY* pPositionNotifies) { return pThis->lpVtbl->SetNotificationPositions(pThis, dwPositionNotifies, pPositionNotifies); } - - -typedef BOOL (CALLBACK * ma_DSEnumCallbackAProc) (GUID* pDeviceGUID, const char* pDeviceDescription, const char* pModule, void* pContext); -typedef HRESULT (WINAPI * ma_DirectSoundCreateProc) (const GUID* pcGuidDevice, ma_IDirectSound** ppDS8, ma_IUnknown* pUnkOuter); -typedef HRESULT (WINAPI * ma_DirectSoundEnumerateAProc) (ma_DSEnumCallbackAProc pDSEnumCallback, void* pContext); -typedef HRESULT (WINAPI * ma_DirectSoundCaptureCreateProc) (const GUID* pcGuidDevice, ma_IDirectSoundCapture** ppDSC8, ma_IUnknown* pUnkOuter); -typedef HRESULT (WINAPI * ma_DirectSoundCaptureEnumerateAProc)(ma_DSEnumCallbackAProc pDSEnumCallback, void* pContext); - -static ma_uint32 ma_get_best_sample_rate_within_range(ma_uint32 sampleRateMin, ma_uint32 sampleRateMax) -{ - /* Normalize the range in case we were given something stupid. */ - if (sampleRateMin < (ma_uint32)ma_standard_sample_rate_min) { - sampleRateMin = (ma_uint32)ma_standard_sample_rate_min; - } - if (sampleRateMax > (ma_uint32)ma_standard_sample_rate_max) { - sampleRateMax = (ma_uint32)ma_standard_sample_rate_max; - } - if (sampleRateMin > sampleRateMax) { - sampleRateMin = sampleRateMax; - } - - if (sampleRateMin == sampleRateMax) { - return sampleRateMax; - } else { - size_t iStandardRate; - for (iStandardRate = 0; iStandardRate < ma_countof(g_maStandardSampleRatePriorities); ++iStandardRate) { - ma_uint32 standardRate = g_maStandardSampleRatePriorities[iStandardRate]; - if (standardRate >= sampleRateMin && standardRate <= sampleRateMax) { - return standardRate; - } - } - } - - /* Should never get here. */ - MA_ASSERT(MA_FALSE); - return 0; -} - -/* -Retrieves the channel count and channel map for the given speaker configuration. If the speaker configuration is unknown, -the channel count and channel map will be left unmodified. -*/ -static void ma_get_channels_from_speaker_config__dsound(DWORD speakerConfig, WORD* pChannelsOut, DWORD* pChannelMapOut) -{ - WORD channels; - DWORD channelMap; - - channels = 0; - if (pChannelsOut != NULL) { - channels = *pChannelsOut; - } - - channelMap = 0; - if (pChannelMapOut != NULL) { - channelMap = *pChannelMapOut; - } - - /* - The speaker configuration is a combination of speaker config and speaker geometry. The lower 8 bits is what we care about. The upper - 16 bits is for the geometry. - */ - switch ((BYTE)(speakerConfig)) { - case 1 /*DSSPEAKER_HEADPHONE*/: channels = 2; channelMap = SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT; break; - case 2 /*DSSPEAKER_MONO*/: channels = 1; channelMap = SPEAKER_FRONT_CENTER; break; - case 3 /*DSSPEAKER_QUAD*/: channels = 4; channelMap = SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT | SPEAKER_BACK_LEFT | SPEAKER_BACK_RIGHT; break; - case 4 /*DSSPEAKER_STEREO*/: channels = 2; channelMap = SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT; break; - case 5 /*DSSPEAKER_SURROUND*/: channels = 4; channelMap = SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT | SPEAKER_FRONT_CENTER | SPEAKER_BACK_CENTER; break; - case 6 /*DSSPEAKER_5POINT1_BACK*/ /*DSSPEAKER_5POINT1*/: channels = 6; channelMap = SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT | SPEAKER_FRONT_CENTER | SPEAKER_LOW_FREQUENCY | SPEAKER_BACK_LEFT | SPEAKER_BACK_RIGHT; break; - case 7 /*DSSPEAKER_7POINT1_WIDE*/ /*DSSPEAKER_7POINT1*/: channels = 8; channelMap = SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT | SPEAKER_FRONT_CENTER | SPEAKER_LOW_FREQUENCY | SPEAKER_BACK_LEFT | SPEAKER_BACK_RIGHT | SPEAKER_FRONT_LEFT_OF_CENTER | SPEAKER_FRONT_RIGHT_OF_CENTER; break; - case 8 /*DSSPEAKER_7POINT1_SURROUND*/: channels = 8; channelMap = SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT | SPEAKER_FRONT_CENTER | SPEAKER_LOW_FREQUENCY | SPEAKER_BACK_LEFT | SPEAKER_BACK_RIGHT | SPEAKER_SIDE_LEFT | SPEAKER_SIDE_RIGHT; break; - case 9 /*DSSPEAKER_5POINT1_SURROUND*/: channels = 6; channelMap = SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT | SPEAKER_FRONT_CENTER | SPEAKER_LOW_FREQUENCY | SPEAKER_SIDE_LEFT | SPEAKER_SIDE_RIGHT; break; - default: break; - } - - if (pChannelsOut != NULL) { - *pChannelsOut = channels; - } - - if (pChannelMapOut != NULL) { - *pChannelMapOut = channelMap; - } -} - - -static ma_result ma_context_create_IDirectSound__dsound(ma_context* pContext, ma_share_mode shareMode, const ma_device_id* pDeviceID, ma_IDirectSound** ppDirectSound) -{ - ma_IDirectSound* pDirectSound; - HWND hWnd; - HRESULT hr; - - MA_ASSERT(pContext != NULL); - MA_ASSERT(ppDirectSound != NULL); - - *ppDirectSound = NULL; - pDirectSound = NULL; - - if (FAILED(((ma_DirectSoundCreateProc)pContext->dsound.DirectSoundCreate)((pDeviceID == NULL) ? NULL : (const GUID*)pDeviceID->dsound, &pDirectSound, NULL))) { - ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[DirectSound] DirectSoundCreate() failed for playback device."); - return MA_FAILED_TO_OPEN_BACKEND_DEVICE; - } - - /* The cooperative level must be set before doing anything else. */ - hWnd = (HWND)pContext->dsound.hWnd; - if (hWnd == 0) { - hWnd = ((MA_PFN_GetForegroundWindow)pContext->win32.GetForegroundWindow)(); - if (hWnd == 0) { - hWnd = ((MA_PFN_GetDesktopWindow)pContext->win32.GetDesktopWindow)(); - } - } - - hr = ma_IDirectSound_SetCooperativeLevel(pDirectSound, hWnd, (shareMode == ma_share_mode_exclusive) ? MA_DSSCL_EXCLUSIVE : MA_DSSCL_PRIORITY); - if (FAILED(hr)) { - ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[DirectSound] IDirectSound_SetCooperateiveLevel() failed for playback device."); - return ma_result_from_HRESULT(hr); - } - - *ppDirectSound = pDirectSound; - return MA_SUCCESS; -} - -static ma_result ma_context_create_IDirectSoundCapture__dsound(ma_context* pContext, ma_share_mode shareMode, const ma_device_id* pDeviceID, ma_IDirectSoundCapture** ppDirectSoundCapture) -{ - ma_IDirectSoundCapture* pDirectSoundCapture; - HRESULT hr; - - MA_ASSERT(pContext != NULL); - MA_ASSERT(ppDirectSoundCapture != NULL); - - /* DirectSound does not support exclusive mode for capture. */ - if (shareMode == ma_share_mode_exclusive) { - return MA_SHARE_MODE_NOT_SUPPORTED; - } - - *ppDirectSoundCapture = NULL; - pDirectSoundCapture = NULL; - - hr = ((ma_DirectSoundCaptureCreateProc)pContext->dsound.DirectSoundCaptureCreate)((pDeviceID == NULL) ? NULL : (const GUID*)pDeviceID->dsound, &pDirectSoundCapture, NULL); - if (FAILED(hr)) { - ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[DirectSound] DirectSoundCaptureCreate() failed for capture device."); - return ma_result_from_HRESULT(hr); - } - - *ppDirectSoundCapture = pDirectSoundCapture; - return MA_SUCCESS; -} - -static ma_result ma_context_get_format_info_for_IDirectSoundCapture__dsound(ma_context* pContext, ma_IDirectSoundCapture* pDirectSoundCapture, WORD* pChannels, WORD* pBitsPerSample, DWORD* pSampleRate) -{ - HRESULT hr; - MA_DSCCAPS caps; - WORD bitsPerSample; - DWORD sampleRate; - - MA_ASSERT(pContext != NULL); - MA_ASSERT(pDirectSoundCapture != NULL); - - if (pChannels) { - *pChannels = 0; - } - if (pBitsPerSample) { - *pBitsPerSample = 0; - } - if (pSampleRate) { - *pSampleRate = 0; - } - - MA_ZERO_OBJECT(&caps); - caps.dwSize = sizeof(caps); - hr = ma_IDirectSoundCapture_GetCaps(pDirectSoundCapture, &caps); - if (FAILED(hr)) { - ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[DirectSound] IDirectSoundCapture_GetCaps() failed for capture device."); - return ma_result_from_HRESULT(hr); - } - - if (pChannels) { - *pChannels = (WORD)caps.dwChannels; - } - - /* The device can support multiple formats. We just go through the different formats in order of priority and pick the first one. This the same type of system as the WinMM backend. */ - bitsPerSample = 16; - sampleRate = 48000; - - if (caps.dwChannels == 1) { - if ((caps.dwFormats & WAVE_FORMAT_48M16) != 0) { - sampleRate = 48000; - } else if ((caps.dwFormats & WAVE_FORMAT_44M16) != 0) { - sampleRate = 44100; - } else if ((caps.dwFormats & WAVE_FORMAT_2M16) != 0) { - sampleRate = 22050; - } else if ((caps.dwFormats & WAVE_FORMAT_1M16) != 0) { - sampleRate = 11025; - } else if ((caps.dwFormats & WAVE_FORMAT_96M16) != 0) { - sampleRate = 96000; - } else { - bitsPerSample = 8; - if ((caps.dwFormats & WAVE_FORMAT_48M08) != 0) { - sampleRate = 48000; - } else if ((caps.dwFormats & WAVE_FORMAT_44M08) != 0) { - sampleRate = 44100; - } else if ((caps.dwFormats & WAVE_FORMAT_2M08) != 0) { - sampleRate = 22050; - } else if ((caps.dwFormats & WAVE_FORMAT_1M08) != 0) { - sampleRate = 11025; - } else if ((caps.dwFormats & WAVE_FORMAT_96M08) != 0) { - sampleRate = 96000; - } else { - bitsPerSample = 16; /* Didn't find it. Just fall back to 16-bit. */ - } - } - } else if (caps.dwChannels == 2) { - if ((caps.dwFormats & WAVE_FORMAT_48S16) != 0) { - sampleRate = 48000; - } else if ((caps.dwFormats & WAVE_FORMAT_44S16) != 0) { - sampleRate = 44100; - } else if ((caps.dwFormats & WAVE_FORMAT_2S16) != 0) { - sampleRate = 22050; - } else if ((caps.dwFormats & WAVE_FORMAT_1S16) != 0) { - sampleRate = 11025; - } else if ((caps.dwFormats & WAVE_FORMAT_96S16) != 0) { - sampleRate = 96000; - } else { - bitsPerSample = 8; - if ((caps.dwFormats & WAVE_FORMAT_48S08) != 0) { - sampleRate = 48000; - } else if ((caps.dwFormats & WAVE_FORMAT_44S08) != 0) { - sampleRate = 44100; - } else if ((caps.dwFormats & WAVE_FORMAT_2S08) != 0) { - sampleRate = 22050; - } else if ((caps.dwFormats & WAVE_FORMAT_1S08) != 0) { - sampleRate = 11025; - } else if ((caps.dwFormats & WAVE_FORMAT_96S08) != 0) { - sampleRate = 96000; - } else { - bitsPerSample = 16; /* Didn't find it. Just fall back to 16-bit. */ - } - } - } - - if (pBitsPerSample) { - *pBitsPerSample = bitsPerSample; - } - if (pSampleRate) { - *pSampleRate = sampleRate; - } - - return MA_SUCCESS; -} - - -typedef struct -{ - ma_context* pContext; - ma_device_type deviceType; - ma_enum_devices_callback_proc callback; - void* pUserData; - ma_bool32 terminated; -} ma_context_enumerate_devices_callback_data__dsound; - -static BOOL CALLBACK ma_context_enumerate_devices_callback__dsound(GUID* lpGuid, const char* lpcstrDescription, const char* lpcstrModule, void* lpContext) -{ - ma_context_enumerate_devices_callback_data__dsound* pData = (ma_context_enumerate_devices_callback_data__dsound*)lpContext; - ma_device_info deviceInfo; - - (void)lpcstrModule; - - MA_ZERO_OBJECT(&deviceInfo); - - /* ID. */ - if (lpGuid != NULL) { - MA_COPY_MEMORY(deviceInfo.id.dsound, lpGuid, 16); - } else { - MA_ZERO_MEMORY(deviceInfo.id.dsound, 16); - deviceInfo.isDefault = MA_TRUE; - } - - /* Name / Description */ - ma_strncpy_s(deviceInfo.name, sizeof(deviceInfo.name), lpcstrDescription, (size_t)-1); - - - /* Call the callback function, but make sure we stop enumerating if the callee requested so. */ - MA_ASSERT(pData != NULL); - pData->terminated = (pData->callback(pData->pContext, pData->deviceType, &deviceInfo, pData->pUserData) == MA_FALSE); - if (pData->terminated) { - return FALSE; /* Stop enumeration. */ - } else { - return TRUE; /* Continue enumeration. */ - } -} - -static ma_result ma_context_enumerate_devices__dsound(ma_context* pContext, ma_enum_devices_callback_proc callback, void* pUserData) -{ - ma_context_enumerate_devices_callback_data__dsound data; - - MA_ASSERT(pContext != NULL); - MA_ASSERT(callback != NULL); - - data.pContext = pContext; - data.callback = callback; - data.pUserData = pUserData; - data.terminated = MA_FALSE; - - /* Playback. */ - if (!data.terminated) { - data.deviceType = ma_device_type_playback; - ((ma_DirectSoundEnumerateAProc)pContext->dsound.DirectSoundEnumerateA)(ma_context_enumerate_devices_callback__dsound, &data); - } - - /* Capture. */ - if (!data.terminated) { - data.deviceType = ma_device_type_capture; - ((ma_DirectSoundCaptureEnumerateAProc)pContext->dsound.DirectSoundCaptureEnumerateA)(ma_context_enumerate_devices_callback__dsound, &data); - } - - return MA_SUCCESS; -} - - -typedef struct -{ - const ma_device_id* pDeviceID; - ma_device_info* pDeviceInfo; - ma_bool32 found; -} ma_context_get_device_info_callback_data__dsound; - -static BOOL CALLBACK ma_context_get_device_info_callback__dsound(GUID* lpGuid, const char* lpcstrDescription, const char* lpcstrModule, void* lpContext) -{ - ma_context_get_device_info_callback_data__dsound* pData = (ma_context_get_device_info_callback_data__dsound*)lpContext; - MA_ASSERT(pData != NULL); - - if ((pData->pDeviceID == NULL || ma_is_guid_null(pData->pDeviceID->dsound)) && (lpGuid == NULL || ma_is_guid_null(lpGuid))) { - /* Default device. */ - ma_strncpy_s(pData->pDeviceInfo->name, sizeof(pData->pDeviceInfo->name), lpcstrDescription, (size_t)-1); - pData->pDeviceInfo->isDefault = MA_TRUE; - pData->found = MA_TRUE; - return FALSE; /* Stop enumeration. */ - } else { - /* Not the default device. */ - if (lpGuid != NULL && pData->pDeviceID != NULL) { - if (memcmp(pData->pDeviceID->dsound, lpGuid, sizeof(pData->pDeviceID->dsound)) == 0) { - ma_strncpy_s(pData->pDeviceInfo->name, sizeof(pData->pDeviceInfo->name), lpcstrDescription, (size_t)-1); - pData->found = MA_TRUE; - return FALSE; /* Stop enumeration. */ - } - } - } - - (void)lpcstrModule; - return TRUE; -} - -static ma_result ma_context_get_device_info__dsound(ma_context* pContext, ma_device_type deviceType, const ma_device_id* pDeviceID, ma_device_info* pDeviceInfo) -{ - ma_result result; - HRESULT hr; - - if (pDeviceID != NULL) { - ma_context_get_device_info_callback_data__dsound data; - - /* ID. */ - MA_COPY_MEMORY(pDeviceInfo->id.dsound, pDeviceID->dsound, 16); - - /* Name / Description. This is retrieved by enumerating over each device until we find that one that matches the input ID. */ - data.pDeviceID = pDeviceID; - data.pDeviceInfo = pDeviceInfo; - data.found = MA_FALSE; - if (deviceType == ma_device_type_playback) { - ((ma_DirectSoundEnumerateAProc)pContext->dsound.DirectSoundEnumerateA)(ma_context_get_device_info_callback__dsound, &data); - } else { - ((ma_DirectSoundCaptureEnumerateAProc)pContext->dsound.DirectSoundCaptureEnumerateA)(ma_context_get_device_info_callback__dsound, &data); - } - - if (!data.found) { - return MA_NO_DEVICE; - } - } else { - /* I don't think there's a way to get the name of the default device with DirectSound. In this case we just need to use defaults. */ - - /* ID */ - MA_ZERO_MEMORY(pDeviceInfo->id.dsound, 16); - - /* Name / Description */ - if (deviceType == ma_device_type_playback) { - ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), MA_DEFAULT_PLAYBACK_DEVICE_NAME, (size_t)-1); - } else { - ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), MA_DEFAULT_CAPTURE_DEVICE_NAME, (size_t)-1); - } - - pDeviceInfo->isDefault = MA_TRUE; - } - - /* Retrieving detailed information is slightly different depending on the device type. */ - if (deviceType == ma_device_type_playback) { - /* Playback. */ - ma_IDirectSound* pDirectSound; - MA_DSCAPS caps; - WORD channels; - - result = ma_context_create_IDirectSound__dsound(pContext, ma_share_mode_shared, pDeviceID, &pDirectSound); - if (result != MA_SUCCESS) { - return result; - } - - MA_ZERO_OBJECT(&caps); - caps.dwSize = sizeof(caps); - hr = ma_IDirectSound_GetCaps(pDirectSound, &caps); - if (FAILED(hr)) { - ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[DirectSound] IDirectSound_GetCaps() failed for playback device."); - return ma_result_from_HRESULT(hr); - } - - - /* Channels. Only a single channel count is reported for DirectSound. */ - if ((caps.dwFlags & MA_DSCAPS_PRIMARYSTEREO) != 0) { - /* It supports at least stereo, but could support more. */ - DWORD speakerConfig; - - channels = 2; - - /* Look at the speaker configuration to get a better idea on the channel count. */ - hr = ma_IDirectSound_GetSpeakerConfig(pDirectSound, &speakerConfig); - if (SUCCEEDED(hr)) { - ma_get_channels_from_speaker_config__dsound(speakerConfig, &channels, NULL); - } - } else { - /* It does not support stereo, which means we are stuck with mono. */ - channels = 1; - } - - - /* - In DirectSound, our native formats are centered around sample rates. All formats are supported, and we're only reporting a single channel - count. However, DirectSound can report a range of supported sample rates. We're only going to include standard rates known by miniaudio - in order to keep the size of this within reason. - */ - if ((caps.dwFlags & MA_DSCAPS_CONTINUOUSRATE) != 0) { - /* Multiple sample rates are supported. We'll report in order of our preferred sample rates. */ - size_t iStandardSampleRate; - for (iStandardSampleRate = 0; iStandardSampleRate < ma_countof(g_maStandardSampleRatePriorities); iStandardSampleRate += 1) { - ma_uint32 sampleRate = g_maStandardSampleRatePriorities[iStandardSampleRate]; - if (sampleRate >= caps.dwMinSecondarySampleRate && sampleRate <= caps.dwMaxSecondarySampleRate) { - pDeviceInfo->nativeDataFormats[pDeviceInfo->nativeDataFormatCount].format = ma_format_unknown; - pDeviceInfo->nativeDataFormats[pDeviceInfo->nativeDataFormatCount].channels = channels; - pDeviceInfo->nativeDataFormats[pDeviceInfo->nativeDataFormatCount].sampleRate = sampleRate; - pDeviceInfo->nativeDataFormats[pDeviceInfo->nativeDataFormatCount].flags = 0; - pDeviceInfo->nativeDataFormatCount += 1; - } - } - } else { - /* Only a single sample rate is supported. */ - pDeviceInfo->nativeDataFormats[pDeviceInfo->nativeDataFormatCount].format = ma_format_unknown; - pDeviceInfo->nativeDataFormats[pDeviceInfo->nativeDataFormatCount].channels = channels; - pDeviceInfo->nativeDataFormats[pDeviceInfo->nativeDataFormatCount].sampleRate = caps.dwMaxSecondarySampleRate; - pDeviceInfo->nativeDataFormats[pDeviceInfo->nativeDataFormatCount].flags = 0; - pDeviceInfo->nativeDataFormatCount += 1; - } - - ma_IDirectSound_Release(pDirectSound); - } else { - /* - Capture. This is a little different to playback due to the say the supported formats are reported. Technically capture - devices can support a number of different formats, but for simplicity and consistency with ma_device_init() I'm just - reporting the best format. - */ - ma_IDirectSoundCapture* pDirectSoundCapture; - WORD channels; - WORD bitsPerSample; - DWORD sampleRate; - - result = ma_context_create_IDirectSoundCapture__dsound(pContext, ma_share_mode_shared, pDeviceID, &pDirectSoundCapture); - if (result != MA_SUCCESS) { - return result; - } - - result = ma_context_get_format_info_for_IDirectSoundCapture__dsound(pContext, pDirectSoundCapture, &channels, &bitsPerSample, &sampleRate); - if (result != MA_SUCCESS) { - ma_IDirectSoundCapture_Release(pDirectSoundCapture); - return result; - } - - ma_IDirectSoundCapture_Release(pDirectSoundCapture); - - /* The format is always an integer format and is based on the bits per sample. */ - if (bitsPerSample == 8) { - pDeviceInfo->nativeDataFormats[0].format = ma_format_u8; - } else if (bitsPerSample == 16) { - pDeviceInfo->nativeDataFormats[0].format = ma_format_s16; - } else if (bitsPerSample == 24) { - pDeviceInfo->nativeDataFormats[0].format = ma_format_s24; - } else if (bitsPerSample == 32) { - pDeviceInfo->nativeDataFormats[0].format = ma_format_s32; - } else { - return MA_FORMAT_NOT_SUPPORTED; - } - - pDeviceInfo->nativeDataFormats[0].channels = channels; - pDeviceInfo->nativeDataFormats[0].sampleRate = sampleRate; - pDeviceInfo->nativeDataFormats[0].flags = 0; - pDeviceInfo->nativeDataFormatCount = 1; - } - - return MA_SUCCESS; -} - - - -static ma_result ma_device_uninit__dsound(ma_device* pDevice) -{ - MA_ASSERT(pDevice != NULL); - - if (pDevice->dsound.pCaptureBuffer != NULL) { - ma_IDirectSoundCaptureBuffer_Release((ma_IDirectSoundCaptureBuffer*)pDevice->dsound.pCaptureBuffer); - } - if (pDevice->dsound.pCapture != NULL) { - ma_IDirectSoundCapture_Release((ma_IDirectSoundCapture*)pDevice->dsound.pCapture); - } - - if (pDevice->dsound.pPlaybackBuffer != NULL) { - ma_IDirectSoundBuffer_Release((ma_IDirectSoundBuffer*)pDevice->dsound.pPlaybackBuffer); - } - if (pDevice->dsound.pPlaybackPrimaryBuffer != NULL) { - ma_IDirectSoundBuffer_Release((ma_IDirectSoundBuffer*)pDevice->dsound.pPlaybackPrimaryBuffer); - } - if (pDevice->dsound.pPlayback != NULL) { - ma_IDirectSound_Release((ma_IDirectSound*)pDevice->dsound.pPlayback); - } - - return MA_SUCCESS; -} - -static ma_result ma_config_to_WAVEFORMATEXTENSIBLE(ma_format format, ma_uint32 channels, ma_uint32 sampleRate, const ma_channel* pChannelMap, MA_WAVEFORMATEXTENSIBLE* pWF) -{ - GUID subformat; - - if (format == ma_format_unknown) { - format = MA_DEFAULT_FORMAT; - } - - if (channels == 0) { - channels = MA_DEFAULT_CHANNELS; - } - - if (sampleRate == 0) { - sampleRate = MA_DEFAULT_SAMPLE_RATE; - } - - switch (format) - { - case ma_format_u8: - case ma_format_s16: - case ma_format_s24: - /*case ma_format_s24_32:*/ - case ma_format_s32: - { - subformat = MA_GUID_KSDATAFORMAT_SUBTYPE_PCM; - } break; - - case ma_format_f32: - { - subformat = MA_GUID_KSDATAFORMAT_SUBTYPE_IEEE_FLOAT; - } break; - - default: - return MA_FORMAT_NOT_SUPPORTED; - } - - MA_ZERO_OBJECT(pWF); - pWF->cbSize = sizeof(*pWF); - pWF->wFormatTag = WAVE_FORMAT_EXTENSIBLE; - pWF->nChannels = (WORD)channels; - pWF->nSamplesPerSec = (DWORD)sampleRate; - pWF->wBitsPerSample = (WORD)(ma_get_bytes_per_sample(format)*8); - pWF->nBlockAlign = (WORD)(pWF->nChannels * pWF->wBitsPerSample / 8); - pWF->nAvgBytesPerSec = pWF->nBlockAlign * pWF->nSamplesPerSec; - pWF->Samples.wValidBitsPerSample = pWF->wBitsPerSample; - pWF->dwChannelMask = ma_channel_map_to_channel_mask__win32(pChannelMap, channels); - pWF->SubFormat = subformat; - - return MA_SUCCESS; -} - -static ma_uint32 ma_calculate_period_size_in_frames_from_descriptor__dsound(const ma_device_descriptor* pDescriptor, ma_uint32 nativeSampleRate, ma_performance_profile performanceProfile) -{ - /* - DirectSound has a minimum period size of 20ms. In practice, this doesn't seem to be enough for - reliable glitch-free processing so going to use 30ms instead. - */ - ma_uint32 minPeriodSizeInFrames = ma_calculate_buffer_size_in_frames_from_milliseconds(30, nativeSampleRate); - ma_uint32 periodSizeInFrames; - - periodSizeInFrames = ma_calculate_buffer_size_in_frames_from_descriptor(pDescriptor, nativeSampleRate, performanceProfile); - if (periodSizeInFrames < minPeriodSizeInFrames) { - periodSizeInFrames = minPeriodSizeInFrames; - } - - return periodSizeInFrames; -} - -static ma_result ma_device_init__dsound(ma_device* pDevice, const ma_device_config* pConfig, ma_device_descriptor* pDescriptorPlayback, ma_device_descriptor* pDescriptorCapture) -{ - ma_result result; - HRESULT hr; - - MA_ASSERT(pDevice != NULL); - - MA_ZERO_OBJECT(&pDevice->dsound); - - if (pConfig->deviceType == ma_device_type_loopback) { - return MA_DEVICE_TYPE_NOT_SUPPORTED; - } - - /* - Unfortunately DirectSound uses different APIs and data structures for playback and capture devices. We need to initialize - the capture device first because we'll want to match its buffer size and period count on the playback side if we're using - full-duplex mode. - */ - if (pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) { - MA_WAVEFORMATEXTENSIBLE wf; - MA_DSCBUFFERDESC descDS; - ma_uint32 periodSizeInFrames; - ma_uint32 periodCount; - char rawdata[1024]; /* <-- Ugly hack to avoid a malloc() due to a crappy DirectSound API. */ - MA_WAVEFORMATEXTENSIBLE* pActualFormat; - - result = ma_config_to_WAVEFORMATEXTENSIBLE(pDescriptorCapture->format, pDescriptorCapture->channels, pDescriptorCapture->sampleRate, pDescriptorCapture->channelMap, &wf); - if (result != MA_SUCCESS) { - return result; - } - - result = ma_context_create_IDirectSoundCapture__dsound(pDevice->pContext, pDescriptorCapture->shareMode, pDescriptorCapture->pDeviceID, (ma_IDirectSoundCapture**)&pDevice->dsound.pCapture); - if (result != MA_SUCCESS) { - ma_device_uninit__dsound(pDevice); - return result; - } - - result = ma_context_get_format_info_for_IDirectSoundCapture__dsound(pDevice->pContext, (ma_IDirectSoundCapture*)pDevice->dsound.pCapture, &wf.nChannels, &wf.wBitsPerSample, &wf.nSamplesPerSec); - if (result != MA_SUCCESS) { - ma_device_uninit__dsound(pDevice); - return result; - } - - wf.nBlockAlign = (WORD)(wf.nChannels * wf.wBitsPerSample / 8); - wf.nAvgBytesPerSec = wf.nBlockAlign * wf.nSamplesPerSec; - wf.Samples.wValidBitsPerSample = wf.wBitsPerSample; - wf.SubFormat = MA_GUID_KSDATAFORMAT_SUBTYPE_PCM; - - /* The size of the buffer must be a clean multiple of the period count. */ - periodSizeInFrames = ma_calculate_period_size_in_frames_from_descriptor__dsound(pDescriptorCapture, wf.nSamplesPerSec, pConfig->performanceProfile); - periodCount = (pDescriptorCapture->periodCount > 0) ? pDescriptorCapture->periodCount : MA_DEFAULT_PERIODS; - - MA_ZERO_OBJECT(&descDS); - descDS.dwSize = sizeof(descDS); - descDS.dwFlags = 0; - descDS.dwBufferBytes = periodSizeInFrames * periodCount * wf.nBlockAlign; - descDS.lpwfxFormat = (MA_WAVEFORMATEX*)&wf; - hr = ma_IDirectSoundCapture_CreateCaptureBuffer((ma_IDirectSoundCapture*)pDevice->dsound.pCapture, &descDS, (ma_IDirectSoundCaptureBuffer**)&pDevice->dsound.pCaptureBuffer, NULL); - if (FAILED(hr)) { - ma_device_uninit__dsound(pDevice); - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[DirectSound] IDirectSoundCapture_CreateCaptureBuffer() failed for capture device."); - return ma_result_from_HRESULT(hr); - } - - /* Get the _actual_ properties of the buffer. */ - pActualFormat = (MA_WAVEFORMATEXTENSIBLE*)rawdata; - hr = ma_IDirectSoundCaptureBuffer_GetFormat((ma_IDirectSoundCaptureBuffer*)pDevice->dsound.pCaptureBuffer, (MA_WAVEFORMATEX*)pActualFormat, sizeof(rawdata), NULL); - if (FAILED(hr)) { - ma_device_uninit__dsound(pDevice); - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[DirectSound] Failed to retrieve the actual format of the capture device's buffer."); - return ma_result_from_HRESULT(hr); - } - - /* We can now start setting the output data formats. */ - pDescriptorCapture->format = ma_format_from_WAVEFORMATEX((MA_WAVEFORMATEX*)pActualFormat); - pDescriptorCapture->channels = pActualFormat->nChannels; - pDescriptorCapture->sampleRate = pActualFormat->nSamplesPerSec; - - /* Get the native channel map based on the channel mask. */ - if (pActualFormat->wFormatTag == WAVE_FORMAT_EXTENSIBLE) { - ma_channel_mask_to_channel_map__win32(pActualFormat->dwChannelMask, pDescriptorCapture->channels, pDescriptorCapture->channelMap); - } else { - ma_channel_mask_to_channel_map__win32(wf.dwChannelMask, pDescriptorCapture->channels, pDescriptorCapture->channelMap); - } - - /* - After getting the actual format the size of the buffer in frames may have actually changed. However, we want this to be as close to what the - user has asked for as possible, so let's go ahead and release the old capture buffer and create a new one in this case. - */ - if (periodSizeInFrames != (descDS.dwBufferBytes / ma_get_bytes_per_frame(pDescriptorCapture->format, pDescriptorCapture->channels) / periodCount)) { - descDS.dwBufferBytes = periodSizeInFrames * ma_get_bytes_per_frame(pDescriptorCapture->format, pDescriptorCapture->channels) * periodCount; - ma_IDirectSoundCaptureBuffer_Release((ma_IDirectSoundCaptureBuffer*)pDevice->dsound.pCaptureBuffer); - - hr = ma_IDirectSoundCapture_CreateCaptureBuffer((ma_IDirectSoundCapture*)pDevice->dsound.pCapture, &descDS, (ma_IDirectSoundCaptureBuffer**)&pDevice->dsound.pCaptureBuffer, NULL); - if (FAILED(hr)) { - ma_device_uninit__dsound(pDevice); - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[DirectSound] Second attempt at IDirectSoundCapture_CreateCaptureBuffer() failed for capture device."); - return ma_result_from_HRESULT(hr); - } - } - - /* DirectSound should give us a buffer exactly the size we asked for. */ - pDescriptorCapture->periodSizeInFrames = periodSizeInFrames; - pDescriptorCapture->periodCount = periodCount; - } - - if (pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) { - MA_WAVEFORMATEXTENSIBLE wf; - MA_DSBUFFERDESC descDSPrimary; - MA_DSCAPS caps; - char rawdata[1024]; /* <-- Ugly hack to avoid a malloc() due to a crappy DirectSound API. */ - MA_WAVEFORMATEXTENSIBLE* pActualFormat; - ma_uint32 periodSizeInFrames; - ma_uint32 periodCount; - MA_DSBUFFERDESC descDS; - WORD nativeChannelCount; - DWORD nativeChannelMask = 0; - - result = ma_config_to_WAVEFORMATEXTENSIBLE(pDescriptorPlayback->format, pDescriptorPlayback->channels, pDescriptorPlayback->sampleRate, pDescriptorPlayback->channelMap, &wf); - if (result != MA_SUCCESS) { - return result; - } - - result = ma_context_create_IDirectSound__dsound(pDevice->pContext, pDescriptorPlayback->shareMode, pDescriptorPlayback->pDeviceID, (ma_IDirectSound**)&pDevice->dsound.pPlayback); - if (result != MA_SUCCESS) { - ma_device_uninit__dsound(pDevice); - return result; - } - - MA_ZERO_OBJECT(&descDSPrimary); - descDSPrimary.dwSize = sizeof(MA_DSBUFFERDESC); - descDSPrimary.dwFlags = MA_DSBCAPS_PRIMARYBUFFER | MA_DSBCAPS_CTRLVOLUME; - hr = ma_IDirectSound_CreateSoundBuffer((ma_IDirectSound*)pDevice->dsound.pPlayback, &descDSPrimary, (ma_IDirectSoundBuffer**)&pDevice->dsound.pPlaybackPrimaryBuffer, NULL); - if (FAILED(hr)) { - ma_device_uninit__dsound(pDevice); - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[DirectSound] IDirectSound_CreateSoundBuffer() failed for playback device's primary buffer."); - return ma_result_from_HRESULT(hr); - } - - - /* We may want to make some adjustments to the format if we are using defaults. */ - MA_ZERO_OBJECT(&caps); - caps.dwSize = sizeof(caps); - hr = ma_IDirectSound_GetCaps((ma_IDirectSound*)pDevice->dsound.pPlayback, &caps); - if (FAILED(hr)) { - ma_device_uninit__dsound(pDevice); - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[DirectSound] IDirectSound_GetCaps() failed for playback device."); - return ma_result_from_HRESULT(hr); - } - - if ((caps.dwFlags & MA_DSCAPS_PRIMARYSTEREO) != 0) { - DWORD speakerConfig; - - /* It supports at least stereo, but could support more. */ - nativeChannelCount = 2; - - /* Look at the speaker configuration to get a better idea on the channel count. */ - if (SUCCEEDED(ma_IDirectSound_GetSpeakerConfig((ma_IDirectSound*)pDevice->dsound.pPlayback, &speakerConfig))) { - ma_get_channels_from_speaker_config__dsound(speakerConfig, &nativeChannelCount, &nativeChannelMask); - } - } else { - /* It does not support stereo, which means we are stuck with mono. */ - nativeChannelCount = 1; - nativeChannelMask = 0x00000001; - } - - if (pDescriptorPlayback->channels == 0) { - wf.nChannels = nativeChannelCount; - wf.dwChannelMask = nativeChannelMask; - } - - if (pDescriptorPlayback->sampleRate == 0) { - /* We base the sample rate on the values returned by GetCaps(). */ - if ((caps.dwFlags & MA_DSCAPS_CONTINUOUSRATE) != 0) { - wf.nSamplesPerSec = ma_get_best_sample_rate_within_range(caps.dwMinSecondarySampleRate, caps.dwMaxSecondarySampleRate); - } else { - wf.nSamplesPerSec = caps.dwMaxSecondarySampleRate; - } - } - - wf.nBlockAlign = (WORD)(wf.nChannels * wf.wBitsPerSample / 8); - wf.nAvgBytesPerSec = wf.nBlockAlign * wf.nSamplesPerSec; - - /* - From MSDN: - - The method succeeds even if the hardware does not support the requested format; DirectSound sets the buffer to the closest - supported format. To determine whether this has happened, an application can call the GetFormat method for the primary buffer - and compare the result with the format that was requested with the SetFormat method. - */ - hr = ma_IDirectSoundBuffer_SetFormat((ma_IDirectSoundBuffer*)pDevice->dsound.pPlaybackPrimaryBuffer, (MA_WAVEFORMATEX*)&wf); - if (FAILED(hr)) { - /* - If setting of the format failed we'll try again with some fallback settings. On Windows 98 I have - observed that IEEE_FLOAT does not work. We'll therefore enforce PCM. I also had issues where a - sample rate of 48000 did not work correctly. Not sure if it was a driver issue or not, but will - use 44100 for the sample rate. - */ - wf.cbSize = 18; /* NOTE: Don't use sizeof(MA_WAVEFORMATEX) here because it's got an extra 2 bytes due to padding. */ - wf.wFormatTag = WAVE_FORMAT_PCM; - wf.wBitsPerSample = 16; - wf.nChannels = nativeChannelCount; - wf.nSamplesPerSec = 44100; - wf.nBlockAlign = wf.nChannels * (wf.wBitsPerSample / 8); - wf.nAvgBytesPerSec = wf.nSamplesPerSec * wf.nBlockAlign; - - hr = ma_IDirectSoundBuffer_SetFormat((ma_IDirectSoundBuffer*)pDevice->dsound.pPlaybackPrimaryBuffer, (MA_WAVEFORMATEX*)&wf); - if (FAILED(hr)) { - ma_device_uninit__dsound(pDevice); - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[DirectSound] Failed to set format of playback device's primary buffer."); - return ma_result_from_HRESULT(hr); - } - } - - /* Get the _actual_ properties of the buffer. */ - pActualFormat = (MA_WAVEFORMATEXTENSIBLE*)rawdata; - hr = ma_IDirectSoundBuffer_GetFormat((ma_IDirectSoundBuffer*)pDevice->dsound.pPlaybackPrimaryBuffer, (MA_WAVEFORMATEX*)pActualFormat, sizeof(rawdata), NULL); - if (FAILED(hr)) { - ma_device_uninit__dsound(pDevice); - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[DirectSound] Failed to retrieve the actual format of the playback device's primary buffer."); - return ma_result_from_HRESULT(hr); - } - - /* We now have enough information to start setting some output properties. */ - pDescriptorPlayback->format = ma_format_from_WAVEFORMATEX((MA_WAVEFORMATEX*)pActualFormat); - pDescriptorPlayback->channels = pActualFormat->nChannels; - pDescriptorPlayback->sampleRate = pActualFormat->nSamplesPerSec; - - /* Get the internal channel map based on the channel mask. */ - if (pActualFormat->wFormatTag == WAVE_FORMAT_EXTENSIBLE) { - ma_channel_mask_to_channel_map__win32(pActualFormat->dwChannelMask, pDescriptorPlayback->channels, pDescriptorPlayback->channelMap); - } else { - ma_channel_mask_to_channel_map__win32(wf.dwChannelMask, pDescriptorPlayback->channels, pDescriptorPlayback->channelMap); - } - - /* The size of the buffer must be a clean multiple of the period count. */ - periodSizeInFrames = ma_calculate_period_size_in_frames_from_descriptor__dsound(pDescriptorPlayback, pDescriptorPlayback->sampleRate, pConfig->performanceProfile); - periodCount = (pDescriptorPlayback->periodCount > 0) ? pDescriptorPlayback->periodCount : MA_DEFAULT_PERIODS; - - /* - Meaning of dwFlags (from MSDN): - - DSBCAPS_CTRLPOSITIONNOTIFY - The buffer has position notification capability. - - DSBCAPS_GLOBALFOCUS - With this flag set, an application using DirectSound can continue to play its buffers if the user switches focus to - another application, even if the new application uses DirectSound. - - DSBCAPS_GETCURRENTPOSITION2 - In the first version of DirectSound, the play cursor was significantly ahead of the actual playing sound on emulated - sound cards; it was directly behind the write cursor. Now, if the DSBCAPS_GETCURRENTPOSITION2 flag is specified, the - application can get a more accurate play cursor. - */ - MA_ZERO_OBJECT(&descDS); - descDS.dwSize = sizeof(descDS); - descDS.dwFlags = MA_DSBCAPS_CTRLPOSITIONNOTIFY | MA_DSBCAPS_GLOBALFOCUS | MA_DSBCAPS_GETCURRENTPOSITION2; - descDS.dwBufferBytes = periodSizeInFrames * periodCount * ma_get_bytes_per_frame(pDescriptorPlayback->format, pDescriptorPlayback->channels); - descDS.lpwfxFormat = (MA_WAVEFORMATEX*)pActualFormat; - hr = ma_IDirectSound_CreateSoundBuffer((ma_IDirectSound*)pDevice->dsound.pPlayback, &descDS, (ma_IDirectSoundBuffer**)&pDevice->dsound.pPlaybackBuffer, NULL); - if (FAILED(hr)) { - ma_device_uninit__dsound(pDevice); - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[DirectSound] IDirectSound_CreateSoundBuffer() failed for playback device's secondary buffer."); - return ma_result_from_HRESULT(hr); - } - - /* DirectSound should give us a buffer exactly the size we asked for. */ - pDescriptorPlayback->periodSizeInFrames = periodSizeInFrames; - pDescriptorPlayback->periodCount = periodCount; - } - - return MA_SUCCESS; -} - - -static ma_result ma_device_data_loop__dsound(ma_device* pDevice) -{ - ma_result result = MA_SUCCESS; - ma_uint32 bpfDeviceCapture = ma_get_bytes_per_frame(pDevice->capture.internalFormat, pDevice->capture.internalChannels); - ma_uint32 bpfDevicePlayback = ma_get_bytes_per_frame(pDevice->playback.internalFormat, pDevice->playback.internalChannels); - HRESULT hr; - DWORD lockOffsetInBytesCapture; - DWORD lockSizeInBytesCapture; - DWORD mappedSizeInBytesCapture; - DWORD mappedDeviceFramesProcessedCapture; - void* pMappedDeviceBufferCapture; - DWORD lockOffsetInBytesPlayback; - DWORD lockSizeInBytesPlayback; - DWORD mappedSizeInBytesPlayback; - void* pMappedDeviceBufferPlayback; - DWORD prevReadCursorInBytesCapture = 0; - DWORD prevPlayCursorInBytesPlayback = 0; - ma_bool32 physicalPlayCursorLoopFlagPlayback = 0; - DWORD virtualWriteCursorInBytesPlayback = 0; - ma_bool32 virtualWriteCursorLoopFlagPlayback = 0; - ma_bool32 isPlaybackDeviceStarted = MA_FALSE; - ma_uint32 framesWrittenToPlaybackDevice = 0; /* For knowing whether or not the playback device needs to be started. */ - ma_uint32 waitTimeInMilliseconds = 1; - DWORD playbackBufferStatus = 0; - - MA_ASSERT(pDevice != NULL); - - /* The first thing to do is start the capture device. The playback device is only started after the first period is written. */ - if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) { - hr = ma_IDirectSoundCaptureBuffer_Start((ma_IDirectSoundCaptureBuffer*)pDevice->dsound.pCaptureBuffer, MA_DSCBSTART_LOOPING); - if (FAILED(hr)) { - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[DirectSound] IDirectSoundCaptureBuffer_Start() failed."); - return ma_result_from_HRESULT(hr); - } - } - - while (ma_device_get_state(pDevice) == ma_device_state_started) { - switch (pDevice->type) - { - case ma_device_type_duplex: - { - DWORD physicalCaptureCursorInBytes; - DWORD physicalReadCursorInBytes; - hr = ma_IDirectSoundCaptureBuffer_GetCurrentPosition((ma_IDirectSoundCaptureBuffer*)pDevice->dsound.pCaptureBuffer, &physicalCaptureCursorInBytes, &physicalReadCursorInBytes); - if (FAILED(hr)) { - return ma_result_from_HRESULT(hr); - } - - /* If nothing is available we just sleep for a bit and return from this iteration. */ - if (physicalReadCursorInBytes == prevReadCursorInBytesCapture) { - ma_sleep(waitTimeInMilliseconds); - continue; /* Nothing is available in the capture buffer. */ - } - - /* - The current position has moved. We need to map all of the captured samples and write them to the playback device, making sure - we don't return until every frame has been copied over. - */ - if (prevReadCursorInBytesCapture < physicalReadCursorInBytes) { - /* The capture position has not looped. This is the simple case. */ - lockOffsetInBytesCapture = prevReadCursorInBytesCapture; - lockSizeInBytesCapture = (physicalReadCursorInBytes - prevReadCursorInBytesCapture); - } else { - /* - The capture position has looped. This is the more complex case. Map to the end of the buffer. If this does not return anything, - do it again from the start. - */ - if (prevReadCursorInBytesCapture < pDevice->capture.internalPeriodSizeInFrames*pDevice->capture.internalPeriods*bpfDeviceCapture) { - /* Lock up to the end of the buffer. */ - lockOffsetInBytesCapture = prevReadCursorInBytesCapture; - lockSizeInBytesCapture = (pDevice->capture.internalPeriodSizeInFrames*pDevice->capture.internalPeriods*bpfDeviceCapture) - prevReadCursorInBytesCapture; - } else { - /* Lock starting from the start of the buffer. */ - lockOffsetInBytesCapture = 0; - lockSizeInBytesCapture = physicalReadCursorInBytes; - } - } - - if (lockSizeInBytesCapture == 0) { - ma_sleep(waitTimeInMilliseconds); - continue; /* Nothing is available in the capture buffer. */ - } - - hr = ma_IDirectSoundCaptureBuffer_Lock((ma_IDirectSoundCaptureBuffer*)pDevice->dsound.pCaptureBuffer, lockOffsetInBytesCapture, lockSizeInBytesCapture, &pMappedDeviceBufferCapture, &mappedSizeInBytesCapture, NULL, NULL, 0); - if (FAILED(hr)) { - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[DirectSound] Failed to map buffer from capture device in preparation for writing to the device."); - return ma_result_from_HRESULT(hr); - } - - - /* At this point we have some input data that we need to output. We do not return until every mapped frame of the input data is written to the playback device. */ - mappedDeviceFramesProcessedCapture = 0; - - for (;;) { /* Keep writing to the playback device. */ - ma_uint8 inputFramesInClientFormat[MA_DATA_CONVERTER_STACK_BUFFER_SIZE]; - ma_uint32 inputFramesInClientFormatCap = sizeof(inputFramesInClientFormat) / ma_get_bytes_per_frame(pDevice->capture.format, pDevice->capture.channels); - ma_uint8 outputFramesInClientFormat[MA_DATA_CONVERTER_STACK_BUFFER_SIZE]; - ma_uint32 outputFramesInClientFormatCap = sizeof(outputFramesInClientFormat) / ma_get_bytes_per_frame(pDevice->playback.format, pDevice->playback.channels); - ma_uint32 outputFramesInClientFormatCount; - ma_uint32 outputFramesInClientFormatConsumed = 0; - ma_uint64 clientCapturedFramesToProcess = ma_min(inputFramesInClientFormatCap, outputFramesInClientFormatCap); - ma_uint64 deviceCapturedFramesToProcess = (mappedSizeInBytesCapture / bpfDeviceCapture) - mappedDeviceFramesProcessedCapture; - void* pRunningMappedDeviceBufferCapture = ma_offset_ptr(pMappedDeviceBufferCapture, mappedDeviceFramesProcessedCapture * bpfDeviceCapture); - - result = ma_data_converter_process_pcm_frames(&pDevice->capture.converter, pRunningMappedDeviceBufferCapture, &deviceCapturedFramesToProcess, inputFramesInClientFormat, &clientCapturedFramesToProcess); - if (result != MA_SUCCESS) { - break; - } - - outputFramesInClientFormatCount = (ma_uint32)clientCapturedFramesToProcess; - mappedDeviceFramesProcessedCapture += (ma_uint32)deviceCapturedFramesToProcess; - - ma_device__handle_data_callback(pDevice, outputFramesInClientFormat, inputFramesInClientFormat, (ma_uint32)clientCapturedFramesToProcess); - - /* At this point we have input and output data in client format. All we need to do now is convert it to the output device format. This may take a few passes. */ - for (;;) { - ma_uint32 framesWrittenThisIteration; - DWORD physicalPlayCursorInBytes; - DWORD physicalWriteCursorInBytes; - DWORD availableBytesPlayback; - DWORD silentPaddingInBytes = 0; /* <-- Must be initialized to 0. */ - - /* We need the physical play and write cursors. */ - if (FAILED(ma_IDirectSoundBuffer_GetCurrentPosition((ma_IDirectSoundBuffer*)pDevice->dsound.pPlaybackBuffer, &physicalPlayCursorInBytes, &physicalWriteCursorInBytes))) { - break; - } - - if (physicalPlayCursorInBytes < prevPlayCursorInBytesPlayback) { - physicalPlayCursorLoopFlagPlayback = !physicalPlayCursorLoopFlagPlayback; - } - prevPlayCursorInBytesPlayback = physicalPlayCursorInBytes; - - /* If there's any bytes available for writing we can do that now. The space between the virtual cursor position and play cursor. */ - if (physicalPlayCursorLoopFlagPlayback == virtualWriteCursorLoopFlagPlayback) { - /* Same loop iteration. The available bytes wraps all the way around from the virtual write cursor to the physical play cursor. */ - if (physicalPlayCursorInBytes <= virtualWriteCursorInBytesPlayback) { - availableBytesPlayback = (pDevice->playback.internalPeriodSizeInFrames*pDevice->playback.internalPeriods*bpfDevicePlayback) - virtualWriteCursorInBytesPlayback; - availableBytesPlayback += physicalPlayCursorInBytes; /* Wrap around. */ - } else { - /* This is an error. */ - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_WARNING, "[DirectSound] (Duplex/Playback): Play cursor has moved in front of the write cursor (same loop iteration). physicalPlayCursorInBytes=%ld, virtualWriteCursorInBytes=%ld.\n", physicalPlayCursorInBytes, virtualWriteCursorInBytesPlayback); - availableBytesPlayback = 0; - } - } else { - /* Different loop iterations. The available bytes only goes from the virtual write cursor to the physical play cursor. */ - if (physicalPlayCursorInBytes >= virtualWriteCursorInBytesPlayback) { - availableBytesPlayback = physicalPlayCursorInBytes - virtualWriteCursorInBytesPlayback; - } else { - /* This is an error. */ - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_WARNING, "[DirectSound] (Duplex/Playback): Write cursor has moved behind the play cursor (different loop iterations). physicalPlayCursorInBytes=%ld, virtualWriteCursorInBytes=%ld.\n", physicalPlayCursorInBytes, virtualWriteCursorInBytesPlayback); - availableBytesPlayback = 0; - } - } - - /* If there's no room available for writing we need to wait for more. */ - if (availableBytesPlayback == 0) { - /* If we haven't started the device yet, this will never get beyond 0. In this case we need to get the device started. */ - if (!isPlaybackDeviceStarted) { - hr = ma_IDirectSoundBuffer_Play((ma_IDirectSoundBuffer*)pDevice->dsound.pPlaybackBuffer, 0, 0, MA_DSBPLAY_LOOPING); - if (FAILED(hr)) { - ma_IDirectSoundCaptureBuffer_Stop((ma_IDirectSoundCaptureBuffer*)pDevice->dsound.pCaptureBuffer); - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[DirectSound] IDirectSoundBuffer_Play() failed."); - return ma_result_from_HRESULT(hr); - } - isPlaybackDeviceStarted = MA_TRUE; - } else { - ma_sleep(waitTimeInMilliseconds); - continue; - } - } - - - /* Getting here means there room available somewhere. We limit this to either the end of the buffer or the physical play cursor, whichever is closest. */ - lockOffsetInBytesPlayback = virtualWriteCursorInBytesPlayback; - if (physicalPlayCursorLoopFlagPlayback == virtualWriteCursorLoopFlagPlayback) { - /* Same loop iteration. Go up to the end of the buffer. */ - lockSizeInBytesPlayback = (pDevice->playback.internalPeriodSizeInFrames*pDevice->playback.internalPeriods*bpfDevicePlayback) - virtualWriteCursorInBytesPlayback; - } else { - /* Different loop iterations. Go up to the physical play cursor. */ - lockSizeInBytesPlayback = physicalPlayCursorInBytes - virtualWriteCursorInBytesPlayback; - } - - hr = ma_IDirectSoundBuffer_Lock((ma_IDirectSoundBuffer*)pDevice->dsound.pPlaybackBuffer, lockOffsetInBytesPlayback, lockSizeInBytesPlayback, &pMappedDeviceBufferPlayback, &mappedSizeInBytesPlayback, NULL, NULL, 0); - if (FAILED(hr)) { - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[DirectSound] Failed to map buffer from playback device in preparation for writing to the device."); - result = ma_result_from_HRESULT(hr); - break; - } - - /* - Experiment: If the playback buffer is being starved, pad it with some silence to get it back in sync. This will cause a glitch, but it may prevent - endless glitching due to it constantly running out of data. - */ - if (isPlaybackDeviceStarted) { - DWORD bytesQueuedForPlayback = (pDevice->playback.internalPeriodSizeInFrames*pDevice->playback.internalPeriods*bpfDevicePlayback) - availableBytesPlayback; - if (bytesQueuedForPlayback < (pDevice->playback.internalPeriodSizeInFrames*bpfDevicePlayback)) { - silentPaddingInBytes = (pDevice->playback.internalPeriodSizeInFrames*2*bpfDevicePlayback) - bytesQueuedForPlayback; - if (silentPaddingInBytes > lockSizeInBytesPlayback) { - silentPaddingInBytes = lockSizeInBytesPlayback; - } - - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_WARNING, "[DirectSound] (Duplex/Playback) Playback buffer starved. availableBytesPlayback=%ld, silentPaddingInBytes=%ld\n", availableBytesPlayback, silentPaddingInBytes); - } - } - - /* At this point we have a buffer for output. */ - if (silentPaddingInBytes > 0) { - MA_ZERO_MEMORY(pMappedDeviceBufferPlayback, silentPaddingInBytes); - framesWrittenThisIteration = silentPaddingInBytes/bpfDevicePlayback; - } else { - ma_uint64 convertedFrameCountIn = (outputFramesInClientFormatCount - outputFramesInClientFormatConsumed); - ma_uint64 convertedFrameCountOut = mappedSizeInBytesPlayback/bpfDevicePlayback; - void* pConvertedFramesIn = ma_offset_ptr(outputFramesInClientFormat, outputFramesInClientFormatConsumed * bpfDevicePlayback); - void* pConvertedFramesOut = pMappedDeviceBufferPlayback; - - result = ma_data_converter_process_pcm_frames(&pDevice->playback.converter, pConvertedFramesIn, &convertedFrameCountIn, pConvertedFramesOut, &convertedFrameCountOut); - if (result != MA_SUCCESS) { - break; - } - - outputFramesInClientFormatConsumed += (ma_uint32)convertedFrameCountOut; - framesWrittenThisIteration = (ma_uint32)convertedFrameCountOut; - } - - - hr = ma_IDirectSoundBuffer_Unlock((ma_IDirectSoundBuffer*)pDevice->dsound.pPlaybackBuffer, pMappedDeviceBufferPlayback, framesWrittenThisIteration*bpfDevicePlayback, NULL, 0); - if (FAILED(hr)) { - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[DirectSound] Failed to unlock internal buffer from playback device after writing to the device."); - result = ma_result_from_HRESULT(hr); - break; - } - - virtualWriteCursorInBytesPlayback += framesWrittenThisIteration*bpfDevicePlayback; - if ((virtualWriteCursorInBytesPlayback/bpfDevicePlayback) == pDevice->playback.internalPeriodSizeInFrames*pDevice->playback.internalPeriods) { - virtualWriteCursorInBytesPlayback = 0; - virtualWriteCursorLoopFlagPlayback = !virtualWriteCursorLoopFlagPlayback; - } - - /* - We may need to start the device. We want two full periods to be written before starting the playback device. Having an extra period adds - a bit of a buffer to prevent the playback buffer from getting starved. - */ - framesWrittenToPlaybackDevice += framesWrittenThisIteration; - if (!isPlaybackDeviceStarted && framesWrittenToPlaybackDevice >= (pDevice->playback.internalPeriodSizeInFrames*2)) { - hr = ma_IDirectSoundBuffer_Play((ma_IDirectSoundBuffer*)pDevice->dsound.pPlaybackBuffer, 0, 0, MA_DSBPLAY_LOOPING); - if (FAILED(hr)) { - ma_IDirectSoundCaptureBuffer_Stop((ma_IDirectSoundCaptureBuffer*)pDevice->dsound.pCaptureBuffer); - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[DirectSound] IDirectSoundBuffer_Play() failed."); - return ma_result_from_HRESULT(hr); - } - isPlaybackDeviceStarted = MA_TRUE; - } - - if (framesWrittenThisIteration < mappedSizeInBytesPlayback/bpfDevicePlayback) { - break; /* We're finished with the output data.*/ - } - } - - if (clientCapturedFramesToProcess == 0) { - break; /* We just consumed every input sample. */ - } - } - - - /* At this point we're done with the mapped portion of the capture buffer. */ - hr = ma_IDirectSoundCaptureBuffer_Unlock((ma_IDirectSoundCaptureBuffer*)pDevice->dsound.pCaptureBuffer, pMappedDeviceBufferCapture, mappedSizeInBytesCapture, NULL, 0); - if (FAILED(hr)) { - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[DirectSound] Failed to unlock internal buffer from capture device after reading from the device."); - return ma_result_from_HRESULT(hr); - } - prevReadCursorInBytesCapture = (lockOffsetInBytesCapture + mappedSizeInBytesCapture); - } break; - - - - case ma_device_type_capture: - { - DWORD physicalCaptureCursorInBytes; - DWORD physicalReadCursorInBytes; - hr = ma_IDirectSoundCaptureBuffer_GetCurrentPosition((ma_IDirectSoundCaptureBuffer*)pDevice->dsound.pCaptureBuffer, &physicalCaptureCursorInBytes, &physicalReadCursorInBytes); - if (FAILED(hr)) { - return MA_ERROR; - } - - /* If the previous capture position is the same as the current position we need to wait a bit longer. */ - if (prevReadCursorInBytesCapture == physicalReadCursorInBytes) { - ma_sleep(waitTimeInMilliseconds); - continue; - } - - /* Getting here means we have capture data available. */ - if (prevReadCursorInBytesCapture < physicalReadCursorInBytes) { - /* The capture position has not looped. This is the simple case. */ - lockOffsetInBytesCapture = prevReadCursorInBytesCapture; - lockSizeInBytesCapture = (physicalReadCursorInBytes - prevReadCursorInBytesCapture); - } else { - /* - The capture position has looped. This is the more complex case. Map to the end of the buffer. If this does not return anything, - do it again from the start. - */ - if (prevReadCursorInBytesCapture < pDevice->capture.internalPeriodSizeInFrames*pDevice->capture.internalPeriods*bpfDeviceCapture) { - /* Lock up to the end of the buffer. */ - lockOffsetInBytesCapture = prevReadCursorInBytesCapture; - lockSizeInBytesCapture = (pDevice->capture.internalPeriodSizeInFrames*pDevice->capture.internalPeriods*bpfDeviceCapture) - prevReadCursorInBytesCapture; - } else { - /* Lock starting from the start of the buffer. */ - lockOffsetInBytesCapture = 0; - lockSizeInBytesCapture = physicalReadCursorInBytes; - } - } - - if (lockSizeInBytesCapture < pDevice->capture.internalPeriodSizeInFrames) { - ma_sleep(waitTimeInMilliseconds); - continue; /* Nothing is available in the capture buffer. */ - } - - hr = ma_IDirectSoundCaptureBuffer_Lock((ma_IDirectSoundCaptureBuffer*)pDevice->dsound.pCaptureBuffer, lockOffsetInBytesCapture, lockSizeInBytesCapture, &pMappedDeviceBufferCapture, &mappedSizeInBytesCapture, NULL, NULL, 0); - if (FAILED(hr)) { - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[DirectSound] Failed to map buffer from capture device in preparation for writing to the device."); - result = ma_result_from_HRESULT(hr); - } - - if (lockSizeInBytesCapture != mappedSizeInBytesCapture) { - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "[DirectSound] (Capture) lockSizeInBytesCapture=%ld != mappedSizeInBytesCapture=%ld\n", lockSizeInBytesCapture, mappedSizeInBytesCapture); - } - - ma_device__send_frames_to_client(pDevice, mappedSizeInBytesCapture/bpfDeviceCapture, pMappedDeviceBufferCapture); - - hr = ma_IDirectSoundCaptureBuffer_Unlock((ma_IDirectSoundCaptureBuffer*)pDevice->dsound.pCaptureBuffer, pMappedDeviceBufferCapture, mappedSizeInBytesCapture, NULL, 0); - if (FAILED(hr)) { - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[DirectSound] Failed to unlock internal buffer from capture device after reading from the device."); - return ma_result_from_HRESULT(hr); - } - prevReadCursorInBytesCapture = lockOffsetInBytesCapture + mappedSizeInBytesCapture; - - if (prevReadCursorInBytesCapture == (pDevice->capture.internalPeriodSizeInFrames*pDevice->capture.internalPeriods*bpfDeviceCapture)) { - prevReadCursorInBytesCapture = 0; - } - } break; - - - - case ma_device_type_playback: - { - DWORD availableBytesPlayback; - DWORD physicalPlayCursorInBytes; - DWORD physicalWriteCursorInBytes; - hr = ma_IDirectSoundBuffer_GetCurrentPosition((ma_IDirectSoundBuffer*)pDevice->dsound.pPlaybackBuffer, &physicalPlayCursorInBytes, &physicalWriteCursorInBytes); - if (FAILED(hr)) { - break; - } - - hr = ma_IDirectSoundBuffer_GetStatus((ma_IDirectSoundBuffer*)pDevice->dsound.pPlaybackBuffer, &playbackBufferStatus); - if (SUCCEEDED(hr) && (playbackBufferStatus & MA_DSBSTATUS_PLAYING) == 0 && isPlaybackDeviceStarted) { - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, "[DirectSound] Attempting to resume audio due to state: %d.", (int)playbackBufferStatus); - hr = ma_IDirectSoundBuffer_Play((ma_IDirectSoundBuffer*)pDevice->dsound.pPlaybackBuffer, 0, 0, MA_DSBPLAY_LOOPING); - if (FAILED(hr)) { - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[DirectSound] IDirectSoundBuffer_Play() failed after attempting to resume from state %d.", (int)playbackBufferStatus); - return ma_result_from_HRESULT(hr); - } - - isPlaybackDeviceStarted = MA_TRUE; - ma_sleep(waitTimeInMilliseconds); - continue; - } - - if (physicalPlayCursorInBytes < prevPlayCursorInBytesPlayback) { - physicalPlayCursorLoopFlagPlayback = !physicalPlayCursorLoopFlagPlayback; - } - prevPlayCursorInBytesPlayback = physicalPlayCursorInBytes; - - /* If there's any bytes available for writing we can do that now. The space between the virtual cursor position and play cursor. */ - if (physicalPlayCursorLoopFlagPlayback == virtualWriteCursorLoopFlagPlayback) { - /* Same loop iteration. The available bytes wraps all the way around from the virtual write cursor to the physical play cursor. */ - if (physicalPlayCursorInBytes <= virtualWriteCursorInBytesPlayback) { - availableBytesPlayback = (pDevice->playback.internalPeriodSizeInFrames*pDevice->playback.internalPeriods*bpfDevicePlayback) - virtualWriteCursorInBytesPlayback; - availableBytesPlayback += physicalPlayCursorInBytes; /* Wrap around. */ - } else { - /* This is an error. */ - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_WARNING, "[DirectSound] (Playback): Play cursor has moved in front of the write cursor (same loop iterations). physicalPlayCursorInBytes=%ld, virtualWriteCursorInBytes=%ld.\n", physicalPlayCursorInBytes, virtualWriteCursorInBytesPlayback); - availableBytesPlayback = 0; - } - } else { - /* Different loop iterations. The available bytes only goes from the virtual write cursor to the physical play cursor. */ - if (physicalPlayCursorInBytes >= virtualWriteCursorInBytesPlayback) { - availableBytesPlayback = physicalPlayCursorInBytes - virtualWriteCursorInBytesPlayback; - } else { - /* This is an error. */ - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_WARNING, "[DirectSound] (Playback): Write cursor has moved behind the play cursor (different loop iterations). physicalPlayCursorInBytes=%ld, virtualWriteCursorInBytes=%ld.\n", physicalPlayCursorInBytes, virtualWriteCursorInBytesPlayback); - availableBytesPlayback = 0; - } - } - - /* If there's no room available for writing we need to wait for more. */ - if (availableBytesPlayback < pDevice->playback.internalPeriodSizeInFrames) { - /* If we haven't started the device yet, this will never get beyond 0. In this case we need to get the device started. */ - if (availableBytesPlayback == 0 && !isPlaybackDeviceStarted) { - hr = ma_IDirectSoundBuffer_Play((ma_IDirectSoundBuffer*)pDevice->dsound.pPlaybackBuffer, 0, 0, MA_DSBPLAY_LOOPING); - if (FAILED(hr)) { - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[DirectSound] IDirectSoundBuffer_Play() failed."); - return ma_result_from_HRESULT(hr); - } - isPlaybackDeviceStarted = MA_TRUE; - } else { - ma_sleep(waitTimeInMilliseconds); - continue; - } - } - - /* Getting here means there room available somewhere. We limit this to either the end of the buffer or the physical play cursor, whichever is closest. */ - lockOffsetInBytesPlayback = virtualWriteCursorInBytesPlayback; - if (physicalPlayCursorLoopFlagPlayback == virtualWriteCursorLoopFlagPlayback) { - /* Same loop iteration. Go up to the end of the buffer. */ - lockSizeInBytesPlayback = (pDevice->playback.internalPeriodSizeInFrames*pDevice->playback.internalPeriods*bpfDevicePlayback) - virtualWriteCursorInBytesPlayback; - } else { - /* Different loop iterations. Go up to the physical play cursor. */ - lockSizeInBytesPlayback = physicalPlayCursorInBytes - virtualWriteCursorInBytesPlayback; - } - - hr = ma_IDirectSoundBuffer_Lock((ma_IDirectSoundBuffer*)pDevice->dsound.pPlaybackBuffer, lockOffsetInBytesPlayback, lockSizeInBytesPlayback, &pMappedDeviceBufferPlayback, &mappedSizeInBytesPlayback, NULL, NULL, 0); - if (FAILED(hr)) { - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[DirectSound] Failed to map buffer from playback device in preparation for writing to the device."); - result = ma_result_from_HRESULT(hr); - break; - } - - /* At this point we have a buffer for output. */ - ma_device__read_frames_from_client(pDevice, (mappedSizeInBytesPlayback/bpfDevicePlayback), pMappedDeviceBufferPlayback); - - hr = ma_IDirectSoundBuffer_Unlock((ma_IDirectSoundBuffer*)pDevice->dsound.pPlaybackBuffer, pMappedDeviceBufferPlayback, mappedSizeInBytesPlayback, NULL, 0); - if (FAILED(hr)) { - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[DirectSound] Failed to unlock internal buffer from playback device after writing to the device."); - result = ma_result_from_HRESULT(hr); - break; - } - - virtualWriteCursorInBytesPlayback += mappedSizeInBytesPlayback; - if (virtualWriteCursorInBytesPlayback == pDevice->playback.internalPeriodSizeInFrames*pDevice->playback.internalPeriods*bpfDevicePlayback) { - virtualWriteCursorInBytesPlayback = 0; - virtualWriteCursorLoopFlagPlayback = !virtualWriteCursorLoopFlagPlayback; - } - - /* - We may need to start the device. We want two full periods to be written before starting the playback device. Having an extra period adds - a bit of a buffer to prevent the playback buffer from getting starved. - */ - framesWrittenToPlaybackDevice += mappedSizeInBytesPlayback/bpfDevicePlayback; - if (!isPlaybackDeviceStarted && framesWrittenToPlaybackDevice >= pDevice->playback.internalPeriodSizeInFrames) { - hr = ma_IDirectSoundBuffer_Play((ma_IDirectSoundBuffer*)pDevice->dsound.pPlaybackBuffer, 0, 0, MA_DSBPLAY_LOOPING); - if (FAILED(hr)) { - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[DirectSound] IDirectSoundBuffer_Play() failed."); - return ma_result_from_HRESULT(hr); - } - isPlaybackDeviceStarted = MA_TRUE; - } - } break; - - - default: return MA_INVALID_ARGS; /* Invalid device type. */ - } - - if (result != MA_SUCCESS) { - return result; - } - } - - /* Getting here means the device is being stopped. */ - if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) { - hr = ma_IDirectSoundCaptureBuffer_Stop((ma_IDirectSoundCaptureBuffer*)pDevice->dsound.pCaptureBuffer); - if (FAILED(hr)) { - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[DirectSound] IDirectSoundCaptureBuffer_Stop() failed."); - return ma_result_from_HRESULT(hr); - } - } - - if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) { - /* The playback device should be drained before stopping. All we do is wait until the available bytes is equal to the size of the buffer. */ - if (isPlaybackDeviceStarted) { - for (;;) { - DWORD availableBytesPlayback = 0; - DWORD physicalPlayCursorInBytes; - DWORD physicalWriteCursorInBytes; - hr = ma_IDirectSoundBuffer_GetCurrentPosition((ma_IDirectSoundBuffer*)pDevice->dsound.pPlaybackBuffer, &physicalPlayCursorInBytes, &physicalWriteCursorInBytes); - if (FAILED(hr)) { - break; - } - - if (physicalPlayCursorInBytes < prevPlayCursorInBytesPlayback) { - physicalPlayCursorLoopFlagPlayback = !physicalPlayCursorLoopFlagPlayback; - } - prevPlayCursorInBytesPlayback = physicalPlayCursorInBytes; - - if (physicalPlayCursorLoopFlagPlayback == virtualWriteCursorLoopFlagPlayback) { - /* Same loop iteration. The available bytes wraps all the way around from the virtual write cursor to the physical play cursor. */ - if (physicalPlayCursorInBytes <= virtualWriteCursorInBytesPlayback) { - availableBytesPlayback = (pDevice->playback.internalPeriodSizeInFrames*pDevice->playback.internalPeriods*bpfDevicePlayback) - virtualWriteCursorInBytesPlayback; - availableBytesPlayback += physicalPlayCursorInBytes; /* Wrap around. */ - } else { - break; - } - } else { - /* Different loop iterations. The available bytes only goes from the virtual write cursor to the physical play cursor. */ - if (physicalPlayCursorInBytes >= virtualWriteCursorInBytesPlayback) { - availableBytesPlayback = physicalPlayCursorInBytes - virtualWriteCursorInBytesPlayback; - } else { - break; - } - } - - if (availableBytesPlayback >= (pDevice->playback.internalPeriodSizeInFrames*pDevice->playback.internalPeriods*bpfDevicePlayback)) { - break; - } - - ma_sleep(waitTimeInMilliseconds); - } - } - - hr = ma_IDirectSoundBuffer_Stop((ma_IDirectSoundBuffer*)pDevice->dsound.pPlaybackBuffer); - if (FAILED(hr)) { - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[DirectSound] IDirectSoundBuffer_Stop() failed."); - return ma_result_from_HRESULT(hr); - } - - ma_IDirectSoundBuffer_SetCurrentPosition((ma_IDirectSoundBuffer*)pDevice->dsound.pPlaybackBuffer, 0); - } - - return MA_SUCCESS; -} - -static ma_result ma_context_uninit__dsound(ma_context* pContext) -{ - MA_ASSERT(pContext != NULL); - MA_ASSERT(pContext->backend == ma_backend_dsound); - - ma_dlclose(ma_context_get_log(pContext), pContext->dsound.hDSoundDLL); - - return MA_SUCCESS; -} - -static ma_result ma_context_init__dsound(ma_context* pContext, const ma_context_config* pConfig, ma_backend_callbacks* pCallbacks) -{ - MA_ASSERT(pContext != NULL); - - (void)pConfig; - - pContext->dsound.hDSoundDLL = ma_dlopen(ma_context_get_log(pContext), "dsound.dll"); - if (pContext->dsound.hDSoundDLL == NULL) { - return MA_API_NOT_FOUND; - } - - pContext->dsound.DirectSoundCreate = ma_dlsym(ma_context_get_log(pContext), pContext->dsound.hDSoundDLL, "DirectSoundCreate"); - pContext->dsound.DirectSoundEnumerateA = ma_dlsym(ma_context_get_log(pContext), pContext->dsound.hDSoundDLL, "DirectSoundEnumerateA"); - pContext->dsound.DirectSoundCaptureCreate = ma_dlsym(ma_context_get_log(pContext), pContext->dsound.hDSoundDLL, "DirectSoundCaptureCreate"); - pContext->dsound.DirectSoundCaptureEnumerateA = ma_dlsym(ma_context_get_log(pContext), pContext->dsound.hDSoundDLL, "DirectSoundCaptureEnumerateA"); - - /* - We need to support all functions or nothing. DirectSound with Windows 95 seems to not work too - well in my testing. For example, it's missing DirectSoundCaptureEnumerateA(). This is a convenient - place to just disable the DirectSound backend for Windows 95. - */ - if (pContext->dsound.DirectSoundCreate == NULL || - pContext->dsound.DirectSoundEnumerateA == NULL || - pContext->dsound.DirectSoundCaptureCreate == NULL || - pContext->dsound.DirectSoundCaptureEnumerateA == NULL) { - return MA_API_NOT_FOUND; - } - - pContext->dsound.hWnd = pConfig->dsound.hWnd; - - pCallbacks->onContextInit = ma_context_init__dsound; - pCallbacks->onContextUninit = ma_context_uninit__dsound; - pCallbacks->onContextEnumerateDevices = ma_context_enumerate_devices__dsound; - pCallbacks->onContextGetDeviceInfo = ma_context_get_device_info__dsound; - pCallbacks->onDeviceInit = ma_device_init__dsound; - pCallbacks->onDeviceUninit = ma_device_uninit__dsound; - pCallbacks->onDeviceStart = NULL; /* Not used. Started in onDeviceDataLoop. */ - pCallbacks->onDeviceStop = NULL; /* Not used. Stopped in onDeviceDataLoop. */ - pCallbacks->onDeviceRead = NULL; /* Not used. Data is read directly in onDeviceDataLoop. */ - pCallbacks->onDeviceWrite = NULL; /* Not used. Data is written directly in onDeviceDataLoop. */ - pCallbacks->onDeviceDataLoop = ma_device_data_loop__dsound; - - return MA_SUCCESS; -} -#endif - - - -/****************************************************************************** - -WinMM Backend - -******************************************************************************/ -#ifdef MA_HAS_WINMM - -/* -Some build configurations will exclude the WinMM API. An example is when WIN32_LEAN_AND_MEAN -is defined. We need to define the types and functions we need manually. -*/ -#define MA_MMSYSERR_NOERROR 0 -#define MA_MMSYSERR_ERROR 1 -#define MA_MMSYSERR_BADDEVICEID 2 -#define MA_MMSYSERR_INVALHANDLE 5 -#define MA_MMSYSERR_NOMEM 7 -#define MA_MMSYSERR_INVALFLAG 10 -#define MA_MMSYSERR_INVALPARAM 11 -#define MA_MMSYSERR_HANDLEBUSY 12 - -#define MA_CALLBACK_EVENT 0x00050000 -#define MA_WAVE_ALLOWSYNC 0x0002 - -#define MA_WHDR_DONE 0x00000001 -#define MA_WHDR_PREPARED 0x00000002 -#define MA_WHDR_BEGINLOOP 0x00000004 -#define MA_WHDR_ENDLOOP 0x00000008 -#define MA_WHDR_INQUEUE 0x00000010 - -#define MA_MAXPNAMELEN 32 - -typedef void* MA_HWAVEIN; -typedef void* MA_HWAVEOUT; -typedef UINT MA_MMRESULT; -typedef UINT MA_MMVERSION; - -typedef struct -{ - WORD wMid; - WORD wPid; - MA_MMVERSION vDriverVersion; - CHAR szPname[MA_MAXPNAMELEN]; - DWORD dwFormats; - WORD wChannels; - WORD wReserved1; -} MA_WAVEINCAPSA; - -typedef struct -{ - WORD wMid; - WORD wPid; - MA_MMVERSION vDriverVersion; - CHAR szPname[MA_MAXPNAMELEN]; - DWORD dwFormats; - WORD wChannels; - WORD wReserved1; - DWORD dwSupport; -} MA_WAVEOUTCAPSA; - -typedef struct tagWAVEHDR -{ - char* lpData; - DWORD dwBufferLength; - DWORD dwBytesRecorded; - DWORD_PTR dwUser; - DWORD dwFlags; - DWORD dwLoops; - struct tagWAVEHDR* lpNext; - DWORD_PTR reserved; -} MA_WAVEHDR; - -typedef struct -{ - WORD wMid; - WORD wPid; - MA_MMVERSION vDriverVersion; - CHAR szPname[MA_MAXPNAMELEN]; - DWORD dwFormats; - WORD wChannels; - WORD wReserved1; - DWORD dwSupport; - GUID ManufacturerGuid; - GUID ProductGuid; - GUID NameGuid; -} MA_WAVEOUTCAPS2A; - -typedef struct -{ - WORD wMid; - WORD wPid; - MA_MMVERSION vDriverVersion; - CHAR szPname[MA_MAXPNAMELEN]; - DWORD dwFormats; - WORD wChannels; - WORD wReserved1; - GUID ManufacturerGuid; - GUID ProductGuid; - GUID NameGuid; -} MA_WAVEINCAPS2A; - -typedef UINT (WINAPI * MA_PFN_waveOutGetNumDevs)(void); -typedef MA_MMRESULT (WINAPI * MA_PFN_waveOutGetDevCapsA)(ma_uintptr uDeviceID, MA_WAVEOUTCAPSA* pwoc, UINT cbwoc); -typedef MA_MMRESULT (WINAPI * MA_PFN_waveOutOpen)(MA_HWAVEOUT* phwo, UINT uDeviceID, const MA_WAVEFORMATEX* pwfx, DWORD_PTR dwCallback, DWORD_PTR dwInstance, DWORD fdwOpen); -typedef MA_MMRESULT (WINAPI * MA_PFN_waveOutClose)(MA_HWAVEOUT hwo); -typedef MA_MMRESULT (WINAPI * MA_PFN_waveOutPrepareHeader)(MA_HWAVEOUT hwo, MA_WAVEHDR* pwh, UINT cbwh); -typedef MA_MMRESULT (WINAPI * MA_PFN_waveOutUnprepareHeader)(MA_HWAVEOUT hwo, MA_WAVEHDR* pwh, UINT cbwh); -typedef MA_MMRESULT (WINAPI * MA_PFN_waveOutWrite)(MA_HWAVEOUT hwo, MA_WAVEHDR* pwh, UINT cbwh); -typedef MA_MMRESULT (WINAPI * MA_PFN_waveOutReset)(MA_HWAVEOUT hwo); -typedef UINT (WINAPI * MA_PFN_waveInGetNumDevs)(void); -typedef MA_MMRESULT (WINAPI * MA_PFN_waveInGetDevCapsA)(ma_uintptr uDeviceID, MA_WAVEINCAPSA* pwic, UINT cbwic); -typedef MA_MMRESULT (WINAPI * MA_PFN_waveInOpen)(MA_HWAVEIN* phwi, UINT uDeviceID, const MA_WAVEFORMATEX* pwfx, DWORD_PTR dwCallback, DWORD_PTR dwInstance, DWORD fdwOpen); -typedef MA_MMRESULT (WINAPI * MA_PFN_waveInClose)(MA_HWAVEIN hwi); -typedef MA_MMRESULT (WINAPI * MA_PFN_waveInPrepareHeader)(MA_HWAVEIN hwi, MA_WAVEHDR* pwh, UINT cbwh); -typedef MA_MMRESULT (WINAPI * MA_PFN_waveInUnprepareHeader)(MA_HWAVEIN hwi, MA_WAVEHDR* pwh, UINT cbwh); -typedef MA_MMRESULT (WINAPI * MA_PFN_waveInAddBuffer)(MA_HWAVEIN hwi, MA_WAVEHDR* pwh, UINT cbwh); -typedef MA_MMRESULT (WINAPI * MA_PFN_waveInStart)(MA_HWAVEIN hwi); -typedef MA_MMRESULT (WINAPI * MA_PFN_waveInReset)(MA_HWAVEIN hwi); - -static ma_result ma_result_from_MMRESULT(MA_MMRESULT resultMM) -{ - switch (resultMM) - { - case MA_MMSYSERR_NOERROR: return MA_SUCCESS; - case MA_MMSYSERR_BADDEVICEID: return MA_INVALID_ARGS; - case MA_MMSYSERR_INVALHANDLE: return MA_INVALID_ARGS; - case MA_MMSYSERR_NOMEM: return MA_OUT_OF_MEMORY; - case MA_MMSYSERR_INVALFLAG: return MA_INVALID_ARGS; - case MA_MMSYSERR_INVALPARAM: return MA_INVALID_ARGS; - case MA_MMSYSERR_HANDLEBUSY: return MA_BUSY; - case MA_MMSYSERR_ERROR: return MA_ERROR; - default: return MA_ERROR; - } -} - -static char* ma_find_last_character(char* str, char ch) -{ - char* last; - - if (str == NULL) { - return NULL; - } - - last = NULL; - while (*str != '\0') { - if (*str == ch) { - last = str; - } - - str += 1; - } - - return last; -} - -static ma_uint32 ma_get_period_size_in_bytes(ma_uint32 periodSizeInFrames, ma_format format, ma_uint32 channels) -{ - return periodSizeInFrames * ma_get_bytes_per_frame(format, channels); -} - - -/* -Our own "WAVECAPS" structure that contains generic information shared between WAVEOUTCAPS2 and WAVEINCAPS2 so -we can do things generically and typesafely. Names are being kept the same for consistency. -*/ -typedef struct -{ - CHAR szPname[MA_MAXPNAMELEN]; - DWORD dwFormats; - WORD wChannels; - GUID NameGuid; -} MA_WAVECAPSA; - -static ma_result ma_get_best_info_from_formats_flags__winmm(DWORD dwFormats, WORD channels, WORD* pBitsPerSample, DWORD* pSampleRate) -{ - WORD bitsPerSample = 0; - DWORD sampleRate = 0; - - if (pBitsPerSample) { - *pBitsPerSample = 0; - } - if (pSampleRate) { - *pSampleRate = 0; - } - - if (channels == 1) { - bitsPerSample = 16; - if ((dwFormats & WAVE_FORMAT_48M16) != 0) { - sampleRate = 48000; - } else if ((dwFormats & WAVE_FORMAT_44M16) != 0) { - sampleRate = 44100; - } else if ((dwFormats & WAVE_FORMAT_2M16) != 0) { - sampleRate = 22050; - } else if ((dwFormats & WAVE_FORMAT_1M16) != 0) { - sampleRate = 11025; - } else if ((dwFormats & WAVE_FORMAT_96M16) != 0) { - sampleRate = 96000; - } else { - bitsPerSample = 8; - if ((dwFormats & WAVE_FORMAT_48M08) != 0) { - sampleRate = 48000; - } else if ((dwFormats & WAVE_FORMAT_44M08) != 0) { - sampleRate = 44100; - } else if ((dwFormats & WAVE_FORMAT_2M08) != 0) { - sampleRate = 22050; - } else if ((dwFormats & WAVE_FORMAT_1M08) != 0) { - sampleRate = 11025; - } else if ((dwFormats & WAVE_FORMAT_96M08) != 0) { - sampleRate = 96000; - } else { - return MA_FORMAT_NOT_SUPPORTED; - } - } - } else { - bitsPerSample = 16; - if ((dwFormats & WAVE_FORMAT_48S16) != 0) { - sampleRate = 48000; - } else if ((dwFormats & WAVE_FORMAT_44S16) != 0) { - sampleRate = 44100; - } else if ((dwFormats & WAVE_FORMAT_2S16) != 0) { - sampleRate = 22050; - } else if ((dwFormats & WAVE_FORMAT_1S16) != 0) { - sampleRate = 11025; - } else if ((dwFormats & WAVE_FORMAT_96S16) != 0) { - sampleRate = 96000; - } else { - bitsPerSample = 8; - if ((dwFormats & WAVE_FORMAT_48S08) != 0) { - sampleRate = 48000; - } else if ((dwFormats & WAVE_FORMAT_44S08) != 0) { - sampleRate = 44100; - } else if ((dwFormats & WAVE_FORMAT_2S08) != 0) { - sampleRate = 22050; - } else if ((dwFormats & WAVE_FORMAT_1S08) != 0) { - sampleRate = 11025; - } else if ((dwFormats & WAVE_FORMAT_96S08) != 0) { - sampleRate = 96000; - } else { - return MA_FORMAT_NOT_SUPPORTED; - } - } - } - - if (pBitsPerSample) { - *pBitsPerSample = bitsPerSample; - } - if (pSampleRate) { - *pSampleRate = sampleRate; - } - - return MA_SUCCESS; -} - -static ma_result ma_formats_flags_to_WAVEFORMATEX__winmm(DWORD dwFormats, WORD channels, MA_WAVEFORMATEX* pWF) -{ - ma_result result; - - MA_ASSERT(pWF != NULL); - - MA_ZERO_OBJECT(pWF); - pWF->cbSize = sizeof(*pWF); - pWF->wFormatTag = WAVE_FORMAT_PCM; - pWF->nChannels = (WORD)channels; - if (pWF->nChannels > 2) { - pWF->nChannels = 2; - } - - result = ma_get_best_info_from_formats_flags__winmm(dwFormats, channels, &pWF->wBitsPerSample, &pWF->nSamplesPerSec); - if (result != MA_SUCCESS) { - return result; - } - - pWF->nBlockAlign = (WORD)(pWF->nChannels * pWF->wBitsPerSample / 8); - pWF->nAvgBytesPerSec = pWF->nBlockAlign * pWF->nSamplesPerSec; - - return MA_SUCCESS; -} - -static ma_result ma_context_get_device_info_from_WAVECAPS(ma_context* pContext, MA_WAVECAPSA* pCaps, ma_device_info* pDeviceInfo) -{ - WORD bitsPerSample; - DWORD sampleRate; - ma_result result; - - MA_ASSERT(pContext != NULL); - MA_ASSERT(pCaps != NULL); - MA_ASSERT(pDeviceInfo != NULL); - - /* - Name / Description - - Unfortunately the name specified in WAVE(OUT/IN)CAPS2 is limited to 31 characters. This results in an unprofessional looking - situation where the names of the devices are truncated. To help work around this, we need to look at the name GUID and try - looking in the registry for the full name. If we can't find it there, we need to just fall back to the default name. - */ - - /* Set the default to begin with. */ - ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), pCaps->szPname, (size_t)-1); - - /* - Now try the registry. There's a few things to consider here: - - The name GUID can be null, in which we case we just need to stick to the original 31 characters. - - If the name GUID is not present in the registry we'll also need to stick to the original 31 characters. - - I like consistency, so I want the returned device names to be consistent with those returned by WASAPI and DirectSound. The - problem, however is that WASAPI and DirectSound use " ()" format (such as "Speakers (High Definition Audio)"), - but WinMM does not specify the component name. From my admittedly limited testing, I've notice the component name seems to - usually fit within the 31 characters of the fixed sized buffer, so what I'm going to do is parse that string for the component - name, and then concatenate the name from the registry. - */ - if (!ma_is_guid_null(&pCaps->NameGuid)) { - WCHAR guidStrW[256]; - if (((MA_PFN_StringFromGUID2)pContext->win32.StringFromGUID2)(&pCaps->NameGuid, guidStrW, ma_countof(guidStrW)) > 0) { - char guidStr[256]; - char keyStr[1024]; - HKEY hKey; - - WideCharToMultiByte(CP_UTF8, 0, guidStrW, -1, guidStr, sizeof(guidStr), 0, FALSE); - - ma_strcpy_s(keyStr, sizeof(keyStr), "SYSTEM\\CurrentControlSet\\Control\\MediaCategories\\"); - ma_strcat_s(keyStr, sizeof(keyStr), guidStr); - - if (((MA_PFN_RegOpenKeyExA)pContext->win32.RegOpenKeyExA)(HKEY_LOCAL_MACHINE, keyStr, 0, KEY_READ, &hKey) == ERROR_SUCCESS) { - BYTE nameFromReg[512]; - DWORD nameFromRegSize = sizeof(nameFromReg); - LONG resultWin32 = ((MA_PFN_RegQueryValueExA)pContext->win32.RegQueryValueExA)(hKey, "Name", 0, NULL, (BYTE*)nameFromReg, (DWORD*)&nameFromRegSize); - ((MA_PFN_RegCloseKey)pContext->win32.RegCloseKey)(hKey); - - if (resultWin32 == ERROR_SUCCESS) { - /* We have the value from the registry, so now we need to construct the name string. */ - char name[1024]; - if (ma_strcpy_s(name, sizeof(name), pDeviceInfo->name) == 0) { - char* nameBeg = ma_find_last_character(name, '('); - if (nameBeg != NULL) { - size_t leadingLen = (nameBeg - name); - ma_strncpy_s(nameBeg + 1, sizeof(name) - leadingLen, (const char*)nameFromReg, (size_t)-1); - - /* The closing ")", if it can fit. */ - if (leadingLen + nameFromRegSize < sizeof(name)-1) { - ma_strcat_s(name, sizeof(name), ")"); - } - - ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), name, (size_t)-1); - } - } - } - } - } - } - - - result = ma_get_best_info_from_formats_flags__winmm(pCaps->dwFormats, pCaps->wChannels, &bitsPerSample, &sampleRate); - if (result != MA_SUCCESS) { - return result; - } - - if (bitsPerSample == 8) { - pDeviceInfo->nativeDataFormats[0].format = ma_format_u8; - } else if (bitsPerSample == 16) { - pDeviceInfo->nativeDataFormats[0].format = ma_format_s16; - } else if (bitsPerSample == 24) { - pDeviceInfo->nativeDataFormats[0].format = ma_format_s24; - } else if (bitsPerSample == 32) { - pDeviceInfo->nativeDataFormats[0].format = ma_format_s32; - } else { - return MA_FORMAT_NOT_SUPPORTED; - } - pDeviceInfo->nativeDataFormats[0].channels = pCaps->wChannels; - pDeviceInfo->nativeDataFormats[0].sampleRate = sampleRate; - pDeviceInfo->nativeDataFormats[0].flags = 0; - pDeviceInfo->nativeDataFormatCount = 1; - - return MA_SUCCESS; -} - -static ma_result ma_context_get_device_info_from_WAVEOUTCAPS2(ma_context* pContext, MA_WAVEOUTCAPS2A* pCaps, ma_device_info* pDeviceInfo) -{ - MA_WAVECAPSA caps; - - MA_ASSERT(pContext != NULL); - MA_ASSERT(pCaps != NULL); - MA_ASSERT(pDeviceInfo != NULL); - - MA_COPY_MEMORY(caps.szPname, pCaps->szPname, sizeof(caps.szPname)); - caps.dwFormats = pCaps->dwFormats; - caps.wChannels = pCaps->wChannels; - caps.NameGuid = pCaps->NameGuid; - return ma_context_get_device_info_from_WAVECAPS(pContext, &caps, pDeviceInfo); -} - -static ma_result ma_context_get_device_info_from_WAVEINCAPS2(ma_context* pContext, MA_WAVEINCAPS2A* pCaps, ma_device_info* pDeviceInfo) -{ - MA_WAVECAPSA caps; - - MA_ASSERT(pContext != NULL); - MA_ASSERT(pCaps != NULL); - MA_ASSERT(pDeviceInfo != NULL); - - MA_COPY_MEMORY(caps.szPname, pCaps->szPname, sizeof(caps.szPname)); - caps.dwFormats = pCaps->dwFormats; - caps.wChannels = pCaps->wChannels; - caps.NameGuid = pCaps->NameGuid; - return ma_context_get_device_info_from_WAVECAPS(pContext, &caps, pDeviceInfo); -} - - -static ma_result ma_context_enumerate_devices__winmm(ma_context* pContext, ma_enum_devices_callback_proc callback, void* pUserData) -{ - UINT playbackDeviceCount; - UINT captureDeviceCount; - UINT iPlaybackDevice; - UINT iCaptureDevice; - - MA_ASSERT(pContext != NULL); - MA_ASSERT(callback != NULL); - - /* Playback. */ - playbackDeviceCount = ((MA_PFN_waveOutGetNumDevs)pContext->winmm.waveOutGetNumDevs)(); - for (iPlaybackDevice = 0; iPlaybackDevice < playbackDeviceCount; ++iPlaybackDevice) { - MA_MMRESULT result; - MA_WAVEOUTCAPS2A caps; - - MA_ZERO_OBJECT(&caps); - - result = ((MA_PFN_waveOutGetDevCapsA)pContext->winmm.waveOutGetDevCapsA)(iPlaybackDevice, (MA_WAVEOUTCAPSA*)&caps, sizeof(caps)); - if (result == MA_MMSYSERR_NOERROR) { - ma_device_info deviceInfo; - - MA_ZERO_OBJECT(&deviceInfo); - deviceInfo.id.winmm = iPlaybackDevice; - - /* The first enumerated device is the default device. */ - if (iPlaybackDevice == 0) { - deviceInfo.isDefault = MA_TRUE; - } - - if (ma_context_get_device_info_from_WAVEOUTCAPS2(pContext, &caps, &deviceInfo) == MA_SUCCESS) { - ma_bool32 cbResult = callback(pContext, ma_device_type_playback, &deviceInfo, pUserData); - if (cbResult == MA_FALSE) { - return MA_SUCCESS; /* Enumeration was stopped. */ - } - } - } - } - - /* Capture. */ - captureDeviceCount = ((MA_PFN_waveInGetNumDevs)pContext->winmm.waveInGetNumDevs)(); - for (iCaptureDevice = 0; iCaptureDevice < captureDeviceCount; ++iCaptureDevice) { - MA_MMRESULT result; - MA_WAVEINCAPS2A caps; - - MA_ZERO_OBJECT(&caps); - - result = ((MA_PFN_waveInGetDevCapsA)pContext->winmm.waveInGetDevCapsA)(iCaptureDevice, (MA_WAVEINCAPSA*)&caps, sizeof(caps)); - if (result == MA_MMSYSERR_NOERROR) { - ma_device_info deviceInfo; - - MA_ZERO_OBJECT(&deviceInfo); - deviceInfo.id.winmm = iCaptureDevice; - - /* The first enumerated device is the default device. */ - if (iCaptureDevice == 0) { - deviceInfo.isDefault = MA_TRUE; - } - - if (ma_context_get_device_info_from_WAVEINCAPS2(pContext, &caps, &deviceInfo) == MA_SUCCESS) { - ma_bool32 cbResult = callback(pContext, ma_device_type_capture, &deviceInfo, pUserData); - if (cbResult == MA_FALSE) { - return MA_SUCCESS; /* Enumeration was stopped. */ - } - } - } - } - - return MA_SUCCESS; -} - -static ma_result ma_context_get_device_info__winmm(ma_context* pContext, ma_device_type deviceType, const ma_device_id* pDeviceID, ma_device_info* pDeviceInfo) -{ - UINT winMMDeviceID; - - MA_ASSERT(pContext != NULL); - - winMMDeviceID = 0; - if (pDeviceID != NULL) { - winMMDeviceID = (UINT)pDeviceID->winmm; - } - - pDeviceInfo->id.winmm = winMMDeviceID; - - /* The first ID is the default device. */ - if (winMMDeviceID == 0) { - pDeviceInfo->isDefault = MA_TRUE; - } - - if (deviceType == ma_device_type_playback) { - MA_MMRESULT result; - MA_WAVEOUTCAPS2A caps; - - MA_ZERO_OBJECT(&caps); - - result = ((MA_PFN_waveOutGetDevCapsA)pContext->winmm.waveOutGetDevCapsA)(winMMDeviceID, (MA_WAVEOUTCAPSA*)&caps, sizeof(caps)); - if (result == MA_MMSYSERR_NOERROR) { - return ma_context_get_device_info_from_WAVEOUTCAPS2(pContext, &caps, pDeviceInfo); - } - } else { - MA_MMRESULT result; - MA_WAVEINCAPS2A caps; - - MA_ZERO_OBJECT(&caps); - - result = ((MA_PFN_waveInGetDevCapsA)pContext->winmm.waveInGetDevCapsA)(winMMDeviceID, (MA_WAVEINCAPSA*)&caps, sizeof(caps)); - if (result == MA_MMSYSERR_NOERROR) { - return ma_context_get_device_info_from_WAVEINCAPS2(pContext, &caps, pDeviceInfo); - } - } - - return MA_NO_DEVICE; -} - - -static ma_result ma_device_uninit__winmm(ma_device* pDevice) -{ - MA_ASSERT(pDevice != NULL); - - if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) { - ((MA_PFN_waveInClose)pDevice->pContext->winmm.waveInClose)((MA_HWAVEIN)pDevice->winmm.hDeviceCapture); - CloseHandle((HANDLE)pDevice->winmm.hEventCapture); - } - - if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) { - ((MA_PFN_waveOutReset)pDevice->pContext->winmm.waveOutReset)((MA_HWAVEOUT)pDevice->winmm.hDevicePlayback); - ((MA_PFN_waveOutClose)pDevice->pContext->winmm.waveOutClose)((MA_HWAVEOUT)pDevice->winmm.hDevicePlayback); - CloseHandle((HANDLE)pDevice->winmm.hEventPlayback); - } - - ma_free(pDevice->winmm._pHeapData, &pDevice->pContext->allocationCallbacks); - - MA_ZERO_OBJECT(&pDevice->winmm); /* Safety. */ - - return MA_SUCCESS; -} - -static ma_uint32 ma_calculate_period_size_in_frames_from_descriptor__winmm(const ma_device_descriptor* pDescriptor, ma_uint32 nativeSampleRate, ma_performance_profile performanceProfile) -{ - /* WinMM has a minimum period size of 40ms. */ - ma_uint32 minPeriodSizeInFrames = ma_calculate_buffer_size_in_frames_from_milliseconds(40, nativeSampleRate); - ma_uint32 periodSizeInFrames; - - periodSizeInFrames = ma_calculate_buffer_size_in_frames_from_descriptor(pDescriptor, nativeSampleRate, performanceProfile); - if (periodSizeInFrames < minPeriodSizeInFrames) { - periodSizeInFrames = minPeriodSizeInFrames; - } - - return periodSizeInFrames; -} - -static ma_result ma_device_init__winmm(ma_device* pDevice, const ma_device_config* pConfig, ma_device_descriptor* pDescriptorPlayback, ma_device_descriptor* pDescriptorCapture) -{ - const char* errorMsg = ""; - ma_result errorCode = MA_ERROR; - ma_result result = MA_SUCCESS; - ma_uint32 heapSize; - UINT winMMDeviceIDPlayback = 0; - UINT winMMDeviceIDCapture = 0; - - MA_ASSERT(pDevice != NULL); - - MA_ZERO_OBJECT(&pDevice->winmm); - - if (pConfig->deviceType == ma_device_type_loopback) { - return MA_DEVICE_TYPE_NOT_SUPPORTED; - } - - /* No exclusive mode with WinMM. */ - if (((pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) && pDescriptorPlayback->shareMode == ma_share_mode_exclusive) || - ((pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) && pDescriptorCapture->shareMode == ma_share_mode_exclusive)) { - return MA_SHARE_MODE_NOT_SUPPORTED; - } - - if (pDescriptorPlayback->pDeviceID != NULL) { - winMMDeviceIDPlayback = (UINT)pDescriptorPlayback->pDeviceID->winmm; - } - if (pDescriptorCapture->pDeviceID != NULL) { - winMMDeviceIDCapture = (UINT)pDescriptorCapture->pDeviceID->winmm; - } - - /* The capture device needs to be initialized first. */ - if (pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) { - MA_WAVEINCAPSA caps; - MA_WAVEFORMATEX wf; - MA_MMRESULT resultMM; - - /* We use an event to know when a new fragment needs to be enqueued. */ - pDevice->winmm.hEventCapture = (ma_handle)CreateEventA(NULL, TRUE, TRUE, NULL); - if (pDevice->winmm.hEventCapture == NULL) { - errorMsg = "[WinMM] Failed to create event for fragment enqueuing for the capture device.", errorCode = ma_result_from_GetLastError(GetLastError()); - goto on_error; - } - - /* The format should be based on the device's actual format. */ - if (((MA_PFN_waveInGetDevCapsA)pDevice->pContext->winmm.waveInGetDevCapsA)(winMMDeviceIDCapture, &caps, sizeof(caps)) != MA_MMSYSERR_NOERROR) { - errorMsg = "[WinMM] Failed to retrieve internal device caps.", errorCode = MA_FORMAT_NOT_SUPPORTED; - goto on_error; - } - - result = ma_formats_flags_to_WAVEFORMATEX__winmm(caps.dwFormats, caps.wChannels, &wf); - if (result != MA_SUCCESS) { - errorMsg = "[WinMM] Could not find appropriate format for internal device.", errorCode = result; - goto on_error; - } - - resultMM = ((MA_PFN_waveInOpen)pDevice->pContext->winmm.waveInOpen)((MA_HWAVEIN*)&pDevice->winmm.hDeviceCapture, winMMDeviceIDCapture, &wf, (DWORD_PTR)pDevice->winmm.hEventCapture, (DWORD_PTR)pDevice, MA_CALLBACK_EVENT | MA_WAVE_ALLOWSYNC); - if (resultMM != MA_MMSYSERR_NOERROR) { - errorMsg = "[WinMM] Failed to open capture device.", errorCode = MA_FAILED_TO_OPEN_BACKEND_DEVICE; - goto on_error; - } - - pDescriptorCapture->format = ma_format_from_WAVEFORMATEX(&wf); - pDescriptorCapture->channels = wf.nChannels; - pDescriptorCapture->sampleRate = wf.nSamplesPerSec; - ma_channel_map_init_standard(ma_standard_channel_map_microsoft, pDescriptorCapture->channelMap, ma_countof(pDescriptorCapture->channelMap), pDescriptorCapture->channels); - pDescriptorCapture->periodCount = pDescriptorCapture->periodCount; - pDescriptorCapture->periodSizeInFrames = ma_calculate_period_size_in_frames_from_descriptor__winmm(pDescriptorCapture, pDescriptorCapture->sampleRate, pConfig->performanceProfile); - } - - if (pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) { - MA_WAVEOUTCAPSA caps; - MA_WAVEFORMATEX wf; - MA_MMRESULT resultMM; - - /* We use an event to know when a new fragment needs to be enqueued. */ - pDevice->winmm.hEventPlayback = (ma_handle)CreateEventA(NULL, TRUE, TRUE, NULL); - if (pDevice->winmm.hEventPlayback == NULL) { - errorMsg = "[WinMM] Failed to create event for fragment enqueuing for the playback device.", errorCode = ma_result_from_GetLastError(GetLastError()); - goto on_error; - } - - /* The format should be based on the device's actual format. */ - if (((MA_PFN_waveOutGetDevCapsA)pDevice->pContext->winmm.waveOutGetDevCapsA)(winMMDeviceIDPlayback, &caps, sizeof(caps)) != MA_MMSYSERR_NOERROR) { - errorMsg = "[WinMM] Failed to retrieve internal device caps.", errorCode = MA_FORMAT_NOT_SUPPORTED; - goto on_error; - } - - result = ma_formats_flags_to_WAVEFORMATEX__winmm(caps.dwFormats, caps.wChannels, &wf); - if (result != MA_SUCCESS) { - errorMsg = "[WinMM] Could not find appropriate format for internal device.", errorCode = result; - goto on_error; - } - - resultMM = ((MA_PFN_waveOutOpen)pDevice->pContext->winmm.waveOutOpen)((MA_HWAVEOUT*)&pDevice->winmm.hDevicePlayback, winMMDeviceIDPlayback, &wf, (DWORD_PTR)pDevice->winmm.hEventPlayback, (DWORD_PTR)pDevice, MA_CALLBACK_EVENT | MA_WAVE_ALLOWSYNC); - if (resultMM != MA_MMSYSERR_NOERROR) { - errorMsg = "[WinMM] Failed to open playback device.", errorCode = MA_FAILED_TO_OPEN_BACKEND_DEVICE; - goto on_error; - } - - pDescriptorPlayback->format = ma_format_from_WAVEFORMATEX(&wf); - pDescriptorPlayback->channels = wf.nChannels; - pDescriptorPlayback->sampleRate = wf.nSamplesPerSec; - ma_channel_map_init_standard(ma_standard_channel_map_microsoft, pDescriptorPlayback->channelMap, ma_countof(pDescriptorPlayback->channelMap), pDescriptorPlayback->channels); - pDescriptorPlayback->periodCount = pDescriptorPlayback->periodCount; - pDescriptorPlayback->periodSizeInFrames = ma_calculate_period_size_in_frames_from_descriptor__winmm(pDescriptorPlayback, pDescriptorPlayback->sampleRate, pConfig->performanceProfile); - } - - /* - The heap allocated data is allocated like so: - - [Capture WAVEHDRs][Playback WAVEHDRs][Capture Intermediary Buffer][Playback Intermediary Buffer] - */ - heapSize = 0; - if (pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) { - heapSize += sizeof(MA_WAVEHDR)*pDescriptorCapture->periodCount + (pDescriptorCapture->periodSizeInFrames * pDescriptorCapture->periodCount * ma_get_bytes_per_frame(pDescriptorCapture->format, pDescriptorCapture->channels)); - } - if (pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) { - heapSize += sizeof(MA_WAVEHDR)*pDescriptorPlayback->periodCount + (pDescriptorPlayback->periodSizeInFrames * pDescriptorPlayback->periodCount * ma_get_bytes_per_frame(pDescriptorPlayback->format, pDescriptorPlayback->channels)); - } - - pDevice->winmm._pHeapData = (ma_uint8*)ma_calloc(heapSize, &pDevice->pContext->allocationCallbacks); - if (pDevice->winmm._pHeapData == NULL) { - errorMsg = "[WinMM] Failed to allocate memory for the intermediary buffer.", errorCode = MA_OUT_OF_MEMORY; - goto on_error; - } - - MA_ZERO_MEMORY(pDevice->winmm._pHeapData, heapSize); - - if (pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) { - ma_uint32 iPeriod; - - if (pConfig->deviceType == ma_device_type_capture) { - pDevice->winmm.pWAVEHDRCapture = pDevice->winmm._pHeapData; - pDevice->winmm.pIntermediaryBufferCapture = pDevice->winmm._pHeapData + (sizeof(MA_WAVEHDR)*(pDescriptorCapture->periodCount)); - } else { - pDevice->winmm.pWAVEHDRCapture = pDevice->winmm._pHeapData; - pDevice->winmm.pIntermediaryBufferCapture = pDevice->winmm._pHeapData + (sizeof(MA_WAVEHDR)*(pDescriptorCapture->periodCount + pDescriptorPlayback->periodCount)); - } - - /* Prepare headers. */ - for (iPeriod = 0; iPeriod < pDescriptorCapture->periodCount; ++iPeriod) { - ma_uint32 periodSizeInBytes = ma_get_period_size_in_bytes(pDescriptorCapture->periodSizeInFrames, pDescriptorCapture->format, pDescriptorCapture->channels); - - ((MA_WAVEHDR*)pDevice->winmm.pWAVEHDRCapture)[iPeriod].lpData = (char*)(pDevice->winmm.pIntermediaryBufferCapture + (periodSizeInBytes*iPeriod)); - ((MA_WAVEHDR*)pDevice->winmm.pWAVEHDRCapture)[iPeriod].dwBufferLength = periodSizeInBytes; - ((MA_WAVEHDR*)pDevice->winmm.pWAVEHDRCapture)[iPeriod].dwFlags = 0L; - ((MA_WAVEHDR*)pDevice->winmm.pWAVEHDRCapture)[iPeriod].dwLoops = 0L; - ((MA_PFN_waveInPrepareHeader)pDevice->pContext->winmm.waveInPrepareHeader)((MA_HWAVEIN)pDevice->winmm.hDeviceCapture, &((MA_WAVEHDR*)pDevice->winmm.pWAVEHDRCapture)[iPeriod], sizeof(MA_WAVEHDR)); - - /* - The user data of the MA_WAVEHDR structure is a single flag the controls whether or not it is ready for writing. Consider it to be named "isLocked". A value of 0 means - it's unlocked and available for writing. A value of 1 means it's locked. - */ - ((MA_WAVEHDR*)pDevice->winmm.pWAVEHDRCapture)[iPeriod].dwUser = 0; - } - } - - if (pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) { - ma_uint32 iPeriod; - - if (pConfig->deviceType == ma_device_type_playback) { - pDevice->winmm.pWAVEHDRPlayback = pDevice->winmm._pHeapData; - pDevice->winmm.pIntermediaryBufferPlayback = pDevice->winmm._pHeapData + (sizeof(MA_WAVEHDR)*pDescriptorPlayback->periodCount); - } else { - pDevice->winmm.pWAVEHDRPlayback = pDevice->winmm._pHeapData + (sizeof(MA_WAVEHDR)*(pDescriptorCapture->periodCount)); - pDevice->winmm.pIntermediaryBufferPlayback = pDevice->winmm._pHeapData + (sizeof(MA_WAVEHDR)*(pDescriptorCapture->periodCount + pDescriptorPlayback->periodCount)) + (pDescriptorCapture->periodSizeInFrames*pDescriptorCapture->periodCount*ma_get_bytes_per_frame(pDescriptorCapture->format, pDescriptorCapture->channels)); - } - - /* Prepare headers. */ - for (iPeriod = 0; iPeriod < pDescriptorPlayback->periodCount; ++iPeriod) { - ma_uint32 periodSizeInBytes = ma_get_period_size_in_bytes(pDescriptorPlayback->periodSizeInFrames, pDescriptorPlayback->format, pDescriptorPlayback->channels); - - ((MA_WAVEHDR*)pDevice->winmm.pWAVEHDRPlayback)[iPeriod].lpData = (char*)(pDevice->winmm.pIntermediaryBufferPlayback + (periodSizeInBytes*iPeriod)); - ((MA_WAVEHDR*)pDevice->winmm.pWAVEHDRPlayback)[iPeriod].dwBufferLength = periodSizeInBytes; - ((MA_WAVEHDR*)pDevice->winmm.pWAVEHDRPlayback)[iPeriod].dwFlags = 0L; - ((MA_WAVEHDR*)pDevice->winmm.pWAVEHDRPlayback)[iPeriod].dwLoops = 0L; - ((MA_PFN_waveOutPrepareHeader)pDevice->pContext->winmm.waveOutPrepareHeader)((MA_HWAVEOUT)pDevice->winmm.hDevicePlayback, &((MA_WAVEHDR*)pDevice->winmm.pWAVEHDRPlayback)[iPeriod], sizeof(MA_WAVEHDR)); - - /* - The user data of the MA_WAVEHDR structure is a single flag the controls whether or not it is ready for writing. Consider it to be named "isLocked". A value of 0 means - it's unlocked and available for writing. A value of 1 means it's locked. - */ - ((MA_WAVEHDR*)pDevice->winmm.pWAVEHDRPlayback)[iPeriod].dwUser = 0; - } - } - - return MA_SUCCESS; - -on_error: - if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) { - if (pDevice->winmm.pWAVEHDRCapture != NULL) { - ma_uint32 iPeriod; - for (iPeriod = 0; iPeriod < pDescriptorCapture->periodCount; ++iPeriod) { - ((MA_PFN_waveInUnprepareHeader)pDevice->pContext->winmm.waveInUnprepareHeader)((MA_HWAVEIN)pDevice->winmm.hDeviceCapture, &((MA_WAVEHDR*)pDevice->winmm.pWAVEHDRCapture)[iPeriod], sizeof(MA_WAVEHDR)); - } - } - - ((MA_PFN_waveInClose)pDevice->pContext->winmm.waveInClose)((MA_HWAVEIN)pDevice->winmm.hDeviceCapture); - } - - if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) { - if (pDevice->winmm.pWAVEHDRCapture != NULL) { - ma_uint32 iPeriod; - for (iPeriod = 0; iPeriod < pDescriptorPlayback->periodCount; ++iPeriod) { - ((MA_PFN_waveOutUnprepareHeader)pDevice->pContext->winmm.waveOutUnprepareHeader)((MA_HWAVEOUT)pDevice->winmm.hDevicePlayback, &((MA_WAVEHDR*)pDevice->winmm.pWAVEHDRPlayback)[iPeriod], sizeof(MA_WAVEHDR)); - } - } - - ((MA_PFN_waveOutClose)pDevice->pContext->winmm.waveOutClose)((MA_HWAVEOUT)pDevice->winmm.hDevicePlayback); - } - - ma_free(pDevice->winmm._pHeapData, &pDevice->pContext->allocationCallbacks); - - if (errorMsg != NULL && errorMsg[0] != '\0') { - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "%s", errorMsg); - } - - return errorCode; -} - -static ma_result ma_device_start__winmm(ma_device* pDevice) -{ - MA_ASSERT(pDevice != NULL); - - if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) { - MA_MMRESULT resultMM; - MA_WAVEHDR* pWAVEHDR; - ma_uint32 iPeriod; - - pWAVEHDR = (MA_WAVEHDR*)pDevice->winmm.pWAVEHDRCapture; - - /* Make sure the event is reset to a non-signaled state to ensure we don't prematurely return from WaitForSingleObject(). */ - ResetEvent((HANDLE)pDevice->winmm.hEventCapture); - - /* To start the device we attach all of the buffers and then start it. As the buffers are filled with data we will get notifications. */ - for (iPeriod = 0; iPeriod < pDevice->capture.internalPeriods; ++iPeriod) { - resultMM = ((MA_PFN_waveInAddBuffer)pDevice->pContext->winmm.waveInAddBuffer)((MA_HWAVEIN)pDevice->winmm.hDeviceCapture, &((MA_WAVEHDR*)pDevice->winmm.pWAVEHDRCapture)[iPeriod], sizeof(MA_WAVEHDR)); - if (resultMM != MA_MMSYSERR_NOERROR) { - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[WinMM] Failed to attach input buffers to capture device in preparation for capture."); - return ma_result_from_MMRESULT(resultMM); - } - - /* Make sure all of the buffers start out locked. We don't want to access them until the backend tells us we can. */ - pWAVEHDR[iPeriod].dwUser = 1; /* 1 = locked. */ - } - - /* Capture devices need to be explicitly started, unlike playback devices. */ - resultMM = ((MA_PFN_waveInStart)pDevice->pContext->winmm.waveInStart)((MA_HWAVEIN)pDevice->winmm.hDeviceCapture); - if (resultMM != MA_MMSYSERR_NOERROR) { - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[WinMM] Failed to start backend device."); - return ma_result_from_MMRESULT(resultMM); - } - } - - if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) { - /* Don't need to do anything for playback. It'll be started automatically in ma_device_start__winmm(). */ - } - - return MA_SUCCESS; -} - -static ma_result ma_device_stop__winmm(ma_device* pDevice) -{ - MA_MMRESULT resultMM; - - MA_ASSERT(pDevice != NULL); - - if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) { - if (pDevice->winmm.hDeviceCapture == NULL) { - return MA_INVALID_ARGS; - } - - resultMM = ((MA_PFN_waveInReset)pDevice->pContext->winmm.waveInReset)((MA_HWAVEIN)pDevice->winmm.hDeviceCapture); - if (resultMM != MA_MMSYSERR_NOERROR) { - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_WARNING, "[WinMM] WARNING: Failed to reset capture device."); - } - } - - if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) { - ma_uint32 iPeriod; - MA_WAVEHDR* pWAVEHDR; - - if (pDevice->winmm.hDevicePlayback == NULL) { - return MA_INVALID_ARGS; - } - - /* We need to drain the device. To do this we just loop over each header and if it's locked just wait for the event. */ - pWAVEHDR = (MA_WAVEHDR*)pDevice->winmm.pWAVEHDRPlayback; - for (iPeriod = 0; iPeriod < pDevice->playback.internalPeriods; iPeriod += 1) { - if (pWAVEHDR[iPeriod].dwUser == 1) { /* 1 = locked. */ - if (WaitForSingleObject((HANDLE)pDevice->winmm.hEventPlayback, INFINITE) != WAIT_OBJECT_0) { - break; /* An error occurred so just abandon ship and stop the device without draining. */ - } - - pWAVEHDR[iPeriod].dwUser = 0; - } - } - - resultMM = ((MA_PFN_waveOutReset)pDevice->pContext->winmm.waveOutReset)((MA_HWAVEOUT)pDevice->winmm.hDevicePlayback); - if (resultMM != MA_MMSYSERR_NOERROR) { - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_WARNING, "[WinMM] WARNING: Failed to reset playback device."); - } - } - - return MA_SUCCESS; -} - -static ma_result ma_device_write__winmm(ma_device* pDevice, const void* pPCMFrames, ma_uint32 frameCount, ma_uint32* pFramesWritten) -{ - ma_result result = MA_SUCCESS; - MA_MMRESULT resultMM; - ma_uint32 totalFramesWritten; - MA_WAVEHDR* pWAVEHDR; - - MA_ASSERT(pDevice != NULL); - MA_ASSERT(pPCMFrames != NULL); - - if (pFramesWritten != NULL) { - *pFramesWritten = 0; - } - - pWAVEHDR = (MA_WAVEHDR*)pDevice->winmm.pWAVEHDRPlayback; - - /* Keep processing as much data as possible. */ - totalFramesWritten = 0; - while (totalFramesWritten < frameCount) { - /* If the current header has some space available we need to write part of it. */ - if (pWAVEHDR[pDevice->winmm.iNextHeaderPlayback].dwUser == 0) { /* 0 = unlocked. */ - /* - This header has room in it. We copy as much of it as we can. If we end up fully consuming the buffer we need to - write it out and move on to the next iteration. - */ - ma_uint32 bpf = ma_get_bytes_per_frame(pDevice->playback.internalFormat, pDevice->playback.internalChannels); - ma_uint32 framesRemainingInHeader = (pWAVEHDR[pDevice->winmm.iNextHeaderPlayback].dwBufferLength/bpf) - pDevice->winmm.headerFramesConsumedPlayback; - - ma_uint32 framesToCopy = ma_min(framesRemainingInHeader, (frameCount - totalFramesWritten)); - const void* pSrc = ma_offset_ptr(pPCMFrames, totalFramesWritten*bpf); - void* pDst = ma_offset_ptr(pWAVEHDR[pDevice->winmm.iNextHeaderPlayback].lpData, pDevice->winmm.headerFramesConsumedPlayback*bpf); - MA_COPY_MEMORY(pDst, pSrc, framesToCopy*bpf); - - pDevice->winmm.headerFramesConsumedPlayback += framesToCopy; - totalFramesWritten += framesToCopy; - - /* If we've consumed the buffer entirely we need to write it out to the device. */ - if (pDevice->winmm.headerFramesConsumedPlayback == (pWAVEHDR[pDevice->winmm.iNextHeaderPlayback].dwBufferLength/bpf)) { - pWAVEHDR[pDevice->winmm.iNextHeaderPlayback].dwUser = 1; /* 1 = locked. */ - pWAVEHDR[pDevice->winmm.iNextHeaderPlayback].dwFlags &= ~MA_WHDR_DONE; /* <-- Need to make sure the WHDR_DONE flag is unset. */ - - /* Make sure the event is reset to a non-signaled state to ensure we don't prematurely return from WaitForSingleObject(). */ - ResetEvent((HANDLE)pDevice->winmm.hEventPlayback); - - /* The device will be started here. */ - resultMM = ((MA_PFN_waveOutWrite)pDevice->pContext->winmm.waveOutWrite)((MA_HWAVEOUT)pDevice->winmm.hDevicePlayback, &pWAVEHDR[pDevice->winmm.iNextHeaderPlayback], sizeof(MA_WAVEHDR)); - if (resultMM != MA_MMSYSERR_NOERROR) { - result = ma_result_from_MMRESULT(resultMM); - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[WinMM] waveOutWrite() failed."); - break; - } - - /* Make sure we move to the next header. */ - pDevice->winmm.iNextHeaderPlayback = (pDevice->winmm.iNextHeaderPlayback + 1) % pDevice->playback.internalPeriods; - pDevice->winmm.headerFramesConsumedPlayback = 0; - } - - /* If at this point we have consumed the entire input buffer we can return. */ - MA_ASSERT(totalFramesWritten <= frameCount); - if (totalFramesWritten == frameCount) { - break; - } - - /* Getting here means there's more to process. */ - continue; - } - - /* Getting here means there isn't enough room in the buffer and we need to wait for one to become available. */ - if (WaitForSingleObject((HANDLE)pDevice->winmm.hEventPlayback, INFINITE) != WAIT_OBJECT_0) { - result = MA_ERROR; - break; - } - - /* Something happened. If the next buffer has been marked as done we need to reset a bit of state. */ - if ((pWAVEHDR[pDevice->winmm.iNextHeaderPlayback].dwFlags & MA_WHDR_DONE) != 0) { - pWAVEHDR[pDevice->winmm.iNextHeaderPlayback].dwUser = 0; /* 0 = unlocked (make it available for writing). */ - pDevice->winmm.headerFramesConsumedPlayback = 0; - } - - /* If the device has been stopped we need to break. */ - if (ma_device_get_state(pDevice) != ma_device_state_started) { - break; - } - } - - if (pFramesWritten != NULL) { - *pFramesWritten = totalFramesWritten; - } - - return result; -} - -static ma_result ma_device_read__winmm(ma_device* pDevice, void* pPCMFrames, ma_uint32 frameCount, ma_uint32* pFramesRead) -{ - ma_result result = MA_SUCCESS; - MA_MMRESULT resultMM; - ma_uint32 totalFramesRead; - MA_WAVEHDR* pWAVEHDR; - - MA_ASSERT(pDevice != NULL); - MA_ASSERT(pPCMFrames != NULL); - - if (pFramesRead != NULL) { - *pFramesRead = 0; - } - - pWAVEHDR = (MA_WAVEHDR*)pDevice->winmm.pWAVEHDRCapture; - - /* Keep processing as much data as possible. */ - totalFramesRead = 0; - while (totalFramesRead < frameCount) { - /* If the current header has some space available we need to write part of it. */ - if (pWAVEHDR[pDevice->winmm.iNextHeaderCapture].dwUser == 0) { /* 0 = unlocked. */ - /* The buffer is available for reading. If we fully consume it we need to add it back to the buffer. */ - ma_uint32 bpf = ma_get_bytes_per_frame(pDevice->capture.internalFormat, pDevice->capture.internalChannels); - ma_uint32 framesRemainingInHeader = (pWAVEHDR[pDevice->winmm.iNextHeaderCapture].dwBufferLength/bpf) - pDevice->winmm.headerFramesConsumedCapture; - - ma_uint32 framesToCopy = ma_min(framesRemainingInHeader, (frameCount - totalFramesRead)); - const void* pSrc = ma_offset_ptr(pWAVEHDR[pDevice->winmm.iNextHeaderCapture].lpData, pDevice->winmm.headerFramesConsumedCapture*bpf); - void* pDst = ma_offset_ptr(pPCMFrames, totalFramesRead*bpf); - MA_COPY_MEMORY(pDst, pSrc, framesToCopy*bpf); - - pDevice->winmm.headerFramesConsumedCapture += framesToCopy; - totalFramesRead += framesToCopy; - - /* If we've consumed the buffer entirely we need to add it back to the device. */ - if (pDevice->winmm.headerFramesConsumedCapture == (pWAVEHDR[pDevice->winmm.iNextHeaderCapture].dwBufferLength/bpf)) { - pWAVEHDR[pDevice->winmm.iNextHeaderCapture].dwUser = 1; /* 1 = locked. */ - pWAVEHDR[pDevice->winmm.iNextHeaderCapture].dwFlags &= ~MA_WHDR_DONE; /* <-- Need to make sure the WHDR_DONE flag is unset. */ - - /* Make sure the event is reset to a non-signaled state to ensure we don't prematurely return from WaitForSingleObject(). */ - ResetEvent((HANDLE)pDevice->winmm.hEventCapture); - - /* The device will be started here. */ - resultMM = ((MA_PFN_waveInAddBuffer)pDevice->pContext->winmm.waveInAddBuffer)((MA_HWAVEIN)pDevice->winmm.hDeviceCapture, &((MA_WAVEHDR*)pDevice->winmm.pWAVEHDRCapture)[pDevice->winmm.iNextHeaderCapture], sizeof(MA_WAVEHDR)); - if (resultMM != MA_MMSYSERR_NOERROR) { - result = ma_result_from_MMRESULT(resultMM); - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[WinMM] waveInAddBuffer() failed."); - break; - } - - /* Make sure we move to the next header. */ - pDevice->winmm.iNextHeaderCapture = (pDevice->winmm.iNextHeaderCapture + 1) % pDevice->capture.internalPeriods; - pDevice->winmm.headerFramesConsumedCapture = 0; - } - - /* If at this point we have filled the entire input buffer we can return. */ - MA_ASSERT(totalFramesRead <= frameCount); - if (totalFramesRead == frameCount) { - break; - } - - /* Getting here means there's more to process. */ - continue; - } - - /* Getting here means there isn't enough any data left to send to the client which means we need to wait for more. */ - if (WaitForSingleObject((HANDLE)pDevice->winmm.hEventCapture, INFINITE) != WAIT_OBJECT_0) { - result = MA_ERROR; - break; - } - - /* Something happened. If the next buffer has been marked as done we need to reset a bit of state. */ - if ((pWAVEHDR[pDevice->winmm.iNextHeaderCapture].dwFlags & MA_WHDR_DONE) != 0) { - pWAVEHDR[pDevice->winmm.iNextHeaderCapture].dwUser = 0; /* 0 = unlocked (make it available for reading). */ - pDevice->winmm.headerFramesConsumedCapture = 0; - } - - /* If the device has been stopped we need to break. */ - if (ma_device_get_state(pDevice) != ma_device_state_started) { - break; - } - } - - if (pFramesRead != NULL) { - *pFramesRead = totalFramesRead; - } - - return result; -} - -static ma_result ma_context_uninit__winmm(ma_context* pContext) -{ - MA_ASSERT(pContext != NULL); - MA_ASSERT(pContext->backend == ma_backend_winmm); - - ma_dlclose(ma_context_get_log(pContext), pContext->winmm.hWinMM); - return MA_SUCCESS; -} - -static ma_result ma_context_init__winmm(ma_context* pContext, const ma_context_config* pConfig, ma_backend_callbacks* pCallbacks) -{ - MA_ASSERT(pContext != NULL); - - (void)pConfig; - - pContext->winmm.hWinMM = ma_dlopen(ma_context_get_log(pContext), "winmm.dll"); - if (pContext->winmm.hWinMM == NULL) { - return MA_NO_BACKEND; - } - - pContext->winmm.waveOutGetNumDevs = ma_dlsym(ma_context_get_log(pContext), pContext->winmm.hWinMM, "waveOutGetNumDevs"); - pContext->winmm.waveOutGetDevCapsA = ma_dlsym(ma_context_get_log(pContext), pContext->winmm.hWinMM, "waveOutGetDevCapsA"); - pContext->winmm.waveOutOpen = ma_dlsym(ma_context_get_log(pContext), pContext->winmm.hWinMM, "waveOutOpen"); - pContext->winmm.waveOutClose = ma_dlsym(ma_context_get_log(pContext), pContext->winmm.hWinMM, "waveOutClose"); - pContext->winmm.waveOutPrepareHeader = ma_dlsym(ma_context_get_log(pContext), pContext->winmm.hWinMM, "waveOutPrepareHeader"); - pContext->winmm.waveOutUnprepareHeader = ma_dlsym(ma_context_get_log(pContext), pContext->winmm.hWinMM, "waveOutUnprepareHeader"); - pContext->winmm.waveOutWrite = ma_dlsym(ma_context_get_log(pContext), pContext->winmm.hWinMM, "waveOutWrite"); - pContext->winmm.waveOutReset = ma_dlsym(ma_context_get_log(pContext), pContext->winmm.hWinMM, "waveOutReset"); - pContext->winmm.waveInGetNumDevs = ma_dlsym(ma_context_get_log(pContext), pContext->winmm.hWinMM, "waveInGetNumDevs"); - pContext->winmm.waveInGetDevCapsA = ma_dlsym(ma_context_get_log(pContext), pContext->winmm.hWinMM, "waveInGetDevCapsA"); - pContext->winmm.waveInOpen = ma_dlsym(ma_context_get_log(pContext), pContext->winmm.hWinMM, "waveInOpen"); - pContext->winmm.waveInClose = ma_dlsym(ma_context_get_log(pContext), pContext->winmm.hWinMM, "waveInClose"); - pContext->winmm.waveInPrepareHeader = ma_dlsym(ma_context_get_log(pContext), pContext->winmm.hWinMM, "waveInPrepareHeader"); - pContext->winmm.waveInUnprepareHeader = ma_dlsym(ma_context_get_log(pContext), pContext->winmm.hWinMM, "waveInUnprepareHeader"); - pContext->winmm.waveInAddBuffer = ma_dlsym(ma_context_get_log(pContext), pContext->winmm.hWinMM, "waveInAddBuffer"); - pContext->winmm.waveInStart = ma_dlsym(ma_context_get_log(pContext), pContext->winmm.hWinMM, "waveInStart"); - pContext->winmm.waveInReset = ma_dlsym(ma_context_get_log(pContext), pContext->winmm.hWinMM, "waveInReset"); - - pCallbacks->onContextInit = ma_context_init__winmm; - pCallbacks->onContextUninit = ma_context_uninit__winmm; - pCallbacks->onContextEnumerateDevices = ma_context_enumerate_devices__winmm; - pCallbacks->onContextGetDeviceInfo = ma_context_get_device_info__winmm; - pCallbacks->onDeviceInit = ma_device_init__winmm; - pCallbacks->onDeviceUninit = ma_device_uninit__winmm; - pCallbacks->onDeviceStart = ma_device_start__winmm; - pCallbacks->onDeviceStop = ma_device_stop__winmm; - pCallbacks->onDeviceRead = ma_device_read__winmm; - pCallbacks->onDeviceWrite = ma_device_write__winmm; - pCallbacks->onDeviceDataLoop = NULL; /* This is a blocking read-write API, so this can be NULL since miniaudio will manage the audio thread for us. */ - - return MA_SUCCESS; -} -#endif - - - - -/****************************************************************************** - -ALSA Backend - -******************************************************************************/ -#ifdef MA_HAS_ALSA - -#include /* poll(), struct pollfd */ -#include /* eventfd() */ - -#ifdef MA_NO_RUNTIME_LINKING - -/* asoundlib.h marks some functions with "inline" which isn't always supported. Need to emulate it. */ -#if !defined(__cplusplus) - #if defined(__STRICT_ANSI__) - #if !defined(inline) - #define inline __inline__ __attribute__((always_inline)) - #define MA_INLINE_DEFINED - #endif - #endif -#endif -#include -#if defined(MA_INLINE_DEFINED) - #undef inline - #undef MA_INLINE_DEFINED -#endif - -typedef snd_pcm_uframes_t ma_snd_pcm_uframes_t; -typedef snd_pcm_sframes_t ma_snd_pcm_sframes_t; -typedef snd_pcm_stream_t ma_snd_pcm_stream_t; -typedef snd_pcm_format_t ma_snd_pcm_format_t; -typedef snd_pcm_access_t ma_snd_pcm_access_t; -typedef snd_pcm_t ma_snd_pcm_t; -typedef snd_pcm_hw_params_t ma_snd_pcm_hw_params_t; -typedef snd_pcm_sw_params_t ma_snd_pcm_sw_params_t; -typedef snd_pcm_format_mask_t ma_snd_pcm_format_mask_t; -typedef snd_pcm_info_t ma_snd_pcm_info_t; -typedef snd_pcm_channel_area_t ma_snd_pcm_channel_area_t; -typedef snd_pcm_chmap_t ma_snd_pcm_chmap_t; -typedef snd_pcm_state_t ma_snd_pcm_state_t; - -/* snd_pcm_stream_t */ -#define MA_SND_PCM_STREAM_PLAYBACK SND_PCM_STREAM_PLAYBACK -#define MA_SND_PCM_STREAM_CAPTURE SND_PCM_STREAM_CAPTURE - -/* snd_pcm_format_t */ -#define MA_SND_PCM_FORMAT_UNKNOWN SND_PCM_FORMAT_UNKNOWN -#define MA_SND_PCM_FORMAT_U8 SND_PCM_FORMAT_U8 -#define MA_SND_PCM_FORMAT_S16_LE SND_PCM_FORMAT_S16_LE -#define MA_SND_PCM_FORMAT_S16_BE SND_PCM_FORMAT_S16_BE -#define MA_SND_PCM_FORMAT_S24_LE SND_PCM_FORMAT_S24_LE -#define MA_SND_PCM_FORMAT_S24_BE SND_PCM_FORMAT_S24_BE -#define MA_SND_PCM_FORMAT_S32_LE SND_PCM_FORMAT_S32_LE -#define MA_SND_PCM_FORMAT_S32_BE SND_PCM_FORMAT_S32_BE -#define MA_SND_PCM_FORMAT_FLOAT_LE SND_PCM_FORMAT_FLOAT_LE -#define MA_SND_PCM_FORMAT_FLOAT_BE SND_PCM_FORMAT_FLOAT_BE -#define MA_SND_PCM_FORMAT_FLOAT64_LE SND_PCM_FORMAT_FLOAT64_LE -#define MA_SND_PCM_FORMAT_FLOAT64_BE SND_PCM_FORMAT_FLOAT64_BE -#define MA_SND_PCM_FORMAT_MU_LAW SND_PCM_FORMAT_MU_LAW -#define MA_SND_PCM_FORMAT_A_LAW SND_PCM_FORMAT_A_LAW -#define MA_SND_PCM_FORMAT_S24_3LE SND_PCM_FORMAT_S24_3LE -#define MA_SND_PCM_FORMAT_S24_3BE SND_PCM_FORMAT_S24_3BE - -/* ma_snd_pcm_access_t */ -#define MA_SND_PCM_ACCESS_MMAP_INTERLEAVED SND_PCM_ACCESS_MMAP_INTERLEAVED -#define MA_SND_PCM_ACCESS_MMAP_NONINTERLEAVED SND_PCM_ACCESS_MMAP_NONINTERLEAVED -#define MA_SND_PCM_ACCESS_MMAP_COMPLEX SND_PCM_ACCESS_MMAP_COMPLEX -#define MA_SND_PCM_ACCESS_RW_INTERLEAVED SND_PCM_ACCESS_RW_INTERLEAVED -#define MA_SND_PCM_ACCESS_RW_NONINTERLEAVED SND_PCM_ACCESS_RW_NONINTERLEAVED - -/* Channel positions. */ -#define MA_SND_CHMAP_UNKNOWN SND_CHMAP_UNKNOWN -#define MA_SND_CHMAP_NA SND_CHMAP_NA -#define MA_SND_CHMAP_MONO SND_CHMAP_MONO -#define MA_SND_CHMAP_FL SND_CHMAP_FL -#define MA_SND_CHMAP_FR SND_CHMAP_FR -#define MA_SND_CHMAP_RL SND_CHMAP_RL -#define MA_SND_CHMAP_RR SND_CHMAP_RR -#define MA_SND_CHMAP_FC SND_CHMAP_FC -#define MA_SND_CHMAP_LFE SND_CHMAP_LFE -#define MA_SND_CHMAP_SL SND_CHMAP_SL -#define MA_SND_CHMAP_SR SND_CHMAP_SR -#define MA_SND_CHMAP_RC SND_CHMAP_RC -#define MA_SND_CHMAP_FLC SND_CHMAP_FLC -#define MA_SND_CHMAP_FRC SND_CHMAP_FRC -#define MA_SND_CHMAP_RLC SND_CHMAP_RLC -#define MA_SND_CHMAP_RRC SND_CHMAP_RRC -#define MA_SND_CHMAP_FLW SND_CHMAP_FLW -#define MA_SND_CHMAP_FRW SND_CHMAP_FRW -#define MA_SND_CHMAP_FLH SND_CHMAP_FLH -#define MA_SND_CHMAP_FCH SND_CHMAP_FCH -#define MA_SND_CHMAP_FRH SND_CHMAP_FRH -#define MA_SND_CHMAP_TC SND_CHMAP_TC -#define MA_SND_CHMAP_TFL SND_CHMAP_TFL -#define MA_SND_CHMAP_TFR SND_CHMAP_TFR -#define MA_SND_CHMAP_TFC SND_CHMAP_TFC -#define MA_SND_CHMAP_TRL SND_CHMAP_TRL -#define MA_SND_CHMAP_TRR SND_CHMAP_TRR -#define MA_SND_CHMAP_TRC SND_CHMAP_TRC -#define MA_SND_CHMAP_TFLC SND_CHMAP_TFLC -#define MA_SND_CHMAP_TFRC SND_CHMAP_TFRC -#define MA_SND_CHMAP_TSL SND_CHMAP_TSL -#define MA_SND_CHMAP_TSR SND_CHMAP_TSR -#define MA_SND_CHMAP_LLFE SND_CHMAP_LLFE -#define MA_SND_CHMAP_RLFE SND_CHMAP_RLFE -#define MA_SND_CHMAP_BC SND_CHMAP_BC -#define MA_SND_CHMAP_BLC SND_CHMAP_BLC -#define MA_SND_CHMAP_BRC SND_CHMAP_BRC - -/* Open mode flags. */ -#define MA_SND_PCM_NO_AUTO_RESAMPLE SND_PCM_NO_AUTO_RESAMPLE -#define MA_SND_PCM_NO_AUTO_CHANNELS SND_PCM_NO_AUTO_CHANNELS -#define MA_SND_PCM_NO_AUTO_FORMAT SND_PCM_NO_AUTO_FORMAT -#else -#include /* For EPIPE, etc. */ -typedef unsigned long ma_snd_pcm_uframes_t; -typedef long ma_snd_pcm_sframes_t; -typedef int ma_snd_pcm_stream_t; -typedef int ma_snd_pcm_format_t; -typedef int ma_snd_pcm_access_t; -typedef int ma_snd_pcm_state_t; -typedef struct ma_snd_pcm_t ma_snd_pcm_t; -typedef struct ma_snd_pcm_hw_params_t ma_snd_pcm_hw_params_t; -typedef struct ma_snd_pcm_sw_params_t ma_snd_pcm_sw_params_t; -typedef struct ma_snd_pcm_format_mask_t ma_snd_pcm_format_mask_t; -typedef struct ma_snd_pcm_info_t ma_snd_pcm_info_t; -typedef struct -{ - void* addr; - unsigned int first; - unsigned int step; -} ma_snd_pcm_channel_area_t; -typedef struct -{ - unsigned int channels; - unsigned int pos[1]; -} ma_snd_pcm_chmap_t; - -/* snd_pcm_state_t */ -#define MA_SND_PCM_STATE_OPEN 0 -#define MA_SND_PCM_STATE_SETUP 1 -#define MA_SND_PCM_STATE_PREPARED 2 -#define MA_SND_PCM_STATE_RUNNING 3 -#define MA_SND_PCM_STATE_XRUN 4 -#define MA_SND_PCM_STATE_DRAINING 5 -#define MA_SND_PCM_STATE_PAUSED 6 -#define MA_SND_PCM_STATE_SUSPENDED 7 -#define MA_SND_PCM_STATE_DISCONNECTED 8 - -/* snd_pcm_stream_t */ -#define MA_SND_PCM_STREAM_PLAYBACK 0 -#define MA_SND_PCM_STREAM_CAPTURE 1 - -/* snd_pcm_format_t */ -#define MA_SND_PCM_FORMAT_UNKNOWN -1 -#define MA_SND_PCM_FORMAT_U8 1 -#define MA_SND_PCM_FORMAT_S16_LE 2 -#define MA_SND_PCM_FORMAT_S16_BE 3 -#define MA_SND_PCM_FORMAT_S24_LE 6 -#define MA_SND_PCM_FORMAT_S24_BE 7 -#define MA_SND_PCM_FORMAT_S32_LE 10 -#define MA_SND_PCM_FORMAT_S32_BE 11 -#define MA_SND_PCM_FORMAT_FLOAT_LE 14 -#define MA_SND_PCM_FORMAT_FLOAT_BE 15 -#define MA_SND_PCM_FORMAT_FLOAT64_LE 16 -#define MA_SND_PCM_FORMAT_FLOAT64_BE 17 -#define MA_SND_PCM_FORMAT_MU_LAW 20 -#define MA_SND_PCM_FORMAT_A_LAW 21 -#define MA_SND_PCM_FORMAT_S24_3LE 32 -#define MA_SND_PCM_FORMAT_S24_3BE 33 - -/* snd_pcm_access_t */ -#define MA_SND_PCM_ACCESS_MMAP_INTERLEAVED 0 -#define MA_SND_PCM_ACCESS_MMAP_NONINTERLEAVED 1 -#define MA_SND_PCM_ACCESS_MMAP_COMPLEX 2 -#define MA_SND_PCM_ACCESS_RW_INTERLEAVED 3 -#define MA_SND_PCM_ACCESS_RW_NONINTERLEAVED 4 - -/* Channel positions. */ -#define MA_SND_CHMAP_UNKNOWN 0 -#define MA_SND_CHMAP_NA 1 -#define MA_SND_CHMAP_MONO 2 -#define MA_SND_CHMAP_FL 3 -#define MA_SND_CHMAP_FR 4 -#define MA_SND_CHMAP_RL 5 -#define MA_SND_CHMAP_RR 6 -#define MA_SND_CHMAP_FC 7 -#define MA_SND_CHMAP_LFE 8 -#define MA_SND_CHMAP_SL 9 -#define MA_SND_CHMAP_SR 10 -#define MA_SND_CHMAP_RC 11 -#define MA_SND_CHMAP_FLC 12 -#define MA_SND_CHMAP_FRC 13 -#define MA_SND_CHMAP_RLC 14 -#define MA_SND_CHMAP_RRC 15 -#define MA_SND_CHMAP_FLW 16 -#define MA_SND_CHMAP_FRW 17 -#define MA_SND_CHMAP_FLH 18 -#define MA_SND_CHMAP_FCH 19 -#define MA_SND_CHMAP_FRH 20 -#define MA_SND_CHMAP_TC 21 -#define MA_SND_CHMAP_TFL 22 -#define MA_SND_CHMAP_TFR 23 -#define MA_SND_CHMAP_TFC 24 -#define MA_SND_CHMAP_TRL 25 -#define MA_SND_CHMAP_TRR 26 -#define MA_SND_CHMAP_TRC 27 -#define MA_SND_CHMAP_TFLC 28 -#define MA_SND_CHMAP_TFRC 29 -#define MA_SND_CHMAP_TSL 30 -#define MA_SND_CHMAP_TSR 31 -#define MA_SND_CHMAP_LLFE 32 -#define MA_SND_CHMAP_RLFE 33 -#define MA_SND_CHMAP_BC 34 -#define MA_SND_CHMAP_BLC 35 -#define MA_SND_CHMAP_BRC 36 - -/* Open mode flags. */ -#define MA_SND_PCM_NO_AUTO_RESAMPLE 0x00010000 -#define MA_SND_PCM_NO_AUTO_CHANNELS 0x00020000 -#define MA_SND_PCM_NO_AUTO_FORMAT 0x00040000 -#endif - -typedef int (* ma_snd_pcm_open_proc) (ma_snd_pcm_t **pcm, const char *name, ma_snd_pcm_stream_t stream, int mode); -typedef int (* ma_snd_pcm_close_proc) (ma_snd_pcm_t *pcm); -typedef size_t (* ma_snd_pcm_hw_params_sizeof_proc) (void); -typedef int (* ma_snd_pcm_hw_params_any_proc) (ma_snd_pcm_t *pcm, ma_snd_pcm_hw_params_t *params); -typedef int (* ma_snd_pcm_hw_params_set_format_proc) (ma_snd_pcm_t *pcm, ma_snd_pcm_hw_params_t *params, ma_snd_pcm_format_t val); -typedef int (* ma_snd_pcm_hw_params_set_format_first_proc) (ma_snd_pcm_t *pcm, ma_snd_pcm_hw_params_t *params, ma_snd_pcm_format_t *format); -typedef void (* ma_snd_pcm_hw_params_get_format_mask_proc) (ma_snd_pcm_hw_params_t *params, ma_snd_pcm_format_mask_t *mask); -typedef int (* ma_snd_pcm_hw_params_set_channels_proc) (ma_snd_pcm_t *pcm, ma_snd_pcm_hw_params_t *params, unsigned int val); -typedef int (* ma_snd_pcm_hw_params_set_channels_near_proc) (ma_snd_pcm_t *pcm, ma_snd_pcm_hw_params_t *params, unsigned int *val); -typedef int (* ma_snd_pcm_hw_params_set_channels_minmax_proc) (ma_snd_pcm_t *pcm, ma_snd_pcm_hw_params_t *params, unsigned int *minimum, unsigned int *maximum); -typedef int (* ma_snd_pcm_hw_params_set_rate_resample_proc) (ma_snd_pcm_t *pcm, ma_snd_pcm_hw_params_t *params, unsigned int val); -typedef int (* ma_snd_pcm_hw_params_set_rate_proc) (ma_snd_pcm_t *pcm, ma_snd_pcm_hw_params_t *params, unsigned int val, int dir); -typedef int (* ma_snd_pcm_hw_params_set_rate_near_proc) (ma_snd_pcm_t *pcm, ma_snd_pcm_hw_params_t *params, unsigned int *val, int *dir); -typedef int (* ma_snd_pcm_hw_params_set_buffer_size_near_proc)(ma_snd_pcm_t *pcm, ma_snd_pcm_hw_params_t *params, ma_snd_pcm_uframes_t *val); -typedef int (* ma_snd_pcm_hw_params_set_periods_near_proc) (ma_snd_pcm_t *pcm, ma_snd_pcm_hw_params_t *params, unsigned int *val, int *dir); -typedef int (* ma_snd_pcm_hw_params_set_access_proc) (ma_snd_pcm_t *pcm, ma_snd_pcm_hw_params_t *params, ma_snd_pcm_access_t _access); -typedef int (* ma_snd_pcm_hw_params_get_format_proc) (const ma_snd_pcm_hw_params_t *params, ma_snd_pcm_format_t *format); -typedef int (* ma_snd_pcm_hw_params_get_channels_proc) (const ma_snd_pcm_hw_params_t *params, unsigned int *val); -typedef int (* ma_snd_pcm_hw_params_get_channels_min_proc) (const ma_snd_pcm_hw_params_t *params, unsigned int *val); -typedef int (* ma_snd_pcm_hw_params_get_channels_max_proc) (const ma_snd_pcm_hw_params_t *params, unsigned int *val); -typedef int (* ma_snd_pcm_hw_params_get_rate_proc) (const ma_snd_pcm_hw_params_t *params, unsigned int *rate, int *dir); -typedef int (* ma_snd_pcm_hw_params_get_rate_min_proc) (const ma_snd_pcm_hw_params_t *params, unsigned int *rate, int *dir); -typedef int (* ma_snd_pcm_hw_params_get_rate_max_proc) (const ma_snd_pcm_hw_params_t *params, unsigned int *rate, int *dir); -typedef int (* ma_snd_pcm_hw_params_get_buffer_size_proc) (const ma_snd_pcm_hw_params_t *params, ma_snd_pcm_uframes_t *val); -typedef int (* ma_snd_pcm_hw_params_get_periods_proc) (const ma_snd_pcm_hw_params_t *params, unsigned int *val, int *dir); -typedef int (* ma_snd_pcm_hw_params_get_access_proc) (const ma_snd_pcm_hw_params_t *params, ma_snd_pcm_access_t *_access); -typedef int (* ma_snd_pcm_hw_params_test_format_proc) (ma_snd_pcm_t *pcm, ma_snd_pcm_hw_params_t *params, ma_snd_pcm_format_t val); -typedef int (* ma_snd_pcm_hw_params_test_channels_proc) (ma_snd_pcm_t *pcm, ma_snd_pcm_hw_params_t *params, unsigned int val); -typedef int (* ma_snd_pcm_hw_params_test_rate_proc) (ma_snd_pcm_t *pcm, ma_snd_pcm_hw_params_t *params, unsigned int val, int dir); -typedef int (* ma_snd_pcm_hw_params_proc) (ma_snd_pcm_t *pcm, ma_snd_pcm_hw_params_t *params); -typedef size_t (* ma_snd_pcm_sw_params_sizeof_proc) (void); -typedef int (* ma_snd_pcm_sw_params_current_proc) (ma_snd_pcm_t *pcm, ma_snd_pcm_sw_params_t *params); -typedef int (* ma_snd_pcm_sw_params_get_boundary_proc) (const ma_snd_pcm_sw_params_t *params, ma_snd_pcm_uframes_t* val); -typedef int (* ma_snd_pcm_sw_params_set_avail_min_proc) (ma_snd_pcm_t *pcm, ma_snd_pcm_sw_params_t *params, ma_snd_pcm_uframes_t val); -typedef int (* ma_snd_pcm_sw_params_set_start_threshold_proc) (ma_snd_pcm_t *pcm, ma_snd_pcm_sw_params_t *params, ma_snd_pcm_uframes_t val); -typedef int (* ma_snd_pcm_sw_params_set_stop_threshold_proc) (ma_snd_pcm_t *pcm, ma_snd_pcm_sw_params_t *params, ma_snd_pcm_uframes_t val); -typedef int (* ma_snd_pcm_sw_params_proc) (ma_snd_pcm_t *pcm, ma_snd_pcm_sw_params_t *params); -typedef size_t (* ma_snd_pcm_format_mask_sizeof_proc) (void); -typedef int (* ma_snd_pcm_format_mask_test_proc) (const ma_snd_pcm_format_mask_t *mask, ma_snd_pcm_format_t val); -typedef ma_snd_pcm_chmap_t * (* ma_snd_pcm_get_chmap_proc) (ma_snd_pcm_t *pcm); -typedef ma_snd_pcm_state_t (* ma_snd_pcm_state_proc) (ma_snd_pcm_t *pcm); -typedef int (* ma_snd_pcm_prepare_proc) (ma_snd_pcm_t *pcm); -typedef int (* ma_snd_pcm_start_proc) (ma_snd_pcm_t *pcm); -typedef int (* ma_snd_pcm_drop_proc) (ma_snd_pcm_t *pcm); -typedef int (* ma_snd_pcm_drain_proc) (ma_snd_pcm_t *pcm); -typedef int (* ma_snd_pcm_reset_proc) (ma_snd_pcm_t *pcm); -typedef int (* ma_snd_device_name_hint_proc) (int card, const char *iface, void ***hints); -typedef char * (* ma_snd_device_name_get_hint_proc) (const void *hint, const char *id); -typedef int (* ma_snd_card_get_index_proc) (const char *name); -typedef int (* ma_snd_device_name_free_hint_proc) (void **hints); -typedef int (* ma_snd_pcm_mmap_begin_proc) (ma_snd_pcm_t *pcm, const ma_snd_pcm_channel_area_t **areas, ma_snd_pcm_uframes_t *offset, ma_snd_pcm_uframes_t *frames); -typedef ma_snd_pcm_sframes_t (* ma_snd_pcm_mmap_commit_proc) (ma_snd_pcm_t *pcm, ma_snd_pcm_uframes_t offset, ma_snd_pcm_uframes_t frames); -typedef int (* ma_snd_pcm_recover_proc) (ma_snd_pcm_t *pcm, int err, int silent); -typedef ma_snd_pcm_sframes_t (* ma_snd_pcm_readi_proc) (ma_snd_pcm_t *pcm, void *buffer, ma_snd_pcm_uframes_t size); -typedef ma_snd_pcm_sframes_t (* ma_snd_pcm_writei_proc) (ma_snd_pcm_t *pcm, const void *buffer, ma_snd_pcm_uframes_t size); -typedef ma_snd_pcm_sframes_t (* ma_snd_pcm_avail_proc) (ma_snd_pcm_t *pcm); -typedef ma_snd_pcm_sframes_t (* ma_snd_pcm_avail_update_proc) (ma_snd_pcm_t *pcm); -typedef int (* ma_snd_pcm_wait_proc) (ma_snd_pcm_t *pcm, int timeout); -typedef int (* ma_snd_pcm_nonblock_proc) (ma_snd_pcm_t *pcm, int nonblock); -typedef int (* ma_snd_pcm_info_proc) (ma_snd_pcm_t *pcm, ma_snd_pcm_info_t* info); -typedef size_t (* ma_snd_pcm_info_sizeof_proc) (void); -typedef const char* (* ma_snd_pcm_info_get_name_proc) (const ma_snd_pcm_info_t* info); -typedef int (* ma_snd_pcm_poll_descriptors_proc) (ma_snd_pcm_t *pcm, struct pollfd *pfds, unsigned int space); -typedef int (* ma_snd_pcm_poll_descriptors_count_proc) (ma_snd_pcm_t *pcm); -typedef int (* ma_snd_pcm_poll_descriptors_revents_proc) (ma_snd_pcm_t *pcm, struct pollfd *pfds, unsigned int nfds, unsigned short *revents); -typedef int (* ma_snd_config_update_free_global_proc) (void); - -/* This array specifies each of the common devices that can be used for both playback and capture. */ -static const char* g_maCommonDeviceNamesALSA[] = { - "default", - "null", - "pulse", - "jack" -}; - -/* This array allows us to blacklist specific playback devices. */ -static const char* g_maBlacklistedPlaybackDeviceNamesALSA[] = { - "" -}; - -/* This array allows us to blacklist specific capture devices. */ -static const char* g_maBlacklistedCaptureDeviceNamesALSA[] = { - "" -}; - - -static ma_snd_pcm_format_t ma_convert_ma_format_to_alsa_format(ma_format format) -{ - ma_snd_pcm_format_t ALSAFormats[] = { - MA_SND_PCM_FORMAT_UNKNOWN, /* ma_format_unknown */ - MA_SND_PCM_FORMAT_U8, /* ma_format_u8 */ - MA_SND_PCM_FORMAT_S16_LE, /* ma_format_s16 */ - MA_SND_PCM_FORMAT_S24_3LE, /* ma_format_s24 */ - MA_SND_PCM_FORMAT_S32_LE, /* ma_format_s32 */ - MA_SND_PCM_FORMAT_FLOAT_LE /* ma_format_f32 */ - }; - - if (ma_is_big_endian()) { - ALSAFormats[0] = MA_SND_PCM_FORMAT_UNKNOWN; - ALSAFormats[1] = MA_SND_PCM_FORMAT_U8; - ALSAFormats[2] = MA_SND_PCM_FORMAT_S16_BE; - ALSAFormats[3] = MA_SND_PCM_FORMAT_S24_3BE; - ALSAFormats[4] = MA_SND_PCM_FORMAT_S32_BE; - ALSAFormats[5] = MA_SND_PCM_FORMAT_FLOAT_BE; - } - - return ALSAFormats[format]; -} - -static ma_format ma_format_from_alsa(ma_snd_pcm_format_t formatALSA) -{ - if (ma_is_little_endian()) { - switch (formatALSA) { - case MA_SND_PCM_FORMAT_S16_LE: return ma_format_s16; - case MA_SND_PCM_FORMAT_S24_3LE: return ma_format_s24; - case MA_SND_PCM_FORMAT_S32_LE: return ma_format_s32; - case MA_SND_PCM_FORMAT_FLOAT_LE: return ma_format_f32; - default: break; - } - } else { - switch (formatALSA) { - case MA_SND_PCM_FORMAT_S16_BE: return ma_format_s16; - case MA_SND_PCM_FORMAT_S24_3BE: return ma_format_s24; - case MA_SND_PCM_FORMAT_S32_BE: return ma_format_s32; - case MA_SND_PCM_FORMAT_FLOAT_BE: return ma_format_f32; - default: break; - } - } - - /* Endian agnostic. */ - switch (formatALSA) { - case MA_SND_PCM_FORMAT_U8: return ma_format_u8; - default: return ma_format_unknown; - } -} - -static ma_channel ma_convert_alsa_channel_position_to_ma_channel(unsigned int alsaChannelPos) -{ - switch (alsaChannelPos) - { - case MA_SND_CHMAP_MONO: return MA_CHANNEL_MONO; - case MA_SND_CHMAP_FL: return MA_CHANNEL_FRONT_LEFT; - case MA_SND_CHMAP_FR: return MA_CHANNEL_FRONT_RIGHT; - case MA_SND_CHMAP_RL: return MA_CHANNEL_BACK_LEFT; - case MA_SND_CHMAP_RR: return MA_CHANNEL_BACK_RIGHT; - case MA_SND_CHMAP_FC: return MA_CHANNEL_FRONT_CENTER; - case MA_SND_CHMAP_LFE: return MA_CHANNEL_LFE; - case MA_SND_CHMAP_SL: return MA_CHANNEL_SIDE_LEFT; - case MA_SND_CHMAP_SR: return MA_CHANNEL_SIDE_RIGHT; - case MA_SND_CHMAP_RC: return MA_CHANNEL_BACK_CENTER; - case MA_SND_CHMAP_FLC: return MA_CHANNEL_FRONT_LEFT_CENTER; - case MA_SND_CHMAP_FRC: return MA_CHANNEL_FRONT_RIGHT_CENTER; - case MA_SND_CHMAP_RLC: return 0; - case MA_SND_CHMAP_RRC: return 0; - case MA_SND_CHMAP_FLW: return 0; - case MA_SND_CHMAP_FRW: return 0; - case MA_SND_CHMAP_FLH: return 0; - case MA_SND_CHMAP_FCH: return 0; - case MA_SND_CHMAP_FRH: return 0; - case MA_SND_CHMAP_TC: return MA_CHANNEL_TOP_CENTER; - case MA_SND_CHMAP_TFL: return MA_CHANNEL_TOP_FRONT_LEFT; - case MA_SND_CHMAP_TFR: return MA_CHANNEL_TOP_FRONT_RIGHT; - case MA_SND_CHMAP_TFC: return MA_CHANNEL_TOP_FRONT_CENTER; - case MA_SND_CHMAP_TRL: return MA_CHANNEL_TOP_BACK_LEFT; - case MA_SND_CHMAP_TRR: return MA_CHANNEL_TOP_BACK_RIGHT; - case MA_SND_CHMAP_TRC: return MA_CHANNEL_TOP_BACK_CENTER; - default: break; - } - - return 0; -} - -static ma_bool32 ma_is_common_device_name__alsa(const char* name) -{ - size_t iName; - for (iName = 0; iName < ma_countof(g_maCommonDeviceNamesALSA); ++iName) { - if (ma_strcmp(name, g_maCommonDeviceNamesALSA[iName]) == 0) { - return MA_TRUE; - } - } - - return MA_FALSE; -} - - -static ma_bool32 ma_is_playback_device_blacklisted__alsa(const char* name) -{ - size_t iName; - for (iName = 0; iName < ma_countof(g_maBlacklistedPlaybackDeviceNamesALSA); ++iName) { - if (ma_strcmp(name, g_maBlacklistedPlaybackDeviceNamesALSA[iName]) == 0) { - return MA_TRUE; - } - } - - return MA_FALSE; -} - -static ma_bool32 ma_is_capture_device_blacklisted__alsa(const char* name) -{ - size_t iName; - for (iName = 0; iName < ma_countof(g_maBlacklistedCaptureDeviceNamesALSA); ++iName) { - if (ma_strcmp(name, g_maBlacklistedCaptureDeviceNamesALSA[iName]) == 0) { - return MA_TRUE; - } - } - - return MA_FALSE; -} - -static ma_bool32 ma_is_device_blacklisted__alsa(ma_device_type deviceType, const char* name) -{ - if (deviceType == ma_device_type_playback) { - return ma_is_playback_device_blacklisted__alsa(name); - } else { - return ma_is_capture_device_blacklisted__alsa(name); - } -} - - -static const char* ma_find_char(const char* str, char c, int* index) -{ - int i = 0; - for (;;) { - if (str[i] == '\0') { - if (index) *index = -1; - return NULL; - } - - if (str[i] == c) { - if (index) *index = i; - return str + i; - } - - i += 1; - } - - /* Should never get here, but treat it as though the character was not found to make me feel better inside. */ - if (index) *index = -1; - return NULL; -} - -static ma_bool32 ma_is_device_name_in_hw_format__alsa(const char* hwid) -{ - /* This function is just checking whether or not hwid is in "hw:%d,%d" format. */ - - int commaPos; - const char* dev; - int i; - - if (hwid == NULL) { - return MA_FALSE; - } - - if (hwid[0] != 'h' || hwid[1] != 'w' || hwid[2] != ':') { - return MA_FALSE; - } - - hwid += 3; - - dev = ma_find_char(hwid, ',', &commaPos); - if (dev == NULL) { - return MA_FALSE; - } else { - dev += 1; /* Skip past the ",". */ - } - - /* Check if the part between the ":" and the "," contains only numbers. If not, return false. */ - for (i = 0; i < commaPos; ++i) { - if (hwid[i] < '0' || hwid[i] > '9') { - return MA_FALSE; - } - } - - /* Check if everything after the "," is numeric. If not, return false. */ - i = 0; - while (dev[i] != '\0') { - if (dev[i] < '0' || dev[i] > '9') { - return MA_FALSE; - } - i += 1; - } - - return MA_TRUE; -} - -static int ma_convert_device_name_to_hw_format__alsa(ma_context* pContext, char* dst, size_t dstSize, const char* src) /* Returns 0 on success, non-0 on error. */ -{ - /* src should look something like this: "hw:CARD=I82801AAICH,DEV=0" */ - - int colonPos; - int commaPos; - char card[256]; - const char* dev; - int cardIndex; - - if (dst == NULL) { - return -1; - } - if (dstSize < 7) { - return -1; /* Absolute minimum size of the output buffer is 7 bytes. */ - } - - *dst = '\0'; /* Safety. */ - if (src == NULL) { - return -1; - } - - /* If the input name is already in "hw:%d,%d" format, just return that verbatim. */ - if (ma_is_device_name_in_hw_format__alsa(src)) { - return ma_strcpy_s(dst, dstSize, src); - } - - src = ma_find_char(src, ':', &colonPos); - if (src == NULL) { - return -1; /* Couldn't find a colon */ - } - - dev = ma_find_char(src, ',', &commaPos); - if (dev == NULL) { - dev = "0"; - ma_strncpy_s(card, sizeof(card), src+6, (size_t)-1); /* +6 = ":CARD=" */ - } else { - dev = dev + 5; /* +5 = ",DEV=" */ - ma_strncpy_s(card, sizeof(card), src+6, commaPos-6); /* +6 = ":CARD=" */ - } - - cardIndex = ((ma_snd_card_get_index_proc)pContext->alsa.snd_card_get_index)(card); - if (cardIndex < 0) { - return -2; /* Failed to retrieve the card index. */ - } - - - /* Construction. */ - dst[0] = 'h'; dst[1] = 'w'; dst[2] = ':'; - if (ma_itoa_s(cardIndex, dst+3, dstSize-3, 10) != 0) { - return -3; - } - if (ma_strcat_s(dst, dstSize, ",") != 0) { - return -3; - } - if (ma_strcat_s(dst, dstSize, dev) != 0) { - return -3; - } - - return 0; -} - -static ma_bool32 ma_does_id_exist_in_list__alsa(ma_device_id* pUniqueIDs, ma_uint32 count, const char* pHWID) -{ - ma_uint32 i; - - MA_ASSERT(pHWID != NULL); - - for (i = 0; i < count; ++i) { - if (ma_strcmp(pUniqueIDs[i].alsa, pHWID) == 0) { - return MA_TRUE; - } - } - - return MA_FALSE; -} - - -static ma_result ma_context_open_pcm__alsa(ma_context* pContext, ma_share_mode shareMode, ma_device_type deviceType, const ma_device_id* pDeviceID, int openMode, ma_snd_pcm_t** ppPCM) -{ - ma_snd_pcm_t* pPCM; - ma_snd_pcm_stream_t stream; - - MA_ASSERT(pContext != NULL); - MA_ASSERT(ppPCM != NULL); - - *ppPCM = NULL; - pPCM = NULL; - - stream = (deviceType == ma_device_type_playback) ? MA_SND_PCM_STREAM_PLAYBACK : MA_SND_PCM_STREAM_CAPTURE; - - if (pDeviceID == NULL) { - ma_bool32 isDeviceOpen; - size_t i; - - /* - We're opening the default device. I don't know if trying anything other than "default" is necessary, but it makes - me feel better to try as hard as we can get to get _something_ working. - */ - const char* defaultDeviceNames[] = { - "default", - NULL, - NULL, - NULL, - NULL, - NULL, - NULL - }; - - if (shareMode == ma_share_mode_exclusive) { - defaultDeviceNames[1] = "hw"; - defaultDeviceNames[2] = "hw:0"; - defaultDeviceNames[3] = "hw:0,0"; - } else { - if (deviceType == ma_device_type_playback) { - defaultDeviceNames[1] = "dmix"; - defaultDeviceNames[2] = "dmix:0"; - defaultDeviceNames[3] = "dmix:0,0"; - } else { - defaultDeviceNames[1] = "dsnoop"; - defaultDeviceNames[2] = "dsnoop:0"; - defaultDeviceNames[3] = "dsnoop:0,0"; - } - defaultDeviceNames[4] = "hw"; - defaultDeviceNames[5] = "hw:0"; - defaultDeviceNames[6] = "hw:0,0"; - } - - isDeviceOpen = MA_FALSE; - for (i = 0; i < ma_countof(defaultDeviceNames); ++i) { - if (defaultDeviceNames[i] != NULL && defaultDeviceNames[i][0] != '\0') { - if (((ma_snd_pcm_open_proc)pContext->alsa.snd_pcm_open)(&pPCM, defaultDeviceNames[i], stream, openMode) == 0) { - isDeviceOpen = MA_TRUE; - break; - } - } - } - - if (!isDeviceOpen) { - ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[ALSA] snd_pcm_open() failed when trying to open an appropriate default device."); - return MA_FAILED_TO_OPEN_BACKEND_DEVICE; - } - } else { - /* - We're trying to open a specific device. There's a few things to consider here: - - miniaudio recognizes a special format of device id that excludes the "hw", "dmix", etc. prefix. It looks like this: ":0,0", ":0,1", etc. When - an ID of this format is specified, it indicates to miniaudio that it can try different combinations of plugins ("hw", "dmix", etc.) until it - finds an appropriate one that works. This comes in very handy when trying to open a device in shared mode ("dmix"), vs exclusive mode ("hw"). - */ - - /* May end up needing to make small adjustments to the ID, so make a copy. */ - ma_device_id deviceID = *pDeviceID; - int resultALSA = -ENODEV; - - if (deviceID.alsa[0] != ':') { - /* The ID is not in ":0,0" format. Use the ID exactly as-is. */ - resultALSA = ((ma_snd_pcm_open_proc)pContext->alsa.snd_pcm_open)(&pPCM, deviceID.alsa, stream, openMode); - } else { - char hwid[256]; - - /* The ID is in ":0,0" format. Try different plugins depending on the shared mode. */ - if (deviceID.alsa[1] == '\0') { - deviceID.alsa[0] = '\0'; /* An ID of ":" should be converted to "". */ - } - - if (shareMode == ma_share_mode_shared) { - if (deviceType == ma_device_type_playback) { - ma_strcpy_s(hwid, sizeof(hwid), "dmix"); - } else { - ma_strcpy_s(hwid, sizeof(hwid), "dsnoop"); - } - - if (ma_strcat_s(hwid, sizeof(hwid), deviceID.alsa) == 0) { - resultALSA = ((ma_snd_pcm_open_proc)pContext->alsa.snd_pcm_open)(&pPCM, hwid, stream, openMode); - } - } - - /* If at this point we still don't have an open device it means we're either preferencing exclusive mode or opening with "dmix"/"dsnoop" failed. */ - if (resultALSA != 0) { - ma_strcpy_s(hwid, sizeof(hwid), "hw"); - if (ma_strcat_s(hwid, sizeof(hwid), deviceID.alsa) == 0) { - resultALSA = ((ma_snd_pcm_open_proc)pContext->alsa.snd_pcm_open)(&pPCM, hwid, stream, openMode); - } - } - } - - if (resultALSA < 0) { - ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[ALSA] snd_pcm_open() failed."); - return ma_result_from_errno(-resultALSA); - } - } - - *ppPCM = pPCM; - return MA_SUCCESS; -} - - -static ma_result ma_context_enumerate_devices__alsa(ma_context* pContext, ma_enum_devices_callback_proc callback, void* pUserData) -{ - int resultALSA; - ma_bool32 cbResult = MA_TRUE; - char** ppDeviceHints; - ma_device_id* pUniqueIDs = NULL; - ma_uint32 uniqueIDCount = 0; - char** ppNextDeviceHint; - - MA_ASSERT(pContext != NULL); - MA_ASSERT(callback != NULL); - - ma_mutex_lock(&pContext->alsa.internalDeviceEnumLock); - - resultALSA = ((ma_snd_device_name_hint_proc)pContext->alsa.snd_device_name_hint)(-1, "pcm", (void***)&ppDeviceHints); - if (resultALSA < 0) { - ma_mutex_unlock(&pContext->alsa.internalDeviceEnumLock); - return ma_result_from_errno(-resultALSA); - } - - ppNextDeviceHint = ppDeviceHints; - while (*ppNextDeviceHint != NULL) { - char* NAME = ((ma_snd_device_name_get_hint_proc)pContext->alsa.snd_device_name_get_hint)(*ppNextDeviceHint, "NAME"); - char* DESC = ((ma_snd_device_name_get_hint_proc)pContext->alsa.snd_device_name_get_hint)(*ppNextDeviceHint, "DESC"); - char* IOID = ((ma_snd_device_name_get_hint_proc)pContext->alsa.snd_device_name_get_hint)(*ppNextDeviceHint, "IOID"); - ma_device_type deviceType = ma_device_type_playback; - ma_bool32 stopEnumeration = MA_FALSE; - char hwid[sizeof(pUniqueIDs->alsa)]; - ma_device_info deviceInfo; - - if ((IOID == NULL || ma_strcmp(IOID, "Output") == 0)) { - deviceType = ma_device_type_playback; - } - if ((IOID != NULL && ma_strcmp(IOID, "Input" ) == 0)) { - deviceType = ma_device_type_capture; - } - - if (NAME != NULL) { - if (pContext->alsa.useVerboseDeviceEnumeration) { - /* Verbose mode. Use the name exactly as-is. */ - ma_strncpy_s(hwid, sizeof(hwid), NAME, (size_t)-1); - } else { - /* Simplified mode. Use ":%d,%d" format. */ - if (ma_convert_device_name_to_hw_format__alsa(pContext, hwid, sizeof(hwid), NAME) == 0) { - /* - At this point, hwid looks like "hw:0,0". In simplified enumeration mode, we actually want to strip off the - plugin name so it looks like ":0,0". The reason for this is that this special format is detected at device - initialization time and is used as an indicator to try to use the most appropriate plugin depending on the - device type and sharing mode. - */ - char* dst = hwid; - char* src = hwid+2; - while ((*dst++ = *src++)); - } else { - /* Conversion to "hw:%d,%d" failed. Just use the name as-is. */ - ma_strncpy_s(hwid, sizeof(hwid), NAME, (size_t)-1); - } - - if (ma_does_id_exist_in_list__alsa(pUniqueIDs, uniqueIDCount, hwid)) { - goto next_device; /* The device has already been enumerated. Move on to the next one. */ - } else { - /* The device has not yet been enumerated. Make sure it's added to our list so that it's not enumerated again. */ - size_t newCapacity = sizeof(*pUniqueIDs) * (uniqueIDCount + 1); - ma_device_id* pNewUniqueIDs = (ma_device_id*)ma_realloc(pUniqueIDs, newCapacity, &pContext->allocationCallbacks); - if (pNewUniqueIDs == NULL) { - goto next_device; /* Failed to allocate memory. */ - } - - pUniqueIDs = pNewUniqueIDs; - MA_COPY_MEMORY(pUniqueIDs[uniqueIDCount].alsa, hwid, sizeof(hwid)); - uniqueIDCount += 1; - } - } - } else { - MA_ZERO_MEMORY(hwid, sizeof(hwid)); - } - - MA_ZERO_OBJECT(&deviceInfo); - ma_strncpy_s(deviceInfo.id.alsa, sizeof(deviceInfo.id.alsa), hwid, (size_t)-1); - - /* - There's no good way to determine whether or not a device is the default on Linux. We're just going to do something simple and - just use the name of "default" as the indicator. - */ - if (ma_strcmp(deviceInfo.id.alsa, "default") == 0) { - deviceInfo.isDefault = MA_TRUE; - } - - - /* - DESC is the friendly name. We treat this slightly differently depending on whether or not we are using verbose - device enumeration. In verbose mode we want to take the entire description so that the end-user can distinguish - between the subdevices of each card/dev pair. In simplified mode, however, we only want the first part of the - description. - - The value in DESC seems to be split into two lines, with the first line being the name of the device and the - second line being a description of the device. I don't like having the description be across two lines because - it makes formatting ugly and annoying. I'm therefore deciding to put it all on a single line with the second line - being put into parentheses. In simplified mode I'm just stripping the second line entirely. - */ - if (DESC != NULL) { - int lfPos; - const char* line2 = ma_find_char(DESC, '\n', &lfPos); - if (line2 != NULL) { - line2 += 1; /* Skip past the new-line character. */ - - if (pContext->alsa.useVerboseDeviceEnumeration) { - /* Verbose mode. Put the second line in brackets. */ - ma_strncpy_s(deviceInfo.name, sizeof(deviceInfo.name), DESC, lfPos); - ma_strcat_s (deviceInfo.name, sizeof(deviceInfo.name), " ("); - ma_strcat_s (deviceInfo.name, sizeof(deviceInfo.name), line2); - ma_strcat_s (deviceInfo.name, sizeof(deviceInfo.name), ")"); - } else { - /* Simplified mode. Strip the second line entirely. */ - ma_strncpy_s(deviceInfo.name, sizeof(deviceInfo.name), DESC, lfPos); - } - } else { - /* There's no second line. Just copy the whole description. */ - ma_strncpy_s(deviceInfo.name, sizeof(deviceInfo.name), DESC, (size_t)-1); - } - } - - if (!ma_is_device_blacklisted__alsa(deviceType, NAME)) { - cbResult = callback(pContext, deviceType, &deviceInfo, pUserData); - } - - /* - Some devices are both playback and capture, but they are only enumerated by ALSA once. We need to fire the callback - again for the other device type in this case. We do this for known devices and where the IOID hint is NULL, which - means both Input and Output. - */ - if (cbResult) { - if (ma_is_common_device_name__alsa(NAME) || IOID == NULL) { - if (deviceType == ma_device_type_playback) { - if (!ma_is_capture_device_blacklisted__alsa(NAME)) { - cbResult = callback(pContext, ma_device_type_capture, &deviceInfo, pUserData); - } - } else { - if (!ma_is_playback_device_blacklisted__alsa(NAME)) { - cbResult = callback(pContext, ma_device_type_playback, &deviceInfo, pUserData); - } - } - } - } - - if (cbResult == MA_FALSE) { - stopEnumeration = MA_TRUE; - } - - next_device: - free(NAME); - free(DESC); - free(IOID); - ppNextDeviceHint += 1; - - /* We need to stop enumeration if the callback returned false. */ - if (stopEnumeration) { - break; - } - } - - ma_free(pUniqueIDs, &pContext->allocationCallbacks); - ((ma_snd_device_name_free_hint_proc)pContext->alsa.snd_device_name_free_hint)((void**)ppDeviceHints); - - ma_mutex_unlock(&pContext->alsa.internalDeviceEnumLock); - - return MA_SUCCESS; -} - - -typedef struct -{ - ma_device_type deviceType; - const ma_device_id* pDeviceID; - ma_share_mode shareMode; - ma_device_info* pDeviceInfo; - ma_bool32 foundDevice; -} ma_context_get_device_info_enum_callback_data__alsa; - -static ma_bool32 ma_context_get_device_info_enum_callback__alsa(ma_context* pContext, ma_device_type deviceType, const ma_device_info* pDeviceInfo, void* pUserData) -{ - ma_context_get_device_info_enum_callback_data__alsa* pData = (ma_context_get_device_info_enum_callback_data__alsa*)pUserData; - MA_ASSERT(pData != NULL); - - (void)pContext; - - if (pData->pDeviceID == NULL && ma_strcmp(pDeviceInfo->id.alsa, "default") == 0) { - ma_strncpy_s(pData->pDeviceInfo->name, sizeof(pData->pDeviceInfo->name), pDeviceInfo->name, (size_t)-1); - pData->foundDevice = MA_TRUE; - } else { - if (pData->deviceType == deviceType && (pData->pDeviceID != NULL && ma_strcmp(pData->pDeviceID->alsa, pDeviceInfo->id.alsa) == 0)) { - ma_strncpy_s(pData->pDeviceInfo->name, sizeof(pData->pDeviceInfo->name), pDeviceInfo->name, (size_t)-1); - pData->foundDevice = MA_TRUE; - } - } - - /* Keep enumerating until we have found the device. */ - return !pData->foundDevice; -} - -static void ma_context_test_rate_and_add_native_data_format__alsa(ma_context* pContext, ma_snd_pcm_t* pPCM, ma_snd_pcm_hw_params_t* pHWParams, ma_format format, ma_uint32 channels, ma_uint32 sampleRate, ma_uint32 flags, ma_device_info* pDeviceInfo) -{ - MA_ASSERT(pPCM != NULL); - MA_ASSERT(pHWParams != NULL); - MA_ASSERT(pDeviceInfo != NULL); - - if (pDeviceInfo->nativeDataFormatCount < ma_countof(pDeviceInfo->nativeDataFormats) && ((ma_snd_pcm_hw_params_test_rate_proc)pContext->alsa.snd_pcm_hw_params_test_rate)(pPCM, pHWParams, sampleRate, 0) == 0) { - pDeviceInfo->nativeDataFormats[pDeviceInfo->nativeDataFormatCount].format = format; - pDeviceInfo->nativeDataFormats[pDeviceInfo->nativeDataFormatCount].channels = channels; - pDeviceInfo->nativeDataFormats[pDeviceInfo->nativeDataFormatCount].sampleRate = sampleRate; - pDeviceInfo->nativeDataFormats[pDeviceInfo->nativeDataFormatCount].flags = flags; - pDeviceInfo->nativeDataFormatCount += 1; - } -} - -static void ma_context_iterate_rates_and_add_native_data_format__alsa(ma_context* pContext, ma_snd_pcm_t* pPCM, ma_snd_pcm_hw_params_t* pHWParams, ma_format format, ma_uint32 channels, ma_uint32 flags, ma_device_info* pDeviceInfo) -{ - ma_uint32 iSampleRate; - unsigned int minSampleRate; - unsigned int maxSampleRate; - int sampleRateDir; /* Not used. Just passed into snd_pcm_hw_params_get_rate_min/max(). */ - - /* There could be a range. */ - ((ma_snd_pcm_hw_params_get_rate_min_proc)pContext->alsa.snd_pcm_hw_params_get_rate_min)(pHWParams, &minSampleRate, &sampleRateDir); - ((ma_snd_pcm_hw_params_get_rate_max_proc)pContext->alsa.snd_pcm_hw_params_get_rate_max)(pHWParams, &maxSampleRate, &sampleRateDir); - - /* Make sure our sample rates are clamped to sane values. Stupid devices like "pulse" will reports rates like "1" which is ridiculous. */ - minSampleRate = ma_clamp(minSampleRate, (unsigned int)ma_standard_sample_rate_min, (unsigned int)ma_standard_sample_rate_max); - maxSampleRate = ma_clamp(maxSampleRate, (unsigned int)ma_standard_sample_rate_min, (unsigned int)ma_standard_sample_rate_max); - - for (iSampleRate = 0; iSampleRate < ma_countof(g_maStandardSampleRatePriorities); iSampleRate += 1) { - ma_uint32 standardSampleRate = g_maStandardSampleRatePriorities[iSampleRate]; - - if (standardSampleRate >= minSampleRate && standardSampleRate <= maxSampleRate) { - ma_context_test_rate_and_add_native_data_format__alsa(pContext, pPCM, pHWParams, format, channels, standardSampleRate, flags, pDeviceInfo); - } - } - - /* Now make sure our min and max rates are included just in case they aren't in the range of our standard rates. */ - if (!ma_is_standard_sample_rate(minSampleRate)) { - ma_context_test_rate_and_add_native_data_format__alsa(pContext, pPCM, pHWParams, format, channels, minSampleRate, flags, pDeviceInfo); - } - - if (!ma_is_standard_sample_rate(maxSampleRate) && maxSampleRate != minSampleRate) { - ma_context_test_rate_and_add_native_data_format__alsa(pContext, pPCM, pHWParams, format, channels, maxSampleRate, flags, pDeviceInfo); - } -} - -static ma_result ma_context_get_device_info__alsa(ma_context* pContext, ma_device_type deviceType, const ma_device_id* pDeviceID, ma_device_info* pDeviceInfo) -{ - ma_context_get_device_info_enum_callback_data__alsa data; - ma_result result; - int resultALSA; - ma_snd_pcm_t* pPCM; - ma_snd_pcm_hw_params_t* pHWParams; - ma_uint32 iFormat; - ma_uint32 iChannel; - - MA_ASSERT(pContext != NULL); - - /* We just enumerate to find basic information about the device. */ - data.deviceType = deviceType; - data.pDeviceID = pDeviceID; - data.pDeviceInfo = pDeviceInfo; - data.foundDevice = MA_FALSE; - result = ma_context_enumerate_devices__alsa(pContext, ma_context_get_device_info_enum_callback__alsa, &data); - if (result != MA_SUCCESS) { - return result; - } - - if (!data.foundDevice) { - return MA_NO_DEVICE; - } - - if (ma_strcmp(pDeviceInfo->id.alsa, "default") == 0) { - pDeviceInfo->isDefault = MA_TRUE; - } - - /* For detailed info we need to open the device. */ - result = ma_context_open_pcm__alsa(pContext, ma_share_mode_shared, deviceType, pDeviceID, 0, &pPCM); - if (result != MA_SUCCESS) { - return result; - } - - /* We need to initialize a HW parameters object in order to know what formats are supported. */ - pHWParams = (ma_snd_pcm_hw_params_t*)ma_calloc(((ma_snd_pcm_hw_params_sizeof_proc)pContext->alsa.snd_pcm_hw_params_sizeof)(), &pContext->allocationCallbacks); - if (pHWParams == NULL) { - ((ma_snd_pcm_close_proc)pContext->alsa.snd_pcm_close)(pPCM); - return MA_OUT_OF_MEMORY; - } - - resultALSA = ((ma_snd_pcm_hw_params_any_proc)pContext->alsa.snd_pcm_hw_params_any)(pPCM, pHWParams); - if (resultALSA < 0) { - ma_free(pHWParams, &pContext->allocationCallbacks); - ((ma_snd_pcm_close_proc)pContext->alsa.snd_pcm_close)(pPCM); - ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[ALSA] Failed to initialize hardware parameters. snd_pcm_hw_params_any() failed."); - return ma_result_from_errno(-resultALSA); - } - - /* - Some ALSA devices can support many permutations of formats, channels and rates. We only support - a fixed number of permutations which means we need to employ some strategies to ensure the best - combinations are returned. An example is the "pulse" device which can do its own data conversion - in software and as a result can support any combination of format, channels and rate. - - We want to ensure that the first data formats are the best. We have a list of favored sample - formats and sample rates, so these will be the basis of our iteration. - */ - - /* Formats. We just iterate over our standard formats and test them, making sure we reset the configuration space each iteration. */ - for (iFormat = 0; iFormat < ma_countof(g_maFormatPriorities); iFormat += 1) { - ma_format format = g_maFormatPriorities[iFormat]; - - /* - For each format we need to make sure we reset the configuration space so we don't return - channel counts and rates that aren't compatible with a format. - */ - ((ma_snd_pcm_hw_params_any_proc)pContext->alsa.snd_pcm_hw_params_any)(pPCM, pHWParams); - - /* Test the format first. If this fails it means the format is not supported and we can skip it. */ - if (((ma_snd_pcm_hw_params_test_format_proc)pContext->alsa.snd_pcm_hw_params_test_format)(pPCM, pHWParams, ma_convert_ma_format_to_alsa_format(format)) == 0) { - /* The format is supported. */ - unsigned int minChannels; - unsigned int maxChannels; - - /* - The configuration space needs to be restricted to this format so we can get an accurate - picture of which sample rates and channel counts are support with this format. - */ - ((ma_snd_pcm_hw_params_set_format_proc)pContext->alsa.snd_pcm_hw_params_set_format)(pPCM, pHWParams, ma_convert_ma_format_to_alsa_format(format)); - - /* Now we need to check for supported channels. */ - ((ma_snd_pcm_hw_params_get_channels_min_proc)pContext->alsa.snd_pcm_hw_params_get_channels_min)(pHWParams, &minChannels); - ((ma_snd_pcm_hw_params_get_channels_max_proc)pContext->alsa.snd_pcm_hw_params_get_channels_max)(pHWParams, &maxChannels); - - if (minChannels > MA_MAX_CHANNELS) { - continue; /* Too many channels. */ - } - if (maxChannels < MA_MIN_CHANNELS) { - continue; /* Not enough channels. */ - } - - /* - Make sure the channel count is clamped. This is mainly intended for the max channels - because some devices can report an unbound maximum. - */ - minChannels = ma_clamp(minChannels, MA_MIN_CHANNELS, MA_MAX_CHANNELS); - maxChannels = ma_clamp(maxChannels, MA_MIN_CHANNELS, MA_MAX_CHANNELS); - - if (minChannels == MA_MIN_CHANNELS && maxChannels == MA_MAX_CHANNELS) { - /* The device supports all channels. Don't iterate over every single one. Instead just set the channels to 0 which means all channels are supported. */ - ma_context_iterate_rates_and_add_native_data_format__alsa(pContext, pPCM, pHWParams, format, 0, 0, pDeviceInfo); /* Intentionally setting the channel count to 0 as that means all channels are supported. */ - } else { - /* The device only supports a specific set of channels. We need to iterate over all of them. */ - for (iChannel = minChannels; iChannel <= maxChannels; iChannel += 1) { - /* Test the channel before applying it to the configuration space. */ - unsigned int channels = iChannel; - - /* Make sure our channel range is reset before testing again or else we'll always fail the test. */ - ((ma_snd_pcm_hw_params_any_proc)pContext->alsa.snd_pcm_hw_params_any)(pPCM, pHWParams); - ((ma_snd_pcm_hw_params_set_format_proc)pContext->alsa.snd_pcm_hw_params_set_format)(pPCM, pHWParams, ma_convert_ma_format_to_alsa_format(format)); - - if (((ma_snd_pcm_hw_params_test_channels_proc)pContext->alsa.snd_pcm_hw_params_test_channels)(pPCM, pHWParams, channels) == 0) { - /* The channel count is supported. */ - - /* The configuration space now needs to be restricted to the channel count before extracting the sample rate. */ - ((ma_snd_pcm_hw_params_set_channels_proc)pContext->alsa.snd_pcm_hw_params_set_channels)(pPCM, pHWParams, channels); - - /* Only after the configuration space has been restricted to the specific channel count should we iterate over our sample rates. */ - ma_context_iterate_rates_and_add_native_data_format__alsa(pContext, pPCM, pHWParams, format, channels, 0, pDeviceInfo); - } else { - /* The channel count is not supported. Skip. */ - } - } - } - } else { - /* The format is not supported. Skip. */ - } - } - - ma_free(pHWParams, &pContext->allocationCallbacks); - - ((ma_snd_pcm_close_proc)pContext->alsa.snd_pcm_close)(pPCM); - return MA_SUCCESS; -} - -static ma_result ma_device_uninit__alsa(ma_device* pDevice) -{ - MA_ASSERT(pDevice != NULL); - - if ((ma_snd_pcm_t*)pDevice->alsa.pPCMCapture) { - ((ma_snd_pcm_close_proc)pDevice->pContext->alsa.snd_pcm_close)((ma_snd_pcm_t*)pDevice->alsa.pPCMCapture); - close(pDevice->alsa.wakeupfdCapture); - ma_free(pDevice->alsa.pPollDescriptorsCapture, &pDevice->pContext->allocationCallbacks); - } - - if ((ma_snd_pcm_t*)pDevice->alsa.pPCMPlayback) { - ((ma_snd_pcm_close_proc)pDevice->pContext->alsa.snd_pcm_close)((ma_snd_pcm_t*)pDevice->alsa.pPCMPlayback); - close(pDevice->alsa.wakeupfdPlayback); - ma_free(pDevice->alsa.pPollDescriptorsPlayback, &pDevice->pContext->allocationCallbacks); - } - - return MA_SUCCESS; -} - -static ma_result ma_device_init_by_type__alsa(ma_device* pDevice, const ma_device_config* pConfig, ma_device_descriptor* pDescriptor, ma_device_type deviceType) -{ - ma_result result; - int resultALSA; - ma_snd_pcm_t* pPCM; - ma_bool32 isUsingMMap; - ma_snd_pcm_format_t formatALSA; - ma_format internalFormat; - ma_uint32 internalChannels; - ma_uint32 internalSampleRate; - ma_channel internalChannelMap[MA_MAX_CHANNELS]; - ma_uint32 internalPeriodSizeInFrames; - ma_uint32 internalPeriods; - int openMode; - ma_snd_pcm_hw_params_t* pHWParams; - ma_snd_pcm_sw_params_t* pSWParams; - ma_snd_pcm_uframes_t bufferBoundary; - int pollDescriptorCount; - struct pollfd* pPollDescriptors; - int wakeupfd; - - MA_ASSERT(pConfig != NULL); - MA_ASSERT(deviceType != ma_device_type_duplex); /* This function should only be called for playback _or_ capture, never duplex. */ - MA_ASSERT(pDevice != NULL); - - formatALSA = ma_convert_ma_format_to_alsa_format(pDescriptor->format); - - openMode = 0; - if (pConfig->alsa.noAutoResample) { - openMode |= MA_SND_PCM_NO_AUTO_RESAMPLE; - } - if (pConfig->alsa.noAutoChannels) { - openMode |= MA_SND_PCM_NO_AUTO_CHANNELS; - } - if (pConfig->alsa.noAutoFormat) { - openMode |= MA_SND_PCM_NO_AUTO_FORMAT; - } - - result = ma_context_open_pcm__alsa(pDevice->pContext, pDescriptor->shareMode, deviceType, pDescriptor->pDeviceID, openMode, &pPCM); - if (result != MA_SUCCESS) { - return result; - } - - - /* Hardware parameters. */ - pHWParams = (ma_snd_pcm_hw_params_t*)ma_calloc(((ma_snd_pcm_hw_params_sizeof_proc)pDevice->pContext->alsa.snd_pcm_hw_params_sizeof)(), &pDevice->pContext->allocationCallbacks); - if (pHWParams == NULL) { - ((ma_snd_pcm_close_proc)pDevice->pContext->alsa.snd_pcm_close)(pPCM); - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[ALSA] Failed to allocate memory for hardware parameters."); - return MA_OUT_OF_MEMORY; - } - - resultALSA = ((ma_snd_pcm_hw_params_any_proc)pDevice->pContext->alsa.snd_pcm_hw_params_any)(pPCM, pHWParams); - if (resultALSA < 0) { - ma_free(pHWParams, &pDevice->pContext->allocationCallbacks); - ((ma_snd_pcm_close_proc)pDevice->pContext->alsa.snd_pcm_close)(pPCM); - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[ALSA] Failed to initialize hardware parameters. snd_pcm_hw_params_any() failed."); - return ma_result_from_errno(-resultALSA); - } - - /* MMAP Mode. Try using interleaved MMAP access. If this fails, fall back to standard readi/writei. */ - isUsingMMap = MA_FALSE; -#if 0 /* NOTE: MMAP mode temporarily disabled. */ - if (deviceType != ma_device_type_capture) { /* <-- Disabling MMAP mode for capture devices because I apparently do not have a device that supports it which means I can't test it... Contributions welcome. */ - if (!pConfig->alsa.noMMap) { - if (((ma_snd_pcm_hw_params_set_access_proc)pDevice->pContext->alsa.snd_pcm_hw_params_set_access)(pPCM, pHWParams, MA_SND_PCM_ACCESS_MMAP_INTERLEAVED) == 0) { - pDevice->alsa.isUsingMMap = MA_TRUE; - } - } - } -#endif - - if (!isUsingMMap) { - resultALSA = ((ma_snd_pcm_hw_params_set_access_proc)pDevice->pContext->alsa.snd_pcm_hw_params_set_access)(pPCM, pHWParams, MA_SND_PCM_ACCESS_RW_INTERLEAVED); - if (resultALSA < 0) { - ma_free(pHWParams, &pDevice->pContext->allocationCallbacks); - ((ma_snd_pcm_close_proc)pDevice->pContext->alsa.snd_pcm_close)(pPCM); - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[ALSA] Failed to set access mode to neither SND_PCM_ACCESS_MMAP_INTERLEAVED nor SND_PCM_ACCESS_RW_INTERLEAVED. snd_pcm_hw_params_set_access() failed."); - return ma_result_from_errno(-resultALSA); - } - } - - /* - Most important properties first. The documentation for OSS (yes, I know this is ALSA!) recommends format, channels, then sample rate. I can't - find any documentation for ALSA specifically, so I'm going to copy the recommendation for OSS. - */ - - /* Format. */ - { - /* - At this point we should have a list of supported formats, so now we need to find the best one. We first check if the requested format is - supported, and if so, use that one. If it's not supported, we just run though a list of formats and try to find the best one. - */ - if (formatALSA == MA_SND_PCM_FORMAT_UNKNOWN || ((ma_snd_pcm_hw_params_test_format_proc)pDevice->pContext->alsa.snd_pcm_hw_params_test_format)(pPCM, pHWParams, formatALSA) != 0) { - /* We're either requesting the native format or the specified format is not supported. */ - size_t iFormat; - - formatALSA = MA_SND_PCM_FORMAT_UNKNOWN; - for (iFormat = 0; iFormat < ma_countof(g_maFormatPriorities); ++iFormat) { - if (((ma_snd_pcm_hw_params_test_format_proc)pDevice->pContext->alsa.snd_pcm_hw_params_test_format)(pPCM, pHWParams, ma_convert_ma_format_to_alsa_format(g_maFormatPriorities[iFormat])) == 0) { - formatALSA = ma_convert_ma_format_to_alsa_format(g_maFormatPriorities[iFormat]); - break; - } - } - - if (formatALSA == MA_SND_PCM_FORMAT_UNKNOWN) { - ma_free(pHWParams, &pDevice->pContext->allocationCallbacks); - ((ma_snd_pcm_close_proc)pDevice->pContext->alsa.snd_pcm_close)(pPCM); - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[ALSA] Format not supported. The device does not support any miniaudio formats."); - return MA_FORMAT_NOT_SUPPORTED; - } - } - - resultALSA = ((ma_snd_pcm_hw_params_set_format_proc)pDevice->pContext->alsa.snd_pcm_hw_params_set_format)(pPCM, pHWParams, formatALSA); - if (resultALSA < 0) { - ma_free(pHWParams, &pDevice->pContext->allocationCallbacks); - ((ma_snd_pcm_close_proc)pDevice->pContext->alsa.snd_pcm_close)(pPCM); - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[ALSA] Format not supported. snd_pcm_hw_params_set_format() failed."); - return ma_result_from_errno(-resultALSA); - } - - internalFormat = ma_format_from_alsa(formatALSA); - if (internalFormat == ma_format_unknown) { - ma_free(pHWParams, &pDevice->pContext->allocationCallbacks); - ((ma_snd_pcm_close_proc)pDevice->pContext->alsa.snd_pcm_close)(pPCM); - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[ALSA] The chosen format is not supported by miniaudio."); - return MA_FORMAT_NOT_SUPPORTED; - } - } - - /* Channels. */ - { - unsigned int channels = pDescriptor->channels; - if (channels == 0) { - channels = MA_DEFAULT_CHANNELS; - } - - resultALSA = ((ma_snd_pcm_hw_params_set_channels_near_proc)pDevice->pContext->alsa.snd_pcm_hw_params_set_channels_near)(pPCM, pHWParams, &channels); - if (resultALSA < 0) { - ma_free(pHWParams, &pDevice->pContext->allocationCallbacks); - ((ma_snd_pcm_close_proc)pDevice->pContext->alsa.snd_pcm_close)(pPCM); - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[ALSA] Failed to set channel count. snd_pcm_hw_params_set_channels_near() failed."); - return ma_result_from_errno(-resultALSA); - } - - internalChannels = (ma_uint32)channels; - } - - /* Sample Rate */ - { - unsigned int sampleRate; - - /* - It appears there's either a bug in ALSA, a bug in some drivers, or I'm doing something silly; but having resampling enabled causes - problems with some device configurations when used in conjunction with MMAP access mode. To fix this problem we need to disable - resampling. - - To reproduce this problem, open the "plug:dmix" device, and set the sample rate to 44100. Internally, it looks like dmix uses a - sample rate of 48000. The hardware parameters will get set correctly with no errors, but it looks like the 44100 -> 48000 resampling - doesn't work properly - but only with MMAP access mode. You will notice skipping/crackling in the audio, and it'll run at a slightly - faster rate. - - miniaudio has built-in support for sample rate conversion (albeit low quality at the moment), so disabling resampling should be fine - for us. The only problem is that it won't be taking advantage of any kind of hardware-accelerated resampling and it won't be very - good quality until I get a chance to improve the quality of miniaudio's software sample rate conversion. - - I don't currently know if the dmix plugin is the only one with this error. Indeed, this is the only one I've been able to reproduce - this error with. In the future, we may want to restrict the disabling of resampling to only known bad plugins. - */ - ((ma_snd_pcm_hw_params_set_rate_resample_proc)pDevice->pContext->alsa.snd_pcm_hw_params_set_rate_resample)(pPCM, pHWParams, 0); - - sampleRate = pDescriptor->sampleRate; - if (sampleRate == 0) { - sampleRate = MA_DEFAULT_SAMPLE_RATE; - } - - resultALSA = ((ma_snd_pcm_hw_params_set_rate_near_proc)pDevice->pContext->alsa.snd_pcm_hw_params_set_rate_near)(pPCM, pHWParams, &sampleRate, 0); - if (resultALSA < 0) { - ma_free(pHWParams, &pDevice->pContext->allocationCallbacks); - ((ma_snd_pcm_close_proc)pDevice->pContext->alsa.snd_pcm_close)(pPCM); - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[ALSA] Sample rate not supported. snd_pcm_hw_params_set_rate_near() failed."); - return ma_result_from_errno(-resultALSA); - } - - internalSampleRate = (ma_uint32)sampleRate; - } - - /* Periods. */ - { - ma_uint32 periods = pDescriptor->periodCount; - - resultALSA = ((ma_snd_pcm_hw_params_set_periods_near_proc)pDevice->pContext->alsa.snd_pcm_hw_params_set_periods_near)(pPCM, pHWParams, &periods, NULL); - if (resultALSA < 0) { - ma_free(pHWParams, &pDevice->pContext->allocationCallbacks); - ((ma_snd_pcm_close_proc)pDevice->pContext->alsa.snd_pcm_close)(pPCM); - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[ALSA] Failed to set period count. snd_pcm_hw_params_set_periods_near() failed."); - return ma_result_from_errno(-resultALSA); - } - - internalPeriods = periods; - } - - /* Buffer Size */ - { - ma_snd_pcm_uframes_t actualBufferSizeInFrames = ma_calculate_buffer_size_in_frames_from_descriptor(pDescriptor, internalSampleRate, pConfig->performanceProfile) * internalPeriods; - - resultALSA = ((ma_snd_pcm_hw_params_set_buffer_size_near_proc)pDevice->pContext->alsa.snd_pcm_hw_params_set_buffer_size_near)(pPCM, pHWParams, &actualBufferSizeInFrames); - if (resultALSA < 0) { - ma_free(pHWParams, &pDevice->pContext->allocationCallbacks); - ((ma_snd_pcm_close_proc)pDevice->pContext->alsa.snd_pcm_close)(pPCM); - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[ALSA] Failed to set buffer size for device. snd_pcm_hw_params_set_buffer_size() failed."); - return ma_result_from_errno(-resultALSA); - } - - internalPeriodSizeInFrames = actualBufferSizeInFrames / internalPeriods; - } - - /* Apply hardware parameters. */ - resultALSA = ((ma_snd_pcm_hw_params_proc)pDevice->pContext->alsa.snd_pcm_hw_params)(pPCM, pHWParams); - if (resultALSA < 0) { - ma_free(pHWParams, &pDevice->pContext->allocationCallbacks); - ((ma_snd_pcm_close_proc)pDevice->pContext->alsa.snd_pcm_close)(pPCM); - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[ALSA] Failed to set hardware parameters. snd_pcm_hw_params() failed."); - return ma_result_from_errno(-resultALSA); - } - - ma_free(pHWParams, &pDevice->pContext->allocationCallbacks); - pHWParams = NULL; - - - /* Software parameters. */ - pSWParams = (ma_snd_pcm_sw_params_t*)ma_calloc(((ma_snd_pcm_sw_params_sizeof_proc)pDevice->pContext->alsa.snd_pcm_sw_params_sizeof)(), &pDevice->pContext->allocationCallbacks); - if (pSWParams == NULL) { - ((ma_snd_pcm_close_proc)pDevice->pContext->alsa.snd_pcm_close)(pPCM); - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[ALSA] Failed to allocate memory for software parameters."); - return MA_OUT_OF_MEMORY; - } - - resultALSA = ((ma_snd_pcm_sw_params_current_proc)pDevice->pContext->alsa.snd_pcm_sw_params_current)(pPCM, pSWParams); - if (resultALSA < 0) { - ma_free(pSWParams, &pDevice->pContext->allocationCallbacks); - ((ma_snd_pcm_close_proc)pDevice->pContext->alsa.snd_pcm_close)(pPCM); - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[ALSA] Failed to initialize software parameters. snd_pcm_sw_params_current() failed."); - return ma_result_from_errno(-resultALSA); - } - - resultALSA = ((ma_snd_pcm_sw_params_set_avail_min_proc)pDevice->pContext->alsa.snd_pcm_sw_params_set_avail_min)(pPCM, pSWParams, ma_prev_power_of_2(internalPeriodSizeInFrames)); - if (resultALSA < 0) { - ma_free(pSWParams, &pDevice->pContext->allocationCallbacks); - ((ma_snd_pcm_close_proc)pDevice->pContext->alsa.snd_pcm_close)(pPCM); - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[ALSA] snd_pcm_sw_params_set_avail_min() failed."); - return ma_result_from_errno(-resultALSA); - } - - resultALSA = ((ma_snd_pcm_sw_params_get_boundary_proc)pDevice->pContext->alsa.snd_pcm_sw_params_get_boundary)(pSWParams, &bufferBoundary); - if (resultALSA < 0) { - bufferBoundary = internalPeriodSizeInFrames * internalPeriods; - } - - if (deviceType == ma_device_type_playback && !isUsingMMap) { /* Only playback devices in writei/readi mode need a start threshold. */ - /* - Subtle detail here with the start threshold. When in playback-only mode (no full-duplex) we can set the start threshold to - the size of a period. But for full-duplex we need to set it such that it is at least two periods. - */ - resultALSA = ((ma_snd_pcm_sw_params_set_start_threshold_proc)pDevice->pContext->alsa.snd_pcm_sw_params_set_start_threshold)(pPCM, pSWParams, internalPeriodSizeInFrames*2); - if (resultALSA < 0) { - ma_free(pSWParams, &pDevice->pContext->allocationCallbacks); - ((ma_snd_pcm_close_proc)pDevice->pContext->alsa.snd_pcm_close)(pPCM); - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[ALSA] Failed to set start threshold for playback device. snd_pcm_sw_params_set_start_threshold() failed."); - return ma_result_from_errno(-resultALSA); - } - - resultALSA = ((ma_snd_pcm_sw_params_set_stop_threshold_proc)pDevice->pContext->alsa.snd_pcm_sw_params_set_stop_threshold)(pPCM, pSWParams, bufferBoundary); - if (resultALSA < 0) { /* Set to boundary to loop instead of stop in the event of an xrun. */ - ma_free(pSWParams, &pDevice->pContext->allocationCallbacks); - ((ma_snd_pcm_close_proc)pDevice->pContext->alsa.snd_pcm_close)(pPCM); - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[ALSA] Failed to set stop threshold for playback device. snd_pcm_sw_params_set_stop_threshold() failed."); - return ma_result_from_errno(-resultALSA); - } - } - - resultALSA = ((ma_snd_pcm_sw_params_proc)pDevice->pContext->alsa.snd_pcm_sw_params)(pPCM, pSWParams); - if (resultALSA < 0) { - ma_free(pSWParams, &pDevice->pContext->allocationCallbacks); - ((ma_snd_pcm_close_proc)pDevice->pContext->alsa.snd_pcm_close)(pPCM); - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[ALSA] Failed to set software parameters. snd_pcm_sw_params() failed."); - return ma_result_from_errno(-resultALSA); - } - - ma_free(pSWParams, &pDevice->pContext->allocationCallbacks); - pSWParams = NULL; - - - /* Grab the internal channel map. For now we're not going to bother trying to change the channel map and instead just do it ourselves. */ - { - ma_snd_pcm_chmap_t* pChmap = NULL; - if (pDevice->pContext->alsa.snd_pcm_get_chmap != NULL) { - pChmap = ((ma_snd_pcm_get_chmap_proc)pDevice->pContext->alsa.snd_pcm_get_chmap)(pPCM); - } - - if (pChmap != NULL) { - ma_uint32 iChannel; - - /* There are cases where the returned channel map can have a different channel count than was returned by snd_pcm_hw_params_set_channels_near(). */ - if (pChmap->channels >= internalChannels) { - /* Drop excess channels. */ - for (iChannel = 0; iChannel < internalChannels; ++iChannel) { - internalChannelMap[iChannel] = ma_convert_alsa_channel_position_to_ma_channel(pChmap->pos[iChannel]); - } - } else { - ma_uint32 i; - - /* - Excess channels use defaults. Do an initial fill with defaults, overwrite the first pChmap->channels, validate to ensure there are no duplicate - channels. If validation fails, fall back to defaults. - */ - ma_bool32 isValid = MA_TRUE; - - /* Fill with defaults. */ - ma_channel_map_init_standard(ma_standard_channel_map_alsa, internalChannelMap, ma_countof(internalChannelMap), internalChannels); - - /* Overwrite first pChmap->channels channels. */ - for (iChannel = 0; iChannel < pChmap->channels; ++iChannel) { - internalChannelMap[iChannel] = ma_convert_alsa_channel_position_to_ma_channel(pChmap->pos[iChannel]); - } - - /* Validate. */ - for (i = 0; i < internalChannels && isValid; ++i) { - ma_uint32 j; - for (j = i+1; j < internalChannels; ++j) { - if (internalChannelMap[i] == internalChannelMap[j]) { - isValid = MA_FALSE; - break; - } - } - } - - /* If our channel map is invalid, fall back to defaults. */ - if (!isValid) { - ma_channel_map_init_standard(ma_standard_channel_map_alsa, internalChannelMap, ma_countof(internalChannelMap), internalChannels); - } - } - - free(pChmap); - pChmap = NULL; - } else { - /* Could not retrieve the channel map. Fall back to a hard-coded assumption. */ - ma_channel_map_init_standard(ma_standard_channel_map_alsa, internalChannelMap, ma_countof(internalChannelMap), internalChannels); - } - } - - - /* - We need to retrieve the poll descriptors so we can use poll() to wait for data to become - available for reading or writing. There's no well defined maximum for this so we're just going - to allocate this on the heap. - */ - pollDescriptorCount = ((ma_snd_pcm_poll_descriptors_count_proc)pDevice->pContext->alsa.snd_pcm_poll_descriptors_count)(pPCM); - if (pollDescriptorCount <= 0) { - ((ma_snd_pcm_close_proc)pDevice->pContext->alsa.snd_pcm_close)(pPCM); - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[ALSA] Failed to retrieve poll descriptors count."); - return MA_ERROR; - } - - pPollDescriptors = (struct pollfd*)ma_malloc(sizeof(*pPollDescriptors) * (pollDescriptorCount + 1), &pDevice->pContext->allocationCallbacks); /* +1 because we want room for the wakeup descriptor. */ - if (pPollDescriptors == NULL) { - ((ma_snd_pcm_close_proc)pDevice->pContext->alsa.snd_pcm_close)(pPCM); - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[ALSA] Failed to allocate memory for poll descriptors."); - return MA_OUT_OF_MEMORY; - } - - /* - We need an eventfd to wakeup from poll() and avoid a deadlock in situations where the driver - never returns from writei() and readi(). This has been observed with the "pulse" device. - */ - wakeupfd = eventfd(0, 0); - if (wakeupfd < 0) { - ma_free(pPollDescriptors, &pDevice->pContext->allocationCallbacks); - ((ma_snd_pcm_close_proc)pDevice->pContext->alsa.snd_pcm_close)(pPCM); - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[ALSA] Failed to create eventfd for poll wakeup."); - return ma_result_from_errno(errno); - } - - /* We'll place the wakeup fd at the start of the buffer. */ - pPollDescriptors[0].fd = wakeupfd; - pPollDescriptors[0].events = POLLIN; /* We only care about waiting to read from the wakeup file descriptor. */ - pPollDescriptors[0].revents = 0; - - /* We can now extract the PCM poll descriptors which we place after the wakeup descriptor. */ - pollDescriptorCount = ((ma_snd_pcm_poll_descriptors_proc)pDevice->pContext->alsa.snd_pcm_poll_descriptors)(pPCM, pPollDescriptors + 1, pollDescriptorCount); /* +1 because we want to place these descriptors after the wakeup descriptor. */ - if (pollDescriptorCount <= 0) { - close(wakeupfd); - ma_free(pPollDescriptors, &pDevice->pContext->allocationCallbacks); - ((ma_snd_pcm_close_proc)pDevice->pContext->alsa.snd_pcm_close)(pPCM); - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[ALSA] Failed to retrieve poll descriptors."); - return MA_ERROR; - } - - if (deviceType == ma_device_type_capture) { - pDevice->alsa.pollDescriptorCountCapture = pollDescriptorCount; - pDevice->alsa.pPollDescriptorsCapture = pPollDescriptors; - pDevice->alsa.wakeupfdCapture = wakeupfd; - } else { - pDevice->alsa.pollDescriptorCountPlayback = pollDescriptorCount; - pDevice->alsa.pPollDescriptorsPlayback = pPollDescriptors; - pDevice->alsa.wakeupfdPlayback = wakeupfd; - } - - - /* We're done. Prepare the device. */ - resultALSA = ((ma_snd_pcm_prepare_proc)pDevice->pContext->alsa.snd_pcm_prepare)(pPCM); - if (resultALSA < 0) { - close(wakeupfd); - ma_free(pPollDescriptors, &pDevice->pContext->allocationCallbacks); - ((ma_snd_pcm_close_proc)pDevice->pContext->alsa.snd_pcm_close)(pPCM); - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[ALSA] Failed to prepare device."); - return ma_result_from_errno(-resultALSA); - } - - - if (deviceType == ma_device_type_capture) { - pDevice->alsa.pPCMCapture = (ma_ptr)pPCM; - pDevice->alsa.isUsingMMapCapture = isUsingMMap; - } else { - pDevice->alsa.pPCMPlayback = (ma_ptr)pPCM; - pDevice->alsa.isUsingMMapPlayback = isUsingMMap; - } - - pDescriptor->format = internalFormat; - pDescriptor->channels = internalChannels; - pDescriptor->sampleRate = internalSampleRate; - ma_channel_map_copy(pDescriptor->channelMap, internalChannelMap, ma_min(internalChannels, MA_MAX_CHANNELS)); - pDescriptor->periodSizeInFrames = internalPeriodSizeInFrames; - pDescriptor->periodCount = internalPeriods; - - return MA_SUCCESS; -} - -static ma_result ma_device_init__alsa(ma_device* pDevice, const ma_device_config* pConfig, ma_device_descriptor* pDescriptorPlayback, ma_device_descriptor* pDescriptorCapture) -{ - MA_ASSERT(pDevice != NULL); - - MA_ZERO_OBJECT(&pDevice->alsa); - - if (pConfig->deviceType == ma_device_type_loopback) { - return MA_DEVICE_TYPE_NOT_SUPPORTED; - } - - if (pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) { - ma_result result = ma_device_init_by_type__alsa(pDevice, pConfig, pDescriptorCapture, ma_device_type_capture); - if (result != MA_SUCCESS) { - return result; - } - } - - if (pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) { - ma_result result = ma_device_init_by_type__alsa(pDevice, pConfig, pDescriptorPlayback, ma_device_type_playback); - if (result != MA_SUCCESS) { - return result; - } - } - - return MA_SUCCESS; -} - -static ma_result ma_device_start__alsa(ma_device* pDevice) -{ - int resultALSA; - - if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) { - resultALSA = ((ma_snd_pcm_start_proc)pDevice->pContext->alsa.snd_pcm_start)((ma_snd_pcm_t*)pDevice->alsa.pPCMCapture); - if (resultALSA < 0) { - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[ALSA] Failed to start capture device."); - return ma_result_from_errno(-resultALSA); - } - } - - if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) { - /* - When data is written to the device we wait for the device to get ready to receive data with poll(). In my testing - I have observed that poll() can sometimes block forever unless the device is started explicitly with snd_pcm_start() - or some data is written with snd_pcm_writei(). - - To resolve this I've decided to do an explicit start with snd_pcm_start(). The problem with this is that the device - is started without any data in the internal buffer which will result in an immediate underrun. If instead we were - to call into snd_pcm_writei() in an attempt to prevent the underrun, we would run the risk of a weird deadlock - issue as documented inside ma_device_write__alsa(). - */ - resultALSA = ((ma_snd_pcm_start_proc)pDevice->pContext->alsa.snd_pcm_start)((ma_snd_pcm_t*)pDevice->alsa.pPCMPlayback); - if (resultALSA < 0) { - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[ALSA] Failed to start playback device."); - return ma_result_from_errno(-resultALSA); - } - } - - return MA_SUCCESS; -} - -static ma_result ma_device_stop__alsa(ma_device* pDevice) -{ - /* - The stop callback will get called on the worker thread after read/write__alsa() has returned. At this point there is - a small chance that our wakeupfd has not been cleared. We'll clear that out now if applicable. - */ - int resultPoll; - int resultRead; - - if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) { - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "[ALSA] Dropping capture device...\n"); - ((ma_snd_pcm_drop_proc)pDevice->pContext->alsa.snd_pcm_drop)((ma_snd_pcm_t*)pDevice->alsa.pPCMCapture); - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "[ALSA] Dropping capture device successful.\n"); - - /* We need to prepare the device again, otherwise we won't be able to restart the device. */ - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "[ALSA] Preparing capture device...\n"); - if (((ma_snd_pcm_prepare_proc)pDevice->pContext->alsa.snd_pcm_prepare)((ma_snd_pcm_t*)pDevice->alsa.pPCMCapture) < 0) { - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "[ALSA] Preparing capture device failed.\n"); - } else { - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "[ALSA] Preparing capture device successful.\n"); - } - - /* Clear the wakeupfd. */ - resultPoll = poll((struct pollfd*)pDevice->alsa.pPollDescriptorsCapture, 1, 0); - if (resultPoll > 0) { - ma_uint64 t; - resultRead = read(((struct pollfd*)pDevice->alsa.pPollDescriptorsCapture)[0].fd, &t, sizeof(t)); - if (resultRead != sizeof(t)) { - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "[ALSA] Failed to read from capture wakeupfd. read() = %d\n", resultRead); - } - } - } - - if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) { - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "[ALSA] Dropping playback device...\n"); - ((ma_snd_pcm_drop_proc)pDevice->pContext->alsa.snd_pcm_drop)((ma_snd_pcm_t*)pDevice->alsa.pPCMPlayback); - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "[ALSA] Dropping playback device successful.\n"); - - /* We need to prepare the device again, otherwise we won't be able to restart the device. */ - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "[ALSA] Preparing playback device...\n"); - if (((ma_snd_pcm_prepare_proc)pDevice->pContext->alsa.snd_pcm_prepare)((ma_snd_pcm_t*)pDevice->alsa.pPCMPlayback) < 0) { - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "[ALSA] Preparing playback device failed.\n"); - } else { - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "[ALSA] Preparing playback device successful.\n"); - } - - /* Clear the wakeupfd. */ - resultPoll = poll((struct pollfd*)pDevice->alsa.pPollDescriptorsPlayback, 1, 0); - if (resultPoll > 0) { - ma_uint64 t; - resultRead = read(((struct pollfd*)pDevice->alsa.pPollDescriptorsPlayback)[0].fd, &t, sizeof(t)); - if (resultRead != sizeof(t)) { - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "[ALSA] Failed to read from playback wakeupfd. read() = %d\n", resultRead); - } - } - } - - return MA_SUCCESS; -} - -static ma_result ma_device_wait__alsa(ma_device* pDevice, ma_snd_pcm_t* pPCM, struct pollfd* pPollDescriptors, int pollDescriptorCount, short requiredEvent) -{ - for (;;) { - unsigned short revents; - int resultALSA; - int resultPoll = poll(pPollDescriptors, pollDescriptorCount, -1); - if (resultPoll < 0) { - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_WARNING, "[ALSA] poll() failed.\n"); - - /* - There have been reports that poll() is returning an error randomly and that instead of - returning an error, simply trying again will work. I'm experimenting with adopting this - advice. - */ - continue; - /*return ma_result_from_errno(errno);*/ - } - - /* - Before checking the ALSA poll descriptor flag we need to check if the wakeup descriptor - has had it's POLLIN flag set. If so, we need to actually read the data and then exit the - function. The wakeup descriptor will be the first item in the descriptors buffer. - */ - if ((pPollDescriptors[0].revents & POLLIN) != 0) { - ma_uint64 t; - int resultRead = read(pPollDescriptors[0].fd, &t, sizeof(t)); /* <-- Important that we read here so that the next write() does not block. */ - if (resultRead < 0) { - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[ALSA] read() failed.\n"); - return ma_result_from_errno(errno); - } - - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "[ALSA] POLLIN set for wakeupfd\n"); - return MA_DEVICE_NOT_STARTED; - } - - /* - Getting here means that some data should be able to be read. We need to use ALSA to - translate the revents flags for us. - */ - resultALSA = ((ma_snd_pcm_poll_descriptors_revents_proc)pDevice->pContext->alsa.snd_pcm_poll_descriptors_revents)(pPCM, pPollDescriptors + 1, pollDescriptorCount - 1, &revents); /* +1, -1 to ignore the wakeup descriptor. */ - if (resultALSA < 0) { - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[ALSA] snd_pcm_poll_descriptors_revents() failed.\n"); - return ma_result_from_errno(-resultALSA); - } - - if ((revents & POLLERR) != 0) { - ma_snd_pcm_state_t state = ((ma_snd_pcm_state_proc)pDevice->pContext->alsa.snd_pcm_state)(pPCM); - if (state == MA_SND_PCM_STATE_XRUN) { - /* The PCM is in a xrun state. This will be recovered from at a higher level. We can disregard this. */ - } else { - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_WARNING, "[ALSA] POLLERR detected. status = %d\n", ((ma_snd_pcm_state_proc)pDevice->pContext->alsa.snd_pcm_state)(pPCM)); - } - } - - if ((revents & requiredEvent) == requiredEvent) { - break; /* We're done. Data available for reading or writing. */ - } - } - - return MA_SUCCESS; -} - -static ma_result ma_device_wait_read__alsa(ma_device* pDevice) -{ - return ma_device_wait__alsa(pDevice, (ma_snd_pcm_t*)pDevice->alsa.pPCMCapture, (struct pollfd*)pDevice->alsa.pPollDescriptorsCapture, pDevice->alsa.pollDescriptorCountCapture + 1, POLLIN); /* +1 to account for the wakeup descriptor. */ -} - -static ma_result ma_device_wait_write__alsa(ma_device* pDevice) -{ - return ma_device_wait__alsa(pDevice, (ma_snd_pcm_t*)pDevice->alsa.pPCMPlayback, (struct pollfd*)pDevice->alsa.pPollDescriptorsPlayback, pDevice->alsa.pollDescriptorCountPlayback + 1, POLLOUT); /* +1 to account for the wakeup descriptor. */ -} - -static ma_result ma_device_read__alsa(ma_device* pDevice, void* pFramesOut, ma_uint32 frameCount, ma_uint32* pFramesRead) -{ - ma_snd_pcm_sframes_t resultALSA = 0; - - MA_ASSERT(pDevice != NULL); - MA_ASSERT(pFramesOut != NULL); - - if (pFramesRead != NULL) { - *pFramesRead = 0; - } - - while (ma_device_get_state(pDevice) == ma_device_state_started) { - ma_result result; - - /* The first thing to do is wait for data to become available for reading. This will return an error code if the device has been stopped. */ - result = ma_device_wait_read__alsa(pDevice); - if (result != MA_SUCCESS) { - return result; - } - - /* Getting here means we should have data available. */ - resultALSA = ((ma_snd_pcm_readi_proc)pDevice->pContext->alsa.snd_pcm_readi)((ma_snd_pcm_t*)pDevice->alsa.pPCMCapture, pFramesOut, frameCount); - if (resultALSA >= 0) { - break; /* Success. */ - } else { - if (resultALSA == -EAGAIN) { - /*ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "EGAIN (read)\n");*/ - continue; /* Try again. */ - } else if (resultALSA == -EPIPE) { - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "EPIPE (read)\n"); - - /* Overrun. Recover and try again. If this fails we need to return an error. */ - resultALSA = ((ma_snd_pcm_recover_proc)pDevice->pContext->alsa.snd_pcm_recover)((ma_snd_pcm_t*)pDevice->alsa.pPCMCapture, resultALSA, MA_TRUE); - if (resultALSA < 0) { - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[ALSA] Failed to recover device after overrun."); - return ma_result_from_errno((int)-resultALSA); - } - - resultALSA = ((ma_snd_pcm_start_proc)pDevice->pContext->alsa.snd_pcm_start)((ma_snd_pcm_t*)pDevice->alsa.pPCMCapture); - if (resultALSA < 0) { - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[ALSA] Failed to start device after underrun."); - return ma_result_from_errno((int)-resultALSA); - } - - continue; /* Try reading again. */ - } - } - } - - if (pFramesRead != NULL) { - *pFramesRead = resultALSA; - } - - return MA_SUCCESS; -} - -static ma_result ma_device_write__alsa(ma_device* pDevice, const void* pFrames, ma_uint32 frameCount, ma_uint32* pFramesWritten) -{ - ma_snd_pcm_sframes_t resultALSA = 0; - - MA_ASSERT(pDevice != NULL); - MA_ASSERT(pFrames != NULL); - - if (pFramesWritten != NULL) { - *pFramesWritten = 0; - } - - while (ma_device_get_state(pDevice) == ma_device_state_started) { - ma_result result; - - /* The first thing to do is wait for space to become available for writing. This will return an error code if the device has been stopped. */ - result = ma_device_wait_write__alsa(pDevice); - if (result != MA_SUCCESS) { - return result; - } - - resultALSA = ((ma_snd_pcm_writei_proc)pDevice->pContext->alsa.snd_pcm_writei)((ma_snd_pcm_t*)pDevice->alsa.pPCMPlayback, pFrames, frameCount); - if (resultALSA >= 0) { - break; /* Success. */ - } else { - if (resultALSA == -EAGAIN) { - /*ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "EGAIN (write)\n");*/ - continue; /* Try again. */ - } else if (resultALSA == -EPIPE) { - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "EPIPE (write)\n"); - - /* Underrun. Recover and try again. If this fails we need to return an error. */ - resultALSA = ((ma_snd_pcm_recover_proc)pDevice->pContext->alsa.snd_pcm_recover)((ma_snd_pcm_t*)pDevice->alsa.pPCMPlayback, resultALSA, MA_TRUE); /* MA_TRUE=silent (don't print anything on error). */ - if (resultALSA < 0) { - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[ALSA] Failed to recover device after underrun."); - return ma_result_from_errno((int)-resultALSA); - } - - /* - In my testing I have had a situation where writei() does not automatically restart the device even though I've set it - up as such in the software parameters. What will happen is writei() will block indefinitely even though the number of - frames is well beyond the auto-start threshold. To work around this I've needed to add an explicit start here. Not sure - if this is me just being stupid and not recovering the device properly, but this definitely feels like something isn't - quite right here. - */ - resultALSA = ((ma_snd_pcm_start_proc)pDevice->pContext->alsa.snd_pcm_start)((ma_snd_pcm_t*)pDevice->alsa.pPCMPlayback); - if (resultALSA < 0) { - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[ALSA] Failed to start device after underrun."); - return ma_result_from_errno((int)-resultALSA); - } - - continue; /* Try writing again. */ - } - } - } - - if (pFramesWritten != NULL) { - *pFramesWritten = resultALSA; - } - - return MA_SUCCESS; -} - -static ma_result ma_device_data_loop_wakeup__alsa(ma_device* pDevice) -{ - ma_uint64 t = 1; - int resultWrite = 0; - - MA_ASSERT(pDevice != NULL); - - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "[ALSA] Waking up...\n"); - - /* Write to an eventfd to trigger a wakeup from poll() and abort any reading or writing. */ - if (pDevice->alsa.pPollDescriptorsCapture != NULL) { - resultWrite = write(pDevice->alsa.wakeupfdCapture, &t, sizeof(t)); - } - if (pDevice->alsa.pPollDescriptorsPlayback != NULL) { - resultWrite = write(pDevice->alsa.wakeupfdPlayback, &t, sizeof(t)); - } - - if (resultWrite < 0) { - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[ALSA] write() failed.\n"); - return ma_result_from_errno(errno); - } - - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "[ALSA] Waking up completed successfully.\n"); - - return MA_SUCCESS; -} - -static ma_result ma_context_uninit__alsa(ma_context* pContext) -{ - MA_ASSERT(pContext != NULL); - MA_ASSERT(pContext->backend == ma_backend_alsa); - - /* Clean up memory for memory leak checkers. */ - ((ma_snd_config_update_free_global_proc)pContext->alsa.snd_config_update_free_global)(); - -#ifndef MA_NO_RUNTIME_LINKING - ma_dlclose(ma_context_get_log(pContext), pContext->alsa.asoundSO); -#endif - - ma_mutex_uninit(&pContext->alsa.internalDeviceEnumLock); - - return MA_SUCCESS; -} - -static ma_result ma_context_init__alsa(ma_context* pContext, const ma_context_config* pConfig, ma_backend_callbacks* pCallbacks) -{ - ma_result result; -#ifndef MA_NO_RUNTIME_LINKING - const char* libasoundNames[] = { - "libasound.so.2", - "libasound.so" - }; - size_t i; - - for (i = 0; i < ma_countof(libasoundNames); ++i) { - pContext->alsa.asoundSO = ma_dlopen(ma_context_get_log(pContext), libasoundNames[i]); - if (pContext->alsa.asoundSO != NULL) { - break; - } - } - - if (pContext->alsa.asoundSO == NULL) { - ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_DEBUG, "[ALSA] Failed to open shared object.\n"); - return MA_NO_BACKEND; - } - - pContext->alsa.snd_pcm_open = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_open"); - pContext->alsa.snd_pcm_close = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_close"); - pContext->alsa.snd_pcm_hw_params_sizeof = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_hw_params_sizeof"); - pContext->alsa.snd_pcm_hw_params_any = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_hw_params_any"); - pContext->alsa.snd_pcm_hw_params_set_format = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_hw_params_set_format"); - pContext->alsa.snd_pcm_hw_params_set_format_first = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_hw_params_set_format_first"); - pContext->alsa.snd_pcm_hw_params_get_format_mask = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_hw_params_get_format_mask"); - pContext->alsa.snd_pcm_hw_params_set_channels = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_hw_params_set_channels"); - pContext->alsa.snd_pcm_hw_params_set_channels_near = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_hw_params_set_channels_near"); - pContext->alsa.snd_pcm_hw_params_set_channels_minmax = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_hw_params_set_channels_minmax"); - pContext->alsa.snd_pcm_hw_params_set_rate_resample = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_hw_params_set_rate_resample"); - pContext->alsa.snd_pcm_hw_params_set_rate = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_hw_params_set_rate"); - pContext->alsa.snd_pcm_hw_params_set_rate_near = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_hw_params_set_rate_near"); - pContext->alsa.snd_pcm_hw_params_set_buffer_size_near = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_hw_params_set_buffer_size_near"); - pContext->alsa.snd_pcm_hw_params_set_periods_near = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_hw_params_set_periods_near"); - pContext->alsa.snd_pcm_hw_params_set_access = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_hw_params_set_access"); - pContext->alsa.snd_pcm_hw_params_get_format = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_hw_params_get_format"); - pContext->alsa.snd_pcm_hw_params_get_channels = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_hw_params_get_channels"); - pContext->alsa.snd_pcm_hw_params_get_channels_min = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_hw_params_get_channels_min"); - pContext->alsa.snd_pcm_hw_params_get_channels_max = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_hw_params_get_channels_max"); - pContext->alsa.snd_pcm_hw_params_get_rate = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_hw_params_get_rate"); - pContext->alsa.snd_pcm_hw_params_get_rate_min = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_hw_params_get_rate_min"); - pContext->alsa.snd_pcm_hw_params_get_rate_max = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_hw_params_get_rate_max"); - pContext->alsa.snd_pcm_hw_params_get_buffer_size = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_hw_params_get_buffer_size"); - pContext->alsa.snd_pcm_hw_params_get_periods = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_hw_params_get_periods"); - pContext->alsa.snd_pcm_hw_params_get_access = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_hw_params_get_access"); - pContext->alsa.snd_pcm_hw_params_test_format = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_hw_params_test_format"); - pContext->alsa.snd_pcm_hw_params_test_channels = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_hw_params_test_channels"); - pContext->alsa.snd_pcm_hw_params_test_rate = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_hw_params_test_rate"); - pContext->alsa.snd_pcm_hw_params = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_hw_params"); - pContext->alsa.snd_pcm_sw_params_sizeof = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_sw_params_sizeof"); - pContext->alsa.snd_pcm_sw_params_current = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_sw_params_current"); - pContext->alsa.snd_pcm_sw_params_get_boundary = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_sw_params_get_boundary"); - pContext->alsa.snd_pcm_sw_params_set_avail_min = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_sw_params_set_avail_min"); - pContext->alsa.snd_pcm_sw_params_set_start_threshold = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_sw_params_set_start_threshold"); - pContext->alsa.snd_pcm_sw_params_set_stop_threshold = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_sw_params_set_stop_threshold"); - pContext->alsa.snd_pcm_sw_params = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_sw_params"); - pContext->alsa.snd_pcm_format_mask_sizeof = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_format_mask_sizeof"); - pContext->alsa.snd_pcm_format_mask_test = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_format_mask_test"); - pContext->alsa.snd_pcm_get_chmap = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_get_chmap"); - pContext->alsa.snd_pcm_state = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_state"); - pContext->alsa.snd_pcm_prepare = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_prepare"); - pContext->alsa.snd_pcm_start = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_start"); - pContext->alsa.snd_pcm_drop = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_drop"); - pContext->alsa.snd_pcm_drain = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_drain"); - pContext->alsa.snd_pcm_reset = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_reset"); - pContext->alsa.snd_device_name_hint = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_device_name_hint"); - pContext->alsa.snd_device_name_get_hint = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_device_name_get_hint"); - pContext->alsa.snd_card_get_index = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_card_get_index"); - pContext->alsa.snd_device_name_free_hint = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_device_name_free_hint"); - pContext->alsa.snd_pcm_mmap_begin = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_mmap_begin"); - pContext->alsa.snd_pcm_mmap_commit = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_mmap_commit"); - pContext->alsa.snd_pcm_recover = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_recover"); - pContext->alsa.snd_pcm_readi = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_readi"); - pContext->alsa.snd_pcm_writei = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_writei"); - pContext->alsa.snd_pcm_avail = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_avail"); - pContext->alsa.snd_pcm_avail_update = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_avail_update"); - pContext->alsa.snd_pcm_wait = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_wait"); - pContext->alsa.snd_pcm_nonblock = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_nonblock"); - pContext->alsa.snd_pcm_info = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_info"); - pContext->alsa.snd_pcm_info_sizeof = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_info_sizeof"); - pContext->alsa.snd_pcm_info_get_name = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_info_get_name"); - pContext->alsa.snd_pcm_poll_descriptors = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_poll_descriptors"); - pContext->alsa.snd_pcm_poll_descriptors_count = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_poll_descriptors_count"); - pContext->alsa.snd_pcm_poll_descriptors_revents = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_poll_descriptors_revents"); - pContext->alsa.snd_config_update_free_global = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_config_update_free_global"); -#else - /* The system below is just for type safety. */ - ma_snd_pcm_open_proc _snd_pcm_open = snd_pcm_open; - ma_snd_pcm_close_proc _snd_pcm_close = snd_pcm_close; - ma_snd_pcm_hw_params_sizeof_proc _snd_pcm_hw_params_sizeof = snd_pcm_hw_params_sizeof; - ma_snd_pcm_hw_params_any_proc _snd_pcm_hw_params_any = snd_pcm_hw_params_any; - ma_snd_pcm_hw_params_set_format_proc _snd_pcm_hw_params_set_format = snd_pcm_hw_params_set_format; - ma_snd_pcm_hw_params_set_format_first_proc _snd_pcm_hw_params_set_format_first = snd_pcm_hw_params_set_format_first; - ma_snd_pcm_hw_params_get_format_mask_proc _snd_pcm_hw_params_get_format_mask = snd_pcm_hw_params_get_format_mask; - ma_snd_pcm_hw_params_set_channels_proc _snd_pcm_hw_params_set_channels = snd_pcm_hw_params_set_channels; - ma_snd_pcm_hw_params_set_channels_near_proc _snd_pcm_hw_params_set_channels_near = snd_pcm_hw_params_set_channels_near; - ma_snd_pcm_hw_params_set_rate_resample_proc _snd_pcm_hw_params_set_rate_resample = snd_pcm_hw_params_set_rate_resample; - ma_snd_pcm_hw_params_set_rate_near _snd_pcm_hw_params_set_rate = snd_pcm_hw_params_set_rate; - ma_snd_pcm_hw_params_set_rate_near_proc _snd_pcm_hw_params_set_rate_near = snd_pcm_hw_params_set_rate_near; - ma_snd_pcm_hw_params_set_rate_minmax_proc _snd_pcm_hw_params_set_rate_minmax = snd_pcm_hw_params_set_rate_minmax; - ma_snd_pcm_hw_params_set_buffer_size_near_proc _snd_pcm_hw_params_set_buffer_size_near = snd_pcm_hw_params_set_buffer_size_near; - ma_snd_pcm_hw_params_set_periods_near_proc _snd_pcm_hw_params_set_periods_near = snd_pcm_hw_params_set_periods_near; - ma_snd_pcm_hw_params_set_access_proc _snd_pcm_hw_params_set_access = snd_pcm_hw_params_set_access; - ma_snd_pcm_hw_params_get_format_proc _snd_pcm_hw_params_get_format = snd_pcm_hw_params_get_format; - ma_snd_pcm_hw_params_get_channels_proc _snd_pcm_hw_params_get_channels = snd_pcm_hw_params_get_channels; - ma_snd_pcm_hw_params_get_channels_min_proc _snd_pcm_hw_params_get_channels_min = snd_pcm_hw_params_get_channels_min; - ma_snd_pcm_hw_params_get_channels_max_proc _snd_pcm_hw_params_get_channels_max = snd_pcm_hw_params_get_channels_max; - ma_snd_pcm_hw_params_get_rate_proc _snd_pcm_hw_params_get_rate = snd_pcm_hw_params_get_rate; - ma_snd_pcm_hw_params_get_rate_min_proc _snd_pcm_hw_params_get_rate_min = snd_pcm_hw_params_get_rate_min; - ma_snd_pcm_hw_params_get_rate_max_proc _snd_pcm_hw_params_get_rate_max = snd_pcm_hw_params_get_rate_max; - ma_snd_pcm_hw_params_get_buffer_size_proc _snd_pcm_hw_params_get_buffer_size = snd_pcm_hw_params_get_buffer_size; - ma_snd_pcm_hw_params_get_periods_proc _snd_pcm_hw_params_get_periods = snd_pcm_hw_params_get_periods; - ma_snd_pcm_hw_params_get_access_proc _snd_pcm_hw_params_get_access = snd_pcm_hw_params_get_access; - ma_snd_pcm_hw_params_test_format_proc _snd_pcm_hw_params_test_format = snd_pcm_hw_params_test_format; - ma_snd_pcm_hw_params_test_channels_proc _snd_pcm_hw_params_test_channels = snd_pcm_hw_params_test_channels; - ma_snd_pcm_hw_params_test_rate_proc _snd_pcm_hw_params_test_rate = snd_pcm_hw_params_test_rate; - ma_snd_pcm_hw_params_proc _snd_pcm_hw_params = snd_pcm_hw_params; - ma_snd_pcm_sw_params_sizeof_proc _snd_pcm_sw_params_sizeof = snd_pcm_sw_params_sizeof; - ma_snd_pcm_sw_params_current_proc _snd_pcm_sw_params_current = snd_pcm_sw_params_current; - ma_snd_pcm_sw_params_get_boundary_proc _snd_pcm_sw_params_get_boundary = snd_pcm_sw_params_get_boundary; - ma_snd_pcm_sw_params_set_avail_min_proc _snd_pcm_sw_params_set_avail_min = snd_pcm_sw_params_set_avail_min; - ma_snd_pcm_sw_params_set_start_threshold_proc _snd_pcm_sw_params_set_start_threshold = snd_pcm_sw_params_set_start_threshold; - ma_snd_pcm_sw_params_set_stop_threshold_proc _snd_pcm_sw_params_set_stop_threshold = snd_pcm_sw_params_set_stop_threshold; - ma_snd_pcm_sw_params_proc _snd_pcm_sw_params = snd_pcm_sw_params; - ma_snd_pcm_format_mask_sizeof_proc _snd_pcm_format_mask_sizeof = snd_pcm_format_mask_sizeof; - ma_snd_pcm_format_mask_test_proc _snd_pcm_format_mask_test = snd_pcm_format_mask_test; - ma_snd_pcm_get_chmap_proc _snd_pcm_get_chmap = snd_pcm_get_chmap; - ma_snd_pcm_state_proc _snd_pcm_state = snd_pcm_state; - ma_snd_pcm_prepare_proc _snd_pcm_prepare = snd_pcm_prepare; - ma_snd_pcm_start_proc _snd_pcm_start = snd_pcm_start; - ma_snd_pcm_drop_proc _snd_pcm_drop = snd_pcm_drop; - ma_snd_pcm_drain_proc _snd_pcm_drain = snd_pcm_drain; - ma_snd_pcm_reset_proc _snd_pcm_reset = snd_pcm_reset; - ma_snd_device_name_hint_proc _snd_device_name_hint = snd_device_name_hint; - ma_snd_device_name_get_hint_proc _snd_device_name_get_hint = snd_device_name_get_hint; - ma_snd_card_get_index_proc _snd_card_get_index = snd_card_get_index; - ma_snd_device_name_free_hint_proc _snd_device_name_free_hint = snd_device_name_free_hint; - ma_snd_pcm_mmap_begin_proc _snd_pcm_mmap_begin = snd_pcm_mmap_begin; - ma_snd_pcm_mmap_commit_proc _snd_pcm_mmap_commit = snd_pcm_mmap_commit; - ma_snd_pcm_recover_proc _snd_pcm_recover = snd_pcm_recover; - ma_snd_pcm_readi_proc _snd_pcm_readi = snd_pcm_readi; - ma_snd_pcm_writei_proc _snd_pcm_writei = snd_pcm_writei; - ma_snd_pcm_avail_proc _snd_pcm_avail = snd_pcm_avail; - ma_snd_pcm_avail_update_proc _snd_pcm_avail_update = snd_pcm_avail_update; - ma_snd_pcm_wait_proc _snd_pcm_wait = snd_pcm_wait; - ma_snd_pcm_nonblock_proc _snd_pcm_nonblock = snd_pcm_nonblock; - ma_snd_pcm_info_proc _snd_pcm_info = snd_pcm_info; - ma_snd_pcm_info_sizeof_proc _snd_pcm_info_sizeof = snd_pcm_info_sizeof; - ma_snd_pcm_info_get_name_proc _snd_pcm_info_get_name = snd_pcm_info_get_name; - ma_snd_pcm_poll_descriptors _snd_pcm_poll_descriptors = snd_pcm_poll_descriptors; - ma_snd_pcm_poll_descriptors_count _snd_pcm_poll_descriptors_count = snd_pcm_poll_descriptors_count; - ma_snd_pcm_poll_descriptors_revents _snd_pcm_poll_descriptors_revents = snd_pcm_poll_descriptors_revents; - ma_snd_config_update_free_global_proc _snd_config_update_free_global = snd_config_update_free_global; - - pContext->alsa.snd_pcm_open = (ma_proc)_snd_pcm_open; - pContext->alsa.snd_pcm_close = (ma_proc)_snd_pcm_close; - pContext->alsa.snd_pcm_hw_params_sizeof = (ma_proc)_snd_pcm_hw_params_sizeof; - pContext->alsa.snd_pcm_hw_params_any = (ma_proc)_snd_pcm_hw_params_any; - pContext->alsa.snd_pcm_hw_params_set_format = (ma_proc)_snd_pcm_hw_params_set_format; - pContext->alsa.snd_pcm_hw_params_set_format_first = (ma_proc)_snd_pcm_hw_params_set_format_first; - pContext->alsa.snd_pcm_hw_params_get_format_mask = (ma_proc)_snd_pcm_hw_params_get_format_mask; - pContext->alsa.snd_pcm_hw_params_set_channels = (ma_proc)_snd_pcm_hw_params_set_channels; - pContext->alsa.snd_pcm_hw_params_set_channels_near = (ma_proc)_snd_pcm_hw_params_set_channels_near; - pContext->alsa.snd_pcm_hw_params_set_channels_minmax = (ma_proc)_snd_pcm_hw_params_set_channels_minmax; - pContext->alsa.snd_pcm_hw_params_set_rate_resample = (ma_proc)_snd_pcm_hw_params_set_rate_resample; - pContext->alsa.snd_pcm_hw_params_set_rate = (ma_proc)_snd_pcm_hw_params_set_rate; - pContext->alsa.snd_pcm_hw_params_set_rate_near = (ma_proc)_snd_pcm_hw_params_set_rate_near; - pContext->alsa.snd_pcm_hw_params_set_buffer_size_near = (ma_proc)_snd_pcm_hw_params_set_buffer_size_near; - pContext->alsa.snd_pcm_hw_params_set_periods_near = (ma_proc)_snd_pcm_hw_params_set_periods_near; - pContext->alsa.snd_pcm_hw_params_set_access = (ma_proc)_snd_pcm_hw_params_set_access; - pContext->alsa.snd_pcm_hw_params_get_format = (ma_proc)_snd_pcm_hw_params_get_format; - pContext->alsa.snd_pcm_hw_params_get_channels = (ma_proc)_snd_pcm_hw_params_get_channels; - pContext->alsa.snd_pcm_hw_params_get_channels_min = (ma_proc)_snd_pcm_hw_params_get_channels_min; - pContext->alsa.snd_pcm_hw_params_get_channels_max = (ma_proc)_snd_pcm_hw_params_get_channels_max; - pContext->alsa.snd_pcm_hw_params_get_rate = (ma_proc)_snd_pcm_hw_params_get_rate; - pContext->alsa.snd_pcm_hw_params_get_rate_min = (ma_proc)_snd_pcm_hw_params_get_rate_min; - pContext->alsa.snd_pcm_hw_params_get_rate_max = (ma_proc)_snd_pcm_hw_params_get_rate_max; - pContext->alsa.snd_pcm_hw_params_get_buffer_size = (ma_proc)_snd_pcm_hw_params_get_buffer_size; - pContext->alsa.snd_pcm_hw_params_get_periods = (ma_proc)_snd_pcm_hw_params_get_periods; - pContext->alsa.snd_pcm_hw_params_get_access = (ma_proc)_snd_pcm_hw_params_get_access; - pContext->alsa.snd_pcm_hw_params_test_format = (ma_proc)_snd_pcm_hw_params_test_format; - pContext->alsa.snd_pcm_hw_params_test_channels = (ma_proc)_snd_pcm_hw_params_test_channels; - pContext->alsa.snd_pcm_hw_params_test_rate = (ma_proc)_snd_pcm_hw_params_test_rate; - pContext->alsa.snd_pcm_hw_params = (ma_proc)_snd_pcm_hw_params; - pContext->alsa.snd_pcm_sw_params_sizeof = (ma_proc)_snd_pcm_sw_params_sizeof; - pContext->alsa.snd_pcm_sw_params_current = (ma_proc)_snd_pcm_sw_params_current; - pContext->alsa.snd_pcm_sw_params_get_boundary = (ma_proc)_snd_pcm_sw_params_get_boundary; - pContext->alsa.snd_pcm_sw_params_set_avail_min = (ma_proc)_snd_pcm_sw_params_set_avail_min; - pContext->alsa.snd_pcm_sw_params_set_start_threshold = (ma_proc)_snd_pcm_sw_params_set_start_threshold; - pContext->alsa.snd_pcm_sw_params_set_stop_threshold = (ma_proc)_snd_pcm_sw_params_set_stop_threshold; - pContext->alsa.snd_pcm_sw_params = (ma_proc)_snd_pcm_sw_params; - pContext->alsa.snd_pcm_format_mask_sizeof = (ma_proc)_snd_pcm_format_mask_sizeof; - pContext->alsa.snd_pcm_format_mask_test = (ma_proc)_snd_pcm_format_mask_test; - pContext->alsa.snd_pcm_get_chmap = (ma_proc)_snd_pcm_get_chmap; - pContext->alsa.snd_pcm_state = (ma_proc)_snd_pcm_state; - pContext->alsa.snd_pcm_prepare = (ma_proc)_snd_pcm_prepare; - pContext->alsa.snd_pcm_start = (ma_proc)_snd_pcm_start; - pContext->alsa.snd_pcm_drop = (ma_proc)_snd_pcm_drop; - pContext->alsa.snd_pcm_drain = (ma_proc)_snd_pcm_drain; - pContext->alsa.snd_pcm_reset = (ma_proc)_snd_pcm_reset; - pContext->alsa.snd_device_name_hint = (ma_proc)_snd_device_name_hint; - pContext->alsa.snd_device_name_get_hint = (ma_proc)_snd_device_name_get_hint; - pContext->alsa.snd_card_get_index = (ma_proc)_snd_card_get_index; - pContext->alsa.snd_device_name_free_hint = (ma_proc)_snd_device_name_free_hint; - pContext->alsa.snd_pcm_mmap_begin = (ma_proc)_snd_pcm_mmap_begin; - pContext->alsa.snd_pcm_mmap_commit = (ma_proc)_snd_pcm_mmap_commit; - pContext->alsa.snd_pcm_recover = (ma_proc)_snd_pcm_recover; - pContext->alsa.snd_pcm_readi = (ma_proc)_snd_pcm_readi; - pContext->alsa.snd_pcm_writei = (ma_proc)_snd_pcm_writei; - pContext->alsa.snd_pcm_avail = (ma_proc)_snd_pcm_avail; - pContext->alsa.snd_pcm_avail_update = (ma_proc)_snd_pcm_avail_update; - pContext->alsa.snd_pcm_wait = (ma_proc)_snd_pcm_wait; - pContext->alsa.snd_pcm_nonblock = (ma_proc)_snd_pcm_nonblock; - pContext->alsa.snd_pcm_info = (ma_proc)_snd_pcm_info; - pContext->alsa.snd_pcm_info_sizeof = (ma_proc)_snd_pcm_info_sizeof; - pContext->alsa.snd_pcm_info_get_name = (ma_proc)_snd_pcm_info_get_name; - pContext->alsa.snd_pcm_poll_descriptors = (ma_proc)_snd_pcm_poll_descriptors; - pContext->alsa.snd_pcm_poll_descriptors_count = (ma_proc)_snd_pcm_poll_descriptors_count; - pContext->alsa.snd_pcm_poll_descriptors_revents = (ma_proc)_snd_pcm_poll_descriptors_revents; - pContext->alsa.snd_config_update_free_global = (ma_proc)_snd_config_update_free_global; -#endif - - pContext->alsa.useVerboseDeviceEnumeration = pConfig->alsa.useVerboseDeviceEnumeration; - - result = ma_mutex_init(&pContext->alsa.internalDeviceEnumLock); - if (result != MA_SUCCESS) { - ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[ALSA] WARNING: Failed to initialize mutex for internal device enumeration."); - return result; - } - - pCallbacks->onContextInit = ma_context_init__alsa; - pCallbacks->onContextUninit = ma_context_uninit__alsa; - pCallbacks->onContextEnumerateDevices = ma_context_enumerate_devices__alsa; - pCallbacks->onContextGetDeviceInfo = ma_context_get_device_info__alsa; - pCallbacks->onDeviceInit = ma_device_init__alsa; - pCallbacks->onDeviceUninit = ma_device_uninit__alsa; - pCallbacks->onDeviceStart = ma_device_start__alsa; - pCallbacks->onDeviceStop = ma_device_stop__alsa; - pCallbacks->onDeviceRead = ma_device_read__alsa; - pCallbacks->onDeviceWrite = ma_device_write__alsa; - pCallbacks->onDeviceDataLoop = NULL; - pCallbacks->onDeviceDataLoopWakeup = ma_device_data_loop_wakeup__alsa; - - return MA_SUCCESS; -} -#endif /* MA_HAS_ALSA */ - - - -/****************************************************************************** - -PulseAudio Backend - -******************************************************************************/ -#ifdef MA_HAS_PULSEAUDIO -/* -The PulseAudio API, along with Apple's Core Audio, is the worst of the mainstream audio APIs. This is a brief description of what's going on -in the PulseAudio backend. I apologize if this gets a bit ranty for your liking - you might want to skip this discussion. - -PulseAudio has something they call the "Simple API", which unfortunately isn't suitable for miniaudio. I've not seen anywhere where it -allows you to enumerate over devices, nor does it seem to support the ability to stop and start streams. Looking at the documentation, it -appears as though the stream is constantly running and you prevent sound from being emitted or captured by simply not calling the read or -write functions. This is not a professional solution as it would be much better to *actually* stop the underlying stream. Perhaps the -simple API has some smarts to do this automatically, but I'm not sure. Another limitation with the simple API is that it seems inefficient -when you want to have multiple streams to a single context. For these reasons, miniaudio is not using the simple API. - -Since we're not using the simple API, we're left with the asynchronous API as our only other option. And boy, is this where it starts to -get fun, and I don't mean that in a good way... - -The problems start with the very name of the API - "asynchronous". Yes, this is an asynchronous oriented API which means your commands -don't immediately take effect. You instead need to issue your commands, and then wait for them to complete. The waiting mechanism is -enabled through the use of a "main loop". In the asynchronous API you cannot get away from the main loop, and the main loop is where almost -all of PulseAudio's problems stem from. - -When you first initialize PulseAudio you need an object referred to as "main loop". You can implement this yourself by defining your own -vtable, but it's much easier to just use one of the built-in main loop implementations. There's two generic implementations called -pa_mainloop and pa_threaded_mainloop, and another implementation specific to GLib called pa_glib_mainloop. We're using pa_threaded_mainloop -because it simplifies management of the worker thread. The idea of the main loop object is pretty self explanatory - you're supposed to use -it to implement a worker thread which runs in a loop. The main loop is where operations are actually executed. - -To initialize the main loop, you just use `pa_threaded_mainloop_new()`. This is the first function you'll call. You can then get a pointer -to the vtable with `pa_threaded_mainloop_get_api()` (the main loop vtable is called `pa_mainloop_api`). Again, you can bypass the threaded -main loop object entirely and just implement `pa_mainloop_api` directly, but there's no need for it unless you're doing something extremely -specialized such as if you want to integrate it into your application's existing main loop infrastructure. - -(EDIT 2021-01-26: miniaudio is no longer using `pa_threaded_mainloop` due to this issue: https://github.com/mackron/miniaudio/issues/262. -It is now using `pa_mainloop` which turns out to be a simpler solution anyway. The rest of this rant still applies, however.) - -Once you have your main loop vtable (the `pa_mainloop_api` object) you can create the PulseAudio context. This is very similar to -miniaudio's context and they map to each other quite well. You have one context to many streams, which is basically the same as miniaudio's -one `ma_context` to many `ma_device`s. Here's where it starts to get annoying, however. When you first create the PulseAudio context, which -is done with `pa_context_new()`, it's not actually connected to anything. When you connect, you call `pa_context_connect()`. However, if -you remember, PulseAudio is an asynchronous API. That means you cannot just assume the context is connected after `pa_context_context()` -has returned. You instead need to wait for it to connect. To do this, you need to either wait for a callback to get fired, which you can -set with `pa_context_set_state_callback()`, or you can continuously poll the context's state. Either way, you need to run this in a loop. -All objects from here out are created from the context, and, I believe, you can't be creating these objects until the context is connected. -This waiting loop is therefore unavoidable. In order for the waiting to ever complete, however, the main loop needs to be running. Before -attempting to connect the context, the main loop needs to be started with `pa_threaded_mainloop_start()`. - -The reason for this asynchronous design is to support cases where you're connecting to a remote server, say through a local network or an -internet connection. However, the *VAST* majority of cases don't involve this at all - they just connect to a local "server" running on the -host machine. The fact that this would be the default rather than making `pa_context_connect()` synchronous tends to boggle the mind. - -Once the context has been created and connected you can start creating a stream. A PulseAudio stream is analogous to miniaudio's device. -The initialization of a stream is fairly standard - you configure some attributes (analogous to miniaudio's device config) and then call -`pa_stream_new()` to actually create it. Here is where we start to get into "operations". When configuring the stream, you can get -information about the source (such as sample format, sample rate, etc.), however it's not synchronous. Instead, a `pa_operation` object -is returned from `pa_context_get_source_info_by_name()` (capture) or `pa_context_get_sink_info_by_name()` (playback). Then, you need to -run a loop (again!) to wait for the operation to complete which you can determine via a callback or polling, just like we did with the -context. Then, as an added bonus, you need to decrement the reference counter of the `pa_operation` object to ensure memory is cleaned up. -All of that just to retrieve basic information about a device! - -Once the basic information about the device has been retrieved, miniaudio can now create the stream with `ma_stream_new()`. Like the -context, this needs to be connected. But we need to be careful here, because we're now about to introduce one of the most horrific design -choices in PulseAudio. - -PulseAudio allows you to specify a callback that is fired when data can be written to or read from a stream. The language is important here -because PulseAudio takes it literally, specifically the "can be". You would think these callbacks would be appropriate as the place for -writing and reading data to and from the stream, and that would be right, except when it's not. When you initialize the stream, you can -set a flag that tells PulseAudio to not start the stream automatically. This is required because miniaudio does not auto-start devices -straight after initialization - you need to call `ma_device_start()` manually. The problem is that even when this flag is specified, -PulseAudio will immediately fire its write or read callback. This is *technically* correct (based on the wording in the documentation) -because indeed, data *can* be written at this point. The problem is that it's not *practical*. It makes sense that the write/read callback -would be where a program will want to write or read data to or from the stream, but when it's called before the application has even -requested that the stream be started, it's just not practical because the program probably isn't ready for any kind of data delivery at -that point (it may still need to load files or whatnot). Instead, this callback should only be fired when the application requests the -stream be started which is how it works with literally *every* other callback-based audio API. Since miniaudio forbids firing of the data -callback until the device has been started (as it should be with *all* callback based APIs), logic needs to be added to ensure miniaudio -doesn't just blindly fire the application-defined data callback from within the PulseAudio callback before the stream has actually been -started. The device state is used for this - if the state is anything other than `ma_device_state_starting` or `ma_device_state_started`, the main data -callback is not fired. - -This, unfortunately, is not the end of the problems with the PulseAudio write callback. Any normal callback based audio API will -continuously fire the callback at regular intervals based on the size of the internal buffer. This will only ever be fired when the device -is running, and will be fired regardless of whether or not the user actually wrote anything to the device/stream. This not the case in -PulseAudio. In PulseAudio, the data callback will *only* be called if you wrote something to it previously. That means, if you don't call -`pa_stream_write()`, the callback will not get fired. On the surface you wouldn't think this would matter because you should be always -writing data, and if you don't have anything to write, just write silence. That's fine until you want to drain the stream. You see, if -you're continuously writing data to the stream, the stream will never get drained! That means in order to drain the stream, you need to -*not* write data to it! But remember, when you don't write data to the stream, the callback won't get fired again! Why is draining -important? Because that's how we've defined stopping to work in miniaudio. In miniaudio, stopping the device requires it to be drained -before returning from ma_device_stop(). So we've stopped the device, which requires us to drain, but draining requires us to *not* write -data to the stream (or else it won't ever complete draining), but not writing to the stream means the callback won't get fired again! - -This becomes a problem when stopping and then restarting the device. When the device is stopped, it's drained, which requires us to *not* -write anything to the stream. But then, since we didn't write anything to it, the write callback will *never* get called again if we just -resume the stream naively. This means that starting the stream requires us to write data to the stream from outside the callback. This -disconnect is something PulseAudio has got seriously wrong - there should only ever be a single source of data delivery, that being the -callback. (I have tried using `pa_stream_flush()` to trigger the write callback to fire, but this just doesn't work for some reason.) - -Once you've created the stream, you need to connect it which involves the whole waiting procedure. This is the same process as the context, -only this time you'll poll for the state with `pa_stream_get_status()`. The starting and stopping of a streaming is referred to as -"corking" in PulseAudio. The analogy is corking a barrel. To start the stream, you uncork it, to stop it you cork it. Personally I think -it's silly - why would you not just call it "starting" and "stopping" like any other normal audio API? Anyway, the act of corking is, you -guessed it, asynchronous. This means you'll need our waiting loop as usual. Again, why this asynchronous design is the default is -absolutely beyond me. Would it really be that hard to just make it run synchronously? - -Teardown is pretty simple (what?!). It's just a matter of calling the relevant `_unref()` function on each object in reverse order that -they were initialized in. - -That's about it from the PulseAudio side. A bit ranty, I know, but they really need to fix that main loop and callback system. They're -embarrassingly unpractical. The main loop thing is an easy fix - have synchronous versions of all APIs. If an application wants these to -run asynchronously, they can execute them in a separate thread themselves. The desire to run these asynchronously is such a niche -requirement - it makes no sense to make it the default. The stream write callback needs to be change, or an alternative provided, that is -constantly fired, regardless of whether or not `pa_stream_write()` has been called, and it needs to take a pointer to a buffer as a -parameter which the program just writes to directly rather than having to call `pa_stream_writable_size()` and `pa_stream_write()`. These -changes alone will change PulseAudio from one of the worst audio APIs to one of the best. -*/ - - -/* -It is assumed pulseaudio.h is available when linking at compile time. When linking at compile time, we use the declarations in the header -to check for type safety. We cannot do this when linking at run time because the header might not be available. -*/ -#ifdef MA_NO_RUNTIME_LINKING - -/* pulseaudio.h marks some functions with "inline" which isn't always supported. Need to emulate it. */ -#if !defined(__cplusplus) - #if defined(__STRICT_ANSI__) - #if !defined(inline) - #define inline __inline__ __attribute__((always_inline)) - #define MA_INLINE_DEFINED - #endif - #endif -#endif -#include -#if defined(MA_INLINE_DEFINED) - #undef inline - #undef MA_INLINE_DEFINED -#endif - -#define MA_PA_OK PA_OK -#define MA_PA_ERR_ACCESS PA_ERR_ACCESS -#define MA_PA_ERR_INVALID PA_ERR_INVALID -#define MA_PA_ERR_NOENTITY PA_ERR_NOENTITY -#define MA_PA_ERR_NOTSUPPORTED PA_ERR_NOTSUPPORTED - -#define MA_PA_CHANNELS_MAX PA_CHANNELS_MAX -#define MA_PA_RATE_MAX PA_RATE_MAX - -typedef pa_context_flags_t ma_pa_context_flags_t; -#define MA_PA_CONTEXT_NOFLAGS PA_CONTEXT_NOFLAGS -#define MA_PA_CONTEXT_NOAUTOSPAWN PA_CONTEXT_NOAUTOSPAWN -#define MA_PA_CONTEXT_NOFAIL PA_CONTEXT_NOFAIL - -typedef pa_stream_flags_t ma_pa_stream_flags_t; -#define MA_PA_STREAM_NOFLAGS PA_STREAM_NOFLAGS -#define MA_PA_STREAM_START_CORKED PA_STREAM_START_CORKED -#define MA_PA_STREAM_INTERPOLATE_TIMING PA_STREAM_INTERPOLATE_TIMING -#define MA_PA_STREAM_NOT_MONOTONIC PA_STREAM_NOT_MONOTONIC -#define MA_PA_STREAM_AUTO_TIMING_UPDATE PA_STREAM_AUTO_TIMING_UPDATE -#define MA_PA_STREAM_NO_REMAP_CHANNELS PA_STREAM_NO_REMAP_CHANNELS -#define MA_PA_STREAM_NO_REMIX_CHANNELS PA_STREAM_NO_REMIX_CHANNELS -#define MA_PA_STREAM_FIX_FORMAT PA_STREAM_FIX_FORMAT -#define MA_PA_STREAM_FIX_RATE PA_STREAM_FIX_RATE -#define MA_PA_STREAM_FIX_CHANNELS PA_STREAM_FIX_CHANNELS -#define MA_PA_STREAM_DONT_MOVE PA_STREAM_DONT_MOVE -#define MA_PA_STREAM_VARIABLE_RATE PA_STREAM_VARIABLE_RATE -#define MA_PA_STREAM_PEAK_DETECT PA_STREAM_PEAK_DETECT -#define MA_PA_STREAM_START_MUTED PA_STREAM_START_MUTED -#define MA_PA_STREAM_ADJUST_LATENCY PA_STREAM_ADJUST_LATENCY -#define MA_PA_STREAM_EARLY_REQUESTS PA_STREAM_EARLY_REQUESTS -#define MA_PA_STREAM_DONT_INHIBIT_AUTO_SUSPEND PA_STREAM_DONT_INHIBIT_AUTO_SUSPEND -#define MA_PA_STREAM_START_UNMUTED PA_STREAM_START_UNMUTED -#define MA_PA_STREAM_FAIL_ON_SUSPEND PA_STREAM_FAIL_ON_SUSPEND -#define MA_PA_STREAM_RELATIVE_VOLUME PA_STREAM_RELATIVE_VOLUME -#define MA_PA_STREAM_PASSTHROUGH PA_STREAM_PASSTHROUGH - -typedef pa_sink_flags_t ma_pa_sink_flags_t; -#define MA_PA_SINK_NOFLAGS PA_SINK_NOFLAGS -#define MA_PA_SINK_HW_VOLUME_CTRL PA_SINK_HW_VOLUME_CTRL -#define MA_PA_SINK_LATENCY PA_SINK_LATENCY -#define MA_PA_SINK_HARDWARE PA_SINK_HARDWARE -#define MA_PA_SINK_NETWORK PA_SINK_NETWORK -#define MA_PA_SINK_HW_MUTE_CTRL PA_SINK_HW_MUTE_CTRL -#define MA_PA_SINK_DECIBEL_VOLUME PA_SINK_DECIBEL_VOLUME -#define MA_PA_SINK_FLAT_VOLUME PA_SINK_FLAT_VOLUME -#define MA_PA_SINK_DYNAMIC_LATENCY PA_SINK_DYNAMIC_LATENCY -#define MA_PA_SINK_SET_FORMATS PA_SINK_SET_FORMATS - -typedef pa_source_flags_t ma_pa_source_flags_t; -#define MA_PA_SOURCE_NOFLAGS PA_SOURCE_NOFLAGS -#define MA_PA_SOURCE_HW_VOLUME_CTRL PA_SOURCE_HW_VOLUME_CTRL -#define MA_PA_SOURCE_LATENCY PA_SOURCE_LATENCY -#define MA_PA_SOURCE_HARDWARE PA_SOURCE_HARDWARE -#define MA_PA_SOURCE_NETWORK PA_SOURCE_NETWORK -#define MA_PA_SOURCE_HW_MUTE_CTRL PA_SOURCE_HW_MUTE_CTRL -#define MA_PA_SOURCE_DECIBEL_VOLUME PA_SOURCE_DECIBEL_VOLUME -#define MA_PA_SOURCE_DYNAMIC_LATENCY PA_SOURCE_DYNAMIC_LATENCY -#define MA_PA_SOURCE_FLAT_VOLUME PA_SOURCE_FLAT_VOLUME - -typedef pa_context_state_t ma_pa_context_state_t; -#define MA_PA_CONTEXT_UNCONNECTED PA_CONTEXT_UNCONNECTED -#define MA_PA_CONTEXT_CONNECTING PA_CONTEXT_CONNECTING -#define MA_PA_CONTEXT_AUTHORIZING PA_CONTEXT_AUTHORIZING -#define MA_PA_CONTEXT_SETTING_NAME PA_CONTEXT_SETTING_NAME -#define MA_PA_CONTEXT_READY PA_CONTEXT_READY -#define MA_PA_CONTEXT_FAILED PA_CONTEXT_FAILED -#define MA_PA_CONTEXT_TERMINATED PA_CONTEXT_TERMINATED - -typedef pa_stream_state_t ma_pa_stream_state_t; -#define MA_PA_STREAM_UNCONNECTED PA_STREAM_UNCONNECTED -#define MA_PA_STREAM_CREATING PA_STREAM_CREATING -#define MA_PA_STREAM_READY PA_STREAM_READY -#define MA_PA_STREAM_FAILED PA_STREAM_FAILED -#define MA_PA_STREAM_TERMINATED PA_STREAM_TERMINATED - -typedef pa_operation_state_t ma_pa_operation_state_t; -#define MA_PA_OPERATION_RUNNING PA_OPERATION_RUNNING -#define MA_PA_OPERATION_DONE PA_OPERATION_DONE -#define MA_PA_OPERATION_CANCELLED PA_OPERATION_CANCELLED - -typedef pa_sink_state_t ma_pa_sink_state_t; -#define MA_PA_SINK_INVALID_STATE PA_SINK_INVALID_STATE -#define MA_PA_SINK_RUNNING PA_SINK_RUNNING -#define MA_PA_SINK_IDLE PA_SINK_IDLE -#define MA_PA_SINK_SUSPENDED PA_SINK_SUSPENDED - -typedef pa_source_state_t ma_pa_source_state_t; -#define MA_PA_SOURCE_INVALID_STATE PA_SOURCE_INVALID_STATE -#define MA_PA_SOURCE_RUNNING PA_SOURCE_RUNNING -#define MA_PA_SOURCE_IDLE PA_SOURCE_IDLE -#define MA_PA_SOURCE_SUSPENDED PA_SOURCE_SUSPENDED - -typedef pa_seek_mode_t ma_pa_seek_mode_t; -#define MA_PA_SEEK_RELATIVE PA_SEEK_RELATIVE -#define MA_PA_SEEK_ABSOLUTE PA_SEEK_ABSOLUTE -#define MA_PA_SEEK_RELATIVE_ON_READ PA_SEEK_RELATIVE_ON_READ -#define MA_PA_SEEK_RELATIVE_END PA_SEEK_RELATIVE_END - -typedef pa_channel_position_t ma_pa_channel_position_t; -#define MA_PA_CHANNEL_POSITION_INVALID PA_CHANNEL_POSITION_INVALID -#define MA_PA_CHANNEL_POSITION_MONO PA_CHANNEL_POSITION_MONO -#define MA_PA_CHANNEL_POSITION_FRONT_LEFT PA_CHANNEL_POSITION_FRONT_LEFT -#define MA_PA_CHANNEL_POSITION_FRONT_RIGHT PA_CHANNEL_POSITION_FRONT_RIGHT -#define MA_PA_CHANNEL_POSITION_FRONT_CENTER PA_CHANNEL_POSITION_FRONT_CENTER -#define MA_PA_CHANNEL_POSITION_REAR_CENTER PA_CHANNEL_POSITION_REAR_CENTER -#define MA_PA_CHANNEL_POSITION_REAR_LEFT PA_CHANNEL_POSITION_REAR_LEFT -#define MA_PA_CHANNEL_POSITION_REAR_RIGHT PA_CHANNEL_POSITION_REAR_RIGHT -#define MA_PA_CHANNEL_POSITION_LFE PA_CHANNEL_POSITION_LFE -#define MA_PA_CHANNEL_POSITION_FRONT_LEFT_OF_CENTER PA_CHANNEL_POSITION_FRONT_LEFT_OF_CENTER -#define MA_PA_CHANNEL_POSITION_FRONT_RIGHT_OF_CENTER PA_CHANNEL_POSITION_FRONT_RIGHT_OF_CENTER -#define MA_PA_CHANNEL_POSITION_SIDE_LEFT PA_CHANNEL_POSITION_SIDE_LEFT -#define MA_PA_CHANNEL_POSITION_SIDE_RIGHT PA_CHANNEL_POSITION_SIDE_RIGHT -#define MA_PA_CHANNEL_POSITION_AUX0 PA_CHANNEL_POSITION_AUX0 -#define MA_PA_CHANNEL_POSITION_AUX1 PA_CHANNEL_POSITION_AUX1 -#define MA_PA_CHANNEL_POSITION_AUX2 PA_CHANNEL_POSITION_AUX2 -#define MA_PA_CHANNEL_POSITION_AUX3 PA_CHANNEL_POSITION_AUX3 -#define MA_PA_CHANNEL_POSITION_AUX4 PA_CHANNEL_POSITION_AUX4 -#define MA_PA_CHANNEL_POSITION_AUX5 PA_CHANNEL_POSITION_AUX5 -#define MA_PA_CHANNEL_POSITION_AUX6 PA_CHANNEL_POSITION_AUX6 -#define MA_PA_CHANNEL_POSITION_AUX7 PA_CHANNEL_POSITION_AUX7 -#define MA_PA_CHANNEL_POSITION_AUX8 PA_CHANNEL_POSITION_AUX8 -#define MA_PA_CHANNEL_POSITION_AUX9 PA_CHANNEL_POSITION_AUX9 -#define MA_PA_CHANNEL_POSITION_AUX10 PA_CHANNEL_POSITION_AUX10 -#define MA_PA_CHANNEL_POSITION_AUX11 PA_CHANNEL_POSITION_AUX11 -#define MA_PA_CHANNEL_POSITION_AUX12 PA_CHANNEL_POSITION_AUX12 -#define MA_PA_CHANNEL_POSITION_AUX13 PA_CHANNEL_POSITION_AUX13 -#define MA_PA_CHANNEL_POSITION_AUX14 PA_CHANNEL_POSITION_AUX14 -#define MA_PA_CHANNEL_POSITION_AUX15 PA_CHANNEL_POSITION_AUX15 -#define MA_PA_CHANNEL_POSITION_AUX16 PA_CHANNEL_POSITION_AUX16 -#define MA_PA_CHANNEL_POSITION_AUX17 PA_CHANNEL_POSITION_AUX17 -#define MA_PA_CHANNEL_POSITION_AUX18 PA_CHANNEL_POSITION_AUX18 -#define MA_PA_CHANNEL_POSITION_AUX19 PA_CHANNEL_POSITION_AUX19 -#define MA_PA_CHANNEL_POSITION_AUX20 PA_CHANNEL_POSITION_AUX20 -#define MA_PA_CHANNEL_POSITION_AUX21 PA_CHANNEL_POSITION_AUX21 -#define MA_PA_CHANNEL_POSITION_AUX22 PA_CHANNEL_POSITION_AUX22 -#define MA_PA_CHANNEL_POSITION_AUX23 PA_CHANNEL_POSITION_AUX23 -#define MA_PA_CHANNEL_POSITION_AUX24 PA_CHANNEL_POSITION_AUX24 -#define MA_PA_CHANNEL_POSITION_AUX25 PA_CHANNEL_POSITION_AUX25 -#define MA_PA_CHANNEL_POSITION_AUX26 PA_CHANNEL_POSITION_AUX26 -#define MA_PA_CHANNEL_POSITION_AUX27 PA_CHANNEL_POSITION_AUX27 -#define MA_PA_CHANNEL_POSITION_AUX28 PA_CHANNEL_POSITION_AUX28 -#define MA_PA_CHANNEL_POSITION_AUX29 PA_CHANNEL_POSITION_AUX29 -#define MA_PA_CHANNEL_POSITION_AUX30 PA_CHANNEL_POSITION_AUX30 -#define MA_PA_CHANNEL_POSITION_AUX31 PA_CHANNEL_POSITION_AUX31 -#define MA_PA_CHANNEL_POSITION_TOP_CENTER PA_CHANNEL_POSITION_TOP_CENTER -#define MA_PA_CHANNEL_POSITION_TOP_FRONT_LEFT PA_CHANNEL_POSITION_TOP_FRONT_LEFT -#define MA_PA_CHANNEL_POSITION_TOP_FRONT_RIGHT PA_CHANNEL_POSITION_TOP_FRONT_RIGHT -#define MA_PA_CHANNEL_POSITION_TOP_FRONT_CENTER PA_CHANNEL_POSITION_TOP_FRONT_CENTER -#define MA_PA_CHANNEL_POSITION_TOP_REAR_LEFT PA_CHANNEL_POSITION_TOP_REAR_LEFT -#define MA_PA_CHANNEL_POSITION_TOP_REAR_RIGHT PA_CHANNEL_POSITION_TOP_REAR_RIGHT -#define MA_PA_CHANNEL_POSITION_TOP_REAR_CENTER PA_CHANNEL_POSITION_TOP_REAR_CENTER -#define MA_PA_CHANNEL_POSITION_LEFT PA_CHANNEL_POSITION_LEFT -#define MA_PA_CHANNEL_POSITION_RIGHT PA_CHANNEL_POSITION_RIGHT -#define MA_PA_CHANNEL_POSITION_CENTER PA_CHANNEL_POSITION_CENTER -#define MA_PA_CHANNEL_POSITION_SUBWOOFER PA_CHANNEL_POSITION_SUBWOOFER - -typedef pa_channel_map_def_t ma_pa_channel_map_def_t; -#define MA_PA_CHANNEL_MAP_AIFF PA_CHANNEL_MAP_AIFF -#define MA_PA_CHANNEL_MAP_ALSA PA_CHANNEL_MAP_ALSA -#define MA_PA_CHANNEL_MAP_AUX PA_CHANNEL_MAP_AUX -#define MA_PA_CHANNEL_MAP_WAVEEX PA_CHANNEL_MAP_WAVEEX -#define MA_PA_CHANNEL_MAP_OSS PA_CHANNEL_MAP_OSS -#define MA_PA_CHANNEL_MAP_DEFAULT PA_CHANNEL_MAP_DEFAULT - -typedef pa_sample_format_t ma_pa_sample_format_t; -#define MA_PA_SAMPLE_INVALID PA_SAMPLE_INVALID -#define MA_PA_SAMPLE_U8 PA_SAMPLE_U8 -#define MA_PA_SAMPLE_ALAW PA_SAMPLE_ALAW -#define MA_PA_SAMPLE_ULAW PA_SAMPLE_ULAW -#define MA_PA_SAMPLE_S16LE PA_SAMPLE_S16LE -#define MA_PA_SAMPLE_S16BE PA_SAMPLE_S16BE -#define MA_PA_SAMPLE_FLOAT32LE PA_SAMPLE_FLOAT32LE -#define MA_PA_SAMPLE_FLOAT32BE PA_SAMPLE_FLOAT32BE -#define MA_PA_SAMPLE_S32LE PA_SAMPLE_S32LE -#define MA_PA_SAMPLE_S32BE PA_SAMPLE_S32BE -#define MA_PA_SAMPLE_S24LE PA_SAMPLE_S24LE -#define MA_PA_SAMPLE_S24BE PA_SAMPLE_S24BE -#define MA_PA_SAMPLE_S24_32LE PA_SAMPLE_S24_32LE -#define MA_PA_SAMPLE_S24_32BE PA_SAMPLE_S24_32BE - -typedef pa_mainloop ma_pa_mainloop; -typedef pa_threaded_mainloop ma_pa_threaded_mainloop; -typedef pa_mainloop_api ma_pa_mainloop_api; -typedef pa_context ma_pa_context; -typedef pa_operation ma_pa_operation; -typedef pa_stream ma_pa_stream; -typedef pa_spawn_api ma_pa_spawn_api; -typedef pa_buffer_attr ma_pa_buffer_attr; -typedef pa_channel_map ma_pa_channel_map; -typedef pa_cvolume ma_pa_cvolume; -typedef pa_sample_spec ma_pa_sample_spec; -typedef pa_sink_info ma_pa_sink_info; -typedef pa_source_info ma_pa_source_info; - -typedef pa_context_notify_cb_t ma_pa_context_notify_cb_t; -typedef pa_sink_info_cb_t ma_pa_sink_info_cb_t; -typedef pa_source_info_cb_t ma_pa_source_info_cb_t; -typedef pa_stream_success_cb_t ma_pa_stream_success_cb_t; -typedef pa_stream_request_cb_t ma_pa_stream_request_cb_t; -typedef pa_stream_notify_cb_t ma_pa_stream_notify_cb_t; -typedef pa_free_cb_t ma_pa_free_cb_t; -#else -#define MA_PA_OK 0 -#define MA_PA_ERR_ACCESS 1 -#define MA_PA_ERR_INVALID 2 -#define MA_PA_ERR_NOENTITY 5 -#define MA_PA_ERR_NOTSUPPORTED 19 - -#define MA_PA_CHANNELS_MAX 32 -#define MA_PA_RATE_MAX 384000 - -typedef int ma_pa_context_flags_t; -#define MA_PA_CONTEXT_NOFLAGS 0x00000000 -#define MA_PA_CONTEXT_NOAUTOSPAWN 0x00000001 -#define MA_PA_CONTEXT_NOFAIL 0x00000002 - -typedef int ma_pa_stream_flags_t; -#define MA_PA_STREAM_NOFLAGS 0x00000000 -#define MA_PA_STREAM_START_CORKED 0x00000001 -#define MA_PA_STREAM_INTERPOLATE_TIMING 0x00000002 -#define MA_PA_STREAM_NOT_MONOTONIC 0x00000004 -#define MA_PA_STREAM_AUTO_TIMING_UPDATE 0x00000008 -#define MA_PA_STREAM_NO_REMAP_CHANNELS 0x00000010 -#define MA_PA_STREAM_NO_REMIX_CHANNELS 0x00000020 -#define MA_PA_STREAM_FIX_FORMAT 0x00000040 -#define MA_PA_STREAM_FIX_RATE 0x00000080 -#define MA_PA_STREAM_FIX_CHANNELS 0x00000100 -#define MA_PA_STREAM_DONT_MOVE 0x00000200 -#define MA_PA_STREAM_VARIABLE_RATE 0x00000400 -#define MA_PA_STREAM_PEAK_DETECT 0x00000800 -#define MA_PA_STREAM_START_MUTED 0x00001000 -#define MA_PA_STREAM_ADJUST_LATENCY 0x00002000 -#define MA_PA_STREAM_EARLY_REQUESTS 0x00004000 -#define MA_PA_STREAM_DONT_INHIBIT_AUTO_SUSPEND 0x00008000 -#define MA_PA_STREAM_START_UNMUTED 0x00010000 -#define MA_PA_STREAM_FAIL_ON_SUSPEND 0x00020000 -#define MA_PA_STREAM_RELATIVE_VOLUME 0x00040000 -#define MA_PA_STREAM_PASSTHROUGH 0x00080000 - -typedef int ma_pa_sink_flags_t; -#define MA_PA_SINK_NOFLAGS 0x00000000 -#define MA_PA_SINK_HW_VOLUME_CTRL 0x00000001 -#define MA_PA_SINK_LATENCY 0x00000002 -#define MA_PA_SINK_HARDWARE 0x00000004 -#define MA_PA_SINK_NETWORK 0x00000008 -#define MA_PA_SINK_HW_MUTE_CTRL 0x00000010 -#define MA_PA_SINK_DECIBEL_VOLUME 0x00000020 -#define MA_PA_SINK_FLAT_VOLUME 0x00000040 -#define MA_PA_SINK_DYNAMIC_LATENCY 0x00000080 -#define MA_PA_SINK_SET_FORMATS 0x00000100 - -typedef int ma_pa_source_flags_t; -#define MA_PA_SOURCE_NOFLAGS 0x00000000 -#define MA_PA_SOURCE_HW_VOLUME_CTRL 0x00000001 -#define MA_PA_SOURCE_LATENCY 0x00000002 -#define MA_PA_SOURCE_HARDWARE 0x00000004 -#define MA_PA_SOURCE_NETWORK 0x00000008 -#define MA_PA_SOURCE_HW_MUTE_CTRL 0x00000010 -#define MA_PA_SOURCE_DECIBEL_VOLUME 0x00000020 -#define MA_PA_SOURCE_DYNAMIC_LATENCY 0x00000040 -#define MA_PA_SOURCE_FLAT_VOLUME 0x00000080 - -typedef int ma_pa_context_state_t; -#define MA_PA_CONTEXT_UNCONNECTED 0 -#define MA_PA_CONTEXT_CONNECTING 1 -#define MA_PA_CONTEXT_AUTHORIZING 2 -#define MA_PA_CONTEXT_SETTING_NAME 3 -#define MA_PA_CONTEXT_READY 4 -#define MA_PA_CONTEXT_FAILED 5 -#define MA_PA_CONTEXT_TERMINATED 6 - -typedef int ma_pa_stream_state_t; -#define MA_PA_STREAM_UNCONNECTED 0 -#define MA_PA_STREAM_CREATING 1 -#define MA_PA_STREAM_READY 2 -#define MA_PA_STREAM_FAILED 3 -#define MA_PA_STREAM_TERMINATED 4 - -typedef int ma_pa_operation_state_t; -#define MA_PA_OPERATION_RUNNING 0 -#define MA_PA_OPERATION_DONE 1 -#define MA_PA_OPERATION_CANCELLED 2 - -typedef int ma_pa_sink_state_t; -#define MA_PA_SINK_INVALID_STATE -1 -#define MA_PA_SINK_RUNNING 0 -#define MA_PA_SINK_IDLE 1 -#define MA_PA_SINK_SUSPENDED 2 - -typedef int ma_pa_source_state_t; -#define MA_PA_SOURCE_INVALID_STATE -1 -#define MA_PA_SOURCE_RUNNING 0 -#define MA_PA_SOURCE_IDLE 1 -#define MA_PA_SOURCE_SUSPENDED 2 - -typedef int ma_pa_seek_mode_t; -#define MA_PA_SEEK_RELATIVE 0 -#define MA_PA_SEEK_ABSOLUTE 1 -#define MA_PA_SEEK_RELATIVE_ON_READ 2 -#define MA_PA_SEEK_RELATIVE_END 3 - -typedef int ma_pa_channel_position_t; -#define MA_PA_CHANNEL_POSITION_INVALID -1 -#define MA_PA_CHANNEL_POSITION_MONO 0 -#define MA_PA_CHANNEL_POSITION_FRONT_LEFT 1 -#define MA_PA_CHANNEL_POSITION_FRONT_RIGHT 2 -#define MA_PA_CHANNEL_POSITION_FRONT_CENTER 3 -#define MA_PA_CHANNEL_POSITION_REAR_CENTER 4 -#define MA_PA_CHANNEL_POSITION_REAR_LEFT 5 -#define MA_PA_CHANNEL_POSITION_REAR_RIGHT 6 -#define MA_PA_CHANNEL_POSITION_LFE 7 -#define MA_PA_CHANNEL_POSITION_FRONT_LEFT_OF_CENTER 8 -#define MA_PA_CHANNEL_POSITION_FRONT_RIGHT_OF_CENTER 9 -#define MA_PA_CHANNEL_POSITION_SIDE_LEFT 10 -#define MA_PA_CHANNEL_POSITION_SIDE_RIGHT 11 -#define MA_PA_CHANNEL_POSITION_AUX0 12 -#define MA_PA_CHANNEL_POSITION_AUX1 13 -#define MA_PA_CHANNEL_POSITION_AUX2 14 -#define MA_PA_CHANNEL_POSITION_AUX3 15 -#define MA_PA_CHANNEL_POSITION_AUX4 16 -#define MA_PA_CHANNEL_POSITION_AUX5 17 -#define MA_PA_CHANNEL_POSITION_AUX6 18 -#define MA_PA_CHANNEL_POSITION_AUX7 19 -#define MA_PA_CHANNEL_POSITION_AUX8 20 -#define MA_PA_CHANNEL_POSITION_AUX9 21 -#define MA_PA_CHANNEL_POSITION_AUX10 22 -#define MA_PA_CHANNEL_POSITION_AUX11 23 -#define MA_PA_CHANNEL_POSITION_AUX12 24 -#define MA_PA_CHANNEL_POSITION_AUX13 25 -#define MA_PA_CHANNEL_POSITION_AUX14 26 -#define MA_PA_CHANNEL_POSITION_AUX15 27 -#define MA_PA_CHANNEL_POSITION_AUX16 28 -#define MA_PA_CHANNEL_POSITION_AUX17 29 -#define MA_PA_CHANNEL_POSITION_AUX18 30 -#define MA_PA_CHANNEL_POSITION_AUX19 31 -#define MA_PA_CHANNEL_POSITION_AUX20 32 -#define MA_PA_CHANNEL_POSITION_AUX21 33 -#define MA_PA_CHANNEL_POSITION_AUX22 34 -#define MA_PA_CHANNEL_POSITION_AUX23 35 -#define MA_PA_CHANNEL_POSITION_AUX24 36 -#define MA_PA_CHANNEL_POSITION_AUX25 37 -#define MA_PA_CHANNEL_POSITION_AUX26 38 -#define MA_PA_CHANNEL_POSITION_AUX27 39 -#define MA_PA_CHANNEL_POSITION_AUX28 40 -#define MA_PA_CHANNEL_POSITION_AUX29 41 -#define MA_PA_CHANNEL_POSITION_AUX30 42 -#define MA_PA_CHANNEL_POSITION_AUX31 43 -#define MA_PA_CHANNEL_POSITION_TOP_CENTER 44 -#define MA_PA_CHANNEL_POSITION_TOP_FRONT_LEFT 45 -#define MA_PA_CHANNEL_POSITION_TOP_FRONT_RIGHT 46 -#define MA_PA_CHANNEL_POSITION_TOP_FRONT_CENTER 47 -#define MA_PA_CHANNEL_POSITION_TOP_REAR_LEFT 48 -#define MA_PA_CHANNEL_POSITION_TOP_REAR_RIGHT 49 -#define MA_PA_CHANNEL_POSITION_TOP_REAR_CENTER 50 -#define MA_PA_CHANNEL_POSITION_LEFT MA_PA_CHANNEL_POSITION_FRONT_LEFT -#define MA_PA_CHANNEL_POSITION_RIGHT MA_PA_CHANNEL_POSITION_FRONT_RIGHT -#define MA_PA_CHANNEL_POSITION_CENTER MA_PA_CHANNEL_POSITION_FRONT_CENTER -#define MA_PA_CHANNEL_POSITION_SUBWOOFER MA_PA_CHANNEL_POSITION_LFE - -typedef int ma_pa_channel_map_def_t; -#define MA_PA_CHANNEL_MAP_AIFF 0 -#define MA_PA_CHANNEL_MAP_ALSA 1 -#define MA_PA_CHANNEL_MAP_AUX 2 -#define MA_PA_CHANNEL_MAP_WAVEEX 3 -#define MA_PA_CHANNEL_MAP_OSS 4 -#define MA_PA_CHANNEL_MAP_DEFAULT MA_PA_CHANNEL_MAP_AIFF - -typedef int ma_pa_sample_format_t; -#define MA_PA_SAMPLE_INVALID -1 -#define MA_PA_SAMPLE_U8 0 -#define MA_PA_SAMPLE_ALAW 1 -#define MA_PA_SAMPLE_ULAW 2 -#define MA_PA_SAMPLE_S16LE 3 -#define MA_PA_SAMPLE_S16BE 4 -#define MA_PA_SAMPLE_FLOAT32LE 5 -#define MA_PA_SAMPLE_FLOAT32BE 6 -#define MA_PA_SAMPLE_S32LE 7 -#define MA_PA_SAMPLE_S32BE 8 -#define MA_PA_SAMPLE_S24LE 9 -#define MA_PA_SAMPLE_S24BE 10 -#define MA_PA_SAMPLE_S24_32LE 11 -#define MA_PA_SAMPLE_S24_32BE 12 - -typedef struct ma_pa_mainloop ma_pa_mainloop; -typedef struct ma_pa_threaded_mainloop ma_pa_threaded_mainloop; -typedef struct ma_pa_mainloop_api ma_pa_mainloop_api; -typedef struct ma_pa_context ma_pa_context; -typedef struct ma_pa_operation ma_pa_operation; -typedef struct ma_pa_stream ma_pa_stream; -typedef struct ma_pa_spawn_api ma_pa_spawn_api; - -typedef struct -{ - ma_uint32 maxlength; - ma_uint32 tlength; - ma_uint32 prebuf; - ma_uint32 minreq; - ma_uint32 fragsize; -} ma_pa_buffer_attr; - -typedef struct -{ - ma_uint8 channels; - ma_pa_channel_position_t map[MA_PA_CHANNELS_MAX]; -} ma_pa_channel_map; - -typedef struct -{ - ma_uint8 channels; - ma_uint32 values[MA_PA_CHANNELS_MAX]; -} ma_pa_cvolume; - -typedef struct -{ - ma_pa_sample_format_t format; - ma_uint32 rate; - ma_uint8 channels; -} ma_pa_sample_spec; - -typedef struct -{ - const char* name; - ma_uint32 index; - const char* description; - ma_pa_sample_spec sample_spec; - ma_pa_channel_map channel_map; - ma_uint32 owner_module; - ma_pa_cvolume volume; - int mute; - ma_uint32 monitor_source; - const char* monitor_source_name; - ma_uint64 latency; - const char* driver; - ma_pa_sink_flags_t flags; - void* proplist; - ma_uint64 configured_latency; - ma_uint32 base_volume; - ma_pa_sink_state_t state; - ma_uint32 n_volume_steps; - ma_uint32 card; - ma_uint32 n_ports; - void** ports; - void* active_port; - ma_uint8 n_formats; - void** formats; -} ma_pa_sink_info; - -typedef struct -{ - const char *name; - ma_uint32 index; - const char *description; - ma_pa_sample_spec sample_spec; - ma_pa_channel_map channel_map; - ma_uint32 owner_module; - ma_pa_cvolume volume; - int mute; - ma_uint32 monitor_of_sink; - const char *monitor_of_sink_name; - ma_uint64 latency; - const char *driver; - ma_pa_source_flags_t flags; - void* proplist; - ma_uint64 configured_latency; - ma_uint32 base_volume; - ma_pa_source_state_t state; - ma_uint32 n_volume_steps; - ma_uint32 card; - ma_uint32 n_ports; - void** ports; - void* active_port; - ma_uint8 n_formats; - void** formats; -} ma_pa_source_info; - -typedef void (* ma_pa_context_notify_cb_t)(ma_pa_context* c, void* userdata); -typedef void (* ma_pa_sink_info_cb_t) (ma_pa_context* c, const ma_pa_sink_info* i, int eol, void* userdata); -typedef void (* ma_pa_source_info_cb_t) (ma_pa_context* c, const ma_pa_source_info* i, int eol, void* userdata); -typedef void (* ma_pa_stream_success_cb_t)(ma_pa_stream* s, int success, void* userdata); -typedef void (* ma_pa_stream_request_cb_t)(ma_pa_stream* s, size_t nbytes, void* userdata); -typedef void (* ma_pa_stream_notify_cb_t) (ma_pa_stream* s, void* userdata); -typedef void (* ma_pa_free_cb_t) (void* p); -#endif - - -typedef ma_pa_mainloop* (* ma_pa_mainloop_new_proc) (void); -typedef void (* ma_pa_mainloop_free_proc) (ma_pa_mainloop* m); -typedef void (* ma_pa_mainloop_quit_proc) (ma_pa_mainloop* m, int retval); -typedef ma_pa_mainloop_api* (* ma_pa_mainloop_get_api_proc) (ma_pa_mainloop* m); -typedef int (* ma_pa_mainloop_iterate_proc) (ma_pa_mainloop* m, int block, int* retval); -typedef void (* ma_pa_mainloop_wakeup_proc) (ma_pa_mainloop* m); -typedef ma_pa_threaded_mainloop* (* ma_pa_threaded_mainloop_new_proc) (void); -typedef void (* ma_pa_threaded_mainloop_free_proc) (ma_pa_threaded_mainloop* m); -typedef int (* ma_pa_threaded_mainloop_start_proc) (ma_pa_threaded_mainloop* m); -typedef void (* ma_pa_threaded_mainloop_stop_proc) (ma_pa_threaded_mainloop* m); -typedef void (* ma_pa_threaded_mainloop_lock_proc) (ma_pa_threaded_mainloop* m); -typedef void (* ma_pa_threaded_mainloop_unlock_proc) (ma_pa_threaded_mainloop* m); -typedef void (* ma_pa_threaded_mainloop_wait_proc) (ma_pa_threaded_mainloop* m); -typedef void (* ma_pa_threaded_mainloop_signal_proc) (ma_pa_threaded_mainloop* m, int wait_for_accept); -typedef void (* ma_pa_threaded_mainloop_accept_proc) (ma_pa_threaded_mainloop* m); -typedef int (* ma_pa_threaded_mainloop_get_retval_proc) (ma_pa_threaded_mainloop* m); -typedef ma_pa_mainloop_api* (* ma_pa_threaded_mainloop_get_api_proc) (ma_pa_threaded_mainloop* m); -typedef int (* ma_pa_threaded_mainloop_in_thread_proc) (ma_pa_threaded_mainloop* m); -typedef void (* ma_pa_threaded_mainloop_set_name_proc) (ma_pa_threaded_mainloop* m, const char* name); -typedef ma_pa_context* (* ma_pa_context_new_proc) (ma_pa_mainloop_api* mainloop, const char* name); -typedef void (* ma_pa_context_unref_proc) (ma_pa_context* c); -typedef int (* ma_pa_context_connect_proc) (ma_pa_context* c, const char* server, ma_pa_context_flags_t flags, const ma_pa_spawn_api* api); -typedef void (* ma_pa_context_disconnect_proc) (ma_pa_context* c); -typedef void (* ma_pa_context_set_state_callback_proc) (ma_pa_context* c, ma_pa_context_notify_cb_t cb, void* userdata); -typedef ma_pa_context_state_t (* ma_pa_context_get_state_proc) (ma_pa_context* c); -typedef ma_pa_operation* (* ma_pa_context_get_sink_info_list_proc) (ma_pa_context* c, ma_pa_sink_info_cb_t cb, void* userdata); -typedef ma_pa_operation* (* ma_pa_context_get_source_info_list_proc) (ma_pa_context* c, ma_pa_source_info_cb_t cb, void* userdata); -typedef ma_pa_operation* (* ma_pa_context_get_sink_info_by_name_proc) (ma_pa_context* c, const char* name, ma_pa_sink_info_cb_t cb, void* userdata); -typedef ma_pa_operation* (* ma_pa_context_get_source_info_by_name_proc)(ma_pa_context* c, const char* name, ma_pa_source_info_cb_t cb, void* userdata); -typedef void (* ma_pa_operation_unref_proc) (ma_pa_operation* o); -typedef ma_pa_operation_state_t (* ma_pa_operation_get_state_proc) (ma_pa_operation* o); -typedef ma_pa_channel_map* (* ma_pa_channel_map_init_extend_proc) (ma_pa_channel_map* m, unsigned channels, ma_pa_channel_map_def_t def); -typedef int (* ma_pa_channel_map_valid_proc) (const ma_pa_channel_map* m); -typedef int (* ma_pa_channel_map_compatible_proc) (const ma_pa_channel_map* m, const ma_pa_sample_spec* ss); -typedef ma_pa_stream* (* ma_pa_stream_new_proc) (ma_pa_context* c, const char* name, const ma_pa_sample_spec* ss, const ma_pa_channel_map* map); -typedef void (* ma_pa_stream_unref_proc) (ma_pa_stream* s); -typedef int (* ma_pa_stream_connect_playback_proc) (ma_pa_stream* s, const char* dev, const ma_pa_buffer_attr* attr, ma_pa_stream_flags_t flags, const ma_pa_cvolume* volume, ma_pa_stream* sync_stream); -typedef int (* ma_pa_stream_connect_record_proc) (ma_pa_stream* s, const char* dev, const ma_pa_buffer_attr* attr, ma_pa_stream_flags_t flags); -typedef int (* ma_pa_stream_disconnect_proc) (ma_pa_stream* s); -typedef ma_pa_stream_state_t (* ma_pa_stream_get_state_proc) (ma_pa_stream* s); -typedef const ma_pa_sample_spec* (* ma_pa_stream_get_sample_spec_proc) (ma_pa_stream* s); -typedef const ma_pa_channel_map* (* ma_pa_stream_get_channel_map_proc) (ma_pa_stream* s); -typedef const ma_pa_buffer_attr* (* ma_pa_stream_get_buffer_attr_proc) (ma_pa_stream* s); -typedef ma_pa_operation* (* ma_pa_stream_set_buffer_attr_proc) (ma_pa_stream* s, const ma_pa_buffer_attr* attr, ma_pa_stream_success_cb_t cb, void* userdata); -typedef const char* (* ma_pa_stream_get_device_name_proc) (ma_pa_stream* s); -typedef void (* ma_pa_stream_set_write_callback_proc) (ma_pa_stream* s, ma_pa_stream_request_cb_t cb, void* userdata); -typedef void (* ma_pa_stream_set_read_callback_proc) (ma_pa_stream* s, ma_pa_stream_request_cb_t cb, void* userdata); -typedef void (* ma_pa_stream_set_suspended_callback_proc) (ma_pa_stream* s, ma_pa_stream_notify_cb_t cb, void* userdata); -typedef void (* ma_pa_stream_set_moved_callback_proc) (ma_pa_stream* s, ma_pa_stream_notify_cb_t cb, void* userdata); -typedef int (* ma_pa_stream_is_suspended_proc) (const ma_pa_stream* s); -typedef ma_pa_operation* (* ma_pa_stream_flush_proc) (ma_pa_stream* s, ma_pa_stream_success_cb_t cb, void* userdata); -typedef ma_pa_operation* (* ma_pa_stream_drain_proc) (ma_pa_stream* s, ma_pa_stream_success_cb_t cb, void* userdata); -typedef int (* ma_pa_stream_is_corked_proc) (ma_pa_stream* s); -typedef ma_pa_operation* (* ma_pa_stream_cork_proc) (ma_pa_stream* s, int b, ma_pa_stream_success_cb_t cb, void* userdata); -typedef ma_pa_operation* (* ma_pa_stream_trigger_proc) (ma_pa_stream* s, ma_pa_stream_success_cb_t cb, void* userdata); -typedef int (* ma_pa_stream_begin_write_proc) (ma_pa_stream* s, void** data, size_t* nbytes); -typedef int (* ma_pa_stream_write_proc) (ma_pa_stream* s, const void* data, size_t nbytes, ma_pa_free_cb_t free_cb, int64_t offset, ma_pa_seek_mode_t seek); -typedef int (* ma_pa_stream_peek_proc) (ma_pa_stream* s, const void** data, size_t* nbytes); -typedef int (* ma_pa_stream_drop_proc) (ma_pa_stream* s); -typedef size_t (* ma_pa_stream_writable_size_proc) (ma_pa_stream* s); -typedef size_t (* ma_pa_stream_readable_size_proc) (ma_pa_stream* s); - -typedef struct -{ - ma_uint32 count; - ma_uint32 capacity; - ma_device_info* pInfo; -} ma_pulse_device_enum_data; - -static ma_result ma_result_from_pulse(int result) -{ - if (result < 0) { - return MA_ERROR; - } - - switch (result) { - case MA_PA_OK: return MA_SUCCESS; - case MA_PA_ERR_ACCESS: return MA_ACCESS_DENIED; - case MA_PA_ERR_INVALID: return MA_INVALID_ARGS; - case MA_PA_ERR_NOENTITY: return MA_NO_DEVICE; - default: return MA_ERROR; - } -} - -#if 0 -static ma_pa_sample_format_t ma_format_to_pulse(ma_format format) -{ - if (ma_is_little_endian()) { - switch (format) { - case ma_format_s16: return MA_PA_SAMPLE_S16LE; - case ma_format_s24: return MA_PA_SAMPLE_S24LE; - case ma_format_s32: return MA_PA_SAMPLE_S32LE; - case ma_format_f32: return MA_PA_SAMPLE_FLOAT32LE; - default: break; - } - } else { - switch (format) { - case ma_format_s16: return MA_PA_SAMPLE_S16BE; - case ma_format_s24: return MA_PA_SAMPLE_S24BE; - case ma_format_s32: return MA_PA_SAMPLE_S32BE; - case ma_format_f32: return MA_PA_SAMPLE_FLOAT32BE; - default: break; - } - } - - /* Endian agnostic. */ - switch (format) { - case ma_format_u8: return MA_PA_SAMPLE_U8; - default: return MA_PA_SAMPLE_INVALID; - } -} -#endif - -static ma_format ma_format_from_pulse(ma_pa_sample_format_t format) -{ - if (ma_is_little_endian()) { - switch (format) { - case MA_PA_SAMPLE_S16LE: return ma_format_s16; - case MA_PA_SAMPLE_S24LE: return ma_format_s24; - case MA_PA_SAMPLE_S32LE: return ma_format_s32; - case MA_PA_SAMPLE_FLOAT32LE: return ma_format_f32; - default: break; - } - } else { - switch (format) { - case MA_PA_SAMPLE_S16BE: return ma_format_s16; - case MA_PA_SAMPLE_S24BE: return ma_format_s24; - case MA_PA_SAMPLE_S32BE: return ma_format_s32; - case MA_PA_SAMPLE_FLOAT32BE: return ma_format_f32; - default: break; - } - } - - /* Endian agnostic. */ - switch (format) { - case MA_PA_SAMPLE_U8: return ma_format_u8; - default: return ma_format_unknown; - } -} - -static ma_channel ma_channel_position_from_pulse(ma_pa_channel_position_t position) -{ - switch (position) - { - case MA_PA_CHANNEL_POSITION_INVALID: return MA_CHANNEL_NONE; - case MA_PA_CHANNEL_POSITION_MONO: return MA_CHANNEL_MONO; - case MA_PA_CHANNEL_POSITION_FRONT_LEFT: return MA_CHANNEL_FRONT_LEFT; - case MA_PA_CHANNEL_POSITION_FRONT_RIGHT: return MA_CHANNEL_FRONT_RIGHT; - case MA_PA_CHANNEL_POSITION_FRONT_CENTER: return MA_CHANNEL_FRONT_CENTER; - case MA_PA_CHANNEL_POSITION_REAR_CENTER: return MA_CHANNEL_BACK_CENTER; - case MA_PA_CHANNEL_POSITION_REAR_LEFT: return MA_CHANNEL_BACK_LEFT; - case MA_PA_CHANNEL_POSITION_REAR_RIGHT: return MA_CHANNEL_BACK_RIGHT; - case MA_PA_CHANNEL_POSITION_LFE: return MA_CHANNEL_LFE; - case MA_PA_CHANNEL_POSITION_FRONT_LEFT_OF_CENTER: return MA_CHANNEL_FRONT_LEFT_CENTER; - case MA_PA_CHANNEL_POSITION_FRONT_RIGHT_OF_CENTER: return MA_CHANNEL_FRONT_RIGHT_CENTER; - case MA_PA_CHANNEL_POSITION_SIDE_LEFT: return MA_CHANNEL_SIDE_LEFT; - case MA_PA_CHANNEL_POSITION_SIDE_RIGHT: return MA_CHANNEL_SIDE_RIGHT; - case MA_PA_CHANNEL_POSITION_AUX0: return MA_CHANNEL_AUX_0; - case MA_PA_CHANNEL_POSITION_AUX1: return MA_CHANNEL_AUX_1; - case MA_PA_CHANNEL_POSITION_AUX2: return MA_CHANNEL_AUX_2; - case MA_PA_CHANNEL_POSITION_AUX3: return MA_CHANNEL_AUX_3; - case MA_PA_CHANNEL_POSITION_AUX4: return MA_CHANNEL_AUX_4; - case MA_PA_CHANNEL_POSITION_AUX5: return MA_CHANNEL_AUX_5; - case MA_PA_CHANNEL_POSITION_AUX6: return MA_CHANNEL_AUX_6; - case MA_PA_CHANNEL_POSITION_AUX7: return MA_CHANNEL_AUX_7; - case MA_PA_CHANNEL_POSITION_AUX8: return MA_CHANNEL_AUX_8; - case MA_PA_CHANNEL_POSITION_AUX9: return MA_CHANNEL_AUX_9; - case MA_PA_CHANNEL_POSITION_AUX10: return MA_CHANNEL_AUX_10; - case MA_PA_CHANNEL_POSITION_AUX11: return MA_CHANNEL_AUX_11; - case MA_PA_CHANNEL_POSITION_AUX12: return MA_CHANNEL_AUX_12; - case MA_PA_CHANNEL_POSITION_AUX13: return MA_CHANNEL_AUX_13; - case MA_PA_CHANNEL_POSITION_AUX14: return MA_CHANNEL_AUX_14; - case MA_PA_CHANNEL_POSITION_AUX15: return MA_CHANNEL_AUX_15; - case MA_PA_CHANNEL_POSITION_AUX16: return MA_CHANNEL_AUX_16; - case MA_PA_CHANNEL_POSITION_AUX17: return MA_CHANNEL_AUX_17; - case MA_PA_CHANNEL_POSITION_AUX18: return MA_CHANNEL_AUX_18; - case MA_PA_CHANNEL_POSITION_AUX19: return MA_CHANNEL_AUX_19; - case MA_PA_CHANNEL_POSITION_AUX20: return MA_CHANNEL_AUX_20; - case MA_PA_CHANNEL_POSITION_AUX21: return MA_CHANNEL_AUX_21; - case MA_PA_CHANNEL_POSITION_AUX22: return MA_CHANNEL_AUX_22; - case MA_PA_CHANNEL_POSITION_AUX23: return MA_CHANNEL_AUX_23; - case MA_PA_CHANNEL_POSITION_AUX24: return MA_CHANNEL_AUX_24; - case MA_PA_CHANNEL_POSITION_AUX25: return MA_CHANNEL_AUX_25; - case MA_PA_CHANNEL_POSITION_AUX26: return MA_CHANNEL_AUX_26; - case MA_PA_CHANNEL_POSITION_AUX27: return MA_CHANNEL_AUX_27; - case MA_PA_CHANNEL_POSITION_AUX28: return MA_CHANNEL_AUX_28; - case MA_PA_CHANNEL_POSITION_AUX29: return MA_CHANNEL_AUX_29; - case MA_PA_CHANNEL_POSITION_AUX30: return MA_CHANNEL_AUX_30; - case MA_PA_CHANNEL_POSITION_AUX31: return MA_CHANNEL_AUX_31; - case MA_PA_CHANNEL_POSITION_TOP_CENTER: return MA_CHANNEL_TOP_CENTER; - case MA_PA_CHANNEL_POSITION_TOP_FRONT_LEFT: return MA_CHANNEL_TOP_FRONT_LEFT; - case MA_PA_CHANNEL_POSITION_TOP_FRONT_RIGHT: return MA_CHANNEL_TOP_FRONT_RIGHT; - case MA_PA_CHANNEL_POSITION_TOP_FRONT_CENTER: return MA_CHANNEL_TOP_FRONT_CENTER; - case MA_PA_CHANNEL_POSITION_TOP_REAR_LEFT: return MA_CHANNEL_TOP_BACK_LEFT; - case MA_PA_CHANNEL_POSITION_TOP_REAR_RIGHT: return MA_CHANNEL_TOP_BACK_RIGHT; - case MA_PA_CHANNEL_POSITION_TOP_REAR_CENTER: return MA_CHANNEL_TOP_BACK_CENTER; - default: return MA_CHANNEL_NONE; - } -} - -#if 0 -static ma_pa_channel_position_t ma_channel_position_to_pulse(ma_channel position) -{ - switch (position) - { - case MA_CHANNEL_NONE: return MA_PA_CHANNEL_POSITION_INVALID; - case MA_CHANNEL_FRONT_LEFT: return MA_PA_CHANNEL_POSITION_FRONT_LEFT; - case MA_CHANNEL_FRONT_RIGHT: return MA_PA_CHANNEL_POSITION_FRONT_RIGHT; - case MA_CHANNEL_FRONT_CENTER: return MA_PA_CHANNEL_POSITION_FRONT_CENTER; - case MA_CHANNEL_LFE: return MA_PA_CHANNEL_POSITION_LFE; - case MA_CHANNEL_BACK_LEFT: return MA_PA_CHANNEL_POSITION_REAR_LEFT; - case MA_CHANNEL_BACK_RIGHT: return MA_PA_CHANNEL_POSITION_REAR_RIGHT; - case MA_CHANNEL_FRONT_LEFT_CENTER: return MA_PA_CHANNEL_POSITION_FRONT_LEFT_OF_CENTER; - case MA_CHANNEL_FRONT_RIGHT_CENTER: return MA_PA_CHANNEL_POSITION_FRONT_RIGHT_OF_CENTER; - case MA_CHANNEL_BACK_CENTER: return MA_PA_CHANNEL_POSITION_REAR_CENTER; - case MA_CHANNEL_SIDE_LEFT: return MA_PA_CHANNEL_POSITION_SIDE_LEFT; - case MA_CHANNEL_SIDE_RIGHT: return MA_PA_CHANNEL_POSITION_SIDE_RIGHT; - case MA_CHANNEL_TOP_CENTER: return MA_PA_CHANNEL_POSITION_TOP_CENTER; - case MA_CHANNEL_TOP_FRONT_LEFT: return MA_PA_CHANNEL_POSITION_TOP_FRONT_LEFT; - case MA_CHANNEL_TOP_FRONT_CENTER: return MA_PA_CHANNEL_POSITION_TOP_FRONT_CENTER; - case MA_CHANNEL_TOP_FRONT_RIGHT: return MA_PA_CHANNEL_POSITION_TOP_FRONT_RIGHT; - case MA_CHANNEL_TOP_BACK_LEFT: return MA_PA_CHANNEL_POSITION_TOP_REAR_LEFT; - case MA_CHANNEL_TOP_BACK_CENTER: return MA_PA_CHANNEL_POSITION_TOP_REAR_CENTER; - case MA_CHANNEL_TOP_BACK_RIGHT: return MA_PA_CHANNEL_POSITION_TOP_REAR_RIGHT; - case MA_CHANNEL_19: return MA_PA_CHANNEL_POSITION_AUX18; - case MA_CHANNEL_20: return MA_PA_CHANNEL_POSITION_AUX19; - case MA_CHANNEL_21: return MA_PA_CHANNEL_POSITION_AUX20; - case MA_CHANNEL_22: return MA_PA_CHANNEL_POSITION_AUX21; - case MA_CHANNEL_23: return MA_PA_CHANNEL_POSITION_AUX22; - case MA_CHANNEL_24: return MA_PA_CHANNEL_POSITION_AUX23; - case MA_CHANNEL_25: return MA_PA_CHANNEL_POSITION_AUX24; - case MA_CHANNEL_26: return MA_PA_CHANNEL_POSITION_AUX25; - case MA_CHANNEL_27: return MA_PA_CHANNEL_POSITION_AUX26; - case MA_CHANNEL_28: return MA_PA_CHANNEL_POSITION_AUX27; - case MA_CHANNEL_29: return MA_PA_CHANNEL_POSITION_AUX28; - case MA_CHANNEL_30: return MA_PA_CHANNEL_POSITION_AUX29; - case MA_CHANNEL_31: return MA_PA_CHANNEL_POSITION_AUX30; - case MA_CHANNEL_32: return MA_PA_CHANNEL_POSITION_AUX31; - default: return (ma_pa_channel_position_t)position; - } -} -#endif - -static ma_result ma_wait_for_operation__pulse(ma_context* pContext, ma_ptr pMainLoop, ma_pa_operation* pOP) -{ - int resultPA; - ma_pa_operation_state_t state; - - MA_ASSERT(pContext != NULL); - MA_ASSERT(pOP != NULL); - - for (;;) { - state = ((ma_pa_operation_get_state_proc)pContext->pulse.pa_operation_get_state)(pOP); - if (state != MA_PA_OPERATION_RUNNING) { - break; /* Done. */ - } - - resultPA = ((ma_pa_mainloop_iterate_proc)pContext->pulse.pa_mainloop_iterate)((ma_pa_mainloop*)pMainLoop, 1, NULL); - if (resultPA < 0) { - return ma_result_from_pulse(resultPA); - } - } - - return MA_SUCCESS; -} - -static ma_result ma_wait_for_operation_and_unref__pulse(ma_context* pContext, ma_ptr pMainLoop, ma_pa_operation* pOP) -{ - ma_result result; - - if (pOP == NULL) { - return MA_INVALID_ARGS; - } - - result = ma_wait_for_operation__pulse(pContext, pMainLoop, pOP); - ((ma_pa_operation_unref_proc)pContext->pulse.pa_operation_unref)(pOP); - - return result; -} - -static ma_result ma_wait_for_pa_context_to_connect__pulse(ma_context* pContext, ma_ptr pMainLoop, ma_ptr pPulseContext) -{ - int resultPA; - ma_pa_context_state_t state; - - for (;;) { - state = ((ma_pa_context_get_state_proc)pContext->pulse.pa_context_get_state)((ma_pa_context*)pPulseContext); - if (state == MA_PA_CONTEXT_READY) { - break; /* Done. */ - } - - if (state == MA_PA_CONTEXT_FAILED || state == MA_PA_CONTEXT_TERMINATED) { - ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[PulseAudio] An error occurred while connecting the PulseAudio context."); - return MA_ERROR; - } - - resultPA = ((ma_pa_mainloop_iterate_proc)pContext->pulse.pa_mainloop_iterate)((ma_pa_mainloop*)pMainLoop, 1, NULL); - if (resultPA < 0) { - return ma_result_from_pulse(resultPA); - } - } - - /* Should never get here. */ - return MA_SUCCESS; -} - -static ma_result ma_wait_for_pa_stream_to_connect__pulse(ma_context* pContext, ma_ptr pMainLoop, ma_ptr pStream) -{ - int resultPA; - ma_pa_stream_state_t state; - - for (;;) { - state = ((ma_pa_stream_get_state_proc)pContext->pulse.pa_stream_get_state)((ma_pa_stream*)pStream); - if (state == MA_PA_STREAM_READY) { - break; /* Done. */ - } - - if (state == MA_PA_STREAM_FAILED || state == MA_PA_STREAM_TERMINATED) { - ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[PulseAudio] An error occurred while connecting the PulseAudio stream."); - return MA_ERROR; - } - - resultPA = ((ma_pa_mainloop_iterate_proc)pContext->pulse.pa_mainloop_iterate)((ma_pa_mainloop*)pMainLoop, 1, NULL); - if (resultPA < 0) { - return ma_result_from_pulse(resultPA); - } - } - - return MA_SUCCESS; -} - - -static ma_result ma_init_pa_mainloop_and_pa_context__pulse(ma_context* pContext, const char* pApplicationName, const char* pServerName, ma_bool32 tryAutoSpawn, ma_ptr* ppMainLoop, ma_ptr* ppPulseContext) -{ - ma_result result; - ma_ptr pMainLoop; - ma_ptr pPulseContext; - - MA_ASSERT(ppMainLoop != NULL); - MA_ASSERT(ppPulseContext != NULL); - - /* The PulseAudio context maps well to miniaudio's notion of a context. The pa_context object will be initialized as part of the ma_context. */ - pMainLoop = ((ma_pa_mainloop_new_proc)pContext->pulse.pa_mainloop_new)(); - if (pMainLoop == NULL) { - ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[PulseAudio] Failed to create mainloop."); - return MA_FAILED_TO_INIT_BACKEND; - } - - pPulseContext = ((ma_pa_context_new_proc)pContext->pulse.pa_context_new)(((ma_pa_mainloop_get_api_proc)pContext->pulse.pa_mainloop_get_api)((ma_pa_mainloop*)pMainLoop), pApplicationName); - if (pPulseContext == NULL) { - ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[PulseAudio] Failed to create PulseAudio context."); - ((ma_pa_mainloop_free_proc)pContext->pulse.pa_mainloop_free)((ma_pa_mainloop*)(pMainLoop)); - return MA_FAILED_TO_INIT_BACKEND; - } - - /* Now we need to connect to the context. Everything is asynchronous so we need to wait for it to connect before returning. */ - result = ma_result_from_pulse(((ma_pa_context_connect_proc)pContext->pulse.pa_context_connect)((ma_pa_context*)pPulseContext, pServerName, (tryAutoSpawn) ? 0 : MA_PA_CONTEXT_NOAUTOSPAWN, NULL)); - if (result != MA_SUCCESS) { - ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[PulseAudio] Failed to connect PulseAudio context."); - ((ma_pa_mainloop_free_proc)pContext->pulse.pa_mainloop_free)((ma_pa_mainloop*)(pMainLoop)); - return result; - } - - /* Since ma_context_init() runs synchronously we need to wait for the PulseAudio context to connect before we return. */ - result = ma_wait_for_pa_context_to_connect__pulse(pContext, pMainLoop, pPulseContext); - if (result != MA_SUCCESS) { - ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[PulseAudio] Waiting for connection failed."); - ((ma_pa_mainloop_free_proc)pContext->pulse.pa_mainloop_free)((ma_pa_mainloop*)(pMainLoop)); - return result; - } - - *ppMainLoop = pMainLoop; - *ppPulseContext = pPulseContext; - - return MA_SUCCESS; -} - - -static void ma_device_sink_info_callback(ma_pa_context* pPulseContext, const ma_pa_sink_info* pInfo, int endOfList, void* pUserData) -{ - ma_pa_sink_info* pInfoOut; - - if (endOfList > 0) { - return; - } - - /* - There has been a report that indicates that pInfo can be null which results - in a null pointer dereference below. We'll check for this for safety. - */ - if (pInfo == NULL) { - return; - } - - pInfoOut = (ma_pa_sink_info*)pUserData; - MA_ASSERT(pInfoOut != NULL); - - *pInfoOut = *pInfo; - - (void)pPulseContext; /* Unused. */ -} - -static void ma_device_source_info_callback(ma_pa_context* pPulseContext, const ma_pa_source_info* pInfo, int endOfList, void* pUserData) -{ - ma_pa_source_info* pInfoOut; - - if (endOfList > 0) { - return; - } - - /* - There has been a report that indicates that pInfo can be null which results - in a null pointer dereference below. We'll check for this for safety. - */ - if (pInfo == NULL) { - return; - } - - pInfoOut = (ma_pa_source_info*)pUserData; - MA_ASSERT(pInfoOut != NULL); - - *pInfoOut = *pInfo; - - (void)pPulseContext; /* Unused. */ -} - -#if 0 -static void ma_device_sink_name_callback(ma_pa_context* pPulseContext, const ma_pa_sink_info* pInfo, int endOfList, void* pUserData) -{ - ma_device* pDevice; - - if (endOfList > 0) { - return; - } - - pDevice = (ma_device*)pUserData; - MA_ASSERT(pDevice != NULL); - - ma_strncpy_s(pDevice->playback.name, sizeof(pDevice->playback.name), pInfo->description, (size_t)-1); - - (void)pPulseContext; /* Unused. */ -} - -static void ma_device_source_name_callback(ma_pa_context* pPulseContext, const ma_pa_source_info* pInfo, int endOfList, void* pUserData) -{ - ma_device* pDevice; - - if (endOfList > 0) { - return; - } - - pDevice = (ma_device*)pUserData; - MA_ASSERT(pDevice != NULL); - - ma_strncpy_s(pDevice->capture.name, sizeof(pDevice->capture.name), pInfo->description, (size_t)-1); - - (void)pPulseContext; /* Unused. */ -} -#endif - -static ma_result ma_context_get_sink_info__pulse(ma_context* pContext, const char* pDeviceName, ma_pa_sink_info* pSinkInfo) -{ - ma_pa_operation* pOP; - - pOP = ((ma_pa_context_get_sink_info_by_name_proc)pContext->pulse.pa_context_get_sink_info_by_name)((ma_pa_context*)pContext->pulse.pPulseContext, pDeviceName, ma_device_sink_info_callback, pSinkInfo); - if (pOP == NULL) { - return MA_ERROR; - } - - return ma_wait_for_operation_and_unref__pulse(pContext, pContext->pulse.pMainLoop, pOP); -} - -static ma_result ma_context_get_source_info__pulse(ma_context* pContext, const char* pDeviceName, ma_pa_source_info* pSourceInfo) -{ - ma_pa_operation* pOP; - - pOP = ((ma_pa_context_get_source_info_by_name_proc)pContext->pulse.pa_context_get_source_info_by_name)((ma_pa_context*)pContext->pulse.pPulseContext, pDeviceName, ma_device_source_info_callback, pSourceInfo); - if (pOP == NULL) { - return MA_ERROR; - } - - return ma_wait_for_operation_and_unref__pulse(pContext, pContext->pulse.pMainLoop, pOP); -} - -static ma_result ma_context_get_default_device_index__pulse(ma_context* pContext, ma_device_type deviceType, ma_uint32* pIndex) -{ - ma_result result; - - MA_ASSERT(pContext != NULL); - MA_ASSERT(pIndex != NULL); - - if (pIndex != NULL) { - *pIndex = (ma_uint32)-1; - } - - if (deviceType == ma_device_type_playback) { - ma_pa_sink_info sinkInfo; - result = ma_context_get_sink_info__pulse(pContext, NULL, &sinkInfo); - if (result != MA_SUCCESS) { - return result; - } - - if (pIndex != NULL) { - *pIndex = sinkInfo.index; - } - } - - if (deviceType == ma_device_type_capture) { - ma_pa_source_info sourceInfo; - result = ma_context_get_source_info__pulse(pContext, NULL, &sourceInfo); - if (result != MA_SUCCESS) { - return result; - } - - if (pIndex != NULL) { - *pIndex = sourceInfo.index; - } - } - - return MA_SUCCESS; -} - - -typedef struct -{ - ma_context* pContext; - ma_enum_devices_callback_proc callback; - void* pUserData; - ma_bool32 isTerminated; - ma_uint32 defaultDeviceIndexPlayback; - ma_uint32 defaultDeviceIndexCapture; -} ma_context_enumerate_devices_callback_data__pulse; - -static void ma_context_enumerate_devices_sink_callback__pulse(ma_pa_context* pPulseContext, const ma_pa_sink_info* pSinkInfo, int endOfList, void* pUserData) -{ - ma_context_enumerate_devices_callback_data__pulse* pData = (ma_context_enumerate_devices_callback_data__pulse*)pUserData; - ma_device_info deviceInfo; - - MA_ASSERT(pData != NULL); - - if (endOfList || pData->isTerminated) { - return; - } - - MA_ZERO_OBJECT(&deviceInfo); - - /* The name from PulseAudio is the ID for miniaudio. */ - if (pSinkInfo->name != NULL) { - ma_strncpy_s(deviceInfo.id.pulse, sizeof(deviceInfo.id.pulse), pSinkInfo->name, (size_t)-1); - } - - /* The description from PulseAudio is the name for miniaudio. */ - if (pSinkInfo->description != NULL) { - ma_strncpy_s(deviceInfo.name, sizeof(deviceInfo.name), pSinkInfo->description, (size_t)-1); - } - - if (pSinkInfo->index == pData->defaultDeviceIndexPlayback) { - deviceInfo.isDefault = MA_TRUE; - } - - pData->isTerminated = !pData->callback(pData->pContext, ma_device_type_playback, &deviceInfo, pData->pUserData); - - (void)pPulseContext; /* Unused. */ -} - -static void ma_context_enumerate_devices_source_callback__pulse(ma_pa_context* pPulseContext, const ma_pa_source_info* pSourceInfo, int endOfList, void* pUserData) -{ - ma_context_enumerate_devices_callback_data__pulse* pData = (ma_context_enumerate_devices_callback_data__pulse*)pUserData; - ma_device_info deviceInfo; - - MA_ASSERT(pData != NULL); - - if (endOfList || pData->isTerminated) { - return; - } - - MA_ZERO_OBJECT(&deviceInfo); - - /* The name from PulseAudio is the ID for miniaudio. */ - if (pSourceInfo->name != NULL) { - ma_strncpy_s(deviceInfo.id.pulse, sizeof(deviceInfo.id.pulse), pSourceInfo->name, (size_t)-1); - } - - /* The description from PulseAudio is the name for miniaudio. */ - if (pSourceInfo->description != NULL) { - ma_strncpy_s(deviceInfo.name, sizeof(deviceInfo.name), pSourceInfo->description, (size_t)-1); - } - - if (pSourceInfo->index == pData->defaultDeviceIndexCapture) { - deviceInfo.isDefault = MA_TRUE; - } - - pData->isTerminated = !pData->callback(pData->pContext, ma_device_type_capture, &deviceInfo, pData->pUserData); - - (void)pPulseContext; /* Unused. */ -} - -static ma_result ma_context_enumerate_devices__pulse(ma_context* pContext, ma_enum_devices_callback_proc callback, void* pUserData) -{ - ma_result result = MA_SUCCESS; - ma_context_enumerate_devices_callback_data__pulse callbackData; - ma_pa_operation* pOP = NULL; - - MA_ASSERT(pContext != NULL); - MA_ASSERT(callback != NULL); - - callbackData.pContext = pContext; - callbackData.callback = callback; - callbackData.pUserData = pUserData; - callbackData.isTerminated = MA_FALSE; - callbackData.defaultDeviceIndexPlayback = (ma_uint32)-1; - callbackData.defaultDeviceIndexCapture = (ma_uint32)-1; - - /* We need to get the index of the default devices. */ - ma_context_get_default_device_index__pulse(pContext, ma_device_type_playback, &callbackData.defaultDeviceIndexPlayback); - ma_context_get_default_device_index__pulse(pContext, ma_device_type_capture, &callbackData.defaultDeviceIndexCapture); - - /* Playback. */ - if (!callbackData.isTerminated) { - pOP = ((ma_pa_context_get_sink_info_list_proc)pContext->pulse.pa_context_get_sink_info_list)((ma_pa_context*)(pContext->pulse.pPulseContext), ma_context_enumerate_devices_sink_callback__pulse, &callbackData); - if (pOP == NULL) { - result = MA_ERROR; - goto done; - } - - result = ma_wait_for_operation__pulse(pContext, pContext->pulse.pMainLoop, pOP); - ((ma_pa_operation_unref_proc)pContext->pulse.pa_operation_unref)(pOP); - - if (result != MA_SUCCESS) { - goto done; - } - } - - - /* Capture. */ - if (!callbackData.isTerminated) { - pOP = ((ma_pa_context_get_source_info_list_proc)pContext->pulse.pa_context_get_source_info_list)((ma_pa_context*)(pContext->pulse.pPulseContext), ma_context_enumerate_devices_source_callback__pulse, &callbackData); - if (pOP == NULL) { - result = MA_ERROR; - goto done; - } - - result = ma_wait_for_operation__pulse(pContext, pContext->pulse.pMainLoop, pOP); - ((ma_pa_operation_unref_proc)pContext->pulse.pa_operation_unref)(pOP); - - if (result != MA_SUCCESS) { - goto done; - } - } - -done: - return result; -} - - -typedef struct -{ - ma_device_info* pDeviceInfo; - ma_uint32 defaultDeviceIndex; - ma_bool32 foundDevice; -} ma_context_get_device_info_callback_data__pulse; - -static void ma_context_get_device_info_sink_callback__pulse(ma_pa_context* pPulseContext, const ma_pa_sink_info* pInfo, int endOfList, void* pUserData) -{ - ma_context_get_device_info_callback_data__pulse* pData = (ma_context_get_device_info_callback_data__pulse*)pUserData; - - if (endOfList > 0) { - return; - } - - MA_ASSERT(pData != NULL); - pData->foundDevice = MA_TRUE; - - if (pInfo->name != NULL) { - ma_strncpy_s(pData->pDeviceInfo->id.pulse, sizeof(pData->pDeviceInfo->id.pulse), pInfo->name, (size_t)-1); - } - - if (pInfo->description != NULL) { - ma_strncpy_s(pData->pDeviceInfo->name, sizeof(pData->pDeviceInfo->name), pInfo->description, (size_t)-1); - } - - /* - We're just reporting a single data format here. I think technically PulseAudio might support - all formats, but I don't trust that PulseAudio will do *anything* right, so I'm just going to - report the "native" device format. - */ - pData->pDeviceInfo->nativeDataFormats[0].format = ma_format_from_pulse(pInfo->sample_spec.format); - pData->pDeviceInfo->nativeDataFormats[0].channels = pInfo->sample_spec.channels; - pData->pDeviceInfo->nativeDataFormats[0].sampleRate = pInfo->sample_spec.rate; - pData->pDeviceInfo->nativeDataFormats[0].flags = 0; - pData->pDeviceInfo->nativeDataFormatCount = 1; - - if (pData->defaultDeviceIndex == pInfo->index) { - pData->pDeviceInfo->isDefault = MA_TRUE; - } - - (void)pPulseContext; /* Unused. */ -} - -static void ma_context_get_device_info_source_callback__pulse(ma_pa_context* pPulseContext, const ma_pa_source_info* pInfo, int endOfList, void* pUserData) -{ - ma_context_get_device_info_callback_data__pulse* pData = (ma_context_get_device_info_callback_data__pulse*)pUserData; - - if (endOfList > 0) { - return; - } - - MA_ASSERT(pData != NULL); - pData->foundDevice = MA_TRUE; - - if (pInfo->name != NULL) { - ma_strncpy_s(pData->pDeviceInfo->id.pulse, sizeof(pData->pDeviceInfo->id.pulse), pInfo->name, (size_t)-1); - } - - if (pInfo->description != NULL) { - ma_strncpy_s(pData->pDeviceInfo->name, sizeof(pData->pDeviceInfo->name), pInfo->description, (size_t)-1); - } - - /* - We're just reporting a single data format here. I think technically PulseAudio might support - all formats, but I don't trust that PulseAudio will do *anything* right, so I'm just going to - report the "native" device format. - */ - pData->pDeviceInfo->nativeDataFormats[0].format = ma_format_from_pulse(pInfo->sample_spec.format); - pData->pDeviceInfo->nativeDataFormats[0].channels = pInfo->sample_spec.channels; - pData->pDeviceInfo->nativeDataFormats[0].sampleRate = pInfo->sample_spec.rate; - pData->pDeviceInfo->nativeDataFormats[0].flags = 0; - pData->pDeviceInfo->nativeDataFormatCount = 1; - - if (pData->defaultDeviceIndex == pInfo->index) { - pData->pDeviceInfo->isDefault = MA_TRUE; - } - - (void)pPulseContext; /* Unused. */ -} - -static ma_result ma_context_get_device_info__pulse(ma_context* pContext, ma_device_type deviceType, const ma_device_id* pDeviceID, ma_device_info* pDeviceInfo) -{ - ma_result result = MA_SUCCESS; - ma_context_get_device_info_callback_data__pulse callbackData; - ma_pa_operation* pOP = NULL; - const char* pDeviceName = NULL; - - MA_ASSERT(pContext != NULL); - - callbackData.pDeviceInfo = pDeviceInfo; - callbackData.foundDevice = MA_FALSE; - - if (pDeviceID != NULL) { - pDeviceName = pDeviceID->pulse; - } else { - pDeviceName = NULL; - } - - result = ma_context_get_default_device_index__pulse(pContext, deviceType, &callbackData.defaultDeviceIndex); - - if (deviceType == ma_device_type_playback) { - pOP = ((ma_pa_context_get_sink_info_by_name_proc)pContext->pulse.pa_context_get_sink_info_by_name)((ma_pa_context*)(pContext->pulse.pPulseContext), pDeviceName, ma_context_get_device_info_sink_callback__pulse, &callbackData); - } else { - pOP = ((ma_pa_context_get_source_info_by_name_proc)pContext->pulse.pa_context_get_source_info_by_name)((ma_pa_context*)(pContext->pulse.pPulseContext), pDeviceName, ma_context_get_device_info_source_callback__pulse, &callbackData); - } - - if (pOP != NULL) { - ma_wait_for_operation_and_unref__pulse(pContext, pContext->pulse.pMainLoop, pOP); - } else { - result = MA_ERROR; - goto done; - } - - if (!callbackData.foundDevice) { - result = MA_NO_DEVICE; - goto done; - } - -done: - return result; -} - -static ma_result ma_device_uninit__pulse(ma_device* pDevice) -{ - ma_context* pContext; - - MA_ASSERT(pDevice != NULL); - - pContext = pDevice->pContext; - MA_ASSERT(pContext != NULL); - - if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) { - ((ma_pa_stream_disconnect_proc)pContext->pulse.pa_stream_disconnect)((ma_pa_stream*)pDevice->pulse.pStreamCapture); - ((ma_pa_stream_unref_proc)pContext->pulse.pa_stream_unref)((ma_pa_stream*)pDevice->pulse.pStreamCapture); - } - - if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) { - ((ma_pa_stream_disconnect_proc)pContext->pulse.pa_stream_disconnect)((ma_pa_stream*)pDevice->pulse.pStreamPlayback); - ((ma_pa_stream_unref_proc)pContext->pulse.pa_stream_unref)((ma_pa_stream*)pDevice->pulse.pStreamPlayback); - } - - if (pDevice->type == ma_device_type_duplex) { - ma_duplex_rb_uninit(&pDevice->duplexRB); - } - - ((ma_pa_context_disconnect_proc)pContext->pulse.pa_context_disconnect)((ma_pa_context*)pDevice->pulse.pPulseContext); - ((ma_pa_context_unref_proc)pContext->pulse.pa_context_unref)((ma_pa_context*)pDevice->pulse.pPulseContext); - ((ma_pa_mainloop_free_proc)pContext->pulse.pa_mainloop_free)((ma_pa_mainloop*)pDevice->pulse.pMainLoop); - - return MA_SUCCESS; -} - -static ma_pa_buffer_attr ma_device__pa_buffer_attr_new(ma_uint32 periodSizeInFrames, ma_uint32 periods, const ma_pa_sample_spec* ss) -{ - ma_pa_buffer_attr attr; - attr.maxlength = periodSizeInFrames * periods * ma_get_bytes_per_frame(ma_format_from_pulse(ss->format), ss->channels); - attr.tlength = attr.maxlength / periods; - attr.prebuf = (ma_uint32)-1; - attr.minreq = (ma_uint32)-1; - attr.fragsize = attr.maxlength / periods; - - return attr; -} - -static ma_pa_stream* ma_device__pa_stream_new__pulse(ma_device* pDevice, const char* pStreamName, const ma_pa_sample_spec* ss, const ma_pa_channel_map* cmap) -{ - static ma_atomic_uint32 g_StreamCounter = { 0 }; - char actualStreamName[256]; - - if (pStreamName != NULL) { - ma_strncpy_s(actualStreamName, sizeof(actualStreamName), pStreamName, (size_t)-1); - } else { - const char* pBaseName = "miniaudio:"; - size_t baseNameLen = strlen(pBaseName); - ma_strcpy_s(actualStreamName, sizeof(actualStreamName), pBaseName); - ma_itoa_s((int)ma_atomic_uint32_get(&g_StreamCounter), actualStreamName + baseNameLen, sizeof(actualStreamName)-baseNameLen, 10); - } - ma_atomic_uint32_fetch_add(&g_StreamCounter, 1); - - return ((ma_pa_stream_new_proc)pDevice->pContext->pulse.pa_stream_new)((ma_pa_context*)pDevice->pulse.pPulseContext, actualStreamName, ss, cmap); -} - - -static void ma_device_on_read__pulse(ma_pa_stream* pStream, size_t byteCount, void* pUserData) -{ - ma_device* pDevice = (ma_device*)pUserData; - ma_uint32 bpf; - ma_uint32 deviceState; - ma_uint64 frameCount; - ma_uint64 framesProcessed; - - MA_ASSERT(pDevice != NULL); - - /* - Don't do anything if the device isn't initialized yet. Yes, this can happen because PulseAudio - can fire this callback before the stream has even started. Ridiculous. - */ - deviceState = ma_device_get_state(pDevice); - if (deviceState != ma_device_state_starting && deviceState != ma_device_state_started) { - return; - } - - bpf = ma_get_bytes_per_frame(pDevice->capture.internalFormat, pDevice->capture.internalChannels); - MA_ASSERT(bpf > 0); - - frameCount = byteCount / bpf; - framesProcessed = 0; - - while (ma_device_get_state(pDevice) == ma_device_state_started && framesProcessed < frameCount) { - const void* pMappedPCMFrames; - size_t bytesMapped; - ma_uint64 framesMapped; - - int pulseResult = ((ma_pa_stream_peek_proc)pDevice->pContext->pulse.pa_stream_peek)(pStream, &pMappedPCMFrames, &bytesMapped); - if (pulseResult < 0) { - break; /* Failed to map. Abort. */ - } - - framesMapped = bytesMapped / bpf; - if (framesMapped > 0) { - if (pMappedPCMFrames != NULL) { - ma_device_handle_backend_data_callback(pDevice, NULL, pMappedPCMFrames, framesMapped); - } else { - /* It's a hole. */ - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "[PulseAudio] ma_device_on_read__pulse: Hole.\n"); - } - - pulseResult = ((ma_pa_stream_drop_proc)pDevice->pContext->pulse.pa_stream_drop)(pStream); - if (pulseResult < 0) { - break; /* Failed to drop the buffer. */ - } - - framesProcessed += framesMapped; - - } else { - /* Nothing was mapped. Just abort. */ - break; - } - } -} - -static ma_result ma_device_write_to_stream__pulse(ma_device* pDevice, ma_pa_stream* pStream, ma_uint64* pFramesProcessed) -{ - ma_result result = MA_SUCCESS; - ma_uint64 framesProcessed = 0; - size_t bytesMapped; - ma_uint32 bpf; - ma_uint32 deviceState; - - MA_ASSERT(pDevice != NULL); - MA_ASSERT(pStream != NULL); - - bpf = ma_get_bytes_per_frame(pDevice->playback.internalFormat, pDevice->playback.internalChannels); - MA_ASSERT(bpf > 0); - - deviceState = ma_device_get_state(pDevice); - - bytesMapped = ((ma_pa_stream_writable_size_proc)pDevice->pContext->pulse.pa_stream_writable_size)(pStream); - if (bytesMapped != (size_t)-1) { - if (bytesMapped > 0) { - ma_uint64 framesMapped; - void* pMappedPCMFrames; - int pulseResult = ((ma_pa_stream_begin_write_proc)pDevice->pContext->pulse.pa_stream_begin_write)(pStream, &pMappedPCMFrames, &bytesMapped); - if (pulseResult < 0) { - result = ma_result_from_pulse(pulseResult); - goto done; - } - - framesMapped = bytesMapped / bpf; - - if (deviceState == ma_device_state_started || deviceState == ma_device_state_starting) { /* Check for starting state just in case this is being used to do the initial fill. */ - ma_device_handle_backend_data_callback(pDevice, pMappedPCMFrames, NULL, framesMapped); - } else { - /* Device is not started. Write silence. */ - ma_silence_pcm_frames(pMappedPCMFrames, framesMapped, pDevice->playback.format, pDevice->playback.channels); - } - - pulseResult = ((ma_pa_stream_write_proc)pDevice->pContext->pulse.pa_stream_write)(pStream, pMappedPCMFrames, bytesMapped, NULL, 0, MA_PA_SEEK_RELATIVE); - if (pulseResult < 0) { - result = ma_result_from_pulse(pulseResult); - goto done; /* Failed to write data to stream. */ - } - - framesProcessed += framesMapped; - } else { - result = MA_SUCCESS; /* No data available for writing. */ - goto done; - } - } else { - result = MA_ERROR; /* Failed to retrieve the writable size. Abort. */ - goto done; - } - -done: - if (pFramesProcessed != NULL) { - *pFramesProcessed = framesProcessed; - } - - return result; -} - -static void ma_device_on_write__pulse(ma_pa_stream* pStream, size_t byteCount, void* pUserData) -{ - ma_device* pDevice = (ma_device*)pUserData; - ma_uint32 bpf; - ma_uint64 frameCount; - ma_uint64 framesProcessed; - ma_uint32 deviceState; - ma_result result; - - MA_ASSERT(pDevice != NULL); - - /* - Don't do anything if the device isn't initialized yet. Yes, this can happen because PulseAudio - can fire this callback before the stream has even started. Ridiculous. - */ - deviceState = ma_device_get_state(pDevice); - if (deviceState != ma_device_state_starting && deviceState != ma_device_state_started) { - return; - } - - bpf = ma_get_bytes_per_frame(pDevice->playback.internalFormat, pDevice->playback.internalChannels); - MA_ASSERT(bpf > 0); - - frameCount = byteCount / bpf; - framesProcessed = 0; - - while (framesProcessed < frameCount) { - ma_uint64 framesProcessedThisIteration; - - /* Don't keep trying to process frames if the device isn't started. */ - deviceState = ma_device_get_state(pDevice); - if (deviceState != ma_device_state_starting && deviceState != ma_device_state_started) { - break; - } - - result = ma_device_write_to_stream__pulse(pDevice, pStream, &framesProcessedThisIteration); - if (result != MA_SUCCESS) { - break; - } - - framesProcessed += framesProcessedThisIteration; - } -} - -static void ma_device_on_suspended__pulse(ma_pa_stream* pStream, void* pUserData) -{ - ma_device* pDevice = (ma_device*)pUserData; - int suspended; - - (void)pStream; - - suspended = ((ma_pa_stream_is_suspended_proc)pDevice->pContext->pulse.pa_stream_is_suspended)(pStream); - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "[Pulse] Device suspended state changed. pa_stream_is_suspended() returned %d.\n", suspended); - - if (suspended < 0) { - return; - } - - if (suspended == 1) { - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "[Pulse] Device suspended state changed. Suspended.\n"); - ma_device__on_notification_stopped(pDevice); - } else { - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "[Pulse] Device suspended state changed. Resumed.\n"); - ma_device__on_notification_started(pDevice); - } -} - -static void ma_device_on_rerouted__pulse(ma_pa_stream* pStream, void* pUserData) -{ - ma_device* pDevice = (ma_device*)pUserData; - - (void)pStream; - (void)pUserData; - - ma_device__on_notification_rerouted(pDevice); -} - -static ma_uint32 ma_calculate_period_size_in_frames_from_descriptor__pulse(const ma_device_descriptor* pDescriptor, ma_uint32 nativeSampleRate, ma_performance_profile performanceProfile) -{ - /* - There have been reports from users where buffers of < ~20ms result glitches when running through - PipeWire. To work around this we're going to have to use a different default buffer size. - */ - const ma_uint32 defaultPeriodSizeInMilliseconds_LowLatency = 25; - const ma_uint32 defaultPeriodSizeInMilliseconds_Conservative = MA_DEFAULT_PERIOD_SIZE_IN_MILLISECONDS_CONSERVATIVE; - - MA_ASSERT(nativeSampleRate != 0); - - if (pDescriptor->periodSizeInFrames == 0) { - if (pDescriptor->periodSizeInMilliseconds == 0) { - if (performanceProfile == ma_performance_profile_low_latency) { - return ma_calculate_buffer_size_in_frames_from_milliseconds(defaultPeriodSizeInMilliseconds_LowLatency, nativeSampleRate); - } else { - return ma_calculate_buffer_size_in_frames_from_milliseconds(defaultPeriodSizeInMilliseconds_Conservative, nativeSampleRate); - } - } else { - return ma_calculate_buffer_size_in_frames_from_milliseconds(pDescriptor->periodSizeInMilliseconds, nativeSampleRate); - } - } else { - return pDescriptor->periodSizeInFrames; - } -} - -static ma_result ma_device_init__pulse(ma_device* pDevice, const ma_device_config* pConfig, ma_device_descriptor* pDescriptorPlayback, ma_device_descriptor* pDescriptorCapture) -{ - /* - Notes for PulseAudio: - - - When both the period size in frames and milliseconds are 0, we default to miniaudio's - default buffer sizes rather than leaving it up to PulseAudio because I don't trust - PulseAudio to give us any kind of reasonable latency by default. - - - Do not ever, *ever* forget to use MA_PA_STREAM_ADJUST_LATENCY. If you don't specify this - flag, capture mode will just not work properly until you open another PulseAudio app. - */ - - ma_result result = MA_SUCCESS; - int error = 0; - const char* devPlayback = NULL; - const char* devCapture = NULL; - ma_format format = ma_format_unknown; - ma_uint32 channels = 0; - ma_uint32 sampleRate = 0; - ma_pa_sink_info sinkInfo; - ma_pa_source_info sourceInfo; - ma_pa_sample_spec ss; - ma_pa_channel_map cmap; - ma_pa_buffer_attr attr; - const ma_pa_sample_spec* pActualSS = NULL; - const ma_pa_buffer_attr* pActualAttr = NULL; - const ma_pa_channel_map* pActualChannelMap = NULL; - ma_uint32 iChannel; - ma_pa_stream_flags_t streamFlags; - - MA_ASSERT(pDevice != NULL); - MA_ZERO_OBJECT(&pDevice->pulse); - - if (pConfig->deviceType == ma_device_type_loopback) { - return MA_DEVICE_TYPE_NOT_SUPPORTED; - } - - /* No exclusive mode with the PulseAudio backend. */ - if (((pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) && pConfig->playback.shareMode == ma_share_mode_exclusive) || - ((pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) && pConfig->capture.shareMode == ma_share_mode_exclusive)) { - return MA_SHARE_MODE_NOT_SUPPORTED; - } - - if (pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) { - if (pDescriptorPlayback->pDeviceID != NULL) { - devPlayback = pDescriptorPlayback->pDeviceID->pulse; - } - - format = pDescriptorPlayback->format; - channels = pDescriptorPlayback->channels; - sampleRate = pDescriptorPlayback->sampleRate; - } - - if (pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) { - if (pDescriptorCapture->pDeviceID != NULL) { - devCapture = pDescriptorCapture->pDeviceID->pulse; - } - - format = pDescriptorCapture->format; - channels = pDescriptorCapture->channels; - sampleRate = pDescriptorCapture->sampleRate; - } - - - - result = ma_init_pa_mainloop_and_pa_context__pulse(pDevice->pContext, pDevice->pContext->pulse.pApplicationName, pDevice->pContext->pulse.pServerName, MA_FALSE, &pDevice->pulse.pMainLoop, &pDevice->pulse.pPulseContext); - if (result != MA_SUCCESS) { - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[PulseAudio] Failed to initialize PA mainloop and context for device.\n"); - return result; - } - - if (pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) { - result = ma_context_get_source_info__pulse(pDevice->pContext, devCapture, &sourceInfo); - if (result != MA_SUCCESS) { - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[PulseAudio] Failed to retrieve source info for capture device."); - goto on_error0; - } - - ss = sourceInfo.sample_spec; - cmap = sourceInfo.channel_map; - - /* Use the requested channel count if we have one. */ - if (pDescriptorCapture->channels != 0) { - ss.channels = pDescriptorCapture->channels; - } - - /* Use a default channel map. */ - ((ma_pa_channel_map_init_extend_proc)pDevice->pContext->pulse.pa_channel_map_init_extend)(&cmap, ss.channels, pConfig->pulse.channelMap); - - /* Use the requested sample rate if one was specified. */ - if (pDescriptorCapture->sampleRate != 0) { - ss.rate = pDescriptorCapture->sampleRate; - } - streamFlags = MA_PA_STREAM_START_CORKED | MA_PA_STREAM_ADJUST_LATENCY; - - if (ma_format_from_pulse(ss.format) == ma_format_unknown) { - if (ma_is_little_endian()) { - ss.format = MA_PA_SAMPLE_FLOAT32LE; - } else { - ss.format = MA_PA_SAMPLE_FLOAT32BE; - } - streamFlags |= MA_PA_STREAM_FIX_FORMAT; - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, "[PulseAudio] sample_spec.format not supported by miniaudio. Defaulting to PA_SAMPLE_FLOAT32.\n"); - } - if (ss.rate == 0) { - ss.rate = MA_DEFAULT_SAMPLE_RATE; - streamFlags |= MA_PA_STREAM_FIX_RATE; - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, "[PulseAudio] sample_spec.rate = 0. Defaulting to %d.\n", ss.rate); - } - if (ss.channels == 0) { - ss.channels = MA_DEFAULT_CHANNELS; - streamFlags |= MA_PA_STREAM_FIX_CHANNELS; - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, "[PulseAudio] sample_spec.channels = 0. Defaulting to %d.\n", ss.channels); - } - - /* We now have enough information to calculate our actual period size in frames. */ - pDescriptorCapture->periodSizeInFrames = ma_calculate_period_size_in_frames_from_descriptor__pulse(pDescriptorCapture, ss.rate, pConfig->performanceProfile); - - attr = ma_device__pa_buffer_attr_new(pDescriptorCapture->periodSizeInFrames, pDescriptorCapture->periodCount, &ss); - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, "[PulseAudio] Capture attr: maxlength=%d, tlength=%d, prebuf=%d, minreq=%d, fragsize=%d; periodSizeInFrames=%d\n", attr.maxlength, attr.tlength, attr.prebuf, attr.minreq, attr.fragsize, pDescriptorCapture->periodSizeInFrames); - - pDevice->pulse.pStreamCapture = ma_device__pa_stream_new__pulse(pDevice, pConfig->pulse.pStreamNameCapture, &ss, &cmap); - if (pDevice->pulse.pStreamCapture == NULL) { - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[PulseAudio] Failed to create PulseAudio capture stream.\n"); - result = MA_ERROR; - goto on_error0; - } - - - /* The callback needs to be set before connecting the stream. */ - ((ma_pa_stream_set_read_callback_proc)pDevice->pContext->pulse.pa_stream_set_read_callback)((ma_pa_stream*)pDevice->pulse.pStreamCapture, ma_device_on_read__pulse, pDevice); - - /* State callback for checking when the device has been corked. */ - ((ma_pa_stream_set_suspended_callback_proc)pDevice->pContext->pulse.pa_stream_set_suspended_callback)((ma_pa_stream*)pDevice->pulse.pStreamCapture, ma_device_on_suspended__pulse, pDevice); - - /* Rerouting notification. */ - ((ma_pa_stream_set_moved_callback_proc)pDevice->pContext->pulse.pa_stream_set_moved_callback)((ma_pa_stream*)pDevice->pulse.pStreamCapture, ma_device_on_rerouted__pulse, pDevice); - - - /* Connect after we've got all of our internal state set up. */ - if (devCapture != NULL) { - streamFlags |= MA_PA_STREAM_DONT_MOVE; - } - - error = ((ma_pa_stream_connect_record_proc)pDevice->pContext->pulse.pa_stream_connect_record)((ma_pa_stream*)pDevice->pulse.pStreamCapture, devCapture, &attr, streamFlags); - if (error != MA_PA_OK) { - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[PulseAudio] Failed to connect PulseAudio capture stream."); - result = ma_result_from_pulse(error); - goto on_error1; - } - - result = ma_wait_for_pa_stream_to_connect__pulse(pDevice->pContext, pDevice->pulse.pMainLoop, (ma_pa_stream*)pDevice->pulse.pStreamCapture); - if (result != MA_SUCCESS) { - goto on_error2; - } - - - /* Internal format. */ - pActualSS = ((ma_pa_stream_get_sample_spec_proc)pDevice->pContext->pulse.pa_stream_get_sample_spec)((ma_pa_stream*)pDevice->pulse.pStreamCapture); - if (pActualSS != NULL) { - ss = *pActualSS; - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, "[PulseAudio] Capture sample spec: format=%s, channels=%d, rate=%d\n", ma_get_format_name(ma_format_from_pulse(ss.format)), ss.channels, ss.rate); - } else { - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, "[PulseAudio] Failed to retrieve capture sample spec.\n"); - } - - pDescriptorCapture->format = ma_format_from_pulse(ss.format); - pDescriptorCapture->channels = ss.channels; - pDescriptorCapture->sampleRate = ss.rate; - - if (pDescriptorCapture->format == ma_format_unknown || pDescriptorCapture->channels == 0 || pDescriptorCapture->sampleRate == 0) { - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[PulseAudio] Capture sample spec is invalid. Device unusable by miniaudio. format=%s, channels=%d, sampleRate=%d.\n", ma_get_format_name(pDescriptorCapture->format), pDescriptorCapture->channels, pDescriptorCapture->sampleRate); - result = MA_ERROR; - goto on_error4; - } - - - /* Internal channel map. */ - pActualChannelMap = ((ma_pa_stream_get_channel_map_proc)pDevice->pContext->pulse.pa_stream_get_channel_map)((ma_pa_stream*)pDevice->pulse.pStreamCapture); - if (pActualChannelMap == NULL) { - pActualChannelMap = &cmap; /* Fallback just in case. */ - } - - /* - Bug in PipeWire. There have been reports that PipeWire is returning AUX channels when reporting - the channel map. To somewhat workaround this, I'm hacking in a hard coded channel map for mono - and stereo. In this case it should be safe to assume mono = MONO and stereo = LEFT/RIGHT. For - all other channel counts we need to just put up with whatever PipeWire reports and hope it gets - fixed sooner than later. I might remove this hack later. - */ - if (pDescriptorCapture->channels > 2) { - for (iChannel = 0; iChannel < pDescriptorCapture->channels; iChannel += 1) { - pDescriptorCapture->channelMap[iChannel] = ma_channel_position_from_pulse(pActualChannelMap->map[iChannel]); - } - } else { - /* Hack for mono and stereo. */ - if (pDescriptorCapture->channels == 1) { - pDescriptorCapture->channelMap[0] = MA_CHANNEL_MONO; - } else if (pDescriptorCapture->channels == 2) { - pDescriptorCapture->channelMap[0] = MA_CHANNEL_FRONT_LEFT; - pDescriptorCapture->channelMap[1] = MA_CHANNEL_FRONT_RIGHT; - } else { - MA_ASSERT(MA_FALSE); /* Should never hit this. */ - } - } - - - /* Buffer. */ - pActualAttr = ((ma_pa_stream_get_buffer_attr_proc)pDevice->pContext->pulse.pa_stream_get_buffer_attr)((ma_pa_stream*)pDevice->pulse.pStreamCapture); - if (pActualAttr != NULL) { - attr = *pActualAttr; - } - - if (attr.fragsize > 0) { - pDescriptorCapture->periodCount = ma_max(attr.maxlength / attr.fragsize, 1); - } else { - pDescriptorCapture->periodCount = 1; - } - - pDescriptorCapture->periodSizeInFrames = attr.maxlength / ma_get_bytes_per_frame(pDescriptorCapture->format, pDescriptorCapture->channels) / pDescriptorCapture->periodCount; - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, "[PulseAudio] Capture actual attr: maxlength=%d, tlength=%d, prebuf=%d, minreq=%d, fragsize=%d; periodSizeInFrames=%d\n", attr.maxlength, attr.tlength, attr.prebuf, attr.minreq, attr.fragsize, pDescriptorCapture->periodSizeInFrames); - } - - if (pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) { - result = ma_context_get_sink_info__pulse(pDevice->pContext, devPlayback, &sinkInfo); - if (result != MA_SUCCESS) { - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[PulseAudio] Failed to retrieve sink info for playback device.\n"); - goto on_error2; - } - - ss = sinkInfo.sample_spec; - cmap = sinkInfo.channel_map; - - /* Use the requested channel count if we have one. */ - if (pDescriptorPlayback->channels != 0) { - ss.channels = pDescriptorPlayback->channels; - } - - /* Use a default channel map. */ - ((ma_pa_channel_map_init_extend_proc)pDevice->pContext->pulse.pa_channel_map_init_extend)(&cmap, ss.channels, pConfig->pulse.channelMap); - - - /* Use the requested sample rate if one was specified. */ - if (pDescriptorPlayback->sampleRate != 0) { - ss.rate = pDescriptorPlayback->sampleRate; - } - - streamFlags = MA_PA_STREAM_START_CORKED | MA_PA_STREAM_ADJUST_LATENCY; - if (ma_format_from_pulse(ss.format) == ma_format_unknown) { - if (ma_is_little_endian()) { - ss.format = MA_PA_SAMPLE_FLOAT32LE; - } else { - ss.format = MA_PA_SAMPLE_FLOAT32BE; - } - streamFlags |= MA_PA_STREAM_FIX_FORMAT; - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, "[PulseAudio] sample_spec.format not supported by miniaudio. Defaulting to PA_SAMPLE_FLOAT32.\n"); - } - if (ss.rate == 0) { - ss.rate = MA_DEFAULT_SAMPLE_RATE; - streamFlags |= MA_PA_STREAM_FIX_RATE; - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, "[PulseAudio] sample_spec.rate = 0. Defaulting to %d.\n", ss.rate); - } - if (ss.channels == 0) { - ss.channels = MA_DEFAULT_CHANNELS; - streamFlags |= MA_PA_STREAM_FIX_CHANNELS; - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, "[PulseAudio] sample_spec.channels = 0. Defaulting to %d.\n", ss.channels); - } - - /* We now have enough information to calculate the actual buffer size in frames. */ - pDescriptorPlayback->periodSizeInFrames = ma_calculate_period_size_in_frames_from_descriptor__pulse(pDescriptorPlayback, ss.rate, pConfig->performanceProfile); - - attr = ma_device__pa_buffer_attr_new(pDescriptorPlayback->periodSizeInFrames, pDescriptorPlayback->periodCount, &ss); - - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, "[PulseAudio] Playback attr: maxlength=%d, tlength=%d, prebuf=%d, minreq=%d, fragsize=%d; periodSizeInFrames=%d\n", attr.maxlength, attr.tlength, attr.prebuf, attr.minreq, attr.fragsize, pDescriptorPlayback->periodSizeInFrames); - - pDevice->pulse.pStreamPlayback = ma_device__pa_stream_new__pulse(pDevice, pConfig->pulse.pStreamNamePlayback, &ss, &cmap); - if (pDevice->pulse.pStreamPlayback == NULL) { - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[PulseAudio] Failed to create PulseAudio playback stream.\n"); - result = MA_ERROR; - goto on_error2; - } - - - /* - Note that this callback will be fired as soon as the stream is connected, even though it's started as corked. The callback needs to handle a - device state of ma_device_state_uninitialized. - */ - ((ma_pa_stream_set_write_callback_proc)pDevice->pContext->pulse.pa_stream_set_write_callback)((ma_pa_stream*)pDevice->pulse.pStreamPlayback, ma_device_on_write__pulse, pDevice); - - /* State callback for checking when the device has been corked. */ - ((ma_pa_stream_set_suspended_callback_proc)pDevice->pContext->pulse.pa_stream_set_suspended_callback)((ma_pa_stream*)pDevice->pulse.pStreamPlayback, ma_device_on_suspended__pulse, pDevice); - - /* Rerouting notification. */ - ((ma_pa_stream_set_moved_callback_proc)pDevice->pContext->pulse.pa_stream_set_moved_callback)((ma_pa_stream*)pDevice->pulse.pStreamPlayback, ma_device_on_rerouted__pulse, pDevice); - - - /* Connect after we've got all of our internal state set up. */ - if (devPlayback != NULL) { - streamFlags |= MA_PA_STREAM_DONT_MOVE; - } - - error = ((ma_pa_stream_connect_playback_proc)pDevice->pContext->pulse.pa_stream_connect_playback)((ma_pa_stream*)pDevice->pulse.pStreamPlayback, devPlayback, &attr, streamFlags, NULL, NULL); - if (error != MA_PA_OK) { - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[PulseAudio] Failed to connect PulseAudio playback stream."); - result = ma_result_from_pulse(error); - goto on_error3; - } - - result = ma_wait_for_pa_stream_to_connect__pulse(pDevice->pContext, pDevice->pulse.pMainLoop, (ma_pa_stream*)pDevice->pulse.pStreamPlayback); - if (result != MA_SUCCESS) { - goto on_error3; - } - - - /* Internal format. */ - pActualSS = ((ma_pa_stream_get_sample_spec_proc)pDevice->pContext->pulse.pa_stream_get_sample_spec)((ma_pa_stream*)pDevice->pulse.pStreamPlayback); - if (pActualSS != NULL) { - ss = *pActualSS; - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, "[PulseAudio] Playback sample spec: format=%s, channels=%d, rate=%d\n", ma_get_format_name(ma_format_from_pulse(ss.format)), ss.channels, ss.rate); - } else { - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, "[PulseAudio] Failed to retrieve playback sample spec.\n"); - } - - pDescriptorPlayback->format = ma_format_from_pulse(ss.format); - pDescriptorPlayback->channels = ss.channels; - pDescriptorPlayback->sampleRate = ss.rate; - - if (pDescriptorPlayback->format == ma_format_unknown || pDescriptorPlayback->channels == 0 || pDescriptorPlayback->sampleRate == 0) { - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[PulseAudio] Playback sample spec is invalid. Device unusable by miniaudio. format=%s, channels=%d, sampleRate=%d.\n", ma_get_format_name(pDescriptorPlayback->format), pDescriptorPlayback->channels, pDescriptorPlayback->sampleRate); - result = MA_ERROR; - goto on_error4; - } - - - /* Internal channel map. */ - pActualChannelMap = ((ma_pa_stream_get_channel_map_proc)pDevice->pContext->pulse.pa_stream_get_channel_map)((ma_pa_stream*)pDevice->pulse.pStreamPlayback); - if (pActualChannelMap == NULL) { - pActualChannelMap = &cmap; /* Fallback just in case. */ - } - - /* - Bug in PipeWire. There have been reports that PipeWire is returning AUX channels when reporting - the channel map. To somewhat workaround this, I'm hacking in a hard coded channel map for mono - and stereo. In this case it should be safe to assume mono = MONO and stereo = LEFT/RIGHT. For - all other channel counts we need to just put up with whatever PipeWire reports and hope it gets - fixed sooner than later. I might remove this hack later. - */ - if (pDescriptorPlayback->channels > 2) { - for (iChannel = 0; iChannel < pDescriptorPlayback->channels; iChannel += 1) { - pDescriptorPlayback->channelMap[iChannel] = ma_channel_position_from_pulse(pActualChannelMap->map[iChannel]); - } - } else { - /* Hack for mono and stereo. */ - if (pDescriptorPlayback->channels == 1) { - pDescriptorPlayback->channelMap[0] = MA_CHANNEL_MONO; - } else if (pDescriptorPlayback->channels == 2) { - pDescriptorPlayback->channelMap[0] = MA_CHANNEL_FRONT_LEFT; - pDescriptorPlayback->channelMap[1] = MA_CHANNEL_FRONT_RIGHT; - } else { - MA_ASSERT(MA_FALSE); /* Should never hit this. */ - } - } - - - /* Buffer. */ - pActualAttr = ((ma_pa_stream_get_buffer_attr_proc)pDevice->pContext->pulse.pa_stream_get_buffer_attr)((ma_pa_stream*)pDevice->pulse.pStreamPlayback); - if (pActualAttr != NULL) { - attr = *pActualAttr; - } - - if (attr.tlength > 0) { - pDescriptorPlayback->periodCount = ma_max(attr.maxlength / attr.tlength, 1); - } else { - pDescriptorPlayback->periodCount = 1; - } - - pDescriptorPlayback->periodSizeInFrames = attr.maxlength / ma_get_bytes_per_frame(pDescriptorPlayback->format, pDescriptorPlayback->channels) / pDescriptorPlayback->periodCount; - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, "[PulseAudio] Playback actual attr: maxlength=%d, tlength=%d, prebuf=%d, minreq=%d, fragsize=%d; internalPeriodSizeInFrames=%d\n", attr.maxlength, attr.tlength, attr.prebuf, attr.minreq, attr.fragsize, pDescriptorPlayback->periodSizeInFrames); - } - - - /* - We need a ring buffer for handling duplex mode. We can use the main duplex ring buffer in the main - part of the ma_device struct. We cannot, however, depend on ma_device_init() initializing this for - us later on because that will only do it if it's a fully asynchronous backend - i.e. the - onDeviceDataLoop callback is NULL, which is not the case for PulseAudio. - */ - if (pConfig->deviceType == ma_device_type_duplex) { - ma_format rbFormat = (format != ma_format_unknown) ? format : pDescriptorCapture->format; - ma_uint32 rbChannels = (channels > 0) ? channels : pDescriptorCapture->channels; - ma_uint32 rbSampleRate = (sampleRate > 0) ? sampleRate : pDescriptorCapture->sampleRate; - - result = ma_duplex_rb_init(rbFormat, rbChannels, rbSampleRate, pDescriptorCapture->sampleRate, pDescriptorCapture->periodSizeInFrames, &pDevice->pContext->allocationCallbacks, &pDevice->duplexRB); - if (result != MA_SUCCESS) { - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[PulseAudio] Failed to initialize ring buffer. %s.\n", ma_result_description(result)); - goto on_error4; - } - } - - return MA_SUCCESS; - - -on_error4: - if (pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) { - ((ma_pa_stream_disconnect_proc)pDevice->pContext->pulse.pa_stream_disconnect)((ma_pa_stream*)pDevice->pulse.pStreamPlayback); - } -on_error3: - if (pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) { - ((ma_pa_stream_unref_proc)pDevice->pContext->pulse.pa_stream_unref)((ma_pa_stream*)pDevice->pulse.pStreamPlayback); - } -on_error2: - if (pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) { - ((ma_pa_stream_disconnect_proc)pDevice->pContext->pulse.pa_stream_disconnect)((ma_pa_stream*)pDevice->pulse.pStreamCapture); - } -on_error1: - if (pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) { - ((ma_pa_stream_unref_proc)pDevice->pContext->pulse.pa_stream_unref)((ma_pa_stream*)pDevice->pulse.pStreamCapture); - } -on_error0: - return result; -} - - -static void ma_pulse_operation_complete_callback(ma_pa_stream* pStream, int success, void* pUserData) -{ - ma_bool32* pIsSuccessful = (ma_bool32*)pUserData; - MA_ASSERT(pIsSuccessful != NULL); - - *pIsSuccessful = (ma_bool32)success; - - (void)pStream; /* Unused. */ -} - -static ma_result ma_device__cork_stream__pulse(ma_device* pDevice, ma_device_type deviceType, int cork) -{ - ma_context* pContext = pDevice->pContext; - ma_bool32 wasSuccessful; - ma_pa_stream* pStream; - ma_pa_operation* pOP; - ma_result result; - - /* This should not be called with a duplex device type. */ - if (deviceType == ma_device_type_duplex) { - return MA_INVALID_ARGS; - } - - wasSuccessful = MA_FALSE; - - pStream = (ma_pa_stream*)((deviceType == ma_device_type_capture) ? pDevice->pulse.pStreamCapture : pDevice->pulse.pStreamPlayback); - MA_ASSERT(pStream != NULL); - - pOP = ((ma_pa_stream_cork_proc)pContext->pulse.pa_stream_cork)(pStream, cork, ma_pulse_operation_complete_callback, &wasSuccessful); - if (pOP == NULL) { - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[PulseAudio] Failed to cork PulseAudio stream."); - return MA_ERROR; - } - - result = ma_wait_for_operation_and_unref__pulse(pDevice->pContext, pDevice->pulse.pMainLoop, pOP); - if (result != MA_SUCCESS) { - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[PulseAudio] An error occurred while waiting for the PulseAudio stream to cork."); - return result; - } - - if (!wasSuccessful) { - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[PulseAudio] Failed to %s PulseAudio stream.", (cork) ? "stop" : "start"); - return MA_ERROR; - } - - return MA_SUCCESS; -} - -static ma_result ma_device_start__pulse(ma_device* pDevice) -{ - ma_result result; - - MA_ASSERT(pDevice != NULL); - - if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) { - result = ma_device__cork_stream__pulse(pDevice, ma_device_type_capture, 0); - if (result != MA_SUCCESS) { - return result; - } - } - - if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) { - /* - We need to fill some data before uncorking. Not doing this will result in the write callback - never getting fired. We're not going to abort if writing fails because I still want the device - to get uncorked. - */ - ma_device_write_to_stream__pulse(pDevice, (ma_pa_stream*)(pDevice->pulse.pStreamPlayback), NULL); /* No need to check the result here. Always want to fall through an uncork.*/ - - result = ma_device__cork_stream__pulse(pDevice, ma_device_type_playback, 0); - if (result != MA_SUCCESS) { - return result; - } - } - - return MA_SUCCESS; -} - -static ma_result ma_device_stop__pulse(ma_device* pDevice) -{ - ma_result result; - - MA_ASSERT(pDevice != NULL); - - if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) { - result = ma_device__cork_stream__pulse(pDevice, ma_device_type_capture, 1); - if (result != MA_SUCCESS) { - return result; - } - } - - if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) { - /* - Ideally we would drain the device here, but there's been cases where PulseAudio seems to be - broken on some systems to the point where no audio processing seems to happen. When this - happens, draining never completes and we get stuck here. For now I'm disabling draining of - the device so we don't just freeze the application. - */ - #if 0 - ma_pa_operation* pOP = ((ma_pa_stream_drain_proc)pDevice->pContext->pulse.pa_stream_drain)((ma_pa_stream*)pDevice->pulse.pStreamPlayback, ma_pulse_operation_complete_callback, &wasSuccessful); - ma_wait_for_operation_and_unref__pulse(pDevice->pContext, pDevice->pulse.pMainLoop, pOP); - #endif - - result = ma_device__cork_stream__pulse(pDevice, ma_device_type_playback, 1); - if (result != MA_SUCCESS) { - return result; - } - } - - return MA_SUCCESS; -} - -static ma_result ma_device_data_loop__pulse(ma_device* pDevice) -{ - int resultPA; - - MA_ASSERT(pDevice != NULL); - - /* NOTE: Don't start the device here. It'll be done at a higher level. */ - - /* - All data is handled through callbacks. All we need to do is iterate over the main loop and let - the callbacks deal with it. - */ - while (ma_device_get_state(pDevice) == ma_device_state_started) { - resultPA = ((ma_pa_mainloop_iterate_proc)pDevice->pContext->pulse.pa_mainloop_iterate)((ma_pa_mainloop*)pDevice->pulse.pMainLoop, 1, NULL); - if (resultPA < 0) { - break; - } - } - - /* NOTE: Don't stop the device here. It'll be done at a higher level. */ - return MA_SUCCESS; -} - -static ma_result ma_device_data_loop_wakeup__pulse(ma_device* pDevice) -{ - MA_ASSERT(pDevice != NULL); - - ((ma_pa_mainloop_wakeup_proc)pDevice->pContext->pulse.pa_mainloop_wakeup)((ma_pa_mainloop*)pDevice->pulse.pMainLoop); - - return MA_SUCCESS; -} - -static ma_result ma_context_uninit__pulse(ma_context* pContext) -{ - MA_ASSERT(pContext != NULL); - MA_ASSERT(pContext->backend == ma_backend_pulseaudio); - - ((ma_pa_context_disconnect_proc)pContext->pulse.pa_context_disconnect)((ma_pa_context*)pContext->pulse.pPulseContext); - ((ma_pa_context_unref_proc)pContext->pulse.pa_context_unref)((ma_pa_context*)pContext->pulse.pPulseContext); - ((ma_pa_mainloop_free_proc)pContext->pulse.pa_mainloop_free)((ma_pa_mainloop*)pContext->pulse.pMainLoop); - - ma_free(pContext->pulse.pServerName, &pContext->allocationCallbacks); - ma_free(pContext->pulse.pApplicationName, &pContext->allocationCallbacks); - -#ifndef MA_NO_RUNTIME_LINKING - ma_dlclose(ma_context_get_log(pContext), pContext->pulse.pulseSO); -#endif - - return MA_SUCCESS; -} - -static ma_result ma_context_init__pulse(ma_context* pContext, const ma_context_config* pConfig, ma_backend_callbacks* pCallbacks) -{ - ma_result result; -#ifndef MA_NO_RUNTIME_LINKING - const char* libpulseNames[] = { - "libpulse.so", - "libpulse.so.0" - }; - size_t i; - - for (i = 0; i < ma_countof(libpulseNames); ++i) { - pContext->pulse.pulseSO = ma_dlopen(ma_context_get_log(pContext), libpulseNames[i]); - if (pContext->pulse.pulseSO != NULL) { - break; - } - } - - if (pContext->pulse.pulseSO == NULL) { - return MA_NO_BACKEND; - } - - pContext->pulse.pa_mainloop_new = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_mainloop_new"); - pContext->pulse.pa_mainloop_free = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_mainloop_free"); - pContext->pulse.pa_mainloop_quit = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_mainloop_quit"); - pContext->pulse.pa_mainloop_get_api = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_mainloop_get_api"); - pContext->pulse.pa_mainloop_iterate = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_mainloop_iterate"); - pContext->pulse.pa_mainloop_wakeup = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_mainloop_wakeup"); - pContext->pulse.pa_threaded_mainloop_new = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_threaded_mainloop_new"); - pContext->pulse.pa_threaded_mainloop_free = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_threaded_mainloop_free"); - pContext->pulse.pa_threaded_mainloop_start = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_threaded_mainloop_start"); - pContext->pulse.pa_threaded_mainloop_stop = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_threaded_mainloop_stop"); - pContext->pulse.pa_threaded_mainloop_lock = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_threaded_mainloop_lock"); - pContext->pulse.pa_threaded_mainloop_unlock = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_threaded_mainloop_unlock"); - pContext->pulse.pa_threaded_mainloop_wait = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_threaded_mainloop_wait"); - pContext->pulse.pa_threaded_mainloop_signal = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_threaded_mainloop_signal"); - pContext->pulse.pa_threaded_mainloop_accept = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_threaded_mainloop_accept"); - pContext->pulse.pa_threaded_mainloop_get_retval = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_threaded_mainloop_get_retval"); - pContext->pulse.pa_threaded_mainloop_get_api = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_threaded_mainloop_get_api"); - pContext->pulse.pa_threaded_mainloop_in_thread = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_threaded_mainloop_in_thread"); - pContext->pulse.pa_threaded_mainloop_set_name = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_threaded_mainloop_set_name"); - pContext->pulse.pa_context_new = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_context_new"); - pContext->pulse.pa_context_unref = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_context_unref"); - pContext->pulse.pa_context_connect = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_context_connect"); - pContext->pulse.pa_context_disconnect = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_context_disconnect"); - pContext->pulse.pa_context_set_state_callback = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_context_set_state_callback"); - pContext->pulse.pa_context_get_state = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_context_get_state"); - pContext->pulse.pa_context_get_sink_info_list = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_context_get_sink_info_list"); - pContext->pulse.pa_context_get_source_info_list = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_context_get_source_info_list"); - pContext->pulse.pa_context_get_sink_info_by_name = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_context_get_sink_info_by_name"); - pContext->pulse.pa_context_get_source_info_by_name = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_context_get_source_info_by_name"); - pContext->pulse.pa_operation_unref = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_operation_unref"); - pContext->pulse.pa_operation_get_state = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_operation_get_state"); - pContext->pulse.pa_channel_map_init_extend = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_channel_map_init_extend"); - pContext->pulse.pa_channel_map_valid = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_channel_map_valid"); - pContext->pulse.pa_channel_map_compatible = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_channel_map_compatible"); - pContext->pulse.pa_stream_new = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_stream_new"); - pContext->pulse.pa_stream_unref = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_stream_unref"); - pContext->pulse.pa_stream_connect_playback = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_stream_connect_playback"); - pContext->pulse.pa_stream_connect_record = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_stream_connect_record"); - pContext->pulse.pa_stream_disconnect = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_stream_disconnect"); - pContext->pulse.pa_stream_get_state = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_stream_get_state"); - pContext->pulse.pa_stream_get_sample_spec = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_stream_get_sample_spec"); - pContext->pulse.pa_stream_get_channel_map = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_stream_get_channel_map"); - pContext->pulse.pa_stream_get_buffer_attr = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_stream_get_buffer_attr"); - pContext->pulse.pa_stream_set_buffer_attr = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_stream_set_buffer_attr"); - pContext->pulse.pa_stream_get_device_name = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_stream_get_device_name"); - pContext->pulse.pa_stream_set_write_callback = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_stream_set_write_callback"); - pContext->pulse.pa_stream_set_read_callback = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_stream_set_read_callback"); - pContext->pulse.pa_stream_set_suspended_callback = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_stream_set_suspended_callback"); - pContext->pulse.pa_stream_set_moved_callback = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_stream_set_moved_callback"); - pContext->pulse.pa_stream_is_suspended = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_stream_is_suspended"); - pContext->pulse.pa_stream_flush = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_stream_flush"); - pContext->pulse.pa_stream_drain = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_stream_drain"); - pContext->pulse.pa_stream_is_corked = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_stream_is_corked"); - pContext->pulse.pa_stream_cork = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_stream_cork"); - pContext->pulse.pa_stream_trigger = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_stream_trigger"); - pContext->pulse.pa_stream_begin_write = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_stream_begin_write"); - pContext->pulse.pa_stream_write = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_stream_write"); - pContext->pulse.pa_stream_peek = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_stream_peek"); - pContext->pulse.pa_stream_drop = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_stream_drop"); - pContext->pulse.pa_stream_writable_size = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_stream_writable_size"); - pContext->pulse.pa_stream_readable_size = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_stream_readable_size"); -#else - /* This strange assignment system is just for type safety. */ - ma_pa_mainloop_new_proc _pa_mainloop_new = pa_mainloop_new; - ma_pa_mainloop_free_proc _pa_mainloop_free = pa_mainloop_free; - ma_pa_mainloop_quit_proc _pa_mainloop_quit = pa_mainloop_quit; - ma_pa_mainloop_get_api_proc _pa_mainloop_get_api = pa_mainloop_get_api; - ma_pa_mainloop_iterate_proc _pa_mainloop_iterate = pa_mainloop_iterate; - ma_pa_mainloop_wakeup_proc _pa_mainloop_wakeup = pa_mainloop_wakeup; - ma_pa_threaded_mainloop_new_proc _pa_threaded_mainloop_new = pa_threaded_mainloop_new; - ma_pa_threaded_mainloop_free_proc _pa_threaded_mainloop_free = pa_threaded_mainloop_free; - ma_pa_threaded_mainloop_start_proc _pa_threaded_mainloop_start = pa_threaded_mainloop_start; - ma_pa_threaded_mainloop_stop_proc _pa_threaded_mainloop_stop = pa_threaded_mainloop_stop; - ma_pa_threaded_mainloop_lock_proc _pa_threaded_mainloop_lock = pa_threaded_mainloop_lock; - ma_pa_threaded_mainloop_unlock_proc _pa_threaded_mainloop_unlock = pa_threaded_mainloop_unlock; - ma_pa_threaded_mainloop_wait_proc _pa_threaded_mainloop_wait = pa_threaded_mainloop_wait; - ma_pa_threaded_mainloop_signal_proc _pa_threaded_mainloop_signal = pa_threaded_mainloop_signal; - ma_pa_threaded_mainloop_accept_proc _pa_threaded_mainloop_accept = pa_threaded_mainloop_accept; - ma_pa_threaded_mainloop_get_retval_proc _pa_threaded_mainloop_get_retval = pa_threaded_mainloop_get_retval; - ma_pa_threaded_mainloop_get_api_proc _pa_threaded_mainloop_get_api = pa_threaded_mainloop_get_api; - ma_pa_threaded_mainloop_in_thread_proc _pa_threaded_mainloop_in_thread = pa_threaded_mainloop_in_thread; - ma_pa_threaded_mainloop_set_name_proc _pa_threaded_mainloop_set_name = pa_threaded_mainloop_set_name; - ma_pa_context_new_proc _pa_context_new = pa_context_new; - ma_pa_context_unref_proc _pa_context_unref = pa_context_unref; - ma_pa_context_connect_proc _pa_context_connect = pa_context_connect; - ma_pa_context_disconnect_proc _pa_context_disconnect = pa_context_disconnect; - ma_pa_context_set_state_callback_proc _pa_context_set_state_callback = pa_context_set_state_callback; - ma_pa_context_get_state_proc _pa_context_get_state = pa_context_get_state; - ma_pa_context_get_sink_info_list_proc _pa_context_get_sink_info_list = pa_context_get_sink_info_list; - ma_pa_context_get_source_info_list_proc _pa_context_get_source_info_list = pa_context_get_source_info_list; - ma_pa_context_get_sink_info_by_name_proc _pa_context_get_sink_info_by_name = pa_context_get_sink_info_by_name; - ma_pa_context_get_source_info_by_name_proc _pa_context_get_source_info_by_name= pa_context_get_source_info_by_name; - ma_pa_operation_unref_proc _pa_operation_unref = pa_operation_unref; - ma_pa_operation_get_state_proc _pa_operation_get_state = pa_operation_get_state; - ma_pa_channel_map_init_extend_proc _pa_channel_map_init_extend = pa_channel_map_init_extend; - ma_pa_channel_map_valid_proc _pa_channel_map_valid = pa_channel_map_valid; - ma_pa_channel_map_compatible_proc _pa_channel_map_compatible = pa_channel_map_compatible; - ma_pa_stream_new_proc _pa_stream_new = pa_stream_new; - ma_pa_stream_unref_proc _pa_stream_unref = pa_stream_unref; - ma_pa_stream_connect_playback_proc _pa_stream_connect_playback = pa_stream_connect_playback; - ma_pa_stream_connect_record_proc _pa_stream_connect_record = pa_stream_connect_record; - ma_pa_stream_disconnect_proc _pa_stream_disconnect = pa_stream_disconnect; - ma_pa_stream_get_state_proc _pa_stream_get_state = pa_stream_get_state; - ma_pa_stream_get_sample_spec_proc _pa_stream_get_sample_spec = pa_stream_get_sample_spec; - ma_pa_stream_get_channel_map_proc _pa_stream_get_channel_map = pa_stream_get_channel_map; - ma_pa_stream_get_buffer_attr_proc _pa_stream_get_buffer_attr = pa_stream_get_buffer_attr; - ma_pa_stream_set_buffer_attr_proc _pa_stream_set_buffer_attr = pa_stream_set_buffer_attr; - ma_pa_stream_get_device_name_proc _pa_stream_get_device_name = pa_stream_get_device_name; - ma_pa_stream_set_write_callback_proc _pa_stream_set_write_callback = pa_stream_set_write_callback; - ma_pa_stream_set_read_callback_proc _pa_stream_set_read_callback = pa_stream_set_read_callback; - ma_pa_stream_set_suspended_callback_proc _pa_stream_set_suspended_callback = pa_stream_set_suspended_callback; - ma_pa_stream_set_moved_callback_proc _pa_stream_set_moved_callback = pa_stream_set_moved_callback; - ma_pa_stream_is_suspended_proc _pa_stream_is_suspended = pa_stream_is_suspended; - ma_pa_stream_flush_proc _pa_stream_flush = pa_stream_flush; - ma_pa_stream_drain_proc _pa_stream_drain = pa_stream_drain; - ma_pa_stream_is_corked_proc _pa_stream_is_corked = pa_stream_is_corked; - ma_pa_stream_cork_proc _pa_stream_cork = pa_stream_cork; - ma_pa_stream_trigger_proc _pa_stream_trigger = pa_stream_trigger; - ma_pa_stream_begin_write_proc _pa_stream_begin_write = pa_stream_begin_write; - ma_pa_stream_write_proc _pa_stream_write = pa_stream_write; - ma_pa_stream_peek_proc _pa_stream_peek = pa_stream_peek; - ma_pa_stream_drop_proc _pa_stream_drop = pa_stream_drop; - ma_pa_stream_writable_size_proc _pa_stream_writable_size = pa_stream_writable_size; - ma_pa_stream_readable_size_proc _pa_stream_readable_size = pa_stream_readable_size; - - pContext->pulse.pa_mainloop_new = (ma_proc)_pa_mainloop_new; - pContext->pulse.pa_mainloop_free = (ma_proc)_pa_mainloop_free; - pContext->pulse.pa_mainloop_quit = (ma_proc)_pa_mainloop_quit; - pContext->pulse.pa_mainloop_get_api = (ma_proc)_pa_mainloop_get_api; - pContext->pulse.pa_mainloop_iterate = (ma_proc)_pa_mainloop_iterate; - pContext->pulse.pa_mainloop_wakeup = (ma_proc)_pa_mainloop_wakeup; - pContext->pulse.pa_threaded_mainloop_new = (ma_proc)_pa_threaded_mainloop_new; - pContext->pulse.pa_threaded_mainloop_free = (ma_proc)_pa_threaded_mainloop_free; - pContext->pulse.pa_threaded_mainloop_start = (ma_proc)_pa_threaded_mainloop_start; - pContext->pulse.pa_threaded_mainloop_stop = (ma_proc)_pa_threaded_mainloop_stop; - pContext->pulse.pa_threaded_mainloop_lock = (ma_proc)_pa_threaded_mainloop_lock; - pContext->pulse.pa_threaded_mainloop_unlock = (ma_proc)_pa_threaded_mainloop_unlock; - pContext->pulse.pa_threaded_mainloop_wait = (ma_proc)_pa_threaded_mainloop_wait; - pContext->pulse.pa_threaded_mainloop_signal = (ma_proc)_pa_threaded_mainloop_signal; - pContext->pulse.pa_threaded_mainloop_accept = (ma_proc)_pa_threaded_mainloop_accept; - pContext->pulse.pa_threaded_mainloop_get_retval = (ma_proc)_pa_threaded_mainloop_get_retval; - pContext->pulse.pa_threaded_mainloop_get_api = (ma_proc)_pa_threaded_mainloop_get_api; - pContext->pulse.pa_threaded_mainloop_in_thread = (ma_proc)_pa_threaded_mainloop_in_thread; - pContext->pulse.pa_threaded_mainloop_set_name = (ma_proc)_pa_threaded_mainloop_set_name; - pContext->pulse.pa_context_new = (ma_proc)_pa_context_new; - pContext->pulse.pa_context_unref = (ma_proc)_pa_context_unref; - pContext->pulse.pa_context_connect = (ma_proc)_pa_context_connect; - pContext->pulse.pa_context_disconnect = (ma_proc)_pa_context_disconnect; - pContext->pulse.pa_context_set_state_callback = (ma_proc)_pa_context_set_state_callback; - pContext->pulse.pa_context_get_state = (ma_proc)_pa_context_get_state; - pContext->pulse.pa_context_get_sink_info_list = (ma_proc)_pa_context_get_sink_info_list; - pContext->pulse.pa_context_get_source_info_list = (ma_proc)_pa_context_get_source_info_list; - pContext->pulse.pa_context_get_sink_info_by_name = (ma_proc)_pa_context_get_sink_info_by_name; - pContext->pulse.pa_context_get_source_info_by_name = (ma_proc)_pa_context_get_source_info_by_name; - pContext->pulse.pa_operation_unref = (ma_proc)_pa_operation_unref; - pContext->pulse.pa_operation_get_state = (ma_proc)_pa_operation_get_state; - pContext->pulse.pa_channel_map_init_extend = (ma_proc)_pa_channel_map_init_extend; - pContext->pulse.pa_channel_map_valid = (ma_proc)_pa_channel_map_valid; - pContext->pulse.pa_channel_map_compatible = (ma_proc)_pa_channel_map_compatible; - pContext->pulse.pa_stream_new = (ma_proc)_pa_stream_new; - pContext->pulse.pa_stream_unref = (ma_proc)_pa_stream_unref; - pContext->pulse.pa_stream_connect_playback = (ma_proc)_pa_stream_connect_playback; - pContext->pulse.pa_stream_connect_record = (ma_proc)_pa_stream_connect_record; - pContext->pulse.pa_stream_disconnect = (ma_proc)_pa_stream_disconnect; - pContext->pulse.pa_stream_get_state = (ma_proc)_pa_stream_get_state; - pContext->pulse.pa_stream_get_sample_spec = (ma_proc)_pa_stream_get_sample_spec; - pContext->pulse.pa_stream_get_channel_map = (ma_proc)_pa_stream_get_channel_map; - pContext->pulse.pa_stream_get_buffer_attr = (ma_proc)_pa_stream_get_buffer_attr; - pContext->pulse.pa_stream_set_buffer_attr = (ma_proc)_pa_stream_set_buffer_attr; - pContext->pulse.pa_stream_get_device_name = (ma_proc)_pa_stream_get_device_name; - pContext->pulse.pa_stream_set_write_callback = (ma_proc)_pa_stream_set_write_callback; - pContext->pulse.pa_stream_set_read_callback = (ma_proc)_pa_stream_set_read_callback; - pContext->pulse.pa_stream_set_suspended_callback = (ma_proc)_pa_stream_set_suspended_callback; - pContext->pulse.pa_stream_set_moved_callback = (ma_proc)_pa_stream_set_moved_callback; - pContext->pulse.pa_stream_is_suspended = (ma_proc)_pa_stream_is_suspended; - pContext->pulse.pa_stream_flush = (ma_proc)_pa_stream_flush; - pContext->pulse.pa_stream_drain = (ma_proc)_pa_stream_drain; - pContext->pulse.pa_stream_is_corked = (ma_proc)_pa_stream_is_corked; - pContext->pulse.pa_stream_cork = (ma_proc)_pa_stream_cork; - pContext->pulse.pa_stream_trigger = (ma_proc)_pa_stream_trigger; - pContext->pulse.pa_stream_begin_write = (ma_proc)_pa_stream_begin_write; - pContext->pulse.pa_stream_write = (ma_proc)_pa_stream_write; - pContext->pulse.pa_stream_peek = (ma_proc)_pa_stream_peek; - pContext->pulse.pa_stream_drop = (ma_proc)_pa_stream_drop; - pContext->pulse.pa_stream_writable_size = (ma_proc)_pa_stream_writable_size; - pContext->pulse.pa_stream_readable_size = (ma_proc)_pa_stream_readable_size; -#endif - - /* We need to make a copy of the application and server names so we can pass them to the pa_context of each device. */ - pContext->pulse.pApplicationName = ma_copy_string(pConfig->pulse.pApplicationName, &pContext->allocationCallbacks); - if (pContext->pulse.pApplicationName == NULL && pConfig->pulse.pApplicationName != NULL) { - return MA_OUT_OF_MEMORY; - } - - pContext->pulse.pServerName = ma_copy_string(pConfig->pulse.pServerName, &pContext->allocationCallbacks); - if (pContext->pulse.pServerName == NULL && pConfig->pulse.pServerName != NULL) { - ma_free(pContext->pulse.pApplicationName, &pContext->allocationCallbacks); - return MA_OUT_OF_MEMORY; - } - - result = ma_init_pa_mainloop_and_pa_context__pulse(pContext, pConfig->pulse.pApplicationName, pConfig->pulse.pServerName, pConfig->pulse.tryAutoSpawn, &pContext->pulse.pMainLoop, &pContext->pulse.pPulseContext); - if (result != MA_SUCCESS) { - ma_free(pContext->pulse.pServerName, &pContext->allocationCallbacks); - ma_free(pContext->pulse.pApplicationName, &pContext->allocationCallbacks); - #ifndef MA_NO_RUNTIME_LINKING - ma_dlclose(ma_context_get_log(pContext), pContext->pulse.pulseSO); - #endif - return result; - } - - /* With pa_mainloop we run a synchronous backend, but we implement our own main loop. */ - pCallbacks->onContextInit = ma_context_init__pulse; - pCallbacks->onContextUninit = ma_context_uninit__pulse; - pCallbacks->onContextEnumerateDevices = ma_context_enumerate_devices__pulse; - pCallbacks->onContextGetDeviceInfo = ma_context_get_device_info__pulse; - pCallbacks->onDeviceInit = ma_device_init__pulse; - pCallbacks->onDeviceUninit = ma_device_uninit__pulse; - pCallbacks->onDeviceStart = ma_device_start__pulse; - pCallbacks->onDeviceStop = ma_device_stop__pulse; - pCallbacks->onDeviceRead = NULL; /* Not used because we're implementing onDeviceDataLoop. */ - pCallbacks->onDeviceWrite = NULL; /* Not used because we're implementing onDeviceDataLoop. */ - pCallbacks->onDeviceDataLoop = ma_device_data_loop__pulse; - pCallbacks->onDeviceDataLoopWakeup = ma_device_data_loop_wakeup__pulse; - - return MA_SUCCESS; -} -#endif - - -/****************************************************************************** - -JACK Backend - -******************************************************************************/ -#ifdef MA_HAS_JACK - -/* It is assumed jack.h is available when compile-time linking is being used. */ -#ifdef MA_NO_RUNTIME_LINKING -#include - -typedef jack_nframes_t ma_jack_nframes_t; -typedef jack_options_t ma_jack_options_t; -typedef jack_status_t ma_jack_status_t; -typedef jack_client_t ma_jack_client_t; -typedef jack_port_t ma_jack_port_t; -typedef JackProcessCallback ma_JackProcessCallback; -typedef JackBufferSizeCallback ma_JackBufferSizeCallback; -typedef JackShutdownCallback ma_JackShutdownCallback; -#define MA_JACK_DEFAULT_AUDIO_TYPE JACK_DEFAULT_AUDIO_TYPE -#define ma_JackNoStartServer JackNoStartServer -#define ma_JackPortIsInput JackPortIsInput -#define ma_JackPortIsOutput JackPortIsOutput -#define ma_JackPortIsPhysical JackPortIsPhysical -#else -typedef ma_uint32 ma_jack_nframes_t; -typedef int ma_jack_options_t; -typedef int ma_jack_status_t; -typedef struct ma_jack_client_t ma_jack_client_t; -typedef struct ma_jack_port_t ma_jack_port_t; -typedef int (* ma_JackProcessCallback) (ma_jack_nframes_t nframes, void* arg); -typedef int (* ma_JackBufferSizeCallback)(ma_jack_nframes_t nframes, void* arg); -typedef void (* ma_JackShutdownCallback) (void* arg); -#define MA_JACK_DEFAULT_AUDIO_TYPE "32 bit float mono audio" -#define ma_JackNoStartServer 1 -#define ma_JackPortIsInput 1 -#define ma_JackPortIsOutput 2 -#define ma_JackPortIsPhysical 4 -#endif - -typedef ma_jack_client_t* (* ma_jack_client_open_proc) (const char* client_name, ma_jack_options_t options, ma_jack_status_t* status, ...); -typedef int (* ma_jack_client_close_proc) (ma_jack_client_t* client); -typedef int (* ma_jack_client_name_size_proc) (void); -typedef int (* ma_jack_set_process_callback_proc) (ma_jack_client_t* client, ma_JackProcessCallback process_callback, void* arg); -typedef int (* ma_jack_set_buffer_size_callback_proc)(ma_jack_client_t* client, ma_JackBufferSizeCallback bufsize_callback, void* arg); -typedef void (* ma_jack_on_shutdown_proc) (ma_jack_client_t* client, ma_JackShutdownCallback function, void* arg); -typedef ma_jack_nframes_t (* ma_jack_get_sample_rate_proc) (ma_jack_client_t* client); -typedef ma_jack_nframes_t (* ma_jack_get_buffer_size_proc) (ma_jack_client_t* client); -typedef const char** (* ma_jack_get_ports_proc) (ma_jack_client_t* client, const char* port_name_pattern, const char* type_name_pattern, unsigned long flags); -typedef int (* ma_jack_activate_proc) (ma_jack_client_t* client); -typedef int (* ma_jack_deactivate_proc) (ma_jack_client_t* client); -typedef int (* ma_jack_connect_proc) (ma_jack_client_t* client, const char* source_port, const char* destination_port); -typedef ma_jack_port_t* (* ma_jack_port_register_proc) (ma_jack_client_t* client, const char* port_name, const char* port_type, unsigned long flags, unsigned long buffer_size); -typedef const char* (* ma_jack_port_name_proc) (const ma_jack_port_t* port); -typedef void* (* ma_jack_port_get_buffer_proc) (ma_jack_port_t* port, ma_jack_nframes_t nframes); -typedef void (* ma_jack_free_proc) (void* ptr); - -static ma_result ma_context_open_client__jack(ma_context* pContext, ma_jack_client_t** ppClient) -{ - size_t maxClientNameSize; - char clientName[256]; - ma_jack_status_t status; - ma_jack_client_t* pClient; - - MA_ASSERT(pContext != NULL); - MA_ASSERT(ppClient != NULL); - - if (ppClient) { - *ppClient = NULL; - } - - maxClientNameSize = ((ma_jack_client_name_size_proc)pContext->jack.jack_client_name_size)(); /* Includes null terminator. */ - ma_strncpy_s(clientName, ma_min(sizeof(clientName), maxClientNameSize), (pContext->jack.pClientName != NULL) ? pContext->jack.pClientName : "miniaudio", (size_t)-1); - - pClient = ((ma_jack_client_open_proc)pContext->jack.jack_client_open)(clientName, (pContext->jack.tryStartServer) ? 0 : ma_JackNoStartServer, &status, NULL); - if (pClient == NULL) { - return MA_FAILED_TO_OPEN_BACKEND_DEVICE; - } - - if (ppClient) { - *ppClient = pClient; - } - - return MA_SUCCESS; -} - - -static ma_result ma_context_enumerate_devices__jack(ma_context* pContext, ma_enum_devices_callback_proc callback, void* pUserData) -{ - ma_bool32 cbResult = MA_TRUE; - - MA_ASSERT(pContext != NULL); - MA_ASSERT(callback != NULL); - - /* Playback. */ - if (cbResult) { - ma_device_info deviceInfo; - MA_ZERO_OBJECT(&deviceInfo); - ma_strncpy_s(deviceInfo.name, sizeof(deviceInfo.name), MA_DEFAULT_PLAYBACK_DEVICE_NAME, (size_t)-1); - deviceInfo.isDefault = MA_TRUE; /* JACK only uses default devices. */ - cbResult = callback(pContext, ma_device_type_playback, &deviceInfo, pUserData); - } - - /* Capture. */ - if (cbResult) { - ma_device_info deviceInfo; - MA_ZERO_OBJECT(&deviceInfo); - ma_strncpy_s(deviceInfo.name, sizeof(deviceInfo.name), MA_DEFAULT_CAPTURE_DEVICE_NAME, (size_t)-1); - deviceInfo.isDefault = MA_TRUE; /* JACK only uses default devices. */ - cbResult = callback(pContext, ma_device_type_capture, &deviceInfo, pUserData); - } - - (void)cbResult; /* For silencing a static analysis warning. */ - - return MA_SUCCESS; -} - -static ma_result ma_context_get_device_info__jack(ma_context* pContext, ma_device_type deviceType, const ma_device_id* pDeviceID, ma_device_info* pDeviceInfo) -{ - ma_jack_client_t* pClient; - ma_result result; - const char** ppPorts; - - MA_ASSERT(pContext != NULL); - - if (pDeviceID != NULL && pDeviceID->jack != 0) { - return MA_NO_DEVICE; /* Don't know the device. */ - } - - /* Name / Description */ - if (deviceType == ma_device_type_playback) { - ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), MA_DEFAULT_PLAYBACK_DEVICE_NAME, (size_t)-1); - } else { - ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), MA_DEFAULT_CAPTURE_DEVICE_NAME, (size_t)-1); - } - - /* Jack only uses default devices. */ - pDeviceInfo->isDefault = MA_TRUE; - - /* Jack only supports f32 and has a specific channel count and sample rate. */ - pDeviceInfo->nativeDataFormats[0].format = ma_format_f32; - - /* The channel count and sample rate can only be determined by opening the device. */ - result = ma_context_open_client__jack(pContext, &pClient); - if (result != MA_SUCCESS) { - ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[JACK] Failed to open client."); - return result; - } - - pDeviceInfo->nativeDataFormats[0].sampleRate = ((ma_jack_get_sample_rate_proc)pContext->jack.jack_get_sample_rate)((ma_jack_client_t*)pClient); - pDeviceInfo->nativeDataFormats[0].channels = 0; - - ppPorts = ((ma_jack_get_ports_proc)pContext->jack.jack_get_ports)((ma_jack_client_t*)pClient, NULL, MA_JACK_DEFAULT_AUDIO_TYPE, ma_JackPortIsPhysical | ((deviceType == ma_device_type_playback) ? ma_JackPortIsInput : ma_JackPortIsOutput)); - if (ppPorts == NULL) { - ((ma_jack_client_close_proc)pContext->jack.jack_client_close)((ma_jack_client_t*)pClient); - ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[JACK] Failed to query physical ports."); - return MA_FAILED_TO_OPEN_BACKEND_DEVICE; - } - - while (ppPorts[pDeviceInfo->nativeDataFormats[0].channels] != NULL) { - pDeviceInfo->nativeDataFormats[0].channels += 1; - } - - pDeviceInfo->nativeDataFormats[0].flags = 0; - pDeviceInfo->nativeDataFormatCount = 1; - - ((ma_jack_free_proc)pContext->jack.jack_free)((void*)ppPorts); - ((ma_jack_client_close_proc)pContext->jack.jack_client_close)((ma_jack_client_t*)pClient); - - (void)pContext; - return MA_SUCCESS; -} - - -static ma_result ma_device_uninit__jack(ma_device* pDevice) -{ - ma_context* pContext; - - MA_ASSERT(pDevice != NULL); - - pContext = pDevice->pContext; - MA_ASSERT(pContext != NULL); - - if (pDevice->jack.pClient != NULL) { - ((ma_jack_client_close_proc)pContext->jack.jack_client_close)((ma_jack_client_t*)pDevice->jack.pClient); - } - - if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) { - ma_free(pDevice->jack.pIntermediaryBufferCapture, &pDevice->pContext->allocationCallbacks); - ma_free(pDevice->jack.ppPortsCapture, &pDevice->pContext->allocationCallbacks); - } - - if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) { - ma_free(pDevice->jack.pIntermediaryBufferPlayback, &pDevice->pContext->allocationCallbacks); - ma_free(pDevice->jack.ppPortsPlayback, &pDevice->pContext->allocationCallbacks); - } - - return MA_SUCCESS; -} - -static void ma_device__jack_shutdown_callback(void* pUserData) -{ - /* JACK died. Stop the device. */ - ma_device* pDevice = (ma_device*)pUserData; - MA_ASSERT(pDevice != NULL); - - ma_device_stop(pDevice); -} - -static int ma_device__jack_buffer_size_callback(ma_jack_nframes_t frameCount, void* pUserData) -{ - ma_device* pDevice = (ma_device*)pUserData; - MA_ASSERT(pDevice != NULL); - - if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) { - size_t newBufferSize = frameCount * (pDevice->capture.internalChannels * ma_get_bytes_per_sample(pDevice->capture.internalFormat)); - float* pNewBuffer = (float*)ma_calloc(newBufferSize, &pDevice->pContext->allocationCallbacks); - if (pNewBuffer == NULL) { - return MA_OUT_OF_MEMORY; - } - - ma_free(pDevice->jack.pIntermediaryBufferCapture, &pDevice->pContext->allocationCallbacks); - - pDevice->jack.pIntermediaryBufferCapture = pNewBuffer; - pDevice->playback.internalPeriodSizeInFrames = frameCount; - } - - if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) { - size_t newBufferSize = frameCount * (pDevice->playback.internalChannels * ma_get_bytes_per_sample(pDevice->playback.internalFormat)); - float* pNewBuffer = (float*)ma_calloc(newBufferSize, &pDevice->pContext->allocationCallbacks); - if (pNewBuffer == NULL) { - return MA_OUT_OF_MEMORY; - } - - ma_free(pDevice->jack.pIntermediaryBufferPlayback, &pDevice->pContext->allocationCallbacks); - - pDevice->jack.pIntermediaryBufferPlayback = pNewBuffer; - pDevice->playback.internalPeriodSizeInFrames = frameCount; - } - - return 0; -} - -static int ma_device__jack_process_callback(ma_jack_nframes_t frameCount, void* pUserData) -{ - ma_device* pDevice; - ma_context* pContext; - ma_uint32 iChannel; - - pDevice = (ma_device*)pUserData; - MA_ASSERT(pDevice != NULL); - - pContext = pDevice->pContext; - MA_ASSERT(pContext != NULL); - - if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) { - /* Channels need to be interleaved. */ - for (iChannel = 0; iChannel < pDevice->capture.internalChannels; ++iChannel) { - const float* pSrc = (const float*)((ma_jack_port_get_buffer_proc)pContext->jack.jack_port_get_buffer)((ma_jack_port_t*)pDevice->jack.ppPortsCapture[iChannel], frameCount); - if (pSrc != NULL) { - float* pDst = pDevice->jack.pIntermediaryBufferCapture + iChannel; - ma_jack_nframes_t iFrame; - for (iFrame = 0; iFrame < frameCount; ++iFrame) { - *pDst = *pSrc; - - pDst += pDevice->capture.internalChannels; - pSrc += 1; - } - } - } - - ma_device_handle_backend_data_callback(pDevice, NULL, pDevice->jack.pIntermediaryBufferCapture, frameCount); - } - - if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) { - ma_device_handle_backend_data_callback(pDevice, pDevice->jack.pIntermediaryBufferPlayback, NULL, frameCount); - - /* Channels need to be deinterleaved. */ - for (iChannel = 0; iChannel < pDevice->playback.internalChannels; ++iChannel) { - float* pDst = (float*)((ma_jack_port_get_buffer_proc)pContext->jack.jack_port_get_buffer)((ma_jack_port_t*)pDevice->jack.ppPortsPlayback[iChannel], frameCount); - if (pDst != NULL) { - const float* pSrc = pDevice->jack.pIntermediaryBufferPlayback + iChannel; - ma_jack_nframes_t iFrame; - for (iFrame = 0; iFrame < frameCount; ++iFrame) { - *pDst = *pSrc; - - pDst += 1; - pSrc += pDevice->playback.internalChannels; - } - } - } - } - - return 0; -} - -static ma_result ma_device_init__jack(ma_device* pDevice, const ma_device_config* pConfig, ma_device_descriptor* pDescriptorPlayback, ma_device_descriptor* pDescriptorCapture) -{ - ma_result result; - ma_uint32 periodSizeInFrames; - - MA_ASSERT(pConfig != NULL); - MA_ASSERT(pDevice != NULL); - - if (pConfig->deviceType == ma_device_type_loopback) { - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[JACK] Loopback mode not supported."); - return MA_DEVICE_TYPE_NOT_SUPPORTED; - } - - /* Only supporting default devices with JACK. */ - if (((pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) && pDescriptorPlayback->pDeviceID != NULL && pDescriptorPlayback->pDeviceID->jack != 0) || - ((pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) && pDescriptorCapture->pDeviceID != NULL && pDescriptorCapture->pDeviceID->jack != 0)) { - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[JACK] Only default devices are supported."); - return MA_NO_DEVICE; - } - - /* No exclusive mode with the JACK backend. */ - if (((pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) && pDescriptorPlayback->shareMode == ma_share_mode_exclusive) || - ((pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) && pDescriptorCapture->shareMode == ma_share_mode_exclusive)) { - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[JACK] Exclusive mode not supported."); - return MA_SHARE_MODE_NOT_SUPPORTED; - } - - /* Open the client. */ - result = ma_context_open_client__jack(pDevice->pContext, (ma_jack_client_t**)&pDevice->jack.pClient); - if (result != MA_SUCCESS) { - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[JACK] Failed to open client."); - return result; - } - - /* Callbacks. */ - if (((ma_jack_set_process_callback_proc)pDevice->pContext->jack.jack_set_process_callback)((ma_jack_client_t*)pDevice->jack.pClient, ma_device__jack_process_callback, pDevice) != 0) { - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[JACK] Failed to set process callback."); - return MA_FAILED_TO_OPEN_BACKEND_DEVICE; - } - if (((ma_jack_set_buffer_size_callback_proc)pDevice->pContext->jack.jack_set_buffer_size_callback)((ma_jack_client_t*)pDevice->jack.pClient, ma_device__jack_buffer_size_callback, pDevice) != 0) { - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[JACK] Failed to set buffer size callback."); - return MA_FAILED_TO_OPEN_BACKEND_DEVICE; - } - - ((ma_jack_on_shutdown_proc)pDevice->pContext->jack.jack_on_shutdown)((ma_jack_client_t*)pDevice->jack.pClient, ma_device__jack_shutdown_callback, pDevice); - - - /* The buffer size in frames can change. */ - periodSizeInFrames = ((ma_jack_get_buffer_size_proc)pDevice->pContext->jack.jack_get_buffer_size)((ma_jack_client_t*)pDevice->jack.pClient); - - if (pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) { - ma_uint32 iPort; - const char** ppPorts; - - pDescriptorCapture->format = ma_format_f32; - pDescriptorCapture->channels = 0; - pDescriptorCapture->sampleRate = ((ma_jack_get_sample_rate_proc)pDevice->pContext->jack.jack_get_sample_rate)((ma_jack_client_t*)pDevice->jack.pClient); - ma_channel_map_init_standard(ma_standard_channel_map_alsa, pDescriptorCapture->channelMap, ma_countof(pDescriptorCapture->channelMap), pDescriptorCapture->channels); - - ppPorts = ((ma_jack_get_ports_proc)pDevice->pContext->jack.jack_get_ports)((ma_jack_client_t*)pDevice->jack.pClient, NULL, MA_JACK_DEFAULT_AUDIO_TYPE, ma_JackPortIsPhysical | ma_JackPortIsOutput); - if (ppPorts == NULL) { - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[JACK] Failed to query physical ports."); - return MA_FAILED_TO_OPEN_BACKEND_DEVICE; - } - - /* Need to count the number of ports first so we can allocate some memory. */ - while (ppPorts[pDescriptorCapture->channels] != NULL) { - pDescriptorCapture->channels += 1; - } - - pDevice->jack.ppPortsCapture = (ma_ptr*)ma_malloc(sizeof(*pDevice->jack.ppPortsCapture) * pDescriptorCapture->channels, &pDevice->pContext->allocationCallbacks); - if (pDevice->jack.ppPortsCapture == NULL) { - return MA_OUT_OF_MEMORY; - } - - for (iPort = 0; iPort < pDescriptorCapture->channels; iPort += 1) { - char name[64]; - ma_strcpy_s(name, sizeof(name), "capture"); - ma_itoa_s((int)iPort, name+7, sizeof(name)-7, 10); /* 7 = length of "capture" */ - - pDevice->jack.ppPortsCapture[iPort] = ((ma_jack_port_register_proc)pDevice->pContext->jack.jack_port_register)((ma_jack_client_t*)pDevice->jack.pClient, name, MA_JACK_DEFAULT_AUDIO_TYPE, ma_JackPortIsInput, 0); - if (pDevice->jack.ppPortsCapture[iPort] == NULL) { - ((ma_jack_free_proc)pDevice->pContext->jack.jack_free)((void*)ppPorts); - ma_device_uninit__jack(pDevice); - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[JACK] Failed to register ports."); - return MA_FAILED_TO_OPEN_BACKEND_DEVICE; - } - } - - ((ma_jack_free_proc)pDevice->pContext->jack.jack_free)((void*)ppPorts); - - pDescriptorCapture->periodSizeInFrames = periodSizeInFrames; - pDescriptorCapture->periodCount = 1; /* There's no notion of a period in JACK. Just set to 1. */ - - pDevice->jack.pIntermediaryBufferCapture = (float*)ma_calloc(pDescriptorCapture->periodSizeInFrames * ma_get_bytes_per_frame(pDescriptorCapture->format, pDescriptorCapture->channels), &pDevice->pContext->allocationCallbacks); - if (pDevice->jack.pIntermediaryBufferCapture == NULL) { - ma_device_uninit__jack(pDevice); - return MA_OUT_OF_MEMORY; - } - } - - if (pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) { - ma_uint32 iPort; - const char** ppPorts; - - pDescriptorPlayback->format = ma_format_f32; - pDescriptorPlayback->channels = 0; - pDescriptorPlayback->sampleRate = ((ma_jack_get_sample_rate_proc)pDevice->pContext->jack.jack_get_sample_rate)((ma_jack_client_t*)pDevice->jack.pClient); - ma_channel_map_init_standard(ma_standard_channel_map_alsa, pDescriptorPlayback->channelMap, ma_countof(pDescriptorPlayback->channelMap), pDescriptorPlayback->channels); - - ppPorts = ((ma_jack_get_ports_proc)pDevice->pContext->jack.jack_get_ports)((ma_jack_client_t*)pDevice->jack.pClient, NULL, MA_JACK_DEFAULT_AUDIO_TYPE, ma_JackPortIsPhysical | ma_JackPortIsInput); - if (ppPorts == NULL) { - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[JACK] Failed to query physical ports."); - return MA_FAILED_TO_OPEN_BACKEND_DEVICE; - } - - /* Need to count the number of ports first so we can allocate some memory. */ - while (ppPorts[pDescriptorPlayback->channels] != NULL) { - pDescriptorPlayback->channels += 1; - } - - pDevice->jack.ppPortsPlayback = (ma_ptr*)ma_malloc(sizeof(*pDevice->jack.ppPortsPlayback) * pDescriptorPlayback->channels, &pDevice->pContext->allocationCallbacks); - if (pDevice->jack.ppPortsPlayback == NULL) { - ma_free(pDevice->jack.ppPortsCapture, &pDevice->pContext->allocationCallbacks); - return MA_OUT_OF_MEMORY; - } - - for (iPort = 0; iPort < pDescriptorPlayback->channels; iPort += 1) { - char name[64]; - ma_strcpy_s(name, sizeof(name), "playback"); - ma_itoa_s((int)iPort, name+8, sizeof(name)-8, 10); /* 8 = length of "playback" */ - - pDevice->jack.ppPortsPlayback[iPort] = ((ma_jack_port_register_proc)pDevice->pContext->jack.jack_port_register)((ma_jack_client_t*)pDevice->jack.pClient, name, MA_JACK_DEFAULT_AUDIO_TYPE, ma_JackPortIsOutput, 0); - if (pDevice->jack.ppPortsPlayback[iPort] == NULL) { - ((ma_jack_free_proc)pDevice->pContext->jack.jack_free)((void*)ppPorts); - ma_device_uninit__jack(pDevice); - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[JACK] Failed to register ports."); - return MA_FAILED_TO_OPEN_BACKEND_DEVICE; - } - } - - ((ma_jack_free_proc)pDevice->pContext->jack.jack_free)((void*)ppPorts); - - pDescriptorPlayback->periodSizeInFrames = periodSizeInFrames; - pDescriptorPlayback->periodCount = 1; /* There's no notion of a period in JACK. Just set to 1. */ - - pDevice->jack.pIntermediaryBufferPlayback = (float*)ma_calloc(pDescriptorPlayback->periodSizeInFrames * ma_get_bytes_per_frame(pDescriptorPlayback->format, pDescriptorPlayback->channels), &pDevice->pContext->allocationCallbacks); - if (pDevice->jack.pIntermediaryBufferPlayback == NULL) { - ma_device_uninit__jack(pDevice); - return MA_OUT_OF_MEMORY; - } - } - - return MA_SUCCESS; -} - - -static ma_result ma_device_start__jack(ma_device* pDevice) -{ - ma_context* pContext = pDevice->pContext; - int resultJACK; - size_t i; - - resultJACK = ((ma_jack_activate_proc)pContext->jack.jack_activate)((ma_jack_client_t*)pDevice->jack.pClient); - if (resultJACK != 0) { - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[JACK] Failed to activate the JACK client."); - return MA_FAILED_TO_START_BACKEND_DEVICE; - } - - if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) { - const char** ppServerPorts = ((ma_jack_get_ports_proc)pContext->jack.jack_get_ports)((ma_jack_client_t*)pDevice->jack.pClient, NULL, MA_JACK_DEFAULT_AUDIO_TYPE, ma_JackPortIsPhysical | ma_JackPortIsOutput); - if (ppServerPorts == NULL) { - ((ma_jack_deactivate_proc)pContext->jack.jack_deactivate)((ma_jack_client_t*)pDevice->jack.pClient); - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[JACK] Failed to retrieve physical ports."); - return MA_ERROR; - } - - for (i = 0; ppServerPorts[i] != NULL; ++i) { - const char* pServerPort = ppServerPorts[i]; - const char* pClientPort = ((ma_jack_port_name_proc)pContext->jack.jack_port_name)((ma_jack_port_t*)pDevice->jack.ppPortsCapture[i]); - - resultJACK = ((ma_jack_connect_proc)pContext->jack.jack_connect)((ma_jack_client_t*)pDevice->jack.pClient, pServerPort, pClientPort); - if (resultJACK != 0) { - ((ma_jack_free_proc)pContext->jack.jack_free)((void*)ppServerPorts); - ((ma_jack_deactivate_proc)pContext->jack.jack_deactivate)((ma_jack_client_t*)pDevice->jack.pClient); - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[JACK] Failed to connect ports."); - return MA_ERROR; - } - } - - ((ma_jack_free_proc)pContext->jack.jack_free)((void*)ppServerPorts); - } - - if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) { - const char** ppServerPorts = ((ma_jack_get_ports_proc)pContext->jack.jack_get_ports)((ma_jack_client_t*)pDevice->jack.pClient, NULL, MA_JACK_DEFAULT_AUDIO_TYPE, ma_JackPortIsPhysical | ma_JackPortIsInput); - if (ppServerPorts == NULL) { - ((ma_jack_deactivate_proc)pContext->jack.jack_deactivate)((ma_jack_client_t*)pDevice->jack.pClient); - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[JACK] Failed to retrieve physical ports."); - return MA_ERROR; - } - - for (i = 0; ppServerPorts[i] != NULL; ++i) { - const char* pServerPort = ppServerPorts[i]; - const char* pClientPort = ((ma_jack_port_name_proc)pContext->jack.jack_port_name)((ma_jack_port_t*)pDevice->jack.ppPortsPlayback[i]); - - resultJACK = ((ma_jack_connect_proc)pContext->jack.jack_connect)((ma_jack_client_t*)pDevice->jack.pClient, pClientPort, pServerPort); - if (resultJACK != 0) { - ((ma_jack_free_proc)pContext->jack.jack_free)((void*)ppServerPorts); - ((ma_jack_deactivate_proc)pContext->jack.jack_deactivate)((ma_jack_client_t*)pDevice->jack.pClient); - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[JACK] Failed to connect ports."); - return MA_ERROR; - } - } - - ((ma_jack_free_proc)pContext->jack.jack_free)((void*)ppServerPorts); - } - - return MA_SUCCESS; -} - -static ma_result ma_device_stop__jack(ma_device* pDevice) -{ - ma_context* pContext = pDevice->pContext; - - if (((ma_jack_deactivate_proc)pContext->jack.jack_deactivate)((ma_jack_client_t*)pDevice->jack.pClient) != 0) { - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[JACK] An error occurred when deactivating the JACK client."); - return MA_ERROR; - } - - ma_device__on_notification_stopped(pDevice); - - return MA_SUCCESS; -} - - -static ma_result ma_context_uninit__jack(ma_context* pContext) -{ - MA_ASSERT(pContext != NULL); - MA_ASSERT(pContext->backend == ma_backend_jack); - - ma_free(pContext->jack.pClientName, &pContext->allocationCallbacks); - pContext->jack.pClientName = NULL; - -#ifndef MA_NO_RUNTIME_LINKING - ma_dlclose(ma_context_get_log(pContext), pContext->jack.jackSO); -#endif - - return MA_SUCCESS; -} - -static ma_result ma_context_init__jack(ma_context* pContext, const ma_context_config* pConfig, ma_backend_callbacks* pCallbacks) -{ -#ifndef MA_NO_RUNTIME_LINKING - const char* libjackNames[] = { -#if defined(MA_WIN32) - "libjack.dll", - "libjack64.dll" -#endif -#if defined(MA_UNIX) - "libjack.so", - "libjack.so.0" -#endif - }; - size_t i; - - for (i = 0; i < ma_countof(libjackNames); ++i) { - pContext->jack.jackSO = ma_dlopen(ma_context_get_log(pContext), libjackNames[i]); - if (pContext->jack.jackSO != NULL) { - break; - } - } - - if (pContext->jack.jackSO == NULL) { - return MA_NO_BACKEND; - } - - pContext->jack.jack_client_open = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->jack.jackSO, "jack_client_open"); - pContext->jack.jack_client_close = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->jack.jackSO, "jack_client_close"); - pContext->jack.jack_client_name_size = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->jack.jackSO, "jack_client_name_size"); - pContext->jack.jack_set_process_callback = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->jack.jackSO, "jack_set_process_callback"); - pContext->jack.jack_set_buffer_size_callback = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->jack.jackSO, "jack_set_buffer_size_callback"); - pContext->jack.jack_on_shutdown = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->jack.jackSO, "jack_on_shutdown"); - pContext->jack.jack_get_sample_rate = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->jack.jackSO, "jack_get_sample_rate"); - pContext->jack.jack_get_buffer_size = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->jack.jackSO, "jack_get_buffer_size"); - pContext->jack.jack_get_ports = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->jack.jackSO, "jack_get_ports"); - pContext->jack.jack_activate = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->jack.jackSO, "jack_activate"); - pContext->jack.jack_deactivate = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->jack.jackSO, "jack_deactivate"); - pContext->jack.jack_connect = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->jack.jackSO, "jack_connect"); - pContext->jack.jack_port_register = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->jack.jackSO, "jack_port_register"); - pContext->jack.jack_port_name = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->jack.jackSO, "jack_port_name"); - pContext->jack.jack_port_get_buffer = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->jack.jackSO, "jack_port_get_buffer"); - pContext->jack.jack_free = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->jack.jackSO, "jack_free"); -#else - /* - This strange assignment system is here just to ensure type safety of miniaudio's function pointer - types. If anything differs slightly the compiler should throw a warning. - */ - ma_jack_client_open_proc _jack_client_open = jack_client_open; - ma_jack_client_close_proc _jack_client_close = jack_client_close; - ma_jack_client_name_size_proc _jack_client_name_size = jack_client_name_size; - ma_jack_set_process_callback_proc _jack_set_process_callback = jack_set_process_callback; - ma_jack_set_buffer_size_callback_proc _jack_set_buffer_size_callback = jack_set_buffer_size_callback; - ma_jack_on_shutdown_proc _jack_on_shutdown = jack_on_shutdown; - ma_jack_get_sample_rate_proc _jack_get_sample_rate = jack_get_sample_rate; - ma_jack_get_buffer_size_proc _jack_get_buffer_size = jack_get_buffer_size; - ma_jack_get_ports_proc _jack_get_ports = jack_get_ports; - ma_jack_activate_proc _jack_activate = jack_activate; - ma_jack_deactivate_proc _jack_deactivate = jack_deactivate; - ma_jack_connect_proc _jack_connect = jack_connect; - ma_jack_port_register_proc _jack_port_register = jack_port_register; - ma_jack_port_name_proc _jack_port_name = jack_port_name; - ma_jack_port_get_buffer_proc _jack_port_get_buffer = jack_port_get_buffer; - ma_jack_free_proc _jack_free = jack_free; - - pContext->jack.jack_client_open = (ma_proc)_jack_client_open; - pContext->jack.jack_client_close = (ma_proc)_jack_client_close; - pContext->jack.jack_client_name_size = (ma_proc)_jack_client_name_size; - pContext->jack.jack_set_process_callback = (ma_proc)_jack_set_process_callback; - pContext->jack.jack_set_buffer_size_callback = (ma_proc)_jack_set_buffer_size_callback; - pContext->jack.jack_on_shutdown = (ma_proc)_jack_on_shutdown; - pContext->jack.jack_get_sample_rate = (ma_proc)_jack_get_sample_rate; - pContext->jack.jack_get_buffer_size = (ma_proc)_jack_get_buffer_size; - pContext->jack.jack_get_ports = (ma_proc)_jack_get_ports; - pContext->jack.jack_activate = (ma_proc)_jack_activate; - pContext->jack.jack_deactivate = (ma_proc)_jack_deactivate; - pContext->jack.jack_connect = (ma_proc)_jack_connect; - pContext->jack.jack_port_register = (ma_proc)_jack_port_register; - pContext->jack.jack_port_name = (ma_proc)_jack_port_name; - pContext->jack.jack_port_get_buffer = (ma_proc)_jack_port_get_buffer; - pContext->jack.jack_free = (ma_proc)_jack_free; -#endif - - if (pConfig->jack.pClientName != NULL) { - pContext->jack.pClientName = ma_copy_string(pConfig->jack.pClientName, &pContext->allocationCallbacks); - } - pContext->jack.tryStartServer = pConfig->jack.tryStartServer; - - /* - Getting here means the JACK library is installed, but it doesn't necessarily mean it's usable. We need to quickly test this by connecting - a temporary client. - */ - { - ma_jack_client_t* pDummyClient; - ma_result result = ma_context_open_client__jack(pContext, &pDummyClient); - if (result != MA_SUCCESS) { - ma_free(pContext->jack.pClientName, &pContext->allocationCallbacks); - #ifndef MA_NO_RUNTIME_LINKING - ma_dlclose(ma_context_get_log(pContext), pContext->jack.jackSO); - #endif - return MA_NO_BACKEND; - } - - ((ma_jack_client_close_proc)pContext->jack.jack_client_close)((ma_jack_client_t*)pDummyClient); - } - - - pCallbacks->onContextInit = ma_context_init__jack; - pCallbacks->onContextUninit = ma_context_uninit__jack; - pCallbacks->onContextEnumerateDevices = ma_context_enumerate_devices__jack; - pCallbacks->onContextGetDeviceInfo = ma_context_get_device_info__jack; - pCallbacks->onDeviceInit = ma_device_init__jack; - pCallbacks->onDeviceUninit = ma_device_uninit__jack; - pCallbacks->onDeviceStart = ma_device_start__jack; - pCallbacks->onDeviceStop = ma_device_stop__jack; - pCallbacks->onDeviceRead = NULL; /* Not used because JACK is asynchronous. */ - pCallbacks->onDeviceWrite = NULL; /* Not used because JACK is asynchronous. */ - pCallbacks->onDeviceDataLoop = NULL; /* Not used because JACK is asynchronous. */ - - return MA_SUCCESS; -} -#endif /* MA_HAS_JACK */ - - - -/****************************************************************************** - -Core Audio Backend - -References -========== -- Technical Note TN2091: Device input using the HAL Output Audio Unit - https://developer.apple.com/library/archive/technotes/tn2091/_index.html - -******************************************************************************/ -#ifdef MA_HAS_COREAUDIO -#include - -#if defined(TARGET_OS_IPHONE) && TARGET_OS_IPHONE == 1 - #define MA_APPLE_MOBILE - #if defined(TARGET_OS_TV) && TARGET_OS_TV == 1 - #define MA_APPLE_TV - #endif - #if defined(TARGET_OS_WATCH) && TARGET_OS_WATCH == 1 - #define MA_APPLE_WATCH - #endif - #if __has_feature(objc_arc) - #define MA_BRIDGE_TRANSFER __bridge_transfer - #define MA_BRIDGE_RETAINED __bridge_retained - #else - #define MA_BRIDGE_TRANSFER - #define MA_BRIDGE_RETAINED - #endif -#else - #define MA_APPLE_DESKTOP -#endif - -#if defined(MA_APPLE_DESKTOP) -#include -#else -#include -#endif - -#include - -/* CoreFoundation */ -typedef Boolean (* ma_CFStringGetCString_proc)(CFStringRef theString, char* buffer, CFIndex bufferSize, CFStringEncoding encoding); -typedef void (* ma_CFRelease_proc)(CFTypeRef cf); - -/* CoreAudio */ -#if defined(MA_APPLE_DESKTOP) -typedef OSStatus (* ma_AudioObjectGetPropertyData_proc)(AudioObjectID inObjectID, const AudioObjectPropertyAddress* inAddress, UInt32 inQualifierDataSize, const void* inQualifierData, UInt32* ioDataSize, void* outData); -typedef OSStatus (* ma_AudioObjectGetPropertyDataSize_proc)(AudioObjectID inObjectID, const AudioObjectPropertyAddress* inAddress, UInt32 inQualifierDataSize, const void* inQualifierData, UInt32* outDataSize); -typedef OSStatus (* ma_AudioObjectSetPropertyData_proc)(AudioObjectID inObjectID, const AudioObjectPropertyAddress* inAddress, UInt32 inQualifierDataSize, const void* inQualifierData, UInt32 inDataSize, const void* inData); -typedef OSStatus (* ma_AudioObjectAddPropertyListener_proc)(AudioObjectID inObjectID, const AudioObjectPropertyAddress* inAddress, AudioObjectPropertyListenerProc inListener, void* inClientData); -typedef OSStatus (* ma_AudioObjectRemovePropertyListener_proc)(AudioObjectID inObjectID, const AudioObjectPropertyAddress* inAddress, AudioObjectPropertyListenerProc inListener, void* inClientData); -#endif - -/* AudioToolbox */ -typedef AudioComponent (* ma_AudioComponentFindNext_proc)(AudioComponent inComponent, const AudioComponentDescription* inDesc); -typedef OSStatus (* ma_AudioComponentInstanceDispose_proc)(AudioComponentInstance inInstance); -typedef OSStatus (* ma_AudioComponentInstanceNew_proc)(AudioComponent inComponent, AudioComponentInstance* outInstance); -typedef OSStatus (* ma_AudioOutputUnitStart_proc)(AudioUnit inUnit); -typedef OSStatus (* ma_AudioOutputUnitStop_proc)(AudioUnit inUnit); -typedef OSStatus (* ma_AudioUnitAddPropertyListener_proc)(AudioUnit inUnit, AudioUnitPropertyID inID, AudioUnitPropertyListenerProc inProc, void* inProcUserData); -typedef OSStatus (* ma_AudioUnitGetPropertyInfo_proc)(AudioUnit inUnit, AudioUnitPropertyID inID, AudioUnitScope inScope, AudioUnitElement inElement, UInt32* outDataSize, Boolean* outWriteable); -typedef OSStatus (* ma_AudioUnitGetProperty_proc)(AudioUnit inUnit, AudioUnitPropertyID inID, AudioUnitScope inScope, AudioUnitElement inElement, void* outData, UInt32* ioDataSize); -typedef OSStatus (* ma_AudioUnitSetProperty_proc)(AudioUnit inUnit, AudioUnitPropertyID inID, AudioUnitScope inScope, AudioUnitElement inElement, const void* inData, UInt32 inDataSize); -typedef OSStatus (* ma_AudioUnitInitialize_proc)(AudioUnit inUnit); -typedef OSStatus (* ma_AudioUnitRender_proc)(AudioUnit inUnit, AudioUnitRenderActionFlags* ioActionFlags, const AudioTimeStamp* inTimeStamp, UInt32 inOutputBusNumber, UInt32 inNumberFrames, AudioBufferList* ioData); - - -#define MA_COREAUDIO_OUTPUT_BUS 0 -#define MA_COREAUDIO_INPUT_BUS 1 - -#if defined(MA_APPLE_DESKTOP) -static ma_result ma_device_reinit_internal__coreaudio(ma_device* pDevice, ma_device_type deviceType, ma_bool32 disposePreviousAudioUnit); -#endif - -/* -Core Audio - -So far, Core Audio has been the worst backend to work with due to being both unintuitive and having almost no documentation -apart from comments in the headers (which admittedly are quite good). For my own purposes, and for anybody out there whose -needing to figure out how this darn thing works, I'm going to outline a few things here. - -Since miniaudio is a fairly low-level API, one of the things it needs is control over specific devices, and it needs to be -able to identify whether or not it can be used as playback and/or capture. The AudioObject API is the only one I've seen -that supports this level of detail. There was some public domain sample code I stumbled across that used the AudioComponent -and AudioUnit APIs, but I couldn't see anything that gave low-level control over device selection and capabilities (the -distinction between playback and capture in particular). Therefore, miniaudio is using the AudioObject API. - -Most (all?) functions in the AudioObject API take a AudioObjectID as its input. This is the device identifier. When -retrieving global information, such as the device list, you use kAudioObjectSystemObject. When retrieving device-specific -data, you pass in the ID for that device. In order to retrieve device-specific IDs you need to enumerate over each of the -devices. This is done using the AudioObjectGetPropertyDataSize() and AudioObjectGetPropertyData() APIs which seem to be -the central APIs for retrieving information about the system and specific devices. - -To use the AudioObjectGetPropertyData() API you need to use the notion of a property address. A property address is a -structure with three variables and is used to identify which property you are getting or setting. The first is the "selector" -which is basically the specific property that you're wanting to retrieve or set. The second is the "scope", which is -typically set to kAudioObjectPropertyScopeGlobal, kAudioObjectPropertyScopeInput for input-specific properties and -kAudioObjectPropertyScopeOutput for output-specific properties. The last is the "element" which is always set to -kAudioObjectPropertyElementMain in miniaudio's case. I don't know of any cases where this would be set to anything different. - -Back to the earlier issue of device retrieval, you first use the AudioObjectGetPropertyDataSize() API to retrieve the size -of the raw data which is just a list of AudioDeviceID's. You use the kAudioObjectSystemObject AudioObjectID, and a property -address with the kAudioHardwarePropertyDevices selector and the kAudioObjectPropertyScopeGlobal scope. Once you have the -size, allocate a block of memory of that size and then call AudioObjectGetPropertyData(). The data is just a list of -AudioDeviceID's so just do "dataSize/sizeof(AudioDeviceID)" to know the device count. -*/ - -#if defined(MA_APPLE_MOBILE) -static void ma_device__on_notification_interruption_began(ma_device* pDevice) -{ - ma_device__on_notification(ma_device_notification_init(pDevice, ma_device_notification_type_interruption_began)); -} - -static void ma_device__on_notification_interruption_ended(ma_device* pDevice) -{ - ma_device__on_notification(ma_device_notification_init(pDevice, ma_device_notification_type_interruption_ended)); -} -#endif - -static ma_result ma_result_from_OSStatus(OSStatus status) -{ - switch (status) - { - case noErr: return MA_SUCCESS; - #if defined(MA_APPLE_DESKTOP) - case kAudioHardwareNotRunningError: return MA_DEVICE_NOT_STARTED; - case kAudioHardwareUnspecifiedError: return MA_ERROR; - case kAudioHardwareUnknownPropertyError: return MA_INVALID_ARGS; - case kAudioHardwareBadPropertySizeError: return MA_INVALID_OPERATION; - case kAudioHardwareIllegalOperationError: return MA_INVALID_OPERATION; - case kAudioHardwareBadObjectError: return MA_INVALID_ARGS; - case kAudioHardwareBadDeviceError: return MA_INVALID_ARGS; - case kAudioHardwareBadStreamError: return MA_INVALID_ARGS; - case kAudioHardwareUnsupportedOperationError: return MA_INVALID_OPERATION; - case kAudioDeviceUnsupportedFormatError: return MA_FORMAT_NOT_SUPPORTED; - case kAudioDevicePermissionsError: return MA_ACCESS_DENIED; - #endif - default: return MA_ERROR; - } -} - -#if 0 -static ma_channel ma_channel_from_AudioChannelBitmap(AudioChannelBitmap bit) -{ - switch (bit) - { - case kAudioChannelBit_Left: return MA_CHANNEL_LEFT; - case kAudioChannelBit_Right: return MA_CHANNEL_RIGHT; - case kAudioChannelBit_Center: return MA_CHANNEL_FRONT_CENTER; - case kAudioChannelBit_LFEScreen: return MA_CHANNEL_LFE; - case kAudioChannelBit_LeftSurround: return MA_CHANNEL_BACK_LEFT; - case kAudioChannelBit_RightSurround: return MA_CHANNEL_BACK_RIGHT; - case kAudioChannelBit_LeftCenter: return MA_CHANNEL_FRONT_LEFT_CENTER; - case kAudioChannelBit_RightCenter: return MA_CHANNEL_FRONT_RIGHT_CENTER; - case kAudioChannelBit_CenterSurround: return MA_CHANNEL_BACK_CENTER; - case kAudioChannelBit_LeftSurroundDirect: return MA_CHANNEL_SIDE_LEFT; - case kAudioChannelBit_RightSurroundDirect: return MA_CHANNEL_SIDE_RIGHT; - case kAudioChannelBit_TopCenterSurround: return MA_CHANNEL_TOP_CENTER; - case kAudioChannelBit_VerticalHeightLeft: return MA_CHANNEL_TOP_FRONT_LEFT; - case kAudioChannelBit_VerticalHeightCenter: return MA_CHANNEL_TOP_FRONT_CENTER; - case kAudioChannelBit_VerticalHeightRight: return MA_CHANNEL_TOP_FRONT_RIGHT; - case kAudioChannelBit_TopBackLeft: return MA_CHANNEL_TOP_BACK_LEFT; - case kAudioChannelBit_TopBackCenter: return MA_CHANNEL_TOP_BACK_CENTER; - case kAudioChannelBit_TopBackRight: return MA_CHANNEL_TOP_BACK_RIGHT; - default: return MA_CHANNEL_NONE; - } -} -#endif - -static ma_result ma_format_from_AudioStreamBasicDescription(const AudioStreamBasicDescription* pDescription, ma_format* pFormatOut) -{ - MA_ASSERT(pDescription != NULL); - MA_ASSERT(pFormatOut != NULL); - - *pFormatOut = ma_format_unknown; /* Safety. */ - - /* There's a few things miniaudio doesn't support. */ - if (pDescription->mFormatID != kAudioFormatLinearPCM) { - return MA_FORMAT_NOT_SUPPORTED; - } - - /* We don't support any non-packed formats that are aligned high. */ - if ((pDescription->mFormatFlags & kLinearPCMFormatFlagIsAlignedHigh) != 0) { - return MA_FORMAT_NOT_SUPPORTED; - } - - /* Only supporting native-endian. */ - if ((ma_is_little_endian() && (pDescription->mFormatFlags & kAudioFormatFlagIsBigEndian) != 0) || (ma_is_big_endian() && (pDescription->mFormatFlags & kAudioFormatFlagIsBigEndian) == 0)) { - return MA_FORMAT_NOT_SUPPORTED; - } - - /* We are not currently supporting non-interleaved formats (this will be added in a future version of miniaudio). */ - /*if ((pDescription->mFormatFlags & kAudioFormatFlagIsNonInterleaved) != 0) { - return MA_FORMAT_NOT_SUPPORTED; - }*/ - - if ((pDescription->mFormatFlags & kLinearPCMFormatFlagIsFloat) != 0) { - if (pDescription->mBitsPerChannel == 32) { - *pFormatOut = ma_format_f32; - return MA_SUCCESS; - } - } else { - if ((pDescription->mFormatFlags & kLinearPCMFormatFlagIsSignedInteger) != 0) { - if (pDescription->mBitsPerChannel == 16) { - *pFormatOut = ma_format_s16; - return MA_SUCCESS; - } else if (pDescription->mBitsPerChannel == 24) { - if (pDescription->mBytesPerFrame == (pDescription->mBitsPerChannel/8 * pDescription->mChannelsPerFrame)) { - *pFormatOut = ma_format_s24; - return MA_SUCCESS; - } else { - if (pDescription->mBytesPerFrame/pDescription->mChannelsPerFrame == sizeof(ma_int32)) { - /* TODO: Implement ma_format_s24_32. */ - /**pFormatOut = ma_format_s24_32;*/ - /*return MA_SUCCESS;*/ - return MA_FORMAT_NOT_SUPPORTED; - } - } - } else if (pDescription->mBitsPerChannel == 32) { - *pFormatOut = ma_format_s32; - return MA_SUCCESS; - } - } else { - if (pDescription->mBitsPerChannel == 8) { - *pFormatOut = ma_format_u8; - return MA_SUCCESS; - } - } - } - - /* Getting here means the format is not supported. */ - return MA_FORMAT_NOT_SUPPORTED; -} - -#if defined(MA_APPLE_DESKTOP) -static ma_channel ma_channel_from_AudioChannelLabel(AudioChannelLabel label) -{ - switch (label) - { - case kAudioChannelLabel_Unknown: return MA_CHANNEL_NONE; - case kAudioChannelLabel_Unused: return MA_CHANNEL_NONE; - case kAudioChannelLabel_UseCoordinates: return MA_CHANNEL_NONE; - case kAudioChannelLabel_Left: return MA_CHANNEL_LEFT; - case kAudioChannelLabel_Right: return MA_CHANNEL_RIGHT; - case kAudioChannelLabel_Center: return MA_CHANNEL_FRONT_CENTER; - case kAudioChannelLabel_LFEScreen: return MA_CHANNEL_LFE; - case kAudioChannelLabel_LeftSurround: return MA_CHANNEL_BACK_LEFT; - case kAudioChannelLabel_RightSurround: return MA_CHANNEL_BACK_RIGHT; - case kAudioChannelLabel_LeftCenter: return MA_CHANNEL_FRONT_LEFT_CENTER; - case kAudioChannelLabel_RightCenter: return MA_CHANNEL_FRONT_RIGHT_CENTER; - case kAudioChannelLabel_CenterSurround: return MA_CHANNEL_BACK_CENTER; - case kAudioChannelLabel_LeftSurroundDirect: return MA_CHANNEL_SIDE_LEFT; - case kAudioChannelLabel_RightSurroundDirect: return MA_CHANNEL_SIDE_RIGHT; - case kAudioChannelLabel_TopCenterSurround: return MA_CHANNEL_TOP_CENTER; - case kAudioChannelLabel_VerticalHeightLeft: return MA_CHANNEL_TOP_FRONT_LEFT; - case kAudioChannelLabel_VerticalHeightCenter: return MA_CHANNEL_TOP_FRONT_CENTER; - case kAudioChannelLabel_VerticalHeightRight: return MA_CHANNEL_TOP_FRONT_RIGHT; - case kAudioChannelLabel_TopBackLeft: return MA_CHANNEL_TOP_BACK_LEFT; - case kAudioChannelLabel_TopBackCenter: return MA_CHANNEL_TOP_BACK_CENTER; - case kAudioChannelLabel_TopBackRight: return MA_CHANNEL_TOP_BACK_RIGHT; - case kAudioChannelLabel_RearSurroundLeft: return MA_CHANNEL_BACK_LEFT; - case kAudioChannelLabel_RearSurroundRight: return MA_CHANNEL_BACK_RIGHT; - case kAudioChannelLabel_LeftWide: return MA_CHANNEL_SIDE_LEFT; - case kAudioChannelLabel_RightWide: return MA_CHANNEL_SIDE_RIGHT; - case kAudioChannelLabel_LFE2: return MA_CHANNEL_LFE; - case kAudioChannelLabel_LeftTotal: return MA_CHANNEL_LEFT; - case kAudioChannelLabel_RightTotal: return MA_CHANNEL_RIGHT; - case kAudioChannelLabel_HearingImpaired: return MA_CHANNEL_NONE; - case kAudioChannelLabel_Narration: return MA_CHANNEL_MONO; - case kAudioChannelLabel_Mono: return MA_CHANNEL_MONO; - case kAudioChannelLabel_DialogCentricMix: return MA_CHANNEL_MONO; - case kAudioChannelLabel_CenterSurroundDirect: return MA_CHANNEL_BACK_CENTER; - case kAudioChannelLabel_Haptic: return MA_CHANNEL_NONE; - case kAudioChannelLabel_Ambisonic_W: return MA_CHANNEL_NONE; - case kAudioChannelLabel_Ambisonic_X: return MA_CHANNEL_NONE; - case kAudioChannelLabel_Ambisonic_Y: return MA_CHANNEL_NONE; - case kAudioChannelLabel_Ambisonic_Z: return MA_CHANNEL_NONE; - case kAudioChannelLabel_MS_Mid: return MA_CHANNEL_LEFT; - case kAudioChannelLabel_MS_Side: return MA_CHANNEL_RIGHT; - case kAudioChannelLabel_XY_X: return MA_CHANNEL_LEFT; - case kAudioChannelLabel_XY_Y: return MA_CHANNEL_RIGHT; - case kAudioChannelLabel_HeadphonesLeft: return MA_CHANNEL_LEFT; - case kAudioChannelLabel_HeadphonesRight: return MA_CHANNEL_RIGHT; - case kAudioChannelLabel_ClickTrack: return MA_CHANNEL_NONE; - case kAudioChannelLabel_ForeignLanguage: return MA_CHANNEL_NONE; - case kAudioChannelLabel_Discrete: return MA_CHANNEL_NONE; - case kAudioChannelLabel_Discrete_0: return MA_CHANNEL_AUX_0; - case kAudioChannelLabel_Discrete_1: return MA_CHANNEL_AUX_1; - case kAudioChannelLabel_Discrete_2: return MA_CHANNEL_AUX_2; - case kAudioChannelLabel_Discrete_3: return MA_CHANNEL_AUX_3; - case kAudioChannelLabel_Discrete_4: return MA_CHANNEL_AUX_4; - case kAudioChannelLabel_Discrete_5: return MA_CHANNEL_AUX_5; - case kAudioChannelLabel_Discrete_6: return MA_CHANNEL_AUX_6; - case kAudioChannelLabel_Discrete_7: return MA_CHANNEL_AUX_7; - case kAudioChannelLabel_Discrete_8: return MA_CHANNEL_AUX_8; - case kAudioChannelLabel_Discrete_9: return MA_CHANNEL_AUX_9; - case kAudioChannelLabel_Discrete_10: return MA_CHANNEL_AUX_10; - case kAudioChannelLabel_Discrete_11: return MA_CHANNEL_AUX_11; - case kAudioChannelLabel_Discrete_12: return MA_CHANNEL_AUX_12; - case kAudioChannelLabel_Discrete_13: return MA_CHANNEL_AUX_13; - case kAudioChannelLabel_Discrete_14: return MA_CHANNEL_AUX_14; - case kAudioChannelLabel_Discrete_15: return MA_CHANNEL_AUX_15; - case kAudioChannelLabel_Discrete_65535: return MA_CHANNEL_NONE; - - #if 0 /* Introduced in a later version of macOS. */ - case kAudioChannelLabel_HOA_ACN: return MA_CHANNEL_NONE; - case kAudioChannelLabel_HOA_ACN_0: return MA_CHANNEL_AUX_0; - case kAudioChannelLabel_HOA_ACN_1: return MA_CHANNEL_AUX_1; - case kAudioChannelLabel_HOA_ACN_2: return MA_CHANNEL_AUX_2; - case kAudioChannelLabel_HOA_ACN_3: return MA_CHANNEL_AUX_3; - case kAudioChannelLabel_HOA_ACN_4: return MA_CHANNEL_AUX_4; - case kAudioChannelLabel_HOA_ACN_5: return MA_CHANNEL_AUX_5; - case kAudioChannelLabel_HOA_ACN_6: return MA_CHANNEL_AUX_6; - case kAudioChannelLabel_HOA_ACN_7: return MA_CHANNEL_AUX_7; - case kAudioChannelLabel_HOA_ACN_8: return MA_CHANNEL_AUX_8; - case kAudioChannelLabel_HOA_ACN_9: return MA_CHANNEL_AUX_9; - case kAudioChannelLabel_HOA_ACN_10: return MA_CHANNEL_AUX_10; - case kAudioChannelLabel_HOA_ACN_11: return MA_CHANNEL_AUX_11; - case kAudioChannelLabel_HOA_ACN_12: return MA_CHANNEL_AUX_12; - case kAudioChannelLabel_HOA_ACN_13: return MA_CHANNEL_AUX_13; - case kAudioChannelLabel_HOA_ACN_14: return MA_CHANNEL_AUX_14; - case kAudioChannelLabel_HOA_ACN_15: return MA_CHANNEL_AUX_15; - case kAudioChannelLabel_HOA_ACN_65024: return MA_CHANNEL_NONE; - #endif - - default: return MA_CHANNEL_NONE; - } -} - -static ma_result ma_get_channel_map_from_AudioChannelLayout(AudioChannelLayout* pChannelLayout, ma_channel* pChannelMap, size_t channelMapCap) -{ - MA_ASSERT(pChannelLayout != NULL); - - if (pChannelLayout->mChannelLayoutTag == kAudioChannelLayoutTag_UseChannelDescriptions) { - UInt32 iChannel; - for (iChannel = 0; iChannel < pChannelLayout->mNumberChannelDescriptions && iChannel < channelMapCap; ++iChannel) { - pChannelMap[iChannel] = ma_channel_from_AudioChannelLabel(pChannelLayout->mChannelDescriptions[iChannel].mChannelLabel); - } - } else -#if 0 - if (pChannelLayout->mChannelLayoutTag == kAudioChannelLayoutTag_UseChannelBitmap) { - /* This is the same kind of system that's used by Windows audio APIs. */ - UInt32 iChannel = 0; - UInt32 iBit; - AudioChannelBitmap bitmap = pChannelLayout->mChannelBitmap; - for (iBit = 0; iBit < 32 && iChannel < channelMapCap; ++iBit) { - AudioChannelBitmap bit = bitmap & (1 << iBit); - if (bit != 0) { - pChannelMap[iChannel++] = ma_channel_from_AudioChannelBit(bit); - } - } - } else -#endif - { - /* - Need to use the tag to determine the channel map. For now I'm just assuming a default channel map, but later on this should - be updated to determine the mapping based on the tag. - */ - UInt32 channelCount; - - /* Our channel map retrieval APIs below take 32-bit integers, so we'll want to clamp the channel map capacity. */ - if (channelMapCap > 0xFFFFFFFF) { - channelMapCap = 0xFFFFFFFF; - } - - channelCount = ma_min(AudioChannelLayoutTag_GetNumberOfChannels(pChannelLayout->mChannelLayoutTag), (UInt32)channelMapCap); - - switch (pChannelLayout->mChannelLayoutTag) - { - case kAudioChannelLayoutTag_Mono: - case kAudioChannelLayoutTag_Stereo: - case kAudioChannelLayoutTag_StereoHeadphones: - case kAudioChannelLayoutTag_MatrixStereo: - case kAudioChannelLayoutTag_MidSide: - case kAudioChannelLayoutTag_XY: - case kAudioChannelLayoutTag_Binaural: - case kAudioChannelLayoutTag_Ambisonic_B_Format: - { - ma_channel_map_init_standard(ma_standard_channel_map_default, pChannelMap, channelMapCap, channelCount); - } break; - - case kAudioChannelLayoutTag_Octagonal: - { - pChannelMap[7] = MA_CHANNEL_SIDE_RIGHT; - pChannelMap[6] = MA_CHANNEL_SIDE_LEFT; - } MA_FALLTHROUGH; /* Intentional fallthrough. */ - case kAudioChannelLayoutTag_Hexagonal: - { - pChannelMap[5] = MA_CHANNEL_BACK_CENTER; - } MA_FALLTHROUGH; /* Intentional fallthrough. */ - case kAudioChannelLayoutTag_Pentagonal: - { - pChannelMap[4] = MA_CHANNEL_FRONT_CENTER; - } MA_FALLTHROUGH; /* Intentional fallthrough. */ - case kAudioChannelLayoutTag_Quadraphonic: - { - pChannelMap[3] = MA_CHANNEL_BACK_RIGHT; - pChannelMap[2] = MA_CHANNEL_BACK_LEFT; - pChannelMap[1] = MA_CHANNEL_RIGHT; - pChannelMap[0] = MA_CHANNEL_LEFT; - } break; - - /* TODO: Add support for more tags here. */ - - default: - { - ma_channel_map_init_standard(ma_standard_channel_map_default, pChannelMap, channelMapCap, channelCount); - } break; - } - } - - return MA_SUCCESS; -} - -#if (defined(MAC_OS_VERSION_12_0) && MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_VERSION_12_0) || \ - (defined(__IPHONE_15_0) && __IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_15_0) -#define AUDIO_OBJECT_PROPERTY_ELEMENT kAudioObjectPropertyElementMain -#else -/* kAudioObjectPropertyElementMaster is deprecated. */ -#define AUDIO_OBJECT_PROPERTY_ELEMENT kAudioObjectPropertyElementMaster -#endif - -/* kAudioDevicePropertyScope* were renamed to kAudioObjectPropertyScope* in 10.8. */ -#if !defined(MAC_OS_X_VERSION_10_8) || (MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_8) -#define kAudioObjectPropertyScopeInput kAudioDevicePropertyScopeInput -#define kAudioObjectPropertyScopeOutput kAudioDevicePropertyScopeOutput -#endif - -static ma_result ma_get_device_object_ids__coreaudio(ma_context* pContext, UInt32* pDeviceCount, AudioObjectID** ppDeviceObjectIDs) /* NOTE: Free the returned buffer with ma_free(). */ -{ - AudioObjectPropertyAddress propAddressDevices; - UInt32 deviceObjectsDataSize; - OSStatus status; - AudioObjectID* pDeviceObjectIDs; - - MA_ASSERT(pContext != NULL); - MA_ASSERT(pDeviceCount != NULL); - MA_ASSERT(ppDeviceObjectIDs != NULL); - - /* Safety. */ - *pDeviceCount = 0; - *ppDeviceObjectIDs = NULL; - - propAddressDevices.mSelector = kAudioHardwarePropertyDevices; - propAddressDevices.mScope = kAudioObjectPropertyScopeGlobal; - propAddressDevices.mElement = AUDIO_OBJECT_PROPERTY_ELEMENT; - - status = ((ma_AudioObjectGetPropertyDataSize_proc)pContext->coreaudio.AudioObjectGetPropertyDataSize)(kAudioObjectSystemObject, &propAddressDevices, 0, NULL, &deviceObjectsDataSize); - if (status != noErr) { - return ma_result_from_OSStatus(status); - } - - pDeviceObjectIDs = (AudioObjectID*)ma_malloc(deviceObjectsDataSize, &pContext->allocationCallbacks); - if (pDeviceObjectIDs == NULL) { - return MA_OUT_OF_MEMORY; - } - - status = ((ma_AudioObjectGetPropertyData_proc)pContext->coreaudio.AudioObjectGetPropertyData)(kAudioObjectSystemObject, &propAddressDevices, 0, NULL, &deviceObjectsDataSize, pDeviceObjectIDs); - if (status != noErr) { - ma_free(pDeviceObjectIDs, &pContext->allocationCallbacks); - return ma_result_from_OSStatus(status); - } - - *pDeviceCount = deviceObjectsDataSize / sizeof(AudioObjectID); - *ppDeviceObjectIDs = pDeviceObjectIDs; - - return MA_SUCCESS; -} - -static ma_result ma_get_AudioObject_uid_as_CFStringRef(ma_context* pContext, AudioObjectID objectID, CFStringRef* pUID) -{ - AudioObjectPropertyAddress propAddress; - UInt32 dataSize; - OSStatus status; - - MA_ASSERT(pContext != NULL); - - propAddress.mSelector = kAudioDevicePropertyDeviceUID; - propAddress.mScope = kAudioObjectPropertyScopeGlobal; - propAddress.mElement = AUDIO_OBJECT_PROPERTY_ELEMENT; - - dataSize = sizeof(*pUID); - status = ((ma_AudioObjectGetPropertyData_proc)pContext->coreaudio.AudioObjectGetPropertyData)(objectID, &propAddress, 0, NULL, &dataSize, pUID); - if (status != noErr) { - return ma_result_from_OSStatus(status); - } - - return MA_SUCCESS; -} - -static ma_result ma_get_AudioObject_uid(ma_context* pContext, AudioObjectID objectID, size_t bufferSize, char* bufferOut) -{ - CFStringRef uid; - ma_result result; - - MA_ASSERT(pContext != NULL); - - result = ma_get_AudioObject_uid_as_CFStringRef(pContext, objectID, &uid); - if (result != MA_SUCCESS) { - return result; - } - - if (!((ma_CFStringGetCString_proc)pContext->coreaudio.CFStringGetCString)(uid, bufferOut, bufferSize, kCFStringEncodingUTF8)) { - return MA_ERROR; - } - - ((ma_CFRelease_proc)pContext->coreaudio.CFRelease)(uid); - return MA_SUCCESS; -} - -static ma_result ma_get_AudioObject_name(ma_context* pContext, AudioObjectID objectID, size_t bufferSize, char* bufferOut) -{ - AudioObjectPropertyAddress propAddress; - CFStringRef deviceName = NULL; - UInt32 dataSize; - OSStatus status; - - MA_ASSERT(pContext != NULL); - - propAddress.mSelector = kAudioDevicePropertyDeviceNameCFString; - propAddress.mScope = kAudioObjectPropertyScopeGlobal; - propAddress.mElement = AUDIO_OBJECT_PROPERTY_ELEMENT; - - dataSize = sizeof(deviceName); - status = ((ma_AudioObjectGetPropertyData_proc)pContext->coreaudio.AudioObjectGetPropertyData)(objectID, &propAddress, 0, NULL, &dataSize, &deviceName); - if (status != noErr) { - return ma_result_from_OSStatus(status); - } - - if (!((ma_CFStringGetCString_proc)pContext->coreaudio.CFStringGetCString)(deviceName, bufferOut, bufferSize, kCFStringEncodingUTF8)) { - return MA_ERROR; - } - - ((ma_CFRelease_proc)pContext->coreaudio.CFRelease)(deviceName); - return MA_SUCCESS; -} - -static ma_bool32 ma_does_AudioObject_support_scope(ma_context* pContext, AudioObjectID deviceObjectID, AudioObjectPropertyScope scope) -{ - AudioObjectPropertyAddress propAddress; - UInt32 dataSize; - OSStatus status; - AudioBufferList* pBufferList; - ma_bool32 isSupported; - - MA_ASSERT(pContext != NULL); - - /* To know whether or not a device is an input device we need ot look at the stream configuration. If it has an output channel it's a playback device. */ - propAddress.mSelector = kAudioDevicePropertyStreamConfiguration; - propAddress.mScope = scope; - propAddress.mElement = AUDIO_OBJECT_PROPERTY_ELEMENT; - - status = ((ma_AudioObjectGetPropertyDataSize_proc)pContext->coreaudio.AudioObjectGetPropertyDataSize)(deviceObjectID, &propAddress, 0, NULL, &dataSize); - if (status != noErr) { - return MA_FALSE; - } - - pBufferList = (AudioBufferList*)ma_malloc(dataSize, &pContext->allocationCallbacks); - if (pBufferList == NULL) { - return MA_FALSE; /* Out of memory. */ - } - - status = ((ma_AudioObjectGetPropertyData_proc)pContext->coreaudio.AudioObjectGetPropertyData)(deviceObjectID, &propAddress, 0, NULL, &dataSize, pBufferList); - if (status != noErr) { - ma_free(pBufferList, &pContext->allocationCallbacks); - return MA_FALSE; - } - - isSupported = MA_FALSE; - if (pBufferList->mNumberBuffers > 0) { - isSupported = MA_TRUE; - } - - ma_free(pBufferList, &pContext->allocationCallbacks); - return isSupported; -} - -static ma_bool32 ma_does_AudioObject_support_playback(ma_context* pContext, AudioObjectID deviceObjectID) -{ - return ma_does_AudioObject_support_scope(pContext, deviceObjectID, kAudioObjectPropertyScopeOutput); -} - -static ma_bool32 ma_does_AudioObject_support_capture(ma_context* pContext, AudioObjectID deviceObjectID) -{ - return ma_does_AudioObject_support_scope(pContext, deviceObjectID, kAudioObjectPropertyScopeInput); -} - - -static ma_result ma_get_AudioObject_stream_descriptions(ma_context* pContext, AudioObjectID deviceObjectID, ma_device_type deviceType, UInt32* pDescriptionCount, AudioStreamRangedDescription** ppDescriptions) /* NOTE: Free the returned pointer with ma_free(). */ -{ - AudioObjectPropertyAddress propAddress; - UInt32 dataSize; - OSStatus status; - AudioStreamRangedDescription* pDescriptions; - - MA_ASSERT(pContext != NULL); - MA_ASSERT(pDescriptionCount != NULL); - MA_ASSERT(ppDescriptions != NULL); - - /* - TODO: Experiment with kAudioStreamPropertyAvailablePhysicalFormats instead of (or in addition to) kAudioStreamPropertyAvailableVirtualFormats. My - MacBook Pro uses s24/32 format, however, which miniaudio does not currently support. - */ - propAddress.mSelector = kAudioStreamPropertyAvailableVirtualFormats; /*kAudioStreamPropertyAvailablePhysicalFormats;*/ - propAddress.mScope = (deviceType == ma_device_type_playback) ? kAudioObjectPropertyScopeOutput : kAudioObjectPropertyScopeInput; - propAddress.mElement = AUDIO_OBJECT_PROPERTY_ELEMENT; - - status = ((ma_AudioObjectGetPropertyDataSize_proc)pContext->coreaudio.AudioObjectGetPropertyDataSize)(deviceObjectID, &propAddress, 0, NULL, &dataSize); - if (status != noErr) { - return ma_result_from_OSStatus(status); - } - - pDescriptions = (AudioStreamRangedDescription*)ma_malloc(dataSize, &pContext->allocationCallbacks); - if (pDescriptions == NULL) { - return MA_OUT_OF_MEMORY; - } - - status = ((ma_AudioObjectGetPropertyData_proc)pContext->coreaudio.AudioObjectGetPropertyData)(deviceObjectID, &propAddress, 0, NULL, &dataSize, pDescriptions); - if (status != noErr) { - ma_free(pDescriptions, &pContext->allocationCallbacks); - return ma_result_from_OSStatus(status); - } - - *pDescriptionCount = dataSize / sizeof(*pDescriptions); - *ppDescriptions = pDescriptions; - return MA_SUCCESS; -} - - -static ma_result ma_get_AudioObject_channel_layout(ma_context* pContext, AudioObjectID deviceObjectID, ma_device_type deviceType, AudioChannelLayout** ppChannelLayout) /* NOTE: Free the returned pointer with ma_free(). */ -{ - AudioObjectPropertyAddress propAddress; - UInt32 dataSize; - OSStatus status; - AudioChannelLayout* pChannelLayout; - - MA_ASSERT(pContext != NULL); - MA_ASSERT(ppChannelLayout != NULL); - - *ppChannelLayout = NULL; /* Safety. */ - - propAddress.mSelector = kAudioDevicePropertyPreferredChannelLayout; - propAddress.mScope = (deviceType == ma_device_type_playback) ? kAudioObjectPropertyScopeOutput : kAudioObjectPropertyScopeInput; - propAddress.mElement = AUDIO_OBJECT_PROPERTY_ELEMENT; - - status = ((ma_AudioObjectGetPropertyDataSize_proc)pContext->coreaudio.AudioObjectGetPropertyDataSize)(deviceObjectID, &propAddress, 0, NULL, &dataSize); - if (status != noErr) { - return ma_result_from_OSStatus(status); - } - - pChannelLayout = (AudioChannelLayout*)ma_malloc(dataSize, &pContext->allocationCallbacks); - if (pChannelLayout == NULL) { - return MA_OUT_OF_MEMORY; - } - - status = ((ma_AudioObjectGetPropertyData_proc)pContext->coreaudio.AudioObjectGetPropertyData)(deviceObjectID, &propAddress, 0, NULL, &dataSize, pChannelLayout); - if (status != noErr) { - ma_free(pChannelLayout, &pContext->allocationCallbacks); - return ma_result_from_OSStatus(status); - } - - *ppChannelLayout = pChannelLayout; - return MA_SUCCESS; -} - -static ma_result ma_get_AudioObject_channel_count(ma_context* pContext, AudioObjectID deviceObjectID, ma_device_type deviceType, ma_uint32* pChannelCount) -{ - AudioChannelLayout* pChannelLayout; - ma_result result; - - MA_ASSERT(pContext != NULL); - MA_ASSERT(pChannelCount != NULL); - - *pChannelCount = 0; /* Safety. */ - - result = ma_get_AudioObject_channel_layout(pContext, deviceObjectID, deviceType, &pChannelLayout); - if (result != MA_SUCCESS) { - return result; - } - - if (pChannelLayout->mChannelLayoutTag == kAudioChannelLayoutTag_UseChannelDescriptions) { - *pChannelCount = pChannelLayout->mNumberChannelDescriptions; - } else if (pChannelLayout->mChannelLayoutTag == kAudioChannelLayoutTag_UseChannelBitmap) { - *pChannelCount = ma_count_set_bits(pChannelLayout->mChannelBitmap); - } else { - *pChannelCount = AudioChannelLayoutTag_GetNumberOfChannels(pChannelLayout->mChannelLayoutTag); - } - - ma_free(pChannelLayout, &pContext->allocationCallbacks); - return MA_SUCCESS; -} - -#if 0 -static ma_result ma_get_AudioObject_channel_map(ma_context* pContext, AudioObjectID deviceObjectID, ma_device_type deviceType, ma_channel* pChannelMap, size_t channelMapCap) -{ - AudioChannelLayout* pChannelLayout; - ma_result result; - - MA_ASSERT(pContext != NULL); - - result = ma_get_AudioObject_channel_layout(pContext, deviceObjectID, deviceType, &pChannelLayout); - if (result != MA_SUCCESS) { - return result; /* Rather than always failing here, would it be more robust to simply assume a default? */ - } - - result = ma_get_channel_map_from_AudioChannelLayout(pChannelLayout, pChannelMap, channelMapCap); - if (result != MA_SUCCESS) { - ma_free(pChannelLayout, &pContext->allocationCallbacks); - return result; - } - - ma_free(pChannelLayout, &pContext->allocationCallbacks); - return result; -} -#endif - -static ma_result ma_get_AudioObject_sample_rates(ma_context* pContext, AudioObjectID deviceObjectID, ma_device_type deviceType, UInt32* pSampleRateRangesCount, AudioValueRange** ppSampleRateRanges) /* NOTE: Free the returned pointer with ma_free(). */ -{ - AudioObjectPropertyAddress propAddress; - UInt32 dataSize; - OSStatus status; - AudioValueRange* pSampleRateRanges; - - MA_ASSERT(pContext != NULL); - MA_ASSERT(pSampleRateRangesCount != NULL); - MA_ASSERT(ppSampleRateRanges != NULL); - - /* Safety. */ - *pSampleRateRangesCount = 0; - *ppSampleRateRanges = NULL; - - propAddress.mSelector = kAudioDevicePropertyAvailableNominalSampleRates; - propAddress.mScope = (deviceType == ma_device_type_playback) ? kAudioObjectPropertyScopeOutput : kAudioObjectPropertyScopeInput; - propAddress.mElement = AUDIO_OBJECT_PROPERTY_ELEMENT; - - status = ((ma_AudioObjectGetPropertyDataSize_proc)pContext->coreaudio.AudioObjectGetPropertyDataSize)(deviceObjectID, &propAddress, 0, NULL, &dataSize); - if (status != noErr) { - return ma_result_from_OSStatus(status); - } - - pSampleRateRanges = (AudioValueRange*)ma_malloc(dataSize, &pContext->allocationCallbacks); - if (pSampleRateRanges == NULL) { - return MA_OUT_OF_MEMORY; - } - - status = ((ma_AudioObjectGetPropertyData_proc)pContext->coreaudio.AudioObjectGetPropertyData)(deviceObjectID, &propAddress, 0, NULL, &dataSize, pSampleRateRanges); - if (status != noErr) { - ma_free(pSampleRateRanges, &pContext->allocationCallbacks); - return ma_result_from_OSStatus(status); - } - - *pSampleRateRangesCount = dataSize / sizeof(*pSampleRateRanges); - *ppSampleRateRanges = pSampleRateRanges; - return MA_SUCCESS; -} - -#if 0 -static ma_result ma_get_AudioObject_get_closest_sample_rate(ma_context* pContext, AudioObjectID deviceObjectID, ma_device_type deviceType, ma_uint32 sampleRateIn, ma_uint32* pSampleRateOut) -{ - UInt32 sampleRateRangeCount; - AudioValueRange* pSampleRateRanges; - ma_result result; - - MA_ASSERT(pContext != NULL); - MA_ASSERT(pSampleRateOut != NULL); - - *pSampleRateOut = 0; /* Safety. */ - - result = ma_get_AudioObject_sample_rates(pContext, deviceObjectID, deviceType, &sampleRateRangeCount, &pSampleRateRanges); - if (result != MA_SUCCESS) { - return result; - } - - if (sampleRateRangeCount == 0) { - ma_free(pSampleRateRanges, &pContext->allocationCallbacks); - return MA_ERROR; /* Should never hit this case should we? */ - } - - if (sampleRateIn == 0) { - /* Search in order of miniaudio's preferred priority. */ - UInt32 iMALSampleRate; - for (iMALSampleRate = 0; iMALSampleRate < ma_countof(g_maStandardSampleRatePriorities); ++iMALSampleRate) { - ma_uint32 malSampleRate = g_maStandardSampleRatePriorities[iMALSampleRate]; - UInt32 iCASampleRate; - for (iCASampleRate = 0; iCASampleRate < sampleRateRangeCount; ++iCASampleRate) { - AudioValueRange caSampleRate = pSampleRateRanges[iCASampleRate]; - if (caSampleRate.mMinimum <= malSampleRate && caSampleRate.mMaximum >= malSampleRate) { - *pSampleRateOut = malSampleRate; - ma_free(pSampleRateRanges, &pContext->allocationCallbacks); - return MA_SUCCESS; - } - } - } - - /* - If we get here it means none of miniaudio's standard sample rates matched any of the supported sample rates from the device. In this - case we just fall back to the first one reported by Core Audio. - */ - MA_ASSERT(sampleRateRangeCount > 0); - - *pSampleRateOut = pSampleRateRanges[0].mMinimum; - ma_free(pSampleRateRanges, &pContext->allocationCallbacks); - return MA_SUCCESS; - } else { - /* Find the closest match to this sample rate. */ - UInt32 currentAbsoluteDifference = INT32_MAX; - UInt32 iCurrentClosestRange = (UInt32)-1; - UInt32 iRange; - for (iRange = 0; iRange < sampleRateRangeCount; ++iRange) { - if (pSampleRateRanges[iRange].mMinimum <= sampleRateIn && pSampleRateRanges[iRange].mMaximum >= sampleRateIn) { - *pSampleRateOut = sampleRateIn; - ma_free(pSampleRateRanges, &pContext->allocationCallbacks); - return MA_SUCCESS; - } else { - UInt32 absoluteDifference; - if (pSampleRateRanges[iRange].mMinimum > sampleRateIn) { - absoluteDifference = pSampleRateRanges[iRange].mMinimum - sampleRateIn; - } else { - absoluteDifference = sampleRateIn - pSampleRateRanges[iRange].mMaximum; - } - - if (currentAbsoluteDifference > absoluteDifference) { - currentAbsoluteDifference = absoluteDifference; - iCurrentClosestRange = iRange; - } - } - } - - MA_ASSERT(iCurrentClosestRange != (UInt32)-1); - - *pSampleRateOut = pSampleRateRanges[iCurrentClosestRange].mMinimum; - ma_free(pSampleRateRanges, &pContext->allocationCallbacks); - return MA_SUCCESS; - } - - /* Should never get here, but it would mean we weren't able to find any suitable sample rates. */ - /*ma_free(pSampleRateRanges, &pContext->allocationCallbacks);*/ - /*return MA_ERROR;*/ -} -#endif - -static ma_result ma_get_AudioObject_closest_buffer_size_in_frames(ma_context* pContext, AudioObjectID deviceObjectID, ma_device_type deviceType, ma_uint32 bufferSizeInFramesIn, ma_uint32* pBufferSizeInFramesOut) -{ - AudioObjectPropertyAddress propAddress; - AudioValueRange bufferSizeRange; - UInt32 dataSize; - OSStatus status; - - MA_ASSERT(pContext != NULL); - MA_ASSERT(pBufferSizeInFramesOut != NULL); - - *pBufferSizeInFramesOut = 0; /* Safety. */ - - propAddress.mSelector = kAudioDevicePropertyBufferFrameSizeRange; - propAddress.mScope = (deviceType == ma_device_type_playback) ? kAudioObjectPropertyScopeOutput : kAudioObjectPropertyScopeInput; - propAddress.mElement = AUDIO_OBJECT_PROPERTY_ELEMENT; - - dataSize = sizeof(bufferSizeRange); - status = ((ma_AudioObjectGetPropertyData_proc)pContext->coreaudio.AudioObjectGetPropertyData)(deviceObjectID, &propAddress, 0, NULL, &dataSize, &bufferSizeRange); - if (status != noErr) { - return ma_result_from_OSStatus(status); - } - - /* This is just a clamp. */ - if (bufferSizeInFramesIn < bufferSizeRange.mMinimum) { - *pBufferSizeInFramesOut = (ma_uint32)bufferSizeRange.mMinimum; - } else if (bufferSizeInFramesIn > bufferSizeRange.mMaximum) { - *pBufferSizeInFramesOut = (ma_uint32)bufferSizeRange.mMaximum; - } else { - *pBufferSizeInFramesOut = bufferSizeInFramesIn; - } - - return MA_SUCCESS; -} - -static ma_result ma_set_AudioObject_buffer_size_in_frames(ma_context* pContext, AudioObjectID deviceObjectID, ma_device_type deviceType, ma_uint32* pPeriodSizeInOut) -{ - ma_result result; - ma_uint32 chosenBufferSizeInFrames; - AudioObjectPropertyAddress propAddress; - UInt32 dataSize; - OSStatus status; - - MA_ASSERT(pContext != NULL); - - result = ma_get_AudioObject_closest_buffer_size_in_frames(pContext, deviceObjectID, deviceType, *pPeriodSizeInOut, &chosenBufferSizeInFrames); - if (result != MA_SUCCESS) { - return result; - } - - /* Try setting the size of the buffer... If this fails we just use whatever is currently set. */ - propAddress.mSelector = kAudioDevicePropertyBufferFrameSize; - propAddress.mScope = (deviceType == ma_device_type_playback) ? kAudioObjectPropertyScopeOutput : kAudioObjectPropertyScopeInput; - propAddress.mElement = AUDIO_OBJECT_PROPERTY_ELEMENT; - - ((ma_AudioObjectSetPropertyData_proc)pContext->coreaudio.AudioObjectSetPropertyData)(deviceObjectID, &propAddress, 0, NULL, sizeof(chosenBufferSizeInFrames), &chosenBufferSizeInFrames); - - /* Get the actual size of the buffer. */ - dataSize = sizeof(*pPeriodSizeInOut); - status = ((ma_AudioObjectGetPropertyData_proc)pContext->coreaudio.AudioObjectGetPropertyData)(deviceObjectID, &propAddress, 0, NULL, &dataSize, &chosenBufferSizeInFrames); - if (status != noErr) { - return ma_result_from_OSStatus(status); - } - - *pPeriodSizeInOut = chosenBufferSizeInFrames; - return MA_SUCCESS; -} - -static ma_result ma_find_default_AudioObjectID(ma_context* pContext, ma_device_type deviceType, AudioObjectID* pDeviceObjectID) -{ - AudioObjectPropertyAddress propAddressDefaultDevice; - UInt32 defaultDeviceObjectIDSize = sizeof(AudioObjectID); - AudioObjectID defaultDeviceObjectID; - OSStatus status; - - MA_ASSERT(pContext != NULL); - MA_ASSERT(pDeviceObjectID != NULL); - - /* Safety. */ - *pDeviceObjectID = 0; - - propAddressDefaultDevice.mScope = kAudioObjectPropertyScopeGlobal; - propAddressDefaultDevice.mElement = AUDIO_OBJECT_PROPERTY_ELEMENT; - if (deviceType == ma_device_type_playback) { - propAddressDefaultDevice.mSelector = kAudioHardwarePropertyDefaultOutputDevice; - } else { - propAddressDefaultDevice.mSelector = kAudioHardwarePropertyDefaultInputDevice; - } - - defaultDeviceObjectIDSize = sizeof(AudioObjectID); - status = ((ma_AudioObjectGetPropertyData_proc)pContext->coreaudio.AudioObjectGetPropertyData)(kAudioObjectSystemObject, &propAddressDefaultDevice, 0, NULL, &defaultDeviceObjectIDSize, &defaultDeviceObjectID); - if (status == noErr) { - *pDeviceObjectID = defaultDeviceObjectID; - return MA_SUCCESS; - } - - /* If we get here it means we couldn't find the device. */ - return MA_NO_DEVICE; -} - -static ma_result ma_find_AudioObjectID(ma_context* pContext, ma_device_type deviceType, const ma_device_id* pDeviceID, AudioObjectID* pDeviceObjectID) -{ - MA_ASSERT(pContext != NULL); - MA_ASSERT(pDeviceObjectID != NULL); - - /* Safety. */ - *pDeviceObjectID = 0; - - if (pDeviceID == NULL) { - /* Default device. */ - return ma_find_default_AudioObjectID(pContext, deviceType, pDeviceObjectID); - } else { - /* Explicit device. */ - UInt32 deviceCount; - AudioObjectID* pDeviceObjectIDs; - ma_result result; - UInt32 iDevice; - - result = ma_get_device_object_ids__coreaudio(pContext, &deviceCount, &pDeviceObjectIDs); - if (result != MA_SUCCESS) { - return result; - } - - for (iDevice = 0; iDevice < deviceCount; ++iDevice) { - AudioObjectID deviceObjectID = pDeviceObjectIDs[iDevice]; - - char uid[256]; - if (ma_get_AudioObject_uid(pContext, deviceObjectID, sizeof(uid), uid) != MA_SUCCESS) { - continue; - } - - if (deviceType == ma_device_type_playback) { - if (ma_does_AudioObject_support_playback(pContext, deviceObjectID)) { - if (strcmp(uid, pDeviceID->coreaudio) == 0) { - *pDeviceObjectID = deviceObjectID; - ma_free(pDeviceObjectIDs, &pContext->allocationCallbacks); - return MA_SUCCESS; - } - } - } else { - if (ma_does_AudioObject_support_capture(pContext, deviceObjectID)) { - if (strcmp(uid, pDeviceID->coreaudio) == 0) { - *pDeviceObjectID = deviceObjectID; - ma_free(pDeviceObjectIDs, &pContext->allocationCallbacks); - return MA_SUCCESS; - } - } - } - } - - ma_free(pDeviceObjectIDs, &pContext->allocationCallbacks); - } - - /* If we get here it means we couldn't find the device. */ - return MA_NO_DEVICE; -} - - -static ma_result ma_find_best_format__coreaudio(ma_context* pContext, AudioObjectID deviceObjectID, ma_device_type deviceType, ma_format format, ma_uint32 channels, ma_uint32 sampleRate, const AudioStreamBasicDescription* pOrigFormat, AudioStreamBasicDescription* pFormat) -{ - UInt32 deviceFormatDescriptionCount; - AudioStreamRangedDescription* pDeviceFormatDescriptions; - ma_result result; - ma_uint32 desiredSampleRate; - ma_uint32 desiredChannelCount; - ma_format desiredFormat; - AudioStreamBasicDescription bestDeviceFormatSoFar; - ma_bool32 hasSupportedFormat; - UInt32 iFormat; - - result = ma_get_AudioObject_stream_descriptions(pContext, deviceObjectID, deviceType, &deviceFormatDescriptionCount, &pDeviceFormatDescriptions); - if (result != MA_SUCCESS) { - return result; - } - - desiredSampleRate = sampleRate; - if (desiredSampleRate == 0) { - desiredSampleRate = (ma_uint32)pOrigFormat->mSampleRate; - } - - desiredChannelCount = channels; - if (desiredChannelCount == 0) { - desiredChannelCount = pOrigFormat->mChannelsPerFrame; - } - - desiredFormat = format; - if (desiredFormat == ma_format_unknown) { - result = ma_format_from_AudioStreamBasicDescription(pOrigFormat, &desiredFormat); - if (result != MA_SUCCESS || desiredFormat == ma_format_unknown) { - desiredFormat = g_maFormatPriorities[0]; - } - } - - /* - If we get here it means we don't have an exact match to what the client is asking for. We'll need to find the closest one. The next - loop will check for formats that have the same sample rate to what we're asking for. If there is, we prefer that one in all cases. - */ - MA_ZERO_OBJECT(&bestDeviceFormatSoFar); - - hasSupportedFormat = MA_FALSE; - for (iFormat = 0; iFormat < deviceFormatDescriptionCount; ++iFormat) { - ma_format formatFromDescription; - ma_result formatResult = ma_format_from_AudioStreamBasicDescription(&pDeviceFormatDescriptions[iFormat].mFormat, &formatFromDescription); - if (formatResult == MA_SUCCESS && formatFromDescription != ma_format_unknown) { - hasSupportedFormat = MA_TRUE; - bestDeviceFormatSoFar = pDeviceFormatDescriptions[iFormat].mFormat; - break; - } - } - - if (!hasSupportedFormat) { - ma_free(pDeviceFormatDescriptions, &pContext->allocationCallbacks); - return MA_FORMAT_NOT_SUPPORTED; - } - - - for (iFormat = 0; iFormat < deviceFormatDescriptionCount; ++iFormat) { - AudioStreamBasicDescription thisDeviceFormat = pDeviceFormatDescriptions[iFormat].mFormat; - ma_format thisSampleFormat; - ma_result formatResult; - ma_format bestSampleFormatSoFar; - - /* If the format is not supported by miniaudio we need to skip this one entirely. */ - formatResult = ma_format_from_AudioStreamBasicDescription(&pDeviceFormatDescriptions[iFormat].mFormat, &thisSampleFormat); - if (formatResult != MA_SUCCESS || thisSampleFormat == ma_format_unknown) { - continue; /* The format is not supported by miniaudio. Skip. */ - } - - ma_format_from_AudioStreamBasicDescription(&bestDeviceFormatSoFar, &bestSampleFormatSoFar); - - /* Getting here means the format is supported by miniaudio which makes this format a candidate. */ - if (thisDeviceFormat.mSampleRate != desiredSampleRate) { - /* - The sample rate does not match, but this format could still be usable, although it's a very low priority. If the best format - so far has an equal sample rate we can just ignore this one. - */ - if (bestDeviceFormatSoFar.mSampleRate == desiredSampleRate) { - continue; /* The best sample rate so far has the same sample rate as what we requested which means it's still the best so far. Skip this format. */ - } else { - /* In this case, neither the best format so far nor this one have the same sample rate. Check the channel count next. */ - if (thisDeviceFormat.mChannelsPerFrame != desiredChannelCount) { - /* This format has a different sample rate _and_ a different channel count. */ - if (bestDeviceFormatSoFar.mChannelsPerFrame == desiredChannelCount) { - continue; /* No change to the best format. */ - } else { - /* - Both this format and the best so far have different sample rates and different channel counts. Whichever has the - best format is the new best. - */ - if (ma_get_format_priority_index(thisSampleFormat) < ma_get_format_priority_index(bestSampleFormatSoFar)) { - bestDeviceFormatSoFar = thisDeviceFormat; - continue; - } else { - continue; /* No change to the best format. */ - } - } - } else { - /* This format has a different sample rate but the desired channel count. */ - if (bestDeviceFormatSoFar.mChannelsPerFrame == desiredChannelCount) { - /* Both this format and the best so far have the desired channel count. Whichever has the best format is the new best. */ - if (ma_get_format_priority_index(thisSampleFormat) < ma_get_format_priority_index(bestSampleFormatSoFar)) { - bestDeviceFormatSoFar = thisDeviceFormat; - continue; - } else { - continue; /* No change to the best format for now. */ - } - } else { - /* This format has the desired channel count, but the best so far does not. We have a new best. */ - bestDeviceFormatSoFar = thisDeviceFormat; - continue; - } - } - } - } else { - /* - The sample rates match which makes this format a very high priority contender. If the best format so far has a different - sample rate it needs to be replaced with this one. - */ - if (bestDeviceFormatSoFar.mSampleRate != desiredSampleRate) { - bestDeviceFormatSoFar = thisDeviceFormat; - continue; - } else { - /* In this case both this format and the best format so far have the same sample rate. Check the channel count next. */ - if (thisDeviceFormat.mChannelsPerFrame == desiredChannelCount) { - /* - In this case this format has the same channel count as what the client is requesting. If the best format so far has - a different count, this one becomes the new best. - */ - if (bestDeviceFormatSoFar.mChannelsPerFrame != desiredChannelCount) { - bestDeviceFormatSoFar = thisDeviceFormat; - continue; - } else { - /* In this case both this format and the best so far have the ideal sample rate and channel count. Check the format. */ - if (thisSampleFormat == desiredFormat) { - bestDeviceFormatSoFar = thisDeviceFormat; - break; /* Found the exact match. */ - } else { - /* The formats are different. The new best format is the one with the highest priority format according to miniaudio. */ - if (ma_get_format_priority_index(thisSampleFormat) < ma_get_format_priority_index(bestSampleFormatSoFar)) { - bestDeviceFormatSoFar = thisDeviceFormat; - continue; - } else { - continue; /* No change to the best format for now. */ - } - } - } - } else { - /* - In this case the channel count is different to what the client has requested. If the best so far has the same channel - count as the requested count then it remains the best. - */ - if (bestDeviceFormatSoFar.mChannelsPerFrame == desiredChannelCount) { - continue; - } else { - /* - This is the case where both have the same sample rate (good) but different channel counts. Right now both have about - the same priority, but we need to compare the format now. - */ - if (thisSampleFormat == bestSampleFormatSoFar) { - if (ma_get_format_priority_index(thisSampleFormat) < ma_get_format_priority_index(bestSampleFormatSoFar)) { - bestDeviceFormatSoFar = thisDeviceFormat; - continue; - } else { - continue; /* No change to the best format for now. */ - } - } - } - } - } - } - } - - *pFormat = bestDeviceFormatSoFar; - - ma_free(pDeviceFormatDescriptions, &pContext->allocationCallbacks); - return MA_SUCCESS; -} - -static ma_result ma_get_AudioUnit_channel_map(ma_context* pContext, AudioUnit audioUnit, ma_device_type deviceType, ma_channel* pChannelMap, size_t channelMapCap) -{ - AudioUnitScope deviceScope; - AudioUnitElement deviceBus; - UInt32 channelLayoutSize; - OSStatus status; - AudioChannelLayout* pChannelLayout; - ma_result result; - - MA_ASSERT(pContext != NULL); - - if (deviceType == ma_device_type_playback) { - deviceScope = kAudioUnitScope_Input; - deviceBus = MA_COREAUDIO_OUTPUT_BUS; - } else { - deviceScope = kAudioUnitScope_Output; - deviceBus = MA_COREAUDIO_INPUT_BUS; - } - - status = ((ma_AudioUnitGetPropertyInfo_proc)pContext->coreaudio.AudioUnitGetPropertyInfo)(audioUnit, kAudioUnitProperty_AudioChannelLayout, deviceScope, deviceBus, &channelLayoutSize, NULL); - if (status != noErr) { - return ma_result_from_OSStatus(status); - } - - pChannelLayout = (AudioChannelLayout*)ma_malloc(channelLayoutSize, &pContext->allocationCallbacks); - if (pChannelLayout == NULL) { - return MA_OUT_OF_MEMORY; - } - - status = ((ma_AudioUnitGetProperty_proc)pContext->coreaudio.AudioUnitGetProperty)(audioUnit, kAudioUnitProperty_AudioChannelLayout, deviceScope, deviceBus, pChannelLayout, &channelLayoutSize); - if (status != noErr) { - ma_free(pChannelLayout, &pContext->allocationCallbacks); - return ma_result_from_OSStatus(status); - } - - result = ma_get_channel_map_from_AudioChannelLayout(pChannelLayout, pChannelMap, channelMapCap); - if (result != MA_SUCCESS) { - ma_free(pChannelLayout, &pContext->allocationCallbacks); - return result; - } - - ma_free(pChannelLayout, &pContext->allocationCallbacks); - return MA_SUCCESS; -} -#endif /* MA_APPLE_DESKTOP */ - - -#if !defined(MA_APPLE_DESKTOP) -static void ma_AVAudioSessionPortDescription_to_device_info(AVAudioSessionPortDescription* pPortDesc, ma_device_info* pInfo) -{ - MA_ZERO_OBJECT(pInfo); - ma_strncpy_s(pInfo->name, sizeof(pInfo->name), [pPortDesc.portName UTF8String], (size_t)-1); - ma_strncpy_s(pInfo->id.coreaudio, sizeof(pInfo->id.coreaudio), [pPortDesc.UID UTF8String], (size_t)-1); -} -#endif - -static ma_result ma_context_enumerate_devices__coreaudio(ma_context* pContext, ma_enum_devices_callback_proc callback, void* pUserData) -{ -#if defined(MA_APPLE_DESKTOP) - UInt32 deviceCount; - AudioObjectID* pDeviceObjectIDs; - AudioObjectID defaultDeviceObjectIDPlayback; - AudioObjectID defaultDeviceObjectIDCapture; - ma_result result; - UInt32 iDevice; - - ma_find_default_AudioObjectID(pContext, ma_device_type_playback, &defaultDeviceObjectIDPlayback); /* OK if this fails. */ - ma_find_default_AudioObjectID(pContext, ma_device_type_capture, &defaultDeviceObjectIDCapture); /* OK if this fails. */ - - result = ma_get_device_object_ids__coreaudio(pContext, &deviceCount, &pDeviceObjectIDs); - if (result != MA_SUCCESS) { - return result; - } - - for (iDevice = 0; iDevice < deviceCount; ++iDevice) { - AudioObjectID deviceObjectID = pDeviceObjectIDs[iDevice]; - ma_device_info info; - - MA_ZERO_OBJECT(&info); - if (ma_get_AudioObject_uid(pContext, deviceObjectID, sizeof(info.id.coreaudio), info.id.coreaudio) != MA_SUCCESS) { - continue; - } - if (ma_get_AudioObject_name(pContext, deviceObjectID, sizeof(info.name), info.name) != MA_SUCCESS) { - continue; - } - - if (ma_does_AudioObject_support_playback(pContext, deviceObjectID)) { - if (deviceObjectID == defaultDeviceObjectIDPlayback) { - info.isDefault = MA_TRUE; - } - - if (!callback(pContext, ma_device_type_playback, &info, pUserData)) { - break; - } - } - if (ma_does_AudioObject_support_capture(pContext, deviceObjectID)) { - if (deviceObjectID == defaultDeviceObjectIDCapture) { - info.isDefault = MA_TRUE; - } - - if (!callback(pContext, ma_device_type_capture, &info, pUserData)) { - break; - } - } - } - - ma_free(pDeviceObjectIDs, &pContext->allocationCallbacks); -#else - ma_device_info info; - NSArray *pInputs = [[[AVAudioSession sharedInstance] currentRoute] inputs]; - NSArray *pOutputs = [[[AVAudioSession sharedInstance] currentRoute] outputs]; - - for (AVAudioSessionPortDescription* pPortDesc in pOutputs) { - ma_AVAudioSessionPortDescription_to_device_info(pPortDesc, &info); - if (!callback(pContext, ma_device_type_playback, &info, pUserData)) { - return MA_SUCCESS; - } - } - - for (AVAudioSessionPortDescription* pPortDesc in pInputs) { - ma_AVAudioSessionPortDescription_to_device_info(pPortDesc, &info); - if (!callback(pContext, ma_device_type_capture, &info, pUserData)) { - return MA_SUCCESS; - } - } -#endif - - return MA_SUCCESS; -} - -static ma_result ma_context_get_device_info__coreaudio(ma_context* pContext, ma_device_type deviceType, const ma_device_id* pDeviceID, ma_device_info* pDeviceInfo) -{ - ma_result result; - - MA_ASSERT(pContext != NULL); - -#if defined(MA_APPLE_DESKTOP) - /* Desktop */ - { - AudioObjectID deviceObjectID; - AudioObjectID defaultDeviceObjectID; - UInt32 streamDescriptionCount; - AudioStreamRangedDescription* pStreamDescriptions; - UInt32 iStreamDescription; - UInt32 sampleRateRangeCount; - AudioValueRange* pSampleRateRanges; - - ma_find_default_AudioObjectID(pContext, deviceType, &defaultDeviceObjectID); /* OK if this fails. */ - - result = ma_find_AudioObjectID(pContext, deviceType, pDeviceID, &deviceObjectID); - if (result != MA_SUCCESS) { - return result; - } - - result = ma_get_AudioObject_uid(pContext, deviceObjectID, sizeof(pDeviceInfo->id.coreaudio), pDeviceInfo->id.coreaudio); - if (result != MA_SUCCESS) { - return result; - } - - result = ma_get_AudioObject_name(pContext, deviceObjectID, sizeof(pDeviceInfo->name), pDeviceInfo->name); - if (result != MA_SUCCESS) { - return result; - } - - if (deviceObjectID == defaultDeviceObjectID) { - pDeviceInfo->isDefault = MA_TRUE; - } - - /* - There could be a large number of permutations here. Fortunately there is only a single channel count - being reported which reduces this quite a bit. For sample rates we're only reporting those that are - one of miniaudio's recognized "standard" rates. If there are still more formats than can fit into - our fixed sized array we'll just need to truncate them. This is unlikely and will probably only happen - if some driver performs software data conversion and therefore reports every possible format and - sample rate. - */ - pDeviceInfo->nativeDataFormatCount = 0; - - /* Formats. */ - { - ma_format uniqueFormats[ma_format_count]; - ma_uint32 uniqueFormatCount = 0; - ma_uint32 channels; - - /* Channels. */ - result = ma_get_AudioObject_channel_count(pContext, deviceObjectID, deviceType, &channels); - if (result != MA_SUCCESS) { - return result; - } - - /* Formats. */ - result = ma_get_AudioObject_stream_descriptions(pContext, deviceObjectID, deviceType, &streamDescriptionCount, &pStreamDescriptions); - if (result != MA_SUCCESS) { - return result; - } - - for (iStreamDescription = 0; iStreamDescription < streamDescriptionCount; ++iStreamDescription) { - ma_format format; - ma_bool32 hasFormatBeenHandled = MA_FALSE; - ma_uint32 iOutputFormat; - ma_uint32 iSampleRate; - - result = ma_format_from_AudioStreamBasicDescription(&pStreamDescriptions[iStreamDescription].mFormat, &format); - if (result != MA_SUCCESS) { - continue; - } - - MA_ASSERT(format != ma_format_unknown); - - /* Make sure the format isn't already in the output list. */ - for (iOutputFormat = 0; iOutputFormat < uniqueFormatCount; ++iOutputFormat) { - if (uniqueFormats[iOutputFormat] == format) { - hasFormatBeenHandled = MA_TRUE; - break; - } - } - - /* If we've already handled this format just skip it. */ - if (hasFormatBeenHandled) { - continue; - } - - uniqueFormats[uniqueFormatCount] = format; - uniqueFormatCount += 1; - - /* Sample Rates */ - result = ma_get_AudioObject_sample_rates(pContext, deviceObjectID, deviceType, &sampleRateRangeCount, &pSampleRateRanges); - if (result != MA_SUCCESS) { - return result; - } - - /* - Annoyingly Core Audio reports a sample rate range. We just get all the standard rates that are - between this range. - */ - for (iSampleRate = 0; iSampleRate < sampleRateRangeCount; ++iSampleRate) { - ma_uint32 iStandardSampleRate; - for (iStandardSampleRate = 0; iStandardSampleRate < ma_countof(g_maStandardSampleRatePriorities); iStandardSampleRate += 1) { - ma_uint32 standardSampleRate = g_maStandardSampleRatePriorities[iStandardSampleRate]; - if (standardSampleRate >= pSampleRateRanges[iSampleRate].mMinimum && standardSampleRate <= pSampleRateRanges[iSampleRate].mMaximum) { - /* We have a new data format. Add it to the list. */ - pDeviceInfo->nativeDataFormats[pDeviceInfo->nativeDataFormatCount].format = format; - pDeviceInfo->nativeDataFormats[pDeviceInfo->nativeDataFormatCount].channels = channels; - pDeviceInfo->nativeDataFormats[pDeviceInfo->nativeDataFormatCount].sampleRate = standardSampleRate; - pDeviceInfo->nativeDataFormats[pDeviceInfo->nativeDataFormatCount].flags = 0; - pDeviceInfo->nativeDataFormatCount += 1; - - if (pDeviceInfo->nativeDataFormatCount >= ma_countof(pDeviceInfo->nativeDataFormats)) { - break; /* No more room for any more formats. */ - } - } - } - } - - ma_free(pSampleRateRanges, &pContext->allocationCallbacks); - - if (pDeviceInfo->nativeDataFormatCount >= ma_countof(pDeviceInfo->nativeDataFormats)) { - break; /* No more room for any more formats. */ - } - } - - ma_free(pStreamDescriptions, &pContext->allocationCallbacks); - } - } -#else - /* Mobile */ - { - AudioComponentDescription desc; - AudioComponent component; - AudioUnit audioUnit; - OSStatus status; - AudioUnitScope formatScope; - AudioUnitElement formatElement; - AudioStreamBasicDescription bestFormat; - UInt32 propSize; - - /* We want to ensure we use a consistent device name to device enumeration. */ - if (pDeviceID != NULL && pDeviceID->coreaudio[0] != '\0') { - ma_bool32 found = MA_FALSE; - if (deviceType == ma_device_type_playback) { - NSArray *pOutputs = [[[AVAudioSession sharedInstance] currentRoute] outputs]; - for (AVAudioSessionPortDescription* pPortDesc in pOutputs) { - if (strcmp(pDeviceID->coreaudio, [pPortDesc.UID UTF8String]) == 0) { - ma_AVAudioSessionPortDescription_to_device_info(pPortDesc, pDeviceInfo); - found = MA_TRUE; - break; - } - } - } else { - NSArray *pInputs = [[[AVAudioSession sharedInstance] currentRoute] inputs]; - for (AVAudioSessionPortDescription* pPortDesc in pInputs) { - if (strcmp(pDeviceID->coreaudio, [pPortDesc.UID UTF8String]) == 0) { - ma_AVAudioSessionPortDescription_to_device_info(pPortDesc, pDeviceInfo); - found = MA_TRUE; - break; - } - } - } - - if (!found) { - return MA_DOES_NOT_EXIST; - } - } else { - if (deviceType == ma_device_type_playback) { - ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), MA_DEFAULT_PLAYBACK_DEVICE_NAME, (size_t)-1); - } else { - ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), MA_DEFAULT_CAPTURE_DEVICE_NAME, (size_t)-1); - } - } - - - /* - Retrieving device information is more annoying on mobile than desktop. For simplicity I'm locking this down to whatever format is - reported on a temporary I/O unit. The problem, however, is that this doesn't return a value for the sample rate which we need to - retrieve from the AVAudioSession shared instance. - */ - desc.componentType = kAudioUnitType_Output; - desc.componentSubType = kAudioUnitSubType_RemoteIO; - desc.componentManufacturer = kAudioUnitManufacturer_Apple; - desc.componentFlags = 0; - desc.componentFlagsMask = 0; - - component = ((ma_AudioComponentFindNext_proc)pContext->coreaudio.AudioComponentFindNext)(NULL, &desc); - if (component == NULL) { - return MA_FAILED_TO_INIT_BACKEND; - } - - status = ((ma_AudioComponentInstanceNew_proc)pContext->coreaudio.AudioComponentInstanceNew)(component, &audioUnit); - if (status != noErr) { - return ma_result_from_OSStatus(status); - } - - formatScope = (deviceType == ma_device_type_playback) ? kAudioUnitScope_Input : kAudioUnitScope_Output; - formatElement = (deviceType == ma_device_type_playback) ? MA_COREAUDIO_OUTPUT_BUS : MA_COREAUDIO_INPUT_BUS; - - propSize = sizeof(bestFormat); - status = ((ma_AudioUnitGetProperty_proc)pContext->coreaudio.AudioUnitGetProperty)(audioUnit, kAudioUnitProperty_StreamFormat, formatScope, formatElement, &bestFormat, &propSize); - if (status != noErr) { - ((ma_AudioComponentInstanceDispose_proc)pContext->coreaudio.AudioComponentInstanceDispose)(audioUnit); - return ma_result_from_OSStatus(status); - } - - ((ma_AudioComponentInstanceDispose_proc)pContext->coreaudio.AudioComponentInstanceDispose)(audioUnit); - audioUnit = NULL; - - /* Only a single format is being reported for iOS. */ - pDeviceInfo->nativeDataFormatCount = 1; - - result = ma_format_from_AudioStreamBasicDescription(&bestFormat, &pDeviceInfo->nativeDataFormats[0].format); - if (result != MA_SUCCESS) { - return result; - } - - pDeviceInfo->nativeDataFormats[0].channels = bestFormat.mChannelsPerFrame; - - /* - It looks like Apple are wanting to push the whole AVAudioSession thing. Thus, we need to use that to determine device settings. To do - this we just get the shared instance and inspect. - */ - @autoreleasepool { - AVAudioSession* pAudioSession = [AVAudioSession sharedInstance]; - MA_ASSERT(pAudioSession != NULL); - - pDeviceInfo->nativeDataFormats[0].sampleRate = (ma_uint32)pAudioSession.sampleRate; - } - } -#endif - - (void)pDeviceInfo; /* Unused. */ - return MA_SUCCESS; -} - -static AudioBufferList* ma_allocate_AudioBufferList__coreaudio(ma_uint32 sizeInFrames, ma_format format, ma_uint32 channels, ma_stream_layout layout, const ma_allocation_callbacks* pAllocationCallbacks) -{ - AudioBufferList* pBufferList; - UInt32 audioBufferSizeInBytes; - size_t allocationSize; - - MA_ASSERT(sizeInFrames > 0); - MA_ASSERT(format != ma_format_unknown); - MA_ASSERT(channels > 0); - - allocationSize = sizeof(AudioBufferList) - sizeof(AudioBuffer); /* Subtract sizeof(AudioBuffer) because that part is dynamically sized. */ - if (layout == ma_stream_layout_interleaved) { - /* Interleaved case. This is the simple case because we just have one buffer. */ - allocationSize += sizeof(AudioBuffer) * 1; - } else { - /* Non-interleaved case. This is the more complex case because there's more than one buffer. */ - allocationSize += sizeof(AudioBuffer) * channels; - } - - allocationSize += sizeInFrames * ma_get_bytes_per_frame(format, channels); - - pBufferList = (AudioBufferList*)ma_malloc(allocationSize, pAllocationCallbacks); - if (pBufferList == NULL) { - return NULL; - } - - audioBufferSizeInBytes = (UInt32)(sizeInFrames * ma_get_bytes_per_sample(format)); - - if (layout == ma_stream_layout_interleaved) { - pBufferList->mNumberBuffers = 1; - pBufferList->mBuffers[0].mNumberChannels = channels; - pBufferList->mBuffers[0].mDataByteSize = audioBufferSizeInBytes * channels; - pBufferList->mBuffers[0].mData = (ma_uint8*)pBufferList + sizeof(AudioBufferList); - } else { - ma_uint32 iBuffer; - pBufferList->mNumberBuffers = channels; - for (iBuffer = 0; iBuffer < pBufferList->mNumberBuffers; ++iBuffer) { - pBufferList->mBuffers[iBuffer].mNumberChannels = 1; - pBufferList->mBuffers[iBuffer].mDataByteSize = audioBufferSizeInBytes; - pBufferList->mBuffers[iBuffer].mData = (ma_uint8*)pBufferList + ((sizeof(AudioBufferList) - sizeof(AudioBuffer)) + (sizeof(AudioBuffer) * channels)) + (audioBufferSizeInBytes * iBuffer); - } - } - - return pBufferList; -} - -static ma_result ma_device_realloc_AudioBufferList__coreaudio(ma_device* pDevice, ma_uint32 sizeInFrames, ma_format format, ma_uint32 channels, ma_stream_layout layout) -{ - MA_ASSERT(pDevice != NULL); - MA_ASSERT(format != ma_format_unknown); - MA_ASSERT(channels > 0); - - /* Only resize the buffer if necessary. */ - if (pDevice->coreaudio.audioBufferCapInFrames < sizeInFrames) { - AudioBufferList* pNewAudioBufferList; - - pNewAudioBufferList = ma_allocate_AudioBufferList__coreaudio(sizeInFrames, format, channels, layout, &pDevice->pContext->allocationCallbacks); - if (pNewAudioBufferList == NULL) { - return MA_OUT_OF_MEMORY; - } - - /* At this point we'll have a new AudioBufferList and we can free the old one. */ - ma_free(pDevice->coreaudio.pAudioBufferList, &pDevice->pContext->allocationCallbacks); - pDevice->coreaudio.pAudioBufferList = pNewAudioBufferList; - pDevice->coreaudio.audioBufferCapInFrames = sizeInFrames; - } - - /* Getting here means the capacity of the audio is fine. */ - return MA_SUCCESS; -} - - -static OSStatus ma_on_output__coreaudio(void* pUserData, AudioUnitRenderActionFlags* pActionFlags, const AudioTimeStamp* pTimeStamp, UInt32 busNumber, UInt32 frameCount, AudioBufferList* pBufferList) -{ - ma_device* pDevice = (ma_device*)pUserData; - ma_stream_layout layout; - - MA_ASSERT(pDevice != NULL); - - /*ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "INFO: Output Callback: busNumber=%d, frameCount=%d, mNumberBuffers=%d\n", (int)busNumber, (int)frameCount, (int)pBufferList->mNumberBuffers);*/ - - /* We need to check whether or not we are outputting interleaved or non-interleaved samples. The way we do this is slightly different for each type. */ - layout = ma_stream_layout_interleaved; - if (pBufferList->mBuffers[0].mNumberChannels != pDevice->playback.internalChannels) { - layout = ma_stream_layout_deinterleaved; - } - - if (layout == ma_stream_layout_interleaved) { - /* For now we can assume everything is interleaved. */ - UInt32 iBuffer; - for (iBuffer = 0; iBuffer < pBufferList->mNumberBuffers; ++iBuffer) { - if (pBufferList->mBuffers[iBuffer].mNumberChannels == pDevice->playback.internalChannels) { - ma_uint32 frameCountForThisBuffer = pBufferList->mBuffers[iBuffer].mDataByteSize / ma_get_bytes_per_frame(pDevice->playback.internalFormat, pDevice->playback.internalChannels); - if (frameCountForThisBuffer > 0) { - ma_device_handle_backend_data_callback(pDevice, pBufferList->mBuffers[iBuffer].mData, NULL, frameCountForThisBuffer); - } - - /*a_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, " frameCount=%d, mNumberChannels=%d, mDataByteSize=%d\n", (int)frameCount, (int)pBufferList->mBuffers[iBuffer].mNumberChannels, (int)pBufferList->mBuffers[iBuffer].mDataByteSize);*/ - } else { - /* - This case is where the number of channels in the output buffer do not match our internal channels. It could mean that it's - not interleaved, in which case we can't handle right now since miniaudio does not yet support non-interleaved streams. We just - output silence here. - */ - MA_ZERO_MEMORY(pBufferList->mBuffers[iBuffer].mData, pBufferList->mBuffers[iBuffer].mDataByteSize); - /*ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, " WARNING: Outputting silence. frameCount=%d, mNumberChannels=%d, mDataByteSize=%d\n", (int)frameCount, (int)pBufferList->mBuffers[iBuffer].mNumberChannels, (int)pBufferList->mBuffers[iBuffer].mDataByteSize);*/ - } - } - } else { - /* This is the deinterleaved case. We need to update each buffer in groups of internalChannels. This assumes each buffer is the same size. */ - MA_ASSERT(pDevice->playback.internalChannels <= MA_MAX_CHANNELS); /* This should have been validated at initialization time. */ - - /* - For safety we'll check that the internal channels is a multiple of the buffer count. If it's not it means something - very strange has happened and we're not going to support it. - */ - if ((pBufferList->mNumberBuffers % pDevice->playback.internalChannels) == 0) { - ma_uint8 tempBuffer[4096]; - UInt32 iBuffer; - - for (iBuffer = 0; iBuffer < pBufferList->mNumberBuffers; iBuffer += pDevice->playback.internalChannels) { - ma_uint32 frameCountPerBuffer = pBufferList->mBuffers[iBuffer].mDataByteSize / ma_get_bytes_per_sample(pDevice->playback.internalFormat); - ma_uint32 framesRemaining = frameCountPerBuffer; - - while (framesRemaining > 0) { - void* ppDeinterleavedBuffers[MA_MAX_CHANNELS]; - ma_uint32 iChannel; - ma_uint32 framesToRead = sizeof(tempBuffer) / ma_get_bytes_per_frame(pDevice->playback.internalFormat, pDevice->playback.internalChannels); - if (framesToRead > framesRemaining) { - framesToRead = framesRemaining; - } - - ma_device_handle_backend_data_callback(pDevice, tempBuffer, NULL, framesToRead); - - for (iChannel = 0; iChannel < pDevice->playback.internalChannels; ++iChannel) { - ppDeinterleavedBuffers[iChannel] = (void*)ma_offset_ptr(pBufferList->mBuffers[iBuffer+iChannel].mData, (frameCountPerBuffer - framesRemaining) * ma_get_bytes_per_sample(pDevice->playback.internalFormat)); - } - - ma_deinterleave_pcm_frames(pDevice->playback.internalFormat, pDevice->playback.internalChannels, framesToRead, tempBuffer, ppDeinterleavedBuffers); - - framesRemaining -= framesToRead; - } - } - } - } - - (void)pActionFlags; - (void)pTimeStamp; - (void)busNumber; - (void)frameCount; - - return noErr; -} - -static OSStatus ma_on_input__coreaudio(void* pUserData, AudioUnitRenderActionFlags* pActionFlags, const AudioTimeStamp* pTimeStamp, UInt32 busNumber, UInt32 frameCount, AudioBufferList* pUnusedBufferList) -{ - ma_device* pDevice = (ma_device*)pUserData; - AudioBufferList* pRenderedBufferList; - ma_result result; - ma_stream_layout layout; - ma_uint32 iBuffer; - OSStatus status; - - MA_ASSERT(pDevice != NULL); - - pRenderedBufferList = (AudioBufferList*)pDevice->coreaudio.pAudioBufferList; - MA_ASSERT(pRenderedBufferList); - - /* We need to check whether or not we are outputting interleaved or non-interleaved samples. The way we do this is slightly different for each type. */ - layout = ma_stream_layout_interleaved; - if (pRenderedBufferList->mBuffers[0].mNumberChannels != pDevice->capture.internalChannels) { - layout = ma_stream_layout_deinterleaved; - } - - /*ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "INFO: Input Callback: busNumber=%d, frameCount=%d, mNumberBuffers=%d\n", (int)busNumber, (int)frameCount, (int)pRenderedBufferList->mNumberBuffers);*/ - - /* - There has been a situation reported where frame count passed into this function is greater than the capacity of - our capture buffer. There doesn't seem to be a reliable way to determine what the maximum frame count will be, - so we need to instead resort to dynamically reallocating our buffer to ensure it's large enough to capture the - number of frames requested by this callback. - */ - result = ma_device_realloc_AudioBufferList__coreaudio(pDevice, frameCount, pDevice->capture.internalFormat, pDevice->capture.internalChannels, layout); - if (result != MA_SUCCESS) { - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "Failed to allocate AudioBufferList for capture.\n"); - return noErr; - } - - pRenderedBufferList = (AudioBufferList*)pDevice->coreaudio.pAudioBufferList; - MA_ASSERT(pRenderedBufferList); - - /* - When you call AudioUnitRender(), Core Audio tries to be helpful by setting the mDataByteSize to the number of bytes - that were actually rendered. The problem with this is that the next call can fail with -50 due to the size no longer - being set to the capacity of the buffer, but instead the size in bytes of the previous render. This will cause a - problem when a future call to this callback specifies a larger number of frames. - - To work around this we need to explicitly set the size of each buffer to their respective size in bytes. - */ - for (iBuffer = 0; iBuffer < pRenderedBufferList->mNumberBuffers; ++iBuffer) { - pRenderedBufferList->mBuffers[iBuffer].mDataByteSize = pDevice->coreaudio.audioBufferCapInFrames * ma_get_bytes_per_sample(pDevice->capture.internalFormat) * pRenderedBufferList->mBuffers[iBuffer].mNumberChannels; - /*printf("DEBUG: nDataByteSize = %d\n", (int)pRenderedBufferList->mBuffers[iBuffer].mDataByteSize);*/ - } - - status = ((ma_AudioUnitRender_proc)pDevice->pContext->coreaudio.AudioUnitRender)((AudioUnit)pDevice->coreaudio.audioUnitCapture, pActionFlags, pTimeStamp, busNumber, frameCount, pRenderedBufferList); - if (status != noErr) { - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "ERROR: AudioUnitRender() failed with %d.\n", (int)status); - return status; - } - - if (layout == ma_stream_layout_interleaved) { - for (iBuffer = 0; iBuffer < pRenderedBufferList->mNumberBuffers; ++iBuffer) { - if (pRenderedBufferList->mBuffers[iBuffer].mNumberChannels == pDevice->capture.internalChannels) { - ma_device_handle_backend_data_callback(pDevice, NULL, pRenderedBufferList->mBuffers[iBuffer].mData, frameCount); - /*ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, " mDataByteSize=%d.\n", (int)pRenderedBufferList->mBuffers[iBuffer].mDataByteSize);*/ - } else { - /* - This case is where the number of channels in the output buffer do not match our internal channels. It could mean that it's - not interleaved, in which case we can't handle right now since miniaudio does not yet support non-interleaved streams. - */ - ma_uint8 silentBuffer[4096]; - ma_uint32 framesRemaining; - - MA_ZERO_MEMORY(silentBuffer, sizeof(silentBuffer)); - - framesRemaining = frameCount; - while (framesRemaining > 0) { - ma_uint32 framesToSend = sizeof(silentBuffer) / ma_get_bytes_per_frame(pDevice->capture.internalFormat, pDevice->capture.internalChannels); - if (framesToSend > framesRemaining) { - framesToSend = framesRemaining; - } - - ma_device_handle_backend_data_callback(pDevice, NULL, silentBuffer, framesToSend); - - framesRemaining -= framesToSend; - } - - /*ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, " WARNING: Outputting silence. frameCount=%d, mNumberChannels=%d, mDataByteSize=%d\n", (int)frameCount, (int)pRenderedBufferList->mBuffers[iBuffer].mNumberChannels, (int)pRenderedBufferList->mBuffers[iBuffer].mDataByteSize);*/ - } - } - } else { - /* This is the deinterleaved case. We need to interleave the audio data before sending it to the client. This assumes each buffer is the same size. */ - MA_ASSERT(pDevice->capture.internalChannels <= MA_MAX_CHANNELS); /* This should have been validated at initialization time. */ - - /* - For safety we'll check that the internal channels is a multiple of the buffer count. If it's not it means something - very strange has happened and we're not going to support it. - */ - if ((pRenderedBufferList->mNumberBuffers % pDevice->capture.internalChannels) == 0) { - ma_uint8 tempBuffer[4096]; - for (iBuffer = 0; iBuffer < pRenderedBufferList->mNumberBuffers; iBuffer += pDevice->capture.internalChannels) { - ma_uint32 framesRemaining = frameCount; - while (framesRemaining > 0) { - void* ppDeinterleavedBuffers[MA_MAX_CHANNELS]; - ma_uint32 iChannel; - ma_uint32 framesToSend = sizeof(tempBuffer) / ma_get_bytes_per_frame(pDevice->capture.internalFormat, pDevice->capture.internalChannels); - if (framesToSend > framesRemaining) { - framesToSend = framesRemaining; - } - - for (iChannel = 0; iChannel < pDevice->capture.internalChannels; ++iChannel) { - ppDeinterleavedBuffers[iChannel] = (void*)ma_offset_ptr(pRenderedBufferList->mBuffers[iBuffer+iChannel].mData, (frameCount - framesRemaining) * ma_get_bytes_per_sample(pDevice->capture.internalFormat)); - } - - ma_interleave_pcm_frames(pDevice->capture.internalFormat, pDevice->capture.internalChannels, framesToSend, (const void**)ppDeinterleavedBuffers, tempBuffer); - ma_device_handle_backend_data_callback(pDevice, NULL, tempBuffer, framesToSend); - - framesRemaining -= framesToSend; - } - } - } - } - - (void)pActionFlags; - (void)pTimeStamp; - (void)busNumber; - (void)frameCount; - (void)pUnusedBufferList; - - return noErr; -} - -static void on_start_stop__coreaudio(void* pUserData, AudioUnit audioUnit, AudioUnitPropertyID propertyID, AudioUnitScope scope, AudioUnitElement element) -{ - ma_device* pDevice = (ma_device*)pUserData; - MA_ASSERT(pDevice != NULL); - - /* Don't do anything if it looks like we're just reinitializing due to a device switch. */ - if (((audioUnit == pDevice->coreaudio.audioUnitPlayback) && pDevice->coreaudio.isSwitchingPlaybackDevice) || - ((audioUnit == pDevice->coreaudio.audioUnitCapture) && pDevice->coreaudio.isSwitchingCaptureDevice)) { - return; - } - - /* - There's been a report of a deadlock here when triggered by ma_device_uninit(). It looks like - AudioUnitGetProprty (called below) and AudioComponentInstanceDispose (called in ma_device_uninit) - can try waiting on the same lock. I'm going to try working around this by not calling any Core - Audio APIs in the callback when the device has been stopped or uninitialized. - */ - if (ma_device_get_state(pDevice) == ma_device_state_uninitialized || ma_device_get_state(pDevice) == ma_device_state_stopping || ma_device_get_state(pDevice) == ma_device_state_stopped) { - ma_device__on_notification_stopped(pDevice); - } else { - UInt32 isRunning; - UInt32 isRunningSize = sizeof(isRunning); - OSStatus status = ((ma_AudioUnitGetProperty_proc)pDevice->pContext->coreaudio.AudioUnitGetProperty)(audioUnit, kAudioOutputUnitProperty_IsRunning, scope, element, &isRunning, &isRunningSize); - if (status != noErr) { - goto done; /* Don't really know what to do in this case... just ignore it, I suppose... */ - } - - if (!isRunning) { - /* - The stop event is a bit annoying in Core Audio because it will be called when we automatically switch the default device. Some scenarios to consider: - - 1) When the device is unplugged, this will be called _before_ the default device change notification. - 2) When the device is changed via the default device change notification, this will be called _after_ the switch. - - For case #1, we just check if there's a new default device available. If so, we just ignore the stop event. For case #2 we check a flag. - */ - if (((audioUnit == pDevice->coreaudio.audioUnitPlayback) && pDevice->coreaudio.isDefaultPlaybackDevice) || - ((audioUnit == pDevice->coreaudio.audioUnitCapture) && pDevice->coreaudio.isDefaultCaptureDevice)) { - /* - It looks like the device is switching through an external event, such as the user unplugging the device or changing the default device - via the operating system's sound settings. If we're re-initializing the device, we just terminate because we want the stopping of the - device to be seamless to the client (we don't want them receiving the stopped event and thinking that the device has stopped when it - hasn't!). - */ - if (((audioUnit == pDevice->coreaudio.audioUnitPlayback) && pDevice->coreaudio.isSwitchingPlaybackDevice) || - ((audioUnit == pDevice->coreaudio.audioUnitCapture) && pDevice->coreaudio.isSwitchingCaptureDevice)) { - goto done; - } - - /* - Getting here means the device is not reinitializing which means it may have been unplugged. From what I can see, it looks like Core Audio - will try switching to the new default device seamlessly. We need to somehow find a way to determine whether or not Core Audio will most - likely be successful in switching to the new device. - - TODO: Try to predict if Core Audio will switch devices. If not, the stopped callback needs to be posted. - */ - goto done; - } - - /* Getting here means we need to stop the device. */ - ma_device__on_notification_stopped(pDevice); - } - } - - (void)propertyID; /* Unused. */ - -done: - /* Always signal the stop event. It's possible for the "else" case to get hit which can happen during an interruption. */ - ma_event_signal(&pDevice->coreaudio.stopEvent); -} - -#if defined(MA_APPLE_DESKTOP) -static ma_spinlock g_DeviceTrackingInitLock_CoreAudio = 0; /* A spinlock for mutal exclusion of the init/uninit of the global tracking data. Initialization to 0 is what we need. */ -static ma_uint32 g_DeviceTrackingInitCounter_CoreAudio = 0; -static ma_mutex g_DeviceTrackingMutex_CoreAudio; -static ma_device** g_ppTrackedDevices_CoreAudio = NULL; -static ma_uint32 g_TrackedDeviceCap_CoreAudio = 0; -static ma_uint32 g_TrackedDeviceCount_CoreAudio = 0; - -static OSStatus ma_default_device_changed__coreaudio(AudioObjectID objectID, UInt32 addressCount, const AudioObjectPropertyAddress* pAddresses, void* pUserData) -{ - ma_device_type deviceType; - - /* Not sure if I really need to check this, but it makes me feel better. */ - if (addressCount == 0) { - return noErr; - } - - if (pAddresses[0].mSelector == kAudioHardwarePropertyDefaultOutputDevice) { - deviceType = ma_device_type_playback; - } else if (pAddresses[0].mSelector == kAudioHardwarePropertyDefaultInputDevice) { - deviceType = ma_device_type_capture; - } else { - return noErr; /* Should never hit this. */ - } - - ma_mutex_lock(&g_DeviceTrackingMutex_CoreAudio); - { - ma_uint32 iDevice; - for (iDevice = 0; iDevice < g_TrackedDeviceCount_CoreAudio; iDevice += 1) { - ma_result reinitResult; - ma_device* pDevice; - - pDevice = g_ppTrackedDevices_CoreAudio[iDevice]; - if (pDevice->type == deviceType || pDevice->type == ma_device_type_duplex) { - if (deviceType == ma_device_type_playback) { - pDevice->coreaudio.isSwitchingPlaybackDevice = MA_TRUE; - reinitResult = ma_device_reinit_internal__coreaudio(pDevice, deviceType, MA_TRUE); - pDevice->coreaudio.isSwitchingPlaybackDevice = MA_FALSE; - } else { - pDevice->coreaudio.isSwitchingCaptureDevice = MA_TRUE; - reinitResult = ma_device_reinit_internal__coreaudio(pDevice, deviceType, MA_TRUE); - pDevice->coreaudio.isSwitchingCaptureDevice = MA_FALSE; - } - - if (reinitResult == MA_SUCCESS) { - ma_device__post_init_setup(pDevice, deviceType); - - /* Restart the device if required. If this fails we need to stop the device entirely. */ - if (ma_device_get_state(pDevice) == ma_device_state_started) { - OSStatus status; - if (deviceType == ma_device_type_playback) { - status = ((ma_AudioOutputUnitStart_proc)pDevice->pContext->coreaudio.AudioOutputUnitStart)((AudioUnit)pDevice->coreaudio.audioUnitPlayback); - if (status != noErr) { - if (pDevice->type == ma_device_type_duplex) { - ((ma_AudioOutputUnitStop_proc)pDevice->pContext->coreaudio.AudioOutputUnitStop)((AudioUnit)pDevice->coreaudio.audioUnitCapture); - } - ma_device__set_state(pDevice, ma_device_state_stopped); - } - } else if (deviceType == ma_device_type_capture) { - status = ((ma_AudioOutputUnitStart_proc)pDevice->pContext->coreaudio.AudioOutputUnitStart)((AudioUnit)pDevice->coreaudio.audioUnitCapture); - if (status != noErr) { - if (pDevice->type == ma_device_type_duplex) { - ((ma_AudioOutputUnitStop_proc)pDevice->pContext->coreaudio.AudioOutputUnitStop)((AudioUnit)pDevice->coreaudio.audioUnitPlayback); - } - ma_device__set_state(pDevice, ma_device_state_stopped); - } - } - } - - ma_device__on_notification_rerouted(pDevice); - } - } - } - } - ma_mutex_unlock(&g_DeviceTrackingMutex_CoreAudio); - - /* Unused parameters. */ - (void)objectID; - (void)pUserData; - - return noErr; -} - -static ma_result ma_context__init_device_tracking__coreaudio(ma_context* pContext) -{ - MA_ASSERT(pContext != NULL); - - ma_spinlock_lock(&g_DeviceTrackingInitLock_CoreAudio); - { - /* Don't do anything if we've already initialized device tracking. */ - if (g_DeviceTrackingInitCounter_CoreAudio == 0) { - AudioObjectPropertyAddress propAddress; - propAddress.mScope = kAudioObjectPropertyScopeGlobal; - propAddress.mElement = AUDIO_OBJECT_PROPERTY_ELEMENT; - - ma_mutex_init(&g_DeviceTrackingMutex_CoreAudio); - - propAddress.mSelector = kAudioHardwarePropertyDefaultInputDevice; - ((ma_AudioObjectAddPropertyListener_proc)pContext->coreaudio.AudioObjectAddPropertyListener)(kAudioObjectSystemObject, &propAddress, &ma_default_device_changed__coreaudio, NULL); - - propAddress.mSelector = kAudioHardwarePropertyDefaultOutputDevice; - ((ma_AudioObjectAddPropertyListener_proc)pContext->coreaudio.AudioObjectAddPropertyListener)(kAudioObjectSystemObject, &propAddress, &ma_default_device_changed__coreaudio, NULL); - - } - g_DeviceTrackingInitCounter_CoreAudio += 1; - } - ma_spinlock_unlock(&g_DeviceTrackingInitLock_CoreAudio); - - return MA_SUCCESS; -} - -static ma_result ma_context__uninit_device_tracking__coreaudio(ma_context* pContext) -{ - MA_ASSERT(pContext != NULL); - - ma_spinlock_lock(&g_DeviceTrackingInitLock_CoreAudio); - { - if (g_DeviceTrackingInitCounter_CoreAudio > 0) - g_DeviceTrackingInitCounter_CoreAudio -= 1; - - if (g_DeviceTrackingInitCounter_CoreAudio == 0) { - AudioObjectPropertyAddress propAddress; - propAddress.mScope = kAudioObjectPropertyScopeGlobal; - propAddress.mElement = AUDIO_OBJECT_PROPERTY_ELEMENT; - - propAddress.mSelector = kAudioHardwarePropertyDefaultInputDevice; - ((ma_AudioObjectRemovePropertyListener_proc)pContext->coreaudio.AudioObjectRemovePropertyListener)(kAudioObjectSystemObject, &propAddress, &ma_default_device_changed__coreaudio, NULL); - - propAddress.mSelector = kAudioHardwarePropertyDefaultOutputDevice; - ((ma_AudioObjectRemovePropertyListener_proc)pContext->coreaudio.AudioObjectRemovePropertyListener)(kAudioObjectSystemObject, &propAddress, &ma_default_device_changed__coreaudio, NULL); - - /* At this point there should be no tracked devices. If not there's an error somewhere. */ - if (g_ppTrackedDevices_CoreAudio != NULL) { - ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_WARNING, "You have uninitialized all contexts while an associated device is still active."); - ma_spinlock_unlock(&g_DeviceTrackingInitLock_CoreAudio); - return MA_INVALID_OPERATION; - } - - ma_mutex_uninit(&g_DeviceTrackingMutex_CoreAudio); - } - } - ma_spinlock_unlock(&g_DeviceTrackingInitLock_CoreAudio); - - return MA_SUCCESS; -} - -static ma_result ma_device__track__coreaudio(ma_device* pDevice) -{ - MA_ASSERT(pDevice != NULL); - - ma_mutex_lock(&g_DeviceTrackingMutex_CoreAudio); - { - /* Allocate memory if required. */ - if (g_TrackedDeviceCap_CoreAudio <= g_TrackedDeviceCount_CoreAudio) { - ma_uint32 newCap; - ma_device** ppNewDevices; - - newCap = g_TrackedDeviceCap_CoreAudio * 2; - if (newCap == 0) { - newCap = 1; - } - - ppNewDevices = (ma_device**)ma_realloc(g_ppTrackedDevices_CoreAudio, sizeof(*g_ppTrackedDevices_CoreAudio)*newCap, &pDevice->pContext->allocationCallbacks); - if (ppNewDevices == NULL) { - ma_mutex_unlock(&g_DeviceTrackingMutex_CoreAudio); - return MA_OUT_OF_MEMORY; - } - - g_ppTrackedDevices_CoreAudio = ppNewDevices; - g_TrackedDeviceCap_CoreAudio = newCap; - } - - g_ppTrackedDevices_CoreAudio[g_TrackedDeviceCount_CoreAudio] = pDevice; - g_TrackedDeviceCount_CoreAudio += 1; - } - ma_mutex_unlock(&g_DeviceTrackingMutex_CoreAudio); - - return MA_SUCCESS; -} - -static ma_result ma_device__untrack__coreaudio(ma_device* pDevice) -{ - MA_ASSERT(pDevice != NULL); - - ma_mutex_lock(&g_DeviceTrackingMutex_CoreAudio); - { - ma_uint32 iDevice; - for (iDevice = 0; iDevice < g_TrackedDeviceCount_CoreAudio; iDevice += 1) { - if (g_ppTrackedDevices_CoreAudio[iDevice] == pDevice) { - /* We've found the device. We now need to remove it from the list. */ - ma_uint32 jDevice; - for (jDevice = iDevice; jDevice < g_TrackedDeviceCount_CoreAudio-1; jDevice += 1) { - g_ppTrackedDevices_CoreAudio[jDevice] = g_ppTrackedDevices_CoreAudio[jDevice+1]; - } - - g_TrackedDeviceCount_CoreAudio -= 1; - - /* If there's nothing else in the list we need to free memory. */ - if (g_TrackedDeviceCount_CoreAudio == 0) { - ma_free(g_ppTrackedDevices_CoreAudio, &pDevice->pContext->allocationCallbacks); - g_ppTrackedDevices_CoreAudio = NULL; - g_TrackedDeviceCap_CoreAudio = 0; - } - - break; - } - } - } - ma_mutex_unlock(&g_DeviceTrackingMutex_CoreAudio); - - return MA_SUCCESS; -} -#endif - -#if defined(MA_APPLE_MOBILE) -@interface ma_ios_notification_handler:NSObject { - ma_device* m_pDevice; -} -@end - -@implementation ma_ios_notification_handler --(id)init:(ma_device*)pDevice -{ - self = [super init]; - m_pDevice = pDevice; - - /* For route changes. */ - [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(handle_route_change:) name:AVAudioSessionRouteChangeNotification object:[AVAudioSession sharedInstance]]; - - /* For interruptions. */ - [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(handle_interruption:) name:AVAudioSessionInterruptionNotification object:[AVAudioSession sharedInstance]]; - - return self; -} - --(void)dealloc -{ - [self remove_handler]; - - #if defined(__has_feature) - #if !__has_feature(objc_arc) - [super dealloc]; - #endif - #endif -} - --(void)remove_handler -{ - [[NSNotificationCenter defaultCenter] removeObserver:self name:AVAudioSessionRouteChangeNotification object:nil]; - [[NSNotificationCenter defaultCenter] removeObserver:self name:AVAudioSessionInterruptionNotification object:nil]; -} - --(void)handle_interruption:(NSNotification*)pNotification -{ - NSInteger type = [[[pNotification userInfo] objectForKey:AVAudioSessionInterruptionTypeKey] integerValue]; - switch (type) - { - case AVAudioSessionInterruptionTypeBegan: - { - ma_log_postf(ma_device_get_log(m_pDevice), MA_LOG_LEVEL_INFO, "[Core Audio] Interruption: AVAudioSessionInterruptionTypeBegan\n"); - - /* - Core Audio will have stopped the internal device automatically, but we need explicitly - stop it at a higher level to ensure miniaudio-specific state is updated for consistency. - */ - ma_device_stop(m_pDevice); - - /* - Fire the notification after the device has been stopped to ensure it's in the correct - state when the notification handler is invoked. - */ - ma_device__on_notification_interruption_began(m_pDevice); - } break; - - case AVAudioSessionInterruptionTypeEnded: - { - ma_log_postf(ma_device_get_log(m_pDevice), MA_LOG_LEVEL_INFO, "[Core Audio] Interruption: AVAudioSessionInterruptionTypeEnded\n"); - ma_device__on_notification_interruption_ended(m_pDevice); - } break; - } -} - --(void)handle_route_change:(NSNotification*)pNotification -{ - AVAudioSession* pSession = [AVAudioSession sharedInstance]; - - NSInteger reason = [[[pNotification userInfo] objectForKey:AVAudioSessionRouteChangeReasonKey] integerValue]; - switch (reason) - { - case AVAudioSessionRouteChangeReasonOldDeviceUnavailable: - { - ma_log_postf(ma_device_get_log(m_pDevice), MA_LOG_LEVEL_INFO, "[Core Audio] Route Changed: AVAudioSessionRouteChangeReasonOldDeviceUnavailable\n"); - } break; - - case AVAudioSessionRouteChangeReasonNewDeviceAvailable: - { - ma_log_postf(ma_device_get_log(m_pDevice), MA_LOG_LEVEL_INFO, "[Core Audio] Route Changed: AVAudioSessionRouteChangeReasonNewDeviceAvailable\n"); - } break; - - case AVAudioSessionRouteChangeReasonNoSuitableRouteForCategory: - { - ma_log_postf(ma_device_get_log(m_pDevice), MA_LOG_LEVEL_INFO, "[Core Audio] Route Changed: AVAudioSessionRouteChangeReasonNoSuitableRouteForCategory\n"); - } break; - - case AVAudioSessionRouteChangeReasonWakeFromSleep: - { - ma_log_postf(ma_device_get_log(m_pDevice), MA_LOG_LEVEL_INFO, "[Core Audio] Route Changed: AVAudioSessionRouteChangeReasonWakeFromSleep\n"); - } break; - - case AVAudioSessionRouteChangeReasonOverride: - { - ma_log_postf(ma_device_get_log(m_pDevice), MA_LOG_LEVEL_INFO, "[Core Audio] Route Changed: AVAudioSessionRouteChangeReasonOverride\n"); - } break; - - case AVAudioSessionRouteChangeReasonCategoryChange: - { - ma_log_postf(ma_device_get_log(m_pDevice), MA_LOG_LEVEL_INFO, "[Core Audio] Route Changed: AVAudioSessionRouteChangeReasonCategoryChange\n"); - } break; - - case AVAudioSessionRouteChangeReasonUnknown: - default: - { - ma_log_postf(ma_device_get_log(m_pDevice), MA_LOG_LEVEL_INFO, "[Core Audio] Route Changed: AVAudioSessionRouteChangeReasonUnknown\n"); - } break; - } - - ma_log_postf(ma_device_get_log(m_pDevice), MA_LOG_LEVEL_DEBUG, "[Core Audio] Changing Route. inputNumberChannels=%d; outputNumberOfChannels=%d\n", (int)pSession.inputNumberOfChannels, (int)pSession.outputNumberOfChannels); - - /* Let the application know about the route change. */ - ma_device__on_notification_rerouted(m_pDevice); -} -@end -#endif - -static ma_result ma_device_uninit__coreaudio(ma_device* pDevice) -{ - MA_ASSERT(pDevice != NULL); - MA_ASSERT(ma_device_get_state(pDevice) == ma_device_state_uninitialized); - -#if defined(MA_APPLE_DESKTOP) - /* - Make sure we're no longer tracking the device. It doesn't matter if we call this for a non-default device because it'll - just gracefully ignore it. - */ - ma_device__untrack__coreaudio(pDevice); -#endif -#if defined(MA_APPLE_MOBILE) - if (pDevice->coreaudio.pNotificationHandler != NULL) { - ma_ios_notification_handler* pNotificationHandler = (MA_BRIDGE_TRANSFER ma_ios_notification_handler*)pDevice->coreaudio.pNotificationHandler; - [pNotificationHandler remove_handler]; - } -#endif - - if (pDevice->coreaudio.audioUnitCapture != NULL) { - ((ma_AudioComponentInstanceDispose_proc)pDevice->pContext->coreaudio.AudioComponentInstanceDispose)((AudioUnit)pDevice->coreaudio.audioUnitCapture); - } - if (pDevice->coreaudio.audioUnitPlayback != NULL) { - ((ma_AudioComponentInstanceDispose_proc)pDevice->pContext->coreaudio.AudioComponentInstanceDispose)((AudioUnit)pDevice->coreaudio.audioUnitPlayback); - } - - if (pDevice->coreaudio.pAudioBufferList) { - ma_free(pDevice->coreaudio.pAudioBufferList, &pDevice->pContext->allocationCallbacks); - } - - return MA_SUCCESS; -} - -typedef struct -{ - ma_bool32 allowNominalSampleRateChange; - - /* Input. */ - ma_format formatIn; - ma_uint32 channelsIn; - ma_uint32 sampleRateIn; - ma_channel channelMapIn[MA_MAX_CHANNELS]; - ma_uint32 periodSizeInFramesIn; - ma_uint32 periodSizeInMillisecondsIn; - ma_uint32 periodsIn; - ma_share_mode shareMode; - ma_performance_profile performanceProfile; - ma_bool32 registerStopEvent; - - /* Output. */ -#if defined(MA_APPLE_DESKTOP) - AudioObjectID deviceObjectID; -#endif - AudioComponent component; - AudioUnit audioUnit; - AudioBufferList* pAudioBufferList; /* Only used for input devices. */ - ma_format formatOut; - ma_uint32 channelsOut; - ma_uint32 sampleRateOut; - ma_channel channelMapOut[MA_MAX_CHANNELS]; - ma_uint32 periodSizeInFramesOut; - ma_uint32 periodsOut; - char deviceName[256]; -} ma_device_init_internal_data__coreaudio; - -static ma_result ma_device_init_internal__coreaudio(ma_context* pContext, ma_device_type deviceType, const ma_device_id* pDeviceID, ma_device_init_internal_data__coreaudio* pData, void* pDevice_DoNotReference) /* <-- pDevice is typed as void* intentionally so as to avoid accidentally referencing it. */ -{ - ma_result result = MA_SUCCESS; - OSStatus status; - UInt32 enableIOFlag; - AudioStreamBasicDescription bestFormat; - ma_uint32 actualPeriodSizeInFrames; - AURenderCallbackStruct callbackInfo; -#if defined(MA_APPLE_DESKTOP) - AudioObjectID deviceObjectID; -#endif - - /* This API should only be used for a single device type: playback or capture. No full-duplex mode. */ - if (deviceType == ma_device_type_duplex) { - return MA_INVALID_ARGS; - } - - MA_ASSERT(pContext != NULL); - MA_ASSERT(deviceType == ma_device_type_playback || deviceType == ma_device_type_capture); - -#if defined(MA_APPLE_DESKTOP) - pData->deviceObjectID = 0; -#endif - pData->component = NULL; - pData->audioUnit = NULL; - pData->pAudioBufferList = NULL; - -#if defined(MA_APPLE_DESKTOP) - result = ma_find_AudioObjectID(pContext, deviceType, pDeviceID, &deviceObjectID); - if (result != MA_SUCCESS) { - return result; - } - - pData->deviceObjectID = deviceObjectID; -#endif - - /* Core audio doesn't really use the notion of a period so we can leave this unmodified, but not too over the top. */ - pData->periodsOut = pData->periodsIn; - if (pData->periodsOut == 0) { - pData->periodsOut = MA_DEFAULT_PERIODS; - } - if (pData->periodsOut > 16) { - pData->periodsOut = 16; - } - - - /* Audio unit. */ - status = ((ma_AudioComponentInstanceNew_proc)pContext->coreaudio.AudioComponentInstanceNew)((AudioComponent)pContext->coreaudio.component, (AudioUnit*)&pData->audioUnit); - if (status != noErr) { - return ma_result_from_OSStatus(status); - } - - - /* The input/output buses need to be explicitly enabled and disabled. We set the flag based on the output unit first, then we just swap it for input. */ - enableIOFlag = 1; - if (deviceType == ma_device_type_capture) { - enableIOFlag = 0; - } - - status = ((ma_AudioUnitSetProperty_proc)pContext->coreaudio.AudioUnitSetProperty)(pData->audioUnit, kAudioOutputUnitProperty_EnableIO, kAudioUnitScope_Output, MA_COREAUDIO_OUTPUT_BUS, &enableIOFlag, sizeof(enableIOFlag)); - if (status != noErr) { - ((ma_AudioComponentInstanceDispose_proc)pContext->coreaudio.AudioComponentInstanceDispose)(pData->audioUnit); - return ma_result_from_OSStatus(status); - } - - enableIOFlag = (enableIOFlag == 0) ? 1 : 0; - status = ((ma_AudioUnitSetProperty_proc)pContext->coreaudio.AudioUnitSetProperty)(pData->audioUnit, kAudioOutputUnitProperty_EnableIO, kAudioUnitScope_Input, MA_COREAUDIO_INPUT_BUS, &enableIOFlag, sizeof(enableIOFlag)); - if (status != noErr) { - ((ma_AudioComponentInstanceDispose_proc)pContext->coreaudio.AudioComponentInstanceDispose)(pData->audioUnit); - return ma_result_from_OSStatus(status); - } - - - /* Set the device to use with this audio unit. This is only used on desktop since we are using defaults on mobile. */ -#if defined(MA_APPLE_DESKTOP) - status = ((ma_AudioUnitSetProperty_proc)pContext->coreaudio.AudioUnitSetProperty)(pData->audioUnit, kAudioOutputUnitProperty_CurrentDevice, kAudioUnitScope_Global, 0, &deviceObjectID, sizeof(deviceObjectID)); - if (status != noErr) { - ((ma_AudioComponentInstanceDispose_proc)pContext->coreaudio.AudioComponentInstanceDispose)(pData->audioUnit); - return ma_result_from_OSStatus(result); - } -#else - /* - For some reason it looks like Apple is only allowing selection of the input device. There does not appear to be any way to change - the default output route. I have no idea why this is like this, but for now we'll only be able to configure capture devices. - */ - if (pDeviceID != NULL) { - if (deviceType == ma_device_type_capture) { - ma_bool32 found = MA_FALSE; - NSArray *pInputs = [[[AVAudioSession sharedInstance] currentRoute] inputs]; - for (AVAudioSessionPortDescription* pPortDesc in pInputs) { - if (strcmp(pDeviceID->coreaudio, [pPortDesc.UID UTF8String]) == 0) { - [[AVAudioSession sharedInstance] setPreferredInput:pPortDesc error:nil]; - found = MA_TRUE; - break; - } - } - - if (found == MA_FALSE) { - return MA_DOES_NOT_EXIST; - } - } - } -#endif - - /* - Format. This is the hardest part of initialization because there's a few variables to take into account. - 1) The format must be supported by the device. - 2) The format must be supported miniaudio. - 3) There's a priority that miniaudio prefers. - - Ideally we would like to use a format that's as close to the hardware as possible so we can get as close to a passthrough as possible. The - most important property is the sample rate. miniaudio can do format conversion for any sample rate and channel count, but cannot do the same - for the sample data format. If the sample data format is not supported by miniaudio it must be ignored completely. - - On mobile platforms this is a bit different. We just force the use of whatever the audio unit's current format is set to. - */ - { - AudioStreamBasicDescription origFormat; - UInt32 origFormatSize = sizeof(origFormat); - AudioUnitScope formatScope = (deviceType == ma_device_type_playback) ? kAudioUnitScope_Input : kAudioUnitScope_Output; - AudioUnitElement formatElement = (deviceType == ma_device_type_playback) ? MA_COREAUDIO_OUTPUT_BUS : MA_COREAUDIO_INPUT_BUS; - - if (deviceType == ma_device_type_playback) { - status = ((ma_AudioUnitGetProperty_proc)pContext->coreaudio.AudioUnitGetProperty)(pData->audioUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Output, MA_COREAUDIO_OUTPUT_BUS, &origFormat, &origFormatSize); - } else { - status = ((ma_AudioUnitGetProperty_proc)pContext->coreaudio.AudioUnitGetProperty)(pData->audioUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Input, MA_COREAUDIO_INPUT_BUS, &origFormat, &origFormatSize); - } - if (status != noErr) { - ((ma_AudioComponentInstanceDispose_proc)pContext->coreaudio.AudioComponentInstanceDispose)(pData->audioUnit); - return ma_result_from_OSStatus(status); - } - - #if defined(MA_APPLE_DESKTOP) - result = ma_find_best_format__coreaudio(pContext, deviceObjectID, deviceType, pData->formatIn, pData->channelsIn, pData->sampleRateIn, &origFormat, &bestFormat); - if (result != MA_SUCCESS) { - ((ma_AudioComponentInstanceDispose_proc)pContext->coreaudio.AudioComponentInstanceDispose)(pData->audioUnit); - return result; - } - - /* - Technical Note TN2091: Device input using the HAL Output Audio Unit - https://developer.apple.com/library/archive/technotes/tn2091/_index.html - - This documentation says the following: - - The internal AudioConverter can handle any *simple* conversion. Typically, this means that a client can specify ANY - variant of the PCM formats. Consequently, the device's sample rate should match the desired sample rate. If sample rate - conversion is needed, it can be accomplished by buffering the input and converting the data on a separate thread with - another AudioConverter. - - The important part here is the mention that it can handle *simple* conversions, which does *not* include sample rate. We - therefore want to ensure the sample rate stays consistent. This document is specifically for input, but I'm going to play it - safe and apply the same rule to output as well. - - I have tried going against the documentation by setting the sample rate anyway, but this just results in AudioUnitRender() - returning a result code of -10863. I have also tried changing the format directly on the input scope on the input bus, but - this just results in `ca_require: IsStreamFormatWritable(inScope, inElement) NotWritable` when trying to set the format. - - Something that does seem to work, however, has been setting the nominal sample rate on the device object. The problem with - this, however, is that it actually changes the sample rate at the operating system level and not just the application. This - could be intrusive to the user, however, so I don't think it's wise to make this the default. Instead I'm making this a - configuration option. When the `coreaudio.allowNominalSampleRateChange` config option is set to true, changing the sample - rate will be allowed. Otherwise it'll be fixed to the current sample rate. To check the system-defined sample rate, run - the Audio MIDI Setup program that comes installed on macOS and observe how the sample rate changes as the sample rate is - changed by miniaudio. - */ - if (pData->allowNominalSampleRateChange) { - AudioValueRange sampleRateRange; - AudioObjectPropertyAddress propAddress; - - sampleRateRange.mMinimum = bestFormat.mSampleRate; - sampleRateRange.mMaximum = bestFormat.mSampleRate; - - propAddress.mSelector = kAudioDevicePropertyNominalSampleRate; - propAddress.mScope = (deviceType == ma_device_type_playback) ? kAudioObjectPropertyScopeOutput : kAudioObjectPropertyScopeInput; - propAddress.mElement = AUDIO_OBJECT_PROPERTY_ELEMENT; - - status = ((ma_AudioObjectSetPropertyData_proc)pContext->coreaudio.AudioObjectSetPropertyData)(deviceObjectID, &propAddress, 0, NULL, sizeof(sampleRateRange), &sampleRateRange); - if (status != noErr) { - bestFormat.mSampleRate = origFormat.mSampleRate; - } - } else { - bestFormat.mSampleRate = origFormat.mSampleRate; - } - - status = ((ma_AudioUnitSetProperty_proc)pContext->coreaudio.AudioUnitSetProperty)(pData->audioUnit, kAudioUnitProperty_StreamFormat, formatScope, formatElement, &bestFormat, sizeof(bestFormat)); - if (status != noErr) { - /* We failed to set the format, so fall back to the current format of the audio unit. */ - bestFormat = origFormat; - } - #else - bestFormat = origFormat; - - /* - Sample rate is a little different here because for some reason kAudioUnitProperty_StreamFormat returns 0... Oh well. We need to instead try - setting the sample rate to what the user has requested and then just see the results of it. Need to use some Objective-C here for this since - it depends on Apple's AVAudioSession API. To do this we just get the shared AVAudioSession instance and then set it. Note that from what I - can tell, it looks like the sample rate is shared between playback and capture for everything. - */ - @autoreleasepool { - AVAudioSession* pAudioSession = [AVAudioSession sharedInstance]; - MA_ASSERT(pAudioSession != NULL); - - [pAudioSession setPreferredSampleRate:(double)pData->sampleRateIn error:nil]; - bestFormat.mSampleRate = pAudioSession.sampleRate; - - /* - I've had a report that the channel count returned by AudioUnitGetProperty above is inconsistent with - AVAudioSession outputNumberOfChannels. I'm going to try using the AVAudioSession values instead. - - UPDATE 20/02/2025: - When testing on the simulator with an iPhone 15 and iOS 17 I get an error when initializing the audio - unit if set the input channels to pAudioSession.inputNumberOfChannels. What is happening is the channel - count returned from AudioUnitGetProperty() above is set to 2, but pAudioSession is reporting a channel - count of 1. When this happens, the call to AudioUnitSetProprty() below just down below will succeed, but - AudioUnitInitialize() further down will fail. The only solution I have come up with is to not set the - channel count to pAudioSession.inputNumberOfChannels. - */ - if (deviceType == ma_device_type_playback) { - bestFormat.mChannelsPerFrame = (UInt32)pAudioSession.outputNumberOfChannels; - } - - #if 0 - if (deviceType == ma_device_type_capture) { - /*printf("DEBUG: bestFormat.mChannelsPerFrame = %d; pAudioSession.inputNumberOfChannels = %d\n", (int)bestFormat.mChannelsPerFrame, (int)pAudioSession.inputNumberOfChannels);*/ - bestFormat.mChannelsPerFrame = (UInt32)pAudioSession.inputNumberOfChannels; - } - #endif - } - - - status = ((ma_AudioUnitSetProperty_proc)pContext->coreaudio.AudioUnitSetProperty)(pData->audioUnit, kAudioUnitProperty_StreamFormat, formatScope, formatElement, &bestFormat, sizeof(bestFormat)); - if (status != noErr) { - ((ma_AudioComponentInstanceDispose_proc)pContext->coreaudio.AudioComponentInstanceDispose)(pData->audioUnit); - return ma_result_from_OSStatus(status); - } - #endif - - result = ma_format_from_AudioStreamBasicDescription(&bestFormat, &pData->formatOut); - if (result != MA_SUCCESS) { - ((ma_AudioComponentInstanceDispose_proc)pContext->coreaudio.AudioComponentInstanceDispose)(pData->audioUnit); - return result; - } - - if (pData->formatOut == ma_format_unknown) { - ((ma_AudioComponentInstanceDispose_proc)pContext->coreaudio.AudioComponentInstanceDispose)(pData->audioUnit); - return MA_FORMAT_NOT_SUPPORTED; - } - - pData->channelsOut = bestFormat.mChannelsPerFrame; - pData->sampleRateOut = (ma_uint32)bestFormat.mSampleRate; - } - - /* Clamp the channel count for safety. */ - if (pData->channelsOut > MA_MAX_CHANNELS) { - pData->channelsOut = MA_MAX_CHANNELS; - } - - /* - Internal channel map. This is weird in my testing. If I use the AudioObject to get the - channel map, the channel descriptions are set to "Unknown" for some reason. To work around - this it looks like retrieving it from the AudioUnit will work. However, and this is where - it gets weird, it doesn't seem to work with capture devices, nor at all on iOS... Therefore - I'm going to fall back to a default assumption in these cases. - */ -#if defined(MA_APPLE_DESKTOP) - result = ma_get_AudioUnit_channel_map(pContext, pData->audioUnit, deviceType, pData->channelMapOut, pData->channelsOut); - if (result != MA_SUCCESS) { - #if 0 - /* Try falling back to the channel map from the AudioObject. */ - result = ma_get_AudioObject_channel_map(pContext, deviceObjectID, deviceType, pData->channelMapOut, pData->channelsOut); - if (result != MA_SUCCESS) { - return result; - } - #else - /* Fall back to default assumptions. */ - ma_channel_map_init_standard(ma_standard_channel_map_default, pData->channelMapOut, ma_countof(pData->channelMapOut), pData->channelsOut); - #endif - } -#else - /* TODO: Figure out how to get the channel map using AVAudioSession. */ - ma_channel_map_init_standard(ma_standard_channel_map_default, pData->channelMapOut, ma_countof(pData->channelMapOut), pData->channelsOut); -#endif - - - /* Buffer size. Not allowing this to be configurable on iOS. */ - if (pData->periodSizeInFramesIn == 0) { - if (pData->periodSizeInMillisecondsIn == 0) { - if (pData->performanceProfile == ma_performance_profile_low_latency) { - actualPeriodSizeInFrames = ma_calculate_buffer_size_in_frames_from_milliseconds(MA_DEFAULT_PERIOD_SIZE_IN_MILLISECONDS_LOW_LATENCY, pData->sampleRateOut); - } else { - actualPeriodSizeInFrames = ma_calculate_buffer_size_in_frames_from_milliseconds(MA_DEFAULT_PERIOD_SIZE_IN_MILLISECONDS_CONSERVATIVE, pData->sampleRateOut); - } - } else { - actualPeriodSizeInFrames = ma_calculate_buffer_size_in_frames_from_milliseconds(pData->periodSizeInMillisecondsIn, pData->sampleRateOut); - } - } else { - actualPeriodSizeInFrames = pData->periodSizeInFramesIn; - } - -#if defined(MA_APPLE_DESKTOP) - result = ma_set_AudioObject_buffer_size_in_frames(pContext, deviceObjectID, deviceType, &actualPeriodSizeInFrames); - if (result != MA_SUCCESS) { - return result; - } -#else - /* - On iOS, the size of the IO buffer needs to be specified in seconds and is a floating point - number. I don't trust any potential truncation errors due to converting from float to integer - so I'm going to explicitly set the actual period size to the next power of 2. - */ - @autoreleasepool { - AVAudioSession* pAudioSession = [AVAudioSession sharedInstance]; - MA_ASSERT(pAudioSession != NULL); - - [pAudioSession setPreferredIOBufferDuration:((float)actualPeriodSizeInFrames / pAudioSession.sampleRate) error:nil]; - actualPeriodSizeInFrames = ma_next_power_of_2((ma_uint32)(pAudioSession.IOBufferDuration * pAudioSession.sampleRate)); - } -#endif - - - /* - During testing I discovered that the buffer size can be too big. You'll get an error like this: - - kAudioUnitErr_TooManyFramesToProcess : inFramesToProcess=4096, mMaxFramesPerSlice=512 - - Note how inFramesToProcess is smaller than mMaxFramesPerSlice. To fix, we need to set kAudioUnitProperty_MaximumFramesPerSlice to that - of the size of our buffer, or do it the other way around and set our buffer size to the kAudioUnitProperty_MaximumFramesPerSlice. - */ - status = ((ma_AudioUnitSetProperty_proc)pContext->coreaudio.AudioUnitSetProperty)(pData->audioUnit, kAudioUnitProperty_MaximumFramesPerSlice, kAudioUnitScope_Global, 0, &actualPeriodSizeInFrames, sizeof(actualPeriodSizeInFrames)); - if (status != noErr) { - ((ma_AudioComponentInstanceDispose_proc)pContext->coreaudio.AudioComponentInstanceDispose)(pData->audioUnit); - return ma_result_from_OSStatus(status); - } - - pData->periodSizeInFramesOut = (ma_uint32)actualPeriodSizeInFrames; - - /* We need a buffer list if this is an input device. We render into this in the input callback. */ - if (deviceType == ma_device_type_capture) { - ma_bool32 isInterleaved = (bestFormat.mFormatFlags & kAudioFormatFlagIsNonInterleaved) == 0; - AudioBufferList* pBufferList; - - pBufferList = ma_allocate_AudioBufferList__coreaudio(pData->periodSizeInFramesOut, pData->formatOut, pData->channelsOut, (isInterleaved) ? ma_stream_layout_interleaved : ma_stream_layout_deinterleaved, &pContext->allocationCallbacks); - if (pBufferList == NULL) { - ((ma_AudioComponentInstanceDispose_proc)pContext->coreaudio.AudioComponentInstanceDispose)(pData->audioUnit); - return MA_OUT_OF_MEMORY; - } - - pData->pAudioBufferList = pBufferList; - } - - /* Callbacks. */ - callbackInfo.inputProcRefCon = pDevice_DoNotReference; - if (deviceType == ma_device_type_playback) { - callbackInfo.inputProc = ma_on_output__coreaudio; - status = ((ma_AudioUnitSetProperty_proc)pContext->coreaudio.AudioUnitSetProperty)(pData->audioUnit, kAudioUnitProperty_SetRenderCallback, kAudioUnitScope_Global, 0, &callbackInfo, sizeof(callbackInfo)); - if (status != noErr) { - ((ma_AudioComponentInstanceDispose_proc)pContext->coreaudio.AudioComponentInstanceDispose)(pData->audioUnit); - return ma_result_from_OSStatus(status); - } - } else { - callbackInfo.inputProc = ma_on_input__coreaudio; - status = ((ma_AudioUnitSetProperty_proc)pContext->coreaudio.AudioUnitSetProperty)(pData->audioUnit, kAudioOutputUnitProperty_SetInputCallback, kAudioUnitScope_Global, 0, &callbackInfo, sizeof(callbackInfo)); - if (status != noErr) { - ((ma_AudioComponentInstanceDispose_proc)pContext->coreaudio.AudioComponentInstanceDispose)(pData->audioUnit); - return ma_result_from_OSStatus(status); - } - } - - /* We need to listen for stop events. */ - if (pData->registerStopEvent) { - status = ((ma_AudioUnitAddPropertyListener_proc)pContext->coreaudio.AudioUnitAddPropertyListener)(pData->audioUnit, kAudioOutputUnitProperty_IsRunning, on_start_stop__coreaudio, pDevice_DoNotReference); - if (status != noErr) { - ((ma_AudioComponentInstanceDispose_proc)pContext->coreaudio.AudioComponentInstanceDispose)(pData->audioUnit); - return ma_result_from_OSStatus(status); - } - } - - /* Initialize the audio unit. */ - status = ((ma_AudioUnitInitialize_proc)pContext->coreaudio.AudioUnitInitialize)(pData->audioUnit); - if (status != noErr) { - ma_free(pData->pAudioBufferList, &pContext->allocationCallbacks); - pData->pAudioBufferList = NULL; - ((ma_AudioComponentInstanceDispose_proc)pContext->coreaudio.AudioComponentInstanceDispose)(pData->audioUnit); - return ma_result_from_OSStatus(status); - } - - /* Grab the name. */ -#if defined(MA_APPLE_DESKTOP) - ma_get_AudioObject_name(pContext, deviceObjectID, sizeof(pData->deviceName), pData->deviceName); -#else - if (deviceType == ma_device_type_playback) { - ma_strcpy_s(pData->deviceName, sizeof(pData->deviceName), MA_DEFAULT_PLAYBACK_DEVICE_NAME); - } else { - ma_strcpy_s(pData->deviceName, sizeof(pData->deviceName), MA_DEFAULT_CAPTURE_DEVICE_NAME); - } -#endif - - return result; -} - -#if defined(MA_APPLE_DESKTOP) -static ma_result ma_device_reinit_internal__coreaudio(ma_device* pDevice, ma_device_type deviceType, ma_bool32 disposePreviousAudioUnit) -{ - ma_device_init_internal_data__coreaudio data; - ma_result result; - - /* This should only be called for playback or capture, not duplex. */ - if (deviceType == ma_device_type_duplex) { - return MA_INVALID_ARGS; - } - - data.allowNominalSampleRateChange = MA_FALSE; /* Don't change the nominal sample rate when switching devices. */ - - if (deviceType == ma_device_type_capture) { - data.formatIn = pDevice->capture.format; - data.channelsIn = pDevice->capture.channels; - data.sampleRateIn = pDevice->sampleRate; - MA_COPY_MEMORY(data.channelMapIn, pDevice->capture.channelMap, sizeof(pDevice->capture.channelMap)); - data.shareMode = pDevice->capture.shareMode; - data.performanceProfile = pDevice->coreaudio.originalPerformanceProfile; - data.registerStopEvent = MA_TRUE; - - if (disposePreviousAudioUnit) { - ((ma_AudioOutputUnitStop_proc)pDevice->pContext->coreaudio.AudioOutputUnitStop)((AudioUnit)pDevice->coreaudio.audioUnitCapture); - ((ma_AudioComponentInstanceDispose_proc)pDevice->pContext->coreaudio.AudioComponentInstanceDispose)((AudioUnit)pDevice->coreaudio.audioUnitCapture); - } - if (pDevice->coreaudio.pAudioBufferList) { - ma_free(pDevice->coreaudio.pAudioBufferList, &pDevice->pContext->allocationCallbacks); - } - } else if (deviceType == ma_device_type_playback) { - data.formatIn = pDevice->playback.format; - data.channelsIn = pDevice->playback.channels; - data.sampleRateIn = pDevice->sampleRate; - MA_COPY_MEMORY(data.channelMapIn, pDevice->playback.channelMap, sizeof(pDevice->playback.channelMap)); - data.shareMode = pDevice->playback.shareMode; - data.performanceProfile = pDevice->coreaudio.originalPerformanceProfile; - data.registerStopEvent = (pDevice->type != ma_device_type_duplex); - - if (disposePreviousAudioUnit) { - ((ma_AudioOutputUnitStop_proc)pDevice->pContext->coreaudio.AudioOutputUnitStop)((AudioUnit)pDevice->coreaudio.audioUnitPlayback); - ((ma_AudioComponentInstanceDispose_proc)pDevice->pContext->coreaudio.AudioComponentInstanceDispose)((AudioUnit)pDevice->coreaudio.audioUnitPlayback); - } - } - data.periodSizeInFramesIn = pDevice->coreaudio.originalPeriodSizeInFrames; - data.periodSizeInMillisecondsIn = pDevice->coreaudio.originalPeriodSizeInMilliseconds; - data.periodsIn = pDevice->coreaudio.originalPeriods; - - /* Need at least 3 periods for duplex. */ - if (data.periodsIn < 3 && pDevice->type == ma_device_type_duplex) { - data.periodsIn = 3; - } - - result = ma_device_init_internal__coreaudio(pDevice->pContext, deviceType, NULL, &data, (void*)pDevice); - if (result != MA_SUCCESS) { - return result; - } - - if (deviceType == ma_device_type_capture) { - #if defined(MA_APPLE_DESKTOP) - pDevice->coreaudio.deviceObjectIDCapture = (ma_uint32)data.deviceObjectID; - ma_get_AudioObject_uid(pDevice->pContext, pDevice->coreaudio.deviceObjectIDCapture, sizeof(pDevice->capture.id.coreaudio), pDevice->capture.id.coreaudio); - #endif - pDevice->coreaudio.audioUnitCapture = (ma_ptr)data.audioUnit; - pDevice->coreaudio.pAudioBufferList = (ma_ptr)data.pAudioBufferList; - pDevice->coreaudio.audioBufferCapInFrames = data.periodSizeInFramesOut; - - pDevice->capture.internalFormat = data.formatOut; - pDevice->capture.internalChannels = data.channelsOut; - pDevice->capture.internalSampleRate = data.sampleRateOut; - MA_COPY_MEMORY(pDevice->capture.internalChannelMap, data.channelMapOut, sizeof(data.channelMapOut)); - pDevice->capture.internalPeriodSizeInFrames = data.periodSizeInFramesOut; - pDevice->capture.internalPeriods = data.periodsOut; - } else if (deviceType == ma_device_type_playback) { - #if defined(MA_APPLE_DESKTOP) - pDevice->coreaudio.deviceObjectIDPlayback = (ma_uint32)data.deviceObjectID; - ma_get_AudioObject_uid(pDevice->pContext, pDevice->coreaudio.deviceObjectIDPlayback, sizeof(pDevice->playback.id.coreaudio), pDevice->playback.id.coreaudio); - #endif - pDevice->coreaudio.audioUnitPlayback = (ma_ptr)data.audioUnit; - - pDevice->playback.internalFormat = data.formatOut; - pDevice->playback.internalChannels = data.channelsOut; - pDevice->playback.internalSampleRate = data.sampleRateOut; - MA_COPY_MEMORY(pDevice->playback.internalChannelMap, data.channelMapOut, sizeof(data.channelMapOut)); - pDevice->playback.internalPeriodSizeInFrames = data.periodSizeInFramesOut; - pDevice->playback.internalPeriods = data.periodsOut; - } - - return MA_SUCCESS; -} -#endif /* MA_APPLE_DESKTOP */ - -static ma_result ma_device_init__coreaudio(ma_device* pDevice, const ma_device_config* pConfig, ma_device_descriptor* pDescriptorPlayback, ma_device_descriptor* pDescriptorCapture) -{ - ma_result result; - - MA_ASSERT(pDevice != NULL); - MA_ASSERT(pConfig != NULL); - - if (pConfig->deviceType == ma_device_type_loopback) { - return MA_DEVICE_TYPE_NOT_SUPPORTED; - } - - /* No exclusive mode with the Core Audio backend for now. */ - if (((pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) && pDescriptorCapture->shareMode == ma_share_mode_exclusive) || - ((pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) && pDescriptorPlayback->shareMode == ma_share_mode_exclusive)) { - return MA_SHARE_MODE_NOT_SUPPORTED; - } - - /* Capture needs to be initialized first. */ - if (pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) { - ma_device_init_internal_data__coreaudio data; - data.allowNominalSampleRateChange = pConfig->coreaudio.allowNominalSampleRateChange; - data.formatIn = pDescriptorCapture->format; - data.channelsIn = pDescriptorCapture->channels; - data.sampleRateIn = pDescriptorCapture->sampleRate; - MA_COPY_MEMORY(data.channelMapIn, pDescriptorCapture->channelMap, sizeof(pDescriptorCapture->channelMap)); - data.periodSizeInFramesIn = pDescriptorCapture->periodSizeInFrames; - data.periodSizeInMillisecondsIn = pDescriptorCapture->periodSizeInMilliseconds; - data.periodsIn = pDescriptorCapture->periodCount; - data.shareMode = pDescriptorCapture->shareMode; - data.performanceProfile = pConfig->performanceProfile; - data.registerStopEvent = MA_TRUE; - - /* Need at least 3 periods for duplex. */ - if (data.periodsIn < 3 && pConfig->deviceType == ma_device_type_duplex) { - data.periodsIn = 3; - } - - result = ma_device_init_internal__coreaudio(pDevice->pContext, ma_device_type_capture, pDescriptorCapture->pDeviceID, &data, (void*)pDevice); - if (result != MA_SUCCESS) { - return result; - } - - pDevice->coreaudio.isDefaultCaptureDevice = (pConfig->capture.pDeviceID == NULL); - #if defined(MA_APPLE_DESKTOP) - pDevice->coreaudio.deviceObjectIDCapture = (ma_uint32)data.deviceObjectID; - #endif - pDevice->coreaudio.audioUnitCapture = (ma_ptr)data.audioUnit; - pDevice->coreaudio.pAudioBufferList = (ma_ptr)data.pAudioBufferList; - pDevice->coreaudio.audioBufferCapInFrames = data.periodSizeInFramesOut; - pDevice->coreaudio.originalPeriodSizeInFrames = pDescriptorCapture->periodSizeInFrames; - pDevice->coreaudio.originalPeriodSizeInMilliseconds = pDescriptorCapture->periodSizeInMilliseconds; - pDevice->coreaudio.originalPeriods = pDescriptorCapture->periodCount; - pDevice->coreaudio.originalPerformanceProfile = pConfig->performanceProfile; - - pDescriptorCapture->format = data.formatOut; - pDescriptorCapture->channels = data.channelsOut; - pDescriptorCapture->sampleRate = data.sampleRateOut; - MA_COPY_MEMORY(pDescriptorCapture->channelMap, data.channelMapOut, sizeof(data.channelMapOut)); - pDescriptorCapture->periodSizeInFrames = data.periodSizeInFramesOut; - pDescriptorCapture->periodCount = data.periodsOut; - - #if defined(MA_APPLE_DESKTOP) - ma_get_AudioObject_uid(pDevice->pContext, pDevice->coreaudio.deviceObjectIDCapture, sizeof(pDevice->capture.id.coreaudio), pDevice->capture.id.coreaudio); - - /* - If we are using the default device we'll need to listen for changes to the system's default device so we can seamlessly - switch the device in the background. - */ - if (pConfig->capture.pDeviceID == NULL) { - ma_device__track__coreaudio(pDevice); - } - #endif - } - - /* Playback. */ - if (pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) { - ma_device_init_internal_data__coreaudio data; - data.allowNominalSampleRateChange = pConfig->coreaudio.allowNominalSampleRateChange; - data.formatIn = pDescriptorPlayback->format; - data.channelsIn = pDescriptorPlayback->channels; - data.sampleRateIn = pDescriptorPlayback->sampleRate; - MA_COPY_MEMORY(data.channelMapIn, pDescriptorPlayback->channelMap, sizeof(pDescriptorPlayback->channelMap)); - data.shareMode = pDescriptorPlayback->shareMode; - data.performanceProfile = pConfig->performanceProfile; - - /* In full-duplex mode we want the playback buffer to be the same size as the capture buffer. */ - if (pConfig->deviceType == ma_device_type_duplex) { - data.periodSizeInFramesIn = pDescriptorCapture->periodSizeInFrames; - data.periodsIn = pDescriptorCapture->periodCount; - data.registerStopEvent = MA_FALSE; - } else { - data.periodSizeInFramesIn = pDescriptorPlayback->periodSizeInFrames; - data.periodSizeInMillisecondsIn = pDescriptorPlayback->periodSizeInMilliseconds; - data.periodsIn = pDescriptorPlayback->periodCount; - data.registerStopEvent = MA_TRUE; - } - - result = ma_device_init_internal__coreaudio(pDevice->pContext, ma_device_type_playback, pDescriptorPlayback->pDeviceID, &data, (void*)pDevice); - if (result != MA_SUCCESS) { - if (pConfig->deviceType == ma_device_type_duplex) { - ((ma_AudioComponentInstanceDispose_proc)pDevice->pContext->coreaudio.AudioComponentInstanceDispose)((AudioUnit)pDevice->coreaudio.audioUnitCapture); - if (pDevice->coreaudio.pAudioBufferList) { - ma_free(pDevice->coreaudio.pAudioBufferList, &pDevice->pContext->allocationCallbacks); - } - } - return result; - } - - pDevice->coreaudio.isDefaultPlaybackDevice = (pConfig->playback.pDeviceID == NULL); - #if defined(MA_APPLE_DESKTOP) - pDevice->coreaudio.deviceObjectIDPlayback = (ma_uint32)data.deviceObjectID; - #endif - pDevice->coreaudio.audioUnitPlayback = (ma_ptr)data.audioUnit; - pDevice->coreaudio.originalPeriodSizeInFrames = pDescriptorPlayback->periodSizeInFrames; - pDevice->coreaudio.originalPeriodSizeInMilliseconds = pDescriptorPlayback->periodSizeInMilliseconds; - pDevice->coreaudio.originalPeriods = pDescriptorPlayback->periodCount; - pDevice->coreaudio.originalPerformanceProfile = pConfig->performanceProfile; - - pDescriptorPlayback->format = data.formatOut; - pDescriptorPlayback->channels = data.channelsOut; - pDescriptorPlayback->sampleRate = data.sampleRateOut; - MA_COPY_MEMORY(pDescriptorPlayback->channelMap, data.channelMapOut, sizeof(data.channelMapOut)); - pDescriptorPlayback->periodSizeInFrames = data.periodSizeInFramesOut; - pDescriptorPlayback->periodCount = data.periodsOut; - - #if defined(MA_APPLE_DESKTOP) - ma_get_AudioObject_uid(pDevice->pContext, pDevice->coreaudio.deviceObjectIDPlayback, sizeof(pDevice->playback.id.coreaudio), pDevice->playback.id.coreaudio); - - /* - If we are using the default device we'll need to listen for changes to the system's default device so we can seamlessly - switch the device in the background. - */ - if (pDescriptorPlayback->pDeviceID == NULL && (pConfig->deviceType != ma_device_type_duplex || pDescriptorCapture->pDeviceID != NULL)) { - ma_device__track__coreaudio(pDevice); - } - #endif - } - - - - /* - When stopping the device, a callback is called on another thread. We need to wait for this callback - before returning from ma_device_stop(). This event is used for this. - */ - ma_event_init(&pDevice->coreaudio.stopEvent); - - /* - We need to detect when a route has changed so we can update the data conversion pipeline accordingly. This is done - differently on non-Desktop Apple platforms. - */ -#if defined(MA_APPLE_MOBILE) - pDevice->coreaudio.pNotificationHandler = (MA_BRIDGE_RETAINED void*)[[ma_ios_notification_handler alloc] init:pDevice]; -#endif - - return MA_SUCCESS; -} - - -static ma_result ma_device_start__coreaudio(ma_device* pDevice) -{ - MA_ASSERT(pDevice != NULL); - - if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) { - OSStatus status = ((ma_AudioOutputUnitStart_proc)pDevice->pContext->coreaudio.AudioOutputUnitStart)((AudioUnit)pDevice->coreaudio.audioUnitCapture); - if (status != noErr) { - return ma_result_from_OSStatus(status); - } - } - - if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) { - OSStatus status = ((ma_AudioOutputUnitStart_proc)pDevice->pContext->coreaudio.AudioOutputUnitStart)((AudioUnit)pDevice->coreaudio.audioUnitPlayback); - if (status != noErr) { - if (pDevice->type == ma_device_type_duplex) { - ((ma_AudioOutputUnitStop_proc)pDevice->pContext->coreaudio.AudioOutputUnitStop)((AudioUnit)pDevice->coreaudio.audioUnitCapture); - } - return ma_result_from_OSStatus(status); - } - } - - return MA_SUCCESS; -} - -static ma_result ma_device_stop__coreaudio(ma_device* pDevice) -{ - MA_ASSERT(pDevice != NULL); - - /* It's not clear from the documentation whether or not AudioOutputUnitStop() actually drains the device or not. */ - - if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) { - OSStatus status = ((ma_AudioOutputUnitStop_proc)pDevice->pContext->coreaudio.AudioOutputUnitStop)((AudioUnit)pDevice->coreaudio.audioUnitCapture); - if (status != noErr) { - return ma_result_from_OSStatus(status); - } - } - - if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) { - OSStatus status = ((ma_AudioOutputUnitStop_proc)pDevice->pContext->coreaudio.AudioOutputUnitStop)((AudioUnit)pDevice->coreaudio.audioUnitPlayback); - if (status != noErr) { - return ma_result_from_OSStatus(status); - } - } - - /* We need to wait for the callback to finish before returning. */ - ma_event_wait(&pDevice->coreaudio.stopEvent); - return MA_SUCCESS; -} - - -static ma_result ma_context_uninit__coreaudio(ma_context* pContext) -{ - MA_ASSERT(pContext != NULL); - MA_ASSERT(pContext->backend == ma_backend_coreaudio); - -#if defined(MA_APPLE_MOBILE) - if (!pContext->coreaudio.noAudioSessionDeactivate) { - if (![[AVAudioSession sharedInstance] setActive:false error:nil]) { - ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "Failed to deactivate audio session."); - return MA_FAILED_TO_INIT_BACKEND; - } - } -#endif - -#if !defined(MA_NO_RUNTIME_LINKING) && !defined(MA_APPLE_MOBILE) - ma_dlclose(ma_context_get_log(pContext), pContext->coreaudio.hAudioUnit); - ma_dlclose(ma_context_get_log(pContext), pContext->coreaudio.hCoreAudio); - ma_dlclose(ma_context_get_log(pContext), pContext->coreaudio.hCoreFoundation); -#endif - -#if !defined(MA_APPLE_MOBILE) - ma_context__uninit_device_tracking__coreaudio(pContext); -#endif - - (void)pContext; - return MA_SUCCESS; -} - -#if defined(MA_APPLE_MOBILE) && defined(__IPHONE_12_0) -static AVAudioSessionCategory ma_to_AVAudioSessionCategory(ma_ios_session_category category) -{ - /* The "default" and "none" categories are treated different and should not be used as an input into this function. */ - MA_ASSERT(category != ma_ios_session_category_default); - MA_ASSERT(category != ma_ios_session_category_none); - - switch (category) { - case ma_ios_session_category_ambient: return AVAudioSessionCategoryAmbient; - case ma_ios_session_category_solo_ambient: return AVAudioSessionCategorySoloAmbient; - case ma_ios_session_category_playback: return AVAudioSessionCategoryPlayback; - case ma_ios_session_category_record: return AVAudioSessionCategoryRecord; - case ma_ios_session_category_play_and_record: return AVAudioSessionCategoryPlayAndRecord; - case ma_ios_session_category_multi_route: return AVAudioSessionCategoryMultiRoute; - case ma_ios_session_category_none: return AVAudioSessionCategoryAmbient; - case ma_ios_session_category_default: return AVAudioSessionCategoryAmbient; - default: return AVAudioSessionCategoryAmbient; - } -} -#endif - -static ma_result ma_context_init__coreaudio(ma_context* pContext, const ma_context_config* pConfig, ma_backend_callbacks* pCallbacks) -{ -#if !defined(MA_APPLE_MOBILE) - ma_result result; -#endif - - MA_ASSERT(pConfig != NULL); - MA_ASSERT(pContext != NULL); - -#if defined(MA_APPLE_MOBILE) - @autoreleasepool { - AVAudioSession* pAudioSession = [AVAudioSession sharedInstance]; - AVAudioSessionCategoryOptions options = pConfig->coreaudio.sessionCategoryOptions; - - MA_ASSERT(pAudioSession != NULL); - - if (pConfig->coreaudio.sessionCategory == ma_ios_session_category_default) { - /* - I'm going to use trial and error to determine our default session category. First we'll try PlayAndRecord. If that fails - we'll try Playback and if that fails we'll try record. If all of these fail we'll just not set the category. - */ - #if !defined(MA_APPLE_TV) && !defined(MA_APPLE_WATCH) - options |= AVAudioSessionCategoryOptionDefaultToSpeaker; - #endif - - if ([pAudioSession setCategory: AVAudioSessionCategoryPlayAndRecord withOptions:options error:nil]) { - /* Using PlayAndRecord */ - } else if ([pAudioSession setCategory: AVAudioSessionCategoryPlayback withOptions:options error:nil]) { - /* Using Playback */ - } else if ([pAudioSession setCategory: AVAudioSessionCategoryRecord withOptions:options error:nil]) { - /* Using Record */ - } else { - /* Leave as default? */ - } - } else { - if (pConfig->coreaudio.sessionCategory != ma_ios_session_category_none) { - #if defined(__IPHONE_12_0) - if (![pAudioSession setCategory: ma_to_AVAudioSessionCategory(pConfig->coreaudio.sessionCategory) withOptions:options error:nil]) { - return MA_INVALID_OPERATION; /* Failed to set session category. */ - } - #else - /* Ignore the session category on version 11 and older, but post a warning. */ - ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_WARNING, "Session category only supported in iOS 12 and newer."); - #endif - } - } - - if (!pConfig->coreaudio.noAudioSessionActivate) { - if (![pAudioSession setActive:true error:nil]) { - ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "Failed to activate audio session."); - return MA_FAILED_TO_INIT_BACKEND; - } - } - } -#endif - -#if !defined(MA_NO_RUNTIME_LINKING) && !defined(MA_APPLE_MOBILE) - pContext->coreaudio.hCoreFoundation = ma_dlopen(ma_context_get_log(pContext), "/System/Library/Frameworks/CoreFoundation.framework/CoreFoundation"); - if (pContext->coreaudio.hCoreFoundation == NULL) { - return MA_API_NOT_FOUND; - } - - pContext->coreaudio.CFStringGetCString = ma_dlsym(ma_context_get_log(pContext), pContext->coreaudio.hCoreFoundation, "CFStringGetCString"); - pContext->coreaudio.CFRelease = ma_dlsym(ma_context_get_log(pContext), pContext->coreaudio.hCoreFoundation, "CFRelease"); - - - pContext->coreaudio.hCoreAudio = ma_dlopen(ma_context_get_log(pContext), "/System/Library/Frameworks/CoreAudio.framework/CoreAudio"); - if (pContext->coreaudio.hCoreAudio == NULL) { - ma_dlclose(ma_context_get_log(pContext), pContext->coreaudio.hCoreFoundation); - return MA_API_NOT_FOUND; - } - - pContext->coreaudio.AudioObjectGetPropertyData = ma_dlsym(ma_context_get_log(pContext), pContext->coreaudio.hCoreAudio, "AudioObjectGetPropertyData"); - pContext->coreaudio.AudioObjectGetPropertyDataSize = ma_dlsym(ma_context_get_log(pContext), pContext->coreaudio.hCoreAudio, "AudioObjectGetPropertyDataSize"); - pContext->coreaudio.AudioObjectSetPropertyData = ma_dlsym(ma_context_get_log(pContext), pContext->coreaudio.hCoreAudio, "AudioObjectSetPropertyData"); - pContext->coreaudio.AudioObjectAddPropertyListener = ma_dlsym(ma_context_get_log(pContext), pContext->coreaudio.hCoreAudio, "AudioObjectAddPropertyListener"); - pContext->coreaudio.AudioObjectRemovePropertyListener = ma_dlsym(ma_context_get_log(pContext), pContext->coreaudio.hCoreAudio, "AudioObjectRemovePropertyListener"); - - /* - It looks like Apple has moved some APIs from AudioUnit into AudioToolbox on more recent versions of macOS. They are still - defined in AudioUnit, but just in case they decide to remove them from there entirely I'm going to implement a fallback. - The way it'll work is that it'll first try AudioUnit, and if the required symbols are not present there we'll fall back to - AudioToolbox. - */ - pContext->coreaudio.hAudioUnit = ma_dlopen(ma_context_get_log(pContext), "/System/Library/Frameworks/AudioUnit.framework/AudioUnit"); - if (pContext->coreaudio.hAudioUnit == NULL) { - ma_dlclose(ma_context_get_log(pContext), pContext->coreaudio.hCoreAudio); - ma_dlclose(ma_context_get_log(pContext), pContext->coreaudio.hCoreFoundation); - return MA_API_NOT_FOUND; - } - - if (ma_dlsym(ma_context_get_log(pContext), pContext->coreaudio.hAudioUnit, "AudioComponentFindNext") == NULL) { - /* Couldn't find the required symbols in AudioUnit, so fall back to AudioToolbox. */ - ma_dlclose(ma_context_get_log(pContext), pContext->coreaudio.hAudioUnit); - pContext->coreaudio.hAudioUnit = ma_dlopen(ma_context_get_log(pContext), "/System/Library/Frameworks/AudioToolbox.framework/AudioToolbox"); - if (pContext->coreaudio.hAudioUnit == NULL) { - ma_dlclose(ma_context_get_log(pContext), pContext->coreaudio.hCoreAudio); - ma_dlclose(ma_context_get_log(pContext), pContext->coreaudio.hCoreFoundation); - return MA_API_NOT_FOUND; - } - } - - pContext->coreaudio.AudioComponentFindNext = ma_dlsym(ma_context_get_log(pContext), pContext->coreaudio.hAudioUnit, "AudioComponentFindNext"); - pContext->coreaudio.AudioComponentInstanceDispose = ma_dlsym(ma_context_get_log(pContext), pContext->coreaudio.hAudioUnit, "AudioComponentInstanceDispose"); - pContext->coreaudio.AudioComponentInstanceNew = ma_dlsym(ma_context_get_log(pContext), pContext->coreaudio.hAudioUnit, "AudioComponentInstanceNew"); - pContext->coreaudio.AudioOutputUnitStart = ma_dlsym(ma_context_get_log(pContext), pContext->coreaudio.hAudioUnit, "AudioOutputUnitStart"); - pContext->coreaudio.AudioOutputUnitStop = ma_dlsym(ma_context_get_log(pContext), pContext->coreaudio.hAudioUnit, "AudioOutputUnitStop"); - pContext->coreaudio.AudioUnitAddPropertyListener = ma_dlsym(ma_context_get_log(pContext), pContext->coreaudio.hAudioUnit, "AudioUnitAddPropertyListener"); - pContext->coreaudio.AudioUnitGetPropertyInfo = ma_dlsym(ma_context_get_log(pContext), pContext->coreaudio.hAudioUnit, "AudioUnitGetPropertyInfo"); - pContext->coreaudio.AudioUnitGetProperty = ma_dlsym(ma_context_get_log(pContext), pContext->coreaudio.hAudioUnit, "AudioUnitGetProperty"); - pContext->coreaudio.AudioUnitSetProperty = ma_dlsym(ma_context_get_log(pContext), pContext->coreaudio.hAudioUnit, "AudioUnitSetProperty"); - pContext->coreaudio.AudioUnitInitialize = ma_dlsym(ma_context_get_log(pContext), pContext->coreaudio.hAudioUnit, "AudioUnitInitialize"); - pContext->coreaudio.AudioUnitRender = ma_dlsym(ma_context_get_log(pContext), pContext->coreaudio.hAudioUnit, "AudioUnitRender"); -#else - pContext->coreaudio.CFStringGetCString = (ma_proc)CFStringGetCString; - pContext->coreaudio.CFRelease = (ma_proc)CFRelease; - - #if defined(MA_APPLE_DESKTOP) - pContext->coreaudio.AudioObjectGetPropertyData = (ma_proc)AudioObjectGetPropertyData; - pContext->coreaudio.AudioObjectGetPropertyDataSize = (ma_proc)AudioObjectGetPropertyDataSize; - pContext->coreaudio.AudioObjectSetPropertyData = (ma_proc)AudioObjectSetPropertyData; - pContext->coreaudio.AudioObjectAddPropertyListener = (ma_proc)AudioObjectAddPropertyListener; - pContext->coreaudio.AudioObjectRemovePropertyListener = (ma_proc)AudioObjectRemovePropertyListener; - #endif - - pContext->coreaudio.AudioComponentFindNext = (ma_proc)AudioComponentFindNext; - pContext->coreaudio.AudioComponentInstanceDispose = (ma_proc)AudioComponentInstanceDispose; - pContext->coreaudio.AudioComponentInstanceNew = (ma_proc)AudioComponentInstanceNew; - pContext->coreaudio.AudioOutputUnitStart = (ma_proc)AudioOutputUnitStart; - pContext->coreaudio.AudioOutputUnitStop = (ma_proc)AudioOutputUnitStop; - pContext->coreaudio.AudioUnitAddPropertyListener = (ma_proc)AudioUnitAddPropertyListener; - pContext->coreaudio.AudioUnitGetPropertyInfo = (ma_proc)AudioUnitGetPropertyInfo; - pContext->coreaudio.AudioUnitGetProperty = (ma_proc)AudioUnitGetProperty; - pContext->coreaudio.AudioUnitSetProperty = (ma_proc)AudioUnitSetProperty; - pContext->coreaudio.AudioUnitInitialize = (ma_proc)AudioUnitInitialize; - pContext->coreaudio.AudioUnitRender = (ma_proc)AudioUnitRender; -#endif - - /* Audio component. */ - { - AudioComponentDescription desc; - desc.componentType = kAudioUnitType_Output; - #if defined(MA_APPLE_DESKTOP) - desc.componentSubType = kAudioUnitSubType_HALOutput; - #else - desc.componentSubType = kAudioUnitSubType_RemoteIO; - #endif - desc.componentManufacturer = kAudioUnitManufacturer_Apple; - desc.componentFlags = 0; - desc.componentFlagsMask = 0; - - pContext->coreaudio.component = ((ma_AudioComponentFindNext_proc)pContext->coreaudio.AudioComponentFindNext)(NULL, &desc); - if (pContext->coreaudio.component == NULL) { - #if !defined(MA_NO_RUNTIME_LINKING) && !defined(MA_APPLE_MOBILE) - ma_dlclose(ma_context_get_log(pContext), pContext->coreaudio.hAudioUnit); - ma_dlclose(ma_context_get_log(pContext), pContext->coreaudio.hCoreAudio); - ma_dlclose(ma_context_get_log(pContext), pContext->coreaudio.hCoreFoundation); - #endif - return MA_FAILED_TO_INIT_BACKEND; - } - } - -#if !defined(MA_APPLE_MOBILE) - result = ma_context__init_device_tracking__coreaudio(pContext); - if (result != MA_SUCCESS) { - #if !defined(MA_NO_RUNTIME_LINKING) && !defined(MA_APPLE_MOBILE) - ma_dlclose(ma_context_get_log(pContext), pContext->coreaudio.hAudioUnit); - ma_dlclose(ma_context_get_log(pContext), pContext->coreaudio.hCoreAudio); - ma_dlclose(ma_context_get_log(pContext), pContext->coreaudio.hCoreFoundation); - #endif - return result; - } -#endif - - pContext->coreaudio.noAudioSessionDeactivate = pConfig->coreaudio.noAudioSessionDeactivate; - - pCallbacks->onContextInit = ma_context_init__coreaudio; - pCallbacks->onContextUninit = ma_context_uninit__coreaudio; - pCallbacks->onContextEnumerateDevices = ma_context_enumerate_devices__coreaudio; - pCallbacks->onContextGetDeviceInfo = ma_context_get_device_info__coreaudio; - pCallbacks->onDeviceInit = ma_device_init__coreaudio; - pCallbacks->onDeviceUninit = ma_device_uninit__coreaudio; - pCallbacks->onDeviceStart = ma_device_start__coreaudio; - pCallbacks->onDeviceStop = ma_device_stop__coreaudio; - pCallbacks->onDeviceRead = NULL; - pCallbacks->onDeviceWrite = NULL; - pCallbacks->onDeviceDataLoop = NULL; - - return MA_SUCCESS; -} -#endif /* MA_HAS_COREAUDIO */ - - - -/****************************************************************************** - -sndio Backend - -******************************************************************************/ -#ifdef MA_HAS_SNDIO -#include - -/* -Only supporting OpenBSD. This did not work very well at all on FreeBSD when I tried it. Not sure if this is due -to miniaudio's implementation or if it's some kind of system configuration issue, but basically the default device -just doesn't emit any sound, or at times you'll hear tiny pieces. I will consider enabling this when there's -demand for it or if I can get it tested and debugged more thoroughly. -*/ -#if 0 -#if defined(__NetBSD__) || defined(__OpenBSD__) -#include -#endif -#if defined(__FreeBSD__) || defined(__DragonFly__) -#include -#endif -#endif - -#define MA_SIO_DEVANY "default" -#define MA_SIO_PLAY 1 -#define MA_SIO_REC 2 -#define MA_SIO_NENC 8 -#define MA_SIO_NCHAN 8 -#define MA_SIO_NRATE 16 -#define MA_SIO_NCONF 4 - -struct ma_sio_hdl; /* <-- Opaque */ - -struct ma_sio_par -{ - unsigned int bits; - unsigned int bps; - unsigned int sig; - unsigned int le; - unsigned int msb; - unsigned int rchan; - unsigned int pchan; - unsigned int rate; - unsigned int bufsz; - unsigned int xrun; - unsigned int round; - unsigned int appbufsz; - int __pad[3]; - unsigned int __magic; -}; - -struct ma_sio_enc -{ - unsigned int bits; - unsigned int bps; - unsigned int sig; - unsigned int le; - unsigned int msb; -}; - -struct ma_sio_conf -{ - unsigned int enc; - unsigned int rchan; - unsigned int pchan; - unsigned int rate; -}; - -struct ma_sio_cap -{ - struct ma_sio_enc enc[MA_SIO_NENC]; - unsigned int rchan[MA_SIO_NCHAN]; - unsigned int pchan[MA_SIO_NCHAN]; - unsigned int rate[MA_SIO_NRATE]; - int __pad[7]; - unsigned int nconf; - struct ma_sio_conf confs[MA_SIO_NCONF]; -}; - -typedef struct ma_sio_hdl* (* ma_sio_open_proc) (const char*, unsigned int, int); -typedef void (* ma_sio_close_proc) (struct ma_sio_hdl*); -typedef int (* ma_sio_setpar_proc) (struct ma_sio_hdl*, struct ma_sio_par*); -typedef int (* ma_sio_getpar_proc) (struct ma_sio_hdl*, struct ma_sio_par*); -typedef int (* ma_sio_getcap_proc) (struct ma_sio_hdl*, struct ma_sio_cap*); -typedef size_t (* ma_sio_write_proc) (struct ma_sio_hdl*, const void*, size_t); -typedef size_t (* ma_sio_read_proc) (struct ma_sio_hdl*, void*, size_t); -typedef int (* ma_sio_start_proc) (struct ma_sio_hdl*); -typedef int (* ma_sio_stop_proc) (struct ma_sio_hdl*); -typedef int (* ma_sio_initpar_proc)(struct ma_sio_par*); - -static ma_uint32 ma_get_standard_sample_rate_priority_index__sndio(ma_uint32 sampleRate) /* Lower = higher priority */ -{ - ma_uint32 i; - for (i = 0; i < ma_countof(g_maStandardSampleRatePriorities); ++i) { - if (g_maStandardSampleRatePriorities[i] == sampleRate) { - return i; - } - } - - return (ma_uint32)-1; -} - -static ma_format ma_format_from_sio_enc__sndio(unsigned int bits, unsigned int bps, unsigned int sig, unsigned int le, unsigned int msb) -{ - /* We only support native-endian right now. */ - if ((ma_is_little_endian() && le == 0) || (ma_is_big_endian() && le == 1)) { - return ma_format_unknown; - } - - if (bits == 8 && bps == 1 && sig == 0) { - return ma_format_u8; - } - if (bits == 16 && bps == 2 && sig == 1) { - return ma_format_s16; - } - if (bits == 24 && bps == 3 && sig == 1) { - return ma_format_s24; - } - if (bits == 24 && bps == 4 && sig == 1 && msb == 0) { - /*return ma_format_s24_32;*/ - } - if (bits == 32 && bps == 4 && sig == 1) { - return ma_format_s32; - } - - return ma_format_unknown; -} - -static ma_format ma_find_best_format_from_sio_cap__sndio(struct ma_sio_cap* caps) -{ - ma_format bestFormat; - unsigned int iConfig; - - MA_ASSERT(caps != NULL); - - bestFormat = ma_format_unknown; - for (iConfig = 0; iConfig < caps->nconf; iConfig += 1) { - unsigned int iEncoding; - for (iEncoding = 0; iEncoding < MA_SIO_NENC; iEncoding += 1) { - unsigned int bits; - unsigned int bps; - unsigned int sig; - unsigned int le; - unsigned int msb; - ma_format format; - - if ((caps->confs[iConfig].enc & (1UL << iEncoding)) == 0) { - continue; - } - - bits = caps->enc[iEncoding].bits; - bps = caps->enc[iEncoding].bps; - sig = caps->enc[iEncoding].sig; - le = caps->enc[iEncoding].le; - msb = caps->enc[iEncoding].msb; - format = ma_format_from_sio_enc__sndio(bits, bps, sig, le, msb); - if (format == ma_format_unknown) { - continue; /* Format not supported. */ - } - - if (bestFormat == ma_format_unknown) { - bestFormat = format; - } else { - if (ma_get_format_priority_index(bestFormat) > ma_get_format_priority_index(format)) { /* <-- Lower = better. */ - bestFormat = format; - } - } - } - } - - return bestFormat; -} - -static ma_uint32 ma_find_best_channels_from_sio_cap__sndio(struct ma_sio_cap* caps, ma_device_type deviceType, ma_format requiredFormat) -{ - ma_uint32 maxChannels; - unsigned int iConfig; - - MA_ASSERT(caps != NULL); - MA_ASSERT(requiredFormat != ma_format_unknown); - - /* Just pick whatever configuration has the most channels. */ - maxChannels = 0; - for (iConfig = 0; iConfig < caps->nconf; iConfig += 1) { - /* The encoding should be of requiredFormat. */ - unsigned int iEncoding; - for (iEncoding = 0; iEncoding < MA_SIO_NENC; iEncoding += 1) { - unsigned int iChannel; - unsigned int bits; - unsigned int bps; - unsigned int sig; - unsigned int le; - unsigned int msb; - ma_format format; - - if ((caps->confs[iConfig].enc & (1UL << iEncoding)) == 0) { - continue; - } - - bits = caps->enc[iEncoding].bits; - bps = caps->enc[iEncoding].bps; - sig = caps->enc[iEncoding].sig; - le = caps->enc[iEncoding].le; - msb = caps->enc[iEncoding].msb; - format = ma_format_from_sio_enc__sndio(bits, bps, sig, le, msb); - if (format != requiredFormat) { - continue; - } - - /* Getting here means the format is supported. Iterate over each channel count and grab the biggest one. */ - for (iChannel = 0; iChannel < MA_SIO_NCHAN; iChannel += 1) { - unsigned int chan = 0; - unsigned int channels; - - if (deviceType == ma_device_type_playback) { - chan = caps->confs[iConfig].pchan; - } else { - chan = caps->confs[iConfig].rchan; - } - - if ((chan & (1UL << iChannel)) == 0) { - continue; - } - - if (deviceType == ma_device_type_playback) { - channels = caps->pchan[iChannel]; - } else { - channels = caps->rchan[iChannel]; - } - - if (maxChannels < channels) { - maxChannels = channels; - } - } - } - } - - return maxChannels; -} - -static ma_uint32 ma_find_best_sample_rate_from_sio_cap__sndio(struct ma_sio_cap* caps, ma_device_type deviceType, ma_format requiredFormat, ma_uint32 requiredChannels) -{ - ma_uint32 firstSampleRate; - ma_uint32 bestSampleRate; - unsigned int iConfig; - - MA_ASSERT(caps != NULL); - MA_ASSERT(requiredFormat != ma_format_unknown); - MA_ASSERT(requiredChannels > 0); - MA_ASSERT(requiredChannels <= MA_MAX_CHANNELS); - - firstSampleRate = 0; /* <-- If the device does not support a standard rate we'll fall back to the first one that's found. */ - bestSampleRate = 0; - - for (iConfig = 0; iConfig < caps->nconf; iConfig += 1) { - /* The encoding should be of requiredFormat. */ - unsigned int iEncoding; - for (iEncoding = 0; iEncoding < MA_SIO_NENC; iEncoding += 1) { - unsigned int iChannel; - unsigned int bits; - unsigned int bps; - unsigned int sig; - unsigned int le; - unsigned int msb; - ma_format format; - - if ((caps->confs[iConfig].enc & (1UL << iEncoding)) == 0) { - continue; - } - - bits = caps->enc[iEncoding].bits; - bps = caps->enc[iEncoding].bps; - sig = caps->enc[iEncoding].sig; - le = caps->enc[iEncoding].le; - msb = caps->enc[iEncoding].msb; - format = ma_format_from_sio_enc__sndio(bits, bps, sig, le, msb); - if (format != requiredFormat) { - continue; - } - - /* Getting here means the format is supported. Iterate over each channel count and grab the biggest one. */ - for (iChannel = 0; iChannel < MA_SIO_NCHAN; iChannel += 1) { - unsigned int chan = 0; - unsigned int channels; - unsigned int iRate; - - if (deviceType == ma_device_type_playback) { - chan = caps->confs[iConfig].pchan; - } else { - chan = caps->confs[iConfig].rchan; - } - - if ((chan & (1UL << iChannel)) == 0) { - continue; - } - - if (deviceType == ma_device_type_playback) { - channels = caps->pchan[iChannel]; - } else { - channels = caps->rchan[iChannel]; - } - - if (channels != requiredChannels) { - continue; - } - - /* Getting here means we have found a compatible encoding/channel pair. */ - for (iRate = 0; iRate < MA_SIO_NRATE; iRate += 1) { - ma_uint32 rate = (ma_uint32)caps->rate[iRate]; - ma_uint32 ratePriority; - - if (firstSampleRate == 0) { - firstSampleRate = rate; - } - - /* Disregard this rate if it's not a standard one. */ - ratePriority = ma_get_standard_sample_rate_priority_index__sndio(rate); - if (ratePriority == (ma_uint32)-1) { - continue; - } - - if (ma_get_standard_sample_rate_priority_index__sndio(bestSampleRate) > ratePriority) { /* Lower = better. */ - bestSampleRate = rate; - } - } - } - } - } - - /* If a standard sample rate was not found just fall back to the first one that was iterated. */ - if (bestSampleRate == 0) { - bestSampleRate = firstSampleRate; - } - - return bestSampleRate; -} - - -static ma_result ma_context_enumerate_devices__sndio(ma_context* pContext, ma_enum_devices_callback_proc callback, void* pUserData) -{ - ma_bool32 isTerminating = MA_FALSE; - struct ma_sio_hdl* handle; - - MA_ASSERT(pContext != NULL); - MA_ASSERT(callback != NULL); - - /* sndio doesn't seem to have a good device enumeration API, so I'm therefore only enumerating over default devices for now. */ - - /* Playback. */ - if (!isTerminating) { - handle = ((ma_sio_open_proc)pContext->sndio.sio_open)(MA_SIO_DEVANY, MA_SIO_PLAY, 0); - if (handle != NULL) { - /* Supports playback. */ - ma_device_info deviceInfo; - MA_ZERO_OBJECT(&deviceInfo); - ma_strcpy_s(deviceInfo.id.sndio, sizeof(deviceInfo.id.sndio), MA_SIO_DEVANY); - ma_strcpy_s(deviceInfo.name, sizeof(deviceInfo.name), MA_DEFAULT_PLAYBACK_DEVICE_NAME); - - isTerminating = !callback(pContext, ma_device_type_playback, &deviceInfo, pUserData); - - ((ma_sio_close_proc)pContext->sndio.sio_close)(handle); - } - } - - /* Capture. */ - if (!isTerminating) { - handle = ((ma_sio_open_proc)pContext->sndio.sio_open)(MA_SIO_DEVANY, MA_SIO_REC, 0); - if (handle != NULL) { - /* Supports capture. */ - ma_device_info deviceInfo; - MA_ZERO_OBJECT(&deviceInfo); - ma_strcpy_s(deviceInfo.id.sndio, sizeof(deviceInfo.id.sndio), "default"); - ma_strcpy_s(deviceInfo.name, sizeof(deviceInfo.name), MA_DEFAULT_CAPTURE_DEVICE_NAME); - - isTerminating = !callback(pContext, ma_device_type_capture, &deviceInfo, pUserData); - - ((ma_sio_close_proc)pContext->sndio.sio_close)(handle); - } - } - - return MA_SUCCESS; -} - -static ma_result ma_context_get_device_info__sndio(ma_context* pContext, ma_device_type deviceType, const ma_device_id* pDeviceID, ma_device_info* pDeviceInfo) -{ - char devid[256]; - struct ma_sio_hdl* handle; - struct ma_sio_cap caps; - unsigned int iConfig; - - MA_ASSERT(pContext != NULL); - - /* We need to open the device before we can get information about it. */ - if (pDeviceID == NULL) { - ma_strcpy_s(devid, sizeof(devid), MA_SIO_DEVANY); - ma_strcpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), (deviceType == ma_device_type_playback) ? MA_DEFAULT_PLAYBACK_DEVICE_NAME : MA_DEFAULT_CAPTURE_DEVICE_NAME); - } else { - ma_strcpy_s(devid, sizeof(devid), pDeviceID->sndio); - ma_strcpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), devid); - } - - handle = ((ma_sio_open_proc)pContext->sndio.sio_open)(devid, (deviceType == ma_device_type_playback) ? MA_SIO_PLAY : MA_SIO_REC, 0); - if (handle == NULL) { - return MA_NO_DEVICE; - } - - if (((ma_sio_getcap_proc)pContext->sndio.sio_getcap)(handle, &caps) == 0) { - return MA_ERROR; - } - - pDeviceInfo->nativeDataFormatCount = 0; - - for (iConfig = 0; iConfig < caps.nconf; iConfig += 1) { - /* - The main thing we care about is that the encoding is supported by miniaudio. If it is, we want to give - preference to some formats over others. - */ - unsigned int iEncoding; - unsigned int iChannel; - unsigned int iRate; - - for (iEncoding = 0; iEncoding < MA_SIO_NENC; iEncoding += 1) { - unsigned int bits; - unsigned int bps; - unsigned int sig; - unsigned int le; - unsigned int msb; - ma_format format; - - if ((caps.confs[iConfig].enc & (1UL << iEncoding)) == 0) { - continue; - } - - bits = caps.enc[iEncoding].bits; - bps = caps.enc[iEncoding].bps; - sig = caps.enc[iEncoding].sig; - le = caps.enc[iEncoding].le; - msb = caps.enc[iEncoding].msb; - format = ma_format_from_sio_enc__sndio(bits, bps, sig, le, msb); - if (format == ma_format_unknown) { - continue; /* Format not supported. */ - } - - - /* Channels. */ - for (iChannel = 0; iChannel < MA_SIO_NCHAN; iChannel += 1) { - unsigned int chan = 0; - unsigned int channels; - - if (deviceType == ma_device_type_playback) { - chan = caps.confs[iConfig].pchan; - } else { - chan = caps.confs[iConfig].rchan; - } - - if ((chan & (1UL << iChannel)) == 0) { - continue; - } - - if (deviceType == ma_device_type_playback) { - channels = caps.pchan[iChannel]; - } else { - channels = caps.rchan[iChannel]; - } - - - /* Sample Rates. */ - for (iRate = 0; iRate < MA_SIO_NRATE; iRate += 1) { - if ((caps.confs[iConfig].rate & (1UL << iRate)) != 0) { - ma_device_info_add_native_data_format(pDeviceInfo, format, channels, caps.rate[iRate], 0); - } - } - } - } - } - - ((ma_sio_close_proc)pContext->sndio.sio_close)(handle); - return MA_SUCCESS; -} - -static ma_result ma_device_uninit__sndio(ma_device* pDevice) -{ - MA_ASSERT(pDevice != NULL); - - if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) { - ((ma_sio_close_proc)pDevice->pContext->sndio.sio_close)((struct ma_sio_hdl*)pDevice->sndio.handleCapture); - } - - if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) { - ((ma_sio_close_proc)pDevice->pContext->sndio.sio_close)((struct ma_sio_hdl*)pDevice->sndio.handlePlayback); - } - - return MA_SUCCESS; -} - -static ma_result ma_device_init_handle__sndio(ma_device* pDevice, const ma_device_config* pConfig, ma_device_descriptor* pDescriptor, ma_device_type deviceType) -{ - const char* pDeviceName; - ma_ptr handle; - int openFlags = 0; - struct ma_sio_cap caps; - struct ma_sio_par par; - const ma_device_id* pDeviceID; - ma_format format; - ma_uint32 channels; - ma_uint32 sampleRate; - ma_format internalFormat; - ma_uint32 internalChannels; - ma_uint32 internalSampleRate; - ma_uint32 internalPeriodSizeInFrames; - ma_uint32 internalPeriods; - - MA_ASSERT(pConfig != NULL); - MA_ASSERT(deviceType != ma_device_type_duplex); - MA_ASSERT(pDevice != NULL); - - if (deviceType == ma_device_type_capture) { - openFlags = MA_SIO_REC; - } else { - openFlags = MA_SIO_PLAY; - } - - pDeviceID = pDescriptor->pDeviceID; - format = pDescriptor->format; - channels = pDescriptor->channels; - sampleRate = pDescriptor->sampleRate; - - pDeviceName = MA_SIO_DEVANY; - if (pDeviceID != NULL) { - pDeviceName = pDeviceID->sndio; - } - - handle = (ma_ptr)((ma_sio_open_proc)pDevice->pContext->sndio.sio_open)(pDeviceName, openFlags, 0); - if (handle == NULL) { - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[sndio] Failed to open device."); - return MA_FAILED_TO_OPEN_BACKEND_DEVICE; - } - - /* We need to retrieve the device caps to determine the most appropriate format to use. */ - if (((ma_sio_getcap_proc)pDevice->pContext->sndio.sio_getcap)((struct ma_sio_hdl*)handle, &caps) == 0) { - ((ma_sio_close_proc)pDevice->pContext->sndio.sio_close)((struct ma_sio_hdl*)handle); - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[sndio] Failed to retrieve device caps."); - return MA_ERROR; - } - - /* - Note: sndio reports a huge range of available channels. This is inconvenient for us because there's no real - way, as far as I can tell, to get the _actual_ channel count of the device. I'm therefore restricting this - to the requested channels, regardless of whether or not the default channel count is requested. - - For hardware devices, I'm suspecting only a single channel count will be reported and we can safely use the - value returned by ma_find_best_channels_from_sio_cap__sndio(). - */ - if (deviceType == ma_device_type_capture) { - if (format == ma_format_unknown) { - format = ma_find_best_format_from_sio_cap__sndio(&caps); - } - - if (channels == 0) { - if (strlen(pDeviceName) > strlen("rsnd/") && strncmp(pDeviceName, "rsnd/", strlen("rsnd/")) == 0) { - channels = ma_find_best_channels_from_sio_cap__sndio(&caps, deviceType, format); - } else { - channels = MA_DEFAULT_CHANNELS; - } - } - } else { - if (format == ma_format_unknown) { - format = ma_find_best_format_from_sio_cap__sndio(&caps); - } - - if (channels == 0) { - if (strlen(pDeviceName) > strlen("rsnd/") && strncmp(pDeviceName, "rsnd/", strlen("rsnd/")) == 0) { - channels = ma_find_best_channels_from_sio_cap__sndio(&caps, deviceType, format); - } else { - channels = MA_DEFAULT_CHANNELS; - } - } - } - - if (sampleRate == 0) { - sampleRate = ma_find_best_sample_rate_from_sio_cap__sndio(&caps, pConfig->deviceType, format, channels); - } - - - ((ma_sio_initpar_proc)pDevice->pContext->sndio.sio_initpar)(&par); - par.msb = 0; - par.le = ma_is_little_endian(); - - switch (format) { - case ma_format_u8: - { - par.bits = 8; - par.bps = 1; - par.sig = 0; - } break; - - case ma_format_s24: - { - par.bits = 24; - par.bps = 3; - par.sig = 1; - } break; - - case ma_format_s32: - { - par.bits = 32; - par.bps = 4; - par.sig = 1; - } break; - - case ma_format_s16: - case ma_format_f32: - case ma_format_unknown: - default: - { - par.bits = 16; - par.bps = 2; - par.sig = 1; - } break; - } - - if (deviceType == ma_device_type_capture) { - par.rchan = channels; - } else { - par.pchan = channels; - } - - par.rate = sampleRate; - - internalPeriodSizeInFrames = ma_calculate_buffer_size_in_frames_from_descriptor(pDescriptor, par.rate, pConfig->performanceProfile); - - par.round = internalPeriodSizeInFrames; - par.appbufsz = par.round * pDescriptor->periodCount; - - if (((ma_sio_setpar_proc)pDevice->pContext->sndio.sio_setpar)((struct ma_sio_hdl*)handle, &par) == 0) { - ((ma_sio_close_proc)pDevice->pContext->sndio.sio_close)((struct ma_sio_hdl*)handle); - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[sndio] Failed to set buffer size."); - return MA_ERROR; - } - - if (((ma_sio_getpar_proc)pDevice->pContext->sndio.sio_getpar)((struct ma_sio_hdl*)handle, &par) == 0) { - ((ma_sio_close_proc)pDevice->pContext->sndio.sio_close)((struct ma_sio_hdl*)handle); - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[sndio] Failed to retrieve buffer size."); - return MA_ERROR; - } - - internalFormat = ma_format_from_sio_enc__sndio(par.bits, par.bps, par.sig, par.le, par.msb); - internalChannels = (deviceType == ma_device_type_capture) ? par.rchan : par.pchan; - internalSampleRate = par.rate; - internalPeriods = par.appbufsz / par.round; - internalPeriodSizeInFrames = par.round; - - if (deviceType == ma_device_type_capture) { - pDevice->sndio.handleCapture = handle; - } else { - pDevice->sndio.handlePlayback = handle; - } - - pDescriptor->format = internalFormat; - pDescriptor->channels = internalChannels; - pDescriptor->sampleRate = internalSampleRate; - ma_channel_map_init_standard(ma_standard_channel_map_sndio, pDescriptor->channelMap, ma_countof(pDescriptor->channelMap), internalChannels); - pDescriptor->periodSizeInFrames = internalPeriodSizeInFrames; - pDescriptor->periodCount = internalPeriods; - - return MA_SUCCESS; -} - -static ma_result ma_device_init__sndio(ma_device* pDevice, const ma_device_config* pConfig, ma_device_descriptor* pDescriptorPlayback, ma_device_descriptor* pDescriptorCapture) -{ - MA_ASSERT(pDevice != NULL); - - MA_ZERO_OBJECT(&pDevice->sndio); - - if (pConfig->deviceType == ma_device_type_loopback) { - return MA_DEVICE_TYPE_NOT_SUPPORTED; - } - - if (pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) { - ma_result result = ma_device_init_handle__sndio(pDevice, pConfig, pDescriptorCapture, ma_device_type_capture); - if (result != MA_SUCCESS) { - return result; - } - } - - if (pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) { - ma_result result = ma_device_init_handle__sndio(pDevice, pConfig, pDescriptorPlayback, ma_device_type_playback); - if (result != MA_SUCCESS) { - return result; - } - } - - return MA_SUCCESS; -} - -static ma_result ma_device_start__sndio(ma_device* pDevice) -{ - MA_ASSERT(pDevice != NULL); - - if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) { - ((ma_sio_start_proc)pDevice->pContext->sndio.sio_start)((struct ma_sio_hdl*)pDevice->sndio.handleCapture); - } - - if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) { - ((ma_sio_start_proc)pDevice->pContext->sndio.sio_start)((struct ma_sio_hdl*)pDevice->sndio.handlePlayback); /* <-- Doesn't actually playback until data is written. */ - } - - return MA_SUCCESS; -} - -static ma_result ma_device_stop__sndio(ma_device* pDevice) -{ - MA_ASSERT(pDevice != NULL); - - /* - From the documentation: - - The sio_stop() function puts the audio subsystem in the same state as before sio_start() is called. It stops recording, drains the play buffer and then - stops playback. If samples to play are queued but playback hasn't started yet then playback is forced immediately; playback will actually stop once the - buffer is drained. In no case are samples in the play buffer discarded. - - Therefore, sio_stop() performs all of the necessary draining for us. - */ - - if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) { - ((ma_sio_stop_proc)pDevice->pContext->sndio.sio_stop)((struct ma_sio_hdl*)pDevice->sndio.handleCapture); - } - - if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) { - ((ma_sio_stop_proc)pDevice->pContext->sndio.sio_stop)((struct ma_sio_hdl*)pDevice->sndio.handlePlayback); - } - - return MA_SUCCESS; -} - -static ma_result ma_device_write__sndio(ma_device* pDevice, const void* pPCMFrames, ma_uint32 frameCount, ma_uint32* pFramesWritten) -{ - int result; - - if (pFramesWritten != NULL) { - *pFramesWritten = 0; - } - - result = ((ma_sio_write_proc)pDevice->pContext->sndio.sio_write)((struct ma_sio_hdl*)pDevice->sndio.handlePlayback, pPCMFrames, frameCount * ma_get_bytes_per_frame(pDevice->playback.internalFormat, pDevice->playback.internalChannels)); - if (result == 0) { - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[sndio] Failed to send data from the client to the device."); - return MA_IO_ERROR; - } - - if (pFramesWritten != NULL) { - *pFramesWritten = frameCount; - } - - return MA_SUCCESS; -} - -static ma_result ma_device_read__sndio(ma_device* pDevice, void* pPCMFrames, ma_uint32 frameCount, ma_uint32* pFramesRead) -{ - int result; - - if (pFramesRead != NULL) { - *pFramesRead = 0; - } - - result = ((ma_sio_read_proc)pDevice->pContext->sndio.sio_read)((struct ma_sio_hdl*)pDevice->sndio.handleCapture, pPCMFrames, frameCount * ma_get_bytes_per_frame(pDevice->capture.internalFormat, pDevice->capture.internalChannels)); - if (result == 0) { - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[sndio] Failed to read data from the device to be sent to the device."); - return MA_IO_ERROR; - } - - if (pFramesRead != NULL) { - *pFramesRead = frameCount; - } - - return MA_SUCCESS; -} - -static ma_result ma_context_uninit__sndio(ma_context* pContext) -{ - MA_ASSERT(pContext != NULL); - MA_ASSERT(pContext->backend == ma_backend_sndio); - - (void)pContext; - return MA_SUCCESS; -} - -static ma_result ma_context_init__sndio(ma_context* pContext, const ma_context_config* pConfig, ma_backend_callbacks* pCallbacks) -{ -#ifndef MA_NO_RUNTIME_LINKING - const char* libsndioNames[] = { - "libsndio.so" - }; - size_t i; - - for (i = 0; i < ma_countof(libsndioNames); ++i) { - pContext->sndio.sndioSO = ma_dlopen(ma_context_get_log(pContext), libsndioNames[i]); - if (pContext->sndio.sndioSO != NULL) { - break; - } - } - - if (pContext->sndio.sndioSO == NULL) { - return MA_NO_BACKEND; - } - - pContext->sndio.sio_open = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->sndio.sndioSO, "sio_open"); - pContext->sndio.sio_close = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->sndio.sndioSO, "sio_close"); - pContext->sndio.sio_setpar = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->sndio.sndioSO, "sio_setpar"); - pContext->sndio.sio_getpar = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->sndio.sndioSO, "sio_getpar"); - pContext->sndio.sio_getcap = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->sndio.sndioSO, "sio_getcap"); - pContext->sndio.sio_write = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->sndio.sndioSO, "sio_write"); - pContext->sndio.sio_read = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->sndio.sndioSO, "sio_read"); - pContext->sndio.sio_start = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->sndio.sndioSO, "sio_start"); - pContext->sndio.sio_stop = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->sndio.sndioSO, "sio_stop"); - pContext->sndio.sio_initpar = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->sndio.sndioSO, "sio_initpar"); -#else - pContext->sndio.sio_open = sio_open; - pContext->sndio.sio_close = sio_close; - pContext->sndio.sio_setpar = sio_setpar; - pContext->sndio.sio_getpar = sio_getpar; - pContext->sndio.sio_getcap = sio_getcap; - pContext->sndio.sio_write = sio_write; - pContext->sndio.sio_read = sio_read; - pContext->sndio.sio_start = sio_start; - pContext->sndio.sio_stop = sio_stop; - pContext->sndio.sio_initpar = sio_initpar; -#endif - - pCallbacks->onContextInit = ma_context_init__sndio; - pCallbacks->onContextUninit = ma_context_uninit__sndio; - pCallbacks->onContextEnumerateDevices = ma_context_enumerate_devices__sndio; - pCallbacks->onContextGetDeviceInfo = ma_context_get_device_info__sndio; - pCallbacks->onDeviceInit = ma_device_init__sndio; - pCallbacks->onDeviceUninit = ma_device_uninit__sndio; - pCallbacks->onDeviceStart = ma_device_start__sndio; - pCallbacks->onDeviceStop = ma_device_stop__sndio; - pCallbacks->onDeviceRead = ma_device_read__sndio; - pCallbacks->onDeviceWrite = ma_device_write__sndio; - pCallbacks->onDeviceDataLoop = NULL; - - (void)pConfig; - return MA_SUCCESS; -} -#endif /* MA_HAS_SNDIO */ - - - -/****************************************************************************** - -audio(4) Backend - -******************************************************************************/ -#ifdef MA_HAS_AUDIO4 -#include -#include -#include -#include -#include -#include -#include - -#ifdef __NetBSD__ -#include -#endif - -#if defined(__OpenBSD__) - #include - #if defined(OpenBSD) && OpenBSD >= 201709 - #define MA_AUDIO4_USE_NEW_API - #endif -#endif - -static void ma_construct_device_id__audio4(char* id, size_t idSize, const char* base, int deviceIndex) -{ - size_t baseLen; - - MA_ASSERT(id != NULL); - MA_ASSERT(idSize > 0); - MA_ASSERT(deviceIndex >= 0); - - baseLen = strlen(base); - MA_ASSERT(idSize > baseLen); - - ma_strcpy_s(id, idSize, base); - ma_itoa_s(deviceIndex, id+baseLen, idSize-baseLen, 10); -} - -static ma_result ma_extract_device_index_from_id__audio4(const char* id, const char* base, int* pIndexOut) -{ - size_t idLen; - size_t baseLen; - const char* deviceIndexStr; - - MA_ASSERT(id != NULL); - MA_ASSERT(base != NULL); - MA_ASSERT(pIndexOut != NULL); - - idLen = strlen(id); - baseLen = strlen(base); - if (idLen <= baseLen) { - return MA_ERROR; /* Doesn't look like the id starts with the base. */ - } - - if (strncmp(id, base, baseLen) != 0) { - return MA_ERROR; /* ID does not begin with base. */ - } - - deviceIndexStr = id + baseLen; - if (deviceIndexStr[0] == '\0') { - return MA_ERROR; /* No index specified in the ID. */ - } - - if (pIndexOut) { - *pIndexOut = atoi(deviceIndexStr); - } - - return MA_SUCCESS; -} - - -#if !defined(MA_AUDIO4_USE_NEW_API) /* Old API */ -static ma_format ma_format_from_encoding__audio4(unsigned int encoding, unsigned int precision) -{ - if (precision == 8 && (encoding == AUDIO_ENCODING_ULINEAR || encoding == AUDIO_ENCODING_ULINEAR || encoding == AUDIO_ENCODING_ULINEAR_LE || encoding == AUDIO_ENCODING_ULINEAR_BE)) { - return ma_format_u8; - } else { - if (ma_is_little_endian() && encoding == AUDIO_ENCODING_SLINEAR_LE) { - if (precision == 16) { - return ma_format_s16; - } else if (precision == 24) { - return ma_format_s24; - } else if (precision == 32) { - return ma_format_s32; - } - } else if (ma_is_big_endian() && encoding == AUDIO_ENCODING_SLINEAR_BE) { - if (precision == 16) { - return ma_format_s16; - } else if (precision == 24) { - return ma_format_s24; - } else if (precision == 32) { - return ma_format_s32; - } - } - } - - return ma_format_unknown; /* Encoding not supported. */ -} - -static void ma_encoding_from_format__audio4(ma_format format, unsigned int* pEncoding, unsigned int* pPrecision) -{ - MA_ASSERT(pEncoding != NULL); - MA_ASSERT(pPrecision != NULL); - - switch (format) - { - case ma_format_u8: - { - *pEncoding = AUDIO_ENCODING_ULINEAR; - *pPrecision = 8; - } break; - - case ma_format_s24: - { - *pEncoding = (ma_is_little_endian()) ? AUDIO_ENCODING_SLINEAR_LE : AUDIO_ENCODING_SLINEAR_BE; - *pPrecision = 24; - } break; - - case ma_format_s32: - { - *pEncoding = (ma_is_little_endian()) ? AUDIO_ENCODING_SLINEAR_LE : AUDIO_ENCODING_SLINEAR_BE; - *pPrecision = 32; - } break; - - case ma_format_s16: - case ma_format_f32: - case ma_format_unknown: - default: - { - *pEncoding = (ma_is_little_endian()) ? AUDIO_ENCODING_SLINEAR_LE : AUDIO_ENCODING_SLINEAR_BE; - *pPrecision = 16; - } break; - } -} - -static ma_format ma_format_from_prinfo__audio4(struct audio_prinfo* prinfo) -{ - return ma_format_from_encoding__audio4(prinfo->encoding, prinfo->precision); -} - -static ma_format ma_best_format_from_fd__audio4(int fd, ma_format preferredFormat) -{ - audio_encoding_t encoding; - ma_uint32 iFormat; - int counter = 0; - - /* First check to see if the preferred format is supported. */ - if (preferredFormat != ma_format_unknown) { - counter = 0; - for (;;) { - MA_ZERO_OBJECT(&encoding); - encoding.index = counter; - if (ioctl(fd, AUDIO_GETENC, &encoding) < 0) { - break; - } - - if (preferredFormat == ma_format_from_encoding__audio4(encoding.encoding, encoding.precision)) { - return preferredFormat; /* Found the preferred format. */ - } - - /* Getting here means this encoding does not match our preferred format so we need to more on to the next encoding. */ - counter += 1; - } - } - - /* Getting here means our preferred format is not supported, so fall back to our standard priorities. */ - for (iFormat = 0; iFormat < ma_countof(g_maFormatPriorities); iFormat += 1) { - ma_format format = g_maFormatPriorities[iFormat]; - - counter = 0; - for (;;) { - MA_ZERO_OBJECT(&encoding); - encoding.index = counter; - if (ioctl(fd, AUDIO_GETENC, &encoding) < 0) { - break; - } - - if (format == ma_format_from_encoding__audio4(encoding.encoding, encoding.precision)) { - return format; /* Found a workable format. */ - } - - /* Getting here means this encoding does not match our preferred format so we need to more on to the next encoding. */ - counter += 1; - } - } - - /* Getting here means not appropriate format was found. */ - return ma_format_unknown; -} -#else -static ma_format ma_format_from_swpar__audio4(struct audio_swpar* par) -{ - if (par->bits == 8 && par->bps == 1 && par->sig == 0) { - return ma_format_u8; - } - if (par->bits == 16 && par->bps == 2 && par->sig == 1 && par->le == ma_is_little_endian()) { - return ma_format_s16; - } - if (par->bits == 24 && par->bps == 3 && par->sig == 1 && par->le == ma_is_little_endian()) { - return ma_format_s24; - } - if (par->bits == 32 && par->bps == 4 && par->sig == 1 && par->le == ma_is_little_endian()) { - return ma_format_f32; - } - - /* Format not supported. */ - return ma_format_unknown; -} -#endif - -static ma_result ma_context_get_device_info_from_fd__audio4(ma_context* pContext, ma_device_type deviceType, int fd, ma_device_info* pDeviceInfo) -{ - audio_device_t fdDevice; - - MA_ASSERT(pContext != NULL); - MA_ASSERT(fd >= 0); - MA_ASSERT(pDeviceInfo != NULL); - - (void)pContext; - (void)deviceType; - - if (ioctl(fd, AUDIO_GETDEV, &fdDevice) < 0) { - return MA_ERROR; /* Failed to retrieve device info. */ - } - - /* Name. */ - ma_strcpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), fdDevice.name); - - #if !defined(MA_AUDIO4_USE_NEW_API) - { - audio_info_t fdInfo; - int counter = 0; - ma_uint32 channels; - ma_uint32 sampleRate; - -#if defined(__NetBSD__) && (__NetBSD_Version__ >= 900000000) - if (ioctl(fd, AUDIO_GETFORMAT, &fdInfo) < 0) { - return MA_ERROR; - } -#else - if (ioctl(fd, AUDIO_GETINFO, &fdInfo) < 0) { - return MA_ERROR; - } -#endif - - if (deviceType == ma_device_type_playback) { - channels = fdInfo.play.channels; - sampleRate = fdInfo.play.sample_rate; - } else { - channels = fdInfo.record.channels; - sampleRate = fdInfo.record.sample_rate; - } - - /* Supported formats. We get this by looking at the encodings. */ - pDeviceInfo->nativeDataFormatCount = 0; - for (;;) { - audio_encoding_t encoding; - ma_format format; - - MA_ZERO_OBJECT(&encoding); - encoding.index = counter; - if (ioctl(fd, AUDIO_GETENC, &encoding) < 0) { - break; - } - - format = ma_format_from_encoding__audio4(encoding.encoding, encoding.precision); - if (format != ma_format_unknown) { - ma_device_info_add_native_data_format(pDeviceInfo, format, channels, sampleRate, 0); - } - - counter += 1; - } - } - #else - { - struct audio_swpar fdPar; - ma_format format; - ma_uint32 channels; - ma_uint32 sampleRate; - - if (ioctl(fd, AUDIO_GETPAR, &fdPar) < 0) { - return MA_ERROR; - } - - format = ma_format_from_swpar__audio4(&fdPar); - if (format == ma_format_unknown) { - return MA_FORMAT_NOT_SUPPORTED; - } - - if (deviceType == ma_device_type_playback) { - channels = fdPar.pchan; - } else { - channels = fdPar.rchan; - } - - sampleRate = fdPar.rate; - - pDeviceInfo->nativeDataFormatCount = 0; - ma_device_info_add_native_data_format(pDeviceInfo, format, channels, sampleRate, 0); - } - #endif - - return MA_SUCCESS; -} - -static ma_result ma_context_enumerate_devices__audio4(ma_context* pContext, ma_enum_devices_callback_proc callback, void* pUserData) -{ - const int maxDevices = 64; - char devpath[256]; - int iDevice; - - MA_ASSERT(pContext != NULL); - MA_ASSERT(callback != NULL); - - /* - Every device will be named "/dev/audioN", with a "/dev/audioctlN" equivalent. We use the "/dev/audioctlN" - version here since we can open it even when another process has control of the "/dev/audioN" device. - */ - for (iDevice = 0; iDevice < maxDevices; ++iDevice) { - struct stat st; - int fd; - ma_bool32 isTerminating = MA_FALSE; - - ma_strcpy_s(devpath, sizeof(devpath), "/dev/audioctl"); - ma_itoa_s(iDevice, devpath+strlen(devpath), sizeof(devpath)-strlen(devpath), 10); - - if (stat(devpath, &st) < 0) { - break; - } - - /* The device exists, but we need to check if it's usable as playback and/or capture. */ - - /* Playback. */ - if (!isTerminating) { - fd = open(devpath, O_RDONLY, 0); - if (fd >= 0) { - /* Supports playback. */ - ma_device_info deviceInfo; - MA_ZERO_OBJECT(&deviceInfo); - ma_construct_device_id__audio4(deviceInfo.id.audio4, sizeof(deviceInfo.id.audio4), "/dev/audio", iDevice); - if (ma_context_get_device_info_from_fd__audio4(pContext, ma_device_type_playback, fd, &deviceInfo) == MA_SUCCESS) { - isTerminating = !callback(pContext, ma_device_type_playback, &deviceInfo, pUserData); - } - - close(fd); - } - } - - /* Capture. */ - if (!isTerminating) { - fd = open(devpath, O_WRONLY, 0); - if (fd >= 0) { - /* Supports capture. */ - ma_device_info deviceInfo; - MA_ZERO_OBJECT(&deviceInfo); - ma_construct_device_id__audio4(deviceInfo.id.audio4, sizeof(deviceInfo.id.audio4), "/dev/audio", iDevice); - if (ma_context_get_device_info_from_fd__audio4(pContext, ma_device_type_capture, fd, &deviceInfo) == MA_SUCCESS) { - isTerminating = !callback(pContext, ma_device_type_capture, &deviceInfo, pUserData); - } - - close(fd); - } - } - - if (isTerminating) { - break; - } - } - - return MA_SUCCESS; -} - -static ma_result ma_context_get_device_info__audio4(ma_context* pContext, ma_device_type deviceType, const ma_device_id* pDeviceID, ma_device_info* pDeviceInfo) -{ - int fd = -1; - int deviceIndex = -1; - char ctlid[256]; - ma_result result; - - MA_ASSERT(pContext != NULL); - - /* - We need to open the "/dev/audioctlN" device to get the info. To do this we need to extract the number - from the device ID which will be in "/dev/audioN" format. - */ - if (pDeviceID == NULL) { - /* Default device. */ - ma_strcpy_s(ctlid, sizeof(ctlid), "/dev/audioctl"); - } else { - /* Specific device. We need to convert from "/dev/audioN" to "/dev/audioctlN". */ - result = ma_extract_device_index_from_id__audio4(pDeviceID->audio4, "/dev/audio", &deviceIndex); - if (result != MA_SUCCESS) { - return result; - } - - ma_construct_device_id__audio4(ctlid, sizeof(ctlid), "/dev/audioctl", deviceIndex); - } - - fd = open(ctlid, (deviceType == ma_device_type_playback) ? O_WRONLY : O_RDONLY, 0); - if (fd == -1) { - return MA_NO_DEVICE; - } - - if (deviceIndex == -1) { - ma_strcpy_s(pDeviceInfo->id.audio4, sizeof(pDeviceInfo->id.audio4), "/dev/audio"); - } else { - ma_construct_device_id__audio4(pDeviceInfo->id.audio4, sizeof(pDeviceInfo->id.audio4), "/dev/audio", deviceIndex); - } - - result = ma_context_get_device_info_from_fd__audio4(pContext, deviceType, fd, pDeviceInfo); - - close(fd); - return result; -} - -static ma_result ma_device_uninit__audio4(ma_device* pDevice) -{ - MA_ASSERT(pDevice != NULL); - - if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) { - close(pDevice->audio4.fdCapture); - } - - if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) { - close(pDevice->audio4.fdPlayback); - } - - return MA_SUCCESS; -} - -static ma_result ma_device_init_fd__audio4(ma_device* pDevice, const ma_device_config* pConfig, ma_device_descriptor* pDescriptor, ma_device_type deviceType) -{ - const char* pDefaultDeviceNames[] = { - "/dev/audio", - "/dev/audio0" - }; - const char* pDefaultDeviceCtlNames[] = { - "/dev/audioctl", - "/dev/audioctl0" - }; - int fd; - int fdFlags = 0; - size_t iDefaultDevice = (size_t)-1; - ma_format internalFormat; - ma_uint32 internalChannels; - ma_uint32 internalSampleRate; - ma_uint32 internalPeriodSizeInFrames; - ma_uint32 internalPeriods; - - MA_ASSERT(pConfig != NULL); - MA_ASSERT(deviceType != ma_device_type_duplex); - MA_ASSERT(pDevice != NULL); - - /* The first thing to do is open the file. */ - if (deviceType == ma_device_type_capture) { - fdFlags = O_RDONLY; - } else { - fdFlags = O_WRONLY; - } - /*fdFlags |= O_NONBLOCK;*/ - - /* Find the index of the default device as a start. We'll use this index later. Set it to (size_t)-1 otherwise. */ - if (pDescriptor->pDeviceID == NULL) { - /* Default device. */ - for (iDefaultDevice = 0; iDefaultDevice < ma_countof(pDefaultDeviceNames); ++iDefaultDevice) { - fd = open(pDefaultDeviceNames[iDefaultDevice], fdFlags, 0); - if (fd != -1) { - break; - } - } - } else { - /* Specific device. */ - fd = open(pDescriptor->pDeviceID->audio4, fdFlags, 0); - - for (iDefaultDevice = 0; iDefaultDevice < ma_countof(pDefaultDeviceNames); iDefaultDevice += 1) { - if (ma_strcmp(pDefaultDeviceNames[iDefaultDevice], pDescriptor->pDeviceID->audio4) == 0) { - break; - } - } - - if (iDefaultDevice == ma_countof(pDefaultDeviceNames)) { - iDefaultDevice = (size_t)-1; - } - } - - if (fd == -1) { - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[audio4] Failed to open device."); - return ma_result_from_errno(errno); - } - - #if !defined(MA_AUDIO4_USE_NEW_API) /* Old API */ - { - audio_info_t fdInfo; - int fdInfoResult = -1; - - /* - The documentation is a little bit unclear to me as to how it handles formats. It says the - following: - - Regardless of formats supported by underlying driver, the audio driver accepts the - following formats. - - By then the next sentence says this: - - `encoding` and `precision` are one of the values obtained by AUDIO_GETENC. - - It sounds like a direct contradiction to me. I'm going to play this safe any only use the - best sample format returned by AUDIO_GETENC. If the requested format is supported we'll - use that, but otherwise we'll just use our standard format priorities to pick an - appropriate one. - */ - AUDIO_INITINFO(&fdInfo); - - /* - Get the default format from the audioctl file if we're asking for a default device. If we - retrieve it from /dev/audio it'll default to mono 8000Hz. - */ - if (iDefaultDevice != (size_t)-1) { - /* We're using a default device. Get the info from the /dev/audioctl file instead of /dev/audio. */ - int fdctl = open(pDefaultDeviceCtlNames[iDefaultDevice], fdFlags, 0); - if (fdctl != -1) { -#if defined(__NetBSD__) && (__NetBSD_Version__ >= 900000000) - fdInfoResult = ioctl(fdctl, AUDIO_GETFORMAT, &fdInfo); -#else - fdInfoResult = ioctl(fdctl, AUDIO_GETINFO, &fdInfo); -#endif - close(fdctl); - } - } - - if (fdInfoResult == -1) { - /* We still don't have the default device info so just retrieve it from the main audio device. */ - if (ioctl(fd, AUDIO_GETINFO, &fdInfo) < 0) { - close(fd); - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[audio4] AUDIO_GETINFO failed."); - return ma_result_from_errno(errno); - } - } - - /* We get the driver to do as much of the data conversion as possible. */ - if (deviceType == ma_device_type_capture) { - fdInfo.mode = AUMODE_RECORD; - ma_encoding_from_format__audio4(ma_best_format_from_fd__audio4(fd, pDescriptor->format), &fdInfo.record.encoding, &fdInfo.record.precision); - - if (pDescriptor->channels != 0) { - fdInfo.record.channels = ma_clamp(pDescriptor->channels, 1, 12); /* From the documentation: `channels` ranges from 1 to 12. */ - } - - if (pDescriptor->sampleRate != 0) { - fdInfo.record.sample_rate = ma_clamp(pDescriptor->sampleRate, 1000, 192000); /* From the documentation: `frequency` ranges from 1000Hz to 192000Hz. (They mean `sample_rate` instead of `frequency`.) */ - } - } else { - fdInfo.mode = AUMODE_PLAY; - ma_encoding_from_format__audio4(ma_best_format_from_fd__audio4(fd, pDescriptor->format), &fdInfo.play.encoding, &fdInfo.play.precision); - - if (pDescriptor->channels != 0) { - fdInfo.play.channels = ma_clamp(pDescriptor->channels, 1, 12); /* From the documentation: `channels` ranges from 1 to 12. */ - } - - if (pDescriptor->sampleRate != 0) { - fdInfo.play.sample_rate = ma_clamp(pDescriptor->sampleRate, 1000, 192000); /* From the documentation: `frequency` ranges from 1000Hz to 192000Hz. (They mean `sample_rate` instead of `frequency`.) */ - } - } - - if (ioctl(fd, AUDIO_SETINFO, &fdInfo) < 0) { - close(fd); - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[audio4] Failed to set device format. AUDIO_SETINFO failed."); - return ma_result_from_errno(errno); - } - - if (ioctl(fd, AUDIO_GETINFO, &fdInfo) < 0) { - close(fd); - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[audio4] AUDIO_GETINFO failed."); - return ma_result_from_errno(errno); - } - - if (deviceType == ma_device_type_capture) { - internalFormat = ma_format_from_prinfo__audio4(&fdInfo.record); - internalChannels = fdInfo.record.channels; - internalSampleRate = fdInfo.record.sample_rate; - } else { - internalFormat = ma_format_from_prinfo__audio4(&fdInfo.play); - internalChannels = fdInfo.play.channels; - internalSampleRate = fdInfo.play.sample_rate; - } - - if (internalFormat == ma_format_unknown) { - close(fd); - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[audio4] The device's internal device format is not supported by miniaudio. The device is unusable."); - return MA_FORMAT_NOT_SUPPORTED; - } - - /* Buffer. */ - { - ma_uint32 internalPeriodSizeInBytes; - - internalPeriodSizeInFrames = ma_calculate_buffer_size_in_frames_from_descriptor(pDescriptor, internalSampleRate, pConfig->performanceProfile); - - internalPeriodSizeInBytes = internalPeriodSizeInFrames * ma_get_bytes_per_frame(internalFormat, internalChannels); - if (internalPeriodSizeInBytes < 16) { - internalPeriodSizeInBytes = 16; - } - - internalPeriods = pDescriptor->periodCount; - if (internalPeriods < 2) { - internalPeriods = 2; - } - - /* What miniaudio calls a period, audio4 calls a block. */ - AUDIO_INITINFO(&fdInfo); - fdInfo.hiwat = internalPeriods; - fdInfo.lowat = internalPeriods-1; - fdInfo.blocksize = internalPeriodSizeInBytes; - if (ioctl(fd, AUDIO_SETINFO, &fdInfo) < 0) { - close(fd); - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[audio4] Failed to set internal buffer size. AUDIO_SETINFO failed."); - return ma_result_from_errno(errno); - } - - internalPeriods = fdInfo.hiwat; - internalPeriodSizeInFrames = fdInfo.blocksize / ma_get_bytes_per_frame(internalFormat, internalChannels); - } - } - #else - { - struct audio_swpar fdPar; - - /* We need to retrieve the format of the device so we can know the channel count and sample rate. Then we can calculate the buffer size. */ - if (ioctl(fd, AUDIO_GETPAR, &fdPar) < 0) { - close(fd); - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[audio4] Failed to retrieve initial device parameters."); - return ma_result_from_errno(errno); - } - - internalFormat = ma_format_from_swpar__audio4(&fdPar); - internalChannels = (deviceType == ma_device_type_capture) ? fdPar.rchan : fdPar.pchan; - internalSampleRate = fdPar.rate; - - if (internalFormat == ma_format_unknown) { - close(fd); - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[audio4] The device's internal device format is not supported by miniaudio. The device is unusable."); - return MA_FORMAT_NOT_SUPPORTED; - } - - /* Buffer. */ - { - ma_uint32 internalPeriodSizeInBytes; - - internalPeriodSizeInFrames = ma_calculate_buffer_size_in_frames_from_descriptor(pDescriptor, internalSampleRate, pConfig->performanceProfile); - - /* What miniaudio calls a period, audio4 calls a block. */ - internalPeriodSizeInBytes = internalPeriodSizeInFrames * ma_get_bytes_per_frame(internalFormat, internalChannels); - if (internalPeriodSizeInBytes < 16) { - internalPeriodSizeInBytes = 16; - } - - fdPar.nblks = pDescriptor->periodCount; - fdPar.round = internalPeriodSizeInBytes; - - if (ioctl(fd, AUDIO_SETPAR, &fdPar) < 0) { - close(fd); - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[audio4] Failed to set device parameters."); - return ma_result_from_errno(errno); - } - - if (ioctl(fd, AUDIO_GETPAR, &fdPar) < 0) { - close(fd); - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[audio4] Failed to retrieve actual device parameters."); - return ma_result_from_errno(errno); - } - } - - internalFormat = ma_format_from_swpar__audio4(&fdPar); - internalChannels = (deviceType == ma_device_type_capture) ? fdPar.rchan : fdPar.pchan; - internalSampleRate = fdPar.rate; - internalPeriods = fdPar.nblks; - internalPeriodSizeInFrames = fdPar.round / ma_get_bytes_per_frame(internalFormat, internalChannels); - } - #endif - - if (internalFormat == ma_format_unknown) { - close(fd); - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[audio4] The device's internal device format is not supported by miniaudio. The device is unusable."); - return MA_FORMAT_NOT_SUPPORTED; - } - - if (deviceType == ma_device_type_capture) { - pDevice->audio4.fdCapture = fd; - } else { - pDevice->audio4.fdPlayback = fd; - } - - pDescriptor->format = internalFormat; - pDescriptor->channels = internalChannels; - pDescriptor->sampleRate = internalSampleRate; - ma_channel_map_init_standard(ma_standard_channel_map_sound4, pDescriptor->channelMap, ma_countof(pDescriptor->channelMap), internalChannels); - pDescriptor->periodSizeInFrames = internalPeriodSizeInFrames; - pDescriptor->periodCount = internalPeriods; - - return MA_SUCCESS; -} - -static ma_result ma_device_init__audio4(ma_device* pDevice, const ma_device_config* pConfig, ma_device_descriptor* pDescriptorPlayback, ma_device_descriptor* pDescriptorCapture) -{ - MA_ASSERT(pDevice != NULL); - - MA_ZERO_OBJECT(&pDevice->audio4); - - if (pConfig->deviceType == ma_device_type_loopback) { - return MA_DEVICE_TYPE_NOT_SUPPORTED; - } - - pDevice->audio4.fdCapture = -1; - pDevice->audio4.fdPlayback = -1; - - /* - The version of the operating system dictates whether or not the device is exclusive or shared. NetBSD - introduced in-kernel mixing which means it's shared. All other BSD flavours are exclusive as far as - I'm aware. - */ -#if defined(__NetBSD_Version__) && __NetBSD_Version__ >= 800000000 - /* NetBSD 8.0+ */ - if (((pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) && pDescriptorPlayback->shareMode == ma_share_mode_exclusive) || - ((pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) && pDescriptorCapture->shareMode == ma_share_mode_exclusive)) { - return MA_SHARE_MODE_NOT_SUPPORTED; - } -#else - /* All other flavors. */ -#endif - - if (pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) { - ma_result result = ma_device_init_fd__audio4(pDevice, pConfig, pDescriptorCapture, ma_device_type_capture); - if (result != MA_SUCCESS) { - return result; - } - } - - if (pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) { - ma_result result = ma_device_init_fd__audio4(pDevice, pConfig, pDescriptorPlayback, ma_device_type_playback); - if (result != MA_SUCCESS) { - if (pConfig->deviceType == ma_device_type_duplex) { - close(pDevice->audio4.fdCapture); - } - return result; - } - } - - return MA_SUCCESS; -} - -static ma_result ma_device_start__audio4(ma_device* pDevice) -{ - MA_ASSERT(pDevice != NULL); - - if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) { - if (pDevice->audio4.fdCapture == -1) { - return MA_INVALID_ARGS; - } - } - - if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) { - if (pDevice->audio4.fdPlayback == -1) { - return MA_INVALID_ARGS; - } - } - - return MA_SUCCESS; -} - -static ma_result ma_device_stop_fd__audio4(ma_device* pDevice, int fd) -{ - if (fd == -1) { - return MA_INVALID_ARGS; - } - -#if !defined(MA_AUDIO4_USE_NEW_API) - if (ioctl(fd, AUDIO_FLUSH, 0) < 0) { - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[audio4] Failed to stop device. AUDIO_FLUSH failed."); - return ma_result_from_errno(errno); - } -#else - if (ioctl(fd, AUDIO_STOP, 0) < 0) { - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[audio4] Failed to stop device. AUDIO_STOP failed."); - return ma_result_from_errno(errno); - } -#endif - - return MA_SUCCESS; -} - -static ma_result ma_device_stop__audio4(ma_device* pDevice) -{ - MA_ASSERT(pDevice != NULL); - - if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) { - ma_result result; - - result = ma_device_stop_fd__audio4(pDevice, pDevice->audio4.fdCapture); - if (result != MA_SUCCESS) { - return result; - } - } - - if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) { - ma_result result; - - /* Drain the device first. If this fails we'll just need to flush without draining. Unfortunately draining isn't available on newer version of OpenBSD. */ - #if !defined(MA_AUDIO4_USE_NEW_API) - ioctl(pDevice->audio4.fdPlayback, AUDIO_DRAIN, 0); - #endif - - /* Here is where the device is stopped immediately. */ - result = ma_device_stop_fd__audio4(pDevice, pDevice->audio4.fdPlayback); - if (result != MA_SUCCESS) { - return result; - } - } - - return MA_SUCCESS; -} - -static ma_result ma_device_write__audio4(ma_device* pDevice, const void* pPCMFrames, ma_uint32 frameCount, ma_uint32* pFramesWritten) -{ - int result; - - if (pFramesWritten != NULL) { - *pFramesWritten = 0; - } - - result = write(pDevice->audio4.fdPlayback, pPCMFrames, frameCount * ma_get_bytes_per_frame(pDevice->playback.internalFormat, pDevice->playback.internalChannels)); - if (result < 0) { - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[audio4] Failed to write data to the device."); - return ma_result_from_errno(errno); - } - - if (pFramesWritten != NULL) { - *pFramesWritten = (ma_uint32)result / ma_get_bytes_per_frame(pDevice->playback.internalFormat, pDevice->playback.internalChannels); - } - - return MA_SUCCESS; -} - -static ma_result ma_device_read__audio4(ma_device* pDevice, void* pPCMFrames, ma_uint32 frameCount, ma_uint32* pFramesRead) -{ - int result; - - if (pFramesRead != NULL) { - *pFramesRead = 0; - } - - result = read(pDevice->audio4.fdCapture, pPCMFrames, frameCount * ma_get_bytes_per_frame(pDevice->capture.internalFormat, pDevice->capture.internalChannels)); - if (result < 0) { - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[audio4] Failed to read data from the device."); - return ma_result_from_errno(errno); - } - - if (pFramesRead != NULL) { - *pFramesRead = (ma_uint32)result / ma_get_bytes_per_frame(pDevice->capture.internalFormat, pDevice->capture.internalChannels); - } - - return MA_SUCCESS; -} - -static ma_result ma_context_uninit__audio4(ma_context* pContext) -{ - MA_ASSERT(pContext != NULL); - MA_ASSERT(pContext->backend == ma_backend_audio4); - - (void)pContext; - return MA_SUCCESS; -} - -static ma_result ma_context_init__audio4(ma_context* pContext, const ma_context_config* pConfig, ma_backend_callbacks* pCallbacks) -{ - MA_ASSERT(pContext != NULL); - - (void)pConfig; - - pCallbacks->onContextInit = ma_context_init__audio4; - pCallbacks->onContextUninit = ma_context_uninit__audio4; - pCallbacks->onContextEnumerateDevices = ma_context_enumerate_devices__audio4; - pCallbacks->onContextGetDeviceInfo = ma_context_get_device_info__audio4; - pCallbacks->onDeviceInit = ma_device_init__audio4; - pCallbacks->onDeviceUninit = ma_device_uninit__audio4; - pCallbacks->onDeviceStart = ma_device_start__audio4; - pCallbacks->onDeviceStop = ma_device_stop__audio4; - pCallbacks->onDeviceRead = ma_device_read__audio4; - pCallbacks->onDeviceWrite = ma_device_write__audio4; - pCallbacks->onDeviceDataLoop = NULL; - - return MA_SUCCESS; -} -#endif /* MA_HAS_AUDIO4 */ - - -/****************************************************************************** - -OSS Backend - -******************************************************************************/ -#ifdef MA_HAS_OSS -#include -#include -#include -#include - -#ifndef SNDCTL_DSP_HALT -#define SNDCTL_DSP_HALT SNDCTL_DSP_RESET -#endif - -#define MA_OSS_DEFAULT_DEVICE_NAME "/dev/dsp" - -static int ma_open_temp_device__oss() -{ - /* The OSS sample code uses "/dev/mixer" as the device for getting system properties so I'm going to do the same. */ - int fd = open("/dev/mixer", O_RDONLY, 0); - if (fd >= 0) { - return fd; - } - - return -1; -} - -static ma_result ma_context_open_device__oss(ma_context* pContext, ma_device_type deviceType, const ma_device_id* pDeviceID, ma_share_mode shareMode, int* pfd) -{ - const char* deviceName; - int flags; - - MA_ASSERT(pContext != NULL); - MA_ASSERT(pfd != NULL); - (void)pContext; - - *pfd = -1; - - /* This function should only be called for playback or capture, not duplex. */ - if (deviceType == ma_device_type_duplex) { - return MA_INVALID_ARGS; - } - - deviceName = MA_OSS_DEFAULT_DEVICE_NAME; - if (pDeviceID != NULL) { - deviceName = pDeviceID->oss; - } - - flags = (deviceType == ma_device_type_playback) ? O_WRONLY : O_RDONLY; - if (shareMode == ma_share_mode_exclusive) { - flags |= O_EXCL; - } - - *pfd = open(deviceName, flags, 0); - if (*pfd == -1) { - return ma_result_from_errno(errno); - } - - return MA_SUCCESS; -} - -static ma_result ma_context_enumerate_devices__oss(ma_context* pContext, ma_enum_devices_callback_proc callback, void* pUserData) -{ - int fd; - oss_sysinfo si; - int result; - - MA_ASSERT(pContext != NULL); - MA_ASSERT(callback != NULL); - - fd = ma_open_temp_device__oss(); - if (fd == -1) { - ma_log_post(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[OSS] Failed to open a temporary device for retrieving system information used for device enumeration."); - return MA_NO_BACKEND; - } - - result = ioctl(fd, SNDCTL_SYSINFO, &si); - if (result != -1) { - int iAudioDevice; - for (iAudioDevice = 0; iAudioDevice < si.numaudios; ++iAudioDevice) { - oss_audioinfo ai; - ai.dev = iAudioDevice; - result = ioctl(fd, SNDCTL_AUDIOINFO, &ai); - if (result != -1) { - if (ai.devnode[0] != '\0') { /* <-- Can be blank, according to documentation. */ - ma_device_info deviceInfo; - ma_bool32 isTerminating = MA_FALSE; - - MA_ZERO_OBJECT(&deviceInfo); - - /* ID */ - ma_strncpy_s(deviceInfo.id.oss, sizeof(deviceInfo.id.oss), ai.devnode, (size_t)-1); - - /* - The human readable device name should be in the "ai.handle" variable, but it can - sometimes be empty in which case we just fall back to "ai.name" which is less user - friendly, but usually has a value. - */ - if (ai.handle[0] != '\0') { - ma_strncpy_s(deviceInfo.name, sizeof(deviceInfo.name), ai.handle, (size_t)-1); - } else { - ma_strncpy_s(deviceInfo.name, sizeof(deviceInfo.name), ai.name, (size_t)-1); - } - - /* The device can be both playback and capture. */ - if (!isTerminating && (ai.caps & PCM_CAP_OUTPUT) != 0) { - isTerminating = !callback(pContext, ma_device_type_playback, &deviceInfo, pUserData); - } - if (!isTerminating && (ai.caps & PCM_CAP_INPUT) != 0) { - isTerminating = !callback(pContext, ma_device_type_capture, &deviceInfo, pUserData); - } - - if (isTerminating) { - break; - } - } - } - } - } else { - close(fd); - ma_log_post(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[OSS] Failed to retrieve system information for device enumeration."); - return MA_NO_BACKEND; - } - - close(fd); - return MA_SUCCESS; -} - -static void ma_context_add_native_data_format__oss(ma_context* pContext, oss_audioinfo* pAudioInfo, ma_format format, ma_device_info* pDeviceInfo) -{ - unsigned int minChannels; - unsigned int maxChannels; - unsigned int iRate; - - MA_ASSERT(pContext != NULL); - MA_ASSERT(pAudioInfo != NULL); - MA_ASSERT(pDeviceInfo != NULL); - - /* If we support all channels we just report 0. */ - minChannels = ma_clamp(pAudioInfo->min_channels, MA_MIN_CHANNELS, MA_MAX_CHANNELS); - maxChannels = ma_clamp(pAudioInfo->max_channels, MA_MIN_CHANNELS, MA_MAX_CHANNELS); - - /* - OSS has this annoying thing where sample rates can be reported in two ways. We prefer explicitness, - which OSS has in the form of nrates/rates, however there are times where nrates can be 0, in which - case we'll need to use min_rate and max_rate and report only standard rates. - */ - if (pAudioInfo->nrates > 0) { - for (iRate = 0; iRate < pAudioInfo->nrates; iRate += 1) { - unsigned int rate = pAudioInfo->rates[iRate]; - - if (minChannels == MA_MIN_CHANNELS && maxChannels == MA_MAX_CHANNELS) { - ma_device_info_add_native_data_format(pDeviceInfo, format, 0, rate, 0); /* Set the channel count to 0 to indicate that all channel counts are supported. */ - } else { - unsigned int iChannel; - for (iChannel = minChannels; iChannel <= maxChannels; iChannel += 1) { - ma_device_info_add_native_data_format(pDeviceInfo, format, iChannel, rate, 0); - } - } - } - } else { - for (iRate = 0; iRate < ma_countof(g_maStandardSampleRatePriorities); iRate += 1) { - ma_uint32 standardRate = g_maStandardSampleRatePriorities[iRate]; - - if (standardRate >= (ma_uint32)pAudioInfo->min_rate && standardRate <= (ma_uint32)pAudioInfo->max_rate) { - if (minChannels == MA_MIN_CHANNELS && maxChannels == MA_MAX_CHANNELS) { - ma_device_info_add_native_data_format(pDeviceInfo, format, 0, standardRate, 0); /* Set the channel count to 0 to indicate that all channel counts are supported. */ - } else { - unsigned int iChannel; - for (iChannel = minChannels; iChannel <= maxChannels; iChannel += 1) { - ma_device_info_add_native_data_format(pDeviceInfo, format, iChannel, standardRate, 0); - } - } - } - } - } -} - -static ma_result ma_context_get_device_info__oss(ma_context* pContext, ma_device_type deviceType, const ma_device_id* pDeviceID, ma_device_info* pDeviceInfo) -{ - ma_bool32 foundDevice; - int fdTemp; - oss_sysinfo si; - int result; - - MA_ASSERT(pContext != NULL); - - /* Handle the default device a little differently. */ - if (pDeviceID == NULL) { - if (deviceType == ma_device_type_playback) { - ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), MA_DEFAULT_PLAYBACK_DEVICE_NAME, (size_t)-1); - } else { - ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), MA_DEFAULT_CAPTURE_DEVICE_NAME, (size_t)-1); - } - - return MA_SUCCESS; - } - - - /* If we get here it means we are _not_ using the default device. */ - foundDevice = MA_FALSE; - - fdTemp = ma_open_temp_device__oss(); - if (fdTemp == -1) { - ma_log_post(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[OSS] Failed to open a temporary device for retrieving system information used for device enumeration."); - return MA_NO_BACKEND; - } - - result = ioctl(fdTemp, SNDCTL_SYSINFO, &si); - if (result != -1) { - int iAudioDevice; - for (iAudioDevice = 0; iAudioDevice < si.numaudios; ++iAudioDevice) { - oss_audioinfo ai; - ai.dev = iAudioDevice; - result = ioctl(fdTemp, SNDCTL_AUDIOINFO, &ai); - if (result != -1) { - if (ma_strcmp(ai.devnode, pDeviceID->oss) == 0) { - /* It has the same name, so now just confirm the type. */ - if ((deviceType == ma_device_type_playback && ((ai.caps & PCM_CAP_OUTPUT) != 0)) || - (deviceType == ma_device_type_capture && ((ai.caps & PCM_CAP_INPUT) != 0))) { - unsigned int formatMask; - - /* ID */ - ma_strncpy_s(pDeviceInfo->id.oss, sizeof(pDeviceInfo->id.oss), ai.devnode, (size_t)-1); - - /* - The human readable device name should be in the "ai.handle" variable, but it can - sometimes be empty in which case we just fall back to "ai.name" which is less user - friendly, but usually has a value. - */ - if (ai.handle[0] != '\0') { - ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), ai.handle, (size_t)-1); - } else { - ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), ai.name, (size_t)-1); - } - - - pDeviceInfo->nativeDataFormatCount = 0; - - if (deviceType == ma_device_type_playback) { - formatMask = ai.oformats; - } else { - formatMask = ai.iformats; - } - - if (((formatMask & AFMT_S16_LE) != 0 && ma_is_little_endian()) || (AFMT_S16_BE && ma_is_big_endian())) { - ma_context_add_native_data_format__oss(pContext, &ai, ma_format_s16, pDeviceInfo); - } - if (((formatMask & AFMT_S32_LE) != 0 && ma_is_little_endian()) || (AFMT_S32_BE && ma_is_big_endian())) { - ma_context_add_native_data_format__oss(pContext, &ai, ma_format_s32, pDeviceInfo); - } - if ((formatMask & AFMT_U8) != 0) { - ma_context_add_native_data_format__oss(pContext, &ai, ma_format_u8, pDeviceInfo); - } - - foundDevice = MA_TRUE; - break; - } - } - } - } - } else { - close(fdTemp); - ma_log_post(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[OSS] Failed to retrieve system information for device enumeration."); - return MA_NO_BACKEND; - } - - - close(fdTemp); - - if (!foundDevice) { - return MA_NO_DEVICE; - } - - return MA_SUCCESS; -} - -static ma_result ma_device_uninit__oss(ma_device* pDevice) -{ - MA_ASSERT(pDevice != NULL); - - if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) { - close(pDevice->oss.fdCapture); - } - - if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) { - close(pDevice->oss.fdPlayback); - } - - return MA_SUCCESS; -} - -static int ma_format_to_oss(ma_format format) -{ - int ossFormat = AFMT_U8; - switch (format) { - case ma_format_s16: ossFormat = (ma_is_little_endian()) ? AFMT_S16_LE : AFMT_S16_BE; break; - case ma_format_s24: ossFormat = (ma_is_little_endian()) ? AFMT_S32_LE : AFMT_S32_BE; break; - case ma_format_s32: ossFormat = (ma_is_little_endian()) ? AFMT_S32_LE : AFMT_S32_BE; break; - case ma_format_f32: ossFormat = (ma_is_little_endian()) ? AFMT_S16_LE : AFMT_S16_BE; break; - case ma_format_u8: - default: ossFormat = AFMT_U8; break; - } - - return ossFormat; -} - -static ma_format ma_format_from_oss(int ossFormat) -{ - if (ossFormat == AFMT_U8) { - return ma_format_u8; - } else { - if (ma_is_little_endian()) { - switch (ossFormat) { - case AFMT_S16_LE: return ma_format_s16; - case AFMT_S32_LE: return ma_format_s32; - default: return ma_format_unknown; - } - } else { - switch (ossFormat) { - case AFMT_S16_BE: return ma_format_s16; - case AFMT_S32_BE: return ma_format_s32; - default: return ma_format_unknown; - } - } - } - - return ma_format_unknown; -} - -static ma_result ma_device_init_fd__oss(ma_device* pDevice, const ma_device_config* pConfig, ma_device_descriptor* pDescriptor, ma_device_type deviceType) -{ - ma_result result; - int ossResult; - int fd; - const ma_device_id* pDeviceID = NULL; - ma_share_mode shareMode; - int ossFormat; - int ossChannels; - int ossSampleRate; - int ossFragment; - - MA_ASSERT(pDevice != NULL); - MA_ASSERT(pConfig != NULL); - MA_ASSERT(deviceType != ma_device_type_duplex); - - pDeviceID = pDescriptor->pDeviceID; - shareMode = pDescriptor->shareMode; - ossFormat = ma_format_to_oss((pDescriptor->format != ma_format_unknown) ? pDescriptor->format : ma_format_s16); /* Use s16 by default because OSS doesn't like floating point. */ - ossChannels = (int)(pDescriptor->channels > 0) ? pDescriptor->channels : MA_DEFAULT_CHANNELS; - ossSampleRate = (int)(pDescriptor->sampleRate > 0) ? pDescriptor->sampleRate : MA_DEFAULT_SAMPLE_RATE; - - result = ma_context_open_device__oss(pDevice->pContext, deviceType, pDeviceID, shareMode, &fd); - if (result != MA_SUCCESS) { - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[OSS] Failed to open device."); - return result; - } - - /* - The OSS documentation is very clear about the order we should be initializing the device's properties: - 1) Format - 2) Channels - 3) Sample rate. - */ - - /* Format. */ - ossResult = ioctl(fd, SNDCTL_DSP_SETFMT, &ossFormat); - if (ossResult == -1) { - close(fd); - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[OSS] Failed to set format."); - return ma_result_from_errno(errno); - } - - /* Channels. */ - ossResult = ioctl(fd, SNDCTL_DSP_CHANNELS, &ossChannels); - if (ossResult == -1) { - close(fd); - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[OSS] Failed to set channel count."); - return ma_result_from_errno(errno); - } - - /* Sample Rate. */ - ossResult = ioctl(fd, SNDCTL_DSP_SPEED, &ossSampleRate); - if (ossResult == -1) { - close(fd); - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[OSS] Failed to set sample rate."); - return ma_result_from_errno(errno); - } - - /* - Buffer. - - The documentation says that the fragment settings should be set as soon as possible, but I'm not sure if - it should be done before or after format/channels/rate. - - OSS wants the fragment size in bytes and a power of 2. When setting, we specify the power, not the actual - value. - */ - { - ma_uint32 periodSizeInFrames; - ma_uint32 periodSizeInBytes; - ma_uint32 ossFragmentSizePower; - - periodSizeInFrames = ma_calculate_buffer_size_in_frames_from_descriptor(pDescriptor, (ma_uint32)ossSampleRate, pConfig->performanceProfile); - - periodSizeInBytes = ma_round_to_power_of_2(periodSizeInFrames * ma_get_bytes_per_frame(ma_format_from_oss(ossFormat), ossChannels)); - if (periodSizeInBytes < 16) { - periodSizeInBytes = 16; - } - - ossFragmentSizePower = 4; - periodSizeInBytes >>= 4; - while (periodSizeInBytes >>= 1) { - ossFragmentSizePower += 1; - } - - ossFragment = (int)((pConfig->periods << 16) | ossFragmentSizePower); - ossResult = ioctl(fd, SNDCTL_DSP_SETFRAGMENT, &ossFragment); - if (ossResult == -1) { - close(fd); - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[OSS] Failed to set fragment size and period count."); - return ma_result_from_errno(errno); - } - } - - /* Internal settings. */ - if (deviceType == ma_device_type_capture) { - pDevice->oss.fdCapture = fd; - } else { - pDevice->oss.fdPlayback = fd; - } - - pDescriptor->format = ma_format_from_oss(ossFormat); - pDescriptor->channels = ossChannels; - pDescriptor->sampleRate = ossSampleRate; - ma_channel_map_init_standard(ma_standard_channel_map_sound4, pDescriptor->channelMap, ma_countof(pDescriptor->channelMap), pDescriptor->channels); - pDescriptor->periodCount = (ma_uint32)(ossFragment >> 16); - pDescriptor->periodSizeInFrames = (ma_uint32)(1 << (ossFragment & 0xFFFF)) / ma_get_bytes_per_frame(pDescriptor->format, pDescriptor->channels); - - if (pDescriptor->format == ma_format_unknown) { - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[OSS] The device's internal format is not supported by miniaudio."); - return MA_FORMAT_NOT_SUPPORTED; - } - - return MA_SUCCESS; -} - -static ma_result ma_device_init__oss(ma_device* pDevice, const ma_device_config* pConfig, ma_device_descriptor* pDescriptorPlayback, ma_device_descriptor* pDescriptorCapture) -{ - MA_ASSERT(pDevice != NULL); - MA_ASSERT(pConfig != NULL); - - MA_ZERO_OBJECT(&pDevice->oss); - - if (pConfig->deviceType == ma_device_type_loopback) { - return MA_DEVICE_TYPE_NOT_SUPPORTED; - } - - if (pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) { - ma_result result = ma_device_init_fd__oss(pDevice, pConfig, pDescriptorCapture, ma_device_type_capture); - if (result != MA_SUCCESS) { - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[OSS] Failed to open device."); - return result; - } - } - - if (pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) { - ma_result result = ma_device_init_fd__oss(pDevice, pConfig, pDescriptorPlayback, ma_device_type_playback); - if (result != MA_SUCCESS) { - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[OSS] Failed to open device."); - return result; - } - } - - return MA_SUCCESS; -} - -/* -Note on Starting and Stopping -============================= -In the past I was using SNDCTL_DSP_HALT to stop the device, however this results in issues when -trying to resume the device again. If we use SNDCTL_DSP_HALT, the next write() or read() will -fail. Instead what we need to do is just not write or read to and from the device when the -device is not running. - -As a result, both the start and stop functions for OSS are just empty stubs. The starting and -stopping logic is handled by ma_device_write__oss() and ma_device_read__oss(). These will check -the device state, and if the device is stopped they will simply not do any kind of processing. - -The downside to this technique is that I've noticed a fairly lengthy delay in stopping the -device, up to a second. This is on a virtual machine, and as such might just be due to the -virtual drivers, but I'm not fully sure. I am not sure how to work around this problem so for -the moment that's just how it's going to have to be. - -When starting the device, OSS will automatically start it when write() or read() is called. -*/ -static ma_result ma_device_start__oss(ma_device* pDevice) -{ - MA_ASSERT(pDevice != NULL); - - /* The device is automatically started with reading and writing. */ - (void)pDevice; - - return MA_SUCCESS; -} - -static ma_result ma_device_stop__oss(ma_device* pDevice) -{ - MA_ASSERT(pDevice != NULL); - - /* See note above on why this is empty. */ - (void)pDevice; - - return MA_SUCCESS; -} - -static ma_result ma_device_write__oss(ma_device* pDevice, const void* pPCMFrames, ma_uint32 frameCount, ma_uint32* pFramesWritten) -{ - int resultOSS; - ma_uint32 deviceState; - - if (pFramesWritten != NULL) { - *pFramesWritten = 0; - } - - /* Don't do any processing if the device is stopped. */ - deviceState = ma_device_get_state(pDevice); - if (deviceState != ma_device_state_started && deviceState != ma_device_state_starting) { - return MA_SUCCESS; - } - - resultOSS = write(pDevice->oss.fdPlayback, pPCMFrames, frameCount * ma_get_bytes_per_frame(pDevice->playback.internalFormat, pDevice->playback.internalChannels)); - if (resultOSS < 0) { - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[OSS] Failed to send data from the client to the device."); - return ma_result_from_errno(errno); - } - - if (pFramesWritten != NULL) { - *pFramesWritten = (ma_uint32)resultOSS / ma_get_bytes_per_frame(pDevice->playback.internalFormat, pDevice->playback.internalChannels); - } - - return MA_SUCCESS; -} - -static ma_result ma_device_read__oss(ma_device* pDevice, void* pPCMFrames, ma_uint32 frameCount, ma_uint32* pFramesRead) -{ - int resultOSS; - ma_uint32 deviceState; - - if (pFramesRead != NULL) { - *pFramesRead = 0; - } - - /* Don't do any processing if the device is stopped. */ - deviceState = ma_device_get_state(pDevice); - if (deviceState != ma_device_state_started && deviceState != ma_device_state_starting) { - return MA_SUCCESS; - } - - resultOSS = read(pDevice->oss.fdCapture, pPCMFrames, frameCount * ma_get_bytes_per_frame(pDevice->capture.internalFormat, pDevice->capture.internalChannels)); - if (resultOSS < 0) { - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[OSS] Failed to read data from the device to be sent to the client."); - return ma_result_from_errno(errno); - } - - if (pFramesRead != NULL) { - *pFramesRead = (ma_uint32)resultOSS / ma_get_bytes_per_frame(pDevice->capture.internalFormat, pDevice->capture.internalChannels); - } - - return MA_SUCCESS; -} - -static ma_result ma_context_uninit__oss(ma_context* pContext) -{ - MA_ASSERT(pContext != NULL); - MA_ASSERT(pContext->backend == ma_backend_oss); - - (void)pContext; - return MA_SUCCESS; -} - -static ma_result ma_context_init__oss(ma_context* pContext, const ma_context_config* pConfig, ma_backend_callbacks* pCallbacks) -{ - int fd; - int ossVersion; - int result; - - MA_ASSERT(pContext != NULL); - - (void)pConfig; - - /* Try opening a temporary device first so we can get version information. This is closed at the end. */ - fd = ma_open_temp_device__oss(); - if (fd == -1) { - ma_log_post(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[OSS] Failed to open temporary device for retrieving system properties."); /* Looks liks OSS isn't installed, or there are no available devices. */ - return MA_NO_BACKEND; - } - - /* Grab the OSS version. */ - ossVersion = 0; - result = ioctl(fd, OSS_GETVERSION, &ossVersion); - if (result == -1) { - close(fd); - ma_log_post(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[OSS] Failed to retrieve OSS version."); - return MA_NO_BACKEND; - } - - /* The file handle to temp device is no longer needed. Close ASAP. */ - close(fd); - - pContext->oss.versionMajor = ((ossVersion & 0xFF0000) >> 16); - pContext->oss.versionMinor = ((ossVersion & 0x00FF00) >> 8); - - pCallbacks->onContextInit = ma_context_init__oss; - pCallbacks->onContextUninit = ma_context_uninit__oss; - pCallbacks->onContextEnumerateDevices = ma_context_enumerate_devices__oss; - pCallbacks->onContextGetDeviceInfo = ma_context_get_device_info__oss; - pCallbacks->onDeviceInit = ma_device_init__oss; - pCallbacks->onDeviceUninit = ma_device_uninit__oss; - pCallbacks->onDeviceStart = ma_device_start__oss; - pCallbacks->onDeviceStop = ma_device_stop__oss; - pCallbacks->onDeviceRead = ma_device_read__oss; - pCallbacks->onDeviceWrite = ma_device_write__oss; - pCallbacks->onDeviceDataLoop = NULL; - - return MA_SUCCESS; -} -#endif /* MA_HAS_OSS */ - - - - - -/****************************************************************************** - -AAudio Backend - -******************************************************************************/ -#ifdef MA_HAS_AAUDIO - -#ifdef MA_NO_RUNTIME_LINKING - #include -#endif - -typedef int32_t ma_aaudio_result_t; -typedef int32_t ma_aaudio_direction_t; -typedef int32_t ma_aaudio_sharing_mode_t; -typedef int32_t ma_aaudio_format_t; -typedef int32_t ma_aaudio_stream_state_t; -typedef int32_t ma_aaudio_performance_mode_t; -typedef int32_t ma_aaudio_usage_t; -typedef int32_t ma_aaudio_content_type_t; -typedef int32_t ma_aaudio_input_preset_t; -typedef int32_t ma_aaudio_allowed_capture_policy_t; -typedef int32_t ma_aaudio_data_callback_result_t; -typedef struct ma_AAudioStreamBuilder_t* ma_AAudioStreamBuilder; -typedef struct ma_AAudioStream_t* ma_AAudioStream; - -#define MA_AAUDIO_UNSPECIFIED 0 - -/* Result codes. miniaudio only cares about the success code. */ -#define MA_AAUDIO_OK 0 - -/* Directions. */ -#define MA_AAUDIO_DIRECTION_OUTPUT 0 -#define MA_AAUDIO_DIRECTION_INPUT 1 - -/* Sharing modes. */ -#define MA_AAUDIO_SHARING_MODE_EXCLUSIVE 0 -#define MA_AAUDIO_SHARING_MODE_SHARED 1 - -/* Formats. */ -#define MA_AAUDIO_FORMAT_PCM_I16 1 -#define MA_AAUDIO_FORMAT_PCM_FLOAT 2 - -/* Stream states. */ -#define MA_AAUDIO_STREAM_STATE_UNINITIALIZED 0 -#define MA_AAUDIO_STREAM_STATE_UNKNOWN 1 -#define MA_AAUDIO_STREAM_STATE_OPEN 2 -#define MA_AAUDIO_STREAM_STATE_STARTING 3 -#define MA_AAUDIO_STREAM_STATE_STARTED 4 -#define MA_AAUDIO_STREAM_STATE_PAUSING 5 -#define MA_AAUDIO_STREAM_STATE_PAUSED 6 -#define MA_AAUDIO_STREAM_STATE_FLUSHING 7 -#define MA_AAUDIO_STREAM_STATE_FLUSHED 8 -#define MA_AAUDIO_STREAM_STATE_STOPPING 9 -#define MA_AAUDIO_STREAM_STATE_STOPPED 10 -#define MA_AAUDIO_STREAM_STATE_CLOSING 11 -#define MA_AAUDIO_STREAM_STATE_CLOSED 12 -#define MA_AAUDIO_STREAM_STATE_DISCONNECTED 13 - -/* Performance modes. */ -#define MA_AAUDIO_PERFORMANCE_MODE_NONE 10 -#define MA_AAUDIO_PERFORMANCE_MODE_POWER_SAVING 11 -#define MA_AAUDIO_PERFORMANCE_MODE_LOW_LATENCY 12 - -/* Usage types. */ -#define MA_AAUDIO_USAGE_MEDIA 1 -#define MA_AAUDIO_USAGE_VOICE_COMMUNICATION 2 -#define MA_AAUDIO_USAGE_VOICE_COMMUNICATION_SIGNALLING 3 -#define MA_AAUDIO_USAGE_ALARM 4 -#define MA_AAUDIO_USAGE_NOTIFICATION 5 -#define MA_AAUDIO_USAGE_NOTIFICATION_RINGTONE 6 -#define MA_AAUDIO_USAGE_NOTIFICATION_EVENT 10 -#define MA_AAUDIO_USAGE_ASSISTANCE_ACCESSIBILITY 11 -#define MA_AAUDIO_USAGE_ASSISTANCE_NAVIGATION_GUIDANCE 12 -#define MA_AAUDIO_USAGE_ASSISTANCE_SONIFICATION 13 -#define MA_AAUDIO_USAGE_GAME 14 -#define MA_AAUDIO_USAGE_ASSISTANT 16 -#define MA_AAUDIO_SYSTEM_USAGE_EMERGENCY 1000 -#define MA_AAUDIO_SYSTEM_USAGE_SAFETY 1001 -#define MA_AAUDIO_SYSTEM_USAGE_VEHICLE_STATUS 1002 -#define MA_AAUDIO_SYSTEM_USAGE_ANNOUNCEMENT 1003 - -/* Content types. */ -#define MA_AAUDIO_CONTENT_TYPE_SPEECH 1 -#define MA_AAUDIO_CONTENT_TYPE_MUSIC 2 -#define MA_AAUDIO_CONTENT_TYPE_MOVIE 3 -#define MA_AAUDIO_CONTENT_TYPE_SONIFICATION 4 - -/* Input presets. */ -#define MA_AAUDIO_INPUT_PRESET_GENERIC 1 -#define MA_AAUDIO_INPUT_PRESET_CAMCORDER 5 -#define MA_AAUDIO_INPUT_PRESET_VOICE_RECOGNITION 6 -#define MA_AAUDIO_INPUT_PRESET_VOICE_COMMUNICATION 7 -#define MA_AAUDIO_INPUT_PRESET_UNPROCESSED 9 -#define MA_AAUDIO_INPUT_PRESET_VOICE_PERFORMANCE 10 - -/* Allowed Capture Policies */ -#define MA_AAUDIO_ALLOW_CAPTURE_BY_ALL 1 -#define MA_AAUDIO_ALLOW_CAPTURE_BY_SYSTEM 2 -#define MA_AAUDIO_ALLOW_CAPTURE_BY_NONE 3 - -/* Callback results. */ -#define MA_AAUDIO_CALLBACK_RESULT_CONTINUE 0 -#define MA_AAUDIO_CALLBACK_RESULT_STOP 1 - - -typedef ma_aaudio_data_callback_result_t (* ma_AAudioStream_dataCallback) (ma_AAudioStream* pStream, void* pUserData, void* pAudioData, int32_t numFrames); -typedef void (* ma_AAudioStream_errorCallback)(ma_AAudioStream *pStream, void *pUserData, ma_aaudio_result_t error); - -typedef ma_aaudio_result_t (* MA_PFN_AAudio_createStreamBuilder) (ma_AAudioStreamBuilder** ppBuilder); -typedef ma_aaudio_result_t (* MA_PFN_AAudioStreamBuilder_delete) (ma_AAudioStreamBuilder* pBuilder); -typedef void (* MA_PFN_AAudioStreamBuilder_setDeviceId) (ma_AAudioStreamBuilder* pBuilder, int32_t deviceId); -typedef void (* MA_PFN_AAudioStreamBuilder_setDirection) (ma_AAudioStreamBuilder* pBuilder, ma_aaudio_direction_t direction); -typedef void (* MA_PFN_AAudioStreamBuilder_setSharingMode) (ma_AAudioStreamBuilder* pBuilder, ma_aaudio_sharing_mode_t sharingMode); -typedef void (* MA_PFN_AAudioStreamBuilder_setFormat) (ma_AAudioStreamBuilder* pBuilder, ma_aaudio_format_t format); -typedef void (* MA_PFN_AAudioStreamBuilder_setChannelCount) (ma_AAudioStreamBuilder* pBuilder, int32_t channelCount); -typedef void (* MA_PFN_AAudioStreamBuilder_setSampleRate) (ma_AAudioStreamBuilder* pBuilder, int32_t sampleRate); -typedef void (* MA_PFN_AAudioStreamBuilder_setBufferCapacityInFrames)(ma_AAudioStreamBuilder* pBuilder, int32_t numFrames); -typedef void (* MA_PFN_AAudioStreamBuilder_setFramesPerDataCallback) (ma_AAudioStreamBuilder* pBuilder, int32_t numFrames); -typedef void (* MA_PFN_AAudioStreamBuilder_setDataCallback) (ma_AAudioStreamBuilder* pBuilder, ma_AAudioStream_dataCallback callback, void* pUserData); -typedef void (* MA_PFN_AAudioStreamBuilder_setErrorCallback) (ma_AAudioStreamBuilder* pBuilder, ma_AAudioStream_errorCallback callback, void* pUserData); -typedef void (* MA_PFN_AAudioStreamBuilder_setPerformanceMode) (ma_AAudioStreamBuilder* pBuilder, ma_aaudio_performance_mode_t mode); -typedef void (* MA_PFN_AAudioStreamBuilder_setUsage) (ma_AAudioStreamBuilder* pBuilder, ma_aaudio_usage_t contentType); -typedef void (* MA_PFN_AAudioStreamBuilder_setContentType) (ma_AAudioStreamBuilder* pBuilder, ma_aaudio_content_type_t contentType); -typedef void (* MA_PFN_AAudioStreamBuilder_setInputPreset) (ma_AAudioStreamBuilder* pBuilder, ma_aaudio_input_preset_t inputPreset); -typedef void (* MA_PFN_AAudioStreamBuilder_setAllowedCapturePolicy) (ma_AAudioStreamBuilder* pBuilder, ma_aaudio_allowed_capture_policy_t policy); -typedef ma_aaudio_result_t (* MA_PFN_AAudioStreamBuilder_openStream) (ma_AAudioStreamBuilder* pBuilder, ma_AAudioStream** ppStream); -typedef ma_aaudio_result_t (* MA_PFN_AAudioStream_close) (ma_AAudioStream* pStream); -typedef ma_aaudio_stream_state_t (* MA_PFN_AAudioStream_getState) (ma_AAudioStream* pStream); -typedef ma_aaudio_result_t (* MA_PFN_AAudioStream_waitForStateChange) (ma_AAudioStream* pStream, ma_aaudio_stream_state_t inputState, ma_aaudio_stream_state_t* pNextState, int64_t timeoutInNanoseconds); -typedef ma_aaudio_format_t (* MA_PFN_AAudioStream_getFormat) (ma_AAudioStream* pStream); -typedef int32_t (* MA_PFN_AAudioStream_getChannelCount) (ma_AAudioStream* pStream); -typedef int32_t (* MA_PFN_AAudioStream_getSampleRate) (ma_AAudioStream* pStream); -typedef int32_t (* MA_PFN_AAudioStream_getBufferCapacityInFrames) (ma_AAudioStream* pStream); -typedef int32_t (* MA_PFN_AAudioStream_getFramesPerDataCallback) (ma_AAudioStream* pStream); -typedef int32_t (* MA_PFN_AAudioStream_getFramesPerBurst) (ma_AAudioStream* pStream); -typedef ma_aaudio_result_t (* MA_PFN_AAudioStream_requestStart) (ma_AAudioStream* pStream); -typedef ma_aaudio_result_t (* MA_PFN_AAudioStream_requestStop) (ma_AAudioStream* pStream); - -static ma_result ma_result_from_aaudio(ma_aaudio_result_t resultAA) -{ - switch (resultAA) - { - case MA_AAUDIO_OK: return MA_SUCCESS; - default: break; - } - - return MA_ERROR; -} - -static ma_aaudio_usage_t ma_to_usage__aaudio(ma_aaudio_usage usage) -{ - switch (usage) { - case ma_aaudio_usage_media: return MA_AAUDIO_USAGE_MEDIA; - case ma_aaudio_usage_voice_communication: return MA_AAUDIO_USAGE_VOICE_COMMUNICATION; - case ma_aaudio_usage_voice_communication_signalling: return MA_AAUDIO_USAGE_VOICE_COMMUNICATION_SIGNALLING; - case ma_aaudio_usage_alarm: return MA_AAUDIO_USAGE_ALARM; - case ma_aaudio_usage_notification: return MA_AAUDIO_USAGE_NOTIFICATION; - case ma_aaudio_usage_notification_ringtone: return MA_AAUDIO_USAGE_NOTIFICATION_RINGTONE; - case ma_aaudio_usage_notification_event: return MA_AAUDIO_USAGE_NOTIFICATION_EVENT; - case ma_aaudio_usage_assistance_accessibility: return MA_AAUDIO_USAGE_ASSISTANCE_ACCESSIBILITY; - case ma_aaudio_usage_assistance_navigation_guidance: return MA_AAUDIO_USAGE_ASSISTANCE_NAVIGATION_GUIDANCE; - case ma_aaudio_usage_assistance_sonification: return MA_AAUDIO_USAGE_ASSISTANCE_SONIFICATION; - case ma_aaudio_usage_game: return MA_AAUDIO_USAGE_GAME; - case ma_aaudio_usage_assitant: return MA_AAUDIO_USAGE_ASSISTANT; - case ma_aaudio_usage_emergency: return MA_AAUDIO_SYSTEM_USAGE_EMERGENCY; - case ma_aaudio_usage_safety: return MA_AAUDIO_SYSTEM_USAGE_SAFETY; - case ma_aaudio_usage_vehicle_status: return MA_AAUDIO_SYSTEM_USAGE_VEHICLE_STATUS; - case ma_aaudio_usage_announcement: return MA_AAUDIO_SYSTEM_USAGE_ANNOUNCEMENT; - default: break; - } - - return MA_AAUDIO_USAGE_MEDIA; -} - -static ma_aaudio_content_type_t ma_to_content_type__aaudio(ma_aaudio_content_type contentType) -{ - switch (contentType) { - case ma_aaudio_content_type_speech: return MA_AAUDIO_CONTENT_TYPE_SPEECH; - case ma_aaudio_content_type_music: return MA_AAUDIO_CONTENT_TYPE_MUSIC; - case ma_aaudio_content_type_movie: return MA_AAUDIO_CONTENT_TYPE_MOVIE; - case ma_aaudio_content_type_sonification: return MA_AAUDIO_CONTENT_TYPE_SONIFICATION; - default: break; - } - - return MA_AAUDIO_CONTENT_TYPE_SPEECH; -} - -static ma_aaudio_input_preset_t ma_to_input_preset__aaudio(ma_aaudio_input_preset inputPreset) -{ - switch (inputPreset) { - case ma_aaudio_input_preset_generic: return MA_AAUDIO_INPUT_PRESET_GENERIC; - case ma_aaudio_input_preset_camcorder: return MA_AAUDIO_INPUT_PRESET_CAMCORDER; - case ma_aaudio_input_preset_voice_recognition: return MA_AAUDIO_INPUT_PRESET_VOICE_RECOGNITION; - case ma_aaudio_input_preset_voice_communication: return MA_AAUDIO_INPUT_PRESET_VOICE_COMMUNICATION; - case ma_aaudio_input_preset_unprocessed: return MA_AAUDIO_INPUT_PRESET_UNPROCESSED; - case ma_aaudio_input_preset_voice_performance: return MA_AAUDIO_INPUT_PRESET_VOICE_PERFORMANCE; - default: break; - } - - return MA_AAUDIO_INPUT_PRESET_GENERIC; -} - -static ma_aaudio_allowed_capture_policy_t ma_to_allowed_capture_policy__aaudio(ma_aaudio_allowed_capture_policy allowedCapturePolicy) -{ - switch (allowedCapturePolicy) { - case ma_aaudio_allow_capture_by_all: return MA_AAUDIO_ALLOW_CAPTURE_BY_ALL; - case ma_aaudio_allow_capture_by_system: return MA_AAUDIO_ALLOW_CAPTURE_BY_SYSTEM; - case ma_aaudio_allow_capture_by_none: return MA_AAUDIO_ALLOW_CAPTURE_BY_NONE; - default: break; - } - - return MA_AAUDIO_ALLOW_CAPTURE_BY_ALL; -} - -static void ma_stream_error_callback__aaudio(ma_AAudioStream* pStream, void* pUserData, ma_aaudio_result_t error) -{ - ma_result result; - ma_job job; - ma_device* pDevice = (ma_device*)pUserData; - MA_ASSERT(pDevice != NULL); - - (void)error; - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, "[AAudio] ERROR CALLBACK: error=%d, AAudioStream_getState()=%d\n", error, ((MA_PFN_AAudioStream_getState)pDevice->pContext->aaudio.AAudioStream_getState)(pStream)); - /* - When we get an error, we'll assume that the stream is in an erroneous state and needs to be restarted. From the documentation, - we cannot do this from the error callback. Therefore we are going to use an event thread for the AAudio backend to do this - cleanly and safely. - */ - job = ma_job_init(MA_JOB_TYPE_DEVICE_AAUDIO_REROUTE); - job.data.device.aaudio.reroute.pDevice = pDevice; - - if (pStream == pDevice->aaudio.pStreamCapture) { - job.data.device.aaudio.reroute.deviceType = ma_device_type_capture; - } - else { - job.data.device.aaudio.reroute.deviceType = ma_device_type_playback; - } - - result = ma_device_job_thread_post(&pDevice->pContext->aaudio.jobThread, &job); - if (result != MA_SUCCESS) { - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, "[AAudio] Device Disconnected. Failed to post job for rerouting.\n"); - return; - } -} - -static ma_aaudio_data_callback_result_t ma_stream_data_callback_capture__aaudio(ma_AAudioStream* pStream, void* pUserData, void* pAudioData, int32_t frameCount) -{ - ma_device* pDevice = (ma_device*)pUserData; - MA_ASSERT(pDevice != NULL); - - if (frameCount > 0) { - ma_device_handle_backend_data_callback(pDevice, NULL, pAudioData, (ma_uint32)frameCount); - } - - (void)pStream; - return MA_AAUDIO_CALLBACK_RESULT_CONTINUE; -} - -static ma_aaudio_data_callback_result_t ma_stream_data_callback_playback__aaudio(ma_AAudioStream* pStream, void* pUserData, void* pAudioData, int32_t frameCount) -{ - ma_device* pDevice = (ma_device*)pUserData; - MA_ASSERT(pDevice != NULL); - - /* - I've had a report that AAudio can sometimes post a frame count of 0. We need to check for that here - so we don't get any errors at a deeper level. I'm doing the same with the capture side for safety, - though I've not yet had any reports about that one. - */ - if (frameCount > 0) { - ma_device_handle_backend_data_callback(pDevice, pAudioData, NULL, (ma_uint32)frameCount); - } - - (void)pStream; - return MA_AAUDIO_CALLBACK_RESULT_CONTINUE; -} - -static ma_result ma_create_and_configure_AAudioStreamBuilder__aaudio(ma_context* pContext, const ma_device_id* pDeviceID, ma_device_type deviceType, ma_share_mode shareMode, const ma_device_descriptor* pDescriptor, const ma_device_config* pConfig, ma_device* pDevice, ma_AAudioStreamBuilder** ppBuilder) -{ - ma_AAudioStreamBuilder* pBuilder; - ma_aaudio_result_t resultAA; - - /* Safety. */ - *ppBuilder = NULL; - - resultAA = ((MA_PFN_AAudio_createStreamBuilder)pContext->aaudio.AAudio_createStreamBuilder)(&pBuilder); - if (resultAA != MA_AAUDIO_OK) { - return ma_result_from_aaudio(resultAA); - } - - if (pDeviceID != NULL) { - ((MA_PFN_AAudioStreamBuilder_setDeviceId)pContext->aaudio.AAudioStreamBuilder_setDeviceId)(pBuilder, pDeviceID->aaudio); - } - - ((MA_PFN_AAudioStreamBuilder_setDirection)pContext->aaudio.AAudioStreamBuilder_setDirection)(pBuilder, (deviceType == ma_device_type_playback) ? MA_AAUDIO_DIRECTION_OUTPUT : MA_AAUDIO_DIRECTION_INPUT); - ((MA_PFN_AAudioStreamBuilder_setSharingMode)pContext->aaudio.AAudioStreamBuilder_setSharingMode)(pBuilder, (shareMode == ma_share_mode_shared) ? MA_AAUDIO_SHARING_MODE_SHARED : MA_AAUDIO_SHARING_MODE_EXCLUSIVE); - - - /* If we have a device descriptor make sure we configure the stream builder to take our requested parameters. */ - if (pDescriptor != NULL) { - MA_ASSERT(pConfig != NULL); /* We must have a device config if we also have a descriptor. The config is required for AAudio specific configuration options. */ - - if (pDescriptor->sampleRate != 0) { - ((MA_PFN_AAudioStreamBuilder_setSampleRate)pContext->aaudio.AAudioStreamBuilder_setSampleRate)(pBuilder, pDescriptor->sampleRate); - } - - if (pDescriptor->channels != 0) { - ((MA_PFN_AAudioStreamBuilder_setChannelCount)pContext->aaudio.AAudioStreamBuilder_setChannelCount)(pBuilder, pDescriptor->channels); - } - - if (pDescriptor->format != ma_format_unknown) { - ((MA_PFN_AAudioStreamBuilder_setFormat)pContext->aaudio.AAudioStreamBuilder_setFormat)(pBuilder, (pDescriptor->format == ma_format_s16) ? MA_AAUDIO_FORMAT_PCM_I16 : MA_AAUDIO_FORMAT_PCM_FLOAT); - } - - - /* - There have been reports where setting the frames per data callback results in an error. - In particular, re-routing may inadvertently switch from low-latency mode, resulting in a less stable - stream from the legacy path (AudioStreamLegacy). To address this, we simply don't set the value. It - can still be set if it's explicitly requested via the aaudio.allowSetBufferCapacity variable in the - device config. - */ - if ((!pConfig->aaudio.enableCompatibilityWorkarounds || ma_android_sdk_version() > 30) && pConfig->aaudio.allowSetBufferCapacity) { - /* - AAudio is annoying when it comes to its buffer calculation stuff because it doesn't let you - retrieve the actual sample rate until after you've opened the stream. But you need to configure - the buffer capacity before you open the stream... :/ - - To solve, we're just going to assume MA_DEFAULT_SAMPLE_RATE (48000) and move on. - */ - ma_uint32 bufferCapacityInFrames = ma_calculate_buffer_size_in_frames_from_descriptor(pDescriptor, pDescriptor->sampleRate, pConfig->performanceProfile) * pDescriptor->periodCount; - - ((MA_PFN_AAudioStreamBuilder_setBufferCapacityInFrames)pContext->aaudio.AAudioStreamBuilder_setBufferCapacityInFrames)(pBuilder, bufferCapacityInFrames); - ((MA_PFN_AAudioStreamBuilder_setFramesPerDataCallback)pContext->aaudio.AAudioStreamBuilder_setFramesPerDataCallback)(pBuilder, bufferCapacityInFrames / pDescriptor->periodCount); - } - - if (deviceType == ma_device_type_capture) { - if (pConfig->aaudio.inputPreset != ma_aaudio_input_preset_default && pContext->aaudio.AAudioStreamBuilder_setInputPreset != NULL) { - ((MA_PFN_AAudioStreamBuilder_setInputPreset)pContext->aaudio.AAudioStreamBuilder_setInputPreset)(pBuilder, ma_to_input_preset__aaudio(pConfig->aaudio.inputPreset)); - } - - ((MA_PFN_AAudioStreamBuilder_setDataCallback)pContext->aaudio.AAudioStreamBuilder_setDataCallback)(pBuilder, ma_stream_data_callback_capture__aaudio, (void*)pDevice); - } else { - if (pConfig->aaudio.usage != ma_aaudio_usage_default && pContext->aaudio.AAudioStreamBuilder_setUsage != NULL) { - ((MA_PFN_AAudioStreamBuilder_setUsage)pContext->aaudio.AAudioStreamBuilder_setUsage)(pBuilder, ma_to_usage__aaudio(pConfig->aaudio.usage)); - } - - if (pConfig->aaudio.contentType != ma_aaudio_content_type_default && pContext->aaudio.AAudioStreamBuilder_setContentType != NULL) { - ((MA_PFN_AAudioStreamBuilder_setContentType)pContext->aaudio.AAudioStreamBuilder_setContentType)(pBuilder, ma_to_content_type__aaudio(pConfig->aaudio.contentType)); - } - - if (pConfig->aaudio.allowedCapturePolicy != ma_aaudio_allow_capture_default && pContext->aaudio.AAudioStreamBuilder_setAllowedCapturePolicy != NULL) { - ((MA_PFN_AAudioStreamBuilder_setAllowedCapturePolicy)pContext->aaudio.AAudioStreamBuilder_setAllowedCapturePolicy)(pBuilder, ma_to_allowed_capture_policy__aaudio(pConfig->aaudio.allowedCapturePolicy)); - } - - ((MA_PFN_AAudioStreamBuilder_setDataCallback)pContext->aaudio.AAudioStreamBuilder_setDataCallback)(pBuilder, ma_stream_data_callback_playback__aaudio, (void*)pDevice); - } - - /* - If we set AAUDIO_PERFORMANCE_MODE_LOW_LATENCY, we allow for MMAP (non-legacy path). - Since there's a mapping between miniaudio's performance profiles and AAudio's performance modes, let's use it. - Beware though, with a conservative performance profile, AAudio will indeed take the legacy path. - */ - ((MA_PFN_AAudioStreamBuilder_setPerformanceMode)pContext->aaudio.AAudioStreamBuilder_setPerformanceMode)(pBuilder, (pConfig->performanceProfile == ma_performance_profile_low_latency) ? MA_AAUDIO_PERFORMANCE_MODE_LOW_LATENCY : MA_AAUDIO_PERFORMANCE_MODE_NONE); - - /* We need to set an error callback to detect device changes. */ - if (pDevice != NULL) { /* <-- pDevice should never be null if pDescriptor is not null, which is always the case if we hit this branch. Check anyway for safety. */ - ((MA_PFN_AAudioStreamBuilder_setErrorCallback)pContext->aaudio.AAudioStreamBuilder_setErrorCallback)(pBuilder, ma_stream_error_callback__aaudio, (void*)pDevice); - } - } - - *ppBuilder = pBuilder; - - return MA_SUCCESS; -} - -static ma_result ma_open_stream_and_close_builder__aaudio(ma_context* pContext, ma_AAudioStreamBuilder* pBuilder, ma_AAudioStream** ppStream) -{ - ma_result result; - - result = ma_result_from_aaudio(((MA_PFN_AAudioStreamBuilder_openStream)pContext->aaudio.AAudioStreamBuilder_openStream)(pBuilder, ppStream)); - ((MA_PFN_AAudioStreamBuilder_delete)pContext->aaudio.AAudioStreamBuilder_delete)(pBuilder); - - return result; -} - -static ma_result ma_open_stream_basic__aaudio(ma_context* pContext, const ma_device_id* pDeviceID, ma_device_type deviceType, ma_share_mode shareMode, ma_AAudioStream** ppStream) -{ - ma_result result; - ma_AAudioStreamBuilder* pBuilder; - - *ppStream = NULL; - - result = ma_create_and_configure_AAudioStreamBuilder__aaudio(pContext, pDeviceID, deviceType, shareMode, NULL, NULL, NULL, &pBuilder); - if (result != MA_SUCCESS) { - return result; - } - - /* Let's give AAudio a hint to avoid the legacy path (AudioStreamLegacy). */ - ((MA_PFN_AAudioStreamBuilder_setPerformanceMode)pContext->aaudio.AAudioStreamBuilder_setPerformanceMode)(pBuilder, MA_AAUDIO_PERFORMANCE_MODE_LOW_LATENCY); - - return ma_open_stream_and_close_builder__aaudio(pContext, pBuilder, ppStream); -} - -static ma_result ma_open_stream__aaudio(ma_device* pDevice, const ma_device_config* pConfig, ma_device_type deviceType, const ma_device_descriptor* pDescriptor, ma_AAudioStream** ppStream) -{ - ma_result result; - ma_AAudioStreamBuilder* pBuilder; - - MA_ASSERT(pDevice != NULL); - MA_ASSERT(pDescriptor != NULL); - MA_ASSERT(deviceType != ma_device_type_duplex); /* This function should not be called for a full-duplex device type. */ - - *ppStream = NULL; - - result = ma_create_and_configure_AAudioStreamBuilder__aaudio(pDevice->pContext, pDescriptor->pDeviceID, deviceType, pDescriptor->shareMode, pDescriptor, pConfig, pDevice, &pBuilder); - if (result != MA_SUCCESS) { - return result; - } - - return ma_open_stream_and_close_builder__aaudio(pDevice->pContext, pBuilder, ppStream); -} - -static ma_result ma_close_stream__aaudio(ma_context* pContext, ma_AAudioStream* pStream) -{ - if (pStream == NULL) { - return MA_INVALID_ARGS; - } - - return ma_result_from_aaudio(((MA_PFN_AAudioStream_close)pContext->aaudio.AAudioStream_close)(pStream)); -} - -static ma_bool32 ma_has_default_device__aaudio(ma_context* pContext, ma_device_type deviceType) -{ - /* The only way to know this is to try creating a stream. */ - ma_AAudioStream* pStream; - ma_result result = ma_open_stream_basic__aaudio(pContext, NULL, deviceType, ma_share_mode_shared, &pStream); - if (result != MA_SUCCESS) { - return MA_FALSE; - } - - ma_close_stream__aaudio(pContext, pStream); - return MA_TRUE; -} - -static ma_result ma_wait_for_simple_state_transition__aaudio(ma_context* pContext, ma_AAudioStream* pStream, ma_aaudio_stream_state_t oldState, ma_aaudio_stream_state_t newState) -{ - ma_aaudio_stream_state_t actualNewState; - ma_aaudio_result_t resultAA = ((MA_PFN_AAudioStream_waitForStateChange)pContext->aaudio.AAudioStream_waitForStateChange)(pStream, oldState, &actualNewState, 5000000000); /* 5 second timeout. */ - if (resultAA != MA_AAUDIO_OK) { - return ma_result_from_aaudio(resultAA); - } - - if (newState != actualNewState) { - return MA_ERROR; /* Failed to transition into the expected state. */ - } - - return MA_SUCCESS; -} - - -static ma_result ma_context_enumerate_devices__aaudio(ma_context* pContext, ma_enum_devices_callback_proc callback, void* pUserData) -{ - ma_bool32 cbResult = MA_TRUE; - - MA_ASSERT(pContext != NULL); - MA_ASSERT(callback != NULL); - - /* Unfortunately AAudio does not have an enumeration API. Therefore I'm only going to report default devices, but only if it can instantiate a stream. */ - - /* Playback. */ - if (cbResult) { - ma_device_info deviceInfo; - MA_ZERO_OBJECT(&deviceInfo); - deviceInfo.id.aaudio = MA_AAUDIO_UNSPECIFIED; - ma_strncpy_s(deviceInfo.name, sizeof(deviceInfo.name), MA_DEFAULT_PLAYBACK_DEVICE_NAME, (size_t)-1); - - if (ma_has_default_device__aaudio(pContext, ma_device_type_playback)) { - cbResult = callback(pContext, ma_device_type_playback, &deviceInfo, pUserData); - } - } - - /* Capture. */ - if (cbResult) { - ma_device_info deviceInfo; - MA_ZERO_OBJECT(&deviceInfo); - deviceInfo.id.aaudio = MA_AAUDIO_UNSPECIFIED; - ma_strncpy_s(deviceInfo.name, sizeof(deviceInfo.name), MA_DEFAULT_CAPTURE_DEVICE_NAME, (size_t)-1); - - if (ma_has_default_device__aaudio(pContext, ma_device_type_capture)) { - cbResult = callback(pContext, ma_device_type_capture, &deviceInfo, pUserData); - } - } - - return MA_SUCCESS; -} - -static void ma_context_add_native_data_format_from_AAudioStream_ex__aaudio(ma_context* pContext, ma_AAudioStream* pStream, ma_format format, ma_uint32 flags, ma_device_info* pDeviceInfo) -{ - MA_ASSERT(pContext != NULL); - MA_ASSERT(pStream != NULL); - MA_ASSERT(pDeviceInfo != NULL); - - pDeviceInfo->nativeDataFormats[pDeviceInfo->nativeDataFormatCount].format = format; - pDeviceInfo->nativeDataFormats[pDeviceInfo->nativeDataFormatCount].channels = ((MA_PFN_AAudioStream_getChannelCount)pContext->aaudio.AAudioStream_getChannelCount)(pStream); - pDeviceInfo->nativeDataFormats[pDeviceInfo->nativeDataFormatCount].sampleRate = ((MA_PFN_AAudioStream_getSampleRate)pContext->aaudio.AAudioStream_getSampleRate)(pStream); - pDeviceInfo->nativeDataFormats[pDeviceInfo->nativeDataFormatCount].flags = flags; - pDeviceInfo->nativeDataFormatCount += 1; -} - -static void ma_context_add_native_data_format_from_AAudioStream__aaudio(ma_context* pContext, ma_AAudioStream* pStream, ma_uint32 flags, ma_device_info* pDeviceInfo) -{ - /* AAudio supports s16 and f32. */ - ma_context_add_native_data_format_from_AAudioStream_ex__aaudio(pContext, pStream, ma_format_f32, flags, pDeviceInfo); - ma_context_add_native_data_format_from_AAudioStream_ex__aaudio(pContext, pStream, ma_format_s16, flags, pDeviceInfo); -} - -static ma_result ma_context_get_device_info__aaudio(ma_context* pContext, ma_device_type deviceType, const ma_device_id* pDeviceID, ma_device_info* pDeviceInfo) -{ - ma_AAudioStream* pStream; - ma_result result; - - MA_ASSERT(pContext != NULL); - - /* ID */ - if (pDeviceID != NULL) { - pDeviceInfo->id.aaudio = pDeviceID->aaudio; - } else { - pDeviceInfo->id.aaudio = MA_AAUDIO_UNSPECIFIED; - } - - /* Name */ - if (deviceType == ma_device_type_playback) { - ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), MA_DEFAULT_PLAYBACK_DEVICE_NAME, (size_t)-1); - } else { - ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), MA_DEFAULT_CAPTURE_DEVICE_NAME, (size_t)-1); - } - - - pDeviceInfo->nativeDataFormatCount = 0; - - /* We'll need to open the device to get accurate sample rate and channel count information. */ - result = ma_open_stream_basic__aaudio(pContext, pDeviceID, deviceType, ma_share_mode_shared, &pStream); - if (result != MA_SUCCESS) { - return result; - } - - ma_context_add_native_data_format_from_AAudioStream__aaudio(pContext, pStream, 0, pDeviceInfo); - - ma_close_stream__aaudio(pContext, pStream); - pStream = NULL; - - return MA_SUCCESS; -} - -static ma_result ma_close_streams__aaudio(ma_device* pDevice) -{ - MA_ASSERT(pDevice != NULL); - - /* When re-routing, streams may have been closed and never re-opened. Hence the extra checks below. */ - if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) { - ma_close_stream__aaudio(pDevice->pContext, (ma_AAudioStream*)pDevice->aaudio.pStreamCapture); - pDevice->aaudio.pStreamCapture = NULL; - } - if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) { - ma_close_stream__aaudio(pDevice->pContext, (ma_AAudioStream*)pDevice->aaudio.pStreamPlayback); - pDevice->aaudio.pStreamPlayback = NULL; - } - - return MA_SUCCESS; -} - -static ma_result ma_device_uninit__aaudio(ma_device* pDevice) -{ - MA_ASSERT(pDevice != NULL); - - /* Wait for any rerouting to finish before attempting to close the streams. */ - ma_mutex_lock(&pDevice->aaudio.rerouteLock); - { - ma_close_streams__aaudio(pDevice); - } - ma_mutex_unlock(&pDevice->aaudio.rerouteLock); - - /* Destroy re-routing lock. */ - ma_mutex_uninit(&pDevice->aaudio.rerouteLock); - - return MA_SUCCESS; -} - -static ma_result ma_device_init_by_type__aaudio(ma_device* pDevice, const ma_device_config* pConfig, ma_device_type deviceType, ma_device_descriptor* pDescriptor, ma_AAudioStream** ppStream) -{ - ma_result result; - int32_t bufferCapacityInFrames; - int32_t framesPerDataCallback; - ma_AAudioStream* pStream; - - MA_ASSERT(pDevice != NULL); - MA_ASSERT(pConfig != NULL); - MA_ASSERT(pDescriptor != NULL); - - *ppStream = NULL; /* Safety. */ - - /* First step is to open the stream. From there we'll be able to extract the internal configuration. */ - result = ma_open_stream__aaudio(pDevice, pConfig, deviceType, pDescriptor, &pStream); - if (result != MA_SUCCESS) { - return result; /* Failed to open the AAudio stream. */ - } - - /* Now extract the internal configuration. */ - pDescriptor->format = (((MA_PFN_AAudioStream_getFormat)pDevice->pContext->aaudio.AAudioStream_getFormat)(pStream) == MA_AAUDIO_FORMAT_PCM_I16) ? ma_format_s16 : ma_format_f32; - pDescriptor->channels = ((MA_PFN_AAudioStream_getChannelCount)pDevice->pContext->aaudio.AAudioStream_getChannelCount)(pStream); - pDescriptor->sampleRate = ((MA_PFN_AAudioStream_getSampleRate)pDevice->pContext->aaudio.AAudioStream_getSampleRate)(pStream); - - /* For the channel map we need to be sure we don't overflow any buffers. */ - if (pDescriptor->channels <= MA_MAX_CHANNELS) { - ma_channel_map_init_standard(ma_standard_channel_map_default, pDescriptor->channelMap, ma_countof(pDescriptor->channelMap), pDescriptor->channels); /* <-- Cannot find info on channel order, so assuming a default. */ - } else { - ma_channel_map_init_blank(pDescriptor->channelMap, MA_MAX_CHANNELS); /* Too many channels. Use a blank channel map. */ - } - - bufferCapacityInFrames = ((MA_PFN_AAudioStream_getBufferCapacityInFrames)pDevice->pContext->aaudio.AAudioStream_getBufferCapacityInFrames)(pStream); - framesPerDataCallback = ((MA_PFN_AAudioStream_getFramesPerDataCallback)pDevice->pContext->aaudio.AAudioStream_getFramesPerDataCallback)(pStream); - - if (framesPerDataCallback > 0) { - pDescriptor->periodSizeInFrames = framesPerDataCallback; - pDescriptor->periodCount = bufferCapacityInFrames / framesPerDataCallback; - } else { - pDescriptor->periodSizeInFrames = bufferCapacityInFrames; - pDescriptor->periodCount = 1; - } - - *ppStream = pStream; - - return MA_SUCCESS; -} - -static ma_result ma_device_init_streams__aaudio(ma_device* pDevice, const ma_device_config* pConfig, ma_device_descriptor* pDescriptorPlayback, ma_device_descriptor* pDescriptorCapture) -{ - ma_result result; - - MA_ASSERT(pDevice != NULL); - - if (pConfig->deviceType == ma_device_type_loopback) { - return MA_DEVICE_TYPE_NOT_SUPPORTED; - } - - pDevice->aaudio.usage = pConfig->aaudio.usage; - pDevice->aaudio.contentType = pConfig->aaudio.contentType; - pDevice->aaudio.inputPreset = pConfig->aaudio.inputPreset; - pDevice->aaudio.allowedCapturePolicy = pConfig->aaudio.allowedCapturePolicy; - pDevice->aaudio.noAutoStartAfterReroute = pConfig->aaudio.noAutoStartAfterReroute; - - if (pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) { - result = ma_device_init_by_type__aaudio(pDevice, pConfig, ma_device_type_capture, pDescriptorCapture, (ma_AAudioStream**)&pDevice->aaudio.pStreamCapture); - if (result != MA_SUCCESS) { - return result; - } - } - - if (pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) { - result = ma_device_init_by_type__aaudio(pDevice, pConfig, ma_device_type_playback, pDescriptorPlayback, (ma_AAudioStream**)&pDevice->aaudio.pStreamPlayback); - if (result != MA_SUCCESS) { - return result; - } - } - - return MA_SUCCESS; -} - -static ma_result ma_device_init__aaudio(ma_device* pDevice, const ma_device_config* pConfig, ma_device_descriptor* pDescriptorPlayback, ma_device_descriptor* pDescriptorCapture) -{ - ma_result result; - - MA_ASSERT(pDevice != NULL); - - result = ma_device_init_streams__aaudio(pDevice, pConfig, pDescriptorPlayback, pDescriptorCapture); - if (result != MA_SUCCESS) { - return result; - } - - result = ma_mutex_init(&pDevice->aaudio.rerouteLock); - if (result != MA_SUCCESS) { - return result; - } - - return MA_SUCCESS; -} - -static ma_result ma_device_start_stream__aaudio(ma_device* pDevice, ma_AAudioStream* pStream) -{ - ma_aaudio_result_t resultAA; - ma_aaudio_stream_state_t currentState; - - MA_ASSERT(pDevice != NULL); - - if (pStream == NULL) { - return MA_INVALID_ARGS; - } - - resultAA = ((MA_PFN_AAudioStream_requestStart)pDevice->pContext->aaudio.AAudioStream_requestStart)(pStream); - if (resultAA != MA_AAUDIO_OK) { - return ma_result_from_aaudio(resultAA); - } - - /* Do we actually need to wait for the device to transition into its started state? */ - - /* The device should be in either a starting or started state. If it's not set to started we need to wait for it to transition. It should go from starting to started. */ - currentState = ((MA_PFN_AAudioStream_getState)pDevice->pContext->aaudio.AAudioStream_getState)(pStream); - if (currentState != MA_AAUDIO_STREAM_STATE_STARTED) { - ma_result result; - - if (currentState != MA_AAUDIO_STREAM_STATE_STARTING) { - return MA_ERROR; /* Expecting the stream to be a starting or started state. */ - } - - result = ma_wait_for_simple_state_transition__aaudio(pDevice->pContext, pStream, currentState, MA_AAUDIO_STREAM_STATE_STARTED); - if (result != MA_SUCCESS) { - return result; - } - } - - return MA_SUCCESS; -} - -static ma_result ma_device_stop_stream__aaudio(ma_device* pDevice, ma_AAudioStream* pStream) -{ - ma_aaudio_result_t resultAA; - ma_aaudio_stream_state_t currentState; - - MA_ASSERT(pDevice != NULL); - - if (pStream == NULL) { - return MA_INVALID_ARGS; - } - - /* - From the AAudio documentation: - - The stream will stop after all of the data currently buffered has been played. - - This maps with miniaudio's requirement that device's be drained which means we don't need to implement any draining logic. - */ - currentState = ((MA_PFN_AAudioStream_getState)pDevice->pContext->aaudio.AAudioStream_getState)(pStream); - if (currentState == MA_AAUDIO_STREAM_STATE_DISCONNECTED) { - return MA_SUCCESS; /* The device is disconnected. Don't try stopping it. */ - } - - resultAA = ((MA_PFN_AAudioStream_requestStop)pDevice->pContext->aaudio.AAudioStream_requestStop)(pStream); - if (resultAA != MA_AAUDIO_OK) { - return ma_result_from_aaudio(resultAA); - } - - /* The device should be in either a stopping or stopped state. If it's not set to started we need to wait for it to transition. It should go from stopping to stopped. */ - currentState = ((MA_PFN_AAudioStream_getState)pDevice->pContext->aaudio.AAudioStream_getState)(pStream); - if (currentState != MA_AAUDIO_STREAM_STATE_STOPPED) { - ma_result result; - - if (currentState != MA_AAUDIO_STREAM_STATE_STOPPING) { - return MA_ERROR; /* Expecting the stream to be a stopping or stopped state. */ - } - - result = ma_wait_for_simple_state_transition__aaudio(pDevice->pContext, pStream, currentState, MA_AAUDIO_STREAM_STATE_STOPPED); - if (result != MA_SUCCESS) { - return result; - } - } - - return MA_SUCCESS; -} - -static ma_result ma_device_start__aaudio(ma_device* pDevice) -{ - MA_ASSERT(pDevice != NULL); - - if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) { - ma_result result = ma_device_start_stream__aaudio(pDevice, (ma_AAudioStream*)pDevice->aaudio.pStreamCapture); - if (result != MA_SUCCESS) { - return result; - } - } - - if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) { - ma_result result = ma_device_start_stream__aaudio(pDevice, (ma_AAudioStream*)pDevice->aaudio.pStreamPlayback); - if (result != MA_SUCCESS) { - if (pDevice->type == ma_device_type_duplex) { - ma_device_stop_stream__aaudio(pDevice, (ma_AAudioStream*)pDevice->aaudio.pStreamCapture); - } - return result; - } - } - - return MA_SUCCESS; -} - -static ma_result ma_device_stop__aaudio(ma_device* pDevice) -{ - MA_ASSERT(pDevice != NULL); - - if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) { - ma_result result = ma_device_stop_stream__aaudio(pDevice, (ma_AAudioStream*)pDevice->aaudio.pStreamCapture); - if (result != MA_SUCCESS) { - return result; - } - } - - if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) { - ma_result result = ma_device_stop_stream__aaudio(pDevice, (ma_AAudioStream*)pDevice->aaudio.pStreamPlayback); - if (result != MA_SUCCESS) { - return result; - } - } - - ma_device__on_notification_stopped(pDevice); - - return MA_SUCCESS; -} - -static ma_result ma_device_reinit__aaudio(ma_device* pDevice, ma_device_type deviceType) -{ - ma_result result; - int32_t retries = 0; - - MA_ASSERT(pDevice != NULL); - - /* - TODO: Stop retrying if main thread is about to uninit device. - */ - ma_mutex_lock(&pDevice->aaudio.rerouteLock); - { -error_disconnected: - /* The first thing to do is close the streams. */ - ma_close_streams__aaudio(pDevice); - - /* Now we need to reinitialize each streams. The hardest part with this is just filling output the config and descriptors. */ - ma_device_config deviceConfig; - ma_device_descriptor descriptorPlayback; - ma_device_descriptor descriptorCapture; - - deviceConfig = ma_device_config_init(deviceType); - deviceConfig.playback.pDeviceID = NULL; /* Only doing rerouting with default devices. */ - deviceConfig.playback.shareMode = pDevice->playback.shareMode; - deviceConfig.playback.format = pDevice->playback.format; - deviceConfig.playback.channels = pDevice->playback.channels; - deviceConfig.capture.pDeviceID = NULL; /* Only doing rerouting with default devices. */ - deviceConfig.capture.shareMode = pDevice->capture.shareMode; - deviceConfig.capture.format = pDevice->capture.format; - deviceConfig.capture.channels = pDevice->capture.channels; - deviceConfig.sampleRate = pDevice->sampleRate; - deviceConfig.aaudio.usage = pDevice->aaudio.usage; - deviceConfig.aaudio.contentType = pDevice->aaudio.contentType; - deviceConfig.aaudio.inputPreset = pDevice->aaudio.inputPreset; - deviceConfig.aaudio.allowedCapturePolicy = pDevice->aaudio.allowedCapturePolicy; - deviceConfig.aaudio.noAutoStartAfterReroute = pDevice->aaudio.noAutoStartAfterReroute; - deviceConfig.periods = 1; - - /* Try to get an accurate period size. */ - if (deviceType == ma_device_type_playback || deviceType == ma_device_type_duplex) { - deviceConfig.periodSizeInFrames = pDevice->playback.internalPeriodSizeInFrames; - } else { - deviceConfig.periodSizeInFrames = pDevice->capture.internalPeriodSizeInFrames; - } - - if (deviceType == ma_device_type_capture || deviceType == ma_device_type_duplex || deviceType == ma_device_type_loopback) { - descriptorCapture.pDeviceID = deviceConfig.capture.pDeviceID; - descriptorCapture.shareMode = deviceConfig.capture.shareMode; - descriptorCapture.format = deviceConfig.capture.format; - descriptorCapture.channels = deviceConfig.capture.channels; - descriptorCapture.sampleRate = deviceConfig.sampleRate; - descriptorCapture.periodSizeInFrames = deviceConfig.periodSizeInFrames; - descriptorCapture.periodCount = deviceConfig.periods; - } - - if (deviceType == ma_device_type_playback || deviceType == ma_device_type_duplex) { - descriptorPlayback.pDeviceID = deviceConfig.playback.pDeviceID; - descriptorPlayback.shareMode = deviceConfig.playback.shareMode; - descriptorPlayback.format = deviceConfig.playback.format; - descriptorPlayback.channels = deviceConfig.playback.channels; - descriptorPlayback.sampleRate = deviceConfig.sampleRate; - descriptorPlayback.periodSizeInFrames = deviceConfig.periodSizeInFrames; - descriptorPlayback.periodCount = deviceConfig.periods; - } - - result = ma_device_init_streams__aaudio(pDevice, &deviceConfig, &descriptorPlayback, &descriptorCapture); - if (result != MA_SUCCESS) { - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_WARNING, "[AAudio] Failed to create stream after route change."); - goto done; - } - - result = ma_device_post_init(pDevice, deviceType, &descriptorPlayback, &descriptorCapture); - if (result != MA_SUCCESS) { - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_WARNING, "[AAudio] Failed to initialize device after route change."); - ma_close_streams__aaudio(pDevice); - goto done; - } - - /* We'll only ever do this in response to a reroute. */ - ma_device__on_notification_rerouted(pDevice); - - /* If the device is started, start the streams. Maybe make this configurable? */ - if (ma_device_get_state(pDevice) == ma_device_state_started) { - if (pDevice->aaudio.noAutoStartAfterReroute == MA_FALSE) { - result = ma_device_start__aaudio(pDevice); - if (result != MA_SUCCESS) { - /* We got disconnected! Retry a few times, until we find a connected device! */ - retries += 1; - if (retries <= 3) { - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, "[AAudio] Failed to start stream after route change, retrying(%d)", retries); - goto error_disconnected; - } - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, "[AAudio] Failed to start stream after route change."); - goto done; - } - } else { - ma_device_stop(pDevice); /* Do a full device stop so we set internal state correctly. */ - } - } - - result = MA_SUCCESS; - } -done: - /* Re-routing done */ - ma_mutex_unlock(&pDevice->aaudio.rerouteLock); - - return result; -} - -static ma_result ma_device_get_info__aaudio(ma_device* pDevice, ma_device_type type, ma_device_info* pDeviceInfo) -{ - ma_AAudioStream* pStream = NULL; - - MA_ASSERT(pDevice != NULL); - MA_ASSERT(type != ma_device_type_duplex); - MA_ASSERT(pDeviceInfo != NULL); - - if (type == ma_device_type_capture) { - pStream = (ma_AAudioStream*)pDevice->aaudio.pStreamCapture; - pDeviceInfo->id.aaudio = pDevice->capture.id.aaudio; - ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), MA_DEFAULT_CAPTURE_DEVICE_NAME, (size_t)-1); /* Only supporting default devices. */ - } - if (type == ma_device_type_playback) { - pStream = (ma_AAudioStream*)pDevice->aaudio.pStreamPlayback; - pDeviceInfo->id.aaudio = pDevice->playback.id.aaudio; - ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), MA_DEFAULT_PLAYBACK_DEVICE_NAME, (size_t)-1); /* Only supporting default devices. */ - } - - /* Safety. Should never happen. */ - if (pStream == NULL) { - return MA_INVALID_OPERATION; - } - - pDeviceInfo->nativeDataFormatCount = 0; - ma_context_add_native_data_format_from_AAudioStream__aaudio(pDevice->pContext, pStream, 0, pDeviceInfo); - - return MA_SUCCESS; -} - - -static ma_result ma_context_uninit__aaudio(ma_context* pContext) -{ - MA_ASSERT(pContext != NULL); - MA_ASSERT(pContext->backend == ma_backend_aaudio); - - ma_device_job_thread_uninit(&pContext->aaudio.jobThread, &pContext->allocationCallbacks); - - ma_dlclose(ma_context_get_log(pContext), pContext->aaudio.hAAudio); - pContext->aaudio.hAAudio = NULL; - - return MA_SUCCESS; -} - -static ma_result ma_context_init__aaudio(ma_context* pContext, const ma_context_config* pConfig, ma_backend_callbacks* pCallbacks) -{ -#if !defined(MA_NO_RUNTIME_LINKING) - size_t i; - const char* libNames[] = { - "libaaudio.so" - }; - - for (i = 0; i < ma_countof(libNames); ++i) { - pContext->aaudio.hAAudio = ma_dlopen(ma_context_get_log(pContext), libNames[i]); - if (pContext->aaudio.hAAudio != NULL) { - break; - } - } - - if (pContext->aaudio.hAAudio == NULL) { - return MA_FAILED_TO_INIT_BACKEND; - } - - pContext->aaudio.AAudio_createStreamBuilder = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->aaudio.hAAudio, "AAudio_createStreamBuilder"); - pContext->aaudio.AAudioStreamBuilder_delete = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->aaudio.hAAudio, "AAudioStreamBuilder_delete"); - pContext->aaudio.AAudioStreamBuilder_setDeviceId = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->aaudio.hAAudio, "AAudioStreamBuilder_setDeviceId"); - pContext->aaudio.AAudioStreamBuilder_setDirection = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->aaudio.hAAudio, "AAudioStreamBuilder_setDirection"); - pContext->aaudio.AAudioStreamBuilder_setSharingMode = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->aaudio.hAAudio, "AAudioStreamBuilder_setSharingMode"); - pContext->aaudio.AAudioStreamBuilder_setFormat = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->aaudio.hAAudio, "AAudioStreamBuilder_setFormat"); - pContext->aaudio.AAudioStreamBuilder_setChannelCount = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->aaudio.hAAudio, "AAudioStreamBuilder_setChannelCount"); - pContext->aaudio.AAudioStreamBuilder_setSampleRate = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->aaudio.hAAudio, "AAudioStreamBuilder_setSampleRate"); - pContext->aaudio.AAudioStreamBuilder_setBufferCapacityInFrames = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->aaudio.hAAudio, "AAudioStreamBuilder_setBufferCapacityInFrames"); - pContext->aaudio.AAudioStreamBuilder_setFramesPerDataCallback = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->aaudio.hAAudio, "AAudioStreamBuilder_setFramesPerDataCallback"); - pContext->aaudio.AAudioStreamBuilder_setDataCallback = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->aaudio.hAAudio, "AAudioStreamBuilder_setDataCallback"); - pContext->aaudio.AAudioStreamBuilder_setErrorCallback = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->aaudio.hAAudio, "AAudioStreamBuilder_setErrorCallback"); - pContext->aaudio.AAudioStreamBuilder_setPerformanceMode = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->aaudio.hAAudio, "AAudioStreamBuilder_setPerformanceMode"); - pContext->aaudio.AAudioStreamBuilder_setUsage = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->aaudio.hAAudio, "AAudioStreamBuilder_setUsage"); - pContext->aaudio.AAudioStreamBuilder_setContentType = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->aaudio.hAAudio, "AAudioStreamBuilder_setContentType"); - pContext->aaudio.AAudioStreamBuilder_setInputPreset = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->aaudio.hAAudio, "AAudioStreamBuilder_setInputPreset"); - pContext->aaudio.AAudioStreamBuilder_setAllowedCapturePolicy = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->aaudio.hAAudio, "AAudioStreamBuilder_setAllowedCapturePolicy"); - pContext->aaudio.AAudioStreamBuilder_openStream = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->aaudio.hAAudio, "AAudioStreamBuilder_openStream"); - pContext->aaudio.AAudioStream_close = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->aaudio.hAAudio, "AAudioStream_close"); - pContext->aaudio.AAudioStream_getState = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->aaudio.hAAudio, "AAudioStream_getState"); - pContext->aaudio.AAudioStream_waitForStateChange = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->aaudio.hAAudio, "AAudioStream_waitForStateChange"); - pContext->aaudio.AAudioStream_getFormat = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->aaudio.hAAudio, "AAudioStream_getFormat"); - pContext->aaudio.AAudioStream_getChannelCount = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->aaudio.hAAudio, "AAudioStream_getChannelCount"); - pContext->aaudio.AAudioStream_getSampleRate = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->aaudio.hAAudio, "AAudioStream_getSampleRate"); - pContext->aaudio.AAudioStream_getBufferCapacityInFrames = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->aaudio.hAAudio, "AAudioStream_getBufferCapacityInFrames"); - pContext->aaudio.AAudioStream_getFramesPerDataCallback = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->aaudio.hAAudio, "AAudioStream_getFramesPerDataCallback"); - pContext->aaudio.AAudioStream_getFramesPerBurst = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->aaudio.hAAudio, "AAudioStream_getFramesPerBurst"); - pContext->aaudio.AAudioStream_requestStart = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->aaudio.hAAudio, "AAudioStream_requestStart"); - pContext->aaudio.AAudioStream_requestStop = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->aaudio.hAAudio, "AAudioStream_requestStop"); -#else - pContext->aaudio.AAudio_createStreamBuilder = (ma_proc)AAudio_createStreamBuilder; - pContext->aaudio.AAudioStreamBuilder_delete = (ma_proc)AAudioStreamBuilder_delete; - pContext->aaudio.AAudioStreamBuilder_setDeviceId = (ma_proc)AAudioStreamBuilder_setDeviceId; - pContext->aaudio.AAudioStreamBuilder_setDirection = (ma_proc)AAudioStreamBuilder_setDirection; - pContext->aaudio.AAudioStreamBuilder_setSharingMode = (ma_proc)AAudioStreamBuilder_setSharingMode; - pContext->aaudio.AAudioStreamBuilder_setFormat = (ma_proc)AAudioStreamBuilder_setFormat; - pContext->aaudio.AAudioStreamBuilder_setChannelCount = (ma_proc)AAudioStreamBuilder_setChannelCount; - pContext->aaudio.AAudioStreamBuilder_setSampleRate = (ma_proc)AAudioStreamBuilder_setSampleRate; - pContext->aaudio.AAudioStreamBuilder_setBufferCapacityInFrames = (ma_proc)AAudioStreamBuilder_setBufferCapacityInFrames; - pContext->aaudio.AAudioStreamBuilder_setFramesPerDataCallback = (ma_proc)AAudioStreamBuilder_setFramesPerDataCallback; - pContext->aaudio.AAudioStreamBuilder_setDataCallback = (ma_proc)AAudioStreamBuilder_setDataCallback; - pContext->aaudio.AAudioStreamBuilder_setErrorCallback = (ma_proc)AAudioStreamBuilder_setErrorCallback; - pContext->aaudio.AAudioStreamBuilder_setPerformanceMode = (ma_proc)AAudioStreamBuilder_setPerformanceMode; - pContext->aaudio.AAudioStreamBuilder_setUsage = (ma_proc)AAudioStreamBuilder_setUsage; - pContext->aaudio.AAudioStreamBuilder_setContentType = (ma_proc)AAudioStreamBuilder_setContentType; - pContext->aaudio.AAudioStreamBuilder_setInputPreset = (ma_proc)AAudioStreamBuilder_setInputPreset; - #if defined(__ANDROID_API__) && __ANDROID_API__ >= 29 - pContext->aaudio.AAudioStreamBuilder_setAllowedCapturePolicy = (ma_proc)AAudioStreamBuilder_setAllowedCapturePolicy; - #endif - pContext->aaudio.AAudioStreamBuilder_openStream = (ma_proc)AAudioStreamBuilder_openStream; - pContext->aaudio.AAudioStream_close = (ma_proc)AAudioStream_close; - pContext->aaudio.AAudioStream_getState = (ma_proc)AAudioStream_getState; - pContext->aaudio.AAudioStream_waitForStateChange = (ma_proc)AAudioStream_waitForStateChange; - pContext->aaudio.AAudioStream_getFormat = (ma_proc)AAudioStream_getFormat; - pContext->aaudio.AAudioStream_getChannelCount = (ma_proc)AAudioStream_getChannelCount; - pContext->aaudio.AAudioStream_getSampleRate = (ma_proc)AAudioStream_getSampleRate; - pContext->aaudio.AAudioStream_getBufferCapacityInFrames = (ma_proc)AAudioStream_getBufferCapacityInFrames; - pContext->aaudio.AAudioStream_getFramesPerDataCallback = (ma_proc)AAudioStream_getFramesPerDataCallback; - pContext->aaudio.AAudioStream_getFramesPerBurst = (ma_proc)AAudioStream_getFramesPerBurst; - pContext->aaudio.AAudioStream_requestStart = (ma_proc)AAudioStream_requestStart; - pContext->aaudio.AAudioStream_requestStop = (ma_proc)AAudioStream_requestStop; -#endif - - pCallbacks->onContextInit = ma_context_init__aaudio; - pCallbacks->onContextUninit = ma_context_uninit__aaudio; - pCallbacks->onContextEnumerateDevices = ma_context_enumerate_devices__aaudio; - pCallbacks->onContextGetDeviceInfo = ma_context_get_device_info__aaudio; - pCallbacks->onDeviceInit = ma_device_init__aaudio; - pCallbacks->onDeviceUninit = ma_device_uninit__aaudio; - pCallbacks->onDeviceStart = ma_device_start__aaudio; - pCallbacks->onDeviceStop = ma_device_stop__aaudio; - pCallbacks->onDeviceRead = NULL; /* Not used because AAudio is asynchronous. */ - pCallbacks->onDeviceWrite = NULL; /* Not used because AAudio is asynchronous. */ - pCallbacks->onDeviceDataLoop = NULL; /* Not used because AAudio is asynchronous. */ - pCallbacks->onDeviceGetInfo = ma_device_get_info__aaudio; - - - /* We need a job thread so we can deal with rerouting. */ - { - ma_result result; - ma_device_job_thread_config jobThreadConfig; - - jobThreadConfig = ma_device_job_thread_config_init(); - - result = ma_device_job_thread_init(&jobThreadConfig, &pContext->allocationCallbacks, &pContext->aaudio.jobThread); - if (result != MA_SUCCESS) { - ma_dlclose(ma_context_get_log(pContext), pContext->aaudio.hAAudio); - pContext->aaudio.hAAudio = NULL; - return result; - } - } - - - (void)pConfig; - return MA_SUCCESS; -} - -static ma_result ma_job_process__device__aaudio_reroute(ma_job* pJob) -{ - ma_result result; - ma_device* pDevice; - - MA_ASSERT(pJob != NULL); - - pDevice = (ma_device*)pJob->data.device.aaudio.reroute.pDevice; - MA_ASSERT(pDevice != NULL); - - /* Here is where we need to reroute the device. To do this we need to uninitialize the stream and reinitialize it. */ - result = ma_device_reinit__aaudio(pDevice, (ma_device_type)pJob->data.device.aaudio.reroute.deviceType); - if (result != MA_SUCCESS) { - /* - Getting here means we failed to reroute the device. The best thing I can think of here is to - just stop the device. - */ - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[AAudio] Stopping device due to reroute failure."); - ma_device_stop(pDevice); - return result; - } - - return MA_SUCCESS; -} -#else -/* Getting here means there is no AAudio backend so we need a no-op job implementation. */ -static ma_result ma_job_process__device__aaudio_reroute(ma_job* pJob) -{ - return ma_job_process__noop(pJob); -} -#endif /* AAudio */ - - -/****************************************************************************** - -OpenSL|ES Backend - -******************************************************************************/ -#ifdef MA_HAS_OPENSL -#include -#ifdef MA_ANDROID -#include -#endif - -typedef SLresult (SLAPIENTRY * ma_slCreateEngine_proc)(SLObjectItf* pEngine, SLuint32 numOptions, SLEngineOption* pEngineOptions, SLuint32 numInterfaces, SLInterfaceID* pInterfaceIds, SLboolean* pInterfaceRequired); - -/* OpenSL|ES has one-per-application objects :( */ -static SLObjectItf g_maEngineObjectSL = NULL; -static SLEngineItf g_maEngineSL = NULL; -static ma_uint32 g_maOpenSLInitCounter = 0; -static ma_spinlock g_maOpenSLSpinlock = 0; /* For init/uninit. */ - -#define MA_OPENSL_OBJ(p) (*((SLObjectItf)(p))) -#define MA_OPENSL_OUTPUTMIX(p) (*((SLOutputMixItf)(p))) -#define MA_OPENSL_PLAY(p) (*((SLPlayItf)(p))) -#define MA_OPENSL_RECORD(p) (*((SLRecordItf)(p))) - -#ifdef MA_ANDROID -#define MA_OPENSL_BUFFERQUEUE(p) (*((SLAndroidSimpleBufferQueueItf)(p))) -#else -#define MA_OPENSL_BUFFERQUEUE(p) (*((SLBufferQueueItf)(p))) -#endif - -static ma_result ma_result_from_OpenSL(SLuint32 result) -{ - switch (result) - { - case SL_RESULT_SUCCESS: return MA_SUCCESS; - case SL_RESULT_PRECONDITIONS_VIOLATED: return MA_ERROR; - case SL_RESULT_PARAMETER_INVALID: return MA_INVALID_ARGS; - case SL_RESULT_MEMORY_FAILURE: return MA_OUT_OF_MEMORY; - case SL_RESULT_RESOURCE_ERROR: return MA_INVALID_DATA; - case SL_RESULT_RESOURCE_LOST: return MA_ERROR; - case SL_RESULT_IO_ERROR: return MA_IO_ERROR; - case SL_RESULT_BUFFER_INSUFFICIENT: return MA_NO_SPACE; - case SL_RESULT_CONTENT_CORRUPTED: return MA_INVALID_DATA; - case SL_RESULT_CONTENT_UNSUPPORTED: return MA_FORMAT_NOT_SUPPORTED; - case SL_RESULT_CONTENT_NOT_FOUND: return MA_ERROR; - case SL_RESULT_PERMISSION_DENIED: return MA_ACCESS_DENIED; - case SL_RESULT_FEATURE_UNSUPPORTED: return MA_NOT_IMPLEMENTED; - case SL_RESULT_INTERNAL_ERROR: return MA_ERROR; - case SL_RESULT_UNKNOWN_ERROR: return MA_ERROR; - case SL_RESULT_OPERATION_ABORTED: return MA_ERROR; - case SL_RESULT_CONTROL_LOST: return MA_ERROR; - default: return MA_ERROR; - } -} - -/* Converts an individual OpenSL-style channel identifier (SL_SPEAKER_FRONT_LEFT, etc.) to miniaudio. */ -static ma_uint8 ma_channel_id_to_ma__opensl(SLuint32 id) -{ - switch (id) - { - case SL_SPEAKER_FRONT_LEFT: return MA_CHANNEL_FRONT_LEFT; - case SL_SPEAKER_FRONT_RIGHT: return MA_CHANNEL_FRONT_RIGHT; - case SL_SPEAKER_FRONT_CENTER: return MA_CHANNEL_FRONT_CENTER; - case SL_SPEAKER_LOW_FREQUENCY: return MA_CHANNEL_LFE; - case SL_SPEAKER_BACK_LEFT: return MA_CHANNEL_BACK_LEFT; - case SL_SPEAKER_BACK_RIGHT: return MA_CHANNEL_BACK_RIGHT; - case SL_SPEAKER_FRONT_LEFT_OF_CENTER: return MA_CHANNEL_FRONT_LEFT_CENTER; - case SL_SPEAKER_FRONT_RIGHT_OF_CENTER: return MA_CHANNEL_FRONT_RIGHT_CENTER; - case SL_SPEAKER_BACK_CENTER: return MA_CHANNEL_BACK_CENTER; - case SL_SPEAKER_SIDE_LEFT: return MA_CHANNEL_SIDE_LEFT; - case SL_SPEAKER_SIDE_RIGHT: return MA_CHANNEL_SIDE_RIGHT; - case SL_SPEAKER_TOP_CENTER: return MA_CHANNEL_TOP_CENTER; - case SL_SPEAKER_TOP_FRONT_LEFT: return MA_CHANNEL_TOP_FRONT_LEFT; - case SL_SPEAKER_TOP_FRONT_CENTER: return MA_CHANNEL_TOP_FRONT_CENTER; - case SL_SPEAKER_TOP_FRONT_RIGHT: return MA_CHANNEL_TOP_FRONT_RIGHT; - case SL_SPEAKER_TOP_BACK_LEFT: return MA_CHANNEL_TOP_BACK_LEFT; - case SL_SPEAKER_TOP_BACK_CENTER: return MA_CHANNEL_TOP_BACK_CENTER; - case SL_SPEAKER_TOP_BACK_RIGHT: return MA_CHANNEL_TOP_BACK_RIGHT; - default: return 0; - } -} - -/* Converts an individual miniaudio channel identifier (MA_CHANNEL_FRONT_LEFT, etc.) to OpenSL-style. */ -static SLuint32 ma_channel_id_to_opensl(ma_uint8 id) -{ - switch (id) - { - case MA_CHANNEL_MONO: return SL_SPEAKER_FRONT_CENTER; - case MA_CHANNEL_FRONT_LEFT: return SL_SPEAKER_FRONT_LEFT; - case MA_CHANNEL_FRONT_RIGHT: return SL_SPEAKER_FRONT_RIGHT; - case MA_CHANNEL_FRONT_CENTER: return SL_SPEAKER_FRONT_CENTER; - case MA_CHANNEL_LFE: return SL_SPEAKER_LOW_FREQUENCY; - case MA_CHANNEL_BACK_LEFT: return SL_SPEAKER_BACK_LEFT; - case MA_CHANNEL_BACK_RIGHT: return SL_SPEAKER_BACK_RIGHT; - case MA_CHANNEL_FRONT_LEFT_CENTER: return SL_SPEAKER_FRONT_LEFT_OF_CENTER; - case MA_CHANNEL_FRONT_RIGHT_CENTER: return SL_SPEAKER_FRONT_RIGHT_OF_CENTER; - case MA_CHANNEL_BACK_CENTER: return SL_SPEAKER_BACK_CENTER; - case MA_CHANNEL_SIDE_LEFT: return SL_SPEAKER_SIDE_LEFT; - case MA_CHANNEL_SIDE_RIGHT: return SL_SPEAKER_SIDE_RIGHT; - case MA_CHANNEL_TOP_CENTER: return SL_SPEAKER_TOP_CENTER; - case MA_CHANNEL_TOP_FRONT_LEFT: return SL_SPEAKER_TOP_FRONT_LEFT; - case MA_CHANNEL_TOP_FRONT_CENTER: return SL_SPEAKER_TOP_FRONT_CENTER; - case MA_CHANNEL_TOP_FRONT_RIGHT: return SL_SPEAKER_TOP_FRONT_RIGHT; - case MA_CHANNEL_TOP_BACK_LEFT: return SL_SPEAKER_TOP_BACK_LEFT; - case MA_CHANNEL_TOP_BACK_CENTER: return SL_SPEAKER_TOP_BACK_CENTER; - case MA_CHANNEL_TOP_BACK_RIGHT: return SL_SPEAKER_TOP_BACK_RIGHT; - default: return 0; - } -} - -/* Converts a channel mapping to an OpenSL-style channel mask. */ -static SLuint32 ma_channel_map_to_channel_mask__opensl(const ma_channel* pChannelMap, ma_uint32 channels) -{ - SLuint32 channelMask = 0; - ma_uint32 iChannel; - for (iChannel = 0; iChannel < channels; ++iChannel) { - channelMask |= ma_channel_id_to_opensl(pChannelMap[iChannel]); - } - - return channelMask; -} - -/* Converts an OpenSL-style channel mask to a miniaudio channel map. */ -static void ma_channel_mask_to_channel_map__opensl(SLuint32 channelMask, ma_uint32 channels, ma_channel* pChannelMap) -{ - if (channels == 1 && channelMask == 0) { - pChannelMap[0] = MA_CHANNEL_MONO; - } else if (channels == 2 && channelMask == 0) { - pChannelMap[0] = MA_CHANNEL_FRONT_LEFT; - pChannelMap[1] = MA_CHANNEL_FRONT_RIGHT; - } else { - if (channels == 1 && (channelMask & SL_SPEAKER_FRONT_CENTER) != 0) { - pChannelMap[0] = MA_CHANNEL_MONO; - } else { - /* Just iterate over each bit. */ - ma_uint32 iChannel = 0; - ma_uint32 iBit; - for (iBit = 0; iBit < 32 && iChannel < channels; ++iBit) { - SLuint32 bitValue = (channelMask & (1UL << iBit)); - if (bitValue != 0) { - /* The bit is set. */ - pChannelMap[iChannel] = ma_channel_id_to_ma__opensl(bitValue); - iChannel += 1; - } - } - } - } -} - -static SLuint32 ma_round_to_standard_sample_rate__opensl(SLuint32 samplesPerSec) -{ - if (samplesPerSec <= SL_SAMPLINGRATE_8) { - return SL_SAMPLINGRATE_8; - } - if (samplesPerSec <= SL_SAMPLINGRATE_11_025) { - return SL_SAMPLINGRATE_11_025; - } - if (samplesPerSec <= SL_SAMPLINGRATE_12) { - return SL_SAMPLINGRATE_12; - } - if (samplesPerSec <= SL_SAMPLINGRATE_16) { - return SL_SAMPLINGRATE_16; - } - if (samplesPerSec <= SL_SAMPLINGRATE_22_05) { - return SL_SAMPLINGRATE_22_05; - } - if (samplesPerSec <= SL_SAMPLINGRATE_24) { - return SL_SAMPLINGRATE_24; - } - if (samplesPerSec <= SL_SAMPLINGRATE_32) { - return SL_SAMPLINGRATE_32; - } - if (samplesPerSec <= SL_SAMPLINGRATE_44_1) { - return SL_SAMPLINGRATE_44_1; - } - if (samplesPerSec <= SL_SAMPLINGRATE_48) { - return SL_SAMPLINGRATE_48; - } - - /* Android doesn't support more than 48000. */ -#ifndef MA_ANDROID - if (samplesPerSec <= SL_SAMPLINGRATE_64) { - return SL_SAMPLINGRATE_64; - } - if (samplesPerSec <= SL_SAMPLINGRATE_88_2) { - return SL_SAMPLINGRATE_88_2; - } - if (samplesPerSec <= SL_SAMPLINGRATE_96) { - return SL_SAMPLINGRATE_96; - } - if (samplesPerSec <= SL_SAMPLINGRATE_192) { - return SL_SAMPLINGRATE_192; - } -#endif - - return SL_SAMPLINGRATE_16; -} - - -static SLint32 ma_to_stream_type__opensl(ma_opensl_stream_type streamType) -{ - switch (streamType) { - case ma_opensl_stream_type_voice: return SL_ANDROID_STREAM_VOICE; - case ma_opensl_stream_type_system: return SL_ANDROID_STREAM_SYSTEM; - case ma_opensl_stream_type_ring: return SL_ANDROID_STREAM_RING; - case ma_opensl_stream_type_media: return SL_ANDROID_STREAM_MEDIA; - case ma_opensl_stream_type_alarm: return SL_ANDROID_STREAM_ALARM; - case ma_opensl_stream_type_notification: return SL_ANDROID_STREAM_NOTIFICATION; - default: break; - } - - return SL_ANDROID_STREAM_VOICE; -} - -static SLint32 ma_to_recording_preset__opensl(ma_opensl_recording_preset recordingPreset) -{ - switch (recordingPreset) { - case ma_opensl_recording_preset_generic: return SL_ANDROID_RECORDING_PRESET_GENERIC; - case ma_opensl_recording_preset_camcorder: return SL_ANDROID_RECORDING_PRESET_CAMCORDER; - case ma_opensl_recording_preset_voice_recognition: return SL_ANDROID_RECORDING_PRESET_VOICE_RECOGNITION; - case ma_opensl_recording_preset_voice_communication: return SL_ANDROID_RECORDING_PRESET_VOICE_COMMUNICATION; - case ma_opensl_recording_preset_voice_unprocessed: return SL_ANDROID_RECORDING_PRESET_UNPROCESSED; - default: break; - } - - return SL_ANDROID_RECORDING_PRESET_NONE; -} - - -static ma_result ma_context_enumerate_devices__opensl(ma_context* pContext, ma_enum_devices_callback_proc callback, void* pUserData) -{ - ma_bool32 cbResult; - - MA_ASSERT(pContext != NULL); - MA_ASSERT(callback != NULL); - - MA_ASSERT(g_maOpenSLInitCounter > 0); /* <-- If you trigger this it means you've either not initialized the context, or you've uninitialized it and then attempted to enumerate devices. */ - if (g_maOpenSLInitCounter == 0) { - return MA_INVALID_OPERATION; - } - - /* - TODO: Test Me. - - This is currently untested, so for now we are just returning default devices. - */ -#if 0 && !defined(MA_ANDROID) - ma_bool32 isTerminated = MA_FALSE; - - SLuint32 pDeviceIDs[128]; - SLint32 deviceCount = sizeof(pDeviceIDs) / sizeof(pDeviceIDs[0]); - - SLAudioIODeviceCapabilitiesItf deviceCaps; - SLresult resultSL = (*g_maEngineObjectSL)->GetInterface(g_maEngineObjectSL, (SLInterfaceID)pContext->opensl.SL_IID_AUDIOIODEVICECAPABILITIES, &deviceCaps); - if (resultSL != SL_RESULT_SUCCESS) { - /* The interface may not be supported so just report a default device. */ - goto return_default_device; - } - - /* Playback */ - if (!isTerminated) { - resultSL = (*deviceCaps)->GetAvailableAudioOutputs(deviceCaps, &deviceCount, pDeviceIDs); - if (resultSL != SL_RESULT_SUCCESS) { - return ma_result_from_OpenSL(resultSL); - } - - for (SLint32 iDevice = 0; iDevice < deviceCount; ++iDevice) { - ma_device_info deviceInfo; - MA_ZERO_OBJECT(&deviceInfo); - deviceInfo.id.opensl = pDeviceIDs[iDevice]; - - SLAudioOutputDescriptor desc; - resultSL = (*deviceCaps)->QueryAudioOutputCapabilities(deviceCaps, deviceInfo.id.opensl, &desc); - if (resultSL == SL_RESULT_SUCCESS) { - ma_strncpy_s(deviceInfo.name, sizeof(deviceInfo.name), (const char*)desc.pDeviceName, (size_t)-1); - - ma_bool32 cbResult = callback(pContext, ma_device_type_playback, &deviceInfo, pUserData); - if (cbResult == MA_FALSE) { - isTerminated = MA_TRUE; - break; - } - } - } - } - - /* Capture */ - if (!isTerminated) { - resultSL = (*deviceCaps)->GetAvailableAudioInputs(deviceCaps, &deviceCount, pDeviceIDs); - if (resultSL != SL_RESULT_SUCCESS) { - return ma_result_from_OpenSL(resultSL); - } - - for (SLint32 iDevice = 0; iDevice < deviceCount; ++iDevice) { - ma_device_info deviceInfo; - MA_ZERO_OBJECT(&deviceInfo); - deviceInfo.id.opensl = pDeviceIDs[iDevice]; - - SLAudioInputDescriptor desc; - resultSL = (*deviceCaps)->QueryAudioInputCapabilities(deviceCaps, deviceInfo.id.opensl, &desc); - if (resultSL == SL_RESULT_SUCCESS) { - ma_strncpy_s(deviceInfo.name, sizeof(deviceInfo.name), (const char*)desc.deviceName, (size_t)-1); - - ma_bool32 cbResult = callback(pContext, ma_device_type_capture, &deviceInfo, pUserData); - if (cbResult == MA_FALSE) { - isTerminated = MA_TRUE; - break; - } - } - } - } - - return MA_SUCCESS; -#else - goto return_default_device; -#endif - -return_default_device:; - cbResult = MA_TRUE; - - /* Playback. */ - if (cbResult) { - ma_device_info deviceInfo; - MA_ZERO_OBJECT(&deviceInfo); - deviceInfo.id.opensl = SL_DEFAULTDEVICEID_AUDIOOUTPUT; - ma_strncpy_s(deviceInfo.name, sizeof(deviceInfo.name), MA_DEFAULT_PLAYBACK_DEVICE_NAME, (size_t)-1); - cbResult = callback(pContext, ma_device_type_playback, &deviceInfo, pUserData); - } - - /* Capture. */ - if (cbResult) { - ma_device_info deviceInfo; - MA_ZERO_OBJECT(&deviceInfo); - deviceInfo.id.opensl = SL_DEFAULTDEVICEID_AUDIOINPUT; - ma_strncpy_s(deviceInfo.name, sizeof(deviceInfo.name), MA_DEFAULT_CAPTURE_DEVICE_NAME, (size_t)-1); - cbResult = callback(pContext, ma_device_type_capture, &deviceInfo, pUserData); - } - - return MA_SUCCESS; -} - -static void ma_context_add_data_format_ex__opensl(ma_context* pContext, ma_format format, ma_uint32 channels, ma_uint32 sampleRate, ma_device_info* pDeviceInfo) -{ - MA_ASSERT(pContext != NULL); - MA_ASSERT(pDeviceInfo != NULL); - - pDeviceInfo->nativeDataFormats[pDeviceInfo->nativeDataFormatCount].format = format; - pDeviceInfo->nativeDataFormats[pDeviceInfo->nativeDataFormatCount].channels = channels; - pDeviceInfo->nativeDataFormats[pDeviceInfo->nativeDataFormatCount].sampleRate = sampleRate; - pDeviceInfo->nativeDataFormats[pDeviceInfo->nativeDataFormatCount].flags = 0; - pDeviceInfo->nativeDataFormatCount += 1; -} - -static void ma_context_add_data_format__opensl(ma_context* pContext, ma_format format, ma_device_info* pDeviceInfo) -{ - ma_uint32 minChannels = 1; - ma_uint32 maxChannels = 2; - ma_uint32 minSampleRate = (ma_uint32)ma_standard_sample_rate_8000; - ma_uint32 maxSampleRate = (ma_uint32)ma_standard_sample_rate_48000; - ma_uint32 iChannel; - ma_uint32 iSampleRate; - - MA_ASSERT(pContext != NULL); - MA_ASSERT(pDeviceInfo != NULL); - - /* - Each sample format can support mono and stereo, and we'll support a small subset of standard - rates (up to 48000). A better solution would be to somehow find a native sample rate. - */ - for (iChannel = minChannels; iChannel < maxChannels; iChannel += 1) { - for (iSampleRate = 0; iSampleRate < ma_countof(g_maStandardSampleRatePriorities); iSampleRate += 1) { - ma_uint32 standardSampleRate = g_maStandardSampleRatePriorities[iSampleRate]; - if (standardSampleRate >= minSampleRate && standardSampleRate <= maxSampleRate) { - ma_context_add_data_format_ex__opensl(pContext, format, iChannel, standardSampleRate, pDeviceInfo); - } - } - } -} - -static ma_result ma_context_get_device_info__opensl(ma_context* pContext, ma_device_type deviceType, const ma_device_id* pDeviceID, ma_device_info* pDeviceInfo) -{ - MA_ASSERT(pContext != NULL); - - MA_ASSERT(g_maOpenSLInitCounter > 0); /* <-- If you trigger this it means you've either not initialized the context, or you've uninitialized it and then attempted to get device info. */ - if (g_maOpenSLInitCounter == 0) { - return MA_INVALID_OPERATION; - } - - /* - TODO: Test Me. - - This is currently untested, so for now we are just returning default devices. - */ -#if 0 && !defined(MA_ANDROID) - SLAudioIODeviceCapabilitiesItf deviceCaps; - SLresult resultSL = (*g_maEngineObjectSL)->GetInterface(g_maEngineObjectSL, (SLInterfaceID)pContext->opensl.SL_IID_AUDIOIODEVICECAPABILITIES, &deviceCaps); - if (resultSL != SL_RESULT_SUCCESS) { - /* The interface may not be supported so just report a default device. */ - goto return_default_device; - } - - if (deviceType == ma_device_type_playback) { - SLAudioOutputDescriptor desc; - resultSL = (*deviceCaps)->QueryAudioOutputCapabilities(deviceCaps, pDeviceID->opensl, &desc); - if (resultSL != SL_RESULT_SUCCESS) { - return ma_result_from_OpenSL(resultSL); - } - - ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), (const char*)desc.pDeviceName, (size_t)-1); - } else { - SLAudioInputDescriptor desc; - resultSL = (*deviceCaps)->QueryAudioInputCapabilities(deviceCaps, pDeviceID->opensl, &desc); - if (resultSL != SL_RESULT_SUCCESS) { - return ma_result_from_OpenSL(resultSL); - } - - ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), (const char*)desc.deviceName, (size_t)-1); - } - - goto return_detailed_info; -#else - goto return_default_device; -#endif - -return_default_device: - if (pDeviceID != NULL) { - if ((deviceType == ma_device_type_playback && pDeviceID->opensl != SL_DEFAULTDEVICEID_AUDIOOUTPUT) || - (deviceType == ma_device_type_capture && pDeviceID->opensl != SL_DEFAULTDEVICEID_AUDIOINPUT)) { - return MA_NO_DEVICE; /* Don't know the device. */ - } - } - - /* ID and Name / Description */ - if (deviceType == ma_device_type_playback) { - pDeviceInfo->id.opensl = SL_DEFAULTDEVICEID_AUDIOOUTPUT; - ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), MA_DEFAULT_PLAYBACK_DEVICE_NAME, (size_t)-1); - } else { - pDeviceInfo->id.opensl = SL_DEFAULTDEVICEID_AUDIOINPUT; - ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), MA_DEFAULT_CAPTURE_DEVICE_NAME, (size_t)-1); - } - - pDeviceInfo->isDefault = MA_TRUE; - - goto return_detailed_info; - - -return_detailed_info: - - /* - For now we're just outputting a set of values that are supported by the API but not necessarily supported - by the device natively. Later on we should work on this so that it more closely reflects the device's - actual native format. - */ - pDeviceInfo->nativeDataFormatCount = 0; -#if defined(MA_ANDROID) && __ANDROID_API__ >= 21 - ma_context_add_data_format__opensl(pContext, ma_format_f32, pDeviceInfo); -#endif - ma_context_add_data_format__opensl(pContext, ma_format_s16, pDeviceInfo); - ma_context_add_data_format__opensl(pContext, ma_format_u8, pDeviceInfo); - - return MA_SUCCESS; -} - - -#ifdef MA_ANDROID -/*void ma_buffer_queue_callback_capture__opensl_android(SLAndroidSimpleBufferQueueItf pBufferQueue, SLuint32 eventFlags, const void* pBuffer, SLuint32 bufferSize, SLuint32 dataUsed, void* pContext)*/ -static void ma_buffer_queue_callback_capture__opensl_android(SLAndroidSimpleBufferQueueItf pBufferQueue, void* pUserData) -{ - ma_device* pDevice = (ma_device*)pUserData; - size_t periodSizeInBytes; - ma_uint8* pBuffer; - SLresult resultSL; - - MA_ASSERT(pDevice != NULL); - - (void)pBufferQueue; - - /* - For now, don't do anything unless the buffer was fully processed. From what I can tell, it looks like - OpenSL|ES 1.1 improves on buffer queues to the point that we could much more intelligently handle this, - but unfortunately it looks like Android is only supporting OpenSL|ES 1.0.1 for now :( - */ - - /* Don't do anything if the device is not started. */ - if (ma_device_get_state(pDevice) != ma_device_state_started) { - return; - } - - /* Don't do anything if the device is being drained. */ - if (pDevice->opensl.isDrainingCapture) { - return; - } - - periodSizeInBytes = pDevice->capture.internalPeriodSizeInFrames * ma_get_bytes_per_frame(pDevice->capture.internalFormat, pDevice->capture.internalChannels); - pBuffer = pDevice->opensl.pBufferCapture + (pDevice->opensl.currentBufferIndexCapture * periodSizeInBytes); - - ma_device_handle_backend_data_callback(pDevice, NULL, pBuffer, pDevice->capture.internalPeriodSizeInFrames); - - resultSL = MA_OPENSL_BUFFERQUEUE(pDevice->opensl.pBufferQueueCapture)->Enqueue((SLAndroidSimpleBufferQueueItf)pDevice->opensl.pBufferQueueCapture, pBuffer, periodSizeInBytes); - if (resultSL != SL_RESULT_SUCCESS) { - return; - } - - pDevice->opensl.currentBufferIndexCapture = (pDevice->opensl.currentBufferIndexCapture + 1) % pDevice->capture.internalPeriods; -} - -static void ma_buffer_queue_callback_playback__opensl_android(SLAndroidSimpleBufferQueueItf pBufferQueue, void* pUserData) -{ - ma_device* pDevice = (ma_device*)pUserData; - size_t periodSizeInBytes; - ma_uint8* pBuffer; - SLresult resultSL; - - MA_ASSERT(pDevice != NULL); - - (void)pBufferQueue; - - /* Don't do anything if the device is not started. */ - if (ma_device_get_state(pDevice) != ma_device_state_started) { - return; - } - - /* Don't do anything if the device is being drained. */ - if (pDevice->opensl.isDrainingPlayback) { - return; - } - - periodSizeInBytes = pDevice->playback.internalPeriodSizeInFrames * ma_get_bytes_per_frame(pDevice->playback.internalFormat, pDevice->playback.internalChannels); - pBuffer = pDevice->opensl.pBufferPlayback + (pDevice->opensl.currentBufferIndexPlayback * periodSizeInBytes); - - ma_device_handle_backend_data_callback(pDevice, pBuffer, NULL, pDevice->playback.internalPeriodSizeInFrames); - - resultSL = MA_OPENSL_BUFFERQUEUE(pDevice->opensl.pBufferQueuePlayback)->Enqueue((SLAndroidSimpleBufferQueueItf)pDevice->opensl.pBufferQueuePlayback, pBuffer, periodSizeInBytes); - if (resultSL != SL_RESULT_SUCCESS) { - return; - } - - pDevice->opensl.currentBufferIndexPlayback = (pDevice->opensl.currentBufferIndexPlayback + 1) % pDevice->playback.internalPeriods; -} -#endif - -static ma_result ma_device_uninit__opensl(ma_device* pDevice) -{ - MA_ASSERT(pDevice != NULL); - - MA_ASSERT(g_maOpenSLInitCounter > 0); /* <-- If you trigger this it means you've either not initialized the context, or you've uninitialized it before uninitializing the device. */ - if (g_maOpenSLInitCounter == 0) { - return MA_INVALID_OPERATION; - } - - if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) { - if (pDevice->opensl.pAudioRecorderObj) { - MA_OPENSL_OBJ(pDevice->opensl.pAudioRecorderObj)->Destroy((SLObjectItf)pDevice->opensl.pAudioRecorderObj); - } - - ma_free(pDevice->opensl.pBufferCapture, &pDevice->pContext->allocationCallbacks); - } - - if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) { - if (pDevice->opensl.pAudioPlayerObj) { - MA_OPENSL_OBJ(pDevice->opensl.pAudioPlayerObj)->Destroy((SLObjectItf)pDevice->opensl.pAudioPlayerObj); - } - if (pDevice->opensl.pOutputMixObj) { - MA_OPENSL_OBJ(pDevice->opensl.pOutputMixObj)->Destroy((SLObjectItf)pDevice->opensl.pOutputMixObj); - } - - ma_free(pDevice->opensl.pBufferPlayback, &pDevice->pContext->allocationCallbacks); - } - - return MA_SUCCESS; -} - -#if defined(MA_ANDROID) && __ANDROID_API__ >= 21 -typedef SLAndroidDataFormat_PCM_EX ma_SLDataFormat_PCM; -#else -typedef SLDataFormat_PCM ma_SLDataFormat_PCM; -#endif - -static ma_result ma_SLDataFormat_PCM_init__opensl(ma_format format, ma_uint32 channels, ma_uint32 sampleRate, const ma_channel* channelMap, ma_SLDataFormat_PCM* pDataFormat) -{ - /* We need to convert our format/channels/rate so that they aren't set to default. */ - if (format == ma_format_unknown) { - format = MA_DEFAULT_FORMAT; - } - if (channels == 0) { - channels = MA_DEFAULT_CHANNELS; - } - if (sampleRate == 0) { - sampleRate = MA_DEFAULT_SAMPLE_RATE; - } - -#if defined(MA_ANDROID) && __ANDROID_API__ >= 21 - if (format == ma_format_f32) { - pDataFormat->formatType = SL_ANDROID_DATAFORMAT_PCM_EX; - pDataFormat->representation = SL_ANDROID_PCM_REPRESENTATION_FLOAT; - } else { - pDataFormat->formatType = SL_DATAFORMAT_PCM; - } -#else - pDataFormat->formatType = SL_DATAFORMAT_PCM; -#endif - - pDataFormat->numChannels = channels; - ((SLDataFormat_PCM*)pDataFormat)->samplesPerSec = ma_round_to_standard_sample_rate__opensl(sampleRate * 1000); /* In millihertz. Annoyingly, the sample rate variable is named differently between SLAndroidDataFormat_PCM_EX and SLDataFormat_PCM */ - pDataFormat->bitsPerSample = ma_get_bytes_per_sample(format) * 8; - pDataFormat->channelMask = ma_channel_map_to_channel_mask__opensl(channelMap, channels); - pDataFormat->endianness = (ma_is_little_endian()) ? SL_BYTEORDER_LITTLEENDIAN : SL_BYTEORDER_BIGENDIAN; - - /* - Android has a few restrictions on the format as documented here: https://developer.android.com/ndk/guides/audio/opensl-for-android.html - - Only mono and stereo is supported. - - Only u8 and s16 formats are supported. - - Maximum sample rate of 48000. - */ -#ifdef MA_ANDROID - if (pDataFormat->numChannels > 2) { - pDataFormat->numChannels = 2; - } -#if __ANDROID_API__ >= 21 - if (pDataFormat->formatType == SL_ANDROID_DATAFORMAT_PCM_EX) { - /* It's floating point. */ - MA_ASSERT(pDataFormat->representation == SL_ANDROID_PCM_REPRESENTATION_FLOAT); - if (pDataFormat->bitsPerSample > 32) { - pDataFormat->bitsPerSample = 32; - } - } else { - if (pDataFormat->bitsPerSample > 16) { - pDataFormat->bitsPerSample = 16; - } - } -#else - if (pDataFormat->bitsPerSample > 16) { - pDataFormat->bitsPerSample = 16; - } -#endif - if (((SLDataFormat_PCM*)pDataFormat)->samplesPerSec > SL_SAMPLINGRATE_48) { - ((SLDataFormat_PCM*)pDataFormat)->samplesPerSec = SL_SAMPLINGRATE_48; - } -#endif - - pDataFormat->containerSize = pDataFormat->bitsPerSample; /* Always tightly packed for now. */ - - return MA_SUCCESS; -} - -static ma_result ma_deconstruct_SLDataFormat_PCM__opensl(ma_SLDataFormat_PCM* pDataFormat, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate, ma_channel* pChannelMap, size_t channelMapCap) -{ - ma_bool32 isFloatingPoint = MA_FALSE; -#if defined(MA_ANDROID) && __ANDROID_API__ >= 21 - if (pDataFormat->formatType == SL_ANDROID_DATAFORMAT_PCM_EX) { - MA_ASSERT(pDataFormat->representation == SL_ANDROID_PCM_REPRESENTATION_FLOAT); - isFloatingPoint = MA_TRUE; - } -#endif - if (isFloatingPoint) { - if (pDataFormat->bitsPerSample == 32) { - *pFormat = ma_format_f32; - } - } else { - if (pDataFormat->bitsPerSample == 8) { - *pFormat = ma_format_u8; - } else if (pDataFormat->bitsPerSample == 16) { - *pFormat = ma_format_s16; - } else if (pDataFormat->bitsPerSample == 24) { - *pFormat = ma_format_s24; - } else if (pDataFormat->bitsPerSample == 32) { - *pFormat = ma_format_s32; - } - } - - *pChannels = pDataFormat->numChannels; - *pSampleRate = ((SLDataFormat_PCM*)pDataFormat)->samplesPerSec / 1000; - ma_channel_mask_to_channel_map__opensl(pDataFormat->channelMask, ma_min(pDataFormat->numChannels, channelMapCap), pChannelMap); - - return MA_SUCCESS; -} - -static ma_result ma_device_init__opensl(ma_device* pDevice, const ma_device_config* pConfig, ma_device_descriptor* pDescriptorPlayback, ma_device_descriptor* pDescriptorCapture) -{ -#ifdef MA_ANDROID - SLDataLocator_AndroidSimpleBufferQueue queue; - SLresult resultSL; - size_t bufferSizeInBytes; - SLInterfaceID itfIDs[2]; - const SLboolean itfIDsRequired[] = { - SL_BOOLEAN_TRUE, /* SL_IID_ANDROIDSIMPLEBUFFERQUEUE */ - SL_BOOLEAN_FALSE /* SL_IID_ANDROIDCONFIGURATION */ - }; -#endif - - MA_ASSERT(g_maOpenSLInitCounter > 0); /* <-- If you trigger this it means you've either not initialized the context, or you've uninitialized it and then attempted to initialize a new device. */ - if (g_maOpenSLInitCounter == 0) { - return MA_INVALID_OPERATION; - } - - if (pConfig->deviceType == ma_device_type_loopback) { - return MA_DEVICE_TYPE_NOT_SUPPORTED; - } - - /* - For now, only supporting Android implementations of OpenSL|ES since that's the only one I've - been able to test with and I currently depend on Android-specific extensions (simple buffer - queues). - */ -#ifdef MA_ANDROID - itfIDs[0] = (SLInterfaceID)pDevice->pContext->opensl.SL_IID_ANDROIDSIMPLEBUFFERQUEUE; - itfIDs[1] = (SLInterfaceID)pDevice->pContext->opensl.SL_IID_ANDROIDCONFIGURATION; - - /* No exclusive mode with OpenSL|ES. */ - if (((pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) && pDescriptorPlayback->shareMode == ma_share_mode_exclusive) || - ((pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) && pDescriptorCapture->shareMode == ma_share_mode_exclusive)) { - return MA_SHARE_MODE_NOT_SUPPORTED; - } - - /* Now we can start initializing the device properly. */ - MA_ASSERT(pDevice != NULL); - MA_ZERO_OBJECT(&pDevice->opensl); - - queue.locatorType = SL_DATALOCATOR_ANDROIDSIMPLEBUFFERQUEUE; - - if (pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) { - ma_SLDataFormat_PCM pcm; - SLDataLocator_IODevice locatorDevice; - SLDataSource source; - SLDataSink sink; - SLAndroidConfigurationItf pRecorderConfig; - - ma_SLDataFormat_PCM_init__opensl(pDescriptorCapture->format, pDescriptorCapture->channels, pDescriptorCapture->sampleRate, pDescriptorCapture->channelMap, &pcm); - - locatorDevice.locatorType = SL_DATALOCATOR_IODEVICE; - locatorDevice.deviceType = SL_IODEVICE_AUDIOINPUT; - locatorDevice.deviceID = SL_DEFAULTDEVICEID_AUDIOINPUT; /* Must always use the default device with Android. */ - locatorDevice.device = NULL; - - source.pLocator = &locatorDevice; - source.pFormat = NULL; - - queue.numBuffers = pDescriptorCapture->periodCount; - - sink.pLocator = &queue; - sink.pFormat = (SLDataFormat_PCM*)&pcm; - - resultSL = (*g_maEngineSL)->CreateAudioRecorder(g_maEngineSL, (SLObjectItf*)&pDevice->opensl.pAudioRecorderObj, &source, &sink, ma_countof(itfIDs), itfIDs, itfIDsRequired); - if (resultSL == SL_RESULT_CONTENT_UNSUPPORTED || resultSL == SL_RESULT_PARAMETER_INVALID) { - /* Unsupported format. Fall back to something safer and try again. If this fails, just abort. */ - pcm.formatType = SL_DATAFORMAT_PCM; - pcm.numChannels = 1; - ((SLDataFormat_PCM*)&pcm)->samplesPerSec = SL_SAMPLINGRATE_16; /* The name of the sample rate variable is different between SLAndroidDataFormat_PCM_EX and SLDataFormat_PCM. */ - pcm.bitsPerSample = 16; - pcm.containerSize = pcm.bitsPerSample; /* Always tightly packed for now. */ - pcm.channelMask = 0; - resultSL = (*g_maEngineSL)->CreateAudioRecorder(g_maEngineSL, (SLObjectItf*)&pDevice->opensl.pAudioRecorderObj, &source, &sink, ma_countof(itfIDs), itfIDs, itfIDsRequired); - } - - if (resultSL != SL_RESULT_SUCCESS) { - ma_device_uninit__opensl(pDevice); - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[OpenSL] Failed to create audio recorder."); - return ma_result_from_OpenSL(resultSL); - } - - - /* Set the recording preset before realizing the player. */ - if (pConfig->opensl.recordingPreset != ma_opensl_recording_preset_default) { - resultSL = MA_OPENSL_OBJ(pDevice->opensl.pAudioRecorderObj)->GetInterface((SLObjectItf)pDevice->opensl.pAudioRecorderObj, (SLInterfaceID)pDevice->pContext->opensl.SL_IID_ANDROIDCONFIGURATION, &pRecorderConfig); - if (resultSL == SL_RESULT_SUCCESS) { - SLint32 recordingPreset = ma_to_recording_preset__opensl(pConfig->opensl.recordingPreset); - resultSL = (*pRecorderConfig)->SetConfiguration(pRecorderConfig, SL_ANDROID_KEY_RECORDING_PRESET, &recordingPreset, sizeof(SLint32)); - if (resultSL != SL_RESULT_SUCCESS) { - /* Failed to set the configuration. Just keep going. */ - } - } - } - - resultSL = MA_OPENSL_OBJ(pDevice->opensl.pAudioRecorderObj)->Realize((SLObjectItf)pDevice->opensl.pAudioRecorderObj, SL_BOOLEAN_FALSE); - if (resultSL != SL_RESULT_SUCCESS) { - ma_device_uninit__opensl(pDevice); - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[OpenSL] Failed to realize audio recorder."); - return ma_result_from_OpenSL(resultSL); - } - - resultSL = MA_OPENSL_OBJ(pDevice->opensl.pAudioRecorderObj)->GetInterface((SLObjectItf)pDevice->opensl.pAudioRecorderObj, (SLInterfaceID)pDevice->pContext->opensl.SL_IID_RECORD, &pDevice->opensl.pAudioRecorder); - if (resultSL != SL_RESULT_SUCCESS) { - ma_device_uninit__opensl(pDevice); - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[OpenSL] Failed to retrieve SL_IID_RECORD interface."); - return ma_result_from_OpenSL(resultSL); - } - - resultSL = MA_OPENSL_OBJ(pDevice->opensl.pAudioRecorderObj)->GetInterface((SLObjectItf)pDevice->opensl.pAudioRecorderObj, (SLInterfaceID)pDevice->pContext->opensl.SL_IID_ANDROIDSIMPLEBUFFERQUEUE, &pDevice->opensl.pBufferQueueCapture); - if (resultSL != SL_RESULT_SUCCESS) { - ma_device_uninit__opensl(pDevice); - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[OpenSL] Failed to retrieve SL_IID_ANDROIDSIMPLEBUFFERQUEUE interface."); - return ma_result_from_OpenSL(resultSL); - } - - resultSL = MA_OPENSL_BUFFERQUEUE(pDevice->opensl.pBufferQueueCapture)->RegisterCallback((SLAndroidSimpleBufferQueueItf)pDevice->opensl.pBufferQueueCapture, ma_buffer_queue_callback_capture__opensl_android, pDevice); - if (resultSL != SL_RESULT_SUCCESS) { - ma_device_uninit__opensl(pDevice); - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[OpenSL] Failed to register buffer queue callback."); - return ma_result_from_OpenSL(resultSL); - } - - /* The internal format is determined by the "pcm" object. */ - ma_deconstruct_SLDataFormat_PCM__opensl(&pcm, &pDescriptorCapture->format, &pDescriptorCapture->channels, &pDescriptorCapture->sampleRate, pDescriptorCapture->channelMap, ma_countof(pDescriptorCapture->channelMap)); - - /* Buffer. */ - pDescriptorCapture->periodSizeInFrames = ma_calculate_buffer_size_in_frames_from_descriptor(pDescriptorCapture, pDescriptorCapture->sampleRate, pConfig->performanceProfile); - pDevice->opensl.currentBufferIndexCapture = 0; - - bufferSizeInBytes = pDescriptorCapture->periodSizeInFrames * ma_get_bytes_per_frame(pDescriptorCapture->format, pDescriptorCapture->channels) * pDescriptorCapture->periodCount; - pDevice->opensl.pBufferCapture = (ma_uint8*)ma_calloc(bufferSizeInBytes, &pDevice->pContext->allocationCallbacks); - if (pDevice->opensl.pBufferCapture == NULL) { - ma_device_uninit__opensl(pDevice); - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[OpenSL] Failed to allocate memory for data buffer."); - return MA_OUT_OF_MEMORY; - } - MA_ZERO_MEMORY(pDevice->opensl.pBufferCapture, bufferSizeInBytes); - } - - if (pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) { - ma_SLDataFormat_PCM pcm; - SLDataSource source; - SLDataLocator_OutputMix outmixLocator; - SLDataSink sink; - SLAndroidConfigurationItf pPlayerConfig; - - ma_SLDataFormat_PCM_init__opensl(pDescriptorPlayback->format, pDescriptorPlayback->channels, pDescriptorPlayback->sampleRate, pDescriptorPlayback->channelMap, &pcm); - - resultSL = (*g_maEngineSL)->CreateOutputMix(g_maEngineSL, (SLObjectItf*)&pDevice->opensl.pOutputMixObj, 0, NULL, NULL); - if (resultSL != SL_RESULT_SUCCESS) { - ma_device_uninit__opensl(pDevice); - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[OpenSL] Failed to create output mix."); - return ma_result_from_OpenSL(resultSL); - } - - resultSL = MA_OPENSL_OBJ(pDevice->opensl.pOutputMixObj)->Realize((SLObjectItf)pDevice->opensl.pOutputMixObj, SL_BOOLEAN_FALSE); - if (resultSL != SL_RESULT_SUCCESS) { - ma_device_uninit__opensl(pDevice); - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[OpenSL] Failed to realize output mix object."); - return ma_result_from_OpenSL(resultSL); - } - - resultSL = MA_OPENSL_OBJ(pDevice->opensl.pOutputMixObj)->GetInterface((SLObjectItf)pDevice->opensl.pOutputMixObj, (SLInterfaceID)pDevice->pContext->opensl.SL_IID_OUTPUTMIX, &pDevice->opensl.pOutputMix); - if (resultSL != SL_RESULT_SUCCESS) { - ma_device_uninit__opensl(pDevice); - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[OpenSL] Failed to retrieve SL_IID_OUTPUTMIX interface."); - return ma_result_from_OpenSL(resultSL); - } - - /* Set the output device. */ - if (pDescriptorPlayback->pDeviceID != NULL) { - SLuint32 deviceID_OpenSL = pDescriptorPlayback->pDeviceID->opensl; - MA_OPENSL_OUTPUTMIX(pDevice->opensl.pOutputMix)->ReRoute((SLOutputMixItf)pDevice->opensl.pOutputMix, 1, &deviceID_OpenSL); - } - - queue.numBuffers = pDescriptorPlayback->periodCount; - - source.pLocator = &queue; - source.pFormat = (SLDataFormat_PCM*)&pcm; - - outmixLocator.locatorType = SL_DATALOCATOR_OUTPUTMIX; - outmixLocator.outputMix = (SLObjectItf)pDevice->opensl.pOutputMixObj; - - sink.pLocator = &outmixLocator; - sink.pFormat = NULL; - - resultSL = (*g_maEngineSL)->CreateAudioPlayer(g_maEngineSL, (SLObjectItf*)&pDevice->opensl.pAudioPlayerObj, &source, &sink, ma_countof(itfIDs), itfIDs, itfIDsRequired); - if (resultSL == SL_RESULT_CONTENT_UNSUPPORTED || resultSL == SL_RESULT_PARAMETER_INVALID) { - /* Unsupported format. Fall back to something safer and try again. If this fails, just abort. */ - pcm.formatType = SL_DATAFORMAT_PCM; - pcm.numChannels = 2; - ((SLDataFormat_PCM*)&pcm)->samplesPerSec = SL_SAMPLINGRATE_16; - pcm.bitsPerSample = 16; - pcm.containerSize = pcm.bitsPerSample; /* Always tightly packed for now. */ - pcm.channelMask = SL_SPEAKER_FRONT_LEFT | SL_SPEAKER_FRONT_RIGHT; - resultSL = (*g_maEngineSL)->CreateAudioPlayer(g_maEngineSL, (SLObjectItf*)&pDevice->opensl.pAudioPlayerObj, &source, &sink, ma_countof(itfIDs), itfIDs, itfIDsRequired); - } - - if (resultSL != SL_RESULT_SUCCESS) { - ma_device_uninit__opensl(pDevice); - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[OpenSL] Failed to create audio player."); - return ma_result_from_OpenSL(resultSL); - } - - - /* Set the stream type before realizing the player. */ - if (pConfig->opensl.streamType != ma_opensl_stream_type_default) { - resultSL = MA_OPENSL_OBJ(pDevice->opensl.pAudioPlayerObj)->GetInterface((SLObjectItf)pDevice->opensl.pAudioPlayerObj, (SLInterfaceID)pDevice->pContext->opensl.SL_IID_ANDROIDCONFIGURATION, &pPlayerConfig); - if (resultSL == SL_RESULT_SUCCESS) { - SLint32 streamType = ma_to_stream_type__opensl(pConfig->opensl.streamType); - resultSL = (*pPlayerConfig)->SetConfiguration(pPlayerConfig, SL_ANDROID_KEY_STREAM_TYPE, &streamType, sizeof(SLint32)); - if (resultSL != SL_RESULT_SUCCESS) { - /* Failed to set the configuration. Just keep going. */ - } - } - } - - resultSL = MA_OPENSL_OBJ(pDevice->opensl.pAudioPlayerObj)->Realize((SLObjectItf)pDevice->opensl.pAudioPlayerObj, SL_BOOLEAN_FALSE); - if (resultSL != SL_RESULT_SUCCESS) { - ma_device_uninit__opensl(pDevice); - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[OpenSL] Failed to realize audio player."); - return ma_result_from_OpenSL(resultSL); - } - - resultSL = MA_OPENSL_OBJ(pDevice->opensl.pAudioPlayerObj)->GetInterface((SLObjectItf)pDevice->opensl.pAudioPlayerObj, (SLInterfaceID)pDevice->pContext->opensl.SL_IID_PLAY, &pDevice->opensl.pAudioPlayer); - if (resultSL != SL_RESULT_SUCCESS) { - ma_device_uninit__opensl(pDevice); - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[OpenSL] Failed to retrieve SL_IID_PLAY interface."); - return ma_result_from_OpenSL(resultSL); - } - - resultSL = MA_OPENSL_OBJ(pDevice->opensl.pAudioPlayerObj)->GetInterface((SLObjectItf)pDevice->opensl.pAudioPlayerObj, (SLInterfaceID)pDevice->pContext->opensl.SL_IID_ANDROIDSIMPLEBUFFERQUEUE, &pDevice->opensl.pBufferQueuePlayback); - if (resultSL != SL_RESULT_SUCCESS) { - ma_device_uninit__opensl(pDevice); - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[OpenSL] Failed to retrieve SL_IID_ANDROIDSIMPLEBUFFERQUEUE interface."); - return ma_result_from_OpenSL(resultSL); - } - - resultSL = MA_OPENSL_BUFFERQUEUE(pDevice->opensl.pBufferQueuePlayback)->RegisterCallback((SLAndroidSimpleBufferQueueItf)pDevice->opensl.pBufferQueuePlayback, ma_buffer_queue_callback_playback__opensl_android, pDevice); - if (resultSL != SL_RESULT_SUCCESS) { - ma_device_uninit__opensl(pDevice); - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[OpenSL] Failed to register buffer queue callback."); - return ma_result_from_OpenSL(resultSL); - } - - /* The internal format is determined by the "pcm" object. */ - ma_deconstruct_SLDataFormat_PCM__opensl(&pcm, &pDescriptorPlayback->format, &pDescriptorPlayback->channels, &pDescriptorPlayback->sampleRate, pDescriptorPlayback->channelMap, ma_countof(pDescriptorPlayback->channelMap)); - - /* Buffer. */ - pDescriptorPlayback->periodSizeInFrames = ma_calculate_buffer_size_in_frames_from_descriptor(pDescriptorPlayback, pDescriptorPlayback->sampleRate, pConfig->performanceProfile); - pDevice->opensl.currentBufferIndexPlayback = 0; - - bufferSizeInBytes = pDescriptorPlayback->periodSizeInFrames * ma_get_bytes_per_frame(pDescriptorPlayback->format, pDescriptorPlayback->channels) * pDescriptorPlayback->periodCount; - pDevice->opensl.pBufferPlayback = (ma_uint8*)ma_calloc(bufferSizeInBytes, &pDevice->pContext->allocationCallbacks); - if (pDevice->opensl.pBufferPlayback == NULL) { - ma_device_uninit__opensl(pDevice); - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[OpenSL] Failed to allocate memory for data buffer."); - return MA_OUT_OF_MEMORY; - } - MA_ZERO_MEMORY(pDevice->opensl.pBufferPlayback, bufferSizeInBytes); - } - - return MA_SUCCESS; -#else - return MA_NO_BACKEND; /* Non-Android implementations are not supported. */ -#endif -} - -static ma_result ma_device_start__opensl(ma_device* pDevice) -{ - SLresult resultSL; - size_t periodSizeInBytes; - ma_uint32 iPeriod; - - MA_ASSERT(pDevice != NULL); - - MA_ASSERT(g_maOpenSLInitCounter > 0); /* <-- If you trigger this it means you've either not initialized the context, or you've uninitialized it and then attempted to start the device. */ - if (g_maOpenSLInitCounter == 0) { - return MA_INVALID_OPERATION; - } - - if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) { - resultSL = MA_OPENSL_RECORD(pDevice->opensl.pAudioRecorder)->SetRecordState((SLRecordItf)pDevice->opensl.pAudioRecorder, SL_RECORDSTATE_RECORDING); - if (resultSL != SL_RESULT_SUCCESS) { - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[OpenSL] Failed to start internal capture device."); - return ma_result_from_OpenSL(resultSL); - } - - periodSizeInBytes = pDevice->capture.internalPeriodSizeInFrames * ma_get_bytes_per_frame(pDevice->capture.internalFormat, pDevice->capture.internalChannels); - for (iPeriod = 0; iPeriod < pDevice->capture.internalPeriods; ++iPeriod) { - resultSL = MA_OPENSL_BUFFERQUEUE(pDevice->opensl.pBufferQueueCapture)->Enqueue((SLAndroidSimpleBufferQueueItf)pDevice->opensl.pBufferQueueCapture, pDevice->opensl.pBufferCapture + (periodSizeInBytes * iPeriod), periodSizeInBytes); - if (resultSL != SL_RESULT_SUCCESS) { - MA_OPENSL_RECORD(pDevice->opensl.pAudioRecorder)->SetRecordState((SLRecordItf)pDevice->opensl.pAudioRecorder, SL_RECORDSTATE_STOPPED); - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[OpenSL] Failed to enqueue buffer for capture device."); - return ma_result_from_OpenSL(resultSL); - } - } - } - - if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) { - resultSL = MA_OPENSL_PLAY(pDevice->opensl.pAudioPlayer)->SetPlayState((SLPlayItf)pDevice->opensl.pAudioPlayer, SL_PLAYSTATE_PLAYING); - if (resultSL != SL_RESULT_SUCCESS) { - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[OpenSL] Failed to start internal playback device."); - return ma_result_from_OpenSL(resultSL); - } - - /* In playback mode (no duplex) we need to load some initial buffers. In duplex mode we need to enqueue silent buffers. */ - if (pDevice->type == ma_device_type_duplex) { - MA_ZERO_MEMORY(pDevice->opensl.pBufferPlayback, pDevice->playback.internalPeriodSizeInFrames * pDevice->playback.internalPeriods * ma_get_bytes_per_frame(pDevice->playback.internalFormat, pDevice->playback.internalChannels)); - } else { - ma_device__read_frames_from_client(pDevice, pDevice->playback.internalPeriodSizeInFrames * pDevice->playback.internalPeriods, pDevice->opensl.pBufferPlayback); - } - - periodSizeInBytes = pDevice->playback.internalPeriodSizeInFrames * ma_get_bytes_per_frame(pDevice->playback.internalFormat, pDevice->playback.internalChannels); - for (iPeriod = 0; iPeriod < pDevice->playback.internalPeriods; ++iPeriod) { - resultSL = MA_OPENSL_BUFFERQUEUE(pDevice->opensl.pBufferQueuePlayback)->Enqueue((SLAndroidSimpleBufferQueueItf)pDevice->opensl.pBufferQueuePlayback, pDevice->opensl.pBufferPlayback + (periodSizeInBytes * iPeriod), periodSizeInBytes); - if (resultSL != SL_RESULT_SUCCESS) { - MA_OPENSL_PLAY(pDevice->opensl.pAudioPlayer)->SetPlayState((SLPlayItf)pDevice->opensl.pAudioPlayer, SL_PLAYSTATE_STOPPED); - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[OpenSL] Failed to enqueue buffer for playback device."); - return ma_result_from_OpenSL(resultSL); - } - } - } - - return MA_SUCCESS; -} - -static ma_result ma_device_drain__opensl(ma_device* pDevice, ma_device_type deviceType) -{ - SLAndroidSimpleBufferQueueItf pBufferQueue; - - MA_ASSERT(deviceType == ma_device_type_capture || deviceType == ma_device_type_playback); - - if (pDevice->type == ma_device_type_capture) { - pBufferQueue = (SLAndroidSimpleBufferQueueItf)pDevice->opensl.pBufferQueueCapture; - pDevice->opensl.isDrainingCapture = MA_TRUE; - } else { - pBufferQueue = (SLAndroidSimpleBufferQueueItf)pDevice->opensl.pBufferQueuePlayback; - pDevice->opensl.isDrainingPlayback = MA_TRUE; - } - - for (;;) { - SLAndroidSimpleBufferQueueState state; - - MA_OPENSL_BUFFERQUEUE(pBufferQueue)->GetState(pBufferQueue, &state); - if (state.count == 0) { - break; - } - - ma_sleep(10); - } - - if (pDevice->type == ma_device_type_capture) { - pDevice->opensl.isDrainingCapture = MA_FALSE; - } else { - pDevice->opensl.isDrainingPlayback = MA_FALSE; - } - - return MA_SUCCESS; -} - -static ma_result ma_device_stop__opensl(ma_device* pDevice) -{ - SLresult resultSL; - - MA_ASSERT(pDevice != NULL); - - MA_ASSERT(g_maOpenSLInitCounter > 0); /* <-- If you trigger this it means you've either not initialized the context, or you've uninitialized it before stopping/uninitializing the device. */ - if (g_maOpenSLInitCounter == 0) { - return MA_INVALID_OPERATION; - } - - if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) { - ma_device_drain__opensl(pDevice, ma_device_type_capture); - - resultSL = MA_OPENSL_RECORD(pDevice->opensl.pAudioRecorder)->SetRecordState((SLRecordItf)pDevice->opensl.pAudioRecorder, SL_RECORDSTATE_STOPPED); - if (resultSL != SL_RESULT_SUCCESS) { - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[OpenSL] Failed to stop internal capture device."); - return ma_result_from_OpenSL(resultSL); - } - - MA_OPENSL_BUFFERQUEUE(pDevice->opensl.pBufferQueueCapture)->Clear((SLAndroidSimpleBufferQueueItf)pDevice->opensl.pBufferQueueCapture); - } - - if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) { - ma_device_drain__opensl(pDevice, ma_device_type_playback); - - resultSL = MA_OPENSL_PLAY(pDevice->opensl.pAudioPlayer)->SetPlayState((SLPlayItf)pDevice->opensl.pAudioPlayer, SL_PLAYSTATE_STOPPED); - if (resultSL != SL_RESULT_SUCCESS) { - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[OpenSL] Failed to stop internal playback device."); - return ma_result_from_OpenSL(resultSL); - } - - MA_OPENSL_BUFFERQUEUE(pDevice->opensl.pBufferQueuePlayback)->Clear((SLAndroidSimpleBufferQueueItf)pDevice->opensl.pBufferQueuePlayback); - } - - /* Make sure the client is aware that the device has stopped. There may be an OpenSL|ES callback for this, but I haven't found it. */ - ma_device__on_notification_stopped(pDevice); - - return MA_SUCCESS; -} - - -static ma_result ma_context_uninit__opensl(ma_context* pContext) -{ - MA_ASSERT(pContext != NULL); - MA_ASSERT(pContext->backend == ma_backend_opensl); - (void)pContext; - - /* Uninit global data. */ - ma_spinlock_lock(&g_maOpenSLSpinlock); - { - MA_ASSERT(g_maOpenSLInitCounter > 0); /* If you've triggered this, it means you have ma_context_init/uninit mismatch. Each successful call to ma_context_init() must be matched up with a call to ma_context_uninit(). */ - - g_maOpenSLInitCounter -= 1; - if (g_maOpenSLInitCounter == 0) { - (*g_maEngineObjectSL)->Destroy(g_maEngineObjectSL); - } - } - ma_spinlock_unlock(&g_maOpenSLSpinlock); - - return MA_SUCCESS; -} - -static ma_result ma_dlsym_SLInterfaceID__opensl(ma_context* pContext, const char* pName, ma_handle* pHandle) -{ - /* We need to return an error if the symbol cannot be found. This is important because there have been reports that some symbols do not exist. */ - ma_handle* p = (ma_handle*)ma_dlsym(ma_context_get_log(pContext), pContext->opensl.libOpenSLES, pName); - if (p == NULL) { - ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_INFO, "[OpenSL] Cannot find symbol %s", pName); - return MA_NO_BACKEND; - } - - *pHandle = *p; - return MA_SUCCESS; -} - -static ma_result ma_context_init_engine_nolock__opensl(ma_context* pContext) -{ - g_maOpenSLInitCounter += 1; - if (g_maOpenSLInitCounter == 1) { - SLresult resultSL; - - resultSL = ((ma_slCreateEngine_proc)pContext->opensl.slCreateEngine)(&g_maEngineObjectSL, 0, NULL, 0, NULL, NULL); - if (resultSL != SL_RESULT_SUCCESS) { - g_maOpenSLInitCounter -= 1; - return ma_result_from_OpenSL(resultSL); - } - - (*g_maEngineObjectSL)->Realize(g_maEngineObjectSL, SL_BOOLEAN_FALSE); - - resultSL = (*g_maEngineObjectSL)->GetInterface(g_maEngineObjectSL, (SLInterfaceID)pContext->opensl.SL_IID_ENGINE, &g_maEngineSL); - if (resultSL != SL_RESULT_SUCCESS) { - (*g_maEngineObjectSL)->Destroy(g_maEngineObjectSL); - g_maOpenSLInitCounter -= 1; - return ma_result_from_OpenSL(resultSL); - } - } - - return MA_SUCCESS; -} - -static ma_result ma_context_init__opensl(ma_context* pContext, const ma_context_config* pConfig, ma_backend_callbacks* pCallbacks) -{ - ma_result result; - -#if !defined(MA_NO_RUNTIME_LINKING) - size_t i; - const char* libOpenSLESNames[] = { - "libOpenSLES.so" - }; -#endif - - MA_ASSERT(pContext != NULL); - - (void)pConfig; - -#if !defined(MA_NO_RUNTIME_LINKING) - /* - Dynamically link against libOpenSLES.so. I have now had multiple reports that SL_IID_ANDROIDSIMPLEBUFFERQUEUE cannot be found. One - report was happening at compile time and another at runtime. To try working around this, I'm going to link to libOpenSLES at runtime - and extract the symbols rather than reference them directly. This should, hopefully, fix these issues as the compiler won't see any - references to the symbols and will hopefully skip the checks. - */ - for (i = 0; i < ma_countof(libOpenSLESNames); i += 1) { - pContext->opensl.libOpenSLES = ma_dlopen(ma_context_get_log(pContext), libOpenSLESNames[i]); - if (pContext->opensl.libOpenSLES != NULL) { - break; - } - } - - if (pContext->opensl.libOpenSLES == NULL) { - ma_log_post(ma_context_get_log(pContext), MA_LOG_LEVEL_INFO, "[OpenSL] Could not find libOpenSLES.so"); - return MA_NO_BACKEND; - } - - result = ma_dlsym_SLInterfaceID__opensl(pContext, "SL_IID_ENGINE", &pContext->opensl.SL_IID_ENGINE); - if (result != MA_SUCCESS) { - ma_dlclose(ma_context_get_log(pContext), pContext->opensl.libOpenSLES); - return result; - } - - result = ma_dlsym_SLInterfaceID__opensl(pContext, "SL_IID_AUDIOIODEVICECAPABILITIES", &pContext->opensl.SL_IID_AUDIOIODEVICECAPABILITIES); - if (result != MA_SUCCESS) { - ma_dlclose(ma_context_get_log(pContext), pContext->opensl.libOpenSLES); - return result; - } - - result = ma_dlsym_SLInterfaceID__opensl(pContext, "SL_IID_ANDROIDSIMPLEBUFFERQUEUE", &pContext->opensl.SL_IID_ANDROIDSIMPLEBUFFERQUEUE); - if (result != MA_SUCCESS) { - ma_dlclose(ma_context_get_log(pContext), pContext->opensl.libOpenSLES); - return result; - } - - result = ma_dlsym_SLInterfaceID__opensl(pContext, "SL_IID_RECORD", &pContext->opensl.SL_IID_RECORD); - if (result != MA_SUCCESS) { - ma_dlclose(ma_context_get_log(pContext), pContext->opensl.libOpenSLES); - return result; - } - - result = ma_dlsym_SLInterfaceID__opensl(pContext, "SL_IID_PLAY", &pContext->opensl.SL_IID_PLAY); - if (result != MA_SUCCESS) { - ma_dlclose(ma_context_get_log(pContext), pContext->opensl.libOpenSLES); - return result; - } - - result = ma_dlsym_SLInterfaceID__opensl(pContext, "SL_IID_OUTPUTMIX", &pContext->opensl.SL_IID_OUTPUTMIX); - if (result != MA_SUCCESS) { - ma_dlclose(ma_context_get_log(pContext), pContext->opensl.libOpenSLES); - return result; - } - - result = ma_dlsym_SLInterfaceID__opensl(pContext, "SL_IID_ANDROIDCONFIGURATION", &pContext->opensl.SL_IID_ANDROIDCONFIGURATION); - if (result != MA_SUCCESS) { - ma_dlclose(ma_context_get_log(pContext), pContext->opensl.libOpenSLES); - return result; - } - - pContext->opensl.slCreateEngine = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->opensl.libOpenSLES, "slCreateEngine"); - if (pContext->opensl.slCreateEngine == NULL) { - ma_dlclose(ma_context_get_log(pContext), pContext->opensl.libOpenSLES); - ma_log_post(ma_context_get_log(pContext), MA_LOG_LEVEL_INFO, "[OpenSL] Cannot find symbol slCreateEngine."); - return MA_NO_BACKEND; - } -#else - pContext->opensl.SL_IID_ENGINE = (ma_handle)SL_IID_ENGINE; - pContext->opensl.SL_IID_AUDIOIODEVICECAPABILITIES = (ma_handle)SL_IID_AUDIOIODEVICECAPABILITIES; - pContext->opensl.SL_IID_ANDROIDSIMPLEBUFFERQUEUE = (ma_handle)SL_IID_ANDROIDSIMPLEBUFFERQUEUE; - pContext->opensl.SL_IID_RECORD = (ma_handle)SL_IID_RECORD; - pContext->opensl.SL_IID_PLAY = (ma_handle)SL_IID_PLAY; - pContext->opensl.SL_IID_OUTPUTMIX = (ma_handle)SL_IID_OUTPUTMIX; - pContext->opensl.SL_IID_ANDROIDCONFIGURATION = (ma_handle)SL_IID_ANDROIDCONFIGURATION; - pContext->opensl.slCreateEngine = (ma_proc)slCreateEngine; -#endif - - - /* Initialize global data first if applicable. */ - ma_spinlock_lock(&g_maOpenSLSpinlock); - { - result = ma_context_init_engine_nolock__opensl(pContext); - } - ma_spinlock_unlock(&g_maOpenSLSpinlock); - - if (result != MA_SUCCESS) { - ma_dlclose(ma_context_get_log(pContext), pContext->opensl.libOpenSLES); - ma_log_post(ma_context_get_log(pContext), MA_LOG_LEVEL_INFO, "[OpenSL] Failed to initialize OpenSL engine."); - return result; - } - - pCallbacks->onContextInit = ma_context_init__opensl; - pCallbacks->onContextUninit = ma_context_uninit__opensl; - pCallbacks->onContextEnumerateDevices = ma_context_enumerate_devices__opensl; - pCallbacks->onContextGetDeviceInfo = ma_context_get_device_info__opensl; - pCallbacks->onDeviceInit = ma_device_init__opensl; - pCallbacks->onDeviceUninit = ma_device_uninit__opensl; - pCallbacks->onDeviceStart = ma_device_start__opensl; - pCallbacks->onDeviceStop = ma_device_stop__opensl; - pCallbacks->onDeviceRead = NULL; /* Not needed because OpenSL|ES is asynchronous. */ - pCallbacks->onDeviceWrite = NULL; /* Not needed because OpenSL|ES is asynchronous. */ - pCallbacks->onDeviceDataLoop = NULL; /* Not needed because OpenSL|ES is asynchronous. */ - - return MA_SUCCESS; -} -#endif /* OpenSL|ES */ - - -/****************************************************************************** - -Web Audio Backend - -******************************************************************************/ -#ifdef MA_HAS_WEBAUDIO -#include - -#if (__EMSCRIPTEN_major__ > 3) || (__EMSCRIPTEN_major__ == 3 && (__EMSCRIPTEN_minor__ > 1 || (__EMSCRIPTEN_minor__ == 1 && __EMSCRIPTEN_tiny__ >= 32))) - #include - #define MA_SUPPORT_AUDIO_WORKLETS - - #if (__EMSCRIPTEN_major__ > 3) || (__EMSCRIPTEN_major__ == 3 && (__EMSCRIPTEN_minor__ > 1 || (__EMSCRIPTEN_minor__ == 1 && __EMSCRIPTEN_tiny__ >= 70))) - #define MA_SUPPORT_AUDIO_WORKLETS_VARIABLE_BUFFER_SIZE - #endif -#endif - -/* -TODO: Version 0.12: Swap this logic around so that AudioWorklets are used by default. Add MA_NO_AUDIO_WORKLETS. -*/ -#if defined(MA_ENABLE_AUDIO_WORKLETS) && defined(MA_SUPPORT_AUDIO_WORKLETS) - #define MA_USE_AUDIO_WORKLETS -#endif - -/* The thread stack size must be a multiple of 16. */ -#ifndef MA_AUDIO_WORKLETS_THREAD_STACK_SIZE -#define MA_AUDIO_WORKLETS_THREAD_STACK_SIZE 131072 -#endif - -#if defined(MA_USE_AUDIO_WORKLETS) -#define MA_WEBAUDIO_LATENCY_HINT_BALANCED "balanced" -#define MA_WEBAUDIO_LATENCY_HINT_INTERACTIVE "interactive" -#define MA_WEBAUDIO_LATENCY_HINT_PLAYBACK "playback" -#endif - -static ma_bool32 ma_is_capture_supported__webaudio() -{ - return EM_ASM_INT({ - return (navigator.mediaDevices !== undefined && navigator.mediaDevices.getUserMedia !== undefined); - }, 0) != 0; /* Must pass in a dummy argument for C99 compatibility. */ -} - -#ifdef __cplusplus -extern "C" { -#endif -void* EMSCRIPTEN_KEEPALIVE ma_malloc_emscripten(size_t sz, const ma_allocation_callbacks* pAllocationCallbacks) -{ - return ma_malloc(sz, pAllocationCallbacks); -} - -void EMSCRIPTEN_KEEPALIVE ma_free_emscripten(void* p, const ma_allocation_callbacks* pAllocationCallbacks) -{ - ma_free(p, pAllocationCallbacks); -} - -void EMSCRIPTEN_KEEPALIVE ma_device_process_pcm_frames_capture__webaudio(ma_device* pDevice, int frameCount, float* pFrames) -{ - ma_device_handle_backend_data_callback(pDevice, NULL, pFrames, (ma_uint32)frameCount); -} - -void EMSCRIPTEN_KEEPALIVE ma_device_process_pcm_frames_playback__webaudio(ma_device* pDevice, int frameCount, float* pFrames) -{ - ma_device_handle_backend_data_callback(pDevice, pFrames, NULL, (ma_uint32)frameCount); -} -#ifdef __cplusplus -} -#endif - -static ma_result ma_context_enumerate_devices__webaudio(ma_context* pContext, ma_enum_devices_callback_proc callback, void* pUserData) -{ - ma_bool32 cbResult = MA_TRUE; - - MA_ASSERT(pContext != NULL); - MA_ASSERT(callback != NULL); - - /* Only supporting default devices for now. */ - - /* Playback. */ - if (cbResult) { - ma_device_info deviceInfo; - MA_ZERO_OBJECT(&deviceInfo); - ma_strncpy_s(deviceInfo.name, sizeof(deviceInfo.name), MA_DEFAULT_PLAYBACK_DEVICE_NAME, (size_t)-1); - deviceInfo.isDefault = MA_TRUE; /* Only supporting default devices. */ - cbResult = callback(pContext, ma_device_type_playback, &deviceInfo, pUserData); - } - - /* Capture. */ - if (cbResult) { - if (ma_is_capture_supported__webaudio()) { - ma_device_info deviceInfo; - MA_ZERO_OBJECT(&deviceInfo); - ma_strncpy_s(deviceInfo.name, sizeof(deviceInfo.name), MA_DEFAULT_CAPTURE_DEVICE_NAME, (size_t)-1); - deviceInfo.isDefault = MA_TRUE; /* Only supporting default devices. */ - cbResult = callback(pContext, ma_device_type_capture, &deviceInfo, pUserData); - } - } - - return MA_SUCCESS; -} - -static ma_result ma_context_get_device_info__webaudio(ma_context* pContext, ma_device_type deviceType, const ma_device_id* pDeviceID, ma_device_info* pDeviceInfo) -{ - MA_ASSERT(pContext != NULL); - - if (deviceType == ma_device_type_capture && !ma_is_capture_supported__webaudio()) { - return MA_NO_DEVICE; - } - - MA_ZERO_MEMORY(pDeviceInfo->id.webaudio, sizeof(pDeviceInfo->id.webaudio)); - - /* Only supporting default devices for now. */ - (void)pDeviceID; - if (deviceType == ma_device_type_playback) { - ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), MA_DEFAULT_PLAYBACK_DEVICE_NAME, (size_t)-1); - } else { - ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), MA_DEFAULT_CAPTURE_DEVICE_NAME, (size_t)-1); - } - - /* Only supporting default devices. */ - pDeviceInfo->isDefault = MA_TRUE; - - /* Web Audio can support any number of channels and sample rates. It only supports f32 formats, however. */ - pDeviceInfo->nativeDataFormats[0].flags = 0; - pDeviceInfo->nativeDataFormats[0].format = ma_format_unknown; - pDeviceInfo->nativeDataFormats[0].channels = 0; /* All channels are supported. */ - pDeviceInfo->nativeDataFormats[0].sampleRate = EM_ASM_INT({ - try { - var temp = new (window.AudioContext || window.webkitAudioContext)(); - var sampleRate = temp.sampleRate; - temp.close(); - return sampleRate; - } catch(e) { - return 0; - } - }, 0); /* Must pass in a dummy argument for C99 compatibility. */ - - if (pDeviceInfo->nativeDataFormats[0].sampleRate == 0) { - return MA_NO_DEVICE; - } - - pDeviceInfo->nativeDataFormatCount = 1; - - return MA_SUCCESS; -} - -static ma_result ma_device_uninit__webaudio(ma_device* pDevice) -{ - MA_ASSERT(pDevice != NULL); - - #if defined(MA_USE_AUDIO_WORKLETS) - { - EM_ASM({ - var device = window.miniaudio.get_device_by_index($0); - - if (device.streamNode !== undefined) { - device.streamNode.disconnect(); - device.streamNode = undefined; - } - - device.pDevice = undefined; - }, pDevice->webaudio.deviceIndex); - - emscripten_destroy_web_audio_node(pDevice->webaudio.audioWorklet); - emscripten_destroy_audio_context(pDevice->webaudio.audioContext); - ma_free(pDevice->webaudio.pStackBuffer, &pDevice->pContext->allocationCallbacks); - } - #else - { - EM_ASM({ - var device = window.miniaudio.get_device_by_index($0); - - /* Make sure all nodes are disconnected and marked for collection. */ - if (device.scriptNode !== undefined) { - device.scriptNode.onaudioprocess = function(e) {}; /* We want to reset the callback to ensure it doesn't get called after AudioContext.close() has returned. Shouldn't happen since we're disconnecting, but just to be safe... */ - device.scriptNode.disconnect(); - device.scriptNode = undefined; - } - - if (device.streamNode !== undefined) { - device.streamNode.disconnect(); - device.streamNode = undefined; - } - - /* - Stop the device. I think there is a chance the callback could get fired after calling this, hence why we want - to clear the callback before closing. - */ - device.webaudio.close(); - device.webaudio = undefined; - device.pDevice = undefined; - }, pDevice->webaudio.deviceIndex); - } - #endif - - /* Clean up the device on the JS side. */ - EM_ASM({ - window.miniaudio.untrack_device_by_index($0); - }, pDevice->webaudio.deviceIndex); - - ma_free(pDevice->webaudio.pIntermediaryBuffer, &pDevice->pContext->allocationCallbacks); - - return MA_SUCCESS; -} - -#if !defined(MA_USE_AUDIO_WORKLETS) -static ma_uint32 ma_calculate_period_size_in_frames_from_descriptor__webaudio(const ma_device_descriptor* pDescriptor, ma_uint32 nativeSampleRate, ma_performance_profile performanceProfile) -{ - /* - There have been reports of the default buffer size being too small on some browsers. If we're using - the default buffer size, we'll make sure the period size is bigger than our standard defaults. - */ - ma_uint32 periodSizeInFrames; - - if (nativeSampleRate == 0) { - nativeSampleRate = MA_DEFAULT_SAMPLE_RATE; - } - - if (pDescriptor->periodSizeInFrames == 0) { - if (pDescriptor->periodSizeInMilliseconds == 0) { - if (performanceProfile == ma_performance_profile_low_latency) { - periodSizeInFrames = ma_calculate_buffer_size_in_frames_from_milliseconds(33, nativeSampleRate); /* 1 frame @ 30 FPS */ - } else { - periodSizeInFrames = ma_calculate_buffer_size_in_frames_from_milliseconds(333, nativeSampleRate); - } - } else { - periodSizeInFrames = ma_calculate_buffer_size_in_frames_from_milliseconds(pDescriptor->periodSizeInMilliseconds, nativeSampleRate); - } - } else { - periodSizeInFrames = pDescriptor->periodSizeInFrames; - } - - /* The size of the buffer must be a power of 2 and between 256 and 16384. */ - if (periodSizeInFrames < 256) { - periodSizeInFrames = 256; - } else if (periodSizeInFrames > 16384) { - periodSizeInFrames = 16384; - } else { - periodSizeInFrames = ma_next_power_of_2(periodSizeInFrames); - } - - return periodSizeInFrames; -} -#endif - - -#if defined(MA_USE_AUDIO_WORKLETS) -typedef struct -{ - ma_device* pDevice; - const ma_device_config* pConfig; - ma_device_descriptor* pDescriptorPlayback; - ma_device_descriptor* pDescriptorCapture; -} ma_audio_worklet_thread_initialized_data; - -static EM_BOOL ma_audio_worklet_process_callback__webaudio(int inputCount, const AudioSampleFrame* pInputs, int outputCount, AudioSampleFrame* pOutputs, int paramCount, const AudioParamFrame* pParams, void* pUserData) -{ - ma_device* pDevice = (ma_device*)pUserData; - ma_uint32 frameCount; - - (void)paramCount; - (void)pParams; - - /* - The Emscripten documentation says that it'll always be 128 frames being passed in. Hard coding it like that feels - like a very bad idea to me. Even if it's hard coded in the backend, the API and documentation should always refer - to variables instead of a hard coded number. In any case, will follow along for the time being. - - Unfortunately the audio data is not interleaved so we'll need to convert it before we give the data to miniaudio - for further processing. - */ - if (pDevice->type == ma_device_type_playback) { - frameCount = pDevice->playback.internalPeriodSizeInFrames; - } else { - frameCount = pDevice->capture.internalPeriodSizeInFrames; - } - - if (ma_device_get_state(pDevice) != ma_device_state_started) { - /* Fill the output buffer with zero to avoid a noise sound */ - for (int i = 0; i < outputCount; i += 1) { - MA_ZERO_MEMORY(pOutputs[i].data, pOutputs[i].numberOfChannels * frameCount * sizeof(float)); - } - - return EM_TRUE; - } - - if (inputCount > 0) { - /* Input data needs to be interleaved before we hand it to the client. */ - for (ma_uint32 iChannel = 0; iChannel < pDevice->capture.internalChannels; iChannel += 1) { - for (ma_uint32 iFrame = 0; iFrame < frameCount; iFrame += 1) { - pDevice->webaudio.pIntermediaryBuffer[iFrame*pDevice->capture.internalChannels + iChannel] = pInputs[0].data[frameCount*iChannel + iFrame]; - } - } - - ma_device_process_pcm_frames_capture__webaudio(pDevice, frameCount, pDevice->webaudio.pIntermediaryBuffer); - } - - if (outputCount > 0) { - /* If it's a capture-only device, we'll need to output silence. */ - if (pDevice->type == ma_device_type_capture) { - MA_ZERO_MEMORY(pOutputs[0].data, frameCount * pDevice->playback.internalChannels * sizeof(float)); - } else { - ma_device_process_pcm_frames_playback__webaudio(pDevice, frameCount, pDevice->webaudio.pIntermediaryBuffer); - - /* We've read the data from the client. Now we need to deinterleave the buffer and output to the output buffer. */ - for (ma_uint32 iChannel = 0; iChannel < pDevice->playback.internalChannels; iChannel += 1) { - for (ma_uint32 iFrame = 0; iFrame < frameCount; iFrame += 1) { - pOutputs[0].data[frameCount*iChannel + iFrame] = pDevice->webaudio.pIntermediaryBuffer[iFrame*pDevice->playback.internalChannels + iChannel]; - } - } - } - } - - return EM_TRUE; -} - - -static void ma_audio_worklet_processor_created__webaudio(EMSCRIPTEN_WEBAUDIO_T audioContext, EM_BOOL success, void* pUserData) -{ - ma_audio_worklet_thread_initialized_data* pParameters = (ma_audio_worklet_thread_initialized_data*)pUserData; - EmscriptenAudioWorkletNodeCreateOptions audioWorkletOptions; - int channels = 0; - size_t intermediaryBufferSizeInFrames; - int sampleRate; - - if (success == EM_FALSE) { - pParameters->pDevice->webaudio.initResult = MA_ERROR; - ma_free(pParameters, &pParameters->pDevice->pContext->allocationCallbacks); - return; - } - - /* The next step is to initialize the audio worklet node. */ - MA_ZERO_OBJECT(&audioWorkletOptions); - - /* - The way channel counts work with Web Audio is confusing. As far as I can tell, there's no way to know the channel - count from MediaStreamAudioSourceNode (what we use for capture)? The only way to have control is to configure an - output channel count on the capture side. This is slightly confusing for capture mode because intuitively you - wouldn't actually connect an output to an input-only node, but this is what we'll have to do in order to have - proper control over the channel count. In the capture case, we'll have to output silence to its output node. - */ - if (pParameters->pConfig->deviceType == ma_device_type_capture) { - channels = (int)((pParameters->pDescriptorCapture->channels > 0) ? pParameters->pDescriptorCapture->channels : MA_DEFAULT_CHANNELS); - audioWorkletOptions.numberOfInputs = 1; - } else { - channels = (int)((pParameters->pDescriptorPlayback->channels > 0) ? pParameters->pDescriptorPlayback->channels : MA_DEFAULT_CHANNELS); - - if (pParameters->pConfig->deviceType == ma_device_type_duplex) { - audioWorkletOptions.numberOfInputs = 1; - } else { - audioWorkletOptions.numberOfInputs = 0; - } - } - - audioWorkletOptions.numberOfOutputs = 1; - audioWorkletOptions.outputChannelCounts = &channels; - - - /* - Now that we know the channel count to use we can allocate the intermediary buffer. The - intermediary buffer is used for interleaving and deinterleaving. - */ - #if defined(MA_SUPPORT_AUDIO_WORKLETS_VARIABLE_BUFFER_SIZE) - { - intermediaryBufferSizeInFrames = (size_t)emscripten_audio_context_quantum_size(audioContext); - } - #else - { - intermediaryBufferSizeInFrames = 128; - } - #endif - - pParameters->pDevice->webaudio.pIntermediaryBuffer = (float*)ma_malloc(intermediaryBufferSizeInFrames * (ma_uint32)channels * sizeof(float), &pParameters->pDevice->pContext->allocationCallbacks); - if (pParameters->pDevice->webaudio.pIntermediaryBuffer == NULL) { - pParameters->pDevice->webaudio.initResult = MA_OUT_OF_MEMORY; - ma_free(pParameters, &pParameters->pDevice->pContext->allocationCallbacks); - return; - } - - pParameters->pDevice->webaudio.audioWorklet = emscripten_create_wasm_audio_worklet_node(audioContext, "miniaudio", &audioWorkletOptions, &ma_audio_worklet_process_callback__webaudio, pParameters->pDevice); - - /* With the audio worklet initialized we can now attach it to the graph. */ - if (pParameters->pConfig->deviceType == ma_device_type_capture || pParameters->pConfig->deviceType == ma_device_type_duplex) { - ma_result attachmentResult = (ma_result)EM_ASM_INT({ - var getUserMediaResult = 0; - var audioWorklet = emscriptenGetAudioObject($0); - var audioContext = emscriptenGetAudioObject($1); - - navigator.mediaDevices.getUserMedia({audio:true, video:false}) - .then(function(stream) { - audioContext.streamNode = audioContext.createMediaStreamSource(stream); - audioContext.streamNode.connect(audioWorklet); - audioWorklet.connect(audioContext.destination); - getUserMediaResult = 0; /* 0 = MA_SUCCESS */ - }) - .catch(function(error) { - console.log("navigator.mediaDevices.getUserMedia Failed: " + error); - getUserMediaResult = -1; /* -1 = MA_ERROR */ - }); - - return getUserMediaResult; - }, pParameters->pDevice->webaudio.audioWorklet, audioContext); - - if (attachmentResult != MA_SUCCESS) { - ma_log_postf(ma_device_get_log(pParameters->pDevice), MA_LOG_LEVEL_ERROR, "Web Audio: Failed to connect capture node."); - emscripten_destroy_web_audio_node(pParameters->pDevice->webaudio.audioWorklet); - pParameters->pDevice->webaudio.initResult = attachmentResult; - ma_free(pParameters, &pParameters->pDevice->pContext->allocationCallbacks); - return; - } - } - - /* If it's playback only we can now attach the worklet node to the graph. This has already been done for the duplex case. */ - if (pParameters->pConfig->deviceType == ma_device_type_playback) { - ma_result attachmentResult = (ma_result)EM_ASM_INT({ - var audioWorklet = emscriptenGetAudioObject($0); - var audioContext = emscriptenGetAudioObject($1); - audioWorklet.connect(audioContext.destination); - return 0; /* 0 = MA_SUCCESS */ - }, pParameters->pDevice->webaudio.audioWorklet, audioContext); - - if (attachmentResult != MA_SUCCESS) { - ma_log_postf(ma_device_get_log(pParameters->pDevice), MA_LOG_LEVEL_ERROR, "Web Audio: Failed to connect playback node."); - pParameters->pDevice->webaudio.initResult = attachmentResult; - ma_free(pParameters, &pParameters->pDevice->pContext->allocationCallbacks); - return; - } - } - - /* We need to update the descriptors so that they reflect the internal data format. Both capture and playback should be the same. */ - sampleRate = EM_ASM_INT({ return emscriptenGetAudioObject($0).sampleRate; }, audioContext); - - if (pParameters->pDescriptorCapture != NULL) { - pParameters->pDescriptorCapture->format = ma_format_f32; - pParameters->pDescriptorCapture->channels = (ma_uint32)channels; - pParameters->pDescriptorCapture->sampleRate = (ma_uint32)sampleRate; - ma_channel_map_init_standard(ma_standard_channel_map_webaudio, pParameters->pDescriptorCapture->channelMap, ma_countof(pParameters->pDescriptorCapture->channelMap), pParameters->pDescriptorCapture->channels); - pParameters->pDescriptorCapture->periodSizeInFrames = intermediaryBufferSizeInFrames; - pParameters->pDescriptorCapture->periodCount = 1; - } - - if (pParameters->pDescriptorPlayback != NULL) { - pParameters->pDescriptorPlayback->format = ma_format_f32; - pParameters->pDescriptorPlayback->channels = (ma_uint32)channels; - pParameters->pDescriptorPlayback->sampleRate = (ma_uint32)sampleRate; - ma_channel_map_init_standard(ma_standard_channel_map_webaudio, pParameters->pDescriptorPlayback->channelMap, ma_countof(pParameters->pDescriptorPlayback->channelMap), pParameters->pDescriptorPlayback->channels); - pParameters->pDescriptorPlayback->periodSizeInFrames = intermediaryBufferSizeInFrames; - pParameters->pDescriptorPlayback->periodCount = 1; - } - - /* At this point we're done and we can return. */ - ma_log_postf(ma_device_get_log(pParameters->pDevice), MA_LOG_LEVEL_DEBUG, "AudioWorklets: Created worklet node: %d\n", pParameters->pDevice->webaudio.audioWorklet); - pParameters->pDevice->webaudio.initResult = MA_SUCCESS; - ma_free(pParameters, &pParameters->pDevice->pContext->allocationCallbacks); -} - -static void ma_audio_worklet_thread_initialized__webaudio(EMSCRIPTEN_WEBAUDIO_T audioContext, EM_BOOL success, void* pUserData) -{ - ma_audio_worklet_thread_initialized_data* pParameters = (ma_audio_worklet_thread_initialized_data*)pUserData; - WebAudioWorkletProcessorCreateOptions workletProcessorOptions; - - MA_ASSERT(pParameters != NULL); - - if (success == EM_FALSE) { - pParameters->pDevice->webaudio.initResult = MA_ERROR; - return; - } - - MA_ZERO_OBJECT(&workletProcessorOptions); - workletProcessorOptions.name = "miniaudio"; /* I'm not entirely sure what to call this. Does this need to be globally unique, or does it need only be unique for a given AudioContext? */ - - emscripten_create_wasm_audio_worklet_processor_async(audioContext, &workletProcessorOptions, ma_audio_worklet_processor_created__webaudio, pParameters); -} -#endif - -static ma_result ma_device_init__webaudio(ma_device* pDevice, const ma_device_config* pConfig, ma_device_descriptor* pDescriptorPlayback, ma_device_descriptor* pDescriptorCapture) -{ - if (pConfig->deviceType == ma_device_type_loopback) { - return MA_DEVICE_TYPE_NOT_SUPPORTED; - } - - /* No exclusive mode with Web Audio. */ - if (((pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) && pDescriptorPlayback->shareMode == ma_share_mode_exclusive) || - ((pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) && pDescriptorCapture->shareMode == ma_share_mode_exclusive)) { - return MA_SHARE_MODE_NOT_SUPPORTED; - } - - /* - With AudioWorklets we'll have just a single AudioContext. I'm not sure why I'm not doing this for ScriptProcessorNode so - it might be worthwhile to look into that as well. - */ - #if defined(MA_USE_AUDIO_WORKLETS) - { - EmscriptenWebAudioCreateAttributes audioContextAttributes; - ma_audio_worklet_thread_initialized_data* pInitParameters; - void* pStackBuffer; - - if (pConfig->performanceProfile == ma_performance_profile_conservative) { - audioContextAttributes.latencyHint = MA_WEBAUDIO_LATENCY_HINT_PLAYBACK; - } else { - audioContextAttributes.latencyHint = MA_WEBAUDIO_LATENCY_HINT_INTERACTIVE; - } - - /* - In my testing, Firefox does not seem to capture audio data properly if the sample rate is set - to anything other than 48K. This does not seem to be the case for other browsers. For this reason, - if the device type is anything other than playback, we'll leave the sample rate as-is and let the - browser pick the appropriate rate for us. - */ - if (pConfig->deviceType == ma_device_type_playback) { - audioContextAttributes.sampleRate = pDescriptorPlayback->sampleRate; - } else { - audioContextAttributes.sampleRate = 0; - } - - /* It's not clear if this can return an error. None of the tests in the Emscripten repository check for this, so neither am I for now. */ - pDevice->webaudio.audioContext = emscripten_create_audio_context(&audioContextAttributes); - - /* - With the context created we can now create the worklet. We can only have a single worklet per audio - context which means we'll need to craft this appropriately to handle duplex devices correctly. - */ - - /* - We now need to create a worker thread. This is a bit weird because we need to allocate our - own buffer for the thread's stack. The stack needs to be aligned to 16 bytes. I'm going to - allocate this on the heap to keep it simple. - */ - pStackBuffer = ma_aligned_malloc(MA_AUDIO_WORKLETS_THREAD_STACK_SIZE, 16, &pDevice->pContext->allocationCallbacks); - if (pStackBuffer == NULL) { - emscripten_destroy_audio_context(pDevice->webaudio.audioContext); - return MA_OUT_OF_MEMORY; - } - - /* Our thread initialization parameters need to be allocated on the heap so they don't go out of scope. */ - pInitParameters = (ma_audio_worklet_thread_initialized_data*)ma_malloc(sizeof(*pInitParameters), &pDevice->pContext->allocationCallbacks); - if (pInitParameters == NULL) { - ma_free(pStackBuffer, &pDevice->pContext->allocationCallbacks); - emscripten_destroy_audio_context(pDevice->webaudio.audioContext); - return MA_OUT_OF_MEMORY; - } - - pInitParameters->pDevice = pDevice; - pInitParameters->pConfig = pConfig; - pInitParameters->pDescriptorPlayback = pDescriptorPlayback; - pInitParameters->pDescriptorCapture = pDescriptorCapture; - - /* - We need to flag the device as not yet initialized so we can wait on it later. Unfortunately all of - the Emscripten WebAudio stuff is asynchronous. - */ - pDevice->webaudio.initResult = MA_BUSY; - { - emscripten_start_wasm_audio_worklet_thread_async(pDevice->webaudio.audioContext, pStackBuffer, MA_AUDIO_WORKLETS_THREAD_STACK_SIZE, ma_audio_worklet_thread_initialized__webaudio, pInitParameters); - } - while (pDevice->webaudio.initResult == MA_BUSY) { emscripten_sleep(1); } /* We must wait for initialization to complete. We're just spinning here. The emscripten_sleep() call is why we need to build with `-sASYNCIFY`. */ - - /* Initialization is now complete. Descriptors were updated when the worklet was initialized. */ - if (pDevice->webaudio.initResult != MA_SUCCESS) { - ma_free(pStackBuffer, &pDevice->pContext->allocationCallbacks); - emscripten_destroy_audio_context(pDevice->webaudio.audioContext); - return pDevice->webaudio.initResult; - } - - /* We need to add an entry to the miniaudio.devices list on the JS side so we can do some JS/C interop. */ - pDevice->webaudio.deviceIndex = EM_ASM_INT({ - return window.miniaudio.track_device({ - webaudio: emscriptenGetAudioObject($0), - state: 1, /* 1 = ma_device_state_stopped */ - pDevice: $1 - }); - }, pDevice->webaudio.audioContext, pDevice); - - return MA_SUCCESS; - } - #else - { - /* ScriptProcessorNode. This path requires us to do almost everything in JS, but we'll do as much as we can in C. */ - ma_uint32 deviceIndex; - ma_uint32 channels; - ma_uint32 sampleRate; - ma_uint32 periodSizeInFrames; - - /* The channel count will depend on the device type. If it's a capture, use its, otherwise use the playback side. */ - if (pConfig->deviceType == ma_device_type_capture) { - channels = (pDescriptorCapture->channels > 0) ? pDescriptorCapture->channels : MA_DEFAULT_CHANNELS; - } else { - channels = (pDescriptorPlayback->channels > 0) ? pDescriptorPlayback->channels : MA_DEFAULT_CHANNELS; - } - - /* - When testing in Firefox, I've seen it where capture mode fails if the sample rate is changed to anything other than it's - native rate. For this reason we're leaving the sample rate untouched for capture devices. - */ - if (pConfig->deviceType == ma_device_type_playback) { - sampleRate = pDescriptorPlayback->sampleRate; - } else { - sampleRate = 0; /* Let the browser decide when capturing. */ - } - - /* The period size needs to be a power of 2. */ - if (pConfig->deviceType == ma_device_type_capture) { - periodSizeInFrames = ma_calculate_period_size_in_frames_from_descriptor__webaudio(pDescriptorCapture, sampleRate, pConfig->performanceProfile); - } else { - periodSizeInFrames = ma_calculate_period_size_in_frames_from_descriptor__webaudio(pDescriptorPlayback, sampleRate, pConfig->performanceProfile); - } - - /* We need an intermediary buffer for doing interleaving and deinterleaving. */ - pDevice->webaudio.pIntermediaryBuffer = (float*)ma_malloc(periodSizeInFrames * channels * sizeof(float), &pDevice->pContext->allocationCallbacks); - if (pDevice->webaudio.pIntermediaryBuffer == NULL) { - return MA_OUT_OF_MEMORY; - } - - deviceIndex = EM_ASM_INT({ - var deviceType = $0; - var channels = $1; - var sampleRate = $2; - var bufferSize = $3; - var pIntermediaryBuffer = $4; - var pDevice = $5; - - if (typeof(window.miniaudio) === 'undefined') { - return -1; /* Context not initialized. */ - } - - var device = {}; - - /* First thing we need is an AudioContext. */ - var audioContextOptions = {}; - if (deviceType == window.miniaudio.device_type.playback && sampleRate != 0) { - audioContextOptions.sampleRate = sampleRate; - } - - device.webaudio = new (window.AudioContext || window.webkitAudioContext)(audioContextOptions); - device.webaudio.suspend(); /* The AudioContext must be created in a suspended state. */ - device.state = window.miniaudio.device_state.stopped; - - /* - We need to create a ScriptProcessorNode. The channel situation is the same as the AudioWorklet path in that we - need to specify an output and configure the channel count there. - */ - var channelCountIn = 0; - var channelCountOut = channels; - if (deviceType != window.miniaudio.device_type.playback) { - channelCountIn = channels; - } - - device.scriptNode = device.webaudio.createScriptProcessor(bufferSize, channelCountIn, channelCountOut); - - /* The node processing callback. */ - device.scriptNode.onaudioprocess = function(e) { - if (device.intermediaryBufferView == null || device.intermediaryBufferView.length == 0) { - device.intermediaryBufferView = new Float32Array(HEAPF32.buffer, pIntermediaryBuffer, bufferSize * channels); - } - - /* Do the capture side first. */ - if (deviceType == window.miniaudio.device_type.capture || deviceType == window.miniaudio.device_type.duplex) { - /* The data must be interleaved before being processed miniaudio. */ - for (var iChannel = 0; iChannel < channels; iChannel += 1) { - var inputBuffer = e.inputBuffer.getChannelData(iChannel); - var intermediaryBuffer = device.intermediaryBufferView; - - for (var iFrame = 0; iFrame < bufferSize; iFrame += 1) { - intermediaryBuffer[iFrame*channels + iChannel] = inputBuffer[iFrame]; - } - } - - _ma_device_process_pcm_frames_capture__webaudio(pDevice, bufferSize, pIntermediaryBuffer); - } - - if (deviceType == window.miniaudio.device_type.playback || deviceType == window.miniaudio.device_type.duplex) { - _ma_device_process_pcm_frames_playback__webaudio(pDevice, bufferSize, pIntermediaryBuffer); - - for (var iChannel = 0; iChannel < e.outputBuffer.numberOfChannels; ++iChannel) { - var outputBuffer = e.outputBuffer.getChannelData(iChannel); - var intermediaryBuffer = device.intermediaryBufferView; - - for (var iFrame = 0; iFrame < bufferSize; iFrame += 1) { - outputBuffer[iFrame] = intermediaryBuffer[iFrame*channels + iChannel]; - } - } - } else { - /* It's a capture-only device. Make sure the output is silenced. */ - for (var iChannel = 0; iChannel < e.outputBuffer.numberOfChannels; ++iChannel) { - e.outputBuffer.getChannelData(iChannel).fill(0.0); - } - } - }; - - /* Now we need to connect our node to the graph. */ - if (deviceType == window.miniaudio.device_type.capture || deviceType == window.miniaudio.device_type.duplex) { - navigator.mediaDevices.getUserMedia({audio:true, video:false}) - .then(function(stream) { - device.streamNode = device.webaudio.createMediaStreamSource(stream); - device.streamNode.connect(device.scriptNode); - device.scriptNode.connect(device.webaudio.destination); - }) - .catch(function(error) { - console.log("Failed to get user media: " + error); - }); - } - - if (deviceType == window.miniaudio.device_type.playback) { - device.scriptNode.connect(device.webaudio.destination); - } - - device.pDevice = pDevice; - - return window.miniaudio.track_device(device); - }, pConfig->deviceType, channels, sampleRate, periodSizeInFrames, pDevice->webaudio.pIntermediaryBuffer, pDevice); - - if (deviceIndex < 0) { - return MA_FAILED_TO_OPEN_BACKEND_DEVICE; - } - - pDevice->webaudio.deviceIndex = deviceIndex; - - /* Grab the sample rate from the audio context directly. */ - sampleRate = (ma_uint32)EM_ASM_INT({ return window.miniaudio.get_device_by_index($0).webaudio.sampleRate; }, deviceIndex); - - if (pDescriptorCapture != NULL) { - pDescriptorCapture->format = ma_format_f32; - pDescriptorCapture->channels = channels; - pDescriptorCapture->sampleRate = sampleRate; - ma_channel_map_init_standard(ma_standard_channel_map_webaudio, pDescriptorCapture->channelMap, ma_countof(pDescriptorCapture->channelMap), pDescriptorCapture->channels); - pDescriptorCapture->periodSizeInFrames = periodSizeInFrames; - pDescriptorCapture->periodCount = 1; - } - - if (pDescriptorPlayback != NULL) { - pDescriptorPlayback->format = ma_format_f32; - pDescriptorPlayback->channels = channels; - pDescriptorPlayback->sampleRate = sampleRate; - ma_channel_map_init_standard(ma_standard_channel_map_webaudio, pDescriptorPlayback->channelMap, ma_countof(pDescriptorPlayback->channelMap), pDescriptorPlayback->channels); - pDescriptorPlayback->periodSizeInFrames = periodSizeInFrames; - pDescriptorPlayback->periodCount = 1; - } - - return MA_SUCCESS; - } - #endif -} - -static ma_result ma_device_start__webaudio(ma_device* pDevice) -{ - MA_ASSERT(pDevice != NULL); - - EM_ASM({ - var device = window.miniaudio.get_device_by_index($0); - device.webaudio.resume(); - device.state = window.miniaudio.device_state.started; - }, pDevice->webaudio.deviceIndex); - - return MA_SUCCESS; -} - -static ma_result ma_device_stop__webaudio(ma_device* pDevice) -{ - MA_ASSERT(pDevice != NULL); - - /* - From the WebAudio API documentation for AudioContext.suspend(): - - Suspends the progression of AudioContext's currentTime, allows any current context processing blocks that are already processed to be played to the - destination, and then allows the system to release its claim on audio hardware. - - I read this to mean that "any current context processing blocks" are processed by suspend() - i.e. They they are drained. We therefore shouldn't need to - do any kind of explicit draining. - */ - EM_ASM({ - var device = window.miniaudio.get_device_by_index($0); - device.webaudio.suspend(); - device.state = window.miniaudio.device_state.stopped; - }, pDevice->webaudio.deviceIndex); - - ma_device__on_notification_stopped(pDevice); - - return MA_SUCCESS; -} - -static ma_result ma_context_uninit__webaudio(ma_context* pContext) -{ - MA_ASSERT(pContext != NULL); - MA_ASSERT(pContext->backend == ma_backend_webaudio); - - (void)pContext; /* Unused. */ - - /* Remove the global miniaudio object from window if there are no more references to it. */ - EM_ASM({ - if (typeof(window.miniaudio) !== 'undefined') { - miniaudio.unlock_event_types.map(function(event_type) { - document.removeEventListener(event_type, miniaudio.unlock, true); - }); - - window.miniaudio.referenceCount -= 1; - if (window.miniaudio.referenceCount === 0) { - delete window.miniaudio; - } - } - }); - - return MA_SUCCESS; -} - -static ma_result ma_context_init__webaudio(ma_context* pContext, const ma_context_config* pConfig, ma_backend_callbacks* pCallbacks) -{ - int resultFromJS; - - MA_ASSERT(pContext != NULL); - - (void)pConfig; /* Unused. */ - - /* Here is where our global JavaScript object is initialized. */ - resultFromJS = EM_ASM_INT({ - if (typeof window === 'undefined' || (window.AudioContext || window.webkitAudioContext) === undefined) { - return 0; /* Web Audio not supported. */ - } - - if (typeof(window.miniaudio) === 'undefined') { - window.miniaudio = { - referenceCount: 0 - }; - - /* Device types. */ - window.miniaudio.device_type = {}; - window.miniaudio.device_type.playback = $0; - window.miniaudio.device_type.capture = $1; - window.miniaudio.device_type.duplex = $2; - - /* Device states. */ - window.miniaudio.device_state = {}; - window.miniaudio.device_state.stopped = $3; - window.miniaudio.device_state.started = $4; - - /* Device cache for mapping devices to indexes for JavaScript/C interop. */ - let miniaudio = window.miniaudio; - miniaudio.devices = []; - - miniaudio.track_device = function(device) { - /* Try inserting into a free slot first. */ - for (var iDevice = 0; iDevice < miniaudio.devices.length; ++iDevice) { - if (miniaudio.devices[iDevice] == null) { - miniaudio.devices[iDevice] = device; - return iDevice; - } - } - - /* Getting here means there is no empty slots in the array so we just push to the end. */ - miniaudio.devices.push(device); - return miniaudio.devices.length - 1; - }; - - miniaudio.untrack_device_by_index = function(deviceIndex) { - /* We just set the device's slot to null. The slot will get reused in the next call to ma_track_device. */ - miniaudio.devices[deviceIndex] = null; - - /* Trim the array if possible. */ - while (miniaudio.devices.length > 0) { - if (miniaudio.devices[miniaudio.devices.length-1] == null) { - miniaudio.devices.pop(); - } else { - break; - } - } - }; - - miniaudio.untrack_device = function(device) { - for (var iDevice = 0; iDevice < miniaudio.devices.length; ++iDevice) { - if (miniaudio.devices[iDevice] == device) { - return miniaudio.untrack_device_by_index(iDevice); - } - } - }; - - miniaudio.get_device_by_index = function(deviceIndex) { - return miniaudio.devices[deviceIndex]; - }; - - miniaudio.unlock_event_types = (function(){ - return ['touchend', 'click']; - })(); - - miniaudio.unlock = function() { - for(var i = 0; i < miniaudio.devices.length; ++i) { - var device = miniaudio.devices[i]; - if (device != null && - device.webaudio != null && - device.state === miniaudio.device_state.started) { - - device.webaudio.resume().then(() => { - _ma_device__on_notification_unlocked(device.pDevice); - }, - (error) => {console.error("Failed to resume audiocontext", error); - }); - } - } - miniaudio.unlock_event_types.map(function(event_type) { - document.removeEventListener(event_type, miniaudio.unlock, true); - }); - }; - - miniaudio.unlock_event_types.map(function(event_type) { - document.addEventListener(event_type, miniaudio.unlock, true); - }); - } - - window.miniaudio.referenceCount += 1; - - return 1; - }, ma_device_type_playback, ma_device_type_capture, ma_device_type_duplex, ma_device_state_stopped, ma_device_state_started); - - if (resultFromJS != 1) { - return MA_FAILED_TO_INIT_BACKEND; - } - - pCallbacks->onContextInit = ma_context_init__webaudio; - pCallbacks->onContextUninit = ma_context_uninit__webaudio; - pCallbacks->onContextEnumerateDevices = ma_context_enumerate_devices__webaudio; - pCallbacks->onContextGetDeviceInfo = ma_context_get_device_info__webaudio; - pCallbacks->onDeviceInit = ma_device_init__webaudio; - pCallbacks->onDeviceUninit = ma_device_uninit__webaudio; - pCallbacks->onDeviceStart = ma_device_start__webaudio; - pCallbacks->onDeviceStop = ma_device_stop__webaudio; - pCallbacks->onDeviceRead = NULL; /* Not needed because WebAudio is asynchronous. */ - pCallbacks->onDeviceWrite = NULL; /* Not needed because WebAudio is asynchronous. */ - pCallbacks->onDeviceDataLoop = NULL; /* Not needed because WebAudio is asynchronous. */ - - return MA_SUCCESS; -} -#endif /* MA_HAS_WEBAUDIO */ - - - -static ma_bool32 ma__is_channel_map_valid(const ma_channel* pChannelMap, ma_uint32 channels) -{ - /* A blank channel map should be allowed, in which case it should use an appropriate default which will depend on context. */ - if (pChannelMap != NULL && pChannelMap[0] != MA_CHANNEL_NONE) { - ma_uint32 iChannel; - - if (channels == 0 || channels > MA_MAX_CHANNELS) { - return MA_FALSE; /* Channel count out of range. */ - } - - /* A channel cannot be present in the channel map more than once. */ - for (iChannel = 0; iChannel < channels; ++iChannel) { - ma_uint32 jChannel; - for (jChannel = iChannel + 1; jChannel < channels; ++jChannel) { - if (pChannelMap[iChannel] == pChannelMap[jChannel]) { - return MA_FALSE; - } - } - } - } - - return MA_TRUE; -} - - -static ma_bool32 ma_context_is_backend_asynchronous(ma_context* pContext) -{ - MA_ASSERT(pContext != NULL); - - if (pContext->callbacks.onDeviceRead == NULL && pContext->callbacks.onDeviceWrite == NULL) { - if (pContext->callbacks.onDeviceDataLoop == NULL) { - return MA_TRUE; - } else { - return MA_FALSE; - } - } else { - return MA_FALSE; - } -} - - -static ma_result ma_device__post_init_setup(ma_device* pDevice, ma_device_type deviceType) -{ - ma_result result; - - MA_ASSERT(pDevice != NULL); - - if (deviceType == ma_device_type_capture || deviceType == ma_device_type_duplex || deviceType == ma_device_type_loopback) { - if (pDevice->capture.format == ma_format_unknown) { - pDevice->capture.format = pDevice->capture.internalFormat; - } - if (pDevice->capture.channels == 0) { - pDevice->capture.channels = pDevice->capture.internalChannels; - } - if (pDevice->capture.channelMap[0] == MA_CHANNEL_NONE) { - MA_ASSERT(pDevice->capture.channels <= MA_MAX_CHANNELS); - if (pDevice->capture.internalChannels == pDevice->capture.channels) { - ma_channel_map_copy(pDevice->capture.channelMap, pDevice->capture.internalChannelMap, pDevice->capture.channels); - } else { - if (pDevice->capture.channelMixMode == ma_channel_mix_mode_simple) { - ma_channel_map_init_blank(pDevice->capture.channelMap, pDevice->capture.channels); - } else { - ma_channel_map_init_standard(ma_standard_channel_map_default, pDevice->capture.channelMap, ma_countof(pDevice->capture.channelMap), pDevice->capture.channels); - } - } - } - } - - if (deviceType == ma_device_type_playback || deviceType == ma_device_type_duplex) { - if (pDevice->playback.format == ma_format_unknown) { - pDevice->playback.format = pDevice->playback.internalFormat; - } - if (pDevice->playback.channels == 0) { - pDevice->playback.channels = pDevice->playback.internalChannels; - } - if (pDevice->playback.channelMap[0] == MA_CHANNEL_NONE) { - MA_ASSERT(pDevice->playback.channels <= MA_MAX_CHANNELS); - if (pDevice->playback.internalChannels == pDevice->playback.channels) { - ma_channel_map_copy(pDevice->playback.channelMap, pDevice->playback.internalChannelMap, pDevice->playback.channels); - } else { - if (pDevice->playback.channelMixMode == ma_channel_mix_mode_simple) { - ma_channel_map_init_blank(pDevice->playback.channelMap, pDevice->playback.channels); - } else { - ma_channel_map_init_standard(ma_standard_channel_map_default, pDevice->playback.channelMap, ma_countof(pDevice->playback.channelMap), pDevice->playback.channels); - } - } - } - } - - if (pDevice->sampleRate == 0) { - if (deviceType == ma_device_type_capture || deviceType == ma_device_type_duplex || deviceType == ma_device_type_loopback) { - pDevice->sampleRate = pDevice->capture.internalSampleRate; - } else { - pDevice->sampleRate = pDevice->playback.internalSampleRate; - } - } - - /* Data converters. */ - if (deviceType == ma_device_type_capture || deviceType == ma_device_type_duplex || deviceType == ma_device_type_loopback) { - /* Converting from internal device format to client format. */ - ma_data_converter_config converterConfig = ma_data_converter_config_init_default(); - converterConfig.formatIn = pDevice->capture.internalFormat; - converterConfig.channelsIn = pDevice->capture.internalChannels; - converterConfig.sampleRateIn = pDevice->capture.internalSampleRate; - converterConfig.pChannelMapIn = pDevice->capture.internalChannelMap; - converterConfig.formatOut = pDevice->capture.format; - converterConfig.channelsOut = pDevice->capture.channels; - converterConfig.sampleRateOut = pDevice->sampleRate; - converterConfig.pChannelMapOut = pDevice->capture.channelMap; - converterConfig.channelMixMode = pDevice->capture.channelMixMode; - converterConfig.calculateLFEFromSpatialChannels = pDevice->capture.calculateLFEFromSpatialChannels; - converterConfig.allowDynamicSampleRate = MA_FALSE; - converterConfig.resampling.algorithm = pDevice->resampling.algorithm; - converterConfig.resampling.linear.lpfOrder = pDevice->resampling.linear.lpfOrder; - converterConfig.resampling.pBackendVTable = pDevice->resampling.pBackendVTable; - converterConfig.resampling.pBackendUserData = pDevice->resampling.pBackendUserData; - - /* Make sure the old converter is uninitialized first. */ - if (ma_device_get_state(pDevice) != ma_device_state_uninitialized) { - ma_data_converter_uninit(&pDevice->capture.converter, &pDevice->pContext->allocationCallbacks); - } - - result = ma_data_converter_init(&converterConfig, &pDevice->pContext->allocationCallbacks, &pDevice->capture.converter); - if (result != MA_SUCCESS) { - return result; - } - } - - if (deviceType == ma_device_type_playback || deviceType == ma_device_type_duplex) { - /* Converting from client format to device format. */ - ma_data_converter_config converterConfig = ma_data_converter_config_init_default(); - converterConfig.formatIn = pDevice->playback.format; - converterConfig.channelsIn = pDevice->playback.channels; - converterConfig.sampleRateIn = pDevice->sampleRate; - converterConfig.pChannelMapIn = pDevice->playback.channelMap; - converterConfig.formatOut = pDevice->playback.internalFormat; - converterConfig.channelsOut = pDevice->playback.internalChannels; - converterConfig.sampleRateOut = pDevice->playback.internalSampleRate; - converterConfig.pChannelMapOut = pDevice->playback.internalChannelMap; - converterConfig.channelMixMode = pDevice->playback.channelMixMode; - converterConfig.calculateLFEFromSpatialChannels = pDevice->playback.calculateLFEFromSpatialChannels; - converterConfig.allowDynamicSampleRate = MA_FALSE; - converterConfig.resampling.algorithm = pDevice->resampling.algorithm; - converterConfig.resampling.linear.lpfOrder = pDevice->resampling.linear.lpfOrder; - converterConfig.resampling.pBackendVTable = pDevice->resampling.pBackendVTable; - converterConfig.resampling.pBackendUserData = pDevice->resampling.pBackendUserData; - - /* Make sure the old converter is uninitialized first. */ - if (ma_device_get_state(pDevice) != ma_device_state_uninitialized) { - ma_data_converter_uninit(&pDevice->playback.converter, &pDevice->pContext->allocationCallbacks); - } - - result = ma_data_converter_init(&converterConfig, &pDevice->pContext->allocationCallbacks, &pDevice->playback.converter); - if (result != MA_SUCCESS) { - return result; - } - } - - - /* - If the device is doing playback (ma_device_type_playback or ma_device_type_duplex), there's - a couple of situations where we'll need a heap allocated cache. - - The first is a duplex device for backends that use a callback for data delivery. The reason - this is needed is that the input stage needs to have a buffer to place the input data while it - waits for the playback stage, after which the miniaudio data callback will get fired. This is - not needed for backends that use a blocking API because miniaudio manages temporary buffers on - the stack to achieve this. - - The other situation is when the data converter does not have the ability to query the number - of input frames that are required in order to process a given number of output frames. When - performing data conversion, it's useful if miniaudio know exactly how many frames it needs - from the client in order to generate a given number of output frames. This way, only exactly - the number of frames are needed to be read from the client which means no cache is necessary. - On the other hand, if miniaudio doesn't know how many frames to read, it is forced to read - in fixed sized chunks and then cache any residual unused input frames, those of which will be - processed at a later stage. - */ - if (deviceType == ma_device_type_playback || deviceType == ma_device_type_duplex) { - ma_uint64 unused; - - pDevice->playback.inputCacheConsumed = 0; - pDevice->playback.inputCacheRemaining = 0; - - if (pDevice->type == ma_device_type_duplex || /* Duplex. backend may decide to use ma_device_handle_backend_data_callback() which will require this cache. */ - ma_data_converter_get_required_input_frame_count(&pDevice->playback.converter, 1, &unused) != MA_SUCCESS) /* Data conversion required input frame calculation not supported. */ - { - /* We need a heap allocated cache. We want to size this based on the period size. */ - void* pNewInputCache; - ma_uint64 newInputCacheCap; - ma_uint64 newInputCacheSizeInBytes; - - newInputCacheCap = ma_calculate_frame_count_after_resampling(pDevice->playback.internalSampleRate, pDevice->sampleRate, pDevice->playback.internalPeriodSizeInFrames); - - newInputCacheSizeInBytes = newInputCacheCap * ma_get_bytes_per_frame(pDevice->playback.format, pDevice->playback.channels); - if (newInputCacheSizeInBytes > MA_SIZE_MAX) { - ma_free(pDevice->playback.pInputCache, &pDevice->pContext->allocationCallbacks); - pDevice->playback.pInputCache = NULL; - pDevice->playback.inputCacheCap = 0; - return MA_OUT_OF_MEMORY; /* Allocation too big. Should never hit this, but makes the cast below safer for 32-bit builds. */ - } - - pNewInputCache = ma_realloc(pDevice->playback.pInputCache, (size_t)newInputCacheSizeInBytes, &pDevice->pContext->allocationCallbacks); - if (pNewInputCache == NULL) { - ma_free(pDevice->playback.pInputCache, &pDevice->pContext->allocationCallbacks); - pDevice->playback.pInputCache = NULL; - pDevice->playback.inputCacheCap = 0; - return MA_OUT_OF_MEMORY; - } - - pDevice->playback.pInputCache = pNewInputCache; - pDevice->playback.inputCacheCap = newInputCacheCap; - } else { - /* Heap allocation not required. Make sure we clear out the old cache just in case this function was called in response to a route change. */ - ma_free(pDevice->playback.pInputCache, &pDevice->pContext->allocationCallbacks); - pDevice->playback.pInputCache = NULL; - pDevice->playback.inputCacheCap = 0; - } - } - - return MA_SUCCESS; -} - -MA_API ma_result ma_device_post_init(ma_device* pDevice, ma_device_type deviceType, const ma_device_descriptor* pDescriptorPlayback, const ma_device_descriptor* pDescriptorCapture) -{ - ma_result result; - - if (pDevice == NULL) { - return MA_INVALID_ARGS; - } - - /* Capture. */ - if (deviceType == ma_device_type_capture || deviceType == ma_device_type_duplex || deviceType == ma_device_type_loopback) { - if (ma_device_descriptor_is_valid(pDescriptorCapture) == MA_FALSE) { - return MA_INVALID_ARGS; - } - - pDevice->capture.internalFormat = pDescriptorCapture->format; - pDevice->capture.internalChannels = pDescriptorCapture->channels; - pDevice->capture.internalSampleRate = pDescriptorCapture->sampleRate; - MA_COPY_MEMORY(pDevice->capture.internalChannelMap, pDescriptorCapture->channelMap, sizeof(pDescriptorCapture->channelMap)); - pDevice->capture.internalPeriodSizeInFrames = pDescriptorCapture->periodSizeInFrames; - pDevice->capture.internalPeriods = pDescriptorCapture->periodCount; - - if (pDevice->capture.internalPeriodSizeInFrames == 0) { - pDevice->capture.internalPeriodSizeInFrames = ma_calculate_buffer_size_in_frames_from_milliseconds(pDescriptorCapture->periodSizeInMilliseconds, pDescriptorCapture->sampleRate); - } - } - - /* Playback. */ - if (deviceType == ma_device_type_playback || deviceType == ma_device_type_duplex) { - if (ma_device_descriptor_is_valid(pDescriptorPlayback) == MA_FALSE) { - return MA_INVALID_ARGS; - } - - pDevice->playback.internalFormat = pDescriptorPlayback->format; - pDevice->playback.internalChannels = pDescriptorPlayback->channels; - pDevice->playback.internalSampleRate = pDescriptorPlayback->sampleRate; - MA_COPY_MEMORY(pDevice->playback.internalChannelMap, pDescriptorPlayback->channelMap, sizeof(pDescriptorPlayback->channelMap)); - pDevice->playback.internalPeriodSizeInFrames = pDescriptorPlayback->periodSizeInFrames; - pDevice->playback.internalPeriods = pDescriptorPlayback->periodCount; - - if (pDevice->playback.internalPeriodSizeInFrames == 0) { - pDevice->playback.internalPeriodSizeInFrames = ma_calculate_buffer_size_in_frames_from_milliseconds(pDescriptorPlayback->periodSizeInMilliseconds, pDescriptorPlayback->sampleRate); - } - } - - /* - The name of the device can be retrieved from device info. This may be temporary and replaced with a `ma_device_get_info(pDevice, deviceType)` instead. - For loopback devices, we need to retrieve the name of the playback device. - */ - { - ma_device_info deviceInfo; - - if (deviceType == ma_device_type_capture || deviceType == ma_device_type_duplex || deviceType == ma_device_type_loopback) { - result = ma_device_get_info(pDevice, ma_device_type_capture, &deviceInfo); - if (result == MA_SUCCESS) { - ma_strncpy_s(pDevice->capture.name, sizeof(pDevice->capture.name), deviceInfo.name, (size_t)-1); - } else { - /* We failed to retrieve the device info. Fall back to a default name. */ - if (pDescriptorCapture->pDeviceID == NULL) { - ma_strncpy_s(pDevice->capture.name, sizeof(pDevice->capture.name), MA_DEFAULT_CAPTURE_DEVICE_NAME, (size_t)-1); - } else { - ma_strncpy_s(pDevice->capture.name, sizeof(pDevice->capture.name), "Capture Device", (size_t)-1); - } - } - } - - if (deviceType == ma_device_type_playback || deviceType == ma_device_type_duplex) { - result = ma_device_get_info(pDevice, ma_device_type_playback, &deviceInfo); - if (result == MA_SUCCESS) { - ma_strncpy_s(pDevice->playback.name, sizeof(pDevice->playback.name), deviceInfo.name, (size_t)-1); - } else { - /* We failed to retrieve the device info. Fall back to a default name. */ - if (pDescriptorPlayback->pDeviceID == NULL) { - ma_strncpy_s(pDevice->playback.name, sizeof(pDevice->playback.name), MA_DEFAULT_PLAYBACK_DEVICE_NAME, (size_t)-1); - } else { - ma_strncpy_s(pDevice->playback.name, sizeof(pDevice->playback.name), "Playback Device", (size_t)-1); - } - } - } - } - - /* Update data conversion. */ - return ma_device__post_init_setup(pDevice, deviceType); /* TODO: Should probably rename ma_device__post_init_setup() to something better. */ -} - - -static ma_thread_result MA_THREADCALL ma_worker_thread(void* pData) -{ - ma_device* pDevice = (ma_device*)pData; -#ifdef MA_WIN32 - HRESULT CoInitializeResult; -#endif - - MA_ASSERT(pDevice != NULL); - -#ifdef MA_WIN32 - CoInitializeResult = ma_CoInitializeEx(pDevice->pContext, NULL, MA_COINIT_VALUE); -#endif - - /* - When the device is being initialized its initial state is set to ma_device_state_uninitialized. Before returning from - ma_device_init(), the state needs to be set to something valid. In miniaudio the device's default state immediately - after initialization is stopped, so therefore we need to mark the device as such. miniaudio will wait on the worker - thread to signal an event to know when the worker thread is ready for action. - */ - ma_device__set_state(pDevice, ma_device_state_stopped); - ma_event_signal(&pDevice->stopEvent); - - for (;;) { /* <-- This loop just keeps the thread alive. The main audio loop is inside. */ - ma_result startResult; - ma_result stopResult; /* <-- This will store the result from onDeviceStop(). If it returns an error, we don't fire the stopped notification callback. */ - - /* We wait on an event to know when something has requested that the device be started and the main loop entered. */ - ma_event_wait(&pDevice->wakeupEvent); - - /* Default result code. */ - pDevice->workResult = MA_SUCCESS; - - /* If the reason for the wake up is that we are terminating, just break from the loop. */ - if (ma_device_get_state(pDevice) == ma_device_state_uninitialized) { - break; - } - - /* - Getting to this point means the device is wanting to get started. The function that has requested that the device - be started will be waiting on an event (pDevice->startEvent) which means we need to make sure we signal the event - in both the success and error case. It's important that the state of the device is set _before_ signaling the event. - */ - MA_ASSERT(ma_device_get_state(pDevice) == ma_device_state_starting); - - /* If the device has a start callback, start it now. */ - if (pDevice->pContext->callbacks.onDeviceStart != NULL) { - startResult = pDevice->pContext->callbacks.onDeviceStart(pDevice); - } else { - startResult = MA_SUCCESS; - } - - /* - If starting was not successful we'll need to loop back to the start and wait for something - to happen (pDevice->wakeupEvent). - */ - if (startResult != MA_SUCCESS) { - pDevice->workResult = startResult; - ma_event_signal(&pDevice->startEvent); /* <-- Always signal the start event so ma_device_start() can return as it'll be waiting on it. */ - continue; - } - - /* Make sure the state is set appropriately. */ - ma_device__set_state(pDevice, ma_device_state_started); /* <-- Set this before signaling the event so that the state is always guaranteed to be good after ma_device_start() has returned. */ - ma_event_signal(&pDevice->startEvent); - - ma_device__on_notification_started(pDevice); - - if (pDevice->pContext->callbacks.onDeviceDataLoop != NULL) { - pDevice->pContext->callbacks.onDeviceDataLoop(pDevice); - } else { - /* The backend is not using a custom main loop implementation, so now fall back to the blocking read-write implementation. */ - ma_device_audio_thread__default_read_write(pDevice); - } - - /* Getting here means we have broken from the main loop which happens the application has requested that device be stopped. */ - if (pDevice->pContext->callbacks.onDeviceStop != NULL) { - stopResult = pDevice->pContext->callbacks.onDeviceStop(pDevice); - } else { - stopResult = MA_SUCCESS; /* No stop callback with the backend. Just assume successful. */ - } - - /* - After the device has stopped, make sure an event is posted. Don't post a stopped event if - stopping failed. This can happen on some backends when the underlying stream has been - stopped due to the device being physically unplugged or disabled via an OS setting. - */ - if (stopResult == MA_SUCCESS) { - ma_device__on_notification_stopped(pDevice); - } - - /* If we stopped because the device has been uninitialized, abort now. */ - if (ma_device_get_state(pDevice) == ma_device_state_uninitialized) { - break; - } - - /* A function somewhere is waiting for the device to have stopped for real so we need to signal an event to allow it to continue. */ - ma_device__set_state(pDevice, ma_device_state_stopped); - ma_event_signal(&pDevice->stopEvent); - } - -#ifdef MA_WIN32 - if (CoInitializeResult == S_OK) { - ma_CoUninitialize(pDevice->pContext); - } -#endif - - return (ma_thread_result)0; -} - - -/* Helper for determining whether or not the given device is initialized. */ -static ma_bool32 ma_device__is_initialized(ma_device* pDevice) -{ - if (pDevice == NULL) { - return MA_FALSE; - } - - return ma_device_get_state(pDevice) != ma_device_state_uninitialized; -} - - -#ifdef MA_WIN32 -static ma_result ma_context_uninit_backend_apis__win32(ma_context* pContext) -{ - /* For some reason UWP complains when CoUninitialize() is called. I'm just not going to call it on UWP. */ -#if defined(MA_WIN32_DESKTOP) || defined(MA_WIN32_GDK) - if (pContext->win32.CoInitializeResult == S_OK) { - ma_CoUninitialize(pContext); - } - - #if defined(MA_WIN32_DESKTOP) - ma_dlclose(ma_context_get_log(pContext), pContext->win32.hUser32DLL); - ma_dlclose(ma_context_get_log(pContext), pContext->win32.hAdvapi32DLL); - #endif - - ma_dlclose(ma_context_get_log(pContext), pContext->win32.hOle32DLL); -#else - (void)pContext; -#endif - - return MA_SUCCESS; -} - -static ma_result ma_context_init_backend_apis__win32(ma_context* pContext) -{ -#if defined(MA_WIN32_DESKTOP) || defined(MA_WIN32_GDK) - #if defined(MA_WIN32_DESKTOP) - /* User32.dll */ - pContext->win32.hUser32DLL = ma_dlopen(ma_context_get_log(pContext), "user32.dll"); - if (pContext->win32.hUser32DLL == NULL) { - return MA_FAILED_TO_INIT_BACKEND; - } - - pContext->win32.GetForegroundWindow = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->win32.hUser32DLL, "GetForegroundWindow"); - pContext->win32.GetDesktopWindow = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->win32.hUser32DLL, "GetDesktopWindow"); - - - /* Advapi32.dll */ - pContext->win32.hAdvapi32DLL = ma_dlopen(ma_context_get_log(pContext), "advapi32.dll"); - if (pContext->win32.hAdvapi32DLL == NULL) { - return MA_FAILED_TO_INIT_BACKEND; - } - - pContext->win32.RegOpenKeyExA = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->win32.hAdvapi32DLL, "RegOpenKeyExA"); - pContext->win32.RegCloseKey = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->win32.hAdvapi32DLL, "RegCloseKey"); - pContext->win32.RegQueryValueExA = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->win32.hAdvapi32DLL, "RegQueryValueExA"); - #endif - - /* Ole32.dll */ - pContext->win32.hOle32DLL = ma_dlopen(ma_context_get_log(pContext), "ole32.dll"); - if (pContext->win32.hOle32DLL == NULL) { - return MA_FAILED_TO_INIT_BACKEND; - } - - pContext->win32.CoInitialize = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->win32.hOle32DLL, "CoInitialize"); - pContext->win32.CoInitializeEx = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->win32.hOle32DLL, "CoInitializeEx"); - pContext->win32.CoUninitialize = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->win32.hOle32DLL, "CoUninitialize"); - pContext->win32.CoCreateInstance = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->win32.hOle32DLL, "CoCreateInstance"); - pContext->win32.CoTaskMemFree = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->win32.hOle32DLL, "CoTaskMemFree"); - pContext->win32.PropVariantClear = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->win32.hOle32DLL, "PropVariantClear"); - pContext->win32.StringFromGUID2 = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->win32.hOle32DLL, "StringFromGUID2"); -#else - (void)pContext; /* Unused. */ -#endif - - pContext->win32.CoInitializeResult = ma_CoInitializeEx(pContext, NULL, MA_COINIT_VALUE); - return MA_SUCCESS; -} -#else -static ma_result ma_context_uninit_backend_apis__nix(ma_context* pContext) -{ - (void)pContext; - - return MA_SUCCESS; -} - -static ma_result ma_context_init_backend_apis__nix(ma_context* pContext) -{ - (void)pContext; - - return MA_SUCCESS; -} -#endif - -static ma_result ma_context_init_backend_apis(ma_context* pContext) -{ - ma_result result; -#ifdef MA_WIN32 - result = ma_context_init_backend_apis__win32(pContext); -#else - result = ma_context_init_backend_apis__nix(pContext); -#endif - - return result; -} - -static ma_result ma_context_uninit_backend_apis(ma_context* pContext) -{ - ma_result result; -#ifdef MA_WIN32 - result = ma_context_uninit_backend_apis__win32(pContext); -#else - result = ma_context_uninit_backend_apis__nix(pContext); -#endif - - return result; -} - - -/* The default capacity doesn't need to be too big. */ -#ifndef MA_DEFAULT_DEVICE_JOB_QUEUE_CAPACITY -#define MA_DEFAULT_DEVICE_JOB_QUEUE_CAPACITY 32 -#endif - -MA_API ma_device_job_thread_config ma_device_job_thread_config_init(void) -{ - ma_device_job_thread_config config; - - MA_ZERO_OBJECT(&config); - config.noThread = MA_FALSE; - config.jobQueueCapacity = MA_DEFAULT_DEVICE_JOB_QUEUE_CAPACITY; - config.jobQueueFlags = 0; - - return config; -} - - -static ma_thread_result MA_THREADCALL ma_device_job_thread_entry(void* pUserData) -{ - ma_device_job_thread* pJobThread = (ma_device_job_thread*)pUserData; - MA_ASSERT(pJobThread != NULL); - - for (;;) { - ma_result result; - ma_job job; - - result = ma_device_job_thread_next(pJobThread, &job); - if (result != MA_SUCCESS) { - break; - } - - if (job.toc.breakup.code == MA_JOB_TYPE_QUIT) { - break; - } - - ma_job_process(&job); - } - - return (ma_thread_result)0; -} - -MA_API ma_result ma_device_job_thread_init(const ma_device_job_thread_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_device_job_thread* pJobThread) -{ - ma_result result; - ma_job_queue_config jobQueueConfig; - - if (pJobThread == NULL) { - return MA_INVALID_ARGS; - } - - MA_ZERO_OBJECT(pJobThread); - - if (pConfig == NULL) { - return MA_INVALID_ARGS; - } - - - /* Initialize the job queue before the thread to ensure it's in a valid state. */ - jobQueueConfig = ma_job_queue_config_init(pConfig->jobQueueFlags, pConfig->jobQueueCapacity); - - result = ma_job_queue_init(&jobQueueConfig, pAllocationCallbacks, &pJobThread->jobQueue); - if (result != MA_SUCCESS) { - return result; /* Failed to initialize job queue. */ - } - - - /* The thread needs to be initialized after the job queue to ensure the thread doesn't try to access it prematurely. */ - if (pConfig->noThread == MA_FALSE) { - result = ma_thread_create(&pJobThread->thread, ma_thread_priority_normal, 0, ma_device_job_thread_entry, pJobThread, pAllocationCallbacks); - if (result != MA_SUCCESS) { - ma_job_queue_uninit(&pJobThread->jobQueue, pAllocationCallbacks); - return result; /* Failed to create the job thread. */ - } - - pJobThread->_hasThread = MA_TRUE; - } else { - pJobThread->_hasThread = MA_FALSE; - } - - - return MA_SUCCESS; -} - -MA_API void ma_device_job_thread_uninit(ma_device_job_thread* pJobThread, const ma_allocation_callbacks* pAllocationCallbacks) -{ - if (pJobThread == NULL) { - return; - } - - /* The first thing to do is post a quit message to the job queue. If we're using a thread we'll need to wait for it. */ - { - ma_job job = ma_job_init(MA_JOB_TYPE_QUIT); - ma_device_job_thread_post(pJobThread, &job); - } - - /* Wait for the thread to terminate naturally. */ - if (pJobThread->_hasThread) { - ma_thread_wait(&pJobThread->thread); - } - - /* At this point the thread should be terminated so we can safely uninitialize the job queue. */ - ma_job_queue_uninit(&pJobThread->jobQueue, pAllocationCallbacks); -} - -MA_API ma_result ma_device_job_thread_post(ma_device_job_thread* pJobThread, const ma_job* pJob) -{ - if (pJobThread == NULL || pJob == NULL) { - return MA_INVALID_ARGS; - } - - return ma_job_queue_post(&pJobThread->jobQueue, pJob); -} - -MA_API ma_result ma_device_job_thread_next(ma_device_job_thread* pJobThread, ma_job* pJob) -{ - if (pJob == NULL) { - return MA_INVALID_ARGS; - } - - MA_ZERO_OBJECT(pJob); - - if (pJobThread == NULL) { - return MA_INVALID_ARGS; - } - - return ma_job_queue_next(&pJobThread->jobQueue, pJob); -} - - -MA_API ma_bool32 ma_device_id_equal(const ma_device_id* pA, const ma_device_id* pB) -{ - size_t i; - - if (pA == NULL || pB == NULL) { - return MA_FALSE; - } - - for (i = 0; i < sizeof(ma_device_id); i += 1) { - if (((const char*)pA)[i] != ((const char*)pB)[i]) { - return MA_FALSE; - } - } - - return MA_TRUE; -} - - - -MA_API ma_context_config ma_context_config_init(void) -{ - ma_context_config config; - MA_ZERO_OBJECT(&config); - - return config; -} - -MA_API ma_result ma_context_init(const ma_backend backends[], ma_uint32 backendCount, const ma_context_config* pConfig, ma_context* pContext) -{ - ma_result result; - ma_context_config defaultConfig; - ma_backend defaultBackends[ma_backend_null+1]; - ma_uint32 iBackend; - ma_backend* pBackendsToIterate; - ma_uint32 backendsToIterateCount; - - if (pContext == NULL) { - return MA_INVALID_ARGS; - } - - MA_ZERO_OBJECT(pContext); - - /* Always make sure the config is set first to ensure properties are available as soon as possible. */ - if (pConfig == NULL) { - defaultConfig = ma_context_config_init(); - pConfig = &defaultConfig; - } - - /* Allocation callbacks need to come first because they'll be passed around to other areas. */ - result = ma_allocation_callbacks_init_copy(&pContext->allocationCallbacks, &pConfig->allocationCallbacks); - if (result != MA_SUCCESS) { - return result; - } - - /* Get a lot set up first so we can start logging ASAP. */ - if (pConfig->pLog != NULL) { - pContext->pLog = pConfig->pLog; - } else { - result = ma_log_init(&pContext->allocationCallbacks, &pContext->log); - if (result == MA_SUCCESS) { - pContext->pLog = &pContext->log; - } else { - pContext->pLog = NULL; /* Logging is not available. */ - } - } - - pContext->threadPriority = pConfig->threadPriority; - pContext->threadStackSize = pConfig->threadStackSize; - pContext->pUserData = pConfig->pUserData; - - /* Backend APIs need to be initialized first. This is where external libraries will be loaded and linked. */ - result = ma_context_init_backend_apis(pContext); - if (result != MA_SUCCESS) { - return result; - } - - for (iBackend = 0; iBackend <= ma_backend_null; ++iBackend) { - defaultBackends[iBackend] = (ma_backend)iBackend; - } - - pBackendsToIterate = (ma_backend*)backends; - backendsToIterateCount = backendCount; - if (pBackendsToIterate == NULL) { - pBackendsToIterate = (ma_backend*)defaultBackends; - backendsToIterateCount = ma_countof(defaultBackends); - } - - MA_ASSERT(pBackendsToIterate != NULL); - - for (iBackend = 0; iBackend < backendsToIterateCount; iBackend += 1) { - ma_backend backend = pBackendsToIterate[iBackend]; - - /* Make sure all callbacks are reset so we don't accidentally drag in any from previously failed initialization attempts. */ - MA_ZERO_OBJECT(&pContext->callbacks); - - /* These backends are using the new callback system. */ - switch (backend) { - #ifdef MA_HAS_WASAPI - case ma_backend_wasapi: - { - pContext->callbacks.onContextInit = ma_context_init__wasapi; - } break; - #endif - #ifdef MA_HAS_DSOUND - case ma_backend_dsound: - { - pContext->callbacks.onContextInit = ma_context_init__dsound; - } break; - #endif - #ifdef MA_HAS_WINMM - case ma_backend_winmm: - { - pContext->callbacks.onContextInit = ma_context_init__winmm; - } break; - #endif - #ifdef MA_HAS_COREAUDIO - case ma_backend_coreaudio: - { - pContext->callbacks.onContextInit = ma_context_init__coreaudio; - } break; - #endif - #ifdef MA_HAS_SNDIO - case ma_backend_sndio: - { - pContext->callbacks.onContextInit = ma_context_init__sndio; - } break; - #endif - #ifdef MA_HAS_AUDIO4 - case ma_backend_audio4: - { - pContext->callbacks.onContextInit = ma_context_init__audio4; - } break; - #endif - #ifdef MA_HAS_OSS - case ma_backend_oss: - { - pContext->callbacks.onContextInit = ma_context_init__oss; - } break; - #endif - #ifdef MA_HAS_PULSEAUDIO - case ma_backend_pulseaudio: - { - pContext->callbacks.onContextInit = ma_context_init__pulse; - } break; - #endif - #ifdef MA_HAS_ALSA - case ma_backend_alsa: - { - pContext->callbacks.onContextInit = ma_context_init__alsa; - } break; - #endif - #ifdef MA_HAS_JACK - case ma_backend_jack: - { - pContext->callbacks.onContextInit = ma_context_init__jack; - } break; - #endif - #ifdef MA_HAS_AAUDIO - case ma_backend_aaudio: - { - if (ma_is_backend_enabled(backend)) { - pContext->callbacks.onContextInit = ma_context_init__aaudio; - } - } break; - #endif - #ifdef MA_HAS_OPENSL - case ma_backend_opensl: - { - if (ma_is_backend_enabled(backend)) { - pContext->callbacks.onContextInit = ma_context_init__opensl; - } - } break; - #endif - #ifdef MA_HAS_WEBAUDIO - case ma_backend_webaudio: - { - pContext->callbacks.onContextInit = ma_context_init__webaudio; - } break; - #endif - #ifdef MA_HAS_CUSTOM - case ma_backend_custom: - { - /* Slightly different logic for custom backends. Custom backends can optionally set all of their callbacks in the config. */ - pContext->callbacks = pConfig->custom; - } break; - #endif - #ifdef MA_HAS_NULL - case ma_backend_null: - { - pContext->callbacks.onContextInit = ma_context_init__null; - } break; - #endif - - default: break; - } - - if (pContext->callbacks.onContextInit != NULL) { - ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_DEBUG, "Attempting to initialize %s backend...\n", ma_get_backend_name(backend)); - result = pContext->callbacks.onContextInit(pContext, pConfig, &pContext->callbacks); - } else { - /* Getting here means the onContextInit callback is not set which means the backend is not enabled. Special case for the custom backend. */ - if (backend != ma_backend_custom) { - result = MA_BACKEND_NOT_ENABLED; - } else { - #if !defined(MA_HAS_CUSTOM) - result = MA_BACKEND_NOT_ENABLED; - #else - result = MA_NO_BACKEND; - #endif - } - } - - /* If this iteration was successful, return. */ - if (result == MA_SUCCESS) { - result = ma_mutex_init(&pContext->deviceEnumLock); - if (result != MA_SUCCESS) { - ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_WARNING, "Failed to initialize mutex for device enumeration. ma_context_get_devices() is not thread safe.\n"); - } - - result = ma_mutex_init(&pContext->deviceInfoLock); - if (result != MA_SUCCESS) { - ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_WARNING, "Failed to initialize mutex for device info retrieval. ma_context_get_device_info() is not thread safe.\n"); - } - - ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_DEBUG, "System Architecture:\n"); - ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_DEBUG, " Endian: %s\n", ma_is_little_endian() ? "LE" : "BE"); - ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_DEBUG, " SSE2: %s\n", ma_has_sse2() ? "YES" : "NO"); - ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_DEBUG, " AVX2: %s\n", ma_has_avx2() ? "YES" : "NO"); - ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_DEBUG, " NEON: %s\n", ma_has_neon() ? "YES" : "NO"); - - pContext->backend = backend; - return result; - } else { - if (result == MA_BACKEND_NOT_ENABLED) { - ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_DEBUG, "%s backend is disabled.\n", ma_get_backend_name(backend)); - } else { - ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_DEBUG, "Failed to initialize %s backend.\n", ma_get_backend_name(backend)); - } - } - } - - /* If we get here it means an error occurred. */ - MA_ZERO_OBJECT(pContext); /* Safety. */ - return MA_NO_BACKEND; -} - -MA_API ma_result ma_context_uninit(ma_context* pContext) -{ - if (pContext == NULL) { - return MA_INVALID_ARGS; - } - - if (pContext->callbacks.onContextUninit != NULL) { - pContext->callbacks.onContextUninit(pContext); - } - - ma_mutex_uninit(&pContext->deviceEnumLock); - ma_mutex_uninit(&pContext->deviceInfoLock); - ma_free(pContext->pDeviceInfos, &pContext->allocationCallbacks); - ma_context_uninit_backend_apis(pContext); - - if (pContext->pLog == &pContext->log) { - ma_log_uninit(&pContext->log); - } - - return MA_SUCCESS; -} - -MA_API size_t ma_context_sizeof(void) -{ - return sizeof(ma_context); -} - - -MA_API ma_log* ma_context_get_log(ma_context* pContext) -{ - if (pContext == NULL) { - return NULL; - } - - return pContext->pLog; -} - - -MA_API ma_result ma_context_enumerate_devices(ma_context* pContext, ma_enum_devices_callback_proc callback, void* pUserData) -{ - ma_result result; - - if (pContext == NULL || callback == NULL) { - return MA_INVALID_ARGS; - } - - if (pContext->callbacks.onContextEnumerateDevices == NULL) { - return MA_INVALID_OPERATION; - } - - ma_mutex_lock(&pContext->deviceEnumLock); - { - result = pContext->callbacks.onContextEnumerateDevices(pContext, callback, pUserData); - } - ma_mutex_unlock(&pContext->deviceEnumLock); - - return result; -} - - -static ma_bool32 ma_context_get_devices__enum_callback(ma_context* pContext, ma_device_type deviceType, const ma_device_info* pInfo, void* pUserData) -{ - /* - We need to insert the device info into our main internal buffer. Where it goes depends on the device type. If it's a capture device - it's just appended to the end. If it's a playback device it's inserted just before the first capture device. - */ - - /* - First make sure we have room. Since the number of devices we add to the list is usually relatively small I've decided to use a - simple fixed size increment for buffer expansion. - */ - const ma_uint32 bufferExpansionCount = 2; - const ma_uint32 totalDeviceInfoCount = pContext->playbackDeviceInfoCount + pContext->captureDeviceInfoCount; - - if (totalDeviceInfoCount >= pContext->deviceInfoCapacity) { - ma_uint32 newCapacity = pContext->deviceInfoCapacity + bufferExpansionCount; - ma_device_info* pNewInfos = (ma_device_info*)ma_realloc(pContext->pDeviceInfos, sizeof(*pContext->pDeviceInfos)*newCapacity, &pContext->allocationCallbacks); - if (pNewInfos == NULL) { - return MA_FALSE; /* Out of memory. */ - } - - pContext->pDeviceInfos = pNewInfos; - pContext->deviceInfoCapacity = newCapacity; - } - - if (deviceType == ma_device_type_playback) { - /* Playback. Insert just before the first capture device. */ - - /* The first thing to do is move all of the capture devices down a slot. */ - ma_uint32 iFirstCaptureDevice = pContext->playbackDeviceInfoCount; - size_t iCaptureDevice; - for (iCaptureDevice = totalDeviceInfoCount; iCaptureDevice > iFirstCaptureDevice; --iCaptureDevice) { - pContext->pDeviceInfos[iCaptureDevice] = pContext->pDeviceInfos[iCaptureDevice-1]; - } - - /* Now just insert where the first capture device was before moving it down a slot. */ - pContext->pDeviceInfos[iFirstCaptureDevice] = *pInfo; - pContext->playbackDeviceInfoCount += 1; - } else { - /* Capture. Insert at the end. */ - pContext->pDeviceInfos[totalDeviceInfoCount] = *pInfo; - pContext->captureDeviceInfoCount += 1; - } - - (void)pUserData; - return MA_TRUE; -} - -MA_API ma_result ma_context_get_devices(ma_context* pContext, ma_device_info** ppPlaybackDeviceInfos, ma_uint32* pPlaybackDeviceCount, ma_device_info** ppCaptureDeviceInfos, ma_uint32* pCaptureDeviceCount) -{ - ma_result result; - - /* Safety. */ - if (ppPlaybackDeviceInfos != NULL) *ppPlaybackDeviceInfos = NULL; - if (pPlaybackDeviceCount != NULL) *pPlaybackDeviceCount = 0; - if (ppCaptureDeviceInfos != NULL) *ppCaptureDeviceInfos = NULL; - if (pCaptureDeviceCount != NULL) *pCaptureDeviceCount = 0; - - if (pContext == NULL) { - return MA_INVALID_ARGS; - } - - if (pContext->callbacks.onContextEnumerateDevices == NULL) { - return MA_INVALID_OPERATION; - } - - /* Note that we don't use ma_context_enumerate_devices() here because we want to do locking at a higher level. */ - ma_mutex_lock(&pContext->deviceEnumLock); - { - /* Reset everything first. */ - pContext->playbackDeviceInfoCount = 0; - pContext->captureDeviceInfoCount = 0; - - /* Now enumerate over available devices. */ - result = pContext->callbacks.onContextEnumerateDevices(pContext, ma_context_get_devices__enum_callback, NULL); - if (result == MA_SUCCESS) { - /* Playback devices. */ - if (ppPlaybackDeviceInfos != NULL) { - *ppPlaybackDeviceInfos = pContext->pDeviceInfos; - } - if (pPlaybackDeviceCount != NULL) { - *pPlaybackDeviceCount = pContext->playbackDeviceInfoCount; - } - - /* Capture devices. */ - if (ppCaptureDeviceInfos != NULL) { - *ppCaptureDeviceInfos = pContext->pDeviceInfos; - /* Capture devices come after playback devices. */ - if (pContext->playbackDeviceInfoCount > 0) { - /* Conditional, because NULL+0 is undefined behavior. */ - *ppCaptureDeviceInfos += pContext->playbackDeviceInfoCount; - } - } - if (pCaptureDeviceCount != NULL) { - *pCaptureDeviceCount = pContext->captureDeviceInfoCount; - } - } - } - ma_mutex_unlock(&pContext->deviceEnumLock); - - return result; -} - -MA_API ma_result ma_context_get_device_info(ma_context* pContext, ma_device_type deviceType, const ma_device_id* pDeviceID, ma_device_info* pDeviceInfo) -{ - ma_result result; - ma_device_info deviceInfo; - - /* NOTE: Do not clear pDeviceInfo on entry. The reason is the pDeviceID may actually point to pDeviceInfo->id which will break things. */ - if (pContext == NULL || pDeviceInfo == NULL) { - return MA_INVALID_ARGS; - } - - MA_ZERO_OBJECT(&deviceInfo); - - /* Help the backend out by copying over the device ID if we have one. */ - if (pDeviceID != NULL) { - MA_COPY_MEMORY(&deviceInfo.id, pDeviceID, sizeof(*pDeviceID)); - } - - if (pContext->callbacks.onContextGetDeviceInfo == NULL) { - return MA_INVALID_OPERATION; - } - - ma_mutex_lock(&pContext->deviceInfoLock); - { - result = pContext->callbacks.onContextGetDeviceInfo(pContext, deviceType, pDeviceID, &deviceInfo); - } - ma_mutex_unlock(&pContext->deviceInfoLock); - - *pDeviceInfo = deviceInfo; - return result; -} - -MA_API ma_bool32 ma_context_is_loopback_supported(ma_context* pContext) -{ - if (pContext == NULL) { - return MA_FALSE; - } - - return ma_is_loopback_supported(pContext->backend); -} - - -MA_API ma_device_config ma_device_config_init(ma_device_type deviceType) -{ - ma_device_config config; - MA_ZERO_OBJECT(&config); - config.deviceType = deviceType; - config.resampling = ma_resampler_config_init(ma_format_unknown, 0, 0, 0, ma_resample_algorithm_linear); /* Format/channels/rate don't matter here. */ - - return config; -} - -MA_API ma_result ma_device_init(ma_context* pContext, const ma_device_config* pConfig, ma_device* pDevice) -{ - ma_result result; - ma_device_descriptor descriptorPlayback; - ma_device_descriptor descriptorCapture; - - /* The context can be null, in which case we self-manage it. */ - if (pContext == NULL) { - return ma_device_init_ex(NULL, 0, NULL, pConfig, pDevice); - } - - if (pDevice == NULL) { - return MA_INVALID_ARGS; - } - - MA_ZERO_OBJECT(pDevice); - - if (pConfig == NULL) { - return MA_INVALID_ARGS; - } - - /* Check that we have our callbacks defined. */ - if (pContext->callbacks.onDeviceInit == NULL) { - return MA_INVALID_OPERATION; - } - - /* Basic config validation. */ - if (pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) { - if (pConfig->capture.channels > MA_MAX_CHANNELS) { - return MA_INVALID_ARGS; - } - - if (!ma__is_channel_map_valid(pConfig->capture.pChannelMap, pConfig->capture.channels)) { - return MA_INVALID_ARGS; - } - } - - if (pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex || pConfig->deviceType == ma_device_type_loopback) { - if (pConfig->playback.channels > MA_MAX_CHANNELS) { - return MA_INVALID_ARGS; - } - - if (!ma__is_channel_map_valid(pConfig->playback.pChannelMap, pConfig->playback.channels)) { - return MA_INVALID_ARGS; - } - } - - pDevice->pContext = pContext; - - /* Set the user data and log callback ASAP to ensure it is available for the entire initialization process. */ - pDevice->pUserData = pConfig->pUserData; - pDevice->onData = pConfig->dataCallback; - pDevice->onNotification = pConfig->notificationCallback; - pDevice->onStop = pConfig->stopCallback; - - if (pConfig->playback.pDeviceID != NULL) { - MA_COPY_MEMORY(&pDevice->playback.id, pConfig->playback.pDeviceID, sizeof(pDevice->playback.id)); - pDevice->playback.pID = &pDevice->playback.id; - } else { - pDevice->playback.pID = NULL; - } - - if (pConfig->capture.pDeviceID != NULL) { - MA_COPY_MEMORY(&pDevice->capture.id, pConfig->capture.pDeviceID, sizeof(pDevice->capture.id)); - pDevice->capture.pID = &pDevice->capture.id; - } else { - pDevice->capture.pID = NULL; - } - - pDevice->noPreSilencedOutputBuffer = pConfig->noPreSilencedOutputBuffer; - pDevice->noClip = pConfig->noClip; - pDevice->noDisableDenormals = pConfig->noDisableDenormals; - pDevice->noFixedSizedCallback = pConfig->noFixedSizedCallback; - ma_atomic_float_set(&pDevice->masterVolumeFactor, 1); - - pDevice->type = pConfig->deviceType; - pDevice->sampleRate = pConfig->sampleRate; - pDevice->resampling.algorithm = pConfig->resampling.algorithm; - pDevice->resampling.linear.lpfOrder = pConfig->resampling.linear.lpfOrder; - pDevice->resampling.pBackendVTable = pConfig->resampling.pBackendVTable; - pDevice->resampling.pBackendUserData = pConfig->resampling.pBackendUserData; - - pDevice->capture.shareMode = pConfig->capture.shareMode; - pDevice->capture.format = pConfig->capture.format; - pDevice->capture.channels = pConfig->capture.channels; - ma_channel_map_copy_or_default(pDevice->capture.channelMap, ma_countof(pDevice->capture.channelMap), pConfig->capture.pChannelMap, pConfig->capture.channels); - pDevice->capture.channelMixMode = pConfig->capture.channelMixMode; - pDevice->capture.calculateLFEFromSpatialChannels = pConfig->capture.calculateLFEFromSpatialChannels; - - pDevice->playback.shareMode = pConfig->playback.shareMode; - pDevice->playback.format = pConfig->playback.format; - pDevice->playback.channels = pConfig->playback.channels; - ma_channel_map_copy_or_default(pDevice->playback.channelMap, ma_countof(pDevice->playback.channelMap), pConfig->playback.pChannelMap, pConfig->playback.channels); - pDevice->playback.channelMixMode = pConfig->playback.channelMixMode; - pDevice->playback.calculateLFEFromSpatialChannels = pConfig->playback.calculateLFEFromSpatialChannels; - - result = ma_mutex_init(&pDevice->startStopLock); - if (result != MA_SUCCESS) { - return result; - } - - /* - When the device is started, the worker thread is the one that does the actual startup of the backend device. We - use a semaphore to wait for the background thread to finish the work. The same applies for stopping the device. - - Each of these semaphores is released internally by the worker thread when the work is completed. The start - semaphore is also used to wake up the worker thread. - */ - result = ma_event_init(&pDevice->wakeupEvent); - if (result != MA_SUCCESS) { - ma_mutex_uninit(&pDevice->startStopLock); - return result; - } - - result = ma_event_init(&pDevice->startEvent); - if (result != MA_SUCCESS) { - ma_event_uninit(&pDevice->wakeupEvent); - ma_mutex_uninit(&pDevice->startStopLock); - return result; - } - - result = ma_event_init(&pDevice->stopEvent); - if (result != MA_SUCCESS) { - ma_event_uninit(&pDevice->startEvent); - ma_event_uninit(&pDevice->wakeupEvent); - ma_mutex_uninit(&pDevice->startStopLock); - return result; - } - - - MA_ZERO_OBJECT(&descriptorPlayback); - descriptorPlayback.pDeviceID = pConfig->playback.pDeviceID; - descriptorPlayback.shareMode = pConfig->playback.shareMode; - descriptorPlayback.format = pConfig->playback.format; - descriptorPlayback.channels = pConfig->playback.channels; - descriptorPlayback.sampleRate = pConfig->sampleRate; - ma_channel_map_copy_or_default(descriptorPlayback.channelMap, ma_countof(descriptorPlayback.channelMap), pConfig->playback.pChannelMap, pConfig->playback.channels); - descriptorPlayback.periodSizeInFrames = pConfig->periodSizeInFrames; - descriptorPlayback.periodSizeInMilliseconds = pConfig->periodSizeInMilliseconds; - descriptorPlayback.periodCount = pConfig->periods; - - if (descriptorPlayback.periodCount == 0) { - descriptorPlayback.periodCount = MA_DEFAULT_PERIODS; - } - - - MA_ZERO_OBJECT(&descriptorCapture); - descriptorCapture.pDeviceID = pConfig->capture.pDeviceID; - descriptorCapture.shareMode = pConfig->capture.shareMode; - descriptorCapture.format = pConfig->capture.format; - descriptorCapture.channels = pConfig->capture.channels; - descriptorCapture.sampleRate = pConfig->sampleRate; - ma_channel_map_copy_or_default(descriptorCapture.channelMap, ma_countof(descriptorCapture.channelMap), pConfig->capture.pChannelMap, pConfig->capture.channels); - descriptorCapture.periodSizeInFrames = pConfig->periodSizeInFrames; - descriptorCapture.periodSizeInMilliseconds = pConfig->periodSizeInMilliseconds; - descriptorCapture.periodCount = pConfig->periods; - - if (descriptorCapture.periodCount == 0) { - descriptorCapture.periodCount = MA_DEFAULT_PERIODS; - } - - - result = pContext->callbacks.onDeviceInit(pDevice, pConfig, &descriptorPlayback, &descriptorCapture); - if (result != MA_SUCCESS) { - ma_event_uninit(&pDevice->startEvent); - ma_event_uninit(&pDevice->wakeupEvent); - ma_mutex_uninit(&pDevice->startStopLock); - return result; - } - -#if 0 - /* - On output the descriptors will contain the *actual* data format of the device. We need this to know how to convert the data between - the requested format and the internal format. - */ - if (pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex || pConfig->deviceType == ma_device_type_loopback) { - if (!ma_device_descriptor_is_valid(&descriptorCapture)) { - ma_device_uninit(pDevice); - return MA_INVALID_ARGS; - } - - pDevice->capture.internalFormat = descriptorCapture.format; - pDevice->capture.internalChannels = descriptorCapture.channels; - pDevice->capture.internalSampleRate = descriptorCapture.sampleRate; - ma_channel_map_copy(pDevice->capture.internalChannelMap, descriptorCapture.channelMap, descriptorCapture.channels); - pDevice->capture.internalPeriodSizeInFrames = descriptorCapture.periodSizeInFrames; - pDevice->capture.internalPeriods = descriptorCapture.periodCount; - - if (pDevice->capture.internalPeriodSizeInFrames == 0) { - pDevice->capture.internalPeriodSizeInFrames = ma_calculate_buffer_size_in_frames_from_milliseconds(descriptorCapture.periodSizeInMilliseconds, descriptorCapture.sampleRate); - } - } - - if (pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) { - if (!ma_device_descriptor_is_valid(&descriptorPlayback)) { - ma_device_uninit(pDevice); - return MA_INVALID_ARGS; - } - - pDevice->playback.internalFormat = descriptorPlayback.format; - pDevice->playback.internalChannels = descriptorPlayback.channels; - pDevice->playback.internalSampleRate = descriptorPlayback.sampleRate; - ma_channel_map_copy(pDevice->playback.internalChannelMap, descriptorPlayback.channelMap, descriptorPlayback.channels); - pDevice->playback.internalPeriodSizeInFrames = descriptorPlayback.periodSizeInFrames; - pDevice->playback.internalPeriods = descriptorPlayback.periodCount; - - if (pDevice->playback.internalPeriodSizeInFrames == 0) { - pDevice->playback.internalPeriodSizeInFrames = ma_calculate_buffer_size_in_frames_from_milliseconds(descriptorPlayback.periodSizeInMilliseconds, descriptorPlayback.sampleRate); - } - } - - - /* - The name of the device can be retrieved from device info. This may be temporary and replaced with a `ma_device_get_info(pDevice, deviceType)` instead. - For loopback devices, we need to retrieve the name of the playback device. - */ - { - ma_device_info deviceInfo; - - if (pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex || pConfig->deviceType == ma_device_type_loopback) { - result = ma_device_get_info(pDevice, (pConfig->deviceType == ma_device_type_loopback) ? ma_device_type_playback : ma_device_type_capture, &deviceInfo); - if (result == MA_SUCCESS) { - ma_strncpy_s(pDevice->capture.name, sizeof(pDevice->capture.name), deviceInfo.name, (size_t)-1); - } else { - /* We failed to retrieve the device info. Fall back to a default name. */ - if (descriptorCapture.pDeviceID == NULL) { - ma_strncpy_s(pDevice->capture.name, sizeof(pDevice->capture.name), MA_DEFAULT_CAPTURE_DEVICE_NAME, (size_t)-1); - } else { - ma_strncpy_s(pDevice->capture.name, sizeof(pDevice->capture.name), "Capture Device", (size_t)-1); - } - } - } - - if (pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) { - result = ma_device_get_info(pDevice, ma_device_type_playback, &deviceInfo); - if (result == MA_SUCCESS) { - ma_strncpy_s(pDevice->playback.name, sizeof(pDevice->playback.name), deviceInfo.name, (size_t)-1); - } else { - /* We failed to retrieve the device info. Fall back to a default name. */ - if (descriptorPlayback.pDeviceID == NULL) { - ma_strncpy_s(pDevice->playback.name, sizeof(pDevice->playback.name), MA_DEFAULT_PLAYBACK_DEVICE_NAME, (size_t)-1); - } else { - ma_strncpy_s(pDevice->playback.name, sizeof(pDevice->playback.name), "Playback Device", (size_t)-1); - } - } - } - } - - - ma_device__post_init_setup(pDevice, pConfig->deviceType); -#endif - - result = ma_device_post_init(pDevice, pConfig->deviceType, &descriptorPlayback, &descriptorCapture); - if (result != MA_SUCCESS) { - ma_device_uninit(pDevice); - return result; - } - - - /* - If we're using fixed sized callbacks we'll need to make use of an intermediary buffer. Needs to - be done after post_init_setup() because we'll need access to the sample rate. - */ - if (pConfig->noFixedSizedCallback == MA_FALSE) { - /* We're using a fixed sized data callback so we'll need an intermediary buffer. */ - ma_uint32 intermediaryBufferCap = pConfig->periodSizeInFrames; - if (intermediaryBufferCap == 0) { - intermediaryBufferCap = ma_calculate_buffer_size_in_frames_from_milliseconds(pConfig->periodSizeInMilliseconds, pDevice->sampleRate); - } - - if (pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex || pConfig->deviceType == ma_device_type_loopback) { - ma_uint32 intermediaryBufferSizeInBytes; - - pDevice->capture.intermediaryBufferLen = 0; - pDevice->capture.intermediaryBufferCap = intermediaryBufferCap; - if (pDevice->capture.intermediaryBufferCap == 0) { - pDevice->capture.intermediaryBufferCap = pDevice->capture.internalPeriodSizeInFrames; - } - - intermediaryBufferSizeInBytes = pDevice->capture.intermediaryBufferCap * ma_get_bytes_per_frame(pDevice->capture.format, pDevice->capture.channels); - - pDevice->capture.pIntermediaryBuffer = ma_malloc((size_t)intermediaryBufferSizeInBytes, &pContext->allocationCallbacks); - if (pDevice->capture.pIntermediaryBuffer == NULL) { - ma_device_uninit(pDevice); - return MA_OUT_OF_MEMORY; - } - - /* Silence the buffer for safety. */ - ma_silence_pcm_frames(pDevice->capture.pIntermediaryBuffer, pDevice->capture.intermediaryBufferCap, pDevice->capture.format, pDevice->capture.channels); - pDevice->capture.intermediaryBufferLen = pDevice->capture.intermediaryBufferCap; - } - - if (pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) { - ma_uint64 intermediaryBufferSizeInBytes; - - pDevice->playback.intermediaryBufferLen = 0; - if (pConfig->deviceType == ma_device_type_duplex) { - pDevice->playback.intermediaryBufferCap = pDevice->capture.intermediaryBufferCap; /* In duplex mode, make sure the intermediary buffer is always the same size as the capture side. */ - } else { - pDevice->playback.intermediaryBufferCap = intermediaryBufferCap; - if (pDevice->playback.intermediaryBufferCap == 0) { - pDevice->playback.intermediaryBufferCap = pDevice->playback.internalPeriodSizeInFrames; - } - } - - intermediaryBufferSizeInBytes = pDevice->playback.intermediaryBufferCap * ma_get_bytes_per_frame(pDevice->playback.format, pDevice->playback.channels); - - pDevice->playback.pIntermediaryBuffer = ma_malloc((size_t)intermediaryBufferSizeInBytes, &pContext->allocationCallbacks); - if (pDevice->playback.pIntermediaryBuffer == NULL) { - ma_device_uninit(pDevice); - return MA_OUT_OF_MEMORY; - } - - /* Silence the buffer for safety. */ - ma_silence_pcm_frames(pDevice->playback.pIntermediaryBuffer, pDevice->playback.intermediaryBufferCap, pDevice->playback.format, pDevice->playback.channels); - pDevice->playback.intermediaryBufferLen = 0; - } - } else { - /* Not using a fixed sized data callback so no need for an intermediary buffer. */ - } - - - /* Some backends don't require the worker thread. */ - if (!ma_context_is_backend_asynchronous(pContext)) { - /* The worker thread. */ - result = ma_thread_create(&pDevice->thread, pContext->threadPriority, pContext->threadStackSize, ma_worker_thread, pDevice, &pContext->allocationCallbacks); - if (result != MA_SUCCESS) { - ma_device_uninit(pDevice); - return result; - } - - /* Wait for the worker thread to put the device into its stopped state for real. */ - ma_event_wait(&pDevice->stopEvent); - MA_ASSERT(ma_device_get_state(pDevice) == ma_device_state_stopped); - } else { - /* - If the backend is asynchronous and the device is duplex, we'll need an intermediary ring buffer. Note that this needs to be done - after ma_device__post_init_setup(). - */ - if (ma_context_is_backend_asynchronous(pContext)) { - if (pConfig->deviceType == ma_device_type_duplex) { - result = ma_duplex_rb_init(pDevice->capture.format, pDevice->capture.channels, pDevice->sampleRate, pDevice->capture.internalSampleRate, pDevice->capture.internalPeriodSizeInFrames, &pDevice->pContext->allocationCallbacks, &pDevice->duplexRB); - if (result != MA_SUCCESS) { - ma_device_uninit(pDevice); - return result; - } - } - } - - ma_device__set_state(pDevice, ma_device_state_stopped); - } - - /* Log device information. */ - { - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, "[%s]\n", ma_get_backend_name(pDevice->pContext->backend)); - if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex || pDevice->type == ma_device_type_loopback) { - char name[MA_MAX_DEVICE_NAME_LENGTH + 1]; - ma_device_get_name(pDevice, ma_device_type_capture, name, sizeof(name), NULL); - - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, " %s (%s)\n", name, "Capture"); - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, " Format: %s -> %s\n", ma_get_format_name(pDevice->capture.internalFormat), ma_get_format_name(pDevice->capture.format)); - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, " Channels: %d -> %d\n", pDevice->capture.internalChannels, pDevice->capture.channels); - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, " Sample Rate: %d -> %d\n", pDevice->capture.internalSampleRate, pDevice->sampleRate); - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, " Buffer Size: %d*%d (%d)\n", pDevice->capture.internalPeriodSizeInFrames, pDevice->capture.internalPeriods, (pDevice->capture.internalPeriodSizeInFrames * pDevice->capture.internalPeriods)); - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, " Conversion:\n"); - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, " Pre Format Conversion: %s\n", pDevice->capture.converter.hasPreFormatConversion ? "YES" : "NO"); - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, " Post Format Conversion: %s\n", pDevice->capture.converter.hasPostFormatConversion ? "YES" : "NO"); - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, " Channel Routing: %s\n", pDevice->capture.converter.hasChannelConverter ? "YES" : "NO"); - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, " Resampling: %s\n", pDevice->capture.converter.hasResampler ? "YES" : "NO"); - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, " Passthrough: %s\n", pDevice->capture.converter.isPassthrough ? "YES" : "NO"); - { - char channelMapStr[1024]; - ma_channel_map_to_string(pDevice->capture.internalChannelMap, pDevice->capture.internalChannels, channelMapStr, sizeof(channelMapStr)); - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, " Channel Map In: {%s}\n", channelMapStr); - - ma_channel_map_to_string(pDevice->capture.channelMap, pDevice->capture.channels, channelMapStr, sizeof(channelMapStr)); - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, " Channel Map Out: {%s}\n", channelMapStr); - } - } - if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) { - char name[MA_MAX_DEVICE_NAME_LENGTH + 1]; - ma_device_get_name(pDevice, ma_device_type_playback, name, sizeof(name), NULL); - - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, " %s (%s)\n", name, "Playback"); - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, " Format: %s -> %s\n", ma_get_format_name(pDevice->playback.format), ma_get_format_name(pDevice->playback.internalFormat)); - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, " Channels: %d -> %d\n", pDevice->playback.channels, pDevice->playback.internalChannels); - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, " Sample Rate: %d -> %d\n", pDevice->sampleRate, pDevice->playback.internalSampleRate); - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, " Buffer Size: %d*%d (%d)\n", pDevice->playback.internalPeriodSizeInFrames, pDevice->playback.internalPeriods, (pDevice->playback.internalPeriodSizeInFrames * pDevice->playback.internalPeriods)); - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, " Conversion:\n"); - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, " Pre Format Conversion: %s\n", pDevice->playback.converter.hasPreFormatConversion ? "YES" : "NO"); - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, " Post Format Conversion: %s\n", pDevice->playback.converter.hasPostFormatConversion ? "YES" : "NO"); - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, " Channel Routing: %s\n", pDevice->playback.converter.hasChannelConverter ? "YES" : "NO"); - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, " Resampling: %s\n", pDevice->playback.converter.hasResampler ? "YES" : "NO"); - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, " Passthrough: %s\n", pDevice->playback.converter.isPassthrough ? "YES" : "NO"); - { - char channelMapStr[1024]; - ma_channel_map_to_string(pDevice->playback.channelMap, pDevice->playback.channels, channelMapStr, sizeof(channelMapStr)); - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, " Channel Map In: {%s}\n", channelMapStr); - - ma_channel_map_to_string(pDevice->playback.internalChannelMap, pDevice->playback.internalChannels, channelMapStr, sizeof(channelMapStr)); - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, " Channel Map Out: {%s}\n", channelMapStr); - } - } - } - - MA_ASSERT(ma_device_get_state(pDevice) == ma_device_state_stopped); - return MA_SUCCESS; -} - -MA_API ma_result ma_device_init_ex(const ma_backend backends[], ma_uint32 backendCount, const ma_context_config* pContextConfig, const ma_device_config* pConfig, ma_device* pDevice) -{ - ma_result result; - ma_context* pContext; - ma_backend defaultBackends[ma_backend_null+1]; - ma_uint32 iBackend; - ma_backend* pBackendsToIterate; - ma_uint32 backendsToIterateCount; - ma_allocation_callbacks allocationCallbacks; - - if (pConfig == NULL) { - return MA_INVALID_ARGS; - } - - if (pContextConfig != NULL) { - result = ma_allocation_callbacks_init_copy(&allocationCallbacks, &pContextConfig->allocationCallbacks); - if (result != MA_SUCCESS) { - return result; - } - } else { - allocationCallbacks = ma_allocation_callbacks_init_default(); - } - - pContext = (ma_context*)ma_malloc(sizeof(*pContext), &allocationCallbacks); - if (pContext == NULL) { - return MA_OUT_OF_MEMORY; - } - - for (iBackend = 0; iBackend <= ma_backend_null; ++iBackend) { - defaultBackends[iBackend] = (ma_backend)iBackend; - } - - pBackendsToIterate = (ma_backend*)backends; - backendsToIterateCount = backendCount; - if (pBackendsToIterate == NULL) { - pBackendsToIterate = (ma_backend*)defaultBackends; - backendsToIterateCount = ma_countof(defaultBackends); - } - - result = MA_NO_BACKEND; - - for (iBackend = 0; iBackend < backendsToIterateCount; ++iBackend) { - /* - This is a hack for iOS. If the context config is null, there's a good chance the - `ma_device_init(NULL, &deviceConfig, pDevice);` pattern is being used. In this - case, set the session category based on the device type. - */ - #if defined(MA_APPLE_MOBILE) - ma_context_config contextConfig; - - if (pContextConfig == NULL) { - contextConfig = ma_context_config_init(); - switch (pConfig->deviceType) { - case ma_device_type_duplex: { - contextConfig.coreaudio.sessionCategory = ma_ios_session_category_play_and_record; - } break; - case ma_device_type_capture: { - contextConfig.coreaudio.sessionCategory = ma_ios_session_category_record; - } break; - case ma_device_type_playback: - default: { - contextConfig.coreaudio.sessionCategory = ma_ios_session_category_playback; - } break; - } - - pContextConfig = &contextConfig; - } - #endif - - result = ma_context_init(&pBackendsToIterate[iBackend], 1, pContextConfig, pContext); - if (result == MA_SUCCESS) { - result = ma_device_init(pContext, pConfig, pDevice); - if (result == MA_SUCCESS) { - break; /* Success. */ - } else { - ma_context_uninit(pContext); /* Failure. */ - } - } - } - - if (result != MA_SUCCESS) { - ma_free(pContext, &allocationCallbacks); - return result; - } - - pDevice->isOwnerOfContext = MA_TRUE; - return result; -} - -MA_API void ma_device_uninit(ma_device* pDevice) -{ - if (!ma_device__is_initialized(pDevice)) { - return; - } - - /* - It's possible for the miniaudio side of the device and the backend to not be in sync due to - system-level situations such as the computer being put into sleep mode and the backend not - notifying miniaudio of the fact the device has stopped. It's possible for this to result in a - deadlock due to miniaudio thinking the device is in a running state, when in fact it's not - running at all. For this reason I am no longer explicitly stopping the device. I don't think - this should affect anyone in practice since uninitializing the backend will naturally stop the - device anyway. - */ - #if 0 - { - /* Make sure the device is stopped first. The backends will probably handle this naturally, but I like to do it explicitly for my own sanity. */ - if (ma_device_is_started(pDevice)) { - ma_device_stop(pDevice); - } - } - #endif - - /* Putting the device into an uninitialized state will make the worker thread return. */ - ma_device__set_state(pDevice, ma_device_state_uninitialized); - - /* Wake up the worker thread and wait for it to properly terminate. */ - if (!ma_context_is_backend_asynchronous(pDevice->pContext)) { - ma_event_signal(&pDevice->wakeupEvent); - ma_thread_wait(&pDevice->thread); - } - - if (pDevice->pContext->callbacks.onDeviceUninit != NULL) { - pDevice->pContext->callbacks.onDeviceUninit(pDevice); - } - - - ma_event_uninit(&pDevice->stopEvent); - ma_event_uninit(&pDevice->startEvent); - ma_event_uninit(&pDevice->wakeupEvent); - ma_mutex_uninit(&pDevice->startStopLock); - - if (ma_context_is_backend_asynchronous(pDevice->pContext)) { - if (pDevice->type == ma_device_type_duplex) { - ma_duplex_rb_uninit(&pDevice->duplexRB); - } - } - - if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex || pDevice->type == ma_device_type_loopback) { - ma_data_converter_uninit(&pDevice->capture.converter, &pDevice->pContext->allocationCallbacks); - } - if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) { - ma_data_converter_uninit(&pDevice->playback.converter, &pDevice->pContext->allocationCallbacks); - } - - if (pDevice->playback.pInputCache != NULL) { - ma_free(pDevice->playback.pInputCache, &pDevice->pContext->allocationCallbacks); - } - - if (pDevice->capture.pIntermediaryBuffer != NULL) { - ma_free(pDevice->capture.pIntermediaryBuffer, &pDevice->pContext->allocationCallbacks); - } - if (pDevice->playback.pIntermediaryBuffer != NULL) { - ma_free(pDevice->playback.pIntermediaryBuffer, &pDevice->pContext->allocationCallbacks); - } - - if (pDevice->isOwnerOfContext) { - ma_allocation_callbacks allocationCallbacks = pDevice->pContext->allocationCallbacks; - - ma_context_uninit(pDevice->pContext); - ma_free(pDevice->pContext, &allocationCallbacks); - } - - MA_ZERO_OBJECT(pDevice); -} - -MA_API ma_context* ma_device_get_context(ma_device* pDevice) -{ - if (pDevice == NULL) { - return NULL; - } - - return pDevice->pContext; -} - -MA_API ma_log* ma_device_get_log(ma_device* pDevice) -{ - return ma_context_get_log(ma_device_get_context(pDevice)); -} - -MA_API ma_result ma_device_get_info(ma_device* pDevice, ma_device_type type, ma_device_info* pDeviceInfo) -{ - if (pDeviceInfo == NULL) { - return MA_INVALID_ARGS; - } - - MA_ZERO_OBJECT(pDeviceInfo); - - if (pDevice == NULL) { - return MA_INVALID_ARGS; - } - - /* If the onDeviceGetInfo() callback is set, use that. Otherwise we'll fall back to ma_context_get_device_info(). */ - if (pDevice->pContext->callbacks.onDeviceGetInfo != NULL) { - return pDevice->pContext->callbacks.onDeviceGetInfo(pDevice, type, pDeviceInfo); - } - - /* Getting here means onDeviceGetInfo is not implemented so we need to fall back to an alternative. */ - if (type == ma_device_type_playback) { - return ma_context_get_device_info(pDevice->pContext, type, pDevice->playback.pID, pDeviceInfo); - } else { - /* - Here we're getting the capture side, which is the branch we'll be entering for a loopback - device, since loopback is capturing. However, if the device is using the default device ID, - it won't get the correct information because it'll think we're asking for the default - capture device, where in fact for loopback we want the default *playback* device. We'll do - a bit of a hack here to make sure we get the correct info. - */ - if (pDevice->type == ma_device_type_loopback && pDevice->capture.pID == NULL) { - type = ma_device_type_playback; - } - - return ma_context_get_device_info(pDevice->pContext, type, pDevice->capture.pID, pDeviceInfo); - } -} - -MA_API ma_result ma_device_get_name(ma_device* pDevice, ma_device_type type, char* pName, size_t nameCap, size_t* pLengthNotIncludingNullTerminator) -{ - ma_result result; - ma_device_info deviceInfo; - - if (pLengthNotIncludingNullTerminator != NULL) { - *pLengthNotIncludingNullTerminator = 0; - } - - if (pName != NULL && nameCap > 0) { - pName[0] = '\0'; - } - - result = ma_device_get_info(pDevice, type, &deviceInfo); - if (result != MA_SUCCESS) { - return result; - } - - if (pName != NULL) { - ma_strncpy_s(pName, nameCap, deviceInfo.name, (size_t)-1); - - /* - For safety, make sure the length is based on the truncated output string rather than the - source. Otherwise the caller might assume the output buffer contains more content than it - actually does. - */ - if (pLengthNotIncludingNullTerminator != NULL) { - *pLengthNotIncludingNullTerminator = strlen(pName); - } - } else { - /* Name not specified. Just report the length of the source string. */ - if (pLengthNotIncludingNullTerminator != NULL) { - *pLengthNotIncludingNullTerminator = strlen(deviceInfo.name); - } - } - - return MA_SUCCESS; -} - -MA_API ma_result ma_device_start(ma_device* pDevice) -{ - ma_result result; - - if (pDevice == NULL) { - return MA_INVALID_ARGS; - } - - if (ma_device_get_state(pDevice) == ma_device_state_uninitialized) { - return MA_INVALID_OPERATION; /* Not initialized. */ - } - - if (ma_device_get_state(pDevice) == ma_device_state_started) { - return MA_SUCCESS; /* Already started. */ - } - - ma_mutex_lock(&pDevice->startStopLock); - { - /* - We need to check again if the device is in a started state because it's possible for one thread to have started the device - while another was waiting on the mutex. - */ - if (ma_device_get_state(pDevice) == ma_device_state_started) { - ma_mutex_unlock(&pDevice->startStopLock); - return MA_SUCCESS; /* Already started. */ - } - - /* Starting and stopping are wrapped in a mutex which means we can assert that the device is in a stopped or paused state. */ - MA_ASSERT(ma_device_get_state(pDevice) == ma_device_state_stopped); - - ma_device__set_state(pDevice, ma_device_state_starting); - - /* Asynchronous backends need to be handled differently. */ - if (ma_context_is_backend_asynchronous(pDevice->pContext)) { - if (pDevice->pContext->callbacks.onDeviceStart != NULL) { - result = pDevice->pContext->callbacks.onDeviceStart(pDevice); - } else { - result = MA_INVALID_OPERATION; - } - - if (result == MA_SUCCESS) { - ma_device__set_state(pDevice, ma_device_state_started); - ma_device__on_notification_started(pDevice); - } - } else { - /* - Synchronous backends are started by signaling an event that's being waited on in the worker thread. We first wake up the - thread and then wait for the start event. - */ - ma_event_signal(&pDevice->wakeupEvent); - - /* - Wait for the worker thread to finish starting the device. Note that the worker thread will be the one who puts the device - into the started state. Don't call ma_device__set_state() here. - */ - ma_event_wait(&pDevice->startEvent); - result = pDevice->workResult; - } - - /* We changed the state from stopped to started, so if we failed, make sure we put the state back to stopped. */ - if (result != MA_SUCCESS) { - ma_device__set_state(pDevice, ma_device_state_stopped); - } - } - ma_mutex_unlock(&pDevice->startStopLock); - - return result; -} - -MA_API ma_result ma_device_stop(ma_device* pDevice) -{ - ma_result result; - - if (pDevice == NULL) { - return MA_INVALID_ARGS; - } - - if (ma_device_get_state(pDevice) == ma_device_state_uninitialized) { - return MA_INVALID_OPERATION; /* Not initialized. */ - } - - if (ma_device_get_state(pDevice) == ma_device_state_stopped) { - return MA_SUCCESS; /* Already stopped. */ - } - - ma_mutex_lock(&pDevice->startStopLock); - { - /* - We need to check again if the device is in a stopped state because it's possible for one thread to have stopped the device - while another was waiting on the mutex. - */ - if (ma_device_get_state(pDevice) == ma_device_state_stopped) { - ma_mutex_unlock(&pDevice->startStopLock); - return MA_SUCCESS; /* Already stopped. */ - } - - /* Starting and stopping are wrapped in a mutex which means we can assert that the device is in a started or paused state. */ - MA_ASSERT(ma_device_get_state(pDevice) == ma_device_state_started); - - ma_device__set_state(pDevice, ma_device_state_stopping); - - /* Asynchronous backends need to be handled differently. */ - if (ma_context_is_backend_asynchronous(pDevice->pContext)) { - /* Asynchronous backends must have a stop operation. */ - if (pDevice->pContext->callbacks.onDeviceStop != NULL) { - result = pDevice->pContext->callbacks.onDeviceStop(pDevice); - } else { - result = MA_INVALID_OPERATION; - } - - ma_device__set_state(pDevice, ma_device_state_stopped); - } else { - /* - Synchronous backends. The stop callback is always called from the worker thread. Do not call the stop callback here. If - the backend is implementing its own audio thread loop we'll need to wake it up if required. Note that we need to make - sure the state of the device is *not* playing right now, which it shouldn't be since we set it above. This is super - important though, so I'm asserting it here as well for extra safety in case we accidentally change something later. - */ - MA_ASSERT(ma_device_get_state(pDevice) != ma_device_state_started); - - if (pDevice->pContext->callbacks.onDeviceDataLoopWakeup != NULL) { - pDevice->pContext->callbacks.onDeviceDataLoopWakeup(pDevice); - } - - /* - We need to wait for the worker thread to become available for work before returning. Note that the worker thread will be - the one who puts the device into the stopped state. Don't call ma_device__set_state() here. - */ - ma_event_wait(&pDevice->stopEvent); - result = MA_SUCCESS; - } - - /* - This is a safety measure to ensure the internal buffer has been cleared so any leftover - does not get played the next time the device starts. Ideally this should be drained by - the backend first. - */ - pDevice->playback.intermediaryBufferLen = 0; - pDevice->playback.inputCacheConsumed = 0; - pDevice->playback.inputCacheRemaining = 0; - } - ma_mutex_unlock(&pDevice->startStopLock); - - return result; -} - -MA_API ma_bool32 ma_device_is_started(const ma_device* pDevice) -{ - return ma_device_get_state(pDevice) == ma_device_state_started; -} - -MA_API ma_device_state ma_device_get_state(const ma_device* pDevice) -{ - if (pDevice == NULL) { - return ma_device_state_uninitialized; - } - - return ma_atomic_device_state_get((ma_atomic_device_state*)&pDevice->state); /* Naughty cast to get rid of a const warning. */ -} - -MA_API ma_result ma_device_set_master_volume(ma_device* pDevice, float volume) -{ - if (pDevice == NULL) { - return MA_INVALID_ARGS; - } - - if (volume < 0.0f) { - return MA_INVALID_ARGS; - } - - ma_atomic_float_set(&pDevice->masterVolumeFactor, volume); - - return MA_SUCCESS; -} - -MA_API ma_result ma_device_get_master_volume(ma_device* pDevice, float* pVolume) -{ - if (pVolume == NULL) { - return MA_INVALID_ARGS; - } - - if (pDevice == NULL) { - *pVolume = 0; - return MA_INVALID_ARGS; - } - - *pVolume = ma_atomic_float_get(&pDevice->masterVolumeFactor); - - return MA_SUCCESS; -} - -MA_API ma_result ma_device_set_master_volume_db(ma_device* pDevice, float gainDB) -{ - if (gainDB > 0) { - return MA_INVALID_ARGS; - } - - return ma_device_set_master_volume(pDevice, ma_volume_db_to_linear(gainDB)); -} - -MA_API ma_result ma_device_get_master_volume_db(ma_device* pDevice, float* pGainDB) -{ - float factor; - ma_result result; - - if (pGainDB == NULL) { - return MA_INVALID_ARGS; - } - - result = ma_device_get_master_volume(pDevice, &factor); - if (result != MA_SUCCESS) { - *pGainDB = 0; - return result; - } - - *pGainDB = ma_volume_linear_to_db(factor); - - return MA_SUCCESS; -} - - -MA_API ma_result ma_device_handle_backend_data_callback(ma_device* pDevice, void* pOutput, const void* pInput, ma_uint32 frameCount) -{ - if (pDevice == NULL) { - return MA_INVALID_ARGS; - } - - if (pOutput == NULL && pInput == NULL) { - return MA_INVALID_ARGS; - } - - /* - There is an assert deeper in the code that checks that frameCount > 0. Since this is a public facing - API we'll need to check for that here. I've had reports that AAudio can sometimes post a frame count - of 0. - */ - if (frameCount == 0) { - return MA_INVALID_ARGS; - } - - if (pDevice->type == ma_device_type_duplex) { - if (pInput != NULL) { - ma_device__handle_duplex_callback_capture(pDevice, frameCount, pInput, &pDevice->duplexRB.rb); - } - - if (pOutput != NULL) { - ma_device__handle_duplex_callback_playback(pDevice, frameCount, pOutput, &pDevice->duplexRB.rb); - } - } else { - if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_loopback) { - if (pInput == NULL) { - return MA_INVALID_ARGS; - } - - ma_device__send_frames_to_client(pDevice, frameCount, pInput); - } - - if (pDevice->type == ma_device_type_playback) { - if (pOutput == NULL) { - return MA_INVALID_ARGS; - } - - ma_device__read_frames_from_client(pDevice, frameCount, pOutput); - } - } - - return MA_SUCCESS; -} - -MA_API ma_uint32 ma_calculate_buffer_size_in_frames_from_descriptor(const ma_device_descriptor* pDescriptor, ma_uint32 nativeSampleRate, ma_performance_profile performanceProfile) -{ - if (pDescriptor == NULL) { - return 0; - } - - /* - We must have a non-0 native sample rate, but some backends don't allow retrieval of this at the - time when the size of the buffer needs to be determined. In this case we need to just take a best - guess and move on. We'll try using the sample rate in pDescriptor first. If that's not set we'll - just fall back to MA_DEFAULT_SAMPLE_RATE. - */ - if (nativeSampleRate == 0) { - nativeSampleRate = pDescriptor->sampleRate; - } - if (nativeSampleRate == 0) { - nativeSampleRate = MA_DEFAULT_SAMPLE_RATE; - } - - MA_ASSERT(nativeSampleRate != 0); - - if (pDescriptor->periodSizeInFrames == 0) { - if (pDescriptor->periodSizeInMilliseconds == 0) { - if (performanceProfile == ma_performance_profile_low_latency) { - return ma_calculate_buffer_size_in_frames_from_milliseconds(MA_DEFAULT_PERIOD_SIZE_IN_MILLISECONDS_LOW_LATENCY, nativeSampleRate); - } else { - return ma_calculate_buffer_size_in_frames_from_milliseconds(MA_DEFAULT_PERIOD_SIZE_IN_MILLISECONDS_CONSERVATIVE, nativeSampleRate); - } - } else { - return ma_calculate_buffer_size_in_frames_from_milliseconds(pDescriptor->periodSizeInMilliseconds, nativeSampleRate); - } - } else { - return pDescriptor->periodSizeInFrames; - } -} -#endif /* MA_NO_DEVICE_IO */ - - -MA_API ma_uint32 ma_calculate_buffer_size_in_milliseconds_from_frames(ma_uint32 bufferSizeInFrames, ma_uint32 sampleRate) -{ - /* Prevent a division by zero. */ - if (sampleRate == 0) { - return 0; - } - - return (bufferSizeInFrames*1000 + (sampleRate - 1)) / sampleRate; -} - -MA_API ma_uint32 ma_calculate_buffer_size_in_frames_from_milliseconds(ma_uint32 bufferSizeInMilliseconds, ma_uint32 sampleRate) -{ - /* Prevent a division by zero. */ - if (sampleRate == 0) { - return 0; - } - - return bufferSizeInMilliseconds*sampleRate / 1000; -} - -MA_API void ma_copy_pcm_frames(void* dst, const void* src, ma_uint64 frameCount, ma_format format, ma_uint32 channels) -{ - if (dst == src) { - return; /* No-op. */ - } - - ma_copy_memory_64(dst, src, frameCount * ma_get_bytes_per_frame(format, channels)); -} - -MA_API void ma_silence_pcm_frames(void* p, ma_uint64 frameCount, ma_format format, ma_uint32 channels) -{ - if (format == ma_format_u8) { - ma_uint64 sampleCount = frameCount * channels; - ma_uint64 iSample; - for (iSample = 0; iSample < sampleCount; iSample += 1) { - ((ma_uint8*)p)[iSample] = 128; - } - } else { - ma_zero_memory_64(p, frameCount * ma_get_bytes_per_frame(format, channels)); - } -} - -MA_API void* ma_offset_pcm_frames_ptr(void* p, ma_uint64 offsetInFrames, ma_format format, ma_uint32 channels) -{ - return ma_offset_ptr(p, offsetInFrames * ma_get_bytes_per_frame(format, channels)); -} - -MA_API const void* ma_offset_pcm_frames_const_ptr(const void* p, ma_uint64 offsetInFrames, ma_format format, ma_uint32 channels) -{ - return ma_offset_ptr(p, offsetInFrames * ma_get_bytes_per_frame(format, channels)); -} - - -MA_API void ma_clip_samples_u8(ma_uint8* pDst, const ma_int16* pSrc, ma_uint64 count) -{ - ma_uint64 iSample; - - MA_ASSERT(pDst != NULL); - MA_ASSERT(pSrc != NULL); - - for (iSample = 0; iSample < count; iSample += 1) { - pDst[iSample] = ma_clip_u8(pSrc[iSample]); - } -} - -MA_API void ma_clip_samples_s16(ma_int16* pDst, const ma_int32* pSrc, ma_uint64 count) -{ - ma_uint64 iSample; - - MA_ASSERT(pDst != NULL); - MA_ASSERT(pSrc != NULL); - - for (iSample = 0; iSample < count; iSample += 1) { - pDst[iSample] = ma_clip_s16(pSrc[iSample]); - } -} - -MA_API void ma_clip_samples_s24(ma_uint8* pDst, const ma_int64* pSrc, ma_uint64 count) -{ - ma_uint64 iSample; - - MA_ASSERT(pDst != NULL); - MA_ASSERT(pSrc != NULL); - - for (iSample = 0; iSample < count; iSample += 1) { - ma_int64 s = ma_clip_s24(pSrc[iSample]); - pDst[iSample*3 + 0] = (ma_uint8)((s & 0x000000FF) >> 0); - pDst[iSample*3 + 1] = (ma_uint8)((s & 0x0000FF00) >> 8); - pDst[iSample*3 + 2] = (ma_uint8)((s & 0x00FF0000) >> 16); - } -} - -MA_API void ma_clip_samples_s32(ma_int32* pDst, const ma_int64* pSrc, ma_uint64 count) -{ - ma_uint64 iSample; - - MA_ASSERT(pDst != NULL); - MA_ASSERT(pSrc != NULL); - - for (iSample = 0; iSample < count; iSample += 1) { - pDst[iSample] = ma_clip_s32(pSrc[iSample]); - } -} - -MA_API void ma_clip_samples_f32(float* pDst, const float* pSrc, ma_uint64 count) -{ - ma_uint64 iSample; - - MA_ASSERT(pDst != NULL); - MA_ASSERT(pSrc != NULL); - - for (iSample = 0; iSample < count; iSample += 1) { - pDst[iSample] = ma_clip_f32(pSrc[iSample]); - } -} - -MA_API void ma_clip_pcm_frames(void* pDst, const void* pSrc, ma_uint64 frameCount, ma_format format, ma_uint32 channels) -{ - ma_uint64 sampleCount; - - MA_ASSERT(pDst != NULL); - MA_ASSERT(pSrc != NULL); - - sampleCount = frameCount * channels; - - switch (format) { - case ma_format_u8: ma_clip_samples_u8( (ma_uint8*)pDst, (const ma_int16*)pSrc, sampleCount); break; - case ma_format_s16: ma_clip_samples_s16((ma_int16*)pDst, (const ma_int32*)pSrc, sampleCount); break; - case ma_format_s24: ma_clip_samples_s24((ma_uint8*)pDst, (const ma_int64*)pSrc, sampleCount); break; - case ma_format_s32: ma_clip_samples_s32((ma_int32*)pDst, (const ma_int64*)pSrc, sampleCount); break; - case ma_format_f32: ma_clip_samples_f32(( float*)pDst, (const float*)pSrc, sampleCount); break; - - /* Do nothing if we don't know the format. We're including these here to silence a compiler warning about enums not being handled by the switch. */ - case ma_format_unknown: - case ma_format_count: - break; - } -} - - -MA_API void ma_copy_and_apply_volume_factor_u8(ma_uint8* pSamplesOut, const ma_uint8* pSamplesIn, ma_uint64 sampleCount, float factor) -{ - ma_uint64 iSample; - - if (pSamplesOut == NULL || pSamplesIn == NULL) { - return; - } - - for (iSample = 0; iSample < sampleCount; iSample += 1) { - pSamplesOut[iSample] = (ma_uint8)(pSamplesIn[iSample] * factor); - } -} - -MA_API void ma_copy_and_apply_volume_factor_s16(ma_int16* pSamplesOut, const ma_int16* pSamplesIn, ma_uint64 sampleCount, float factor) -{ - ma_uint64 iSample; - - if (pSamplesOut == NULL || pSamplesIn == NULL) { - return; - } - - for (iSample = 0; iSample < sampleCount; iSample += 1) { - pSamplesOut[iSample] = (ma_int16)(pSamplesIn[iSample] * factor); - } -} - -MA_API void ma_copy_and_apply_volume_factor_s24(void* pSamplesOut, const void* pSamplesIn, ma_uint64 sampleCount, float factor) -{ - ma_uint64 iSample; - ma_uint8* pSamplesOut8; - ma_uint8* pSamplesIn8; - - if (pSamplesOut == NULL || pSamplesIn == NULL) { - return; - } - - pSamplesOut8 = (ma_uint8*)pSamplesOut; - pSamplesIn8 = (ma_uint8*)pSamplesIn; - - for (iSample = 0; iSample < sampleCount; iSample += 1) { - ma_int32 sampleS32; - - sampleS32 = (ma_int32)(((ma_uint32)(pSamplesIn8[iSample*3+0]) << 8) | ((ma_uint32)(pSamplesIn8[iSample*3+1]) << 16) | ((ma_uint32)(pSamplesIn8[iSample*3+2])) << 24); - sampleS32 = (ma_int32)(sampleS32 * factor); - - pSamplesOut8[iSample*3+0] = (ma_uint8)(((ma_uint32)sampleS32 & 0x0000FF00) >> 8); - pSamplesOut8[iSample*3+1] = (ma_uint8)(((ma_uint32)sampleS32 & 0x00FF0000) >> 16); - pSamplesOut8[iSample*3+2] = (ma_uint8)(((ma_uint32)sampleS32 & 0xFF000000) >> 24); - } -} - -MA_API void ma_copy_and_apply_volume_factor_s32(ma_int32* pSamplesOut, const ma_int32* pSamplesIn, ma_uint64 sampleCount, float factor) -{ - ma_uint64 iSample; - - if (pSamplesOut == NULL || pSamplesIn == NULL) { - return; - } - - for (iSample = 0; iSample < sampleCount; iSample += 1) { - pSamplesOut[iSample] = (ma_int32)(pSamplesIn[iSample] * factor); - } -} - -MA_API void ma_copy_and_apply_volume_factor_f32(float* pSamplesOut, const float* pSamplesIn, ma_uint64 sampleCount, float factor) -{ - ma_uint64 iSample; - - if (pSamplesOut == NULL || pSamplesIn == NULL) { - return; - } - - if (factor == 1) { - if (pSamplesOut == pSamplesIn) { - /* In place. No-op. */ - } else { - /* Just a copy. */ - for (iSample = 0; iSample < sampleCount; iSample += 1) { - pSamplesOut[iSample] = pSamplesIn[iSample]; - } - } - } else { - for (iSample = 0; iSample < sampleCount; iSample += 1) { - pSamplesOut[iSample] = pSamplesIn[iSample] * factor; - } - } -} - -MA_API void ma_apply_volume_factor_u8(ma_uint8* pSamples, ma_uint64 sampleCount, float factor) -{ - ma_copy_and_apply_volume_factor_u8(pSamples, pSamples, sampleCount, factor); -} - -MA_API void ma_apply_volume_factor_s16(ma_int16* pSamples, ma_uint64 sampleCount, float factor) -{ - ma_copy_and_apply_volume_factor_s16(pSamples, pSamples, sampleCount, factor); -} - -MA_API void ma_apply_volume_factor_s24(void* pSamples, ma_uint64 sampleCount, float factor) -{ - ma_copy_and_apply_volume_factor_s24(pSamples, pSamples, sampleCount, factor); -} - -MA_API void ma_apply_volume_factor_s32(ma_int32* pSamples, ma_uint64 sampleCount, float factor) -{ - ma_copy_and_apply_volume_factor_s32(pSamples, pSamples, sampleCount, factor); -} - -MA_API void ma_apply_volume_factor_f32(float* pSamples, ma_uint64 sampleCount, float factor) -{ - ma_copy_and_apply_volume_factor_f32(pSamples, pSamples, sampleCount, factor); -} - -MA_API void ma_copy_and_apply_volume_factor_pcm_frames_u8(ma_uint8* pFramesOut, const ma_uint8* pFramesIn, ma_uint64 frameCount, ma_uint32 channels, float factor) -{ - ma_copy_and_apply_volume_factor_u8(pFramesOut, pFramesIn, frameCount*channels, factor); -} - -MA_API void ma_copy_and_apply_volume_factor_pcm_frames_s16(ma_int16* pFramesOut, const ma_int16* pFramesIn, ma_uint64 frameCount, ma_uint32 channels, float factor) -{ - ma_copy_and_apply_volume_factor_s16(pFramesOut, pFramesIn, frameCount*channels, factor); -} - -MA_API void ma_copy_and_apply_volume_factor_pcm_frames_s24(void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount, ma_uint32 channels, float factor) -{ - ma_copy_and_apply_volume_factor_s24(pFramesOut, pFramesIn, frameCount*channels, factor); -} - -MA_API void ma_copy_and_apply_volume_factor_pcm_frames_s32(ma_int32* pFramesOut, const ma_int32* pFramesIn, ma_uint64 frameCount, ma_uint32 channels, float factor) -{ - ma_copy_and_apply_volume_factor_s32(pFramesOut, pFramesIn, frameCount*channels, factor); -} - -MA_API void ma_copy_and_apply_volume_factor_pcm_frames_f32(float* pFramesOut, const float* pFramesIn, ma_uint64 frameCount, ma_uint32 channels, float factor) -{ - ma_copy_and_apply_volume_factor_f32(pFramesOut, pFramesIn, frameCount*channels, factor); -} - -MA_API void ma_copy_and_apply_volume_factor_pcm_frames(void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount, ma_format format, ma_uint32 channels, float factor) -{ - switch (format) - { - case ma_format_u8: ma_copy_and_apply_volume_factor_pcm_frames_u8 ((ma_uint8*)pFramesOut, (const ma_uint8*)pFramesIn, frameCount, channels, factor); return; - case ma_format_s16: ma_copy_and_apply_volume_factor_pcm_frames_s16((ma_int16*)pFramesOut, (const ma_int16*)pFramesIn, frameCount, channels, factor); return; - case ma_format_s24: ma_copy_and_apply_volume_factor_pcm_frames_s24( pFramesOut, pFramesIn, frameCount, channels, factor); return; - case ma_format_s32: ma_copy_and_apply_volume_factor_pcm_frames_s32((ma_int32*)pFramesOut, (const ma_int32*)pFramesIn, frameCount, channels, factor); return; - case ma_format_f32: ma_copy_and_apply_volume_factor_pcm_frames_f32( (float*)pFramesOut, (const float*)pFramesIn, frameCount, channels, factor); return; - default: return; /* Do nothing. */ - } -} - -MA_API void ma_apply_volume_factor_pcm_frames_u8(ma_uint8* pFrames, ma_uint64 frameCount, ma_uint32 channels, float factor) -{ - ma_copy_and_apply_volume_factor_pcm_frames_u8(pFrames, pFrames, frameCount, channels, factor); -} - -MA_API void ma_apply_volume_factor_pcm_frames_s16(ma_int16* pFrames, ma_uint64 frameCount, ma_uint32 channels, float factor) -{ - ma_copy_and_apply_volume_factor_pcm_frames_s16(pFrames, pFrames, frameCount, channels, factor); -} - -MA_API void ma_apply_volume_factor_pcm_frames_s24(void* pFrames, ma_uint64 frameCount, ma_uint32 channels, float factor) -{ - ma_copy_and_apply_volume_factor_pcm_frames_s24(pFrames, pFrames, frameCount, channels, factor); -} - -MA_API void ma_apply_volume_factor_pcm_frames_s32(ma_int32* pFrames, ma_uint64 frameCount, ma_uint32 channels, float factor) -{ - ma_copy_and_apply_volume_factor_pcm_frames_s32(pFrames, pFrames, frameCount, channels, factor); -} - -MA_API void ma_apply_volume_factor_pcm_frames_f32(float* pFrames, ma_uint64 frameCount, ma_uint32 channels, float factor) -{ - ma_copy_and_apply_volume_factor_pcm_frames_f32(pFrames, pFrames, frameCount, channels, factor); -} - -MA_API void ma_apply_volume_factor_pcm_frames(void* pFramesOut, ma_uint64 frameCount, ma_format format, ma_uint32 channels, float factor) -{ - ma_copy_and_apply_volume_factor_pcm_frames(pFramesOut, pFramesOut, frameCount, format, channels, factor); -} - - -MA_API void ma_copy_and_apply_volume_factor_per_channel_f32(float* pFramesOut, const float* pFramesIn, ma_uint64 frameCount, ma_uint32 channels, float* pChannelGains) -{ - ma_uint64 iFrame; - - if (channels == 2) { - /* TODO: Do an optimized implementation for stereo and mono. Can do a SIMD optimized implementation as well. */ - } - - for (iFrame = 0; iFrame < frameCount; iFrame += 1) { - ma_uint32 iChannel; - for (iChannel = 0; iChannel < channels; iChannel += 1) { - pFramesOut[iFrame * channels + iChannel] = pFramesIn[iFrame * channels + iChannel] * pChannelGains[iChannel]; - } - } -} - - - -static MA_INLINE ma_int16 ma_apply_volume_unclipped_u8(ma_int16 x, ma_int16 volume) -{ - return (ma_int16)(((ma_int32)x * (ma_int32)volume) >> 8); -} - -static MA_INLINE ma_int32 ma_apply_volume_unclipped_s16(ma_int32 x, ma_int16 volume) -{ - return (ma_int32)((x * volume) >> 8); -} - -static MA_INLINE ma_int64 ma_apply_volume_unclipped_s24(ma_int64 x, ma_int16 volume) -{ - return (ma_int64)((x * volume) >> 8); -} - -static MA_INLINE ma_int64 ma_apply_volume_unclipped_s32(ma_int64 x, ma_int16 volume) -{ - return (ma_int64)((x * volume) >> 8); -} - -static MA_INLINE float ma_apply_volume_unclipped_f32(float x, float volume) -{ - return x * volume; -} - - -MA_API void ma_copy_and_apply_volume_and_clip_samples_u8(ma_uint8* pDst, const ma_int16* pSrc, ma_uint64 count, float volume) -{ - ma_uint64 iSample; - ma_int16 volumeFixed; - - MA_ASSERT(pDst != NULL); - MA_ASSERT(pSrc != NULL); - - volumeFixed = ma_float_to_fixed_16(volume); - - for (iSample = 0; iSample < count; iSample += 1) { - pDst[iSample] = ma_clip_u8(ma_apply_volume_unclipped_u8(pSrc[iSample], volumeFixed)); - } -} - -MA_API void ma_copy_and_apply_volume_and_clip_samples_s16(ma_int16* pDst, const ma_int32* pSrc, ma_uint64 count, float volume) -{ - ma_uint64 iSample; - ma_int16 volumeFixed; - - MA_ASSERT(pDst != NULL); - MA_ASSERT(pSrc != NULL); - - volumeFixed = ma_float_to_fixed_16(volume); - - for (iSample = 0; iSample < count; iSample += 1) { - pDst[iSample] = ma_clip_s16(ma_apply_volume_unclipped_s16(pSrc[iSample], volumeFixed)); - } -} - -MA_API void ma_copy_and_apply_volume_and_clip_samples_s24(ma_uint8* pDst, const ma_int64* pSrc, ma_uint64 count, float volume) -{ - ma_uint64 iSample; - ma_int16 volumeFixed; - - MA_ASSERT(pDst != NULL); - MA_ASSERT(pSrc != NULL); - - volumeFixed = ma_float_to_fixed_16(volume); - - for (iSample = 0; iSample < count; iSample += 1) { - ma_int64 s = ma_clip_s24(ma_apply_volume_unclipped_s24(pSrc[iSample], volumeFixed)); - pDst[iSample*3 + 0] = (ma_uint8)((s & 0x000000FF) >> 0); - pDst[iSample*3 + 1] = (ma_uint8)((s & 0x0000FF00) >> 8); - pDst[iSample*3 + 2] = (ma_uint8)((s & 0x00FF0000) >> 16); - } -} - -MA_API void ma_copy_and_apply_volume_and_clip_samples_s32(ma_int32* pDst, const ma_int64* pSrc, ma_uint64 count, float volume) -{ - ma_uint64 iSample; - ma_int16 volumeFixed; - - MA_ASSERT(pDst != NULL); - MA_ASSERT(pSrc != NULL); - - volumeFixed = ma_float_to_fixed_16(volume); - - for (iSample = 0; iSample < count; iSample += 1) { - pDst[iSample] = ma_clip_s32(ma_apply_volume_unclipped_s32(pSrc[iSample], volumeFixed)); - } -} - -MA_API void ma_copy_and_apply_volume_and_clip_samples_f32(float* pDst, const float* pSrc, ma_uint64 count, float volume) -{ - ma_uint64 iSample; - - MA_ASSERT(pDst != NULL); - MA_ASSERT(pSrc != NULL); - - /* For the f32 case we need to make sure this supports in-place processing where the input and output buffers are the same. */ - - for (iSample = 0; iSample < count; iSample += 1) { - pDst[iSample] = ma_clip_f32(ma_apply_volume_unclipped_f32(pSrc[iSample], volume)); - } -} - -MA_API void ma_copy_and_apply_volume_and_clip_pcm_frames(void* pDst, const void* pSrc, ma_uint64 frameCount, ma_format format, ma_uint32 channels, float volume) -{ - MA_ASSERT(pDst != NULL); - MA_ASSERT(pSrc != NULL); - - if (volume == 1) { - ma_clip_pcm_frames(pDst, pSrc, frameCount, format, channels); /* Optimized case for volume = 1. */ - } else if (volume == 0) { - ma_silence_pcm_frames(pDst, frameCount, format, channels); /* Optimized case for volume = 0. */ - } else { - ma_uint64 sampleCount = frameCount * channels; - - switch (format) { - case ma_format_u8: ma_copy_and_apply_volume_and_clip_samples_u8( (ma_uint8*)pDst, (const ma_int16*)pSrc, sampleCount, volume); break; - case ma_format_s16: ma_copy_and_apply_volume_and_clip_samples_s16((ma_int16*)pDst, (const ma_int32*)pSrc, sampleCount, volume); break; - case ma_format_s24: ma_copy_and_apply_volume_and_clip_samples_s24((ma_uint8*)pDst, (const ma_int64*)pSrc, sampleCount, volume); break; - case ma_format_s32: ma_copy_and_apply_volume_and_clip_samples_s32((ma_int32*)pDst, (const ma_int64*)pSrc, sampleCount, volume); break; - case ma_format_f32: ma_copy_and_apply_volume_and_clip_samples_f32(( float*)pDst, (const float*)pSrc, sampleCount, volume); break; - - /* Do nothing if we don't know the format. We're including these here to silence a compiler warning about enums not being handled by the switch. */ - case ma_format_unknown: - case ma_format_count: - break; - } - } -} - - - -MA_API float ma_volume_linear_to_db(float factor) -{ - return 20*ma_log10f(factor); -} - -MA_API float ma_volume_db_to_linear(float gain) -{ - return ma_powf(10, gain/20.0f); -} - - -MA_API ma_result ma_mix_pcm_frames_f32(float* pDst, const float* pSrc, ma_uint64 frameCount, ma_uint32 channels, float volume) -{ - ma_uint64 iSample; - ma_uint64 sampleCount; - - if (pDst == NULL || pSrc == NULL || channels == 0) { - return MA_INVALID_ARGS; - } - - if (volume == 0) { - return MA_SUCCESS; /* No changes if the volume is 0. */ - } - - sampleCount = frameCount * channels; - - if (volume == 1) { - for (iSample = 0; iSample < sampleCount; iSample += 1) { - pDst[iSample] += pSrc[iSample]; - } - } else { - for (iSample = 0; iSample < sampleCount; iSample += 1) { - pDst[iSample] += ma_apply_volume_unclipped_f32(pSrc[iSample], volume); - } - } - - return MA_SUCCESS; -} - - - -/************************************************************************************************************************************************************** - -Format Conversion - -**************************************************************************************************************************************************************/ - -static MA_INLINE ma_int16 ma_pcm_sample_f32_to_s16(float x) -{ - return (ma_int16)(x * 32767.0f); -} - -static MA_INLINE ma_int16 ma_pcm_sample_u8_to_s16_no_scale(ma_uint8 x) -{ - return (ma_int16)((ma_int16)x - 128); -} - -static MA_INLINE ma_int64 ma_pcm_sample_s24_to_s32_no_scale(const ma_uint8* x) -{ - return (ma_int64)(((ma_uint64)x[0] << 40) | ((ma_uint64)x[1] << 48) | ((ma_uint64)x[2] << 56)) >> 40; /* Make sure the sign bits are maintained. */ -} - -static MA_INLINE void ma_pcm_sample_s32_to_s24_no_scale(ma_int64 x, ma_uint8* s24) -{ - s24[0] = (ma_uint8)((x & 0x000000FF) >> 0); - s24[1] = (ma_uint8)((x & 0x0000FF00) >> 8); - s24[2] = (ma_uint8)((x & 0x00FF0000) >> 16); -} - - -/* u8 */ -MA_API void ma_pcm_u8_to_u8(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) -{ - (void)ditherMode; - ma_copy_memory_64(dst, src, count * sizeof(ma_uint8)); -} - - -static MA_INLINE void ma_pcm_u8_to_s16__reference(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) -{ - ma_int16* dst_s16 = (ma_int16*)dst; - const ma_uint8* src_u8 = (const ma_uint8*)src; - - ma_uint64 i; - for (i = 0; i < count; i += 1) { - ma_int16 x = src_u8[i]; - x = (ma_int16)(x - 128); - x = (ma_int16)(x << 8); - dst_s16[i] = x; - } - - (void)ditherMode; -} - -static MA_INLINE void ma_pcm_u8_to_s16__optimized(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) -{ - ma_pcm_u8_to_s16__reference(dst, src, count, ditherMode); -} - -#if defined(MA_SUPPORT_SSE2) -static MA_INLINE void ma_pcm_u8_to_s16__sse2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) -{ - ma_pcm_u8_to_s16__optimized(dst, src, count, ditherMode); -} -#endif -#if defined(MA_SUPPORT_NEON) -static MA_INLINE void ma_pcm_u8_to_s16__neon(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) -{ - ma_pcm_u8_to_s16__optimized(dst, src, count, ditherMode); -} -#endif - -MA_API void ma_pcm_u8_to_s16(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) -{ -#ifdef MA_USE_REFERENCE_CONVERSION_APIS - ma_pcm_u8_to_s16__reference(dst, src, count, ditherMode); -#else - # if defined(MA_SUPPORT_SSE2) - if (ma_has_sse2()) { - ma_pcm_u8_to_s16__sse2(dst, src, count, ditherMode); - } else - #elif defined(MA_SUPPORT_NEON) - if (ma_has_neon()) { - ma_pcm_u8_to_s16__neon(dst, src, count, ditherMode); - } else - #endif - { - ma_pcm_u8_to_s16__optimized(dst, src, count, ditherMode); - } -#endif -} - - -static MA_INLINE void ma_pcm_u8_to_s24__reference(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) -{ - ma_uint8* dst_s24 = (ma_uint8*)dst; - const ma_uint8* src_u8 = (const ma_uint8*)src; - - ma_uint64 i; - for (i = 0; i < count; i += 1) { - ma_int16 x = src_u8[i]; - x = (ma_int16)(x - 128); - - dst_s24[i*3+0] = 0; - dst_s24[i*3+1] = 0; - dst_s24[i*3+2] = (ma_uint8)((ma_int8)x); - } - - (void)ditherMode; -} - -static MA_INLINE void ma_pcm_u8_to_s24__optimized(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) -{ - ma_pcm_u8_to_s24__reference(dst, src, count, ditherMode); -} - -#if defined(MA_SUPPORT_SSE2) -static MA_INLINE void ma_pcm_u8_to_s24__sse2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) -{ - ma_pcm_u8_to_s24__optimized(dst, src, count, ditherMode); -} -#endif -#if defined(MA_SUPPORT_NEON) -static MA_INLINE void ma_pcm_u8_to_s24__neon(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) -{ - ma_pcm_u8_to_s24__optimized(dst, src, count, ditherMode); -} -#endif - -MA_API void ma_pcm_u8_to_s24(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) -{ -#ifdef MA_USE_REFERENCE_CONVERSION_APIS - ma_pcm_u8_to_s24__reference(dst, src, count, ditherMode); -#else - # if defined(MA_SUPPORT_SSE2) - if (ma_has_sse2()) { - ma_pcm_u8_to_s24__sse2(dst, src, count, ditherMode); - } else - #elif defined(MA_SUPPORT_NEON) - if (ma_has_neon()) { - ma_pcm_u8_to_s24__neon(dst, src, count, ditherMode); - } else - #endif - { - ma_pcm_u8_to_s24__optimized(dst, src, count, ditherMode); - } -#endif -} - - -static MA_INLINE void ma_pcm_u8_to_s32__reference(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) -{ - ma_int32* dst_s32 = (ma_int32*)dst; - const ma_uint8* src_u8 = (const ma_uint8*)src; - - ma_uint64 i; - for (i = 0; i < count; i += 1) { - ma_int32 x = src_u8[i]; - x = x - 128; - x = x << 24; - dst_s32[i] = x; - } - - (void)ditherMode; -} - -static MA_INLINE void ma_pcm_u8_to_s32__optimized(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) -{ - ma_pcm_u8_to_s32__reference(dst, src, count, ditherMode); -} - -#if defined(MA_SUPPORT_SSE2) -static MA_INLINE void ma_pcm_u8_to_s32__sse2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) -{ - ma_pcm_u8_to_s32__optimized(dst, src, count, ditherMode); -} -#endif -#if defined(MA_SUPPORT_NEON) -static MA_INLINE void ma_pcm_u8_to_s32__neon(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) -{ - ma_pcm_u8_to_s32__optimized(dst, src, count, ditherMode); -} -#endif - -MA_API void ma_pcm_u8_to_s32(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) -{ -#ifdef MA_USE_REFERENCE_CONVERSION_APIS - ma_pcm_u8_to_s32__reference(dst, src, count, ditherMode); -#else - # if defined(MA_SUPPORT_SSE2) - if (ma_has_sse2()) { - ma_pcm_u8_to_s32__sse2(dst, src, count, ditherMode); - } else - #elif defined(MA_SUPPORT_NEON) - if (ma_has_neon()) { - ma_pcm_u8_to_s32__neon(dst, src, count, ditherMode); - } else - #endif - { - ma_pcm_u8_to_s32__optimized(dst, src, count, ditherMode); - } -#endif -} - - -static MA_INLINE void ma_pcm_u8_to_f32__reference(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) -{ - float* dst_f32 = (float*)dst; - const ma_uint8* src_u8 = (const ma_uint8*)src; - - ma_uint64 i; - for (i = 0; i < count; i += 1) { - float x = (float)src_u8[i]; - x = x * 0.00784313725490196078f; /* 0..255 to 0..2 */ - x = x - 1; /* 0..2 to -1..1 */ - - dst_f32[i] = x; - } - - (void)ditherMode; -} - -static MA_INLINE void ma_pcm_u8_to_f32__optimized(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) -{ - ma_pcm_u8_to_f32__reference(dst, src, count, ditherMode); -} - -#if defined(MA_SUPPORT_SSE2) -static MA_INLINE void ma_pcm_u8_to_f32__sse2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) -{ - ma_pcm_u8_to_f32__optimized(dst, src, count, ditherMode); -} -#endif -#if defined(MA_SUPPORT_NEON) -static MA_INLINE void ma_pcm_u8_to_f32__neon(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) -{ - ma_pcm_u8_to_f32__optimized(dst, src, count, ditherMode); -} -#endif - -MA_API void ma_pcm_u8_to_f32(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) -{ -#ifdef MA_USE_REFERENCE_CONVERSION_APIS - ma_pcm_u8_to_f32__reference(dst, src, count, ditherMode); -#else - # if defined(MA_SUPPORT_SSE2) - if (ma_has_sse2()) { - ma_pcm_u8_to_f32__sse2(dst, src, count, ditherMode); - } else - #elif defined(MA_SUPPORT_NEON) - if (ma_has_neon()) { - ma_pcm_u8_to_f32__neon(dst, src, count, ditherMode); - } else - #endif - { - ma_pcm_u8_to_f32__optimized(dst, src, count, ditherMode); - } -#endif -} - - -#ifdef MA_USE_REFERENCE_CONVERSION_APIS -static MA_INLINE void ma_pcm_interleave_u8__reference(void* dst, const void** src, ma_uint64 frameCount, ma_uint32 channels) -{ - ma_uint8* dst_u8 = (ma_uint8*)dst; - const ma_uint8** src_u8 = (const ma_uint8**)src; - - ma_uint64 iFrame; - for (iFrame = 0; iFrame < frameCount; iFrame += 1) { - ma_uint32 iChannel; - for (iChannel = 0; iChannel < channels; iChannel += 1) { - dst_u8[iFrame*channels + iChannel] = src_u8[iChannel][iFrame]; - } - } -} -#else -static MA_INLINE void ma_pcm_interleave_u8__optimized(void* dst, const void** src, ma_uint64 frameCount, ma_uint32 channels) -{ - ma_uint8* dst_u8 = (ma_uint8*)dst; - const ma_uint8** src_u8 = (const ma_uint8**)src; - - if (channels == 1) { - ma_copy_memory_64(dst, src[0], frameCount * sizeof(ma_uint8)); - } else if (channels == 2) { - ma_uint64 iFrame; - for (iFrame = 0; iFrame < frameCount; iFrame += 1) { - dst_u8[iFrame*2 + 0] = src_u8[0][iFrame]; - dst_u8[iFrame*2 + 1] = src_u8[1][iFrame]; - } - } else { - ma_uint64 iFrame; - for (iFrame = 0; iFrame < frameCount; iFrame += 1) { - ma_uint32 iChannel; - for (iChannel = 0; iChannel < channels; iChannel += 1) { - dst_u8[iFrame*channels + iChannel] = src_u8[iChannel][iFrame]; - } - } - } -} -#endif - -MA_API void ma_pcm_interleave_u8(void* dst, const void** src, ma_uint64 frameCount, ma_uint32 channels) -{ -#ifdef MA_USE_REFERENCE_CONVERSION_APIS - ma_pcm_interleave_u8__reference(dst, src, frameCount, channels); -#else - ma_pcm_interleave_u8__optimized(dst, src, frameCount, channels); -#endif -} - - -static MA_INLINE void ma_pcm_deinterleave_u8__reference(void** dst, const void* src, ma_uint64 frameCount, ma_uint32 channels) -{ - ma_uint8** dst_u8 = (ma_uint8**)dst; - const ma_uint8* src_u8 = (const ma_uint8*)src; - - ma_uint64 iFrame; - for (iFrame = 0; iFrame < frameCount; iFrame += 1) { - ma_uint32 iChannel; - for (iChannel = 0; iChannel < channels; iChannel += 1) { - dst_u8[iChannel][iFrame] = src_u8[iFrame*channels + iChannel]; - } - } -} - -static MA_INLINE void ma_pcm_deinterleave_u8__optimized(void** dst, const void* src, ma_uint64 frameCount, ma_uint32 channels) -{ - ma_pcm_deinterleave_u8__reference(dst, src, frameCount, channels); -} - -MA_API void ma_pcm_deinterleave_u8(void** dst, const void* src, ma_uint64 frameCount, ma_uint32 channels) -{ -#ifdef MA_USE_REFERENCE_CONVERSION_APIS - ma_pcm_deinterleave_u8__reference(dst, src, frameCount, channels); -#else - ma_pcm_deinterleave_u8__optimized(dst, src, frameCount, channels); -#endif -} - - -/* s16 */ -static MA_INLINE void ma_pcm_s16_to_u8__reference(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) -{ - ma_uint8* dst_u8 = (ma_uint8*)dst; - const ma_int16* src_s16 = (const ma_int16*)src; - - if (ditherMode == ma_dither_mode_none) { - ma_uint64 i; - for (i = 0; i < count; i += 1) { - ma_int16 x = src_s16[i]; - x = (ma_int16)(x >> 8); - x = (ma_int16)(x + 128); - dst_u8[i] = (ma_uint8)x; - } - } else { - ma_uint64 i; - for (i = 0; i < count; i += 1) { - ma_int16 x = src_s16[i]; - - /* Dither. Don't overflow. */ - ma_int32 dither = ma_dither_s32(ditherMode, -0x80, 0x7F); - if ((x + dither) <= 0x7FFF) { - x = (ma_int16)(x + dither); - } else { - x = 0x7FFF; - } - - x = (ma_int16)(x >> 8); - x = (ma_int16)(x + 128); - dst_u8[i] = (ma_uint8)x; - } - } -} - -static MA_INLINE void ma_pcm_s16_to_u8__optimized(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) -{ - ma_pcm_s16_to_u8__reference(dst, src, count, ditherMode); -} - -#if defined(MA_SUPPORT_SSE2) -static MA_INLINE void ma_pcm_s16_to_u8__sse2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) -{ - ma_pcm_s16_to_u8__optimized(dst, src, count, ditherMode); -} -#endif -#if defined(MA_SUPPORT_NEON) -static MA_INLINE void ma_pcm_s16_to_u8__neon(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) -{ - ma_pcm_s16_to_u8__optimized(dst, src, count, ditherMode); -} -#endif - -MA_API void ma_pcm_s16_to_u8(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) -{ -#ifdef MA_USE_REFERENCE_CONVERSION_APIS - ma_pcm_s16_to_u8__reference(dst, src, count, ditherMode); -#else - # if defined(MA_SUPPORT_SSE2) - if (ma_has_sse2()) { - ma_pcm_s16_to_u8__sse2(dst, src, count, ditherMode); - } else - #elif defined(MA_SUPPORT_NEON) - if (ma_has_neon()) { - ma_pcm_s16_to_u8__neon(dst, src, count, ditherMode); - } else - #endif - { - ma_pcm_s16_to_u8__optimized(dst, src, count, ditherMode); - } -#endif -} - - -MA_API void ma_pcm_s16_to_s16(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) -{ - (void)ditherMode; - ma_copy_memory_64(dst, src, count * sizeof(ma_int16)); -} - - -static MA_INLINE void ma_pcm_s16_to_s24__reference(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) -{ - ma_uint8* dst_s24 = (ma_uint8*)dst; - const ma_int16* src_s16 = (const ma_int16*)src; - - ma_uint64 i; - for (i = 0; i < count; i += 1) { - dst_s24[i*3+0] = 0; - dst_s24[i*3+1] = (ma_uint8)(src_s16[i] & 0xFF); - dst_s24[i*3+2] = (ma_uint8)(src_s16[i] >> 8); - } - - (void)ditherMode; -} - -static MA_INLINE void ma_pcm_s16_to_s24__optimized(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) -{ - ma_pcm_s16_to_s24__reference(dst, src, count, ditherMode); -} - -#if defined(MA_SUPPORT_SSE2) -static MA_INLINE void ma_pcm_s16_to_s24__sse2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) -{ - ma_pcm_s16_to_s24__optimized(dst, src, count, ditherMode); -} -#endif -#if defined(MA_SUPPORT_NEON) -static MA_INLINE void ma_pcm_s16_to_s24__neon(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) -{ - ma_pcm_s16_to_s24__optimized(dst, src, count, ditherMode); -} -#endif - -MA_API void ma_pcm_s16_to_s24(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) -{ -#ifdef MA_USE_REFERENCE_CONVERSION_APIS - ma_pcm_s16_to_s24__reference(dst, src, count, ditherMode); -#else - # if defined(MA_SUPPORT_SSE2) - if (ma_has_sse2()) { - ma_pcm_s16_to_s24__sse2(dst, src, count, ditherMode); - } else - #elif defined(MA_SUPPORT_NEON) - if (ma_has_neon()) { - ma_pcm_s16_to_s24__neon(dst, src, count, ditherMode); - } else - #endif - { - ma_pcm_s16_to_s24__optimized(dst, src, count, ditherMode); - } -#endif -} - - -static MA_INLINE void ma_pcm_s16_to_s32__reference(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) -{ - ma_int32* dst_s32 = (ma_int32*)dst; - const ma_int16* src_s16 = (const ma_int16*)src; - - ma_uint64 i; - for (i = 0; i < count; i += 1) { - dst_s32[i] = src_s16[i] << 16; - } - - (void)ditherMode; -} - -static MA_INLINE void ma_pcm_s16_to_s32__optimized(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) -{ - ma_pcm_s16_to_s32__reference(dst, src, count, ditherMode); -} - -#if defined(MA_SUPPORT_SSE2) -static MA_INLINE void ma_pcm_s16_to_s32__sse2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) -{ - ma_pcm_s16_to_s32__optimized(dst, src, count, ditherMode); -} -#endif -#if defined(MA_SUPPORT_NEON) -static MA_INLINE void ma_pcm_s16_to_s32__neon(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) -{ - ma_pcm_s16_to_s32__optimized(dst, src, count, ditherMode); -} -#endif - -MA_API void ma_pcm_s16_to_s32(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) -{ -#ifdef MA_USE_REFERENCE_CONVERSION_APIS - ma_pcm_s16_to_s32__reference(dst, src, count, ditherMode); -#else - # if defined(MA_SUPPORT_SSE2) - if (ma_has_sse2()) { - ma_pcm_s16_to_s32__sse2(dst, src, count, ditherMode); - } else - #elif defined(MA_SUPPORT_NEON) - if (ma_has_neon()) { - ma_pcm_s16_to_s32__neon(dst, src, count, ditherMode); - } else - #endif - { - ma_pcm_s16_to_s32__optimized(dst, src, count, ditherMode); - } -#endif -} - - -static MA_INLINE void ma_pcm_s16_to_f32__reference(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) -{ - float* dst_f32 = (float*)dst; - const ma_int16* src_s16 = (const ma_int16*)src; - - ma_uint64 i; - for (i = 0; i < count; i += 1) { - float x = (float)src_s16[i]; - -#if 0 - /* The accurate way. */ - x = x + 32768.0f; /* -32768..32767 to 0..65535 */ - x = x * 0.00003051804379339284f; /* 0..65535 to 0..2 */ - x = x - 1; /* 0..2 to -1..1 */ -#else - /* The fast way. */ - x = x * 0.000030517578125f; /* -32768..32767 to -1..0.999969482421875 */ -#endif - - dst_f32[i] = x; - } - - (void)ditherMode; -} - -static MA_INLINE void ma_pcm_s16_to_f32__optimized(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) -{ - ma_pcm_s16_to_f32__reference(dst, src, count, ditherMode); -} - -#if defined(MA_SUPPORT_SSE2) -static MA_INLINE void ma_pcm_s16_to_f32__sse2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) -{ - ma_pcm_s16_to_f32__optimized(dst, src, count, ditherMode); -} -#endif -#if defined(MA_SUPPORT_NEON) -static MA_INLINE void ma_pcm_s16_to_f32__neon(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) -{ - ma_pcm_s16_to_f32__optimized(dst, src, count, ditherMode); -} -#endif - -MA_API void ma_pcm_s16_to_f32(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) -{ -#ifdef MA_USE_REFERENCE_CONVERSION_APIS - ma_pcm_s16_to_f32__reference(dst, src, count, ditherMode); -#else - # if defined(MA_SUPPORT_SSE2) - if (ma_has_sse2()) { - ma_pcm_s16_to_f32__sse2(dst, src, count, ditherMode); - } else - #elif defined(MA_SUPPORT_NEON) - if (ma_has_neon()) { - ma_pcm_s16_to_f32__neon(dst, src, count, ditherMode); - } else - #endif - { - ma_pcm_s16_to_f32__optimized(dst, src, count, ditherMode); - } -#endif -} - - -static MA_INLINE void ma_pcm_interleave_s16__reference(void* dst, const void** src, ma_uint64 frameCount, ma_uint32 channels) -{ - ma_int16* dst_s16 = (ma_int16*)dst; - const ma_int16** src_s16 = (const ma_int16**)src; - - ma_uint64 iFrame; - for (iFrame = 0; iFrame < frameCount; iFrame += 1) { - ma_uint32 iChannel; - for (iChannel = 0; iChannel < channels; iChannel += 1) { - dst_s16[iFrame*channels + iChannel] = src_s16[iChannel][iFrame]; - } - } -} - -static MA_INLINE void ma_pcm_interleave_s16__optimized(void* dst, const void** src, ma_uint64 frameCount, ma_uint32 channels) -{ - ma_pcm_interleave_s16__reference(dst, src, frameCount, channels); -} - -MA_API void ma_pcm_interleave_s16(void* dst, const void** src, ma_uint64 frameCount, ma_uint32 channels) -{ -#ifdef MA_USE_REFERENCE_CONVERSION_APIS - ma_pcm_interleave_s16__reference(dst, src, frameCount, channels); -#else - ma_pcm_interleave_s16__optimized(dst, src, frameCount, channels); -#endif -} - - -static MA_INLINE void ma_pcm_deinterleave_s16__reference(void** dst, const void* src, ma_uint64 frameCount, ma_uint32 channels) -{ - ma_int16** dst_s16 = (ma_int16**)dst; - const ma_int16* src_s16 = (const ma_int16*)src; - - ma_uint64 iFrame; - for (iFrame = 0; iFrame < frameCount; iFrame += 1) { - ma_uint32 iChannel; - for (iChannel = 0; iChannel < channels; iChannel += 1) { - dst_s16[iChannel][iFrame] = src_s16[iFrame*channels + iChannel]; - } - } -} - -static MA_INLINE void ma_pcm_deinterleave_s16__optimized(void** dst, const void* src, ma_uint64 frameCount, ma_uint32 channels) -{ - ma_pcm_deinterleave_s16__reference(dst, src, frameCount, channels); -} - -MA_API void ma_pcm_deinterleave_s16(void** dst, const void* src, ma_uint64 frameCount, ma_uint32 channels) -{ -#ifdef MA_USE_REFERENCE_CONVERSION_APIS - ma_pcm_deinterleave_s16__reference(dst, src, frameCount, channels); -#else - ma_pcm_deinterleave_s16__optimized(dst, src, frameCount, channels); -#endif -} - - -/* s24 */ -static MA_INLINE void ma_pcm_s24_to_u8__reference(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) -{ - ma_uint8* dst_u8 = (ma_uint8*)dst; - const ma_uint8* src_s24 = (const ma_uint8*)src; - - if (ditherMode == ma_dither_mode_none) { - ma_uint64 i; - for (i = 0; i < count; i += 1) { - dst_u8[i] = (ma_uint8)((ma_int8)src_s24[i*3 + 2] + 128); - } - } else { - ma_uint64 i; - for (i = 0; i < count; i += 1) { - ma_int32 x = (ma_int32)(((ma_uint32)(src_s24[i*3+0]) << 8) | ((ma_uint32)(src_s24[i*3+1]) << 16) | ((ma_uint32)(src_s24[i*3+2])) << 24); - - /* Dither. Don't overflow. */ - ma_int32 dither = ma_dither_s32(ditherMode, -0x800000, 0x7FFFFF); - if ((ma_int64)x + dither <= 0x7FFFFFFF) { - x = x + dither; - } else { - x = 0x7FFFFFFF; - } - - x = x >> 24; - x = x + 128; - dst_u8[i] = (ma_uint8)x; - } - } -} - -static MA_INLINE void ma_pcm_s24_to_u8__optimized(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) -{ - ma_pcm_s24_to_u8__reference(dst, src, count, ditherMode); -} - -#if defined(MA_SUPPORT_SSE2) -static MA_INLINE void ma_pcm_s24_to_u8__sse2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) -{ - ma_pcm_s24_to_u8__optimized(dst, src, count, ditherMode); -} -#endif -#if defined(MA_SUPPORT_NEON) -static MA_INLINE void ma_pcm_s24_to_u8__neon(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) -{ - ma_pcm_s24_to_u8__optimized(dst, src, count, ditherMode); -} -#endif - -MA_API void ma_pcm_s24_to_u8(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) -{ -#ifdef MA_USE_REFERENCE_CONVERSION_APIS - ma_pcm_s24_to_u8__reference(dst, src, count, ditherMode); -#else - # if defined(MA_SUPPORT_SSE2) - if (ma_has_sse2()) { - ma_pcm_s24_to_u8__sse2(dst, src, count, ditherMode); - } else - #elif defined(MA_SUPPORT_NEON) - if (ma_has_neon()) { - ma_pcm_s24_to_u8__neon(dst, src, count, ditherMode); - } else - #endif - { - ma_pcm_s24_to_u8__optimized(dst, src, count, ditherMode); - } -#endif -} - - -static MA_INLINE void ma_pcm_s24_to_s16__reference(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) -{ - ma_int16* dst_s16 = (ma_int16*)dst; - const ma_uint8* src_s24 = (const ma_uint8*)src; - - if (ditherMode == ma_dither_mode_none) { - ma_uint64 i; - for (i = 0; i < count; i += 1) { - ma_uint16 dst_lo = ((ma_uint16)src_s24[i*3 + 1]); - ma_uint16 dst_hi = (ma_uint16)((ma_uint16)src_s24[i*3 + 2] << 8); - dst_s16[i] = (ma_int16)(dst_lo | dst_hi); - } - } else { - ma_uint64 i; - for (i = 0; i < count; i += 1) { - ma_int32 x = (ma_int32)(((ma_uint32)(src_s24[i*3+0]) << 8) | ((ma_uint32)(src_s24[i*3+1]) << 16) | ((ma_uint32)(src_s24[i*3+2])) << 24); - - /* Dither. Don't overflow. */ - ma_int32 dither = ma_dither_s32(ditherMode, -0x8000, 0x7FFF); - if ((ma_int64)x + dither <= 0x7FFFFFFF) { - x = x + dither; - } else { - x = 0x7FFFFFFF; - } - - x = x >> 16; - dst_s16[i] = (ma_int16)x; - } - } -} - -static MA_INLINE void ma_pcm_s24_to_s16__optimized(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) -{ - ma_pcm_s24_to_s16__reference(dst, src, count, ditherMode); -} - -#if defined(MA_SUPPORT_SSE2) -static MA_INLINE void ma_pcm_s24_to_s16__sse2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) -{ - ma_pcm_s24_to_s16__optimized(dst, src, count, ditherMode); -} -#endif -#if defined(MA_SUPPORT_NEON) -static MA_INLINE void ma_pcm_s24_to_s16__neon(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) -{ - ma_pcm_s24_to_s16__optimized(dst, src, count, ditherMode); -} -#endif - -MA_API void ma_pcm_s24_to_s16(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) -{ -#ifdef MA_USE_REFERENCE_CONVERSION_APIS - ma_pcm_s24_to_s16__reference(dst, src, count, ditherMode); -#else - # if defined(MA_SUPPORT_SSE2) - if (ma_has_sse2()) { - ma_pcm_s24_to_s16__sse2(dst, src, count, ditherMode); - } else - #elif defined(MA_SUPPORT_NEON) - if (ma_has_neon()) { - ma_pcm_s24_to_s16__neon(dst, src, count, ditherMode); - } else - #endif - { - ma_pcm_s24_to_s16__optimized(dst, src, count, ditherMode); - } -#endif -} - - -MA_API void ma_pcm_s24_to_s24(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) -{ - (void)ditherMode; - - ma_copy_memory_64(dst, src, count * 3); -} - - -static MA_INLINE void ma_pcm_s24_to_s32__reference(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) -{ - ma_int32* dst_s32 = (ma_int32*)dst; - const ma_uint8* src_s24 = (const ma_uint8*)src; - - ma_uint64 i; - for (i = 0; i < count; i += 1) { - dst_s32[i] = (ma_int32)(((ma_uint32)(src_s24[i*3+0]) << 8) | ((ma_uint32)(src_s24[i*3+1]) << 16) | ((ma_uint32)(src_s24[i*3+2])) << 24); - } - - (void)ditherMode; -} - -static MA_INLINE void ma_pcm_s24_to_s32__optimized(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) -{ - ma_pcm_s24_to_s32__reference(dst, src, count, ditherMode); -} - -#if defined(MA_SUPPORT_SSE2) -static MA_INLINE void ma_pcm_s24_to_s32__sse2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) -{ - ma_pcm_s24_to_s32__optimized(dst, src, count, ditherMode); -} -#endif -#if defined(MA_SUPPORT_NEON) -static MA_INLINE void ma_pcm_s24_to_s32__neon(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) -{ - ma_pcm_s24_to_s32__optimized(dst, src, count, ditherMode); -} -#endif - -MA_API void ma_pcm_s24_to_s32(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) -{ -#ifdef MA_USE_REFERENCE_CONVERSION_APIS - ma_pcm_s24_to_s32__reference(dst, src, count, ditherMode); -#else - # if defined(MA_SUPPORT_SSE2) - if (ma_has_sse2()) { - ma_pcm_s24_to_s32__sse2(dst, src, count, ditherMode); - } else - #elif defined(MA_SUPPORT_NEON) - if (ma_has_neon()) { - ma_pcm_s24_to_s32__neon(dst, src, count, ditherMode); - } else - #endif - { - ma_pcm_s24_to_s32__optimized(dst, src, count, ditherMode); - } -#endif -} - - -static MA_INLINE void ma_pcm_s24_to_f32__reference(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) -{ - float* dst_f32 = (float*)dst; - const ma_uint8* src_s24 = (const ma_uint8*)src; - - ma_uint64 i; - for (i = 0; i < count; i += 1) { - float x = (float)(((ma_int32)(((ma_uint32)(src_s24[i*3+0]) << 8) | ((ma_uint32)(src_s24[i*3+1]) << 16) | ((ma_uint32)(src_s24[i*3+2])) << 24)) >> 8); - -#if 0 - /* The accurate way. */ - x = x + 8388608.0f; /* -8388608..8388607 to 0..16777215 */ - x = x * 0.00000011920929665621f; /* 0..16777215 to 0..2 */ - x = x - 1; /* 0..2 to -1..1 */ -#else - /* The fast way. */ - x = x * 0.00000011920928955078125f; /* -8388608..8388607 to -1..0.999969482421875 */ -#endif - - dst_f32[i] = x; - } - - (void)ditherMode; -} - -static MA_INLINE void ma_pcm_s24_to_f32__optimized(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) -{ - ma_pcm_s24_to_f32__reference(dst, src, count, ditherMode); -} - -#if defined(MA_SUPPORT_SSE2) -static MA_INLINE void ma_pcm_s24_to_f32__sse2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) -{ - ma_pcm_s24_to_f32__optimized(dst, src, count, ditherMode); -} -#endif -#if defined(MA_SUPPORT_NEON) -static MA_INLINE void ma_pcm_s24_to_f32__neon(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) -{ - ma_pcm_s24_to_f32__optimized(dst, src, count, ditherMode); -} -#endif - -MA_API void ma_pcm_s24_to_f32(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) -{ -#ifdef MA_USE_REFERENCE_CONVERSION_APIS - ma_pcm_s24_to_f32__reference(dst, src, count, ditherMode); -#else - # if defined(MA_SUPPORT_SSE2) - if (ma_has_sse2()) { - ma_pcm_s24_to_f32__sse2(dst, src, count, ditherMode); - } else - #elif defined(MA_SUPPORT_NEON) - if (ma_has_neon()) { - ma_pcm_s24_to_f32__neon(dst, src, count, ditherMode); - } else - #endif - { - ma_pcm_s24_to_f32__optimized(dst, src, count, ditherMode); - } -#endif -} - - -static MA_INLINE void ma_pcm_interleave_s24__reference(void* dst, const void** src, ma_uint64 frameCount, ma_uint32 channels) -{ - ma_uint8* dst8 = (ma_uint8*)dst; - const ma_uint8** src8 = (const ma_uint8**)src; - - ma_uint64 iFrame; - for (iFrame = 0; iFrame < frameCount; iFrame += 1) { - ma_uint32 iChannel; - for (iChannel = 0; iChannel < channels; iChannel += 1) { - dst8[iFrame*3*channels + iChannel*3 + 0] = src8[iChannel][iFrame*3 + 0]; - dst8[iFrame*3*channels + iChannel*3 + 1] = src8[iChannel][iFrame*3 + 1]; - dst8[iFrame*3*channels + iChannel*3 + 2] = src8[iChannel][iFrame*3 + 2]; - } - } -} - -static MA_INLINE void ma_pcm_interleave_s24__optimized(void* dst, const void** src, ma_uint64 frameCount, ma_uint32 channels) -{ - ma_pcm_interleave_s24__reference(dst, src, frameCount, channels); -} - -MA_API void ma_pcm_interleave_s24(void* dst, const void** src, ma_uint64 frameCount, ma_uint32 channels) -{ -#ifdef MA_USE_REFERENCE_CONVERSION_APIS - ma_pcm_interleave_s24__reference(dst, src, frameCount, channels); -#else - ma_pcm_interleave_s24__optimized(dst, src, frameCount, channels); -#endif -} - - -static MA_INLINE void ma_pcm_deinterleave_s24__reference(void** dst, const void* src, ma_uint64 frameCount, ma_uint32 channels) -{ - ma_uint8** dst8 = (ma_uint8**)dst; - const ma_uint8* src8 = (const ma_uint8*)src; - - ma_uint32 iFrame; - for (iFrame = 0; iFrame < frameCount; iFrame += 1) { - ma_uint32 iChannel; - for (iChannel = 0; iChannel < channels; iChannel += 1) { - dst8[iChannel][iFrame*3 + 0] = src8[iFrame*3*channels + iChannel*3 + 0]; - dst8[iChannel][iFrame*3 + 1] = src8[iFrame*3*channels + iChannel*3 + 1]; - dst8[iChannel][iFrame*3 + 2] = src8[iFrame*3*channels + iChannel*3 + 2]; - } - } -} - -static MA_INLINE void ma_pcm_deinterleave_s24__optimized(void** dst, const void* src, ma_uint64 frameCount, ma_uint32 channels) -{ - ma_pcm_deinterleave_s24__reference(dst, src, frameCount, channels); -} - -MA_API void ma_pcm_deinterleave_s24(void** dst, const void* src, ma_uint64 frameCount, ma_uint32 channels) -{ -#ifdef MA_USE_REFERENCE_CONVERSION_APIS - ma_pcm_deinterleave_s24__reference(dst, src, frameCount, channels); -#else - ma_pcm_deinterleave_s24__optimized(dst, src, frameCount, channels); -#endif -} - - - -/* s32 */ -static MA_INLINE void ma_pcm_s32_to_u8__reference(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) -{ - ma_uint8* dst_u8 = (ma_uint8*)dst; - const ma_int32* src_s32 = (const ma_int32*)src; - - if (ditherMode == ma_dither_mode_none) { - ma_uint64 i; - for (i = 0; i < count; i += 1) { - ma_int32 x = src_s32[i]; - x = x >> 24; - x = x + 128; - dst_u8[i] = (ma_uint8)x; - } - } else { - ma_uint64 i; - for (i = 0; i < count; i += 1) { - ma_int32 x = src_s32[i]; - - /* Dither. Don't overflow. */ - ma_int32 dither = ma_dither_s32(ditherMode, -0x800000, 0x7FFFFF); - if ((ma_int64)x + dither <= 0x7FFFFFFF) { - x = x + dither; - } else { - x = 0x7FFFFFFF; - } - - x = x >> 24; - x = x + 128; - dst_u8[i] = (ma_uint8)x; - } - } -} - -static MA_INLINE void ma_pcm_s32_to_u8__optimized(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) -{ - ma_pcm_s32_to_u8__reference(dst, src, count, ditherMode); -} - -#if defined(MA_SUPPORT_SSE2) -static MA_INLINE void ma_pcm_s32_to_u8__sse2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) -{ - ma_pcm_s32_to_u8__optimized(dst, src, count, ditherMode); -} -#endif -#if defined(MA_SUPPORT_NEON) -static MA_INLINE void ma_pcm_s32_to_u8__neon(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) -{ - ma_pcm_s32_to_u8__optimized(dst, src, count, ditherMode); -} -#endif - -MA_API void ma_pcm_s32_to_u8(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) -{ -#ifdef MA_USE_REFERENCE_CONVERSION_APIS - ma_pcm_s32_to_u8__reference(dst, src, count, ditherMode); -#else - # if defined(MA_SUPPORT_SSE2) - if (ma_has_sse2()) { - ma_pcm_s32_to_u8__sse2(dst, src, count, ditherMode); - } else - #elif defined(MA_SUPPORT_NEON) - if (ma_has_neon()) { - ma_pcm_s32_to_u8__neon(dst, src, count, ditherMode); - } else - #endif - { - ma_pcm_s32_to_u8__optimized(dst, src, count, ditherMode); - } -#endif -} - - -static MA_INLINE void ma_pcm_s32_to_s16__reference(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) -{ - ma_int16* dst_s16 = (ma_int16*)dst; - const ma_int32* src_s32 = (const ma_int32*)src; - - if (ditherMode == ma_dither_mode_none) { - ma_uint64 i; - for (i = 0; i < count; i += 1) { - ma_int32 x = src_s32[i]; - x = x >> 16; - dst_s16[i] = (ma_int16)x; - } - } else { - ma_uint64 i; - for (i = 0; i < count; i += 1) { - ma_int32 x = src_s32[i]; - - /* Dither. Don't overflow. */ - ma_int32 dither = ma_dither_s32(ditherMode, -0x8000, 0x7FFF); - if ((ma_int64)x + dither <= 0x7FFFFFFF) { - x = x + dither; - } else { - x = 0x7FFFFFFF; - } - - x = x >> 16; - dst_s16[i] = (ma_int16)x; - } - } -} - -static MA_INLINE void ma_pcm_s32_to_s16__optimized(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) -{ - ma_pcm_s32_to_s16__reference(dst, src, count, ditherMode); -} - -#if defined(MA_SUPPORT_SSE2) -static MA_INLINE void ma_pcm_s32_to_s16__sse2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) -{ - ma_pcm_s32_to_s16__optimized(dst, src, count, ditherMode); -} -#endif -#if defined(MA_SUPPORT_NEON) -static MA_INLINE void ma_pcm_s32_to_s16__neon(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) -{ - ma_pcm_s32_to_s16__optimized(dst, src, count, ditherMode); -} -#endif - -MA_API void ma_pcm_s32_to_s16(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) -{ -#ifdef MA_USE_REFERENCE_CONVERSION_APIS - ma_pcm_s32_to_s16__reference(dst, src, count, ditherMode); -#else - # if defined(MA_SUPPORT_SSE2) - if (ma_has_sse2()) { - ma_pcm_s32_to_s16__sse2(dst, src, count, ditherMode); - } else - #elif defined(MA_SUPPORT_NEON) - if (ma_has_neon()) { - ma_pcm_s32_to_s16__neon(dst, src, count, ditherMode); - } else - #endif - { - ma_pcm_s32_to_s16__optimized(dst, src, count, ditherMode); - } -#endif -} - - -static MA_INLINE void ma_pcm_s32_to_s24__reference(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) -{ - ma_uint8* dst_s24 = (ma_uint8*)dst; - const ma_int32* src_s32 = (const ma_int32*)src; - - ma_uint64 i; - for (i = 0; i < count; i += 1) { - ma_uint32 x = (ma_uint32)src_s32[i]; - dst_s24[i*3+0] = (ma_uint8)((x & 0x0000FF00) >> 8); - dst_s24[i*3+1] = (ma_uint8)((x & 0x00FF0000) >> 16); - dst_s24[i*3+2] = (ma_uint8)((x & 0xFF000000) >> 24); - } - - (void)ditherMode; /* No dithering for s32 -> s24. */ -} - -static MA_INLINE void ma_pcm_s32_to_s24__optimized(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) -{ - ma_pcm_s32_to_s24__reference(dst, src, count, ditherMode); -} - -#if defined(MA_SUPPORT_SSE2) -static MA_INLINE void ma_pcm_s32_to_s24__sse2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) -{ - ma_pcm_s32_to_s24__optimized(dst, src, count, ditherMode); -} -#endif -#if defined(MA_SUPPORT_NEON) -static MA_INLINE void ma_pcm_s32_to_s24__neon(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) -{ - ma_pcm_s32_to_s24__optimized(dst, src, count, ditherMode); -} -#endif - -MA_API void ma_pcm_s32_to_s24(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) -{ -#ifdef MA_USE_REFERENCE_CONVERSION_APIS - ma_pcm_s32_to_s24__reference(dst, src, count, ditherMode); -#else - # if defined(MA_SUPPORT_SSE2) - if (ma_has_sse2()) { - ma_pcm_s32_to_s24__sse2(dst, src, count, ditherMode); - } else - #elif defined(MA_SUPPORT_NEON) - if (ma_has_neon()) { - ma_pcm_s32_to_s24__neon(dst, src, count, ditherMode); - } else - #endif - { - ma_pcm_s32_to_s24__optimized(dst, src, count, ditherMode); - } -#endif -} - - -MA_API void ma_pcm_s32_to_s32(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) -{ - (void)ditherMode; - - ma_copy_memory_64(dst, src, count * sizeof(ma_int32)); -} - - -static MA_INLINE void ma_pcm_s32_to_f32__reference(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) -{ - float* dst_f32 = (float*)dst; - const ma_int32* src_s32 = (const ma_int32*)src; - - ma_uint64 i; - for (i = 0; i < count; i += 1) { - double x = src_s32[i]; - -#if 0 - x = x + 2147483648.0; - x = x * 0.0000000004656612873077392578125; - x = x - 1; -#else - x = x / 2147483648.0; -#endif - - dst_f32[i] = (float)x; - } - - (void)ditherMode; /* No dithering for s32 -> f32. */ -} - -static MA_INLINE void ma_pcm_s32_to_f32__optimized(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) -{ - ma_pcm_s32_to_f32__reference(dst, src, count, ditherMode); -} - -#if defined(MA_SUPPORT_SSE2) -static MA_INLINE void ma_pcm_s32_to_f32__sse2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) -{ - ma_pcm_s32_to_f32__optimized(dst, src, count, ditherMode); -} -#endif -#if defined(MA_SUPPORT_NEON) -static MA_INLINE void ma_pcm_s32_to_f32__neon(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) -{ - ma_pcm_s32_to_f32__optimized(dst, src, count, ditherMode); -} -#endif - -MA_API void ma_pcm_s32_to_f32(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) -{ -#ifdef MA_USE_REFERENCE_CONVERSION_APIS - ma_pcm_s32_to_f32__reference(dst, src, count, ditherMode); -#else - # if defined(MA_SUPPORT_SSE2) - if (ma_has_sse2()) { - ma_pcm_s32_to_f32__sse2(dst, src, count, ditherMode); - } else - #elif defined(MA_SUPPORT_NEON) - if (ma_has_neon()) { - ma_pcm_s32_to_f32__neon(dst, src, count, ditherMode); - } else - #endif - { - ma_pcm_s32_to_f32__optimized(dst, src, count, ditherMode); - } -#endif -} - - -static MA_INLINE void ma_pcm_interleave_s32__reference(void* dst, const void** src, ma_uint64 frameCount, ma_uint32 channels) -{ - ma_int32* dst_s32 = (ma_int32*)dst; - const ma_int32** src_s32 = (const ma_int32**)src; - - ma_uint64 iFrame; - for (iFrame = 0; iFrame < frameCount; iFrame += 1) { - ma_uint32 iChannel; - for (iChannel = 0; iChannel < channels; iChannel += 1) { - dst_s32[iFrame*channels + iChannel] = src_s32[iChannel][iFrame]; - } - } -} - -static MA_INLINE void ma_pcm_interleave_s32__optimized(void* dst, const void** src, ma_uint64 frameCount, ma_uint32 channels) -{ - ma_pcm_interleave_s32__reference(dst, src, frameCount, channels); -} - -MA_API void ma_pcm_interleave_s32(void* dst, const void** src, ma_uint64 frameCount, ma_uint32 channels) -{ -#ifdef MA_USE_REFERENCE_CONVERSION_APIS - ma_pcm_interleave_s32__reference(dst, src, frameCount, channels); -#else - ma_pcm_interleave_s32__optimized(dst, src, frameCount, channels); -#endif -} - - -static MA_INLINE void ma_pcm_deinterleave_s32__reference(void** dst, const void* src, ma_uint64 frameCount, ma_uint32 channels) -{ - ma_int32** dst_s32 = (ma_int32**)dst; - const ma_int32* src_s32 = (const ma_int32*)src; - - ma_uint64 iFrame; - for (iFrame = 0; iFrame < frameCount; iFrame += 1) { - ma_uint32 iChannel; - for (iChannel = 0; iChannel < channels; iChannel += 1) { - dst_s32[iChannel][iFrame] = src_s32[iFrame*channels + iChannel]; - } - } -} - -static MA_INLINE void ma_pcm_deinterleave_s32__optimized(void** dst, const void* src, ma_uint64 frameCount, ma_uint32 channels) -{ - ma_pcm_deinterleave_s32__reference(dst, src, frameCount, channels); -} - -MA_API void ma_pcm_deinterleave_s32(void** dst, const void* src, ma_uint64 frameCount, ma_uint32 channels) -{ -#ifdef MA_USE_REFERENCE_CONVERSION_APIS - ma_pcm_deinterleave_s32__reference(dst, src, frameCount, channels); -#else - ma_pcm_deinterleave_s32__optimized(dst, src, frameCount, channels); -#endif -} - - -/* f32 */ -static MA_INLINE void ma_pcm_f32_to_u8__reference(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) -{ - ma_uint64 i; - - ma_uint8* dst_u8 = (ma_uint8*)dst; - const float* src_f32 = (const float*)src; - - float ditherMin = 0; - float ditherMax = 0; - if (ditherMode != ma_dither_mode_none) { - ditherMin = 1.0f / -128; - ditherMax = 1.0f / 127; - } - - for (i = 0; i < count; i += 1) { - float x = src_f32[i]; - x = x + ma_dither_f32(ditherMode, ditherMin, ditherMax); - x = ((x < -1) ? -1 : ((x > 1) ? 1 : x)); /* clip */ - x = x + 1; /* -1..1 to 0..2 */ - x = x * 127.5f; /* 0..2 to 0..255 */ - - dst_u8[i] = (ma_uint8)x; - } -} - -static MA_INLINE void ma_pcm_f32_to_u8__optimized(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) -{ - ma_pcm_f32_to_u8__reference(dst, src, count, ditherMode); -} - -#if defined(MA_SUPPORT_SSE2) -static MA_INLINE void ma_pcm_f32_to_u8__sse2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) -{ - ma_pcm_f32_to_u8__optimized(dst, src, count, ditherMode); -} -#endif -#if defined(MA_SUPPORT_NEON) -static MA_INLINE void ma_pcm_f32_to_u8__neon(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) -{ - ma_pcm_f32_to_u8__optimized(dst, src, count, ditherMode); -} -#endif - -MA_API void ma_pcm_f32_to_u8(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) -{ -#ifdef MA_USE_REFERENCE_CONVERSION_APIS - ma_pcm_f32_to_u8__reference(dst, src, count, ditherMode); -#else - # if defined(MA_SUPPORT_SSE2) - if (ma_has_sse2()) { - ma_pcm_f32_to_u8__sse2(dst, src, count, ditherMode); - } else - #elif defined(MA_SUPPORT_NEON) - if (ma_has_neon()) { - ma_pcm_f32_to_u8__neon(dst, src, count, ditherMode); - } else - #endif - { - ma_pcm_f32_to_u8__optimized(dst, src, count, ditherMode); - } -#endif -} - -#ifdef MA_USE_REFERENCE_CONVERSION_APIS -static MA_INLINE void ma_pcm_f32_to_s16__reference(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) -{ - ma_uint64 i; - - ma_int16* dst_s16 = (ma_int16*)dst; - const float* src_f32 = (const float*)src; - - float ditherMin = 0; - float ditherMax = 0; - if (ditherMode != ma_dither_mode_none) { - ditherMin = 1.0f / -32768; - ditherMax = 1.0f / 32767; - } - - for (i = 0; i < count; i += 1) { - float x = src_f32[i]; - x = x + ma_dither_f32(ditherMode, ditherMin, ditherMax); - x = ((x < -1) ? -1 : ((x > 1) ? 1 : x)); /* clip */ - -#if 0 - /* The accurate way. */ - x = x + 1; /* -1..1 to 0..2 */ - x = x * 32767.5f; /* 0..2 to 0..65535 */ - x = x - 32768.0f; /* 0...65535 to -32768..32767 */ -#else - /* The fast way. */ - x = x * 32767.0f; /* -1..1 to -32767..32767 */ -#endif - - dst_s16[i] = (ma_int16)x; - } -} -#else -static MA_INLINE void ma_pcm_f32_to_s16__optimized(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) -{ - ma_uint64 i; - ma_uint64 i4; - ma_uint64 count4; - - ma_int16* dst_s16 = (ma_int16*)dst; - const float* src_f32 = (const float*)src; - - float ditherMin = 0; - float ditherMax = 0; - if (ditherMode != ma_dither_mode_none) { - ditherMin = 1.0f / -32768; - ditherMax = 1.0f / 32767; - } - - /* Unrolled. */ - i = 0; - count4 = count >> 2; - for (i4 = 0; i4 < count4; i4 += 1) { - float d0 = ma_dither_f32(ditherMode, ditherMin, ditherMax); - float d1 = ma_dither_f32(ditherMode, ditherMin, ditherMax); - float d2 = ma_dither_f32(ditherMode, ditherMin, ditherMax); - float d3 = ma_dither_f32(ditherMode, ditherMin, ditherMax); - - float x0 = src_f32[i+0]; - float x1 = src_f32[i+1]; - float x2 = src_f32[i+2]; - float x3 = src_f32[i+3]; - - x0 = x0 + d0; - x1 = x1 + d1; - x2 = x2 + d2; - x3 = x3 + d3; - - x0 = ((x0 < -1) ? -1 : ((x0 > 1) ? 1 : x0)); - x1 = ((x1 < -1) ? -1 : ((x1 > 1) ? 1 : x1)); - x2 = ((x2 < -1) ? -1 : ((x2 > 1) ? 1 : x2)); - x3 = ((x3 < -1) ? -1 : ((x3 > 1) ? 1 : x3)); - - x0 = x0 * 32767.0f; - x1 = x1 * 32767.0f; - x2 = x2 * 32767.0f; - x3 = x3 * 32767.0f; - - dst_s16[i+0] = (ma_int16)x0; - dst_s16[i+1] = (ma_int16)x1; - dst_s16[i+2] = (ma_int16)x2; - dst_s16[i+3] = (ma_int16)x3; - - i += 4; - } - - /* Leftover. */ - for (; i < count; i += 1) { - float x = src_f32[i]; - x = x + ma_dither_f32(ditherMode, ditherMin, ditherMax); - x = ((x < -1) ? -1 : ((x > 1) ? 1 : x)); /* clip */ - x = x * 32767.0f; /* -1..1 to -32767..32767 */ - - dst_s16[i] = (ma_int16)x; - } -} - -#if defined(MA_SUPPORT_SSE2) -static MA_INLINE void ma_pcm_f32_to_s16__sse2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) -{ - ma_uint64 i; - ma_uint64 i8; - ma_uint64 count8; - ma_int16* dst_s16; - const float* src_f32; - float ditherMin; - float ditherMax; - - /* Both the input and output buffers need to be aligned to 16 bytes. */ - if ((((ma_uintptr)dst & 15) != 0) || (((ma_uintptr)src & 15) != 0)) { - ma_pcm_f32_to_s16__optimized(dst, src, count, ditherMode); - return; - } - - dst_s16 = (ma_int16*)dst; - src_f32 = (const float*)src; - - ditherMin = 0; - ditherMax = 0; - if (ditherMode != ma_dither_mode_none) { - ditherMin = 1.0f / -32768; - ditherMax = 1.0f / 32767; - } - - i = 0; - - /* SSE2. SSE allows us to output 8 s16's at a time which means our loop is unrolled 8 times. */ - count8 = count >> 3; - for (i8 = 0; i8 < count8; i8 += 1) { - __m128 d0; - __m128 d1; - __m128 x0; - __m128 x1; - - if (ditherMode == ma_dither_mode_none) { - d0 = _mm_set1_ps(0); - d1 = _mm_set1_ps(0); - } else if (ditherMode == ma_dither_mode_rectangle) { - d0 = _mm_set_ps( - ma_dither_f32_rectangle(ditherMin, ditherMax), - ma_dither_f32_rectangle(ditherMin, ditherMax), - ma_dither_f32_rectangle(ditherMin, ditherMax), - ma_dither_f32_rectangle(ditherMin, ditherMax) - ); - d1 = _mm_set_ps( - ma_dither_f32_rectangle(ditherMin, ditherMax), - ma_dither_f32_rectangle(ditherMin, ditherMax), - ma_dither_f32_rectangle(ditherMin, ditherMax), - ma_dither_f32_rectangle(ditherMin, ditherMax) - ); - } else { - d0 = _mm_set_ps( - ma_dither_f32_triangle(ditherMin, ditherMax), - ma_dither_f32_triangle(ditherMin, ditherMax), - ma_dither_f32_triangle(ditherMin, ditherMax), - ma_dither_f32_triangle(ditherMin, ditherMax) - ); - d1 = _mm_set_ps( - ma_dither_f32_triangle(ditherMin, ditherMax), - ma_dither_f32_triangle(ditherMin, ditherMax), - ma_dither_f32_triangle(ditherMin, ditherMax), - ma_dither_f32_triangle(ditherMin, ditherMax) - ); - } - - x0 = *((__m128*)(src_f32 + i) + 0); - x1 = *((__m128*)(src_f32 + i) + 1); - - x0 = _mm_add_ps(x0, d0); - x1 = _mm_add_ps(x1, d1); - - x0 = _mm_mul_ps(x0, _mm_set1_ps(32767.0f)); - x1 = _mm_mul_ps(x1, _mm_set1_ps(32767.0f)); - - _mm_stream_si128(((__m128i*)(dst_s16 + i)), _mm_packs_epi32(_mm_cvttps_epi32(x0), _mm_cvttps_epi32(x1))); - - i += 8; - } - - - /* Leftover. */ - for (; i < count; i += 1) { - float x = src_f32[i]; - x = x + ma_dither_f32(ditherMode, ditherMin, ditherMax); - x = ((x < -1) ? -1 : ((x > 1) ? 1 : x)); /* clip */ - x = x * 32767.0f; /* -1..1 to -32767..32767 */ - - dst_s16[i] = (ma_int16)x; - } -} -#endif /* SSE2 */ - -#if defined(MA_SUPPORT_NEON) -static MA_INLINE void ma_pcm_f32_to_s16__neon(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) -{ - ma_uint64 i; - ma_uint64 i8; - ma_uint64 count8; - ma_int16* dst_s16; - const float* src_f32; - float ditherMin; - float ditherMax; - - if (!ma_has_neon()) { - ma_pcm_f32_to_s16__optimized(dst, src, count, ditherMode); - return; - } - - /* Both the input and output buffers need to be aligned to 16 bytes. */ - if ((((ma_uintptr)dst & 15) != 0) || (((ma_uintptr)src & 15) != 0)) { - ma_pcm_f32_to_s16__optimized(dst, src, count, ditherMode); - return; - } - - dst_s16 = (ma_int16*)dst; - src_f32 = (const float*)src; - - ditherMin = 0; - ditherMax = 0; - if (ditherMode != ma_dither_mode_none) { - ditherMin = 1.0f / -32768; - ditherMax = 1.0f / 32767; - } - - i = 0; - - /* NEON. NEON allows us to output 8 s16's at a time which means our loop is unrolled 8 times. */ - count8 = count >> 3; - for (i8 = 0; i8 < count8; i8 += 1) { - float32x4_t d0; - float32x4_t d1; - float32x4_t x0; - float32x4_t x1; - int32x4_t i0; - int32x4_t i1; - - if (ditherMode == ma_dither_mode_none) { - d0 = vmovq_n_f32(0); - d1 = vmovq_n_f32(0); - } else if (ditherMode == ma_dither_mode_rectangle) { - float d0v[4]; - float d1v[4]; - - d0v[0] = ma_dither_f32_rectangle(ditherMin, ditherMax); - d0v[1] = ma_dither_f32_rectangle(ditherMin, ditherMax); - d0v[2] = ma_dither_f32_rectangle(ditherMin, ditherMax); - d0v[3] = ma_dither_f32_rectangle(ditherMin, ditherMax); - d0 = vld1q_f32(d0v); - - d1v[0] = ma_dither_f32_rectangle(ditherMin, ditherMax); - d1v[1] = ma_dither_f32_rectangle(ditherMin, ditherMax); - d1v[2] = ma_dither_f32_rectangle(ditherMin, ditherMax); - d1v[3] = ma_dither_f32_rectangle(ditherMin, ditherMax); - d1 = vld1q_f32(d1v); - } else { - float d0v[4]; - float d1v[4]; - - d0v[0] = ma_dither_f32_triangle(ditherMin, ditherMax); - d0v[1] = ma_dither_f32_triangle(ditherMin, ditherMax); - d0v[2] = ma_dither_f32_triangle(ditherMin, ditherMax); - d0v[3] = ma_dither_f32_triangle(ditherMin, ditherMax); - d0 = vld1q_f32(d0v); - - d1v[0] = ma_dither_f32_triangle(ditherMin, ditherMax); - d1v[1] = ma_dither_f32_triangle(ditherMin, ditherMax); - d1v[2] = ma_dither_f32_triangle(ditherMin, ditherMax); - d1v[3] = ma_dither_f32_triangle(ditherMin, ditherMax); - d1 = vld1q_f32(d1v); - } - - x0 = *((float32x4_t*)(src_f32 + i) + 0); - x1 = *((float32x4_t*)(src_f32 + i) + 1); - - x0 = vaddq_f32(x0, d0); - x1 = vaddq_f32(x1, d1); - - x0 = vmulq_n_f32(x0, 32767.0f); - x1 = vmulq_n_f32(x1, 32767.0f); - - i0 = vcvtq_s32_f32(x0); - i1 = vcvtq_s32_f32(x1); - *((int16x8_t*)(dst_s16 + i)) = vcombine_s16(vqmovn_s32(i0), vqmovn_s32(i1)); - - i += 8; - } - - - /* Leftover. */ - for (; i < count; i += 1) { - float x = src_f32[i]; - x = x + ma_dither_f32(ditherMode, ditherMin, ditherMax); - x = ((x < -1) ? -1 : ((x > 1) ? 1 : x)); /* clip */ - x = x * 32767.0f; /* -1..1 to -32767..32767 */ - - dst_s16[i] = (ma_int16)x; - } -} -#endif /* Neon */ -#endif /* MA_USE_REFERENCE_CONVERSION_APIS */ - -MA_API void ma_pcm_f32_to_s16(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) -{ -#ifdef MA_USE_REFERENCE_CONVERSION_APIS - ma_pcm_f32_to_s16__reference(dst, src, count, ditherMode); -#else - # if defined(MA_SUPPORT_SSE2) - if (ma_has_sse2()) { - ma_pcm_f32_to_s16__sse2(dst, src, count, ditherMode); - } else - #elif defined(MA_SUPPORT_NEON) - if (ma_has_neon()) { - ma_pcm_f32_to_s16__neon(dst, src, count, ditherMode); - } else - #endif - { - ma_pcm_f32_to_s16__optimized(dst, src, count, ditherMode); - } -#endif -} - - -static MA_INLINE void ma_pcm_f32_to_s24__reference(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) -{ - ma_uint8* dst_s24 = (ma_uint8*)dst; - const float* src_f32 = (const float*)src; - - ma_uint64 i; - for (i = 0; i < count; i += 1) { - ma_int32 r; - float x = src_f32[i]; - x = ((x < -1) ? -1 : ((x > 1) ? 1 : x)); /* clip */ - -#if 0 - /* The accurate way. */ - x = x + 1; /* -1..1 to 0..2 */ - x = x * 8388607.5f; /* 0..2 to 0..16777215 */ - x = x - 8388608.0f; /* 0..16777215 to -8388608..8388607 */ -#else - /* The fast way. */ - x = x * 8388607.0f; /* -1..1 to -8388607..8388607 */ -#endif - - r = (ma_int32)x; - dst_s24[(i*3)+0] = (ma_uint8)((r & 0x0000FF) >> 0); - dst_s24[(i*3)+1] = (ma_uint8)((r & 0x00FF00) >> 8); - dst_s24[(i*3)+2] = (ma_uint8)((r & 0xFF0000) >> 16); - } - - (void)ditherMode; /* No dithering for f32 -> s24. */ -} - -static MA_INLINE void ma_pcm_f32_to_s24__optimized(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) -{ - ma_pcm_f32_to_s24__reference(dst, src, count, ditherMode); -} - -#if defined(MA_SUPPORT_SSE2) -static MA_INLINE void ma_pcm_f32_to_s24__sse2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) -{ - ma_pcm_f32_to_s24__optimized(dst, src, count, ditherMode); -} -#endif -#if defined(MA_SUPPORT_NEON) -static MA_INLINE void ma_pcm_f32_to_s24__neon(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) -{ - ma_pcm_f32_to_s24__optimized(dst, src, count, ditherMode); -} -#endif - -MA_API void ma_pcm_f32_to_s24(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) -{ -#ifdef MA_USE_REFERENCE_CONVERSION_APIS - ma_pcm_f32_to_s24__reference(dst, src, count, ditherMode); -#else - # if defined(MA_SUPPORT_SSE2) - if (ma_has_sse2()) { - ma_pcm_f32_to_s24__sse2(dst, src, count, ditherMode); - } else - #elif defined(MA_SUPPORT_NEON) - if (ma_has_neon()) { - ma_pcm_f32_to_s24__neon(dst, src, count, ditherMode); - } else - #endif - { - ma_pcm_f32_to_s24__optimized(dst, src, count, ditherMode); - } -#endif -} - - -static MA_INLINE void ma_pcm_f32_to_s32__reference(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) -{ - ma_int32* dst_s32 = (ma_int32*)dst; - const float* src_f32 = (const float*)src; - - ma_uint32 i; - for (i = 0; i < count; i += 1) { - double x = src_f32[i]; - x = ((x < -1) ? -1 : ((x > 1) ? 1 : x)); /* clip */ - -#if 0 - /* The accurate way. */ - x = x + 1; /* -1..1 to 0..2 */ - x = x * 2147483647.5; /* 0..2 to 0..4294967295 */ - x = x - 2147483648.0; /* 0...4294967295 to -2147483648..2147483647 */ -#else - /* The fast way. */ - x = x * 2147483647.0; /* -1..1 to -2147483647..2147483647 */ -#endif - - dst_s32[i] = (ma_int32)x; - } - - (void)ditherMode; /* No dithering for f32 -> s32. */ -} - -static MA_INLINE void ma_pcm_f32_to_s32__optimized(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) -{ - ma_pcm_f32_to_s32__reference(dst, src, count, ditherMode); -} - -#if defined(MA_SUPPORT_SSE2) -static MA_INLINE void ma_pcm_f32_to_s32__sse2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) -{ - ma_pcm_f32_to_s32__optimized(dst, src, count, ditherMode); -} -#endif -#if defined(MA_SUPPORT_NEON) -static MA_INLINE void ma_pcm_f32_to_s32__neon(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) -{ - ma_pcm_f32_to_s32__optimized(dst, src, count, ditherMode); -} -#endif - -MA_API void ma_pcm_f32_to_s32(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) -{ -#ifdef MA_USE_REFERENCE_CONVERSION_APIS - ma_pcm_f32_to_s32__reference(dst, src, count, ditherMode); -#else - # if defined(MA_SUPPORT_SSE2) - if (ma_has_sse2()) { - ma_pcm_f32_to_s32__sse2(dst, src, count, ditherMode); - } else - #elif defined(MA_SUPPORT_NEON) - if (ma_has_neon()) { - ma_pcm_f32_to_s32__neon(dst, src, count, ditherMode); - } else - #endif - { - ma_pcm_f32_to_s32__optimized(dst, src, count, ditherMode); - } -#endif -} - - -MA_API void ma_pcm_f32_to_f32(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) -{ - (void)ditherMode; - - ma_copy_memory_64(dst, src, count * sizeof(float)); -} - - -static void ma_pcm_interleave_f32__reference(void* dst, const void** src, ma_uint64 frameCount, ma_uint32 channels) -{ - float* dst_f32 = (float*)dst; - const float** src_f32 = (const float**)src; - - ma_uint64 iFrame; - for (iFrame = 0; iFrame < frameCount; iFrame += 1) { - ma_uint32 iChannel; - for (iChannel = 0; iChannel < channels; iChannel += 1) { - dst_f32[iFrame*channels + iChannel] = src_f32[iChannel][iFrame]; - } - } -} - -static void ma_pcm_interleave_f32__optimized(void* dst, const void** src, ma_uint64 frameCount, ma_uint32 channels) -{ - ma_pcm_interleave_f32__reference(dst, src, frameCount, channels); -} - -MA_API void ma_pcm_interleave_f32(void* dst, const void** src, ma_uint64 frameCount, ma_uint32 channels) -{ -#ifdef MA_USE_REFERENCE_CONVERSION_APIS - ma_pcm_interleave_f32__reference(dst, src, frameCount, channels); -#else - ma_pcm_interleave_f32__optimized(dst, src, frameCount, channels); -#endif -} - - -static void ma_pcm_deinterleave_f32__reference(void** dst, const void* src, ma_uint64 frameCount, ma_uint32 channels) -{ - float** dst_f32 = (float**)dst; - const float* src_f32 = (const float*)src; - - ma_uint64 iFrame; - for (iFrame = 0; iFrame < frameCount; iFrame += 1) { - ma_uint32 iChannel; - for (iChannel = 0; iChannel < channels; iChannel += 1) { - dst_f32[iChannel][iFrame] = src_f32[iFrame*channels + iChannel]; - } - } -} - -static void ma_pcm_deinterleave_f32__optimized(void** dst, const void* src, ma_uint64 frameCount, ma_uint32 channels) -{ - ma_pcm_deinterleave_f32__reference(dst, src, frameCount, channels); -} - -MA_API void ma_pcm_deinterleave_f32(void** dst, const void* src, ma_uint64 frameCount, ma_uint32 channels) -{ -#ifdef MA_USE_REFERENCE_CONVERSION_APIS - ma_pcm_deinterleave_f32__reference(dst, src, frameCount, channels); -#else - ma_pcm_deinterleave_f32__optimized(dst, src, frameCount, channels); -#endif -} - - -MA_API void ma_pcm_convert(void* pOut, ma_format formatOut, const void* pIn, ma_format formatIn, ma_uint64 sampleCount, ma_dither_mode ditherMode) -{ - if (formatOut == formatIn) { - ma_copy_memory_64(pOut, pIn, sampleCount * ma_get_bytes_per_sample(formatOut)); - return; - } - - switch (formatIn) - { - case ma_format_u8: - { - switch (formatOut) - { - case ma_format_s16: ma_pcm_u8_to_s16(pOut, pIn, sampleCount, ditherMode); return; - case ma_format_s24: ma_pcm_u8_to_s24(pOut, pIn, sampleCount, ditherMode); return; - case ma_format_s32: ma_pcm_u8_to_s32(pOut, pIn, sampleCount, ditherMode); return; - case ma_format_f32: ma_pcm_u8_to_f32(pOut, pIn, sampleCount, ditherMode); return; - default: break; - } - } break; - - case ma_format_s16: - { - switch (formatOut) - { - case ma_format_u8: ma_pcm_s16_to_u8( pOut, pIn, sampleCount, ditherMode); return; - case ma_format_s24: ma_pcm_s16_to_s24(pOut, pIn, sampleCount, ditherMode); return; - case ma_format_s32: ma_pcm_s16_to_s32(pOut, pIn, sampleCount, ditherMode); return; - case ma_format_f32: ma_pcm_s16_to_f32(pOut, pIn, sampleCount, ditherMode); return; - default: break; - } - } break; - - case ma_format_s24: - { - switch (formatOut) - { - case ma_format_u8: ma_pcm_s24_to_u8( pOut, pIn, sampleCount, ditherMode); return; - case ma_format_s16: ma_pcm_s24_to_s16(pOut, pIn, sampleCount, ditherMode); return; - case ma_format_s32: ma_pcm_s24_to_s32(pOut, pIn, sampleCount, ditherMode); return; - case ma_format_f32: ma_pcm_s24_to_f32(pOut, pIn, sampleCount, ditherMode); return; - default: break; - } - } break; - - case ma_format_s32: - { - switch (formatOut) - { - case ma_format_u8: ma_pcm_s32_to_u8( pOut, pIn, sampleCount, ditherMode); return; - case ma_format_s16: ma_pcm_s32_to_s16(pOut, pIn, sampleCount, ditherMode); return; - case ma_format_s24: ma_pcm_s32_to_s24(pOut, pIn, sampleCount, ditherMode); return; - case ma_format_f32: ma_pcm_s32_to_f32(pOut, pIn, sampleCount, ditherMode); return; - default: break; - } - } break; - - case ma_format_f32: - { - switch (formatOut) - { - case ma_format_u8: ma_pcm_f32_to_u8( pOut, pIn, sampleCount, ditherMode); return; - case ma_format_s16: ma_pcm_f32_to_s16(pOut, pIn, sampleCount, ditherMode); return; - case ma_format_s24: ma_pcm_f32_to_s24(pOut, pIn, sampleCount, ditherMode); return; - case ma_format_s32: ma_pcm_f32_to_s32(pOut, pIn, sampleCount, ditherMode); return; - default: break; - } - } break; - - default: break; - } -} - -MA_API void ma_convert_pcm_frames_format(void* pOut, ma_format formatOut, const void* pIn, ma_format formatIn, ma_uint64 frameCount, ma_uint32 channels, ma_dither_mode ditherMode) -{ - ma_pcm_convert(pOut, formatOut, pIn, formatIn, frameCount * channels, ditherMode); -} - -MA_API void ma_deinterleave_pcm_frames(ma_format format, ma_uint32 channels, ma_uint64 frameCount, const void* pInterleavedPCMFrames, void** ppDeinterleavedPCMFrames) -{ - if (pInterleavedPCMFrames == NULL || ppDeinterleavedPCMFrames == NULL) { - return; /* Invalid args. */ - } - - /* For efficiency we do this per format. */ - switch (format) { - case ma_format_s16: - { - const ma_int16* pSrcS16 = (const ma_int16*)pInterleavedPCMFrames; - ma_uint64 iPCMFrame; - for (iPCMFrame = 0; iPCMFrame < frameCount; ++iPCMFrame) { - ma_uint32 iChannel; - for (iChannel = 0; iChannel < channels; ++iChannel) { - ma_int16* pDstS16 = (ma_int16*)ppDeinterleavedPCMFrames[iChannel]; - pDstS16[iPCMFrame] = pSrcS16[iPCMFrame*channels+iChannel]; - } - } - } break; - - case ma_format_f32: - { - const float* pSrcF32 = (const float*)pInterleavedPCMFrames; - ma_uint64 iPCMFrame; - for (iPCMFrame = 0; iPCMFrame < frameCount; ++iPCMFrame) { - ma_uint32 iChannel; - for (iChannel = 0; iChannel < channels; ++iChannel) { - float* pDstF32 = (float*)ppDeinterleavedPCMFrames[iChannel]; - pDstF32[iPCMFrame] = pSrcF32[iPCMFrame*channels+iChannel]; - } - } - } break; - - default: - { - ma_uint32 sampleSizeInBytes = ma_get_bytes_per_sample(format); - ma_uint64 iPCMFrame; - for (iPCMFrame = 0; iPCMFrame < frameCount; ++iPCMFrame) { - ma_uint32 iChannel; - for (iChannel = 0; iChannel < channels; ++iChannel) { - void* pDst = ma_offset_ptr(ppDeinterleavedPCMFrames[iChannel], iPCMFrame*sampleSizeInBytes); - const void* pSrc = ma_offset_ptr(pInterleavedPCMFrames, (iPCMFrame*channels+iChannel)*sampleSizeInBytes); - memcpy(pDst, pSrc, sampleSizeInBytes); - } - } - } break; - } -} - -MA_API void ma_interleave_pcm_frames(ma_format format, ma_uint32 channels, ma_uint64 frameCount, const void** ppDeinterleavedPCMFrames, void* pInterleavedPCMFrames) -{ - switch (format) - { - case ma_format_s16: - { - ma_int16* pDstS16 = (ma_int16*)pInterleavedPCMFrames; - ma_uint64 iPCMFrame; - for (iPCMFrame = 0; iPCMFrame < frameCount; ++iPCMFrame) { - ma_uint32 iChannel; - for (iChannel = 0; iChannel < channels; ++iChannel) { - const ma_int16* pSrcS16 = (const ma_int16*)ppDeinterleavedPCMFrames[iChannel]; - pDstS16[iPCMFrame*channels+iChannel] = pSrcS16[iPCMFrame]; - } - } - } break; - - case ma_format_f32: - { - float* pDstF32 = (float*)pInterleavedPCMFrames; - ma_uint64 iPCMFrame; - for (iPCMFrame = 0; iPCMFrame < frameCount; ++iPCMFrame) { - ma_uint32 iChannel; - for (iChannel = 0; iChannel < channels; ++iChannel) { - const float* pSrcF32 = (const float*)ppDeinterleavedPCMFrames[iChannel]; - pDstF32[iPCMFrame*channels+iChannel] = pSrcF32[iPCMFrame]; - } - } - } break; - - default: - { - ma_uint32 sampleSizeInBytes = ma_get_bytes_per_sample(format); - ma_uint64 iPCMFrame; - for (iPCMFrame = 0; iPCMFrame < frameCount; ++iPCMFrame) { - ma_uint32 iChannel; - for (iChannel = 0; iChannel < channels; ++iChannel) { - void* pDst = ma_offset_ptr(pInterleavedPCMFrames, (iPCMFrame*channels+iChannel)*sampleSizeInBytes); - const void* pSrc = ma_offset_ptr(ppDeinterleavedPCMFrames[iChannel], iPCMFrame*sampleSizeInBytes); - memcpy(pDst, pSrc, sampleSizeInBytes); - } - } - } break; - } -} - - -/************************************************************************************************************************************************************** - -Biquad Filter - -**************************************************************************************************************************************************************/ -#ifndef MA_BIQUAD_FIXED_POINT_SHIFT -#define MA_BIQUAD_FIXED_POINT_SHIFT 14 -#endif - -static ma_int32 ma_biquad_float_to_fp(double x) -{ - return (ma_int32)(x * (1 << MA_BIQUAD_FIXED_POINT_SHIFT)); -} - -MA_API ma_biquad_config ma_biquad_config_init(ma_format format, ma_uint32 channels, double b0, double b1, double b2, double a0, double a1, double a2) -{ - ma_biquad_config config; - - MA_ZERO_OBJECT(&config); - config.format = format; - config.channels = channels; - config.b0 = b0; - config.b1 = b1; - config.b2 = b2; - config.a0 = a0; - config.a1 = a1; - config.a2 = a2; - - return config; -} - - -typedef struct -{ - size_t sizeInBytes; - size_t r1Offset; - size_t r2Offset; -} ma_biquad_heap_layout; - -static ma_result ma_biquad_get_heap_layout(const ma_biquad_config* pConfig, ma_biquad_heap_layout* pHeapLayout) -{ - MA_ASSERT(pHeapLayout != NULL); - - MA_ZERO_OBJECT(pHeapLayout); - - if (pConfig == NULL) { - return MA_INVALID_ARGS; - } - - if (pConfig->channels == 0) { - return MA_INVALID_ARGS; - } - - pHeapLayout->sizeInBytes = 0; - - /* R0 */ - pHeapLayout->r1Offset = pHeapLayout->sizeInBytes; - pHeapLayout->sizeInBytes += sizeof(ma_biquad_coefficient) * pConfig->channels; - - /* R1 */ - pHeapLayout->r2Offset = pHeapLayout->sizeInBytes; - pHeapLayout->sizeInBytes += sizeof(ma_biquad_coefficient) * pConfig->channels; - - /* Make sure allocation size is aligned. */ - pHeapLayout->sizeInBytes = ma_align_64(pHeapLayout->sizeInBytes); - - return MA_SUCCESS; -} - -MA_API ma_result ma_biquad_get_heap_size(const ma_biquad_config* pConfig, size_t* pHeapSizeInBytes) -{ - ma_result result; - ma_biquad_heap_layout heapLayout; - - if (pHeapSizeInBytes == NULL) { - return MA_INVALID_ARGS; - } - - *pHeapSizeInBytes = 0; - - result = ma_biquad_get_heap_layout(pConfig, &heapLayout); - if (result != MA_SUCCESS) { - return result; - } - - *pHeapSizeInBytes = heapLayout.sizeInBytes; - - return MA_SUCCESS; -} - -MA_API ma_result ma_biquad_init_preallocated(const ma_biquad_config* pConfig, void* pHeap, ma_biquad* pBQ) -{ - ma_result result; - ma_biquad_heap_layout heapLayout; - - if (pBQ == NULL) { - return MA_INVALID_ARGS; - } - - MA_ZERO_OBJECT(pBQ); - - result = ma_biquad_get_heap_layout(pConfig, &heapLayout); - if (result != MA_SUCCESS) { - return result; - } - - pBQ->_pHeap = pHeap; - MA_ZERO_MEMORY(pHeap, heapLayout.sizeInBytes); - - pBQ->pR1 = (ma_biquad_coefficient*)ma_offset_ptr(pHeap, heapLayout.r1Offset); - pBQ->pR2 = (ma_biquad_coefficient*)ma_offset_ptr(pHeap, heapLayout.r2Offset); - - return ma_biquad_reinit(pConfig, pBQ); -} - -MA_API ma_result ma_biquad_init(const ma_biquad_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_biquad* pBQ) -{ - ma_result result; - size_t heapSizeInBytes; - void* pHeap; - - result = ma_biquad_get_heap_size(pConfig, &heapSizeInBytes); - if (result != MA_SUCCESS) { - return result; - } - - if (heapSizeInBytes > 0) { - pHeap = ma_malloc(heapSizeInBytes, pAllocationCallbacks); - if (pHeap == NULL) { - return MA_OUT_OF_MEMORY; - } - } else { - pHeap = NULL; - } - - result = ma_biquad_init_preallocated(pConfig, pHeap, pBQ); - if (result != MA_SUCCESS) { - ma_free(pHeap, pAllocationCallbacks); - return result; - } - - pBQ->_ownsHeap = MA_TRUE; - return MA_SUCCESS; -} - -MA_API void ma_biquad_uninit(ma_biquad* pBQ, const ma_allocation_callbacks* pAllocationCallbacks) -{ - if (pBQ == NULL) { - return; - } - - if (pBQ->_ownsHeap) { - ma_free(pBQ->_pHeap, pAllocationCallbacks); - } -} - -MA_API ma_result ma_biquad_reinit(const ma_biquad_config* pConfig, ma_biquad* pBQ) -{ - if (pBQ == NULL || pConfig == NULL) { - return MA_INVALID_ARGS; - } - - if (pConfig->a0 == 0) { - return MA_INVALID_ARGS; /* Division by zero. */ - } - - /* Only supporting f32 and s16. */ - if (pConfig->format != ma_format_f32 && pConfig->format != ma_format_s16) { - return MA_INVALID_ARGS; - } - - /* The format cannot be changed after initialization. */ - if (pBQ->format != ma_format_unknown && pBQ->format != pConfig->format) { - return MA_INVALID_OPERATION; - } - - /* The channel count cannot be changed after initialization. */ - if (pBQ->channels != 0 && pBQ->channels != pConfig->channels) { - return MA_INVALID_OPERATION; - } - - - pBQ->format = pConfig->format; - pBQ->channels = pConfig->channels; - - /* Normalize. */ - if (pConfig->format == ma_format_f32) { - pBQ->b0.f32 = (float)(pConfig->b0 / pConfig->a0); - pBQ->b1.f32 = (float)(pConfig->b1 / pConfig->a0); - pBQ->b2.f32 = (float)(pConfig->b2 / pConfig->a0); - pBQ->a1.f32 = (float)(pConfig->a1 / pConfig->a0); - pBQ->a2.f32 = (float)(pConfig->a2 / pConfig->a0); - } else { - pBQ->b0.s32 = ma_biquad_float_to_fp(pConfig->b0 / pConfig->a0); - pBQ->b1.s32 = ma_biquad_float_to_fp(pConfig->b1 / pConfig->a0); - pBQ->b2.s32 = ma_biquad_float_to_fp(pConfig->b2 / pConfig->a0); - pBQ->a1.s32 = ma_biquad_float_to_fp(pConfig->a1 / pConfig->a0); - pBQ->a2.s32 = ma_biquad_float_to_fp(pConfig->a2 / pConfig->a0); - } - - return MA_SUCCESS; -} - -MA_API ma_result ma_biquad_clear_cache(ma_biquad* pBQ) -{ - if (pBQ == NULL) { - return MA_INVALID_ARGS; - } - - if (pBQ->format == ma_format_f32) { - pBQ->pR1->f32 = 0; - pBQ->pR2->f32 = 0; - } else { - pBQ->pR1->s32 = 0; - pBQ->pR2->s32 = 0; - } - - return MA_SUCCESS; -} - -static MA_INLINE void ma_biquad_process_pcm_frame_f32__direct_form_2_transposed(ma_biquad* pBQ, float* pY, const float* pX) -{ - ma_uint32 c; - const ma_uint32 channels = pBQ->channels; - const float b0 = pBQ->b0.f32; - const float b1 = pBQ->b1.f32; - const float b2 = pBQ->b2.f32; - const float a1 = pBQ->a1.f32; - const float a2 = pBQ->a2.f32; - - MA_ASSUME(channels > 0); - for (c = 0; c < channels; c += 1) { - float r1 = pBQ->pR1[c].f32; - float r2 = pBQ->pR2[c].f32; - float x = pX[c]; - float y; - - y = b0*x + r1; - r1 = b1*x - a1*y + r2; - r2 = b2*x - a2*y; - - pY[c] = y; - pBQ->pR1[c].f32 = r1; - pBQ->pR2[c].f32 = r2; - } -} - -static MA_INLINE void ma_biquad_process_pcm_frame_f32(ma_biquad* pBQ, float* pY, const float* pX) -{ - ma_biquad_process_pcm_frame_f32__direct_form_2_transposed(pBQ, pY, pX); -} - -static MA_INLINE void ma_biquad_process_pcm_frame_s16__direct_form_2_transposed(ma_biquad* pBQ, ma_int16* pY, const ma_int16* pX) -{ - ma_uint32 c; - const ma_uint32 channels = pBQ->channels; - const ma_int32 b0 = pBQ->b0.s32; - const ma_int32 b1 = pBQ->b1.s32; - const ma_int32 b2 = pBQ->b2.s32; - const ma_int32 a1 = pBQ->a1.s32; - const ma_int32 a2 = pBQ->a2.s32; - - MA_ASSUME(channels > 0); - for (c = 0; c < channels; c += 1) { - ma_int32 r1 = pBQ->pR1[c].s32; - ma_int32 r2 = pBQ->pR2[c].s32; - ma_int32 x = pX[c]; - ma_int32 y; - - y = (b0*x + r1) >> MA_BIQUAD_FIXED_POINT_SHIFT; - r1 = (b1*x - a1*y + r2); - r2 = (b2*x - a2*y); - - pY[c] = (ma_int16)ma_clamp(y, -32768, 32767); - pBQ->pR1[c].s32 = r1; - pBQ->pR2[c].s32 = r2; - } -} - -static MA_INLINE void ma_biquad_process_pcm_frame_s16(ma_biquad* pBQ, ma_int16* pY, const ma_int16* pX) -{ - ma_biquad_process_pcm_frame_s16__direct_form_2_transposed(pBQ, pY, pX); -} - -MA_API ma_result ma_biquad_process_pcm_frames(ma_biquad* pBQ, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount) -{ - ma_uint32 n; - - if (pBQ == NULL || pFramesOut == NULL || pFramesIn == NULL) { - return MA_INVALID_ARGS; - } - - /* Note that the logic below needs to support in-place filtering. That is, it must support the case where pFramesOut and pFramesIn are the same. */ - - if (pBQ->format == ma_format_f32) { - /* */ float* pY = ( float*)pFramesOut; - const float* pX = (const float*)pFramesIn; - - for (n = 0; n < frameCount; n += 1) { - ma_biquad_process_pcm_frame_f32__direct_form_2_transposed(pBQ, pY, pX); - pY += pBQ->channels; - pX += pBQ->channels; - } - } else if (pBQ->format == ma_format_s16) { - /* */ ma_int16* pY = ( ma_int16*)pFramesOut; - const ma_int16* pX = (const ma_int16*)pFramesIn; - - for (n = 0; n < frameCount; n += 1) { - ma_biquad_process_pcm_frame_s16__direct_form_2_transposed(pBQ, pY, pX); - pY += pBQ->channels; - pX += pBQ->channels; - } - } else { - MA_ASSERT(MA_FALSE); - return MA_INVALID_ARGS; /* Format not supported. Should never hit this because it's checked in ma_biquad_init() and ma_biquad_reinit(). */ - } - - return MA_SUCCESS; -} - -MA_API ma_uint32 ma_biquad_get_latency(const ma_biquad* pBQ) -{ - if (pBQ == NULL) { - return 0; - } - - return 2; -} - - -/************************************************************************************************************************************************************** - -Low-Pass Filter - -**************************************************************************************************************************************************************/ -MA_API ma_lpf1_config ma_lpf1_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRate, double cutoffFrequency) -{ - ma_lpf1_config config; - - MA_ZERO_OBJECT(&config); - config.format = format; - config.channels = channels; - config.sampleRate = sampleRate; - config.cutoffFrequency = cutoffFrequency; - config.q = 0.5; - - return config; -} - -MA_API ma_lpf2_config ma_lpf2_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRate, double cutoffFrequency, double q) -{ - ma_lpf2_config config; - - MA_ZERO_OBJECT(&config); - config.format = format; - config.channels = channels; - config.sampleRate = sampleRate; - config.cutoffFrequency = cutoffFrequency; - config.q = q; - - /* Q cannot be 0 or else it'll result in a division by 0. In this case just default to 0.707107. */ - if (config.q == 0) { - config.q = 0.707107; - } - - return config; -} - - -typedef struct -{ - size_t sizeInBytes; - size_t r1Offset; -} ma_lpf1_heap_layout; - -static ma_result ma_lpf1_get_heap_layout(const ma_lpf1_config* pConfig, ma_lpf1_heap_layout* pHeapLayout) -{ - MA_ASSERT(pHeapLayout != NULL); - - MA_ZERO_OBJECT(pHeapLayout); - - if (pConfig == NULL) { - return MA_INVALID_ARGS; - } - - if (pConfig->channels == 0) { - return MA_INVALID_ARGS; - } - - pHeapLayout->sizeInBytes = 0; - - /* R1 */ - pHeapLayout->r1Offset = pHeapLayout->sizeInBytes; - pHeapLayout->sizeInBytes += sizeof(ma_biquad_coefficient) * pConfig->channels; - - /* Make sure allocation size is aligned. */ - pHeapLayout->sizeInBytes = ma_align_64(pHeapLayout->sizeInBytes); - - return MA_SUCCESS; -} - -MA_API ma_result ma_lpf1_get_heap_size(const ma_lpf1_config* pConfig, size_t* pHeapSizeInBytes) -{ - ma_result result; - ma_lpf1_heap_layout heapLayout; - - if (pHeapSizeInBytes == NULL) { - return MA_INVALID_ARGS; - } - - result = ma_lpf1_get_heap_layout(pConfig, &heapLayout); - if (result != MA_SUCCESS) { - return result; - } - - *pHeapSizeInBytes = heapLayout.sizeInBytes; - - return MA_SUCCESS; -} - -MA_API ma_result ma_lpf1_init_preallocated(const ma_lpf1_config* pConfig, void* pHeap, ma_lpf1* pLPF) -{ - ma_result result; - ma_lpf1_heap_layout heapLayout; - - if (pLPF == NULL) { - return MA_INVALID_ARGS; - } - - MA_ZERO_OBJECT(pLPF); - - result = ma_lpf1_get_heap_layout(pConfig, &heapLayout); - if (result != MA_SUCCESS) { - return result; - } - - pLPF->_pHeap = pHeap; - MA_ZERO_MEMORY(pHeap, heapLayout.sizeInBytes); - - pLPF->pR1 = (ma_biquad_coefficient*)ma_offset_ptr(pHeap, heapLayout.r1Offset); - - return ma_lpf1_reinit(pConfig, pLPF); -} - -MA_API ma_result ma_lpf1_init(const ma_lpf1_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_lpf1* pLPF) -{ - ma_result result; - size_t heapSizeInBytes; - void* pHeap; - - result = ma_lpf1_get_heap_size(pConfig, &heapSizeInBytes); - if (result != MA_SUCCESS) { - return result; - } - - if (heapSizeInBytes > 0) { - pHeap = ma_malloc(heapSizeInBytes, pAllocationCallbacks); - if (pHeap == NULL) { - return MA_OUT_OF_MEMORY; - } - } else { - pHeap = NULL; - } - - result = ma_lpf1_init_preallocated(pConfig, pHeap, pLPF); - if (result != MA_SUCCESS) { - ma_free(pHeap, pAllocationCallbacks); - return result; - } - - pLPF->_ownsHeap = MA_TRUE; - return MA_SUCCESS; -} - -MA_API void ma_lpf1_uninit(ma_lpf1* pLPF, const ma_allocation_callbacks* pAllocationCallbacks) -{ - if (pLPF == NULL) { - return; - } - - if (pLPF->_ownsHeap) { - ma_free(pLPF->_pHeap, pAllocationCallbacks); - } -} - -MA_API ma_result ma_lpf1_reinit(const ma_lpf1_config* pConfig, ma_lpf1* pLPF) -{ - double a; - - if (pLPF == NULL || pConfig == NULL) { - return MA_INVALID_ARGS; - } - - /* Only supporting f32 and s16. */ - if (pConfig->format != ma_format_f32 && pConfig->format != ma_format_s16) { - return MA_INVALID_ARGS; - } - - /* The format cannot be changed after initialization. */ - if (pLPF->format != ma_format_unknown && pLPF->format != pConfig->format) { - return MA_INVALID_OPERATION; - } - - /* The channel count cannot be changed after initialization. */ - if (pLPF->channels != 0 && pLPF->channels != pConfig->channels) { - return MA_INVALID_OPERATION; - } - - pLPF->format = pConfig->format; - pLPF->channels = pConfig->channels; - - a = ma_expd(-2 * MA_PI_D * pConfig->cutoffFrequency / pConfig->sampleRate); - if (pConfig->format == ma_format_f32) { - pLPF->a.f32 = (float)a; - } else { - pLPF->a.s32 = ma_biquad_float_to_fp(a); - } - - return MA_SUCCESS; -} - -MA_API ma_result ma_lpf1_clear_cache(ma_lpf1* pLPF) -{ - if (pLPF == NULL) { - return MA_INVALID_ARGS; - } - - if (pLPF->format == ma_format_f32) { - pLPF->a.f32 = 0; - } else { - pLPF->a.s32 = 0; - } - - return MA_SUCCESS; -} - -static MA_INLINE void ma_lpf1_process_pcm_frame_f32(ma_lpf1* pLPF, float* pY, const float* pX) -{ - ma_uint32 c; - const ma_uint32 channels = pLPF->channels; - const float a = pLPF->a.f32; - const float b = 1 - a; - - MA_ASSUME(channels > 0); - for (c = 0; c < channels; c += 1) { - float r1 = pLPF->pR1[c].f32; - float x = pX[c]; - float y; - - y = b*x + a*r1; - - pY[c] = y; - pLPF->pR1[c].f32 = y; - } -} - -static MA_INLINE void ma_lpf1_process_pcm_frame_s16(ma_lpf1* pLPF, ma_int16* pY, const ma_int16* pX) -{ - ma_uint32 c; - const ma_uint32 channels = pLPF->channels; - const ma_int32 a = pLPF->a.s32; - const ma_int32 b = ((1 << MA_BIQUAD_FIXED_POINT_SHIFT) - a); - - MA_ASSUME(channels > 0); - for (c = 0; c < channels; c += 1) { - ma_int32 r1 = pLPF->pR1[c].s32; - ma_int32 x = pX[c]; - ma_int32 y; - - y = (b*x + a*r1) >> MA_BIQUAD_FIXED_POINT_SHIFT; - - pY[c] = (ma_int16)y; - pLPF->pR1[c].s32 = (ma_int32)y; - } -} - -MA_API ma_result ma_lpf1_process_pcm_frames(ma_lpf1* pLPF, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount) -{ - ma_uint32 n; - - if (pLPF == NULL || pFramesOut == NULL || pFramesIn == NULL) { - return MA_INVALID_ARGS; - } - - /* Note that the logic below needs to support in-place filtering. That is, it must support the case where pFramesOut and pFramesIn are the same. */ - - if (pLPF->format == ma_format_f32) { - /* */ float* pY = ( float*)pFramesOut; - const float* pX = (const float*)pFramesIn; - - for (n = 0; n < frameCount; n += 1) { - ma_lpf1_process_pcm_frame_f32(pLPF, pY, pX); - pY += pLPF->channels; - pX += pLPF->channels; - } - } else if (pLPF->format == ma_format_s16) { - /* */ ma_int16* pY = ( ma_int16*)pFramesOut; - const ma_int16* pX = (const ma_int16*)pFramesIn; - - for (n = 0; n < frameCount; n += 1) { - ma_lpf1_process_pcm_frame_s16(pLPF, pY, pX); - pY += pLPF->channels; - pX += pLPF->channels; - } - } else { - MA_ASSERT(MA_FALSE); - return MA_INVALID_ARGS; /* Format not supported. Should never hit this because it's checked in ma_biquad_init() and ma_biquad_reinit(). */ - } - - return MA_SUCCESS; -} - -MA_API ma_uint32 ma_lpf1_get_latency(const ma_lpf1* pLPF) -{ - if (pLPF == NULL) { - return 0; - } - - return 1; -} - - -static MA_INLINE ma_biquad_config ma_lpf2__get_biquad_config(const ma_lpf2_config* pConfig) -{ - ma_biquad_config bqConfig; - double q; - double w; - double s; - double c; - double a; - - MA_ASSERT(pConfig != NULL); - - q = pConfig->q; - w = 2 * MA_PI_D * pConfig->cutoffFrequency / pConfig->sampleRate; - s = ma_sind(w); - c = ma_cosd(w); - a = s / (2*q); - - bqConfig.b0 = (1 - c) / 2; - bqConfig.b1 = 1 - c; - bqConfig.b2 = (1 - c) / 2; - bqConfig.a0 = 1 + a; - bqConfig.a1 = -2 * c; - bqConfig.a2 = 1 - a; - - bqConfig.format = pConfig->format; - bqConfig.channels = pConfig->channels; - - return bqConfig; -} - -MA_API ma_result ma_lpf2_get_heap_size(const ma_lpf2_config* pConfig, size_t* pHeapSizeInBytes) -{ - ma_biquad_config bqConfig; - bqConfig = ma_lpf2__get_biquad_config(pConfig); - - return ma_biquad_get_heap_size(&bqConfig, pHeapSizeInBytes); -} - -MA_API ma_result ma_lpf2_init_preallocated(const ma_lpf2_config* pConfig, void* pHeap, ma_lpf2* pLPF) -{ - ma_result result; - ma_biquad_config bqConfig; - - if (pLPF == NULL) { - return MA_INVALID_ARGS; - } - - MA_ZERO_OBJECT(pLPF); - - if (pConfig == NULL) { - return MA_INVALID_ARGS; - } - - bqConfig = ma_lpf2__get_biquad_config(pConfig); - result = ma_biquad_init_preallocated(&bqConfig, pHeap, &pLPF->bq); - if (result != MA_SUCCESS) { - return result; - } - - return MA_SUCCESS; -} - -MA_API ma_result ma_lpf2_init(const ma_lpf2_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_lpf2* pLPF) -{ - ma_result result; - size_t heapSizeInBytes; - void* pHeap; - - result = ma_lpf2_get_heap_size(pConfig, &heapSizeInBytes); - if (result != MA_SUCCESS) { - return result; - } - - if (heapSizeInBytes > 0) { - pHeap = ma_malloc(heapSizeInBytes, pAllocationCallbacks); - if (pHeap == NULL) { - return MA_OUT_OF_MEMORY; - } - } else { - pHeap = NULL; - } - - result = ma_lpf2_init_preallocated(pConfig, pHeap, pLPF); - if (result != MA_SUCCESS) { - ma_free(pHeap, pAllocationCallbacks); - return result; - } - - pLPF->bq._ownsHeap = MA_TRUE; /* <-- This will cause the biquad to take ownership of the heap and free it when it's uninitialized. */ - return MA_SUCCESS; -} - -MA_API void ma_lpf2_uninit(ma_lpf2* pLPF, const ma_allocation_callbacks* pAllocationCallbacks) -{ - if (pLPF == NULL) { - return; - } - - ma_biquad_uninit(&pLPF->bq, pAllocationCallbacks); /* <-- This will free the heap allocation. */ -} - -MA_API ma_result ma_lpf2_reinit(const ma_lpf2_config* pConfig, ma_lpf2* pLPF) -{ - ma_result result; - ma_biquad_config bqConfig; - - if (pLPF == NULL || pConfig == NULL) { - return MA_INVALID_ARGS; - } - - bqConfig = ma_lpf2__get_biquad_config(pConfig); - result = ma_biquad_reinit(&bqConfig, &pLPF->bq); - if (result != MA_SUCCESS) { - return result; - } - - return MA_SUCCESS; -} - -MA_API ma_result ma_lpf2_clear_cache(ma_lpf2* pLPF) -{ - if (pLPF == NULL) { - return MA_INVALID_ARGS; - } - - ma_biquad_clear_cache(&pLPF->bq); - - return MA_SUCCESS; -} - -static MA_INLINE void ma_lpf2_process_pcm_frame_s16(ma_lpf2* pLPF, ma_int16* pFrameOut, const ma_int16* pFrameIn) -{ - ma_biquad_process_pcm_frame_s16(&pLPF->bq, pFrameOut, pFrameIn); -} - -static MA_INLINE void ma_lpf2_process_pcm_frame_f32(ma_lpf2* pLPF, float* pFrameOut, const float* pFrameIn) -{ - ma_biquad_process_pcm_frame_f32(&pLPF->bq, pFrameOut, pFrameIn); -} - -MA_API ma_result ma_lpf2_process_pcm_frames(ma_lpf2* pLPF, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount) -{ - if (pLPF == NULL) { - return MA_INVALID_ARGS; - } - - return ma_biquad_process_pcm_frames(&pLPF->bq, pFramesOut, pFramesIn, frameCount); -} - -MA_API ma_uint32 ma_lpf2_get_latency(const ma_lpf2* pLPF) -{ - if (pLPF == NULL) { - return 0; - } - - return ma_biquad_get_latency(&pLPF->bq); -} - - -MA_API ma_lpf_config ma_lpf_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRate, double cutoffFrequency, ma_uint32 order) -{ - ma_lpf_config config; - - MA_ZERO_OBJECT(&config); - config.format = format; - config.channels = channels; - config.sampleRate = sampleRate; - config.cutoffFrequency = cutoffFrequency; - config.order = ma_min(order, MA_MAX_FILTER_ORDER); - - return config; -} - - -typedef struct -{ - size_t sizeInBytes; - size_t lpf1Offset; - size_t lpf2Offset; /* Offset of the first second order filter. Subsequent filters will come straight after, and will each have the same heap size. */ -} ma_lpf_heap_layout; - -static void ma_lpf_calculate_sub_lpf_counts(ma_uint32 order, ma_uint32* pLPF1Count, ma_uint32* pLPF2Count) -{ - MA_ASSERT(pLPF1Count != NULL); - MA_ASSERT(pLPF2Count != NULL); - - *pLPF1Count = order % 2; - *pLPF2Count = order / 2; -} - -static ma_result ma_lpf_get_heap_layout(const ma_lpf_config* pConfig, ma_lpf_heap_layout* pHeapLayout) -{ - ma_result result; - ma_uint32 lpf1Count; - ma_uint32 lpf2Count; - ma_uint32 ilpf1; - ma_uint32 ilpf2; - - MA_ASSERT(pHeapLayout != NULL); - - MA_ZERO_OBJECT(pHeapLayout); - - if (pConfig == NULL) { - return MA_INVALID_ARGS; - } - - if (pConfig->channels == 0) { - return MA_INVALID_ARGS; - } - - if (pConfig->order > MA_MAX_FILTER_ORDER) { - return MA_INVALID_ARGS; - } - - ma_lpf_calculate_sub_lpf_counts(pConfig->order, &lpf1Count, &lpf2Count); - - pHeapLayout->sizeInBytes = 0; - - /* LPF 1 */ - pHeapLayout->lpf1Offset = pHeapLayout->sizeInBytes; - for (ilpf1 = 0; ilpf1 < lpf1Count; ilpf1 += 1) { - size_t lpf1HeapSizeInBytes; - ma_lpf1_config lpf1Config = ma_lpf1_config_init(pConfig->format, pConfig->channels, pConfig->sampleRate, pConfig->cutoffFrequency); - - result = ma_lpf1_get_heap_size(&lpf1Config, &lpf1HeapSizeInBytes); - if (result != MA_SUCCESS) { - return result; - } - - pHeapLayout->sizeInBytes += sizeof(ma_lpf1) + lpf1HeapSizeInBytes; - } - - /* LPF 2*/ - pHeapLayout->lpf2Offset = pHeapLayout->sizeInBytes; - for (ilpf2 = 0; ilpf2 < lpf2Count; ilpf2 += 1) { - size_t lpf2HeapSizeInBytes; - ma_lpf2_config lpf2Config = ma_lpf2_config_init(pConfig->format, pConfig->channels, pConfig->sampleRate, pConfig->cutoffFrequency, 0.707107); /* <-- The "q" parameter does not matter for the purpose of calculating the heap size. */ - - result = ma_lpf2_get_heap_size(&lpf2Config, &lpf2HeapSizeInBytes); - if (result != MA_SUCCESS) { - return result; - } - - pHeapLayout->sizeInBytes += sizeof(ma_lpf2) + lpf2HeapSizeInBytes; - } - - /* Make sure allocation size is aligned. */ - pHeapLayout->sizeInBytes = ma_align_64(pHeapLayout->sizeInBytes); - - return MA_SUCCESS; -} - -static ma_result ma_lpf_reinit__internal(const ma_lpf_config* pConfig, void* pHeap, ma_lpf* pLPF, ma_bool32 isNew) -{ - ma_result result; - ma_uint32 lpf1Count; - ma_uint32 lpf2Count; - ma_uint32 ilpf1; - ma_uint32 ilpf2; - ma_lpf_heap_layout heapLayout; /* Only used if isNew is true. */ - - if (pLPF == NULL || pConfig == NULL) { - return MA_INVALID_ARGS; - } - - /* Only supporting f32 and s16. */ - if (pConfig->format != ma_format_f32 && pConfig->format != ma_format_s16) { - return MA_INVALID_ARGS; - } - - /* The format cannot be changed after initialization. */ - if (pLPF->format != ma_format_unknown && pLPF->format != pConfig->format) { - return MA_INVALID_OPERATION; - } - - /* The channel count cannot be changed after initialization. */ - if (pLPF->channels != 0 && pLPF->channels != pConfig->channels) { - return MA_INVALID_OPERATION; - } - - if (pConfig->order > MA_MAX_FILTER_ORDER) { - return MA_INVALID_ARGS; - } - - ma_lpf_calculate_sub_lpf_counts(pConfig->order, &lpf1Count, &lpf2Count); - - /* The filter order can't change between reinits. */ - if (!isNew) { - if (pLPF->lpf1Count != lpf1Count || pLPF->lpf2Count != lpf2Count) { - return MA_INVALID_OPERATION; - } - } - - if (isNew) { - result = ma_lpf_get_heap_layout(pConfig, &heapLayout); - if (result != MA_SUCCESS) { - return result; - } - - pLPF->_pHeap = pHeap; - MA_ZERO_MEMORY(pHeap, heapLayout.sizeInBytes); - - pLPF->pLPF1 = (ma_lpf1*)ma_offset_ptr(pHeap, heapLayout.lpf1Offset); - pLPF->pLPF2 = (ma_lpf2*)ma_offset_ptr(pHeap, heapLayout.lpf2Offset); - } else { - MA_ZERO_OBJECT(&heapLayout); /* To silence a compiler warning. */ - } - - for (ilpf1 = 0; ilpf1 < lpf1Count; ilpf1 += 1) { - ma_lpf1_config lpf1Config = ma_lpf1_config_init(pConfig->format, pConfig->channels, pConfig->sampleRate, pConfig->cutoffFrequency); - - if (isNew) { - size_t lpf1HeapSizeInBytes; - - result = ma_lpf1_get_heap_size(&lpf1Config, &lpf1HeapSizeInBytes); - if (result == MA_SUCCESS) { - result = ma_lpf1_init_preallocated(&lpf1Config, ma_offset_ptr(pHeap, heapLayout.lpf1Offset + (sizeof(ma_lpf1) * lpf1Count) + (ilpf1 * lpf1HeapSizeInBytes)), &pLPF->pLPF1[ilpf1]); - } - } else { - result = ma_lpf1_reinit(&lpf1Config, &pLPF->pLPF1[ilpf1]); - } - - if (result != MA_SUCCESS) { - ma_uint32 jlpf1; - - for (jlpf1 = 0; jlpf1 < ilpf1; jlpf1 += 1) { - ma_lpf1_uninit(&pLPF->pLPF1[jlpf1], NULL); /* No need for allocation callbacks here since we used a preallocated heap allocation. */ - } - - return result; - } - } - - for (ilpf2 = 0; ilpf2 < lpf2Count; ilpf2 += 1) { - ma_lpf2_config lpf2Config; - double q; - double a; - - /* Tempting to use 0.707107, but won't result in a Butterworth filter if the order is > 2. */ - if (lpf1Count == 1) { - a = (1 + ilpf2*1) * (MA_PI_D/(pConfig->order*1)); /* Odd order. */ - } else { - a = (1 + ilpf2*2) * (MA_PI_D/(pConfig->order*2)); /* Even order. */ - } - q = 1 / (2*ma_cosd(a)); - - lpf2Config = ma_lpf2_config_init(pConfig->format, pConfig->channels, pConfig->sampleRate, pConfig->cutoffFrequency, q); - - if (isNew) { - size_t lpf2HeapSizeInBytes; - - result = ma_lpf2_get_heap_size(&lpf2Config, &lpf2HeapSizeInBytes); - if (result == MA_SUCCESS) { - result = ma_lpf2_init_preallocated(&lpf2Config, ma_offset_ptr(pHeap, heapLayout.lpf2Offset + (sizeof(ma_lpf2) * lpf2Count) + (ilpf2 * lpf2HeapSizeInBytes)), &pLPF->pLPF2[ilpf2]); - } - } else { - result = ma_lpf2_reinit(&lpf2Config, &pLPF->pLPF2[ilpf2]); - } - - if (result != MA_SUCCESS) { - ma_uint32 jlpf1; - ma_uint32 jlpf2; - - for (jlpf1 = 0; jlpf1 < lpf1Count; jlpf1 += 1) { - ma_lpf1_uninit(&pLPF->pLPF1[jlpf1], NULL); /* No need for allocation callbacks here since we used a preallocated heap allocation. */ - } - - for (jlpf2 = 0; jlpf2 < ilpf2; jlpf2 += 1) { - ma_lpf2_uninit(&pLPF->pLPF2[jlpf2], NULL); /* No need for allocation callbacks here since we used a preallocated heap allocation. */ - } - - return result; - } - } - - pLPF->lpf1Count = lpf1Count; - pLPF->lpf2Count = lpf2Count; - pLPF->format = pConfig->format; - pLPF->channels = pConfig->channels; - pLPF->sampleRate = pConfig->sampleRate; - - return MA_SUCCESS; -} - -MA_API ma_result ma_lpf_get_heap_size(const ma_lpf_config* pConfig, size_t* pHeapSizeInBytes) -{ - ma_result result; - ma_lpf_heap_layout heapLayout; - - if (pHeapSizeInBytes == NULL) { - return MA_INVALID_ARGS; - } - - *pHeapSizeInBytes = 0; - - result = ma_lpf_get_heap_layout(pConfig, &heapLayout); - if (result != MA_SUCCESS) { - return result; - } - - *pHeapSizeInBytes = heapLayout.sizeInBytes; - - return result; -} - -MA_API ma_result ma_lpf_init_preallocated(const ma_lpf_config* pConfig, void* pHeap, ma_lpf* pLPF) -{ - if (pLPF == NULL) { - return MA_INVALID_ARGS; - } - - MA_ZERO_OBJECT(pLPF); - - return ma_lpf_reinit__internal(pConfig, pHeap, pLPF, /*isNew*/MA_TRUE); -} - -MA_API ma_result ma_lpf_init(const ma_lpf_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_lpf* pLPF) -{ - ma_result result; - size_t heapSizeInBytes; - void* pHeap; - - result = ma_lpf_get_heap_size(pConfig, &heapSizeInBytes); - if (result != MA_SUCCESS) { - return result; - } - - if (heapSizeInBytes > 0) { - pHeap = ma_malloc(heapSizeInBytes, pAllocationCallbacks); - if (pHeap == NULL) { - return MA_OUT_OF_MEMORY; - } - } else { - pHeap = NULL; - } - - result = ma_lpf_init_preallocated(pConfig, pHeap, pLPF); - if (result != MA_SUCCESS) { - ma_free(pHeap, pAllocationCallbacks); - return result; - } - - pLPF->_ownsHeap = MA_TRUE; - return MA_SUCCESS; -} - -MA_API void ma_lpf_uninit(ma_lpf* pLPF, const ma_allocation_callbacks* pAllocationCallbacks) -{ - ma_uint32 ilpf1; - ma_uint32 ilpf2; - - if (pLPF == NULL) { - return; - } - - for (ilpf1 = 0; ilpf1 < pLPF->lpf1Count; ilpf1 += 1) { - ma_lpf1_uninit(&pLPF->pLPF1[ilpf1], pAllocationCallbacks); - } - - for (ilpf2 = 0; ilpf2 < pLPF->lpf2Count; ilpf2 += 1) { - ma_lpf2_uninit(&pLPF->pLPF2[ilpf2], pAllocationCallbacks); - } - - if (pLPF->_ownsHeap) { - ma_free(pLPF->_pHeap, pAllocationCallbacks); - } -} - -MA_API ma_result ma_lpf_reinit(const ma_lpf_config* pConfig, ma_lpf* pLPF) -{ - return ma_lpf_reinit__internal(pConfig, NULL, pLPF, /*isNew*/MA_FALSE); -} - -MA_API ma_result ma_lpf_clear_cache(ma_lpf* pLPF) -{ - ma_uint32 ilpf1; - ma_uint32 ilpf2; - - if (pLPF == NULL) { - return MA_INVALID_ARGS; - } - - for (ilpf1 = 0; ilpf1 < pLPF->lpf1Count; ilpf1 += 1) { - ma_lpf1_clear_cache(&pLPF->pLPF1[ilpf1]); - } - - for (ilpf2 = 0; ilpf2 < pLPF->lpf2Count; ilpf2 += 1) { - ma_lpf2_clear_cache(&pLPF->pLPF2[ilpf2]); - } - - return MA_SUCCESS; -} - -static MA_INLINE void ma_lpf_process_pcm_frame_f32(ma_lpf* pLPF, float* pY, const void* pX) -{ - ma_uint32 ilpf1; - ma_uint32 ilpf2; - - MA_ASSERT(pLPF->format == ma_format_f32); - - MA_MOVE_MEMORY(pY, pX, ma_get_bytes_per_frame(pLPF->format, pLPF->channels)); - - for (ilpf1 = 0; ilpf1 < pLPF->lpf1Count; ilpf1 += 1) { - ma_lpf1_process_pcm_frame_f32(&pLPF->pLPF1[ilpf1], pY, pY); - } - - for (ilpf2 = 0; ilpf2 < pLPF->lpf2Count; ilpf2 += 1) { - ma_lpf2_process_pcm_frame_f32(&pLPF->pLPF2[ilpf2], pY, pY); - } -} - -static MA_INLINE void ma_lpf_process_pcm_frame_s16(ma_lpf* pLPF, ma_int16* pY, const ma_int16* pX) -{ - ma_uint32 ilpf1; - ma_uint32 ilpf2; - - MA_ASSERT(pLPF->format == ma_format_s16); - - MA_MOVE_MEMORY(pY, pX, ma_get_bytes_per_frame(pLPF->format, pLPF->channels)); - - for (ilpf1 = 0; ilpf1 < pLPF->lpf1Count; ilpf1 += 1) { - ma_lpf1_process_pcm_frame_s16(&pLPF->pLPF1[ilpf1], pY, pY); - } - - for (ilpf2 = 0; ilpf2 < pLPF->lpf2Count; ilpf2 += 1) { - ma_lpf2_process_pcm_frame_s16(&pLPF->pLPF2[ilpf2], pY, pY); - } -} - -MA_API ma_result ma_lpf_process_pcm_frames(ma_lpf* pLPF, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount) -{ - ma_result result; - ma_uint32 ilpf1; - ma_uint32 ilpf2; - - if (pLPF == NULL) { - return MA_INVALID_ARGS; - } - - /* Faster path for in-place. */ - if (pFramesOut == pFramesIn) { - for (ilpf1 = 0; ilpf1 < pLPF->lpf1Count; ilpf1 += 1) { - result = ma_lpf1_process_pcm_frames(&pLPF->pLPF1[ilpf1], pFramesOut, pFramesOut, frameCount); - if (result != MA_SUCCESS) { - return result; - } - } - - for (ilpf2 = 0; ilpf2 < pLPF->lpf2Count; ilpf2 += 1) { - result = ma_lpf2_process_pcm_frames(&pLPF->pLPF2[ilpf2], pFramesOut, pFramesOut, frameCount); - if (result != MA_SUCCESS) { - return result; - } - } - } - - /* Slightly slower path for copying. */ - if (pFramesOut != pFramesIn) { - ma_uint32 iFrame; - - /* */ if (pLPF->format == ma_format_f32) { - /* */ float* pFramesOutF32 = ( float*)pFramesOut; - const float* pFramesInF32 = (const float*)pFramesIn; - - for (iFrame = 0; iFrame < frameCount; iFrame += 1) { - ma_lpf_process_pcm_frame_f32(pLPF, pFramesOutF32, pFramesInF32); - pFramesOutF32 += pLPF->channels; - pFramesInF32 += pLPF->channels; - } - } else if (pLPF->format == ma_format_s16) { - /* */ ma_int16* pFramesOutS16 = ( ma_int16*)pFramesOut; - const ma_int16* pFramesInS16 = (const ma_int16*)pFramesIn; - - for (iFrame = 0; iFrame < frameCount; iFrame += 1) { - ma_lpf_process_pcm_frame_s16(pLPF, pFramesOutS16, pFramesInS16); - pFramesOutS16 += pLPF->channels; - pFramesInS16 += pLPF->channels; - } - } else { - MA_ASSERT(MA_FALSE); - return MA_INVALID_OPERATION; /* Should never hit this. */ - } - } - - return MA_SUCCESS; -} - -MA_API ma_uint32 ma_lpf_get_latency(const ma_lpf* pLPF) -{ - if (pLPF == NULL) { - return 0; - } - - return pLPF->lpf2Count*2 + pLPF->lpf1Count; -} - - -/************************************************************************************************************************************************************** - -High-Pass Filtering - -**************************************************************************************************************************************************************/ -MA_API ma_hpf1_config ma_hpf1_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRate, double cutoffFrequency) -{ - ma_hpf1_config config; - - MA_ZERO_OBJECT(&config); - config.format = format; - config.channels = channels; - config.sampleRate = sampleRate; - config.cutoffFrequency = cutoffFrequency; - - return config; -} - -MA_API ma_hpf2_config ma_hpf2_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRate, double cutoffFrequency, double q) -{ - ma_hpf2_config config; - - MA_ZERO_OBJECT(&config); - config.format = format; - config.channels = channels; - config.sampleRate = sampleRate; - config.cutoffFrequency = cutoffFrequency; - config.q = q; - - /* Q cannot be 0 or else it'll result in a division by 0. In this case just default to 0.707107. */ - if (config.q == 0) { - config.q = 0.707107; - } - - return config; -} - - -typedef struct -{ - size_t sizeInBytes; - size_t r1Offset; -} ma_hpf1_heap_layout; - -static ma_result ma_hpf1_get_heap_layout(const ma_hpf1_config* pConfig, ma_hpf1_heap_layout* pHeapLayout) -{ - MA_ASSERT(pHeapLayout != NULL); - - MA_ZERO_OBJECT(pHeapLayout); - - if (pConfig == NULL) { - return MA_INVALID_ARGS; - } - - if (pConfig->channels == 0) { - return MA_INVALID_ARGS; - } - - pHeapLayout->sizeInBytes = 0; - - /* R1 */ - pHeapLayout->r1Offset = pHeapLayout->sizeInBytes; - pHeapLayout->sizeInBytes += sizeof(ma_biquad_coefficient) * pConfig->channels; - - /* Make sure allocation size is aligned. */ - pHeapLayout->sizeInBytes = ma_align_64(pHeapLayout->sizeInBytes); - - return MA_SUCCESS; -} - -MA_API ma_result ma_hpf1_get_heap_size(const ma_hpf1_config* pConfig, size_t* pHeapSizeInBytes) -{ - ma_result result; - ma_hpf1_heap_layout heapLayout; - - if (pHeapSizeInBytes == NULL) { - return MA_INVALID_ARGS; - } - - result = ma_hpf1_get_heap_layout(pConfig, &heapLayout); - if (result != MA_SUCCESS) { - return result; - } - - *pHeapSizeInBytes = heapLayout.sizeInBytes; - - return MA_SUCCESS; -} - -MA_API ma_result ma_hpf1_init_preallocated(const ma_hpf1_config* pConfig, void* pHeap, ma_hpf1* pLPF) -{ - ma_result result; - ma_hpf1_heap_layout heapLayout; - - if (pLPF == NULL) { - return MA_INVALID_ARGS; - } - - MA_ZERO_OBJECT(pLPF); - - result = ma_hpf1_get_heap_layout(pConfig, &heapLayout); - if (result != MA_SUCCESS) { - return result; - } - - pLPF->_pHeap = pHeap; - MA_ZERO_MEMORY(pHeap, heapLayout.sizeInBytes); - - pLPF->pR1 = (ma_biquad_coefficient*)ma_offset_ptr(pHeap, heapLayout.r1Offset); - - return ma_hpf1_reinit(pConfig, pLPF); -} - -MA_API ma_result ma_hpf1_init(const ma_hpf1_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_hpf1* pLPF) -{ - ma_result result; - size_t heapSizeInBytes; - void* pHeap; - - result = ma_hpf1_get_heap_size(pConfig, &heapSizeInBytes); - if (result != MA_SUCCESS) { - return result; - } - - if (heapSizeInBytes > 0) { - pHeap = ma_malloc(heapSizeInBytes, pAllocationCallbacks); - if (pHeap == NULL) { - return MA_OUT_OF_MEMORY; - } - } else { - pHeap = NULL; - } - - result = ma_hpf1_init_preallocated(pConfig, pHeap, pLPF); - if (result != MA_SUCCESS) { - ma_free(pHeap, pAllocationCallbacks); - return result; - } - - pLPF->_ownsHeap = MA_TRUE; - return MA_SUCCESS; -} - -MA_API void ma_hpf1_uninit(ma_hpf1* pHPF, const ma_allocation_callbacks* pAllocationCallbacks) -{ - if (pHPF == NULL) { - return; - } - - if (pHPF->_ownsHeap) { - ma_free(pHPF->_pHeap, pAllocationCallbacks); - } -} - -MA_API ma_result ma_hpf1_reinit(const ma_hpf1_config* pConfig, ma_hpf1* pHPF) -{ - double a; - - if (pHPF == NULL || pConfig == NULL) { - return MA_INVALID_ARGS; - } - - /* Only supporting f32 and s16. */ - if (pConfig->format != ma_format_f32 && pConfig->format != ma_format_s16) { - return MA_INVALID_ARGS; - } - - /* The format cannot be changed after initialization. */ - if (pHPF->format != ma_format_unknown && pHPF->format != pConfig->format) { - return MA_INVALID_OPERATION; - } - - /* The channel count cannot be changed after initialization. */ - if (pHPF->channels != 0 && pHPF->channels != pConfig->channels) { - return MA_INVALID_OPERATION; - } - - pHPF->format = pConfig->format; - pHPF->channels = pConfig->channels; - - a = ma_expd(-2 * MA_PI_D * pConfig->cutoffFrequency / pConfig->sampleRate); - if (pConfig->format == ma_format_f32) { - pHPF->a.f32 = (float)a; - } else { - pHPF->a.s32 = ma_biquad_float_to_fp(a); - } - - return MA_SUCCESS; -} - -static MA_INLINE void ma_hpf1_process_pcm_frame_f32(ma_hpf1* pHPF, float* pY, const float* pX) -{ - ma_uint32 c; - const ma_uint32 channels = pHPF->channels; - const float a = 1 - pHPF->a.f32; - const float b = 1 - a; - - MA_ASSUME(channels > 0); - for (c = 0; c < channels; c += 1) { - float r1 = pHPF->pR1[c].f32; - float x = pX[c]; - float y; - - y = b*x - a*r1; - - pY[c] = y; - pHPF->pR1[c].f32 = y; - } -} - -static MA_INLINE void ma_hpf1_process_pcm_frame_s16(ma_hpf1* pHPF, ma_int16* pY, const ma_int16* pX) -{ - ma_uint32 c; - const ma_uint32 channels = pHPF->channels; - const ma_int32 a = ((1 << MA_BIQUAD_FIXED_POINT_SHIFT) - pHPF->a.s32); - const ma_int32 b = ((1 << MA_BIQUAD_FIXED_POINT_SHIFT) - a); - - MA_ASSUME(channels > 0); - for (c = 0; c < channels; c += 1) { - ma_int32 r1 = pHPF->pR1[c].s32; - ma_int32 x = pX[c]; - ma_int32 y; - - y = (b*x - a*r1) >> MA_BIQUAD_FIXED_POINT_SHIFT; - - pY[c] = (ma_int16)y; - pHPF->pR1[c].s32 = (ma_int32)y; - } -} - -MA_API ma_result ma_hpf1_process_pcm_frames(ma_hpf1* pHPF, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount) -{ - ma_uint32 n; - - if (pHPF == NULL || pFramesOut == NULL || pFramesIn == NULL) { - return MA_INVALID_ARGS; - } - - /* Note that the logic below needs to support in-place filtering. That is, it must support the case where pFramesOut and pFramesIn are the same. */ - - if (pHPF->format == ma_format_f32) { - /* */ float* pY = ( float*)pFramesOut; - const float* pX = (const float*)pFramesIn; - - for (n = 0; n < frameCount; n += 1) { - ma_hpf1_process_pcm_frame_f32(pHPF, pY, pX); - pY += pHPF->channels; - pX += pHPF->channels; - } - } else if (pHPF->format == ma_format_s16) { - /* */ ma_int16* pY = ( ma_int16*)pFramesOut; - const ma_int16* pX = (const ma_int16*)pFramesIn; - - for (n = 0; n < frameCount; n += 1) { - ma_hpf1_process_pcm_frame_s16(pHPF, pY, pX); - pY += pHPF->channels; - pX += pHPF->channels; - } - } else { - MA_ASSERT(MA_FALSE); - return MA_INVALID_ARGS; /* Format not supported. Should never hit this because it's checked in ma_biquad_init() and ma_biquad_reinit(). */ - } - - return MA_SUCCESS; -} - -MA_API ma_uint32 ma_hpf1_get_latency(const ma_hpf1* pHPF) -{ - if (pHPF == NULL) { - return 0; - } - - return 1; -} - - -static MA_INLINE ma_biquad_config ma_hpf2__get_biquad_config(const ma_hpf2_config* pConfig) -{ - ma_biquad_config bqConfig; - double q; - double w; - double s; - double c; - double a; - - MA_ASSERT(pConfig != NULL); - - q = pConfig->q; - w = 2 * MA_PI_D * pConfig->cutoffFrequency / pConfig->sampleRate; - s = ma_sind(w); - c = ma_cosd(w); - a = s / (2*q); - - bqConfig.b0 = (1 + c) / 2; - bqConfig.b1 = -(1 + c); - bqConfig.b2 = (1 + c) / 2; - bqConfig.a0 = 1 + a; - bqConfig.a1 = -2 * c; - bqConfig.a2 = 1 - a; - - bqConfig.format = pConfig->format; - bqConfig.channels = pConfig->channels; - - return bqConfig; -} - -MA_API ma_result ma_hpf2_get_heap_size(const ma_hpf2_config* pConfig, size_t* pHeapSizeInBytes) -{ - ma_biquad_config bqConfig; - bqConfig = ma_hpf2__get_biquad_config(pConfig); - - return ma_biquad_get_heap_size(&bqConfig, pHeapSizeInBytes); -} - -MA_API ma_result ma_hpf2_init_preallocated(const ma_hpf2_config* pConfig, void* pHeap, ma_hpf2* pHPF) -{ - ma_result result; - ma_biquad_config bqConfig; - - if (pHPF == NULL) { - return MA_INVALID_ARGS; - } - - MA_ZERO_OBJECT(pHPF); - - if (pConfig == NULL) { - return MA_INVALID_ARGS; - } - - bqConfig = ma_hpf2__get_biquad_config(pConfig); - result = ma_biquad_init_preallocated(&bqConfig, pHeap, &pHPF->bq); - if (result != MA_SUCCESS) { - return result; - } - - return MA_SUCCESS; -} - -MA_API ma_result ma_hpf2_init(const ma_hpf2_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_hpf2* pHPF) -{ - ma_result result; - size_t heapSizeInBytes; - void* pHeap; - - result = ma_hpf2_get_heap_size(pConfig, &heapSizeInBytes); - if (result != MA_SUCCESS) { - return result; - } - - if (heapSizeInBytes > 0) { - pHeap = ma_malloc(heapSizeInBytes, pAllocationCallbacks); - if (pHeap == NULL) { - return MA_OUT_OF_MEMORY; - } - } else { - pHeap = NULL; - } - - result = ma_hpf2_init_preallocated(pConfig, pHeap, pHPF); - if (result != MA_SUCCESS) { - ma_free(pHeap, pAllocationCallbacks); - return result; - } - - pHPF->bq._ownsHeap = MA_TRUE; /* <-- This will cause the biquad to take ownership of the heap and free it when it's uninitialized. */ - return MA_SUCCESS; -} - -MA_API void ma_hpf2_uninit(ma_hpf2* pHPF, const ma_allocation_callbacks* pAllocationCallbacks) -{ - if (pHPF == NULL) { - return; - } - - ma_biquad_uninit(&pHPF->bq, pAllocationCallbacks); /* <-- This will free the heap allocation. */ -} - -MA_API ma_result ma_hpf2_reinit(const ma_hpf2_config* pConfig, ma_hpf2* pHPF) -{ - ma_result result; - ma_biquad_config bqConfig; - - if (pHPF == NULL || pConfig == NULL) { - return MA_INVALID_ARGS; - } - - bqConfig = ma_hpf2__get_biquad_config(pConfig); - result = ma_biquad_reinit(&bqConfig, &pHPF->bq); - if (result != MA_SUCCESS) { - return result; - } - - return MA_SUCCESS; -} - -static MA_INLINE void ma_hpf2_process_pcm_frame_s16(ma_hpf2* pHPF, ma_int16* pFrameOut, const ma_int16* pFrameIn) -{ - ma_biquad_process_pcm_frame_s16(&pHPF->bq, pFrameOut, pFrameIn); -} - -static MA_INLINE void ma_hpf2_process_pcm_frame_f32(ma_hpf2* pHPF, float* pFrameOut, const float* pFrameIn) -{ - ma_biquad_process_pcm_frame_f32(&pHPF->bq, pFrameOut, pFrameIn); -} - -MA_API ma_result ma_hpf2_process_pcm_frames(ma_hpf2* pHPF, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount) -{ - if (pHPF == NULL) { - return MA_INVALID_ARGS; - } - - return ma_biquad_process_pcm_frames(&pHPF->bq, pFramesOut, pFramesIn, frameCount); -} - -MA_API ma_uint32 ma_hpf2_get_latency(const ma_hpf2* pHPF) -{ - if (pHPF == NULL) { - return 0; - } - - return ma_biquad_get_latency(&pHPF->bq); -} - - -MA_API ma_hpf_config ma_hpf_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRate, double cutoffFrequency, ma_uint32 order) -{ - ma_hpf_config config; - - MA_ZERO_OBJECT(&config); - config.format = format; - config.channels = channels; - config.sampleRate = sampleRate; - config.cutoffFrequency = cutoffFrequency; - config.order = ma_min(order, MA_MAX_FILTER_ORDER); - - return config; -} - - -typedef struct -{ - size_t sizeInBytes; - size_t hpf1Offset; - size_t hpf2Offset; /* Offset of the first second order filter. Subsequent filters will come straight after, and will each have the same heap size. */ -} ma_hpf_heap_layout; - -static void ma_hpf_calculate_sub_hpf_counts(ma_uint32 order, ma_uint32* pHPF1Count, ma_uint32* pHPF2Count) -{ - MA_ASSERT(pHPF1Count != NULL); - MA_ASSERT(pHPF2Count != NULL); - - *pHPF1Count = order % 2; - *pHPF2Count = order / 2; -} - -static ma_result ma_hpf_get_heap_layout(const ma_hpf_config* pConfig, ma_hpf_heap_layout* pHeapLayout) -{ - ma_result result; - ma_uint32 hpf1Count; - ma_uint32 hpf2Count; - ma_uint32 ihpf1; - ma_uint32 ihpf2; - - MA_ASSERT(pHeapLayout != NULL); - - MA_ZERO_OBJECT(pHeapLayout); - - if (pConfig == NULL) { - return MA_INVALID_ARGS; - } - - if (pConfig->channels == 0) { - return MA_INVALID_ARGS; - } - - if (pConfig->order > MA_MAX_FILTER_ORDER) { - return MA_INVALID_ARGS; - } - - ma_hpf_calculate_sub_hpf_counts(pConfig->order, &hpf1Count, &hpf2Count); - - pHeapLayout->sizeInBytes = 0; - - /* HPF 1 */ - pHeapLayout->hpf1Offset = pHeapLayout->sizeInBytes; - for (ihpf1 = 0; ihpf1 < hpf1Count; ihpf1 += 1) { - size_t hpf1HeapSizeInBytes; - ma_hpf1_config hpf1Config = ma_hpf1_config_init(pConfig->format, pConfig->channels, pConfig->sampleRate, pConfig->cutoffFrequency); - - result = ma_hpf1_get_heap_size(&hpf1Config, &hpf1HeapSizeInBytes); - if (result != MA_SUCCESS) { - return result; - } - - pHeapLayout->sizeInBytes += sizeof(ma_hpf1) + hpf1HeapSizeInBytes; - } - - /* HPF 2*/ - pHeapLayout->hpf2Offset = pHeapLayout->sizeInBytes; - for (ihpf2 = 0; ihpf2 < hpf2Count; ihpf2 += 1) { - size_t hpf2HeapSizeInBytes; - ma_hpf2_config hpf2Config = ma_hpf2_config_init(pConfig->format, pConfig->channels, pConfig->sampleRate, pConfig->cutoffFrequency, 0.707107); /* <-- The "q" parameter does not matter for the purpose of calculating the heap size. */ - - result = ma_hpf2_get_heap_size(&hpf2Config, &hpf2HeapSizeInBytes); - if (result != MA_SUCCESS) { - return result; - } - - pHeapLayout->sizeInBytes += sizeof(ma_hpf2) + hpf2HeapSizeInBytes; - } - - /* Make sure allocation size is aligned. */ - pHeapLayout->sizeInBytes = ma_align_64(pHeapLayout->sizeInBytes); - - return MA_SUCCESS; -} - -static ma_result ma_hpf_reinit__internal(const ma_hpf_config* pConfig, void* pHeap, ma_hpf* pHPF, ma_bool32 isNew) -{ - ma_result result; - ma_uint32 hpf1Count; - ma_uint32 hpf2Count; - ma_uint32 ihpf1; - ma_uint32 ihpf2; - ma_hpf_heap_layout heapLayout; /* Only used if isNew is true. */ - - if (pHPF == NULL || pConfig == NULL) { - return MA_INVALID_ARGS; - } - - /* Only supporting f32 and s16. */ - if (pConfig->format != ma_format_f32 && pConfig->format != ma_format_s16) { - return MA_INVALID_ARGS; - } - - /* The format cannot be changed after initialization. */ - if (pHPF->format != ma_format_unknown && pHPF->format != pConfig->format) { - return MA_INVALID_OPERATION; - } - - /* The channel count cannot be changed after initialization. */ - if (pHPF->channels != 0 && pHPF->channels != pConfig->channels) { - return MA_INVALID_OPERATION; - } - - if (pConfig->order > MA_MAX_FILTER_ORDER) { - return MA_INVALID_ARGS; - } - - ma_hpf_calculate_sub_hpf_counts(pConfig->order, &hpf1Count, &hpf2Count); - - /* The filter order can't change between reinits. */ - if (!isNew) { - if (pHPF->hpf1Count != hpf1Count || pHPF->hpf2Count != hpf2Count) { - return MA_INVALID_OPERATION; - } - } - - if (isNew) { - result = ma_hpf_get_heap_layout(pConfig, &heapLayout); - if (result != MA_SUCCESS) { - return result; - } - - pHPF->_pHeap = pHeap; - MA_ZERO_MEMORY(pHeap, heapLayout.sizeInBytes); - - pHPF->pHPF1 = (ma_hpf1*)ma_offset_ptr(pHeap, heapLayout.hpf1Offset); - pHPF->pHPF2 = (ma_hpf2*)ma_offset_ptr(pHeap, heapLayout.hpf2Offset); - } else { - MA_ZERO_OBJECT(&heapLayout); /* To silence a compiler warning. */ - } - - for (ihpf1 = 0; ihpf1 < hpf1Count; ihpf1 += 1) { - ma_hpf1_config hpf1Config = ma_hpf1_config_init(pConfig->format, pConfig->channels, pConfig->sampleRate, pConfig->cutoffFrequency); - - if (isNew) { - size_t hpf1HeapSizeInBytes; - - result = ma_hpf1_get_heap_size(&hpf1Config, &hpf1HeapSizeInBytes); - if (result == MA_SUCCESS) { - result = ma_hpf1_init_preallocated(&hpf1Config, ma_offset_ptr(pHeap, heapLayout.hpf1Offset + (sizeof(ma_hpf1) * hpf1Count) + (ihpf1 * hpf1HeapSizeInBytes)), &pHPF->pHPF1[ihpf1]); - } - } else { - result = ma_hpf1_reinit(&hpf1Config, &pHPF->pHPF1[ihpf1]); - } - - if (result != MA_SUCCESS) { - ma_uint32 jhpf1; - - for (jhpf1 = 0; jhpf1 < ihpf1; jhpf1 += 1) { - ma_hpf1_uninit(&pHPF->pHPF1[jhpf1], NULL); /* No need for allocation callbacks here since we used a preallocated heap allocation. */ - } - - return result; - } - } - - for (ihpf2 = 0; ihpf2 < hpf2Count; ihpf2 += 1) { - ma_hpf2_config hpf2Config; - double q; - double a; - - /* Tempting to use 0.707107, but won't result in a Butterworth filter if the order is > 2. */ - if (hpf1Count == 1) { - a = (1 + ihpf2*1) * (MA_PI_D/(pConfig->order*1)); /* Odd order. */ - } else { - a = (1 + ihpf2*2) * (MA_PI_D/(pConfig->order*2)); /* Even order. */ - } - q = 1 / (2*ma_cosd(a)); - - hpf2Config = ma_hpf2_config_init(pConfig->format, pConfig->channels, pConfig->sampleRate, pConfig->cutoffFrequency, q); - - if (isNew) { - size_t hpf2HeapSizeInBytes; - - result = ma_hpf2_get_heap_size(&hpf2Config, &hpf2HeapSizeInBytes); - if (result == MA_SUCCESS) { - result = ma_hpf2_init_preallocated(&hpf2Config, ma_offset_ptr(pHeap, heapLayout.hpf2Offset + (sizeof(ma_hpf2) * hpf2Count) + (ihpf2 * hpf2HeapSizeInBytes)), &pHPF->pHPF2[ihpf2]); - } - } else { - result = ma_hpf2_reinit(&hpf2Config, &pHPF->pHPF2[ihpf2]); - } - - if (result != MA_SUCCESS) { - ma_uint32 jhpf1; - ma_uint32 jhpf2; - - for (jhpf1 = 0; jhpf1 < hpf1Count; jhpf1 += 1) { - ma_hpf1_uninit(&pHPF->pHPF1[jhpf1], NULL); /* No need for allocation callbacks here since we used a preallocated heap allocation. */ - } - - for (jhpf2 = 0; jhpf2 < ihpf2; jhpf2 += 1) { - ma_hpf2_uninit(&pHPF->pHPF2[jhpf2], NULL); /* No need for allocation callbacks here since we used a preallocated heap allocation. */ - } - - return result; - } - } - - pHPF->hpf1Count = hpf1Count; - pHPF->hpf2Count = hpf2Count; - pHPF->format = pConfig->format; - pHPF->channels = pConfig->channels; - pHPF->sampleRate = pConfig->sampleRate; - - return MA_SUCCESS; -} - -MA_API ma_result ma_hpf_get_heap_size(const ma_hpf_config* pConfig, size_t* pHeapSizeInBytes) -{ - ma_result result; - ma_hpf_heap_layout heapLayout; - - if (pHeapSizeInBytes == NULL) { - return MA_INVALID_ARGS; - } - - *pHeapSizeInBytes = 0; - - result = ma_hpf_get_heap_layout(pConfig, &heapLayout); - if (result != MA_SUCCESS) { - return result; - } - - *pHeapSizeInBytes = heapLayout.sizeInBytes; - - return result; -} - -MA_API ma_result ma_hpf_init_preallocated(const ma_hpf_config* pConfig, void* pHeap, ma_hpf* pLPF) -{ - if (pLPF == NULL) { - return MA_INVALID_ARGS; - } - - MA_ZERO_OBJECT(pLPF); - - return ma_hpf_reinit__internal(pConfig, pHeap, pLPF, /*isNew*/MA_TRUE); -} - -MA_API ma_result ma_hpf_init(const ma_hpf_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_hpf* pHPF) -{ - ma_result result; - size_t heapSizeInBytes; - void* pHeap; - - result = ma_hpf_get_heap_size(pConfig, &heapSizeInBytes); - if (result != MA_SUCCESS) { - return result; - } - - if (heapSizeInBytes > 0) { - pHeap = ma_malloc(heapSizeInBytes, pAllocationCallbacks); - if (pHeap == NULL) { - return MA_OUT_OF_MEMORY; - } - } else { - pHeap = NULL; - } - - result = ma_hpf_init_preallocated(pConfig, pHeap, pHPF); - if (result != MA_SUCCESS) { - ma_free(pHeap, pAllocationCallbacks); - return result; - } - - pHPF->_ownsHeap = MA_TRUE; - return MA_SUCCESS; -} - -MA_API void ma_hpf_uninit(ma_hpf* pHPF, const ma_allocation_callbacks* pAllocationCallbacks) -{ - ma_uint32 ihpf1; - ma_uint32 ihpf2; - - if (pHPF == NULL) { - return; - } - - for (ihpf1 = 0; ihpf1 < pHPF->hpf1Count; ihpf1 += 1) { - ma_hpf1_uninit(&pHPF->pHPF1[ihpf1], pAllocationCallbacks); - } - - for (ihpf2 = 0; ihpf2 < pHPF->hpf2Count; ihpf2 += 1) { - ma_hpf2_uninit(&pHPF->pHPF2[ihpf2], pAllocationCallbacks); - } - - if (pHPF->_ownsHeap) { - ma_free(pHPF->_pHeap, pAllocationCallbacks); - } -} - -MA_API ma_result ma_hpf_reinit(const ma_hpf_config* pConfig, ma_hpf* pHPF) -{ - return ma_hpf_reinit__internal(pConfig, NULL, pHPF, /*isNew*/MA_FALSE); -} - -MA_API ma_result ma_hpf_process_pcm_frames(ma_hpf* pHPF, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount) -{ - ma_result result; - ma_uint32 ihpf1; - ma_uint32 ihpf2; - - if (pHPF == NULL) { - return MA_INVALID_ARGS; - } - - /* Faster path for in-place. */ - if (pFramesOut == pFramesIn) { - for (ihpf1 = 0; ihpf1 < pHPF->hpf1Count; ihpf1 += 1) { - result = ma_hpf1_process_pcm_frames(&pHPF->pHPF1[ihpf1], pFramesOut, pFramesOut, frameCount); - if (result != MA_SUCCESS) { - return result; - } - } - - for (ihpf2 = 0; ihpf2 < pHPF->hpf2Count; ihpf2 += 1) { - result = ma_hpf2_process_pcm_frames(&pHPF->pHPF2[ihpf2], pFramesOut, pFramesOut, frameCount); - if (result != MA_SUCCESS) { - return result; - } - } - } - - /* Slightly slower path for copying. */ - if (pFramesOut != pFramesIn) { - ma_uint32 iFrame; - - /* */ if (pHPF->format == ma_format_f32) { - /* */ float* pFramesOutF32 = ( float*)pFramesOut; - const float* pFramesInF32 = (const float*)pFramesIn; - - for (iFrame = 0; iFrame < frameCount; iFrame += 1) { - MA_COPY_MEMORY(pFramesOutF32, pFramesInF32, ma_get_bytes_per_frame(pHPF->format, pHPF->channels)); - - for (ihpf1 = 0; ihpf1 < pHPF->hpf1Count; ihpf1 += 1) { - ma_hpf1_process_pcm_frame_f32(&pHPF->pHPF1[ihpf1], pFramesOutF32, pFramesOutF32); - } - - for (ihpf2 = 0; ihpf2 < pHPF->hpf2Count; ihpf2 += 1) { - ma_hpf2_process_pcm_frame_f32(&pHPF->pHPF2[ihpf2], pFramesOutF32, pFramesOutF32); - } - - pFramesOutF32 += pHPF->channels; - pFramesInF32 += pHPF->channels; - } - } else if (pHPF->format == ma_format_s16) { - /* */ ma_int16* pFramesOutS16 = ( ma_int16*)pFramesOut; - const ma_int16* pFramesInS16 = (const ma_int16*)pFramesIn; - - for (iFrame = 0; iFrame < frameCount; iFrame += 1) { - MA_COPY_MEMORY(pFramesOutS16, pFramesInS16, ma_get_bytes_per_frame(pHPF->format, pHPF->channels)); - - for (ihpf1 = 0; ihpf1 < pHPF->hpf1Count; ihpf1 += 1) { - ma_hpf1_process_pcm_frame_s16(&pHPF->pHPF1[ihpf1], pFramesOutS16, pFramesOutS16); - } - - for (ihpf2 = 0; ihpf2 < pHPF->hpf2Count; ihpf2 += 1) { - ma_hpf2_process_pcm_frame_s16(&pHPF->pHPF2[ihpf2], pFramesOutS16, pFramesOutS16); - } - - pFramesOutS16 += pHPF->channels; - pFramesInS16 += pHPF->channels; - } - } else { - MA_ASSERT(MA_FALSE); - return MA_INVALID_OPERATION; /* Should never hit this. */ - } - } - - return MA_SUCCESS; -} - -MA_API ma_uint32 ma_hpf_get_latency(const ma_hpf* pHPF) -{ - if (pHPF == NULL) { - return 0; - } - - return pHPF->hpf2Count*2 + pHPF->hpf1Count; -} - - -/************************************************************************************************************************************************************** - -Band-Pass Filtering - -**************************************************************************************************************************************************************/ -MA_API ma_bpf2_config ma_bpf2_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRate, double cutoffFrequency, double q) -{ - ma_bpf2_config config; - - MA_ZERO_OBJECT(&config); - config.format = format; - config.channels = channels; - config.sampleRate = sampleRate; - config.cutoffFrequency = cutoffFrequency; - config.q = q; - - /* Q cannot be 0 or else it'll result in a division by 0. In this case just default to 0.707107. */ - if (config.q == 0) { - config.q = 0.707107; - } - - return config; -} - - -static MA_INLINE ma_biquad_config ma_bpf2__get_biquad_config(const ma_bpf2_config* pConfig) -{ - ma_biquad_config bqConfig; - double q; - double w; - double s; - double c; - double a; - - MA_ASSERT(pConfig != NULL); - - q = pConfig->q; - w = 2 * MA_PI_D * pConfig->cutoffFrequency / pConfig->sampleRate; - s = ma_sind(w); - c = ma_cosd(w); - a = s / (2*q); - - bqConfig.b0 = q * a; - bqConfig.b1 = 0; - bqConfig.b2 = -q * a; - bqConfig.a0 = 1 + a; - bqConfig.a1 = -2 * c; - bqConfig.a2 = 1 - a; - - bqConfig.format = pConfig->format; - bqConfig.channels = pConfig->channels; - - return bqConfig; -} - -MA_API ma_result ma_bpf2_get_heap_size(const ma_bpf2_config* pConfig, size_t* pHeapSizeInBytes) -{ - ma_biquad_config bqConfig; - bqConfig = ma_bpf2__get_biquad_config(pConfig); - - return ma_biquad_get_heap_size(&bqConfig, pHeapSizeInBytes); -} - -MA_API ma_result ma_bpf2_init_preallocated(const ma_bpf2_config* pConfig, void* pHeap, ma_bpf2* pBPF) -{ - ma_result result; - ma_biquad_config bqConfig; - - if (pBPF == NULL) { - return MA_INVALID_ARGS; - } - - MA_ZERO_OBJECT(pBPF); - - if (pConfig == NULL) { - return MA_INVALID_ARGS; - } - - bqConfig = ma_bpf2__get_biquad_config(pConfig); - result = ma_biquad_init_preallocated(&bqConfig, pHeap, &pBPF->bq); - if (result != MA_SUCCESS) { - return result; - } - - return MA_SUCCESS; -} - -MA_API ma_result ma_bpf2_init(const ma_bpf2_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_bpf2* pBPF) -{ - ma_result result; - size_t heapSizeInBytes; - void* pHeap; - - result = ma_bpf2_get_heap_size(pConfig, &heapSizeInBytes); - if (result != MA_SUCCESS) { - return result; - } - - if (heapSizeInBytes > 0) { - pHeap = ma_malloc(heapSizeInBytes, pAllocationCallbacks); - if (pHeap == NULL) { - return MA_OUT_OF_MEMORY; - } - } else { - pHeap = NULL; - } - - result = ma_bpf2_init_preallocated(pConfig, pHeap, pBPF); - if (result != MA_SUCCESS) { - ma_free(pHeap, pAllocationCallbacks); - return result; - } - - pBPF->bq._ownsHeap = MA_TRUE; /* <-- This will cause the biquad to take ownership of the heap and free it when it's uninitialized. */ - return MA_SUCCESS; -} - -MA_API void ma_bpf2_uninit(ma_bpf2* pBPF, const ma_allocation_callbacks* pAllocationCallbacks) -{ - if (pBPF == NULL) { - return; - } - - ma_biquad_uninit(&pBPF->bq, pAllocationCallbacks); /* <-- This will free the heap allocation. */ -} - -MA_API ma_result ma_bpf2_reinit(const ma_bpf2_config* pConfig, ma_bpf2* pBPF) -{ - ma_result result; - ma_biquad_config bqConfig; - - if (pBPF == NULL || pConfig == NULL) { - return MA_INVALID_ARGS; - } - - bqConfig = ma_bpf2__get_biquad_config(pConfig); - result = ma_biquad_reinit(&bqConfig, &pBPF->bq); - if (result != MA_SUCCESS) { - return result; - } - - return MA_SUCCESS; -} - -static MA_INLINE void ma_bpf2_process_pcm_frame_s16(ma_bpf2* pBPF, ma_int16* pFrameOut, const ma_int16* pFrameIn) -{ - ma_biquad_process_pcm_frame_s16(&pBPF->bq, pFrameOut, pFrameIn); -} - -static MA_INLINE void ma_bpf2_process_pcm_frame_f32(ma_bpf2* pBPF, float* pFrameOut, const float* pFrameIn) -{ - ma_biquad_process_pcm_frame_f32(&pBPF->bq, pFrameOut, pFrameIn); -} - -MA_API ma_result ma_bpf2_process_pcm_frames(ma_bpf2* pBPF, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount) -{ - if (pBPF == NULL) { - return MA_INVALID_ARGS; - } - - return ma_biquad_process_pcm_frames(&pBPF->bq, pFramesOut, pFramesIn, frameCount); -} - -MA_API ma_uint32 ma_bpf2_get_latency(const ma_bpf2* pBPF) -{ - if (pBPF == NULL) { - return 0; - } - - return ma_biquad_get_latency(&pBPF->bq); -} - - -MA_API ma_bpf_config ma_bpf_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRate, double cutoffFrequency, ma_uint32 order) -{ - ma_bpf_config config; - - MA_ZERO_OBJECT(&config); - config.format = format; - config.channels = channels; - config.sampleRate = sampleRate; - config.cutoffFrequency = cutoffFrequency; - config.order = ma_min(order, MA_MAX_FILTER_ORDER); - - return config; -} - - -typedef struct -{ - size_t sizeInBytes; - size_t bpf2Offset; -} ma_bpf_heap_layout; - -static ma_result ma_bpf_get_heap_layout(const ma_bpf_config* pConfig, ma_bpf_heap_layout* pHeapLayout) -{ - ma_result result; - ma_uint32 bpf2Count; - ma_uint32 ibpf2; - - MA_ASSERT(pHeapLayout != NULL); - - MA_ZERO_OBJECT(pHeapLayout); - - if (pConfig == NULL) { - return MA_INVALID_ARGS; - } - - if (pConfig->order > MA_MAX_FILTER_ORDER) { - return MA_INVALID_ARGS; - } - - /* We must have an even number of order. */ - if ((pConfig->order & 0x1) != 0) { - return MA_INVALID_ARGS; - } - - bpf2Count = pConfig->order / 2; - - pHeapLayout->sizeInBytes = 0; - - /* BPF 2 */ - pHeapLayout->bpf2Offset = pHeapLayout->sizeInBytes; - for (ibpf2 = 0; ibpf2 < bpf2Count; ibpf2 += 1) { - size_t bpf2HeapSizeInBytes; - ma_bpf2_config bpf2Config = ma_bpf2_config_init(pConfig->format, pConfig->channels, pConfig->sampleRate, pConfig->cutoffFrequency, 0.707107); /* <-- The "q" parameter does not matter for the purpose of calculating the heap size. */ - - result = ma_bpf2_get_heap_size(&bpf2Config, &bpf2HeapSizeInBytes); - if (result != MA_SUCCESS) { - return result; - } - - pHeapLayout->sizeInBytes += sizeof(ma_bpf2) + bpf2HeapSizeInBytes; - } - - /* Make sure allocation size is aligned. */ - pHeapLayout->sizeInBytes = ma_align_64(pHeapLayout->sizeInBytes); - - return MA_SUCCESS; -} - -static ma_result ma_bpf_reinit__internal(const ma_bpf_config* pConfig, void* pHeap, ma_bpf* pBPF, ma_bool32 isNew) -{ - ma_result result; - ma_uint32 bpf2Count; - ma_uint32 ibpf2; - ma_bpf_heap_layout heapLayout; /* Only used if isNew is true. */ - - if (pBPF == NULL || pConfig == NULL) { - return MA_INVALID_ARGS; - } - - /* Only supporting f32 and s16. */ - if (pConfig->format != ma_format_f32 && pConfig->format != ma_format_s16) { - return MA_INVALID_ARGS; - } - - /* The format cannot be changed after initialization. */ - if (pBPF->format != ma_format_unknown && pBPF->format != pConfig->format) { - return MA_INVALID_OPERATION; - } - - /* The channel count cannot be changed after initialization. */ - if (pBPF->channels != 0 && pBPF->channels != pConfig->channels) { - return MA_INVALID_OPERATION; - } - - if (pConfig->order > MA_MAX_FILTER_ORDER) { - return MA_INVALID_ARGS; - } - - /* We must have an even number of order. */ - if ((pConfig->order & 0x1) != 0) { - return MA_INVALID_ARGS; - } - - bpf2Count = pConfig->order / 2; - - /* The filter order can't change between reinits. */ - if (!isNew) { - if (pBPF->bpf2Count != bpf2Count) { - return MA_INVALID_OPERATION; - } - } - - if (isNew) { - result = ma_bpf_get_heap_layout(pConfig, &heapLayout); - if (result != MA_SUCCESS) { - return result; - } - - pBPF->_pHeap = pHeap; - MA_ZERO_MEMORY(pHeap, heapLayout.sizeInBytes); - - pBPF->pBPF2 = (ma_bpf2*)ma_offset_ptr(pHeap, heapLayout.bpf2Offset); - } else { - MA_ZERO_OBJECT(&heapLayout); - } - - for (ibpf2 = 0; ibpf2 < bpf2Count; ibpf2 += 1) { - ma_bpf2_config bpf2Config; - double q; - - /* TODO: Calculate Q to make this a proper Butterworth filter. */ - q = 0.707107; - - bpf2Config = ma_bpf2_config_init(pConfig->format, pConfig->channels, pConfig->sampleRate, pConfig->cutoffFrequency, q); - - if (isNew) { - size_t bpf2HeapSizeInBytes; - - result = ma_bpf2_get_heap_size(&bpf2Config, &bpf2HeapSizeInBytes); - if (result == MA_SUCCESS) { - result = ma_bpf2_init_preallocated(&bpf2Config, ma_offset_ptr(pHeap, heapLayout.bpf2Offset + (sizeof(ma_bpf2) * bpf2Count) + (ibpf2 * bpf2HeapSizeInBytes)), &pBPF->pBPF2[ibpf2]); - } - } else { - result = ma_bpf2_reinit(&bpf2Config, &pBPF->pBPF2[ibpf2]); - } - - if (result != MA_SUCCESS) { - return result; - } - } - - pBPF->bpf2Count = bpf2Count; - pBPF->format = pConfig->format; - pBPF->channels = pConfig->channels; - - return MA_SUCCESS; -} - - -MA_API ma_result ma_bpf_get_heap_size(const ma_bpf_config* pConfig, size_t* pHeapSizeInBytes) -{ - ma_result result; - ma_bpf_heap_layout heapLayout; - - if (pHeapSizeInBytes == NULL) { - return MA_INVALID_ARGS; - } - - *pHeapSizeInBytes = 0; - - result = ma_bpf_get_heap_layout(pConfig, &heapLayout); - if (result != MA_SUCCESS) { - return result; - } - - *pHeapSizeInBytes = heapLayout.sizeInBytes; - - return MA_SUCCESS; -} - -MA_API ma_result ma_bpf_init_preallocated(const ma_bpf_config* pConfig, void* pHeap, ma_bpf* pBPF) -{ - if (pBPF == NULL) { - return MA_INVALID_ARGS; - } - - MA_ZERO_OBJECT(pBPF); - - return ma_bpf_reinit__internal(pConfig, pHeap, pBPF, /*isNew*/MA_TRUE); -} - -MA_API ma_result ma_bpf_init(const ma_bpf_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_bpf* pBPF) -{ - ma_result result; - size_t heapSizeInBytes; - void* pHeap; - - result = ma_bpf_get_heap_size(pConfig, &heapSizeInBytes); - if (result != MA_SUCCESS) { - return result; - } - - if (heapSizeInBytes > 0) { - pHeap = ma_malloc(heapSizeInBytes, pAllocationCallbacks); - if (pHeap == NULL) { - return MA_OUT_OF_MEMORY; - } - } else { - pHeap = NULL; - } - - result = ma_bpf_init_preallocated(pConfig, pHeap, pBPF); - if (result != MA_SUCCESS) { - ma_free(pHeap, pAllocationCallbacks); - return result; - } - - pBPF->_ownsHeap = MA_TRUE; - return MA_SUCCESS; -} - -MA_API void ma_bpf_uninit(ma_bpf* pBPF, const ma_allocation_callbacks* pAllocationCallbacks) -{ - ma_uint32 ibpf2; - - if (pBPF == NULL) { - return; - } - - for (ibpf2 = 0; ibpf2 < pBPF->bpf2Count; ibpf2 += 1) { - ma_bpf2_uninit(&pBPF->pBPF2[ibpf2], pAllocationCallbacks); - } - - if (pBPF->_ownsHeap) { - ma_free(pBPF->_pHeap, pAllocationCallbacks); - } -} - -MA_API ma_result ma_bpf_reinit(const ma_bpf_config* pConfig, ma_bpf* pBPF) -{ - return ma_bpf_reinit__internal(pConfig, NULL, pBPF, /*isNew*/MA_FALSE); -} - -MA_API ma_result ma_bpf_process_pcm_frames(ma_bpf* pBPF, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount) -{ - ma_result result; - ma_uint32 ibpf2; - - if (pBPF == NULL) { - return MA_INVALID_ARGS; - } - - /* Faster path for in-place. */ - if (pFramesOut == pFramesIn) { - for (ibpf2 = 0; ibpf2 < pBPF->bpf2Count; ibpf2 += 1) { - result = ma_bpf2_process_pcm_frames(&pBPF->pBPF2[ibpf2], pFramesOut, pFramesOut, frameCount); - if (result != MA_SUCCESS) { - return result; - } - } - } - - /* Slightly slower path for copying. */ - if (pFramesOut != pFramesIn) { - ma_uint32 iFrame; - - /* */ if (pBPF->format == ma_format_f32) { - /* */ float* pFramesOutF32 = ( float*)pFramesOut; - const float* pFramesInF32 = (const float*)pFramesIn; - - for (iFrame = 0; iFrame < frameCount; iFrame += 1) { - MA_COPY_MEMORY(pFramesOutF32, pFramesInF32, ma_get_bytes_per_frame(pBPF->format, pBPF->channels)); - - for (ibpf2 = 0; ibpf2 < pBPF->bpf2Count; ibpf2 += 1) { - ma_bpf2_process_pcm_frame_f32(&pBPF->pBPF2[ibpf2], pFramesOutF32, pFramesOutF32); - } - - pFramesOutF32 += pBPF->channels; - pFramesInF32 += pBPF->channels; - } - } else if (pBPF->format == ma_format_s16) { - /* */ ma_int16* pFramesOutS16 = ( ma_int16*)pFramesOut; - const ma_int16* pFramesInS16 = (const ma_int16*)pFramesIn; - - for (iFrame = 0; iFrame < frameCount; iFrame += 1) { - MA_COPY_MEMORY(pFramesOutS16, pFramesInS16, ma_get_bytes_per_frame(pBPF->format, pBPF->channels)); - - for (ibpf2 = 0; ibpf2 < pBPF->bpf2Count; ibpf2 += 1) { - ma_bpf2_process_pcm_frame_s16(&pBPF->pBPF2[ibpf2], pFramesOutS16, pFramesOutS16); - } - - pFramesOutS16 += pBPF->channels; - pFramesInS16 += pBPF->channels; - } - } else { - MA_ASSERT(MA_FALSE); - return MA_INVALID_OPERATION; /* Should never hit this. */ - } - } - - return MA_SUCCESS; -} - -MA_API ma_uint32 ma_bpf_get_latency(const ma_bpf* pBPF) -{ - if (pBPF == NULL) { - return 0; - } - - return pBPF->bpf2Count*2; -} - - -/************************************************************************************************************************************************************** - -Notching Filter - -**************************************************************************************************************************************************************/ -MA_API ma_notch2_config ma_notch2_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRate, double q, double frequency) -{ - ma_notch2_config config; - - MA_ZERO_OBJECT(&config); - config.format = format; - config.channels = channels; - config.sampleRate = sampleRate; - config.q = q; - config.frequency = frequency; - - if (config.q == 0) { - config.q = 0.707107; - } - - return config; -} - - -static MA_INLINE ma_biquad_config ma_notch2__get_biquad_config(const ma_notch2_config* pConfig) -{ - ma_biquad_config bqConfig; - double q; - double w; - double s; - double c; - double a; - - MA_ASSERT(pConfig != NULL); - - q = pConfig->q; - w = 2 * MA_PI_D * pConfig->frequency / pConfig->sampleRate; - s = ma_sind(w); - c = ma_cosd(w); - a = s / (2*q); - - bqConfig.b0 = 1; - bqConfig.b1 = -2 * c; - bqConfig.b2 = 1; - bqConfig.a0 = 1 + a; - bqConfig.a1 = -2 * c; - bqConfig.a2 = 1 - a; - - bqConfig.format = pConfig->format; - bqConfig.channels = pConfig->channels; - - return bqConfig; -} - -MA_API ma_result ma_notch2_get_heap_size(const ma_notch2_config* pConfig, size_t* pHeapSizeInBytes) -{ - ma_biquad_config bqConfig; - bqConfig = ma_notch2__get_biquad_config(pConfig); - - return ma_biquad_get_heap_size(&bqConfig, pHeapSizeInBytes); -} - -MA_API ma_result ma_notch2_init_preallocated(const ma_notch2_config* pConfig, void* pHeap, ma_notch2* pFilter) -{ - ma_result result; - ma_biquad_config bqConfig; - - if (pFilter == NULL) { - return MA_INVALID_ARGS; - } - - MA_ZERO_OBJECT(pFilter); - - if (pConfig == NULL) { - return MA_INVALID_ARGS; - } - - bqConfig = ma_notch2__get_biquad_config(pConfig); - result = ma_biquad_init_preallocated(&bqConfig, pHeap, &pFilter->bq); - if (result != MA_SUCCESS) { - return result; - } - - return MA_SUCCESS; -} - -MA_API ma_result ma_notch2_init(const ma_notch2_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_notch2* pFilter) -{ - ma_result result; - size_t heapSizeInBytes; - void* pHeap; - - result = ma_notch2_get_heap_size(pConfig, &heapSizeInBytes); - if (result != MA_SUCCESS) { - return result; - } - - if (heapSizeInBytes > 0) { - pHeap = ma_malloc(heapSizeInBytes, pAllocationCallbacks); - if (pHeap == NULL) { - return MA_OUT_OF_MEMORY; - } - } else { - pHeap = NULL; - } - - result = ma_notch2_init_preallocated(pConfig, pHeap, pFilter); - if (result != MA_SUCCESS) { - ma_free(pHeap, pAllocationCallbacks); - return result; - } - - pFilter->bq._ownsHeap = MA_TRUE; /* <-- This will cause the biquad to take ownership of the heap and free it when it's uninitialized. */ - return MA_SUCCESS; -} - -MA_API void ma_notch2_uninit(ma_notch2* pFilter, const ma_allocation_callbacks* pAllocationCallbacks) -{ - if (pFilter == NULL) { - return; - } - - ma_biquad_uninit(&pFilter->bq, pAllocationCallbacks); /* <-- This will free the heap allocation. */ -} - -MA_API ma_result ma_notch2_reinit(const ma_notch2_config* pConfig, ma_notch2* pFilter) -{ - ma_result result; - ma_biquad_config bqConfig; - - if (pFilter == NULL || pConfig == NULL) { - return MA_INVALID_ARGS; - } - - bqConfig = ma_notch2__get_biquad_config(pConfig); - result = ma_biquad_reinit(&bqConfig, &pFilter->bq); - if (result != MA_SUCCESS) { - return result; - } - - return MA_SUCCESS; -} - -static MA_INLINE void ma_notch2_process_pcm_frame_s16(ma_notch2* pFilter, ma_int16* pFrameOut, const ma_int16* pFrameIn) -{ - ma_biquad_process_pcm_frame_s16(&pFilter->bq, pFrameOut, pFrameIn); -} - -static MA_INLINE void ma_notch2_process_pcm_frame_f32(ma_notch2* pFilter, float* pFrameOut, const float* pFrameIn) -{ - ma_biquad_process_pcm_frame_f32(&pFilter->bq, pFrameOut, pFrameIn); -} - -MA_API ma_result ma_notch2_process_pcm_frames(ma_notch2* pFilter, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount) -{ - if (pFilter == NULL) { - return MA_INVALID_ARGS; - } - - return ma_biquad_process_pcm_frames(&pFilter->bq, pFramesOut, pFramesIn, frameCount); -} - -MA_API ma_uint32 ma_notch2_get_latency(const ma_notch2* pFilter) -{ - if (pFilter == NULL) { - return 0; - } - - return ma_biquad_get_latency(&pFilter->bq); -} - - - -/************************************************************************************************************************************************************** - -Peaking EQ Filter - -**************************************************************************************************************************************************************/ -MA_API ma_peak2_config ma_peak2_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRate, double gainDB, double q, double frequency) -{ - ma_peak2_config config; - - MA_ZERO_OBJECT(&config); - config.format = format; - config.channels = channels; - config.sampleRate = sampleRate; - config.gainDB = gainDB; - config.q = q; - config.frequency = frequency; - - if (config.q == 0) { - config.q = 0.707107; - } - - return config; -} - - -static MA_INLINE ma_biquad_config ma_peak2__get_biquad_config(const ma_peak2_config* pConfig) -{ - ma_biquad_config bqConfig; - double q; - double w; - double s; - double c; - double a; - double A; - - MA_ASSERT(pConfig != NULL); - - q = pConfig->q; - w = 2 * MA_PI_D * pConfig->frequency / pConfig->sampleRate; - s = ma_sind(w); - c = ma_cosd(w); - a = s / (2*q); - A = ma_powd(10, (pConfig->gainDB / 40)); - - bqConfig.b0 = 1 + (a * A); - bqConfig.b1 = -2 * c; - bqConfig.b2 = 1 - (a * A); - bqConfig.a0 = 1 + (a / A); - bqConfig.a1 = -2 * c; - bqConfig.a2 = 1 - (a / A); - - bqConfig.format = pConfig->format; - bqConfig.channels = pConfig->channels; - - return bqConfig; -} - -MA_API ma_result ma_peak2_get_heap_size(const ma_peak2_config* pConfig, size_t* pHeapSizeInBytes) -{ - ma_biquad_config bqConfig; - bqConfig = ma_peak2__get_biquad_config(pConfig); - - return ma_biquad_get_heap_size(&bqConfig, pHeapSizeInBytes); -} - -MA_API ma_result ma_peak2_init_preallocated(const ma_peak2_config* pConfig, void* pHeap, ma_peak2* pFilter) -{ - ma_result result; - ma_biquad_config bqConfig; - - if (pFilter == NULL) { - return MA_INVALID_ARGS; - } - - MA_ZERO_OBJECT(pFilter); - - if (pConfig == NULL) { - return MA_INVALID_ARGS; - } - - bqConfig = ma_peak2__get_biquad_config(pConfig); - result = ma_biquad_init_preallocated(&bqConfig, pHeap, &pFilter->bq); - if (result != MA_SUCCESS) { - return result; - } - - return MA_SUCCESS; -} - -MA_API ma_result ma_peak2_init(const ma_peak2_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_peak2* pFilter) -{ - ma_result result; - size_t heapSizeInBytes; - void* pHeap; - - result = ma_peak2_get_heap_size(pConfig, &heapSizeInBytes); - if (result != MA_SUCCESS) { - return result; - } - - if (heapSizeInBytes > 0) { - pHeap = ma_malloc(heapSizeInBytes, pAllocationCallbacks); - if (pHeap == NULL) { - return MA_OUT_OF_MEMORY; - } - } else { - pHeap = NULL; - } - - result = ma_peak2_init_preallocated(pConfig, pHeap, pFilter); - if (result != MA_SUCCESS) { - ma_free(pHeap, pAllocationCallbacks); - return result; - } - - pFilter->bq._ownsHeap = MA_TRUE; /* <-- This will cause the biquad to take ownership of the heap and free it when it's uninitialized. */ - return MA_SUCCESS; -} - -MA_API void ma_peak2_uninit(ma_peak2* pFilter, const ma_allocation_callbacks* pAllocationCallbacks) -{ - if (pFilter == NULL) { - return; - } - - ma_biquad_uninit(&pFilter->bq, pAllocationCallbacks); /* <-- This will free the heap allocation. */ -} - -MA_API ma_result ma_peak2_reinit(const ma_peak2_config* pConfig, ma_peak2* pFilter) -{ - ma_result result; - ma_biquad_config bqConfig; - - if (pFilter == NULL || pConfig == NULL) { - return MA_INVALID_ARGS; - } - - bqConfig = ma_peak2__get_biquad_config(pConfig); - result = ma_biquad_reinit(&bqConfig, &pFilter->bq); - if (result != MA_SUCCESS) { - return result; - } - - return MA_SUCCESS; -} - -static MA_INLINE void ma_peak2_process_pcm_frame_s16(ma_peak2* pFilter, ma_int16* pFrameOut, const ma_int16* pFrameIn) -{ - ma_biquad_process_pcm_frame_s16(&pFilter->bq, pFrameOut, pFrameIn); -} - -static MA_INLINE void ma_peak2_process_pcm_frame_f32(ma_peak2* pFilter, float* pFrameOut, const float* pFrameIn) -{ - ma_biquad_process_pcm_frame_f32(&pFilter->bq, pFrameOut, pFrameIn); -} - -MA_API ma_result ma_peak2_process_pcm_frames(ma_peak2* pFilter, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount) -{ - if (pFilter == NULL) { - return MA_INVALID_ARGS; - } - - return ma_biquad_process_pcm_frames(&pFilter->bq, pFramesOut, pFramesIn, frameCount); -} - -MA_API ma_uint32 ma_peak2_get_latency(const ma_peak2* pFilter) -{ - if (pFilter == NULL) { - return 0; - } - - return ma_biquad_get_latency(&pFilter->bq); -} - - -/************************************************************************************************************************************************************** - -Low Shelf Filter - -**************************************************************************************************************************************************************/ -MA_API ma_loshelf2_config ma_loshelf2_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRate, double gainDB, double shelfSlope, double frequency) -{ - ma_loshelf2_config config; - - MA_ZERO_OBJECT(&config); - config.format = format; - config.channels = channels; - config.sampleRate = sampleRate; - config.gainDB = gainDB; - config.shelfSlope = shelfSlope; - config.frequency = frequency; - - return config; -} - - -static MA_INLINE ma_biquad_config ma_loshelf2__get_biquad_config(const ma_loshelf2_config* pConfig) -{ - ma_biquad_config bqConfig; - double w; - double s; - double c; - double A; - double S; - double a; - double sqrtA; - - MA_ASSERT(pConfig != NULL); - - w = 2 * MA_PI_D * pConfig->frequency / pConfig->sampleRate; - s = ma_sind(w); - c = ma_cosd(w); - A = ma_powd(10, (pConfig->gainDB / 40)); - S = pConfig->shelfSlope; - a = s/2 * ma_sqrtd((A + 1/A) * (1/S - 1) + 2); - sqrtA = 2*ma_sqrtd(A)*a; - - bqConfig.b0 = A * ((A + 1) - (A - 1)*c + sqrtA); - bqConfig.b1 = 2 * A * ((A - 1) - (A + 1)*c); - bqConfig.b2 = A * ((A + 1) - (A - 1)*c - sqrtA); - bqConfig.a0 = (A + 1) + (A - 1)*c + sqrtA; - bqConfig.a1 = -2 * ((A - 1) + (A + 1)*c); - bqConfig.a2 = (A + 1) + (A - 1)*c - sqrtA; - - bqConfig.format = pConfig->format; - bqConfig.channels = pConfig->channels; - - return bqConfig; -} - -MA_API ma_result ma_loshelf2_get_heap_size(const ma_loshelf2_config* pConfig, size_t* pHeapSizeInBytes) -{ - ma_biquad_config bqConfig; - bqConfig = ma_loshelf2__get_biquad_config(pConfig); - - return ma_biquad_get_heap_size(&bqConfig, pHeapSizeInBytes); -} - -MA_API ma_result ma_loshelf2_init_preallocated(const ma_loshelf2_config* pConfig, void* pHeap, ma_loshelf2* pFilter) -{ - ma_result result; - ma_biquad_config bqConfig; - - if (pFilter == NULL) { - return MA_INVALID_ARGS; - } - - MA_ZERO_OBJECT(pFilter); - - if (pConfig == NULL) { - return MA_INVALID_ARGS; - } - - bqConfig = ma_loshelf2__get_biquad_config(pConfig); - result = ma_biquad_init_preallocated(&bqConfig, pHeap, &pFilter->bq); - if (result != MA_SUCCESS) { - return result; - } - - return MA_SUCCESS; -} - -MA_API ma_result ma_loshelf2_init(const ma_loshelf2_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_loshelf2* pFilter) -{ - ma_result result; - size_t heapSizeInBytes; - void* pHeap; - - result = ma_loshelf2_get_heap_size(pConfig, &heapSizeInBytes); - if (result != MA_SUCCESS) { - return result; - } - - if (heapSizeInBytes > 0) { - pHeap = ma_malloc(heapSizeInBytes, pAllocationCallbacks); - if (pHeap == NULL) { - return MA_OUT_OF_MEMORY; - } - } else { - pHeap = NULL; - } - - result = ma_loshelf2_init_preallocated(pConfig, pHeap, pFilter); - if (result != MA_SUCCESS) { - ma_free(pHeap, pAllocationCallbacks); - return result; - } - - pFilter->bq._ownsHeap = MA_TRUE; /* <-- This will cause the biquad to take ownership of the heap and free it when it's uninitialized. */ - return MA_SUCCESS; -} - -MA_API void ma_loshelf2_uninit(ma_loshelf2* pFilter, const ma_allocation_callbacks* pAllocationCallbacks) -{ - if (pFilter == NULL) { - return; - } - - ma_biquad_uninit(&pFilter->bq, pAllocationCallbacks); /* <-- This will free the heap allocation. */ -} - -MA_API ma_result ma_loshelf2_reinit(const ma_loshelf2_config* pConfig, ma_loshelf2* pFilter) -{ - ma_result result; - ma_biquad_config bqConfig; - - if (pFilter == NULL || pConfig == NULL) { - return MA_INVALID_ARGS; - } - - bqConfig = ma_loshelf2__get_biquad_config(pConfig); - result = ma_biquad_reinit(&bqConfig, &pFilter->bq); - if (result != MA_SUCCESS) { - return result; - } - - return MA_SUCCESS; -} - -static MA_INLINE void ma_loshelf2_process_pcm_frame_s16(ma_loshelf2* pFilter, ma_int16* pFrameOut, const ma_int16* pFrameIn) -{ - ma_biquad_process_pcm_frame_s16(&pFilter->bq, pFrameOut, pFrameIn); -} - -static MA_INLINE void ma_loshelf2_process_pcm_frame_f32(ma_loshelf2* pFilter, float* pFrameOut, const float* pFrameIn) -{ - ma_biquad_process_pcm_frame_f32(&pFilter->bq, pFrameOut, pFrameIn); -} - -MA_API ma_result ma_loshelf2_process_pcm_frames(ma_loshelf2* pFilter, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount) -{ - if (pFilter == NULL) { - return MA_INVALID_ARGS; - } - - return ma_biquad_process_pcm_frames(&pFilter->bq, pFramesOut, pFramesIn, frameCount); -} - -MA_API ma_uint32 ma_loshelf2_get_latency(const ma_loshelf2* pFilter) -{ - if (pFilter == NULL) { - return 0; - } - - return ma_biquad_get_latency(&pFilter->bq); -} - - -/************************************************************************************************************************************************************** - -High Shelf Filter - -**************************************************************************************************************************************************************/ -MA_API ma_hishelf2_config ma_hishelf2_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRate, double gainDB, double shelfSlope, double frequency) -{ - ma_hishelf2_config config; - - MA_ZERO_OBJECT(&config); - config.format = format; - config.channels = channels; - config.sampleRate = sampleRate; - config.gainDB = gainDB; - config.shelfSlope = shelfSlope; - config.frequency = frequency; - - return config; -} - - -static MA_INLINE ma_biquad_config ma_hishelf2__get_biquad_config(const ma_hishelf2_config* pConfig) -{ - ma_biquad_config bqConfig; - double w; - double s; - double c; - double A; - double S; - double a; - double sqrtA; - - MA_ASSERT(pConfig != NULL); - - w = 2 * MA_PI_D * pConfig->frequency / pConfig->sampleRate; - s = ma_sind(w); - c = ma_cosd(w); - A = ma_powd(10, (pConfig->gainDB / 40)); - S = pConfig->shelfSlope; - a = s/2 * ma_sqrtd((A + 1/A) * (1/S - 1) + 2); - sqrtA = 2*ma_sqrtd(A)*a; - - bqConfig.b0 = A * ((A + 1) + (A - 1)*c + sqrtA); - bqConfig.b1 = -2 * A * ((A - 1) + (A + 1)*c); - bqConfig.b2 = A * ((A + 1) + (A - 1)*c - sqrtA); - bqConfig.a0 = (A + 1) - (A - 1)*c + sqrtA; - bqConfig.a1 = 2 * ((A - 1) - (A + 1)*c); - bqConfig.a2 = (A + 1) - (A - 1)*c - sqrtA; - - bqConfig.format = pConfig->format; - bqConfig.channels = pConfig->channels; - - return bqConfig; -} - -MA_API ma_result ma_hishelf2_get_heap_size(const ma_hishelf2_config* pConfig, size_t* pHeapSizeInBytes) -{ - ma_biquad_config bqConfig; - bqConfig = ma_hishelf2__get_biquad_config(pConfig); - - return ma_biquad_get_heap_size(&bqConfig, pHeapSizeInBytes); -} - -MA_API ma_result ma_hishelf2_init_preallocated(const ma_hishelf2_config* pConfig, void* pHeap, ma_hishelf2* pFilter) -{ - ma_result result; - ma_biquad_config bqConfig; - - if (pFilter == NULL) { - return MA_INVALID_ARGS; - } - - MA_ZERO_OBJECT(pFilter); - - if (pConfig == NULL) { - return MA_INVALID_ARGS; - } - - bqConfig = ma_hishelf2__get_biquad_config(pConfig); - result = ma_biquad_init_preallocated(&bqConfig, pHeap, &pFilter->bq); - if (result != MA_SUCCESS) { - return result; - } - - return MA_SUCCESS; -} - -MA_API ma_result ma_hishelf2_init(const ma_hishelf2_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_hishelf2* pFilter) -{ - ma_result result; - size_t heapSizeInBytes; - void* pHeap; - - result = ma_hishelf2_get_heap_size(pConfig, &heapSizeInBytes); - if (result != MA_SUCCESS) { - return result; - } - - if (heapSizeInBytes > 0) { - pHeap = ma_malloc(heapSizeInBytes, pAllocationCallbacks); - if (pHeap == NULL) { - return MA_OUT_OF_MEMORY; - } - } else { - pHeap = NULL; - } - - result = ma_hishelf2_init_preallocated(pConfig, pHeap, pFilter); - if (result != MA_SUCCESS) { - ma_free(pHeap, pAllocationCallbacks); - return result; - } - - pFilter->bq._ownsHeap = MA_TRUE; /* <-- This will cause the biquad to take ownership of the heap and free it when it's uninitialized. */ - return MA_SUCCESS; -} - -MA_API void ma_hishelf2_uninit(ma_hishelf2* pFilter, const ma_allocation_callbacks* pAllocationCallbacks) -{ - if (pFilter == NULL) { - return; - } - - ma_biquad_uninit(&pFilter->bq, pAllocationCallbacks); /* <-- This will free the heap allocation. */ -} - -MA_API ma_result ma_hishelf2_reinit(const ma_hishelf2_config* pConfig, ma_hishelf2* pFilter) -{ - ma_result result; - ma_biquad_config bqConfig; - - if (pFilter == NULL || pConfig == NULL) { - return MA_INVALID_ARGS; - } - - bqConfig = ma_hishelf2__get_biquad_config(pConfig); - result = ma_biquad_reinit(&bqConfig, &pFilter->bq); - if (result != MA_SUCCESS) { - return result; - } - - return MA_SUCCESS; -} - -static MA_INLINE void ma_hishelf2_process_pcm_frame_s16(ma_hishelf2* pFilter, ma_int16* pFrameOut, const ma_int16* pFrameIn) -{ - ma_biquad_process_pcm_frame_s16(&pFilter->bq, pFrameOut, pFrameIn); -} - -static MA_INLINE void ma_hishelf2_process_pcm_frame_f32(ma_hishelf2* pFilter, float* pFrameOut, const float* pFrameIn) -{ - ma_biquad_process_pcm_frame_f32(&pFilter->bq, pFrameOut, pFrameIn); -} - -MA_API ma_result ma_hishelf2_process_pcm_frames(ma_hishelf2* pFilter, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount) -{ - if (pFilter == NULL) { - return MA_INVALID_ARGS; - } - - return ma_biquad_process_pcm_frames(&pFilter->bq, pFramesOut, pFramesIn, frameCount); -} - -MA_API ma_uint32 ma_hishelf2_get_latency(const ma_hishelf2* pFilter) -{ - if (pFilter == NULL) { - return 0; - } - - return ma_biquad_get_latency(&pFilter->bq); -} - - - -/* -Delay -*/ -MA_API ma_delay_config ma_delay_config_init(ma_uint32 channels, ma_uint32 sampleRate, ma_uint32 delayInFrames, float decay) -{ - ma_delay_config config; - - MA_ZERO_OBJECT(&config); - config.channels = channels; - config.sampleRate = sampleRate; - config.delayInFrames = delayInFrames; - config.delayStart = (decay == 0) ? MA_TRUE : MA_FALSE; /* Delay the start if it looks like we're not configuring an echo. */ - config.wet = 1; - config.dry = 1; - config.decay = decay; - - return config; -} - - -MA_API ma_result ma_delay_init(const ma_delay_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_delay* pDelay) -{ - if (pDelay == NULL) { - return MA_INVALID_ARGS; - } - - MA_ZERO_OBJECT(pDelay); - - if (pConfig == NULL) { - return MA_INVALID_ARGS; - } - - if (pConfig->decay < 0 || pConfig->decay > 1) { - return MA_INVALID_ARGS; - } - - pDelay->config = *pConfig; - pDelay->bufferSizeInFrames = pConfig->delayInFrames; - pDelay->cursor = 0; - - pDelay->pBuffer = (float*)ma_malloc((size_t)(pDelay->bufferSizeInFrames * ma_get_bytes_per_frame(ma_format_f32, pConfig->channels)), pAllocationCallbacks); - if (pDelay->pBuffer == NULL) { - return MA_OUT_OF_MEMORY; - } - - ma_silence_pcm_frames(pDelay->pBuffer, pDelay->bufferSizeInFrames, ma_format_f32, pConfig->channels); - - return MA_SUCCESS; -} - -MA_API void ma_delay_uninit(ma_delay* pDelay, const ma_allocation_callbacks* pAllocationCallbacks) -{ - if (pDelay == NULL) { - return; - } - - ma_free(pDelay->pBuffer, pAllocationCallbacks); -} - -MA_API ma_result ma_delay_process_pcm_frames(ma_delay* pDelay, void* pFramesOut, const void* pFramesIn, ma_uint32 frameCount) -{ - ma_uint32 iFrame; - ma_uint32 iChannel; - float* pFramesOutF32 = (float*)pFramesOut; - const float* pFramesInF32 = (const float*)pFramesIn; - - if (pDelay == NULL || pFramesOut == NULL || pFramesIn == NULL) { - return MA_INVALID_ARGS; - } - - for (iFrame = 0; iFrame < frameCount; iFrame += 1) { - for (iChannel = 0; iChannel < pDelay->config.channels; iChannel += 1) { - ma_uint32 iBuffer = (pDelay->cursor * pDelay->config.channels) + iChannel; - - if (pDelay->config.delayStart) { - /* Delayed start. */ - - /* Read */ - pFramesOutF32[iChannel] = pDelay->pBuffer[iBuffer] * pDelay->config.wet; - - /* Feedback */ - pDelay->pBuffer[iBuffer] = (pDelay->pBuffer[iBuffer] * pDelay->config.decay) + (pFramesInF32[iChannel] * pDelay->config.dry); - } else { - /* Immediate start */ - - /* Feedback */ - pDelay->pBuffer[iBuffer] = (pDelay->pBuffer[iBuffer] * pDelay->config.decay) + (pFramesInF32[iChannel] * pDelay->config.dry); - - /* Read */ - pFramesOutF32[iChannel] = pDelay->pBuffer[iBuffer] * pDelay->config.wet; - } - } - - pDelay->cursor = (pDelay->cursor + 1) % pDelay->bufferSizeInFrames; - - pFramesOutF32 += pDelay->config.channels; - pFramesInF32 += pDelay->config.channels; - } - - return MA_SUCCESS; -} - -MA_API void ma_delay_set_wet(ma_delay* pDelay, float value) -{ - if (pDelay == NULL) { - return; - } - - pDelay->config.wet = value; -} - -MA_API float ma_delay_get_wet(const ma_delay* pDelay) -{ - if (pDelay == NULL) { - return 0; - } - - return pDelay->config.wet; -} - -MA_API void ma_delay_set_dry(ma_delay* pDelay, float value) -{ - if (pDelay == NULL) { - return; - } - - pDelay->config.dry = value; -} - -MA_API float ma_delay_get_dry(const ma_delay* pDelay) -{ - if (pDelay == NULL) { - return 0; - } - - return pDelay->config.dry; -} - -MA_API void ma_delay_set_decay(ma_delay* pDelay, float value) -{ - if (pDelay == NULL) { - return; - } - - pDelay->config.decay = value; -} - -MA_API float ma_delay_get_decay(const ma_delay* pDelay) -{ - if (pDelay == NULL) { - return 0; - } - - return pDelay->config.decay; -} - - -MA_API ma_gainer_config ma_gainer_config_init(ma_uint32 channels, ma_uint32 smoothTimeInFrames) -{ - ma_gainer_config config; - - MA_ZERO_OBJECT(&config); - config.channels = channels; - config.smoothTimeInFrames = smoothTimeInFrames; - - return config; -} - - -typedef struct -{ - size_t sizeInBytes; - size_t oldGainsOffset; - size_t newGainsOffset; -} ma_gainer_heap_layout; - -static ma_result ma_gainer_get_heap_layout(const ma_gainer_config* pConfig, ma_gainer_heap_layout* pHeapLayout) -{ - MA_ASSERT(pHeapLayout != NULL); - - MA_ZERO_OBJECT(pHeapLayout); - - if (pConfig == NULL) { - return MA_INVALID_ARGS; - } - - if (pConfig->channels == 0) { - return MA_INVALID_ARGS; - } - - pHeapLayout->sizeInBytes = 0; - - /* Old gains. */ - pHeapLayout->oldGainsOffset = pHeapLayout->sizeInBytes; - pHeapLayout->sizeInBytes += sizeof(float) * pConfig->channels; - - /* New gains. */ - pHeapLayout->newGainsOffset = pHeapLayout->sizeInBytes; - pHeapLayout->sizeInBytes += sizeof(float) * pConfig->channels; - - /* Alignment. */ - pHeapLayout->sizeInBytes = ma_align_64(pHeapLayout->sizeInBytes); - - return MA_SUCCESS; -} - - -MA_API ma_result ma_gainer_get_heap_size(const ma_gainer_config* pConfig, size_t* pHeapSizeInBytes) -{ - ma_result result; - ma_gainer_heap_layout heapLayout; - - if (pHeapSizeInBytes == NULL) { - return MA_INVALID_ARGS; - } - - *pHeapSizeInBytes = 0; - - result = ma_gainer_get_heap_layout(pConfig, &heapLayout); - if (result != MA_SUCCESS) { - return MA_INVALID_ARGS; - } - - *pHeapSizeInBytes = heapLayout.sizeInBytes; - - return MA_SUCCESS; -} - - -MA_API ma_result ma_gainer_init_preallocated(const ma_gainer_config* pConfig, void* pHeap, ma_gainer* pGainer) -{ - ma_result result; - ma_gainer_heap_layout heapLayout; - ma_uint32 iChannel; - - if (pGainer == NULL) { - return MA_INVALID_ARGS; - } - - MA_ZERO_OBJECT(pGainer); - - if (pConfig == NULL || pHeap == NULL) { - return MA_INVALID_ARGS; - } - - result = ma_gainer_get_heap_layout(pConfig, &heapLayout); - if (result != MA_SUCCESS) { - return result; - } - - pGainer->_pHeap = pHeap; - MA_ZERO_MEMORY(pHeap, heapLayout.sizeInBytes); - - pGainer->pOldGains = (float*)ma_offset_ptr(pHeap, heapLayout.oldGainsOffset); - pGainer->pNewGains = (float*)ma_offset_ptr(pHeap, heapLayout.newGainsOffset); - pGainer->masterVolume = 1; - - pGainer->config = *pConfig; - pGainer->t = (ma_uint32)-1; /* No interpolation by default. */ - - for (iChannel = 0; iChannel < pConfig->channels; iChannel += 1) { - pGainer->pOldGains[iChannel] = 1; - pGainer->pNewGains[iChannel] = 1; - } - - return MA_SUCCESS; -} - -MA_API ma_result ma_gainer_init(const ma_gainer_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_gainer* pGainer) -{ - ma_result result; - size_t heapSizeInBytes; - void* pHeap; - - result = ma_gainer_get_heap_size(pConfig, &heapSizeInBytes); - if (result != MA_SUCCESS) { - return result; /* Failed to retrieve the size of the heap allocation. */ - } - - if (heapSizeInBytes > 0) { - pHeap = ma_malloc(heapSizeInBytes, pAllocationCallbacks); - if (pHeap == NULL) { - return MA_OUT_OF_MEMORY; - } - } else { - pHeap = NULL; - } - - result = ma_gainer_init_preallocated(pConfig, pHeap, pGainer); - if (result != MA_SUCCESS) { - ma_free(pHeap, pAllocationCallbacks); - return result; - } - - pGainer->_ownsHeap = MA_TRUE; - return MA_SUCCESS; -} - -MA_API void ma_gainer_uninit(ma_gainer* pGainer, const ma_allocation_callbacks* pAllocationCallbacks) -{ - if (pGainer == NULL) { - return; - } - - if (pGainer->_ownsHeap) { - ma_free(pGainer->_pHeap, pAllocationCallbacks); - } -} - -static float ma_gainer_calculate_current_gain(const ma_gainer* pGainer, ma_uint32 channel) -{ - float a = (float)pGainer->t / pGainer->config.smoothTimeInFrames; - return ma_mix_f32_fast(pGainer->pOldGains[channel], pGainer->pNewGains[channel], a); -} - -static /*__attribute__((noinline))*/ ma_result ma_gainer_process_pcm_frames_internal(ma_gainer * pGainer, void* MA_RESTRICT pFramesOut, const void* MA_RESTRICT pFramesIn, ma_uint64 frameCount) -{ - ma_uint64 iFrame; - ma_uint32 iChannel; - ma_uint64 interpolatedFrameCount; - - MA_ASSERT(pGainer != NULL); - - /* - We don't necessarily need to apply a linear interpolation for the entire frameCount frames. When - linear interpolation is not needed we can do a simple volume adjustment which will be more - efficient than a lerp with an alpha value of 1. - - To do this, all we need to do is determine how many frames need to have a lerp applied. Then we - just process that number of frames with linear interpolation. After that we run on an optimized - path which just applies the new gains without a lerp. - */ - if (pGainer->t >= pGainer->config.smoothTimeInFrames) { - interpolatedFrameCount = 0; - } else { - interpolatedFrameCount = pGainer->t - pGainer->config.smoothTimeInFrames; - if (interpolatedFrameCount > frameCount) { - interpolatedFrameCount = frameCount; - } - } - - /* - Start off with our interpolated frames. When we do this, we'll adjust frameCount and our pointers - so that the fast path can work naturally without consideration of the interpolated path. - */ - if (interpolatedFrameCount > 0) { - /* We can allow the input and output buffers to be null in which case we'll just update the internal timer. */ - if (pFramesOut != NULL && pFramesIn != NULL) { - /* - All we're really doing here is moving the old gains towards the new gains. We don't want to - be modifying the gains inside the ma_gainer object because that will break things. Instead - we can make a copy here on the stack. For extreme channel counts we can fall back to a slower - implementation which just uses a standard lerp. - */ - float* pFramesOutF32 = (float*)pFramesOut; - const float* pFramesInF32 = (const float*)pFramesIn; - float a = (float)pGainer->t / pGainer->config.smoothTimeInFrames; - float d = 1.0f / pGainer->config.smoothTimeInFrames; - - if (pGainer->config.channels <= 32) { - float pRunningGain[32]; - float pRunningGainDelta[32]; /* Could this be heap-allocated as part of the ma_gainer object? */ - - /* Initialize the running gain. */ - for (iChannel = 0; iChannel < pGainer->config.channels; iChannel += 1) { - float t = (pGainer->pNewGains[iChannel] - pGainer->pOldGains[iChannel]) * pGainer->masterVolume; - pRunningGainDelta[iChannel] = t * d; - pRunningGain[iChannel] = (pGainer->pOldGains[iChannel] * pGainer->masterVolume) + (t * a); - } - - iFrame = 0; - - /* Optimized paths for common channel counts. This is mostly just experimenting with some SIMD ideas. It's not necessarily final. */ - if (pGainer->config.channels == 2) { - #if defined(MA_SUPPORT_SSE2) - if (ma_has_sse2()) { - ma_uint64 unrolledLoopCount = interpolatedFrameCount >> 1; - - /* Expand some arrays so we can have a clean SIMD loop below. */ - __m128 runningGainDelta0 = _mm_set_ps(pRunningGainDelta[1], pRunningGainDelta[0], pRunningGainDelta[1], pRunningGainDelta[0]); - __m128 runningGain0 = _mm_set_ps(pRunningGain[1] + pRunningGainDelta[1], pRunningGain[0] + pRunningGainDelta[0], pRunningGain[1], pRunningGain[0]); - - for (; iFrame < unrolledLoopCount; iFrame += 1) { - _mm_storeu_ps(&pFramesOutF32[iFrame*4 + 0], _mm_mul_ps(_mm_loadu_ps(&pFramesInF32[iFrame*4 + 0]), runningGain0)); - runningGain0 = _mm_add_ps(runningGain0, runningGainDelta0); - } - - iFrame = unrolledLoopCount << 1; - } else - #endif - { - /* - Two different scalar implementations here. Clang (and I assume GCC) will vectorize - both of these, but the bottom version results in a nicer vectorization with less - instructions emitted. The problem, however, is that the bottom version runs slower - when compiled with MSVC. The top version will be partially vectorized by MSVC. - */ - #if defined(_MSC_VER) && !defined(__clang__) - ma_uint64 unrolledLoopCount = interpolatedFrameCount >> 1; - - /* Expand some arrays so we can have a clean 4x SIMD operation in the loop. */ - pRunningGainDelta[2] = pRunningGainDelta[0]; - pRunningGainDelta[3] = pRunningGainDelta[1]; - pRunningGain[2] = pRunningGain[0] + pRunningGainDelta[0]; - pRunningGain[3] = pRunningGain[1] + pRunningGainDelta[1]; - - for (; iFrame < unrolledLoopCount; iFrame += 1) { - pFramesOutF32[iFrame*4 + 0] = pFramesInF32[iFrame*4 + 0] * pRunningGain[0]; - pFramesOutF32[iFrame*4 + 1] = pFramesInF32[iFrame*4 + 1] * pRunningGain[1]; - pFramesOutF32[iFrame*4 + 2] = pFramesInF32[iFrame*4 + 2] * pRunningGain[2]; - pFramesOutF32[iFrame*4 + 3] = pFramesInF32[iFrame*4 + 3] * pRunningGain[3]; - - /* Move the running gain forward towards the new gain. */ - pRunningGain[0] += pRunningGainDelta[0]; - pRunningGain[1] += pRunningGainDelta[1]; - pRunningGain[2] += pRunningGainDelta[2]; - pRunningGain[3] += pRunningGainDelta[3]; - } - - iFrame = unrolledLoopCount << 1; - #else - for (; iFrame < interpolatedFrameCount; iFrame += 1) { - for (iChannel = 0; iChannel < 2; iChannel += 1) { - pFramesOutF32[iFrame*2 + iChannel] = pFramesInF32[iFrame*2 + iChannel] * pRunningGain[iChannel]; - } - - for (iChannel = 0; iChannel < 2; iChannel += 1) { - pRunningGain[iChannel] += pRunningGainDelta[iChannel]; - } - } - #endif - } - } else if (pGainer->config.channels == 6) { - #if defined(MA_SUPPORT_SSE2) - if (ma_has_sse2()) { - /* - For 6 channels things are a bit more complicated because 6 isn't cleanly divisible by 4. We need to do 2 frames - at a time, meaning we'll be doing 12 samples in a group. Like the stereo case we'll need to expand some arrays - so we can do clean 4x SIMD operations. - */ - ma_uint64 unrolledLoopCount = interpolatedFrameCount >> 1; - - /* Expand some arrays so we can have a clean SIMD loop below. */ - __m128 runningGainDelta0 = _mm_set_ps(pRunningGainDelta[3], pRunningGainDelta[2], pRunningGainDelta[1], pRunningGainDelta[0]); - __m128 runningGainDelta1 = _mm_set_ps(pRunningGainDelta[1], pRunningGainDelta[0], pRunningGainDelta[5], pRunningGainDelta[4]); - __m128 runningGainDelta2 = _mm_set_ps(pRunningGainDelta[5], pRunningGainDelta[4], pRunningGainDelta[3], pRunningGainDelta[2]); - - __m128 runningGain0 = _mm_set_ps(pRunningGain[3], pRunningGain[2], pRunningGain[1], pRunningGain[0]); - __m128 runningGain1 = _mm_set_ps(pRunningGain[1] + pRunningGainDelta[1], pRunningGain[0] + pRunningGainDelta[0], pRunningGain[5], pRunningGain[4]); - __m128 runningGain2 = _mm_set_ps(pRunningGain[5] + pRunningGainDelta[5], pRunningGain[4] + pRunningGainDelta[4], pRunningGain[3] + pRunningGainDelta[3], pRunningGain[2] + pRunningGainDelta[2]); - - for (; iFrame < unrolledLoopCount; iFrame += 1) { - _mm_storeu_ps(&pFramesOutF32[iFrame*12 + 0], _mm_mul_ps(_mm_loadu_ps(&pFramesInF32[iFrame*12 + 0]), runningGain0)); - _mm_storeu_ps(&pFramesOutF32[iFrame*12 + 4], _mm_mul_ps(_mm_loadu_ps(&pFramesInF32[iFrame*12 + 4]), runningGain1)); - _mm_storeu_ps(&pFramesOutF32[iFrame*12 + 8], _mm_mul_ps(_mm_loadu_ps(&pFramesInF32[iFrame*12 + 8]), runningGain2)); - - runningGain0 = _mm_add_ps(runningGain0, runningGainDelta0); - runningGain1 = _mm_add_ps(runningGain1, runningGainDelta1); - runningGain2 = _mm_add_ps(runningGain2, runningGainDelta2); - } - - iFrame = unrolledLoopCount << 1; - } else - #endif - { - for (; iFrame < interpolatedFrameCount; iFrame += 1) { - for (iChannel = 0; iChannel < 6; iChannel += 1) { - pFramesOutF32[iFrame*6 + iChannel] = pFramesInF32[iFrame*6 + iChannel] * pRunningGain[iChannel]; - } - - /* Move the running gain forward towards the new gain. */ - for (iChannel = 0; iChannel < 6; iChannel += 1) { - pRunningGain[iChannel] += pRunningGainDelta[iChannel]; - } - } - } - } else if (pGainer->config.channels == 8) { - /* For 8 channels we can just go over frame by frame and do all eight channels as 2 separate 4x SIMD operations. */ - #if defined(MA_SUPPORT_SSE2) - if (ma_has_sse2()) { - __m128 runningGainDelta0 = _mm_loadu_ps(&pRunningGainDelta[0]); - __m128 runningGainDelta1 = _mm_loadu_ps(&pRunningGainDelta[4]); - __m128 runningGain0 = _mm_loadu_ps(&pRunningGain[0]); - __m128 runningGain1 = _mm_loadu_ps(&pRunningGain[4]); - - for (; iFrame < interpolatedFrameCount; iFrame += 1) { - _mm_storeu_ps(&pFramesOutF32[iFrame*8 + 0], _mm_mul_ps(_mm_loadu_ps(&pFramesInF32[iFrame*8 + 0]), runningGain0)); - _mm_storeu_ps(&pFramesOutF32[iFrame*8 + 4], _mm_mul_ps(_mm_loadu_ps(&pFramesInF32[iFrame*8 + 4]), runningGain1)); - - runningGain0 = _mm_add_ps(runningGain0, runningGainDelta0); - runningGain1 = _mm_add_ps(runningGain1, runningGainDelta1); - } - } else - #endif - { - /* This is crafted so that it auto-vectorizes when compiled with Clang. */ - for (; iFrame < interpolatedFrameCount; iFrame += 1) { - for (iChannel = 0; iChannel < 8; iChannel += 1) { - pFramesOutF32[iFrame*8 + iChannel] = pFramesInF32[iFrame*8 + iChannel] * pRunningGain[iChannel]; - } - - /* Move the running gain forward towards the new gain. */ - for (iChannel = 0; iChannel < 8; iChannel += 1) { - pRunningGain[iChannel] += pRunningGainDelta[iChannel]; - } - } - } - } - - for (; iFrame < interpolatedFrameCount; iFrame += 1) { - for (iChannel = 0; iChannel < pGainer->config.channels; iChannel += 1) { - pFramesOutF32[iFrame*pGainer->config.channels + iChannel] = pFramesInF32[iFrame*pGainer->config.channels + iChannel] * pRunningGain[iChannel]; - pRunningGain[iChannel] += pRunningGainDelta[iChannel]; - } - } - } else { - /* Slower path for extreme channel counts where we can't fit enough on the stack. We could also move this to the heap as part of the ma_gainer object which might even be better since it'll only be updated when the gains actually change. */ - for (iFrame = 0; iFrame < interpolatedFrameCount; iFrame += 1) { - for (iChannel = 0; iChannel < pGainer->config.channels; iChannel += 1) { - pFramesOutF32[iFrame*pGainer->config.channels + iChannel] = pFramesInF32[iFrame*pGainer->config.channels + iChannel] * ma_mix_f32_fast(pGainer->pOldGains[iChannel], pGainer->pNewGains[iChannel], a) * pGainer->masterVolume; - } - - a += d; - } - } - } - - /* Make sure the timer is updated. */ - pGainer->t = (ma_uint32)ma_min(pGainer->t + interpolatedFrameCount, pGainer->config.smoothTimeInFrames); - - /* Adjust our arguments so the next part can work normally. */ - frameCount -= interpolatedFrameCount; - pFramesOut = ma_offset_ptr(pFramesOut, interpolatedFrameCount * sizeof(float)); - pFramesIn = ma_offset_ptr(pFramesIn, interpolatedFrameCount * sizeof(float)); - } - - /* All we need to do here is apply the new gains using an optimized path. */ - if (pFramesOut != NULL && pFramesIn != NULL) { - if (pGainer->config.channels <= 32) { - float gains[32]; - for (iChannel = 0; iChannel < pGainer->config.channels; iChannel += 1) { - gains[iChannel] = pGainer->pNewGains[iChannel] * pGainer->masterVolume; - } - - ma_copy_and_apply_volume_factor_per_channel_f32((float*)pFramesOut, (const float*)pFramesIn, frameCount, pGainer->config.channels, gains); - } else { - /* Slow path. Too many channels to fit on the stack. Need to apply a master volume as a separate path. */ - for (iFrame = 0; iFrame < frameCount; iFrame += 1) { - for (iChannel = 0; iChannel < pGainer->config.channels; iChannel += 1) { - ((float*)pFramesOut)[iFrame*pGainer->config.channels + iChannel] = ((const float*)pFramesIn)[iFrame*pGainer->config.channels + iChannel] * pGainer->pNewGains[iChannel] * pGainer->masterVolume; - } - } - } - } - - /* Now that some frames have been processed we need to make sure future changes to the gain are interpolated. */ - if (pGainer->t == (ma_uint32)-1) { - pGainer->t = (ma_uint32)ma_min(pGainer->config.smoothTimeInFrames, frameCount); - } - -#if 0 - if (pGainer->t >= pGainer->config.smoothTimeInFrames) { - /* Fast path. No gain calculation required. */ - ma_copy_and_apply_volume_factor_per_channel_f32(pFramesOutF32, pFramesInF32, frameCount, pGainer->config.channels, pGainer->pNewGains); - ma_apply_volume_factor_f32(pFramesOutF32, frameCount * pGainer->config.channels, pGainer->masterVolume); - - /* Now that some frames have been processed we need to make sure future changes to the gain are interpolated. */ - if (pGainer->t == (ma_uint32)-1) { - pGainer->t = pGainer->config.smoothTimeInFrames; - } - } else { - /* Slow path. Need to interpolate the gain for each channel individually. */ - - /* We can allow the input and output buffers to be null in which case we'll just update the internal timer. */ - if (pFramesOut != NULL && pFramesIn != NULL) { - float a = (float)pGainer->t / pGainer->config.smoothTimeInFrames; - float d = 1.0f / pGainer->config.smoothTimeInFrames; - ma_uint32 channelCount = pGainer->config.channels; - - for (iFrame = 0; iFrame < frameCount; iFrame += 1) { - for (iChannel = 0; iChannel < channelCount; iChannel += 1) { - pFramesOutF32[iChannel] = pFramesInF32[iChannel] * ma_mix_f32_fast(pGainer->pOldGains[iChannel], pGainer->pNewGains[iChannel], a) * pGainer->masterVolume; - } - - pFramesOutF32 += channelCount; - pFramesInF32 += channelCount; - - a += d; - if (a > 1) { - a = 1; - } - } - } - - pGainer->t = (ma_uint32)ma_min(pGainer->t + frameCount, pGainer->config.smoothTimeInFrames); - - #if 0 /* Reference implementation. */ - for (iFrame = 0; iFrame < frameCount; iFrame += 1) { - /* We can allow the input and output buffers to be null in which case we'll just update the internal timer. */ - if (pFramesOut != NULL && pFramesIn != NULL) { - for (iChannel = 0; iChannel < pGainer->config.channels; iChannel += 1) { - pFramesOutF32[iFrame * pGainer->config.channels + iChannel] = pFramesInF32[iFrame * pGainer->config.channels + iChannel] * ma_gainer_calculate_current_gain(pGainer, iChannel) * pGainer->masterVolume; - } - } - - /* Move interpolation time forward, but don't go beyond our smoothing time. */ - pGainer->t = ma_min(pGainer->t + 1, pGainer->config.smoothTimeInFrames); - } - #endif - } -#endif - - return MA_SUCCESS; -} - -MA_API ma_result ma_gainer_process_pcm_frames(ma_gainer* pGainer, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount) -{ - if (pGainer == NULL) { - return MA_INVALID_ARGS; - } - - /* - ma_gainer_process_pcm_frames_internal() marks pFramesOut and pFramesIn with MA_RESTRICT which - helps with auto-vectorization. - */ - return ma_gainer_process_pcm_frames_internal(pGainer, pFramesOut, pFramesIn, frameCount); -} - -static void ma_gainer_set_gain_by_index(ma_gainer* pGainer, float newGain, ma_uint32 iChannel) -{ - pGainer->pOldGains[iChannel] = ma_gainer_calculate_current_gain(pGainer, iChannel); - pGainer->pNewGains[iChannel] = newGain; -} - -static void ma_gainer_reset_smoothing_time(ma_gainer* pGainer) -{ - if (pGainer->t == (ma_uint32)-1) { - pGainer->t = pGainer->config.smoothTimeInFrames; /* No smoothing required for initial gains setting. */ - } else { - pGainer->t = 0; - } -} - -MA_API ma_result ma_gainer_set_gain(ma_gainer* pGainer, float newGain) -{ - ma_uint32 iChannel; - - if (pGainer == NULL) { - return MA_INVALID_ARGS; - } - - for (iChannel = 0; iChannel < pGainer->config.channels; iChannel += 1) { - ma_gainer_set_gain_by_index(pGainer, newGain, iChannel); - } - - /* The smoothing time needs to be reset to ensure we always interpolate by the configured smoothing time, but only if it's not the first setting. */ - ma_gainer_reset_smoothing_time(pGainer); - - return MA_SUCCESS; -} - -MA_API ma_result ma_gainer_set_gains(ma_gainer* pGainer, float* pNewGains) -{ - ma_uint32 iChannel; - - if (pGainer == NULL || pNewGains == NULL) { - return MA_INVALID_ARGS; - } - - for (iChannel = 0; iChannel < pGainer->config.channels; iChannel += 1) { - ma_gainer_set_gain_by_index(pGainer, pNewGains[iChannel], iChannel); - } - - /* The smoothing time needs to be reset to ensure we always interpolate by the configured smoothing time, but only if it's not the first setting. */ - ma_gainer_reset_smoothing_time(pGainer); - - return MA_SUCCESS; -} - -MA_API ma_result ma_gainer_set_master_volume(ma_gainer* pGainer, float volume) -{ - if (pGainer == NULL) { - return MA_INVALID_ARGS; - } - - pGainer->masterVolume = volume; - - return MA_SUCCESS; -} - -MA_API ma_result ma_gainer_get_master_volume(const ma_gainer* pGainer, float* pVolume) -{ - if (pGainer == NULL || pVolume == NULL) { - return MA_INVALID_ARGS; - } - - *pVolume = pGainer->masterVolume; - - return MA_SUCCESS; -} - - -MA_API ma_panner_config ma_panner_config_init(ma_format format, ma_uint32 channels) -{ - ma_panner_config config; - - MA_ZERO_OBJECT(&config); - config.format = format; - config.channels = channels; - config.mode = ma_pan_mode_balance; /* Set to balancing mode by default because it's consistent with other audio engines and most likely what the caller is expecting. */ - config.pan = 0; - - return config; -} - - -MA_API ma_result ma_panner_init(const ma_panner_config* pConfig, ma_panner* pPanner) -{ - if (pPanner == NULL) { - return MA_INVALID_ARGS; - } - - MA_ZERO_OBJECT(pPanner); - - if (pConfig == NULL) { - return MA_INVALID_ARGS; - } - - pPanner->format = pConfig->format; - pPanner->channels = pConfig->channels; - pPanner->mode = pConfig->mode; - pPanner->pan = pConfig->pan; - - return MA_SUCCESS; -} - -static void ma_stereo_balance_pcm_frames_f32(float* pFramesOut, const float* pFramesIn, ma_uint64 frameCount, float pan) -{ - ma_uint64 iFrame; - - if (pan > 0) { - float factor = 1.0f - pan; - if (pFramesOut == pFramesIn) { - for (iFrame = 0; iFrame < frameCount; iFrame += 1) { - pFramesOut[iFrame*2 + 0] = pFramesIn[iFrame*2 + 0] * factor; - } - } else { - for (iFrame = 0; iFrame < frameCount; iFrame += 1) { - pFramesOut[iFrame*2 + 0] = pFramesIn[iFrame*2 + 0] * factor; - pFramesOut[iFrame*2 + 1] = pFramesIn[iFrame*2 + 1]; - } - } - } else { - float factor = 1.0f + pan; - if (pFramesOut == pFramesIn) { - for (iFrame = 0; iFrame < frameCount; iFrame += 1) { - pFramesOut[iFrame*2 + 1] = pFramesIn[iFrame*2 + 1] * factor; - } - } else { - for (iFrame = 0; iFrame < frameCount; iFrame += 1) { - pFramesOut[iFrame*2 + 0] = pFramesIn[iFrame*2 + 0]; - pFramesOut[iFrame*2 + 1] = pFramesIn[iFrame*2 + 1] * factor; - } - } - } -} - -static void ma_stereo_balance_pcm_frames(void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount, ma_format format, float pan) -{ - if (pan == 0) { - /* Fast path. No panning required. */ - if (pFramesOut == pFramesIn) { - /* No-op */ - } else { - ma_copy_pcm_frames(pFramesOut, pFramesIn, frameCount, format, 2); - } - - return; - } - - switch (format) { - case ma_format_f32: ma_stereo_balance_pcm_frames_f32((float*)pFramesOut, (float*)pFramesIn, frameCount, pan); break; - - /* Unknown format. Just copy. */ - default: - { - ma_copy_pcm_frames(pFramesOut, pFramesIn, frameCount, format, 2); - } break; - } -} - - -static void ma_stereo_pan_pcm_frames_f32(float* pFramesOut, const float* pFramesIn, ma_uint64 frameCount, float pan) -{ - ma_uint64 iFrame; - - if (pan > 0) { - float factorL0 = 1.0f - pan; - float factorL1 = 0.0f + pan; - - for (iFrame = 0; iFrame < frameCount; iFrame += 1) { - float sample0 = (pFramesIn[iFrame*2 + 0] * factorL0); - float sample1 = (pFramesIn[iFrame*2 + 0] * factorL1) + pFramesIn[iFrame*2 + 1]; - - pFramesOut[iFrame*2 + 0] = sample0; - pFramesOut[iFrame*2 + 1] = sample1; - } - } else { - float factorR0 = 0.0f - pan; - float factorR1 = 1.0f + pan; - - for (iFrame = 0; iFrame < frameCount; iFrame += 1) { - float sample0 = pFramesIn[iFrame*2 + 0] + (pFramesIn[iFrame*2 + 1] * factorR0); - float sample1 = (pFramesIn[iFrame*2 + 1] * factorR1); - - pFramesOut[iFrame*2 + 0] = sample0; - pFramesOut[iFrame*2 + 1] = sample1; - } - } -} - -static void ma_stereo_pan_pcm_frames(void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount, ma_format format, float pan) -{ - if (pan == 0) { - /* Fast path. No panning required. */ - if (pFramesOut == pFramesIn) { - /* No-op */ - } else { - ma_copy_pcm_frames(pFramesOut, pFramesIn, frameCount, format, 2); - } - - return; - } - - switch (format) { - case ma_format_f32: ma_stereo_pan_pcm_frames_f32((float*)pFramesOut, (float*)pFramesIn, frameCount, pan); break; - - /* Unknown format. Just copy. */ - default: - { - ma_copy_pcm_frames(pFramesOut, pFramesIn, frameCount, format, 2); - } break; - } -} - -MA_API ma_result ma_panner_process_pcm_frames(ma_panner* pPanner, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount) -{ - if (pPanner == NULL || pFramesOut == NULL || pFramesIn == NULL) { - return MA_INVALID_ARGS; - } - - if (pPanner->channels == 2) { - /* Stereo case. For now assume channel 0 is left and channel right is 1, but should probably add support for a channel map. */ - if (pPanner->mode == ma_pan_mode_balance) { - ma_stereo_balance_pcm_frames(pFramesOut, pFramesIn, frameCount, pPanner->format, pPanner->pan); - } else { - ma_stereo_pan_pcm_frames(pFramesOut, pFramesIn, frameCount, pPanner->format, pPanner->pan); - } - } else { - if (pPanner->channels == 1) { - /* Panning has no effect on mono streams. */ - ma_copy_pcm_frames(pFramesOut, pFramesIn, frameCount, pPanner->format, pPanner->channels); - } else { - /* For now we're not going to support non-stereo set ups. Not sure how I want to handle this case just yet. */ - ma_copy_pcm_frames(pFramesOut, pFramesIn, frameCount, pPanner->format, pPanner->channels); - } - } - - return MA_SUCCESS; -} - -MA_API void ma_panner_set_mode(ma_panner* pPanner, ma_pan_mode mode) -{ - if (pPanner == NULL) { - return; - } - - pPanner->mode = mode; -} - -MA_API ma_pan_mode ma_panner_get_mode(const ma_panner* pPanner) -{ - if (pPanner == NULL) { - return ma_pan_mode_balance; - } - - return pPanner->mode; -} - -MA_API void ma_panner_set_pan(ma_panner* pPanner, float pan) -{ - if (pPanner == NULL) { - return; - } - - pPanner->pan = ma_clamp(pan, -1.0f, 1.0f); -} - -MA_API float ma_panner_get_pan(const ma_panner* pPanner) -{ - if (pPanner == NULL) { - return 0; - } - - return pPanner->pan; -} - - - - -MA_API ma_fader_config ma_fader_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRate) -{ - ma_fader_config config; - - MA_ZERO_OBJECT(&config); - config.format = format; - config.channels = channels; - config.sampleRate = sampleRate; - - return config; -} - - -MA_API ma_result ma_fader_init(const ma_fader_config* pConfig, ma_fader* pFader) -{ - if (pFader == NULL) { - return MA_INVALID_ARGS; - } - - MA_ZERO_OBJECT(pFader); - - if (pConfig == NULL) { - return MA_INVALID_ARGS; - } - - /* Only f32 is supported for now. */ - if (pConfig->format != ma_format_f32) { - return MA_INVALID_ARGS; - } - - pFader->config = *pConfig; - pFader->volumeBeg = 1; - pFader->volumeEnd = 1; - pFader->lengthInFrames = 0; - pFader->cursorInFrames = 0; - - return MA_SUCCESS; -} - -MA_API ma_result ma_fader_process_pcm_frames(ma_fader* pFader, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount) -{ - if (pFader == NULL) { - return MA_INVALID_ARGS; - } - - /* If the cursor is still negative we need to just copy the absolute number of those frames, but no more than frameCount. */ - if (pFader->cursorInFrames < 0) { - ma_uint64 absCursorInFrames = (ma_uint64)0 - pFader->cursorInFrames; - if (absCursorInFrames > frameCount) { - absCursorInFrames = frameCount; - } - - ma_copy_pcm_frames(pFramesOut, pFramesIn, absCursorInFrames, pFader->config.format, pFader->config.channels); - - pFader->cursorInFrames += absCursorInFrames; - frameCount -= absCursorInFrames; - pFramesOut = ma_offset_ptr(pFramesOut, ma_get_bytes_per_frame(pFader->config.format, pFader->config.channels)*absCursorInFrames); - pFramesIn = ma_offset_ptr(pFramesIn, ma_get_bytes_per_frame(pFader->config.format, pFader->config.channels)*absCursorInFrames); - } - - if (pFader->cursorInFrames >= 0) { - /* - For now we need to clamp frameCount so that the cursor never overflows 32-bits. This is required for - the conversion to a float which we use for the linear interpolation. This might be changed later. - */ - if (frameCount + pFader->cursorInFrames > UINT_MAX) { - frameCount = UINT_MAX - pFader->cursorInFrames; - } - - /* Optimized path if volumeBeg and volumeEnd are equal. */ - if (pFader->volumeBeg == pFader->volumeEnd) { - if (pFader->volumeBeg == 1) { - /* Straight copy. */ - ma_copy_pcm_frames(pFramesOut, pFramesIn, frameCount, pFader->config.format, pFader->config.channels); - } else { - /* Copy with volume. */ - ma_copy_and_apply_volume_and_clip_pcm_frames(pFramesOut, pFramesIn, frameCount, pFader->config.format, pFader->config.channels, pFader->volumeBeg); - } - } else { - /* Slower path. Volumes are different, so may need to do an interpolation. */ - if ((ma_uint64)pFader->cursorInFrames >= pFader->lengthInFrames) { - /* Fast path. We've gone past the end of the fade period so just apply the end volume to all samples. */ - ma_copy_and_apply_volume_and_clip_pcm_frames(pFramesOut, pFramesIn, frameCount, pFader->config.format, pFader->config.channels, pFader->volumeEnd); - } else { - /* Slow path. This is where we do the actual fading. */ - ma_uint64 iFrame; - ma_uint32 iChannel; - - /* For now we only support f32. Support for other formats might be added later. */ - if (pFader->config.format == ma_format_f32) { - const float* pFramesInF32 = (const float*)pFramesIn; - /* */ float* pFramesOutF32 = ( float*)pFramesOut; - - for (iFrame = 0; iFrame < frameCount; iFrame += 1) { - float a = (ma_uint32)ma_min(pFader->cursorInFrames + iFrame, pFader->lengthInFrames) / (float)((ma_uint32)pFader->lengthInFrames); /* Safe cast due to the frameCount clamp at the top of this function. */ - float volume = ma_mix_f32_fast(pFader->volumeBeg, pFader->volumeEnd, a); - - for (iChannel = 0; iChannel < pFader->config.channels; iChannel += 1) { - pFramesOutF32[iFrame*pFader->config.channels + iChannel] = pFramesInF32[iFrame*pFader->config.channels + iChannel] * volume; - } - } - } else { - return MA_NOT_IMPLEMENTED; - } - } - } - } - - pFader->cursorInFrames += frameCount; - - return MA_SUCCESS; -} - -MA_API void ma_fader_get_data_format(const ma_fader* pFader, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate) -{ - if (pFader == NULL) { - return; - } - - if (pFormat != NULL) { - *pFormat = pFader->config.format; - } - - if (pChannels != NULL) { - *pChannels = pFader->config.channels; - } - - if (pSampleRate != NULL) { - *pSampleRate = pFader->config.sampleRate; - } -} - -MA_API void ma_fader_set_fade(ma_fader* pFader, float volumeBeg, float volumeEnd, ma_uint64 lengthInFrames) -{ - ma_fader_set_fade_ex(pFader, volumeBeg, volumeEnd, lengthInFrames, 0); -} - -MA_API void ma_fader_set_fade_ex(ma_fader* pFader, float volumeBeg, float volumeEnd, ma_uint64 lengthInFrames, ma_int64 startOffsetInFrames) -{ - if (pFader == NULL) { - return; - } - - /* If the volume is negative, use current volume. */ - if (volumeBeg < 0) { - volumeBeg = ma_fader_get_current_volume(pFader); - } - - /* - The length needs to be clamped to 32-bits due to how we convert it to a float for linear - interpolation reasons. I might change this requirement later, but for now it's not important. - */ - if (lengthInFrames > UINT_MAX) { - lengthInFrames = UINT_MAX; - } - - /* The start offset needs to be clamped to ensure it doesn't overflow a signed number. */ - if (startOffsetInFrames > INT_MAX) { - startOffsetInFrames = INT_MAX; - } - - pFader->volumeBeg = volumeBeg; - pFader->volumeEnd = volumeEnd; - pFader->lengthInFrames = lengthInFrames; - pFader->cursorInFrames = -startOffsetInFrames; -} - -MA_API float ma_fader_get_current_volume(const ma_fader* pFader) -{ - if (pFader == NULL) { - return 0.0f; - } - - /* Any frames prior to the start of the fade period will be at unfaded volume. */ - if (pFader->cursorInFrames < 0) { - return 1.0f; - } - - /* The current volume depends on the position of the cursor. */ - if (pFader->cursorInFrames == 0) { - return pFader->volumeBeg; - } else if ((ma_uint64)pFader->cursorInFrames >= pFader->lengthInFrames) { /* Safe case because the < 0 case was checked above. */ - return pFader->volumeEnd; - } else { - /* The cursor is somewhere inside the fading period. We can figure this out with a simple linear interpolation between volumeBeg and volumeEnd based on our cursor position. */ - return ma_mix_f32_fast(pFader->volumeBeg, pFader->volumeEnd, (ma_uint32)pFader->cursorInFrames / (float)((ma_uint32)pFader->lengthInFrames)); /* Safe cast to uint32 because we clamp it in ma_fader_process_pcm_frames(). */ - } -} - - - - - -MA_API ma_vec3f ma_vec3f_init_3f(float x, float y, float z) -{ - ma_vec3f v; - - v.x = x; - v.y = y; - v.z = z; - - return v; -} - -MA_API ma_vec3f ma_vec3f_sub(ma_vec3f a, ma_vec3f b) -{ - return ma_vec3f_init_3f( - a.x - b.x, - a.y - b.y, - a.z - b.z - ); -} - -MA_API ma_vec3f ma_vec3f_neg(ma_vec3f a) -{ - return ma_vec3f_init_3f( - -a.x, - -a.y, - -a.z - ); -} - -MA_API float ma_vec3f_dot(ma_vec3f a, ma_vec3f b) -{ - return a.x*b.x + a.y*b.y + a.z*b.z; -} - -MA_API float ma_vec3f_len2(ma_vec3f v) -{ - return ma_vec3f_dot(v, v); -} - -MA_API float ma_vec3f_len(ma_vec3f v) -{ - return (float)ma_sqrtd(ma_vec3f_len2(v)); -} - - - -MA_API float ma_vec3f_dist(ma_vec3f a, ma_vec3f b) -{ - return ma_vec3f_len(ma_vec3f_sub(a, b)); -} - -MA_API ma_vec3f ma_vec3f_normalize(ma_vec3f v) -{ - float invLen; - float len2 = ma_vec3f_len2(v); - if (len2 == 0) { - return ma_vec3f_init_3f(0, 0, 0); - } - - invLen = ma_rsqrtf(len2); - v.x *= invLen; - v.y *= invLen; - v.z *= invLen; - - return v; -} - -MA_API ma_vec3f ma_vec3f_cross(ma_vec3f a, ma_vec3f b) -{ - return ma_vec3f_init_3f( - a.y*b.z - a.z*b.y, - a.z*b.x - a.x*b.z, - a.x*b.y - a.y*b.x - ); -} - - -MA_API void ma_atomic_vec3f_init(ma_atomic_vec3f* v, ma_vec3f value) -{ - v->v = value; - v->lock = 0; /* Important this is initialized to 0. */ -} - -MA_API void ma_atomic_vec3f_set(ma_atomic_vec3f* v, ma_vec3f value) -{ - ma_spinlock_lock(&v->lock); - { - v->v = value; - } - ma_spinlock_unlock(&v->lock); -} - -MA_API ma_vec3f ma_atomic_vec3f_get(ma_atomic_vec3f* v) -{ - ma_vec3f r; - - ma_spinlock_lock(&v->lock); - { - r = v->v; - } - ma_spinlock_unlock(&v->lock); - - return r; -} - - - -static void ma_channel_map_apply_f32(float* pFramesOut, const ma_channel* pChannelMapOut, ma_uint32 channelsOut, const float* pFramesIn, const ma_channel* pChannelMapIn, ma_uint32 channelsIn, ma_uint64 frameCount, ma_channel_mix_mode mode, ma_mono_expansion_mode monoExpansionMode); -static ma_bool32 ma_is_spatial_channel_position(ma_channel channelPosition); - - -#ifndef MA_DEFAULT_SPEED_OF_SOUND -#define MA_DEFAULT_SPEED_OF_SOUND 343.3f -#endif - -/* -These vectors represent the direction that speakers are facing from the center point. They're used -for panning in the spatializer. Must be normalized. -*/ -static ma_vec3f g_maChannelDirections[MA_CHANNEL_POSITION_COUNT] = { - { 0.0f, 0.0f, -1.0f }, /* MA_CHANNEL_NONE */ - { 0.0f, 0.0f, -1.0f }, /* MA_CHANNEL_MONO */ - {-0.7071f, 0.0f, -0.7071f }, /* MA_CHANNEL_FRONT_LEFT */ - {+0.7071f, 0.0f, -0.7071f }, /* MA_CHANNEL_FRONT_RIGHT */ - { 0.0f, 0.0f, -1.0f }, /* MA_CHANNEL_FRONT_CENTER */ - { 0.0f, 0.0f, -1.0f }, /* MA_CHANNEL_LFE */ - {-0.7071f, 0.0f, +0.7071f }, /* MA_CHANNEL_BACK_LEFT */ - {+0.7071f, 0.0f, +0.7071f }, /* MA_CHANNEL_BACK_RIGHT */ - {-0.3162f, 0.0f, -0.9487f }, /* MA_CHANNEL_FRONT_LEFT_CENTER */ - {+0.3162f, 0.0f, -0.9487f }, /* MA_CHANNEL_FRONT_RIGHT_CENTER */ - { 0.0f, 0.0f, +1.0f }, /* MA_CHANNEL_BACK_CENTER */ - {-1.0f, 0.0f, 0.0f }, /* MA_CHANNEL_SIDE_LEFT */ - {+1.0f, 0.0f, 0.0f }, /* MA_CHANNEL_SIDE_RIGHT */ - { 0.0f, +1.0f, 0.0f }, /* MA_CHANNEL_TOP_CENTER */ - {-0.5774f, +0.5774f, -0.5774f }, /* MA_CHANNEL_TOP_FRONT_LEFT */ - { 0.0f, +0.7071f, -0.7071f }, /* MA_CHANNEL_TOP_FRONT_CENTER */ - {+0.5774f, +0.5774f, -0.5774f }, /* MA_CHANNEL_TOP_FRONT_RIGHT */ - {-0.5774f, +0.5774f, +0.5774f }, /* MA_CHANNEL_TOP_BACK_LEFT */ - { 0.0f, +0.7071f, +0.7071f }, /* MA_CHANNEL_TOP_BACK_CENTER */ - {+0.5774f, +0.5774f, +0.5774f }, /* MA_CHANNEL_TOP_BACK_RIGHT */ - { 0.0f, 0.0f, -1.0f }, /* MA_CHANNEL_AUX_0 */ - { 0.0f, 0.0f, -1.0f }, /* MA_CHANNEL_AUX_1 */ - { 0.0f, 0.0f, -1.0f }, /* MA_CHANNEL_AUX_2 */ - { 0.0f, 0.0f, -1.0f }, /* MA_CHANNEL_AUX_3 */ - { 0.0f, 0.0f, -1.0f }, /* MA_CHANNEL_AUX_4 */ - { 0.0f, 0.0f, -1.0f }, /* MA_CHANNEL_AUX_5 */ - { 0.0f, 0.0f, -1.0f }, /* MA_CHANNEL_AUX_6 */ - { 0.0f, 0.0f, -1.0f }, /* MA_CHANNEL_AUX_7 */ - { 0.0f, 0.0f, -1.0f }, /* MA_CHANNEL_AUX_8 */ - { 0.0f, 0.0f, -1.0f }, /* MA_CHANNEL_AUX_9 */ - { 0.0f, 0.0f, -1.0f }, /* MA_CHANNEL_AUX_10 */ - { 0.0f, 0.0f, -1.0f }, /* MA_CHANNEL_AUX_11 */ - { 0.0f, 0.0f, -1.0f }, /* MA_CHANNEL_AUX_12 */ - { 0.0f, 0.0f, -1.0f }, /* MA_CHANNEL_AUX_13 */ - { 0.0f, 0.0f, -1.0f }, /* MA_CHANNEL_AUX_14 */ - { 0.0f, 0.0f, -1.0f }, /* MA_CHANNEL_AUX_15 */ - { 0.0f, 0.0f, -1.0f }, /* MA_CHANNEL_AUX_16 */ - { 0.0f, 0.0f, -1.0f }, /* MA_CHANNEL_AUX_17 */ - { 0.0f, 0.0f, -1.0f }, /* MA_CHANNEL_AUX_18 */ - { 0.0f, 0.0f, -1.0f }, /* MA_CHANNEL_AUX_19 */ - { 0.0f, 0.0f, -1.0f }, /* MA_CHANNEL_AUX_20 */ - { 0.0f, 0.0f, -1.0f }, /* MA_CHANNEL_AUX_21 */ - { 0.0f, 0.0f, -1.0f }, /* MA_CHANNEL_AUX_22 */ - { 0.0f, 0.0f, -1.0f }, /* MA_CHANNEL_AUX_23 */ - { 0.0f, 0.0f, -1.0f }, /* MA_CHANNEL_AUX_24 */ - { 0.0f, 0.0f, -1.0f }, /* MA_CHANNEL_AUX_25 */ - { 0.0f, 0.0f, -1.0f }, /* MA_CHANNEL_AUX_26 */ - { 0.0f, 0.0f, -1.0f }, /* MA_CHANNEL_AUX_27 */ - { 0.0f, 0.0f, -1.0f }, /* MA_CHANNEL_AUX_28 */ - { 0.0f, 0.0f, -1.0f }, /* MA_CHANNEL_AUX_29 */ - { 0.0f, 0.0f, -1.0f }, /* MA_CHANNEL_AUX_30 */ - { 0.0f, 0.0f, -1.0f } /* MA_CHANNEL_AUX_31 */ -}; - -static ma_vec3f ma_get_channel_direction(ma_channel channel) -{ - if (channel >= MA_CHANNEL_POSITION_COUNT) { - return ma_vec3f_init_3f(0, 0, -1); - } else { - return g_maChannelDirections[channel]; - } -} - - - -static float ma_attenuation_inverse(float distance, float minDistance, float maxDistance, float rolloff) -{ - if (minDistance >= maxDistance) { - return 1; /* To avoid division by zero. Do not attenuate. */ - } - - return minDistance / (minDistance + rolloff * (ma_clamp(distance, minDistance, maxDistance) - minDistance)); -} - -static float ma_attenuation_linear(float distance, float minDistance, float maxDistance, float rolloff) -{ - if (minDistance >= maxDistance) { - return 1; /* To avoid division by zero. Do not attenuate. */ - } - - return 1 - rolloff * (ma_clamp(distance, minDistance, maxDistance) - minDistance) / (maxDistance - minDistance); -} - -static float ma_attenuation_exponential(float distance, float minDistance, float maxDistance, float rolloff) -{ - if (minDistance >= maxDistance) { - return 1; /* To avoid division by zero. Do not attenuate. */ - } - - return (float)ma_powd(ma_clamp(distance, minDistance, maxDistance) / minDistance, -rolloff); -} - - -/* -Doppler Effect calculation taken from the OpenAL spec, with two main differences: - - 1) The source to listener vector will have already been calculated at an earlier step so we can - just use that directly. We need only the position of the source relative to the origin. - - 2) We don't scale by a frequency because we actually just want the ratio which we'll plug straight - into the resampler directly. -*/ -static float ma_doppler_pitch(ma_vec3f relativePosition, ma_vec3f sourceVelocity, ma_vec3f listenVelocity, float speedOfSound, float dopplerFactor) -{ - float len; - float vls; - float vss; - - len = ma_vec3f_len(relativePosition); - - /* - There's a case where the position of the source will be right on top of the listener in which - case the length will be 0 and we'll end up with a division by zero. We can just return a ratio - of 1.0 in this case. This is not considered in the OpenAL spec, but is necessary. - */ - if (len == 0) { - return 1.0; - } - - vls = ma_vec3f_dot(relativePosition, listenVelocity) / len; - vss = ma_vec3f_dot(relativePosition, sourceVelocity) / len; - - vls = ma_min(vls, speedOfSound / dopplerFactor); - vss = ma_min(vss, speedOfSound / dopplerFactor); - - return (speedOfSound - dopplerFactor*vls) / (speedOfSound - dopplerFactor*vss); -} - - -static void ma_get_default_channel_map_for_spatializer(ma_channel* pChannelMap, size_t channelMapCap, ma_uint32 channelCount) -{ - /* - Special case for stereo. Want to default the left and right speakers to side left and side - right so that they're facing directly down the X axis rather than slightly forward. Not - doing this will result in sounds being quieter when behind the listener. This might - actually be good for some scenarios, but I don't think it's an appropriate default because - it can be a bit unexpected. - */ - if (channelCount == 2) { - pChannelMap[0] = MA_CHANNEL_SIDE_LEFT; - pChannelMap[1] = MA_CHANNEL_SIDE_RIGHT; - } else { - ma_channel_map_init_standard(ma_standard_channel_map_default, pChannelMap, channelMapCap, channelCount); - } -} - - -MA_API ma_spatializer_listener_config ma_spatializer_listener_config_init(ma_uint32 channelsOut) -{ - ma_spatializer_listener_config config; - - MA_ZERO_OBJECT(&config); - config.channelsOut = channelsOut; - config.pChannelMapOut = NULL; - config.handedness = ma_handedness_right; - config.worldUp = ma_vec3f_init_3f(0, 1, 0); - config.coneInnerAngleInRadians = 6.283185f; /* 360 degrees. */ - config.coneOuterAngleInRadians = 6.283185f; /* 360 degrees. */ - config.coneOuterGain = 0; - config.speedOfSound = 343.3f; /* Same as OpenAL. Used for doppler effect. */ - - return config; -} - - -typedef struct -{ - size_t sizeInBytes; - size_t channelMapOutOffset; -} ma_spatializer_listener_heap_layout; - -static ma_result ma_spatializer_listener_get_heap_layout(const ma_spatializer_listener_config* pConfig, ma_spatializer_listener_heap_layout* pHeapLayout) -{ - MA_ASSERT(pHeapLayout != NULL); - - MA_ZERO_OBJECT(pHeapLayout); - - if (pConfig == NULL) { - return MA_INVALID_ARGS; - } - - if (pConfig->channelsOut == 0) { - return MA_INVALID_ARGS; - } - - pHeapLayout->sizeInBytes = 0; - - /* Channel map. We always need this, even for passthroughs. */ - pHeapLayout->channelMapOutOffset = pHeapLayout->sizeInBytes; - pHeapLayout->sizeInBytes += ma_align_64(sizeof(*pConfig->pChannelMapOut) * pConfig->channelsOut); - - return MA_SUCCESS; -} - - -MA_API ma_result ma_spatializer_listener_get_heap_size(const ma_spatializer_listener_config* pConfig, size_t* pHeapSizeInBytes) -{ - ma_result result; - ma_spatializer_listener_heap_layout heapLayout; - - if (pHeapSizeInBytes == NULL) { - return MA_INVALID_ARGS; - } - - *pHeapSizeInBytes = 0; - - result = ma_spatializer_listener_get_heap_layout(pConfig, &heapLayout); - if (result != MA_SUCCESS) { - return result; - } - - *pHeapSizeInBytes = heapLayout.sizeInBytes; - - return MA_SUCCESS; -} - -MA_API ma_result ma_spatializer_listener_init_preallocated(const ma_spatializer_listener_config* pConfig, void* pHeap, ma_spatializer_listener* pListener) -{ - ma_result result; - ma_spatializer_listener_heap_layout heapLayout; - - if (pListener == NULL) { - return MA_INVALID_ARGS; - } - - MA_ZERO_OBJECT(pListener); - - result = ma_spatializer_listener_get_heap_layout(pConfig, &heapLayout); - if (result != MA_SUCCESS) { - return result; - } - - pListener->_pHeap = pHeap; - MA_ZERO_MEMORY(pHeap, heapLayout.sizeInBytes); - - pListener->config = *pConfig; - ma_atomic_vec3f_init(&pListener->position, ma_vec3f_init_3f(0, 0, 0)); - ma_atomic_vec3f_init(&pListener->direction, ma_vec3f_init_3f(0, 0, -1)); - ma_atomic_vec3f_init(&pListener->velocity, ma_vec3f_init_3f(0, 0, 0)); - pListener->isEnabled = MA_TRUE; - - /* Swap the forward direction if we're left handed (it was initialized based on right handed). */ - if (pListener->config.handedness == ma_handedness_left) { - ma_vec3f negDir = ma_vec3f_neg(ma_spatializer_listener_get_direction(pListener)); - ma_spatializer_listener_set_direction(pListener, negDir.x, negDir.y, negDir.z); - } - - - /* We must always have a valid channel map. */ - pListener->config.pChannelMapOut = (ma_channel*)ma_offset_ptr(pHeap, heapLayout.channelMapOutOffset); - - /* Use a slightly different default channel map for stereo. */ - if (pConfig->pChannelMapOut == NULL) { - ma_get_default_channel_map_for_spatializer(pListener->config.pChannelMapOut, pConfig->channelsOut, pConfig->channelsOut); - } else { - ma_channel_map_copy_or_default(pListener->config.pChannelMapOut, pConfig->channelsOut, pConfig->pChannelMapOut, pConfig->channelsOut); - } - - return MA_SUCCESS; -} - -MA_API ma_result ma_spatializer_listener_init(const ma_spatializer_listener_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_spatializer_listener* pListener) -{ - ma_result result; - size_t heapSizeInBytes; - void* pHeap; - - result = ma_spatializer_listener_get_heap_size(pConfig, &heapSizeInBytes); - if (result != MA_SUCCESS) { - return result; - } - - if (heapSizeInBytes > 0) { - pHeap = ma_malloc(heapSizeInBytes, pAllocationCallbacks); - if (pHeap == NULL) { - return MA_OUT_OF_MEMORY; - } - } else { - pHeap = NULL; - } - - result = ma_spatializer_listener_init_preallocated(pConfig, pHeap, pListener); - if (result != MA_SUCCESS) { - ma_free(pHeap, pAllocationCallbacks); - return result; - } - - pListener->_ownsHeap = MA_TRUE; - return MA_SUCCESS; -} - -MA_API void ma_spatializer_listener_uninit(ma_spatializer_listener* pListener, const ma_allocation_callbacks* pAllocationCallbacks) -{ - if (pListener == NULL) { - return; - } - - if (pListener->_ownsHeap) { - ma_free(pListener->_pHeap, pAllocationCallbacks); - } -} - -MA_API ma_channel* ma_spatializer_listener_get_channel_map(ma_spatializer_listener* pListener) -{ - if (pListener == NULL) { - return NULL; - } - - return pListener->config.pChannelMapOut; -} - -MA_API void ma_spatializer_listener_set_cone(ma_spatializer_listener* pListener, float innerAngleInRadians, float outerAngleInRadians, float outerGain) -{ - if (pListener == NULL) { - return; - } - - pListener->config.coneInnerAngleInRadians = innerAngleInRadians; - pListener->config.coneOuterAngleInRadians = outerAngleInRadians; - pListener->config.coneOuterGain = outerGain; -} - -MA_API void ma_spatializer_listener_get_cone(const ma_spatializer_listener* pListener, float* pInnerAngleInRadians, float* pOuterAngleInRadians, float* pOuterGain) -{ - if (pListener == NULL) { - return; - } - - if (pInnerAngleInRadians != NULL) { - *pInnerAngleInRadians = pListener->config.coneInnerAngleInRadians; - } - - if (pOuterAngleInRadians != NULL) { - *pOuterAngleInRadians = pListener->config.coneOuterAngleInRadians; - } - - if (pOuterGain != NULL) { - *pOuterGain = pListener->config.coneOuterGain; - } -} - -MA_API void ma_spatializer_listener_set_position(ma_spatializer_listener* pListener, float x, float y, float z) -{ - if (pListener == NULL) { - return; - } - - ma_atomic_vec3f_set(&pListener->position, ma_vec3f_init_3f(x, y, z)); -} - -MA_API ma_vec3f ma_spatializer_listener_get_position(const ma_spatializer_listener* pListener) -{ - if (pListener == NULL) { - return ma_vec3f_init_3f(0, 0, 0); - } - - return ma_atomic_vec3f_get((ma_atomic_vec3f*)&pListener->position); /* Naughty const-cast. It's just for atomically loading the vec3 which should be safe. */ -} - -MA_API void ma_spatializer_listener_set_direction(ma_spatializer_listener* pListener, float x, float y, float z) -{ - if (pListener == NULL) { - return; - } - - ma_atomic_vec3f_set(&pListener->direction, ma_vec3f_init_3f(x, y, z)); -} - -MA_API ma_vec3f ma_spatializer_listener_get_direction(const ma_spatializer_listener* pListener) -{ - if (pListener == NULL) { - return ma_vec3f_init_3f(0, 0, -1); - } - - return ma_atomic_vec3f_get((ma_atomic_vec3f*)&pListener->direction); /* Naughty const-cast. It's just for atomically loading the vec3 which should be safe. */ -} - -MA_API void ma_spatializer_listener_set_velocity(ma_spatializer_listener* pListener, float x, float y, float z) -{ - if (pListener == NULL) { - return; - } - - ma_atomic_vec3f_set(&pListener->velocity, ma_vec3f_init_3f(x, y, z)); -} - -MA_API ma_vec3f ma_spatializer_listener_get_velocity(const ma_spatializer_listener* pListener) -{ - if (pListener == NULL) { - return ma_vec3f_init_3f(0, 0, 0); - } - - return ma_atomic_vec3f_get((ma_atomic_vec3f*)&pListener->velocity); /* Naughty const-cast. It's just for atomically loading the vec3 which should be safe. */ -} - -MA_API void ma_spatializer_listener_set_speed_of_sound(ma_spatializer_listener* pListener, float speedOfSound) -{ - if (pListener == NULL) { - return; - } - - pListener->config.speedOfSound = speedOfSound; -} - -MA_API float ma_spatializer_listener_get_speed_of_sound(const ma_spatializer_listener* pListener) -{ - if (pListener == NULL) { - return 0; - } - - return pListener->config.speedOfSound; -} - -MA_API void ma_spatializer_listener_set_world_up(ma_spatializer_listener* pListener, float x, float y, float z) -{ - if (pListener == NULL) { - return; - } - - pListener->config.worldUp = ma_vec3f_init_3f(x, y, z); -} - -MA_API ma_vec3f ma_spatializer_listener_get_world_up(const ma_spatializer_listener* pListener) -{ - if (pListener == NULL) { - return ma_vec3f_init_3f(0, 1, 0); - } - - return pListener->config.worldUp; -} - -MA_API void ma_spatializer_listener_set_enabled(ma_spatializer_listener* pListener, ma_bool32 isEnabled) -{ - if (pListener == NULL) { - return; - } - - pListener->isEnabled = isEnabled; -} - -MA_API ma_bool32 ma_spatializer_listener_is_enabled(const ma_spatializer_listener* pListener) -{ - if (pListener == NULL) { - return MA_FALSE; - } - - return pListener->isEnabled; -} - - - - -MA_API ma_spatializer_config ma_spatializer_config_init(ma_uint32 channelsIn, ma_uint32 channelsOut) -{ - ma_spatializer_config config; - - MA_ZERO_OBJECT(&config); - config.channelsIn = channelsIn; - config.channelsOut = channelsOut; - config.pChannelMapIn = NULL; - config.attenuationModel = ma_attenuation_model_inverse; - config.positioning = ma_positioning_absolute; - config.handedness = ma_handedness_right; - config.minGain = 0; - config.maxGain = 1; - config.minDistance = 1; - config.maxDistance = MA_FLT_MAX; - config.rolloff = 1; - config.coneInnerAngleInRadians = 6.283185f; /* 360 degrees. */ - config.coneOuterAngleInRadians = 6.283185f; /* 360 degrees. */ - config.coneOuterGain = 0.0f; - config.dopplerFactor = 1; - config.directionalAttenuationFactor = 1; - config.minSpatializationChannelGain = 0.2f; - config.gainSmoothTimeInFrames = 360; /* 7.5ms @ 48K. */ - - return config; -} - - -static ma_gainer_config ma_spatializer_gainer_config_init(const ma_spatializer_config* pConfig) -{ - MA_ASSERT(pConfig != NULL); - return ma_gainer_config_init(pConfig->channelsOut, pConfig->gainSmoothTimeInFrames); -} - -static ma_result ma_spatializer_validate_config(const ma_spatializer_config* pConfig) -{ - MA_ASSERT(pConfig != NULL); - - if (pConfig->channelsIn == 0 || pConfig->channelsOut == 0) { - return MA_INVALID_ARGS; - } - - return MA_SUCCESS; -} - -typedef struct -{ - size_t sizeInBytes; - size_t channelMapInOffset; - size_t newChannelGainsOffset; - size_t gainerOffset; -} ma_spatializer_heap_layout; - -static ma_result ma_spatializer_get_heap_layout(const ma_spatializer_config* pConfig, ma_spatializer_heap_layout* pHeapLayout) -{ - ma_result result; - - MA_ASSERT(pHeapLayout != NULL); - - MA_ZERO_OBJECT(pHeapLayout); - - if (pConfig == NULL) { - return MA_INVALID_ARGS; - } - - result = ma_spatializer_validate_config(pConfig); - if (result != MA_SUCCESS) { - return result; - } - - pHeapLayout->sizeInBytes = 0; - - /* Channel map. */ - pHeapLayout->channelMapInOffset = MA_SIZE_MAX; /* <-- MA_SIZE_MAX indicates no allocation necessary. */ - if (pConfig->pChannelMapIn != NULL) { - pHeapLayout->channelMapInOffset = pHeapLayout->sizeInBytes; - pHeapLayout->sizeInBytes += ma_align_64(sizeof(*pConfig->pChannelMapIn) * pConfig->channelsIn); - } - - /* New channel gains for output. */ - pHeapLayout->newChannelGainsOffset = pHeapLayout->sizeInBytes; - pHeapLayout->sizeInBytes += ma_align_64(sizeof(float) * pConfig->channelsOut); - - /* Gainer. */ - { - size_t gainerHeapSizeInBytes; - ma_gainer_config gainerConfig; - - gainerConfig = ma_spatializer_gainer_config_init(pConfig); - - result = ma_gainer_get_heap_size(&gainerConfig, &gainerHeapSizeInBytes); - if (result != MA_SUCCESS) { - return result; - } - - pHeapLayout->gainerOffset = pHeapLayout->sizeInBytes; - pHeapLayout->sizeInBytes += ma_align_64(gainerHeapSizeInBytes); - } - - return MA_SUCCESS; -} - -MA_API ma_result ma_spatializer_get_heap_size(const ma_spatializer_config* pConfig, size_t* pHeapSizeInBytes) -{ - ma_result result; - ma_spatializer_heap_layout heapLayout; - - if (pHeapSizeInBytes == NULL) { - return MA_INVALID_ARGS; - } - - *pHeapSizeInBytes = 0; /* Safety. */ - - result = ma_spatializer_get_heap_layout(pConfig, &heapLayout); - if (result != MA_SUCCESS) { - return result; - } - - *pHeapSizeInBytes = heapLayout.sizeInBytes; - - return MA_SUCCESS; -} - - -MA_API ma_result ma_spatializer_init_preallocated(const ma_spatializer_config* pConfig, void* pHeap, ma_spatializer* pSpatializer) -{ - ma_result result; - ma_spatializer_heap_layout heapLayout; - ma_gainer_config gainerConfig; - - if (pSpatializer == NULL) { - return MA_INVALID_ARGS; - } - - MA_ZERO_OBJECT(pSpatializer); - - if (pConfig == NULL || pHeap == NULL) { - return MA_INVALID_ARGS; - } - - result = ma_spatializer_get_heap_layout(pConfig, &heapLayout); - if (result != MA_SUCCESS) { - return result; - } - - pSpatializer->_pHeap = pHeap; - MA_ZERO_MEMORY(pHeap, heapLayout.sizeInBytes); - - pSpatializer->channelsIn = pConfig->channelsIn; - pSpatializer->channelsOut = pConfig->channelsOut; - pSpatializer->attenuationModel = pConfig->attenuationModel; - pSpatializer->positioning = pConfig->positioning; - pSpatializer->handedness = pConfig->handedness; - pSpatializer->minGain = pConfig->minGain; - pSpatializer->maxGain = pConfig->maxGain; - pSpatializer->minDistance = pConfig->minDistance; - pSpatializer->maxDistance = pConfig->maxDistance; - pSpatializer->rolloff = pConfig->rolloff; - pSpatializer->coneInnerAngleInRadians = pConfig->coneInnerAngleInRadians; - pSpatializer->coneOuterAngleInRadians = pConfig->coneOuterAngleInRadians; - pSpatializer->coneOuterGain = pConfig->coneOuterGain; - pSpatializer->dopplerFactor = pConfig->dopplerFactor; - pSpatializer->minSpatializationChannelGain = pConfig->minSpatializationChannelGain; - pSpatializer->directionalAttenuationFactor = pConfig->directionalAttenuationFactor; - pSpatializer->gainSmoothTimeInFrames = pConfig->gainSmoothTimeInFrames; - ma_atomic_vec3f_init(&pSpatializer->position, ma_vec3f_init_3f(0, 0, 0)); - ma_atomic_vec3f_init(&pSpatializer->direction, ma_vec3f_init_3f(0, 0, -1)); - ma_atomic_vec3f_init(&pSpatializer->velocity, ma_vec3f_init_3f(0, 0, 0)); - pSpatializer->dopplerPitch = 1; - - /* Swap the forward direction if we're left handed (it was initialized based on right handed). */ - if (pSpatializer->handedness == ma_handedness_left) { - ma_vec3f negDir = ma_vec3f_neg(ma_spatializer_get_direction(pSpatializer)); - ma_spatializer_set_direction(pSpatializer, negDir.x, negDir.y, negDir.z); - } - - /* Channel map. This will be on the heap. */ - if (pConfig->pChannelMapIn != NULL) { - pSpatializer->pChannelMapIn = (ma_channel*)ma_offset_ptr(pHeap, heapLayout.channelMapInOffset); - ma_channel_map_copy_or_default(pSpatializer->pChannelMapIn, pSpatializer->channelsIn, pConfig->pChannelMapIn, pSpatializer->channelsIn); - } - - /* New channel gains for output channels. */ - pSpatializer->pNewChannelGainsOut = (float*)ma_offset_ptr(pHeap, heapLayout.newChannelGainsOffset); - - /* Gainer. */ - gainerConfig = ma_spatializer_gainer_config_init(pConfig); - - result = ma_gainer_init_preallocated(&gainerConfig, ma_offset_ptr(pHeap, heapLayout.gainerOffset), &pSpatializer->gainer); - if (result != MA_SUCCESS) { - return result; /* Failed to initialize the gainer. */ - } - - return MA_SUCCESS; -} - -MA_API ma_result ma_spatializer_init(const ma_spatializer_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_spatializer* pSpatializer) -{ - ma_result result; - size_t heapSizeInBytes; - void* pHeap; - - /* We'll need a heap allocation to retrieve the size. */ - result = ma_spatializer_get_heap_size(pConfig, &heapSizeInBytes); - if (result != MA_SUCCESS) { - return result; - } - - if (heapSizeInBytes > 0) { - pHeap = ma_malloc(heapSizeInBytes, pAllocationCallbacks); - if (pHeap == NULL) { - return MA_OUT_OF_MEMORY; - } - } else { - pHeap = NULL; - } - - result = ma_spatializer_init_preallocated(pConfig, pHeap, pSpatializer); - if (result != MA_SUCCESS) { - ma_free(pHeap, pAllocationCallbacks); - return result; - } - - pSpatializer->_ownsHeap = MA_TRUE; - return MA_SUCCESS; -} - -MA_API void ma_spatializer_uninit(ma_spatializer* pSpatializer, const ma_allocation_callbacks* pAllocationCallbacks) -{ - if (pSpatializer == NULL) { - return; - } - - ma_gainer_uninit(&pSpatializer->gainer, pAllocationCallbacks); - - if (pSpatializer->_ownsHeap) { - ma_free(pSpatializer->_pHeap, pAllocationCallbacks); - } -} - -static float ma_calculate_angular_gain(ma_vec3f dirA, ma_vec3f dirB, float coneInnerAngleInRadians, float coneOuterAngleInRadians, float coneOuterGain) -{ - /* - Angular attenuation. - - Unlike distance gain, the math for this is not specified by the OpenAL spec so we'll just go ahead and figure - this out for ourselves at the expense of possibly being inconsistent with other implementations. - - To do cone attenuation, I'm just using the same math that we'd use to implement a basic spotlight in OpenGL. We - just need to get the direction from the source to the listener and then do a dot product against that and the - direction of the spotlight. Then we just compare that dot product against the cosine of the inner and outer - angles. If the dot product is greater than the outer angle, we just use coneOuterGain. If it's less than - the inner angle, we just use a gain of 1. Otherwise we linearly interpolate between 1 and coneOuterGain. - */ - if (coneInnerAngleInRadians < 6.283185f) { - float angularGain = 1; - float cutoffInner = (float)ma_cosd(coneInnerAngleInRadians*0.5f); - float cutoffOuter = (float)ma_cosd(coneOuterAngleInRadians*0.5f); - float d; - - d = ma_vec3f_dot(dirA, dirB); - - if (d > cutoffInner) { - /* It's inside the inner angle. */ - angularGain = 1; - } else { - /* It's outside the inner angle. */ - if (d > cutoffOuter) { - /* It's between the inner and outer angle. We need to linearly interpolate between 1 and coneOuterGain. */ - angularGain = ma_mix_f32(coneOuterGain, 1, (d - cutoffOuter) / (cutoffInner - cutoffOuter)); - } else { - /* It's outside the outer angle. */ - angularGain = coneOuterGain; - } - } - - /*printf("d = %f; cutoffInner = %f; cutoffOuter = %f; angularGain = %f\n", d, cutoffInner, cutoffOuter, angularGain);*/ - return angularGain; - } else { - /* Inner angle is 360 degrees so no need to do any attenuation. */ - return 1; - } -} - -MA_API ma_result ma_spatializer_process_pcm_frames(ma_spatializer* pSpatializer, ma_spatializer_listener* pListener, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount) -{ - ma_channel* pChannelMapIn = pSpatializer->pChannelMapIn; - ma_channel* pChannelMapOut = pListener->config.pChannelMapOut; - - if (pSpatializer == NULL) { - return MA_INVALID_ARGS; - } - - /* If we're not spatializing we need to run an optimized path. */ - if (ma_atomic_load_i32(&pSpatializer->attenuationModel) == ma_attenuation_model_none) { - if (ma_spatializer_listener_is_enabled(pListener)) { - /* No attenuation is required, but we'll need to do some channel conversion. */ - if (pSpatializer->channelsIn == pSpatializer->channelsOut) { - ma_copy_pcm_frames(pFramesOut, pFramesIn, frameCount, ma_format_f32, pSpatializer->channelsIn); - } else { - ma_channel_map_apply_f32((float*)pFramesOut, pChannelMapOut, pSpatializer->channelsOut, (const float*)pFramesIn, pChannelMapIn, pSpatializer->channelsIn, frameCount, ma_channel_mix_mode_rectangular, ma_mono_expansion_mode_default); /* Safe casts to float* because f32 is the only supported format. */ - } - } else { - /* The listener is disabled. Output silence. */ - ma_silence_pcm_frames(pFramesOut, frameCount, ma_format_f32, pSpatializer->channelsOut); - } - - /* - We're not doing attenuation so don't bother with doppler for now. I'm not sure if this is - the correct thinking so might need to review this later. - */ - pSpatializer->dopplerPitch = 1; - } else { - /* - Let's first determine which listener the sound is closest to. Need to keep in mind that we - might not have a world or any listeners, in which case we just spatializer based on the - listener being positioned at the origin (0, 0, 0). - */ - ma_vec3f relativePosNormalized; - ma_vec3f relativePos; /* The position relative to the listener. */ - ma_vec3f relativeDir; /* The direction of the sound, relative to the listener. */ - ma_vec3f listenerVel; /* The velocity of the listener. For doppler pitch calculation. */ - float speedOfSound; - float distance = 0; - float gain = 1; - ma_uint32 iChannel; - const ma_uint32 channelsOut = pSpatializer->channelsOut; - const ma_uint32 channelsIn = pSpatializer->channelsIn; - float minDistance = ma_spatializer_get_min_distance(pSpatializer); - float maxDistance = ma_spatializer_get_max_distance(pSpatializer); - float rolloff = ma_spatializer_get_rolloff(pSpatializer); - float dopplerFactor = ma_spatializer_get_doppler_factor(pSpatializer); - - /* - We'll need the listener velocity for doppler pitch calculations. The speed of sound is - defined by the listener, so we'll grab that here too. - */ - if (pListener != NULL) { - listenerVel = ma_spatializer_listener_get_velocity(pListener); - speedOfSound = pListener->config.speedOfSound; - } else { - listenerVel = ma_vec3f_init_3f(0, 0, 0); - speedOfSound = MA_DEFAULT_SPEED_OF_SOUND; - } - - if (pListener == NULL || ma_spatializer_get_positioning(pSpatializer) == ma_positioning_relative) { - /* There's no listener or we're using relative positioning. */ - relativePos = ma_spatializer_get_position(pSpatializer); - relativeDir = ma_spatializer_get_direction(pSpatializer); - } else { - /* - We've found a listener and we're using absolute positioning. We need to transform the - sound's position and direction so that it's relative to listener. Later on we'll use - this for determining the factors to apply to each channel to apply the panning effect. - */ - ma_spatializer_get_relative_position_and_direction(pSpatializer, pListener, &relativePos, &relativeDir); - } - - distance = ma_vec3f_len(relativePos); - - /* We've gathered the data, so now we can apply some spatialization. */ - switch (ma_spatializer_get_attenuation_model(pSpatializer)) { - case ma_attenuation_model_inverse: - { - gain = ma_attenuation_inverse(distance, minDistance, maxDistance, rolloff); - } break; - case ma_attenuation_model_linear: - { - gain = ma_attenuation_linear(distance, minDistance, maxDistance, rolloff); - } break; - case ma_attenuation_model_exponential: - { - gain = ma_attenuation_exponential(distance, minDistance, maxDistance, rolloff); - } break; - case ma_attenuation_model_none: - default: - { - gain = 1; - } break; - } - - /* Normalize the position. */ - if (distance > 0.001f) { - float distanceInv = 1/distance; - relativePosNormalized = relativePos; - relativePosNormalized.x *= distanceInv; - relativePosNormalized.y *= distanceInv; - relativePosNormalized.z *= distanceInv; - } else { - distance = 0; - relativePosNormalized = ma_vec3f_init_3f(0, 0, 0); - } - - /* - Angular attenuation. - - Unlike distance gain, the math for this is not specified by the OpenAL spec so we'll just go ahead and figure - this out for ourselves at the expense of possibly being inconsistent with other implementations. - - To do cone attenuation, I'm just using the same math that we'd use to implement a basic spotlight in OpenGL. We - just need to get the direction from the source to the listener and then do a dot product against that and the - direction of the spotlight. Then we just compare that dot product against the cosine of the inner and outer - angles. If the dot product is greater than the outer angle, we just use coneOuterGain. If it's less than - the inner angle, we just use a gain of 1. Otherwise we linearly interpolate between 1 and coneOuterGain. - */ - if (distance > 0) { - /* Source angular gain. */ - float spatializerConeInnerAngle; - float spatializerConeOuterAngle; - float spatializerConeOuterGain; - ma_spatializer_get_cone(pSpatializer, &spatializerConeInnerAngle, &spatializerConeOuterAngle, &spatializerConeOuterGain); - - gain *= ma_calculate_angular_gain(relativeDir, ma_vec3f_neg(relativePosNormalized), spatializerConeInnerAngle, spatializerConeOuterAngle, spatializerConeOuterGain); - - /* - We're supporting angular gain on the listener as well for those who want to reduce the volume of sounds that - are positioned behind the listener. On default settings, this will have no effect. - */ - if (pListener != NULL && pListener->config.coneInnerAngleInRadians < 6.283185f) { - ma_vec3f listenerDirection; - float listenerInnerAngle; - float listenerOuterAngle; - float listenerOuterGain; - - if (pListener->config.handedness == ma_handedness_right) { - listenerDirection = ma_vec3f_init_3f(0, 0, -1); - } else { - listenerDirection = ma_vec3f_init_3f(0, 0, +1); - } - - listenerInnerAngle = pListener->config.coneInnerAngleInRadians; - listenerOuterAngle = pListener->config.coneOuterAngleInRadians; - listenerOuterGain = pListener->config.coneOuterGain; - - gain *= ma_calculate_angular_gain(listenerDirection, relativePosNormalized, listenerInnerAngle, listenerOuterAngle, listenerOuterGain); - } - } else { - /* The sound is right on top of the listener. Don't do any angular attenuation. */ - } - - - /* Clamp the gain. */ - gain = ma_clamp(gain, ma_spatializer_get_min_gain(pSpatializer), ma_spatializer_get_max_gain(pSpatializer)); - - /* - The gain needs to be applied per-channel here. The spatialization code below will be changing the per-channel - gains which will then eventually be passed into the gainer which will deal with smoothing the gain transitions - to avoid harsh changes in gain. - */ - for (iChannel = 0; iChannel < channelsOut; iChannel += 1) { - pSpatializer->pNewChannelGainsOut[iChannel] = gain; - } - - /* - Convert to our output channel count. If the listener is disabled we just output silence here. We cannot ignore - the whole section of code here because we need to update some internal spatialization state. - */ - if (ma_spatializer_listener_is_enabled(pListener)) { - ma_channel_map_apply_f32((float*)pFramesOut, pChannelMapOut, channelsOut, (const float*)pFramesIn, pChannelMapIn, channelsIn, frameCount, ma_channel_mix_mode_rectangular, ma_mono_expansion_mode_default); - } else { - ma_silence_pcm_frames(pFramesOut, frameCount, ma_format_f32, pSpatializer->channelsOut); - } - - - /* - Panning. This is where we'll apply the gain and convert to the output channel count. We have an optimized path for - when we're converting to a mono stream. In that case we don't really need to do any panning - we just apply the - gain to the final output. - */ - /*printf("distance=%f; gain=%f\n", distance, gain);*/ - - /* We must have a valid channel map here to ensure we spatialize properly. */ - MA_ASSERT(pChannelMapOut != NULL); - - /* - We're not converting to mono so we'll want to apply some panning. This is where the feeling of something being - to the left, right, infront or behind the listener is calculated. I'm just using a basic model here. Note that - the code below is not based on any specific algorithm. I'm just implementing this off the top of my head and - seeing how it goes. There might be better ways to do this. - - To determine the direction of the sound relative to a speaker I'm using dot products. Each speaker is given a - direction. For example, the left channel in a stereo system will be -1 on the X axis and the right channel will - be +1 on the X axis. A dot product is performed against the direction vector of the channel and the normalized - position of the sound. - */ - - /* - Calculate our per-channel gains. We do this based on the normalized relative position of the sound and it's - relation to the direction of the channel. - */ - if (distance > 0) { - ma_vec3f unitPos = relativePos; - float distanceInv = 1/distance; - unitPos.x *= distanceInv; - unitPos.y *= distanceInv; - unitPos.z *= distanceInv; - - for (iChannel = 0; iChannel < channelsOut; iChannel += 1) { - ma_channel channelOut; - float d; - float dMin; - - channelOut = ma_channel_map_get_channel(pChannelMapOut, channelsOut, iChannel); - if (ma_is_spatial_channel_position(channelOut)) { - d = ma_mix_f32_fast(1, ma_vec3f_dot(unitPos, ma_get_channel_direction(channelOut)), ma_spatializer_get_directional_attenuation_factor(pSpatializer)); - } else { - d = 1; /* It's not a spatial channel so there's no real notion of direction. */ - } - - /* - In my testing, if the panning effect is too aggressive it makes spatialization feel uncomfortable. - The "dMin" variable below is used to control the aggressiveness of the panning effect. When set to - 0, panning will be most extreme and any sounds that are positioned on the opposite side of the - speaker will be completely silent from that speaker. Not only does this feel uncomfortable, it - doesn't even remotely represent the real world at all because sounds that come from your right side - are still clearly audible from your left side. Setting "dMin" to 1 will result in no panning at - all, which is also not ideal. By setting it to something greater than 0, the spatialization effect - becomes much less dramatic and a lot more bearable. - - Summary: 0 = more extreme panning; 1 = no panning. - */ - dMin = pSpatializer->minSpatializationChannelGain; - - /* - At this point, "d" will be positive if the sound is on the same side as the channel and negative if - it's on the opposite side. It will be in the range of -1..1. There's two ways I can think of to - calculate a panning value. The first is to simply convert it to 0..1, however this has a problem - which I'm not entirely happy with. Considering a stereo system, when a sound is positioned right - in front of the listener it'll result in each speaker getting a gain of 0.5. I don't know if I like - the idea of having a scaling factor of 0.5 being applied to a sound when it's sitting right in front - of the listener. I would intuitively expect that to be played at full volume, or close to it. - - The second idea I think of is to only apply a reduction in gain when the sound is on the opposite - side of the speaker. That is, reduce the gain only when the dot product is negative. The problem - with this is that there will not be any attenuation as the sound sweeps around the 180 degrees - where the dot product is positive. The idea with this option is that you leave the gain at 1 when - the sound is being played on the same side as the speaker and then you just reduce the volume when - the sound is on the other side. - - The summarize, I think the first option should give a better sense of spatialization, but the second - option is better for preserving the sound's power. - - UPDATE: In my testing, I find the first option to sound better. You can feel the sense of space a - bit better, but you can also hear the reduction in volume when it's right in front. - */ - #if 1 - { - /* - Scale the dot product from -1..1 to 0..1. Will result in a sound directly in front losing power - by being played at 0.5 gain. - */ - d = (d + 1) * 0.5f; /* -1..1 to 0..1 */ - d = ma_max(d, dMin); - pSpatializer->pNewChannelGainsOut[iChannel] *= d; - } - #else - { - /* - Only reduce the volume of the sound if it's on the opposite side. This path keeps the volume more - consistent, but comes at the expense of a worse sense of space and positioning. - */ - if (d < 0) { - d += 1; /* Move into the positive range. */ - d = ma_max(d, dMin); - channelGainsOut[iChannel] *= d; - } - } - #endif - } - } else { - /* Assume the sound is right on top of us. Don't do any panning. */ - } - - /* Now we need to apply the volume to each channel. This needs to run through the gainer to ensure we get a smooth volume transition. */ - ma_gainer_set_gains(&pSpatializer->gainer, pSpatializer->pNewChannelGainsOut); - ma_gainer_process_pcm_frames(&pSpatializer->gainer, pFramesOut, pFramesOut, frameCount); - - /* - Before leaving we'll want to update our doppler pitch so that the caller can apply some - pitch shifting if they desire. Note that we need to negate the relative position here - because the doppler calculation needs to be source-to-listener, but ours is listener-to- - source. - */ - if (dopplerFactor > 0) { - pSpatializer->dopplerPitch = ma_doppler_pitch(ma_vec3f_sub(ma_spatializer_listener_get_position(pListener), ma_spatializer_get_position(pSpatializer)), ma_spatializer_get_velocity(pSpatializer), listenerVel, speedOfSound, dopplerFactor); - } else { - pSpatializer->dopplerPitch = 1; - } - } - - return MA_SUCCESS; -} - -MA_API ma_result ma_spatializer_set_master_volume(ma_spatializer* pSpatializer, float volume) -{ - if (pSpatializer == NULL) { - return MA_INVALID_ARGS; - } - - return ma_gainer_set_master_volume(&pSpatializer->gainer, volume); -} - -MA_API ma_result ma_spatializer_get_master_volume(const ma_spatializer* pSpatializer, float* pVolume) -{ - if (pSpatializer == NULL) { - return MA_INVALID_ARGS; - } - - return ma_gainer_get_master_volume(&pSpatializer->gainer, pVolume); -} - -MA_API ma_uint32 ma_spatializer_get_input_channels(const ma_spatializer* pSpatializer) -{ - if (pSpatializer == NULL) { - return 0; - } - - return pSpatializer->channelsIn; -} - -MA_API ma_uint32 ma_spatializer_get_output_channels(const ma_spatializer* pSpatializer) -{ - if (pSpatializer == NULL) { - return 0; - } - - return pSpatializer->channelsOut; -} - -MA_API void ma_spatializer_set_attenuation_model(ma_spatializer* pSpatializer, ma_attenuation_model attenuationModel) -{ - if (pSpatializer == NULL) { - return; - } - - ma_atomic_exchange_i32(&pSpatializer->attenuationModel, attenuationModel); -} - -MA_API ma_attenuation_model ma_spatializer_get_attenuation_model(const ma_spatializer* pSpatializer) -{ - if (pSpatializer == NULL) { - return ma_attenuation_model_none; - } - - return (ma_attenuation_model)ma_atomic_load_i32(&pSpatializer->attenuationModel); -} - -MA_API void ma_spatializer_set_positioning(ma_spatializer* pSpatializer, ma_positioning positioning) -{ - if (pSpatializer == NULL) { - return; - } - - ma_atomic_exchange_i32(&pSpatializer->positioning, positioning); -} - -MA_API ma_positioning ma_spatializer_get_positioning(const ma_spatializer* pSpatializer) -{ - if (pSpatializer == NULL) { - return ma_positioning_absolute; - } - - return (ma_positioning)ma_atomic_load_i32(&pSpatializer->positioning); -} - -MA_API void ma_spatializer_set_rolloff(ma_spatializer* pSpatializer, float rolloff) -{ - if (pSpatializer == NULL) { - return; - } - - ma_atomic_exchange_f32(&pSpatializer->rolloff, rolloff); -} - -MA_API float ma_spatializer_get_rolloff(const ma_spatializer* pSpatializer) -{ - if (pSpatializer == NULL) { - return 0; - } - - return ma_atomic_load_f32(&pSpatializer->rolloff); -} - -MA_API void ma_spatializer_set_min_gain(ma_spatializer* pSpatializer, float minGain) -{ - if (pSpatializer == NULL) { - return; - } - - ma_atomic_exchange_f32(&pSpatializer->minGain, minGain); -} - -MA_API float ma_spatializer_get_min_gain(const ma_spatializer* pSpatializer) -{ - if (pSpatializer == NULL) { - return 0; - } - - return ma_atomic_load_f32(&pSpatializer->minGain); -} - -MA_API void ma_spatializer_set_max_gain(ma_spatializer* pSpatializer, float maxGain) -{ - if (pSpatializer == NULL) { - return; - } - - ma_atomic_exchange_f32(&pSpatializer->maxGain, maxGain); -} - -MA_API float ma_spatializer_get_max_gain(const ma_spatializer* pSpatializer) -{ - if (pSpatializer == NULL) { - return 0; - } - - return ma_atomic_load_f32(&pSpatializer->maxGain); -} - -MA_API void ma_spatializer_set_min_distance(ma_spatializer* pSpatializer, float minDistance) -{ - if (pSpatializer == NULL) { - return; - } - - ma_atomic_exchange_f32(&pSpatializer->minDistance, minDistance); -} - -MA_API float ma_spatializer_get_min_distance(const ma_spatializer* pSpatializer) -{ - if (pSpatializer == NULL) { - return 0; - } - - return ma_atomic_load_f32(&pSpatializer->minDistance); -} - -MA_API void ma_spatializer_set_max_distance(ma_spatializer* pSpatializer, float maxDistance) -{ - if (pSpatializer == NULL) { - return; - } - - ma_atomic_exchange_f32(&pSpatializer->maxDistance, maxDistance); -} - -MA_API float ma_spatializer_get_max_distance(const ma_spatializer* pSpatializer) -{ - if (pSpatializer == NULL) { - return 0; - } - - return ma_atomic_load_f32(&pSpatializer->maxDistance); -} - -MA_API void ma_spatializer_set_cone(ma_spatializer* pSpatializer, float innerAngleInRadians, float outerAngleInRadians, float outerGain) -{ - if (pSpatializer == NULL) { - return; - } - - ma_atomic_exchange_f32(&pSpatializer->coneInnerAngleInRadians, innerAngleInRadians); - ma_atomic_exchange_f32(&pSpatializer->coneOuterAngleInRadians, outerAngleInRadians); - ma_atomic_exchange_f32(&pSpatializer->coneOuterGain, outerGain); -} - -MA_API void ma_spatializer_get_cone(const ma_spatializer* pSpatializer, float* pInnerAngleInRadians, float* pOuterAngleInRadians, float* pOuterGain) -{ - if (pSpatializer == NULL) { - return; - } - - if (pInnerAngleInRadians != NULL) { - *pInnerAngleInRadians = ma_atomic_load_f32(&pSpatializer->coneInnerAngleInRadians); - } - - if (pOuterAngleInRadians != NULL) { - *pOuterAngleInRadians = ma_atomic_load_f32(&pSpatializer->coneOuterAngleInRadians); - } - - if (pOuterGain != NULL) { - *pOuterGain = ma_atomic_load_f32(&pSpatializer->coneOuterGain); - } -} - -MA_API void ma_spatializer_set_doppler_factor(ma_spatializer* pSpatializer, float dopplerFactor) -{ - if (pSpatializer == NULL) { - return; - } - - ma_atomic_exchange_f32(&pSpatializer->dopplerFactor, dopplerFactor); -} - -MA_API float ma_spatializer_get_doppler_factor(const ma_spatializer* pSpatializer) -{ - if (pSpatializer == NULL) { - return 1; - } - - return ma_atomic_load_f32(&pSpatializer->dopplerFactor); -} - -MA_API void ma_spatializer_set_directional_attenuation_factor(ma_spatializer* pSpatializer, float directionalAttenuationFactor) -{ - if (pSpatializer == NULL) { - return; - } - - ma_atomic_exchange_f32(&pSpatializer->directionalAttenuationFactor, directionalAttenuationFactor); -} - -MA_API float ma_spatializer_get_directional_attenuation_factor(const ma_spatializer* pSpatializer) -{ - if (pSpatializer == NULL) { - return 1; - } - - return ma_atomic_load_f32(&pSpatializer->directionalAttenuationFactor); -} - -MA_API void ma_spatializer_set_position(ma_spatializer* pSpatializer, float x, float y, float z) -{ - if (pSpatializer == NULL) { - return; - } - - ma_atomic_vec3f_set(&pSpatializer->position, ma_vec3f_init_3f(x, y, z)); -} - -MA_API ma_vec3f ma_spatializer_get_position(const ma_spatializer* pSpatializer) -{ - if (pSpatializer == NULL) { - return ma_vec3f_init_3f(0, 0, 0); - } - - return ma_atomic_vec3f_get((ma_atomic_vec3f*)&pSpatializer->position); /* Naughty const-cast. It's just for atomically loading the vec3 which should be safe. */ -} - -MA_API void ma_spatializer_set_direction(ma_spatializer* pSpatializer, float x, float y, float z) -{ - if (pSpatializer == NULL) { - return; - } - - ma_atomic_vec3f_set(&pSpatializer->direction, ma_vec3f_init_3f(x, y, z)); -} - -MA_API ma_vec3f ma_spatializer_get_direction(const ma_spatializer* pSpatializer) -{ - if (pSpatializer == NULL) { - return ma_vec3f_init_3f(0, 0, -1); - } - - return ma_atomic_vec3f_get((ma_atomic_vec3f*)&pSpatializer->direction); /* Naughty const-cast. It's just for atomically loading the vec3 which should be safe. */ -} - -MA_API void ma_spatializer_set_velocity(ma_spatializer* pSpatializer, float x, float y, float z) -{ - if (pSpatializer == NULL) { - return; - } - - ma_atomic_vec3f_set(&pSpatializer->velocity, ma_vec3f_init_3f(x, y, z)); -} - -MA_API ma_vec3f ma_spatializer_get_velocity(const ma_spatializer* pSpatializer) -{ - if (pSpatializer == NULL) { - return ma_vec3f_init_3f(0, 0, 0); - } - - return ma_atomic_vec3f_get((ma_atomic_vec3f*)&pSpatializer->velocity); /* Naughty const-cast. It's just for atomically loading the vec3 which should be safe. */ -} - -MA_API void ma_spatializer_get_relative_position_and_direction(const ma_spatializer* pSpatializer, const ma_spatializer_listener* pListener, ma_vec3f* pRelativePos, ma_vec3f* pRelativeDir) -{ - if (pRelativePos != NULL) { - pRelativePos->x = 0; - pRelativePos->y = 0; - pRelativePos->z = 0; - } - - if (pRelativeDir != NULL) { - pRelativeDir->x = 0; - pRelativeDir->y = 0; - pRelativeDir->z = -1; - } - - if (pSpatializer == NULL) { - return; - } - - if (pListener == NULL || ma_spatializer_get_positioning(pSpatializer) == ma_positioning_relative) { - /* There's no listener or we're using relative positioning. */ - if (pRelativePos != NULL) { - *pRelativePos = ma_spatializer_get_position(pSpatializer); - } - if (pRelativeDir != NULL) { - *pRelativeDir = ma_spatializer_get_direction(pSpatializer); - } - } else { - ma_vec3f spatializerPosition; - ma_vec3f spatializerDirection; - ma_vec3f listenerPosition; - ma_vec3f listenerDirection; - ma_vec3f v; - ma_vec3f axisX; - ma_vec3f axisY; - ma_vec3f axisZ; - float m[4][4]; - - spatializerPosition = ma_spatializer_get_position(pSpatializer); - spatializerDirection = ma_spatializer_get_direction(pSpatializer); - listenerPosition = ma_spatializer_listener_get_position(pListener); - listenerDirection = ma_spatializer_listener_get_direction(pListener); - - /* - We need to calculate the right vector from our forward and up vectors. This is done with - a cross product. - */ - axisZ = ma_vec3f_normalize(listenerDirection); /* Normalization required here because we can't trust the caller. */ - axisX = ma_vec3f_normalize(ma_vec3f_cross(axisZ, pListener->config.worldUp)); /* Normalization required here because the world up vector may not be perpendicular with the forward vector. */ - - /* - The calculation of axisX above can result in a zero-length vector if the listener is - looking straight up on the Y axis. We'll need to fall back to a +X in this case so that - the calculations below don't fall apart. This is where a quaternion based listener and - sound orientation would come in handy. - */ - if (ma_vec3f_len2(axisX) == 0) { - axisX = ma_vec3f_init_3f(1, 0, 0); - } - - axisY = ma_vec3f_cross(axisX, axisZ); /* No normalization is required here because axisX and axisZ are unit length and perpendicular. */ - - /* - We need to swap the X axis if we're left handed because otherwise the cross product above - will have resulted in it pointing in the wrong direction (right handed was assumed in the - cross products above). - */ - if (pListener->config.handedness == ma_handedness_left) { - axisX = ma_vec3f_neg(axisX); - } - - /* Lookat. */ - m[0][0] = axisX.x; m[1][0] = axisX.y; m[2][0] = axisX.z; m[3][0] = -ma_vec3f_dot(axisX, listenerPosition); - m[0][1] = axisY.x; m[1][1] = axisY.y; m[2][1] = axisY.z; m[3][1] = -ma_vec3f_dot(axisY, listenerPosition); - m[0][2] = -axisZ.x; m[1][2] = -axisZ.y; m[2][2] = -axisZ.z; m[3][2] = -ma_vec3f_dot(ma_vec3f_neg(axisZ), listenerPosition); - m[0][3] = 0; m[1][3] = 0; m[2][3] = 0; m[3][3] = 1; - - /* - Multiply the lookat matrix by the spatializer position to transform it to listener - space. This allows calculations to work based on the sound being relative to the - origin which makes things simpler. - */ - if (pRelativePos != NULL) { - v = spatializerPosition; - pRelativePos->x = m[0][0] * v.x + m[1][0] * v.y + m[2][0] * v.z + m[3][0] * 1; - pRelativePos->y = m[0][1] * v.x + m[1][1] * v.y + m[2][1] * v.z + m[3][1] * 1; - pRelativePos->z = m[0][2] * v.x + m[1][2] * v.y + m[2][2] * v.z + m[3][2] * 1; - } - - /* - The direction of the sound needs to also be transformed so that it's relative to the - rotation of the listener. - */ - if (pRelativeDir != NULL) { - v = spatializerDirection; - pRelativeDir->x = m[0][0] * v.x + m[1][0] * v.y + m[2][0] * v.z; - pRelativeDir->y = m[0][1] * v.x + m[1][1] * v.y + m[2][1] * v.z; - pRelativeDir->z = m[0][2] * v.x + m[1][2] * v.y + m[2][2] * v.z; - } - } -} - - - - -/************************************************************************************************************************************************************** - -Resampling - -**************************************************************************************************************************************************************/ -MA_API ma_linear_resampler_config ma_linear_resampler_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRateIn, ma_uint32 sampleRateOut) -{ - ma_linear_resampler_config config; - MA_ZERO_OBJECT(&config); - config.format = format; - config.channels = channels; - config.sampleRateIn = sampleRateIn; - config.sampleRateOut = sampleRateOut; - config.lpfOrder = ma_min(MA_DEFAULT_RESAMPLER_LPF_ORDER, MA_MAX_FILTER_ORDER); - config.lpfNyquistFactor = 1; - - return config; -} - - -typedef struct -{ - size_t sizeInBytes; - size_t x0Offset; - size_t x1Offset; - size_t lpfOffset; -} ma_linear_resampler_heap_layout; - - -static void ma_linear_resampler_adjust_timer_for_new_rate(ma_linear_resampler* pResampler, ma_uint32 oldSampleRateOut, ma_uint32 newSampleRateOut) -{ - /* - So what's happening here? Basically we need to adjust the fractional component of the time advance based on the new rate. The old time advance will - be based on the old sample rate, but we are needing to adjust it to that it's based on the new sample rate. - */ - ma_uint32 oldRateTimeWhole = pResampler->inTimeFrac / oldSampleRateOut; /* <-- This should almost never be anything other than 0, but leaving it here to make this more general and robust just in case. */ - ma_uint32 oldRateTimeFract = pResampler->inTimeFrac % oldSampleRateOut; - - pResampler->inTimeFrac = - (oldRateTimeWhole * newSampleRateOut) + - ((oldRateTimeFract * newSampleRateOut) / oldSampleRateOut); - - /* Make sure the fractional part is less than the output sample rate. */ - pResampler->inTimeInt += pResampler->inTimeFrac / pResampler->config.sampleRateOut; - pResampler->inTimeFrac = pResampler->inTimeFrac % pResampler->config.sampleRateOut; -} - -static ma_result ma_linear_resampler_set_rate_internal(ma_linear_resampler* pResampler, void* pHeap, ma_linear_resampler_heap_layout* pHeapLayout, ma_uint32 sampleRateIn, ma_uint32 sampleRateOut, ma_bool32 isResamplerAlreadyInitialized) -{ - ma_result result; - ma_uint32 gcf; - ma_uint32 lpfSampleRate; - double lpfCutoffFrequency; - ma_lpf_config lpfConfig; - ma_uint32 oldSampleRateOut; /* Required for adjusting time advance down the bottom. */ - - if (pResampler == NULL) { - return MA_INVALID_ARGS; - } - - if (sampleRateIn == 0 || sampleRateOut == 0) { - return MA_INVALID_ARGS; - } - - oldSampleRateOut = pResampler->config.sampleRateOut; - - pResampler->config.sampleRateIn = sampleRateIn; - pResampler->config.sampleRateOut = sampleRateOut; - - /* Simplify the sample rate. */ - gcf = ma_gcf_u32(pResampler->config.sampleRateIn, pResampler->config.sampleRateOut); - pResampler->config.sampleRateIn /= gcf; - pResampler->config.sampleRateOut /= gcf; - - /* Always initialize the low-pass filter, even when the order is 0. */ - if (pResampler->config.lpfOrder > MA_MAX_FILTER_ORDER) { - return MA_INVALID_ARGS; - } - - lpfSampleRate = (ma_uint32)(ma_max(pResampler->config.sampleRateIn, pResampler->config.sampleRateOut)); - lpfCutoffFrequency = ( double)(ma_min(pResampler->config.sampleRateIn, pResampler->config.sampleRateOut) * 0.5 * pResampler->config.lpfNyquistFactor); - - lpfConfig = ma_lpf_config_init(pResampler->config.format, pResampler->config.channels, lpfSampleRate, lpfCutoffFrequency, pResampler->config.lpfOrder); - - /* - If the resampler is already initialized we don't want to do a fresh initialization of the low-pass filter because it will result in the cached frames - getting cleared. Instead we re-initialize the filter which will maintain any cached frames. - */ - if (isResamplerAlreadyInitialized) { - result = ma_lpf_reinit(&lpfConfig, &pResampler->lpf); - } else { - result = ma_lpf_init_preallocated(&lpfConfig, ma_offset_ptr(pHeap, pHeapLayout->lpfOffset), &pResampler->lpf); - } - - if (result != MA_SUCCESS) { - return result; - } - - - pResampler->inAdvanceInt = pResampler->config.sampleRateIn / pResampler->config.sampleRateOut; - pResampler->inAdvanceFrac = pResampler->config.sampleRateIn % pResampler->config.sampleRateOut; - - /* Our timer was based on the old rate. We need to adjust it so that it's based on the new rate. */ - ma_linear_resampler_adjust_timer_for_new_rate(pResampler, oldSampleRateOut, pResampler->config.sampleRateOut); - - return MA_SUCCESS; -} - -static ma_result ma_linear_resampler_get_heap_layout(const ma_linear_resampler_config* pConfig, ma_linear_resampler_heap_layout* pHeapLayout) -{ - MA_ASSERT(pHeapLayout != NULL); - - MA_ZERO_OBJECT(pHeapLayout); - - if (pConfig == NULL) { - return MA_INVALID_ARGS; - } - - if (pConfig->format != ma_format_f32 && pConfig->format != ma_format_s16) { - return MA_INVALID_ARGS; - } - - if (pConfig->channels == 0) { - return MA_INVALID_ARGS; - } - - pHeapLayout->sizeInBytes = 0; - - /* x0 */ - pHeapLayout->x0Offset = pHeapLayout->sizeInBytes; - if (pConfig->format == ma_format_f32) { - pHeapLayout->sizeInBytes += sizeof(float) * pConfig->channels; - } else { - pHeapLayout->sizeInBytes += sizeof(ma_int16) * pConfig->channels; - } - - /* x1 */ - pHeapLayout->x1Offset = pHeapLayout->sizeInBytes; - if (pConfig->format == ma_format_f32) { - pHeapLayout->sizeInBytes += sizeof(float) * pConfig->channels; - } else { - pHeapLayout->sizeInBytes += sizeof(ma_int16) * pConfig->channels; - } - - /* LPF */ - pHeapLayout->lpfOffset = ma_align_64(pHeapLayout->sizeInBytes); - { - ma_result result; - size_t lpfHeapSizeInBytes; - ma_lpf_config lpfConfig = ma_lpf_config_init(pConfig->format, pConfig->channels, 1, 1, pConfig->lpfOrder); /* Sample rate and cutoff frequency do not matter. */ - - result = ma_lpf_get_heap_size(&lpfConfig, &lpfHeapSizeInBytes); - if (result != MA_SUCCESS) { - return result; - } - - pHeapLayout->sizeInBytes += lpfHeapSizeInBytes; - } - - /* Make sure allocation size is aligned. */ - pHeapLayout->sizeInBytes = ma_align_64(pHeapLayout->sizeInBytes); - - return MA_SUCCESS; -} - -MA_API ma_result ma_linear_resampler_get_heap_size(const ma_linear_resampler_config* pConfig, size_t* pHeapSizeInBytes) -{ - ma_result result; - ma_linear_resampler_heap_layout heapLayout; - - if (pHeapSizeInBytes == NULL) { - return MA_INVALID_ARGS; - } - - *pHeapSizeInBytes = 0; - - result = ma_linear_resampler_get_heap_layout(pConfig, &heapLayout); - if (result != MA_SUCCESS) { - return result; - } - - *pHeapSizeInBytes = heapLayout.sizeInBytes; - - return MA_SUCCESS; -} - -MA_API ma_result ma_linear_resampler_init_preallocated(const ma_linear_resampler_config* pConfig, void* pHeap, ma_linear_resampler* pResampler) -{ - ma_result result; - ma_linear_resampler_heap_layout heapLayout; - - if (pResampler == NULL) { - return MA_INVALID_ARGS; - } - - MA_ZERO_OBJECT(pResampler); - - result = ma_linear_resampler_get_heap_layout(pConfig, &heapLayout); - if (result != MA_SUCCESS) { - return result; - } - - pResampler->config = *pConfig; - - pResampler->_pHeap = pHeap; - MA_ZERO_MEMORY(pHeap, heapLayout.sizeInBytes); - - if (pConfig->format == ma_format_f32) { - pResampler->x0.f32 = (float*)ma_offset_ptr(pHeap, heapLayout.x0Offset); - pResampler->x1.f32 = (float*)ma_offset_ptr(pHeap, heapLayout.x1Offset); - } else { - pResampler->x0.s16 = (ma_int16*)ma_offset_ptr(pHeap, heapLayout.x0Offset); - pResampler->x1.s16 = (ma_int16*)ma_offset_ptr(pHeap, heapLayout.x1Offset); - } - - /* Setting the rate will set up the filter and time advances for us. */ - result = ma_linear_resampler_set_rate_internal(pResampler, pHeap, &heapLayout, pConfig->sampleRateIn, pConfig->sampleRateOut, /* isResamplerAlreadyInitialized = */ MA_FALSE); - if (result != MA_SUCCESS) { - return result; - } - - pResampler->inTimeInt = 1; /* Set this to one to force an input sample to always be loaded for the first output frame. */ - pResampler->inTimeFrac = 0; - - return MA_SUCCESS; -} - -MA_API ma_result ma_linear_resampler_init(const ma_linear_resampler_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_linear_resampler* pResampler) -{ - ma_result result; - size_t heapSizeInBytes; - void* pHeap; - - result = ma_linear_resampler_get_heap_size(pConfig, &heapSizeInBytes); - if (result != MA_SUCCESS) { - return result; - } - - if (heapSizeInBytes > 0) { - pHeap = ma_malloc(heapSizeInBytes, pAllocationCallbacks); - if (pHeap == NULL) { - return MA_OUT_OF_MEMORY; - } - } else { - pHeap = NULL; - } - - result = ma_linear_resampler_init_preallocated(pConfig, pHeap, pResampler); - if (result != MA_SUCCESS) { - ma_free(pHeap, pAllocationCallbacks); - return result; - } - - pResampler->_ownsHeap = MA_TRUE; - return MA_SUCCESS; -} - -MA_API void ma_linear_resampler_uninit(ma_linear_resampler* pResampler, const ma_allocation_callbacks* pAllocationCallbacks) -{ - if (pResampler == NULL) { - return; - } - - ma_lpf_uninit(&pResampler->lpf, pAllocationCallbacks); - - if (pResampler->_ownsHeap) { - ma_free(pResampler->_pHeap, pAllocationCallbacks); - } -} - -static MA_INLINE ma_int16 ma_linear_resampler_mix_s16(ma_int16 x, ma_int16 y, ma_int32 a, const ma_int32 shift) -{ - ma_int32 b; - ma_int32 c; - ma_int32 r; - - MA_ASSERT(a <= (1<> shift); -} - -static void ma_linear_resampler_interpolate_frame_s16(ma_linear_resampler* pResampler, ma_int16* MA_RESTRICT pFrameOut) -{ - ma_uint32 c; - ma_uint32 a; - const ma_uint32 channels = pResampler->config.channels; - const ma_uint32 shift = 12; - - MA_ASSERT(pResampler != NULL); - MA_ASSERT(pFrameOut != NULL); - - a = (pResampler->inTimeFrac << shift) / pResampler->config.sampleRateOut; - - MA_ASSUME(channels > 0); - for (c = 0; c < channels; c += 1) { - ma_int16 s = ma_linear_resampler_mix_s16(pResampler->x0.s16[c], pResampler->x1.s16[c], a, shift); - pFrameOut[c] = s; - } -} - - -static void ma_linear_resampler_interpolate_frame_f32(ma_linear_resampler* pResampler, float* MA_RESTRICT pFrameOut) -{ - ma_uint32 c; - float a; - const ma_uint32 channels = pResampler->config.channels; - - MA_ASSERT(pResampler != NULL); - MA_ASSERT(pFrameOut != NULL); - - a = (float)pResampler->inTimeFrac / pResampler->config.sampleRateOut; - - MA_ASSUME(channels > 0); - for (c = 0; c < channels; c += 1) { - float s = ma_mix_f32_fast(pResampler->x0.f32[c], pResampler->x1.f32[c], a); - pFrameOut[c] = s; - } -} - -static ma_result ma_linear_resampler_process_pcm_frames_s16_downsample(ma_linear_resampler* pResampler, const void* pFramesIn, ma_uint64* pFrameCountIn, void* pFramesOut, ma_uint64* pFrameCountOut) -{ - const ma_int16* pFramesInS16; - /* */ ma_int16* pFramesOutS16; - ma_uint64 frameCountIn; - ma_uint64 frameCountOut; - ma_uint64 framesProcessedIn; - ma_uint64 framesProcessedOut; - - MA_ASSERT(pResampler != NULL); - MA_ASSERT(pFrameCountIn != NULL); - MA_ASSERT(pFrameCountOut != NULL); - - pFramesInS16 = (const ma_int16*)pFramesIn; - pFramesOutS16 = ( ma_int16*)pFramesOut; - frameCountIn = *pFrameCountIn; - frameCountOut = *pFrameCountOut; - framesProcessedIn = 0; - framesProcessedOut = 0; - - while (framesProcessedOut < frameCountOut) { - /* Before interpolating we need to load the buffers. When doing this we need to ensure we run every input sample through the filter. */ - while (pResampler->inTimeInt > 0 && frameCountIn > framesProcessedIn) { - ma_uint32 iChannel; - - if (pFramesInS16 != NULL) { - for (iChannel = 0; iChannel < pResampler->config.channels; iChannel += 1) { - pResampler->x0.s16[iChannel] = pResampler->x1.s16[iChannel]; - pResampler->x1.s16[iChannel] = pFramesInS16[iChannel]; - } - pFramesInS16 += pResampler->config.channels; - } else { - for (iChannel = 0; iChannel < pResampler->config.channels; iChannel += 1) { - pResampler->x0.s16[iChannel] = pResampler->x1.s16[iChannel]; - pResampler->x1.s16[iChannel] = 0; - } - } - - /* Filter. Do not apply filtering if sample rates are the same or else you'll get dangerous glitching. */ - if (pResampler->config.sampleRateIn != pResampler->config.sampleRateOut) { - ma_lpf_process_pcm_frame_s16(&pResampler->lpf, pResampler->x1.s16, pResampler->x1.s16); - } - - framesProcessedIn += 1; - pResampler->inTimeInt -= 1; - } - - if (pResampler->inTimeInt > 0) { - break; /* Ran out of input data. */ - } - - /* Getting here means the frames have been loaded and filtered and we can generate the next output frame. */ - if (pFramesOutS16 != NULL) { - MA_ASSERT(pResampler->inTimeInt == 0); - ma_linear_resampler_interpolate_frame_s16(pResampler, pFramesOutS16); - - pFramesOutS16 += pResampler->config.channels; - } - - framesProcessedOut += 1; - - /* Advance time forward. */ - pResampler->inTimeInt += pResampler->inAdvanceInt; - pResampler->inTimeFrac += pResampler->inAdvanceFrac; - if (pResampler->inTimeFrac >= pResampler->config.sampleRateOut) { - pResampler->inTimeFrac -= pResampler->config.sampleRateOut; - pResampler->inTimeInt += 1; - } - } - - *pFrameCountIn = framesProcessedIn; - *pFrameCountOut = framesProcessedOut; - - return MA_SUCCESS; -} - -static ma_result ma_linear_resampler_process_pcm_frames_s16_upsample(ma_linear_resampler* pResampler, const void* pFramesIn, ma_uint64* pFrameCountIn, void* pFramesOut, ma_uint64* pFrameCountOut) -{ - const ma_int16* pFramesInS16; - /* */ ma_int16* pFramesOutS16; - ma_uint64 frameCountIn; - ma_uint64 frameCountOut; - ma_uint64 framesProcessedIn; - ma_uint64 framesProcessedOut; - - MA_ASSERT(pResampler != NULL); - MA_ASSERT(pFrameCountIn != NULL); - MA_ASSERT(pFrameCountOut != NULL); - - pFramesInS16 = (const ma_int16*)pFramesIn; - pFramesOutS16 = ( ma_int16*)pFramesOut; - frameCountIn = *pFrameCountIn; - frameCountOut = *pFrameCountOut; - framesProcessedIn = 0; - framesProcessedOut = 0; - - while (framesProcessedOut < frameCountOut) { - /* Before interpolating we need to load the buffers. */ - while (pResampler->inTimeInt > 0 && frameCountIn > framesProcessedIn) { - ma_uint32 iChannel; - - if (pFramesInS16 != NULL) { - for (iChannel = 0; iChannel < pResampler->config.channels; iChannel += 1) { - pResampler->x0.s16[iChannel] = pResampler->x1.s16[iChannel]; - pResampler->x1.s16[iChannel] = pFramesInS16[iChannel]; - } - pFramesInS16 += pResampler->config.channels; - } else { - for (iChannel = 0; iChannel < pResampler->config.channels; iChannel += 1) { - pResampler->x0.s16[iChannel] = pResampler->x1.s16[iChannel]; - pResampler->x1.s16[iChannel] = 0; - } - } - - framesProcessedIn += 1; - pResampler->inTimeInt -= 1; - } - - if (pResampler->inTimeInt > 0) { - break; /* Ran out of input data. */ - } - - /* Getting here means the frames have been loaded and we can generate the next output frame. */ - if (pFramesOutS16 != NULL) { - MA_ASSERT(pResampler->inTimeInt == 0); - ma_linear_resampler_interpolate_frame_s16(pResampler, pFramesOutS16); - - /* Filter. Do not apply filtering if sample rates are the same or else you'll get dangerous glitching. */ - if (pResampler->config.sampleRateIn != pResampler->config.sampleRateOut) { - ma_lpf_process_pcm_frame_s16(&pResampler->lpf, pFramesOutS16, pFramesOutS16); - } - - pFramesOutS16 += pResampler->config.channels; - } - - framesProcessedOut += 1; - - /* Advance time forward. */ - pResampler->inTimeInt += pResampler->inAdvanceInt; - pResampler->inTimeFrac += pResampler->inAdvanceFrac; - if (pResampler->inTimeFrac >= pResampler->config.sampleRateOut) { - pResampler->inTimeFrac -= pResampler->config.sampleRateOut; - pResampler->inTimeInt += 1; - } - } - - *pFrameCountIn = framesProcessedIn; - *pFrameCountOut = framesProcessedOut; - - return MA_SUCCESS; -} - -static ma_result ma_linear_resampler_process_pcm_frames_s16(ma_linear_resampler* pResampler, const void* pFramesIn, ma_uint64* pFrameCountIn, void* pFramesOut, ma_uint64* pFrameCountOut) -{ - MA_ASSERT(pResampler != NULL); - - if (pResampler->config.sampleRateIn > pResampler->config.sampleRateOut) { - return ma_linear_resampler_process_pcm_frames_s16_downsample(pResampler, pFramesIn, pFrameCountIn, pFramesOut, pFrameCountOut); - } else { - return ma_linear_resampler_process_pcm_frames_s16_upsample(pResampler, pFramesIn, pFrameCountIn, pFramesOut, pFrameCountOut); - } -} - - -static ma_result ma_linear_resampler_process_pcm_frames_f32_downsample(ma_linear_resampler* pResampler, const void* pFramesIn, ma_uint64* pFrameCountIn, void* pFramesOut, ma_uint64* pFrameCountOut) -{ - const float* pFramesInF32; - /* */ float* pFramesOutF32; - ma_uint64 frameCountIn; - ma_uint64 frameCountOut; - ma_uint64 framesProcessedIn; - ma_uint64 framesProcessedOut; - - MA_ASSERT(pResampler != NULL); - MA_ASSERT(pFrameCountIn != NULL); - MA_ASSERT(pFrameCountOut != NULL); - - pFramesInF32 = (const float*)pFramesIn; - pFramesOutF32 = ( float*)pFramesOut; - frameCountIn = *pFrameCountIn; - frameCountOut = *pFrameCountOut; - framesProcessedIn = 0; - framesProcessedOut = 0; - - while (framesProcessedOut < frameCountOut) { - /* Before interpolating we need to load the buffers. When doing this we need to ensure we run every input sample through the filter. */ - while (pResampler->inTimeInt > 0 && frameCountIn > framesProcessedIn) { - ma_uint32 iChannel; - - if (pFramesInF32 != NULL) { - for (iChannel = 0; iChannel < pResampler->config.channels; iChannel += 1) { - pResampler->x0.f32[iChannel] = pResampler->x1.f32[iChannel]; - pResampler->x1.f32[iChannel] = pFramesInF32[iChannel]; - } - pFramesInF32 += pResampler->config.channels; - } else { - for (iChannel = 0; iChannel < pResampler->config.channels; iChannel += 1) { - pResampler->x0.f32[iChannel] = pResampler->x1.f32[iChannel]; - pResampler->x1.f32[iChannel] = 0; - } - } - - /* Filter. Do not apply filtering if sample rates are the same or else you'll get dangerous glitching. */ - if (pResampler->config.sampleRateIn != pResampler->config.sampleRateOut) { - ma_lpf_process_pcm_frame_f32(&pResampler->lpf, pResampler->x1.f32, pResampler->x1.f32); - } - - framesProcessedIn += 1; - pResampler->inTimeInt -= 1; - } - - if (pResampler->inTimeInt > 0) { - break; /* Ran out of input data. */ - } - - /* Getting here means the frames have been loaded and filtered and we can generate the next output frame. */ - if (pFramesOutF32 != NULL) { - MA_ASSERT(pResampler->inTimeInt == 0); - ma_linear_resampler_interpolate_frame_f32(pResampler, pFramesOutF32); - - pFramesOutF32 += pResampler->config.channels; - } - - framesProcessedOut += 1; - - /* Advance time forward. */ - pResampler->inTimeInt += pResampler->inAdvanceInt; - pResampler->inTimeFrac += pResampler->inAdvanceFrac; - if (pResampler->inTimeFrac >= pResampler->config.sampleRateOut) { - pResampler->inTimeFrac -= pResampler->config.sampleRateOut; - pResampler->inTimeInt += 1; - } - } - - *pFrameCountIn = framesProcessedIn; - *pFrameCountOut = framesProcessedOut; - - return MA_SUCCESS; -} - -static ma_result ma_linear_resampler_process_pcm_frames_f32_upsample(ma_linear_resampler* pResampler, const void* pFramesIn, ma_uint64* pFrameCountIn, void* pFramesOut, ma_uint64* pFrameCountOut) -{ - const float* pFramesInF32; - /* */ float* pFramesOutF32; - ma_uint64 frameCountIn; - ma_uint64 frameCountOut; - ma_uint64 framesProcessedIn; - ma_uint64 framesProcessedOut; - - MA_ASSERT(pResampler != NULL); - MA_ASSERT(pFrameCountIn != NULL); - MA_ASSERT(pFrameCountOut != NULL); - - pFramesInF32 = (const float*)pFramesIn; - pFramesOutF32 = ( float*)pFramesOut; - frameCountIn = *pFrameCountIn; - frameCountOut = *pFrameCountOut; - framesProcessedIn = 0; - framesProcessedOut = 0; - - while (framesProcessedOut < frameCountOut) { - /* Before interpolating we need to load the buffers. */ - while (pResampler->inTimeInt > 0 && frameCountIn > framesProcessedIn) { - ma_uint32 iChannel; - - if (pFramesInF32 != NULL) { - for (iChannel = 0; iChannel < pResampler->config.channels; iChannel += 1) { - pResampler->x0.f32[iChannel] = pResampler->x1.f32[iChannel]; - pResampler->x1.f32[iChannel] = pFramesInF32[iChannel]; - } - pFramesInF32 += pResampler->config.channels; - } else { - for (iChannel = 0; iChannel < pResampler->config.channels; iChannel += 1) { - pResampler->x0.f32[iChannel] = pResampler->x1.f32[iChannel]; - pResampler->x1.f32[iChannel] = 0; - } - } - - framesProcessedIn += 1; - pResampler->inTimeInt -= 1; - } - - if (pResampler->inTimeInt > 0) { - break; /* Ran out of input data. */ - } - - /* Getting here means the frames have been loaded and we can generate the next output frame. */ - if (pFramesOutF32 != NULL) { - MA_ASSERT(pResampler->inTimeInt == 0); - ma_linear_resampler_interpolate_frame_f32(pResampler, pFramesOutF32); - - /* Filter. Do not apply filtering if sample rates are the same or else you'll get dangerous glitching. */ - if (pResampler->config.sampleRateIn != pResampler->config.sampleRateOut) { - ma_lpf_process_pcm_frame_f32(&pResampler->lpf, pFramesOutF32, pFramesOutF32); - } - - pFramesOutF32 += pResampler->config.channels; - } - - framesProcessedOut += 1; - - /* Advance time forward. */ - pResampler->inTimeInt += pResampler->inAdvanceInt; - pResampler->inTimeFrac += pResampler->inAdvanceFrac; - if (pResampler->inTimeFrac >= pResampler->config.sampleRateOut) { - pResampler->inTimeFrac -= pResampler->config.sampleRateOut; - pResampler->inTimeInt += 1; - } - } - - *pFrameCountIn = framesProcessedIn; - *pFrameCountOut = framesProcessedOut; - - return MA_SUCCESS; -} - -static ma_result ma_linear_resampler_process_pcm_frames_f32(ma_linear_resampler* pResampler, const void* pFramesIn, ma_uint64* pFrameCountIn, void* pFramesOut, ma_uint64* pFrameCountOut) -{ - MA_ASSERT(pResampler != NULL); - - if (pResampler->config.sampleRateIn > pResampler->config.sampleRateOut) { - return ma_linear_resampler_process_pcm_frames_f32_downsample(pResampler, pFramesIn, pFrameCountIn, pFramesOut, pFrameCountOut); - } else { - return ma_linear_resampler_process_pcm_frames_f32_upsample(pResampler, pFramesIn, pFrameCountIn, pFramesOut, pFrameCountOut); - } -} - - -MA_API ma_result ma_linear_resampler_process_pcm_frames(ma_linear_resampler* pResampler, const void* pFramesIn, ma_uint64* pFrameCountIn, void* pFramesOut, ma_uint64* pFrameCountOut) -{ - if (pResampler == NULL) { - return MA_INVALID_ARGS; - } - - /* */ if (pResampler->config.format == ma_format_s16) { - return ma_linear_resampler_process_pcm_frames_s16(pResampler, pFramesIn, pFrameCountIn, pFramesOut, pFrameCountOut); - } else if (pResampler->config.format == ma_format_f32) { - return ma_linear_resampler_process_pcm_frames_f32(pResampler, pFramesIn, pFrameCountIn, pFramesOut, pFrameCountOut); - } else { - /* Should never get here. Getting here means the format is not supported and you didn't check the return value of ma_linear_resampler_init(). */ - MA_ASSERT(MA_FALSE); - return MA_INVALID_ARGS; - } -} - - -MA_API ma_result ma_linear_resampler_set_rate(ma_linear_resampler* pResampler, ma_uint32 sampleRateIn, ma_uint32 sampleRateOut) -{ - return ma_linear_resampler_set_rate_internal(pResampler, NULL, NULL, sampleRateIn, sampleRateOut, /* isResamplerAlreadyInitialized = */ MA_TRUE); -} - -MA_API ma_result ma_linear_resampler_set_rate_ratio(ma_linear_resampler* pResampler, float ratioInOut) -{ - ma_uint32 n; - ma_uint32 d; - - if (pResampler == NULL) { - return MA_INVALID_ARGS; - } - - if (ratioInOut <= 0) { - return MA_INVALID_ARGS; - } - - d = 1000000; - n = (ma_uint32)(ratioInOut * d); - - if (n == 0) { - return MA_INVALID_ARGS; /* Ratio too small. */ - } - - MA_ASSERT(n != 0); - - return ma_linear_resampler_set_rate(pResampler, n, d); -} - -MA_API ma_uint64 ma_linear_resampler_get_input_latency(const ma_linear_resampler* pResampler) -{ - if (pResampler == NULL) { - return 0; - } - - return 1 + ma_lpf_get_latency(&pResampler->lpf); -} - -MA_API ma_uint64 ma_linear_resampler_get_output_latency(const ma_linear_resampler* pResampler) -{ - if (pResampler == NULL) { - return 0; - } - - return ma_linear_resampler_get_input_latency(pResampler) * pResampler->config.sampleRateOut / pResampler->config.sampleRateIn; -} - -MA_API ma_result ma_linear_resampler_get_required_input_frame_count(const ma_linear_resampler* pResampler, ma_uint64 outputFrameCount, ma_uint64* pInputFrameCount) -{ - ma_uint64 inputFrameCount; - - if (pInputFrameCount == NULL) { - return MA_INVALID_ARGS; - } - - *pInputFrameCount = 0; - - if (pResampler == NULL) { - return MA_INVALID_ARGS; - } - - if (outputFrameCount == 0) { - return MA_SUCCESS; - } - - /* Any whole input frames are consumed before the first output frame is generated. */ - inputFrameCount = pResampler->inTimeInt; - outputFrameCount -= 1; - - /* The rest of the output frames can be calculated in constant time. */ - inputFrameCount += outputFrameCount * pResampler->inAdvanceInt; - inputFrameCount += (pResampler->inTimeFrac + (outputFrameCount * pResampler->inAdvanceFrac)) / pResampler->config.sampleRateOut; - - *pInputFrameCount = inputFrameCount; - - return MA_SUCCESS; -} - -MA_API ma_result ma_linear_resampler_get_expected_output_frame_count(const ma_linear_resampler* pResampler, ma_uint64 inputFrameCount, ma_uint64* pOutputFrameCount) -{ - ma_uint64 outputFrameCount; - ma_uint64 preliminaryInputFrameCountFromFrac; - ma_uint64 preliminaryInputFrameCount; - - if (pOutputFrameCount == NULL) { - return MA_INVALID_ARGS; - } - - *pOutputFrameCount = 0; - - if (pResampler == NULL) { - return MA_INVALID_ARGS; - } - - /* - The first step is to get a preliminary output frame count. This will either be exactly equal to what we need, or less by 1. We need to - determine how many input frames will be consumed by this value. If it's greater than our original input frame count it means we won't - be able to generate an extra frame because we will have run out of input data. Otherwise we will have enough input for the generation - of an extra output frame. This add-by-one logic is necessary due to how the data loading logic works when processing frames. - */ - outputFrameCount = (inputFrameCount * pResampler->config.sampleRateOut) / pResampler->config.sampleRateIn; - - /* - We need to determine how many *whole* input frames will have been processed to generate our preliminary output frame count. This is - used in the logic below to determine whether or not we need to add an extra output frame. - */ - preliminaryInputFrameCountFromFrac = (pResampler->inTimeFrac + outputFrameCount*pResampler->inAdvanceFrac) / pResampler->config.sampleRateOut; - preliminaryInputFrameCount = (pResampler->inTimeInt + outputFrameCount*pResampler->inAdvanceInt ) + preliminaryInputFrameCountFromFrac; - - /* - If the total number of *whole* input frames that would be required to generate our preliminary output frame count is greater than - the amount of whole input frames we have available as input we need to *not* add an extra output frame as there won't be enough data - to actually process. Otherwise we need to add the extra output frame. - */ - if (preliminaryInputFrameCount <= inputFrameCount) { - outputFrameCount += 1; - } - - *pOutputFrameCount = outputFrameCount; - - return MA_SUCCESS; -} - -MA_API ma_result ma_linear_resampler_reset(ma_linear_resampler* pResampler) -{ - ma_uint32 iChannel; - - if (pResampler == NULL) { - return MA_INVALID_ARGS; - } - - /* Timers need to be cleared back to zero. */ - pResampler->inTimeInt = 1; /* Set this to one to force an input sample to always be loaded for the first output frame. */ - pResampler->inTimeFrac = 0; - - /* Cached samples need to be cleared. */ - if (pResampler->config.format == ma_format_f32) { - for (iChannel = 0; iChannel < pResampler->config.channels; iChannel += 1) { - pResampler->x0.f32[iChannel] = 0; - pResampler->x1.f32[iChannel] = 0; - } - } else { - for (iChannel = 0; iChannel < pResampler->config.channels; iChannel += 1) { - pResampler->x0.s16[iChannel] = 0; - pResampler->x1.s16[iChannel] = 0; - } - } - - /* The low pass filter needs to have its cache reset. */ - ma_lpf_clear_cache(&pResampler->lpf); - - return MA_SUCCESS; -} - - - -/* Linear resampler backend vtable. */ -static ma_linear_resampler_config ma_resampling_backend_get_config__linear(const ma_resampler_config* pConfig) -{ - ma_linear_resampler_config linearConfig; - - linearConfig = ma_linear_resampler_config_init(pConfig->format, pConfig->channels, pConfig->sampleRateIn, pConfig->sampleRateOut); - linearConfig.lpfOrder = pConfig->linear.lpfOrder; - - return linearConfig; -} - -static ma_result ma_resampling_backend_get_heap_size__linear(void* pUserData, const ma_resampler_config* pConfig, size_t* pHeapSizeInBytes) -{ - ma_linear_resampler_config linearConfig; - - (void)pUserData; - - linearConfig = ma_resampling_backend_get_config__linear(pConfig); - - return ma_linear_resampler_get_heap_size(&linearConfig, pHeapSizeInBytes); -} - -static ma_result ma_resampling_backend_init__linear(void* pUserData, const ma_resampler_config* pConfig, void* pHeap, ma_resampling_backend** ppBackend) -{ - ma_resampler* pResampler = (ma_resampler*)pUserData; - ma_result result; - ma_linear_resampler_config linearConfig; - - (void)pUserData; - - linearConfig = ma_resampling_backend_get_config__linear(pConfig); - - result = ma_linear_resampler_init_preallocated(&linearConfig, pHeap, &pResampler->state.linear); - if (result != MA_SUCCESS) { - return result; - } - - *ppBackend = &pResampler->state.linear; - - return MA_SUCCESS; -} - -static void ma_resampling_backend_uninit__linear(void* pUserData, ma_resampling_backend* pBackend, const ma_allocation_callbacks* pAllocationCallbacks) -{ - (void)pUserData; - - ma_linear_resampler_uninit((ma_linear_resampler*)pBackend, pAllocationCallbacks); -} - -static ma_result ma_resampling_backend_process__linear(void* pUserData, ma_resampling_backend* pBackend, const void* pFramesIn, ma_uint64* pFrameCountIn, void* pFramesOut, ma_uint64* pFrameCountOut) -{ - (void)pUserData; - - return ma_linear_resampler_process_pcm_frames((ma_linear_resampler*)pBackend, pFramesIn, pFrameCountIn, pFramesOut, pFrameCountOut); -} - -static ma_result ma_resampling_backend_set_rate__linear(void* pUserData, ma_resampling_backend* pBackend, ma_uint32 sampleRateIn, ma_uint32 sampleRateOut) -{ - (void)pUserData; - - return ma_linear_resampler_set_rate((ma_linear_resampler*)pBackend, sampleRateIn, sampleRateOut); -} - -static ma_uint64 ma_resampling_backend_get_input_latency__linear(void* pUserData, const ma_resampling_backend* pBackend) -{ - (void)pUserData; - - return ma_linear_resampler_get_input_latency((const ma_linear_resampler*)pBackend); -} - -static ma_uint64 ma_resampling_backend_get_output_latency__linear(void* pUserData, const ma_resampling_backend* pBackend) -{ - (void)pUserData; - - return ma_linear_resampler_get_output_latency((const ma_linear_resampler*)pBackend); -} - -static ma_result ma_resampling_backend_get_required_input_frame_count__linear(void* pUserData, const ma_resampling_backend* pBackend, ma_uint64 outputFrameCount, ma_uint64* pInputFrameCount) -{ - (void)pUserData; - - return ma_linear_resampler_get_required_input_frame_count((const ma_linear_resampler*)pBackend, outputFrameCount, pInputFrameCount); -} - -static ma_result ma_resampling_backend_get_expected_output_frame_count__linear(void* pUserData, const ma_resampling_backend* pBackend, ma_uint64 inputFrameCount, ma_uint64* pOutputFrameCount) -{ - (void)pUserData; - - return ma_linear_resampler_get_expected_output_frame_count((const ma_linear_resampler*)pBackend, inputFrameCount, pOutputFrameCount); -} - -static ma_result ma_resampling_backend_reset__linear(void* pUserData, ma_resampling_backend* pBackend) -{ - (void)pUserData; - - return ma_linear_resampler_reset((ma_linear_resampler*)pBackend); -} - -static ma_resampling_backend_vtable g_ma_linear_resampler_vtable = -{ - ma_resampling_backend_get_heap_size__linear, - ma_resampling_backend_init__linear, - ma_resampling_backend_uninit__linear, - ma_resampling_backend_process__linear, - ma_resampling_backend_set_rate__linear, - ma_resampling_backend_get_input_latency__linear, - ma_resampling_backend_get_output_latency__linear, - ma_resampling_backend_get_required_input_frame_count__linear, - ma_resampling_backend_get_expected_output_frame_count__linear, - ma_resampling_backend_reset__linear -}; - - - -MA_API ma_resampler_config ma_resampler_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRateIn, ma_uint32 sampleRateOut, ma_resample_algorithm algorithm) -{ - ma_resampler_config config; - - MA_ZERO_OBJECT(&config); - config.format = format; - config.channels = channels; - config.sampleRateIn = sampleRateIn; - config.sampleRateOut = sampleRateOut; - config.algorithm = algorithm; - - /* Linear. */ - config.linear.lpfOrder = ma_min(MA_DEFAULT_RESAMPLER_LPF_ORDER, MA_MAX_FILTER_ORDER); - - return config; -} - -static ma_result ma_resampler_get_vtable(const ma_resampler_config* pConfig, ma_resampler* pResampler, ma_resampling_backend_vtable** ppVTable, void** ppUserData) -{ - MA_ASSERT(pConfig != NULL); - MA_ASSERT(ppVTable != NULL); - MA_ASSERT(ppUserData != NULL); - - /* Safety. */ - *ppVTable = NULL; - *ppUserData = NULL; - - switch (pConfig->algorithm) - { - case ma_resample_algorithm_linear: - { - *ppVTable = &g_ma_linear_resampler_vtable; - *ppUserData = pResampler; - } break; - - case ma_resample_algorithm_custom: - { - *ppVTable = pConfig->pBackendVTable; - *ppUserData = pConfig->pBackendUserData; - } break; - - default: return MA_INVALID_ARGS; - } - - return MA_SUCCESS; -} - -MA_API ma_result ma_resampler_get_heap_size(const ma_resampler_config* pConfig, size_t* pHeapSizeInBytes) -{ - ma_result result; - ma_resampling_backend_vtable* pVTable; - void* pVTableUserData; - - if (pHeapSizeInBytes == NULL) { - return MA_INVALID_ARGS; - } - - *pHeapSizeInBytes = 0; - - if (pConfig == NULL) { - return MA_INVALID_ARGS; - } - - result = ma_resampler_get_vtable(pConfig, NULL, &pVTable, &pVTableUserData); - if (result != MA_SUCCESS) { - return result; - } - - if (pVTable == NULL || pVTable->onGetHeapSize == NULL) { - return MA_NOT_IMPLEMENTED; - } - - result = pVTable->onGetHeapSize(pVTableUserData, pConfig, pHeapSizeInBytes); - if (result != MA_SUCCESS) { - return result; - } - - return MA_SUCCESS; -} - -MA_API ma_result ma_resampler_init_preallocated(const ma_resampler_config* pConfig, void* pHeap, ma_resampler* pResampler) -{ - ma_result result; - - if (pResampler == NULL) { - return MA_INVALID_ARGS; - } - - MA_ZERO_OBJECT(pResampler); - - if (pConfig == NULL) { - return MA_INVALID_ARGS; - } - - pResampler->_pHeap = pHeap; - pResampler->format = pConfig->format; - pResampler->channels = pConfig->channels; - pResampler->sampleRateIn = pConfig->sampleRateIn; - pResampler->sampleRateOut = pConfig->sampleRateOut; - - result = ma_resampler_get_vtable(pConfig, pResampler, &pResampler->pBackendVTable, &pResampler->pBackendUserData); - if (result != MA_SUCCESS) { - return result; - } - - if (pResampler->pBackendVTable == NULL || pResampler->pBackendVTable->onInit == NULL) { - return MA_NOT_IMPLEMENTED; /* onInit not implemented. */ - } - - result = pResampler->pBackendVTable->onInit(pResampler->pBackendUserData, pConfig, pHeap, &pResampler->pBackend); - if (result != MA_SUCCESS) { - return result; - } - - return MA_SUCCESS; -} - -MA_API ma_result ma_resampler_init(const ma_resampler_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_resampler* pResampler) -{ - ma_result result; - size_t heapSizeInBytes; - void* pHeap; - - result = ma_resampler_get_heap_size(pConfig, &heapSizeInBytes); - if (result != MA_SUCCESS) { - return result; - } - - if (heapSizeInBytes > 0) { - pHeap = ma_malloc(heapSizeInBytes, pAllocationCallbacks); - if (pHeap == NULL) { - return MA_OUT_OF_MEMORY; - } - } else { - pHeap = NULL; - } - - result = ma_resampler_init_preallocated(pConfig, pHeap, pResampler); - if (result != MA_SUCCESS) { - ma_free(pHeap, pAllocationCallbacks); - return result; - } - - pResampler->_ownsHeap = MA_TRUE; - return MA_SUCCESS; -} - -MA_API void ma_resampler_uninit(ma_resampler* pResampler, const ma_allocation_callbacks* pAllocationCallbacks) -{ - if (pResampler == NULL) { - return; - } - - if (pResampler->pBackendVTable == NULL || pResampler->pBackendVTable->onUninit == NULL) { - return; - } - - pResampler->pBackendVTable->onUninit(pResampler->pBackendUserData, pResampler->pBackend, pAllocationCallbacks); - - if (pResampler->_ownsHeap) { - ma_free(pResampler->_pHeap, pAllocationCallbacks); - } -} - -MA_API ma_result ma_resampler_process_pcm_frames(ma_resampler* pResampler, const void* pFramesIn, ma_uint64* pFrameCountIn, void* pFramesOut, ma_uint64* pFrameCountOut) -{ - if (pResampler == NULL) { - return MA_INVALID_ARGS; - } - - if (pFrameCountOut == NULL && pFrameCountIn == NULL) { - return MA_INVALID_ARGS; - } - - if (pResampler->pBackendVTable == NULL || pResampler->pBackendVTable->onProcess == NULL) { - return MA_NOT_IMPLEMENTED; - } - - return pResampler->pBackendVTable->onProcess(pResampler->pBackendUserData, pResampler->pBackend, pFramesIn, pFrameCountIn, pFramesOut, pFrameCountOut); -} - -MA_API ma_result ma_resampler_set_rate(ma_resampler* pResampler, ma_uint32 sampleRateIn, ma_uint32 sampleRateOut) -{ - ma_result result; - - if (pResampler == NULL) { - return MA_INVALID_ARGS; - } - - if (sampleRateIn == 0 || sampleRateOut == 0) { - return MA_INVALID_ARGS; - } - - if (pResampler->pBackendVTable == NULL || pResampler->pBackendVTable->onSetRate == NULL) { - return MA_NOT_IMPLEMENTED; - } - - result = pResampler->pBackendVTable->onSetRate(pResampler->pBackendUserData, pResampler->pBackend, sampleRateIn, sampleRateOut); - if (result != MA_SUCCESS) { - return result; - } - - pResampler->sampleRateIn = sampleRateIn; - pResampler->sampleRateOut = sampleRateOut; - - return MA_SUCCESS; -} - -MA_API ma_result ma_resampler_set_rate_ratio(ma_resampler* pResampler, float ratio) -{ - ma_uint32 n; - ma_uint32 d; - - if (pResampler == NULL) { - return MA_INVALID_ARGS; - } - - if (ratio <= 0) { - return MA_INVALID_ARGS; - } - - d = 1000; - n = (ma_uint32)(ratio * d); - - if (n == 0) { - return MA_INVALID_ARGS; /* Ratio too small. */ - } - - MA_ASSERT(n != 0); - - return ma_resampler_set_rate(pResampler, n, d); -} - -MA_API ma_uint64 ma_resampler_get_input_latency(const ma_resampler* pResampler) -{ - if (pResampler == NULL) { - return 0; - } - - if (pResampler->pBackendVTable == NULL || pResampler->pBackendVTable->onGetInputLatency == NULL) { - return 0; - } - - return pResampler->pBackendVTable->onGetInputLatency(pResampler->pBackendUserData, pResampler->pBackend); -} - -MA_API ma_uint64 ma_resampler_get_output_latency(const ma_resampler* pResampler) -{ - if (pResampler == NULL) { - return 0; - } - - if (pResampler->pBackendVTable == NULL || pResampler->pBackendVTable->onGetOutputLatency == NULL) { - return 0; - } - - return pResampler->pBackendVTable->onGetOutputLatency(pResampler->pBackendUserData, pResampler->pBackend); -} - -MA_API ma_result ma_resampler_get_required_input_frame_count(const ma_resampler* pResampler, ma_uint64 outputFrameCount, ma_uint64* pInputFrameCount) -{ - if (pInputFrameCount == NULL) { - return MA_INVALID_ARGS; - } - - *pInputFrameCount = 0; - - if (pResampler == NULL) { - return MA_INVALID_ARGS; - } - - if (pResampler->pBackendVTable == NULL || pResampler->pBackendVTable->onGetRequiredInputFrameCount == NULL) { - return MA_NOT_IMPLEMENTED; - } - - return pResampler->pBackendVTable->onGetRequiredInputFrameCount(pResampler->pBackendUserData, pResampler->pBackend, outputFrameCount, pInputFrameCount); -} - -MA_API ma_result ma_resampler_get_expected_output_frame_count(const ma_resampler* pResampler, ma_uint64 inputFrameCount, ma_uint64* pOutputFrameCount) -{ - if (pOutputFrameCount == NULL) { - return MA_INVALID_ARGS; - } - - *pOutputFrameCount = 0; - - if (pResampler == NULL) { - return MA_INVALID_ARGS; - } - - if (pResampler->pBackendVTable == NULL || pResampler->pBackendVTable->onGetExpectedOutputFrameCount == NULL) { - return MA_NOT_IMPLEMENTED; - } - - return pResampler->pBackendVTable->onGetExpectedOutputFrameCount(pResampler->pBackendUserData, pResampler->pBackend, inputFrameCount, pOutputFrameCount); -} - -MA_API ma_result ma_resampler_reset(ma_resampler* pResampler) -{ - if (pResampler == NULL) { - return MA_INVALID_ARGS; - } - - if (pResampler->pBackendVTable == NULL || pResampler->pBackendVTable->onReset == NULL) { - return MA_NOT_IMPLEMENTED; - } - - return pResampler->pBackendVTable->onReset(pResampler->pBackendUserData, pResampler->pBackend); -} - -/************************************************************************************************************************************************************** - -Channel Conversion - -**************************************************************************************************************************************************************/ -#ifndef MA_CHANNEL_CONVERTER_FIXED_POINT_SHIFT -#define MA_CHANNEL_CONVERTER_FIXED_POINT_SHIFT 12 -#endif - -#define MA_PLANE_LEFT 0 -#define MA_PLANE_RIGHT 1 -#define MA_PLANE_FRONT 2 -#define MA_PLANE_BACK 3 -#define MA_PLANE_BOTTOM 4 -#define MA_PLANE_TOP 5 - -static float g_maChannelPlaneRatios[MA_CHANNEL_POSITION_COUNT][6] = { - { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_NONE */ - { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_MONO */ - { 0.5f, 0.0f, 0.5f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_FRONT_LEFT */ - { 0.0f, 0.5f, 0.5f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_FRONT_RIGHT */ - { 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_FRONT_CENTER */ - { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_LFE */ - { 0.5f, 0.0f, 0.0f, 0.5f, 0.0f, 0.0f}, /* MA_CHANNEL_BACK_LEFT */ - { 0.0f, 0.5f, 0.0f, 0.5f, 0.0f, 0.0f}, /* MA_CHANNEL_BACK_RIGHT */ - { 0.25f, 0.0f, 0.75f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_FRONT_LEFT_CENTER */ - { 0.0f, 0.25f, 0.75f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_FRONT_RIGHT_CENTER */ - { 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f}, /* MA_CHANNEL_BACK_CENTER */ - { 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_SIDE_LEFT */ - { 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_SIDE_RIGHT */ - { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f}, /* MA_CHANNEL_TOP_CENTER */ - { 0.33f, 0.0f, 0.33f, 0.0f, 0.0f, 0.34f}, /* MA_CHANNEL_TOP_FRONT_LEFT */ - { 0.0f, 0.0f, 0.5f, 0.0f, 0.0f, 0.5f}, /* MA_CHANNEL_TOP_FRONT_CENTER */ - { 0.0f, 0.33f, 0.33f, 0.0f, 0.0f, 0.34f}, /* MA_CHANNEL_TOP_FRONT_RIGHT */ - { 0.33f, 0.0f, 0.0f, 0.33f, 0.0f, 0.34f}, /* MA_CHANNEL_TOP_BACK_LEFT */ - { 0.0f, 0.0f, 0.0f, 0.5f, 0.0f, 0.5f}, /* MA_CHANNEL_TOP_BACK_CENTER */ - { 0.0f, 0.33f, 0.0f, 0.33f, 0.0f, 0.34f}, /* MA_CHANNEL_TOP_BACK_RIGHT */ - { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_0 */ - { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_1 */ - { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_2 */ - { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_3 */ - { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_4 */ - { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_5 */ - { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_6 */ - { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_7 */ - { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_8 */ - { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_9 */ - { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_10 */ - { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_11 */ - { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_12 */ - { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_13 */ - { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_14 */ - { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_15 */ - { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_16 */ - { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_17 */ - { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_18 */ - { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_19 */ - { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_20 */ - { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_21 */ - { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_22 */ - { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_23 */ - { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_24 */ - { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_25 */ - { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_26 */ - { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_27 */ - { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_28 */ - { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_29 */ - { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_30 */ - { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_31 */ -}; - -static float ma_calculate_channel_position_rectangular_weight(ma_channel channelPositionA, ma_channel channelPositionB) -{ - /* - Imagine the following simplified example: You have a single input speaker which is the front/left speaker which you want to convert to - the following output configuration: - - - front/left - - side/left - - back/left - - The front/left output is easy - it the same speaker position so it receives the full contribution of the front/left input. The amount - of contribution to apply to the side/left and back/left speakers, however, is a bit more complicated. - - Imagine the front/left speaker as emitting audio from two planes - the front plane and the left plane. You can think of the front/left - speaker emitting half of its total volume from the front, and the other half from the left. Since part of its volume is being emitted - from the left side, and the side/left and back/left channels also emit audio from the left plane, one would expect that they would - receive some amount of contribution from front/left speaker. The amount of contribution depends on how many planes are shared between - the two speakers. Note that in the examples below I've added a top/front/left speaker as an example just to show how the math works - across 3 spatial dimensions. - - The first thing to do is figure out how each speaker's volume is spread over each of plane: - - front/left: 2 planes (front and left) = 1/2 = half its total volume on each plane - - side/left: 1 plane (left only) = 1/1 = entire volume from left plane - - back/left: 2 planes (back and left) = 1/2 = half its total volume on each plane - - top/front/left: 3 planes (top, front and left) = 1/3 = one third its total volume on each plane - - The amount of volume each channel contributes to each of its planes is what controls how much it is willing to given and take to other - channels on the same plane. The volume that is willing to the given by one channel is multiplied by the volume that is willing to be - taken by the other to produce the final contribution. - */ - - /* Contribution = Sum(Volume to Give * Volume to Take) */ - float contribution = - g_maChannelPlaneRatios[channelPositionA][0] * g_maChannelPlaneRatios[channelPositionB][0] + - g_maChannelPlaneRatios[channelPositionA][1] * g_maChannelPlaneRatios[channelPositionB][1] + - g_maChannelPlaneRatios[channelPositionA][2] * g_maChannelPlaneRatios[channelPositionB][2] + - g_maChannelPlaneRatios[channelPositionA][3] * g_maChannelPlaneRatios[channelPositionB][3] + - g_maChannelPlaneRatios[channelPositionA][4] * g_maChannelPlaneRatios[channelPositionB][4] + - g_maChannelPlaneRatios[channelPositionA][5] * g_maChannelPlaneRatios[channelPositionB][5]; - - return contribution; -} - -MA_API ma_channel_converter_config ma_channel_converter_config_init(ma_format format, ma_uint32 channelsIn, const ma_channel* pChannelMapIn, ma_uint32 channelsOut, const ma_channel* pChannelMapOut, ma_channel_mix_mode mixingMode) -{ - ma_channel_converter_config config; - - MA_ZERO_OBJECT(&config); - config.format = format; - config.channelsIn = channelsIn; - config.channelsOut = channelsOut; - config.pChannelMapIn = pChannelMapIn; - config.pChannelMapOut = pChannelMapOut; - config.mixingMode = mixingMode; - - return config; -} - -static ma_int32 ma_channel_converter_float_to_fixed(float x) -{ - return (ma_int32)(x * (1< 0); - - for (iChannel = 0; iChannel < channels; ++iChannel) { - if (ma_is_spatial_channel_position(ma_channel_map_get_channel(pChannelMap, channels, iChannel))) { - spatialChannelCount++; - } - } - - return spatialChannelCount; -} - -static ma_bool32 ma_is_spatial_channel_position(ma_channel channelPosition) -{ - int i; - - if (channelPosition == MA_CHANNEL_NONE || channelPosition == MA_CHANNEL_MONO || channelPosition == MA_CHANNEL_LFE) { - return MA_FALSE; - } - - if (channelPosition >= MA_CHANNEL_AUX_0 && channelPosition <= MA_CHANNEL_AUX_31) { - return MA_FALSE; - } - - for (i = 0; i < 6; ++i) { /* Each side of a cube. */ - if (g_maChannelPlaneRatios[channelPosition][i] != 0) { - return MA_TRUE; - } - } - - return MA_FALSE; -} - - -static ma_bool32 ma_channel_map_is_passthrough(const ma_channel* pChannelMapIn, ma_uint32 channelsIn, const ma_channel* pChannelMapOut, ma_uint32 channelsOut) -{ - if (channelsOut == channelsIn) { - return ma_channel_map_is_equal(pChannelMapOut, pChannelMapIn, channelsOut); - } else { - return MA_FALSE; /* Channel counts differ, so cannot be a passthrough. */ - } -} - -static ma_channel_conversion_path ma_channel_map_get_conversion_path(const ma_channel* pChannelMapIn, ma_uint32 channelsIn, const ma_channel* pChannelMapOut, ma_uint32 channelsOut, ma_channel_mix_mode mode) -{ - if (ma_channel_map_is_passthrough(pChannelMapIn, channelsIn, pChannelMapOut, channelsOut)) { - return ma_channel_conversion_path_passthrough; - } - - if (channelsOut == 1 && (pChannelMapOut == NULL || pChannelMapOut[0] == MA_CHANNEL_MONO)) { - return ma_channel_conversion_path_mono_out; - } - - if (channelsIn == 1 && (pChannelMapIn == NULL || pChannelMapIn[0] == MA_CHANNEL_MONO)) { - return ma_channel_conversion_path_mono_in; - } - - if (mode == ma_channel_mix_mode_custom_weights) { - return ma_channel_conversion_path_weights; - } - - /* - We can use a simple shuffle if both channel maps have the same channel count and all channel - positions are present in both. - */ - if (channelsIn == channelsOut) { - ma_uint32 iChannelIn; - ma_bool32 areAllChannelPositionsPresent = MA_TRUE; - for (iChannelIn = 0; iChannelIn < channelsIn; ++iChannelIn) { - ma_bool32 isInputChannelPositionInOutput = ma_channel_map_contains_channel_position(channelsOut, pChannelMapOut, ma_channel_map_get_channel(pChannelMapIn, channelsIn, iChannelIn)); - if (!isInputChannelPositionInOutput) { - areAllChannelPositionsPresent = MA_FALSE; - break; - } - } - - if (areAllChannelPositionsPresent) { - return ma_channel_conversion_path_shuffle; - } - } - - /* Getting here means we'll need to use weights. */ - return ma_channel_conversion_path_weights; -} - - -static ma_result ma_channel_map_build_shuffle_table(const ma_channel* pChannelMapIn, ma_uint32 channelCountIn, const ma_channel* pChannelMapOut, ma_uint32 channelCountOut, ma_uint8* pShuffleTable) -{ - ma_uint32 iChannelIn; - ma_uint32 iChannelOut; - - if (pShuffleTable == NULL || channelCountIn == 0 || channelCountOut == 0) { - return MA_INVALID_ARGS; - } - - /* - When building the shuffle table we just do a 1:1 mapping based on the first occurrence of a channel. If the - input channel has more than one occurrence of a channel position, the second one will be ignored. - */ - for (iChannelOut = 0; iChannelOut < channelCountOut; iChannelOut += 1) { - ma_channel channelOut; - - /* Default to MA_CHANNEL_INDEX_NULL so that if a mapping is not found it'll be set appropriately. */ - pShuffleTable[iChannelOut] = MA_CHANNEL_INDEX_NULL; - - channelOut = ma_channel_map_get_channel(pChannelMapOut, channelCountOut, iChannelOut); - for (iChannelIn = 0; iChannelIn < channelCountIn; iChannelIn += 1) { - ma_channel channelIn; - - channelIn = ma_channel_map_get_channel(pChannelMapIn, channelCountIn, iChannelIn); - if (channelOut == channelIn) { - pShuffleTable[iChannelOut] = (ma_uint8)iChannelIn; - break; - } - - /* - Getting here means the channels don't exactly match, but we are going to support some - relaxed matching for practicality. If, for example, there are two stereo channel maps, - but one uses front left/right and the other uses side left/right, it makes logical - sense to just map these. The way we'll do it is we'll check if there is a logical - corresponding mapping, and if so, apply it, but we will *not* break from the loop, - thereby giving the loop a chance to find an exact match later which will take priority. - */ - switch (channelOut) - { - /* Left channels. */ - case MA_CHANNEL_FRONT_LEFT: - case MA_CHANNEL_SIDE_LEFT: - { - switch (channelIn) { - case MA_CHANNEL_FRONT_LEFT: - case MA_CHANNEL_SIDE_LEFT: - { - pShuffleTable[iChannelOut] = (ma_uint8)iChannelIn; - } break; - } - } break; - - /* Right channels. */ - case MA_CHANNEL_FRONT_RIGHT: - case MA_CHANNEL_SIDE_RIGHT: - { - switch (channelIn) { - case MA_CHANNEL_FRONT_RIGHT: - case MA_CHANNEL_SIDE_RIGHT: - { - pShuffleTable[iChannelOut] = (ma_uint8)iChannelIn; - } break; - } - } break; - - default: break; - } - } - } - - return MA_SUCCESS; -} - - -static void ma_channel_map_apply_shuffle_table_u8(ma_uint8* pFramesOut, ma_uint32 channelsOut, const ma_uint8* pFramesIn, ma_uint32 channelsIn, ma_uint64 frameCount, const ma_uint8* pShuffleTable) -{ - ma_uint64 iFrame; - ma_uint32 iChannelOut; - - for (iFrame = 0; iFrame < frameCount; iFrame += 1) { - for (iChannelOut = 0; iChannelOut < channelsOut; iChannelOut += 1) { - ma_uint8 iChannelIn = pShuffleTable[iChannelOut]; - if (iChannelIn < channelsIn) { /* For safety, and to deal with MA_CHANNEL_INDEX_NULL. */ - pFramesOut[iChannelOut] = pFramesIn[iChannelIn]; - } else { - pFramesOut[iChannelOut] = 0; - } - } - - pFramesOut += channelsOut; - pFramesIn += channelsIn; - } -} - -static void ma_channel_map_apply_shuffle_table_s16(ma_int16* pFramesOut, ma_uint32 channelsOut, const ma_int16* pFramesIn, ma_uint32 channelsIn, ma_uint64 frameCount, const ma_uint8* pShuffleTable) -{ - ma_uint64 iFrame; - ma_uint32 iChannelOut; - - for (iFrame = 0; iFrame < frameCount; iFrame += 1) { - for (iChannelOut = 0; iChannelOut < channelsOut; iChannelOut += 1) { - ma_uint8 iChannelIn = pShuffleTable[iChannelOut]; - if (iChannelIn < channelsIn) { /* For safety, and to deal with MA_CHANNEL_INDEX_NULL. */ - pFramesOut[iChannelOut] = pFramesIn[iChannelIn]; - } else { - pFramesOut[iChannelOut] = 0; - } - } - - pFramesOut += channelsOut; - pFramesIn += channelsIn; - } -} - -static void ma_channel_map_apply_shuffle_table_s24(ma_uint8* pFramesOut, ma_uint32 channelsOut, const ma_uint8* pFramesIn, ma_uint32 channelsIn, ma_uint64 frameCount, const ma_uint8* pShuffleTable) -{ - ma_uint64 iFrame; - ma_uint32 iChannelOut; - - for (iFrame = 0; iFrame < frameCount; iFrame += 1) { - for (iChannelOut = 0; iChannelOut < channelsOut; iChannelOut += 1) { - ma_uint8 iChannelIn = pShuffleTable[iChannelOut]; - if (iChannelIn < channelsIn) { /* For safety, and to deal with MA_CHANNEL_INDEX_NULL. */ - pFramesOut[iChannelOut*3 + 0] = pFramesIn[iChannelIn*3 + 0]; - pFramesOut[iChannelOut*3 + 1] = pFramesIn[iChannelIn*3 + 1]; - pFramesOut[iChannelOut*3 + 2] = pFramesIn[iChannelIn*3 + 2]; - } else { - pFramesOut[iChannelOut*3 + 0] = 0; - } pFramesOut[iChannelOut*3 + 1] = 0; - } pFramesOut[iChannelOut*3 + 2] = 0; - - pFramesOut += channelsOut*3; - pFramesIn += channelsIn*3; - } -} - -static void ma_channel_map_apply_shuffle_table_s32(ma_int32* pFramesOut, ma_uint32 channelsOut, const ma_int32* pFramesIn, ma_uint32 channelsIn, ma_uint64 frameCount, const ma_uint8* pShuffleTable) -{ - ma_uint64 iFrame; - ma_uint32 iChannelOut; - - for (iFrame = 0; iFrame < frameCount; iFrame += 1) { - for (iChannelOut = 0; iChannelOut < channelsOut; iChannelOut += 1) { - ma_uint8 iChannelIn = pShuffleTable[iChannelOut]; - if (iChannelIn < channelsIn) { /* For safety, and to deal with MA_CHANNEL_INDEX_NULL. */ - pFramesOut[iChannelOut] = pFramesIn[iChannelIn]; - } else { - pFramesOut[iChannelOut] = 0; - } - } - - pFramesOut += channelsOut; - pFramesIn += channelsIn; - } -} - -static void ma_channel_map_apply_shuffle_table_f32(float* pFramesOut, ma_uint32 channelsOut, const float* pFramesIn, ma_uint32 channelsIn, ma_uint64 frameCount, const ma_uint8* pShuffleTable) -{ - ma_uint64 iFrame; - ma_uint32 iChannelOut; - - for (iFrame = 0; iFrame < frameCount; iFrame += 1) { - for (iChannelOut = 0; iChannelOut < channelsOut; iChannelOut += 1) { - ma_uint8 iChannelIn = pShuffleTable[iChannelOut]; - if (iChannelIn < channelsIn) { /* For safety, and to deal with MA_CHANNEL_INDEX_NULL. */ - pFramesOut[iChannelOut] = pFramesIn[iChannelIn]; - } else { - pFramesOut[iChannelOut] = 0; - } - } - - pFramesOut += channelsOut; - pFramesIn += channelsIn; - } -} - -static ma_result ma_channel_map_apply_shuffle_table(void* pFramesOut, ma_uint32 channelsOut, const void* pFramesIn, ma_uint32 channelsIn, ma_uint64 frameCount, const ma_uint8* pShuffleTable, ma_format format) -{ - if (pFramesOut == NULL || pFramesIn == NULL || channelsOut == 0 || pShuffleTable == NULL) { - return MA_INVALID_ARGS; - } - - switch (format) - { - case ma_format_u8: - { - ma_channel_map_apply_shuffle_table_u8((ma_uint8*)pFramesOut, channelsOut, (const ma_uint8*)pFramesIn, channelsIn, frameCount, pShuffleTable); - } break; - - case ma_format_s16: - { - ma_channel_map_apply_shuffle_table_s16((ma_int16*)pFramesOut, channelsOut, (const ma_int16*)pFramesIn, channelsIn, frameCount, pShuffleTable); - } break; - - case ma_format_s24: - { - ma_channel_map_apply_shuffle_table_s24((ma_uint8*)pFramesOut, channelsOut, (const ma_uint8*)pFramesIn, channelsIn, frameCount, pShuffleTable); - } break; - - case ma_format_s32: - { - ma_channel_map_apply_shuffle_table_s32((ma_int32*)pFramesOut, channelsOut, (const ma_int32*)pFramesIn, channelsIn, frameCount, pShuffleTable); - } break; - - case ma_format_f32: - { - ma_channel_map_apply_shuffle_table_f32((float*)pFramesOut, channelsOut, (const float*)pFramesIn, channelsIn, frameCount, pShuffleTable); - } break; - - default: return MA_INVALID_ARGS; /* Unknown format. */ - } - - return MA_SUCCESS; -} - -static ma_result ma_channel_map_apply_mono_out_f32(float* pFramesOut, const float* pFramesIn, const ma_channel* pChannelMapIn, ma_uint32 channelsIn, ma_uint64 frameCount) -{ - ma_uint64 iFrame; - ma_uint32 iChannelIn; - ma_uint32 accumulationCount; - - if (pFramesOut == NULL || pFramesIn == NULL || channelsIn == 0) { - return MA_INVALID_ARGS; - } - - /* In this case the output stream needs to be the average of all channels, ignoring NONE. */ - - /* A quick pre-processing step to get the accumulation counter since we're ignoring NONE channels. */ - accumulationCount = 0; - for (iChannelIn = 0; iChannelIn < channelsIn; iChannelIn += 1) { - if (ma_channel_map_get_channel(pChannelMapIn, channelsIn, iChannelIn) != MA_CHANNEL_NONE) { - accumulationCount += 1; - } - } - - if (accumulationCount > 0) { /* <-- Prevent a division by zero. */ - for (iFrame = 0; iFrame < frameCount; iFrame += 1) { - float accumulation = 0; - - for (iChannelIn = 0; iChannelIn < channelsIn; iChannelIn += 1) { - ma_channel channelIn = ma_channel_map_get_channel(pChannelMapIn, channelsIn, iChannelIn); - if (channelIn != MA_CHANNEL_NONE) { - accumulation += pFramesIn[iChannelIn]; - } - } - - pFramesOut[0] = accumulation / accumulationCount; - pFramesOut += 1; - pFramesIn += channelsIn; - } - } else { - ma_silence_pcm_frames(pFramesOut, frameCount, ma_format_f32, 1); - } - - return MA_SUCCESS; -} - -static ma_result ma_channel_map_apply_mono_in_f32(float* MA_RESTRICT pFramesOut, const ma_channel* pChannelMapOut, ma_uint32 channelsOut, const float* MA_RESTRICT pFramesIn, ma_uint64 frameCount, ma_mono_expansion_mode monoExpansionMode) -{ - ma_uint64 iFrame; - ma_uint32 iChannelOut; - - if (pFramesOut == NULL || channelsOut == 0 || pFramesIn == NULL) { - return MA_INVALID_ARGS; - } - - /* Note that the MA_CHANNEL_NONE channel must be ignored in all cases. */ - switch (monoExpansionMode) - { - case ma_mono_expansion_mode_average: - { - float weight; - ma_uint32 validChannelCount = 0; - - for (iChannelOut = 0; iChannelOut < channelsOut; iChannelOut += 1) { - ma_channel channelOut = ma_channel_map_get_channel(pChannelMapOut, channelsOut, iChannelOut); - if (channelOut != MA_CHANNEL_NONE) { - validChannelCount += 1; - } - } - - weight = 1.0f / validChannelCount; - - for (iFrame = 0; iFrame < frameCount; iFrame += 1) { - for (iChannelOut = 0; iChannelOut < channelsOut; iChannelOut += 1) { - ma_channel channelOut = ma_channel_map_get_channel(pChannelMapOut, channelsOut, iChannelOut); - if (channelOut != MA_CHANNEL_NONE) { - pFramesOut[iChannelOut] = pFramesIn[0] * weight; - } - } - - pFramesOut += channelsOut; - pFramesIn += 1; - } - } break; - - case ma_mono_expansion_mode_stereo_only: - { - if (channelsOut >= 2) { - ma_uint32 iChannelLeft = (ma_uint32)-1; - ma_uint32 iChannelRight = (ma_uint32)-1; - - /* - We first need to find our stereo channels. We prefer front-left and front-right, but - if they're not available, we'll also try side-left and side-right. If neither are - available we'll fall through to the default case below. - */ - for (iChannelOut = 0; iChannelOut < channelsOut; iChannelOut += 1) { - ma_channel channelOut = ma_channel_map_get_channel(pChannelMapOut, channelsOut, iChannelOut); - if (channelOut == MA_CHANNEL_SIDE_LEFT) { - iChannelLeft = iChannelOut; - } - if (channelOut == MA_CHANNEL_SIDE_RIGHT) { - iChannelRight = iChannelOut; - } - } - - for (iChannelOut = 0; iChannelOut < channelsOut; iChannelOut += 1) { - ma_channel channelOut = ma_channel_map_get_channel(pChannelMapOut, channelsOut, iChannelOut); - if (channelOut == MA_CHANNEL_FRONT_LEFT) { - iChannelLeft = iChannelOut; - } - if (channelOut == MA_CHANNEL_FRONT_RIGHT) { - iChannelRight = iChannelOut; - } - } - - - if (iChannelLeft != (ma_uint32)-1 && iChannelRight != (ma_uint32)-1) { - /* We found our stereo channels so we can duplicate the signal across those channels. */ - for (iFrame = 0; iFrame < frameCount; iFrame += 1) { - for (iChannelOut = 0; iChannelOut < channelsOut; iChannelOut += 1) { - ma_channel channelOut = ma_channel_map_get_channel(pChannelMapOut, channelsOut, iChannelOut); - if (channelOut != MA_CHANNEL_NONE) { - if (iChannelOut == iChannelLeft || iChannelOut == iChannelRight) { - pFramesOut[iChannelOut] = pFramesIn[0]; - } else { - pFramesOut[iChannelOut] = 0.0f; - } - } - } - - pFramesOut += channelsOut; - pFramesIn += 1; - } - - break; /* Get out of the switch. */ - } else { - /* Fallthrough. Does not have left and right channels. */ - goto default_handler; - } - } else { - /* Fallthrough. Does not have stereo channels. */ - goto default_handler; - } - }; /* Fallthrough. See comments above. */ - - case ma_mono_expansion_mode_duplicate: - default: - { - default_handler: - { - if (channelsOut <= MA_MAX_CHANNELS) { - ma_bool32 hasEmptyChannel = MA_FALSE; - ma_channel channelPositions[MA_MAX_CHANNELS]; - for (iChannelOut = 0; iChannelOut < channelsOut; iChannelOut += 1) { - channelPositions[iChannelOut] = ma_channel_map_get_channel(pChannelMapOut, channelsOut, iChannelOut); - if (channelPositions[iChannelOut] == MA_CHANNEL_NONE) { - hasEmptyChannel = MA_TRUE; - } - } - - if (hasEmptyChannel == MA_FALSE) { - /* - Faster path when there's no MA_CHANNEL_NONE channel positions. This should hopefully - help the compiler with auto-vectorization.m - */ - if (channelsOut == 2) { - #if defined(MA_SUPPORT_SSE2) - if (ma_has_sse2()) { - /* We want to do two frames in each iteration. */ - ma_uint64 unrolledFrameCount = frameCount >> 1; - - for (iFrame = 0; iFrame < unrolledFrameCount; iFrame += 1) { - __m128 in0 = _mm_set1_ps(pFramesIn[iFrame*2 + 0]); - __m128 in1 = _mm_set1_ps(pFramesIn[iFrame*2 + 1]); - _mm_storeu_ps(&pFramesOut[iFrame*4 + 0], _mm_shuffle_ps(in0, in1, _MM_SHUFFLE(0, 0, 0, 0))); - } - - /* Tail. */ - iFrame = unrolledFrameCount << 1; - goto generic_on_fastpath; - } else - #endif - { - for (iFrame = 0; iFrame < frameCount; iFrame += 1) { - for (iChannelOut = 0; iChannelOut < 2; iChannelOut += 1) { - pFramesOut[iFrame*2 + iChannelOut] = pFramesIn[iFrame]; - } - } - } - } else if (channelsOut == 6) { - #if defined(MA_SUPPORT_SSE2) - if (ma_has_sse2()) { - /* We want to do two frames in each iteration so we can have a multiple of 4 samples. */ - ma_uint64 unrolledFrameCount = frameCount >> 1; - - for (iFrame = 0; iFrame < unrolledFrameCount; iFrame += 1) { - __m128 in0 = _mm_set1_ps(pFramesIn[iFrame*2 + 0]); - __m128 in1 = _mm_set1_ps(pFramesIn[iFrame*2 + 1]); - - _mm_storeu_ps(&pFramesOut[iFrame*12 + 0], in0); - _mm_storeu_ps(&pFramesOut[iFrame*12 + 4], _mm_shuffle_ps(in0, in1, _MM_SHUFFLE(0, 0, 0, 0))); - _mm_storeu_ps(&pFramesOut[iFrame*12 + 8], in1); - } - - /* Tail. */ - iFrame = unrolledFrameCount << 1; - goto generic_on_fastpath; - } else - #endif - { - for (iFrame = 0; iFrame < frameCount; iFrame += 1) { - for (iChannelOut = 0; iChannelOut < 6; iChannelOut += 1) { - pFramesOut[iFrame*6 + iChannelOut] = pFramesIn[iFrame]; - } - } - } - } else if (channelsOut == 8) { - #if defined(MA_SUPPORT_SSE2) - if (ma_has_sse2()) { - for (iFrame = 0; iFrame < frameCount; iFrame += 1) { - __m128 in = _mm_set1_ps(pFramesIn[iFrame]); - _mm_storeu_ps(&pFramesOut[iFrame*8 + 0], in); - _mm_storeu_ps(&pFramesOut[iFrame*8 + 4], in); - } - } else - #endif - { - for (iFrame = 0; iFrame < frameCount; iFrame += 1) { - for (iChannelOut = 0; iChannelOut < 8; iChannelOut += 1) { - pFramesOut[iFrame*8 + iChannelOut] = pFramesIn[iFrame]; - } - } - } - } else { - iFrame = 0; - - #if defined(MA_SUPPORT_SSE2) /* For silencing a warning with non-x86 builds. */ - generic_on_fastpath: - #endif - { - for (; iFrame < frameCount; iFrame += 1) { - for (iChannelOut = 0; iChannelOut < channelsOut; iChannelOut += 1) { - pFramesOut[iFrame*channelsOut + iChannelOut] = pFramesIn[iFrame]; - } - } - } - } - } else { - /* Slow path. Need to handle MA_CHANNEL_NONE. */ - for (iFrame = 0; iFrame < frameCount; iFrame += 1) { - for (iChannelOut = 0; iChannelOut < channelsOut; iChannelOut += 1) { - if (channelPositions[iChannelOut] != MA_CHANNEL_NONE) { - pFramesOut[iFrame*channelsOut + iChannelOut] = pFramesIn[iFrame]; - } - } - } - } - } else { - /* Slow path. Too many channels to store on the stack. */ - for (iFrame = 0; iFrame < frameCount; iFrame += 1) { - for (iChannelOut = 0; iChannelOut < channelsOut; iChannelOut += 1) { - ma_channel channelOut = ma_channel_map_get_channel(pChannelMapOut, channelsOut, iChannelOut); - if (channelOut != MA_CHANNEL_NONE) { - pFramesOut[iFrame*channelsOut + iChannelOut] = pFramesIn[iFrame]; - } - } - } - } - } - } break; - } - - return MA_SUCCESS; -} - -static void ma_channel_map_apply_f32(float* pFramesOut, const ma_channel* pChannelMapOut, ma_uint32 channelsOut, const float* pFramesIn, const ma_channel* pChannelMapIn, ma_uint32 channelsIn, ma_uint64 frameCount, ma_channel_mix_mode mode, ma_mono_expansion_mode monoExpansionMode) -{ - ma_channel_conversion_path conversionPath = ma_channel_map_get_conversion_path(pChannelMapIn, channelsIn, pChannelMapOut, channelsOut, mode); - - /* Optimized Path: Passthrough */ - if (conversionPath == ma_channel_conversion_path_passthrough) { - ma_copy_pcm_frames(pFramesOut, pFramesIn, frameCount, ma_format_f32, channelsOut); - return; - } - - /* Special Path: Mono Output. */ - if (conversionPath == ma_channel_conversion_path_mono_out) { - ma_channel_map_apply_mono_out_f32(pFramesOut, pFramesIn, pChannelMapIn, channelsIn, frameCount); - return; - } - - /* Special Path: Mono Input. */ - if (conversionPath == ma_channel_conversion_path_mono_in) { - ma_channel_map_apply_mono_in_f32(pFramesOut, pChannelMapOut, channelsOut, pFramesIn, frameCount, monoExpansionMode); - return; - } - - /* Getting here means we aren't running on an optimized conversion path. */ - if (channelsOut <= MA_MAX_CHANNELS) { - ma_result result; - - if (mode == ma_channel_mix_mode_simple) { - ma_channel shuffleTable[MA_MAX_CHANNELS]; - - result = ma_channel_map_build_shuffle_table(pChannelMapIn, channelsIn, pChannelMapOut, channelsOut, shuffleTable); - if (result != MA_SUCCESS) { - return; - } - - result = ma_channel_map_apply_shuffle_table(pFramesOut, channelsOut, pFramesIn, channelsIn, frameCount, shuffleTable, ma_format_f32); - if (result != MA_SUCCESS) { - return; - } - } else { - ma_uint32 iFrame; - ma_uint32 iChannelOut; - ma_uint32 iChannelIn; - float weights[32][32]; /* Do not use MA_MAX_CHANNELS here! */ - - /* - If we have a small enough number of channels, pre-compute the weights. Otherwise we'll just need to - fall back to a slower path because otherwise we'll run out of stack space. - */ - if (channelsIn <= ma_countof(weights) && channelsOut <= ma_countof(weights)) { - /* Pre-compute weights. */ - for (iChannelOut = 0; iChannelOut < channelsOut; iChannelOut += 1) { - ma_channel channelOut = ma_channel_map_get_channel(pChannelMapOut, channelsOut, iChannelOut); - for (iChannelIn = 0; iChannelIn < channelsIn; iChannelIn += 1) { - ma_channel channelIn = ma_channel_map_get_channel(pChannelMapIn, channelsIn, iChannelIn); - weights[iChannelOut][iChannelIn] = ma_calculate_channel_position_rectangular_weight(channelOut, channelIn); - } - } - - iFrame = 0; - - /* Experiment: Try an optimized unroll for some specific cases to see how it improves performance. RESULT: Good gains. */ - if (channelsOut == 8) { - /* Experiment 2: Expand the inner loop to see what kind of different it makes. RESULT: Small, but worthwhile gain. */ - if (channelsIn == 2) { - for (; iFrame < frameCount; iFrame += 1) { - float accumulation[8] = { 0, 0, 0, 0, 0, 0, 0, 0 }; - - accumulation[0] += pFramesIn[iFrame*2 + 0] * weights[0][0]; - accumulation[1] += pFramesIn[iFrame*2 + 0] * weights[1][0]; - accumulation[2] += pFramesIn[iFrame*2 + 0] * weights[2][0]; - accumulation[3] += pFramesIn[iFrame*2 + 0] * weights[3][0]; - accumulation[4] += pFramesIn[iFrame*2 + 0] * weights[4][0]; - accumulation[5] += pFramesIn[iFrame*2 + 0] * weights[5][0]; - accumulation[6] += pFramesIn[iFrame*2 + 0] * weights[6][0]; - accumulation[7] += pFramesIn[iFrame*2 + 0] * weights[7][0]; - - accumulation[0] += pFramesIn[iFrame*2 + 1] * weights[0][1]; - accumulation[1] += pFramesIn[iFrame*2 + 1] * weights[1][1]; - accumulation[2] += pFramesIn[iFrame*2 + 1] * weights[2][1]; - accumulation[3] += pFramesIn[iFrame*2 + 1] * weights[3][1]; - accumulation[4] += pFramesIn[iFrame*2 + 1] * weights[4][1]; - accumulation[5] += pFramesIn[iFrame*2 + 1] * weights[5][1]; - accumulation[6] += pFramesIn[iFrame*2 + 1] * weights[6][1]; - accumulation[7] += pFramesIn[iFrame*2 + 1] * weights[7][1]; - - pFramesOut[iFrame*8 + 0] = accumulation[0]; - pFramesOut[iFrame*8 + 1] = accumulation[1]; - pFramesOut[iFrame*8 + 2] = accumulation[2]; - pFramesOut[iFrame*8 + 3] = accumulation[3]; - pFramesOut[iFrame*8 + 4] = accumulation[4]; - pFramesOut[iFrame*8 + 5] = accumulation[5]; - pFramesOut[iFrame*8 + 6] = accumulation[6]; - pFramesOut[iFrame*8 + 7] = accumulation[7]; - } - } else { - /* When outputting to 8 channels, we can do everything in groups of two 4x SIMD operations. */ - for (; iFrame < frameCount; iFrame += 1) { - float accumulation[8] = { 0, 0, 0, 0, 0, 0, 0, 0 }; - - for (iChannelIn = 0; iChannelIn < channelsIn; iChannelIn += 1) { - accumulation[0] += pFramesIn[iFrame*channelsIn + iChannelIn] * weights[0][iChannelIn]; - accumulation[1] += pFramesIn[iFrame*channelsIn + iChannelIn] * weights[1][iChannelIn]; - accumulation[2] += pFramesIn[iFrame*channelsIn + iChannelIn] * weights[2][iChannelIn]; - accumulation[3] += pFramesIn[iFrame*channelsIn + iChannelIn] * weights[3][iChannelIn]; - accumulation[4] += pFramesIn[iFrame*channelsIn + iChannelIn] * weights[4][iChannelIn]; - accumulation[5] += pFramesIn[iFrame*channelsIn + iChannelIn] * weights[5][iChannelIn]; - accumulation[6] += pFramesIn[iFrame*channelsIn + iChannelIn] * weights[6][iChannelIn]; - accumulation[7] += pFramesIn[iFrame*channelsIn + iChannelIn] * weights[7][iChannelIn]; - } - - pFramesOut[iFrame*8 + 0] = accumulation[0]; - pFramesOut[iFrame*8 + 1] = accumulation[1]; - pFramesOut[iFrame*8 + 2] = accumulation[2]; - pFramesOut[iFrame*8 + 3] = accumulation[3]; - pFramesOut[iFrame*8 + 4] = accumulation[4]; - pFramesOut[iFrame*8 + 5] = accumulation[5]; - pFramesOut[iFrame*8 + 6] = accumulation[6]; - pFramesOut[iFrame*8 + 7] = accumulation[7]; - } - } - } else if (channelsOut == 6) { - /* - When outputting to 6 channels we unfortunately don't have a nice multiple of 4 to do 4x SIMD operations. Instead we'll - expand our weights and do two frames at a time. - */ - for (; iFrame < frameCount; iFrame += 1) { - float accumulation[12] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; - - for (iChannelIn = 0; iChannelIn < channelsIn; iChannelIn += 1) { - accumulation[0] += pFramesIn[iFrame*channelsIn + iChannelIn] * weights[0][iChannelIn]; - accumulation[1] += pFramesIn[iFrame*channelsIn + iChannelIn] * weights[1][iChannelIn]; - accumulation[2] += pFramesIn[iFrame*channelsIn + iChannelIn] * weights[2][iChannelIn]; - accumulation[3] += pFramesIn[iFrame*channelsIn + iChannelIn] * weights[3][iChannelIn]; - accumulation[4] += pFramesIn[iFrame*channelsIn + iChannelIn] * weights[4][iChannelIn]; - accumulation[5] += pFramesIn[iFrame*channelsIn + iChannelIn] * weights[5][iChannelIn]; - } - - pFramesOut[iFrame*6 + 0] = accumulation[0]; - pFramesOut[iFrame*6 + 1] = accumulation[1]; - pFramesOut[iFrame*6 + 2] = accumulation[2]; - pFramesOut[iFrame*6 + 3] = accumulation[3]; - pFramesOut[iFrame*6 + 4] = accumulation[4]; - pFramesOut[iFrame*6 + 5] = accumulation[5]; - } - } - - /* Leftover frames. */ - for (; iFrame < frameCount; iFrame += 1) { - for (iChannelOut = 0; iChannelOut < channelsOut; iChannelOut += 1) { - float accumulation = 0; - - for (iChannelIn = 0; iChannelIn < channelsIn; iChannelIn += 1) { - accumulation += pFramesIn[iFrame*channelsIn + iChannelIn] * weights[iChannelOut][iChannelIn]; - } - - pFramesOut[iFrame*channelsOut + iChannelOut] = accumulation; - } - } - } else { - /* Cannot pre-compute weights because not enough room in stack-allocated buffer. */ - for (iFrame = 0; iFrame < frameCount; iFrame += 1) { - for (iChannelOut = 0; iChannelOut < channelsOut; iChannelOut += 1) { - float accumulation = 0; - ma_channel channelOut = ma_channel_map_get_channel(pChannelMapOut, channelsOut, iChannelOut); - - for (iChannelIn = 0; iChannelIn < channelsIn; iChannelIn += 1) { - ma_channel channelIn = ma_channel_map_get_channel(pChannelMapIn, channelsIn, iChannelIn); - accumulation += pFramesIn[iFrame*channelsIn + iChannelIn] * ma_calculate_channel_position_rectangular_weight(channelOut, channelIn); - } - - pFramesOut[iFrame*channelsOut + iChannelOut] = accumulation; - } - } - } - } - } else { - /* Fall back to silence. If you hit this, what are you doing with so many channels?! */ - ma_silence_pcm_frames(pFramesOut, frameCount, ma_format_f32, channelsOut); - } -} - - -typedef struct -{ - size_t sizeInBytes; - size_t channelMapInOffset; - size_t channelMapOutOffset; - size_t shuffleTableOffset; - size_t weightsOffset; -} ma_channel_converter_heap_layout; - -static ma_channel_conversion_path ma_channel_converter_config_get_conversion_path(const ma_channel_converter_config* pConfig) -{ - return ma_channel_map_get_conversion_path(pConfig->pChannelMapIn, pConfig->channelsIn, pConfig->pChannelMapOut, pConfig->channelsOut, pConfig->mixingMode); -} - -static ma_result ma_channel_converter_get_heap_layout(const ma_channel_converter_config* pConfig, ma_channel_converter_heap_layout* pHeapLayout) -{ - ma_channel_conversion_path conversionPath; - - MA_ASSERT(pHeapLayout != NULL); - - if (pConfig == NULL) { - return MA_INVALID_ARGS; - } - - if (pConfig->channelsIn == 0 || pConfig->channelsOut == 0) { - return MA_INVALID_ARGS; - } - - if (!ma_channel_map_is_valid(pConfig->pChannelMapIn, pConfig->channelsIn)) { - return MA_INVALID_ARGS; - } - - if (!ma_channel_map_is_valid(pConfig->pChannelMapOut, pConfig->channelsOut)) { - return MA_INVALID_ARGS; - } - - pHeapLayout->sizeInBytes = 0; - - /* Input channel map. Only need to allocate this if we have an input channel map (otherwise default channel map is assumed). */ - pHeapLayout->channelMapInOffset = pHeapLayout->sizeInBytes; - if (pConfig->pChannelMapIn != NULL) { - pHeapLayout->sizeInBytes += sizeof(ma_channel) * pConfig->channelsIn; - } - - /* Output channel map. Only need to allocate this if we have an output channel map (otherwise default channel map is assumed). */ - pHeapLayout->channelMapOutOffset = pHeapLayout->sizeInBytes; - if (pConfig->pChannelMapOut != NULL) { - pHeapLayout->sizeInBytes += sizeof(ma_channel) * pConfig->channelsOut; - } - - /* Alignment for the next section. */ - pHeapLayout->sizeInBytes = ma_align_64(pHeapLayout->sizeInBytes); - - /* Whether or not we use weights of a shuffle table depends on the channel map themselves and the algorithm we've chosen. */ - conversionPath = ma_channel_converter_config_get_conversion_path(pConfig); - - /* Shuffle table */ - pHeapLayout->shuffleTableOffset = pHeapLayout->sizeInBytes; - if (conversionPath == ma_channel_conversion_path_shuffle) { - pHeapLayout->sizeInBytes += sizeof(ma_uint8) * pConfig->channelsOut; - } - - /* Weights */ - pHeapLayout->weightsOffset = pHeapLayout->sizeInBytes; - if (conversionPath == ma_channel_conversion_path_weights) { - pHeapLayout->sizeInBytes += sizeof(float*) * pConfig->channelsIn; - pHeapLayout->sizeInBytes += sizeof(float ) * pConfig->channelsIn * pConfig->channelsOut; - } - - /* Make sure allocation size is aligned. */ - pHeapLayout->sizeInBytes = ma_align_64(pHeapLayout->sizeInBytes); - - return MA_SUCCESS; -} - -MA_API ma_result ma_channel_converter_get_heap_size(const ma_channel_converter_config* pConfig, size_t* pHeapSizeInBytes) -{ - ma_result result; - ma_channel_converter_heap_layout heapLayout; - - if (pHeapSizeInBytes == NULL) { - return MA_INVALID_ARGS; - } - - *pHeapSizeInBytes = 0; - - result = ma_channel_converter_get_heap_layout(pConfig, &heapLayout); - if (result != MA_SUCCESS) { - return result; - } - - *pHeapSizeInBytes = heapLayout.sizeInBytes; - - return MA_SUCCESS; -} - -MA_API ma_result ma_channel_converter_init_preallocated(const ma_channel_converter_config* pConfig, void* pHeap, ma_channel_converter* pConverter) -{ - ma_result result; - ma_channel_converter_heap_layout heapLayout; - - if (pConverter == NULL) { - return MA_INVALID_ARGS; - } - - MA_ZERO_OBJECT(pConverter); - - result = ma_channel_converter_get_heap_layout(pConfig, &heapLayout); - if (result != MA_SUCCESS) { - return result; - } - - pConverter->_pHeap = pHeap; - MA_ZERO_MEMORY(pConverter->_pHeap, heapLayout.sizeInBytes); - - pConverter->format = pConfig->format; - pConverter->channelsIn = pConfig->channelsIn; - pConverter->channelsOut = pConfig->channelsOut; - pConverter->mixingMode = pConfig->mixingMode; - - if (pConfig->pChannelMapIn != NULL) { - pConverter->pChannelMapIn = (ma_channel*)ma_offset_ptr(pHeap, heapLayout.channelMapInOffset); - ma_channel_map_copy_or_default(pConverter->pChannelMapIn, pConfig->channelsIn, pConfig->pChannelMapIn, pConfig->channelsIn); - } else { - pConverter->pChannelMapIn = NULL; /* Use default channel map. */ - } - - if (pConfig->pChannelMapOut != NULL) { - pConverter->pChannelMapOut = (ma_channel*)ma_offset_ptr(pHeap, heapLayout.channelMapOutOffset); - ma_channel_map_copy_or_default(pConverter->pChannelMapOut, pConfig->channelsOut, pConfig->pChannelMapOut, pConfig->channelsOut); - } else { - pConverter->pChannelMapOut = NULL; /* Use default channel map. */ - } - - pConverter->conversionPath = ma_channel_converter_config_get_conversion_path(pConfig); - - if (pConverter->conversionPath == ma_channel_conversion_path_shuffle) { - pConverter->pShuffleTable = (ma_uint8*)ma_offset_ptr(pHeap, heapLayout.shuffleTableOffset); - ma_channel_map_build_shuffle_table(pConverter->pChannelMapIn, pConverter->channelsIn, pConverter->pChannelMapOut, pConverter->channelsOut, pConverter->pShuffleTable); - } - - if (pConverter->conversionPath == ma_channel_conversion_path_weights) { - ma_uint32 iChannelIn; - ma_uint32 iChannelOut; - - if (pConverter->format == ma_format_f32) { - pConverter->weights.f32 = (float** )ma_offset_ptr(pHeap, heapLayout.weightsOffset); - for (iChannelIn = 0; iChannelIn < pConverter->channelsIn; iChannelIn += 1) { - pConverter->weights.f32[iChannelIn] = (float*)ma_offset_ptr(pHeap, heapLayout.weightsOffset + ((sizeof(float*) * pConverter->channelsIn) + (sizeof(float) * pConverter->channelsOut * iChannelIn))); - } - } else { - pConverter->weights.s16 = (ma_int32**)ma_offset_ptr(pHeap, heapLayout.weightsOffset); - for (iChannelIn = 0; iChannelIn < pConverter->channelsIn; iChannelIn += 1) { - pConverter->weights.s16[iChannelIn] = (ma_int32*)ma_offset_ptr(pHeap, heapLayout.weightsOffset + ((sizeof(ma_int32*) * pConverter->channelsIn) + (sizeof(ma_int32) * pConverter->channelsOut * iChannelIn))); - } - } - - /* Silence our weights by default. */ - for (iChannelIn = 0; iChannelIn < pConverter->channelsIn; iChannelIn += 1) { - for (iChannelOut = 0; iChannelOut < pConverter->channelsOut; iChannelOut += 1) { - if (pConverter->format == ma_format_f32) { - pConverter->weights.f32[iChannelIn][iChannelOut] = 0.0f; - } else { - pConverter->weights.s16[iChannelIn][iChannelOut] = 0; - } - } - } - - /* - We now need to fill out our weights table. This is determined by the mixing mode. - */ - - /* In all cases we need to make sure all channels that are present in both channel maps have a 1:1 mapping. */ - for (iChannelIn = 0; iChannelIn < pConverter->channelsIn; ++iChannelIn) { - ma_channel channelPosIn = ma_channel_map_get_channel(pConverter->pChannelMapIn, pConverter->channelsIn, iChannelIn); - - for (iChannelOut = 0; iChannelOut < pConverter->channelsOut; ++iChannelOut) { - ma_channel channelPosOut = ma_channel_map_get_channel(pConverter->pChannelMapOut, pConverter->channelsOut, iChannelOut); - - if (channelPosIn == channelPosOut) { - float weight = 1; - - if (pConverter->format == ma_format_f32) { - pConverter->weights.f32[iChannelIn][iChannelOut] = weight; - } else { - pConverter->weights.s16[iChannelIn][iChannelOut] = ma_channel_converter_float_to_fixed(weight); - } - } - } - } - - switch (pConverter->mixingMode) - { - case ma_channel_mix_mode_custom_weights: - { - if (pConfig->ppWeights == NULL) { - return MA_INVALID_ARGS; /* Config specified a custom weights mixing mode, but no custom weights have been specified. */ - } - - for (iChannelIn = 0; iChannelIn < pConverter->channelsIn; iChannelIn += 1) { - for (iChannelOut = 0; iChannelOut < pConverter->channelsOut; iChannelOut += 1) { - float weight = pConfig->ppWeights[iChannelIn][iChannelOut]; - - if (pConverter->format == ma_format_f32) { - pConverter->weights.f32[iChannelIn][iChannelOut] = weight; - } else { - pConverter->weights.s16[iChannelIn][iChannelOut] = ma_channel_converter_float_to_fixed(weight); - } - } - } - } break; - - case ma_channel_mix_mode_simple: - { - /* - In simple mode, only set weights for channels that have exactly matching types, leave the rest at - zero. The 1:1 mappings have already been covered before this switch statement. - */ - } break; - - case ma_channel_mix_mode_rectangular: - default: - { - /* Unmapped input channels. */ - for (iChannelIn = 0; iChannelIn < pConverter->channelsIn; ++iChannelIn) { - ma_channel channelPosIn = ma_channel_map_get_channel(pConverter->pChannelMapIn, pConverter->channelsIn, iChannelIn); - - if (ma_is_spatial_channel_position(channelPosIn)) { - if (!ma_channel_map_contains_channel_position(pConverter->channelsOut, pConverter->pChannelMapOut, channelPosIn)) { - for (iChannelOut = 0; iChannelOut < pConverter->channelsOut; ++iChannelOut) { - ma_channel channelPosOut = ma_channel_map_get_channel(pConverter->pChannelMapOut, pConverter->channelsOut, iChannelOut); - - if (ma_is_spatial_channel_position(channelPosOut)) { - float weight = 0; - if (pConverter->mixingMode == ma_channel_mix_mode_rectangular) { - weight = ma_calculate_channel_position_rectangular_weight(channelPosIn, channelPosOut); - } - - /* Only apply the weight if we haven't already got some contribution from the respective channels. */ - if (pConverter->format == ma_format_f32) { - if (pConverter->weights.f32[iChannelIn][iChannelOut] == 0) { - pConverter->weights.f32[iChannelIn][iChannelOut] = weight; - } - } else { - if (pConverter->weights.s16[iChannelIn][iChannelOut] == 0) { - pConverter->weights.s16[iChannelIn][iChannelOut] = ma_channel_converter_float_to_fixed(weight); - } - } - } - } - } - } - } - - /* Unmapped output channels. */ - for (iChannelOut = 0; iChannelOut < pConverter->channelsOut; ++iChannelOut) { - ma_channel channelPosOut = ma_channel_map_get_channel(pConverter->pChannelMapOut, pConverter->channelsOut, iChannelOut); - - if (ma_is_spatial_channel_position(channelPosOut)) { - if (!ma_channel_map_contains_channel_position(pConverter->channelsIn, pConverter->pChannelMapIn, channelPosOut)) { - for (iChannelIn = 0; iChannelIn < pConverter->channelsIn; ++iChannelIn) { - ma_channel channelPosIn = ma_channel_map_get_channel(pConverter->pChannelMapIn, pConverter->channelsIn, iChannelIn); - - if (ma_is_spatial_channel_position(channelPosIn)) { - float weight = 0; - if (pConverter->mixingMode == ma_channel_mix_mode_rectangular) { - weight = ma_calculate_channel_position_rectangular_weight(channelPosIn, channelPosOut); - } - - /* Only apply the weight if we haven't already got some contribution from the respective channels. */ - if (pConverter->format == ma_format_f32) { - if (pConverter->weights.f32[iChannelIn][iChannelOut] == 0) { - pConverter->weights.f32[iChannelIn][iChannelOut] = weight; - } - } else { - if (pConverter->weights.s16[iChannelIn][iChannelOut] == 0) { - pConverter->weights.s16[iChannelIn][iChannelOut] = ma_channel_converter_float_to_fixed(weight); - } - } - } - } - } - } - } - - /* If LFE is in the output channel map but was not present in the input channel map, configure its weight now */ - if (pConfig->calculateLFEFromSpatialChannels) { - if (!ma_channel_map_contains_channel_position(pConverter->channelsIn, pConverter->pChannelMapIn, MA_CHANNEL_LFE)) { - ma_uint32 spatialChannelCount = ma_channel_map_get_spatial_channel_count(pConverter->pChannelMapIn, pConverter->channelsIn); - ma_uint32 iChannelOutLFE; - - if (spatialChannelCount > 0 && ma_channel_map_find_channel_position(pConverter->channelsOut, pConverter->pChannelMapOut, MA_CHANNEL_LFE, &iChannelOutLFE)) { - const float weightForLFE = 1.0f / spatialChannelCount; - for (iChannelIn = 0; iChannelIn < pConverter->channelsIn; ++iChannelIn) { - const ma_channel channelPosIn = ma_channel_map_get_channel(pConverter->pChannelMapIn, pConverter->channelsIn, iChannelIn); - if (ma_is_spatial_channel_position(channelPosIn)) { - if (pConverter->format == ma_format_f32) { - if (pConverter->weights.f32[iChannelIn][iChannelOutLFE] == 0) { - pConverter->weights.f32[iChannelIn][iChannelOutLFE] = weightForLFE; - } - } else { - if (pConverter->weights.s16[iChannelIn][iChannelOutLFE] == 0) { - pConverter->weights.s16[iChannelIn][iChannelOutLFE] = ma_channel_converter_float_to_fixed(weightForLFE); - } - } - } - } - } - } - } - } break; - } - } - - return MA_SUCCESS; -} - -MA_API ma_result ma_channel_converter_init(const ma_channel_converter_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_channel_converter* pConverter) -{ - ma_result result; - size_t heapSizeInBytes; - void* pHeap; - - result = ma_channel_converter_get_heap_size(pConfig, &heapSizeInBytes); - if (result != MA_SUCCESS) { - return result; - } - - if (heapSizeInBytes > 0) { - pHeap = ma_malloc(heapSizeInBytes, pAllocationCallbacks); - if (pHeap == NULL) { - return MA_OUT_OF_MEMORY; - } - } else { - pHeap = NULL; - } - - result = ma_channel_converter_init_preallocated(pConfig, pHeap, pConverter); - if (result != MA_SUCCESS) { - ma_free(pHeap, pAllocationCallbacks); - return result; - } - - pConverter->_ownsHeap = MA_TRUE; - return MA_SUCCESS; -} - -MA_API void ma_channel_converter_uninit(ma_channel_converter* pConverter, const ma_allocation_callbacks* pAllocationCallbacks) -{ - if (pConverter == NULL) { - return; - } - - if (pConverter->_ownsHeap) { - ma_free(pConverter->_pHeap, pAllocationCallbacks); - } -} - -static ma_result ma_channel_converter_process_pcm_frames__passthrough(ma_channel_converter* pConverter, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount) -{ - MA_ASSERT(pConverter != NULL); - MA_ASSERT(pFramesOut != NULL); - MA_ASSERT(pFramesIn != NULL); - - ma_copy_memory_64(pFramesOut, pFramesIn, frameCount * ma_get_bytes_per_frame(pConverter->format, pConverter->channelsOut)); - return MA_SUCCESS; -} - -static ma_result ma_channel_converter_process_pcm_frames__shuffle(ma_channel_converter* pConverter, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount) -{ - MA_ASSERT(pConverter != NULL); - MA_ASSERT(pFramesOut != NULL); - MA_ASSERT(pFramesIn != NULL); - MA_ASSERT(pConverter->channelsIn == pConverter->channelsOut); - - return ma_channel_map_apply_shuffle_table(pFramesOut, pConverter->channelsOut, pFramesIn, pConverter->channelsIn, frameCount, pConverter->pShuffleTable, pConverter->format); -} - -static ma_result ma_channel_converter_process_pcm_frames__mono_in(ma_channel_converter* pConverter, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount) -{ - ma_uint64 iFrame; - - MA_ASSERT(pConverter != NULL); - MA_ASSERT(pFramesOut != NULL); - MA_ASSERT(pFramesIn != NULL); - MA_ASSERT(pConverter->channelsIn == 1); - - switch (pConverter->format) - { - case ma_format_u8: - { - /* */ ma_uint8* pFramesOutU8 = ( ma_uint8*)pFramesOut; - const ma_uint8* pFramesInU8 = (const ma_uint8*)pFramesIn; - - for (iFrame = 0; iFrame < frameCount; ++iFrame) { - ma_uint32 iChannel; - for (iChannel = 0; iChannel < pConverter->channelsOut; iChannel += 1) { - pFramesOutU8[iFrame*pConverter->channelsOut + iChannel] = pFramesInU8[iFrame]; - } - } - } break; - - case ma_format_s16: - { - /* */ ma_int16* pFramesOutS16 = ( ma_int16*)pFramesOut; - const ma_int16* pFramesInS16 = (const ma_int16*)pFramesIn; - - if (pConverter->channelsOut == 2) { - for (iFrame = 0; iFrame < frameCount; ++iFrame) { - pFramesOutS16[iFrame*2 + 0] = pFramesInS16[iFrame]; - pFramesOutS16[iFrame*2 + 1] = pFramesInS16[iFrame]; - } - } else { - for (iFrame = 0; iFrame < frameCount; ++iFrame) { - ma_uint32 iChannel; - for (iChannel = 0; iChannel < pConverter->channelsOut; iChannel += 1) { - pFramesOutS16[iFrame*pConverter->channelsOut + iChannel] = pFramesInS16[iFrame]; - } - } - } - } break; - - case ma_format_s24: - { - /* */ ma_uint8* pFramesOutS24 = ( ma_uint8*)pFramesOut; - const ma_uint8* pFramesInS24 = (const ma_uint8*)pFramesIn; - - for (iFrame = 0; iFrame < frameCount; ++iFrame) { - ma_uint32 iChannel; - for (iChannel = 0; iChannel < pConverter->channelsOut; iChannel += 1) { - ma_uint64 iSampleOut = iFrame*pConverter->channelsOut + iChannel; - ma_uint64 iSampleIn = iFrame; - pFramesOutS24[iSampleOut*3 + 0] = pFramesInS24[iSampleIn*3 + 0]; - pFramesOutS24[iSampleOut*3 + 1] = pFramesInS24[iSampleIn*3 + 1]; - pFramesOutS24[iSampleOut*3 + 2] = pFramesInS24[iSampleIn*3 + 2]; - } - } - } break; - - case ma_format_s32: - { - /* */ ma_int32* pFramesOutS32 = ( ma_int32*)pFramesOut; - const ma_int32* pFramesInS32 = (const ma_int32*)pFramesIn; - - for (iFrame = 0; iFrame < frameCount; ++iFrame) { - ma_uint32 iChannel; - for (iChannel = 0; iChannel < pConverter->channelsOut; iChannel += 1) { - pFramesOutS32[iFrame*pConverter->channelsOut + iChannel] = pFramesInS32[iFrame]; - } - } - } break; - - case ma_format_f32: - { - /* */ float* pFramesOutF32 = ( float*)pFramesOut; - const float* pFramesInF32 = (const float*)pFramesIn; - - if (pConverter->channelsOut == 2) { - for (iFrame = 0; iFrame < frameCount; ++iFrame) { - pFramesOutF32[iFrame*2 + 0] = pFramesInF32[iFrame]; - pFramesOutF32[iFrame*2 + 1] = pFramesInF32[iFrame]; - } - } else { - for (iFrame = 0; iFrame < frameCount; ++iFrame) { - ma_uint32 iChannel; - for (iChannel = 0; iChannel < pConverter->channelsOut; iChannel += 1) { - pFramesOutF32[iFrame*pConverter->channelsOut + iChannel] = pFramesInF32[iFrame]; - } - } - } - } break; - - default: return MA_INVALID_OPERATION; /* Unknown format. */ - } - - return MA_SUCCESS; -} - -static ma_result ma_channel_converter_process_pcm_frames__mono_out(ma_channel_converter* pConverter, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount) -{ - ma_uint64 iFrame; - ma_uint32 iChannel; - - MA_ASSERT(pConverter != NULL); - MA_ASSERT(pFramesOut != NULL); - MA_ASSERT(pFramesIn != NULL); - MA_ASSERT(pConverter->channelsOut == 1); - - switch (pConverter->format) - { - case ma_format_u8: - { - /* */ ma_uint8* pFramesOutU8 = ( ma_uint8*)pFramesOut; - const ma_uint8* pFramesInU8 = (const ma_uint8*)pFramesIn; - - for (iFrame = 0; iFrame < frameCount; ++iFrame) { - ma_int32 t = 0; - for (iChannel = 0; iChannel < pConverter->channelsIn; iChannel += 1) { - t += ma_pcm_sample_u8_to_s16_no_scale(pFramesInU8[iFrame*pConverter->channelsIn + iChannel]); - } - - pFramesOutU8[iFrame] = ma_clip_u8(t / pConverter->channelsOut); - } - } break; - - case ma_format_s16: - { - /* */ ma_int16* pFramesOutS16 = ( ma_int16*)pFramesOut; - const ma_int16* pFramesInS16 = (const ma_int16*)pFramesIn; - - for (iFrame = 0; iFrame < frameCount; ++iFrame) { - ma_int32 t = 0; - for (iChannel = 0; iChannel < pConverter->channelsIn; iChannel += 1) { - t += pFramesInS16[iFrame*pConverter->channelsIn + iChannel]; - } - - pFramesOutS16[iFrame] = (ma_int16)(t / pConverter->channelsIn); - } - } break; - - case ma_format_s24: - { - /* */ ma_uint8* pFramesOutS24 = ( ma_uint8*)pFramesOut; - const ma_uint8* pFramesInS24 = (const ma_uint8*)pFramesIn; - - for (iFrame = 0; iFrame < frameCount; ++iFrame) { - ma_int64 t = 0; - for (iChannel = 0; iChannel < pConverter->channelsIn; iChannel += 1) { - t += ma_pcm_sample_s24_to_s32_no_scale(&pFramesInS24[(iFrame*pConverter->channelsIn + iChannel)*3]); - } - - ma_pcm_sample_s32_to_s24_no_scale(t / pConverter->channelsIn, &pFramesOutS24[iFrame*3]); - } - } break; - - case ma_format_s32: - { - /* */ ma_int32* pFramesOutS32 = ( ma_int32*)pFramesOut; - const ma_int32* pFramesInS32 = (const ma_int32*)pFramesIn; - - for (iFrame = 0; iFrame < frameCount; ++iFrame) { - ma_int64 t = 0; - for (iChannel = 0; iChannel < pConverter->channelsIn; iChannel += 1) { - t += pFramesInS32[iFrame*pConverter->channelsIn + iChannel]; - } - - pFramesOutS32[iFrame] = (ma_int32)(t / pConverter->channelsIn); - } - } break; - - case ma_format_f32: - { - /* */ float* pFramesOutF32 = ( float*)pFramesOut; - const float* pFramesInF32 = (const float*)pFramesIn; - - for (iFrame = 0; iFrame < frameCount; ++iFrame) { - float t = 0; - for (iChannel = 0; iChannel < pConverter->channelsIn; iChannel += 1) { - t += pFramesInF32[iFrame*pConverter->channelsIn + iChannel]; - } - - pFramesOutF32[iFrame] = t / pConverter->channelsIn; - } - } break; - - default: return MA_INVALID_OPERATION; /* Unknown format. */ - } - - return MA_SUCCESS; -} - -static ma_result ma_channel_converter_process_pcm_frames__weights(ma_channel_converter* pConverter, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount) -{ - ma_uint32 iFrame; - ma_uint32 iChannelIn; - ma_uint32 iChannelOut; - - MA_ASSERT(pConverter != NULL); - MA_ASSERT(pFramesOut != NULL); - MA_ASSERT(pFramesIn != NULL); - - /* This is the more complicated case. Each of the output channels is accumulated with 0 or more input channels. */ - - /* Clear. */ - ma_zero_memory_64(pFramesOut, frameCount * ma_get_bytes_per_frame(pConverter->format, pConverter->channelsOut)); - - /* Accumulate. */ - switch (pConverter->format) - { - case ma_format_u8: - { - /* */ ma_uint8* pFramesOutU8 = ( ma_uint8*)pFramesOut; - const ma_uint8* pFramesInU8 = (const ma_uint8*)pFramesIn; - - for (iFrame = 0; iFrame < frameCount; iFrame += 1) { - for (iChannelIn = 0; iChannelIn < pConverter->channelsIn; ++iChannelIn) { - for (iChannelOut = 0; iChannelOut < pConverter->channelsOut; ++iChannelOut) { - ma_int16 u8_O = ma_pcm_sample_u8_to_s16_no_scale(pFramesOutU8[iFrame*pConverter->channelsOut + iChannelOut]); - ma_int16 u8_I = ma_pcm_sample_u8_to_s16_no_scale(pFramesInU8 [iFrame*pConverter->channelsIn + iChannelIn ]); - ma_int32 s = (ma_int32)ma_clamp(u8_O + ((u8_I * pConverter->weights.s16[iChannelIn][iChannelOut]) >> MA_CHANNEL_CONVERTER_FIXED_POINT_SHIFT), -128, 127); - pFramesOutU8[iFrame*pConverter->channelsOut + iChannelOut] = ma_clip_u8((ma_int16)s); - } - } - } - } break; - - case ma_format_s16: - { - /* */ ma_int16* pFramesOutS16 = ( ma_int16*)pFramesOut; - const ma_int16* pFramesInS16 = (const ma_int16*)pFramesIn; - - for (iFrame = 0; iFrame < frameCount; iFrame += 1) { - for (iChannelIn = 0; iChannelIn < pConverter->channelsIn; ++iChannelIn) { - for (iChannelOut = 0; iChannelOut < pConverter->channelsOut; ++iChannelOut) { - ma_int32 s = pFramesOutS16[iFrame*pConverter->channelsOut + iChannelOut]; - s += (pFramesInS16[iFrame*pConverter->channelsIn + iChannelIn] * pConverter->weights.s16[iChannelIn][iChannelOut]) >> MA_CHANNEL_CONVERTER_FIXED_POINT_SHIFT; - - pFramesOutS16[iFrame*pConverter->channelsOut + iChannelOut] = (ma_int16)ma_clamp(s, -32768, 32767); - } - } - } - } break; - - case ma_format_s24: - { - /* */ ma_uint8* pFramesOutS24 = ( ma_uint8*)pFramesOut; - const ma_uint8* pFramesInS24 = (const ma_uint8*)pFramesIn; - - for (iFrame = 0; iFrame < frameCount; iFrame += 1) { - for (iChannelIn = 0; iChannelIn < pConverter->channelsIn; ++iChannelIn) { - for (iChannelOut = 0; iChannelOut < pConverter->channelsOut; ++iChannelOut) { - ma_int64 s24_O = ma_pcm_sample_s24_to_s32_no_scale(&pFramesOutS24[(iFrame*pConverter->channelsOut + iChannelOut)*3]); - ma_int64 s24_I = ma_pcm_sample_s24_to_s32_no_scale(&pFramesInS24 [(iFrame*pConverter->channelsIn + iChannelIn )*3]); - ma_int64 s24 = (ma_int32)ma_clamp(s24_O + ((s24_I * pConverter->weights.s16[iChannelIn][iChannelOut]) >> MA_CHANNEL_CONVERTER_FIXED_POINT_SHIFT), -8388608, 8388607); - ma_pcm_sample_s32_to_s24_no_scale(s24, &pFramesOutS24[(iFrame*pConverter->channelsOut + iChannelOut)*3]); - } - } - } - } break; - - case ma_format_s32: - { - /* */ ma_int32* pFramesOutS32 = ( ma_int32*)pFramesOut; - const ma_int32* pFramesInS32 = (const ma_int32*)pFramesIn; - - for (iFrame = 0; iFrame < frameCount; iFrame += 1) { - for (iChannelIn = 0; iChannelIn < pConverter->channelsIn; ++iChannelIn) { - for (iChannelOut = 0; iChannelOut < pConverter->channelsOut; ++iChannelOut) { - ma_int64 s = pFramesOutS32[iFrame*pConverter->channelsOut + iChannelOut]; - s += ((ma_int64)pFramesInS32[iFrame*pConverter->channelsIn + iChannelIn] * pConverter->weights.s16[iChannelIn][iChannelOut]) >> MA_CHANNEL_CONVERTER_FIXED_POINT_SHIFT; - - pFramesOutS32[iFrame*pConverter->channelsOut + iChannelOut] = ma_clip_s32(s); - } - } - } - } break; - - case ma_format_f32: - { - /* */ float* pFramesOutF32 = ( float*)pFramesOut; - const float* pFramesInF32 = (const float*)pFramesIn; - - for (iFrame = 0; iFrame < frameCount; iFrame += 1) { - for (iChannelIn = 0; iChannelIn < pConverter->channelsIn; ++iChannelIn) { - for (iChannelOut = 0; iChannelOut < pConverter->channelsOut; ++iChannelOut) { - pFramesOutF32[iFrame*pConverter->channelsOut + iChannelOut] += pFramesInF32[iFrame*pConverter->channelsIn + iChannelIn] * pConverter->weights.f32[iChannelIn][iChannelOut]; - } - } - } - } break; - - default: return MA_INVALID_OPERATION; /* Unknown format. */ - } - - return MA_SUCCESS; -} - -MA_API ma_result ma_channel_converter_process_pcm_frames(ma_channel_converter* pConverter, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount) -{ - if (pConverter == NULL) { - return MA_INVALID_ARGS; - } - - if (pFramesOut == NULL) { - return MA_INVALID_ARGS; - } - - if (pFramesIn == NULL) { - ma_zero_memory_64(pFramesOut, frameCount * ma_get_bytes_per_frame(pConverter->format, pConverter->channelsOut)); - return MA_SUCCESS; - } - - switch (pConverter->conversionPath) - { - case ma_channel_conversion_path_passthrough: return ma_channel_converter_process_pcm_frames__passthrough(pConverter, pFramesOut, pFramesIn, frameCount); - case ma_channel_conversion_path_mono_out: return ma_channel_converter_process_pcm_frames__mono_out(pConverter, pFramesOut, pFramesIn, frameCount); - case ma_channel_conversion_path_mono_in: return ma_channel_converter_process_pcm_frames__mono_in(pConverter, pFramesOut, pFramesIn, frameCount); - case ma_channel_conversion_path_shuffle: return ma_channel_converter_process_pcm_frames__shuffle(pConverter, pFramesOut, pFramesIn, frameCount); - case ma_channel_conversion_path_weights: - default: - { - return ma_channel_converter_process_pcm_frames__weights(pConverter, pFramesOut, pFramesIn, frameCount); - } - } -} - -MA_API ma_result ma_channel_converter_get_input_channel_map(const ma_channel_converter* pConverter, ma_channel* pChannelMap, size_t channelMapCap) -{ - if (pConverter == NULL || pChannelMap == NULL) { - return MA_INVALID_ARGS; - } - - ma_channel_map_copy_or_default(pChannelMap, channelMapCap, pConverter->pChannelMapIn, pConverter->channelsIn); - - return MA_SUCCESS; -} - -MA_API ma_result ma_channel_converter_get_output_channel_map(const ma_channel_converter* pConverter, ma_channel* pChannelMap, size_t channelMapCap) -{ - if (pConverter == NULL || pChannelMap == NULL) { - return MA_INVALID_ARGS; - } - - ma_channel_map_copy_or_default(pChannelMap, channelMapCap, pConverter->pChannelMapOut, pConverter->channelsOut); - - return MA_SUCCESS; -} - - -/************************************************************************************************************************************************************** - -Data Conversion - -**************************************************************************************************************************************************************/ -MA_API ma_data_converter_config ma_data_converter_config_init_default(void) -{ - ma_data_converter_config config; - MA_ZERO_OBJECT(&config); - - config.ditherMode = ma_dither_mode_none; - config.resampling.algorithm = ma_resample_algorithm_linear; - config.allowDynamicSampleRate = MA_FALSE; /* Disable dynamic sample rates by default because dynamic rate adjustments should be quite rare and it allows an optimization for cases when the in and out sample rates are the same. */ - - /* Linear resampling defaults. */ - config.resampling.linear.lpfOrder = 1; - - return config; -} - -MA_API ma_data_converter_config ma_data_converter_config_init(ma_format formatIn, ma_format formatOut, ma_uint32 channelsIn, ma_uint32 channelsOut, ma_uint32 sampleRateIn, ma_uint32 sampleRateOut) -{ - ma_data_converter_config config = ma_data_converter_config_init_default(); - config.formatIn = formatIn; - config.formatOut = formatOut; - config.channelsIn = channelsIn; - config.channelsOut = channelsOut; - config.sampleRateIn = sampleRateIn; - config.sampleRateOut = sampleRateOut; - - return config; -} - - -typedef struct -{ - size_t sizeInBytes; - size_t channelConverterOffset; - size_t resamplerOffset; -} ma_data_converter_heap_layout; - -static ma_bool32 ma_data_converter_config_is_resampler_required(const ma_data_converter_config* pConfig) -{ - MA_ASSERT(pConfig != NULL); - - return pConfig->allowDynamicSampleRate || pConfig->sampleRateIn != pConfig->sampleRateOut; -} - -static ma_format ma_data_converter_config_get_mid_format(const ma_data_converter_config* pConfig) -{ - MA_ASSERT(pConfig != NULL); - - /* - We want to avoid as much data conversion as possible. The channel converter and linear - resampler both support s16 and f32 natively. We need to decide on the format to use for this - stage. We call this the mid format because it's used in the middle stage of the conversion - pipeline. If the output format is either s16 or f32 we use that one. If that is not the case it - will do the same thing for the input format. If it's neither we just use f32. If we are using a - custom resampling backend, we can only guarantee that f32 will be supported so we'll be forced - to use that if resampling is required. - */ - if (ma_data_converter_config_is_resampler_required(pConfig) && pConfig->resampling.algorithm != ma_resample_algorithm_linear) { - return ma_format_f32; /* <-- Force f32 since that is the only one we can guarantee will be supported by the resampler. */ - } else { - /* */ if (pConfig->formatOut == ma_format_s16 || pConfig->formatOut == ma_format_f32) { - return pConfig->formatOut; - } else if (pConfig->formatIn == ma_format_s16 || pConfig->formatIn == ma_format_f32) { - return pConfig->formatIn; - } else { - return ma_format_f32; - } - } -} - -static ma_channel_converter_config ma_channel_converter_config_init_from_data_converter_config(const ma_data_converter_config* pConfig) -{ - ma_channel_converter_config channelConverterConfig; - - MA_ASSERT(pConfig != NULL); - - channelConverterConfig = ma_channel_converter_config_init(ma_data_converter_config_get_mid_format(pConfig), pConfig->channelsIn, pConfig->pChannelMapIn, pConfig->channelsOut, pConfig->pChannelMapOut, pConfig->channelMixMode); - channelConverterConfig.ppWeights = pConfig->ppChannelWeights; - channelConverterConfig.calculateLFEFromSpatialChannels = pConfig->calculateLFEFromSpatialChannels; - - return channelConverterConfig; -} - -static ma_resampler_config ma_resampler_config_init_from_data_converter_config(const ma_data_converter_config* pConfig) -{ - ma_resampler_config resamplerConfig; - ma_uint32 resamplerChannels; - - MA_ASSERT(pConfig != NULL); - - /* The resampler is the most expensive part of the conversion process, so we need to do it at the stage where the channel count is at it's lowest. */ - if (pConfig->channelsIn < pConfig->channelsOut) { - resamplerChannels = pConfig->channelsIn; - } else { - resamplerChannels = pConfig->channelsOut; - } - - resamplerConfig = ma_resampler_config_init(ma_data_converter_config_get_mid_format(pConfig), resamplerChannels, pConfig->sampleRateIn, pConfig->sampleRateOut, pConfig->resampling.algorithm); - resamplerConfig.linear = pConfig->resampling.linear; - resamplerConfig.pBackendVTable = pConfig->resampling.pBackendVTable; - resamplerConfig.pBackendUserData = pConfig->resampling.pBackendUserData; - - return resamplerConfig; -} - -static ma_result ma_data_converter_get_heap_layout(const ma_data_converter_config* pConfig, ma_data_converter_heap_layout* pHeapLayout) -{ - ma_result result; - - MA_ASSERT(pHeapLayout != NULL); - - MA_ZERO_OBJECT(pHeapLayout); - - if (pConfig == NULL) { - return MA_INVALID_ARGS; - } - - if (pConfig->channelsIn == 0 || pConfig->channelsOut == 0) { - return MA_INVALID_ARGS; - } - - pHeapLayout->sizeInBytes = 0; - - /* Channel converter. */ - pHeapLayout->channelConverterOffset = pHeapLayout->sizeInBytes; - { - size_t heapSizeInBytes; - ma_channel_converter_config channelConverterConfig = ma_channel_converter_config_init_from_data_converter_config(pConfig); - - result = ma_channel_converter_get_heap_size(&channelConverterConfig, &heapSizeInBytes); - if (result != MA_SUCCESS) { - return result; - } - - pHeapLayout->sizeInBytes += heapSizeInBytes; - } - - /* Resampler. */ - pHeapLayout->resamplerOffset = pHeapLayout->sizeInBytes; - if (ma_data_converter_config_is_resampler_required(pConfig)) { - size_t heapSizeInBytes; - ma_resampler_config resamplerConfig = ma_resampler_config_init_from_data_converter_config(pConfig); - - result = ma_resampler_get_heap_size(&resamplerConfig, &heapSizeInBytes); - if (result != MA_SUCCESS) { - return result; - } - - pHeapLayout->sizeInBytes += heapSizeInBytes; - } - - /* Make sure allocation size is aligned. */ - pHeapLayout->sizeInBytes = ma_align_64(pHeapLayout->sizeInBytes); - - return MA_SUCCESS; -} - -MA_API ma_result ma_data_converter_get_heap_size(const ma_data_converter_config* pConfig, size_t* pHeapSizeInBytes) -{ - ma_result result; - ma_data_converter_heap_layout heapLayout; - - if (pHeapSizeInBytes == NULL) { - return MA_INVALID_ARGS; - } - - *pHeapSizeInBytes = 0; - - result = ma_data_converter_get_heap_layout(pConfig, &heapLayout); - if (result != MA_SUCCESS) { - return result; - } - - *pHeapSizeInBytes = heapLayout.sizeInBytes; - - return MA_SUCCESS; -} - -MA_API ma_result ma_data_converter_init_preallocated(const ma_data_converter_config* pConfig, void* pHeap, ma_data_converter* pConverter) -{ - ma_result result; - ma_data_converter_heap_layout heapLayout; - ma_format midFormat; - ma_bool32 isResamplingRequired; - - if (pConverter == NULL) { - return MA_INVALID_ARGS; - } - - MA_ZERO_OBJECT(pConverter); - - result = ma_data_converter_get_heap_layout(pConfig, &heapLayout); - if (result != MA_SUCCESS) { - return result; - } - - pConverter->_pHeap = pHeap; - MA_ZERO_MEMORY(pHeap, heapLayout.sizeInBytes); - - pConverter->formatIn = pConfig->formatIn; - pConverter->formatOut = pConfig->formatOut; - pConverter->channelsIn = pConfig->channelsIn; - pConverter->channelsOut = pConfig->channelsOut; - pConverter->sampleRateIn = pConfig->sampleRateIn; - pConverter->sampleRateOut = pConfig->sampleRateOut; - pConverter->ditherMode = pConfig->ditherMode; - - /* - Determine if resampling is required. We need to do this so we can determine an appropriate - mid format to use. If resampling is required, the mid format must be ma_format_f32 since - that is the only one that is guaranteed to supported by custom resampling backends. - */ - isResamplingRequired = ma_data_converter_config_is_resampler_required(pConfig); - midFormat = ma_data_converter_config_get_mid_format(pConfig); - - - /* Channel converter. We always initialize this, but we check if it configures itself as a passthrough to determine whether or not it's needed. */ - { - ma_channel_converter_config channelConverterConfig = ma_channel_converter_config_init_from_data_converter_config(pConfig); - - result = ma_channel_converter_init_preallocated(&channelConverterConfig, ma_offset_ptr(pHeap, heapLayout.channelConverterOffset), &pConverter->channelConverter); - if (result != MA_SUCCESS) { - return result; - } - - /* If the channel converter is not a passthrough we need to enable it. Otherwise we can skip it. */ - if (pConverter->channelConverter.conversionPath != ma_channel_conversion_path_passthrough) { - pConverter->hasChannelConverter = MA_TRUE; - } - } - - - /* Resampler. */ - if (isResamplingRequired) { - ma_resampler_config resamplerConfig = ma_resampler_config_init_from_data_converter_config(pConfig); - - result = ma_resampler_init_preallocated(&resamplerConfig, ma_offset_ptr(pHeap, heapLayout.resamplerOffset), &pConverter->resampler); - if (result != MA_SUCCESS) { - return result; - } - - pConverter->hasResampler = MA_TRUE; - } - - - /* We can simplify pre- and post-format conversion if we have neither channel conversion nor resampling. */ - if (pConverter->hasChannelConverter == MA_FALSE && pConverter->hasResampler == MA_FALSE) { - /* We have neither channel conversion nor resampling so we'll only need one of pre- or post-format conversion, or none if the input and output formats are the same. */ - if (pConverter->formatIn == pConverter->formatOut) { - /* The formats are the same so we can just pass through. */ - pConverter->hasPreFormatConversion = MA_FALSE; - pConverter->hasPostFormatConversion = MA_FALSE; - } else { - /* The formats are different so we need to do either pre- or post-format conversion. It doesn't matter which. */ - pConverter->hasPreFormatConversion = MA_FALSE; - pConverter->hasPostFormatConversion = MA_TRUE; - } - } else { - /* We have a channel converter and/or resampler so we'll need channel conversion based on the mid format. */ - if (pConverter->formatIn != midFormat) { - pConverter->hasPreFormatConversion = MA_TRUE; - } - if (pConverter->formatOut != midFormat) { - pConverter->hasPostFormatConversion = MA_TRUE; - } - } - - /* We can enable passthrough optimizations if applicable. Note that we'll only be able to do this if the sample rate is static. */ - if (pConverter->hasPreFormatConversion == MA_FALSE && - pConverter->hasPostFormatConversion == MA_FALSE && - pConverter->hasChannelConverter == MA_FALSE && - pConverter->hasResampler == MA_FALSE) { - pConverter->isPassthrough = MA_TRUE; - } - - - /* We now need to determine our execution path. */ - if (pConverter->isPassthrough) { - pConverter->executionPath = ma_data_converter_execution_path_passthrough; - } else { - if (pConverter->channelsIn < pConverter->channelsOut) { - /* Do resampling first, if necessary. */ - MA_ASSERT(pConverter->hasChannelConverter == MA_TRUE); - - if (pConverter->hasResampler) { - pConverter->executionPath = ma_data_converter_execution_path_resample_first; - } else { - pConverter->executionPath = ma_data_converter_execution_path_channels_only; - } - } else { - /* Do channel conversion first, if necessary. */ - if (pConverter->hasChannelConverter) { - if (pConverter->hasResampler) { - pConverter->executionPath = ma_data_converter_execution_path_channels_first; - } else { - pConverter->executionPath = ma_data_converter_execution_path_channels_only; - } - } else { - /* Channel routing not required. */ - if (pConverter->hasResampler) { - pConverter->executionPath = ma_data_converter_execution_path_resample_only; - } else { - pConverter->executionPath = ma_data_converter_execution_path_format_only; - } - } - } - } - - return MA_SUCCESS; -} - -MA_API ma_result ma_data_converter_init(const ma_data_converter_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_data_converter* pConverter) -{ - ma_result result; - size_t heapSizeInBytes; - void* pHeap; - - result = ma_data_converter_get_heap_size(pConfig, &heapSizeInBytes); - if (result != MA_SUCCESS) { - return result; - } - - if (heapSizeInBytes > 0) { - pHeap = ma_malloc(heapSizeInBytes, pAllocationCallbacks); - if (pHeap == NULL) { - return MA_OUT_OF_MEMORY; - } - } else { - pHeap = NULL; - } - - result = ma_data_converter_init_preallocated(pConfig, pHeap, pConverter); - if (result != MA_SUCCESS) { - ma_free(pHeap, pAllocationCallbacks); - return result; - } - - pConverter->_ownsHeap = MA_TRUE; - return MA_SUCCESS; -} - -MA_API void ma_data_converter_uninit(ma_data_converter* pConverter, const ma_allocation_callbacks* pAllocationCallbacks) -{ - if (pConverter == NULL) { - return; - } - - if (pConverter->hasResampler) { - ma_resampler_uninit(&pConverter->resampler, pAllocationCallbacks); - } - - ma_channel_converter_uninit(&pConverter->channelConverter, pAllocationCallbacks); - - if (pConverter->_ownsHeap) { - ma_free(pConverter->_pHeap, pAllocationCallbacks); - } -} - -static ma_result ma_data_converter_process_pcm_frames__passthrough(ma_data_converter* pConverter, const void* pFramesIn, ma_uint64* pFrameCountIn, void* pFramesOut, ma_uint64* pFrameCountOut) -{ - ma_uint64 frameCountIn; - ma_uint64 frameCountOut; - ma_uint64 frameCount; - - MA_ASSERT(pConverter != NULL); - - frameCountIn = 0; - if (pFrameCountIn != NULL) { - frameCountIn = *pFrameCountIn; - } - - frameCountOut = 0; - if (pFrameCountOut != NULL) { - frameCountOut = *pFrameCountOut; - } - - frameCount = ma_min(frameCountIn, frameCountOut); - - if (pFramesOut != NULL) { - if (pFramesIn != NULL) { - ma_copy_memory_64(pFramesOut, pFramesIn, frameCount * ma_get_bytes_per_frame(pConverter->formatOut, pConverter->channelsOut)); - } else { - ma_zero_memory_64(pFramesOut, frameCount * ma_get_bytes_per_frame(pConverter->formatOut, pConverter->channelsOut)); - } - } - - if (pFrameCountIn != NULL) { - *pFrameCountIn = frameCount; - } - if (pFrameCountOut != NULL) { - *pFrameCountOut = frameCount; - } - - return MA_SUCCESS; -} - -static ma_result ma_data_converter_process_pcm_frames__format_only(ma_data_converter* pConverter, const void* pFramesIn, ma_uint64* pFrameCountIn, void* pFramesOut, ma_uint64* pFrameCountOut) -{ - ma_uint64 frameCountIn; - ma_uint64 frameCountOut; - ma_uint64 frameCount; - - MA_ASSERT(pConverter != NULL); - - frameCountIn = 0; - if (pFrameCountIn != NULL) { - frameCountIn = *pFrameCountIn; - } - - frameCountOut = 0; - if (pFrameCountOut != NULL) { - frameCountOut = *pFrameCountOut; - } - - frameCount = ma_min(frameCountIn, frameCountOut); - - if (pFramesOut != NULL) { - if (pFramesIn != NULL) { - ma_convert_pcm_frames_format(pFramesOut, pConverter->formatOut, pFramesIn, pConverter->formatIn, frameCount, pConverter->channelsIn, pConverter->ditherMode); - } else { - ma_zero_memory_64(pFramesOut, frameCount * ma_get_bytes_per_frame(pConverter->formatOut, pConverter->channelsOut)); - } - } - - if (pFrameCountIn != NULL) { - *pFrameCountIn = frameCount; - } - if (pFrameCountOut != NULL) { - *pFrameCountOut = frameCount; - } - - return MA_SUCCESS; -} - - -static ma_result ma_data_converter_process_pcm_frames__resample_with_format_conversion(ma_data_converter* pConverter, const void* pFramesIn, ma_uint64* pFrameCountIn, void* pFramesOut, ma_uint64* pFrameCountOut) -{ - ma_result result = MA_SUCCESS; - ma_uint64 frameCountIn; - ma_uint64 frameCountOut; - ma_uint64 framesProcessedIn; - ma_uint64 framesProcessedOut; - - MA_ASSERT(pConverter != NULL); - - frameCountIn = 0; - if (pFrameCountIn != NULL) { - frameCountIn = *pFrameCountIn; - } - - frameCountOut = 0; - if (pFrameCountOut != NULL) { - frameCountOut = *pFrameCountOut; - } - - framesProcessedIn = 0; - framesProcessedOut = 0; - - while (framesProcessedOut < frameCountOut) { - ma_uint8 pTempBufferOut[MA_DATA_CONVERTER_STACK_BUFFER_SIZE]; - const ma_uint32 tempBufferOutCap = sizeof(pTempBufferOut) / ma_get_bytes_per_frame(pConverter->resampler.format, pConverter->resampler.channels); - const void* pFramesInThisIteration; - /* */ void* pFramesOutThisIteration; - ma_uint64 frameCountInThisIteration; - ma_uint64 frameCountOutThisIteration; - - if (pFramesIn != NULL) { - pFramesInThisIteration = ma_offset_ptr(pFramesIn, framesProcessedIn * ma_get_bytes_per_frame(pConverter->formatIn, pConverter->channelsIn)); - } else { - pFramesInThisIteration = NULL; - } - - if (pFramesOut != NULL) { - pFramesOutThisIteration = ma_offset_ptr(pFramesOut, framesProcessedOut * ma_get_bytes_per_frame(pConverter->formatOut, pConverter->channelsOut)); - } else { - pFramesOutThisIteration = NULL; - } - - /* Do a pre format conversion if necessary. */ - if (pConverter->hasPreFormatConversion) { - ma_uint8 pTempBufferIn[MA_DATA_CONVERTER_STACK_BUFFER_SIZE]; - const ma_uint32 tempBufferInCap = sizeof(pTempBufferIn) / ma_get_bytes_per_frame(pConverter->resampler.format, pConverter->resampler.channels); - - frameCountInThisIteration = (frameCountIn - framesProcessedIn); - if (frameCountInThisIteration > tempBufferInCap) { - frameCountInThisIteration = tempBufferInCap; - } - - if (pConverter->hasPostFormatConversion) { - if (frameCountInThisIteration > tempBufferOutCap) { - frameCountInThisIteration = tempBufferOutCap; - } - } - - if (pFramesInThisIteration != NULL) { - ma_convert_pcm_frames_format(pTempBufferIn, pConverter->resampler.format, pFramesInThisIteration, pConverter->formatIn, frameCountInThisIteration, pConverter->channelsIn, pConverter->ditherMode); - } else { - MA_ZERO_MEMORY(pTempBufferIn, sizeof(pTempBufferIn)); - } - - frameCountOutThisIteration = (frameCountOut - framesProcessedOut); - - if (pConverter->hasPostFormatConversion) { - /* Both input and output conversion required. Output to the temp buffer. */ - if (frameCountOutThisIteration > tempBufferOutCap) { - frameCountOutThisIteration = tempBufferOutCap; - } - - result = ma_resampler_process_pcm_frames(&pConverter->resampler, pTempBufferIn, &frameCountInThisIteration, pTempBufferOut, &frameCountOutThisIteration); - } else { - /* Only pre-format required. Output straight to the output buffer. */ - result = ma_resampler_process_pcm_frames(&pConverter->resampler, pTempBufferIn, &frameCountInThisIteration, pFramesOutThisIteration, &frameCountOutThisIteration); - } - - if (result != MA_SUCCESS) { - break; - } - } else { - /* No pre-format required. Just read straight from the input buffer. */ - MA_ASSERT(pConverter->hasPostFormatConversion == MA_TRUE); - - frameCountInThisIteration = (frameCountIn - framesProcessedIn); - frameCountOutThisIteration = (frameCountOut - framesProcessedOut); - if (frameCountOutThisIteration > tempBufferOutCap) { - frameCountOutThisIteration = tempBufferOutCap; - } - - result = ma_resampler_process_pcm_frames(&pConverter->resampler, pFramesInThisIteration, &frameCountInThisIteration, pTempBufferOut, &frameCountOutThisIteration); - if (result != MA_SUCCESS) { - break; - } - } - - /* If we are doing a post format conversion we need to do that now. */ - if (pConverter->hasPostFormatConversion) { - if (pFramesOutThisIteration != NULL) { - ma_convert_pcm_frames_format(pFramesOutThisIteration, pConverter->formatOut, pTempBufferOut, pConverter->resampler.format, frameCountOutThisIteration, pConverter->resampler.channels, pConverter->ditherMode); - } - } - - framesProcessedIn += frameCountInThisIteration; - framesProcessedOut += frameCountOutThisIteration; - - MA_ASSERT(framesProcessedIn <= frameCountIn); - MA_ASSERT(framesProcessedOut <= frameCountOut); - - if (frameCountOutThisIteration == 0) { - break; /* Consumed all of our input data. */ - } - } - - if (pFrameCountIn != NULL) { - *pFrameCountIn = framesProcessedIn; - } - if (pFrameCountOut != NULL) { - *pFrameCountOut = framesProcessedOut; - } - - return result; -} - -static ma_result ma_data_converter_process_pcm_frames__resample_only(ma_data_converter* pConverter, const void* pFramesIn, ma_uint64* pFrameCountIn, void* pFramesOut, ma_uint64* pFrameCountOut) -{ - MA_ASSERT(pConverter != NULL); - - if (pConverter->hasPreFormatConversion == MA_FALSE && pConverter->hasPostFormatConversion == MA_FALSE) { - /* Neither pre- nor post-format required. This is simple case where only resampling is required. */ - return ma_resampler_process_pcm_frames(&pConverter->resampler, pFramesIn, pFrameCountIn, pFramesOut, pFrameCountOut); - } else { - /* Format conversion required. */ - return ma_data_converter_process_pcm_frames__resample_with_format_conversion(pConverter, pFramesIn, pFrameCountIn, pFramesOut, pFrameCountOut); - } -} - -static ma_result ma_data_converter_process_pcm_frames__channels_only(ma_data_converter* pConverter, const void* pFramesIn, ma_uint64* pFrameCountIn, void* pFramesOut, ma_uint64* pFrameCountOut) -{ - ma_result result; - ma_uint64 frameCountIn; - ma_uint64 frameCountOut; - ma_uint64 frameCount; - - MA_ASSERT(pConverter != NULL); - - frameCountIn = 0; - if (pFrameCountIn != NULL) { - frameCountIn = *pFrameCountIn; - } - - frameCountOut = 0; - if (pFrameCountOut != NULL) { - frameCountOut = *pFrameCountOut; - } - - frameCount = ma_min(frameCountIn, frameCountOut); - - if (pConverter->hasPreFormatConversion == MA_FALSE && pConverter->hasPostFormatConversion == MA_FALSE) { - /* No format conversion required. */ - result = ma_channel_converter_process_pcm_frames(&pConverter->channelConverter, pFramesOut, pFramesIn, frameCount); - if (result != MA_SUCCESS) { - return result; - } - } else { - /* Format conversion required. */ - ma_uint64 framesProcessed = 0; - - while (framesProcessed < frameCount) { - ma_uint8 pTempBufferOut[MA_DATA_CONVERTER_STACK_BUFFER_SIZE]; - const ma_uint32 tempBufferOutCap = sizeof(pTempBufferOut) / ma_get_bytes_per_frame(pConverter->channelConverter.format, pConverter->channelConverter.channelsOut); - const void* pFramesInThisIteration; - /* */ void* pFramesOutThisIteration; - ma_uint64 frameCountThisIteration; - - if (pFramesIn != NULL) { - pFramesInThisIteration = ma_offset_ptr(pFramesIn, framesProcessed * ma_get_bytes_per_frame(pConverter->formatIn, pConverter->channelsIn)); - } else { - pFramesInThisIteration = NULL; - } - - if (pFramesOut != NULL) { - pFramesOutThisIteration = ma_offset_ptr(pFramesOut, framesProcessed * ma_get_bytes_per_frame(pConverter->formatOut, pConverter->channelsOut)); - } else { - pFramesOutThisIteration = NULL; - } - - /* Do a pre format conversion if necessary. */ - if (pConverter->hasPreFormatConversion) { - ma_uint8 pTempBufferIn[MA_DATA_CONVERTER_STACK_BUFFER_SIZE]; - const ma_uint32 tempBufferInCap = sizeof(pTempBufferIn) / ma_get_bytes_per_frame(pConverter->channelConverter.format, pConverter->channelConverter.channelsIn); - - frameCountThisIteration = (frameCount - framesProcessed); - if (frameCountThisIteration > tempBufferInCap) { - frameCountThisIteration = tempBufferInCap; - } - - if (pConverter->hasPostFormatConversion) { - if (frameCountThisIteration > tempBufferOutCap) { - frameCountThisIteration = tempBufferOutCap; - } - } - - if (pFramesInThisIteration != NULL) { - ma_convert_pcm_frames_format(pTempBufferIn, pConverter->channelConverter.format, pFramesInThisIteration, pConverter->formatIn, frameCountThisIteration, pConverter->channelsIn, pConverter->ditherMode); - } else { - MA_ZERO_MEMORY(pTempBufferIn, sizeof(pTempBufferIn)); - } - - if (pConverter->hasPostFormatConversion) { - /* Both input and output conversion required. Output to the temp buffer. */ - result = ma_channel_converter_process_pcm_frames(&pConverter->channelConverter, pTempBufferOut, pTempBufferIn, frameCountThisIteration); - } else { - /* Only pre-format required. Output straight to the output buffer. */ - result = ma_channel_converter_process_pcm_frames(&pConverter->channelConverter, pFramesOutThisIteration, pTempBufferIn, frameCountThisIteration); - } - - if (result != MA_SUCCESS) { - break; - } - } else { - /* No pre-format required. Just read straight from the input buffer. */ - MA_ASSERT(pConverter->hasPostFormatConversion == MA_TRUE); - - frameCountThisIteration = (frameCount - framesProcessed); - if (frameCountThisIteration > tempBufferOutCap) { - frameCountThisIteration = tempBufferOutCap; - } - - result = ma_channel_converter_process_pcm_frames(&pConverter->channelConverter, pTempBufferOut, pFramesInThisIteration, frameCountThisIteration); - if (result != MA_SUCCESS) { - break; - } - } - - /* If we are doing a post format conversion we need to do that now. */ - if (pConverter->hasPostFormatConversion) { - if (pFramesOutThisIteration != NULL) { - ma_convert_pcm_frames_format(pFramesOutThisIteration, pConverter->formatOut, pTempBufferOut, pConverter->channelConverter.format, frameCountThisIteration, pConverter->channelConverter.channelsOut, pConverter->ditherMode); - } - } - - framesProcessed += frameCountThisIteration; - } - } - - if (pFrameCountIn != NULL) { - *pFrameCountIn = frameCount; - } - if (pFrameCountOut != NULL) { - *pFrameCountOut = frameCount; - } - - return MA_SUCCESS; -} - -static ma_result ma_data_converter_process_pcm_frames__resample_first(ma_data_converter* pConverter, const void* pFramesIn, ma_uint64* pFrameCountIn, void* pFramesOut, ma_uint64* pFrameCountOut) -{ - ma_result result; - ma_uint64 frameCountIn; - ma_uint64 frameCountOut; - ma_uint64 framesProcessedIn; - ma_uint64 framesProcessedOut; - ma_uint8 pTempBufferIn[MA_DATA_CONVERTER_STACK_BUFFER_SIZE]; /* In resampler format. */ - ma_uint64 tempBufferInCap; - ma_uint8 pTempBufferMid[MA_DATA_CONVERTER_STACK_BUFFER_SIZE]; /* In resampler format, channel converter input format. */ - ma_uint64 tempBufferMidCap; - ma_uint8 pTempBufferOut[MA_DATA_CONVERTER_STACK_BUFFER_SIZE]; /* In channel converter output format. */ - ma_uint64 tempBufferOutCap; - - MA_ASSERT(pConverter != NULL); - MA_ASSERT(pConverter->resampler.format == pConverter->channelConverter.format); - MA_ASSERT(pConverter->resampler.channels == pConverter->channelConverter.channelsIn); - MA_ASSERT(pConverter->resampler.channels < pConverter->channelConverter.channelsOut); - - frameCountIn = 0; - if (pFrameCountIn != NULL) { - frameCountIn = *pFrameCountIn; - } - - frameCountOut = 0; - if (pFrameCountOut != NULL) { - frameCountOut = *pFrameCountOut; - } - - framesProcessedIn = 0; - framesProcessedOut = 0; - - tempBufferInCap = sizeof(pTempBufferIn) / ma_get_bytes_per_frame(pConverter->resampler.format, pConverter->resampler.channels); - tempBufferMidCap = sizeof(pTempBufferIn) / ma_get_bytes_per_frame(pConverter->resampler.format, pConverter->resampler.channels); - tempBufferOutCap = sizeof(pTempBufferOut) / ma_get_bytes_per_frame(pConverter->channelConverter.format, pConverter->channelConverter.channelsOut); - - while (framesProcessedOut < frameCountOut) { - ma_uint64 frameCountInThisIteration; - ma_uint64 frameCountOutThisIteration; - const void* pRunningFramesIn = NULL; - void* pRunningFramesOut = NULL; - const void* pResampleBufferIn; - void* pChannelsBufferOut; - - if (pFramesIn != NULL) { - pRunningFramesIn = ma_offset_ptr(pFramesIn, framesProcessedIn * ma_get_bytes_per_frame(pConverter->formatIn, pConverter->channelsIn)); - } - if (pFramesOut != NULL) { - pRunningFramesOut = ma_offset_ptr(pFramesOut, framesProcessedOut * ma_get_bytes_per_frame(pConverter->formatOut, pConverter->channelsOut)); - } - - /* Run input data through the resampler and output it to the temporary buffer. */ - frameCountInThisIteration = (frameCountIn - framesProcessedIn); - - if (pConverter->hasPreFormatConversion) { - if (frameCountInThisIteration > tempBufferInCap) { - frameCountInThisIteration = tempBufferInCap; - } - } - - frameCountOutThisIteration = (frameCountOut - framesProcessedOut); - if (frameCountOutThisIteration > tempBufferMidCap) { - frameCountOutThisIteration = tempBufferMidCap; - } - - /* We can't read more frames than can fit in the output buffer. */ - if (pConverter->hasPostFormatConversion) { - if (frameCountOutThisIteration > tempBufferOutCap) { - frameCountOutThisIteration = tempBufferOutCap; - } - } - - /* We need to ensure we don't try to process too many input frames that we run out of room in the output buffer. If this happens we'll end up glitching. */ - - /* - We need to try to predict how many input frames will be required for the resampler. If the - resampler can tell us, we'll use that. Otherwise we'll need to make a best guess. The further - off we are from this, the more wasted format conversions we'll end up doing. - */ - #if 1 - { - ma_uint64 requiredInputFrameCount; - - result = ma_resampler_get_required_input_frame_count(&pConverter->resampler, frameCountOutThisIteration, &requiredInputFrameCount); - if (result != MA_SUCCESS) { - /* Fall back to a best guess. */ - requiredInputFrameCount = (frameCountOutThisIteration * pConverter->resampler.sampleRateIn) / pConverter->resampler.sampleRateOut; - } - - if (frameCountInThisIteration > requiredInputFrameCount) { - frameCountInThisIteration = requiredInputFrameCount; - } - } - #endif - - if (pConverter->hasPreFormatConversion) { - if (pFramesIn != NULL) { - ma_convert_pcm_frames_format(pTempBufferIn, pConverter->resampler.format, pRunningFramesIn, pConverter->formatIn, frameCountInThisIteration, pConverter->channelsIn, pConverter->ditherMode); - pResampleBufferIn = pTempBufferIn; - } else { - pResampleBufferIn = NULL; - } - } else { - pResampleBufferIn = pRunningFramesIn; - } - - result = ma_resampler_process_pcm_frames(&pConverter->resampler, pResampleBufferIn, &frameCountInThisIteration, pTempBufferMid, &frameCountOutThisIteration); - if (result != MA_SUCCESS) { - return result; - } - - - /* - The input data has been resampled so now we need to run it through the channel converter. The input data is always contained in pTempBufferMid. We only need to do - this part if we have an output buffer. - */ - if (pFramesOut != NULL) { - if (pConverter->hasPostFormatConversion) { - pChannelsBufferOut = pTempBufferOut; - } else { - pChannelsBufferOut = pRunningFramesOut; - } - - result = ma_channel_converter_process_pcm_frames(&pConverter->channelConverter, pChannelsBufferOut, pTempBufferMid, frameCountOutThisIteration); - if (result != MA_SUCCESS) { - return result; - } - - /* Finally we do post format conversion. */ - if (pConverter->hasPostFormatConversion) { - ma_convert_pcm_frames_format(pRunningFramesOut, pConverter->formatOut, pChannelsBufferOut, pConverter->channelConverter.format, frameCountOutThisIteration, pConverter->channelConverter.channelsOut, pConverter->ditherMode); - } - } - - - framesProcessedIn += frameCountInThisIteration; - framesProcessedOut += frameCountOutThisIteration; - - MA_ASSERT(framesProcessedIn <= frameCountIn); - MA_ASSERT(framesProcessedOut <= frameCountOut); - - if (frameCountOutThisIteration == 0) { - break; /* Consumed all of our input data. */ - } - } - - if (pFrameCountIn != NULL) { - *pFrameCountIn = framesProcessedIn; - } - if (pFrameCountOut != NULL) { - *pFrameCountOut = framesProcessedOut; - } - - return MA_SUCCESS; -} - -static ma_result ma_data_converter_process_pcm_frames__channels_first(ma_data_converter* pConverter, const void* pFramesIn, ma_uint64* pFrameCountIn, void* pFramesOut, ma_uint64* pFrameCountOut) -{ - ma_result result; - ma_uint64 frameCountIn; - ma_uint64 frameCountOut; - ma_uint64 framesProcessedIn; - ma_uint64 framesProcessedOut; - ma_uint8 pTempBufferIn[MA_DATA_CONVERTER_STACK_BUFFER_SIZE]; /* In resampler format. */ - ma_uint64 tempBufferInCap; - ma_uint8 pTempBufferMid[MA_DATA_CONVERTER_STACK_BUFFER_SIZE]; /* In resampler format, channel converter input format. */ - ma_uint64 tempBufferMidCap; - ma_uint8 pTempBufferOut[MA_DATA_CONVERTER_STACK_BUFFER_SIZE]; /* In channel converter output format. */ - ma_uint64 tempBufferOutCap; - - MA_ASSERT(pConverter != NULL); - MA_ASSERT(pConverter->resampler.format == pConverter->channelConverter.format); - MA_ASSERT(pConverter->resampler.channels == pConverter->channelConverter.channelsOut); - MA_ASSERT(pConverter->resampler.channels <= pConverter->channelConverter.channelsIn); - - frameCountIn = 0; - if (pFrameCountIn != NULL) { - frameCountIn = *pFrameCountIn; - } - - frameCountOut = 0; - if (pFrameCountOut != NULL) { - frameCountOut = *pFrameCountOut; - } - - framesProcessedIn = 0; - framesProcessedOut = 0; - - tempBufferInCap = sizeof(pTempBufferIn) / ma_get_bytes_per_frame(pConverter->channelConverter.format, pConverter->channelConverter.channelsIn); - tempBufferMidCap = sizeof(pTempBufferIn) / ma_get_bytes_per_frame(pConverter->channelConverter.format, pConverter->channelConverter.channelsOut); - tempBufferOutCap = sizeof(pTempBufferOut) / ma_get_bytes_per_frame(pConverter->resampler.format, pConverter->resampler.channels); - - while (framesProcessedOut < frameCountOut) { - ma_uint64 frameCountInThisIteration; - ma_uint64 frameCountOutThisIteration; - const void* pRunningFramesIn = NULL; - void* pRunningFramesOut = NULL; - const void* pChannelsBufferIn; - void* pResampleBufferOut; - - if (pFramesIn != NULL) { - pRunningFramesIn = ma_offset_ptr(pFramesIn, framesProcessedIn * ma_get_bytes_per_frame(pConverter->formatIn, pConverter->channelsIn)); - } - if (pFramesOut != NULL) { - pRunningFramesOut = ma_offset_ptr(pFramesOut, framesProcessedOut * ma_get_bytes_per_frame(pConverter->formatOut, pConverter->channelsOut)); - } - - /* - Before doing any processing we need to determine how many frames we should try processing - this iteration, for both input and output. The resampler requires us to perform format and - channel conversion before passing any data into it. If we get our input count wrong, we'll - end up performing redundant pre-processing. This isn't the end of the world, but it does - result in some inefficiencies proportionate to how far our estimates are off. - - If the resampler has a means to calculate exactly how much we'll need, we'll use that. - Otherwise we'll make a best guess. In order to do this, we'll need to calculate the output - frame count first. - */ - frameCountOutThisIteration = (frameCountOut - framesProcessedOut); - if (frameCountOutThisIteration > tempBufferMidCap) { - frameCountOutThisIteration = tempBufferMidCap; - } - - if (pConverter->hasPostFormatConversion) { - if (frameCountOutThisIteration > tempBufferOutCap) { - frameCountOutThisIteration = tempBufferOutCap; - } - } - - /* Now that we have the output frame count we can determine the input frame count. */ - frameCountInThisIteration = (frameCountIn - framesProcessedIn); - if (pConverter->hasPreFormatConversion) { - if (frameCountInThisIteration > tempBufferInCap) { - frameCountInThisIteration = tempBufferInCap; - } - } - - if (frameCountInThisIteration > tempBufferMidCap) { - frameCountInThisIteration = tempBufferMidCap; - } - - #if 1 - { - ma_uint64 requiredInputFrameCount; - - result = ma_resampler_get_required_input_frame_count(&pConverter->resampler, frameCountOutThisIteration, &requiredInputFrameCount); - if (result != MA_SUCCESS) { - /* Fall back to a best guess. */ - requiredInputFrameCount = (frameCountOutThisIteration * pConverter->resampler.sampleRateIn) / pConverter->resampler.sampleRateOut; - } - - if (frameCountInThisIteration > requiredInputFrameCount) { - frameCountInThisIteration = requiredInputFrameCount; - } - } - #endif - - - /* Pre format conversion. */ - if (pConverter->hasPreFormatConversion) { - if (pRunningFramesIn != NULL) { - ma_convert_pcm_frames_format(pTempBufferIn, pConverter->channelConverter.format, pRunningFramesIn, pConverter->formatIn, frameCountInThisIteration, pConverter->channelsIn, pConverter->ditherMode); - pChannelsBufferIn = pTempBufferIn; - } else { - pChannelsBufferIn = NULL; - } - } else { - pChannelsBufferIn = pRunningFramesIn; - } - - - /* Channel conversion. */ - result = ma_channel_converter_process_pcm_frames(&pConverter->channelConverter, pTempBufferMid, pChannelsBufferIn, frameCountInThisIteration); - if (result != MA_SUCCESS) { - return result; - } - - - /* Resampling. */ - if (pConverter->hasPostFormatConversion) { - pResampleBufferOut = pTempBufferOut; - } else { - pResampleBufferOut = pRunningFramesOut; - } - - result = ma_resampler_process_pcm_frames(&pConverter->resampler, pTempBufferMid, &frameCountInThisIteration, pResampleBufferOut, &frameCountOutThisIteration); - if (result != MA_SUCCESS) { - return result; - } - - - /* Post format conversion. */ - if (pConverter->hasPostFormatConversion) { - if (pRunningFramesOut != NULL) { - ma_convert_pcm_frames_format(pRunningFramesOut, pConverter->formatOut, pResampleBufferOut, pConverter->resampler.format, frameCountOutThisIteration, pConverter->channelsOut, pConverter->ditherMode); - } - } - - - framesProcessedIn += frameCountInThisIteration; - framesProcessedOut += frameCountOutThisIteration; - - MA_ASSERT(framesProcessedIn <= frameCountIn); - MA_ASSERT(framesProcessedOut <= frameCountOut); - - if (frameCountOutThisIteration == 0) { - break; /* Consumed all of our input data. */ - } - } - - if (pFrameCountIn != NULL) { - *pFrameCountIn = framesProcessedIn; - } - if (pFrameCountOut != NULL) { - *pFrameCountOut = framesProcessedOut; - } - - return MA_SUCCESS; -} - -MA_API ma_result ma_data_converter_process_pcm_frames(ma_data_converter* pConverter, const void* pFramesIn, ma_uint64* pFrameCountIn, void* pFramesOut, ma_uint64* pFrameCountOut) -{ - if (pConverter == NULL) { - return MA_INVALID_ARGS; - } - - switch (pConverter->executionPath) - { - case ma_data_converter_execution_path_passthrough: return ma_data_converter_process_pcm_frames__passthrough(pConverter, pFramesIn, pFrameCountIn, pFramesOut, pFrameCountOut); - case ma_data_converter_execution_path_format_only: return ma_data_converter_process_pcm_frames__format_only(pConverter, pFramesIn, pFrameCountIn, pFramesOut, pFrameCountOut); - case ma_data_converter_execution_path_channels_only: return ma_data_converter_process_pcm_frames__channels_only(pConverter, pFramesIn, pFrameCountIn, pFramesOut, pFrameCountOut); - case ma_data_converter_execution_path_resample_only: return ma_data_converter_process_pcm_frames__resample_only(pConverter, pFramesIn, pFrameCountIn, pFramesOut, pFrameCountOut); - case ma_data_converter_execution_path_resample_first: return ma_data_converter_process_pcm_frames__resample_first(pConverter, pFramesIn, pFrameCountIn, pFramesOut, pFrameCountOut); - case ma_data_converter_execution_path_channels_first: return ma_data_converter_process_pcm_frames__channels_first(pConverter, pFramesIn, pFrameCountIn, pFramesOut, pFrameCountOut); - default: return MA_INVALID_OPERATION; /* Should never hit this. */ - } -} - -MA_API ma_result ma_data_converter_set_rate(ma_data_converter* pConverter, ma_uint32 sampleRateIn, ma_uint32 sampleRateOut) -{ - if (pConverter == NULL) { - return MA_INVALID_ARGS; - } - - if (pConverter->hasResampler == MA_FALSE) { - return MA_INVALID_OPERATION; /* Dynamic resampling not enabled. */ - } - - return ma_resampler_set_rate(&pConverter->resampler, sampleRateIn, sampleRateOut); -} - -MA_API ma_result ma_data_converter_set_rate_ratio(ma_data_converter* pConverter, float ratioInOut) -{ - if (pConverter == NULL) { - return MA_INVALID_ARGS; - } - - if (pConverter->hasResampler == MA_FALSE) { - return MA_INVALID_OPERATION; /* Dynamic resampling not enabled. */ - } - - return ma_resampler_set_rate_ratio(&pConverter->resampler, ratioInOut); -} - -MA_API ma_uint64 ma_data_converter_get_input_latency(const ma_data_converter* pConverter) -{ - if (pConverter == NULL) { - return 0; - } - - if (pConverter->hasResampler) { - return ma_resampler_get_input_latency(&pConverter->resampler); - } - - return 0; /* No latency without a resampler. */ -} - -MA_API ma_uint64 ma_data_converter_get_output_latency(const ma_data_converter* pConverter) -{ - if (pConverter == NULL) { - return 0; - } - - if (pConverter->hasResampler) { - return ma_resampler_get_output_latency(&pConverter->resampler); - } - - return 0; /* No latency without a resampler. */ -} - -MA_API ma_result ma_data_converter_get_required_input_frame_count(const ma_data_converter* pConverter, ma_uint64 outputFrameCount, ma_uint64* pInputFrameCount) -{ - if (pInputFrameCount == NULL) { - return MA_INVALID_ARGS; - } - - *pInputFrameCount = 0; - - if (pConverter == NULL) { - return MA_INVALID_ARGS; - } - - if (pConverter->hasResampler) { - return ma_resampler_get_required_input_frame_count(&pConverter->resampler, outputFrameCount, pInputFrameCount); - } else { - *pInputFrameCount = outputFrameCount; /* 1:1 */ - return MA_SUCCESS; - } -} - -MA_API ma_result ma_data_converter_get_expected_output_frame_count(const ma_data_converter* pConverter, ma_uint64 inputFrameCount, ma_uint64* pOutputFrameCount) -{ - if (pOutputFrameCount == NULL) { - return MA_INVALID_ARGS; - } - - *pOutputFrameCount = 0; - - if (pConverter == NULL) { - return MA_INVALID_ARGS; - } - - if (pConverter->hasResampler) { - return ma_resampler_get_expected_output_frame_count(&pConverter->resampler, inputFrameCount, pOutputFrameCount); - } else { - *pOutputFrameCount = inputFrameCount; /* 1:1 */ - return MA_SUCCESS; - } -} - -MA_API ma_result ma_data_converter_get_input_channel_map(const ma_data_converter* pConverter, ma_channel* pChannelMap, size_t channelMapCap) -{ - if (pConverter == NULL || pChannelMap == NULL) { - return MA_INVALID_ARGS; - } - - if (pConverter->hasChannelConverter) { - ma_channel_converter_get_output_channel_map(&pConverter->channelConverter, pChannelMap, channelMapCap); - } else { - ma_channel_map_init_standard(ma_standard_channel_map_default, pChannelMap, channelMapCap, pConverter->channelsOut); - } - - return MA_SUCCESS; -} - -MA_API ma_result ma_data_converter_get_output_channel_map(const ma_data_converter* pConverter, ma_channel* pChannelMap, size_t channelMapCap) -{ - if (pConverter == NULL || pChannelMap == NULL) { - return MA_INVALID_ARGS; - } - - if (pConverter->hasChannelConverter) { - ma_channel_converter_get_input_channel_map(&pConverter->channelConverter, pChannelMap, channelMapCap); - } else { - ma_channel_map_init_standard(ma_standard_channel_map_default, pChannelMap, channelMapCap, pConverter->channelsIn); - } - - return MA_SUCCESS; -} - -MA_API ma_result ma_data_converter_reset(ma_data_converter* pConverter) -{ - if (pConverter == NULL) { - return MA_INVALID_ARGS; - } - - /* There's nothing to do if we're not resampling. */ - if (pConverter->hasResampler == MA_FALSE) { - return MA_SUCCESS; - } - - return ma_resampler_reset(&pConverter->resampler); -} - - - -/************************************************************************************************************************************************************** - -Channel Maps - -**************************************************************************************************************************************************************/ -static ma_channel ma_channel_map_init_standard_channel(ma_standard_channel_map standardChannelMap, ma_uint32 channelCount, ma_uint32 channelIndex); - -MA_API ma_channel ma_channel_map_get_channel(const ma_channel* pChannelMap, ma_uint32 channelCount, ma_uint32 channelIndex) -{ - if (pChannelMap == NULL) { - return ma_channel_map_init_standard_channel(ma_standard_channel_map_default, channelCount, channelIndex); - } else { - if (channelIndex >= channelCount) { - return MA_CHANNEL_NONE; - } - - return pChannelMap[channelIndex]; - } -} - -MA_API void ma_channel_map_init_blank(ma_channel* pChannelMap, ma_uint32 channels) -{ - if (pChannelMap == NULL) { - return; - } - - MA_ZERO_MEMORY(pChannelMap, sizeof(*pChannelMap) * channels); -} - - -static ma_channel ma_channel_map_init_standard_channel_microsoft(ma_uint32 channelCount, ma_uint32 channelIndex) -{ - if (channelCount == 0 || channelIndex >= channelCount) { - return MA_CHANNEL_NONE; - } - - /* This is the Microsoft channel map. Based off the speaker configurations mentioned here: https://docs.microsoft.com/en-us/windows-hardware/drivers/ddi/content/ksmedia/ns-ksmedia-ksaudio_channel_config */ - switch (channelCount) - { - case 0: return MA_CHANNEL_NONE; - - case 1: - { - return MA_CHANNEL_MONO; - } break; - - case 2: - { - switch (channelIndex) { - case 0: return MA_CHANNEL_FRONT_LEFT; - case 1: return MA_CHANNEL_FRONT_RIGHT; - } - } break; - - case 3: /* No defined, but best guess. */ - { - switch (channelIndex) { - case 0: return MA_CHANNEL_FRONT_LEFT; - case 1: return MA_CHANNEL_FRONT_RIGHT; - case 2: return MA_CHANNEL_FRONT_CENTER; - } - } break; - - case 4: - { - switch (channelIndex) { - #ifndef MA_USE_QUAD_MICROSOFT_CHANNEL_MAP - /* Surround. Using the Surround profile has the advantage of the 3rd channel (MA_CHANNEL_FRONT_CENTER) mapping nicely with higher channel counts. */ - case 0: return MA_CHANNEL_FRONT_LEFT; - case 1: return MA_CHANNEL_FRONT_RIGHT; - case 2: return MA_CHANNEL_FRONT_CENTER; - case 3: return MA_CHANNEL_BACK_CENTER; - #else - /* Quad. */ - case 0: return MA_CHANNEL_FRONT_LEFT; - case 1: return MA_CHANNEL_FRONT_RIGHT; - case 2: return MA_CHANNEL_BACK_LEFT; - case 3: return MA_CHANNEL_BACK_RIGHT; - #endif - } - } break; - - case 5: /* Not defined, but best guess. */ - { - switch (channelIndex) { - case 0: return MA_CHANNEL_FRONT_LEFT; - case 1: return MA_CHANNEL_FRONT_RIGHT; - case 2: return MA_CHANNEL_FRONT_CENTER; - case 3: return MA_CHANNEL_BACK_LEFT; - case 4: return MA_CHANNEL_BACK_RIGHT; - } - } break; - - case 6: - { - switch (channelIndex) { - case 0: return MA_CHANNEL_FRONT_LEFT; - case 1: return MA_CHANNEL_FRONT_RIGHT; - case 2: return MA_CHANNEL_FRONT_CENTER; - case 3: return MA_CHANNEL_LFE; - case 4: return MA_CHANNEL_SIDE_LEFT; - case 5: return MA_CHANNEL_SIDE_RIGHT; - } - } break; - - case 7: /* Not defined, but best guess. */ - { - switch (channelIndex) { - case 0: return MA_CHANNEL_FRONT_LEFT; - case 1: return MA_CHANNEL_FRONT_RIGHT; - case 2: return MA_CHANNEL_FRONT_CENTER; - case 3: return MA_CHANNEL_LFE; - case 4: return MA_CHANNEL_BACK_CENTER; - case 5: return MA_CHANNEL_SIDE_LEFT; - case 6: return MA_CHANNEL_SIDE_RIGHT; - } - } break; - - case 8: - default: - { - switch (channelIndex) { - case 0: return MA_CHANNEL_FRONT_LEFT; - case 1: return MA_CHANNEL_FRONT_RIGHT; - case 2: return MA_CHANNEL_FRONT_CENTER; - case 3: return MA_CHANNEL_LFE; - case 4: return MA_CHANNEL_BACK_LEFT; - case 5: return MA_CHANNEL_BACK_RIGHT; - case 6: return MA_CHANNEL_SIDE_LEFT; - case 7: return MA_CHANNEL_SIDE_RIGHT; - } - } break; - } - - if (channelCount > 8) { - if (channelIndex < 32) { /* We have 32 AUX channels. */ - return (ma_channel)(MA_CHANNEL_AUX_0 + (channelIndex - 8)); - } - } - - /* Getting here means we don't know how to map the channel position so just return MA_CHANNEL_NONE. */ - return MA_CHANNEL_NONE; -} - -static ma_channel ma_channel_map_init_standard_channel_alsa(ma_uint32 channelCount, ma_uint32 channelIndex) -{ - switch (channelCount) - { - case 0: return MA_CHANNEL_NONE; - - case 1: - { - return MA_CHANNEL_MONO; - } break; - - case 2: - { - switch (channelIndex) { - case 0: return MA_CHANNEL_FRONT_LEFT; - case 1: return MA_CHANNEL_FRONT_RIGHT; - } - } break; - - case 3: - { - switch (channelIndex) { - case 0: return MA_CHANNEL_FRONT_LEFT; - case 1: return MA_CHANNEL_FRONT_RIGHT; - case 2: return MA_CHANNEL_FRONT_CENTER; - } - } break; - - case 4: - { - switch (channelIndex) { - case 0: return MA_CHANNEL_FRONT_LEFT; - case 1: return MA_CHANNEL_FRONT_RIGHT; - case 2: return MA_CHANNEL_BACK_LEFT; - case 3: return MA_CHANNEL_BACK_RIGHT; - } - } break; - - case 5: - { - switch (channelIndex) { - case 0: return MA_CHANNEL_FRONT_LEFT; - case 1: return MA_CHANNEL_FRONT_RIGHT; - case 2: return MA_CHANNEL_BACK_LEFT; - case 3: return MA_CHANNEL_BACK_RIGHT; - case 4: return MA_CHANNEL_FRONT_CENTER; - } - } break; - - case 6: - { - switch (channelIndex) { - case 0: return MA_CHANNEL_FRONT_LEFT; - case 1: return MA_CHANNEL_FRONT_RIGHT; - case 2: return MA_CHANNEL_BACK_LEFT; - case 3: return MA_CHANNEL_BACK_RIGHT; - case 4: return MA_CHANNEL_FRONT_CENTER; - case 5: return MA_CHANNEL_LFE; - } - } break; - - case 7: - { - switch (channelIndex) { - case 0: return MA_CHANNEL_FRONT_LEFT; - case 1: return MA_CHANNEL_FRONT_RIGHT; - case 2: return MA_CHANNEL_BACK_LEFT; - case 3: return MA_CHANNEL_BACK_RIGHT; - case 4: return MA_CHANNEL_FRONT_CENTER; - case 5: return MA_CHANNEL_LFE; - case 6: return MA_CHANNEL_BACK_CENTER; - } - } break; - - case 8: - default: - { - switch (channelIndex) { - case 0: return MA_CHANNEL_FRONT_LEFT; - case 1: return MA_CHANNEL_FRONT_RIGHT; - case 2: return MA_CHANNEL_BACK_LEFT; - case 3: return MA_CHANNEL_BACK_RIGHT; - case 4: return MA_CHANNEL_FRONT_CENTER; - case 5: return MA_CHANNEL_LFE; - case 6: return MA_CHANNEL_SIDE_LEFT; - case 7: return MA_CHANNEL_SIDE_RIGHT; - } - } break; - } - - if (channelCount > 8) { - if (channelIndex < 32) { /* We have 32 AUX channels. */ - return (ma_channel)(MA_CHANNEL_AUX_0 + (channelIndex - 8)); - } - } - - /* Getting here means we don't know how to map the channel position so just return MA_CHANNEL_NONE. */ - return MA_CHANNEL_NONE; -} - -static ma_channel ma_channel_map_init_standard_channel_rfc3551(ma_uint32 channelCount, ma_uint32 channelIndex) -{ - switch (channelCount) - { - case 0: return MA_CHANNEL_NONE; - - case 1: - { - return MA_CHANNEL_MONO; - } break; - - case 2: - { - switch (channelIndex) { - case 0: return MA_CHANNEL_FRONT_LEFT; - case 1: return MA_CHANNEL_FRONT_RIGHT; - } - } break; - - case 3: - { - switch (channelIndex) { - case 0: return MA_CHANNEL_FRONT_LEFT; - case 1: return MA_CHANNEL_FRONT_RIGHT; - case 2: return MA_CHANNEL_FRONT_CENTER; - } - } break; - - case 4: - { - switch (channelIndex) { - case 0: return MA_CHANNEL_FRONT_LEFT; - case 2: return MA_CHANNEL_FRONT_CENTER; - case 1: return MA_CHANNEL_FRONT_RIGHT; - case 3: return MA_CHANNEL_BACK_CENTER; - } - } break; - - case 5: - { - switch (channelIndex) { - case 0: return MA_CHANNEL_FRONT_LEFT; - case 1: return MA_CHANNEL_FRONT_RIGHT; - case 2: return MA_CHANNEL_FRONT_CENTER; - case 3: return MA_CHANNEL_BACK_LEFT; - case 4: return MA_CHANNEL_BACK_RIGHT; - } - } break; - - case 6: - default: - { - switch (channelIndex) { - case 0: return MA_CHANNEL_FRONT_LEFT; - case 1: return MA_CHANNEL_SIDE_LEFT; - case 2: return MA_CHANNEL_FRONT_CENTER; - case 3: return MA_CHANNEL_FRONT_RIGHT; - case 4: return MA_CHANNEL_SIDE_RIGHT; - case 5: return MA_CHANNEL_BACK_CENTER; - } - } break; - } - - if (channelCount > 6) { - if (channelIndex < 32) { /* We have 32 AUX channels. */ - return (ma_channel)(MA_CHANNEL_AUX_0 + (channelIndex - 6)); - } - } - - /* Getting here means we don't know how to map the channel position so just return MA_CHANNEL_NONE. */ - return MA_CHANNEL_NONE; -} - -static ma_channel ma_channel_map_init_standard_channel_flac(ma_uint32 channelCount, ma_uint32 channelIndex) -{ - switch (channelCount) - { - case 0: return MA_CHANNEL_NONE; - - case 1: - { - return MA_CHANNEL_MONO; - } break; - - case 2: - { - switch (channelIndex) { - case 0: return MA_CHANNEL_FRONT_LEFT; - case 1: return MA_CHANNEL_FRONT_RIGHT; - } - } break; - - case 3: - { - switch (channelIndex) { - case 0: return MA_CHANNEL_FRONT_LEFT; - case 1: return MA_CHANNEL_FRONT_RIGHT; - case 2: return MA_CHANNEL_FRONT_CENTER; - } - } break; - - case 4: - { - switch (channelIndex) { - case 0: return MA_CHANNEL_FRONT_LEFT; - case 1: return MA_CHANNEL_FRONT_RIGHT; - case 2: return MA_CHANNEL_BACK_LEFT; - case 3: return MA_CHANNEL_BACK_RIGHT; - } - } break; - - case 5: - { - switch (channelIndex) { - case 0: return MA_CHANNEL_FRONT_LEFT; - case 1: return MA_CHANNEL_FRONT_RIGHT; - case 2: return MA_CHANNEL_FRONT_CENTER; - case 3: return MA_CHANNEL_BACK_LEFT; - case 4: return MA_CHANNEL_BACK_RIGHT; - } - } break; - - case 6: - { - switch (channelIndex) { - case 0: return MA_CHANNEL_FRONT_LEFT; - case 1: return MA_CHANNEL_FRONT_RIGHT; - case 2: return MA_CHANNEL_FRONT_CENTER; - case 3: return MA_CHANNEL_LFE; - case 4: return MA_CHANNEL_BACK_LEFT; - case 5: return MA_CHANNEL_BACK_RIGHT; - } - } break; - - case 7: - { - switch (channelIndex) { - case 0: return MA_CHANNEL_FRONT_LEFT; - case 1: return MA_CHANNEL_FRONT_RIGHT; - case 2: return MA_CHANNEL_FRONT_CENTER; - case 3: return MA_CHANNEL_LFE; - case 4: return MA_CHANNEL_BACK_CENTER; - case 5: return MA_CHANNEL_SIDE_LEFT; - case 6: return MA_CHANNEL_SIDE_RIGHT; - } - } break; - - case 8: - default: - { - switch (channelIndex) { - case 0: return MA_CHANNEL_FRONT_LEFT; - case 1: return MA_CHANNEL_FRONT_RIGHT; - case 2: return MA_CHANNEL_FRONT_CENTER; - case 3: return MA_CHANNEL_LFE; - case 4: return MA_CHANNEL_BACK_LEFT; - case 5: return MA_CHANNEL_BACK_RIGHT; - case 6: return MA_CHANNEL_SIDE_LEFT; - case 7: return MA_CHANNEL_SIDE_RIGHT; - } - } break; - } - - if (channelCount > 8) { - if (channelIndex < 32) { /* We have 32 AUX channels. */ - return (ma_channel)(MA_CHANNEL_AUX_0 + (channelIndex - 8)); - } - } - - /* Getting here means we don't know how to map the channel position so just return MA_CHANNEL_NONE. */ - return MA_CHANNEL_NONE; -} - -static ma_channel ma_channel_map_init_standard_channel_vorbis(ma_uint32 channelCount, ma_uint32 channelIndex) -{ - switch (channelCount) - { - case 0: return MA_CHANNEL_NONE; - - case 1: - { - return MA_CHANNEL_MONO; - } break; - - case 2: - { - switch (channelIndex) { - case 0: return MA_CHANNEL_FRONT_LEFT; - case 1: return MA_CHANNEL_FRONT_RIGHT; - } - } break; - - case 3: - { - switch (channelIndex) { - case 0: return MA_CHANNEL_FRONT_LEFT; - case 1: return MA_CHANNEL_FRONT_CENTER; - case 2: return MA_CHANNEL_FRONT_RIGHT; - } - } break; - - case 4: - { - switch (channelIndex) { - case 0: return MA_CHANNEL_FRONT_LEFT; - case 1: return MA_CHANNEL_FRONT_RIGHT; - case 2: return MA_CHANNEL_BACK_LEFT; - case 3: return MA_CHANNEL_BACK_RIGHT; - } - } break; - - case 5: - { - switch (channelIndex) { - case 0: return MA_CHANNEL_FRONT_LEFT; - case 1: return MA_CHANNEL_FRONT_CENTER; - case 2: return MA_CHANNEL_FRONT_RIGHT; - case 3: return MA_CHANNEL_BACK_LEFT; - case 4: return MA_CHANNEL_BACK_RIGHT; - } - } break; - - case 6: - { - switch (channelIndex) { - case 0: return MA_CHANNEL_FRONT_LEFT; - case 1: return MA_CHANNEL_FRONT_CENTER; - case 2: return MA_CHANNEL_FRONT_RIGHT; - case 3: return MA_CHANNEL_BACK_LEFT; - case 4: return MA_CHANNEL_BACK_RIGHT; - case 5: return MA_CHANNEL_LFE; - } - } break; - - case 7: - { - switch (channelIndex) { - case 0: return MA_CHANNEL_FRONT_LEFT; - case 1: return MA_CHANNEL_FRONT_CENTER; - case 2: return MA_CHANNEL_FRONT_RIGHT; - case 3: return MA_CHANNEL_SIDE_LEFT; - case 4: return MA_CHANNEL_SIDE_RIGHT; - case 5: return MA_CHANNEL_BACK_CENTER; - case 6: return MA_CHANNEL_LFE; - } - } break; - - case 8: - default: - { - switch (channelIndex) { - case 0: return MA_CHANNEL_FRONT_LEFT; - case 1: return MA_CHANNEL_FRONT_CENTER; - case 2: return MA_CHANNEL_FRONT_RIGHT; - case 3: return MA_CHANNEL_SIDE_LEFT; - case 4: return MA_CHANNEL_SIDE_RIGHT; - case 5: return MA_CHANNEL_BACK_LEFT; - case 6: return MA_CHANNEL_BACK_RIGHT; - case 7: return MA_CHANNEL_LFE; - } - } break; - } - - if (channelCount > 8) { - if (channelIndex < 32) { /* We have 32 AUX channels. */ - return (ma_channel)(MA_CHANNEL_AUX_0 + (channelIndex - 8)); - } - } - - /* Getting here means we don't know how to map the channel position so just return MA_CHANNEL_NONE. */ - return MA_CHANNEL_NONE; -} - -static ma_channel ma_channel_map_init_standard_channel_sound4(ma_uint32 channelCount, ma_uint32 channelIndex) -{ - switch (channelCount) - { - case 0: return MA_CHANNEL_NONE; - - case 1: - { - return MA_CHANNEL_MONO; - } break; - - case 2: - { - switch (channelIndex) { - case 0: return MA_CHANNEL_FRONT_LEFT; - case 1: return MA_CHANNEL_FRONT_RIGHT; - } - } break; - - case 3: - { - switch (channelIndex) { - case 0: return MA_CHANNEL_FRONT_LEFT; - case 1: return MA_CHANNEL_FRONT_RIGHT; - case 2: return MA_CHANNEL_FRONT_CENTER; - } - } break; - - case 4: - { - switch (channelIndex) { - case 0: return MA_CHANNEL_FRONT_LEFT; - case 1: return MA_CHANNEL_FRONT_RIGHT; - case 2: return MA_CHANNEL_BACK_LEFT; - case 3: return MA_CHANNEL_BACK_RIGHT; - } - } break; - - case 5: - { - switch (channelIndex) { - case 0: return MA_CHANNEL_FRONT_LEFT; - case 1: return MA_CHANNEL_FRONT_RIGHT; - case 2: return MA_CHANNEL_FRONT_CENTER; - case 3: return MA_CHANNEL_BACK_LEFT; - case 4: return MA_CHANNEL_BACK_RIGHT; - } - } break; - - case 6: - { - switch (channelIndex) { - case 0: return MA_CHANNEL_FRONT_LEFT; - case 1: return MA_CHANNEL_FRONT_CENTER; - case 2: return MA_CHANNEL_FRONT_RIGHT; - case 3: return MA_CHANNEL_BACK_LEFT; - case 4: return MA_CHANNEL_BACK_RIGHT; - case 5: return MA_CHANNEL_LFE; - } - } break; - - case 7: - { - switch (channelIndex) { - case 0: return MA_CHANNEL_FRONT_LEFT; - case 1: return MA_CHANNEL_FRONT_CENTER; - case 2: return MA_CHANNEL_FRONT_RIGHT; - case 3: return MA_CHANNEL_SIDE_LEFT; - case 4: return MA_CHANNEL_SIDE_RIGHT; - case 5: return MA_CHANNEL_BACK_CENTER; - case 6: return MA_CHANNEL_LFE; - } - } break; - - case 8: - default: - { - switch (channelIndex) { - case 0: return MA_CHANNEL_FRONT_LEFT; - case 1: return MA_CHANNEL_FRONT_CENTER; - case 2: return MA_CHANNEL_FRONT_RIGHT; - case 3: return MA_CHANNEL_SIDE_LEFT; - case 4: return MA_CHANNEL_SIDE_RIGHT; - case 5: return MA_CHANNEL_BACK_LEFT; - case 6: return MA_CHANNEL_BACK_RIGHT; - case 7: return MA_CHANNEL_LFE; - } - } break; - } - - if (channelCount > 8) { - if (channelIndex < 32) { /* We have 32 AUX channels. */ - return (ma_channel)(MA_CHANNEL_AUX_0 + (channelIndex - 8)); - } - } - - /* Getting here means we don't know how to map the channel position so just return MA_CHANNEL_NONE. */ - return MA_CHANNEL_NONE; -} - -static ma_channel ma_channel_map_init_standard_channel_sndio(ma_uint32 channelCount, ma_uint32 channelIndex) -{ - switch (channelCount) - { - case 0: return MA_CHANNEL_NONE; - - case 1: - { - return MA_CHANNEL_MONO; - } break; - - case 2: - { - switch (channelIndex) { - case 0: return MA_CHANNEL_FRONT_LEFT; - case 1: return MA_CHANNEL_FRONT_RIGHT; - } - } break; - - case 3: /* No defined, but best guess. */ - { - switch (channelIndex) { - case 0: return MA_CHANNEL_FRONT_LEFT; - case 1: return MA_CHANNEL_FRONT_RIGHT; - case 2: return MA_CHANNEL_FRONT_CENTER; - } - } break; - - case 4: - { - switch (channelIndex) { - case 0: return MA_CHANNEL_FRONT_LEFT; - case 1: return MA_CHANNEL_FRONT_RIGHT; - case 2: return MA_CHANNEL_BACK_LEFT; - case 3: return MA_CHANNEL_BACK_RIGHT; - } - } break; - - case 5: /* Not defined, but best guess. */ - { - switch (channelIndex) { - case 0: return MA_CHANNEL_FRONT_LEFT; - case 1: return MA_CHANNEL_FRONT_RIGHT; - case 2: return MA_CHANNEL_BACK_LEFT; - case 3: return MA_CHANNEL_BACK_RIGHT; - case 4: return MA_CHANNEL_FRONT_CENTER; - } - } break; - - case 6: - default: - { - switch (channelIndex) { - case 0: return MA_CHANNEL_FRONT_LEFT; - case 1: return MA_CHANNEL_FRONT_RIGHT; - case 2: return MA_CHANNEL_BACK_LEFT; - case 3: return MA_CHANNEL_BACK_RIGHT; - case 4: return MA_CHANNEL_FRONT_CENTER; - case 5: return MA_CHANNEL_LFE; - } - } break; - } - - if (channelCount > 6) { - if (channelIndex < 32) { /* We have 32 AUX channels. */ - return (ma_channel)(MA_CHANNEL_AUX_0 + (channelIndex - 6)); - } - } - - /* Getting here means we don't know how to map the channel position so just return MA_CHANNEL_NONE. */ - return MA_CHANNEL_NONE; -} - - -static ma_channel ma_channel_map_init_standard_channel(ma_standard_channel_map standardChannelMap, ma_uint32 channelCount, ma_uint32 channelIndex) -{ - if (channelCount == 0 || channelIndex >= channelCount) { - return MA_CHANNEL_NONE; - } - - switch (standardChannelMap) - { - case ma_standard_channel_map_alsa: - { - return ma_channel_map_init_standard_channel_alsa(channelCount, channelIndex); - } break; - - case ma_standard_channel_map_rfc3551: - { - return ma_channel_map_init_standard_channel_rfc3551(channelCount, channelIndex); - } break; - - case ma_standard_channel_map_flac: - { - return ma_channel_map_init_standard_channel_flac(channelCount, channelIndex); - } break; - - case ma_standard_channel_map_vorbis: - { - return ma_channel_map_init_standard_channel_vorbis(channelCount, channelIndex); - } break; - - case ma_standard_channel_map_sound4: - { - return ma_channel_map_init_standard_channel_sound4(channelCount, channelIndex); - } break; - - case ma_standard_channel_map_sndio: - { - return ma_channel_map_init_standard_channel_sndio(channelCount, channelIndex); - } break; - - case ma_standard_channel_map_microsoft: /* Also default. */ - /*case ma_standard_channel_map_default;*/ - default: - { - return ma_channel_map_init_standard_channel_microsoft(channelCount, channelIndex); - } break; - } -} - -MA_API void ma_channel_map_init_standard(ma_standard_channel_map standardChannelMap, ma_channel* pChannelMap, size_t channelMapCap, ma_uint32 channels) -{ - ma_uint32 iChannel; - - if (pChannelMap == NULL || channelMapCap == 0 || channels == 0) { - return; - } - - for (iChannel = 0; iChannel < channels; iChannel += 1) { - if (channelMapCap == 0) { - break; /* Ran out of room. */ - } - - pChannelMap[0] = ma_channel_map_init_standard_channel(standardChannelMap, channels, iChannel); - pChannelMap += 1; - channelMapCap -= 1; - } -} - -MA_API void ma_channel_map_copy(ma_channel* pOut, const ma_channel* pIn, ma_uint32 channels) -{ - if (pOut != NULL && pIn != NULL && channels > 0) { - MA_COPY_MEMORY(pOut, pIn, sizeof(*pOut) * channels); - } -} - -MA_API void ma_channel_map_copy_or_default(ma_channel* pOut, size_t channelMapCapOut, const ma_channel* pIn, ma_uint32 channels) -{ - if (pOut == NULL || channels == 0) { - return; - } - - if (pIn != NULL) { - ma_channel_map_copy(pOut, pIn, channels); - } else { - ma_channel_map_init_standard(ma_standard_channel_map_default, pOut, channelMapCapOut, channels); - } -} - -MA_API ma_bool32 ma_channel_map_is_valid(const ma_channel* pChannelMap, ma_uint32 channels) -{ - /* A channel count of 0 is invalid. */ - if (channels == 0) { - return MA_FALSE; - } - - /* It does not make sense to have a mono channel when there is more than 1 channel. */ - if (channels > 1) { - ma_uint32 iChannel; - for (iChannel = 0; iChannel < channels; ++iChannel) { - if (ma_channel_map_get_channel(pChannelMap, channels, iChannel) == MA_CHANNEL_MONO) { - return MA_FALSE; - } - } - } - - return MA_TRUE; -} - -MA_API ma_bool32 ma_channel_map_is_equal(const ma_channel* pChannelMapA, const ma_channel* pChannelMapB, ma_uint32 channels) -{ - ma_uint32 iChannel; - - if (pChannelMapA == pChannelMapB) { - return MA_TRUE; - } - - for (iChannel = 0; iChannel < channels; ++iChannel) { - if (ma_channel_map_get_channel(pChannelMapA, channels, iChannel) != ma_channel_map_get_channel(pChannelMapB, channels, iChannel)) { - return MA_FALSE; - } - } - - return MA_TRUE; -} - -MA_API ma_bool32 ma_channel_map_is_blank(const ma_channel* pChannelMap, ma_uint32 channels) -{ - ma_uint32 iChannel; - - /* A null channel map is equivalent to the default channel map. */ - if (pChannelMap == NULL) { - return MA_FALSE; - } - - for (iChannel = 0; iChannel < channels; ++iChannel) { - if (pChannelMap[iChannel] != MA_CHANNEL_NONE) { - return MA_FALSE; - } - } - - return MA_TRUE; -} - -MA_API ma_bool32 ma_channel_map_contains_channel_position(ma_uint32 channels, const ma_channel* pChannelMap, ma_channel channelPosition) -{ - return ma_channel_map_find_channel_position(channels, pChannelMap, channelPosition, NULL); -} - -MA_API ma_bool32 ma_channel_map_find_channel_position(ma_uint32 channels, const ma_channel* pChannelMap, ma_channel channelPosition, ma_uint32* pChannelIndex) -{ - ma_uint32 iChannel; - - if (pChannelIndex != NULL) { - *pChannelIndex = (ma_uint32)-1; - } - - for (iChannel = 0; iChannel < channels; ++iChannel) { - if (ma_channel_map_get_channel(pChannelMap, channels, iChannel) == channelPosition) { - if (pChannelIndex != NULL) { - *pChannelIndex = iChannel; - } - - return MA_TRUE; - } - } - - /* Getting here means the channel position was not found. */ - return MA_FALSE; -} - -MA_API size_t ma_channel_map_to_string(const ma_channel* pChannelMap, ma_uint32 channels, char* pBufferOut, size_t bufferCap) -{ - size_t len; - ma_uint32 iChannel; - - len = 0; - - for (iChannel = 0; iChannel < channels; iChannel += 1) { - const char* pChannelStr = ma_channel_position_to_string(ma_channel_map_get_channel(pChannelMap, channels, iChannel)); - size_t channelStrLen = strlen(pChannelStr); - - /* Append the string if necessary. */ - if (pBufferOut != NULL && bufferCap > len + channelStrLen) { - MA_COPY_MEMORY(pBufferOut + len, pChannelStr, channelStrLen); - } - len += channelStrLen; - - /* Append a space if it's not the last item. */ - if (iChannel+1 < channels) { - if (pBufferOut != NULL && bufferCap > len + 1) { - pBufferOut[len] = ' '; - } - len += 1; - } - } - - /* Null terminate. Don't increment the length here. */ - if (pBufferOut != NULL && bufferCap > len + 1) { - pBufferOut[len] = '\0'; - } - - return len; -} - -MA_API const char* ma_channel_position_to_string(ma_channel channel) -{ - switch (channel) - { - case MA_CHANNEL_NONE : return "CHANNEL_NONE"; - case MA_CHANNEL_MONO : return "CHANNEL_MONO"; - case MA_CHANNEL_FRONT_LEFT : return "CHANNEL_FRONT_LEFT"; - case MA_CHANNEL_FRONT_RIGHT : return "CHANNEL_FRONT_RIGHT"; - case MA_CHANNEL_FRONT_CENTER : return "CHANNEL_FRONT_CENTER"; - case MA_CHANNEL_LFE : return "CHANNEL_LFE"; - case MA_CHANNEL_BACK_LEFT : return "CHANNEL_BACK_LEFT"; - case MA_CHANNEL_BACK_RIGHT : return "CHANNEL_BACK_RIGHT"; - case MA_CHANNEL_FRONT_LEFT_CENTER : return "CHANNEL_FRONT_LEFT_CENTER"; - case MA_CHANNEL_FRONT_RIGHT_CENTER: return "CHANNEL_FRONT_RIGHT_CENTER"; - case MA_CHANNEL_BACK_CENTER : return "CHANNEL_BACK_CENTER"; - case MA_CHANNEL_SIDE_LEFT : return "CHANNEL_SIDE_LEFT"; - case MA_CHANNEL_SIDE_RIGHT : return "CHANNEL_SIDE_RIGHT"; - case MA_CHANNEL_TOP_CENTER : return "CHANNEL_TOP_CENTER"; - case MA_CHANNEL_TOP_FRONT_LEFT : return "CHANNEL_TOP_FRONT_LEFT"; - case MA_CHANNEL_TOP_FRONT_CENTER : return "CHANNEL_TOP_FRONT_CENTER"; - case MA_CHANNEL_TOP_FRONT_RIGHT : return "CHANNEL_TOP_FRONT_RIGHT"; - case MA_CHANNEL_TOP_BACK_LEFT : return "CHANNEL_TOP_BACK_LEFT"; - case MA_CHANNEL_TOP_BACK_CENTER : return "CHANNEL_TOP_BACK_CENTER"; - case MA_CHANNEL_TOP_BACK_RIGHT : return "CHANNEL_TOP_BACK_RIGHT"; - case MA_CHANNEL_AUX_0 : return "CHANNEL_AUX_0"; - case MA_CHANNEL_AUX_1 : return "CHANNEL_AUX_1"; - case MA_CHANNEL_AUX_2 : return "CHANNEL_AUX_2"; - case MA_CHANNEL_AUX_3 : return "CHANNEL_AUX_3"; - case MA_CHANNEL_AUX_4 : return "CHANNEL_AUX_4"; - case MA_CHANNEL_AUX_5 : return "CHANNEL_AUX_5"; - case MA_CHANNEL_AUX_6 : return "CHANNEL_AUX_6"; - case MA_CHANNEL_AUX_7 : return "CHANNEL_AUX_7"; - case MA_CHANNEL_AUX_8 : return "CHANNEL_AUX_8"; - case MA_CHANNEL_AUX_9 : return "CHANNEL_AUX_9"; - case MA_CHANNEL_AUX_10 : return "CHANNEL_AUX_10"; - case MA_CHANNEL_AUX_11 : return "CHANNEL_AUX_11"; - case MA_CHANNEL_AUX_12 : return "CHANNEL_AUX_12"; - case MA_CHANNEL_AUX_13 : return "CHANNEL_AUX_13"; - case MA_CHANNEL_AUX_14 : return "CHANNEL_AUX_14"; - case MA_CHANNEL_AUX_15 : return "CHANNEL_AUX_15"; - case MA_CHANNEL_AUX_16 : return "CHANNEL_AUX_16"; - case MA_CHANNEL_AUX_17 : return "CHANNEL_AUX_17"; - case MA_CHANNEL_AUX_18 : return "CHANNEL_AUX_18"; - case MA_CHANNEL_AUX_19 : return "CHANNEL_AUX_19"; - case MA_CHANNEL_AUX_20 : return "CHANNEL_AUX_20"; - case MA_CHANNEL_AUX_21 : return "CHANNEL_AUX_21"; - case MA_CHANNEL_AUX_22 : return "CHANNEL_AUX_22"; - case MA_CHANNEL_AUX_23 : return "CHANNEL_AUX_23"; - case MA_CHANNEL_AUX_24 : return "CHANNEL_AUX_24"; - case MA_CHANNEL_AUX_25 : return "CHANNEL_AUX_25"; - case MA_CHANNEL_AUX_26 : return "CHANNEL_AUX_26"; - case MA_CHANNEL_AUX_27 : return "CHANNEL_AUX_27"; - case MA_CHANNEL_AUX_28 : return "CHANNEL_AUX_28"; - case MA_CHANNEL_AUX_29 : return "CHANNEL_AUX_29"; - case MA_CHANNEL_AUX_30 : return "CHANNEL_AUX_30"; - case MA_CHANNEL_AUX_31 : return "CHANNEL_AUX_31"; - default: break; - } - - return "UNKNOWN"; -} - - - -/************************************************************************************************************************************************************** - -Conversion Helpers - -**************************************************************************************************************************************************************/ -MA_API ma_uint64 ma_convert_frames(void* pOut, ma_uint64 frameCountOut, ma_format formatOut, ma_uint32 channelsOut, ma_uint32 sampleRateOut, const void* pIn, ma_uint64 frameCountIn, ma_format formatIn, ma_uint32 channelsIn, ma_uint32 sampleRateIn) -{ - ma_data_converter_config config; - - config = ma_data_converter_config_init(formatIn, formatOut, channelsIn, channelsOut, sampleRateIn, sampleRateOut); - config.resampling.linear.lpfOrder = ma_min(MA_DEFAULT_RESAMPLER_LPF_ORDER, MA_MAX_FILTER_ORDER); - - return ma_convert_frames_ex(pOut, frameCountOut, pIn, frameCountIn, &config); -} - -MA_API ma_uint64 ma_convert_frames_ex(void* pOut, ma_uint64 frameCountOut, const void* pIn, ma_uint64 frameCountIn, const ma_data_converter_config* pConfig) -{ - ma_result result; - ma_data_converter converter; - - if (frameCountIn == 0 || pConfig == NULL) { - return 0; - } - - result = ma_data_converter_init(pConfig, NULL, &converter); - if (result != MA_SUCCESS) { - return 0; /* Failed to initialize the data converter. */ - } - - if (pOut == NULL) { - result = ma_data_converter_get_expected_output_frame_count(&converter, frameCountIn, &frameCountOut); - if (result != MA_SUCCESS) { - if (result == MA_NOT_IMPLEMENTED) { - /* No way to calculate the number of frames, so we'll need to brute force it and loop. */ - frameCountOut = 0; - - while (frameCountIn > 0) { - ma_uint64 framesProcessedIn = frameCountIn; - ma_uint64 framesProcessedOut = 0xFFFFFFFF; - - result = ma_data_converter_process_pcm_frames(&converter, pIn, &framesProcessedIn, NULL, &framesProcessedOut); - if (result != MA_SUCCESS) { - break; - } - - frameCountIn -= framesProcessedIn; - } - } - } - } else { - result = ma_data_converter_process_pcm_frames(&converter, pIn, &frameCountIn, pOut, &frameCountOut); - if (result != MA_SUCCESS) { - frameCountOut = 0; - } - } - - ma_data_converter_uninit(&converter, NULL); - return frameCountOut; -} - - -/************************************************************************************************************************************************************** - -Ring Buffer - -**************************************************************************************************************************************************************/ -static MA_INLINE ma_uint32 ma_rb__extract_offset_in_bytes(ma_uint32 encodedOffset) -{ - return encodedOffset & 0x7FFFFFFF; -} - -static MA_INLINE ma_uint32 ma_rb__extract_offset_loop_flag(ma_uint32 encodedOffset) -{ - return encodedOffset & 0x80000000; -} - -static MA_INLINE void* ma_rb__get_read_ptr(ma_rb* pRB) -{ - MA_ASSERT(pRB != NULL); - return ma_offset_ptr(pRB->pBuffer, ma_rb__extract_offset_in_bytes(ma_atomic_load_32(&pRB->encodedReadOffset))); -} - -static MA_INLINE void* ma_rb__get_write_ptr(ma_rb* pRB) -{ - MA_ASSERT(pRB != NULL); - return ma_offset_ptr(pRB->pBuffer, ma_rb__extract_offset_in_bytes(ma_atomic_load_32(&pRB->encodedWriteOffset))); -} - -static MA_INLINE ma_uint32 ma_rb__construct_offset(ma_uint32 offsetInBytes, ma_uint32 offsetLoopFlag) -{ - return offsetLoopFlag | offsetInBytes; -} - -static MA_INLINE void ma_rb__deconstruct_offset(ma_uint32 encodedOffset, ma_uint32* pOffsetInBytes, ma_uint32* pOffsetLoopFlag) -{ - MA_ASSERT(pOffsetInBytes != NULL); - MA_ASSERT(pOffsetLoopFlag != NULL); - - *pOffsetInBytes = ma_rb__extract_offset_in_bytes(encodedOffset); - *pOffsetLoopFlag = ma_rb__extract_offset_loop_flag(encodedOffset); -} - - -MA_API ma_result ma_rb_init_ex(size_t subbufferSizeInBytes, size_t subbufferCount, size_t subbufferStrideInBytes, void* pOptionalPreallocatedBuffer, const ma_allocation_callbacks* pAllocationCallbacks, ma_rb* pRB) -{ - ma_result result; - const ma_uint32 maxSubBufferSize = 0x7FFFFFFF - (MA_SIMD_ALIGNMENT-1); - - if (pRB == NULL) { - return MA_INVALID_ARGS; - } - - if (subbufferSizeInBytes == 0 || subbufferCount == 0) { - return MA_INVALID_ARGS; - } - - if (subbufferSizeInBytes > maxSubBufferSize) { - return MA_INVALID_ARGS; /* Maximum buffer size is ~2GB. The most significant bit is a flag for use internally. */ - } - - - MA_ZERO_OBJECT(pRB); - - result = ma_allocation_callbacks_init_copy(&pRB->allocationCallbacks, pAllocationCallbacks); - if (result != MA_SUCCESS) { - return result; - } - - pRB->subbufferSizeInBytes = (ma_uint32)subbufferSizeInBytes; - pRB->subbufferCount = (ma_uint32)subbufferCount; - - if (pOptionalPreallocatedBuffer != NULL) { - pRB->subbufferStrideInBytes = (ma_uint32)subbufferStrideInBytes; - pRB->pBuffer = pOptionalPreallocatedBuffer; - } else { - size_t bufferSizeInBytes; - - /* - Here is where we allocate our own buffer. We always want to align this to MA_SIMD_ALIGNMENT for future SIMD optimization opportunity. To do this - we need to make sure the stride is a multiple of MA_SIMD_ALIGNMENT. - */ - pRB->subbufferStrideInBytes = (pRB->subbufferSizeInBytes + (MA_SIMD_ALIGNMENT-1)) & ~MA_SIMD_ALIGNMENT; - - bufferSizeInBytes = (size_t)pRB->subbufferCount*pRB->subbufferStrideInBytes; - pRB->pBuffer = ma_aligned_malloc(bufferSizeInBytes, MA_SIMD_ALIGNMENT, &pRB->allocationCallbacks); - if (pRB->pBuffer == NULL) { - return MA_OUT_OF_MEMORY; - } - - MA_ZERO_MEMORY(pRB->pBuffer, bufferSizeInBytes); - pRB->ownsBuffer = MA_TRUE; - } - - return MA_SUCCESS; -} - -MA_API ma_result ma_rb_init(size_t bufferSizeInBytes, void* pOptionalPreallocatedBuffer, const ma_allocation_callbacks* pAllocationCallbacks, ma_rb* pRB) -{ - return ma_rb_init_ex(bufferSizeInBytes, 1, 0, pOptionalPreallocatedBuffer, pAllocationCallbacks, pRB); -} - -MA_API void ma_rb_uninit(ma_rb* pRB) -{ - if (pRB == NULL) { - return; - } - - if (pRB->ownsBuffer) { - ma_aligned_free(pRB->pBuffer, &pRB->allocationCallbacks); - } -} - -MA_API void ma_rb_reset(ma_rb* pRB) -{ - if (pRB == NULL) { - return; - } - - ma_atomic_exchange_32(&pRB->encodedReadOffset, 0); - ma_atomic_exchange_32(&pRB->encodedWriteOffset, 0); -} - -MA_API ma_result ma_rb_acquire_read(ma_rb* pRB, size_t* pSizeInBytes, void** ppBufferOut) -{ - ma_uint32 writeOffset; - ma_uint32 writeOffsetInBytes; - ma_uint32 writeOffsetLoopFlag; - ma_uint32 readOffset; - ma_uint32 readOffsetInBytes; - ma_uint32 readOffsetLoopFlag; - size_t bytesAvailable; - size_t bytesRequested; - - if (pRB == NULL || pSizeInBytes == NULL || ppBufferOut == NULL) { - return MA_INVALID_ARGS; - } - - /* The returned buffer should never move ahead of the write pointer. */ - writeOffset = ma_atomic_load_32(&pRB->encodedWriteOffset); - ma_rb__deconstruct_offset(writeOffset, &writeOffsetInBytes, &writeOffsetLoopFlag); - - readOffset = ma_atomic_load_32(&pRB->encodedReadOffset); - ma_rb__deconstruct_offset(readOffset, &readOffsetInBytes, &readOffsetLoopFlag); - - /* - The number of bytes available depends on whether or not the read and write pointers are on the same loop iteration. If so, we - can only read up to the write pointer. If not, we can only read up to the end of the buffer. - */ - if (readOffsetLoopFlag == writeOffsetLoopFlag) { - bytesAvailable = writeOffsetInBytes - readOffsetInBytes; - } else { - bytesAvailable = pRB->subbufferSizeInBytes - readOffsetInBytes; - } - - bytesRequested = *pSizeInBytes; - if (bytesRequested > bytesAvailable) { - bytesRequested = bytesAvailable; - } - - *pSizeInBytes = bytesRequested; - (*ppBufferOut) = ma_rb__get_read_ptr(pRB); - - return MA_SUCCESS; -} - -MA_API ma_result ma_rb_commit_read(ma_rb* pRB, size_t sizeInBytes) -{ - ma_uint32 readOffset; - ma_uint32 readOffsetInBytes; - ma_uint32 readOffsetLoopFlag; - ma_uint32 newReadOffsetInBytes; - ma_uint32 newReadOffsetLoopFlag; - - if (pRB == NULL) { - return MA_INVALID_ARGS; - } - - readOffset = ma_atomic_load_32(&pRB->encodedReadOffset); - ma_rb__deconstruct_offset(readOffset, &readOffsetInBytes, &readOffsetLoopFlag); - - /* Check that sizeInBytes is correct. It should never go beyond the end of the buffer. */ - newReadOffsetInBytes = (ma_uint32)(readOffsetInBytes + sizeInBytes); - if (newReadOffsetInBytes > pRB->subbufferSizeInBytes) { - return MA_INVALID_ARGS; /* <-- sizeInBytes will cause the read offset to overflow. */ - } - - /* Move the read pointer back to the start if necessary. */ - newReadOffsetLoopFlag = readOffsetLoopFlag; - if (newReadOffsetInBytes == pRB->subbufferSizeInBytes) { - newReadOffsetInBytes = 0; - newReadOffsetLoopFlag ^= 0x80000000; - } - - ma_atomic_exchange_32(&pRB->encodedReadOffset, ma_rb__construct_offset(newReadOffsetInBytes, newReadOffsetLoopFlag)); - - return MA_SUCCESS; -} - -MA_API ma_result ma_rb_acquire_write(ma_rb* pRB, size_t* pSizeInBytes, void** ppBufferOut) -{ - ma_uint32 readOffset; - ma_uint32 readOffsetInBytes; - ma_uint32 readOffsetLoopFlag; - ma_uint32 writeOffset; - ma_uint32 writeOffsetInBytes; - ma_uint32 writeOffsetLoopFlag; - size_t bytesAvailable; - size_t bytesRequested; - - if (pRB == NULL || pSizeInBytes == NULL || ppBufferOut == NULL) { - return MA_INVALID_ARGS; - } - - /* The returned buffer should never overtake the read buffer. */ - readOffset = ma_atomic_load_32(&pRB->encodedReadOffset); - ma_rb__deconstruct_offset(readOffset, &readOffsetInBytes, &readOffsetLoopFlag); - - writeOffset = ma_atomic_load_32(&pRB->encodedWriteOffset); - ma_rb__deconstruct_offset(writeOffset, &writeOffsetInBytes, &writeOffsetLoopFlag); - - /* - In the case of writing, if the write pointer and the read pointer are on the same loop iteration we can only - write up to the end of the buffer. Otherwise we can only write up to the read pointer. The write pointer should - never overtake the read pointer. - */ - if (writeOffsetLoopFlag == readOffsetLoopFlag) { - bytesAvailable = pRB->subbufferSizeInBytes - writeOffsetInBytes; - } else { - bytesAvailable = readOffsetInBytes - writeOffsetInBytes; - } - - bytesRequested = *pSizeInBytes; - if (bytesRequested > bytesAvailable) { - bytesRequested = bytesAvailable; - } - - *pSizeInBytes = bytesRequested; - *ppBufferOut = ma_rb__get_write_ptr(pRB); - - /* Clear the buffer if desired. */ - if (pRB->clearOnWriteAcquire) { - MA_ZERO_MEMORY(*ppBufferOut, *pSizeInBytes); - } - - return MA_SUCCESS; -} - -MA_API ma_result ma_rb_commit_write(ma_rb* pRB, size_t sizeInBytes) -{ - ma_uint32 writeOffset; - ma_uint32 writeOffsetInBytes; - ma_uint32 writeOffsetLoopFlag; - ma_uint32 newWriteOffsetInBytes; - ma_uint32 newWriteOffsetLoopFlag; - - if (pRB == NULL) { - return MA_INVALID_ARGS; - } - - writeOffset = ma_atomic_load_32(&pRB->encodedWriteOffset); - ma_rb__deconstruct_offset(writeOffset, &writeOffsetInBytes, &writeOffsetLoopFlag); - - /* Check that sizeInBytes is correct. It should never go beyond the end of the buffer. */ - newWriteOffsetInBytes = (ma_uint32)(writeOffsetInBytes + sizeInBytes); - if (newWriteOffsetInBytes > pRB->subbufferSizeInBytes) { - return MA_INVALID_ARGS; /* <-- sizeInBytes will cause the read offset to overflow. */ - } - - /* Move the read pointer back to the start if necessary. */ - newWriteOffsetLoopFlag = writeOffsetLoopFlag; - if (newWriteOffsetInBytes == pRB->subbufferSizeInBytes) { - newWriteOffsetInBytes = 0; - newWriteOffsetLoopFlag ^= 0x80000000; - } - - ma_atomic_exchange_32(&pRB->encodedWriteOffset, ma_rb__construct_offset(newWriteOffsetInBytes, newWriteOffsetLoopFlag)); - - return MA_SUCCESS; -} - -MA_API ma_result ma_rb_seek_read(ma_rb* pRB, size_t offsetInBytes) -{ - ma_uint32 readOffset; - ma_uint32 readOffsetInBytes; - ma_uint32 readOffsetLoopFlag; - ma_uint32 writeOffset; - ma_uint32 writeOffsetInBytes; - ma_uint32 writeOffsetLoopFlag; - ma_uint32 newReadOffsetInBytes; - ma_uint32 newReadOffsetLoopFlag; - - if (pRB == NULL || offsetInBytes > pRB->subbufferSizeInBytes) { - return MA_INVALID_ARGS; - } - - readOffset = ma_atomic_load_32(&pRB->encodedReadOffset); - ma_rb__deconstruct_offset(readOffset, &readOffsetInBytes, &readOffsetLoopFlag); - - writeOffset = ma_atomic_load_32(&pRB->encodedWriteOffset); - ma_rb__deconstruct_offset(writeOffset, &writeOffsetInBytes, &writeOffsetLoopFlag); - - newReadOffsetLoopFlag = readOffsetLoopFlag; - - /* We cannot go past the write buffer. */ - if (readOffsetLoopFlag == writeOffsetLoopFlag) { - if ((readOffsetInBytes + offsetInBytes) > writeOffsetInBytes) { - newReadOffsetInBytes = writeOffsetInBytes; - } else { - newReadOffsetInBytes = (ma_uint32)(readOffsetInBytes + offsetInBytes); - } - } else { - /* May end up looping. */ - if ((readOffsetInBytes + offsetInBytes) >= pRB->subbufferSizeInBytes) { - newReadOffsetInBytes = (ma_uint32)(readOffsetInBytes + offsetInBytes) - pRB->subbufferSizeInBytes; - newReadOffsetLoopFlag ^= 0x80000000; /* <-- Looped. */ - } else { - newReadOffsetInBytes = (ma_uint32)(readOffsetInBytes + offsetInBytes); - } - } - - ma_atomic_exchange_32(&pRB->encodedReadOffset, ma_rb__construct_offset(newReadOffsetInBytes, newReadOffsetLoopFlag)); - return MA_SUCCESS; -} - -MA_API ma_result ma_rb_seek_write(ma_rb* pRB, size_t offsetInBytes) -{ - ma_uint32 readOffset; - ma_uint32 readOffsetInBytes; - ma_uint32 readOffsetLoopFlag; - ma_uint32 writeOffset; - ma_uint32 writeOffsetInBytes; - ma_uint32 writeOffsetLoopFlag; - ma_uint32 newWriteOffsetInBytes; - ma_uint32 newWriteOffsetLoopFlag; - - if (pRB == NULL) { - return MA_INVALID_ARGS; - } - - readOffset = ma_atomic_load_32(&pRB->encodedReadOffset); - ma_rb__deconstruct_offset(readOffset, &readOffsetInBytes, &readOffsetLoopFlag); - - writeOffset = ma_atomic_load_32(&pRB->encodedWriteOffset); - ma_rb__deconstruct_offset(writeOffset, &writeOffsetInBytes, &writeOffsetLoopFlag); - - newWriteOffsetLoopFlag = writeOffsetLoopFlag; - - /* We cannot go past the write buffer. */ - if (readOffsetLoopFlag == writeOffsetLoopFlag) { - /* May end up looping. */ - if ((writeOffsetInBytes + offsetInBytes) >= pRB->subbufferSizeInBytes) { - newWriteOffsetInBytes = (ma_uint32)(writeOffsetInBytes + offsetInBytes) - pRB->subbufferSizeInBytes; - newWriteOffsetLoopFlag ^= 0x80000000; /* <-- Looped. */ - } else { - newWriteOffsetInBytes = (ma_uint32)(writeOffsetInBytes + offsetInBytes); - } - } else { - if ((writeOffsetInBytes + offsetInBytes) > readOffsetInBytes) { - newWriteOffsetInBytes = readOffsetInBytes; - } else { - newWriteOffsetInBytes = (ma_uint32)(writeOffsetInBytes + offsetInBytes); - } - } - - ma_atomic_exchange_32(&pRB->encodedWriteOffset, ma_rb__construct_offset(newWriteOffsetInBytes, newWriteOffsetLoopFlag)); - return MA_SUCCESS; -} - -MA_API ma_int32 ma_rb_pointer_distance(ma_rb* pRB) -{ - ma_uint32 readOffset; - ma_uint32 readOffsetInBytes; - ma_uint32 readOffsetLoopFlag; - ma_uint32 writeOffset; - ma_uint32 writeOffsetInBytes; - ma_uint32 writeOffsetLoopFlag; - - if (pRB == NULL) { - return 0; - } - - readOffset = ma_atomic_load_32(&pRB->encodedReadOffset); - ma_rb__deconstruct_offset(readOffset, &readOffsetInBytes, &readOffsetLoopFlag); - - writeOffset = ma_atomic_load_32(&pRB->encodedWriteOffset); - ma_rb__deconstruct_offset(writeOffset, &writeOffsetInBytes, &writeOffsetLoopFlag); - - if (readOffsetLoopFlag == writeOffsetLoopFlag) { - return writeOffsetInBytes - readOffsetInBytes; - } else { - return writeOffsetInBytes + (pRB->subbufferSizeInBytes - readOffsetInBytes); - } -} - -MA_API ma_uint32 ma_rb_available_read(ma_rb* pRB) -{ - ma_int32 dist; - - if (pRB == NULL) { - return 0; - } - - dist = ma_rb_pointer_distance(pRB); - if (dist < 0) { - return 0; - } - - return dist; -} - -MA_API ma_uint32 ma_rb_available_write(ma_rb* pRB) -{ - if (pRB == NULL) { - return 0; - } - - return (ma_uint32)(ma_rb_get_subbuffer_size(pRB) - ma_rb_pointer_distance(pRB)); -} - -MA_API size_t ma_rb_get_subbuffer_size(ma_rb* pRB) -{ - if (pRB == NULL) { - return 0; - } - - return pRB->subbufferSizeInBytes; -} - -MA_API size_t ma_rb_get_subbuffer_stride(ma_rb* pRB) -{ - if (pRB == NULL) { - return 0; - } - - if (pRB->subbufferStrideInBytes == 0) { - return (size_t)pRB->subbufferSizeInBytes; - } - - return (size_t)pRB->subbufferStrideInBytes; -} - -MA_API size_t ma_rb_get_subbuffer_offset(ma_rb* pRB, size_t subbufferIndex) -{ - if (pRB == NULL) { - return 0; - } - - return subbufferIndex * ma_rb_get_subbuffer_stride(pRB); -} - -MA_API void* ma_rb_get_subbuffer_ptr(ma_rb* pRB, size_t subbufferIndex, void* pBuffer) -{ - if (pRB == NULL) { - return NULL; - } - - return ma_offset_ptr(pBuffer, ma_rb_get_subbuffer_offset(pRB, subbufferIndex)); -} - - - -static ma_result ma_pcm_rb_data_source__on_read(ma_data_source* pDataSource, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead) -{ - /* Since there's no notion of an end, we don't ever want to return MA_AT_END here. But it is possible to return 0. */ - ma_pcm_rb* pRB = (ma_pcm_rb*)pDataSource; - ma_result result; - ma_uint64 totalFramesRead; - - MA_ASSERT(pRB != NULL); - - /* We need to run this in a loop since the ring buffer itself may loop. */ - totalFramesRead = 0; - while (totalFramesRead < frameCount) { - void* pMappedBuffer; - ma_uint32 mappedFrameCount; - ma_uint64 framesToRead = frameCount - totalFramesRead; - if (framesToRead > 0xFFFFFFFF) { - framesToRead = 0xFFFFFFFF; - } - - mappedFrameCount = (ma_uint32)framesToRead; - result = ma_pcm_rb_acquire_read(pRB, &mappedFrameCount, &pMappedBuffer); - if (result != MA_SUCCESS) { - break; - } - - if (mappedFrameCount == 0) { - break; /* <-- End of ring buffer. */ - } - - ma_copy_pcm_frames(ma_offset_pcm_frames_ptr(pFramesOut, totalFramesRead, pRB->format, pRB->channels), pMappedBuffer, mappedFrameCount, pRB->format, pRB->channels); - - result = ma_pcm_rb_commit_read(pRB, mappedFrameCount); - if (result != MA_SUCCESS) { - break; - } - - totalFramesRead += mappedFrameCount; - } - - /* - There is no notion of an "end" in a ring buffer. If we didn't have enough data to fill the requested frame - count we'll need to pad with silence. If we don't do this, totalFramesRead might equal 0 which will result - in the data source layer at a higher level translating this to MA_AT_END which is incorrect for a ring buffer. - */ - if (totalFramesRead < frameCount) { - ma_silence_pcm_frames(ma_offset_pcm_frames_ptr(pFramesOut, totalFramesRead, pRB->format, pRB->channels), (frameCount - totalFramesRead), pRB->format, pRB->channels); - totalFramesRead = frameCount; - } - - *pFramesRead = totalFramesRead; - return MA_SUCCESS; -} - -static ma_result ma_pcm_rb_data_source__on_get_data_format(ma_data_source* pDataSource, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate, ma_channel* pChannelMap, size_t channelMapCap) -{ - ma_pcm_rb* pRB = (ma_pcm_rb*)pDataSource; - MA_ASSERT(pRB != NULL); - - if (pFormat != NULL) { - *pFormat = pRB->format; - } - - if (pChannels != NULL) { - *pChannels = pRB->channels; - } - - if (pSampleRate != NULL) { - *pSampleRate = pRB->sampleRate; - } - - /* Just assume the default channel map. */ - if (pChannelMap != NULL) { - ma_channel_map_init_standard(ma_standard_channel_map_default, pChannelMap, channelMapCap, pRB->channels); - } - - return MA_SUCCESS; -} - -static ma_data_source_vtable ma_gRBDataSourceVTable = -{ - ma_pcm_rb_data_source__on_read, - NULL, /* onSeek */ - ma_pcm_rb_data_source__on_get_data_format, - NULL, /* onGetCursor */ - NULL, /* onGetLength */ - NULL, /* onSetLooping */ - 0 -}; - -static MA_INLINE ma_uint32 ma_pcm_rb_get_bpf(ma_pcm_rb* pRB) -{ - MA_ASSERT(pRB != NULL); - - return ma_get_bytes_per_frame(pRB->format, pRB->channels); -} - -MA_API ma_result ma_pcm_rb_init_ex(ma_format format, ma_uint32 channels, ma_uint32 subbufferSizeInFrames, ma_uint32 subbufferCount, ma_uint32 subbufferStrideInFrames, void* pOptionalPreallocatedBuffer, const ma_allocation_callbacks* pAllocationCallbacks, ma_pcm_rb* pRB) -{ - ma_uint32 bpf; - ma_result result; - - if (pRB == NULL) { - return MA_INVALID_ARGS; - } - - MA_ZERO_OBJECT(pRB); - - bpf = ma_get_bytes_per_frame(format, channels); - if (bpf == 0) { - return MA_INVALID_ARGS; - } - - result = ma_rb_init_ex(subbufferSizeInFrames*bpf, subbufferCount, subbufferStrideInFrames*bpf, pOptionalPreallocatedBuffer, pAllocationCallbacks, &pRB->rb); - if (result != MA_SUCCESS) { - return result; - } - - pRB->format = format; - pRB->channels = channels; - pRB->sampleRate = 0; /* The sample rate is not passed in as a parameter. */ - - /* The PCM ring buffer is a data source. We need to get that set up as well. */ - { - ma_data_source_config dataSourceConfig = ma_data_source_config_init(); - dataSourceConfig.vtable = &ma_gRBDataSourceVTable; - - result = ma_data_source_init(&dataSourceConfig, &pRB->ds); - if (result != MA_SUCCESS) { - ma_rb_uninit(&pRB->rb); - return result; - } - } - - return MA_SUCCESS; -} - -MA_API ma_result ma_pcm_rb_init(ma_format format, ma_uint32 channels, ma_uint32 bufferSizeInFrames, void* pOptionalPreallocatedBuffer, const ma_allocation_callbacks* pAllocationCallbacks, ma_pcm_rb* pRB) -{ - return ma_pcm_rb_init_ex(format, channels, bufferSizeInFrames, 1, 0, pOptionalPreallocatedBuffer, pAllocationCallbacks, pRB); -} - -MA_API void ma_pcm_rb_uninit(ma_pcm_rb* pRB) -{ - if (pRB == NULL) { - return; - } - - ma_data_source_uninit(&pRB->ds); - ma_rb_uninit(&pRB->rb); -} - -MA_API void ma_pcm_rb_reset(ma_pcm_rb* pRB) -{ - if (pRB == NULL) { - return; - } - - ma_rb_reset(&pRB->rb); -} - -MA_API ma_result ma_pcm_rb_acquire_read(ma_pcm_rb* pRB, ma_uint32* pSizeInFrames, void** ppBufferOut) -{ - size_t sizeInBytes; - ma_result result; - - if (pRB == NULL || pSizeInFrames == NULL) { - return MA_INVALID_ARGS; - } - - sizeInBytes = *pSizeInFrames * ma_pcm_rb_get_bpf(pRB); - - result = ma_rb_acquire_read(&pRB->rb, &sizeInBytes, ppBufferOut); - if (result != MA_SUCCESS) { - return result; - } - - *pSizeInFrames = (ma_uint32)(sizeInBytes / (size_t)ma_pcm_rb_get_bpf(pRB)); - return MA_SUCCESS; -} - -MA_API ma_result ma_pcm_rb_commit_read(ma_pcm_rb* pRB, ma_uint32 sizeInFrames) -{ - if (pRB == NULL) { - return MA_INVALID_ARGS; - } - - return ma_rb_commit_read(&pRB->rb, sizeInFrames * ma_pcm_rb_get_bpf(pRB)); -} - -MA_API ma_result ma_pcm_rb_acquire_write(ma_pcm_rb* pRB, ma_uint32* pSizeInFrames, void** ppBufferOut) -{ - size_t sizeInBytes; - ma_result result; - - if (pRB == NULL) { - return MA_INVALID_ARGS; - } - - sizeInBytes = *pSizeInFrames * ma_pcm_rb_get_bpf(pRB); - - result = ma_rb_acquire_write(&pRB->rb, &sizeInBytes, ppBufferOut); - if (result != MA_SUCCESS) { - return result; - } - - *pSizeInFrames = (ma_uint32)(sizeInBytes / ma_pcm_rb_get_bpf(pRB)); - return MA_SUCCESS; -} - -MA_API ma_result ma_pcm_rb_commit_write(ma_pcm_rb* pRB, ma_uint32 sizeInFrames) -{ - if (pRB == NULL) { - return MA_INVALID_ARGS; - } - - return ma_rb_commit_write(&pRB->rb, sizeInFrames * ma_pcm_rb_get_bpf(pRB)); -} - -MA_API ma_result ma_pcm_rb_seek_read(ma_pcm_rb* pRB, ma_uint32 offsetInFrames) -{ - if (pRB == NULL) { - return MA_INVALID_ARGS; - } - - return ma_rb_seek_read(&pRB->rb, offsetInFrames * ma_pcm_rb_get_bpf(pRB)); -} - -MA_API ma_result ma_pcm_rb_seek_write(ma_pcm_rb* pRB, ma_uint32 offsetInFrames) -{ - if (pRB == NULL) { - return MA_INVALID_ARGS; - } - - return ma_rb_seek_write(&pRB->rb, offsetInFrames * ma_pcm_rb_get_bpf(pRB)); -} - -MA_API ma_int32 ma_pcm_rb_pointer_distance(ma_pcm_rb* pRB) -{ - if (pRB == NULL) { - return 0; - } - - return ma_rb_pointer_distance(&pRB->rb) / ma_pcm_rb_get_bpf(pRB); -} - -MA_API ma_uint32 ma_pcm_rb_available_read(ma_pcm_rb* pRB) -{ - if (pRB == NULL) { - return 0; - } - - return ma_rb_available_read(&pRB->rb) / ma_pcm_rb_get_bpf(pRB); -} - -MA_API ma_uint32 ma_pcm_rb_available_write(ma_pcm_rb* pRB) -{ - if (pRB == NULL) { - return 0; - } - - return ma_rb_available_write(&pRB->rb) / ma_pcm_rb_get_bpf(pRB); -} - -MA_API ma_uint32 ma_pcm_rb_get_subbuffer_size(ma_pcm_rb* pRB) -{ - if (pRB == NULL) { - return 0; - } - - return (ma_uint32)(ma_rb_get_subbuffer_size(&pRB->rb) / ma_pcm_rb_get_bpf(pRB)); -} - -MA_API ma_uint32 ma_pcm_rb_get_subbuffer_stride(ma_pcm_rb* pRB) -{ - if (pRB == NULL) { - return 0; - } - - return (ma_uint32)(ma_rb_get_subbuffer_stride(&pRB->rb) / ma_pcm_rb_get_bpf(pRB)); -} - -MA_API ma_uint32 ma_pcm_rb_get_subbuffer_offset(ma_pcm_rb* pRB, ma_uint32 subbufferIndex) -{ - if (pRB == NULL) { - return 0; - } - - return (ma_uint32)(ma_rb_get_subbuffer_offset(&pRB->rb, subbufferIndex) / ma_pcm_rb_get_bpf(pRB)); -} - -MA_API void* ma_pcm_rb_get_subbuffer_ptr(ma_pcm_rb* pRB, ma_uint32 subbufferIndex, void* pBuffer) -{ - if (pRB == NULL) { - return NULL; - } - - return ma_rb_get_subbuffer_ptr(&pRB->rb, subbufferIndex, pBuffer); -} - -MA_API ma_format ma_pcm_rb_get_format(const ma_pcm_rb* pRB) -{ - if (pRB == NULL) { - return ma_format_unknown; - } - - return pRB->format; -} - -MA_API ma_uint32 ma_pcm_rb_get_channels(const ma_pcm_rb* pRB) -{ - if (pRB == NULL) { - return 0; - } - - return pRB->channels; -} - -MA_API ma_uint32 ma_pcm_rb_get_sample_rate(const ma_pcm_rb* pRB) -{ - if (pRB == NULL) { - return 0; - } - - return pRB->sampleRate; -} - -MA_API void ma_pcm_rb_set_sample_rate(ma_pcm_rb* pRB, ma_uint32 sampleRate) -{ - if (pRB == NULL) { - return; - } - - pRB->sampleRate = sampleRate; -} - - - -MA_API ma_result ma_duplex_rb_init(ma_format captureFormat, ma_uint32 captureChannels, ma_uint32 sampleRate, ma_uint32 captureInternalSampleRate, ma_uint32 captureInternalPeriodSizeInFrames, const ma_allocation_callbacks* pAllocationCallbacks, ma_duplex_rb* pRB) -{ - ma_result result; - ma_uint32 sizeInFrames; - - sizeInFrames = (ma_uint32)ma_calculate_frame_count_after_resampling(sampleRate, captureInternalSampleRate, captureInternalPeriodSizeInFrames * 5); - if (sizeInFrames == 0) { - return MA_INVALID_ARGS; - } - - result = ma_pcm_rb_init(captureFormat, captureChannels, sizeInFrames, NULL, pAllocationCallbacks, &pRB->rb); - if (result != MA_SUCCESS) { - return result; - } - - /* Seek forward a bit so we have a bit of a buffer in case of desyncs. */ - ma_pcm_rb_seek_write((ma_pcm_rb*)pRB, captureInternalPeriodSizeInFrames * 2); - - return MA_SUCCESS; -} - -MA_API ma_result ma_duplex_rb_uninit(ma_duplex_rb* pRB) -{ - ma_pcm_rb_uninit((ma_pcm_rb*)pRB); - return MA_SUCCESS; -} - - - -/************************************************************************************************************************************************************** - -Miscellaneous Helpers - -**************************************************************************************************************************************************************/ -MA_API const char* ma_result_description(ma_result result) -{ - switch (result) - { - case MA_SUCCESS: return "No error"; - case MA_ERROR: return "Unknown error"; - case MA_INVALID_ARGS: return "Invalid argument"; - case MA_INVALID_OPERATION: return "Invalid operation"; - case MA_OUT_OF_MEMORY: return "Out of memory"; - case MA_OUT_OF_RANGE: return "Out of range"; - case MA_ACCESS_DENIED: return "Permission denied"; - case MA_DOES_NOT_EXIST: return "Resource does not exist"; - case MA_ALREADY_EXISTS: return "Resource already exists"; - case MA_TOO_MANY_OPEN_FILES: return "Too many open files"; - case MA_INVALID_FILE: return "Invalid file"; - case MA_TOO_BIG: return "Too large"; - case MA_PATH_TOO_LONG: return "Path too long"; - case MA_NAME_TOO_LONG: return "Name too long"; - case MA_NOT_DIRECTORY: return "Not a directory"; - case MA_IS_DIRECTORY: return "Is a directory"; - case MA_DIRECTORY_NOT_EMPTY: return "Directory not empty"; - case MA_AT_END: return "At end"; - case MA_NO_SPACE: return "No space available"; - case MA_BUSY: return "Device or resource busy"; - case MA_IO_ERROR: return "Input/output error"; - case MA_INTERRUPT: return "Interrupted"; - case MA_UNAVAILABLE: return "Resource unavailable"; - case MA_ALREADY_IN_USE: return "Resource already in use"; - case MA_BAD_ADDRESS: return "Bad address"; - case MA_BAD_SEEK: return "Illegal seek"; - case MA_BAD_PIPE: return "Broken pipe"; - case MA_DEADLOCK: return "Deadlock"; - case MA_TOO_MANY_LINKS: return "Too many links"; - case MA_NOT_IMPLEMENTED: return "Not implemented"; - case MA_NO_MESSAGE: return "No message of desired type"; - case MA_BAD_MESSAGE: return "Invalid message"; - case MA_NO_DATA_AVAILABLE: return "No data available"; - case MA_INVALID_DATA: return "Invalid data"; - case MA_TIMEOUT: return "Timeout"; - case MA_NO_NETWORK: return "Network unavailable"; - case MA_NOT_UNIQUE: return "Not unique"; - case MA_NOT_SOCKET: return "Socket operation on non-socket"; - case MA_NO_ADDRESS: return "Destination address required"; - case MA_BAD_PROTOCOL: return "Protocol wrong type for socket"; - case MA_PROTOCOL_UNAVAILABLE: return "Protocol not available"; - case MA_PROTOCOL_NOT_SUPPORTED: return "Protocol not supported"; - case MA_PROTOCOL_FAMILY_NOT_SUPPORTED: return "Protocol family not supported"; - case MA_ADDRESS_FAMILY_NOT_SUPPORTED: return "Address family not supported"; - case MA_SOCKET_NOT_SUPPORTED: return "Socket type not supported"; - case MA_CONNECTION_RESET: return "Connection reset"; - case MA_ALREADY_CONNECTED: return "Already connected"; - case MA_NOT_CONNECTED: return "Not connected"; - case MA_CONNECTION_REFUSED: return "Connection refused"; - case MA_NO_HOST: return "No host"; - case MA_IN_PROGRESS: return "Operation in progress"; - case MA_CANCELLED: return "Operation cancelled"; - case MA_MEMORY_ALREADY_MAPPED: return "Memory already mapped"; - - case MA_FORMAT_NOT_SUPPORTED: return "Format not supported"; - case MA_DEVICE_TYPE_NOT_SUPPORTED: return "Device type not supported"; - case MA_SHARE_MODE_NOT_SUPPORTED: return "Share mode not supported"; - case MA_NO_BACKEND: return "No backend"; - case MA_NO_DEVICE: return "No device"; - case MA_API_NOT_FOUND: return "API not found"; - case MA_INVALID_DEVICE_CONFIG: return "Invalid device config"; - - case MA_DEVICE_NOT_INITIALIZED: return "Device not initialized"; - case MA_DEVICE_NOT_STARTED: return "Device not started"; - - case MA_FAILED_TO_INIT_BACKEND: return "Failed to initialize backend"; - case MA_FAILED_TO_OPEN_BACKEND_DEVICE: return "Failed to open backend device"; - case MA_FAILED_TO_START_BACKEND_DEVICE: return "Failed to start backend device"; - case MA_FAILED_TO_STOP_BACKEND_DEVICE: return "Failed to stop backend device"; - - default: return "Unknown error"; - } -} - -MA_API void* ma_malloc(size_t sz, const ma_allocation_callbacks* pAllocationCallbacks) -{ - if (pAllocationCallbacks != NULL) { - if (pAllocationCallbacks->onMalloc != NULL) { - return pAllocationCallbacks->onMalloc(sz, pAllocationCallbacks->pUserData); - } else { - return NULL; /* Do not fall back to the default implementation. */ - } - } else { - return ma__malloc_default(sz, NULL); - } -} - -MA_API void* ma_calloc(size_t sz, const ma_allocation_callbacks* pAllocationCallbacks) -{ - void* p = ma_malloc(sz, pAllocationCallbacks); - if (p != NULL) { - MA_ZERO_MEMORY(p, sz); - } - - return p; -} - -MA_API void* ma_realloc(void* p, size_t sz, const ma_allocation_callbacks* pAllocationCallbacks) -{ - if (pAllocationCallbacks != NULL) { - if (pAllocationCallbacks->onRealloc != NULL) { - return pAllocationCallbacks->onRealloc(p, sz, pAllocationCallbacks->pUserData); - } else { - return NULL; /* Do not fall back to the default implementation. */ - } - } else { - return ma__realloc_default(p, sz, NULL); - } -} - -MA_API void ma_free(void* p, const ma_allocation_callbacks* pAllocationCallbacks) -{ - if (p == NULL) { - return; - } - - if (pAllocationCallbacks != NULL) { - if (pAllocationCallbacks->onFree != NULL) { - pAllocationCallbacks->onFree(p, pAllocationCallbacks->pUserData); - } else { - return; /* Do no fall back to the default implementation. */ - } - } else { - ma__free_default(p, NULL); - } -} - -MA_API void* ma_aligned_malloc(size_t sz, size_t alignment, const ma_allocation_callbacks* pAllocationCallbacks) -{ - size_t extraBytes; - void* pUnaligned; - void* pAligned; - - if (alignment == 0) { - return 0; - } - - extraBytes = alignment-1 + sizeof(void*); - - pUnaligned = ma_malloc(sz + extraBytes, pAllocationCallbacks); - if (pUnaligned == NULL) { - return NULL; - } - - pAligned = (void*)(((ma_uintptr)pUnaligned + extraBytes) & ~((ma_uintptr)(alignment-1))); - ((void**)pAligned)[-1] = pUnaligned; - - return pAligned; -} - -MA_API void ma_aligned_free(void* p, const ma_allocation_callbacks* pAllocationCallbacks) -{ - ma_free(((void**)p)[-1], pAllocationCallbacks); -} - -MA_API const char* ma_get_format_name(ma_format format) -{ - switch (format) - { - case ma_format_unknown: return "Unknown"; - case ma_format_u8: return "8-bit Unsigned Integer"; - case ma_format_s16: return "16-bit Signed Integer"; - case ma_format_s24: return "24-bit Signed Integer (Tightly Packed)"; - case ma_format_s32: return "32-bit Signed Integer"; - case ma_format_f32: return "32-bit IEEE Floating Point"; - default: return "Invalid"; - } -} - -MA_API void ma_blend_f32(float* pOut, float* pInA, float* pInB, float factor, ma_uint32 channels) -{ - ma_uint32 i; - for (i = 0; i < channels; ++i) { - pOut[i] = ma_mix_f32(pInA[i], pInB[i], factor); - } -} - - -MA_API ma_uint32 ma_get_bytes_per_sample(ma_format format) -{ - ma_uint32 sizes[] = { - 0, /* unknown */ - 1, /* u8 */ - 2, /* s16 */ - 3, /* s24 */ - 4, /* s32 */ - 4, /* f32 */ - }; - return sizes[format]; -} - - - -#define MA_DATA_SOURCE_DEFAULT_RANGE_BEG 0 -#define MA_DATA_SOURCE_DEFAULT_RANGE_END ~((ma_uint64)0) -#define MA_DATA_SOURCE_DEFAULT_LOOP_POINT_BEG 0 -#define MA_DATA_SOURCE_DEFAULT_LOOP_POINT_END ~((ma_uint64)0) - -MA_API ma_data_source_config ma_data_source_config_init(void) -{ - ma_data_source_config config; - - MA_ZERO_OBJECT(&config); - - return config; -} - - -MA_API ma_result ma_data_source_init(const ma_data_source_config* pConfig, ma_data_source* pDataSource) -{ - ma_data_source_base* pDataSourceBase = (ma_data_source_base*)pDataSource; - - if (pDataSource == NULL) { - return MA_INVALID_ARGS; - } - - MA_ZERO_OBJECT(pDataSourceBase); - - if (pConfig == NULL) { - return MA_INVALID_ARGS; - } - - if (pConfig->vtable == NULL) { - return MA_INVALID_ARGS; - } - - pDataSourceBase->vtable = pConfig->vtable; - pDataSourceBase->rangeBegInFrames = MA_DATA_SOURCE_DEFAULT_RANGE_BEG; - pDataSourceBase->rangeEndInFrames = MA_DATA_SOURCE_DEFAULT_RANGE_END; - pDataSourceBase->loopBegInFrames = MA_DATA_SOURCE_DEFAULT_LOOP_POINT_BEG; - pDataSourceBase->loopEndInFrames = MA_DATA_SOURCE_DEFAULT_LOOP_POINT_END; - pDataSourceBase->pCurrent = pDataSource; /* Always read from ourself by default. */ - pDataSourceBase->pNext = NULL; - pDataSourceBase->onGetNext = NULL; - - return MA_SUCCESS; -} - -MA_API void ma_data_source_uninit(ma_data_source* pDataSource) -{ - if (pDataSource == NULL) { - return; - } - - /* - This is placeholder in case we need this later. Data sources need to call this in their - uninitialization routine to ensure things work later on if something is added here. - */ -} - -static ma_result ma_data_source_resolve_current(ma_data_source* pDataSource, ma_data_source** ppCurrentDataSource) -{ - ma_data_source_base* pCurrentDataSource = (ma_data_source_base*)pDataSource; - - MA_ASSERT(pDataSource != NULL); - MA_ASSERT(ppCurrentDataSource != NULL); - - if (pCurrentDataSource->pCurrent == NULL) { - /* - The current data source is NULL. If we're using this in the context of a chain we need to return NULL - here so that we don't end up looping. Otherwise we just return the data source itself. - */ - if (pCurrentDataSource->pNext != NULL || pCurrentDataSource->onGetNext != NULL) { - pCurrentDataSource = NULL; - } else { - pCurrentDataSource = (ma_data_source_base*)pDataSource; /* Not being used in a chain. Make sure we just always read from the data source itself at all times. */ - } - } else { - pCurrentDataSource = (ma_data_source_base*)pCurrentDataSource->pCurrent; - } - - *ppCurrentDataSource = pCurrentDataSource; - - return MA_SUCCESS; -} - -static ma_result ma_data_source_read_pcm_frames_from_backend(ma_data_source* pDataSource, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead) -{ - ma_data_source_base* pDataSourceBase = (ma_data_source_base*)pDataSource; - - MA_ASSERT(pDataSourceBase != NULL); - MA_ASSERT(pDataSourceBase->vtable != NULL); - MA_ASSERT(pDataSourceBase->vtable->onRead != NULL); - MA_ASSERT(pFramesRead != NULL); - - if (pFramesOut != NULL) { - return pDataSourceBase->vtable->onRead(pDataSourceBase, pFramesOut, frameCount, pFramesRead); - } else { - /* - No output buffer. Probably seeking forward. Read and discard. Can probably optimize this in terms of - onSeek and onGetCursor, but need to keep in mind that the data source may not implement these functions. - */ - ma_result result; - ma_uint64 framesRead; - ma_format format; - ma_uint32 channels; - ma_uint64 discardBufferCapInFrames; - ma_uint8 pDiscardBuffer[4096]; - - result = ma_data_source_get_data_format(pDataSource, &format, &channels, NULL, NULL, 0); - if (result != MA_SUCCESS) { - return result; - } - - discardBufferCapInFrames = sizeof(pDiscardBuffer) / ma_get_bytes_per_frame(format, channels); - - framesRead = 0; - while (framesRead < frameCount) { - ma_uint64 framesReadThisIteration = 0; - ma_uint64 framesToRead = frameCount - framesRead; - if (framesToRead > discardBufferCapInFrames) { - framesToRead = discardBufferCapInFrames; - } - - result = pDataSourceBase->vtable->onRead(pDataSourceBase, pDiscardBuffer, framesToRead, &framesReadThisIteration); - if (result != MA_SUCCESS) { - return result; - } - - framesRead += framesReadThisIteration; - } - - *pFramesRead = framesRead; - - return MA_SUCCESS; - } -} - -static ma_result ma_data_source_read_pcm_frames_within_range(ma_data_source* pDataSource, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead) -{ - ma_data_source_base* pDataSourceBase = (ma_data_source_base*)pDataSource; - ma_result result; - ma_uint64 framesRead = 0; - ma_bool32 loop = ma_data_source_is_looping(pDataSource); - - if (pDataSourceBase == NULL) { - return MA_AT_END; - } - - if (frameCount == 0) { - return MA_INVALID_ARGS; - } - - MA_ASSERT(pDataSourceBase->vtable != NULL); - - if ((pDataSourceBase->vtable->flags & MA_DATA_SOURCE_SELF_MANAGED_RANGE_AND_LOOP_POINT) != 0 || (pDataSourceBase->rangeEndInFrames == ~((ma_uint64)0) && (pDataSourceBase->loopEndInFrames == ~((ma_uint64)0) || loop == MA_FALSE))) { - /* Either the data source is self-managing the range, or no range is set - just read like normal. The data source itself will tell us when the end is reached. */ - result = ma_data_source_read_pcm_frames_from_backend(pDataSource, pFramesOut, frameCount, &framesRead); - } else { - /* Need to clamp to within the range. */ - ma_uint64 relativeCursor; - ma_uint64 absoluteCursor; - - result = ma_data_source_get_cursor_in_pcm_frames(pDataSourceBase, &relativeCursor); - if (result != MA_SUCCESS) { - /* Failed to retrieve the cursor. Cannot read within a range or loop points. Just read like normal - this may happen for things like noise data sources where it doesn't really matter. */ - result = ma_data_source_read_pcm_frames_from_backend(pDataSource, pFramesOut, frameCount, &framesRead); - } else { - ma_uint64 rangeBeg; - ma_uint64 rangeEnd; - - /* We have the cursor. We need to make sure we don't read beyond our range. */ - rangeBeg = pDataSourceBase->rangeBegInFrames; - rangeEnd = pDataSourceBase->rangeEndInFrames; - - absoluteCursor = rangeBeg + relativeCursor; - - /* If looping, make sure we're within range. */ - if (loop) { - if (pDataSourceBase->loopEndInFrames != ~((ma_uint64)0)) { - rangeEnd = ma_min(rangeEnd, pDataSourceBase->rangeBegInFrames + pDataSourceBase->loopEndInFrames); - } - } - - if (frameCount > (rangeEnd - absoluteCursor) && rangeEnd != ~((ma_uint64)0)) { - frameCount = (rangeEnd - absoluteCursor); - } - - /* - If the cursor is sitting on the end of the range the frame count will be set to 0 which can - result in MA_INVALID_ARGS. In this case, we don't want to try reading, but instead return - MA_AT_END so the higher level function can know about it. - */ - if (frameCount > 0) { - result = ma_data_source_read_pcm_frames_from_backend(pDataSource, pFramesOut, frameCount, &framesRead); - } else { - result = MA_AT_END; /* The cursor is sitting on the end of the range which means we're at the end. */ - } - } - } - - if (pFramesRead != NULL) { - *pFramesRead = framesRead; - } - - /* We need to make sure MA_AT_END is returned if we hit the end of the range. */ - if (result == MA_SUCCESS && framesRead == 0) { - result = MA_AT_END; - } - - return result; -} - -MA_API ma_result ma_data_source_read_pcm_frames(ma_data_source* pDataSource, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead) -{ - ma_result result = MA_SUCCESS; - ma_data_source_base* pDataSourceBase = (ma_data_source_base*)pDataSource; - ma_data_source_base* pCurrentDataSource; - void* pRunningFramesOut = pFramesOut; - ma_uint64 totalFramesProcessed = 0; - ma_format format; - ma_uint32 channels; - ma_uint32 emptyLoopCounter = 0; /* Keeps track of how many times 0 frames have been read. For infinite loop detection of sounds with no audio data. */ - ma_bool32 loop; - - if (pFramesRead != NULL) { - *pFramesRead = 0; - } - - if (frameCount == 0) { - return MA_INVALID_ARGS; - } - - if (pDataSourceBase == NULL) { - return MA_INVALID_ARGS; - } - - loop = ma_data_source_is_looping(pDataSource); - - /* - We need to know the data format so we can advance the output buffer as we read frames. If this - fails, chaining will not work and we'll just read as much as we can from the current source. - */ - if (ma_data_source_get_data_format(pDataSource, &format, &channels, NULL, NULL, 0) != MA_SUCCESS) { - result = ma_data_source_resolve_current(pDataSource, (ma_data_source**)&pCurrentDataSource); - if (result != MA_SUCCESS) { - return result; - } - - return ma_data_source_read_pcm_frames_within_range(pCurrentDataSource, pFramesOut, frameCount, pFramesRead); - } - - /* - Looping is a bit of a special case. When the `loop` argument is true, chaining will not work and - only the current data source will be read from. - */ - - /* Keep reading until we've read as many frames as possible. */ - while (totalFramesProcessed < frameCount) { - ma_uint64 framesProcessed; - ma_uint64 framesRemaining = frameCount - totalFramesProcessed; - - /* We need to resolve the data source that we'll actually be reading from. */ - result = ma_data_source_resolve_current(pDataSource, (ma_data_source**)&pCurrentDataSource); - if (result != MA_SUCCESS) { - break; - } - - if (pCurrentDataSource == NULL) { - break; - } - - result = ma_data_source_read_pcm_frames_within_range(pCurrentDataSource, pRunningFramesOut, framesRemaining, &framesProcessed); - totalFramesProcessed += framesProcessed; - - /* - If we encountered an error from the read callback, make sure it's propagated to the caller. The caller may need to know whether or not MA_BUSY is returned which is - not necessarily considered an error. - */ - if (result != MA_SUCCESS && result != MA_AT_END) { - break; - } - - /* - We can determine if we've reached the end by checking if ma_data_source_read_pcm_frames_within_range() returned - MA_AT_END. To loop back to the start, all we need to do is seek back to the first frame. - */ - if (result == MA_AT_END) { - /* - The result needs to be reset back to MA_SUCCESS (from MA_AT_END) so that we don't - accidentally return MA_AT_END when data has been read in prior loop iterations. at the - end of this function, the result will be checked for MA_SUCCESS, and if the total - number of frames processed is 0, will be explicitly set to MA_AT_END. - */ - result = MA_SUCCESS; - - /* - We reached the end. If we're looping, we just loop back to the start of the current - data source. If we're not looping we need to check if we have another in the chain, and - if so, switch to it. - */ - if (loop) { - if (framesProcessed == 0) { - emptyLoopCounter += 1; - if (emptyLoopCounter > 1) { - break; /* Infinite loop detected. Get out. */ - } - } else { - emptyLoopCounter = 0; - } - - result = ma_data_source_seek_to_pcm_frame(pCurrentDataSource, pCurrentDataSource->loopBegInFrames); - if (result != MA_SUCCESS) { - break; /* Failed to loop. Abort. */ - } - - /* Don't return MA_AT_END for looping sounds. */ - result = MA_SUCCESS; - } else { - if (pCurrentDataSource->pNext != NULL) { - pDataSourceBase->pCurrent = pCurrentDataSource->pNext; - } else if (pCurrentDataSource->onGetNext != NULL) { - pDataSourceBase->pCurrent = pCurrentDataSource->onGetNext(pCurrentDataSource); - if (pDataSourceBase->pCurrent == NULL) { - break; /* Our callback did not return a next data source. We're done. */ - } - } else { - /* Reached the end of the chain. We're done. */ - break; - } - - /* The next data source needs to be rewound to ensure data is read in looping scenarios. */ - result = ma_data_source_seek_to_pcm_frame(pDataSourceBase->pCurrent, 0); - if (result != MA_SUCCESS) { - break; - } - } - } - - if (pRunningFramesOut != NULL) { - pRunningFramesOut = ma_offset_ptr(pRunningFramesOut, framesProcessed * ma_get_bytes_per_frame(format, channels)); - } - } - - if (pFramesRead != NULL) { - *pFramesRead = totalFramesProcessed; - } - - MA_ASSERT(!(result == MA_AT_END && totalFramesProcessed > 0)); /* We should never be returning MA_AT_END if we read some data. */ - - if (result == MA_SUCCESS && totalFramesProcessed == 0) { - result = MA_AT_END; - } - - return result; -} - -MA_API ma_result ma_data_source_seek_pcm_frames(ma_data_source* pDataSource, ma_uint64 frameCount, ma_uint64* pFramesSeeked) -{ - return ma_data_source_read_pcm_frames(pDataSource, NULL, frameCount, pFramesSeeked); -} - -MA_API ma_result ma_data_source_seek_to_pcm_frame(ma_data_source* pDataSource, ma_uint64 frameIndex) -{ - ma_data_source_base* pDataSourceBase = (ma_data_source_base*)pDataSource; - - if (pDataSourceBase == NULL) { - return MA_INVALID_ARGS; - } - - if (pDataSourceBase->vtable->onSeek == NULL) { - return MA_NOT_IMPLEMENTED; - } - - if (frameIndex > pDataSourceBase->rangeEndInFrames) { - return MA_INVALID_OPERATION; /* Trying to seek too far forward. */ - } - - MA_ASSERT(pDataSourceBase->vtable != NULL); - - return pDataSourceBase->vtable->onSeek(pDataSource, pDataSourceBase->rangeBegInFrames + frameIndex); -} - -MA_API ma_result ma_data_source_seek_seconds(ma_data_source* pDataSource, float secondCount, float* pSecondsSeeked) -{ - ma_uint64 frameCount; - ma_uint64 framesSeeked = 0; - ma_uint32 sampleRate; - ma_result result; - - if (pDataSource == NULL) { - return MA_INVALID_ARGS; - } - - result = ma_data_source_get_data_format(pDataSource, NULL, NULL, &sampleRate, NULL, 0); - if (result != MA_SUCCESS) { - return result; - } - - /* We need PCM frames instead of seconds */ - frameCount = (ma_uint64)(secondCount * sampleRate); - - result = ma_data_source_seek_pcm_frames(pDataSource, frameCount, &framesSeeked); - - /* VC6 doesn't support division between unsigned 64-bit integer and floating point number. Signed integer needed. This shouldn't affect anything in practice */ - *pSecondsSeeked = (ma_int64)framesSeeked / (float)sampleRate; - return result; -} - -MA_API ma_result ma_data_source_seek_to_second(ma_data_source* pDataSource, float seekPointInSeconds) -{ - ma_uint64 frameIndex; - ma_uint32 sampleRate; - ma_result result; - - if (pDataSource == NULL) { - return MA_INVALID_ARGS; - } - - result = ma_data_source_get_data_format(pDataSource, NULL, NULL, &sampleRate, NULL, 0); - if (result != MA_SUCCESS) { - return result; - } - - /* We need PCM frames instead of seconds */ - frameIndex = (ma_uint64)(seekPointInSeconds * sampleRate); - - return ma_data_source_seek_to_pcm_frame(pDataSource, frameIndex); -} - -MA_API ma_result ma_data_source_get_data_format(ma_data_source* pDataSource, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate, ma_channel* pChannelMap, size_t channelMapCap) -{ - ma_data_source_base* pDataSourceBase = (ma_data_source_base*)pDataSource; - ma_result result; - ma_format format; - ma_uint32 channels; - ma_uint32 sampleRate; - - /* Initialize to defaults for safety just in case the data source does not implement this callback. */ - if (pFormat != NULL) { - *pFormat = ma_format_unknown; - } - if (pChannels != NULL) { - *pChannels = 0; - } - if (pSampleRate != NULL) { - *pSampleRate = 0; - } - if (pChannelMap != NULL) { - MA_ZERO_MEMORY(pChannelMap, sizeof(*pChannelMap) * channelMapCap); - } - - if (pDataSourceBase == NULL) { - return MA_INVALID_ARGS; - } - - MA_ASSERT(pDataSourceBase->vtable != NULL); - - if (pDataSourceBase->vtable->onGetDataFormat == NULL) { - return MA_NOT_IMPLEMENTED; - } - - result = pDataSourceBase->vtable->onGetDataFormat(pDataSource, &format, &channels, &sampleRate, pChannelMap, channelMapCap); - if (result != MA_SUCCESS) { - return result; - } - - if (pFormat != NULL) { - *pFormat = format; - } - if (pChannels != NULL) { - *pChannels = channels; - } - if (pSampleRate != NULL) { - *pSampleRate = sampleRate; - } - - /* Channel map was passed in directly to the callback. This is safe due to the channelMapCap parameter. */ - - return MA_SUCCESS; -} - -MA_API ma_result ma_data_source_get_cursor_in_pcm_frames(ma_data_source* pDataSource, ma_uint64* pCursor) -{ - ma_data_source_base* pDataSourceBase = (ma_data_source_base*)pDataSource; - ma_result result; - ma_uint64 cursor; - - if (pCursor == NULL) { - return MA_INVALID_ARGS; - } - - *pCursor = 0; - - if (pDataSourceBase == NULL) { - return MA_SUCCESS; - } - - MA_ASSERT(pDataSourceBase->vtable != NULL); - - if (pDataSourceBase->vtable->onGetCursor == NULL) { - return MA_NOT_IMPLEMENTED; - } - - result = pDataSourceBase->vtable->onGetCursor(pDataSourceBase, &cursor); - if (result != MA_SUCCESS) { - return result; - } - - /* The cursor needs to be made relative to the start of the range. */ - if (cursor < pDataSourceBase->rangeBegInFrames) { /* Safety check so we don't return some huge number. */ - *pCursor = 0; - } else { - *pCursor = cursor - pDataSourceBase->rangeBegInFrames; - } - - return MA_SUCCESS; -} - -MA_API ma_result ma_data_source_get_length_in_pcm_frames(ma_data_source* pDataSource, ma_uint64* pLength) -{ - ma_data_source_base* pDataSourceBase = (ma_data_source_base*)pDataSource; - - if (pLength == NULL) { - return MA_INVALID_ARGS; - } - - *pLength = 0; - - if (pDataSourceBase == NULL) { - return MA_INVALID_ARGS; - } - - MA_ASSERT(pDataSourceBase->vtable != NULL); - - /* - If we have a range defined we'll use that to determine the length. This is one of rare times - where we'll actually trust the caller. If they've set the range, I think it's mostly safe to - assume they've set it based on some higher level knowledge of the structure of the sound bank. - */ - if (pDataSourceBase->rangeEndInFrames != ~((ma_uint64)0)) { - *pLength = pDataSourceBase->rangeEndInFrames - pDataSourceBase->rangeBegInFrames; - return MA_SUCCESS; - } - - /* - Getting here means a range is not defined so we'll need to get the data source itself to tell - us the length. - */ - if (pDataSourceBase->vtable->onGetLength == NULL) { - return MA_NOT_IMPLEMENTED; - } - - return pDataSourceBase->vtable->onGetLength(pDataSource, pLength); -} - -MA_API ma_result ma_data_source_get_cursor_in_seconds(ma_data_source* pDataSource, float* pCursor) -{ - ma_result result; - ma_uint64 cursorInPCMFrames; - ma_uint32 sampleRate; - - if (pCursor == NULL) { - return MA_INVALID_ARGS; - } - - *pCursor = 0; - - result = ma_data_source_get_cursor_in_pcm_frames(pDataSource, &cursorInPCMFrames); - if (result != MA_SUCCESS) { - return result; - } - - result = ma_data_source_get_data_format(pDataSource, NULL, NULL, &sampleRate, NULL, 0); - if (result != MA_SUCCESS) { - return result; - } - - /* VC6 does not support division of unsigned 64-bit integers with floating point numbers. Need to use a signed number. This shouldn't effect anything in practice. */ - *pCursor = (ma_int64)cursorInPCMFrames / (float)sampleRate; - - return MA_SUCCESS; -} - -MA_API ma_result ma_data_source_get_length_in_seconds(ma_data_source* pDataSource, float* pLength) -{ - ma_result result; - ma_uint64 lengthInPCMFrames; - ma_uint32 sampleRate; - - if (pLength == NULL) { - return MA_INVALID_ARGS; - } - - *pLength = 0; - - result = ma_data_source_get_length_in_pcm_frames(pDataSource, &lengthInPCMFrames); - if (result != MA_SUCCESS) { - return result; - } - - result = ma_data_source_get_data_format(pDataSource, NULL, NULL, &sampleRate, NULL, 0); - if (result != MA_SUCCESS) { - return result; - } - - /* VC6 does not support division of unsigned 64-bit integers with floating point numbers. Need to use a signed number. This shouldn't effect anything in practice. */ - *pLength = (ma_int64)lengthInPCMFrames / (float)sampleRate; - - return MA_SUCCESS; -} - -MA_API ma_result ma_data_source_set_looping(ma_data_source* pDataSource, ma_bool32 isLooping) -{ - ma_data_source_base* pDataSourceBase = (ma_data_source_base*)pDataSource; - - if (pDataSource == NULL) { - return MA_INVALID_ARGS; - } - - ma_atomic_exchange_32(&pDataSourceBase->isLooping, isLooping); - - MA_ASSERT(pDataSourceBase->vtable != NULL); - - /* If there's no callback for this just treat it as a successful no-op. */ - if (pDataSourceBase->vtable->onSetLooping == NULL) { - return MA_SUCCESS; - } - - return pDataSourceBase->vtable->onSetLooping(pDataSource, isLooping); -} - -MA_API ma_bool32 ma_data_source_is_looping(const ma_data_source* pDataSource) -{ - const ma_data_source_base* pDataSourceBase = (const ma_data_source_base*)pDataSource; - - if (pDataSource == NULL) { - return MA_FALSE; - } - - return ma_atomic_load_32(&pDataSourceBase->isLooping); -} - -MA_API ma_result ma_data_source_set_range_in_pcm_frames(ma_data_source* pDataSource, ma_uint64 rangeBegInFrames, ma_uint64 rangeEndInFrames) -{ - ma_data_source_base* pDataSourceBase = (ma_data_source_base*)pDataSource; - ma_result result; - ma_uint64 relativeCursor; - ma_uint64 absoluteCursor; - ma_bool32 doSeekAdjustment = MA_FALSE; - - if (pDataSource == NULL) { - return MA_INVALID_ARGS; - } - - if (rangeEndInFrames < rangeBegInFrames) { - return MA_INVALID_ARGS; /* The end of the range must come after the beginning. */ - } - - /* - We may need to adjust the position of the cursor to ensure it's clamped to the range. Grab it now - so we can calculate its absolute position before we change the range. - */ - result = ma_data_source_get_cursor_in_pcm_frames(pDataSource, &relativeCursor); - if (result == MA_SUCCESS) { - doSeekAdjustment = MA_TRUE; - absoluteCursor = relativeCursor + pDataSourceBase->rangeBegInFrames; - } else { - /* - We couldn't get the position of the cursor. It probably means the data source has no notion - of a cursor. We'll just leave it at position 0. Don't treat this as an error. - */ - doSeekAdjustment = MA_FALSE; - relativeCursor = 0; - absoluteCursor = 0; - } - - pDataSourceBase->rangeBegInFrames = rangeBegInFrames; - pDataSourceBase->rangeEndInFrames = rangeEndInFrames; - - /* - The commented out logic below was intended to maintain loop points in response to a change in the - range. However, this is not useful because it results in the sound breaking when you move the range - outside of the old loop points. I'm simplifying this by simply resetting the loop points. The - caller is expected to update their loop points if they change the range. - - In practice this should be mostly a non-issue because the majority of the time the range will be - set once right after initialization. - */ - pDataSourceBase->loopBegInFrames = 0; - pDataSourceBase->loopEndInFrames = ~((ma_uint64)0); - - - /* - Seek to within range. Note that our seek positions here are relative to the new range. We don't want - to do this if we failed to retrieve the cursor earlier on because it probably means the data source - has no notion of a cursor. In practice the seek would probably fail (which we silently ignore), but - I'm just not even going to attempt it. - */ - if (doSeekAdjustment) { - if (absoluteCursor < rangeBegInFrames) { - ma_data_source_seek_to_pcm_frame(pDataSource, 0); - } else if (absoluteCursor > rangeEndInFrames) { - ma_data_source_seek_to_pcm_frame(pDataSource, rangeEndInFrames - rangeBegInFrames); - } - } - - return MA_SUCCESS; -} - -MA_API void ma_data_source_get_range_in_pcm_frames(const ma_data_source* pDataSource, ma_uint64* pRangeBegInFrames, ma_uint64* pRangeEndInFrames) -{ - const ma_data_source_base* pDataSourceBase = (const ma_data_source_base*)pDataSource; - - if (pRangeBegInFrames != NULL) { - *pRangeBegInFrames = 0; - } - if (pRangeEndInFrames != NULL) { - *pRangeEndInFrames = 0; - } - - if (pDataSource == NULL) { - return; - } - - if (pRangeBegInFrames != NULL) { - *pRangeBegInFrames = pDataSourceBase->rangeBegInFrames; - } - - if (pRangeEndInFrames != NULL) { - *pRangeEndInFrames = pDataSourceBase->rangeEndInFrames; - } -} - -MA_API ma_result ma_data_source_set_loop_point_in_pcm_frames(ma_data_source* pDataSource, ma_uint64 loopBegInFrames, ma_uint64 loopEndInFrames) -{ - ma_data_source_base* pDataSourceBase = (ma_data_source_base*)pDataSource; - - if (pDataSource == NULL) { - return MA_INVALID_ARGS; - } - - if (loopEndInFrames < loopBegInFrames) { - return MA_INVALID_ARGS; /* The end of the loop point must come after the beginning. */ - } - - if (loopEndInFrames > pDataSourceBase->rangeEndInFrames && loopEndInFrames != ~((ma_uint64)0)) { - return MA_INVALID_ARGS; /* The end of the loop point must not go beyond the range. */ - } - - pDataSourceBase->loopBegInFrames = loopBegInFrames; - pDataSourceBase->loopEndInFrames = loopEndInFrames; - - /* The end cannot exceed the range. */ - if (pDataSourceBase->loopEndInFrames > (pDataSourceBase->rangeEndInFrames - pDataSourceBase->rangeBegInFrames) && pDataSourceBase->loopEndInFrames != ~((ma_uint64)0)) { - pDataSourceBase->loopEndInFrames = (pDataSourceBase->rangeEndInFrames - pDataSourceBase->rangeBegInFrames); - } - - return MA_SUCCESS; -} - -MA_API void ma_data_source_get_loop_point_in_pcm_frames(const ma_data_source* pDataSource, ma_uint64* pLoopBegInFrames, ma_uint64* pLoopEndInFrames) -{ - const ma_data_source_base* pDataSourceBase = (const ma_data_source_base*)pDataSource; - - if (pLoopBegInFrames != NULL) { - *pLoopBegInFrames = 0; - } - if (pLoopEndInFrames != NULL) { - *pLoopEndInFrames = 0; - } - - if (pDataSource == NULL) { - return; - } - - if (pLoopBegInFrames != NULL) { - *pLoopBegInFrames = pDataSourceBase->loopBegInFrames; - } - - if (pLoopEndInFrames != NULL) { - *pLoopEndInFrames = pDataSourceBase->loopEndInFrames; - } -} - -MA_API ma_result ma_data_source_set_current(ma_data_source* pDataSource, ma_data_source* pCurrentDataSource) -{ - ma_data_source_base* pDataSourceBase = (ma_data_source_base*)pDataSource; - - if (pDataSource == NULL) { - return MA_INVALID_ARGS; - } - - pDataSourceBase->pCurrent = pCurrentDataSource; - - return MA_SUCCESS; -} - -MA_API ma_data_source* ma_data_source_get_current(const ma_data_source* pDataSource) -{ - const ma_data_source_base* pDataSourceBase = (const ma_data_source_base*)pDataSource; - - if (pDataSource == NULL) { - return NULL; - } - - return pDataSourceBase->pCurrent; -} - -MA_API ma_result ma_data_source_set_next(ma_data_source* pDataSource, ma_data_source* pNextDataSource) -{ - ma_data_source_base* pDataSourceBase = (ma_data_source_base*)pDataSource; - - if (pDataSource == NULL) { - return MA_INVALID_ARGS; - } - - pDataSourceBase->pNext = pNextDataSource; - - return MA_SUCCESS; -} - -MA_API ma_data_source* ma_data_source_get_next(const ma_data_source* pDataSource) -{ - const ma_data_source_base* pDataSourceBase = (const ma_data_source_base*)pDataSource; - - if (pDataSource == NULL) { - return NULL; - } - - return pDataSourceBase->pNext; -} - -MA_API ma_result ma_data_source_set_next_callback(ma_data_source* pDataSource, ma_data_source_get_next_proc onGetNext) -{ - ma_data_source_base* pDataSourceBase = (ma_data_source_base*)pDataSource; - - if (pDataSource == NULL) { - return MA_INVALID_ARGS; - } - - pDataSourceBase->onGetNext = onGetNext; - - return MA_SUCCESS; -} - -MA_API ma_data_source_get_next_proc ma_data_source_get_next_callback(const ma_data_source* pDataSource) -{ - const ma_data_source_base* pDataSourceBase = (const ma_data_source_base*)pDataSource; - - if (pDataSource == NULL) { - return NULL; - } - - return pDataSourceBase->onGetNext; -} - - -static ma_result ma_audio_buffer_ref__data_source_on_read(ma_data_source* pDataSource, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead) -{ - ma_audio_buffer_ref* pAudioBufferRef = (ma_audio_buffer_ref*)pDataSource; - ma_uint64 framesRead = ma_audio_buffer_ref_read_pcm_frames(pAudioBufferRef, pFramesOut, frameCount, MA_FALSE); - - if (pFramesRead != NULL) { - *pFramesRead = framesRead; - } - - if (framesRead < frameCount || framesRead == 0) { - return MA_AT_END; - } - - return MA_SUCCESS; -} - -static ma_result ma_audio_buffer_ref__data_source_on_seek(ma_data_source* pDataSource, ma_uint64 frameIndex) -{ - return ma_audio_buffer_ref_seek_to_pcm_frame((ma_audio_buffer_ref*)pDataSource, frameIndex); -} - -static ma_result ma_audio_buffer_ref__data_source_on_get_data_format(ma_data_source* pDataSource, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate, ma_channel* pChannelMap, size_t channelMapCap) -{ - ma_audio_buffer_ref* pAudioBufferRef = (ma_audio_buffer_ref*)pDataSource; - - *pFormat = pAudioBufferRef->format; - *pChannels = pAudioBufferRef->channels; - *pSampleRate = pAudioBufferRef->sampleRate; - ma_channel_map_init_standard(ma_standard_channel_map_default, pChannelMap, channelMapCap, pAudioBufferRef->channels); - - return MA_SUCCESS; -} - -static ma_result ma_audio_buffer_ref__data_source_on_get_cursor(ma_data_source* pDataSource, ma_uint64* pCursor) -{ - ma_audio_buffer_ref* pAudioBufferRef = (ma_audio_buffer_ref*)pDataSource; - - *pCursor = pAudioBufferRef->cursor; - - return MA_SUCCESS; -} - -static ma_result ma_audio_buffer_ref__data_source_on_get_length(ma_data_source* pDataSource, ma_uint64* pLength) -{ - ma_audio_buffer_ref* pAudioBufferRef = (ma_audio_buffer_ref*)pDataSource; - - *pLength = pAudioBufferRef->sizeInFrames; - - return MA_SUCCESS; -} - -static ma_data_source_vtable g_ma_audio_buffer_ref_data_source_vtable = -{ - ma_audio_buffer_ref__data_source_on_read, - ma_audio_buffer_ref__data_source_on_seek, - ma_audio_buffer_ref__data_source_on_get_data_format, - ma_audio_buffer_ref__data_source_on_get_cursor, - ma_audio_buffer_ref__data_source_on_get_length, - NULL, /* onSetLooping */ - 0 -}; - -MA_API ma_result ma_audio_buffer_ref_init(ma_format format, ma_uint32 channels, const void* pData, ma_uint64 sizeInFrames, ma_audio_buffer_ref* pAudioBufferRef) -{ - ma_result result; - ma_data_source_config dataSourceConfig; - - if (pAudioBufferRef == NULL) { - return MA_INVALID_ARGS; - } - - MA_ZERO_OBJECT(pAudioBufferRef); - - dataSourceConfig = ma_data_source_config_init(); - dataSourceConfig.vtable = &g_ma_audio_buffer_ref_data_source_vtable; - - result = ma_data_source_init(&dataSourceConfig, &pAudioBufferRef->ds); - if (result != MA_SUCCESS) { - return result; - } - - pAudioBufferRef->format = format; - pAudioBufferRef->channels = channels; - pAudioBufferRef->sampleRate = 0; /* TODO: Version 0.12. Set this to sampleRate. */ - pAudioBufferRef->cursor = 0; - pAudioBufferRef->sizeInFrames = sizeInFrames; - pAudioBufferRef->pData = pData; - - return MA_SUCCESS; -} - -MA_API void ma_audio_buffer_ref_uninit(ma_audio_buffer_ref* pAudioBufferRef) -{ - if (pAudioBufferRef == NULL) { - return; - } - - ma_data_source_uninit(&pAudioBufferRef->ds); -} - -MA_API ma_result ma_audio_buffer_ref_set_data(ma_audio_buffer_ref* pAudioBufferRef, const void* pData, ma_uint64 sizeInFrames) -{ - if (pAudioBufferRef == NULL) { - return MA_INVALID_ARGS; - } - - pAudioBufferRef->cursor = 0; - pAudioBufferRef->sizeInFrames = sizeInFrames; - pAudioBufferRef->pData = pData; - - return MA_SUCCESS; -} - -MA_API ma_uint64 ma_audio_buffer_ref_read_pcm_frames(ma_audio_buffer_ref* pAudioBufferRef, void* pFramesOut, ma_uint64 frameCount, ma_bool32 loop) -{ - ma_uint64 totalFramesRead = 0; - - if (pAudioBufferRef == NULL) { - return 0; - } - - if (frameCount == 0) { - return 0; - } - - while (totalFramesRead < frameCount) { - ma_uint64 framesAvailable = pAudioBufferRef->sizeInFrames - pAudioBufferRef->cursor; - ma_uint64 framesRemaining = frameCount - totalFramesRead; - ma_uint64 framesToRead; - - framesToRead = framesRemaining; - if (framesToRead > framesAvailable) { - framesToRead = framesAvailable; - } - - if (pFramesOut != NULL) { - ma_copy_pcm_frames(ma_offset_ptr(pFramesOut, totalFramesRead * ma_get_bytes_per_frame(pAudioBufferRef->format, pAudioBufferRef->channels)), ma_offset_ptr(pAudioBufferRef->pData, pAudioBufferRef->cursor * ma_get_bytes_per_frame(pAudioBufferRef->format, pAudioBufferRef->channels)), framesToRead, pAudioBufferRef->format, pAudioBufferRef->channels); - } - - totalFramesRead += framesToRead; - - pAudioBufferRef->cursor += framesToRead; - if (pAudioBufferRef->cursor == pAudioBufferRef->sizeInFrames) { - if (loop) { - pAudioBufferRef->cursor = 0; - } else { - break; /* We've reached the end and we're not looping. Done. */ - } - } - - MA_ASSERT(pAudioBufferRef->cursor < pAudioBufferRef->sizeInFrames); - } - - return totalFramesRead; -} - -MA_API ma_result ma_audio_buffer_ref_seek_to_pcm_frame(ma_audio_buffer_ref* pAudioBufferRef, ma_uint64 frameIndex) -{ - if (pAudioBufferRef == NULL) { - return MA_INVALID_ARGS; - } - - if (frameIndex > pAudioBufferRef->sizeInFrames) { - return MA_INVALID_ARGS; - } - - pAudioBufferRef->cursor = (size_t)frameIndex; - - return MA_SUCCESS; -} - -MA_API ma_result ma_audio_buffer_ref_map(ma_audio_buffer_ref* pAudioBufferRef, void** ppFramesOut, ma_uint64* pFrameCount) -{ - ma_uint64 framesAvailable; - ma_uint64 frameCount = 0; - - if (ppFramesOut != NULL) { - *ppFramesOut = NULL; /* Safety. */ - } - - if (pFrameCount != NULL) { - frameCount = *pFrameCount; - *pFrameCount = 0; /* Safety. */ - } - - if (pAudioBufferRef == NULL || ppFramesOut == NULL || pFrameCount == NULL) { - return MA_INVALID_ARGS; - } - - framesAvailable = pAudioBufferRef->sizeInFrames - pAudioBufferRef->cursor; - if (frameCount > framesAvailable) { - frameCount = framesAvailable; - } - - *ppFramesOut = ma_offset_ptr(pAudioBufferRef->pData, pAudioBufferRef->cursor * ma_get_bytes_per_frame(pAudioBufferRef->format, pAudioBufferRef->channels)); - *pFrameCount = frameCount; - - return MA_SUCCESS; -} - -MA_API ma_result ma_audio_buffer_ref_unmap(ma_audio_buffer_ref* pAudioBufferRef, ma_uint64 frameCount) -{ - ma_uint64 framesAvailable; - - if (pAudioBufferRef == NULL) { - return MA_INVALID_ARGS; - } - - framesAvailable = pAudioBufferRef->sizeInFrames - pAudioBufferRef->cursor; - if (frameCount > framesAvailable) { - return MA_INVALID_ARGS; /* The frame count was too big. This should never happen in an unmapping. Need to make sure the caller is aware of this. */ - } - - pAudioBufferRef->cursor += frameCount; - - if (pAudioBufferRef->cursor == pAudioBufferRef->sizeInFrames) { - return MA_AT_END; /* Successful. Need to tell the caller that the end has been reached so that it can loop if desired. */ - } else { - return MA_SUCCESS; - } -} - -MA_API ma_bool32 ma_audio_buffer_ref_at_end(const ma_audio_buffer_ref* pAudioBufferRef) -{ - if (pAudioBufferRef == NULL) { - return MA_FALSE; - } - - return pAudioBufferRef->cursor == pAudioBufferRef->sizeInFrames; -} - -MA_API ma_result ma_audio_buffer_ref_get_cursor_in_pcm_frames(const ma_audio_buffer_ref* pAudioBufferRef, ma_uint64* pCursor) -{ - if (pCursor == NULL) { - return MA_INVALID_ARGS; - } - - *pCursor = 0; - - if (pAudioBufferRef == NULL) { - return MA_INVALID_ARGS; - } - - *pCursor = pAudioBufferRef->cursor; - - return MA_SUCCESS; -} - -MA_API ma_result ma_audio_buffer_ref_get_length_in_pcm_frames(const ma_audio_buffer_ref* pAudioBufferRef, ma_uint64* pLength) -{ - if (pLength == NULL) { - return MA_INVALID_ARGS; - } - - *pLength = 0; - - if (pAudioBufferRef == NULL) { - return MA_INVALID_ARGS; - } - - *pLength = pAudioBufferRef->sizeInFrames; - - return MA_SUCCESS; -} - -MA_API ma_result ma_audio_buffer_ref_get_available_frames(const ma_audio_buffer_ref* pAudioBufferRef, ma_uint64* pAvailableFrames) -{ - if (pAvailableFrames == NULL) { - return MA_INVALID_ARGS; - } - - *pAvailableFrames = 0; - - if (pAudioBufferRef == NULL) { - return MA_INVALID_ARGS; - } - - if (pAudioBufferRef->sizeInFrames <= pAudioBufferRef->cursor) { - *pAvailableFrames = 0; - } else { - *pAvailableFrames = pAudioBufferRef->sizeInFrames - pAudioBufferRef->cursor; - } - - return MA_SUCCESS; -} - - - - -MA_API ma_audio_buffer_config ma_audio_buffer_config_init(ma_format format, ma_uint32 channels, ma_uint64 sizeInFrames, const void* pData, const ma_allocation_callbacks* pAllocationCallbacks) -{ - ma_audio_buffer_config config; - - MA_ZERO_OBJECT(&config); - config.format = format; - config.channels = channels; - config.sampleRate = 0; /* TODO: Version 0.12. Set this to sampleRate. */ - config.sizeInFrames = sizeInFrames; - config.pData = pData; - ma_allocation_callbacks_init_copy(&config.allocationCallbacks, pAllocationCallbacks); - - return config; -} - -static ma_result ma_audio_buffer_init_ex(const ma_audio_buffer_config* pConfig, ma_bool32 doCopy, ma_audio_buffer* pAudioBuffer) -{ - ma_result result; - - if (pAudioBuffer == NULL) { - return MA_INVALID_ARGS; - } - - MA_ZERO_MEMORY(pAudioBuffer, sizeof(*pAudioBuffer) - sizeof(pAudioBuffer->_pExtraData)); /* Safety. Don't overwrite the extra data. */ - - if (pConfig == NULL) { - return MA_INVALID_ARGS; - } - - if (pConfig->sizeInFrames == 0) { - return MA_INVALID_ARGS; /* Not allowing buffer sizes of 0 frames. */ - } - - result = ma_audio_buffer_ref_init(pConfig->format, pConfig->channels, NULL, 0, &pAudioBuffer->ref); - if (result != MA_SUCCESS) { - return result; - } - - /* TODO: Version 0.12. Set this in ma_audio_buffer_ref_init() instead of here. */ - pAudioBuffer->ref.sampleRate = pConfig->sampleRate; - - ma_allocation_callbacks_init_copy(&pAudioBuffer->allocationCallbacks, &pConfig->allocationCallbacks); - - if (doCopy) { - ma_uint64 allocationSizeInBytes; - void* pData; - - allocationSizeInBytes = pConfig->sizeInFrames * ma_get_bytes_per_frame(pConfig->format, pConfig->channels); - if (allocationSizeInBytes > MA_SIZE_MAX) { - return MA_OUT_OF_MEMORY; /* Too big. */ - } - - pData = ma_malloc((size_t)allocationSizeInBytes, &pAudioBuffer->allocationCallbacks); /* Safe cast to size_t. */ - if (pData == NULL) { - return MA_OUT_OF_MEMORY; - } - - if (pConfig->pData != NULL) { - ma_copy_pcm_frames(pData, pConfig->pData, pConfig->sizeInFrames, pConfig->format, pConfig->channels); - } else { - ma_silence_pcm_frames(pData, pConfig->sizeInFrames, pConfig->format, pConfig->channels); - } - - ma_audio_buffer_ref_set_data(&pAudioBuffer->ref, pData, pConfig->sizeInFrames); - pAudioBuffer->ownsData = MA_TRUE; - } else { - ma_audio_buffer_ref_set_data(&pAudioBuffer->ref, pConfig->pData, pConfig->sizeInFrames); - pAudioBuffer->ownsData = MA_FALSE; - } - - return MA_SUCCESS; -} - -static void ma_audio_buffer_uninit_ex(ma_audio_buffer* pAudioBuffer, ma_bool32 doFree) -{ - if (pAudioBuffer == NULL) { - return; - } - - if (pAudioBuffer->ownsData && pAudioBuffer->ref.pData != &pAudioBuffer->_pExtraData[0]) { - ma_free((void*)pAudioBuffer->ref.pData, &pAudioBuffer->allocationCallbacks); /* Naugty const cast, but OK in this case since we've guarded it with the ownsData check. */ - } - - if (doFree) { - ma_free(pAudioBuffer, &pAudioBuffer->allocationCallbacks); - } - - ma_audio_buffer_ref_uninit(&pAudioBuffer->ref); -} - -MA_API ma_result ma_audio_buffer_init(const ma_audio_buffer_config* pConfig, ma_audio_buffer* pAudioBuffer) -{ - return ma_audio_buffer_init_ex(pConfig, MA_FALSE, pAudioBuffer); -} - -MA_API ma_result ma_audio_buffer_init_copy(const ma_audio_buffer_config* pConfig, ma_audio_buffer* pAudioBuffer) -{ - return ma_audio_buffer_init_ex(pConfig, MA_TRUE, pAudioBuffer); -} - -MA_API ma_result ma_audio_buffer_alloc_and_init(const ma_audio_buffer_config* pConfig, ma_audio_buffer** ppAudioBuffer) -{ - ma_result result; - ma_audio_buffer* pAudioBuffer; - ma_audio_buffer_config innerConfig; /* We'll be making some changes to the config, so need to make a copy. */ - ma_uint64 allocationSizeInBytes; - - if (ppAudioBuffer == NULL) { - return MA_INVALID_ARGS; - } - - *ppAudioBuffer = NULL; /* Safety. */ - - if (pConfig == NULL) { - return MA_INVALID_ARGS; - } - - innerConfig = *pConfig; - ma_allocation_callbacks_init_copy(&innerConfig.allocationCallbacks, &pConfig->allocationCallbacks); - - allocationSizeInBytes = sizeof(*pAudioBuffer) - sizeof(pAudioBuffer->_pExtraData) + (pConfig->sizeInFrames * ma_get_bytes_per_frame(pConfig->format, pConfig->channels)); - if (allocationSizeInBytes > MA_SIZE_MAX) { - return MA_OUT_OF_MEMORY; /* Too big. */ - } - - pAudioBuffer = (ma_audio_buffer*)ma_malloc((size_t)allocationSizeInBytes, &innerConfig.allocationCallbacks); /* Safe cast to size_t. */ - if (pAudioBuffer == NULL) { - return MA_OUT_OF_MEMORY; - } - - if (pConfig->pData != NULL) { - ma_copy_pcm_frames(&pAudioBuffer->_pExtraData[0], pConfig->pData, pConfig->sizeInFrames, pConfig->format, pConfig->channels); - } else { - ma_silence_pcm_frames(&pAudioBuffer->_pExtraData[0], pConfig->sizeInFrames, pConfig->format, pConfig->channels); - } - - innerConfig.pData = &pAudioBuffer->_pExtraData[0]; - - result = ma_audio_buffer_init_ex(&innerConfig, MA_FALSE, pAudioBuffer); - if (result != MA_SUCCESS) { - ma_free(pAudioBuffer, &innerConfig.allocationCallbacks); - return result; - } - - *ppAudioBuffer = pAudioBuffer; - - return MA_SUCCESS; -} - -MA_API void ma_audio_buffer_uninit(ma_audio_buffer* pAudioBuffer) -{ - ma_audio_buffer_uninit_ex(pAudioBuffer, MA_FALSE); -} - -MA_API void ma_audio_buffer_uninit_and_free(ma_audio_buffer* pAudioBuffer) -{ - ma_audio_buffer_uninit_ex(pAudioBuffer, MA_TRUE); -} - -MA_API ma_uint64 ma_audio_buffer_read_pcm_frames(ma_audio_buffer* pAudioBuffer, void* pFramesOut, ma_uint64 frameCount, ma_bool32 loop) -{ - if (pAudioBuffer == NULL) { - return 0; - } - - return ma_audio_buffer_ref_read_pcm_frames(&pAudioBuffer->ref, pFramesOut, frameCount, loop); -} - -MA_API ma_result ma_audio_buffer_seek_to_pcm_frame(ma_audio_buffer* pAudioBuffer, ma_uint64 frameIndex) -{ - if (pAudioBuffer == NULL) { - return MA_INVALID_ARGS; - } - - return ma_audio_buffer_ref_seek_to_pcm_frame(&pAudioBuffer->ref, frameIndex); -} - -MA_API ma_result ma_audio_buffer_map(ma_audio_buffer* pAudioBuffer, void** ppFramesOut, ma_uint64* pFrameCount) -{ - if (ppFramesOut != NULL) { - *ppFramesOut = NULL; /* Safety. */ - } - - if (pAudioBuffer == NULL) { - if (pFrameCount != NULL) { - *pFrameCount = 0; - } - - return MA_INVALID_ARGS; - } - - return ma_audio_buffer_ref_map(&pAudioBuffer->ref, ppFramesOut, pFrameCount); -} - -MA_API ma_result ma_audio_buffer_unmap(ma_audio_buffer* pAudioBuffer, ma_uint64 frameCount) -{ - if (pAudioBuffer == NULL) { - return MA_INVALID_ARGS; - } - - return ma_audio_buffer_ref_unmap(&pAudioBuffer->ref, frameCount); -} - -MA_API ma_bool32 ma_audio_buffer_at_end(const ma_audio_buffer* pAudioBuffer) -{ - if (pAudioBuffer == NULL) { - return MA_FALSE; - } - - return ma_audio_buffer_ref_at_end(&pAudioBuffer->ref); -} - -MA_API ma_result ma_audio_buffer_get_cursor_in_pcm_frames(const ma_audio_buffer* pAudioBuffer, ma_uint64* pCursor) -{ - if (pAudioBuffer == NULL) { - return MA_INVALID_ARGS; - } - - return ma_audio_buffer_ref_get_cursor_in_pcm_frames(&pAudioBuffer->ref, pCursor); -} - -MA_API ma_result ma_audio_buffer_get_length_in_pcm_frames(const ma_audio_buffer* pAudioBuffer, ma_uint64* pLength) -{ - if (pAudioBuffer == NULL) { - return MA_INVALID_ARGS; - } - - return ma_audio_buffer_ref_get_length_in_pcm_frames(&pAudioBuffer->ref, pLength); -} - -MA_API ma_result ma_audio_buffer_get_available_frames(const ma_audio_buffer* pAudioBuffer, ma_uint64* pAvailableFrames) -{ - if (pAvailableFrames == NULL) { - return MA_INVALID_ARGS; - } - - *pAvailableFrames = 0; - - if (pAudioBuffer == NULL) { - return MA_INVALID_ARGS; - } - - return ma_audio_buffer_ref_get_available_frames(&pAudioBuffer->ref, pAvailableFrames); -} - - - - - -MA_API ma_result ma_paged_audio_buffer_data_init(ma_format format, ma_uint32 channels, ma_paged_audio_buffer_data* pData) -{ - if (pData == NULL) { - return MA_INVALID_ARGS; - } - - MA_ZERO_OBJECT(pData); - - pData->format = format; - pData->channels = channels; - pData->pTail = &pData->head; - - return MA_SUCCESS; -} - -MA_API void ma_paged_audio_buffer_data_uninit(ma_paged_audio_buffer_data* pData, const ma_allocation_callbacks* pAllocationCallbacks) -{ - ma_paged_audio_buffer_page* pPage; - - if (pData == NULL) { - return; - } - - /* All pages need to be freed. */ - pPage = (ma_paged_audio_buffer_page*)ma_atomic_load_ptr(&pData->head.pNext); - while (pPage != NULL) { - ma_paged_audio_buffer_page* pNext = (ma_paged_audio_buffer_page*)ma_atomic_load_ptr(&pPage->pNext); - - ma_free(pPage, pAllocationCallbacks); - pPage = pNext; - } -} - -MA_API ma_paged_audio_buffer_page* ma_paged_audio_buffer_data_get_head(ma_paged_audio_buffer_data* pData) -{ - if (pData == NULL) { - return NULL; - } - - return &pData->head; -} - -MA_API ma_paged_audio_buffer_page* ma_paged_audio_buffer_data_get_tail(ma_paged_audio_buffer_data* pData) -{ - if (pData == NULL) { - return NULL; - } - - return pData->pTail; -} - -MA_API ma_result ma_paged_audio_buffer_data_get_length_in_pcm_frames(ma_paged_audio_buffer_data* pData, ma_uint64* pLength) -{ - ma_paged_audio_buffer_page* pPage; - - if (pLength == NULL) { - return MA_INVALID_ARGS; - } - - *pLength = 0; - - if (pData == NULL) { - return MA_INVALID_ARGS; - } - - /* Calculate the length from the linked list. */ - for (pPage = (ma_paged_audio_buffer_page*)ma_atomic_load_ptr(&pData->head.pNext); pPage != NULL; pPage = (ma_paged_audio_buffer_page*)ma_atomic_load_ptr(&pPage->pNext)) { - *pLength += pPage->sizeInFrames; - } - - return MA_SUCCESS; -} - -MA_API ma_result ma_paged_audio_buffer_data_allocate_page(ma_paged_audio_buffer_data* pData, ma_uint64 pageSizeInFrames, const void* pInitialData, const ma_allocation_callbacks* pAllocationCallbacks, ma_paged_audio_buffer_page** ppPage) -{ - ma_paged_audio_buffer_page* pPage; - ma_uint64 allocationSize; - - if (ppPage == NULL) { - return MA_INVALID_ARGS; - } - - *ppPage = NULL; - - if (pData == NULL) { - return MA_INVALID_ARGS; - } - - allocationSize = sizeof(*pPage) + (pageSizeInFrames * ma_get_bytes_per_frame(pData->format, pData->channels)); - if (allocationSize > MA_SIZE_MAX) { - return MA_OUT_OF_MEMORY; /* Too big. */ - } - - pPage = (ma_paged_audio_buffer_page*)ma_malloc((size_t)allocationSize, pAllocationCallbacks); /* Safe cast to size_t. */ - if (pPage == NULL) { - return MA_OUT_OF_MEMORY; - } - - pPage->pNext = NULL; - pPage->sizeInFrames = pageSizeInFrames; - - if (pInitialData != NULL) { - ma_copy_pcm_frames(pPage->pAudioData, pInitialData, pageSizeInFrames, pData->format, pData->channels); - } - - *ppPage = pPage; - - return MA_SUCCESS; -} - -MA_API ma_result ma_paged_audio_buffer_data_free_page(ma_paged_audio_buffer_data* pData, ma_paged_audio_buffer_page* pPage, const ma_allocation_callbacks* pAllocationCallbacks) -{ - if (pData == NULL || pPage == NULL) { - return MA_INVALID_ARGS; - } - - /* It's assumed the page is not attached to the list. */ - ma_free(pPage, pAllocationCallbacks); - - return MA_SUCCESS; -} - -MA_API ma_result ma_paged_audio_buffer_data_append_page(ma_paged_audio_buffer_data* pData, ma_paged_audio_buffer_page* pPage) -{ - if (pData == NULL || pPage == NULL) { - return MA_INVALID_ARGS; - } - - /* This function assumes the page has been filled with audio data by this point. As soon as we append, the page will be available for reading. */ - - /* First thing to do is update the tail. */ - for (;;) { - ma_paged_audio_buffer_page* pOldTail = (ma_paged_audio_buffer_page*)ma_atomic_load_ptr(&pData->pTail); - ma_paged_audio_buffer_page* pNewTail = pPage; - - if (ma_atomic_compare_exchange_weak_ptr((volatile void**)&pData->pTail, (void**)&pOldTail, pNewTail)) { - /* Here is where we append the page to the list. After this, the page is attached to the list and ready to be read from. */ - ma_atomic_exchange_ptr(&pOldTail->pNext, pPage); - break; /* Done. */ - } - } - - return MA_SUCCESS; -} - -MA_API ma_result ma_paged_audio_buffer_data_allocate_and_append_page(ma_paged_audio_buffer_data* pData, ma_uint32 pageSizeInFrames, const void* pInitialData, const ma_allocation_callbacks* pAllocationCallbacks) -{ - ma_result result; - ma_paged_audio_buffer_page* pPage; - - result = ma_paged_audio_buffer_data_allocate_page(pData, pageSizeInFrames, pInitialData, pAllocationCallbacks, &pPage); - if (result != MA_SUCCESS) { - return result; - } - - return ma_paged_audio_buffer_data_append_page(pData, pPage); /* <-- Should never fail. */ -} - - -MA_API ma_paged_audio_buffer_config ma_paged_audio_buffer_config_init(ma_paged_audio_buffer_data* pData) -{ - ma_paged_audio_buffer_config config; - - MA_ZERO_OBJECT(&config); - config.pData = pData; - - return config; -} - - -static ma_result ma_paged_audio_buffer__data_source_on_read(ma_data_source* pDataSource, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead) -{ - return ma_paged_audio_buffer_read_pcm_frames((ma_paged_audio_buffer*)pDataSource, pFramesOut, frameCount, pFramesRead); -} - -static ma_result ma_paged_audio_buffer__data_source_on_seek(ma_data_source* pDataSource, ma_uint64 frameIndex) -{ - return ma_paged_audio_buffer_seek_to_pcm_frame((ma_paged_audio_buffer*)pDataSource, frameIndex); -} - -static ma_result ma_paged_audio_buffer__data_source_on_get_data_format(ma_data_source* pDataSource, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate, ma_channel* pChannelMap, size_t channelMapCap) -{ - ma_paged_audio_buffer* pPagedAudioBuffer = (ma_paged_audio_buffer*)pDataSource; - - *pFormat = pPagedAudioBuffer->pData->format; - *pChannels = pPagedAudioBuffer->pData->channels; - *pSampleRate = 0; /* There is no notion of a sample rate with audio buffers. */ - ma_channel_map_init_standard(ma_standard_channel_map_default, pChannelMap, channelMapCap, pPagedAudioBuffer->pData->channels); - - return MA_SUCCESS; -} - -static ma_result ma_paged_audio_buffer__data_source_on_get_cursor(ma_data_source* pDataSource, ma_uint64* pCursor) -{ - return ma_paged_audio_buffer_get_cursor_in_pcm_frames((ma_paged_audio_buffer*)pDataSource, pCursor); -} - -static ma_result ma_paged_audio_buffer__data_source_on_get_length(ma_data_source* pDataSource, ma_uint64* pLength) -{ - return ma_paged_audio_buffer_get_length_in_pcm_frames((ma_paged_audio_buffer*)pDataSource, pLength); -} - -static ma_data_source_vtable g_ma_paged_audio_buffer_data_source_vtable = -{ - ma_paged_audio_buffer__data_source_on_read, - ma_paged_audio_buffer__data_source_on_seek, - ma_paged_audio_buffer__data_source_on_get_data_format, - ma_paged_audio_buffer__data_source_on_get_cursor, - ma_paged_audio_buffer__data_source_on_get_length, - NULL, /* onSetLooping */ - 0 -}; - -MA_API ma_result ma_paged_audio_buffer_init(const ma_paged_audio_buffer_config* pConfig, ma_paged_audio_buffer* pPagedAudioBuffer) -{ - ma_result result; - ma_data_source_config dataSourceConfig; - - if (pPagedAudioBuffer == NULL) { - return MA_INVALID_ARGS; - } - - MA_ZERO_OBJECT(pPagedAudioBuffer); - - /* A config is required for the format and channel count. */ - if (pConfig == NULL) { - return MA_INVALID_ARGS; - } - - if (pConfig->pData == NULL) { - return MA_INVALID_ARGS; /* No underlying data specified. */ - } - - dataSourceConfig = ma_data_source_config_init(); - dataSourceConfig.vtable = &g_ma_paged_audio_buffer_data_source_vtable; - - result = ma_data_source_init(&dataSourceConfig, &pPagedAudioBuffer->ds); - if (result != MA_SUCCESS) { - return result; - } - - pPagedAudioBuffer->pData = pConfig->pData; - pPagedAudioBuffer->pCurrent = ma_paged_audio_buffer_data_get_head(pConfig->pData); - pPagedAudioBuffer->relativeCursor = 0; - pPagedAudioBuffer->absoluteCursor = 0; - - return MA_SUCCESS; -} - -MA_API void ma_paged_audio_buffer_uninit(ma_paged_audio_buffer* pPagedAudioBuffer) -{ - if (pPagedAudioBuffer == NULL) { - return; - } - - /* Nothing to do. The data needs to be deleted separately. */ -} - -MA_API ma_result ma_paged_audio_buffer_read_pcm_frames(ma_paged_audio_buffer* pPagedAudioBuffer, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead) -{ - ma_result result = MA_SUCCESS; - ma_uint64 totalFramesRead = 0; - ma_format format; - ma_uint32 channels; - - if (pPagedAudioBuffer == NULL) { - return MA_INVALID_ARGS; - } - - format = pPagedAudioBuffer->pData->format; - channels = pPagedAudioBuffer->pData->channels; - - while (totalFramesRead < frameCount) { - /* Read from the current page. The buffer should never be in a state where this is NULL. */ - ma_uint64 framesRemainingInCurrentPage; - ma_uint64 framesRemainingToRead = frameCount - totalFramesRead; - ma_uint64 framesToReadThisIteration; - - MA_ASSERT(pPagedAudioBuffer->pCurrent != NULL); - - framesRemainingInCurrentPage = pPagedAudioBuffer->pCurrent->sizeInFrames - pPagedAudioBuffer->relativeCursor; - - framesToReadThisIteration = ma_min(framesRemainingInCurrentPage, framesRemainingToRead); - ma_copy_pcm_frames(ma_offset_pcm_frames_ptr(pFramesOut, totalFramesRead, format, channels), ma_offset_pcm_frames_ptr(pPagedAudioBuffer->pCurrent->pAudioData, pPagedAudioBuffer->relativeCursor, format, channels), framesToReadThisIteration, format, channels); - totalFramesRead += framesToReadThisIteration; - - pPagedAudioBuffer->absoluteCursor += framesToReadThisIteration; - pPagedAudioBuffer->relativeCursor += framesToReadThisIteration; - - /* Move to the next page if necessary. If there's no more pages, we need to return MA_AT_END. */ - MA_ASSERT(pPagedAudioBuffer->relativeCursor <= pPagedAudioBuffer->pCurrent->sizeInFrames); - - if (pPagedAudioBuffer->relativeCursor == pPagedAudioBuffer->pCurrent->sizeInFrames) { - /* We reached the end of the page. Need to move to the next. If there's no more pages, we're done. */ - ma_paged_audio_buffer_page* pNext = (ma_paged_audio_buffer_page*)ma_atomic_load_ptr(&pPagedAudioBuffer->pCurrent->pNext); - if (pNext == NULL) { - result = MA_AT_END; - break; /* We've reached the end. */ - } else { - pPagedAudioBuffer->pCurrent = pNext; - pPagedAudioBuffer->relativeCursor = 0; - } - } - } - - if (pFramesRead != NULL) { - *pFramesRead = totalFramesRead; - } - - return result; -} - -MA_API ma_result ma_paged_audio_buffer_seek_to_pcm_frame(ma_paged_audio_buffer* pPagedAudioBuffer, ma_uint64 frameIndex) -{ - if (pPagedAudioBuffer == NULL) { - return MA_INVALID_ARGS; - } - - if (frameIndex == pPagedAudioBuffer->absoluteCursor) { - return MA_SUCCESS; /* Nothing to do. */ - } - - if (frameIndex < pPagedAudioBuffer->absoluteCursor) { - /* Moving backwards. Need to move the cursor back to the start, and then move forward. */ - pPagedAudioBuffer->pCurrent = ma_paged_audio_buffer_data_get_head(pPagedAudioBuffer->pData); - pPagedAudioBuffer->absoluteCursor = 0; - pPagedAudioBuffer->relativeCursor = 0; - - /* Fall through to the forward seeking section below. */ - } - - if (frameIndex > pPagedAudioBuffer->absoluteCursor) { - /* Moving forward. */ - ma_paged_audio_buffer_page* pPage; - ma_uint64 runningCursor = 0; - - for (pPage = (ma_paged_audio_buffer_page*)ma_atomic_load_ptr(&ma_paged_audio_buffer_data_get_head(pPagedAudioBuffer->pData)->pNext); pPage != NULL; pPage = (ma_paged_audio_buffer_page*)ma_atomic_load_ptr(&pPage->pNext)) { - ma_uint64 pageRangeBeg = runningCursor; - ma_uint64 pageRangeEnd = pageRangeBeg + pPage->sizeInFrames; - - if (frameIndex >= pageRangeBeg) { - if (frameIndex < pageRangeEnd || (frameIndex == pageRangeEnd && pPage == (ma_paged_audio_buffer_page*)ma_atomic_load_ptr(ma_paged_audio_buffer_data_get_tail(pPagedAudioBuffer->pData)))) { /* A small edge case - allow seeking to the very end of the buffer. */ - /* We found the page. */ - pPagedAudioBuffer->pCurrent = pPage; - pPagedAudioBuffer->absoluteCursor = frameIndex; - pPagedAudioBuffer->relativeCursor = frameIndex - pageRangeBeg; - return MA_SUCCESS; - } - } - - runningCursor = pageRangeEnd; - } - - /* Getting here means we tried seeking too far forward. Don't change any state. */ - return MA_BAD_SEEK; - } - - return MA_SUCCESS; -} - -MA_API ma_result ma_paged_audio_buffer_get_cursor_in_pcm_frames(ma_paged_audio_buffer* pPagedAudioBuffer, ma_uint64* pCursor) -{ - if (pCursor == NULL) { - return MA_INVALID_ARGS; - } - - *pCursor = 0; /* Safety. */ - - if (pPagedAudioBuffer == NULL) { - return MA_INVALID_ARGS; - } - - *pCursor = pPagedAudioBuffer->absoluteCursor; - - return MA_SUCCESS; -} - -MA_API ma_result ma_paged_audio_buffer_get_length_in_pcm_frames(ma_paged_audio_buffer* pPagedAudioBuffer, ma_uint64* pLength) -{ - return ma_paged_audio_buffer_data_get_length_in_pcm_frames(pPagedAudioBuffer->pData, pLength); -} - - - -/************************************************************************************************************************************************************** - -VFS - -**************************************************************************************************************************************************************/ -MA_API ma_result ma_vfs_open(ma_vfs* pVFS, const char* pFilePath, ma_uint32 openMode, ma_vfs_file* pFile) -{ - ma_vfs_callbacks* pCallbacks = (ma_vfs_callbacks*)pVFS; - - if (pFile == NULL) { - return MA_INVALID_ARGS; - } - - *pFile = NULL; - - if (pVFS == NULL || pFilePath == NULL || openMode == 0) { - return MA_INVALID_ARGS; - } - - if (pCallbacks->onOpen == NULL) { - return MA_NOT_IMPLEMENTED; - } - - return pCallbacks->onOpen(pVFS, pFilePath, openMode, pFile); -} - -MA_API ma_result ma_vfs_open_w(ma_vfs* pVFS, const wchar_t* pFilePath, ma_uint32 openMode, ma_vfs_file* pFile) -{ - ma_vfs_callbacks* pCallbacks = (ma_vfs_callbacks*)pVFS; - - if (pFile == NULL) { - return MA_INVALID_ARGS; - } - - *pFile = NULL; - - if (pVFS == NULL || pFilePath == NULL || openMode == 0) { - return MA_INVALID_ARGS; - } - - if (pCallbacks->onOpenW == NULL) { - return MA_NOT_IMPLEMENTED; - } - - return pCallbacks->onOpenW(pVFS, pFilePath, openMode, pFile); -} - -MA_API ma_result ma_vfs_close(ma_vfs* pVFS, ma_vfs_file file) -{ - ma_vfs_callbacks* pCallbacks = (ma_vfs_callbacks*)pVFS; - - if (pVFS == NULL || file == NULL) { - return MA_INVALID_ARGS; - } - - if (pCallbacks->onClose == NULL) { - return MA_NOT_IMPLEMENTED; - } - - return pCallbacks->onClose(pVFS, file); -} - -MA_API ma_result ma_vfs_read(ma_vfs* pVFS, ma_vfs_file file, void* pDst, size_t sizeInBytes, size_t* pBytesRead) -{ - ma_vfs_callbacks* pCallbacks = (ma_vfs_callbacks*)pVFS; - ma_result result; - size_t bytesRead = 0; - - if (pBytesRead != NULL) { - *pBytesRead = 0; - } - - if (pVFS == NULL || file == NULL || pDst == NULL) { - return MA_INVALID_ARGS; - } - - if (pCallbacks->onRead == NULL) { - return MA_NOT_IMPLEMENTED; - } - - result = pCallbacks->onRead(pVFS, file, pDst, sizeInBytes, &bytesRead); - - if (pBytesRead != NULL) { - *pBytesRead = bytesRead; - } - - if (result == MA_SUCCESS && bytesRead == 0 && sizeInBytes > 0) { - result = MA_AT_END; - } - - return result; -} - -MA_API ma_result ma_vfs_write(ma_vfs* pVFS, ma_vfs_file file, const void* pSrc, size_t sizeInBytes, size_t* pBytesWritten) -{ - ma_vfs_callbacks* pCallbacks = (ma_vfs_callbacks*)pVFS; - - if (pBytesWritten != NULL) { - *pBytesWritten = 0; - } - - if (pVFS == NULL || file == NULL || pSrc == NULL) { - return MA_INVALID_ARGS; - } - - if (pCallbacks->onWrite == NULL) { - return MA_NOT_IMPLEMENTED; - } - - return pCallbacks->onWrite(pVFS, file, pSrc, sizeInBytes, pBytesWritten); -} - -MA_API ma_result ma_vfs_seek(ma_vfs* pVFS, ma_vfs_file file, ma_int64 offset, ma_seek_origin origin) -{ - ma_vfs_callbacks* pCallbacks = (ma_vfs_callbacks*)pVFS; - - if (pVFS == NULL || file == NULL) { - return MA_INVALID_ARGS; - } - - if (pCallbacks->onSeek == NULL) { - return MA_NOT_IMPLEMENTED; - } - - return pCallbacks->onSeek(pVFS, file, offset, origin); -} - -MA_API ma_result ma_vfs_tell(ma_vfs* pVFS, ma_vfs_file file, ma_int64* pCursor) -{ - ma_vfs_callbacks* pCallbacks = (ma_vfs_callbacks*)pVFS; - - if (pCursor == NULL) { - return MA_INVALID_ARGS; - } - - *pCursor = 0; - - if (pVFS == NULL || file == NULL) { - return MA_INVALID_ARGS; - } - - if (pCallbacks->onTell == NULL) { - return MA_NOT_IMPLEMENTED; - } - - return pCallbacks->onTell(pVFS, file, pCursor); -} - -MA_API ma_result ma_vfs_info(ma_vfs* pVFS, ma_vfs_file file, ma_file_info* pInfo) -{ - ma_vfs_callbacks* pCallbacks = (ma_vfs_callbacks*)pVFS; - - if (pInfo == NULL) { - return MA_INVALID_ARGS; - } - - MA_ZERO_OBJECT(pInfo); - - if (pVFS == NULL || file == NULL) { - return MA_INVALID_ARGS; - } - - if (pCallbacks->onInfo == NULL) { - return MA_NOT_IMPLEMENTED; - } - - return pCallbacks->onInfo(pVFS, file, pInfo); -} - - -#if !defined(MA_USE_WIN32_FILEIO) && (defined(MA_WIN32) && defined(MA_WIN32_DESKTOP) && !defined(MA_NO_WIN32_FILEIO) && !defined(MA_POSIX)) - #define MA_USE_WIN32_FILEIO -#endif - -#if defined(MA_USE_WIN32_FILEIO) -/* -We need to dynamically load SetFilePointer or SetFilePointerEx because older versions of Windows do -not have the Ex version. We therefore need to do some dynamic branching depending on what's available. - -We load these when we load our first file from the default VFS. It's left open for the life of the -program and is left to the OS to uninitialize when the program terminates. -*/ -typedef DWORD (__stdcall * ma_SetFilePointer_proc)(HANDLE hFile, LONG lDistanceToMove, LONG* lpDistanceToMoveHigh, DWORD dwMoveMethod); -typedef BOOL (__stdcall * ma_SetFilePointerEx_proc)(HANDLE hFile, LARGE_INTEGER liDistanceToMove, LARGE_INTEGER* lpNewFilePointer, DWORD dwMoveMethod); - -static ma_handle hKernel32DLL = NULL; -static ma_SetFilePointer_proc ma_SetFilePointer = NULL; -static ma_SetFilePointerEx_proc ma_SetFilePointerEx = NULL; - -static void ma_win32_fileio_init(void) -{ - if (hKernel32DLL == NULL) { - hKernel32DLL = ma_dlopen(NULL, "kernel32.dll"); - if (hKernel32DLL != NULL) { - ma_SetFilePointer = (ma_SetFilePointer_proc) ma_dlsym(NULL, hKernel32DLL, "SetFilePointer"); - ma_SetFilePointerEx = (ma_SetFilePointerEx_proc)ma_dlsym(NULL, hKernel32DLL, "SetFilePointerEx"); - } - } -} - -static void ma_default_vfs__get_open_settings_win32(ma_uint32 openMode, DWORD* pDesiredAccess, DWORD* pShareMode, DWORD* pCreationDisposition) -{ - *pDesiredAccess = 0; - if ((openMode & MA_OPEN_MODE_READ) != 0) { - *pDesiredAccess |= GENERIC_READ; - } - if ((openMode & MA_OPEN_MODE_WRITE) != 0) { - *pDesiredAccess |= GENERIC_WRITE; - } - - *pShareMode = 0; - if ((openMode & MA_OPEN_MODE_READ) != 0) { - *pShareMode |= FILE_SHARE_READ; - } - - if ((openMode & MA_OPEN_MODE_WRITE) != 0) { - *pCreationDisposition = CREATE_ALWAYS; /* Opening in write mode. Truncate. */ - } else { - *pCreationDisposition = OPEN_EXISTING; /* Opening in read mode. File must exist. */ - } -} - -static ma_result ma_default_vfs_open__win32(ma_vfs* pVFS, const char* pFilePath, ma_uint32 openMode, ma_vfs_file* pFile) -{ - HANDLE hFile; - DWORD dwDesiredAccess; - DWORD dwShareMode; - DWORD dwCreationDisposition; - - (void)pVFS; - - /* Load some Win32 symbols dynamically so we can dynamically check for the existence of SetFilePointerEx. */ - ma_win32_fileio_init(); - - ma_default_vfs__get_open_settings_win32(openMode, &dwDesiredAccess, &dwShareMode, &dwCreationDisposition); - - hFile = CreateFileA(pFilePath, dwDesiredAccess, dwShareMode, NULL, dwCreationDisposition, FILE_ATTRIBUTE_NORMAL, NULL); - if (hFile == INVALID_HANDLE_VALUE) { - return ma_result_from_GetLastError(GetLastError()); - } - - *pFile = hFile; - return MA_SUCCESS; -} - -static ma_result ma_default_vfs_open_w__win32(ma_vfs* pVFS, const wchar_t* pFilePath, ma_uint32 openMode, ma_vfs_file* pFile) -{ - HANDLE hFile; - DWORD dwDesiredAccess; - DWORD dwShareMode; - DWORD dwCreationDisposition; - - (void)pVFS; - - /* Load some Win32 symbols dynamically so we can dynamically check for the existence of SetFilePointerEx. */ - ma_win32_fileio_init(); - - ma_default_vfs__get_open_settings_win32(openMode, &dwDesiredAccess, &dwShareMode, &dwCreationDisposition); - - hFile = CreateFileW(pFilePath, dwDesiredAccess, dwShareMode, NULL, dwCreationDisposition, FILE_ATTRIBUTE_NORMAL, NULL); - if (hFile == INVALID_HANDLE_VALUE) { - return ma_result_from_GetLastError(GetLastError()); - } - - *pFile = hFile; - return MA_SUCCESS; -} - -static ma_result ma_default_vfs_close__win32(ma_vfs* pVFS, ma_vfs_file file) -{ - (void)pVFS; - - if (CloseHandle((HANDLE)file) == 0) { - return ma_result_from_GetLastError(GetLastError()); - } - - return MA_SUCCESS; -} - - -static ma_result ma_default_vfs_read__win32(ma_vfs* pVFS, ma_vfs_file file, void* pDst, size_t sizeInBytes, size_t* pBytesRead) -{ - ma_result result = MA_SUCCESS; - size_t totalBytesRead; - - (void)pVFS; - - totalBytesRead = 0; - while (totalBytesRead < sizeInBytes) { - size_t bytesRemaining; - DWORD bytesToRead; - DWORD bytesRead; - BOOL readResult; - - bytesRemaining = sizeInBytes - totalBytesRead; - if (bytesRemaining >= 0xFFFFFFFF) { - bytesToRead = 0xFFFFFFFF; - } else { - bytesToRead = (DWORD)bytesRemaining; - } - - readResult = ReadFile((HANDLE)file, ma_offset_ptr(pDst, totalBytesRead), bytesToRead, &bytesRead, NULL); - if (readResult == 1 && bytesRead == 0) { - result = MA_AT_END; - break; /* EOF */ - } - - totalBytesRead += bytesRead; - - if (bytesRead < bytesToRead) { - break; /* EOF */ - } - - if (readResult == 0) { - result = ma_result_from_GetLastError(GetLastError()); - break; - } - } - - if (pBytesRead != NULL) { - *pBytesRead = totalBytesRead; - } - - return result; -} - -static ma_result ma_default_vfs_write__win32(ma_vfs* pVFS, ma_vfs_file file, const void* pSrc, size_t sizeInBytes, size_t* pBytesWritten) -{ - ma_result result = MA_SUCCESS; - size_t totalBytesWritten; - - (void)pVFS; - - totalBytesWritten = 0; - while (totalBytesWritten < sizeInBytes) { - size_t bytesRemaining; - DWORD bytesToWrite; - DWORD bytesWritten; - BOOL writeResult; - - bytesRemaining = sizeInBytes - totalBytesWritten; - if (bytesRemaining >= 0xFFFFFFFF) { - bytesToWrite = 0xFFFFFFFF; - } else { - bytesToWrite = (DWORD)bytesRemaining; - } - - writeResult = WriteFile((HANDLE)file, ma_offset_ptr(pSrc, totalBytesWritten), bytesToWrite, &bytesWritten, NULL); - totalBytesWritten += bytesWritten; - - if (writeResult == 0) { - result = ma_result_from_GetLastError(GetLastError()); - break; - } - } - - if (pBytesWritten != NULL) { - *pBytesWritten = totalBytesWritten; - } - - return result; -} - - -static ma_result ma_default_vfs_seek__win32(ma_vfs* pVFS, ma_vfs_file file, ma_int64 offset, ma_seek_origin origin) -{ - LARGE_INTEGER liDistanceToMove; - DWORD dwMoveMethod; - BOOL result; - - (void)pVFS; - - liDistanceToMove.QuadPart = offset; - - /* */ if (origin == ma_seek_origin_current) { - dwMoveMethod = FILE_CURRENT; - } else if (origin == ma_seek_origin_end) { - dwMoveMethod = FILE_END; - } else { - dwMoveMethod = FILE_BEGIN; - } - - if (ma_SetFilePointerEx != NULL) { - result = ma_SetFilePointerEx((HANDLE)file, liDistanceToMove, NULL, dwMoveMethod); - } else if (ma_SetFilePointer != NULL) { - /* No SetFilePointerEx() so restrict to 31 bits. */ - if (offset > 0x7FFFFFFF) { - return MA_OUT_OF_RANGE; - } - - result = ma_SetFilePointer((HANDLE)file, (LONG)liDistanceToMove.QuadPart, NULL, dwMoveMethod); - } else { - return MA_NOT_IMPLEMENTED; - } - - if (result == 0) { - return ma_result_from_GetLastError(GetLastError()); - } - - return MA_SUCCESS; -} - -static ma_result ma_default_vfs_tell__win32(ma_vfs* pVFS, ma_vfs_file file, ma_int64* pCursor) -{ - LARGE_INTEGER liZero; - LARGE_INTEGER liTell; - BOOL result; - - (void)pVFS; - - liZero.QuadPart = 0; - - if (ma_SetFilePointerEx != NULL) { - result = ma_SetFilePointerEx((HANDLE)file, liZero, &liTell, FILE_CURRENT); - } else if (ma_SetFilePointer != NULL) { - LONG tell; - - result = ma_SetFilePointer((HANDLE)file, (LONG)liZero.QuadPart, &tell, FILE_CURRENT); - liTell.QuadPart = tell; - } else { - return MA_NOT_IMPLEMENTED; - } - - if (result == 0) { - return ma_result_from_GetLastError(GetLastError()); - } - - if (pCursor != NULL) { - *pCursor = liTell.QuadPart; - } - - return MA_SUCCESS; -} - -static ma_result ma_default_vfs_info__win32(ma_vfs* pVFS, ma_vfs_file file, ma_file_info* pInfo) -{ - BY_HANDLE_FILE_INFORMATION fi; - BOOL result; - - (void)pVFS; - - result = GetFileInformationByHandle((HANDLE)file, &fi); - if (result == 0) { - return ma_result_from_GetLastError(GetLastError()); - } - - pInfo->sizeInBytes = ((ma_uint64)fi.nFileSizeHigh << 32) | ((ma_uint64)fi.nFileSizeLow); - - return MA_SUCCESS; -} -#else -static ma_result ma_default_vfs_open__stdio(ma_vfs* pVFS, const char* pFilePath, ma_uint32 openMode, ma_vfs_file* pFile) -{ - ma_result result; - FILE* pFileStd; - const char* pOpenModeStr; - - MA_ASSERT(pFilePath != NULL); - MA_ASSERT(openMode != 0); - MA_ASSERT(pFile != NULL); - - (void)pVFS; - - if ((openMode & MA_OPEN_MODE_READ) != 0) { - if ((openMode & MA_OPEN_MODE_WRITE) != 0) { - pOpenModeStr = "r+"; - } else { - pOpenModeStr = "rb"; - } - } else { - pOpenModeStr = "wb"; - } - - result = ma_fopen(&pFileStd, pFilePath, pOpenModeStr); - if (result != MA_SUCCESS) { - return result; - } - - *pFile = pFileStd; - - return MA_SUCCESS; -} - -static ma_result ma_default_vfs_open_w__stdio(ma_vfs* pVFS, const wchar_t* pFilePath, ma_uint32 openMode, ma_vfs_file* pFile) -{ - ma_result result; - FILE* pFileStd; - const wchar_t* pOpenModeStr; - - MA_ASSERT(pFilePath != NULL); - MA_ASSERT(openMode != 0); - MA_ASSERT(pFile != NULL); - - (void)pVFS; - - if ((openMode & MA_OPEN_MODE_READ) != 0) { - if ((openMode & MA_OPEN_MODE_WRITE) != 0) { - pOpenModeStr = L"r+"; - } else { - pOpenModeStr = L"rb"; - } - } else { - pOpenModeStr = L"wb"; - } - - result = ma_wfopen(&pFileStd, pFilePath, pOpenModeStr, (pVFS != NULL) ? &((ma_default_vfs*)pVFS)->allocationCallbacks : NULL); - if (result != MA_SUCCESS) { - return result; - } - - *pFile = pFileStd; - - return MA_SUCCESS; -} - -static ma_result ma_default_vfs_close__stdio(ma_vfs* pVFS, ma_vfs_file file) -{ - MA_ASSERT(file != NULL); - - (void)pVFS; - - fclose((FILE*)file); - - return MA_SUCCESS; -} - -static ma_result ma_default_vfs_read__stdio(ma_vfs* pVFS, ma_vfs_file file, void* pDst, size_t sizeInBytes, size_t* pBytesRead) -{ - size_t result; - - MA_ASSERT(file != NULL); - MA_ASSERT(pDst != NULL); - - (void)pVFS; - - result = fread(pDst, 1, sizeInBytes, (FILE*)file); - - if (pBytesRead != NULL) { - *pBytesRead = result; - } - - if (result != sizeInBytes) { - if (result == 0 && feof((FILE*)file)) { - return MA_AT_END; - } else { - return ma_result_from_errno(ferror((FILE*)file)); - } - } - - return MA_SUCCESS; -} - -static ma_result ma_default_vfs_write__stdio(ma_vfs* pVFS, ma_vfs_file file, const void* pSrc, size_t sizeInBytes, size_t* pBytesWritten) -{ - size_t result; - - MA_ASSERT(file != NULL); - MA_ASSERT(pSrc != NULL); - - (void)pVFS; - - result = fwrite(pSrc, 1, sizeInBytes, (FILE*)file); - - if (pBytesWritten != NULL) { - *pBytesWritten = result; - } - - if (result != sizeInBytes) { - return ma_result_from_errno(ferror((FILE*)file)); - } - - return MA_SUCCESS; -} - -static ma_result ma_default_vfs_seek__stdio(ma_vfs* pVFS, ma_vfs_file file, ma_int64 offset, ma_seek_origin origin) -{ - int result; - int whence; - - MA_ASSERT(file != NULL); - - (void)pVFS; - - if (origin == ma_seek_origin_start) { - whence = SEEK_SET; - } else if (origin == ma_seek_origin_end) { - whence = SEEK_END; - } else { - whence = SEEK_CUR; - } - -#if defined(_WIN32) - #if defined(_MSC_VER) && _MSC_VER > 1200 - result = _fseeki64((FILE*)file, offset, whence); - #else - /* No _fseeki64() so restrict to 31 bits. */ - if (offset > 0x7FFFFFFF) { - return MA_OUT_OF_RANGE; - } - - result = fseek((FILE*)file, (int)offset, whence); - #endif -#else - result = fseek((FILE*)file, (long int)offset, whence); -#endif - if (result != 0) { - return MA_ERROR; - } - - return MA_SUCCESS; -} - -static ma_result ma_default_vfs_tell__stdio(ma_vfs* pVFS, ma_vfs_file file, ma_int64* pCursor) -{ - ma_int64 result; - - MA_ASSERT(file != NULL); - MA_ASSERT(pCursor != NULL); - - (void)pVFS; - -#if defined(_WIN32) - #if defined(_MSC_VER) && _MSC_VER > 1200 - result = _ftelli64((FILE*)file); - #else - result = ftell((FILE*)file); - #endif -#else - result = ftell((FILE*)file); -#endif - - *pCursor = result; - - return MA_SUCCESS; -} - -#if !defined(_MSC_VER) && !((defined(_POSIX_C_SOURCE) && _POSIX_C_SOURCE >= 1) || defined(_XOPEN_SOURCE) || defined(_POSIX_SOURCE)) && !defined(MA_BSD) -int fileno(FILE *stream); -#endif - -static ma_result ma_default_vfs_info__stdio(ma_vfs* pVFS, ma_vfs_file file, ma_file_info* pInfo) -{ - int fd; - struct stat info; - - MA_ASSERT(file != NULL); - MA_ASSERT(pInfo != NULL); - - (void)pVFS; - -#if defined(_MSC_VER) - fd = _fileno((FILE*)file); -#else - fd = fileno((FILE*)file); -#endif - - if (fstat(fd, &info) != 0) { - return ma_result_from_errno(errno); - } - - pInfo->sizeInBytes = info.st_size; - - return MA_SUCCESS; -} -#endif - - -static ma_result ma_default_vfs_open(ma_vfs* pVFS, const char* pFilePath, ma_uint32 openMode, ma_vfs_file* pFile) -{ - if (pFile == NULL) { - return MA_INVALID_ARGS; - } - - *pFile = NULL; - - if (pFilePath == NULL || openMode == 0) { - return MA_INVALID_ARGS; - } - -#if defined(MA_USE_WIN32_FILEIO) - return ma_default_vfs_open__win32(pVFS, pFilePath, openMode, pFile); -#else - return ma_default_vfs_open__stdio(pVFS, pFilePath, openMode, pFile); -#endif -} - -static ma_result ma_default_vfs_open_w(ma_vfs* pVFS, const wchar_t* pFilePath, ma_uint32 openMode, ma_vfs_file* pFile) -{ - if (pFile == NULL) { - return MA_INVALID_ARGS; - } - - *pFile = NULL; - - if (pFilePath == NULL || openMode == 0) { - return MA_INVALID_ARGS; - } - -#if defined(MA_USE_WIN32_FILEIO) - return ma_default_vfs_open_w__win32(pVFS, pFilePath, openMode, pFile); -#else - return ma_default_vfs_open_w__stdio(pVFS, pFilePath, openMode, pFile); -#endif -} - -static ma_result ma_default_vfs_close(ma_vfs* pVFS, ma_vfs_file file) -{ - if (file == NULL) { - return MA_INVALID_ARGS; - } - -#if defined(MA_USE_WIN32_FILEIO) - return ma_default_vfs_close__win32(pVFS, file); -#else - return ma_default_vfs_close__stdio(pVFS, file); -#endif -} - -static ma_result ma_default_vfs_read(ma_vfs* pVFS, ma_vfs_file file, void* pDst, size_t sizeInBytes, size_t* pBytesRead) -{ - if (pBytesRead != NULL) { - *pBytesRead = 0; - } - - if (file == NULL || pDst == NULL) { - return MA_INVALID_ARGS; - } - -#if defined(MA_USE_WIN32_FILEIO) - return ma_default_vfs_read__win32(pVFS, file, pDst, sizeInBytes, pBytesRead); -#else - return ma_default_vfs_read__stdio(pVFS, file, pDst, sizeInBytes, pBytesRead); -#endif -} - -static ma_result ma_default_vfs_write(ma_vfs* pVFS, ma_vfs_file file, const void* pSrc, size_t sizeInBytes, size_t* pBytesWritten) -{ - if (pBytesWritten != NULL) { - *pBytesWritten = 0; - } - - if (file == NULL || pSrc == NULL) { - return MA_INVALID_ARGS; - } - -#if defined(MA_USE_WIN32_FILEIO) - return ma_default_vfs_write__win32(pVFS, file, pSrc, sizeInBytes, pBytesWritten); -#else - return ma_default_vfs_write__stdio(pVFS, file, pSrc, sizeInBytes, pBytesWritten); -#endif -} - -static ma_result ma_default_vfs_seek(ma_vfs* pVFS, ma_vfs_file file, ma_int64 offset, ma_seek_origin origin) -{ - if (file == NULL) { - return MA_INVALID_ARGS; - } - -#if defined(MA_USE_WIN32_FILEIO) - return ma_default_vfs_seek__win32(pVFS, file, offset, origin); -#else - return ma_default_vfs_seek__stdio(pVFS, file, offset, origin); -#endif -} - -static ma_result ma_default_vfs_tell(ma_vfs* pVFS, ma_vfs_file file, ma_int64* pCursor) -{ - if (pCursor == NULL) { - return MA_INVALID_ARGS; - } - - *pCursor = 0; - - if (file == NULL) { - return MA_INVALID_ARGS; - } - -#if defined(MA_USE_WIN32_FILEIO) - return ma_default_vfs_tell__win32(pVFS, file, pCursor); -#else - return ma_default_vfs_tell__stdio(pVFS, file, pCursor); -#endif -} - -static ma_result ma_default_vfs_info(ma_vfs* pVFS, ma_vfs_file file, ma_file_info* pInfo) -{ - if (pInfo == NULL) { - return MA_INVALID_ARGS; - } - - MA_ZERO_OBJECT(pInfo); - - if (file == NULL) { - return MA_INVALID_ARGS; - } - -#if defined(MA_USE_WIN32_FILEIO) - return ma_default_vfs_info__win32(pVFS, file, pInfo); -#else - return ma_default_vfs_info__stdio(pVFS, file, pInfo); -#endif -} - - -MA_API ma_result ma_default_vfs_init(ma_default_vfs* pVFS, const ma_allocation_callbacks* pAllocationCallbacks) -{ - if (pVFS == NULL) { - return MA_INVALID_ARGS; - } - - pVFS->cb.onOpen = ma_default_vfs_open; - pVFS->cb.onOpenW = ma_default_vfs_open_w; - pVFS->cb.onClose = ma_default_vfs_close; - pVFS->cb.onRead = ma_default_vfs_read; - pVFS->cb.onWrite = ma_default_vfs_write; - pVFS->cb.onSeek = ma_default_vfs_seek; - pVFS->cb.onTell = ma_default_vfs_tell; - pVFS->cb.onInfo = ma_default_vfs_info; - ma_allocation_callbacks_init_copy(&pVFS->allocationCallbacks, pAllocationCallbacks); - - return MA_SUCCESS; -} - - -MA_API ma_result ma_vfs_or_default_open(ma_vfs* pVFS, const char* pFilePath, ma_uint32 openMode, ma_vfs_file* pFile) -{ - if (pVFS != NULL) { - return ma_vfs_open(pVFS, pFilePath, openMode, pFile); - } else { - return ma_default_vfs_open(pVFS, pFilePath, openMode, pFile); - } -} - -MA_API ma_result ma_vfs_or_default_open_w(ma_vfs* pVFS, const wchar_t* pFilePath, ma_uint32 openMode, ma_vfs_file* pFile) -{ - if (pVFS != NULL) { - return ma_vfs_open_w(pVFS, pFilePath, openMode, pFile); - } else { - return ma_default_vfs_open_w(pVFS, pFilePath, openMode, pFile); - } -} - -MA_API ma_result ma_vfs_or_default_close(ma_vfs* pVFS, ma_vfs_file file) -{ - if (pVFS != NULL) { - return ma_vfs_close(pVFS, file); - } else { - return ma_default_vfs_close(pVFS, file); - } -} - -MA_API ma_result ma_vfs_or_default_read(ma_vfs* pVFS, ma_vfs_file file, void* pDst, size_t sizeInBytes, size_t* pBytesRead) -{ - if (pVFS != NULL) { - return ma_vfs_read(pVFS, file, pDst, sizeInBytes, pBytesRead); - } else { - return ma_default_vfs_read(pVFS, file, pDst, sizeInBytes, pBytesRead); - } -} - -MA_API ma_result ma_vfs_or_default_write(ma_vfs* pVFS, ma_vfs_file file, const void* pSrc, size_t sizeInBytes, size_t* pBytesWritten) -{ - if (pVFS != NULL) { - return ma_vfs_write(pVFS, file, pSrc, sizeInBytes, pBytesWritten); - } else { - return ma_default_vfs_write(pVFS, file, pSrc, sizeInBytes, pBytesWritten); - } -} - -MA_API ma_result ma_vfs_or_default_seek(ma_vfs* pVFS, ma_vfs_file file, ma_int64 offset, ma_seek_origin origin) -{ - if (pVFS != NULL) { - return ma_vfs_seek(pVFS, file, offset, origin); - } else { - return ma_default_vfs_seek(pVFS, file, offset, origin); - } -} - -MA_API ma_result ma_vfs_or_default_tell(ma_vfs* pVFS, ma_vfs_file file, ma_int64* pCursor) -{ - if (pVFS != NULL) { - return ma_vfs_tell(pVFS, file, pCursor); - } else { - return ma_default_vfs_tell(pVFS, file, pCursor); - } -} - -MA_API ma_result ma_vfs_or_default_info(ma_vfs* pVFS, ma_vfs_file file, ma_file_info* pInfo) -{ - if (pVFS != NULL) { - return ma_vfs_info(pVFS, file, pInfo); - } else { - return ma_default_vfs_info(pVFS, file, pInfo); - } -} - - - -static ma_result ma_vfs_open_and_read_file_ex(ma_vfs* pVFS, const char* pFilePath, const wchar_t* pFilePathW, void** ppData, size_t* pSize, const ma_allocation_callbacks* pAllocationCallbacks) -{ - ma_result result; - ma_vfs_file file; - ma_file_info info; - void* pData; - size_t bytesRead; - - if (ppData != NULL) { - *ppData = NULL; - } - if (pSize != NULL) { - *pSize = 0; - } - - if (ppData == NULL) { - return MA_INVALID_ARGS; - } - - if (pFilePath != NULL) { - result = ma_vfs_or_default_open(pVFS, pFilePath, MA_OPEN_MODE_READ, &file); - } else { - result = ma_vfs_or_default_open_w(pVFS, pFilePathW, MA_OPEN_MODE_READ, &file); - } - if (result != MA_SUCCESS) { - return result; - } - - result = ma_vfs_or_default_info(pVFS, file, &info); - if (result != MA_SUCCESS) { - ma_vfs_or_default_close(pVFS, file); - return result; - } - - if (info.sizeInBytes > MA_SIZE_MAX) { - ma_vfs_or_default_close(pVFS, file); - return MA_TOO_BIG; - } - - pData = ma_malloc((size_t)info.sizeInBytes, pAllocationCallbacks); /* Safe cast. */ - if (pData == NULL) { - ma_vfs_or_default_close(pVFS, file); - return result; - } - - result = ma_vfs_or_default_read(pVFS, file, pData, (size_t)info.sizeInBytes, &bytesRead); /* Safe cast. */ - ma_vfs_or_default_close(pVFS, file); - - if (result != MA_SUCCESS) { - ma_free(pData, pAllocationCallbacks); - return result; - } - - if (pSize != NULL) { - *pSize = bytesRead; - } - - MA_ASSERT(ppData != NULL); - *ppData = pData; - - return MA_SUCCESS; -} - -MA_API ma_result ma_vfs_open_and_read_file(ma_vfs* pVFS, const char* pFilePath, void** ppData, size_t* pSize, const ma_allocation_callbacks* pAllocationCallbacks) -{ - return ma_vfs_open_and_read_file_ex(pVFS, pFilePath, NULL, ppData, pSize, pAllocationCallbacks); -} - -MA_API ma_result ma_vfs_open_and_read_file_w(ma_vfs* pVFS, const wchar_t* pFilePath, void** ppData, size_t* pSize, const ma_allocation_callbacks* pAllocationCallbacks) -{ - return ma_vfs_open_and_read_file_ex(pVFS, NULL, pFilePath, ppData, pSize, pAllocationCallbacks); -} - - - -/************************************************************************************************************************************************************** - -Decoding and Encoding Headers. These are auto-generated from a tool. - -**************************************************************************************************************************************************************/ -#if !defined(MA_NO_WAV) && (!defined(MA_NO_DECODING) || !defined(MA_NO_ENCODING)) -/* dr_wav_h begin */ -#ifndef ma_dr_wav_h -#define ma_dr_wav_h -#ifdef __cplusplus -extern "C" { -#endif -#define MA_DR_WAV_STRINGIFY(x) #x -#define MA_DR_WAV_XSTRINGIFY(x) MA_DR_WAV_STRINGIFY(x) -#define MA_DR_WAV_VERSION_MAJOR 0 -#define MA_DR_WAV_VERSION_MINOR 13 -#define MA_DR_WAV_VERSION_REVISION 18 -#define MA_DR_WAV_VERSION_STRING MA_DR_WAV_XSTRINGIFY(MA_DR_WAV_VERSION_MAJOR) "." MA_DR_WAV_XSTRINGIFY(MA_DR_WAV_VERSION_MINOR) "." MA_DR_WAV_XSTRINGIFY(MA_DR_WAV_VERSION_REVISION) -#include -#define MA_DR_WAVE_FORMAT_PCM 0x1 -#define MA_DR_WAVE_FORMAT_ADPCM 0x2 -#define MA_DR_WAVE_FORMAT_IEEE_FLOAT 0x3 -#define MA_DR_WAVE_FORMAT_ALAW 0x6 -#define MA_DR_WAVE_FORMAT_MULAW 0x7 -#define MA_DR_WAVE_FORMAT_DVI_ADPCM 0x11 -#define MA_DR_WAVE_FORMAT_EXTENSIBLE 0xFFFE -#define MA_DR_WAV_SEQUENTIAL 0x00000001 -#define MA_DR_WAV_WITH_METADATA 0x00000002 -MA_API void ma_dr_wav_version(ma_uint32* pMajor, ma_uint32* pMinor, ma_uint32* pRevision); -MA_API const char* ma_dr_wav_version_string(void); -typedef enum -{ - ma_dr_wav_seek_origin_start, - ma_dr_wav_seek_origin_current -} ma_dr_wav_seek_origin; -typedef enum -{ - ma_dr_wav_container_riff, - ma_dr_wav_container_rifx, - ma_dr_wav_container_w64, - ma_dr_wav_container_rf64, - ma_dr_wav_container_aiff -} ma_dr_wav_container; -typedef struct -{ - union - { - ma_uint8 fourcc[4]; - ma_uint8 guid[16]; - } id; - ma_uint64 sizeInBytes; - unsigned int paddingSize; -} ma_dr_wav_chunk_header; -typedef struct -{ - ma_uint16 formatTag; - ma_uint16 channels; - ma_uint32 sampleRate; - ma_uint32 avgBytesPerSec; - ma_uint16 blockAlign; - ma_uint16 bitsPerSample; - ma_uint16 extendedSize; - ma_uint16 validBitsPerSample; - ma_uint32 channelMask; - ma_uint8 subFormat[16]; -} ma_dr_wav_fmt; -MA_API ma_uint16 ma_dr_wav_fmt_get_format(const ma_dr_wav_fmt* pFMT); -typedef size_t (* ma_dr_wav_read_proc)(void* pUserData, void* pBufferOut, size_t bytesToRead); -typedef size_t (* ma_dr_wav_write_proc)(void* pUserData, const void* pData, size_t bytesToWrite); -typedef ma_bool32 (* ma_dr_wav_seek_proc)(void* pUserData, int offset, ma_dr_wav_seek_origin origin); -typedef ma_uint64 (* ma_dr_wav_chunk_proc)(void* pChunkUserData, ma_dr_wav_read_proc onRead, ma_dr_wav_seek_proc onSeek, void* pReadSeekUserData, const ma_dr_wav_chunk_header* pChunkHeader, ma_dr_wav_container container, const ma_dr_wav_fmt* pFMT); -typedef struct -{ - const ma_uint8* data; - size_t dataSize; - size_t currentReadPos; -} ma_dr_wav__memory_stream; -typedef struct -{ - void** ppData; - size_t* pDataSize; - size_t dataSize; - size_t dataCapacity; - size_t currentWritePos; -} ma_dr_wav__memory_stream_write; -typedef struct -{ - ma_dr_wav_container container; - ma_uint32 format; - ma_uint32 channels; - ma_uint32 sampleRate; - ma_uint32 bitsPerSample; -} ma_dr_wav_data_format; -typedef enum -{ - ma_dr_wav_metadata_type_none = 0, - ma_dr_wav_metadata_type_unknown = 1 << 0, - ma_dr_wav_metadata_type_smpl = 1 << 1, - ma_dr_wav_metadata_type_inst = 1 << 2, - ma_dr_wav_metadata_type_cue = 1 << 3, - ma_dr_wav_metadata_type_acid = 1 << 4, - ma_dr_wav_metadata_type_bext = 1 << 5, - ma_dr_wav_metadata_type_list_label = 1 << 6, - ma_dr_wav_metadata_type_list_note = 1 << 7, - ma_dr_wav_metadata_type_list_labelled_cue_region = 1 << 8, - ma_dr_wav_metadata_type_list_info_software = 1 << 9, - ma_dr_wav_metadata_type_list_info_copyright = 1 << 10, - ma_dr_wav_metadata_type_list_info_title = 1 << 11, - ma_dr_wav_metadata_type_list_info_artist = 1 << 12, - ma_dr_wav_metadata_type_list_info_comment = 1 << 13, - ma_dr_wav_metadata_type_list_info_date = 1 << 14, - ma_dr_wav_metadata_type_list_info_genre = 1 << 15, - ma_dr_wav_metadata_type_list_info_album = 1 << 16, - ma_dr_wav_metadata_type_list_info_tracknumber = 1 << 17, - ma_dr_wav_metadata_type_list_all_info_strings = ma_dr_wav_metadata_type_list_info_software - | ma_dr_wav_metadata_type_list_info_copyright - | ma_dr_wav_metadata_type_list_info_title - | ma_dr_wav_metadata_type_list_info_artist - | ma_dr_wav_metadata_type_list_info_comment - | ma_dr_wav_metadata_type_list_info_date - | ma_dr_wav_metadata_type_list_info_genre - | ma_dr_wav_metadata_type_list_info_album - | ma_dr_wav_metadata_type_list_info_tracknumber, - ma_dr_wav_metadata_type_list_all_adtl = ma_dr_wav_metadata_type_list_label - | ma_dr_wav_metadata_type_list_note - | ma_dr_wav_metadata_type_list_labelled_cue_region, - ma_dr_wav_metadata_type_all = -2, - ma_dr_wav_metadata_type_all_including_unknown = -1 -} ma_dr_wav_metadata_type; -typedef enum -{ - ma_dr_wav_smpl_loop_type_forward = 0, - ma_dr_wav_smpl_loop_type_pingpong = 1, - ma_dr_wav_smpl_loop_type_backward = 2 -} ma_dr_wav_smpl_loop_type; -typedef struct -{ - ma_uint32 cuePointId; - ma_uint32 type; - ma_uint32 firstSampleByteOffset; - ma_uint32 lastSampleByteOffset; - ma_uint32 sampleFraction; - ma_uint32 playCount; -} ma_dr_wav_smpl_loop; -typedef struct -{ - ma_uint32 manufacturerId; - ma_uint32 productId; - ma_uint32 samplePeriodNanoseconds; - ma_uint32 midiUnityNote; - ma_uint32 midiPitchFraction; - ma_uint32 smpteFormat; - ma_uint32 smpteOffset; - ma_uint32 sampleLoopCount; - ma_uint32 samplerSpecificDataSizeInBytes; - ma_dr_wav_smpl_loop* pLoops; - ma_uint8* pSamplerSpecificData; -} ma_dr_wav_smpl; -typedef struct -{ - ma_int8 midiUnityNote; - ma_int8 fineTuneCents; - ma_int8 gainDecibels; - ma_int8 lowNote; - ma_int8 highNote; - ma_int8 lowVelocity; - ma_int8 highVelocity; -} ma_dr_wav_inst; -typedef struct -{ - ma_uint32 id; - ma_uint32 playOrderPosition; - ma_uint8 dataChunkId[4]; - ma_uint32 chunkStart; - ma_uint32 blockStart; - ma_uint32 sampleByteOffset; -} ma_dr_wav_cue_point; -typedef struct -{ - ma_uint32 cuePointCount; - ma_dr_wav_cue_point *pCuePoints; -} ma_dr_wav_cue; -typedef enum -{ - ma_dr_wav_acid_flag_one_shot = 1, - ma_dr_wav_acid_flag_root_note_set = 2, - ma_dr_wav_acid_flag_stretch = 4, - ma_dr_wav_acid_flag_disk_based = 8, - ma_dr_wav_acid_flag_acidizer = 16 -} ma_dr_wav_acid_flag; -typedef struct -{ - ma_uint32 flags; - ma_uint16 midiUnityNote; - ma_uint16 reserved1; - float reserved2; - ma_uint32 numBeats; - ma_uint16 meterDenominator; - ma_uint16 meterNumerator; - float tempo; -} ma_dr_wav_acid; -typedef struct -{ - ma_uint32 cuePointId; - ma_uint32 stringLength; - char* pString; -} ma_dr_wav_list_label_or_note; -typedef struct -{ - char* pDescription; - char* pOriginatorName; - char* pOriginatorReference; - char pOriginationDate[10]; - char pOriginationTime[8]; - ma_uint64 timeReference; - ma_uint16 version; - char* pCodingHistory; - ma_uint32 codingHistorySize; - ma_uint8* pUMID; - ma_uint16 loudnessValue; - ma_uint16 loudnessRange; - ma_uint16 maxTruePeakLevel; - ma_uint16 maxMomentaryLoudness; - ma_uint16 maxShortTermLoudness; -} ma_dr_wav_bext; -typedef struct -{ - ma_uint32 stringLength; - char* pString; -} ma_dr_wav_list_info_text; -typedef struct -{ - ma_uint32 cuePointId; - ma_uint32 sampleLength; - ma_uint8 purposeId[4]; - ma_uint16 country; - ma_uint16 language; - ma_uint16 dialect; - ma_uint16 codePage; - ma_uint32 stringLength; - char* pString; -} ma_dr_wav_list_labelled_cue_region; -typedef enum -{ - ma_dr_wav_metadata_location_invalid, - ma_dr_wav_metadata_location_top_level, - ma_dr_wav_metadata_location_inside_info_list, - ma_dr_wav_metadata_location_inside_adtl_list -} ma_dr_wav_metadata_location; -typedef struct -{ - ma_uint8 id[4]; - ma_dr_wav_metadata_location chunkLocation; - ma_uint32 dataSizeInBytes; - ma_uint8* pData; -} ma_dr_wav_unknown_metadata; -typedef struct -{ - ma_dr_wav_metadata_type type; - union - { - ma_dr_wav_cue cue; - ma_dr_wav_smpl smpl; - ma_dr_wav_acid acid; - ma_dr_wav_inst inst; - ma_dr_wav_bext bext; - ma_dr_wav_list_label_or_note labelOrNote; - ma_dr_wav_list_labelled_cue_region labelledCueRegion; - ma_dr_wav_list_info_text infoText; - ma_dr_wav_unknown_metadata unknown; - } data; -} ma_dr_wav_metadata; -typedef struct -{ - ma_dr_wav_read_proc onRead; - ma_dr_wav_write_proc onWrite; - ma_dr_wav_seek_proc onSeek; - void* pUserData; - ma_allocation_callbacks allocationCallbacks; - ma_dr_wav_container container; - ma_dr_wav_fmt fmt; - ma_uint32 sampleRate; - ma_uint16 channels; - ma_uint16 bitsPerSample; - ma_uint16 translatedFormatTag; - ma_uint64 totalPCMFrameCount; - ma_uint64 dataChunkDataSize; - ma_uint64 dataChunkDataPos; - ma_uint64 bytesRemaining; - ma_uint64 readCursorInPCMFrames; - ma_uint64 dataChunkDataSizeTargetWrite; - ma_bool32 isSequentialWrite; - ma_dr_wav_metadata* pMetadata; - ma_uint32 metadataCount; - ma_dr_wav__memory_stream memoryStream; - ma_dr_wav__memory_stream_write memoryStreamWrite; - struct - { - ma_uint32 bytesRemainingInBlock; - ma_uint16 predictor[2]; - ma_int32 delta[2]; - ma_int32 cachedFrames[4]; - ma_uint32 cachedFrameCount; - ma_int32 prevFrames[2][2]; - } msadpcm; - struct - { - ma_uint32 bytesRemainingInBlock; - ma_int32 predictor[2]; - ma_int32 stepIndex[2]; - ma_int32 cachedFrames[16]; - ma_uint32 cachedFrameCount; - } ima; - struct - { - ma_bool8 isLE; - ma_bool8 isUnsigned; - } aiff; -} ma_dr_wav; -MA_API ma_bool32 ma_dr_wav_init(ma_dr_wav* pWav, ma_dr_wav_read_proc onRead, ma_dr_wav_seek_proc onSeek, void* pUserData, const ma_allocation_callbacks* pAllocationCallbacks); -MA_API ma_bool32 ma_dr_wav_init_ex(ma_dr_wav* pWav, ma_dr_wav_read_proc onRead, ma_dr_wav_seek_proc onSeek, ma_dr_wav_chunk_proc onChunk, void* pReadSeekUserData, void* pChunkUserData, ma_uint32 flags, const ma_allocation_callbacks* pAllocationCallbacks); -MA_API ma_bool32 ma_dr_wav_init_with_metadata(ma_dr_wav* pWav, ma_dr_wav_read_proc onRead, ma_dr_wav_seek_proc onSeek, void* pUserData, ma_uint32 flags, const ma_allocation_callbacks* pAllocationCallbacks); -MA_API ma_bool32 ma_dr_wav_init_write(ma_dr_wav* pWav, const ma_dr_wav_data_format* pFormat, ma_dr_wav_write_proc onWrite, ma_dr_wav_seek_proc onSeek, void* pUserData, const ma_allocation_callbacks* pAllocationCallbacks); -MA_API ma_bool32 ma_dr_wav_init_write_sequential(ma_dr_wav* pWav, const ma_dr_wav_data_format* pFormat, ma_uint64 totalSampleCount, ma_dr_wav_write_proc onWrite, void* pUserData, const ma_allocation_callbacks* pAllocationCallbacks); -MA_API ma_bool32 ma_dr_wav_init_write_sequential_pcm_frames(ma_dr_wav* pWav, const ma_dr_wav_data_format* pFormat, ma_uint64 totalPCMFrameCount, ma_dr_wav_write_proc onWrite, void* pUserData, const ma_allocation_callbacks* pAllocationCallbacks); -MA_API ma_bool32 ma_dr_wav_init_write_with_metadata(ma_dr_wav* pWav, const ma_dr_wav_data_format* pFormat, ma_dr_wav_write_proc onWrite, ma_dr_wav_seek_proc onSeek, void* pUserData, const ma_allocation_callbacks* pAllocationCallbacks, ma_dr_wav_metadata* pMetadata, ma_uint32 metadataCount); -MA_API ma_uint64 ma_dr_wav_target_write_size_bytes(const ma_dr_wav_data_format* pFormat, ma_uint64 totalFrameCount, ma_dr_wav_metadata* pMetadata, ma_uint32 metadataCount); -MA_API ma_dr_wav_metadata* ma_dr_wav_take_ownership_of_metadata(ma_dr_wav* pWav); -MA_API ma_result ma_dr_wav_uninit(ma_dr_wav* pWav); -MA_API size_t ma_dr_wav_read_raw(ma_dr_wav* pWav, size_t bytesToRead, void* pBufferOut); -MA_API ma_uint64 ma_dr_wav_read_pcm_frames(ma_dr_wav* pWav, ma_uint64 framesToRead, void* pBufferOut); -MA_API ma_uint64 ma_dr_wav_read_pcm_frames_le(ma_dr_wav* pWav, ma_uint64 framesToRead, void* pBufferOut); -MA_API ma_uint64 ma_dr_wav_read_pcm_frames_be(ma_dr_wav* pWav, ma_uint64 framesToRead, void* pBufferOut); -MA_API ma_bool32 ma_dr_wav_seek_to_pcm_frame(ma_dr_wav* pWav, ma_uint64 targetFrameIndex); -MA_API ma_result ma_dr_wav_get_cursor_in_pcm_frames(ma_dr_wav* pWav, ma_uint64* pCursor); -MA_API ma_result ma_dr_wav_get_length_in_pcm_frames(ma_dr_wav* pWav, ma_uint64* pLength); -MA_API size_t ma_dr_wav_write_raw(ma_dr_wav* pWav, size_t bytesToWrite, const void* pData); -MA_API ma_uint64 ma_dr_wav_write_pcm_frames(ma_dr_wav* pWav, ma_uint64 framesToWrite, const void* pData); -MA_API ma_uint64 ma_dr_wav_write_pcm_frames_le(ma_dr_wav* pWav, ma_uint64 framesToWrite, const void* pData); -MA_API ma_uint64 ma_dr_wav_write_pcm_frames_be(ma_dr_wav* pWav, ma_uint64 framesToWrite, const void* pData); -#ifndef MA_DR_WAV_NO_CONVERSION_API -MA_API ma_uint64 ma_dr_wav_read_pcm_frames_s16(ma_dr_wav* pWav, ma_uint64 framesToRead, ma_int16* pBufferOut); -MA_API ma_uint64 ma_dr_wav_read_pcm_frames_s16le(ma_dr_wav* pWav, ma_uint64 framesToRead, ma_int16* pBufferOut); -MA_API ma_uint64 ma_dr_wav_read_pcm_frames_s16be(ma_dr_wav* pWav, ma_uint64 framesToRead, ma_int16* pBufferOut); -MA_API void ma_dr_wav_u8_to_s16(ma_int16* pOut, const ma_uint8* pIn, size_t sampleCount); -MA_API void ma_dr_wav_s24_to_s16(ma_int16* pOut, const ma_uint8* pIn, size_t sampleCount); -MA_API void ma_dr_wav_s32_to_s16(ma_int16* pOut, const ma_int32* pIn, size_t sampleCount); -MA_API void ma_dr_wav_f32_to_s16(ma_int16* pOut, const float* pIn, size_t sampleCount); -MA_API void ma_dr_wav_f64_to_s16(ma_int16* pOut, const double* pIn, size_t sampleCount); -MA_API void ma_dr_wav_alaw_to_s16(ma_int16* pOut, const ma_uint8* pIn, size_t sampleCount); -MA_API void ma_dr_wav_mulaw_to_s16(ma_int16* pOut, const ma_uint8* pIn, size_t sampleCount); -MA_API ma_uint64 ma_dr_wav_read_pcm_frames_f32(ma_dr_wav* pWav, ma_uint64 framesToRead, float* pBufferOut); -MA_API ma_uint64 ma_dr_wav_read_pcm_frames_f32le(ma_dr_wav* pWav, ma_uint64 framesToRead, float* pBufferOut); -MA_API ma_uint64 ma_dr_wav_read_pcm_frames_f32be(ma_dr_wav* pWav, ma_uint64 framesToRead, float* pBufferOut); -MA_API void ma_dr_wav_u8_to_f32(float* pOut, const ma_uint8* pIn, size_t sampleCount); -MA_API void ma_dr_wav_s16_to_f32(float* pOut, const ma_int16* pIn, size_t sampleCount); -MA_API void ma_dr_wav_s24_to_f32(float* pOut, const ma_uint8* pIn, size_t sampleCount); -MA_API void ma_dr_wav_s32_to_f32(float* pOut, const ma_int32* pIn, size_t sampleCount); -MA_API void ma_dr_wav_f64_to_f32(float* pOut, const double* pIn, size_t sampleCount); -MA_API void ma_dr_wav_alaw_to_f32(float* pOut, const ma_uint8* pIn, size_t sampleCount); -MA_API void ma_dr_wav_mulaw_to_f32(float* pOut, const ma_uint8* pIn, size_t sampleCount); -MA_API ma_uint64 ma_dr_wav_read_pcm_frames_s32(ma_dr_wav* pWav, ma_uint64 framesToRead, ma_int32* pBufferOut); -MA_API ma_uint64 ma_dr_wav_read_pcm_frames_s32le(ma_dr_wav* pWav, ma_uint64 framesToRead, ma_int32* pBufferOut); -MA_API ma_uint64 ma_dr_wav_read_pcm_frames_s32be(ma_dr_wav* pWav, ma_uint64 framesToRead, ma_int32* pBufferOut); -MA_API void ma_dr_wav_u8_to_s32(ma_int32* pOut, const ma_uint8* pIn, size_t sampleCount); -MA_API void ma_dr_wav_s16_to_s32(ma_int32* pOut, const ma_int16* pIn, size_t sampleCount); -MA_API void ma_dr_wav_s24_to_s32(ma_int32* pOut, const ma_uint8* pIn, size_t sampleCount); -MA_API void ma_dr_wav_f32_to_s32(ma_int32* pOut, const float* pIn, size_t sampleCount); -MA_API void ma_dr_wav_f64_to_s32(ma_int32* pOut, const double* pIn, size_t sampleCount); -MA_API void ma_dr_wav_alaw_to_s32(ma_int32* pOut, const ma_uint8* pIn, size_t sampleCount); -MA_API void ma_dr_wav_mulaw_to_s32(ma_int32* pOut, const ma_uint8* pIn, size_t sampleCount); -#endif -#ifndef MA_DR_WAV_NO_STDIO -MA_API ma_bool32 ma_dr_wav_init_file(ma_dr_wav* pWav, const char* filename, const ma_allocation_callbacks* pAllocationCallbacks); -MA_API ma_bool32 ma_dr_wav_init_file_ex(ma_dr_wav* pWav, const char* filename, ma_dr_wav_chunk_proc onChunk, void* pChunkUserData, ma_uint32 flags, const ma_allocation_callbacks* pAllocationCallbacks); -MA_API ma_bool32 ma_dr_wav_init_file_w(ma_dr_wav* pWav, const wchar_t* filename, const ma_allocation_callbacks* pAllocationCallbacks); -MA_API ma_bool32 ma_dr_wav_init_file_ex_w(ma_dr_wav* pWav, const wchar_t* filename, ma_dr_wav_chunk_proc onChunk, void* pChunkUserData, ma_uint32 flags, const ma_allocation_callbacks* pAllocationCallbacks); -MA_API ma_bool32 ma_dr_wav_init_file_with_metadata(ma_dr_wav* pWav, const char* filename, ma_uint32 flags, const ma_allocation_callbacks* pAllocationCallbacks); -MA_API ma_bool32 ma_dr_wav_init_file_with_metadata_w(ma_dr_wav* pWav, const wchar_t* filename, ma_uint32 flags, const ma_allocation_callbacks* pAllocationCallbacks); -MA_API ma_bool32 ma_dr_wav_init_file_write(ma_dr_wav* pWav, const char* filename, const ma_dr_wav_data_format* pFormat, const ma_allocation_callbacks* pAllocationCallbacks); -MA_API ma_bool32 ma_dr_wav_init_file_write_sequential(ma_dr_wav* pWav, const char* filename, const ma_dr_wav_data_format* pFormat, ma_uint64 totalSampleCount, const ma_allocation_callbacks* pAllocationCallbacks); -MA_API ma_bool32 ma_dr_wav_init_file_write_sequential_pcm_frames(ma_dr_wav* pWav, const char* filename, const ma_dr_wav_data_format* pFormat, ma_uint64 totalPCMFrameCount, const ma_allocation_callbacks* pAllocationCallbacks); -MA_API ma_bool32 ma_dr_wav_init_file_write_w(ma_dr_wav* pWav, const wchar_t* filename, const ma_dr_wav_data_format* pFormat, const ma_allocation_callbacks* pAllocationCallbacks); -MA_API ma_bool32 ma_dr_wav_init_file_write_sequential_w(ma_dr_wav* pWav, const wchar_t* filename, const ma_dr_wav_data_format* pFormat, ma_uint64 totalSampleCount, const ma_allocation_callbacks* pAllocationCallbacks); -MA_API ma_bool32 ma_dr_wav_init_file_write_sequential_pcm_frames_w(ma_dr_wav* pWav, const wchar_t* filename, const ma_dr_wav_data_format* pFormat, ma_uint64 totalPCMFrameCount, const ma_allocation_callbacks* pAllocationCallbacks); -#endif -MA_API ma_bool32 ma_dr_wav_init_memory(ma_dr_wav* pWav, const void* data, size_t dataSize, const ma_allocation_callbacks* pAllocationCallbacks); -MA_API ma_bool32 ma_dr_wav_init_memory_ex(ma_dr_wav* pWav, const void* data, size_t dataSize, ma_dr_wav_chunk_proc onChunk, void* pChunkUserData, ma_uint32 flags, const ma_allocation_callbacks* pAllocationCallbacks); -MA_API ma_bool32 ma_dr_wav_init_memory_with_metadata(ma_dr_wav* pWav, const void* data, size_t dataSize, ma_uint32 flags, const ma_allocation_callbacks* pAllocationCallbacks); -MA_API ma_bool32 ma_dr_wav_init_memory_write(ma_dr_wav* pWav, void** ppData, size_t* pDataSize, const ma_dr_wav_data_format* pFormat, const ma_allocation_callbacks* pAllocationCallbacks); -MA_API ma_bool32 ma_dr_wav_init_memory_write_sequential(ma_dr_wav* pWav, void** ppData, size_t* pDataSize, const ma_dr_wav_data_format* pFormat, ma_uint64 totalSampleCount, const ma_allocation_callbacks* pAllocationCallbacks); -MA_API ma_bool32 ma_dr_wav_init_memory_write_sequential_pcm_frames(ma_dr_wav* pWav, void** ppData, size_t* pDataSize, const ma_dr_wav_data_format* pFormat, ma_uint64 totalPCMFrameCount, const ma_allocation_callbacks* pAllocationCallbacks); -#ifndef MA_DR_WAV_NO_CONVERSION_API -MA_API ma_int16* ma_dr_wav_open_and_read_pcm_frames_s16(ma_dr_wav_read_proc onRead, ma_dr_wav_seek_proc onSeek, void* pUserData, unsigned int* channelsOut, unsigned int* sampleRateOut, ma_uint64* totalFrameCountOut, const ma_allocation_callbacks* pAllocationCallbacks); -MA_API float* ma_dr_wav_open_and_read_pcm_frames_f32(ma_dr_wav_read_proc onRead, ma_dr_wav_seek_proc onSeek, void* pUserData, unsigned int* channelsOut, unsigned int* sampleRateOut, ma_uint64* totalFrameCountOut, const ma_allocation_callbacks* pAllocationCallbacks); -MA_API ma_int32* ma_dr_wav_open_and_read_pcm_frames_s32(ma_dr_wav_read_proc onRead, ma_dr_wav_seek_proc onSeek, void* pUserData, unsigned int* channelsOut, unsigned int* sampleRateOut, ma_uint64* totalFrameCountOut, const ma_allocation_callbacks* pAllocationCallbacks); -#ifndef MA_DR_WAV_NO_STDIO -MA_API ma_int16* ma_dr_wav_open_file_and_read_pcm_frames_s16(const char* filename, unsigned int* channelsOut, unsigned int* sampleRateOut, ma_uint64* totalFrameCountOut, const ma_allocation_callbacks* pAllocationCallbacks); -MA_API float* ma_dr_wav_open_file_and_read_pcm_frames_f32(const char* filename, unsigned int* channelsOut, unsigned int* sampleRateOut, ma_uint64* totalFrameCountOut, const ma_allocation_callbacks* pAllocationCallbacks); -MA_API ma_int32* ma_dr_wav_open_file_and_read_pcm_frames_s32(const char* filename, unsigned int* channelsOut, unsigned int* sampleRateOut, ma_uint64* totalFrameCountOut, const ma_allocation_callbacks* pAllocationCallbacks); -MA_API ma_int16* ma_dr_wav_open_file_and_read_pcm_frames_s16_w(const wchar_t* filename, unsigned int* channelsOut, unsigned int* sampleRateOut, ma_uint64* totalFrameCountOut, const ma_allocation_callbacks* pAllocationCallbacks); -MA_API float* ma_dr_wav_open_file_and_read_pcm_frames_f32_w(const wchar_t* filename, unsigned int* channelsOut, unsigned int* sampleRateOut, ma_uint64* totalFrameCountOut, const ma_allocation_callbacks* pAllocationCallbacks); -MA_API ma_int32* ma_dr_wav_open_file_and_read_pcm_frames_s32_w(const wchar_t* filename, unsigned int* channelsOut, unsigned int* sampleRateOut, ma_uint64* totalFrameCountOut, const ma_allocation_callbacks* pAllocationCallbacks); -#endif -MA_API ma_int16* ma_dr_wav_open_memory_and_read_pcm_frames_s16(const void* data, size_t dataSize, unsigned int* channelsOut, unsigned int* sampleRateOut, ma_uint64* totalFrameCountOut, const ma_allocation_callbacks* pAllocationCallbacks); -MA_API float* ma_dr_wav_open_memory_and_read_pcm_frames_f32(const void* data, size_t dataSize, unsigned int* channelsOut, unsigned int* sampleRateOut, ma_uint64* totalFrameCountOut, const ma_allocation_callbacks* pAllocationCallbacks); -MA_API ma_int32* ma_dr_wav_open_memory_and_read_pcm_frames_s32(const void* data, size_t dataSize, unsigned int* channelsOut, unsigned int* sampleRateOut, ma_uint64* totalFrameCountOut, const ma_allocation_callbacks* pAllocationCallbacks); -#endif -MA_API void ma_dr_wav_free(void* p, const ma_allocation_callbacks* pAllocationCallbacks); -MA_API ma_uint16 ma_dr_wav_bytes_to_u16(const ma_uint8* data); -MA_API ma_int16 ma_dr_wav_bytes_to_s16(const ma_uint8* data); -MA_API ma_uint32 ma_dr_wav_bytes_to_u32(const ma_uint8* data); -MA_API ma_int32 ma_dr_wav_bytes_to_s32(const ma_uint8* data); -MA_API ma_uint64 ma_dr_wav_bytes_to_u64(const ma_uint8* data); -MA_API ma_int64 ma_dr_wav_bytes_to_s64(const ma_uint8* data); -MA_API float ma_dr_wav_bytes_to_f32(const ma_uint8* data); -MA_API ma_bool32 ma_dr_wav_guid_equal(const ma_uint8 a[16], const ma_uint8 b[16]); -MA_API ma_bool32 ma_dr_wav_fourcc_equal(const ma_uint8* a, const char* b); -#ifdef __cplusplus -} -#endif -#endif -/* dr_wav_h end */ -#endif /* MA_NO_WAV */ - -#if !defined(MA_NO_FLAC) && !defined(MA_NO_DECODING) -/* dr_flac_h begin */ -#ifndef ma_dr_flac_h -#define ma_dr_flac_h -#ifdef __cplusplus -extern "C" { -#endif -#define MA_DR_FLAC_STRINGIFY(x) #x -#define MA_DR_FLAC_XSTRINGIFY(x) MA_DR_FLAC_STRINGIFY(x) -#define MA_DR_FLAC_VERSION_MAJOR 0 -#define MA_DR_FLAC_VERSION_MINOR 12 -#define MA_DR_FLAC_VERSION_REVISION 43 -#define MA_DR_FLAC_VERSION_STRING MA_DR_FLAC_XSTRINGIFY(MA_DR_FLAC_VERSION_MAJOR) "." MA_DR_FLAC_XSTRINGIFY(MA_DR_FLAC_VERSION_MINOR) "." MA_DR_FLAC_XSTRINGIFY(MA_DR_FLAC_VERSION_REVISION) -#include -#if defined(_MSC_VER) && _MSC_VER >= 1700 - #define MA_DR_FLAC_DEPRECATED __declspec(deprecated) -#elif (defined(__GNUC__) && __GNUC__ >= 4) - #define MA_DR_FLAC_DEPRECATED __attribute__((deprecated)) -#elif defined(__has_feature) - #if __has_feature(attribute_deprecated) - #define MA_DR_FLAC_DEPRECATED __attribute__((deprecated)) - #else - #define MA_DR_FLAC_DEPRECATED - #endif -#else - #define MA_DR_FLAC_DEPRECATED -#endif -MA_API void ma_dr_flac_version(ma_uint32* pMajor, ma_uint32* pMinor, ma_uint32* pRevision); -MA_API const char* ma_dr_flac_version_string(void); -#ifndef MA_DR_FLAC_BUFFER_SIZE -#define MA_DR_FLAC_BUFFER_SIZE 4096 -#endif -#ifdef MA_64BIT -typedef ma_uint64 ma_dr_flac_cache_t; -#else -typedef ma_uint32 ma_dr_flac_cache_t; -#endif -#define MA_DR_FLAC_METADATA_BLOCK_TYPE_STREAMINFO 0 -#define MA_DR_FLAC_METADATA_BLOCK_TYPE_PADDING 1 -#define MA_DR_FLAC_METADATA_BLOCK_TYPE_APPLICATION 2 -#define MA_DR_FLAC_METADATA_BLOCK_TYPE_SEEKTABLE 3 -#define MA_DR_FLAC_METADATA_BLOCK_TYPE_VORBIS_COMMENT 4 -#define MA_DR_FLAC_METADATA_BLOCK_TYPE_CUESHEET 5 -#define MA_DR_FLAC_METADATA_BLOCK_TYPE_PICTURE 6 -#define MA_DR_FLAC_METADATA_BLOCK_TYPE_INVALID 127 -#define MA_DR_FLAC_PICTURE_TYPE_OTHER 0 -#define MA_DR_FLAC_PICTURE_TYPE_FILE_ICON 1 -#define MA_DR_FLAC_PICTURE_TYPE_OTHER_FILE_ICON 2 -#define MA_DR_FLAC_PICTURE_TYPE_COVER_FRONT 3 -#define MA_DR_FLAC_PICTURE_TYPE_COVER_BACK 4 -#define MA_DR_FLAC_PICTURE_TYPE_LEAFLET_PAGE 5 -#define MA_DR_FLAC_PICTURE_TYPE_MEDIA 6 -#define MA_DR_FLAC_PICTURE_TYPE_LEAD_ARTIST 7 -#define MA_DR_FLAC_PICTURE_TYPE_ARTIST 8 -#define MA_DR_FLAC_PICTURE_TYPE_CONDUCTOR 9 -#define MA_DR_FLAC_PICTURE_TYPE_BAND 10 -#define MA_DR_FLAC_PICTURE_TYPE_COMPOSER 11 -#define MA_DR_FLAC_PICTURE_TYPE_LYRICIST 12 -#define MA_DR_FLAC_PICTURE_TYPE_RECORDING_LOCATION 13 -#define MA_DR_FLAC_PICTURE_TYPE_DURING_RECORDING 14 -#define MA_DR_FLAC_PICTURE_TYPE_DURING_PERFORMANCE 15 -#define MA_DR_FLAC_PICTURE_TYPE_SCREEN_CAPTURE 16 -#define MA_DR_FLAC_PICTURE_TYPE_BRIGHT_COLORED_FISH 17 -#define MA_DR_FLAC_PICTURE_TYPE_ILLUSTRATION 18 -#define MA_DR_FLAC_PICTURE_TYPE_BAND_LOGOTYPE 19 -#define MA_DR_FLAC_PICTURE_TYPE_PUBLISHER_LOGOTYPE 20 -typedef enum -{ - ma_dr_flac_container_native, - ma_dr_flac_container_ogg, - ma_dr_flac_container_unknown -} ma_dr_flac_container; -typedef enum -{ - ma_dr_flac_seek_origin_start, - ma_dr_flac_seek_origin_current -} ma_dr_flac_seek_origin; -typedef struct -{ - ma_uint64 firstPCMFrame; - ma_uint64 flacFrameOffset; - ma_uint16 pcmFrameCount; -} ma_dr_flac_seekpoint; -typedef struct -{ - ma_uint16 minBlockSizeInPCMFrames; - ma_uint16 maxBlockSizeInPCMFrames; - ma_uint32 minFrameSizeInPCMFrames; - ma_uint32 maxFrameSizeInPCMFrames; - ma_uint32 sampleRate; - ma_uint8 channels; - ma_uint8 bitsPerSample; - ma_uint64 totalPCMFrameCount; - ma_uint8 md5[16]; -} ma_dr_flac_streaminfo; -typedef struct -{ - ma_uint32 type; - const void* pRawData; - ma_uint32 rawDataSize; - union - { - ma_dr_flac_streaminfo streaminfo; - struct - { - int unused; - } padding; - struct - { - ma_uint32 id; - const void* pData; - ma_uint32 dataSize; - } application; - struct - { - ma_uint32 seekpointCount; - const ma_dr_flac_seekpoint* pSeekpoints; - } seektable; - struct - { - ma_uint32 vendorLength; - const char* vendor; - ma_uint32 commentCount; - const void* pComments; - } vorbis_comment; - struct - { - char catalog[128]; - ma_uint64 leadInSampleCount; - ma_bool32 isCD; - ma_uint8 trackCount; - const void* pTrackData; - } cuesheet; - struct - { - ma_uint32 type; - ma_uint32 mimeLength; - const char* mime; - ma_uint32 descriptionLength; - const char* description; - ma_uint32 width; - ma_uint32 height; - ma_uint32 colorDepth; - ma_uint32 indexColorCount; - ma_uint32 pictureDataSize; - const ma_uint8* pPictureData; - } picture; - } data; -} ma_dr_flac_metadata; -typedef size_t (* ma_dr_flac_read_proc)(void* pUserData, void* pBufferOut, size_t bytesToRead); -typedef ma_bool32 (* ma_dr_flac_seek_proc)(void* pUserData, int offset, ma_dr_flac_seek_origin origin); -typedef void (* ma_dr_flac_meta_proc)(void* pUserData, ma_dr_flac_metadata* pMetadata); -typedef struct -{ - const ma_uint8* data; - size_t dataSize; - size_t currentReadPos; -} ma_dr_flac__memory_stream; -typedef struct -{ - ma_dr_flac_read_proc onRead; - ma_dr_flac_seek_proc onSeek; - void* pUserData; - size_t unalignedByteCount; - ma_dr_flac_cache_t unalignedCache; - ma_uint32 nextL2Line; - ma_uint32 consumedBits; - ma_dr_flac_cache_t cacheL2[MA_DR_FLAC_BUFFER_SIZE/sizeof(ma_dr_flac_cache_t)]; - ma_dr_flac_cache_t cache; - ma_uint16 crc16; - ma_dr_flac_cache_t crc16Cache; - ma_uint32 crc16CacheIgnoredBytes; -} ma_dr_flac_bs; -typedef struct -{ - ma_uint8 subframeType; - ma_uint8 wastedBitsPerSample; - ma_uint8 lpcOrder; - ma_int32* pSamplesS32; -} ma_dr_flac_subframe; -typedef struct -{ - ma_uint64 pcmFrameNumber; - ma_uint32 flacFrameNumber; - ma_uint32 sampleRate; - ma_uint16 blockSizeInPCMFrames; - ma_uint8 channelAssignment; - ma_uint8 bitsPerSample; - ma_uint8 crc8; -} ma_dr_flac_frame_header; -typedef struct -{ - ma_dr_flac_frame_header header; - ma_uint32 pcmFramesRemaining; - ma_dr_flac_subframe subframes[8]; -} ma_dr_flac_frame; -typedef struct -{ - ma_dr_flac_meta_proc onMeta; - void* pUserDataMD; - ma_allocation_callbacks allocationCallbacks; - ma_uint32 sampleRate; - ma_uint8 channels; - ma_uint8 bitsPerSample; - ma_uint16 maxBlockSizeInPCMFrames; - ma_uint64 totalPCMFrameCount; - ma_dr_flac_container container; - ma_uint32 seekpointCount; - ma_dr_flac_frame currentFLACFrame; - ma_uint64 currentPCMFrame; - ma_uint64 firstFLACFramePosInBytes; - ma_dr_flac__memory_stream memoryStream; - ma_int32* pDecodedSamples; - ma_dr_flac_seekpoint* pSeekpoints; - void* _oggbs; - ma_bool32 _noSeekTableSeek : 1; - ma_bool32 _noBinarySearchSeek : 1; - ma_bool32 _noBruteForceSeek : 1; - ma_dr_flac_bs bs; - ma_uint8 pExtraData[1]; -} ma_dr_flac; -MA_API ma_dr_flac* ma_dr_flac_open(ma_dr_flac_read_proc onRead, ma_dr_flac_seek_proc onSeek, void* pUserData, const ma_allocation_callbacks* pAllocationCallbacks); -MA_API ma_dr_flac* ma_dr_flac_open_relaxed(ma_dr_flac_read_proc onRead, ma_dr_flac_seek_proc onSeek, ma_dr_flac_container container, void* pUserData, const ma_allocation_callbacks* pAllocationCallbacks); -MA_API ma_dr_flac* ma_dr_flac_open_with_metadata(ma_dr_flac_read_proc onRead, ma_dr_flac_seek_proc onSeek, ma_dr_flac_meta_proc onMeta, void* pUserData, const ma_allocation_callbacks* pAllocationCallbacks); -MA_API ma_dr_flac* ma_dr_flac_open_with_metadata_relaxed(ma_dr_flac_read_proc onRead, ma_dr_flac_seek_proc onSeek, ma_dr_flac_meta_proc onMeta, ma_dr_flac_container container, void* pUserData, const ma_allocation_callbacks* pAllocationCallbacks); -MA_API void ma_dr_flac_close(ma_dr_flac* pFlac); -MA_API ma_uint64 ma_dr_flac_read_pcm_frames_s32(ma_dr_flac* pFlac, ma_uint64 framesToRead, ma_int32* pBufferOut); -MA_API ma_uint64 ma_dr_flac_read_pcm_frames_s16(ma_dr_flac* pFlac, ma_uint64 framesToRead, ma_int16* pBufferOut); -MA_API ma_uint64 ma_dr_flac_read_pcm_frames_f32(ma_dr_flac* pFlac, ma_uint64 framesToRead, float* pBufferOut); -MA_API ma_bool32 ma_dr_flac_seek_to_pcm_frame(ma_dr_flac* pFlac, ma_uint64 pcmFrameIndex); -#ifndef MA_DR_FLAC_NO_STDIO -MA_API ma_dr_flac* ma_dr_flac_open_file(const char* pFileName, const ma_allocation_callbacks* pAllocationCallbacks); -MA_API ma_dr_flac* ma_dr_flac_open_file_w(const wchar_t* pFileName, const ma_allocation_callbacks* pAllocationCallbacks); -MA_API ma_dr_flac* ma_dr_flac_open_file_with_metadata(const char* pFileName, ma_dr_flac_meta_proc onMeta, void* pUserData, const ma_allocation_callbacks* pAllocationCallbacks); -MA_API ma_dr_flac* ma_dr_flac_open_file_with_metadata_w(const wchar_t* pFileName, ma_dr_flac_meta_proc onMeta, void* pUserData, const ma_allocation_callbacks* pAllocationCallbacks); -#endif -MA_API ma_dr_flac* ma_dr_flac_open_memory(const void* pData, size_t dataSize, const ma_allocation_callbacks* pAllocationCallbacks); -MA_API ma_dr_flac* ma_dr_flac_open_memory_with_metadata(const void* pData, size_t dataSize, ma_dr_flac_meta_proc onMeta, void* pUserData, const ma_allocation_callbacks* pAllocationCallbacks); -MA_API ma_int32* ma_dr_flac_open_and_read_pcm_frames_s32(ma_dr_flac_read_proc onRead, ma_dr_flac_seek_proc onSeek, void* pUserData, unsigned int* channels, unsigned int* sampleRate, ma_uint64* totalPCMFrameCount, const ma_allocation_callbacks* pAllocationCallbacks); -MA_API ma_int16* ma_dr_flac_open_and_read_pcm_frames_s16(ma_dr_flac_read_proc onRead, ma_dr_flac_seek_proc onSeek, void* pUserData, unsigned int* channels, unsigned int* sampleRate, ma_uint64* totalPCMFrameCount, const ma_allocation_callbacks* pAllocationCallbacks); -MA_API float* ma_dr_flac_open_and_read_pcm_frames_f32(ma_dr_flac_read_proc onRead, ma_dr_flac_seek_proc onSeek, void* pUserData, unsigned int* channels, unsigned int* sampleRate, ma_uint64* totalPCMFrameCount, const ma_allocation_callbacks* pAllocationCallbacks); -#ifndef MA_DR_FLAC_NO_STDIO -MA_API ma_int32* ma_dr_flac_open_file_and_read_pcm_frames_s32(const char* filename, unsigned int* channels, unsigned int* sampleRate, ma_uint64* totalPCMFrameCount, const ma_allocation_callbacks* pAllocationCallbacks); -MA_API ma_int16* ma_dr_flac_open_file_and_read_pcm_frames_s16(const char* filename, unsigned int* channels, unsigned int* sampleRate, ma_uint64* totalPCMFrameCount, const ma_allocation_callbacks* pAllocationCallbacks); -MA_API float* ma_dr_flac_open_file_and_read_pcm_frames_f32(const char* filename, unsigned int* channels, unsigned int* sampleRate, ma_uint64* totalPCMFrameCount, const ma_allocation_callbacks* pAllocationCallbacks); -#endif -MA_API ma_int32* ma_dr_flac_open_memory_and_read_pcm_frames_s32(const void* data, size_t dataSize, unsigned int* channels, unsigned int* sampleRate, ma_uint64* totalPCMFrameCount, const ma_allocation_callbacks* pAllocationCallbacks); -MA_API ma_int16* ma_dr_flac_open_memory_and_read_pcm_frames_s16(const void* data, size_t dataSize, unsigned int* channels, unsigned int* sampleRate, ma_uint64* totalPCMFrameCount, const ma_allocation_callbacks* pAllocationCallbacks); -MA_API float* ma_dr_flac_open_memory_and_read_pcm_frames_f32(const void* data, size_t dataSize, unsigned int* channels, unsigned int* sampleRate, ma_uint64* totalPCMFrameCount, const ma_allocation_callbacks* pAllocationCallbacks); -MA_API void ma_dr_flac_free(void* p, const ma_allocation_callbacks* pAllocationCallbacks); -typedef struct -{ - ma_uint32 countRemaining; - const char* pRunningData; -} ma_dr_flac_vorbis_comment_iterator; -MA_API void ma_dr_flac_init_vorbis_comment_iterator(ma_dr_flac_vorbis_comment_iterator* pIter, ma_uint32 commentCount, const void* pComments); -MA_API const char* ma_dr_flac_next_vorbis_comment(ma_dr_flac_vorbis_comment_iterator* pIter, ma_uint32* pCommentLengthOut); -typedef struct -{ - ma_uint32 countRemaining; - const char* pRunningData; -} ma_dr_flac_cuesheet_track_iterator; -typedef struct -{ - ma_uint64 offset; - ma_uint8 index; - ma_uint8 reserved[3]; -} ma_dr_flac_cuesheet_track_index; -typedef struct -{ - ma_uint64 offset; - ma_uint8 trackNumber; - char ISRC[12]; - ma_bool8 isAudio; - ma_bool8 preEmphasis; - ma_uint8 indexCount; - const ma_dr_flac_cuesheet_track_index* pIndexPoints; -} ma_dr_flac_cuesheet_track; -MA_API void ma_dr_flac_init_cuesheet_track_iterator(ma_dr_flac_cuesheet_track_iterator* pIter, ma_uint32 trackCount, const void* pTrackData); -MA_API ma_bool32 ma_dr_flac_next_cuesheet_track(ma_dr_flac_cuesheet_track_iterator* pIter, ma_dr_flac_cuesheet_track* pCuesheetTrack); -#ifdef __cplusplus -} -#endif -#endif -/* dr_flac_h end */ -#endif /* MA_NO_FLAC */ - -#if !defined(MA_NO_MP3) && !defined(MA_NO_DECODING) -/* dr_mp3_h begin */ -#ifndef ma_dr_mp3_h -#define ma_dr_mp3_h -#ifdef __cplusplus -extern "C" { -#endif -#define MA_DR_MP3_STRINGIFY(x) #x -#define MA_DR_MP3_XSTRINGIFY(x) MA_DR_MP3_STRINGIFY(x) -#define MA_DR_MP3_VERSION_MAJOR 0 -#define MA_DR_MP3_VERSION_MINOR 6 -#define MA_DR_MP3_VERSION_REVISION 40 -#define MA_DR_MP3_VERSION_STRING MA_DR_MP3_XSTRINGIFY(MA_DR_MP3_VERSION_MAJOR) "." MA_DR_MP3_XSTRINGIFY(MA_DR_MP3_VERSION_MINOR) "." MA_DR_MP3_XSTRINGIFY(MA_DR_MP3_VERSION_REVISION) -#include -#define MA_DR_MP3_MAX_PCM_FRAMES_PER_MP3_FRAME 1152 -#define MA_DR_MP3_MAX_SAMPLES_PER_FRAME (MA_DR_MP3_MAX_PCM_FRAMES_PER_MP3_FRAME*2) -MA_API void ma_dr_mp3_version(ma_uint32* pMajor, ma_uint32* pMinor, ma_uint32* pRevision); -MA_API const char* ma_dr_mp3_version_string(void); -typedef struct -{ - int frame_bytes, channels, hz, layer, bitrate_kbps; -} ma_dr_mp3dec_frame_info; -typedef struct -{ - float mdct_overlap[2][9*32], qmf_state[15*2*32]; - int reserv, free_format_bytes; - ma_uint8 header[4], reserv_buf[511]; -} ma_dr_mp3dec; -MA_API void ma_dr_mp3dec_init(ma_dr_mp3dec *dec); -MA_API int ma_dr_mp3dec_decode_frame(ma_dr_mp3dec *dec, const ma_uint8 *mp3, int mp3_bytes, void *pcm, ma_dr_mp3dec_frame_info *info); -MA_API void ma_dr_mp3dec_f32_to_s16(const float *in, ma_int16 *out, size_t num_samples); -typedef enum -{ - ma_dr_mp3_seek_origin_start, - ma_dr_mp3_seek_origin_current -} ma_dr_mp3_seek_origin; -typedef struct -{ - ma_uint64 seekPosInBytes; - ma_uint64 pcmFrameIndex; - ma_uint16 mp3FramesToDiscard; - ma_uint16 pcmFramesToDiscard; -} ma_dr_mp3_seek_point; -typedef size_t (* ma_dr_mp3_read_proc)(void* pUserData, void* pBufferOut, size_t bytesToRead); -typedef ma_bool32 (* ma_dr_mp3_seek_proc)(void* pUserData, int offset, ma_dr_mp3_seek_origin origin); -typedef struct -{ - ma_uint32 channels; - ma_uint32 sampleRate; -} ma_dr_mp3_config; -typedef struct -{ - ma_dr_mp3dec decoder; - ma_uint32 channels; - ma_uint32 sampleRate; - ma_dr_mp3_read_proc onRead; - ma_dr_mp3_seek_proc onSeek; - void* pUserData; - ma_allocation_callbacks allocationCallbacks; - ma_uint32 mp3FrameChannels; - ma_uint32 mp3FrameSampleRate; - ma_uint32 pcmFramesConsumedInMP3Frame; - ma_uint32 pcmFramesRemainingInMP3Frame; - ma_uint8 pcmFrames[sizeof(float)*MA_DR_MP3_MAX_SAMPLES_PER_FRAME]; - ma_uint64 currentPCMFrame; - ma_uint64 streamCursor; - ma_dr_mp3_seek_point* pSeekPoints; - ma_uint32 seekPointCount; - size_t dataSize; - size_t dataCapacity; - size_t dataConsumed; - ma_uint8* pData; - ma_bool32 atEnd : 1; - struct - { - const ma_uint8* pData; - size_t dataSize; - size_t currentReadPos; - } memory; -} ma_dr_mp3; -MA_API ma_bool32 ma_dr_mp3_init(ma_dr_mp3* pMP3, ma_dr_mp3_read_proc onRead, ma_dr_mp3_seek_proc onSeek, void* pUserData, const ma_allocation_callbacks* pAllocationCallbacks); -MA_API ma_bool32 ma_dr_mp3_init_memory(ma_dr_mp3* pMP3, const void* pData, size_t dataSize, const ma_allocation_callbacks* pAllocationCallbacks); -#ifndef MA_DR_MP3_NO_STDIO -MA_API ma_bool32 ma_dr_mp3_init_file(ma_dr_mp3* pMP3, const char* pFilePath, const ma_allocation_callbacks* pAllocationCallbacks); -MA_API ma_bool32 ma_dr_mp3_init_file_w(ma_dr_mp3* pMP3, const wchar_t* pFilePath, const ma_allocation_callbacks* pAllocationCallbacks); -#endif -MA_API void ma_dr_mp3_uninit(ma_dr_mp3* pMP3); -MA_API ma_uint64 ma_dr_mp3_read_pcm_frames_f32(ma_dr_mp3* pMP3, ma_uint64 framesToRead, float* pBufferOut); -MA_API ma_uint64 ma_dr_mp3_read_pcm_frames_s16(ma_dr_mp3* pMP3, ma_uint64 framesToRead, ma_int16* pBufferOut); -MA_API ma_bool32 ma_dr_mp3_seek_to_pcm_frame(ma_dr_mp3* pMP3, ma_uint64 frameIndex); -MA_API ma_uint64 ma_dr_mp3_get_pcm_frame_count(ma_dr_mp3* pMP3); -MA_API ma_uint64 ma_dr_mp3_get_mp3_frame_count(ma_dr_mp3* pMP3); -MA_API ma_bool32 ma_dr_mp3_get_mp3_and_pcm_frame_count(ma_dr_mp3* pMP3, ma_uint64* pMP3FrameCount, ma_uint64* pPCMFrameCount); -MA_API ma_bool32 ma_dr_mp3_calculate_seek_points(ma_dr_mp3* pMP3, ma_uint32* pSeekPointCount, ma_dr_mp3_seek_point* pSeekPoints); -MA_API ma_bool32 ma_dr_mp3_bind_seek_table(ma_dr_mp3* pMP3, ma_uint32 seekPointCount, ma_dr_mp3_seek_point* pSeekPoints); -MA_API float* ma_dr_mp3_open_and_read_pcm_frames_f32(ma_dr_mp3_read_proc onRead, ma_dr_mp3_seek_proc onSeek, void* pUserData, ma_dr_mp3_config* pConfig, ma_uint64* pTotalFrameCount, const ma_allocation_callbacks* pAllocationCallbacks); -MA_API ma_int16* ma_dr_mp3_open_and_read_pcm_frames_s16(ma_dr_mp3_read_proc onRead, ma_dr_mp3_seek_proc onSeek, void* pUserData, ma_dr_mp3_config* pConfig, ma_uint64* pTotalFrameCount, const ma_allocation_callbacks* pAllocationCallbacks); -MA_API float* ma_dr_mp3_open_memory_and_read_pcm_frames_f32(const void* pData, size_t dataSize, ma_dr_mp3_config* pConfig, ma_uint64* pTotalFrameCount, const ma_allocation_callbacks* pAllocationCallbacks); -MA_API ma_int16* ma_dr_mp3_open_memory_and_read_pcm_frames_s16(const void* pData, size_t dataSize, ma_dr_mp3_config* pConfig, ma_uint64* pTotalFrameCount, const ma_allocation_callbacks* pAllocationCallbacks); -#ifndef MA_DR_MP3_NO_STDIO -MA_API float* ma_dr_mp3_open_file_and_read_pcm_frames_f32(const char* filePath, ma_dr_mp3_config* pConfig, ma_uint64* pTotalFrameCount, const ma_allocation_callbacks* pAllocationCallbacks); -MA_API ma_int16* ma_dr_mp3_open_file_and_read_pcm_frames_s16(const char* filePath, ma_dr_mp3_config* pConfig, ma_uint64* pTotalFrameCount, const ma_allocation_callbacks* pAllocationCallbacks); -#endif -MA_API void* ma_dr_mp3_malloc(size_t sz, const ma_allocation_callbacks* pAllocationCallbacks); -MA_API void ma_dr_mp3_free(void* p, const ma_allocation_callbacks* pAllocationCallbacks); -#ifdef __cplusplus -} -#endif -#endif -/* dr_mp3_h end */ -#endif /* MA_NO_MP3 */ - - -/************************************************************************************************************************************************************** - -Decoding - -**************************************************************************************************************************************************************/ -#ifndef MA_NO_DECODING - -static ma_result ma_decoder_read_bytes(ma_decoder* pDecoder, void* pBufferOut, size_t bytesToRead, size_t* pBytesRead) -{ - MA_ASSERT(pDecoder != NULL); - - return pDecoder->onRead(pDecoder, pBufferOut, bytesToRead, pBytesRead); -} - -static ma_result ma_decoder_seek_bytes(ma_decoder* pDecoder, ma_int64 byteOffset, ma_seek_origin origin) -{ - MA_ASSERT(pDecoder != NULL); - - return pDecoder->onSeek(pDecoder, byteOffset, origin); -} - -static ma_result ma_decoder_tell_bytes(ma_decoder* pDecoder, ma_int64* pCursor) -{ - MA_ASSERT(pDecoder != NULL); - - if (pDecoder->onTell == NULL) { - return MA_NOT_IMPLEMENTED; - } - - return pDecoder->onTell(pDecoder, pCursor); -} - - -MA_API ma_decoding_backend_config ma_decoding_backend_config_init(ma_format preferredFormat, ma_uint32 seekPointCount) -{ - ma_decoding_backend_config config; - - MA_ZERO_OBJECT(&config); - config.preferredFormat = preferredFormat; - config.seekPointCount = seekPointCount; - - return config; -} - - -MA_API ma_decoder_config ma_decoder_config_init(ma_format outputFormat, ma_uint32 outputChannels, ma_uint32 outputSampleRate) -{ - ma_decoder_config config; - MA_ZERO_OBJECT(&config); - config.format = outputFormat; - config.channels = outputChannels; - config.sampleRate = outputSampleRate; - config.resampling = ma_resampler_config_init(ma_format_unknown, 0, 0, 0, ma_resample_algorithm_linear); /* Format/channels/rate doesn't matter here. */ - config.encodingFormat = ma_encoding_format_unknown; - - /* Note that we are intentionally leaving the channel map empty here which will cause the default channel map to be used. */ - - return config; -} - -MA_API ma_decoder_config ma_decoder_config_init_default(void) -{ - return ma_decoder_config_init(ma_format_unknown, 0, 0); -} - -MA_API ma_decoder_config ma_decoder_config_init_copy(const ma_decoder_config* pConfig) -{ - ma_decoder_config config; - if (pConfig != NULL) { - config = *pConfig; - } else { - MA_ZERO_OBJECT(&config); - } - - return config; -} - -static ma_result ma_decoder__init_data_converter(ma_decoder* pDecoder, const ma_decoder_config* pConfig) -{ - ma_result result; - ma_data_converter_config converterConfig; - ma_format internalFormat; - ma_uint32 internalChannels; - ma_uint32 internalSampleRate; - ma_channel internalChannelMap[MA_MAX_CHANNELS]; - - MA_ASSERT(pDecoder != NULL); - MA_ASSERT(pConfig != NULL); - - result = ma_data_source_get_data_format(pDecoder->pBackend, &internalFormat, &internalChannels, &internalSampleRate, internalChannelMap, ma_countof(internalChannelMap)); - if (result != MA_SUCCESS) { - return result; /* Failed to retrieve the internal data format. */ - } - - - /* Make sure we're not asking for too many channels. */ - if (pConfig->channels > MA_MAX_CHANNELS) { - return MA_INVALID_ARGS; - } - - /* The internal channels should have already been validated at a higher level, but we'll do it again explicitly here for safety. */ - if (internalChannels > MA_MAX_CHANNELS) { - return MA_INVALID_ARGS; - } - - - /* Output format. */ - if (pConfig->format == ma_format_unknown) { - pDecoder->outputFormat = internalFormat; - } else { - pDecoder->outputFormat = pConfig->format; - } - - if (pConfig->channels == 0) { - pDecoder->outputChannels = internalChannels; - } else { - pDecoder->outputChannels = pConfig->channels; - } - - if (pConfig->sampleRate == 0) { - pDecoder->outputSampleRate = internalSampleRate; - } else { - pDecoder->outputSampleRate = pConfig->sampleRate; - } - - converterConfig = ma_data_converter_config_init( - internalFormat, pDecoder->outputFormat, - internalChannels, pDecoder->outputChannels, - internalSampleRate, pDecoder->outputSampleRate - ); - converterConfig.pChannelMapIn = internalChannelMap; - converterConfig.pChannelMapOut = pConfig->pChannelMap; - converterConfig.channelMixMode = pConfig->channelMixMode; - converterConfig.ditherMode = pConfig->ditherMode; - converterConfig.allowDynamicSampleRate = MA_FALSE; /* Never allow dynamic sample rate conversion. Setting this to true will disable passthrough optimizations. */ - converterConfig.resampling = pConfig->resampling; - - result = ma_data_converter_init(&converterConfig, &pDecoder->allocationCallbacks, &pDecoder->converter); - if (result != MA_SUCCESS) { - return result; - } - - /* - Now that we have the decoder we need to determine whether or not we need a heap-allocated cache. We'll - need this if the data converter does not support calculation of the required input frame count. To - determine support for this we'll just run a test. - */ - { - ma_uint64 unused; - - result = ma_data_converter_get_required_input_frame_count(&pDecoder->converter, 1, &unused); - if (result != MA_SUCCESS) { - /* - We were unable to calculate the required input frame count which means we'll need to use - a heap-allocated cache. - */ - ma_uint64 inputCacheCapSizeInBytes; - - pDecoder->inputCacheCap = MA_DATA_CONVERTER_STACK_BUFFER_SIZE / ma_get_bytes_per_frame(internalFormat, internalChannels); - - /* Not strictly necessary, but keeping here for safety in case we change the default value of pDecoder->inputCacheCap. */ - inputCacheCapSizeInBytes = pDecoder->inputCacheCap * ma_get_bytes_per_frame(internalFormat, internalChannels); - if (inputCacheCapSizeInBytes > MA_SIZE_MAX) { - ma_data_converter_uninit(&pDecoder->converter, &pDecoder->allocationCallbacks); - return MA_OUT_OF_MEMORY; - } - - pDecoder->pInputCache = ma_malloc((size_t)inputCacheCapSizeInBytes, &pDecoder->allocationCallbacks); /* Safe cast to size_t. */ - if (pDecoder->pInputCache == NULL) { - ma_data_converter_uninit(&pDecoder->converter, &pDecoder->allocationCallbacks); - return MA_OUT_OF_MEMORY; - } - } - } - - return MA_SUCCESS; -} - - - -static ma_result ma_decoder_internal_on_read__custom(void* pUserData, void* pBufferOut, size_t bytesToRead, size_t* pBytesRead) -{ - ma_decoder* pDecoder = (ma_decoder*)pUserData; - MA_ASSERT(pDecoder != NULL); - - return ma_decoder_read_bytes(pDecoder, pBufferOut, bytesToRead, pBytesRead); -} - -static ma_result ma_decoder_internal_on_seek__custom(void* pUserData, ma_int64 offset, ma_seek_origin origin) -{ - ma_decoder* pDecoder = (ma_decoder*)pUserData; - MA_ASSERT(pDecoder != NULL); - - return ma_decoder_seek_bytes(pDecoder, offset, origin); -} - -static ma_result ma_decoder_internal_on_tell__custom(void* pUserData, ma_int64* pCursor) -{ - ma_decoder* pDecoder = (ma_decoder*)pUserData; - MA_ASSERT(pDecoder != NULL); - - return ma_decoder_tell_bytes(pDecoder, pCursor); -} - - -static ma_result ma_decoder_init_from_vtable__internal(const ma_decoding_backend_vtable* pVTable, void* pVTableUserData, const ma_decoder_config* pConfig, ma_decoder* pDecoder) -{ - ma_result result; - ma_decoding_backend_config backendConfig; - ma_data_source* pBackend; - - MA_ASSERT(pVTable != NULL); - MA_ASSERT(pConfig != NULL); - MA_ASSERT(pDecoder != NULL); - - if (pVTable->onInit == NULL) { - return MA_NOT_IMPLEMENTED; - } - - backendConfig = ma_decoding_backend_config_init(pConfig->format, pConfig->seekPointCount); - - result = pVTable->onInit(pVTableUserData, ma_decoder_internal_on_read__custom, ma_decoder_internal_on_seek__custom, ma_decoder_internal_on_tell__custom, pDecoder, &backendConfig, &pDecoder->allocationCallbacks, &pBackend); - if (result != MA_SUCCESS) { - return result; /* Failed to initialize the backend from this vtable. */ - } - - /* Getting here means we were able to initialize the backend so we can now initialize the decoder. */ - pDecoder->pBackend = pBackend; - pDecoder->pBackendVTable = pVTable; - pDecoder->pBackendUserData = pConfig->pCustomBackendUserData; - - return MA_SUCCESS; -} - -static ma_result ma_decoder_init_from_file__internal(const ma_decoding_backend_vtable* pVTable, void* pVTableUserData, const char* pFilePath, const ma_decoder_config* pConfig, ma_decoder* pDecoder) -{ - ma_result result; - ma_decoding_backend_config backendConfig; - ma_data_source* pBackend; - - MA_ASSERT(pVTable != NULL); - MA_ASSERT(pConfig != NULL); - MA_ASSERT(pDecoder != NULL); - - if (pVTable->onInitFile == NULL) { - return MA_NOT_IMPLEMENTED; - } - - backendConfig = ma_decoding_backend_config_init(pConfig->format, pConfig->seekPointCount); - - result = pVTable->onInitFile(pVTableUserData, pFilePath, &backendConfig, &pDecoder->allocationCallbacks, &pBackend); - if (result != MA_SUCCESS) { - return result; /* Failed to initialize the backend from this vtable. */ - } - - /* Getting here means we were able to initialize the backend so we can now initialize the decoder. */ - pDecoder->pBackend = pBackend; - pDecoder->pBackendVTable = pVTable; - pDecoder->pBackendUserData = pConfig->pCustomBackendUserData; - - return MA_SUCCESS; -} - -static ma_result ma_decoder_init_from_file_w__internal(const ma_decoding_backend_vtable* pVTable, void* pVTableUserData, const wchar_t* pFilePath, const ma_decoder_config* pConfig, ma_decoder* pDecoder) -{ - ma_result result; - ma_decoding_backend_config backendConfig; - ma_data_source* pBackend; - - MA_ASSERT(pVTable != NULL); - MA_ASSERT(pConfig != NULL); - MA_ASSERT(pDecoder != NULL); - - if (pVTable->onInitFileW == NULL) { - return MA_NOT_IMPLEMENTED; - } - - backendConfig = ma_decoding_backend_config_init(pConfig->format, pConfig->seekPointCount); - - result = pVTable->onInitFileW(pVTableUserData, pFilePath, &backendConfig, &pDecoder->allocationCallbacks, &pBackend); - if (result != MA_SUCCESS) { - return result; /* Failed to initialize the backend from this vtable. */ - } - - /* Getting here means we were able to initialize the backend so we can now initialize the decoder. */ - pDecoder->pBackend = pBackend; - pDecoder->pBackendVTable = pVTable; - pDecoder->pBackendUserData = pConfig->pCustomBackendUserData; - - return MA_SUCCESS; -} - -static ma_result ma_decoder_init_from_memory__internal(const ma_decoding_backend_vtable* pVTable, void* pVTableUserData, const void* pData, size_t dataSize, const ma_decoder_config* pConfig, ma_decoder* pDecoder) -{ - ma_result result; - ma_decoding_backend_config backendConfig; - ma_data_source* pBackend; - - MA_ASSERT(pVTable != NULL); - MA_ASSERT(pConfig != NULL); - MA_ASSERT(pDecoder != NULL); - - if (pVTable->onInitMemory == NULL) { - return MA_NOT_IMPLEMENTED; - } - - backendConfig = ma_decoding_backend_config_init(pConfig->format, pConfig->seekPointCount); - - result = pVTable->onInitMemory(pVTableUserData, pData, dataSize, &backendConfig, &pDecoder->allocationCallbacks, &pBackend); - if (result != MA_SUCCESS) { - return result; /* Failed to initialize the backend from this vtable. */ - } - - /* Getting here means we were able to initialize the backend so we can now initialize the decoder. */ - pDecoder->pBackend = pBackend; - pDecoder->pBackendVTable = pVTable; - pDecoder->pBackendUserData = pConfig->pCustomBackendUserData; - - return MA_SUCCESS; -} - - - -static ma_result ma_decoder_init_custom__internal(const ma_decoder_config* pConfig, ma_decoder* pDecoder) -{ - ma_result result = MA_NO_BACKEND; - size_t ivtable; - - MA_ASSERT(pConfig != NULL); - MA_ASSERT(pDecoder != NULL); - - if (pConfig->ppCustomBackendVTables == NULL) { - return MA_NO_BACKEND; - } - - /* The order each backend is listed is what defines the priority. */ - for (ivtable = 0; ivtable < pConfig->customBackendCount; ivtable += 1) { - const ma_decoding_backend_vtable* pVTable = pConfig->ppCustomBackendVTables[ivtable]; - if (pVTable != NULL) { - result = ma_decoder_init_from_vtable__internal(pVTable, pConfig->pCustomBackendUserData, pConfig, pDecoder); - if (result == MA_SUCCESS) { - return MA_SUCCESS; - } else { - /* Initialization failed. Move on to the next one, but seek back to the start first so the next vtable starts from the first byte of the file. */ - result = ma_decoder_seek_bytes(pDecoder, 0, ma_seek_origin_start); - if (result != MA_SUCCESS) { - return result; /* Failed to seek back to the start. */ - } - } - } else { - /* No vtable. */ - } - } - - /* Getting here means we couldn't find a backend. */ - return MA_NO_BACKEND; -} - -static ma_result ma_decoder_init_custom_from_file__internal(const char* pFilePath, const ma_decoder_config* pConfig, ma_decoder* pDecoder) -{ - ma_result result = MA_NO_BACKEND; - size_t ivtable; - - MA_ASSERT(pConfig != NULL); - MA_ASSERT(pDecoder != NULL); - - if (pConfig->ppCustomBackendVTables == NULL) { - return MA_NO_BACKEND; - } - - /* The order each backend is listed is what defines the priority. */ - for (ivtable = 0; ivtable < pConfig->customBackendCount; ivtable += 1) { - const ma_decoding_backend_vtable* pVTable = pConfig->ppCustomBackendVTables[ivtable]; - if (pVTable != NULL) { - result = ma_decoder_init_from_file__internal(pVTable, pConfig->pCustomBackendUserData, pFilePath, pConfig, pDecoder); - if (result == MA_SUCCESS) { - return MA_SUCCESS; - } - } else { - /* No vtable. */ - } - } - - /* Getting here means we couldn't find a backend. */ - return MA_NO_BACKEND; -} - -static ma_result ma_decoder_init_custom_from_file_w__internal(const wchar_t* pFilePath, const ma_decoder_config* pConfig, ma_decoder* pDecoder) -{ - ma_result result = MA_NO_BACKEND; - size_t ivtable; - - MA_ASSERT(pConfig != NULL); - MA_ASSERT(pDecoder != NULL); - - if (pConfig->ppCustomBackendVTables == NULL) { - return MA_NO_BACKEND; - } - - /* The order each backend is listed is what defines the priority. */ - for (ivtable = 0; ivtable < pConfig->customBackendCount; ivtable += 1) { - const ma_decoding_backend_vtable* pVTable = pConfig->ppCustomBackendVTables[ivtable]; - if (pVTable != NULL) { - result = ma_decoder_init_from_file_w__internal(pVTable, pConfig->pCustomBackendUserData, pFilePath, pConfig, pDecoder); - if (result == MA_SUCCESS) { - return MA_SUCCESS; - } - } else { - /* No vtable. */ - } - } - - /* Getting here means we couldn't find a backend. */ - return MA_NO_BACKEND; -} - -static ma_result ma_decoder_init_custom_from_memory__internal(const void* pData, size_t dataSize, const ma_decoder_config* pConfig, ma_decoder* pDecoder) -{ - ma_result result = MA_NO_BACKEND; - size_t ivtable; - - MA_ASSERT(pConfig != NULL); - MA_ASSERT(pDecoder != NULL); - - if (pConfig->ppCustomBackendVTables == NULL) { - return MA_NO_BACKEND; - } - - /* The order each backend is listed is what defines the priority. */ - for (ivtable = 0; ivtable < pConfig->customBackendCount; ivtable += 1) { - const ma_decoding_backend_vtable* pVTable = pConfig->ppCustomBackendVTables[ivtable]; - if (pVTable != NULL) { - result = ma_decoder_init_from_memory__internal(pVTable, pConfig->pCustomBackendUserData, pData, dataSize, pConfig, pDecoder); - if (result == MA_SUCCESS) { - return MA_SUCCESS; - } - } else { - /* No vtable. */ - } - } - - /* Getting here means we couldn't find a backend. */ - return MA_NO_BACKEND; -} - - -/* WAV */ -#ifdef ma_dr_wav_h -#define MA_HAS_WAV - -typedef struct -{ - ma_data_source_base ds; - ma_read_proc onRead; - ma_seek_proc onSeek; - ma_tell_proc onTell; - void* pReadSeekTellUserData; - ma_format format; /* Can be f32, s16 or s32. */ -#if !defined(MA_NO_WAV) - ma_dr_wav dr; -#endif -} ma_wav; - -MA_API ma_result ma_wav_init(ma_read_proc onRead, ma_seek_proc onSeek, ma_tell_proc onTell, void* pReadSeekTellUserData, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_wav* pWav); -MA_API ma_result ma_wav_init_file(const char* pFilePath, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_wav* pWav); -MA_API ma_result ma_wav_init_file_w(const wchar_t* pFilePath, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_wav* pWav); -MA_API ma_result ma_wav_init_memory(const void* pData, size_t dataSize, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_wav* pWav); -MA_API void ma_wav_uninit(ma_wav* pWav, const ma_allocation_callbacks* pAllocationCallbacks); -MA_API ma_result ma_wav_read_pcm_frames(ma_wav* pWav, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead); -MA_API ma_result ma_wav_seek_to_pcm_frame(ma_wav* pWav, ma_uint64 frameIndex); -MA_API ma_result ma_wav_get_data_format(ma_wav* pWav, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate, ma_channel* pChannelMap, size_t channelMapCap); -MA_API ma_result ma_wav_get_cursor_in_pcm_frames(ma_wav* pWav, ma_uint64* pCursor); -MA_API ma_result ma_wav_get_length_in_pcm_frames(ma_wav* pWav, ma_uint64* pLength); - - -static ma_result ma_wav_ds_read(ma_data_source* pDataSource, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead) -{ - return ma_wav_read_pcm_frames((ma_wav*)pDataSource, pFramesOut, frameCount, pFramesRead); -} - -static ma_result ma_wav_ds_seek(ma_data_source* pDataSource, ma_uint64 frameIndex) -{ - return ma_wav_seek_to_pcm_frame((ma_wav*)pDataSource, frameIndex); -} - -static ma_result ma_wav_ds_get_data_format(ma_data_source* pDataSource, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate, ma_channel* pChannelMap, size_t channelMapCap) -{ - return ma_wav_get_data_format((ma_wav*)pDataSource, pFormat, pChannels, pSampleRate, pChannelMap, channelMapCap); -} - -static ma_result ma_wav_ds_get_cursor(ma_data_source* pDataSource, ma_uint64* pCursor) -{ - return ma_wav_get_cursor_in_pcm_frames((ma_wav*)pDataSource, pCursor); -} - -static ma_result ma_wav_ds_get_length(ma_data_source* pDataSource, ma_uint64* pLength) -{ - return ma_wav_get_length_in_pcm_frames((ma_wav*)pDataSource, pLength); -} - -static ma_data_source_vtable g_ma_wav_ds_vtable = -{ - ma_wav_ds_read, - ma_wav_ds_seek, - ma_wav_ds_get_data_format, - ma_wav_ds_get_cursor, - ma_wav_ds_get_length, - NULL, /* onSetLooping */ - 0 -}; - - -#if !defined(MA_NO_WAV) -static size_t ma_wav_dr_callback__read(void* pUserData, void* pBufferOut, size_t bytesToRead) -{ - ma_wav* pWav = (ma_wav*)pUserData; - ma_result result; - size_t bytesRead; - - MA_ASSERT(pWav != NULL); - - result = pWav->onRead(pWav->pReadSeekTellUserData, pBufferOut, bytesToRead, &bytesRead); - (void)result; - - return bytesRead; -} - -static ma_bool32 ma_wav_dr_callback__seek(void* pUserData, int offset, ma_dr_wav_seek_origin origin) -{ - ma_wav* pWav = (ma_wav*)pUserData; - ma_result result; - ma_seek_origin maSeekOrigin; - - MA_ASSERT(pWav != NULL); - - maSeekOrigin = ma_seek_origin_start; - if (origin == ma_dr_wav_seek_origin_current) { - maSeekOrigin = ma_seek_origin_current; - } - - result = pWav->onSeek(pWav->pReadSeekTellUserData, offset, maSeekOrigin); - if (result != MA_SUCCESS) { - return MA_FALSE; - } - - return MA_TRUE; -} -#endif - -static ma_result ma_wav_init_internal(const ma_decoding_backend_config* pConfig, ma_wav* pWav) -{ - ma_result result; - ma_data_source_config dataSourceConfig; - - if (pWav == NULL) { - return MA_INVALID_ARGS; - } - - MA_ZERO_OBJECT(pWav); - pWav->format = ma_format_unknown; /* Use closest match to source file by default. */ - - if (pConfig != NULL && (pConfig->preferredFormat == ma_format_f32 || pConfig->preferredFormat == ma_format_s16 || pConfig->preferredFormat == ma_format_s32)) { - pWav->format = pConfig->preferredFormat; - } else { - /* Getting here means something other than f32 and s16 was specified. Just leave this unset to use the default format. */ - } - - dataSourceConfig = ma_data_source_config_init(); - dataSourceConfig.vtable = &g_ma_wav_ds_vtable; - - result = ma_data_source_init(&dataSourceConfig, &pWav->ds); - if (result != MA_SUCCESS) { - return result; /* Failed to initialize the base data source. */ - } - - return MA_SUCCESS; -} - -static ma_result ma_wav_post_init(ma_wav* pWav) -{ - /* - If an explicit format was not specified, try picking the closest match based on the internal - format. The format needs to be supported by miniaudio. - */ - if (pWav->format == ma_format_unknown) { - switch (pWav->dr.translatedFormatTag) - { - case MA_DR_WAVE_FORMAT_PCM: - { - if (pWav->dr.bitsPerSample == 8) { - pWav->format = ma_format_u8; - } else if (pWav->dr.bitsPerSample == 16) { - pWav->format = ma_format_s16; - } else if (pWav->dr.bitsPerSample == 24) { - pWav->format = ma_format_s24; - } else if (pWav->dr.bitsPerSample == 32) { - pWav->format = ma_format_s32; - } - } break; - - case MA_DR_WAVE_FORMAT_IEEE_FLOAT: - { - if (pWav->dr.bitsPerSample == 32) { - pWav->format = ma_format_f32; - } - } break; - - default: break; - } - - /* Fall back to f32 if we couldn't find anything. */ - if (pWav->format == ma_format_unknown) { - pWav->format = ma_format_f32; - } - } - - return MA_SUCCESS; -} - -MA_API ma_result ma_wav_init(ma_read_proc onRead, ma_seek_proc onSeek, ma_tell_proc onTell, void* pReadSeekTellUserData, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_wav* pWav) -{ - ma_result result; - - result = ma_wav_init_internal(pConfig, pWav); - if (result != MA_SUCCESS) { - return result; - } - - if (onRead == NULL || onSeek == NULL) { - return MA_INVALID_ARGS; /* onRead and onSeek are mandatory. */ - } - - pWav->onRead = onRead; - pWav->onSeek = onSeek; - pWav->onTell = onTell; - pWav->pReadSeekTellUserData = pReadSeekTellUserData; - - #if !defined(MA_NO_WAV) - { - ma_bool32 wavResult; - - wavResult = ma_dr_wav_init(&pWav->dr, ma_wav_dr_callback__read, ma_wav_dr_callback__seek, pWav, pAllocationCallbacks); - if (wavResult != MA_TRUE) { - return MA_INVALID_FILE; - } - - ma_wav_post_init(pWav); - - return MA_SUCCESS; - } - #else - { - /* wav is disabled. */ - (void)pAllocationCallbacks; - return MA_NOT_IMPLEMENTED; - } - #endif -} - -MA_API ma_result ma_wav_init_file(const char* pFilePath, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_wav* pWav) -{ - ma_result result; - - result = ma_wav_init_internal(pConfig, pWav); - if (result != MA_SUCCESS) { - return result; - } - - #if !defined(MA_NO_WAV) - { - ma_bool32 wavResult; - - wavResult = ma_dr_wav_init_file(&pWav->dr, pFilePath, pAllocationCallbacks); - if (wavResult != MA_TRUE) { - return MA_INVALID_FILE; - } - - ma_wav_post_init(pWav); - - return MA_SUCCESS; - } - #else - { - /* wav is disabled. */ - (void)pFilePath; - (void)pAllocationCallbacks; - return MA_NOT_IMPLEMENTED; - } - #endif -} - -MA_API ma_result ma_wav_init_file_w(const wchar_t* pFilePath, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_wav* pWav) -{ - ma_result result; - - result = ma_wav_init_internal(pConfig, pWav); - if (result != MA_SUCCESS) { - return result; - } - - #if !defined(MA_NO_WAV) - { - ma_bool32 wavResult; - - wavResult = ma_dr_wav_init_file_w(&pWav->dr, pFilePath, pAllocationCallbacks); - if (wavResult != MA_TRUE) { - return MA_INVALID_FILE; - } - - ma_wav_post_init(pWav); - - return MA_SUCCESS; - } - #else - { - /* wav is disabled. */ - (void)pFilePath; - (void)pAllocationCallbacks; - return MA_NOT_IMPLEMENTED; - } - #endif -} - -MA_API ma_result ma_wav_init_memory(const void* pData, size_t dataSize, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_wav* pWav) -{ - ma_result result; - - result = ma_wav_init_internal(pConfig, pWav); - if (result != MA_SUCCESS) { - return result; - } - - #if !defined(MA_NO_WAV) - { - ma_bool32 wavResult; - - wavResult = ma_dr_wav_init_memory(&pWav->dr, pData, dataSize, pAllocationCallbacks); - if (wavResult != MA_TRUE) { - return MA_INVALID_FILE; - } - - ma_wav_post_init(pWav); - - return MA_SUCCESS; - } - #else - { - /* wav is disabled. */ - (void)pData; - (void)dataSize; - (void)pAllocationCallbacks; - return MA_NOT_IMPLEMENTED; - } - #endif -} - -MA_API void ma_wav_uninit(ma_wav* pWav, const ma_allocation_callbacks* pAllocationCallbacks) -{ - if (pWav == NULL) { - return; - } - - (void)pAllocationCallbacks; - - #if !defined(MA_NO_WAV) - { - ma_dr_wav_uninit(&pWav->dr); - } - #else - { - /* wav is disabled. Should never hit this since initialization would have failed. */ - MA_ASSERT(MA_FALSE); - } - #endif - - ma_data_source_uninit(&pWav->ds); -} - -MA_API ma_result ma_wav_read_pcm_frames(ma_wav* pWav, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead) -{ - if (pFramesRead != NULL) { - *pFramesRead = 0; - } - - if (frameCount == 0) { - return MA_INVALID_ARGS; - } - - if (pWav == NULL) { - return MA_INVALID_ARGS; - } - - #if !defined(MA_NO_WAV) - { - /* We always use floating point format. */ - ma_result result = MA_SUCCESS; /* Must be initialized to MA_SUCCESS. */ - ma_uint64 totalFramesRead = 0; - ma_format format; - - ma_wav_get_data_format(pWav, &format, NULL, NULL, NULL, 0); - - switch (format) - { - case ma_format_f32: - { - totalFramesRead = ma_dr_wav_read_pcm_frames_f32(&pWav->dr, frameCount, (float*)pFramesOut); - } break; - - case ma_format_s16: - { - totalFramesRead = ma_dr_wav_read_pcm_frames_s16(&pWav->dr, frameCount, (ma_int16*)pFramesOut); - } break; - - case ma_format_s32: - { - totalFramesRead = ma_dr_wav_read_pcm_frames_s32(&pWav->dr, frameCount, (ma_int32*)pFramesOut); - } break; - - /* Fallback to a raw read. */ - case ma_format_unknown: return MA_INVALID_OPERATION; /* <-- this should never be hit because initialization would just fall back to a supported format. */ - default: - { - totalFramesRead = ma_dr_wav_read_pcm_frames(&pWav->dr, frameCount, pFramesOut); - } break; - } - - /* In the future we'll update ma_dr_wav to return MA_AT_END for us. */ - if (totalFramesRead == 0) { - result = MA_AT_END; - } - - if (pFramesRead != NULL) { - *pFramesRead = totalFramesRead; - } - - if (result == MA_SUCCESS && totalFramesRead == 0) { - result = MA_AT_END; - } - - return result; - } - #else - { - /* wav is disabled. Should never hit this since initialization would have failed. */ - MA_ASSERT(MA_FALSE); - - (void)pFramesOut; - (void)frameCount; - (void)pFramesRead; - - return MA_NOT_IMPLEMENTED; - } - #endif -} - -MA_API ma_result ma_wav_seek_to_pcm_frame(ma_wav* pWav, ma_uint64 frameIndex) -{ - if (pWav == NULL) { - return MA_INVALID_ARGS; - } - - #if !defined(MA_NO_WAV) - { - ma_bool32 wavResult; - - wavResult = ma_dr_wav_seek_to_pcm_frame(&pWav->dr, frameIndex); - if (wavResult != MA_TRUE) { - return MA_ERROR; - } - - return MA_SUCCESS; - } - #else - { - /* wav is disabled. Should never hit this since initialization would have failed. */ - MA_ASSERT(MA_FALSE); - - (void)frameIndex; - - return MA_NOT_IMPLEMENTED; - } - #endif -} - -MA_API ma_result ma_wav_get_data_format(ma_wav* pWav, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate, ma_channel* pChannelMap, size_t channelMapCap) -{ - /* Defaults for safety. */ - if (pFormat != NULL) { - *pFormat = ma_format_unknown; - } - if (pChannels != NULL) { - *pChannels = 0; - } - if (pSampleRate != NULL) { - *pSampleRate = 0; - } - if (pChannelMap != NULL) { - MA_ZERO_MEMORY(pChannelMap, sizeof(*pChannelMap) * channelMapCap); - } - - if (pWav == NULL) { - return MA_INVALID_OPERATION; - } - - if (pFormat != NULL) { - *pFormat = pWav->format; - } - - #if !defined(MA_NO_WAV) - { - if (pChannels != NULL) { - *pChannels = pWav->dr.channels; - } - - if (pSampleRate != NULL) { - *pSampleRate = pWav->dr.sampleRate; - } - - if (pChannelMap != NULL) { - ma_channel_map_init_standard(ma_standard_channel_map_microsoft, pChannelMap, channelMapCap, pWav->dr.channels); - } - - return MA_SUCCESS; - } - #else - { - /* wav is disabled. Should never hit this since initialization would have failed. */ - MA_ASSERT(MA_FALSE); - return MA_NOT_IMPLEMENTED; - } - #endif -} - -MA_API ma_result ma_wav_get_cursor_in_pcm_frames(ma_wav* pWav, ma_uint64* pCursor) -{ - if (pCursor == NULL) { - return MA_INVALID_ARGS; - } - - *pCursor = 0; /* Safety. */ - - if (pWav == NULL) { - return MA_INVALID_ARGS; - } - - #if !defined(MA_NO_WAV) - { - ma_result wavResult = ma_dr_wav_get_cursor_in_pcm_frames(&pWav->dr, pCursor); - if (wavResult != MA_SUCCESS) { - return (ma_result)wavResult; /* ma_dr_wav result codes map to miniaudio's. */ - } - - return MA_SUCCESS; - } - #else - { - /* wav is disabled. Should never hit this since initialization would have failed. */ - MA_ASSERT(MA_FALSE); - return MA_NOT_IMPLEMENTED; - } - #endif -} - -MA_API ma_result ma_wav_get_length_in_pcm_frames(ma_wav* pWav, ma_uint64* pLength) -{ - if (pLength == NULL) { - return MA_INVALID_ARGS; - } - - *pLength = 0; /* Safety. */ - - if (pWav == NULL) { - return MA_INVALID_ARGS; - } - - #if !defined(MA_NO_WAV) - { - ma_result wavResult = ma_dr_wav_get_length_in_pcm_frames(&pWav->dr, pLength); - if (wavResult != MA_SUCCESS) { - return (ma_result)wavResult; /* ma_dr_wav result codes map to miniaudio's. */ - } - - return MA_SUCCESS; - } - #else - { - /* wav is disabled. Should never hit this since initialization would have failed. */ - MA_ASSERT(MA_FALSE); - return MA_NOT_IMPLEMENTED; - } - #endif -} - - -static ma_result ma_decoding_backend_init__wav(void* pUserData, ma_read_proc onRead, ma_seek_proc onSeek, ma_tell_proc onTell, void* pReadSeekTellUserData, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_data_source** ppBackend) -{ - ma_result result; - ma_wav* pWav; - - (void)pUserData; /* For now not using pUserData, but once we start storing the vorbis decoder state within the ma_decoder structure this will be set to the decoder so we can avoid a malloc. */ - - /* For now we're just allocating the decoder backend on the heap. */ - pWav = (ma_wav*)ma_malloc(sizeof(*pWav), pAllocationCallbacks); - if (pWav == NULL) { - return MA_OUT_OF_MEMORY; - } - - result = ma_wav_init(onRead, onSeek, onTell, pReadSeekTellUserData, pConfig, pAllocationCallbacks, pWav); - if (result != MA_SUCCESS) { - ma_free(pWav, pAllocationCallbacks); - return result; - } - - *ppBackend = pWav; - - return MA_SUCCESS; -} - -static ma_result ma_decoding_backend_init_file__wav(void* pUserData, const char* pFilePath, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_data_source** ppBackend) -{ - ma_result result; - ma_wav* pWav; - - (void)pUserData; /* For now not using pUserData, but once we start storing the vorbis decoder state within the ma_decoder structure this will be set to the decoder so we can avoid a malloc. */ - - /* For now we're just allocating the decoder backend on the heap. */ - pWav = (ma_wav*)ma_malloc(sizeof(*pWav), pAllocationCallbacks); - if (pWav == NULL) { - return MA_OUT_OF_MEMORY; - } - - result = ma_wav_init_file(pFilePath, pConfig, pAllocationCallbacks, pWav); - if (result != MA_SUCCESS) { - ma_free(pWav, pAllocationCallbacks); - return result; - } - - *ppBackend = pWav; - - return MA_SUCCESS; -} - -static ma_result ma_decoding_backend_init_file_w__wav(void* pUserData, const wchar_t* pFilePath, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_data_source** ppBackend) -{ - ma_result result; - ma_wav* pWav; - - (void)pUserData; /* For now not using pUserData, but once we start storing the vorbis decoder state within the ma_decoder structure this will be set to the decoder so we can avoid a malloc. */ - - /* For now we're just allocating the decoder backend on the heap. */ - pWav = (ma_wav*)ma_malloc(sizeof(*pWav), pAllocationCallbacks); - if (pWav == NULL) { - return MA_OUT_OF_MEMORY; - } - - result = ma_wav_init_file_w(pFilePath, pConfig, pAllocationCallbacks, pWav); - if (result != MA_SUCCESS) { - ma_free(pWav, pAllocationCallbacks); - return result; - } - - *ppBackend = pWav; - - return MA_SUCCESS; -} - -static ma_result ma_decoding_backend_init_memory__wav(void* pUserData, const void* pData, size_t dataSize, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_data_source** ppBackend) -{ - ma_result result; - ma_wav* pWav; - - (void)pUserData; /* For now not using pUserData, but once we start storing the vorbis decoder state within the ma_decoder structure this will be set to the decoder so we can avoid a malloc. */ - - /* For now we're just allocating the decoder backend on the heap. */ - pWav = (ma_wav*)ma_malloc(sizeof(*pWav), pAllocationCallbacks); - if (pWav == NULL) { - return MA_OUT_OF_MEMORY; - } - - result = ma_wav_init_memory(pData, dataSize, pConfig, pAllocationCallbacks, pWav); - if (result != MA_SUCCESS) { - ma_free(pWav, pAllocationCallbacks); - return result; - } - - *ppBackend = pWav; - - return MA_SUCCESS; -} - -static void ma_decoding_backend_uninit__wav(void* pUserData, ma_data_source* pBackend, const ma_allocation_callbacks* pAllocationCallbacks) -{ - ma_wav* pWav = (ma_wav*)pBackend; - - (void)pUserData; - - ma_wav_uninit(pWav, pAllocationCallbacks); - ma_free(pWav, pAllocationCallbacks); -} - -static ma_decoding_backend_vtable g_ma_decoding_backend_vtable_wav = -{ - ma_decoding_backend_init__wav, - ma_decoding_backend_init_file__wav, - ma_decoding_backend_init_file_w__wav, - ma_decoding_backend_init_memory__wav, - ma_decoding_backend_uninit__wav -}; - -static ma_result ma_decoder_init_wav__internal(const ma_decoder_config* pConfig, ma_decoder* pDecoder) -{ - return ma_decoder_init_from_vtable__internal(&g_ma_decoding_backend_vtable_wav, NULL, pConfig, pDecoder); -} - -static ma_result ma_decoder_init_wav_from_file__internal(const char* pFilePath, const ma_decoder_config* pConfig, ma_decoder* pDecoder) -{ - return ma_decoder_init_from_file__internal(&g_ma_decoding_backend_vtable_wav, NULL, pFilePath, pConfig, pDecoder); -} - -static ma_result ma_decoder_init_wav_from_file_w__internal(const wchar_t* pFilePath, const ma_decoder_config* pConfig, ma_decoder* pDecoder) -{ - return ma_decoder_init_from_file_w__internal(&g_ma_decoding_backend_vtable_wav, NULL, pFilePath, pConfig, pDecoder); -} - -static ma_result ma_decoder_init_wav_from_memory__internal(const void* pData, size_t dataSize, const ma_decoder_config* pConfig, ma_decoder* pDecoder) -{ - return ma_decoder_init_from_memory__internal(&g_ma_decoding_backend_vtable_wav, NULL, pData, dataSize, pConfig, pDecoder); -} -#endif /* ma_dr_wav_h */ - -/* FLAC */ -#ifdef ma_dr_flac_h -#define MA_HAS_FLAC - -typedef struct -{ - ma_data_source_base ds; - ma_read_proc onRead; - ma_seek_proc onSeek; - ma_tell_proc onTell; - void* pReadSeekTellUserData; - ma_format format; /* Can be f32, s16 or s32. */ -#if !defined(MA_NO_FLAC) - ma_dr_flac* dr; -#endif -} ma_flac; - -MA_API ma_result ma_flac_init(ma_read_proc onRead, ma_seek_proc onSeek, ma_tell_proc onTell, void* pReadSeekTellUserData, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_flac* pFlac); -MA_API ma_result ma_flac_init_file(const char* pFilePath, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_flac* pFlac); -MA_API ma_result ma_flac_init_file_w(const wchar_t* pFilePath, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_flac* pFlac); -MA_API ma_result ma_flac_init_memory(const void* pData, size_t dataSize, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_flac* pFlac); -MA_API void ma_flac_uninit(ma_flac* pFlac, const ma_allocation_callbacks* pAllocationCallbacks); -MA_API ma_result ma_flac_read_pcm_frames(ma_flac* pFlac, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead); -MA_API ma_result ma_flac_seek_to_pcm_frame(ma_flac* pFlac, ma_uint64 frameIndex); -MA_API ma_result ma_flac_get_data_format(ma_flac* pFlac, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate, ma_channel* pChannelMap, size_t channelMapCap); -MA_API ma_result ma_flac_get_cursor_in_pcm_frames(ma_flac* pFlac, ma_uint64* pCursor); -MA_API ma_result ma_flac_get_length_in_pcm_frames(ma_flac* pFlac, ma_uint64* pLength); - - -static ma_result ma_flac_ds_read(ma_data_source* pDataSource, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead) -{ - return ma_flac_read_pcm_frames((ma_flac*)pDataSource, pFramesOut, frameCount, pFramesRead); -} - -static ma_result ma_flac_ds_seek(ma_data_source* pDataSource, ma_uint64 frameIndex) -{ - return ma_flac_seek_to_pcm_frame((ma_flac*)pDataSource, frameIndex); -} - -static ma_result ma_flac_ds_get_data_format(ma_data_source* pDataSource, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate, ma_channel* pChannelMap, size_t channelMapCap) -{ - return ma_flac_get_data_format((ma_flac*)pDataSource, pFormat, pChannels, pSampleRate, pChannelMap, channelMapCap); -} - -static ma_result ma_flac_ds_get_cursor(ma_data_source* pDataSource, ma_uint64* pCursor) -{ - return ma_flac_get_cursor_in_pcm_frames((ma_flac*)pDataSource, pCursor); -} - -static ma_result ma_flac_ds_get_length(ma_data_source* pDataSource, ma_uint64* pLength) -{ - return ma_flac_get_length_in_pcm_frames((ma_flac*)pDataSource, pLength); -} - -static ma_data_source_vtable g_ma_flac_ds_vtable = -{ - ma_flac_ds_read, - ma_flac_ds_seek, - ma_flac_ds_get_data_format, - ma_flac_ds_get_cursor, - ma_flac_ds_get_length, - NULL, /* onSetLooping */ - 0 -}; - - -#if !defined(MA_NO_FLAC) -static size_t ma_flac_dr_callback__read(void* pUserData, void* pBufferOut, size_t bytesToRead) -{ - ma_flac* pFlac = (ma_flac*)pUserData; - ma_result result; - size_t bytesRead; - - MA_ASSERT(pFlac != NULL); - - result = pFlac->onRead(pFlac->pReadSeekTellUserData, pBufferOut, bytesToRead, &bytesRead); - (void)result; - - return bytesRead; -} - -static ma_bool32 ma_flac_dr_callback__seek(void* pUserData, int offset, ma_dr_flac_seek_origin origin) -{ - ma_flac* pFlac = (ma_flac*)pUserData; - ma_result result; - ma_seek_origin maSeekOrigin; - - MA_ASSERT(pFlac != NULL); - - maSeekOrigin = ma_seek_origin_start; - if (origin == ma_dr_flac_seek_origin_current) { - maSeekOrigin = ma_seek_origin_current; - } - - result = pFlac->onSeek(pFlac->pReadSeekTellUserData, offset, maSeekOrigin); - if (result != MA_SUCCESS) { - return MA_FALSE; - } - - return MA_TRUE; -} -#endif - -static ma_result ma_flac_init_internal(const ma_decoding_backend_config* pConfig, ma_flac* pFlac) -{ - ma_result result; - ma_data_source_config dataSourceConfig; - - if (pFlac == NULL) { - return MA_INVALID_ARGS; - } - - MA_ZERO_OBJECT(pFlac); - pFlac->format = ma_format_f32; /* f32 by default. */ - - if (pConfig != NULL && (pConfig->preferredFormat == ma_format_f32 || pConfig->preferredFormat == ma_format_s16 || pConfig->preferredFormat == ma_format_s32)) { - pFlac->format = pConfig->preferredFormat; - } else { - /* Getting here means something other than f32 and s16 was specified. Just leave this unset to use the default format. */ - } - - dataSourceConfig = ma_data_source_config_init(); - dataSourceConfig.vtable = &g_ma_flac_ds_vtable; - - result = ma_data_source_init(&dataSourceConfig, &pFlac->ds); - if (result != MA_SUCCESS) { - return result; /* Failed to initialize the base data source. */ - } - - return MA_SUCCESS; -} - -MA_API ma_result ma_flac_init(ma_read_proc onRead, ma_seek_proc onSeek, ma_tell_proc onTell, void* pReadSeekTellUserData, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_flac* pFlac) -{ - ma_result result; - - result = ma_flac_init_internal(pConfig, pFlac); - if (result != MA_SUCCESS) { - return result; - } - - if (onRead == NULL || onSeek == NULL) { - return MA_INVALID_ARGS; /* onRead and onSeek are mandatory. */ - } - - pFlac->onRead = onRead; - pFlac->onSeek = onSeek; - pFlac->onTell = onTell; - pFlac->pReadSeekTellUserData = pReadSeekTellUserData; - - #if !defined(MA_NO_FLAC) - { - pFlac->dr = ma_dr_flac_open(ma_flac_dr_callback__read, ma_flac_dr_callback__seek, pFlac, pAllocationCallbacks); - if (pFlac->dr == NULL) { - return MA_INVALID_FILE; - } - - return MA_SUCCESS; - } - #else - { - /* flac is disabled. */ - (void)pAllocationCallbacks; - return MA_NOT_IMPLEMENTED; - } - #endif -} - -MA_API ma_result ma_flac_init_file(const char* pFilePath, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_flac* pFlac) -{ - ma_result result; - - result = ma_flac_init_internal(pConfig, pFlac); - if (result != MA_SUCCESS) { - return result; - } - - #if !defined(MA_NO_FLAC) - { - pFlac->dr = ma_dr_flac_open_file(pFilePath, pAllocationCallbacks); - if (pFlac->dr == NULL) { - return MA_INVALID_FILE; - } - - return MA_SUCCESS; - } - #else - { - /* flac is disabled. */ - (void)pFilePath; - (void)pAllocationCallbacks; - return MA_NOT_IMPLEMENTED; - } - #endif -} - -MA_API ma_result ma_flac_init_file_w(const wchar_t* pFilePath, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_flac* pFlac) -{ - ma_result result; - - result = ma_flac_init_internal(pConfig, pFlac); - if (result != MA_SUCCESS) { - return result; - } - - #if !defined(MA_NO_FLAC) - { - pFlac->dr = ma_dr_flac_open_file_w(pFilePath, pAllocationCallbacks); - if (pFlac->dr == NULL) { - return MA_INVALID_FILE; - } - - return MA_SUCCESS; - } - #else - { - /* flac is disabled. */ - (void)pFilePath; - (void)pAllocationCallbacks; - return MA_NOT_IMPLEMENTED; - } - #endif -} - -MA_API ma_result ma_flac_init_memory(const void* pData, size_t dataSize, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_flac* pFlac) -{ - ma_result result; - - result = ma_flac_init_internal(pConfig, pFlac); - if (result != MA_SUCCESS) { - return result; - } - - #if !defined(MA_NO_FLAC) - { - pFlac->dr = ma_dr_flac_open_memory(pData, dataSize, pAllocationCallbacks); - if (pFlac->dr == NULL) { - return MA_INVALID_FILE; - } - - return MA_SUCCESS; - } - #else - { - /* flac is disabled. */ - (void)pData; - (void)dataSize; - (void)pAllocationCallbacks; - return MA_NOT_IMPLEMENTED; - } - #endif -} - -MA_API void ma_flac_uninit(ma_flac* pFlac, const ma_allocation_callbacks* pAllocationCallbacks) -{ - if (pFlac == NULL) { - return; - } - - (void)pAllocationCallbacks; - - #if !defined(MA_NO_FLAC) - { - ma_dr_flac_close(pFlac->dr); - } - #else - { - /* flac is disabled. Should never hit this since initialization would have failed. */ - MA_ASSERT(MA_FALSE); - } - #endif - - ma_data_source_uninit(&pFlac->ds); -} - -MA_API ma_result ma_flac_read_pcm_frames(ma_flac* pFlac, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead) -{ - if (pFramesRead != NULL) { - *pFramesRead = 0; - } - - if (frameCount == 0) { - return MA_INVALID_ARGS; - } - - if (pFlac == NULL) { - return MA_INVALID_ARGS; - } - - #if !defined(MA_NO_FLAC) - { - /* We always use floating point format. */ - ma_result result = MA_SUCCESS; /* Must be initialized to MA_SUCCESS. */ - ma_uint64 totalFramesRead = 0; - ma_format format; - - ma_flac_get_data_format(pFlac, &format, NULL, NULL, NULL, 0); - - switch (format) - { - case ma_format_f32: - { - totalFramesRead = ma_dr_flac_read_pcm_frames_f32(pFlac->dr, frameCount, (float*)pFramesOut); - } break; - - case ma_format_s16: - { - totalFramesRead = ma_dr_flac_read_pcm_frames_s16(pFlac->dr, frameCount, (ma_int16*)pFramesOut); - } break; - - case ma_format_s32: - { - totalFramesRead = ma_dr_flac_read_pcm_frames_s32(pFlac->dr, frameCount, (ma_int32*)pFramesOut); - } break; - - case ma_format_u8: - case ma_format_s24: - case ma_format_unknown: - default: - { - return MA_INVALID_OPERATION; - }; - } - - /* In the future we'll update ma_dr_flac to return MA_AT_END for us. */ - if (totalFramesRead == 0) { - result = MA_AT_END; - } - - if (pFramesRead != NULL) { - *pFramesRead = totalFramesRead; - } - - if (result == MA_SUCCESS && totalFramesRead == 0) { - result = MA_AT_END; - } - - return result; - } - #else - { - /* flac is disabled. Should never hit this since initialization would have failed. */ - MA_ASSERT(MA_FALSE); - - (void)pFramesOut; - (void)frameCount; - (void)pFramesRead; - - return MA_NOT_IMPLEMENTED; - } - #endif -} - -MA_API ma_result ma_flac_seek_to_pcm_frame(ma_flac* pFlac, ma_uint64 frameIndex) -{ - if (pFlac == NULL) { - return MA_INVALID_ARGS; - } - - #if !defined(MA_NO_FLAC) - { - ma_bool32 flacResult; - - flacResult = ma_dr_flac_seek_to_pcm_frame(pFlac->dr, frameIndex); - if (flacResult != MA_TRUE) { - return MA_ERROR; - } - - return MA_SUCCESS; - } - #else - { - /* flac is disabled. Should never hit this since initialization would have failed. */ - MA_ASSERT(MA_FALSE); - - (void)frameIndex; - - return MA_NOT_IMPLEMENTED; - } - #endif -} - -MA_API ma_result ma_flac_get_data_format(ma_flac* pFlac, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate, ma_channel* pChannelMap, size_t channelMapCap) -{ - /* Defaults for safety. */ - if (pFormat != NULL) { - *pFormat = ma_format_unknown; - } - if (pChannels != NULL) { - *pChannels = 0; - } - if (pSampleRate != NULL) { - *pSampleRate = 0; - } - if (pChannelMap != NULL) { - MA_ZERO_MEMORY(pChannelMap, sizeof(*pChannelMap) * channelMapCap); - } - - if (pFlac == NULL) { - return MA_INVALID_OPERATION; - } - - if (pFormat != NULL) { - *pFormat = pFlac->format; - } - - #if !defined(MA_NO_FLAC) - { - if (pChannels != NULL) { - *pChannels = pFlac->dr->channels; - } - - if (pSampleRate != NULL) { - *pSampleRate = pFlac->dr->sampleRate; - } - - if (pChannelMap != NULL) { - ma_channel_map_init_standard(ma_standard_channel_map_microsoft, pChannelMap, channelMapCap, pFlac->dr->channels); - } - - return MA_SUCCESS; - } - #else - { - /* flac is disabled. Should never hit this since initialization would have failed. */ - MA_ASSERT(MA_FALSE); - return MA_NOT_IMPLEMENTED; - } - #endif -} - -MA_API ma_result ma_flac_get_cursor_in_pcm_frames(ma_flac* pFlac, ma_uint64* pCursor) -{ - if (pCursor == NULL) { - return MA_INVALID_ARGS; - } - - *pCursor = 0; /* Safety. */ - - if (pFlac == NULL) { - return MA_INVALID_ARGS; - } - - #if !defined(MA_NO_FLAC) - { - *pCursor = pFlac->dr->currentPCMFrame; - - return MA_SUCCESS; - } - #else - { - /* flac is disabled. Should never hit this since initialization would have failed. */ - MA_ASSERT(MA_FALSE); - return MA_NOT_IMPLEMENTED; - } - #endif -} - -MA_API ma_result ma_flac_get_length_in_pcm_frames(ma_flac* pFlac, ma_uint64* pLength) -{ - if (pLength == NULL) { - return MA_INVALID_ARGS; - } - - *pLength = 0; /* Safety. */ - - if (pFlac == NULL) { - return MA_INVALID_ARGS; - } - - #if !defined(MA_NO_FLAC) - { - *pLength = pFlac->dr->totalPCMFrameCount; - - return MA_SUCCESS; - } - #else - { - /* flac is disabled. Should never hit this since initialization would have failed. */ - MA_ASSERT(MA_FALSE); - return MA_NOT_IMPLEMENTED; - } - #endif -} - - -static ma_result ma_decoding_backend_init__flac(void* pUserData, ma_read_proc onRead, ma_seek_proc onSeek, ma_tell_proc onTell, void* pReadSeekTellUserData, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_data_source** ppBackend) -{ - ma_result result; - ma_flac* pFlac; - - (void)pUserData; /* For now not using pUserData, but once we start storing the vorbis decoder state within the ma_decoder structure this will be set to the decoder so we can avoid a malloc. */ - - /* For now we're just allocating the decoder backend on the heap. */ - pFlac = (ma_flac*)ma_malloc(sizeof(*pFlac), pAllocationCallbacks); - if (pFlac == NULL) { - return MA_OUT_OF_MEMORY; - } - - result = ma_flac_init(onRead, onSeek, onTell, pReadSeekTellUserData, pConfig, pAllocationCallbacks, pFlac); - if (result != MA_SUCCESS) { - ma_free(pFlac, pAllocationCallbacks); - return result; - } - - *ppBackend = pFlac; - - return MA_SUCCESS; -} - -static ma_result ma_decoding_backend_init_file__flac(void* pUserData, const char* pFilePath, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_data_source** ppBackend) -{ - ma_result result; - ma_flac* pFlac; - - (void)pUserData; /* For now not using pUserData, but once we start storing the vorbis decoder state within the ma_decoder structure this will be set to the decoder so we can avoid a malloc. */ - - /* For now we're just allocating the decoder backend on the heap. */ - pFlac = (ma_flac*)ma_malloc(sizeof(*pFlac), pAllocationCallbacks); - if (pFlac == NULL) { - return MA_OUT_OF_MEMORY; - } - - result = ma_flac_init_file(pFilePath, pConfig, pAllocationCallbacks, pFlac); - if (result != MA_SUCCESS) { - ma_free(pFlac, pAllocationCallbacks); - return result; - } - - *ppBackend = pFlac; - - return MA_SUCCESS; -} - -static ma_result ma_decoding_backend_init_file_w__flac(void* pUserData, const wchar_t* pFilePath, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_data_source** ppBackend) -{ - ma_result result; - ma_flac* pFlac; - - (void)pUserData; /* For now not using pUserData, but once we start storing the vorbis decoder state within the ma_decoder structure this will be set to the decoder so we can avoid a malloc. */ - - /* For now we're just allocating the decoder backend on the heap. */ - pFlac = (ma_flac*)ma_malloc(sizeof(*pFlac), pAllocationCallbacks); - if (pFlac == NULL) { - return MA_OUT_OF_MEMORY; - } - - result = ma_flac_init_file_w(pFilePath, pConfig, pAllocationCallbacks, pFlac); - if (result != MA_SUCCESS) { - ma_free(pFlac, pAllocationCallbacks); - return result; - } - - *ppBackend = pFlac; - - return MA_SUCCESS; -} - -static ma_result ma_decoding_backend_init_memory__flac(void* pUserData, const void* pData, size_t dataSize, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_data_source** ppBackend) -{ - ma_result result; - ma_flac* pFlac; - - (void)pUserData; /* For now not using pUserData, but once we start storing the vorbis decoder state within the ma_decoder structure this will be set to the decoder so we can avoid a malloc. */ - - /* For now we're just allocating the decoder backend on the heap. */ - pFlac = (ma_flac*)ma_malloc(sizeof(*pFlac), pAllocationCallbacks); - if (pFlac == NULL) { - return MA_OUT_OF_MEMORY; - } - - result = ma_flac_init_memory(pData, dataSize, pConfig, pAllocationCallbacks, pFlac); - if (result != MA_SUCCESS) { - ma_free(pFlac, pAllocationCallbacks); - return result; - } - - *ppBackend = pFlac; - - return MA_SUCCESS; -} - -static void ma_decoding_backend_uninit__flac(void* pUserData, ma_data_source* pBackend, const ma_allocation_callbacks* pAllocationCallbacks) -{ - ma_flac* pFlac = (ma_flac*)pBackend; - - (void)pUserData; - - ma_flac_uninit(pFlac, pAllocationCallbacks); - ma_free(pFlac, pAllocationCallbacks); -} - -static ma_decoding_backend_vtable g_ma_decoding_backend_vtable_flac = -{ - ma_decoding_backend_init__flac, - ma_decoding_backend_init_file__flac, - ma_decoding_backend_init_file_w__flac, - ma_decoding_backend_init_memory__flac, - ma_decoding_backend_uninit__flac -}; - -static ma_result ma_decoder_init_flac__internal(const ma_decoder_config* pConfig, ma_decoder* pDecoder) -{ - return ma_decoder_init_from_vtable__internal(&g_ma_decoding_backend_vtable_flac, NULL, pConfig, pDecoder); -} - -static ma_result ma_decoder_init_flac_from_file__internal(const char* pFilePath, const ma_decoder_config* pConfig, ma_decoder* pDecoder) -{ - return ma_decoder_init_from_file__internal(&g_ma_decoding_backend_vtable_flac, NULL, pFilePath, pConfig, pDecoder); -} - -static ma_result ma_decoder_init_flac_from_file_w__internal(const wchar_t* pFilePath, const ma_decoder_config* pConfig, ma_decoder* pDecoder) -{ - return ma_decoder_init_from_file_w__internal(&g_ma_decoding_backend_vtable_flac, NULL, pFilePath, pConfig, pDecoder); -} - -static ma_result ma_decoder_init_flac_from_memory__internal(const void* pData, size_t dataSize, const ma_decoder_config* pConfig, ma_decoder* pDecoder) -{ - return ma_decoder_init_from_memory__internal(&g_ma_decoding_backend_vtable_flac, NULL, pData, dataSize, pConfig, pDecoder); -} -#endif /* ma_dr_flac_h */ - -/* MP3 */ -#ifdef ma_dr_mp3_h -#define MA_HAS_MP3 - -typedef struct -{ - ma_data_source_base ds; - ma_read_proc onRead; - ma_seek_proc onSeek; - ma_tell_proc onTell; - void* pReadSeekTellUserData; - ma_format format; /* Can be f32 or s16. */ -#if !defined(MA_NO_MP3) - ma_dr_mp3 dr; - ma_uint32 seekPointCount; - ma_dr_mp3_seek_point* pSeekPoints; /* Only used if seek table generation is used. */ -#endif -} ma_mp3; - -MA_API ma_result ma_mp3_init(ma_read_proc onRead, ma_seek_proc onSeek, ma_tell_proc onTell, void* pReadSeekTellUserData, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_mp3* pMP3); -MA_API ma_result ma_mp3_init_file(const char* pFilePath, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_mp3* pMP3); -MA_API ma_result ma_mp3_init_file_w(const wchar_t* pFilePath, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_mp3* pMP3); -MA_API ma_result ma_mp3_init_memory(const void* pData, size_t dataSize, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_mp3* pMP3); -MA_API void ma_mp3_uninit(ma_mp3* pMP3, const ma_allocation_callbacks* pAllocationCallbacks); -MA_API ma_result ma_mp3_read_pcm_frames(ma_mp3* pMP3, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead); -MA_API ma_result ma_mp3_seek_to_pcm_frame(ma_mp3* pMP3, ma_uint64 frameIndex); -MA_API ma_result ma_mp3_get_data_format(ma_mp3* pMP3, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate, ma_channel* pChannelMap, size_t channelMapCap); -MA_API ma_result ma_mp3_get_cursor_in_pcm_frames(ma_mp3* pMP3, ma_uint64* pCursor); -MA_API ma_result ma_mp3_get_length_in_pcm_frames(ma_mp3* pMP3, ma_uint64* pLength); - - -static ma_result ma_mp3_ds_read(ma_data_source* pDataSource, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead) -{ - return ma_mp3_read_pcm_frames((ma_mp3*)pDataSource, pFramesOut, frameCount, pFramesRead); -} - -static ma_result ma_mp3_ds_seek(ma_data_source* pDataSource, ma_uint64 frameIndex) -{ - return ma_mp3_seek_to_pcm_frame((ma_mp3*)pDataSource, frameIndex); -} - -static ma_result ma_mp3_ds_get_data_format(ma_data_source* pDataSource, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate, ma_channel* pChannelMap, size_t channelMapCap) -{ - return ma_mp3_get_data_format((ma_mp3*)pDataSource, pFormat, pChannels, pSampleRate, pChannelMap, channelMapCap); -} - -static ma_result ma_mp3_ds_get_cursor(ma_data_source* pDataSource, ma_uint64* pCursor) -{ - return ma_mp3_get_cursor_in_pcm_frames((ma_mp3*)pDataSource, pCursor); -} - -static ma_result ma_mp3_ds_get_length(ma_data_source* pDataSource, ma_uint64* pLength) -{ - return ma_mp3_get_length_in_pcm_frames((ma_mp3*)pDataSource, pLength); -} - -static ma_data_source_vtable g_ma_mp3_ds_vtable = -{ - ma_mp3_ds_read, - ma_mp3_ds_seek, - ma_mp3_ds_get_data_format, - ma_mp3_ds_get_cursor, - ma_mp3_ds_get_length, - NULL, /* onSetLooping */ - 0 -}; - - -#if !defined(MA_NO_MP3) -static size_t ma_mp3_dr_callback__read(void* pUserData, void* pBufferOut, size_t bytesToRead) -{ - ma_mp3* pMP3 = (ma_mp3*)pUserData; - ma_result result; - size_t bytesRead; - - MA_ASSERT(pMP3 != NULL); - - result = pMP3->onRead(pMP3->pReadSeekTellUserData, pBufferOut, bytesToRead, &bytesRead); - (void)result; - - return bytesRead; -} - -static ma_bool32 ma_mp3_dr_callback__seek(void* pUserData, int offset, ma_dr_mp3_seek_origin origin) -{ - ma_mp3* pMP3 = (ma_mp3*)pUserData; - ma_result result; - ma_seek_origin maSeekOrigin; - - MA_ASSERT(pMP3 != NULL); - - maSeekOrigin = ma_seek_origin_start; - if (origin == ma_dr_mp3_seek_origin_current) { - maSeekOrigin = ma_seek_origin_current; - } - - result = pMP3->onSeek(pMP3->pReadSeekTellUserData, offset, maSeekOrigin); - if (result != MA_SUCCESS) { - return MA_FALSE; - } - - return MA_TRUE; -} -#endif - -static ma_result ma_mp3_init_internal(const ma_decoding_backend_config* pConfig, ma_mp3* pMP3) -{ - ma_result result; - ma_data_source_config dataSourceConfig; - - if (pMP3 == NULL) { - return MA_INVALID_ARGS; - } - - MA_ZERO_OBJECT(pMP3); - pMP3->format = ma_format_f32; /* f32 by default. */ - - if (pConfig != NULL && (pConfig->preferredFormat == ma_format_f32 || pConfig->preferredFormat == ma_format_s16)) { - pMP3->format = pConfig->preferredFormat; - } else { - /* Getting here means something other than f32 and s16 was specified. Just leave this unset to use the default format. */ - } - - dataSourceConfig = ma_data_source_config_init(); - dataSourceConfig.vtable = &g_ma_mp3_ds_vtable; - - result = ma_data_source_init(&dataSourceConfig, &pMP3->ds); - if (result != MA_SUCCESS) { - return result; /* Failed to initialize the base data source. */ - } - - return MA_SUCCESS; -} - -static ma_result ma_mp3_generate_seek_table(ma_mp3* pMP3, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks) -{ - ma_bool32 mp3Result; - ma_uint32 seekPointCount = 0; - ma_dr_mp3_seek_point* pSeekPoints = NULL; - - MA_ASSERT(pMP3 != NULL); - MA_ASSERT(pConfig != NULL); - - seekPointCount = pConfig->seekPointCount; - if (seekPointCount > 0) { - pSeekPoints = (ma_dr_mp3_seek_point*)ma_malloc(sizeof(*pMP3->pSeekPoints) * seekPointCount, pAllocationCallbacks); - if (pSeekPoints == NULL) { - return MA_OUT_OF_MEMORY; - } - } - - mp3Result = ma_dr_mp3_calculate_seek_points(&pMP3->dr, &seekPointCount, pSeekPoints); - if (mp3Result != MA_TRUE) { - ma_free(pSeekPoints, pAllocationCallbacks); - return MA_ERROR; - } - - mp3Result = ma_dr_mp3_bind_seek_table(&pMP3->dr, seekPointCount, pSeekPoints); - if (mp3Result != MA_TRUE) { - ma_free(pSeekPoints, pAllocationCallbacks); - return MA_ERROR; - } - - pMP3->seekPointCount = seekPointCount; - pMP3->pSeekPoints = pSeekPoints; - - return MA_SUCCESS; -} - -static ma_result ma_mp3_post_init(ma_mp3* pMP3, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks) -{ - ma_result result; - - result = ma_mp3_generate_seek_table(pMP3, pConfig, pAllocationCallbacks); - if (result != MA_SUCCESS) { - return result; - } - - return MA_SUCCESS; -} - -MA_API ma_result ma_mp3_init(ma_read_proc onRead, ma_seek_proc onSeek, ma_tell_proc onTell, void* pReadSeekTellUserData, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_mp3* pMP3) -{ - ma_result result; - - result = ma_mp3_init_internal(pConfig, pMP3); - if (result != MA_SUCCESS) { - return result; - } - - if (onRead == NULL || onSeek == NULL) { - return MA_INVALID_ARGS; /* onRead and onSeek are mandatory. */ - } - - pMP3->onRead = onRead; - pMP3->onSeek = onSeek; - pMP3->onTell = onTell; - pMP3->pReadSeekTellUserData = pReadSeekTellUserData; - - #if !defined(MA_NO_MP3) - { - ma_bool32 mp3Result; - - mp3Result = ma_dr_mp3_init(&pMP3->dr, ma_mp3_dr_callback__read, ma_mp3_dr_callback__seek, pMP3, pAllocationCallbacks); - if (mp3Result != MA_TRUE) { - return MA_INVALID_FILE; - } - - ma_mp3_post_init(pMP3, pConfig, pAllocationCallbacks); - - return MA_SUCCESS; - } - #else - { - /* mp3 is disabled. */ - (void)pAllocationCallbacks; - return MA_NOT_IMPLEMENTED; - } - #endif -} - -MA_API ma_result ma_mp3_init_file(const char* pFilePath, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_mp3* pMP3) -{ - ma_result result; - - result = ma_mp3_init_internal(pConfig, pMP3); - if (result != MA_SUCCESS) { - return result; - } - - #if !defined(MA_NO_MP3) - { - ma_bool32 mp3Result; - - mp3Result = ma_dr_mp3_init_file(&pMP3->dr, pFilePath, pAllocationCallbacks); - if (mp3Result != MA_TRUE) { - return MA_INVALID_FILE; - } - - ma_mp3_post_init(pMP3, pConfig, pAllocationCallbacks); - - return MA_SUCCESS; - } - #else - { - /* mp3 is disabled. */ - (void)pFilePath; - (void)pAllocationCallbacks; - return MA_NOT_IMPLEMENTED; - } - #endif -} - -MA_API ma_result ma_mp3_init_file_w(const wchar_t* pFilePath, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_mp3* pMP3) -{ - ma_result result; - - result = ma_mp3_init_internal(pConfig, pMP3); - if (result != MA_SUCCESS) { - return result; - } - - #if !defined(MA_NO_MP3) - { - ma_bool32 mp3Result; - - mp3Result = ma_dr_mp3_init_file_w(&pMP3->dr, pFilePath, pAllocationCallbacks); - if (mp3Result != MA_TRUE) { - return MA_INVALID_FILE; - } - - ma_mp3_post_init(pMP3, pConfig, pAllocationCallbacks); - - return MA_SUCCESS; - } - #else - { - /* mp3 is disabled. */ - (void)pFilePath; - (void)pAllocationCallbacks; - return MA_NOT_IMPLEMENTED; - } - #endif -} - -MA_API ma_result ma_mp3_init_memory(const void* pData, size_t dataSize, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_mp3* pMP3) -{ - ma_result result; - - result = ma_mp3_init_internal(pConfig, pMP3); - if (result != MA_SUCCESS) { - return result; - } - - #if !defined(MA_NO_MP3) - { - ma_bool32 mp3Result; - - mp3Result = ma_dr_mp3_init_memory(&pMP3->dr, pData, dataSize, pAllocationCallbacks); - if (mp3Result != MA_TRUE) { - return MA_INVALID_FILE; - } - - ma_mp3_post_init(pMP3, pConfig, pAllocationCallbacks); - - return MA_SUCCESS; - } - #else - { - /* mp3 is disabled. */ - (void)pData; - (void)dataSize; - (void)pAllocationCallbacks; - return MA_NOT_IMPLEMENTED; - } - #endif -} - -MA_API void ma_mp3_uninit(ma_mp3* pMP3, const ma_allocation_callbacks* pAllocationCallbacks) -{ - if (pMP3 == NULL) { - return; - } - - #if !defined(MA_NO_MP3) - { - ma_dr_mp3_uninit(&pMP3->dr); - } - #else - { - /* mp3 is disabled. Should never hit this since initialization would have failed. */ - MA_ASSERT(MA_FALSE); - } - #endif - - /* Seek points need to be freed after the MP3 decoder has been uninitialized to ensure they're no longer being referenced. */ - ma_free(pMP3->pSeekPoints, pAllocationCallbacks); - - ma_data_source_uninit(&pMP3->ds); -} - -MA_API ma_result ma_mp3_read_pcm_frames(ma_mp3* pMP3, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead) -{ - if (pFramesRead != NULL) { - *pFramesRead = 0; - } - - if (frameCount == 0) { - return MA_INVALID_ARGS; - } - - if (pMP3 == NULL) { - return MA_INVALID_ARGS; - } - - #if !defined(MA_NO_MP3) - { - /* We always use floating point format. */ - ma_result result = MA_SUCCESS; /* Must be initialized to MA_SUCCESS. */ - ma_uint64 totalFramesRead = 0; - ma_format format; - - ma_mp3_get_data_format(pMP3, &format, NULL, NULL, NULL, 0); - - switch (format) - { - case ma_format_f32: - { - totalFramesRead = ma_dr_mp3_read_pcm_frames_f32(&pMP3->dr, frameCount, (float*)pFramesOut); - } break; - - case ma_format_s16: - { - totalFramesRead = ma_dr_mp3_read_pcm_frames_s16(&pMP3->dr, frameCount, (ma_int16*)pFramesOut); - } break; - - case ma_format_u8: - case ma_format_s24: - case ma_format_s32: - case ma_format_unknown: - default: - { - return MA_INVALID_OPERATION; - }; - } - - /* In the future we'll update ma_dr_mp3 to return MA_AT_END for us. */ - if (totalFramesRead == 0) { - result = MA_AT_END; - } - - if (pFramesRead != NULL) { - *pFramesRead = totalFramesRead; - } - - return result; - } - #else - { - /* mp3 is disabled. Should never hit this since initialization would have failed. */ - MA_ASSERT(MA_FALSE); - - (void)pFramesOut; - (void)frameCount; - (void)pFramesRead; - - return MA_NOT_IMPLEMENTED; - } - #endif -} - -MA_API ma_result ma_mp3_seek_to_pcm_frame(ma_mp3* pMP3, ma_uint64 frameIndex) -{ - if (pMP3 == NULL) { - return MA_INVALID_ARGS; - } - - #if !defined(MA_NO_MP3) - { - ma_bool32 mp3Result; - - mp3Result = ma_dr_mp3_seek_to_pcm_frame(&pMP3->dr, frameIndex); - if (mp3Result != MA_TRUE) { - return MA_ERROR; - } - - return MA_SUCCESS; - } - #else - { - /* mp3 is disabled. Should never hit this since initialization would have failed. */ - MA_ASSERT(MA_FALSE); - - (void)frameIndex; - - return MA_NOT_IMPLEMENTED; - } - #endif -} - -MA_API ma_result ma_mp3_get_data_format(ma_mp3* pMP3, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate, ma_channel* pChannelMap, size_t channelMapCap) -{ - /* Defaults for safety. */ - if (pFormat != NULL) { - *pFormat = ma_format_unknown; - } - if (pChannels != NULL) { - *pChannels = 0; - } - if (pSampleRate != NULL) { - *pSampleRate = 0; - } - if (pChannelMap != NULL) { - MA_ZERO_MEMORY(pChannelMap, sizeof(*pChannelMap) * channelMapCap); - } - - if (pMP3 == NULL) { - return MA_INVALID_OPERATION; - } - - if (pFormat != NULL) { - *pFormat = pMP3->format; - } - - #if !defined(MA_NO_MP3) - { - if (pChannels != NULL) { - *pChannels = pMP3->dr.channels; - } - - if (pSampleRate != NULL) { - *pSampleRate = pMP3->dr.sampleRate; - } - - if (pChannelMap != NULL) { - ma_channel_map_init_standard(ma_standard_channel_map_default, pChannelMap, channelMapCap, pMP3->dr.channels); - } - - return MA_SUCCESS; - } - #else - { - /* mp3 is disabled. Should never hit this since initialization would have failed. */ - MA_ASSERT(MA_FALSE); - return MA_NOT_IMPLEMENTED; - } - #endif -} - -MA_API ma_result ma_mp3_get_cursor_in_pcm_frames(ma_mp3* pMP3, ma_uint64* pCursor) -{ - if (pCursor == NULL) { - return MA_INVALID_ARGS; - } - - *pCursor = 0; /* Safety. */ - - if (pMP3 == NULL) { - return MA_INVALID_ARGS; - } - - #if !defined(MA_NO_MP3) - { - *pCursor = pMP3->dr.currentPCMFrame; - - return MA_SUCCESS; - } - #else - { - /* mp3 is disabled. Should never hit this since initialization would have failed. */ - MA_ASSERT(MA_FALSE); - return MA_NOT_IMPLEMENTED; - } - #endif -} - -MA_API ma_result ma_mp3_get_length_in_pcm_frames(ma_mp3* pMP3, ma_uint64* pLength) -{ - if (pLength == NULL) { - return MA_INVALID_ARGS; - } - - *pLength = 0; /* Safety. */ - - if (pMP3 == NULL) { - return MA_INVALID_ARGS; - } - - #if !defined(MA_NO_MP3) - { - *pLength = ma_dr_mp3_get_pcm_frame_count(&pMP3->dr); - - return MA_SUCCESS; - } - #else - { - /* mp3 is disabled. Should never hit this since initialization would have failed. */ - MA_ASSERT(MA_FALSE); - return MA_NOT_IMPLEMENTED; - } - #endif -} - - -static ma_result ma_decoding_backend_init__mp3(void* pUserData, ma_read_proc onRead, ma_seek_proc onSeek, ma_tell_proc onTell, void* pReadSeekTellUserData, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_data_source** ppBackend) -{ - ma_result result; - ma_mp3* pMP3; - - (void)pUserData; /* For now not using pUserData, but once we start storing the vorbis decoder state within the ma_decoder structure this will be set to the decoder so we can avoid a malloc. */ - - /* For now we're just allocating the decoder backend on the heap. */ - pMP3 = (ma_mp3*)ma_malloc(sizeof(*pMP3), pAllocationCallbacks); - if (pMP3 == NULL) { - return MA_OUT_OF_MEMORY; - } - - result = ma_mp3_init(onRead, onSeek, onTell, pReadSeekTellUserData, pConfig, pAllocationCallbacks, pMP3); - if (result != MA_SUCCESS) { - ma_free(pMP3, pAllocationCallbacks); - return result; - } - - *ppBackend = pMP3; - - return MA_SUCCESS; -} - -static ma_result ma_decoding_backend_init_file__mp3(void* pUserData, const char* pFilePath, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_data_source** ppBackend) -{ - ma_result result; - ma_mp3* pMP3; - - (void)pUserData; /* For now not using pUserData, but once we start storing the vorbis decoder state within the ma_decoder structure this will be set to the decoder so we can avoid a malloc. */ - - /* For now we're just allocating the decoder backend on the heap. */ - pMP3 = (ma_mp3*)ma_malloc(sizeof(*pMP3), pAllocationCallbacks); - if (pMP3 == NULL) { - return MA_OUT_OF_MEMORY; - } - - result = ma_mp3_init_file(pFilePath, pConfig, pAllocationCallbacks, pMP3); - if (result != MA_SUCCESS) { - ma_free(pMP3, pAllocationCallbacks); - return result; - } - - *ppBackend = pMP3; - - return MA_SUCCESS; -} - -static ma_result ma_decoding_backend_init_file_w__mp3(void* pUserData, const wchar_t* pFilePath, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_data_source** ppBackend) -{ - ma_result result; - ma_mp3* pMP3; - - (void)pUserData; /* For now not using pUserData, but once we start storing the vorbis decoder state within the ma_decoder structure this will be set to the decoder so we can avoid a malloc. */ - - /* For now we're just allocating the decoder backend on the heap. */ - pMP3 = (ma_mp3*)ma_malloc(sizeof(*pMP3), pAllocationCallbacks); - if (pMP3 == NULL) { - return MA_OUT_OF_MEMORY; - } - - result = ma_mp3_init_file_w(pFilePath, pConfig, pAllocationCallbacks, pMP3); - if (result != MA_SUCCESS) { - ma_free(pMP3, pAllocationCallbacks); - return result; - } - - *ppBackend = pMP3; - - return MA_SUCCESS; -} - -static ma_result ma_decoding_backend_init_memory__mp3(void* pUserData, const void* pData, size_t dataSize, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_data_source** ppBackend) -{ - ma_result result; - ma_mp3* pMP3; - - (void)pUserData; /* For now not using pUserData, but once we start storing the vorbis decoder state within the ma_decoder structure this will be set to the decoder so we can avoid a malloc. */ - - /* For now we're just allocating the decoder backend on the heap. */ - pMP3 = (ma_mp3*)ma_malloc(sizeof(*pMP3), pAllocationCallbacks); - if (pMP3 == NULL) { - return MA_OUT_OF_MEMORY; - } - - result = ma_mp3_init_memory(pData, dataSize, pConfig, pAllocationCallbacks, pMP3); - if (result != MA_SUCCESS) { - ma_free(pMP3, pAllocationCallbacks); - return result; - } - - *ppBackend = pMP3; - - return MA_SUCCESS; -} - -static void ma_decoding_backend_uninit__mp3(void* pUserData, ma_data_source* pBackend, const ma_allocation_callbacks* pAllocationCallbacks) -{ - ma_mp3* pMP3 = (ma_mp3*)pBackend; - - (void)pUserData; - - ma_mp3_uninit(pMP3, pAllocationCallbacks); - ma_free(pMP3, pAllocationCallbacks); -} - -static ma_decoding_backend_vtable g_ma_decoding_backend_vtable_mp3 = -{ - ma_decoding_backend_init__mp3, - ma_decoding_backend_init_file__mp3, - ma_decoding_backend_init_file_w__mp3, - ma_decoding_backend_init_memory__mp3, - ma_decoding_backend_uninit__mp3 -}; - -static ma_result ma_decoder_init_mp3__internal(const ma_decoder_config* pConfig, ma_decoder* pDecoder) -{ - return ma_decoder_init_from_vtable__internal(&g_ma_decoding_backend_vtable_mp3, NULL, pConfig, pDecoder); -} - -static ma_result ma_decoder_init_mp3_from_file__internal(const char* pFilePath, const ma_decoder_config* pConfig, ma_decoder* pDecoder) -{ - return ma_decoder_init_from_file__internal(&g_ma_decoding_backend_vtable_mp3, NULL, pFilePath, pConfig, pDecoder); -} - -static ma_result ma_decoder_init_mp3_from_file_w__internal(const wchar_t* pFilePath, const ma_decoder_config* pConfig, ma_decoder* pDecoder) -{ - return ma_decoder_init_from_file_w__internal(&g_ma_decoding_backend_vtable_mp3, NULL, pFilePath, pConfig, pDecoder); -} - -static ma_result ma_decoder_init_mp3_from_memory__internal(const void* pData, size_t dataSize, const ma_decoder_config* pConfig, ma_decoder* pDecoder) -{ - return ma_decoder_init_from_memory__internal(&g_ma_decoding_backend_vtable_mp3, NULL, pData, dataSize, pConfig, pDecoder); -} -#endif /* ma_dr_mp3_h */ - -/* Vorbis */ -#ifdef STB_VORBIS_INCLUDE_STB_VORBIS_H -#define MA_HAS_VORBIS - -/* The size in bytes of each chunk of data to read from the Vorbis stream. */ -#define MA_VORBIS_DATA_CHUNK_SIZE 4096 - -typedef struct -{ - ma_data_source_base ds; - ma_read_proc onRead; - ma_seek_proc onSeek; - ma_tell_proc onTell; - void* pReadSeekTellUserData; - ma_allocation_callbacks allocationCallbacks; /* Store the allocation callbacks within the structure because we may need to dynamically expand a buffer in ma_stbvorbis_read_pcm_frames() when using push mode. */ - ma_format format; /* Only f32 is allowed with stb_vorbis. */ - ma_uint32 channels; - ma_uint32 sampleRate; - ma_uint64 cursor; -#if !defined(MA_NO_VORBIS) - stb_vorbis* stb; - ma_bool32 usingPushMode; - struct - { - ma_uint8* pData; - size_t dataSize; - size_t dataCapacity; - size_t audioStartOffsetInBytes; - ma_uint32 framesConsumed; /* The number of frames consumed in ppPacketData. */ - ma_uint32 framesRemaining; /* The number of frames remaining in ppPacketData. */ - float** ppPacketData; - } push; -#endif -} ma_stbvorbis; - -MA_API ma_result ma_stbvorbis_init(ma_read_proc onRead, ma_seek_proc onSeek, ma_tell_proc onTell, void* pReadSeekTellUserData, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_stbvorbis* pVorbis); -MA_API ma_result ma_stbvorbis_init_file(const char* pFilePath, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_stbvorbis* pVorbis); -MA_API ma_result ma_stbvorbis_init_memory(const void* pData, size_t dataSize, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_stbvorbis* pVorbis); -MA_API void ma_stbvorbis_uninit(ma_stbvorbis* pVorbis, const ma_allocation_callbacks* pAllocationCallbacks); -MA_API ma_result ma_stbvorbis_read_pcm_frames(ma_stbvorbis* pVorbis, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead); -MA_API ma_result ma_stbvorbis_seek_to_pcm_frame(ma_stbvorbis* pVorbis, ma_uint64 frameIndex); -MA_API ma_result ma_stbvorbis_get_data_format(ma_stbvorbis* pVorbis, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate, ma_channel* pChannelMap, size_t channelMapCap); -MA_API ma_result ma_stbvorbis_get_cursor_in_pcm_frames(ma_stbvorbis* pVorbis, ma_uint64* pCursor); -MA_API ma_result ma_stbvorbis_get_length_in_pcm_frames(ma_stbvorbis* pVorbis, ma_uint64* pLength); - - -static ma_result ma_stbvorbis_ds_read(ma_data_source* pDataSource, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead) -{ - return ma_stbvorbis_read_pcm_frames((ma_stbvorbis*)pDataSource, pFramesOut, frameCount, pFramesRead); -} - -static ma_result ma_stbvorbis_ds_seek(ma_data_source* pDataSource, ma_uint64 frameIndex) -{ - return ma_stbvorbis_seek_to_pcm_frame((ma_stbvorbis*)pDataSource, frameIndex); -} - -static ma_result ma_stbvorbis_ds_get_data_format(ma_data_source* pDataSource, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate, ma_channel* pChannelMap, size_t channelMapCap) -{ - return ma_stbvorbis_get_data_format((ma_stbvorbis*)pDataSource, pFormat, pChannels, pSampleRate, pChannelMap, channelMapCap); -} - -static ma_result ma_stbvorbis_ds_get_cursor(ma_data_source* pDataSource, ma_uint64* pCursor) -{ - return ma_stbvorbis_get_cursor_in_pcm_frames((ma_stbvorbis*)pDataSource, pCursor); -} - -static ma_result ma_stbvorbis_ds_get_length(ma_data_source* pDataSource, ma_uint64* pLength) -{ - return ma_stbvorbis_get_length_in_pcm_frames((ma_stbvorbis*)pDataSource, pLength); -} - -static ma_data_source_vtable g_ma_stbvorbis_ds_vtable = -{ - ma_stbvorbis_ds_read, - ma_stbvorbis_ds_seek, - ma_stbvorbis_ds_get_data_format, - ma_stbvorbis_ds_get_cursor, - ma_stbvorbis_ds_get_length, - NULL, /* onSetLooping */ - 0 -}; - - -static ma_result ma_stbvorbis_init_internal(const ma_decoding_backend_config* pConfig, ma_stbvorbis* pVorbis) -{ - ma_result result; - ma_data_source_config dataSourceConfig; - - (void)pConfig; - - if (pVorbis == NULL) { - return MA_INVALID_ARGS; - } - - MA_ZERO_OBJECT(pVorbis); - pVorbis->format = ma_format_f32; /* Only supporting f32. */ - - dataSourceConfig = ma_data_source_config_init(); - dataSourceConfig.vtable = &g_ma_stbvorbis_ds_vtable; - - result = ma_data_source_init(&dataSourceConfig, &pVorbis->ds); - if (result != MA_SUCCESS) { - return result; /* Failed to initialize the base data source. */ - } - - return MA_SUCCESS; -} - -#if !defined(MA_NO_VORBIS) -static ma_result ma_stbvorbis_post_init(ma_stbvorbis* pVorbis) -{ - stb_vorbis_info info; - - MA_ASSERT(pVorbis != NULL); - - info = stb_vorbis_get_info(pVorbis->stb); - - pVorbis->channels = info.channels; - pVorbis->sampleRate = info.sample_rate; - - return MA_SUCCESS; -} - -static ma_result ma_stbvorbis_init_internal_decoder_push(ma_stbvorbis* pVorbis) -{ - ma_result result; - stb_vorbis* stb; - size_t dataSize = 0; - size_t dataCapacity = 0; - ma_uint8* pData = NULL; /* <-- Must be initialized to NULL. */ - - for (;;) { - int vorbisError; - int consumedDataSize; /* <-- Fill by stb_vorbis_open_pushdata(). */ - size_t bytesRead; - ma_uint8* pNewData; - - /* Allocate memory for the new chunk. */ - dataCapacity += MA_VORBIS_DATA_CHUNK_SIZE; - pNewData = (ma_uint8*)ma_realloc(pData, dataCapacity, &pVorbis->allocationCallbacks); - if (pNewData == NULL) { - ma_free(pData, &pVorbis->allocationCallbacks); - return MA_OUT_OF_MEMORY; - } - - pData = pNewData; - - /* Read in the next chunk. */ - result = pVorbis->onRead(pVorbis->pReadSeekTellUserData, ma_offset_ptr(pData, dataSize), (dataCapacity - dataSize), &bytesRead); - dataSize += bytesRead; - - if (result != MA_SUCCESS) { - ma_free(pData, &pVorbis->allocationCallbacks); - return result; - } - - /* We have a maximum of 31 bits with stb_vorbis. */ - if (dataSize > INT_MAX) { - ma_free(pData, &pVorbis->allocationCallbacks); - return MA_TOO_BIG; - } - - stb = stb_vorbis_open_pushdata(pData, (int)dataSize, &consumedDataSize, &vorbisError, NULL); - if (stb != NULL) { - /* - Successfully opened the Vorbis decoder. We might have some leftover unprocessed - data so we'll need to move that down to the front. - */ - dataSize -= (size_t)consumedDataSize; /* Consume the data. */ - MA_MOVE_MEMORY(pData, ma_offset_ptr(pData, consumedDataSize), dataSize); - - /* - We need to track the start point so we can seek back to the start of the audio - data when seeking. - */ - pVorbis->push.audioStartOffsetInBytes = consumedDataSize; - - break; - } else { - /* Failed to open the decoder. */ - if (vorbisError == VORBIS_need_more_data) { - continue; - } else { - ma_free(pData, &pVorbis->allocationCallbacks); - return MA_ERROR; /* Failed to open the stb_vorbis decoder. */ - } - } - } - - MA_ASSERT(stb != NULL); - pVorbis->stb = stb; - pVorbis->push.pData = pData; - pVorbis->push.dataSize = dataSize; - pVorbis->push.dataCapacity = dataCapacity; - - return MA_SUCCESS; -} -#endif - -MA_API ma_result ma_stbvorbis_init(ma_read_proc onRead, ma_seek_proc onSeek, ma_tell_proc onTell, void* pReadSeekTellUserData, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_stbvorbis* pVorbis) -{ - ma_result result; - - result = ma_stbvorbis_init_internal(pConfig, pVorbis); - if (result != MA_SUCCESS) { - return result; - } - - if (onRead == NULL || onSeek == NULL) { - return MA_INVALID_ARGS; /* onRead and onSeek are mandatory. */ - } - - pVorbis->onRead = onRead; - pVorbis->onSeek = onSeek; - pVorbis->onTell = onTell; - pVorbis->pReadSeekTellUserData = pReadSeekTellUserData; - ma_allocation_callbacks_init_copy(&pVorbis->allocationCallbacks, pAllocationCallbacks); - - #if !defined(MA_NO_VORBIS) - { - /* - stb_vorbis lacks a callback based API for its pulling API which means we're stuck with the - pushing API. In order for us to be able to successfully initialize the decoder we need to - supply it with enough data. We need to keep loading data until we have enough. - */ - result = ma_stbvorbis_init_internal_decoder_push(pVorbis); - if (result != MA_SUCCESS) { - return result; - } - - pVorbis->usingPushMode = MA_TRUE; - - result = ma_stbvorbis_post_init(pVorbis); - if (result != MA_SUCCESS) { - stb_vorbis_close(pVorbis->stb); - ma_free(pVorbis->push.pData, pAllocationCallbacks); - return result; - } - - return MA_SUCCESS; - } - #else - { - /* vorbis is disabled. */ - (void)pAllocationCallbacks; - return MA_NOT_IMPLEMENTED; - } - #endif -} - -MA_API ma_result ma_stbvorbis_init_file(const char* pFilePath, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_stbvorbis* pVorbis) -{ - ma_result result; - - result = ma_stbvorbis_init_internal(pConfig, pVorbis); - if (result != MA_SUCCESS) { - return result; - } - - #if !defined(MA_NO_VORBIS) - { - (void)pAllocationCallbacks; /* Don't know how to make use of this with stb_vorbis. */ - - /* We can use stb_vorbis' pull mode for file based streams. */ - pVorbis->stb = stb_vorbis_open_filename(pFilePath, NULL, NULL); - if (pVorbis->stb == NULL) { - return MA_INVALID_FILE; - } - - pVorbis->usingPushMode = MA_FALSE; - - result = ma_stbvorbis_post_init(pVorbis); - if (result != MA_SUCCESS) { - stb_vorbis_close(pVorbis->stb); - return result; - } - - return MA_SUCCESS; - } - #else - { - /* vorbis is disabled. */ - (void)pFilePath; - (void)pAllocationCallbacks; - return MA_NOT_IMPLEMENTED; - } - #endif -} - -MA_API ma_result ma_stbvorbis_init_memory(const void* pData, size_t dataSize, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_stbvorbis* pVorbis) -{ - ma_result result; - - result = ma_stbvorbis_init_internal(pConfig, pVorbis); - if (result != MA_SUCCESS) { - return result; - } - - #if !defined(MA_NO_VORBIS) - { - (void)pAllocationCallbacks; - - /* stb_vorbis uses an int as its size specifier, restricting it to 32-bit even on 64-bit systems. *sigh*. */ - if (dataSize > INT_MAX) { - return MA_TOO_BIG; - } - - pVorbis->stb = stb_vorbis_open_memory((const unsigned char*)pData, (int)dataSize, NULL, NULL); - if (pVorbis->stb == NULL) { - return MA_INVALID_FILE; - } - - pVorbis->usingPushMode = MA_FALSE; - - result = ma_stbvorbis_post_init(pVorbis); - if (result != MA_SUCCESS) { - stb_vorbis_close(pVorbis->stb); - return result; - } - - return MA_SUCCESS; - } - #else - { - /* vorbis is disabled. */ - (void)pData; - (void)dataSize; - (void)pAllocationCallbacks; - return MA_NOT_IMPLEMENTED; - } - #endif -} - -MA_API void ma_stbvorbis_uninit(ma_stbvorbis* pVorbis, const ma_allocation_callbacks* pAllocationCallbacks) -{ - if (pVorbis == NULL) { - return; - } - - #if !defined(MA_NO_VORBIS) - { - stb_vorbis_close(pVorbis->stb); - - /* We'll have to clear some memory if we're using push mode. */ - if (pVorbis->usingPushMode) { - ma_free(pVorbis->push.pData, pAllocationCallbacks); - } - } - #else - { - /* vorbis is disabled. Should never hit this since initialization would have failed. */ - MA_ASSERT(MA_FALSE); - } - #endif - - ma_data_source_uninit(&pVorbis->ds); -} - -MA_API ma_result ma_stbvorbis_read_pcm_frames(ma_stbvorbis* pVorbis, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead) -{ - if (pFramesRead != NULL) { - *pFramesRead = 0; - } - - if (frameCount == 0) { - return MA_INVALID_ARGS; - } - - if (pVorbis == NULL) { - return MA_INVALID_ARGS; - } - - #if !defined(MA_NO_VORBIS) - { - /* We always use floating point format. */ - ma_result result = MA_SUCCESS; /* Must be initialized to MA_SUCCESS. */ - ma_uint64 totalFramesRead = 0; - ma_format format; - ma_uint32 channels; - - ma_stbvorbis_get_data_format(pVorbis, &format, &channels, NULL, NULL, 0); - - if (format == ma_format_f32) { - /* We read differently depending on whether or not we're using push mode. */ - if (pVorbis->usingPushMode) { - /* Push mode. This is the complex case. */ - float* pFramesOutF32 = (float*)pFramesOut; - - while (totalFramesRead < frameCount) { - /* The first thing to do is read from any already-cached frames. */ - ma_uint32 framesToReadFromCache = (ma_uint32)ma_min(pVorbis->push.framesRemaining, (frameCount - totalFramesRead)); /* Safe cast because pVorbis->framesRemaining is 32-bit. */ - - /* The output pointer can be null in which case we just treat it as a seek. */ - if (pFramesOut != NULL) { - ma_uint64 iFrame; - for (iFrame = 0; iFrame < framesToReadFromCache; iFrame += 1) { - ma_uint32 iChannel; - for (iChannel = 0; iChannel < pVorbis->channels; iChannel += 1) { - pFramesOutF32[iChannel] = pVorbis->push.ppPacketData[iChannel][pVorbis->push.framesConsumed + iFrame]; - } - - pFramesOutF32 += pVorbis->channels; - } - } - - /* Update pointers and counters. */ - pVorbis->push.framesConsumed += framesToReadFromCache; - pVorbis->push.framesRemaining -= framesToReadFromCache; - totalFramesRead += framesToReadFromCache; - - /* Don't bother reading any more frames right now if we've just finished loading. */ - if (totalFramesRead == frameCount) { - break; - } - - MA_ASSERT(pVorbis->push.framesRemaining == 0); - - /* Getting here means we've run out of cached frames. We'll need to load some more. */ - for (;;) { - int samplesRead = 0; - int consumedDataSize; - - /* We need to case dataSize to an int, so make sure we can do it safely. */ - if (pVorbis->push.dataSize > INT_MAX) { - break; /* Too big. */ - } - - consumedDataSize = stb_vorbis_decode_frame_pushdata(pVorbis->stb, pVorbis->push.pData, (int)pVorbis->push.dataSize, NULL, &pVorbis->push.ppPacketData, &samplesRead); - if (consumedDataSize != 0) { - /* Successfully decoded a Vorbis frame. Consume the data. */ - pVorbis->push.dataSize -= (size_t)consumedDataSize; - MA_MOVE_MEMORY(pVorbis->push.pData, ma_offset_ptr(pVorbis->push.pData, consumedDataSize), pVorbis->push.dataSize); - - pVorbis->push.framesConsumed = 0; - pVorbis->push.framesRemaining = samplesRead; - - break; - } else { - /* Not enough data. Read more. */ - size_t bytesRead; - - /* Expand the data buffer if necessary. */ - if (pVorbis->push.dataCapacity == pVorbis->push.dataSize) { - size_t newCap = pVorbis->push.dataCapacity + MA_VORBIS_DATA_CHUNK_SIZE; - ma_uint8* pNewData; - - pNewData = (ma_uint8*)ma_realloc(pVorbis->push.pData, newCap, &pVorbis->allocationCallbacks); - if (pNewData == NULL) { - result = MA_OUT_OF_MEMORY; - break; - } - - pVorbis->push.pData = pNewData; - pVorbis->push.dataCapacity = newCap; - } - - /* We should have enough room to load some data. */ - result = pVorbis->onRead(pVorbis->pReadSeekTellUserData, ma_offset_ptr(pVorbis->push.pData, pVorbis->push.dataSize), (pVorbis->push.dataCapacity - pVorbis->push.dataSize), &bytesRead); - pVorbis->push.dataSize += bytesRead; - - if (result != MA_SUCCESS) { - break; /* Failed to read any data. Get out. */ - } - } - } - - /* If we don't have a success code at this point it means we've encountered an error or the end of the file has been reached (probably the latter). */ - if (result != MA_SUCCESS) { - break; - } - } - } else { - /* Pull mode. This is the simple case, but we still need to run in a loop because stb_vorbis loves using 32-bit instead of 64-bit. */ - while (totalFramesRead < frameCount) { - ma_uint64 framesRemaining = (frameCount - totalFramesRead); - int framesRead; - - if (framesRemaining > INT_MAX) { - framesRemaining = INT_MAX; - } - - framesRead = stb_vorbis_get_samples_float_interleaved(pVorbis->stb, channels, (float*)ma_offset_pcm_frames_ptr(pFramesOut, totalFramesRead, format, channels), (int)framesRemaining * channels); /* Safe cast. */ - totalFramesRead += framesRead; - - if (framesRead < (int)framesRemaining) { - break; /* Nothing left to read. Get out. */ - } - } - } - } else { - result = MA_INVALID_ARGS; - } - - pVorbis->cursor += totalFramesRead; - - if (totalFramesRead == 0) { - result = MA_AT_END; - } - - if (pFramesRead != NULL) { - *pFramesRead = totalFramesRead; - } - - if (result == MA_SUCCESS && totalFramesRead == 0) { - result = MA_AT_END; - } - - return result; - } - #else - { - /* vorbis is disabled. Should never hit this since initialization would have failed. */ - MA_ASSERT(MA_FALSE); - - (void)pFramesOut; - (void)frameCount; - (void)pFramesRead; - - return MA_NOT_IMPLEMENTED; - } - #endif -} - -MA_API ma_result ma_stbvorbis_seek_to_pcm_frame(ma_stbvorbis* pVorbis, ma_uint64 frameIndex) -{ - if (pVorbis == NULL) { - return MA_INVALID_ARGS; - } - - #if !defined(MA_NO_VORBIS) - { - /* Different seeking methods depending on whether or not we're using push mode. */ - if (pVorbis->usingPushMode) { - /* Push mode. This is the complex case. */ - ma_result result; - float buffer[4096]; - - /* If we're seeking backwards, we need to seek back to the start and then brute-force forward. */ - if (frameIndex < pVorbis->cursor) { - if (frameIndex > 0x7FFFFFFF) { - return MA_INVALID_ARGS; /* Trying to seek beyond the 32-bit maximum of stb_vorbis. */ - } - - /* - This is wildly inefficient due to me having trouble getting sample exact seeking working - robustly with stb_vorbis_flush_pushdata(). The only way I can think to make this work - perfectly is to reinitialize the decoder. Note that we only enter this path when seeking - backwards. This will hopefully be removed once we get our own Vorbis decoder implemented. - */ - stb_vorbis_close(pVorbis->stb); - ma_free(pVorbis->push.pData, &pVorbis->allocationCallbacks); - - MA_ZERO_OBJECT(&pVorbis->push); - - /* Seek to the start of the file. */ - result = pVorbis->onSeek(pVorbis->pReadSeekTellUserData, 0, ma_seek_origin_start); - if (result != MA_SUCCESS) { - return result; - } - - result = ma_stbvorbis_init_internal_decoder_push(pVorbis); - if (result != MA_SUCCESS) { - return result; - } - - /* At this point we should be sitting on the first frame. */ - pVorbis->cursor = 0; - } - - /* We're just brute-forcing this for now. */ - while (pVorbis->cursor < frameIndex) { - ma_uint64 framesRead; - ma_uint64 framesToRead = ma_countof(buffer)/pVorbis->channels; - if (framesToRead > (frameIndex - pVorbis->cursor)) { - framesToRead = (frameIndex - pVorbis->cursor); - } - - result = ma_stbvorbis_read_pcm_frames(pVorbis, buffer, framesToRead, &framesRead); - if (result != MA_SUCCESS) { - return result; - } - } - } else { - /* Pull mode. This is the simple case. */ - int vorbisResult; - - if (frameIndex > UINT_MAX) { - return MA_INVALID_ARGS; /* Trying to seek beyond the 32-bit maximum of stb_vorbis. */ - } - - vorbisResult = stb_vorbis_seek(pVorbis->stb, (unsigned int)frameIndex); /* Safe cast. */ - if (vorbisResult == 0) { - return MA_ERROR; /* See failed. */ - } - - pVorbis->cursor = frameIndex; - } - - return MA_SUCCESS; - } - #else - { - /* vorbis is disabled. Should never hit this since initialization would have failed. */ - MA_ASSERT(MA_FALSE); - - (void)frameIndex; - - return MA_NOT_IMPLEMENTED; - } - #endif -} - -MA_API ma_result ma_stbvorbis_get_data_format(ma_stbvorbis* pVorbis, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate, ma_channel* pChannelMap, size_t channelMapCap) -{ - /* Defaults for safety. */ - if (pFormat != NULL) { - *pFormat = ma_format_unknown; - } - if (pChannels != NULL) { - *pChannels = 0; - } - if (pSampleRate != NULL) { - *pSampleRate = 0; - } - if (pChannelMap != NULL) { - MA_ZERO_MEMORY(pChannelMap, sizeof(*pChannelMap) * channelMapCap); - } - - if (pVorbis == NULL) { - return MA_INVALID_OPERATION; - } - - if (pFormat != NULL) { - *pFormat = pVorbis->format; - } - - #if !defined(MA_NO_VORBIS) - { - if (pChannels != NULL) { - *pChannels = pVorbis->channels; - } - - if (pSampleRate != NULL) { - *pSampleRate = pVorbis->sampleRate; - } - - if (pChannelMap != NULL) { - ma_channel_map_init_standard(ma_standard_channel_map_vorbis, pChannelMap, channelMapCap, pVorbis->channels); - } - - return MA_SUCCESS; - } - #else - { - /* vorbis is disabled. Should never hit this since initialization would have failed. */ - MA_ASSERT(MA_FALSE); - return MA_NOT_IMPLEMENTED; - } - #endif -} - -MA_API ma_result ma_stbvorbis_get_cursor_in_pcm_frames(ma_stbvorbis* pVorbis, ma_uint64* pCursor) -{ - if (pCursor == NULL) { - return MA_INVALID_ARGS; - } - - *pCursor = 0; /* Safety. */ - - if (pVorbis == NULL) { - return MA_INVALID_ARGS; - } - - #if !defined(MA_NO_VORBIS) - { - *pCursor = pVorbis->cursor; - - return MA_SUCCESS; - } - #else - { - /* vorbis is disabled. Should never hit this since initialization would have failed. */ - MA_ASSERT(MA_FALSE); - return MA_NOT_IMPLEMENTED; - } - #endif -} - -MA_API ma_result ma_stbvorbis_get_length_in_pcm_frames(ma_stbvorbis* pVorbis, ma_uint64* pLength) -{ - if (pLength == NULL) { - return MA_INVALID_ARGS; - } - - *pLength = 0; /* Safety. */ - - if (pVorbis == NULL) { - return MA_INVALID_ARGS; - } - - #if !defined(MA_NO_VORBIS) - { - if (pVorbis->usingPushMode) { - *pLength = 0; /* I don't know of a good way to determine this reliably with stb_vorbis and push mode. */ - } else { - *pLength = stb_vorbis_stream_length_in_samples(pVorbis->stb); - } - - return MA_SUCCESS; - } - #else - { - /* vorbis is disabled. Should never hit this since initialization would have failed. */ - MA_ASSERT(MA_FALSE); - return MA_NOT_IMPLEMENTED; - } - #endif -} - - -static ma_result ma_decoding_backend_init__stbvorbis(void* pUserData, ma_read_proc onRead, ma_seek_proc onSeek, ma_tell_proc onTell, void* pReadSeekTellUserData, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_data_source** ppBackend) -{ - ma_result result; - ma_stbvorbis* pVorbis; - - (void)pUserData; /* For now not using pUserData, but once we start storing the vorbis decoder state within the ma_decoder structure this will be set to the decoder so we can avoid a malloc. */ - - /* For now we're just allocating the decoder backend on the heap. */ - pVorbis = (ma_stbvorbis*)ma_malloc(sizeof(*pVorbis), pAllocationCallbacks); - if (pVorbis == NULL) { - return MA_OUT_OF_MEMORY; - } - - result = ma_stbvorbis_init(onRead, onSeek, onTell, pReadSeekTellUserData, pConfig, pAllocationCallbacks, pVorbis); - if (result != MA_SUCCESS) { - ma_free(pVorbis, pAllocationCallbacks); - return result; - } - - *ppBackend = pVorbis; - - return MA_SUCCESS; -} - -static ma_result ma_decoding_backend_init_file__stbvorbis(void* pUserData, const char* pFilePath, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_data_source** ppBackend) -{ - ma_result result; - ma_stbvorbis* pVorbis; - - (void)pUserData; /* For now not using pUserData, but once we start storing the vorbis decoder state within the ma_decoder structure this will be set to the decoder so we can avoid a malloc. */ - - /* For now we're just allocating the decoder backend on the heap. */ - pVorbis = (ma_stbvorbis*)ma_malloc(sizeof(*pVorbis), pAllocationCallbacks); - if (pVorbis == NULL) { - return MA_OUT_OF_MEMORY; - } - - result = ma_stbvorbis_init_file(pFilePath, pConfig, pAllocationCallbacks, pVorbis); - if (result != MA_SUCCESS) { - ma_free(pVorbis, pAllocationCallbacks); - return result; - } - - *ppBackend = pVorbis; - - return MA_SUCCESS; -} - -static ma_result ma_decoding_backend_init_memory__stbvorbis(void* pUserData, const void* pData, size_t dataSize, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_data_source** ppBackend) -{ - ma_result result; - ma_stbvorbis* pVorbis; - - (void)pUserData; /* For now not using pUserData, but once we start storing the vorbis decoder state within the ma_decoder structure this will be set to the decoder so we can avoid a malloc. */ - - /* For now we're just allocating the decoder backend on the heap. */ - pVorbis = (ma_stbvorbis*)ma_malloc(sizeof(*pVorbis), pAllocationCallbacks); - if (pVorbis == NULL) { - return MA_OUT_OF_MEMORY; - } - - result = ma_stbvorbis_init_memory(pData, dataSize, pConfig, pAllocationCallbacks, pVorbis); - if (result != MA_SUCCESS) { - ma_free(pVorbis, pAllocationCallbacks); - return result; - } - - *ppBackend = pVorbis; - - return MA_SUCCESS; -} - -static void ma_decoding_backend_uninit__stbvorbis(void* pUserData, ma_data_source* pBackend, const ma_allocation_callbacks* pAllocationCallbacks) -{ - ma_stbvorbis* pVorbis = (ma_stbvorbis*)pBackend; - - (void)pUserData; - - ma_stbvorbis_uninit(pVorbis, pAllocationCallbacks); - ma_free(pVorbis, pAllocationCallbacks); -} - -static ma_decoding_backend_vtable g_ma_decoding_backend_vtable_stbvorbis = -{ - ma_decoding_backend_init__stbvorbis, - ma_decoding_backend_init_file__stbvorbis, - NULL, /* onInitFileW() */ - ma_decoding_backend_init_memory__stbvorbis, - ma_decoding_backend_uninit__stbvorbis -}; - -static ma_result ma_decoder_init_vorbis__internal(const ma_decoder_config* pConfig, ma_decoder* pDecoder) -{ - return ma_decoder_init_from_vtable__internal(&g_ma_decoding_backend_vtable_stbvorbis, NULL, pConfig, pDecoder); -} - -static ma_result ma_decoder_init_vorbis_from_file__internal(const char* pFilePath, const ma_decoder_config* pConfig, ma_decoder* pDecoder) -{ - return ma_decoder_init_from_file__internal(&g_ma_decoding_backend_vtable_stbvorbis, NULL, pFilePath, pConfig, pDecoder); -} - -static ma_result ma_decoder_init_vorbis_from_file_w__internal(const wchar_t* pFilePath, const ma_decoder_config* pConfig, ma_decoder* pDecoder) -{ - return ma_decoder_init_from_file_w__internal(&g_ma_decoding_backend_vtable_stbvorbis, NULL, pFilePath, pConfig, pDecoder); -} - -static ma_result ma_decoder_init_vorbis_from_memory__internal(const void* pData, size_t dataSize, const ma_decoder_config* pConfig, ma_decoder* pDecoder) -{ - return ma_decoder_init_from_memory__internal(&g_ma_decoding_backend_vtable_stbvorbis, NULL, pData, dataSize, pConfig, pDecoder); -} -#endif /* STB_VORBIS_INCLUDE_STB_VORBIS_H */ - - - -static ma_result ma_decoder__init_allocation_callbacks(const ma_decoder_config* pConfig, ma_decoder* pDecoder) -{ - MA_ASSERT(pDecoder != NULL); - - if (pConfig != NULL) { - return ma_allocation_callbacks_init_copy(&pDecoder->allocationCallbacks, &pConfig->allocationCallbacks); - } else { - pDecoder->allocationCallbacks = ma_allocation_callbacks_init_default(); - return MA_SUCCESS; - } -} - -static ma_result ma_decoder__data_source_on_read(ma_data_source* pDataSource, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead) -{ - return ma_decoder_read_pcm_frames((ma_decoder*)pDataSource, pFramesOut, frameCount, pFramesRead); -} - -static ma_result ma_decoder__data_source_on_seek(ma_data_source* pDataSource, ma_uint64 frameIndex) -{ - return ma_decoder_seek_to_pcm_frame((ma_decoder*)pDataSource, frameIndex); -} - -static ma_result ma_decoder__data_source_on_get_data_format(ma_data_source* pDataSource, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate, ma_channel* pChannelMap, size_t channelMapCap) -{ - return ma_decoder_get_data_format((ma_decoder*)pDataSource, pFormat, pChannels, pSampleRate, pChannelMap, channelMapCap); -} - -static ma_result ma_decoder__data_source_on_get_cursor(ma_data_source* pDataSource, ma_uint64* pCursor) -{ - return ma_decoder_get_cursor_in_pcm_frames((ma_decoder*)pDataSource, pCursor); -} - -static ma_result ma_decoder__data_source_on_get_length(ma_data_source* pDataSource, ma_uint64* pLength) -{ - return ma_decoder_get_length_in_pcm_frames((ma_decoder*)pDataSource, pLength); -} - -static ma_data_source_vtable g_ma_decoder_data_source_vtable = -{ - ma_decoder__data_source_on_read, - ma_decoder__data_source_on_seek, - ma_decoder__data_source_on_get_data_format, - ma_decoder__data_source_on_get_cursor, - ma_decoder__data_source_on_get_length, - NULL, /* onSetLooping */ - 0 -}; - -static ma_result ma_decoder__preinit(ma_decoder_read_proc onRead, ma_decoder_seek_proc onSeek, ma_decoder_tell_proc onTell, void* pUserData, const ma_decoder_config* pConfig, ma_decoder* pDecoder) -{ - ma_result result; - ma_data_source_config dataSourceConfig; - - MA_ASSERT(pConfig != NULL); - - if (pDecoder == NULL) { - return MA_INVALID_ARGS; - } - - MA_ZERO_OBJECT(pDecoder); - - dataSourceConfig = ma_data_source_config_init(); - dataSourceConfig.vtable = &g_ma_decoder_data_source_vtable; - - result = ma_data_source_init(&dataSourceConfig, &pDecoder->ds); - if (result != MA_SUCCESS) { - return result; - } - - pDecoder->onRead = onRead; - pDecoder->onSeek = onSeek; - pDecoder->onTell = onTell; - pDecoder->pUserData = pUserData; - - result = ma_decoder__init_allocation_callbacks(pConfig, pDecoder); - if (result != MA_SUCCESS) { - ma_data_source_uninit(&pDecoder->ds); - return result; - } - - return MA_SUCCESS; -} - -static ma_result ma_decoder__postinit(const ma_decoder_config* pConfig, ma_decoder* pDecoder) -{ - ma_result result; - - result = ma_decoder__init_data_converter(pDecoder, pConfig); - - /* If we failed post initialization we need to uninitialize the decoder before returning to prevent a memory leak. */ - if (result != MA_SUCCESS) { - ma_decoder_uninit(pDecoder); - return result; - } - - return result; -} - - -static ma_result ma_decoder_init__internal(ma_decoder_read_proc onRead, ma_decoder_seek_proc onSeek, void* pUserData, const ma_decoder_config* pConfig, ma_decoder* pDecoder) -{ - ma_result result = MA_NO_BACKEND; - - MA_ASSERT(pConfig != NULL); - MA_ASSERT(pDecoder != NULL); - - /* Silence some warnings in the case that we don't have any decoder backends enabled. */ - (void)onRead; - (void)onSeek; - (void)pUserData; - - - /* If we've specified a specific encoding type, try that first. */ - if (pConfig->encodingFormat != ma_encoding_format_unknown) { - #ifdef MA_HAS_WAV - if (pConfig->encodingFormat == ma_encoding_format_wav) { - result = ma_decoder_init_wav__internal(pConfig, pDecoder); - } - #endif - #ifdef MA_HAS_FLAC - if (pConfig->encodingFormat == ma_encoding_format_flac) { - result = ma_decoder_init_flac__internal(pConfig, pDecoder); - } - #endif - #ifdef MA_HAS_MP3 - if (pConfig->encodingFormat == ma_encoding_format_mp3) { - result = ma_decoder_init_mp3__internal(pConfig, pDecoder); - } - #endif - #ifdef MA_HAS_VORBIS - if (pConfig->encodingFormat == ma_encoding_format_vorbis) { - result = ma_decoder_init_vorbis__internal(pConfig, pDecoder); - } - #endif - - /* If we weren't able to initialize the decoder, seek back to the start to give the next attempts a clean start. */ - if (result != MA_SUCCESS) { - onSeek(pDecoder, 0, ma_seek_origin_start); - } - } - - if (result != MA_SUCCESS) { - /* Getting here means we couldn't load a specific decoding backend based on the encoding format. */ - - /* - We use trial and error to open a decoder. We prioritize custom decoders so that if they - implement the same encoding format they take priority over the built-in decoders. - */ - if (result != MA_SUCCESS) { - result = ma_decoder_init_custom__internal(pConfig, pDecoder); - if (result != MA_SUCCESS) { - onSeek(pDecoder, 0, ma_seek_origin_start); - } - } - - /* - If we get to this point and we still haven't found a decoder, and the caller has requested a - specific encoding format, there's no hope for it. Abort. - */ - if (pConfig->encodingFormat != ma_encoding_format_unknown) { - return MA_NO_BACKEND; - } - - #ifdef MA_HAS_WAV - if (result != MA_SUCCESS) { - result = ma_decoder_init_wav__internal(pConfig, pDecoder); - if (result != MA_SUCCESS) { - onSeek(pDecoder, 0, ma_seek_origin_start); - } - } - #endif - #ifdef MA_HAS_FLAC - if (result != MA_SUCCESS) { - result = ma_decoder_init_flac__internal(pConfig, pDecoder); - if (result != MA_SUCCESS) { - onSeek(pDecoder, 0, ma_seek_origin_start); - } - } - #endif - #ifdef MA_HAS_MP3 - if (result != MA_SUCCESS) { - result = ma_decoder_init_mp3__internal(pConfig, pDecoder); - if (result != MA_SUCCESS) { - onSeek(pDecoder, 0, ma_seek_origin_start); - } - } - #endif - #ifdef MA_HAS_VORBIS - if (result != MA_SUCCESS) { - result = ma_decoder_init_vorbis__internal(pConfig, pDecoder); - if (result != MA_SUCCESS) { - onSeek(pDecoder, 0, ma_seek_origin_start); - } - } - #endif - } - - if (result != MA_SUCCESS) { - return result; - } - - return ma_decoder__postinit(pConfig, pDecoder); -} - -MA_API ma_result ma_decoder_init(ma_decoder_read_proc onRead, ma_decoder_seek_proc onSeek, void* pUserData, const ma_decoder_config* pConfig, ma_decoder* pDecoder) -{ - ma_decoder_config config; - ma_result result; - - config = ma_decoder_config_init_copy(pConfig); - - result = ma_decoder__preinit(onRead, onSeek, NULL, pUserData, &config, pDecoder); - if (result != MA_SUCCESS) { - return result; - } - - return ma_decoder_init__internal(onRead, onSeek, pUserData, &config, pDecoder); -} - - -static ma_result ma_decoder__on_read_memory(ma_decoder* pDecoder, void* pBufferOut, size_t bytesToRead, size_t* pBytesRead) -{ - size_t bytesRemaining; - - MA_ASSERT(pDecoder->data.memory.dataSize >= pDecoder->data.memory.currentReadPos); - - if (pBytesRead != NULL) { - *pBytesRead = 0; - } - - bytesRemaining = pDecoder->data.memory.dataSize - pDecoder->data.memory.currentReadPos; - if (bytesToRead > bytesRemaining) { - bytesToRead = bytesRemaining; - } - - if (bytesRemaining == 0) { - return MA_AT_END; - } - - if (bytesToRead > 0) { - MA_COPY_MEMORY(pBufferOut, pDecoder->data.memory.pData + pDecoder->data.memory.currentReadPos, bytesToRead); - pDecoder->data.memory.currentReadPos += bytesToRead; - } - - if (pBytesRead != NULL) { - *pBytesRead = bytesToRead; - } - - return MA_SUCCESS; -} - -static ma_result ma_decoder__on_seek_memory(ma_decoder* pDecoder, ma_int64 byteOffset, ma_seek_origin origin) -{ - if (byteOffset > 0 && (ma_uint64)byteOffset > MA_SIZE_MAX) { - return MA_BAD_SEEK; - } - - if (origin == ma_seek_origin_current) { - if (byteOffset > 0) { - if (pDecoder->data.memory.currentReadPos + byteOffset > pDecoder->data.memory.dataSize) { - byteOffset = (ma_int64)(pDecoder->data.memory.dataSize - pDecoder->data.memory.currentReadPos); /* Trying to seek too far forward. */ - } - - pDecoder->data.memory.currentReadPos += (size_t)byteOffset; - } else { - if (pDecoder->data.memory.currentReadPos < (size_t)-byteOffset) { - byteOffset = -(ma_int64)pDecoder->data.memory.currentReadPos; /* Trying to seek too far backwards. */ - } - - pDecoder->data.memory.currentReadPos -= (size_t)-byteOffset; - } - } else { - if (origin == ma_seek_origin_end) { - if (byteOffset < 0) { - byteOffset = -byteOffset; - } - - if (byteOffset > (ma_int64)pDecoder->data.memory.dataSize) { - pDecoder->data.memory.currentReadPos = 0; /* Trying to seek too far back. */ - } else { - pDecoder->data.memory.currentReadPos = pDecoder->data.memory.dataSize - (size_t)byteOffset; - } - } else { - if ((size_t)byteOffset <= pDecoder->data.memory.dataSize) { - pDecoder->data.memory.currentReadPos = (size_t)byteOffset; - } else { - pDecoder->data.memory.currentReadPos = pDecoder->data.memory.dataSize; /* Trying to seek too far forward. */ - } - } - } - - return MA_SUCCESS; -} - -static ma_result ma_decoder__on_tell_memory(ma_decoder* pDecoder, ma_int64* pCursor) -{ - MA_ASSERT(pDecoder != NULL); - MA_ASSERT(pCursor != NULL); - - *pCursor = (ma_int64)pDecoder->data.memory.currentReadPos; - - return MA_SUCCESS; -} - -static ma_result ma_decoder__preinit_memory_wrapper(const void* pData, size_t dataSize, const ma_decoder_config* pConfig, ma_decoder* pDecoder) -{ - ma_result result = ma_decoder__preinit(ma_decoder__on_read_memory, ma_decoder__on_seek_memory, ma_decoder__on_tell_memory, NULL, pConfig, pDecoder); - if (result != MA_SUCCESS) { - return result; - } - - if (pData == NULL || dataSize == 0) { - return MA_INVALID_ARGS; - } - - pDecoder->data.memory.pData = (const ma_uint8*)pData; - pDecoder->data.memory.dataSize = dataSize; - pDecoder->data.memory.currentReadPos = 0; - - (void)pConfig; - return MA_SUCCESS; -} - -MA_API ma_result ma_decoder_init_memory(const void* pData, size_t dataSize, const ma_decoder_config* pConfig, ma_decoder* pDecoder) -{ - ma_result result; - ma_decoder_config config; - - config = ma_decoder_config_init_copy(pConfig); - - result = ma_decoder__preinit(NULL, NULL, NULL, NULL, &config, pDecoder); - if (result != MA_SUCCESS) { - return result; - } - - if (pData == NULL || dataSize == 0) { - return MA_INVALID_ARGS; - } - - /* If the backend has support for loading from a file path we'll want to use that. If that all fails we'll fall back to the VFS path. */ - result = MA_NO_BACKEND; - - if (config.encodingFormat != ma_encoding_format_unknown) { - #ifdef MA_HAS_WAV - if (config.encodingFormat == ma_encoding_format_wav) { - result = ma_decoder_init_wav_from_memory__internal(pData, dataSize, &config, pDecoder); - } - #endif - #ifdef MA_HAS_FLAC - if (config.encodingFormat == ma_encoding_format_flac) { - result = ma_decoder_init_flac_from_memory__internal(pData, dataSize, &config, pDecoder); - } - #endif - #ifdef MA_HAS_MP3 - if (config.encodingFormat == ma_encoding_format_mp3) { - result = ma_decoder_init_mp3_from_memory__internal(pData, dataSize, &config, pDecoder); - } - #endif - #ifdef MA_HAS_VORBIS - if (config.encodingFormat == ma_encoding_format_vorbis) { - result = ma_decoder_init_vorbis_from_memory__internal(pData, dataSize, &config, pDecoder); - } - #endif - } - - if (result != MA_SUCCESS) { - /* Getting here means we weren't able to initialize a decoder of a specific encoding format. */ - - /* - We use trial and error to open a decoder. We prioritize custom decoders so that if they - implement the same encoding format they take priority over the built-in decoders. - */ - result = ma_decoder_init_custom_from_memory__internal(pData, dataSize, &config, pDecoder); - - /* - If we get to this point and we still haven't found a decoder, and the caller has requested a - specific encoding format, there's no hope for it. Abort. - */ - if (result != MA_SUCCESS && config.encodingFormat != ma_encoding_format_unknown) { - return MA_NO_BACKEND; - } - - /* Use trial and error for stock decoders. */ - if (result != MA_SUCCESS) { - #ifdef MA_HAS_WAV - if (result != MA_SUCCESS) { - result = ma_decoder_init_wav_from_memory__internal(pData, dataSize, &config, pDecoder); - } - #endif - #ifdef MA_HAS_FLAC - if (result != MA_SUCCESS) { - result = ma_decoder_init_flac_from_memory__internal(pData, dataSize, &config, pDecoder); - } - #endif - #ifdef MA_HAS_MP3 - if (result != MA_SUCCESS) { - result = ma_decoder_init_mp3_from_memory__internal(pData, dataSize, &config, pDecoder); - } - #endif - #ifdef MA_HAS_VORBIS - if (result != MA_SUCCESS) { - result = ma_decoder_init_vorbis_from_memory__internal(pData, dataSize, &config, pDecoder); - } - #endif - } - } - - /* - If at this point we still haven't successfully initialized the decoder it most likely means - the backend doesn't have an implementation for loading from a file path. We'll try using - miniaudio's built-in file IO for loading file. - */ - if (result == MA_SUCCESS) { - /* Initialization was successful. Finish up. */ - result = ma_decoder__postinit(&config, pDecoder); - if (result != MA_SUCCESS) { - /* - The backend was initialized successfully, but for some reason post-initialization failed. This is most likely - due to an out of memory error. We're going to abort with an error here and not try to recover. - */ - if (pDecoder->pBackendVTable != NULL && pDecoder->pBackendVTable->onUninit != NULL) { - pDecoder->pBackendVTable->onUninit(pDecoder->pBackendUserData, &pDecoder->pBackend, &pDecoder->allocationCallbacks); - } - - return result; - } - } else { - /* Probably no implementation for loading from a block of memory. Use miniaudio's abstraction instead. */ - result = ma_decoder__preinit_memory_wrapper(pData, dataSize, &config, pDecoder); - if (result != MA_SUCCESS) { - return result; - } - - result = ma_decoder_init__internal(ma_decoder__on_read_memory, ma_decoder__on_seek_memory, NULL, &config, pDecoder); - if (result != MA_SUCCESS) { - return result; - } - } - - return MA_SUCCESS; -} - - -#if defined(MA_HAS_WAV) || \ - defined(MA_HAS_MP3) || \ - defined(MA_HAS_FLAC) || \ - defined(MA_HAS_VORBIS) -#define MA_HAS_PATH_API -#endif - -#if defined(MA_HAS_PATH_API) -static const char* ma_path_file_name(const char* path) -{ - const char* fileName; - - if (path == NULL) { - return NULL; - } - - fileName = path; - - /* We just loop through the path until we find the last slash. */ - while (path[0] != '\0') { - if (path[0] == '/' || path[0] == '\\') { - fileName = path; - } - - path += 1; - } - - /* At this point the file name is sitting on a slash, so just move forward. */ - while (fileName[0] != '\0' && (fileName[0] == '/' || fileName[0] == '\\')) { - fileName += 1; - } - - return fileName; -} - -static const wchar_t* ma_path_file_name_w(const wchar_t* path) -{ - const wchar_t* fileName; - - if (path == NULL) { - return NULL; - } - - fileName = path; - - /* We just loop through the path until we find the last slash. */ - while (path[0] != '\0') { - if (path[0] == '/' || path[0] == '\\') { - fileName = path; - } - - path += 1; - } - - /* At this point the file name is sitting on a slash, so just move forward. */ - while (fileName[0] != '\0' && (fileName[0] == '/' || fileName[0] == '\\')) { - fileName += 1; - } - - return fileName; -} - - -static const char* ma_path_extension(const char* path) -{ - const char* extension; - const char* lastOccurance; - - if (path == NULL) { - path = ""; - } - - extension = ma_path_file_name(path); - lastOccurance = NULL; - - /* Just find the last '.' and return. */ - while (extension[0] != '\0') { - if (extension[0] == '.') { - extension += 1; - lastOccurance = extension; - } - - extension += 1; - } - - return (lastOccurance != NULL) ? lastOccurance : extension; -} - -static const wchar_t* ma_path_extension_w(const wchar_t* path) -{ - const wchar_t* extension; - const wchar_t* lastOccurance; - - if (path == NULL) { - path = L""; - } - - extension = ma_path_file_name_w(path); - lastOccurance = NULL; - - /* Just find the last '.' and return. */ - while (extension[0] != '\0') { - if (extension[0] == '.') { - extension += 1; - lastOccurance = extension; - } - - extension += 1; - } - - return (lastOccurance != NULL) ? lastOccurance : extension; -} - - -static ma_bool32 ma_path_extension_equal(const char* path, const char* extension) -{ - const char* ext1; - const char* ext2; - - if (path == NULL || extension == NULL) { - return MA_FALSE; - } - - ext1 = extension; - ext2 = ma_path_extension(path); - -#if defined(_MSC_VER) || defined(__DMC__) - return _stricmp(ext1, ext2) == 0; -#else - return strcasecmp(ext1, ext2) == 0; -#endif -} - -static ma_bool32 ma_path_extension_equal_w(const wchar_t* path, const wchar_t* extension) -{ - const wchar_t* ext1; - const wchar_t* ext2; - - if (path == NULL || extension == NULL) { - return MA_FALSE; - } - - ext1 = extension; - ext2 = ma_path_extension_w(path); - -#if defined(_MSC_VER) || defined(__WATCOMC__) || defined(__DMC__) - return _wcsicmp(ext1, ext2) == 0; -#else - /* - I'm not aware of a wide character version of strcasecmp(). I'm therefore converting the extensions to multibyte strings and comparing those. This - isn't the most efficient way to do it, but it should work OK. - */ - { - char ext1MB[4096]; - char ext2MB[4096]; - const wchar_t* pext1 = ext1; - const wchar_t* pext2 = ext2; - mbstate_t mbs1; - mbstate_t mbs2; - - MA_ZERO_OBJECT(&mbs1); - MA_ZERO_OBJECT(&mbs2); - - if (wcsrtombs(ext1MB, &pext1, sizeof(ext1MB), &mbs1) == (size_t)-1) { - return MA_FALSE; - } - if (wcsrtombs(ext2MB, &pext2, sizeof(ext2MB), &mbs2) == (size_t)-1) { - return MA_FALSE; - } - - return strcasecmp(ext1MB, ext2MB) == 0; - } -#endif -} -#endif /* MA_HAS_PATH_API */ - - - -static ma_result ma_decoder__on_read_vfs(ma_decoder* pDecoder, void* pBufferOut, size_t bytesToRead, size_t* pBytesRead) -{ - MA_ASSERT(pDecoder != NULL); - MA_ASSERT(pBufferOut != NULL); - - return ma_vfs_or_default_read(pDecoder->data.vfs.pVFS, pDecoder->data.vfs.file, pBufferOut, bytesToRead, pBytesRead); -} - -static ma_result ma_decoder__on_seek_vfs(ma_decoder* pDecoder, ma_int64 offset, ma_seek_origin origin) -{ - MA_ASSERT(pDecoder != NULL); - - return ma_vfs_or_default_seek(pDecoder->data.vfs.pVFS, pDecoder->data.vfs.file, offset, origin); -} - -static ma_result ma_decoder__on_tell_vfs(ma_decoder* pDecoder, ma_int64* pCursor) -{ - MA_ASSERT(pDecoder != NULL); - - return ma_vfs_or_default_tell(pDecoder->data.vfs.pVFS, pDecoder->data.vfs.file, pCursor); -} - -static ma_result ma_decoder__preinit_vfs(ma_vfs* pVFS, const char* pFilePath, const ma_decoder_config* pConfig, ma_decoder* pDecoder) -{ - ma_result result; - ma_vfs_file file; - - result = ma_decoder__preinit(ma_decoder__on_read_vfs, ma_decoder__on_seek_vfs, ma_decoder__on_tell_vfs, NULL, pConfig, pDecoder); - if (result != MA_SUCCESS) { - return result; - } - - if (pFilePath == NULL || pFilePath[0] == '\0') { - return MA_INVALID_ARGS; - } - - result = ma_vfs_or_default_open(pVFS, pFilePath, MA_OPEN_MODE_READ, &file); - if (result != MA_SUCCESS) { - return result; - } - - pDecoder->data.vfs.pVFS = pVFS; - pDecoder->data.vfs.file = file; - - return MA_SUCCESS; -} - -MA_API ma_result ma_decoder_init_vfs(ma_vfs* pVFS, const char* pFilePath, const ma_decoder_config* pConfig, ma_decoder* pDecoder) -{ - ma_result result; - ma_decoder_config config; - - config = ma_decoder_config_init_copy(pConfig); - result = ma_decoder__preinit_vfs(pVFS, pFilePath, &config, pDecoder); - if (result != MA_SUCCESS) { - return result; - } - - result = MA_NO_BACKEND; - - if (config.encodingFormat != ma_encoding_format_unknown) { - #ifdef MA_HAS_WAV - if (config.encodingFormat == ma_encoding_format_wav) { - result = ma_decoder_init_wav__internal(&config, pDecoder); - } - #endif - #ifdef MA_HAS_FLAC - if (config.encodingFormat == ma_encoding_format_flac) { - result = ma_decoder_init_flac__internal(&config, pDecoder); - } - #endif - #ifdef MA_HAS_MP3 - if (config.encodingFormat == ma_encoding_format_mp3) { - result = ma_decoder_init_mp3__internal(&config, pDecoder); - } - #endif - #ifdef MA_HAS_VORBIS - if (config.encodingFormat == ma_encoding_format_vorbis) { - result = ma_decoder_init_vorbis__internal(&config, pDecoder); - } - #endif - - /* Make sure we seek back to the start if we didn't initialize a decoder successfully so the next attempts have a fresh start. */ - if (result != MA_SUCCESS) { - ma_decoder__on_seek_vfs(pDecoder, 0, ma_seek_origin_start); - } - } - - if (result != MA_SUCCESS) { - /* Getting here means we weren't able to initialize a decoder of a specific encoding format. */ - - /* - We use trial and error to open a decoder. We prioritize custom decoders so that if they - implement the same encoding format they take priority over the built-in decoders. - */ - if (result != MA_SUCCESS) { - result = ma_decoder_init_custom__internal(&config, pDecoder); - if (result != MA_SUCCESS) { - ma_decoder__on_seek_vfs(pDecoder, 0, ma_seek_origin_start); - } - } - - /* - If we get to this point and we still haven't found a decoder, and the caller has requested a - specific encoding format, there's no hope for it. Abort. - */ - if (config.encodingFormat != ma_encoding_format_unknown) { - return MA_NO_BACKEND; - } - - #ifdef MA_HAS_WAV - if (result != MA_SUCCESS && ma_path_extension_equal(pFilePath, "wav")) { - result = ma_decoder_init_wav__internal(&config, pDecoder); - if (result != MA_SUCCESS) { - ma_decoder__on_seek_vfs(pDecoder, 0, ma_seek_origin_start); - } - } - #endif - #ifdef MA_HAS_FLAC - if (result != MA_SUCCESS && ma_path_extension_equal(pFilePath, "flac")) { - result = ma_decoder_init_flac__internal(&config, pDecoder); - if (result != MA_SUCCESS) { - ma_decoder__on_seek_vfs(pDecoder, 0, ma_seek_origin_start); - } - } - #endif - #ifdef MA_HAS_MP3 - if (result != MA_SUCCESS && ma_path_extension_equal(pFilePath, "mp3")) { - result = ma_decoder_init_mp3__internal(&config, pDecoder); - if (result != MA_SUCCESS) { - ma_decoder__on_seek_vfs(pDecoder, 0, ma_seek_origin_start); - } - } - #endif - } - - /* If we still haven't got a result just use trial and error. Otherwise we can finish up. */ - if (result != MA_SUCCESS) { - result = ma_decoder_init__internal(ma_decoder__on_read_vfs, ma_decoder__on_seek_vfs, NULL, &config, pDecoder); - } else { - result = ma_decoder__postinit(&config, pDecoder); - } - - if (result != MA_SUCCESS) { - if (pDecoder->data.vfs.file != NULL) { /* <-- Will be reset to NULL if ma_decoder_uninit() is called in one of the steps above which allows us to avoid a double close of the file. */ - ma_vfs_or_default_close(pVFS, pDecoder->data.vfs.file); - } - - return result; - } - - return MA_SUCCESS; -} - - -static ma_result ma_decoder__preinit_vfs_w(ma_vfs* pVFS, const wchar_t* pFilePath, const ma_decoder_config* pConfig, ma_decoder* pDecoder) -{ - ma_result result; - ma_vfs_file file; - - result = ma_decoder__preinit(ma_decoder__on_read_vfs, ma_decoder__on_seek_vfs, ma_decoder__on_tell_vfs, NULL, pConfig, pDecoder); - if (result != MA_SUCCESS) { - return result; - } - - if (pFilePath == NULL || pFilePath[0] == '\0') { - return MA_INVALID_ARGS; - } - - result = ma_vfs_or_default_open_w(pVFS, pFilePath, MA_OPEN_MODE_READ, &file); - if (result != MA_SUCCESS) { - return result; - } - - pDecoder->data.vfs.pVFS = pVFS; - pDecoder->data.vfs.file = file; - - return MA_SUCCESS; -} - -MA_API ma_result ma_decoder_init_vfs_w(ma_vfs* pVFS, const wchar_t* pFilePath, const ma_decoder_config* pConfig, ma_decoder* pDecoder) -{ - ma_result result; - ma_decoder_config config; - - config = ma_decoder_config_init_copy(pConfig); - result = ma_decoder__preinit_vfs_w(pVFS, pFilePath, &config, pDecoder); - if (result != MA_SUCCESS) { - return result; - } - - result = MA_NO_BACKEND; - - if (config.encodingFormat != ma_encoding_format_unknown) { - #ifdef MA_HAS_WAV - if (config.encodingFormat == ma_encoding_format_wav) { - result = ma_decoder_init_wav__internal(&config, pDecoder); - } - #endif - #ifdef MA_HAS_FLAC - if (config.encodingFormat == ma_encoding_format_flac) { - result = ma_decoder_init_flac__internal(&config, pDecoder); - } - #endif - #ifdef MA_HAS_MP3 - if (config.encodingFormat == ma_encoding_format_mp3) { - result = ma_decoder_init_mp3__internal(&config, pDecoder); - } - #endif - #ifdef MA_HAS_VORBIS - if (config.encodingFormat == ma_encoding_format_vorbis) { - result = ma_decoder_init_vorbis__internal(&config, pDecoder); - } - #endif - - /* Make sure we seek back to the start if we didn't initialize a decoder successfully so the next attempts have a fresh start. */ - if (result != MA_SUCCESS) { - ma_decoder__on_seek_vfs(pDecoder, 0, ma_seek_origin_start); - } - } - - if (result != MA_SUCCESS) { - /* Getting here means we weren't able to initialize a decoder of a specific encoding format. */ - - /* - We use trial and error to open a decoder. We prioritize custom decoders so that if they - implement the same encoding format they take priority over the built-in decoders. - */ - if (result != MA_SUCCESS) { - result = ma_decoder_init_custom__internal(&config, pDecoder); - if (result != MA_SUCCESS) { - ma_decoder__on_seek_vfs(pDecoder, 0, ma_seek_origin_start); - } - } - - /* - If we get to this point and we still haven't found a decoder, and the caller has requested a - specific encoding format, there's no hope for it. Abort. - */ - if (config.encodingFormat != ma_encoding_format_unknown) { - return MA_NO_BACKEND; - } - - #ifdef MA_HAS_WAV - if (result != MA_SUCCESS && ma_path_extension_equal_w(pFilePath, L"wav")) { - result = ma_decoder_init_wav__internal(&config, pDecoder); - if (result != MA_SUCCESS) { - ma_decoder__on_seek_vfs(pDecoder, 0, ma_seek_origin_start); - } - } - #endif - #ifdef MA_HAS_FLAC - if (result != MA_SUCCESS && ma_path_extension_equal_w(pFilePath, L"flac")) { - result = ma_decoder_init_flac__internal(&config, pDecoder); - if (result != MA_SUCCESS) { - ma_decoder__on_seek_vfs(pDecoder, 0, ma_seek_origin_start); - } - } - #endif - #ifdef MA_HAS_MP3 - if (result != MA_SUCCESS && ma_path_extension_equal_w(pFilePath, L"mp3")) { - result = ma_decoder_init_mp3__internal(&config, pDecoder); - if (result != MA_SUCCESS) { - ma_decoder__on_seek_vfs(pDecoder, 0, ma_seek_origin_start); - } - } - #endif - } - - /* If we still haven't got a result just use trial and error. Otherwise we can finish up. */ - if (result != MA_SUCCESS) { - result = ma_decoder_init__internal(ma_decoder__on_read_vfs, ma_decoder__on_seek_vfs, NULL, &config, pDecoder); - } else { - result = ma_decoder__postinit(&config, pDecoder); - } - - if (result != MA_SUCCESS) { - ma_vfs_or_default_close(pVFS, pDecoder->data.vfs.file); - return result; - } - - return MA_SUCCESS; -} - - -static ma_result ma_decoder__preinit_file(const char* pFilePath, const ma_decoder_config* pConfig, ma_decoder* pDecoder) -{ - ma_result result; - - result = ma_decoder__preinit(NULL, NULL, NULL, NULL, pConfig, pDecoder); - if (result != MA_SUCCESS) { - return result; - } - - if (pFilePath == NULL || pFilePath[0] == '\0') { - return MA_INVALID_ARGS; - } - - return MA_SUCCESS; -} - -MA_API ma_result ma_decoder_init_file(const char* pFilePath, const ma_decoder_config* pConfig, ma_decoder* pDecoder) -{ - ma_result result; - ma_decoder_config config; - - config = ma_decoder_config_init_copy(pConfig); - result = ma_decoder__preinit_file(pFilePath, &config, pDecoder); - if (result != MA_SUCCESS) { - return result; - } - - /* If the backend has support for loading from a file path we'll want to use that. If that all fails we'll fall back to the VFS path. */ - result = MA_NO_BACKEND; - - if (config.encodingFormat != ma_encoding_format_unknown) { - #ifdef MA_HAS_WAV - if (config.encodingFormat == ma_encoding_format_wav) { - result = ma_decoder_init_wav_from_file__internal(pFilePath, &config, pDecoder); - } - #endif - #ifdef MA_HAS_FLAC - if (config.encodingFormat == ma_encoding_format_flac) { - result = ma_decoder_init_flac_from_file__internal(pFilePath, &config, pDecoder); - } - #endif - #ifdef MA_HAS_MP3 - if (config.encodingFormat == ma_encoding_format_mp3) { - result = ma_decoder_init_mp3_from_file__internal(pFilePath, &config, pDecoder); - } - #endif - #ifdef MA_HAS_VORBIS - if (config.encodingFormat == ma_encoding_format_vorbis) { - result = ma_decoder_init_vorbis_from_file__internal(pFilePath, &config, pDecoder); - } - #endif - } - - if (result != MA_SUCCESS) { - /* Getting here means we weren't able to initialize a decoder of a specific encoding format. */ - - /* - We use trial and error to open a decoder. We prioritize custom decoders so that if they - implement the same encoding format they take priority over the built-in decoders. - */ - result = ma_decoder_init_custom_from_file__internal(pFilePath, &config, pDecoder); - - /* - If we get to this point and we still haven't found a decoder, and the caller has requested a - specific encoding format, there's no hope for it. Abort. - */ - if (result != MA_SUCCESS && config.encodingFormat != ma_encoding_format_unknown) { - return MA_NO_BACKEND; - } - - /* First try loading based on the file extension so we don't waste time opening and closing files. */ - #ifdef MA_HAS_WAV - if (result != MA_SUCCESS && ma_path_extension_equal(pFilePath, "wav")) { - result = ma_decoder_init_wav_from_file__internal(pFilePath, &config, pDecoder); - } - #endif - #ifdef MA_HAS_FLAC - if (result != MA_SUCCESS && ma_path_extension_equal(pFilePath, "flac")) { - result = ma_decoder_init_flac_from_file__internal(pFilePath, &config, pDecoder); - } - #endif - #ifdef MA_HAS_MP3 - if (result != MA_SUCCESS && ma_path_extension_equal(pFilePath, "mp3")) { - result = ma_decoder_init_mp3_from_file__internal(pFilePath, &config, pDecoder); - } - #endif - #ifdef MA_HAS_VORBIS - if (result != MA_SUCCESS && ma_path_extension_equal(pFilePath, "ogg")) { - result = ma_decoder_init_vorbis_from_file__internal(pFilePath, &config, pDecoder); - } - #endif - - /* - If we still haven't got a result just use trial and error. Custom decoders have already been attempted, so here we - need only iterate over our stock decoders. - */ - if (result != MA_SUCCESS) { - #ifdef MA_HAS_WAV - if (result != MA_SUCCESS) { - result = ma_decoder_init_wav_from_file__internal(pFilePath, &config, pDecoder); - } - #endif - #ifdef MA_HAS_FLAC - if (result != MA_SUCCESS) { - result = ma_decoder_init_flac_from_file__internal(pFilePath, &config, pDecoder); - } - #endif - #ifdef MA_HAS_MP3 - if (result != MA_SUCCESS) { - result = ma_decoder_init_mp3_from_file__internal(pFilePath, &config, pDecoder); - } - #endif - #ifdef MA_HAS_VORBIS - if (result != MA_SUCCESS) { - result = ma_decoder_init_vorbis_from_file__internal(pFilePath, &config, pDecoder); - } - #endif - } - } - - /* - If at this point we still haven't successfully initialized the decoder it most likely means - the backend doesn't have an implementation for loading from a file path. We'll try using - miniaudio's built-in file IO for loading file. - */ - if (result == MA_SUCCESS) { - /* Initialization was successful. Finish up. */ - result = ma_decoder__postinit(&config, pDecoder); - if (result != MA_SUCCESS) { - /* - The backend was initialized successfully, but for some reason post-initialization failed. This is most likely - due to an out of memory error. We're going to abort with an error here and not try to recover. - */ - if (pDecoder->pBackendVTable != NULL && pDecoder->pBackendVTable->onUninit != NULL) { - pDecoder->pBackendVTable->onUninit(pDecoder->pBackendUserData, &pDecoder->pBackend, &pDecoder->allocationCallbacks); - } - - return result; - } - } else { - /* Probably no implementation for loading from a file path. Use miniaudio's file IO instead. */ - result = ma_decoder_init_vfs(NULL, pFilePath, pConfig, pDecoder); - if (result != MA_SUCCESS) { - return result; - } - } - - return MA_SUCCESS; -} - -static ma_result ma_decoder__preinit_file_w(const wchar_t* pFilePath, const ma_decoder_config* pConfig, ma_decoder* pDecoder) -{ - ma_result result; - - result = ma_decoder__preinit(NULL, NULL, NULL, NULL, pConfig, pDecoder); - if (result != MA_SUCCESS) { - return result; - } - - if (pFilePath == NULL || pFilePath[0] == '\0') { - return MA_INVALID_ARGS; - } - - return MA_SUCCESS; -} - -MA_API ma_result ma_decoder_init_file_w(const wchar_t* pFilePath, const ma_decoder_config* pConfig, ma_decoder* pDecoder) -{ - ma_result result; - ma_decoder_config config; - - config = ma_decoder_config_init_copy(pConfig); - result = ma_decoder__preinit_file_w(pFilePath, &config, pDecoder); - if (result != MA_SUCCESS) { - return result; - } - - /* If the backend has support for loading from a file path we'll want to use that. If that all fails we'll fall back to the VFS path. */ - result = MA_NO_BACKEND; - - if (config.encodingFormat != ma_encoding_format_unknown) { - #ifdef MA_HAS_WAV - if (config.encodingFormat == ma_encoding_format_wav) { - result = ma_decoder_init_wav_from_file_w__internal(pFilePath, &config, pDecoder); - } - #endif - #ifdef MA_HAS_FLAC - if (config.encodingFormat == ma_encoding_format_flac) { - result = ma_decoder_init_flac_from_file_w__internal(pFilePath, &config, pDecoder); - } - #endif - #ifdef MA_HAS_MP3 - if (config.encodingFormat == ma_encoding_format_mp3) { - result = ma_decoder_init_mp3_from_file_w__internal(pFilePath, &config, pDecoder); - } - #endif - #ifdef MA_HAS_VORBIS - if (config.encodingFormat == ma_encoding_format_vorbis) { - result = ma_decoder_init_vorbis_from_file_w__internal(pFilePath, &config, pDecoder); - } - #endif - } - - if (result != MA_SUCCESS) { - /* Getting here means we weren't able to initialize a decoder of a specific encoding format. */ - - /* - We use trial and error to open a decoder. We prioritize custom decoders so that if they - implement the same encoding format they take priority over the built-in decoders. - */ - result = ma_decoder_init_custom_from_file_w__internal(pFilePath, &config, pDecoder); - - /* - If we get to this point and we still haven't found a decoder, and the caller has requested a - specific encoding format, there's no hope for it. Abort. - */ - if (result != MA_SUCCESS && config.encodingFormat != ma_encoding_format_unknown) { - return MA_NO_BACKEND; - } - - /* First try loading based on the file extension so we don't waste time opening and closing files. */ - #ifdef MA_HAS_WAV - if (result != MA_SUCCESS && ma_path_extension_equal_w(pFilePath, L"wav")) { - result = ma_decoder_init_wav_from_file_w__internal(pFilePath, &config, pDecoder); - } - #endif - #ifdef MA_HAS_FLAC - if (result != MA_SUCCESS && ma_path_extension_equal_w(pFilePath, L"flac")) { - result = ma_decoder_init_flac_from_file_w__internal(pFilePath, &config, pDecoder); - } - #endif - #ifdef MA_HAS_MP3 - if (result != MA_SUCCESS && ma_path_extension_equal_w(pFilePath, L"mp3")) { - result = ma_decoder_init_mp3_from_file_w__internal(pFilePath, &config, pDecoder); - } - #endif - #ifdef MA_HAS_VORBIS - if (result != MA_SUCCESS && ma_path_extension_equal_w(pFilePath, L"ogg")) { - result = ma_decoder_init_vorbis_from_file_w__internal(pFilePath, &config, pDecoder); - } - #endif - - /* - If we still haven't got a result just use trial and error. Custom decoders have already been attempted, so here we - need only iterate over our stock decoders. - */ - if (result != MA_SUCCESS) { - #ifdef MA_HAS_WAV - if (result != MA_SUCCESS) { - result = ma_decoder_init_wav_from_file_w__internal(pFilePath, &config, pDecoder); - } - #endif - #ifdef MA_HAS_FLAC - if (result != MA_SUCCESS) { - result = ma_decoder_init_flac_from_file_w__internal(pFilePath, &config, pDecoder); - } - #endif - #ifdef MA_HAS_MP3 - if (result != MA_SUCCESS) { - result = ma_decoder_init_mp3_from_file_w__internal(pFilePath, &config, pDecoder); - } - #endif - #ifdef MA_HAS_VORBIS - if (result != MA_SUCCESS) { - result = ma_decoder_init_vorbis_from_file_w__internal(pFilePath, &config, pDecoder); - } - #endif - } - } - - /* - If at this point we still haven't successfully initialized the decoder it most likely means - the backend doesn't have an implementation for loading from a file path. We'll try using - miniaudio's built-in file IO for loading file. - */ - if (result == MA_SUCCESS) { - /* Initialization was successful. Finish up. */ - result = ma_decoder__postinit(&config, pDecoder); - if (result != MA_SUCCESS) { - /* - The backend was initialized successfully, but for some reason post-initialization failed. This is most likely - due to an out of memory error. We're going to abort with an error here and not try to recover. - */ - if (pDecoder->pBackendVTable != NULL && pDecoder->pBackendVTable->onUninit != NULL) { - pDecoder->pBackendVTable->onUninit(pDecoder->pBackendUserData, &pDecoder->pBackend, &pDecoder->allocationCallbacks); - } - - return result; - } - } else { - /* Probably no implementation for loading from a file path. Use miniaudio's file IO instead. */ - result = ma_decoder_init_vfs_w(NULL, pFilePath, pConfig, pDecoder); - if (result != MA_SUCCESS) { - return result; - } - } - - return MA_SUCCESS; -} - -MA_API ma_result ma_decoder_uninit(ma_decoder* pDecoder) -{ - if (pDecoder == NULL) { - return MA_INVALID_ARGS; - } - - if (pDecoder->pBackend != NULL) { - if (pDecoder->pBackendVTable != NULL && pDecoder->pBackendVTable->onUninit != NULL) { - pDecoder->pBackendVTable->onUninit(pDecoder->pBackendUserData, pDecoder->pBackend, &pDecoder->allocationCallbacks); - } - } - - if (pDecoder->onRead == ma_decoder__on_read_vfs) { - ma_vfs_or_default_close(pDecoder->data.vfs.pVFS, pDecoder->data.vfs.file); - pDecoder->data.vfs.file = NULL; - } - - ma_data_converter_uninit(&pDecoder->converter, &pDecoder->allocationCallbacks); - ma_data_source_uninit(&pDecoder->ds); - - if (pDecoder->pInputCache != NULL) { - ma_free(pDecoder->pInputCache, &pDecoder->allocationCallbacks); - } - - return MA_SUCCESS; -} - -MA_API ma_result ma_decoder_read_pcm_frames(ma_decoder* pDecoder, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead) -{ - ma_result result = MA_SUCCESS; - ma_uint64 totalFramesReadOut; - void* pRunningFramesOut; - - if (pFramesRead != NULL) { - *pFramesRead = 0; /* Safety. */ - } - - if (frameCount == 0) { - return MA_INVALID_ARGS; - } - - if (pDecoder == NULL) { - return MA_INVALID_ARGS; - } - - if (pDecoder->pBackend == NULL) { - return MA_INVALID_OPERATION; - } - - /* Fast path. */ - if (pDecoder->converter.isPassthrough) { - result = ma_data_source_read_pcm_frames(pDecoder->pBackend, pFramesOut, frameCount, &totalFramesReadOut); - } else { - /* - Getting here means we need to do data conversion. If we're seeking forward and are _not_ doing resampling we can run this in a fast path. If we're doing resampling we - need to run through each sample because we need to ensure its internal cache is updated. - */ - if (pFramesOut == NULL && pDecoder->converter.hasResampler == MA_FALSE) { - result = ma_data_source_read_pcm_frames(pDecoder->pBackend, NULL, frameCount, &totalFramesReadOut); - } else { - /* Slow path. Need to run everything through the data converter. */ - ma_format internalFormat; - ma_uint32 internalChannels; - - totalFramesReadOut = 0; - pRunningFramesOut = pFramesOut; - - result = ma_data_source_get_data_format(pDecoder->pBackend, &internalFormat, &internalChannels, NULL, NULL, 0); - if (result != MA_SUCCESS) { - return result; /* Failed to retrieve the internal format and channel count. */ - } - - /* - We run a different path depending on whether or not we are using a heap-allocated - intermediary buffer or not. If the data converter does not support the calculation of - the required number of input frames, we'll use the heap-allocated path. Otherwise we'll - use the stack-allocated path. - */ - if (pDecoder->pInputCache != NULL) { - /* We don't have a way of determining the required number of input frames, so need to persistently store input data in a cache. */ - while (totalFramesReadOut < frameCount) { - ma_uint64 framesToReadThisIterationIn; - ma_uint64 framesToReadThisIterationOut; - - /* If there's any data available in the cache, that needs to get processed first. */ - if (pDecoder->inputCacheRemaining > 0) { - framesToReadThisIterationOut = (frameCount - totalFramesReadOut); - framesToReadThisIterationIn = framesToReadThisIterationOut; - if (framesToReadThisIterationIn > pDecoder->inputCacheRemaining) { - framesToReadThisIterationIn = pDecoder->inputCacheRemaining; - } - - result = ma_data_converter_process_pcm_frames(&pDecoder->converter, ma_offset_pcm_frames_ptr(pDecoder->pInputCache, pDecoder->inputCacheConsumed, internalFormat, internalChannels), &framesToReadThisIterationIn, pRunningFramesOut, &framesToReadThisIterationOut); - if (result != MA_SUCCESS) { - break; - } - - pDecoder->inputCacheConsumed += framesToReadThisIterationIn; - pDecoder->inputCacheRemaining -= framesToReadThisIterationIn; - - totalFramesReadOut += framesToReadThisIterationOut; - - if (pRunningFramesOut != NULL) { - pRunningFramesOut = ma_offset_ptr(pRunningFramesOut, framesToReadThisIterationOut * ma_get_bytes_per_frame(pDecoder->outputFormat, pDecoder->outputChannels)); - } - - if (framesToReadThisIterationIn == 0 && framesToReadThisIterationOut == 0) { - break; /* We're done. */ - } - } - - /* Getting here means there's no data in the cache and we need to fill it up from the data source. */ - if (pDecoder->inputCacheRemaining == 0) { - pDecoder->inputCacheConsumed = 0; - - result = ma_data_source_read_pcm_frames(pDecoder->pBackend, pDecoder->pInputCache, pDecoder->inputCacheCap, &pDecoder->inputCacheRemaining); - if (result != MA_SUCCESS) { - break; - } - } - } - } else { - /* We have a way of determining the required number of input frames so just use the stack. */ - while (totalFramesReadOut < frameCount) { - ma_uint8 pIntermediaryBuffer[MA_DATA_CONVERTER_STACK_BUFFER_SIZE]; /* In internal format. */ - ma_uint64 intermediaryBufferCap = sizeof(pIntermediaryBuffer) / ma_get_bytes_per_frame(internalFormat, internalChannels); - ma_uint64 framesToReadThisIterationIn; - ma_uint64 framesReadThisIterationIn; - ma_uint64 framesToReadThisIterationOut; - ma_uint64 framesReadThisIterationOut; - ma_uint64 requiredInputFrameCount; - - framesToReadThisIterationOut = (frameCount - totalFramesReadOut); - framesToReadThisIterationIn = framesToReadThisIterationOut; - if (framesToReadThisIterationIn > intermediaryBufferCap) { - framesToReadThisIterationIn = intermediaryBufferCap; - } - - ma_data_converter_get_required_input_frame_count(&pDecoder->converter, framesToReadThisIterationOut, &requiredInputFrameCount); - if (framesToReadThisIterationIn > requiredInputFrameCount) { - framesToReadThisIterationIn = requiredInputFrameCount; - } - - if (requiredInputFrameCount > 0) { - result = ma_data_source_read_pcm_frames(pDecoder->pBackend, pIntermediaryBuffer, framesToReadThisIterationIn, &framesReadThisIterationIn); - - /* - Note here that even if we've reached the end, we don't want to abort because there might be more output frames needing to be - generated from cached input data, which might happen if resampling is being performed. - */ - if (result != MA_SUCCESS && result != MA_AT_END) { - break; - } - } else { - framesReadThisIterationIn = 0; - pIntermediaryBuffer[0] = 0; /* <-- This is just to silence a static analysis warning. */ - } - - /* - At this point we have our decoded data in input format and now we need to convert to output format. Note that even if we didn't read any - input frames, we still want to try processing frames because there may some output frames generated from cached input data. - */ - framesReadThisIterationOut = framesToReadThisIterationOut; - result = ma_data_converter_process_pcm_frames(&pDecoder->converter, pIntermediaryBuffer, &framesReadThisIterationIn, pRunningFramesOut, &framesReadThisIterationOut); - if (result != MA_SUCCESS) { - break; - } - - totalFramesReadOut += framesReadThisIterationOut; - - if (pRunningFramesOut != NULL) { - pRunningFramesOut = ma_offset_ptr(pRunningFramesOut, framesReadThisIterationOut * ma_get_bytes_per_frame(pDecoder->outputFormat, pDecoder->outputChannels)); - } - - if (framesReadThisIterationIn == 0 && framesReadThisIterationOut == 0) { - break; /* We're done. */ - } - } - } - } - } - - pDecoder->readPointerInPCMFrames += totalFramesReadOut; - - if (pFramesRead != NULL) { - *pFramesRead = totalFramesReadOut; - } - - if (result == MA_SUCCESS && totalFramesReadOut == 0) { - result = MA_AT_END; - } - - return result; -} - -MA_API ma_result ma_decoder_seek_to_pcm_frame(ma_decoder* pDecoder, ma_uint64 frameIndex) -{ - if (pDecoder == NULL) { - return MA_INVALID_ARGS; - } - - if (pDecoder->pBackend != NULL) { - ma_result result; - ma_uint64 internalFrameIndex; - ma_uint32 internalSampleRate; - ma_uint64 currentFrameIndex; - - result = ma_data_source_get_data_format(pDecoder->pBackend, NULL, NULL, &internalSampleRate, NULL, 0); - if (result != MA_SUCCESS) { - return result; /* Failed to retrieve the internal sample rate. */ - } - - if (internalSampleRate == pDecoder->outputSampleRate) { - internalFrameIndex = frameIndex; - } else { - internalFrameIndex = ma_calculate_frame_count_after_resampling(internalSampleRate, pDecoder->outputSampleRate, frameIndex); - } - - /* Only seek if we're requesting a different frame to what we're currently sitting on. */ - ma_data_source_get_cursor_in_pcm_frames(pDecoder->pBackend, ¤tFrameIndex); - if (currentFrameIndex != internalFrameIndex) { - result = ma_data_source_seek_to_pcm_frame(pDecoder->pBackend, internalFrameIndex); - if (result == MA_SUCCESS) { - pDecoder->readPointerInPCMFrames = frameIndex; - } - - /* Reset the data converter so that any cached data in the resampler is cleared. */ - ma_data_converter_reset(&pDecoder->converter); - } - - return result; - } - - /* Should never get here, but if we do it means onSeekToPCMFrame was not set by the backend. */ - return MA_INVALID_ARGS; -} - -MA_API ma_result ma_decoder_get_data_format(ma_decoder* pDecoder, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate, ma_channel* pChannelMap, size_t channelMapCap) -{ - if (pDecoder == NULL) { - return MA_INVALID_ARGS; - } - - if (pFormat != NULL) { - *pFormat = pDecoder->outputFormat; - } - - if (pChannels != NULL) { - *pChannels = pDecoder->outputChannels; - } - - if (pSampleRate != NULL) { - *pSampleRate = pDecoder->outputSampleRate; - } - - if (pChannelMap != NULL) { - ma_data_converter_get_output_channel_map(&pDecoder->converter, pChannelMap, channelMapCap); - } - - return MA_SUCCESS; -} - -MA_API ma_result ma_decoder_get_cursor_in_pcm_frames(ma_decoder* pDecoder, ma_uint64* pCursor) -{ - if (pCursor == NULL) { - return MA_INVALID_ARGS; - } - - *pCursor = 0; - - if (pDecoder == NULL) { - return MA_INVALID_ARGS; - } - - *pCursor = pDecoder->readPointerInPCMFrames; - - return MA_SUCCESS; -} - -MA_API ma_result ma_decoder_get_length_in_pcm_frames(ma_decoder* pDecoder, ma_uint64* pLength) -{ - if (pLength == NULL) { - return MA_INVALID_ARGS; - } - - *pLength = 0; - - if (pDecoder == NULL) { - return MA_INVALID_ARGS; - } - - if (pDecoder->pBackend != NULL) { - ma_result result; - ma_uint64 internalLengthInPCMFrames; - ma_uint32 internalSampleRate; - - result = ma_data_source_get_length_in_pcm_frames(pDecoder->pBackend, &internalLengthInPCMFrames); - if (result != MA_SUCCESS) { - return result; /* Failed to retrieve the internal length. */ - } - - result = ma_data_source_get_data_format(pDecoder->pBackend, NULL, NULL, &internalSampleRate, NULL, 0); - if (result != MA_SUCCESS) { - return result; /* Failed to retrieve the internal sample rate. */ - } - - if (internalSampleRate == pDecoder->outputSampleRate) { - *pLength = internalLengthInPCMFrames; - } else { - *pLength = ma_calculate_frame_count_after_resampling(pDecoder->outputSampleRate, internalSampleRate, internalLengthInPCMFrames); - } - - return MA_SUCCESS; - } else { - return MA_NO_BACKEND; - } -} - -MA_API ma_result ma_decoder_get_available_frames(ma_decoder* pDecoder, ma_uint64* pAvailableFrames) -{ - ma_result result; - ma_uint64 totalFrameCount; - - if (pAvailableFrames == NULL) { - return MA_INVALID_ARGS; - } - - *pAvailableFrames = 0; - - if (pDecoder == NULL) { - return MA_INVALID_ARGS; - } - - result = ma_decoder_get_length_in_pcm_frames(pDecoder, &totalFrameCount); - if (result != MA_SUCCESS) { - return result; - } - - if (totalFrameCount <= pDecoder->readPointerInPCMFrames) { - *pAvailableFrames = 0; - } else { - *pAvailableFrames = totalFrameCount - pDecoder->readPointerInPCMFrames; - } - - return MA_SUCCESS; -} - - -static ma_result ma_decoder__full_decode_and_uninit(ma_decoder* pDecoder, ma_decoder_config* pConfigOut, ma_uint64* pFrameCountOut, void** ppPCMFramesOut) -{ - ma_result result; - ma_uint64 totalFrameCount; - ma_uint64 bpf; - ma_uint64 dataCapInFrames; - void* pPCMFramesOut; - - MA_ASSERT(pDecoder != NULL); - - totalFrameCount = 0; - bpf = ma_get_bytes_per_frame(pDecoder->outputFormat, pDecoder->outputChannels); - - /* The frame count is unknown until we try reading. Thus, we just run in a loop. */ - dataCapInFrames = 0; - pPCMFramesOut = NULL; - for (;;) { - ma_uint64 frameCountToTryReading; - ma_uint64 framesJustRead; - - /* Make room if there's not enough. */ - if (totalFrameCount == dataCapInFrames) { - void* pNewPCMFramesOut; - ma_uint64 newDataCapInFrames = dataCapInFrames*2; - if (newDataCapInFrames == 0) { - newDataCapInFrames = 4096; - } - - if ((newDataCapInFrames * bpf) > MA_SIZE_MAX) { - ma_free(pPCMFramesOut, &pDecoder->allocationCallbacks); - return MA_TOO_BIG; - } - - pNewPCMFramesOut = (void*)ma_realloc(pPCMFramesOut, (size_t)(newDataCapInFrames * bpf), &pDecoder->allocationCallbacks); - if (pNewPCMFramesOut == NULL) { - ma_free(pPCMFramesOut, &pDecoder->allocationCallbacks); - return MA_OUT_OF_MEMORY; - } - - dataCapInFrames = newDataCapInFrames; - pPCMFramesOut = pNewPCMFramesOut; - } - - frameCountToTryReading = dataCapInFrames - totalFrameCount; - MA_ASSERT(frameCountToTryReading > 0); - - result = ma_decoder_read_pcm_frames(pDecoder, (ma_uint8*)pPCMFramesOut + (totalFrameCount * bpf), frameCountToTryReading, &framesJustRead); - totalFrameCount += framesJustRead; - - if (result != MA_SUCCESS) { - break; - } - - if (framesJustRead < frameCountToTryReading) { - break; - } - } - - - if (pConfigOut != NULL) { - pConfigOut->format = pDecoder->outputFormat; - pConfigOut->channels = pDecoder->outputChannels; - pConfigOut->sampleRate = pDecoder->outputSampleRate; - } - - if (ppPCMFramesOut != NULL) { - *ppPCMFramesOut = pPCMFramesOut; - } else { - ma_free(pPCMFramesOut, &pDecoder->allocationCallbacks); - } - - if (pFrameCountOut != NULL) { - *pFrameCountOut = totalFrameCount; - } - - ma_decoder_uninit(pDecoder); - return MA_SUCCESS; -} - -MA_API ma_result ma_decode_from_vfs(ma_vfs* pVFS, const char* pFilePath, ma_decoder_config* pConfig, ma_uint64* pFrameCountOut, void** ppPCMFramesOut) -{ - ma_result result; - ma_decoder_config config; - ma_decoder decoder; - - if (pFrameCountOut != NULL) { - *pFrameCountOut = 0; - } - if (ppPCMFramesOut != NULL) { - *ppPCMFramesOut = NULL; - } - - config = ma_decoder_config_init_copy(pConfig); - - result = ma_decoder_init_vfs(pVFS, pFilePath, &config, &decoder); - if (result != MA_SUCCESS) { - return result; - } - - result = ma_decoder__full_decode_and_uninit(&decoder, pConfig, pFrameCountOut, ppPCMFramesOut); - - return result; -} - -MA_API ma_result ma_decode_file(const char* pFilePath, ma_decoder_config* pConfig, ma_uint64* pFrameCountOut, void** ppPCMFramesOut) -{ - return ma_decode_from_vfs(NULL, pFilePath, pConfig, pFrameCountOut, ppPCMFramesOut); -} - -MA_API ma_result ma_decode_memory(const void* pData, size_t dataSize, ma_decoder_config* pConfig, ma_uint64* pFrameCountOut, void** ppPCMFramesOut) -{ - ma_decoder_config config; - ma_decoder decoder; - ma_result result; - - if (pFrameCountOut != NULL) { - *pFrameCountOut = 0; - } - if (ppPCMFramesOut != NULL) { - *ppPCMFramesOut = NULL; - } - - if (pData == NULL || dataSize == 0) { - return MA_INVALID_ARGS; - } - - config = ma_decoder_config_init_copy(pConfig); - - result = ma_decoder_init_memory(pData, dataSize, &config, &decoder); - if (result != MA_SUCCESS) { - return result; - } - - return ma_decoder__full_decode_and_uninit(&decoder, pConfig, pFrameCountOut, ppPCMFramesOut); -} -#endif /* MA_NO_DECODING */ - - -#ifndef MA_NO_ENCODING - -#if defined(MA_HAS_WAV) -static size_t ma_encoder__internal_on_write_wav(void* pUserData, const void* pData, size_t bytesToWrite) -{ - ma_encoder* pEncoder = (ma_encoder*)pUserData; - size_t bytesWritten = 0; - - MA_ASSERT(pEncoder != NULL); - - pEncoder->onWrite(pEncoder, pData, bytesToWrite, &bytesWritten); - return bytesWritten; -} - -static ma_bool32 ma_encoder__internal_on_seek_wav(void* pUserData, int offset, ma_dr_wav_seek_origin origin) -{ - ma_encoder* pEncoder = (ma_encoder*)pUserData; - ma_result result; - - MA_ASSERT(pEncoder != NULL); - - result = pEncoder->onSeek(pEncoder, offset, (origin == ma_dr_wav_seek_origin_start) ? ma_seek_origin_start : ma_seek_origin_current); - if (result != MA_SUCCESS) { - return MA_FALSE; - } else { - return MA_TRUE; - } -} - -static ma_result ma_encoder__on_init_wav(ma_encoder* pEncoder) -{ - ma_dr_wav_data_format wavFormat; - ma_allocation_callbacks allocationCallbacks; - ma_dr_wav* pWav; - - MA_ASSERT(pEncoder != NULL); - - pWav = (ma_dr_wav*)ma_malloc(sizeof(*pWav), &pEncoder->config.allocationCallbacks); - if (pWav == NULL) { - return MA_OUT_OF_MEMORY; - } - - wavFormat.container = ma_dr_wav_container_riff; - wavFormat.channels = pEncoder->config.channels; - wavFormat.sampleRate = pEncoder->config.sampleRate; - wavFormat.bitsPerSample = ma_get_bytes_per_sample(pEncoder->config.format) * 8; - if (pEncoder->config.format == ma_format_f32) { - wavFormat.format = MA_DR_WAVE_FORMAT_IEEE_FLOAT; - } else { - wavFormat.format = MA_DR_WAVE_FORMAT_PCM; - } - - allocationCallbacks.pUserData = pEncoder->config.allocationCallbacks.pUserData; - allocationCallbacks.onMalloc = pEncoder->config.allocationCallbacks.onMalloc; - allocationCallbacks.onRealloc = pEncoder->config.allocationCallbacks.onRealloc; - allocationCallbacks.onFree = pEncoder->config.allocationCallbacks.onFree; - - if (!ma_dr_wav_init_write(pWav, &wavFormat, ma_encoder__internal_on_write_wav, ma_encoder__internal_on_seek_wav, pEncoder, &allocationCallbacks)) { - return MA_ERROR; - } - - pEncoder->pInternalEncoder = pWav; - - return MA_SUCCESS; -} - -static void ma_encoder__on_uninit_wav(ma_encoder* pEncoder) -{ - ma_dr_wav* pWav; - - MA_ASSERT(pEncoder != NULL); - - pWav = (ma_dr_wav*)pEncoder->pInternalEncoder; - MA_ASSERT(pWav != NULL); - - ma_dr_wav_uninit(pWav); - ma_free(pWav, &pEncoder->config.allocationCallbacks); -} - -static ma_result ma_encoder__on_write_pcm_frames_wav(ma_encoder* pEncoder, const void* pFramesIn, ma_uint64 frameCount, ma_uint64* pFramesWritten) -{ - ma_dr_wav* pWav; - ma_uint64 framesWritten; - - MA_ASSERT(pEncoder != NULL); - - pWav = (ma_dr_wav*)pEncoder->pInternalEncoder; - MA_ASSERT(pWav != NULL); - - framesWritten = ma_dr_wav_write_pcm_frames(pWav, frameCount, pFramesIn); - - if (pFramesWritten != NULL) { - *pFramesWritten = framesWritten; - } - - return MA_SUCCESS; -} -#endif - -MA_API ma_encoder_config ma_encoder_config_init(ma_encoding_format encodingFormat, ma_format format, ma_uint32 channels, ma_uint32 sampleRate) -{ - ma_encoder_config config; - - MA_ZERO_OBJECT(&config); - config.encodingFormat = encodingFormat; - config.format = format; - config.channels = channels; - config.sampleRate = sampleRate; - - return config; -} - -MA_API ma_result ma_encoder_preinit(const ma_encoder_config* pConfig, ma_encoder* pEncoder) -{ - ma_result result; - - if (pEncoder == NULL) { - return MA_INVALID_ARGS; - } - - MA_ZERO_OBJECT(pEncoder); - - if (pConfig == NULL) { - return MA_INVALID_ARGS; - } - - if (pConfig->format == ma_format_unknown || pConfig->channels == 0 || pConfig->sampleRate == 0) { - return MA_INVALID_ARGS; - } - - pEncoder->config = *pConfig; - - result = ma_allocation_callbacks_init_copy(&pEncoder->config.allocationCallbacks, &pConfig->allocationCallbacks); - if (result != MA_SUCCESS) { - return result; - } - - return MA_SUCCESS; -} - -MA_API ma_result ma_encoder_init__internal(ma_encoder_write_proc onWrite, ma_encoder_seek_proc onSeek, void* pUserData, ma_encoder* pEncoder) -{ - ma_result result = MA_SUCCESS; - - /* This assumes ma_encoder_preinit() has been called prior. */ - MA_ASSERT(pEncoder != NULL); - - if (onWrite == NULL || onSeek == NULL) { - return MA_INVALID_ARGS; - } - - pEncoder->onWrite = onWrite; - pEncoder->onSeek = onSeek; - pEncoder->pUserData = pUserData; - - switch (pEncoder->config.encodingFormat) - { - case ma_encoding_format_wav: - { - #if defined(MA_HAS_WAV) - pEncoder->onInit = ma_encoder__on_init_wav; - pEncoder->onUninit = ma_encoder__on_uninit_wav; - pEncoder->onWritePCMFrames = ma_encoder__on_write_pcm_frames_wav; - #else - result = MA_NO_BACKEND; - #endif - } break; - - default: - { - result = MA_INVALID_ARGS; - } break; - } - - /* Getting here means we should have our backend callbacks set up. */ - if (result == MA_SUCCESS) { - result = pEncoder->onInit(pEncoder); - } - - return result; -} - -static ma_result ma_encoder__on_write_vfs(ma_encoder* pEncoder, const void* pBufferIn, size_t bytesToWrite, size_t* pBytesWritten) -{ - return ma_vfs_or_default_write(pEncoder->data.vfs.pVFS, pEncoder->data.vfs.file, pBufferIn, bytesToWrite, pBytesWritten); -} - -static ma_result ma_encoder__on_seek_vfs(ma_encoder* pEncoder, ma_int64 offset, ma_seek_origin origin) -{ - return ma_vfs_or_default_seek(pEncoder->data.vfs.pVFS, pEncoder->data.vfs.file, offset, origin); -} - -MA_API ma_result ma_encoder_init_vfs(ma_vfs* pVFS, const char* pFilePath, const ma_encoder_config* pConfig, ma_encoder* pEncoder) -{ - ma_result result; - ma_vfs_file file; - - result = ma_encoder_preinit(pConfig, pEncoder); - if (result != MA_SUCCESS) { - return result; - } - - /* Now open the file. If this fails we don't need to uninitialize the encoder. */ - result = ma_vfs_or_default_open(pVFS, pFilePath, MA_OPEN_MODE_WRITE, &file); - if (result != MA_SUCCESS) { - return result; - } - - pEncoder->data.vfs.pVFS = pVFS; - pEncoder->data.vfs.file = file; - - result = ma_encoder_init__internal(ma_encoder__on_write_vfs, ma_encoder__on_seek_vfs, NULL, pEncoder); - if (result != MA_SUCCESS) { - ma_vfs_or_default_close(pVFS, file); - return result; - } - - return MA_SUCCESS; -} - -MA_API ma_result ma_encoder_init_vfs_w(ma_vfs* pVFS, const wchar_t* pFilePath, const ma_encoder_config* pConfig, ma_encoder* pEncoder) -{ - ma_result result; - ma_vfs_file file; - - result = ma_encoder_preinit(pConfig, pEncoder); - if (result != MA_SUCCESS) { - return result; - } - - /* Now open the file. If this fails we don't need to uninitialize the encoder. */ - result = ma_vfs_or_default_open_w(pVFS, pFilePath, MA_OPEN_MODE_WRITE, &file); - if (result != MA_SUCCESS) { - return result; - } - - pEncoder->data.vfs.pVFS = pVFS; - pEncoder->data.vfs.file = file; - - result = ma_encoder_init__internal(ma_encoder__on_write_vfs, ma_encoder__on_seek_vfs, NULL, pEncoder); - if (result != MA_SUCCESS) { - ma_vfs_or_default_close(pVFS, file); - return result; - } - - return MA_SUCCESS; -} - -MA_API ma_result ma_encoder_init_file(const char* pFilePath, const ma_encoder_config* pConfig, ma_encoder* pEncoder) -{ - return ma_encoder_init_vfs(NULL, pFilePath, pConfig, pEncoder); -} - -MA_API ma_result ma_encoder_init_file_w(const wchar_t* pFilePath, const ma_encoder_config* pConfig, ma_encoder* pEncoder) -{ - return ma_encoder_init_vfs_w(NULL, pFilePath, pConfig, pEncoder); -} - -MA_API ma_result ma_encoder_init(ma_encoder_write_proc onWrite, ma_encoder_seek_proc onSeek, void* pUserData, const ma_encoder_config* pConfig, ma_encoder* pEncoder) -{ - ma_result result; - - result = ma_encoder_preinit(pConfig, pEncoder); - if (result != MA_SUCCESS) { - return result; - } - - return ma_encoder_init__internal(onWrite, onSeek, pUserData, pEncoder); -} - - -MA_API void ma_encoder_uninit(ma_encoder* pEncoder) -{ - if (pEncoder == NULL) { - return; - } - - if (pEncoder->onUninit) { - pEncoder->onUninit(pEncoder); - } - - /* If we have a file handle, close it. */ - if (pEncoder->onWrite == ma_encoder__on_write_vfs) { - ma_vfs_or_default_close(pEncoder->data.vfs.pVFS, pEncoder->data.vfs.file); - pEncoder->data.vfs.file = NULL; - } -} - - -MA_API ma_result ma_encoder_write_pcm_frames(ma_encoder* pEncoder, const void* pFramesIn, ma_uint64 frameCount, ma_uint64* pFramesWritten) -{ - if (pFramesWritten != NULL) { - *pFramesWritten = 0; - } - - if (pEncoder == NULL || pFramesIn == NULL) { - return MA_INVALID_ARGS; - } - - return pEncoder->onWritePCMFrames(pEncoder, pFramesIn, frameCount, pFramesWritten); -} -#endif /* MA_NO_ENCODING */ - - - -/************************************************************************************************************************************************************** - -Generation - -**************************************************************************************************************************************************************/ -#ifndef MA_NO_GENERATION -MA_API ma_waveform_config ma_waveform_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRate, ma_waveform_type type, double amplitude, double frequency) -{ - ma_waveform_config config; - - MA_ZERO_OBJECT(&config); - config.format = format; - config.channels = channels; - config.sampleRate = sampleRate; - config.type = type; - config.amplitude = amplitude; - config.frequency = frequency; - - return config; -} - -static ma_result ma_waveform__data_source_on_read(ma_data_source* pDataSource, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead) -{ - return ma_waveform_read_pcm_frames((ma_waveform*)pDataSource, pFramesOut, frameCount, pFramesRead); -} - -static ma_result ma_waveform__data_source_on_seek(ma_data_source* pDataSource, ma_uint64 frameIndex) -{ - return ma_waveform_seek_to_pcm_frame((ma_waveform*)pDataSource, frameIndex); -} - -static ma_result ma_waveform__data_source_on_get_data_format(ma_data_source* pDataSource, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate, ma_channel* pChannelMap, size_t channelMapCap) -{ - ma_waveform* pWaveform = (ma_waveform*)pDataSource; - - *pFormat = pWaveform->config.format; - *pChannels = pWaveform->config.channels; - *pSampleRate = pWaveform->config.sampleRate; - ma_channel_map_init_standard(ma_standard_channel_map_default, pChannelMap, channelMapCap, pWaveform->config.channels); - - return MA_SUCCESS; -} - -static ma_result ma_waveform__data_source_on_get_cursor(ma_data_source* pDataSource, ma_uint64* pCursor) -{ - ma_waveform* pWaveform = (ma_waveform*)pDataSource; - - *pCursor = (ma_uint64)(pWaveform->time / pWaveform->advance); - - return MA_SUCCESS; -} - -static double ma_waveform__calculate_advance(ma_uint32 sampleRate, double frequency) -{ - return (1.0 / (sampleRate / frequency)); -} - -static void ma_waveform__update_advance(ma_waveform* pWaveform) -{ - pWaveform->advance = ma_waveform__calculate_advance(pWaveform->config.sampleRate, pWaveform->config.frequency); -} - -static ma_data_source_vtable g_ma_waveform_data_source_vtable = -{ - ma_waveform__data_source_on_read, - ma_waveform__data_source_on_seek, - ma_waveform__data_source_on_get_data_format, - ma_waveform__data_source_on_get_cursor, - NULL, /* onGetLength. There's no notion of a length in waveforms. */ - NULL, /* onSetLooping */ - 0 -}; - -MA_API ma_result ma_waveform_init(const ma_waveform_config* pConfig, ma_waveform* pWaveform) -{ - ma_result result; - ma_data_source_config dataSourceConfig; - - if (pWaveform == NULL) { - return MA_INVALID_ARGS; - } - - MA_ZERO_OBJECT(pWaveform); - - dataSourceConfig = ma_data_source_config_init(); - dataSourceConfig.vtable = &g_ma_waveform_data_source_vtable; - - result = ma_data_source_init(&dataSourceConfig, &pWaveform->ds); - if (result != MA_SUCCESS) { - return result; - } - - pWaveform->config = *pConfig; - pWaveform->advance = ma_waveform__calculate_advance(pWaveform->config.sampleRate, pWaveform->config.frequency); - pWaveform->time = 0; - - return MA_SUCCESS; -} - -MA_API void ma_waveform_uninit(ma_waveform* pWaveform) -{ - if (pWaveform == NULL) { - return; - } - - ma_data_source_uninit(&pWaveform->ds); -} - -MA_API ma_result ma_waveform_set_amplitude(ma_waveform* pWaveform, double amplitude) -{ - if (pWaveform == NULL) { - return MA_INVALID_ARGS; - } - - pWaveform->config.amplitude = amplitude; - return MA_SUCCESS; -} - -MA_API ma_result ma_waveform_set_frequency(ma_waveform* pWaveform, double frequency) -{ - if (pWaveform == NULL) { - return MA_INVALID_ARGS; - } - - pWaveform->config.frequency = frequency; - ma_waveform__update_advance(pWaveform); - - return MA_SUCCESS; -} - -MA_API ma_result ma_waveform_set_type(ma_waveform* pWaveform, ma_waveform_type type) -{ - if (pWaveform == NULL) { - return MA_INVALID_ARGS; - } - - pWaveform->config.type = type; - return MA_SUCCESS; -} - -MA_API ma_result ma_waveform_set_sample_rate(ma_waveform* pWaveform, ma_uint32 sampleRate) -{ - if (pWaveform == NULL) { - return MA_INVALID_ARGS; - } - - pWaveform->config.sampleRate = sampleRate; - ma_waveform__update_advance(pWaveform); - - return MA_SUCCESS; -} - -static float ma_waveform_sine_f32(double time, double amplitude) -{ - return (float)(ma_sind(MA_TAU_D * time) * amplitude); -} - -static ma_int16 ma_waveform_sine_s16(double time, double amplitude) -{ - return ma_pcm_sample_f32_to_s16(ma_waveform_sine_f32(time, amplitude)); -} - -static float ma_waveform_square_f32(double time, double dutyCycle, double amplitude) -{ - double f = time - (ma_int64)time; - double r; - - if (f < dutyCycle) { - r = amplitude; - } else { - r = -amplitude; - } - - return (float)r; -} - -static ma_int16 ma_waveform_square_s16(double time, double dutyCycle, double amplitude) -{ - return ma_pcm_sample_f32_to_s16(ma_waveform_square_f32(time, dutyCycle, amplitude)); -} - -static float ma_waveform_triangle_f32(double time, double amplitude) -{ - double f = time - (ma_int64)time; - double r; - - r = 2 * ma_abs(2 * (f - 0.5)) - 1; - - return (float)(r * amplitude); -} - -static ma_int16 ma_waveform_triangle_s16(double time, double amplitude) -{ - return ma_pcm_sample_f32_to_s16(ma_waveform_triangle_f32(time, amplitude)); -} - -static float ma_waveform_sawtooth_f32(double time, double amplitude) -{ - double f = time - (ma_int64)time; - double r; - - r = 2 * (f - 0.5); - - return (float)(r * amplitude); -} - -static ma_int16 ma_waveform_sawtooth_s16(double time, double amplitude) -{ - return ma_pcm_sample_f32_to_s16(ma_waveform_sawtooth_f32(time, amplitude)); -} - -static void ma_waveform_read_pcm_frames__sine(ma_waveform* pWaveform, void* pFramesOut, ma_uint64 frameCount) -{ - ma_uint64 iFrame; - ma_uint64 iChannel; - ma_uint32 bps = ma_get_bytes_per_sample(pWaveform->config.format); - ma_uint32 bpf = bps * pWaveform->config.channels; - - MA_ASSERT(pWaveform != NULL); - MA_ASSERT(pFramesOut != NULL); - - if (pWaveform->config.format == ma_format_f32) { - float* pFramesOutF32 = (float*)pFramesOut; - for (iFrame = 0; iFrame < frameCount; iFrame += 1) { - float s = ma_waveform_sine_f32(pWaveform->time, pWaveform->config.amplitude); - pWaveform->time += pWaveform->advance; - - for (iChannel = 0; iChannel < pWaveform->config.channels; iChannel += 1) { - pFramesOutF32[iFrame*pWaveform->config.channels + iChannel] = s; - } - } - } else if (pWaveform->config.format == ma_format_s16) { - ma_int16* pFramesOutS16 = (ma_int16*)pFramesOut; - for (iFrame = 0; iFrame < frameCount; iFrame += 1) { - ma_int16 s = ma_waveform_sine_s16(pWaveform->time, pWaveform->config.amplitude); - pWaveform->time += pWaveform->advance; - - for (iChannel = 0; iChannel < pWaveform->config.channels; iChannel += 1) { - pFramesOutS16[iFrame*pWaveform->config.channels + iChannel] = s; - } - } - } else { - for (iFrame = 0; iFrame < frameCount; iFrame += 1) { - float s = ma_waveform_sine_f32(pWaveform->time, pWaveform->config.amplitude); - pWaveform->time += pWaveform->advance; - - for (iChannel = 0; iChannel < pWaveform->config.channels; iChannel += 1) { - ma_pcm_convert(ma_offset_ptr(pFramesOut, iFrame*bpf + iChannel*bps), pWaveform->config.format, &s, ma_format_f32, 1, ma_dither_mode_none); - } - } - } -} - -static void ma_waveform_read_pcm_frames__square(ma_waveform* pWaveform, double dutyCycle, void* pFramesOut, ma_uint64 frameCount) -{ - ma_uint64 iFrame; - ma_uint64 iChannel; - ma_uint32 bps = ma_get_bytes_per_sample(pWaveform->config.format); - ma_uint32 bpf = bps * pWaveform->config.channels; - - MA_ASSERT(pWaveform != NULL); - MA_ASSERT(pFramesOut != NULL); - - if (pWaveform->config.format == ma_format_f32) { - float* pFramesOutF32 = (float*)pFramesOut; - for (iFrame = 0; iFrame < frameCount; iFrame += 1) { - float s = ma_waveform_square_f32(pWaveform->time, dutyCycle, pWaveform->config.amplitude); - pWaveform->time += pWaveform->advance; - - for (iChannel = 0; iChannel < pWaveform->config.channels; iChannel += 1) { - pFramesOutF32[iFrame*pWaveform->config.channels + iChannel] = s; - } - } - } else if (pWaveform->config.format == ma_format_s16) { - ma_int16* pFramesOutS16 = (ma_int16*)pFramesOut; - for (iFrame = 0; iFrame < frameCount; iFrame += 1) { - ma_int16 s = ma_waveform_square_s16(pWaveform->time, dutyCycle, pWaveform->config.amplitude); - pWaveform->time += pWaveform->advance; - - for (iChannel = 0; iChannel < pWaveform->config.channels; iChannel += 1) { - pFramesOutS16[iFrame*pWaveform->config.channels + iChannel] = s; - } - } - } else { - for (iFrame = 0; iFrame < frameCount; iFrame += 1) { - float s = ma_waveform_square_f32(pWaveform->time, dutyCycle, pWaveform->config.amplitude); - pWaveform->time += pWaveform->advance; - - for (iChannel = 0; iChannel < pWaveform->config.channels; iChannel += 1) { - ma_pcm_convert(ma_offset_ptr(pFramesOut, iFrame*bpf + iChannel*bps), pWaveform->config.format, &s, ma_format_f32, 1, ma_dither_mode_none); - } - } - } -} - -static void ma_waveform_read_pcm_frames__triangle(ma_waveform* pWaveform, void* pFramesOut, ma_uint64 frameCount) -{ - ma_uint64 iFrame; - ma_uint64 iChannel; - ma_uint32 bps = ma_get_bytes_per_sample(pWaveform->config.format); - ma_uint32 bpf = bps * pWaveform->config.channels; - - MA_ASSERT(pWaveform != NULL); - MA_ASSERT(pFramesOut != NULL); - - if (pWaveform->config.format == ma_format_f32) { - float* pFramesOutF32 = (float*)pFramesOut; - for (iFrame = 0; iFrame < frameCount; iFrame += 1) { - float s = ma_waveform_triangle_f32(pWaveform->time, pWaveform->config.amplitude); - pWaveform->time += pWaveform->advance; - - for (iChannel = 0; iChannel < pWaveform->config.channels; iChannel += 1) { - pFramesOutF32[iFrame*pWaveform->config.channels + iChannel] = s; - } - } - } else if (pWaveform->config.format == ma_format_s16) { - ma_int16* pFramesOutS16 = (ma_int16*)pFramesOut; - for (iFrame = 0; iFrame < frameCount; iFrame += 1) { - ma_int16 s = ma_waveform_triangle_s16(pWaveform->time, pWaveform->config.amplitude); - pWaveform->time += pWaveform->advance; - - for (iChannel = 0; iChannel < pWaveform->config.channels; iChannel += 1) { - pFramesOutS16[iFrame*pWaveform->config.channels + iChannel] = s; - } - } - } else { - for (iFrame = 0; iFrame < frameCount; iFrame += 1) { - float s = ma_waveform_triangle_f32(pWaveform->time, pWaveform->config.amplitude); - pWaveform->time += pWaveform->advance; - - for (iChannel = 0; iChannel < pWaveform->config.channels; iChannel += 1) { - ma_pcm_convert(ma_offset_ptr(pFramesOut, iFrame*bpf + iChannel*bps), pWaveform->config.format, &s, ma_format_f32, 1, ma_dither_mode_none); - } - } - } -} - -static void ma_waveform_read_pcm_frames__sawtooth(ma_waveform* pWaveform, void* pFramesOut, ma_uint64 frameCount) -{ - ma_uint64 iFrame; - ma_uint64 iChannel; - ma_uint32 bps = ma_get_bytes_per_sample(pWaveform->config.format); - ma_uint32 bpf = bps * pWaveform->config.channels; - - MA_ASSERT(pWaveform != NULL); - MA_ASSERT(pFramesOut != NULL); - - if (pWaveform->config.format == ma_format_f32) { - float* pFramesOutF32 = (float*)pFramesOut; - for (iFrame = 0; iFrame < frameCount; iFrame += 1) { - float s = ma_waveform_sawtooth_f32(pWaveform->time, pWaveform->config.amplitude); - pWaveform->time += pWaveform->advance; - - for (iChannel = 0; iChannel < pWaveform->config.channels; iChannel += 1) { - pFramesOutF32[iFrame*pWaveform->config.channels + iChannel] = s; - } - } - } else if (pWaveform->config.format == ma_format_s16) { - ma_int16* pFramesOutS16 = (ma_int16*)pFramesOut; - for (iFrame = 0; iFrame < frameCount; iFrame += 1) { - ma_int16 s = ma_waveform_sawtooth_s16(pWaveform->time, pWaveform->config.amplitude); - pWaveform->time += pWaveform->advance; - - for (iChannel = 0; iChannel < pWaveform->config.channels; iChannel += 1) { - pFramesOutS16[iFrame*pWaveform->config.channels + iChannel] = s; - } - } - } else { - for (iFrame = 0; iFrame < frameCount; iFrame += 1) { - float s = ma_waveform_sawtooth_f32(pWaveform->time, pWaveform->config.amplitude); - pWaveform->time += pWaveform->advance; - - for (iChannel = 0; iChannel < pWaveform->config.channels; iChannel += 1) { - ma_pcm_convert(ma_offset_ptr(pFramesOut, iFrame*bpf + iChannel*bps), pWaveform->config.format, &s, ma_format_f32, 1, ma_dither_mode_none); - } - } - } -} - -MA_API ma_result ma_waveform_read_pcm_frames(ma_waveform* pWaveform, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead) -{ - if (pFramesRead != NULL) { - *pFramesRead = 0; - } - - if (frameCount == 0) { - return MA_INVALID_ARGS; - } - - if (pWaveform == NULL) { - return MA_INVALID_ARGS; - } - - if (pFramesOut != NULL) { - switch (pWaveform->config.type) - { - case ma_waveform_type_sine: - { - ma_waveform_read_pcm_frames__sine(pWaveform, pFramesOut, frameCount); - } break; - - case ma_waveform_type_square: - { - ma_waveform_read_pcm_frames__square(pWaveform, 0.5, pFramesOut, frameCount); - } break; - - case ma_waveform_type_triangle: - { - ma_waveform_read_pcm_frames__triangle(pWaveform, pFramesOut, frameCount); - } break; - - case ma_waveform_type_sawtooth: - { - ma_waveform_read_pcm_frames__sawtooth(pWaveform, pFramesOut, frameCount); - } break; - - default: return MA_INVALID_OPERATION; /* Unknown waveform type. */ - } - } else { - pWaveform->time += pWaveform->advance * (ma_int64)frameCount; /* Cast to int64 required for VC6. Won't affect anything in practice. */ - } - - if (pFramesRead != NULL) { - *pFramesRead = frameCount; - } - - return MA_SUCCESS; -} - -MA_API ma_result ma_waveform_seek_to_pcm_frame(ma_waveform* pWaveform, ma_uint64 frameIndex) -{ - if (pWaveform == NULL) { - return MA_INVALID_ARGS; - } - - pWaveform->time = pWaveform->advance * (ma_int64)frameIndex; /* Casting for VC6. Won't be an issue in practice. */ - - return MA_SUCCESS; -} - -MA_API ma_pulsewave_config ma_pulsewave_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRate, double dutyCycle, double amplitude, double frequency) -{ - ma_pulsewave_config config; - - MA_ZERO_OBJECT(&config); - config.format = format; - config.channels = channels; - config.sampleRate = sampleRate; - config.dutyCycle = dutyCycle; - config.amplitude = amplitude; - config.frequency = frequency; - - return config; -} - -MA_API ma_result ma_pulsewave_init(const ma_pulsewave_config* pConfig, ma_pulsewave* pWaveform) -{ - ma_result result; - ma_waveform_config config; - - if (pWaveform == NULL) { - return MA_INVALID_ARGS; - } - - MA_ZERO_OBJECT(pWaveform); - - config = ma_waveform_config_init( - pConfig->format, - pConfig->channels, - pConfig->sampleRate, - ma_waveform_type_square, - pConfig->amplitude, - pConfig->frequency - ); - - result = ma_waveform_init(&config, &pWaveform->waveform); - ma_pulsewave_set_duty_cycle(pWaveform, pConfig->dutyCycle); - - return result; -} - -MA_API void ma_pulsewave_uninit(ma_pulsewave* pWaveform) -{ - if (pWaveform == NULL) { - return; - } - - ma_waveform_uninit(&pWaveform->waveform); -} - -MA_API ma_result ma_pulsewave_read_pcm_frames(ma_pulsewave* pWaveform, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead) -{ - if (pFramesRead != NULL) { - *pFramesRead = 0; - } - - if (frameCount == 0) { - return MA_INVALID_ARGS; - } - - if (pWaveform == NULL) { - return MA_INVALID_ARGS; - } - - if (pFramesOut != NULL) { - ma_waveform_read_pcm_frames__square(&pWaveform->waveform, pWaveform->config.dutyCycle, pFramesOut, frameCount); - } else { - pWaveform->waveform.time += pWaveform->waveform.advance * (ma_int64)frameCount; /* Cast to int64 required for VC6. Won't affect anything in practice. */ - } - - if (pFramesRead != NULL) { - *pFramesRead = frameCount; - } - - return MA_SUCCESS; -} - -MA_API ma_result ma_pulsewave_seek_to_pcm_frame(ma_pulsewave* pWaveform, ma_uint64 frameIndex) -{ - if (pWaveform == NULL) { - return MA_INVALID_ARGS; - } - - ma_waveform_seek_to_pcm_frame(&pWaveform->waveform, frameIndex); - - return MA_SUCCESS; -} - -MA_API ma_result ma_pulsewave_set_amplitude(ma_pulsewave* pWaveform, double amplitude) -{ - if (pWaveform == NULL) { - return MA_INVALID_ARGS; - } - - pWaveform->config.amplitude = amplitude; - ma_waveform_set_amplitude(&pWaveform->waveform, amplitude); - - return MA_SUCCESS; -} - -MA_API ma_result ma_pulsewave_set_frequency(ma_pulsewave* pWaveform, double frequency) -{ - if (pWaveform == NULL) { - return MA_INVALID_ARGS; - } - - pWaveform->config.frequency = frequency; - ma_waveform_set_frequency(&pWaveform->waveform, frequency); - - return MA_SUCCESS; -} - -MA_API ma_result ma_pulsewave_set_sample_rate(ma_pulsewave* pWaveform, ma_uint32 sampleRate) -{ - if (pWaveform == NULL) { - return MA_INVALID_ARGS; - } - - pWaveform->config.sampleRate = sampleRate; - ma_waveform_set_sample_rate(&pWaveform->waveform, sampleRate); - - return MA_SUCCESS; -} - -MA_API ma_result ma_pulsewave_set_duty_cycle(ma_pulsewave* pWaveform, double dutyCycle) -{ - if (pWaveform == NULL) { - return MA_INVALID_ARGS; - } - - pWaveform->config.dutyCycle = dutyCycle; - - return MA_SUCCESS; -} - - - -MA_API ma_noise_config ma_noise_config_init(ma_format format, ma_uint32 channels, ma_noise_type type, ma_int32 seed, double amplitude) -{ - ma_noise_config config; - MA_ZERO_OBJECT(&config); - - config.format = format; - config.channels = channels; - config.type = type; - config.seed = seed; - config.amplitude = amplitude; - - if (config.seed == 0) { - config.seed = MA_DEFAULT_LCG_SEED; - } - - return config; -} - - -static ma_result ma_noise__data_source_on_read(ma_data_source* pDataSource, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead) -{ - return ma_noise_read_pcm_frames((ma_noise*)pDataSource, pFramesOut, frameCount, pFramesRead); -} - -static ma_result ma_noise__data_source_on_seek(ma_data_source* pDataSource, ma_uint64 frameIndex) -{ - /* No-op. Just pretend to be successful. */ - (void)pDataSource; - (void)frameIndex; - return MA_SUCCESS; -} - -static ma_result ma_noise__data_source_on_get_data_format(ma_data_source* pDataSource, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate, ma_channel* pChannelMap, size_t channelMapCap) -{ - ma_noise* pNoise = (ma_noise*)pDataSource; - - *pFormat = pNoise->config.format; - *pChannels = pNoise->config.channels; - *pSampleRate = 0; /* There is no notion of sample rate with noise generation. */ - ma_channel_map_init_standard(ma_standard_channel_map_default, pChannelMap, channelMapCap, pNoise->config.channels); - - return MA_SUCCESS; -} - -static ma_data_source_vtable g_ma_noise_data_source_vtable = -{ - ma_noise__data_source_on_read, - ma_noise__data_source_on_seek, /* No-op for noise. */ - ma_noise__data_source_on_get_data_format, - NULL, /* onGetCursor. No notion of a cursor for noise. */ - NULL, /* onGetLength. No notion of a length for noise. */ - NULL, /* onSetLooping */ - 0 -}; - - -#ifndef MA_PINK_NOISE_BIN_SIZE -#define MA_PINK_NOISE_BIN_SIZE 16 -#endif - -typedef struct -{ - size_t sizeInBytes; - struct - { - size_t binOffset; - size_t accumulationOffset; - size_t counterOffset; - } pink; - struct - { - size_t accumulationOffset; - } brownian; -} ma_noise_heap_layout; - -static ma_result ma_noise_get_heap_layout(const ma_noise_config* pConfig, ma_noise_heap_layout* pHeapLayout) -{ - MA_ASSERT(pHeapLayout != NULL); - - MA_ZERO_OBJECT(pHeapLayout); - - if (pConfig == NULL) { - return MA_INVALID_ARGS; - } - - if (pConfig->channels == 0) { - return MA_INVALID_ARGS; - } - - pHeapLayout->sizeInBytes = 0; - - /* Pink. */ - if (pConfig->type == ma_noise_type_pink) { - /* bin */ - pHeapLayout->pink.binOffset = pHeapLayout->sizeInBytes; - pHeapLayout->sizeInBytes += sizeof(double*) * pConfig->channels; - pHeapLayout->sizeInBytes += sizeof(double ) * pConfig->channels * MA_PINK_NOISE_BIN_SIZE; - - /* accumulation */ - pHeapLayout->pink.accumulationOffset = pHeapLayout->sizeInBytes; - pHeapLayout->sizeInBytes += sizeof(double) * pConfig->channels; - - /* counter */ - pHeapLayout->pink.counterOffset = pHeapLayout->sizeInBytes; - pHeapLayout->sizeInBytes += sizeof(ma_uint32) * pConfig->channels; - } - - /* Brownian. */ - if (pConfig->type == ma_noise_type_brownian) { - /* accumulation */ - pHeapLayout->brownian.accumulationOffset = pHeapLayout->sizeInBytes; - pHeapLayout->sizeInBytes += sizeof(double) * pConfig->channels; - } - - /* Make sure allocation size is aligned. */ - pHeapLayout->sizeInBytes = ma_align_64(pHeapLayout->sizeInBytes); - - return MA_SUCCESS; -} - -MA_API ma_result ma_noise_get_heap_size(const ma_noise_config* pConfig, size_t* pHeapSizeInBytes) -{ - ma_result result; - ma_noise_heap_layout heapLayout; - - if (pHeapSizeInBytes == NULL) { - return MA_INVALID_ARGS; - } - - *pHeapSizeInBytes = 0; - - result = ma_noise_get_heap_layout(pConfig, &heapLayout); - if (result != MA_SUCCESS) { - return result; - } - - *pHeapSizeInBytes = heapLayout.sizeInBytes; - - return MA_SUCCESS; -} - -MA_API ma_result ma_noise_init_preallocated(const ma_noise_config* pConfig, void* pHeap, ma_noise* pNoise) -{ - ma_result result; - ma_noise_heap_layout heapLayout; - ma_data_source_config dataSourceConfig; - ma_uint32 iChannel; - - if (pNoise == NULL) { - return MA_INVALID_ARGS; - } - - MA_ZERO_OBJECT(pNoise); - - result = ma_noise_get_heap_layout(pConfig, &heapLayout); - if (result != MA_SUCCESS) { - return result; - } - - pNoise->_pHeap = pHeap; - MA_ZERO_MEMORY(pNoise->_pHeap, heapLayout.sizeInBytes); - - dataSourceConfig = ma_data_source_config_init(); - dataSourceConfig.vtable = &g_ma_noise_data_source_vtable; - - result = ma_data_source_init(&dataSourceConfig, &pNoise->ds); - if (result != MA_SUCCESS) { - return result; - } - - pNoise->config = *pConfig; - ma_lcg_seed(&pNoise->lcg, pConfig->seed); - - if (pNoise->config.type == ma_noise_type_pink) { - pNoise->state.pink.bin = (double** )ma_offset_ptr(pHeap, heapLayout.pink.binOffset); - pNoise->state.pink.accumulation = (double* )ma_offset_ptr(pHeap, heapLayout.pink.accumulationOffset); - pNoise->state.pink.counter = (ma_uint32*)ma_offset_ptr(pHeap, heapLayout.pink.counterOffset); - - for (iChannel = 0; iChannel < pConfig->channels; iChannel += 1) { - pNoise->state.pink.bin[iChannel] = (double*)ma_offset_ptr(pHeap, heapLayout.pink.binOffset + (sizeof(double*) * pConfig->channels) + (sizeof(double) * MA_PINK_NOISE_BIN_SIZE * iChannel)); - pNoise->state.pink.accumulation[iChannel] = 0; - pNoise->state.pink.counter[iChannel] = 1; - } - } - - if (pNoise->config.type == ma_noise_type_brownian) { - pNoise->state.brownian.accumulation = (double*)ma_offset_ptr(pHeap, heapLayout.brownian.accumulationOffset); - - for (iChannel = 0; iChannel < pConfig->channels; iChannel += 1) { - pNoise->state.brownian.accumulation[iChannel] = 0; - } - } - - return MA_SUCCESS; -} - -MA_API ma_result ma_noise_init(const ma_noise_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_noise* pNoise) -{ - ma_result result; - size_t heapSizeInBytes; - void* pHeap; - - result = ma_noise_get_heap_size(pConfig, &heapSizeInBytes); - if (result != MA_SUCCESS) { - return result; - } - - if (heapSizeInBytes > 0) { - pHeap = ma_malloc(heapSizeInBytes, pAllocationCallbacks); - if (pHeap == NULL) { - return MA_OUT_OF_MEMORY; - } - } else { - pHeap = NULL; - } - - result = ma_noise_init_preallocated(pConfig, pHeap, pNoise); - if (result != MA_SUCCESS) { - ma_free(pHeap, pAllocationCallbacks); - return result; - } - - pNoise->_ownsHeap = MA_TRUE; - return MA_SUCCESS; -} - -MA_API void ma_noise_uninit(ma_noise* pNoise, const ma_allocation_callbacks* pAllocationCallbacks) -{ - if (pNoise == NULL) { - return; - } - - ma_data_source_uninit(&pNoise->ds); - - if (pNoise->_ownsHeap) { - ma_free(pNoise->_pHeap, pAllocationCallbacks); - } -} - -MA_API ma_result ma_noise_set_amplitude(ma_noise* pNoise, double amplitude) -{ - if (pNoise == NULL) { - return MA_INVALID_ARGS; - } - - pNoise->config.amplitude = amplitude; - return MA_SUCCESS; -} - -MA_API ma_result ma_noise_set_seed(ma_noise* pNoise, ma_int32 seed) -{ - if (pNoise == NULL) { - return MA_INVALID_ARGS; - } - - pNoise->lcg.state = seed; - return MA_SUCCESS; -} - - -MA_API ma_result ma_noise_set_type(ma_noise* pNoise, ma_noise_type type) -{ - if (pNoise == NULL) { - return MA_INVALID_ARGS; - } - - /* - This function should never have been implemented in the first place. Changing the type dynamically is not - supported. Instead you need to uninitialize and reinitialize a fresh `ma_noise` object. This function - will be removed in version 0.12. - */ - MA_ASSERT(MA_FALSE); - (void)type; - - return MA_INVALID_OPERATION; -} - -static MA_INLINE float ma_noise_f32_white(ma_noise* pNoise) -{ - return (float)(ma_lcg_rand_f64(&pNoise->lcg) * pNoise->config.amplitude); -} - -static MA_INLINE ma_int16 ma_noise_s16_white(ma_noise* pNoise) -{ - return ma_pcm_sample_f32_to_s16(ma_noise_f32_white(pNoise)); -} - -static MA_INLINE ma_uint64 ma_noise_read_pcm_frames__white(ma_noise* pNoise, void* pFramesOut, ma_uint64 frameCount) -{ - ma_uint64 iFrame; - ma_uint32 iChannel; - const ma_uint32 channels = pNoise->config.channels; - MA_ASSUME(channels > 0); - - if (pNoise->config.format == ma_format_f32) { - float* pFramesOutF32 = (float*)pFramesOut; - if (pNoise->config.duplicateChannels) { - for (iFrame = 0; iFrame < frameCount; iFrame += 1) { - float s = ma_noise_f32_white(pNoise); - for (iChannel = 0; iChannel < channels; iChannel += 1) { - pFramesOutF32[iFrame*channels + iChannel] = s; - } - } - } else { - for (iFrame = 0; iFrame < frameCount; iFrame += 1) { - for (iChannel = 0; iChannel < channels; iChannel += 1) { - pFramesOutF32[iFrame*channels + iChannel] = ma_noise_f32_white(pNoise); - } - } - } - } else if (pNoise->config.format == ma_format_s16) { - ma_int16* pFramesOutS16 = (ma_int16*)pFramesOut; - if (pNoise->config.duplicateChannels) { - for (iFrame = 0; iFrame < frameCount; iFrame += 1) { - ma_int16 s = ma_noise_s16_white(pNoise); - for (iChannel = 0; iChannel < channels; iChannel += 1) { - pFramesOutS16[iFrame*channels + iChannel] = s; - } - } - } else { - for (iFrame = 0; iFrame < frameCount; iFrame += 1) { - for (iChannel = 0; iChannel < channels; iChannel += 1) { - pFramesOutS16[iFrame*channels + iChannel] = ma_noise_s16_white(pNoise); - } - } - } - } else { - const ma_uint32 bps = ma_get_bytes_per_sample(pNoise->config.format); - const ma_uint32 bpf = bps * channels; - - if (pNoise->config.duplicateChannels) { - for (iFrame = 0; iFrame < frameCount; iFrame += 1) { - float s = ma_noise_f32_white(pNoise); - for (iChannel = 0; iChannel < channels; iChannel += 1) { - ma_pcm_convert(ma_offset_ptr(pFramesOut, iFrame*bpf + iChannel*bps), pNoise->config.format, &s, ma_format_f32, 1, ma_dither_mode_none); - } - } - } else { - for (iFrame = 0; iFrame < frameCount; iFrame += 1) { - for (iChannel = 0; iChannel < channels; iChannel += 1) { - float s = ma_noise_f32_white(pNoise); - ma_pcm_convert(ma_offset_ptr(pFramesOut, iFrame*bpf + iChannel*bps), pNoise->config.format, &s, ma_format_f32, 1, ma_dither_mode_none); - } - } - } - } - - return frameCount; -} - - -static MA_INLINE unsigned int ma_tzcnt32(unsigned int x) -{ - unsigned int n; - - /* Special case for odd numbers since they should happen about half the time. */ - if (x & 0x1) { - return 0; - } - - if (x == 0) { - return sizeof(x) << 3; - } - - n = 1; - if ((x & 0x0000FFFF) == 0) { x >>= 16; n += 16; } - if ((x & 0x000000FF) == 0) { x >>= 8; n += 8; } - if ((x & 0x0000000F) == 0) { x >>= 4; n += 4; } - if ((x & 0x00000003) == 0) { x >>= 2; n += 2; } - n -= x & 0x00000001; - - return n; -} - -/* -Pink noise generation based on Tonic (public domain) with modifications. https://github.com/TonicAudio/Tonic/blob/master/src/Tonic/Noise.h - -This is basically _the_ reference for pink noise from what I've found: http://www.firstpr.com.au/dsp/pink-noise/ -*/ -static MA_INLINE float ma_noise_f32_pink(ma_noise* pNoise, ma_uint32 iChannel) -{ - double result; - double binPrev; - double binNext; - unsigned int ibin; - - ibin = ma_tzcnt32(pNoise->state.pink.counter[iChannel]) & (MA_PINK_NOISE_BIN_SIZE - 1); - - binPrev = pNoise->state.pink.bin[iChannel][ibin]; - binNext = ma_lcg_rand_f64(&pNoise->lcg); - pNoise->state.pink.bin[iChannel][ibin] = binNext; - - pNoise->state.pink.accumulation[iChannel] += (binNext - binPrev); - pNoise->state.pink.counter[iChannel] += 1; - - result = (ma_lcg_rand_f64(&pNoise->lcg) + pNoise->state.pink.accumulation[iChannel]); - result /= 10; - - return (float)(result * pNoise->config.amplitude); -} - -static MA_INLINE ma_int16 ma_noise_s16_pink(ma_noise* pNoise, ma_uint32 iChannel) -{ - return ma_pcm_sample_f32_to_s16(ma_noise_f32_pink(pNoise, iChannel)); -} - -static MA_INLINE ma_uint64 ma_noise_read_pcm_frames__pink(ma_noise* pNoise, void* pFramesOut, ma_uint64 frameCount) -{ - ma_uint64 iFrame; - ma_uint32 iChannel; - const ma_uint32 channels = pNoise->config.channels; - MA_ASSUME(channels > 0); - - if (pNoise->config.format == ma_format_f32) { - float* pFramesOutF32 = (float*)pFramesOut; - if (pNoise->config.duplicateChannels) { - for (iFrame = 0; iFrame < frameCount; iFrame += 1) { - float s = ma_noise_f32_pink(pNoise, 0); - for (iChannel = 0; iChannel < channels; iChannel += 1) { - pFramesOutF32[iFrame*channels + iChannel] = s; - } - } - } else { - for (iFrame = 0; iFrame < frameCount; iFrame += 1) { - for (iChannel = 0; iChannel < channels; iChannel += 1) { - pFramesOutF32[iFrame*channels + iChannel] = ma_noise_f32_pink(pNoise, iChannel); - } - } - } - } else if (pNoise->config.format == ma_format_s16) { - ma_int16* pFramesOutS16 = (ma_int16*)pFramesOut; - if (pNoise->config.duplicateChannels) { - for (iFrame = 0; iFrame < frameCount; iFrame += 1) { - ma_int16 s = ma_noise_s16_pink(pNoise, 0); - for (iChannel = 0; iChannel < channels; iChannel += 1) { - pFramesOutS16[iFrame*channels + iChannel] = s; - } - } - } else { - for (iFrame = 0; iFrame < frameCount; iFrame += 1) { - for (iChannel = 0; iChannel < channels; iChannel += 1) { - pFramesOutS16[iFrame*channels + iChannel] = ma_noise_s16_pink(pNoise, iChannel); - } - } - } - } else { - const ma_uint32 bps = ma_get_bytes_per_sample(pNoise->config.format); - const ma_uint32 bpf = bps * channels; - - if (pNoise->config.duplicateChannels) { - for (iFrame = 0; iFrame < frameCount; iFrame += 1) { - float s = ma_noise_f32_pink(pNoise, 0); - for (iChannel = 0; iChannel < channels; iChannel += 1) { - ma_pcm_convert(ma_offset_ptr(pFramesOut, iFrame*bpf + iChannel*bps), pNoise->config.format, &s, ma_format_f32, 1, ma_dither_mode_none); - } - } - } else { - for (iFrame = 0; iFrame < frameCount; iFrame += 1) { - for (iChannel = 0; iChannel < channels; iChannel += 1) { - float s = ma_noise_f32_pink(pNoise, iChannel); - ma_pcm_convert(ma_offset_ptr(pFramesOut, iFrame*bpf + iChannel*bps), pNoise->config.format, &s, ma_format_f32, 1, ma_dither_mode_none); - } - } - } - } - - return frameCount; -} - - -static MA_INLINE float ma_noise_f32_brownian(ma_noise* pNoise, ma_uint32 iChannel) -{ - double result; - - result = (ma_lcg_rand_f64(&pNoise->lcg) + pNoise->state.brownian.accumulation[iChannel]); - result /= 1.005; /* Don't escape the -1..1 range on average. */ - - pNoise->state.brownian.accumulation[iChannel] = result; - result /= 20; - - return (float)(result * pNoise->config.amplitude); -} - -static MA_INLINE ma_int16 ma_noise_s16_brownian(ma_noise* pNoise, ma_uint32 iChannel) -{ - return ma_pcm_sample_f32_to_s16(ma_noise_f32_brownian(pNoise, iChannel)); -} - -static MA_INLINE ma_uint64 ma_noise_read_pcm_frames__brownian(ma_noise* pNoise, void* pFramesOut, ma_uint64 frameCount) -{ - ma_uint64 iFrame; - ma_uint32 iChannel; - const ma_uint32 channels = pNoise->config.channels; - MA_ASSUME(channels > 0); - - if (pNoise->config.format == ma_format_f32) { - float* pFramesOutF32 = (float*)pFramesOut; - if (pNoise->config.duplicateChannels) { - for (iFrame = 0; iFrame < frameCount; iFrame += 1) { - float s = ma_noise_f32_brownian(pNoise, 0); - for (iChannel = 0; iChannel < channels; iChannel += 1) { - pFramesOutF32[iFrame*channels + iChannel] = s; - } - } - } else { - for (iFrame = 0; iFrame < frameCount; iFrame += 1) { - for (iChannel = 0; iChannel < channels; iChannel += 1) { - pFramesOutF32[iFrame*channels + iChannel] = ma_noise_f32_brownian(pNoise, iChannel); - } - } - } - } else if (pNoise->config.format == ma_format_s16) { - ma_int16* pFramesOutS16 = (ma_int16*)pFramesOut; - if (pNoise->config.duplicateChannels) { - for (iFrame = 0; iFrame < frameCount; iFrame += 1) { - ma_int16 s = ma_noise_s16_brownian(pNoise, 0); - for (iChannel = 0; iChannel < channels; iChannel += 1) { - pFramesOutS16[iFrame*channels + iChannel] = s; - } - } - } else { - for (iFrame = 0; iFrame < frameCount; iFrame += 1) { - for (iChannel = 0; iChannel < channels; iChannel += 1) { - pFramesOutS16[iFrame*channels + iChannel] = ma_noise_s16_brownian(pNoise, iChannel); - } - } - } - } else { - const ma_uint32 bps = ma_get_bytes_per_sample(pNoise->config.format); - const ma_uint32 bpf = bps * channels; - - if (pNoise->config.duplicateChannels) { - for (iFrame = 0; iFrame < frameCount; iFrame += 1) { - float s = ma_noise_f32_brownian(pNoise, 0); - for (iChannel = 0; iChannel < channels; iChannel += 1) { - ma_pcm_convert(ma_offset_ptr(pFramesOut, iFrame*bpf + iChannel*bps), pNoise->config.format, &s, ma_format_f32, 1, ma_dither_mode_none); - } - } - } else { - for (iFrame = 0; iFrame < frameCount; iFrame += 1) { - for (iChannel = 0; iChannel < channels; iChannel += 1) { - float s = ma_noise_f32_brownian(pNoise, iChannel); - ma_pcm_convert(ma_offset_ptr(pFramesOut, iFrame*bpf + iChannel*bps), pNoise->config.format, &s, ma_format_f32, 1, ma_dither_mode_none); - } - } - } - } - - return frameCount; -} - -MA_API ma_result ma_noise_read_pcm_frames(ma_noise* pNoise, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead) -{ - ma_uint64 framesRead = 0; - - if (pFramesRead != NULL) { - *pFramesRead = 0; - } - - if (frameCount == 0) { - return MA_INVALID_ARGS; - } - - if (pNoise == NULL) { - return MA_INVALID_ARGS; - } - - /* The output buffer is allowed to be NULL. Since we aren't tracking cursors or anything we can just do nothing and pretend to be successful. */ - if (pFramesOut == NULL) { - framesRead = frameCount; - } else { - switch (pNoise->config.type) { - case ma_noise_type_white: framesRead = ma_noise_read_pcm_frames__white (pNoise, pFramesOut, frameCount); break; - case ma_noise_type_pink: framesRead = ma_noise_read_pcm_frames__pink (pNoise, pFramesOut, frameCount); break; - case ma_noise_type_brownian: framesRead = ma_noise_read_pcm_frames__brownian(pNoise, pFramesOut, frameCount); break; - default: return MA_INVALID_OPERATION; /* Unknown noise type. */ - } - } - - if (pFramesRead != NULL) { - *pFramesRead = framesRead; - } - - return MA_SUCCESS; -} -#endif /* MA_NO_GENERATION */ - - - -#ifndef MA_NO_RESOURCE_MANAGER -#ifndef MA_RESOURCE_MANAGER_PAGE_SIZE_IN_MILLISECONDS -#define MA_RESOURCE_MANAGER_PAGE_SIZE_IN_MILLISECONDS 1000 -#endif - -#ifndef MA_JOB_TYPE_RESOURCE_MANAGER_QUEUE_CAPACITY -#define MA_JOB_TYPE_RESOURCE_MANAGER_QUEUE_CAPACITY 1024 -#endif - -MA_API ma_resource_manager_pipeline_notifications ma_resource_manager_pipeline_notifications_init(void) -{ - ma_resource_manager_pipeline_notifications notifications; - - MA_ZERO_OBJECT(¬ifications); - - return notifications; -} - -static void ma_resource_manager_pipeline_notifications_signal_all_notifications(const ma_resource_manager_pipeline_notifications* pPipelineNotifications) -{ - if (pPipelineNotifications == NULL) { - return; - } - - if (pPipelineNotifications->init.pNotification) { ma_async_notification_signal(pPipelineNotifications->init.pNotification); } - if (pPipelineNotifications->done.pNotification) { ma_async_notification_signal(pPipelineNotifications->done.pNotification); } -} - -static void ma_resource_manager_pipeline_notifications_acquire_all_fences(const ma_resource_manager_pipeline_notifications* pPipelineNotifications) -{ - if (pPipelineNotifications == NULL) { - return; - } - - if (pPipelineNotifications->init.pFence != NULL) { ma_fence_acquire(pPipelineNotifications->init.pFence); } - if (pPipelineNotifications->done.pFence != NULL) { ma_fence_acquire(pPipelineNotifications->done.pFence); } -} - -static void ma_resource_manager_pipeline_notifications_release_all_fences(const ma_resource_manager_pipeline_notifications* pPipelineNotifications) -{ - if (pPipelineNotifications == NULL) { - return; - } - - if (pPipelineNotifications->init.pFence != NULL) { ma_fence_release(pPipelineNotifications->init.pFence); } - if (pPipelineNotifications->done.pFence != NULL) { ma_fence_release(pPipelineNotifications->done.pFence); } -} - - - -#ifndef MA_DEFAULT_HASH_SEED -#define MA_DEFAULT_HASH_SEED 42 -#endif - -/* MurmurHash3. Based on code from https://github.com/PeterScott/murmur3/blob/master/murmur3.c (public domain). */ -#if defined(__clang__) || (defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6))) - #pragma GCC diagnostic push - #if __GNUC__ >= 7 - #pragma GCC diagnostic ignored "-Wimplicit-fallthrough" - #endif -#endif - -static MA_INLINE ma_uint32 ma_rotl32(ma_uint32 x, ma_int8 r) -{ - return (x << r) | (x >> (32 - r)); -} - -static MA_INLINE ma_uint32 ma_hash_getblock(const ma_uint32* blocks, int i) -{ - ma_uint32 block; - - /* Try silencing a sanitization warning about unaligned access by doing a memcpy() instead of assignment. */ - MA_COPY_MEMORY(&block, ma_offset_ptr(blocks, i * sizeof(block)), sizeof(block)); - - if (ma_is_little_endian()) { - return block; - } else { - return ma_swap_endian_uint32(block); - } -} - -static MA_INLINE ma_uint32 ma_hash_fmix32(ma_uint32 h) -{ - h ^= h >> 16; - h *= 0x85ebca6b; - h ^= h >> 13; - h *= 0xc2b2ae35; - h ^= h >> 16; - - return h; -} - -static ma_uint32 ma_hash_32(const void* key, int len, ma_uint32 seed) -{ - const ma_uint8* data = (const ma_uint8*)key; - const ma_uint32* blocks; - const ma_uint8* tail; - const int nblocks = len / 4; - ma_uint32 h1 = seed; - ma_uint32 c1 = 0xcc9e2d51; - ma_uint32 c2 = 0x1b873593; - ma_uint32 k1; - int i; - - blocks = (const ma_uint32 *)(data + nblocks*4); - - for(i = -nblocks; i; i++) { - k1 = ma_hash_getblock(blocks,i); - - k1 *= c1; - k1 = ma_rotl32(k1, 15); - k1 *= c2; - - h1 ^= k1; - h1 = ma_rotl32(h1, 13); - h1 = h1*5 + 0xe6546b64; - } - - - tail = (const ma_uint8*)(data + nblocks*4); - - k1 = 0; - switch(len & 3) { - case 3: k1 ^= tail[2] << 16; - case 2: k1 ^= tail[1] << 8; - case 1: k1 ^= tail[0]; - k1 *= c1; k1 = ma_rotl32(k1, 15); k1 *= c2; h1 ^= k1; - }; - - - h1 ^= len; - h1 = ma_hash_fmix32(h1); - - return h1; -} - -#if defined(__clang__) || (defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6))) - #pragma GCC diagnostic push -#endif -/* End MurmurHash3 */ - -static ma_uint32 ma_hash_string_32(const char* str) -{ - return ma_hash_32(str, (int)strlen(str), MA_DEFAULT_HASH_SEED); -} - -static ma_uint32 ma_hash_string_w_32(const wchar_t* str) -{ - return ma_hash_32(str, (int)wcslen(str) * sizeof(*str), MA_DEFAULT_HASH_SEED); -} - - - - -/* -Basic BST Functions -*/ -static ma_result ma_resource_manager_data_buffer_node_search(ma_resource_manager* pResourceManager, ma_uint32 hashedName32, ma_resource_manager_data_buffer_node** ppDataBufferNode) -{ - ma_resource_manager_data_buffer_node* pCurrentNode; - - MA_ASSERT(pResourceManager != NULL); - MA_ASSERT(ppDataBufferNode != NULL); - - pCurrentNode = pResourceManager->pRootDataBufferNode; - while (pCurrentNode != NULL) { - if (hashedName32 == pCurrentNode->hashedName32) { - break; /* Found. */ - } else if (hashedName32 < pCurrentNode->hashedName32) { - pCurrentNode = pCurrentNode->pChildLo; - } else { - pCurrentNode = pCurrentNode->pChildHi; - } - } - - *ppDataBufferNode = pCurrentNode; - - if (pCurrentNode == NULL) { - return MA_DOES_NOT_EXIST; - } else { - return MA_SUCCESS; - } -} - -static ma_result ma_resource_manager_data_buffer_node_insert_point(ma_resource_manager* pResourceManager, ma_uint32 hashedName32, ma_resource_manager_data_buffer_node** ppInsertPoint) -{ - ma_result result = MA_SUCCESS; - ma_resource_manager_data_buffer_node* pCurrentNode; - - MA_ASSERT(pResourceManager != NULL); - MA_ASSERT(ppInsertPoint != NULL); - - *ppInsertPoint = NULL; - - if (pResourceManager->pRootDataBufferNode == NULL) { - return MA_SUCCESS; /* No items. */ - } - - /* We need to find the node that will become the parent of the new node. If a node is found that already has the same hashed name we need to return MA_ALREADY_EXISTS. */ - pCurrentNode = pResourceManager->pRootDataBufferNode; - while (pCurrentNode != NULL) { - if (hashedName32 == pCurrentNode->hashedName32) { - result = MA_ALREADY_EXISTS; - break; - } else { - if (hashedName32 < pCurrentNode->hashedName32) { - if (pCurrentNode->pChildLo == NULL) { - result = MA_SUCCESS; - break; - } else { - pCurrentNode = pCurrentNode->pChildLo; - } - } else { - if (pCurrentNode->pChildHi == NULL) { - result = MA_SUCCESS; - break; - } else { - pCurrentNode = pCurrentNode->pChildHi; - } - } - } - } - - *ppInsertPoint = pCurrentNode; - return result; -} - -static ma_result ma_resource_manager_data_buffer_node_insert_at(ma_resource_manager* pResourceManager, ma_resource_manager_data_buffer_node* pDataBufferNode, ma_resource_manager_data_buffer_node* pInsertPoint) -{ - MA_ASSERT(pResourceManager != NULL); - MA_ASSERT(pDataBufferNode != NULL); - - /* The key must have been set before calling this function. */ - MA_ASSERT(pDataBufferNode->hashedName32 != 0); - - if (pInsertPoint == NULL) { - /* It's the first node. */ - pResourceManager->pRootDataBufferNode = pDataBufferNode; - } else { - /* It's not the first node. It needs to be inserted. */ - if (pDataBufferNode->hashedName32 < pInsertPoint->hashedName32) { - MA_ASSERT(pInsertPoint->pChildLo == NULL); - pInsertPoint->pChildLo = pDataBufferNode; - } else { - MA_ASSERT(pInsertPoint->pChildHi == NULL); - pInsertPoint->pChildHi = pDataBufferNode; - } - } - - pDataBufferNode->pParent = pInsertPoint; - - return MA_SUCCESS; -} - -#if 0 /* Unused for now. */ -static ma_result ma_resource_manager_data_buffer_node_insert(ma_resource_manager* pResourceManager, ma_resource_manager_data_buffer_node* pDataBufferNode) -{ - ma_result result; - ma_resource_manager_data_buffer_node* pInsertPoint; - - MA_ASSERT(pResourceManager != NULL); - MA_ASSERT(pDataBufferNode != NULL); - - result = ma_resource_manager_data_buffer_node_insert_point(pResourceManager, pDataBufferNode->hashedName32, &pInsertPoint); - if (result != MA_SUCCESS) { - return MA_INVALID_ARGS; - } - - return ma_resource_manager_data_buffer_node_insert_at(pResourceManager, pDataBufferNode, pInsertPoint); -} -#endif - -static MA_INLINE ma_resource_manager_data_buffer_node* ma_resource_manager_data_buffer_node_find_min(ma_resource_manager_data_buffer_node* pDataBufferNode) -{ - ma_resource_manager_data_buffer_node* pCurrentNode; - - MA_ASSERT(pDataBufferNode != NULL); - - pCurrentNode = pDataBufferNode; - while (pCurrentNode->pChildLo != NULL) { - pCurrentNode = pCurrentNode->pChildLo; - } - - return pCurrentNode; -} - -static MA_INLINE ma_resource_manager_data_buffer_node* ma_resource_manager_data_buffer_node_find_max(ma_resource_manager_data_buffer_node* pDataBufferNode) -{ - ma_resource_manager_data_buffer_node* pCurrentNode; - - MA_ASSERT(pDataBufferNode != NULL); - - pCurrentNode = pDataBufferNode; - while (pCurrentNode->pChildHi != NULL) { - pCurrentNode = pCurrentNode->pChildHi; - } - - return pCurrentNode; -} - -static MA_INLINE ma_resource_manager_data_buffer_node* ma_resource_manager_data_buffer_node_find_inorder_successor(ma_resource_manager_data_buffer_node* pDataBufferNode) -{ - MA_ASSERT(pDataBufferNode != NULL); - MA_ASSERT(pDataBufferNode->pChildHi != NULL); - - return ma_resource_manager_data_buffer_node_find_min(pDataBufferNode->pChildHi); -} - -static MA_INLINE ma_resource_manager_data_buffer_node* ma_resource_manager_data_buffer_node_find_inorder_predecessor(ma_resource_manager_data_buffer_node* pDataBufferNode) -{ - MA_ASSERT(pDataBufferNode != NULL); - MA_ASSERT(pDataBufferNode->pChildLo != NULL); - - return ma_resource_manager_data_buffer_node_find_max(pDataBufferNode->pChildLo); -} - -static ma_result ma_resource_manager_data_buffer_node_remove(ma_resource_manager* pResourceManager, ma_resource_manager_data_buffer_node* pDataBufferNode) -{ - MA_ASSERT(pResourceManager != NULL); - MA_ASSERT(pDataBufferNode != NULL); - - if (pDataBufferNode->pChildLo == NULL) { - if (pDataBufferNode->pChildHi == NULL) { - /* Simple case - deleting a buffer with no children. */ - if (pDataBufferNode->pParent == NULL) { - MA_ASSERT(pResourceManager->pRootDataBufferNode == pDataBufferNode); /* There is only a single buffer in the tree which should be equal to the root node. */ - pResourceManager->pRootDataBufferNode = NULL; - } else { - if (pDataBufferNode->pParent->pChildLo == pDataBufferNode) { - pDataBufferNode->pParent->pChildLo = NULL; - } else { - pDataBufferNode->pParent->pChildHi = NULL; - } - } - } else { - /* Node has one child - pChildHi != NULL. */ - pDataBufferNode->pChildHi->pParent = pDataBufferNode->pParent; - - if (pDataBufferNode->pParent == NULL) { - MA_ASSERT(pResourceManager->pRootDataBufferNode == pDataBufferNode); - pResourceManager->pRootDataBufferNode = pDataBufferNode->pChildHi; - } else { - if (pDataBufferNode->pParent->pChildLo == pDataBufferNode) { - pDataBufferNode->pParent->pChildLo = pDataBufferNode->pChildHi; - } else { - pDataBufferNode->pParent->pChildHi = pDataBufferNode->pChildHi; - } - } - } - } else { - if (pDataBufferNode->pChildHi == NULL) { - /* Node has one child - pChildLo != NULL. */ - pDataBufferNode->pChildLo->pParent = pDataBufferNode->pParent; - - if (pDataBufferNode->pParent == NULL) { - MA_ASSERT(pResourceManager->pRootDataBufferNode == pDataBufferNode); - pResourceManager->pRootDataBufferNode = pDataBufferNode->pChildLo; - } else { - if (pDataBufferNode->pParent->pChildLo == pDataBufferNode) { - pDataBufferNode->pParent->pChildLo = pDataBufferNode->pChildLo; - } else { - pDataBufferNode->pParent->pChildHi = pDataBufferNode->pChildLo; - } - } - } else { - /* Complex case - deleting a node with two children. */ - ma_resource_manager_data_buffer_node* pReplacementDataBufferNode; - - /* For now we are just going to use the in-order successor as the replacement, but we may want to try to keep this balanced by switching between the two. */ - pReplacementDataBufferNode = ma_resource_manager_data_buffer_node_find_inorder_successor(pDataBufferNode); - MA_ASSERT(pReplacementDataBufferNode != NULL); - - /* - Now that we have our replacement node we can make the change. The simple way to do this would be to just exchange the values, and then remove the replacement - node, however we track specific nodes via pointers which means we can't just swap out the values. We need to instead just change the pointers around. The - replacement node should have at most 1 child. Therefore, we can detach it in terms of our simpler cases above. What we're essentially doing is detaching the - replacement node and reinserting it into the same position as the deleted node. - */ - MA_ASSERT(pReplacementDataBufferNode->pParent != NULL); /* The replacement node should never be the root which means it should always have a parent. */ - MA_ASSERT(pReplacementDataBufferNode->pChildLo == NULL); /* Because we used in-order successor. This would be pChildHi == NULL if we used in-order predecessor. */ - - if (pReplacementDataBufferNode->pChildHi == NULL) { - if (pReplacementDataBufferNode->pParent->pChildLo == pReplacementDataBufferNode) { - pReplacementDataBufferNode->pParent->pChildLo = NULL; - } else { - pReplacementDataBufferNode->pParent->pChildHi = NULL; - } - } else { - pReplacementDataBufferNode->pChildHi->pParent = pReplacementDataBufferNode->pParent; - if (pReplacementDataBufferNode->pParent->pChildLo == pReplacementDataBufferNode) { - pReplacementDataBufferNode->pParent->pChildLo = pReplacementDataBufferNode->pChildHi; - } else { - pReplacementDataBufferNode->pParent->pChildHi = pReplacementDataBufferNode->pChildHi; - } - } - - - /* The replacement node has essentially been detached from the binary tree, so now we need to replace the old data buffer with it. The first thing to update is the parent */ - if (pDataBufferNode->pParent != NULL) { - if (pDataBufferNode->pParent->pChildLo == pDataBufferNode) { - pDataBufferNode->pParent->pChildLo = pReplacementDataBufferNode; - } else { - pDataBufferNode->pParent->pChildHi = pReplacementDataBufferNode; - } - } - - /* Now need to update the replacement node's pointers. */ - pReplacementDataBufferNode->pParent = pDataBufferNode->pParent; - pReplacementDataBufferNode->pChildLo = pDataBufferNode->pChildLo; - pReplacementDataBufferNode->pChildHi = pDataBufferNode->pChildHi; - - /* Now the children of the replacement node need to have their parent pointers updated. */ - if (pReplacementDataBufferNode->pChildLo != NULL) { - pReplacementDataBufferNode->pChildLo->pParent = pReplacementDataBufferNode; - } - if (pReplacementDataBufferNode->pChildHi != NULL) { - pReplacementDataBufferNode->pChildHi->pParent = pReplacementDataBufferNode; - } - - /* Now the root node needs to be updated. */ - if (pResourceManager->pRootDataBufferNode == pDataBufferNode) { - pResourceManager->pRootDataBufferNode = pReplacementDataBufferNode; - } - } - } - - return MA_SUCCESS; -} - -#if 0 /* Unused for now. */ -static ma_result ma_resource_manager_data_buffer_node_remove_by_key(ma_resource_manager* pResourceManager, ma_uint32 hashedName32) -{ - ma_result result; - ma_resource_manager_data_buffer_node* pDataBufferNode; - - result = ma_resource_manager_data_buffer_search(pResourceManager, hashedName32, &pDataBufferNode); - if (result != MA_SUCCESS) { - return result; /* Could not find the data buffer. */ - } - - return ma_resource_manager_data_buffer_remove(pResourceManager, pDataBufferNode); -} -#endif - -static ma_resource_manager_data_supply_type ma_resource_manager_data_buffer_node_get_data_supply_type(ma_resource_manager_data_buffer_node* pDataBufferNode) -{ - return (ma_resource_manager_data_supply_type)ma_atomic_load_i32(&pDataBufferNode->data.type); -} - -static void ma_resource_manager_data_buffer_node_set_data_supply_type(ma_resource_manager_data_buffer_node* pDataBufferNode, ma_resource_manager_data_supply_type supplyType) -{ - ma_atomic_exchange_i32(&pDataBufferNode->data.type, supplyType); -} - -static ma_result ma_resource_manager_data_buffer_node_increment_ref(ma_resource_manager* pResourceManager, ma_resource_manager_data_buffer_node* pDataBufferNode, ma_uint32* pNewRefCount) -{ - ma_uint32 refCount; - - MA_ASSERT(pResourceManager != NULL); - MA_ASSERT(pDataBufferNode != NULL); - - (void)pResourceManager; - - refCount = ma_atomic_fetch_add_32(&pDataBufferNode->refCount, 1) + 1; - - if (pNewRefCount != NULL) { - *pNewRefCount = refCount; - } - - return MA_SUCCESS; -} - -static ma_result ma_resource_manager_data_buffer_node_decrement_ref(ma_resource_manager* pResourceManager, ma_resource_manager_data_buffer_node* pDataBufferNode, ma_uint32* pNewRefCount) -{ - ma_uint32 refCount; - - MA_ASSERT(pResourceManager != NULL); - MA_ASSERT(pDataBufferNode != NULL); - - (void)pResourceManager; - - refCount = ma_atomic_fetch_sub_32(&pDataBufferNode->refCount, 1) - 1; - - if (pNewRefCount != NULL) { - *pNewRefCount = refCount; - } - - return MA_SUCCESS; -} - -static void ma_resource_manager_data_buffer_node_free(ma_resource_manager* pResourceManager, ma_resource_manager_data_buffer_node* pDataBufferNode) -{ - MA_ASSERT(pResourceManager != NULL); - MA_ASSERT(pDataBufferNode != NULL); - - if (pDataBufferNode->isDataOwnedByResourceManager) { - if (ma_resource_manager_data_buffer_node_get_data_supply_type(pDataBufferNode) == ma_resource_manager_data_supply_type_encoded) { - ma_free((void*)pDataBufferNode->data.backend.encoded.pData, &pResourceManager->config.allocationCallbacks); - pDataBufferNode->data.backend.encoded.pData = NULL; - pDataBufferNode->data.backend.encoded.sizeInBytes = 0; - } else if (ma_resource_manager_data_buffer_node_get_data_supply_type(pDataBufferNode) == ma_resource_manager_data_supply_type_decoded) { - ma_free((void*)pDataBufferNode->data.backend.decoded.pData, &pResourceManager->config.allocationCallbacks); - pDataBufferNode->data.backend.decoded.pData = NULL; - pDataBufferNode->data.backend.decoded.totalFrameCount = 0; - } else if (ma_resource_manager_data_buffer_node_get_data_supply_type(pDataBufferNode) == ma_resource_manager_data_supply_type_decoded_paged) { - ma_paged_audio_buffer_data_uninit(&pDataBufferNode->data.backend.decodedPaged.data, &pResourceManager->config.allocationCallbacks); - } else { - /* Should never hit this if the node was successfully initialized. */ - MA_ASSERT(pDataBufferNode->result != MA_SUCCESS); - } - } - - /* The data buffer itself needs to be freed. */ - ma_free(pDataBufferNode, &pResourceManager->config.allocationCallbacks); -} - -static ma_result ma_resource_manager_data_buffer_node_result(const ma_resource_manager_data_buffer_node* pDataBufferNode) -{ - MA_ASSERT(pDataBufferNode != NULL); - - return (ma_result)ma_atomic_load_i32((ma_result*)&pDataBufferNode->result); /* Need a naughty const-cast here. */ -} - - -static ma_bool32 ma_resource_manager_is_threading_enabled(const ma_resource_manager* pResourceManager) -{ - MA_ASSERT(pResourceManager != NULL); - - return (pResourceManager->config.flags & MA_RESOURCE_MANAGER_FLAG_NO_THREADING) == 0; -} - - -typedef struct -{ - union - { - ma_async_notification_event e; - ma_async_notification_poll p; - } backend; /* Must be the first member. */ - ma_resource_manager* pResourceManager; -} ma_resource_manager_inline_notification; - -static ma_result ma_resource_manager_inline_notification_init(ma_resource_manager* pResourceManager, ma_resource_manager_inline_notification* pNotification) -{ - MA_ASSERT(pResourceManager != NULL); - MA_ASSERT(pNotification != NULL); - - pNotification->pResourceManager = pResourceManager; - - if (ma_resource_manager_is_threading_enabled(pResourceManager)) { - return ma_async_notification_event_init(&pNotification->backend.e); - } else { - return ma_async_notification_poll_init(&pNotification->backend.p); - } -} - -static void ma_resource_manager_inline_notification_uninit(ma_resource_manager_inline_notification* pNotification) -{ - MA_ASSERT(pNotification != NULL); - - if (ma_resource_manager_is_threading_enabled(pNotification->pResourceManager)) { - ma_async_notification_event_uninit(&pNotification->backend.e); - } else { - /* No need to uninitialize a polling notification. */ - } -} - -static void ma_resource_manager_inline_notification_wait(ma_resource_manager_inline_notification* pNotification) -{ - MA_ASSERT(pNotification != NULL); - - if (ma_resource_manager_is_threading_enabled(pNotification->pResourceManager)) { - ma_async_notification_event_wait(&pNotification->backend.e); - } else { - while (ma_async_notification_poll_is_signalled(&pNotification->backend.p) == MA_FALSE) { - ma_result result = ma_resource_manager_process_next_job(pNotification->pResourceManager); - if (result == MA_NO_DATA_AVAILABLE || result == MA_CANCELLED) { - break; - } - } - } -} - -static void ma_resource_manager_inline_notification_wait_and_uninit(ma_resource_manager_inline_notification* pNotification) -{ - ma_resource_manager_inline_notification_wait(pNotification); - ma_resource_manager_inline_notification_uninit(pNotification); -} - - -static void ma_resource_manager_data_buffer_bst_lock(ma_resource_manager* pResourceManager) -{ - MA_ASSERT(pResourceManager != NULL); - - if (ma_resource_manager_is_threading_enabled(pResourceManager)) { - #ifndef MA_NO_THREADING - { - ma_mutex_lock(&pResourceManager->dataBufferBSTLock); - } - #else - { - MA_ASSERT(MA_FALSE); /* Should never hit this. */ - } - #endif - } else { - /* Threading not enabled. Do nothing. */ - } -} - -static void ma_resource_manager_data_buffer_bst_unlock(ma_resource_manager* pResourceManager) -{ - MA_ASSERT(pResourceManager != NULL); - - if (ma_resource_manager_is_threading_enabled(pResourceManager)) { - #ifndef MA_NO_THREADING - { - ma_mutex_unlock(&pResourceManager->dataBufferBSTLock); - } - #else - { - MA_ASSERT(MA_FALSE); /* Should never hit this. */ - } - #endif - } else { - /* Threading not enabled. Do nothing. */ - } -} - -#ifndef MA_NO_THREADING -static ma_thread_result MA_THREADCALL ma_resource_manager_job_thread(void* pUserData) -{ - ma_resource_manager* pResourceManager = (ma_resource_manager*)pUserData; - MA_ASSERT(pResourceManager != NULL); - - for (;;) { - ma_result result; - ma_job job; - - result = ma_resource_manager_next_job(pResourceManager, &job); - if (result != MA_SUCCESS) { - break; - } - - /* Terminate if we got a quit message. */ - if (job.toc.breakup.code == MA_JOB_TYPE_QUIT) { - break; - } - - ma_job_process(&job); - } - - return (ma_thread_result)0; -} -#endif - -MA_API ma_resource_manager_config ma_resource_manager_config_init(void) -{ - ma_resource_manager_config config; - - MA_ZERO_OBJECT(&config); - config.decodedFormat = ma_format_unknown; - config.decodedChannels = 0; - config.decodedSampleRate = 0; - config.jobThreadCount = 1; /* A single miniaudio-managed job thread by default. */ - config.jobQueueCapacity = MA_JOB_TYPE_RESOURCE_MANAGER_QUEUE_CAPACITY; - - /* Flags. */ - config.flags = 0; - #ifdef MA_NO_THREADING - { - /* Threading is disabled at compile time so disable threading at runtime as well by default. */ - config.flags |= MA_RESOURCE_MANAGER_FLAG_NO_THREADING; - config.jobThreadCount = 0; - } - #endif - - return config; -} - - -MA_API ma_result ma_resource_manager_init(const ma_resource_manager_config* pConfig, ma_resource_manager* pResourceManager) -{ - ma_result result; - ma_job_queue_config jobQueueConfig; - - if (pResourceManager == NULL) { - return MA_INVALID_ARGS; - } - - MA_ZERO_OBJECT(pResourceManager); - - if (pConfig == NULL) { - return MA_INVALID_ARGS; - } - - #ifndef MA_NO_THREADING - { - if (pConfig->jobThreadCount > ma_countof(pResourceManager->jobThreads)) { - return MA_INVALID_ARGS; /* Requesting too many job threads. */ - } - } - #endif - - pResourceManager->config = *pConfig; - ma_allocation_callbacks_init_copy(&pResourceManager->config.allocationCallbacks, &pConfig->allocationCallbacks); - - /* Get the log set up early so we can start using it as soon as possible. */ - if (pResourceManager->config.pLog == NULL) { - result = ma_log_init(&pResourceManager->config.allocationCallbacks, &pResourceManager->log); - if (result == MA_SUCCESS) { - pResourceManager->config.pLog = &pResourceManager->log; - } else { - pResourceManager->config.pLog = NULL; /* Logging is unavailable. */ - } - } - - if (pResourceManager->config.pVFS == NULL) { - result = ma_default_vfs_init(&pResourceManager->defaultVFS, &pResourceManager->config.allocationCallbacks); - if (result != MA_SUCCESS) { - return result; /* Failed to initialize the default file system. */ - } - - pResourceManager->config.pVFS = &pResourceManager->defaultVFS; - } - - /* If threading has been disabled at compile time, enforce it at run time as well. */ - #ifdef MA_NO_THREADING - { - pResourceManager->config.flags |= MA_RESOURCE_MANAGER_FLAG_NO_THREADING; - } - #endif - - /* We need to force MA_RESOURCE_MANAGER_FLAG_NON_BLOCKING if MA_RESOURCE_MANAGER_FLAG_NO_THREADING is set. */ - if ((pResourceManager->config.flags & MA_RESOURCE_MANAGER_FLAG_NO_THREADING) != 0) { - pResourceManager->config.flags |= MA_RESOURCE_MANAGER_FLAG_NON_BLOCKING; - - /* We cannot allow job threads when MA_RESOURCE_MANAGER_FLAG_NO_THREADING has been set. This is an invalid use case. */ - if (pResourceManager->config.jobThreadCount > 0) { - return MA_INVALID_ARGS; - } - } - - /* Job queue. */ - jobQueueConfig.capacity = pResourceManager->config.jobQueueCapacity; - jobQueueConfig.flags = 0; - if ((pResourceManager->config.flags & MA_RESOURCE_MANAGER_FLAG_NON_BLOCKING) != 0) { - if (pResourceManager->config.jobThreadCount > 0) { - return MA_INVALID_ARGS; /* Non-blocking mode is only valid for self-managed job threads. */ - } - - jobQueueConfig.flags |= MA_JOB_QUEUE_FLAG_NON_BLOCKING; - } - - result = ma_job_queue_init(&jobQueueConfig, &pResourceManager->config.allocationCallbacks, &pResourceManager->jobQueue); - if (result != MA_SUCCESS) { - return result; - } - - - /* Custom decoding backends. */ - if (pConfig->ppCustomDecodingBackendVTables != NULL && pConfig->customDecodingBackendCount > 0) { - size_t sizeInBytes = sizeof(*pResourceManager->config.ppCustomDecodingBackendVTables) * pConfig->customDecodingBackendCount; - ma_decoding_backend_vtable** ppCustomDecodingBackendVTables; - - ppCustomDecodingBackendVTables = (ma_decoding_backend_vtable**)ma_malloc(sizeInBytes, &pResourceManager->config.allocationCallbacks); - if (pResourceManager->config.ppCustomDecodingBackendVTables == NULL) { - ma_job_queue_uninit(&pResourceManager->jobQueue, &pResourceManager->config.allocationCallbacks); - return MA_OUT_OF_MEMORY; - } - - MA_COPY_MEMORY(ppCustomDecodingBackendVTables, pConfig->ppCustomDecodingBackendVTables, sizeInBytes); - - pResourceManager->config.ppCustomDecodingBackendVTables = ppCustomDecodingBackendVTables; - pResourceManager->config.customDecodingBackendCount = pConfig->customDecodingBackendCount; - pResourceManager->config.pCustomDecodingBackendUserData = pConfig->pCustomDecodingBackendUserData; - } - - - - /* Here is where we initialize our threading stuff. We don't do this if we don't support threading. */ - if (ma_resource_manager_is_threading_enabled(pResourceManager)) { - #ifndef MA_NO_THREADING - { - ma_uint32 iJobThread; - - /* Data buffer lock. */ - result = ma_mutex_init(&pResourceManager->dataBufferBSTLock); - if (result != MA_SUCCESS) { - ma_job_queue_uninit(&pResourceManager->jobQueue, &pResourceManager->config.allocationCallbacks); - return result; - } - - /* Create the job threads last to ensure the threads has access to valid data. */ - for (iJobThread = 0; iJobThread < pResourceManager->config.jobThreadCount; iJobThread += 1) { - result = ma_thread_create(&pResourceManager->jobThreads[iJobThread], ma_thread_priority_normal, pResourceManager->config.jobThreadStackSize, ma_resource_manager_job_thread, pResourceManager, &pResourceManager->config.allocationCallbacks); - if (result != MA_SUCCESS) { - ma_mutex_uninit(&pResourceManager->dataBufferBSTLock); - ma_job_queue_uninit(&pResourceManager->jobQueue, &pResourceManager->config.allocationCallbacks); - return result; - } - } - } - #else - { - /* Threading is disabled at compile time. We should never get here because validation checks should have already been performed. */ - MA_ASSERT(MA_FALSE); - } - #endif - } - - return MA_SUCCESS; -} - - -static void ma_resource_manager_delete_all_data_buffer_nodes(ma_resource_manager* pResourceManager) -{ - MA_ASSERT(pResourceManager); - - /* If everything was done properly, there shouldn't be any active data buffers. */ - while (pResourceManager->pRootDataBufferNode != NULL) { - ma_resource_manager_data_buffer_node* pDataBufferNode = pResourceManager->pRootDataBufferNode; - ma_resource_manager_data_buffer_node_remove(pResourceManager, pDataBufferNode); - - /* The data buffer has been removed from the BST, so now we need to free its data. */ - ma_resource_manager_data_buffer_node_free(pResourceManager, pDataBufferNode); - } -} - -MA_API void ma_resource_manager_uninit(ma_resource_manager* pResourceManager) -{ - if (pResourceManager == NULL) { - return; - } - - /* - Job threads need to be killed first. To do this we need to post a quit message to the message queue and then wait for the thread. The quit message will never be removed from the - queue which means it will never not be returned after being encountered for the first time which means all threads will eventually receive it. - */ - ma_resource_manager_post_job_quit(pResourceManager); - - /* Wait for every job to finish before continuing to ensure nothing is sill trying to access any of our objects below. */ - if (ma_resource_manager_is_threading_enabled(pResourceManager)) { - #ifndef MA_NO_THREADING - { - ma_uint32 iJobThread; - - for (iJobThread = 0; iJobThread < pResourceManager->config.jobThreadCount; iJobThread += 1) { - ma_thread_wait(&pResourceManager->jobThreads[iJobThread]); - } - } - #else - { - MA_ASSERT(MA_FALSE); /* Should never hit this. */ - } - #endif - } - - /* At this point the thread should have returned and no other thread should be accessing our data. We can now delete all data buffers. */ - ma_resource_manager_delete_all_data_buffer_nodes(pResourceManager); - - /* The job queue is no longer needed. */ - ma_job_queue_uninit(&pResourceManager->jobQueue, &pResourceManager->config.allocationCallbacks); - - /* We're no longer doing anything with data buffers so the lock can now be uninitialized. */ - if (ma_resource_manager_is_threading_enabled(pResourceManager)) { - #ifndef MA_NO_THREADING - { - ma_mutex_uninit(&pResourceManager->dataBufferBSTLock); - } - #else - { - MA_ASSERT(MA_FALSE); /* Should never hit this. */ - } - #endif - } - - ma_free((ma_decoding_backend_vtable**)pResourceManager->config.ppCustomDecodingBackendVTables, &pResourceManager->config.allocationCallbacks); /* <-- Naughty const-cast, but this is safe. */ - - if (pResourceManager->config.pLog == &pResourceManager->log) { - ma_log_uninit(&pResourceManager->log); - } -} - -MA_API ma_log* ma_resource_manager_get_log(ma_resource_manager* pResourceManager) -{ - if (pResourceManager == NULL) { - return NULL; - } - - return pResourceManager->config.pLog; -} - - - -MA_API ma_resource_manager_data_source_config ma_resource_manager_data_source_config_init(void) -{ - ma_resource_manager_data_source_config config; - - MA_ZERO_OBJECT(&config); - config.rangeBegInPCMFrames = MA_DATA_SOURCE_DEFAULT_RANGE_BEG; - config.rangeEndInPCMFrames = MA_DATA_SOURCE_DEFAULT_RANGE_END; - config.loopPointBegInPCMFrames = MA_DATA_SOURCE_DEFAULT_LOOP_POINT_BEG; - config.loopPointEndInPCMFrames = MA_DATA_SOURCE_DEFAULT_LOOP_POINT_END; - config.isLooping = MA_FALSE; - - return config; -} - - -static ma_decoder_config ma_resource_manager__init_decoder_config(ma_resource_manager* pResourceManager) -{ - ma_decoder_config config; - - config = ma_decoder_config_init(pResourceManager->config.decodedFormat, pResourceManager->config.decodedChannels, pResourceManager->config.decodedSampleRate); - config.allocationCallbacks = pResourceManager->config.allocationCallbacks; - config.ppCustomBackendVTables = pResourceManager->config.ppCustomDecodingBackendVTables; - config.customBackendCount = pResourceManager->config.customDecodingBackendCount; - config.pCustomBackendUserData = pResourceManager->config.pCustomDecodingBackendUserData; - - return config; -} - -static ma_result ma_resource_manager__init_decoder(ma_resource_manager* pResourceManager, const char* pFilePath, const wchar_t* pFilePathW, ma_decoder* pDecoder) -{ - ma_result result; - ma_decoder_config config; - - MA_ASSERT(pResourceManager != NULL); - MA_ASSERT(pFilePath != NULL || pFilePathW != NULL); - MA_ASSERT(pDecoder != NULL); - - config = ma_resource_manager__init_decoder_config(pResourceManager); - - if (pFilePath != NULL) { - result = ma_decoder_init_vfs(pResourceManager->config.pVFS, pFilePath, &config, pDecoder); - if (result != MA_SUCCESS) { - ma_log_postf(ma_resource_manager_get_log(pResourceManager), MA_LOG_LEVEL_WARNING, "Failed to load file \"%s\". %s.\n", pFilePath, ma_result_description(result)); - return result; - } - } else { - result = ma_decoder_init_vfs_w(pResourceManager->config.pVFS, pFilePathW, &config, pDecoder); - if (result != MA_SUCCESS) { - #if (defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L) || defined(_MSC_VER) - ma_log_postf(ma_resource_manager_get_log(pResourceManager), MA_LOG_LEVEL_WARNING, "Failed to load file \"%ls\". %s.\n", pFilePathW, ma_result_description(result)); - #endif - return result; - } - } - - return MA_SUCCESS; -} - -static ma_bool32 ma_resource_manager_data_buffer_has_connector(ma_resource_manager_data_buffer* pDataBuffer) -{ - return ma_atomic_bool32_get(&pDataBuffer->isConnectorInitialized); -} - -static ma_data_source* ma_resource_manager_data_buffer_get_connector(ma_resource_manager_data_buffer* pDataBuffer) -{ - if (ma_resource_manager_data_buffer_has_connector(pDataBuffer) == MA_FALSE) { - return NULL; /* Connector not yet initialized. */ - } - - switch (pDataBuffer->pNode->data.type) - { - case ma_resource_manager_data_supply_type_encoded: return &pDataBuffer->connector.decoder; - case ma_resource_manager_data_supply_type_decoded: return &pDataBuffer->connector.buffer; - case ma_resource_manager_data_supply_type_decoded_paged: return &pDataBuffer->connector.pagedBuffer; - - case ma_resource_manager_data_supply_type_unknown: - default: - { - ma_log_postf(ma_resource_manager_get_log(pDataBuffer->pResourceManager), MA_LOG_LEVEL_ERROR, "Failed to retrieve data buffer connector. Unknown data supply type.\n"); - return NULL; - }; - }; -} - -static ma_result ma_resource_manager_data_buffer_init_connector(ma_resource_manager_data_buffer* pDataBuffer, const ma_resource_manager_data_source_config* pConfig, ma_async_notification* pInitNotification, ma_fence* pInitFence) -{ - ma_result result; - - MA_ASSERT(pDataBuffer != NULL); - MA_ASSERT(pConfig != NULL); - MA_ASSERT(ma_resource_manager_data_buffer_has_connector(pDataBuffer) == MA_FALSE); - - /* The underlying data buffer must be initialized before we'll be able to know how to initialize the backend. */ - result = ma_resource_manager_data_buffer_node_result(pDataBuffer->pNode); - if (result != MA_SUCCESS && result != MA_BUSY) { - return result; /* The data buffer is in an erroneous state. */ - } - - /* - We need to initialize either a ma_decoder or an ma_audio_buffer depending on whether or not the backing data is encoded or decoded. These act as the - "instance" to the data and are used to form the connection between underlying data buffer and the data source. If the data buffer is decoded, we can use - an ma_audio_buffer. This enables us to use memory mapping when mixing which saves us a bit of data movement overhead. - */ - switch (ma_resource_manager_data_buffer_node_get_data_supply_type(pDataBuffer->pNode)) - { - case ma_resource_manager_data_supply_type_encoded: /* Connector is a decoder. */ - { - ma_decoder_config config; - config = ma_resource_manager__init_decoder_config(pDataBuffer->pResourceManager); - result = ma_decoder_init_memory(pDataBuffer->pNode->data.backend.encoded.pData, pDataBuffer->pNode->data.backend.encoded.sizeInBytes, &config, &pDataBuffer->connector.decoder); - } break; - - case ma_resource_manager_data_supply_type_decoded: /* Connector is an audio buffer. */ - { - ma_audio_buffer_config config; - config = ma_audio_buffer_config_init(pDataBuffer->pNode->data.backend.decoded.format, pDataBuffer->pNode->data.backend.decoded.channels, pDataBuffer->pNode->data.backend.decoded.totalFrameCount, pDataBuffer->pNode->data.backend.decoded.pData, NULL); - result = ma_audio_buffer_init(&config, &pDataBuffer->connector.buffer); - } break; - - case ma_resource_manager_data_supply_type_decoded_paged: /* Connector is a paged audio buffer. */ - { - ma_paged_audio_buffer_config config; - config = ma_paged_audio_buffer_config_init(&pDataBuffer->pNode->data.backend.decodedPaged.data); - result = ma_paged_audio_buffer_init(&config, &pDataBuffer->connector.pagedBuffer); - } break; - - case ma_resource_manager_data_supply_type_unknown: - default: - { - /* Unknown data supply type. Should never happen. Need to post an error here. */ - return MA_INVALID_ARGS; - }; - } - - /* - Initialization of the connector is when we can fire the init notification. This will give the application access to - the format/channels/rate of the data source. - */ - if (result == MA_SUCCESS) { - /* - The resource manager supports the ability to set the range and loop settings via a config at - initialization time. This results in an case where the ranges could be set explicitly via - ma_data_source_set_*() before we get to this point here. If this happens, we'll end up - hitting a case where we just override those settings which results in what feels like a bug. - - To address this we only change the relevant properties if they're not equal to defaults. If - they're equal to defaults there's no need to change them anyway. If they're *not* set to the - default values, we can assume the user has set the range and loop settings via the config. If - they're doing their own calls to ma_data_source_set_*() in addition to setting them via the - config, that's entirely on the caller and any synchronization issue becomes their problem. - */ - if (pConfig->rangeBegInPCMFrames != MA_DATA_SOURCE_DEFAULT_RANGE_BEG || pConfig->rangeEndInPCMFrames != MA_DATA_SOURCE_DEFAULT_RANGE_END) { - ma_data_source_set_range_in_pcm_frames(pDataBuffer, pConfig->rangeBegInPCMFrames, pConfig->rangeEndInPCMFrames); - } - - if (pConfig->loopPointBegInPCMFrames != MA_DATA_SOURCE_DEFAULT_LOOP_POINT_BEG || pConfig->loopPointEndInPCMFrames != MA_DATA_SOURCE_DEFAULT_LOOP_POINT_END) { - ma_data_source_set_loop_point_in_pcm_frames(pDataBuffer, pConfig->loopPointBegInPCMFrames, pConfig->loopPointEndInPCMFrames); - } - - if (pConfig->isLooping != MA_FALSE) { - ma_data_source_set_looping(pDataBuffer, pConfig->isLooping); - } - - ma_atomic_bool32_set(&pDataBuffer->isConnectorInitialized, MA_TRUE); - - if (pInitNotification != NULL) { - ma_async_notification_signal(pInitNotification); - } - - if (pInitFence != NULL) { - ma_fence_release(pInitFence); - } - } - - /* At this point the backend should be initialized. We do *not* want to set pDataSource->result here - that needs to be done at a higher level to ensure it's done as the last step. */ - return result; -} - -static ma_result ma_resource_manager_data_buffer_uninit_connector(ma_resource_manager* pResourceManager, ma_resource_manager_data_buffer* pDataBuffer) -{ - MA_ASSERT(pResourceManager != NULL); - MA_ASSERT(pDataBuffer != NULL); - - (void)pResourceManager; - - switch (ma_resource_manager_data_buffer_node_get_data_supply_type(pDataBuffer->pNode)) - { - case ma_resource_manager_data_supply_type_encoded: /* Connector is a decoder. */ - { - ma_decoder_uninit(&pDataBuffer->connector.decoder); - } break; - - case ma_resource_manager_data_supply_type_decoded: /* Connector is an audio buffer. */ - { - ma_audio_buffer_uninit(&pDataBuffer->connector.buffer); - } break; - - case ma_resource_manager_data_supply_type_decoded_paged: /* Connector is a paged audio buffer. */ - { - ma_paged_audio_buffer_uninit(&pDataBuffer->connector.pagedBuffer); - } break; - - case ma_resource_manager_data_supply_type_unknown: - default: - { - /* Unknown data supply type. Should never happen. Need to post an error here. */ - return MA_INVALID_ARGS; - }; - } - - return MA_SUCCESS; -} - -static ma_uint32 ma_resource_manager_data_buffer_node_next_execution_order(ma_resource_manager_data_buffer_node* pDataBufferNode) -{ - MA_ASSERT(pDataBufferNode != NULL); - return ma_atomic_fetch_add_32(&pDataBufferNode->executionCounter, 1); -} - -static ma_result ma_resource_manager_data_buffer_node_init_supply_encoded(ma_resource_manager* pResourceManager, ma_resource_manager_data_buffer_node* pDataBufferNode, const char* pFilePath, const wchar_t* pFilePathW) -{ - ma_result result; - size_t dataSizeInBytes; - void* pData; - - MA_ASSERT(pResourceManager != NULL); - MA_ASSERT(pDataBufferNode != NULL); - MA_ASSERT(pFilePath != NULL || pFilePathW != NULL); - - result = ma_vfs_open_and_read_file_ex(pResourceManager->config.pVFS, pFilePath, pFilePathW, &pData, &dataSizeInBytes, &pResourceManager->config.allocationCallbacks); - if (result != MA_SUCCESS) { - if (pFilePath != NULL) { - ma_log_postf(ma_resource_manager_get_log(pResourceManager), MA_LOG_LEVEL_WARNING, "Failed to load file \"%s\". %s.\n", pFilePath, ma_result_description(result)); - } else { - #if (defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L) || defined(_MSC_VER) - ma_log_postf(ma_resource_manager_get_log(pResourceManager), MA_LOG_LEVEL_WARNING, "Failed to load file \"%ls\". %s.\n", pFilePathW, ma_result_description(result)); - #endif - } - - return result; - } - - pDataBufferNode->data.backend.encoded.pData = pData; - pDataBufferNode->data.backend.encoded.sizeInBytes = dataSizeInBytes; - ma_resource_manager_data_buffer_node_set_data_supply_type(pDataBufferNode, ma_resource_manager_data_supply_type_encoded); /* <-- Must be set last. */ - - return MA_SUCCESS; -} - -static ma_result ma_resource_manager_data_buffer_node_init_supply_decoded(ma_resource_manager* pResourceManager, ma_resource_manager_data_buffer_node* pDataBufferNode, const char* pFilePath, const wchar_t* pFilePathW, ma_uint32 flags, ma_decoder** ppDecoder) -{ - ma_result result = MA_SUCCESS; - ma_decoder* pDecoder; - ma_uint64 totalFrameCount; - - MA_ASSERT(pResourceManager != NULL); - MA_ASSERT(pDataBufferNode != NULL); - MA_ASSERT(ppDecoder != NULL); - MA_ASSERT(pFilePath != NULL || pFilePathW != NULL); - - *ppDecoder = NULL; /* For safety. */ - - pDecoder = (ma_decoder*)ma_malloc(sizeof(*pDecoder), &pResourceManager->config.allocationCallbacks); - if (pDecoder == NULL) { - return MA_OUT_OF_MEMORY; - } - - result = ma_resource_manager__init_decoder(pResourceManager, pFilePath, pFilePathW, pDecoder); - if (result != MA_SUCCESS) { - ma_free(pDecoder, &pResourceManager->config.allocationCallbacks); - return result; - } - - /* - At this point we have the decoder and we now need to initialize the data supply. This will - be either a decoded buffer, or a decoded paged buffer. A regular buffer is just one big heap - allocated buffer, whereas a paged buffer is a linked list of paged-sized buffers. The latter - is used when the length of a sound is unknown until a full decode has been performed. - */ - if ((flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_UNKNOWN_LENGTH) == 0) { - result = ma_decoder_get_length_in_pcm_frames(pDecoder, &totalFrameCount); - if (result != MA_SUCCESS) { - return result; - } - } else { - totalFrameCount = 0; - } - - if (totalFrameCount > 0) { - /* It's a known length. The data supply is a regular decoded buffer. */ - ma_uint64 dataSizeInBytes; - void* pData; - - dataSizeInBytes = totalFrameCount * ma_get_bytes_per_frame(pDecoder->outputFormat, pDecoder->outputChannels); - if (dataSizeInBytes > MA_SIZE_MAX) { - ma_decoder_uninit(pDecoder); - ma_free(pDecoder, &pResourceManager->config.allocationCallbacks); - return MA_TOO_BIG; - } - - pData = ma_malloc((size_t)dataSizeInBytes, &pResourceManager->config.allocationCallbacks); - if (pData == NULL) { - ma_decoder_uninit(pDecoder); - ma_free(pDecoder, &pResourceManager->config.allocationCallbacks); - return MA_OUT_OF_MEMORY; - } - - /* The buffer needs to be initialized to silence in case the caller reads from it. */ - ma_silence_pcm_frames(pData, totalFrameCount, pDecoder->outputFormat, pDecoder->outputChannels); - - /* Data has been allocated and the data supply can now be initialized. */ - pDataBufferNode->data.backend.decoded.pData = pData; - pDataBufferNode->data.backend.decoded.totalFrameCount = totalFrameCount; - pDataBufferNode->data.backend.decoded.format = pDecoder->outputFormat; - pDataBufferNode->data.backend.decoded.channels = pDecoder->outputChannels; - pDataBufferNode->data.backend.decoded.sampleRate = pDecoder->outputSampleRate; - pDataBufferNode->data.backend.decoded.decodedFrameCount = 0; - ma_resource_manager_data_buffer_node_set_data_supply_type(pDataBufferNode, ma_resource_manager_data_supply_type_decoded); /* <-- Must be set last. */ - } else { - /* - It's an unknown length. The data supply is a paged decoded buffer. Setting this up is - actually easier than the non-paged decoded buffer because we just need to initialize - a ma_paged_audio_buffer object. - */ - result = ma_paged_audio_buffer_data_init(pDecoder->outputFormat, pDecoder->outputChannels, &pDataBufferNode->data.backend.decodedPaged.data); - if (result != MA_SUCCESS) { - ma_decoder_uninit(pDecoder); - ma_free(pDecoder, &pResourceManager->config.allocationCallbacks); - return result; - } - - pDataBufferNode->data.backend.decodedPaged.sampleRate = pDecoder->outputSampleRate; - pDataBufferNode->data.backend.decodedPaged.decodedFrameCount = 0; - ma_resource_manager_data_buffer_node_set_data_supply_type(pDataBufferNode, ma_resource_manager_data_supply_type_decoded_paged); /* <-- Must be set last. */ - } - - *ppDecoder = pDecoder; - - return MA_SUCCESS; -} - -static ma_result ma_resource_manager_data_buffer_node_decode_next_page(ma_resource_manager* pResourceManager, ma_resource_manager_data_buffer_node* pDataBufferNode, ma_decoder* pDecoder) -{ - ma_result result = MA_SUCCESS; - ma_uint64 pageSizeInFrames; - ma_uint64 framesToTryReading; - ma_uint64 framesRead; - - MA_ASSERT(pResourceManager != NULL); - MA_ASSERT(pDataBufferNode != NULL); - MA_ASSERT(pDecoder != NULL); - - /* We need to know the size of a page in frames to know how many frames to decode. */ - pageSizeInFrames = MA_RESOURCE_MANAGER_PAGE_SIZE_IN_MILLISECONDS * (pDecoder->outputSampleRate/1000); - framesToTryReading = pageSizeInFrames; - - /* - Here is where we do the decoding of the next page. We'll run a slightly different path depending - on whether or not we're using a flat or paged buffer because the allocation of the page differs - between the two. For a flat buffer it's an offset to an already-allocated buffer. For a paged - buffer, we need to allocate a new page and attach it to the linked list. - */ - switch (ma_resource_manager_data_buffer_node_get_data_supply_type(pDataBufferNode)) - { - case ma_resource_manager_data_supply_type_decoded: - { - /* The destination buffer is an offset to the existing buffer. Don't read more than we originally retrieved when we first initialized the decoder. */ - void* pDst; - ma_uint64 framesRemaining = pDataBufferNode->data.backend.decoded.totalFrameCount - pDataBufferNode->data.backend.decoded.decodedFrameCount; - if (framesToTryReading > framesRemaining) { - framesToTryReading = framesRemaining; - } - - if (framesToTryReading > 0) { - pDst = ma_offset_ptr( - pDataBufferNode->data.backend.decoded.pData, - pDataBufferNode->data.backend.decoded.decodedFrameCount * ma_get_bytes_per_frame(pDataBufferNode->data.backend.decoded.format, pDataBufferNode->data.backend.decoded.channels) - ); - MA_ASSERT(pDst != NULL); - - result = ma_decoder_read_pcm_frames(pDecoder, pDst, framesToTryReading, &framesRead); - if (framesRead > 0) { - pDataBufferNode->data.backend.decoded.decodedFrameCount += framesRead; - } - } else { - framesRead = 0; - } - } break; - - case ma_resource_manager_data_supply_type_decoded_paged: - { - /* The destination buffer is a freshly allocated page. */ - ma_paged_audio_buffer_page* pPage; - - result = ma_paged_audio_buffer_data_allocate_page(&pDataBufferNode->data.backend.decodedPaged.data, framesToTryReading, NULL, &pResourceManager->config.allocationCallbacks, &pPage); - if (result != MA_SUCCESS) { - return result; - } - - result = ma_decoder_read_pcm_frames(pDecoder, pPage->pAudioData, framesToTryReading, &framesRead); - if (result == MA_SUCCESS && framesRead > 0) { - pPage->sizeInFrames = framesRead; - - result = ma_paged_audio_buffer_data_append_page(&pDataBufferNode->data.backend.decodedPaged.data, pPage); - if (result == MA_SUCCESS) { - pDataBufferNode->data.backend.decodedPaged.decodedFrameCount += framesRead; - } else { - /* Failed to append the page. Just abort and set the status to MA_AT_END. */ - ma_paged_audio_buffer_data_free_page(&pDataBufferNode->data.backend.decodedPaged.data, pPage, &pResourceManager->config.allocationCallbacks); - result = MA_AT_END; - } - } else { - /* No frames were read. Free the page and just set the status to MA_AT_END. */ - ma_paged_audio_buffer_data_free_page(&pDataBufferNode->data.backend.decodedPaged.data, pPage, &pResourceManager->config.allocationCallbacks); - result = MA_AT_END; - } - } break; - - case ma_resource_manager_data_supply_type_encoded: - case ma_resource_manager_data_supply_type_unknown: - default: - { - /* Unexpected data supply type. */ - ma_log_postf(ma_resource_manager_get_log(pResourceManager), MA_LOG_LEVEL_ERROR, "Unexpected data supply type (%d) when decoding page.", ma_resource_manager_data_buffer_node_get_data_supply_type(pDataBufferNode)); - return MA_ERROR; - }; - } - - if (result == MA_SUCCESS && framesRead == 0) { - result = MA_AT_END; - } - - return result; -} - -static ma_result ma_resource_manager_data_buffer_node_acquire_critical_section(ma_resource_manager* pResourceManager, const char* pFilePath, const wchar_t* pFilePathW, ma_uint32 hashedName32, ma_uint32 flags, const ma_resource_manager_data_supply* pExistingData, ma_fence* pInitFence, ma_fence* pDoneFence, ma_resource_manager_inline_notification* pInitNotification, ma_resource_manager_data_buffer_node** ppDataBufferNode) -{ - ma_result result = MA_SUCCESS; - ma_resource_manager_data_buffer_node* pDataBufferNode = NULL; - ma_resource_manager_data_buffer_node* pInsertPoint; - - if (ppDataBufferNode != NULL) { - *ppDataBufferNode = NULL; - } - - result = ma_resource_manager_data_buffer_node_insert_point(pResourceManager, hashedName32, &pInsertPoint); - if (result == MA_ALREADY_EXISTS) { - /* The node already exists. We just need to increment the reference count. */ - pDataBufferNode = pInsertPoint; - - result = ma_resource_manager_data_buffer_node_increment_ref(pResourceManager, pDataBufferNode, NULL); - if (result != MA_SUCCESS) { - return result; /* Should never happen. Failed to increment the reference count. */ - } - - result = MA_ALREADY_EXISTS; - goto done; - } else { - /* - The node does not already exist. We need to post a LOAD_DATA_BUFFER_NODE job here. This - needs to be done inside the critical section to ensure an uninitialization of the node - does not occur before initialization on another thread. - */ - pDataBufferNode = (ma_resource_manager_data_buffer_node*)ma_malloc(sizeof(*pDataBufferNode), &pResourceManager->config.allocationCallbacks); - if (pDataBufferNode == NULL) { - return MA_OUT_OF_MEMORY; - } - - MA_ZERO_OBJECT(pDataBufferNode); - pDataBufferNode->hashedName32 = hashedName32; - pDataBufferNode->refCount = 1; /* Always set to 1 by default (this is our first reference). */ - - if (pExistingData == NULL) { - pDataBufferNode->data.type = ma_resource_manager_data_supply_type_unknown; /* <-- We won't know this until we start decoding. */ - pDataBufferNode->result = MA_BUSY; /* Must be set to MA_BUSY before we leave the critical section, so might as well do it now. */ - pDataBufferNode->isDataOwnedByResourceManager = MA_TRUE; - } else { - pDataBufferNode->data = *pExistingData; - pDataBufferNode->result = MA_SUCCESS; /* Not loading asynchronously, so just set the status */ - pDataBufferNode->isDataOwnedByResourceManager = MA_FALSE; - } - - result = ma_resource_manager_data_buffer_node_insert_at(pResourceManager, pDataBufferNode, pInsertPoint); - if (result != MA_SUCCESS) { - ma_free(pDataBufferNode, &pResourceManager->config.allocationCallbacks); - return result; /* Should never happen. Failed to insert the data buffer into the BST. */ - } - - /* - Here is where we'll post the job, but only if we're loading asynchronously. If we're - loading synchronously we'll defer loading to a later stage, outside of the critical - section. - */ - if (pDataBufferNode->isDataOwnedByResourceManager && (flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_ASYNC) != 0) { - /* Loading asynchronously. Post the job. */ - ma_job job; - char* pFilePathCopy = NULL; - wchar_t* pFilePathWCopy = NULL; - - /* We need a copy of the file path. We should probably make this more efficient, but for now we'll do a transient memory allocation. */ - if (pFilePath != NULL) { - pFilePathCopy = ma_copy_string(pFilePath, &pResourceManager->config.allocationCallbacks); - } else { - pFilePathWCopy = ma_copy_string_w(pFilePathW, &pResourceManager->config.allocationCallbacks); - } - - if (pFilePathCopy == NULL && pFilePathWCopy == NULL) { - ma_resource_manager_data_buffer_node_remove(pResourceManager, pDataBufferNode); - ma_free(pDataBufferNode, &pResourceManager->config.allocationCallbacks); - return MA_OUT_OF_MEMORY; - } - - if ((flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_WAIT_INIT) != 0) { - ma_resource_manager_inline_notification_init(pResourceManager, pInitNotification); - } - - /* Acquire init and done fences before posting the job. These will be unacquired by the job thread. */ - if (pInitFence != NULL) { ma_fence_acquire(pInitFence); } - if (pDoneFence != NULL) { ma_fence_acquire(pDoneFence); } - - /* We now have everything we need to post the job to the job thread. */ - job = ma_job_init(MA_JOB_TYPE_RESOURCE_MANAGER_LOAD_DATA_BUFFER_NODE); - job.order = ma_resource_manager_data_buffer_node_next_execution_order(pDataBufferNode); - job.data.resourceManager.loadDataBufferNode.pResourceManager = pResourceManager; - job.data.resourceManager.loadDataBufferNode.pDataBufferNode = pDataBufferNode; - job.data.resourceManager.loadDataBufferNode.pFilePath = pFilePathCopy; - job.data.resourceManager.loadDataBufferNode.pFilePathW = pFilePathWCopy; - job.data.resourceManager.loadDataBufferNode.flags = flags; - job.data.resourceManager.loadDataBufferNode.pInitNotification = ((flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_WAIT_INIT) != 0) ? pInitNotification : NULL; - job.data.resourceManager.loadDataBufferNode.pDoneNotification = NULL; - job.data.resourceManager.loadDataBufferNode.pInitFence = pInitFence; - job.data.resourceManager.loadDataBufferNode.pDoneFence = pDoneFence; - - if ((flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_WAIT_INIT) != 0) { - result = ma_job_process(&job); - } else { - result = ma_resource_manager_post_job(pResourceManager, &job); - } - - if (result != MA_SUCCESS) { - /* Failed to post job. Probably ran out of memory. */ - ma_log_postf(ma_resource_manager_get_log(pResourceManager), MA_LOG_LEVEL_ERROR, "Failed to post MA_JOB_TYPE_RESOURCE_MANAGER_LOAD_DATA_BUFFER_NODE job. %s.\n", ma_result_description(result)); - - /* - Fences were acquired before posting the job, but since the job was not able to - be posted, we need to make sure we release them so nothing gets stuck waiting. - */ - if (pInitFence != NULL) { ma_fence_release(pInitFence); } - if (pDoneFence != NULL) { ma_fence_release(pDoneFence); } - - if ((flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_WAIT_INIT) != 0) { - ma_resource_manager_inline_notification_uninit(pInitNotification); - } else { - /* These will have been freed by the job thread, but with WAIT_INIT they will already have happened since the job has already been handled. */ - ma_free(pFilePathCopy, &pResourceManager->config.allocationCallbacks); - ma_free(pFilePathWCopy, &pResourceManager->config.allocationCallbacks); - } - - ma_resource_manager_data_buffer_node_remove(pResourceManager, pDataBufferNode); - ma_free(pDataBufferNode, &pResourceManager->config.allocationCallbacks); - - return result; - } - } - } - -done: - if (ppDataBufferNode != NULL) { - *ppDataBufferNode = pDataBufferNode; - } - - return result; -} - -static ma_result ma_resource_manager_data_buffer_node_acquire(ma_resource_manager* pResourceManager, const char* pFilePath, const wchar_t* pFilePathW, ma_uint32 hashedName32, ma_uint32 flags, const ma_resource_manager_data_supply* pExistingData, ma_fence* pInitFence, ma_fence* pDoneFence, ma_resource_manager_data_buffer_node** ppDataBufferNode) -{ - ma_result result = MA_SUCCESS; - ma_bool32 nodeAlreadyExists = MA_FALSE; - ma_resource_manager_data_buffer_node* pDataBufferNode = NULL; - ma_resource_manager_inline_notification initNotification; /* Used when the WAIT_INIT flag is set. */ - - if (ppDataBufferNode != NULL) { - *ppDataBufferNode = NULL; /* Safety. */ - } - - if (pResourceManager == NULL || (pFilePath == NULL && pFilePathW == NULL && hashedName32 == 0)) { - return MA_INVALID_ARGS; - } - - /* If we're specifying existing data, it must be valid. */ - if (pExistingData != NULL && pExistingData->type == ma_resource_manager_data_supply_type_unknown) { - return MA_INVALID_ARGS; - } - - /* If we don't support threading, remove the ASYNC flag to make the rest of this a bit simpler. */ - if (ma_resource_manager_is_threading_enabled(pResourceManager) == MA_FALSE) { - flags &= ~MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_ASYNC; - } - - if (hashedName32 == 0) { - if (pFilePath != NULL) { - hashedName32 = ma_hash_string_32(pFilePath); - } else { - hashedName32 = ma_hash_string_w_32(pFilePathW); - } - } - - /* - Here is where we either increment the node's reference count or allocate a new one and add it - to the BST. When allocating a new node, we need to make sure the LOAD_DATA_BUFFER_NODE job is - posted inside the critical section just in case the caller immediately uninitializes the node - as this will ensure the FREE_DATA_BUFFER_NODE job is given an execution order such that the - node is not uninitialized before initialization. - */ - ma_resource_manager_data_buffer_bst_lock(pResourceManager); - { - result = ma_resource_manager_data_buffer_node_acquire_critical_section(pResourceManager, pFilePath, pFilePathW, hashedName32, flags, pExistingData, pInitFence, pDoneFence, &initNotification, &pDataBufferNode); - } - ma_resource_manager_data_buffer_bst_unlock(pResourceManager); - - if (result == MA_ALREADY_EXISTS) { - nodeAlreadyExists = MA_TRUE; - result = MA_SUCCESS; - } else { - if (result != MA_SUCCESS) { - return result; - } - } - - /* - If we're loading synchronously, we'll need to load everything now. When loading asynchronously, - a job will have been posted inside the BST critical section so that an uninitialization can be - allocated an appropriate execution order thereby preventing it from being uninitialized before - the node is initialized by the decoding thread(s). - */ - if (nodeAlreadyExists == MA_FALSE) { /* Don't need to try loading anything if the node already exists. */ - if (pFilePath == NULL && pFilePathW == NULL) { - /* - If this path is hit, it means a buffer is being copied (i.e. initialized from only the - hashed name), but that node has been freed in the meantime, probably from some other - thread. This is an invalid operation. - */ - ma_log_postf(ma_resource_manager_get_log(pResourceManager), MA_LOG_LEVEL_WARNING, "Cloning data buffer node failed because the source node was released. The source node must remain valid until the cloning has completed.\n"); - result = MA_INVALID_OPERATION; - goto done; - } - - if (pDataBufferNode->isDataOwnedByResourceManager) { - if ((flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_ASYNC) == 0) { - /* Loading synchronously. Load the sound in it's entirety here. */ - if ((flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_DECODE) == 0) { - /* No decoding. This is the simple case - just store the file contents in memory. */ - result = ma_resource_manager_data_buffer_node_init_supply_encoded(pResourceManager, pDataBufferNode, pFilePath, pFilePathW); - if (result != MA_SUCCESS) { - goto done; - } - } else { - /* Decoding. We do this the same way as we do when loading asynchronously. */ - ma_decoder* pDecoder; - result = ma_resource_manager_data_buffer_node_init_supply_decoded(pResourceManager, pDataBufferNode, pFilePath, pFilePathW, flags, &pDecoder); - if (result != MA_SUCCESS) { - goto done; - } - - /* We have the decoder, now decode page by page just like we do when loading asynchronously. */ - for (;;) { - /* Decode next page. */ - result = ma_resource_manager_data_buffer_node_decode_next_page(pResourceManager, pDataBufferNode, pDecoder); - if (result != MA_SUCCESS) { - break; /* Will return MA_AT_END when the last page has been decoded. */ - } - } - - /* Reaching the end needs to be considered successful. */ - if (result == MA_AT_END) { - result = MA_SUCCESS; - } - - /* - At this point the data buffer is either fully decoded or some error occurred. Either - way, the decoder is no longer necessary. - */ - ma_decoder_uninit(pDecoder); - ma_free(pDecoder, &pResourceManager->config.allocationCallbacks); - } - - /* Getting here means we were successful. Make sure the status of the node is updated accordingly. */ - ma_atomic_exchange_i32(&pDataBufferNode->result, result); - } else { - /* Loading asynchronously. We may need to wait for initialization. */ - if ((flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_WAIT_INIT) != 0) { - ma_resource_manager_inline_notification_wait(&initNotification); - } - } - } else { - /* The data is not managed by the resource manager so there's nothing else to do. */ - MA_ASSERT(pExistingData != NULL); - } - } - -done: - /* If we failed to initialize the data buffer we need to free it. */ - if (result != MA_SUCCESS) { - if (nodeAlreadyExists == MA_FALSE) { - ma_resource_manager_data_buffer_node_remove(pResourceManager, pDataBufferNode); - ma_free(pDataBufferNode, &pResourceManager->config.allocationCallbacks); - } - } - - /* - The init notification needs to be uninitialized. This will be used if the node does not already - exist, and we've specified ASYNC | WAIT_INIT. - */ - if (nodeAlreadyExists == MA_FALSE && pDataBufferNode->isDataOwnedByResourceManager && (flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_ASYNC) != 0) { - if ((flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_WAIT_INIT) != 0) { - ma_resource_manager_inline_notification_uninit(&initNotification); - } - } - - if (ppDataBufferNode != NULL) { - *ppDataBufferNode = pDataBufferNode; - } - - return result; -} - -static ma_result ma_resource_manager_data_buffer_node_unacquire(ma_resource_manager* pResourceManager, ma_resource_manager_data_buffer_node* pDataBufferNode, const char* pName, const wchar_t* pNameW) -{ - ma_result result = MA_SUCCESS; - ma_uint32 refCount = 0xFFFFFFFF; /* The new reference count of the node after decrementing. Initialize to non-0 to be safe we don't fall into the freeing path. */ - ma_uint32 hashedName32 = 0; - - if (pResourceManager == NULL) { - return MA_INVALID_ARGS; - } - - if (pDataBufferNode == NULL) { - if (pName == NULL && pNameW == NULL) { - return MA_INVALID_ARGS; - } - - if (pName != NULL) { - hashedName32 = ma_hash_string_32(pName); - } else { - hashedName32 = ma_hash_string_w_32(pNameW); - } - } - - /* - The first thing to do is decrement the reference counter of the node. Then, if the reference - count is zero, we need to free the node. If the node is still in the process of loading, we'll - need to post a job to the job queue to free the node. Otherwise we'll just do it here. - */ - ma_resource_manager_data_buffer_bst_lock(pResourceManager); - { - /* Might need to find the node. Must be done inside the critical section. */ - if (pDataBufferNode == NULL) { - result = ma_resource_manager_data_buffer_node_search(pResourceManager, hashedName32, &pDataBufferNode); - if (result != MA_SUCCESS) { - goto stage2; /* Couldn't find the node. */ - } - } - - result = ma_resource_manager_data_buffer_node_decrement_ref(pResourceManager, pDataBufferNode, &refCount); - if (result != MA_SUCCESS) { - goto stage2; /* Should never happen. */ - } - - if (refCount == 0) { - result = ma_resource_manager_data_buffer_node_remove(pResourceManager, pDataBufferNode); - if (result != MA_SUCCESS) { - goto stage2; /* An error occurred when trying to remove the data buffer. This should never happen. */ - } - } - } - ma_resource_manager_data_buffer_bst_unlock(pResourceManager); - -stage2: - if (result != MA_SUCCESS) { - return result; - } - - /* - Here is where we need to free the node. We don't want to do this inside the critical section - above because we want to keep that as small as possible for multi-threaded efficiency. - */ - if (refCount == 0) { - if (ma_resource_manager_data_buffer_node_result(pDataBufferNode) == MA_BUSY) { - /* The sound is still loading. We need to delay the freeing of the node to a safe time. */ - ma_job job; - - /* We need to mark the node as unavailable for the sake of the resource manager worker threads. */ - ma_atomic_exchange_i32(&pDataBufferNode->result, MA_UNAVAILABLE); - - job = ma_job_init(MA_JOB_TYPE_RESOURCE_MANAGER_FREE_DATA_BUFFER_NODE); - job.order = ma_resource_manager_data_buffer_node_next_execution_order(pDataBufferNode); - job.data.resourceManager.freeDataBufferNode.pResourceManager = pResourceManager; - job.data.resourceManager.freeDataBufferNode.pDataBufferNode = pDataBufferNode; - - result = ma_resource_manager_post_job(pResourceManager, &job); - if (result != MA_SUCCESS) { - ma_log_postf(ma_resource_manager_get_log(pResourceManager), MA_LOG_LEVEL_ERROR, "Failed to post MA_JOB_TYPE_RESOURCE_MANAGER_FREE_DATA_BUFFER_NODE job. %s.\n", ma_result_description(result)); - return result; - } - - /* If we don't support threading, process the job queue here. */ - if (ma_resource_manager_is_threading_enabled(pResourceManager) == MA_FALSE) { - while (ma_resource_manager_data_buffer_node_result(pDataBufferNode) == MA_BUSY) { - result = ma_resource_manager_process_next_job(pResourceManager); - if (result == MA_NO_DATA_AVAILABLE || result == MA_CANCELLED) { - result = MA_SUCCESS; - break; - } - } - } else { - /* Threading is enabled. The job queue will deal with the rest of the cleanup from here. */ - } - } else { - /* The sound isn't loading so we can just free the node here. */ - ma_resource_manager_data_buffer_node_free(pResourceManager, pDataBufferNode); - } - } - - return result; -} - - - -static ma_uint32 ma_resource_manager_data_buffer_next_execution_order(ma_resource_manager_data_buffer* pDataBuffer) -{ - MA_ASSERT(pDataBuffer != NULL); - return ma_atomic_fetch_add_32(&pDataBuffer->executionCounter, 1); -} - -static ma_result ma_resource_manager_data_buffer_cb__read_pcm_frames(ma_data_source* pDataSource, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead) -{ - return ma_resource_manager_data_buffer_read_pcm_frames((ma_resource_manager_data_buffer*)pDataSource, pFramesOut, frameCount, pFramesRead); -} - -static ma_result ma_resource_manager_data_buffer_cb__seek_to_pcm_frame(ma_data_source* pDataSource, ma_uint64 frameIndex) -{ - return ma_resource_manager_data_buffer_seek_to_pcm_frame((ma_resource_manager_data_buffer*)pDataSource, frameIndex); -} - -static ma_result ma_resource_manager_data_buffer_cb__get_data_format(ma_data_source* pDataSource, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate, ma_channel* pChannelMap, size_t channelMapCap) -{ - return ma_resource_manager_data_buffer_get_data_format((ma_resource_manager_data_buffer*)pDataSource, pFormat, pChannels, pSampleRate, pChannelMap, channelMapCap); -} - -static ma_result ma_resource_manager_data_buffer_cb__get_cursor_in_pcm_frames(ma_data_source* pDataSource, ma_uint64* pCursor) -{ - return ma_resource_manager_data_buffer_get_cursor_in_pcm_frames((ma_resource_manager_data_buffer*)pDataSource, pCursor); -} - -static ma_result ma_resource_manager_data_buffer_cb__get_length_in_pcm_frames(ma_data_source* pDataSource, ma_uint64* pLength) -{ - return ma_resource_manager_data_buffer_get_length_in_pcm_frames((ma_resource_manager_data_buffer*)pDataSource, pLength); -} - -static ma_result ma_resource_manager_data_buffer_cb__set_looping(ma_data_source* pDataSource, ma_bool32 isLooping) -{ - ma_resource_manager_data_buffer* pDataBuffer = (ma_resource_manager_data_buffer*)pDataSource; - MA_ASSERT(pDataBuffer != NULL); - - ma_atomic_exchange_32(&pDataBuffer->isLooping, isLooping); - - /* The looping state needs to be set on the connector as well or else looping won't work when we read audio data. */ - ma_data_source_set_looping(ma_resource_manager_data_buffer_get_connector(pDataBuffer), isLooping); - - return MA_SUCCESS; -} - -static ma_data_source_vtable g_ma_resource_manager_data_buffer_vtable = -{ - ma_resource_manager_data_buffer_cb__read_pcm_frames, - ma_resource_manager_data_buffer_cb__seek_to_pcm_frame, - ma_resource_manager_data_buffer_cb__get_data_format, - ma_resource_manager_data_buffer_cb__get_cursor_in_pcm_frames, - ma_resource_manager_data_buffer_cb__get_length_in_pcm_frames, - ma_resource_manager_data_buffer_cb__set_looping, - 0 -}; - -static ma_result ma_resource_manager_data_buffer_init_ex_internal(ma_resource_manager* pResourceManager, const ma_resource_manager_data_source_config* pConfig, ma_uint32 hashedName32, ma_resource_manager_data_buffer* pDataBuffer) -{ - ma_result result = MA_SUCCESS; - ma_resource_manager_data_buffer_node* pDataBufferNode; - ma_data_source_config dataSourceConfig; - ma_bool32 async; - ma_uint32 flags; - ma_resource_manager_pipeline_notifications notifications; - - if (pDataBuffer == NULL) { - if (pConfig != NULL && pConfig->pNotifications != NULL) { - ma_resource_manager_pipeline_notifications_signal_all_notifications(pConfig->pNotifications); - } - - return MA_INVALID_ARGS; - } - - MA_ZERO_OBJECT(pDataBuffer); - - if (pConfig == NULL) { - return MA_INVALID_ARGS; - } - - if (pConfig->pNotifications != NULL) { - notifications = *pConfig->pNotifications; /* From here on out we should be referencing `notifications` instead of `pNotifications`. Set this to NULL to catch errors at testing time. */ - } else { - MA_ZERO_OBJECT(¬ifications); - } - - /* For safety, always remove the ASYNC flag if threading is disabled on the resource manager. */ - flags = pConfig->flags; - if (ma_resource_manager_is_threading_enabled(pResourceManager) == MA_FALSE) { - flags &= ~MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_ASYNC; - } - - if (pConfig->isLooping) { - flags |= MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_LOOPING; - } - - async = (flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_ASYNC) != 0; - - /* - Fences need to be acquired before doing anything. These must be acquired and released outside of - the node to ensure there's no holes where ma_fence_wait() could prematurely return before the - data buffer has completed initialization. - - When loading asynchronously, the node acquisition routine below will acquire the fences on this - thread and then release them on the async thread when the operation is complete. - - These fences are always released at the "done" tag at the end of this function. They'll be - acquired a second if loading asynchronously. This double acquisition system is just done to - simplify code maintenance. - */ - ma_resource_manager_pipeline_notifications_acquire_all_fences(¬ifications); - { - /* We first need to acquire a node. If ASYNC is not set, this will not return until the entire sound has been loaded. */ - result = ma_resource_manager_data_buffer_node_acquire(pResourceManager, pConfig->pFilePath, pConfig->pFilePathW, hashedName32, flags, NULL, notifications.init.pFence, notifications.done.pFence, &pDataBufferNode); - if (result != MA_SUCCESS) { - ma_resource_manager_pipeline_notifications_signal_all_notifications(¬ifications); - goto done; - } - - dataSourceConfig = ma_data_source_config_init(); - dataSourceConfig.vtable = &g_ma_resource_manager_data_buffer_vtable; - - result = ma_data_source_init(&dataSourceConfig, &pDataBuffer->ds); - if (result != MA_SUCCESS) { - ma_resource_manager_data_buffer_node_unacquire(pResourceManager, pDataBufferNode, NULL, NULL); - ma_resource_manager_pipeline_notifications_signal_all_notifications(¬ifications); - goto done; - } - - pDataBuffer->pResourceManager = pResourceManager; - pDataBuffer->pNode = pDataBufferNode; - pDataBuffer->flags = flags; - pDataBuffer->result = MA_BUSY; /* Always default to MA_BUSY for safety. It'll be overwritten when loading completes or an error occurs. */ - - /* If we're loading asynchronously we need to post a job to the job queue to initialize the connector. */ - if (async == MA_FALSE || ma_resource_manager_data_buffer_node_result(pDataBufferNode) == MA_SUCCESS) { - /* Loading synchronously or the data has already been fully loaded. We can just initialize the connector from here without a job. */ - result = ma_resource_manager_data_buffer_init_connector(pDataBuffer, pConfig, NULL, NULL); - ma_atomic_exchange_i32(&pDataBuffer->result, result); - - ma_resource_manager_pipeline_notifications_signal_all_notifications(¬ifications); - goto done; - } else { - /* The node's data supply isn't initialized yet. The caller has requested that we load asynchronously so we need to post a job to do this. */ - ma_job job; - ma_resource_manager_inline_notification initNotification; /* Used when the WAIT_INIT flag is set. */ - - if ((flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_WAIT_INIT) != 0) { - ma_resource_manager_inline_notification_init(pResourceManager, &initNotification); - } - - /* - The status of the data buffer needs to be set to MA_BUSY before posting the job so that the - worker thread is aware of its busy state. If the LOAD_DATA_BUFFER job sees a status other - than MA_BUSY, it'll assume an error and fall through to an early exit. - */ - ma_atomic_exchange_i32(&pDataBuffer->result, MA_BUSY); - - /* Acquire fences a second time. These will be released by the async thread. */ - ma_resource_manager_pipeline_notifications_acquire_all_fences(¬ifications); - - job = ma_job_init(MA_JOB_TYPE_RESOURCE_MANAGER_LOAD_DATA_BUFFER); - job.order = ma_resource_manager_data_buffer_next_execution_order(pDataBuffer); - job.data.resourceManager.loadDataBuffer.pDataBuffer = pDataBuffer; - job.data.resourceManager.loadDataBuffer.pInitNotification = ((flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_WAIT_INIT) != 0) ? &initNotification : notifications.init.pNotification; - job.data.resourceManager.loadDataBuffer.pDoneNotification = notifications.done.pNotification; - job.data.resourceManager.loadDataBuffer.pInitFence = notifications.init.pFence; - job.data.resourceManager.loadDataBuffer.pDoneFence = notifications.done.pFence; - job.data.resourceManager.loadDataBuffer.rangeBegInPCMFrames = pConfig->rangeBegInPCMFrames; - job.data.resourceManager.loadDataBuffer.rangeEndInPCMFrames = pConfig->rangeEndInPCMFrames; - job.data.resourceManager.loadDataBuffer.loopPointBegInPCMFrames = pConfig->loopPointBegInPCMFrames; - job.data.resourceManager.loadDataBuffer.loopPointEndInPCMFrames = pConfig->loopPointEndInPCMFrames; - job.data.resourceManager.loadDataBuffer.isLooping = (flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_LOOPING) != 0; - - /* If we need to wait for initialization to complete we can just process the job in place. */ - if ((flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_WAIT_INIT) != 0) { - result = ma_job_process(&job); - } else { - result = ma_resource_manager_post_job(pResourceManager, &job); - } - - if (result != MA_SUCCESS) { - /* We failed to post the job. Most likely there isn't enough room in the queue's buffer. */ - ma_log_postf(ma_resource_manager_get_log(pResourceManager), MA_LOG_LEVEL_ERROR, "Failed to post MA_JOB_TYPE_RESOURCE_MANAGER_LOAD_DATA_BUFFER job. %s.\n", ma_result_description(result)); - ma_atomic_exchange_i32(&pDataBuffer->result, result); - - /* Release the fences after the result has been set on the data buffer. */ - ma_resource_manager_pipeline_notifications_release_all_fences(¬ifications); - } else { - if ((flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_WAIT_INIT) != 0) { - ma_resource_manager_inline_notification_wait(&initNotification); - - if (notifications.init.pNotification != NULL) { - ma_async_notification_signal(notifications.init.pNotification); - } - - /* NOTE: Do not release the init fence here. It will have been done by the job. */ - - /* Make sure we return an error if initialization failed on the async thread. */ - result = ma_resource_manager_data_buffer_result(pDataBuffer); - if (result == MA_BUSY) { - result = MA_SUCCESS; - } - } - } - - if ((flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_WAIT_INIT) != 0) { - ma_resource_manager_inline_notification_uninit(&initNotification); - } - } - - if (result != MA_SUCCESS) { - ma_resource_manager_data_buffer_node_unacquire(pResourceManager, pDataBufferNode, NULL, NULL); - goto done; - } - } -done: - if (result == MA_SUCCESS) { - if (pConfig->initialSeekPointInPCMFrames > 0) { - ma_resource_manager_data_buffer_seek_to_pcm_frame(pDataBuffer, pConfig->initialSeekPointInPCMFrames); - } - } - - ma_resource_manager_pipeline_notifications_release_all_fences(¬ifications); - - return result; -} - -MA_API ma_result ma_resource_manager_data_buffer_init_ex(ma_resource_manager* pResourceManager, const ma_resource_manager_data_source_config* pConfig, ma_resource_manager_data_buffer* pDataBuffer) -{ - return ma_resource_manager_data_buffer_init_ex_internal(pResourceManager, pConfig, 0, pDataBuffer); -} - -MA_API ma_result ma_resource_manager_data_buffer_init(ma_resource_manager* pResourceManager, const char* pFilePath, ma_uint32 flags, const ma_resource_manager_pipeline_notifications* pNotifications, ma_resource_manager_data_buffer* pDataBuffer) -{ - ma_resource_manager_data_source_config config; - - config = ma_resource_manager_data_source_config_init(); - config.pFilePath = pFilePath; - config.flags = flags; - config.pNotifications = pNotifications; - - return ma_resource_manager_data_buffer_init_ex(pResourceManager, &config, pDataBuffer); -} - -MA_API ma_result ma_resource_manager_data_buffer_init_w(ma_resource_manager* pResourceManager, const wchar_t* pFilePath, ma_uint32 flags, const ma_resource_manager_pipeline_notifications* pNotifications, ma_resource_manager_data_buffer* pDataBuffer) -{ - ma_resource_manager_data_source_config config; - - config = ma_resource_manager_data_source_config_init(); - config.pFilePathW = pFilePath; - config.flags = flags; - config.pNotifications = pNotifications; - - return ma_resource_manager_data_buffer_init_ex(pResourceManager, &config, pDataBuffer); -} - -MA_API ma_result ma_resource_manager_data_buffer_init_copy(ma_resource_manager* pResourceManager, const ma_resource_manager_data_buffer* pExistingDataBuffer, ma_resource_manager_data_buffer* pDataBuffer) -{ - ma_resource_manager_data_source_config config; - - if (pExistingDataBuffer == NULL) { - return MA_INVALID_ARGS; - } - - MA_ASSERT(pExistingDataBuffer->pNode != NULL); /* <-- If you've triggered this, you've passed in an invalid existing data buffer. */ - - config = ma_resource_manager_data_source_config_init(); - config.flags = pExistingDataBuffer->flags; - - return ma_resource_manager_data_buffer_init_ex_internal(pResourceManager, &config, pExistingDataBuffer->pNode->hashedName32, pDataBuffer); -} - -static ma_result ma_resource_manager_data_buffer_uninit_internal(ma_resource_manager_data_buffer* pDataBuffer) -{ - MA_ASSERT(pDataBuffer != NULL); - - /* The connector should be uninitialized first. */ - ma_resource_manager_data_buffer_uninit_connector(pDataBuffer->pResourceManager, pDataBuffer); - - /* With the connector uninitialized we can unacquire the node. */ - ma_resource_manager_data_buffer_node_unacquire(pDataBuffer->pResourceManager, pDataBuffer->pNode, NULL, NULL); - - /* The base data source needs to be uninitialized as well. */ - ma_data_source_uninit(&pDataBuffer->ds); - - return MA_SUCCESS; -} - -MA_API ma_result ma_resource_manager_data_buffer_uninit(ma_resource_manager_data_buffer* pDataBuffer) -{ - ma_result result; - - if (pDataBuffer == NULL) { - return MA_INVALID_ARGS; - } - - if (ma_resource_manager_data_buffer_result(pDataBuffer) == MA_SUCCESS) { - /* The data buffer can be deleted synchronously. */ - return ma_resource_manager_data_buffer_uninit_internal(pDataBuffer); - } else { - /* - The data buffer needs to be deleted asynchronously because it's still loading. With the status set to MA_UNAVAILABLE, no more pages will - be loaded and the uninitialization should happen fairly quickly. Since the caller owns the data buffer, we need to wait for this event - to get processed before returning. - */ - ma_resource_manager_inline_notification notification; - ma_job job; - - /* - We need to mark the node as unavailable so we don't try reading from it anymore, but also to - let the loading thread know that it needs to abort it's loading procedure. - */ - ma_atomic_exchange_i32(&pDataBuffer->result, MA_UNAVAILABLE); - - result = ma_resource_manager_inline_notification_init(pDataBuffer->pResourceManager, ¬ification); - if (result != MA_SUCCESS) { - return result; /* Failed to create the notification. This should rarely, if ever, happen. */ - } - - job = ma_job_init(MA_JOB_TYPE_RESOURCE_MANAGER_FREE_DATA_BUFFER); - job.order = ma_resource_manager_data_buffer_next_execution_order(pDataBuffer); - job.data.resourceManager.freeDataBuffer.pDataBuffer = pDataBuffer; - job.data.resourceManager.freeDataBuffer.pDoneNotification = ¬ification; - job.data.resourceManager.freeDataBuffer.pDoneFence = NULL; - - result = ma_resource_manager_post_job(pDataBuffer->pResourceManager, &job); - if (result != MA_SUCCESS) { - ma_resource_manager_inline_notification_uninit(¬ification); - return result; - } - - ma_resource_manager_inline_notification_wait_and_uninit(¬ification); - } - - return result; -} - -MA_API ma_result ma_resource_manager_data_buffer_read_pcm_frames(ma_resource_manager_data_buffer* pDataBuffer, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead) -{ - ma_result result = MA_SUCCESS; - ma_uint64 framesRead = 0; - ma_bool32 isDecodedBufferBusy = MA_FALSE; - - /* Safety. */ - if (pFramesRead != NULL) { - *pFramesRead = 0; - } - - if (frameCount == 0) { - return MA_INVALID_ARGS; - } - - /* - We cannot be using the data buffer after it's been uninitialized. If you trigger this assert it means you're trying to read from the data buffer after - it's been uninitialized or is in the process of uninitializing. - */ - MA_ASSERT(ma_resource_manager_data_buffer_node_result(pDataBuffer->pNode) != MA_UNAVAILABLE); - - /* If the node is not initialized we need to abort with a busy code. */ - if (ma_resource_manager_data_buffer_has_connector(pDataBuffer) == MA_FALSE) { - return MA_BUSY; /* Still loading. */ - } - - /* - If we've got a seek scheduled we'll want to do that before reading. However, for paged buffers, there's - a chance that the sound hasn't yet been decoded up to the seek point will result in the seek failing. If - this happens, we need to keep the seek scheduled and return MA_BUSY. - */ - if (pDataBuffer->seekToCursorOnNextRead) { - pDataBuffer->seekToCursorOnNextRead = MA_FALSE; - - result = ma_data_source_seek_to_pcm_frame(ma_resource_manager_data_buffer_get_connector(pDataBuffer), pDataBuffer->seekTargetInPCMFrames); - if (result != MA_SUCCESS) { - if (result == MA_BAD_SEEK && ma_resource_manager_data_buffer_node_get_data_supply_type(pDataBuffer->pNode) == ma_resource_manager_data_supply_type_decoded_paged) { - pDataBuffer->seekToCursorOnNextRead = MA_TRUE; /* Keep the seek scheduled. We just haven't loaded enough data yet to do the seek properly. */ - return MA_BUSY; - } - - return result; - } - } - - /* - For decoded buffers (not paged) we need to check beforehand how many frames we have available. We cannot - exceed this amount. We'll read as much as we can, and then return MA_BUSY. - */ - if (ma_resource_manager_data_buffer_node_get_data_supply_type(pDataBuffer->pNode) == ma_resource_manager_data_supply_type_decoded) { - ma_uint64 availableFrames; - - isDecodedBufferBusy = (ma_resource_manager_data_buffer_node_result(pDataBuffer->pNode) == MA_BUSY); - - if (ma_resource_manager_data_buffer_get_available_frames(pDataBuffer, &availableFrames) == MA_SUCCESS) { - /* Don't try reading more than the available frame count if the data buffer node is still loading. */ - if (isDecodedBufferBusy) { - if (frameCount > availableFrames) { - frameCount = availableFrames; - - /* - If there's no frames available we want to set the status to MA_AT_END. The logic below - will check if the node is busy, and if so, change it to MA_BUSY. The reason we do this - is because we don't want to call `ma_data_source_read_pcm_frames()` if the frame count - is 0 because that'll result in a situation where it's possible MA_AT_END won't get - returned. - */ - if (frameCount == 0) { - result = MA_AT_END; - } - } else { - isDecodedBufferBusy = MA_FALSE; /* We have enough frames available in the buffer to avoid a MA_BUSY status. */ - } - } else { - /* - Getting here means the buffer has been fully loaded. We can just pass the frame count straight - into ma_data_source_read_pcm_frames() below and let ma_data_source handle it. - */ - } - } - } - - /* Don't attempt to read anything if we've got no frames available. */ - if (frameCount > 0) { - result = ma_data_source_read_pcm_frames(ma_resource_manager_data_buffer_get_connector(pDataBuffer), pFramesOut, frameCount, &framesRead); - } - - /* - If we returned MA_AT_END, but the node is still loading, we don't want to return that code or else the caller will interpret the sound - as at the end and terminate decoding. - */ - if (result == MA_AT_END) { - if (ma_resource_manager_data_buffer_node_result(pDataBuffer->pNode) == MA_BUSY) { - result = MA_BUSY; - } - } - - if (isDecodedBufferBusy) { - result = MA_BUSY; - } - - if (pFramesRead != NULL) { - *pFramesRead = framesRead; - } - - if (result == MA_SUCCESS && framesRead == 0) { - result = MA_AT_END; - } - - return result; -} - -MA_API ma_result ma_resource_manager_data_buffer_seek_to_pcm_frame(ma_resource_manager_data_buffer* pDataBuffer, ma_uint64 frameIndex) -{ - ma_result result; - - /* We cannot be using the data source after it's been uninitialized. */ - MA_ASSERT(ma_resource_manager_data_buffer_node_result(pDataBuffer->pNode) != MA_UNAVAILABLE); - - /* If we haven't yet got a connector we need to abort. */ - if (ma_resource_manager_data_buffer_has_connector(pDataBuffer) == MA_FALSE) { - pDataBuffer->seekTargetInPCMFrames = frameIndex; - pDataBuffer->seekToCursorOnNextRead = MA_TRUE; - return MA_BUSY; /* Still loading. */ - } - - result = ma_data_source_seek_to_pcm_frame(ma_resource_manager_data_buffer_get_connector(pDataBuffer), frameIndex); - if (result != MA_SUCCESS) { - return result; - } - - pDataBuffer->seekTargetInPCMFrames = ~(ma_uint64)0; /* <-- For identification purposes. */ - pDataBuffer->seekToCursorOnNextRead = MA_FALSE; - - return MA_SUCCESS; -} - -MA_API ma_result ma_resource_manager_data_buffer_get_data_format(ma_resource_manager_data_buffer* pDataBuffer, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate, ma_channel* pChannelMap, size_t channelMapCap) -{ - /* We cannot be using the data source after it's been uninitialized. */ - MA_ASSERT(ma_resource_manager_data_buffer_node_result(pDataBuffer->pNode) != MA_UNAVAILABLE); - - switch (ma_resource_manager_data_buffer_node_get_data_supply_type(pDataBuffer->pNode)) - { - case ma_resource_manager_data_supply_type_encoded: - { - return ma_data_source_get_data_format(&pDataBuffer->connector.decoder, pFormat, pChannels, pSampleRate, pChannelMap, channelMapCap); - }; - - case ma_resource_manager_data_supply_type_decoded: - { - *pFormat = pDataBuffer->pNode->data.backend.decoded.format; - *pChannels = pDataBuffer->pNode->data.backend.decoded.channels; - *pSampleRate = pDataBuffer->pNode->data.backend.decoded.sampleRate; - ma_channel_map_init_standard(ma_standard_channel_map_default, pChannelMap, channelMapCap, pDataBuffer->pNode->data.backend.decoded.channels); - return MA_SUCCESS; - }; - - case ma_resource_manager_data_supply_type_decoded_paged: - { - *pFormat = pDataBuffer->pNode->data.backend.decodedPaged.data.format; - *pChannels = pDataBuffer->pNode->data.backend.decodedPaged.data.channels; - *pSampleRate = pDataBuffer->pNode->data.backend.decodedPaged.sampleRate; - ma_channel_map_init_standard(ma_standard_channel_map_default, pChannelMap, channelMapCap, pDataBuffer->pNode->data.backend.decoded.channels); - return MA_SUCCESS; - }; - - case ma_resource_manager_data_supply_type_unknown: - { - return MA_BUSY; /* Still loading. */ - }; - - default: - { - /* Unknown supply type. Should never hit this. */ - return MA_INVALID_ARGS; - } - } -} - -MA_API ma_result ma_resource_manager_data_buffer_get_cursor_in_pcm_frames(ma_resource_manager_data_buffer* pDataBuffer, ma_uint64* pCursor) -{ - /* We cannot be using the data source after it's been uninitialized. */ - MA_ASSERT(ma_resource_manager_data_buffer_node_result(pDataBuffer->pNode) != MA_UNAVAILABLE); - - if (pDataBuffer == NULL || pCursor == NULL) { - return MA_INVALID_ARGS; - } - - *pCursor = 0; - - switch (ma_resource_manager_data_buffer_node_get_data_supply_type(pDataBuffer->pNode)) - { - case ma_resource_manager_data_supply_type_encoded: - { - return ma_decoder_get_cursor_in_pcm_frames(&pDataBuffer->connector.decoder, pCursor); - }; - - case ma_resource_manager_data_supply_type_decoded: - { - return ma_audio_buffer_get_cursor_in_pcm_frames(&pDataBuffer->connector.buffer, pCursor); - }; - - case ma_resource_manager_data_supply_type_decoded_paged: - { - return ma_paged_audio_buffer_get_cursor_in_pcm_frames(&pDataBuffer->connector.pagedBuffer, pCursor); - }; - - case ma_resource_manager_data_supply_type_unknown: - { - return MA_BUSY; - }; - - default: - { - return MA_INVALID_ARGS; - } - } -} - -MA_API ma_result ma_resource_manager_data_buffer_get_length_in_pcm_frames(ma_resource_manager_data_buffer* pDataBuffer, ma_uint64* pLength) -{ - /* We cannot be using the data source after it's been uninitialized. */ - MA_ASSERT(ma_resource_manager_data_buffer_node_result(pDataBuffer->pNode) != MA_UNAVAILABLE); - - if (pDataBuffer == NULL || pLength == NULL) { - return MA_INVALID_ARGS; - } - - if (ma_resource_manager_data_buffer_node_get_data_supply_type(pDataBuffer->pNode) == ma_resource_manager_data_supply_type_unknown) { - return MA_BUSY; /* Still loading. */ - } - - return ma_data_source_get_length_in_pcm_frames(ma_resource_manager_data_buffer_get_connector(pDataBuffer), pLength); -} - -MA_API ma_result ma_resource_manager_data_buffer_result(const ma_resource_manager_data_buffer* pDataBuffer) -{ - if (pDataBuffer == NULL) { - return MA_INVALID_ARGS; - } - - return (ma_result)ma_atomic_load_i32((ma_result*)&pDataBuffer->result); /* Need a naughty const-cast here. */ -} - -MA_API ma_result ma_resource_manager_data_buffer_set_looping(ma_resource_manager_data_buffer* pDataBuffer, ma_bool32 isLooping) -{ - return ma_data_source_set_looping(pDataBuffer, isLooping); -} - -MA_API ma_bool32 ma_resource_manager_data_buffer_is_looping(const ma_resource_manager_data_buffer* pDataBuffer) -{ - return ma_data_source_is_looping(pDataBuffer); -} - -MA_API ma_result ma_resource_manager_data_buffer_get_available_frames(ma_resource_manager_data_buffer* pDataBuffer, ma_uint64* pAvailableFrames) -{ - if (pAvailableFrames == NULL) { - return MA_INVALID_ARGS; - } - - *pAvailableFrames = 0; - - if (pDataBuffer == NULL) { - return MA_INVALID_ARGS; - } - - if (ma_resource_manager_data_buffer_node_get_data_supply_type(pDataBuffer->pNode) == ma_resource_manager_data_supply_type_unknown) { - if (ma_resource_manager_data_buffer_node_result(pDataBuffer->pNode) == MA_BUSY) { - return MA_BUSY; - } else { - return MA_INVALID_OPERATION; /* No connector. */ - } - } - - switch (ma_resource_manager_data_buffer_node_get_data_supply_type(pDataBuffer->pNode)) - { - case ma_resource_manager_data_supply_type_encoded: - { - return ma_decoder_get_available_frames(&pDataBuffer->connector.decoder, pAvailableFrames); - }; - - case ma_resource_manager_data_supply_type_decoded: - { - return ma_audio_buffer_get_available_frames(&pDataBuffer->connector.buffer, pAvailableFrames); - }; - - case ma_resource_manager_data_supply_type_decoded_paged: - { - ma_uint64 cursor; - ma_paged_audio_buffer_get_cursor_in_pcm_frames(&pDataBuffer->connector.pagedBuffer, &cursor); - - if (pDataBuffer->pNode->data.backend.decodedPaged.decodedFrameCount > cursor) { - *pAvailableFrames = pDataBuffer->pNode->data.backend.decodedPaged.decodedFrameCount - cursor; - } else { - *pAvailableFrames = 0; - } - - return MA_SUCCESS; - }; - - case ma_resource_manager_data_supply_type_unknown: - default: - { - /* Unknown supply type. Should never hit this. */ - return MA_INVALID_ARGS; - } - } -} - -MA_API ma_result ma_resource_manager_register_file(ma_resource_manager* pResourceManager, const char* pFilePath, ma_uint32 flags) -{ - return ma_resource_manager_data_buffer_node_acquire(pResourceManager, pFilePath, NULL, 0, flags, NULL, NULL, NULL, NULL); -} - -MA_API ma_result ma_resource_manager_register_file_w(ma_resource_manager* pResourceManager, const wchar_t* pFilePath, ma_uint32 flags) -{ - return ma_resource_manager_data_buffer_node_acquire(pResourceManager, NULL, pFilePath, 0, flags, NULL, NULL, NULL, NULL); -} - - -static ma_result ma_resource_manager_register_data(ma_resource_manager* pResourceManager, const char* pName, const wchar_t* pNameW, ma_resource_manager_data_supply* pExistingData) -{ - return ma_resource_manager_data_buffer_node_acquire(pResourceManager, pName, pNameW, 0, 0, pExistingData, NULL, NULL, NULL); -} - -static ma_result ma_resource_manager_register_decoded_data_internal(ma_resource_manager* pResourceManager, const char* pName, const wchar_t* pNameW, const void* pData, ma_uint64 frameCount, ma_format format, ma_uint32 channels, ma_uint32 sampleRate) -{ - ma_resource_manager_data_supply data; - data.type = ma_resource_manager_data_supply_type_decoded; - data.backend.decoded.pData = pData; - data.backend.decoded.totalFrameCount = frameCount; - data.backend.decoded.format = format; - data.backend.decoded.channels = channels; - data.backend.decoded.sampleRate = sampleRate; - - return ma_resource_manager_register_data(pResourceManager, pName, pNameW, &data); -} - -MA_API ma_result ma_resource_manager_register_decoded_data(ma_resource_manager* pResourceManager, const char* pName, const void* pData, ma_uint64 frameCount, ma_format format, ma_uint32 channels, ma_uint32 sampleRate) -{ - return ma_resource_manager_register_decoded_data_internal(pResourceManager, pName, NULL, pData, frameCount, format, channels, sampleRate); -} - -MA_API ma_result ma_resource_manager_register_decoded_data_w(ma_resource_manager* pResourceManager, const wchar_t* pName, const void* pData, ma_uint64 frameCount, ma_format format, ma_uint32 channels, ma_uint32 sampleRate) -{ - return ma_resource_manager_register_decoded_data_internal(pResourceManager, NULL, pName, pData, frameCount, format, channels, sampleRate); -} - - -static ma_result ma_resource_manager_register_encoded_data_internal(ma_resource_manager* pResourceManager, const char* pName, const wchar_t* pNameW, const void* pData, size_t sizeInBytes) -{ - ma_resource_manager_data_supply data; - data.type = ma_resource_manager_data_supply_type_encoded; - data.backend.encoded.pData = pData; - data.backend.encoded.sizeInBytes = sizeInBytes; - - return ma_resource_manager_register_data(pResourceManager, pName, pNameW, &data); -} - -MA_API ma_result ma_resource_manager_register_encoded_data(ma_resource_manager* pResourceManager, const char* pName, const void* pData, size_t sizeInBytes) -{ - return ma_resource_manager_register_encoded_data_internal(pResourceManager, pName, NULL, pData, sizeInBytes); -} - -MA_API ma_result ma_resource_manager_register_encoded_data_w(ma_resource_manager* pResourceManager, const wchar_t* pName, const void* pData, size_t sizeInBytes) -{ - return ma_resource_manager_register_encoded_data_internal(pResourceManager, NULL, pName, pData, sizeInBytes); -} - - -MA_API ma_result ma_resource_manager_unregister_file(ma_resource_manager* pResourceManager, const char* pFilePath) -{ - return ma_resource_manager_unregister_data(pResourceManager, pFilePath); -} - -MA_API ma_result ma_resource_manager_unregister_file_w(ma_resource_manager* pResourceManager, const wchar_t* pFilePath) -{ - return ma_resource_manager_unregister_data_w(pResourceManager, pFilePath); -} - -MA_API ma_result ma_resource_manager_unregister_data(ma_resource_manager* pResourceManager, const char* pName) -{ - return ma_resource_manager_data_buffer_node_unacquire(pResourceManager, NULL, pName, NULL); -} - -MA_API ma_result ma_resource_manager_unregister_data_w(ma_resource_manager* pResourceManager, const wchar_t* pName) -{ - return ma_resource_manager_data_buffer_node_unacquire(pResourceManager, NULL, NULL, pName); -} - - -static ma_uint32 ma_resource_manager_data_stream_next_execution_order(ma_resource_manager_data_stream* pDataStream) -{ - MA_ASSERT(pDataStream != NULL); - return ma_atomic_fetch_add_32(&pDataStream->executionCounter, 1); -} - -static ma_bool32 ma_resource_manager_data_stream_is_decoder_at_end(const ma_resource_manager_data_stream* pDataStream) -{ - MA_ASSERT(pDataStream != NULL); - return ma_atomic_load_32((ma_bool32*)&pDataStream->isDecoderAtEnd); -} - -static ma_uint32 ma_resource_manager_data_stream_seek_counter(const ma_resource_manager_data_stream* pDataStream) -{ - MA_ASSERT(pDataStream != NULL); - return ma_atomic_load_32((ma_uint32*)&pDataStream->seekCounter); -} - - -static ma_result ma_resource_manager_data_stream_cb__read_pcm_frames(ma_data_source* pDataSource, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead) -{ - return ma_resource_manager_data_stream_read_pcm_frames((ma_resource_manager_data_stream*)pDataSource, pFramesOut, frameCount, pFramesRead); -} - -static ma_result ma_resource_manager_data_stream_cb__seek_to_pcm_frame(ma_data_source* pDataSource, ma_uint64 frameIndex) -{ - return ma_resource_manager_data_stream_seek_to_pcm_frame((ma_resource_manager_data_stream*)pDataSource, frameIndex); -} - -static ma_result ma_resource_manager_data_stream_cb__get_data_format(ma_data_source* pDataSource, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate, ma_channel* pChannelMap, size_t channelMapCap) -{ - return ma_resource_manager_data_stream_get_data_format((ma_resource_manager_data_stream*)pDataSource, pFormat, pChannels, pSampleRate, pChannelMap, channelMapCap); -} - -static ma_result ma_resource_manager_data_stream_cb__get_cursor_in_pcm_frames(ma_data_source* pDataSource, ma_uint64* pCursor) -{ - return ma_resource_manager_data_stream_get_cursor_in_pcm_frames((ma_resource_manager_data_stream*)pDataSource, pCursor); -} - -static ma_result ma_resource_manager_data_stream_cb__get_length_in_pcm_frames(ma_data_source* pDataSource, ma_uint64* pLength) -{ - return ma_resource_manager_data_stream_get_length_in_pcm_frames((ma_resource_manager_data_stream*)pDataSource, pLength); -} - -static ma_result ma_resource_manager_data_stream_cb__set_looping(ma_data_source* pDataSource, ma_bool32 isLooping) -{ - ma_resource_manager_data_stream* pDataStream = (ma_resource_manager_data_stream*)pDataSource; - MA_ASSERT(pDataStream != NULL); - - ma_atomic_exchange_32(&pDataStream->isLooping, isLooping); - - return MA_SUCCESS; -} - -static ma_data_source_vtable g_ma_resource_manager_data_stream_vtable = -{ - ma_resource_manager_data_stream_cb__read_pcm_frames, - ma_resource_manager_data_stream_cb__seek_to_pcm_frame, - ma_resource_manager_data_stream_cb__get_data_format, - ma_resource_manager_data_stream_cb__get_cursor_in_pcm_frames, - ma_resource_manager_data_stream_cb__get_length_in_pcm_frames, - ma_resource_manager_data_stream_cb__set_looping, - 0 /*MA_DATA_SOURCE_SELF_MANAGED_RANGE_AND_LOOP_POINT*/ -}; - -static void ma_resource_manager_data_stream_set_absolute_cursor(ma_resource_manager_data_stream* pDataStream, ma_uint64 absoluteCursor) -{ - /* Loop if possible. */ - if (absoluteCursor > pDataStream->totalLengthInPCMFrames && pDataStream->totalLengthInPCMFrames > 0) { - absoluteCursor = absoluteCursor % pDataStream->totalLengthInPCMFrames; - } - - ma_atomic_exchange_64(&pDataStream->absoluteCursor, absoluteCursor); -} - -MA_API ma_result ma_resource_manager_data_stream_init_ex(ma_resource_manager* pResourceManager, const ma_resource_manager_data_source_config* pConfig, ma_resource_manager_data_stream* pDataStream) -{ - ma_result result; - ma_data_source_config dataSourceConfig; - char* pFilePathCopy = NULL; - wchar_t* pFilePathWCopy = NULL; - ma_job job; - ma_bool32 waitBeforeReturning = MA_FALSE; - ma_resource_manager_inline_notification waitNotification; - ma_resource_manager_pipeline_notifications notifications; - ma_uint32 flags; - - if (pDataStream == NULL) { - if (pConfig != NULL && pConfig->pNotifications != NULL) { - ma_resource_manager_pipeline_notifications_signal_all_notifications(pConfig->pNotifications); - } - - return MA_INVALID_ARGS; - } - - MA_ZERO_OBJECT(pDataStream); - - if (pConfig == NULL) { - return MA_INVALID_ARGS; - } - - if (pConfig->pNotifications != NULL) { - notifications = *pConfig->pNotifications; /* From here on out, `notifications` should be used instead of `pNotifications`. Setting this to NULL to catch any errors at testing time. */ - } else { - MA_ZERO_OBJECT(¬ifications); - } - - dataSourceConfig = ma_data_source_config_init(); - dataSourceConfig.vtable = &g_ma_resource_manager_data_stream_vtable; - - result = ma_data_source_init(&dataSourceConfig, &pDataStream->ds); - if (result != MA_SUCCESS) { - ma_resource_manager_pipeline_notifications_signal_all_notifications(¬ifications); - return result; - } - - flags = pConfig->flags; - if (pConfig->isLooping) { - flags |= MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_LOOPING; - } - - pDataStream->pResourceManager = pResourceManager; - pDataStream->flags = pConfig->flags; - pDataStream->result = MA_BUSY; - - ma_data_source_set_range_in_pcm_frames(pDataStream, pConfig->rangeBegInPCMFrames, pConfig->rangeEndInPCMFrames); - ma_data_source_set_loop_point_in_pcm_frames(pDataStream, pConfig->loopPointBegInPCMFrames, pConfig->loopPointEndInPCMFrames); - ma_data_source_set_looping(pDataStream, (flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_LOOPING) != 0); - - if (pResourceManager == NULL || (pConfig->pFilePath == NULL && pConfig->pFilePathW == NULL)) { - ma_resource_manager_pipeline_notifications_signal_all_notifications(¬ifications); - return MA_INVALID_ARGS; - } - - /* We want all access to the VFS and the internal decoder to happen on the job thread just to keep things easier to manage for the VFS. */ - - /* We need a copy of the file path. We should probably make this more efficient, but for now we'll do a transient memory allocation. */ - if (pConfig->pFilePath != NULL) { - pFilePathCopy = ma_copy_string(pConfig->pFilePath, &pResourceManager->config.allocationCallbacks); - } else { - pFilePathWCopy = ma_copy_string_w(pConfig->pFilePathW, &pResourceManager->config.allocationCallbacks); - } - - if (pFilePathCopy == NULL && pFilePathWCopy == NULL) { - ma_resource_manager_pipeline_notifications_signal_all_notifications(¬ifications); - return MA_OUT_OF_MEMORY; - } - - /* - We need to check for the presence of MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_ASYNC. If it's not set, we need to wait before returning. Otherwise we - can return immediately. Likewise, we'll also check for MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_WAIT_INIT and do the same. - */ - if ((pConfig->flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_ASYNC) == 0 || (pConfig->flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_WAIT_INIT) != 0) { - waitBeforeReturning = MA_TRUE; - ma_resource_manager_inline_notification_init(pResourceManager, &waitNotification); - } - - ma_resource_manager_pipeline_notifications_acquire_all_fences(¬ifications); - - /* Set the absolute cursor to our initial seek position so retrieval of the cursor returns a good value. */ - ma_resource_manager_data_stream_set_absolute_cursor(pDataStream, pConfig->initialSeekPointInPCMFrames); - - /* We now have everything we need to post the job. This is the last thing we need to do from here. The rest will be done by the job thread. */ - job = ma_job_init(MA_JOB_TYPE_RESOURCE_MANAGER_LOAD_DATA_STREAM); - job.order = ma_resource_manager_data_stream_next_execution_order(pDataStream); - job.data.resourceManager.loadDataStream.pDataStream = pDataStream; - job.data.resourceManager.loadDataStream.pFilePath = pFilePathCopy; - job.data.resourceManager.loadDataStream.pFilePathW = pFilePathWCopy; - job.data.resourceManager.loadDataStream.initialSeekPoint = pConfig->initialSeekPointInPCMFrames; - job.data.resourceManager.loadDataStream.pInitNotification = (waitBeforeReturning == MA_TRUE) ? &waitNotification : notifications.init.pNotification; - job.data.resourceManager.loadDataStream.pInitFence = notifications.init.pFence; - result = ma_resource_manager_post_job(pResourceManager, &job); - if (result != MA_SUCCESS) { - ma_resource_manager_pipeline_notifications_signal_all_notifications(¬ifications); - ma_resource_manager_pipeline_notifications_release_all_fences(¬ifications); - - if (waitBeforeReturning) { - ma_resource_manager_inline_notification_uninit(&waitNotification); - } - - ma_free(pFilePathCopy, &pResourceManager->config.allocationCallbacks); - ma_free(pFilePathWCopy, &pResourceManager->config.allocationCallbacks); - return result; - } - - /* Wait if needed. */ - if (waitBeforeReturning) { - ma_resource_manager_inline_notification_wait_and_uninit(&waitNotification); - - if (notifications.init.pNotification != NULL) { - ma_async_notification_signal(notifications.init.pNotification); - } - - /* - If there was an error during initialization make sure we return that result here. We don't want to do this - if we're not waiting because it will most likely be in a busy state. - */ - if (pDataStream->result != MA_SUCCESS) { - return pDataStream->result; - } - - /* NOTE: Do not release pInitFence here. That will be done by the job. */ - } - - return MA_SUCCESS; -} - -MA_API ma_result ma_resource_manager_data_stream_init(ma_resource_manager* pResourceManager, const char* pFilePath, ma_uint32 flags, const ma_resource_manager_pipeline_notifications* pNotifications, ma_resource_manager_data_stream* pDataStream) -{ - ma_resource_manager_data_source_config config; - - config = ma_resource_manager_data_source_config_init(); - config.pFilePath = pFilePath; - config.flags = flags; - config.pNotifications = pNotifications; - - return ma_resource_manager_data_stream_init_ex(pResourceManager, &config, pDataStream); -} - -MA_API ma_result ma_resource_manager_data_stream_init_w(ma_resource_manager* pResourceManager, const wchar_t* pFilePath, ma_uint32 flags, const ma_resource_manager_pipeline_notifications* pNotifications, ma_resource_manager_data_stream* pDataStream) -{ - ma_resource_manager_data_source_config config; - - config = ma_resource_manager_data_source_config_init(); - config.pFilePathW = pFilePath; - config.flags = flags; - config.pNotifications = pNotifications; - - return ma_resource_manager_data_stream_init_ex(pResourceManager, &config, pDataStream); -} - -MA_API ma_result ma_resource_manager_data_stream_uninit(ma_resource_manager_data_stream* pDataStream) -{ - ma_resource_manager_inline_notification freeEvent; - ma_job job; - - if (pDataStream == NULL) { - return MA_INVALID_ARGS; - } - - /* The first thing to do is set the result to unavailable. This will prevent future page decoding. */ - ma_atomic_exchange_i32(&pDataStream->result, MA_UNAVAILABLE); - - /* - We need to post a job to ensure we're not in the middle or decoding or anything. Because the object is owned by the caller, we'll need - to wait for it to complete before returning which means we need an event. - */ - ma_resource_manager_inline_notification_init(pDataStream->pResourceManager, &freeEvent); - - job = ma_job_init(MA_JOB_TYPE_RESOURCE_MANAGER_FREE_DATA_STREAM); - job.order = ma_resource_manager_data_stream_next_execution_order(pDataStream); - job.data.resourceManager.freeDataStream.pDataStream = pDataStream; - job.data.resourceManager.freeDataStream.pDoneNotification = &freeEvent; - job.data.resourceManager.freeDataStream.pDoneFence = NULL; - ma_resource_manager_post_job(pDataStream->pResourceManager, &job); - - /* We need to wait for the job to finish processing before we return. */ - ma_resource_manager_inline_notification_wait_and_uninit(&freeEvent); - - return MA_SUCCESS; -} - - -static ma_uint32 ma_resource_manager_data_stream_get_page_size_in_frames(ma_resource_manager_data_stream* pDataStream) -{ - MA_ASSERT(pDataStream != NULL); - MA_ASSERT(pDataStream->isDecoderInitialized == MA_TRUE); - - return MA_RESOURCE_MANAGER_PAGE_SIZE_IN_MILLISECONDS * (pDataStream->decoder.outputSampleRate/1000); -} - -static void* ma_resource_manager_data_stream_get_page_data_pointer(ma_resource_manager_data_stream* pDataStream, ma_uint32 pageIndex, ma_uint32 relativeCursor) -{ - MA_ASSERT(pDataStream != NULL); - MA_ASSERT(pDataStream->isDecoderInitialized == MA_TRUE); - MA_ASSERT(pageIndex == 0 || pageIndex == 1); - - return ma_offset_ptr(pDataStream->pPageData, ((ma_resource_manager_data_stream_get_page_size_in_frames(pDataStream) * pageIndex) + relativeCursor) * ma_get_bytes_per_frame(pDataStream->decoder.outputFormat, pDataStream->decoder.outputChannels)); -} - -static void ma_resource_manager_data_stream_fill_page(ma_resource_manager_data_stream* pDataStream, ma_uint32 pageIndex) -{ - ma_result result = MA_SUCCESS; - ma_uint64 pageSizeInFrames; - ma_uint64 totalFramesReadForThisPage = 0; - void* pPageData = ma_resource_manager_data_stream_get_page_data_pointer(pDataStream, pageIndex, 0); - - pageSizeInFrames = ma_resource_manager_data_stream_get_page_size_in_frames(pDataStream); - - /* The decoder needs to inherit the stream's looping and range state. */ - { - ma_uint64 rangeBeg; - ma_uint64 rangeEnd; - ma_uint64 loopPointBeg; - ma_uint64 loopPointEnd; - - ma_data_source_set_looping(&pDataStream->decoder, ma_resource_manager_data_stream_is_looping(pDataStream)); - - ma_data_source_get_range_in_pcm_frames(pDataStream, &rangeBeg, &rangeEnd); - ma_data_source_set_range_in_pcm_frames(&pDataStream->decoder, rangeBeg, rangeEnd); - - ma_data_source_get_loop_point_in_pcm_frames(pDataStream, &loopPointBeg, &loopPointEnd); - ma_data_source_set_loop_point_in_pcm_frames(&pDataStream->decoder, loopPointBeg, loopPointEnd); - } - - /* Just read straight from the decoder. It will deal with ranges and looping for us. */ - result = ma_data_source_read_pcm_frames(&pDataStream->decoder, pPageData, pageSizeInFrames, &totalFramesReadForThisPage); - if (result == MA_AT_END || totalFramesReadForThisPage < pageSizeInFrames) { - ma_atomic_exchange_32(&pDataStream->isDecoderAtEnd, MA_TRUE); - } - - ma_atomic_exchange_32(&pDataStream->pageFrameCount[pageIndex], (ma_uint32)totalFramesReadForThisPage); - ma_atomic_exchange_32(&pDataStream->isPageValid[pageIndex], MA_TRUE); -} - -static void ma_resource_manager_data_stream_fill_pages(ma_resource_manager_data_stream* pDataStream) -{ - ma_uint32 iPage; - - MA_ASSERT(pDataStream != NULL); - - for (iPage = 0; iPage < 2; iPage += 1) { - ma_resource_manager_data_stream_fill_page(pDataStream, iPage); - } -} - - -static ma_result ma_resource_manager_data_stream_map(ma_resource_manager_data_stream* pDataStream, void** ppFramesOut, ma_uint64* pFrameCount) -{ - ma_uint64 framesAvailable; - ma_uint64 frameCount = 0; - - /* We cannot be using the data source after it's been uninitialized. */ - MA_ASSERT(ma_resource_manager_data_stream_result(pDataStream) != MA_UNAVAILABLE); - - if (pFrameCount != NULL) { - frameCount = *pFrameCount; - *pFrameCount = 0; - } - if (ppFramesOut != NULL) { - *ppFramesOut = NULL; - } - - if (pDataStream == NULL || ppFramesOut == NULL || pFrameCount == NULL) { - return MA_INVALID_ARGS; - } - - if (ma_resource_manager_data_stream_result(pDataStream) != MA_SUCCESS) { - return MA_INVALID_OPERATION; - } - - /* Don't attempt to read while we're in the middle of seeking. Tell the caller that we're busy. */ - if (ma_resource_manager_data_stream_seek_counter(pDataStream) > 0) { - return MA_BUSY; - } - - /* If the page we're on is invalid it means we've caught up to the job thread. */ - if (ma_atomic_load_32(&pDataStream->isPageValid[pDataStream->currentPageIndex]) == MA_FALSE) { - framesAvailable = 0; - } else { - /* - The page we're on is valid so we must have some frames available. We need to make sure that we don't overflow into the next page, even if it's valid. The reason is - that the unmap process will only post an update for one page at a time. Keeping mapping tied to page boundaries makes this simpler. - */ - ma_uint32 currentPageFrameCount = ma_atomic_load_32(&pDataStream->pageFrameCount[pDataStream->currentPageIndex]); - MA_ASSERT(currentPageFrameCount >= pDataStream->relativeCursor); - - framesAvailable = currentPageFrameCount - pDataStream->relativeCursor; - } - - /* If there's no frames available and the result is set to MA_AT_END we need to return MA_AT_END. */ - if (framesAvailable == 0) { - if (ma_resource_manager_data_stream_is_decoder_at_end(pDataStream)) { - return MA_AT_END; - } else { - return MA_BUSY; /* There are no frames available, but we're not marked as EOF so we might have caught up to the job thread. Need to return MA_BUSY and wait for more data. */ - } - } - - MA_ASSERT(framesAvailable > 0); - - if (frameCount > framesAvailable) { - frameCount = framesAvailable; - } - - *ppFramesOut = ma_resource_manager_data_stream_get_page_data_pointer(pDataStream, pDataStream->currentPageIndex, pDataStream->relativeCursor); - *pFrameCount = frameCount; - - return MA_SUCCESS; -} - -static ma_result ma_resource_manager_data_stream_unmap(ma_resource_manager_data_stream* pDataStream, ma_uint64 frameCount) -{ - ma_uint32 newRelativeCursor; - ma_uint32 pageSizeInFrames; - ma_job job; - - /* We cannot be using the data source after it's been uninitialized. */ - MA_ASSERT(ma_resource_manager_data_stream_result(pDataStream) != MA_UNAVAILABLE); - - if (pDataStream == NULL) { - return MA_INVALID_ARGS; - } - - if (ma_resource_manager_data_stream_result(pDataStream) != MA_SUCCESS) { - return MA_INVALID_OPERATION; - } - - /* The frame count should always fit inside a 32-bit integer. */ - if (frameCount > 0xFFFFFFFF) { - return MA_INVALID_ARGS; - } - - pageSizeInFrames = ma_resource_manager_data_stream_get_page_size_in_frames(pDataStream); - - /* The absolute cursor needs to be updated for ma_resource_manager_data_stream_get_cursor_in_pcm_frames(). */ - ma_resource_manager_data_stream_set_absolute_cursor(pDataStream, ma_atomic_load_64(&pDataStream->absoluteCursor) + frameCount); - - /* Here is where we need to check if we need to load a new page, and if so, post a job to load it. */ - newRelativeCursor = pDataStream->relativeCursor + (ma_uint32)frameCount; - - /* If the new cursor has flowed over to the next page we need to mark the old one as invalid and post an event for it. */ - if (newRelativeCursor >= pageSizeInFrames) { - newRelativeCursor -= pageSizeInFrames; - - /* Here is where we post the job start decoding. */ - job = ma_job_init(MA_JOB_TYPE_RESOURCE_MANAGER_PAGE_DATA_STREAM); - job.order = ma_resource_manager_data_stream_next_execution_order(pDataStream); - job.data.resourceManager.pageDataStream.pDataStream = pDataStream; - job.data.resourceManager.pageDataStream.pageIndex = pDataStream->currentPageIndex; - - /* The page needs to be marked as invalid so that the public API doesn't try reading from it. */ - ma_atomic_exchange_32(&pDataStream->isPageValid[pDataStream->currentPageIndex], MA_FALSE); - - /* Before posting the job we need to make sure we set some state. */ - pDataStream->relativeCursor = newRelativeCursor; - pDataStream->currentPageIndex = (pDataStream->currentPageIndex + 1) & 0x01; - return ma_resource_manager_post_job(pDataStream->pResourceManager, &job); - } else { - /* We haven't moved into a new page so we can just move the cursor forward. */ - pDataStream->relativeCursor = newRelativeCursor; - return MA_SUCCESS; - } -} - - -MA_API ma_result ma_resource_manager_data_stream_read_pcm_frames(ma_resource_manager_data_stream* pDataStream, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead) -{ - ma_result result = MA_SUCCESS; - ma_uint64 totalFramesProcessed; - ma_format format; - ma_uint32 channels; - - /* Safety. */ - if (pFramesRead != NULL) { - *pFramesRead = 0; - } - - if (frameCount == 0) { - return MA_INVALID_ARGS; - } - - /* We cannot be using the data source after it's been uninitialized. */ - MA_ASSERT(ma_resource_manager_data_stream_result(pDataStream) != MA_UNAVAILABLE); - - if (pDataStream == NULL) { - return MA_INVALID_ARGS; - } - - if (ma_resource_manager_data_stream_result(pDataStream) != MA_SUCCESS) { - return MA_INVALID_OPERATION; - } - - /* Don't attempt to read while we're in the middle of seeking. Tell the caller that we're busy. */ - if (ma_resource_manager_data_stream_seek_counter(pDataStream) > 0) { - return MA_BUSY; - } - - ma_resource_manager_data_stream_get_data_format(pDataStream, &format, &channels, NULL, NULL, 0); - - /* Reading is implemented in terms of map/unmap. We need to run this in a loop because mapping is clamped against page boundaries. */ - totalFramesProcessed = 0; - while (totalFramesProcessed < frameCount) { - void* pMappedFrames; - ma_uint64 mappedFrameCount; - - mappedFrameCount = frameCount - totalFramesProcessed; - result = ma_resource_manager_data_stream_map(pDataStream, &pMappedFrames, &mappedFrameCount); - if (result != MA_SUCCESS) { - break; - } - - /* Copy the mapped data to the output buffer if we have one. It's allowed for pFramesOut to be NULL in which case a relative forward seek is performed. */ - if (pFramesOut != NULL) { - ma_copy_pcm_frames(ma_offset_pcm_frames_ptr(pFramesOut, totalFramesProcessed, format, channels), pMappedFrames, mappedFrameCount, format, channels); - } - - totalFramesProcessed += mappedFrameCount; - - result = ma_resource_manager_data_stream_unmap(pDataStream, mappedFrameCount); - if (result != MA_SUCCESS) { - break; /* This is really bad - will only get an error here if we failed to post a job to the queue for loading the next page. */ - } - } - - if (pFramesRead != NULL) { - *pFramesRead = totalFramesProcessed; - } - - if (result == MA_SUCCESS && totalFramesProcessed == 0) { - result = MA_AT_END; - } - - return result; -} - -MA_API ma_result ma_resource_manager_data_stream_seek_to_pcm_frame(ma_resource_manager_data_stream* pDataStream, ma_uint64 frameIndex) -{ - ma_job job; - ma_result streamResult; - - streamResult = ma_resource_manager_data_stream_result(pDataStream); - - /* We cannot be using the data source after it's been uninitialized. */ - MA_ASSERT(streamResult != MA_UNAVAILABLE); - - if (pDataStream == NULL) { - return MA_INVALID_ARGS; - } - - if (streamResult != MA_SUCCESS && streamResult != MA_BUSY) { - return MA_INVALID_OPERATION; - } - - /* If we're not already seeking and we're sitting on the same frame, just make this a no-op. */ - if (ma_atomic_load_32(&pDataStream->seekCounter) == 0) { - if (ma_atomic_load_64(&pDataStream->absoluteCursor) == frameIndex) { - return MA_SUCCESS; - } - } - - - /* Increment the seek counter first to indicate to read_paged_pcm_frames() and map_paged_pcm_frames() that we are in the middle of a seek and MA_BUSY should be returned. */ - ma_atomic_fetch_add_32(&pDataStream->seekCounter, 1); - - /* Update the absolute cursor so that ma_resource_manager_data_stream_get_cursor_in_pcm_frames() returns the new position. */ - ma_resource_manager_data_stream_set_absolute_cursor(pDataStream, frameIndex); - - /* - We need to clear our currently loaded pages so that the stream starts playback from the new seek point as soon as possible. These are for the purpose of the public - API and will be ignored by the seek job. The seek job will operate on the assumption that both pages have been marked as invalid and the cursor is at the start of - the first page. - */ - pDataStream->relativeCursor = 0; - pDataStream->currentPageIndex = 0; - ma_atomic_exchange_32(&pDataStream->isPageValid[0], MA_FALSE); - ma_atomic_exchange_32(&pDataStream->isPageValid[1], MA_FALSE); - - /* Make sure the data stream is not marked as at the end or else if we seek in response to hitting the end, we won't be able to read any more data. */ - ma_atomic_exchange_32(&pDataStream->isDecoderAtEnd, MA_FALSE); - - /* - The public API is not allowed to touch the internal decoder so we need to use a job to perform the seek. When seeking, the job thread will assume both pages - are invalid and any content contained within them will be discarded and replaced with newly decoded data. - */ - job = ma_job_init(MA_JOB_TYPE_RESOURCE_MANAGER_SEEK_DATA_STREAM); - job.order = ma_resource_manager_data_stream_next_execution_order(pDataStream); - job.data.resourceManager.seekDataStream.pDataStream = pDataStream; - job.data.resourceManager.seekDataStream.frameIndex = frameIndex; - return ma_resource_manager_post_job(pDataStream->pResourceManager, &job); -} - -MA_API ma_result ma_resource_manager_data_stream_get_data_format(ma_resource_manager_data_stream* pDataStream, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate, ma_channel* pChannelMap, size_t channelMapCap) -{ - /* We cannot be using the data source after it's been uninitialized. */ - MA_ASSERT(ma_resource_manager_data_stream_result(pDataStream) != MA_UNAVAILABLE); - - if (pFormat != NULL) { - *pFormat = ma_format_unknown; - } - - if (pChannels != NULL) { - *pChannels = 0; - } - - if (pSampleRate != NULL) { - *pSampleRate = 0; - } - - if (pChannelMap != NULL) { - MA_ZERO_MEMORY(pChannelMap, sizeof(*pChannelMap) * channelMapCap); - } - - if (pDataStream == NULL) { - return MA_INVALID_ARGS; - } - - if (ma_resource_manager_data_stream_result(pDataStream) != MA_SUCCESS) { - return MA_INVALID_OPERATION; - } - - /* - We're being a little bit naughty here and accessing the internal decoder from the public API. The output data format is constant, and we've defined this function - such that the application is responsible for ensuring it's not called while uninitializing so it should be safe. - */ - return ma_data_source_get_data_format(&pDataStream->decoder, pFormat, pChannels, pSampleRate, pChannelMap, channelMapCap); -} - -MA_API ma_result ma_resource_manager_data_stream_get_cursor_in_pcm_frames(ma_resource_manager_data_stream* pDataStream, ma_uint64* pCursor) -{ - ma_result result; - - if (pCursor == NULL) { - return MA_INVALID_ARGS; - } - - *pCursor = 0; - - /* We cannot be using the data source after it's been uninitialized. */ - MA_ASSERT(ma_resource_manager_data_stream_result(pDataStream) != MA_UNAVAILABLE); - - if (pDataStream == NULL) { - return MA_INVALID_ARGS; - } - - /* - If the stream is in an erroneous state we need to return an invalid operation. We can allow - this to be called when the data stream is in a busy state because the caller may have asked - for an initial seek position and it's convenient to return that as the cursor position. - */ - result = ma_resource_manager_data_stream_result(pDataStream); - if (result != MA_SUCCESS && result != MA_BUSY) { - return MA_INVALID_OPERATION; - } - - *pCursor = ma_atomic_load_64(&pDataStream->absoluteCursor); - - return MA_SUCCESS; -} - -MA_API ma_result ma_resource_manager_data_stream_get_length_in_pcm_frames(ma_resource_manager_data_stream* pDataStream, ma_uint64* pLength) -{ - ma_result streamResult; - - if (pLength == NULL) { - return MA_INVALID_ARGS; - } - - *pLength = 0; - - streamResult = ma_resource_manager_data_stream_result(pDataStream); - - /* We cannot be using the data source after it's been uninitialized. */ - MA_ASSERT(streamResult != MA_UNAVAILABLE); - - if (pDataStream == NULL) { - return MA_INVALID_ARGS; - } - - if (streamResult != MA_SUCCESS) { - return streamResult; - } - - /* - We most definitely do not want to be calling ma_decoder_get_length_in_pcm_frames() directly. Instead we want to use a cached value that we - calculated when we initialized it on the job thread. - */ - *pLength = pDataStream->totalLengthInPCMFrames; - if (*pLength == 0) { - return MA_NOT_IMPLEMENTED; /* Some decoders may not have a known length. */ - } - - return MA_SUCCESS; -} - -MA_API ma_result ma_resource_manager_data_stream_result(const ma_resource_manager_data_stream* pDataStream) -{ - if (pDataStream == NULL) { - return MA_INVALID_ARGS; - } - - return (ma_result)ma_atomic_load_i32(&pDataStream->result); -} - -MA_API ma_result ma_resource_manager_data_stream_set_looping(ma_resource_manager_data_stream* pDataStream, ma_bool32 isLooping) -{ - return ma_data_source_set_looping(pDataStream, isLooping); -} - -MA_API ma_bool32 ma_resource_manager_data_stream_is_looping(const ma_resource_manager_data_stream* pDataStream) -{ - if (pDataStream == NULL) { - return MA_FALSE; - } - - return ma_atomic_load_32((ma_bool32*)&pDataStream->isLooping); /* Naughty const-cast. Value won't change from here in practice (maybe from another thread). */ -} - -MA_API ma_result ma_resource_manager_data_stream_get_available_frames(ma_resource_manager_data_stream* pDataStream, ma_uint64* pAvailableFrames) -{ - ma_uint32 pageIndex0; - ma_uint32 pageIndex1; - ma_uint32 relativeCursor; - ma_uint64 availableFrames; - - if (pAvailableFrames == NULL) { - return MA_INVALID_ARGS; - } - - *pAvailableFrames = 0; - - if (pDataStream == NULL) { - return MA_INVALID_ARGS; - } - - pageIndex0 = pDataStream->currentPageIndex; - pageIndex1 = (pDataStream->currentPageIndex + 1) & 0x01; - relativeCursor = pDataStream->relativeCursor; - - availableFrames = 0; - if (ma_atomic_load_32(&pDataStream->isPageValid[pageIndex0])) { - availableFrames += ma_atomic_load_32(&pDataStream->pageFrameCount[pageIndex0]) - relativeCursor; - if (ma_atomic_load_32(&pDataStream->isPageValid[pageIndex1])) { - availableFrames += ma_atomic_load_32(&pDataStream->pageFrameCount[pageIndex1]); - } - } - - *pAvailableFrames = availableFrames; - return MA_SUCCESS; -} - - -static ma_result ma_resource_manager_data_source_preinit(ma_resource_manager* pResourceManager, const ma_resource_manager_data_source_config* pConfig, ma_resource_manager_data_source* pDataSource) -{ - if (pDataSource == NULL) { - return MA_INVALID_ARGS; - } - - MA_ZERO_OBJECT(pDataSource); - - if (pConfig == NULL) { - return MA_INVALID_ARGS; - } - - if (pResourceManager == NULL) { - return MA_INVALID_ARGS; - } - - pDataSource->flags = pConfig->flags; - if (pConfig->isLooping) { - pDataSource->flags |= MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_LOOPING; - } - - return MA_SUCCESS; -} - -MA_API ma_result ma_resource_manager_data_source_init_ex(ma_resource_manager* pResourceManager, const ma_resource_manager_data_source_config* pConfig, ma_resource_manager_data_source* pDataSource) -{ - ma_result result; - - result = ma_resource_manager_data_source_preinit(pResourceManager, pConfig, pDataSource); - if (result != MA_SUCCESS) { - return result; - } - - /* The data source itself is just a data stream or a data buffer. */ - if ((pConfig->flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_STREAM) != 0) { - return ma_resource_manager_data_stream_init_ex(pResourceManager, pConfig, &pDataSource->backend.stream); - } else { - return ma_resource_manager_data_buffer_init_ex(pResourceManager, pConfig, &pDataSource->backend.buffer); - } -} - -MA_API ma_result ma_resource_manager_data_source_init(ma_resource_manager* pResourceManager, const char* pName, ma_uint32 flags, const ma_resource_manager_pipeline_notifications* pNotifications, ma_resource_manager_data_source* pDataSource) -{ - ma_resource_manager_data_source_config config; - - config = ma_resource_manager_data_source_config_init(); - config.pFilePath = pName; - config.flags = flags; - config.pNotifications = pNotifications; - - return ma_resource_manager_data_source_init_ex(pResourceManager, &config, pDataSource); -} - -MA_API ma_result ma_resource_manager_data_source_init_w(ma_resource_manager* pResourceManager, const wchar_t* pName, ma_uint32 flags, const ma_resource_manager_pipeline_notifications* pNotifications, ma_resource_manager_data_source* pDataSource) -{ - ma_resource_manager_data_source_config config; - - config = ma_resource_manager_data_source_config_init(); - config.pFilePathW = pName; - config.flags = flags; - config.pNotifications = pNotifications; - - return ma_resource_manager_data_source_init_ex(pResourceManager, &config, pDataSource); -} - -MA_API ma_result ma_resource_manager_data_source_init_copy(ma_resource_manager* pResourceManager, const ma_resource_manager_data_source* pExistingDataSource, ma_resource_manager_data_source* pDataSource) -{ - ma_result result; - ma_resource_manager_data_source_config config; - - if (pExistingDataSource == NULL) { - return MA_INVALID_ARGS; - } - - config = ma_resource_manager_data_source_config_init(); - config.flags = pExistingDataSource->flags; - - result = ma_resource_manager_data_source_preinit(pResourceManager, &config, pDataSource); - if (result != MA_SUCCESS) { - return result; - } - - /* Copying can only be done from data buffers. Streams cannot be copied. */ - if ((pExistingDataSource->flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_STREAM) != 0) { - return MA_INVALID_OPERATION; - } - - return ma_resource_manager_data_buffer_init_copy(pResourceManager, &pExistingDataSource->backend.buffer, &pDataSource->backend.buffer); -} - -MA_API ma_result ma_resource_manager_data_source_uninit(ma_resource_manager_data_source* pDataSource) -{ - if (pDataSource == NULL) { - return MA_INVALID_ARGS; - } - - /* All we need to is uninitialize the underlying data buffer or data stream. */ - if ((pDataSource->flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_STREAM) != 0) { - return ma_resource_manager_data_stream_uninit(&pDataSource->backend.stream); - } else { - return ma_resource_manager_data_buffer_uninit(&pDataSource->backend.buffer); - } -} - -MA_API ma_result ma_resource_manager_data_source_read_pcm_frames(ma_resource_manager_data_source* pDataSource, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead) -{ - /* Safety. */ - if (pFramesRead != NULL) { - *pFramesRead = 0; - } - - if (pDataSource == NULL) { - return MA_INVALID_ARGS; - } - - if ((pDataSource->flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_STREAM) != 0) { - return ma_resource_manager_data_stream_read_pcm_frames(&pDataSource->backend.stream, pFramesOut, frameCount, pFramesRead); - } else { - return ma_resource_manager_data_buffer_read_pcm_frames(&pDataSource->backend.buffer, pFramesOut, frameCount, pFramesRead); - } -} - -MA_API ma_result ma_resource_manager_data_source_seek_to_pcm_frame(ma_resource_manager_data_source* pDataSource, ma_uint64 frameIndex) -{ - if (pDataSource == NULL) { - return MA_INVALID_ARGS; - } - - if ((pDataSource->flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_STREAM) != 0) { - return ma_resource_manager_data_stream_seek_to_pcm_frame(&pDataSource->backend.stream, frameIndex); - } else { - return ma_resource_manager_data_buffer_seek_to_pcm_frame(&pDataSource->backend.buffer, frameIndex); - } -} - -MA_API ma_result ma_resource_manager_data_source_map(ma_resource_manager_data_source* pDataSource, void** ppFramesOut, ma_uint64* pFrameCount) -{ - if (pDataSource == NULL) { - return MA_INVALID_ARGS; - } - - if ((pDataSource->flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_STREAM) != 0) { - return ma_resource_manager_data_stream_map(&pDataSource->backend.stream, ppFramesOut, pFrameCount); - } else { - return MA_NOT_IMPLEMENTED; /* Mapping not supported with data buffers. */ - } -} - -MA_API ma_result ma_resource_manager_data_source_unmap(ma_resource_manager_data_source* pDataSource, ma_uint64 frameCount) -{ - if (pDataSource == NULL) { - return MA_INVALID_ARGS; - } - - if ((pDataSource->flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_STREAM) != 0) { - return ma_resource_manager_data_stream_unmap(&pDataSource->backend.stream, frameCount); - } else { - return MA_NOT_IMPLEMENTED; /* Mapping not supported with data buffers. */ - } -} - -MA_API ma_result ma_resource_manager_data_source_get_data_format(ma_resource_manager_data_source* pDataSource, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate, ma_channel* pChannelMap, size_t channelMapCap) -{ - if (pDataSource == NULL) { - return MA_INVALID_ARGS; - } - - if ((pDataSource->flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_STREAM) != 0) { - return ma_resource_manager_data_stream_get_data_format(&pDataSource->backend.stream, pFormat, pChannels, pSampleRate, pChannelMap, channelMapCap); - } else { - return ma_resource_manager_data_buffer_get_data_format(&pDataSource->backend.buffer, pFormat, pChannels, pSampleRate, pChannelMap, channelMapCap); - } -} - -MA_API ma_result ma_resource_manager_data_source_get_cursor_in_pcm_frames(ma_resource_manager_data_source* pDataSource, ma_uint64* pCursor) -{ - if (pDataSource == NULL) { - return MA_INVALID_ARGS; - } - - if ((pDataSource->flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_STREAM) != 0) { - return ma_resource_manager_data_stream_get_cursor_in_pcm_frames(&pDataSource->backend.stream, pCursor); - } else { - return ma_resource_manager_data_buffer_get_cursor_in_pcm_frames(&pDataSource->backend.buffer, pCursor); - } -} - -MA_API ma_result ma_resource_manager_data_source_get_length_in_pcm_frames(ma_resource_manager_data_source* pDataSource, ma_uint64* pLength) -{ - if (pDataSource == NULL) { - return MA_INVALID_ARGS; - } - - if ((pDataSource->flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_STREAM) != 0) { - return ma_resource_manager_data_stream_get_length_in_pcm_frames(&pDataSource->backend.stream, pLength); - } else { - return ma_resource_manager_data_buffer_get_length_in_pcm_frames(&pDataSource->backend.buffer, pLength); - } -} - -MA_API ma_result ma_resource_manager_data_source_result(const ma_resource_manager_data_source* pDataSource) -{ - if (pDataSource == NULL) { - return MA_INVALID_ARGS; - } - - if ((pDataSource->flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_STREAM) != 0) { - return ma_resource_manager_data_stream_result(&pDataSource->backend.stream); - } else { - return ma_resource_manager_data_buffer_result(&pDataSource->backend.buffer); - } -} - -MA_API ma_result ma_resource_manager_data_source_set_looping(ma_resource_manager_data_source* pDataSource, ma_bool32 isLooping) -{ - if (pDataSource == NULL) { - return MA_INVALID_ARGS; - } - - if ((pDataSource->flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_STREAM) != 0) { - return ma_resource_manager_data_stream_set_looping(&pDataSource->backend.stream, isLooping); - } else { - return ma_resource_manager_data_buffer_set_looping(&pDataSource->backend.buffer, isLooping); - } -} - -MA_API ma_bool32 ma_resource_manager_data_source_is_looping(const ma_resource_manager_data_source* pDataSource) -{ - if (pDataSource == NULL) { - return MA_FALSE; - } - - if ((pDataSource->flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_STREAM) != 0) { - return ma_resource_manager_data_stream_is_looping(&pDataSource->backend.stream); - } else { - return ma_resource_manager_data_buffer_is_looping(&pDataSource->backend.buffer); - } -} - -MA_API ma_result ma_resource_manager_data_source_get_available_frames(ma_resource_manager_data_source* pDataSource, ma_uint64* pAvailableFrames) -{ - if (pAvailableFrames == NULL) { - return MA_INVALID_ARGS; - } - - *pAvailableFrames = 0; - - if (pDataSource == NULL) { - return MA_INVALID_ARGS; - } - - if ((pDataSource->flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_STREAM) != 0) { - return ma_resource_manager_data_stream_get_available_frames(&pDataSource->backend.stream, pAvailableFrames); - } else { - return ma_resource_manager_data_buffer_get_available_frames(&pDataSource->backend.buffer, pAvailableFrames); - } -} - - -MA_API ma_result ma_resource_manager_post_job(ma_resource_manager* pResourceManager, const ma_job* pJob) -{ - if (pResourceManager == NULL) { - return MA_INVALID_ARGS; - } - - return ma_job_queue_post(&pResourceManager->jobQueue, pJob); -} - -MA_API ma_result ma_resource_manager_post_job_quit(ma_resource_manager* pResourceManager) -{ - ma_job job = ma_job_init(MA_JOB_TYPE_QUIT); - return ma_resource_manager_post_job(pResourceManager, &job); -} - -MA_API ma_result ma_resource_manager_next_job(ma_resource_manager* pResourceManager, ma_job* pJob) -{ - if (pResourceManager == NULL) { - return MA_INVALID_ARGS; - } - - return ma_job_queue_next(&pResourceManager->jobQueue, pJob); -} - - -static ma_result ma_job_process__resource_manager__load_data_buffer_node(ma_job* pJob) -{ - ma_result result = MA_SUCCESS; - ma_resource_manager* pResourceManager; - ma_resource_manager_data_buffer_node* pDataBufferNode; - - MA_ASSERT(pJob != NULL); - - pResourceManager = (ma_resource_manager*)pJob->data.resourceManager.loadDataBufferNode.pResourceManager; - MA_ASSERT(pResourceManager != NULL); - - pDataBufferNode = (ma_resource_manager_data_buffer_node*)pJob->data.resourceManager.loadDataBufferNode.pDataBufferNode; - MA_ASSERT(pDataBufferNode != NULL); - MA_ASSERT(pDataBufferNode->isDataOwnedByResourceManager == MA_TRUE); /* The data should always be owned by the resource manager. */ - - /* The data buffer is not getting deleted, but we may be getting executed out of order. If so, we need to push the job back onto the queue and return. */ - if (pJob->order != ma_atomic_load_32(&pDataBufferNode->executionPointer)) { - return ma_resource_manager_post_job(pResourceManager, pJob); /* Attempting to execute out of order. Probably interleaved with a MA_JOB_TYPE_RESOURCE_MANAGER_FREE_DATA_BUFFER job. */ - } - - /* First thing we need to do is check whether or not the data buffer is getting deleted. If so we just abort. */ - if (ma_resource_manager_data_buffer_node_result(pDataBufferNode) != MA_BUSY) { - result = ma_resource_manager_data_buffer_node_result(pDataBufferNode); /* The data buffer may be getting deleted before it's even been loaded. */ - goto done; - } - - /* - We're ready to start loading. Essentially what we're doing here is initializing the data supply - of the node. Once this is complete, data buffers can have their connectors initialized which - will allow then to have audio data read from them. - - Note that when the data supply type has been moved away from "unknown", that is when other threads - will determine that the node is available for data delivery and the data buffer connectors can be - initialized. Therefore, it's important that it is set after the data supply has been initialized. - */ - if ((pJob->data.resourceManager.loadDataBufferNode.flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_DECODE) != 0) { - /* - Decoding. This is the complex case because we're not going to be doing the entire decoding - process here. Instead it's going to be split of multiple jobs and loaded in pages. The - reason for this is to evenly distribute decoding time across multiple sounds, rather than - having one huge sound hog all the available processing resources. - - The first thing we do is initialize a decoder. This is allocated on the heap and is passed - around to the paging jobs. When the last paging job has completed it's processing, it'll - free the decoder for us. - - This job does not do any actual decoding. It instead just posts a PAGE_DATA_BUFFER_NODE job - which is where the actual decoding work will be done. However, once this job is complete, - the node will be in a state where data buffer connectors can be initialized. - */ - ma_decoder* pDecoder; /* <-- Free'd on the last page decode. */ - ma_job pageDataBufferNodeJob; - - /* Allocate the decoder by initializing a decoded data supply. */ - result = ma_resource_manager_data_buffer_node_init_supply_decoded(pResourceManager, pDataBufferNode, pJob->data.resourceManager.loadDataBufferNode.pFilePath, pJob->data.resourceManager.loadDataBufferNode.pFilePathW, pJob->data.resourceManager.loadDataBufferNode.flags, &pDecoder); - - /* - Don't ever propagate an MA_BUSY result code or else the resource manager will think the - node is just busy decoding rather than in an error state. This should never happen, but - including this logic for safety just in case. - */ - if (result == MA_BUSY) { - result = MA_ERROR; - } - - if (result != MA_SUCCESS) { - if (pJob->data.resourceManager.loadDataBufferNode.pFilePath != NULL) { - ma_log_postf(ma_resource_manager_get_log(pResourceManager), MA_LOG_LEVEL_WARNING, "Failed to initialize data supply for \"%s\". %s.\n", pJob->data.resourceManager.loadDataBufferNode.pFilePath, ma_result_description(result)); - } else { - #if (defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L) || defined(_MSC_VER) - ma_log_postf(ma_resource_manager_get_log(pResourceManager), MA_LOG_LEVEL_WARNING, "Failed to initialize data supply for \"%ls\", %s.\n", pJob->data.resourceManager.loadDataBufferNode.pFilePathW, ma_result_description(result)); - #endif - } - - goto done; - } - - /* - At this point the node's data supply is initialized and other threads can start initializing - their data buffer connectors. However, no data will actually be available until we start to - actually decode it. To do this, we need to post a paging job which is where the decoding - work is done. - - Note that if an error occurred at an earlier point, this section will have been skipped. - */ - pageDataBufferNodeJob = ma_job_init(MA_JOB_TYPE_RESOURCE_MANAGER_PAGE_DATA_BUFFER_NODE); - pageDataBufferNodeJob.order = ma_resource_manager_data_buffer_node_next_execution_order(pDataBufferNode); - pageDataBufferNodeJob.data.resourceManager.pageDataBufferNode.pResourceManager = pResourceManager; - pageDataBufferNodeJob.data.resourceManager.pageDataBufferNode.pDataBufferNode = pDataBufferNode; - pageDataBufferNodeJob.data.resourceManager.pageDataBufferNode.pDecoder = pDecoder; - pageDataBufferNodeJob.data.resourceManager.pageDataBufferNode.pDoneNotification = pJob->data.resourceManager.loadDataBufferNode.pDoneNotification; - pageDataBufferNodeJob.data.resourceManager.pageDataBufferNode.pDoneFence = pJob->data.resourceManager.loadDataBufferNode.pDoneFence; - - /* The job has been set up so it can now be posted. */ - result = ma_resource_manager_post_job(pResourceManager, &pageDataBufferNodeJob); - - /* - When we get here, we want to make sure the result code is set to MA_BUSY. The reason for - this is that the result will be copied over to the node's internal result variable. In - this case, since the decoding is still in-progress, we need to make sure the result code - is set to MA_BUSY. - */ - if (result != MA_SUCCESS) { - ma_log_postf(ma_resource_manager_get_log(pResourceManager), MA_LOG_LEVEL_ERROR, "Failed to post MA_JOB_TYPE_RESOURCE_MANAGER_PAGE_DATA_BUFFER_NODE job. %s\n", ma_result_description(result)); - ma_decoder_uninit(pDecoder); - ma_free(pDecoder, &pResourceManager->config.allocationCallbacks); - } else { - result = MA_BUSY; - } - } else { - /* No decoding. This is the simple case. We need only read the file content into memory and we're done. */ - result = ma_resource_manager_data_buffer_node_init_supply_encoded(pResourceManager, pDataBufferNode, pJob->data.resourceManager.loadDataBufferNode.pFilePath, pJob->data.resourceManager.loadDataBufferNode.pFilePathW); - } - - -done: - /* File paths are no longer needed. */ - ma_free(pJob->data.resourceManager.loadDataBufferNode.pFilePath, &pResourceManager->config.allocationCallbacks); - ma_free(pJob->data.resourceManager.loadDataBufferNode.pFilePathW, &pResourceManager->config.allocationCallbacks); - - /* - We need to set the result to at the very end to ensure no other threads try reading the data before we've fully initialized the object. Other threads - are going to be inspecting this variable to determine whether or not they're ready to read data. We can only change the result if it's set to MA_BUSY - because otherwise we may be changing away from an error code which would be bad. An example is if the application creates a data buffer, but then - immediately deletes it before we've got to this point. In this case, pDataBuffer->result will be MA_UNAVAILABLE, and setting it to MA_SUCCESS or any - other error code would cause the buffer to look like it's in a state that it's not. - */ - ma_atomic_compare_and_swap_i32(&pDataBufferNode->result, MA_BUSY, result); - - /* At this point initialization is complete and we can signal the notification if any. */ - if (pJob->data.resourceManager.loadDataBufferNode.pInitNotification != NULL) { - ma_async_notification_signal(pJob->data.resourceManager.loadDataBufferNode.pInitNotification); - } - if (pJob->data.resourceManager.loadDataBufferNode.pInitFence != NULL) { - ma_fence_release(pJob->data.resourceManager.loadDataBufferNode.pInitFence); - } - - /* If we have a success result it means we've fully loaded the buffer. This will happen in the non-decoding case. */ - if (result != MA_BUSY) { - if (pJob->data.resourceManager.loadDataBufferNode.pDoneNotification != NULL) { - ma_async_notification_signal(pJob->data.resourceManager.loadDataBufferNode.pDoneNotification); - } - if (pJob->data.resourceManager.loadDataBufferNode.pDoneFence != NULL) { - ma_fence_release(pJob->data.resourceManager.loadDataBufferNode.pDoneFence); - } - } - - /* Increment the node's execution pointer so that the next jobs can be processed. This is how we keep decoding of pages in-order. */ - ma_atomic_fetch_add_32(&pDataBufferNode->executionPointer, 1); - - /* A busy result should be considered successful from the point of view of the job system. */ - if (result == MA_BUSY) { - result = MA_SUCCESS; - } - - return result; -} - -static ma_result ma_job_process__resource_manager__free_data_buffer_node(ma_job* pJob) -{ - ma_resource_manager* pResourceManager; - ma_resource_manager_data_buffer_node* pDataBufferNode; - - MA_ASSERT(pJob != NULL); - - pResourceManager = (ma_resource_manager*)pJob->data.resourceManager.freeDataBufferNode.pResourceManager; - MA_ASSERT(pResourceManager != NULL); - - pDataBufferNode = (ma_resource_manager_data_buffer_node*)pJob->data.resourceManager.freeDataBufferNode.pDataBufferNode; - MA_ASSERT(pDataBufferNode != NULL); - - if (pJob->order != ma_atomic_load_32(&pDataBufferNode->executionPointer)) { - return ma_resource_manager_post_job(pResourceManager, pJob); /* Out of order. */ - } - - ma_resource_manager_data_buffer_node_free(pResourceManager, pDataBufferNode); - - /* The event needs to be signalled last. */ - if (pJob->data.resourceManager.freeDataBufferNode.pDoneNotification != NULL) { - ma_async_notification_signal(pJob->data.resourceManager.freeDataBufferNode.pDoneNotification); - } - - if (pJob->data.resourceManager.freeDataBufferNode.pDoneFence != NULL) { - ma_fence_release(pJob->data.resourceManager.freeDataBufferNode.pDoneFence); - } - - ma_atomic_fetch_add_32(&pDataBufferNode->executionPointer, 1); - return MA_SUCCESS; -} - -static ma_result ma_job_process__resource_manager__page_data_buffer_node(ma_job* pJob) -{ - ma_result result = MA_SUCCESS; - ma_resource_manager* pResourceManager; - ma_resource_manager_data_buffer_node* pDataBufferNode; - - MA_ASSERT(pJob != NULL); - - pResourceManager = (ma_resource_manager*)pJob->data.resourceManager.pageDataBufferNode.pResourceManager; - MA_ASSERT(pResourceManager != NULL); - - pDataBufferNode = (ma_resource_manager_data_buffer_node*)pJob->data.resourceManager.pageDataBufferNode.pDataBufferNode; - MA_ASSERT(pDataBufferNode != NULL); - - if (pJob->order != ma_atomic_load_32(&pDataBufferNode->executionPointer)) { - return ma_resource_manager_post_job(pResourceManager, pJob); /* Out of order. */ - } - - /* Don't do any more decoding if the data buffer has started the uninitialization process. */ - result = ma_resource_manager_data_buffer_node_result(pDataBufferNode); - if (result != MA_BUSY) { - goto done; - } - - /* We're ready to decode the next page. */ - result = ma_resource_manager_data_buffer_node_decode_next_page(pResourceManager, pDataBufferNode, (ma_decoder*)pJob->data.resourceManager.pageDataBufferNode.pDecoder); - - /* - If we have a success code by this point, we want to post another job. We're going to set the - result back to MA_BUSY to make it clear that there's still more to load. - */ - if (result == MA_SUCCESS) { - ma_job newJob; - newJob = *pJob; /* Everything is the same as the input job, except the execution order. */ - newJob.order = ma_resource_manager_data_buffer_node_next_execution_order(pDataBufferNode); /* We need a fresh execution order. */ - - result = ma_resource_manager_post_job(pResourceManager, &newJob); - - /* Since the sound isn't yet fully decoded we want the status to be set to busy. */ - if (result == MA_SUCCESS) { - result = MA_BUSY; - } - } - -done: - /* If there's still more to decode the result will be set to MA_BUSY. Otherwise we can free the decoder. */ - if (result != MA_BUSY) { - ma_decoder_uninit((ma_decoder*)pJob->data.resourceManager.pageDataBufferNode.pDecoder); - ma_free(pJob->data.resourceManager.pageDataBufferNode.pDecoder, &pResourceManager->config.allocationCallbacks); - } - - /* If we reached the end we need to treat it as successful. */ - if (result == MA_AT_END) { - result = MA_SUCCESS; - } - - /* Make sure we set the result of node in case some error occurred. */ - ma_atomic_compare_and_swap_i32(&pDataBufferNode->result, MA_BUSY, result); - - /* Signal the notification after setting the result in case the notification callback wants to inspect the result code. */ - if (result != MA_BUSY) { - if (pJob->data.resourceManager.pageDataBufferNode.pDoneNotification != NULL) { - ma_async_notification_signal(pJob->data.resourceManager.pageDataBufferNode.pDoneNotification); - } - - if (pJob->data.resourceManager.pageDataBufferNode.pDoneFence != NULL) { - ma_fence_release(pJob->data.resourceManager.pageDataBufferNode.pDoneFence); - } - } - - ma_atomic_fetch_add_32(&pDataBufferNode->executionPointer, 1); - return result; -} - - -static ma_result ma_job_process__resource_manager__load_data_buffer(ma_job* pJob) -{ - ma_result result = MA_SUCCESS; - ma_resource_manager* pResourceManager; - ma_resource_manager_data_buffer* pDataBuffer; - ma_resource_manager_data_supply_type dataSupplyType = ma_resource_manager_data_supply_type_unknown; - ma_bool32 isConnectorInitialized = MA_FALSE; - - /* - All we're doing here is checking if the node has finished loading. If not, we just re-post the job - and keep waiting. Otherwise we increment the execution counter and set the buffer's result code. - */ - MA_ASSERT(pJob != NULL); - - pDataBuffer = (ma_resource_manager_data_buffer*)pJob->data.resourceManager.loadDataBuffer.pDataBuffer; - MA_ASSERT(pDataBuffer != NULL); - - pResourceManager = pDataBuffer->pResourceManager; - - if (pJob->order != ma_atomic_load_32(&pDataBuffer->executionPointer)) { - return ma_resource_manager_post_job(pResourceManager, pJob); /* Attempting to execute out of order. Probably interleaved with a MA_JOB_TYPE_RESOURCE_MANAGER_FREE_DATA_BUFFER job. */ - } - - /* - First thing we need to do is check whether or not the data buffer is getting deleted. If so we - just abort, but making sure we increment the execution pointer. - */ - result = ma_resource_manager_data_buffer_result(pDataBuffer); - if (result != MA_BUSY) { - goto done; /* <-- This will ensure the execution pointer is incremented. */ - } else { - result = MA_SUCCESS; /* <-- Make sure this is reset. */ - (void)result; /* <-- This is to suppress a static analysis diagnostic about "result" not being used. But for safety when I do future maintenance I don't want to delete that assignment. */ - } - - /* Try initializing the connector if we haven't already. */ - isConnectorInitialized = ma_resource_manager_data_buffer_has_connector(pDataBuffer); - if (isConnectorInitialized == MA_FALSE) { - dataSupplyType = ma_resource_manager_data_buffer_node_get_data_supply_type(pDataBuffer->pNode); - - if (dataSupplyType != ma_resource_manager_data_supply_type_unknown) { - /* We can now initialize the connector. If this fails, we need to abort. It's very rare for this to fail. */ - ma_resource_manager_data_source_config dataSourceConfig; /* For setting initial looping state and range. */ - dataSourceConfig = ma_resource_manager_data_source_config_init(); - dataSourceConfig.rangeBegInPCMFrames = pJob->data.resourceManager.loadDataBuffer.rangeBegInPCMFrames; - dataSourceConfig.rangeEndInPCMFrames = pJob->data.resourceManager.loadDataBuffer.rangeEndInPCMFrames; - dataSourceConfig.loopPointBegInPCMFrames = pJob->data.resourceManager.loadDataBuffer.loopPointBegInPCMFrames; - dataSourceConfig.loopPointEndInPCMFrames = pJob->data.resourceManager.loadDataBuffer.loopPointEndInPCMFrames; - dataSourceConfig.isLooping = pJob->data.resourceManager.loadDataBuffer.isLooping; - - result = ma_resource_manager_data_buffer_init_connector(pDataBuffer, &dataSourceConfig, pJob->data.resourceManager.loadDataBuffer.pInitNotification, pJob->data.resourceManager.loadDataBuffer.pInitFence); - if (result != MA_SUCCESS) { - ma_log_postf(ma_resource_manager_get_log(pResourceManager), MA_LOG_LEVEL_ERROR, "Failed to initialize connector for data buffer. %s.\n", ma_result_description(result)); - goto done; - } - } else { - /* Don't have a known data supply type. Most likely the data buffer node is still loading, but it could be that an error occurred. */ - } - } else { - /* The connector is already initialized. Nothing to do here. */ - } - - /* - If the data node is still loading, we need to repost the job and *not* increment the execution - pointer (i.e. we need to not fall through to the "done" label). - - There is a hole between here and the where the data connector is initialized where the data - buffer node may have finished initializing. We need to check for this by checking the result of - the data buffer node and whether or not we had an unknown data supply type at the time of - trying to initialize the data connector. - */ - result = ma_resource_manager_data_buffer_node_result(pDataBuffer->pNode); - if (result == MA_BUSY || (result == MA_SUCCESS && isConnectorInitialized == MA_FALSE && dataSupplyType == ma_resource_manager_data_supply_type_unknown)) { - return ma_resource_manager_post_job(pResourceManager, pJob); - } - -done: - /* Only move away from a busy code so that we don't trash any existing error codes. */ - ma_atomic_compare_and_swap_i32(&pDataBuffer->result, MA_BUSY, result); - - /* Only signal the other threads after the result has been set just for cleanliness sake. */ - if (pJob->data.resourceManager.loadDataBuffer.pDoneNotification != NULL) { - ma_async_notification_signal(pJob->data.resourceManager.loadDataBuffer.pDoneNotification); - } - if (pJob->data.resourceManager.loadDataBuffer.pDoneFence != NULL) { - ma_fence_release(pJob->data.resourceManager.loadDataBuffer.pDoneFence); - } - - /* - If at this point the data buffer has not had it's connector initialized, it means the - notification event was never signalled which means we need to signal it here. - */ - if (ma_resource_manager_data_buffer_has_connector(pDataBuffer) == MA_FALSE && result != MA_SUCCESS) { - if (pJob->data.resourceManager.loadDataBuffer.pInitNotification != NULL) { - ma_async_notification_signal(pJob->data.resourceManager.loadDataBuffer.pInitNotification); - } - if (pJob->data.resourceManager.loadDataBuffer.pInitFence != NULL) { - ma_fence_release(pJob->data.resourceManager.loadDataBuffer.pInitFence); - } - } - - ma_atomic_fetch_add_32(&pDataBuffer->executionPointer, 1); - return result; -} - -static ma_result ma_job_process__resource_manager__free_data_buffer(ma_job* pJob) -{ - ma_resource_manager* pResourceManager; - ma_resource_manager_data_buffer* pDataBuffer; - - MA_ASSERT(pJob != NULL); - - pDataBuffer = (ma_resource_manager_data_buffer*)pJob->data.resourceManager.freeDataBuffer.pDataBuffer; - MA_ASSERT(pDataBuffer != NULL); - - pResourceManager = pDataBuffer->pResourceManager; - - if (pJob->order != ma_atomic_load_32(&pDataBuffer->executionPointer)) { - return ma_resource_manager_post_job(pResourceManager, pJob); /* Out of order. */ - } - - ma_resource_manager_data_buffer_uninit_internal(pDataBuffer); - - /* The event needs to be signalled last. */ - if (pJob->data.resourceManager.freeDataBuffer.pDoneNotification != NULL) { - ma_async_notification_signal(pJob->data.resourceManager.freeDataBuffer.pDoneNotification); - } - - if (pJob->data.resourceManager.freeDataBuffer.pDoneFence != NULL) { - ma_fence_release(pJob->data.resourceManager.freeDataBuffer.pDoneFence); - } - - ma_atomic_fetch_add_32(&pDataBuffer->executionPointer, 1); - return MA_SUCCESS; -} - -static ma_result ma_job_process__resource_manager__load_data_stream(ma_job* pJob) -{ - ma_result result = MA_SUCCESS; - ma_decoder_config decoderConfig; - ma_uint32 pageBufferSizeInBytes; - ma_resource_manager* pResourceManager; - ma_resource_manager_data_stream* pDataStream; - - MA_ASSERT(pJob != NULL); - - pDataStream = (ma_resource_manager_data_stream*)pJob->data.resourceManager.loadDataStream.pDataStream; - MA_ASSERT(pDataStream != NULL); - - pResourceManager = pDataStream->pResourceManager; - - if (pJob->order != ma_atomic_load_32(&pDataStream->executionPointer)) { - return ma_resource_manager_post_job(pResourceManager, pJob); /* Out of order. */ - } - - if (ma_resource_manager_data_stream_result(pDataStream) != MA_BUSY) { - result = MA_INVALID_OPERATION; /* Most likely the data stream is being uninitialized. */ - goto done; - } - - /* We need to initialize the decoder first so we can determine the size of the pages. */ - decoderConfig = ma_resource_manager__init_decoder_config(pResourceManager); - - if (pJob->data.resourceManager.loadDataStream.pFilePath != NULL) { - result = ma_decoder_init_vfs(pResourceManager->config.pVFS, pJob->data.resourceManager.loadDataStream.pFilePath, &decoderConfig, &pDataStream->decoder); - } else { - result = ma_decoder_init_vfs_w(pResourceManager->config.pVFS, pJob->data.resourceManager.loadDataStream.pFilePathW, &decoderConfig, &pDataStream->decoder); - } - if (result != MA_SUCCESS) { - goto done; - } - - /* Retrieve the total length of the file before marking the decoder as loaded. */ - if ((pDataStream->flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_UNKNOWN_LENGTH) == 0) { - result = ma_decoder_get_length_in_pcm_frames(&pDataStream->decoder, &pDataStream->totalLengthInPCMFrames); - if (result != MA_SUCCESS) { - goto done; /* Failed to retrieve the length. */ - } - } else { - pDataStream->totalLengthInPCMFrames = 0; - } - - /* - Only mark the decoder as initialized when the length of the decoder has been retrieved because that can possibly require a scan over the whole file - and we don't want to have another thread trying to access the decoder while it's scanning. - */ - pDataStream->isDecoderInitialized = MA_TRUE; - - /* We have the decoder so we can now initialize our page buffer. */ - pageBufferSizeInBytes = ma_resource_manager_data_stream_get_page_size_in_frames(pDataStream) * 2 * ma_get_bytes_per_frame(pDataStream->decoder.outputFormat, pDataStream->decoder.outputChannels); - - pDataStream->pPageData = ma_malloc(pageBufferSizeInBytes, &pResourceManager->config.allocationCallbacks); - if (pDataStream->pPageData == NULL) { - ma_decoder_uninit(&pDataStream->decoder); - result = MA_OUT_OF_MEMORY; - goto done; - } - - /* Seek to our initial seek point before filling the initial pages. */ - ma_decoder_seek_to_pcm_frame(&pDataStream->decoder, pJob->data.resourceManager.loadDataStream.initialSeekPoint); - - /* We have our decoder and our page buffer, so now we need to fill our pages. */ - ma_resource_manager_data_stream_fill_pages(pDataStream); - - /* And now we're done. We want to make sure the result is MA_SUCCESS. */ - result = MA_SUCCESS; - -done: - ma_free(pJob->data.resourceManager.loadDataStream.pFilePath, &pResourceManager->config.allocationCallbacks); - ma_free(pJob->data.resourceManager.loadDataStream.pFilePathW, &pResourceManager->config.allocationCallbacks); - - /* We can only change the status away from MA_BUSY. If it's set to anything else it means an error has occurred somewhere or the uninitialization process has started (most likely). */ - ma_atomic_compare_and_swap_i32(&pDataStream->result, MA_BUSY, result); - - /* Only signal the other threads after the result has been set just for cleanliness sake. */ - if (pJob->data.resourceManager.loadDataStream.pInitNotification != NULL) { - ma_async_notification_signal(pJob->data.resourceManager.loadDataStream.pInitNotification); - } - if (pJob->data.resourceManager.loadDataStream.pInitFence != NULL) { - ma_fence_release(pJob->data.resourceManager.loadDataStream.pInitFence); - } - - ma_atomic_fetch_add_32(&pDataStream->executionPointer, 1); - return result; -} - -static ma_result ma_job_process__resource_manager__free_data_stream(ma_job* pJob) -{ - ma_resource_manager* pResourceManager; - ma_resource_manager_data_stream* pDataStream; - - MA_ASSERT(pJob != NULL); - - pDataStream = (ma_resource_manager_data_stream*)pJob->data.resourceManager.freeDataStream.pDataStream; - MA_ASSERT(pDataStream != NULL); - - pResourceManager = pDataStream->pResourceManager; - - if (pJob->order != ma_atomic_load_32(&pDataStream->executionPointer)) { - return ma_resource_manager_post_job(pResourceManager, pJob); /* Out of order. */ - } - - /* If our status is not MA_UNAVAILABLE we have a bug somewhere. */ - MA_ASSERT(ma_resource_manager_data_stream_result(pDataStream) == MA_UNAVAILABLE); - - if (pDataStream->isDecoderInitialized) { - ma_decoder_uninit(&pDataStream->decoder); - } - - if (pDataStream->pPageData != NULL) { - ma_free(pDataStream->pPageData, &pResourceManager->config.allocationCallbacks); - pDataStream->pPageData = NULL; /* Just in case... */ - } - - ma_data_source_uninit(&pDataStream->ds); - - /* The event needs to be signalled last. */ - if (pJob->data.resourceManager.freeDataStream.pDoneNotification != NULL) { - ma_async_notification_signal(pJob->data.resourceManager.freeDataStream.pDoneNotification); - } - if (pJob->data.resourceManager.freeDataStream.pDoneFence != NULL) { - ma_fence_release(pJob->data.resourceManager.freeDataStream.pDoneFence); - } - - /*ma_atomic_fetch_add_32(&pDataStream->executionPointer, 1);*/ - return MA_SUCCESS; -} - -static ma_result ma_job_process__resource_manager__page_data_stream(ma_job* pJob) -{ - ma_result result = MA_SUCCESS; - ma_resource_manager* pResourceManager; - ma_resource_manager_data_stream* pDataStream; - - MA_ASSERT(pJob != NULL); - - pDataStream = (ma_resource_manager_data_stream*)pJob->data.resourceManager.pageDataStream.pDataStream; - MA_ASSERT(pDataStream != NULL); - - pResourceManager = pDataStream->pResourceManager; - - if (pJob->order != ma_atomic_load_32(&pDataStream->executionPointer)) { - return ma_resource_manager_post_job(pResourceManager, pJob); /* Out of order. */ - } - - /* For streams, the status should be MA_SUCCESS. */ - if (ma_resource_manager_data_stream_result(pDataStream) != MA_SUCCESS) { - result = MA_INVALID_OPERATION; - goto done; - } - - ma_resource_manager_data_stream_fill_page(pDataStream, pJob->data.resourceManager.pageDataStream.pageIndex); - -done: - ma_atomic_fetch_add_32(&pDataStream->executionPointer, 1); - return result; -} - -static ma_result ma_job_process__resource_manager__seek_data_stream(ma_job* pJob) -{ - ma_result result = MA_SUCCESS; - ma_resource_manager* pResourceManager; - ma_resource_manager_data_stream* pDataStream; - - MA_ASSERT(pJob != NULL); - - pDataStream = (ma_resource_manager_data_stream*)pJob->data.resourceManager.seekDataStream.pDataStream; - MA_ASSERT(pDataStream != NULL); - - pResourceManager = pDataStream->pResourceManager; - - if (pJob->order != ma_atomic_load_32(&pDataStream->executionPointer)) { - return ma_resource_manager_post_job(pResourceManager, pJob); /* Out of order. */ - } - - /* For streams the status should be MA_SUCCESS for this to do anything. */ - if (ma_resource_manager_data_stream_result(pDataStream) != MA_SUCCESS || pDataStream->isDecoderInitialized == MA_FALSE) { - result = MA_INVALID_OPERATION; - goto done; - } - - /* - With seeking we just assume both pages are invalid and the relative frame cursor at position 0. This is basically exactly the same as loading, except - instead of initializing the decoder, we seek to a frame. - */ - ma_decoder_seek_to_pcm_frame(&pDataStream->decoder, pJob->data.resourceManager.seekDataStream.frameIndex); - - /* After seeking we'll need to reload the pages. */ - ma_resource_manager_data_stream_fill_pages(pDataStream); - - /* We need to let the public API know that we're done seeking. */ - ma_atomic_fetch_sub_32(&pDataStream->seekCounter, 1); - -done: - ma_atomic_fetch_add_32(&pDataStream->executionPointer, 1); - return result; -} - -MA_API ma_result ma_resource_manager_process_job(ma_resource_manager* pResourceManager, ma_job* pJob) -{ - if (pResourceManager == NULL || pJob == NULL) { - return MA_INVALID_ARGS; - } - - return ma_job_process(pJob); -} - -MA_API ma_result ma_resource_manager_process_next_job(ma_resource_manager* pResourceManager) -{ - ma_result result; - ma_job job; - - if (pResourceManager == NULL) { - return MA_INVALID_ARGS; - } - - /* This will return MA_CANCELLED if the next job is a quit job. */ - result = ma_resource_manager_next_job(pResourceManager, &job); - if (result != MA_SUCCESS) { - return result; - } - - return ma_job_process(&job); -} -#else -/* We'll get here if the resource manager is being excluded from the build. We need to define the job processing callbacks as no-ops. */ -static ma_result ma_job_process__resource_manager__load_data_buffer_node(ma_job* pJob) { return ma_job_process__noop(pJob); } -static ma_result ma_job_process__resource_manager__free_data_buffer_node(ma_job* pJob) { return ma_job_process__noop(pJob); } -static ma_result ma_job_process__resource_manager__page_data_buffer_node(ma_job* pJob) { return ma_job_process__noop(pJob); } -static ma_result ma_job_process__resource_manager__load_data_buffer(ma_job* pJob) { return ma_job_process__noop(pJob); } -static ma_result ma_job_process__resource_manager__free_data_buffer(ma_job* pJob) { return ma_job_process__noop(pJob); } -static ma_result ma_job_process__resource_manager__load_data_stream(ma_job* pJob) { return ma_job_process__noop(pJob); } -static ma_result ma_job_process__resource_manager__free_data_stream(ma_job* pJob) { return ma_job_process__noop(pJob); } -static ma_result ma_job_process__resource_manager__page_data_stream(ma_job* pJob) { return ma_job_process__noop(pJob); } -static ma_result ma_job_process__resource_manager__seek_data_stream(ma_job* pJob) { return ma_job_process__noop(pJob); } -#endif /* MA_NO_RESOURCE_MANAGER */ - - -#ifndef MA_NO_NODE_GRAPH - -static ma_stack* ma_stack_init(size_t sizeInBytes, const ma_allocation_callbacks* pAllocationCallbacks) -{ - ma_stack* pStack; - - if (sizeInBytes == 0) { - return NULL; - } - - pStack = (ma_stack*)ma_malloc(sizeof(*pStack) - sizeof(pStack->_data) + sizeInBytes, pAllocationCallbacks); - if (pStack == NULL) { - return NULL; - } - - pStack->offset = 0; - pStack->sizeInBytes = sizeInBytes; - - return pStack; -} - -static void ma_stack_uninit(ma_stack* pStack, const ma_allocation_callbacks* pAllocationCallbacks) -{ - if (pStack == NULL) { - return; - } - - ma_free(pStack, pAllocationCallbacks); -} - -static void* ma_stack_alloc(ma_stack* pStack, size_t sz) -{ - /* The size of the allocation is stored in the memory directly before the pointer. This needs to include padding to keep it aligned to ma_uintptr */ - void* p = (void*)((char*)pStack->_data + pStack->offset); - size_t* pSize = (size_t*)p; - - sz = (sz + (sizeof(ma_uintptr) - 1)) & ~(sizeof(ma_uintptr) - 1); /* Padding. */ - if (pStack->offset + sz + sizeof(size_t) > pStack->sizeInBytes) { - return NULL; /* Out of memory. */ - } - - pStack->offset += sz + sizeof(size_t); - - *pSize = sz; - return (void*)((char*)p + sizeof(size_t)); -} - -static void ma_stack_free(ma_stack* pStack, void* p) -{ - size_t* pSize; - - if (p == NULL) { - return; - } - - pSize = (size_t*)p - 1; - pStack->offset -= *pSize + sizeof(size_t); -} - - - -/* 10ms @ 48K = 480. Must never exceed 65535. */ -#ifndef MA_DEFAULT_NODE_CACHE_CAP_IN_FRAMES_PER_BUS -#define MA_DEFAULT_NODE_CACHE_CAP_IN_FRAMES_PER_BUS 480 -#endif - -#ifndef MA_DEFAULT_PREMIX_STACK_SIZE_PER_CHANNEL -#define MA_DEFAULT_PREMIX_STACK_SIZE_PER_CHANNEL 524288 -#endif - -static ma_result ma_node_read_pcm_frames(ma_node* pNode, ma_uint32 outputBusIndex, float* pFramesOut, ma_uint32 frameCount, ma_uint32* pFramesRead, ma_uint64 globalTime); - -MA_API void ma_debug_fill_pcm_frames_with_sine_wave(float* pFramesOut, ma_uint32 frameCount, ma_format format, ma_uint32 channels, ma_uint32 sampleRate) -{ - #ifndef MA_NO_GENERATION - { - ma_waveform_config waveformConfig; - ma_waveform waveform; - - waveformConfig = ma_waveform_config_init(format, channels, sampleRate, ma_waveform_type_sine, 1.0, 400); - ma_waveform_init(&waveformConfig, &waveform); - ma_waveform_read_pcm_frames(&waveform, pFramesOut, frameCount, NULL); - } - #else - { - (void)pFramesOut; - (void)frameCount; - (void)format; - (void)channels; - (void)sampleRate; - #if defined(MA_DEBUG_OUTPUT) - { - #if _MSC_VER - #pragma message ("ma_debug_fill_pcm_frames_with_sine_wave() will do nothing because MA_NO_GENERATION is enabled.") - #endif - } - #endif - } - #endif -} - - - -MA_API ma_node_graph_config ma_node_graph_config_init(ma_uint32 channels) -{ - ma_node_graph_config config; - - MA_ZERO_OBJECT(&config); - config.channels = channels; - config.processingSizeInFrames = 0; - - return config; -} - - -static void ma_node_graph_set_is_reading(ma_node_graph* pNodeGraph, ma_bool32 isReading) -{ - MA_ASSERT(pNodeGraph != NULL); - ma_atomic_exchange_32(&pNodeGraph->isReading, isReading); -} - -#if 0 -static ma_bool32 ma_node_graph_is_reading(ma_node_graph* pNodeGraph) -{ - MA_ASSERT(pNodeGraph != NULL); - return ma_atomic_load_32(&pNodeGraph->isReading); -} -#endif - - -static void ma_node_graph_node_process_pcm_frames(ma_node* pNode, const float** ppFramesIn, ma_uint32* pFrameCountIn, float** ppFramesOut, ma_uint32* pFrameCountOut) -{ - ma_node_graph* pNodeGraph = (ma_node_graph*)pNode; - ma_uint64 framesRead; - - ma_node_graph_read_pcm_frames(pNodeGraph, ppFramesOut[0], *pFrameCountOut, &framesRead); - - *pFrameCountOut = (ma_uint32)framesRead; /* Safe cast. */ - - (void)ppFramesIn; - (void)pFrameCountIn; -} - -static ma_node_vtable g_node_graph_node_vtable = -{ - ma_node_graph_node_process_pcm_frames, - NULL, /* onGetRequiredInputFrameCount */ - 0, /* 0 input buses. */ - 1, /* 1 output bus. */ - 0 /* Flags. */ -}; - -static void ma_node_graph_endpoint_process_pcm_frames(ma_node* pNode, const float** ppFramesIn, ma_uint32* pFrameCountIn, float** ppFramesOut, ma_uint32* pFrameCountOut) -{ - MA_ASSERT(pNode != NULL); - MA_ASSERT(ma_node_get_input_bus_count(pNode) == 1); - MA_ASSERT(ma_node_get_output_bus_count(pNode) == 1); - - /* Input channel count needs to be the same as the output channel count. */ - MA_ASSERT(ma_node_get_input_channels(pNode, 0) == ma_node_get_output_channels(pNode, 0)); - - /* We don't need to do anything here because it's a passthrough. */ - (void)pNode; - (void)ppFramesIn; - (void)pFrameCountIn; - (void)ppFramesOut; - (void)pFrameCountOut; - -#if 0 - /* The data has already been mixed. We just need to move it to the output buffer. */ - if (ppFramesIn != NULL) { - ma_copy_pcm_frames(ppFramesOut[0], ppFramesIn[0], *pFrameCountOut, ma_format_f32, ma_node_get_output_channels(pNode, 0)); - } -#endif -} - -static ma_node_vtable g_node_graph_endpoint_vtable = -{ - ma_node_graph_endpoint_process_pcm_frames, - NULL, /* onGetRequiredInputFrameCount */ - 1, /* 1 input bus. */ - 1, /* 1 output bus. */ - MA_NODE_FLAG_PASSTHROUGH /* Flags. The endpoint is a passthrough. */ -}; - -MA_API ma_result ma_node_graph_init(const ma_node_graph_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_node_graph* pNodeGraph) -{ - ma_result result; - ma_node_config baseConfig; - ma_node_config endpointConfig; - - if (pNodeGraph == NULL) { - return MA_INVALID_ARGS; - } - - MA_ZERO_OBJECT(pNodeGraph); - pNodeGraph->processingSizeInFrames = pConfig->processingSizeInFrames; - - /* Base node so we can use the node graph as a node into another graph. */ - baseConfig = ma_node_config_init(); - baseConfig.vtable = &g_node_graph_node_vtable; - baseConfig.pOutputChannels = &pConfig->channels; - - result = ma_node_init(pNodeGraph, &baseConfig, pAllocationCallbacks, &pNodeGraph->base); - if (result != MA_SUCCESS) { - return result; - } - - - /* Endpoint. */ - endpointConfig = ma_node_config_init(); - endpointConfig.vtable = &g_node_graph_endpoint_vtable; - endpointConfig.pInputChannels = &pConfig->channels; - endpointConfig.pOutputChannels = &pConfig->channels; - - result = ma_node_init(pNodeGraph, &endpointConfig, pAllocationCallbacks, &pNodeGraph->endpoint); - if (result != MA_SUCCESS) { - ma_node_uninit(&pNodeGraph->base, pAllocationCallbacks); - return result; - } - - - /* Processing cache. */ - if (pConfig->processingSizeInFrames > 0) { - pNodeGraph->pProcessingCache = (float*)ma_malloc(pConfig->processingSizeInFrames * pConfig->channels * sizeof(float), pAllocationCallbacks); - if (pNodeGraph->pProcessingCache == NULL) { - ma_node_uninit(&pNodeGraph->endpoint, pAllocationCallbacks); - ma_node_uninit(&pNodeGraph->base, pAllocationCallbacks); - return MA_OUT_OF_MEMORY; - } - } - - - /* - We need a pre-mix stack. The size of this stack is configurable via the config. The default value depends on the channel count. - */ - { - size_t preMixStackSizeInBytes = pConfig->preMixStackSizeInBytes; - if (preMixStackSizeInBytes == 0) { - preMixStackSizeInBytes = pConfig->channels * MA_DEFAULT_PREMIX_STACK_SIZE_PER_CHANNEL; - } - - pNodeGraph->pPreMixStack = ma_stack_init(preMixStackSizeInBytes, pAllocationCallbacks); - if (pNodeGraph->pPreMixStack == NULL) { - ma_node_uninit(&pNodeGraph->endpoint, pAllocationCallbacks); - ma_node_uninit(&pNodeGraph->base, pAllocationCallbacks); - if (pNodeGraph->pProcessingCache != NULL) { - ma_free(pNodeGraph->pProcessingCache, pAllocationCallbacks); - } - - return MA_OUT_OF_MEMORY; - } - } - - - return MA_SUCCESS; -} - -MA_API void ma_node_graph_uninit(ma_node_graph* pNodeGraph, const ma_allocation_callbacks* pAllocationCallbacks) -{ - if (pNodeGraph == NULL) { - return; - } - - ma_node_uninit(&pNodeGraph->endpoint, pAllocationCallbacks); - ma_node_uninit(&pNodeGraph->base, pAllocationCallbacks); - - if (pNodeGraph->pProcessingCache != NULL) { - ma_free(pNodeGraph->pProcessingCache, pAllocationCallbacks); - pNodeGraph->pProcessingCache = NULL; - } - - if (pNodeGraph->pPreMixStack != NULL) { - ma_stack_uninit(pNodeGraph->pPreMixStack, pAllocationCallbacks); - pNodeGraph->pPreMixStack = NULL; - } -} - -MA_API ma_node* ma_node_graph_get_endpoint(ma_node_graph* pNodeGraph) -{ - if (pNodeGraph == NULL) { - return NULL; - } - - return &pNodeGraph->endpoint; -} - -MA_API ma_result ma_node_graph_read_pcm_frames(ma_node_graph* pNodeGraph, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead) -{ - ma_result result = MA_SUCCESS; - ma_uint64 totalFramesRead; - ma_uint32 channels; - - if (pFramesRead != NULL) { - *pFramesRead = 0; /* Safety. */ - } - - if (pNodeGraph == NULL) { - return MA_INVALID_ARGS; - } - - channels = ma_node_get_output_channels(&pNodeGraph->endpoint, 0); - - - /* We'll be nice and try to do a full read of all frameCount frames. */ - totalFramesRead = 0; - while (totalFramesRead < frameCount) { - ma_uint32 framesJustRead; - ma_uint64 framesToRead; - float* pRunningFramesOut; - - framesToRead = frameCount - totalFramesRead; - if (framesToRead > 0xFFFFFFFF) { - framesToRead = 0xFFFFFFFF; - } - - pRunningFramesOut = (float*)ma_offset_pcm_frames_ptr(pFramesOut, totalFramesRead, ma_format_f32, channels); - - /* If there's anything in the cache, consume that first. */ - if (pNodeGraph->processingCacheFramesRemaining > 0) { - ma_uint32 framesToReadFromCache; - - framesToReadFromCache = (ma_uint32)framesToRead; - if (framesToReadFromCache > pNodeGraph->processingCacheFramesRemaining) { - framesToReadFromCache = pNodeGraph->processingCacheFramesRemaining; - } - - MA_COPY_MEMORY(pRunningFramesOut, pNodeGraph->pProcessingCache, framesToReadFromCache * channels * sizeof(float)); - MA_MOVE_MEMORY(pNodeGraph->pProcessingCache, pNodeGraph->pProcessingCache + (framesToReadFromCache * channels), (pNodeGraph->processingCacheFramesRemaining - framesToReadFromCache) * channels * sizeof(float)); - pNodeGraph->processingCacheFramesRemaining -= framesToReadFromCache; - - totalFramesRead += framesToReadFromCache; - continue; - } else { - /* - If processingSizeInFrames is non-zero, we need to make sure we always read in chunks of that size. If the frame count is less than - that, we need to read into the cache and then continue on. - */ - float* pReadDst = pRunningFramesOut; - - if (pNodeGraph->processingSizeInFrames > 0) { - if (framesToRead < pNodeGraph->processingSizeInFrames) { - pReadDst = pNodeGraph->pProcessingCache; /* We need to read into the cache because otherwise we'll overflow the output buffer. */ - } - - framesToRead = pNodeGraph->processingSizeInFrames; - } - - ma_node_graph_set_is_reading(pNodeGraph, MA_TRUE); - { - result = ma_node_read_pcm_frames(&pNodeGraph->endpoint, 0, pReadDst, (ma_uint32)framesToRead, &framesJustRead, ma_node_get_time(&pNodeGraph->endpoint)); - } - ma_node_graph_set_is_reading(pNodeGraph, MA_FALSE); - - /* - Do not increment the total frames read counter if we read into the cache. We use this to determine how many frames have - been written to the final output buffer. - */ - if (pReadDst == pNodeGraph->pProcessingCache) { - /* We read into the cache. */ - pNodeGraph->processingCacheFramesRemaining = framesJustRead; - } else { - /* We read straight into the output buffer. */ - totalFramesRead += framesJustRead; - } - - if (result != MA_SUCCESS) { - break; - } - - /* Abort if we weren't able to read any frames or else we risk getting stuck in a loop. */ - if (framesJustRead == 0) { - break; - } - } - } - - /* Let's go ahead and silence any leftover frames just for some added safety to ensure the caller doesn't try emitting garbage out of the speakers. */ - if (totalFramesRead < frameCount) { - ma_silence_pcm_frames(ma_offset_pcm_frames_ptr(pFramesOut, totalFramesRead, ma_format_f32, channels), (frameCount - totalFramesRead), ma_format_f32, channels); - } - - if (pFramesRead != NULL) { - *pFramesRead = totalFramesRead; - } - - return result; -} - -MA_API ma_uint32 ma_node_graph_get_channels(const ma_node_graph* pNodeGraph) -{ - if (pNodeGraph == NULL) { - return 0; - } - - return ma_node_get_output_channels(&pNodeGraph->endpoint, 0); -} - -MA_API ma_uint64 ma_node_graph_get_time(const ma_node_graph* pNodeGraph) -{ - if (pNodeGraph == NULL) { - return 0; - } - - return ma_node_get_time(&pNodeGraph->endpoint); /* Global time is just the local time of the endpoint. */ -} - -MA_API ma_result ma_node_graph_set_time(ma_node_graph* pNodeGraph, ma_uint64 globalTime) -{ - if (pNodeGraph == NULL) { - return MA_INVALID_ARGS; - } - - return ma_node_set_time(&pNodeGraph->endpoint, globalTime); /* Global time is just the local time of the endpoint. */ -} - - -#define MA_NODE_OUTPUT_BUS_FLAG_HAS_READ 0x01 /* Whether or not this bus ready to read more data. Only used on nodes with multiple output buses. */ - -static ma_result ma_node_output_bus_init(ma_node* pNode, ma_uint32 outputBusIndex, ma_uint32 channels, ma_node_output_bus* pOutputBus) -{ - MA_ASSERT(pOutputBus != NULL); - MA_ASSERT(outputBusIndex < MA_MAX_NODE_BUS_COUNT); - MA_ASSERT(outputBusIndex < ma_node_get_output_bus_count(pNode)); - MA_ASSERT(channels < 256); - - MA_ZERO_OBJECT(pOutputBus); - - if (channels == 0) { - return MA_INVALID_ARGS; - } - - pOutputBus->pNode = pNode; - pOutputBus->outputBusIndex = (ma_uint8)outputBusIndex; - pOutputBus->channels = (ma_uint8)channels; - pOutputBus->flags = MA_NODE_OUTPUT_BUS_FLAG_HAS_READ; /* <-- Important that this flag is set by default. */ - pOutputBus->volume = 1; - - return MA_SUCCESS; -} - -static void ma_node_output_bus_lock(ma_node_output_bus* pOutputBus) -{ - ma_spinlock_lock(&pOutputBus->lock); -} - -static void ma_node_output_bus_unlock(ma_node_output_bus* pOutputBus) -{ - ma_spinlock_unlock(&pOutputBus->lock); -} - - -static ma_uint32 ma_node_output_bus_get_channels(const ma_node_output_bus* pOutputBus) -{ - return pOutputBus->channels; -} - - -static void ma_node_output_bus_set_has_read(ma_node_output_bus* pOutputBus, ma_bool32 hasRead) -{ - if (hasRead) { - ma_atomic_fetch_or_32(&pOutputBus->flags, MA_NODE_OUTPUT_BUS_FLAG_HAS_READ); - } else { - ma_atomic_fetch_and_32(&pOutputBus->flags, (ma_uint32)~MA_NODE_OUTPUT_BUS_FLAG_HAS_READ); - } -} - -static ma_bool32 ma_node_output_bus_has_read(ma_node_output_bus* pOutputBus) -{ - return (ma_atomic_load_32(&pOutputBus->flags) & MA_NODE_OUTPUT_BUS_FLAG_HAS_READ) != 0; -} - - -static void ma_node_output_bus_set_is_attached(ma_node_output_bus* pOutputBus, ma_bool32 isAttached) -{ - ma_atomic_exchange_32(&pOutputBus->isAttached, isAttached); -} - -static ma_bool32 ma_node_output_bus_is_attached(ma_node_output_bus* pOutputBus) -{ - return ma_atomic_load_32(&pOutputBus->isAttached); -} - - -static ma_result ma_node_output_bus_set_volume(ma_node_output_bus* pOutputBus, float volume) -{ - MA_ASSERT(pOutputBus != NULL); - - if (volume < 0.0f) { - volume = 0.0f; - } - - ma_atomic_exchange_f32(&pOutputBus->volume, volume); - - return MA_SUCCESS; -} - -static float ma_node_output_bus_get_volume(const ma_node_output_bus* pOutputBus) -{ - return ma_atomic_load_f32((float*)&pOutputBus->volume); -} - - -static ma_result ma_node_input_bus_init(ma_uint32 channels, ma_node_input_bus* pInputBus) -{ - MA_ASSERT(pInputBus != NULL); - MA_ASSERT(channels < 256); - - MA_ZERO_OBJECT(pInputBus); - - if (channels == 0) { - return MA_INVALID_ARGS; - } - - pInputBus->channels = (ma_uint8)channels; - - return MA_SUCCESS; -} - -static void ma_node_input_bus_lock(ma_node_input_bus* pInputBus) -{ - MA_ASSERT(pInputBus != NULL); - - ma_spinlock_lock(&pInputBus->lock); -} - -static void ma_node_input_bus_unlock(ma_node_input_bus* pInputBus) -{ - MA_ASSERT(pInputBus != NULL); - - ma_spinlock_unlock(&pInputBus->lock); -} - - -static void ma_node_input_bus_next_begin(ma_node_input_bus* pInputBus) -{ - ma_atomic_fetch_add_32(&pInputBus->nextCounter, 1); -} - -static void ma_node_input_bus_next_end(ma_node_input_bus* pInputBus) -{ - ma_atomic_fetch_sub_32(&pInputBus->nextCounter, 1); -} - -static ma_uint32 ma_node_input_bus_get_next_counter(ma_node_input_bus* pInputBus) -{ - return ma_atomic_load_32(&pInputBus->nextCounter); -} - - -static ma_uint32 ma_node_input_bus_get_channels(const ma_node_input_bus* pInputBus) -{ - return pInputBus->channels; -} - - -static void ma_node_input_bus_detach__no_output_bus_lock(ma_node_input_bus* pInputBus, ma_node_output_bus* pOutputBus) -{ - MA_ASSERT(pInputBus != NULL); - MA_ASSERT(pOutputBus != NULL); - - /* - Mark the output bus as detached first. This will prevent future iterations on the audio thread - from iterating this output bus. - */ - ma_node_output_bus_set_is_attached(pOutputBus, MA_FALSE); - - /* - We cannot use the output bus lock here since it'll be getting used at a higher level, but we do - still need to use the input bus lock since we'll be updating pointers on two different output - buses. The same rules apply here as the attaching case. Although we're using a lock here, we're - *not* using a lock when iterating over the list in the audio thread. We therefore need to craft - this in a way such that the iteration on the audio thread doesn't break. - - The first thing to do is swap out the "next" pointer of the previous output bus with the - new "next" output bus. This is the operation that matters for iteration on the audio thread. - After that, the previous pointer on the new "next" pointer needs to be updated, after which - point the linked list will be in a good state. - */ - ma_node_input_bus_lock(pInputBus); - { - ma_node_output_bus* pOldPrev = (ma_node_output_bus*)ma_atomic_load_ptr(&pOutputBus->pPrev); - ma_node_output_bus* pOldNext = (ma_node_output_bus*)ma_atomic_load_ptr(&pOutputBus->pNext); - - if (pOldPrev != NULL) { - ma_atomic_exchange_ptr(&pOldPrev->pNext, pOldNext); /* <-- This is where the output bus is detached from the list. */ - } - if (pOldNext != NULL) { - ma_atomic_exchange_ptr(&pOldNext->pPrev, pOldPrev); /* <-- This is required for detachment. */ - } - } - ma_node_input_bus_unlock(pInputBus); - - /* At this point the output bus is detached and the linked list is completely unaware of it. Reset some data for safety. */ - ma_atomic_exchange_ptr(&pOutputBus->pNext, NULL); /* Using atomic exchanges here, mainly for the benefit of analysis tools which don't always recognize spinlocks. */ - ma_atomic_exchange_ptr(&pOutputBus->pPrev, NULL); /* As above. */ - pOutputBus->pInputNode = NULL; - pOutputBus->inputNodeInputBusIndex = 0; - - - /* - For thread-safety reasons, we don't want to be returning from this straight away. We need to - wait for the audio thread to finish with the output bus. There's two things we need to wait - for. The first is the part that selects the next output bus in the list, and the other is the - part that reads from the output bus. Basically all we're doing is waiting for the input bus - to stop referencing the output bus. - - We're doing this part last because we want the section above to run while the audio thread - is finishing up with the output bus, just for efficiency reasons. We marked the output bus as - detached right at the top of this function which is going to prevent the audio thread from - iterating the output bus again. - */ - - /* Part 1: Wait for the current iteration to complete. */ - while (ma_node_input_bus_get_next_counter(pInputBus) > 0) { - ma_yield(); - } - - /* Part 2: Wait for any reads to complete. */ - while (ma_atomic_load_32(&pOutputBus->refCount) > 0) { - ma_yield(); - } - - /* - At this point we're done detaching and we can be guaranteed that the audio thread is not going - to attempt to reference this output bus again (until attached again). - */ -} - -#if 0 /* Not used at the moment, but leaving here in case I need it later. */ -static void ma_node_input_bus_detach(ma_node_input_bus* pInputBus, ma_node_output_bus* pOutputBus) -{ - MA_ASSERT(pInputBus != NULL); - MA_ASSERT(pOutputBus != NULL); - - ma_node_output_bus_lock(pOutputBus); - { - ma_node_input_bus_detach__no_output_bus_lock(pInputBus, pOutputBus); - } - ma_node_output_bus_unlock(pOutputBus); -} -#endif - -static void ma_node_input_bus_attach(ma_node_input_bus* pInputBus, ma_node_output_bus* pOutputBus, ma_node* pNewInputNode, ma_uint32 inputNodeInputBusIndex) -{ - MA_ASSERT(pInputBus != NULL); - MA_ASSERT(pOutputBus != NULL); - - ma_node_output_bus_lock(pOutputBus); - { - ma_node_output_bus* pOldInputNode = (ma_node_output_bus*)ma_atomic_load_ptr(&pOutputBus->pInputNode); - - /* Detach from any existing attachment first if necessary. */ - if (pOldInputNode != NULL) { - ma_node_input_bus_detach__no_output_bus_lock(pInputBus, pOutputBus); - } - - /* - At this point we can be sure the output bus is not attached to anything. The linked list in the - old input bus has been updated so that pOutputBus will not get iterated again. - */ - pOutputBus->pInputNode = pNewInputNode; /* No need for an atomic assignment here because modification of this variable always happens within a lock. */ - pOutputBus->inputNodeInputBusIndex = (ma_uint8)inputNodeInputBusIndex; - - /* - Now we need to attach the output bus to the linked list. This involves updating two pointers on - two different output buses so I'm going to go ahead and keep this simple and just use a lock. - There are ways to do this without a lock, but it's just too hard to maintain for its value. - - Although we're locking here, it's important to remember that we're *not* locking when iterating - and reading audio data since that'll be running on the audio thread. As a result we need to be - careful how we craft this so that we don't break iteration. What we're going to do is always - attach the new item so that it becomes the first item in the list. That way, as we're iterating - we won't break any links in the list and iteration will continue safely. The detaching case will - also be crafted in a way as to not break list iteration. It's important to remember to use - atomic exchanges here since no locking is happening on the audio thread during iteration. - */ - ma_node_input_bus_lock(pInputBus); - { - ma_node_output_bus* pNewPrev = &pInputBus->head; - ma_node_output_bus* pNewNext = (ma_node_output_bus*)ma_atomic_load_ptr(&pInputBus->head.pNext); - - /* Update the local output bus. */ - ma_atomic_exchange_ptr(&pOutputBus->pPrev, pNewPrev); - ma_atomic_exchange_ptr(&pOutputBus->pNext, pNewNext); - - /* Update the other output buses to point back to the local output bus. */ - ma_atomic_exchange_ptr(&pInputBus->head.pNext, pOutputBus); /* <-- This is where the output bus is actually attached to the input bus. */ - - /* Do the previous pointer last. This is only used for detachment. */ - if (pNewNext != NULL) { - ma_atomic_exchange_ptr(&pNewNext->pPrev, pOutputBus); - } - } - ma_node_input_bus_unlock(pInputBus); - - /* - Mark the node as attached last. This is used to controlling whether or the output bus will be - iterated on the audio thread. Mainly required for detachment purposes. - */ - ma_node_output_bus_set_is_attached(pOutputBus, MA_TRUE); - } - ma_node_output_bus_unlock(pOutputBus); -} - -static ma_node_output_bus* ma_node_input_bus_next(ma_node_input_bus* pInputBus, ma_node_output_bus* pOutputBus) -{ - ma_node_output_bus* pNext; - - MA_ASSERT(pInputBus != NULL); - - if (pOutputBus == NULL) { - return NULL; - } - - ma_node_input_bus_next_begin(pInputBus); - { - pNext = pOutputBus; - for (;;) { - pNext = (ma_node_output_bus*)ma_atomic_load_ptr(&pNext->pNext); - if (pNext == NULL) { - break; /* Reached the end. */ - } - - if (ma_node_output_bus_is_attached(pNext) == MA_FALSE) { - continue; /* The node is not attached. Keep checking. */ - } - - /* The next node has been selected. */ - break; - } - - /* We need to increment the reference count of the selected node. */ - if (pNext != NULL) { - ma_atomic_fetch_add_32(&pNext->refCount, 1); - } - - /* The previous node is no longer being referenced. */ - ma_atomic_fetch_sub_32(&pOutputBus->refCount, 1); - } - ma_node_input_bus_next_end(pInputBus); - - return pNext; -} - -static ma_node_output_bus* ma_node_input_bus_first(ma_node_input_bus* pInputBus) -{ - return ma_node_input_bus_next(pInputBus, &pInputBus->head); -} - - - -static ma_result ma_node_input_bus_read_pcm_frames(ma_node* pInputNode, ma_node_input_bus* pInputBus, float* pFramesOut, ma_uint32 frameCount, ma_uint32* pFramesRead, ma_uint64 globalTime) -{ - ma_result result = MA_SUCCESS; - ma_node_output_bus* pOutputBus; - ma_node_output_bus* pFirst; - ma_uint32 inputChannels; - ma_bool32 doesOutputBufferHaveContent = MA_FALSE; - - /* - This will be called from the audio thread which means we can't be doing any locking. Basically, - this function will not perform any locking, whereas attaching and detaching will, but crafted in - such a way that we don't need to perform any locking here. The important thing to remember is - to always iterate in a forward direction. - - In order to process any data we need to first read from all input buses. That's where this - function comes in. This iterates over each of the attachments and accumulates/mixes them. We - also convert the channels to the nodes output channel count before mixing. We want to do this - channel conversion so that the caller of this function can invoke the processing callback - without having to do it themselves. - - When we iterate over each of the attachments on the input bus, we need to read as much data as - we can from each of them so that we don't end up with holes between each of the attachments. To - do this, we need to read from each attachment in a loop and read as many frames as we can, up - to `frameCount`. - */ - MA_ASSERT(pInputNode != NULL); - MA_ASSERT(pFramesRead != NULL); /* pFramesRead is critical and must always be specified. On input it's undefined and on output it'll be set to the number of frames actually read. */ - - *pFramesRead = 0; /* Safety. */ - - inputChannels = ma_node_input_bus_get_channels(pInputBus); - - /* - We need to be careful with how we call ma_node_input_bus_first() and ma_node_input_bus_next(). They - are both critical to our lock-free thread-safety system. We can only call ma_node_input_bus_first() - once per iteration, however we have an optimization to checks whether or not it's the first item in - the list. We therefore need to store a pointer to the first item rather than repeatedly calling - ma_node_input_bus_first(). It's safe to keep hold of this pointer, so long as we don't dereference it - after calling ma_node_input_bus_next(), which we won't be. - */ - pFirst = ma_node_input_bus_first(pInputBus); - if (pFirst == NULL) { - return MA_SUCCESS; /* No attachments. Read nothing. */ - } - - for (pOutputBus = pFirst; pOutputBus != NULL; pOutputBus = ma_node_input_bus_next(pInputBus, pOutputBus)) { - ma_uint32 framesProcessed = 0; - ma_bool32 isSilentOutput = MA_FALSE; - - MA_ASSERT(pOutputBus->pNode != NULL); - MA_ASSERT(((ma_node_base*)pOutputBus->pNode)->vtable != NULL); - - isSilentOutput = (((ma_node_base*)pOutputBus->pNode)->vtable->flags & MA_NODE_FLAG_SILENT_OUTPUT) != 0; - - if (pFramesOut != NULL) { - /* Read. */ - while (framesProcessed < frameCount) { - float* pRunningFramesOut; - ma_uint32 framesToRead; - ma_uint32 framesJustRead = 0; - - framesToRead = frameCount - framesProcessed; - pRunningFramesOut = ma_offset_pcm_frames_ptr_f32(pFramesOut, framesProcessed, inputChannels); - - if (doesOutputBufferHaveContent == MA_FALSE) { - /* Fast path. First attachment. We just read straight into the output buffer (no mixing required). */ - result = ma_node_read_pcm_frames(pOutputBus->pNode, pOutputBus->outputBusIndex, pRunningFramesOut, framesToRead, &framesJustRead, globalTime + framesProcessed); - } else { - /* Slow path. Not the first attachment. Mixing required. */ - ma_uint32 preMixBufferCapInFrames = ((ma_node_base*)pInputNode)->cachedDataCapInFramesPerBus; - float* pPreMixBuffer = (float*)ma_stack_alloc(((ma_node_base*)pInputNode)->pNodeGraph->pPreMixStack, preMixBufferCapInFrames * inputChannels * sizeof(float)); - - if (pPreMixBuffer == NULL) { - /* - If you're hitting this assert it means you've got an unusually deep chain of nodes, you've got an excessively large processing - size, or you have a combination of both, and as a result have run out of stack space. You can increase this using the - preMixStackSizeInBytes variable in ma_node_graph_config. If you're using ma_engine, you can do it via the preMixStackSizeInBytes - variable in ma_engine_config. It defaults to 512KB per output channel. - */ - MA_ASSERT(MA_FALSE); - } else { - if (framesToRead > preMixBufferCapInFrames) { - framesToRead = preMixBufferCapInFrames; - } - - result = ma_node_read_pcm_frames(pOutputBus->pNode, pOutputBus->outputBusIndex, pPreMixBuffer, framesToRead, &framesJustRead, globalTime + framesProcessed); - if (result == MA_SUCCESS || result == MA_AT_END) { - if (isSilentOutput == MA_FALSE) { /* Don't mix if the node outputs silence. */ - ma_mix_pcm_frames_f32(pRunningFramesOut, pPreMixBuffer, framesJustRead, inputChannels, /*volume*/1); - } - } - - /* The pre-mix buffer is no longer required. */ - ma_stack_free(((ma_node_base*)pInputNode)->pNodeGraph->pPreMixStack, pPreMixBuffer); - pPreMixBuffer = NULL; - } - } - - framesProcessed += framesJustRead; - - /* If we reached the end or otherwise failed to read any data we need to finish up with this output node. */ - if (result != MA_SUCCESS) { - break; - } - - /* If we didn't read anything, abort so we don't get stuck in a loop. */ - if (framesJustRead == 0) { - break; - } - } - - /* If it's the first attachment we didn't do any mixing. Any leftover samples need to be silenced. */ - if (pOutputBus == pFirst && framesProcessed < frameCount) { - ma_silence_pcm_frames(ma_offset_pcm_frames_ptr(pFramesOut, framesProcessed, ma_format_f32, inputChannels), (frameCount - framesProcessed), ma_format_f32, inputChannels); - } - - if (isSilentOutput == MA_FALSE) { - doesOutputBufferHaveContent = MA_TRUE; - } - } else { - /* Seek. */ - ma_node_read_pcm_frames(pOutputBus->pNode, pOutputBus->outputBusIndex, NULL, frameCount, &framesProcessed, globalTime); - } - } - - /* If we didn't output anything, output silence. */ - if (doesOutputBufferHaveContent == MA_FALSE && pFramesOut != NULL) { - ma_silence_pcm_frames(pFramesOut, frameCount, ma_format_f32, inputChannels); - } - - /* In this path we always "process" the entire amount. */ - *pFramesRead = frameCount; - - return result; -} - - -MA_API ma_node_config ma_node_config_init(void) -{ - ma_node_config config; - - MA_ZERO_OBJECT(&config); - config.initialState = ma_node_state_started; /* Nodes are started by default. */ - config.inputBusCount = MA_NODE_BUS_COUNT_UNKNOWN; - config.outputBusCount = MA_NODE_BUS_COUNT_UNKNOWN; - - return config; -} - -static ma_uint16 ma_node_config_get_cache_size_in_frames(const ma_node_config* pConfig, const ma_node_graph* pNodeGraph) -{ - ma_uint32 cacheSizeInFrames; - - (void)pConfig; - - if (pNodeGraph->processingSizeInFrames > 0) { - cacheSizeInFrames = pNodeGraph->processingSizeInFrames; - } else { - cacheSizeInFrames = MA_DEFAULT_NODE_CACHE_CAP_IN_FRAMES_PER_BUS; - } - - if (cacheSizeInFrames > 0xFFFF) { - cacheSizeInFrames = 0xFFFF; - } - - return (ma_uint16)cacheSizeInFrames; -} - - - -static ma_result ma_node_detach_full(ma_node* pNode); - -static float* ma_node_get_cached_input_ptr(ma_node* pNode, ma_uint32 inputBusIndex) -{ - ma_node_base* pNodeBase = (ma_node_base*)pNode; - ma_uint32 iInputBus; - float* pBasePtr; - - MA_ASSERT(pNodeBase != NULL); - - /* Input data is stored at the front of the buffer. */ - pBasePtr = pNodeBase->pCachedData; - for (iInputBus = 0; iInputBus < inputBusIndex; iInputBus += 1) { - pBasePtr += pNodeBase->cachedDataCapInFramesPerBus * ma_node_input_bus_get_channels(&pNodeBase->pInputBuses[iInputBus]); - } - - return pBasePtr; -} - -static float* ma_node_get_cached_output_ptr(ma_node* pNode, ma_uint32 outputBusIndex) -{ - ma_node_base* pNodeBase = (ma_node_base*)pNode; - ma_uint32 iInputBus; - ma_uint32 iOutputBus; - float* pBasePtr; - - MA_ASSERT(pNodeBase != NULL); - - /* Cached output data starts after the input data. */ - pBasePtr = pNodeBase->pCachedData; - for (iInputBus = 0; iInputBus < ma_node_get_input_bus_count(pNodeBase); iInputBus += 1) { - pBasePtr += pNodeBase->cachedDataCapInFramesPerBus * ma_node_input_bus_get_channels(&pNodeBase->pInputBuses[iInputBus]); - } - - for (iOutputBus = 0; iOutputBus < outputBusIndex; iOutputBus += 1) { - pBasePtr += pNodeBase->cachedDataCapInFramesPerBus * ma_node_output_bus_get_channels(&pNodeBase->pOutputBuses[iOutputBus]); - } - - return pBasePtr; -} - - -typedef struct -{ - size_t sizeInBytes; - size_t inputBusOffset; - size_t outputBusOffset; - size_t cachedDataOffset; - ma_uint32 inputBusCount; /* So it doesn't have to be calculated twice. */ - ma_uint32 outputBusCount; /* So it doesn't have to be calculated twice. */ -} ma_node_heap_layout; - -static ma_result ma_node_translate_bus_counts(const ma_node_config* pConfig, ma_uint32* pInputBusCount, ma_uint32* pOutputBusCount) -{ - ma_uint32 inputBusCount; - ma_uint32 outputBusCount; - - MA_ASSERT(pConfig != NULL); - MA_ASSERT(pInputBusCount != NULL); - MA_ASSERT(pOutputBusCount != NULL); - - /* Bus counts are determined by the vtable, unless they're set to `MA_NODE_BUS_COUNT_UNKNWON`, in which case they're taken from the config. */ - if (pConfig->vtable->inputBusCount == MA_NODE_BUS_COUNT_UNKNOWN) { - inputBusCount = pConfig->inputBusCount; - } else { - inputBusCount = pConfig->vtable->inputBusCount; - - if (pConfig->inputBusCount != MA_NODE_BUS_COUNT_UNKNOWN && pConfig->inputBusCount != pConfig->vtable->inputBusCount) { - return MA_INVALID_ARGS; /* Invalid configuration. You must not specify a conflicting bus count between the node's config and the vtable. */ - } - } - - if (pConfig->vtable->outputBusCount == MA_NODE_BUS_COUNT_UNKNOWN) { - outputBusCount = pConfig->outputBusCount; - } else { - outputBusCount = pConfig->vtable->outputBusCount; - - if (pConfig->outputBusCount != MA_NODE_BUS_COUNT_UNKNOWN && pConfig->outputBusCount != pConfig->vtable->outputBusCount) { - return MA_INVALID_ARGS; /* Invalid configuration. You must not specify a conflicting bus count between the node's config and the vtable. */ - } - } - - /* Bus counts must be within limits. */ - if (inputBusCount > MA_MAX_NODE_BUS_COUNT || outputBusCount > MA_MAX_NODE_BUS_COUNT) { - return MA_INVALID_ARGS; - } - - - /* We must have channel counts for each bus. */ - if ((inputBusCount > 0 && pConfig->pInputChannels == NULL) || (outputBusCount > 0 && pConfig->pOutputChannels == NULL)) { - return MA_INVALID_ARGS; /* You must specify channel counts for each input and output bus. */ - } - - - /* Some special rules for passthrough nodes. */ - if ((pConfig->vtable->flags & MA_NODE_FLAG_PASSTHROUGH) != 0) { - if ((pConfig->vtable->inputBusCount != 0 && pConfig->vtable->inputBusCount != 1) || pConfig->vtable->outputBusCount != 1) { - return MA_INVALID_ARGS; /* Passthrough nodes must have exactly 1 output bus and either 0 or 1 input bus. */ - } - - if (pConfig->pInputChannels[0] != pConfig->pOutputChannels[0]) { - return MA_INVALID_ARGS; /* Passthrough nodes must have the same number of channels between input and output nodes. */ - } - } - - - *pInputBusCount = inputBusCount; - *pOutputBusCount = outputBusCount; - - return MA_SUCCESS; -} - -static ma_result ma_node_get_heap_layout(ma_node_graph* pNodeGraph, const ma_node_config* pConfig, ma_node_heap_layout* pHeapLayout) -{ - ma_result result; - ma_uint32 inputBusCount; - ma_uint32 outputBusCount; - - MA_ASSERT(pHeapLayout != NULL); - - MA_ZERO_OBJECT(pHeapLayout); - - if (pConfig == NULL || pConfig->vtable == NULL || pConfig->vtable->onProcess == NULL) { - return MA_INVALID_ARGS; - } - - result = ma_node_translate_bus_counts(pConfig, &inputBusCount, &outputBusCount); - if (result != MA_SUCCESS) { - return result; - } - - pHeapLayout->sizeInBytes = 0; - - /* Input buses. */ - if (inputBusCount > MA_MAX_NODE_LOCAL_BUS_COUNT) { - pHeapLayout->inputBusOffset = pHeapLayout->sizeInBytes; - pHeapLayout->sizeInBytes += ma_align_64(sizeof(ma_node_input_bus) * inputBusCount); - } else { - pHeapLayout->inputBusOffset = MA_SIZE_MAX; /* MA_SIZE_MAX indicates that no heap allocation is required for the input bus. */ - } - - /* Output buses. */ - if (outputBusCount > MA_MAX_NODE_LOCAL_BUS_COUNT) { - pHeapLayout->outputBusOffset = pHeapLayout->sizeInBytes; - pHeapLayout->sizeInBytes += ma_align_64(sizeof(ma_node_output_bus) * outputBusCount); - } else { - pHeapLayout->outputBusOffset = MA_SIZE_MAX; - } - - /* - Cached audio data. - - We need to allocate memory for caching both input and output data. We have an optimization - where no caching is necessary for specific conditions: - - - The node has 0 inputs and 1 output. - - When a node meets the above conditions, no cache is allocated. - - The size choice for this buffer is a little bit finicky. We don't want to be too wasteful by - allocating too much, but at the same time we want it be large enough so that enough frames can - be processed for each call to ma_node_read_pcm_frames() so that it keeps things efficient. For - now I'm going with 10ms @ 48K which is 480 frames per bus. This is configurable at compile - time. It might also be worth investigating whether or not this can be configured at run time. - */ - if (inputBusCount == 0 && outputBusCount == 1) { - /* Fast path. No cache needed. */ - pHeapLayout->cachedDataOffset = MA_SIZE_MAX; - } else { - /* Slow path. Cache needed. */ - size_t cachedDataSizeInBytes = 0; - ma_uint32 cacheCapInFrames; - ma_uint32 iBus; - - /* The capacity of the cache is based on our callback processing size. */ - cacheCapInFrames = ma_node_config_get_cache_size_in_frames(pConfig, pNodeGraph); - - for (iBus = 0; iBus < inputBusCount; iBus += 1) { - cachedDataSizeInBytes += cacheCapInFrames * ma_get_bytes_per_frame(ma_format_f32, pConfig->pInputChannels[iBus]); - } - - for (iBus = 0; iBus < outputBusCount; iBus += 1) { - cachedDataSizeInBytes += cacheCapInFrames * ma_get_bytes_per_frame(ma_format_f32, pConfig->pOutputChannels[iBus]); - } - - pHeapLayout->cachedDataOffset = pHeapLayout->sizeInBytes; - pHeapLayout->sizeInBytes += ma_align_64(cachedDataSizeInBytes); - } - - - /* - Not technically part of the heap, but we can output the input and output bus counts so we can - avoid a redundant call to ma_node_translate_bus_counts(). - */ - pHeapLayout->inputBusCount = inputBusCount; - pHeapLayout->outputBusCount = outputBusCount; - - /* Make sure allocation size is aligned. */ - pHeapLayout->sizeInBytes = ma_align_64(pHeapLayout->sizeInBytes); - - return MA_SUCCESS; -} - -MA_API ma_result ma_node_get_heap_size(ma_node_graph* pNodeGraph, const ma_node_config* pConfig, size_t* pHeapSizeInBytes) -{ - ma_result result; - ma_node_heap_layout heapLayout; - - if (pHeapSizeInBytes == NULL) { - return MA_INVALID_ARGS; - } - - *pHeapSizeInBytes = 0; - - result = ma_node_get_heap_layout(pNodeGraph, pConfig, &heapLayout); - if (result != MA_SUCCESS) { - return result; - } - - *pHeapSizeInBytes = heapLayout.sizeInBytes; - - return MA_SUCCESS; -} - -MA_API ma_result ma_node_init_preallocated(ma_node_graph* pNodeGraph, const ma_node_config* pConfig, void* pHeap, ma_node* pNode) -{ - ma_node_base* pNodeBase = (ma_node_base*)pNode; - ma_result result; - ma_node_heap_layout heapLayout; - ma_uint32 iInputBus; - ma_uint32 iOutputBus; - - if (pNodeBase == NULL) { - return MA_INVALID_ARGS; - } - - MA_ZERO_OBJECT(pNodeBase); - - result = ma_node_get_heap_layout(pNodeGraph, pConfig, &heapLayout); - if (result != MA_SUCCESS) { - return result; - } - - pNodeBase->_pHeap = pHeap; - MA_ZERO_MEMORY(pHeap, heapLayout.sizeInBytes); - - pNodeBase->pNodeGraph = pNodeGraph; - pNodeBase->vtable = pConfig->vtable; - pNodeBase->state = pConfig->initialState; - pNodeBase->stateTimes[ma_node_state_started] = 0; - pNodeBase->stateTimes[ma_node_state_stopped] = (ma_uint64)(ma_int64)-1; /* Weird casting for VC6 compatibility. */ - pNodeBase->inputBusCount = heapLayout.inputBusCount; - pNodeBase->outputBusCount = heapLayout.outputBusCount; - - if (heapLayout.inputBusOffset != MA_SIZE_MAX) { - pNodeBase->pInputBuses = (ma_node_input_bus*)ma_offset_ptr(pHeap, heapLayout.inputBusOffset); - } else { - pNodeBase->pInputBuses = pNodeBase->_inputBuses; - } - - if (heapLayout.outputBusOffset != MA_SIZE_MAX) { - pNodeBase->pOutputBuses = (ma_node_output_bus*)ma_offset_ptr(pHeap, heapLayout.outputBusOffset); - } else { - pNodeBase->pOutputBuses = pNodeBase->_outputBuses; - } - - if (heapLayout.cachedDataOffset != MA_SIZE_MAX) { - pNodeBase->pCachedData = (float*)ma_offset_ptr(pHeap, heapLayout.cachedDataOffset); - pNodeBase->cachedDataCapInFramesPerBus = ma_node_config_get_cache_size_in_frames(pConfig, pNodeGraph); - } else { - pNodeBase->pCachedData = NULL; - } - - - /* We need to run an initialization step for each input and output bus. */ - for (iInputBus = 0; iInputBus < ma_node_get_input_bus_count(pNodeBase); iInputBus += 1) { - result = ma_node_input_bus_init(pConfig->pInputChannels[iInputBus], &pNodeBase->pInputBuses[iInputBus]); - if (result != MA_SUCCESS) { - return result; - } - } - - for (iOutputBus = 0; iOutputBus < ma_node_get_output_bus_count(pNodeBase); iOutputBus += 1) { - result = ma_node_output_bus_init(pNodeBase, iOutputBus, pConfig->pOutputChannels[iOutputBus], &pNodeBase->pOutputBuses[iOutputBus]); - if (result != MA_SUCCESS) { - return result; - } - } - - - /* The cached data needs to be initialized to silence (or a sine wave tone if we're debugging). */ - if (pNodeBase->pCachedData != NULL) { - ma_uint32 iBus; - - #if 1 /* Toggle this between 0 and 1 to turn debugging on or off. 1 = fill with a sine wave for debugging; 0 = fill with silence. */ - /* For safety we'll go ahead and default the buffer to silence. */ - for (iBus = 0; iBus < ma_node_get_input_bus_count(pNodeBase); iBus += 1) { - ma_silence_pcm_frames(ma_node_get_cached_input_ptr(pNode, iBus), pNodeBase->cachedDataCapInFramesPerBus, ma_format_f32, ma_node_input_bus_get_channels(&pNodeBase->pInputBuses[iBus])); - } - for (iBus = 0; iBus < ma_node_get_output_bus_count(pNodeBase); iBus += 1) { - ma_silence_pcm_frames(ma_node_get_cached_output_ptr(pNode, iBus), pNodeBase->cachedDataCapInFramesPerBus, ma_format_f32, ma_node_output_bus_get_channels(&pNodeBase->pOutputBuses[iBus])); - } - #else - /* For debugging. Default to a sine wave. */ - for (iBus = 0; iBus < ma_node_get_input_bus_count(pNodeBase); iBus += 1) { - ma_debug_fill_pcm_frames_with_sine_wave(ma_node_get_cached_input_ptr(pNode, iBus), pNodeBase->cachedDataCapInFramesPerBus, ma_format_f32, ma_node_input_bus_get_channels(&pNodeBase->pInputBuses[iBus]), 48000); - } - for (iBus = 0; iBus < ma_node_get_output_bus_count(pNodeBase); iBus += 1) { - ma_debug_fill_pcm_frames_with_sine_wave(ma_node_get_cached_output_ptr(pNode, iBus), pNodeBase->cachedDataCapInFramesPerBus, ma_format_f32, ma_node_output_bus_get_channels(&pNodeBase->pOutputBuses[iBus]), 48000); - } - #endif - } - - return MA_SUCCESS; -} - -MA_API ma_result ma_node_init(ma_node_graph* pNodeGraph, const ma_node_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_node* pNode) -{ - ma_result result; - size_t heapSizeInBytes; - void* pHeap; - - result = ma_node_get_heap_size(pNodeGraph, pConfig, &heapSizeInBytes); - if (result != MA_SUCCESS) { - return result; - } - - if (heapSizeInBytes > 0) { - pHeap = ma_malloc(heapSizeInBytes, pAllocationCallbacks); - if (pHeap == NULL) { - return MA_OUT_OF_MEMORY; - } - } else { - pHeap = NULL; - } - - result = ma_node_init_preallocated(pNodeGraph, pConfig, pHeap, pNode); - if (result != MA_SUCCESS) { - ma_free(pHeap, pAllocationCallbacks); - return result; - } - - ((ma_node_base*)pNode)->_ownsHeap = MA_TRUE; - return MA_SUCCESS; -} - -MA_API void ma_node_uninit(ma_node* pNode, const ma_allocation_callbacks* pAllocationCallbacks) -{ - ma_node_base* pNodeBase = (ma_node_base*)pNode; - - if (pNodeBase == NULL) { - return; - } - - /* - The first thing we need to do is fully detach the node. This will detach all inputs and - outputs. We need to do this first because it will sever the connection with the node graph and - allow us to complete uninitialization without needing to worry about thread-safety with the - audio thread. The detachment process will wait for any local processing of the node to finish. - */ - ma_node_detach_full(pNode); - - /* - At this point the node should be completely unreferenced by the node graph and we can finish up - the uninitialization process without needing to worry about thread-safety. - */ - if (pNodeBase->_ownsHeap) { - ma_free(pNodeBase->_pHeap, pAllocationCallbacks); - } -} - -MA_API ma_node_graph* ma_node_get_node_graph(const ma_node* pNode) -{ - if (pNode == NULL) { - return NULL; - } - - return ((const ma_node_base*)pNode)->pNodeGraph; -} - -MA_API ma_uint32 ma_node_get_input_bus_count(const ma_node* pNode) -{ - if (pNode == NULL) { - return 0; - } - - return ((ma_node_base*)pNode)->inputBusCount; -} - -MA_API ma_uint32 ma_node_get_output_bus_count(const ma_node* pNode) -{ - if (pNode == NULL) { - return 0; - } - - return ((ma_node_base*)pNode)->outputBusCount; -} - - -MA_API ma_uint32 ma_node_get_input_channels(const ma_node* pNode, ma_uint32 inputBusIndex) -{ - const ma_node_base* pNodeBase = (const ma_node_base*)pNode; - - if (pNode == NULL) { - return 0; - } - - if (inputBusIndex >= ma_node_get_input_bus_count(pNode)) { - return 0; /* Invalid bus index. */ - } - - return ma_node_input_bus_get_channels(&pNodeBase->pInputBuses[inputBusIndex]); -} - -MA_API ma_uint32 ma_node_get_output_channels(const ma_node* pNode, ma_uint32 outputBusIndex) -{ - const ma_node_base* pNodeBase = (const ma_node_base*)pNode; - - if (pNode == NULL) { - return 0; - } - - if (outputBusIndex >= ma_node_get_output_bus_count(pNode)) { - return 0; /* Invalid bus index. */ - } - - return ma_node_output_bus_get_channels(&pNodeBase->pOutputBuses[outputBusIndex]); -} - - -static ma_result ma_node_detach_full(ma_node* pNode) -{ - ma_node_base* pNodeBase = (ma_node_base*)pNode; - ma_uint32 iInputBus; - - if (pNodeBase == NULL) { - return MA_INVALID_ARGS; - } - - /* - Make sure the node is completely detached first. This will not return until the output bus is - guaranteed to no longer be referenced by the audio thread. - */ - ma_node_detach_all_output_buses(pNode); - - /* - At this point all output buses will have been detached from the graph and we can be guaranteed - that none of its input nodes will be getting processed by the graph. We can detach these - without needing to worry about the audio thread touching them. - */ - for (iInputBus = 0; iInputBus < ma_node_get_input_bus_count(pNode); iInputBus += 1) { - ma_node_input_bus* pInputBus; - ma_node_output_bus* pOutputBus; - - pInputBus = &pNodeBase->pInputBuses[iInputBus]; - - /* - This is important. We cannot be using ma_node_input_bus_first() or ma_node_input_bus_next(). Those - functions are specifically for the audio thread. We'll instead just manually iterate using standard - linked list logic. We don't need to worry about the audio thread referencing these because the step - above severed the connection to the graph. - */ - for (pOutputBus = (ma_node_output_bus*)ma_atomic_load_ptr(&pInputBus->head.pNext); pOutputBus != NULL; pOutputBus = (ma_node_output_bus*)ma_atomic_load_ptr(&pInputBus->head.pNext)) { - ma_node_detach_output_bus(pOutputBus->pNode, pOutputBus->outputBusIndex); /* This won't do any waiting in practice and should be efficient. */ - } - } - - return MA_SUCCESS; -} - -MA_API ma_result ma_node_detach_output_bus(ma_node* pNode, ma_uint32 outputBusIndex) -{ - ma_result result = MA_SUCCESS; - ma_node_base* pNodeBase = (ma_node_base*)pNode; - ma_node_base* pInputNodeBase; - - if (pNode == NULL) { - return MA_INVALID_ARGS; - } - - if (outputBusIndex >= ma_node_get_output_bus_count(pNode)) { - return MA_INVALID_ARGS; /* Invalid output bus index. */ - } - - /* We need to lock the output bus because we need to inspect the input node and grab its input bus. */ - ma_node_output_bus_lock(&pNodeBase->pOutputBuses[outputBusIndex]); - { - pInputNodeBase = (ma_node_base*)pNodeBase->pOutputBuses[outputBusIndex].pInputNode; - if (pInputNodeBase != NULL) { - ma_node_input_bus_detach__no_output_bus_lock(&pInputNodeBase->pInputBuses[pNodeBase->pOutputBuses[outputBusIndex].inputNodeInputBusIndex], &pNodeBase->pOutputBuses[outputBusIndex]); - } - } - ma_node_output_bus_unlock(&pNodeBase->pOutputBuses[outputBusIndex]); - - return result; -} - -MA_API ma_result ma_node_detach_all_output_buses(ma_node* pNode) -{ - ma_uint32 iOutputBus; - - if (pNode == NULL) { - return MA_INVALID_ARGS; - } - - for (iOutputBus = 0; iOutputBus < ma_node_get_output_bus_count(pNode); iOutputBus += 1) { - ma_node_detach_output_bus(pNode, iOutputBus); - } - - return MA_SUCCESS; -} - -MA_API ma_result ma_node_attach_output_bus(ma_node* pNode, ma_uint32 outputBusIndex, ma_node* pOtherNode, ma_uint32 otherNodeInputBusIndex) -{ - ma_node_base* pNodeBase = (ma_node_base*)pNode; - ma_node_base* pOtherNodeBase = (ma_node_base*)pOtherNode; - - if (pNodeBase == NULL || pOtherNodeBase == NULL) { - return MA_INVALID_ARGS; - } - - if (pNodeBase == pOtherNodeBase) { - return MA_INVALID_OPERATION; /* Cannot attach a node to itself. */ - } - - if (outputBusIndex >= ma_node_get_output_bus_count(pNode) || otherNodeInputBusIndex >= ma_node_get_input_bus_count(pOtherNode)) { - return MA_INVALID_OPERATION; /* Invalid bus index. */ - } - - /* The output channel count of the output node must be the same as the input channel count of the input node. */ - if (ma_node_get_output_channels(pNode, outputBusIndex) != ma_node_get_input_channels(pOtherNode, otherNodeInputBusIndex)) { - return MA_INVALID_OPERATION; /* Channel count is incompatible. */ - } - - /* This will deal with detaching if the output bus is already attached to something. */ - ma_node_input_bus_attach(&pOtherNodeBase->pInputBuses[otherNodeInputBusIndex], &pNodeBase->pOutputBuses[outputBusIndex], pOtherNode, otherNodeInputBusIndex); - - return MA_SUCCESS; -} - -MA_API ma_result ma_node_set_output_bus_volume(ma_node* pNode, ma_uint32 outputBusIndex, float volume) -{ - ma_node_base* pNodeBase = (ma_node_base*)pNode; - - if (pNodeBase == NULL) { - return MA_INVALID_ARGS; - } - - if (outputBusIndex >= ma_node_get_output_bus_count(pNode)) { - return MA_INVALID_ARGS; /* Invalid bus index. */ - } - - return ma_node_output_bus_set_volume(&pNodeBase->pOutputBuses[outputBusIndex], volume); -} - -MA_API float ma_node_get_output_bus_volume(const ma_node* pNode, ma_uint32 outputBusIndex) -{ - const ma_node_base* pNodeBase = (const ma_node_base*)pNode; - - if (pNodeBase == NULL) { - return 0; - } - - if (outputBusIndex >= ma_node_get_output_bus_count(pNode)) { - return 0; /* Invalid bus index. */ - } - - return ma_node_output_bus_get_volume(&pNodeBase->pOutputBuses[outputBusIndex]); -} - -MA_API ma_result ma_node_set_state(ma_node* pNode, ma_node_state state) -{ - ma_node_base* pNodeBase = (ma_node_base*)pNode; - - if (pNodeBase == NULL) { - return MA_INVALID_ARGS; - } - - ma_atomic_exchange_i32(&pNodeBase->state, state); - - return MA_SUCCESS; -} - -MA_API ma_node_state ma_node_get_state(const ma_node* pNode) -{ - const ma_node_base* pNodeBase = (const ma_node_base*)pNode; - - if (pNodeBase == NULL) { - return ma_node_state_stopped; - } - - return (ma_node_state)ma_atomic_load_i32(&pNodeBase->state); -} - -MA_API ma_result ma_node_set_state_time(ma_node* pNode, ma_node_state state, ma_uint64 globalTime) -{ - if (pNode == NULL) { - return MA_INVALID_ARGS; - } - - /* Validation check for safety since we'll be using this as an index into stateTimes[]. */ - if (state != ma_node_state_started && state != ma_node_state_stopped) { - return MA_INVALID_ARGS; - } - - ma_atomic_exchange_64(&((ma_node_base*)pNode)->stateTimes[state], globalTime); - - return MA_SUCCESS; -} - -MA_API ma_uint64 ma_node_get_state_time(const ma_node* pNode, ma_node_state state) -{ - if (pNode == NULL) { - return 0; - } - - /* Validation check for safety since we'll be using this as an index into stateTimes[]. */ - if (state != ma_node_state_started && state != ma_node_state_stopped) { - return 0; - } - - return ma_atomic_load_64(&((ma_node_base*)pNode)->stateTimes[state]); -} - -MA_API ma_node_state ma_node_get_state_by_time(const ma_node* pNode, ma_uint64 globalTime) -{ - if (pNode == NULL) { - return ma_node_state_stopped; - } - - return ma_node_get_state_by_time_range(pNode, globalTime, globalTime); -} - -MA_API ma_node_state ma_node_get_state_by_time_range(const ma_node* pNode, ma_uint64 globalTimeBeg, ma_uint64 globalTimeEnd) -{ - ma_node_state state; - - if (pNode == NULL) { - return ma_node_state_stopped; - } - - state = ma_node_get_state(pNode); - - /* An explicitly stopped node is always stopped. */ - if (state == ma_node_state_stopped) { - return ma_node_state_stopped; - } - - /* - Getting here means the node is marked as started, but it may still not be truly started due to - its start time not having been reached yet. Also, the stop time may have also been reached in - which case it'll be considered stopped. - */ - if (ma_node_get_state_time(pNode, ma_node_state_started) > globalTimeBeg) { - return ma_node_state_stopped; /* Start time has not yet been reached. */ - } - - if (ma_node_get_state_time(pNode, ma_node_state_stopped) <= globalTimeEnd) { - return ma_node_state_stopped; /* Stop time has been reached. */ - } - - /* Getting here means the node is marked as started and is within its start/stop times. */ - return ma_node_state_started; -} - -MA_API ma_uint64 ma_node_get_time(const ma_node* pNode) -{ - if (pNode == NULL) { - return 0; - } - - return ma_atomic_load_64(&((ma_node_base*)pNode)->localTime); -} - -MA_API ma_result ma_node_set_time(ma_node* pNode, ma_uint64 localTime) -{ - if (pNode == NULL) { - return MA_INVALID_ARGS; - } - - ma_atomic_exchange_64(&((ma_node_base*)pNode)->localTime, localTime); - - return MA_SUCCESS; -} - - - -static void ma_node_process_pcm_frames_internal(ma_node* pNode, const float** ppFramesIn, ma_uint32* pFrameCountIn, float** ppFramesOut, ma_uint32* pFrameCountOut) -{ - ma_node_base* pNodeBase = (ma_node_base*)pNode; - - MA_ASSERT(pNode != NULL); - - if (pNodeBase->vtable->onProcess) { - pNodeBase->vtable->onProcess(pNode, ppFramesIn, pFrameCountIn, ppFramesOut, pFrameCountOut); - } -} - -static ma_result ma_node_read_pcm_frames(ma_node* pNode, ma_uint32 outputBusIndex, float* pFramesOut, ma_uint32 frameCount, ma_uint32* pFramesRead, ma_uint64 globalTime) -{ - ma_node_base* pNodeBase = (ma_node_base*)pNode; - ma_result result = MA_SUCCESS; - ma_uint32 iInputBus; - ma_uint32 iOutputBus; - ma_uint32 inputBusCount; - ma_uint32 outputBusCount; - ma_uint32 totalFramesRead = 0; - float* ppFramesIn[MA_MAX_NODE_BUS_COUNT]; - float* ppFramesOut[MA_MAX_NODE_BUS_COUNT]; - ma_uint64 globalTimeBeg; - ma_uint64 globalTimeEnd; - ma_uint64 startTime; - ma_uint64 stopTime; - ma_uint32 timeOffsetBeg; - ma_uint32 timeOffsetEnd; - ma_uint32 frameCountIn; - ma_uint32 frameCountOut; - - /* - pFramesRead is mandatory. It must be used to determine how many frames were read. It's normal and - expected that the number of frames read may be different to that requested. Therefore, the caller - must look at this value to correctly determine how many frames were read. - */ - MA_ASSERT(pFramesRead != NULL); /* <-- If you've triggered this assert, you're using this function wrong. You *must* use this variable and inspect it after the call returns. */ - if (pFramesRead == NULL) { - return MA_INVALID_ARGS; - } - - *pFramesRead = 0; /* Safety. */ - - if (pNodeBase == NULL) { - return MA_INVALID_ARGS; - } - - if (outputBusIndex >= ma_node_get_output_bus_count(pNodeBase)) { - return MA_INVALID_ARGS; /* Invalid output bus index. */ - } - - /* Don't do anything if we're in a stopped state. */ - if (ma_node_get_state_by_time_range(pNode, globalTime, globalTime + frameCount) != ma_node_state_started) { - return MA_SUCCESS; /* We're in a stopped state. This is not an error - we just need to not read anything. */ - } - - - globalTimeBeg = globalTime; - globalTimeEnd = globalTime + frameCount; - startTime = ma_node_get_state_time(pNode, ma_node_state_started); - stopTime = ma_node_get_state_time(pNode, ma_node_state_stopped); - - /* - At this point we know that we are inside our start/stop times. However, we may need to adjust - our frame count and output pointer to accommodate since we could be straddling the time period - that this function is getting called for. - - It's possible (and likely) that the start time does not line up with the output buffer. We - therefore need to offset it by a number of frames to accommodate. The same thing applies for - the stop time. - */ - timeOffsetBeg = (globalTimeBeg < startTime) ? (ma_uint32)(globalTimeEnd - startTime) : 0; - timeOffsetEnd = (globalTimeEnd > stopTime) ? (ma_uint32)(globalTimeEnd - stopTime) : 0; - - /* Trim based on the start offset. We need to silence the start of the buffer. */ - if (timeOffsetBeg > 0) { - ma_silence_pcm_frames(pFramesOut, timeOffsetBeg, ma_format_f32, ma_node_get_output_channels(pNode, outputBusIndex)); - pFramesOut += timeOffsetBeg * ma_node_get_output_channels(pNode, outputBusIndex); - frameCount -= timeOffsetBeg; - } - - /* Trim based on the end offset. We don't need to silence the tail section because we'll just have a reduced value written to pFramesRead. */ - if (timeOffsetEnd > 0) { - frameCount -= timeOffsetEnd; - } - - - /* We run on different paths depending on the bus counts. */ - inputBusCount = ma_node_get_input_bus_count(pNode); - outputBusCount = ma_node_get_output_bus_count(pNode); - - /* - Run a simplified path when there are no inputs and one output. In this case there's nothing to - actually read and we can go straight to output. This is a very common scenario because the vast - majority of data source nodes will use this setup so this optimization I think is worthwhile. - */ - if (inputBusCount == 0 && outputBusCount == 1) { - /* Fast path. No need to read from input and no need for any caching. */ - frameCountIn = 0; - frameCountOut = frameCount; /* Just read as much as we can. The callback will return what was actually read. */ - - ppFramesOut[0] = pFramesOut; - - /* - If it's a passthrough we won't be expecting the callback to output anything, so we'll - need to pre-silence the output buffer. - */ - if ((pNodeBase->vtable->flags & MA_NODE_FLAG_PASSTHROUGH) != 0) { - ma_silence_pcm_frames(pFramesOut, frameCount, ma_format_f32, ma_node_get_output_channels(pNode, outputBusIndex)); - } - - ma_node_process_pcm_frames_internal(pNode, NULL, &frameCountIn, ppFramesOut, &frameCountOut); - totalFramesRead = frameCountOut; - } else { - /* Slow path. Need to read input data. */ - if ((pNodeBase->vtable->flags & MA_NODE_FLAG_PASSTHROUGH) != 0) { - /* - Fast path. We're running a passthrough. We need to read directly into the output buffer, but - still fire the callback so that event handling and trigger nodes can do their thing. Since - it's a passthrough there's no need for any kind of caching logic. - */ - MA_ASSERT(outputBusCount == inputBusCount); - MA_ASSERT(outputBusCount == 1); - MA_ASSERT(outputBusIndex == 0); - - /* We just read directly from input bus to output buffer, and then afterwards fire the callback. */ - ppFramesOut[0] = pFramesOut; - ppFramesIn[0] = ppFramesOut[0]; - - result = ma_node_input_bus_read_pcm_frames(pNodeBase, &pNodeBase->pInputBuses[0], ppFramesIn[0], frameCount, &totalFramesRead, globalTime); - if (result == MA_SUCCESS) { - /* Even though it's a passthrough, we still need to fire the callback. */ - frameCountIn = totalFramesRead; - frameCountOut = totalFramesRead; - - if (totalFramesRead > 0) { - ma_node_process_pcm_frames_internal(pNode, (const float**)ppFramesIn, &frameCountIn, ppFramesOut, &frameCountOut); /* From GCC: expected 'const float **' but argument is of type 'float **'. Shouldn't this be implicit? Explicit cast to silence the warning. */ - } - - /* - A passthrough should never have modified the input and output frame counts. If you're - triggering these asserts you need to fix your processing callback. - */ - MA_ASSERT(frameCountIn == totalFramesRead); - MA_ASSERT(frameCountOut == totalFramesRead); - } - } else { - /* Slow path. Need to do caching. */ - ma_uint32 framesToProcessIn; - ma_uint32 framesToProcessOut; - ma_bool32 consumeNullInput = MA_FALSE; - - /* - We use frameCount as a basis for the number of frames to read since that's what's being - requested, however we still need to clamp it to whatever can fit in the cache. - - This will also be used as the basis for determining how many input frames to read. This is - not ideal because it can result in too many input frames being read which introduces latency. - To solve this, nodes can implement an optional callback called onGetRequiredInputFrameCount - which is used as hint to miniaudio as to how many input frames it needs to read at a time. This - callback is completely optional, and if it's not set, miniaudio will assume `frameCount`. - - This function will be called multiple times for each period of time, once for each output node. - We cannot read from each input node each time this function is called. Instead we need to check - whether or not this is first output bus to be read from for this time period, and if so, read - from our input data. - - To determine whether or not we're ready to read data, we check a flag. There will be one flag - for each output. When the flag is set, it means data has been read previously and that we're - ready to advance time forward for our input nodes by reading fresh data. - */ - framesToProcessOut = frameCount; - if (framesToProcessOut > pNodeBase->cachedDataCapInFramesPerBus) { - framesToProcessOut = pNodeBase->cachedDataCapInFramesPerBus; - } - - framesToProcessIn = frameCount; - if (pNodeBase->vtable->onGetRequiredInputFrameCount) { - pNodeBase->vtable->onGetRequiredInputFrameCount(pNode, framesToProcessOut, &framesToProcessIn); /* <-- It does not matter if this fails. */ - } - if (framesToProcessIn > pNodeBase->cachedDataCapInFramesPerBus) { - framesToProcessIn = pNodeBase->cachedDataCapInFramesPerBus; - } - - - MA_ASSERT(framesToProcessIn <= 0xFFFF); - MA_ASSERT(framesToProcessOut <= 0xFFFF); - - if (ma_node_output_bus_has_read(&pNodeBase->pOutputBuses[outputBusIndex])) { - /* Getting here means we need to do another round of processing. */ - pNodeBase->cachedFrameCountOut = 0; - - for (;;) { - frameCountOut = 0; - - /* - We need to prepare our output frame pointers for processing. In the same iteration we need - to mark every output bus as unread so that future calls to this function for different buses - for the current time period don't pull in data when they should instead be reading from cache. - */ - for (iOutputBus = 0; iOutputBus < outputBusCount; iOutputBus += 1) { - ma_node_output_bus_set_has_read(&pNodeBase->pOutputBuses[iOutputBus], MA_FALSE); /* <-- This is what tells the next calls to this function for other output buses for this time period to read from cache instead of pulling in more data. */ - ppFramesOut[iOutputBus] = ma_node_get_cached_output_ptr(pNode, iOutputBus); - } - - /* We only need to read from input buses if there isn't already some data in the cache. */ - if (pNodeBase->cachedFrameCountIn == 0) { - ma_uint32 maxFramesReadIn = 0; - - /* Here is where we pull in data from the input buses. This is what will trigger an advance in time. */ - for (iInputBus = 0; iInputBus < inputBusCount; iInputBus += 1) { - ma_uint32 framesRead; - - /* The first thing to do is get the offset within our bulk allocation to store this input data. */ - ppFramesIn[iInputBus] = ma_node_get_cached_input_ptr(pNode, iInputBus); - - /* Once we've determined our destination pointer we can read. Note that we must inspect the number of frames read and fill any leftovers with silence for safety. */ - result = ma_node_input_bus_read_pcm_frames(pNodeBase, &pNodeBase->pInputBuses[iInputBus], ppFramesIn[iInputBus], framesToProcessIn, &framesRead, globalTime); - if (result != MA_SUCCESS) { - /* It doesn't really matter if we fail because we'll just fill with silence. */ - framesRead = 0; /* Just for safety, but I don't think it's really needed. */ - } - - /* TODO: Minor optimization opportunity here. If no frames were read and the buffer is already filled with silence, no need to re-silence it. */ - /* Any leftover frames need to silenced for safety. */ - if (framesRead < framesToProcessIn) { - ma_silence_pcm_frames(ppFramesIn[iInputBus] + (framesRead * ma_node_get_input_channels(pNodeBase, iInputBus)), (framesToProcessIn - framesRead), ma_format_f32, ma_node_get_input_channels(pNodeBase, iInputBus)); - } - - maxFramesReadIn = ma_max(maxFramesReadIn, framesRead); - } - - /* This was a fresh load of input data so reset our consumption counter. */ - pNodeBase->consumedFrameCountIn = 0; - - /* - We don't want to keep processing if there's nothing to process, so set the number of cached - input frames to the maximum number we read from each attachment (the lesser will be padded - with silence). If we didn't read anything, this will be set to 0 and the entire buffer will - have been assigned to silence. This being equal to 0 is an important property for us because - it allows us to detect when NULL can be passed into the processing callback for the input - buffer for the purpose of continuous processing. - */ - pNodeBase->cachedFrameCountIn = (ma_uint16)maxFramesReadIn; - } else { - /* We don't need to read anything, but we do need to prepare our input frame pointers. */ - for (iInputBus = 0; iInputBus < inputBusCount; iInputBus += 1) { - ppFramesIn[iInputBus] = ma_node_get_cached_input_ptr(pNode, iInputBus) + (pNodeBase->consumedFrameCountIn * ma_node_get_input_channels(pNodeBase, iInputBus)); - } - } - - /* - At this point we have our input data so now we need to do some processing. Sneaky little - optimization here - we can set the pointer to the output buffer for this output bus so - that the final copy into the output buffer is done directly by onProcess(). - */ - if (pFramesOut != NULL) { - ppFramesOut[outputBusIndex] = ma_offset_pcm_frames_ptr_f32(pFramesOut, pNodeBase->cachedFrameCountOut, ma_node_get_output_channels(pNode, outputBusIndex)); - } - - - /* Give the processing function the entire capacity of the output buffer. */ - frameCountOut = (framesToProcessOut - pNodeBase->cachedFrameCountOut); - - /* - We need to treat nodes with continuous processing a little differently. For these ones, - we always want to fire the callback with the requested number of frames, regardless of - pNodeBase->cachedFrameCountIn, which could be 0. Also, we want to check if we can pass - in NULL for the input buffer to the callback. - */ - if ((pNodeBase->vtable->flags & MA_NODE_FLAG_CONTINUOUS_PROCESSING) != 0) { - /* We're using continuous processing. Make sure we specify the whole frame count at all times. */ - frameCountIn = framesToProcessIn; /* Give the processing function as much input data as we've got in the buffer, including any silenced padding from short reads. */ - - if ((pNodeBase->vtable->flags & MA_NODE_FLAG_ALLOW_NULL_INPUT) != 0 && pNodeBase->consumedFrameCountIn == 0 && pNodeBase->cachedFrameCountIn == 0) { - consumeNullInput = MA_TRUE; - } else { - consumeNullInput = MA_FALSE; - } - - /* - Since we're using continuous processing we're always passing in a full frame count - regardless of how much input data was read. If this is greater than what we read as - input, we'll end up with an underflow. We instead need to make sure our cached frame - count is set to the number of frames we'll be passing to the data callback. Not - doing this will result in an underflow when we "consume" the cached data later on. - - Note that this check needs to be done after the "consumeNullInput" check above because - we use the property of cachedFrameCountIn being 0 to determine whether or not we - should be passing in a null pointer to the processing callback for when the node is - configured with MA_NODE_FLAG_ALLOW_NULL_INPUT. - */ - if (pNodeBase->cachedFrameCountIn < (ma_uint16)frameCountIn) { - pNodeBase->cachedFrameCountIn = (ma_uint16)frameCountIn; - } - } else { - frameCountIn = pNodeBase->cachedFrameCountIn; /* Give the processing function as much valid input data as we've got. */ - consumeNullInput = MA_FALSE; - } - - /* - Process data slightly differently depending on whether or not we're consuming NULL - input (checked just above). - */ - if (consumeNullInput) { - ma_node_process_pcm_frames_internal(pNode, NULL, &frameCountIn, ppFramesOut, &frameCountOut); - } else { - /* - We want to skip processing if there's no input data, but we can only do that safely if - we know that there is no chance of any output frames being produced. If continuous - processing is being used, this won't be a problem because the input frame count will - always be non-0. However, if continuous processing is *not* enabled and input and output - data is processed at different rates, we still need to process that last input frame - because there could be a few excess output frames needing to be produced from cached - data. The `MA_NODE_FLAG_DIFFERENT_PROCESSING_RATES` flag is used as the indicator for - determining whether or not we need to process the node even when there are no input - frames available right now. - */ - if (frameCountIn > 0 || (pNodeBase->vtable->flags & MA_NODE_FLAG_DIFFERENT_PROCESSING_RATES) != 0) { - ma_node_process_pcm_frames_internal(pNode, (const float**)ppFramesIn, &frameCountIn, ppFramesOut, &frameCountOut); /* From GCC: expected 'const float **' but argument is of type 'float **'. Shouldn't this be implicit? Explicit cast to silence the warning. */ - } else { - frameCountOut = 0; /* No data was processed. */ - } - } - - /* - Thanks to our sneaky optimization above we don't need to do any data copying directly into - the output buffer - the onProcess() callback just did that for us. We do, however, need to - apply the number of input and output frames that were processed. Note that due to continuous - processing above, we need to do explicit checks here. If we just consumed a NULL input - buffer it means that no actual input data was processed from the internal buffers and we - don't want to be modifying any counters. - */ - if (consumeNullInput == MA_FALSE) { - pNodeBase->consumedFrameCountIn += (ma_uint16)frameCountIn; - pNodeBase->cachedFrameCountIn -= (ma_uint16)frameCountIn; - } - - /* The cached output frame count is always equal to what we just read. */ - pNodeBase->cachedFrameCountOut += (ma_uint16)frameCountOut; - - /* If we couldn't process any data, we're done. The loop needs to be terminated here or else we'll get stuck in a loop. */ - if (pNodeBase->cachedFrameCountOut == framesToProcessOut || (frameCountOut == 0 && frameCountIn == 0)) { - break; - } - } - } else { - /* - We're not needing to read anything from the input buffer so just read directly from our - already-processed data. - */ - if (pFramesOut != NULL) { - ma_copy_pcm_frames(pFramesOut, ma_node_get_cached_output_ptr(pNodeBase, outputBusIndex), pNodeBase->cachedFrameCountOut, ma_format_f32, ma_node_get_output_channels(pNodeBase, outputBusIndex)); - } - } - - /* The number of frames read is always equal to the number of cached output frames. */ - totalFramesRead = pNodeBase->cachedFrameCountOut; - - /* Now that we've read the data, make sure our read flag is set. */ - ma_node_output_bus_set_has_read(&pNodeBase->pOutputBuses[outputBusIndex], MA_TRUE); - } - } - - /* Apply volume, if necessary. */ - ma_apply_volume_factor_f32(pFramesOut, totalFramesRead * ma_node_get_output_channels(pNodeBase, outputBusIndex), ma_node_output_bus_get_volume(&pNodeBase->pOutputBuses[outputBusIndex])); - - /* Advance our local time forward. */ - ma_atomic_fetch_add_64(&pNodeBase->localTime, (ma_uint64)totalFramesRead); - - *pFramesRead = totalFramesRead + timeOffsetBeg; /* Must include the silenced section at the start of the buffer. */ - return result; -} - - - - -/* Data source node. */ -MA_API ma_data_source_node_config ma_data_source_node_config_init(ma_data_source* pDataSource) -{ - ma_data_source_node_config config; - - MA_ZERO_OBJECT(&config); - config.nodeConfig = ma_node_config_init(); - config.pDataSource = pDataSource; - - return config; -} - - -static void ma_data_source_node_process_pcm_frames(ma_node* pNode, const float** ppFramesIn, ma_uint32* pFrameCountIn, float** ppFramesOut, ma_uint32* pFrameCountOut) -{ - ma_data_source_node* pDataSourceNode = (ma_data_source_node*)pNode; - ma_format format; - ma_uint32 channels; - ma_uint32 frameCount; - ma_uint64 framesRead = 0; - - MA_ASSERT(pDataSourceNode != NULL); - MA_ASSERT(pDataSourceNode->pDataSource != NULL); - MA_ASSERT(ma_node_get_input_bus_count(pDataSourceNode) == 0); - MA_ASSERT(ma_node_get_output_bus_count(pDataSourceNode) == 1); - - /* We don't want to read from ppFramesIn at all. Instead we read from the data source. */ - (void)ppFramesIn; - (void)pFrameCountIn; - - frameCount = *pFrameCountOut; - - /* miniaudio should never be calling this with a frame count of zero. */ - MA_ASSERT(frameCount > 0); - - if (ma_data_source_get_data_format(pDataSourceNode->pDataSource, &format, &channels, NULL, NULL, 0) == MA_SUCCESS) { /* <-- Don't care about sample rate here. */ - /* The node graph system requires samples be in floating point format. This is checked in ma_data_source_node_init(). */ - MA_ASSERT(format == ma_format_f32); - (void)format; /* Just to silence some static analysis tools. */ - - ma_data_source_read_pcm_frames(pDataSourceNode->pDataSource, ppFramesOut[0], frameCount, &framesRead); - } - - *pFrameCountOut = (ma_uint32)framesRead; -} - -static ma_node_vtable g_ma_data_source_node_vtable = -{ - ma_data_source_node_process_pcm_frames, - NULL, /* onGetRequiredInputFrameCount */ - 0, /* 0 input buses. */ - 1, /* 1 output bus. */ - 0 -}; - -MA_API ma_result ma_data_source_node_init(ma_node_graph* pNodeGraph, const ma_data_source_node_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_data_source_node* pDataSourceNode) -{ - ma_result result; - ma_format format; /* For validating the format, which must be ma_format_f32. */ - ma_uint32 channels; /* For specifying the channel count of the output bus. */ - ma_node_config baseConfig; - - if (pDataSourceNode == NULL) { - return MA_INVALID_ARGS; - } - - MA_ZERO_OBJECT(pDataSourceNode); - - if (pConfig == NULL) { - return MA_INVALID_ARGS; - } - - result = ma_data_source_get_data_format(pConfig->pDataSource, &format, &channels, NULL, NULL, 0); /* Don't care about sample rate. This will check pDataSource for NULL. */ - if (result != MA_SUCCESS) { - return result; - } - - MA_ASSERT(format == ma_format_f32); /* <-- If you've triggered this it means your data source is not outputting floating-point samples. You must configure your data source to use ma_format_f32. */ - if (format != ma_format_f32) { - return MA_INVALID_ARGS; /* Invalid format. */ - } - - /* The channel count is defined by the data source. If the caller has manually changed the channels we just ignore it. */ - baseConfig = pConfig->nodeConfig; - baseConfig.vtable = &g_ma_data_source_node_vtable; /* Explicitly set the vtable here to prevent callers from setting it incorrectly. */ - - /* - The channel count is defined by the data source. It is invalid for the caller to manually set - the channel counts in the config. `ma_data_source_node_config_init()` will have defaulted the - channel count pointer to NULL which is how it must remain. If you trigger any of these asserts - it means you're explicitly setting the channel count. Instead, configure the output channel - count of your data source to be the necessary channel count. - */ - if (baseConfig.pOutputChannels != NULL) { - return MA_INVALID_ARGS; - } - - baseConfig.pOutputChannels = &channels; - - result = ma_node_init(pNodeGraph, &baseConfig, pAllocationCallbacks, &pDataSourceNode->base); - if (result != MA_SUCCESS) { - return result; - } - - pDataSourceNode->pDataSource = pConfig->pDataSource; - - return MA_SUCCESS; -} - -MA_API void ma_data_source_node_uninit(ma_data_source_node* pDataSourceNode, const ma_allocation_callbacks* pAllocationCallbacks) -{ - ma_node_uninit(&pDataSourceNode->base, pAllocationCallbacks); -} - -MA_API ma_result ma_data_source_node_set_looping(ma_data_source_node* pDataSourceNode, ma_bool32 isLooping) -{ - if (pDataSourceNode == NULL) { - return MA_INVALID_ARGS; - } - - return ma_data_source_set_looping(pDataSourceNode->pDataSource, isLooping); -} - -MA_API ma_bool32 ma_data_source_node_is_looping(ma_data_source_node* pDataSourceNode) -{ - if (pDataSourceNode == NULL) { - return MA_FALSE; - } - - return ma_data_source_is_looping(pDataSourceNode->pDataSource); -} - - - -/* Splitter Node. */ -MA_API ma_splitter_node_config ma_splitter_node_config_init(ma_uint32 channels) -{ - ma_splitter_node_config config; - - MA_ZERO_OBJECT(&config); - config.nodeConfig = ma_node_config_init(); - config.channels = channels; - config.outputBusCount = 2; - - return config; -} - - -static void ma_splitter_node_process_pcm_frames(ma_node* pNode, const float** ppFramesIn, ma_uint32* pFrameCountIn, float** ppFramesOut, ma_uint32* pFrameCountOut) -{ - ma_node_base* pNodeBase = (ma_node_base*)pNode; - ma_uint32 iOutputBus; - ma_uint32 channels; - - MA_ASSERT(pNodeBase != NULL); - MA_ASSERT(ma_node_get_input_bus_count(pNodeBase) == 1); - - /* We don't need to consider the input frame count - it'll be the same as the output frame count and we process everything. */ - (void)pFrameCountIn; - - /* NOTE: This assumes the same number of channels for all inputs and outputs. This was checked in ma_splitter_node_init(). */ - channels = ma_node_get_input_channels(pNodeBase, 0); - - /* Splitting is just copying the first input bus and copying it over to each output bus. */ - for (iOutputBus = 0; iOutputBus < ma_node_get_output_bus_count(pNodeBase); iOutputBus += 1) { - ma_copy_pcm_frames(ppFramesOut[iOutputBus], ppFramesIn[0], *pFrameCountOut, ma_format_f32, channels); - } -} - -static ma_node_vtable g_ma_splitter_node_vtable = -{ - ma_splitter_node_process_pcm_frames, - NULL, /* onGetRequiredInputFrameCount */ - 1, /* 1 input bus. */ - MA_NODE_BUS_COUNT_UNKNOWN, /* The output bus count is specified on a per-node basis. */ - 0 -}; - -MA_API ma_result ma_splitter_node_init(ma_node_graph* pNodeGraph, const ma_splitter_node_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_splitter_node* pSplitterNode) -{ - ma_result result; - ma_node_config baseConfig; - ma_uint32 pInputChannels[1]; - ma_uint32 pOutputChannels[MA_MAX_NODE_BUS_COUNT]; - ma_uint32 iOutputBus; - - if (pSplitterNode == NULL) { - return MA_INVALID_ARGS; - } - - MA_ZERO_OBJECT(pSplitterNode); - - if (pConfig == NULL) { - return MA_INVALID_ARGS; - } - - if (pConfig->outputBusCount > MA_MAX_NODE_BUS_COUNT) { - return MA_INVALID_ARGS; /* Too many output buses. */ - } - - /* Splitters require the same number of channels between inputs and outputs. */ - pInputChannels[0] = pConfig->channels; - for (iOutputBus = 0; iOutputBus < pConfig->outputBusCount; iOutputBus += 1) { - pOutputChannels[iOutputBus] = pConfig->channels; - } - - baseConfig = pConfig->nodeConfig; - baseConfig.vtable = &g_ma_splitter_node_vtable; - baseConfig.pInputChannels = pInputChannels; - baseConfig.pOutputChannels = pOutputChannels; - baseConfig.outputBusCount = pConfig->outputBusCount; - - result = ma_node_init(pNodeGraph, &baseConfig, pAllocationCallbacks, &pSplitterNode->base); - if (result != MA_SUCCESS) { - return result; /* Failed to initialize the base node. */ - } - - return MA_SUCCESS; -} - -MA_API void ma_splitter_node_uninit(ma_splitter_node* pSplitterNode, const ma_allocation_callbacks* pAllocationCallbacks) -{ - ma_node_uninit(pSplitterNode, pAllocationCallbacks); -} - - -/* -Biquad Node -*/ -MA_API ma_biquad_node_config ma_biquad_node_config_init(ma_uint32 channels, float b0, float b1, float b2, float a0, float a1, float a2) -{ - ma_biquad_node_config config; - - config.nodeConfig = ma_node_config_init(); - config.biquad = ma_biquad_config_init(ma_format_f32, channels, b0, b1, b2, a0, a1, a2); - - return config; -} - -static void ma_biquad_node_process_pcm_frames(ma_node* pNode, const float** ppFramesIn, ma_uint32* pFrameCountIn, float** ppFramesOut, ma_uint32* pFrameCountOut) -{ - ma_biquad_node* pLPFNode = (ma_biquad_node*)pNode; - - MA_ASSERT(pNode != NULL); - (void)pFrameCountIn; - - ma_biquad_process_pcm_frames(&pLPFNode->biquad, ppFramesOut[0], ppFramesIn[0], *pFrameCountOut); -} - -static ma_node_vtable g_ma_biquad_node_vtable = -{ - ma_biquad_node_process_pcm_frames, - NULL, /* onGetRequiredInputFrameCount */ - 1, /* One input. */ - 1, /* One output. */ - 0 /* Default flags. */ -}; - -MA_API ma_result ma_biquad_node_init(ma_node_graph* pNodeGraph, const ma_biquad_node_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_biquad_node* pNode) -{ - ma_result result; - ma_node_config baseNodeConfig; - - if (pNode == NULL) { - return MA_INVALID_ARGS; - } - - MA_ZERO_OBJECT(pNode); - - if (pConfig == NULL) { - return MA_INVALID_ARGS; - } - - if (pConfig->biquad.format != ma_format_f32) { - return MA_INVALID_ARGS; /* The format must be f32. */ - } - - result = ma_biquad_init(&pConfig->biquad, pAllocationCallbacks, &pNode->biquad); - if (result != MA_SUCCESS) { - return result; - } - - baseNodeConfig = ma_node_config_init(); - baseNodeConfig.vtable = &g_ma_biquad_node_vtable; - baseNodeConfig.pInputChannels = &pConfig->biquad.channels; - baseNodeConfig.pOutputChannels = &pConfig->biquad.channels; - - result = ma_node_init(pNodeGraph, &baseNodeConfig, pAllocationCallbacks, pNode); - if (result != MA_SUCCESS) { - return result; - } - - return result; -} - -MA_API ma_result ma_biquad_node_reinit(const ma_biquad_config* pConfig, ma_biquad_node* pNode) -{ - ma_biquad_node* pLPFNode = (ma_biquad_node*)pNode; - - MA_ASSERT(pNode != NULL); - - return ma_biquad_reinit(pConfig, &pLPFNode->biquad); -} - -MA_API void ma_biquad_node_uninit(ma_biquad_node* pNode, const ma_allocation_callbacks* pAllocationCallbacks) -{ - ma_biquad_node* pLPFNode = (ma_biquad_node*)pNode; - - if (pNode == NULL) { - return; - } - - ma_node_uninit(pNode, pAllocationCallbacks); - ma_biquad_uninit(&pLPFNode->biquad, pAllocationCallbacks); -} - - - -/* -Low Pass Filter Node -*/ -MA_API ma_lpf_node_config ma_lpf_node_config_init(ma_uint32 channels, ma_uint32 sampleRate, double cutoffFrequency, ma_uint32 order) -{ - ma_lpf_node_config config; - - config.nodeConfig = ma_node_config_init(); - config.lpf = ma_lpf_config_init(ma_format_f32, channels, sampleRate, cutoffFrequency, order); - - return config; -} - -static void ma_lpf_node_process_pcm_frames(ma_node* pNode, const float** ppFramesIn, ma_uint32* pFrameCountIn, float** ppFramesOut, ma_uint32* pFrameCountOut) -{ - ma_lpf_node* pLPFNode = (ma_lpf_node*)pNode; - - MA_ASSERT(pNode != NULL); - (void)pFrameCountIn; - - ma_lpf_process_pcm_frames(&pLPFNode->lpf, ppFramesOut[0], ppFramesIn[0], *pFrameCountOut); -} - -static ma_node_vtable g_ma_lpf_node_vtable = -{ - ma_lpf_node_process_pcm_frames, - NULL, /* onGetRequiredInputFrameCount */ - 1, /* One input. */ - 1, /* One output. */ - 0 /* Default flags. */ -}; - -MA_API ma_result ma_lpf_node_init(ma_node_graph* pNodeGraph, const ma_lpf_node_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_lpf_node* pNode) -{ - ma_result result; - ma_node_config baseNodeConfig; - - if (pNode == NULL) { - return MA_INVALID_ARGS; - } - - MA_ZERO_OBJECT(pNode); - - if (pConfig == NULL) { - return MA_INVALID_ARGS; - } - - if (pConfig->lpf.format != ma_format_f32) { - return MA_INVALID_ARGS; /* The format must be f32. */ - } - - result = ma_lpf_init(&pConfig->lpf, pAllocationCallbacks, &pNode->lpf); - if (result != MA_SUCCESS) { - return result; - } - - baseNodeConfig = ma_node_config_init(); - baseNodeConfig.vtable = &g_ma_lpf_node_vtable; - baseNodeConfig.pInputChannels = &pConfig->lpf.channels; - baseNodeConfig.pOutputChannels = &pConfig->lpf.channels; - - result = ma_node_init(pNodeGraph, &baseNodeConfig, pAllocationCallbacks, pNode); - if (result != MA_SUCCESS) { - return result; - } - - return result; -} - -MA_API ma_result ma_lpf_node_reinit(const ma_lpf_config* pConfig, ma_lpf_node* pNode) -{ - ma_lpf_node* pLPFNode = (ma_lpf_node*)pNode; - - if (pNode == NULL) { - return MA_INVALID_ARGS; - } - - return ma_lpf_reinit(pConfig, &pLPFNode->lpf); -} - -MA_API void ma_lpf_node_uninit(ma_lpf_node* pNode, const ma_allocation_callbacks* pAllocationCallbacks) -{ - ma_lpf_node* pLPFNode = (ma_lpf_node*)pNode; - - if (pNode == NULL) { - return; - } - - ma_node_uninit(pNode, pAllocationCallbacks); - ma_lpf_uninit(&pLPFNode->lpf, pAllocationCallbacks); -} - - - -/* -High Pass Filter Node -*/ -MA_API ma_hpf_node_config ma_hpf_node_config_init(ma_uint32 channels, ma_uint32 sampleRate, double cutoffFrequency, ma_uint32 order) -{ - ma_hpf_node_config config; - - config.nodeConfig = ma_node_config_init(); - config.hpf = ma_hpf_config_init(ma_format_f32, channels, sampleRate, cutoffFrequency, order); - - return config; -} - -static void ma_hpf_node_process_pcm_frames(ma_node* pNode, const float** ppFramesIn, ma_uint32* pFrameCountIn, float** ppFramesOut, ma_uint32* pFrameCountOut) -{ - ma_hpf_node* pHPFNode = (ma_hpf_node*)pNode; - - MA_ASSERT(pNode != NULL); - (void)pFrameCountIn; - - ma_hpf_process_pcm_frames(&pHPFNode->hpf, ppFramesOut[0], ppFramesIn[0], *pFrameCountOut); -} - -static ma_node_vtable g_ma_hpf_node_vtable = -{ - ma_hpf_node_process_pcm_frames, - NULL, /* onGetRequiredInputFrameCount */ - 1, /* One input. */ - 1, /* One output. */ - 0 /* Default flags. */ -}; - -MA_API ma_result ma_hpf_node_init(ma_node_graph* pNodeGraph, const ma_hpf_node_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_hpf_node* pNode) -{ - ma_result result; - ma_node_config baseNodeConfig; - - if (pNode == NULL) { - return MA_INVALID_ARGS; - } - - MA_ZERO_OBJECT(pNode); - - if (pConfig == NULL) { - return MA_INVALID_ARGS; - } - - if (pConfig->hpf.format != ma_format_f32) { - return MA_INVALID_ARGS; /* The format must be f32. */ - } - - result = ma_hpf_init(&pConfig->hpf, pAllocationCallbacks, &pNode->hpf); - if (result != MA_SUCCESS) { - return result; - } - - baseNodeConfig = ma_node_config_init(); - baseNodeConfig.vtable = &g_ma_hpf_node_vtable; - baseNodeConfig.pInputChannels = &pConfig->hpf.channels; - baseNodeConfig.pOutputChannels = &pConfig->hpf.channels; - - result = ma_node_init(pNodeGraph, &baseNodeConfig, pAllocationCallbacks, pNode); - if (result != MA_SUCCESS) { - return result; - } - - return result; -} - -MA_API ma_result ma_hpf_node_reinit(const ma_hpf_config* pConfig, ma_hpf_node* pNode) -{ - ma_hpf_node* pHPFNode = (ma_hpf_node*)pNode; - - if (pNode == NULL) { - return MA_INVALID_ARGS; - } - - return ma_hpf_reinit(pConfig, &pHPFNode->hpf); -} - -MA_API void ma_hpf_node_uninit(ma_hpf_node* pNode, const ma_allocation_callbacks* pAllocationCallbacks) -{ - ma_hpf_node* pHPFNode = (ma_hpf_node*)pNode; - - if (pNode == NULL) { - return; - } - - ma_node_uninit(pNode, pAllocationCallbacks); - ma_hpf_uninit(&pHPFNode->hpf, pAllocationCallbacks); -} - - - - -/* -Band Pass Filter Node -*/ -MA_API ma_bpf_node_config ma_bpf_node_config_init(ma_uint32 channels, ma_uint32 sampleRate, double cutoffFrequency, ma_uint32 order) -{ - ma_bpf_node_config config; - - config.nodeConfig = ma_node_config_init(); - config.bpf = ma_bpf_config_init(ma_format_f32, channels, sampleRate, cutoffFrequency, order); - - return config; -} - -static void ma_bpf_node_process_pcm_frames(ma_node* pNode, const float** ppFramesIn, ma_uint32* pFrameCountIn, float** ppFramesOut, ma_uint32* pFrameCountOut) -{ - ma_bpf_node* pBPFNode = (ma_bpf_node*)pNode; - - MA_ASSERT(pNode != NULL); - (void)pFrameCountIn; - - ma_bpf_process_pcm_frames(&pBPFNode->bpf, ppFramesOut[0], ppFramesIn[0], *pFrameCountOut); -} - -static ma_node_vtable g_ma_bpf_node_vtable = -{ - ma_bpf_node_process_pcm_frames, - NULL, /* onGetRequiredInputFrameCount */ - 1, /* One input. */ - 1, /* One output. */ - 0 /* Default flags. */ -}; - -MA_API ma_result ma_bpf_node_init(ma_node_graph* pNodeGraph, const ma_bpf_node_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_bpf_node* pNode) -{ - ma_result result; - ma_node_config baseNodeConfig; - - if (pNode == NULL) { - return MA_INVALID_ARGS; - } - - MA_ZERO_OBJECT(pNode); - - if (pConfig == NULL) { - return MA_INVALID_ARGS; - } - - if (pConfig->bpf.format != ma_format_f32) { - return MA_INVALID_ARGS; /* The format must be f32. */ - } - - result = ma_bpf_init(&pConfig->bpf, pAllocationCallbacks, &pNode->bpf); - if (result != MA_SUCCESS) { - return result; - } - - baseNodeConfig = ma_node_config_init(); - baseNodeConfig.vtable = &g_ma_bpf_node_vtable; - baseNodeConfig.pInputChannels = &pConfig->bpf.channels; - baseNodeConfig.pOutputChannels = &pConfig->bpf.channels; - - result = ma_node_init(pNodeGraph, &baseNodeConfig, pAllocationCallbacks, pNode); - if (result != MA_SUCCESS) { - return result; - } - - return result; -} - -MA_API ma_result ma_bpf_node_reinit(const ma_bpf_config* pConfig, ma_bpf_node* pNode) -{ - ma_bpf_node* pBPFNode = (ma_bpf_node*)pNode; - - if (pNode == NULL) { - return MA_INVALID_ARGS; - } - - return ma_bpf_reinit(pConfig, &pBPFNode->bpf); -} - -MA_API void ma_bpf_node_uninit(ma_bpf_node* pNode, const ma_allocation_callbacks* pAllocationCallbacks) -{ - ma_bpf_node* pBPFNode = (ma_bpf_node*)pNode; - - if (pNode == NULL) { - return; - } - - ma_node_uninit(pNode, pAllocationCallbacks); - ma_bpf_uninit(&pBPFNode->bpf, pAllocationCallbacks); -} - - - -/* -Notching Filter Node -*/ -MA_API ma_notch_node_config ma_notch_node_config_init(ma_uint32 channels, ma_uint32 sampleRate, double q, double frequency) -{ - ma_notch_node_config config; - - config.nodeConfig = ma_node_config_init(); - config.notch = ma_notch2_config_init(ma_format_f32, channels, sampleRate, q, frequency); - - return config; -} - -static void ma_notch_node_process_pcm_frames(ma_node* pNode, const float** ppFramesIn, ma_uint32* pFrameCountIn, float** ppFramesOut, ma_uint32* pFrameCountOut) -{ - ma_notch_node* pBPFNode = (ma_notch_node*)pNode; - - MA_ASSERT(pNode != NULL); - (void)pFrameCountIn; - - ma_notch2_process_pcm_frames(&pBPFNode->notch, ppFramesOut[0], ppFramesIn[0], *pFrameCountOut); -} - -static ma_node_vtable g_ma_notch_node_vtable = -{ - ma_notch_node_process_pcm_frames, - NULL, /* onGetRequiredInputFrameCount */ - 1, /* One input. */ - 1, /* One output. */ - 0 /* Default flags. */ -}; - -MA_API ma_result ma_notch_node_init(ma_node_graph* pNodeGraph, const ma_notch_node_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_notch_node* pNode) -{ - ma_result result; - ma_node_config baseNodeConfig; - - if (pNode == NULL) { - return MA_INVALID_ARGS; - } - - MA_ZERO_OBJECT(pNode); - - if (pConfig == NULL) { - return MA_INVALID_ARGS; - } - - if (pConfig->notch.format != ma_format_f32) { - return MA_INVALID_ARGS; /* The format must be f32. */ - } - - result = ma_notch2_init(&pConfig->notch, pAllocationCallbacks, &pNode->notch); - if (result != MA_SUCCESS) { - return result; - } - - baseNodeConfig = ma_node_config_init(); - baseNodeConfig.vtable = &g_ma_notch_node_vtable; - baseNodeConfig.pInputChannels = &pConfig->notch.channels; - baseNodeConfig.pOutputChannels = &pConfig->notch.channels; - - result = ma_node_init(pNodeGraph, &baseNodeConfig, pAllocationCallbacks, pNode); - if (result != MA_SUCCESS) { - return result; - } - - return result; -} - -MA_API ma_result ma_notch_node_reinit(const ma_notch_config* pConfig, ma_notch_node* pNode) -{ - ma_notch_node* pNotchNode = (ma_notch_node*)pNode; - - if (pNode == NULL) { - return MA_INVALID_ARGS; - } - - return ma_notch2_reinit(pConfig, &pNotchNode->notch); -} - -MA_API void ma_notch_node_uninit(ma_notch_node* pNode, const ma_allocation_callbacks* pAllocationCallbacks) -{ - ma_notch_node* pNotchNode = (ma_notch_node*)pNode; - - if (pNode == NULL) { - return; - } - - ma_node_uninit(pNode, pAllocationCallbacks); - ma_notch2_uninit(&pNotchNode->notch, pAllocationCallbacks); -} - - - -/* -Peaking Filter Node -*/ -MA_API ma_peak_node_config ma_peak_node_config_init(ma_uint32 channels, ma_uint32 sampleRate, double gainDB, double q, double frequency) -{ - ma_peak_node_config config; - - config.nodeConfig = ma_node_config_init(); - config.peak = ma_peak2_config_init(ma_format_f32, channels, sampleRate, gainDB, q, frequency); - - return config; -} - -static void ma_peak_node_process_pcm_frames(ma_node* pNode, const float** ppFramesIn, ma_uint32* pFrameCountIn, float** ppFramesOut, ma_uint32* pFrameCountOut) -{ - ma_peak_node* pBPFNode = (ma_peak_node*)pNode; - - MA_ASSERT(pNode != NULL); - (void)pFrameCountIn; - - ma_peak2_process_pcm_frames(&pBPFNode->peak, ppFramesOut[0], ppFramesIn[0], *pFrameCountOut); -} - -static ma_node_vtable g_ma_peak_node_vtable = -{ - ma_peak_node_process_pcm_frames, - NULL, /* onGetRequiredInputFrameCount */ - 1, /* One input. */ - 1, /* One output. */ - 0 /* Default flags. */ -}; - -MA_API ma_result ma_peak_node_init(ma_node_graph* pNodeGraph, const ma_peak_node_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_peak_node* pNode) -{ - ma_result result; - ma_node_config baseNodeConfig; - - if (pNode == NULL) { - return MA_INVALID_ARGS; - } - - MA_ZERO_OBJECT(pNode); - - if (pConfig == NULL) { - return MA_INVALID_ARGS; - } - - if (pConfig->peak.format != ma_format_f32) { - return MA_INVALID_ARGS; /* The format must be f32. */ - } - - result = ma_peak2_init(&pConfig->peak, pAllocationCallbacks, &pNode->peak); - if (result != MA_SUCCESS) { - ma_node_uninit(pNode, pAllocationCallbacks); - return result; - } - - baseNodeConfig = ma_node_config_init(); - baseNodeConfig.vtable = &g_ma_peak_node_vtable; - baseNodeConfig.pInputChannels = &pConfig->peak.channels; - baseNodeConfig.pOutputChannels = &pConfig->peak.channels; - - result = ma_node_init(pNodeGraph, &baseNodeConfig, pAllocationCallbacks, pNode); - if (result != MA_SUCCESS) { - return result; - } - - return result; -} - -MA_API ma_result ma_peak_node_reinit(const ma_peak_config* pConfig, ma_peak_node* pNode) -{ - ma_peak_node* pPeakNode = (ma_peak_node*)pNode; - - if (pNode == NULL) { - return MA_INVALID_ARGS; - } - - return ma_peak2_reinit(pConfig, &pPeakNode->peak); -} - -MA_API void ma_peak_node_uninit(ma_peak_node* pNode, const ma_allocation_callbacks* pAllocationCallbacks) -{ - ma_peak_node* pPeakNode = (ma_peak_node*)pNode; - - if (pNode == NULL) { - return; - } - - ma_node_uninit(pNode, pAllocationCallbacks); - ma_peak2_uninit(&pPeakNode->peak, pAllocationCallbacks); -} - - - -/* -Low Shelf Filter Node -*/ -MA_API ma_loshelf_node_config ma_loshelf_node_config_init(ma_uint32 channels, ma_uint32 sampleRate, double gainDB, double q, double frequency) -{ - ma_loshelf_node_config config; - - config.nodeConfig = ma_node_config_init(); - config.loshelf = ma_loshelf2_config_init(ma_format_f32, channels, sampleRate, gainDB, q, frequency); - - return config; -} - -static void ma_loshelf_node_process_pcm_frames(ma_node* pNode, const float** ppFramesIn, ma_uint32* pFrameCountIn, float** ppFramesOut, ma_uint32* pFrameCountOut) -{ - ma_loshelf_node* pBPFNode = (ma_loshelf_node*)pNode; - - MA_ASSERT(pNode != NULL); - (void)pFrameCountIn; - - ma_loshelf2_process_pcm_frames(&pBPFNode->loshelf, ppFramesOut[0], ppFramesIn[0], *pFrameCountOut); -} - -static ma_node_vtable g_ma_loshelf_node_vtable = -{ - ma_loshelf_node_process_pcm_frames, - NULL, /* onGetRequiredInputFrameCount */ - 1, /* One input. */ - 1, /* One output. */ - 0 /* Default flags. */ -}; - -MA_API ma_result ma_loshelf_node_init(ma_node_graph* pNodeGraph, const ma_loshelf_node_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_loshelf_node* pNode) -{ - ma_result result; - ma_node_config baseNodeConfig; - - if (pNode == NULL) { - return MA_INVALID_ARGS; - } - - MA_ZERO_OBJECT(pNode); - - if (pConfig == NULL) { - return MA_INVALID_ARGS; - } - - if (pConfig->loshelf.format != ma_format_f32) { - return MA_INVALID_ARGS; /* The format must be f32. */ - } - - result = ma_loshelf2_init(&pConfig->loshelf, pAllocationCallbacks, &pNode->loshelf); - if (result != MA_SUCCESS) { - return result; - } - - baseNodeConfig = ma_node_config_init(); - baseNodeConfig.vtable = &g_ma_loshelf_node_vtable; - baseNodeConfig.pInputChannels = &pConfig->loshelf.channels; - baseNodeConfig.pOutputChannels = &pConfig->loshelf.channels; - - result = ma_node_init(pNodeGraph, &baseNodeConfig, pAllocationCallbacks, pNode); - if (result != MA_SUCCESS) { - return result; - } - - return result; -} - -MA_API ma_result ma_loshelf_node_reinit(const ma_loshelf_config* pConfig, ma_loshelf_node* pNode) -{ - ma_loshelf_node* pLoshelfNode = (ma_loshelf_node*)pNode; - - if (pNode == NULL) { - return MA_INVALID_ARGS; - } - - return ma_loshelf2_reinit(pConfig, &pLoshelfNode->loshelf); -} - -MA_API void ma_loshelf_node_uninit(ma_loshelf_node* pNode, const ma_allocation_callbacks* pAllocationCallbacks) -{ - ma_loshelf_node* pLoshelfNode = (ma_loshelf_node*)pNode; - - if (pNode == NULL) { - return; - } - - ma_node_uninit(pNode, pAllocationCallbacks); - ma_loshelf2_uninit(&pLoshelfNode->loshelf, pAllocationCallbacks); -} - - - -/* -High Shelf Filter Node -*/ -MA_API ma_hishelf_node_config ma_hishelf_node_config_init(ma_uint32 channels, ma_uint32 sampleRate, double gainDB, double q, double frequency) -{ - ma_hishelf_node_config config; - - config.nodeConfig = ma_node_config_init(); - config.hishelf = ma_hishelf2_config_init(ma_format_f32, channels, sampleRate, gainDB, q, frequency); - - return config; -} - -static void ma_hishelf_node_process_pcm_frames(ma_node* pNode, const float** ppFramesIn, ma_uint32* pFrameCountIn, float** ppFramesOut, ma_uint32* pFrameCountOut) -{ - ma_hishelf_node* pBPFNode = (ma_hishelf_node*)pNode; - - MA_ASSERT(pNode != NULL); - (void)pFrameCountIn; - - ma_hishelf2_process_pcm_frames(&pBPFNode->hishelf, ppFramesOut[0], ppFramesIn[0], *pFrameCountOut); -} - -static ma_node_vtable g_ma_hishelf_node_vtable = -{ - ma_hishelf_node_process_pcm_frames, - NULL, /* onGetRequiredInputFrameCount */ - 1, /* One input. */ - 1, /* One output. */ - 0 /* Default flags. */ -}; - -MA_API ma_result ma_hishelf_node_init(ma_node_graph* pNodeGraph, const ma_hishelf_node_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_hishelf_node* pNode) -{ - ma_result result; - ma_node_config baseNodeConfig; - - if (pNode == NULL) { - return MA_INVALID_ARGS; - } - - MA_ZERO_OBJECT(pNode); - - if (pConfig == NULL) { - return MA_INVALID_ARGS; - } - - if (pConfig->hishelf.format != ma_format_f32) { - return MA_INVALID_ARGS; /* The format must be f32. */ - } - - result = ma_hishelf2_init(&pConfig->hishelf, pAllocationCallbacks, &pNode->hishelf); - if (result != MA_SUCCESS) { - return result; - } - - baseNodeConfig = ma_node_config_init(); - baseNodeConfig.vtable = &g_ma_hishelf_node_vtable; - baseNodeConfig.pInputChannels = &pConfig->hishelf.channels; - baseNodeConfig.pOutputChannels = &pConfig->hishelf.channels; - - result = ma_node_init(pNodeGraph, &baseNodeConfig, pAllocationCallbacks, pNode); - if (result != MA_SUCCESS) { - return result; - } - - return result; -} - -MA_API ma_result ma_hishelf_node_reinit(const ma_hishelf_config* pConfig, ma_hishelf_node* pNode) -{ - ma_hishelf_node* pHishelfNode = (ma_hishelf_node*)pNode; - - if (pNode == NULL) { - return MA_INVALID_ARGS; - } - - return ma_hishelf2_reinit(pConfig, &pHishelfNode->hishelf); -} - -MA_API void ma_hishelf_node_uninit(ma_hishelf_node* pNode, const ma_allocation_callbacks* pAllocationCallbacks) -{ - ma_hishelf_node* pHishelfNode = (ma_hishelf_node*)pNode; - - if (pNode == NULL) { - return; - } - - ma_node_uninit(pNode, pAllocationCallbacks); - ma_hishelf2_uninit(&pHishelfNode->hishelf, pAllocationCallbacks); -} - - - - -MA_API ma_delay_node_config ma_delay_node_config_init(ma_uint32 channels, ma_uint32 sampleRate, ma_uint32 delayInFrames, float decay) -{ - ma_delay_node_config config; - - config.nodeConfig = ma_node_config_init(); - config.delay = ma_delay_config_init(channels, sampleRate, delayInFrames, decay); - - return config; -} - - -static void ma_delay_node_process_pcm_frames(ma_node* pNode, const float** ppFramesIn, ma_uint32* pFrameCountIn, float** ppFramesOut, ma_uint32* pFrameCountOut) -{ - ma_delay_node* pDelayNode = (ma_delay_node*)pNode; - - (void)pFrameCountIn; - - ma_delay_process_pcm_frames(&pDelayNode->delay, ppFramesOut[0], ppFramesIn[0], *pFrameCountOut); -} - -static ma_node_vtable g_ma_delay_node_vtable = -{ - ma_delay_node_process_pcm_frames, - NULL, - 1, /* 1 input channels. */ - 1, /* 1 output channel. */ - MA_NODE_FLAG_CONTINUOUS_PROCESSING /* Delay requires continuous processing to ensure the tail get's processed. */ -}; - -MA_API ma_result ma_delay_node_init(ma_node_graph* pNodeGraph, const ma_delay_node_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_delay_node* pDelayNode) -{ - ma_result result; - ma_node_config baseConfig; - - if (pDelayNode == NULL) { - return MA_INVALID_ARGS; - } - - MA_ZERO_OBJECT(pDelayNode); - - result = ma_delay_init(&pConfig->delay, pAllocationCallbacks, &pDelayNode->delay); - if (result != MA_SUCCESS) { - return result; - } - - baseConfig = pConfig->nodeConfig; - baseConfig.vtable = &g_ma_delay_node_vtable; - baseConfig.pInputChannels = &pConfig->delay.channels; - baseConfig.pOutputChannels = &pConfig->delay.channels; - - result = ma_node_init(pNodeGraph, &baseConfig, pAllocationCallbacks, &pDelayNode->baseNode); - if (result != MA_SUCCESS) { - ma_delay_uninit(&pDelayNode->delay, pAllocationCallbacks); - return result; - } - - return result; -} - -MA_API void ma_delay_node_uninit(ma_delay_node* pDelayNode, const ma_allocation_callbacks* pAllocationCallbacks) -{ - if (pDelayNode == NULL) { - return; - } - - /* The base node is always uninitialized first. */ - ma_node_uninit(pDelayNode, pAllocationCallbacks); - ma_delay_uninit(&pDelayNode->delay, pAllocationCallbacks); -} - -MA_API void ma_delay_node_set_wet(ma_delay_node* pDelayNode, float value) -{ - if (pDelayNode == NULL) { - return; - } - - ma_delay_set_wet(&pDelayNode->delay, value); -} - -MA_API float ma_delay_node_get_wet(const ma_delay_node* pDelayNode) -{ - if (pDelayNode == NULL) { - return 0; - } - - return ma_delay_get_wet(&pDelayNode->delay); -} - -MA_API void ma_delay_node_set_dry(ma_delay_node* pDelayNode, float value) -{ - if (pDelayNode == NULL) { - return; - } - - ma_delay_set_dry(&pDelayNode->delay, value); -} - -MA_API float ma_delay_node_get_dry(const ma_delay_node* pDelayNode) -{ - if (pDelayNode == NULL) { - return 0; - } - - return ma_delay_get_dry(&pDelayNode->delay); -} - -MA_API void ma_delay_node_set_decay(ma_delay_node* pDelayNode, float value) -{ - if (pDelayNode == NULL) { - return; - } - - ma_delay_set_decay(&pDelayNode->delay, value); -} - -MA_API float ma_delay_node_get_decay(const ma_delay_node* pDelayNode) -{ - if (pDelayNode == NULL) { - return 0; - } - - return ma_delay_get_decay(&pDelayNode->delay); -} -#endif /* MA_NO_NODE_GRAPH */ - - -/* SECTION: miniaudio_engine.c */ -#if !defined(MA_NO_ENGINE) && !defined(MA_NO_NODE_GRAPH) -/************************************************************************************************************************************************************** - -Engine - -**************************************************************************************************************************************************************/ -#define MA_SEEK_TARGET_NONE (~(ma_uint64)0) - - -static void ma_sound_set_at_end(ma_sound* pSound, ma_bool32 atEnd) -{ - MA_ASSERT(pSound != NULL); - ma_atomic_exchange_32(&pSound->atEnd, atEnd); - - /* Fire any callbacks or events. */ - if (atEnd) { - if (pSound->endCallback != NULL) { - pSound->endCallback(pSound->pEndCallbackUserData, pSound); - } - } -} - -static ma_bool32 ma_sound_get_at_end(const ma_sound* pSound) -{ - MA_ASSERT(pSound != NULL); - return ma_atomic_load_32(&pSound->atEnd); -} - - -MA_API ma_engine_node_config ma_engine_node_config_init(ma_engine* pEngine, ma_engine_node_type type, ma_uint32 flags) -{ - ma_engine_node_config config; - - MA_ZERO_OBJECT(&config); - config.pEngine = pEngine; - config.type = type; - config.isPitchDisabled = (flags & MA_SOUND_FLAG_NO_PITCH) != 0; - config.isSpatializationDisabled = (flags & MA_SOUND_FLAG_NO_SPATIALIZATION) != 0; - config.monoExpansionMode = pEngine->monoExpansionMode; - - return config; -} - - -static void ma_engine_node_update_pitch_if_required(ma_engine_node* pEngineNode) -{ - ma_bool32 isUpdateRequired = MA_FALSE; - float newPitch; - - MA_ASSERT(pEngineNode != NULL); - - newPitch = ma_atomic_load_explicit_f32(&pEngineNode->pitch, ma_atomic_memory_order_acquire); - - if (pEngineNode->oldPitch != newPitch) { - pEngineNode->oldPitch = newPitch; - isUpdateRequired = MA_TRUE; - } - - if (pEngineNode->oldDopplerPitch != pEngineNode->spatializer.dopplerPitch) { - pEngineNode->oldDopplerPitch = pEngineNode->spatializer.dopplerPitch; - isUpdateRequired = MA_TRUE; - } - - if (isUpdateRequired) { - float basePitch = (float)pEngineNode->sampleRate / ma_engine_get_sample_rate(pEngineNode->pEngine); - ma_linear_resampler_set_rate_ratio(&pEngineNode->resampler, basePitch * pEngineNode->oldPitch * pEngineNode->oldDopplerPitch); - } -} - -static ma_bool32 ma_engine_node_is_pitching_enabled(const ma_engine_node* pEngineNode) -{ - MA_ASSERT(pEngineNode != NULL); - - /* Don't try to be clever by skipping resampling in the pitch=1 case or else you'll glitch when moving away from 1. */ - return !ma_atomic_load_explicit_32(&pEngineNode->isPitchDisabled, ma_atomic_memory_order_acquire); -} - -static ma_bool32 ma_engine_node_is_spatialization_enabled(const ma_engine_node* pEngineNode) -{ - MA_ASSERT(pEngineNode != NULL); - - return !ma_atomic_load_explicit_32(&pEngineNode->isSpatializationDisabled, ma_atomic_memory_order_acquire); -} - -static ma_uint64 ma_engine_node_get_required_input_frame_count(const ma_engine_node* pEngineNode, ma_uint64 outputFrameCount) -{ - ma_uint64 inputFrameCount = 0; - - if (ma_engine_node_is_pitching_enabled(pEngineNode)) { - ma_result result = ma_linear_resampler_get_required_input_frame_count(&pEngineNode->resampler, outputFrameCount, &inputFrameCount); - if (result != MA_SUCCESS) { - inputFrameCount = 0; - } - } else { - inputFrameCount = outputFrameCount; /* No resampling, so 1:1. */ - } - - return inputFrameCount; -} - -static ma_result ma_engine_node_set_volume(ma_engine_node* pEngineNode, float volume) -{ - if (pEngineNode == NULL) { - return MA_INVALID_ARGS; - } - - ma_atomic_float_set(&pEngineNode->volume, volume); - - /* If we're not smoothing we should bypass the volume gainer entirely. */ - if (pEngineNode->volumeSmoothTimeInPCMFrames == 0) { - /* We should always have an active spatializer because it can be enabled and disabled dynamically. We can just use that for holding our volume. */ - ma_spatializer_set_master_volume(&pEngineNode->spatializer, volume); - } else { - /* We're using volume smoothing, so apply the master volume to the gainer. */ - ma_gainer_set_gain(&pEngineNode->volumeGainer, volume); - } - - return MA_SUCCESS; -} - -static ma_result ma_engine_node_get_volume(const ma_engine_node* pEngineNode, float* pVolume) -{ - if (pVolume == NULL) { - return MA_INVALID_ARGS; - } - - *pVolume = 0.0f; - - if (pEngineNode == NULL) { - return MA_INVALID_ARGS; - } - - *pVolume = ma_atomic_float_get((ma_atomic_float*)&pEngineNode->volume); - - return MA_SUCCESS; -} - - -static void ma_engine_node_process_pcm_frames__general(ma_engine_node* pEngineNode, const float** ppFramesIn, ma_uint32* pFrameCountIn, float** ppFramesOut, ma_uint32* pFrameCountOut) -{ - ma_uint32 frameCountIn; - ma_uint32 frameCountOut; - ma_uint32 totalFramesProcessedIn; - ma_uint32 totalFramesProcessedOut; - ma_uint32 channelsIn; - ma_uint32 channelsOut; - ma_bool32 isPitchingEnabled; - ma_bool32 isFadingEnabled; - ma_bool32 isSpatializationEnabled; - ma_bool32 isPanningEnabled; - ma_bool32 isVolumeSmoothingEnabled; - - frameCountIn = *pFrameCountIn; - frameCountOut = *pFrameCountOut; - - channelsIn = ma_spatializer_get_input_channels(&pEngineNode->spatializer); - channelsOut = ma_spatializer_get_output_channels(&pEngineNode->spatializer); - - totalFramesProcessedIn = 0; - totalFramesProcessedOut = 0; - - /* Update the fader if applicable. */ - { - ma_uint64 fadeLengthInFrames = ma_atomic_uint64_get(&pEngineNode->fadeSettings.fadeLengthInFrames); - if (fadeLengthInFrames != ~(ma_uint64)0) { - float fadeVolumeBeg = ma_atomic_float_get(&pEngineNode->fadeSettings.volumeBeg); - float fadeVolumeEnd = ma_atomic_float_get(&pEngineNode->fadeSettings.volumeEnd); - ma_int64 fadeStartOffsetInFrames = (ma_int64)ma_atomic_uint64_get(&pEngineNode->fadeSettings.absoluteGlobalTimeInFrames); - if (fadeStartOffsetInFrames == (ma_int64)(~(ma_uint64)0)) { - fadeStartOffsetInFrames = 0; - } else { - fadeStartOffsetInFrames -= ma_engine_get_time_in_pcm_frames(pEngineNode->pEngine); - } - - ma_fader_set_fade_ex(&pEngineNode->fader, fadeVolumeBeg, fadeVolumeEnd, fadeLengthInFrames, fadeStartOffsetInFrames); - - /* Reset the fade length so we don't erroneously apply it again. */ - ma_atomic_uint64_set(&pEngineNode->fadeSettings.fadeLengthInFrames, ~(ma_uint64)0); - } - } - - isPitchingEnabled = ma_engine_node_is_pitching_enabled(pEngineNode); - isFadingEnabled = pEngineNode->fader.volumeBeg != 1 || pEngineNode->fader.volumeEnd != 1; - isSpatializationEnabled = ma_engine_node_is_spatialization_enabled(pEngineNode); - isPanningEnabled = pEngineNode->panner.pan != 0 && channelsOut != 1; - isVolumeSmoothingEnabled = pEngineNode->volumeSmoothTimeInPCMFrames > 0; - - /* Keep going while we've still got data available for processing. */ - while (totalFramesProcessedOut < frameCountOut) { - /* - We need to process in a specific order. We always do resampling first because it's likely - we're going to be increasing the channel count after spatialization. Also, I want to do - fading based on the output sample rate. - - We'll first read into a buffer from the resampler. Then we'll do all processing that - operates on the on the input channel count. We'll then get the spatializer to output to - the output buffer and then do all effects from that point directly in the output buffer - in-place. - - Note that we're always running the resampler if pitching is enabled, even when the pitch - is 1. If we try to be clever and skip resampling when the pitch is 1, we'll get a glitch - when we move away from 1, back to 1, and then away from 1 again. We'll want to implement - any pitch=1 optimizations in the resampler itself. - - There's a small optimization here that we'll utilize since it might be a fairly common - case. When the input and output channel counts are the same, we'll read straight into the - output buffer from the resampler and do everything in-place. - */ - const float* pRunningFramesIn; - float* pRunningFramesOut; - float* pWorkingBuffer; /* This is the buffer that we'll be processing frames in. This is in input channels. */ - float temp[MA_DATA_CONVERTER_STACK_BUFFER_SIZE / sizeof(float)]; - ma_uint32 tempCapInFrames = ma_countof(temp) / channelsIn; - ma_uint32 framesAvailableIn; - ma_uint32 framesAvailableOut; - ma_uint32 framesJustProcessedIn; - ma_uint32 framesJustProcessedOut; - ma_bool32 isWorkingBufferValid = MA_FALSE; - - framesAvailableIn = frameCountIn - totalFramesProcessedIn; - framesAvailableOut = frameCountOut - totalFramesProcessedOut; - - pRunningFramesIn = ma_offset_pcm_frames_const_ptr_f32(ppFramesIn[0], totalFramesProcessedIn, channelsIn); - pRunningFramesOut = ma_offset_pcm_frames_ptr_f32(ppFramesOut[0], totalFramesProcessedOut, channelsOut); - - if (channelsIn == channelsOut) { - /* Fast path. Channel counts are the same. No need for an intermediary input buffer. */ - pWorkingBuffer = pRunningFramesOut; - } else { - /* Slow path. Channel counts are different. Need to use an intermediary input buffer. */ - pWorkingBuffer = temp; - if (framesAvailableOut > tempCapInFrames) { - framesAvailableOut = tempCapInFrames; - } - } - - /* First is resampler. */ - if (isPitchingEnabled) { - ma_uint64 resampleFrameCountIn = framesAvailableIn; - ma_uint64 resampleFrameCountOut = framesAvailableOut; - - ma_linear_resampler_process_pcm_frames(&pEngineNode->resampler, pRunningFramesIn, &resampleFrameCountIn, pWorkingBuffer, &resampleFrameCountOut); - isWorkingBufferValid = MA_TRUE; - - framesJustProcessedIn = (ma_uint32)resampleFrameCountIn; - framesJustProcessedOut = (ma_uint32)resampleFrameCountOut; - } else { - framesJustProcessedIn = ma_min(framesAvailableIn, framesAvailableOut); - framesJustProcessedOut = framesJustProcessedIn; /* When no resampling is being performed, the number of output frames is the same as input frames. */ - } - - /* Fading. */ - if (isFadingEnabled) { - if (isWorkingBufferValid) { - ma_fader_process_pcm_frames(&pEngineNode->fader, pWorkingBuffer, pWorkingBuffer, framesJustProcessedOut); /* In-place processing. */ - } else { - ma_fader_process_pcm_frames(&pEngineNode->fader, pWorkingBuffer, pRunningFramesIn, framesJustProcessedOut); - isWorkingBufferValid = MA_TRUE; - } - } - - /* - If we're using smoothing, we won't be applying volume via the spatializer, but instead from a ma_gainer. In this case - we'll want to apply our volume now. - */ - if (isVolumeSmoothingEnabled) { - if (isWorkingBufferValid) { - ma_gainer_process_pcm_frames(&pEngineNode->volumeGainer, pWorkingBuffer, pWorkingBuffer, framesJustProcessedOut); - } else { - ma_gainer_process_pcm_frames(&pEngineNode->volumeGainer, pWorkingBuffer, pRunningFramesIn, framesJustProcessedOut); - isWorkingBufferValid = MA_TRUE; - } - } - - /* - If at this point we still haven't actually done anything with the working buffer we need - to just read straight from the input buffer. - */ - if (isWorkingBufferValid == MA_FALSE) { - pWorkingBuffer = (float*)pRunningFramesIn; /* Naughty const cast, but it's safe at this point because we won't ever be writing to it from this point out. */ - } - - /* Spatialization. */ - if (isSpatializationEnabled) { - ma_uint32 iListener; - - /* - When determining the listener to use, we first check to see if the sound is pinned to a - specific listener. If so, we use that. Otherwise we just use the closest listener. - */ - if (pEngineNode->pinnedListenerIndex != MA_LISTENER_INDEX_CLOSEST && pEngineNode->pinnedListenerIndex < ma_engine_get_listener_count(pEngineNode->pEngine)) { - iListener = pEngineNode->pinnedListenerIndex; - } else { - ma_vec3f spatializerPosition = ma_spatializer_get_position(&pEngineNode->spatializer); - iListener = ma_engine_find_closest_listener(pEngineNode->pEngine, spatializerPosition.x, spatializerPosition.y, spatializerPosition.z); - } - - ma_spatializer_process_pcm_frames(&pEngineNode->spatializer, &pEngineNode->pEngine->listeners[iListener], pRunningFramesOut, pWorkingBuffer, framesJustProcessedOut); - } else { - /* No spatialization, but we still need to do channel conversion and master volume. */ - float volume; - ma_engine_node_get_volume(pEngineNode, &volume); /* Should never fail. */ - - if (channelsIn == channelsOut) { - /* No channel conversion required. Just copy straight to the output buffer. */ - if (isVolumeSmoothingEnabled) { - /* Volume has already been applied. Just copy straight to the output buffer. */ - ma_copy_pcm_frames(pRunningFramesOut, pWorkingBuffer, framesJustProcessedOut * channelsOut, ma_format_f32, channelsOut); - } else { - /* Volume has not been applied yet. Copy and apply volume in the same pass. */ - ma_copy_and_apply_volume_factor_f32(pRunningFramesOut, pWorkingBuffer, framesJustProcessedOut * channelsOut, volume); - } - } else { - /* Channel conversion required. TODO: Add support for channel maps here. */ - ma_channel_map_apply_f32(pRunningFramesOut, NULL, channelsOut, pWorkingBuffer, NULL, channelsIn, framesJustProcessedOut, ma_channel_mix_mode_simple, pEngineNode->monoExpansionMode); - - /* If we're using smoothing, the volume will have already been applied. */ - if (!isVolumeSmoothingEnabled) { - ma_apply_volume_factor_f32(pRunningFramesOut, framesJustProcessedOut * channelsOut, volume); - } - } - } - - /* At this point we can guarantee that the output buffer contains valid data. We can process everything in place now. */ - - /* Panning. */ - if (isPanningEnabled) { - ma_panner_process_pcm_frames(&pEngineNode->panner, pRunningFramesOut, pRunningFramesOut, framesJustProcessedOut); /* In-place processing. */ - } - - /* We're done for this chunk. */ - totalFramesProcessedIn += framesJustProcessedIn; - totalFramesProcessedOut += framesJustProcessedOut; - - /* If we didn't process any output frames this iteration it means we've either run out of input data, or run out of room in the output buffer. */ - if (framesJustProcessedOut == 0) { - break; - } - } - - /* At this point we're done processing. */ - *pFrameCountIn = totalFramesProcessedIn; - *pFrameCountOut = totalFramesProcessedOut; -} - -static void ma_engine_node_process_pcm_frames__sound(ma_node* pNode, const float** ppFramesIn, ma_uint32* pFrameCountIn, float** ppFramesOut, ma_uint32* pFrameCountOut) -{ - /* For sounds, we need to first read from the data source. Then we need to apply the engine effects (pan, pitch, fades, etc.). */ - ma_result result = MA_SUCCESS; - ma_sound* pSound = (ma_sound*)pNode; - ma_uint32 frameCount = *pFrameCountOut; - ma_uint32 totalFramesRead = 0; - ma_format dataSourceFormat; - ma_uint32 dataSourceChannels; - ma_uint8 temp[MA_DATA_CONVERTER_STACK_BUFFER_SIZE]; - ma_uint32 tempCapInFrames; - ma_uint64 seekTarget; - - /* This is a data source node which means no input buses. */ - (void)ppFramesIn; - (void)pFrameCountIn; - - /* If we're marked at the end we need to stop the sound and do nothing. */ - if (ma_sound_at_end(pSound)) { - ma_sound_stop(pSound); - *pFrameCountOut = 0; - return; - } - - /* If we're seeking, do so now before reading. */ - seekTarget = ma_atomic_load_64(&pSound->seekTarget); - if (seekTarget != MA_SEEK_TARGET_NONE) { - ma_data_source_seek_to_pcm_frame(pSound->pDataSource, seekTarget); - - /* Any time-dependant effects need to have their times updated. */ - ma_node_set_time(pSound, seekTarget); - - ma_atomic_exchange_64(&pSound->seekTarget, MA_SEEK_TARGET_NONE); - } - - /* - We want to update the pitch once. For sounds, this can be either at the start or at the end. If - we don't force this to only ever be updating once, we could end up in a situation where - retrieving the required input frame count ends up being different to what we actually retrieve. - What could happen is that the required input frame count is calculated, the pitch is update, - and then this processing function is called resulting in a different number of input frames - being processed. Do not call this in ma_engine_node_process_pcm_frames__general() or else - you'll hit the aforementioned bug. - */ - ma_engine_node_update_pitch_if_required(&pSound->engineNode); - - /* - For the convenience of the caller, we're doing to allow data sources to use non-floating-point formats and channel counts that differ - from the main engine. - */ - result = ma_data_source_get_data_format(pSound->pDataSource, &dataSourceFormat, &dataSourceChannels, NULL, NULL, 0); - if (result == MA_SUCCESS) { - tempCapInFrames = sizeof(temp) / ma_get_bytes_per_frame(dataSourceFormat, dataSourceChannels); - - /* Keep reading until we've read as much as was requested or we reach the end of the data source. */ - while (totalFramesRead < frameCount) { - ma_uint32 framesRemaining = frameCount - totalFramesRead; - ma_uint32 framesToRead; - ma_uint64 framesJustRead; - ma_uint32 frameCountIn; - ma_uint32 frameCountOut; - const float* pRunningFramesIn; - float* pRunningFramesOut; - - /* - The first thing we need to do is read into the temporary buffer. We can calculate exactly - how many input frames we'll need after resampling. - */ - framesToRead = (ma_uint32)ma_engine_node_get_required_input_frame_count(&pSound->engineNode, framesRemaining); - if (framesToRead > tempCapInFrames) { - framesToRead = tempCapInFrames; - } - - result = ma_data_source_read_pcm_frames(pSound->pDataSource, temp, framesToRead, &framesJustRead); - - /* If we reached the end of the sound we'll want to mark it as at the end and stop it. This should never be returned for looping sounds. */ - if (result == MA_AT_END) { - ma_sound_set_at_end(pSound, MA_TRUE); /* This will be set to false in ma_sound_start(). */ - } - - pRunningFramesOut = ma_offset_pcm_frames_ptr_f32(ppFramesOut[0], totalFramesRead, ma_node_get_output_channels(pNode, 0)); - - frameCountIn = (ma_uint32)framesJustRead; - frameCountOut = framesRemaining; - - /* Convert if necessary. */ - if (dataSourceFormat == ma_format_f32) { - /* Fast path. No data conversion necessary. */ - pRunningFramesIn = (float*)temp; - ma_engine_node_process_pcm_frames__general(&pSound->engineNode, &pRunningFramesIn, &frameCountIn, &pRunningFramesOut, &frameCountOut); - } else { - /* Slow path. Need to do sample format conversion to f32. If we give the f32 buffer the same count as the first temp buffer, we're guaranteed it'll be large enough. */ - float tempf32[MA_DATA_CONVERTER_STACK_BUFFER_SIZE]; /* Do not do `MA_DATA_CONVERTER_STACK_BUFFER_SIZE/sizeof(float)` here like we've done in other places. */ - ma_convert_pcm_frames_format(tempf32, ma_format_f32, temp, dataSourceFormat, framesJustRead, dataSourceChannels, ma_dither_mode_none); - - /* Now that we have our samples in f32 format we can process like normal. */ - pRunningFramesIn = tempf32; - ma_engine_node_process_pcm_frames__general(&pSound->engineNode, &pRunningFramesIn, &frameCountIn, &pRunningFramesOut, &frameCountOut); - } - - /* We should have processed all of our input frames since we calculated the required number of input frames at the top. */ - MA_ASSERT(frameCountIn == framesJustRead); - totalFramesRead += (ma_uint32)frameCountOut; /* Safe cast. */ - - if (result != MA_SUCCESS || ma_sound_at_end(pSound)) { - break; /* Might have reached the end. */ - } - } - } - - *pFrameCountOut = totalFramesRead; -} - -static void ma_engine_node_process_pcm_frames__group(ma_node* pNode, const float** ppFramesIn, ma_uint32* pFrameCountIn, float** ppFramesOut, ma_uint32* pFrameCountOut) -{ - /* - Make sure the pitch is updated before trying to read anything. It's important that this is done - only once and not in ma_engine_node_process_pcm_frames__general(). The reason for this is that - ma_engine_node_process_pcm_frames__general() will call ma_engine_node_get_required_input_frame_count(), - and if another thread modifies the pitch just after that call it can result in a glitch due to - the input rate changing. - */ - ma_engine_node_update_pitch_if_required((ma_engine_node*)pNode); - - /* For groups, the input data has already been read and we just need to apply the effect. */ - ma_engine_node_process_pcm_frames__general((ma_engine_node*)pNode, ppFramesIn, pFrameCountIn, ppFramesOut, pFrameCountOut); -} - -static ma_result ma_engine_node_get_required_input_frame_count__group(ma_node* pNode, ma_uint32 outputFrameCount, ma_uint32* pInputFrameCount) -{ - ma_uint64 inputFrameCount; - - MA_ASSERT(pInputFrameCount != NULL); - - /* Our pitch will affect this calculation. We need to update it. */ - ma_engine_node_update_pitch_if_required((ma_engine_node*)pNode); - - inputFrameCount = ma_engine_node_get_required_input_frame_count((ma_engine_node*)pNode, outputFrameCount); - if (inputFrameCount > 0xFFFFFFFF) { - inputFrameCount = 0xFFFFFFFF; /* Will never happen because miniaudio will only ever process in relatively small chunks. */ - } - - *pInputFrameCount = (ma_uint32)inputFrameCount; - - return MA_SUCCESS; -} - - -static ma_node_vtable g_ma_engine_node_vtable__sound = -{ - ma_engine_node_process_pcm_frames__sound, - NULL, /* onGetRequiredInputFrameCount */ - 0, /* Sounds are data source nodes which means they have zero inputs (their input is drawn from the data source itself). */ - 1, /* Sounds have one output bus. */ - 0 /* Default flags. */ -}; - -static ma_node_vtable g_ma_engine_node_vtable__group = -{ - ma_engine_node_process_pcm_frames__group, - ma_engine_node_get_required_input_frame_count__group, - 1, /* Groups have one input bus. */ - 1, /* Groups have one output bus. */ - MA_NODE_FLAG_DIFFERENT_PROCESSING_RATES /* The engine node does resampling so should let miniaudio know about it. */ -}; - - - -static ma_node_config ma_engine_node_base_node_config_init(const ma_engine_node_config* pConfig) -{ - ma_node_config baseNodeConfig; - - if (pConfig->type == ma_engine_node_type_sound) { - /* Sound. */ - baseNodeConfig = ma_node_config_init(); - baseNodeConfig.vtable = &g_ma_engine_node_vtable__sound; - baseNodeConfig.initialState = ma_node_state_stopped; /* Sounds are stopped by default. */ - } else { - /* Group. */ - baseNodeConfig = ma_node_config_init(); - baseNodeConfig.vtable = &g_ma_engine_node_vtable__group; - baseNodeConfig.initialState = ma_node_state_started; /* Groups are started by default. */ - } - - return baseNodeConfig; -} - -static ma_spatializer_config ma_engine_node_spatializer_config_init(const ma_node_config* pBaseNodeConfig) -{ - return ma_spatializer_config_init(pBaseNodeConfig->pInputChannels[0], pBaseNodeConfig->pOutputChannels[0]); -} - -typedef struct -{ - size_t sizeInBytes; - size_t baseNodeOffset; - size_t resamplerOffset; - size_t spatializerOffset; - size_t gainerOffset; -} ma_engine_node_heap_layout; - -static ma_result ma_engine_node_get_heap_layout(const ma_engine_node_config* pConfig, ma_engine_node_heap_layout* pHeapLayout) -{ - ma_result result; - size_t tempHeapSize; - ma_node_config baseNodeConfig; - ma_linear_resampler_config resamplerConfig; - ma_spatializer_config spatializerConfig; - ma_gainer_config gainerConfig; - ma_uint32 channelsIn; - ma_uint32 channelsOut; - ma_channel defaultStereoChannelMap[2] = {MA_CHANNEL_SIDE_LEFT, MA_CHANNEL_SIDE_RIGHT}; /* <-- Consistent with the default channel map of a stereo listener. Means channel conversion can run on a fast path. */ - - MA_ASSERT(pHeapLayout); - - MA_ZERO_OBJECT(pHeapLayout); - - if (pConfig == NULL) { - return MA_INVALID_ARGS; - } - - if (pConfig->pEngine == NULL) { - return MA_INVALID_ARGS; /* An engine must be specified. */ - } - - pHeapLayout->sizeInBytes = 0; - - channelsIn = (pConfig->channelsIn != 0) ? pConfig->channelsIn : ma_engine_get_channels(pConfig->pEngine); - channelsOut = (pConfig->channelsOut != 0) ? pConfig->channelsOut : ma_engine_get_channels(pConfig->pEngine); - - - /* Base node. */ - baseNodeConfig = ma_engine_node_base_node_config_init(pConfig); - baseNodeConfig.pInputChannels = &channelsIn; - baseNodeConfig.pOutputChannels = &channelsOut; - - result = ma_node_get_heap_size(ma_engine_get_node_graph(pConfig->pEngine), &baseNodeConfig, &tempHeapSize); - if (result != MA_SUCCESS) { - return result; /* Failed to retrieve the size of the heap for the base node. */ - } - - pHeapLayout->baseNodeOffset = pHeapLayout->sizeInBytes; - pHeapLayout->sizeInBytes += ma_align_64(tempHeapSize); - - - /* Resmapler. */ - resamplerConfig = ma_linear_resampler_config_init(ma_format_f32, channelsIn, 1, 1); /* Input and output sample rates don't affect the calculation of the heap size. */ - resamplerConfig.lpfOrder = 0; - - result = ma_linear_resampler_get_heap_size(&resamplerConfig, &tempHeapSize); - if (result != MA_SUCCESS) { - return result; /* Failed to retrieve the size of the heap for the resampler. */ - } - - pHeapLayout->resamplerOffset = pHeapLayout->sizeInBytes; - pHeapLayout->sizeInBytes += ma_align_64(tempHeapSize); - - - /* Spatializer. */ - spatializerConfig = ma_engine_node_spatializer_config_init(&baseNodeConfig); - - if (spatializerConfig.channelsIn == 2) { - spatializerConfig.pChannelMapIn = defaultStereoChannelMap; - } - - result = ma_spatializer_get_heap_size(&spatializerConfig, &tempHeapSize); - if (result != MA_SUCCESS) { - return result; /* Failed to retrieve the size of the heap for the spatializer. */ - } - - pHeapLayout->spatializerOffset = pHeapLayout->sizeInBytes; - pHeapLayout->sizeInBytes += ma_align_64(tempHeapSize); - - - /* Gainer. Will not be used if we are not using smoothing. */ - if (pConfig->volumeSmoothTimeInPCMFrames > 0) { - gainerConfig = ma_gainer_config_init(channelsIn, pConfig->volumeSmoothTimeInPCMFrames); - - result = ma_gainer_get_heap_size(&gainerConfig, &tempHeapSize); - if (result != MA_SUCCESS) { - return result; - } - - pHeapLayout->gainerOffset = pHeapLayout->sizeInBytes; - pHeapLayout->sizeInBytes += ma_align_64(tempHeapSize); - } - - - return MA_SUCCESS; -} - -MA_API ma_result ma_engine_node_get_heap_size(const ma_engine_node_config* pConfig, size_t* pHeapSizeInBytes) -{ - ma_result result; - ma_engine_node_heap_layout heapLayout; - - if (pHeapSizeInBytes == NULL) { - return MA_INVALID_ARGS; - } - - *pHeapSizeInBytes = 0; - - result = ma_engine_node_get_heap_layout(pConfig, &heapLayout); - if (result != MA_SUCCESS) { - return result; - } - - *pHeapSizeInBytes = heapLayout.sizeInBytes; - - return MA_SUCCESS; -} - -MA_API ma_result ma_engine_node_init_preallocated(const ma_engine_node_config* pConfig, void* pHeap, ma_engine_node* pEngineNode) -{ - ma_result result; - ma_engine_node_heap_layout heapLayout; - ma_node_config baseNodeConfig; - ma_linear_resampler_config resamplerConfig; - ma_fader_config faderConfig; - ma_spatializer_config spatializerConfig; - ma_panner_config pannerConfig; - ma_gainer_config gainerConfig; - ma_uint32 channelsIn; - ma_uint32 channelsOut; - ma_channel defaultStereoChannelMap[2] = {MA_CHANNEL_SIDE_LEFT, MA_CHANNEL_SIDE_RIGHT}; /* <-- Consistent with the default channel map of a stereo listener. Means channel conversion can run on a fast path. */ - - if (pEngineNode == NULL) { - return MA_INVALID_ARGS; - } - - MA_ZERO_OBJECT(pEngineNode); - - result = ma_engine_node_get_heap_layout(pConfig, &heapLayout); - if (result != MA_SUCCESS) { - return result; - } - - if (pConfig->pinnedListenerIndex != MA_LISTENER_INDEX_CLOSEST && pConfig->pinnedListenerIndex >= ma_engine_get_listener_count(pConfig->pEngine)) { - return MA_INVALID_ARGS; /* Invalid listener. */ - } - - pEngineNode->_pHeap = pHeap; - MA_ZERO_MEMORY(pHeap, heapLayout.sizeInBytes); - - pEngineNode->pEngine = pConfig->pEngine; - pEngineNode->sampleRate = (pConfig->sampleRate > 0) ? pConfig->sampleRate : ma_engine_get_sample_rate(pEngineNode->pEngine); - pEngineNode->volumeSmoothTimeInPCMFrames = pConfig->volumeSmoothTimeInPCMFrames; - pEngineNode->monoExpansionMode = pConfig->monoExpansionMode; - ma_atomic_float_set(&pEngineNode->volume, 1); - pEngineNode->pitch = 1; - pEngineNode->oldPitch = 1; - pEngineNode->oldDopplerPitch = 1; - pEngineNode->isPitchDisabled = pConfig->isPitchDisabled; - pEngineNode->isSpatializationDisabled = pConfig->isSpatializationDisabled; - pEngineNode->pinnedListenerIndex = pConfig->pinnedListenerIndex; - ma_atomic_float_set(&pEngineNode->fadeSettings.volumeBeg, 1); - ma_atomic_float_set(&pEngineNode->fadeSettings.volumeEnd, 1); - ma_atomic_uint64_set(&pEngineNode->fadeSettings.fadeLengthInFrames, (~(ma_uint64)0)); - ma_atomic_uint64_set(&pEngineNode->fadeSettings.absoluteGlobalTimeInFrames, (~(ma_uint64)0)); /* <-- Indicates that the fade should start immediately. */ - - channelsIn = (pConfig->channelsIn != 0) ? pConfig->channelsIn : ma_engine_get_channels(pConfig->pEngine); - channelsOut = (pConfig->channelsOut != 0) ? pConfig->channelsOut : ma_engine_get_channels(pConfig->pEngine); - - /* - If the sample rate of the sound is different to the engine, make sure pitching is enabled so that the resampler - is activated. Not doing this will result in the sound not being resampled if MA_SOUND_FLAG_NO_PITCH is used. - */ - if (pEngineNode->sampleRate != ma_engine_get_sample_rate(pEngineNode->pEngine)) { - pEngineNode->isPitchDisabled = MA_FALSE; - } - - - /* Base node. */ - baseNodeConfig = ma_engine_node_base_node_config_init(pConfig); - baseNodeConfig.pInputChannels = &channelsIn; - baseNodeConfig.pOutputChannels = &channelsOut; - - result = ma_node_init_preallocated(&pConfig->pEngine->nodeGraph, &baseNodeConfig, ma_offset_ptr(pHeap, heapLayout.baseNodeOffset), &pEngineNode->baseNode); - if (result != MA_SUCCESS) { - goto error0; - } - - - /* - We can now initialize the effects we need in order to implement the engine node. There's a - defined order of operations here, mainly centered around when we convert our channels from the - data source's native channel count to the engine's channel count. As a rule, we want to do as - much computation as possible before spatialization because there's a chance that will increase - the channel count, thereby increasing the amount of work needing to be done to process. - */ - - /* We'll always do resampling first. */ - resamplerConfig = ma_linear_resampler_config_init(ma_format_f32, baseNodeConfig.pInputChannels[0], pEngineNode->sampleRate, ma_engine_get_sample_rate(pEngineNode->pEngine)); - resamplerConfig.lpfOrder = 0; /* <-- Need to disable low-pass filtering for pitch shifting for now because there's cases where the biquads are becoming unstable. Need to figure out a better fix for this. */ - - result = ma_linear_resampler_init_preallocated(&resamplerConfig, ma_offset_ptr(pHeap, heapLayout.resamplerOffset), &pEngineNode->resampler); - if (result != MA_SUCCESS) { - goto error1; - } - - - /* After resampling will come the fader. */ - faderConfig = ma_fader_config_init(ma_format_f32, baseNodeConfig.pInputChannels[0], ma_engine_get_sample_rate(pEngineNode->pEngine)); - - result = ma_fader_init(&faderConfig, &pEngineNode->fader); - if (result != MA_SUCCESS) { - goto error2; - } - - - /* - Spatialization comes next. We spatialize based on the node's output channel count. It's up the caller to - ensure channels counts link up correctly in the node graph. - */ - spatializerConfig = ma_engine_node_spatializer_config_init(&baseNodeConfig); - spatializerConfig.gainSmoothTimeInFrames = pEngineNode->pEngine->gainSmoothTimeInFrames; - - if (spatializerConfig.channelsIn == 2) { - spatializerConfig.pChannelMapIn = defaultStereoChannelMap; - } - - result = ma_spatializer_init_preallocated(&spatializerConfig, ma_offset_ptr(pHeap, heapLayout.spatializerOffset), &pEngineNode->spatializer); - if (result != MA_SUCCESS) { - goto error2; - } - - - /* - After spatialization comes panning. We need to do this after spatialization because otherwise we wouldn't - be able to pan mono sounds. - */ - pannerConfig = ma_panner_config_init(ma_format_f32, baseNodeConfig.pOutputChannels[0]); - - result = ma_panner_init(&pannerConfig, &pEngineNode->panner); - if (result != MA_SUCCESS) { - goto error3; - } - - - /* We'll need a gainer for smoothing out volume changes if we have a non-zero smooth time. We apply this before converting to the output channel count. */ - if (pConfig->volumeSmoothTimeInPCMFrames > 0) { - gainerConfig = ma_gainer_config_init(channelsIn, pConfig->volumeSmoothTimeInPCMFrames); - - result = ma_gainer_init_preallocated(&gainerConfig, ma_offset_ptr(pHeap, heapLayout.gainerOffset), &pEngineNode->volumeGainer); - if (result != MA_SUCCESS) { - goto error3; - } - } - - - return MA_SUCCESS; - - /* No need for allocation callbacks here because we use a preallocated heap. */ -error3: ma_spatializer_uninit(&pEngineNode->spatializer, NULL); -error2: ma_linear_resampler_uninit(&pEngineNode->resampler, NULL); -error1: ma_node_uninit(&pEngineNode->baseNode, NULL); -error0: return result; -} - -MA_API ma_result ma_engine_node_init(const ma_engine_node_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_engine_node* pEngineNode) -{ - ma_result result; - size_t heapSizeInBytes; - void* pHeap; - - result = ma_engine_node_get_heap_size(pConfig, &heapSizeInBytes); - if (result != MA_SUCCESS) { - return result; - } - - if (heapSizeInBytes > 0) { - pHeap = ma_malloc(heapSizeInBytes, pAllocationCallbacks); - if (pHeap == NULL) { - return MA_OUT_OF_MEMORY; - } - } else { - pHeap = NULL; - } - - result = ma_engine_node_init_preallocated(pConfig, pHeap, pEngineNode); - if (result != MA_SUCCESS) { - ma_free(pHeap, pAllocationCallbacks); - return result; - } - - pEngineNode->_ownsHeap = MA_TRUE; - return MA_SUCCESS; -} - -MA_API void ma_engine_node_uninit(ma_engine_node* pEngineNode, const ma_allocation_callbacks* pAllocationCallbacks) -{ - /* - The base node always needs to be uninitialized first to ensure it's detached from the graph completely before we - destroy anything that might be in the middle of being used by the processing function. - */ - ma_node_uninit(&pEngineNode->baseNode, pAllocationCallbacks); - - /* Now that the node has been uninitialized we can safely uninitialize the rest. */ - if (pEngineNode->volumeSmoothTimeInPCMFrames > 0) { - ma_gainer_uninit(&pEngineNode->volumeGainer, pAllocationCallbacks); - } - - ma_spatializer_uninit(&pEngineNode->spatializer, pAllocationCallbacks); - ma_linear_resampler_uninit(&pEngineNode->resampler, pAllocationCallbacks); - - /* Free the heap last. */ - if (pEngineNode->_ownsHeap) { - ma_free(pEngineNode->_pHeap, pAllocationCallbacks); - } -} - - -MA_API ma_sound_config ma_sound_config_init(void) -{ - return ma_sound_config_init_2(NULL); -} - -MA_API ma_sound_config ma_sound_config_init_2(ma_engine* pEngine) -{ - ma_sound_config config; - - MA_ZERO_OBJECT(&config); - - if (pEngine != NULL) { - config.monoExpansionMode = pEngine->monoExpansionMode; - } else { - config.monoExpansionMode = ma_mono_expansion_mode_default; - } - - config.rangeEndInPCMFrames = ~((ma_uint64)0); - config.loopPointEndInPCMFrames = ~((ma_uint64)0); - - return config; -} - -MA_API ma_sound_group_config ma_sound_group_config_init(void) -{ - return ma_sound_group_config_init_2(NULL); -} - -MA_API ma_sound_group_config ma_sound_group_config_init_2(ma_engine* pEngine) -{ - ma_sound_group_config config; - - MA_ZERO_OBJECT(&config); - - if (pEngine != NULL) { - config.monoExpansionMode = pEngine->monoExpansionMode; - } else { - config.monoExpansionMode = ma_mono_expansion_mode_default; - } - - return config; -} - - -MA_API ma_engine_config ma_engine_config_init(void) -{ - ma_engine_config config; - - MA_ZERO_OBJECT(&config); - config.listenerCount = 1; /* Always want at least one listener. */ - config.monoExpansionMode = ma_mono_expansion_mode_default; - - return config; -} - - -#if !defined(MA_NO_DEVICE_IO) -static void ma_engine_data_callback_internal(ma_device* pDevice, void* pFramesOut, const void* pFramesIn, ma_uint32 frameCount) -{ - ma_engine* pEngine = (ma_engine*)pDevice->pUserData; - - (void)pFramesIn; - - /* - Experiment: Try processing a resource manager job if we're on the Emscripten build. - - This serves two purposes: - - 1) It ensures jobs are actually processed at some point since we cannot guarantee that the - caller is doing the right thing and calling ma_resource_manager_process_next_job(); and - - 2) It's an attempt at working around an issue where processing jobs on the Emscripten main - loop doesn't work as well as it should. When trying to load sounds without the `DECODE` - flag or with the `ASYNC` flag, the sound data is just not able to be loaded in time - before the callback is processed. I think it's got something to do with the single- - threaded nature of Web, but I'm not entirely sure. - */ - #if !defined(MA_NO_RESOURCE_MANAGER) && defined(MA_EMSCRIPTEN) - { - if (pEngine->pResourceManager != NULL) { - if ((pEngine->pResourceManager->config.flags & MA_RESOURCE_MANAGER_FLAG_NO_THREADING) != 0) { - ma_resource_manager_process_next_job(pEngine->pResourceManager); - } - } - } - #endif - - ma_engine_read_pcm_frames(pEngine, pFramesOut, frameCount, NULL); -} - -static ma_uint32 ma_device__get_processing_size_in_frames(ma_device* pDevice) -{ - /* - The processing size is the period size. The device can have a fixed sized processing size, or - it can be decided by the backend in which case it can be variable. - */ - if (pDevice->playback.intermediaryBufferCap > 0) { - /* Using a fixed sized processing callback. */ - return pDevice->playback.intermediaryBufferCap; - } else { - /* Not using a fixed sized processing callback. Need to estimate the processing size based on the backend. */ - return pDevice->playback.internalPeriodSizeInFrames; - } -} -#endif - -MA_API ma_result ma_engine_init(const ma_engine_config* pConfig, ma_engine* pEngine) -{ - ma_result result; - ma_node_graph_config nodeGraphConfig; - ma_engine_config engineConfig; - ma_spatializer_listener_config listenerConfig; - ma_uint32 iListener; - - if (pEngine == NULL) { - return MA_INVALID_ARGS; - } - - MA_ZERO_OBJECT(pEngine); - - /* The config is allowed to be NULL in which case we use defaults for everything. */ - if (pConfig != NULL) { - engineConfig = *pConfig; - } else { - engineConfig = ma_engine_config_init(); - } - - pEngine->monoExpansionMode = engineConfig.monoExpansionMode; - pEngine->defaultVolumeSmoothTimeInPCMFrames = engineConfig.defaultVolumeSmoothTimeInPCMFrames; - pEngine->onProcess = engineConfig.onProcess; - pEngine->pProcessUserData = engineConfig.pProcessUserData; - ma_allocation_callbacks_init_copy(&pEngine->allocationCallbacks, &engineConfig.allocationCallbacks); - - #if !defined(MA_NO_RESOURCE_MANAGER) - { - pEngine->pResourceManager = engineConfig.pResourceManager; - } - #endif - - #if !defined(MA_NO_DEVICE_IO) - { - pEngine->pDevice = engineConfig.pDevice; - - /* If we don't have a device, we need one. */ - if (pEngine->pDevice == NULL && engineConfig.noDevice == MA_FALSE) { - ma_device_config deviceConfig; - - pEngine->pDevice = (ma_device*)ma_malloc(sizeof(*pEngine->pDevice), &pEngine->allocationCallbacks); - if (pEngine->pDevice == NULL) { - return MA_OUT_OF_MEMORY; - } - - deviceConfig = ma_device_config_init(ma_device_type_playback); - deviceConfig.playback.pDeviceID = engineConfig.pPlaybackDeviceID; - deviceConfig.playback.format = ma_format_f32; - deviceConfig.playback.channels = engineConfig.channels; - deviceConfig.sampleRate = engineConfig.sampleRate; - deviceConfig.dataCallback = (engineConfig.dataCallback != NULL) ? engineConfig.dataCallback : ma_engine_data_callback_internal; - deviceConfig.pUserData = pEngine; - deviceConfig.notificationCallback = engineConfig.notificationCallback; - deviceConfig.periodSizeInFrames = engineConfig.periodSizeInFrames; - deviceConfig.periodSizeInMilliseconds = engineConfig.periodSizeInMilliseconds; - deviceConfig.noPreSilencedOutputBuffer = MA_TRUE; /* We'll always be outputting to every frame in the callback so there's no need for a pre-silenced buffer. */ - deviceConfig.noClip = MA_TRUE; /* The engine will do clipping itself. */ - - if (engineConfig.pContext == NULL) { - ma_context_config contextConfig = ma_context_config_init(); - contextConfig.allocationCallbacks = pEngine->allocationCallbacks; - contextConfig.pLog = engineConfig.pLog; - - /* If the engine config does not specify a log, use the resource manager's if we have one. */ - #ifndef MA_NO_RESOURCE_MANAGER - { - if (contextConfig.pLog == NULL && engineConfig.pResourceManager != NULL) { - contextConfig.pLog = ma_resource_manager_get_log(engineConfig.pResourceManager); - } - } - #endif - - result = ma_device_init_ex(NULL, 0, &contextConfig, &deviceConfig, pEngine->pDevice); - } else { - result = ma_device_init(engineConfig.pContext, &deviceConfig, pEngine->pDevice); - } - - if (result != MA_SUCCESS) { - ma_free(pEngine->pDevice, &pEngine->allocationCallbacks); - pEngine->pDevice = NULL; - return result; - } - - pEngine->ownsDevice = MA_TRUE; - } - - /* Update the channel count and sample rate of the engine config so we can reference it below. */ - if (pEngine->pDevice != NULL) { - engineConfig.channels = pEngine->pDevice->playback.channels; - engineConfig.sampleRate = pEngine->pDevice->sampleRate; - - /* - The processing size used by the engine is determined by engineConfig.periodSizeInFrames. We want - to make this equal to what the device is using for it's period size. If we don't do that, it's - possible that the node graph will split it's processing into multiple passes which can introduce - glitching. - */ - engineConfig.periodSizeInFrames = ma_device__get_processing_size_in_frames(pEngine->pDevice); - } - } - #endif - - if (engineConfig.channels == 0 || engineConfig.sampleRate == 0) { - return MA_INVALID_ARGS; - } - - pEngine->sampleRate = engineConfig.sampleRate; - - /* The engine always uses either the log that was passed into the config, or the context's log is available. */ - if (engineConfig.pLog != NULL) { - pEngine->pLog = engineConfig.pLog; - } else { - #if !defined(MA_NO_DEVICE_IO) - { - pEngine->pLog = ma_device_get_log(pEngine->pDevice); - } - #else - { - pEngine->pLog = NULL; - } - #endif - } - - - /* The engine is a node graph. This needs to be initialized after we have the device so we can determine the channel count. */ - nodeGraphConfig = ma_node_graph_config_init(engineConfig.channels); - nodeGraphConfig.processingSizeInFrames = engineConfig.periodSizeInFrames; - nodeGraphConfig.preMixStackSizeInBytes = engineConfig.preMixStackSizeInBytes; - - result = ma_node_graph_init(&nodeGraphConfig, &pEngine->allocationCallbacks, &pEngine->nodeGraph); - if (result != MA_SUCCESS) { - goto on_error_1; - } - - - /* We need at least one listener. */ - if (engineConfig.listenerCount == 0) { - engineConfig.listenerCount = 1; - } - - if (engineConfig.listenerCount > MA_ENGINE_MAX_LISTENERS) { - result = MA_INVALID_ARGS; /* Too many listeners. */ - goto on_error_1; - } - - for (iListener = 0; iListener < engineConfig.listenerCount; iListener += 1) { - listenerConfig = ma_spatializer_listener_config_init(ma_node_graph_get_channels(&pEngine->nodeGraph)); - - /* - If we're using a device, use the device's channel map for the listener. Otherwise just use - miniaudio's default channel map. - */ - #if !defined(MA_NO_DEVICE_IO) - { - if (pEngine->pDevice != NULL) { - /* - Temporarily disabled. There is a subtle bug here where front-left and front-right - will be used by the device's channel map, but this is not what we want to use for - spatialization. Instead we want to use side-left and side-right. I need to figure - out a better solution for this. For now, disabling the use of device channel maps. - */ - /*listenerConfig.pChannelMapOut = pEngine->pDevice->playback.channelMap;*/ - } - } - #endif - - result = ma_spatializer_listener_init(&listenerConfig, &pEngine->allocationCallbacks, &pEngine->listeners[iListener]); /* TODO: Change this to a pre-allocated heap. */ - if (result != MA_SUCCESS) { - goto on_error_2; - } - - pEngine->listenerCount += 1; - } - - - /* Gain smoothing for spatialized sounds. */ - pEngine->gainSmoothTimeInFrames = engineConfig.gainSmoothTimeInFrames; - if (pEngine->gainSmoothTimeInFrames == 0) { - ma_uint32 gainSmoothTimeInMilliseconds = engineConfig.gainSmoothTimeInMilliseconds; - if (gainSmoothTimeInMilliseconds == 0) { - gainSmoothTimeInMilliseconds = 8; - } - - pEngine->gainSmoothTimeInFrames = (gainSmoothTimeInMilliseconds * ma_engine_get_sample_rate(pEngine)) / 1000; /* 8ms by default. */ - } - - - /* We need a resource manager. */ - #ifndef MA_NO_RESOURCE_MANAGER - { - if (pEngine->pResourceManager == NULL) { - ma_resource_manager_config resourceManagerConfig; - - pEngine->pResourceManager = (ma_resource_manager*)ma_malloc(sizeof(*pEngine->pResourceManager), &pEngine->allocationCallbacks); - if (pEngine->pResourceManager == NULL) { - result = MA_OUT_OF_MEMORY; - goto on_error_2; - } - - resourceManagerConfig = ma_resource_manager_config_init(); - resourceManagerConfig.pLog = pEngine->pLog; /* Always use the engine's log for internally-managed resource managers. */ - resourceManagerConfig.decodedFormat = ma_format_f32; - resourceManagerConfig.decodedChannels = 0; /* Leave the decoded channel count as 0 so we can get good spatialization. */ - resourceManagerConfig.decodedSampleRate = ma_engine_get_sample_rate(pEngine); - ma_allocation_callbacks_init_copy(&resourceManagerConfig.allocationCallbacks, &pEngine->allocationCallbacks); - resourceManagerConfig.pVFS = engineConfig.pResourceManagerVFS; - - /* The Emscripten build cannot use threads unless it's targeting pthreads. */ - #if defined(MA_EMSCRIPTEN) && !defined(__EMSCRIPTEN_PTHREADS__) - { - resourceManagerConfig.jobThreadCount = 0; - resourceManagerConfig.flags |= MA_RESOURCE_MANAGER_FLAG_NO_THREADING; - } - #endif - - result = ma_resource_manager_init(&resourceManagerConfig, pEngine->pResourceManager); - if (result != MA_SUCCESS) { - goto on_error_3; - } - - pEngine->ownsResourceManager = MA_TRUE; - } - } - #endif - - /* Setup some stuff for inlined sounds. That is sounds played with ma_engine_play_sound(). */ - pEngine->inlinedSoundLock = 0; - pEngine->pInlinedSoundHead = NULL; - - /* Start the engine if required. This should always be the last step. */ - #if !defined(MA_NO_DEVICE_IO) - { - if (engineConfig.noAutoStart == MA_FALSE && pEngine->pDevice != NULL) { - result = ma_engine_start(pEngine); - if (result != MA_SUCCESS) { - goto on_error_4; /* Failed to start the engine. */ - } - } - } - #endif - - return MA_SUCCESS; - -#if !defined(MA_NO_DEVICE_IO) -on_error_4: -#endif -#if !defined(MA_NO_RESOURCE_MANAGER) -on_error_3: - if (pEngine->ownsResourceManager) { - ma_free(pEngine->pResourceManager, &pEngine->allocationCallbacks); - } -#endif /* MA_NO_RESOURCE_MANAGER */ -on_error_2: - for (iListener = 0; iListener < pEngine->listenerCount; iListener += 1) { - ma_spatializer_listener_uninit(&pEngine->listeners[iListener], &pEngine->allocationCallbacks); - } - - ma_node_graph_uninit(&pEngine->nodeGraph, &pEngine->allocationCallbacks); -on_error_1: - #if !defined(MA_NO_DEVICE_IO) - { - if (pEngine->ownsDevice) { - ma_device_uninit(pEngine->pDevice); - ma_free(pEngine->pDevice, &pEngine->allocationCallbacks); - } - } - #endif - - return result; -} - -MA_API void ma_engine_uninit(ma_engine* pEngine) -{ - ma_uint32 iListener; - - if (pEngine == NULL) { - return; - } - - /* The device must be uninitialized before the node graph to ensure the audio thread doesn't try accessing it. */ - #if !defined(MA_NO_DEVICE_IO) - { - if (pEngine->ownsDevice) { - ma_device_uninit(pEngine->pDevice); - ma_free(pEngine->pDevice, &pEngine->allocationCallbacks); - } else { - if (pEngine->pDevice != NULL) { - ma_device_stop(pEngine->pDevice); - } - } - } - #endif - - /* - All inlined sounds need to be deleted. I'm going to use a lock here just to future proof in case - I want to do some kind of garbage collection later on. - */ - ma_spinlock_lock(&pEngine->inlinedSoundLock); - { - for (;;) { - ma_sound_inlined* pSoundToDelete = pEngine->pInlinedSoundHead; - if (pSoundToDelete == NULL) { - break; /* Done. */ - } - - pEngine->pInlinedSoundHead = pSoundToDelete->pNext; - - ma_sound_uninit(&pSoundToDelete->sound); - ma_free(pSoundToDelete, &pEngine->allocationCallbacks); - } - } - ma_spinlock_unlock(&pEngine->inlinedSoundLock); - - for (iListener = 0; iListener < pEngine->listenerCount; iListener += 1) { - ma_spatializer_listener_uninit(&pEngine->listeners[iListener], &pEngine->allocationCallbacks); - } - - /* Make sure the node graph is uninitialized after the audio thread has been shutdown to prevent accessing of the node graph after being uninitialized. */ - ma_node_graph_uninit(&pEngine->nodeGraph, &pEngine->allocationCallbacks); - - /* Uninitialize the resource manager last to ensure we don't have a thread still trying to access it. */ -#ifndef MA_NO_RESOURCE_MANAGER - if (pEngine->ownsResourceManager) { - ma_resource_manager_uninit(pEngine->pResourceManager); - ma_free(pEngine->pResourceManager, &pEngine->allocationCallbacks); - } -#endif -} - -MA_API ma_result ma_engine_read_pcm_frames(ma_engine* pEngine, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead) -{ - ma_result result; - ma_uint64 framesRead = 0; - - if (pFramesRead != NULL) { - *pFramesRead = 0; - } - - result = ma_node_graph_read_pcm_frames(&pEngine->nodeGraph, pFramesOut, frameCount, &framesRead); - if (result != MA_SUCCESS) { - return result; - } - - if (pFramesRead != NULL) { - *pFramesRead = framesRead; - } - - if (pEngine->onProcess) { - pEngine->onProcess(pEngine->pProcessUserData, (float*)pFramesOut, framesRead); /* Safe cast to float* because the engine always works on floating point samples. */ - } - - return MA_SUCCESS; -} - -MA_API ma_node_graph* ma_engine_get_node_graph(ma_engine* pEngine) -{ - if (pEngine == NULL) { - return NULL; - } - - return &pEngine->nodeGraph; -} - -#if !defined(MA_NO_RESOURCE_MANAGER) -MA_API ma_resource_manager* ma_engine_get_resource_manager(ma_engine* pEngine) -{ - if (pEngine == NULL) { - return NULL; - } - - #if !defined(MA_NO_RESOURCE_MANAGER) - { - return pEngine->pResourceManager; - } - #else - { - return NULL; - } - #endif -} -#endif - -MA_API ma_device* ma_engine_get_device(ma_engine* pEngine) -{ - if (pEngine == NULL) { - return NULL; - } - - #if !defined(MA_NO_DEVICE_IO) - { - return pEngine->pDevice; - } - #else - { - return NULL; - } - #endif -} - -MA_API ma_log* ma_engine_get_log(ma_engine* pEngine) -{ - if (pEngine == NULL) { - return NULL; - } - - if (pEngine->pLog != NULL) { - return pEngine->pLog; - } else { - #if !defined(MA_NO_DEVICE_IO) - { - return ma_device_get_log(ma_engine_get_device(pEngine)); - } - #else - { - return NULL; - } - #endif - } -} - -MA_API ma_node* ma_engine_get_endpoint(ma_engine* pEngine) -{ - return ma_node_graph_get_endpoint(&pEngine->nodeGraph); -} - -MA_API ma_uint64 ma_engine_get_time_in_pcm_frames(const ma_engine* pEngine) -{ - return ma_node_graph_get_time(&pEngine->nodeGraph); -} - -MA_API ma_uint64 ma_engine_get_time_in_milliseconds(const ma_engine* pEngine) -{ - return ma_engine_get_time_in_pcm_frames(pEngine) * 1000 / ma_engine_get_sample_rate(pEngine); -} - -MA_API ma_result ma_engine_set_time_in_pcm_frames(ma_engine* pEngine, ma_uint64 globalTime) -{ - return ma_node_graph_set_time(&pEngine->nodeGraph, globalTime); -} - -MA_API ma_result ma_engine_set_time_in_milliseconds(ma_engine* pEngine, ma_uint64 globalTime) -{ - return ma_engine_set_time_in_pcm_frames(pEngine, globalTime * ma_engine_get_sample_rate(pEngine) / 1000); -} - -MA_API ma_uint64 ma_engine_get_time(const ma_engine* pEngine) -{ - return ma_engine_get_time_in_pcm_frames(pEngine); -} - -MA_API ma_result ma_engine_set_time(ma_engine* pEngine, ma_uint64 globalTime) -{ - return ma_engine_set_time_in_pcm_frames(pEngine, globalTime); -} - -MA_API ma_uint32 ma_engine_get_channels(const ma_engine* pEngine) -{ - return ma_node_graph_get_channels(&pEngine->nodeGraph); -} - -MA_API ma_uint32 ma_engine_get_sample_rate(const ma_engine* pEngine) -{ - if (pEngine == NULL) { - return 0; - } - - return pEngine->sampleRate; -} - - -MA_API ma_result ma_engine_start(ma_engine* pEngine) -{ - ma_result result; - - if (pEngine == NULL) { - return MA_INVALID_ARGS; - } - - #if !defined(MA_NO_DEVICE_IO) - { - if (pEngine->pDevice != NULL) { - result = ma_device_start(pEngine->pDevice); - } else { - result = MA_INVALID_OPERATION; /* The engine is running without a device which means there's no real notion of "starting" the engine. */ - } - } - #else - { - result = MA_INVALID_OPERATION; /* Device IO is disabled, so there's no real notion of "starting" the engine. */ - } - #endif - - if (result != MA_SUCCESS) { - return result; - } - - return MA_SUCCESS; -} - -MA_API ma_result ma_engine_stop(ma_engine* pEngine) -{ - ma_result result; - - if (pEngine == NULL) { - return MA_INVALID_ARGS; - } - - #if !defined(MA_NO_DEVICE_IO) - { - if (pEngine->pDevice != NULL) { - result = ma_device_stop(pEngine->pDevice); - } else { - result = MA_INVALID_OPERATION; /* The engine is running without a device which means there's no real notion of "stopping" the engine. */ - } - } - #else - { - result = MA_INVALID_OPERATION; /* Device IO is disabled, so there's no real notion of "stopping" the engine. */ - } - #endif - - if (result != MA_SUCCESS) { - return result; - } - - return MA_SUCCESS; -} - -MA_API ma_result ma_engine_set_volume(ma_engine* pEngine, float volume) -{ - if (pEngine == NULL) { - return MA_INVALID_ARGS; - } - - return ma_node_set_output_bus_volume(ma_node_graph_get_endpoint(&pEngine->nodeGraph), 0, volume); -} - -MA_API float ma_engine_get_volume(ma_engine* pEngine) -{ - if (pEngine == NULL) { - return 0; - } - - return ma_node_get_output_bus_volume(ma_node_graph_get_endpoint(&pEngine->nodeGraph), 0); -} - -MA_API ma_result ma_engine_set_gain_db(ma_engine* pEngine, float gainDB) -{ - return ma_engine_set_volume(pEngine, ma_volume_db_to_linear(gainDB)); -} - -MA_API float ma_engine_get_gain_db(ma_engine* pEngine) -{ - return ma_volume_linear_to_db(ma_engine_get_volume(pEngine)); -} - - -MA_API ma_uint32 ma_engine_get_listener_count(const ma_engine* pEngine) -{ - if (pEngine == NULL) { - return 0; - } - - return pEngine->listenerCount; -} - -MA_API ma_uint32 ma_engine_find_closest_listener(const ma_engine* pEngine, float absolutePosX, float absolutePosY, float absolutePosZ) -{ - ma_uint32 iListener; - ma_uint32 iListenerClosest; - float closestLen2 = MA_FLT_MAX; - - if (pEngine == NULL || pEngine->listenerCount == 1) { - return 0; - } - - iListenerClosest = 0; - for (iListener = 0; iListener < pEngine->listenerCount; iListener += 1) { - if (ma_engine_listener_is_enabled(pEngine, iListener)) { - float len2 = ma_vec3f_len2(ma_vec3f_sub(ma_spatializer_listener_get_position(&pEngine->listeners[iListener]), ma_vec3f_init_3f(absolutePosX, absolutePosY, absolutePosZ))); - if (closestLen2 > len2) { - closestLen2 = len2; - iListenerClosest = iListener; - } - } - } - - MA_ASSERT(iListenerClosest < 255); - return iListenerClosest; -} - -MA_API void ma_engine_listener_set_position(ma_engine* pEngine, ma_uint32 listenerIndex, float x, float y, float z) -{ - if (pEngine == NULL || listenerIndex >= pEngine->listenerCount) { - return; - } - - ma_spatializer_listener_set_position(&pEngine->listeners[listenerIndex], x, y, z); -} - -MA_API ma_vec3f ma_engine_listener_get_position(const ma_engine* pEngine, ma_uint32 listenerIndex) -{ - if (pEngine == NULL || listenerIndex >= pEngine->listenerCount) { - return ma_vec3f_init_3f(0, 0, 0); - } - - return ma_spatializer_listener_get_position(&pEngine->listeners[listenerIndex]); -} - -MA_API void ma_engine_listener_set_direction(ma_engine* pEngine, ma_uint32 listenerIndex, float x, float y, float z) -{ - if (pEngine == NULL || listenerIndex >= pEngine->listenerCount) { - return; - } - - ma_spatializer_listener_set_direction(&pEngine->listeners[listenerIndex], x, y, z); -} - -MA_API ma_vec3f ma_engine_listener_get_direction(const ma_engine* pEngine, ma_uint32 listenerIndex) -{ - if (pEngine == NULL || listenerIndex >= pEngine->listenerCount) { - return ma_vec3f_init_3f(0, 0, -1); - } - - return ma_spatializer_listener_get_direction(&pEngine->listeners[listenerIndex]); -} - -MA_API void ma_engine_listener_set_velocity(ma_engine* pEngine, ma_uint32 listenerIndex, float x, float y, float z) -{ - if (pEngine == NULL || listenerIndex >= pEngine->listenerCount) { - return; - } - - ma_spatializer_listener_set_velocity(&pEngine->listeners[listenerIndex], x, y, z); -} - -MA_API ma_vec3f ma_engine_listener_get_velocity(const ma_engine* pEngine, ma_uint32 listenerIndex) -{ - if (pEngine == NULL || listenerIndex >= pEngine->listenerCount) { - return ma_vec3f_init_3f(0, 0, 0); - } - - return ma_spatializer_listener_get_velocity(&pEngine->listeners[listenerIndex]); -} - -MA_API void ma_engine_listener_set_cone(ma_engine* pEngine, ma_uint32 listenerIndex, float innerAngleInRadians, float outerAngleInRadians, float outerGain) -{ - if (pEngine == NULL || listenerIndex >= pEngine->listenerCount) { - return; - } - - ma_spatializer_listener_set_cone(&pEngine->listeners[listenerIndex], innerAngleInRadians, outerAngleInRadians, outerGain); -} - -MA_API void ma_engine_listener_get_cone(const ma_engine* pEngine, ma_uint32 listenerIndex, float* pInnerAngleInRadians, float* pOuterAngleInRadians, float* pOuterGain) -{ - if (pInnerAngleInRadians != NULL) { - *pInnerAngleInRadians = 0; - } - - if (pOuterAngleInRadians != NULL) { - *pOuterAngleInRadians = 0; - } - - if (pOuterGain != NULL) { - *pOuterGain = 0; - } - - if (pEngine == NULL || listenerIndex >= pEngine->listenerCount) { - return; - } - - ma_spatializer_listener_get_cone(&pEngine->listeners[listenerIndex], pInnerAngleInRadians, pOuterAngleInRadians, pOuterGain); -} - -MA_API void ma_engine_listener_set_world_up(ma_engine* pEngine, ma_uint32 listenerIndex, float x, float y, float z) -{ - if (pEngine == NULL || listenerIndex >= pEngine->listenerCount) { - return; - } - - ma_spatializer_listener_set_world_up(&pEngine->listeners[listenerIndex], x, y, z); -} - -MA_API ma_vec3f ma_engine_listener_get_world_up(const ma_engine* pEngine, ma_uint32 listenerIndex) -{ - if (pEngine == NULL || listenerIndex >= pEngine->listenerCount) { - return ma_vec3f_init_3f(0, 1, 0); - } - - return ma_spatializer_listener_get_world_up(&pEngine->listeners[listenerIndex]); -} - -MA_API void ma_engine_listener_set_enabled(ma_engine* pEngine, ma_uint32 listenerIndex, ma_bool32 isEnabled) -{ - if (pEngine == NULL || listenerIndex >= pEngine->listenerCount) { - return; - } - - ma_spatializer_listener_set_enabled(&pEngine->listeners[listenerIndex], isEnabled); -} - -MA_API ma_bool32 ma_engine_listener_is_enabled(const ma_engine* pEngine, ma_uint32 listenerIndex) -{ - if (pEngine == NULL || listenerIndex >= pEngine->listenerCount) { - return MA_FALSE; - } - - return ma_spatializer_listener_is_enabled(&pEngine->listeners[listenerIndex]); -} - - -#ifndef MA_NO_RESOURCE_MANAGER -MA_API ma_result ma_engine_play_sound_ex(ma_engine* pEngine, const char* pFilePath, ma_node* pNode, ma_uint32 nodeInputBusIndex) -{ - ma_result result = MA_SUCCESS; - ma_sound_inlined* pSound = NULL; - ma_sound_inlined* pNextSound = NULL; - - if (pEngine == NULL || pFilePath == NULL) { - return MA_INVALID_ARGS; - } - - /* Attach to the endpoint node if nothing is specified. */ - if (pNode == NULL) { - pNode = ma_node_graph_get_endpoint(&pEngine->nodeGraph); - nodeInputBusIndex = 0; - } - - /* - We want to check if we can recycle an already-allocated inlined sound. Since this is just a - helper I'm not *too* concerned about performance here and I'm happy to use a lock to keep - the implementation simple. Maybe this can be optimized later if there's enough demand, but - if this function is being used it probably means the caller doesn't really care too much. - - What we do is check the atEnd flag. When this is true, we can recycle the sound. Otherwise - we just keep iterating. If we reach the end without finding a sound to recycle we just - allocate a new one. This doesn't scale well for a massive number of sounds being played - simultaneously as we don't ever actually free the sound objects. Some kind of garbage - collection routine might be valuable for this which I'll think about. - */ - ma_spinlock_lock(&pEngine->inlinedSoundLock); - { - ma_uint32 soundFlags = 0; - - for (pNextSound = pEngine->pInlinedSoundHead; pNextSound != NULL; pNextSound = pNextSound->pNext) { - if (ma_sound_at_end(&pNextSound->sound)) { - /* - The sound is at the end which means it's available for recycling. All we need to do - is uninitialize it and reinitialize it. All we're doing is recycling memory. - */ - pSound = pNextSound; - ma_atomic_fetch_sub_32(&pEngine->inlinedSoundCount, 1); - break; - } - } - - if (pSound != NULL) { - /* - We actually want to detach the sound from the list here. The reason is because we want the sound - to be in a consistent state at the non-recycled case to simplify the logic below. - */ - if (pEngine->pInlinedSoundHead == pSound) { - pEngine->pInlinedSoundHead = pSound->pNext; - } - - if (pSound->pPrev != NULL) { - pSound->pPrev->pNext = pSound->pNext; - } - if (pSound->pNext != NULL) { - pSound->pNext->pPrev = pSound->pPrev; - } - - /* Now the previous sound needs to be uninitialized. */ - ma_sound_uninit(&pNextSound->sound); - } else { - /* No sound available for recycling. Allocate one now. */ - pSound = (ma_sound_inlined*)ma_malloc(sizeof(*pSound), &pEngine->allocationCallbacks); - } - - if (pSound != NULL) { /* Safety check for the allocation above. */ - /* - At this point we should have memory allocated for the inlined sound. We just need - to initialize it like a normal sound now. - */ - soundFlags |= MA_SOUND_FLAG_ASYNC; /* For inlined sounds we don't want to be sitting around waiting for stuff to load so force an async load. */ - soundFlags |= MA_SOUND_FLAG_NO_DEFAULT_ATTACHMENT; /* We want specific control over where the sound is attached in the graph. We'll attach it manually just before playing the sound. */ - soundFlags |= MA_SOUND_FLAG_NO_PITCH; /* Pitching isn't usable with inlined sounds, so disable it to save on speed. */ - soundFlags |= MA_SOUND_FLAG_NO_SPATIALIZATION; /* Not currently doing spatialization with inlined sounds, but this might actually change later. For now disable spatialization. Will be removed if we ever add support for spatialization here. */ - - result = ma_sound_init_from_file(pEngine, pFilePath, soundFlags, NULL, NULL, &pSound->sound); - if (result == MA_SUCCESS) { - /* Now attach the sound to the graph. */ - result = ma_node_attach_output_bus(pSound, 0, pNode, nodeInputBusIndex); - if (result == MA_SUCCESS) { - /* At this point the sound should be loaded and we can go ahead and add it to the list. The new item becomes the new head. */ - pSound->pNext = pEngine->pInlinedSoundHead; - pSound->pPrev = NULL; - - pEngine->pInlinedSoundHead = pSound; /* <-- This is what attaches the sound to the list. */ - if (pSound->pNext != NULL) { - pSound->pNext->pPrev = pSound; - } - } else { - ma_free(pSound, &pEngine->allocationCallbacks); - } - } else { - ma_free(pSound, &pEngine->allocationCallbacks); - } - } else { - result = MA_OUT_OF_MEMORY; - } - } - ma_spinlock_unlock(&pEngine->inlinedSoundLock); - - if (result != MA_SUCCESS) { - return result; - } - - /* Finally we can start playing the sound. */ - result = ma_sound_start(&pSound->sound); - if (result != MA_SUCCESS) { - /* Failed to start the sound. We need to mark it for recycling and return an error. */ - ma_atomic_exchange_32(&pSound->sound.atEnd, MA_TRUE); - return result; - } - - ma_atomic_fetch_add_32(&pEngine->inlinedSoundCount, 1); - return result; -} - -MA_API ma_result ma_engine_play_sound(ma_engine* pEngine, const char* pFilePath, ma_sound_group* pGroup) -{ - return ma_engine_play_sound_ex(pEngine, pFilePath, pGroup, 0); -} -#endif - - -static ma_result ma_sound_preinit(ma_engine* pEngine, ma_sound* pSound) -{ - if (pSound == NULL) { - return MA_INVALID_ARGS; - } - - MA_ZERO_OBJECT(pSound); - pSound->seekTarget = MA_SEEK_TARGET_NONE; - - if (pEngine == NULL) { - return MA_INVALID_ARGS; - } - - return MA_SUCCESS; -} - -static ma_result ma_sound_init_from_data_source_internal(ma_engine* pEngine, const ma_sound_config* pConfig, ma_sound* pSound) -{ - ma_result result; - ma_engine_node_config engineNodeConfig; - ma_engine_node_type type; /* Will be set to ma_engine_node_type_group if no data source is specified. */ - - /* Do not clear pSound to zero here - that's done at a higher level with ma_sound_preinit(). */ - MA_ASSERT(pEngine != NULL); - MA_ASSERT(pSound != NULL); - - if (pConfig == NULL) { - return MA_INVALID_ARGS; - } - - pSound->pDataSource = pConfig->pDataSource; - - if (pConfig->pDataSource != NULL) { - type = ma_engine_node_type_sound; - } else { - type = ma_engine_node_type_group; - } - - /* - Sounds are engine nodes. Before we can initialize this we need to determine the channel count. - If we can't do this we need to abort. It's up to the caller to ensure they're using a data - source that provides this information upfront. - */ - engineNodeConfig = ma_engine_node_config_init(pEngine, type, pConfig->flags); - engineNodeConfig.channelsIn = pConfig->channelsIn; - engineNodeConfig.channelsOut = pConfig->channelsOut; - engineNodeConfig.volumeSmoothTimeInPCMFrames = pConfig->volumeSmoothTimeInPCMFrames; - engineNodeConfig.monoExpansionMode = pConfig->monoExpansionMode; - - if (engineNodeConfig.volumeSmoothTimeInPCMFrames == 0) { - engineNodeConfig.volumeSmoothTimeInPCMFrames = pEngine->defaultVolumeSmoothTimeInPCMFrames; - } - - /* If we're loading from a data source the input channel count needs to be the data source's native channel count. */ - if (pConfig->pDataSource != NULL) { - result = ma_data_source_get_data_format(pConfig->pDataSource, NULL, &engineNodeConfig.channelsIn, &engineNodeConfig.sampleRate, NULL, 0); - if (result != MA_SUCCESS) { - return result; /* Failed to retrieve the channel count. */ - } - - if (engineNodeConfig.channelsIn == 0) { - return MA_INVALID_OPERATION; /* Invalid channel count. */ - } - - if (engineNodeConfig.channelsOut == MA_SOUND_SOURCE_CHANNEL_COUNT) { - engineNodeConfig.channelsOut = engineNodeConfig.channelsIn; - } - } - - - /* Getting here means we should have a valid channel count and we can initialize the engine node. */ - result = ma_engine_node_init(&engineNodeConfig, &pEngine->allocationCallbacks, &pSound->engineNode); - if (result != MA_SUCCESS) { - return result; - } - - /* If no attachment is specified, attach the sound straight to the endpoint. */ - if (pConfig->pInitialAttachment == NULL) { - /* No group. Attach straight to the endpoint by default, unless the caller has requested that it not. */ - if ((pConfig->flags & MA_SOUND_FLAG_NO_DEFAULT_ATTACHMENT) == 0) { - result = ma_node_attach_output_bus(pSound, 0, ma_node_graph_get_endpoint(&pEngine->nodeGraph), 0); - } - } else { - /* An attachment is specified. Attach to it by default. The sound has only a single output bus, and the config will specify which input bus to attach to. */ - result = ma_node_attach_output_bus(pSound, 0, pConfig->pInitialAttachment, pConfig->initialAttachmentInputBusIndex); - } - - if (result != MA_SUCCESS) { - ma_engine_node_uninit(&pSound->engineNode, &pEngine->allocationCallbacks); - return result; - } - - - /* Apply initial range and looping state to the data source if applicable. */ - if (pConfig->rangeBegInPCMFrames != 0 || pConfig->rangeEndInPCMFrames != ~((ma_uint64)0)) { - ma_data_source_set_range_in_pcm_frames(ma_sound_get_data_source(pSound), pConfig->rangeBegInPCMFrames, pConfig->rangeEndInPCMFrames); - } - - if (pConfig->loopPointBegInPCMFrames != 0 || pConfig->loopPointEndInPCMFrames != ~((ma_uint64)0)) { - ma_data_source_set_range_in_pcm_frames(ma_sound_get_data_source(pSound), pConfig->loopPointBegInPCMFrames, pConfig->loopPointEndInPCMFrames); - } - - ma_sound_set_looping(pSound, pConfig->isLooping || ((pConfig->flags & MA_SOUND_FLAG_LOOPING) != 0)); - - return MA_SUCCESS; -} - -#ifndef MA_NO_RESOURCE_MANAGER -MA_API ma_result ma_sound_init_from_file_internal(ma_engine* pEngine, const ma_sound_config* pConfig, ma_sound* pSound) -{ - ma_result result = MA_SUCCESS; - ma_uint32 flags; - ma_sound_config config; - ma_resource_manager_pipeline_notifications notifications; - - /* - The engine requires knowledge of the channel count of the underlying data source before it can - initialize the sound. Therefore, we need to make the resource manager wait until initialization - of the underlying data source to be initialized so we can get access to the channel count. To - do this, the MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_WAIT_INIT is forced. - - Because we're initializing the data source before the sound, there's a chance the notification - will get triggered before this function returns. This is OK, so long as the caller is aware of - it and can avoid accessing the sound from within the notification. - */ - flags = pConfig->flags | MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_WAIT_INIT; - if (pConfig->isLooping) { - flags |= MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_LOOPING; - } - - pSound->pResourceManagerDataSource = (ma_resource_manager_data_source*)ma_malloc(sizeof(*pSound->pResourceManagerDataSource), &pEngine->allocationCallbacks); - if (pSound->pResourceManagerDataSource == NULL) { - return MA_OUT_OF_MEMORY; - } - - /* Removed in 0.12. Set pDoneFence on the notifications. */ - notifications = pConfig->initNotifications; - if (pConfig->pDoneFence != NULL && notifications.done.pFence == NULL) { - notifications.done.pFence = pConfig->pDoneFence; - } - - /* - We must wrap everything around the fence if one was specified. This ensures ma_fence_wait() does - not return prematurely before the sound has finished initializing. - */ - if (notifications.done.pFence) { ma_fence_acquire(notifications.done.pFence); } - { - ma_resource_manager_data_source_config resourceManagerDataSourceConfig = ma_resource_manager_data_source_config_init(); - resourceManagerDataSourceConfig.pFilePath = pConfig->pFilePath; - resourceManagerDataSourceConfig.pFilePathW = pConfig->pFilePathW; - resourceManagerDataSourceConfig.flags = flags; - resourceManagerDataSourceConfig.pNotifications = ¬ifications; - resourceManagerDataSourceConfig.initialSeekPointInPCMFrames = pConfig->initialSeekPointInPCMFrames; - resourceManagerDataSourceConfig.rangeBegInPCMFrames = pConfig->rangeBegInPCMFrames; - resourceManagerDataSourceConfig.rangeEndInPCMFrames = pConfig->rangeEndInPCMFrames; - resourceManagerDataSourceConfig.loopPointBegInPCMFrames = pConfig->loopPointBegInPCMFrames; - resourceManagerDataSourceConfig.loopPointEndInPCMFrames = pConfig->loopPointEndInPCMFrames; - resourceManagerDataSourceConfig.isLooping = (flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_LOOPING) != 0; - - result = ma_resource_manager_data_source_init_ex(pEngine->pResourceManager, &resourceManagerDataSourceConfig, pSound->pResourceManagerDataSource); - if (result != MA_SUCCESS) { - goto done; - } - - pSound->ownsDataSource = MA_TRUE; /* <-- Important. Not setting this will result in the resource manager data source never getting uninitialized. */ - - /* We need to use a slightly customized version of the config so we'll need to make a copy. */ - config = *pConfig; - config.pFilePath = NULL; - config.pFilePathW = NULL; - config.pDataSource = pSound->pResourceManagerDataSource; - - result = ma_sound_init_from_data_source_internal(pEngine, &config, pSound); - if (result != MA_SUCCESS) { - ma_resource_manager_data_source_uninit(pSound->pResourceManagerDataSource); - ma_free(pSound->pResourceManagerDataSource, &pEngine->allocationCallbacks); - MA_ZERO_OBJECT(pSound); - goto done; - } - } -done: - if (notifications.done.pFence) { ma_fence_release(notifications.done.pFence); } - return result; -} - -MA_API ma_result ma_sound_init_from_file(ma_engine* pEngine, const char* pFilePath, ma_uint32 flags, ma_sound_group* pGroup, ma_fence* pDoneFence, ma_sound* pSound) -{ - ma_sound_config config; - - if (pFilePath == NULL) { - return MA_INVALID_ARGS; - } - - config = ma_sound_config_init_2(pEngine); - config.pFilePath = pFilePath; - config.flags = flags; - config.pInitialAttachment = pGroup; - config.pDoneFence = pDoneFence; - - return ma_sound_init_ex(pEngine, &config, pSound); -} - -MA_API ma_result ma_sound_init_from_file_w(ma_engine* pEngine, const wchar_t* pFilePath, ma_uint32 flags, ma_sound_group* pGroup, ma_fence* pDoneFence, ma_sound* pSound) -{ - ma_sound_config config; - - if (pFilePath == NULL) { - return MA_INVALID_ARGS; - } - - config = ma_sound_config_init_2(pEngine); - config.pFilePathW = pFilePath; - config.flags = flags; - config.pInitialAttachment = pGroup; - config.pDoneFence = pDoneFence; - - return ma_sound_init_ex(pEngine, &config, pSound); -} - -MA_API ma_result ma_sound_init_copy(ma_engine* pEngine, const ma_sound* pExistingSound, ma_uint32 flags, ma_sound_group* pGroup, ma_sound* pSound) -{ - ma_result result; - ma_sound_config config; - - result = ma_sound_preinit(pEngine, pSound); - if (result != MA_SUCCESS) { - return result; - } - - if (pExistingSound == NULL) { - return MA_INVALID_ARGS; - } - - /* Cloning only works for data buffers (not streams) that are loaded from the resource manager. */ - if (pExistingSound->pResourceManagerDataSource == NULL) { - return MA_INVALID_OPERATION; - } - - /* - We need to make a clone of the data source. If the data source is not a data buffer (i.e. a stream) - this will fail. - */ - pSound->pResourceManagerDataSource = (ma_resource_manager_data_source*)ma_malloc(sizeof(*pSound->pResourceManagerDataSource), &pEngine->allocationCallbacks); - if (pSound->pResourceManagerDataSource == NULL) { - return MA_OUT_OF_MEMORY; - } - - result = ma_resource_manager_data_source_init_copy(pEngine->pResourceManager, pExistingSound->pResourceManagerDataSource, pSound->pResourceManagerDataSource); - if (result != MA_SUCCESS) { - ma_free(pSound->pResourceManagerDataSource, &pEngine->allocationCallbacks); - return result; - } - - config = ma_sound_config_init_2(pEngine); - config.pDataSource = pSound->pResourceManagerDataSource; - config.flags = flags; - config.pInitialAttachment = pGroup; - config.monoExpansionMode = pExistingSound->engineNode.monoExpansionMode; - config.volumeSmoothTimeInPCMFrames = pExistingSound->engineNode.volumeSmoothTimeInPCMFrames; - - result = ma_sound_init_from_data_source_internal(pEngine, &config, pSound); - if (result != MA_SUCCESS) { - ma_resource_manager_data_source_uninit(pSound->pResourceManagerDataSource); - ma_free(pSound->pResourceManagerDataSource, &pEngine->allocationCallbacks); - MA_ZERO_OBJECT(pSound); - return result; - } - - /* Make sure the sound is marked as the owner of the data source or else it will never get uninitialized. */ - pSound->ownsDataSource = MA_TRUE; - - return MA_SUCCESS; -} -#endif - -MA_API ma_result ma_sound_init_from_data_source(ma_engine* pEngine, ma_data_source* pDataSource, ma_uint32 flags, ma_sound_group* pGroup, ma_sound* pSound) -{ - ma_sound_config config = ma_sound_config_init_2(pEngine); - config.pDataSource = pDataSource; - config.flags = flags; - config.pInitialAttachment = pGroup; - return ma_sound_init_ex(pEngine, &config, pSound); -} - -MA_API ma_result ma_sound_init_ex(ma_engine* pEngine, const ma_sound_config* pConfig, ma_sound* pSound) -{ - ma_result result; - - result = ma_sound_preinit(pEngine, pSound); - if (result != MA_SUCCESS) { - return result; - } - - if (pConfig == NULL) { - return MA_INVALID_ARGS; - } - - pSound->endCallback = pConfig->endCallback; - pSound->pEndCallbackUserData = pConfig->pEndCallbackUserData; - - /* We need to load the sound differently depending on whether or not we're loading from a file. */ -#ifndef MA_NO_RESOURCE_MANAGER - if (pConfig->pFilePath != NULL || pConfig->pFilePathW != NULL) { - return ma_sound_init_from_file_internal(pEngine, pConfig, pSound); - } else -#endif - { - /* - Getting here means we're not loading from a file. We may be loading from an already-initialized - data source, or none at all. If we aren't specifying any data source, we'll be initializing - the equivalent to a group. ma_data_source_init_from_data_source_internal() will deal with this - for us, so no special treatment required here. - */ - return ma_sound_init_from_data_source_internal(pEngine, pConfig, pSound); - } -} - -MA_API void ma_sound_uninit(ma_sound* pSound) -{ - if (pSound == NULL) { - return; - } - - /* - Always uninitialize the node first. This ensures it's detached from the graph and does not return until it has done - so which makes thread safety beyond this point trivial. - */ - ma_engine_node_uninit(&pSound->engineNode, &pSound->engineNode.pEngine->allocationCallbacks); - - /* Once the sound is detached from the group we can guarantee that it won't be referenced by the mixer thread which means it's safe for us to destroy the data source. */ -#ifndef MA_NO_RESOURCE_MANAGER - if (pSound->ownsDataSource) { - ma_resource_manager_data_source_uninit(pSound->pResourceManagerDataSource); - ma_free(pSound->pResourceManagerDataSource, &pSound->engineNode.pEngine->allocationCallbacks); - pSound->pDataSource = NULL; - } -#else - MA_ASSERT(pSound->ownsDataSource == MA_FALSE); -#endif -} - -MA_API ma_engine* ma_sound_get_engine(const ma_sound* pSound) -{ - if (pSound == NULL) { - return NULL; - } - - return pSound->engineNode.pEngine; -} - -MA_API ma_data_source* ma_sound_get_data_source(const ma_sound* pSound) -{ - if (pSound == NULL) { - return NULL; - } - - return pSound->pDataSource; -} - -MA_API ma_result ma_sound_start(ma_sound* pSound) -{ - if (pSound == NULL) { - return MA_INVALID_ARGS; - } - - /* If the sound is already playing, do nothing. */ - if (ma_sound_is_playing(pSound)) { - return MA_SUCCESS; - } - - /* If the sound is at the end it means we want to start from the start again. */ - if (ma_sound_at_end(pSound)) { - ma_result result = ma_data_source_seek_to_pcm_frame(pSound->pDataSource, 0); - if (result != MA_SUCCESS && result != MA_NOT_IMPLEMENTED) { - return result; /* Failed to seek back to the start. */ - } - - /* Make sure we clear the end indicator. */ - ma_atomic_exchange_32(&pSound->atEnd, MA_FALSE); - } - - /* Make sure the sound is started. If there's a start delay, the sound won't actually start until the start time is reached. */ - ma_node_set_state(pSound, ma_node_state_started); - - return MA_SUCCESS; -} - -MA_API ma_result ma_sound_stop(ma_sound* pSound) -{ - if (pSound == NULL) { - return MA_INVALID_ARGS; - } - - /* This will stop the sound immediately. Use ma_sound_set_stop_time() to stop the sound at a specific time. */ - ma_node_set_state(pSound, ma_node_state_stopped); - - return MA_SUCCESS; -} - -MA_API ma_result ma_sound_stop_with_fade_in_pcm_frames(ma_sound* pSound, ma_uint64 fadeLengthInFrames) -{ - if (pSound == NULL) { - return MA_INVALID_ARGS; - } - - /* Stopping with a fade out requires us to schedule the stop into the future by the fade length. */ - ma_sound_set_stop_time_with_fade_in_pcm_frames(pSound, ma_engine_get_time_in_pcm_frames(ma_sound_get_engine(pSound)) + fadeLengthInFrames, fadeLengthInFrames); - - return MA_SUCCESS; -} - -MA_API ma_result ma_sound_stop_with_fade_in_milliseconds(ma_sound* pSound, ma_uint64 fadeLengthInMilliseconds) -{ - ma_uint64 sampleRate; - - if (pSound == NULL) { - return MA_INVALID_ARGS; - } - - sampleRate = ma_engine_get_sample_rate(ma_sound_get_engine(pSound)); - - return ma_sound_stop_with_fade_in_pcm_frames(pSound, (fadeLengthInMilliseconds * sampleRate) / 1000); -} - -MA_API void ma_sound_set_volume(ma_sound* pSound, float volume) -{ - if (pSound == NULL) { - return; - } - - ma_engine_node_set_volume(&pSound->engineNode, volume); -} - -MA_API float ma_sound_get_volume(const ma_sound* pSound) -{ - float volume = 0; - - if (pSound == NULL) { - return 0; - } - - ma_engine_node_get_volume(&pSound->engineNode, &volume); - - return volume; -} - -MA_API void ma_sound_set_pan(ma_sound* pSound, float pan) -{ - if (pSound == NULL) { - return; - } - - ma_panner_set_pan(&pSound->engineNode.panner, pan); -} - -MA_API float ma_sound_get_pan(const ma_sound* pSound) -{ - if (pSound == NULL) { - return 0; - } - - return ma_panner_get_pan(&pSound->engineNode.panner); -} - -MA_API void ma_sound_set_pan_mode(ma_sound* pSound, ma_pan_mode panMode) -{ - if (pSound == NULL) { - return; - } - - ma_panner_set_mode(&pSound->engineNode.panner, panMode); -} - -MA_API ma_pan_mode ma_sound_get_pan_mode(const ma_sound* pSound) -{ - if (pSound == NULL) { - return ma_pan_mode_balance; - } - - return ma_panner_get_mode(&pSound->engineNode.panner); -} - -MA_API void ma_sound_set_pitch(ma_sound* pSound, float pitch) -{ - if (pSound == NULL) { - return; - } - - if (pitch <= 0) { - return; - } - - ma_atomic_exchange_explicit_f32(&pSound->engineNode.pitch, pitch, ma_atomic_memory_order_release); -} - -MA_API float ma_sound_get_pitch(const ma_sound* pSound) -{ - if (pSound == NULL) { - return 0; - } - - return ma_atomic_load_f32(&pSound->engineNode.pitch); /* Naughty const-cast for this. */ -} - -MA_API void ma_sound_set_spatialization_enabled(ma_sound* pSound, ma_bool32 enabled) -{ - if (pSound == NULL) { - return; - } - - ma_atomic_exchange_explicit_32(&pSound->engineNode.isSpatializationDisabled, !enabled, ma_atomic_memory_order_release); -} - -MA_API ma_bool32 ma_sound_is_spatialization_enabled(const ma_sound* pSound) -{ - if (pSound == NULL) { - return MA_FALSE; - } - - return ma_engine_node_is_spatialization_enabled(&pSound->engineNode); -} - -MA_API void ma_sound_set_pinned_listener_index(ma_sound* pSound, ma_uint32 listenerIndex) -{ - if (pSound == NULL || listenerIndex >= ma_engine_get_listener_count(ma_sound_get_engine(pSound))) { - return; - } - - ma_atomic_exchange_explicit_32(&pSound->engineNode.pinnedListenerIndex, listenerIndex, ma_atomic_memory_order_release); -} - -MA_API ma_uint32 ma_sound_get_pinned_listener_index(const ma_sound* pSound) -{ - if (pSound == NULL) { - return MA_LISTENER_INDEX_CLOSEST; - } - - return ma_atomic_load_explicit_32(&pSound->engineNode.pinnedListenerIndex, ma_atomic_memory_order_acquire); -} - -MA_API ma_uint32 ma_sound_get_listener_index(const ma_sound* pSound) -{ - ma_uint32 listenerIndex; - - if (pSound == NULL) { - return 0; - } - - listenerIndex = ma_sound_get_pinned_listener_index(pSound); - if (listenerIndex == MA_LISTENER_INDEX_CLOSEST) { - ma_vec3f position = ma_sound_get_position(pSound); - return ma_engine_find_closest_listener(ma_sound_get_engine(pSound), position.x, position.y, position.z); - } - - return listenerIndex; -} - -MA_API ma_vec3f ma_sound_get_direction_to_listener(const ma_sound* pSound) -{ - ma_vec3f relativePos; - ma_engine* pEngine; - - if (pSound == NULL) { - return ma_vec3f_init_3f(0, 0, -1); - } - - pEngine = ma_sound_get_engine(pSound); - if (pEngine == NULL) { - return ma_vec3f_init_3f(0, 0, -1); - } - - ma_spatializer_get_relative_position_and_direction(&pSound->engineNode.spatializer, &pEngine->listeners[ma_sound_get_listener_index(pSound)], &relativePos, NULL); - - return ma_vec3f_normalize(ma_vec3f_neg(relativePos)); -} - -MA_API void ma_sound_set_position(ma_sound* pSound, float x, float y, float z) -{ - if (pSound == NULL) { - return; - } - - ma_spatializer_set_position(&pSound->engineNode.spatializer, x, y, z); -} - -MA_API ma_vec3f ma_sound_get_position(const ma_sound* pSound) -{ - if (pSound == NULL) { - return ma_vec3f_init_3f(0, 0, 0); - } - - return ma_spatializer_get_position(&pSound->engineNode.spatializer); -} - -MA_API void ma_sound_set_direction(ma_sound* pSound, float x, float y, float z) -{ - if (pSound == NULL) { - return; - } - - ma_spatializer_set_direction(&pSound->engineNode.spatializer, x, y, z); -} - -MA_API ma_vec3f ma_sound_get_direction(const ma_sound* pSound) -{ - if (pSound == NULL) { - return ma_vec3f_init_3f(0, 0, 0); - } - - return ma_spatializer_get_direction(&pSound->engineNode.spatializer); -} - -MA_API void ma_sound_set_velocity(ma_sound* pSound, float x, float y, float z) -{ - if (pSound == NULL) { - return; - } - - ma_spatializer_set_velocity(&pSound->engineNode.spatializer, x, y, z); -} - -MA_API ma_vec3f ma_sound_get_velocity(const ma_sound* pSound) -{ - if (pSound == NULL) { - return ma_vec3f_init_3f(0, 0, 0); - } - - return ma_spatializer_get_velocity(&pSound->engineNode.spatializer); -} - -MA_API void ma_sound_set_attenuation_model(ma_sound* pSound, ma_attenuation_model attenuationModel) -{ - if (pSound == NULL) { - return; - } - - ma_spatializer_set_attenuation_model(&pSound->engineNode.spatializer, attenuationModel); -} - -MA_API ma_attenuation_model ma_sound_get_attenuation_model(const ma_sound* pSound) -{ - if (pSound == NULL) { - return ma_attenuation_model_none; - } - - return ma_spatializer_get_attenuation_model(&pSound->engineNode.spatializer); -} - -MA_API void ma_sound_set_positioning(ma_sound* pSound, ma_positioning positioning) -{ - if (pSound == NULL) { - return; - } - - ma_spatializer_set_positioning(&pSound->engineNode.spatializer, positioning); -} - -MA_API ma_positioning ma_sound_get_positioning(const ma_sound* pSound) -{ - if (pSound == NULL) { - return ma_positioning_absolute; - } - - return ma_spatializer_get_positioning(&pSound->engineNode.spatializer); -} - -MA_API void ma_sound_set_rolloff(ma_sound* pSound, float rolloff) -{ - if (pSound == NULL) { - return; - } - - ma_spatializer_set_rolloff(&pSound->engineNode.spatializer, rolloff); -} - -MA_API float ma_sound_get_rolloff(const ma_sound* pSound) -{ - if (pSound == NULL) { - return 0; - } - - return ma_spatializer_get_rolloff(&pSound->engineNode.spatializer); -} - -MA_API void ma_sound_set_min_gain(ma_sound* pSound, float minGain) -{ - if (pSound == NULL) { - return; - } - - ma_spatializer_set_min_gain(&pSound->engineNode.spatializer, minGain); -} - -MA_API float ma_sound_get_min_gain(const ma_sound* pSound) -{ - if (pSound == NULL) { - return 0; - } - - return ma_spatializer_get_min_gain(&pSound->engineNode.spatializer); -} - -MA_API void ma_sound_set_max_gain(ma_sound* pSound, float maxGain) -{ - if (pSound == NULL) { - return; - } - - ma_spatializer_set_max_gain(&pSound->engineNode.spatializer, maxGain); -} - -MA_API float ma_sound_get_max_gain(const ma_sound* pSound) -{ - if (pSound == NULL) { - return 0; - } - - return ma_spatializer_get_max_gain(&pSound->engineNode.spatializer); -} - -MA_API void ma_sound_set_min_distance(ma_sound* pSound, float minDistance) -{ - if (pSound == NULL) { - return; - } - - ma_spatializer_set_min_distance(&pSound->engineNode.spatializer, minDistance); -} - -MA_API float ma_sound_get_min_distance(const ma_sound* pSound) -{ - if (pSound == NULL) { - return 0; - } - - return ma_spatializer_get_min_distance(&pSound->engineNode.spatializer); -} - -MA_API void ma_sound_set_max_distance(ma_sound* pSound, float maxDistance) -{ - if (pSound == NULL) { - return; - } - - ma_spatializer_set_max_distance(&pSound->engineNode.spatializer, maxDistance); -} - -MA_API float ma_sound_get_max_distance(const ma_sound* pSound) -{ - if (pSound == NULL) { - return 0; - } - - return ma_spatializer_get_max_distance(&pSound->engineNode.spatializer); -} - -MA_API void ma_sound_set_cone(ma_sound* pSound, float innerAngleInRadians, float outerAngleInRadians, float outerGain) -{ - if (pSound == NULL) { - return; - } - - ma_spatializer_set_cone(&pSound->engineNode.spatializer, innerAngleInRadians, outerAngleInRadians, outerGain); -} - -MA_API void ma_sound_get_cone(const ma_sound* pSound, float* pInnerAngleInRadians, float* pOuterAngleInRadians, float* pOuterGain) -{ - if (pInnerAngleInRadians != NULL) { - *pInnerAngleInRadians = 0; - } - - if (pOuterAngleInRadians != NULL) { - *pOuterAngleInRadians = 0; - } - - if (pOuterGain != NULL) { - *pOuterGain = 0; - } - - if (pSound == NULL) { - return; - } - - ma_spatializer_get_cone(&pSound->engineNode.spatializer, pInnerAngleInRadians, pOuterAngleInRadians, pOuterGain); -} - -MA_API void ma_sound_set_doppler_factor(ma_sound* pSound, float dopplerFactor) -{ - if (pSound == NULL) { - return; - } - - ma_spatializer_set_doppler_factor(&pSound->engineNode.spatializer, dopplerFactor); -} - -MA_API float ma_sound_get_doppler_factor(const ma_sound* pSound) -{ - if (pSound == NULL) { - return 0; - } - - return ma_spatializer_get_doppler_factor(&pSound->engineNode.spatializer); -} - -MA_API void ma_sound_set_directional_attenuation_factor(ma_sound* pSound, float directionalAttenuationFactor) -{ - if (pSound == NULL) { - return; - } - - ma_spatializer_set_directional_attenuation_factor(&pSound->engineNode.spatializer, directionalAttenuationFactor); -} - -MA_API float ma_sound_get_directional_attenuation_factor(const ma_sound* pSound) -{ - if (pSound == NULL) { - return 1; - } - - return ma_spatializer_get_directional_attenuation_factor(&pSound->engineNode.spatializer); -} - - -MA_API void ma_sound_set_fade_in_pcm_frames(ma_sound* pSound, float volumeBeg, float volumeEnd, ma_uint64 fadeLengthInFrames) -{ - if (pSound == NULL) { - return; - } - - ma_sound_set_fade_start_in_pcm_frames(pSound, volumeBeg, volumeEnd, fadeLengthInFrames, (~(ma_uint64)0)); -} - -MA_API void ma_sound_set_fade_in_milliseconds(ma_sound* pSound, float volumeBeg, float volumeEnd, ma_uint64 fadeLengthInMilliseconds) -{ - if (pSound == NULL) { - return; - } - - ma_sound_set_fade_in_pcm_frames(pSound, volumeBeg, volumeEnd, (fadeLengthInMilliseconds * pSound->engineNode.fader.config.sampleRate) / 1000); -} - -MA_API void ma_sound_set_fade_start_in_pcm_frames(ma_sound* pSound, float volumeBeg, float volumeEnd, ma_uint64 fadeLengthInFrames, ma_uint64 absoluteGlobalTimeInFrames) -{ - if (pSound == NULL) { - return; - } - - /* - We don't want to update the fader at this point because we need to use the engine's current time - to derive the fader's start offset. The timer is being updated on the audio thread so in order to - do this as accurately as possible we'll need to defer this to the audio thread. - */ - ma_atomic_float_set(&pSound->engineNode.fadeSettings.volumeBeg, volumeBeg); - ma_atomic_float_set(&pSound->engineNode.fadeSettings.volumeEnd, volumeEnd); - ma_atomic_uint64_set(&pSound->engineNode.fadeSettings.fadeLengthInFrames, fadeLengthInFrames); - ma_atomic_uint64_set(&pSound->engineNode.fadeSettings.absoluteGlobalTimeInFrames, absoluteGlobalTimeInFrames); -} - -MA_API void ma_sound_set_fade_start_in_milliseconds(ma_sound* pSound, float volumeBeg, float volumeEnd, ma_uint64 fadeLengthInMilliseconds, ma_uint64 absoluteGlobalTimeInMilliseconds) -{ - ma_uint32 sampleRate; - - if (pSound == NULL) { - return; - } - - sampleRate = ma_engine_get_sample_rate(ma_sound_get_engine(pSound)); - - ma_sound_set_fade_start_in_pcm_frames(pSound, volumeBeg, volumeEnd, (fadeLengthInMilliseconds * sampleRate) / 1000, (absoluteGlobalTimeInMilliseconds * sampleRate) / 1000); -} - -MA_API float ma_sound_get_current_fade_volume(const ma_sound* pSound) -{ - if (pSound == NULL) { - return MA_INVALID_ARGS; - } - - return ma_fader_get_current_volume(&pSound->engineNode.fader); -} - -MA_API void ma_sound_set_start_time_in_pcm_frames(ma_sound* pSound, ma_uint64 absoluteGlobalTimeInFrames) -{ - if (pSound == NULL) { - return; - } - - ma_node_set_state_time(pSound, ma_node_state_started, absoluteGlobalTimeInFrames); -} - -MA_API void ma_sound_set_start_time_in_milliseconds(ma_sound* pSound, ma_uint64 absoluteGlobalTimeInMilliseconds) -{ - if (pSound == NULL) { - return; - } - - ma_sound_set_start_time_in_pcm_frames(pSound, absoluteGlobalTimeInMilliseconds * ma_engine_get_sample_rate(ma_sound_get_engine(pSound)) / 1000); -} - -MA_API void ma_sound_set_stop_time_in_pcm_frames(ma_sound* pSound, ma_uint64 absoluteGlobalTimeInFrames) -{ - if (pSound == NULL) { - return; - } - - ma_sound_set_stop_time_with_fade_in_pcm_frames(pSound, absoluteGlobalTimeInFrames, 0); -} - -MA_API void ma_sound_set_stop_time_in_milliseconds(ma_sound* pSound, ma_uint64 absoluteGlobalTimeInMilliseconds) -{ - if (pSound == NULL) { - return; - } - - ma_sound_set_stop_time_in_pcm_frames(pSound, absoluteGlobalTimeInMilliseconds * ma_engine_get_sample_rate(ma_sound_get_engine(pSound)) / 1000); -} - -MA_API void ma_sound_set_stop_time_with_fade_in_pcm_frames(ma_sound* pSound, ma_uint64 stopAbsoluteGlobalTimeInFrames, ma_uint64 fadeLengthInFrames) -{ - if (pSound == NULL) { - return; - } - - if (fadeLengthInFrames > 0) { - if (fadeLengthInFrames > stopAbsoluteGlobalTimeInFrames) { - fadeLengthInFrames = stopAbsoluteGlobalTimeInFrames; - } - - ma_sound_set_fade_start_in_pcm_frames(pSound, -1, 0, fadeLengthInFrames, stopAbsoluteGlobalTimeInFrames - fadeLengthInFrames); - } - - ma_node_set_state_time(pSound, ma_node_state_stopped, stopAbsoluteGlobalTimeInFrames); -} - -MA_API void ma_sound_set_stop_time_with_fade_in_milliseconds(ma_sound* pSound, ma_uint64 stopAbsoluteGlobalTimeInMilliseconds, ma_uint64 fadeLengthInMilliseconds) -{ - ma_uint32 sampleRate; - - if (pSound == NULL) { - return; - } - - sampleRate = ma_engine_get_sample_rate(ma_sound_get_engine(pSound)); - - ma_sound_set_stop_time_with_fade_in_pcm_frames(pSound, (stopAbsoluteGlobalTimeInMilliseconds * sampleRate) / 1000, (fadeLengthInMilliseconds * sampleRate) / 1000); -} - -MA_API ma_bool32 ma_sound_is_playing(const ma_sound* pSound) -{ - if (pSound == NULL) { - return MA_FALSE; - } - - return ma_node_get_state_by_time(pSound, ma_engine_get_time_in_pcm_frames(ma_sound_get_engine(pSound))) == ma_node_state_started; -} - -MA_API ma_uint64 ma_sound_get_time_in_pcm_frames(const ma_sound* pSound) -{ - if (pSound == NULL) { - return 0; - } - - return ma_node_get_time(pSound); -} - -MA_API ma_uint64 ma_sound_get_time_in_milliseconds(const ma_sound* pSound) -{ - return ma_sound_get_time_in_pcm_frames(pSound) * 1000 / ma_engine_get_sample_rate(ma_sound_get_engine(pSound)); -} - -MA_API void ma_sound_set_looping(ma_sound* pSound, ma_bool32 isLooping) -{ - if (pSound == NULL) { - return; - } - - /* Looping is only a valid concept if the sound is backed by a data source. */ - if (pSound->pDataSource == NULL) { - return; - } - - /* The looping state needs to be applied to the data source in order for any looping to actually happen. */ - ma_data_source_set_looping(pSound->pDataSource, isLooping); -} - -MA_API ma_bool32 ma_sound_is_looping(const ma_sound* pSound) -{ - if (pSound == NULL) { - return MA_FALSE; - } - - /* There is no notion of looping for sounds that are not backed by a data source. */ - if (pSound->pDataSource == NULL) { - return MA_FALSE; - } - - return ma_data_source_is_looping(pSound->pDataSource); -} - -MA_API ma_bool32 ma_sound_at_end(const ma_sound* pSound) -{ - if (pSound == NULL) { - return MA_FALSE; - } - - /* There is no notion of an end of a sound if it's not backed by a data source. */ - if (pSound->pDataSource == NULL) { - return MA_FALSE; - } - - return ma_sound_get_at_end(pSound); -} - -MA_API ma_result ma_sound_seek_to_pcm_frame(ma_sound* pSound, ma_uint64 frameIndex) -{ - if (pSound == NULL) { - return MA_INVALID_ARGS; - } - - /* Seeking is only valid for sounds that are backed by a data source. */ - if (pSound->pDataSource == NULL) { - return MA_INVALID_OPERATION; - } - - /* We can't be seeking while reading at the same time. We just set the seek target and get the mixing thread to do the actual seek. */ - ma_atomic_exchange_64(&pSound->seekTarget, frameIndex); - - return MA_SUCCESS; -} - -MA_API ma_result ma_sound_seek_to_second(ma_sound* pSound, float seekPointInSeconds) -{ - ma_uint64 frameIndex; - ma_uint32 sampleRate; - ma_result result; - - if (pSound == NULL) { - return MA_INVALID_ARGS; - } - - result = ma_sound_get_data_format(pSound, NULL, NULL, &sampleRate, NULL, 0); - if (result != MA_SUCCESS) { - return result; - } - - /* We need PCM frames. We need to convert first */ - frameIndex = (ma_uint64)(seekPointInSeconds * sampleRate); - - return ma_sound_seek_to_pcm_frame(pSound, frameIndex); -} - -MA_API ma_result ma_sound_get_data_format(ma_sound* pSound, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate, ma_channel* pChannelMap, size_t channelMapCap) -{ - if (pSound == NULL) { - return MA_INVALID_ARGS; - } - - /* The data format is retrieved directly from the data source if the sound is backed by one. Otherwise we pull it from the node. */ - if (pSound->pDataSource == NULL) { - ma_uint32 channels; - - if (pFormat != NULL) { - *pFormat = ma_format_f32; - } - - channels = ma_node_get_input_channels(&pSound->engineNode, 0); - if (pChannels != NULL) { - *pChannels = channels; - } - - if (pSampleRate != NULL) { - *pSampleRate = pSound->engineNode.resampler.config.sampleRateIn; - } - - if (pChannelMap != NULL) { - ma_channel_map_init_standard(ma_standard_channel_map_default, pChannelMap, channelMapCap, channels); - } - - return MA_SUCCESS; - } else { - return ma_data_source_get_data_format(pSound->pDataSource, pFormat, pChannels, pSampleRate, pChannelMap, channelMapCap); - } -} - -MA_API ma_result ma_sound_get_cursor_in_pcm_frames(ma_sound* pSound, ma_uint64* pCursor) -{ - ma_uint64 seekTarget; - - if (pSound == NULL) { - return MA_INVALID_ARGS; - } - - /* The notion of a cursor is only valid for sounds that are backed by a data source. */ - if (pSound->pDataSource == NULL) { - return MA_INVALID_OPERATION; - } - - seekTarget = ma_atomic_load_64(&pSound->seekTarget); - if (seekTarget != MA_SEEK_TARGET_NONE) { - *pCursor = seekTarget; - return MA_SUCCESS; - } else { - return ma_data_source_get_cursor_in_pcm_frames(pSound->pDataSource, pCursor); - } -} - -MA_API ma_result ma_sound_get_length_in_pcm_frames(ma_sound* pSound, ma_uint64* pLength) -{ - if (pSound == NULL) { - return MA_INVALID_ARGS; - } - - /* The notion of a sound length is only valid for sounds that are backed by a data source. */ - if (pSound->pDataSource == NULL) { - return MA_INVALID_OPERATION; - } - - return ma_data_source_get_length_in_pcm_frames(pSound->pDataSource, pLength); -} - -MA_API ma_result ma_sound_get_cursor_in_seconds(ma_sound* pSound, float* pCursor) -{ - ma_result result; - ma_uint64 cursorInPCMFrames; - ma_uint32 sampleRate; - - if (pCursor != NULL) { - *pCursor = 0; - } - - result = ma_sound_get_cursor_in_pcm_frames(pSound, &cursorInPCMFrames); - if (result != MA_SUCCESS) { - return result; - } - - result = ma_sound_get_data_format(pSound, NULL, NULL, &sampleRate, NULL, 0); - if (result != MA_SUCCESS) { - return result; - } - - /* VC6 does not support division of unsigned 64-bit integers with floating point numbers. Need to use a signed number. This shouldn't effect anything in practice. */ - *pCursor = (ma_int64)cursorInPCMFrames / (float)sampleRate; - - return MA_SUCCESS; -} - -MA_API ma_result ma_sound_get_length_in_seconds(ma_sound* pSound, float* pLength) -{ - if (pSound == NULL) { - return MA_INVALID_ARGS; - } - - /* The notion of a sound length is only valid for sounds that are backed by a data source. */ - if (pSound->pDataSource == NULL) { - return MA_INVALID_OPERATION; - } - - return ma_data_source_get_length_in_seconds(pSound->pDataSource, pLength); -} - -MA_API ma_result ma_sound_set_end_callback(ma_sound* pSound, ma_sound_end_proc callback, void* pUserData) -{ - if (pSound == NULL) { - return MA_INVALID_ARGS; - } - - /* The notion of an end is only valid for sounds that are backed by a data source. */ - if (pSound->pDataSource == NULL) { - return MA_INVALID_OPERATION; - } - - pSound->endCallback = callback; - pSound->pEndCallbackUserData = pUserData; - - return MA_SUCCESS; -} - - -MA_API ma_result ma_sound_group_init(ma_engine* pEngine, ma_uint32 flags, ma_sound_group* pParentGroup, ma_sound_group* pGroup) -{ - ma_sound_group_config config = ma_sound_group_config_init_2(pEngine); - config.flags = flags; - config.pInitialAttachment = pParentGroup; - return ma_sound_group_init_ex(pEngine, &config, pGroup); -} - -MA_API ma_result ma_sound_group_init_ex(ma_engine* pEngine, const ma_sound_group_config* pConfig, ma_sound_group* pGroup) -{ - ma_sound_config soundConfig; - - if (pGroup == NULL) { - return MA_INVALID_ARGS; - } - - MA_ZERO_OBJECT(pGroup); - - if (pConfig == NULL) { - return MA_INVALID_ARGS; - } - - /* A sound group is just a sound without a data source. */ - soundConfig = *pConfig; - soundConfig.pFilePath = NULL; - soundConfig.pFilePathW = NULL; - soundConfig.pDataSource = NULL; - - /* - Groups need to have spatialization disabled by default because I think it'll be pretty rare - that programs will want to spatialize groups (but not unheard of). Certainly it feels like - disabling this by default feels like the right option. Spatialization can be enabled with a - call to ma_sound_group_set_spatialization_enabled(). - */ - soundConfig.flags |= MA_SOUND_FLAG_NO_SPATIALIZATION; - - return ma_sound_init_ex(pEngine, &soundConfig, pGroup); -} - -MA_API void ma_sound_group_uninit(ma_sound_group* pGroup) -{ - ma_sound_uninit(pGroup); -} - -MA_API ma_engine* ma_sound_group_get_engine(const ma_sound_group* pGroup) -{ - return ma_sound_get_engine(pGroup); -} - -MA_API ma_result ma_sound_group_start(ma_sound_group* pGroup) -{ - return ma_sound_start(pGroup); -} - -MA_API ma_result ma_sound_group_stop(ma_sound_group* pGroup) -{ - return ma_sound_stop(pGroup); -} - -MA_API void ma_sound_group_set_volume(ma_sound_group* pGroup, float volume) -{ - ma_sound_set_volume(pGroup, volume); -} - -MA_API float ma_sound_group_get_volume(const ma_sound_group* pGroup) -{ - return ma_sound_get_volume(pGroup); -} - -MA_API void ma_sound_group_set_pan(ma_sound_group* pGroup, float pan) -{ - ma_sound_set_pan(pGroup, pan); -} - -MA_API float ma_sound_group_get_pan(const ma_sound_group* pGroup) -{ - return ma_sound_get_pan(pGroup); -} - -MA_API void ma_sound_group_set_pan_mode(ma_sound_group* pGroup, ma_pan_mode panMode) -{ - ma_sound_set_pan_mode(pGroup, panMode); -} - -MA_API ma_pan_mode ma_sound_group_get_pan_mode(const ma_sound_group* pGroup) -{ - return ma_sound_get_pan_mode(pGroup); -} - -MA_API void ma_sound_group_set_pitch(ma_sound_group* pGroup, float pitch) -{ - ma_sound_set_pitch(pGroup, pitch); -} - -MA_API float ma_sound_group_get_pitch(const ma_sound_group* pGroup) -{ - return ma_sound_get_pitch(pGroup); -} - -MA_API void ma_sound_group_set_spatialization_enabled(ma_sound_group* pGroup, ma_bool32 enabled) -{ - ma_sound_set_spatialization_enabled(pGroup, enabled); -} - -MA_API ma_bool32 ma_sound_group_is_spatialization_enabled(const ma_sound_group* pGroup) -{ - return ma_sound_is_spatialization_enabled(pGroup); -} - -MA_API void ma_sound_group_set_pinned_listener_index(ma_sound_group* pGroup, ma_uint32 listenerIndex) -{ - ma_sound_set_pinned_listener_index(pGroup, listenerIndex); -} - -MA_API ma_uint32 ma_sound_group_get_pinned_listener_index(const ma_sound_group* pGroup) -{ - return ma_sound_get_pinned_listener_index(pGroup); -} - -MA_API ma_uint32 ma_sound_group_get_listener_index(const ma_sound_group* pGroup) -{ - return ma_sound_get_listener_index(pGroup); -} - -MA_API ma_vec3f ma_sound_group_get_direction_to_listener(const ma_sound_group* pGroup) -{ - return ma_sound_get_direction_to_listener(pGroup); -} - -MA_API void ma_sound_group_set_position(ma_sound_group* pGroup, float x, float y, float z) -{ - ma_sound_set_position(pGroup, x, y, z); -} - -MA_API ma_vec3f ma_sound_group_get_position(const ma_sound_group* pGroup) -{ - return ma_sound_get_position(pGroup); -} - -MA_API void ma_sound_group_set_direction(ma_sound_group* pGroup, float x, float y, float z) -{ - ma_sound_set_direction(pGroup, x, y, z); -} - -MA_API ma_vec3f ma_sound_group_get_direction(const ma_sound_group* pGroup) -{ - return ma_sound_get_direction(pGroup); -} - -MA_API void ma_sound_group_set_velocity(ma_sound_group* pGroup, float x, float y, float z) -{ - ma_sound_set_velocity(pGroup, x, y, z); -} - -MA_API ma_vec3f ma_sound_group_get_velocity(const ma_sound_group* pGroup) -{ - return ma_sound_get_velocity(pGroup); -} - -MA_API void ma_sound_group_set_attenuation_model(ma_sound_group* pGroup, ma_attenuation_model attenuationModel) -{ - ma_sound_set_attenuation_model(pGroup, attenuationModel); -} - -MA_API ma_attenuation_model ma_sound_group_get_attenuation_model(const ma_sound_group* pGroup) -{ - return ma_sound_get_attenuation_model(pGroup); -} - -MA_API void ma_sound_group_set_positioning(ma_sound_group* pGroup, ma_positioning positioning) -{ - ma_sound_set_positioning(pGroup, positioning); -} - -MA_API ma_positioning ma_sound_group_get_positioning(const ma_sound_group* pGroup) -{ - return ma_sound_get_positioning(pGroup); -} - -MA_API void ma_sound_group_set_rolloff(ma_sound_group* pGroup, float rolloff) -{ - ma_sound_set_rolloff(pGroup, rolloff); -} - -MA_API float ma_sound_group_get_rolloff(const ma_sound_group* pGroup) -{ - return ma_sound_get_rolloff(pGroup); -} - -MA_API void ma_sound_group_set_min_gain(ma_sound_group* pGroup, float minGain) -{ - ma_sound_set_min_gain(pGroup, minGain); -} - -MA_API float ma_sound_group_get_min_gain(const ma_sound_group* pGroup) -{ - return ma_sound_get_min_gain(pGroup); -} - -MA_API void ma_sound_group_set_max_gain(ma_sound_group* pGroup, float maxGain) -{ - ma_sound_set_max_gain(pGroup, maxGain); -} - -MA_API float ma_sound_group_get_max_gain(const ma_sound_group* pGroup) -{ - return ma_sound_get_max_gain(pGroup); -} - -MA_API void ma_sound_group_set_min_distance(ma_sound_group* pGroup, float minDistance) -{ - ma_sound_set_min_distance(pGroup, minDistance); -} - -MA_API float ma_sound_group_get_min_distance(const ma_sound_group* pGroup) -{ - return ma_sound_get_min_distance(pGroup); -} - -MA_API void ma_sound_group_set_max_distance(ma_sound_group* pGroup, float maxDistance) -{ - ma_sound_set_max_distance(pGroup, maxDistance); -} - -MA_API float ma_sound_group_get_max_distance(const ma_sound_group* pGroup) -{ - return ma_sound_get_max_distance(pGroup); -} - -MA_API void ma_sound_group_set_cone(ma_sound_group* pGroup, float innerAngleInRadians, float outerAngleInRadians, float outerGain) -{ - ma_sound_set_cone(pGroup, innerAngleInRadians, outerAngleInRadians, outerGain); -} - -MA_API void ma_sound_group_get_cone(const ma_sound_group* pGroup, float* pInnerAngleInRadians, float* pOuterAngleInRadians, float* pOuterGain) -{ - ma_sound_get_cone(pGroup, pInnerAngleInRadians, pOuterAngleInRadians, pOuterGain); -} - -MA_API void ma_sound_group_set_doppler_factor(ma_sound_group* pGroup, float dopplerFactor) -{ - ma_sound_set_doppler_factor(pGroup, dopplerFactor); -} - -MA_API float ma_sound_group_get_doppler_factor(const ma_sound_group* pGroup) -{ - return ma_sound_get_doppler_factor(pGroup); -} - -MA_API void ma_sound_group_set_directional_attenuation_factor(ma_sound_group* pGroup, float directionalAttenuationFactor) -{ - ma_sound_set_directional_attenuation_factor(pGroup, directionalAttenuationFactor); -} - -MA_API float ma_sound_group_get_directional_attenuation_factor(const ma_sound_group* pGroup) -{ - return ma_sound_get_directional_attenuation_factor(pGroup); -} - -MA_API void ma_sound_group_set_fade_in_pcm_frames(ma_sound_group* pGroup, float volumeBeg, float volumeEnd, ma_uint64 fadeLengthInFrames) -{ - ma_sound_set_fade_in_pcm_frames(pGroup, volumeBeg, volumeEnd, fadeLengthInFrames); -} - -MA_API void ma_sound_group_set_fade_in_milliseconds(ma_sound_group* pGroup, float volumeBeg, float volumeEnd, ma_uint64 fadeLengthInMilliseconds) -{ - ma_sound_set_fade_in_milliseconds(pGroup, volumeBeg, volumeEnd, fadeLengthInMilliseconds); -} - -MA_API float ma_sound_group_get_current_fade_volume(ma_sound_group* pGroup) -{ - return ma_sound_get_current_fade_volume(pGroup); -} - -MA_API void ma_sound_group_set_start_time_in_pcm_frames(ma_sound_group* pGroup, ma_uint64 absoluteGlobalTimeInFrames) -{ - ma_sound_set_start_time_in_pcm_frames(pGroup, absoluteGlobalTimeInFrames); -} - -MA_API void ma_sound_group_set_start_time_in_milliseconds(ma_sound_group* pGroup, ma_uint64 absoluteGlobalTimeInMilliseconds) -{ - ma_sound_set_start_time_in_milliseconds(pGroup, absoluteGlobalTimeInMilliseconds); -} - -MA_API void ma_sound_group_set_stop_time_in_pcm_frames(ma_sound_group* pGroup, ma_uint64 absoluteGlobalTimeInFrames) -{ - ma_sound_set_stop_time_in_pcm_frames(pGroup, absoluteGlobalTimeInFrames); -} - -MA_API void ma_sound_group_set_stop_time_in_milliseconds(ma_sound_group* pGroup, ma_uint64 absoluteGlobalTimeInMilliseconds) -{ - ma_sound_set_stop_time_in_milliseconds(pGroup, absoluteGlobalTimeInMilliseconds); -} - -MA_API ma_bool32 ma_sound_group_is_playing(const ma_sound_group* pGroup) -{ - return ma_sound_is_playing(pGroup); -} - -MA_API ma_uint64 ma_sound_group_get_time_in_pcm_frames(const ma_sound_group* pGroup) -{ - return ma_sound_get_time_in_pcm_frames(pGroup); -} -#endif /* MA_NO_ENGINE */ -/* END SECTION: miniaudio_engine.c */ - - - -/************************************************************************************************************************************************************** -*************************************************************************************************************************************************************** - -Auto Generated -============== -All code below is auto-generated from a tool. This mostly consists of decoding backend implementations such as ma_dr_wav, ma_dr_flac, etc. If you find a bug in the -code below please report the bug to the respective repository for the relevant project (probably dr_libs). - -*************************************************************************************************************************************************************** -**************************************************************************************************************************************************************/ -#if !defined(MA_NO_WAV) && (!defined(MA_NO_DECODING) || !defined(MA_NO_ENCODING)) -#if !defined(MA_DR_WAV_IMPLEMENTATION) -/* dr_wav_c begin */ -#ifndef ma_dr_wav_c -#define ma_dr_wav_c -#ifdef __MRC__ -#pragma options opt off -#endif -#include -#include -#include -#ifndef MA_DR_WAV_NO_STDIO -#include -#ifndef MA_DR_WAV_NO_WCHAR -#include -#endif -#endif -#ifndef MA_DR_WAV_ASSERT -#include -#define MA_DR_WAV_ASSERT(expression) assert(expression) -#endif -#ifndef MA_DR_WAV_MALLOC -#define MA_DR_WAV_MALLOC(sz) malloc((sz)) -#endif -#ifndef MA_DR_WAV_REALLOC -#define MA_DR_WAV_REALLOC(p, sz) realloc((p), (sz)) -#endif -#ifndef MA_DR_WAV_FREE -#define MA_DR_WAV_FREE(p) free((p)) -#endif -#ifndef MA_DR_WAV_COPY_MEMORY -#define MA_DR_WAV_COPY_MEMORY(dst, src, sz) memcpy((dst), (src), (sz)) -#endif -#ifndef MA_DR_WAV_ZERO_MEMORY -#define MA_DR_WAV_ZERO_MEMORY(p, sz) memset((p), 0, (sz)) -#endif -#ifndef MA_DR_WAV_ZERO_OBJECT -#define MA_DR_WAV_ZERO_OBJECT(p) MA_DR_WAV_ZERO_MEMORY((p), sizeof(*p)) -#endif -#define ma_dr_wav_countof(x) (sizeof(x) / sizeof(x[0])) -#define ma_dr_wav_align(x, a) ((((x) + (a) - 1) / (a)) * (a)) -#define ma_dr_wav_min(a, b) (((a) < (b)) ? (a) : (b)) -#define ma_dr_wav_max(a, b) (((a) > (b)) ? (a) : (b)) -#define ma_dr_wav_clamp(x, lo, hi) (ma_dr_wav_max((lo), ma_dr_wav_min((hi), (x)))) -#define ma_dr_wav_offset_ptr(p, offset) (((ma_uint8*)(p)) + (offset)) -#define MA_DR_WAV_MAX_SIMD_VECTOR_SIZE 32 -#define MA_DR_WAV_INT64_MIN ((ma_int64) ((ma_uint64)0x80000000 << 32)) -#define MA_DR_WAV_INT64_MAX ((ma_int64)(((ma_uint64)0x7FFFFFFF << 32) | 0xFFFFFFFF)) -#if defined(_MSC_VER) && _MSC_VER >= 1400 - #define MA_DR_WAV_HAS_BYTESWAP16_INTRINSIC - #define MA_DR_WAV_HAS_BYTESWAP32_INTRINSIC - #define MA_DR_WAV_HAS_BYTESWAP64_INTRINSIC -#elif defined(__clang__) - #if defined(__has_builtin) - #if __has_builtin(__builtin_bswap16) - #define MA_DR_WAV_HAS_BYTESWAP16_INTRINSIC - #endif - #if __has_builtin(__builtin_bswap32) - #define MA_DR_WAV_HAS_BYTESWAP32_INTRINSIC - #endif - #if __has_builtin(__builtin_bswap64) - #define MA_DR_WAV_HAS_BYTESWAP64_INTRINSIC - #endif - #endif -#elif defined(__GNUC__) - #if ((__GNUC__ > 4) || (__GNUC__ == 4 && __GNUC_MINOR__ >= 3)) - #define MA_DR_WAV_HAS_BYTESWAP32_INTRINSIC - #define MA_DR_WAV_HAS_BYTESWAP64_INTRINSIC - #endif - #if ((__GNUC__ > 4) || (__GNUC__ == 4 && __GNUC_MINOR__ >= 8)) - #define MA_DR_WAV_HAS_BYTESWAP16_INTRINSIC - #endif -#endif -MA_API void ma_dr_wav_version(ma_uint32* pMajor, ma_uint32* pMinor, ma_uint32* pRevision) -{ - if (pMajor) { - *pMajor = MA_DR_WAV_VERSION_MAJOR; - } - if (pMinor) { - *pMinor = MA_DR_WAV_VERSION_MINOR; - } - if (pRevision) { - *pRevision = MA_DR_WAV_VERSION_REVISION; - } -} -MA_API const char* ma_dr_wav_version_string(void) -{ - return MA_DR_WAV_VERSION_STRING; -} -#ifndef MA_DR_WAV_MAX_SAMPLE_RATE -#define MA_DR_WAV_MAX_SAMPLE_RATE 384000 -#endif -#ifndef MA_DR_WAV_MAX_CHANNELS -#define MA_DR_WAV_MAX_CHANNELS 256 -#endif -#ifndef MA_DR_WAV_MAX_BITS_PER_SAMPLE -#define MA_DR_WAV_MAX_BITS_PER_SAMPLE 64 -#endif -static const ma_uint8 ma_dr_wavGUID_W64_RIFF[16] = {0x72,0x69,0x66,0x66, 0x2E,0x91, 0xCF,0x11, 0xA5,0xD6, 0x28,0xDB,0x04,0xC1,0x00,0x00}; -static const ma_uint8 ma_dr_wavGUID_W64_WAVE[16] = {0x77,0x61,0x76,0x65, 0xF3,0xAC, 0xD3,0x11, 0x8C,0xD1, 0x00,0xC0,0x4F,0x8E,0xDB,0x8A}; -static const ma_uint8 ma_dr_wavGUID_W64_FMT [16] = {0x66,0x6D,0x74,0x20, 0xF3,0xAC, 0xD3,0x11, 0x8C,0xD1, 0x00,0xC0,0x4F,0x8E,0xDB,0x8A}; -static const ma_uint8 ma_dr_wavGUID_W64_FACT[16] = {0x66,0x61,0x63,0x74, 0xF3,0xAC, 0xD3,0x11, 0x8C,0xD1, 0x00,0xC0,0x4F,0x8E,0xDB,0x8A}; -static const ma_uint8 ma_dr_wavGUID_W64_DATA[16] = {0x64,0x61,0x74,0x61, 0xF3,0xAC, 0xD3,0x11, 0x8C,0xD1, 0x00,0xC0,0x4F,0x8E,0xDB,0x8A}; -static MA_INLINE int ma_dr_wav__is_little_endian(void) -{ -#if defined(MA_X86) || defined(MA_X64) - return MA_TRUE; -#elif defined(__BYTE_ORDER) && defined(__LITTLE_ENDIAN) && __BYTE_ORDER == __LITTLE_ENDIAN - return MA_TRUE; -#else - int n = 1; - return (*(char*)&n) == 1; -#endif -} -static MA_INLINE void ma_dr_wav_bytes_to_guid(const ma_uint8* data, ma_uint8* guid) -{ - int i; - for (i = 0; i < 16; ++i) { - guid[i] = data[i]; - } -} -static MA_INLINE ma_uint16 ma_dr_wav__bswap16(ma_uint16 n) -{ -#ifdef MA_DR_WAV_HAS_BYTESWAP16_INTRINSIC - #if defined(_MSC_VER) - return _byteswap_ushort(n); - #elif defined(__GNUC__) || defined(__clang__) - return __builtin_bswap16(n); - #else - #error "This compiler does not support the byte swap intrinsic." - #endif -#else - return ((n & 0xFF00) >> 8) | - ((n & 0x00FF) << 8); -#endif -} -static MA_INLINE ma_uint32 ma_dr_wav__bswap32(ma_uint32 n) -{ -#ifdef MA_DR_WAV_HAS_BYTESWAP32_INTRINSIC - #if defined(_MSC_VER) - return _byteswap_ulong(n); - #elif defined(__GNUC__) || defined(__clang__) - #if defined(MA_ARM) && (defined(__ARM_ARCH) && __ARM_ARCH >= 6) && !defined(MA_64BIT) - ma_uint32 r; - __asm__ __volatile__ ( - #if defined(MA_64BIT) - "rev %w[out], %w[in]" : [out]"=r"(r) : [in]"r"(n) - #else - "rev %[out], %[in]" : [out]"=r"(r) : [in]"r"(n) - #endif - ); - return r; - #else - return __builtin_bswap32(n); - #endif - #else - #error "This compiler does not support the byte swap intrinsic." - #endif -#else - return ((n & 0xFF000000) >> 24) | - ((n & 0x00FF0000) >> 8) | - ((n & 0x0000FF00) << 8) | - ((n & 0x000000FF) << 24); -#endif -} -static MA_INLINE ma_uint64 ma_dr_wav__bswap64(ma_uint64 n) -{ -#ifdef MA_DR_WAV_HAS_BYTESWAP64_INTRINSIC - #if defined(_MSC_VER) - return _byteswap_uint64(n); - #elif defined(__GNUC__) || defined(__clang__) - return __builtin_bswap64(n); - #else - #error "This compiler does not support the byte swap intrinsic." - #endif -#else - return ((n & ((ma_uint64)0xFF000000 << 32)) >> 56) | - ((n & ((ma_uint64)0x00FF0000 << 32)) >> 40) | - ((n & ((ma_uint64)0x0000FF00 << 32)) >> 24) | - ((n & ((ma_uint64)0x000000FF << 32)) >> 8) | - ((n & ((ma_uint64)0xFF000000 )) << 8) | - ((n & ((ma_uint64)0x00FF0000 )) << 24) | - ((n & ((ma_uint64)0x0000FF00 )) << 40) | - ((n & ((ma_uint64)0x000000FF )) << 56); -#endif -} -static MA_INLINE ma_int16 ma_dr_wav__bswap_s16(ma_int16 n) -{ - return (ma_int16)ma_dr_wav__bswap16((ma_uint16)n); -} -static MA_INLINE void ma_dr_wav__bswap_samples_s16(ma_int16* pSamples, ma_uint64 sampleCount) -{ - ma_uint64 iSample; - for (iSample = 0; iSample < sampleCount; iSample += 1) { - pSamples[iSample] = ma_dr_wav__bswap_s16(pSamples[iSample]); - } -} -static MA_INLINE void ma_dr_wav__bswap_s24(ma_uint8* p) -{ - ma_uint8 t; - t = p[0]; - p[0] = p[2]; - p[2] = t; -} -static MA_INLINE void ma_dr_wav__bswap_samples_s24(ma_uint8* pSamples, ma_uint64 sampleCount) -{ - ma_uint64 iSample; - for (iSample = 0; iSample < sampleCount; iSample += 1) { - ma_uint8* pSample = pSamples + (iSample*3); - ma_dr_wav__bswap_s24(pSample); - } -} -static MA_INLINE ma_int32 ma_dr_wav__bswap_s32(ma_int32 n) -{ - return (ma_int32)ma_dr_wav__bswap32((ma_uint32)n); -} -static MA_INLINE void ma_dr_wav__bswap_samples_s32(ma_int32* pSamples, ma_uint64 sampleCount) -{ - ma_uint64 iSample; - for (iSample = 0; iSample < sampleCount; iSample += 1) { - pSamples[iSample] = ma_dr_wav__bswap_s32(pSamples[iSample]); - } -} -static MA_INLINE ma_int64 ma_dr_wav__bswap_s64(ma_int64 n) -{ - return (ma_int64)ma_dr_wav__bswap64((ma_uint64)n); -} -static MA_INLINE void ma_dr_wav__bswap_samples_s64(ma_int64* pSamples, ma_uint64 sampleCount) -{ - ma_uint64 iSample; - for (iSample = 0; iSample < sampleCount; iSample += 1) { - pSamples[iSample] = ma_dr_wav__bswap_s64(pSamples[iSample]); - } -} -static MA_INLINE float ma_dr_wav__bswap_f32(float n) -{ - union { - ma_uint32 i; - float f; - } x; - x.f = n; - x.i = ma_dr_wav__bswap32(x.i); - return x.f; -} -static MA_INLINE void ma_dr_wav__bswap_samples_f32(float* pSamples, ma_uint64 sampleCount) -{ - ma_uint64 iSample; - for (iSample = 0; iSample < sampleCount; iSample += 1) { - pSamples[iSample] = ma_dr_wav__bswap_f32(pSamples[iSample]); - } -} -static MA_INLINE void ma_dr_wav__bswap_samples(void* pSamples, ma_uint64 sampleCount, ma_uint32 bytesPerSample) -{ - switch (bytesPerSample) - { - case 1: - { - } break; - case 2: - { - ma_dr_wav__bswap_samples_s16((ma_int16*)pSamples, sampleCount); - } break; - case 3: - { - ma_dr_wav__bswap_samples_s24((ma_uint8*)pSamples, sampleCount); - } break; - case 4: - { - ma_dr_wav__bswap_samples_s32((ma_int32*)pSamples, sampleCount); - } break; - case 8: - { - ma_dr_wav__bswap_samples_s64((ma_int64*)pSamples, sampleCount); - } break; - default: - { - MA_DR_WAV_ASSERT(MA_FALSE); - } break; - } -} -MA_PRIVATE MA_INLINE ma_bool32 ma_dr_wav_is_container_be(ma_dr_wav_container container) -{ - if (container == ma_dr_wav_container_rifx || container == ma_dr_wav_container_aiff) { - return MA_TRUE; - } else { - return MA_FALSE; - } -} -MA_PRIVATE MA_INLINE ma_uint16 ma_dr_wav_bytes_to_u16_le(const ma_uint8* data) -{ - return ((ma_uint16)data[0] << 0) | ((ma_uint16)data[1] << 8); -} -MA_PRIVATE MA_INLINE ma_uint16 ma_dr_wav_bytes_to_u16_be(const ma_uint8* data) -{ - return ((ma_uint16)data[1] << 0) | ((ma_uint16)data[0] << 8); -} -MA_PRIVATE MA_INLINE ma_uint16 ma_dr_wav_bytes_to_u16_ex(const ma_uint8* data, ma_dr_wav_container container) -{ - if (ma_dr_wav_is_container_be(container)) { - return ma_dr_wav_bytes_to_u16_be(data); - } else { - return ma_dr_wav_bytes_to_u16_le(data); - } -} -MA_PRIVATE MA_INLINE ma_uint32 ma_dr_wav_bytes_to_u32_le(const ma_uint8* data) -{ - return ((ma_uint32)data[0] << 0) | ((ma_uint32)data[1] << 8) | ((ma_uint32)data[2] << 16) | ((ma_uint32)data[3] << 24); -} -MA_PRIVATE MA_INLINE ma_uint32 ma_dr_wav_bytes_to_u32_be(const ma_uint8* data) -{ - return ((ma_uint32)data[3] << 0) | ((ma_uint32)data[2] << 8) | ((ma_uint32)data[1] << 16) | ((ma_uint32)data[0] << 24); -} -MA_PRIVATE MA_INLINE ma_uint32 ma_dr_wav_bytes_to_u32_ex(const ma_uint8* data, ma_dr_wav_container container) -{ - if (ma_dr_wav_is_container_be(container)) { - return ma_dr_wav_bytes_to_u32_be(data); - } else { - return ma_dr_wav_bytes_to_u32_le(data); - } -} -MA_PRIVATE ma_int64 ma_dr_wav_aiff_extented_to_s64(const ma_uint8* data) -{ - ma_uint32 exponent = ((ma_uint32)data[0] << 8) | data[1]; - ma_uint64 hi = ((ma_uint64)data[2] << 24) | ((ma_uint64)data[3] << 16) | ((ma_uint64)data[4] << 8) | ((ma_uint64)data[5] << 0); - ma_uint64 lo = ((ma_uint64)data[6] << 24) | ((ma_uint64)data[7] << 16) | ((ma_uint64)data[8] << 8) | ((ma_uint64)data[9] << 0); - ma_uint64 significand = (hi << 32) | lo; - int sign = exponent >> 15; - exponent &= 0x7FFF; - if (exponent == 0 && significand == 0) { - return 0; - } else if (exponent == 0x7FFF) { - return sign ? MA_DR_WAV_INT64_MIN : MA_DR_WAV_INT64_MAX; - } - exponent -= 16383; - if (exponent > 63) { - return sign ? MA_DR_WAV_INT64_MIN : MA_DR_WAV_INT64_MAX; - } else if (exponent < 1) { - return 0; - } - significand >>= (63 - exponent); - if (sign) { - return -(ma_int64)significand; - } else { - return (ma_int64)significand; - } -} -MA_PRIVATE void* ma_dr_wav__malloc_default(size_t sz, void* pUserData) -{ - (void)pUserData; - return MA_DR_WAV_MALLOC(sz); -} -MA_PRIVATE void* ma_dr_wav__realloc_default(void* p, size_t sz, void* pUserData) -{ - (void)pUserData; - return MA_DR_WAV_REALLOC(p, sz); -} -MA_PRIVATE void ma_dr_wav__free_default(void* p, void* pUserData) -{ - (void)pUserData; - MA_DR_WAV_FREE(p); -} -MA_PRIVATE void* ma_dr_wav__malloc_from_callbacks(size_t sz, const ma_allocation_callbacks* pAllocationCallbacks) -{ - if (pAllocationCallbacks == NULL) { - return NULL; - } - if (pAllocationCallbacks->onMalloc != NULL) { - return pAllocationCallbacks->onMalloc(sz, pAllocationCallbacks->pUserData); - } - if (pAllocationCallbacks->onRealloc != NULL) { - return pAllocationCallbacks->onRealloc(NULL, sz, pAllocationCallbacks->pUserData); - } - return NULL; -} -MA_PRIVATE void* ma_dr_wav__realloc_from_callbacks(void* p, size_t szNew, size_t szOld, const ma_allocation_callbacks* pAllocationCallbacks) -{ - if (pAllocationCallbacks == NULL) { - return NULL; - } - if (pAllocationCallbacks->onRealloc != NULL) { - return pAllocationCallbacks->onRealloc(p, szNew, pAllocationCallbacks->pUserData); - } - if (pAllocationCallbacks->onMalloc != NULL && pAllocationCallbacks->onFree != NULL) { - void* p2; - p2 = pAllocationCallbacks->onMalloc(szNew, pAllocationCallbacks->pUserData); - if (p2 == NULL) { - return NULL; - } - if (p != NULL) { - MA_DR_WAV_COPY_MEMORY(p2, p, szOld); - pAllocationCallbacks->onFree(p, pAllocationCallbacks->pUserData); - } - return p2; - } - return NULL; -} -MA_PRIVATE void ma_dr_wav__free_from_callbacks(void* p, const ma_allocation_callbacks* pAllocationCallbacks) -{ - if (p == NULL || pAllocationCallbacks == NULL) { - return; - } - if (pAllocationCallbacks->onFree != NULL) { - pAllocationCallbacks->onFree(p, pAllocationCallbacks->pUserData); - } -} -MA_PRIVATE ma_allocation_callbacks ma_dr_wav_copy_allocation_callbacks_or_defaults(const ma_allocation_callbacks* pAllocationCallbacks) -{ - if (pAllocationCallbacks != NULL) { - return *pAllocationCallbacks; - } else { - ma_allocation_callbacks allocationCallbacks; - allocationCallbacks.pUserData = NULL; - allocationCallbacks.onMalloc = ma_dr_wav__malloc_default; - allocationCallbacks.onRealloc = ma_dr_wav__realloc_default; - allocationCallbacks.onFree = ma_dr_wav__free_default; - return allocationCallbacks; - } -} -static MA_INLINE ma_bool32 ma_dr_wav__is_compressed_format_tag(ma_uint16 formatTag) -{ - return - formatTag == MA_DR_WAVE_FORMAT_ADPCM || - formatTag == MA_DR_WAVE_FORMAT_DVI_ADPCM; -} -MA_PRIVATE unsigned int ma_dr_wav__chunk_padding_size_riff(ma_uint64 chunkSize) -{ - return (unsigned int)(chunkSize % 2); -} -MA_PRIVATE unsigned int ma_dr_wav__chunk_padding_size_w64(ma_uint64 chunkSize) -{ - return (unsigned int)(chunkSize % 8); -} -MA_PRIVATE ma_uint64 ma_dr_wav_read_pcm_frames_s16__msadpcm(ma_dr_wav* pWav, ma_uint64 samplesToRead, ma_int16* pBufferOut); -MA_PRIVATE ma_uint64 ma_dr_wav_read_pcm_frames_s16__ima(ma_dr_wav* pWav, ma_uint64 samplesToRead, ma_int16* pBufferOut); -MA_PRIVATE ma_bool32 ma_dr_wav_init_write__internal(ma_dr_wav* pWav, const ma_dr_wav_data_format* pFormat, ma_uint64 totalSampleCount); -MA_PRIVATE ma_result ma_dr_wav__read_chunk_header(ma_dr_wav_read_proc onRead, void* pUserData, ma_dr_wav_container container, ma_uint64* pRunningBytesReadOut, ma_dr_wav_chunk_header* pHeaderOut) -{ - if (container == ma_dr_wav_container_riff || container == ma_dr_wav_container_rifx || container == ma_dr_wav_container_rf64 || container == ma_dr_wav_container_aiff) { - ma_uint8 sizeInBytes[4]; - if (onRead(pUserData, pHeaderOut->id.fourcc, 4) != 4) { - return MA_AT_END; - } - if (onRead(pUserData, sizeInBytes, 4) != 4) { - return MA_INVALID_FILE; - } - pHeaderOut->sizeInBytes = ma_dr_wav_bytes_to_u32_ex(sizeInBytes, container); - pHeaderOut->paddingSize = ma_dr_wav__chunk_padding_size_riff(pHeaderOut->sizeInBytes); - *pRunningBytesReadOut += 8; - } else if (container == ma_dr_wav_container_w64) { - ma_uint8 sizeInBytes[8]; - if (onRead(pUserData, pHeaderOut->id.guid, 16) != 16) { - return MA_AT_END; - } - if (onRead(pUserData, sizeInBytes, 8) != 8) { - return MA_INVALID_FILE; - } - pHeaderOut->sizeInBytes = ma_dr_wav_bytes_to_u64(sizeInBytes) - 24; - pHeaderOut->paddingSize = ma_dr_wav__chunk_padding_size_w64(pHeaderOut->sizeInBytes); - *pRunningBytesReadOut += 24; - } else { - return MA_INVALID_FILE; - } - return MA_SUCCESS; -} -MA_PRIVATE ma_bool32 ma_dr_wav__seek_forward(ma_dr_wav_seek_proc onSeek, ma_uint64 offset, void* pUserData) -{ - ma_uint64 bytesRemainingToSeek = offset; - while (bytesRemainingToSeek > 0) { - if (bytesRemainingToSeek > 0x7FFFFFFF) { - if (!onSeek(pUserData, 0x7FFFFFFF, ma_dr_wav_seek_origin_current)) { - return MA_FALSE; - } - bytesRemainingToSeek -= 0x7FFFFFFF; - } else { - if (!onSeek(pUserData, (int)bytesRemainingToSeek, ma_dr_wav_seek_origin_current)) { - return MA_FALSE; - } - bytesRemainingToSeek = 0; - } - } - return MA_TRUE; -} -MA_PRIVATE ma_bool32 ma_dr_wav__seek_from_start(ma_dr_wav_seek_proc onSeek, ma_uint64 offset, void* pUserData) -{ - if (offset <= 0x7FFFFFFF) { - return onSeek(pUserData, (int)offset, ma_dr_wav_seek_origin_start); - } - if (!onSeek(pUserData, 0x7FFFFFFF, ma_dr_wav_seek_origin_start)) { - return MA_FALSE; - } - offset -= 0x7FFFFFFF; - for (;;) { - if (offset <= 0x7FFFFFFF) { - return onSeek(pUserData, (int)offset, ma_dr_wav_seek_origin_current); - } - if (!onSeek(pUserData, 0x7FFFFFFF, ma_dr_wav_seek_origin_current)) { - return MA_FALSE; - } - offset -= 0x7FFFFFFF; - } -} -MA_PRIVATE size_t ma_dr_wav__on_read(ma_dr_wav_read_proc onRead, void* pUserData, void* pBufferOut, size_t bytesToRead, ma_uint64* pCursor) -{ - size_t bytesRead; - MA_DR_WAV_ASSERT(onRead != NULL); - MA_DR_WAV_ASSERT(pCursor != NULL); - bytesRead = onRead(pUserData, pBufferOut, bytesToRead); - *pCursor += bytesRead; - return bytesRead; -} -#if 0 -MA_PRIVATE ma_bool32 ma_dr_wav__on_seek(ma_dr_wav_seek_proc onSeek, void* pUserData, int offset, ma_dr_wav_seek_origin origin, ma_uint64* pCursor) -{ - MA_DR_WAV_ASSERT(onSeek != NULL); - MA_DR_WAV_ASSERT(pCursor != NULL); - if (!onSeek(pUserData, offset, origin)) { - return MA_FALSE; - } - if (origin == ma_dr_wav_seek_origin_start) { - *pCursor = offset; - } else { - *pCursor += offset; - } - return MA_TRUE; -} -#endif -#define MA_DR_WAV_SMPL_BYTES 36 -#define MA_DR_WAV_SMPL_LOOP_BYTES 24 -#define MA_DR_WAV_INST_BYTES 7 -#define MA_DR_WAV_ACID_BYTES 24 -#define MA_DR_WAV_CUE_BYTES 4 -#define MA_DR_WAV_BEXT_BYTES 602 -#define MA_DR_WAV_BEXT_DESCRIPTION_BYTES 256 -#define MA_DR_WAV_BEXT_ORIGINATOR_NAME_BYTES 32 -#define MA_DR_WAV_BEXT_ORIGINATOR_REF_BYTES 32 -#define MA_DR_WAV_BEXT_RESERVED_BYTES 180 -#define MA_DR_WAV_BEXT_UMID_BYTES 64 -#define MA_DR_WAV_CUE_POINT_BYTES 24 -#define MA_DR_WAV_LIST_LABEL_OR_NOTE_BYTES 4 -#define MA_DR_WAV_LIST_LABELLED_TEXT_BYTES 20 -#define MA_DR_WAV_METADATA_ALIGNMENT 8 -typedef enum -{ - ma_dr_wav__metadata_parser_stage_count, - ma_dr_wav__metadata_parser_stage_read -} ma_dr_wav__metadata_parser_stage; -typedef struct -{ - ma_dr_wav_read_proc onRead; - ma_dr_wav_seek_proc onSeek; - void *pReadSeekUserData; - ma_dr_wav__metadata_parser_stage stage; - ma_dr_wav_metadata *pMetadata; - ma_uint32 metadataCount; - ma_uint8 *pData; - ma_uint8 *pDataCursor; - ma_uint64 metadataCursor; - ma_uint64 extraCapacity; -} ma_dr_wav__metadata_parser; -MA_PRIVATE size_t ma_dr_wav__metadata_memory_capacity(ma_dr_wav__metadata_parser* pParser) -{ - ma_uint64 cap = sizeof(ma_dr_wav_metadata) * (ma_uint64)pParser->metadataCount + pParser->extraCapacity; - if (cap > MA_SIZE_MAX) { - return 0; - } - return (size_t)cap; -} -MA_PRIVATE ma_uint8* ma_dr_wav__metadata_get_memory(ma_dr_wav__metadata_parser* pParser, size_t size, size_t align) -{ - ma_uint8* pResult; - if (align) { - ma_uintptr modulo = (ma_uintptr)pParser->pDataCursor % align; - if (modulo != 0) { - pParser->pDataCursor += align - modulo; - } - } - pResult = pParser->pDataCursor; - MA_DR_WAV_ASSERT((pResult + size) <= (pParser->pData + ma_dr_wav__metadata_memory_capacity(pParser))); - pParser->pDataCursor += size; - return pResult; -} -MA_PRIVATE void ma_dr_wav__metadata_request_extra_memory_for_stage_2(ma_dr_wav__metadata_parser* pParser, size_t bytes, size_t align) -{ - size_t extra = bytes + (align ? (align - 1) : 0); - pParser->extraCapacity += extra; -} -MA_PRIVATE ma_result ma_dr_wav__metadata_alloc(ma_dr_wav__metadata_parser* pParser, ma_allocation_callbacks* pAllocationCallbacks) -{ - if (pParser->extraCapacity != 0 || pParser->metadataCount != 0) { - pAllocationCallbacks->onFree(pParser->pData, pAllocationCallbacks->pUserData); - pParser->pData = (ma_uint8*)pAllocationCallbacks->onMalloc(ma_dr_wav__metadata_memory_capacity(pParser), pAllocationCallbacks->pUserData); - pParser->pDataCursor = pParser->pData; - if (pParser->pData == NULL) { - return MA_OUT_OF_MEMORY; - } - pParser->pMetadata = (ma_dr_wav_metadata*)ma_dr_wav__metadata_get_memory(pParser, sizeof(ma_dr_wav_metadata) * pParser->metadataCount, 1); - pParser->metadataCursor = 0; - } - return MA_SUCCESS; -} -MA_PRIVATE size_t ma_dr_wav__metadata_parser_read(ma_dr_wav__metadata_parser* pParser, void* pBufferOut, size_t bytesToRead, ma_uint64* pCursor) -{ - if (pCursor != NULL) { - return ma_dr_wav__on_read(pParser->onRead, pParser->pReadSeekUserData, pBufferOut, bytesToRead, pCursor); - } else { - return pParser->onRead(pParser->pReadSeekUserData, pBufferOut, bytesToRead); - } -} -MA_PRIVATE ma_uint64 ma_dr_wav__read_smpl_to_metadata_obj(ma_dr_wav__metadata_parser* pParser, const ma_dr_wav_chunk_header* pChunkHeader, ma_dr_wav_metadata* pMetadata) -{ - ma_uint8 smplHeaderData[MA_DR_WAV_SMPL_BYTES]; - ma_uint64 totalBytesRead = 0; - size_t bytesJustRead; - if (pMetadata == NULL) { - return 0; - } - bytesJustRead = ma_dr_wav__metadata_parser_read(pParser, smplHeaderData, sizeof(smplHeaderData), &totalBytesRead); - MA_DR_WAV_ASSERT(pParser->stage == ma_dr_wav__metadata_parser_stage_read); - MA_DR_WAV_ASSERT(pChunkHeader != NULL); - if (pMetadata != NULL && bytesJustRead == sizeof(smplHeaderData)) { - ma_uint32 iSampleLoop; - pMetadata->type = ma_dr_wav_metadata_type_smpl; - pMetadata->data.smpl.manufacturerId = ma_dr_wav_bytes_to_u32(smplHeaderData + 0); - pMetadata->data.smpl.productId = ma_dr_wav_bytes_to_u32(smplHeaderData + 4); - pMetadata->data.smpl.samplePeriodNanoseconds = ma_dr_wav_bytes_to_u32(smplHeaderData + 8); - pMetadata->data.smpl.midiUnityNote = ma_dr_wav_bytes_to_u32(smplHeaderData + 12); - pMetadata->data.smpl.midiPitchFraction = ma_dr_wav_bytes_to_u32(smplHeaderData + 16); - pMetadata->data.smpl.smpteFormat = ma_dr_wav_bytes_to_u32(smplHeaderData + 20); - pMetadata->data.smpl.smpteOffset = ma_dr_wav_bytes_to_u32(smplHeaderData + 24); - pMetadata->data.smpl.sampleLoopCount = ma_dr_wav_bytes_to_u32(smplHeaderData + 28); - pMetadata->data.smpl.samplerSpecificDataSizeInBytes = ma_dr_wav_bytes_to_u32(smplHeaderData + 32); - if (pMetadata->data.smpl.sampleLoopCount == (pChunkHeader->sizeInBytes - MA_DR_WAV_SMPL_BYTES) / MA_DR_WAV_SMPL_LOOP_BYTES) { - pMetadata->data.smpl.pLoops = (ma_dr_wav_smpl_loop*)ma_dr_wav__metadata_get_memory(pParser, sizeof(ma_dr_wav_smpl_loop) * pMetadata->data.smpl.sampleLoopCount, MA_DR_WAV_METADATA_ALIGNMENT); - for (iSampleLoop = 0; iSampleLoop < pMetadata->data.smpl.sampleLoopCount; ++iSampleLoop) { - ma_uint8 smplLoopData[MA_DR_WAV_SMPL_LOOP_BYTES]; - bytesJustRead = ma_dr_wav__metadata_parser_read(pParser, smplLoopData, sizeof(smplLoopData), &totalBytesRead); - if (bytesJustRead == sizeof(smplLoopData)) { - pMetadata->data.smpl.pLoops[iSampleLoop].cuePointId = ma_dr_wav_bytes_to_u32(smplLoopData + 0); - pMetadata->data.smpl.pLoops[iSampleLoop].type = ma_dr_wav_bytes_to_u32(smplLoopData + 4); - pMetadata->data.smpl.pLoops[iSampleLoop].firstSampleByteOffset = ma_dr_wav_bytes_to_u32(smplLoopData + 8); - pMetadata->data.smpl.pLoops[iSampleLoop].lastSampleByteOffset = ma_dr_wav_bytes_to_u32(smplLoopData + 12); - pMetadata->data.smpl.pLoops[iSampleLoop].sampleFraction = ma_dr_wav_bytes_to_u32(smplLoopData + 16); - pMetadata->data.smpl.pLoops[iSampleLoop].playCount = ma_dr_wav_bytes_to_u32(smplLoopData + 20); - } else { - break; - } - } - if (pMetadata->data.smpl.samplerSpecificDataSizeInBytes > 0) { - pMetadata->data.smpl.pSamplerSpecificData = ma_dr_wav__metadata_get_memory(pParser, pMetadata->data.smpl.samplerSpecificDataSizeInBytes, 1); - MA_DR_WAV_ASSERT(pMetadata->data.smpl.pSamplerSpecificData != NULL); - ma_dr_wav__metadata_parser_read(pParser, pMetadata->data.smpl.pSamplerSpecificData, pMetadata->data.smpl.samplerSpecificDataSizeInBytes, &totalBytesRead); - } - } - } - return totalBytesRead; -} -MA_PRIVATE ma_uint64 ma_dr_wav__read_cue_to_metadata_obj(ma_dr_wav__metadata_parser* pParser, const ma_dr_wav_chunk_header* pChunkHeader, ma_dr_wav_metadata* pMetadata) -{ - ma_uint8 cueHeaderSectionData[MA_DR_WAV_CUE_BYTES]; - ma_uint64 totalBytesRead = 0; - size_t bytesJustRead; - if (pMetadata == NULL) { - return 0; - } - bytesJustRead = ma_dr_wav__metadata_parser_read(pParser, cueHeaderSectionData, sizeof(cueHeaderSectionData), &totalBytesRead); - MA_DR_WAV_ASSERT(pParser->stage == ma_dr_wav__metadata_parser_stage_read); - if (bytesJustRead == sizeof(cueHeaderSectionData)) { - pMetadata->type = ma_dr_wav_metadata_type_cue; - pMetadata->data.cue.cuePointCount = ma_dr_wav_bytes_to_u32(cueHeaderSectionData); - if (pMetadata->data.cue.cuePointCount == (pChunkHeader->sizeInBytes - MA_DR_WAV_CUE_BYTES) / MA_DR_WAV_CUE_POINT_BYTES) { - pMetadata->data.cue.pCuePoints = (ma_dr_wav_cue_point*)ma_dr_wav__metadata_get_memory(pParser, sizeof(ma_dr_wav_cue_point) * pMetadata->data.cue.cuePointCount, MA_DR_WAV_METADATA_ALIGNMENT); - MA_DR_WAV_ASSERT(pMetadata->data.cue.pCuePoints != NULL); - if (pMetadata->data.cue.cuePointCount > 0) { - ma_uint32 iCuePoint; - for (iCuePoint = 0; iCuePoint < pMetadata->data.cue.cuePointCount; ++iCuePoint) { - ma_uint8 cuePointData[MA_DR_WAV_CUE_POINT_BYTES]; - bytesJustRead = ma_dr_wav__metadata_parser_read(pParser, cuePointData, sizeof(cuePointData), &totalBytesRead); - if (bytesJustRead == sizeof(cuePointData)) { - pMetadata->data.cue.pCuePoints[iCuePoint].id = ma_dr_wav_bytes_to_u32(cuePointData + 0); - pMetadata->data.cue.pCuePoints[iCuePoint].playOrderPosition = ma_dr_wav_bytes_to_u32(cuePointData + 4); - pMetadata->data.cue.pCuePoints[iCuePoint].dataChunkId[0] = cuePointData[8]; - pMetadata->data.cue.pCuePoints[iCuePoint].dataChunkId[1] = cuePointData[9]; - pMetadata->data.cue.pCuePoints[iCuePoint].dataChunkId[2] = cuePointData[10]; - pMetadata->data.cue.pCuePoints[iCuePoint].dataChunkId[3] = cuePointData[11]; - pMetadata->data.cue.pCuePoints[iCuePoint].chunkStart = ma_dr_wav_bytes_to_u32(cuePointData + 12); - pMetadata->data.cue.pCuePoints[iCuePoint].blockStart = ma_dr_wav_bytes_to_u32(cuePointData + 16); - pMetadata->data.cue.pCuePoints[iCuePoint].sampleByteOffset = ma_dr_wav_bytes_to_u32(cuePointData + 20); - } else { - break; - } - } - } - } - } - return totalBytesRead; -} -MA_PRIVATE ma_uint64 ma_dr_wav__read_inst_to_metadata_obj(ma_dr_wav__metadata_parser* pParser, ma_dr_wav_metadata* pMetadata) -{ - ma_uint8 instData[MA_DR_WAV_INST_BYTES]; - ma_uint64 bytesRead; - if (pMetadata == NULL) { - return 0; - } - bytesRead = ma_dr_wav__metadata_parser_read(pParser, instData, sizeof(instData), NULL); - MA_DR_WAV_ASSERT(pParser->stage == ma_dr_wav__metadata_parser_stage_read); - if (bytesRead == sizeof(instData)) { - pMetadata->type = ma_dr_wav_metadata_type_inst; - pMetadata->data.inst.midiUnityNote = (ma_int8)instData[0]; - pMetadata->data.inst.fineTuneCents = (ma_int8)instData[1]; - pMetadata->data.inst.gainDecibels = (ma_int8)instData[2]; - pMetadata->data.inst.lowNote = (ma_int8)instData[3]; - pMetadata->data.inst.highNote = (ma_int8)instData[4]; - pMetadata->data.inst.lowVelocity = (ma_int8)instData[5]; - pMetadata->data.inst.highVelocity = (ma_int8)instData[6]; - } - return bytesRead; -} -MA_PRIVATE ma_uint64 ma_dr_wav__read_acid_to_metadata_obj(ma_dr_wav__metadata_parser* pParser, ma_dr_wav_metadata* pMetadata) -{ - ma_uint8 acidData[MA_DR_WAV_ACID_BYTES]; - ma_uint64 bytesRead; - if (pMetadata == NULL) { - return 0; - } - bytesRead = ma_dr_wav__metadata_parser_read(pParser, acidData, sizeof(acidData), NULL); - MA_DR_WAV_ASSERT(pParser->stage == ma_dr_wav__metadata_parser_stage_read); - if (bytesRead == sizeof(acidData)) { - pMetadata->type = ma_dr_wav_metadata_type_acid; - pMetadata->data.acid.flags = ma_dr_wav_bytes_to_u32(acidData + 0); - pMetadata->data.acid.midiUnityNote = ma_dr_wav_bytes_to_u16(acidData + 4); - pMetadata->data.acid.reserved1 = ma_dr_wav_bytes_to_u16(acidData + 6); - pMetadata->data.acid.reserved2 = ma_dr_wav_bytes_to_f32(acidData + 8); - pMetadata->data.acid.numBeats = ma_dr_wav_bytes_to_u32(acidData + 12); - pMetadata->data.acid.meterDenominator = ma_dr_wav_bytes_to_u16(acidData + 16); - pMetadata->data.acid.meterNumerator = ma_dr_wav_bytes_to_u16(acidData + 18); - pMetadata->data.acid.tempo = ma_dr_wav_bytes_to_f32(acidData + 20); - } - return bytesRead; -} -MA_PRIVATE size_t ma_dr_wav__strlen(const char* str) -{ - size_t result = 0; - while (*str++) { - result += 1; - } - return result; -} -MA_PRIVATE size_t ma_dr_wav__strlen_clamped(const char* str, size_t maxToRead) -{ - size_t result = 0; - while (*str++ && result < maxToRead) { - result += 1; - } - return result; -} -MA_PRIVATE char* ma_dr_wav__metadata_copy_string(ma_dr_wav__metadata_parser* pParser, const char* str, size_t maxToRead) -{ - size_t len = ma_dr_wav__strlen_clamped(str, maxToRead); - if (len) { - char* result = (char*)ma_dr_wav__metadata_get_memory(pParser, len + 1, 1); - MA_DR_WAV_ASSERT(result != NULL); - MA_DR_WAV_COPY_MEMORY(result, str, len); - result[len] = '\0'; - return result; - } else { - return NULL; - } -} -typedef struct -{ - const void* pBuffer; - size_t sizeInBytes; - size_t cursor; -} ma_dr_wav_buffer_reader; -MA_PRIVATE ma_result ma_dr_wav_buffer_reader_init(const void* pBuffer, size_t sizeInBytes, ma_dr_wav_buffer_reader* pReader) -{ - MA_DR_WAV_ASSERT(pBuffer != NULL); - MA_DR_WAV_ASSERT(pReader != NULL); - MA_DR_WAV_ZERO_OBJECT(pReader); - pReader->pBuffer = pBuffer; - pReader->sizeInBytes = sizeInBytes; - pReader->cursor = 0; - return MA_SUCCESS; -} -MA_PRIVATE const void* ma_dr_wav_buffer_reader_ptr(const ma_dr_wav_buffer_reader* pReader) -{ - MA_DR_WAV_ASSERT(pReader != NULL); - return ma_dr_wav_offset_ptr(pReader->pBuffer, pReader->cursor); -} -MA_PRIVATE ma_result ma_dr_wav_buffer_reader_seek(ma_dr_wav_buffer_reader* pReader, size_t bytesToSeek) -{ - MA_DR_WAV_ASSERT(pReader != NULL); - if (pReader->cursor + bytesToSeek > pReader->sizeInBytes) { - return MA_BAD_SEEK; - } - pReader->cursor += bytesToSeek; - return MA_SUCCESS; -} -MA_PRIVATE ma_result ma_dr_wav_buffer_reader_read(ma_dr_wav_buffer_reader* pReader, void* pDst, size_t bytesToRead, size_t* pBytesRead) -{ - ma_result result = MA_SUCCESS; - size_t bytesRemaining; - MA_DR_WAV_ASSERT(pReader != NULL); - if (pBytesRead != NULL) { - *pBytesRead = 0; - } - bytesRemaining = (pReader->sizeInBytes - pReader->cursor); - if (bytesToRead > bytesRemaining) { - bytesToRead = bytesRemaining; - } - if (pDst == NULL) { - result = ma_dr_wav_buffer_reader_seek(pReader, bytesToRead); - } else { - MA_DR_WAV_COPY_MEMORY(pDst, ma_dr_wav_buffer_reader_ptr(pReader), bytesToRead); - pReader->cursor += bytesToRead; - } - MA_DR_WAV_ASSERT(pReader->cursor <= pReader->sizeInBytes); - if (result == MA_SUCCESS) { - if (pBytesRead != NULL) { - *pBytesRead = bytesToRead; - } - } - return MA_SUCCESS; -} -MA_PRIVATE ma_result ma_dr_wav_buffer_reader_read_u16(ma_dr_wav_buffer_reader* pReader, ma_uint16* pDst) -{ - ma_result result; - size_t bytesRead; - ma_uint8 data[2]; - MA_DR_WAV_ASSERT(pReader != NULL); - MA_DR_WAV_ASSERT(pDst != NULL); - *pDst = 0; - result = ma_dr_wav_buffer_reader_read(pReader, data, sizeof(*pDst), &bytesRead); - if (result != MA_SUCCESS || bytesRead != sizeof(*pDst)) { - return result; - } - *pDst = ma_dr_wav_bytes_to_u16(data); - return MA_SUCCESS; -} -MA_PRIVATE ma_result ma_dr_wav_buffer_reader_read_u32(ma_dr_wav_buffer_reader* pReader, ma_uint32* pDst) -{ - ma_result result; - size_t bytesRead; - ma_uint8 data[4]; - MA_DR_WAV_ASSERT(pReader != NULL); - MA_DR_WAV_ASSERT(pDst != NULL); - *pDst = 0; - result = ma_dr_wav_buffer_reader_read(pReader, data, sizeof(*pDst), &bytesRead); - if (result != MA_SUCCESS || bytesRead != sizeof(*pDst)) { - return result; - } - *pDst = ma_dr_wav_bytes_to_u32(data); - return MA_SUCCESS; -} -MA_PRIVATE ma_uint64 ma_dr_wav__read_bext_to_metadata_obj(ma_dr_wav__metadata_parser* pParser, ma_dr_wav_metadata* pMetadata, ma_uint64 chunkSize) -{ - ma_uint8 bextData[MA_DR_WAV_BEXT_BYTES]; - size_t bytesRead = ma_dr_wav__metadata_parser_read(pParser, bextData, sizeof(bextData), NULL); - MA_DR_WAV_ASSERT(pParser->stage == ma_dr_wav__metadata_parser_stage_read); - if (bytesRead == sizeof(bextData)) { - ma_dr_wav_buffer_reader reader; - ma_uint32 timeReferenceLow; - ma_uint32 timeReferenceHigh; - size_t extraBytes; - pMetadata->type = ma_dr_wav_metadata_type_bext; - if (ma_dr_wav_buffer_reader_init(bextData, bytesRead, &reader) == MA_SUCCESS) { - pMetadata->data.bext.pDescription = ma_dr_wav__metadata_copy_string(pParser, (const char*)ma_dr_wav_buffer_reader_ptr(&reader), MA_DR_WAV_BEXT_DESCRIPTION_BYTES); - ma_dr_wav_buffer_reader_seek(&reader, MA_DR_WAV_BEXT_DESCRIPTION_BYTES); - pMetadata->data.bext.pOriginatorName = ma_dr_wav__metadata_copy_string(pParser, (const char*)ma_dr_wav_buffer_reader_ptr(&reader), MA_DR_WAV_BEXT_ORIGINATOR_NAME_BYTES); - ma_dr_wav_buffer_reader_seek(&reader, MA_DR_WAV_BEXT_ORIGINATOR_NAME_BYTES); - pMetadata->data.bext.pOriginatorReference = ma_dr_wav__metadata_copy_string(pParser, (const char*)ma_dr_wav_buffer_reader_ptr(&reader), MA_DR_WAV_BEXT_ORIGINATOR_REF_BYTES); - ma_dr_wav_buffer_reader_seek(&reader, MA_DR_WAV_BEXT_ORIGINATOR_REF_BYTES); - ma_dr_wav_buffer_reader_read(&reader, pMetadata->data.bext.pOriginationDate, sizeof(pMetadata->data.bext.pOriginationDate), NULL); - ma_dr_wav_buffer_reader_read(&reader, pMetadata->data.bext.pOriginationTime, sizeof(pMetadata->data.bext.pOriginationTime), NULL); - ma_dr_wav_buffer_reader_read_u32(&reader, &timeReferenceLow); - ma_dr_wav_buffer_reader_read_u32(&reader, &timeReferenceHigh); - pMetadata->data.bext.timeReference = ((ma_uint64)timeReferenceHigh << 32) + timeReferenceLow; - ma_dr_wav_buffer_reader_read_u16(&reader, &pMetadata->data.bext.version); - pMetadata->data.bext.pUMID = ma_dr_wav__metadata_get_memory(pParser, MA_DR_WAV_BEXT_UMID_BYTES, 1); - ma_dr_wav_buffer_reader_read(&reader, pMetadata->data.bext.pUMID, MA_DR_WAV_BEXT_UMID_BYTES, NULL); - ma_dr_wav_buffer_reader_read_u16(&reader, &pMetadata->data.bext.loudnessValue); - ma_dr_wav_buffer_reader_read_u16(&reader, &pMetadata->data.bext.loudnessRange); - ma_dr_wav_buffer_reader_read_u16(&reader, &pMetadata->data.bext.maxTruePeakLevel); - ma_dr_wav_buffer_reader_read_u16(&reader, &pMetadata->data.bext.maxMomentaryLoudness); - ma_dr_wav_buffer_reader_read_u16(&reader, &pMetadata->data.bext.maxShortTermLoudness); - MA_DR_WAV_ASSERT((ma_dr_wav_offset_ptr(ma_dr_wav_buffer_reader_ptr(&reader), MA_DR_WAV_BEXT_RESERVED_BYTES)) == (bextData + MA_DR_WAV_BEXT_BYTES)); - extraBytes = (size_t)(chunkSize - MA_DR_WAV_BEXT_BYTES); - if (extraBytes > 0) { - pMetadata->data.bext.pCodingHistory = (char*)ma_dr_wav__metadata_get_memory(pParser, extraBytes + 1, 1); - MA_DR_WAV_ASSERT(pMetadata->data.bext.pCodingHistory != NULL); - bytesRead += ma_dr_wav__metadata_parser_read(pParser, pMetadata->data.bext.pCodingHistory, extraBytes, NULL); - pMetadata->data.bext.codingHistorySize = (ma_uint32)ma_dr_wav__strlen(pMetadata->data.bext.pCodingHistory); - } else { - pMetadata->data.bext.pCodingHistory = NULL; - pMetadata->data.bext.codingHistorySize = 0; - } - } - } - return bytesRead; -} -MA_PRIVATE ma_uint64 ma_dr_wav__read_list_label_or_note_to_metadata_obj(ma_dr_wav__metadata_parser* pParser, ma_dr_wav_metadata* pMetadata, ma_uint64 chunkSize, ma_dr_wav_metadata_type type) -{ - ma_uint8 cueIDBuffer[MA_DR_WAV_LIST_LABEL_OR_NOTE_BYTES]; - ma_uint64 totalBytesRead = 0; - size_t bytesJustRead = ma_dr_wav__metadata_parser_read(pParser, cueIDBuffer, sizeof(cueIDBuffer), &totalBytesRead); - MA_DR_WAV_ASSERT(pParser->stage == ma_dr_wav__metadata_parser_stage_read); - if (bytesJustRead == sizeof(cueIDBuffer)) { - ma_uint32 sizeIncludingNullTerminator; - pMetadata->type = type; - pMetadata->data.labelOrNote.cuePointId = ma_dr_wav_bytes_to_u32(cueIDBuffer); - sizeIncludingNullTerminator = (ma_uint32)chunkSize - MA_DR_WAV_LIST_LABEL_OR_NOTE_BYTES; - if (sizeIncludingNullTerminator > 0) { - pMetadata->data.labelOrNote.stringLength = sizeIncludingNullTerminator - 1; - pMetadata->data.labelOrNote.pString = (char*)ma_dr_wav__metadata_get_memory(pParser, sizeIncludingNullTerminator, 1); - MA_DR_WAV_ASSERT(pMetadata->data.labelOrNote.pString != NULL); - ma_dr_wav__metadata_parser_read(pParser, pMetadata->data.labelOrNote.pString, sizeIncludingNullTerminator, &totalBytesRead); - } else { - pMetadata->data.labelOrNote.stringLength = 0; - pMetadata->data.labelOrNote.pString = NULL; - } - } - return totalBytesRead; -} -MA_PRIVATE ma_uint64 ma_dr_wav__read_list_labelled_cue_region_to_metadata_obj(ma_dr_wav__metadata_parser* pParser, ma_dr_wav_metadata* pMetadata, ma_uint64 chunkSize) -{ - ma_uint8 buffer[MA_DR_WAV_LIST_LABELLED_TEXT_BYTES]; - ma_uint64 totalBytesRead = 0; - size_t bytesJustRead = ma_dr_wav__metadata_parser_read(pParser, buffer, sizeof(buffer), &totalBytesRead); - MA_DR_WAV_ASSERT(pParser->stage == ma_dr_wav__metadata_parser_stage_read); - if (bytesJustRead == sizeof(buffer)) { - ma_uint32 sizeIncludingNullTerminator; - pMetadata->type = ma_dr_wav_metadata_type_list_labelled_cue_region; - pMetadata->data.labelledCueRegion.cuePointId = ma_dr_wav_bytes_to_u32(buffer + 0); - pMetadata->data.labelledCueRegion.sampleLength = ma_dr_wav_bytes_to_u32(buffer + 4); - pMetadata->data.labelledCueRegion.purposeId[0] = buffer[8]; - pMetadata->data.labelledCueRegion.purposeId[1] = buffer[9]; - pMetadata->data.labelledCueRegion.purposeId[2] = buffer[10]; - pMetadata->data.labelledCueRegion.purposeId[3] = buffer[11]; - pMetadata->data.labelledCueRegion.country = ma_dr_wav_bytes_to_u16(buffer + 12); - pMetadata->data.labelledCueRegion.language = ma_dr_wav_bytes_to_u16(buffer + 14); - pMetadata->data.labelledCueRegion.dialect = ma_dr_wav_bytes_to_u16(buffer + 16); - pMetadata->data.labelledCueRegion.codePage = ma_dr_wav_bytes_to_u16(buffer + 18); - sizeIncludingNullTerminator = (ma_uint32)chunkSize - MA_DR_WAV_LIST_LABELLED_TEXT_BYTES; - if (sizeIncludingNullTerminator > 0) { - pMetadata->data.labelledCueRegion.stringLength = sizeIncludingNullTerminator - 1; - pMetadata->data.labelledCueRegion.pString = (char*)ma_dr_wav__metadata_get_memory(pParser, sizeIncludingNullTerminator, 1); - MA_DR_WAV_ASSERT(pMetadata->data.labelledCueRegion.pString != NULL); - ma_dr_wav__metadata_parser_read(pParser, pMetadata->data.labelledCueRegion.pString, sizeIncludingNullTerminator, &totalBytesRead); - } else { - pMetadata->data.labelledCueRegion.stringLength = 0; - pMetadata->data.labelledCueRegion.pString = NULL; - } - } - return totalBytesRead; -} -MA_PRIVATE ma_uint64 ma_dr_wav__metadata_process_info_text_chunk(ma_dr_wav__metadata_parser* pParser, ma_uint64 chunkSize, ma_dr_wav_metadata_type type) -{ - ma_uint64 bytesRead = 0; - ma_uint32 stringSizeWithNullTerminator = (ma_uint32)chunkSize; - if (pParser->stage == ma_dr_wav__metadata_parser_stage_count) { - pParser->metadataCount += 1; - ma_dr_wav__metadata_request_extra_memory_for_stage_2(pParser, stringSizeWithNullTerminator, 1); - } else { - ma_dr_wav_metadata* pMetadata = &pParser->pMetadata[pParser->metadataCursor]; - pMetadata->type = type; - if (stringSizeWithNullTerminator > 0) { - pMetadata->data.infoText.stringLength = stringSizeWithNullTerminator - 1; - pMetadata->data.infoText.pString = (char*)ma_dr_wav__metadata_get_memory(pParser, stringSizeWithNullTerminator, 1); - MA_DR_WAV_ASSERT(pMetadata->data.infoText.pString != NULL); - bytesRead = ma_dr_wav__metadata_parser_read(pParser, pMetadata->data.infoText.pString, (size_t)stringSizeWithNullTerminator, NULL); - if (bytesRead == chunkSize) { - pParser->metadataCursor += 1; - } else { - } - } else { - pMetadata->data.infoText.stringLength = 0; - pMetadata->data.infoText.pString = NULL; - pParser->metadataCursor += 1; - } - } - return bytesRead; -} -MA_PRIVATE ma_uint64 ma_dr_wav__metadata_process_unknown_chunk(ma_dr_wav__metadata_parser* pParser, const ma_uint8* pChunkId, ma_uint64 chunkSize, ma_dr_wav_metadata_location location) -{ - ma_uint64 bytesRead = 0; - if (location == ma_dr_wav_metadata_location_invalid) { - return 0; - } - if (ma_dr_wav_fourcc_equal(pChunkId, "data") || ma_dr_wav_fourcc_equal(pChunkId, "fmt ") || ma_dr_wav_fourcc_equal(pChunkId, "fact")) { - return 0; - } - if (pParser->stage == ma_dr_wav__metadata_parser_stage_count) { - pParser->metadataCount += 1; - ma_dr_wav__metadata_request_extra_memory_for_stage_2(pParser, (size_t)chunkSize, 1); - } else { - ma_dr_wav_metadata* pMetadata = &pParser->pMetadata[pParser->metadataCursor]; - pMetadata->type = ma_dr_wav_metadata_type_unknown; - pMetadata->data.unknown.chunkLocation = location; - pMetadata->data.unknown.id[0] = pChunkId[0]; - pMetadata->data.unknown.id[1] = pChunkId[1]; - pMetadata->data.unknown.id[2] = pChunkId[2]; - pMetadata->data.unknown.id[3] = pChunkId[3]; - pMetadata->data.unknown.dataSizeInBytes = (ma_uint32)chunkSize; - pMetadata->data.unknown.pData = (ma_uint8 *)ma_dr_wav__metadata_get_memory(pParser, (size_t)chunkSize, 1); - MA_DR_WAV_ASSERT(pMetadata->data.unknown.pData != NULL); - bytesRead = ma_dr_wav__metadata_parser_read(pParser, pMetadata->data.unknown.pData, pMetadata->data.unknown.dataSizeInBytes, NULL); - if (bytesRead == pMetadata->data.unknown.dataSizeInBytes) { - pParser->metadataCursor += 1; - } else { - } - } - return bytesRead; -} -MA_PRIVATE ma_bool32 ma_dr_wav__chunk_matches(ma_dr_wav_metadata_type allowedMetadataTypes, const ma_uint8* pChunkID, ma_dr_wav_metadata_type type, const char* pID) -{ - return (allowedMetadataTypes & type) && ma_dr_wav_fourcc_equal(pChunkID, pID); -} -MA_PRIVATE ma_uint64 ma_dr_wav__metadata_process_chunk(ma_dr_wav__metadata_parser* pParser, const ma_dr_wav_chunk_header* pChunkHeader, ma_dr_wav_metadata_type allowedMetadataTypes) -{ - const ma_uint8 *pChunkID = pChunkHeader->id.fourcc; - ma_uint64 bytesRead = 0; - if (ma_dr_wav__chunk_matches(allowedMetadataTypes, pChunkID, ma_dr_wav_metadata_type_smpl, "smpl")) { - if (pChunkHeader->sizeInBytes >= MA_DR_WAV_SMPL_BYTES) { - if (pParser->stage == ma_dr_wav__metadata_parser_stage_count) { - ma_uint8 buffer[4]; - size_t bytesJustRead; - if (!pParser->onSeek(pParser->pReadSeekUserData, 28, ma_dr_wav_seek_origin_current)) { - return bytesRead; - } - bytesRead += 28; - bytesJustRead = ma_dr_wav__metadata_parser_read(pParser, buffer, sizeof(buffer), &bytesRead); - if (bytesJustRead == sizeof(buffer)) { - ma_uint32 loopCount = ma_dr_wav_bytes_to_u32(buffer); - ma_uint64 calculatedLoopCount; - calculatedLoopCount = (pChunkHeader->sizeInBytes - MA_DR_WAV_SMPL_BYTES) / MA_DR_WAV_SMPL_LOOP_BYTES; - if (calculatedLoopCount == loopCount) { - bytesJustRead = ma_dr_wav__metadata_parser_read(pParser, buffer, sizeof(buffer), &bytesRead); - if (bytesJustRead == sizeof(buffer)) { - ma_uint32 samplerSpecificDataSizeInBytes = ma_dr_wav_bytes_to_u32(buffer); - pParser->metadataCount += 1; - ma_dr_wav__metadata_request_extra_memory_for_stage_2(pParser, sizeof(ma_dr_wav_smpl_loop) * loopCount, MA_DR_WAV_METADATA_ALIGNMENT); - ma_dr_wav__metadata_request_extra_memory_for_stage_2(pParser, samplerSpecificDataSizeInBytes, 1); - } - } else { - } - } - } else { - bytesRead = ma_dr_wav__read_smpl_to_metadata_obj(pParser, pChunkHeader, &pParser->pMetadata[pParser->metadataCursor]); - if (bytesRead == pChunkHeader->sizeInBytes) { - pParser->metadataCursor += 1; - } else { - } - } - } else { - } - } else if (ma_dr_wav__chunk_matches(allowedMetadataTypes, pChunkID, ma_dr_wav_metadata_type_inst, "inst")) { - if (pChunkHeader->sizeInBytes == MA_DR_WAV_INST_BYTES) { - if (pParser->stage == ma_dr_wav__metadata_parser_stage_count) { - pParser->metadataCount += 1; - } else { - bytesRead = ma_dr_wav__read_inst_to_metadata_obj(pParser, &pParser->pMetadata[pParser->metadataCursor]); - if (bytesRead == pChunkHeader->sizeInBytes) { - pParser->metadataCursor += 1; - } else { - } - } - } else { - } - } else if (ma_dr_wav__chunk_matches(allowedMetadataTypes, pChunkID, ma_dr_wav_metadata_type_acid, "acid")) { - if (pChunkHeader->sizeInBytes == MA_DR_WAV_ACID_BYTES) { - if (pParser->stage == ma_dr_wav__metadata_parser_stage_count) { - pParser->metadataCount += 1; - } else { - bytesRead = ma_dr_wav__read_acid_to_metadata_obj(pParser, &pParser->pMetadata[pParser->metadataCursor]); - if (bytesRead == pChunkHeader->sizeInBytes) { - pParser->metadataCursor += 1; - } else { - } - } - } else { - } - } else if (ma_dr_wav__chunk_matches(allowedMetadataTypes, pChunkID, ma_dr_wav_metadata_type_cue, "cue ")) { - if (pChunkHeader->sizeInBytes >= MA_DR_WAV_CUE_BYTES) { - if (pParser->stage == ma_dr_wav__metadata_parser_stage_count) { - size_t cueCount; - pParser->metadataCount += 1; - cueCount = (size_t)(pChunkHeader->sizeInBytes - MA_DR_WAV_CUE_BYTES) / MA_DR_WAV_CUE_POINT_BYTES; - ma_dr_wav__metadata_request_extra_memory_for_stage_2(pParser, sizeof(ma_dr_wav_cue_point) * cueCount, MA_DR_WAV_METADATA_ALIGNMENT); - } else { - bytesRead = ma_dr_wav__read_cue_to_metadata_obj(pParser, pChunkHeader, &pParser->pMetadata[pParser->metadataCursor]); - if (bytesRead == pChunkHeader->sizeInBytes) { - pParser->metadataCursor += 1; - } else { - } - } - } else { - } - } else if (ma_dr_wav__chunk_matches(allowedMetadataTypes, pChunkID, ma_dr_wav_metadata_type_bext, "bext")) { - if (pChunkHeader->sizeInBytes >= MA_DR_WAV_BEXT_BYTES) { - if (pParser->stage == ma_dr_wav__metadata_parser_stage_count) { - char buffer[MA_DR_WAV_BEXT_DESCRIPTION_BYTES + 1]; - size_t allocSizeNeeded = MA_DR_WAV_BEXT_UMID_BYTES; - size_t bytesJustRead; - buffer[MA_DR_WAV_BEXT_DESCRIPTION_BYTES] = '\0'; - bytesJustRead = ma_dr_wav__metadata_parser_read(pParser, buffer, MA_DR_WAV_BEXT_DESCRIPTION_BYTES, &bytesRead); - if (bytesJustRead != MA_DR_WAV_BEXT_DESCRIPTION_BYTES) { - return bytesRead; - } - allocSizeNeeded += ma_dr_wav__strlen(buffer) + 1; - buffer[MA_DR_WAV_BEXT_ORIGINATOR_NAME_BYTES] = '\0'; - bytesJustRead = ma_dr_wav__metadata_parser_read(pParser, buffer, MA_DR_WAV_BEXT_ORIGINATOR_NAME_BYTES, &bytesRead); - if (bytesJustRead != MA_DR_WAV_BEXT_ORIGINATOR_NAME_BYTES) { - return bytesRead; - } - allocSizeNeeded += ma_dr_wav__strlen(buffer) + 1; - buffer[MA_DR_WAV_BEXT_ORIGINATOR_REF_BYTES] = '\0'; - bytesJustRead = ma_dr_wav__metadata_parser_read(pParser, buffer, MA_DR_WAV_BEXT_ORIGINATOR_REF_BYTES, &bytesRead); - if (bytesJustRead != MA_DR_WAV_BEXT_ORIGINATOR_REF_BYTES) { - return bytesRead; - } - allocSizeNeeded += ma_dr_wav__strlen(buffer) + 1; - allocSizeNeeded += (size_t)pChunkHeader->sizeInBytes - MA_DR_WAV_BEXT_BYTES; - ma_dr_wav__metadata_request_extra_memory_for_stage_2(pParser, allocSizeNeeded, 1); - pParser->metadataCount += 1; - } else { - bytesRead = ma_dr_wav__read_bext_to_metadata_obj(pParser, &pParser->pMetadata[pParser->metadataCursor], pChunkHeader->sizeInBytes); - if (bytesRead == pChunkHeader->sizeInBytes) { - pParser->metadataCursor += 1; - } else { - } - } - } else { - } - } else if (ma_dr_wav_fourcc_equal(pChunkID, "LIST") || ma_dr_wav_fourcc_equal(pChunkID, "list")) { - ma_dr_wav_metadata_location listType = ma_dr_wav_metadata_location_invalid; - while (bytesRead < pChunkHeader->sizeInBytes) { - ma_uint8 subchunkId[4]; - ma_uint8 subchunkSizeBuffer[4]; - ma_uint64 subchunkDataSize; - ma_uint64 subchunkBytesRead = 0; - ma_uint64 bytesJustRead = ma_dr_wav__metadata_parser_read(pParser, subchunkId, sizeof(subchunkId), &bytesRead); - if (bytesJustRead != sizeof(subchunkId)) { - break; - } - if (ma_dr_wav_fourcc_equal(subchunkId, "adtl")) { - listType = ma_dr_wav_metadata_location_inside_adtl_list; - continue; - } else if (ma_dr_wav_fourcc_equal(subchunkId, "INFO")) { - listType = ma_dr_wav_metadata_location_inside_info_list; - continue; - } - bytesJustRead = ma_dr_wav__metadata_parser_read(pParser, subchunkSizeBuffer, sizeof(subchunkSizeBuffer), &bytesRead); - if (bytesJustRead != sizeof(subchunkSizeBuffer)) { - break; - } - subchunkDataSize = ma_dr_wav_bytes_to_u32(subchunkSizeBuffer); - if (ma_dr_wav__chunk_matches(allowedMetadataTypes, subchunkId, ma_dr_wav_metadata_type_list_label, "labl") || ma_dr_wav__chunk_matches(allowedMetadataTypes, subchunkId, ma_dr_wav_metadata_type_list_note, "note")) { - if (subchunkDataSize >= MA_DR_WAV_LIST_LABEL_OR_NOTE_BYTES) { - ma_uint64 stringSizeWithNullTerm = subchunkDataSize - MA_DR_WAV_LIST_LABEL_OR_NOTE_BYTES; - if (pParser->stage == ma_dr_wav__metadata_parser_stage_count) { - pParser->metadataCount += 1; - ma_dr_wav__metadata_request_extra_memory_for_stage_2(pParser, (size_t)stringSizeWithNullTerm, 1); - } else { - subchunkBytesRead = ma_dr_wav__read_list_label_or_note_to_metadata_obj(pParser, &pParser->pMetadata[pParser->metadataCursor], subchunkDataSize, ma_dr_wav_fourcc_equal(subchunkId, "labl") ? ma_dr_wav_metadata_type_list_label : ma_dr_wav_metadata_type_list_note); - if (subchunkBytesRead == subchunkDataSize) { - pParser->metadataCursor += 1; - } else { - } - } - } else { - } - } else if (ma_dr_wav__chunk_matches(allowedMetadataTypes, subchunkId, ma_dr_wav_metadata_type_list_labelled_cue_region, "ltxt")) { - if (subchunkDataSize >= MA_DR_WAV_LIST_LABELLED_TEXT_BYTES) { - ma_uint64 stringSizeWithNullTerminator = subchunkDataSize - MA_DR_WAV_LIST_LABELLED_TEXT_BYTES; - if (pParser->stage == ma_dr_wav__metadata_parser_stage_count) { - pParser->metadataCount += 1; - ma_dr_wav__metadata_request_extra_memory_for_stage_2(pParser, (size_t)stringSizeWithNullTerminator, 1); - } else { - subchunkBytesRead = ma_dr_wav__read_list_labelled_cue_region_to_metadata_obj(pParser, &pParser->pMetadata[pParser->metadataCursor], subchunkDataSize); - if (subchunkBytesRead == subchunkDataSize) { - pParser->metadataCursor += 1; - } else { - } - } - } else { - } - } else if (ma_dr_wav__chunk_matches(allowedMetadataTypes, subchunkId, ma_dr_wav_metadata_type_list_info_software, "ISFT")) { - subchunkBytesRead = ma_dr_wav__metadata_process_info_text_chunk(pParser, subchunkDataSize, ma_dr_wav_metadata_type_list_info_software); - } else if (ma_dr_wav__chunk_matches(allowedMetadataTypes, subchunkId, ma_dr_wav_metadata_type_list_info_copyright, "ICOP")) { - subchunkBytesRead = ma_dr_wav__metadata_process_info_text_chunk(pParser, subchunkDataSize, ma_dr_wav_metadata_type_list_info_copyright); - } else if (ma_dr_wav__chunk_matches(allowedMetadataTypes, subchunkId, ma_dr_wav_metadata_type_list_info_title, "INAM")) { - subchunkBytesRead = ma_dr_wav__metadata_process_info_text_chunk(pParser, subchunkDataSize, ma_dr_wav_metadata_type_list_info_title); - } else if (ma_dr_wav__chunk_matches(allowedMetadataTypes, subchunkId, ma_dr_wav_metadata_type_list_info_artist, "IART")) { - subchunkBytesRead = ma_dr_wav__metadata_process_info_text_chunk(pParser, subchunkDataSize, ma_dr_wav_metadata_type_list_info_artist); - } else if (ma_dr_wav__chunk_matches(allowedMetadataTypes, subchunkId, ma_dr_wav_metadata_type_list_info_comment, "ICMT")) { - subchunkBytesRead = ma_dr_wav__metadata_process_info_text_chunk(pParser, subchunkDataSize, ma_dr_wav_metadata_type_list_info_comment); - } else if (ma_dr_wav__chunk_matches(allowedMetadataTypes, subchunkId, ma_dr_wav_metadata_type_list_info_date, "ICRD")) { - subchunkBytesRead = ma_dr_wav__metadata_process_info_text_chunk(pParser, subchunkDataSize, ma_dr_wav_metadata_type_list_info_date); - } else if (ma_dr_wav__chunk_matches(allowedMetadataTypes, subchunkId, ma_dr_wav_metadata_type_list_info_genre, "IGNR")) { - subchunkBytesRead = ma_dr_wav__metadata_process_info_text_chunk(pParser, subchunkDataSize, ma_dr_wav_metadata_type_list_info_genre); - } else if (ma_dr_wav__chunk_matches(allowedMetadataTypes, subchunkId, ma_dr_wav_metadata_type_list_info_album, "IPRD")) { - subchunkBytesRead = ma_dr_wav__metadata_process_info_text_chunk(pParser, subchunkDataSize, ma_dr_wav_metadata_type_list_info_album); - } else if (ma_dr_wav__chunk_matches(allowedMetadataTypes, subchunkId, ma_dr_wav_metadata_type_list_info_tracknumber, "ITRK")) { - subchunkBytesRead = ma_dr_wav__metadata_process_info_text_chunk(pParser, subchunkDataSize, ma_dr_wav_metadata_type_list_info_tracknumber); - } else if ((allowedMetadataTypes & ma_dr_wav_metadata_type_unknown) != 0) { - subchunkBytesRead = ma_dr_wav__metadata_process_unknown_chunk(pParser, subchunkId, subchunkDataSize, listType); - } - bytesRead += subchunkBytesRead; - MA_DR_WAV_ASSERT(subchunkBytesRead <= subchunkDataSize); - if (subchunkBytesRead < subchunkDataSize) { - ma_uint64 bytesToSeek = subchunkDataSize - subchunkBytesRead; - if (!pParser->onSeek(pParser->pReadSeekUserData, (int)bytesToSeek, ma_dr_wav_seek_origin_current)) { - break; - } - bytesRead += bytesToSeek; - } - if ((subchunkDataSize % 2) == 1) { - if (!pParser->onSeek(pParser->pReadSeekUserData, 1, ma_dr_wav_seek_origin_current)) { - break; - } - bytesRead += 1; - } - } - } else if ((allowedMetadataTypes & ma_dr_wav_metadata_type_unknown) != 0) { - bytesRead = ma_dr_wav__metadata_process_unknown_chunk(pParser, pChunkID, pChunkHeader->sizeInBytes, ma_dr_wav_metadata_location_top_level); - } - return bytesRead; -} -MA_PRIVATE ma_uint32 ma_dr_wav_get_bytes_per_pcm_frame(ma_dr_wav* pWav) -{ - ma_uint32 bytesPerFrame; - if ((pWav->bitsPerSample & 0x7) == 0) { - bytesPerFrame = (pWav->bitsPerSample * pWav->fmt.channels) >> 3; - } else { - bytesPerFrame = pWav->fmt.blockAlign; - } - if (pWav->translatedFormatTag == MA_DR_WAVE_FORMAT_ALAW || pWav->translatedFormatTag == MA_DR_WAVE_FORMAT_MULAW) { - if (bytesPerFrame != pWav->fmt.channels) { - return 0; - } - } - return bytesPerFrame; -} -MA_API ma_uint16 ma_dr_wav_fmt_get_format(const ma_dr_wav_fmt* pFMT) -{ - if (pFMT == NULL) { - return 0; - } - if (pFMT->formatTag != MA_DR_WAVE_FORMAT_EXTENSIBLE) { - return pFMT->formatTag; - } else { - return ma_dr_wav_bytes_to_u16(pFMT->subFormat); - } -} -MA_PRIVATE ma_bool32 ma_dr_wav_preinit(ma_dr_wav* pWav, ma_dr_wav_read_proc onRead, ma_dr_wav_seek_proc onSeek, void* pReadSeekUserData, const ma_allocation_callbacks* pAllocationCallbacks) -{ - if (pWav == NULL || onRead == NULL || onSeek == NULL) { - return MA_FALSE; - } - MA_DR_WAV_ZERO_MEMORY(pWav, sizeof(*pWav)); - pWav->onRead = onRead; - pWav->onSeek = onSeek; - pWav->pUserData = pReadSeekUserData; - pWav->allocationCallbacks = ma_dr_wav_copy_allocation_callbacks_or_defaults(pAllocationCallbacks); - if (pWav->allocationCallbacks.onFree == NULL || (pWav->allocationCallbacks.onMalloc == NULL && pWav->allocationCallbacks.onRealloc == NULL)) { - return MA_FALSE; - } - return MA_TRUE; -} -MA_PRIVATE ma_bool32 ma_dr_wav_init__internal(ma_dr_wav* pWav, ma_dr_wav_chunk_proc onChunk, void* pChunkUserData, ma_uint32 flags) -{ - ma_result result; - ma_uint64 cursor; - ma_bool32 sequential; - ma_uint8 riff[4]; - ma_dr_wav_fmt fmt; - unsigned short translatedFormatTag; - ma_uint64 dataChunkSize = 0; - ma_uint64 sampleCountFromFactChunk = 0; - ma_uint64 metadataStartPos; - ma_dr_wav__metadata_parser metadataParser; - ma_bool8 isProcessingMetadata = MA_FALSE; - ma_bool8 foundChunk_fmt = MA_FALSE; - ma_bool8 foundChunk_data = MA_FALSE; - ma_bool8 isAIFCFormType = MA_FALSE; - ma_uint64 aiffFrameCount = 0; - cursor = 0; - sequential = (flags & MA_DR_WAV_SEQUENTIAL) != 0; - MA_DR_WAV_ZERO_OBJECT(&fmt); - if (ma_dr_wav__on_read(pWav->onRead, pWav->pUserData, riff, sizeof(riff), &cursor) != sizeof(riff)) { - return MA_FALSE; - } - if (ma_dr_wav_fourcc_equal(riff, "RIFF")) { - pWav->container = ma_dr_wav_container_riff; - } else if (ma_dr_wav_fourcc_equal(riff, "RIFX")) { - pWav->container = ma_dr_wav_container_rifx; - } else if (ma_dr_wav_fourcc_equal(riff, "riff")) { - int i; - ma_uint8 riff2[12]; - pWav->container = ma_dr_wav_container_w64; - if (ma_dr_wav__on_read(pWav->onRead, pWav->pUserData, riff2, sizeof(riff2), &cursor) != sizeof(riff2)) { - return MA_FALSE; - } - for (i = 0; i < 12; ++i) { - if (riff2[i] != ma_dr_wavGUID_W64_RIFF[i+4]) { - return MA_FALSE; - } - } - } else if (ma_dr_wav_fourcc_equal(riff, "RF64")) { - pWav->container = ma_dr_wav_container_rf64; - } else if (ma_dr_wav_fourcc_equal(riff, "FORM")) { - pWav->container = ma_dr_wav_container_aiff; - } else { - return MA_FALSE; - } - if (pWav->container == ma_dr_wav_container_riff || pWav->container == ma_dr_wav_container_rifx || pWav->container == ma_dr_wav_container_rf64) { - ma_uint8 chunkSizeBytes[4]; - ma_uint8 wave[4]; - if (ma_dr_wav__on_read(pWav->onRead, pWav->pUserData, chunkSizeBytes, sizeof(chunkSizeBytes), &cursor) != sizeof(chunkSizeBytes)) { - return MA_FALSE; - } - if (pWav->container == ma_dr_wav_container_riff || pWav->container == ma_dr_wav_container_rifx) { - if (ma_dr_wav_bytes_to_u32_ex(chunkSizeBytes, pWav->container) < 36) { - } - } else if (pWav->container == ma_dr_wav_container_rf64) { - if (ma_dr_wav_bytes_to_u32_le(chunkSizeBytes) != 0xFFFFFFFF) { - return MA_FALSE; - } - } else { - return MA_FALSE; - } - if (ma_dr_wav__on_read(pWav->onRead, pWav->pUserData, wave, sizeof(wave), &cursor) != sizeof(wave)) { - return MA_FALSE; - } - if (!ma_dr_wav_fourcc_equal(wave, "WAVE")) { - return MA_FALSE; - } - } else if (pWav->container == ma_dr_wav_container_w64) { - ma_uint8 chunkSizeBytes[8]; - ma_uint8 wave[16]; - if (ma_dr_wav__on_read(pWav->onRead, pWav->pUserData, chunkSizeBytes, sizeof(chunkSizeBytes), &cursor) != sizeof(chunkSizeBytes)) { - return MA_FALSE; - } - if (ma_dr_wav_bytes_to_u64(chunkSizeBytes) < 80) { - return MA_FALSE; - } - if (ma_dr_wav__on_read(pWav->onRead, pWav->pUserData, wave, sizeof(wave), &cursor) != sizeof(wave)) { - return MA_FALSE; - } - if (!ma_dr_wav_guid_equal(wave, ma_dr_wavGUID_W64_WAVE)) { - return MA_FALSE; - } - } else if (pWav->container == ma_dr_wav_container_aiff) { - ma_uint8 chunkSizeBytes[4]; - ma_uint8 aiff[4]; - if (ma_dr_wav__on_read(pWav->onRead, pWav->pUserData, chunkSizeBytes, sizeof(chunkSizeBytes), &cursor) != sizeof(chunkSizeBytes)) { - return MA_FALSE; - } - if (ma_dr_wav_bytes_to_u32_be(chunkSizeBytes) < 18) { - return MA_FALSE; - } - if (ma_dr_wav__on_read(pWav->onRead, pWav->pUserData, aiff, sizeof(aiff), &cursor) != sizeof(aiff)) { - return MA_FALSE; - } - if (ma_dr_wav_fourcc_equal(aiff, "AIFF")) { - isAIFCFormType = MA_FALSE; - } else if (ma_dr_wav_fourcc_equal(aiff, "AIFC")) { - isAIFCFormType = MA_TRUE; - } else { - return MA_FALSE; - } - } else { - return MA_FALSE; - } - if (pWav->container == ma_dr_wav_container_rf64) { - ma_uint8 sizeBytes[8]; - ma_uint64 bytesRemainingInChunk; - ma_dr_wav_chunk_header header; - result = ma_dr_wav__read_chunk_header(pWav->onRead, pWav->pUserData, pWav->container, &cursor, &header); - if (result != MA_SUCCESS) { - return MA_FALSE; - } - if (!ma_dr_wav_fourcc_equal(header.id.fourcc, "ds64")) { - return MA_FALSE; - } - bytesRemainingInChunk = header.sizeInBytes + header.paddingSize; - if (!ma_dr_wav__seek_forward(pWav->onSeek, 8, pWav->pUserData)) { - return MA_FALSE; - } - bytesRemainingInChunk -= 8; - cursor += 8; - if (ma_dr_wav__on_read(pWav->onRead, pWav->pUserData, sizeBytes, sizeof(sizeBytes), &cursor) != sizeof(sizeBytes)) { - return MA_FALSE; - } - bytesRemainingInChunk -= 8; - dataChunkSize = ma_dr_wav_bytes_to_u64(sizeBytes); - if (ma_dr_wav__on_read(pWav->onRead, pWav->pUserData, sizeBytes, sizeof(sizeBytes), &cursor) != sizeof(sizeBytes)) { - return MA_FALSE; - } - bytesRemainingInChunk -= 8; - sampleCountFromFactChunk = ma_dr_wav_bytes_to_u64(sizeBytes); - if (!ma_dr_wav__seek_forward(pWav->onSeek, bytesRemainingInChunk, pWav->pUserData)) { - return MA_FALSE; - } - cursor += bytesRemainingInChunk; - } - metadataStartPos = cursor; - isProcessingMetadata = !sequential && ((flags & MA_DR_WAV_WITH_METADATA) != 0); - if (pWav->container != ma_dr_wav_container_riff && pWav->container != ma_dr_wav_container_rf64) { - isProcessingMetadata = MA_FALSE; - } - MA_DR_WAV_ZERO_MEMORY(&metadataParser, sizeof(metadataParser)); - if (isProcessingMetadata) { - metadataParser.onRead = pWav->onRead; - metadataParser.onSeek = pWav->onSeek; - metadataParser.pReadSeekUserData = pWav->pUserData; - metadataParser.stage = ma_dr_wav__metadata_parser_stage_count; - } - for (;;) { - ma_dr_wav_chunk_header header; - ma_uint64 chunkSize; - result = ma_dr_wav__read_chunk_header(pWav->onRead, pWav->pUserData, pWav->container, &cursor, &header); - if (result != MA_SUCCESS) { - break; - } - chunkSize = header.sizeInBytes; - if (!sequential && onChunk != NULL) { - ma_uint64 callbackBytesRead = onChunk(pChunkUserData, pWav->onRead, pWav->onSeek, pWav->pUserData, &header, pWav->container, &fmt); - if (callbackBytesRead > 0) { - if (ma_dr_wav__seek_from_start(pWav->onSeek, cursor, pWav->pUserData) == MA_FALSE) { - return MA_FALSE; - } - } - } - if (((pWav->container == ma_dr_wav_container_riff || pWav->container == ma_dr_wav_container_rifx || pWav->container == ma_dr_wav_container_rf64) && ma_dr_wav_fourcc_equal(header.id.fourcc, "fmt ")) || - ((pWav->container == ma_dr_wav_container_w64) && ma_dr_wav_guid_equal(header.id.guid, ma_dr_wavGUID_W64_FMT))) { - ma_uint8 fmtData[16]; - foundChunk_fmt = MA_TRUE; - if (pWav->onRead(pWav->pUserData, fmtData, sizeof(fmtData)) != sizeof(fmtData)) { - return MA_FALSE; - } - cursor += sizeof(fmtData); - fmt.formatTag = ma_dr_wav_bytes_to_u16_ex(fmtData + 0, pWav->container); - fmt.channels = ma_dr_wav_bytes_to_u16_ex(fmtData + 2, pWav->container); - fmt.sampleRate = ma_dr_wav_bytes_to_u32_ex(fmtData + 4, pWav->container); - fmt.avgBytesPerSec = ma_dr_wav_bytes_to_u32_ex(fmtData + 8, pWav->container); - fmt.blockAlign = ma_dr_wav_bytes_to_u16_ex(fmtData + 12, pWav->container); - fmt.bitsPerSample = ma_dr_wav_bytes_to_u16_ex(fmtData + 14, pWav->container); - fmt.extendedSize = 0; - fmt.validBitsPerSample = 0; - fmt.channelMask = 0; - MA_DR_WAV_ZERO_MEMORY(fmt.subFormat, sizeof(fmt.subFormat)); - if (header.sizeInBytes > 16) { - ma_uint8 fmt_cbSize[2]; - int bytesReadSoFar = 0; - if (pWav->onRead(pWav->pUserData, fmt_cbSize, sizeof(fmt_cbSize)) != sizeof(fmt_cbSize)) { - return MA_FALSE; - } - cursor += sizeof(fmt_cbSize); - bytesReadSoFar = 18; - fmt.extendedSize = ma_dr_wav_bytes_to_u16_ex(fmt_cbSize, pWav->container); - if (fmt.extendedSize > 0) { - if (fmt.formatTag == MA_DR_WAVE_FORMAT_EXTENSIBLE) { - if (fmt.extendedSize != 22) { - return MA_FALSE; - } - } - if (fmt.formatTag == MA_DR_WAVE_FORMAT_EXTENSIBLE) { - ma_uint8 fmtext[22]; - if (pWav->onRead(pWav->pUserData, fmtext, fmt.extendedSize) != fmt.extendedSize) { - return MA_FALSE; - } - fmt.validBitsPerSample = ma_dr_wav_bytes_to_u16_ex(fmtext + 0, pWav->container); - fmt.channelMask = ma_dr_wav_bytes_to_u32_ex(fmtext + 2, pWav->container); - ma_dr_wav_bytes_to_guid(fmtext + 6, fmt.subFormat); - } else { - if (pWav->onSeek(pWav->pUserData, fmt.extendedSize, ma_dr_wav_seek_origin_current) == MA_FALSE) { - return MA_FALSE; - } - } - cursor += fmt.extendedSize; - bytesReadSoFar += fmt.extendedSize; - } - if (pWav->onSeek(pWav->pUserData, (int)(header.sizeInBytes - bytesReadSoFar), ma_dr_wav_seek_origin_current) == MA_FALSE) { - return MA_FALSE; - } - cursor += (header.sizeInBytes - bytesReadSoFar); - } - if (header.paddingSize > 0) { - if (ma_dr_wav__seek_forward(pWav->onSeek, header.paddingSize, pWav->pUserData) == MA_FALSE) { - break; - } - cursor += header.paddingSize; - } - continue; - } - if (((pWav->container == ma_dr_wav_container_riff || pWav->container == ma_dr_wav_container_rifx || pWav->container == ma_dr_wav_container_rf64) && ma_dr_wav_fourcc_equal(header.id.fourcc, "data")) || - ((pWav->container == ma_dr_wav_container_w64) && ma_dr_wav_guid_equal(header.id.guid, ma_dr_wavGUID_W64_DATA))) { - foundChunk_data = MA_TRUE; - pWav->dataChunkDataPos = cursor; - if (pWav->container != ma_dr_wav_container_rf64) { - dataChunkSize = chunkSize; - } - if (sequential || !isProcessingMetadata) { - break; - } else { - chunkSize += header.paddingSize; - if (ma_dr_wav__seek_forward(pWav->onSeek, chunkSize, pWav->pUserData) == MA_FALSE) { - break; - } - cursor += chunkSize; - continue; - } - } - if (((pWav->container == ma_dr_wav_container_riff || pWav->container == ma_dr_wav_container_rifx || pWav->container == ma_dr_wav_container_rf64) && ma_dr_wav_fourcc_equal(header.id.fourcc, "fact")) || - ((pWav->container == ma_dr_wav_container_w64) && ma_dr_wav_guid_equal(header.id.guid, ma_dr_wavGUID_W64_FACT))) { - if (pWav->container == ma_dr_wav_container_riff || pWav->container == ma_dr_wav_container_rifx) { - ma_uint8 sampleCount[4]; - if (ma_dr_wav__on_read(pWav->onRead, pWav->pUserData, &sampleCount, 4, &cursor) != 4) { - return MA_FALSE; - } - chunkSize -= 4; - if (pWav->translatedFormatTag == MA_DR_WAVE_FORMAT_ADPCM) { - sampleCountFromFactChunk = ma_dr_wav_bytes_to_u32_ex(sampleCount, pWav->container); - } else { - sampleCountFromFactChunk = 0; - } - } else if (pWav->container == ma_dr_wav_container_w64) { - if (ma_dr_wav__on_read(pWav->onRead, pWav->pUserData, &sampleCountFromFactChunk, 8, &cursor) != 8) { - return MA_FALSE; - } - chunkSize -= 8; - } else if (pWav->container == ma_dr_wav_container_rf64) { - } - chunkSize += header.paddingSize; - if (ma_dr_wav__seek_forward(pWav->onSeek, chunkSize, pWav->pUserData) == MA_FALSE) { - break; - } - cursor += chunkSize; - continue; - } - if (pWav->container == ma_dr_wav_container_aiff && ma_dr_wav_fourcc_equal(header.id.fourcc, "COMM")) { - ma_uint8 commData[24]; - ma_uint32 commDataBytesToRead; - ma_uint16 channels; - ma_uint32 frameCount; - ma_uint16 sampleSizeInBits; - ma_int64 sampleRate; - ma_uint16 compressionFormat; - foundChunk_fmt = MA_TRUE; - if (isAIFCFormType) { - commDataBytesToRead = 24; - if (header.sizeInBytes < commDataBytesToRead) { - return MA_FALSE; - } - } else { - commDataBytesToRead = 18; - if (header.sizeInBytes != commDataBytesToRead) { - return MA_FALSE; - } - } - if (ma_dr_wav__on_read(pWav->onRead, pWav->pUserData, commData, commDataBytesToRead, &cursor) != commDataBytesToRead) { - return MA_FALSE; - } - channels = ma_dr_wav_bytes_to_u16_ex (commData + 0, pWav->container); - frameCount = ma_dr_wav_bytes_to_u32_ex (commData + 2, pWav->container); - sampleSizeInBits = ma_dr_wav_bytes_to_u16_ex (commData + 6, pWav->container); - sampleRate = ma_dr_wav_aiff_extented_to_s64(commData + 8); - if (sampleRate < 0 || sampleRate > 0xFFFFFFFF) { - return MA_FALSE; - } - if (isAIFCFormType) { - const ma_uint8* type = commData + 18; - if (ma_dr_wav_fourcc_equal(type, "NONE")) { - compressionFormat = MA_DR_WAVE_FORMAT_PCM; - } else if (ma_dr_wav_fourcc_equal(type, "raw ")) { - compressionFormat = MA_DR_WAVE_FORMAT_PCM; - if (sampleSizeInBits == 8) { - pWav->aiff.isUnsigned = MA_TRUE; - } - } else if (ma_dr_wav_fourcc_equal(type, "sowt")) { - compressionFormat = MA_DR_WAVE_FORMAT_PCM; - pWav->aiff.isLE = MA_TRUE; - } else if (ma_dr_wav_fourcc_equal(type, "fl32") || ma_dr_wav_fourcc_equal(type, "fl64") || ma_dr_wav_fourcc_equal(type, "FL32") || ma_dr_wav_fourcc_equal(type, "FL64")) { - compressionFormat = MA_DR_WAVE_FORMAT_IEEE_FLOAT; - } else if (ma_dr_wav_fourcc_equal(type, "alaw") || ma_dr_wav_fourcc_equal(type, "ALAW")) { - compressionFormat = MA_DR_WAVE_FORMAT_ALAW; - } else if (ma_dr_wav_fourcc_equal(type, "ulaw") || ma_dr_wav_fourcc_equal(type, "ULAW")) { - compressionFormat = MA_DR_WAVE_FORMAT_MULAW; - } else if (ma_dr_wav_fourcc_equal(type, "ima4")) { - compressionFormat = MA_DR_WAVE_FORMAT_DVI_ADPCM; - sampleSizeInBits = 4; - (void)compressionFormat; - (void)sampleSizeInBits; - return MA_FALSE; - } else { - return MA_FALSE; - } - } else { - compressionFormat = MA_DR_WAVE_FORMAT_PCM; - } - aiffFrameCount = frameCount; - fmt.formatTag = compressionFormat; - fmt.channels = channels; - fmt.sampleRate = (ma_uint32)sampleRate; - fmt.bitsPerSample = sampleSizeInBits; - fmt.blockAlign = (ma_uint16)(fmt.channels * fmt.bitsPerSample / 8); - fmt.avgBytesPerSec = fmt.blockAlign * fmt.sampleRate; - if (fmt.blockAlign == 0 && compressionFormat == MA_DR_WAVE_FORMAT_DVI_ADPCM) { - fmt.blockAlign = 34 * fmt.channels; - } - if (compressionFormat == MA_DR_WAVE_FORMAT_ALAW || compressionFormat == MA_DR_WAVE_FORMAT_MULAW) { - if (fmt.bitsPerSample > 8) { - fmt.bitsPerSample = 8; - fmt.blockAlign = fmt.channels; - } - } - fmt.bitsPerSample += (fmt.bitsPerSample & 7); - if (isAIFCFormType) { - if (ma_dr_wav__seek_forward(pWav->onSeek, (chunkSize - commDataBytesToRead), pWav->pUserData) == MA_FALSE) { - return MA_FALSE; - } - cursor += (chunkSize - commDataBytesToRead); - } - continue; - } - if (pWav->container == ma_dr_wav_container_aiff && ma_dr_wav_fourcc_equal(header.id.fourcc, "SSND")) { - ma_uint8 offsetAndBlockSizeData[8]; - ma_uint32 offset; - foundChunk_data = MA_TRUE; - if (ma_dr_wav__on_read(pWav->onRead, pWav->pUserData, offsetAndBlockSizeData, sizeof(offsetAndBlockSizeData), &cursor) != sizeof(offsetAndBlockSizeData)) { - return MA_FALSE; - } - offset = ma_dr_wav_bytes_to_u32_ex(offsetAndBlockSizeData + 0, pWav->container); - if (ma_dr_wav__seek_forward(pWav->onSeek, offset, pWav->pUserData) == MA_FALSE) { - return MA_FALSE; - } - cursor += offset; - pWav->dataChunkDataPos = cursor; - dataChunkSize = chunkSize; - if (sequential || !isProcessingMetadata) { - break; - } else { - if (ma_dr_wav__seek_forward(pWav->onSeek, chunkSize, pWav->pUserData) == MA_FALSE) { - break; - } - cursor += chunkSize; - continue; - } - } - if (isProcessingMetadata) { - ma_dr_wav__metadata_process_chunk(&metadataParser, &header, ma_dr_wav_metadata_type_all_including_unknown); - if (ma_dr_wav__seek_from_start(pWav->onSeek, cursor, pWav->pUserData) == MA_FALSE) { - break; - } - } - chunkSize += header.paddingSize; - if (ma_dr_wav__seek_forward(pWav->onSeek, chunkSize, pWav->pUserData) == MA_FALSE) { - break; - } - cursor += chunkSize; - } - if (!foundChunk_fmt || !foundChunk_data) { - return MA_FALSE; - } - if ((fmt.sampleRate == 0 || fmt.sampleRate > MA_DR_WAV_MAX_SAMPLE_RATE ) || - (fmt.channels == 0 || fmt.channels > MA_DR_WAV_MAX_CHANNELS ) || - (fmt.bitsPerSample == 0 || fmt.bitsPerSample > MA_DR_WAV_MAX_BITS_PER_SAMPLE) || - fmt.blockAlign == 0) { - return MA_FALSE; - } - translatedFormatTag = fmt.formatTag; - if (translatedFormatTag == MA_DR_WAVE_FORMAT_EXTENSIBLE) { - translatedFormatTag = ma_dr_wav_bytes_to_u16_ex(fmt.subFormat + 0, pWav->container); - } - if (!sequential) { - if (!ma_dr_wav__seek_from_start(pWav->onSeek, pWav->dataChunkDataPos, pWav->pUserData)) { - return MA_FALSE; - } - cursor = pWav->dataChunkDataPos; - } - if (isProcessingMetadata && metadataParser.metadataCount > 0) { - if (ma_dr_wav__seek_from_start(pWav->onSeek, metadataStartPos, pWav->pUserData) == MA_FALSE) { - return MA_FALSE; - } - result = ma_dr_wav__metadata_alloc(&metadataParser, &pWav->allocationCallbacks); - if (result != MA_SUCCESS) { - return MA_FALSE; - } - metadataParser.stage = ma_dr_wav__metadata_parser_stage_read; - for (;;) { - ma_dr_wav_chunk_header header; - ma_uint64 metadataBytesRead; - result = ma_dr_wav__read_chunk_header(pWav->onRead, pWav->pUserData, pWav->container, &cursor, &header); - if (result != MA_SUCCESS) { - break; - } - metadataBytesRead = ma_dr_wav__metadata_process_chunk(&metadataParser, &header, ma_dr_wav_metadata_type_all_including_unknown); - if (ma_dr_wav__seek_forward(pWav->onSeek, (header.sizeInBytes + header.paddingSize) - metadataBytesRead, pWav->pUserData) == MA_FALSE) { - ma_dr_wav_free(metadataParser.pMetadata, &pWav->allocationCallbacks); - return MA_FALSE; - } - } - pWav->pMetadata = metadataParser.pMetadata; - pWav->metadataCount = metadataParser.metadataCount; - } - if (dataChunkSize == 0xFFFFFFFF && (pWav->container == ma_dr_wav_container_riff || pWav->container == ma_dr_wav_container_rifx) && pWav->isSequentialWrite == MA_FALSE) { - dataChunkSize = 0; - for (;;) { - ma_uint8 temp[4096]; - size_t bytesRead = pWav->onRead(pWav->pUserData, temp, sizeof(temp)); - dataChunkSize += bytesRead; - if (bytesRead < sizeof(temp)) { - break; - } - } - } - if (ma_dr_wav__seek_from_start(pWav->onSeek, pWav->dataChunkDataPos, pWav->pUserData) == MA_FALSE) { - ma_dr_wav_free(pWav->pMetadata, &pWav->allocationCallbacks); - return MA_FALSE; - } - pWav->fmt = fmt; - pWav->sampleRate = fmt.sampleRate; - pWav->channels = fmt.channels; - pWav->bitsPerSample = fmt.bitsPerSample; - pWav->bytesRemaining = dataChunkSize; - pWav->translatedFormatTag = translatedFormatTag; - pWav->dataChunkDataSize = dataChunkSize; - if (sampleCountFromFactChunk != 0) { - pWav->totalPCMFrameCount = sampleCountFromFactChunk; - } else if (aiffFrameCount != 0) { - pWav->totalPCMFrameCount = aiffFrameCount; - } else { - ma_uint32 bytesPerFrame = ma_dr_wav_get_bytes_per_pcm_frame(pWav); - if (bytesPerFrame == 0) { - ma_dr_wav_free(pWav->pMetadata, &pWav->allocationCallbacks); - return MA_FALSE; - } - pWav->totalPCMFrameCount = dataChunkSize / bytesPerFrame; - if (pWav->translatedFormatTag == MA_DR_WAVE_FORMAT_ADPCM) { - ma_uint64 totalBlockHeaderSizeInBytes; - ma_uint64 blockCount = dataChunkSize / fmt.blockAlign; - if ((blockCount * fmt.blockAlign) < dataChunkSize) { - blockCount += 1; - } - totalBlockHeaderSizeInBytes = blockCount * (6*fmt.channels); - pWav->totalPCMFrameCount = ((dataChunkSize - totalBlockHeaderSizeInBytes) * 2) / fmt.channels; - } - if (pWav->translatedFormatTag == MA_DR_WAVE_FORMAT_DVI_ADPCM) { - ma_uint64 totalBlockHeaderSizeInBytes; - ma_uint64 blockCount = dataChunkSize / fmt.blockAlign; - if ((blockCount * fmt.blockAlign) < dataChunkSize) { - blockCount += 1; - } - totalBlockHeaderSizeInBytes = blockCount * (4*fmt.channels); - pWav->totalPCMFrameCount = ((dataChunkSize - totalBlockHeaderSizeInBytes) * 2) / fmt.channels; - pWav->totalPCMFrameCount += blockCount; - } - } - if (pWav->translatedFormatTag == MA_DR_WAVE_FORMAT_ADPCM || pWav->translatedFormatTag == MA_DR_WAVE_FORMAT_DVI_ADPCM) { - if (pWav->channels > 2) { - ma_dr_wav_free(pWav->pMetadata, &pWav->allocationCallbacks); - return MA_FALSE; - } - } - if (ma_dr_wav_get_bytes_per_pcm_frame(pWav) == 0) { - ma_dr_wav_free(pWav->pMetadata, &pWav->allocationCallbacks); - return MA_FALSE; - } -#ifdef MA_DR_WAV_LIBSNDFILE_COMPAT - if (pWav->translatedFormatTag == MA_DR_WAVE_FORMAT_ADPCM) { - ma_uint64 blockCount = dataChunkSize / fmt.blockAlign; - pWav->totalPCMFrameCount = (((blockCount * (fmt.blockAlign - (6*pWav->channels))) * 2)) / fmt.channels; - } - if (pWav->translatedFormatTag == MA_DR_WAVE_FORMAT_DVI_ADPCM) { - ma_uint64 blockCount = dataChunkSize / fmt.blockAlign; - pWav->totalPCMFrameCount = (((blockCount * (fmt.blockAlign - (4*pWav->channels))) * 2) + (blockCount * pWav->channels)) / fmt.channels; - } -#endif - return MA_TRUE; -} -MA_API ma_bool32 ma_dr_wav_init(ma_dr_wav* pWav, ma_dr_wav_read_proc onRead, ma_dr_wav_seek_proc onSeek, void* pUserData, const ma_allocation_callbacks* pAllocationCallbacks) -{ - return ma_dr_wav_init_ex(pWav, onRead, onSeek, NULL, pUserData, NULL, 0, pAllocationCallbacks); -} -MA_API ma_bool32 ma_dr_wav_init_ex(ma_dr_wav* pWav, ma_dr_wav_read_proc onRead, ma_dr_wav_seek_proc onSeek, ma_dr_wav_chunk_proc onChunk, void* pReadSeekUserData, void* pChunkUserData, ma_uint32 flags, const ma_allocation_callbacks* pAllocationCallbacks) -{ - if (!ma_dr_wav_preinit(pWav, onRead, onSeek, pReadSeekUserData, pAllocationCallbacks)) { - return MA_FALSE; - } - return ma_dr_wav_init__internal(pWav, onChunk, pChunkUserData, flags); -} -MA_API ma_bool32 ma_dr_wav_init_with_metadata(ma_dr_wav* pWav, ma_dr_wav_read_proc onRead, ma_dr_wav_seek_proc onSeek, void* pUserData, ma_uint32 flags, const ma_allocation_callbacks* pAllocationCallbacks) -{ - if (!ma_dr_wav_preinit(pWav, onRead, onSeek, pUserData, pAllocationCallbacks)) { - return MA_FALSE; - } - return ma_dr_wav_init__internal(pWav, NULL, NULL, flags | MA_DR_WAV_WITH_METADATA); -} -MA_API ma_dr_wav_metadata* ma_dr_wav_take_ownership_of_metadata(ma_dr_wav* pWav) -{ - ma_dr_wav_metadata *result = pWav->pMetadata; - pWav->pMetadata = NULL; - pWav->metadataCount = 0; - return result; -} -MA_PRIVATE size_t ma_dr_wav__write(ma_dr_wav* pWav, const void* pData, size_t dataSize) -{ - MA_DR_WAV_ASSERT(pWav != NULL); - MA_DR_WAV_ASSERT(pWav->onWrite != NULL); - return pWav->onWrite(pWav->pUserData, pData, dataSize); -} -MA_PRIVATE size_t ma_dr_wav__write_byte(ma_dr_wav* pWav, ma_uint8 byte) -{ - MA_DR_WAV_ASSERT(pWav != NULL); - MA_DR_WAV_ASSERT(pWav->onWrite != NULL); - return pWav->onWrite(pWav->pUserData, &byte, 1); -} -MA_PRIVATE size_t ma_dr_wav__write_u16ne_to_le(ma_dr_wav* pWav, ma_uint16 value) -{ - MA_DR_WAV_ASSERT(pWav != NULL); - MA_DR_WAV_ASSERT(pWav->onWrite != NULL); - if (!ma_dr_wav__is_little_endian()) { - value = ma_dr_wav__bswap16(value); - } - return ma_dr_wav__write(pWav, &value, 2); -} -MA_PRIVATE size_t ma_dr_wav__write_u32ne_to_le(ma_dr_wav* pWav, ma_uint32 value) -{ - MA_DR_WAV_ASSERT(pWav != NULL); - MA_DR_WAV_ASSERT(pWav->onWrite != NULL); - if (!ma_dr_wav__is_little_endian()) { - value = ma_dr_wav__bswap32(value); - } - return ma_dr_wav__write(pWav, &value, 4); -} -MA_PRIVATE size_t ma_dr_wav__write_u64ne_to_le(ma_dr_wav* pWav, ma_uint64 value) -{ - MA_DR_WAV_ASSERT(pWav != NULL); - MA_DR_WAV_ASSERT(pWav->onWrite != NULL); - if (!ma_dr_wav__is_little_endian()) { - value = ma_dr_wav__bswap64(value); - } - return ma_dr_wav__write(pWav, &value, 8); -} -MA_PRIVATE size_t ma_dr_wav__write_f32ne_to_le(ma_dr_wav* pWav, float value) -{ - union { - ma_uint32 u32; - float f32; - } u; - MA_DR_WAV_ASSERT(pWav != NULL); - MA_DR_WAV_ASSERT(pWav->onWrite != NULL); - u.f32 = value; - if (!ma_dr_wav__is_little_endian()) { - u.u32 = ma_dr_wav__bswap32(u.u32); - } - return ma_dr_wav__write(pWav, &u.u32, 4); -} -MA_PRIVATE size_t ma_dr_wav__write_or_count(ma_dr_wav* pWav, const void* pData, size_t dataSize) -{ - if (pWav == NULL) { - return dataSize; - } - return ma_dr_wav__write(pWav, pData, dataSize); -} -MA_PRIVATE size_t ma_dr_wav__write_or_count_byte(ma_dr_wav* pWav, ma_uint8 byte) -{ - if (pWav == NULL) { - return 1; - } - return ma_dr_wav__write_byte(pWav, byte); -} -MA_PRIVATE size_t ma_dr_wav__write_or_count_u16ne_to_le(ma_dr_wav* pWav, ma_uint16 value) -{ - if (pWav == NULL) { - return 2; - } - return ma_dr_wav__write_u16ne_to_le(pWav, value); -} -MA_PRIVATE size_t ma_dr_wav__write_or_count_u32ne_to_le(ma_dr_wav* pWav, ma_uint32 value) -{ - if (pWav == NULL) { - return 4; - } - return ma_dr_wav__write_u32ne_to_le(pWav, value); -} -#if 0 -MA_PRIVATE size_t ma_dr_wav__write_or_count_u64ne_to_le(ma_dr_wav* pWav, ma_uint64 value) -{ - if (pWav == NULL) { - return 8; - } - return ma_dr_wav__write_u64ne_to_le(pWav, value); -} -#endif -MA_PRIVATE size_t ma_dr_wav__write_or_count_f32ne_to_le(ma_dr_wav* pWav, float value) -{ - if (pWav == NULL) { - return 4; - } - return ma_dr_wav__write_f32ne_to_le(pWav, value); -} -MA_PRIVATE size_t ma_dr_wav__write_or_count_string_to_fixed_size_buf(ma_dr_wav* pWav, char* str, size_t bufFixedSize) -{ - size_t len; - if (pWav == NULL) { - return bufFixedSize; - } - len = ma_dr_wav__strlen_clamped(str, bufFixedSize); - ma_dr_wav__write_or_count(pWav, str, len); - if (len < bufFixedSize) { - size_t i; - for (i = 0; i < bufFixedSize - len; ++i) { - ma_dr_wav__write_byte(pWav, 0); - } - } - return bufFixedSize; -} -MA_PRIVATE size_t ma_dr_wav__write_or_count_metadata(ma_dr_wav* pWav, ma_dr_wav_metadata* pMetadatas, ma_uint32 metadataCount) -{ - size_t bytesWritten = 0; - ma_bool32 hasListAdtl = MA_FALSE; - ma_bool32 hasListInfo = MA_FALSE; - ma_uint32 iMetadata; - if (pMetadatas == NULL || metadataCount == 0) { - return 0; - } - for (iMetadata = 0; iMetadata < metadataCount; ++iMetadata) { - ma_dr_wav_metadata* pMetadata = &pMetadatas[iMetadata]; - ma_uint32 chunkSize = 0; - if ((pMetadata->type & ma_dr_wav_metadata_type_list_all_info_strings) || (pMetadata->type == ma_dr_wav_metadata_type_unknown && pMetadata->data.unknown.chunkLocation == ma_dr_wav_metadata_location_inside_info_list)) { - hasListInfo = MA_TRUE; - } - if ((pMetadata->type & ma_dr_wav_metadata_type_list_all_adtl) || (pMetadata->type == ma_dr_wav_metadata_type_unknown && pMetadata->data.unknown.chunkLocation == ma_dr_wav_metadata_location_inside_adtl_list)) { - hasListAdtl = MA_TRUE; - } - switch (pMetadata->type) { - case ma_dr_wav_metadata_type_smpl: - { - ma_uint32 iLoop; - chunkSize = MA_DR_WAV_SMPL_BYTES + MA_DR_WAV_SMPL_LOOP_BYTES * pMetadata->data.smpl.sampleLoopCount + pMetadata->data.smpl.samplerSpecificDataSizeInBytes; - bytesWritten += ma_dr_wav__write_or_count(pWav, "smpl", 4); - bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, chunkSize); - bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, pMetadata->data.smpl.manufacturerId); - bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, pMetadata->data.smpl.productId); - bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, pMetadata->data.smpl.samplePeriodNanoseconds); - bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, pMetadata->data.smpl.midiUnityNote); - bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, pMetadata->data.smpl.midiPitchFraction); - bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, pMetadata->data.smpl.smpteFormat); - bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, pMetadata->data.smpl.smpteOffset); - bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, pMetadata->data.smpl.sampleLoopCount); - bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, pMetadata->data.smpl.samplerSpecificDataSizeInBytes); - for (iLoop = 0; iLoop < pMetadata->data.smpl.sampleLoopCount; ++iLoop) { - bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, pMetadata->data.smpl.pLoops[iLoop].cuePointId); - bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, pMetadata->data.smpl.pLoops[iLoop].type); - bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, pMetadata->data.smpl.pLoops[iLoop].firstSampleByteOffset); - bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, pMetadata->data.smpl.pLoops[iLoop].lastSampleByteOffset); - bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, pMetadata->data.smpl.pLoops[iLoop].sampleFraction); - bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, pMetadata->data.smpl.pLoops[iLoop].playCount); - } - if (pMetadata->data.smpl.samplerSpecificDataSizeInBytes > 0) { - bytesWritten += ma_dr_wav__write_or_count(pWav, pMetadata->data.smpl.pSamplerSpecificData, pMetadata->data.smpl.samplerSpecificDataSizeInBytes); - } - } break; - case ma_dr_wav_metadata_type_inst: - { - chunkSize = MA_DR_WAV_INST_BYTES; - bytesWritten += ma_dr_wav__write_or_count(pWav, "inst", 4); - bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, chunkSize); - bytesWritten += ma_dr_wav__write_or_count(pWav, &pMetadata->data.inst.midiUnityNote, 1); - bytesWritten += ma_dr_wav__write_or_count(pWav, &pMetadata->data.inst.fineTuneCents, 1); - bytesWritten += ma_dr_wav__write_or_count(pWav, &pMetadata->data.inst.gainDecibels, 1); - bytesWritten += ma_dr_wav__write_or_count(pWav, &pMetadata->data.inst.lowNote, 1); - bytesWritten += ma_dr_wav__write_or_count(pWav, &pMetadata->data.inst.highNote, 1); - bytesWritten += ma_dr_wav__write_or_count(pWav, &pMetadata->data.inst.lowVelocity, 1); - bytesWritten += ma_dr_wav__write_or_count(pWav, &pMetadata->data.inst.highVelocity, 1); - } break; - case ma_dr_wav_metadata_type_cue: - { - ma_uint32 iCuePoint; - chunkSize = MA_DR_WAV_CUE_BYTES + MA_DR_WAV_CUE_POINT_BYTES * pMetadata->data.cue.cuePointCount; - bytesWritten += ma_dr_wav__write_or_count(pWav, "cue ", 4); - bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, chunkSize); - bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, pMetadata->data.cue.cuePointCount); - for (iCuePoint = 0; iCuePoint < pMetadata->data.cue.cuePointCount; ++iCuePoint) { - bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, pMetadata->data.cue.pCuePoints[iCuePoint].id); - bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, pMetadata->data.cue.pCuePoints[iCuePoint].playOrderPosition); - bytesWritten += ma_dr_wav__write_or_count(pWav, pMetadata->data.cue.pCuePoints[iCuePoint].dataChunkId, 4); - bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, pMetadata->data.cue.pCuePoints[iCuePoint].chunkStart); - bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, pMetadata->data.cue.pCuePoints[iCuePoint].blockStart); - bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, pMetadata->data.cue.pCuePoints[iCuePoint].sampleByteOffset); - } - } break; - case ma_dr_wav_metadata_type_acid: - { - chunkSize = MA_DR_WAV_ACID_BYTES; - bytesWritten += ma_dr_wav__write_or_count(pWav, "acid", 4); - bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, chunkSize); - bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, pMetadata->data.acid.flags); - bytesWritten += ma_dr_wav__write_or_count_u16ne_to_le(pWav, pMetadata->data.acid.midiUnityNote); - bytesWritten += ma_dr_wav__write_or_count_u16ne_to_le(pWav, pMetadata->data.acid.reserved1); - bytesWritten += ma_dr_wav__write_or_count_f32ne_to_le(pWav, pMetadata->data.acid.reserved2); - bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, pMetadata->data.acid.numBeats); - bytesWritten += ma_dr_wav__write_or_count_u16ne_to_le(pWav, pMetadata->data.acid.meterDenominator); - bytesWritten += ma_dr_wav__write_or_count_u16ne_to_le(pWav, pMetadata->data.acid.meterNumerator); - bytesWritten += ma_dr_wav__write_or_count_f32ne_to_le(pWav, pMetadata->data.acid.tempo); - } break; - case ma_dr_wav_metadata_type_bext: - { - char reservedBuf[MA_DR_WAV_BEXT_RESERVED_BYTES]; - ma_uint32 timeReferenceLow; - ma_uint32 timeReferenceHigh; - chunkSize = MA_DR_WAV_BEXT_BYTES + pMetadata->data.bext.codingHistorySize; - bytesWritten += ma_dr_wav__write_or_count(pWav, "bext", 4); - bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, chunkSize); - bytesWritten += ma_dr_wav__write_or_count_string_to_fixed_size_buf(pWav, pMetadata->data.bext.pDescription, MA_DR_WAV_BEXT_DESCRIPTION_BYTES); - bytesWritten += ma_dr_wav__write_or_count_string_to_fixed_size_buf(pWav, pMetadata->data.bext.pOriginatorName, MA_DR_WAV_BEXT_ORIGINATOR_NAME_BYTES); - bytesWritten += ma_dr_wav__write_or_count_string_to_fixed_size_buf(pWav, pMetadata->data.bext.pOriginatorReference, MA_DR_WAV_BEXT_ORIGINATOR_REF_BYTES); - bytesWritten += ma_dr_wav__write_or_count(pWav, pMetadata->data.bext.pOriginationDate, sizeof(pMetadata->data.bext.pOriginationDate)); - bytesWritten += ma_dr_wav__write_or_count(pWav, pMetadata->data.bext.pOriginationTime, sizeof(pMetadata->data.bext.pOriginationTime)); - timeReferenceLow = (ma_uint32)(pMetadata->data.bext.timeReference & 0xFFFFFFFF); - timeReferenceHigh = (ma_uint32)(pMetadata->data.bext.timeReference >> 32); - bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, timeReferenceLow); - bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, timeReferenceHigh); - bytesWritten += ma_dr_wav__write_or_count_u16ne_to_le(pWav, pMetadata->data.bext.version); - bytesWritten += ma_dr_wav__write_or_count(pWav, pMetadata->data.bext.pUMID, MA_DR_WAV_BEXT_UMID_BYTES); - bytesWritten += ma_dr_wav__write_or_count_u16ne_to_le(pWav, pMetadata->data.bext.loudnessValue); - bytesWritten += ma_dr_wav__write_or_count_u16ne_to_le(pWav, pMetadata->data.bext.loudnessRange); - bytesWritten += ma_dr_wav__write_or_count_u16ne_to_le(pWav, pMetadata->data.bext.maxTruePeakLevel); - bytesWritten += ma_dr_wav__write_or_count_u16ne_to_le(pWav, pMetadata->data.bext.maxMomentaryLoudness); - bytesWritten += ma_dr_wav__write_or_count_u16ne_to_le(pWav, pMetadata->data.bext.maxShortTermLoudness); - MA_DR_WAV_ZERO_MEMORY(reservedBuf, sizeof(reservedBuf)); - bytesWritten += ma_dr_wav__write_or_count(pWav, reservedBuf, sizeof(reservedBuf)); - if (pMetadata->data.bext.codingHistorySize > 0) { - bytesWritten += ma_dr_wav__write_or_count(pWav, pMetadata->data.bext.pCodingHistory, pMetadata->data.bext.codingHistorySize); - } - } break; - case ma_dr_wav_metadata_type_unknown: - { - if (pMetadata->data.unknown.chunkLocation == ma_dr_wav_metadata_location_top_level) { - chunkSize = pMetadata->data.unknown.dataSizeInBytes; - bytesWritten += ma_dr_wav__write_or_count(pWav, pMetadata->data.unknown.id, 4); - bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, chunkSize); - bytesWritten += ma_dr_wav__write_or_count(pWav, pMetadata->data.unknown.pData, pMetadata->data.unknown.dataSizeInBytes); - } - } break; - default: break; - } - if ((chunkSize % 2) != 0) { - bytesWritten += ma_dr_wav__write_or_count_byte(pWav, 0); - } - } - if (hasListInfo) { - ma_uint32 chunkSize = 4; - for (iMetadata = 0; iMetadata < metadataCount; ++iMetadata) { - ma_dr_wav_metadata* pMetadata = &pMetadatas[iMetadata]; - if ((pMetadata->type & ma_dr_wav_metadata_type_list_all_info_strings)) { - chunkSize += 8; - chunkSize += pMetadata->data.infoText.stringLength + 1; - } else if (pMetadata->type == ma_dr_wav_metadata_type_unknown && pMetadata->data.unknown.chunkLocation == ma_dr_wav_metadata_location_inside_info_list) { - chunkSize += 8; - chunkSize += pMetadata->data.unknown.dataSizeInBytes; - } - if ((chunkSize % 2) != 0) { - chunkSize += 1; - } - } - bytesWritten += ma_dr_wav__write_or_count(pWav, "LIST", 4); - bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, chunkSize); - bytesWritten += ma_dr_wav__write_or_count(pWav, "INFO", 4); - for (iMetadata = 0; iMetadata < metadataCount; ++iMetadata) { - ma_dr_wav_metadata* pMetadata = &pMetadatas[iMetadata]; - ma_uint32 subchunkSize = 0; - if (pMetadata->type & ma_dr_wav_metadata_type_list_all_info_strings) { - const char* pID = NULL; - switch (pMetadata->type) { - case ma_dr_wav_metadata_type_list_info_software: pID = "ISFT"; break; - case ma_dr_wav_metadata_type_list_info_copyright: pID = "ICOP"; break; - case ma_dr_wav_metadata_type_list_info_title: pID = "INAM"; break; - case ma_dr_wav_metadata_type_list_info_artist: pID = "IART"; break; - case ma_dr_wav_metadata_type_list_info_comment: pID = "ICMT"; break; - case ma_dr_wav_metadata_type_list_info_date: pID = "ICRD"; break; - case ma_dr_wav_metadata_type_list_info_genre: pID = "IGNR"; break; - case ma_dr_wav_metadata_type_list_info_album: pID = "IPRD"; break; - case ma_dr_wav_metadata_type_list_info_tracknumber: pID = "ITRK"; break; - default: break; - } - MA_DR_WAV_ASSERT(pID != NULL); - if (pMetadata->data.infoText.stringLength) { - subchunkSize = pMetadata->data.infoText.stringLength + 1; - bytesWritten += ma_dr_wav__write_or_count(pWav, pID, 4); - bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, subchunkSize); - bytesWritten += ma_dr_wav__write_or_count(pWav, pMetadata->data.infoText.pString, pMetadata->data.infoText.stringLength); - bytesWritten += ma_dr_wav__write_or_count_byte(pWav, '\0'); - } - } else if (pMetadata->type == ma_dr_wav_metadata_type_unknown && pMetadata->data.unknown.chunkLocation == ma_dr_wav_metadata_location_inside_info_list) { - if (pMetadata->data.unknown.dataSizeInBytes) { - subchunkSize = pMetadata->data.unknown.dataSizeInBytes; - bytesWritten += ma_dr_wav__write_or_count(pWav, pMetadata->data.unknown.id, 4); - bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, pMetadata->data.unknown.dataSizeInBytes); - bytesWritten += ma_dr_wav__write_or_count(pWav, pMetadata->data.unknown.pData, subchunkSize); - } - } - if ((subchunkSize % 2) != 0) { - bytesWritten += ma_dr_wav__write_or_count_byte(pWav, 0); - } - } - } - if (hasListAdtl) { - ma_uint32 chunkSize = 4; - for (iMetadata = 0; iMetadata < metadataCount; ++iMetadata) { - ma_dr_wav_metadata* pMetadata = &pMetadatas[iMetadata]; - switch (pMetadata->type) - { - case ma_dr_wav_metadata_type_list_label: - case ma_dr_wav_metadata_type_list_note: - { - chunkSize += 8; - chunkSize += MA_DR_WAV_LIST_LABEL_OR_NOTE_BYTES; - if (pMetadata->data.labelOrNote.stringLength > 0) { - chunkSize += pMetadata->data.labelOrNote.stringLength + 1; - } - } break; - case ma_dr_wav_metadata_type_list_labelled_cue_region: - { - chunkSize += 8; - chunkSize += MA_DR_WAV_LIST_LABELLED_TEXT_BYTES; - if (pMetadata->data.labelledCueRegion.stringLength > 0) { - chunkSize += pMetadata->data.labelledCueRegion.stringLength + 1; - } - } break; - case ma_dr_wav_metadata_type_unknown: - { - if (pMetadata->data.unknown.chunkLocation == ma_dr_wav_metadata_location_inside_adtl_list) { - chunkSize += 8; - chunkSize += pMetadata->data.unknown.dataSizeInBytes; - } - } break; - default: break; - } - if ((chunkSize % 2) != 0) { - chunkSize += 1; - } - } - bytesWritten += ma_dr_wav__write_or_count(pWav, "LIST", 4); - bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, chunkSize); - bytesWritten += ma_dr_wav__write_or_count(pWav, "adtl", 4); - for (iMetadata = 0; iMetadata < metadataCount; ++iMetadata) { - ma_dr_wav_metadata* pMetadata = &pMetadatas[iMetadata]; - ma_uint32 subchunkSize = 0; - switch (pMetadata->type) - { - case ma_dr_wav_metadata_type_list_label: - case ma_dr_wav_metadata_type_list_note: - { - if (pMetadata->data.labelOrNote.stringLength > 0) { - const char *pID = NULL; - if (pMetadata->type == ma_dr_wav_metadata_type_list_label) { - pID = "labl"; - } - else if (pMetadata->type == ma_dr_wav_metadata_type_list_note) { - pID = "note"; - } - MA_DR_WAV_ASSERT(pID != NULL); - MA_DR_WAV_ASSERT(pMetadata->data.labelOrNote.pString != NULL); - subchunkSize = MA_DR_WAV_LIST_LABEL_OR_NOTE_BYTES; - bytesWritten += ma_dr_wav__write_or_count(pWav, pID, 4); - subchunkSize += pMetadata->data.labelOrNote.stringLength + 1; - bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, subchunkSize); - bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, pMetadata->data.labelOrNote.cuePointId); - bytesWritten += ma_dr_wav__write_or_count(pWav, pMetadata->data.labelOrNote.pString, pMetadata->data.labelOrNote.stringLength); - bytesWritten += ma_dr_wav__write_or_count_byte(pWav, '\0'); - } - } break; - case ma_dr_wav_metadata_type_list_labelled_cue_region: - { - subchunkSize = MA_DR_WAV_LIST_LABELLED_TEXT_BYTES; - bytesWritten += ma_dr_wav__write_or_count(pWav, "ltxt", 4); - if (pMetadata->data.labelledCueRegion.stringLength > 0) { - subchunkSize += pMetadata->data.labelledCueRegion.stringLength + 1; - } - bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, subchunkSize); - bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, pMetadata->data.labelledCueRegion.cuePointId); - bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, pMetadata->data.labelledCueRegion.sampleLength); - bytesWritten += ma_dr_wav__write_or_count(pWav, pMetadata->data.labelledCueRegion.purposeId, 4); - bytesWritten += ma_dr_wav__write_or_count_u16ne_to_le(pWav, pMetadata->data.labelledCueRegion.country); - bytesWritten += ma_dr_wav__write_or_count_u16ne_to_le(pWav, pMetadata->data.labelledCueRegion.language); - bytesWritten += ma_dr_wav__write_or_count_u16ne_to_le(pWav, pMetadata->data.labelledCueRegion.dialect); - bytesWritten += ma_dr_wav__write_or_count_u16ne_to_le(pWav, pMetadata->data.labelledCueRegion.codePage); - if (pMetadata->data.labelledCueRegion.stringLength > 0) { - MA_DR_WAV_ASSERT(pMetadata->data.labelledCueRegion.pString != NULL); - bytesWritten += ma_dr_wav__write_or_count(pWav, pMetadata->data.labelledCueRegion.pString, pMetadata->data.labelledCueRegion.stringLength); - bytesWritten += ma_dr_wav__write_or_count_byte(pWav, '\0'); - } - } break; - case ma_dr_wav_metadata_type_unknown: - { - if (pMetadata->data.unknown.chunkLocation == ma_dr_wav_metadata_location_inside_adtl_list) { - subchunkSize = pMetadata->data.unknown.dataSizeInBytes; - MA_DR_WAV_ASSERT(pMetadata->data.unknown.pData != NULL); - bytesWritten += ma_dr_wav__write_or_count(pWav, pMetadata->data.unknown.id, 4); - bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, subchunkSize); - bytesWritten += ma_dr_wav__write_or_count(pWav, pMetadata->data.unknown.pData, subchunkSize); - } - } break; - default: break; - } - if ((subchunkSize % 2) != 0) { - bytesWritten += ma_dr_wav__write_or_count_byte(pWav, 0); - } - } - } - MA_DR_WAV_ASSERT((bytesWritten % 2) == 0); - return bytesWritten; -} -MA_PRIVATE ma_uint32 ma_dr_wav__riff_chunk_size_riff(ma_uint64 dataChunkSize, ma_dr_wav_metadata* pMetadata, ma_uint32 metadataCount) -{ - ma_uint64 chunkSize = 4 + 24 + (ma_uint64)ma_dr_wav__write_or_count_metadata(NULL, pMetadata, metadataCount) + 8 + dataChunkSize + ma_dr_wav__chunk_padding_size_riff(dataChunkSize); - if (chunkSize > 0xFFFFFFFFUL) { - chunkSize = 0xFFFFFFFFUL; - } - return (ma_uint32)chunkSize; -} -MA_PRIVATE ma_uint32 ma_dr_wav__data_chunk_size_riff(ma_uint64 dataChunkSize) -{ - if (dataChunkSize <= 0xFFFFFFFFUL) { - return (ma_uint32)dataChunkSize; - } else { - return 0xFFFFFFFFUL; - } -} -MA_PRIVATE ma_uint64 ma_dr_wav__riff_chunk_size_w64(ma_uint64 dataChunkSize) -{ - ma_uint64 dataSubchunkPaddingSize = ma_dr_wav__chunk_padding_size_w64(dataChunkSize); - return 80 + 24 + dataChunkSize + dataSubchunkPaddingSize; -} -MA_PRIVATE ma_uint64 ma_dr_wav__data_chunk_size_w64(ma_uint64 dataChunkSize) -{ - return 24 + dataChunkSize; -} -MA_PRIVATE ma_uint64 ma_dr_wav__riff_chunk_size_rf64(ma_uint64 dataChunkSize, ma_dr_wav_metadata *metadata, ma_uint32 numMetadata) -{ - ma_uint64 chunkSize = 4 + 36 + 24 + (ma_uint64)ma_dr_wav__write_or_count_metadata(NULL, metadata, numMetadata) + 8 + dataChunkSize + ma_dr_wav__chunk_padding_size_riff(dataChunkSize); - if (chunkSize > 0xFFFFFFFFUL) { - chunkSize = 0xFFFFFFFFUL; - } - return chunkSize; -} -MA_PRIVATE ma_uint64 ma_dr_wav__data_chunk_size_rf64(ma_uint64 dataChunkSize) -{ - return dataChunkSize; -} -MA_PRIVATE ma_bool32 ma_dr_wav_preinit_write(ma_dr_wav* pWav, const ma_dr_wav_data_format* pFormat, ma_bool32 isSequential, ma_dr_wav_write_proc onWrite, ma_dr_wav_seek_proc onSeek, void* pUserData, const ma_allocation_callbacks* pAllocationCallbacks) -{ - if (pWav == NULL || onWrite == NULL) { - return MA_FALSE; - } - if (!isSequential && onSeek == NULL) { - return MA_FALSE; - } - if (pFormat->format == MA_DR_WAVE_FORMAT_EXTENSIBLE) { - return MA_FALSE; - } - if (pFormat->format == MA_DR_WAVE_FORMAT_ADPCM || pFormat->format == MA_DR_WAVE_FORMAT_DVI_ADPCM) { - return MA_FALSE; - } - MA_DR_WAV_ZERO_MEMORY(pWav, sizeof(*pWav)); - pWav->onWrite = onWrite; - pWav->onSeek = onSeek; - pWav->pUserData = pUserData; - pWav->allocationCallbacks = ma_dr_wav_copy_allocation_callbacks_or_defaults(pAllocationCallbacks); - if (pWav->allocationCallbacks.onFree == NULL || (pWav->allocationCallbacks.onMalloc == NULL && pWav->allocationCallbacks.onRealloc == NULL)) { - return MA_FALSE; - } - pWav->fmt.formatTag = (ma_uint16)pFormat->format; - pWav->fmt.channels = (ma_uint16)pFormat->channels; - pWav->fmt.sampleRate = pFormat->sampleRate; - pWav->fmt.avgBytesPerSec = (ma_uint32)((pFormat->bitsPerSample * pFormat->sampleRate * pFormat->channels) / 8); - pWav->fmt.blockAlign = (ma_uint16)((pFormat->channels * pFormat->bitsPerSample) / 8); - pWav->fmt.bitsPerSample = (ma_uint16)pFormat->bitsPerSample; - pWav->fmt.extendedSize = 0; - pWav->isSequentialWrite = isSequential; - return MA_TRUE; -} -MA_PRIVATE ma_bool32 ma_dr_wav_init_write__internal(ma_dr_wav* pWav, const ma_dr_wav_data_format* pFormat, ma_uint64 totalSampleCount) -{ - size_t runningPos = 0; - ma_uint64 initialDataChunkSize = 0; - ma_uint64 chunkSizeFMT; - if (pWav->isSequentialWrite) { - initialDataChunkSize = (totalSampleCount * pWav->fmt.bitsPerSample) / 8; - if (pFormat->container == ma_dr_wav_container_riff) { - if (initialDataChunkSize > (0xFFFFFFFFUL - 36)) { - return MA_FALSE; - } - } - } - pWav->dataChunkDataSizeTargetWrite = initialDataChunkSize; - if (pFormat->container == ma_dr_wav_container_riff) { - ma_uint32 chunkSizeRIFF = 28 + (ma_uint32)initialDataChunkSize; - runningPos += ma_dr_wav__write(pWav, "RIFF", 4); - runningPos += ma_dr_wav__write_u32ne_to_le(pWav, chunkSizeRIFF); - runningPos += ma_dr_wav__write(pWav, "WAVE", 4); - } else if (pFormat->container == ma_dr_wav_container_w64) { - ma_uint64 chunkSizeRIFF = 80 + 24 + initialDataChunkSize; - runningPos += ma_dr_wav__write(pWav, ma_dr_wavGUID_W64_RIFF, 16); - runningPos += ma_dr_wav__write_u64ne_to_le(pWav, chunkSizeRIFF); - runningPos += ma_dr_wav__write(pWav, ma_dr_wavGUID_W64_WAVE, 16); - } else if (pFormat->container == ma_dr_wav_container_rf64) { - runningPos += ma_dr_wav__write(pWav, "RF64", 4); - runningPos += ma_dr_wav__write_u32ne_to_le(pWav, 0xFFFFFFFF); - runningPos += ma_dr_wav__write(pWav, "WAVE", 4); - } else { - return MA_FALSE; - } - if (pFormat->container == ma_dr_wav_container_rf64) { - ma_uint32 initialds64ChunkSize = 28; - ma_uint64 initialRiffChunkSize = 8 + initialds64ChunkSize + initialDataChunkSize; - runningPos += ma_dr_wav__write(pWav, "ds64", 4); - runningPos += ma_dr_wav__write_u32ne_to_le(pWav, initialds64ChunkSize); - runningPos += ma_dr_wav__write_u64ne_to_le(pWav, initialRiffChunkSize); - runningPos += ma_dr_wav__write_u64ne_to_le(pWav, initialDataChunkSize); - runningPos += ma_dr_wav__write_u64ne_to_le(pWav, totalSampleCount); - runningPos += ma_dr_wav__write_u32ne_to_le(pWav, 0); - } - if (pFormat->container == ma_dr_wav_container_riff || pFormat->container == ma_dr_wav_container_rf64) { - chunkSizeFMT = 16; - runningPos += ma_dr_wav__write(pWav, "fmt ", 4); - runningPos += ma_dr_wav__write_u32ne_to_le(pWav, (ma_uint32)chunkSizeFMT); - } else if (pFormat->container == ma_dr_wav_container_w64) { - chunkSizeFMT = 40; - runningPos += ma_dr_wav__write(pWav, ma_dr_wavGUID_W64_FMT, 16); - runningPos += ma_dr_wav__write_u64ne_to_le(pWav, chunkSizeFMT); - } - runningPos += ma_dr_wav__write_u16ne_to_le(pWav, pWav->fmt.formatTag); - runningPos += ma_dr_wav__write_u16ne_to_le(pWav, pWav->fmt.channels); - runningPos += ma_dr_wav__write_u32ne_to_le(pWav, pWav->fmt.sampleRate); - runningPos += ma_dr_wav__write_u32ne_to_le(pWav, pWav->fmt.avgBytesPerSec); - runningPos += ma_dr_wav__write_u16ne_to_le(pWav, pWav->fmt.blockAlign); - runningPos += ma_dr_wav__write_u16ne_to_le(pWav, pWav->fmt.bitsPerSample); - if (!pWav->isSequentialWrite && pWav->pMetadata != NULL && pWav->metadataCount > 0 && (pFormat->container == ma_dr_wav_container_riff || pFormat->container == ma_dr_wav_container_rf64)) { - runningPos += ma_dr_wav__write_or_count_metadata(pWav, pWav->pMetadata, pWav->metadataCount); - } - pWav->dataChunkDataPos = runningPos; - if (pFormat->container == ma_dr_wav_container_riff) { - ma_uint32 chunkSizeDATA = (ma_uint32)initialDataChunkSize; - runningPos += ma_dr_wav__write(pWav, "data", 4); - runningPos += ma_dr_wav__write_u32ne_to_le(pWav, chunkSizeDATA); - } else if (pFormat->container == ma_dr_wav_container_w64) { - ma_uint64 chunkSizeDATA = 24 + initialDataChunkSize; - runningPos += ma_dr_wav__write(pWav, ma_dr_wavGUID_W64_DATA, 16); - runningPos += ma_dr_wav__write_u64ne_to_le(pWav, chunkSizeDATA); - } else if (pFormat->container == ma_dr_wav_container_rf64) { - runningPos += ma_dr_wav__write(pWav, "data", 4); - runningPos += ma_dr_wav__write_u32ne_to_le(pWav, 0xFFFFFFFF); - } - pWav->container = pFormat->container; - pWav->channels = (ma_uint16)pFormat->channels; - pWav->sampleRate = pFormat->sampleRate; - pWav->bitsPerSample = (ma_uint16)pFormat->bitsPerSample; - pWav->translatedFormatTag = (ma_uint16)pFormat->format; - pWav->dataChunkDataPos = runningPos; - return MA_TRUE; -} -MA_API ma_bool32 ma_dr_wav_init_write(ma_dr_wav* pWav, const ma_dr_wav_data_format* pFormat, ma_dr_wav_write_proc onWrite, ma_dr_wav_seek_proc onSeek, void* pUserData, const ma_allocation_callbacks* pAllocationCallbacks) -{ - if (!ma_dr_wav_preinit_write(pWav, pFormat, MA_FALSE, onWrite, onSeek, pUserData, pAllocationCallbacks)) { - return MA_FALSE; - } - return ma_dr_wav_init_write__internal(pWav, pFormat, 0); -} -MA_API ma_bool32 ma_dr_wav_init_write_sequential(ma_dr_wav* pWav, const ma_dr_wav_data_format* pFormat, ma_uint64 totalSampleCount, ma_dr_wav_write_proc onWrite, void* pUserData, const ma_allocation_callbacks* pAllocationCallbacks) -{ - if (!ma_dr_wav_preinit_write(pWav, pFormat, MA_TRUE, onWrite, NULL, pUserData, pAllocationCallbacks)) { - return MA_FALSE; - } - return ma_dr_wav_init_write__internal(pWav, pFormat, totalSampleCount); -} -MA_API ma_bool32 ma_dr_wav_init_write_sequential_pcm_frames(ma_dr_wav* pWav, const ma_dr_wav_data_format* pFormat, ma_uint64 totalPCMFrameCount, ma_dr_wav_write_proc onWrite, void* pUserData, const ma_allocation_callbacks* pAllocationCallbacks) -{ - if (pFormat == NULL) { - return MA_FALSE; - } - return ma_dr_wav_init_write_sequential(pWav, pFormat, totalPCMFrameCount*pFormat->channels, onWrite, pUserData, pAllocationCallbacks); -} -MA_API ma_bool32 ma_dr_wav_init_write_with_metadata(ma_dr_wav* pWav, const ma_dr_wav_data_format* pFormat, ma_dr_wav_write_proc onWrite, ma_dr_wav_seek_proc onSeek, void* pUserData, const ma_allocation_callbacks* pAllocationCallbacks, ma_dr_wav_metadata* pMetadata, ma_uint32 metadataCount) -{ - if (!ma_dr_wav_preinit_write(pWav, pFormat, MA_FALSE, onWrite, onSeek, pUserData, pAllocationCallbacks)) { - return MA_FALSE; - } - pWav->pMetadata = pMetadata; - pWav->metadataCount = metadataCount; - return ma_dr_wav_init_write__internal(pWav, pFormat, 0); -} -MA_API ma_uint64 ma_dr_wav_target_write_size_bytes(const ma_dr_wav_data_format* pFormat, ma_uint64 totalFrameCount, ma_dr_wav_metadata* pMetadata, ma_uint32 metadataCount) -{ - ma_uint64 targetDataSizeBytes = (ma_uint64)((ma_int64)totalFrameCount * pFormat->channels * pFormat->bitsPerSample/8.0); - ma_uint64 riffChunkSizeBytes; - ma_uint64 fileSizeBytes = 0; - if (pFormat->container == ma_dr_wav_container_riff) { - riffChunkSizeBytes = ma_dr_wav__riff_chunk_size_riff(targetDataSizeBytes, pMetadata, metadataCount); - fileSizeBytes = (8 + riffChunkSizeBytes); - } else if (pFormat->container == ma_dr_wav_container_w64) { - riffChunkSizeBytes = ma_dr_wav__riff_chunk_size_w64(targetDataSizeBytes); - fileSizeBytes = riffChunkSizeBytes; - } else if (pFormat->container == ma_dr_wav_container_rf64) { - riffChunkSizeBytes = ma_dr_wav__riff_chunk_size_rf64(targetDataSizeBytes, pMetadata, metadataCount); - fileSizeBytes = (8 + riffChunkSizeBytes); - } - return fileSizeBytes; -} -#ifndef MA_DR_WAV_NO_STDIO -MA_PRIVATE size_t ma_dr_wav__on_read_stdio(void* pUserData, void* pBufferOut, size_t bytesToRead) -{ - return fread(pBufferOut, 1, bytesToRead, (FILE*)pUserData); -} -MA_PRIVATE size_t ma_dr_wav__on_write_stdio(void* pUserData, const void* pData, size_t bytesToWrite) -{ - return fwrite(pData, 1, bytesToWrite, (FILE*)pUserData); -} -MA_PRIVATE ma_bool32 ma_dr_wav__on_seek_stdio(void* pUserData, int offset, ma_dr_wav_seek_origin origin) -{ - return fseek((FILE*)pUserData, offset, (origin == ma_dr_wav_seek_origin_current) ? SEEK_CUR : SEEK_SET) == 0; -} -MA_API ma_bool32 ma_dr_wav_init_file(ma_dr_wav* pWav, const char* filename, const ma_allocation_callbacks* pAllocationCallbacks) -{ - return ma_dr_wav_init_file_ex(pWav, filename, NULL, NULL, 0, pAllocationCallbacks); -} -MA_PRIVATE ma_bool32 ma_dr_wav_init_file__internal_FILE(ma_dr_wav* pWav, FILE* pFile, ma_dr_wav_chunk_proc onChunk, void* pChunkUserData, ma_uint32 flags, const ma_allocation_callbacks* pAllocationCallbacks) -{ - ma_bool32 result; - result = ma_dr_wav_preinit(pWav, ma_dr_wav__on_read_stdio, ma_dr_wav__on_seek_stdio, (void*)pFile, pAllocationCallbacks); - if (result != MA_TRUE) { - fclose(pFile); - return result; - } - result = ma_dr_wav_init__internal(pWav, onChunk, pChunkUserData, flags); - if (result != MA_TRUE) { - fclose(pFile); - return result; - } - return MA_TRUE; -} -MA_API ma_bool32 ma_dr_wav_init_file_ex(ma_dr_wav* pWav, const char* filename, ma_dr_wav_chunk_proc onChunk, void* pChunkUserData, ma_uint32 flags, const ma_allocation_callbacks* pAllocationCallbacks) -{ - FILE* pFile; - if (ma_fopen(&pFile, filename, "rb") != MA_SUCCESS) { - return MA_FALSE; - } - return ma_dr_wav_init_file__internal_FILE(pWav, pFile, onChunk, pChunkUserData, flags, pAllocationCallbacks); -} -#ifndef MA_DR_WAV_NO_WCHAR -MA_API ma_bool32 ma_dr_wav_init_file_w(ma_dr_wav* pWav, const wchar_t* filename, const ma_allocation_callbacks* pAllocationCallbacks) -{ - return ma_dr_wav_init_file_ex_w(pWav, filename, NULL, NULL, 0, pAllocationCallbacks); -} -MA_API ma_bool32 ma_dr_wav_init_file_ex_w(ma_dr_wav* pWav, const wchar_t* filename, ma_dr_wav_chunk_proc onChunk, void* pChunkUserData, ma_uint32 flags, const ma_allocation_callbacks* pAllocationCallbacks) -{ - FILE* pFile; - if (ma_wfopen(&pFile, filename, L"rb", pAllocationCallbacks) != MA_SUCCESS) { - return MA_FALSE; - } - return ma_dr_wav_init_file__internal_FILE(pWav, pFile, onChunk, pChunkUserData, flags, pAllocationCallbacks); -} -#endif -MA_API ma_bool32 ma_dr_wav_init_file_with_metadata(ma_dr_wav* pWav, const char* filename, ma_uint32 flags, const ma_allocation_callbacks* pAllocationCallbacks) -{ - FILE* pFile; - if (ma_fopen(&pFile, filename, "rb") != MA_SUCCESS) { - return MA_FALSE; - } - return ma_dr_wav_init_file__internal_FILE(pWav, pFile, NULL, NULL, flags | MA_DR_WAV_WITH_METADATA, pAllocationCallbacks); -} -#ifndef MA_DR_WAV_NO_WCHAR -MA_API ma_bool32 ma_dr_wav_init_file_with_metadata_w(ma_dr_wav* pWav, const wchar_t* filename, ma_uint32 flags, const ma_allocation_callbacks* pAllocationCallbacks) -{ - FILE* pFile; - if (ma_wfopen(&pFile, filename, L"rb", pAllocationCallbacks) != MA_SUCCESS) { - return MA_FALSE; - } - return ma_dr_wav_init_file__internal_FILE(pWav, pFile, NULL, NULL, flags | MA_DR_WAV_WITH_METADATA, pAllocationCallbacks); -} -#endif -MA_PRIVATE ma_bool32 ma_dr_wav_init_file_write__internal_FILE(ma_dr_wav* pWav, FILE* pFile, const ma_dr_wav_data_format* pFormat, ma_uint64 totalSampleCount, ma_bool32 isSequential, const ma_allocation_callbacks* pAllocationCallbacks) -{ - ma_bool32 result; - result = ma_dr_wav_preinit_write(pWav, pFormat, isSequential, ma_dr_wav__on_write_stdio, ma_dr_wav__on_seek_stdio, (void*)pFile, pAllocationCallbacks); - if (result != MA_TRUE) { - fclose(pFile); - return result; - } - result = ma_dr_wav_init_write__internal(pWav, pFormat, totalSampleCount); - if (result != MA_TRUE) { - fclose(pFile); - return result; - } - return MA_TRUE; -} -MA_PRIVATE ma_bool32 ma_dr_wav_init_file_write__internal(ma_dr_wav* pWav, const char* filename, const ma_dr_wav_data_format* pFormat, ma_uint64 totalSampleCount, ma_bool32 isSequential, const ma_allocation_callbacks* pAllocationCallbacks) -{ - FILE* pFile; - if (ma_fopen(&pFile, filename, "wb") != MA_SUCCESS) { - return MA_FALSE; - } - return ma_dr_wav_init_file_write__internal_FILE(pWav, pFile, pFormat, totalSampleCount, isSequential, pAllocationCallbacks); -} -#ifndef MA_DR_WAV_NO_WCHAR -MA_PRIVATE ma_bool32 ma_dr_wav_init_file_write_w__internal(ma_dr_wav* pWav, const wchar_t* filename, const ma_dr_wav_data_format* pFormat, ma_uint64 totalSampleCount, ma_bool32 isSequential, const ma_allocation_callbacks* pAllocationCallbacks) -{ - FILE* pFile; - if (ma_wfopen(&pFile, filename, L"wb", pAllocationCallbacks) != MA_SUCCESS) { - return MA_FALSE; - } - return ma_dr_wav_init_file_write__internal_FILE(pWav, pFile, pFormat, totalSampleCount, isSequential, pAllocationCallbacks); -} -#endif -MA_API ma_bool32 ma_dr_wav_init_file_write(ma_dr_wav* pWav, const char* filename, const ma_dr_wav_data_format* pFormat, const ma_allocation_callbacks* pAllocationCallbacks) -{ - return ma_dr_wav_init_file_write__internal(pWav, filename, pFormat, 0, MA_FALSE, pAllocationCallbacks); -} -MA_API ma_bool32 ma_dr_wav_init_file_write_sequential(ma_dr_wav* pWav, const char* filename, const ma_dr_wav_data_format* pFormat, ma_uint64 totalSampleCount, const ma_allocation_callbacks* pAllocationCallbacks) -{ - return ma_dr_wav_init_file_write__internal(pWav, filename, pFormat, totalSampleCount, MA_TRUE, pAllocationCallbacks); -} -MA_API ma_bool32 ma_dr_wav_init_file_write_sequential_pcm_frames(ma_dr_wav* pWav, const char* filename, const ma_dr_wav_data_format* pFormat, ma_uint64 totalPCMFrameCount, const ma_allocation_callbacks* pAllocationCallbacks) -{ - if (pFormat == NULL) { - return MA_FALSE; - } - return ma_dr_wav_init_file_write_sequential(pWav, filename, pFormat, totalPCMFrameCount*pFormat->channels, pAllocationCallbacks); -} -#ifndef MA_DR_WAV_NO_WCHAR -MA_API ma_bool32 ma_dr_wav_init_file_write_w(ma_dr_wav* pWav, const wchar_t* filename, const ma_dr_wav_data_format* pFormat, const ma_allocation_callbacks* pAllocationCallbacks) -{ - return ma_dr_wav_init_file_write_w__internal(pWav, filename, pFormat, 0, MA_FALSE, pAllocationCallbacks); -} -MA_API ma_bool32 ma_dr_wav_init_file_write_sequential_w(ma_dr_wav* pWav, const wchar_t* filename, const ma_dr_wav_data_format* pFormat, ma_uint64 totalSampleCount, const ma_allocation_callbacks* pAllocationCallbacks) -{ - return ma_dr_wav_init_file_write_w__internal(pWav, filename, pFormat, totalSampleCount, MA_TRUE, pAllocationCallbacks); -} -MA_API ma_bool32 ma_dr_wav_init_file_write_sequential_pcm_frames_w(ma_dr_wav* pWav, const wchar_t* filename, const ma_dr_wav_data_format* pFormat, ma_uint64 totalPCMFrameCount, const ma_allocation_callbacks* pAllocationCallbacks) -{ - if (pFormat == NULL) { - return MA_FALSE; - } - return ma_dr_wav_init_file_write_sequential_w(pWav, filename, pFormat, totalPCMFrameCount*pFormat->channels, pAllocationCallbacks); -} -#endif -#endif -MA_PRIVATE size_t ma_dr_wav__on_read_memory(void* pUserData, void* pBufferOut, size_t bytesToRead) -{ - ma_dr_wav* pWav = (ma_dr_wav*)pUserData; - size_t bytesRemaining; - MA_DR_WAV_ASSERT(pWav != NULL); - MA_DR_WAV_ASSERT(pWav->memoryStream.dataSize >= pWav->memoryStream.currentReadPos); - bytesRemaining = pWav->memoryStream.dataSize - pWav->memoryStream.currentReadPos; - if (bytesToRead > bytesRemaining) { - bytesToRead = bytesRemaining; - } - if (bytesToRead > 0) { - MA_DR_WAV_COPY_MEMORY(pBufferOut, pWav->memoryStream.data + pWav->memoryStream.currentReadPos, bytesToRead); - pWav->memoryStream.currentReadPos += bytesToRead; - } - return bytesToRead; -} -MA_PRIVATE ma_bool32 ma_dr_wav__on_seek_memory(void* pUserData, int offset, ma_dr_wav_seek_origin origin) -{ - ma_dr_wav* pWav = (ma_dr_wav*)pUserData; - MA_DR_WAV_ASSERT(pWav != NULL); - if (origin == ma_dr_wav_seek_origin_current) { - if (offset > 0) { - if (pWav->memoryStream.currentReadPos + offset > pWav->memoryStream.dataSize) { - return MA_FALSE; - } - } else { - if (pWav->memoryStream.currentReadPos < (size_t)-offset) { - return MA_FALSE; - } - } - pWav->memoryStream.currentReadPos += offset; - } else { - if ((ma_uint32)offset <= pWav->memoryStream.dataSize) { - pWav->memoryStream.currentReadPos = offset; - } else { - return MA_FALSE; - } - } - return MA_TRUE; -} -MA_PRIVATE size_t ma_dr_wav__on_write_memory(void* pUserData, const void* pDataIn, size_t bytesToWrite) -{ - ma_dr_wav* pWav = (ma_dr_wav*)pUserData; - size_t bytesRemaining; - MA_DR_WAV_ASSERT(pWav != NULL); - MA_DR_WAV_ASSERT(pWav->memoryStreamWrite.dataCapacity >= pWav->memoryStreamWrite.currentWritePos); - bytesRemaining = pWav->memoryStreamWrite.dataCapacity - pWav->memoryStreamWrite.currentWritePos; - if (bytesRemaining < bytesToWrite) { - void* pNewData; - size_t newDataCapacity = (pWav->memoryStreamWrite.dataCapacity == 0) ? 256 : pWav->memoryStreamWrite.dataCapacity * 2; - if ((newDataCapacity - pWav->memoryStreamWrite.currentWritePos) < bytesToWrite) { - newDataCapacity = pWav->memoryStreamWrite.currentWritePos + bytesToWrite; - } - pNewData = ma_dr_wav__realloc_from_callbacks(*pWav->memoryStreamWrite.ppData, newDataCapacity, pWav->memoryStreamWrite.dataCapacity, &pWav->allocationCallbacks); - if (pNewData == NULL) { - return 0; - } - *pWav->memoryStreamWrite.ppData = pNewData; - pWav->memoryStreamWrite.dataCapacity = newDataCapacity; - } - MA_DR_WAV_COPY_MEMORY(((ma_uint8*)(*pWav->memoryStreamWrite.ppData)) + pWav->memoryStreamWrite.currentWritePos, pDataIn, bytesToWrite); - pWav->memoryStreamWrite.currentWritePos += bytesToWrite; - if (pWav->memoryStreamWrite.dataSize < pWav->memoryStreamWrite.currentWritePos) { - pWav->memoryStreamWrite.dataSize = pWav->memoryStreamWrite.currentWritePos; - } - *pWav->memoryStreamWrite.pDataSize = pWav->memoryStreamWrite.dataSize; - return bytesToWrite; -} -MA_PRIVATE ma_bool32 ma_dr_wav__on_seek_memory_write(void* pUserData, int offset, ma_dr_wav_seek_origin origin) -{ - ma_dr_wav* pWav = (ma_dr_wav*)pUserData; - MA_DR_WAV_ASSERT(pWav != NULL); - if (origin == ma_dr_wav_seek_origin_current) { - if (offset > 0) { - if (pWav->memoryStreamWrite.currentWritePos + offset > pWav->memoryStreamWrite.dataSize) { - offset = (int)(pWav->memoryStreamWrite.dataSize - pWav->memoryStreamWrite.currentWritePos); - } - } else { - if (pWav->memoryStreamWrite.currentWritePos < (size_t)-offset) { - offset = -(int)pWav->memoryStreamWrite.currentWritePos; - } - } - pWav->memoryStreamWrite.currentWritePos += offset; - } else { - if ((ma_uint32)offset <= pWav->memoryStreamWrite.dataSize) { - pWav->memoryStreamWrite.currentWritePos = offset; - } else { - pWav->memoryStreamWrite.currentWritePos = pWav->memoryStreamWrite.dataSize; - } - } - return MA_TRUE; -} -MA_API ma_bool32 ma_dr_wav_init_memory(ma_dr_wav* pWav, const void* data, size_t dataSize, const ma_allocation_callbacks* pAllocationCallbacks) -{ - return ma_dr_wav_init_memory_ex(pWav, data, dataSize, NULL, NULL, 0, pAllocationCallbacks); -} -MA_API ma_bool32 ma_dr_wav_init_memory_ex(ma_dr_wav* pWav, const void* data, size_t dataSize, ma_dr_wav_chunk_proc onChunk, void* pChunkUserData, ma_uint32 flags, const ma_allocation_callbacks* pAllocationCallbacks) -{ - if (data == NULL || dataSize == 0) { - return MA_FALSE; - } - if (!ma_dr_wav_preinit(pWav, ma_dr_wav__on_read_memory, ma_dr_wav__on_seek_memory, pWav, pAllocationCallbacks)) { - return MA_FALSE; - } - pWav->memoryStream.data = (const ma_uint8*)data; - pWav->memoryStream.dataSize = dataSize; - pWav->memoryStream.currentReadPos = 0; - return ma_dr_wav_init__internal(pWav, onChunk, pChunkUserData, flags); -} -MA_API ma_bool32 ma_dr_wav_init_memory_with_metadata(ma_dr_wav* pWav, const void* data, size_t dataSize, ma_uint32 flags, const ma_allocation_callbacks* pAllocationCallbacks) -{ - if (data == NULL || dataSize == 0) { - return MA_FALSE; - } - if (!ma_dr_wav_preinit(pWav, ma_dr_wav__on_read_memory, ma_dr_wav__on_seek_memory, pWav, pAllocationCallbacks)) { - return MA_FALSE; - } - pWav->memoryStream.data = (const ma_uint8*)data; - pWav->memoryStream.dataSize = dataSize; - pWav->memoryStream.currentReadPos = 0; - return ma_dr_wav_init__internal(pWav, NULL, NULL, flags | MA_DR_WAV_WITH_METADATA); -} -MA_PRIVATE ma_bool32 ma_dr_wav_init_memory_write__internal(ma_dr_wav* pWav, void** ppData, size_t* pDataSize, const ma_dr_wav_data_format* pFormat, ma_uint64 totalSampleCount, ma_bool32 isSequential, const ma_allocation_callbacks* pAllocationCallbacks) -{ - if (ppData == NULL || pDataSize == NULL) { - return MA_FALSE; - } - *ppData = NULL; - *pDataSize = 0; - if (!ma_dr_wav_preinit_write(pWav, pFormat, isSequential, ma_dr_wav__on_write_memory, ma_dr_wav__on_seek_memory_write, pWav, pAllocationCallbacks)) { - return MA_FALSE; - } - pWav->memoryStreamWrite.ppData = ppData; - pWav->memoryStreamWrite.pDataSize = pDataSize; - pWav->memoryStreamWrite.dataSize = 0; - pWav->memoryStreamWrite.dataCapacity = 0; - pWav->memoryStreamWrite.currentWritePos = 0; - return ma_dr_wav_init_write__internal(pWav, pFormat, totalSampleCount); -} -MA_API ma_bool32 ma_dr_wav_init_memory_write(ma_dr_wav* pWav, void** ppData, size_t* pDataSize, const ma_dr_wav_data_format* pFormat, const ma_allocation_callbacks* pAllocationCallbacks) -{ - return ma_dr_wav_init_memory_write__internal(pWav, ppData, pDataSize, pFormat, 0, MA_FALSE, pAllocationCallbacks); -} -MA_API ma_bool32 ma_dr_wav_init_memory_write_sequential(ma_dr_wav* pWav, void** ppData, size_t* pDataSize, const ma_dr_wav_data_format* pFormat, ma_uint64 totalSampleCount, const ma_allocation_callbacks* pAllocationCallbacks) -{ - return ma_dr_wav_init_memory_write__internal(pWav, ppData, pDataSize, pFormat, totalSampleCount, MA_TRUE, pAllocationCallbacks); -} -MA_API ma_bool32 ma_dr_wav_init_memory_write_sequential_pcm_frames(ma_dr_wav* pWav, void** ppData, size_t* pDataSize, const ma_dr_wav_data_format* pFormat, ma_uint64 totalPCMFrameCount, const ma_allocation_callbacks* pAllocationCallbacks) -{ - if (pFormat == NULL) { - return MA_FALSE; - } - return ma_dr_wav_init_memory_write_sequential(pWav, ppData, pDataSize, pFormat, totalPCMFrameCount*pFormat->channels, pAllocationCallbacks); -} -MA_API ma_result ma_dr_wav_uninit(ma_dr_wav* pWav) -{ - ma_result result = MA_SUCCESS; - if (pWav == NULL) { - return MA_INVALID_ARGS; - } - if (pWav->onWrite != NULL) { - ma_uint32 paddingSize = 0; - if (pWav->container == ma_dr_wav_container_riff || pWav->container == ma_dr_wav_container_rf64) { - paddingSize = ma_dr_wav__chunk_padding_size_riff(pWav->dataChunkDataSize); - } else { - paddingSize = ma_dr_wav__chunk_padding_size_w64(pWav->dataChunkDataSize); - } - if (paddingSize > 0) { - ma_uint64 paddingData = 0; - ma_dr_wav__write(pWav, &paddingData, paddingSize); - } - if (pWav->onSeek && !pWav->isSequentialWrite) { - if (pWav->container == ma_dr_wav_container_riff) { - if (pWav->onSeek(pWav->pUserData, 4, ma_dr_wav_seek_origin_start)) { - ma_uint32 riffChunkSize = ma_dr_wav__riff_chunk_size_riff(pWav->dataChunkDataSize, pWav->pMetadata, pWav->metadataCount); - ma_dr_wav__write_u32ne_to_le(pWav, riffChunkSize); - } - if (pWav->onSeek(pWav->pUserData, (int)pWav->dataChunkDataPos - 4, ma_dr_wav_seek_origin_start)) { - ma_uint32 dataChunkSize = ma_dr_wav__data_chunk_size_riff(pWav->dataChunkDataSize); - ma_dr_wav__write_u32ne_to_le(pWav, dataChunkSize); - } - } else if (pWav->container == ma_dr_wav_container_w64) { - if (pWav->onSeek(pWav->pUserData, 16, ma_dr_wav_seek_origin_start)) { - ma_uint64 riffChunkSize = ma_dr_wav__riff_chunk_size_w64(pWav->dataChunkDataSize); - ma_dr_wav__write_u64ne_to_le(pWav, riffChunkSize); - } - if (pWav->onSeek(pWav->pUserData, (int)pWav->dataChunkDataPos - 8, ma_dr_wav_seek_origin_start)) { - ma_uint64 dataChunkSize = ma_dr_wav__data_chunk_size_w64(pWav->dataChunkDataSize); - ma_dr_wav__write_u64ne_to_le(pWav, dataChunkSize); - } - } else if (pWav->container == ma_dr_wav_container_rf64) { - int ds64BodyPos = 12 + 8; - if (pWav->onSeek(pWav->pUserData, ds64BodyPos + 0, ma_dr_wav_seek_origin_start)) { - ma_uint64 riffChunkSize = ma_dr_wav__riff_chunk_size_rf64(pWav->dataChunkDataSize, pWav->pMetadata, pWav->metadataCount); - ma_dr_wav__write_u64ne_to_le(pWav, riffChunkSize); - } - if (pWav->onSeek(pWav->pUserData, ds64BodyPos + 8, ma_dr_wav_seek_origin_start)) { - ma_uint64 dataChunkSize = ma_dr_wav__data_chunk_size_rf64(pWav->dataChunkDataSize); - ma_dr_wav__write_u64ne_to_le(pWav, dataChunkSize); - } - } - } - if (pWav->isSequentialWrite) { - if (pWav->dataChunkDataSize != pWav->dataChunkDataSizeTargetWrite) { - result = MA_INVALID_FILE; - } - } - } else { - ma_dr_wav_free(pWav->pMetadata, &pWav->allocationCallbacks); - } -#ifndef MA_DR_WAV_NO_STDIO - if (pWav->onRead == ma_dr_wav__on_read_stdio || pWav->onWrite == ma_dr_wav__on_write_stdio) { - fclose((FILE*)pWav->pUserData); - } -#endif - return result; -} -MA_API size_t ma_dr_wav_read_raw(ma_dr_wav* pWav, size_t bytesToRead, void* pBufferOut) -{ - size_t bytesRead; - ma_uint32 bytesPerFrame; - if (pWav == NULL || bytesToRead == 0) { - return 0; - } - if (bytesToRead > pWav->bytesRemaining) { - bytesToRead = (size_t)pWav->bytesRemaining; - } - if (bytesToRead == 0) { - return 0; - } - bytesPerFrame = ma_dr_wav_get_bytes_per_pcm_frame(pWav); - if (bytesPerFrame == 0) { - return 0; - } - if (pBufferOut != NULL) { - bytesRead = pWav->onRead(pWav->pUserData, pBufferOut, bytesToRead); - } else { - bytesRead = 0; - while (bytesRead < bytesToRead) { - size_t bytesToSeek = (bytesToRead - bytesRead); - if (bytesToSeek > 0x7FFFFFFF) { - bytesToSeek = 0x7FFFFFFF; - } - if (pWav->onSeek(pWav->pUserData, (int)bytesToSeek, ma_dr_wav_seek_origin_current) == MA_FALSE) { - break; - } - bytesRead += bytesToSeek; - } - while (bytesRead < bytesToRead) { - ma_uint8 buffer[4096]; - size_t bytesSeeked; - size_t bytesToSeek = (bytesToRead - bytesRead); - if (bytesToSeek > sizeof(buffer)) { - bytesToSeek = sizeof(buffer); - } - bytesSeeked = pWav->onRead(pWav->pUserData, buffer, bytesToSeek); - bytesRead += bytesSeeked; - if (bytesSeeked < bytesToSeek) { - break; - } - } - } - pWav->readCursorInPCMFrames += bytesRead / bytesPerFrame; - pWav->bytesRemaining -= bytesRead; - return bytesRead; -} -MA_API ma_uint64 ma_dr_wav_read_pcm_frames_le(ma_dr_wav* pWav, ma_uint64 framesToRead, void* pBufferOut) -{ - ma_uint32 bytesPerFrame; - ma_uint64 bytesToRead; - ma_uint64 framesRemainingInFile; - if (pWav == NULL || framesToRead == 0) { - return 0; - } - if (ma_dr_wav__is_compressed_format_tag(pWav->translatedFormatTag)) { - return 0; - } - framesRemainingInFile = pWav->totalPCMFrameCount - pWav->readCursorInPCMFrames; - if (framesToRead > framesRemainingInFile) { - framesToRead = framesRemainingInFile; - } - bytesPerFrame = ma_dr_wav_get_bytes_per_pcm_frame(pWav); - if (bytesPerFrame == 0) { - return 0; - } - bytesToRead = framesToRead * bytesPerFrame; - if (bytesToRead > MA_SIZE_MAX) { - bytesToRead = (MA_SIZE_MAX / bytesPerFrame) * bytesPerFrame; - } - if (bytesToRead == 0) { - return 0; - } - return ma_dr_wav_read_raw(pWav, (size_t)bytesToRead, pBufferOut) / bytesPerFrame; -} -MA_API ma_uint64 ma_dr_wav_read_pcm_frames_be(ma_dr_wav* pWav, ma_uint64 framesToRead, void* pBufferOut) -{ - ma_uint64 framesRead = ma_dr_wav_read_pcm_frames_le(pWav, framesToRead, pBufferOut); - if (pBufferOut != NULL) { - ma_uint32 bytesPerFrame = ma_dr_wav_get_bytes_per_pcm_frame(pWav); - if (bytesPerFrame == 0) { - return 0; - } - ma_dr_wav__bswap_samples(pBufferOut, framesRead*pWav->channels, bytesPerFrame/pWav->channels); - } - return framesRead; -} -MA_API ma_uint64 ma_dr_wav_read_pcm_frames(ma_dr_wav* pWav, ma_uint64 framesToRead, void* pBufferOut) -{ - ma_uint64 framesRead = 0; - if (ma_dr_wav_is_container_be(pWav->container)) { - if (pWav->container != ma_dr_wav_container_aiff || pWav->aiff.isLE == MA_FALSE) { - if (ma_dr_wav__is_little_endian()) { - framesRead = ma_dr_wav_read_pcm_frames_be(pWav, framesToRead, pBufferOut); - } else { - framesRead = ma_dr_wav_read_pcm_frames_le(pWav, framesToRead, pBufferOut); - } - goto post_process; - } - } - if (ma_dr_wav__is_little_endian()) { - framesRead = ma_dr_wav_read_pcm_frames_le(pWav, framesToRead, pBufferOut); - } else { - framesRead = ma_dr_wav_read_pcm_frames_be(pWav, framesToRead, pBufferOut); - } - post_process: - { - if (pWav->container == ma_dr_wav_container_aiff && pWav->bitsPerSample == 8 && pWav->aiff.isUnsigned == MA_FALSE) { - if (pBufferOut != NULL) { - ma_uint64 iSample; - for (iSample = 0; iSample < framesRead * pWav->channels; iSample += 1) { - ((ma_uint8*)pBufferOut)[iSample] += 128; - } - } - } - } - return framesRead; -} -MA_PRIVATE ma_bool32 ma_dr_wav_seek_to_first_pcm_frame(ma_dr_wav* pWav) -{ - if (pWav->onWrite != NULL) { - return MA_FALSE; - } - if (!pWav->onSeek(pWav->pUserData, (int)pWav->dataChunkDataPos, ma_dr_wav_seek_origin_start)) { - return MA_FALSE; - } - if (ma_dr_wav__is_compressed_format_tag(pWav->translatedFormatTag)) { - if (pWav->translatedFormatTag == MA_DR_WAVE_FORMAT_ADPCM) { - MA_DR_WAV_ZERO_OBJECT(&pWav->msadpcm); - } else if (pWav->translatedFormatTag == MA_DR_WAVE_FORMAT_DVI_ADPCM) { - MA_DR_WAV_ZERO_OBJECT(&pWav->ima); - } else { - MA_DR_WAV_ASSERT(MA_FALSE); - } - } - pWav->readCursorInPCMFrames = 0; - pWav->bytesRemaining = pWav->dataChunkDataSize; - return MA_TRUE; -} -MA_API ma_bool32 ma_dr_wav_seek_to_pcm_frame(ma_dr_wav* pWav, ma_uint64 targetFrameIndex) -{ - if (pWav == NULL || pWav->onSeek == NULL) { - return MA_FALSE; - } - if (pWav->onWrite != NULL) { - return MA_FALSE; - } - if (pWav->totalPCMFrameCount == 0) { - return MA_TRUE; - } - if (targetFrameIndex > pWav->totalPCMFrameCount) { - targetFrameIndex = pWav->totalPCMFrameCount; - } - if (ma_dr_wav__is_compressed_format_tag(pWav->translatedFormatTag)) { - if (targetFrameIndex < pWav->readCursorInPCMFrames) { - if (!ma_dr_wav_seek_to_first_pcm_frame(pWav)) { - return MA_FALSE; - } - } - if (targetFrameIndex > pWav->readCursorInPCMFrames) { - ma_uint64 offsetInFrames = targetFrameIndex - pWav->readCursorInPCMFrames; - ma_int16 devnull[2048]; - while (offsetInFrames > 0) { - ma_uint64 framesRead = 0; - ma_uint64 framesToRead = offsetInFrames; - if (framesToRead > ma_dr_wav_countof(devnull)/pWav->channels) { - framesToRead = ma_dr_wav_countof(devnull)/pWav->channels; - } - if (pWav->translatedFormatTag == MA_DR_WAVE_FORMAT_ADPCM) { - framesRead = ma_dr_wav_read_pcm_frames_s16__msadpcm(pWav, framesToRead, devnull); - } else if (pWav->translatedFormatTag == MA_DR_WAVE_FORMAT_DVI_ADPCM) { - framesRead = ma_dr_wav_read_pcm_frames_s16__ima(pWav, framesToRead, devnull); - } else { - MA_DR_WAV_ASSERT(MA_FALSE); - } - if (framesRead != framesToRead) { - return MA_FALSE; - } - offsetInFrames -= framesRead; - } - } - } else { - ma_uint64 totalSizeInBytes; - ma_uint64 currentBytePos; - ma_uint64 targetBytePos; - ma_uint64 offset; - ma_uint32 bytesPerFrame; - bytesPerFrame = ma_dr_wav_get_bytes_per_pcm_frame(pWav); - if (bytesPerFrame == 0) { - return MA_FALSE; - } - totalSizeInBytes = pWav->totalPCMFrameCount * bytesPerFrame; - currentBytePos = totalSizeInBytes - pWav->bytesRemaining; - targetBytePos = targetFrameIndex * bytesPerFrame; - if (currentBytePos < targetBytePos) { - offset = (targetBytePos - currentBytePos); - } else { - if (!ma_dr_wav_seek_to_first_pcm_frame(pWav)) { - return MA_FALSE; - } - offset = targetBytePos; - } - while (offset > 0) { - int offset32 = ((offset > INT_MAX) ? INT_MAX : (int)offset); - if (!pWav->onSeek(pWav->pUserData, offset32, ma_dr_wav_seek_origin_current)) { - return MA_FALSE; - } - pWav->readCursorInPCMFrames += offset32 / bytesPerFrame; - pWav->bytesRemaining -= offset32; - offset -= offset32; - } - } - return MA_TRUE; -} -MA_API ma_result ma_dr_wav_get_cursor_in_pcm_frames(ma_dr_wav* pWav, ma_uint64* pCursor) -{ - if (pCursor == NULL) { - return MA_INVALID_ARGS; - } - *pCursor = 0; - if (pWav == NULL) { - return MA_INVALID_ARGS; - } - *pCursor = pWav->readCursorInPCMFrames; - return MA_SUCCESS; -} -MA_API ma_result ma_dr_wav_get_length_in_pcm_frames(ma_dr_wav* pWav, ma_uint64* pLength) -{ - if (pLength == NULL) { - return MA_INVALID_ARGS; - } - *pLength = 0; - if (pWav == NULL) { - return MA_INVALID_ARGS; - } - *pLength = pWav->totalPCMFrameCount; - return MA_SUCCESS; -} -MA_API size_t ma_dr_wav_write_raw(ma_dr_wav* pWav, size_t bytesToWrite, const void* pData) -{ - size_t bytesWritten; - if (pWav == NULL || bytesToWrite == 0 || pData == NULL) { - return 0; - } - bytesWritten = pWav->onWrite(pWav->pUserData, pData, bytesToWrite); - pWav->dataChunkDataSize += bytesWritten; - return bytesWritten; -} -MA_API ma_uint64 ma_dr_wav_write_pcm_frames_le(ma_dr_wav* pWav, ma_uint64 framesToWrite, const void* pData) -{ - ma_uint64 bytesToWrite; - ma_uint64 bytesWritten; - const ma_uint8* pRunningData; - if (pWav == NULL || framesToWrite == 0 || pData == NULL) { - return 0; - } - bytesToWrite = ((framesToWrite * pWav->channels * pWav->bitsPerSample) / 8); - if (bytesToWrite > MA_SIZE_MAX) { - return 0; - } - bytesWritten = 0; - pRunningData = (const ma_uint8*)pData; - while (bytesToWrite > 0) { - size_t bytesJustWritten; - ma_uint64 bytesToWriteThisIteration; - bytesToWriteThisIteration = bytesToWrite; - MA_DR_WAV_ASSERT(bytesToWriteThisIteration <= MA_SIZE_MAX); - bytesJustWritten = ma_dr_wav_write_raw(pWav, (size_t)bytesToWriteThisIteration, pRunningData); - if (bytesJustWritten == 0) { - break; - } - bytesToWrite -= bytesJustWritten; - bytesWritten += bytesJustWritten; - pRunningData += bytesJustWritten; - } - return (bytesWritten * 8) / pWav->bitsPerSample / pWav->channels; -} -MA_API ma_uint64 ma_dr_wav_write_pcm_frames_be(ma_dr_wav* pWav, ma_uint64 framesToWrite, const void* pData) -{ - ma_uint64 bytesToWrite; - ma_uint64 bytesWritten; - ma_uint32 bytesPerSample; - const ma_uint8* pRunningData; - if (pWav == NULL || framesToWrite == 0 || pData == NULL) { - return 0; - } - bytesToWrite = ((framesToWrite * pWav->channels * pWav->bitsPerSample) / 8); - if (bytesToWrite > MA_SIZE_MAX) { - return 0; - } - bytesWritten = 0; - pRunningData = (const ma_uint8*)pData; - bytesPerSample = ma_dr_wav_get_bytes_per_pcm_frame(pWav) / pWav->channels; - if (bytesPerSample == 0) { - return 0; - } - while (bytesToWrite > 0) { - ma_uint8 temp[4096]; - ma_uint32 sampleCount; - size_t bytesJustWritten; - ma_uint64 bytesToWriteThisIteration; - bytesToWriteThisIteration = bytesToWrite; - MA_DR_WAV_ASSERT(bytesToWriteThisIteration <= MA_SIZE_MAX); - sampleCount = sizeof(temp)/bytesPerSample; - if (bytesToWriteThisIteration > ((ma_uint64)sampleCount)*bytesPerSample) { - bytesToWriteThisIteration = ((ma_uint64)sampleCount)*bytesPerSample; - } - MA_DR_WAV_COPY_MEMORY(temp, pRunningData, (size_t)bytesToWriteThisIteration); - ma_dr_wav__bswap_samples(temp, sampleCount, bytesPerSample); - bytesJustWritten = ma_dr_wav_write_raw(pWav, (size_t)bytesToWriteThisIteration, temp); - if (bytesJustWritten == 0) { - break; - } - bytesToWrite -= bytesJustWritten; - bytesWritten += bytesJustWritten; - pRunningData += bytesJustWritten; - } - return (bytesWritten * 8) / pWav->bitsPerSample / pWav->channels; -} -MA_API ma_uint64 ma_dr_wav_write_pcm_frames(ma_dr_wav* pWav, ma_uint64 framesToWrite, const void* pData) -{ - if (ma_dr_wav__is_little_endian()) { - return ma_dr_wav_write_pcm_frames_le(pWav, framesToWrite, pData); - } else { - return ma_dr_wav_write_pcm_frames_be(pWav, framesToWrite, pData); - } -} -MA_PRIVATE ma_uint64 ma_dr_wav_read_pcm_frames_s16__msadpcm(ma_dr_wav* pWav, ma_uint64 framesToRead, ma_int16* pBufferOut) -{ - ma_uint64 totalFramesRead = 0; - static ma_int32 adaptationTable[] = { - 230, 230, 230, 230, 307, 409, 512, 614, - 768, 614, 512, 409, 307, 230, 230, 230 - }; - static ma_int32 coeff1Table[] = { 256, 512, 0, 192, 240, 460, 392 }; - static ma_int32 coeff2Table[] = { 0, -256, 0, 64, 0, -208, -232 }; - MA_DR_WAV_ASSERT(pWav != NULL); - MA_DR_WAV_ASSERT(framesToRead > 0); - while (pWav->readCursorInPCMFrames < pWav->totalPCMFrameCount) { - MA_DR_WAV_ASSERT(framesToRead > 0); - if (pWav->msadpcm.cachedFrameCount == 0 && pWav->msadpcm.bytesRemainingInBlock == 0) { - if (pWav->channels == 1) { - ma_uint8 header[7]; - if (pWav->onRead(pWav->pUserData, header, sizeof(header)) != sizeof(header)) { - return totalFramesRead; - } - pWav->msadpcm.bytesRemainingInBlock = pWav->fmt.blockAlign - sizeof(header); - pWav->msadpcm.predictor[0] = header[0]; - pWav->msadpcm.delta[0] = ma_dr_wav_bytes_to_s16(header + 1); - pWav->msadpcm.prevFrames[0][1] = (ma_int32)ma_dr_wav_bytes_to_s16(header + 3); - pWav->msadpcm.prevFrames[0][0] = (ma_int32)ma_dr_wav_bytes_to_s16(header + 5); - pWav->msadpcm.cachedFrames[2] = pWav->msadpcm.prevFrames[0][0]; - pWav->msadpcm.cachedFrames[3] = pWav->msadpcm.prevFrames[0][1]; - pWav->msadpcm.cachedFrameCount = 2; - if (pWav->msadpcm.predictor[0] >= ma_dr_wav_countof(coeff1Table)) { - return totalFramesRead; - } - } else { - ma_uint8 header[14]; - if (pWav->onRead(pWav->pUserData, header, sizeof(header)) != sizeof(header)) { - return totalFramesRead; - } - pWav->msadpcm.bytesRemainingInBlock = pWav->fmt.blockAlign - sizeof(header); - pWav->msadpcm.predictor[0] = header[0]; - pWav->msadpcm.predictor[1] = header[1]; - pWav->msadpcm.delta[0] = ma_dr_wav_bytes_to_s16(header + 2); - pWav->msadpcm.delta[1] = ma_dr_wav_bytes_to_s16(header + 4); - pWav->msadpcm.prevFrames[0][1] = (ma_int32)ma_dr_wav_bytes_to_s16(header + 6); - pWav->msadpcm.prevFrames[1][1] = (ma_int32)ma_dr_wav_bytes_to_s16(header + 8); - pWav->msadpcm.prevFrames[0][0] = (ma_int32)ma_dr_wav_bytes_to_s16(header + 10); - pWav->msadpcm.prevFrames[1][0] = (ma_int32)ma_dr_wav_bytes_to_s16(header + 12); - pWav->msadpcm.cachedFrames[0] = pWav->msadpcm.prevFrames[0][0]; - pWav->msadpcm.cachedFrames[1] = pWav->msadpcm.prevFrames[1][0]; - pWav->msadpcm.cachedFrames[2] = pWav->msadpcm.prevFrames[0][1]; - pWav->msadpcm.cachedFrames[3] = pWav->msadpcm.prevFrames[1][1]; - pWav->msadpcm.cachedFrameCount = 2; - if (pWav->msadpcm.predictor[0] >= ma_dr_wav_countof(coeff1Table) || pWav->msadpcm.predictor[1] >= ma_dr_wav_countof(coeff2Table)) { - return totalFramesRead; - } - } - } - while (framesToRead > 0 && pWav->msadpcm.cachedFrameCount > 0 && pWav->readCursorInPCMFrames < pWav->totalPCMFrameCount) { - if (pBufferOut != NULL) { - ma_uint32 iSample = 0; - for (iSample = 0; iSample < pWav->channels; iSample += 1) { - pBufferOut[iSample] = (ma_int16)pWav->msadpcm.cachedFrames[(ma_dr_wav_countof(pWav->msadpcm.cachedFrames) - (pWav->msadpcm.cachedFrameCount*pWav->channels)) + iSample]; - } - pBufferOut += pWav->channels; - } - framesToRead -= 1; - totalFramesRead += 1; - pWav->readCursorInPCMFrames += 1; - pWav->msadpcm.cachedFrameCount -= 1; - } - if (framesToRead == 0) { - break; - } - if (pWav->msadpcm.cachedFrameCount == 0) { - if (pWav->msadpcm.bytesRemainingInBlock == 0) { - continue; - } else { - ma_uint8 nibbles; - ma_int32 nibble0; - ma_int32 nibble1; - if (pWav->onRead(pWav->pUserData, &nibbles, 1) != 1) { - return totalFramesRead; - } - pWav->msadpcm.bytesRemainingInBlock -= 1; - nibble0 = ((nibbles & 0xF0) >> 4); if ((nibbles & 0x80)) { nibble0 |= 0xFFFFFFF0UL; } - nibble1 = ((nibbles & 0x0F) >> 0); if ((nibbles & 0x08)) { nibble1 |= 0xFFFFFFF0UL; } - if (pWav->channels == 1) { - ma_int32 newSample0; - ma_int32 newSample1; - newSample0 = ((pWav->msadpcm.prevFrames[0][1] * coeff1Table[pWav->msadpcm.predictor[0]]) + (pWav->msadpcm.prevFrames[0][0] * coeff2Table[pWav->msadpcm.predictor[0]])) >> 8; - newSample0 += nibble0 * pWav->msadpcm.delta[0]; - newSample0 = ma_dr_wav_clamp(newSample0, -32768, 32767); - pWav->msadpcm.delta[0] = (adaptationTable[((nibbles & 0xF0) >> 4)] * pWav->msadpcm.delta[0]) >> 8; - if (pWav->msadpcm.delta[0] < 16) { - pWav->msadpcm.delta[0] = 16; - } - pWav->msadpcm.prevFrames[0][0] = pWav->msadpcm.prevFrames[0][1]; - pWav->msadpcm.prevFrames[0][1] = newSample0; - newSample1 = ((pWav->msadpcm.prevFrames[0][1] * coeff1Table[pWav->msadpcm.predictor[0]]) + (pWav->msadpcm.prevFrames[0][0] * coeff2Table[pWav->msadpcm.predictor[0]])) >> 8; - newSample1 += nibble1 * pWav->msadpcm.delta[0]; - newSample1 = ma_dr_wav_clamp(newSample1, -32768, 32767); - pWav->msadpcm.delta[0] = (adaptationTable[((nibbles & 0x0F) >> 0)] * pWav->msadpcm.delta[0]) >> 8; - if (pWav->msadpcm.delta[0] < 16) { - pWav->msadpcm.delta[0] = 16; - } - pWav->msadpcm.prevFrames[0][0] = pWav->msadpcm.prevFrames[0][1]; - pWav->msadpcm.prevFrames[0][1] = newSample1; - pWav->msadpcm.cachedFrames[2] = newSample0; - pWav->msadpcm.cachedFrames[3] = newSample1; - pWav->msadpcm.cachedFrameCount = 2; - } else { - ma_int32 newSample0; - ma_int32 newSample1; - newSample0 = ((pWav->msadpcm.prevFrames[0][1] * coeff1Table[pWav->msadpcm.predictor[0]]) + (pWav->msadpcm.prevFrames[0][0] * coeff2Table[pWav->msadpcm.predictor[0]])) >> 8; - newSample0 += nibble0 * pWav->msadpcm.delta[0]; - newSample0 = ma_dr_wav_clamp(newSample0, -32768, 32767); - pWav->msadpcm.delta[0] = (adaptationTable[((nibbles & 0xF0) >> 4)] * pWav->msadpcm.delta[0]) >> 8; - if (pWav->msadpcm.delta[0] < 16) { - pWav->msadpcm.delta[0] = 16; - } - pWav->msadpcm.prevFrames[0][0] = pWav->msadpcm.prevFrames[0][1]; - pWav->msadpcm.prevFrames[0][1] = newSample0; - newSample1 = ((pWav->msadpcm.prevFrames[1][1] * coeff1Table[pWav->msadpcm.predictor[1]]) + (pWav->msadpcm.prevFrames[1][0] * coeff2Table[pWav->msadpcm.predictor[1]])) >> 8; - newSample1 += nibble1 * pWav->msadpcm.delta[1]; - newSample1 = ma_dr_wav_clamp(newSample1, -32768, 32767); - pWav->msadpcm.delta[1] = (adaptationTable[((nibbles & 0x0F) >> 0)] * pWav->msadpcm.delta[1]) >> 8; - if (pWav->msadpcm.delta[1] < 16) { - pWav->msadpcm.delta[1] = 16; - } - pWav->msadpcm.prevFrames[1][0] = pWav->msadpcm.prevFrames[1][1]; - pWav->msadpcm.prevFrames[1][1] = newSample1; - pWav->msadpcm.cachedFrames[2] = newSample0; - pWav->msadpcm.cachedFrames[3] = newSample1; - pWav->msadpcm.cachedFrameCount = 1; - } - } - } - } - return totalFramesRead; -} -MA_PRIVATE ma_uint64 ma_dr_wav_read_pcm_frames_s16__ima(ma_dr_wav* pWav, ma_uint64 framesToRead, ma_int16* pBufferOut) -{ - ma_uint64 totalFramesRead = 0; - ma_uint32 iChannel; - static ma_int32 indexTable[16] = { - -1, -1, -1, -1, 2, 4, 6, 8, - -1, -1, -1, -1, 2, 4, 6, 8 - }; - static ma_int32 stepTable[89] = { - 7, 8, 9, 10, 11, 12, 13, 14, 16, 17, - 19, 21, 23, 25, 28, 31, 34, 37, 41, 45, - 50, 55, 60, 66, 73, 80, 88, 97, 107, 118, - 130, 143, 157, 173, 190, 209, 230, 253, 279, 307, - 337, 371, 408, 449, 494, 544, 598, 658, 724, 796, - 876, 963, 1060, 1166, 1282, 1411, 1552, 1707, 1878, 2066, - 2272, 2499, 2749, 3024, 3327, 3660, 4026, 4428, 4871, 5358, - 5894, 6484, 7132, 7845, 8630, 9493, 10442, 11487, 12635, 13899, - 15289, 16818, 18500, 20350, 22385, 24623, 27086, 29794, 32767 - }; - MA_DR_WAV_ASSERT(pWav != NULL); - MA_DR_WAV_ASSERT(framesToRead > 0); - while (pWav->readCursorInPCMFrames < pWav->totalPCMFrameCount) { - MA_DR_WAV_ASSERT(framesToRead > 0); - if (pWav->ima.cachedFrameCount == 0 && pWav->ima.bytesRemainingInBlock == 0) { - if (pWav->channels == 1) { - ma_uint8 header[4]; - if (pWav->onRead(pWav->pUserData, header, sizeof(header)) != sizeof(header)) { - return totalFramesRead; - } - pWav->ima.bytesRemainingInBlock = pWav->fmt.blockAlign - sizeof(header); - if (header[2] >= ma_dr_wav_countof(stepTable)) { - pWav->onSeek(pWav->pUserData, pWav->ima.bytesRemainingInBlock, ma_dr_wav_seek_origin_current); - pWav->ima.bytesRemainingInBlock = 0; - return totalFramesRead; - } - pWav->ima.predictor[0] = (ma_int16)ma_dr_wav_bytes_to_u16(header + 0); - pWav->ima.stepIndex[0] = ma_dr_wav_clamp(header[2], 0, (ma_int32)ma_dr_wav_countof(stepTable)-1); - pWav->ima.cachedFrames[ma_dr_wav_countof(pWav->ima.cachedFrames) - 1] = pWav->ima.predictor[0]; - pWav->ima.cachedFrameCount = 1; - } else { - ma_uint8 header[8]; - if (pWav->onRead(pWav->pUserData, header, sizeof(header)) != sizeof(header)) { - return totalFramesRead; - } - pWav->ima.bytesRemainingInBlock = pWav->fmt.blockAlign - sizeof(header); - if (header[2] >= ma_dr_wav_countof(stepTable) || header[6] >= ma_dr_wav_countof(stepTable)) { - pWav->onSeek(pWav->pUserData, pWav->ima.bytesRemainingInBlock, ma_dr_wav_seek_origin_current); - pWav->ima.bytesRemainingInBlock = 0; - return totalFramesRead; - } - pWav->ima.predictor[0] = ma_dr_wav_bytes_to_s16(header + 0); - pWav->ima.stepIndex[0] = ma_dr_wav_clamp(header[2], 0, (ma_int32)ma_dr_wav_countof(stepTable)-1); - pWav->ima.predictor[1] = ma_dr_wav_bytes_to_s16(header + 4); - pWav->ima.stepIndex[1] = ma_dr_wav_clamp(header[6], 0, (ma_int32)ma_dr_wav_countof(stepTable)-1); - pWav->ima.cachedFrames[ma_dr_wav_countof(pWav->ima.cachedFrames) - 2] = pWav->ima.predictor[0]; - pWav->ima.cachedFrames[ma_dr_wav_countof(pWav->ima.cachedFrames) - 1] = pWav->ima.predictor[1]; - pWav->ima.cachedFrameCount = 1; - } - } - while (framesToRead > 0 && pWav->ima.cachedFrameCount > 0 && pWav->readCursorInPCMFrames < pWav->totalPCMFrameCount) { - if (pBufferOut != NULL) { - ma_uint32 iSample; - for (iSample = 0; iSample < pWav->channels; iSample += 1) { - pBufferOut[iSample] = (ma_int16)pWav->ima.cachedFrames[(ma_dr_wav_countof(pWav->ima.cachedFrames) - (pWav->ima.cachedFrameCount*pWav->channels)) + iSample]; - } - pBufferOut += pWav->channels; - } - framesToRead -= 1; - totalFramesRead += 1; - pWav->readCursorInPCMFrames += 1; - pWav->ima.cachedFrameCount -= 1; - } - if (framesToRead == 0) { - break; - } - if (pWav->ima.cachedFrameCount == 0) { - if (pWav->ima.bytesRemainingInBlock == 0) { - continue; - } else { - pWav->ima.cachedFrameCount = 8; - for (iChannel = 0; iChannel < pWav->channels; ++iChannel) { - ma_uint32 iByte; - ma_uint8 nibbles[4]; - if (pWav->onRead(pWav->pUserData, &nibbles, 4) != 4) { - pWav->ima.cachedFrameCount = 0; - return totalFramesRead; - } - pWav->ima.bytesRemainingInBlock -= 4; - for (iByte = 0; iByte < 4; ++iByte) { - ma_uint8 nibble0 = ((nibbles[iByte] & 0x0F) >> 0); - ma_uint8 nibble1 = ((nibbles[iByte] & 0xF0) >> 4); - ma_int32 step = stepTable[pWav->ima.stepIndex[iChannel]]; - ma_int32 predictor = pWav->ima.predictor[iChannel]; - ma_int32 diff = step >> 3; - if (nibble0 & 1) diff += step >> 2; - if (nibble0 & 2) diff += step >> 1; - if (nibble0 & 4) diff += step; - if (nibble0 & 8) diff = -diff; - predictor = ma_dr_wav_clamp(predictor + diff, -32768, 32767); - pWav->ima.predictor[iChannel] = predictor; - pWav->ima.stepIndex[iChannel] = ma_dr_wav_clamp(pWav->ima.stepIndex[iChannel] + indexTable[nibble0], 0, (ma_int32)ma_dr_wav_countof(stepTable)-1); - pWav->ima.cachedFrames[(ma_dr_wav_countof(pWav->ima.cachedFrames) - (pWav->ima.cachedFrameCount*pWav->channels)) + (iByte*2+0)*pWav->channels + iChannel] = predictor; - step = stepTable[pWav->ima.stepIndex[iChannel]]; - predictor = pWav->ima.predictor[iChannel]; - diff = step >> 3; - if (nibble1 & 1) diff += step >> 2; - if (nibble1 & 2) diff += step >> 1; - if (nibble1 & 4) diff += step; - if (nibble1 & 8) diff = -diff; - predictor = ma_dr_wav_clamp(predictor + diff, -32768, 32767); - pWav->ima.predictor[iChannel] = predictor; - pWav->ima.stepIndex[iChannel] = ma_dr_wav_clamp(pWav->ima.stepIndex[iChannel] + indexTable[nibble1], 0, (ma_int32)ma_dr_wav_countof(stepTable)-1); - pWav->ima.cachedFrames[(ma_dr_wav_countof(pWav->ima.cachedFrames) - (pWav->ima.cachedFrameCount*pWav->channels)) + (iByte*2+1)*pWav->channels + iChannel] = predictor; - } - } - } - } - } - return totalFramesRead; -} -#ifndef MA_DR_WAV_NO_CONVERSION_API -static unsigned short g_ma_dr_wavAlawTable[256] = { - 0xEA80, 0xEB80, 0xE880, 0xE980, 0xEE80, 0xEF80, 0xEC80, 0xED80, 0xE280, 0xE380, 0xE080, 0xE180, 0xE680, 0xE780, 0xE480, 0xE580, - 0xF540, 0xF5C0, 0xF440, 0xF4C0, 0xF740, 0xF7C0, 0xF640, 0xF6C0, 0xF140, 0xF1C0, 0xF040, 0xF0C0, 0xF340, 0xF3C0, 0xF240, 0xF2C0, - 0xAA00, 0xAE00, 0xA200, 0xA600, 0xBA00, 0xBE00, 0xB200, 0xB600, 0x8A00, 0x8E00, 0x8200, 0x8600, 0x9A00, 0x9E00, 0x9200, 0x9600, - 0xD500, 0xD700, 0xD100, 0xD300, 0xDD00, 0xDF00, 0xD900, 0xDB00, 0xC500, 0xC700, 0xC100, 0xC300, 0xCD00, 0xCF00, 0xC900, 0xCB00, - 0xFEA8, 0xFEB8, 0xFE88, 0xFE98, 0xFEE8, 0xFEF8, 0xFEC8, 0xFED8, 0xFE28, 0xFE38, 0xFE08, 0xFE18, 0xFE68, 0xFE78, 0xFE48, 0xFE58, - 0xFFA8, 0xFFB8, 0xFF88, 0xFF98, 0xFFE8, 0xFFF8, 0xFFC8, 0xFFD8, 0xFF28, 0xFF38, 0xFF08, 0xFF18, 0xFF68, 0xFF78, 0xFF48, 0xFF58, - 0xFAA0, 0xFAE0, 0xFA20, 0xFA60, 0xFBA0, 0xFBE0, 0xFB20, 0xFB60, 0xF8A0, 0xF8E0, 0xF820, 0xF860, 0xF9A0, 0xF9E0, 0xF920, 0xF960, - 0xFD50, 0xFD70, 0xFD10, 0xFD30, 0xFDD0, 0xFDF0, 0xFD90, 0xFDB0, 0xFC50, 0xFC70, 0xFC10, 0xFC30, 0xFCD0, 0xFCF0, 0xFC90, 0xFCB0, - 0x1580, 0x1480, 0x1780, 0x1680, 0x1180, 0x1080, 0x1380, 0x1280, 0x1D80, 0x1C80, 0x1F80, 0x1E80, 0x1980, 0x1880, 0x1B80, 0x1A80, - 0x0AC0, 0x0A40, 0x0BC0, 0x0B40, 0x08C0, 0x0840, 0x09C0, 0x0940, 0x0EC0, 0x0E40, 0x0FC0, 0x0F40, 0x0CC0, 0x0C40, 0x0DC0, 0x0D40, - 0x5600, 0x5200, 0x5E00, 0x5A00, 0x4600, 0x4200, 0x4E00, 0x4A00, 0x7600, 0x7200, 0x7E00, 0x7A00, 0x6600, 0x6200, 0x6E00, 0x6A00, - 0x2B00, 0x2900, 0x2F00, 0x2D00, 0x2300, 0x2100, 0x2700, 0x2500, 0x3B00, 0x3900, 0x3F00, 0x3D00, 0x3300, 0x3100, 0x3700, 0x3500, - 0x0158, 0x0148, 0x0178, 0x0168, 0x0118, 0x0108, 0x0138, 0x0128, 0x01D8, 0x01C8, 0x01F8, 0x01E8, 0x0198, 0x0188, 0x01B8, 0x01A8, - 0x0058, 0x0048, 0x0078, 0x0068, 0x0018, 0x0008, 0x0038, 0x0028, 0x00D8, 0x00C8, 0x00F8, 0x00E8, 0x0098, 0x0088, 0x00B8, 0x00A8, - 0x0560, 0x0520, 0x05E0, 0x05A0, 0x0460, 0x0420, 0x04E0, 0x04A0, 0x0760, 0x0720, 0x07E0, 0x07A0, 0x0660, 0x0620, 0x06E0, 0x06A0, - 0x02B0, 0x0290, 0x02F0, 0x02D0, 0x0230, 0x0210, 0x0270, 0x0250, 0x03B0, 0x0390, 0x03F0, 0x03D0, 0x0330, 0x0310, 0x0370, 0x0350 -}; -static unsigned short g_ma_dr_wavMulawTable[256] = { - 0x8284, 0x8684, 0x8A84, 0x8E84, 0x9284, 0x9684, 0x9A84, 0x9E84, 0xA284, 0xA684, 0xAA84, 0xAE84, 0xB284, 0xB684, 0xBA84, 0xBE84, - 0xC184, 0xC384, 0xC584, 0xC784, 0xC984, 0xCB84, 0xCD84, 0xCF84, 0xD184, 0xD384, 0xD584, 0xD784, 0xD984, 0xDB84, 0xDD84, 0xDF84, - 0xE104, 0xE204, 0xE304, 0xE404, 0xE504, 0xE604, 0xE704, 0xE804, 0xE904, 0xEA04, 0xEB04, 0xEC04, 0xED04, 0xEE04, 0xEF04, 0xF004, - 0xF0C4, 0xF144, 0xF1C4, 0xF244, 0xF2C4, 0xF344, 0xF3C4, 0xF444, 0xF4C4, 0xF544, 0xF5C4, 0xF644, 0xF6C4, 0xF744, 0xF7C4, 0xF844, - 0xF8A4, 0xF8E4, 0xF924, 0xF964, 0xF9A4, 0xF9E4, 0xFA24, 0xFA64, 0xFAA4, 0xFAE4, 0xFB24, 0xFB64, 0xFBA4, 0xFBE4, 0xFC24, 0xFC64, - 0xFC94, 0xFCB4, 0xFCD4, 0xFCF4, 0xFD14, 0xFD34, 0xFD54, 0xFD74, 0xFD94, 0xFDB4, 0xFDD4, 0xFDF4, 0xFE14, 0xFE34, 0xFE54, 0xFE74, - 0xFE8C, 0xFE9C, 0xFEAC, 0xFEBC, 0xFECC, 0xFEDC, 0xFEEC, 0xFEFC, 0xFF0C, 0xFF1C, 0xFF2C, 0xFF3C, 0xFF4C, 0xFF5C, 0xFF6C, 0xFF7C, - 0xFF88, 0xFF90, 0xFF98, 0xFFA0, 0xFFA8, 0xFFB0, 0xFFB8, 0xFFC0, 0xFFC8, 0xFFD0, 0xFFD8, 0xFFE0, 0xFFE8, 0xFFF0, 0xFFF8, 0x0000, - 0x7D7C, 0x797C, 0x757C, 0x717C, 0x6D7C, 0x697C, 0x657C, 0x617C, 0x5D7C, 0x597C, 0x557C, 0x517C, 0x4D7C, 0x497C, 0x457C, 0x417C, - 0x3E7C, 0x3C7C, 0x3A7C, 0x387C, 0x367C, 0x347C, 0x327C, 0x307C, 0x2E7C, 0x2C7C, 0x2A7C, 0x287C, 0x267C, 0x247C, 0x227C, 0x207C, - 0x1EFC, 0x1DFC, 0x1CFC, 0x1BFC, 0x1AFC, 0x19FC, 0x18FC, 0x17FC, 0x16FC, 0x15FC, 0x14FC, 0x13FC, 0x12FC, 0x11FC, 0x10FC, 0x0FFC, - 0x0F3C, 0x0EBC, 0x0E3C, 0x0DBC, 0x0D3C, 0x0CBC, 0x0C3C, 0x0BBC, 0x0B3C, 0x0ABC, 0x0A3C, 0x09BC, 0x093C, 0x08BC, 0x083C, 0x07BC, - 0x075C, 0x071C, 0x06DC, 0x069C, 0x065C, 0x061C, 0x05DC, 0x059C, 0x055C, 0x051C, 0x04DC, 0x049C, 0x045C, 0x041C, 0x03DC, 0x039C, - 0x036C, 0x034C, 0x032C, 0x030C, 0x02EC, 0x02CC, 0x02AC, 0x028C, 0x026C, 0x024C, 0x022C, 0x020C, 0x01EC, 0x01CC, 0x01AC, 0x018C, - 0x0174, 0x0164, 0x0154, 0x0144, 0x0134, 0x0124, 0x0114, 0x0104, 0x00F4, 0x00E4, 0x00D4, 0x00C4, 0x00B4, 0x00A4, 0x0094, 0x0084, - 0x0078, 0x0070, 0x0068, 0x0060, 0x0058, 0x0050, 0x0048, 0x0040, 0x0038, 0x0030, 0x0028, 0x0020, 0x0018, 0x0010, 0x0008, 0x0000 -}; -static MA_INLINE ma_int16 ma_dr_wav__alaw_to_s16(ma_uint8 sampleIn) -{ - return (short)g_ma_dr_wavAlawTable[sampleIn]; -} -static MA_INLINE ma_int16 ma_dr_wav__mulaw_to_s16(ma_uint8 sampleIn) -{ - return (short)g_ma_dr_wavMulawTable[sampleIn]; -} -MA_PRIVATE void ma_dr_wav__pcm_to_s16(ma_int16* pOut, const ma_uint8* pIn, size_t totalSampleCount, unsigned int bytesPerSample) -{ - size_t i; - if (bytesPerSample == 1) { - ma_dr_wav_u8_to_s16(pOut, pIn, totalSampleCount); - return; - } - if (bytesPerSample == 2) { - for (i = 0; i < totalSampleCount; ++i) { - *pOut++ = ((const ma_int16*)pIn)[i]; - } - return; - } - if (bytesPerSample == 3) { - ma_dr_wav_s24_to_s16(pOut, pIn, totalSampleCount); - return; - } - if (bytesPerSample == 4) { - ma_dr_wav_s32_to_s16(pOut, (const ma_int32*)pIn, totalSampleCount); - return; - } - if (bytesPerSample > 8) { - MA_DR_WAV_ZERO_MEMORY(pOut, totalSampleCount * sizeof(*pOut)); - return; - } - for (i = 0; i < totalSampleCount; ++i) { - ma_uint64 sample = 0; - unsigned int shift = (8 - bytesPerSample) * 8; - unsigned int j; - for (j = 0; j < bytesPerSample; j += 1) { - MA_DR_WAV_ASSERT(j < 8); - sample |= (ma_uint64)(pIn[j]) << shift; - shift += 8; - } - pIn += j; - *pOut++ = (ma_int16)((ma_int64)sample >> 48); - } -} -MA_PRIVATE void ma_dr_wav__ieee_to_s16(ma_int16* pOut, const ma_uint8* pIn, size_t totalSampleCount, unsigned int bytesPerSample) -{ - if (bytesPerSample == 4) { - ma_dr_wav_f32_to_s16(pOut, (const float*)pIn, totalSampleCount); - return; - } else if (bytesPerSample == 8) { - ma_dr_wav_f64_to_s16(pOut, (const double*)pIn, totalSampleCount); - return; - } else { - MA_DR_WAV_ZERO_MEMORY(pOut, totalSampleCount * sizeof(*pOut)); - return; - } -} -MA_PRIVATE ma_uint64 ma_dr_wav_read_pcm_frames_s16__pcm(ma_dr_wav* pWav, ma_uint64 framesToRead, ma_int16* pBufferOut) -{ - ma_uint64 totalFramesRead; - ma_uint8 sampleData[4096] = {0}; - ma_uint32 bytesPerFrame; - ma_uint32 bytesPerSample; - ma_uint64 samplesRead; - if ((pWav->translatedFormatTag == MA_DR_WAVE_FORMAT_PCM && pWav->bitsPerSample == 16) || pBufferOut == NULL) { - return ma_dr_wav_read_pcm_frames(pWav, framesToRead, pBufferOut); - } - bytesPerFrame = ma_dr_wav_get_bytes_per_pcm_frame(pWav); - if (bytesPerFrame == 0) { - return 0; - } - bytesPerSample = bytesPerFrame / pWav->channels; - if (bytesPerSample == 0 || (bytesPerFrame % pWav->channels) != 0) { - return 0; - } - totalFramesRead = 0; - while (framesToRead > 0) { - ma_uint64 framesToReadThisIteration = ma_dr_wav_min(framesToRead, sizeof(sampleData)/bytesPerFrame); - ma_uint64 framesRead = ma_dr_wav_read_pcm_frames(pWav, framesToReadThisIteration, sampleData); - if (framesRead == 0) { - break; - } - MA_DR_WAV_ASSERT(framesRead <= framesToReadThisIteration); - samplesRead = framesRead * pWav->channels; - if ((samplesRead * bytesPerSample) > sizeof(sampleData)) { - MA_DR_WAV_ASSERT(MA_FALSE); - break; - } - ma_dr_wav__pcm_to_s16(pBufferOut, sampleData, (size_t)samplesRead, bytesPerSample); - pBufferOut += samplesRead; - framesToRead -= framesRead; - totalFramesRead += framesRead; - } - return totalFramesRead; -} -MA_PRIVATE ma_uint64 ma_dr_wav_read_pcm_frames_s16__ieee(ma_dr_wav* pWav, ma_uint64 framesToRead, ma_int16* pBufferOut) -{ - ma_uint64 totalFramesRead; - ma_uint8 sampleData[4096] = {0}; - ma_uint32 bytesPerFrame; - ma_uint32 bytesPerSample; - ma_uint64 samplesRead; - if (pBufferOut == NULL) { - return ma_dr_wav_read_pcm_frames(pWav, framesToRead, NULL); - } - bytesPerFrame = ma_dr_wav_get_bytes_per_pcm_frame(pWav); - if (bytesPerFrame == 0) { - return 0; - } - bytesPerSample = bytesPerFrame / pWav->channels; - if (bytesPerSample == 0 || (bytesPerFrame % pWav->channels) != 0) { - return 0; - } - totalFramesRead = 0; - while (framesToRead > 0) { - ma_uint64 framesToReadThisIteration = ma_dr_wav_min(framesToRead, sizeof(sampleData)/bytesPerFrame); - ma_uint64 framesRead = ma_dr_wav_read_pcm_frames(pWav, framesToReadThisIteration, sampleData); - if (framesRead == 0) { - break; - } - MA_DR_WAV_ASSERT(framesRead <= framesToReadThisIteration); - samplesRead = framesRead * pWav->channels; - if ((samplesRead * bytesPerSample) > sizeof(sampleData)) { - MA_DR_WAV_ASSERT(MA_FALSE); - break; - } - ma_dr_wav__ieee_to_s16(pBufferOut, sampleData, (size_t)samplesRead, bytesPerSample); - pBufferOut += samplesRead; - framesToRead -= framesRead; - totalFramesRead += framesRead; - } - return totalFramesRead; -} -MA_PRIVATE ma_uint64 ma_dr_wav_read_pcm_frames_s16__alaw(ma_dr_wav* pWav, ma_uint64 framesToRead, ma_int16* pBufferOut) -{ - ma_uint64 totalFramesRead; - ma_uint8 sampleData[4096] = {0}; - ma_uint32 bytesPerFrame; - ma_uint32 bytesPerSample; - ma_uint64 samplesRead; - if (pBufferOut == NULL) { - return ma_dr_wav_read_pcm_frames(pWav, framesToRead, NULL); - } - bytesPerFrame = ma_dr_wav_get_bytes_per_pcm_frame(pWav); - if (bytesPerFrame == 0) { - return 0; - } - bytesPerSample = bytesPerFrame / pWav->channels; - if (bytesPerSample == 0 || (bytesPerFrame % pWav->channels) != 0) { - return 0; - } - totalFramesRead = 0; - while (framesToRead > 0) { - ma_uint64 framesToReadThisIteration = ma_dr_wav_min(framesToRead, sizeof(sampleData)/bytesPerFrame); - ma_uint64 framesRead = ma_dr_wav_read_pcm_frames(pWav, framesToReadThisIteration, sampleData); - if (framesRead == 0) { - break; - } - MA_DR_WAV_ASSERT(framesRead <= framesToReadThisIteration); - samplesRead = framesRead * pWav->channels; - if ((samplesRead * bytesPerSample) > sizeof(sampleData)) { - MA_DR_WAV_ASSERT(MA_FALSE); - break; - } - ma_dr_wav_alaw_to_s16(pBufferOut, sampleData, (size_t)samplesRead); - #ifdef MA_DR_WAV_LIBSNDFILE_COMPAT - { - if (pWav->container == ma_dr_wav_container_aiff) { - ma_uint64 iSample; - for (iSample = 0; iSample < samplesRead; iSample += 1) { - pBufferOut[iSample] = -pBufferOut[iSample]; - } - } - } - #endif - pBufferOut += samplesRead; - framesToRead -= framesRead; - totalFramesRead += framesRead; - } - return totalFramesRead; -} -MA_PRIVATE ma_uint64 ma_dr_wav_read_pcm_frames_s16__mulaw(ma_dr_wav* pWav, ma_uint64 framesToRead, ma_int16* pBufferOut) -{ - ma_uint64 totalFramesRead; - ma_uint8 sampleData[4096] = {0}; - ma_uint32 bytesPerFrame; - ma_uint32 bytesPerSample; - ma_uint64 samplesRead; - if (pBufferOut == NULL) { - return ma_dr_wav_read_pcm_frames(pWav, framesToRead, NULL); - } - bytesPerFrame = ma_dr_wav_get_bytes_per_pcm_frame(pWav); - if (bytesPerFrame == 0) { - return 0; - } - bytesPerSample = bytesPerFrame / pWav->channels; - if (bytesPerSample == 0 || (bytesPerFrame % pWav->channels) != 0) { - return 0; - } - totalFramesRead = 0; - while (framesToRead > 0) { - ma_uint64 framesToReadThisIteration = ma_dr_wav_min(framesToRead, sizeof(sampleData)/bytesPerFrame); - ma_uint64 framesRead = ma_dr_wav_read_pcm_frames(pWav, framesToReadThisIteration, sampleData); - if (framesRead == 0) { - break; - } - MA_DR_WAV_ASSERT(framesRead <= framesToReadThisIteration); - samplesRead = framesRead * pWav->channels; - if ((samplesRead * bytesPerSample) > sizeof(sampleData)) { - MA_DR_WAV_ASSERT(MA_FALSE); - break; - } - ma_dr_wav_mulaw_to_s16(pBufferOut, sampleData, (size_t)samplesRead); - #ifdef MA_DR_WAV_LIBSNDFILE_COMPAT - { - if (pWav->container == ma_dr_wav_container_aiff) { - ma_uint64 iSample; - for (iSample = 0; iSample < samplesRead; iSample += 1) { - pBufferOut[iSample] = -pBufferOut[iSample]; - } - } - } - #endif - pBufferOut += samplesRead; - framesToRead -= framesRead; - totalFramesRead += framesRead; - } - return totalFramesRead; -} -MA_API ma_uint64 ma_dr_wav_read_pcm_frames_s16(ma_dr_wav* pWav, ma_uint64 framesToRead, ma_int16* pBufferOut) -{ - if (pWav == NULL || framesToRead == 0) { - return 0; - } - if (pBufferOut == NULL) { - return ma_dr_wav_read_pcm_frames(pWav, framesToRead, NULL); - } - if (framesToRead * pWav->channels * sizeof(ma_int16) > MA_SIZE_MAX) { - framesToRead = MA_SIZE_MAX / sizeof(ma_int16) / pWav->channels; - } - if (pWav->translatedFormatTag == MA_DR_WAVE_FORMAT_PCM) { - return ma_dr_wav_read_pcm_frames_s16__pcm(pWav, framesToRead, pBufferOut); - } - if (pWav->translatedFormatTag == MA_DR_WAVE_FORMAT_IEEE_FLOAT) { - return ma_dr_wav_read_pcm_frames_s16__ieee(pWav, framesToRead, pBufferOut); - } - if (pWav->translatedFormatTag == MA_DR_WAVE_FORMAT_ALAW) { - return ma_dr_wav_read_pcm_frames_s16__alaw(pWav, framesToRead, pBufferOut); - } - if (pWav->translatedFormatTag == MA_DR_WAVE_FORMAT_MULAW) { - return ma_dr_wav_read_pcm_frames_s16__mulaw(pWav, framesToRead, pBufferOut); - } - if (pWav->translatedFormatTag == MA_DR_WAVE_FORMAT_ADPCM) { - return ma_dr_wav_read_pcm_frames_s16__msadpcm(pWav, framesToRead, pBufferOut); - } - if (pWav->translatedFormatTag == MA_DR_WAVE_FORMAT_DVI_ADPCM) { - return ma_dr_wav_read_pcm_frames_s16__ima(pWav, framesToRead, pBufferOut); - } - return 0; -} -MA_API ma_uint64 ma_dr_wav_read_pcm_frames_s16le(ma_dr_wav* pWav, ma_uint64 framesToRead, ma_int16* pBufferOut) -{ - ma_uint64 framesRead = ma_dr_wav_read_pcm_frames_s16(pWav, framesToRead, pBufferOut); - if (pBufferOut != NULL && ma_dr_wav__is_little_endian() == MA_FALSE) { - ma_dr_wav__bswap_samples_s16(pBufferOut, framesRead*pWav->channels); - } - return framesRead; -} -MA_API ma_uint64 ma_dr_wav_read_pcm_frames_s16be(ma_dr_wav* pWav, ma_uint64 framesToRead, ma_int16* pBufferOut) -{ - ma_uint64 framesRead = ma_dr_wav_read_pcm_frames_s16(pWav, framesToRead, pBufferOut); - if (pBufferOut != NULL && ma_dr_wav__is_little_endian() == MA_TRUE) { - ma_dr_wav__bswap_samples_s16(pBufferOut, framesRead*pWav->channels); - } - return framesRead; -} -MA_API void ma_dr_wav_u8_to_s16(ma_int16* pOut, const ma_uint8* pIn, size_t sampleCount) -{ - int r; - size_t i; - for (i = 0; i < sampleCount; ++i) { - int x = pIn[i]; - r = x << 8; - r = r - 32768; - pOut[i] = (short)r; - } -} -MA_API void ma_dr_wav_s24_to_s16(ma_int16* pOut, const ma_uint8* pIn, size_t sampleCount) -{ - int r; - size_t i; - for (i = 0; i < sampleCount; ++i) { - int x = ((int)(((unsigned int)(((const ma_uint8*)pIn)[i*3+0]) << 8) | ((unsigned int)(((const ma_uint8*)pIn)[i*3+1]) << 16) | ((unsigned int)(((const ma_uint8*)pIn)[i*3+2])) << 24)) >> 8; - r = x >> 8; - pOut[i] = (short)r; - } -} -MA_API void ma_dr_wav_s32_to_s16(ma_int16* pOut, const ma_int32* pIn, size_t sampleCount) -{ - int r; - size_t i; - for (i = 0; i < sampleCount; ++i) { - int x = pIn[i]; - r = x >> 16; - pOut[i] = (short)r; - } -} -MA_API void ma_dr_wav_f32_to_s16(ma_int16* pOut, const float* pIn, size_t sampleCount) -{ - int r; - size_t i; - for (i = 0; i < sampleCount; ++i) { - float x = pIn[i]; - float c; - c = ((x < -1) ? -1 : ((x > 1) ? 1 : x)); - c = c + 1; - r = (int)(c * 32767.5f); - r = r - 32768; - pOut[i] = (short)r; - } -} -MA_API void ma_dr_wav_f64_to_s16(ma_int16* pOut, const double* pIn, size_t sampleCount) -{ - int r; - size_t i; - for (i = 0; i < sampleCount; ++i) { - double x = pIn[i]; - double c; - c = ((x < -1) ? -1 : ((x > 1) ? 1 : x)); - c = c + 1; - r = (int)(c * 32767.5); - r = r - 32768; - pOut[i] = (short)r; - } -} -MA_API void ma_dr_wav_alaw_to_s16(ma_int16* pOut, const ma_uint8* pIn, size_t sampleCount) -{ - size_t i; - for (i = 0; i < sampleCount; ++i) { - pOut[i] = ma_dr_wav__alaw_to_s16(pIn[i]); - } -} -MA_API void ma_dr_wav_mulaw_to_s16(ma_int16* pOut, const ma_uint8* pIn, size_t sampleCount) -{ - size_t i; - for (i = 0; i < sampleCount; ++i) { - pOut[i] = ma_dr_wav__mulaw_to_s16(pIn[i]); - } -} -MA_PRIVATE void ma_dr_wav__pcm_to_f32(float* pOut, const ma_uint8* pIn, size_t sampleCount, unsigned int bytesPerSample) -{ - unsigned int i; - if (bytesPerSample == 1) { - ma_dr_wav_u8_to_f32(pOut, pIn, sampleCount); - return; - } - if (bytesPerSample == 2) { - ma_dr_wav_s16_to_f32(pOut, (const ma_int16*)pIn, sampleCount); - return; - } - if (bytesPerSample == 3) { - ma_dr_wav_s24_to_f32(pOut, pIn, sampleCount); - return; - } - if (bytesPerSample == 4) { - ma_dr_wav_s32_to_f32(pOut, (const ma_int32*)pIn, sampleCount); - return; - } - if (bytesPerSample > 8) { - MA_DR_WAV_ZERO_MEMORY(pOut, sampleCount * sizeof(*pOut)); - return; - } - for (i = 0; i < sampleCount; ++i) { - ma_uint64 sample = 0; - unsigned int shift = (8 - bytesPerSample) * 8; - unsigned int j; - for (j = 0; j < bytesPerSample; j += 1) { - MA_DR_WAV_ASSERT(j < 8); - sample |= (ma_uint64)(pIn[j]) << shift; - shift += 8; - } - pIn += j; - *pOut++ = (float)((ma_int64)sample / 9223372036854775807.0); - } -} -MA_PRIVATE void ma_dr_wav__ieee_to_f32(float* pOut, const ma_uint8* pIn, size_t sampleCount, unsigned int bytesPerSample) -{ - if (bytesPerSample == 4) { - unsigned int i; - for (i = 0; i < sampleCount; ++i) { - *pOut++ = ((const float*)pIn)[i]; - } - return; - } else if (bytesPerSample == 8) { - ma_dr_wav_f64_to_f32(pOut, (const double*)pIn, sampleCount); - return; - } else { - MA_DR_WAV_ZERO_MEMORY(pOut, sampleCount * sizeof(*pOut)); - return; - } -} -MA_PRIVATE ma_uint64 ma_dr_wav_read_pcm_frames_f32__pcm(ma_dr_wav* pWav, ma_uint64 framesToRead, float* pBufferOut) -{ - ma_uint64 totalFramesRead; - ma_uint8 sampleData[4096] = {0}; - ma_uint32 bytesPerFrame; - ma_uint32 bytesPerSample; - ma_uint64 samplesRead; - bytesPerFrame = ma_dr_wav_get_bytes_per_pcm_frame(pWav); - if (bytesPerFrame == 0) { - return 0; - } - bytesPerSample = bytesPerFrame / pWav->channels; - if (bytesPerSample == 0 || (bytesPerFrame % pWav->channels) != 0) { - return 0; - } - totalFramesRead = 0; - while (framesToRead > 0) { - ma_uint64 framesToReadThisIteration = ma_dr_wav_min(framesToRead, sizeof(sampleData)/bytesPerFrame); - ma_uint64 framesRead = ma_dr_wav_read_pcm_frames(pWav, framesToReadThisIteration, sampleData); - if (framesRead == 0) { - break; - } - MA_DR_WAV_ASSERT(framesRead <= framesToReadThisIteration); - samplesRead = framesRead * pWav->channels; - if ((samplesRead * bytesPerSample) > sizeof(sampleData)) { - MA_DR_WAV_ASSERT(MA_FALSE); - break; - } - ma_dr_wav__pcm_to_f32(pBufferOut, sampleData, (size_t)samplesRead, bytesPerSample); - pBufferOut += samplesRead; - framesToRead -= framesRead; - totalFramesRead += framesRead; - } - return totalFramesRead; -} -MA_PRIVATE ma_uint64 ma_dr_wav_read_pcm_frames_f32__msadpcm_ima(ma_dr_wav* pWav, ma_uint64 framesToRead, float* pBufferOut) -{ - ma_uint64 totalFramesRead; - ma_int16 samples16[2048]; - totalFramesRead = 0; - while (framesToRead > 0) { - ma_uint64 framesToReadThisIteration = ma_dr_wav_min(framesToRead, ma_dr_wav_countof(samples16)/pWav->channels); - ma_uint64 framesRead = ma_dr_wav_read_pcm_frames_s16(pWav, framesToReadThisIteration, samples16); - if (framesRead == 0) { - break; - } - MA_DR_WAV_ASSERT(framesRead <= framesToReadThisIteration); - ma_dr_wav_s16_to_f32(pBufferOut, samples16, (size_t)(framesRead*pWav->channels)); - pBufferOut += framesRead*pWav->channels; - framesToRead -= framesRead; - totalFramesRead += framesRead; - } - return totalFramesRead; -} -MA_PRIVATE ma_uint64 ma_dr_wav_read_pcm_frames_f32__ieee(ma_dr_wav* pWav, ma_uint64 framesToRead, float* pBufferOut) -{ - ma_uint64 totalFramesRead; - ma_uint8 sampleData[4096] = {0}; - ma_uint32 bytesPerFrame; - ma_uint32 bytesPerSample; - ma_uint64 samplesRead; - if (pWav->translatedFormatTag == MA_DR_WAVE_FORMAT_IEEE_FLOAT && pWav->bitsPerSample == 32) { - return ma_dr_wav_read_pcm_frames(pWav, framesToRead, pBufferOut); - } - bytesPerFrame = ma_dr_wav_get_bytes_per_pcm_frame(pWav); - if (bytesPerFrame == 0) { - return 0; - } - bytesPerSample = bytesPerFrame / pWav->channels; - if (bytesPerSample == 0 || (bytesPerFrame % pWav->channels) != 0) { - return 0; - } - totalFramesRead = 0; - while (framesToRead > 0) { - ma_uint64 framesToReadThisIteration = ma_dr_wav_min(framesToRead, sizeof(sampleData)/bytesPerFrame); - ma_uint64 framesRead = ma_dr_wav_read_pcm_frames(pWav, framesToReadThisIteration, sampleData); - if (framesRead == 0) { - break; - } - MA_DR_WAV_ASSERT(framesRead <= framesToReadThisIteration); - samplesRead = framesRead * pWav->channels; - if ((samplesRead * bytesPerSample) > sizeof(sampleData)) { - MA_DR_WAV_ASSERT(MA_FALSE); - break; - } - ma_dr_wav__ieee_to_f32(pBufferOut, sampleData, (size_t)samplesRead, bytesPerSample); - pBufferOut += samplesRead; - framesToRead -= framesRead; - totalFramesRead += framesRead; - } - return totalFramesRead; -} -MA_PRIVATE ma_uint64 ma_dr_wav_read_pcm_frames_f32__alaw(ma_dr_wav* pWav, ma_uint64 framesToRead, float* pBufferOut) -{ - ma_uint64 totalFramesRead; - ma_uint8 sampleData[4096] = {0}; - ma_uint32 bytesPerFrame; - ma_uint32 bytesPerSample; - ma_uint64 samplesRead; - bytesPerFrame = ma_dr_wav_get_bytes_per_pcm_frame(pWav); - if (bytesPerFrame == 0) { - return 0; - } - bytesPerSample = bytesPerFrame / pWav->channels; - if (bytesPerSample == 0 || (bytesPerFrame % pWav->channels) != 0) { - return 0; - } - totalFramesRead = 0; - while (framesToRead > 0) { - ma_uint64 framesToReadThisIteration = ma_dr_wav_min(framesToRead, sizeof(sampleData)/bytesPerFrame); - ma_uint64 framesRead = ma_dr_wav_read_pcm_frames(pWav, framesToReadThisIteration, sampleData); - if (framesRead == 0) { - break; - } - MA_DR_WAV_ASSERT(framesRead <= framesToReadThisIteration); - samplesRead = framesRead * pWav->channels; - if ((samplesRead * bytesPerSample) > sizeof(sampleData)) { - MA_DR_WAV_ASSERT(MA_FALSE); - break; - } - ma_dr_wav_alaw_to_f32(pBufferOut, sampleData, (size_t)samplesRead); - #ifdef MA_DR_WAV_LIBSNDFILE_COMPAT - { - if (pWav->container == ma_dr_wav_container_aiff) { - ma_uint64 iSample; - for (iSample = 0; iSample < samplesRead; iSample += 1) { - pBufferOut[iSample] = -pBufferOut[iSample]; - } - } - } - #endif - pBufferOut += samplesRead; - framesToRead -= framesRead; - totalFramesRead += framesRead; - } - return totalFramesRead; -} -MA_PRIVATE ma_uint64 ma_dr_wav_read_pcm_frames_f32__mulaw(ma_dr_wav* pWav, ma_uint64 framesToRead, float* pBufferOut) -{ - ma_uint64 totalFramesRead; - ma_uint8 sampleData[4096] = {0}; - ma_uint32 bytesPerFrame; - ma_uint32 bytesPerSample; - ma_uint64 samplesRead; - bytesPerFrame = ma_dr_wav_get_bytes_per_pcm_frame(pWav); - if (bytesPerFrame == 0) { - return 0; - } - bytesPerSample = bytesPerFrame / pWav->channels; - if (bytesPerSample == 0 || (bytesPerFrame % pWav->channels) != 0) { - return 0; - } - totalFramesRead = 0; - while (framesToRead > 0) { - ma_uint64 framesToReadThisIteration = ma_dr_wav_min(framesToRead, sizeof(sampleData)/bytesPerFrame); - ma_uint64 framesRead = ma_dr_wav_read_pcm_frames(pWav, framesToReadThisIteration, sampleData); - if (framesRead == 0) { - break; - } - MA_DR_WAV_ASSERT(framesRead <= framesToReadThisIteration); - samplesRead = framesRead * pWav->channels; - if ((samplesRead * bytesPerSample) > sizeof(sampleData)) { - MA_DR_WAV_ASSERT(MA_FALSE); - break; - } - ma_dr_wav_mulaw_to_f32(pBufferOut, sampleData, (size_t)samplesRead); - #ifdef MA_DR_WAV_LIBSNDFILE_COMPAT - { - if (pWav->container == ma_dr_wav_container_aiff) { - ma_uint64 iSample; - for (iSample = 0; iSample < samplesRead; iSample += 1) { - pBufferOut[iSample] = -pBufferOut[iSample]; - } - } - } - #endif - pBufferOut += samplesRead; - framesToRead -= framesRead; - totalFramesRead += framesRead; - } - return totalFramesRead; -} -MA_API ma_uint64 ma_dr_wav_read_pcm_frames_f32(ma_dr_wav* pWav, ma_uint64 framesToRead, float* pBufferOut) -{ - if (pWav == NULL || framesToRead == 0) { - return 0; - } - if (pBufferOut == NULL) { - return ma_dr_wav_read_pcm_frames(pWav, framesToRead, NULL); - } - if (framesToRead * pWav->channels * sizeof(float) > MA_SIZE_MAX) { - framesToRead = MA_SIZE_MAX / sizeof(float) / pWav->channels; - } - if (pWav->translatedFormatTag == MA_DR_WAVE_FORMAT_PCM) { - return ma_dr_wav_read_pcm_frames_f32__pcm(pWav, framesToRead, pBufferOut); - } - if (pWav->translatedFormatTag == MA_DR_WAVE_FORMAT_ADPCM || pWav->translatedFormatTag == MA_DR_WAVE_FORMAT_DVI_ADPCM) { - return ma_dr_wav_read_pcm_frames_f32__msadpcm_ima(pWav, framesToRead, pBufferOut); - } - if (pWav->translatedFormatTag == MA_DR_WAVE_FORMAT_IEEE_FLOAT) { - return ma_dr_wav_read_pcm_frames_f32__ieee(pWav, framesToRead, pBufferOut); - } - if (pWav->translatedFormatTag == MA_DR_WAVE_FORMAT_ALAW) { - return ma_dr_wav_read_pcm_frames_f32__alaw(pWav, framesToRead, pBufferOut); - } - if (pWav->translatedFormatTag == MA_DR_WAVE_FORMAT_MULAW) { - return ma_dr_wav_read_pcm_frames_f32__mulaw(pWav, framesToRead, pBufferOut); - } - return 0; -} -MA_API ma_uint64 ma_dr_wav_read_pcm_frames_f32le(ma_dr_wav* pWav, ma_uint64 framesToRead, float* pBufferOut) -{ - ma_uint64 framesRead = ma_dr_wav_read_pcm_frames_f32(pWav, framesToRead, pBufferOut); - if (pBufferOut != NULL && ma_dr_wav__is_little_endian() == MA_FALSE) { - ma_dr_wav__bswap_samples_f32(pBufferOut, framesRead*pWav->channels); - } - return framesRead; -} -MA_API ma_uint64 ma_dr_wav_read_pcm_frames_f32be(ma_dr_wav* pWav, ma_uint64 framesToRead, float* pBufferOut) -{ - ma_uint64 framesRead = ma_dr_wav_read_pcm_frames_f32(pWav, framesToRead, pBufferOut); - if (pBufferOut != NULL && ma_dr_wav__is_little_endian() == MA_TRUE) { - ma_dr_wav__bswap_samples_f32(pBufferOut, framesRead*pWav->channels); - } - return framesRead; -} -MA_API void ma_dr_wav_u8_to_f32(float* pOut, const ma_uint8* pIn, size_t sampleCount) -{ - size_t i; - if (pOut == NULL || pIn == NULL) { - return; - } -#ifdef MA_DR_WAV_LIBSNDFILE_COMPAT - for (i = 0; i < sampleCount; ++i) { - *pOut++ = (pIn[i] / 256.0f) * 2 - 1; - } -#else - for (i = 0; i < sampleCount; ++i) { - float x = pIn[i]; - x = x * 0.00784313725490196078f; - x = x - 1; - *pOut++ = x; - } -#endif -} -MA_API void ma_dr_wav_s16_to_f32(float* pOut, const ma_int16* pIn, size_t sampleCount) -{ - size_t i; - if (pOut == NULL || pIn == NULL) { - return; - } - for (i = 0; i < sampleCount; ++i) { - *pOut++ = pIn[i] * 0.000030517578125f; - } -} -MA_API void ma_dr_wav_s24_to_f32(float* pOut, const ma_uint8* pIn, size_t sampleCount) -{ - size_t i; - if (pOut == NULL || pIn == NULL) { - return; - } - for (i = 0; i < sampleCount; ++i) { - double x; - ma_uint32 a = ((ma_uint32)(pIn[i*3+0]) << 8); - ma_uint32 b = ((ma_uint32)(pIn[i*3+1]) << 16); - ma_uint32 c = ((ma_uint32)(pIn[i*3+2]) << 24); - x = (double)((ma_int32)(a | b | c) >> 8); - *pOut++ = (float)(x * 0.00000011920928955078125); - } -} -MA_API void ma_dr_wav_s32_to_f32(float* pOut, const ma_int32* pIn, size_t sampleCount) -{ - size_t i; - if (pOut == NULL || pIn == NULL) { - return; - } - for (i = 0; i < sampleCount; ++i) { - *pOut++ = (float)(pIn[i] / 2147483648.0); - } -} -MA_API void ma_dr_wav_f64_to_f32(float* pOut, const double* pIn, size_t sampleCount) -{ - size_t i; - if (pOut == NULL || pIn == NULL) { - return; - } - for (i = 0; i < sampleCount; ++i) { - *pOut++ = (float)pIn[i]; - } -} -MA_API void ma_dr_wav_alaw_to_f32(float* pOut, const ma_uint8* pIn, size_t sampleCount) -{ - size_t i; - if (pOut == NULL || pIn == NULL) { - return; - } - for (i = 0; i < sampleCount; ++i) { - *pOut++ = ma_dr_wav__alaw_to_s16(pIn[i]) / 32768.0f; - } -} -MA_API void ma_dr_wav_mulaw_to_f32(float* pOut, const ma_uint8* pIn, size_t sampleCount) -{ - size_t i; - if (pOut == NULL || pIn == NULL) { - return; - } - for (i = 0; i < sampleCount; ++i) { - *pOut++ = ma_dr_wav__mulaw_to_s16(pIn[i]) / 32768.0f; - } -} -MA_PRIVATE void ma_dr_wav__pcm_to_s32(ma_int32* pOut, const ma_uint8* pIn, size_t totalSampleCount, unsigned int bytesPerSample) -{ - unsigned int i; - if (bytesPerSample == 1) { - ma_dr_wav_u8_to_s32(pOut, pIn, totalSampleCount); - return; - } - if (bytesPerSample == 2) { - ma_dr_wav_s16_to_s32(pOut, (const ma_int16*)pIn, totalSampleCount); - return; - } - if (bytesPerSample == 3) { - ma_dr_wav_s24_to_s32(pOut, pIn, totalSampleCount); - return; - } - if (bytesPerSample == 4) { - for (i = 0; i < totalSampleCount; ++i) { - *pOut++ = ((const ma_int32*)pIn)[i]; - } - return; - } - if (bytesPerSample > 8) { - MA_DR_WAV_ZERO_MEMORY(pOut, totalSampleCount * sizeof(*pOut)); - return; - } - for (i = 0; i < totalSampleCount; ++i) { - ma_uint64 sample = 0; - unsigned int shift = (8 - bytesPerSample) * 8; - unsigned int j; - for (j = 0; j < bytesPerSample; j += 1) { - MA_DR_WAV_ASSERT(j < 8); - sample |= (ma_uint64)(pIn[j]) << shift; - shift += 8; - } - pIn += j; - *pOut++ = (ma_int32)((ma_int64)sample >> 32); - } -} -MA_PRIVATE void ma_dr_wav__ieee_to_s32(ma_int32* pOut, const ma_uint8* pIn, size_t totalSampleCount, unsigned int bytesPerSample) -{ - if (bytesPerSample == 4) { - ma_dr_wav_f32_to_s32(pOut, (const float*)pIn, totalSampleCount); - return; - } else if (bytesPerSample == 8) { - ma_dr_wav_f64_to_s32(pOut, (const double*)pIn, totalSampleCount); - return; - } else { - MA_DR_WAV_ZERO_MEMORY(pOut, totalSampleCount * sizeof(*pOut)); - return; - } -} -MA_PRIVATE ma_uint64 ma_dr_wav_read_pcm_frames_s32__pcm(ma_dr_wav* pWav, ma_uint64 framesToRead, ma_int32* pBufferOut) -{ - ma_uint64 totalFramesRead; - ma_uint8 sampleData[4096] = {0}; - ma_uint32 bytesPerFrame; - ma_uint32 bytesPerSample; - ma_uint64 samplesRead; - if (pWav->translatedFormatTag == MA_DR_WAVE_FORMAT_PCM && pWav->bitsPerSample == 32) { - return ma_dr_wav_read_pcm_frames(pWav, framesToRead, pBufferOut); - } - bytesPerFrame = ma_dr_wav_get_bytes_per_pcm_frame(pWav); - if (bytesPerFrame == 0) { - return 0; - } - bytesPerSample = bytesPerFrame / pWav->channels; - if (bytesPerSample == 0 || (bytesPerFrame % pWav->channels) != 0) { - return 0; - } - totalFramesRead = 0; - while (framesToRead > 0) { - ma_uint64 framesToReadThisIteration = ma_dr_wav_min(framesToRead, sizeof(sampleData)/bytesPerFrame); - ma_uint64 framesRead = ma_dr_wav_read_pcm_frames(pWav, framesToReadThisIteration, sampleData); - if (framesRead == 0) { - break; - } - MA_DR_WAV_ASSERT(framesRead <= framesToReadThisIteration); - samplesRead = framesRead * pWav->channels; - if ((samplesRead * bytesPerSample) > sizeof(sampleData)) { - MA_DR_WAV_ASSERT(MA_FALSE); - break; - } - ma_dr_wav__pcm_to_s32(pBufferOut, sampleData, (size_t)samplesRead, bytesPerSample); - pBufferOut += samplesRead; - framesToRead -= framesRead; - totalFramesRead += framesRead; - } - return totalFramesRead; -} -MA_PRIVATE ma_uint64 ma_dr_wav_read_pcm_frames_s32__msadpcm_ima(ma_dr_wav* pWav, ma_uint64 framesToRead, ma_int32* pBufferOut) -{ - ma_uint64 totalFramesRead = 0; - ma_int16 samples16[2048]; - while (framesToRead > 0) { - ma_uint64 framesToReadThisIteration = ma_dr_wav_min(framesToRead, ma_dr_wav_countof(samples16)/pWav->channels); - ma_uint64 framesRead = ma_dr_wav_read_pcm_frames_s16(pWav, framesToReadThisIteration, samples16); - if (framesRead == 0) { - break; - } - MA_DR_WAV_ASSERT(framesRead <= framesToReadThisIteration); - ma_dr_wav_s16_to_s32(pBufferOut, samples16, (size_t)(framesRead*pWav->channels)); - pBufferOut += framesRead*pWav->channels; - framesToRead -= framesRead; - totalFramesRead += framesRead; - } - return totalFramesRead; -} -MA_PRIVATE ma_uint64 ma_dr_wav_read_pcm_frames_s32__ieee(ma_dr_wav* pWav, ma_uint64 framesToRead, ma_int32* pBufferOut) -{ - ma_uint64 totalFramesRead; - ma_uint8 sampleData[4096] = {0}; - ma_uint32 bytesPerFrame; - ma_uint32 bytesPerSample; - ma_uint64 samplesRead; - bytesPerFrame = ma_dr_wav_get_bytes_per_pcm_frame(pWav); - if (bytesPerFrame == 0) { - return 0; - } - bytesPerSample = bytesPerFrame / pWav->channels; - if (bytesPerSample == 0 || (bytesPerFrame % pWav->channels) != 0) { - return 0; - } - totalFramesRead = 0; - while (framesToRead > 0) { - ma_uint64 framesToReadThisIteration = ma_dr_wav_min(framesToRead, sizeof(sampleData)/bytesPerFrame); - ma_uint64 framesRead = ma_dr_wav_read_pcm_frames(pWav, framesToReadThisIteration, sampleData); - if (framesRead == 0) { - break; - } - MA_DR_WAV_ASSERT(framesRead <= framesToReadThisIteration); - samplesRead = framesRead * pWav->channels; - if ((samplesRead * bytesPerSample) > sizeof(sampleData)) { - MA_DR_WAV_ASSERT(MA_FALSE); - break; - } - ma_dr_wav__ieee_to_s32(pBufferOut, sampleData, (size_t)samplesRead, bytesPerSample); - pBufferOut += samplesRead; - framesToRead -= framesRead; - totalFramesRead += framesRead; - } - return totalFramesRead; -} -MA_PRIVATE ma_uint64 ma_dr_wav_read_pcm_frames_s32__alaw(ma_dr_wav* pWav, ma_uint64 framesToRead, ma_int32* pBufferOut) -{ - ma_uint64 totalFramesRead; - ma_uint8 sampleData[4096] = {0}; - ma_uint32 bytesPerFrame; - ma_uint32 bytesPerSample; - ma_uint64 samplesRead; - bytesPerFrame = ma_dr_wav_get_bytes_per_pcm_frame(pWav); - if (bytesPerFrame == 0) { - return 0; - } - bytesPerSample = bytesPerFrame / pWav->channels; - if (bytesPerSample == 0 || (bytesPerFrame % pWav->channels) != 0) { - return 0; - } - totalFramesRead = 0; - while (framesToRead > 0) { - ma_uint64 framesToReadThisIteration = ma_dr_wav_min(framesToRead, sizeof(sampleData)/bytesPerFrame); - ma_uint64 framesRead = ma_dr_wav_read_pcm_frames(pWav, framesToReadThisIteration, sampleData); - if (framesRead == 0) { - break; - } - MA_DR_WAV_ASSERT(framesRead <= framesToReadThisIteration); - samplesRead = framesRead * pWav->channels; - if ((samplesRead * bytesPerSample) > sizeof(sampleData)) { - MA_DR_WAV_ASSERT(MA_FALSE); - break; - } - ma_dr_wav_alaw_to_s32(pBufferOut, sampleData, (size_t)samplesRead); - #ifdef MA_DR_WAV_LIBSNDFILE_COMPAT - { - if (pWav->container == ma_dr_wav_container_aiff) { - ma_uint64 iSample; - for (iSample = 0; iSample < samplesRead; iSample += 1) { - pBufferOut[iSample] = -pBufferOut[iSample]; - } - } - } - #endif - pBufferOut += samplesRead; - framesToRead -= framesRead; - totalFramesRead += framesRead; - } - return totalFramesRead; -} -MA_PRIVATE ma_uint64 ma_dr_wav_read_pcm_frames_s32__mulaw(ma_dr_wav* pWav, ma_uint64 framesToRead, ma_int32* pBufferOut) -{ - ma_uint64 totalFramesRead; - ma_uint8 sampleData[4096] = {0}; - ma_uint32 bytesPerFrame; - ma_uint32 bytesPerSample; - ma_uint64 samplesRead; - bytesPerFrame = ma_dr_wav_get_bytes_per_pcm_frame(pWav); - if (bytesPerFrame == 0) { - return 0; - } - bytesPerSample = bytesPerFrame / pWav->channels; - if (bytesPerSample == 0 || (bytesPerFrame % pWav->channels) != 0) { - return 0; - } - totalFramesRead = 0; - while (framesToRead > 0) { - ma_uint64 framesToReadThisIteration = ma_dr_wav_min(framesToRead, sizeof(sampleData)/bytesPerFrame); - ma_uint64 framesRead = ma_dr_wav_read_pcm_frames(pWav, framesToReadThisIteration, sampleData); - if (framesRead == 0) { - break; - } - MA_DR_WAV_ASSERT(framesRead <= framesToReadThisIteration); - samplesRead = framesRead * pWav->channels; - if ((samplesRead * bytesPerSample) > sizeof(sampleData)) { - MA_DR_WAV_ASSERT(MA_FALSE); - break; - } - ma_dr_wav_mulaw_to_s32(pBufferOut, sampleData, (size_t)samplesRead); - #ifdef MA_DR_WAV_LIBSNDFILE_COMPAT - { - if (pWav->container == ma_dr_wav_container_aiff) { - ma_uint64 iSample; - for (iSample = 0; iSample < samplesRead; iSample += 1) { - pBufferOut[iSample] = -pBufferOut[iSample]; - } - } - } - #endif - pBufferOut += samplesRead; - framesToRead -= framesRead; - totalFramesRead += framesRead; - } - return totalFramesRead; -} -MA_API ma_uint64 ma_dr_wav_read_pcm_frames_s32(ma_dr_wav* pWav, ma_uint64 framesToRead, ma_int32* pBufferOut) -{ - if (pWav == NULL || framesToRead == 0) { - return 0; - } - if (pBufferOut == NULL) { - return ma_dr_wav_read_pcm_frames(pWav, framesToRead, NULL); - } - if (framesToRead * pWav->channels * sizeof(ma_int32) > MA_SIZE_MAX) { - framesToRead = MA_SIZE_MAX / sizeof(ma_int32) / pWav->channels; - } - if (pWav->translatedFormatTag == MA_DR_WAVE_FORMAT_PCM) { - return ma_dr_wav_read_pcm_frames_s32__pcm(pWav, framesToRead, pBufferOut); - } - if (pWav->translatedFormatTag == MA_DR_WAVE_FORMAT_ADPCM || pWav->translatedFormatTag == MA_DR_WAVE_FORMAT_DVI_ADPCM) { - return ma_dr_wav_read_pcm_frames_s32__msadpcm_ima(pWav, framesToRead, pBufferOut); - } - if (pWav->translatedFormatTag == MA_DR_WAVE_FORMAT_IEEE_FLOAT) { - return ma_dr_wav_read_pcm_frames_s32__ieee(pWav, framesToRead, pBufferOut); - } - if (pWav->translatedFormatTag == MA_DR_WAVE_FORMAT_ALAW) { - return ma_dr_wav_read_pcm_frames_s32__alaw(pWav, framesToRead, pBufferOut); - } - if (pWav->translatedFormatTag == MA_DR_WAVE_FORMAT_MULAW) { - return ma_dr_wav_read_pcm_frames_s32__mulaw(pWav, framesToRead, pBufferOut); - } - return 0; -} -MA_API ma_uint64 ma_dr_wav_read_pcm_frames_s32le(ma_dr_wav* pWav, ma_uint64 framesToRead, ma_int32* pBufferOut) -{ - ma_uint64 framesRead = ma_dr_wav_read_pcm_frames_s32(pWav, framesToRead, pBufferOut); - if (pBufferOut != NULL && ma_dr_wav__is_little_endian() == MA_FALSE) { - ma_dr_wav__bswap_samples_s32(pBufferOut, framesRead*pWav->channels); - } - return framesRead; -} -MA_API ma_uint64 ma_dr_wav_read_pcm_frames_s32be(ma_dr_wav* pWav, ma_uint64 framesToRead, ma_int32* pBufferOut) -{ - ma_uint64 framesRead = ma_dr_wav_read_pcm_frames_s32(pWav, framesToRead, pBufferOut); - if (pBufferOut != NULL && ma_dr_wav__is_little_endian() == MA_TRUE) { - ma_dr_wav__bswap_samples_s32(pBufferOut, framesRead*pWav->channels); - } - return framesRead; -} -MA_API void ma_dr_wav_u8_to_s32(ma_int32* pOut, const ma_uint8* pIn, size_t sampleCount) -{ - size_t i; - if (pOut == NULL || pIn == NULL) { - return; - } - for (i = 0; i < sampleCount; ++i) { - *pOut++ = ((int)pIn[i] - 128) << 24; - } -} -MA_API void ma_dr_wav_s16_to_s32(ma_int32* pOut, const ma_int16* pIn, size_t sampleCount) -{ - size_t i; - if (pOut == NULL || pIn == NULL) { - return; - } - for (i = 0; i < sampleCount; ++i) { - *pOut++ = pIn[i] << 16; - } -} -MA_API void ma_dr_wav_s24_to_s32(ma_int32* pOut, const ma_uint8* pIn, size_t sampleCount) -{ - size_t i; - if (pOut == NULL || pIn == NULL) { - return; - } - for (i = 0; i < sampleCount; ++i) { - unsigned int s0 = pIn[i*3 + 0]; - unsigned int s1 = pIn[i*3 + 1]; - unsigned int s2 = pIn[i*3 + 2]; - ma_int32 sample32 = (ma_int32)((s0 << 8) | (s1 << 16) | (s2 << 24)); - *pOut++ = sample32; - } -} -MA_API void ma_dr_wav_f32_to_s32(ma_int32* pOut, const float* pIn, size_t sampleCount) -{ - size_t i; - if (pOut == NULL || pIn == NULL) { - return; - } - for (i = 0; i < sampleCount; ++i) { - *pOut++ = (ma_int32)(2147483648.0f * pIn[i]); - } -} -MA_API void ma_dr_wav_f64_to_s32(ma_int32* pOut, const double* pIn, size_t sampleCount) -{ - size_t i; - if (pOut == NULL || pIn == NULL) { - return; - } - for (i = 0; i < sampleCount; ++i) { - *pOut++ = (ma_int32)(2147483648.0 * pIn[i]); - } -} -MA_API void ma_dr_wav_alaw_to_s32(ma_int32* pOut, const ma_uint8* pIn, size_t sampleCount) -{ - size_t i; - if (pOut == NULL || pIn == NULL) { - return; - } - for (i = 0; i < sampleCount; ++i) { - *pOut++ = ((ma_int32)ma_dr_wav__alaw_to_s16(pIn[i])) << 16; - } -} -MA_API void ma_dr_wav_mulaw_to_s32(ma_int32* pOut, const ma_uint8* pIn, size_t sampleCount) -{ - size_t i; - if (pOut == NULL || pIn == NULL) { - return; - } - for (i= 0; i < sampleCount; ++i) { - *pOut++ = ((ma_int32)ma_dr_wav__mulaw_to_s16(pIn[i])) << 16; - } -} -MA_PRIVATE ma_int16* ma_dr_wav__read_pcm_frames_and_close_s16(ma_dr_wav* pWav, unsigned int* channels, unsigned int* sampleRate, ma_uint64* totalFrameCount) -{ - ma_uint64 sampleDataSize; - ma_int16* pSampleData; - ma_uint64 framesRead; - MA_DR_WAV_ASSERT(pWav != NULL); - sampleDataSize = pWav->totalPCMFrameCount * pWav->channels * sizeof(ma_int16); - if (sampleDataSize > MA_SIZE_MAX) { - ma_dr_wav_uninit(pWav); - return NULL; - } - pSampleData = (ma_int16*)ma_dr_wav__malloc_from_callbacks((size_t)sampleDataSize, &pWav->allocationCallbacks); - if (pSampleData == NULL) { - ma_dr_wav_uninit(pWav); - return NULL; - } - framesRead = ma_dr_wav_read_pcm_frames_s16(pWav, (size_t)pWav->totalPCMFrameCount, pSampleData); - if (framesRead != pWav->totalPCMFrameCount) { - ma_dr_wav__free_from_callbacks(pSampleData, &pWav->allocationCallbacks); - ma_dr_wav_uninit(pWav); - return NULL; - } - ma_dr_wav_uninit(pWav); - if (sampleRate) { - *sampleRate = pWav->sampleRate; - } - if (channels) { - *channels = pWav->channels; - } - if (totalFrameCount) { - *totalFrameCount = pWav->totalPCMFrameCount; - } - return pSampleData; -} -MA_PRIVATE float* ma_dr_wav__read_pcm_frames_and_close_f32(ma_dr_wav* pWav, unsigned int* channels, unsigned int* sampleRate, ma_uint64* totalFrameCount) -{ - ma_uint64 sampleDataSize; - float* pSampleData; - ma_uint64 framesRead; - MA_DR_WAV_ASSERT(pWav != NULL); - sampleDataSize = pWav->totalPCMFrameCount * pWav->channels * sizeof(float); - if (sampleDataSize > MA_SIZE_MAX) { - ma_dr_wav_uninit(pWav); - return NULL; - } - pSampleData = (float*)ma_dr_wav__malloc_from_callbacks((size_t)sampleDataSize, &pWav->allocationCallbacks); - if (pSampleData == NULL) { - ma_dr_wav_uninit(pWav); - return NULL; - } - framesRead = ma_dr_wav_read_pcm_frames_f32(pWav, (size_t)pWav->totalPCMFrameCount, pSampleData); - if (framesRead != pWav->totalPCMFrameCount) { - ma_dr_wav__free_from_callbacks(pSampleData, &pWav->allocationCallbacks); - ma_dr_wav_uninit(pWav); - return NULL; - } - ma_dr_wav_uninit(pWav); - if (sampleRate) { - *sampleRate = pWav->sampleRate; - } - if (channels) { - *channels = pWav->channels; - } - if (totalFrameCount) { - *totalFrameCount = pWav->totalPCMFrameCount; - } - return pSampleData; -} -MA_PRIVATE ma_int32* ma_dr_wav__read_pcm_frames_and_close_s32(ma_dr_wav* pWav, unsigned int* channels, unsigned int* sampleRate, ma_uint64* totalFrameCount) -{ - ma_uint64 sampleDataSize; - ma_int32* pSampleData; - ma_uint64 framesRead; - MA_DR_WAV_ASSERT(pWav != NULL); - sampleDataSize = pWav->totalPCMFrameCount * pWav->channels * sizeof(ma_int32); - if (sampleDataSize > MA_SIZE_MAX) { - ma_dr_wav_uninit(pWav); - return NULL; - } - pSampleData = (ma_int32*)ma_dr_wav__malloc_from_callbacks((size_t)sampleDataSize, &pWav->allocationCallbacks); - if (pSampleData == NULL) { - ma_dr_wav_uninit(pWav); - return NULL; - } - framesRead = ma_dr_wav_read_pcm_frames_s32(pWav, (size_t)pWav->totalPCMFrameCount, pSampleData); - if (framesRead != pWav->totalPCMFrameCount) { - ma_dr_wav__free_from_callbacks(pSampleData, &pWav->allocationCallbacks); - ma_dr_wav_uninit(pWav); - return NULL; - } - ma_dr_wav_uninit(pWav); - if (sampleRate) { - *sampleRate = pWav->sampleRate; - } - if (channels) { - *channels = pWav->channels; - } - if (totalFrameCount) { - *totalFrameCount = pWav->totalPCMFrameCount; - } - return pSampleData; -} -MA_API ma_int16* ma_dr_wav_open_and_read_pcm_frames_s16(ma_dr_wav_read_proc onRead, ma_dr_wav_seek_proc onSeek, void* pUserData, unsigned int* channelsOut, unsigned int* sampleRateOut, ma_uint64* totalFrameCountOut, const ma_allocation_callbacks* pAllocationCallbacks) -{ - ma_dr_wav wav; - if (channelsOut) { - *channelsOut = 0; - } - if (sampleRateOut) { - *sampleRateOut = 0; - } - if (totalFrameCountOut) { - *totalFrameCountOut = 0; - } - if (!ma_dr_wav_init(&wav, onRead, onSeek, pUserData, pAllocationCallbacks)) { - return NULL; - } - return ma_dr_wav__read_pcm_frames_and_close_s16(&wav, channelsOut, sampleRateOut, totalFrameCountOut); -} -MA_API float* ma_dr_wav_open_and_read_pcm_frames_f32(ma_dr_wav_read_proc onRead, ma_dr_wav_seek_proc onSeek, void* pUserData, unsigned int* channelsOut, unsigned int* sampleRateOut, ma_uint64* totalFrameCountOut, const ma_allocation_callbacks* pAllocationCallbacks) -{ - ma_dr_wav wav; - if (channelsOut) { - *channelsOut = 0; - } - if (sampleRateOut) { - *sampleRateOut = 0; - } - if (totalFrameCountOut) { - *totalFrameCountOut = 0; - } - if (!ma_dr_wav_init(&wav, onRead, onSeek, pUserData, pAllocationCallbacks)) { - return NULL; - } - return ma_dr_wav__read_pcm_frames_and_close_f32(&wav, channelsOut, sampleRateOut, totalFrameCountOut); -} -MA_API ma_int32* ma_dr_wav_open_and_read_pcm_frames_s32(ma_dr_wav_read_proc onRead, ma_dr_wav_seek_proc onSeek, void* pUserData, unsigned int* channelsOut, unsigned int* sampleRateOut, ma_uint64* totalFrameCountOut, const ma_allocation_callbacks* pAllocationCallbacks) -{ - ma_dr_wav wav; - if (channelsOut) { - *channelsOut = 0; - } - if (sampleRateOut) { - *sampleRateOut = 0; - } - if (totalFrameCountOut) { - *totalFrameCountOut = 0; - } - if (!ma_dr_wav_init(&wav, onRead, onSeek, pUserData, pAllocationCallbacks)) { - return NULL; - } - return ma_dr_wav__read_pcm_frames_and_close_s32(&wav, channelsOut, sampleRateOut, totalFrameCountOut); -} -#ifndef MA_DR_WAV_NO_STDIO -MA_API ma_int16* ma_dr_wav_open_file_and_read_pcm_frames_s16(const char* filename, unsigned int* channelsOut, unsigned int* sampleRateOut, ma_uint64* totalFrameCountOut, const ma_allocation_callbacks* pAllocationCallbacks) -{ - ma_dr_wav wav; - if (channelsOut) { - *channelsOut = 0; - } - if (sampleRateOut) { - *sampleRateOut = 0; - } - if (totalFrameCountOut) { - *totalFrameCountOut = 0; - } - if (!ma_dr_wav_init_file(&wav, filename, pAllocationCallbacks)) { - return NULL; - } - return ma_dr_wav__read_pcm_frames_and_close_s16(&wav, channelsOut, sampleRateOut, totalFrameCountOut); -} -MA_API float* ma_dr_wav_open_file_and_read_pcm_frames_f32(const char* filename, unsigned int* channelsOut, unsigned int* sampleRateOut, ma_uint64* totalFrameCountOut, const ma_allocation_callbacks* pAllocationCallbacks) -{ - ma_dr_wav wav; - if (channelsOut) { - *channelsOut = 0; - } - if (sampleRateOut) { - *sampleRateOut = 0; - } - if (totalFrameCountOut) { - *totalFrameCountOut = 0; - } - if (!ma_dr_wav_init_file(&wav, filename, pAllocationCallbacks)) { - return NULL; - } - return ma_dr_wav__read_pcm_frames_and_close_f32(&wav, channelsOut, sampleRateOut, totalFrameCountOut); -} -MA_API ma_int32* ma_dr_wav_open_file_and_read_pcm_frames_s32(const char* filename, unsigned int* channelsOut, unsigned int* sampleRateOut, ma_uint64* totalFrameCountOut, const ma_allocation_callbacks* pAllocationCallbacks) -{ - ma_dr_wav wav; - if (channelsOut) { - *channelsOut = 0; - } - if (sampleRateOut) { - *sampleRateOut = 0; - } - if (totalFrameCountOut) { - *totalFrameCountOut = 0; - } - if (!ma_dr_wav_init_file(&wav, filename, pAllocationCallbacks)) { - return NULL; - } - return ma_dr_wav__read_pcm_frames_and_close_s32(&wav, channelsOut, sampleRateOut, totalFrameCountOut); -} -#ifndef MA_DR_WAV_NO_WCHAR -MA_API ma_int16* ma_dr_wav_open_file_and_read_pcm_frames_s16_w(const wchar_t* filename, unsigned int* channelsOut, unsigned int* sampleRateOut, ma_uint64* totalFrameCountOut, const ma_allocation_callbacks* pAllocationCallbacks) -{ - ma_dr_wav wav; - if (sampleRateOut) { - *sampleRateOut = 0; - } - if (channelsOut) { - *channelsOut = 0; - } - if (totalFrameCountOut) { - *totalFrameCountOut = 0; - } - if (!ma_dr_wav_init_file_w(&wav, filename, pAllocationCallbacks)) { - return NULL; - } - return ma_dr_wav__read_pcm_frames_and_close_s16(&wav, channelsOut, sampleRateOut, totalFrameCountOut); -} -MA_API float* ma_dr_wav_open_file_and_read_pcm_frames_f32_w(const wchar_t* filename, unsigned int* channelsOut, unsigned int* sampleRateOut, ma_uint64* totalFrameCountOut, const ma_allocation_callbacks* pAllocationCallbacks) -{ - ma_dr_wav wav; - if (sampleRateOut) { - *sampleRateOut = 0; - } - if (channelsOut) { - *channelsOut = 0; - } - if (totalFrameCountOut) { - *totalFrameCountOut = 0; - } - if (!ma_dr_wav_init_file_w(&wav, filename, pAllocationCallbacks)) { - return NULL; - } - return ma_dr_wav__read_pcm_frames_and_close_f32(&wav, channelsOut, sampleRateOut, totalFrameCountOut); -} -MA_API ma_int32* ma_dr_wav_open_file_and_read_pcm_frames_s32_w(const wchar_t* filename, unsigned int* channelsOut, unsigned int* sampleRateOut, ma_uint64* totalFrameCountOut, const ma_allocation_callbacks* pAllocationCallbacks) -{ - ma_dr_wav wav; - if (sampleRateOut) { - *sampleRateOut = 0; - } - if (channelsOut) { - *channelsOut = 0; - } - if (totalFrameCountOut) { - *totalFrameCountOut = 0; - } - if (!ma_dr_wav_init_file_w(&wav, filename, pAllocationCallbacks)) { - return NULL; - } - return ma_dr_wav__read_pcm_frames_and_close_s32(&wav, channelsOut, sampleRateOut, totalFrameCountOut); -} -#endif -#endif -MA_API ma_int16* ma_dr_wav_open_memory_and_read_pcm_frames_s16(const void* data, size_t dataSize, unsigned int* channelsOut, unsigned int* sampleRateOut, ma_uint64* totalFrameCountOut, const ma_allocation_callbacks* pAllocationCallbacks) -{ - ma_dr_wav wav; - if (channelsOut) { - *channelsOut = 0; - } - if (sampleRateOut) { - *sampleRateOut = 0; - } - if (totalFrameCountOut) { - *totalFrameCountOut = 0; - } - if (!ma_dr_wav_init_memory(&wav, data, dataSize, pAllocationCallbacks)) { - return NULL; - } - return ma_dr_wav__read_pcm_frames_and_close_s16(&wav, channelsOut, sampleRateOut, totalFrameCountOut); -} -MA_API float* ma_dr_wav_open_memory_and_read_pcm_frames_f32(const void* data, size_t dataSize, unsigned int* channelsOut, unsigned int* sampleRateOut, ma_uint64* totalFrameCountOut, const ma_allocation_callbacks* pAllocationCallbacks) -{ - ma_dr_wav wav; - if (channelsOut) { - *channelsOut = 0; - } - if (sampleRateOut) { - *sampleRateOut = 0; - } - if (totalFrameCountOut) { - *totalFrameCountOut = 0; - } - if (!ma_dr_wav_init_memory(&wav, data, dataSize, pAllocationCallbacks)) { - return NULL; - } - return ma_dr_wav__read_pcm_frames_and_close_f32(&wav, channelsOut, sampleRateOut, totalFrameCountOut); -} -MA_API ma_int32* ma_dr_wav_open_memory_and_read_pcm_frames_s32(const void* data, size_t dataSize, unsigned int* channelsOut, unsigned int* sampleRateOut, ma_uint64* totalFrameCountOut, const ma_allocation_callbacks* pAllocationCallbacks) -{ - ma_dr_wav wav; - if (channelsOut) { - *channelsOut = 0; - } - if (sampleRateOut) { - *sampleRateOut = 0; - } - if (totalFrameCountOut) { - *totalFrameCountOut = 0; - } - if (!ma_dr_wav_init_memory(&wav, data, dataSize, pAllocationCallbacks)) { - return NULL; - } - return ma_dr_wav__read_pcm_frames_and_close_s32(&wav, channelsOut, sampleRateOut, totalFrameCountOut); -} -#endif -MA_API void ma_dr_wav_free(void* p, const ma_allocation_callbacks* pAllocationCallbacks) -{ - if (pAllocationCallbacks != NULL) { - ma_dr_wav__free_from_callbacks(p, pAllocationCallbacks); - } else { - ma_dr_wav__free_default(p, NULL); - } -} -MA_API ma_uint16 ma_dr_wav_bytes_to_u16(const ma_uint8* data) -{ - return ((ma_uint16)data[0] << 0) | ((ma_uint16)data[1] << 8); -} -MA_API ma_int16 ma_dr_wav_bytes_to_s16(const ma_uint8* data) -{ - return (ma_int16)ma_dr_wav_bytes_to_u16(data); -} -MA_API ma_uint32 ma_dr_wav_bytes_to_u32(const ma_uint8* data) -{ - return ma_dr_wav_bytes_to_u32_le(data); -} -MA_API float ma_dr_wav_bytes_to_f32(const ma_uint8* data) -{ - union { - ma_uint32 u32; - float f32; - } value; - value.u32 = ma_dr_wav_bytes_to_u32(data); - return value.f32; -} -MA_API ma_int32 ma_dr_wav_bytes_to_s32(const ma_uint8* data) -{ - return (ma_int32)ma_dr_wav_bytes_to_u32(data); -} -MA_API ma_uint64 ma_dr_wav_bytes_to_u64(const ma_uint8* data) -{ - return - ((ma_uint64)data[0] << 0) | ((ma_uint64)data[1] << 8) | ((ma_uint64)data[2] << 16) | ((ma_uint64)data[3] << 24) | - ((ma_uint64)data[4] << 32) | ((ma_uint64)data[5] << 40) | ((ma_uint64)data[6] << 48) | ((ma_uint64)data[7] << 56); -} -MA_API ma_int64 ma_dr_wav_bytes_to_s64(const ma_uint8* data) -{ - return (ma_int64)ma_dr_wav_bytes_to_u64(data); -} -MA_API ma_bool32 ma_dr_wav_guid_equal(const ma_uint8 a[16], const ma_uint8 b[16]) -{ - int i; - for (i = 0; i < 16; i += 1) { - if (a[i] != b[i]) { - return MA_FALSE; - } - } - return MA_TRUE; -} -MA_API ma_bool32 ma_dr_wav_fourcc_equal(const ma_uint8* a, const char* b) -{ - return - a[0] == b[0] && - a[1] == b[1] && - a[2] == b[2] && - a[3] == b[3]; -} -#ifdef __MRC__ -#pragma options opt reset -#endif -#endif -/* dr_wav_c end */ -#endif /* MA_DR_WAV_IMPLEMENTATION */ -#endif /* MA_NO_WAV */ - -#if !defined(MA_NO_FLAC) && !defined(MA_NO_DECODING) -#if !defined(MA_DR_FLAC_IMPLEMENTATION) -/* dr_flac_c begin */ -#ifndef ma_dr_flac_c -#define ma_dr_flac_c -#if defined(__clang__) || (defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6))) - #pragma GCC diagnostic push - #if __GNUC__ >= 7 - #pragma GCC diagnostic ignored "-Wimplicit-fallthrough" - #endif -#endif -#ifdef __linux__ - #ifndef _BSD_SOURCE - #define _BSD_SOURCE - #endif - #ifndef _DEFAULT_SOURCE - #define _DEFAULT_SOURCE - #endif - #ifndef __USE_BSD - #define __USE_BSD - #endif - #include -#endif -#include -#include -#if !defined(MA_DR_FLAC_NO_SIMD) - #if defined(MA_X64) || defined(MA_X86) - #if defined(_MSC_VER) && !defined(__clang__) - #if _MSC_VER >= 1400 && !defined(MA_DR_FLAC_NO_SSE2) - #define MA_DR_FLAC_SUPPORT_SSE2 - #endif - #if _MSC_VER >= 1600 && !defined(MA_DR_FLAC_NO_SSE41) - #define MA_DR_FLAC_SUPPORT_SSE41 - #endif - #elif defined(__clang__) || (defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 3))) - #if defined(__SSE2__) && !defined(MA_DR_FLAC_NO_SSE2) - #define MA_DR_FLAC_SUPPORT_SSE2 - #endif - #if defined(__SSE4_1__) && !defined(MA_DR_FLAC_NO_SSE41) - #define MA_DR_FLAC_SUPPORT_SSE41 - #endif - #endif - #if !defined(__GNUC__) && !defined(__clang__) && defined(__has_include) - #if !defined(MA_DR_FLAC_SUPPORT_SSE2) && !defined(MA_DR_FLAC_NO_SSE2) && __has_include() - #define MA_DR_FLAC_SUPPORT_SSE2 - #endif - #if !defined(MA_DR_FLAC_SUPPORT_SSE41) && !defined(MA_DR_FLAC_NO_SSE41) && __has_include() - #define MA_DR_FLAC_SUPPORT_SSE41 - #endif - #endif - #if defined(MA_DR_FLAC_SUPPORT_SSE41) - #include - #elif defined(MA_DR_FLAC_SUPPORT_SSE2) - #include - #endif - #endif - #if defined(MA_ARM) - #if !defined(MA_DR_FLAC_NO_NEON) && (defined(__ARM_NEON) || defined(__aarch64__) || defined(_M_ARM64)) - #define MA_DR_FLAC_SUPPORT_NEON - #include - #endif - #endif -#endif -#if !defined(MA_DR_FLAC_NO_SIMD) && (defined(MA_X86) || defined(MA_X64)) - #if defined(_MSC_VER) && !defined(__clang__) - #if _MSC_VER >= 1400 - #include - static void ma_dr_flac__cpuid(int info[4], int fid) - { - __cpuid(info, fid); - } - #else - #define MA_DR_FLAC_NO_CPUID - #endif - #else - #if defined(__GNUC__) || defined(__clang__) - static void ma_dr_flac__cpuid(int info[4], int fid) - { - #if defined(MA_X86) && defined(__PIC__) - __asm__ __volatile__ ( - "xchg{l} {%%}ebx, %k1;" - "cpuid;" - "xchg{l} {%%}ebx, %k1;" - : "=a"(info[0]), "=&r"(info[1]), "=c"(info[2]), "=d"(info[3]) : "a"(fid), "c"(0) - ); - #else - __asm__ __volatile__ ( - "cpuid" : "=a"(info[0]), "=b"(info[1]), "=c"(info[2]), "=d"(info[3]) : "a"(fid), "c"(0) - ); - #endif - } - #else - #define MA_DR_FLAC_NO_CPUID - #endif - #endif -#else - #define MA_DR_FLAC_NO_CPUID -#endif -static MA_INLINE ma_bool32 ma_dr_flac_has_sse2(void) -{ -#if defined(MA_DR_FLAC_SUPPORT_SSE2) - #if (defined(MA_X64) || defined(MA_X86)) && !defined(MA_DR_FLAC_NO_SSE2) - #if defined(MA_X64) - return MA_TRUE; - #elif (defined(_M_IX86_FP) && _M_IX86_FP == 2) || defined(__SSE2__) - return MA_TRUE; - #else - #if defined(MA_DR_FLAC_NO_CPUID) - return MA_FALSE; - #else - int info[4]; - ma_dr_flac__cpuid(info, 1); - return (info[3] & (1 << 26)) != 0; - #endif - #endif - #else - return MA_FALSE; - #endif -#else - return MA_FALSE; -#endif -} -static MA_INLINE ma_bool32 ma_dr_flac_has_sse41(void) -{ -#if defined(MA_DR_FLAC_SUPPORT_SSE41) - #if (defined(MA_X64) || defined(MA_X86)) && !defined(MA_DR_FLAC_NO_SSE41) - #if defined(__SSE4_1__) || defined(__AVX__) - return MA_TRUE; - #else - #if defined(MA_DR_FLAC_NO_CPUID) - return MA_FALSE; - #else - int info[4]; - ma_dr_flac__cpuid(info, 1); - return (info[2] & (1 << 19)) != 0; - #endif - #endif - #else - return MA_FALSE; - #endif -#else - return MA_FALSE; -#endif -} -#if defined(_MSC_VER) && _MSC_VER >= 1500 && (defined(MA_X86) || defined(MA_X64)) && !defined(__clang__) - #define MA_DR_FLAC_HAS_LZCNT_INTRINSIC -#elif (defined(__GNUC__) && ((__GNUC__ > 4) || (__GNUC__ == 4 && __GNUC_MINOR__ >= 7))) - #define MA_DR_FLAC_HAS_LZCNT_INTRINSIC -#elif defined(__clang__) - #if defined(__has_builtin) - #if __has_builtin(__builtin_clzll) || __has_builtin(__builtin_clzl) - #define MA_DR_FLAC_HAS_LZCNT_INTRINSIC - #endif - #endif -#endif -#if defined(_MSC_VER) && _MSC_VER >= 1400 && !defined(__clang__) - #define MA_DR_FLAC_HAS_BYTESWAP16_INTRINSIC - #define MA_DR_FLAC_HAS_BYTESWAP32_INTRINSIC - #define MA_DR_FLAC_HAS_BYTESWAP64_INTRINSIC -#elif defined(__clang__) - #if defined(__has_builtin) - #if __has_builtin(__builtin_bswap16) - #define MA_DR_FLAC_HAS_BYTESWAP16_INTRINSIC - #endif - #if __has_builtin(__builtin_bswap32) - #define MA_DR_FLAC_HAS_BYTESWAP32_INTRINSIC - #endif - #if __has_builtin(__builtin_bswap64) - #define MA_DR_FLAC_HAS_BYTESWAP64_INTRINSIC - #endif - #endif -#elif defined(__GNUC__) - #if ((__GNUC__ > 4) || (__GNUC__ == 4 && __GNUC_MINOR__ >= 3)) - #define MA_DR_FLAC_HAS_BYTESWAP32_INTRINSIC - #define MA_DR_FLAC_HAS_BYTESWAP64_INTRINSIC - #endif - #if ((__GNUC__ > 4) || (__GNUC__ == 4 && __GNUC_MINOR__ >= 8)) - #define MA_DR_FLAC_HAS_BYTESWAP16_INTRINSIC - #endif -#elif defined(__WATCOMC__) && defined(__386__) - #define MA_DR_FLAC_HAS_BYTESWAP16_INTRINSIC - #define MA_DR_FLAC_HAS_BYTESWAP32_INTRINSIC - #define MA_DR_FLAC_HAS_BYTESWAP64_INTRINSIC - extern __inline ma_uint16 _watcom_bswap16(ma_uint16); - extern __inline ma_uint32 _watcom_bswap32(ma_uint32); - extern __inline ma_uint64 _watcom_bswap64(ma_uint64); -#pragma aux _watcom_bswap16 = \ - "xchg al, ah" \ - parm [ax] \ - value [ax] \ - modify nomemory; -#pragma aux _watcom_bswap32 = \ - "bswap eax" \ - parm [eax] \ - value [eax] \ - modify nomemory; -#pragma aux _watcom_bswap64 = \ - "bswap eax" \ - "bswap edx" \ - "xchg eax,edx" \ - parm [eax edx] \ - value [eax edx] \ - modify nomemory; -#endif -#ifndef MA_DR_FLAC_ASSERT -#include -#define MA_DR_FLAC_ASSERT(expression) assert(expression) -#endif -#ifndef MA_DR_FLAC_MALLOC -#define MA_DR_FLAC_MALLOC(sz) malloc((sz)) -#endif -#ifndef MA_DR_FLAC_REALLOC -#define MA_DR_FLAC_REALLOC(p, sz) realloc((p), (sz)) -#endif -#ifndef MA_DR_FLAC_FREE -#define MA_DR_FLAC_FREE(p) free((p)) -#endif -#ifndef MA_DR_FLAC_COPY_MEMORY -#define MA_DR_FLAC_COPY_MEMORY(dst, src, sz) memcpy((dst), (src), (sz)) -#endif -#ifndef MA_DR_FLAC_ZERO_MEMORY -#define MA_DR_FLAC_ZERO_MEMORY(p, sz) memset((p), 0, (sz)) -#endif -#ifndef MA_DR_FLAC_ZERO_OBJECT -#define MA_DR_FLAC_ZERO_OBJECT(p) MA_DR_FLAC_ZERO_MEMORY((p), sizeof(*(p))) -#endif -#define MA_DR_FLAC_MAX_SIMD_VECTOR_SIZE 64 -#define MA_DR_FLAC_SUBFRAME_CONSTANT 0 -#define MA_DR_FLAC_SUBFRAME_VERBATIM 1 -#define MA_DR_FLAC_SUBFRAME_FIXED 8 -#define MA_DR_FLAC_SUBFRAME_LPC 32 -#define MA_DR_FLAC_SUBFRAME_RESERVED 255 -#define MA_DR_FLAC_RESIDUAL_CODING_METHOD_PARTITIONED_RICE 0 -#define MA_DR_FLAC_RESIDUAL_CODING_METHOD_PARTITIONED_RICE2 1 -#define MA_DR_FLAC_CHANNEL_ASSIGNMENT_INDEPENDENT 0 -#define MA_DR_FLAC_CHANNEL_ASSIGNMENT_LEFT_SIDE 8 -#define MA_DR_FLAC_CHANNEL_ASSIGNMENT_RIGHT_SIDE 9 -#define MA_DR_FLAC_CHANNEL_ASSIGNMENT_MID_SIDE 10 -#define MA_DR_FLAC_SEEKPOINT_SIZE_IN_BYTES 18 -#define MA_DR_FLAC_CUESHEET_TRACK_SIZE_IN_BYTES 36 -#define MA_DR_FLAC_CUESHEET_TRACK_INDEX_SIZE_IN_BYTES 12 -#define ma_dr_flac_align(x, a) ((((x) + (a) - 1) / (a)) * (a)) -MA_API void ma_dr_flac_version(ma_uint32* pMajor, ma_uint32* pMinor, ma_uint32* pRevision) -{ - if (pMajor) { - *pMajor = MA_DR_FLAC_VERSION_MAJOR; - } - if (pMinor) { - *pMinor = MA_DR_FLAC_VERSION_MINOR; - } - if (pRevision) { - *pRevision = MA_DR_FLAC_VERSION_REVISION; - } -} -MA_API const char* ma_dr_flac_version_string(void) -{ - return MA_DR_FLAC_VERSION_STRING; -} -#if defined(__has_feature) - #if __has_feature(thread_sanitizer) - #define MA_DR_FLAC_NO_THREAD_SANITIZE __attribute__((no_sanitize("thread"))) - #else - #define MA_DR_FLAC_NO_THREAD_SANITIZE - #endif -#else - #define MA_DR_FLAC_NO_THREAD_SANITIZE -#endif -#if defined(MA_DR_FLAC_HAS_LZCNT_INTRINSIC) -static ma_bool32 ma_dr_flac__gIsLZCNTSupported = MA_FALSE; -#endif -#ifndef MA_DR_FLAC_NO_CPUID -static ma_bool32 ma_dr_flac__gIsSSE2Supported = MA_FALSE; -static ma_bool32 ma_dr_flac__gIsSSE41Supported = MA_FALSE; -MA_DR_FLAC_NO_THREAD_SANITIZE static void ma_dr_flac__init_cpu_caps(void) -{ - static ma_bool32 isCPUCapsInitialized = MA_FALSE; - if (!isCPUCapsInitialized) { -#if defined(MA_DR_FLAC_HAS_LZCNT_INTRINSIC) - int info[4] = {0}; - ma_dr_flac__cpuid(info, 0x80000001); - ma_dr_flac__gIsLZCNTSupported = (info[2] & (1 << 5)) != 0; -#endif - ma_dr_flac__gIsSSE2Supported = ma_dr_flac_has_sse2(); - ma_dr_flac__gIsSSE41Supported = ma_dr_flac_has_sse41(); - isCPUCapsInitialized = MA_TRUE; - } -} -#else -static ma_bool32 ma_dr_flac__gIsNEONSupported = MA_FALSE; -static MA_INLINE ma_bool32 ma_dr_flac__has_neon(void) -{ -#if defined(MA_DR_FLAC_SUPPORT_NEON) - #if defined(MA_ARM) && !defined(MA_DR_FLAC_NO_NEON) - #if (defined(__ARM_NEON) || defined(__aarch64__) || defined(_M_ARM64)) - return MA_TRUE; - #else - return MA_FALSE; - #endif - #else - return MA_FALSE; - #endif -#else - return MA_FALSE; -#endif -} -MA_DR_FLAC_NO_THREAD_SANITIZE static void ma_dr_flac__init_cpu_caps(void) -{ - ma_dr_flac__gIsNEONSupported = ma_dr_flac__has_neon(); -#if defined(MA_DR_FLAC_HAS_LZCNT_INTRINSIC) && defined(MA_ARM) && (defined(__ARM_ARCH) && __ARM_ARCH >= 5) - ma_dr_flac__gIsLZCNTSupported = MA_TRUE; -#endif -} -#endif -static MA_INLINE ma_bool32 ma_dr_flac__is_little_endian(void) -{ -#if defined(MA_X86) || defined(MA_X64) - return MA_TRUE; -#elif defined(__BYTE_ORDER) && defined(__LITTLE_ENDIAN) && __BYTE_ORDER == __LITTLE_ENDIAN - return MA_TRUE; -#else - int n = 1; - return (*(char*)&n) == 1; -#endif -} -static MA_INLINE ma_uint16 ma_dr_flac__swap_endian_uint16(ma_uint16 n) -{ -#ifdef MA_DR_FLAC_HAS_BYTESWAP16_INTRINSIC - #if defined(_MSC_VER) && !defined(__clang__) - return _byteswap_ushort(n); - #elif defined(__GNUC__) || defined(__clang__) - return __builtin_bswap16(n); - #elif defined(__WATCOMC__) && defined(__386__) - return _watcom_bswap16(n); - #else - #error "This compiler does not support the byte swap intrinsic." - #endif -#else - return ((n & 0xFF00) >> 8) | - ((n & 0x00FF) << 8); -#endif -} -static MA_INLINE ma_uint32 ma_dr_flac__swap_endian_uint32(ma_uint32 n) -{ -#ifdef MA_DR_FLAC_HAS_BYTESWAP32_INTRINSIC - #if defined(_MSC_VER) && !defined(__clang__) - return _byteswap_ulong(n); - #elif defined(__GNUC__) || defined(__clang__) - #if defined(MA_ARM) && (defined(__ARM_ARCH) && __ARM_ARCH >= 6) && !defined(__ARM_ARCH_6M__) && !defined(MA_64BIT) - ma_uint32 r; - __asm__ __volatile__ ( - #if defined(MA_64BIT) - "rev %w[out], %w[in]" : [out]"=r"(r) : [in]"r"(n) - #else - "rev %[out], %[in]" : [out]"=r"(r) : [in]"r"(n) - #endif - ); - return r; - #else - return __builtin_bswap32(n); - #endif - #elif defined(__WATCOMC__) && defined(__386__) - return _watcom_bswap32(n); - #else - #error "This compiler does not support the byte swap intrinsic." - #endif -#else - return ((n & 0xFF000000) >> 24) | - ((n & 0x00FF0000) >> 8) | - ((n & 0x0000FF00) << 8) | - ((n & 0x000000FF) << 24); -#endif -} -static MA_INLINE ma_uint64 ma_dr_flac__swap_endian_uint64(ma_uint64 n) -{ -#ifdef MA_DR_FLAC_HAS_BYTESWAP64_INTRINSIC - #if defined(_MSC_VER) && !defined(__clang__) - return _byteswap_uint64(n); - #elif defined(__GNUC__) || defined(__clang__) - return __builtin_bswap64(n); - #elif defined(__WATCOMC__) && defined(__386__) - return _watcom_bswap64(n); - #else - #error "This compiler does not support the byte swap intrinsic." - #endif -#else - return ((n & ((ma_uint64)0xFF000000 << 32)) >> 56) | - ((n & ((ma_uint64)0x00FF0000 << 32)) >> 40) | - ((n & ((ma_uint64)0x0000FF00 << 32)) >> 24) | - ((n & ((ma_uint64)0x000000FF << 32)) >> 8) | - ((n & ((ma_uint64)0xFF000000 )) << 8) | - ((n & ((ma_uint64)0x00FF0000 )) << 24) | - ((n & ((ma_uint64)0x0000FF00 )) << 40) | - ((n & ((ma_uint64)0x000000FF )) << 56); -#endif -} -static MA_INLINE ma_uint16 ma_dr_flac__be2host_16(ma_uint16 n) -{ - if (ma_dr_flac__is_little_endian()) { - return ma_dr_flac__swap_endian_uint16(n); - } - return n; -} -static MA_INLINE ma_uint32 ma_dr_flac__be2host_32(ma_uint32 n) -{ - if (ma_dr_flac__is_little_endian()) { - return ma_dr_flac__swap_endian_uint32(n); - } - return n; -} -static MA_INLINE ma_uint32 ma_dr_flac__be2host_32_ptr_unaligned(const void* pData) -{ - const ma_uint8* pNum = (ma_uint8*)pData; - return *(pNum) << 24 | *(pNum+1) << 16 | *(pNum+2) << 8 | *(pNum+3); -} -static MA_INLINE ma_uint64 ma_dr_flac__be2host_64(ma_uint64 n) -{ - if (ma_dr_flac__is_little_endian()) { - return ma_dr_flac__swap_endian_uint64(n); - } - return n; -} -static MA_INLINE ma_uint32 ma_dr_flac__le2host_32(ma_uint32 n) -{ - if (!ma_dr_flac__is_little_endian()) { - return ma_dr_flac__swap_endian_uint32(n); - } - return n; -} -static MA_INLINE ma_uint32 ma_dr_flac__le2host_32_ptr_unaligned(const void* pData) -{ - const ma_uint8* pNum = (ma_uint8*)pData; - return *pNum | *(pNum+1) << 8 | *(pNum+2) << 16 | *(pNum+3) << 24; -} -static MA_INLINE ma_uint32 ma_dr_flac__unsynchsafe_32(ma_uint32 n) -{ - ma_uint32 result = 0; - result |= (n & 0x7F000000) >> 3; - result |= (n & 0x007F0000) >> 2; - result |= (n & 0x00007F00) >> 1; - result |= (n & 0x0000007F) >> 0; - return result; -} -static ma_uint8 ma_dr_flac__crc8_table[] = { - 0x00, 0x07, 0x0E, 0x09, 0x1C, 0x1B, 0x12, 0x15, 0x38, 0x3F, 0x36, 0x31, 0x24, 0x23, 0x2A, 0x2D, - 0x70, 0x77, 0x7E, 0x79, 0x6C, 0x6B, 0x62, 0x65, 0x48, 0x4F, 0x46, 0x41, 0x54, 0x53, 0x5A, 0x5D, - 0xE0, 0xE7, 0xEE, 0xE9, 0xFC, 0xFB, 0xF2, 0xF5, 0xD8, 0xDF, 0xD6, 0xD1, 0xC4, 0xC3, 0xCA, 0xCD, - 0x90, 0x97, 0x9E, 0x99, 0x8C, 0x8B, 0x82, 0x85, 0xA8, 0xAF, 0xA6, 0xA1, 0xB4, 0xB3, 0xBA, 0xBD, - 0xC7, 0xC0, 0xC9, 0xCE, 0xDB, 0xDC, 0xD5, 0xD2, 0xFF, 0xF8, 0xF1, 0xF6, 0xE3, 0xE4, 0xED, 0xEA, - 0xB7, 0xB0, 0xB9, 0xBE, 0xAB, 0xAC, 0xA5, 0xA2, 0x8F, 0x88, 0x81, 0x86, 0x93, 0x94, 0x9D, 0x9A, - 0x27, 0x20, 0x29, 0x2E, 0x3B, 0x3C, 0x35, 0x32, 0x1F, 0x18, 0x11, 0x16, 0x03, 0x04, 0x0D, 0x0A, - 0x57, 0x50, 0x59, 0x5E, 0x4B, 0x4C, 0x45, 0x42, 0x6F, 0x68, 0x61, 0x66, 0x73, 0x74, 0x7D, 0x7A, - 0x89, 0x8E, 0x87, 0x80, 0x95, 0x92, 0x9B, 0x9C, 0xB1, 0xB6, 0xBF, 0xB8, 0xAD, 0xAA, 0xA3, 0xA4, - 0xF9, 0xFE, 0xF7, 0xF0, 0xE5, 0xE2, 0xEB, 0xEC, 0xC1, 0xC6, 0xCF, 0xC8, 0xDD, 0xDA, 0xD3, 0xD4, - 0x69, 0x6E, 0x67, 0x60, 0x75, 0x72, 0x7B, 0x7C, 0x51, 0x56, 0x5F, 0x58, 0x4D, 0x4A, 0x43, 0x44, - 0x19, 0x1E, 0x17, 0x10, 0x05, 0x02, 0x0B, 0x0C, 0x21, 0x26, 0x2F, 0x28, 0x3D, 0x3A, 0x33, 0x34, - 0x4E, 0x49, 0x40, 0x47, 0x52, 0x55, 0x5C, 0x5B, 0x76, 0x71, 0x78, 0x7F, 0x6A, 0x6D, 0x64, 0x63, - 0x3E, 0x39, 0x30, 0x37, 0x22, 0x25, 0x2C, 0x2B, 0x06, 0x01, 0x08, 0x0F, 0x1A, 0x1D, 0x14, 0x13, - 0xAE, 0xA9, 0xA0, 0xA7, 0xB2, 0xB5, 0xBC, 0xBB, 0x96, 0x91, 0x98, 0x9F, 0x8A, 0x8D, 0x84, 0x83, - 0xDE, 0xD9, 0xD0, 0xD7, 0xC2, 0xC5, 0xCC, 0xCB, 0xE6, 0xE1, 0xE8, 0xEF, 0xFA, 0xFD, 0xF4, 0xF3 -}; -static ma_uint16 ma_dr_flac__crc16_table[] = { - 0x0000, 0x8005, 0x800F, 0x000A, 0x801B, 0x001E, 0x0014, 0x8011, - 0x8033, 0x0036, 0x003C, 0x8039, 0x0028, 0x802D, 0x8027, 0x0022, - 0x8063, 0x0066, 0x006C, 0x8069, 0x0078, 0x807D, 0x8077, 0x0072, - 0x0050, 0x8055, 0x805F, 0x005A, 0x804B, 0x004E, 0x0044, 0x8041, - 0x80C3, 0x00C6, 0x00CC, 0x80C9, 0x00D8, 0x80DD, 0x80D7, 0x00D2, - 0x00F0, 0x80F5, 0x80FF, 0x00FA, 0x80EB, 0x00EE, 0x00E4, 0x80E1, - 0x00A0, 0x80A5, 0x80AF, 0x00AA, 0x80BB, 0x00BE, 0x00B4, 0x80B1, - 0x8093, 0x0096, 0x009C, 0x8099, 0x0088, 0x808D, 0x8087, 0x0082, - 0x8183, 0x0186, 0x018C, 0x8189, 0x0198, 0x819D, 0x8197, 0x0192, - 0x01B0, 0x81B5, 0x81BF, 0x01BA, 0x81AB, 0x01AE, 0x01A4, 0x81A1, - 0x01E0, 0x81E5, 0x81EF, 0x01EA, 0x81FB, 0x01FE, 0x01F4, 0x81F1, - 0x81D3, 0x01D6, 0x01DC, 0x81D9, 0x01C8, 0x81CD, 0x81C7, 0x01C2, - 0x0140, 0x8145, 0x814F, 0x014A, 0x815B, 0x015E, 0x0154, 0x8151, - 0x8173, 0x0176, 0x017C, 0x8179, 0x0168, 0x816D, 0x8167, 0x0162, - 0x8123, 0x0126, 0x012C, 0x8129, 0x0138, 0x813D, 0x8137, 0x0132, - 0x0110, 0x8115, 0x811F, 0x011A, 0x810B, 0x010E, 0x0104, 0x8101, - 0x8303, 0x0306, 0x030C, 0x8309, 0x0318, 0x831D, 0x8317, 0x0312, - 0x0330, 0x8335, 0x833F, 0x033A, 0x832B, 0x032E, 0x0324, 0x8321, - 0x0360, 0x8365, 0x836F, 0x036A, 0x837B, 0x037E, 0x0374, 0x8371, - 0x8353, 0x0356, 0x035C, 0x8359, 0x0348, 0x834D, 0x8347, 0x0342, - 0x03C0, 0x83C5, 0x83CF, 0x03CA, 0x83DB, 0x03DE, 0x03D4, 0x83D1, - 0x83F3, 0x03F6, 0x03FC, 0x83F9, 0x03E8, 0x83ED, 0x83E7, 0x03E2, - 0x83A3, 0x03A6, 0x03AC, 0x83A9, 0x03B8, 0x83BD, 0x83B7, 0x03B2, - 0x0390, 0x8395, 0x839F, 0x039A, 0x838B, 0x038E, 0x0384, 0x8381, - 0x0280, 0x8285, 0x828F, 0x028A, 0x829B, 0x029E, 0x0294, 0x8291, - 0x82B3, 0x02B6, 0x02BC, 0x82B9, 0x02A8, 0x82AD, 0x82A7, 0x02A2, - 0x82E3, 0x02E6, 0x02EC, 0x82E9, 0x02F8, 0x82FD, 0x82F7, 0x02F2, - 0x02D0, 0x82D5, 0x82DF, 0x02DA, 0x82CB, 0x02CE, 0x02C4, 0x82C1, - 0x8243, 0x0246, 0x024C, 0x8249, 0x0258, 0x825D, 0x8257, 0x0252, - 0x0270, 0x8275, 0x827F, 0x027A, 0x826B, 0x026E, 0x0264, 0x8261, - 0x0220, 0x8225, 0x822F, 0x022A, 0x823B, 0x023E, 0x0234, 0x8231, - 0x8213, 0x0216, 0x021C, 0x8219, 0x0208, 0x820D, 0x8207, 0x0202 -}; -static MA_INLINE ma_uint8 ma_dr_flac_crc8_byte(ma_uint8 crc, ma_uint8 data) -{ - return ma_dr_flac__crc8_table[crc ^ data]; -} -static MA_INLINE ma_uint8 ma_dr_flac_crc8(ma_uint8 crc, ma_uint32 data, ma_uint32 count) -{ -#ifdef MA_DR_FLAC_NO_CRC - (void)crc; - (void)data; - (void)count; - return 0; -#else -#if 0 - ma_uint8 p = 0x07; - for (int i = count-1; i >= 0; --i) { - ma_uint8 bit = (data & (1 << i)) >> i; - if (crc & 0x80) { - crc = ((crc << 1) | bit) ^ p; - } else { - crc = ((crc << 1) | bit); - } - } - return crc; -#else - ma_uint32 wholeBytes; - ma_uint32 leftoverBits; - ma_uint64 leftoverDataMask; - static ma_uint64 leftoverDataMaskTable[8] = { - 0x00, 0x01, 0x03, 0x07, 0x0F, 0x1F, 0x3F, 0x7F - }; - MA_DR_FLAC_ASSERT(count <= 32); - wholeBytes = count >> 3; - leftoverBits = count - (wholeBytes*8); - leftoverDataMask = leftoverDataMaskTable[leftoverBits]; - switch (wholeBytes) { - case 4: crc = ma_dr_flac_crc8_byte(crc, (ma_uint8)((data & (0xFF000000UL << leftoverBits)) >> (24 + leftoverBits))); - case 3: crc = ma_dr_flac_crc8_byte(crc, (ma_uint8)((data & (0x00FF0000UL << leftoverBits)) >> (16 + leftoverBits))); - case 2: crc = ma_dr_flac_crc8_byte(crc, (ma_uint8)((data & (0x0000FF00UL << leftoverBits)) >> ( 8 + leftoverBits))); - case 1: crc = ma_dr_flac_crc8_byte(crc, (ma_uint8)((data & (0x000000FFUL << leftoverBits)) >> ( 0 + leftoverBits))); - case 0: if (leftoverBits > 0) crc = (ma_uint8)((crc << leftoverBits) ^ ma_dr_flac__crc8_table[(crc >> (8 - leftoverBits)) ^ (data & leftoverDataMask)]); - } - return crc; -#endif -#endif -} -static MA_INLINE ma_uint16 ma_dr_flac_crc16_byte(ma_uint16 crc, ma_uint8 data) -{ - return (crc << 8) ^ ma_dr_flac__crc16_table[(ma_uint8)(crc >> 8) ^ data]; -} -static MA_INLINE ma_uint16 ma_dr_flac_crc16_cache(ma_uint16 crc, ma_dr_flac_cache_t data) -{ -#ifdef MA_64BIT - crc = ma_dr_flac_crc16_byte(crc, (ma_uint8)((data >> 56) & 0xFF)); - crc = ma_dr_flac_crc16_byte(crc, (ma_uint8)((data >> 48) & 0xFF)); - crc = ma_dr_flac_crc16_byte(crc, (ma_uint8)((data >> 40) & 0xFF)); - crc = ma_dr_flac_crc16_byte(crc, (ma_uint8)((data >> 32) & 0xFF)); -#endif - crc = ma_dr_flac_crc16_byte(crc, (ma_uint8)((data >> 24) & 0xFF)); - crc = ma_dr_flac_crc16_byte(crc, (ma_uint8)((data >> 16) & 0xFF)); - crc = ma_dr_flac_crc16_byte(crc, (ma_uint8)((data >> 8) & 0xFF)); - crc = ma_dr_flac_crc16_byte(crc, (ma_uint8)((data >> 0) & 0xFF)); - return crc; -} -static MA_INLINE ma_uint16 ma_dr_flac_crc16_bytes(ma_uint16 crc, ma_dr_flac_cache_t data, ma_uint32 byteCount) -{ - switch (byteCount) - { -#ifdef MA_64BIT - case 8: crc = ma_dr_flac_crc16_byte(crc, (ma_uint8)((data >> 56) & 0xFF)); - case 7: crc = ma_dr_flac_crc16_byte(crc, (ma_uint8)((data >> 48) & 0xFF)); - case 6: crc = ma_dr_flac_crc16_byte(crc, (ma_uint8)((data >> 40) & 0xFF)); - case 5: crc = ma_dr_flac_crc16_byte(crc, (ma_uint8)((data >> 32) & 0xFF)); -#endif - case 4: crc = ma_dr_flac_crc16_byte(crc, (ma_uint8)((data >> 24) & 0xFF)); - case 3: crc = ma_dr_flac_crc16_byte(crc, (ma_uint8)((data >> 16) & 0xFF)); - case 2: crc = ma_dr_flac_crc16_byte(crc, (ma_uint8)((data >> 8) & 0xFF)); - case 1: crc = ma_dr_flac_crc16_byte(crc, (ma_uint8)((data >> 0) & 0xFF)); - } - return crc; -} -#if 0 -static MA_INLINE ma_uint16 ma_dr_flac_crc16__32bit(ma_uint16 crc, ma_uint32 data, ma_uint32 count) -{ -#ifdef MA_DR_FLAC_NO_CRC - (void)crc; - (void)data; - (void)count; - return 0; -#else -#if 0 - ma_uint16 p = 0x8005; - for (int i = count-1; i >= 0; --i) { - ma_uint16 bit = (data & (1ULL << i)) >> i; - if (r & 0x8000) { - r = ((r << 1) | bit) ^ p; - } else { - r = ((r << 1) | bit); - } - } - return crc; -#else - ma_uint32 wholeBytes; - ma_uint32 leftoverBits; - ma_uint64 leftoverDataMask; - static ma_uint64 leftoverDataMaskTable[8] = { - 0x00, 0x01, 0x03, 0x07, 0x0F, 0x1F, 0x3F, 0x7F - }; - MA_DR_FLAC_ASSERT(count <= 64); - wholeBytes = count >> 3; - leftoverBits = count & 7; - leftoverDataMask = leftoverDataMaskTable[leftoverBits]; - switch (wholeBytes) { - default: - case 4: crc = ma_dr_flac_crc16_byte(crc, (ma_uint8)((data & (0xFF000000UL << leftoverBits)) >> (24 + leftoverBits))); - case 3: crc = ma_dr_flac_crc16_byte(crc, (ma_uint8)((data & (0x00FF0000UL << leftoverBits)) >> (16 + leftoverBits))); - case 2: crc = ma_dr_flac_crc16_byte(crc, (ma_uint8)((data & (0x0000FF00UL << leftoverBits)) >> ( 8 + leftoverBits))); - case 1: crc = ma_dr_flac_crc16_byte(crc, (ma_uint8)((data & (0x000000FFUL << leftoverBits)) >> ( 0 + leftoverBits))); - case 0: if (leftoverBits > 0) crc = (crc << leftoverBits) ^ ma_dr_flac__crc16_table[(crc >> (16 - leftoverBits)) ^ (data & leftoverDataMask)]; - } - return crc; -#endif -#endif -} -static MA_INLINE ma_uint16 ma_dr_flac_crc16__64bit(ma_uint16 crc, ma_uint64 data, ma_uint32 count) -{ -#ifdef MA_DR_FLAC_NO_CRC - (void)crc; - (void)data; - (void)count; - return 0; -#else - ma_uint32 wholeBytes; - ma_uint32 leftoverBits; - ma_uint64 leftoverDataMask; - static ma_uint64 leftoverDataMaskTable[8] = { - 0x00, 0x01, 0x03, 0x07, 0x0F, 0x1F, 0x3F, 0x7F - }; - MA_DR_FLAC_ASSERT(count <= 64); - wholeBytes = count >> 3; - leftoverBits = count & 7; - leftoverDataMask = leftoverDataMaskTable[leftoverBits]; - switch (wholeBytes) { - default: - case 8: crc = ma_dr_flac_crc16_byte(crc, (ma_uint8)((data & (((ma_uint64)0xFF000000 << 32) << leftoverBits)) >> (56 + leftoverBits))); - case 7: crc = ma_dr_flac_crc16_byte(crc, (ma_uint8)((data & (((ma_uint64)0x00FF0000 << 32) << leftoverBits)) >> (48 + leftoverBits))); - case 6: crc = ma_dr_flac_crc16_byte(crc, (ma_uint8)((data & (((ma_uint64)0x0000FF00 << 32) << leftoverBits)) >> (40 + leftoverBits))); - case 5: crc = ma_dr_flac_crc16_byte(crc, (ma_uint8)((data & (((ma_uint64)0x000000FF << 32) << leftoverBits)) >> (32 + leftoverBits))); - case 4: crc = ma_dr_flac_crc16_byte(crc, (ma_uint8)((data & (((ma_uint64)0xFF000000 ) << leftoverBits)) >> (24 + leftoverBits))); - case 3: crc = ma_dr_flac_crc16_byte(crc, (ma_uint8)((data & (((ma_uint64)0x00FF0000 ) << leftoverBits)) >> (16 + leftoverBits))); - case 2: crc = ma_dr_flac_crc16_byte(crc, (ma_uint8)((data & (((ma_uint64)0x0000FF00 ) << leftoverBits)) >> ( 8 + leftoverBits))); - case 1: crc = ma_dr_flac_crc16_byte(crc, (ma_uint8)((data & (((ma_uint64)0x000000FF ) << leftoverBits)) >> ( 0 + leftoverBits))); - case 0: if (leftoverBits > 0) crc = (crc << leftoverBits) ^ ma_dr_flac__crc16_table[(crc >> (16 - leftoverBits)) ^ (data & leftoverDataMask)]; - } - return crc; -#endif -} -static MA_INLINE ma_uint16 ma_dr_flac_crc16(ma_uint16 crc, ma_dr_flac_cache_t data, ma_uint32 count) -{ -#ifdef MA_64BIT - return ma_dr_flac_crc16__64bit(crc, data, count); -#else - return ma_dr_flac_crc16__32bit(crc, data, count); -#endif -} -#endif -#ifdef MA_64BIT -#define ma_dr_flac__be2host__cache_line ma_dr_flac__be2host_64 -#else -#define ma_dr_flac__be2host__cache_line ma_dr_flac__be2host_32 -#endif -#define MA_DR_FLAC_CACHE_L1_SIZE_BYTES(bs) (sizeof((bs)->cache)) -#define MA_DR_FLAC_CACHE_L1_SIZE_BITS(bs) (sizeof((bs)->cache)*8) -#define MA_DR_FLAC_CACHE_L1_BITS_REMAINING(bs) (MA_DR_FLAC_CACHE_L1_SIZE_BITS(bs) - (bs)->consumedBits) -#define MA_DR_FLAC_CACHE_L1_SELECTION_MASK(_bitCount) (~((~(ma_dr_flac_cache_t)0) >> (_bitCount))) -#define MA_DR_FLAC_CACHE_L1_SELECTION_SHIFT(bs, _bitCount) (MA_DR_FLAC_CACHE_L1_SIZE_BITS(bs) - (_bitCount)) -#define MA_DR_FLAC_CACHE_L1_SELECT(bs, _bitCount) (((bs)->cache) & MA_DR_FLAC_CACHE_L1_SELECTION_MASK(_bitCount)) -#define MA_DR_FLAC_CACHE_L1_SELECT_AND_SHIFT(bs, _bitCount) (MA_DR_FLAC_CACHE_L1_SELECT((bs), (_bitCount)) >> MA_DR_FLAC_CACHE_L1_SELECTION_SHIFT((bs), (_bitCount))) -#define MA_DR_FLAC_CACHE_L1_SELECT_AND_SHIFT_SAFE(bs, _bitCount)(MA_DR_FLAC_CACHE_L1_SELECT((bs), (_bitCount)) >> (MA_DR_FLAC_CACHE_L1_SELECTION_SHIFT((bs), (_bitCount)) & (MA_DR_FLAC_CACHE_L1_SIZE_BITS(bs)-1))) -#define MA_DR_FLAC_CACHE_L2_SIZE_BYTES(bs) (sizeof((bs)->cacheL2)) -#define MA_DR_FLAC_CACHE_L2_LINE_COUNT(bs) (MA_DR_FLAC_CACHE_L2_SIZE_BYTES(bs) / sizeof((bs)->cacheL2[0])) -#define MA_DR_FLAC_CACHE_L2_LINES_REMAINING(bs) (MA_DR_FLAC_CACHE_L2_LINE_COUNT(bs) - (bs)->nextL2Line) -#ifndef MA_DR_FLAC_NO_CRC -static MA_INLINE void ma_dr_flac__reset_crc16(ma_dr_flac_bs* bs) -{ - bs->crc16 = 0; - bs->crc16CacheIgnoredBytes = bs->consumedBits >> 3; -} -static MA_INLINE void ma_dr_flac__update_crc16(ma_dr_flac_bs* bs) -{ - if (bs->crc16CacheIgnoredBytes == 0) { - bs->crc16 = ma_dr_flac_crc16_cache(bs->crc16, bs->crc16Cache); - } else { - bs->crc16 = ma_dr_flac_crc16_bytes(bs->crc16, bs->crc16Cache, MA_DR_FLAC_CACHE_L1_SIZE_BYTES(bs) - bs->crc16CacheIgnoredBytes); - bs->crc16CacheIgnoredBytes = 0; - } -} -static MA_INLINE ma_uint16 ma_dr_flac__flush_crc16(ma_dr_flac_bs* bs) -{ - MA_DR_FLAC_ASSERT((MA_DR_FLAC_CACHE_L1_BITS_REMAINING(bs) & 7) == 0); - if (MA_DR_FLAC_CACHE_L1_BITS_REMAINING(bs) == 0) { - ma_dr_flac__update_crc16(bs); - } else { - bs->crc16 = ma_dr_flac_crc16_bytes(bs->crc16, bs->crc16Cache >> MA_DR_FLAC_CACHE_L1_BITS_REMAINING(bs), (bs->consumedBits >> 3) - bs->crc16CacheIgnoredBytes); - bs->crc16CacheIgnoredBytes = bs->consumedBits >> 3; - } - return bs->crc16; -} -#endif -static MA_INLINE ma_bool32 ma_dr_flac__reload_l1_cache_from_l2(ma_dr_flac_bs* bs) -{ - size_t bytesRead; - size_t alignedL1LineCount; - if (bs->nextL2Line < MA_DR_FLAC_CACHE_L2_LINE_COUNT(bs)) { - bs->cache = bs->cacheL2[bs->nextL2Line++]; - return MA_TRUE; - } - if (bs->unalignedByteCount > 0) { - return MA_FALSE; - } - bytesRead = bs->onRead(bs->pUserData, bs->cacheL2, MA_DR_FLAC_CACHE_L2_SIZE_BYTES(bs)); - bs->nextL2Line = 0; - if (bytesRead == MA_DR_FLAC_CACHE_L2_SIZE_BYTES(bs)) { - bs->cache = bs->cacheL2[bs->nextL2Line++]; - return MA_TRUE; - } - alignedL1LineCount = bytesRead / MA_DR_FLAC_CACHE_L1_SIZE_BYTES(bs); - bs->unalignedByteCount = bytesRead - (alignedL1LineCount * MA_DR_FLAC_CACHE_L1_SIZE_BYTES(bs)); - if (bs->unalignedByteCount > 0) { - bs->unalignedCache = bs->cacheL2[alignedL1LineCount]; - } - if (alignedL1LineCount > 0) { - size_t offset = MA_DR_FLAC_CACHE_L2_LINE_COUNT(bs) - alignedL1LineCount; - size_t i; - for (i = alignedL1LineCount; i > 0; --i) { - bs->cacheL2[i-1 + offset] = bs->cacheL2[i-1]; - } - bs->nextL2Line = (ma_uint32)offset; - bs->cache = bs->cacheL2[bs->nextL2Line++]; - return MA_TRUE; - } else { - bs->nextL2Line = MA_DR_FLAC_CACHE_L2_LINE_COUNT(bs); - return MA_FALSE; - } -} -static ma_bool32 ma_dr_flac__reload_cache(ma_dr_flac_bs* bs) -{ - size_t bytesRead; -#ifndef MA_DR_FLAC_NO_CRC - ma_dr_flac__update_crc16(bs); -#endif - if (ma_dr_flac__reload_l1_cache_from_l2(bs)) { - bs->cache = ma_dr_flac__be2host__cache_line(bs->cache); - bs->consumedBits = 0; -#ifndef MA_DR_FLAC_NO_CRC - bs->crc16Cache = bs->cache; -#endif - return MA_TRUE; - } - bytesRead = bs->unalignedByteCount; - if (bytesRead == 0) { - bs->consumedBits = MA_DR_FLAC_CACHE_L1_SIZE_BITS(bs); - return MA_FALSE; - } - MA_DR_FLAC_ASSERT(bytesRead < MA_DR_FLAC_CACHE_L1_SIZE_BYTES(bs)); - bs->consumedBits = (ma_uint32)(MA_DR_FLAC_CACHE_L1_SIZE_BYTES(bs) - bytesRead) * 8; - bs->cache = ma_dr_flac__be2host__cache_line(bs->unalignedCache); - bs->cache &= MA_DR_FLAC_CACHE_L1_SELECTION_MASK(MA_DR_FLAC_CACHE_L1_BITS_REMAINING(bs)); - bs->unalignedByteCount = 0; -#ifndef MA_DR_FLAC_NO_CRC - bs->crc16Cache = bs->cache >> bs->consumedBits; - bs->crc16CacheIgnoredBytes = bs->consumedBits >> 3; -#endif - return MA_TRUE; -} -static void ma_dr_flac__reset_cache(ma_dr_flac_bs* bs) -{ - bs->nextL2Line = MA_DR_FLAC_CACHE_L2_LINE_COUNT(bs); - bs->consumedBits = MA_DR_FLAC_CACHE_L1_SIZE_BITS(bs); - bs->cache = 0; - bs->unalignedByteCount = 0; - bs->unalignedCache = 0; -#ifndef MA_DR_FLAC_NO_CRC - bs->crc16Cache = 0; - bs->crc16CacheIgnoredBytes = 0; -#endif -} -static MA_INLINE ma_bool32 ma_dr_flac__read_uint32(ma_dr_flac_bs* bs, unsigned int bitCount, ma_uint32* pResultOut) -{ - MA_DR_FLAC_ASSERT(bs != NULL); - MA_DR_FLAC_ASSERT(pResultOut != NULL); - MA_DR_FLAC_ASSERT(bitCount > 0); - MA_DR_FLAC_ASSERT(bitCount <= 32); - if (bs->consumedBits == MA_DR_FLAC_CACHE_L1_SIZE_BITS(bs)) { - if (!ma_dr_flac__reload_cache(bs)) { - return MA_FALSE; - } - } - if (bitCount <= MA_DR_FLAC_CACHE_L1_BITS_REMAINING(bs)) { -#ifdef MA_64BIT - *pResultOut = (ma_uint32)MA_DR_FLAC_CACHE_L1_SELECT_AND_SHIFT(bs, bitCount); - bs->consumedBits += bitCount; - bs->cache <<= bitCount; -#else - if (bitCount < MA_DR_FLAC_CACHE_L1_SIZE_BITS(bs)) { - *pResultOut = (ma_uint32)MA_DR_FLAC_CACHE_L1_SELECT_AND_SHIFT(bs, bitCount); - bs->consumedBits += bitCount; - bs->cache <<= bitCount; - } else { - *pResultOut = (ma_uint32)bs->cache; - bs->consumedBits = MA_DR_FLAC_CACHE_L1_SIZE_BITS(bs); - bs->cache = 0; - } -#endif - return MA_TRUE; - } else { - ma_uint32 bitCountHi = MA_DR_FLAC_CACHE_L1_BITS_REMAINING(bs); - ma_uint32 bitCountLo = bitCount - bitCountHi; - ma_uint32 resultHi; - MA_DR_FLAC_ASSERT(bitCountHi > 0); - MA_DR_FLAC_ASSERT(bitCountHi < 32); - resultHi = (ma_uint32)MA_DR_FLAC_CACHE_L1_SELECT_AND_SHIFT(bs, bitCountHi); - if (!ma_dr_flac__reload_cache(bs)) { - return MA_FALSE; - } - if (bitCountLo > MA_DR_FLAC_CACHE_L1_BITS_REMAINING(bs)) { - return MA_FALSE; - } - *pResultOut = (resultHi << bitCountLo) | (ma_uint32)MA_DR_FLAC_CACHE_L1_SELECT_AND_SHIFT(bs, bitCountLo); - bs->consumedBits += bitCountLo; - bs->cache <<= bitCountLo; - return MA_TRUE; - } -} -static ma_bool32 ma_dr_flac__read_int32(ma_dr_flac_bs* bs, unsigned int bitCount, ma_int32* pResult) -{ - ma_uint32 result; - MA_DR_FLAC_ASSERT(bs != NULL); - MA_DR_FLAC_ASSERT(pResult != NULL); - MA_DR_FLAC_ASSERT(bitCount > 0); - MA_DR_FLAC_ASSERT(bitCount <= 32); - if (!ma_dr_flac__read_uint32(bs, bitCount, &result)) { - return MA_FALSE; - } - if (bitCount < 32) { - ma_uint32 signbit; - signbit = ((result >> (bitCount-1)) & 0x01); - result |= (~signbit + 1) << bitCount; - } - *pResult = (ma_int32)result; - return MA_TRUE; -} -#ifdef MA_64BIT -static ma_bool32 ma_dr_flac__read_uint64(ma_dr_flac_bs* bs, unsigned int bitCount, ma_uint64* pResultOut) -{ - ma_uint32 resultHi; - ma_uint32 resultLo; - MA_DR_FLAC_ASSERT(bitCount <= 64); - MA_DR_FLAC_ASSERT(bitCount > 32); - if (!ma_dr_flac__read_uint32(bs, bitCount - 32, &resultHi)) { - return MA_FALSE; - } - if (!ma_dr_flac__read_uint32(bs, 32, &resultLo)) { - return MA_FALSE; - } - *pResultOut = (((ma_uint64)resultHi) << 32) | ((ma_uint64)resultLo); - return MA_TRUE; -} -#endif -#if 0 -static ma_bool32 ma_dr_flac__read_int64(ma_dr_flac_bs* bs, unsigned int bitCount, ma_int64* pResultOut) -{ - ma_uint64 result; - ma_uint64 signbit; - MA_DR_FLAC_ASSERT(bitCount <= 64); - if (!ma_dr_flac__read_uint64(bs, bitCount, &result)) { - return MA_FALSE; - } - signbit = ((result >> (bitCount-1)) & 0x01); - result |= (~signbit + 1) << bitCount; - *pResultOut = (ma_int64)result; - return MA_TRUE; -} -#endif -static ma_bool32 ma_dr_flac__read_uint16(ma_dr_flac_bs* bs, unsigned int bitCount, ma_uint16* pResult) -{ - ma_uint32 result; - MA_DR_FLAC_ASSERT(bs != NULL); - MA_DR_FLAC_ASSERT(pResult != NULL); - MA_DR_FLAC_ASSERT(bitCount > 0); - MA_DR_FLAC_ASSERT(bitCount <= 16); - if (!ma_dr_flac__read_uint32(bs, bitCount, &result)) { - return MA_FALSE; - } - *pResult = (ma_uint16)result; - return MA_TRUE; -} -#if 0 -static ma_bool32 ma_dr_flac__read_int16(ma_dr_flac_bs* bs, unsigned int bitCount, ma_int16* pResult) -{ - ma_int32 result; - MA_DR_FLAC_ASSERT(bs != NULL); - MA_DR_FLAC_ASSERT(pResult != NULL); - MA_DR_FLAC_ASSERT(bitCount > 0); - MA_DR_FLAC_ASSERT(bitCount <= 16); - if (!ma_dr_flac__read_int32(bs, bitCount, &result)) { - return MA_FALSE; - } - *pResult = (ma_int16)result; - return MA_TRUE; -} -#endif -static ma_bool32 ma_dr_flac__read_uint8(ma_dr_flac_bs* bs, unsigned int bitCount, ma_uint8* pResult) -{ - ma_uint32 result; - MA_DR_FLAC_ASSERT(bs != NULL); - MA_DR_FLAC_ASSERT(pResult != NULL); - MA_DR_FLAC_ASSERT(bitCount > 0); - MA_DR_FLAC_ASSERT(bitCount <= 8); - if (!ma_dr_flac__read_uint32(bs, bitCount, &result)) { - return MA_FALSE; - } - *pResult = (ma_uint8)result; - return MA_TRUE; -} -static ma_bool32 ma_dr_flac__read_int8(ma_dr_flac_bs* bs, unsigned int bitCount, ma_int8* pResult) -{ - ma_int32 result; - MA_DR_FLAC_ASSERT(bs != NULL); - MA_DR_FLAC_ASSERT(pResult != NULL); - MA_DR_FLAC_ASSERT(bitCount > 0); - MA_DR_FLAC_ASSERT(bitCount <= 8); - if (!ma_dr_flac__read_int32(bs, bitCount, &result)) { - return MA_FALSE; - } - *pResult = (ma_int8)result; - return MA_TRUE; -} -static ma_bool32 ma_dr_flac__seek_bits(ma_dr_flac_bs* bs, size_t bitsToSeek) -{ - if (bitsToSeek <= MA_DR_FLAC_CACHE_L1_BITS_REMAINING(bs)) { - bs->consumedBits += (ma_uint32)bitsToSeek; - bs->cache <<= bitsToSeek; - return MA_TRUE; - } else { - bitsToSeek -= MA_DR_FLAC_CACHE_L1_BITS_REMAINING(bs); - bs->consumedBits += MA_DR_FLAC_CACHE_L1_BITS_REMAINING(bs); - bs->cache = 0; -#ifdef MA_64BIT - while (bitsToSeek >= MA_DR_FLAC_CACHE_L1_SIZE_BITS(bs)) { - ma_uint64 bin; - if (!ma_dr_flac__read_uint64(bs, MA_DR_FLAC_CACHE_L1_SIZE_BITS(bs), &bin)) { - return MA_FALSE; - } - bitsToSeek -= MA_DR_FLAC_CACHE_L1_SIZE_BITS(bs); - } -#else - while (bitsToSeek >= MA_DR_FLAC_CACHE_L1_SIZE_BITS(bs)) { - ma_uint32 bin; - if (!ma_dr_flac__read_uint32(bs, MA_DR_FLAC_CACHE_L1_SIZE_BITS(bs), &bin)) { - return MA_FALSE; - } - bitsToSeek -= MA_DR_FLAC_CACHE_L1_SIZE_BITS(bs); - } -#endif - while (bitsToSeek >= 8) { - ma_uint8 bin; - if (!ma_dr_flac__read_uint8(bs, 8, &bin)) { - return MA_FALSE; - } - bitsToSeek -= 8; - } - if (bitsToSeek > 0) { - ma_uint8 bin; - if (!ma_dr_flac__read_uint8(bs, (ma_uint32)bitsToSeek, &bin)) { - return MA_FALSE; - } - bitsToSeek = 0; - } - MA_DR_FLAC_ASSERT(bitsToSeek == 0); - return MA_TRUE; - } -} -static ma_bool32 ma_dr_flac__find_and_seek_to_next_sync_code(ma_dr_flac_bs* bs) -{ - MA_DR_FLAC_ASSERT(bs != NULL); - if (!ma_dr_flac__seek_bits(bs, MA_DR_FLAC_CACHE_L1_BITS_REMAINING(bs) & 7)) { - return MA_FALSE; - } - for (;;) { - ma_uint8 hi; -#ifndef MA_DR_FLAC_NO_CRC - ma_dr_flac__reset_crc16(bs); -#endif - if (!ma_dr_flac__read_uint8(bs, 8, &hi)) { - return MA_FALSE; - } - if (hi == 0xFF) { - ma_uint8 lo; - if (!ma_dr_flac__read_uint8(bs, 6, &lo)) { - return MA_FALSE; - } - if (lo == 0x3E) { - return MA_TRUE; - } else { - if (!ma_dr_flac__seek_bits(bs, MA_DR_FLAC_CACHE_L1_BITS_REMAINING(bs) & 7)) { - return MA_FALSE; - } - } - } - } -} -#if defined(MA_DR_FLAC_HAS_LZCNT_INTRINSIC) -#define MA_DR_FLAC_IMPLEMENT_CLZ_LZCNT -#endif -#if defined(_MSC_VER) && _MSC_VER >= 1400 && (defined(MA_X64) || defined(MA_X86)) && !defined(__clang__) -#define MA_DR_FLAC_IMPLEMENT_CLZ_MSVC -#endif -#if defined(__WATCOMC__) && defined(__386__) -#define MA_DR_FLAC_IMPLEMENT_CLZ_WATCOM -#endif -#ifdef __MRC__ -#include -#define MA_DR_FLAC_IMPLEMENT_CLZ_MRC -#endif -static MA_INLINE ma_uint32 ma_dr_flac__clz_software(ma_dr_flac_cache_t x) -{ - ma_uint32 n; - static ma_uint32 clz_table_4[] = { - 0, - 4, - 3, 3, - 2, 2, 2, 2, - 1, 1, 1, 1, 1, 1, 1, 1 - }; - if (x == 0) { - return sizeof(x)*8; - } - n = clz_table_4[x >> (sizeof(x)*8 - 4)]; - if (n == 0) { -#ifdef MA_64BIT - if ((x & ((ma_uint64)0xFFFFFFFF << 32)) == 0) { n = 32; x <<= 32; } - if ((x & ((ma_uint64)0xFFFF0000 << 32)) == 0) { n += 16; x <<= 16; } - if ((x & ((ma_uint64)0xFF000000 << 32)) == 0) { n += 8; x <<= 8; } - if ((x & ((ma_uint64)0xF0000000 << 32)) == 0) { n += 4; x <<= 4; } -#else - if ((x & 0xFFFF0000) == 0) { n = 16; x <<= 16; } - if ((x & 0xFF000000) == 0) { n += 8; x <<= 8; } - if ((x & 0xF0000000) == 0) { n += 4; x <<= 4; } -#endif - n += clz_table_4[x >> (sizeof(x)*8 - 4)]; - } - return n - 1; -} -#ifdef MA_DR_FLAC_IMPLEMENT_CLZ_LZCNT -static MA_INLINE ma_bool32 ma_dr_flac__is_lzcnt_supported(void) -{ -#if defined(MA_DR_FLAC_HAS_LZCNT_INTRINSIC) && defined(MA_ARM) && (defined(__ARM_ARCH) && __ARM_ARCH >= 5) - return MA_TRUE; -#elif defined(__MRC__) - return MA_TRUE; -#else - #ifdef MA_DR_FLAC_HAS_LZCNT_INTRINSIC - return ma_dr_flac__gIsLZCNTSupported; - #else - return MA_FALSE; - #endif -#endif -} -static MA_INLINE ma_uint32 ma_dr_flac__clz_lzcnt(ma_dr_flac_cache_t x) -{ -#if defined(_MSC_VER) - #ifdef MA_64BIT - return (ma_uint32)__lzcnt64(x); - #else - return (ma_uint32)__lzcnt(x); - #endif -#else - #if defined(__GNUC__) || defined(__clang__) - #if defined(MA_X64) - { - ma_uint64 r; - __asm__ __volatile__ ( - "lzcnt{ %1, %0| %0, %1}" : "=r"(r) : "r"(x) : "cc" - ); - return (ma_uint32)r; - } - #elif defined(MA_X86) - { - ma_uint32 r; - __asm__ __volatile__ ( - "lzcnt{l %1, %0| %0, %1}" : "=r"(r) : "r"(x) : "cc" - ); - return r; - } - #elif defined(MA_ARM) && (defined(__ARM_ARCH) && __ARM_ARCH >= 5) && !defined(__ARM_ARCH_6M__) && !defined(MA_64BIT) - { - unsigned int r; - __asm__ __volatile__ ( - #if defined(MA_64BIT) - "clz %w[out], %w[in]" : [out]"=r"(r) : [in]"r"(x) - #else - "clz %[out], %[in]" : [out]"=r"(r) : [in]"r"(x) - #endif - ); - return r; - } - #else - if (x == 0) { - return sizeof(x)*8; - } - #ifdef MA_64BIT - return (ma_uint32)__builtin_clzll((ma_uint64)x); - #else - return (ma_uint32)__builtin_clzl((ma_uint32)x); - #endif - #endif - #else - #error "This compiler does not support the lzcnt intrinsic." - #endif -#endif -} -#endif -#ifdef MA_DR_FLAC_IMPLEMENT_CLZ_MSVC -#include -static MA_INLINE ma_uint32 ma_dr_flac__clz_msvc(ma_dr_flac_cache_t x) -{ - ma_uint32 n; - if (x == 0) { - return sizeof(x)*8; - } -#ifdef MA_64BIT - _BitScanReverse64((unsigned long*)&n, x); -#else - _BitScanReverse((unsigned long*)&n, x); -#endif - return sizeof(x)*8 - n - 1; -} -#endif -#ifdef MA_DR_FLAC_IMPLEMENT_CLZ_WATCOM -static __inline ma_uint32 ma_dr_flac__clz_watcom (ma_uint32); -#ifdef MA_DR_FLAC_IMPLEMENT_CLZ_WATCOM_LZCNT -#pragma aux ma_dr_flac__clz_watcom_lzcnt = \ - "db 0F3h, 0Fh, 0BDh, 0C0h" \ - parm [eax] \ - value [eax] \ - modify nomemory; -#else -#pragma aux ma_dr_flac__clz_watcom = \ - "bsr eax, eax" \ - "xor eax, 31" \ - parm [eax] nomemory \ - value [eax] \ - modify exact [eax] nomemory; -#endif -#endif -static MA_INLINE ma_uint32 ma_dr_flac__clz(ma_dr_flac_cache_t x) -{ -#ifdef MA_DR_FLAC_IMPLEMENT_CLZ_LZCNT - if (ma_dr_flac__is_lzcnt_supported()) { - return ma_dr_flac__clz_lzcnt(x); - } else -#endif - { -#ifdef MA_DR_FLAC_IMPLEMENT_CLZ_MSVC - return ma_dr_flac__clz_msvc(x); -#elif defined(MA_DR_FLAC_IMPLEMENT_CLZ_WATCOM_LZCNT) - return ma_dr_flac__clz_watcom_lzcnt(x); -#elif defined(MA_DR_FLAC_IMPLEMENT_CLZ_WATCOM) - return (x == 0) ? sizeof(x)*8 : ma_dr_flac__clz_watcom(x); -#elif defined(__MRC__) - return __cntlzw(x); -#else - return ma_dr_flac__clz_software(x); -#endif - } -} -static MA_INLINE ma_bool32 ma_dr_flac__seek_past_next_set_bit(ma_dr_flac_bs* bs, unsigned int* pOffsetOut) -{ - ma_uint32 zeroCounter = 0; - ma_uint32 setBitOffsetPlus1; - while (bs->cache == 0) { - zeroCounter += (ma_uint32)MA_DR_FLAC_CACHE_L1_BITS_REMAINING(bs); - if (!ma_dr_flac__reload_cache(bs)) { - return MA_FALSE; - } - } - if (bs->cache == 1) { - *pOffsetOut = zeroCounter + (ma_uint32)MA_DR_FLAC_CACHE_L1_BITS_REMAINING(bs) - 1; - if (!ma_dr_flac__reload_cache(bs)) { - return MA_FALSE; - } - return MA_TRUE; - } - setBitOffsetPlus1 = ma_dr_flac__clz(bs->cache); - setBitOffsetPlus1 += 1; - if (setBitOffsetPlus1 > MA_DR_FLAC_CACHE_L1_BITS_REMAINING(bs)) { - return MA_FALSE; - } - bs->consumedBits += setBitOffsetPlus1; - bs->cache <<= setBitOffsetPlus1; - *pOffsetOut = zeroCounter + setBitOffsetPlus1 - 1; - return MA_TRUE; -} -static ma_bool32 ma_dr_flac__seek_to_byte(ma_dr_flac_bs* bs, ma_uint64 offsetFromStart) -{ - MA_DR_FLAC_ASSERT(bs != NULL); - MA_DR_FLAC_ASSERT(offsetFromStart > 0); - if (offsetFromStart > 0x7FFFFFFF) { - ma_uint64 bytesRemaining = offsetFromStart; - if (!bs->onSeek(bs->pUserData, 0x7FFFFFFF, ma_dr_flac_seek_origin_start)) { - return MA_FALSE; - } - bytesRemaining -= 0x7FFFFFFF; - while (bytesRemaining > 0x7FFFFFFF) { - if (!bs->onSeek(bs->pUserData, 0x7FFFFFFF, ma_dr_flac_seek_origin_current)) { - return MA_FALSE; - } - bytesRemaining -= 0x7FFFFFFF; - } - if (bytesRemaining > 0) { - if (!bs->onSeek(bs->pUserData, (int)bytesRemaining, ma_dr_flac_seek_origin_current)) { - return MA_FALSE; - } - } - } else { - if (!bs->onSeek(bs->pUserData, (int)offsetFromStart, ma_dr_flac_seek_origin_start)) { - return MA_FALSE; - } - } - ma_dr_flac__reset_cache(bs); - return MA_TRUE; -} -static ma_result ma_dr_flac__read_utf8_coded_number(ma_dr_flac_bs* bs, ma_uint64* pNumberOut, ma_uint8* pCRCOut) -{ - ma_uint8 crc; - ma_uint64 result; - ma_uint8 utf8[7] = {0}; - int byteCount; - int i; - MA_DR_FLAC_ASSERT(bs != NULL); - MA_DR_FLAC_ASSERT(pNumberOut != NULL); - MA_DR_FLAC_ASSERT(pCRCOut != NULL); - crc = *pCRCOut; - if (!ma_dr_flac__read_uint8(bs, 8, utf8)) { - *pNumberOut = 0; - return MA_AT_END; - } - crc = ma_dr_flac_crc8(crc, utf8[0], 8); - if ((utf8[0] & 0x80) == 0) { - *pNumberOut = utf8[0]; - *pCRCOut = crc; - return MA_SUCCESS; - } - if ((utf8[0] & 0xE0) == 0xC0) { - byteCount = 2; - } else if ((utf8[0] & 0xF0) == 0xE0) { - byteCount = 3; - } else if ((utf8[0] & 0xF8) == 0xF0) { - byteCount = 4; - } else if ((utf8[0] & 0xFC) == 0xF8) { - byteCount = 5; - } else if ((utf8[0] & 0xFE) == 0xFC) { - byteCount = 6; - } else if ((utf8[0] & 0xFF) == 0xFE) { - byteCount = 7; - } else { - *pNumberOut = 0; - return MA_CRC_MISMATCH; - } - MA_DR_FLAC_ASSERT(byteCount > 1); - result = (ma_uint64)(utf8[0] & (0xFF >> (byteCount + 1))); - for (i = 1; i < byteCount; ++i) { - if (!ma_dr_flac__read_uint8(bs, 8, utf8 + i)) { - *pNumberOut = 0; - return MA_AT_END; - } - crc = ma_dr_flac_crc8(crc, utf8[i], 8); - result = (result << 6) | (utf8[i] & 0x3F); - } - *pNumberOut = result; - *pCRCOut = crc; - return MA_SUCCESS; -} -static MA_INLINE ma_uint32 ma_dr_flac__ilog2_u32(ma_uint32 x) -{ -#if 1 - ma_uint32 result = 0; - while (x > 0) { - result += 1; - x >>= 1; - } - return result; -#endif -} -static MA_INLINE ma_bool32 ma_dr_flac__use_64_bit_prediction(ma_uint32 bitsPerSample, ma_uint32 order, ma_uint32 precision) -{ - return bitsPerSample + precision + ma_dr_flac__ilog2_u32(order) > 32; -} -#if defined(__clang__) -__attribute__((no_sanitize("signed-integer-overflow"))) -#endif -static MA_INLINE ma_int32 ma_dr_flac__calculate_prediction_32(ma_uint32 order, ma_int32 shift, const ma_int32* coefficients, ma_int32* pDecodedSamples) -{ - ma_int32 prediction = 0; - MA_DR_FLAC_ASSERT(order <= 32); - switch (order) - { - case 32: prediction += coefficients[31] * pDecodedSamples[-32]; - case 31: prediction += coefficients[30] * pDecodedSamples[-31]; - case 30: prediction += coefficients[29] * pDecodedSamples[-30]; - case 29: prediction += coefficients[28] * pDecodedSamples[-29]; - case 28: prediction += coefficients[27] * pDecodedSamples[-28]; - case 27: prediction += coefficients[26] * pDecodedSamples[-27]; - case 26: prediction += coefficients[25] * pDecodedSamples[-26]; - case 25: prediction += coefficients[24] * pDecodedSamples[-25]; - case 24: prediction += coefficients[23] * pDecodedSamples[-24]; - case 23: prediction += coefficients[22] * pDecodedSamples[-23]; - case 22: prediction += coefficients[21] * pDecodedSamples[-22]; - case 21: prediction += coefficients[20] * pDecodedSamples[-21]; - case 20: prediction += coefficients[19] * pDecodedSamples[-20]; - case 19: prediction += coefficients[18] * pDecodedSamples[-19]; - case 18: prediction += coefficients[17] * pDecodedSamples[-18]; - case 17: prediction += coefficients[16] * pDecodedSamples[-17]; - case 16: prediction += coefficients[15] * pDecodedSamples[-16]; - case 15: prediction += coefficients[14] * pDecodedSamples[-15]; - case 14: prediction += coefficients[13] * pDecodedSamples[-14]; - case 13: prediction += coefficients[12] * pDecodedSamples[-13]; - case 12: prediction += coefficients[11] * pDecodedSamples[-12]; - case 11: prediction += coefficients[10] * pDecodedSamples[-11]; - case 10: prediction += coefficients[ 9] * pDecodedSamples[-10]; - case 9: prediction += coefficients[ 8] * pDecodedSamples[- 9]; - case 8: prediction += coefficients[ 7] * pDecodedSamples[- 8]; - case 7: prediction += coefficients[ 6] * pDecodedSamples[- 7]; - case 6: prediction += coefficients[ 5] * pDecodedSamples[- 6]; - case 5: prediction += coefficients[ 4] * pDecodedSamples[- 5]; - case 4: prediction += coefficients[ 3] * pDecodedSamples[- 4]; - case 3: prediction += coefficients[ 2] * pDecodedSamples[- 3]; - case 2: prediction += coefficients[ 1] * pDecodedSamples[- 2]; - case 1: prediction += coefficients[ 0] * pDecodedSamples[- 1]; - } - return (ma_int32)(prediction >> shift); -} -static MA_INLINE ma_int32 ma_dr_flac__calculate_prediction_64(ma_uint32 order, ma_int32 shift, const ma_int32* coefficients, ma_int32* pDecodedSamples) -{ - ma_int64 prediction; - MA_DR_FLAC_ASSERT(order <= 32); -#ifndef MA_64BIT - if (order == 8) - { - prediction = coefficients[0] * (ma_int64)pDecodedSamples[-1]; - prediction += coefficients[1] * (ma_int64)pDecodedSamples[-2]; - prediction += coefficients[2] * (ma_int64)pDecodedSamples[-3]; - prediction += coefficients[3] * (ma_int64)pDecodedSamples[-4]; - prediction += coefficients[4] * (ma_int64)pDecodedSamples[-5]; - prediction += coefficients[5] * (ma_int64)pDecodedSamples[-6]; - prediction += coefficients[6] * (ma_int64)pDecodedSamples[-7]; - prediction += coefficients[7] * (ma_int64)pDecodedSamples[-8]; - } - else if (order == 7) - { - prediction = coefficients[0] * (ma_int64)pDecodedSamples[-1]; - prediction += coefficients[1] * (ma_int64)pDecodedSamples[-2]; - prediction += coefficients[2] * (ma_int64)pDecodedSamples[-3]; - prediction += coefficients[3] * (ma_int64)pDecodedSamples[-4]; - prediction += coefficients[4] * (ma_int64)pDecodedSamples[-5]; - prediction += coefficients[5] * (ma_int64)pDecodedSamples[-6]; - prediction += coefficients[6] * (ma_int64)pDecodedSamples[-7]; - } - else if (order == 3) - { - prediction = coefficients[0] * (ma_int64)pDecodedSamples[-1]; - prediction += coefficients[1] * (ma_int64)pDecodedSamples[-2]; - prediction += coefficients[2] * (ma_int64)pDecodedSamples[-3]; - } - else if (order == 6) - { - prediction = coefficients[0] * (ma_int64)pDecodedSamples[-1]; - prediction += coefficients[1] * (ma_int64)pDecodedSamples[-2]; - prediction += coefficients[2] * (ma_int64)pDecodedSamples[-3]; - prediction += coefficients[3] * (ma_int64)pDecodedSamples[-4]; - prediction += coefficients[4] * (ma_int64)pDecodedSamples[-5]; - prediction += coefficients[5] * (ma_int64)pDecodedSamples[-6]; - } - else if (order == 5) - { - prediction = coefficients[0] * (ma_int64)pDecodedSamples[-1]; - prediction += coefficients[1] * (ma_int64)pDecodedSamples[-2]; - prediction += coefficients[2] * (ma_int64)pDecodedSamples[-3]; - prediction += coefficients[3] * (ma_int64)pDecodedSamples[-4]; - prediction += coefficients[4] * (ma_int64)pDecodedSamples[-5]; - } - else if (order == 4) - { - prediction = coefficients[0] * (ma_int64)pDecodedSamples[-1]; - prediction += coefficients[1] * (ma_int64)pDecodedSamples[-2]; - prediction += coefficients[2] * (ma_int64)pDecodedSamples[-3]; - prediction += coefficients[3] * (ma_int64)pDecodedSamples[-4]; - } - else if (order == 12) - { - prediction = coefficients[0] * (ma_int64)pDecodedSamples[-1]; - prediction += coefficients[1] * (ma_int64)pDecodedSamples[-2]; - prediction += coefficients[2] * (ma_int64)pDecodedSamples[-3]; - prediction += coefficients[3] * (ma_int64)pDecodedSamples[-4]; - prediction += coefficients[4] * (ma_int64)pDecodedSamples[-5]; - prediction += coefficients[5] * (ma_int64)pDecodedSamples[-6]; - prediction += coefficients[6] * (ma_int64)pDecodedSamples[-7]; - prediction += coefficients[7] * (ma_int64)pDecodedSamples[-8]; - prediction += coefficients[8] * (ma_int64)pDecodedSamples[-9]; - prediction += coefficients[9] * (ma_int64)pDecodedSamples[-10]; - prediction += coefficients[10] * (ma_int64)pDecodedSamples[-11]; - prediction += coefficients[11] * (ma_int64)pDecodedSamples[-12]; - } - else if (order == 2) - { - prediction = coefficients[0] * (ma_int64)pDecodedSamples[-1]; - prediction += coefficients[1] * (ma_int64)pDecodedSamples[-2]; - } - else if (order == 1) - { - prediction = coefficients[0] * (ma_int64)pDecodedSamples[-1]; - } - else if (order == 10) - { - prediction = coefficients[0] * (ma_int64)pDecodedSamples[-1]; - prediction += coefficients[1] * (ma_int64)pDecodedSamples[-2]; - prediction += coefficients[2] * (ma_int64)pDecodedSamples[-3]; - prediction += coefficients[3] * (ma_int64)pDecodedSamples[-4]; - prediction += coefficients[4] * (ma_int64)pDecodedSamples[-5]; - prediction += coefficients[5] * (ma_int64)pDecodedSamples[-6]; - prediction += coefficients[6] * (ma_int64)pDecodedSamples[-7]; - prediction += coefficients[7] * (ma_int64)pDecodedSamples[-8]; - prediction += coefficients[8] * (ma_int64)pDecodedSamples[-9]; - prediction += coefficients[9] * (ma_int64)pDecodedSamples[-10]; - } - else if (order == 9) - { - prediction = coefficients[0] * (ma_int64)pDecodedSamples[-1]; - prediction += coefficients[1] * (ma_int64)pDecodedSamples[-2]; - prediction += coefficients[2] * (ma_int64)pDecodedSamples[-3]; - prediction += coefficients[3] * (ma_int64)pDecodedSamples[-4]; - prediction += coefficients[4] * (ma_int64)pDecodedSamples[-5]; - prediction += coefficients[5] * (ma_int64)pDecodedSamples[-6]; - prediction += coefficients[6] * (ma_int64)pDecodedSamples[-7]; - prediction += coefficients[7] * (ma_int64)pDecodedSamples[-8]; - prediction += coefficients[8] * (ma_int64)pDecodedSamples[-9]; - } - else if (order == 11) - { - prediction = coefficients[0] * (ma_int64)pDecodedSamples[-1]; - prediction += coefficients[1] * (ma_int64)pDecodedSamples[-2]; - prediction += coefficients[2] * (ma_int64)pDecodedSamples[-3]; - prediction += coefficients[3] * (ma_int64)pDecodedSamples[-4]; - prediction += coefficients[4] * (ma_int64)pDecodedSamples[-5]; - prediction += coefficients[5] * (ma_int64)pDecodedSamples[-6]; - prediction += coefficients[6] * (ma_int64)pDecodedSamples[-7]; - prediction += coefficients[7] * (ma_int64)pDecodedSamples[-8]; - prediction += coefficients[8] * (ma_int64)pDecodedSamples[-9]; - prediction += coefficients[9] * (ma_int64)pDecodedSamples[-10]; - prediction += coefficients[10] * (ma_int64)pDecodedSamples[-11]; - } - else - { - int j; - prediction = 0; - for (j = 0; j < (int)order; ++j) { - prediction += coefficients[j] * (ma_int64)pDecodedSamples[-j-1]; - } - } -#endif -#ifdef MA_64BIT - prediction = 0; - switch (order) - { - case 32: prediction += coefficients[31] * (ma_int64)pDecodedSamples[-32]; - case 31: prediction += coefficients[30] * (ma_int64)pDecodedSamples[-31]; - case 30: prediction += coefficients[29] * (ma_int64)pDecodedSamples[-30]; - case 29: prediction += coefficients[28] * (ma_int64)pDecodedSamples[-29]; - case 28: prediction += coefficients[27] * (ma_int64)pDecodedSamples[-28]; - case 27: prediction += coefficients[26] * (ma_int64)pDecodedSamples[-27]; - case 26: prediction += coefficients[25] * (ma_int64)pDecodedSamples[-26]; - case 25: prediction += coefficients[24] * (ma_int64)pDecodedSamples[-25]; - case 24: prediction += coefficients[23] * (ma_int64)pDecodedSamples[-24]; - case 23: prediction += coefficients[22] * (ma_int64)pDecodedSamples[-23]; - case 22: prediction += coefficients[21] * (ma_int64)pDecodedSamples[-22]; - case 21: prediction += coefficients[20] * (ma_int64)pDecodedSamples[-21]; - case 20: prediction += coefficients[19] * (ma_int64)pDecodedSamples[-20]; - case 19: prediction += coefficients[18] * (ma_int64)pDecodedSamples[-19]; - case 18: prediction += coefficients[17] * (ma_int64)pDecodedSamples[-18]; - case 17: prediction += coefficients[16] * (ma_int64)pDecodedSamples[-17]; - case 16: prediction += coefficients[15] * (ma_int64)pDecodedSamples[-16]; - case 15: prediction += coefficients[14] * (ma_int64)pDecodedSamples[-15]; - case 14: prediction += coefficients[13] * (ma_int64)pDecodedSamples[-14]; - case 13: prediction += coefficients[12] * (ma_int64)pDecodedSamples[-13]; - case 12: prediction += coefficients[11] * (ma_int64)pDecodedSamples[-12]; - case 11: prediction += coefficients[10] * (ma_int64)pDecodedSamples[-11]; - case 10: prediction += coefficients[ 9] * (ma_int64)pDecodedSamples[-10]; - case 9: prediction += coefficients[ 8] * (ma_int64)pDecodedSamples[- 9]; - case 8: prediction += coefficients[ 7] * (ma_int64)pDecodedSamples[- 8]; - case 7: prediction += coefficients[ 6] * (ma_int64)pDecodedSamples[- 7]; - case 6: prediction += coefficients[ 5] * (ma_int64)pDecodedSamples[- 6]; - case 5: prediction += coefficients[ 4] * (ma_int64)pDecodedSamples[- 5]; - case 4: prediction += coefficients[ 3] * (ma_int64)pDecodedSamples[- 4]; - case 3: prediction += coefficients[ 2] * (ma_int64)pDecodedSamples[- 3]; - case 2: prediction += coefficients[ 1] * (ma_int64)pDecodedSamples[- 2]; - case 1: prediction += coefficients[ 0] * (ma_int64)pDecodedSamples[- 1]; - } -#endif - return (ma_int32)(prediction >> shift); -} -#if 0 -static ma_bool32 ma_dr_flac__decode_samples_with_residual__rice__reference(ma_dr_flac_bs* bs, ma_uint32 bitsPerSample, ma_uint32 count, ma_uint8 riceParam, ma_uint32 lpcOrder, ma_int32 lpcShift, ma_uint32 lpcPrecision, const ma_int32* coefficients, ma_int32* pSamplesOut) -{ - ma_uint32 i; - MA_DR_FLAC_ASSERT(bs != NULL); - MA_DR_FLAC_ASSERT(pSamplesOut != NULL); - for (i = 0; i < count; ++i) { - ma_uint32 zeroCounter = 0; - for (;;) { - ma_uint8 bit; - if (!ma_dr_flac__read_uint8(bs, 1, &bit)) { - return MA_FALSE; - } - if (bit == 0) { - zeroCounter += 1; - } else { - break; - } - } - ma_uint32 decodedRice; - if (riceParam > 0) { - if (!ma_dr_flac__read_uint32(bs, riceParam, &decodedRice)) { - return MA_FALSE; - } - } else { - decodedRice = 0; - } - decodedRice |= (zeroCounter << riceParam); - if ((decodedRice & 0x01)) { - decodedRice = ~(decodedRice >> 1); - } else { - decodedRice = (decodedRice >> 1); - } - if (ma_dr_flac__use_64_bit_prediction(bitsPerSample, lpcOrder, lpcPrecision)) { - pSamplesOut[i] = decodedRice + ma_dr_flac__calculate_prediction_64(lpcOrder, lpcShift, coefficients, pSamplesOut + i); - } else { - pSamplesOut[i] = decodedRice + ma_dr_flac__calculate_prediction_32(lpcOrder, lpcShift, coefficients, pSamplesOut + i); - } - } - return MA_TRUE; -} -#endif -#if 0 -static ma_bool32 ma_dr_flac__read_rice_parts__reference(ma_dr_flac_bs* bs, ma_uint8 riceParam, ma_uint32* pZeroCounterOut, ma_uint32* pRiceParamPartOut) -{ - ma_uint32 zeroCounter = 0; - ma_uint32 decodedRice; - for (;;) { - ma_uint8 bit; - if (!ma_dr_flac__read_uint8(bs, 1, &bit)) { - return MA_FALSE; - } - if (bit == 0) { - zeroCounter += 1; - } else { - break; - } - } - if (riceParam > 0) { - if (!ma_dr_flac__read_uint32(bs, riceParam, &decodedRice)) { - return MA_FALSE; - } - } else { - decodedRice = 0; - } - *pZeroCounterOut = zeroCounter; - *pRiceParamPartOut = decodedRice; - return MA_TRUE; -} -#endif -#if 0 -static MA_INLINE ma_bool32 ma_dr_flac__read_rice_parts(ma_dr_flac_bs* bs, ma_uint8 riceParam, ma_uint32* pZeroCounterOut, ma_uint32* pRiceParamPartOut) -{ - ma_dr_flac_cache_t riceParamMask; - ma_uint32 zeroCounter; - ma_uint32 setBitOffsetPlus1; - ma_uint32 riceParamPart; - ma_uint32 riceLength; - MA_DR_FLAC_ASSERT(riceParam > 0); - riceParamMask = MA_DR_FLAC_CACHE_L1_SELECTION_MASK(riceParam); - zeroCounter = 0; - while (bs->cache == 0) { - zeroCounter += (ma_uint32)MA_DR_FLAC_CACHE_L1_BITS_REMAINING(bs); - if (!ma_dr_flac__reload_cache(bs)) { - return MA_FALSE; - } - } - setBitOffsetPlus1 = ma_dr_flac__clz(bs->cache); - zeroCounter += setBitOffsetPlus1; - setBitOffsetPlus1 += 1; - riceLength = setBitOffsetPlus1 + riceParam; - if (riceLength < MA_DR_FLAC_CACHE_L1_BITS_REMAINING(bs)) { - riceParamPart = (ma_uint32)((bs->cache & (riceParamMask >> setBitOffsetPlus1)) >> MA_DR_FLAC_CACHE_L1_SELECTION_SHIFT(bs, riceLength)); - bs->consumedBits += riceLength; - bs->cache <<= riceLength; - } else { - ma_uint32 bitCountLo; - ma_dr_flac_cache_t resultHi; - bs->consumedBits += riceLength; - bs->cache <<= setBitOffsetPlus1 & (MA_DR_FLAC_CACHE_L1_SIZE_BITS(bs)-1); - bitCountLo = bs->consumedBits - MA_DR_FLAC_CACHE_L1_SIZE_BITS(bs); - resultHi = MA_DR_FLAC_CACHE_L1_SELECT_AND_SHIFT(bs, riceParam); - if (bs->nextL2Line < MA_DR_FLAC_CACHE_L2_LINE_COUNT(bs)) { -#ifndef MA_DR_FLAC_NO_CRC - ma_dr_flac__update_crc16(bs); -#endif - bs->cache = ma_dr_flac__be2host__cache_line(bs->cacheL2[bs->nextL2Line++]); - bs->consumedBits = 0; -#ifndef MA_DR_FLAC_NO_CRC - bs->crc16Cache = bs->cache; -#endif - } else { - if (!ma_dr_flac__reload_cache(bs)) { - return MA_FALSE; - } - if (bitCountLo > MA_DR_FLAC_CACHE_L1_BITS_REMAINING(bs)) { - return MA_FALSE; - } - } - riceParamPart = (ma_uint32)(resultHi | MA_DR_FLAC_CACHE_L1_SELECT_AND_SHIFT_SAFE(bs, bitCountLo)); - bs->consumedBits += bitCountLo; - bs->cache <<= bitCountLo; - } - pZeroCounterOut[0] = zeroCounter; - pRiceParamPartOut[0] = riceParamPart; - return MA_TRUE; -} -#endif -static MA_INLINE ma_bool32 ma_dr_flac__read_rice_parts_x1(ma_dr_flac_bs* bs, ma_uint8 riceParam, ma_uint32* pZeroCounterOut, ma_uint32* pRiceParamPartOut) -{ - ma_uint32 riceParamPlus1 = riceParam + 1; - ma_uint32 riceParamPlus1Shift = MA_DR_FLAC_CACHE_L1_SELECTION_SHIFT(bs, riceParamPlus1); - ma_uint32 riceParamPlus1MaxConsumedBits = MA_DR_FLAC_CACHE_L1_SIZE_BITS(bs) - riceParamPlus1; - ma_dr_flac_cache_t bs_cache = bs->cache; - ma_uint32 bs_consumedBits = bs->consumedBits; - ma_uint32 lzcount = ma_dr_flac__clz(bs_cache); - if (lzcount < sizeof(bs_cache)*8) { - pZeroCounterOut[0] = lzcount; - extract_rice_param_part: - bs_cache <<= lzcount; - bs_consumedBits += lzcount; - if (bs_consumedBits <= riceParamPlus1MaxConsumedBits) { - pRiceParamPartOut[0] = (ma_uint32)(bs_cache >> riceParamPlus1Shift); - bs_cache <<= riceParamPlus1; - bs_consumedBits += riceParamPlus1; - } else { - ma_uint32 riceParamPartHi; - ma_uint32 riceParamPartLo; - ma_uint32 riceParamPartLoBitCount; - riceParamPartHi = (ma_uint32)(bs_cache >> riceParamPlus1Shift); - riceParamPartLoBitCount = bs_consumedBits - riceParamPlus1MaxConsumedBits; - MA_DR_FLAC_ASSERT(riceParamPartLoBitCount > 0 && riceParamPartLoBitCount < 32); - if (bs->nextL2Line < MA_DR_FLAC_CACHE_L2_LINE_COUNT(bs)) { - #ifndef MA_DR_FLAC_NO_CRC - ma_dr_flac__update_crc16(bs); - #endif - bs_cache = ma_dr_flac__be2host__cache_line(bs->cacheL2[bs->nextL2Line++]); - bs_consumedBits = riceParamPartLoBitCount; - #ifndef MA_DR_FLAC_NO_CRC - bs->crc16Cache = bs_cache; - #endif - } else { - if (!ma_dr_flac__reload_cache(bs)) { - return MA_FALSE; - } - if (riceParamPartLoBitCount > MA_DR_FLAC_CACHE_L1_BITS_REMAINING(bs)) { - return MA_FALSE; - } - bs_cache = bs->cache; - bs_consumedBits = bs->consumedBits + riceParamPartLoBitCount; - } - riceParamPartLo = (ma_uint32)(bs_cache >> (MA_DR_FLAC_CACHE_L1_SELECTION_SHIFT(bs, riceParamPartLoBitCount))); - pRiceParamPartOut[0] = riceParamPartHi | riceParamPartLo; - bs_cache <<= riceParamPartLoBitCount; - } - } else { - ma_uint32 zeroCounter = (ma_uint32)(MA_DR_FLAC_CACHE_L1_SIZE_BITS(bs) - bs_consumedBits); - for (;;) { - if (bs->nextL2Line < MA_DR_FLAC_CACHE_L2_LINE_COUNT(bs)) { - #ifndef MA_DR_FLAC_NO_CRC - ma_dr_flac__update_crc16(bs); - #endif - bs_cache = ma_dr_flac__be2host__cache_line(bs->cacheL2[bs->nextL2Line++]); - bs_consumedBits = 0; - #ifndef MA_DR_FLAC_NO_CRC - bs->crc16Cache = bs_cache; - #endif - } else { - if (!ma_dr_flac__reload_cache(bs)) { - return MA_FALSE; - } - bs_cache = bs->cache; - bs_consumedBits = bs->consumedBits; - } - lzcount = ma_dr_flac__clz(bs_cache); - zeroCounter += lzcount; - if (lzcount < sizeof(bs_cache)*8) { - break; - } - } - pZeroCounterOut[0] = zeroCounter; - goto extract_rice_param_part; - } - bs->cache = bs_cache; - bs->consumedBits = bs_consumedBits; - return MA_TRUE; -} -static MA_INLINE ma_bool32 ma_dr_flac__seek_rice_parts(ma_dr_flac_bs* bs, ma_uint8 riceParam) -{ - ma_uint32 riceParamPlus1 = riceParam + 1; - ma_uint32 riceParamPlus1MaxConsumedBits = MA_DR_FLAC_CACHE_L1_SIZE_BITS(bs) - riceParamPlus1; - ma_dr_flac_cache_t bs_cache = bs->cache; - ma_uint32 bs_consumedBits = bs->consumedBits; - ma_uint32 lzcount = ma_dr_flac__clz(bs_cache); - if (lzcount < sizeof(bs_cache)*8) { - extract_rice_param_part: - bs_cache <<= lzcount; - bs_consumedBits += lzcount; - if (bs_consumedBits <= riceParamPlus1MaxConsumedBits) { - bs_cache <<= riceParamPlus1; - bs_consumedBits += riceParamPlus1; - } else { - ma_uint32 riceParamPartLoBitCount = bs_consumedBits - riceParamPlus1MaxConsumedBits; - MA_DR_FLAC_ASSERT(riceParamPartLoBitCount > 0 && riceParamPartLoBitCount < 32); - if (bs->nextL2Line < MA_DR_FLAC_CACHE_L2_LINE_COUNT(bs)) { - #ifndef MA_DR_FLAC_NO_CRC - ma_dr_flac__update_crc16(bs); - #endif - bs_cache = ma_dr_flac__be2host__cache_line(bs->cacheL2[bs->nextL2Line++]); - bs_consumedBits = riceParamPartLoBitCount; - #ifndef MA_DR_FLAC_NO_CRC - bs->crc16Cache = bs_cache; - #endif - } else { - if (!ma_dr_flac__reload_cache(bs)) { - return MA_FALSE; - } - if (riceParamPartLoBitCount > MA_DR_FLAC_CACHE_L1_BITS_REMAINING(bs)) { - return MA_FALSE; - } - bs_cache = bs->cache; - bs_consumedBits = bs->consumedBits + riceParamPartLoBitCount; - } - bs_cache <<= riceParamPartLoBitCount; - } - } else { - for (;;) { - if (bs->nextL2Line < MA_DR_FLAC_CACHE_L2_LINE_COUNT(bs)) { - #ifndef MA_DR_FLAC_NO_CRC - ma_dr_flac__update_crc16(bs); - #endif - bs_cache = ma_dr_flac__be2host__cache_line(bs->cacheL2[bs->nextL2Line++]); - bs_consumedBits = 0; - #ifndef MA_DR_FLAC_NO_CRC - bs->crc16Cache = bs_cache; - #endif - } else { - if (!ma_dr_flac__reload_cache(bs)) { - return MA_FALSE; - } - bs_cache = bs->cache; - bs_consumedBits = bs->consumedBits; - } - lzcount = ma_dr_flac__clz(bs_cache); - if (lzcount < sizeof(bs_cache)*8) { - break; - } - } - goto extract_rice_param_part; - } - bs->cache = bs_cache; - bs->consumedBits = bs_consumedBits; - return MA_TRUE; -} -static ma_bool32 ma_dr_flac__decode_samples_with_residual__rice__scalar_zeroorder(ma_dr_flac_bs* bs, ma_uint32 bitsPerSample, ma_uint32 count, ma_uint8 riceParam, ma_uint32 order, ma_int32 shift, const ma_int32* coefficients, ma_int32* pSamplesOut) -{ - ma_uint32 t[2] = {0x00000000, 0xFFFFFFFF}; - ma_uint32 zeroCountPart0; - ma_uint32 riceParamPart0; - ma_uint32 riceParamMask; - ma_uint32 i; - MA_DR_FLAC_ASSERT(bs != NULL); - MA_DR_FLAC_ASSERT(pSamplesOut != NULL); - (void)bitsPerSample; - (void)order; - (void)shift; - (void)coefficients; - riceParamMask = (ma_uint32)~((~0UL) << riceParam); - i = 0; - while (i < count) { - if (!ma_dr_flac__read_rice_parts_x1(bs, riceParam, &zeroCountPart0, &riceParamPart0)) { - return MA_FALSE; - } - riceParamPart0 &= riceParamMask; - riceParamPart0 |= (zeroCountPart0 << riceParam); - riceParamPart0 = (riceParamPart0 >> 1) ^ t[riceParamPart0 & 0x01]; - pSamplesOut[i] = riceParamPart0; - i += 1; - } - return MA_TRUE; -} -static ma_bool32 ma_dr_flac__decode_samples_with_residual__rice__scalar(ma_dr_flac_bs* bs, ma_uint32 bitsPerSample, ma_uint32 count, ma_uint8 riceParam, ma_uint32 lpcOrder, ma_int32 lpcShift, ma_uint32 lpcPrecision, const ma_int32* coefficients, ma_int32* pSamplesOut) -{ - ma_uint32 t[2] = {0x00000000, 0xFFFFFFFF}; - ma_uint32 zeroCountPart0 = 0; - ma_uint32 zeroCountPart1 = 0; - ma_uint32 zeroCountPart2 = 0; - ma_uint32 zeroCountPart3 = 0; - ma_uint32 riceParamPart0 = 0; - ma_uint32 riceParamPart1 = 0; - ma_uint32 riceParamPart2 = 0; - ma_uint32 riceParamPart3 = 0; - ma_uint32 riceParamMask; - const ma_int32* pSamplesOutEnd; - ma_uint32 i; - MA_DR_FLAC_ASSERT(bs != NULL); - MA_DR_FLAC_ASSERT(pSamplesOut != NULL); - if (lpcOrder == 0) { - return ma_dr_flac__decode_samples_with_residual__rice__scalar_zeroorder(bs, bitsPerSample, count, riceParam, lpcOrder, lpcShift, coefficients, pSamplesOut); - } - riceParamMask = (ma_uint32)~((~0UL) << riceParam); - pSamplesOutEnd = pSamplesOut + (count & ~3); - if (ma_dr_flac__use_64_bit_prediction(bitsPerSample, lpcOrder, lpcPrecision)) { - while (pSamplesOut < pSamplesOutEnd) { - if (!ma_dr_flac__read_rice_parts_x1(bs, riceParam, &zeroCountPart0, &riceParamPart0) || - !ma_dr_flac__read_rice_parts_x1(bs, riceParam, &zeroCountPart1, &riceParamPart1) || - !ma_dr_flac__read_rice_parts_x1(bs, riceParam, &zeroCountPart2, &riceParamPart2) || - !ma_dr_flac__read_rice_parts_x1(bs, riceParam, &zeroCountPart3, &riceParamPart3)) { - return MA_FALSE; - } - riceParamPart0 &= riceParamMask; - riceParamPart1 &= riceParamMask; - riceParamPart2 &= riceParamMask; - riceParamPart3 &= riceParamMask; - riceParamPart0 |= (zeroCountPart0 << riceParam); - riceParamPart1 |= (zeroCountPart1 << riceParam); - riceParamPart2 |= (zeroCountPart2 << riceParam); - riceParamPart3 |= (zeroCountPart3 << riceParam); - riceParamPart0 = (riceParamPart0 >> 1) ^ t[riceParamPart0 & 0x01]; - riceParamPart1 = (riceParamPart1 >> 1) ^ t[riceParamPart1 & 0x01]; - riceParamPart2 = (riceParamPart2 >> 1) ^ t[riceParamPart2 & 0x01]; - riceParamPart3 = (riceParamPart3 >> 1) ^ t[riceParamPart3 & 0x01]; - pSamplesOut[0] = riceParamPart0 + ma_dr_flac__calculate_prediction_64(lpcOrder, lpcShift, coefficients, pSamplesOut + 0); - pSamplesOut[1] = riceParamPart1 + ma_dr_flac__calculate_prediction_64(lpcOrder, lpcShift, coefficients, pSamplesOut + 1); - pSamplesOut[2] = riceParamPart2 + ma_dr_flac__calculate_prediction_64(lpcOrder, lpcShift, coefficients, pSamplesOut + 2); - pSamplesOut[3] = riceParamPart3 + ma_dr_flac__calculate_prediction_64(lpcOrder, lpcShift, coefficients, pSamplesOut + 3); - pSamplesOut += 4; - } - } else { - while (pSamplesOut < pSamplesOutEnd) { - if (!ma_dr_flac__read_rice_parts_x1(bs, riceParam, &zeroCountPart0, &riceParamPart0) || - !ma_dr_flac__read_rice_parts_x1(bs, riceParam, &zeroCountPart1, &riceParamPart1) || - !ma_dr_flac__read_rice_parts_x1(bs, riceParam, &zeroCountPart2, &riceParamPart2) || - !ma_dr_flac__read_rice_parts_x1(bs, riceParam, &zeroCountPart3, &riceParamPart3)) { - return MA_FALSE; - } - riceParamPart0 &= riceParamMask; - riceParamPart1 &= riceParamMask; - riceParamPart2 &= riceParamMask; - riceParamPart3 &= riceParamMask; - riceParamPart0 |= (zeroCountPart0 << riceParam); - riceParamPart1 |= (zeroCountPart1 << riceParam); - riceParamPart2 |= (zeroCountPart2 << riceParam); - riceParamPart3 |= (zeroCountPart3 << riceParam); - riceParamPart0 = (riceParamPart0 >> 1) ^ t[riceParamPart0 & 0x01]; - riceParamPart1 = (riceParamPart1 >> 1) ^ t[riceParamPart1 & 0x01]; - riceParamPart2 = (riceParamPart2 >> 1) ^ t[riceParamPart2 & 0x01]; - riceParamPart3 = (riceParamPart3 >> 1) ^ t[riceParamPart3 & 0x01]; - pSamplesOut[0] = riceParamPart0 + ma_dr_flac__calculate_prediction_32(lpcOrder, lpcShift, coefficients, pSamplesOut + 0); - pSamplesOut[1] = riceParamPart1 + ma_dr_flac__calculate_prediction_32(lpcOrder, lpcShift, coefficients, pSamplesOut + 1); - pSamplesOut[2] = riceParamPart2 + ma_dr_flac__calculate_prediction_32(lpcOrder, lpcShift, coefficients, pSamplesOut + 2); - pSamplesOut[3] = riceParamPart3 + ma_dr_flac__calculate_prediction_32(lpcOrder, lpcShift, coefficients, pSamplesOut + 3); - pSamplesOut += 4; - } - } - i = (count & ~3); - while (i < count) { - if (!ma_dr_flac__read_rice_parts_x1(bs, riceParam, &zeroCountPart0, &riceParamPart0)) { - return MA_FALSE; - } - riceParamPart0 &= riceParamMask; - riceParamPart0 |= (zeroCountPart0 << riceParam); - riceParamPart0 = (riceParamPart0 >> 1) ^ t[riceParamPart0 & 0x01]; - if (ma_dr_flac__use_64_bit_prediction(bitsPerSample, lpcOrder, lpcPrecision)) { - pSamplesOut[0] = riceParamPart0 + ma_dr_flac__calculate_prediction_64(lpcOrder, lpcShift, coefficients, pSamplesOut + 0); - } else { - pSamplesOut[0] = riceParamPart0 + ma_dr_flac__calculate_prediction_32(lpcOrder, lpcShift, coefficients, pSamplesOut + 0); - } - i += 1; - pSamplesOut += 1; - } - return MA_TRUE; -} -#if defined(MA_DR_FLAC_SUPPORT_SSE2) -static MA_INLINE __m128i ma_dr_flac__mm_packs_interleaved_epi32(__m128i a, __m128i b) -{ - __m128i r; - r = _mm_packs_epi32(a, b); - r = _mm_shuffle_epi32(r, _MM_SHUFFLE(3, 1, 2, 0)); - r = _mm_shufflehi_epi16(r, _MM_SHUFFLE(3, 1, 2, 0)); - r = _mm_shufflelo_epi16(r, _MM_SHUFFLE(3, 1, 2, 0)); - return r; -} -#endif -#if defined(MA_DR_FLAC_SUPPORT_SSE41) -static MA_INLINE __m128i ma_dr_flac__mm_not_si128(__m128i a) -{ - return _mm_xor_si128(a, _mm_cmpeq_epi32(_mm_setzero_si128(), _mm_setzero_si128())); -} -static MA_INLINE __m128i ma_dr_flac__mm_hadd_epi32(__m128i x) -{ - __m128i x64 = _mm_add_epi32(x, _mm_shuffle_epi32(x, _MM_SHUFFLE(1, 0, 3, 2))); - __m128i x32 = _mm_shufflelo_epi16(x64, _MM_SHUFFLE(1, 0, 3, 2)); - return _mm_add_epi32(x64, x32); -} -static MA_INLINE __m128i ma_dr_flac__mm_hadd_epi64(__m128i x) -{ - return _mm_add_epi64(x, _mm_shuffle_epi32(x, _MM_SHUFFLE(1, 0, 3, 2))); -} -static MA_INLINE __m128i ma_dr_flac__mm_srai_epi64(__m128i x, int count) -{ - __m128i lo = _mm_srli_epi64(x, count); - __m128i hi = _mm_srai_epi32(x, count); - hi = _mm_and_si128(hi, _mm_set_epi32(0xFFFFFFFF, 0, 0xFFFFFFFF, 0)); - return _mm_or_si128(lo, hi); -} -static ma_bool32 ma_dr_flac__decode_samples_with_residual__rice__sse41_32(ma_dr_flac_bs* bs, ma_uint32 count, ma_uint8 riceParam, ma_uint32 order, ma_int32 shift, const ma_int32* coefficients, ma_int32* pSamplesOut) -{ - int i; - ma_uint32 riceParamMask; - ma_int32* pDecodedSamples = pSamplesOut; - ma_int32* pDecodedSamplesEnd = pSamplesOut + (count & ~3); - ma_uint32 zeroCountParts0 = 0; - ma_uint32 zeroCountParts1 = 0; - ma_uint32 zeroCountParts2 = 0; - ma_uint32 zeroCountParts3 = 0; - ma_uint32 riceParamParts0 = 0; - ma_uint32 riceParamParts1 = 0; - ma_uint32 riceParamParts2 = 0; - ma_uint32 riceParamParts3 = 0; - __m128i coefficients128_0; - __m128i coefficients128_4; - __m128i coefficients128_8; - __m128i samples128_0; - __m128i samples128_4; - __m128i samples128_8; - __m128i riceParamMask128; - const ma_uint32 t[2] = {0x00000000, 0xFFFFFFFF}; - riceParamMask = (ma_uint32)~((~0UL) << riceParam); - riceParamMask128 = _mm_set1_epi32(riceParamMask); - coefficients128_0 = _mm_setzero_si128(); - coefficients128_4 = _mm_setzero_si128(); - coefficients128_8 = _mm_setzero_si128(); - samples128_0 = _mm_setzero_si128(); - samples128_4 = _mm_setzero_si128(); - samples128_8 = _mm_setzero_si128(); -#if 1 - { - int runningOrder = order; - if (runningOrder >= 4) { - coefficients128_0 = _mm_loadu_si128((const __m128i*)(coefficients + 0)); - samples128_0 = _mm_loadu_si128((const __m128i*)(pSamplesOut - 4)); - runningOrder -= 4; - } else { - switch (runningOrder) { - case 3: coefficients128_0 = _mm_set_epi32(0, coefficients[2], coefficients[1], coefficients[0]); samples128_0 = _mm_set_epi32(pSamplesOut[-1], pSamplesOut[-2], pSamplesOut[-3], 0); break; - case 2: coefficients128_0 = _mm_set_epi32(0, 0, coefficients[1], coefficients[0]); samples128_0 = _mm_set_epi32(pSamplesOut[-1], pSamplesOut[-2], 0, 0); break; - case 1: coefficients128_0 = _mm_set_epi32(0, 0, 0, coefficients[0]); samples128_0 = _mm_set_epi32(pSamplesOut[-1], 0, 0, 0); break; - } - runningOrder = 0; - } - if (runningOrder >= 4) { - coefficients128_4 = _mm_loadu_si128((const __m128i*)(coefficients + 4)); - samples128_4 = _mm_loadu_si128((const __m128i*)(pSamplesOut - 8)); - runningOrder -= 4; - } else { - switch (runningOrder) { - case 3: coefficients128_4 = _mm_set_epi32(0, coefficients[6], coefficients[5], coefficients[4]); samples128_4 = _mm_set_epi32(pSamplesOut[-5], pSamplesOut[-6], pSamplesOut[-7], 0); break; - case 2: coefficients128_4 = _mm_set_epi32(0, 0, coefficients[5], coefficients[4]); samples128_4 = _mm_set_epi32(pSamplesOut[-5], pSamplesOut[-6], 0, 0); break; - case 1: coefficients128_4 = _mm_set_epi32(0, 0, 0, coefficients[4]); samples128_4 = _mm_set_epi32(pSamplesOut[-5], 0, 0, 0); break; - } - runningOrder = 0; - } - if (runningOrder == 4) { - coefficients128_8 = _mm_loadu_si128((const __m128i*)(coefficients + 8)); - samples128_8 = _mm_loadu_si128((const __m128i*)(pSamplesOut - 12)); - runningOrder -= 4; - } else { - switch (runningOrder) { - case 3: coefficients128_8 = _mm_set_epi32(0, coefficients[10], coefficients[9], coefficients[8]); samples128_8 = _mm_set_epi32(pSamplesOut[-9], pSamplesOut[-10], pSamplesOut[-11], 0); break; - case 2: coefficients128_8 = _mm_set_epi32(0, 0, coefficients[9], coefficients[8]); samples128_8 = _mm_set_epi32(pSamplesOut[-9], pSamplesOut[-10], 0, 0); break; - case 1: coefficients128_8 = _mm_set_epi32(0, 0, 0, coefficients[8]); samples128_8 = _mm_set_epi32(pSamplesOut[-9], 0, 0, 0); break; - } - runningOrder = 0; - } - coefficients128_0 = _mm_shuffle_epi32(coefficients128_0, _MM_SHUFFLE(0, 1, 2, 3)); - coefficients128_4 = _mm_shuffle_epi32(coefficients128_4, _MM_SHUFFLE(0, 1, 2, 3)); - coefficients128_8 = _mm_shuffle_epi32(coefficients128_8, _MM_SHUFFLE(0, 1, 2, 3)); - } -#else - switch (order) - { - case 12: ((ma_int32*)&coefficients128_8)[0] = coefficients[11]; ((ma_int32*)&samples128_8)[0] = pDecodedSamples[-12]; - case 11: ((ma_int32*)&coefficients128_8)[1] = coefficients[10]; ((ma_int32*)&samples128_8)[1] = pDecodedSamples[-11]; - case 10: ((ma_int32*)&coefficients128_8)[2] = coefficients[ 9]; ((ma_int32*)&samples128_8)[2] = pDecodedSamples[-10]; - case 9: ((ma_int32*)&coefficients128_8)[3] = coefficients[ 8]; ((ma_int32*)&samples128_8)[3] = pDecodedSamples[- 9]; - case 8: ((ma_int32*)&coefficients128_4)[0] = coefficients[ 7]; ((ma_int32*)&samples128_4)[0] = pDecodedSamples[- 8]; - case 7: ((ma_int32*)&coefficients128_4)[1] = coefficients[ 6]; ((ma_int32*)&samples128_4)[1] = pDecodedSamples[- 7]; - case 6: ((ma_int32*)&coefficients128_4)[2] = coefficients[ 5]; ((ma_int32*)&samples128_4)[2] = pDecodedSamples[- 6]; - case 5: ((ma_int32*)&coefficients128_4)[3] = coefficients[ 4]; ((ma_int32*)&samples128_4)[3] = pDecodedSamples[- 5]; - case 4: ((ma_int32*)&coefficients128_0)[0] = coefficients[ 3]; ((ma_int32*)&samples128_0)[0] = pDecodedSamples[- 4]; - case 3: ((ma_int32*)&coefficients128_0)[1] = coefficients[ 2]; ((ma_int32*)&samples128_0)[1] = pDecodedSamples[- 3]; - case 2: ((ma_int32*)&coefficients128_0)[2] = coefficients[ 1]; ((ma_int32*)&samples128_0)[2] = pDecodedSamples[- 2]; - case 1: ((ma_int32*)&coefficients128_0)[3] = coefficients[ 0]; ((ma_int32*)&samples128_0)[3] = pDecodedSamples[- 1]; - } -#endif - while (pDecodedSamples < pDecodedSamplesEnd) { - __m128i prediction128; - __m128i zeroCountPart128; - __m128i riceParamPart128; - if (!ma_dr_flac__read_rice_parts_x1(bs, riceParam, &zeroCountParts0, &riceParamParts0) || - !ma_dr_flac__read_rice_parts_x1(bs, riceParam, &zeroCountParts1, &riceParamParts1) || - !ma_dr_flac__read_rice_parts_x1(bs, riceParam, &zeroCountParts2, &riceParamParts2) || - !ma_dr_flac__read_rice_parts_x1(bs, riceParam, &zeroCountParts3, &riceParamParts3)) { - return MA_FALSE; - } - zeroCountPart128 = _mm_set_epi32(zeroCountParts3, zeroCountParts2, zeroCountParts1, zeroCountParts0); - riceParamPart128 = _mm_set_epi32(riceParamParts3, riceParamParts2, riceParamParts1, riceParamParts0); - riceParamPart128 = _mm_and_si128(riceParamPart128, riceParamMask128); - riceParamPart128 = _mm_or_si128(riceParamPart128, _mm_slli_epi32(zeroCountPart128, riceParam)); - riceParamPart128 = _mm_xor_si128(_mm_srli_epi32(riceParamPart128, 1), _mm_add_epi32(ma_dr_flac__mm_not_si128(_mm_and_si128(riceParamPart128, _mm_set1_epi32(0x01))), _mm_set1_epi32(0x01))); - if (order <= 4) { - for (i = 0; i < 4; i += 1) { - prediction128 = _mm_mullo_epi32(coefficients128_0, samples128_0); - prediction128 = ma_dr_flac__mm_hadd_epi32(prediction128); - prediction128 = _mm_srai_epi32(prediction128, shift); - prediction128 = _mm_add_epi32(riceParamPart128, prediction128); - samples128_0 = _mm_alignr_epi8(prediction128, samples128_0, 4); - riceParamPart128 = _mm_alignr_epi8(_mm_setzero_si128(), riceParamPart128, 4); - } - } else if (order <= 8) { - for (i = 0; i < 4; i += 1) { - prediction128 = _mm_mullo_epi32(coefficients128_4, samples128_4); - prediction128 = _mm_add_epi32(prediction128, _mm_mullo_epi32(coefficients128_0, samples128_0)); - prediction128 = ma_dr_flac__mm_hadd_epi32(prediction128); - prediction128 = _mm_srai_epi32(prediction128, shift); - prediction128 = _mm_add_epi32(riceParamPart128, prediction128); - samples128_4 = _mm_alignr_epi8(samples128_0, samples128_4, 4); - samples128_0 = _mm_alignr_epi8(prediction128, samples128_0, 4); - riceParamPart128 = _mm_alignr_epi8(_mm_setzero_si128(), riceParamPart128, 4); - } - } else { - for (i = 0; i < 4; i += 1) { - prediction128 = _mm_mullo_epi32(coefficients128_8, samples128_8); - prediction128 = _mm_add_epi32(prediction128, _mm_mullo_epi32(coefficients128_4, samples128_4)); - prediction128 = _mm_add_epi32(prediction128, _mm_mullo_epi32(coefficients128_0, samples128_0)); - prediction128 = ma_dr_flac__mm_hadd_epi32(prediction128); - prediction128 = _mm_srai_epi32(prediction128, shift); - prediction128 = _mm_add_epi32(riceParamPart128, prediction128); - samples128_8 = _mm_alignr_epi8(samples128_4, samples128_8, 4); - samples128_4 = _mm_alignr_epi8(samples128_0, samples128_4, 4); - samples128_0 = _mm_alignr_epi8(prediction128, samples128_0, 4); - riceParamPart128 = _mm_alignr_epi8(_mm_setzero_si128(), riceParamPart128, 4); - } - } - _mm_storeu_si128((__m128i*)pDecodedSamples, samples128_0); - pDecodedSamples += 4; - } - i = (count & ~3); - while (i < (int)count) { - if (!ma_dr_flac__read_rice_parts_x1(bs, riceParam, &zeroCountParts0, &riceParamParts0)) { - return MA_FALSE; - } - riceParamParts0 &= riceParamMask; - riceParamParts0 |= (zeroCountParts0 << riceParam); - riceParamParts0 = (riceParamParts0 >> 1) ^ t[riceParamParts0 & 0x01]; - pDecodedSamples[0] = riceParamParts0 + ma_dr_flac__calculate_prediction_32(order, shift, coefficients, pDecodedSamples); - i += 1; - pDecodedSamples += 1; - } - return MA_TRUE; -} -static ma_bool32 ma_dr_flac__decode_samples_with_residual__rice__sse41_64(ma_dr_flac_bs* bs, ma_uint32 count, ma_uint8 riceParam, ma_uint32 order, ma_int32 shift, const ma_int32* coefficients, ma_int32* pSamplesOut) -{ - int i; - ma_uint32 riceParamMask; - ma_int32* pDecodedSamples = pSamplesOut; - ma_int32* pDecodedSamplesEnd = pSamplesOut + (count & ~3); - ma_uint32 zeroCountParts0 = 0; - ma_uint32 zeroCountParts1 = 0; - ma_uint32 zeroCountParts2 = 0; - ma_uint32 zeroCountParts3 = 0; - ma_uint32 riceParamParts0 = 0; - ma_uint32 riceParamParts1 = 0; - ma_uint32 riceParamParts2 = 0; - ma_uint32 riceParamParts3 = 0; - __m128i coefficients128_0; - __m128i coefficients128_4; - __m128i coefficients128_8; - __m128i samples128_0; - __m128i samples128_4; - __m128i samples128_8; - __m128i prediction128; - __m128i riceParamMask128; - const ma_uint32 t[2] = {0x00000000, 0xFFFFFFFF}; - MA_DR_FLAC_ASSERT(order <= 12); - riceParamMask = (ma_uint32)~((~0UL) << riceParam); - riceParamMask128 = _mm_set1_epi32(riceParamMask); - prediction128 = _mm_setzero_si128(); - coefficients128_0 = _mm_setzero_si128(); - coefficients128_4 = _mm_setzero_si128(); - coefficients128_8 = _mm_setzero_si128(); - samples128_0 = _mm_setzero_si128(); - samples128_4 = _mm_setzero_si128(); - samples128_8 = _mm_setzero_si128(); -#if 1 - { - int runningOrder = order; - if (runningOrder >= 4) { - coefficients128_0 = _mm_loadu_si128((const __m128i*)(coefficients + 0)); - samples128_0 = _mm_loadu_si128((const __m128i*)(pSamplesOut - 4)); - runningOrder -= 4; - } else { - switch (runningOrder) { - case 3: coefficients128_0 = _mm_set_epi32(0, coefficients[2], coefficients[1], coefficients[0]); samples128_0 = _mm_set_epi32(pSamplesOut[-1], pSamplesOut[-2], pSamplesOut[-3], 0); break; - case 2: coefficients128_0 = _mm_set_epi32(0, 0, coefficients[1], coefficients[0]); samples128_0 = _mm_set_epi32(pSamplesOut[-1], pSamplesOut[-2], 0, 0); break; - case 1: coefficients128_0 = _mm_set_epi32(0, 0, 0, coefficients[0]); samples128_0 = _mm_set_epi32(pSamplesOut[-1], 0, 0, 0); break; - } - runningOrder = 0; - } - if (runningOrder >= 4) { - coefficients128_4 = _mm_loadu_si128((const __m128i*)(coefficients + 4)); - samples128_4 = _mm_loadu_si128((const __m128i*)(pSamplesOut - 8)); - runningOrder -= 4; - } else { - switch (runningOrder) { - case 3: coefficients128_4 = _mm_set_epi32(0, coefficients[6], coefficients[5], coefficients[4]); samples128_4 = _mm_set_epi32(pSamplesOut[-5], pSamplesOut[-6], pSamplesOut[-7], 0); break; - case 2: coefficients128_4 = _mm_set_epi32(0, 0, coefficients[5], coefficients[4]); samples128_4 = _mm_set_epi32(pSamplesOut[-5], pSamplesOut[-6], 0, 0); break; - case 1: coefficients128_4 = _mm_set_epi32(0, 0, 0, coefficients[4]); samples128_4 = _mm_set_epi32(pSamplesOut[-5], 0, 0, 0); break; - } - runningOrder = 0; - } - if (runningOrder == 4) { - coefficients128_8 = _mm_loadu_si128((const __m128i*)(coefficients + 8)); - samples128_8 = _mm_loadu_si128((const __m128i*)(pSamplesOut - 12)); - runningOrder -= 4; - } else { - switch (runningOrder) { - case 3: coefficients128_8 = _mm_set_epi32(0, coefficients[10], coefficients[9], coefficients[8]); samples128_8 = _mm_set_epi32(pSamplesOut[-9], pSamplesOut[-10], pSamplesOut[-11], 0); break; - case 2: coefficients128_8 = _mm_set_epi32(0, 0, coefficients[9], coefficients[8]); samples128_8 = _mm_set_epi32(pSamplesOut[-9], pSamplesOut[-10], 0, 0); break; - case 1: coefficients128_8 = _mm_set_epi32(0, 0, 0, coefficients[8]); samples128_8 = _mm_set_epi32(pSamplesOut[-9], 0, 0, 0); break; - } - runningOrder = 0; - } - coefficients128_0 = _mm_shuffle_epi32(coefficients128_0, _MM_SHUFFLE(0, 1, 2, 3)); - coefficients128_4 = _mm_shuffle_epi32(coefficients128_4, _MM_SHUFFLE(0, 1, 2, 3)); - coefficients128_8 = _mm_shuffle_epi32(coefficients128_8, _MM_SHUFFLE(0, 1, 2, 3)); - } -#else - switch (order) - { - case 12: ((ma_int32*)&coefficients128_8)[0] = coefficients[11]; ((ma_int32*)&samples128_8)[0] = pDecodedSamples[-12]; - case 11: ((ma_int32*)&coefficients128_8)[1] = coefficients[10]; ((ma_int32*)&samples128_8)[1] = pDecodedSamples[-11]; - case 10: ((ma_int32*)&coefficients128_8)[2] = coefficients[ 9]; ((ma_int32*)&samples128_8)[2] = pDecodedSamples[-10]; - case 9: ((ma_int32*)&coefficients128_8)[3] = coefficients[ 8]; ((ma_int32*)&samples128_8)[3] = pDecodedSamples[- 9]; - case 8: ((ma_int32*)&coefficients128_4)[0] = coefficients[ 7]; ((ma_int32*)&samples128_4)[0] = pDecodedSamples[- 8]; - case 7: ((ma_int32*)&coefficients128_4)[1] = coefficients[ 6]; ((ma_int32*)&samples128_4)[1] = pDecodedSamples[- 7]; - case 6: ((ma_int32*)&coefficients128_4)[2] = coefficients[ 5]; ((ma_int32*)&samples128_4)[2] = pDecodedSamples[- 6]; - case 5: ((ma_int32*)&coefficients128_4)[3] = coefficients[ 4]; ((ma_int32*)&samples128_4)[3] = pDecodedSamples[- 5]; - case 4: ((ma_int32*)&coefficients128_0)[0] = coefficients[ 3]; ((ma_int32*)&samples128_0)[0] = pDecodedSamples[- 4]; - case 3: ((ma_int32*)&coefficients128_0)[1] = coefficients[ 2]; ((ma_int32*)&samples128_0)[1] = pDecodedSamples[- 3]; - case 2: ((ma_int32*)&coefficients128_0)[2] = coefficients[ 1]; ((ma_int32*)&samples128_0)[2] = pDecodedSamples[- 2]; - case 1: ((ma_int32*)&coefficients128_0)[3] = coefficients[ 0]; ((ma_int32*)&samples128_0)[3] = pDecodedSamples[- 1]; - } -#endif - while (pDecodedSamples < pDecodedSamplesEnd) { - __m128i zeroCountPart128; - __m128i riceParamPart128; - if (!ma_dr_flac__read_rice_parts_x1(bs, riceParam, &zeroCountParts0, &riceParamParts0) || - !ma_dr_flac__read_rice_parts_x1(bs, riceParam, &zeroCountParts1, &riceParamParts1) || - !ma_dr_flac__read_rice_parts_x1(bs, riceParam, &zeroCountParts2, &riceParamParts2) || - !ma_dr_flac__read_rice_parts_x1(bs, riceParam, &zeroCountParts3, &riceParamParts3)) { - return MA_FALSE; - } - zeroCountPart128 = _mm_set_epi32(zeroCountParts3, zeroCountParts2, zeroCountParts1, zeroCountParts0); - riceParamPart128 = _mm_set_epi32(riceParamParts3, riceParamParts2, riceParamParts1, riceParamParts0); - riceParamPart128 = _mm_and_si128(riceParamPart128, riceParamMask128); - riceParamPart128 = _mm_or_si128(riceParamPart128, _mm_slli_epi32(zeroCountPart128, riceParam)); - riceParamPart128 = _mm_xor_si128(_mm_srli_epi32(riceParamPart128, 1), _mm_add_epi32(ma_dr_flac__mm_not_si128(_mm_and_si128(riceParamPart128, _mm_set1_epi32(1))), _mm_set1_epi32(1))); - for (i = 0; i < 4; i += 1) { - prediction128 = _mm_xor_si128(prediction128, prediction128); - switch (order) - { - case 12: - case 11: prediction128 = _mm_add_epi64(prediction128, _mm_mul_epi32(_mm_shuffle_epi32(coefficients128_8, _MM_SHUFFLE(1, 1, 0, 0)), _mm_shuffle_epi32(samples128_8, _MM_SHUFFLE(1, 1, 0, 0)))); - case 10: - case 9: prediction128 = _mm_add_epi64(prediction128, _mm_mul_epi32(_mm_shuffle_epi32(coefficients128_8, _MM_SHUFFLE(3, 3, 2, 2)), _mm_shuffle_epi32(samples128_8, _MM_SHUFFLE(3, 3, 2, 2)))); - case 8: - case 7: prediction128 = _mm_add_epi64(prediction128, _mm_mul_epi32(_mm_shuffle_epi32(coefficients128_4, _MM_SHUFFLE(1, 1, 0, 0)), _mm_shuffle_epi32(samples128_4, _MM_SHUFFLE(1, 1, 0, 0)))); - case 6: - case 5: prediction128 = _mm_add_epi64(prediction128, _mm_mul_epi32(_mm_shuffle_epi32(coefficients128_4, _MM_SHUFFLE(3, 3, 2, 2)), _mm_shuffle_epi32(samples128_4, _MM_SHUFFLE(3, 3, 2, 2)))); - case 4: - case 3: prediction128 = _mm_add_epi64(prediction128, _mm_mul_epi32(_mm_shuffle_epi32(coefficients128_0, _MM_SHUFFLE(1, 1, 0, 0)), _mm_shuffle_epi32(samples128_0, _MM_SHUFFLE(1, 1, 0, 0)))); - case 2: - case 1: prediction128 = _mm_add_epi64(prediction128, _mm_mul_epi32(_mm_shuffle_epi32(coefficients128_0, _MM_SHUFFLE(3, 3, 2, 2)), _mm_shuffle_epi32(samples128_0, _MM_SHUFFLE(3, 3, 2, 2)))); - } - prediction128 = ma_dr_flac__mm_hadd_epi64(prediction128); - prediction128 = ma_dr_flac__mm_srai_epi64(prediction128, shift); - prediction128 = _mm_add_epi32(riceParamPart128, prediction128); - samples128_8 = _mm_alignr_epi8(samples128_4, samples128_8, 4); - samples128_4 = _mm_alignr_epi8(samples128_0, samples128_4, 4); - samples128_0 = _mm_alignr_epi8(prediction128, samples128_0, 4); - riceParamPart128 = _mm_alignr_epi8(_mm_setzero_si128(), riceParamPart128, 4); - } - _mm_storeu_si128((__m128i*)pDecodedSamples, samples128_0); - pDecodedSamples += 4; - } - i = (count & ~3); - while (i < (int)count) { - if (!ma_dr_flac__read_rice_parts_x1(bs, riceParam, &zeroCountParts0, &riceParamParts0)) { - return MA_FALSE; - } - riceParamParts0 &= riceParamMask; - riceParamParts0 |= (zeroCountParts0 << riceParam); - riceParamParts0 = (riceParamParts0 >> 1) ^ t[riceParamParts0 & 0x01]; - pDecodedSamples[0] = riceParamParts0 + ma_dr_flac__calculate_prediction_64(order, shift, coefficients, pDecodedSamples); - i += 1; - pDecodedSamples += 1; - } - return MA_TRUE; -} -static ma_bool32 ma_dr_flac__decode_samples_with_residual__rice__sse41(ma_dr_flac_bs* bs, ma_uint32 bitsPerSample, ma_uint32 count, ma_uint8 riceParam, ma_uint32 lpcOrder, ma_int32 lpcShift, ma_uint32 lpcPrecision, const ma_int32* coefficients, ma_int32* pSamplesOut) -{ - MA_DR_FLAC_ASSERT(bs != NULL); - MA_DR_FLAC_ASSERT(pSamplesOut != NULL); - if (lpcOrder > 0 && lpcOrder <= 12) { - if (ma_dr_flac__use_64_bit_prediction(bitsPerSample, lpcOrder, lpcPrecision)) { - return ma_dr_flac__decode_samples_with_residual__rice__sse41_64(bs, count, riceParam, lpcOrder, lpcShift, coefficients, pSamplesOut); - } else { - return ma_dr_flac__decode_samples_with_residual__rice__sse41_32(bs, count, riceParam, lpcOrder, lpcShift, coefficients, pSamplesOut); - } - } else { - return ma_dr_flac__decode_samples_with_residual__rice__scalar(bs, bitsPerSample, count, riceParam, lpcOrder, lpcShift, lpcPrecision, coefficients, pSamplesOut); - } -} -#endif -#if defined(MA_DR_FLAC_SUPPORT_NEON) -static MA_INLINE void ma_dr_flac__vst2q_s32(ma_int32* p, int32x4x2_t x) -{ - vst1q_s32(p+0, x.val[0]); - vst1q_s32(p+4, x.val[1]); -} -static MA_INLINE void ma_dr_flac__vst2q_u32(ma_uint32* p, uint32x4x2_t x) -{ - vst1q_u32(p+0, x.val[0]); - vst1q_u32(p+4, x.val[1]); -} -static MA_INLINE void ma_dr_flac__vst2q_f32(float* p, float32x4x2_t x) -{ - vst1q_f32(p+0, x.val[0]); - vst1q_f32(p+4, x.val[1]); -} -static MA_INLINE void ma_dr_flac__vst2q_s16(ma_int16* p, int16x4x2_t x) -{ - vst1q_s16(p, vcombine_s16(x.val[0], x.val[1])); -} -static MA_INLINE void ma_dr_flac__vst2q_u16(ma_uint16* p, uint16x4x2_t x) -{ - vst1q_u16(p, vcombine_u16(x.val[0], x.val[1])); -} -static MA_INLINE int32x4_t ma_dr_flac__vdupq_n_s32x4(ma_int32 x3, ma_int32 x2, ma_int32 x1, ma_int32 x0) -{ - ma_int32 x[4]; - x[3] = x3; - x[2] = x2; - x[1] = x1; - x[0] = x0; - return vld1q_s32(x); -} -static MA_INLINE int32x4_t ma_dr_flac__valignrq_s32_1(int32x4_t a, int32x4_t b) -{ - return vextq_s32(b, a, 1); -} -static MA_INLINE uint32x4_t ma_dr_flac__valignrq_u32_1(uint32x4_t a, uint32x4_t b) -{ - return vextq_u32(b, a, 1); -} -static MA_INLINE int32x2_t ma_dr_flac__vhaddq_s32(int32x4_t x) -{ - int32x2_t r = vadd_s32(vget_high_s32(x), vget_low_s32(x)); - return vpadd_s32(r, r); -} -static MA_INLINE int64x1_t ma_dr_flac__vhaddq_s64(int64x2_t x) -{ - return vadd_s64(vget_high_s64(x), vget_low_s64(x)); -} -static MA_INLINE int32x4_t ma_dr_flac__vrevq_s32(int32x4_t x) -{ - return vrev64q_s32(vcombine_s32(vget_high_s32(x), vget_low_s32(x))); -} -static MA_INLINE int32x4_t ma_dr_flac__vnotq_s32(int32x4_t x) -{ - return veorq_s32(x, vdupq_n_s32(0xFFFFFFFF)); -} -static MA_INLINE uint32x4_t ma_dr_flac__vnotq_u32(uint32x4_t x) -{ - return veorq_u32(x, vdupq_n_u32(0xFFFFFFFF)); -} -static ma_bool32 ma_dr_flac__decode_samples_with_residual__rice__neon_32(ma_dr_flac_bs* bs, ma_uint32 count, ma_uint8 riceParam, ma_uint32 order, ma_int32 shift, const ma_int32* coefficients, ma_int32* pSamplesOut) -{ - int i; - ma_uint32 riceParamMask; - ma_int32* pDecodedSamples = pSamplesOut; - ma_int32* pDecodedSamplesEnd = pSamplesOut + (count & ~3); - ma_uint32 zeroCountParts[4]; - ma_uint32 riceParamParts[4]; - int32x4_t coefficients128_0; - int32x4_t coefficients128_4; - int32x4_t coefficients128_8; - int32x4_t samples128_0; - int32x4_t samples128_4; - int32x4_t samples128_8; - uint32x4_t riceParamMask128; - int32x4_t riceParam128; - int32x2_t shift64; - uint32x4_t one128; - const ma_uint32 t[2] = {0x00000000, 0xFFFFFFFF}; - riceParamMask = (ma_uint32)~((~0UL) << riceParam); - riceParamMask128 = vdupq_n_u32(riceParamMask); - riceParam128 = vdupq_n_s32(riceParam); - shift64 = vdup_n_s32(-shift); - one128 = vdupq_n_u32(1); - { - int runningOrder = order; - ma_int32 tempC[4] = {0, 0, 0, 0}; - ma_int32 tempS[4] = {0, 0, 0, 0}; - if (runningOrder >= 4) { - coefficients128_0 = vld1q_s32(coefficients + 0); - samples128_0 = vld1q_s32(pSamplesOut - 4); - runningOrder -= 4; - } else { - switch (runningOrder) { - case 3: tempC[2] = coefficients[2]; tempS[1] = pSamplesOut[-3]; - case 2: tempC[1] = coefficients[1]; tempS[2] = pSamplesOut[-2]; - case 1: tempC[0] = coefficients[0]; tempS[3] = pSamplesOut[-1]; - } - coefficients128_0 = vld1q_s32(tempC); - samples128_0 = vld1q_s32(tempS); - runningOrder = 0; - } - if (runningOrder >= 4) { - coefficients128_4 = vld1q_s32(coefficients + 4); - samples128_4 = vld1q_s32(pSamplesOut - 8); - runningOrder -= 4; - } else { - switch (runningOrder) { - case 3: tempC[2] = coefficients[6]; tempS[1] = pSamplesOut[-7]; - case 2: tempC[1] = coefficients[5]; tempS[2] = pSamplesOut[-6]; - case 1: tempC[0] = coefficients[4]; tempS[3] = pSamplesOut[-5]; - } - coefficients128_4 = vld1q_s32(tempC); - samples128_4 = vld1q_s32(tempS); - runningOrder = 0; - } - if (runningOrder == 4) { - coefficients128_8 = vld1q_s32(coefficients + 8); - samples128_8 = vld1q_s32(pSamplesOut - 12); - runningOrder -= 4; - } else { - switch (runningOrder) { - case 3: tempC[2] = coefficients[10]; tempS[1] = pSamplesOut[-11]; - case 2: tempC[1] = coefficients[ 9]; tempS[2] = pSamplesOut[-10]; - case 1: tempC[0] = coefficients[ 8]; tempS[3] = pSamplesOut[- 9]; - } - coefficients128_8 = vld1q_s32(tempC); - samples128_8 = vld1q_s32(tempS); - runningOrder = 0; - } - coefficients128_0 = ma_dr_flac__vrevq_s32(coefficients128_0); - coefficients128_4 = ma_dr_flac__vrevq_s32(coefficients128_4); - coefficients128_8 = ma_dr_flac__vrevq_s32(coefficients128_8); - } - while (pDecodedSamples < pDecodedSamplesEnd) { - int32x4_t prediction128; - int32x2_t prediction64; - uint32x4_t zeroCountPart128; - uint32x4_t riceParamPart128; - if (!ma_dr_flac__read_rice_parts_x1(bs, riceParam, &zeroCountParts[0], &riceParamParts[0]) || - !ma_dr_flac__read_rice_parts_x1(bs, riceParam, &zeroCountParts[1], &riceParamParts[1]) || - !ma_dr_flac__read_rice_parts_x1(bs, riceParam, &zeroCountParts[2], &riceParamParts[2]) || - !ma_dr_flac__read_rice_parts_x1(bs, riceParam, &zeroCountParts[3], &riceParamParts[3])) { - return MA_FALSE; - } - zeroCountPart128 = vld1q_u32(zeroCountParts); - riceParamPart128 = vld1q_u32(riceParamParts); - riceParamPart128 = vandq_u32(riceParamPart128, riceParamMask128); - riceParamPart128 = vorrq_u32(riceParamPart128, vshlq_u32(zeroCountPart128, riceParam128)); - riceParamPart128 = veorq_u32(vshrq_n_u32(riceParamPart128, 1), vaddq_u32(ma_dr_flac__vnotq_u32(vandq_u32(riceParamPart128, one128)), one128)); - if (order <= 4) { - for (i = 0; i < 4; i += 1) { - prediction128 = vmulq_s32(coefficients128_0, samples128_0); - prediction64 = ma_dr_flac__vhaddq_s32(prediction128); - prediction64 = vshl_s32(prediction64, shift64); - prediction64 = vadd_s32(prediction64, vget_low_s32(vreinterpretq_s32_u32(riceParamPart128))); - samples128_0 = ma_dr_flac__valignrq_s32_1(vcombine_s32(prediction64, vdup_n_s32(0)), samples128_0); - riceParamPart128 = ma_dr_flac__valignrq_u32_1(vdupq_n_u32(0), riceParamPart128); - } - } else if (order <= 8) { - for (i = 0; i < 4; i += 1) { - prediction128 = vmulq_s32(coefficients128_4, samples128_4); - prediction128 = vmlaq_s32(prediction128, coefficients128_0, samples128_0); - prediction64 = ma_dr_flac__vhaddq_s32(prediction128); - prediction64 = vshl_s32(prediction64, shift64); - prediction64 = vadd_s32(prediction64, vget_low_s32(vreinterpretq_s32_u32(riceParamPart128))); - samples128_4 = ma_dr_flac__valignrq_s32_1(samples128_0, samples128_4); - samples128_0 = ma_dr_flac__valignrq_s32_1(vcombine_s32(prediction64, vdup_n_s32(0)), samples128_0); - riceParamPart128 = ma_dr_flac__valignrq_u32_1(vdupq_n_u32(0), riceParamPart128); - } - } else { - for (i = 0; i < 4; i += 1) { - prediction128 = vmulq_s32(coefficients128_8, samples128_8); - prediction128 = vmlaq_s32(prediction128, coefficients128_4, samples128_4); - prediction128 = vmlaq_s32(prediction128, coefficients128_0, samples128_0); - prediction64 = ma_dr_flac__vhaddq_s32(prediction128); - prediction64 = vshl_s32(prediction64, shift64); - prediction64 = vadd_s32(prediction64, vget_low_s32(vreinterpretq_s32_u32(riceParamPart128))); - samples128_8 = ma_dr_flac__valignrq_s32_1(samples128_4, samples128_8); - samples128_4 = ma_dr_flac__valignrq_s32_1(samples128_0, samples128_4); - samples128_0 = ma_dr_flac__valignrq_s32_1(vcombine_s32(prediction64, vdup_n_s32(0)), samples128_0); - riceParamPart128 = ma_dr_flac__valignrq_u32_1(vdupq_n_u32(0), riceParamPart128); - } - } - vst1q_s32(pDecodedSamples, samples128_0); - pDecodedSamples += 4; - } - i = (count & ~3); - while (i < (int)count) { - if (!ma_dr_flac__read_rice_parts_x1(bs, riceParam, &zeroCountParts[0], &riceParamParts[0])) { - return MA_FALSE; - } - riceParamParts[0] &= riceParamMask; - riceParamParts[0] |= (zeroCountParts[0] << riceParam); - riceParamParts[0] = (riceParamParts[0] >> 1) ^ t[riceParamParts[0] & 0x01]; - pDecodedSamples[0] = riceParamParts[0] + ma_dr_flac__calculate_prediction_32(order, shift, coefficients, pDecodedSamples); - i += 1; - pDecodedSamples += 1; - } - return MA_TRUE; -} -static ma_bool32 ma_dr_flac__decode_samples_with_residual__rice__neon_64(ma_dr_flac_bs* bs, ma_uint32 count, ma_uint8 riceParam, ma_uint32 order, ma_int32 shift, const ma_int32* coefficients, ma_int32* pSamplesOut) -{ - int i; - ma_uint32 riceParamMask; - ma_int32* pDecodedSamples = pSamplesOut; - ma_int32* pDecodedSamplesEnd = pSamplesOut + (count & ~3); - ma_uint32 zeroCountParts[4]; - ma_uint32 riceParamParts[4]; - int32x4_t coefficients128_0; - int32x4_t coefficients128_4; - int32x4_t coefficients128_8; - int32x4_t samples128_0; - int32x4_t samples128_4; - int32x4_t samples128_8; - uint32x4_t riceParamMask128; - int32x4_t riceParam128; - int64x1_t shift64; - uint32x4_t one128; - int64x2_t prediction128 = { 0 }; - uint32x4_t zeroCountPart128; - uint32x4_t riceParamPart128; - const ma_uint32 t[2] = {0x00000000, 0xFFFFFFFF}; - riceParamMask = (ma_uint32)~((~0UL) << riceParam); - riceParamMask128 = vdupq_n_u32(riceParamMask); - riceParam128 = vdupq_n_s32(riceParam); - shift64 = vdup_n_s64(-shift); - one128 = vdupq_n_u32(1); - { - int runningOrder = order; - ma_int32 tempC[4] = {0, 0, 0, 0}; - ma_int32 tempS[4] = {0, 0, 0, 0}; - if (runningOrder >= 4) { - coefficients128_0 = vld1q_s32(coefficients + 0); - samples128_0 = vld1q_s32(pSamplesOut - 4); - runningOrder -= 4; - } else { - switch (runningOrder) { - case 3: tempC[2] = coefficients[2]; tempS[1] = pSamplesOut[-3]; - case 2: tempC[1] = coefficients[1]; tempS[2] = pSamplesOut[-2]; - case 1: tempC[0] = coefficients[0]; tempS[3] = pSamplesOut[-1]; - } - coefficients128_0 = vld1q_s32(tempC); - samples128_0 = vld1q_s32(tempS); - runningOrder = 0; - } - if (runningOrder >= 4) { - coefficients128_4 = vld1q_s32(coefficients + 4); - samples128_4 = vld1q_s32(pSamplesOut - 8); - runningOrder -= 4; - } else { - switch (runningOrder) { - case 3: tempC[2] = coefficients[6]; tempS[1] = pSamplesOut[-7]; - case 2: tempC[1] = coefficients[5]; tempS[2] = pSamplesOut[-6]; - case 1: tempC[0] = coefficients[4]; tempS[3] = pSamplesOut[-5]; - } - coefficients128_4 = vld1q_s32(tempC); - samples128_4 = vld1q_s32(tempS); - runningOrder = 0; - } - if (runningOrder == 4) { - coefficients128_8 = vld1q_s32(coefficients + 8); - samples128_8 = vld1q_s32(pSamplesOut - 12); - runningOrder -= 4; - } else { - switch (runningOrder) { - case 3: tempC[2] = coefficients[10]; tempS[1] = pSamplesOut[-11]; - case 2: tempC[1] = coefficients[ 9]; tempS[2] = pSamplesOut[-10]; - case 1: tempC[0] = coefficients[ 8]; tempS[3] = pSamplesOut[- 9]; - } - coefficients128_8 = vld1q_s32(tempC); - samples128_8 = vld1q_s32(tempS); - runningOrder = 0; - } - coefficients128_0 = ma_dr_flac__vrevq_s32(coefficients128_0); - coefficients128_4 = ma_dr_flac__vrevq_s32(coefficients128_4); - coefficients128_8 = ma_dr_flac__vrevq_s32(coefficients128_8); - } - while (pDecodedSamples < pDecodedSamplesEnd) { - if (!ma_dr_flac__read_rice_parts_x1(bs, riceParam, &zeroCountParts[0], &riceParamParts[0]) || - !ma_dr_flac__read_rice_parts_x1(bs, riceParam, &zeroCountParts[1], &riceParamParts[1]) || - !ma_dr_flac__read_rice_parts_x1(bs, riceParam, &zeroCountParts[2], &riceParamParts[2]) || - !ma_dr_flac__read_rice_parts_x1(bs, riceParam, &zeroCountParts[3], &riceParamParts[3])) { - return MA_FALSE; - } - zeroCountPart128 = vld1q_u32(zeroCountParts); - riceParamPart128 = vld1q_u32(riceParamParts); - riceParamPart128 = vandq_u32(riceParamPart128, riceParamMask128); - riceParamPart128 = vorrq_u32(riceParamPart128, vshlq_u32(zeroCountPart128, riceParam128)); - riceParamPart128 = veorq_u32(vshrq_n_u32(riceParamPart128, 1), vaddq_u32(ma_dr_flac__vnotq_u32(vandq_u32(riceParamPart128, one128)), one128)); - for (i = 0; i < 4; i += 1) { - int64x1_t prediction64; - prediction128 = veorq_s64(prediction128, prediction128); - switch (order) - { - case 12: - case 11: prediction128 = vaddq_s64(prediction128, vmull_s32(vget_low_s32(coefficients128_8), vget_low_s32(samples128_8))); - case 10: - case 9: prediction128 = vaddq_s64(prediction128, vmull_s32(vget_high_s32(coefficients128_8), vget_high_s32(samples128_8))); - case 8: - case 7: prediction128 = vaddq_s64(prediction128, vmull_s32(vget_low_s32(coefficients128_4), vget_low_s32(samples128_4))); - case 6: - case 5: prediction128 = vaddq_s64(prediction128, vmull_s32(vget_high_s32(coefficients128_4), vget_high_s32(samples128_4))); - case 4: - case 3: prediction128 = vaddq_s64(prediction128, vmull_s32(vget_low_s32(coefficients128_0), vget_low_s32(samples128_0))); - case 2: - case 1: prediction128 = vaddq_s64(prediction128, vmull_s32(vget_high_s32(coefficients128_0), vget_high_s32(samples128_0))); - } - prediction64 = ma_dr_flac__vhaddq_s64(prediction128); - prediction64 = vshl_s64(prediction64, shift64); - prediction64 = vadd_s64(prediction64, vdup_n_s64(vgetq_lane_u32(riceParamPart128, 0))); - samples128_8 = ma_dr_flac__valignrq_s32_1(samples128_4, samples128_8); - samples128_4 = ma_dr_flac__valignrq_s32_1(samples128_0, samples128_4); - samples128_0 = ma_dr_flac__valignrq_s32_1(vcombine_s32(vreinterpret_s32_s64(prediction64), vdup_n_s32(0)), samples128_0); - riceParamPart128 = ma_dr_flac__valignrq_u32_1(vdupq_n_u32(0), riceParamPart128); - } - vst1q_s32(pDecodedSamples, samples128_0); - pDecodedSamples += 4; - } - i = (count & ~3); - while (i < (int)count) { - if (!ma_dr_flac__read_rice_parts_x1(bs, riceParam, &zeroCountParts[0], &riceParamParts[0])) { - return MA_FALSE; - } - riceParamParts[0] &= riceParamMask; - riceParamParts[0] |= (zeroCountParts[0] << riceParam); - riceParamParts[0] = (riceParamParts[0] >> 1) ^ t[riceParamParts[0] & 0x01]; - pDecodedSamples[0] = riceParamParts[0] + ma_dr_flac__calculate_prediction_64(order, shift, coefficients, pDecodedSamples); - i += 1; - pDecodedSamples += 1; - } - return MA_TRUE; -} -static ma_bool32 ma_dr_flac__decode_samples_with_residual__rice__neon(ma_dr_flac_bs* bs, ma_uint32 bitsPerSample, ma_uint32 count, ma_uint8 riceParam, ma_uint32 lpcOrder, ma_int32 lpcShift, ma_uint32 lpcPrecision, const ma_int32* coefficients, ma_int32* pSamplesOut) -{ - MA_DR_FLAC_ASSERT(bs != NULL); - MA_DR_FLAC_ASSERT(pSamplesOut != NULL); - if (lpcOrder > 0 && lpcOrder <= 12) { - if (ma_dr_flac__use_64_bit_prediction(bitsPerSample, lpcOrder, lpcPrecision)) { - return ma_dr_flac__decode_samples_with_residual__rice__neon_64(bs, count, riceParam, lpcOrder, lpcShift, coefficients, pSamplesOut); - } else { - return ma_dr_flac__decode_samples_with_residual__rice__neon_32(bs, count, riceParam, lpcOrder, lpcShift, coefficients, pSamplesOut); - } - } else { - return ma_dr_flac__decode_samples_with_residual__rice__scalar(bs, bitsPerSample, count, riceParam, lpcOrder, lpcShift, lpcPrecision, coefficients, pSamplesOut); - } -} -#endif -static ma_bool32 ma_dr_flac__decode_samples_with_residual__rice(ma_dr_flac_bs* bs, ma_uint32 bitsPerSample, ma_uint32 count, ma_uint8 riceParam, ma_uint32 lpcOrder, ma_int32 lpcShift, ma_uint32 lpcPrecision, const ma_int32* coefficients, ma_int32* pSamplesOut) -{ -#if defined(MA_DR_FLAC_SUPPORT_SSE41) - if (ma_dr_flac__gIsSSE41Supported) { - return ma_dr_flac__decode_samples_with_residual__rice__sse41(bs, bitsPerSample, count, riceParam, lpcOrder, lpcShift, lpcPrecision, coefficients, pSamplesOut); - } else -#elif defined(MA_DR_FLAC_SUPPORT_NEON) - if (ma_dr_flac__gIsNEONSupported) { - return ma_dr_flac__decode_samples_with_residual__rice__neon(bs, bitsPerSample, count, riceParam, lpcOrder, lpcShift, lpcPrecision, coefficients, pSamplesOut); - } else -#endif - { - #if 0 - return ma_dr_flac__decode_samples_with_residual__rice__reference(bs, bitsPerSample, count, riceParam, lpcOrder, lpcShift, lpcPrecision, coefficients, pSamplesOut); - #else - return ma_dr_flac__decode_samples_with_residual__rice__scalar(bs, bitsPerSample, count, riceParam, lpcOrder, lpcShift, lpcPrecision, coefficients, pSamplesOut); - #endif - } -} -static ma_bool32 ma_dr_flac__read_and_seek_residual__rice(ma_dr_flac_bs* bs, ma_uint32 count, ma_uint8 riceParam) -{ - ma_uint32 i; - MA_DR_FLAC_ASSERT(bs != NULL); - for (i = 0; i < count; ++i) { - if (!ma_dr_flac__seek_rice_parts(bs, riceParam)) { - return MA_FALSE; - } - } - return MA_TRUE; -} -#if defined(__clang__) -__attribute__((no_sanitize("signed-integer-overflow"))) -#endif -static ma_bool32 ma_dr_flac__decode_samples_with_residual__unencoded(ma_dr_flac_bs* bs, ma_uint32 bitsPerSample, ma_uint32 count, ma_uint8 unencodedBitsPerSample, ma_uint32 lpcOrder, ma_int32 lpcShift, ma_uint32 lpcPrecision, const ma_int32* coefficients, ma_int32* pSamplesOut) -{ - ma_uint32 i; - MA_DR_FLAC_ASSERT(bs != NULL); - MA_DR_FLAC_ASSERT(unencodedBitsPerSample <= 31); - MA_DR_FLAC_ASSERT(pSamplesOut != NULL); - for (i = 0; i < count; ++i) { - if (unencodedBitsPerSample > 0) { - if (!ma_dr_flac__read_int32(bs, unencodedBitsPerSample, pSamplesOut + i)) { - return MA_FALSE; - } - } else { - pSamplesOut[i] = 0; - } - if (ma_dr_flac__use_64_bit_prediction(bitsPerSample, lpcOrder, lpcPrecision)) { - pSamplesOut[i] += ma_dr_flac__calculate_prediction_64(lpcOrder, lpcShift, coefficients, pSamplesOut + i); - } else { - pSamplesOut[i] += ma_dr_flac__calculate_prediction_32(lpcOrder, lpcShift, coefficients, pSamplesOut + i); - } - } - return MA_TRUE; -} -static ma_bool32 ma_dr_flac__decode_samples_with_residual(ma_dr_flac_bs* bs, ma_uint32 bitsPerSample, ma_uint32 blockSize, ma_uint32 lpcOrder, ma_int32 lpcShift, ma_uint32 lpcPrecision, const ma_int32* coefficients, ma_int32* pDecodedSamples) -{ - ma_uint8 residualMethod; - ma_uint8 partitionOrder; - ma_uint32 samplesInPartition; - ma_uint32 partitionsRemaining; - MA_DR_FLAC_ASSERT(bs != NULL); - MA_DR_FLAC_ASSERT(blockSize != 0); - MA_DR_FLAC_ASSERT(pDecodedSamples != NULL); - if (!ma_dr_flac__read_uint8(bs, 2, &residualMethod)) { - return MA_FALSE; - } - if (residualMethod != MA_DR_FLAC_RESIDUAL_CODING_METHOD_PARTITIONED_RICE && residualMethod != MA_DR_FLAC_RESIDUAL_CODING_METHOD_PARTITIONED_RICE2) { - return MA_FALSE; - } - pDecodedSamples += lpcOrder; - if (!ma_dr_flac__read_uint8(bs, 4, &partitionOrder)) { - return MA_FALSE; - } - if (partitionOrder > 8) { - return MA_FALSE; - } - if ((blockSize / (1 << partitionOrder)) < lpcOrder) { - return MA_FALSE; - } - samplesInPartition = (blockSize / (1 << partitionOrder)) - lpcOrder; - partitionsRemaining = (1 << partitionOrder); - for (;;) { - ma_uint8 riceParam = 0; - if (residualMethod == MA_DR_FLAC_RESIDUAL_CODING_METHOD_PARTITIONED_RICE) { - if (!ma_dr_flac__read_uint8(bs, 4, &riceParam)) { - return MA_FALSE; - } - if (riceParam == 15) { - riceParam = 0xFF; - } - } else if (residualMethod == MA_DR_FLAC_RESIDUAL_CODING_METHOD_PARTITIONED_RICE2) { - if (!ma_dr_flac__read_uint8(bs, 5, &riceParam)) { - return MA_FALSE; - } - if (riceParam == 31) { - riceParam = 0xFF; - } - } - if (riceParam != 0xFF) { - if (!ma_dr_flac__decode_samples_with_residual__rice(bs, bitsPerSample, samplesInPartition, riceParam, lpcOrder, lpcShift, lpcPrecision, coefficients, pDecodedSamples)) { - return MA_FALSE; - } - } else { - ma_uint8 unencodedBitsPerSample = 0; - if (!ma_dr_flac__read_uint8(bs, 5, &unencodedBitsPerSample)) { - return MA_FALSE; - } - if (!ma_dr_flac__decode_samples_with_residual__unencoded(bs, bitsPerSample, samplesInPartition, unencodedBitsPerSample, lpcOrder, lpcShift, lpcPrecision, coefficients, pDecodedSamples)) { - return MA_FALSE; - } - } - pDecodedSamples += samplesInPartition; - if (partitionsRemaining == 1) { - break; - } - partitionsRemaining -= 1; - if (partitionOrder != 0) { - samplesInPartition = blockSize / (1 << partitionOrder); - } - } - return MA_TRUE; -} -static ma_bool32 ma_dr_flac__read_and_seek_residual(ma_dr_flac_bs* bs, ma_uint32 blockSize, ma_uint32 order) -{ - ma_uint8 residualMethod; - ma_uint8 partitionOrder; - ma_uint32 samplesInPartition; - ma_uint32 partitionsRemaining; - MA_DR_FLAC_ASSERT(bs != NULL); - MA_DR_FLAC_ASSERT(blockSize != 0); - if (!ma_dr_flac__read_uint8(bs, 2, &residualMethod)) { - return MA_FALSE; - } - if (residualMethod != MA_DR_FLAC_RESIDUAL_CODING_METHOD_PARTITIONED_RICE && residualMethod != MA_DR_FLAC_RESIDUAL_CODING_METHOD_PARTITIONED_RICE2) { - return MA_FALSE; - } - if (!ma_dr_flac__read_uint8(bs, 4, &partitionOrder)) { - return MA_FALSE; - } - if (partitionOrder > 8) { - return MA_FALSE; - } - if ((blockSize / (1 << partitionOrder)) <= order) { - return MA_FALSE; - } - samplesInPartition = (blockSize / (1 << partitionOrder)) - order; - partitionsRemaining = (1 << partitionOrder); - for (;;) - { - ma_uint8 riceParam = 0; - if (residualMethod == MA_DR_FLAC_RESIDUAL_CODING_METHOD_PARTITIONED_RICE) { - if (!ma_dr_flac__read_uint8(bs, 4, &riceParam)) { - return MA_FALSE; - } - if (riceParam == 15) { - riceParam = 0xFF; - } - } else if (residualMethod == MA_DR_FLAC_RESIDUAL_CODING_METHOD_PARTITIONED_RICE2) { - if (!ma_dr_flac__read_uint8(bs, 5, &riceParam)) { - return MA_FALSE; - } - if (riceParam == 31) { - riceParam = 0xFF; - } - } - if (riceParam != 0xFF) { - if (!ma_dr_flac__read_and_seek_residual__rice(bs, samplesInPartition, riceParam)) { - return MA_FALSE; - } - } else { - ma_uint8 unencodedBitsPerSample = 0; - if (!ma_dr_flac__read_uint8(bs, 5, &unencodedBitsPerSample)) { - return MA_FALSE; - } - if (!ma_dr_flac__seek_bits(bs, unencodedBitsPerSample * samplesInPartition)) { - return MA_FALSE; - } - } - if (partitionsRemaining == 1) { - break; - } - partitionsRemaining -= 1; - samplesInPartition = blockSize / (1 << partitionOrder); - } - return MA_TRUE; -} -static ma_bool32 ma_dr_flac__decode_samples__constant(ma_dr_flac_bs* bs, ma_uint32 blockSize, ma_uint32 subframeBitsPerSample, ma_int32* pDecodedSamples) -{ - ma_uint32 i; - ma_int32 sample; - if (!ma_dr_flac__read_int32(bs, subframeBitsPerSample, &sample)) { - return MA_FALSE; - } - for (i = 0; i < blockSize; ++i) { - pDecodedSamples[i] = sample; - } - return MA_TRUE; -} -static ma_bool32 ma_dr_flac__decode_samples__verbatim(ma_dr_flac_bs* bs, ma_uint32 blockSize, ma_uint32 subframeBitsPerSample, ma_int32* pDecodedSamples) -{ - ma_uint32 i; - for (i = 0; i < blockSize; ++i) { - ma_int32 sample; - if (!ma_dr_flac__read_int32(bs, subframeBitsPerSample, &sample)) { - return MA_FALSE; - } - pDecodedSamples[i] = sample; - } - return MA_TRUE; -} -static ma_bool32 ma_dr_flac__decode_samples__fixed(ma_dr_flac_bs* bs, ma_uint32 blockSize, ma_uint32 subframeBitsPerSample, ma_uint8 lpcOrder, ma_int32* pDecodedSamples) -{ - ma_uint32 i; - static ma_int32 lpcCoefficientsTable[5][4] = { - {0, 0, 0, 0}, - {1, 0, 0, 0}, - {2, -1, 0, 0}, - {3, -3, 1, 0}, - {4, -6, 4, -1} - }; - for (i = 0; i < lpcOrder; ++i) { - ma_int32 sample; - if (!ma_dr_flac__read_int32(bs, subframeBitsPerSample, &sample)) { - return MA_FALSE; - } - pDecodedSamples[i] = sample; - } - if (!ma_dr_flac__decode_samples_with_residual(bs, subframeBitsPerSample, blockSize, lpcOrder, 0, 4, lpcCoefficientsTable[lpcOrder], pDecodedSamples)) { - return MA_FALSE; - } - return MA_TRUE; -} -static ma_bool32 ma_dr_flac__decode_samples__lpc(ma_dr_flac_bs* bs, ma_uint32 blockSize, ma_uint32 bitsPerSample, ma_uint8 lpcOrder, ma_int32* pDecodedSamples) -{ - ma_uint8 i; - ma_uint8 lpcPrecision; - ma_int8 lpcShift; - ma_int32 coefficients[32]; - for (i = 0; i < lpcOrder; ++i) { - ma_int32 sample; - if (!ma_dr_flac__read_int32(bs, bitsPerSample, &sample)) { - return MA_FALSE; - } - pDecodedSamples[i] = sample; - } - if (!ma_dr_flac__read_uint8(bs, 4, &lpcPrecision)) { - return MA_FALSE; - } - if (lpcPrecision == 15) { - return MA_FALSE; - } - lpcPrecision += 1; - if (!ma_dr_flac__read_int8(bs, 5, &lpcShift)) { - return MA_FALSE; - } - if (lpcShift < 0) { - return MA_FALSE; - } - MA_DR_FLAC_ZERO_MEMORY(coefficients, sizeof(coefficients)); - for (i = 0; i < lpcOrder; ++i) { - if (!ma_dr_flac__read_int32(bs, lpcPrecision, coefficients + i)) { - return MA_FALSE; - } - } - if (!ma_dr_flac__decode_samples_with_residual(bs, bitsPerSample, blockSize, lpcOrder, lpcShift, lpcPrecision, coefficients, pDecodedSamples)) { - return MA_FALSE; - } - return MA_TRUE; -} -static ma_bool32 ma_dr_flac__read_next_flac_frame_header(ma_dr_flac_bs* bs, ma_uint8 streaminfoBitsPerSample, ma_dr_flac_frame_header* header) -{ - const ma_uint32 sampleRateTable[12] = {0, 88200, 176400, 192000, 8000, 16000, 22050, 24000, 32000, 44100, 48000, 96000}; - const ma_uint8 bitsPerSampleTable[8] = {0, 8, 12, (ma_uint8)-1, 16, 20, 24, (ma_uint8)-1}; - MA_DR_FLAC_ASSERT(bs != NULL); - MA_DR_FLAC_ASSERT(header != NULL); - for (;;) { - ma_uint8 crc8 = 0xCE; - ma_uint8 reserved = 0; - ma_uint8 blockingStrategy = 0; - ma_uint8 blockSize = 0; - ma_uint8 sampleRate = 0; - ma_uint8 channelAssignment = 0; - ma_uint8 bitsPerSample = 0; - ma_bool32 isVariableBlockSize; - if (!ma_dr_flac__find_and_seek_to_next_sync_code(bs)) { - return MA_FALSE; - } - if (!ma_dr_flac__read_uint8(bs, 1, &reserved)) { - return MA_FALSE; - } - if (reserved == 1) { - continue; - } - crc8 = ma_dr_flac_crc8(crc8, reserved, 1); - if (!ma_dr_flac__read_uint8(bs, 1, &blockingStrategy)) { - return MA_FALSE; - } - crc8 = ma_dr_flac_crc8(crc8, blockingStrategy, 1); - if (!ma_dr_flac__read_uint8(bs, 4, &blockSize)) { - return MA_FALSE; - } - if (blockSize == 0) { - continue; - } - crc8 = ma_dr_flac_crc8(crc8, blockSize, 4); - if (!ma_dr_flac__read_uint8(bs, 4, &sampleRate)) { - return MA_FALSE; - } - crc8 = ma_dr_flac_crc8(crc8, sampleRate, 4); - if (!ma_dr_flac__read_uint8(bs, 4, &channelAssignment)) { - return MA_FALSE; - } - if (channelAssignment > 10) { - continue; - } - crc8 = ma_dr_flac_crc8(crc8, channelAssignment, 4); - if (!ma_dr_flac__read_uint8(bs, 3, &bitsPerSample)) { - return MA_FALSE; - } - if (bitsPerSample == 3 || bitsPerSample == 7) { - continue; - } - crc8 = ma_dr_flac_crc8(crc8, bitsPerSample, 3); - if (!ma_dr_flac__read_uint8(bs, 1, &reserved)) { - return MA_FALSE; - } - if (reserved == 1) { - continue; - } - crc8 = ma_dr_flac_crc8(crc8, reserved, 1); - isVariableBlockSize = blockingStrategy == 1; - if (isVariableBlockSize) { - ma_uint64 pcmFrameNumber; - ma_result result = ma_dr_flac__read_utf8_coded_number(bs, &pcmFrameNumber, &crc8); - if (result != MA_SUCCESS) { - if (result == MA_AT_END) { - return MA_FALSE; - } else { - continue; - } - } - header->flacFrameNumber = 0; - header->pcmFrameNumber = pcmFrameNumber; - } else { - ma_uint64 flacFrameNumber = 0; - ma_result result = ma_dr_flac__read_utf8_coded_number(bs, &flacFrameNumber, &crc8); - if (result != MA_SUCCESS) { - if (result == MA_AT_END) { - return MA_FALSE; - } else { - continue; - } - } - header->flacFrameNumber = (ma_uint32)flacFrameNumber; - header->pcmFrameNumber = 0; - } - MA_DR_FLAC_ASSERT(blockSize > 0); - if (blockSize == 1) { - header->blockSizeInPCMFrames = 192; - } else if (blockSize <= 5) { - MA_DR_FLAC_ASSERT(blockSize >= 2); - header->blockSizeInPCMFrames = 576 * (1 << (blockSize - 2)); - } else if (blockSize == 6) { - if (!ma_dr_flac__read_uint16(bs, 8, &header->blockSizeInPCMFrames)) { - return MA_FALSE; - } - crc8 = ma_dr_flac_crc8(crc8, header->blockSizeInPCMFrames, 8); - header->blockSizeInPCMFrames += 1; - } else if (blockSize == 7) { - if (!ma_dr_flac__read_uint16(bs, 16, &header->blockSizeInPCMFrames)) { - return MA_FALSE; - } - crc8 = ma_dr_flac_crc8(crc8, header->blockSizeInPCMFrames, 16); - if (header->blockSizeInPCMFrames == 0xFFFF) { - return MA_FALSE; - } - header->blockSizeInPCMFrames += 1; - } else { - MA_DR_FLAC_ASSERT(blockSize >= 8); - header->blockSizeInPCMFrames = 256 * (1 << (blockSize - 8)); - } - if (sampleRate <= 11) { - header->sampleRate = sampleRateTable[sampleRate]; - } else if (sampleRate == 12) { - if (!ma_dr_flac__read_uint32(bs, 8, &header->sampleRate)) { - return MA_FALSE; - } - crc8 = ma_dr_flac_crc8(crc8, header->sampleRate, 8); - header->sampleRate *= 1000; - } else if (sampleRate == 13) { - if (!ma_dr_flac__read_uint32(bs, 16, &header->sampleRate)) { - return MA_FALSE; - } - crc8 = ma_dr_flac_crc8(crc8, header->sampleRate, 16); - } else if (sampleRate == 14) { - if (!ma_dr_flac__read_uint32(bs, 16, &header->sampleRate)) { - return MA_FALSE; - } - crc8 = ma_dr_flac_crc8(crc8, header->sampleRate, 16); - header->sampleRate *= 10; - } else { - continue; - } - header->channelAssignment = channelAssignment; - header->bitsPerSample = bitsPerSampleTable[bitsPerSample]; - if (header->bitsPerSample == 0) { - header->bitsPerSample = streaminfoBitsPerSample; - } - if (header->bitsPerSample != streaminfoBitsPerSample) { - return MA_FALSE; - } - if (!ma_dr_flac__read_uint8(bs, 8, &header->crc8)) { - return MA_FALSE; - } -#ifndef MA_DR_FLAC_NO_CRC - if (header->crc8 != crc8) { - continue; - } -#endif - return MA_TRUE; - } -} -static ma_bool32 ma_dr_flac__read_subframe_header(ma_dr_flac_bs* bs, ma_dr_flac_subframe* pSubframe) -{ - ma_uint8 header; - int type; - if (!ma_dr_flac__read_uint8(bs, 8, &header)) { - return MA_FALSE; - } - if ((header & 0x80) != 0) { - return MA_FALSE; - } - pSubframe->lpcOrder = 0; - type = (header & 0x7E) >> 1; - if (type == 0) { - pSubframe->subframeType = MA_DR_FLAC_SUBFRAME_CONSTANT; - } else if (type == 1) { - pSubframe->subframeType = MA_DR_FLAC_SUBFRAME_VERBATIM; - } else { - if ((type & 0x20) != 0) { - pSubframe->subframeType = MA_DR_FLAC_SUBFRAME_LPC; - pSubframe->lpcOrder = (ma_uint8)(type & 0x1F) + 1; - } else if ((type & 0x08) != 0) { - pSubframe->subframeType = MA_DR_FLAC_SUBFRAME_FIXED; - pSubframe->lpcOrder = (ma_uint8)(type & 0x07); - if (pSubframe->lpcOrder > 4) { - pSubframe->subframeType = MA_DR_FLAC_SUBFRAME_RESERVED; - pSubframe->lpcOrder = 0; - } - } else { - pSubframe->subframeType = MA_DR_FLAC_SUBFRAME_RESERVED; - } - } - if (pSubframe->subframeType == MA_DR_FLAC_SUBFRAME_RESERVED) { - return MA_FALSE; - } - pSubframe->wastedBitsPerSample = 0; - if ((header & 0x01) == 1) { - unsigned int wastedBitsPerSample; - if (!ma_dr_flac__seek_past_next_set_bit(bs, &wastedBitsPerSample)) { - return MA_FALSE; - } - pSubframe->wastedBitsPerSample = (ma_uint8)wastedBitsPerSample + 1; - } - return MA_TRUE; -} -static ma_bool32 ma_dr_flac__decode_subframe(ma_dr_flac_bs* bs, ma_dr_flac_frame* frame, int subframeIndex, ma_int32* pDecodedSamplesOut) -{ - ma_dr_flac_subframe* pSubframe; - ma_uint32 subframeBitsPerSample; - MA_DR_FLAC_ASSERT(bs != NULL); - MA_DR_FLAC_ASSERT(frame != NULL); - pSubframe = frame->subframes + subframeIndex; - if (!ma_dr_flac__read_subframe_header(bs, pSubframe)) { - return MA_FALSE; - } - subframeBitsPerSample = frame->header.bitsPerSample; - if ((frame->header.channelAssignment == MA_DR_FLAC_CHANNEL_ASSIGNMENT_LEFT_SIDE || frame->header.channelAssignment == MA_DR_FLAC_CHANNEL_ASSIGNMENT_MID_SIDE) && subframeIndex == 1) { - subframeBitsPerSample += 1; - } else if (frame->header.channelAssignment == MA_DR_FLAC_CHANNEL_ASSIGNMENT_RIGHT_SIDE && subframeIndex == 0) { - subframeBitsPerSample += 1; - } - if (subframeBitsPerSample > 32) { - return MA_FALSE; - } - if (pSubframe->wastedBitsPerSample >= subframeBitsPerSample) { - return MA_FALSE; - } - subframeBitsPerSample -= pSubframe->wastedBitsPerSample; - pSubframe->pSamplesS32 = pDecodedSamplesOut; - if (frame->header.blockSizeInPCMFrames < pSubframe->lpcOrder) { - return MA_FALSE; - } - switch (pSubframe->subframeType) - { - case MA_DR_FLAC_SUBFRAME_CONSTANT: - { - ma_dr_flac__decode_samples__constant(bs, frame->header.blockSizeInPCMFrames, subframeBitsPerSample, pSubframe->pSamplesS32); - } break; - case MA_DR_FLAC_SUBFRAME_VERBATIM: - { - ma_dr_flac__decode_samples__verbatim(bs, frame->header.blockSizeInPCMFrames, subframeBitsPerSample, pSubframe->pSamplesS32); - } break; - case MA_DR_FLAC_SUBFRAME_FIXED: - { - ma_dr_flac__decode_samples__fixed(bs, frame->header.blockSizeInPCMFrames, subframeBitsPerSample, pSubframe->lpcOrder, pSubframe->pSamplesS32); - } break; - case MA_DR_FLAC_SUBFRAME_LPC: - { - ma_dr_flac__decode_samples__lpc(bs, frame->header.blockSizeInPCMFrames, subframeBitsPerSample, pSubframe->lpcOrder, pSubframe->pSamplesS32); - } break; - default: return MA_FALSE; - } - return MA_TRUE; -} -static ma_bool32 ma_dr_flac__seek_subframe(ma_dr_flac_bs* bs, ma_dr_flac_frame* frame, int subframeIndex) -{ - ma_dr_flac_subframe* pSubframe; - ma_uint32 subframeBitsPerSample; - MA_DR_FLAC_ASSERT(bs != NULL); - MA_DR_FLAC_ASSERT(frame != NULL); - pSubframe = frame->subframes + subframeIndex; - if (!ma_dr_flac__read_subframe_header(bs, pSubframe)) { - return MA_FALSE; - } - subframeBitsPerSample = frame->header.bitsPerSample; - if ((frame->header.channelAssignment == MA_DR_FLAC_CHANNEL_ASSIGNMENT_LEFT_SIDE || frame->header.channelAssignment == MA_DR_FLAC_CHANNEL_ASSIGNMENT_MID_SIDE) && subframeIndex == 1) { - subframeBitsPerSample += 1; - } else if (frame->header.channelAssignment == MA_DR_FLAC_CHANNEL_ASSIGNMENT_RIGHT_SIDE && subframeIndex == 0) { - subframeBitsPerSample += 1; - } - if (pSubframe->wastedBitsPerSample >= subframeBitsPerSample) { - return MA_FALSE; - } - subframeBitsPerSample -= pSubframe->wastedBitsPerSample; - pSubframe->pSamplesS32 = NULL; - switch (pSubframe->subframeType) - { - case MA_DR_FLAC_SUBFRAME_CONSTANT: - { - if (!ma_dr_flac__seek_bits(bs, subframeBitsPerSample)) { - return MA_FALSE; - } - } break; - case MA_DR_FLAC_SUBFRAME_VERBATIM: - { - unsigned int bitsToSeek = frame->header.blockSizeInPCMFrames * subframeBitsPerSample; - if (!ma_dr_flac__seek_bits(bs, bitsToSeek)) { - return MA_FALSE; - } - } break; - case MA_DR_FLAC_SUBFRAME_FIXED: - { - unsigned int bitsToSeek = pSubframe->lpcOrder * subframeBitsPerSample; - if (!ma_dr_flac__seek_bits(bs, bitsToSeek)) { - return MA_FALSE; - } - if (!ma_dr_flac__read_and_seek_residual(bs, frame->header.blockSizeInPCMFrames, pSubframe->lpcOrder)) { - return MA_FALSE; - } - } break; - case MA_DR_FLAC_SUBFRAME_LPC: - { - ma_uint8 lpcPrecision; - unsigned int bitsToSeek = pSubframe->lpcOrder * subframeBitsPerSample; - if (!ma_dr_flac__seek_bits(bs, bitsToSeek)) { - return MA_FALSE; - } - if (!ma_dr_flac__read_uint8(bs, 4, &lpcPrecision)) { - return MA_FALSE; - } - if (lpcPrecision == 15) { - return MA_FALSE; - } - lpcPrecision += 1; - bitsToSeek = (pSubframe->lpcOrder * lpcPrecision) + 5; - if (!ma_dr_flac__seek_bits(bs, bitsToSeek)) { - return MA_FALSE; - } - if (!ma_dr_flac__read_and_seek_residual(bs, frame->header.blockSizeInPCMFrames, pSubframe->lpcOrder)) { - return MA_FALSE; - } - } break; - default: return MA_FALSE; - } - return MA_TRUE; -} -static MA_INLINE ma_uint8 ma_dr_flac__get_channel_count_from_channel_assignment(ma_int8 channelAssignment) -{ - ma_uint8 lookup[] = {1, 2, 3, 4, 5, 6, 7, 8, 2, 2, 2}; - MA_DR_FLAC_ASSERT(channelAssignment <= 10); - return lookup[channelAssignment]; -} -static ma_result ma_dr_flac__decode_flac_frame(ma_dr_flac* pFlac) -{ - int channelCount; - int i; - ma_uint8 paddingSizeInBits; - ma_uint16 desiredCRC16; -#ifndef MA_DR_FLAC_NO_CRC - ma_uint16 actualCRC16; -#endif - MA_DR_FLAC_ZERO_MEMORY(pFlac->currentFLACFrame.subframes, sizeof(pFlac->currentFLACFrame.subframes)); - if (pFlac->currentFLACFrame.header.blockSizeInPCMFrames > pFlac->maxBlockSizeInPCMFrames) { - return MA_ERROR; - } - channelCount = ma_dr_flac__get_channel_count_from_channel_assignment(pFlac->currentFLACFrame.header.channelAssignment); - if (channelCount != (int)pFlac->channels) { - return MA_ERROR; - } - for (i = 0; i < channelCount; ++i) { - if (!ma_dr_flac__decode_subframe(&pFlac->bs, &pFlac->currentFLACFrame, i, pFlac->pDecodedSamples + (pFlac->currentFLACFrame.header.blockSizeInPCMFrames * i))) { - return MA_ERROR; - } - } - paddingSizeInBits = (ma_uint8)(MA_DR_FLAC_CACHE_L1_BITS_REMAINING(&pFlac->bs) & 7); - if (paddingSizeInBits > 0) { - ma_uint8 padding = 0; - if (!ma_dr_flac__read_uint8(&pFlac->bs, paddingSizeInBits, &padding)) { - return MA_AT_END; - } - } -#ifndef MA_DR_FLAC_NO_CRC - actualCRC16 = ma_dr_flac__flush_crc16(&pFlac->bs); -#endif - if (!ma_dr_flac__read_uint16(&pFlac->bs, 16, &desiredCRC16)) { - return MA_AT_END; - } -#ifndef MA_DR_FLAC_NO_CRC - if (actualCRC16 != desiredCRC16) { - return MA_CRC_MISMATCH; - } -#endif - pFlac->currentFLACFrame.pcmFramesRemaining = pFlac->currentFLACFrame.header.blockSizeInPCMFrames; - return MA_SUCCESS; -} -static ma_result ma_dr_flac__seek_flac_frame(ma_dr_flac* pFlac) -{ - int channelCount; - int i; - ma_uint16 desiredCRC16; -#ifndef MA_DR_FLAC_NO_CRC - ma_uint16 actualCRC16; -#endif - channelCount = ma_dr_flac__get_channel_count_from_channel_assignment(pFlac->currentFLACFrame.header.channelAssignment); - for (i = 0; i < channelCount; ++i) { - if (!ma_dr_flac__seek_subframe(&pFlac->bs, &pFlac->currentFLACFrame, i)) { - return MA_ERROR; - } - } - if (!ma_dr_flac__seek_bits(&pFlac->bs, MA_DR_FLAC_CACHE_L1_BITS_REMAINING(&pFlac->bs) & 7)) { - return MA_ERROR; - } -#ifndef MA_DR_FLAC_NO_CRC - actualCRC16 = ma_dr_flac__flush_crc16(&pFlac->bs); -#endif - if (!ma_dr_flac__read_uint16(&pFlac->bs, 16, &desiredCRC16)) { - return MA_AT_END; - } -#ifndef MA_DR_FLAC_NO_CRC - if (actualCRC16 != desiredCRC16) { - return MA_CRC_MISMATCH; - } -#endif - return MA_SUCCESS; -} -static ma_bool32 ma_dr_flac__read_and_decode_next_flac_frame(ma_dr_flac* pFlac) -{ - MA_DR_FLAC_ASSERT(pFlac != NULL); - for (;;) { - ma_result result; - if (!ma_dr_flac__read_next_flac_frame_header(&pFlac->bs, pFlac->bitsPerSample, &pFlac->currentFLACFrame.header)) { - return MA_FALSE; - } - result = ma_dr_flac__decode_flac_frame(pFlac); - if (result != MA_SUCCESS) { - if (result == MA_CRC_MISMATCH) { - continue; - } else { - return MA_FALSE; - } - } - return MA_TRUE; - } -} -static void ma_dr_flac__get_pcm_frame_range_of_current_flac_frame(ma_dr_flac* pFlac, ma_uint64* pFirstPCMFrame, ma_uint64* pLastPCMFrame) -{ - ma_uint64 firstPCMFrame; - ma_uint64 lastPCMFrame; - MA_DR_FLAC_ASSERT(pFlac != NULL); - firstPCMFrame = pFlac->currentFLACFrame.header.pcmFrameNumber; - if (firstPCMFrame == 0) { - firstPCMFrame = ((ma_uint64)pFlac->currentFLACFrame.header.flacFrameNumber) * pFlac->maxBlockSizeInPCMFrames; - } - lastPCMFrame = firstPCMFrame + pFlac->currentFLACFrame.header.blockSizeInPCMFrames; - if (lastPCMFrame > 0) { - lastPCMFrame -= 1; - } - if (pFirstPCMFrame) { - *pFirstPCMFrame = firstPCMFrame; - } - if (pLastPCMFrame) { - *pLastPCMFrame = lastPCMFrame; - } -} -static ma_bool32 ma_dr_flac__seek_to_first_frame(ma_dr_flac* pFlac) -{ - ma_bool32 result; - MA_DR_FLAC_ASSERT(pFlac != NULL); - result = ma_dr_flac__seek_to_byte(&pFlac->bs, pFlac->firstFLACFramePosInBytes); - MA_DR_FLAC_ZERO_MEMORY(&pFlac->currentFLACFrame, sizeof(pFlac->currentFLACFrame)); - pFlac->currentPCMFrame = 0; - return result; -} -static MA_INLINE ma_result ma_dr_flac__seek_to_next_flac_frame(ma_dr_flac* pFlac) -{ - MA_DR_FLAC_ASSERT(pFlac != NULL); - return ma_dr_flac__seek_flac_frame(pFlac); -} -static ma_uint64 ma_dr_flac__seek_forward_by_pcm_frames(ma_dr_flac* pFlac, ma_uint64 pcmFramesToSeek) -{ - ma_uint64 pcmFramesRead = 0; - while (pcmFramesToSeek > 0) { - if (pFlac->currentFLACFrame.pcmFramesRemaining == 0) { - if (!ma_dr_flac__read_and_decode_next_flac_frame(pFlac)) { - break; - } - } else { - if (pFlac->currentFLACFrame.pcmFramesRemaining > pcmFramesToSeek) { - pcmFramesRead += pcmFramesToSeek; - pFlac->currentFLACFrame.pcmFramesRemaining -= (ma_uint32)pcmFramesToSeek; - pcmFramesToSeek = 0; - } else { - pcmFramesRead += pFlac->currentFLACFrame.pcmFramesRemaining; - pcmFramesToSeek -= pFlac->currentFLACFrame.pcmFramesRemaining; - pFlac->currentFLACFrame.pcmFramesRemaining = 0; - } - } - } - pFlac->currentPCMFrame += pcmFramesRead; - return pcmFramesRead; -} -static ma_bool32 ma_dr_flac__seek_to_pcm_frame__brute_force(ma_dr_flac* pFlac, ma_uint64 pcmFrameIndex) -{ - ma_bool32 isMidFrame = MA_FALSE; - ma_uint64 runningPCMFrameCount; - MA_DR_FLAC_ASSERT(pFlac != NULL); - if (pcmFrameIndex >= pFlac->currentPCMFrame) { - runningPCMFrameCount = pFlac->currentPCMFrame; - if (pFlac->currentPCMFrame == 0 && pFlac->currentFLACFrame.pcmFramesRemaining == 0) { - if (!ma_dr_flac__read_next_flac_frame_header(&pFlac->bs, pFlac->bitsPerSample, &pFlac->currentFLACFrame.header)) { - return MA_FALSE; - } - } else { - isMidFrame = MA_TRUE; - } - } else { - runningPCMFrameCount = 0; - if (!ma_dr_flac__seek_to_first_frame(pFlac)) { - return MA_FALSE; - } - if (!ma_dr_flac__read_next_flac_frame_header(&pFlac->bs, pFlac->bitsPerSample, &pFlac->currentFLACFrame.header)) { - return MA_FALSE; - } - } - for (;;) { - ma_uint64 pcmFrameCountInThisFLACFrame; - ma_uint64 firstPCMFrameInFLACFrame = 0; - ma_uint64 lastPCMFrameInFLACFrame = 0; - ma_dr_flac__get_pcm_frame_range_of_current_flac_frame(pFlac, &firstPCMFrameInFLACFrame, &lastPCMFrameInFLACFrame); - pcmFrameCountInThisFLACFrame = (lastPCMFrameInFLACFrame - firstPCMFrameInFLACFrame) + 1; - if (pcmFrameIndex < (runningPCMFrameCount + pcmFrameCountInThisFLACFrame)) { - ma_uint64 pcmFramesToDecode = pcmFrameIndex - runningPCMFrameCount; - if (!isMidFrame) { - ma_result result = ma_dr_flac__decode_flac_frame(pFlac); - if (result == MA_SUCCESS) { - return ma_dr_flac__seek_forward_by_pcm_frames(pFlac, pcmFramesToDecode) == pcmFramesToDecode; - } else { - if (result == MA_CRC_MISMATCH) { - goto next_iteration; - } else { - return MA_FALSE; - } - } - } else { - return ma_dr_flac__seek_forward_by_pcm_frames(pFlac, pcmFramesToDecode) == pcmFramesToDecode; - } - } else { - if (!isMidFrame) { - ma_result result = ma_dr_flac__seek_to_next_flac_frame(pFlac); - if (result == MA_SUCCESS) { - runningPCMFrameCount += pcmFrameCountInThisFLACFrame; - } else { - if (result == MA_CRC_MISMATCH) { - goto next_iteration; - } else { - return MA_FALSE; - } - } - } else { - runningPCMFrameCount += pFlac->currentFLACFrame.pcmFramesRemaining; - pFlac->currentFLACFrame.pcmFramesRemaining = 0; - isMidFrame = MA_FALSE; - } - if (pcmFrameIndex == pFlac->totalPCMFrameCount && runningPCMFrameCount == pFlac->totalPCMFrameCount) { - return MA_TRUE; - } - } - next_iteration: - if (!ma_dr_flac__read_next_flac_frame_header(&pFlac->bs, pFlac->bitsPerSample, &pFlac->currentFLACFrame.header)) { - return MA_FALSE; - } - } -} -#if !defined(MA_DR_FLAC_NO_CRC) -#define MA_DR_FLAC_BINARY_SEARCH_APPROX_COMPRESSION_RATIO 0.6f -static ma_bool32 ma_dr_flac__seek_to_approximate_flac_frame_to_byte(ma_dr_flac* pFlac, ma_uint64 targetByte, ma_uint64 rangeLo, ma_uint64 rangeHi, ma_uint64* pLastSuccessfulSeekOffset) -{ - MA_DR_FLAC_ASSERT(pFlac != NULL); - MA_DR_FLAC_ASSERT(pLastSuccessfulSeekOffset != NULL); - MA_DR_FLAC_ASSERT(targetByte >= rangeLo); - MA_DR_FLAC_ASSERT(targetByte <= rangeHi); - *pLastSuccessfulSeekOffset = pFlac->firstFLACFramePosInBytes; - for (;;) { - ma_uint64 lastTargetByte = targetByte; - if (!ma_dr_flac__seek_to_byte(&pFlac->bs, targetByte)) { - if (targetByte == 0) { - ma_dr_flac__seek_to_first_frame(pFlac); - return MA_FALSE; - } - targetByte = rangeLo + ((rangeHi - rangeLo)/2); - rangeHi = targetByte; - } else { - MA_DR_FLAC_ZERO_MEMORY(&pFlac->currentFLACFrame, sizeof(pFlac->currentFLACFrame)); -#if 1 - if (!ma_dr_flac__read_and_decode_next_flac_frame(pFlac)) { - targetByte = rangeLo + ((rangeHi - rangeLo)/2); - rangeHi = targetByte; - } else { - break; - } -#else - if (!ma_dr_flac__read_next_flac_frame_header(&pFlac->bs, pFlac->bitsPerSample, &pFlac->currentFLACFrame.header)) { - targetByte = rangeLo + ((rangeHi - rangeLo)/2); - rangeHi = targetByte; - } else { - break; - } -#endif - } - if(targetByte == lastTargetByte) { - return MA_FALSE; - } - } - ma_dr_flac__get_pcm_frame_range_of_current_flac_frame(pFlac, &pFlac->currentPCMFrame, NULL); - MA_DR_FLAC_ASSERT(targetByte <= rangeHi); - *pLastSuccessfulSeekOffset = targetByte; - return MA_TRUE; -} -static ma_bool32 ma_dr_flac__decode_flac_frame_and_seek_forward_by_pcm_frames(ma_dr_flac* pFlac, ma_uint64 offset) -{ -#if 0 - if (ma_dr_flac__decode_flac_frame(pFlac) != MA_SUCCESS) { - if (ma_dr_flac__read_and_decode_next_flac_frame(pFlac) == MA_FALSE) { - return MA_FALSE; - } - } -#endif - return ma_dr_flac__seek_forward_by_pcm_frames(pFlac, offset) == offset; -} -static ma_bool32 ma_dr_flac__seek_to_pcm_frame__binary_search_internal(ma_dr_flac* pFlac, ma_uint64 pcmFrameIndex, ma_uint64 byteRangeLo, ma_uint64 byteRangeHi) -{ - ma_uint64 targetByte; - ma_uint64 pcmRangeLo = pFlac->totalPCMFrameCount; - ma_uint64 pcmRangeHi = 0; - ma_uint64 lastSuccessfulSeekOffset = (ma_uint64)-1; - ma_uint64 closestSeekOffsetBeforeTargetPCMFrame = byteRangeLo; - ma_uint32 seekForwardThreshold = (pFlac->maxBlockSizeInPCMFrames != 0) ? pFlac->maxBlockSizeInPCMFrames*2 : 4096; - targetByte = byteRangeLo + (ma_uint64)(((ma_int64)((pcmFrameIndex - pFlac->currentPCMFrame) * pFlac->channels * pFlac->bitsPerSample)/8.0f) * MA_DR_FLAC_BINARY_SEARCH_APPROX_COMPRESSION_RATIO); - if (targetByte > byteRangeHi) { - targetByte = byteRangeHi; - } - for (;;) { - if (ma_dr_flac__seek_to_approximate_flac_frame_to_byte(pFlac, targetByte, byteRangeLo, byteRangeHi, &lastSuccessfulSeekOffset)) { - ma_uint64 newPCMRangeLo; - ma_uint64 newPCMRangeHi; - ma_dr_flac__get_pcm_frame_range_of_current_flac_frame(pFlac, &newPCMRangeLo, &newPCMRangeHi); - if (pcmRangeLo == newPCMRangeLo) { - if (!ma_dr_flac__seek_to_approximate_flac_frame_to_byte(pFlac, closestSeekOffsetBeforeTargetPCMFrame, closestSeekOffsetBeforeTargetPCMFrame, byteRangeHi, &lastSuccessfulSeekOffset)) { - break; - } - if (ma_dr_flac__decode_flac_frame_and_seek_forward_by_pcm_frames(pFlac, pcmFrameIndex - pFlac->currentPCMFrame)) { - return MA_TRUE; - } else { - break; - } - } - pcmRangeLo = newPCMRangeLo; - pcmRangeHi = newPCMRangeHi; - if (pcmRangeLo <= pcmFrameIndex && pcmRangeHi >= pcmFrameIndex) { - if (ma_dr_flac__decode_flac_frame_and_seek_forward_by_pcm_frames(pFlac, pcmFrameIndex - pFlac->currentPCMFrame) ) { - return MA_TRUE; - } else { - break; - } - } else { - const float approxCompressionRatio = (ma_int64)(lastSuccessfulSeekOffset - pFlac->firstFLACFramePosInBytes) / ((ma_int64)(pcmRangeLo * pFlac->channels * pFlac->bitsPerSample)/8.0f); - if (pcmRangeLo > pcmFrameIndex) { - byteRangeHi = lastSuccessfulSeekOffset; - if (byteRangeLo > byteRangeHi) { - byteRangeLo = byteRangeHi; - } - targetByte = byteRangeLo + ((byteRangeHi - byteRangeLo) / 2); - if (targetByte < byteRangeLo) { - targetByte = byteRangeLo; - } - } else { - if ((pcmFrameIndex - pcmRangeLo) < seekForwardThreshold) { - if (ma_dr_flac__decode_flac_frame_and_seek_forward_by_pcm_frames(pFlac, pcmFrameIndex - pFlac->currentPCMFrame)) { - return MA_TRUE; - } else { - break; - } - } else { - byteRangeLo = lastSuccessfulSeekOffset; - if (byteRangeHi < byteRangeLo) { - byteRangeHi = byteRangeLo; - } - targetByte = lastSuccessfulSeekOffset + (ma_uint64)(((ma_int64)((pcmFrameIndex-pcmRangeLo) * pFlac->channels * pFlac->bitsPerSample)/8.0f) * approxCompressionRatio); - if (targetByte > byteRangeHi) { - targetByte = byteRangeHi; - } - if (closestSeekOffsetBeforeTargetPCMFrame < lastSuccessfulSeekOffset) { - closestSeekOffsetBeforeTargetPCMFrame = lastSuccessfulSeekOffset; - } - } - } - } - } else { - break; - } - } - ma_dr_flac__seek_to_first_frame(pFlac); - return MA_FALSE; -} -static ma_bool32 ma_dr_flac__seek_to_pcm_frame__binary_search(ma_dr_flac* pFlac, ma_uint64 pcmFrameIndex) -{ - ma_uint64 byteRangeLo; - ma_uint64 byteRangeHi; - ma_uint32 seekForwardThreshold = (pFlac->maxBlockSizeInPCMFrames != 0) ? pFlac->maxBlockSizeInPCMFrames*2 : 4096; - if (ma_dr_flac__seek_to_first_frame(pFlac) == MA_FALSE) { - return MA_FALSE; - } - if (pcmFrameIndex < seekForwardThreshold) { - return ma_dr_flac__seek_forward_by_pcm_frames(pFlac, pcmFrameIndex) == pcmFrameIndex; - } - byteRangeLo = pFlac->firstFLACFramePosInBytes; - byteRangeHi = pFlac->firstFLACFramePosInBytes + (ma_uint64)((ma_int64)(pFlac->totalPCMFrameCount * pFlac->channels * pFlac->bitsPerSample)/8.0f); - return ma_dr_flac__seek_to_pcm_frame__binary_search_internal(pFlac, pcmFrameIndex, byteRangeLo, byteRangeHi); -} -#endif -static ma_bool32 ma_dr_flac__seek_to_pcm_frame__seek_table(ma_dr_flac* pFlac, ma_uint64 pcmFrameIndex) -{ - ma_uint32 iClosestSeekpoint = 0; - ma_bool32 isMidFrame = MA_FALSE; - ma_uint64 runningPCMFrameCount; - ma_uint32 iSeekpoint; - MA_DR_FLAC_ASSERT(pFlac != NULL); - if (pFlac->pSeekpoints == NULL || pFlac->seekpointCount == 0) { - return MA_FALSE; - } - if (pFlac->pSeekpoints[0].firstPCMFrame > pcmFrameIndex) { - return MA_FALSE; - } - for (iSeekpoint = 0; iSeekpoint < pFlac->seekpointCount; ++iSeekpoint) { - if (pFlac->pSeekpoints[iSeekpoint].firstPCMFrame >= pcmFrameIndex) { - break; - } - iClosestSeekpoint = iSeekpoint; - } - if (pFlac->pSeekpoints[iClosestSeekpoint].pcmFrameCount == 0 || pFlac->pSeekpoints[iClosestSeekpoint].pcmFrameCount > pFlac->maxBlockSizeInPCMFrames) { - return MA_FALSE; - } - if (pFlac->pSeekpoints[iClosestSeekpoint].firstPCMFrame > pFlac->totalPCMFrameCount && pFlac->totalPCMFrameCount > 0) { - return MA_FALSE; - } -#if !defined(MA_DR_FLAC_NO_CRC) - if (pFlac->totalPCMFrameCount > 0) { - ma_uint64 byteRangeLo; - ma_uint64 byteRangeHi; - byteRangeHi = pFlac->firstFLACFramePosInBytes + (ma_uint64)((ma_int64)(pFlac->totalPCMFrameCount * pFlac->channels * pFlac->bitsPerSample)/8.0f); - byteRangeLo = pFlac->firstFLACFramePosInBytes + pFlac->pSeekpoints[iClosestSeekpoint].flacFrameOffset; - if (iClosestSeekpoint < pFlac->seekpointCount-1) { - ma_uint32 iNextSeekpoint = iClosestSeekpoint + 1; - if (pFlac->pSeekpoints[iClosestSeekpoint].flacFrameOffset >= pFlac->pSeekpoints[iNextSeekpoint].flacFrameOffset || pFlac->pSeekpoints[iNextSeekpoint].pcmFrameCount == 0) { - return MA_FALSE; - } - if (pFlac->pSeekpoints[iNextSeekpoint].firstPCMFrame != (((ma_uint64)0xFFFFFFFF << 32) | 0xFFFFFFFF)) { - byteRangeHi = pFlac->firstFLACFramePosInBytes + pFlac->pSeekpoints[iNextSeekpoint].flacFrameOffset - 1; - } - } - if (ma_dr_flac__seek_to_byte(&pFlac->bs, pFlac->firstFLACFramePosInBytes + pFlac->pSeekpoints[iClosestSeekpoint].flacFrameOffset)) { - if (ma_dr_flac__read_next_flac_frame_header(&pFlac->bs, pFlac->bitsPerSample, &pFlac->currentFLACFrame.header)) { - ma_dr_flac__get_pcm_frame_range_of_current_flac_frame(pFlac, &pFlac->currentPCMFrame, NULL); - if (ma_dr_flac__seek_to_pcm_frame__binary_search_internal(pFlac, pcmFrameIndex, byteRangeLo, byteRangeHi)) { - return MA_TRUE; - } - } - } - } -#endif - if (pcmFrameIndex >= pFlac->currentPCMFrame && pFlac->pSeekpoints[iClosestSeekpoint].firstPCMFrame <= pFlac->currentPCMFrame) { - runningPCMFrameCount = pFlac->currentPCMFrame; - if (pFlac->currentPCMFrame == 0 && pFlac->currentFLACFrame.pcmFramesRemaining == 0) { - if (!ma_dr_flac__read_next_flac_frame_header(&pFlac->bs, pFlac->bitsPerSample, &pFlac->currentFLACFrame.header)) { - return MA_FALSE; - } - } else { - isMidFrame = MA_TRUE; - } - } else { - runningPCMFrameCount = pFlac->pSeekpoints[iClosestSeekpoint].firstPCMFrame; - if (!ma_dr_flac__seek_to_byte(&pFlac->bs, pFlac->firstFLACFramePosInBytes + pFlac->pSeekpoints[iClosestSeekpoint].flacFrameOffset)) { - return MA_FALSE; - } - if (!ma_dr_flac__read_next_flac_frame_header(&pFlac->bs, pFlac->bitsPerSample, &pFlac->currentFLACFrame.header)) { - return MA_FALSE; - } - } - for (;;) { - ma_uint64 pcmFrameCountInThisFLACFrame; - ma_uint64 firstPCMFrameInFLACFrame = 0; - ma_uint64 lastPCMFrameInFLACFrame = 0; - ma_dr_flac__get_pcm_frame_range_of_current_flac_frame(pFlac, &firstPCMFrameInFLACFrame, &lastPCMFrameInFLACFrame); - pcmFrameCountInThisFLACFrame = (lastPCMFrameInFLACFrame - firstPCMFrameInFLACFrame) + 1; - if (pcmFrameIndex < (runningPCMFrameCount + pcmFrameCountInThisFLACFrame)) { - ma_uint64 pcmFramesToDecode = pcmFrameIndex - runningPCMFrameCount; - if (!isMidFrame) { - ma_result result = ma_dr_flac__decode_flac_frame(pFlac); - if (result == MA_SUCCESS) { - return ma_dr_flac__seek_forward_by_pcm_frames(pFlac, pcmFramesToDecode) == pcmFramesToDecode; - } else { - if (result == MA_CRC_MISMATCH) { - goto next_iteration; - } else { - return MA_FALSE; - } - } - } else { - return ma_dr_flac__seek_forward_by_pcm_frames(pFlac, pcmFramesToDecode) == pcmFramesToDecode; - } - } else { - if (!isMidFrame) { - ma_result result = ma_dr_flac__seek_to_next_flac_frame(pFlac); - if (result == MA_SUCCESS) { - runningPCMFrameCount += pcmFrameCountInThisFLACFrame; - } else { - if (result == MA_CRC_MISMATCH) { - goto next_iteration; - } else { - return MA_FALSE; - } - } - } else { - runningPCMFrameCount += pFlac->currentFLACFrame.pcmFramesRemaining; - pFlac->currentFLACFrame.pcmFramesRemaining = 0; - isMidFrame = MA_FALSE; - } - if (pcmFrameIndex == pFlac->totalPCMFrameCount && runningPCMFrameCount == pFlac->totalPCMFrameCount) { - return MA_TRUE; - } - } - next_iteration: - if (!ma_dr_flac__read_next_flac_frame_header(&pFlac->bs, pFlac->bitsPerSample, &pFlac->currentFLACFrame.header)) { - return MA_FALSE; - } - } -} -#ifndef MA_DR_FLAC_NO_OGG -typedef struct -{ - ma_uint8 capturePattern[4]; - ma_uint8 structureVersion; - ma_uint8 headerType; - ma_uint64 granulePosition; - ma_uint32 serialNumber; - ma_uint32 sequenceNumber; - ma_uint32 checksum; - ma_uint8 segmentCount; - ma_uint8 segmentTable[255]; -} ma_dr_flac_ogg_page_header; -#endif -typedef struct -{ - ma_dr_flac_read_proc onRead; - ma_dr_flac_seek_proc onSeek; - ma_dr_flac_meta_proc onMeta; - ma_dr_flac_container container; - void* pUserData; - void* pUserDataMD; - ma_uint32 sampleRate; - ma_uint8 channels; - ma_uint8 bitsPerSample; - ma_uint64 totalPCMFrameCount; - ma_uint16 maxBlockSizeInPCMFrames; - ma_uint64 runningFilePos; - ma_bool32 hasStreamInfoBlock; - ma_bool32 hasMetadataBlocks; - ma_dr_flac_bs bs; - ma_dr_flac_frame_header firstFrameHeader; -#ifndef MA_DR_FLAC_NO_OGG - ma_uint32 oggSerial; - ma_uint64 oggFirstBytePos; - ma_dr_flac_ogg_page_header oggBosHeader; -#endif -} ma_dr_flac_init_info; -static MA_INLINE void ma_dr_flac__decode_block_header(ma_uint32 blockHeader, ma_uint8* isLastBlock, ma_uint8* blockType, ma_uint32* blockSize) -{ - blockHeader = ma_dr_flac__be2host_32(blockHeader); - *isLastBlock = (ma_uint8)((blockHeader & 0x80000000UL) >> 31); - *blockType = (ma_uint8)((blockHeader & 0x7F000000UL) >> 24); - *blockSize = (blockHeader & 0x00FFFFFFUL); -} -static MA_INLINE ma_bool32 ma_dr_flac__read_and_decode_block_header(ma_dr_flac_read_proc onRead, void* pUserData, ma_uint8* isLastBlock, ma_uint8* blockType, ma_uint32* blockSize) -{ - ma_uint32 blockHeader; - *blockSize = 0; - if (onRead(pUserData, &blockHeader, 4) != 4) { - return MA_FALSE; - } - ma_dr_flac__decode_block_header(blockHeader, isLastBlock, blockType, blockSize); - return MA_TRUE; -} -static ma_bool32 ma_dr_flac__read_streaminfo(ma_dr_flac_read_proc onRead, void* pUserData, ma_dr_flac_streaminfo* pStreamInfo) -{ - ma_uint32 blockSizes; - ma_uint64 frameSizes = 0; - ma_uint64 importantProps; - ma_uint8 md5[16]; - if (onRead(pUserData, &blockSizes, 4) != 4) { - return MA_FALSE; - } - if (onRead(pUserData, &frameSizes, 6) != 6) { - return MA_FALSE; - } - if (onRead(pUserData, &importantProps, 8) != 8) { - return MA_FALSE; - } - if (onRead(pUserData, md5, sizeof(md5)) != sizeof(md5)) { - return MA_FALSE; - } - blockSizes = ma_dr_flac__be2host_32(blockSizes); - frameSizes = ma_dr_flac__be2host_64(frameSizes); - importantProps = ma_dr_flac__be2host_64(importantProps); - pStreamInfo->minBlockSizeInPCMFrames = (ma_uint16)((blockSizes & 0xFFFF0000) >> 16); - pStreamInfo->maxBlockSizeInPCMFrames = (ma_uint16) (blockSizes & 0x0000FFFF); - pStreamInfo->minFrameSizeInPCMFrames = (ma_uint32)((frameSizes & (((ma_uint64)0x00FFFFFF << 16) << 24)) >> 40); - pStreamInfo->maxFrameSizeInPCMFrames = (ma_uint32)((frameSizes & (((ma_uint64)0x00FFFFFF << 16) << 0)) >> 16); - pStreamInfo->sampleRate = (ma_uint32)((importantProps & (((ma_uint64)0x000FFFFF << 16) << 28)) >> 44); - pStreamInfo->channels = (ma_uint8 )((importantProps & (((ma_uint64)0x0000000E << 16) << 24)) >> 41) + 1; - pStreamInfo->bitsPerSample = (ma_uint8 )((importantProps & (((ma_uint64)0x0000001F << 16) << 20)) >> 36) + 1; - pStreamInfo->totalPCMFrameCount = ((importantProps & ((((ma_uint64)0x0000000F << 16) << 16) | 0xFFFFFFFF))); - MA_DR_FLAC_COPY_MEMORY(pStreamInfo->md5, md5, sizeof(md5)); - return MA_TRUE; -} -static void* ma_dr_flac__malloc_default(size_t sz, void* pUserData) -{ - (void)pUserData; - return MA_DR_FLAC_MALLOC(sz); -} -static void* ma_dr_flac__realloc_default(void* p, size_t sz, void* pUserData) -{ - (void)pUserData; - return MA_DR_FLAC_REALLOC(p, sz); -} -static void ma_dr_flac__free_default(void* p, void* pUserData) -{ - (void)pUserData; - MA_DR_FLAC_FREE(p); -} -static void* ma_dr_flac__malloc_from_callbacks(size_t sz, const ma_allocation_callbacks* pAllocationCallbacks) -{ - if (pAllocationCallbacks == NULL) { - return NULL; - } - if (pAllocationCallbacks->onMalloc != NULL) { - return pAllocationCallbacks->onMalloc(sz, pAllocationCallbacks->pUserData); - } - if (pAllocationCallbacks->onRealloc != NULL) { - return pAllocationCallbacks->onRealloc(NULL, sz, pAllocationCallbacks->pUserData); - } - return NULL; -} -static void* ma_dr_flac__realloc_from_callbacks(void* p, size_t szNew, size_t szOld, const ma_allocation_callbacks* pAllocationCallbacks) -{ - if (pAllocationCallbacks == NULL) { - return NULL; - } - if (pAllocationCallbacks->onRealloc != NULL) { - return pAllocationCallbacks->onRealloc(p, szNew, pAllocationCallbacks->pUserData); - } - if (pAllocationCallbacks->onMalloc != NULL && pAllocationCallbacks->onFree != NULL) { - void* p2; - p2 = pAllocationCallbacks->onMalloc(szNew, pAllocationCallbacks->pUserData); - if (p2 == NULL) { - return NULL; - } - if (p != NULL) { - MA_DR_FLAC_COPY_MEMORY(p2, p, szOld); - pAllocationCallbacks->onFree(p, pAllocationCallbacks->pUserData); - } - return p2; - } - return NULL; -} -static void ma_dr_flac__free_from_callbacks(void* p, const ma_allocation_callbacks* pAllocationCallbacks) -{ - if (p == NULL || pAllocationCallbacks == NULL) { - return; - } - if (pAllocationCallbacks->onFree != NULL) { - pAllocationCallbacks->onFree(p, pAllocationCallbacks->pUserData); - } -} -static ma_bool32 ma_dr_flac__read_and_decode_metadata(ma_dr_flac_read_proc onRead, ma_dr_flac_seek_proc onSeek, ma_dr_flac_meta_proc onMeta, void* pUserData, void* pUserDataMD, ma_uint64* pFirstFramePos, ma_uint64* pSeektablePos, ma_uint32* pSeekpointCount, ma_allocation_callbacks* pAllocationCallbacks) -{ - ma_uint64 runningFilePos = 42; - ma_uint64 seektablePos = 0; - ma_uint32 seektableSize = 0; - for (;;) { - ma_dr_flac_metadata metadata; - ma_uint8 isLastBlock = 0; - ma_uint8 blockType = 0; - ma_uint32 blockSize; - if (ma_dr_flac__read_and_decode_block_header(onRead, pUserData, &isLastBlock, &blockType, &blockSize) == MA_FALSE) { - return MA_FALSE; - } - runningFilePos += 4; - metadata.type = blockType; - metadata.pRawData = NULL; - metadata.rawDataSize = 0; - switch (blockType) - { - case MA_DR_FLAC_METADATA_BLOCK_TYPE_APPLICATION: - { - if (blockSize < 4) { - return MA_FALSE; - } - if (onMeta) { - void* pRawData = ma_dr_flac__malloc_from_callbacks(blockSize, pAllocationCallbacks); - if (pRawData == NULL) { - return MA_FALSE; - } - if (onRead(pUserData, pRawData, blockSize) != blockSize) { - ma_dr_flac__free_from_callbacks(pRawData, pAllocationCallbacks); - return MA_FALSE; - } - metadata.pRawData = pRawData; - metadata.rawDataSize = blockSize; - metadata.data.application.id = ma_dr_flac__be2host_32(*(ma_uint32*)pRawData); - metadata.data.application.pData = (const void*)((ma_uint8*)pRawData + sizeof(ma_uint32)); - metadata.data.application.dataSize = blockSize - sizeof(ma_uint32); - onMeta(pUserDataMD, &metadata); - ma_dr_flac__free_from_callbacks(pRawData, pAllocationCallbacks); - } - } break; - case MA_DR_FLAC_METADATA_BLOCK_TYPE_SEEKTABLE: - { - seektablePos = runningFilePos; - seektableSize = blockSize; - if (onMeta) { - ma_uint32 seekpointCount; - ma_uint32 iSeekpoint; - void* pRawData; - seekpointCount = blockSize/MA_DR_FLAC_SEEKPOINT_SIZE_IN_BYTES; - pRawData = ma_dr_flac__malloc_from_callbacks(seekpointCount * sizeof(ma_dr_flac_seekpoint), pAllocationCallbacks); - if (pRawData == NULL) { - return MA_FALSE; - } - for (iSeekpoint = 0; iSeekpoint < seekpointCount; ++iSeekpoint) { - ma_dr_flac_seekpoint* pSeekpoint = (ma_dr_flac_seekpoint*)pRawData + iSeekpoint; - if (onRead(pUserData, pSeekpoint, MA_DR_FLAC_SEEKPOINT_SIZE_IN_BYTES) != MA_DR_FLAC_SEEKPOINT_SIZE_IN_BYTES) { - ma_dr_flac__free_from_callbacks(pRawData, pAllocationCallbacks); - return MA_FALSE; - } - pSeekpoint->firstPCMFrame = ma_dr_flac__be2host_64(pSeekpoint->firstPCMFrame); - pSeekpoint->flacFrameOffset = ma_dr_flac__be2host_64(pSeekpoint->flacFrameOffset); - pSeekpoint->pcmFrameCount = ma_dr_flac__be2host_16(pSeekpoint->pcmFrameCount); - } - metadata.pRawData = pRawData; - metadata.rawDataSize = blockSize; - metadata.data.seektable.seekpointCount = seekpointCount; - metadata.data.seektable.pSeekpoints = (const ma_dr_flac_seekpoint*)pRawData; - onMeta(pUserDataMD, &metadata); - ma_dr_flac__free_from_callbacks(pRawData, pAllocationCallbacks); - } - } break; - case MA_DR_FLAC_METADATA_BLOCK_TYPE_VORBIS_COMMENT: - { - if (blockSize < 8) { - return MA_FALSE; - } - if (onMeta) { - void* pRawData; - const char* pRunningData; - const char* pRunningDataEnd; - ma_uint32 i; - pRawData = ma_dr_flac__malloc_from_callbacks(blockSize, pAllocationCallbacks); - if (pRawData == NULL) { - return MA_FALSE; - } - if (onRead(pUserData, pRawData, blockSize) != blockSize) { - ma_dr_flac__free_from_callbacks(pRawData, pAllocationCallbacks); - return MA_FALSE; - } - metadata.pRawData = pRawData; - metadata.rawDataSize = blockSize; - pRunningData = (const char*)pRawData; - pRunningDataEnd = (const char*)pRawData + blockSize; - metadata.data.vorbis_comment.vendorLength = ma_dr_flac__le2host_32_ptr_unaligned(pRunningData); pRunningData += 4; - if ((pRunningDataEnd - pRunningData) - 4 < (ma_int64)metadata.data.vorbis_comment.vendorLength) { - ma_dr_flac__free_from_callbacks(pRawData, pAllocationCallbacks); - return MA_FALSE; - } - metadata.data.vorbis_comment.vendor = pRunningData; pRunningData += metadata.data.vorbis_comment.vendorLength; - metadata.data.vorbis_comment.commentCount = ma_dr_flac__le2host_32_ptr_unaligned(pRunningData); pRunningData += 4; - if ((pRunningDataEnd - pRunningData) / sizeof(ma_uint32) < metadata.data.vorbis_comment.commentCount) { - ma_dr_flac__free_from_callbacks(pRawData, pAllocationCallbacks); - return MA_FALSE; - } - metadata.data.vorbis_comment.pComments = pRunningData; - for (i = 0; i < metadata.data.vorbis_comment.commentCount; ++i) { - ma_uint32 commentLength; - if (pRunningDataEnd - pRunningData < 4) { - ma_dr_flac__free_from_callbacks(pRawData, pAllocationCallbacks); - return MA_FALSE; - } - commentLength = ma_dr_flac__le2host_32_ptr_unaligned(pRunningData); pRunningData += 4; - if (pRunningDataEnd - pRunningData < (ma_int64)commentLength) { - ma_dr_flac__free_from_callbacks(pRawData, pAllocationCallbacks); - return MA_FALSE; - } - pRunningData += commentLength; - } - onMeta(pUserDataMD, &metadata); - ma_dr_flac__free_from_callbacks(pRawData, pAllocationCallbacks); - } - } break; - case MA_DR_FLAC_METADATA_BLOCK_TYPE_CUESHEET: - { - if (blockSize < 396) { - return MA_FALSE; - } - if (onMeta) { - void* pRawData; - const char* pRunningData; - const char* pRunningDataEnd; - size_t bufferSize; - ma_uint8 iTrack; - ma_uint8 iIndex; - void* pTrackData; - pRawData = ma_dr_flac__malloc_from_callbacks(blockSize, pAllocationCallbacks); - if (pRawData == NULL) { - return MA_FALSE; - } - if (onRead(pUserData, pRawData, blockSize) != blockSize) { - ma_dr_flac__free_from_callbacks(pRawData, pAllocationCallbacks); - return MA_FALSE; - } - metadata.pRawData = pRawData; - metadata.rawDataSize = blockSize; - pRunningData = (const char*)pRawData; - pRunningDataEnd = (const char*)pRawData + blockSize; - MA_DR_FLAC_COPY_MEMORY(metadata.data.cuesheet.catalog, pRunningData, 128); pRunningData += 128; - metadata.data.cuesheet.leadInSampleCount = ma_dr_flac__be2host_64(*(const ma_uint64*)pRunningData); pRunningData += 8; - metadata.data.cuesheet.isCD = (pRunningData[0] & 0x80) != 0; pRunningData += 259; - metadata.data.cuesheet.trackCount = pRunningData[0]; pRunningData += 1; - metadata.data.cuesheet.pTrackData = NULL; - { - const char* pRunningDataSaved = pRunningData; - bufferSize = metadata.data.cuesheet.trackCount * MA_DR_FLAC_CUESHEET_TRACK_SIZE_IN_BYTES; - for (iTrack = 0; iTrack < metadata.data.cuesheet.trackCount; ++iTrack) { - ma_uint8 indexCount; - ma_uint32 indexPointSize; - if (pRunningDataEnd - pRunningData < MA_DR_FLAC_CUESHEET_TRACK_SIZE_IN_BYTES) { - ma_dr_flac__free_from_callbacks(pRawData, pAllocationCallbacks); - return MA_FALSE; - } - pRunningData += 35; - indexCount = pRunningData[0]; - pRunningData += 1; - bufferSize += indexCount * sizeof(ma_dr_flac_cuesheet_track_index); - indexPointSize = indexCount * MA_DR_FLAC_CUESHEET_TRACK_INDEX_SIZE_IN_BYTES; - if (pRunningDataEnd - pRunningData < (ma_int64)indexPointSize) { - ma_dr_flac__free_from_callbacks(pRawData, pAllocationCallbacks); - return MA_FALSE; - } - pRunningData += indexPointSize; - } - pRunningData = pRunningDataSaved; - } - { - char* pRunningTrackData; - pTrackData = ma_dr_flac__malloc_from_callbacks(bufferSize, pAllocationCallbacks); - if (pTrackData == NULL) { - ma_dr_flac__free_from_callbacks(pRawData, pAllocationCallbacks); - return MA_FALSE; - } - pRunningTrackData = (char*)pTrackData; - for (iTrack = 0; iTrack < metadata.data.cuesheet.trackCount; ++iTrack) { - ma_uint8 indexCount; - MA_DR_FLAC_COPY_MEMORY(pRunningTrackData, pRunningData, MA_DR_FLAC_CUESHEET_TRACK_SIZE_IN_BYTES); - pRunningData += MA_DR_FLAC_CUESHEET_TRACK_SIZE_IN_BYTES-1; - pRunningTrackData += MA_DR_FLAC_CUESHEET_TRACK_SIZE_IN_BYTES-1; - indexCount = pRunningData[0]; - pRunningData += 1; - pRunningTrackData += 1; - for (iIndex = 0; iIndex < indexCount; ++iIndex) { - ma_dr_flac_cuesheet_track_index* pTrackIndex = (ma_dr_flac_cuesheet_track_index*)pRunningTrackData; - MA_DR_FLAC_COPY_MEMORY(pRunningTrackData, pRunningData, MA_DR_FLAC_CUESHEET_TRACK_INDEX_SIZE_IN_BYTES); - pRunningData += MA_DR_FLAC_CUESHEET_TRACK_INDEX_SIZE_IN_BYTES; - pRunningTrackData += sizeof(ma_dr_flac_cuesheet_track_index); - pTrackIndex->offset = ma_dr_flac__be2host_64(pTrackIndex->offset); - } - } - metadata.data.cuesheet.pTrackData = pTrackData; - } - ma_dr_flac__free_from_callbacks(pRawData, pAllocationCallbacks); - pRawData = NULL; - onMeta(pUserDataMD, &metadata); - ma_dr_flac__free_from_callbacks(pTrackData, pAllocationCallbacks); - pTrackData = NULL; - } - } break; - case MA_DR_FLAC_METADATA_BLOCK_TYPE_PICTURE: - { - if (blockSize < 32) { - return MA_FALSE; - } - if (onMeta) { - void* pRawData; - const char* pRunningData; - const char* pRunningDataEnd; - pRawData = ma_dr_flac__malloc_from_callbacks(blockSize, pAllocationCallbacks); - if (pRawData == NULL) { - return MA_FALSE; - } - if (onRead(pUserData, pRawData, blockSize) != blockSize) { - ma_dr_flac__free_from_callbacks(pRawData, pAllocationCallbacks); - return MA_FALSE; - } - metadata.pRawData = pRawData; - metadata.rawDataSize = blockSize; - pRunningData = (const char*)pRawData; - pRunningDataEnd = (const char*)pRawData + blockSize; - metadata.data.picture.type = ma_dr_flac__be2host_32_ptr_unaligned(pRunningData); pRunningData += 4; - metadata.data.picture.mimeLength = ma_dr_flac__be2host_32_ptr_unaligned(pRunningData); pRunningData += 4; - if ((pRunningDataEnd - pRunningData) - 24 < (ma_int64)metadata.data.picture.mimeLength) { - ma_dr_flac__free_from_callbacks(pRawData, pAllocationCallbacks); - return MA_FALSE; - } - metadata.data.picture.mime = pRunningData; pRunningData += metadata.data.picture.mimeLength; - metadata.data.picture.descriptionLength = ma_dr_flac__be2host_32_ptr_unaligned(pRunningData); pRunningData += 4; - if ((pRunningDataEnd - pRunningData) - 20 < (ma_int64)metadata.data.picture.descriptionLength) { - ma_dr_flac__free_from_callbacks(pRawData, pAllocationCallbacks); - return MA_FALSE; - } - metadata.data.picture.description = pRunningData; pRunningData += metadata.data.picture.descriptionLength; - metadata.data.picture.width = ma_dr_flac__be2host_32_ptr_unaligned(pRunningData); pRunningData += 4; - metadata.data.picture.height = ma_dr_flac__be2host_32_ptr_unaligned(pRunningData); pRunningData += 4; - metadata.data.picture.colorDepth = ma_dr_flac__be2host_32_ptr_unaligned(pRunningData); pRunningData += 4; - metadata.data.picture.indexColorCount = ma_dr_flac__be2host_32_ptr_unaligned(pRunningData); pRunningData += 4; - metadata.data.picture.pictureDataSize = ma_dr_flac__be2host_32_ptr_unaligned(pRunningData); pRunningData += 4; - metadata.data.picture.pPictureData = (const ma_uint8*)pRunningData; - if (pRunningDataEnd - pRunningData < (ma_int64)metadata.data.picture.pictureDataSize) { - ma_dr_flac__free_from_callbacks(pRawData, pAllocationCallbacks); - return MA_FALSE; - } - onMeta(pUserDataMD, &metadata); - ma_dr_flac__free_from_callbacks(pRawData, pAllocationCallbacks); - } - } break; - case MA_DR_FLAC_METADATA_BLOCK_TYPE_PADDING: - { - if (onMeta) { - metadata.data.padding.unused = 0; - if (!onSeek(pUserData, blockSize, ma_dr_flac_seek_origin_current)) { - isLastBlock = MA_TRUE; - } else { - onMeta(pUserDataMD, &metadata); - } - } - } break; - case MA_DR_FLAC_METADATA_BLOCK_TYPE_INVALID: - { - if (onMeta) { - if (!onSeek(pUserData, blockSize, ma_dr_flac_seek_origin_current)) { - isLastBlock = MA_TRUE; - } - } - } break; - default: - { - if (onMeta) { - void* pRawData = ma_dr_flac__malloc_from_callbacks(blockSize, pAllocationCallbacks); - if (pRawData == NULL) { - return MA_FALSE; - } - if (onRead(pUserData, pRawData, blockSize) != blockSize) { - ma_dr_flac__free_from_callbacks(pRawData, pAllocationCallbacks); - return MA_FALSE; - } - metadata.pRawData = pRawData; - metadata.rawDataSize = blockSize; - onMeta(pUserDataMD, &metadata); - ma_dr_flac__free_from_callbacks(pRawData, pAllocationCallbacks); - } - } break; - } - if (onMeta == NULL && blockSize > 0) { - if (!onSeek(pUserData, blockSize, ma_dr_flac_seek_origin_current)) { - isLastBlock = MA_TRUE; - } - } - runningFilePos += blockSize; - if (isLastBlock) { - break; - } - } - *pSeektablePos = seektablePos; - *pSeekpointCount = seektableSize / MA_DR_FLAC_SEEKPOINT_SIZE_IN_BYTES; - *pFirstFramePos = runningFilePos; - return MA_TRUE; -} -static ma_bool32 ma_dr_flac__init_private__native(ma_dr_flac_init_info* pInit, ma_dr_flac_read_proc onRead, ma_dr_flac_seek_proc onSeek, ma_dr_flac_meta_proc onMeta, void* pUserData, void* pUserDataMD, ma_bool32 relaxed) -{ - ma_uint8 isLastBlock; - ma_uint8 blockType; - ma_uint32 blockSize; - (void)onSeek; - pInit->container = ma_dr_flac_container_native; - if (!ma_dr_flac__read_and_decode_block_header(onRead, pUserData, &isLastBlock, &blockType, &blockSize)) { - return MA_FALSE; - } - if (blockType != MA_DR_FLAC_METADATA_BLOCK_TYPE_STREAMINFO || blockSize != 34) { - if (!relaxed) { - return MA_FALSE; - } else { - pInit->hasStreamInfoBlock = MA_FALSE; - pInit->hasMetadataBlocks = MA_FALSE; - if (!ma_dr_flac__read_next_flac_frame_header(&pInit->bs, 0, &pInit->firstFrameHeader)) { - return MA_FALSE; - } - if (pInit->firstFrameHeader.bitsPerSample == 0) { - return MA_FALSE; - } - pInit->sampleRate = pInit->firstFrameHeader.sampleRate; - pInit->channels = ma_dr_flac__get_channel_count_from_channel_assignment(pInit->firstFrameHeader.channelAssignment); - pInit->bitsPerSample = pInit->firstFrameHeader.bitsPerSample; - pInit->maxBlockSizeInPCMFrames = 65535; - return MA_TRUE; - } - } else { - ma_dr_flac_streaminfo streaminfo; - if (!ma_dr_flac__read_streaminfo(onRead, pUserData, &streaminfo)) { - return MA_FALSE; - } - pInit->hasStreamInfoBlock = MA_TRUE; - pInit->sampleRate = streaminfo.sampleRate; - pInit->channels = streaminfo.channels; - pInit->bitsPerSample = streaminfo.bitsPerSample; - pInit->totalPCMFrameCount = streaminfo.totalPCMFrameCount; - pInit->maxBlockSizeInPCMFrames = streaminfo.maxBlockSizeInPCMFrames; - pInit->hasMetadataBlocks = !isLastBlock; - if (onMeta) { - ma_dr_flac_metadata metadata; - metadata.type = MA_DR_FLAC_METADATA_BLOCK_TYPE_STREAMINFO; - metadata.pRawData = NULL; - metadata.rawDataSize = 0; - metadata.data.streaminfo = streaminfo; - onMeta(pUserDataMD, &metadata); - } - return MA_TRUE; - } -} -#ifndef MA_DR_FLAC_NO_OGG -#define MA_DR_FLAC_OGG_MAX_PAGE_SIZE 65307 -#define MA_DR_FLAC_OGG_CAPTURE_PATTERN_CRC32 1605413199 -typedef enum -{ - ma_dr_flac_ogg_recover_on_crc_mismatch, - ma_dr_flac_ogg_fail_on_crc_mismatch -} ma_dr_flac_ogg_crc_mismatch_recovery; -#ifndef MA_DR_FLAC_NO_CRC -static ma_uint32 ma_dr_flac__crc32_table[] = { - 0x00000000L, 0x04C11DB7L, 0x09823B6EL, 0x0D4326D9L, - 0x130476DCL, 0x17C56B6BL, 0x1A864DB2L, 0x1E475005L, - 0x2608EDB8L, 0x22C9F00FL, 0x2F8AD6D6L, 0x2B4BCB61L, - 0x350C9B64L, 0x31CD86D3L, 0x3C8EA00AL, 0x384FBDBDL, - 0x4C11DB70L, 0x48D0C6C7L, 0x4593E01EL, 0x4152FDA9L, - 0x5F15ADACL, 0x5BD4B01BL, 0x569796C2L, 0x52568B75L, - 0x6A1936C8L, 0x6ED82B7FL, 0x639B0DA6L, 0x675A1011L, - 0x791D4014L, 0x7DDC5DA3L, 0x709F7B7AL, 0x745E66CDL, - 0x9823B6E0L, 0x9CE2AB57L, 0x91A18D8EL, 0x95609039L, - 0x8B27C03CL, 0x8FE6DD8BL, 0x82A5FB52L, 0x8664E6E5L, - 0xBE2B5B58L, 0xBAEA46EFL, 0xB7A96036L, 0xB3687D81L, - 0xAD2F2D84L, 0xA9EE3033L, 0xA4AD16EAL, 0xA06C0B5DL, - 0xD4326D90L, 0xD0F37027L, 0xDDB056FEL, 0xD9714B49L, - 0xC7361B4CL, 0xC3F706FBL, 0xCEB42022L, 0xCA753D95L, - 0xF23A8028L, 0xF6FB9D9FL, 0xFBB8BB46L, 0xFF79A6F1L, - 0xE13EF6F4L, 0xE5FFEB43L, 0xE8BCCD9AL, 0xEC7DD02DL, - 0x34867077L, 0x30476DC0L, 0x3D044B19L, 0x39C556AEL, - 0x278206ABL, 0x23431B1CL, 0x2E003DC5L, 0x2AC12072L, - 0x128E9DCFL, 0x164F8078L, 0x1B0CA6A1L, 0x1FCDBB16L, - 0x018AEB13L, 0x054BF6A4L, 0x0808D07DL, 0x0CC9CDCAL, - 0x7897AB07L, 0x7C56B6B0L, 0x71159069L, 0x75D48DDEL, - 0x6B93DDDBL, 0x6F52C06CL, 0x6211E6B5L, 0x66D0FB02L, - 0x5E9F46BFL, 0x5A5E5B08L, 0x571D7DD1L, 0x53DC6066L, - 0x4D9B3063L, 0x495A2DD4L, 0x44190B0DL, 0x40D816BAL, - 0xACA5C697L, 0xA864DB20L, 0xA527FDF9L, 0xA1E6E04EL, - 0xBFA1B04BL, 0xBB60ADFCL, 0xB6238B25L, 0xB2E29692L, - 0x8AAD2B2FL, 0x8E6C3698L, 0x832F1041L, 0x87EE0DF6L, - 0x99A95DF3L, 0x9D684044L, 0x902B669DL, 0x94EA7B2AL, - 0xE0B41DE7L, 0xE4750050L, 0xE9362689L, 0xEDF73B3EL, - 0xF3B06B3BL, 0xF771768CL, 0xFA325055L, 0xFEF34DE2L, - 0xC6BCF05FL, 0xC27DEDE8L, 0xCF3ECB31L, 0xCBFFD686L, - 0xD5B88683L, 0xD1799B34L, 0xDC3ABDEDL, 0xD8FBA05AL, - 0x690CE0EEL, 0x6DCDFD59L, 0x608EDB80L, 0x644FC637L, - 0x7A089632L, 0x7EC98B85L, 0x738AAD5CL, 0x774BB0EBL, - 0x4F040D56L, 0x4BC510E1L, 0x46863638L, 0x42472B8FL, - 0x5C007B8AL, 0x58C1663DL, 0x558240E4L, 0x51435D53L, - 0x251D3B9EL, 0x21DC2629L, 0x2C9F00F0L, 0x285E1D47L, - 0x36194D42L, 0x32D850F5L, 0x3F9B762CL, 0x3B5A6B9BL, - 0x0315D626L, 0x07D4CB91L, 0x0A97ED48L, 0x0E56F0FFL, - 0x1011A0FAL, 0x14D0BD4DL, 0x19939B94L, 0x1D528623L, - 0xF12F560EL, 0xF5EE4BB9L, 0xF8AD6D60L, 0xFC6C70D7L, - 0xE22B20D2L, 0xE6EA3D65L, 0xEBA91BBCL, 0xEF68060BL, - 0xD727BBB6L, 0xD3E6A601L, 0xDEA580D8L, 0xDA649D6FL, - 0xC423CD6AL, 0xC0E2D0DDL, 0xCDA1F604L, 0xC960EBB3L, - 0xBD3E8D7EL, 0xB9FF90C9L, 0xB4BCB610L, 0xB07DABA7L, - 0xAE3AFBA2L, 0xAAFBE615L, 0xA7B8C0CCL, 0xA379DD7BL, - 0x9B3660C6L, 0x9FF77D71L, 0x92B45BA8L, 0x9675461FL, - 0x8832161AL, 0x8CF30BADL, 0x81B02D74L, 0x857130C3L, - 0x5D8A9099L, 0x594B8D2EL, 0x5408ABF7L, 0x50C9B640L, - 0x4E8EE645L, 0x4A4FFBF2L, 0x470CDD2BL, 0x43CDC09CL, - 0x7B827D21L, 0x7F436096L, 0x7200464FL, 0x76C15BF8L, - 0x68860BFDL, 0x6C47164AL, 0x61043093L, 0x65C52D24L, - 0x119B4BE9L, 0x155A565EL, 0x18197087L, 0x1CD86D30L, - 0x029F3D35L, 0x065E2082L, 0x0B1D065BL, 0x0FDC1BECL, - 0x3793A651L, 0x3352BBE6L, 0x3E119D3FL, 0x3AD08088L, - 0x2497D08DL, 0x2056CD3AL, 0x2D15EBE3L, 0x29D4F654L, - 0xC5A92679L, 0xC1683BCEL, 0xCC2B1D17L, 0xC8EA00A0L, - 0xD6AD50A5L, 0xD26C4D12L, 0xDF2F6BCBL, 0xDBEE767CL, - 0xE3A1CBC1L, 0xE760D676L, 0xEA23F0AFL, 0xEEE2ED18L, - 0xF0A5BD1DL, 0xF464A0AAL, 0xF9278673L, 0xFDE69BC4L, - 0x89B8FD09L, 0x8D79E0BEL, 0x803AC667L, 0x84FBDBD0L, - 0x9ABC8BD5L, 0x9E7D9662L, 0x933EB0BBL, 0x97FFAD0CL, - 0xAFB010B1L, 0xAB710D06L, 0xA6322BDFL, 0xA2F33668L, - 0xBCB4666DL, 0xB8757BDAL, 0xB5365D03L, 0xB1F740B4L -}; -#endif -static MA_INLINE ma_uint32 ma_dr_flac_crc32_byte(ma_uint32 crc32, ma_uint8 data) -{ -#ifndef MA_DR_FLAC_NO_CRC - return (crc32 << 8) ^ ma_dr_flac__crc32_table[(ma_uint8)((crc32 >> 24) & 0xFF) ^ data]; -#else - (void)data; - return crc32; -#endif -} -#if 0 -static MA_INLINE ma_uint32 ma_dr_flac_crc32_uint32(ma_uint32 crc32, ma_uint32 data) -{ - crc32 = ma_dr_flac_crc32_byte(crc32, (ma_uint8)((data >> 24) & 0xFF)); - crc32 = ma_dr_flac_crc32_byte(crc32, (ma_uint8)((data >> 16) & 0xFF)); - crc32 = ma_dr_flac_crc32_byte(crc32, (ma_uint8)((data >> 8) & 0xFF)); - crc32 = ma_dr_flac_crc32_byte(crc32, (ma_uint8)((data >> 0) & 0xFF)); - return crc32; -} -static MA_INLINE ma_uint32 ma_dr_flac_crc32_uint64(ma_uint32 crc32, ma_uint64 data) -{ - crc32 = ma_dr_flac_crc32_uint32(crc32, (ma_uint32)((data >> 32) & 0xFFFFFFFF)); - crc32 = ma_dr_flac_crc32_uint32(crc32, (ma_uint32)((data >> 0) & 0xFFFFFFFF)); - return crc32; -} -#endif -static MA_INLINE ma_uint32 ma_dr_flac_crc32_buffer(ma_uint32 crc32, ma_uint8* pData, ma_uint32 dataSize) -{ - ma_uint32 i; - for (i = 0; i < dataSize; ++i) { - crc32 = ma_dr_flac_crc32_byte(crc32, pData[i]); - } - return crc32; -} -static MA_INLINE ma_bool32 ma_dr_flac_ogg__is_capture_pattern(ma_uint8 pattern[4]) -{ - return pattern[0] == 'O' && pattern[1] == 'g' && pattern[2] == 'g' && pattern[3] == 'S'; -} -static MA_INLINE ma_uint32 ma_dr_flac_ogg__get_page_header_size(ma_dr_flac_ogg_page_header* pHeader) -{ - return 27 + pHeader->segmentCount; -} -static MA_INLINE ma_uint32 ma_dr_flac_ogg__get_page_body_size(ma_dr_flac_ogg_page_header* pHeader) -{ - ma_uint32 pageBodySize = 0; - int i; - for (i = 0; i < pHeader->segmentCount; ++i) { - pageBodySize += pHeader->segmentTable[i]; - } - return pageBodySize; -} -static ma_result ma_dr_flac_ogg__read_page_header_after_capture_pattern(ma_dr_flac_read_proc onRead, void* pUserData, ma_dr_flac_ogg_page_header* pHeader, ma_uint32* pBytesRead, ma_uint32* pCRC32) -{ - ma_uint8 data[23]; - ma_uint32 i; - MA_DR_FLAC_ASSERT(*pCRC32 == MA_DR_FLAC_OGG_CAPTURE_PATTERN_CRC32); - if (onRead(pUserData, data, 23) != 23) { - return MA_AT_END; - } - *pBytesRead += 23; - pHeader->capturePattern[0] = 'O'; - pHeader->capturePattern[1] = 'g'; - pHeader->capturePattern[2] = 'g'; - pHeader->capturePattern[3] = 'S'; - pHeader->structureVersion = data[0]; - pHeader->headerType = data[1]; - MA_DR_FLAC_COPY_MEMORY(&pHeader->granulePosition, &data[ 2], 8); - MA_DR_FLAC_COPY_MEMORY(&pHeader->serialNumber, &data[10], 4); - MA_DR_FLAC_COPY_MEMORY(&pHeader->sequenceNumber, &data[14], 4); - MA_DR_FLAC_COPY_MEMORY(&pHeader->checksum, &data[18], 4); - pHeader->segmentCount = data[22]; - data[18] = 0; - data[19] = 0; - data[20] = 0; - data[21] = 0; - for (i = 0; i < 23; ++i) { - *pCRC32 = ma_dr_flac_crc32_byte(*pCRC32, data[i]); - } - if (onRead(pUserData, pHeader->segmentTable, pHeader->segmentCount) != pHeader->segmentCount) { - return MA_AT_END; - } - *pBytesRead += pHeader->segmentCount; - for (i = 0; i < pHeader->segmentCount; ++i) { - *pCRC32 = ma_dr_flac_crc32_byte(*pCRC32, pHeader->segmentTable[i]); - } - return MA_SUCCESS; -} -static ma_result ma_dr_flac_ogg__read_page_header(ma_dr_flac_read_proc onRead, void* pUserData, ma_dr_flac_ogg_page_header* pHeader, ma_uint32* pBytesRead, ma_uint32* pCRC32) -{ - ma_uint8 id[4]; - *pBytesRead = 0; - if (onRead(pUserData, id, 4) != 4) { - return MA_AT_END; - } - *pBytesRead += 4; - for (;;) { - if (ma_dr_flac_ogg__is_capture_pattern(id)) { - ma_result result; - *pCRC32 = MA_DR_FLAC_OGG_CAPTURE_PATTERN_CRC32; - result = ma_dr_flac_ogg__read_page_header_after_capture_pattern(onRead, pUserData, pHeader, pBytesRead, pCRC32); - if (result == MA_SUCCESS) { - return MA_SUCCESS; - } else { - if (result == MA_CRC_MISMATCH) { - continue; - } else { - return result; - } - } - } else { - id[0] = id[1]; - id[1] = id[2]; - id[2] = id[3]; - if (onRead(pUserData, &id[3], 1) != 1) { - return MA_AT_END; - } - *pBytesRead += 1; - } - } -} -typedef struct -{ - ma_dr_flac_read_proc onRead; - ma_dr_flac_seek_proc onSeek; - void* pUserData; - ma_uint64 currentBytePos; - ma_uint64 firstBytePos; - ma_uint32 serialNumber; - ma_dr_flac_ogg_page_header bosPageHeader; - ma_dr_flac_ogg_page_header currentPageHeader; - ma_uint32 bytesRemainingInPage; - ma_uint32 pageDataSize; - ma_uint8 pageData[MA_DR_FLAC_OGG_MAX_PAGE_SIZE]; -} ma_dr_flac_oggbs; -static size_t ma_dr_flac_oggbs__read_physical(ma_dr_flac_oggbs* oggbs, void* bufferOut, size_t bytesToRead) -{ - size_t bytesActuallyRead = oggbs->onRead(oggbs->pUserData, bufferOut, bytesToRead); - oggbs->currentBytePos += bytesActuallyRead; - return bytesActuallyRead; -} -static ma_bool32 ma_dr_flac_oggbs__seek_physical(ma_dr_flac_oggbs* oggbs, ma_uint64 offset, ma_dr_flac_seek_origin origin) -{ - if (origin == ma_dr_flac_seek_origin_start) { - if (offset <= 0x7FFFFFFF) { - if (!oggbs->onSeek(oggbs->pUserData, (int)offset, ma_dr_flac_seek_origin_start)) { - return MA_FALSE; - } - oggbs->currentBytePos = offset; - return MA_TRUE; - } else { - if (!oggbs->onSeek(oggbs->pUserData, 0x7FFFFFFF, ma_dr_flac_seek_origin_start)) { - return MA_FALSE; - } - oggbs->currentBytePos = offset; - return ma_dr_flac_oggbs__seek_physical(oggbs, offset - 0x7FFFFFFF, ma_dr_flac_seek_origin_current); - } - } else { - while (offset > 0x7FFFFFFF) { - if (!oggbs->onSeek(oggbs->pUserData, 0x7FFFFFFF, ma_dr_flac_seek_origin_current)) { - return MA_FALSE; - } - oggbs->currentBytePos += 0x7FFFFFFF; - offset -= 0x7FFFFFFF; - } - if (!oggbs->onSeek(oggbs->pUserData, (int)offset, ma_dr_flac_seek_origin_current)) { - return MA_FALSE; - } - oggbs->currentBytePos += offset; - return MA_TRUE; - } -} -static ma_bool32 ma_dr_flac_oggbs__goto_next_page(ma_dr_flac_oggbs* oggbs, ma_dr_flac_ogg_crc_mismatch_recovery recoveryMethod) -{ - ma_dr_flac_ogg_page_header header; - for (;;) { - ma_uint32 crc32 = 0; - ma_uint32 bytesRead; - ma_uint32 pageBodySize; -#ifndef MA_DR_FLAC_NO_CRC - ma_uint32 actualCRC32; -#endif - if (ma_dr_flac_ogg__read_page_header(oggbs->onRead, oggbs->pUserData, &header, &bytesRead, &crc32) != MA_SUCCESS) { - return MA_FALSE; - } - oggbs->currentBytePos += bytesRead; - pageBodySize = ma_dr_flac_ogg__get_page_body_size(&header); - if (pageBodySize > MA_DR_FLAC_OGG_MAX_PAGE_SIZE) { - continue; - } - if (header.serialNumber != oggbs->serialNumber) { - if (pageBodySize > 0 && !ma_dr_flac_oggbs__seek_physical(oggbs, pageBodySize, ma_dr_flac_seek_origin_current)) { - return MA_FALSE; - } - continue; - } - if (ma_dr_flac_oggbs__read_physical(oggbs, oggbs->pageData, pageBodySize) != pageBodySize) { - return MA_FALSE; - } - oggbs->pageDataSize = pageBodySize; -#ifndef MA_DR_FLAC_NO_CRC - actualCRC32 = ma_dr_flac_crc32_buffer(crc32, oggbs->pageData, oggbs->pageDataSize); - if (actualCRC32 != header.checksum) { - if (recoveryMethod == ma_dr_flac_ogg_recover_on_crc_mismatch) { - continue; - } else { - ma_dr_flac_oggbs__goto_next_page(oggbs, ma_dr_flac_ogg_recover_on_crc_mismatch); - return MA_FALSE; - } - } -#else - (void)recoveryMethod; -#endif - oggbs->currentPageHeader = header; - oggbs->bytesRemainingInPage = pageBodySize; - return MA_TRUE; - } -} -#if 0 -static ma_uint8 ma_dr_flac_oggbs__get_current_segment_index(ma_dr_flac_oggbs* oggbs, ma_uint8* pBytesRemainingInSeg) -{ - ma_uint32 bytesConsumedInPage = ma_dr_flac_ogg__get_page_body_size(&oggbs->currentPageHeader) - oggbs->bytesRemainingInPage; - ma_uint8 iSeg = 0; - ma_uint32 iByte = 0; - while (iByte < bytesConsumedInPage) { - ma_uint8 segmentSize = oggbs->currentPageHeader.segmentTable[iSeg]; - if (iByte + segmentSize > bytesConsumedInPage) { - break; - } else { - iSeg += 1; - iByte += segmentSize; - } - } - *pBytesRemainingInSeg = oggbs->currentPageHeader.segmentTable[iSeg] - (ma_uint8)(bytesConsumedInPage - iByte); - return iSeg; -} -static ma_bool32 ma_dr_flac_oggbs__seek_to_next_packet(ma_dr_flac_oggbs* oggbs) -{ - for (;;) { - ma_bool32 atEndOfPage = MA_FALSE; - ma_uint8 bytesRemainingInSeg; - ma_uint8 iFirstSeg = ma_dr_flac_oggbs__get_current_segment_index(oggbs, &bytesRemainingInSeg); - ma_uint32 bytesToEndOfPacketOrPage = bytesRemainingInSeg; - for (ma_uint8 iSeg = iFirstSeg; iSeg < oggbs->currentPageHeader.segmentCount; ++iSeg) { - ma_uint8 segmentSize = oggbs->currentPageHeader.segmentTable[iSeg]; - if (segmentSize < 255) { - if (iSeg == oggbs->currentPageHeader.segmentCount-1) { - atEndOfPage = MA_TRUE; - } - break; - } - bytesToEndOfPacketOrPage += segmentSize; - } - ma_dr_flac_oggbs__seek_physical(oggbs, bytesToEndOfPacketOrPage, ma_dr_flac_seek_origin_current); - oggbs->bytesRemainingInPage -= bytesToEndOfPacketOrPage; - if (atEndOfPage) { - if (!ma_dr_flac_oggbs__goto_next_page(oggbs)) { - return MA_FALSE; - } - if ((oggbs->currentPageHeader.headerType & 0x01) == 0) { - return MA_TRUE; - } - } else { - return MA_TRUE; - } - } -} -static ma_bool32 ma_dr_flac_oggbs__seek_to_next_frame(ma_dr_flac_oggbs* oggbs) -{ - return ma_dr_flac_oggbs__seek_to_next_packet(oggbs); -} -#endif -static size_t ma_dr_flac__on_read_ogg(void* pUserData, void* bufferOut, size_t bytesToRead) -{ - ma_dr_flac_oggbs* oggbs = (ma_dr_flac_oggbs*)pUserData; - ma_uint8* pRunningBufferOut = (ma_uint8*)bufferOut; - size_t bytesRead = 0; - MA_DR_FLAC_ASSERT(oggbs != NULL); - MA_DR_FLAC_ASSERT(pRunningBufferOut != NULL); - while (bytesRead < bytesToRead) { - size_t bytesRemainingToRead = bytesToRead - bytesRead; - if (oggbs->bytesRemainingInPage >= bytesRemainingToRead) { - MA_DR_FLAC_COPY_MEMORY(pRunningBufferOut, oggbs->pageData + (oggbs->pageDataSize - oggbs->bytesRemainingInPage), bytesRemainingToRead); - bytesRead += bytesRemainingToRead; - oggbs->bytesRemainingInPage -= (ma_uint32)bytesRemainingToRead; - break; - } - if (oggbs->bytesRemainingInPage > 0) { - MA_DR_FLAC_COPY_MEMORY(pRunningBufferOut, oggbs->pageData + (oggbs->pageDataSize - oggbs->bytesRemainingInPage), oggbs->bytesRemainingInPage); - bytesRead += oggbs->bytesRemainingInPage; - pRunningBufferOut += oggbs->bytesRemainingInPage; - oggbs->bytesRemainingInPage = 0; - } - MA_DR_FLAC_ASSERT(bytesRemainingToRead > 0); - if (!ma_dr_flac_oggbs__goto_next_page(oggbs, ma_dr_flac_ogg_recover_on_crc_mismatch)) { - break; - } - } - return bytesRead; -} -static ma_bool32 ma_dr_flac__on_seek_ogg(void* pUserData, int offset, ma_dr_flac_seek_origin origin) -{ - ma_dr_flac_oggbs* oggbs = (ma_dr_flac_oggbs*)pUserData; - int bytesSeeked = 0; - MA_DR_FLAC_ASSERT(oggbs != NULL); - MA_DR_FLAC_ASSERT(offset >= 0); - if (origin == ma_dr_flac_seek_origin_start) { - if (!ma_dr_flac_oggbs__seek_physical(oggbs, (int)oggbs->firstBytePos, ma_dr_flac_seek_origin_start)) { - return MA_FALSE; - } - if (!ma_dr_flac_oggbs__goto_next_page(oggbs, ma_dr_flac_ogg_fail_on_crc_mismatch)) { - return MA_FALSE; - } - return ma_dr_flac__on_seek_ogg(pUserData, offset, ma_dr_flac_seek_origin_current); - } - MA_DR_FLAC_ASSERT(origin == ma_dr_flac_seek_origin_current); - while (bytesSeeked < offset) { - int bytesRemainingToSeek = offset - bytesSeeked; - MA_DR_FLAC_ASSERT(bytesRemainingToSeek >= 0); - if (oggbs->bytesRemainingInPage >= (size_t)bytesRemainingToSeek) { - bytesSeeked += bytesRemainingToSeek; - (void)bytesSeeked; - oggbs->bytesRemainingInPage -= bytesRemainingToSeek; - break; - } - if (oggbs->bytesRemainingInPage > 0) { - bytesSeeked += (int)oggbs->bytesRemainingInPage; - oggbs->bytesRemainingInPage = 0; - } - MA_DR_FLAC_ASSERT(bytesRemainingToSeek > 0); - if (!ma_dr_flac_oggbs__goto_next_page(oggbs, ma_dr_flac_ogg_fail_on_crc_mismatch)) { - return MA_FALSE; - } - } - return MA_TRUE; -} -static ma_bool32 ma_dr_flac_ogg__seek_to_pcm_frame(ma_dr_flac* pFlac, ma_uint64 pcmFrameIndex) -{ - ma_dr_flac_oggbs* oggbs = (ma_dr_flac_oggbs*)pFlac->_oggbs; - ma_uint64 originalBytePos; - ma_uint64 runningGranulePosition; - ma_uint64 runningFrameBytePos; - ma_uint64 runningPCMFrameCount; - MA_DR_FLAC_ASSERT(oggbs != NULL); - originalBytePos = oggbs->currentBytePos; - if (!ma_dr_flac__seek_to_byte(&pFlac->bs, pFlac->firstFLACFramePosInBytes)) { - return MA_FALSE; - } - oggbs->bytesRemainingInPage = 0; - runningGranulePosition = 0; - for (;;) { - if (!ma_dr_flac_oggbs__goto_next_page(oggbs, ma_dr_flac_ogg_recover_on_crc_mismatch)) { - ma_dr_flac_oggbs__seek_physical(oggbs, originalBytePos, ma_dr_flac_seek_origin_start); - return MA_FALSE; - } - runningFrameBytePos = oggbs->currentBytePos - ma_dr_flac_ogg__get_page_header_size(&oggbs->currentPageHeader) - oggbs->pageDataSize; - if (oggbs->currentPageHeader.granulePosition >= pcmFrameIndex) { - break; - } - if ((oggbs->currentPageHeader.headerType & 0x01) == 0) { - if (oggbs->currentPageHeader.segmentTable[0] >= 2) { - ma_uint8 firstBytesInPage[2]; - firstBytesInPage[0] = oggbs->pageData[0]; - firstBytesInPage[1] = oggbs->pageData[1]; - if ((firstBytesInPage[0] == 0xFF) && (firstBytesInPage[1] & 0xFC) == 0xF8) { - runningGranulePosition = oggbs->currentPageHeader.granulePosition; - } - continue; - } - } - } - if (!ma_dr_flac_oggbs__seek_physical(oggbs, runningFrameBytePos, ma_dr_flac_seek_origin_start)) { - return MA_FALSE; - } - if (!ma_dr_flac_oggbs__goto_next_page(oggbs, ma_dr_flac_ogg_recover_on_crc_mismatch)) { - return MA_FALSE; - } - runningPCMFrameCount = runningGranulePosition; - for (;;) { - ma_uint64 firstPCMFrameInFLACFrame = 0; - ma_uint64 lastPCMFrameInFLACFrame = 0; - ma_uint64 pcmFrameCountInThisFrame; - if (!ma_dr_flac__read_next_flac_frame_header(&pFlac->bs, pFlac->bitsPerSample, &pFlac->currentFLACFrame.header)) { - return MA_FALSE; - } - ma_dr_flac__get_pcm_frame_range_of_current_flac_frame(pFlac, &firstPCMFrameInFLACFrame, &lastPCMFrameInFLACFrame); - pcmFrameCountInThisFrame = (lastPCMFrameInFLACFrame - firstPCMFrameInFLACFrame) + 1; - if (pcmFrameIndex == pFlac->totalPCMFrameCount && (runningPCMFrameCount + pcmFrameCountInThisFrame) == pFlac->totalPCMFrameCount) { - ma_result result = ma_dr_flac__decode_flac_frame(pFlac); - if (result == MA_SUCCESS) { - pFlac->currentPCMFrame = pcmFrameIndex; - pFlac->currentFLACFrame.pcmFramesRemaining = 0; - return MA_TRUE; - } else { - return MA_FALSE; - } - } - if (pcmFrameIndex < (runningPCMFrameCount + pcmFrameCountInThisFrame)) { - ma_result result = ma_dr_flac__decode_flac_frame(pFlac); - if (result == MA_SUCCESS) { - ma_uint64 pcmFramesToDecode = (size_t)(pcmFrameIndex - runningPCMFrameCount); - if (pcmFramesToDecode == 0) { - return MA_TRUE; - } - pFlac->currentPCMFrame = runningPCMFrameCount; - return ma_dr_flac__seek_forward_by_pcm_frames(pFlac, pcmFramesToDecode) == pcmFramesToDecode; - } else { - if (result == MA_CRC_MISMATCH) { - continue; - } else { - return MA_FALSE; - } - } - } else { - ma_result result = ma_dr_flac__seek_to_next_flac_frame(pFlac); - if (result == MA_SUCCESS) { - runningPCMFrameCount += pcmFrameCountInThisFrame; - } else { - if (result == MA_CRC_MISMATCH) { - continue; - } else { - return MA_FALSE; - } - } - } - } -} -static ma_bool32 ma_dr_flac__init_private__ogg(ma_dr_flac_init_info* pInit, ma_dr_flac_read_proc onRead, ma_dr_flac_seek_proc onSeek, ma_dr_flac_meta_proc onMeta, void* pUserData, void* pUserDataMD, ma_bool32 relaxed) -{ - ma_dr_flac_ogg_page_header header; - ma_uint32 crc32 = MA_DR_FLAC_OGG_CAPTURE_PATTERN_CRC32; - ma_uint32 bytesRead = 0; - (void)relaxed; - pInit->container = ma_dr_flac_container_ogg; - pInit->oggFirstBytePos = 0; - if (ma_dr_flac_ogg__read_page_header_after_capture_pattern(onRead, pUserData, &header, &bytesRead, &crc32) != MA_SUCCESS) { - return MA_FALSE; - } - pInit->runningFilePos += bytesRead; - for (;;) { - int pageBodySize; - if ((header.headerType & 0x02) == 0) { - return MA_FALSE; - } - pageBodySize = ma_dr_flac_ogg__get_page_body_size(&header); - if (pageBodySize == 51) { - ma_uint32 bytesRemainingInPage = pageBodySize; - ma_uint8 packetType; - if (onRead(pUserData, &packetType, 1) != 1) { - return MA_FALSE; - } - bytesRemainingInPage -= 1; - if (packetType == 0x7F) { - ma_uint8 sig[4]; - if (onRead(pUserData, sig, 4) != 4) { - return MA_FALSE; - } - bytesRemainingInPage -= 4; - if (sig[0] == 'F' && sig[1] == 'L' && sig[2] == 'A' && sig[3] == 'C') { - ma_uint8 mappingVersion[2]; - if (onRead(pUserData, mappingVersion, 2) != 2) { - return MA_FALSE; - } - if (mappingVersion[0] != 1) { - return MA_FALSE; - } - if (!onSeek(pUserData, 2, ma_dr_flac_seek_origin_current)) { - return MA_FALSE; - } - if (onRead(pUserData, sig, 4) != 4) { - return MA_FALSE; - } - if (sig[0] == 'f' && sig[1] == 'L' && sig[2] == 'a' && sig[3] == 'C') { - ma_dr_flac_streaminfo streaminfo; - ma_uint8 isLastBlock; - ma_uint8 blockType; - ma_uint32 blockSize; - if (!ma_dr_flac__read_and_decode_block_header(onRead, pUserData, &isLastBlock, &blockType, &blockSize)) { - return MA_FALSE; - } - if (blockType != MA_DR_FLAC_METADATA_BLOCK_TYPE_STREAMINFO || blockSize != 34) { - return MA_FALSE; - } - if (ma_dr_flac__read_streaminfo(onRead, pUserData, &streaminfo)) { - pInit->hasStreamInfoBlock = MA_TRUE; - pInit->sampleRate = streaminfo.sampleRate; - pInit->channels = streaminfo.channels; - pInit->bitsPerSample = streaminfo.bitsPerSample; - pInit->totalPCMFrameCount = streaminfo.totalPCMFrameCount; - pInit->maxBlockSizeInPCMFrames = streaminfo.maxBlockSizeInPCMFrames; - pInit->hasMetadataBlocks = !isLastBlock; - if (onMeta) { - ma_dr_flac_metadata metadata; - metadata.type = MA_DR_FLAC_METADATA_BLOCK_TYPE_STREAMINFO; - metadata.pRawData = NULL; - metadata.rawDataSize = 0; - metadata.data.streaminfo = streaminfo; - onMeta(pUserDataMD, &metadata); - } - pInit->runningFilePos += pageBodySize; - pInit->oggFirstBytePos = pInit->runningFilePos - 79; - pInit->oggSerial = header.serialNumber; - pInit->oggBosHeader = header; - break; - } else { - return MA_FALSE; - } - } else { - return MA_FALSE; - } - } else { - if (!onSeek(pUserData, bytesRemainingInPage, ma_dr_flac_seek_origin_current)) { - return MA_FALSE; - } - } - } else { - if (!onSeek(pUserData, bytesRemainingInPage, ma_dr_flac_seek_origin_current)) { - return MA_FALSE; - } - } - } else { - if (!onSeek(pUserData, pageBodySize, ma_dr_flac_seek_origin_current)) { - return MA_FALSE; - } - } - pInit->runningFilePos += pageBodySize; - if (ma_dr_flac_ogg__read_page_header(onRead, pUserData, &header, &bytesRead, &crc32) != MA_SUCCESS) { - return MA_FALSE; - } - pInit->runningFilePos += bytesRead; - } - pInit->hasMetadataBlocks = MA_TRUE; - return MA_TRUE; -} -#endif -static ma_bool32 ma_dr_flac__init_private(ma_dr_flac_init_info* pInit, ma_dr_flac_read_proc onRead, ma_dr_flac_seek_proc onSeek, ma_dr_flac_meta_proc onMeta, ma_dr_flac_container container, void* pUserData, void* pUserDataMD) -{ - ma_bool32 relaxed; - ma_uint8 id[4]; - if (pInit == NULL || onRead == NULL || onSeek == NULL) { - return MA_FALSE; - } - MA_DR_FLAC_ZERO_MEMORY(pInit, sizeof(*pInit)); - pInit->onRead = onRead; - pInit->onSeek = onSeek; - pInit->onMeta = onMeta; - pInit->container = container; - pInit->pUserData = pUserData; - pInit->pUserDataMD = pUserDataMD; - pInit->bs.onRead = onRead; - pInit->bs.onSeek = onSeek; - pInit->bs.pUserData = pUserData; - ma_dr_flac__reset_cache(&pInit->bs); - relaxed = container != ma_dr_flac_container_unknown; - for (;;) { - if (onRead(pUserData, id, 4) != 4) { - return MA_FALSE; - } - pInit->runningFilePos += 4; - if (id[0] == 'I' && id[1] == 'D' && id[2] == '3') { - ma_uint8 header[6]; - ma_uint8 flags; - ma_uint32 headerSize; - if (onRead(pUserData, header, 6) != 6) { - return MA_FALSE; - } - pInit->runningFilePos += 6; - flags = header[1]; - MA_DR_FLAC_COPY_MEMORY(&headerSize, header+2, 4); - headerSize = ma_dr_flac__unsynchsafe_32(ma_dr_flac__be2host_32(headerSize)); - if (flags & 0x10) { - headerSize += 10; - } - if (!onSeek(pUserData, headerSize, ma_dr_flac_seek_origin_current)) { - return MA_FALSE; - } - pInit->runningFilePos += headerSize; - } else { - break; - } - } - if (id[0] == 'f' && id[1] == 'L' && id[2] == 'a' && id[3] == 'C') { - return ma_dr_flac__init_private__native(pInit, onRead, onSeek, onMeta, pUserData, pUserDataMD, relaxed); - } -#ifndef MA_DR_FLAC_NO_OGG - if (id[0] == 'O' && id[1] == 'g' && id[2] == 'g' && id[3] == 'S') { - return ma_dr_flac__init_private__ogg(pInit, onRead, onSeek, onMeta, pUserData, pUserDataMD, relaxed); - } -#endif - if (relaxed) { - if (container == ma_dr_flac_container_native) { - return ma_dr_flac__init_private__native(pInit, onRead, onSeek, onMeta, pUserData, pUserDataMD, relaxed); - } -#ifndef MA_DR_FLAC_NO_OGG - if (container == ma_dr_flac_container_ogg) { - return ma_dr_flac__init_private__ogg(pInit, onRead, onSeek, onMeta, pUserData, pUserDataMD, relaxed); - } -#endif - } - return MA_FALSE; -} -static void ma_dr_flac__init_from_info(ma_dr_flac* pFlac, const ma_dr_flac_init_info* pInit) -{ - MA_DR_FLAC_ASSERT(pFlac != NULL); - MA_DR_FLAC_ASSERT(pInit != NULL); - MA_DR_FLAC_ZERO_MEMORY(pFlac, sizeof(*pFlac)); - pFlac->bs = pInit->bs; - pFlac->onMeta = pInit->onMeta; - pFlac->pUserDataMD = pInit->pUserDataMD; - pFlac->maxBlockSizeInPCMFrames = pInit->maxBlockSizeInPCMFrames; - pFlac->sampleRate = pInit->sampleRate; - pFlac->channels = (ma_uint8)pInit->channels; - pFlac->bitsPerSample = (ma_uint8)pInit->bitsPerSample; - pFlac->totalPCMFrameCount = pInit->totalPCMFrameCount; - pFlac->container = pInit->container; -} -static ma_dr_flac* ma_dr_flac_open_with_metadata_private(ma_dr_flac_read_proc onRead, ma_dr_flac_seek_proc onSeek, ma_dr_flac_meta_proc onMeta, ma_dr_flac_container container, void* pUserData, void* pUserDataMD, const ma_allocation_callbacks* pAllocationCallbacks) -{ - ma_dr_flac_init_info init; - ma_uint32 allocationSize; - ma_uint32 wholeSIMDVectorCountPerChannel; - ma_uint32 decodedSamplesAllocationSize; -#ifndef MA_DR_FLAC_NO_OGG - ma_dr_flac_oggbs* pOggbs = NULL; -#endif - ma_uint64 firstFramePos; - ma_uint64 seektablePos; - ma_uint32 seekpointCount; - ma_allocation_callbacks allocationCallbacks; - ma_dr_flac* pFlac; - ma_dr_flac__init_cpu_caps(); - if (!ma_dr_flac__init_private(&init, onRead, onSeek, onMeta, container, pUserData, pUserDataMD)) { - return NULL; - } - if (pAllocationCallbacks != NULL) { - allocationCallbacks = *pAllocationCallbacks; - if (allocationCallbacks.onFree == NULL || (allocationCallbacks.onMalloc == NULL && allocationCallbacks.onRealloc == NULL)) { - return NULL; - } - } else { - allocationCallbacks.pUserData = NULL; - allocationCallbacks.onMalloc = ma_dr_flac__malloc_default; - allocationCallbacks.onRealloc = ma_dr_flac__realloc_default; - allocationCallbacks.onFree = ma_dr_flac__free_default; - } - allocationSize = sizeof(ma_dr_flac); - if ((init.maxBlockSizeInPCMFrames % (MA_DR_FLAC_MAX_SIMD_VECTOR_SIZE / sizeof(ma_int32))) == 0) { - wholeSIMDVectorCountPerChannel = (init.maxBlockSizeInPCMFrames / (MA_DR_FLAC_MAX_SIMD_VECTOR_SIZE / sizeof(ma_int32))); - } else { - wholeSIMDVectorCountPerChannel = (init.maxBlockSizeInPCMFrames / (MA_DR_FLAC_MAX_SIMD_VECTOR_SIZE / sizeof(ma_int32))) + 1; - } - decodedSamplesAllocationSize = wholeSIMDVectorCountPerChannel * MA_DR_FLAC_MAX_SIMD_VECTOR_SIZE * init.channels; - allocationSize += decodedSamplesAllocationSize; - allocationSize += MA_DR_FLAC_MAX_SIMD_VECTOR_SIZE; -#ifndef MA_DR_FLAC_NO_OGG - if (init.container == ma_dr_flac_container_ogg) { - allocationSize += sizeof(ma_dr_flac_oggbs); - pOggbs = (ma_dr_flac_oggbs*)ma_dr_flac__malloc_from_callbacks(sizeof(*pOggbs), &allocationCallbacks); - if (pOggbs == NULL) { - return NULL; - } - MA_DR_FLAC_ZERO_MEMORY(pOggbs, sizeof(*pOggbs)); - pOggbs->onRead = onRead; - pOggbs->onSeek = onSeek; - pOggbs->pUserData = pUserData; - pOggbs->currentBytePos = init.oggFirstBytePos; - pOggbs->firstBytePos = init.oggFirstBytePos; - pOggbs->serialNumber = init.oggSerial; - pOggbs->bosPageHeader = init.oggBosHeader; - pOggbs->bytesRemainingInPage = 0; - } -#endif - firstFramePos = 42; - seektablePos = 0; - seekpointCount = 0; - if (init.hasMetadataBlocks) { - ma_dr_flac_read_proc onReadOverride = onRead; - ma_dr_flac_seek_proc onSeekOverride = onSeek; - void* pUserDataOverride = pUserData; -#ifndef MA_DR_FLAC_NO_OGG - if (init.container == ma_dr_flac_container_ogg) { - onReadOverride = ma_dr_flac__on_read_ogg; - onSeekOverride = ma_dr_flac__on_seek_ogg; - pUserDataOverride = (void*)pOggbs; - } -#endif - if (!ma_dr_flac__read_and_decode_metadata(onReadOverride, onSeekOverride, onMeta, pUserDataOverride, pUserDataMD, &firstFramePos, &seektablePos, &seekpointCount, &allocationCallbacks)) { - #ifndef MA_DR_FLAC_NO_OGG - ma_dr_flac__free_from_callbacks(pOggbs, &allocationCallbacks); - #endif - return NULL; - } - allocationSize += seekpointCount * sizeof(ma_dr_flac_seekpoint); - } - pFlac = (ma_dr_flac*)ma_dr_flac__malloc_from_callbacks(allocationSize, &allocationCallbacks); - if (pFlac == NULL) { - #ifndef MA_DR_FLAC_NO_OGG - ma_dr_flac__free_from_callbacks(pOggbs, &allocationCallbacks); - #endif - return NULL; - } - ma_dr_flac__init_from_info(pFlac, &init); - pFlac->allocationCallbacks = allocationCallbacks; - pFlac->pDecodedSamples = (ma_int32*)ma_dr_flac_align((size_t)pFlac->pExtraData, MA_DR_FLAC_MAX_SIMD_VECTOR_SIZE); -#ifndef MA_DR_FLAC_NO_OGG - if (init.container == ma_dr_flac_container_ogg) { - ma_dr_flac_oggbs* pInternalOggbs = (ma_dr_flac_oggbs*)((ma_uint8*)pFlac->pDecodedSamples + decodedSamplesAllocationSize + (seekpointCount * sizeof(ma_dr_flac_seekpoint))); - MA_DR_FLAC_COPY_MEMORY(pInternalOggbs, pOggbs, sizeof(*pOggbs)); - ma_dr_flac__free_from_callbacks(pOggbs, &allocationCallbacks); - pOggbs = NULL; - pFlac->bs.onRead = ma_dr_flac__on_read_ogg; - pFlac->bs.onSeek = ma_dr_flac__on_seek_ogg; - pFlac->bs.pUserData = (void*)pInternalOggbs; - pFlac->_oggbs = (void*)pInternalOggbs; - } -#endif - pFlac->firstFLACFramePosInBytes = firstFramePos; -#ifndef MA_DR_FLAC_NO_OGG - if (init.container == ma_dr_flac_container_ogg) - { - pFlac->pSeekpoints = NULL; - pFlac->seekpointCount = 0; - } - else -#endif - { - if (seektablePos != 0) { - pFlac->seekpointCount = seekpointCount; - pFlac->pSeekpoints = (ma_dr_flac_seekpoint*)((ma_uint8*)pFlac->pDecodedSamples + decodedSamplesAllocationSize); - MA_DR_FLAC_ASSERT(pFlac->bs.onSeek != NULL); - MA_DR_FLAC_ASSERT(pFlac->bs.onRead != NULL); - if (pFlac->bs.onSeek(pFlac->bs.pUserData, (int)seektablePos, ma_dr_flac_seek_origin_start)) { - ma_uint32 iSeekpoint; - for (iSeekpoint = 0; iSeekpoint < seekpointCount; iSeekpoint += 1) { - if (pFlac->bs.onRead(pFlac->bs.pUserData, pFlac->pSeekpoints + iSeekpoint, MA_DR_FLAC_SEEKPOINT_SIZE_IN_BYTES) == MA_DR_FLAC_SEEKPOINT_SIZE_IN_BYTES) { - pFlac->pSeekpoints[iSeekpoint].firstPCMFrame = ma_dr_flac__be2host_64(pFlac->pSeekpoints[iSeekpoint].firstPCMFrame); - pFlac->pSeekpoints[iSeekpoint].flacFrameOffset = ma_dr_flac__be2host_64(pFlac->pSeekpoints[iSeekpoint].flacFrameOffset); - pFlac->pSeekpoints[iSeekpoint].pcmFrameCount = ma_dr_flac__be2host_16(pFlac->pSeekpoints[iSeekpoint].pcmFrameCount); - } else { - pFlac->pSeekpoints = NULL; - pFlac->seekpointCount = 0; - break; - } - } - if (!pFlac->bs.onSeek(pFlac->bs.pUserData, (int)pFlac->firstFLACFramePosInBytes, ma_dr_flac_seek_origin_start)) { - ma_dr_flac__free_from_callbacks(pFlac, &allocationCallbacks); - return NULL; - } - } else { - pFlac->pSeekpoints = NULL; - pFlac->seekpointCount = 0; - } - } - } - if (!init.hasStreamInfoBlock) { - pFlac->currentFLACFrame.header = init.firstFrameHeader; - for (;;) { - ma_result result = ma_dr_flac__decode_flac_frame(pFlac); - if (result == MA_SUCCESS) { - break; - } else { - if (result == MA_CRC_MISMATCH) { - if (!ma_dr_flac__read_next_flac_frame_header(&pFlac->bs, pFlac->bitsPerSample, &pFlac->currentFLACFrame.header)) { - ma_dr_flac__free_from_callbacks(pFlac, &allocationCallbacks); - return NULL; - } - continue; - } else { - ma_dr_flac__free_from_callbacks(pFlac, &allocationCallbacks); - return NULL; - } - } - } - } - return pFlac; -} -#ifndef MA_DR_FLAC_NO_STDIO -#include -#ifndef MA_DR_FLAC_NO_WCHAR -#include -#endif -static size_t ma_dr_flac__on_read_stdio(void* pUserData, void* bufferOut, size_t bytesToRead) -{ - return fread(bufferOut, 1, bytesToRead, (FILE*)pUserData); -} -static ma_bool32 ma_dr_flac__on_seek_stdio(void* pUserData, int offset, ma_dr_flac_seek_origin origin) -{ - MA_DR_FLAC_ASSERT(offset >= 0); - return fseek((FILE*)pUserData, offset, (origin == ma_dr_flac_seek_origin_current) ? SEEK_CUR : SEEK_SET) == 0; -} -MA_API ma_dr_flac* ma_dr_flac_open_file(const char* pFileName, const ma_allocation_callbacks* pAllocationCallbacks) -{ - ma_dr_flac* pFlac; - FILE* pFile; - if (ma_fopen(&pFile, pFileName, "rb") != MA_SUCCESS) { - return NULL; - } - pFlac = ma_dr_flac_open(ma_dr_flac__on_read_stdio, ma_dr_flac__on_seek_stdio, (void*)pFile, pAllocationCallbacks); - if (pFlac == NULL) { - fclose(pFile); - return NULL; - } - return pFlac; -} -#ifndef MA_DR_FLAC_NO_WCHAR -MA_API ma_dr_flac* ma_dr_flac_open_file_w(const wchar_t* pFileName, const ma_allocation_callbacks* pAllocationCallbacks) -{ - ma_dr_flac* pFlac; - FILE* pFile; - if (ma_wfopen(&pFile, pFileName, L"rb", pAllocationCallbacks) != MA_SUCCESS) { - return NULL; - } - pFlac = ma_dr_flac_open(ma_dr_flac__on_read_stdio, ma_dr_flac__on_seek_stdio, (void*)pFile, pAllocationCallbacks); - if (pFlac == NULL) { - fclose(pFile); - return NULL; - } - return pFlac; -} -#endif -MA_API ma_dr_flac* ma_dr_flac_open_file_with_metadata(const char* pFileName, ma_dr_flac_meta_proc onMeta, void* pUserData, const ma_allocation_callbacks* pAllocationCallbacks) -{ - ma_dr_flac* pFlac; - FILE* pFile; - if (ma_fopen(&pFile, pFileName, "rb") != MA_SUCCESS) { - return NULL; - } - pFlac = ma_dr_flac_open_with_metadata_private(ma_dr_flac__on_read_stdio, ma_dr_flac__on_seek_stdio, onMeta, ma_dr_flac_container_unknown, (void*)pFile, pUserData, pAllocationCallbacks); - if (pFlac == NULL) { - fclose(pFile); - return pFlac; - } - return pFlac; -} -#ifndef MA_DR_FLAC_NO_WCHAR -MA_API ma_dr_flac* ma_dr_flac_open_file_with_metadata_w(const wchar_t* pFileName, ma_dr_flac_meta_proc onMeta, void* pUserData, const ma_allocation_callbacks* pAllocationCallbacks) -{ - ma_dr_flac* pFlac; - FILE* pFile; - if (ma_wfopen(&pFile, pFileName, L"rb", pAllocationCallbacks) != MA_SUCCESS) { - return NULL; - } - pFlac = ma_dr_flac_open_with_metadata_private(ma_dr_flac__on_read_stdio, ma_dr_flac__on_seek_stdio, onMeta, ma_dr_flac_container_unknown, (void*)pFile, pUserData, pAllocationCallbacks); - if (pFlac == NULL) { - fclose(pFile); - return pFlac; - } - return pFlac; -} -#endif -#endif -static size_t ma_dr_flac__on_read_memory(void* pUserData, void* bufferOut, size_t bytesToRead) -{ - ma_dr_flac__memory_stream* memoryStream = (ma_dr_flac__memory_stream*)pUserData; - size_t bytesRemaining; - MA_DR_FLAC_ASSERT(memoryStream != NULL); - MA_DR_FLAC_ASSERT(memoryStream->dataSize >= memoryStream->currentReadPos); - bytesRemaining = memoryStream->dataSize - memoryStream->currentReadPos; - if (bytesToRead > bytesRemaining) { - bytesToRead = bytesRemaining; - } - if (bytesToRead > 0) { - MA_DR_FLAC_COPY_MEMORY(bufferOut, memoryStream->data + memoryStream->currentReadPos, bytesToRead); - memoryStream->currentReadPos += bytesToRead; - } - return bytesToRead; -} -static ma_bool32 ma_dr_flac__on_seek_memory(void* pUserData, int offset, ma_dr_flac_seek_origin origin) -{ - ma_dr_flac__memory_stream* memoryStream = (ma_dr_flac__memory_stream*)pUserData; - MA_DR_FLAC_ASSERT(memoryStream != NULL); - MA_DR_FLAC_ASSERT(offset >= 0); - if (offset > (ma_int64)memoryStream->dataSize) { - return MA_FALSE; - } - if (origin == ma_dr_flac_seek_origin_current) { - if (memoryStream->currentReadPos + offset <= memoryStream->dataSize) { - memoryStream->currentReadPos += offset; - } else { - return MA_FALSE; - } - } else { - if ((ma_uint32)offset <= memoryStream->dataSize) { - memoryStream->currentReadPos = offset; - } else { - return MA_FALSE; - } - } - return MA_TRUE; -} -MA_API ma_dr_flac* ma_dr_flac_open_memory(const void* pData, size_t dataSize, const ma_allocation_callbacks* pAllocationCallbacks) -{ - ma_dr_flac__memory_stream memoryStream; - ma_dr_flac* pFlac; - memoryStream.data = (const ma_uint8*)pData; - memoryStream.dataSize = dataSize; - memoryStream.currentReadPos = 0; - pFlac = ma_dr_flac_open(ma_dr_flac__on_read_memory, ma_dr_flac__on_seek_memory, &memoryStream, pAllocationCallbacks); - if (pFlac == NULL) { - return NULL; - } - pFlac->memoryStream = memoryStream; -#ifndef MA_DR_FLAC_NO_OGG - if (pFlac->container == ma_dr_flac_container_ogg) - { - ma_dr_flac_oggbs* oggbs = (ma_dr_flac_oggbs*)pFlac->_oggbs; - oggbs->pUserData = &pFlac->memoryStream; - } - else -#endif - { - pFlac->bs.pUserData = &pFlac->memoryStream; - } - return pFlac; -} -MA_API ma_dr_flac* ma_dr_flac_open_memory_with_metadata(const void* pData, size_t dataSize, ma_dr_flac_meta_proc onMeta, void* pUserData, const ma_allocation_callbacks* pAllocationCallbacks) -{ - ma_dr_flac__memory_stream memoryStream; - ma_dr_flac* pFlac; - memoryStream.data = (const ma_uint8*)pData; - memoryStream.dataSize = dataSize; - memoryStream.currentReadPos = 0; - pFlac = ma_dr_flac_open_with_metadata_private(ma_dr_flac__on_read_memory, ma_dr_flac__on_seek_memory, onMeta, ma_dr_flac_container_unknown, &memoryStream, pUserData, pAllocationCallbacks); - if (pFlac == NULL) { - return NULL; - } - pFlac->memoryStream = memoryStream; -#ifndef MA_DR_FLAC_NO_OGG - if (pFlac->container == ma_dr_flac_container_ogg) - { - ma_dr_flac_oggbs* oggbs = (ma_dr_flac_oggbs*)pFlac->_oggbs; - oggbs->pUserData = &pFlac->memoryStream; - } - else -#endif - { - pFlac->bs.pUserData = &pFlac->memoryStream; - } - return pFlac; -} -MA_API ma_dr_flac* ma_dr_flac_open(ma_dr_flac_read_proc onRead, ma_dr_flac_seek_proc onSeek, void* pUserData, const ma_allocation_callbacks* pAllocationCallbacks) -{ - return ma_dr_flac_open_with_metadata_private(onRead, onSeek, NULL, ma_dr_flac_container_unknown, pUserData, pUserData, pAllocationCallbacks); -} -MA_API ma_dr_flac* ma_dr_flac_open_relaxed(ma_dr_flac_read_proc onRead, ma_dr_flac_seek_proc onSeek, ma_dr_flac_container container, void* pUserData, const ma_allocation_callbacks* pAllocationCallbacks) -{ - return ma_dr_flac_open_with_metadata_private(onRead, onSeek, NULL, container, pUserData, pUserData, pAllocationCallbacks); -} -MA_API ma_dr_flac* ma_dr_flac_open_with_metadata(ma_dr_flac_read_proc onRead, ma_dr_flac_seek_proc onSeek, ma_dr_flac_meta_proc onMeta, void* pUserData, const ma_allocation_callbacks* pAllocationCallbacks) -{ - return ma_dr_flac_open_with_metadata_private(onRead, onSeek, onMeta, ma_dr_flac_container_unknown, pUserData, pUserData, pAllocationCallbacks); -} -MA_API ma_dr_flac* ma_dr_flac_open_with_metadata_relaxed(ma_dr_flac_read_proc onRead, ma_dr_flac_seek_proc onSeek, ma_dr_flac_meta_proc onMeta, ma_dr_flac_container container, void* pUserData, const ma_allocation_callbacks* pAllocationCallbacks) -{ - return ma_dr_flac_open_with_metadata_private(onRead, onSeek, onMeta, container, pUserData, pUserData, pAllocationCallbacks); -} -MA_API void ma_dr_flac_close(ma_dr_flac* pFlac) -{ - if (pFlac == NULL) { - return; - } -#ifndef MA_DR_FLAC_NO_STDIO - if (pFlac->bs.onRead == ma_dr_flac__on_read_stdio) { - fclose((FILE*)pFlac->bs.pUserData); - } -#ifndef MA_DR_FLAC_NO_OGG - if (pFlac->container == ma_dr_flac_container_ogg) { - ma_dr_flac_oggbs* oggbs = (ma_dr_flac_oggbs*)pFlac->_oggbs; - MA_DR_FLAC_ASSERT(pFlac->bs.onRead == ma_dr_flac__on_read_ogg); - if (oggbs->onRead == ma_dr_flac__on_read_stdio) { - fclose((FILE*)oggbs->pUserData); - } - } -#endif -#endif - ma_dr_flac__free_from_callbacks(pFlac, &pFlac->allocationCallbacks); -} -#if 0 -static MA_INLINE void ma_dr_flac_read_pcm_frames_s32__decode_left_side__reference(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, ma_int32* pOutputSamples) -{ - ma_uint64 i; - for (i = 0; i < frameCount; ++i) { - ma_uint32 left = (ma_uint32)pInputSamples0[i] << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample); - ma_uint32 side = (ma_uint32)pInputSamples1[i] << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample); - ma_uint32 right = left - side; - pOutputSamples[i*2+0] = (ma_int32)left; - pOutputSamples[i*2+1] = (ma_int32)right; - } -} -#endif -static MA_INLINE void ma_dr_flac_read_pcm_frames_s32__decode_left_side__scalar(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, ma_int32* pOutputSamples) -{ - ma_uint64 i; - ma_uint64 frameCount4 = frameCount >> 2; - const ma_uint32* pInputSamples0U32 = (const ma_uint32*)pInputSamples0; - const ma_uint32* pInputSamples1U32 = (const ma_uint32*)pInputSamples1; - ma_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; - ma_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; - for (i = 0; i < frameCount4; ++i) { - ma_uint32 left0 = pInputSamples0U32[i*4+0] << shift0; - ma_uint32 left1 = pInputSamples0U32[i*4+1] << shift0; - ma_uint32 left2 = pInputSamples0U32[i*4+2] << shift0; - ma_uint32 left3 = pInputSamples0U32[i*4+3] << shift0; - ma_uint32 side0 = pInputSamples1U32[i*4+0] << shift1; - ma_uint32 side1 = pInputSamples1U32[i*4+1] << shift1; - ma_uint32 side2 = pInputSamples1U32[i*4+2] << shift1; - ma_uint32 side3 = pInputSamples1U32[i*4+3] << shift1; - ma_uint32 right0 = left0 - side0; - ma_uint32 right1 = left1 - side1; - ma_uint32 right2 = left2 - side2; - ma_uint32 right3 = left3 - side3; - pOutputSamples[i*8+0] = (ma_int32)left0; - pOutputSamples[i*8+1] = (ma_int32)right0; - pOutputSamples[i*8+2] = (ma_int32)left1; - pOutputSamples[i*8+3] = (ma_int32)right1; - pOutputSamples[i*8+4] = (ma_int32)left2; - pOutputSamples[i*8+5] = (ma_int32)right2; - pOutputSamples[i*8+6] = (ma_int32)left3; - pOutputSamples[i*8+7] = (ma_int32)right3; - } - for (i = (frameCount4 << 2); i < frameCount; ++i) { - ma_uint32 left = pInputSamples0U32[i] << shift0; - ma_uint32 side = pInputSamples1U32[i] << shift1; - ma_uint32 right = left - side; - pOutputSamples[i*2+0] = (ma_int32)left; - pOutputSamples[i*2+1] = (ma_int32)right; - } -} -#if defined(MA_DR_FLAC_SUPPORT_SSE2) -static MA_INLINE void ma_dr_flac_read_pcm_frames_s32__decode_left_side__sse2(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, ma_int32* pOutputSamples) -{ - ma_uint64 i; - ma_uint64 frameCount4 = frameCount >> 2; - const ma_uint32* pInputSamples0U32 = (const ma_uint32*)pInputSamples0; - const ma_uint32* pInputSamples1U32 = (const ma_uint32*)pInputSamples1; - ma_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; - ma_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; - MA_DR_FLAC_ASSERT(pFlac->bitsPerSample <= 24); - for (i = 0; i < frameCount4; ++i) { - __m128i left = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples0 + i), shift0); - __m128i side = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples1 + i), shift1); - __m128i right = _mm_sub_epi32(left, side); - _mm_storeu_si128((__m128i*)(pOutputSamples + i*8 + 0), _mm_unpacklo_epi32(left, right)); - _mm_storeu_si128((__m128i*)(pOutputSamples + i*8 + 4), _mm_unpackhi_epi32(left, right)); - } - for (i = (frameCount4 << 2); i < frameCount; ++i) { - ma_uint32 left = pInputSamples0U32[i] << shift0; - ma_uint32 side = pInputSamples1U32[i] << shift1; - ma_uint32 right = left - side; - pOutputSamples[i*2+0] = (ma_int32)left; - pOutputSamples[i*2+1] = (ma_int32)right; - } -} -#endif -#if defined(MA_DR_FLAC_SUPPORT_NEON) -static MA_INLINE void ma_dr_flac_read_pcm_frames_s32__decode_left_side__neon(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, ma_int32* pOutputSamples) -{ - ma_uint64 i; - ma_uint64 frameCount4 = frameCount >> 2; - const ma_uint32* pInputSamples0U32 = (const ma_uint32*)pInputSamples0; - const ma_uint32* pInputSamples1U32 = (const ma_uint32*)pInputSamples1; - ma_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; - ma_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; - int32x4_t shift0_4; - int32x4_t shift1_4; - MA_DR_FLAC_ASSERT(pFlac->bitsPerSample <= 24); - shift0_4 = vdupq_n_s32(shift0); - shift1_4 = vdupq_n_s32(shift1); - for (i = 0; i < frameCount4; ++i) { - uint32x4_t left; - uint32x4_t side; - uint32x4_t right; - left = vshlq_u32(vld1q_u32(pInputSamples0U32 + i*4), shift0_4); - side = vshlq_u32(vld1q_u32(pInputSamples1U32 + i*4), shift1_4); - right = vsubq_u32(left, side); - ma_dr_flac__vst2q_u32((ma_uint32*)pOutputSamples + i*8, vzipq_u32(left, right)); - } - for (i = (frameCount4 << 2); i < frameCount; ++i) { - ma_uint32 left = pInputSamples0U32[i] << shift0; - ma_uint32 side = pInputSamples1U32[i] << shift1; - ma_uint32 right = left - side; - pOutputSamples[i*2+0] = (ma_int32)left; - pOutputSamples[i*2+1] = (ma_int32)right; - } -} -#endif -static MA_INLINE void ma_dr_flac_read_pcm_frames_s32__decode_left_side(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, ma_int32* pOutputSamples) -{ -#if defined(MA_DR_FLAC_SUPPORT_SSE2) - if (ma_dr_flac__gIsSSE2Supported && pFlac->bitsPerSample <= 24) { - ma_dr_flac_read_pcm_frames_s32__decode_left_side__sse2(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); - } else -#elif defined(MA_DR_FLAC_SUPPORT_NEON) - if (ma_dr_flac__gIsNEONSupported && pFlac->bitsPerSample <= 24) { - ma_dr_flac_read_pcm_frames_s32__decode_left_side__neon(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); - } else -#endif - { -#if 0 - ma_dr_flac_read_pcm_frames_s32__decode_left_side__reference(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); -#else - ma_dr_flac_read_pcm_frames_s32__decode_left_side__scalar(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); -#endif - } -} -#if 0 -static MA_INLINE void ma_dr_flac_read_pcm_frames_s32__decode_right_side__reference(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, ma_int32* pOutputSamples) -{ - ma_uint64 i; - for (i = 0; i < frameCount; ++i) { - ma_uint32 side = (ma_uint32)pInputSamples0[i] << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample); - ma_uint32 right = (ma_uint32)pInputSamples1[i] << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample); - ma_uint32 left = right + side; - pOutputSamples[i*2+0] = (ma_int32)left; - pOutputSamples[i*2+1] = (ma_int32)right; - } -} -#endif -static MA_INLINE void ma_dr_flac_read_pcm_frames_s32__decode_right_side__scalar(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, ma_int32* pOutputSamples) -{ - ma_uint64 i; - ma_uint64 frameCount4 = frameCount >> 2; - const ma_uint32* pInputSamples0U32 = (const ma_uint32*)pInputSamples0; - const ma_uint32* pInputSamples1U32 = (const ma_uint32*)pInputSamples1; - ma_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; - ma_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; - for (i = 0; i < frameCount4; ++i) { - ma_uint32 side0 = pInputSamples0U32[i*4+0] << shift0; - ma_uint32 side1 = pInputSamples0U32[i*4+1] << shift0; - ma_uint32 side2 = pInputSamples0U32[i*4+2] << shift0; - ma_uint32 side3 = pInputSamples0U32[i*4+3] << shift0; - ma_uint32 right0 = pInputSamples1U32[i*4+0] << shift1; - ma_uint32 right1 = pInputSamples1U32[i*4+1] << shift1; - ma_uint32 right2 = pInputSamples1U32[i*4+2] << shift1; - ma_uint32 right3 = pInputSamples1U32[i*4+3] << shift1; - ma_uint32 left0 = right0 + side0; - ma_uint32 left1 = right1 + side1; - ma_uint32 left2 = right2 + side2; - ma_uint32 left3 = right3 + side3; - pOutputSamples[i*8+0] = (ma_int32)left0; - pOutputSamples[i*8+1] = (ma_int32)right0; - pOutputSamples[i*8+2] = (ma_int32)left1; - pOutputSamples[i*8+3] = (ma_int32)right1; - pOutputSamples[i*8+4] = (ma_int32)left2; - pOutputSamples[i*8+5] = (ma_int32)right2; - pOutputSamples[i*8+6] = (ma_int32)left3; - pOutputSamples[i*8+7] = (ma_int32)right3; - } - for (i = (frameCount4 << 2); i < frameCount; ++i) { - ma_uint32 side = pInputSamples0U32[i] << shift0; - ma_uint32 right = pInputSamples1U32[i] << shift1; - ma_uint32 left = right + side; - pOutputSamples[i*2+0] = (ma_int32)left; - pOutputSamples[i*2+1] = (ma_int32)right; - } -} -#if defined(MA_DR_FLAC_SUPPORT_SSE2) -static MA_INLINE void ma_dr_flac_read_pcm_frames_s32__decode_right_side__sse2(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, ma_int32* pOutputSamples) -{ - ma_uint64 i; - ma_uint64 frameCount4 = frameCount >> 2; - const ma_uint32* pInputSamples0U32 = (const ma_uint32*)pInputSamples0; - const ma_uint32* pInputSamples1U32 = (const ma_uint32*)pInputSamples1; - ma_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; - ma_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; - MA_DR_FLAC_ASSERT(pFlac->bitsPerSample <= 24); - for (i = 0; i < frameCount4; ++i) { - __m128i side = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples0 + i), shift0); - __m128i right = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples1 + i), shift1); - __m128i left = _mm_add_epi32(right, side); - _mm_storeu_si128((__m128i*)(pOutputSamples + i*8 + 0), _mm_unpacklo_epi32(left, right)); - _mm_storeu_si128((__m128i*)(pOutputSamples + i*8 + 4), _mm_unpackhi_epi32(left, right)); - } - for (i = (frameCount4 << 2); i < frameCount; ++i) { - ma_uint32 side = pInputSamples0U32[i] << shift0; - ma_uint32 right = pInputSamples1U32[i] << shift1; - ma_uint32 left = right + side; - pOutputSamples[i*2+0] = (ma_int32)left; - pOutputSamples[i*2+1] = (ma_int32)right; - } -} -#endif -#if defined(MA_DR_FLAC_SUPPORT_NEON) -static MA_INLINE void ma_dr_flac_read_pcm_frames_s32__decode_right_side__neon(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, ma_int32* pOutputSamples) -{ - ma_uint64 i; - ma_uint64 frameCount4 = frameCount >> 2; - const ma_uint32* pInputSamples0U32 = (const ma_uint32*)pInputSamples0; - const ma_uint32* pInputSamples1U32 = (const ma_uint32*)pInputSamples1; - ma_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; - ma_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; - int32x4_t shift0_4; - int32x4_t shift1_4; - MA_DR_FLAC_ASSERT(pFlac->bitsPerSample <= 24); - shift0_4 = vdupq_n_s32(shift0); - shift1_4 = vdupq_n_s32(shift1); - for (i = 0; i < frameCount4; ++i) { - uint32x4_t side; - uint32x4_t right; - uint32x4_t left; - side = vshlq_u32(vld1q_u32(pInputSamples0U32 + i*4), shift0_4); - right = vshlq_u32(vld1q_u32(pInputSamples1U32 + i*4), shift1_4); - left = vaddq_u32(right, side); - ma_dr_flac__vst2q_u32((ma_uint32*)pOutputSamples + i*8, vzipq_u32(left, right)); - } - for (i = (frameCount4 << 2); i < frameCount; ++i) { - ma_uint32 side = pInputSamples0U32[i] << shift0; - ma_uint32 right = pInputSamples1U32[i] << shift1; - ma_uint32 left = right + side; - pOutputSamples[i*2+0] = (ma_int32)left; - pOutputSamples[i*2+1] = (ma_int32)right; - } -} -#endif -static MA_INLINE void ma_dr_flac_read_pcm_frames_s32__decode_right_side(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, ma_int32* pOutputSamples) -{ -#if defined(MA_DR_FLAC_SUPPORT_SSE2) - if (ma_dr_flac__gIsSSE2Supported && pFlac->bitsPerSample <= 24) { - ma_dr_flac_read_pcm_frames_s32__decode_right_side__sse2(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); - } else -#elif defined(MA_DR_FLAC_SUPPORT_NEON) - if (ma_dr_flac__gIsNEONSupported && pFlac->bitsPerSample <= 24) { - ma_dr_flac_read_pcm_frames_s32__decode_right_side__neon(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); - } else -#endif - { -#if 0 - ma_dr_flac_read_pcm_frames_s32__decode_right_side__reference(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); -#else - ma_dr_flac_read_pcm_frames_s32__decode_right_side__scalar(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); -#endif - } -} -#if 0 -static MA_INLINE void ma_dr_flac_read_pcm_frames_s32__decode_mid_side__reference(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, ma_int32* pOutputSamples) -{ - for (ma_uint64 i = 0; i < frameCount; ++i) { - ma_uint32 mid = pInputSamples0U32[i] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; - ma_uint32 side = pInputSamples1U32[i] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; - mid = (mid << 1) | (side & 0x01); - pOutputSamples[i*2+0] = (ma_int32)((ma_uint32)((ma_int32)(mid + side) >> 1) << unusedBitsPerSample); - pOutputSamples[i*2+1] = (ma_int32)((ma_uint32)((ma_int32)(mid - side) >> 1) << unusedBitsPerSample); - } -} -#endif -static MA_INLINE void ma_dr_flac_read_pcm_frames_s32__decode_mid_side__scalar(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, ma_int32* pOutputSamples) -{ - ma_uint64 i; - ma_uint64 frameCount4 = frameCount >> 2; - const ma_uint32* pInputSamples0U32 = (const ma_uint32*)pInputSamples0; - const ma_uint32* pInputSamples1U32 = (const ma_uint32*)pInputSamples1; - ma_int32 shift = unusedBitsPerSample; - if (shift > 0) { - shift -= 1; - for (i = 0; i < frameCount4; ++i) { - ma_uint32 temp0L; - ma_uint32 temp1L; - ma_uint32 temp2L; - ma_uint32 temp3L; - ma_uint32 temp0R; - ma_uint32 temp1R; - ma_uint32 temp2R; - ma_uint32 temp3R; - ma_uint32 mid0 = pInputSamples0U32[i*4+0] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; - ma_uint32 mid1 = pInputSamples0U32[i*4+1] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; - ma_uint32 mid2 = pInputSamples0U32[i*4+2] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; - ma_uint32 mid3 = pInputSamples0U32[i*4+3] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; - ma_uint32 side0 = pInputSamples1U32[i*4+0] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; - ma_uint32 side1 = pInputSamples1U32[i*4+1] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; - ma_uint32 side2 = pInputSamples1U32[i*4+2] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; - ma_uint32 side3 = pInputSamples1U32[i*4+3] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; - mid0 = (mid0 << 1) | (side0 & 0x01); - mid1 = (mid1 << 1) | (side1 & 0x01); - mid2 = (mid2 << 1) | (side2 & 0x01); - mid3 = (mid3 << 1) | (side3 & 0x01); - temp0L = (mid0 + side0) << shift; - temp1L = (mid1 + side1) << shift; - temp2L = (mid2 + side2) << shift; - temp3L = (mid3 + side3) << shift; - temp0R = (mid0 - side0) << shift; - temp1R = (mid1 - side1) << shift; - temp2R = (mid2 - side2) << shift; - temp3R = (mid3 - side3) << shift; - pOutputSamples[i*8+0] = (ma_int32)temp0L; - pOutputSamples[i*8+1] = (ma_int32)temp0R; - pOutputSamples[i*8+2] = (ma_int32)temp1L; - pOutputSamples[i*8+3] = (ma_int32)temp1R; - pOutputSamples[i*8+4] = (ma_int32)temp2L; - pOutputSamples[i*8+5] = (ma_int32)temp2R; - pOutputSamples[i*8+6] = (ma_int32)temp3L; - pOutputSamples[i*8+7] = (ma_int32)temp3R; - } - } else { - for (i = 0; i < frameCount4; ++i) { - ma_uint32 temp0L; - ma_uint32 temp1L; - ma_uint32 temp2L; - ma_uint32 temp3L; - ma_uint32 temp0R; - ma_uint32 temp1R; - ma_uint32 temp2R; - ma_uint32 temp3R; - ma_uint32 mid0 = pInputSamples0U32[i*4+0] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; - ma_uint32 mid1 = pInputSamples0U32[i*4+1] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; - ma_uint32 mid2 = pInputSamples0U32[i*4+2] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; - ma_uint32 mid3 = pInputSamples0U32[i*4+3] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; - ma_uint32 side0 = pInputSamples1U32[i*4+0] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; - ma_uint32 side1 = pInputSamples1U32[i*4+1] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; - ma_uint32 side2 = pInputSamples1U32[i*4+2] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; - ma_uint32 side3 = pInputSamples1U32[i*4+3] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; - mid0 = (mid0 << 1) | (side0 & 0x01); - mid1 = (mid1 << 1) | (side1 & 0x01); - mid2 = (mid2 << 1) | (side2 & 0x01); - mid3 = (mid3 << 1) | (side3 & 0x01); - temp0L = (ma_uint32)((ma_int32)(mid0 + side0) >> 1); - temp1L = (ma_uint32)((ma_int32)(mid1 + side1) >> 1); - temp2L = (ma_uint32)((ma_int32)(mid2 + side2) >> 1); - temp3L = (ma_uint32)((ma_int32)(mid3 + side3) >> 1); - temp0R = (ma_uint32)((ma_int32)(mid0 - side0) >> 1); - temp1R = (ma_uint32)((ma_int32)(mid1 - side1) >> 1); - temp2R = (ma_uint32)((ma_int32)(mid2 - side2) >> 1); - temp3R = (ma_uint32)((ma_int32)(mid3 - side3) >> 1); - pOutputSamples[i*8+0] = (ma_int32)temp0L; - pOutputSamples[i*8+1] = (ma_int32)temp0R; - pOutputSamples[i*8+2] = (ma_int32)temp1L; - pOutputSamples[i*8+3] = (ma_int32)temp1R; - pOutputSamples[i*8+4] = (ma_int32)temp2L; - pOutputSamples[i*8+5] = (ma_int32)temp2R; - pOutputSamples[i*8+6] = (ma_int32)temp3L; - pOutputSamples[i*8+7] = (ma_int32)temp3R; - } - } - for (i = (frameCount4 << 2); i < frameCount; ++i) { - ma_uint32 mid = pInputSamples0U32[i] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; - ma_uint32 side = pInputSamples1U32[i] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; - mid = (mid << 1) | (side & 0x01); - pOutputSamples[i*2+0] = (ma_int32)((ma_uint32)((ma_int32)(mid + side) >> 1) << unusedBitsPerSample); - pOutputSamples[i*2+1] = (ma_int32)((ma_uint32)((ma_int32)(mid - side) >> 1) << unusedBitsPerSample); - } -} -#if defined(MA_DR_FLAC_SUPPORT_SSE2) -static MA_INLINE void ma_dr_flac_read_pcm_frames_s32__decode_mid_side__sse2(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, ma_int32* pOutputSamples) -{ - ma_uint64 i; - ma_uint64 frameCount4 = frameCount >> 2; - const ma_uint32* pInputSamples0U32 = (const ma_uint32*)pInputSamples0; - const ma_uint32* pInputSamples1U32 = (const ma_uint32*)pInputSamples1; - ma_int32 shift = unusedBitsPerSample; - MA_DR_FLAC_ASSERT(pFlac->bitsPerSample <= 24); - if (shift == 0) { - for (i = 0; i < frameCount4; ++i) { - __m128i mid; - __m128i side; - __m128i left; - __m128i right; - mid = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples0 + i), pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample); - side = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples1 + i), pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample); - mid = _mm_or_si128(_mm_slli_epi32(mid, 1), _mm_and_si128(side, _mm_set1_epi32(0x01))); - left = _mm_srai_epi32(_mm_add_epi32(mid, side), 1); - right = _mm_srai_epi32(_mm_sub_epi32(mid, side), 1); - _mm_storeu_si128((__m128i*)(pOutputSamples + i*8 + 0), _mm_unpacklo_epi32(left, right)); - _mm_storeu_si128((__m128i*)(pOutputSamples + i*8 + 4), _mm_unpackhi_epi32(left, right)); - } - for (i = (frameCount4 << 2); i < frameCount; ++i) { - ma_uint32 mid = pInputSamples0U32[i] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; - ma_uint32 side = pInputSamples1U32[i] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; - mid = (mid << 1) | (side & 0x01); - pOutputSamples[i*2+0] = (ma_int32)(mid + side) >> 1; - pOutputSamples[i*2+1] = (ma_int32)(mid - side) >> 1; - } - } else { - shift -= 1; - for (i = 0; i < frameCount4; ++i) { - __m128i mid; - __m128i side; - __m128i left; - __m128i right; - mid = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples0 + i), pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample); - side = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples1 + i), pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample); - mid = _mm_or_si128(_mm_slli_epi32(mid, 1), _mm_and_si128(side, _mm_set1_epi32(0x01))); - left = _mm_slli_epi32(_mm_add_epi32(mid, side), shift); - right = _mm_slli_epi32(_mm_sub_epi32(mid, side), shift); - _mm_storeu_si128((__m128i*)(pOutputSamples + i*8 + 0), _mm_unpacklo_epi32(left, right)); - _mm_storeu_si128((__m128i*)(pOutputSamples + i*8 + 4), _mm_unpackhi_epi32(left, right)); - } - for (i = (frameCount4 << 2); i < frameCount; ++i) { - ma_uint32 mid = pInputSamples0U32[i] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; - ma_uint32 side = pInputSamples1U32[i] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; - mid = (mid << 1) | (side & 0x01); - pOutputSamples[i*2+0] = (ma_int32)((mid + side) << shift); - pOutputSamples[i*2+1] = (ma_int32)((mid - side) << shift); - } - } -} -#endif -#if defined(MA_DR_FLAC_SUPPORT_NEON) -static MA_INLINE void ma_dr_flac_read_pcm_frames_s32__decode_mid_side__neon(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, ma_int32* pOutputSamples) -{ - ma_uint64 i; - ma_uint64 frameCount4 = frameCount >> 2; - const ma_uint32* pInputSamples0U32 = (const ma_uint32*)pInputSamples0; - const ma_uint32* pInputSamples1U32 = (const ma_uint32*)pInputSamples1; - ma_int32 shift = unusedBitsPerSample; - int32x4_t wbpsShift0_4; - int32x4_t wbpsShift1_4; - uint32x4_t one4; - MA_DR_FLAC_ASSERT(pFlac->bitsPerSample <= 24); - wbpsShift0_4 = vdupq_n_s32(pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample); - wbpsShift1_4 = vdupq_n_s32(pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample); - one4 = vdupq_n_u32(1); - if (shift == 0) { - for (i = 0; i < frameCount4; ++i) { - uint32x4_t mid; - uint32x4_t side; - int32x4_t left; - int32x4_t right; - mid = vshlq_u32(vld1q_u32(pInputSamples0U32 + i*4), wbpsShift0_4); - side = vshlq_u32(vld1q_u32(pInputSamples1U32 + i*4), wbpsShift1_4); - mid = vorrq_u32(vshlq_n_u32(mid, 1), vandq_u32(side, one4)); - left = vshrq_n_s32(vreinterpretq_s32_u32(vaddq_u32(mid, side)), 1); - right = vshrq_n_s32(vreinterpretq_s32_u32(vsubq_u32(mid, side)), 1); - ma_dr_flac__vst2q_s32(pOutputSamples + i*8, vzipq_s32(left, right)); - } - for (i = (frameCount4 << 2); i < frameCount; ++i) { - ma_uint32 mid = pInputSamples0U32[i] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; - ma_uint32 side = pInputSamples1U32[i] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; - mid = (mid << 1) | (side & 0x01); - pOutputSamples[i*2+0] = (ma_int32)(mid + side) >> 1; - pOutputSamples[i*2+1] = (ma_int32)(mid - side) >> 1; - } - } else { - int32x4_t shift4; - shift -= 1; - shift4 = vdupq_n_s32(shift); - for (i = 0; i < frameCount4; ++i) { - uint32x4_t mid; - uint32x4_t side; - int32x4_t left; - int32x4_t right; - mid = vshlq_u32(vld1q_u32(pInputSamples0U32 + i*4), wbpsShift0_4); - side = vshlq_u32(vld1q_u32(pInputSamples1U32 + i*4), wbpsShift1_4); - mid = vorrq_u32(vshlq_n_u32(mid, 1), vandq_u32(side, one4)); - left = vreinterpretq_s32_u32(vshlq_u32(vaddq_u32(mid, side), shift4)); - right = vreinterpretq_s32_u32(vshlq_u32(vsubq_u32(mid, side), shift4)); - ma_dr_flac__vst2q_s32(pOutputSamples + i*8, vzipq_s32(left, right)); - } - for (i = (frameCount4 << 2); i < frameCount; ++i) { - ma_uint32 mid = pInputSamples0U32[i] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; - ma_uint32 side = pInputSamples1U32[i] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; - mid = (mid << 1) | (side & 0x01); - pOutputSamples[i*2+0] = (ma_int32)((mid + side) << shift); - pOutputSamples[i*2+1] = (ma_int32)((mid - side) << shift); - } - } -} -#endif -static MA_INLINE void ma_dr_flac_read_pcm_frames_s32__decode_mid_side(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, ma_int32* pOutputSamples) -{ -#if defined(MA_DR_FLAC_SUPPORT_SSE2) - if (ma_dr_flac__gIsSSE2Supported && pFlac->bitsPerSample <= 24) { - ma_dr_flac_read_pcm_frames_s32__decode_mid_side__sse2(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); - } else -#elif defined(MA_DR_FLAC_SUPPORT_NEON) - if (ma_dr_flac__gIsNEONSupported && pFlac->bitsPerSample <= 24) { - ma_dr_flac_read_pcm_frames_s32__decode_mid_side__neon(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); - } else -#endif - { -#if 0 - ma_dr_flac_read_pcm_frames_s32__decode_mid_side__reference(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); -#else - ma_dr_flac_read_pcm_frames_s32__decode_mid_side__scalar(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); -#endif - } -} -#if 0 -static MA_INLINE void ma_dr_flac_read_pcm_frames_s32__decode_independent_stereo__reference(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, ma_int32* pOutputSamples) -{ - for (ma_uint64 i = 0; i < frameCount; ++i) { - pOutputSamples[i*2+0] = (ma_int32)((ma_uint32)pInputSamples0[i] << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample)); - pOutputSamples[i*2+1] = (ma_int32)((ma_uint32)pInputSamples1[i] << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample)); - } -} -#endif -static MA_INLINE void ma_dr_flac_read_pcm_frames_s32__decode_independent_stereo__scalar(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, ma_int32* pOutputSamples) -{ - ma_uint64 i; - ma_uint64 frameCount4 = frameCount >> 2; - const ma_uint32* pInputSamples0U32 = (const ma_uint32*)pInputSamples0; - const ma_uint32* pInputSamples1U32 = (const ma_uint32*)pInputSamples1; - ma_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; - ma_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; - for (i = 0; i < frameCount4; ++i) { - ma_uint32 tempL0 = pInputSamples0U32[i*4+0] << shift0; - ma_uint32 tempL1 = pInputSamples0U32[i*4+1] << shift0; - ma_uint32 tempL2 = pInputSamples0U32[i*4+2] << shift0; - ma_uint32 tempL3 = pInputSamples0U32[i*4+3] << shift0; - ma_uint32 tempR0 = pInputSamples1U32[i*4+0] << shift1; - ma_uint32 tempR1 = pInputSamples1U32[i*4+1] << shift1; - ma_uint32 tempR2 = pInputSamples1U32[i*4+2] << shift1; - ma_uint32 tempR3 = pInputSamples1U32[i*4+3] << shift1; - pOutputSamples[i*8+0] = (ma_int32)tempL0; - pOutputSamples[i*8+1] = (ma_int32)tempR0; - pOutputSamples[i*8+2] = (ma_int32)tempL1; - pOutputSamples[i*8+3] = (ma_int32)tempR1; - pOutputSamples[i*8+4] = (ma_int32)tempL2; - pOutputSamples[i*8+5] = (ma_int32)tempR2; - pOutputSamples[i*8+6] = (ma_int32)tempL3; - pOutputSamples[i*8+7] = (ma_int32)tempR3; - } - for (i = (frameCount4 << 2); i < frameCount; ++i) { - pOutputSamples[i*2+0] = (ma_int32)(pInputSamples0U32[i] << shift0); - pOutputSamples[i*2+1] = (ma_int32)(pInputSamples1U32[i] << shift1); - } -} -#if defined(MA_DR_FLAC_SUPPORT_SSE2) -static MA_INLINE void ma_dr_flac_read_pcm_frames_s32__decode_independent_stereo__sse2(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, ma_int32* pOutputSamples) -{ - ma_uint64 i; - ma_uint64 frameCount4 = frameCount >> 2; - const ma_uint32* pInputSamples0U32 = (const ma_uint32*)pInputSamples0; - const ma_uint32* pInputSamples1U32 = (const ma_uint32*)pInputSamples1; - ma_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; - ma_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; - for (i = 0; i < frameCount4; ++i) { - __m128i left = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples0 + i), shift0); - __m128i right = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples1 + i), shift1); - _mm_storeu_si128((__m128i*)(pOutputSamples + i*8 + 0), _mm_unpacklo_epi32(left, right)); - _mm_storeu_si128((__m128i*)(pOutputSamples + i*8 + 4), _mm_unpackhi_epi32(left, right)); - } - for (i = (frameCount4 << 2); i < frameCount; ++i) { - pOutputSamples[i*2+0] = (ma_int32)(pInputSamples0U32[i] << shift0); - pOutputSamples[i*2+1] = (ma_int32)(pInputSamples1U32[i] << shift1); - } -} -#endif -#if defined(MA_DR_FLAC_SUPPORT_NEON) -static MA_INLINE void ma_dr_flac_read_pcm_frames_s32__decode_independent_stereo__neon(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, ma_int32* pOutputSamples) -{ - ma_uint64 i; - ma_uint64 frameCount4 = frameCount >> 2; - const ma_uint32* pInputSamples0U32 = (const ma_uint32*)pInputSamples0; - const ma_uint32* pInputSamples1U32 = (const ma_uint32*)pInputSamples1; - ma_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; - ma_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; - int32x4_t shift4_0 = vdupq_n_s32(shift0); - int32x4_t shift4_1 = vdupq_n_s32(shift1); - for (i = 0; i < frameCount4; ++i) { - int32x4_t left; - int32x4_t right; - left = vreinterpretq_s32_u32(vshlq_u32(vld1q_u32(pInputSamples0U32 + i*4), shift4_0)); - right = vreinterpretq_s32_u32(vshlq_u32(vld1q_u32(pInputSamples1U32 + i*4), shift4_1)); - ma_dr_flac__vst2q_s32(pOutputSamples + i*8, vzipq_s32(left, right)); - } - for (i = (frameCount4 << 2); i < frameCount; ++i) { - pOutputSamples[i*2+0] = (ma_int32)(pInputSamples0U32[i] << shift0); - pOutputSamples[i*2+1] = (ma_int32)(pInputSamples1U32[i] << shift1); - } -} -#endif -static MA_INLINE void ma_dr_flac_read_pcm_frames_s32__decode_independent_stereo(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, ma_int32* pOutputSamples) -{ -#if defined(MA_DR_FLAC_SUPPORT_SSE2) - if (ma_dr_flac__gIsSSE2Supported && pFlac->bitsPerSample <= 24) { - ma_dr_flac_read_pcm_frames_s32__decode_independent_stereo__sse2(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); - } else -#elif defined(MA_DR_FLAC_SUPPORT_NEON) - if (ma_dr_flac__gIsNEONSupported && pFlac->bitsPerSample <= 24) { - ma_dr_flac_read_pcm_frames_s32__decode_independent_stereo__neon(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); - } else -#endif - { -#if 0 - ma_dr_flac_read_pcm_frames_s32__decode_independent_stereo__reference(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); -#else - ma_dr_flac_read_pcm_frames_s32__decode_independent_stereo__scalar(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); -#endif - } -} -MA_API ma_uint64 ma_dr_flac_read_pcm_frames_s32(ma_dr_flac* pFlac, ma_uint64 framesToRead, ma_int32* pBufferOut) -{ - ma_uint64 framesRead; - ma_uint32 unusedBitsPerSample; - if (pFlac == NULL || framesToRead == 0) { - return 0; - } - if (pBufferOut == NULL) { - return ma_dr_flac__seek_forward_by_pcm_frames(pFlac, framesToRead); - } - MA_DR_FLAC_ASSERT(pFlac->bitsPerSample <= 32); - unusedBitsPerSample = 32 - pFlac->bitsPerSample; - framesRead = 0; - while (framesToRead > 0) { - if (pFlac->currentFLACFrame.pcmFramesRemaining == 0) { - if (!ma_dr_flac__read_and_decode_next_flac_frame(pFlac)) { - break; - } - } else { - unsigned int channelCount = ma_dr_flac__get_channel_count_from_channel_assignment(pFlac->currentFLACFrame.header.channelAssignment); - ma_uint64 iFirstPCMFrame = pFlac->currentFLACFrame.header.blockSizeInPCMFrames - pFlac->currentFLACFrame.pcmFramesRemaining; - ma_uint64 frameCountThisIteration = framesToRead; - if (frameCountThisIteration > pFlac->currentFLACFrame.pcmFramesRemaining) { - frameCountThisIteration = pFlac->currentFLACFrame.pcmFramesRemaining; - } - if (channelCount == 2) { - const ma_int32* pDecodedSamples0 = pFlac->currentFLACFrame.subframes[0].pSamplesS32 + iFirstPCMFrame; - const ma_int32* pDecodedSamples1 = pFlac->currentFLACFrame.subframes[1].pSamplesS32 + iFirstPCMFrame; - switch (pFlac->currentFLACFrame.header.channelAssignment) - { - case MA_DR_FLAC_CHANNEL_ASSIGNMENT_LEFT_SIDE: - { - ma_dr_flac_read_pcm_frames_s32__decode_left_side(pFlac, frameCountThisIteration, unusedBitsPerSample, pDecodedSamples0, pDecodedSamples1, pBufferOut); - } break; - case MA_DR_FLAC_CHANNEL_ASSIGNMENT_RIGHT_SIDE: - { - ma_dr_flac_read_pcm_frames_s32__decode_right_side(pFlac, frameCountThisIteration, unusedBitsPerSample, pDecodedSamples0, pDecodedSamples1, pBufferOut); - } break; - case MA_DR_FLAC_CHANNEL_ASSIGNMENT_MID_SIDE: - { - ma_dr_flac_read_pcm_frames_s32__decode_mid_side(pFlac, frameCountThisIteration, unusedBitsPerSample, pDecodedSamples0, pDecodedSamples1, pBufferOut); - } break; - case MA_DR_FLAC_CHANNEL_ASSIGNMENT_INDEPENDENT: - default: - { - ma_dr_flac_read_pcm_frames_s32__decode_independent_stereo(pFlac, frameCountThisIteration, unusedBitsPerSample, pDecodedSamples0, pDecodedSamples1, pBufferOut); - } break; - } - } else { - ma_uint64 i; - for (i = 0; i < frameCountThisIteration; ++i) { - unsigned int j; - for (j = 0; j < channelCount; ++j) { - pBufferOut[(i*channelCount)+j] = (ma_int32)((ma_uint32)(pFlac->currentFLACFrame.subframes[j].pSamplesS32[iFirstPCMFrame + i]) << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[j].wastedBitsPerSample)); - } - } - } - framesRead += frameCountThisIteration; - pBufferOut += frameCountThisIteration * channelCount; - framesToRead -= frameCountThisIteration; - pFlac->currentPCMFrame += frameCountThisIteration; - pFlac->currentFLACFrame.pcmFramesRemaining -= (ma_uint32)frameCountThisIteration; - } - } - return framesRead; -} -#if 0 -static MA_INLINE void ma_dr_flac_read_pcm_frames_s16__decode_left_side__reference(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, ma_int16* pOutputSamples) -{ - ma_uint64 i; - for (i = 0; i < frameCount; ++i) { - ma_uint32 left = (ma_uint32)pInputSamples0[i] << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample); - ma_uint32 side = (ma_uint32)pInputSamples1[i] << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample); - ma_uint32 right = left - side; - left >>= 16; - right >>= 16; - pOutputSamples[i*2+0] = (ma_int16)left; - pOutputSamples[i*2+1] = (ma_int16)right; - } -} -#endif -static MA_INLINE void ma_dr_flac_read_pcm_frames_s16__decode_left_side__scalar(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, ma_int16* pOutputSamples) -{ - ma_uint64 i; - ma_uint64 frameCount4 = frameCount >> 2; - const ma_uint32* pInputSamples0U32 = (const ma_uint32*)pInputSamples0; - const ma_uint32* pInputSamples1U32 = (const ma_uint32*)pInputSamples1; - ma_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; - ma_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; - for (i = 0; i < frameCount4; ++i) { - ma_uint32 left0 = pInputSamples0U32[i*4+0] << shift0; - ma_uint32 left1 = pInputSamples0U32[i*4+1] << shift0; - ma_uint32 left2 = pInputSamples0U32[i*4+2] << shift0; - ma_uint32 left3 = pInputSamples0U32[i*4+3] << shift0; - ma_uint32 side0 = pInputSamples1U32[i*4+0] << shift1; - ma_uint32 side1 = pInputSamples1U32[i*4+1] << shift1; - ma_uint32 side2 = pInputSamples1U32[i*4+2] << shift1; - ma_uint32 side3 = pInputSamples1U32[i*4+3] << shift1; - ma_uint32 right0 = left0 - side0; - ma_uint32 right1 = left1 - side1; - ma_uint32 right2 = left2 - side2; - ma_uint32 right3 = left3 - side3; - left0 >>= 16; - left1 >>= 16; - left2 >>= 16; - left3 >>= 16; - right0 >>= 16; - right1 >>= 16; - right2 >>= 16; - right3 >>= 16; - pOutputSamples[i*8+0] = (ma_int16)left0; - pOutputSamples[i*8+1] = (ma_int16)right0; - pOutputSamples[i*8+2] = (ma_int16)left1; - pOutputSamples[i*8+3] = (ma_int16)right1; - pOutputSamples[i*8+4] = (ma_int16)left2; - pOutputSamples[i*8+5] = (ma_int16)right2; - pOutputSamples[i*8+6] = (ma_int16)left3; - pOutputSamples[i*8+7] = (ma_int16)right3; - } - for (i = (frameCount4 << 2); i < frameCount; ++i) { - ma_uint32 left = pInputSamples0U32[i] << shift0; - ma_uint32 side = pInputSamples1U32[i] << shift1; - ma_uint32 right = left - side; - left >>= 16; - right >>= 16; - pOutputSamples[i*2+0] = (ma_int16)left; - pOutputSamples[i*2+1] = (ma_int16)right; - } -} -#if defined(MA_DR_FLAC_SUPPORT_SSE2) -static MA_INLINE void ma_dr_flac_read_pcm_frames_s16__decode_left_side__sse2(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, ma_int16* pOutputSamples) -{ - ma_uint64 i; - ma_uint64 frameCount4 = frameCount >> 2; - const ma_uint32* pInputSamples0U32 = (const ma_uint32*)pInputSamples0; - const ma_uint32* pInputSamples1U32 = (const ma_uint32*)pInputSamples1; - ma_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; - ma_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; - MA_DR_FLAC_ASSERT(pFlac->bitsPerSample <= 24); - for (i = 0; i < frameCount4; ++i) { - __m128i left = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples0 + i), shift0); - __m128i side = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples1 + i), shift1); - __m128i right = _mm_sub_epi32(left, side); - left = _mm_srai_epi32(left, 16); - right = _mm_srai_epi32(right, 16); - _mm_storeu_si128((__m128i*)(pOutputSamples + i*8), ma_dr_flac__mm_packs_interleaved_epi32(left, right)); - } - for (i = (frameCount4 << 2); i < frameCount; ++i) { - ma_uint32 left = pInputSamples0U32[i] << shift0; - ma_uint32 side = pInputSamples1U32[i] << shift1; - ma_uint32 right = left - side; - left >>= 16; - right >>= 16; - pOutputSamples[i*2+0] = (ma_int16)left; - pOutputSamples[i*2+1] = (ma_int16)right; - } -} -#endif -#if defined(MA_DR_FLAC_SUPPORT_NEON) -static MA_INLINE void ma_dr_flac_read_pcm_frames_s16__decode_left_side__neon(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, ma_int16* pOutputSamples) -{ - ma_uint64 i; - ma_uint64 frameCount4 = frameCount >> 2; - const ma_uint32* pInputSamples0U32 = (const ma_uint32*)pInputSamples0; - const ma_uint32* pInputSamples1U32 = (const ma_uint32*)pInputSamples1; - ma_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; - ma_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; - int32x4_t shift0_4; - int32x4_t shift1_4; - MA_DR_FLAC_ASSERT(pFlac->bitsPerSample <= 24); - shift0_4 = vdupq_n_s32(shift0); - shift1_4 = vdupq_n_s32(shift1); - for (i = 0; i < frameCount4; ++i) { - uint32x4_t left; - uint32x4_t side; - uint32x4_t right; - left = vshlq_u32(vld1q_u32(pInputSamples0U32 + i*4), shift0_4); - side = vshlq_u32(vld1q_u32(pInputSamples1U32 + i*4), shift1_4); - right = vsubq_u32(left, side); - left = vshrq_n_u32(left, 16); - right = vshrq_n_u32(right, 16); - ma_dr_flac__vst2q_u16((ma_uint16*)pOutputSamples + i*8, vzip_u16(vmovn_u32(left), vmovn_u32(right))); - } - for (i = (frameCount4 << 2); i < frameCount; ++i) { - ma_uint32 left = pInputSamples0U32[i] << shift0; - ma_uint32 side = pInputSamples1U32[i] << shift1; - ma_uint32 right = left - side; - left >>= 16; - right >>= 16; - pOutputSamples[i*2+0] = (ma_int16)left; - pOutputSamples[i*2+1] = (ma_int16)right; - } -} -#endif -static MA_INLINE void ma_dr_flac_read_pcm_frames_s16__decode_left_side(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, ma_int16* pOutputSamples) -{ -#if defined(MA_DR_FLAC_SUPPORT_SSE2) - if (ma_dr_flac__gIsSSE2Supported && pFlac->bitsPerSample <= 24) { - ma_dr_flac_read_pcm_frames_s16__decode_left_side__sse2(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); - } else -#elif defined(MA_DR_FLAC_SUPPORT_NEON) - if (ma_dr_flac__gIsNEONSupported && pFlac->bitsPerSample <= 24) { - ma_dr_flac_read_pcm_frames_s16__decode_left_side__neon(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); - } else -#endif - { -#if 0 - ma_dr_flac_read_pcm_frames_s16__decode_left_side__reference(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); -#else - ma_dr_flac_read_pcm_frames_s16__decode_left_side__scalar(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); -#endif - } -} -#if 0 -static MA_INLINE void ma_dr_flac_read_pcm_frames_s16__decode_right_side__reference(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, ma_int16* pOutputSamples) -{ - ma_uint64 i; - for (i = 0; i < frameCount; ++i) { - ma_uint32 side = (ma_uint32)pInputSamples0[i] << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample); - ma_uint32 right = (ma_uint32)pInputSamples1[i] << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample); - ma_uint32 left = right + side; - left >>= 16; - right >>= 16; - pOutputSamples[i*2+0] = (ma_int16)left; - pOutputSamples[i*2+1] = (ma_int16)right; - } -} -#endif -static MA_INLINE void ma_dr_flac_read_pcm_frames_s16__decode_right_side__scalar(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, ma_int16* pOutputSamples) -{ - ma_uint64 i; - ma_uint64 frameCount4 = frameCount >> 2; - const ma_uint32* pInputSamples0U32 = (const ma_uint32*)pInputSamples0; - const ma_uint32* pInputSamples1U32 = (const ma_uint32*)pInputSamples1; - ma_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; - ma_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; - for (i = 0; i < frameCount4; ++i) { - ma_uint32 side0 = pInputSamples0U32[i*4+0] << shift0; - ma_uint32 side1 = pInputSamples0U32[i*4+1] << shift0; - ma_uint32 side2 = pInputSamples0U32[i*4+2] << shift0; - ma_uint32 side3 = pInputSamples0U32[i*4+3] << shift0; - ma_uint32 right0 = pInputSamples1U32[i*4+0] << shift1; - ma_uint32 right1 = pInputSamples1U32[i*4+1] << shift1; - ma_uint32 right2 = pInputSamples1U32[i*4+2] << shift1; - ma_uint32 right3 = pInputSamples1U32[i*4+3] << shift1; - ma_uint32 left0 = right0 + side0; - ma_uint32 left1 = right1 + side1; - ma_uint32 left2 = right2 + side2; - ma_uint32 left3 = right3 + side3; - left0 >>= 16; - left1 >>= 16; - left2 >>= 16; - left3 >>= 16; - right0 >>= 16; - right1 >>= 16; - right2 >>= 16; - right3 >>= 16; - pOutputSamples[i*8+0] = (ma_int16)left0; - pOutputSamples[i*8+1] = (ma_int16)right0; - pOutputSamples[i*8+2] = (ma_int16)left1; - pOutputSamples[i*8+3] = (ma_int16)right1; - pOutputSamples[i*8+4] = (ma_int16)left2; - pOutputSamples[i*8+5] = (ma_int16)right2; - pOutputSamples[i*8+6] = (ma_int16)left3; - pOutputSamples[i*8+7] = (ma_int16)right3; - } - for (i = (frameCount4 << 2); i < frameCount; ++i) { - ma_uint32 side = pInputSamples0U32[i] << shift0; - ma_uint32 right = pInputSamples1U32[i] << shift1; - ma_uint32 left = right + side; - left >>= 16; - right >>= 16; - pOutputSamples[i*2+0] = (ma_int16)left; - pOutputSamples[i*2+1] = (ma_int16)right; - } -} -#if defined(MA_DR_FLAC_SUPPORT_SSE2) -static MA_INLINE void ma_dr_flac_read_pcm_frames_s16__decode_right_side__sse2(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, ma_int16* pOutputSamples) -{ - ma_uint64 i; - ma_uint64 frameCount4 = frameCount >> 2; - const ma_uint32* pInputSamples0U32 = (const ma_uint32*)pInputSamples0; - const ma_uint32* pInputSamples1U32 = (const ma_uint32*)pInputSamples1; - ma_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; - ma_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; - MA_DR_FLAC_ASSERT(pFlac->bitsPerSample <= 24); - for (i = 0; i < frameCount4; ++i) { - __m128i side = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples0 + i), shift0); - __m128i right = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples1 + i), shift1); - __m128i left = _mm_add_epi32(right, side); - left = _mm_srai_epi32(left, 16); - right = _mm_srai_epi32(right, 16); - _mm_storeu_si128((__m128i*)(pOutputSamples + i*8), ma_dr_flac__mm_packs_interleaved_epi32(left, right)); - } - for (i = (frameCount4 << 2); i < frameCount; ++i) { - ma_uint32 side = pInputSamples0U32[i] << shift0; - ma_uint32 right = pInputSamples1U32[i] << shift1; - ma_uint32 left = right + side; - left >>= 16; - right >>= 16; - pOutputSamples[i*2+0] = (ma_int16)left; - pOutputSamples[i*2+1] = (ma_int16)right; - } -} -#endif -#if defined(MA_DR_FLAC_SUPPORT_NEON) -static MA_INLINE void ma_dr_flac_read_pcm_frames_s16__decode_right_side__neon(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, ma_int16* pOutputSamples) -{ - ma_uint64 i; - ma_uint64 frameCount4 = frameCount >> 2; - const ma_uint32* pInputSamples0U32 = (const ma_uint32*)pInputSamples0; - const ma_uint32* pInputSamples1U32 = (const ma_uint32*)pInputSamples1; - ma_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; - ma_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; - int32x4_t shift0_4; - int32x4_t shift1_4; - MA_DR_FLAC_ASSERT(pFlac->bitsPerSample <= 24); - shift0_4 = vdupq_n_s32(shift0); - shift1_4 = vdupq_n_s32(shift1); - for (i = 0; i < frameCount4; ++i) { - uint32x4_t side; - uint32x4_t right; - uint32x4_t left; - side = vshlq_u32(vld1q_u32(pInputSamples0U32 + i*4), shift0_4); - right = vshlq_u32(vld1q_u32(pInputSamples1U32 + i*4), shift1_4); - left = vaddq_u32(right, side); - left = vshrq_n_u32(left, 16); - right = vshrq_n_u32(right, 16); - ma_dr_flac__vst2q_u16((ma_uint16*)pOutputSamples + i*8, vzip_u16(vmovn_u32(left), vmovn_u32(right))); - } - for (i = (frameCount4 << 2); i < frameCount; ++i) { - ma_uint32 side = pInputSamples0U32[i] << shift0; - ma_uint32 right = pInputSamples1U32[i] << shift1; - ma_uint32 left = right + side; - left >>= 16; - right >>= 16; - pOutputSamples[i*2+0] = (ma_int16)left; - pOutputSamples[i*2+1] = (ma_int16)right; - } -} -#endif -static MA_INLINE void ma_dr_flac_read_pcm_frames_s16__decode_right_side(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, ma_int16* pOutputSamples) -{ -#if defined(MA_DR_FLAC_SUPPORT_SSE2) - if (ma_dr_flac__gIsSSE2Supported && pFlac->bitsPerSample <= 24) { - ma_dr_flac_read_pcm_frames_s16__decode_right_side__sse2(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); - } else -#elif defined(MA_DR_FLAC_SUPPORT_NEON) - if (ma_dr_flac__gIsNEONSupported && pFlac->bitsPerSample <= 24) { - ma_dr_flac_read_pcm_frames_s16__decode_right_side__neon(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); - } else -#endif - { -#if 0 - ma_dr_flac_read_pcm_frames_s16__decode_right_side__reference(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); -#else - ma_dr_flac_read_pcm_frames_s16__decode_right_side__scalar(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); -#endif - } -} -#if 0 -static MA_INLINE void ma_dr_flac_read_pcm_frames_s16__decode_mid_side__reference(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, ma_int16* pOutputSamples) -{ - for (ma_uint64 i = 0; i < frameCount; ++i) { - ma_uint32 mid = (ma_uint32)pInputSamples0[i] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; - ma_uint32 side = (ma_uint32)pInputSamples1[i] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; - mid = (mid << 1) | (side & 0x01); - pOutputSamples[i*2+0] = (ma_int16)(((ma_uint32)((ma_int32)(mid + side) >> 1) << unusedBitsPerSample) >> 16); - pOutputSamples[i*2+1] = (ma_int16)(((ma_uint32)((ma_int32)(mid - side) >> 1) << unusedBitsPerSample) >> 16); - } -} -#endif -static MA_INLINE void ma_dr_flac_read_pcm_frames_s16__decode_mid_side__scalar(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, ma_int16* pOutputSamples) -{ - ma_uint64 i; - ma_uint64 frameCount4 = frameCount >> 2; - const ma_uint32* pInputSamples0U32 = (const ma_uint32*)pInputSamples0; - const ma_uint32* pInputSamples1U32 = (const ma_uint32*)pInputSamples1; - ma_uint32 shift = unusedBitsPerSample; - if (shift > 0) { - shift -= 1; - for (i = 0; i < frameCount4; ++i) { - ma_uint32 temp0L; - ma_uint32 temp1L; - ma_uint32 temp2L; - ma_uint32 temp3L; - ma_uint32 temp0R; - ma_uint32 temp1R; - ma_uint32 temp2R; - ma_uint32 temp3R; - ma_uint32 mid0 = pInputSamples0U32[i*4+0] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; - ma_uint32 mid1 = pInputSamples0U32[i*4+1] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; - ma_uint32 mid2 = pInputSamples0U32[i*4+2] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; - ma_uint32 mid3 = pInputSamples0U32[i*4+3] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; - ma_uint32 side0 = pInputSamples1U32[i*4+0] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; - ma_uint32 side1 = pInputSamples1U32[i*4+1] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; - ma_uint32 side2 = pInputSamples1U32[i*4+2] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; - ma_uint32 side3 = pInputSamples1U32[i*4+3] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; - mid0 = (mid0 << 1) | (side0 & 0x01); - mid1 = (mid1 << 1) | (side1 & 0x01); - mid2 = (mid2 << 1) | (side2 & 0x01); - mid3 = (mid3 << 1) | (side3 & 0x01); - temp0L = (mid0 + side0) << shift; - temp1L = (mid1 + side1) << shift; - temp2L = (mid2 + side2) << shift; - temp3L = (mid3 + side3) << shift; - temp0R = (mid0 - side0) << shift; - temp1R = (mid1 - side1) << shift; - temp2R = (mid2 - side2) << shift; - temp3R = (mid3 - side3) << shift; - temp0L >>= 16; - temp1L >>= 16; - temp2L >>= 16; - temp3L >>= 16; - temp0R >>= 16; - temp1R >>= 16; - temp2R >>= 16; - temp3R >>= 16; - pOutputSamples[i*8+0] = (ma_int16)temp0L; - pOutputSamples[i*8+1] = (ma_int16)temp0R; - pOutputSamples[i*8+2] = (ma_int16)temp1L; - pOutputSamples[i*8+3] = (ma_int16)temp1R; - pOutputSamples[i*8+4] = (ma_int16)temp2L; - pOutputSamples[i*8+5] = (ma_int16)temp2R; - pOutputSamples[i*8+6] = (ma_int16)temp3L; - pOutputSamples[i*8+7] = (ma_int16)temp3R; - } - } else { - for (i = 0; i < frameCount4; ++i) { - ma_uint32 temp0L; - ma_uint32 temp1L; - ma_uint32 temp2L; - ma_uint32 temp3L; - ma_uint32 temp0R; - ma_uint32 temp1R; - ma_uint32 temp2R; - ma_uint32 temp3R; - ma_uint32 mid0 = pInputSamples0U32[i*4+0] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; - ma_uint32 mid1 = pInputSamples0U32[i*4+1] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; - ma_uint32 mid2 = pInputSamples0U32[i*4+2] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; - ma_uint32 mid3 = pInputSamples0U32[i*4+3] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; - ma_uint32 side0 = pInputSamples1U32[i*4+0] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; - ma_uint32 side1 = pInputSamples1U32[i*4+1] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; - ma_uint32 side2 = pInputSamples1U32[i*4+2] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; - ma_uint32 side3 = pInputSamples1U32[i*4+3] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; - mid0 = (mid0 << 1) | (side0 & 0x01); - mid1 = (mid1 << 1) | (side1 & 0x01); - mid2 = (mid2 << 1) | (side2 & 0x01); - mid3 = (mid3 << 1) | (side3 & 0x01); - temp0L = ((ma_int32)(mid0 + side0) >> 1); - temp1L = ((ma_int32)(mid1 + side1) >> 1); - temp2L = ((ma_int32)(mid2 + side2) >> 1); - temp3L = ((ma_int32)(mid3 + side3) >> 1); - temp0R = ((ma_int32)(mid0 - side0) >> 1); - temp1R = ((ma_int32)(mid1 - side1) >> 1); - temp2R = ((ma_int32)(mid2 - side2) >> 1); - temp3R = ((ma_int32)(mid3 - side3) >> 1); - temp0L >>= 16; - temp1L >>= 16; - temp2L >>= 16; - temp3L >>= 16; - temp0R >>= 16; - temp1R >>= 16; - temp2R >>= 16; - temp3R >>= 16; - pOutputSamples[i*8+0] = (ma_int16)temp0L; - pOutputSamples[i*8+1] = (ma_int16)temp0R; - pOutputSamples[i*8+2] = (ma_int16)temp1L; - pOutputSamples[i*8+3] = (ma_int16)temp1R; - pOutputSamples[i*8+4] = (ma_int16)temp2L; - pOutputSamples[i*8+5] = (ma_int16)temp2R; - pOutputSamples[i*8+6] = (ma_int16)temp3L; - pOutputSamples[i*8+7] = (ma_int16)temp3R; - } - } - for (i = (frameCount4 << 2); i < frameCount; ++i) { - ma_uint32 mid = pInputSamples0U32[i] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; - ma_uint32 side = pInputSamples1U32[i] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; - mid = (mid << 1) | (side & 0x01); - pOutputSamples[i*2+0] = (ma_int16)(((ma_uint32)((ma_int32)(mid + side) >> 1) << unusedBitsPerSample) >> 16); - pOutputSamples[i*2+1] = (ma_int16)(((ma_uint32)((ma_int32)(mid - side) >> 1) << unusedBitsPerSample) >> 16); - } -} -#if defined(MA_DR_FLAC_SUPPORT_SSE2) -static MA_INLINE void ma_dr_flac_read_pcm_frames_s16__decode_mid_side__sse2(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, ma_int16* pOutputSamples) -{ - ma_uint64 i; - ma_uint64 frameCount4 = frameCount >> 2; - const ma_uint32* pInputSamples0U32 = (const ma_uint32*)pInputSamples0; - const ma_uint32* pInputSamples1U32 = (const ma_uint32*)pInputSamples1; - ma_uint32 shift = unusedBitsPerSample; - MA_DR_FLAC_ASSERT(pFlac->bitsPerSample <= 24); - if (shift == 0) { - for (i = 0; i < frameCount4; ++i) { - __m128i mid; - __m128i side; - __m128i left; - __m128i right; - mid = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples0 + i), pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample); - side = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples1 + i), pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample); - mid = _mm_or_si128(_mm_slli_epi32(mid, 1), _mm_and_si128(side, _mm_set1_epi32(0x01))); - left = _mm_srai_epi32(_mm_add_epi32(mid, side), 1); - right = _mm_srai_epi32(_mm_sub_epi32(mid, side), 1); - left = _mm_srai_epi32(left, 16); - right = _mm_srai_epi32(right, 16); - _mm_storeu_si128((__m128i*)(pOutputSamples + i*8), ma_dr_flac__mm_packs_interleaved_epi32(left, right)); - } - for (i = (frameCount4 << 2); i < frameCount; ++i) { - ma_uint32 mid = pInputSamples0U32[i] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; - ma_uint32 side = pInputSamples1U32[i] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; - mid = (mid << 1) | (side & 0x01); - pOutputSamples[i*2+0] = (ma_int16)(((ma_int32)(mid + side) >> 1) >> 16); - pOutputSamples[i*2+1] = (ma_int16)(((ma_int32)(mid - side) >> 1) >> 16); - } - } else { - shift -= 1; - for (i = 0; i < frameCount4; ++i) { - __m128i mid; - __m128i side; - __m128i left; - __m128i right; - mid = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples0 + i), pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample); - side = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples1 + i), pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample); - mid = _mm_or_si128(_mm_slli_epi32(mid, 1), _mm_and_si128(side, _mm_set1_epi32(0x01))); - left = _mm_slli_epi32(_mm_add_epi32(mid, side), shift); - right = _mm_slli_epi32(_mm_sub_epi32(mid, side), shift); - left = _mm_srai_epi32(left, 16); - right = _mm_srai_epi32(right, 16); - _mm_storeu_si128((__m128i*)(pOutputSamples + i*8), ma_dr_flac__mm_packs_interleaved_epi32(left, right)); - } - for (i = (frameCount4 << 2); i < frameCount; ++i) { - ma_uint32 mid = pInputSamples0U32[i] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; - ma_uint32 side = pInputSamples1U32[i] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; - mid = (mid << 1) | (side & 0x01); - pOutputSamples[i*2+0] = (ma_int16)(((mid + side) << shift) >> 16); - pOutputSamples[i*2+1] = (ma_int16)(((mid - side) << shift) >> 16); - } - } -} -#endif -#if defined(MA_DR_FLAC_SUPPORT_NEON) -static MA_INLINE void ma_dr_flac_read_pcm_frames_s16__decode_mid_side__neon(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, ma_int16* pOutputSamples) -{ - ma_uint64 i; - ma_uint64 frameCount4 = frameCount >> 2; - const ma_uint32* pInputSamples0U32 = (const ma_uint32*)pInputSamples0; - const ma_uint32* pInputSamples1U32 = (const ma_uint32*)pInputSamples1; - ma_uint32 shift = unusedBitsPerSample; - int32x4_t wbpsShift0_4; - int32x4_t wbpsShift1_4; - MA_DR_FLAC_ASSERT(pFlac->bitsPerSample <= 24); - wbpsShift0_4 = vdupq_n_s32(pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample); - wbpsShift1_4 = vdupq_n_s32(pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample); - if (shift == 0) { - for (i = 0; i < frameCount4; ++i) { - uint32x4_t mid; - uint32x4_t side; - int32x4_t left; - int32x4_t right; - mid = vshlq_u32(vld1q_u32(pInputSamples0U32 + i*4), wbpsShift0_4); - side = vshlq_u32(vld1q_u32(pInputSamples1U32 + i*4), wbpsShift1_4); - mid = vorrq_u32(vshlq_n_u32(mid, 1), vandq_u32(side, vdupq_n_u32(1))); - left = vshrq_n_s32(vreinterpretq_s32_u32(vaddq_u32(mid, side)), 1); - right = vshrq_n_s32(vreinterpretq_s32_u32(vsubq_u32(mid, side)), 1); - left = vshrq_n_s32(left, 16); - right = vshrq_n_s32(right, 16); - ma_dr_flac__vst2q_s16(pOutputSamples + i*8, vzip_s16(vmovn_s32(left), vmovn_s32(right))); - } - for (i = (frameCount4 << 2); i < frameCount; ++i) { - ma_uint32 mid = pInputSamples0U32[i] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; - ma_uint32 side = pInputSamples1U32[i] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; - mid = (mid << 1) | (side & 0x01); - pOutputSamples[i*2+0] = (ma_int16)(((ma_int32)(mid + side) >> 1) >> 16); - pOutputSamples[i*2+1] = (ma_int16)(((ma_int32)(mid - side) >> 1) >> 16); - } - } else { - int32x4_t shift4; - shift -= 1; - shift4 = vdupq_n_s32(shift); - for (i = 0; i < frameCount4; ++i) { - uint32x4_t mid; - uint32x4_t side; - int32x4_t left; - int32x4_t right; - mid = vshlq_u32(vld1q_u32(pInputSamples0U32 + i*4), wbpsShift0_4); - side = vshlq_u32(vld1q_u32(pInputSamples1U32 + i*4), wbpsShift1_4); - mid = vorrq_u32(vshlq_n_u32(mid, 1), vandq_u32(side, vdupq_n_u32(1))); - left = vreinterpretq_s32_u32(vshlq_u32(vaddq_u32(mid, side), shift4)); - right = vreinterpretq_s32_u32(vshlq_u32(vsubq_u32(mid, side), shift4)); - left = vshrq_n_s32(left, 16); - right = vshrq_n_s32(right, 16); - ma_dr_flac__vst2q_s16(pOutputSamples + i*8, vzip_s16(vmovn_s32(left), vmovn_s32(right))); - } - for (i = (frameCount4 << 2); i < frameCount; ++i) { - ma_uint32 mid = pInputSamples0U32[i] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; - ma_uint32 side = pInputSamples1U32[i] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; - mid = (mid << 1) | (side & 0x01); - pOutputSamples[i*2+0] = (ma_int16)(((mid + side) << shift) >> 16); - pOutputSamples[i*2+1] = (ma_int16)(((mid - side) << shift) >> 16); - } - } -} -#endif -static MA_INLINE void ma_dr_flac_read_pcm_frames_s16__decode_mid_side(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, ma_int16* pOutputSamples) -{ -#if defined(MA_DR_FLAC_SUPPORT_SSE2) - if (ma_dr_flac__gIsSSE2Supported && pFlac->bitsPerSample <= 24) { - ma_dr_flac_read_pcm_frames_s16__decode_mid_side__sse2(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); - } else -#elif defined(MA_DR_FLAC_SUPPORT_NEON) - if (ma_dr_flac__gIsNEONSupported && pFlac->bitsPerSample <= 24) { - ma_dr_flac_read_pcm_frames_s16__decode_mid_side__neon(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); - } else -#endif - { -#if 0 - ma_dr_flac_read_pcm_frames_s16__decode_mid_side__reference(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); -#else - ma_dr_flac_read_pcm_frames_s16__decode_mid_side__scalar(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); -#endif - } -} -#if 0 -static MA_INLINE void ma_dr_flac_read_pcm_frames_s16__decode_independent_stereo__reference(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, ma_int16* pOutputSamples) -{ - for (ma_uint64 i = 0; i < frameCount; ++i) { - pOutputSamples[i*2+0] = (ma_int16)((ma_int32)((ma_uint32)pInputSamples0[i] << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample)) >> 16); - pOutputSamples[i*2+1] = (ma_int16)((ma_int32)((ma_uint32)pInputSamples1[i] << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample)) >> 16); - } -} -#endif -static MA_INLINE void ma_dr_flac_read_pcm_frames_s16__decode_independent_stereo__scalar(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, ma_int16* pOutputSamples) -{ - ma_uint64 i; - ma_uint64 frameCount4 = frameCount >> 2; - const ma_uint32* pInputSamples0U32 = (const ma_uint32*)pInputSamples0; - const ma_uint32* pInputSamples1U32 = (const ma_uint32*)pInputSamples1; - ma_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; - ma_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; - for (i = 0; i < frameCount4; ++i) { - ma_uint32 tempL0 = pInputSamples0U32[i*4+0] << shift0; - ma_uint32 tempL1 = pInputSamples0U32[i*4+1] << shift0; - ma_uint32 tempL2 = pInputSamples0U32[i*4+2] << shift0; - ma_uint32 tempL3 = pInputSamples0U32[i*4+3] << shift0; - ma_uint32 tempR0 = pInputSamples1U32[i*4+0] << shift1; - ma_uint32 tempR1 = pInputSamples1U32[i*4+1] << shift1; - ma_uint32 tempR2 = pInputSamples1U32[i*4+2] << shift1; - ma_uint32 tempR3 = pInputSamples1U32[i*4+3] << shift1; - tempL0 >>= 16; - tempL1 >>= 16; - tempL2 >>= 16; - tempL3 >>= 16; - tempR0 >>= 16; - tempR1 >>= 16; - tempR2 >>= 16; - tempR3 >>= 16; - pOutputSamples[i*8+0] = (ma_int16)tempL0; - pOutputSamples[i*8+1] = (ma_int16)tempR0; - pOutputSamples[i*8+2] = (ma_int16)tempL1; - pOutputSamples[i*8+3] = (ma_int16)tempR1; - pOutputSamples[i*8+4] = (ma_int16)tempL2; - pOutputSamples[i*8+5] = (ma_int16)tempR2; - pOutputSamples[i*8+6] = (ma_int16)tempL3; - pOutputSamples[i*8+7] = (ma_int16)tempR3; - } - for (i = (frameCount4 << 2); i < frameCount; ++i) { - pOutputSamples[i*2+0] = (ma_int16)((pInputSamples0U32[i] << shift0) >> 16); - pOutputSamples[i*2+1] = (ma_int16)((pInputSamples1U32[i] << shift1) >> 16); - } -} -#if defined(MA_DR_FLAC_SUPPORT_SSE2) -static MA_INLINE void ma_dr_flac_read_pcm_frames_s16__decode_independent_stereo__sse2(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, ma_int16* pOutputSamples) -{ - ma_uint64 i; - ma_uint64 frameCount4 = frameCount >> 2; - const ma_uint32* pInputSamples0U32 = (const ma_uint32*)pInputSamples0; - const ma_uint32* pInputSamples1U32 = (const ma_uint32*)pInputSamples1; - ma_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; - ma_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; - for (i = 0; i < frameCount4; ++i) { - __m128i left = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples0 + i), shift0); - __m128i right = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples1 + i), shift1); - left = _mm_srai_epi32(left, 16); - right = _mm_srai_epi32(right, 16); - _mm_storeu_si128((__m128i*)(pOutputSamples + i*8), ma_dr_flac__mm_packs_interleaved_epi32(left, right)); - } - for (i = (frameCount4 << 2); i < frameCount; ++i) { - pOutputSamples[i*2+0] = (ma_int16)((pInputSamples0U32[i] << shift0) >> 16); - pOutputSamples[i*2+1] = (ma_int16)((pInputSamples1U32[i] << shift1) >> 16); - } -} -#endif -#if defined(MA_DR_FLAC_SUPPORT_NEON) -static MA_INLINE void ma_dr_flac_read_pcm_frames_s16__decode_independent_stereo__neon(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, ma_int16* pOutputSamples) -{ - ma_uint64 i; - ma_uint64 frameCount4 = frameCount >> 2; - const ma_uint32* pInputSamples0U32 = (const ma_uint32*)pInputSamples0; - const ma_uint32* pInputSamples1U32 = (const ma_uint32*)pInputSamples1; - ma_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; - ma_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; - int32x4_t shift0_4 = vdupq_n_s32(shift0); - int32x4_t shift1_4 = vdupq_n_s32(shift1); - for (i = 0; i < frameCount4; ++i) { - int32x4_t left; - int32x4_t right; - left = vreinterpretq_s32_u32(vshlq_u32(vld1q_u32(pInputSamples0U32 + i*4), shift0_4)); - right = vreinterpretq_s32_u32(vshlq_u32(vld1q_u32(pInputSamples1U32 + i*4), shift1_4)); - left = vshrq_n_s32(left, 16); - right = vshrq_n_s32(right, 16); - ma_dr_flac__vst2q_s16(pOutputSamples + i*8, vzip_s16(vmovn_s32(left), vmovn_s32(right))); - } - for (i = (frameCount4 << 2); i < frameCount; ++i) { - pOutputSamples[i*2+0] = (ma_int16)((pInputSamples0U32[i] << shift0) >> 16); - pOutputSamples[i*2+1] = (ma_int16)((pInputSamples1U32[i] << shift1) >> 16); - } -} -#endif -static MA_INLINE void ma_dr_flac_read_pcm_frames_s16__decode_independent_stereo(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, ma_int16* pOutputSamples) -{ -#if defined(MA_DR_FLAC_SUPPORT_SSE2) - if (ma_dr_flac__gIsSSE2Supported && pFlac->bitsPerSample <= 24) { - ma_dr_flac_read_pcm_frames_s16__decode_independent_stereo__sse2(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); - } else -#elif defined(MA_DR_FLAC_SUPPORT_NEON) - if (ma_dr_flac__gIsNEONSupported && pFlac->bitsPerSample <= 24) { - ma_dr_flac_read_pcm_frames_s16__decode_independent_stereo__neon(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); - } else -#endif - { -#if 0 - ma_dr_flac_read_pcm_frames_s16__decode_independent_stereo__reference(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); -#else - ma_dr_flac_read_pcm_frames_s16__decode_independent_stereo__scalar(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); -#endif - } -} -MA_API ma_uint64 ma_dr_flac_read_pcm_frames_s16(ma_dr_flac* pFlac, ma_uint64 framesToRead, ma_int16* pBufferOut) -{ - ma_uint64 framesRead; - ma_uint32 unusedBitsPerSample; - if (pFlac == NULL || framesToRead == 0) { - return 0; - } - if (pBufferOut == NULL) { - return ma_dr_flac__seek_forward_by_pcm_frames(pFlac, framesToRead); - } - MA_DR_FLAC_ASSERT(pFlac->bitsPerSample <= 32); - unusedBitsPerSample = 32 - pFlac->bitsPerSample; - framesRead = 0; - while (framesToRead > 0) { - if (pFlac->currentFLACFrame.pcmFramesRemaining == 0) { - if (!ma_dr_flac__read_and_decode_next_flac_frame(pFlac)) { - break; - } - } else { - unsigned int channelCount = ma_dr_flac__get_channel_count_from_channel_assignment(pFlac->currentFLACFrame.header.channelAssignment); - ma_uint64 iFirstPCMFrame = pFlac->currentFLACFrame.header.blockSizeInPCMFrames - pFlac->currentFLACFrame.pcmFramesRemaining; - ma_uint64 frameCountThisIteration = framesToRead; - if (frameCountThisIteration > pFlac->currentFLACFrame.pcmFramesRemaining) { - frameCountThisIteration = pFlac->currentFLACFrame.pcmFramesRemaining; - } - if (channelCount == 2) { - const ma_int32* pDecodedSamples0 = pFlac->currentFLACFrame.subframes[0].pSamplesS32 + iFirstPCMFrame; - const ma_int32* pDecodedSamples1 = pFlac->currentFLACFrame.subframes[1].pSamplesS32 + iFirstPCMFrame; - switch (pFlac->currentFLACFrame.header.channelAssignment) - { - case MA_DR_FLAC_CHANNEL_ASSIGNMENT_LEFT_SIDE: - { - ma_dr_flac_read_pcm_frames_s16__decode_left_side(pFlac, frameCountThisIteration, unusedBitsPerSample, pDecodedSamples0, pDecodedSamples1, pBufferOut); - } break; - case MA_DR_FLAC_CHANNEL_ASSIGNMENT_RIGHT_SIDE: - { - ma_dr_flac_read_pcm_frames_s16__decode_right_side(pFlac, frameCountThisIteration, unusedBitsPerSample, pDecodedSamples0, pDecodedSamples1, pBufferOut); - } break; - case MA_DR_FLAC_CHANNEL_ASSIGNMENT_MID_SIDE: - { - ma_dr_flac_read_pcm_frames_s16__decode_mid_side(pFlac, frameCountThisIteration, unusedBitsPerSample, pDecodedSamples0, pDecodedSamples1, pBufferOut); - } break; - case MA_DR_FLAC_CHANNEL_ASSIGNMENT_INDEPENDENT: - default: - { - ma_dr_flac_read_pcm_frames_s16__decode_independent_stereo(pFlac, frameCountThisIteration, unusedBitsPerSample, pDecodedSamples0, pDecodedSamples1, pBufferOut); - } break; - } - } else { - ma_uint64 i; - for (i = 0; i < frameCountThisIteration; ++i) { - unsigned int j; - for (j = 0; j < channelCount; ++j) { - ma_int32 sampleS32 = (ma_int32)((ma_uint32)(pFlac->currentFLACFrame.subframes[j].pSamplesS32[iFirstPCMFrame + i]) << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[j].wastedBitsPerSample)); - pBufferOut[(i*channelCount)+j] = (ma_int16)(sampleS32 >> 16); - } - } - } - framesRead += frameCountThisIteration; - pBufferOut += frameCountThisIteration * channelCount; - framesToRead -= frameCountThisIteration; - pFlac->currentPCMFrame += frameCountThisIteration; - pFlac->currentFLACFrame.pcmFramesRemaining -= (ma_uint32)frameCountThisIteration; - } - } - return framesRead; -} -#if 0 -static MA_INLINE void ma_dr_flac_read_pcm_frames_f32__decode_left_side__reference(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, float* pOutputSamples) -{ - ma_uint64 i; - for (i = 0; i < frameCount; ++i) { - ma_uint32 left = (ma_uint32)pInputSamples0[i] << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample); - ma_uint32 side = (ma_uint32)pInputSamples1[i] << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample); - ma_uint32 right = left - side; - pOutputSamples[i*2+0] = (float)((ma_int32)left / 2147483648.0); - pOutputSamples[i*2+1] = (float)((ma_int32)right / 2147483648.0); - } -} -#endif -static MA_INLINE void ma_dr_flac_read_pcm_frames_f32__decode_left_side__scalar(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, float* pOutputSamples) -{ - ma_uint64 i; - ma_uint64 frameCount4 = frameCount >> 2; - const ma_uint32* pInputSamples0U32 = (const ma_uint32*)pInputSamples0; - const ma_uint32* pInputSamples1U32 = (const ma_uint32*)pInputSamples1; - ma_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; - ma_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; - float factor = 1 / 2147483648.0; - for (i = 0; i < frameCount4; ++i) { - ma_uint32 left0 = pInputSamples0U32[i*4+0] << shift0; - ma_uint32 left1 = pInputSamples0U32[i*4+1] << shift0; - ma_uint32 left2 = pInputSamples0U32[i*4+2] << shift0; - ma_uint32 left3 = pInputSamples0U32[i*4+3] << shift0; - ma_uint32 side0 = pInputSamples1U32[i*4+0] << shift1; - ma_uint32 side1 = pInputSamples1U32[i*4+1] << shift1; - ma_uint32 side2 = pInputSamples1U32[i*4+2] << shift1; - ma_uint32 side3 = pInputSamples1U32[i*4+3] << shift1; - ma_uint32 right0 = left0 - side0; - ma_uint32 right1 = left1 - side1; - ma_uint32 right2 = left2 - side2; - ma_uint32 right3 = left3 - side3; - pOutputSamples[i*8+0] = (ma_int32)left0 * factor; - pOutputSamples[i*8+1] = (ma_int32)right0 * factor; - pOutputSamples[i*8+2] = (ma_int32)left1 * factor; - pOutputSamples[i*8+3] = (ma_int32)right1 * factor; - pOutputSamples[i*8+4] = (ma_int32)left2 * factor; - pOutputSamples[i*8+5] = (ma_int32)right2 * factor; - pOutputSamples[i*8+6] = (ma_int32)left3 * factor; - pOutputSamples[i*8+7] = (ma_int32)right3 * factor; - } - for (i = (frameCount4 << 2); i < frameCount; ++i) { - ma_uint32 left = pInputSamples0U32[i] << shift0; - ma_uint32 side = pInputSamples1U32[i] << shift1; - ma_uint32 right = left - side; - pOutputSamples[i*2+0] = (ma_int32)left * factor; - pOutputSamples[i*2+1] = (ma_int32)right * factor; - } -} -#if defined(MA_DR_FLAC_SUPPORT_SSE2) -static MA_INLINE void ma_dr_flac_read_pcm_frames_f32__decode_left_side__sse2(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, float* pOutputSamples) -{ - ma_uint64 i; - ma_uint64 frameCount4 = frameCount >> 2; - const ma_uint32* pInputSamples0U32 = (const ma_uint32*)pInputSamples0; - const ma_uint32* pInputSamples1U32 = (const ma_uint32*)pInputSamples1; - ma_uint32 shift0 = (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample) - 8; - ma_uint32 shift1 = (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample) - 8; - __m128 factor; - MA_DR_FLAC_ASSERT(pFlac->bitsPerSample <= 24); - factor = _mm_set1_ps(1.0f / 8388608.0f); - for (i = 0; i < frameCount4; ++i) { - __m128i left = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples0 + i), shift0); - __m128i side = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples1 + i), shift1); - __m128i right = _mm_sub_epi32(left, side); - __m128 leftf = _mm_mul_ps(_mm_cvtepi32_ps(left), factor); - __m128 rightf = _mm_mul_ps(_mm_cvtepi32_ps(right), factor); - _mm_storeu_ps(pOutputSamples + i*8 + 0, _mm_unpacklo_ps(leftf, rightf)); - _mm_storeu_ps(pOutputSamples + i*8 + 4, _mm_unpackhi_ps(leftf, rightf)); - } - for (i = (frameCount4 << 2); i < frameCount; ++i) { - ma_uint32 left = pInputSamples0U32[i] << shift0; - ma_uint32 side = pInputSamples1U32[i] << shift1; - ma_uint32 right = left - side; - pOutputSamples[i*2+0] = (ma_int32)left / 8388608.0f; - pOutputSamples[i*2+1] = (ma_int32)right / 8388608.0f; - } -} -#endif -#if defined(MA_DR_FLAC_SUPPORT_NEON) -static MA_INLINE void ma_dr_flac_read_pcm_frames_f32__decode_left_side__neon(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, float* pOutputSamples) -{ - ma_uint64 i; - ma_uint64 frameCount4 = frameCount >> 2; - const ma_uint32* pInputSamples0U32 = (const ma_uint32*)pInputSamples0; - const ma_uint32* pInputSamples1U32 = (const ma_uint32*)pInputSamples1; - ma_uint32 shift0 = (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample) - 8; - ma_uint32 shift1 = (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample) - 8; - float32x4_t factor4; - int32x4_t shift0_4; - int32x4_t shift1_4; - MA_DR_FLAC_ASSERT(pFlac->bitsPerSample <= 24); - factor4 = vdupq_n_f32(1.0f / 8388608.0f); - shift0_4 = vdupq_n_s32(shift0); - shift1_4 = vdupq_n_s32(shift1); - for (i = 0; i < frameCount4; ++i) { - uint32x4_t left; - uint32x4_t side; - uint32x4_t right; - float32x4_t leftf; - float32x4_t rightf; - left = vshlq_u32(vld1q_u32(pInputSamples0U32 + i*4), shift0_4); - side = vshlq_u32(vld1q_u32(pInputSamples1U32 + i*4), shift1_4); - right = vsubq_u32(left, side); - leftf = vmulq_f32(vcvtq_f32_s32(vreinterpretq_s32_u32(left)), factor4); - rightf = vmulq_f32(vcvtq_f32_s32(vreinterpretq_s32_u32(right)), factor4); - ma_dr_flac__vst2q_f32(pOutputSamples + i*8, vzipq_f32(leftf, rightf)); - } - for (i = (frameCount4 << 2); i < frameCount; ++i) { - ma_uint32 left = pInputSamples0U32[i] << shift0; - ma_uint32 side = pInputSamples1U32[i] << shift1; - ma_uint32 right = left - side; - pOutputSamples[i*2+0] = (ma_int32)left / 8388608.0f; - pOutputSamples[i*2+1] = (ma_int32)right / 8388608.0f; - } -} -#endif -static MA_INLINE void ma_dr_flac_read_pcm_frames_f32__decode_left_side(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, float* pOutputSamples) -{ -#if defined(MA_DR_FLAC_SUPPORT_SSE2) - if (ma_dr_flac__gIsSSE2Supported && pFlac->bitsPerSample <= 24) { - ma_dr_flac_read_pcm_frames_f32__decode_left_side__sse2(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); - } else -#elif defined(MA_DR_FLAC_SUPPORT_NEON) - if (ma_dr_flac__gIsNEONSupported && pFlac->bitsPerSample <= 24) { - ma_dr_flac_read_pcm_frames_f32__decode_left_side__neon(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); - } else -#endif - { -#if 0 - ma_dr_flac_read_pcm_frames_f32__decode_left_side__reference(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); -#else - ma_dr_flac_read_pcm_frames_f32__decode_left_side__scalar(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); -#endif - } -} -#if 0 -static MA_INLINE void ma_dr_flac_read_pcm_frames_f32__decode_right_side__reference(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, float* pOutputSamples) -{ - ma_uint64 i; - for (i = 0; i < frameCount; ++i) { - ma_uint32 side = (ma_uint32)pInputSamples0[i] << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample); - ma_uint32 right = (ma_uint32)pInputSamples1[i] << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample); - ma_uint32 left = right + side; - pOutputSamples[i*2+0] = (float)((ma_int32)left / 2147483648.0); - pOutputSamples[i*2+1] = (float)((ma_int32)right / 2147483648.0); - } -} -#endif -static MA_INLINE void ma_dr_flac_read_pcm_frames_f32__decode_right_side__scalar(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, float* pOutputSamples) -{ - ma_uint64 i; - ma_uint64 frameCount4 = frameCount >> 2; - const ma_uint32* pInputSamples0U32 = (const ma_uint32*)pInputSamples0; - const ma_uint32* pInputSamples1U32 = (const ma_uint32*)pInputSamples1; - ma_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; - ma_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; - float factor = 1 / 2147483648.0; - for (i = 0; i < frameCount4; ++i) { - ma_uint32 side0 = pInputSamples0U32[i*4+0] << shift0; - ma_uint32 side1 = pInputSamples0U32[i*4+1] << shift0; - ma_uint32 side2 = pInputSamples0U32[i*4+2] << shift0; - ma_uint32 side3 = pInputSamples0U32[i*4+3] << shift0; - ma_uint32 right0 = pInputSamples1U32[i*4+0] << shift1; - ma_uint32 right1 = pInputSamples1U32[i*4+1] << shift1; - ma_uint32 right2 = pInputSamples1U32[i*4+2] << shift1; - ma_uint32 right3 = pInputSamples1U32[i*4+3] << shift1; - ma_uint32 left0 = right0 + side0; - ma_uint32 left1 = right1 + side1; - ma_uint32 left2 = right2 + side2; - ma_uint32 left3 = right3 + side3; - pOutputSamples[i*8+0] = (ma_int32)left0 * factor; - pOutputSamples[i*8+1] = (ma_int32)right0 * factor; - pOutputSamples[i*8+2] = (ma_int32)left1 * factor; - pOutputSamples[i*8+3] = (ma_int32)right1 * factor; - pOutputSamples[i*8+4] = (ma_int32)left2 * factor; - pOutputSamples[i*8+5] = (ma_int32)right2 * factor; - pOutputSamples[i*8+6] = (ma_int32)left3 * factor; - pOutputSamples[i*8+7] = (ma_int32)right3 * factor; - } - for (i = (frameCount4 << 2); i < frameCount; ++i) { - ma_uint32 side = pInputSamples0U32[i] << shift0; - ma_uint32 right = pInputSamples1U32[i] << shift1; - ma_uint32 left = right + side; - pOutputSamples[i*2+0] = (ma_int32)left * factor; - pOutputSamples[i*2+1] = (ma_int32)right * factor; - } -} -#if defined(MA_DR_FLAC_SUPPORT_SSE2) -static MA_INLINE void ma_dr_flac_read_pcm_frames_f32__decode_right_side__sse2(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, float* pOutputSamples) -{ - ma_uint64 i; - ma_uint64 frameCount4 = frameCount >> 2; - const ma_uint32* pInputSamples0U32 = (const ma_uint32*)pInputSamples0; - const ma_uint32* pInputSamples1U32 = (const ma_uint32*)pInputSamples1; - ma_uint32 shift0 = (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample) - 8; - ma_uint32 shift1 = (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample) - 8; - __m128 factor; - MA_DR_FLAC_ASSERT(pFlac->bitsPerSample <= 24); - factor = _mm_set1_ps(1.0f / 8388608.0f); - for (i = 0; i < frameCount4; ++i) { - __m128i side = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples0 + i), shift0); - __m128i right = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples1 + i), shift1); - __m128i left = _mm_add_epi32(right, side); - __m128 leftf = _mm_mul_ps(_mm_cvtepi32_ps(left), factor); - __m128 rightf = _mm_mul_ps(_mm_cvtepi32_ps(right), factor); - _mm_storeu_ps(pOutputSamples + i*8 + 0, _mm_unpacklo_ps(leftf, rightf)); - _mm_storeu_ps(pOutputSamples + i*8 + 4, _mm_unpackhi_ps(leftf, rightf)); - } - for (i = (frameCount4 << 2); i < frameCount; ++i) { - ma_uint32 side = pInputSamples0U32[i] << shift0; - ma_uint32 right = pInputSamples1U32[i] << shift1; - ma_uint32 left = right + side; - pOutputSamples[i*2+0] = (ma_int32)left / 8388608.0f; - pOutputSamples[i*2+1] = (ma_int32)right / 8388608.0f; - } -} -#endif -#if defined(MA_DR_FLAC_SUPPORT_NEON) -static MA_INLINE void ma_dr_flac_read_pcm_frames_f32__decode_right_side__neon(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, float* pOutputSamples) -{ - ma_uint64 i; - ma_uint64 frameCount4 = frameCount >> 2; - const ma_uint32* pInputSamples0U32 = (const ma_uint32*)pInputSamples0; - const ma_uint32* pInputSamples1U32 = (const ma_uint32*)pInputSamples1; - ma_uint32 shift0 = (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample) - 8; - ma_uint32 shift1 = (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample) - 8; - float32x4_t factor4; - int32x4_t shift0_4; - int32x4_t shift1_4; - MA_DR_FLAC_ASSERT(pFlac->bitsPerSample <= 24); - factor4 = vdupq_n_f32(1.0f / 8388608.0f); - shift0_4 = vdupq_n_s32(shift0); - shift1_4 = vdupq_n_s32(shift1); - for (i = 0; i < frameCount4; ++i) { - uint32x4_t side; - uint32x4_t right; - uint32x4_t left; - float32x4_t leftf; - float32x4_t rightf; - side = vshlq_u32(vld1q_u32(pInputSamples0U32 + i*4), shift0_4); - right = vshlq_u32(vld1q_u32(pInputSamples1U32 + i*4), shift1_4); - left = vaddq_u32(right, side); - leftf = vmulq_f32(vcvtq_f32_s32(vreinterpretq_s32_u32(left)), factor4); - rightf = vmulq_f32(vcvtq_f32_s32(vreinterpretq_s32_u32(right)), factor4); - ma_dr_flac__vst2q_f32(pOutputSamples + i*8, vzipq_f32(leftf, rightf)); - } - for (i = (frameCount4 << 2); i < frameCount; ++i) { - ma_uint32 side = pInputSamples0U32[i] << shift0; - ma_uint32 right = pInputSamples1U32[i] << shift1; - ma_uint32 left = right + side; - pOutputSamples[i*2+0] = (ma_int32)left / 8388608.0f; - pOutputSamples[i*2+1] = (ma_int32)right / 8388608.0f; - } -} -#endif -static MA_INLINE void ma_dr_flac_read_pcm_frames_f32__decode_right_side(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, float* pOutputSamples) -{ -#if defined(MA_DR_FLAC_SUPPORT_SSE2) - if (ma_dr_flac__gIsSSE2Supported && pFlac->bitsPerSample <= 24) { - ma_dr_flac_read_pcm_frames_f32__decode_right_side__sse2(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); - } else -#elif defined(MA_DR_FLAC_SUPPORT_NEON) - if (ma_dr_flac__gIsNEONSupported && pFlac->bitsPerSample <= 24) { - ma_dr_flac_read_pcm_frames_f32__decode_right_side__neon(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); - } else -#endif - { -#if 0 - ma_dr_flac_read_pcm_frames_f32__decode_right_side__reference(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); -#else - ma_dr_flac_read_pcm_frames_f32__decode_right_side__scalar(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); -#endif - } -} -#if 0 -static MA_INLINE void ma_dr_flac_read_pcm_frames_f32__decode_mid_side__reference(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, float* pOutputSamples) -{ - for (ma_uint64 i = 0; i < frameCount; ++i) { - ma_uint32 mid = (ma_uint32)pInputSamples0[i] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; - ma_uint32 side = (ma_uint32)pInputSamples1[i] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; - mid = (mid << 1) | (side & 0x01); - pOutputSamples[i*2+0] = (float)((((ma_int32)(mid + side) >> 1) << (unusedBitsPerSample)) / 2147483648.0); - pOutputSamples[i*2+1] = (float)((((ma_int32)(mid - side) >> 1) << (unusedBitsPerSample)) / 2147483648.0); - } -} -#endif -static MA_INLINE void ma_dr_flac_read_pcm_frames_f32__decode_mid_side__scalar(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, float* pOutputSamples) -{ - ma_uint64 i; - ma_uint64 frameCount4 = frameCount >> 2; - const ma_uint32* pInputSamples0U32 = (const ma_uint32*)pInputSamples0; - const ma_uint32* pInputSamples1U32 = (const ma_uint32*)pInputSamples1; - ma_uint32 shift = unusedBitsPerSample; - float factor = 1 / 2147483648.0; - if (shift > 0) { - shift -= 1; - for (i = 0; i < frameCount4; ++i) { - ma_uint32 temp0L; - ma_uint32 temp1L; - ma_uint32 temp2L; - ma_uint32 temp3L; - ma_uint32 temp0R; - ma_uint32 temp1R; - ma_uint32 temp2R; - ma_uint32 temp3R; - ma_uint32 mid0 = pInputSamples0U32[i*4+0] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; - ma_uint32 mid1 = pInputSamples0U32[i*4+1] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; - ma_uint32 mid2 = pInputSamples0U32[i*4+2] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; - ma_uint32 mid3 = pInputSamples0U32[i*4+3] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; - ma_uint32 side0 = pInputSamples1U32[i*4+0] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; - ma_uint32 side1 = pInputSamples1U32[i*4+1] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; - ma_uint32 side2 = pInputSamples1U32[i*4+2] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; - ma_uint32 side3 = pInputSamples1U32[i*4+3] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; - mid0 = (mid0 << 1) | (side0 & 0x01); - mid1 = (mid1 << 1) | (side1 & 0x01); - mid2 = (mid2 << 1) | (side2 & 0x01); - mid3 = (mid3 << 1) | (side3 & 0x01); - temp0L = (mid0 + side0) << shift; - temp1L = (mid1 + side1) << shift; - temp2L = (mid2 + side2) << shift; - temp3L = (mid3 + side3) << shift; - temp0R = (mid0 - side0) << shift; - temp1R = (mid1 - side1) << shift; - temp2R = (mid2 - side2) << shift; - temp3R = (mid3 - side3) << shift; - pOutputSamples[i*8+0] = (ma_int32)temp0L * factor; - pOutputSamples[i*8+1] = (ma_int32)temp0R * factor; - pOutputSamples[i*8+2] = (ma_int32)temp1L * factor; - pOutputSamples[i*8+3] = (ma_int32)temp1R * factor; - pOutputSamples[i*8+4] = (ma_int32)temp2L * factor; - pOutputSamples[i*8+5] = (ma_int32)temp2R * factor; - pOutputSamples[i*8+6] = (ma_int32)temp3L * factor; - pOutputSamples[i*8+7] = (ma_int32)temp3R * factor; - } - } else { - for (i = 0; i < frameCount4; ++i) { - ma_uint32 temp0L; - ma_uint32 temp1L; - ma_uint32 temp2L; - ma_uint32 temp3L; - ma_uint32 temp0R; - ma_uint32 temp1R; - ma_uint32 temp2R; - ma_uint32 temp3R; - ma_uint32 mid0 = pInputSamples0U32[i*4+0] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; - ma_uint32 mid1 = pInputSamples0U32[i*4+1] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; - ma_uint32 mid2 = pInputSamples0U32[i*4+2] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; - ma_uint32 mid3 = pInputSamples0U32[i*4+3] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; - ma_uint32 side0 = pInputSamples1U32[i*4+0] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; - ma_uint32 side1 = pInputSamples1U32[i*4+1] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; - ma_uint32 side2 = pInputSamples1U32[i*4+2] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; - ma_uint32 side3 = pInputSamples1U32[i*4+3] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; - mid0 = (mid0 << 1) | (side0 & 0x01); - mid1 = (mid1 << 1) | (side1 & 0x01); - mid2 = (mid2 << 1) | (side2 & 0x01); - mid3 = (mid3 << 1) | (side3 & 0x01); - temp0L = (ma_uint32)((ma_int32)(mid0 + side0) >> 1); - temp1L = (ma_uint32)((ma_int32)(mid1 + side1) >> 1); - temp2L = (ma_uint32)((ma_int32)(mid2 + side2) >> 1); - temp3L = (ma_uint32)((ma_int32)(mid3 + side3) >> 1); - temp0R = (ma_uint32)((ma_int32)(mid0 - side0) >> 1); - temp1R = (ma_uint32)((ma_int32)(mid1 - side1) >> 1); - temp2R = (ma_uint32)((ma_int32)(mid2 - side2) >> 1); - temp3R = (ma_uint32)((ma_int32)(mid3 - side3) >> 1); - pOutputSamples[i*8+0] = (ma_int32)temp0L * factor; - pOutputSamples[i*8+1] = (ma_int32)temp0R * factor; - pOutputSamples[i*8+2] = (ma_int32)temp1L * factor; - pOutputSamples[i*8+3] = (ma_int32)temp1R * factor; - pOutputSamples[i*8+4] = (ma_int32)temp2L * factor; - pOutputSamples[i*8+5] = (ma_int32)temp2R * factor; - pOutputSamples[i*8+6] = (ma_int32)temp3L * factor; - pOutputSamples[i*8+7] = (ma_int32)temp3R * factor; - } - } - for (i = (frameCount4 << 2); i < frameCount; ++i) { - ma_uint32 mid = pInputSamples0U32[i] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; - ma_uint32 side = pInputSamples1U32[i] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; - mid = (mid << 1) | (side & 0x01); - pOutputSamples[i*2+0] = (ma_int32)((ma_uint32)((ma_int32)(mid + side) >> 1) << unusedBitsPerSample) * factor; - pOutputSamples[i*2+1] = (ma_int32)((ma_uint32)((ma_int32)(mid - side) >> 1) << unusedBitsPerSample) * factor; - } -} -#if defined(MA_DR_FLAC_SUPPORT_SSE2) -static MA_INLINE void ma_dr_flac_read_pcm_frames_f32__decode_mid_side__sse2(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, float* pOutputSamples) -{ - ma_uint64 i; - ma_uint64 frameCount4 = frameCount >> 2; - const ma_uint32* pInputSamples0U32 = (const ma_uint32*)pInputSamples0; - const ma_uint32* pInputSamples1U32 = (const ma_uint32*)pInputSamples1; - ma_uint32 shift = unusedBitsPerSample - 8; - float factor; - __m128 factor128; - MA_DR_FLAC_ASSERT(pFlac->bitsPerSample <= 24); - factor = 1.0f / 8388608.0f; - factor128 = _mm_set1_ps(factor); - if (shift == 0) { - for (i = 0; i < frameCount4; ++i) { - __m128i mid; - __m128i side; - __m128i tempL; - __m128i tempR; - __m128 leftf; - __m128 rightf; - mid = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples0 + i), pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample); - side = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples1 + i), pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample); - mid = _mm_or_si128(_mm_slli_epi32(mid, 1), _mm_and_si128(side, _mm_set1_epi32(0x01))); - tempL = _mm_srai_epi32(_mm_add_epi32(mid, side), 1); - tempR = _mm_srai_epi32(_mm_sub_epi32(mid, side), 1); - leftf = _mm_mul_ps(_mm_cvtepi32_ps(tempL), factor128); - rightf = _mm_mul_ps(_mm_cvtepi32_ps(tempR), factor128); - _mm_storeu_ps(pOutputSamples + i*8 + 0, _mm_unpacklo_ps(leftf, rightf)); - _mm_storeu_ps(pOutputSamples + i*8 + 4, _mm_unpackhi_ps(leftf, rightf)); - } - for (i = (frameCount4 << 2); i < frameCount; ++i) { - ma_uint32 mid = pInputSamples0U32[i] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; - ma_uint32 side = pInputSamples1U32[i] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; - mid = (mid << 1) | (side & 0x01); - pOutputSamples[i*2+0] = ((ma_int32)(mid + side) >> 1) * factor; - pOutputSamples[i*2+1] = ((ma_int32)(mid - side) >> 1) * factor; - } - } else { - shift -= 1; - for (i = 0; i < frameCount4; ++i) { - __m128i mid; - __m128i side; - __m128i tempL; - __m128i tempR; - __m128 leftf; - __m128 rightf; - mid = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples0 + i), pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample); - side = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples1 + i), pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample); - mid = _mm_or_si128(_mm_slli_epi32(mid, 1), _mm_and_si128(side, _mm_set1_epi32(0x01))); - tempL = _mm_slli_epi32(_mm_add_epi32(mid, side), shift); - tempR = _mm_slli_epi32(_mm_sub_epi32(mid, side), shift); - leftf = _mm_mul_ps(_mm_cvtepi32_ps(tempL), factor128); - rightf = _mm_mul_ps(_mm_cvtepi32_ps(tempR), factor128); - _mm_storeu_ps(pOutputSamples + i*8 + 0, _mm_unpacklo_ps(leftf, rightf)); - _mm_storeu_ps(pOutputSamples + i*8 + 4, _mm_unpackhi_ps(leftf, rightf)); - } - for (i = (frameCount4 << 2); i < frameCount; ++i) { - ma_uint32 mid = pInputSamples0U32[i] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; - ma_uint32 side = pInputSamples1U32[i] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; - mid = (mid << 1) | (side & 0x01); - pOutputSamples[i*2+0] = (ma_int32)((mid + side) << shift) * factor; - pOutputSamples[i*2+1] = (ma_int32)((mid - side) << shift) * factor; - } - } -} -#endif -#if defined(MA_DR_FLAC_SUPPORT_NEON) -static MA_INLINE void ma_dr_flac_read_pcm_frames_f32__decode_mid_side__neon(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, float* pOutputSamples) -{ - ma_uint64 i; - ma_uint64 frameCount4 = frameCount >> 2; - const ma_uint32* pInputSamples0U32 = (const ma_uint32*)pInputSamples0; - const ma_uint32* pInputSamples1U32 = (const ma_uint32*)pInputSamples1; - ma_uint32 shift = unusedBitsPerSample - 8; - float factor; - float32x4_t factor4; - int32x4_t shift4; - int32x4_t wbps0_4; - int32x4_t wbps1_4; - MA_DR_FLAC_ASSERT(pFlac->bitsPerSample <= 24); - factor = 1.0f / 8388608.0f; - factor4 = vdupq_n_f32(factor); - wbps0_4 = vdupq_n_s32(pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample); - wbps1_4 = vdupq_n_s32(pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample); - if (shift == 0) { - for (i = 0; i < frameCount4; ++i) { - int32x4_t lefti; - int32x4_t righti; - float32x4_t leftf; - float32x4_t rightf; - uint32x4_t mid = vshlq_u32(vld1q_u32(pInputSamples0U32 + i*4), wbps0_4); - uint32x4_t side = vshlq_u32(vld1q_u32(pInputSamples1U32 + i*4), wbps1_4); - mid = vorrq_u32(vshlq_n_u32(mid, 1), vandq_u32(side, vdupq_n_u32(1))); - lefti = vshrq_n_s32(vreinterpretq_s32_u32(vaddq_u32(mid, side)), 1); - righti = vshrq_n_s32(vreinterpretq_s32_u32(vsubq_u32(mid, side)), 1); - leftf = vmulq_f32(vcvtq_f32_s32(lefti), factor4); - rightf = vmulq_f32(vcvtq_f32_s32(righti), factor4); - ma_dr_flac__vst2q_f32(pOutputSamples + i*8, vzipq_f32(leftf, rightf)); - } - for (i = (frameCount4 << 2); i < frameCount; ++i) { - ma_uint32 mid = pInputSamples0U32[i] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; - ma_uint32 side = pInputSamples1U32[i] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; - mid = (mid << 1) | (side & 0x01); - pOutputSamples[i*2+0] = ((ma_int32)(mid + side) >> 1) * factor; - pOutputSamples[i*2+1] = ((ma_int32)(mid - side) >> 1) * factor; - } - } else { - shift -= 1; - shift4 = vdupq_n_s32(shift); - for (i = 0; i < frameCount4; ++i) { - uint32x4_t mid; - uint32x4_t side; - int32x4_t lefti; - int32x4_t righti; - float32x4_t leftf; - float32x4_t rightf; - mid = vshlq_u32(vld1q_u32(pInputSamples0U32 + i*4), wbps0_4); - side = vshlq_u32(vld1q_u32(pInputSamples1U32 + i*4), wbps1_4); - mid = vorrq_u32(vshlq_n_u32(mid, 1), vandq_u32(side, vdupq_n_u32(1))); - lefti = vreinterpretq_s32_u32(vshlq_u32(vaddq_u32(mid, side), shift4)); - righti = vreinterpretq_s32_u32(vshlq_u32(vsubq_u32(mid, side), shift4)); - leftf = vmulq_f32(vcvtq_f32_s32(lefti), factor4); - rightf = vmulq_f32(vcvtq_f32_s32(righti), factor4); - ma_dr_flac__vst2q_f32(pOutputSamples + i*8, vzipq_f32(leftf, rightf)); - } - for (i = (frameCount4 << 2); i < frameCount; ++i) { - ma_uint32 mid = pInputSamples0U32[i] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; - ma_uint32 side = pInputSamples1U32[i] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; - mid = (mid << 1) | (side & 0x01); - pOutputSamples[i*2+0] = (ma_int32)((mid + side) << shift) * factor; - pOutputSamples[i*2+1] = (ma_int32)((mid - side) << shift) * factor; - } - } -} -#endif -static MA_INLINE void ma_dr_flac_read_pcm_frames_f32__decode_mid_side(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, float* pOutputSamples) -{ -#if defined(MA_DR_FLAC_SUPPORT_SSE2) - if (ma_dr_flac__gIsSSE2Supported && pFlac->bitsPerSample <= 24) { - ma_dr_flac_read_pcm_frames_f32__decode_mid_side__sse2(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); - } else -#elif defined(MA_DR_FLAC_SUPPORT_NEON) - if (ma_dr_flac__gIsNEONSupported && pFlac->bitsPerSample <= 24) { - ma_dr_flac_read_pcm_frames_f32__decode_mid_side__neon(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); - } else -#endif - { -#if 0 - ma_dr_flac_read_pcm_frames_f32__decode_mid_side__reference(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); -#else - ma_dr_flac_read_pcm_frames_f32__decode_mid_side__scalar(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); -#endif - } -} -#if 0 -static MA_INLINE void ma_dr_flac_read_pcm_frames_f32__decode_independent_stereo__reference(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, float* pOutputSamples) -{ - for (ma_uint64 i = 0; i < frameCount; ++i) { - pOutputSamples[i*2+0] = (float)((ma_int32)((ma_uint32)pInputSamples0[i] << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample)) / 2147483648.0); - pOutputSamples[i*2+1] = (float)((ma_int32)((ma_uint32)pInputSamples1[i] << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample)) / 2147483648.0); - } -} -#endif -static MA_INLINE void ma_dr_flac_read_pcm_frames_f32__decode_independent_stereo__scalar(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, float* pOutputSamples) -{ - ma_uint64 i; - ma_uint64 frameCount4 = frameCount >> 2; - const ma_uint32* pInputSamples0U32 = (const ma_uint32*)pInputSamples0; - const ma_uint32* pInputSamples1U32 = (const ma_uint32*)pInputSamples1; - ma_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; - ma_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; - float factor = 1 / 2147483648.0; - for (i = 0; i < frameCount4; ++i) { - ma_uint32 tempL0 = pInputSamples0U32[i*4+0] << shift0; - ma_uint32 tempL1 = pInputSamples0U32[i*4+1] << shift0; - ma_uint32 tempL2 = pInputSamples0U32[i*4+2] << shift0; - ma_uint32 tempL3 = pInputSamples0U32[i*4+3] << shift0; - ma_uint32 tempR0 = pInputSamples1U32[i*4+0] << shift1; - ma_uint32 tempR1 = pInputSamples1U32[i*4+1] << shift1; - ma_uint32 tempR2 = pInputSamples1U32[i*4+2] << shift1; - ma_uint32 tempR3 = pInputSamples1U32[i*4+3] << shift1; - pOutputSamples[i*8+0] = (ma_int32)tempL0 * factor; - pOutputSamples[i*8+1] = (ma_int32)tempR0 * factor; - pOutputSamples[i*8+2] = (ma_int32)tempL1 * factor; - pOutputSamples[i*8+3] = (ma_int32)tempR1 * factor; - pOutputSamples[i*8+4] = (ma_int32)tempL2 * factor; - pOutputSamples[i*8+5] = (ma_int32)tempR2 * factor; - pOutputSamples[i*8+6] = (ma_int32)tempL3 * factor; - pOutputSamples[i*8+7] = (ma_int32)tempR3 * factor; - } - for (i = (frameCount4 << 2); i < frameCount; ++i) { - pOutputSamples[i*2+0] = (ma_int32)(pInputSamples0U32[i] << shift0) * factor; - pOutputSamples[i*2+1] = (ma_int32)(pInputSamples1U32[i] << shift1) * factor; - } -} -#if defined(MA_DR_FLAC_SUPPORT_SSE2) -static MA_INLINE void ma_dr_flac_read_pcm_frames_f32__decode_independent_stereo__sse2(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, float* pOutputSamples) -{ - ma_uint64 i; - ma_uint64 frameCount4 = frameCount >> 2; - const ma_uint32* pInputSamples0U32 = (const ma_uint32*)pInputSamples0; - const ma_uint32* pInputSamples1U32 = (const ma_uint32*)pInputSamples1; - ma_uint32 shift0 = (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample) - 8; - ma_uint32 shift1 = (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample) - 8; - float factor = 1.0f / 8388608.0f; - __m128 factor128 = _mm_set1_ps(factor); - for (i = 0; i < frameCount4; ++i) { - __m128i lefti; - __m128i righti; - __m128 leftf; - __m128 rightf; - lefti = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples0 + i), shift0); - righti = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples1 + i), shift1); - leftf = _mm_mul_ps(_mm_cvtepi32_ps(lefti), factor128); - rightf = _mm_mul_ps(_mm_cvtepi32_ps(righti), factor128); - _mm_storeu_ps(pOutputSamples + i*8 + 0, _mm_unpacklo_ps(leftf, rightf)); - _mm_storeu_ps(pOutputSamples + i*8 + 4, _mm_unpackhi_ps(leftf, rightf)); - } - for (i = (frameCount4 << 2); i < frameCount; ++i) { - pOutputSamples[i*2+0] = (ma_int32)(pInputSamples0U32[i] << shift0) * factor; - pOutputSamples[i*2+1] = (ma_int32)(pInputSamples1U32[i] << shift1) * factor; - } -} -#endif -#if defined(MA_DR_FLAC_SUPPORT_NEON) -static MA_INLINE void ma_dr_flac_read_pcm_frames_f32__decode_independent_stereo__neon(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, float* pOutputSamples) -{ - ma_uint64 i; - ma_uint64 frameCount4 = frameCount >> 2; - const ma_uint32* pInputSamples0U32 = (const ma_uint32*)pInputSamples0; - const ma_uint32* pInputSamples1U32 = (const ma_uint32*)pInputSamples1; - ma_uint32 shift0 = (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample) - 8; - ma_uint32 shift1 = (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample) - 8; - float factor = 1.0f / 8388608.0f; - float32x4_t factor4 = vdupq_n_f32(factor); - int32x4_t shift0_4 = vdupq_n_s32(shift0); - int32x4_t shift1_4 = vdupq_n_s32(shift1); - for (i = 0; i < frameCount4; ++i) { - int32x4_t lefti; - int32x4_t righti; - float32x4_t leftf; - float32x4_t rightf; - lefti = vreinterpretq_s32_u32(vshlq_u32(vld1q_u32(pInputSamples0U32 + i*4), shift0_4)); - righti = vreinterpretq_s32_u32(vshlq_u32(vld1q_u32(pInputSamples1U32 + i*4), shift1_4)); - leftf = vmulq_f32(vcvtq_f32_s32(lefti), factor4); - rightf = vmulq_f32(vcvtq_f32_s32(righti), factor4); - ma_dr_flac__vst2q_f32(pOutputSamples + i*8, vzipq_f32(leftf, rightf)); - } - for (i = (frameCount4 << 2); i < frameCount; ++i) { - pOutputSamples[i*2+0] = (ma_int32)(pInputSamples0U32[i] << shift0) * factor; - pOutputSamples[i*2+1] = (ma_int32)(pInputSamples1U32[i] << shift1) * factor; - } -} -#endif -static MA_INLINE void ma_dr_flac_read_pcm_frames_f32__decode_independent_stereo(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, float* pOutputSamples) -{ -#if defined(MA_DR_FLAC_SUPPORT_SSE2) - if (ma_dr_flac__gIsSSE2Supported && pFlac->bitsPerSample <= 24) { - ma_dr_flac_read_pcm_frames_f32__decode_independent_stereo__sse2(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); - } else -#elif defined(MA_DR_FLAC_SUPPORT_NEON) - if (ma_dr_flac__gIsNEONSupported && pFlac->bitsPerSample <= 24) { - ma_dr_flac_read_pcm_frames_f32__decode_independent_stereo__neon(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); - } else -#endif - { -#if 0 - ma_dr_flac_read_pcm_frames_f32__decode_independent_stereo__reference(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); -#else - ma_dr_flac_read_pcm_frames_f32__decode_independent_stereo__scalar(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); -#endif - } -} -MA_API ma_uint64 ma_dr_flac_read_pcm_frames_f32(ma_dr_flac* pFlac, ma_uint64 framesToRead, float* pBufferOut) -{ - ma_uint64 framesRead; - ma_uint32 unusedBitsPerSample; - if (pFlac == NULL || framesToRead == 0) { - return 0; - } - if (pBufferOut == NULL) { - return ma_dr_flac__seek_forward_by_pcm_frames(pFlac, framesToRead); - } - MA_DR_FLAC_ASSERT(pFlac->bitsPerSample <= 32); - unusedBitsPerSample = 32 - pFlac->bitsPerSample; - framesRead = 0; - while (framesToRead > 0) { - if (pFlac->currentFLACFrame.pcmFramesRemaining == 0) { - if (!ma_dr_flac__read_and_decode_next_flac_frame(pFlac)) { - break; - } - } else { - unsigned int channelCount = ma_dr_flac__get_channel_count_from_channel_assignment(pFlac->currentFLACFrame.header.channelAssignment); - ma_uint64 iFirstPCMFrame = pFlac->currentFLACFrame.header.blockSizeInPCMFrames - pFlac->currentFLACFrame.pcmFramesRemaining; - ma_uint64 frameCountThisIteration = framesToRead; - if (frameCountThisIteration > pFlac->currentFLACFrame.pcmFramesRemaining) { - frameCountThisIteration = pFlac->currentFLACFrame.pcmFramesRemaining; - } - if (channelCount == 2) { - const ma_int32* pDecodedSamples0 = pFlac->currentFLACFrame.subframes[0].pSamplesS32 + iFirstPCMFrame; - const ma_int32* pDecodedSamples1 = pFlac->currentFLACFrame.subframes[1].pSamplesS32 + iFirstPCMFrame; - switch (pFlac->currentFLACFrame.header.channelAssignment) - { - case MA_DR_FLAC_CHANNEL_ASSIGNMENT_LEFT_SIDE: - { - ma_dr_flac_read_pcm_frames_f32__decode_left_side(pFlac, frameCountThisIteration, unusedBitsPerSample, pDecodedSamples0, pDecodedSamples1, pBufferOut); - } break; - case MA_DR_FLAC_CHANNEL_ASSIGNMENT_RIGHT_SIDE: - { - ma_dr_flac_read_pcm_frames_f32__decode_right_side(pFlac, frameCountThisIteration, unusedBitsPerSample, pDecodedSamples0, pDecodedSamples1, pBufferOut); - } break; - case MA_DR_FLAC_CHANNEL_ASSIGNMENT_MID_SIDE: - { - ma_dr_flac_read_pcm_frames_f32__decode_mid_side(pFlac, frameCountThisIteration, unusedBitsPerSample, pDecodedSamples0, pDecodedSamples1, pBufferOut); - } break; - case MA_DR_FLAC_CHANNEL_ASSIGNMENT_INDEPENDENT: - default: - { - ma_dr_flac_read_pcm_frames_f32__decode_independent_stereo(pFlac, frameCountThisIteration, unusedBitsPerSample, pDecodedSamples0, pDecodedSamples1, pBufferOut); - } break; - } - } else { - ma_uint64 i; - for (i = 0; i < frameCountThisIteration; ++i) { - unsigned int j; - for (j = 0; j < channelCount; ++j) { - ma_int32 sampleS32 = (ma_int32)((ma_uint32)(pFlac->currentFLACFrame.subframes[j].pSamplesS32[iFirstPCMFrame + i]) << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[j].wastedBitsPerSample)); - pBufferOut[(i*channelCount)+j] = (float)(sampleS32 / 2147483648.0); - } - } - } - framesRead += frameCountThisIteration; - pBufferOut += frameCountThisIteration * channelCount; - framesToRead -= frameCountThisIteration; - pFlac->currentPCMFrame += frameCountThisIteration; - pFlac->currentFLACFrame.pcmFramesRemaining -= (unsigned int)frameCountThisIteration; - } - } - return framesRead; -} -MA_API ma_bool32 ma_dr_flac_seek_to_pcm_frame(ma_dr_flac* pFlac, ma_uint64 pcmFrameIndex) -{ - if (pFlac == NULL) { - return MA_FALSE; - } - if (pFlac->currentPCMFrame == pcmFrameIndex) { - return MA_TRUE; - } - if (pFlac->firstFLACFramePosInBytes == 0) { - return MA_FALSE; - } - if (pcmFrameIndex == 0) { - pFlac->currentPCMFrame = 0; - return ma_dr_flac__seek_to_first_frame(pFlac); - } else { - ma_bool32 wasSuccessful = MA_FALSE; - ma_uint64 originalPCMFrame = pFlac->currentPCMFrame; - if (pcmFrameIndex > pFlac->totalPCMFrameCount) { - pcmFrameIndex = pFlac->totalPCMFrameCount; - } - if (pcmFrameIndex > pFlac->currentPCMFrame) { - ma_uint32 offset = (ma_uint32)(pcmFrameIndex - pFlac->currentPCMFrame); - if (pFlac->currentFLACFrame.pcmFramesRemaining > offset) { - pFlac->currentFLACFrame.pcmFramesRemaining -= offset; - pFlac->currentPCMFrame = pcmFrameIndex; - return MA_TRUE; - } - } else { - ma_uint32 offsetAbs = (ma_uint32)(pFlac->currentPCMFrame - pcmFrameIndex); - ma_uint32 currentFLACFramePCMFrameCount = pFlac->currentFLACFrame.header.blockSizeInPCMFrames; - ma_uint32 currentFLACFramePCMFramesConsumed = currentFLACFramePCMFrameCount - pFlac->currentFLACFrame.pcmFramesRemaining; - if (currentFLACFramePCMFramesConsumed > offsetAbs) { - pFlac->currentFLACFrame.pcmFramesRemaining += offsetAbs; - pFlac->currentPCMFrame = pcmFrameIndex; - return MA_TRUE; - } - } -#ifndef MA_DR_FLAC_NO_OGG - if (pFlac->container == ma_dr_flac_container_ogg) - { - wasSuccessful = ma_dr_flac_ogg__seek_to_pcm_frame(pFlac, pcmFrameIndex); - } - else -#endif - { - if (!pFlac->_noSeekTableSeek) { - wasSuccessful = ma_dr_flac__seek_to_pcm_frame__seek_table(pFlac, pcmFrameIndex); - } -#if !defined(MA_DR_FLAC_NO_CRC) - if (!wasSuccessful && !pFlac->_noBinarySearchSeek && pFlac->totalPCMFrameCount > 0) { - wasSuccessful = ma_dr_flac__seek_to_pcm_frame__binary_search(pFlac, pcmFrameIndex); - } -#endif - if (!wasSuccessful && !pFlac->_noBruteForceSeek) { - wasSuccessful = ma_dr_flac__seek_to_pcm_frame__brute_force(pFlac, pcmFrameIndex); - } - } - if (wasSuccessful) { - pFlac->currentPCMFrame = pcmFrameIndex; - } else { - if (ma_dr_flac_seek_to_pcm_frame(pFlac, originalPCMFrame) == MA_FALSE) { - ma_dr_flac_seek_to_pcm_frame(pFlac, 0); - } - } - return wasSuccessful; - } -} -#define MA_DR_FLAC_DEFINE_FULL_READ_AND_CLOSE(extension, type) \ -static type* ma_dr_flac__full_read_and_close_ ## extension (ma_dr_flac* pFlac, unsigned int* channelsOut, unsigned int* sampleRateOut, ma_uint64* totalPCMFrameCountOut)\ -{ \ - type* pSampleData = NULL; \ - ma_uint64 totalPCMFrameCount; \ - \ - MA_DR_FLAC_ASSERT(pFlac != NULL); \ - \ - totalPCMFrameCount = pFlac->totalPCMFrameCount; \ - \ - if (totalPCMFrameCount == 0) { \ - type buffer[4096]; \ - ma_uint64 pcmFramesRead; \ - size_t sampleDataBufferSize = sizeof(buffer); \ - \ - pSampleData = (type*)ma_dr_flac__malloc_from_callbacks(sampleDataBufferSize, &pFlac->allocationCallbacks); \ - if (pSampleData == NULL) { \ - goto on_error; \ - } \ - \ - while ((pcmFramesRead = (ma_uint64)ma_dr_flac_read_pcm_frames_##extension(pFlac, sizeof(buffer)/sizeof(buffer[0])/pFlac->channels, buffer)) > 0) { \ - if (((totalPCMFrameCount + pcmFramesRead) * pFlac->channels * sizeof(type)) > sampleDataBufferSize) { \ - type* pNewSampleData; \ - size_t newSampleDataBufferSize; \ - \ - newSampleDataBufferSize = sampleDataBufferSize * 2; \ - pNewSampleData = (type*)ma_dr_flac__realloc_from_callbacks(pSampleData, newSampleDataBufferSize, sampleDataBufferSize, &pFlac->allocationCallbacks); \ - if (pNewSampleData == NULL) { \ - ma_dr_flac__free_from_callbacks(pSampleData, &pFlac->allocationCallbacks); \ - goto on_error; \ - } \ - \ - sampleDataBufferSize = newSampleDataBufferSize; \ - pSampleData = pNewSampleData; \ - } \ - \ - MA_DR_FLAC_COPY_MEMORY(pSampleData + (totalPCMFrameCount*pFlac->channels), buffer, (size_t)(pcmFramesRead*pFlac->channels*sizeof(type))); \ - totalPCMFrameCount += pcmFramesRead; \ - } \ - \ - \ - MA_DR_FLAC_ZERO_MEMORY(pSampleData + (totalPCMFrameCount*pFlac->channels), (size_t)(sampleDataBufferSize - totalPCMFrameCount*pFlac->channels*sizeof(type))); \ - } else { \ - ma_uint64 dataSize = totalPCMFrameCount*pFlac->channels*sizeof(type); \ - if (dataSize > (ma_uint64)MA_SIZE_MAX) { \ - goto on_error; \ - } \ - \ - pSampleData = (type*)ma_dr_flac__malloc_from_callbacks((size_t)dataSize, &pFlac->allocationCallbacks); \ - if (pSampleData == NULL) { \ - goto on_error; \ - } \ - \ - totalPCMFrameCount = ma_dr_flac_read_pcm_frames_##extension(pFlac, pFlac->totalPCMFrameCount, pSampleData); \ - } \ - \ - if (sampleRateOut) *sampleRateOut = pFlac->sampleRate; \ - if (channelsOut) *channelsOut = pFlac->channels; \ - if (totalPCMFrameCountOut) *totalPCMFrameCountOut = totalPCMFrameCount; \ - \ - ma_dr_flac_close(pFlac); \ - return pSampleData; \ - \ -on_error: \ - ma_dr_flac_close(pFlac); \ - return NULL; \ -} -MA_DR_FLAC_DEFINE_FULL_READ_AND_CLOSE(s32, ma_int32) -MA_DR_FLAC_DEFINE_FULL_READ_AND_CLOSE(s16, ma_int16) -MA_DR_FLAC_DEFINE_FULL_READ_AND_CLOSE(f32, float) -MA_API ma_int32* ma_dr_flac_open_and_read_pcm_frames_s32(ma_dr_flac_read_proc onRead, ma_dr_flac_seek_proc onSeek, void* pUserData, unsigned int* channelsOut, unsigned int* sampleRateOut, ma_uint64* totalPCMFrameCountOut, const ma_allocation_callbacks* pAllocationCallbacks) -{ - ma_dr_flac* pFlac; - if (channelsOut) { - *channelsOut = 0; - } - if (sampleRateOut) { - *sampleRateOut = 0; - } - if (totalPCMFrameCountOut) { - *totalPCMFrameCountOut = 0; - } - pFlac = ma_dr_flac_open(onRead, onSeek, pUserData, pAllocationCallbacks); - if (pFlac == NULL) { - return NULL; - } - return ma_dr_flac__full_read_and_close_s32(pFlac, channelsOut, sampleRateOut, totalPCMFrameCountOut); -} -MA_API ma_int16* ma_dr_flac_open_and_read_pcm_frames_s16(ma_dr_flac_read_proc onRead, ma_dr_flac_seek_proc onSeek, void* pUserData, unsigned int* channelsOut, unsigned int* sampleRateOut, ma_uint64* totalPCMFrameCountOut, const ma_allocation_callbacks* pAllocationCallbacks) -{ - ma_dr_flac* pFlac; - if (channelsOut) { - *channelsOut = 0; - } - if (sampleRateOut) { - *sampleRateOut = 0; - } - if (totalPCMFrameCountOut) { - *totalPCMFrameCountOut = 0; - } - pFlac = ma_dr_flac_open(onRead, onSeek, pUserData, pAllocationCallbacks); - if (pFlac == NULL) { - return NULL; - } - return ma_dr_flac__full_read_and_close_s16(pFlac, channelsOut, sampleRateOut, totalPCMFrameCountOut); -} -MA_API float* ma_dr_flac_open_and_read_pcm_frames_f32(ma_dr_flac_read_proc onRead, ma_dr_flac_seek_proc onSeek, void* pUserData, unsigned int* channelsOut, unsigned int* sampleRateOut, ma_uint64* totalPCMFrameCountOut, const ma_allocation_callbacks* pAllocationCallbacks) -{ - ma_dr_flac* pFlac; - if (channelsOut) { - *channelsOut = 0; - } - if (sampleRateOut) { - *sampleRateOut = 0; - } - if (totalPCMFrameCountOut) { - *totalPCMFrameCountOut = 0; - } - pFlac = ma_dr_flac_open(onRead, onSeek, pUserData, pAllocationCallbacks); - if (pFlac == NULL) { - return NULL; - } - return ma_dr_flac__full_read_and_close_f32(pFlac, channelsOut, sampleRateOut, totalPCMFrameCountOut); -} -#ifndef MA_DR_FLAC_NO_STDIO -MA_API ma_int32* ma_dr_flac_open_file_and_read_pcm_frames_s32(const char* filename, unsigned int* channels, unsigned int* sampleRate, ma_uint64* totalPCMFrameCount, const ma_allocation_callbacks* pAllocationCallbacks) -{ - ma_dr_flac* pFlac; - if (sampleRate) { - *sampleRate = 0; - } - if (channels) { - *channels = 0; - } - if (totalPCMFrameCount) { - *totalPCMFrameCount = 0; - } - pFlac = ma_dr_flac_open_file(filename, pAllocationCallbacks); - if (pFlac == NULL) { - return NULL; - } - return ma_dr_flac__full_read_and_close_s32(pFlac, channels, sampleRate, totalPCMFrameCount); -} -MA_API ma_int16* ma_dr_flac_open_file_and_read_pcm_frames_s16(const char* filename, unsigned int* channels, unsigned int* sampleRate, ma_uint64* totalPCMFrameCount, const ma_allocation_callbacks* pAllocationCallbacks) -{ - ma_dr_flac* pFlac; - if (sampleRate) { - *sampleRate = 0; - } - if (channels) { - *channels = 0; - } - if (totalPCMFrameCount) { - *totalPCMFrameCount = 0; - } - pFlac = ma_dr_flac_open_file(filename, pAllocationCallbacks); - if (pFlac == NULL) { - return NULL; - } - return ma_dr_flac__full_read_and_close_s16(pFlac, channels, sampleRate, totalPCMFrameCount); -} -MA_API float* ma_dr_flac_open_file_and_read_pcm_frames_f32(const char* filename, unsigned int* channels, unsigned int* sampleRate, ma_uint64* totalPCMFrameCount, const ma_allocation_callbacks* pAllocationCallbacks) -{ - ma_dr_flac* pFlac; - if (sampleRate) { - *sampleRate = 0; - } - if (channels) { - *channels = 0; - } - if (totalPCMFrameCount) { - *totalPCMFrameCount = 0; - } - pFlac = ma_dr_flac_open_file(filename, pAllocationCallbacks); - if (pFlac == NULL) { - return NULL; - } - return ma_dr_flac__full_read_and_close_f32(pFlac, channels, sampleRate, totalPCMFrameCount); -} -#endif -MA_API ma_int32* ma_dr_flac_open_memory_and_read_pcm_frames_s32(const void* data, size_t dataSize, unsigned int* channels, unsigned int* sampleRate, ma_uint64* totalPCMFrameCount, const ma_allocation_callbacks* pAllocationCallbacks) -{ - ma_dr_flac* pFlac; - if (sampleRate) { - *sampleRate = 0; - } - if (channels) { - *channels = 0; - } - if (totalPCMFrameCount) { - *totalPCMFrameCount = 0; - } - pFlac = ma_dr_flac_open_memory(data, dataSize, pAllocationCallbacks); - if (pFlac == NULL) { - return NULL; - } - return ma_dr_flac__full_read_and_close_s32(pFlac, channels, sampleRate, totalPCMFrameCount); -} -MA_API ma_int16* ma_dr_flac_open_memory_and_read_pcm_frames_s16(const void* data, size_t dataSize, unsigned int* channels, unsigned int* sampleRate, ma_uint64* totalPCMFrameCount, const ma_allocation_callbacks* pAllocationCallbacks) -{ - ma_dr_flac* pFlac; - if (sampleRate) { - *sampleRate = 0; - } - if (channels) { - *channels = 0; - } - if (totalPCMFrameCount) { - *totalPCMFrameCount = 0; - } - pFlac = ma_dr_flac_open_memory(data, dataSize, pAllocationCallbacks); - if (pFlac == NULL) { - return NULL; - } - return ma_dr_flac__full_read_and_close_s16(pFlac, channels, sampleRate, totalPCMFrameCount); -} -MA_API float* ma_dr_flac_open_memory_and_read_pcm_frames_f32(const void* data, size_t dataSize, unsigned int* channels, unsigned int* sampleRate, ma_uint64* totalPCMFrameCount, const ma_allocation_callbacks* pAllocationCallbacks) -{ - ma_dr_flac* pFlac; - if (sampleRate) { - *sampleRate = 0; - } - if (channels) { - *channels = 0; - } - if (totalPCMFrameCount) { - *totalPCMFrameCount = 0; - } - pFlac = ma_dr_flac_open_memory(data, dataSize, pAllocationCallbacks); - if (pFlac == NULL) { - return NULL; - } - return ma_dr_flac__full_read_and_close_f32(pFlac, channels, sampleRate, totalPCMFrameCount); -} -MA_API void ma_dr_flac_free(void* p, const ma_allocation_callbacks* pAllocationCallbacks) -{ - if (pAllocationCallbacks != NULL) { - ma_dr_flac__free_from_callbacks(p, pAllocationCallbacks); - } else { - ma_dr_flac__free_default(p, NULL); - } -} -MA_API void ma_dr_flac_init_vorbis_comment_iterator(ma_dr_flac_vorbis_comment_iterator* pIter, ma_uint32 commentCount, const void* pComments) -{ - if (pIter == NULL) { - return; - } - pIter->countRemaining = commentCount; - pIter->pRunningData = (const char*)pComments; -} -MA_API const char* ma_dr_flac_next_vorbis_comment(ma_dr_flac_vorbis_comment_iterator* pIter, ma_uint32* pCommentLengthOut) -{ - ma_int32 length; - const char* pComment; - if (pCommentLengthOut) { - *pCommentLengthOut = 0; - } - if (pIter == NULL || pIter->countRemaining == 0 || pIter->pRunningData == NULL) { - return NULL; - } - length = ma_dr_flac__le2host_32_ptr_unaligned(pIter->pRunningData); - pIter->pRunningData += 4; - pComment = pIter->pRunningData; - pIter->pRunningData += length; - pIter->countRemaining -= 1; - if (pCommentLengthOut) { - *pCommentLengthOut = length; - } - return pComment; -} -MA_API void ma_dr_flac_init_cuesheet_track_iterator(ma_dr_flac_cuesheet_track_iterator* pIter, ma_uint32 trackCount, const void* pTrackData) -{ - if (pIter == NULL) { - return; - } - pIter->countRemaining = trackCount; - pIter->pRunningData = (const char*)pTrackData; -} -MA_API ma_bool32 ma_dr_flac_next_cuesheet_track(ma_dr_flac_cuesheet_track_iterator* pIter, ma_dr_flac_cuesheet_track* pCuesheetTrack) -{ - ma_dr_flac_cuesheet_track cuesheetTrack; - const char* pRunningData; - ma_uint64 offsetHi; - ma_uint64 offsetLo; - if (pIter == NULL || pIter->countRemaining == 0 || pIter->pRunningData == NULL) { - return MA_FALSE; - } - pRunningData = pIter->pRunningData; - offsetHi = ma_dr_flac__be2host_32(*(const ma_uint32*)pRunningData); pRunningData += 4; - offsetLo = ma_dr_flac__be2host_32(*(const ma_uint32*)pRunningData); pRunningData += 4; - cuesheetTrack.offset = offsetLo | (offsetHi << 32); - cuesheetTrack.trackNumber = pRunningData[0]; pRunningData += 1; - MA_DR_FLAC_COPY_MEMORY(cuesheetTrack.ISRC, pRunningData, sizeof(cuesheetTrack.ISRC)); pRunningData += 12; - cuesheetTrack.isAudio = (pRunningData[0] & 0x80) != 0; - cuesheetTrack.preEmphasis = (pRunningData[0] & 0x40) != 0; pRunningData += 14; - cuesheetTrack.indexCount = pRunningData[0]; pRunningData += 1; - cuesheetTrack.pIndexPoints = (const ma_dr_flac_cuesheet_track_index*)pRunningData; pRunningData += cuesheetTrack.indexCount * sizeof(ma_dr_flac_cuesheet_track_index); - pIter->pRunningData = pRunningData; - pIter->countRemaining -= 1; - if (pCuesheetTrack) { - *pCuesheetTrack = cuesheetTrack; - } - return MA_TRUE; -} -#if defined(__clang__) || (defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6))) - #pragma GCC diagnostic pop -#endif -#endif -/* dr_flac_c end */ -#endif /* MA_DR_FLAC_IMPLEMENTATION */ -#endif /* MA_NO_FLAC */ - -#if !defined(MA_NO_MP3) && !defined(MA_NO_DECODING) -#if !defined(MA_DR_MP3_IMPLEMENTATION) -/* dr_mp3_c begin */ -#ifndef ma_dr_mp3_c -#define ma_dr_mp3_c -#include -#include -#include -MA_API void ma_dr_mp3_version(ma_uint32* pMajor, ma_uint32* pMinor, ma_uint32* pRevision) -{ - if (pMajor) { - *pMajor = MA_DR_MP3_VERSION_MAJOR; - } - if (pMinor) { - *pMinor = MA_DR_MP3_VERSION_MINOR; - } - if (pRevision) { - *pRevision = MA_DR_MP3_VERSION_REVISION; - } -} -MA_API const char* ma_dr_mp3_version_string(void) -{ - return MA_DR_MP3_VERSION_STRING; -} -#if defined(__TINYC__) -#define MA_DR_MP3_NO_SIMD -#endif -#define MA_DR_MP3_OFFSET_PTR(p, offset) ((void*)((ma_uint8*)(p) + (offset))) -#define MA_DR_MP3_MAX_FREE_FORMAT_FRAME_SIZE 2304 -#ifndef MA_DR_MP3_MAX_FRAME_SYNC_MATCHES -#define MA_DR_MP3_MAX_FRAME_SYNC_MATCHES 10 -#endif -#define MA_DR_MP3_MAX_L3_FRAME_PAYLOAD_BYTES MA_DR_MP3_MAX_FREE_FORMAT_FRAME_SIZE -#define MA_DR_MP3_MAX_BITRESERVOIR_BYTES 511 -#define MA_DR_MP3_SHORT_BLOCK_TYPE 2 -#define MA_DR_MP3_STOP_BLOCK_TYPE 3 -#define MA_DR_MP3_MODE_MONO 3 -#define MA_DR_MP3_MODE_JOINT_STEREO 1 -#define MA_DR_MP3_HDR_SIZE 4 -#define MA_DR_MP3_HDR_IS_MONO(h) (((h[3]) & 0xC0) == 0xC0) -#define MA_DR_MP3_HDR_IS_MS_STEREO(h) (((h[3]) & 0xE0) == 0x60) -#define MA_DR_MP3_HDR_IS_FREE_FORMAT(h) (((h[2]) & 0xF0) == 0) -#define MA_DR_MP3_HDR_IS_CRC(h) (!((h[1]) & 1)) -#define MA_DR_MP3_HDR_TEST_PADDING(h) ((h[2]) & 0x2) -#define MA_DR_MP3_HDR_TEST_MPEG1(h) ((h[1]) & 0x8) -#define MA_DR_MP3_HDR_TEST_NOT_MPEG25(h) ((h[1]) & 0x10) -#define MA_DR_MP3_HDR_TEST_I_STEREO(h) ((h[3]) & 0x10) -#define MA_DR_MP3_HDR_TEST_MS_STEREO(h) ((h[3]) & 0x20) -#define MA_DR_MP3_HDR_GET_STEREO_MODE(h) (((h[3]) >> 6) & 3) -#define MA_DR_MP3_HDR_GET_STEREO_MODE_EXT(h) (((h[3]) >> 4) & 3) -#define MA_DR_MP3_HDR_GET_LAYER(h) (((h[1]) >> 1) & 3) -#define MA_DR_MP3_HDR_GET_BITRATE(h) ((h[2]) >> 4) -#define MA_DR_MP3_HDR_GET_SAMPLE_RATE(h) (((h[2]) >> 2) & 3) -#define MA_DR_MP3_HDR_GET_MY_SAMPLE_RATE(h) (MA_DR_MP3_HDR_GET_SAMPLE_RATE(h) + (((h[1] >> 3) & 1) + ((h[1] >> 4) & 1))*3) -#define MA_DR_MP3_HDR_IS_FRAME_576(h) ((h[1] & 14) == 2) -#define MA_DR_MP3_HDR_IS_LAYER_1(h) ((h[1] & 6) == 6) -#define MA_DR_MP3_BITS_DEQUANTIZER_OUT -1 -#define MA_DR_MP3_MAX_SCF (255 + MA_DR_MP3_BITS_DEQUANTIZER_OUT*4 - 210) -#define MA_DR_MP3_MAX_SCFI ((MA_DR_MP3_MAX_SCF + 3) & ~3) -#define MA_DR_MP3_MIN(a, b) ((a) > (b) ? (b) : (a)) -#define MA_DR_MP3_MAX(a, b) ((a) < (b) ? (b) : (a)) -#if !defined(MA_DR_MP3_NO_SIMD) -#if !defined(MA_DR_MP3_ONLY_SIMD) && (defined(_M_X64) || defined(__x86_64__) || defined(__aarch64__) || defined(_M_ARM64) || defined(_M_ARM64EC)) -#define MA_DR_MP3_ONLY_SIMD -#endif -#if ((defined(_MSC_VER) && _MSC_VER >= 1400) && defined(_M_X64)) || ((defined(__i386) || defined(_M_IX86) || defined(__i386__) || defined(__x86_64__)) && ((defined(_M_IX86_FP) && _M_IX86_FP == 2) || defined(__SSE2__))) -#if defined(_MSC_VER) -#include -#endif -#include -#define MA_DR_MP3_HAVE_SSE 1 -#define MA_DR_MP3_HAVE_SIMD 1 -#define MA_DR_MP3_VSTORE _mm_storeu_ps -#define MA_DR_MP3_VLD _mm_loadu_ps -#define MA_DR_MP3_VSET _mm_set1_ps -#define MA_DR_MP3_VADD _mm_add_ps -#define MA_DR_MP3_VSUB _mm_sub_ps -#define MA_DR_MP3_VMUL _mm_mul_ps -#define MA_DR_MP3_VMAC(a, x, y) _mm_add_ps(a, _mm_mul_ps(x, y)) -#define MA_DR_MP3_VMSB(a, x, y) _mm_sub_ps(a, _mm_mul_ps(x, y)) -#define MA_DR_MP3_VMUL_S(x, s) _mm_mul_ps(x, _mm_set1_ps(s)) -#define MA_DR_MP3_VREV(x) _mm_shuffle_ps(x, x, _MM_SHUFFLE(0, 1, 2, 3)) -typedef __m128 ma_dr_mp3_f4; -#if defined(_MSC_VER) || defined(MA_DR_MP3_ONLY_SIMD) -#define ma_dr_mp3_cpuid __cpuid -#else -static __inline__ __attribute__((always_inline)) void ma_dr_mp3_cpuid(int CPUInfo[], const int InfoType) -{ -#if defined(__PIC__) - __asm__ __volatile__( -#if defined(__x86_64__) - "push %%rbx\n" - "cpuid\n" - "xchgl %%ebx, %1\n" - "pop %%rbx\n" -#else - "xchgl %%ebx, %1\n" - "cpuid\n" - "xchgl %%ebx, %1\n" -#endif - : "=a" (CPUInfo[0]), "=r" (CPUInfo[1]), "=c" (CPUInfo[2]), "=d" (CPUInfo[3]) - : "a" (InfoType)); -#else - __asm__ __volatile__( - "cpuid" - : "=a" (CPUInfo[0]), "=b" (CPUInfo[1]), "=c" (CPUInfo[2]), "=d" (CPUInfo[3]) - : "a" (InfoType)); -#endif -} -#endif -static int ma_dr_mp3_have_simd(void) -{ -#ifdef MA_DR_MP3_ONLY_SIMD - return 1; -#else - static int g_have_simd; - int CPUInfo[4]; -#ifdef MINIMP3_TEST - static int g_counter; - if (g_counter++ > 100) - return 0; -#endif - if (g_have_simd) - goto end; - ma_dr_mp3_cpuid(CPUInfo, 0); - if (CPUInfo[0] > 0) - { - ma_dr_mp3_cpuid(CPUInfo, 1); - g_have_simd = (CPUInfo[3] & (1 << 26)) + 1; - return g_have_simd - 1; - } -end: - return g_have_simd - 1; -#endif -} -#elif defined(__ARM_NEON) || defined(__aarch64__) || defined(_M_ARM64) || defined(_M_ARM64EC) -#include -#define MA_DR_MP3_HAVE_SSE 0 -#define MA_DR_MP3_HAVE_SIMD 1 -#define MA_DR_MP3_VSTORE vst1q_f32 -#define MA_DR_MP3_VLD vld1q_f32 -#define MA_DR_MP3_VSET vmovq_n_f32 -#define MA_DR_MP3_VADD vaddq_f32 -#define MA_DR_MP3_VSUB vsubq_f32 -#define MA_DR_MP3_VMUL vmulq_f32 -#define MA_DR_MP3_VMAC(a, x, y) vmlaq_f32(a, x, y) -#define MA_DR_MP3_VMSB(a, x, y) vmlsq_f32(a, x, y) -#define MA_DR_MP3_VMUL_S(x, s) vmulq_f32(x, vmovq_n_f32(s)) -#define MA_DR_MP3_VREV(x) vcombine_f32(vget_high_f32(vrev64q_f32(x)), vget_low_f32(vrev64q_f32(x))) -typedef float32x4_t ma_dr_mp3_f4; -static int ma_dr_mp3_have_simd(void) -{ - return 1; -} -#else -#define MA_DR_MP3_HAVE_SSE 0 -#define MA_DR_MP3_HAVE_SIMD 0 -#ifdef MA_DR_MP3_ONLY_SIMD -#error MA_DR_MP3_ONLY_SIMD used, but SSE/NEON not enabled -#endif -#endif -#else -#define MA_DR_MP3_HAVE_SIMD 0 -#endif -#if defined(__ARM_ARCH) && (__ARM_ARCH >= 6) && !defined(__aarch64__) && !defined(_M_ARM64) && !defined(_M_ARM64EC) && !defined(__ARM_ARCH_6M__) -#define MA_DR_MP3_HAVE_ARMV6 1 -static __inline__ __attribute__((always_inline)) ma_int32 ma_dr_mp3_clip_int16_arm(ma_int32 a) -{ - ma_int32 x = 0; - __asm__ ("ssat %0, #16, %1" : "=r"(x) : "r"(a)); - return x; -} -#else -#define MA_DR_MP3_HAVE_ARMV6 0 -#endif -#ifndef MA_DR_MP3_ASSERT -#include -#define MA_DR_MP3_ASSERT(expression) assert(expression) -#endif -#ifndef MA_DR_MP3_COPY_MEMORY -#define MA_DR_MP3_COPY_MEMORY(dst, src, sz) memcpy((dst), (src), (sz)) -#endif -#ifndef MA_DR_MP3_MOVE_MEMORY -#define MA_DR_MP3_MOVE_MEMORY(dst, src, sz) memmove((dst), (src), (sz)) -#endif -#ifndef MA_DR_MP3_ZERO_MEMORY -#define MA_DR_MP3_ZERO_MEMORY(p, sz) memset((p), 0, (sz)) -#endif -#define MA_DR_MP3_ZERO_OBJECT(p) MA_DR_MP3_ZERO_MEMORY((p), sizeof(*(p))) -#ifndef MA_DR_MP3_MALLOC -#define MA_DR_MP3_MALLOC(sz) malloc((sz)) -#endif -#ifndef MA_DR_MP3_REALLOC -#define MA_DR_MP3_REALLOC(p, sz) realloc((p), (sz)) -#endif -#ifndef MA_DR_MP3_FREE -#define MA_DR_MP3_FREE(p) free((p)) -#endif -typedef struct -{ - const ma_uint8 *buf; - int pos, limit; -} ma_dr_mp3_bs; -typedef struct -{ - float scf[3*64]; - ma_uint8 total_bands, stereo_bands, bitalloc[64], scfcod[64]; -} ma_dr_mp3_L12_scale_info; -typedef struct -{ - ma_uint8 tab_offset, code_tab_width, band_count; -} ma_dr_mp3_L12_subband_alloc; -typedef struct -{ - const ma_uint8 *sfbtab; - ma_uint16 part_23_length, big_values, scalefac_compress; - ma_uint8 global_gain, block_type, mixed_block_flag, n_long_sfb, n_short_sfb; - ma_uint8 table_select[3], region_count[3], subblock_gain[3]; - ma_uint8 preflag, scalefac_scale, count1_table, scfsi; -} ma_dr_mp3_L3_gr_info; -typedef struct -{ - ma_dr_mp3_bs bs; - ma_uint8 maindata[MA_DR_MP3_MAX_BITRESERVOIR_BYTES + MA_DR_MP3_MAX_L3_FRAME_PAYLOAD_BYTES]; - ma_dr_mp3_L3_gr_info gr_info[4]; - float grbuf[2][576], scf[40], syn[18 + 15][2*32]; - ma_uint8 ist_pos[2][39]; -} ma_dr_mp3dec_scratch; -static void ma_dr_mp3_bs_init(ma_dr_mp3_bs *bs, const ma_uint8 *data, int bytes) -{ - bs->buf = data; - bs->pos = 0; - bs->limit = bytes*8; -} -static ma_uint32 ma_dr_mp3_bs_get_bits(ma_dr_mp3_bs *bs, int n) -{ - ma_uint32 next, cache = 0, s = bs->pos & 7; - int shl = n + s; - const ma_uint8 *p = bs->buf + (bs->pos >> 3); - if ((bs->pos += n) > bs->limit) - return 0; - next = *p++ & (255 >> s); - while ((shl -= 8) > 0) - { - cache |= next << shl; - next = *p++; - } - return cache | (next >> -shl); -} -static int ma_dr_mp3_hdr_valid(const ma_uint8 *h) -{ - return h[0] == 0xff && - ((h[1] & 0xF0) == 0xf0 || (h[1] & 0xFE) == 0xe2) && - (MA_DR_MP3_HDR_GET_LAYER(h) != 0) && - (MA_DR_MP3_HDR_GET_BITRATE(h) != 15) && - (MA_DR_MP3_HDR_GET_SAMPLE_RATE(h) != 3); -} -static int ma_dr_mp3_hdr_compare(const ma_uint8 *h1, const ma_uint8 *h2) -{ - return ma_dr_mp3_hdr_valid(h2) && - ((h1[1] ^ h2[1]) & 0xFE) == 0 && - ((h1[2] ^ h2[2]) & 0x0C) == 0 && - !(MA_DR_MP3_HDR_IS_FREE_FORMAT(h1) ^ MA_DR_MP3_HDR_IS_FREE_FORMAT(h2)); -} -static unsigned ma_dr_mp3_hdr_bitrate_kbps(const ma_uint8 *h) -{ - static const ma_uint8 halfrate[2][3][15] = { - { { 0,4,8,12,16,20,24,28,32,40,48,56,64,72,80 }, { 0,4,8,12,16,20,24,28,32,40,48,56,64,72,80 }, { 0,16,24,28,32,40,48,56,64,72,80,88,96,112,128 } }, - { { 0,16,20,24,28,32,40,48,56,64,80,96,112,128,160 }, { 0,16,24,28,32,40,48,56,64,80,96,112,128,160,192 }, { 0,16,32,48,64,80,96,112,128,144,160,176,192,208,224 } }, - }; - return 2*halfrate[!!MA_DR_MP3_HDR_TEST_MPEG1(h)][MA_DR_MP3_HDR_GET_LAYER(h) - 1][MA_DR_MP3_HDR_GET_BITRATE(h)]; -} -static unsigned ma_dr_mp3_hdr_sample_rate_hz(const ma_uint8 *h) -{ - static const unsigned g_hz[3] = { 44100, 48000, 32000 }; - return g_hz[MA_DR_MP3_HDR_GET_SAMPLE_RATE(h)] >> (int)!MA_DR_MP3_HDR_TEST_MPEG1(h) >> (int)!MA_DR_MP3_HDR_TEST_NOT_MPEG25(h); -} -static unsigned ma_dr_mp3_hdr_frame_samples(const ma_uint8 *h) -{ - return MA_DR_MP3_HDR_IS_LAYER_1(h) ? 384 : (1152 >> (int)MA_DR_MP3_HDR_IS_FRAME_576(h)); -} -static int ma_dr_mp3_hdr_frame_bytes(const ma_uint8 *h, int free_format_size) -{ - int frame_bytes = ma_dr_mp3_hdr_frame_samples(h)*ma_dr_mp3_hdr_bitrate_kbps(h)*125/ma_dr_mp3_hdr_sample_rate_hz(h); - if (MA_DR_MP3_HDR_IS_LAYER_1(h)) - { - frame_bytes &= ~3; - } - return frame_bytes ? frame_bytes : free_format_size; -} -static int ma_dr_mp3_hdr_padding(const ma_uint8 *h) -{ - return MA_DR_MP3_HDR_TEST_PADDING(h) ? (MA_DR_MP3_HDR_IS_LAYER_1(h) ? 4 : 1) : 0; -} -#ifndef MA_DR_MP3_ONLY_MP3 -static const ma_dr_mp3_L12_subband_alloc *ma_dr_mp3_L12_subband_alloc_table(const ma_uint8 *hdr, ma_dr_mp3_L12_scale_info *sci) -{ - const ma_dr_mp3_L12_subband_alloc *alloc; - int mode = MA_DR_MP3_HDR_GET_STEREO_MODE(hdr); - int nbands, stereo_bands = (mode == MA_DR_MP3_MODE_MONO) ? 0 : (mode == MA_DR_MP3_MODE_JOINT_STEREO) ? (MA_DR_MP3_HDR_GET_STEREO_MODE_EXT(hdr) << 2) + 4 : 32; - if (MA_DR_MP3_HDR_IS_LAYER_1(hdr)) - { - static const ma_dr_mp3_L12_subband_alloc g_alloc_L1[] = { { 76, 4, 32 } }; - alloc = g_alloc_L1; - nbands = 32; - } else if (!MA_DR_MP3_HDR_TEST_MPEG1(hdr)) - { - static const ma_dr_mp3_L12_subband_alloc g_alloc_L2M2[] = { { 60, 4, 4 }, { 44, 3, 7 }, { 44, 2, 19 } }; - alloc = g_alloc_L2M2; - nbands = 30; - } else - { - static const ma_dr_mp3_L12_subband_alloc g_alloc_L2M1[] = { { 0, 4, 3 }, { 16, 4, 8 }, { 32, 3, 12 }, { 40, 2, 7 } }; - int sample_rate_idx = MA_DR_MP3_HDR_GET_SAMPLE_RATE(hdr); - unsigned kbps = ma_dr_mp3_hdr_bitrate_kbps(hdr) >> (int)(mode != MA_DR_MP3_MODE_MONO); - if (!kbps) - { - kbps = 192; - } - alloc = g_alloc_L2M1; - nbands = 27; - if (kbps < 56) - { - static const ma_dr_mp3_L12_subband_alloc g_alloc_L2M1_lowrate[] = { { 44, 4, 2 }, { 44, 3, 10 } }; - alloc = g_alloc_L2M1_lowrate; - nbands = sample_rate_idx == 2 ? 12 : 8; - } else if (kbps >= 96 && sample_rate_idx != 1) - { - nbands = 30; - } - } - sci->total_bands = (ma_uint8)nbands; - sci->stereo_bands = (ma_uint8)MA_DR_MP3_MIN(stereo_bands, nbands); - return alloc; -} -static void ma_dr_mp3_L12_read_scalefactors(ma_dr_mp3_bs *bs, ma_uint8 *pba, ma_uint8 *scfcod, int bands, float *scf) -{ - static const float g_deq_L12[18*3] = { -#define MA_DR_MP3_DQ(x) 9.53674316e-07f/x, 7.56931807e-07f/x, 6.00777173e-07f/x - MA_DR_MP3_DQ(3),MA_DR_MP3_DQ(7),MA_DR_MP3_DQ(15),MA_DR_MP3_DQ(31),MA_DR_MP3_DQ(63),MA_DR_MP3_DQ(127),MA_DR_MP3_DQ(255),MA_DR_MP3_DQ(511),MA_DR_MP3_DQ(1023),MA_DR_MP3_DQ(2047),MA_DR_MP3_DQ(4095),MA_DR_MP3_DQ(8191),MA_DR_MP3_DQ(16383),MA_DR_MP3_DQ(32767),MA_DR_MP3_DQ(65535),MA_DR_MP3_DQ(3),MA_DR_MP3_DQ(5),MA_DR_MP3_DQ(9) - }; - int i, m; - for (i = 0; i < bands; i++) - { - float s = 0; - int ba = *pba++; - int mask = ba ? 4 + ((19 >> scfcod[i]) & 3) : 0; - for (m = 4; m; m >>= 1) - { - if (mask & m) - { - int b = ma_dr_mp3_bs_get_bits(bs, 6); - s = g_deq_L12[ba*3 - 6 + b % 3]*(int)(1 << 21 >> b/3); - } - *scf++ = s; - } - } -} -static void ma_dr_mp3_L12_read_scale_info(const ma_uint8 *hdr, ma_dr_mp3_bs *bs, ma_dr_mp3_L12_scale_info *sci) -{ - static const ma_uint8 g_bitalloc_code_tab[] = { - 0,17, 3, 4, 5,6,7, 8,9,10,11,12,13,14,15,16, - 0,17,18, 3,19,4,5, 6,7, 8, 9,10,11,12,13,16, - 0,17,18, 3,19,4,5,16, - 0,17,18,16, - 0,17,18,19, 4,5,6, 7,8, 9,10,11,12,13,14,15, - 0,17,18, 3,19,4,5, 6,7, 8, 9,10,11,12,13,14, - 0, 2, 3, 4, 5,6,7, 8,9,10,11,12,13,14,15,16 - }; - const ma_dr_mp3_L12_subband_alloc *subband_alloc = ma_dr_mp3_L12_subband_alloc_table(hdr, sci); - int i, k = 0, ba_bits = 0; - const ma_uint8 *ba_code_tab = g_bitalloc_code_tab; - for (i = 0; i < sci->total_bands; i++) - { - ma_uint8 ba; - if (i == k) - { - k += subband_alloc->band_count; - ba_bits = subband_alloc->code_tab_width; - ba_code_tab = g_bitalloc_code_tab + subband_alloc->tab_offset; - subband_alloc++; - } - ba = ba_code_tab[ma_dr_mp3_bs_get_bits(bs, ba_bits)]; - sci->bitalloc[2*i] = ba; - if (i < sci->stereo_bands) - { - ba = ba_code_tab[ma_dr_mp3_bs_get_bits(bs, ba_bits)]; - } - sci->bitalloc[2*i + 1] = sci->stereo_bands ? ba : 0; - } - for (i = 0; i < 2*sci->total_bands; i++) - { - sci->scfcod[i] = (ma_uint8)(sci->bitalloc[i] ? MA_DR_MP3_HDR_IS_LAYER_1(hdr) ? 2 : ma_dr_mp3_bs_get_bits(bs, 2) : 6); - } - ma_dr_mp3_L12_read_scalefactors(bs, sci->bitalloc, sci->scfcod, sci->total_bands*2, sci->scf); - for (i = sci->stereo_bands; i < sci->total_bands; i++) - { - sci->bitalloc[2*i + 1] = 0; - } -} -static int ma_dr_mp3_L12_dequantize_granule(float *grbuf, ma_dr_mp3_bs *bs, ma_dr_mp3_L12_scale_info *sci, int group_size) -{ - int i, j, k, choff = 576; - for (j = 0; j < 4; j++) - { - float *dst = grbuf + group_size*j; - for (i = 0; i < 2*sci->total_bands; i++) - { - int ba = sci->bitalloc[i]; - if (ba != 0) - { - if (ba < 17) - { - int half = (1 << (ba - 1)) - 1; - for (k = 0; k < group_size; k++) - { - dst[k] = (float)((int)ma_dr_mp3_bs_get_bits(bs, ba) - half); - } - } else - { - unsigned mod = (2 << (ba - 17)) + 1; - unsigned code = ma_dr_mp3_bs_get_bits(bs, mod + 2 - (mod >> 3)); - for (k = 0; k < group_size; k++, code /= mod) - { - dst[k] = (float)((int)(code % mod - mod/2)); - } - } - } - dst += choff; - choff = 18 - choff; - } - } - return group_size*4; -} -static void ma_dr_mp3_L12_apply_scf_384(ma_dr_mp3_L12_scale_info *sci, const float *scf, float *dst) -{ - int i, k; - MA_DR_MP3_COPY_MEMORY(dst + 576 + sci->stereo_bands*18, dst + sci->stereo_bands*18, (sci->total_bands - sci->stereo_bands)*18*sizeof(float)); - for (i = 0; i < sci->total_bands; i++, dst += 18, scf += 6) - { - for (k = 0; k < 12; k++) - { - dst[k + 0] *= scf[0]; - dst[k + 576] *= scf[3]; - } - } -} -#endif -static int ma_dr_mp3_L3_read_side_info(ma_dr_mp3_bs *bs, ma_dr_mp3_L3_gr_info *gr, const ma_uint8 *hdr) -{ - static const ma_uint8 g_scf_long[8][23] = { - { 6,6,6,6,6,6,8,10,12,14,16,20,24,28,32,38,46,52,60,68,58,54,0 }, - { 12,12,12,12,12,12,16,20,24,28,32,40,48,56,64,76,90,2,2,2,2,2,0 }, - { 6,6,6,6,6,6,8,10,12,14,16,20,24,28,32,38,46,52,60,68,58,54,0 }, - { 6,6,6,6,6,6,8,10,12,14,16,18,22,26,32,38,46,54,62,70,76,36,0 }, - { 6,6,6,6,6,6,8,10,12,14,16,20,24,28,32,38,46,52,60,68,58,54,0 }, - { 4,4,4,4,4,4,6,6,8,8,10,12,16,20,24,28,34,42,50,54,76,158,0 }, - { 4,4,4,4,4,4,6,6,6,8,10,12,16,18,22,28,34,40,46,54,54,192,0 }, - { 4,4,4,4,4,4,6,6,8,10,12,16,20,24,30,38,46,56,68,84,102,26,0 } - }; - static const ma_uint8 g_scf_short[8][40] = { - { 4,4,4,4,4,4,4,4,4,6,6,6,8,8,8,10,10,10,12,12,12,14,14,14,18,18,18,24,24,24,30,30,30,40,40,40,18,18,18,0 }, - { 8,8,8,8,8,8,8,8,8,12,12,12,16,16,16,20,20,20,24,24,24,28,28,28,36,36,36,2,2,2,2,2,2,2,2,2,26,26,26,0 }, - { 4,4,4,4,4,4,4,4,4,6,6,6,6,6,6,8,8,8,10,10,10,14,14,14,18,18,18,26,26,26,32,32,32,42,42,42,18,18,18,0 }, - { 4,4,4,4,4,4,4,4,4,6,6,6,8,8,8,10,10,10,12,12,12,14,14,14,18,18,18,24,24,24,32,32,32,44,44,44,12,12,12,0 }, - { 4,4,4,4,4,4,4,4,4,6,6,6,8,8,8,10,10,10,12,12,12,14,14,14,18,18,18,24,24,24,30,30,30,40,40,40,18,18,18,0 }, - { 4,4,4,4,4,4,4,4,4,4,4,4,6,6,6,8,8,8,10,10,10,12,12,12,14,14,14,18,18,18,22,22,22,30,30,30,56,56,56,0 }, - { 4,4,4,4,4,4,4,4,4,4,4,4,6,6,6,6,6,6,10,10,10,12,12,12,14,14,14,16,16,16,20,20,20,26,26,26,66,66,66,0 }, - { 4,4,4,4,4,4,4,4,4,4,4,4,6,6,6,8,8,8,12,12,12,16,16,16,20,20,20,26,26,26,34,34,34,42,42,42,12,12,12,0 } - }; - static const ma_uint8 g_scf_mixed[8][40] = { - { 6,6,6,6,6,6,6,6,6,8,8,8,10,10,10,12,12,12,14,14,14,18,18,18,24,24,24,30,30,30,40,40,40,18,18,18,0 }, - { 12,12,12,4,4,4,8,8,8,12,12,12,16,16,16,20,20,20,24,24,24,28,28,28,36,36,36,2,2,2,2,2,2,2,2,2,26,26,26,0 }, - { 6,6,6,6,6,6,6,6,6,6,6,6,8,8,8,10,10,10,14,14,14,18,18,18,26,26,26,32,32,32,42,42,42,18,18,18,0 }, - { 6,6,6,6,6,6,6,6,6,8,8,8,10,10,10,12,12,12,14,14,14,18,18,18,24,24,24,32,32,32,44,44,44,12,12,12,0 }, - { 6,6,6,6,6,6,6,6,6,8,8,8,10,10,10,12,12,12,14,14,14,18,18,18,24,24,24,30,30,30,40,40,40,18,18,18,0 }, - { 4,4,4,4,4,4,6,6,4,4,4,6,6,6,8,8,8,10,10,10,12,12,12,14,14,14,18,18,18,22,22,22,30,30,30,56,56,56,0 }, - { 4,4,4,4,4,4,6,6,4,4,4,6,6,6,6,6,6,10,10,10,12,12,12,14,14,14,16,16,16,20,20,20,26,26,26,66,66,66,0 }, - { 4,4,4,4,4,4,6,6,4,4,4,6,6,6,8,8,8,12,12,12,16,16,16,20,20,20,26,26,26,34,34,34,42,42,42,12,12,12,0 } - }; - unsigned tables, scfsi = 0; - int main_data_begin, part_23_sum = 0; - int gr_count = MA_DR_MP3_HDR_IS_MONO(hdr) ? 1 : 2; - int sr_idx = MA_DR_MP3_HDR_GET_MY_SAMPLE_RATE(hdr); sr_idx -= (sr_idx != 0); - if (MA_DR_MP3_HDR_TEST_MPEG1(hdr)) - { - gr_count *= 2; - main_data_begin = ma_dr_mp3_bs_get_bits(bs, 9); - scfsi = ma_dr_mp3_bs_get_bits(bs, 7 + gr_count); - } else - { - main_data_begin = ma_dr_mp3_bs_get_bits(bs, 8 + gr_count) >> gr_count; - } - do - { - if (MA_DR_MP3_HDR_IS_MONO(hdr)) - { - scfsi <<= 4; - } - gr->part_23_length = (ma_uint16)ma_dr_mp3_bs_get_bits(bs, 12); - part_23_sum += gr->part_23_length; - gr->big_values = (ma_uint16)ma_dr_mp3_bs_get_bits(bs, 9); - if (gr->big_values > 288) - { - return -1; - } - gr->global_gain = (ma_uint8)ma_dr_mp3_bs_get_bits(bs, 8); - gr->scalefac_compress = (ma_uint16)ma_dr_mp3_bs_get_bits(bs, MA_DR_MP3_HDR_TEST_MPEG1(hdr) ? 4 : 9); - gr->sfbtab = g_scf_long[sr_idx]; - gr->n_long_sfb = 22; - gr->n_short_sfb = 0; - if (ma_dr_mp3_bs_get_bits(bs, 1)) - { - gr->block_type = (ma_uint8)ma_dr_mp3_bs_get_bits(bs, 2); - if (!gr->block_type) - { - return -1; - } - gr->mixed_block_flag = (ma_uint8)ma_dr_mp3_bs_get_bits(bs, 1); - gr->region_count[0] = 7; - gr->region_count[1] = 255; - if (gr->block_type == MA_DR_MP3_SHORT_BLOCK_TYPE) - { - scfsi &= 0x0F0F; - if (!gr->mixed_block_flag) - { - gr->region_count[0] = 8; - gr->sfbtab = g_scf_short[sr_idx]; - gr->n_long_sfb = 0; - gr->n_short_sfb = 39; - } else - { - gr->sfbtab = g_scf_mixed[sr_idx]; - gr->n_long_sfb = MA_DR_MP3_HDR_TEST_MPEG1(hdr) ? 8 : 6; - gr->n_short_sfb = 30; - } - } - tables = ma_dr_mp3_bs_get_bits(bs, 10); - tables <<= 5; - gr->subblock_gain[0] = (ma_uint8)ma_dr_mp3_bs_get_bits(bs, 3); - gr->subblock_gain[1] = (ma_uint8)ma_dr_mp3_bs_get_bits(bs, 3); - gr->subblock_gain[2] = (ma_uint8)ma_dr_mp3_bs_get_bits(bs, 3); - } else - { - gr->block_type = 0; - gr->mixed_block_flag = 0; - tables = ma_dr_mp3_bs_get_bits(bs, 15); - gr->region_count[0] = (ma_uint8)ma_dr_mp3_bs_get_bits(bs, 4); - gr->region_count[1] = (ma_uint8)ma_dr_mp3_bs_get_bits(bs, 3); - gr->region_count[2] = 255; - } - gr->table_select[0] = (ma_uint8)(tables >> 10); - gr->table_select[1] = (ma_uint8)((tables >> 5) & 31); - gr->table_select[2] = (ma_uint8)((tables) & 31); - gr->preflag = (ma_uint8)(MA_DR_MP3_HDR_TEST_MPEG1(hdr) ? ma_dr_mp3_bs_get_bits(bs, 1) : (gr->scalefac_compress >= 500)); - gr->scalefac_scale = (ma_uint8)ma_dr_mp3_bs_get_bits(bs, 1); - gr->count1_table = (ma_uint8)ma_dr_mp3_bs_get_bits(bs, 1); - gr->scfsi = (ma_uint8)((scfsi >> 12) & 15); - scfsi <<= 4; - gr++; - } while(--gr_count); - if (part_23_sum + bs->pos > bs->limit + main_data_begin*8) - { - return -1; - } - return main_data_begin; -} -static void ma_dr_mp3_L3_read_scalefactors(ma_uint8 *scf, ma_uint8 *ist_pos, const ma_uint8 *scf_size, const ma_uint8 *scf_count, ma_dr_mp3_bs *bitbuf, int scfsi) -{ - int i, k; - for (i = 0; i < 4 && scf_count[i]; i++, scfsi *= 2) - { - int cnt = scf_count[i]; - if (scfsi & 8) - { - MA_DR_MP3_COPY_MEMORY(scf, ist_pos, cnt); - } else - { - int bits = scf_size[i]; - if (!bits) - { - MA_DR_MP3_ZERO_MEMORY(scf, cnt); - MA_DR_MP3_ZERO_MEMORY(ist_pos, cnt); - } else - { - int max_scf = (scfsi < 0) ? (1 << bits) - 1 : -1; - for (k = 0; k < cnt; k++) - { - int s = ma_dr_mp3_bs_get_bits(bitbuf, bits); - ist_pos[k] = (ma_uint8)(s == max_scf ? -1 : s); - scf[k] = (ma_uint8)s; - } - } - } - ist_pos += cnt; - scf += cnt; - } - scf[0] = scf[1] = scf[2] = 0; -} -static float ma_dr_mp3_L3_ldexp_q2(float y, int exp_q2) -{ - static const float g_expfrac[4] = { 9.31322575e-10f,7.83145814e-10f,6.58544508e-10f,5.53767716e-10f }; - int e; - do - { - e = MA_DR_MP3_MIN(30*4, exp_q2); - y *= g_expfrac[e & 3]*(1 << 30 >> (e >> 2)); - } while ((exp_q2 -= e) > 0); - return y; -} -static void ma_dr_mp3_L3_decode_scalefactors(const ma_uint8 *hdr, ma_uint8 *ist_pos, ma_dr_mp3_bs *bs, const ma_dr_mp3_L3_gr_info *gr, float *scf, int ch) -{ - static const ma_uint8 g_scf_partitions[3][28] = { - { 6,5,5, 5,6,5,5,5,6,5, 7,3,11,10,0,0, 7, 7, 7,0, 6, 6,6,3, 8, 8,5,0 }, - { 8,9,6,12,6,9,9,9,6,9,12,6,15,18,0,0, 6,15,12,0, 6,12,9,6, 6,18,9,0 }, - { 9,9,6,12,9,9,9,9,9,9,12,6,18,18,0,0,12,12,12,0,12, 9,9,6,15,12,9,0 } - }; - const ma_uint8 *scf_partition = g_scf_partitions[!!gr->n_short_sfb + !gr->n_long_sfb]; - ma_uint8 scf_size[4], iscf[40]; - int i, scf_shift = gr->scalefac_scale + 1, gain_exp, scfsi = gr->scfsi; - float gain; - if (MA_DR_MP3_HDR_TEST_MPEG1(hdr)) - { - static const ma_uint8 g_scfc_decode[16] = { 0,1,2,3, 12,5,6,7, 9,10,11,13, 14,15,18,19 }; - int part = g_scfc_decode[gr->scalefac_compress]; - scf_size[1] = scf_size[0] = (ma_uint8)(part >> 2); - scf_size[3] = scf_size[2] = (ma_uint8)(part & 3); - } else - { - static const ma_uint8 g_mod[6*4] = { 5,5,4,4,5,5,4,1,4,3,1,1,5,6,6,1,4,4,4,1,4,3,1,1 }; - int k, modprod, sfc, ist = MA_DR_MP3_HDR_TEST_I_STEREO(hdr) && ch; - sfc = gr->scalefac_compress >> ist; - for (k = ist*3*4; sfc >= 0; sfc -= modprod, k += 4) - { - for (modprod = 1, i = 3; i >= 0; i--) - { - scf_size[i] = (ma_uint8)(sfc / modprod % g_mod[k + i]); - modprod *= g_mod[k + i]; - } - } - scf_partition += k; - scfsi = -16; - } - ma_dr_mp3_L3_read_scalefactors(iscf, ist_pos, scf_size, scf_partition, bs, scfsi); - if (gr->n_short_sfb) - { - int sh = 3 - scf_shift; - for (i = 0; i < gr->n_short_sfb; i += 3) - { - iscf[gr->n_long_sfb + i + 0] = (ma_uint8)(iscf[gr->n_long_sfb + i + 0] + (gr->subblock_gain[0] << sh)); - iscf[gr->n_long_sfb + i + 1] = (ma_uint8)(iscf[gr->n_long_sfb + i + 1] + (gr->subblock_gain[1] << sh)); - iscf[gr->n_long_sfb + i + 2] = (ma_uint8)(iscf[gr->n_long_sfb + i + 2] + (gr->subblock_gain[2] << sh)); - } - } else if (gr->preflag) - { - static const ma_uint8 g_preamp[10] = { 1,1,1,1,2,2,3,3,3,2 }; - for (i = 0; i < 10; i++) - { - iscf[11 + i] = (ma_uint8)(iscf[11 + i] + g_preamp[i]); - } - } - gain_exp = gr->global_gain + MA_DR_MP3_BITS_DEQUANTIZER_OUT*4 - 210 - (MA_DR_MP3_HDR_IS_MS_STEREO(hdr) ? 2 : 0); - gain = ma_dr_mp3_L3_ldexp_q2(1 << (MA_DR_MP3_MAX_SCFI/4), MA_DR_MP3_MAX_SCFI - gain_exp); - for (i = 0; i < (int)(gr->n_long_sfb + gr->n_short_sfb); i++) - { - scf[i] = ma_dr_mp3_L3_ldexp_q2(gain, iscf[i] << scf_shift); - } -} -static const float g_ma_dr_mp3_pow43[129 + 16] = { - 0,-1,-2.519842f,-4.326749f,-6.349604f,-8.549880f,-10.902724f,-13.390518f,-16.000000f,-18.720754f,-21.544347f,-24.463781f,-27.473142f,-30.567351f,-33.741992f,-36.993181f, - 0,1,2.519842f,4.326749f,6.349604f,8.549880f,10.902724f,13.390518f,16.000000f,18.720754f,21.544347f,24.463781f,27.473142f,30.567351f,33.741992f,36.993181f,40.317474f,43.711787f,47.173345f,50.699631f,54.288352f,57.937408f,61.644865f,65.408941f,69.227979f,73.100443f,77.024898f,81.000000f,85.024491f,89.097188f,93.216975f,97.382800f,101.593667f,105.848633f,110.146801f,114.487321f,118.869381f,123.292209f,127.755065f,132.257246f,136.798076f,141.376907f,145.993119f,150.646117f,155.335327f,160.060199f,164.820202f,169.614826f,174.443577f,179.305980f,184.201575f,189.129918f,194.090580f,199.083145f,204.107210f,209.162385f,214.248292f,219.364564f,224.510845f,229.686789f,234.892058f,240.126328f,245.389280f,250.680604f,256.000000f,261.347174f,266.721841f,272.123723f,277.552547f,283.008049f,288.489971f,293.998060f,299.532071f,305.091761f,310.676898f,316.287249f,321.922592f,327.582707f,333.267377f,338.976394f,344.709550f,350.466646f,356.247482f,362.051866f,367.879608f,373.730522f,379.604427f,385.501143f,391.420496f,397.362314f,403.326427f,409.312672f,415.320884f,421.350905f,427.402579f,433.475750f,439.570269f,445.685987f,451.822757f,457.980436f,464.158883f,470.357960f,476.577530f,482.817459f,489.077615f,495.357868f,501.658090f,507.978156f,514.317941f,520.677324f,527.056184f,533.454404f,539.871867f,546.308458f,552.764065f,559.238575f,565.731879f,572.243870f,578.774440f,585.323483f,591.890898f,598.476581f,605.080431f,611.702349f,618.342238f,625.000000f,631.675540f,638.368763f,645.079578f -}; -static float ma_dr_mp3_L3_pow_43(int x) -{ - float frac; - int sign, mult = 256; - if (x < 129) - { - return g_ma_dr_mp3_pow43[16 + x]; - } - if (x < 1024) - { - mult = 16; - x <<= 3; - } - sign = 2*x & 64; - frac = (float)((x & 63) - sign) / ((x & ~63) + sign); - return g_ma_dr_mp3_pow43[16 + ((x + sign) >> 6)]*(1.f + frac*((4.f/3) + frac*(2.f/9)))*mult; -} -static void ma_dr_mp3_L3_huffman(float *dst, ma_dr_mp3_bs *bs, const ma_dr_mp3_L3_gr_info *gr_info, const float *scf, int layer3gr_limit) -{ - static const ma_int16 tabs[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 785,785,785,785,784,784,784,784,513,513,513,513,513,513,513,513,256,256,256,256,256,256,256,256,256,256,256,256,256,256,256,256, - -255,1313,1298,1282,785,785,785,785,784,784,784,784,769,769,769,769,256,256,256,256,256,256,256,256,256,256,256,256,256,256,256,256,290,288, - -255,1313,1298,1282,769,769,769,769,529,529,529,529,529,529,529,529,528,528,528,528,528,528,528,528,512,512,512,512,512,512,512,512,290,288, - -253,-318,-351,-367,785,785,785,785,784,784,784,784,769,769,769,769,256,256,256,256,256,256,256,256,256,256,256,256,256,256,256,256,819,818,547,547,275,275,275,275,561,560,515,546,289,274,288,258, - -254,-287,1329,1299,1314,1312,1057,1057,1042,1042,1026,1026,784,784,784,784,529,529,529,529,529,529,529,529,769,769,769,769,768,768,768,768,563,560,306,306,291,259, - -252,-413,-477,-542,1298,-575,1041,1041,784,784,784,784,769,769,769,769,256,256,256,256,256,256,256,256,256,256,256,256,256,256,256,256,-383,-399,1107,1092,1106,1061,849,849,789,789,1104,1091,773,773,1076,1075,341,340,325,309,834,804,577,577,532,532,516,516,832,818,803,816,561,561,531,531,515,546,289,289,288,258, - -252,-429,-493,-559,1057,1057,1042,1042,529,529,529,529,529,529,529,529,784,784,784,784,769,769,769,769,512,512,512,512,512,512,512,512,-382,1077,-415,1106,1061,1104,849,849,789,789,1091,1076,1029,1075,834,834,597,581,340,340,339,324,804,833,532,532,832,772,818,803,817,787,816,771,290,290,290,290,288,258, - -253,-349,-414,-447,-463,1329,1299,-479,1314,1312,1057,1057,1042,1042,1026,1026,785,785,785,785,784,784,784,784,769,769,769,769,768,768,768,768,-319,851,821,-335,836,850,805,849,341,340,325,336,533,533,579,579,564,564,773,832,578,548,563,516,321,276,306,291,304,259, - -251,-572,-733,-830,-863,-879,1041,1041,784,784,784,784,769,769,769,769,256,256,256,256,256,256,256,256,256,256,256,256,256,256,256,256,-511,-527,-543,1396,1351,1381,1366,1395,1335,1380,-559,1334,1138,1138,1063,1063,1350,1392,1031,1031,1062,1062,1364,1363,1120,1120,1333,1348,881,881,881,881,375,374,359,373,343,358,341,325,791,791,1123,1122,-703,1105,1045,-719,865,865,790,790,774,774,1104,1029,338,293,323,308,-799,-815,833,788,772,818,803,816,322,292,307,320,561,531,515,546,289,274,288,258, - -251,-525,-605,-685,-765,-831,-846,1298,1057,1057,1312,1282,785,785,785,785,784,784,784,784,769,769,769,769,512,512,512,512,512,512,512,512,1399,1398,1383,1367,1382,1396,1351,-511,1381,1366,1139,1139,1079,1079,1124,1124,1364,1349,1363,1333,882,882,882,882,807,807,807,807,1094,1094,1136,1136,373,341,535,535,881,775,867,822,774,-591,324,338,-671,849,550,550,866,864,609,609,293,336,534,534,789,835,773,-751,834,804,308,307,833,788,832,772,562,562,547,547,305,275,560,515,290,290, - -252,-397,-477,-557,-622,-653,-719,-735,-750,1329,1299,1314,1057,1057,1042,1042,1312,1282,1024,1024,785,785,785,785,784,784,784,784,769,769,769,769,-383,1127,1141,1111,1126,1140,1095,1110,869,869,883,883,1079,1109,882,882,375,374,807,868,838,881,791,-463,867,822,368,263,852,837,836,-543,610,610,550,550,352,336,534,534,865,774,851,821,850,805,593,533,579,564,773,832,578,578,548,548,577,577,307,276,306,291,516,560,259,259, - -250,-2107,-2507,-2764,-2909,-2974,-3007,-3023,1041,1041,1040,1040,769,769,769,769,256,256,256,256,256,256,256,256,256,256,256,256,256,256,256,256,-767,-1052,-1213,-1277,-1358,-1405,-1469,-1535,-1550,-1582,-1614,-1647,-1662,-1694,-1726,-1759,-1774,-1807,-1822,-1854,-1886,1565,-1919,-1935,-1951,-1967,1731,1730,1580,1717,-1983,1729,1564,-1999,1548,-2015,-2031,1715,1595,-2047,1714,-2063,1610,-2079,1609,-2095,1323,1323,1457,1457,1307,1307,1712,1547,1641,1700,1699,1594,1685,1625,1442,1442,1322,1322,-780,-973,-910,1279,1278,1277,1262,1276,1261,1275,1215,1260,1229,-959,974,974,989,989,-943,735,478,478,495,463,506,414,-1039,1003,958,1017,927,942,987,957,431,476,1272,1167,1228,-1183,1256,-1199,895,895,941,941,1242,1227,1212,1135,1014,1014,490,489,503,487,910,1013,985,925,863,894,970,955,1012,847,-1343,831,755,755,984,909,428,366,754,559,-1391,752,486,457,924,997,698,698,983,893,740,740,908,877,739,739,667,667,953,938,497,287,271,271,683,606,590,712,726,574,302,302,738,736,481,286,526,725,605,711,636,724,696,651,589,681,666,710,364,467,573,695,466,466,301,465,379,379,709,604,665,679,316,316,634,633,436,436,464,269,424,394,452,332,438,363,347,408,393,448,331,422,362,407,392,421,346,406,391,376,375,359,1441,1306,-2367,1290,-2383,1337,-2399,-2415,1426,1321,-2431,1411,1336,-2447,-2463,-2479,1169,1169,1049,1049,1424,1289,1412,1352,1319,-2495,1154,1154,1064,1064,1153,1153,416,390,360,404,403,389,344,374,373,343,358,372,327,357,342,311,356,326,1395,1394,1137,1137,1047,1047,1365,1392,1287,1379,1334,1364,1349,1378,1318,1363,792,792,792,792,1152,1152,1032,1032,1121,1121,1046,1046,1120,1120,1030,1030,-2895,1106,1061,1104,849,849,789,789,1091,1076,1029,1090,1060,1075,833,833,309,324,532,532,832,772,818,803,561,561,531,560,515,546,289,274,288,258, - -250,-1179,-1579,-1836,-1996,-2124,-2253,-2333,-2413,-2477,-2542,-2574,-2607,-2622,-2655,1314,1313,1298,1312,1282,785,785,785,785,1040,1040,1025,1025,768,768,768,768,-766,-798,-830,-862,-895,-911,-927,-943,-959,-975,-991,-1007,-1023,-1039,-1055,-1070,1724,1647,-1103,-1119,1631,1767,1662,1738,1708,1723,-1135,1780,1615,1779,1599,1677,1646,1778,1583,-1151,1777,1567,1737,1692,1765,1722,1707,1630,1751,1661,1764,1614,1736,1676,1763,1750,1645,1598,1721,1691,1762,1706,1582,1761,1566,-1167,1749,1629,767,766,751,765,494,494,735,764,719,749,734,763,447,447,748,718,477,506,431,491,446,476,461,505,415,430,475,445,504,399,460,489,414,503,383,474,429,459,502,502,746,752,488,398,501,473,413,472,486,271,480,270,-1439,-1455,1357,-1471,-1487,-1503,1341,1325,-1519,1489,1463,1403,1309,-1535,1372,1448,1418,1476,1356,1462,1387,-1551,1475,1340,1447,1402,1386,-1567,1068,1068,1474,1461,455,380,468,440,395,425,410,454,364,467,466,464,453,269,409,448,268,432,1371,1473,1432,1417,1308,1460,1355,1446,1459,1431,1083,1083,1401,1416,1458,1445,1067,1067,1370,1457,1051,1051,1291,1430,1385,1444,1354,1415,1400,1443,1082,1082,1173,1113,1186,1066,1185,1050,-1967,1158,1128,1172,1097,1171,1081,-1983,1157,1112,416,266,375,400,1170,1142,1127,1065,793,793,1169,1033,1156,1096,1141,1111,1155,1080,1126,1140,898,898,808,808,897,897,792,792,1095,1152,1032,1125,1110,1139,1079,1124,882,807,838,881,853,791,-2319,867,368,263,822,852,837,866,806,865,-2399,851,352,262,534,534,821,836,594,594,549,549,593,593,533,533,848,773,579,579,564,578,548,563,276,276,577,576,306,291,516,560,305,305,275,259, - -251,-892,-2058,-2620,-2828,-2957,-3023,-3039,1041,1041,1040,1040,769,769,769,769,256,256,256,256,256,256,256,256,256,256,256,256,256,256,256,256,-511,-527,-543,-559,1530,-575,-591,1528,1527,1407,1526,1391,1023,1023,1023,1023,1525,1375,1268,1268,1103,1103,1087,1087,1039,1039,1523,-604,815,815,815,815,510,495,509,479,508,463,507,447,431,505,415,399,-734,-782,1262,-815,1259,1244,-831,1258,1228,-847,-863,1196,-879,1253,987,987,748,-767,493,493,462,477,414,414,686,669,478,446,461,445,474,429,487,458,412,471,1266,1264,1009,1009,799,799,-1019,-1276,-1452,-1581,-1677,-1757,-1821,-1886,-1933,-1997,1257,1257,1483,1468,1512,1422,1497,1406,1467,1496,1421,1510,1134,1134,1225,1225,1466,1451,1374,1405,1252,1252,1358,1480,1164,1164,1251,1251,1238,1238,1389,1465,-1407,1054,1101,-1423,1207,-1439,830,830,1248,1038,1237,1117,1223,1148,1236,1208,411,426,395,410,379,269,1193,1222,1132,1235,1221,1116,976,976,1192,1162,1177,1220,1131,1191,963,963,-1647,961,780,-1663,558,558,994,993,437,408,393,407,829,978,813,797,947,-1743,721,721,377,392,844,950,828,890,706,706,812,859,796,960,948,843,934,874,571,571,-1919,690,555,689,421,346,539,539,944,779,918,873,932,842,903,888,570,570,931,917,674,674,-2575,1562,-2591,1609,-2607,1654,1322,1322,1441,1441,1696,1546,1683,1593,1669,1624,1426,1426,1321,1321,1639,1680,1425,1425,1305,1305,1545,1668,1608,1623,1667,1592,1638,1666,1320,1320,1652,1607,1409,1409,1304,1304,1288,1288,1664,1637,1395,1395,1335,1335,1622,1636,1394,1394,1319,1319,1606,1621,1392,1392,1137,1137,1137,1137,345,390,360,375,404,373,1047,-2751,-2767,-2783,1062,1121,1046,-2799,1077,-2815,1106,1061,789,789,1105,1104,263,355,310,340,325,354,352,262,339,324,1091,1076,1029,1090,1060,1075,833,833,788,788,1088,1028,818,818,803,803,561,561,531,531,816,771,546,546,289,274,288,258, - -253,-317,-381,-446,-478,-509,1279,1279,-811,-1179,-1451,-1756,-1900,-2028,-2189,-2253,-2333,-2414,-2445,-2511,-2526,1313,1298,-2559,1041,1041,1040,1040,1025,1025,1024,1024,1022,1007,1021,991,1020,975,1019,959,687,687,1018,1017,671,671,655,655,1016,1015,639,639,758,758,623,623,757,607,756,591,755,575,754,559,543,543,1009,783,-575,-621,-685,-749,496,-590,750,749,734,748,974,989,1003,958,988,973,1002,942,987,957,972,1001,926,986,941,971,956,1000,910,985,925,999,894,970,-1071,-1087,-1102,1390,-1135,1436,1509,1451,1374,-1151,1405,1358,1480,1420,-1167,1507,1494,1389,1342,1465,1435,1450,1326,1505,1310,1493,1373,1479,1404,1492,1464,1419,428,443,472,397,736,526,464,464,486,457,442,471,484,482,1357,1449,1434,1478,1388,1491,1341,1490,1325,1489,1463,1403,1309,1477,1372,1448,1418,1433,1476,1356,1462,1387,-1439,1475,1340,1447,1402,1474,1324,1461,1371,1473,269,448,1432,1417,1308,1460,-1711,1459,-1727,1441,1099,1099,1446,1386,1431,1401,-1743,1289,1083,1083,1160,1160,1458,1445,1067,1067,1370,1457,1307,1430,1129,1129,1098,1098,268,432,267,416,266,400,-1887,1144,1187,1082,1173,1113,1186,1066,1050,1158,1128,1143,1172,1097,1171,1081,420,391,1157,1112,1170,1142,1127,1065,1169,1049,1156,1096,1141,1111,1155,1080,1126,1154,1064,1153,1140,1095,1048,-2159,1125,1110,1137,-2175,823,823,1139,1138,807,807,384,264,368,263,868,838,853,791,867,822,852,837,866,806,865,790,-2319,851,821,836,352,262,850,805,849,-2399,533,533,835,820,336,261,578,548,563,577,532,532,832,772,562,562,547,547,305,275,560,515,290,290,288,258 }; - static const ma_uint8 tab32[] = { 130,162,193,209,44,28,76,140,9,9,9,9,9,9,9,9,190,254,222,238,126,94,157,157,109,61,173,205}; - static const ma_uint8 tab33[] = { 252,236,220,204,188,172,156,140,124,108,92,76,60,44,28,12 }; - static const ma_int16 tabindex[2*16] = { 0,32,64,98,0,132,180,218,292,364,426,538,648,746,0,1126,1460,1460,1460,1460,1460,1460,1460,1460,1842,1842,1842,1842,1842,1842,1842,1842 }; - static const ma_uint8 g_linbits[] = { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,2,3,4,6,8,10,13,4,5,6,7,8,9,11,13 }; -#define MA_DR_MP3_PEEK_BITS(n) (bs_cache >> (32 - (n))) -#define MA_DR_MP3_FLUSH_BITS(n) { bs_cache <<= (n); bs_sh += (n); } -#define MA_DR_MP3_CHECK_BITS while (bs_sh >= 0) { bs_cache |= (ma_uint32)*bs_next_ptr++ << bs_sh; bs_sh -= 8; } -#define MA_DR_MP3_BSPOS ((bs_next_ptr - bs->buf)*8 - 24 + bs_sh) - float one = 0.0f; - int ireg = 0, big_val_cnt = gr_info->big_values; - const ma_uint8 *sfb = gr_info->sfbtab; - const ma_uint8 *bs_next_ptr = bs->buf + bs->pos/8; - ma_uint32 bs_cache = (((bs_next_ptr[0]*256u + bs_next_ptr[1])*256u + bs_next_ptr[2])*256u + bs_next_ptr[3]) << (bs->pos & 7); - int pairs_to_decode, np, bs_sh = (bs->pos & 7) - 8; - bs_next_ptr += 4; - while (big_val_cnt > 0) - { - int tab_num = gr_info->table_select[ireg]; - int sfb_cnt = gr_info->region_count[ireg++]; - const ma_int16 *codebook = tabs + tabindex[tab_num]; - int linbits = g_linbits[tab_num]; - if (linbits) - { - do - { - np = *sfb++ / 2; - pairs_to_decode = MA_DR_MP3_MIN(big_val_cnt, np); - one = *scf++; - do - { - int j, w = 5; - int leaf = codebook[MA_DR_MP3_PEEK_BITS(w)]; - while (leaf < 0) - { - MA_DR_MP3_FLUSH_BITS(w); - w = leaf & 7; - leaf = codebook[MA_DR_MP3_PEEK_BITS(w) - (leaf >> 3)]; - } - MA_DR_MP3_FLUSH_BITS(leaf >> 8); - for (j = 0; j < 2; j++, dst++, leaf >>= 4) - { - int lsb = leaf & 0x0F; - if (lsb == 15) - { - lsb += MA_DR_MP3_PEEK_BITS(linbits); - MA_DR_MP3_FLUSH_BITS(linbits); - MA_DR_MP3_CHECK_BITS; - *dst = one*ma_dr_mp3_L3_pow_43(lsb)*((ma_int32)bs_cache < 0 ? -1: 1); - } else - { - *dst = g_ma_dr_mp3_pow43[16 + lsb - 16*(bs_cache >> 31)]*one; - } - MA_DR_MP3_FLUSH_BITS(lsb ? 1 : 0); - } - MA_DR_MP3_CHECK_BITS; - } while (--pairs_to_decode); - } while ((big_val_cnt -= np) > 0 && --sfb_cnt >= 0); - } else - { - do - { - np = *sfb++ / 2; - pairs_to_decode = MA_DR_MP3_MIN(big_val_cnt, np); - one = *scf++; - do - { - int j, w = 5; - int leaf = codebook[MA_DR_MP3_PEEK_BITS(w)]; - while (leaf < 0) - { - MA_DR_MP3_FLUSH_BITS(w); - w = leaf & 7; - leaf = codebook[MA_DR_MP3_PEEK_BITS(w) - (leaf >> 3)]; - } - MA_DR_MP3_FLUSH_BITS(leaf >> 8); - for (j = 0; j < 2; j++, dst++, leaf >>= 4) - { - int lsb = leaf & 0x0F; - *dst = g_ma_dr_mp3_pow43[16 + lsb - 16*(bs_cache >> 31)]*one; - MA_DR_MP3_FLUSH_BITS(lsb ? 1 : 0); - } - MA_DR_MP3_CHECK_BITS; - } while (--pairs_to_decode); - } while ((big_val_cnt -= np) > 0 && --sfb_cnt >= 0); - } - } - for (np = 1 - big_val_cnt;; dst += 4) - { - const ma_uint8 *codebook_count1 = (gr_info->count1_table) ? tab33 : tab32; - int leaf = codebook_count1[MA_DR_MP3_PEEK_BITS(4)]; - if (!(leaf & 8)) - { - leaf = codebook_count1[(leaf >> 3) + (bs_cache << 4 >> (32 - (leaf & 3)))]; - } - MA_DR_MP3_FLUSH_BITS(leaf & 7); - if (MA_DR_MP3_BSPOS > layer3gr_limit) - { - break; - } -#define MA_DR_MP3_RELOAD_SCALEFACTOR if (!--np) { np = *sfb++/2; if (!np) break; one = *scf++; } -#define MA_DR_MP3_DEQ_COUNT1(s) if (leaf & (128 >> s)) { dst[s] = ((ma_int32)bs_cache < 0) ? -one : one; MA_DR_MP3_FLUSH_BITS(1) } - MA_DR_MP3_RELOAD_SCALEFACTOR; - MA_DR_MP3_DEQ_COUNT1(0); - MA_DR_MP3_DEQ_COUNT1(1); - MA_DR_MP3_RELOAD_SCALEFACTOR; - MA_DR_MP3_DEQ_COUNT1(2); - MA_DR_MP3_DEQ_COUNT1(3); - MA_DR_MP3_CHECK_BITS; - } - bs->pos = layer3gr_limit; -} -static void ma_dr_mp3_L3_midside_stereo(float *left, int n) -{ - int i = 0; - float *right = left + 576; -#if MA_DR_MP3_HAVE_SIMD - if (ma_dr_mp3_have_simd()) - { - for (; i < n - 3; i += 4) - { - ma_dr_mp3_f4 vl = MA_DR_MP3_VLD(left + i); - ma_dr_mp3_f4 vr = MA_DR_MP3_VLD(right + i); - MA_DR_MP3_VSTORE(left + i, MA_DR_MP3_VADD(vl, vr)); - MA_DR_MP3_VSTORE(right + i, MA_DR_MP3_VSUB(vl, vr)); - } -#ifdef __GNUC__ - if (__builtin_constant_p(n % 4 == 0) && n % 4 == 0) - return; -#endif - } -#endif - for (; i < n; i++) - { - float a = left[i]; - float b = right[i]; - left[i] = a + b; - right[i] = a - b; - } -} -static void ma_dr_mp3_L3_intensity_stereo_band(float *left, int n, float kl, float kr) -{ - int i; - for (i = 0; i < n; i++) - { - left[i + 576] = left[i]*kr; - left[i] = left[i]*kl; - } -} -static void ma_dr_mp3_L3_stereo_top_band(const float *right, const ma_uint8 *sfb, int nbands, int max_band[3]) -{ - int i, k; - max_band[0] = max_band[1] = max_band[2] = -1; - for (i = 0; i < nbands; i++) - { - for (k = 0; k < sfb[i]; k += 2) - { - if (right[k] != 0 || right[k + 1] != 0) - { - max_band[i % 3] = i; - break; - } - } - right += sfb[i]; - } -} -static void ma_dr_mp3_L3_stereo_process(float *left, const ma_uint8 *ist_pos, const ma_uint8 *sfb, const ma_uint8 *hdr, int max_band[3], int mpeg2_sh) -{ - static const float g_pan[7*2] = { 0,1,0.21132487f,0.78867513f,0.36602540f,0.63397460f,0.5f,0.5f,0.63397460f,0.36602540f,0.78867513f,0.21132487f,1,0 }; - unsigned i, max_pos = MA_DR_MP3_HDR_TEST_MPEG1(hdr) ? 7 : 64; - for (i = 0; sfb[i]; i++) - { - unsigned ipos = ist_pos[i]; - if ((int)i > max_band[i % 3] && ipos < max_pos) - { - float kl, kr, s = MA_DR_MP3_HDR_TEST_MS_STEREO(hdr) ? 1.41421356f : 1; - if (MA_DR_MP3_HDR_TEST_MPEG1(hdr)) - { - kl = g_pan[2*ipos]; - kr = g_pan[2*ipos + 1]; - } else - { - kl = 1; - kr = ma_dr_mp3_L3_ldexp_q2(1, (ipos + 1) >> 1 << mpeg2_sh); - if (ipos & 1) - { - kl = kr; - kr = 1; - } - } - ma_dr_mp3_L3_intensity_stereo_band(left, sfb[i], kl*s, kr*s); - } else if (MA_DR_MP3_HDR_TEST_MS_STEREO(hdr)) - { - ma_dr_mp3_L3_midside_stereo(left, sfb[i]); - } - left += sfb[i]; - } -} -static void ma_dr_mp3_L3_intensity_stereo(float *left, ma_uint8 *ist_pos, const ma_dr_mp3_L3_gr_info *gr, const ma_uint8 *hdr) -{ - int max_band[3], n_sfb = gr->n_long_sfb + gr->n_short_sfb; - int i, max_blocks = gr->n_short_sfb ? 3 : 1; - ma_dr_mp3_L3_stereo_top_band(left + 576, gr->sfbtab, n_sfb, max_band); - if (gr->n_long_sfb) - { - max_band[0] = max_band[1] = max_band[2] = MA_DR_MP3_MAX(MA_DR_MP3_MAX(max_band[0], max_band[1]), max_band[2]); - } - for (i = 0; i < max_blocks; i++) - { - int default_pos = MA_DR_MP3_HDR_TEST_MPEG1(hdr) ? 3 : 0; - int itop = n_sfb - max_blocks + i; - int prev = itop - max_blocks; - ist_pos[itop] = (ma_uint8)(max_band[i] >= prev ? default_pos : ist_pos[prev]); - } - ma_dr_mp3_L3_stereo_process(left, ist_pos, gr->sfbtab, hdr, max_band, gr[1].scalefac_compress & 1); -} -static void ma_dr_mp3_L3_reorder(float *grbuf, float *scratch, const ma_uint8 *sfb) -{ - int i, len; - float *src = grbuf, *dst = scratch; - for (;0 != (len = *sfb); sfb += 3, src += 2*len) - { - for (i = 0; i < len; i++, src++) - { - *dst++ = src[0*len]; - *dst++ = src[1*len]; - *dst++ = src[2*len]; - } - } - MA_DR_MP3_COPY_MEMORY(grbuf, scratch, (dst - scratch)*sizeof(float)); -} -static void ma_dr_mp3_L3_antialias(float *grbuf, int nbands) -{ - static const float g_aa[2][8] = { - {0.85749293f,0.88174200f,0.94962865f,0.98331459f,0.99551782f,0.99916056f,0.99989920f,0.99999316f}, - {0.51449576f,0.47173197f,0.31337745f,0.18191320f,0.09457419f,0.04096558f,0.01419856f,0.00369997f} - }; - for (; nbands > 0; nbands--, grbuf += 18) - { - int i = 0; -#if MA_DR_MP3_HAVE_SIMD - if (ma_dr_mp3_have_simd()) for (; i < 8; i += 4) - { - ma_dr_mp3_f4 vu = MA_DR_MP3_VLD(grbuf + 18 + i); - ma_dr_mp3_f4 vd = MA_DR_MP3_VLD(grbuf + 14 - i); - ma_dr_mp3_f4 vc0 = MA_DR_MP3_VLD(g_aa[0] + i); - ma_dr_mp3_f4 vc1 = MA_DR_MP3_VLD(g_aa[1] + i); - vd = MA_DR_MP3_VREV(vd); - MA_DR_MP3_VSTORE(grbuf + 18 + i, MA_DR_MP3_VSUB(MA_DR_MP3_VMUL(vu, vc0), MA_DR_MP3_VMUL(vd, vc1))); - vd = MA_DR_MP3_VADD(MA_DR_MP3_VMUL(vu, vc1), MA_DR_MP3_VMUL(vd, vc0)); - MA_DR_MP3_VSTORE(grbuf + 14 - i, MA_DR_MP3_VREV(vd)); - } -#endif -#ifndef MA_DR_MP3_ONLY_SIMD - for(; i < 8; i++) - { - float u = grbuf[18 + i]; - float d = grbuf[17 - i]; - grbuf[18 + i] = u*g_aa[0][i] - d*g_aa[1][i]; - grbuf[17 - i] = u*g_aa[1][i] + d*g_aa[0][i]; - } -#endif - } -} -static void ma_dr_mp3_L3_dct3_9(float *y) -{ - float s0, s1, s2, s3, s4, s5, s6, s7, s8, t0, t2, t4; - s0 = y[0]; s2 = y[2]; s4 = y[4]; s6 = y[6]; s8 = y[8]; - t0 = s0 + s6*0.5f; - s0 -= s6; - t4 = (s4 + s2)*0.93969262f; - t2 = (s8 + s2)*0.76604444f; - s6 = (s4 - s8)*0.17364818f; - s4 += s8 - s2; - s2 = s0 - s4*0.5f; - y[4] = s4 + s0; - s8 = t0 - t2 + s6; - s0 = t0 - t4 + t2; - s4 = t0 + t4 - s6; - s1 = y[1]; s3 = y[3]; s5 = y[5]; s7 = y[7]; - s3 *= 0.86602540f; - t0 = (s5 + s1)*0.98480775f; - t4 = (s5 - s7)*0.34202014f; - t2 = (s1 + s7)*0.64278761f; - s1 = (s1 - s5 - s7)*0.86602540f; - s5 = t0 - s3 - t2; - s7 = t4 - s3 - t0; - s3 = t4 + s3 - t2; - y[0] = s4 - s7; - y[1] = s2 + s1; - y[2] = s0 - s3; - y[3] = s8 + s5; - y[5] = s8 - s5; - y[6] = s0 + s3; - y[7] = s2 - s1; - y[8] = s4 + s7; -} -static void ma_dr_mp3_L3_imdct36(float *grbuf, float *overlap, const float *window, int nbands) -{ - int i, j; - static const float g_twid9[18] = { - 0.73727734f,0.79335334f,0.84339145f,0.88701083f,0.92387953f,0.95371695f,0.97629601f,0.99144486f,0.99904822f,0.67559021f,0.60876143f,0.53729961f,0.46174861f,0.38268343f,0.30070580f,0.21643961f,0.13052619f,0.04361938f - }; - for (j = 0; j < nbands; j++, grbuf += 18, overlap += 9) - { - float co[9], si[9]; - co[0] = -grbuf[0]; - si[0] = grbuf[17]; - for (i = 0; i < 4; i++) - { - si[8 - 2*i] = grbuf[4*i + 1] - grbuf[4*i + 2]; - co[1 + 2*i] = grbuf[4*i + 1] + grbuf[4*i + 2]; - si[7 - 2*i] = grbuf[4*i + 4] - grbuf[4*i + 3]; - co[2 + 2*i] = -(grbuf[4*i + 3] + grbuf[4*i + 4]); - } - ma_dr_mp3_L3_dct3_9(co); - ma_dr_mp3_L3_dct3_9(si); - si[1] = -si[1]; - si[3] = -si[3]; - si[5] = -si[5]; - si[7] = -si[7]; - i = 0; -#if MA_DR_MP3_HAVE_SIMD - if (ma_dr_mp3_have_simd()) for (; i < 8; i += 4) - { - ma_dr_mp3_f4 vovl = MA_DR_MP3_VLD(overlap + i); - ma_dr_mp3_f4 vc = MA_DR_MP3_VLD(co + i); - ma_dr_mp3_f4 vs = MA_DR_MP3_VLD(si + i); - ma_dr_mp3_f4 vr0 = MA_DR_MP3_VLD(g_twid9 + i); - ma_dr_mp3_f4 vr1 = MA_DR_MP3_VLD(g_twid9 + 9 + i); - ma_dr_mp3_f4 vw0 = MA_DR_MP3_VLD(window + i); - ma_dr_mp3_f4 vw1 = MA_DR_MP3_VLD(window + 9 + i); - ma_dr_mp3_f4 vsum = MA_DR_MP3_VADD(MA_DR_MP3_VMUL(vc, vr1), MA_DR_MP3_VMUL(vs, vr0)); - MA_DR_MP3_VSTORE(overlap + i, MA_DR_MP3_VSUB(MA_DR_MP3_VMUL(vc, vr0), MA_DR_MP3_VMUL(vs, vr1))); - MA_DR_MP3_VSTORE(grbuf + i, MA_DR_MP3_VSUB(MA_DR_MP3_VMUL(vovl, vw0), MA_DR_MP3_VMUL(vsum, vw1))); - vsum = MA_DR_MP3_VADD(MA_DR_MP3_VMUL(vovl, vw1), MA_DR_MP3_VMUL(vsum, vw0)); - MA_DR_MP3_VSTORE(grbuf + 14 - i, MA_DR_MP3_VREV(vsum)); - } -#endif - for (; i < 9; i++) - { - float ovl = overlap[i]; - float sum = co[i]*g_twid9[9 + i] + si[i]*g_twid9[0 + i]; - overlap[i] = co[i]*g_twid9[0 + i] - si[i]*g_twid9[9 + i]; - grbuf[i] = ovl*window[0 + i] - sum*window[9 + i]; - grbuf[17 - i] = ovl*window[9 + i] + sum*window[0 + i]; - } - } -} -static void ma_dr_mp3_L3_idct3(float x0, float x1, float x2, float *dst) -{ - float m1 = x1*0.86602540f; - float a1 = x0 - x2*0.5f; - dst[1] = x0 + x2; - dst[0] = a1 + m1; - dst[2] = a1 - m1; -} -static void ma_dr_mp3_L3_imdct12(float *x, float *dst, float *overlap) -{ - static const float g_twid3[6] = { 0.79335334f,0.92387953f,0.99144486f, 0.60876143f,0.38268343f,0.13052619f }; - float co[3], si[3]; - int i; - ma_dr_mp3_L3_idct3(-x[0], x[6] + x[3], x[12] + x[9], co); - ma_dr_mp3_L3_idct3(x[15], x[12] - x[9], x[6] - x[3], si); - si[1] = -si[1]; - for (i = 0; i < 3; i++) - { - float ovl = overlap[i]; - float sum = co[i]*g_twid3[3 + i] + si[i]*g_twid3[0 + i]; - overlap[i] = co[i]*g_twid3[0 + i] - si[i]*g_twid3[3 + i]; - dst[i] = ovl*g_twid3[2 - i] - sum*g_twid3[5 - i]; - dst[5 - i] = ovl*g_twid3[5 - i] + sum*g_twid3[2 - i]; - } -} -static void ma_dr_mp3_L3_imdct_short(float *grbuf, float *overlap, int nbands) -{ - for (;nbands > 0; nbands--, overlap += 9, grbuf += 18) - { - float tmp[18]; - MA_DR_MP3_COPY_MEMORY(tmp, grbuf, sizeof(tmp)); - MA_DR_MP3_COPY_MEMORY(grbuf, overlap, 6*sizeof(float)); - ma_dr_mp3_L3_imdct12(tmp, grbuf + 6, overlap + 6); - ma_dr_mp3_L3_imdct12(tmp + 1, grbuf + 12, overlap + 6); - ma_dr_mp3_L3_imdct12(tmp + 2, overlap, overlap + 6); - } -} -static void ma_dr_mp3_L3_change_sign(float *grbuf) -{ - int b, i; - for (b = 0, grbuf += 18; b < 32; b += 2, grbuf += 36) - for (i = 1; i < 18; i += 2) - grbuf[i] = -grbuf[i]; -} -static void ma_dr_mp3_L3_imdct_gr(float *grbuf, float *overlap, unsigned block_type, unsigned n_long_bands) -{ - static const float g_mdct_window[2][18] = { - { 0.99904822f,0.99144486f,0.97629601f,0.95371695f,0.92387953f,0.88701083f,0.84339145f,0.79335334f,0.73727734f,0.04361938f,0.13052619f,0.21643961f,0.30070580f,0.38268343f,0.46174861f,0.53729961f,0.60876143f,0.67559021f }, - { 1,1,1,1,1,1,0.99144486f,0.92387953f,0.79335334f,0,0,0,0,0,0,0.13052619f,0.38268343f,0.60876143f } - }; - if (n_long_bands) - { - ma_dr_mp3_L3_imdct36(grbuf, overlap, g_mdct_window[0], n_long_bands); - grbuf += 18*n_long_bands; - overlap += 9*n_long_bands; - } - if (block_type == MA_DR_MP3_SHORT_BLOCK_TYPE) - ma_dr_mp3_L3_imdct_short(grbuf, overlap, 32 - n_long_bands); - else - ma_dr_mp3_L3_imdct36(grbuf, overlap, g_mdct_window[block_type == MA_DR_MP3_STOP_BLOCK_TYPE], 32 - n_long_bands); -} -static void ma_dr_mp3_L3_save_reservoir(ma_dr_mp3dec *h, ma_dr_mp3dec_scratch *s) -{ - int pos = (s->bs.pos + 7)/8u; - int remains = s->bs.limit/8u - pos; - if (remains > MA_DR_MP3_MAX_BITRESERVOIR_BYTES) - { - pos += remains - MA_DR_MP3_MAX_BITRESERVOIR_BYTES; - remains = MA_DR_MP3_MAX_BITRESERVOIR_BYTES; - } - if (remains > 0) - { - MA_DR_MP3_MOVE_MEMORY(h->reserv_buf, s->maindata + pos, remains); - } - h->reserv = remains; -} -static int ma_dr_mp3_L3_restore_reservoir(ma_dr_mp3dec *h, ma_dr_mp3_bs *bs, ma_dr_mp3dec_scratch *s, int main_data_begin) -{ - int frame_bytes = (bs->limit - bs->pos)/8; - int bytes_have = MA_DR_MP3_MIN(h->reserv, main_data_begin); - MA_DR_MP3_COPY_MEMORY(s->maindata, h->reserv_buf + MA_DR_MP3_MAX(0, h->reserv - main_data_begin), MA_DR_MP3_MIN(h->reserv, main_data_begin)); - MA_DR_MP3_COPY_MEMORY(s->maindata + bytes_have, bs->buf + bs->pos/8, frame_bytes); - ma_dr_mp3_bs_init(&s->bs, s->maindata, bytes_have + frame_bytes); - return h->reserv >= main_data_begin; -} -static void ma_dr_mp3_L3_decode(ma_dr_mp3dec *h, ma_dr_mp3dec_scratch *s, ma_dr_mp3_L3_gr_info *gr_info, int nch) -{ - int ch; - for (ch = 0; ch < nch; ch++) - { - int layer3gr_limit = s->bs.pos + gr_info[ch].part_23_length; - ma_dr_mp3_L3_decode_scalefactors(h->header, s->ist_pos[ch], &s->bs, gr_info + ch, s->scf, ch); - ma_dr_mp3_L3_huffman(s->grbuf[ch], &s->bs, gr_info + ch, s->scf, layer3gr_limit); - } - if (MA_DR_MP3_HDR_TEST_I_STEREO(h->header)) - { - ma_dr_mp3_L3_intensity_stereo(s->grbuf[0], s->ist_pos[1], gr_info, h->header); - } else if (MA_DR_MP3_HDR_IS_MS_STEREO(h->header)) - { - ma_dr_mp3_L3_midside_stereo(s->grbuf[0], 576); - } - for (ch = 0; ch < nch; ch++, gr_info++) - { - int aa_bands = 31; - int n_long_bands = (gr_info->mixed_block_flag ? 2 : 0) << (int)(MA_DR_MP3_HDR_GET_MY_SAMPLE_RATE(h->header) == 2); - if (gr_info->n_short_sfb) - { - aa_bands = n_long_bands - 1; - ma_dr_mp3_L3_reorder(s->grbuf[ch] + n_long_bands*18, s->syn[0], gr_info->sfbtab + gr_info->n_long_sfb); - } - ma_dr_mp3_L3_antialias(s->grbuf[ch], aa_bands); - ma_dr_mp3_L3_imdct_gr(s->grbuf[ch], h->mdct_overlap[ch], gr_info->block_type, n_long_bands); - ma_dr_mp3_L3_change_sign(s->grbuf[ch]); - } -} -static void ma_dr_mp3d_DCT_II(float *grbuf, int n) -{ - static const float g_sec[24] = { - 10.19000816f,0.50060302f,0.50241929f,3.40760851f,0.50547093f,0.52249861f,2.05778098f,0.51544732f,0.56694406f,1.48416460f,0.53104258f,0.64682180f,1.16943991f,0.55310392f,0.78815460f,0.97256821f,0.58293498f,1.06067765f,0.83934963f,0.62250412f,1.72244716f,0.74453628f,0.67480832f,5.10114861f - }; - int i, k = 0; -#if MA_DR_MP3_HAVE_SIMD - if (ma_dr_mp3_have_simd()) for (; k < n; k += 4) - { - ma_dr_mp3_f4 t[4][8], *x; - float *y = grbuf + k; - for (x = t[0], i = 0; i < 8; i++, x++) - { - ma_dr_mp3_f4 x0 = MA_DR_MP3_VLD(&y[i*18]); - ma_dr_mp3_f4 x1 = MA_DR_MP3_VLD(&y[(15 - i)*18]); - ma_dr_mp3_f4 x2 = MA_DR_MP3_VLD(&y[(16 + i)*18]); - ma_dr_mp3_f4 x3 = MA_DR_MP3_VLD(&y[(31 - i)*18]); - ma_dr_mp3_f4 t0 = MA_DR_MP3_VADD(x0, x3); - ma_dr_mp3_f4 t1 = MA_DR_MP3_VADD(x1, x2); - ma_dr_mp3_f4 t2 = MA_DR_MP3_VMUL_S(MA_DR_MP3_VSUB(x1, x2), g_sec[3*i + 0]); - ma_dr_mp3_f4 t3 = MA_DR_MP3_VMUL_S(MA_DR_MP3_VSUB(x0, x3), g_sec[3*i + 1]); - x[0] = MA_DR_MP3_VADD(t0, t1); - x[8] = MA_DR_MP3_VMUL_S(MA_DR_MP3_VSUB(t0, t1), g_sec[3*i + 2]); - x[16] = MA_DR_MP3_VADD(t3, t2); - x[24] = MA_DR_MP3_VMUL_S(MA_DR_MP3_VSUB(t3, t2), g_sec[3*i + 2]); - } - for (x = t[0], i = 0; i < 4; i++, x += 8) - { - ma_dr_mp3_f4 x0 = x[0], x1 = x[1], x2 = x[2], x3 = x[3], x4 = x[4], x5 = x[5], x6 = x[6], x7 = x[7], xt; - xt = MA_DR_MP3_VSUB(x0, x7); x0 = MA_DR_MP3_VADD(x0, x7); - x7 = MA_DR_MP3_VSUB(x1, x6); x1 = MA_DR_MP3_VADD(x1, x6); - x6 = MA_DR_MP3_VSUB(x2, x5); x2 = MA_DR_MP3_VADD(x2, x5); - x5 = MA_DR_MP3_VSUB(x3, x4); x3 = MA_DR_MP3_VADD(x3, x4); - x4 = MA_DR_MP3_VSUB(x0, x3); x0 = MA_DR_MP3_VADD(x0, x3); - x3 = MA_DR_MP3_VSUB(x1, x2); x1 = MA_DR_MP3_VADD(x1, x2); - x[0] = MA_DR_MP3_VADD(x0, x1); - x[4] = MA_DR_MP3_VMUL_S(MA_DR_MP3_VSUB(x0, x1), 0.70710677f); - x5 = MA_DR_MP3_VADD(x5, x6); - x6 = MA_DR_MP3_VMUL_S(MA_DR_MP3_VADD(x6, x7), 0.70710677f); - x7 = MA_DR_MP3_VADD(x7, xt); - x3 = MA_DR_MP3_VMUL_S(MA_DR_MP3_VADD(x3, x4), 0.70710677f); - x5 = MA_DR_MP3_VSUB(x5, MA_DR_MP3_VMUL_S(x7, 0.198912367f)); - x7 = MA_DR_MP3_VADD(x7, MA_DR_MP3_VMUL_S(x5, 0.382683432f)); - x5 = MA_DR_MP3_VSUB(x5, MA_DR_MP3_VMUL_S(x7, 0.198912367f)); - x0 = MA_DR_MP3_VSUB(xt, x6); xt = MA_DR_MP3_VADD(xt, x6); - x[1] = MA_DR_MP3_VMUL_S(MA_DR_MP3_VADD(xt, x7), 0.50979561f); - x[2] = MA_DR_MP3_VMUL_S(MA_DR_MP3_VADD(x4, x3), 0.54119611f); - x[3] = MA_DR_MP3_VMUL_S(MA_DR_MP3_VSUB(x0, x5), 0.60134488f); - x[5] = MA_DR_MP3_VMUL_S(MA_DR_MP3_VADD(x0, x5), 0.89997619f); - x[6] = MA_DR_MP3_VMUL_S(MA_DR_MP3_VSUB(x4, x3), 1.30656302f); - x[7] = MA_DR_MP3_VMUL_S(MA_DR_MP3_VSUB(xt, x7), 2.56291556f); - } - if (k > n - 3) - { -#if MA_DR_MP3_HAVE_SSE -#define MA_DR_MP3_VSAVE2(i, v) _mm_storel_pi((__m64 *)(void*)&y[i*18], v) -#else -#define MA_DR_MP3_VSAVE2(i, v) vst1_f32((float32_t *)&y[(i)*18], vget_low_f32(v)) -#endif - for (i = 0; i < 7; i++, y += 4*18) - { - ma_dr_mp3_f4 s = MA_DR_MP3_VADD(t[3][i], t[3][i + 1]); - MA_DR_MP3_VSAVE2(0, t[0][i]); - MA_DR_MP3_VSAVE2(1, MA_DR_MP3_VADD(t[2][i], s)); - MA_DR_MP3_VSAVE2(2, MA_DR_MP3_VADD(t[1][i], t[1][i + 1])); - MA_DR_MP3_VSAVE2(3, MA_DR_MP3_VADD(t[2][1 + i], s)); - } - MA_DR_MP3_VSAVE2(0, t[0][7]); - MA_DR_MP3_VSAVE2(1, MA_DR_MP3_VADD(t[2][7], t[3][7])); - MA_DR_MP3_VSAVE2(2, t[1][7]); - MA_DR_MP3_VSAVE2(3, t[3][7]); - } else - { -#define MA_DR_MP3_VSAVE4(i, v) MA_DR_MP3_VSTORE(&y[(i)*18], v) - for (i = 0; i < 7; i++, y += 4*18) - { - ma_dr_mp3_f4 s = MA_DR_MP3_VADD(t[3][i], t[3][i + 1]); - MA_DR_MP3_VSAVE4(0, t[0][i]); - MA_DR_MP3_VSAVE4(1, MA_DR_MP3_VADD(t[2][i], s)); - MA_DR_MP3_VSAVE4(2, MA_DR_MP3_VADD(t[1][i], t[1][i + 1])); - MA_DR_MP3_VSAVE4(3, MA_DR_MP3_VADD(t[2][1 + i], s)); - } - MA_DR_MP3_VSAVE4(0, t[0][7]); - MA_DR_MP3_VSAVE4(1, MA_DR_MP3_VADD(t[2][7], t[3][7])); - MA_DR_MP3_VSAVE4(2, t[1][7]); - MA_DR_MP3_VSAVE4(3, t[3][7]); - } - } else -#endif -#ifdef MA_DR_MP3_ONLY_SIMD - {} -#else - for (; k < n; k++) - { - float t[4][8], *x, *y = grbuf + k; - for (x = t[0], i = 0; i < 8; i++, x++) - { - float x0 = y[i*18]; - float x1 = y[(15 - i)*18]; - float x2 = y[(16 + i)*18]; - float x3 = y[(31 - i)*18]; - float t0 = x0 + x3; - float t1 = x1 + x2; - float t2 = (x1 - x2)*g_sec[3*i + 0]; - float t3 = (x0 - x3)*g_sec[3*i + 1]; - x[0] = t0 + t1; - x[8] = (t0 - t1)*g_sec[3*i + 2]; - x[16] = t3 + t2; - x[24] = (t3 - t2)*g_sec[3*i + 2]; - } - for (x = t[0], i = 0; i < 4; i++, x += 8) - { - float x0 = x[0], x1 = x[1], x2 = x[2], x3 = x[3], x4 = x[4], x5 = x[5], x6 = x[6], x7 = x[7], xt; - xt = x0 - x7; x0 += x7; - x7 = x1 - x6; x1 += x6; - x6 = x2 - x5; x2 += x5; - x5 = x3 - x4; x3 += x4; - x4 = x0 - x3; x0 += x3; - x3 = x1 - x2; x1 += x2; - x[0] = x0 + x1; - x[4] = (x0 - x1)*0.70710677f; - x5 = x5 + x6; - x6 = (x6 + x7)*0.70710677f; - x7 = x7 + xt; - x3 = (x3 + x4)*0.70710677f; - x5 -= x7*0.198912367f; - x7 += x5*0.382683432f; - x5 -= x7*0.198912367f; - x0 = xt - x6; xt += x6; - x[1] = (xt + x7)*0.50979561f; - x[2] = (x4 + x3)*0.54119611f; - x[3] = (x0 - x5)*0.60134488f; - x[5] = (x0 + x5)*0.89997619f; - x[6] = (x4 - x3)*1.30656302f; - x[7] = (xt - x7)*2.56291556f; - } - for (i = 0; i < 7; i++, y += 4*18) - { - y[0*18] = t[0][i]; - y[1*18] = t[2][i] + t[3][i] + t[3][i + 1]; - y[2*18] = t[1][i] + t[1][i + 1]; - y[3*18] = t[2][i + 1] + t[3][i] + t[3][i + 1]; - } - y[0*18] = t[0][7]; - y[1*18] = t[2][7] + t[3][7]; - y[2*18] = t[1][7]; - y[3*18] = t[3][7]; - } -#endif -} -#ifndef MA_DR_MP3_FLOAT_OUTPUT -typedef ma_int16 ma_dr_mp3d_sample_t; -static ma_int16 ma_dr_mp3d_scale_pcm(float sample) -{ - ma_int16 s; -#if MA_DR_MP3_HAVE_ARMV6 - ma_int32 s32 = (ma_int32)(sample + .5f); - s32 -= (s32 < 0); - s = (ma_int16)ma_dr_mp3_clip_int16_arm(s32); -#else - if (sample >= 32766.5f) return (ma_int16) 32767; - if (sample <= -32767.5f) return (ma_int16)-32768; - s = (ma_int16)(sample + .5f); - s -= (s < 0); -#endif - return s; -} -#else -typedef float ma_dr_mp3d_sample_t; -static float ma_dr_mp3d_scale_pcm(float sample) -{ - return sample*(1.f/32768.f); -} -#endif -static void ma_dr_mp3d_synth_pair(ma_dr_mp3d_sample_t *pcm, int nch, const float *z) -{ - float a; - a = (z[14*64] - z[ 0]) * 29; - a += (z[ 1*64] + z[13*64]) * 213; - a += (z[12*64] - z[ 2*64]) * 459; - a += (z[ 3*64] + z[11*64]) * 2037; - a += (z[10*64] - z[ 4*64]) * 5153; - a += (z[ 5*64] + z[ 9*64]) * 6574; - a += (z[ 8*64] - z[ 6*64]) * 37489; - a += z[ 7*64] * 75038; - pcm[0] = ma_dr_mp3d_scale_pcm(a); - z += 2; - a = z[14*64] * 104; - a += z[12*64] * 1567; - a += z[10*64] * 9727; - a += z[ 8*64] * 64019; - a += z[ 6*64] * -9975; - a += z[ 4*64] * -45; - a += z[ 2*64] * 146; - a += z[ 0*64] * -5; - pcm[16*nch] = ma_dr_mp3d_scale_pcm(a); -} -static void ma_dr_mp3d_synth(float *xl, ma_dr_mp3d_sample_t *dstl, int nch, float *lins) -{ - int i; - float *xr = xl + 576*(nch - 1); - ma_dr_mp3d_sample_t *dstr = dstl + (nch - 1); - static const float g_win[] = { - -1,26,-31,208,218,401,-519,2063,2000,4788,-5517,7134,5959,35640,-39336,74992, - -1,24,-35,202,222,347,-581,2080,1952,4425,-5879,7640,5288,33791,-41176,74856, - -1,21,-38,196,225,294,-645,2087,1893,4063,-6237,8092,4561,31947,-43006,74630, - -1,19,-41,190,227,244,-711,2085,1822,3705,-6589,8492,3776,30112,-44821,74313, - -1,17,-45,183,228,197,-779,2075,1739,3351,-6935,8840,2935,28289,-46617,73908, - -1,16,-49,176,228,153,-848,2057,1644,3004,-7271,9139,2037,26482,-48390,73415, - -2,14,-53,169,227,111,-919,2032,1535,2663,-7597,9389,1082,24694,-50137,72835, - -2,13,-58,161,224,72,-991,2001,1414,2330,-7910,9592,70,22929,-51853,72169, - -2,11,-63,154,221,36,-1064,1962,1280,2006,-8209,9750,-998,21189,-53534,71420, - -2,10,-68,147,215,2,-1137,1919,1131,1692,-8491,9863,-2122,19478,-55178,70590, - -3,9,-73,139,208,-29,-1210,1870,970,1388,-8755,9935,-3300,17799,-56778,69679, - -3,8,-79,132,200,-57,-1283,1817,794,1095,-8998,9966,-4533,16155,-58333,68692, - -4,7,-85,125,189,-83,-1356,1759,605,814,-9219,9959,-5818,14548,-59838,67629, - -4,7,-91,117,177,-106,-1428,1698,402,545,-9416,9916,-7154,12980,-61289,66494, - -5,6,-97,111,163,-127,-1498,1634,185,288,-9585,9838,-8540,11455,-62684,65290 - }; - float *zlin = lins + 15*64; - const float *w = g_win; - zlin[4*15] = xl[18*16]; - zlin[4*15 + 1] = xr[18*16]; - zlin[4*15 + 2] = xl[0]; - zlin[4*15 + 3] = xr[0]; - zlin[4*31] = xl[1 + 18*16]; - zlin[4*31 + 1] = xr[1 + 18*16]; - zlin[4*31 + 2] = xl[1]; - zlin[4*31 + 3] = xr[1]; - ma_dr_mp3d_synth_pair(dstr, nch, lins + 4*15 + 1); - ma_dr_mp3d_synth_pair(dstr + 32*nch, nch, lins + 4*15 + 64 + 1); - ma_dr_mp3d_synth_pair(dstl, nch, lins + 4*15); - ma_dr_mp3d_synth_pair(dstl + 32*nch, nch, lins + 4*15 + 64); -#if MA_DR_MP3_HAVE_SIMD - if (ma_dr_mp3_have_simd()) for (i = 14; i >= 0; i--) - { -#define MA_DR_MP3_VLOAD(k) ma_dr_mp3_f4 w0 = MA_DR_MP3_VSET(*w++); ma_dr_mp3_f4 w1 = MA_DR_MP3_VSET(*w++); ma_dr_mp3_f4 vz = MA_DR_MP3_VLD(&zlin[4*i - 64*k]); ma_dr_mp3_f4 vy = MA_DR_MP3_VLD(&zlin[4*i - 64*(15 - k)]); -#define MA_DR_MP3_V0(k) { MA_DR_MP3_VLOAD(k) b = MA_DR_MP3_VADD(MA_DR_MP3_VMUL(vz, w1), MA_DR_MP3_VMUL(vy, w0)) ; a = MA_DR_MP3_VSUB(MA_DR_MP3_VMUL(vz, w0), MA_DR_MP3_VMUL(vy, w1)); } -#define MA_DR_MP3_V1(k) { MA_DR_MP3_VLOAD(k) b = MA_DR_MP3_VADD(b, MA_DR_MP3_VADD(MA_DR_MP3_VMUL(vz, w1), MA_DR_MP3_VMUL(vy, w0))); a = MA_DR_MP3_VADD(a, MA_DR_MP3_VSUB(MA_DR_MP3_VMUL(vz, w0), MA_DR_MP3_VMUL(vy, w1))); } -#define MA_DR_MP3_V2(k) { MA_DR_MP3_VLOAD(k) b = MA_DR_MP3_VADD(b, MA_DR_MP3_VADD(MA_DR_MP3_VMUL(vz, w1), MA_DR_MP3_VMUL(vy, w0))); a = MA_DR_MP3_VADD(a, MA_DR_MP3_VSUB(MA_DR_MP3_VMUL(vy, w1), MA_DR_MP3_VMUL(vz, w0))); } - ma_dr_mp3_f4 a, b; - zlin[4*i] = xl[18*(31 - i)]; - zlin[4*i + 1] = xr[18*(31 - i)]; - zlin[4*i + 2] = xl[1 + 18*(31 - i)]; - zlin[4*i + 3] = xr[1 + 18*(31 - i)]; - zlin[4*i + 64] = xl[1 + 18*(1 + i)]; - zlin[4*i + 64 + 1] = xr[1 + 18*(1 + i)]; - zlin[4*i - 64 + 2] = xl[18*(1 + i)]; - zlin[4*i - 64 + 3] = xr[18*(1 + i)]; - MA_DR_MP3_V0(0) MA_DR_MP3_V2(1) MA_DR_MP3_V1(2) MA_DR_MP3_V2(3) MA_DR_MP3_V1(4) MA_DR_MP3_V2(5) MA_DR_MP3_V1(6) MA_DR_MP3_V2(7) - { -#ifndef MA_DR_MP3_FLOAT_OUTPUT -#if MA_DR_MP3_HAVE_SSE - static const ma_dr_mp3_f4 g_max = { 32767.0f, 32767.0f, 32767.0f, 32767.0f }; - static const ma_dr_mp3_f4 g_min = { -32768.0f, -32768.0f, -32768.0f, -32768.0f }; - __m128i pcm8 = _mm_packs_epi32(_mm_cvtps_epi32(_mm_max_ps(_mm_min_ps(a, g_max), g_min)), - _mm_cvtps_epi32(_mm_max_ps(_mm_min_ps(b, g_max), g_min))); - dstr[(15 - i)*nch] = (ma_int16)_mm_extract_epi16(pcm8, 1); - dstr[(17 + i)*nch] = (ma_int16)_mm_extract_epi16(pcm8, 5); - dstl[(15 - i)*nch] = (ma_int16)_mm_extract_epi16(pcm8, 0); - dstl[(17 + i)*nch] = (ma_int16)_mm_extract_epi16(pcm8, 4); - dstr[(47 - i)*nch] = (ma_int16)_mm_extract_epi16(pcm8, 3); - dstr[(49 + i)*nch] = (ma_int16)_mm_extract_epi16(pcm8, 7); - dstl[(47 - i)*nch] = (ma_int16)_mm_extract_epi16(pcm8, 2); - dstl[(49 + i)*nch] = (ma_int16)_mm_extract_epi16(pcm8, 6); -#else - int16x4_t pcma, pcmb; - a = MA_DR_MP3_VADD(a, MA_DR_MP3_VSET(0.5f)); - b = MA_DR_MP3_VADD(b, MA_DR_MP3_VSET(0.5f)); - pcma = vqmovn_s32(vqaddq_s32(vcvtq_s32_f32(a), vreinterpretq_s32_u32(vcltq_f32(a, MA_DR_MP3_VSET(0))))); - pcmb = vqmovn_s32(vqaddq_s32(vcvtq_s32_f32(b), vreinterpretq_s32_u32(vcltq_f32(b, MA_DR_MP3_VSET(0))))); - vst1_lane_s16(dstr + (15 - i)*nch, pcma, 1); - vst1_lane_s16(dstr + (17 + i)*nch, pcmb, 1); - vst1_lane_s16(dstl + (15 - i)*nch, pcma, 0); - vst1_lane_s16(dstl + (17 + i)*nch, pcmb, 0); - vst1_lane_s16(dstr + (47 - i)*nch, pcma, 3); - vst1_lane_s16(dstr + (49 + i)*nch, pcmb, 3); - vst1_lane_s16(dstl + (47 - i)*nch, pcma, 2); - vst1_lane_s16(dstl + (49 + i)*nch, pcmb, 2); -#endif -#else - #if MA_DR_MP3_HAVE_SSE - static const ma_dr_mp3_f4 g_scale = { 1.0f/32768.0f, 1.0f/32768.0f, 1.0f/32768.0f, 1.0f/32768.0f }; - #else - const ma_dr_mp3_f4 g_scale = vdupq_n_f32(1.0f/32768.0f); - #endif - a = MA_DR_MP3_VMUL(a, g_scale); - b = MA_DR_MP3_VMUL(b, g_scale); -#if MA_DR_MP3_HAVE_SSE - _mm_store_ss(dstr + (15 - i)*nch, _mm_shuffle_ps(a, a, _MM_SHUFFLE(1, 1, 1, 1))); - _mm_store_ss(dstr + (17 + i)*nch, _mm_shuffle_ps(b, b, _MM_SHUFFLE(1, 1, 1, 1))); - _mm_store_ss(dstl + (15 - i)*nch, _mm_shuffle_ps(a, a, _MM_SHUFFLE(0, 0, 0, 0))); - _mm_store_ss(dstl + (17 + i)*nch, _mm_shuffle_ps(b, b, _MM_SHUFFLE(0, 0, 0, 0))); - _mm_store_ss(dstr + (47 - i)*nch, _mm_shuffle_ps(a, a, _MM_SHUFFLE(3, 3, 3, 3))); - _mm_store_ss(dstr + (49 + i)*nch, _mm_shuffle_ps(b, b, _MM_SHUFFLE(3, 3, 3, 3))); - _mm_store_ss(dstl + (47 - i)*nch, _mm_shuffle_ps(a, a, _MM_SHUFFLE(2, 2, 2, 2))); - _mm_store_ss(dstl + (49 + i)*nch, _mm_shuffle_ps(b, b, _MM_SHUFFLE(2, 2, 2, 2))); -#else - vst1q_lane_f32(dstr + (15 - i)*nch, a, 1); - vst1q_lane_f32(dstr + (17 + i)*nch, b, 1); - vst1q_lane_f32(dstl + (15 - i)*nch, a, 0); - vst1q_lane_f32(dstl + (17 + i)*nch, b, 0); - vst1q_lane_f32(dstr + (47 - i)*nch, a, 3); - vst1q_lane_f32(dstr + (49 + i)*nch, b, 3); - vst1q_lane_f32(dstl + (47 - i)*nch, a, 2); - vst1q_lane_f32(dstl + (49 + i)*nch, b, 2); -#endif -#endif - } - } else -#endif -#ifdef MA_DR_MP3_ONLY_SIMD - {} -#else - for (i = 14; i >= 0; i--) - { -#define MA_DR_MP3_LOAD(k) float w0 = *w++; float w1 = *w++; float *vz = &zlin[4*i - k*64]; float *vy = &zlin[4*i - (15 - k)*64]; -#define MA_DR_MP3_S0(k) { int j; MA_DR_MP3_LOAD(k); for (j = 0; j < 4; j++) b[j] = vz[j]*w1 + vy[j]*w0, a[j] = vz[j]*w0 - vy[j]*w1; } -#define MA_DR_MP3_S1(k) { int j; MA_DR_MP3_LOAD(k); for (j = 0; j < 4; j++) b[j] += vz[j]*w1 + vy[j]*w0, a[j] += vz[j]*w0 - vy[j]*w1; } -#define MA_DR_MP3_S2(k) { int j; MA_DR_MP3_LOAD(k); for (j = 0; j < 4; j++) b[j] += vz[j]*w1 + vy[j]*w0, a[j] += vy[j]*w1 - vz[j]*w0; } - float a[4], b[4]; - zlin[4*i] = xl[18*(31 - i)]; - zlin[4*i + 1] = xr[18*(31 - i)]; - zlin[4*i + 2] = xl[1 + 18*(31 - i)]; - zlin[4*i + 3] = xr[1 + 18*(31 - i)]; - zlin[4*(i + 16)] = xl[1 + 18*(1 + i)]; - zlin[4*(i + 16) + 1] = xr[1 + 18*(1 + i)]; - zlin[4*(i - 16) + 2] = xl[18*(1 + i)]; - zlin[4*(i - 16) + 3] = xr[18*(1 + i)]; - MA_DR_MP3_S0(0) MA_DR_MP3_S2(1) MA_DR_MP3_S1(2) MA_DR_MP3_S2(3) MA_DR_MP3_S1(4) MA_DR_MP3_S2(5) MA_DR_MP3_S1(6) MA_DR_MP3_S2(7) - dstr[(15 - i)*nch] = ma_dr_mp3d_scale_pcm(a[1]); - dstr[(17 + i)*nch] = ma_dr_mp3d_scale_pcm(b[1]); - dstl[(15 - i)*nch] = ma_dr_mp3d_scale_pcm(a[0]); - dstl[(17 + i)*nch] = ma_dr_mp3d_scale_pcm(b[0]); - dstr[(47 - i)*nch] = ma_dr_mp3d_scale_pcm(a[3]); - dstr[(49 + i)*nch] = ma_dr_mp3d_scale_pcm(b[3]); - dstl[(47 - i)*nch] = ma_dr_mp3d_scale_pcm(a[2]); - dstl[(49 + i)*nch] = ma_dr_mp3d_scale_pcm(b[2]); - } -#endif -} -static void ma_dr_mp3d_synth_granule(float *qmf_state, float *grbuf, int nbands, int nch, ma_dr_mp3d_sample_t *pcm, float *lins) -{ - int i; - for (i = 0; i < nch; i++) - { - ma_dr_mp3d_DCT_II(grbuf + 576*i, nbands); - } - MA_DR_MP3_COPY_MEMORY(lins, qmf_state, sizeof(float)*15*64); - for (i = 0; i < nbands; i += 2) - { - ma_dr_mp3d_synth(grbuf + i, pcm + 32*nch*i, nch, lins + i*64); - } -#ifndef MA_DR_MP3_NONSTANDARD_BUT_LOGICAL - if (nch == 1) - { - for (i = 0; i < 15*64; i += 2) - { - qmf_state[i] = lins[nbands*64 + i]; - } - } else -#endif - { - MA_DR_MP3_COPY_MEMORY(qmf_state, lins + nbands*64, sizeof(float)*15*64); - } -} -static int ma_dr_mp3d_match_frame(const ma_uint8 *hdr, int mp3_bytes, int frame_bytes) -{ - int i, nmatch; - for (i = 0, nmatch = 0; nmatch < MA_DR_MP3_MAX_FRAME_SYNC_MATCHES; nmatch++) - { - i += ma_dr_mp3_hdr_frame_bytes(hdr + i, frame_bytes) + ma_dr_mp3_hdr_padding(hdr + i); - if (i + MA_DR_MP3_HDR_SIZE > mp3_bytes) - return nmatch > 0; - if (!ma_dr_mp3_hdr_compare(hdr, hdr + i)) - return 0; - } - return 1; -} -static int ma_dr_mp3d_find_frame(const ma_uint8 *mp3, int mp3_bytes, int *free_format_bytes, int *ptr_frame_bytes) -{ - int i, k; - for (i = 0; i < mp3_bytes - MA_DR_MP3_HDR_SIZE; i++, mp3++) - { - if (ma_dr_mp3_hdr_valid(mp3)) - { - int frame_bytes = ma_dr_mp3_hdr_frame_bytes(mp3, *free_format_bytes); - int frame_and_padding = frame_bytes + ma_dr_mp3_hdr_padding(mp3); - for (k = MA_DR_MP3_HDR_SIZE; !frame_bytes && k < MA_DR_MP3_MAX_FREE_FORMAT_FRAME_SIZE && i + 2*k < mp3_bytes - MA_DR_MP3_HDR_SIZE; k++) - { - if (ma_dr_mp3_hdr_compare(mp3, mp3 + k)) - { - int fb = k - ma_dr_mp3_hdr_padding(mp3); - int nextfb = fb + ma_dr_mp3_hdr_padding(mp3 + k); - if (i + k + nextfb + MA_DR_MP3_HDR_SIZE > mp3_bytes || !ma_dr_mp3_hdr_compare(mp3, mp3 + k + nextfb)) - continue; - frame_and_padding = k; - frame_bytes = fb; - *free_format_bytes = fb; - } - } - if ((frame_bytes && i + frame_and_padding <= mp3_bytes && - ma_dr_mp3d_match_frame(mp3, mp3_bytes - i, frame_bytes)) || - (!i && frame_and_padding == mp3_bytes)) - { - *ptr_frame_bytes = frame_and_padding; - return i; - } - *free_format_bytes = 0; - } - } - *ptr_frame_bytes = 0; - return mp3_bytes; -} -MA_API void ma_dr_mp3dec_init(ma_dr_mp3dec *dec) -{ - dec->header[0] = 0; -} -MA_API int ma_dr_mp3dec_decode_frame(ma_dr_mp3dec *dec, const ma_uint8 *mp3, int mp3_bytes, void *pcm, ma_dr_mp3dec_frame_info *info) -{ - int i = 0, igr, frame_size = 0, success = 1; - const ma_uint8 *hdr; - ma_dr_mp3_bs bs_frame[1]; - ma_dr_mp3dec_scratch scratch; - if (mp3_bytes > 4 && dec->header[0] == 0xff && ma_dr_mp3_hdr_compare(dec->header, mp3)) - { - frame_size = ma_dr_mp3_hdr_frame_bytes(mp3, dec->free_format_bytes) + ma_dr_mp3_hdr_padding(mp3); - if (frame_size != mp3_bytes && (frame_size + MA_DR_MP3_HDR_SIZE > mp3_bytes || !ma_dr_mp3_hdr_compare(mp3, mp3 + frame_size))) - { - frame_size = 0; - } - } - if (!frame_size) - { - MA_DR_MP3_ZERO_MEMORY(dec, sizeof(ma_dr_mp3dec)); - i = ma_dr_mp3d_find_frame(mp3, mp3_bytes, &dec->free_format_bytes, &frame_size); - if (!frame_size || i + frame_size > mp3_bytes) - { - info->frame_bytes = i; - return 0; - } - } - hdr = mp3 + i; - MA_DR_MP3_COPY_MEMORY(dec->header, hdr, MA_DR_MP3_HDR_SIZE); - info->frame_bytes = i + frame_size; - info->channels = MA_DR_MP3_HDR_IS_MONO(hdr) ? 1 : 2; - info->hz = ma_dr_mp3_hdr_sample_rate_hz(hdr); - info->layer = 4 - MA_DR_MP3_HDR_GET_LAYER(hdr); - info->bitrate_kbps = ma_dr_mp3_hdr_bitrate_kbps(hdr); - ma_dr_mp3_bs_init(bs_frame, hdr + MA_DR_MP3_HDR_SIZE, frame_size - MA_DR_MP3_HDR_SIZE); - if (MA_DR_MP3_HDR_IS_CRC(hdr)) - { - ma_dr_mp3_bs_get_bits(bs_frame, 16); - } - if (info->layer == 3) - { - int main_data_begin = ma_dr_mp3_L3_read_side_info(bs_frame, scratch.gr_info, hdr); - if (main_data_begin < 0 || bs_frame->pos > bs_frame->limit) - { - ma_dr_mp3dec_init(dec); - return 0; - } - success = ma_dr_mp3_L3_restore_reservoir(dec, bs_frame, &scratch, main_data_begin); - if (success && pcm != NULL) - { - for (igr = 0; igr < (MA_DR_MP3_HDR_TEST_MPEG1(hdr) ? 2 : 1); igr++, pcm = MA_DR_MP3_OFFSET_PTR(pcm, sizeof(ma_dr_mp3d_sample_t)*576*info->channels)) - { - MA_DR_MP3_ZERO_MEMORY(scratch.grbuf[0], 576*2*sizeof(float)); - ma_dr_mp3_L3_decode(dec, &scratch, scratch.gr_info + igr*info->channels, info->channels); - ma_dr_mp3d_synth_granule(dec->qmf_state, scratch.grbuf[0], 18, info->channels, (ma_dr_mp3d_sample_t*)pcm, scratch.syn[0]); - } - } - ma_dr_mp3_L3_save_reservoir(dec, &scratch); - } else - { -#ifdef MA_DR_MP3_ONLY_MP3 - return 0; -#else - ma_dr_mp3_L12_scale_info sci[1]; - if (pcm == NULL) { - return ma_dr_mp3_hdr_frame_samples(hdr); - } - ma_dr_mp3_L12_read_scale_info(hdr, bs_frame, sci); - MA_DR_MP3_ZERO_MEMORY(scratch.grbuf[0], 576*2*sizeof(float)); - for (i = 0, igr = 0; igr < 3; igr++) - { - if (12 == (i += ma_dr_mp3_L12_dequantize_granule(scratch.grbuf[0] + i, bs_frame, sci, info->layer | 1))) - { - i = 0; - ma_dr_mp3_L12_apply_scf_384(sci, sci->scf + igr, scratch.grbuf[0]); - ma_dr_mp3d_synth_granule(dec->qmf_state, scratch.grbuf[0], 12, info->channels, (ma_dr_mp3d_sample_t*)pcm, scratch.syn[0]); - MA_DR_MP3_ZERO_MEMORY(scratch.grbuf[0], 576*2*sizeof(float)); - pcm = MA_DR_MP3_OFFSET_PTR(pcm, sizeof(ma_dr_mp3d_sample_t)*384*info->channels); - } - if (bs_frame->pos > bs_frame->limit) - { - ma_dr_mp3dec_init(dec); - return 0; - } - } -#endif - } - return success*ma_dr_mp3_hdr_frame_samples(dec->header); -} -MA_API void ma_dr_mp3dec_f32_to_s16(const float *in, ma_int16 *out, size_t num_samples) -{ - size_t i = 0; -#if MA_DR_MP3_HAVE_SIMD - size_t aligned_count = num_samples & ~7; - for(; i < aligned_count; i+=8) - { - ma_dr_mp3_f4 scale = MA_DR_MP3_VSET(32768.0f); - ma_dr_mp3_f4 a = MA_DR_MP3_VMUL(MA_DR_MP3_VLD(&in[i ]), scale); - ma_dr_mp3_f4 b = MA_DR_MP3_VMUL(MA_DR_MP3_VLD(&in[i+4]), scale); -#if MA_DR_MP3_HAVE_SSE - ma_dr_mp3_f4 s16max = MA_DR_MP3_VSET( 32767.0f); - ma_dr_mp3_f4 s16min = MA_DR_MP3_VSET(-32768.0f); - __m128i pcm8 = _mm_packs_epi32(_mm_cvtps_epi32(_mm_max_ps(_mm_min_ps(a, s16max), s16min)), - _mm_cvtps_epi32(_mm_max_ps(_mm_min_ps(b, s16max), s16min))); - out[i ] = (ma_int16)_mm_extract_epi16(pcm8, 0); - out[i+1] = (ma_int16)_mm_extract_epi16(pcm8, 1); - out[i+2] = (ma_int16)_mm_extract_epi16(pcm8, 2); - out[i+3] = (ma_int16)_mm_extract_epi16(pcm8, 3); - out[i+4] = (ma_int16)_mm_extract_epi16(pcm8, 4); - out[i+5] = (ma_int16)_mm_extract_epi16(pcm8, 5); - out[i+6] = (ma_int16)_mm_extract_epi16(pcm8, 6); - out[i+7] = (ma_int16)_mm_extract_epi16(pcm8, 7); -#else - int16x4_t pcma, pcmb; - a = MA_DR_MP3_VADD(a, MA_DR_MP3_VSET(0.5f)); - b = MA_DR_MP3_VADD(b, MA_DR_MP3_VSET(0.5f)); - pcma = vqmovn_s32(vqaddq_s32(vcvtq_s32_f32(a), vreinterpretq_s32_u32(vcltq_f32(a, MA_DR_MP3_VSET(0))))); - pcmb = vqmovn_s32(vqaddq_s32(vcvtq_s32_f32(b), vreinterpretq_s32_u32(vcltq_f32(b, MA_DR_MP3_VSET(0))))); - vst1_lane_s16(out+i , pcma, 0); - vst1_lane_s16(out+i+1, pcma, 1); - vst1_lane_s16(out+i+2, pcma, 2); - vst1_lane_s16(out+i+3, pcma, 3); - vst1_lane_s16(out+i+4, pcmb, 0); - vst1_lane_s16(out+i+5, pcmb, 1); - vst1_lane_s16(out+i+6, pcmb, 2); - vst1_lane_s16(out+i+7, pcmb, 3); -#endif - } -#endif - for(; i < num_samples; i++) - { - float sample = in[i] * 32768.0f; - if (sample >= 32766.5f) - out[i] = (ma_int16) 32767; - else if (sample <= -32767.5f) - out[i] = (ma_int16)-32768; - else - { - short s = (ma_int16)(sample + .5f); - s -= (s < 0); - out[i] = s; - } - } -} -#ifndef MA_DR_MP3_SEEK_LEADING_MP3_FRAMES -#define MA_DR_MP3_SEEK_LEADING_MP3_FRAMES 2 -#endif -#define MA_DR_MP3_MIN_DATA_CHUNK_SIZE 16384 -#ifndef MA_DR_MP3_DATA_CHUNK_SIZE -#define MA_DR_MP3_DATA_CHUNK_SIZE (MA_DR_MP3_MIN_DATA_CHUNK_SIZE*4) -#endif -#define MA_DR_MP3_COUNTOF(x) (sizeof(x) / sizeof(x[0])) -#define MA_DR_MP3_CLAMP(x, lo, hi) (MA_DR_MP3_MAX(lo, MA_DR_MP3_MIN(x, hi))) -#ifndef MA_DR_MP3_PI_D -#define MA_DR_MP3_PI_D 3.14159265358979323846264 -#endif -#define MA_DR_MP3_DEFAULT_RESAMPLER_LPF_ORDER 2 -static MA_INLINE float ma_dr_mp3_mix_f32(float x, float y, float a) -{ - return x*(1-a) + y*a; -} -static MA_INLINE float ma_dr_mp3_mix_f32_fast(float x, float y, float a) -{ - float r0 = (y - x); - float r1 = r0*a; - return x + r1; -} -static MA_INLINE ma_uint32 ma_dr_mp3_gcf_u32(ma_uint32 a, ma_uint32 b) -{ - for (;;) { - if (b == 0) { - break; - } else { - ma_uint32 t = a; - a = b; - b = t % a; - } - } - return a; -} -static void* ma_dr_mp3__malloc_default(size_t sz, void* pUserData) -{ - (void)pUserData; - return MA_DR_MP3_MALLOC(sz); -} -static void* ma_dr_mp3__realloc_default(void* p, size_t sz, void* pUserData) -{ - (void)pUserData; - return MA_DR_MP3_REALLOC(p, sz); -} -static void ma_dr_mp3__free_default(void* p, void* pUserData) -{ - (void)pUserData; - MA_DR_MP3_FREE(p); -} -static void* ma_dr_mp3__malloc_from_callbacks(size_t sz, const ma_allocation_callbacks* pAllocationCallbacks) -{ - if (pAllocationCallbacks == NULL) { - return NULL; - } - if (pAllocationCallbacks->onMalloc != NULL) { - return pAllocationCallbacks->onMalloc(sz, pAllocationCallbacks->pUserData); - } - if (pAllocationCallbacks->onRealloc != NULL) { - return pAllocationCallbacks->onRealloc(NULL, sz, pAllocationCallbacks->pUserData); - } - return NULL; -} -static void* ma_dr_mp3__realloc_from_callbacks(void* p, size_t szNew, size_t szOld, const ma_allocation_callbacks* pAllocationCallbacks) -{ - if (pAllocationCallbacks == NULL) { - return NULL; - } - if (pAllocationCallbacks->onRealloc != NULL) { - return pAllocationCallbacks->onRealloc(p, szNew, pAllocationCallbacks->pUserData); - } - if (pAllocationCallbacks->onMalloc != NULL && pAllocationCallbacks->onFree != NULL) { - void* p2; - p2 = pAllocationCallbacks->onMalloc(szNew, pAllocationCallbacks->pUserData); - if (p2 == NULL) { - return NULL; - } - if (p != NULL) { - MA_DR_MP3_COPY_MEMORY(p2, p, szOld); - pAllocationCallbacks->onFree(p, pAllocationCallbacks->pUserData); - } - return p2; - } - return NULL; -} -static void ma_dr_mp3__free_from_callbacks(void* p, const ma_allocation_callbacks* pAllocationCallbacks) -{ - if (p == NULL || pAllocationCallbacks == NULL) { - return; - } - if (pAllocationCallbacks->onFree != NULL) { - pAllocationCallbacks->onFree(p, pAllocationCallbacks->pUserData); - } -} -static ma_allocation_callbacks ma_dr_mp3_copy_allocation_callbacks_or_defaults(const ma_allocation_callbacks* pAllocationCallbacks) -{ - if (pAllocationCallbacks != NULL) { - return *pAllocationCallbacks; - } else { - ma_allocation_callbacks allocationCallbacks; - allocationCallbacks.pUserData = NULL; - allocationCallbacks.onMalloc = ma_dr_mp3__malloc_default; - allocationCallbacks.onRealloc = ma_dr_mp3__realloc_default; - allocationCallbacks.onFree = ma_dr_mp3__free_default; - return allocationCallbacks; - } -} -static size_t ma_dr_mp3__on_read(ma_dr_mp3* pMP3, void* pBufferOut, size_t bytesToRead) -{ - size_t bytesRead = pMP3->onRead(pMP3->pUserData, pBufferOut, bytesToRead); - pMP3->streamCursor += bytesRead; - return bytesRead; -} -static ma_bool32 ma_dr_mp3__on_seek(ma_dr_mp3* pMP3, int offset, ma_dr_mp3_seek_origin origin) -{ - MA_DR_MP3_ASSERT(offset >= 0); - if (!pMP3->onSeek(pMP3->pUserData, offset, origin)) { - return MA_FALSE; - } - if (origin == ma_dr_mp3_seek_origin_start) { - pMP3->streamCursor = (ma_uint64)offset; - } else { - pMP3->streamCursor += offset; - } - return MA_TRUE; -} -static ma_bool32 ma_dr_mp3__on_seek_64(ma_dr_mp3* pMP3, ma_uint64 offset, ma_dr_mp3_seek_origin origin) -{ - if (offset <= 0x7FFFFFFF) { - return ma_dr_mp3__on_seek(pMP3, (int)offset, origin); - } - if (!ma_dr_mp3__on_seek(pMP3, 0x7FFFFFFF, ma_dr_mp3_seek_origin_start)) { - return MA_FALSE; - } - offset -= 0x7FFFFFFF; - while (offset > 0) { - if (offset <= 0x7FFFFFFF) { - if (!ma_dr_mp3__on_seek(pMP3, (int)offset, ma_dr_mp3_seek_origin_current)) { - return MA_FALSE; - } - offset = 0; - } else { - if (!ma_dr_mp3__on_seek(pMP3, 0x7FFFFFFF, ma_dr_mp3_seek_origin_current)) { - return MA_FALSE; - } - offset -= 0x7FFFFFFF; - } - } - return MA_TRUE; -} -static ma_uint32 ma_dr_mp3_decode_next_frame_ex__callbacks(ma_dr_mp3* pMP3, ma_dr_mp3d_sample_t* pPCMFrames) -{ - ma_uint32 pcmFramesRead = 0; - MA_DR_MP3_ASSERT(pMP3 != NULL); - MA_DR_MP3_ASSERT(pMP3->onRead != NULL); - if (pMP3->atEnd) { - return 0; - } - for (;;) { - ma_dr_mp3dec_frame_info info; - if (pMP3->dataSize < MA_DR_MP3_MIN_DATA_CHUNK_SIZE) { - size_t bytesRead; - if (pMP3->pData != NULL) { - MA_DR_MP3_MOVE_MEMORY(pMP3->pData, pMP3->pData + pMP3->dataConsumed, pMP3->dataSize); - } - pMP3->dataConsumed = 0; - if (pMP3->dataCapacity < MA_DR_MP3_DATA_CHUNK_SIZE) { - ma_uint8* pNewData; - size_t newDataCap; - newDataCap = MA_DR_MP3_DATA_CHUNK_SIZE; - pNewData = (ma_uint8*)ma_dr_mp3__realloc_from_callbacks(pMP3->pData, newDataCap, pMP3->dataCapacity, &pMP3->allocationCallbacks); - if (pNewData == NULL) { - return 0; - } - pMP3->pData = pNewData; - pMP3->dataCapacity = newDataCap; - } - bytesRead = ma_dr_mp3__on_read(pMP3, pMP3->pData + pMP3->dataSize, (pMP3->dataCapacity - pMP3->dataSize)); - if (bytesRead == 0) { - if (pMP3->dataSize == 0) { - pMP3->atEnd = MA_TRUE; - return 0; - } - } - pMP3->dataSize += bytesRead; - } - if (pMP3->dataSize > INT_MAX) { - pMP3->atEnd = MA_TRUE; - return 0; - } - MA_DR_MP3_ASSERT(pMP3->pData != NULL); - MA_DR_MP3_ASSERT(pMP3->dataCapacity > 0); - if (pMP3->pData == NULL) { - return 0; - } - pcmFramesRead = ma_dr_mp3dec_decode_frame(&pMP3->decoder, pMP3->pData + pMP3->dataConsumed, (int)pMP3->dataSize, pPCMFrames, &info); - if (info.frame_bytes > 0) { - pMP3->dataConsumed += (size_t)info.frame_bytes; - pMP3->dataSize -= (size_t)info.frame_bytes; - } - if (pcmFramesRead > 0) { - pcmFramesRead = ma_dr_mp3_hdr_frame_samples(pMP3->decoder.header); - pMP3->pcmFramesConsumedInMP3Frame = 0; - pMP3->pcmFramesRemainingInMP3Frame = pcmFramesRead; - pMP3->mp3FrameChannels = info.channels; - pMP3->mp3FrameSampleRate = info.hz; - break; - } else if (info.frame_bytes == 0) { - size_t bytesRead; - MA_DR_MP3_MOVE_MEMORY(pMP3->pData, pMP3->pData + pMP3->dataConsumed, pMP3->dataSize); - pMP3->dataConsumed = 0; - if (pMP3->dataCapacity == pMP3->dataSize) { - ma_uint8* pNewData; - size_t newDataCap; - newDataCap = pMP3->dataCapacity + MA_DR_MP3_DATA_CHUNK_SIZE; - pNewData = (ma_uint8*)ma_dr_mp3__realloc_from_callbacks(pMP3->pData, newDataCap, pMP3->dataCapacity, &pMP3->allocationCallbacks); - if (pNewData == NULL) { - return 0; - } - pMP3->pData = pNewData; - pMP3->dataCapacity = newDataCap; - } - bytesRead = ma_dr_mp3__on_read(pMP3, pMP3->pData + pMP3->dataSize, (pMP3->dataCapacity - pMP3->dataSize)); - if (bytesRead == 0) { - pMP3->atEnd = MA_TRUE; - return 0; - } - pMP3->dataSize += bytesRead; - } - }; - return pcmFramesRead; -} -static ma_uint32 ma_dr_mp3_decode_next_frame_ex__memory(ma_dr_mp3* pMP3, ma_dr_mp3d_sample_t* pPCMFrames) -{ - ma_uint32 pcmFramesRead = 0; - ma_dr_mp3dec_frame_info info; - MA_DR_MP3_ASSERT(pMP3 != NULL); - MA_DR_MP3_ASSERT(pMP3->memory.pData != NULL); - if (pMP3->atEnd) { - return 0; - } - for (;;) { - pcmFramesRead = ma_dr_mp3dec_decode_frame(&pMP3->decoder, pMP3->memory.pData + pMP3->memory.currentReadPos, (int)(pMP3->memory.dataSize - pMP3->memory.currentReadPos), pPCMFrames, &info); - if (pcmFramesRead > 0) { - pcmFramesRead = ma_dr_mp3_hdr_frame_samples(pMP3->decoder.header); - pMP3->pcmFramesConsumedInMP3Frame = 0; - pMP3->pcmFramesRemainingInMP3Frame = pcmFramesRead; - pMP3->mp3FrameChannels = info.channels; - pMP3->mp3FrameSampleRate = info.hz; - break; - } else if (info.frame_bytes > 0) { - pMP3->memory.currentReadPos += (size_t)info.frame_bytes; - } else { - break; - } - } - pMP3->memory.currentReadPos += (size_t)info.frame_bytes; - return pcmFramesRead; -} -static ma_uint32 ma_dr_mp3_decode_next_frame_ex(ma_dr_mp3* pMP3, ma_dr_mp3d_sample_t* pPCMFrames) -{ - if (pMP3->memory.pData != NULL && pMP3->memory.dataSize > 0) { - return ma_dr_mp3_decode_next_frame_ex__memory(pMP3, pPCMFrames); - } else { - return ma_dr_mp3_decode_next_frame_ex__callbacks(pMP3, pPCMFrames); - } -} -static ma_uint32 ma_dr_mp3_decode_next_frame(ma_dr_mp3* pMP3) -{ - MA_DR_MP3_ASSERT(pMP3 != NULL); - return ma_dr_mp3_decode_next_frame_ex(pMP3, (ma_dr_mp3d_sample_t*)pMP3->pcmFrames); -} -#if 0 -static ma_uint32 ma_dr_mp3_seek_next_frame(ma_dr_mp3* pMP3) -{ - ma_uint32 pcmFrameCount; - MA_DR_MP3_ASSERT(pMP3 != NULL); - pcmFrameCount = ma_dr_mp3_decode_next_frame_ex(pMP3, NULL); - if (pcmFrameCount == 0) { - return 0; - } - pMP3->currentPCMFrame += pcmFrameCount; - pMP3->pcmFramesConsumedInMP3Frame = pcmFrameCount; - pMP3->pcmFramesRemainingInMP3Frame = 0; - return pcmFrameCount; -} -#endif -static ma_bool32 ma_dr_mp3_init_internal(ma_dr_mp3* pMP3, ma_dr_mp3_read_proc onRead, ma_dr_mp3_seek_proc onSeek, void* pUserData, const ma_allocation_callbacks* pAllocationCallbacks) -{ - MA_DR_MP3_ASSERT(pMP3 != NULL); - MA_DR_MP3_ASSERT(onRead != NULL); - ma_dr_mp3dec_init(&pMP3->decoder); - pMP3->onRead = onRead; - pMP3->onSeek = onSeek; - pMP3->pUserData = pUserData; - pMP3->allocationCallbacks = ma_dr_mp3_copy_allocation_callbacks_or_defaults(pAllocationCallbacks); - if (pMP3->allocationCallbacks.onFree == NULL || (pMP3->allocationCallbacks.onMalloc == NULL && pMP3->allocationCallbacks.onRealloc == NULL)) { - return MA_FALSE; - } - if (ma_dr_mp3_decode_next_frame(pMP3) == 0) { - ma_dr_mp3__free_from_callbacks(pMP3->pData, &pMP3->allocationCallbacks); - return MA_FALSE; - } - pMP3->channels = pMP3->mp3FrameChannels; - pMP3->sampleRate = pMP3->mp3FrameSampleRate; - return MA_TRUE; -} -MA_API ma_bool32 ma_dr_mp3_init(ma_dr_mp3* pMP3, ma_dr_mp3_read_proc onRead, ma_dr_mp3_seek_proc onSeek, void* pUserData, const ma_allocation_callbacks* pAllocationCallbacks) -{ - if (pMP3 == NULL || onRead == NULL) { - return MA_FALSE; - } - MA_DR_MP3_ZERO_OBJECT(pMP3); - return ma_dr_mp3_init_internal(pMP3, onRead, onSeek, pUserData, pAllocationCallbacks); -} -static size_t ma_dr_mp3__on_read_memory(void* pUserData, void* pBufferOut, size_t bytesToRead) -{ - ma_dr_mp3* pMP3 = (ma_dr_mp3*)pUserData; - size_t bytesRemaining; - MA_DR_MP3_ASSERT(pMP3 != NULL); - MA_DR_MP3_ASSERT(pMP3->memory.dataSize >= pMP3->memory.currentReadPos); - bytesRemaining = pMP3->memory.dataSize - pMP3->memory.currentReadPos; - if (bytesToRead > bytesRemaining) { - bytesToRead = bytesRemaining; - } - if (bytesToRead > 0) { - MA_DR_MP3_COPY_MEMORY(pBufferOut, pMP3->memory.pData + pMP3->memory.currentReadPos, bytesToRead); - pMP3->memory.currentReadPos += bytesToRead; - } - return bytesToRead; -} -static ma_bool32 ma_dr_mp3__on_seek_memory(void* pUserData, int byteOffset, ma_dr_mp3_seek_origin origin) -{ - ma_dr_mp3* pMP3 = (ma_dr_mp3*)pUserData; - MA_DR_MP3_ASSERT(pMP3 != NULL); - if (origin == ma_dr_mp3_seek_origin_current) { - if (byteOffset > 0) { - if (pMP3->memory.currentReadPos + byteOffset > pMP3->memory.dataSize) { - byteOffset = (int)(pMP3->memory.dataSize - pMP3->memory.currentReadPos); - } - } else { - if (pMP3->memory.currentReadPos < (size_t)-byteOffset) { - byteOffset = -(int)pMP3->memory.currentReadPos; - } - } - pMP3->memory.currentReadPos += byteOffset; - } else { - if ((ma_uint32)byteOffset <= pMP3->memory.dataSize) { - pMP3->memory.currentReadPos = byteOffset; - } else { - pMP3->memory.currentReadPos = pMP3->memory.dataSize; - } - } - return MA_TRUE; -} -MA_API ma_bool32 ma_dr_mp3_init_memory(ma_dr_mp3* pMP3, const void* pData, size_t dataSize, const ma_allocation_callbacks* pAllocationCallbacks) -{ - if (pMP3 == NULL) { - return MA_FALSE; - } - MA_DR_MP3_ZERO_OBJECT(pMP3); - if (pData == NULL || dataSize == 0) { - return MA_FALSE; - } - pMP3->memory.pData = (const ma_uint8*)pData; - pMP3->memory.dataSize = dataSize; - pMP3->memory.currentReadPos = 0; - return ma_dr_mp3_init_internal(pMP3, ma_dr_mp3__on_read_memory, ma_dr_mp3__on_seek_memory, pMP3, pAllocationCallbacks); -} -#ifndef MA_DR_MP3_NO_STDIO -#include -#include -static size_t ma_dr_mp3__on_read_stdio(void* pUserData, void* pBufferOut, size_t bytesToRead) -{ - return fread(pBufferOut, 1, bytesToRead, (FILE*)pUserData); -} -static ma_bool32 ma_dr_mp3__on_seek_stdio(void* pUserData, int offset, ma_dr_mp3_seek_origin origin) -{ - return fseek((FILE*)pUserData, offset, (origin == ma_dr_mp3_seek_origin_current) ? SEEK_CUR : SEEK_SET) == 0; -} -MA_API ma_bool32 ma_dr_mp3_init_file(ma_dr_mp3* pMP3, const char* pFilePath, const ma_allocation_callbacks* pAllocationCallbacks) -{ - ma_bool32 result; - FILE* pFile; - if (ma_fopen(&pFile, pFilePath, "rb") != MA_SUCCESS) { - return MA_FALSE; - } - result = ma_dr_mp3_init(pMP3, ma_dr_mp3__on_read_stdio, ma_dr_mp3__on_seek_stdio, (void*)pFile, pAllocationCallbacks); - if (result != MA_TRUE) { - fclose(pFile); - return result; - } - return MA_TRUE; -} -MA_API ma_bool32 ma_dr_mp3_init_file_w(ma_dr_mp3* pMP3, const wchar_t* pFilePath, const ma_allocation_callbacks* pAllocationCallbacks) -{ - ma_bool32 result; - FILE* pFile; - if (ma_wfopen(&pFile, pFilePath, L"rb", pAllocationCallbacks) != MA_SUCCESS) { - return MA_FALSE; - } - result = ma_dr_mp3_init(pMP3, ma_dr_mp3__on_read_stdio, ma_dr_mp3__on_seek_stdio, (void*)pFile, pAllocationCallbacks); - if (result != MA_TRUE) { - fclose(pFile); - return result; - } - return MA_TRUE; -} -#endif -MA_API void ma_dr_mp3_uninit(ma_dr_mp3* pMP3) -{ - if (pMP3 == NULL) { - return; - } -#ifndef MA_DR_MP3_NO_STDIO - if (pMP3->onRead == ma_dr_mp3__on_read_stdio) { - FILE* pFile = (FILE*)pMP3->pUserData; - if (pFile != NULL) { - fclose(pFile); - pMP3->pUserData = NULL; - } - } -#endif - ma_dr_mp3__free_from_callbacks(pMP3->pData, &pMP3->allocationCallbacks); -} -#if defined(MA_DR_MP3_FLOAT_OUTPUT) -static void ma_dr_mp3_f32_to_s16(ma_int16* dst, const float* src, ma_uint64 sampleCount) -{ - ma_uint64 i; - ma_uint64 i4; - ma_uint64 sampleCount4; - i = 0; - sampleCount4 = sampleCount >> 2; - for (i4 = 0; i4 < sampleCount4; i4 += 1) { - float x0 = src[i+0]; - float x1 = src[i+1]; - float x2 = src[i+2]; - float x3 = src[i+3]; - x0 = ((x0 < -1) ? -1 : ((x0 > 1) ? 1 : x0)); - x1 = ((x1 < -1) ? -1 : ((x1 > 1) ? 1 : x1)); - x2 = ((x2 < -1) ? -1 : ((x2 > 1) ? 1 : x2)); - x3 = ((x3 < -1) ? -1 : ((x3 > 1) ? 1 : x3)); - x0 = x0 * 32767.0f; - x1 = x1 * 32767.0f; - x2 = x2 * 32767.0f; - x3 = x3 * 32767.0f; - dst[i+0] = (ma_int16)x0; - dst[i+1] = (ma_int16)x1; - dst[i+2] = (ma_int16)x2; - dst[i+3] = (ma_int16)x3; - i += 4; - } - for (; i < sampleCount; i += 1) { - float x = src[i]; - x = ((x < -1) ? -1 : ((x > 1) ? 1 : x)); - x = x * 32767.0f; - dst[i] = (ma_int16)x; - } -} -#endif -#if !defined(MA_DR_MP3_FLOAT_OUTPUT) -static void ma_dr_mp3_s16_to_f32(float* dst, const ma_int16* src, ma_uint64 sampleCount) -{ - ma_uint64 i; - for (i = 0; i < sampleCount; i += 1) { - float x = (float)src[i]; - x = x * 0.000030517578125f; - dst[i] = x; - } -} -#endif -static ma_uint64 ma_dr_mp3_read_pcm_frames_raw(ma_dr_mp3* pMP3, ma_uint64 framesToRead, void* pBufferOut) -{ - ma_uint64 totalFramesRead = 0; - MA_DR_MP3_ASSERT(pMP3 != NULL); - MA_DR_MP3_ASSERT(pMP3->onRead != NULL); - while (framesToRead > 0) { - ma_uint32 framesToConsume = (ma_uint32)MA_DR_MP3_MIN(pMP3->pcmFramesRemainingInMP3Frame, framesToRead); - if (pBufferOut != NULL) { - #if defined(MA_DR_MP3_FLOAT_OUTPUT) - float* pFramesOutF32 = (float*)MA_DR_MP3_OFFSET_PTR(pBufferOut, sizeof(float) * totalFramesRead * pMP3->channels); - float* pFramesInF32 = (float*)MA_DR_MP3_OFFSET_PTR(&pMP3->pcmFrames[0], sizeof(float) * pMP3->pcmFramesConsumedInMP3Frame * pMP3->mp3FrameChannels); - MA_DR_MP3_COPY_MEMORY(pFramesOutF32, pFramesInF32, sizeof(float) * framesToConsume * pMP3->channels); - #else - ma_int16* pFramesOutS16 = (ma_int16*)MA_DR_MP3_OFFSET_PTR(pBufferOut, sizeof(ma_int16) * totalFramesRead * pMP3->channels); - ma_int16* pFramesInS16 = (ma_int16*)MA_DR_MP3_OFFSET_PTR(&pMP3->pcmFrames[0], sizeof(ma_int16) * pMP3->pcmFramesConsumedInMP3Frame * pMP3->mp3FrameChannels); - MA_DR_MP3_COPY_MEMORY(pFramesOutS16, pFramesInS16, sizeof(ma_int16) * framesToConsume * pMP3->channels); - #endif - } - pMP3->currentPCMFrame += framesToConsume; - pMP3->pcmFramesConsumedInMP3Frame += framesToConsume; - pMP3->pcmFramesRemainingInMP3Frame -= framesToConsume; - totalFramesRead += framesToConsume; - framesToRead -= framesToConsume; - if (framesToRead == 0) { - break; - } - MA_DR_MP3_ASSERT(pMP3->pcmFramesRemainingInMP3Frame == 0); - if (ma_dr_mp3_decode_next_frame(pMP3) == 0) { - break; - } - } - return totalFramesRead; -} -MA_API ma_uint64 ma_dr_mp3_read_pcm_frames_f32(ma_dr_mp3* pMP3, ma_uint64 framesToRead, float* pBufferOut) -{ - if (pMP3 == NULL || pMP3->onRead == NULL) { - return 0; - } -#if defined(MA_DR_MP3_FLOAT_OUTPUT) - return ma_dr_mp3_read_pcm_frames_raw(pMP3, framesToRead, pBufferOut); -#else - { - ma_int16 pTempS16[8192]; - ma_uint64 totalPCMFramesRead = 0; - while (totalPCMFramesRead < framesToRead) { - ma_uint64 framesJustRead; - ma_uint64 framesRemaining = framesToRead - totalPCMFramesRead; - ma_uint64 framesToReadNow = MA_DR_MP3_COUNTOF(pTempS16) / pMP3->channels; - if (framesToReadNow > framesRemaining) { - framesToReadNow = framesRemaining; - } - framesJustRead = ma_dr_mp3_read_pcm_frames_raw(pMP3, framesToReadNow, pTempS16); - if (framesJustRead == 0) { - break; - } - ma_dr_mp3_s16_to_f32((float*)MA_DR_MP3_OFFSET_PTR(pBufferOut, sizeof(float) * totalPCMFramesRead * pMP3->channels), pTempS16, framesJustRead * pMP3->channels); - totalPCMFramesRead += framesJustRead; - } - return totalPCMFramesRead; - } -#endif -} -MA_API ma_uint64 ma_dr_mp3_read_pcm_frames_s16(ma_dr_mp3* pMP3, ma_uint64 framesToRead, ma_int16* pBufferOut) -{ - if (pMP3 == NULL || pMP3->onRead == NULL) { - return 0; - } -#if !defined(MA_DR_MP3_FLOAT_OUTPUT) - return ma_dr_mp3_read_pcm_frames_raw(pMP3, framesToRead, pBufferOut); -#else - { - float pTempF32[4096]; - ma_uint64 totalPCMFramesRead = 0; - while (totalPCMFramesRead < framesToRead) { - ma_uint64 framesJustRead; - ma_uint64 framesRemaining = framesToRead - totalPCMFramesRead; - ma_uint64 framesToReadNow = MA_DR_MP3_COUNTOF(pTempF32) / pMP3->channels; - if (framesToReadNow > framesRemaining) { - framesToReadNow = framesRemaining; - } - framesJustRead = ma_dr_mp3_read_pcm_frames_raw(pMP3, framesToReadNow, pTempF32); - if (framesJustRead == 0) { - break; - } - ma_dr_mp3_f32_to_s16((ma_int16*)MA_DR_MP3_OFFSET_PTR(pBufferOut, sizeof(ma_int16) * totalPCMFramesRead * pMP3->channels), pTempF32, framesJustRead * pMP3->channels); - totalPCMFramesRead += framesJustRead; - } - return totalPCMFramesRead; - } -#endif -} -static void ma_dr_mp3_reset(ma_dr_mp3* pMP3) -{ - MA_DR_MP3_ASSERT(pMP3 != NULL); - pMP3->pcmFramesConsumedInMP3Frame = 0; - pMP3->pcmFramesRemainingInMP3Frame = 0; - pMP3->currentPCMFrame = 0; - pMP3->dataSize = 0; - pMP3->atEnd = MA_FALSE; - ma_dr_mp3dec_init(&pMP3->decoder); -} -static ma_bool32 ma_dr_mp3_seek_to_start_of_stream(ma_dr_mp3* pMP3) -{ - MA_DR_MP3_ASSERT(pMP3 != NULL); - MA_DR_MP3_ASSERT(pMP3->onSeek != NULL); - if (!ma_dr_mp3__on_seek(pMP3, 0, ma_dr_mp3_seek_origin_start)) { - return MA_FALSE; - } - ma_dr_mp3_reset(pMP3); - return MA_TRUE; -} -static ma_bool32 ma_dr_mp3_seek_forward_by_pcm_frames__brute_force(ma_dr_mp3* pMP3, ma_uint64 frameOffset) -{ - ma_uint64 framesRead; -#if defined(MA_DR_MP3_FLOAT_OUTPUT) - framesRead = ma_dr_mp3_read_pcm_frames_f32(pMP3, frameOffset, NULL); -#else - framesRead = ma_dr_mp3_read_pcm_frames_s16(pMP3, frameOffset, NULL); -#endif - if (framesRead != frameOffset) { - return MA_FALSE; - } - return MA_TRUE; -} -static ma_bool32 ma_dr_mp3_seek_to_pcm_frame__brute_force(ma_dr_mp3* pMP3, ma_uint64 frameIndex) -{ - MA_DR_MP3_ASSERT(pMP3 != NULL); - if (frameIndex == pMP3->currentPCMFrame) { - return MA_TRUE; - } - if (frameIndex < pMP3->currentPCMFrame) { - if (!ma_dr_mp3_seek_to_start_of_stream(pMP3)) { - return MA_FALSE; - } - } - MA_DR_MP3_ASSERT(frameIndex >= pMP3->currentPCMFrame); - return ma_dr_mp3_seek_forward_by_pcm_frames__brute_force(pMP3, (frameIndex - pMP3->currentPCMFrame)); -} -static ma_bool32 ma_dr_mp3_find_closest_seek_point(ma_dr_mp3* pMP3, ma_uint64 frameIndex, ma_uint32* pSeekPointIndex) -{ - ma_uint32 iSeekPoint; - MA_DR_MP3_ASSERT(pSeekPointIndex != NULL); - *pSeekPointIndex = 0; - if (frameIndex < pMP3->pSeekPoints[0].pcmFrameIndex) { - return MA_FALSE; - } - for (iSeekPoint = 0; iSeekPoint < pMP3->seekPointCount; ++iSeekPoint) { - if (pMP3->pSeekPoints[iSeekPoint].pcmFrameIndex > frameIndex) { - break; - } - *pSeekPointIndex = iSeekPoint; - } - return MA_TRUE; -} -static ma_bool32 ma_dr_mp3_seek_to_pcm_frame__seek_table(ma_dr_mp3* pMP3, ma_uint64 frameIndex) -{ - ma_dr_mp3_seek_point seekPoint; - ma_uint32 priorSeekPointIndex; - ma_uint16 iMP3Frame; - ma_uint64 leftoverFrames; - MA_DR_MP3_ASSERT(pMP3 != NULL); - MA_DR_MP3_ASSERT(pMP3->pSeekPoints != NULL); - MA_DR_MP3_ASSERT(pMP3->seekPointCount > 0); - if (ma_dr_mp3_find_closest_seek_point(pMP3, frameIndex, &priorSeekPointIndex)) { - seekPoint = pMP3->pSeekPoints[priorSeekPointIndex]; - } else { - seekPoint.seekPosInBytes = 0; - seekPoint.pcmFrameIndex = 0; - seekPoint.mp3FramesToDiscard = 0; - seekPoint.pcmFramesToDiscard = 0; - } - if (!ma_dr_mp3__on_seek_64(pMP3, seekPoint.seekPosInBytes, ma_dr_mp3_seek_origin_start)) { - return MA_FALSE; - } - ma_dr_mp3_reset(pMP3); - for (iMP3Frame = 0; iMP3Frame < seekPoint.mp3FramesToDiscard; ++iMP3Frame) { - ma_uint32 pcmFramesRead; - ma_dr_mp3d_sample_t* pPCMFrames; - pPCMFrames = NULL; - if (iMP3Frame == seekPoint.mp3FramesToDiscard-1) { - pPCMFrames = (ma_dr_mp3d_sample_t*)pMP3->pcmFrames; - } - pcmFramesRead = ma_dr_mp3_decode_next_frame_ex(pMP3, pPCMFrames); - if (pcmFramesRead == 0) { - return MA_FALSE; - } - } - pMP3->currentPCMFrame = seekPoint.pcmFrameIndex - seekPoint.pcmFramesToDiscard; - leftoverFrames = frameIndex - pMP3->currentPCMFrame; - return ma_dr_mp3_seek_forward_by_pcm_frames__brute_force(pMP3, leftoverFrames); -} -MA_API ma_bool32 ma_dr_mp3_seek_to_pcm_frame(ma_dr_mp3* pMP3, ma_uint64 frameIndex) -{ - if (pMP3 == NULL || pMP3->onSeek == NULL) { - return MA_FALSE; - } - if (frameIndex == 0) { - return ma_dr_mp3_seek_to_start_of_stream(pMP3); - } - if (pMP3->pSeekPoints != NULL && pMP3->seekPointCount > 0) { - return ma_dr_mp3_seek_to_pcm_frame__seek_table(pMP3, frameIndex); - } else { - return ma_dr_mp3_seek_to_pcm_frame__brute_force(pMP3, frameIndex); - } -} -MA_API ma_bool32 ma_dr_mp3_get_mp3_and_pcm_frame_count(ma_dr_mp3* pMP3, ma_uint64* pMP3FrameCount, ma_uint64* pPCMFrameCount) -{ - ma_uint64 currentPCMFrame; - ma_uint64 totalPCMFrameCount; - ma_uint64 totalMP3FrameCount; - if (pMP3 == NULL) { - return MA_FALSE; - } - if (pMP3->onSeek == NULL) { - return MA_FALSE; - } - currentPCMFrame = pMP3->currentPCMFrame; - if (!ma_dr_mp3_seek_to_start_of_stream(pMP3)) { - return MA_FALSE; - } - totalPCMFrameCount = 0; - totalMP3FrameCount = 0; - for (;;) { - ma_uint32 pcmFramesInCurrentMP3Frame; - pcmFramesInCurrentMP3Frame = ma_dr_mp3_decode_next_frame_ex(pMP3, NULL); - if (pcmFramesInCurrentMP3Frame == 0) { - break; - } - totalPCMFrameCount += pcmFramesInCurrentMP3Frame; - totalMP3FrameCount += 1; - } - if (!ma_dr_mp3_seek_to_start_of_stream(pMP3)) { - return MA_FALSE; - } - if (!ma_dr_mp3_seek_to_pcm_frame(pMP3, currentPCMFrame)) { - return MA_FALSE; - } - if (pMP3FrameCount != NULL) { - *pMP3FrameCount = totalMP3FrameCount; - } - if (pPCMFrameCount != NULL) { - *pPCMFrameCount = totalPCMFrameCount; - } - return MA_TRUE; -} -MA_API ma_uint64 ma_dr_mp3_get_pcm_frame_count(ma_dr_mp3* pMP3) -{ - ma_uint64 totalPCMFrameCount; - if (!ma_dr_mp3_get_mp3_and_pcm_frame_count(pMP3, NULL, &totalPCMFrameCount)) { - return 0; - } - return totalPCMFrameCount; -} -MA_API ma_uint64 ma_dr_mp3_get_mp3_frame_count(ma_dr_mp3* pMP3) -{ - ma_uint64 totalMP3FrameCount; - if (!ma_dr_mp3_get_mp3_and_pcm_frame_count(pMP3, &totalMP3FrameCount, NULL)) { - return 0; - } - return totalMP3FrameCount; -} -static void ma_dr_mp3__accumulate_running_pcm_frame_count(ma_dr_mp3* pMP3, ma_uint32 pcmFrameCountIn, ma_uint64* pRunningPCMFrameCount, float* pRunningPCMFrameCountFractionalPart) -{ - float srcRatio; - float pcmFrameCountOutF; - ma_uint32 pcmFrameCountOut; - srcRatio = (float)pMP3->mp3FrameSampleRate / (float)pMP3->sampleRate; - MA_DR_MP3_ASSERT(srcRatio > 0); - pcmFrameCountOutF = *pRunningPCMFrameCountFractionalPart + (pcmFrameCountIn / srcRatio); - pcmFrameCountOut = (ma_uint32)pcmFrameCountOutF; - *pRunningPCMFrameCountFractionalPart = pcmFrameCountOutF - pcmFrameCountOut; - *pRunningPCMFrameCount += pcmFrameCountOut; -} -typedef struct -{ - ma_uint64 bytePos; - ma_uint64 pcmFrameIndex; -} ma_dr_mp3__seeking_mp3_frame_info; -MA_API ma_bool32 ma_dr_mp3_calculate_seek_points(ma_dr_mp3* pMP3, ma_uint32* pSeekPointCount, ma_dr_mp3_seek_point* pSeekPoints) -{ - ma_uint32 seekPointCount; - ma_uint64 currentPCMFrame; - ma_uint64 totalMP3FrameCount; - ma_uint64 totalPCMFrameCount; - if (pMP3 == NULL || pSeekPointCount == NULL || pSeekPoints == NULL) { - return MA_FALSE; - } - seekPointCount = *pSeekPointCount; - if (seekPointCount == 0) { - return MA_FALSE; - } - currentPCMFrame = pMP3->currentPCMFrame; - if (!ma_dr_mp3_get_mp3_and_pcm_frame_count(pMP3, &totalMP3FrameCount, &totalPCMFrameCount)) { - return MA_FALSE; - } - if (totalMP3FrameCount < MA_DR_MP3_SEEK_LEADING_MP3_FRAMES+1) { - seekPointCount = 1; - pSeekPoints[0].seekPosInBytes = 0; - pSeekPoints[0].pcmFrameIndex = 0; - pSeekPoints[0].mp3FramesToDiscard = 0; - pSeekPoints[0].pcmFramesToDiscard = 0; - } else { - ma_uint64 pcmFramesBetweenSeekPoints; - ma_dr_mp3__seeking_mp3_frame_info mp3FrameInfo[MA_DR_MP3_SEEK_LEADING_MP3_FRAMES+1]; - ma_uint64 runningPCMFrameCount = 0; - float runningPCMFrameCountFractionalPart = 0; - ma_uint64 nextTargetPCMFrame; - ma_uint32 iMP3Frame; - ma_uint32 iSeekPoint; - if (seekPointCount > totalMP3FrameCount-1) { - seekPointCount = (ma_uint32)totalMP3FrameCount-1; - } - pcmFramesBetweenSeekPoints = totalPCMFrameCount / (seekPointCount+1); - if (!ma_dr_mp3_seek_to_start_of_stream(pMP3)) { - return MA_FALSE; - } - for (iMP3Frame = 0; iMP3Frame < MA_DR_MP3_SEEK_LEADING_MP3_FRAMES+1; ++iMP3Frame) { - ma_uint32 pcmFramesInCurrentMP3FrameIn; - MA_DR_MP3_ASSERT(pMP3->streamCursor >= pMP3->dataSize); - mp3FrameInfo[iMP3Frame].bytePos = pMP3->streamCursor - pMP3->dataSize; - mp3FrameInfo[iMP3Frame].pcmFrameIndex = runningPCMFrameCount; - pcmFramesInCurrentMP3FrameIn = ma_dr_mp3_decode_next_frame_ex(pMP3, NULL); - if (pcmFramesInCurrentMP3FrameIn == 0) { - return MA_FALSE; - } - ma_dr_mp3__accumulate_running_pcm_frame_count(pMP3, pcmFramesInCurrentMP3FrameIn, &runningPCMFrameCount, &runningPCMFrameCountFractionalPart); - } - nextTargetPCMFrame = 0; - for (iSeekPoint = 0; iSeekPoint < seekPointCount; ++iSeekPoint) { - nextTargetPCMFrame += pcmFramesBetweenSeekPoints; - for (;;) { - if (nextTargetPCMFrame < runningPCMFrameCount) { - pSeekPoints[iSeekPoint].seekPosInBytes = mp3FrameInfo[0].bytePos; - pSeekPoints[iSeekPoint].pcmFrameIndex = nextTargetPCMFrame; - pSeekPoints[iSeekPoint].mp3FramesToDiscard = MA_DR_MP3_SEEK_LEADING_MP3_FRAMES; - pSeekPoints[iSeekPoint].pcmFramesToDiscard = (ma_uint16)(nextTargetPCMFrame - mp3FrameInfo[MA_DR_MP3_SEEK_LEADING_MP3_FRAMES-1].pcmFrameIndex); - break; - } else { - size_t i; - ma_uint32 pcmFramesInCurrentMP3FrameIn; - for (i = 0; i < MA_DR_MP3_COUNTOF(mp3FrameInfo)-1; ++i) { - mp3FrameInfo[i] = mp3FrameInfo[i+1]; - } - mp3FrameInfo[MA_DR_MP3_COUNTOF(mp3FrameInfo)-1].bytePos = pMP3->streamCursor - pMP3->dataSize; - mp3FrameInfo[MA_DR_MP3_COUNTOF(mp3FrameInfo)-1].pcmFrameIndex = runningPCMFrameCount; - pcmFramesInCurrentMP3FrameIn = ma_dr_mp3_decode_next_frame_ex(pMP3, NULL); - if (pcmFramesInCurrentMP3FrameIn == 0) { - pSeekPoints[iSeekPoint].seekPosInBytes = mp3FrameInfo[0].bytePos; - pSeekPoints[iSeekPoint].pcmFrameIndex = nextTargetPCMFrame; - pSeekPoints[iSeekPoint].mp3FramesToDiscard = MA_DR_MP3_SEEK_LEADING_MP3_FRAMES; - pSeekPoints[iSeekPoint].pcmFramesToDiscard = (ma_uint16)(nextTargetPCMFrame - mp3FrameInfo[MA_DR_MP3_SEEK_LEADING_MP3_FRAMES-1].pcmFrameIndex); - break; - } - ma_dr_mp3__accumulate_running_pcm_frame_count(pMP3, pcmFramesInCurrentMP3FrameIn, &runningPCMFrameCount, &runningPCMFrameCountFractionalPart); - } - } - } - if (!ma_dr_mp3_seek_to_start_of_stream(pMP3)) { - return MA_FALSE; - } - if (!ma_dr_mp3_seek_to_pcm_frame(pMP3, currentPCMFrame)) { - return MA_FALSE; - } - } - *pSeekPointCount = seekPointCount; - return MA_TRUE; -} -MA_API ma_bool32 ma_dr_mp3_bind_seek_table(ma_dr_mp3* pMP3, ma_uint32 seekPointCount, ma_dr_mp3_seek_point* pSeekPoints) -{ - if (pMP3 == NULL) { - return MA_FALSE; - } - if (seekPointCount == 0 || pSeekPoints == NULL) { - pMP3->seekPointCount = 0; - pMP3->pSeekPoints = NULL; - } else { - pMP3->seekPointCount = seekPointCount; - pMP3->pSeekPoints = pSeekPoints; - } - return MA_TRUE; -} -static float* ma_dr_mp3__full_read_and_close_f32(ma_dr_mp3* pMP3, ma_dr_mp3_config* pConfig, ma_uint64* pTotalFrameCount) -{ - ma_uint64 totalFramesRead = 0; - ma_uint64 framesCapacity = 0; - float* pFrames = NULL; - float temp[4096]; - MA_DR_MP3_ASSERT(pMP3 != NULL); - for (;;) { - ma_uint64 framesToReadRightNow = MA_DR_MP3_COUNTOF(temp) / pMP3->channels; - ma_uint64 framesJustRead = ma_dr_mp3_read_pcm_frames_f32(pMP3, framesToReadRightNow, temp); - if (framesJustRead == 0) { - break; - } - if (framesCapacity < totalFramesRead + framesJustRead) { - ma_uint64 oldFramesBufferSize; - ma_uint64 newFramesBufferSize; - ma_uint64 newFramesCap; - float* pNewFrames; - newFramesCap = framesCapacity * 2; - if (newFramesCap < totalFramesRead + framesJustRead) { - newFramesCap = totalFramesRead + framesJustRead; - } - oldFramesBufferSize = framesCapacity * pMP3->channels * sizeof(float); - newFramesBufferSize = newFramesCap * pMP3->channels * sizeof(float); - if (newFramesBufferSize > (ma_uint64)MA_SIZE_MAX) { - break; - } - pNewFrames = (float*)ma_dr_mp3__realloc_from_callbacks(pFrames, (size_t)newFramesBufferSize, (size_t)oldFramesBufferSize, &pMP3->allocationCallbacks); - if (pNewFrames == NULL) { - ma_dr_mp3__free_from_callbacks(pFrames, &pMP3->allocationCallbacks); - break; - } - pFrames = pNewFrames; - framesCapacity = newFramesCap; - } - MA_DR_MP3_COPY_MEMORY(pFrames + totalFramesRead*pMP3->channels, temp, (size_t)(framesJustRead*pMP3->channels*sizeof(float))); - totalFramesRead += framesJustRead; - if (framesJustRead != framesToReadRightNow) { - break; - } - } - if (pConfig != NULL) { - pConfig->channels = pMP3->channels; - pConfig->sampleRate = pMP3->sampleRate; - } - ma_dr_mp3_uninit(pMP3); - if (pTotalFrameCount) { - *pTotalFrameCount = totalFramesRead; - } - return pFrames; -} -static ma_int16* ma_dr_mp3__full_read_and_close_s16(ma_dr_mp3* pMP3, ma_dr_mp3_config* pConfig, ma_uint64* pTotalFrameCount) -{ - ma_uint64 totalFramesRead = 0; - ma_uint64 framesCapacity = 0; - ma_int16* pFrames = NULL; - ma_int16 temp[4096]; - MA_DR_MP3_ASSERT(pMP3 != NULL); - for (;;) { - ma_uint64 framesToReadRightNow = MA_DR_MP3_COUNTOF(temp) / pMP3->channels; - ma_uint64 framesJustRead = ma_dr_mp3_read_pcm_frames_s16(pMP3, framesToReadRightNow, temp); - if (framesJustRead == 0) { - break; - } - if (framesCapacity < totalFramesRead + framesJustRead) { - ma_uint64 newFramesBufferSize; - ma_uint64 oldFramesBufferSize; - ma_uint64 newFramesCap; - ma_int16* pNewFrames; - newFramesCap = framesCapacity * 2; - if (newFramesCap < totalFramesRead + framesJustRead) { - newFramesCap = totalFramesRead + framesJustRead; - } - oldFramesBufferSize = framesCapacity * pMP3->channels * sizeof(ma_int16); - newFramesBufferSize = newFramesCap * pMP3->channels * sizeof(ma_int16); - if (newFramesBufferSize > (ma_uint64)MA_SIZE_MAX) { - break; - } - pNewFrames = (ma_int16*)ma_dr_mp3__realloc_from_callbacks(pFrames, (size_t)newFramesBufferSize, (size_t)oldFramesBufferSize, &pMP3->allocationCallbacks); - if (pNewFrames == NULL) { - ma_dr_mp3__free_from_callbacks(pFrames, &pMP3->allocationCallbacks); - break; - } - pFrames = pNewFrames; - framesCapacity = newFramesCap; - } - MA_DR_MP3_COPY_MEMORY(pFrames + totalFramesRead*pMP3->channels, temp, (size_t)(framesJustRead*pMP3->channels*sizeof(ma_int16))); - totalFramesRead += framesJustRead; - if (framesJustRead != framesToReadRightNow) { - break; - } - } - if (pConfig != NULL) { - pConfig->channels = pMP3->channels; - pConfig->sampleRate = pMP3->sampleRate; - } - ma_dr_mp3_uninit(pMP3); - if (pTotalFrameCount) { - *pTotalFrameCount = totalFramesRead; - } - return pFrames; -} -MA_API float* ma_dr_mp3_open_and_read_pcm_frames_f32(ma_dr_mp3_read_proc onRead, ma_dr_mp3_seek_proc onSeek, void* pUserData, ma_dr_mp3_config* pConfig, ma_uint64* pTotalFrameCount, const ma_allocation_callbacks* pAllocationCallbacks) -{ - ma_dr_mp3 mp3; - if (!ma_dr_mp3_init(&mp3, onRead, onSeek, pUserData, pAllocationCallbacks)) { - return NULL; - } - return ma_dr_mp3__full_read_and_close_f32(&mp3, pConfig, pTotalFrameCount); -} -MA_API ma_int16* ma_dr_mp3_open_and_read_pcm_frames_s16(ma_dr_mp3_read_proc onRead, ma_dr_mp3_seek_proc onSeek, void* pUserData, ma_dr_mp3_config* pConfig, ma_uint64* pTotalFrameCount, const ma_allocation_callbacks* pAllocationCallbacks) -{ - ma_dr_mp3 mp3; - if (!ma_dr_mp3_init(&mp3, onRead, onSeek, pUserData, pAllocationCallbacks)) { - return NULL; - } - return ma_dr_mp3__full_read_and_close_s16(&mp3, pConfig, pTotalFrameCount); -} -MA_API float* ma_dr_mp3_open_memory_and_read_pcm_frames_f32(const void* pData, size_t dataSize, ma_dr_mp3_config* pConfig, ma_uint64* pTotalFrameCount, const ma_allocation_callbacks* pAllocationCallbacks) -{ - ma_dr_mp3 mp3; - if (!ma_dr_mp3_init_memory(&mp3, pData, dataSize, pAllocationCallbacks)) { - return NULL; - } - return ma_dr_mp3__full_read_and_close_f32(&mp3, pConfig, pTotalFrameCount); -} -MA_API ma_int16* ma_dr_mp3_open_memory_and_read_pcm_frames_s16(const void* pData, size_t dataSize, ma_dr_mp3_config* pConfig, ma_uint64* pTotalFrameCount, const ma_allocation_callbacks* pAllocationCallbacks) -{ - ma_dr_mp3 mp3; - if (!ma_dr_mp3_init_memory(&mp3, pData, dataSize, pAllocationCallbacks)) { - return NULL; - } - return ma_dr_mp3__full_read_and_close_s16(&mp3, pConfig, pTotalFrameCount); -} -#ifndef MA_DR_MP3_NO_STDIO -MA_API float* ma_dr_mp3_open_file_and_read_pcm_frames_f32(const char* filePath, ma_dr_mp3_config* pConfig, ma_uint64* pTotalFrameCount, const ma_allocation_callbacks* pAllocationCallbacks) -{ - ma_dr_mp3 mp3; - if (!ma_dr_mp3_init_file(&mp3, filePath, pAllocationCallbacks)) { - return NULL; - } - return ma_dr_mp3__full_read_and_close_f32(&mp3, pConfig, pTotalFrameCount); -} -MA_API ma_int16* ma_dr_mp3_open_file_and_read_pcm_frames_s16(const char* filePath, ma_dr_mp3_config* pConfig, ma_uint64* pTotalFrameCount, const ma_allocation_callbacks* pAllocationCallbacks) -{ - ma_dr_mp3 mp3; - if (!ma_dr_mp3_init_file(&mp3, filePath, pAllocationCallbacks)) { - return NULL; - } - return ma_dr_mp3__full_read_and_close_s16(&mp3, pConfig, pTotalFrameCount); -} -#endif -MA_API void* ma_dr_mp3_malloc(size_t sz, const ma_allocation_callbacks* pAllocationCallbacks) -{ - if (pAllocationCallbacks != NULL) { - return ma_dr_mp3__malloc_from_callbacks(sz, pAllocationCallbacks); - } else { - return ma_dr_mp3__malloc_default(sz, NULL); - } -} -MA_API void ma_dr_mp3_free(void* p, const ma_allocation_callbacks* pAllocationCallbacks) -{ - if (pAllocationCallbacks != NULL) { - ma_dr_mp3__free_from_callbacks(p, pAllocationCallbacks); - } else { - ma_dr_mp3__free_default(p, NULL); - } -} -#endif -/* dr_mp3_c end */ -#endif /* MA_DR_MP3_IMPLEMENTATION */ -#endif /* MA_NO_MP3 */ - - -/* End globally disabled warnings. */ -#if defined(_MSC_VER) - #pragma warning(pop) -#endif - -#endif /* miniaudio_c */ -#endif /* MINIAUDIO_IMPLEMENTATION */ - - -/* -This software is available as a choice of the following licenses. Choose -whichever you prefer. - -=============================================================================== -ALTERNATIVE 1 - Public Domain (www.unlicense.org) -=============================================================================== -This is free and unencumbered software released into the public domain. - -Anyone is free to copy, modify, publish, use, compile, sell, or distribute this -software, either in source code form or as a compiled binary, for any purpose, -commercial or non-commercial, and by any means. - -In jurisdictions that recognize copyright laws, the author or authors of this -software dedicate any and all copyright interest in the software to the public -domain. We make this dedication for the benefit of the public at large and to -the detriment of our heirs and successors. We intend this dedication to be an -overt act of relinquishment in perpetuity of all present and future rights to -this software under copyright law. - -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 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. - -For more information, please refer to - -=============================================================================== -ALTERNATIVE 2 - MIT No Attribution -=============================================================================== -Copyright 2025 David Reid - -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. - -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. -*/ diff --git a/tests-cmake/filter-wav/CMakeLists.txt b/tests-cmake/filter-wav/CMakeLists.txt index 09cc09f4da..9c7a423413 100644 --- a/tests-cmake/filter-wav/CMakeLists.txt +++ b/tests-cmake/filter-wav/CMakeLists.txt @@ -4,16 +4,9 @@ cmake_minimum_required(VERSION 3.20) project(filter-wav) set (CMAKE_CXX_STANDARD 11) set (DCMAKE_CXX_FLAGS "-Werror") - -# add_compile_options(-Wstack-usage=1024) - -# Add Portaduio for desktop build -add_compile_options(-DIS_DESKTOP) -FetchContent_Declare(portaudio GIT_REPOSITORY "https://github.com/PortAudio/portaudio.git" GIT_TAG v19.7.0 ) -FetchContent_GetProperties(portaudio) -if(NOT portaudio_POPULATED) - FetchContent_Populate(portaudio) - add_subdirectory(${portaudio_SOURCE_DIR} ${CMAKE_CURRENT_BINARY_DIR}/portaudio) +if (CMAKE_CXX_COMPILER_ID STREQUAL "Clang") + set (CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -fno-omit-frame-pointer -fsanitize=address") + set (CMAKE_LINKER_FLAGS_DEBUG "${CMAKE_LINKER_FLAGS_DEBUG} -fno-omit-frame-pointer -fsanitize=address") endif() # Build with arduino-audio-tools @@ -22,11 +15,11 @@ if(CMAKE_SOURCE_DIR STREQUAL CMAKE_CURRENT_SOURCE_DIR) endif() # build sketch as executable -add_executable (filter-wav filter-wav.cpp) +add_executable (filter-wav filter-wav.cpp ../main.cpp ) target_include_directories(filter-wav PRIVATE "${arduino_emulator_SOURCE_DIR}/ArduinoCore-Linux/libraries/SdFat" ) # set preprocessor defines -target_compile_definitions(filter-wav PUBLIC -DIS_DESKTOP) +target_compile_definitions(filter-wav PUBLIC -DEXIT_ON_STOP -DIS_DESKTOP) # specify libraries target_link_libraries(filter-wav portaudio arduino_emulator arduino-audio-tools) diff --git a/tests-cmake/filter-wav/filter-wav.cpp b/tests-cmake/filter-wav/filter-wav.cpp index 39e063c356..53f77c26bd 100644 --- a/tests-cmake/filter-wav/filter-wav.cpp +++ b/tests-cmake/filter-wav/filter-wav.cpp @@ -1,7 +1,7 @@ // Simple wrapper for Arduino sketch to compilable with cpp in cmake #include "Arduino.h" #include "AudioTools.h" -#include "AudioTools/AudioLibs/PortAudioStream.h" +#include "AudioLibs/PortAudioStream.h" #include "SdFat.h" // define FIR filter https://fiiir.com/ @@ -161,7 +161,7 @@ MusicalNotes notes; void setup(){ Serial.begin(115200); - AudioToolsLogger.begin(Serial, AudioToolsLogLevel::Info); + AudioLogger::instance().begin(Serial, AudioLogger::Info); auto cfg = wave.defaultConfig(); cfg.sample_rate = sample_rate; diff --git a/tests-cmake/filter/CMakeLists.txt b/tests-cmake/filter/CMakeLists.txt index 1ee15459b8..18581d5a42 100644 --- a/tests-cmake/filter/CMakeLists.txt +++ b/tests-cmake/filter/CMakeLists.txt @@ -4,16 +4,9 @@ cmake_minimum_required(VERSION 3.20) project(filter) set (CMAKE_CXX_STANDARD 11) set (DCMAKE_CXX_FLAGS "-Werror") - -# add_compile_options(-Wstack-usage=1024) - -# Add Portaduio for desktop build -add_compile_options(-DIS_DESKTOP) -FetchContent_Declare(portaudio GIT_REPOSITORY "https://github.com/PortAudio/portaudio.git" GIT_TAG v19.7.0 ) -FetchContent_GetProperties(portaudio) -if(NOT portaudio_POPULATED) - FetchContent_Populate(portaudio) - add_subdirectory(${portaudio_SOURCE_DIR} ${CMAKE_CURRENT_BINARY_DIR}/portaudio) +if (CMAKE_CXX_COMPILER_ID STREQUAL "Clang") + set (CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -fno-omit-frame-pointer -fsanitize=address") + set (CMAKE_LINKER_FLAGS_DEBUG "${CMAKE_LINKER_FLAGS_DEBUG} -fno-omit-frame-pointer -fsanitize=address") endif() # Build with arduino-audio-tools @@ -22,10 +15,10 @@ if(CMAKE_SOURCE_DIR STREQUAL CMAKE_CURRENT_SOURCE_DIR) endif() # build sketch as executable -add_executable (filter filter.cpp) +add_executable (filter filter.cpp ../main.cpp) # set preprocessor defines -target_compile_definitions(filter PUBLIC -DIS_DESKTOP) +target_compile_definitions(filter PUBLIC -DEXIT_ON_STOP -DIS_DESKTOP) # specify libraries target_link_libraries(filter portaudio arduino_emulator arduino-audio-tools) diff --git a/tests-cmake/filter/filter.cpp b/tests-cmake/filter/filter.cpp index 56a84a935b..ff60303ea7 100644 --- a/tests-cmake/filter/filter.cpp +++ b/tests-cmake/filter/filter.cpp @@ -1,7 +1,7 @@ // Simple wrapper for Arduino sketch to compilable with cpp in cmake #include "Arduino.h" #include "AudioTools.h" -#include "AudioTools/AudioLibs/PortAudioStream.h" +#include "AudioLibs/PortAudioStream.h" // define FIR filter float coef[] = { 0.021, 0.096, 0.146, 0.096, 0.021}; @@ -17,7 +17,7 @@ StreamCopy copier(out, in_stream, 1012); // copies void setup(){ Serial.begin(115200); - AudioToolsLogger.begin(Serial, AudioToolsLogLevel::Info); + AudioLogger::instance().begin(Serial, AudioLogger::Info); auto cfg = noise.defaultConfig(); cfg.sample_rate = sample_rate; diff --git a/tests-cmake/generator/CMakeLists.txt b/tests-cmake/generator/CMakeLists.txt index af8ca748db..004f2c5796 100644 --- a/tests-cmake/generator/CMakeLists.txt +++ b/tests-cmake/generator/CMakeLists.txt @@ -14,10 +14,10 @@ if(CMAKE_SOURCE_DIR STREQUAL CMAKE_CURRENT_SOURCE_DIR) endif() # build sketch as executable -add_executable (generator generator.cpp) +add_executable (generator generator.cpp ../main.cpp) # set preprocessor defines -target_compile_definitions(generator PUBLIC -DIS_DESKTOP) +target_compile_definitions(generator PUBLIC -DEXIT_ON_STOP -DIS_DESKTOP) # specify libraries target_link_libraries(generator arduino_emulator arduino-audio-tools) diff --git a/tests-cmake/generator/generator.cpp b/tests-cmake/generator/generator.cpp index 98d0fb75c0..d8b6cd151a 100644 --- a/tests-cmake/generator/generator.cpp +++ b/tests-cmake/generator/generator.cpp @@ -1,7 +1,8 @@ // Simple wrapper for Arduino sketch to compilable with cpp in cmake #include "Arduino.h" #include "AudioTools.h" -#include "AudioTools/AudioLibs/StdioStream.h" +//#include "AudioLibs/PortAudioStream.h" +#include "AudioLibs/StdioStream.h" //LinuxStdio out; // Output to Desktop CsvOutput out(Serial); @@ -11,7 +12,7 @@ StreamCopy copier(out, in_stream); // copies sound to out void setup(){ Serial.begin(115200); - AudioToolsLogger.begin(Serial, AudioToolsLogLevel::Info); + AudioLogger::instance().begin(Serial, AudioLogger::Info); auto cfg = out.defaultConfig(); cfg.bits_per_sample = 24; diff --git a/tests-cmake/ifft/CMakeLists.txt b/tests-cmake/ifft/CMakeLists.txt deleted file mode 100644 index 2f07cb9c45..0000000000 --- a/tests-cmake/ifft/CMakeLists.txt +++ /dev/null @@ -1,35 +0,0 @@ -cmake_minimum_required(VERSION 3.20) - - -# set the project name -project(ifft) -set (CMAKE_CXX_STANDARD 11) -set (DCMAKE_CXX_FLAGS "-Werror") -set (CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -O0 -ldl -lpthread -lm") -set (CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} -O0 -ldl -lpthread -lm") - -# Emulator is not necessary for -DIS_MIN_DESKTOP -set(ADD_ARDUINO_EMULATOR OFF CACHE BOOL "Add Arduino Emulator Library") -set(ADD_PORTAUDIO OFF CACHE BOOL "No Portaudio") - -# Build with arduino-audio-tools -if(CMAKE_SOURCE_DIR STREQUAL CMAKE_CURRENT_SOURCE_DIR) - add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/../.. ${CMAKE_CURRENT_BINARY_DIR}/arduino-audio-tools ) -endif() - -# Download miniaudio.h -file(DOWNLOAD https://raw.githubusercontent.com/mackron/miniaudio/master/miniaudio.h - ${CMAKE_CURRENT_SOURCE_DIR}/miniaudio.h) - - -# build sketch as executable -add_executable (ifft ifft.cpp) - -# set preprocessor defines -target_compile_definitions(ifft PUBLIC -DIS_MIN_DESKTOP) - -# specify libraries -target_link_libraries(ifft arduino-audio-tools) - -# access to miniaudio in sketch directory -target_include_directories(ifft PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}) diff --git a/tests-cmake/ifft/ifft.cpp b/tests-cmake/ifft/ifft.cpp deleted file mode 100644 index fc81b2adbc..0000000000 --- a/tests-cmake/ifft/ifft.cpp +++ /dev/null @@ -1,38 +0,0 @@ -#include "AudioTools.h" -#include "AudioTools/AudioLibs/AudioRealFFT.h" // using RealFFT -#include "AudioTools/AudioLibs/MiniAudioStream.h" - -AudioInfo info(44100, 2, 16); -AudioRealFFT afft; // or AudioKissFFT -//CsvOutput out(Serial); -MiniAudioStream out; -StreamCopy copier(out, afft); -int bin_idx = 0; - -// privide fft data -void fftFillData(AudioFFTBase &fft) { - fft.clearBins(); - FFTBin bin{1.0f,1.0f}; - assert(fft.setBin(bin_idx, bin)); - - // restart from first bin - if (++bin_idx>=fft.size()) bin_idx = 0; -} - -void setup() { - AudioToolsLogger.begin(Serial, AudioToolsLogLevel::Warning); - - // Setup FFT - auto tcfg = afft.defaultConfig(RX_MODE); - tcfg.copyFrom(info); - tcfg.length = 1024; - tcfg.callback = fftFillData; - afft.begin(tcfg); - - // setup output - auto ocfg = out.defaultConfig(TX_MODE); - ocfg.copyFrom(info); - out.begin(ocfg); -} - -void loop() { copier.copy(); } \ No newline at end of file diff --git a/tests-cmake/ifft/miniaudio.h b/tests-cmake/ifft/miniaudio.h deleted file mode 100644 index c74bebeb3c..0000000000 --- a/tests-cmake/ifft/miniaudio.h +++ /dev/null @@ -1,93468 +0,0 @@ -/* -Audio playback and capture library. Choice of public domain or MIT-0. See license statements at the end of this file. -miniaudio - v0.11.22 - 2025-02-24 - -David Reid - mackron@gmail.com - -Website: https://miniaud.io -Documentation: https://miniaud.io/docs -GitHub: https://github.com/mackron/miniaudio -*/ - -/* -1. Introduction -=============== -To use miniaudio, include "miniaudio.h": - - ```c - #include "miniaudio.h" - ``` - -The implementation is contained in "miniaudio.c". Just compile this like any other source file. You -can include miniaudio.c if you want to compile your project as a single translation unit: - - ```c - #include "miniaudio.c" - ``` - -miniaudio includes both low level and high level APIs. The low level API is good for those who want -to do all of their mixing themselves and only require a light weight interface to the underlying -audio device. The high level API is good for those who have complex mixing and effect requirements. - -In miniaudio, objects are transparent structures. Unlike many other libraries, there are no handles -to opaque objects which means you need to allocate memory for objects yourself. In the examples -presented in this documentation you will often see objects declared on the stack. You need to be -careful when translating these examples to your own code so that you don't accidentally declare -your objects on the stack and then cause them to become invalid once the function returns. In -addition, you must ensure the memory address of your objects remain the same throughout their -lifetime. You therefore cannot be making copies of your objects. - -A config/init pattern is used throughout the entire library. The idea is that you set up a config -object and pass that into the initialization routine. The advantage to this system is that the -config object can be initialized with logical defaults and new properties added to it without -breaking the API. The config object can be allocated on the stack and does not need to be -maintained after initialization of the corresponding object. - - -1.1. Low Level API ------------------- -The low level API gives you access to the raw audio data of an audio device. It supports playback, -capture, full-duplex and loopback (WASAPI only). You can enumerate over devices to determine which -physical device(s) you want to connect to. - -The low level API uses the concept of a "device" as the abstraction for physical devices. The idea -is that you choose a physical device to emit or capture audio from, and then move data to/from the -device when miniaudio tells you to. Data is delivered to and from devices asynchronously via a -callback which you specify when initializing the device. - -When initializing the device you first need to configure it. The device configuration allows you to -specify things like the format of the data delivered via the callback, the size of the internal -buffer and the ID of the device you want to emit or capture audio from. - -Once you have the device configuration set up you can initialize the device. When initializing a -device you need to allocate memory for the device object beforehand. This gives the application -complete control over how the memory is allocated. In the example below we initialize a playback -device on the stack, but you could allocate it on the heap if that suits your situation better. - - ```c - void data_callback(ma_device* pDevice, void* pOutput, const void* pInput, ma_uint32 frameCount) - { - // In playback mode copy data to pOutput. In capture mode read data from pInput. In full-duplex mode, both - // pOutput and pInput will be valid and you can move data from pInput into pOutput. Never process more than - // frameCount frames. - } - - int main() - { - ma_device_config config = ma_device_config_init(ma_device_type_playback); - config.playback.format = ma_format_f32; // Set to ma_format_unknown to use the device's native format. - config.playback.channels = 2; // Set to 0 to use the device's native channel count. - config.sampleRate = 48000; // Set to 0 to use the device's native sample rate. - config.dataCallback = data_callback; // This function will be called when miniaudio needs more data. - config.pUserData = pMyCustomData; // Can be accessed from the device object (device.pUserData). - - ma_device device; - if (ma_device_init(NULL, &config, &device) != MA_SUCCESS) { - return -1; // Failed to initialize the device. - } - - ma_device_start(&device); // The device is sleeping by default so you'll need to start it manually. - - // Do something here. Probably your program's main loop. - - ma_device_uninit(&device); - return 0; - } - ``` - -In the example above, `data_callback()` is where audio data is written and read from the device. -The idea is in playback mode you cause sound to be emitted from the speakers by writing audio data -to the output buffer (`pOutput` in the example). In capture mode you read data from the input -buffer (`pInput`) to extract sound captured by the microphone. The `frameCount` parameter tells you -how many frames can be written to the output buffer and read from the input buffer. A "frame" is -one sample for each channel. For example, in a stereo stream (2 channels), one frame is 2 -samples: one for the left, one for the right. The channel count is defined by the device config. -The size in bytes of an individual sample is defined by the sample format which is also specified -in the device config. Multi-channel audio data is always interleaved, which means the samples for -each frame are stored next to each other in memory. For example, in a stereo stream the first pair -of samples will be the left and right samples for the first frame, the second pair of samples will -be the left and right samples for the second frame, etc. - -The configuration of the device is defined by the `ma_device_config` structure. The config object -is always initialized with `ma_device_config_init()`. It's important to always initialize the -config with this function as it initializes it with logical defaults and ensures your program -doesn't break when new members are added to the `ma_device_config` structure. The example above -uses a fairly simple and standard device configuration. The call to `ma_device_config_init()` takes -a single parameter, which is whether or not the device is a playback, capture, duplex or loopback -device (loopback devices are not supported on all backends). The `config.playback.format` member -sets the sample format which can be one of the following (all formats are native-endian): - - +---------------+----------------------------------------+---------------------------+ - | Symbol | Description | Range | - +---------------+----------------------------------------+---------------------------+ - | ma_format_f32 | 32-bit floating point | [-1, 1] | - | ma_format_s16 | 16-bit signed integer | [-32768, 32767] | - | ma_format_s24 | 24-bit signed integer (tightly packed) | [-8388608, 8388607] | - | ma_format_s32 | 32-bit signed integer | [-2147483648, 2147483647] | - | ma_format_u8 | 8-bit unsigned integer | [0, 255] | - +---------------+----------------------------------------+---------------------------+ - -The `config.playback.channels` member sets the number of channels to use with the device. The -channel count cannot exceed MA_MAX_CHANNELS. The `config.sampleRate` member sets the sample rate -(which must be the same for both playback and capture in full-duplex configurations). This is -usually set to 44100 or 48000, but can be set to anything. It's recommended to keep this between -8000 and 384000, however. - -Note that leaving the format, channel count and/or sample rate at their default values will result -in the internal device's native configuration being used which is useful if you want to avoid the -overhead of miniaudio's automatic data conversion. - -In addition to the sample format, channel count and sample rate, the data callback and user data -pointer are also set via the config. The user data pointer is not passed into the callback as a -parameter, but is instead set to the `pUserData` member of `ma_device` which you can access -directly since all miniaudio structures are transparent. - -Initializing the device is done with `ma_device_init()`. This will return a result code telling you -what went wrong, if anything. On success it will return `MA_SUCCESS`. After initialization is -complete the device will be in a stopped state. To start it, use `ma_device_start()`. -Uninitializing the device will stop it, which is what the example above does, but you can also stop -the device with `ma_device_stop()`. To resume the device simply call `ma_device_start()` again. -Note that it's important to never stop or start the device from inside the callback. This will -result in a deadlock. Instead you set a variable or signal an event indicating that the device -needs to stop and handle it in a different thread. The following APIs must never be called inside -the callback: - - ```c - ma_device_init() - ma_device_init_ex() - ma_device_uninit() - ma_device_start() - ma_device_stop() - ``` - -You must never try uninitializing and reinitializing a device inside the callback. You must also -never try to stop and start it from inside the callback. There are a few other things you shouldn't -do in the callback depending on your requirements, however this isn't so much a thread-safety -thing, but rather a real-time processing thing which is beyond the scope of this introduction. - -The example above demonstrates the initialization of a playback device, but it works exactly the -same for capture. All you need to do is change the device type from `ma_device_type_playback` to -`ma_device_type_capture` when setting up the config, like so: - - ```c - ma_device_config config = ma_device_config_init(ma_device_type_capture); - config.capture.format = MY_FORMAT; - config.capture.channels = MY_CHANNEL_COUNT; - ``` - -In the data callback you just read from the input buffer (`pInput` in the example above) and leave -the output buffer alone (it will be set to NULL when the device type is set to -`ma_device_type_capture`). - -These are the available device types and how you should handle the buffers in the callback: - - +-------------------------+--------------------------------------------------------+ - | Device Type | Callback Behavior | - +-------------------------+--------------------------------------------------------+ - | ma_device_type_playback | Write to output buffer, leave input buffer untouched. | - | ma_device_type_capture | Read from input buffer, leave output buffer untouched. | - | ma_device_type_duplex | Read from input buffer, write to output buffer. | - | ma_device_type_loopback | Read from input buffer, leave output buffer untouched. | - +-------------------------+--------------------------------------------------------+ - -You will notice in the example above that the sample format and channel count is specified -separately for playback and capture. This is to support different data formats between the playback -and capture devices in a full-duplex system. An example may be that you want to capture audio data -as a monaural stream (one channel), but output sound to a stereo speaker system. Note that if you -use different formats between playback and capture in a full-duplex configuration you will need to -convert the data yourself. There are functions available to help you do this which will be -explained later. - -The example above did not specify a physical device to connect to which means it will use the -operating system's default device. If you have multiple physical devices connected and you want to -use a specific one you will need to specify the device ID in the configuration, like so: - - ```c - config.playback.pDeviceID = pMyPlaybackDeviceID; // Only if requesting a playback or duplex device. - config.capture.pDeviceID = pMyCaptureDeviceID; // Only if requesting a capture, duplex or loopback device. - ``` - -To retrieve the device ID you will need to perform device enumeration, however this requires the -use of a new concept called the "context". Conceptually speaking the context sits above the device. -There is one context to many devices. The purpose of the context is to represent the backend at a -more global level and to perform operations outside the scope of an individual device. Mainly it is -used for performing run-time linking against backend libraries, initializing backends and -enumerating devices. The example below shows how to enumerate devices. - - ```c - ma_context context; - if (ma_context_init(NULL, 0, NULL, &context) != MA_SUCCESS) { - // Error. - } - - ma_device_info* pPlaybackInfos; - ma_uint32 playbackCount; - ma_device_info* pCaptureInfos; - ma_uint32 captureCount; - if (ma_context_get_devices(&context, &pPlaybackInfos, &playbackCount, &pCaptureInfos, &captureCount) != MA_SUCCESS) { - // Error. - } - - // Loop over each device info and do something with it. Here we just print the name with their index. You may want - // to give the user the opportunity to choose which device they'd prefer. - for (ma_uint32 iDevice = 0; iDevice < playbackCount; iDevice += 1) { - printf("%d - %s\n", iDevice, pPlaybackInfos[iDevice].name); - } - - ma_device_config config = ma_device_config_init(ma_device_type_playback); - config.playback.pDeviceID = &pPlaybackInfos[chosenPlaybackDeviceIndex].id; - config.playback.format = MY_FORMAT; - config.playback.channels = MY_CHANNEL_COUNT; - config.sampleRate = MY_SAMPLE_RATE; - config.dataCallback = data_callback; - config.pUserData = pMyCustomData; - - ma_device device; - if (ma_device_init(&context, &config, &device) != MA_SUCCESS) { - // Error - } - - ... - - ma_device_uninit(&device); - ma_context_uninit(&context); - ``` - -The first thing we do in this example is initialize a `ma_context` object with `ma_context_init()`. -The first parameter is a pointer to a list of `ma_backend` values which are used to override the -default backend priorities. When this is NULL, as in this example, miniaudio's default priorities -are used. The second parameter is the number of backends listed in the array pointed to by the -first parameter. The third parameter is a pointer to a `ma_context_config` object which can be -NULL, in which case defaults are used. The context configuration is used for setting the logging -callback, custom memory allocation callbacks, user-defined data and some backend-specific -configurations. - -Once the context has been initialized you can enumerate devices. In the example above we use the -simpler `ma_context_get_devices()`, however you can also use a callback for handling devices by -using `ma_context_enumerate_devices()`. When using `ma_context_get_devices()` you provide a pointer -to a pointer that will, upon output, be set to a pointer to a buffer containing a list of -`ma_device_info` structures. You also provide a pointer to an unsigned integer that will receive -the number of items in the returned buffer. Do not free the returned buffers as their memory is -managed internally by miniaudio. - -The `ma_device_info` structure contains an `id` member which is the ID you pass to the device -config. It also contains the name of the device which is useful for presenting a list of devices -to the user via the UI. - -When creating your own context you will want to pass it to `ma_device_init()` when initializing the -device. Passing in NULL, like we do in the first example, will result in miniaudio creating the -context for you, which you don't want to do since you've already created a context. Note that -internally the context is only tracked by it's pointer which means you must not change the location -of the `ma_context` object. If this is an issue, consider using `malloc()` to allocate memory for -the context. - - -1.2. High Level API -------------------- -The high level API consists of three main parts: - - * Resource management for loading and streaming sounds. - * A node graph for advanced mixing and effect processing. - * A high level "engine" that wraps around the resource manager and node graph. - -The resource manager (`ma_resource_manager`) is used for loading sounds. It supports loading sounds -fully into memory and also streaming. It will also deal with reference counting for you which -avoids the same sound being loaded multiple times. - -The node graph is used for mixing and effect processing. The idea is that you connect a number of -nodes into the graph by connecting each node's outputs to another node's inputs. Each node can -implement its own effect. By chaining nodes together, advanced mixing and effect processing can -be achieved. - -The engine encapsulates both the resource manager and the node graph to create a simple, easy to -use high level API. The resource manager and node graph APIs are covered in more later sections of -this manual. - -The code below shows how you can initialize an engine using it's default configuration. - - ```c - ma_result result; - ma_engine engine; - - result = ma_engine_init(NULL, &engine); - if (result != MA_SUCCESS) { - return result; // Failed to initialize the engine. - } - ``` - -This creates an engine instance which will initialize a device internally which you can access with -`ma_engine_get_device()`. It will also initialize a resource manager for you which can be accessed -with `ma_engine_get_resource_manager()`. The engine itself is a node graph (`ma_node_graph`) which -means you can pass a pointer to the engine object into any of the `ma_node_graph` APIs (with a -cast). Alternatively, you can use `ma_engine_get_node_graph()` instead of a cast. - -Note that all objects in miniaudio, including the `ma_engine` object in the example above, are -transparent structures. There are no handles to opaque structures in miniaudio which means you need -to be mindful of how you declare them. In the example above we are declaring it on the stack, but -this will result in the struct being invalidated once the function encapsulating it returns. If -allocating the engine on the heap is more appropriate, you can easily do so with a standard call -to `malloc()` or whatever heap allocation routine you like: - - ```c - ma_engine* pEngine = malloc(sizeof(*pEngine)); - ``` - -The `ma_engine` API uses the same config/init pattern used all throughout miniaudio. To configure -an engine, you can fill out a `ma_engine_config` object and pass it into the first parameter of -`ma_engine_init()`: - - ```c - ma_result result; - ma_engine engine; - ma_engine_config engineConfig; - - engineConfig = ma_engine_config_init(); - engineConfig.pResourceManager = &myCustomResourceManager; // <-- Initialized as some earlier stage. - - result = ma_engine_init(&engineConfig, &engine); - if (result != MA_SUCCESS) { - return result; - } - ``` - -This creates an engine instance using a custom config. In this particular example it's showing how -you can specify a custom resource manager rather than having the engine initialize one internally. -This is particularly useful if you want to have multiple engine's share the same resource manager. - -The engine must be uninitialized with `ma_engine_uninit()` when it's no longer needed. - -By default the engine will be started, but nothing will be playing because no sounds have been -initialized. The easiest but least flexible way of playing a sound is like so: - - ```c - ma_engine_play_sound(&engine, "my_sound.wav", NULL); - ``` - -This plays what miniaudio calls an "inline" sound. It plays the sound once, and then puts the -internal sound up for recycling. The last parameter is used to specify which sound group the sound -should be associated with which will be explained later. This particular way of playing a sound is -simple, but lacks flexibility and features. A more flexible way of playing a sound is to first -initialize a sound: - - ```c - ma_result result; - ma_sound sound; - - result = ma_sound_init_from_file(&engine, "my_sound.wav", 0, NULL, NULL, &sound); - if (result != MA_SUCCESS) { - return result; - } - - ma_sound_start(&sound); - ``` - -This returns a `ma_sound` object which represents a single instance of the specified sound file. If -you want to play the same file multiple times simultaneously, you need to create one sound for each -instance. - -Sounds should be uninitialized with `ma_sound_uninit()`. - -Sounds are not started by default. Start a sound with `ma_sound_start()` and stop it with -`ma_sound_stop()`. When a sound is stopped, it is not rewound to the start. Use -`ma_sound_seek_to_pcm_frame(&sound, 0)` to seek back to the start of a sound. By default, starting -and stopping sounds happens immediately, but sometimes it might be convenient to schedule the sound -the be started and/or stopped at a specific time. This can be done with the following functions: - - ```c - ma_sound_set_start_time_in_pcm_frames() - ma_sound_set_start_time_in_milliseconds() - ma_sound_set_stop_time_in_pcm_frames() - ma_sound_set_stop_time_in_milliseconds() - ``` - -The start/stop time needs to be specified based on the absolute timer which is controlled by the -engine. The current global time in PCM frames can be retrieved with -`ma_engine_get_time_in_pcm_frames()`. The engine's global time can be changed with -`ma_engine_set_time_in_pcm_frames()` for synchronization purposes if required. Note that scheduling -a start time still requires an explicit call to `ma_sound_start()` before anything will play: - - ```c - ma_sound_set_start_time_in_pcm_frames(&sound, ma_engine_get_time_in_pcm_frames(&engine) + (ma_engine_get_sample_rate(&engine) * 2); - ma_sound_start(&sound); - ``` - -The third parameter of `ma_sound_init_from_file()` is a set of flags that control how the sound be -loaded and a few options on which features should be enabled for that sound. By default, the sound -is synchronously loaded fully into memory straight from the file system without any kind of -decoding. If you want to decode the sound before storing it in memory, you need to specify the -`MA_SOUND_FLAG_DECODE` flag. This is useful if you want to incur the cost of decoding at an earlier -stage, such as a loading stage. Without this option, decoding will happen dynamically at mixing -time which might be too expensive on the audio thread. - -If you want to load the sound asynchronously, you can specify the `MA_SOUND_FLAG_ASYNC` flag. This -will result in `ma_sound_init_from_file()` returning quickly, but the sound will not start playing -until the sound has had some audio decoded. - -The fourth parameter is a pointer to sound group. A sound group is used as a mechanism to organise -sounds into groups which have their own effect processing and volume control. An example is a game -which might have separate groups for sfx, voice and music. Each of these groups have their own -independent volume control. Use `ma_sound_group_init()` or `ma_sound_group_init_ex()` to initialize -a sound group. - -Sounds and sound groups are nodes in the engine's node graph and can be plugged into any `ma_node` -API. This makes it possible to connect sounds and sound groups to effect nodes to produce complex -effect chains. - -A sound can have its volume changed with `ma_sound_set_volume()`. If you prefer decibel volume -control you can use `ma_volume_db_to_linear()` to convert from decibel representation to linear. - -Panning and pitching is supported with `ma_sound_set_pan()` and `ma_sound_set_pitch()`. If you know -a sound will never have its pitch changed with `ma_sound_set_pitch()` or via the doppler effect, -you can specify the `MA_SOUND_FLAG_NO_PITCH` flag when initializing the sound for an optimization. - -By default, sounds and sound groups have spatialization enabled. If you don't ever want to -spatialize your sounds, initialize the sound with the `MA_SOUND_FLAG_NO_SPATIALIZATION` flag. The -spatialization model is fairly simple and is roughly on feature parity with OpenAL. HRTF and -environmental occlusion are not currently supported, but planned for the future. The supported -features include: - - * Sound and listener positioning and orientation with cones - * Attenuation models: none, inverse, linear and exponential - * Doppler effect - -Sounds can be faded in and out with `ma_sound_set_fade_in_pcm_frames()`. - -To check if a sound is currently playing, you can use `ma_sound_is_playing()`. To check if a sound -is at the end, use `ma_sound_at_end()`. Looping of a sound can be controlled with -`ma_sound_set_looping()`. Use `ma_sound_is_looping()` to check whether or not the sound is looping. - - - -2. Building -=========== -miniaudio should work cleanly out of the box without the need to download or install any -dependencies. See below for platform-specific details. - -Note that GCC and Clang require `-msse2`, `-mavx2`, etc. for SIMD optimizations. - -If you get errors about undefined references to `__sync_val_compare_and_swap_8`, `__atomic_load_8`, -etc. you need to link with `-latomic`. - - -2.1. Windows ------------- -The Windows build should compile cleanly on all popular compilers without the need to configure any -include paths nor link to any libraries. - -The UWP build may require linking to mmdevapi.lib if you get errors about an unresolved external -symbol for `ActivateAudioInterfaceAsync()`. - - -2.2. macOS and iOS ------------------- -The macOS build should compile cleanly without the need to download any dependencies nor link to -any libraries or frameworks. The iOS build needs to be compiled as Objective-C and will need to -link the relevant frameworks but should compile cleanly out of the box with Xcode. Compiling -through the command line requires linking to `-lpthread` and `-lm`. - -Due to the way miniaudio links to frameworks at runtime, your application may not pass Apple's -notarization process. To fix this there are two options. The first is to compile with -`-DMA_NO_RUNTIME_LINKING` which in turn will require linking with -`-framework CoreFoundation -framework CoreAudio -framework AudioToolbox`. If you get errors about -AudioToolbox, try with `-framework AudioUnit` instead. You may get this when using older versions -of iOS. Alternatively, if you would rather keep using runtime linking you can add the following to -your entitlements.xcent file: - - ``` - com.apple.security.cs.allow-dyld-environment-variables - - com.apple.security.cs.allow-unsigned-executable-memory - - ``` - -See this discussion for more info: https://github.com/mackron/miniaudio/issues/203. - - -2.3. Linux ----------- -The Linux build only requires linking to `-ldl`, `-lpthread` and `-lm`. You do not need any -development packages. You may need to link with `-latomic` if you're compiling for 32-bit ARM. - - -2.4. BSD --------- -The BSD build only requires linking to `-lpthread` and `-lm`. NetBSD uses audio(4), OpenBSD uses -sndio and FreeBSD uses OSS. You may need to link with `-latomic` if you're compiling for 32-bit -ARM. - - -2.5. Android ------------- -AAudio is the highest priority backend on Android. This should work out of the box without needing -any kind of compiler configuration. Support for AAudio starts with Android 8 which means older -versions will fall back to OpenSL|ES which requires API level 16+. - -There have been reports that the OpenSL|ES backend fails to initialize on some Android based -devices due to `dlopen()` failing to open "libOpenSLES.so". If this happens on your platform -you'll need to disable run-time linking with `MA_NO_RUNTIME_LINKING` and link with -lOpenSLES. - - -2.6. Emscripten ---------------- -The Emscripten build emits Web Audio JavaScript directly and should compile cleanly out of the box. -You cannot use `-std=c*` compiler flags, nor `-ansi`. - -You can enable the use of AudioWorkets by defining `MA_ENABLE_AUDIO_WORKLETS` and then compiling -with the following options: - - -sAUDIO_WORKLET=1 -sWASM_WORKERS=1 -sASYNCIFY - -An example for compiling with AudioWorklet support might look like this: - - emcc program.c -o bin/program.html -DMA_ENABLE_AUDIO_WORKLETS -sAUDIO_WORKLET=1 -sWASM_WORKERS=1 -sASYNCIFY - -To run locally, you'll need to use emrun: - - emrun bin/program.html - - - -2.7. Build Options ------------------- -`#define` these options before including miniaudio.c, or pass them as compiler flags: - - +----------------------------------+--------------------------------------------------------------------+ - | Option | Description | - +----------------------------------+--------------------------------------------------------------------+ - | MA_NO_WASAPI | Disables the WASAPI backend. | - +----------------------------------+--------------------------------------------------------------------+ - | MA_NO_DSOUND | Disables the DirectSound backend. | - +----------------------------------+--------------------------------------------------------------------+ - | MA_NO_WINMM | Disables the WinMM backend. | - +----------------------------------+--------------------------------------------------------------------+ - | MA_NO_ALSA | Disables the ALSA backend. | - +----------------------------------+--------------------------------------------------------------------+ - | MA_NO_PULSEAUDIO | Disables the PulseAudio backend. | - +----------------------------------+--------------------------------------------------------------------+ - | MA_NO_JACK | Disables the JACK backend. | - +----------------------------------+--------------------------------------------------------------------+ - | MA_NO_COREAUDIO | Disables the Core Audio backend. | - +----------------------------------+--------------------------------------------------------------------+ - | MA_NO_SNDIO | Disables the sndio backend. | - +----------------------------------+--------------------------------------------------------------------+ - | MA_NO_AUDIO4 | Disables the audio(4) backend. | - +----------------------------------+--------------------------------------------------------------------+ - | MA_NO_OSS | Disables the OSS backend. | - +----------------------------------+--------------------------------------------------------------------+ - | MA_NO_AAUDIO | Disables the AAudio backend. | - +----------------------------------+--------------------------------------------------------------------+ - | MA_NO_OPENSL | Disables the OpenSL|ES backend. | - +----------------------------------+--------------------------------------------------------------------+ - | MA_NO_WEBAUDIO | Disables the Web Audio backend. | - +----------------------------------+--------------------------------------------------------------------+ - | MA_NO_CUSTOM | Disables support for custom backends. | - +----------------------------------+--------------------------------------------------------------------+ - | MA_NO_NULL | Disables the null backend. | - +----------------------------------+--------------------------------------------------------------------+ - | MA_ENABLE_ONLY_SPECIFIC_BACKENDS | Disables all backends by default and requires `MA_ENABLE_*` to | - | | enable specific backends. | - +----------------------------------+--------------------------------------------------------------------+ - | MA_ENABLE_WASAPI | Used in conjunction with MA_ENABLE_ONLY_SPECIFIC_BACKENDS to | - | | enable the WASAPI backend. | - +----------------------------------+--------------------------------------------------------------------+ - | MA_ENABLE_DSOUND | Used in conjunction with MA_ENABLE_ONLY_SPECIFIC_BACKENDS to | - | | enable the DirectSound backend. | - +----------------------------------+--------------------------------------------------------------------+ - | MA_ENABLE_WINMM | Used in conjunction with MA_ENABLE_ONLY_SPECIFIC_BACKENDS to | - | | enable the WinMM backend. | - +----------------------------------+--------------------------------------------------------------------+ - | MA_ENABLE_ALSA | Used in conjunction with MA_ENABLE_ONLY_SPECIFIC_BACKENDS to | - | | enable the ALSA backend. | - +----------------------------------+--------------------------------------------------------------------+ - | MA_ENABLE_PULSEAUDIO | Used in conjunction with MA_ENABLE_ONLY_SPECIFIC_BACKENDS to | - | | enable the PulseAudio backend. | - +----------------------------------+--------------------------------------------------------------------+ - | MA_ENABLE_JACK | Used in conjunction with MA_ENABLE_ONLY_SPECIFIC_BACKENDS to | - | | enable the JACK backend. | - +----------------------------------+--------------------------------------------------------------------+ - | MA_ENABLE_COREAUDIO | Used in conjunction with MA_ENABLE_ONLY_SPECIFIC_BACKENDS to | - | | enable the Core Audio backend. | - +----------------------------------+--------------------------------------------------------------------+ - | MA_ENABLE_SNDIO | Used in conjunction with MA_ENABLE_ONLY_SPECIFIC_BACKENDS to | - | | enable the sndio backend. | - +----------------------------------+--------------------------------------------------------------------+ - | MA_ENABLE_AUDIO4 | Used in conjunction with MA_ENABLE_ONLY_SPECIFIC_BACKENDS to | - | | enable the audio(4) backend. | - +----------------------------------+--------------------------------------------------------------------+ - | MA_ENABLE_OSS | Used in conjunction with MA_ENABLE_ONLY_SPECIFIC_BACKENDS to | - | | enable the OSS backend. | - +----------------------------------+--------------------------------------------------------------------+ - | MA_ENABLE_AAUDIO | Used in conjunction with MA_ENABLE_ONLY_SPECIFIC_BACKENDS to | - | | enable the AAudio backend. | - +----------------------------------+--------------------------------------------------------------------+ - | MA_ENABLE_OPENSL | Used in conjunction with MA_ENABLE_ONLY_SPECIFIC_BACKENDS to | - | | enable the OpenSL|ES backend. | - +----------------------------------+--------------------------------------------------------------------+ - | MA_ENABLE_WEBAUDIO | Used in conjunction with MA_ENABLE_ONLY_SPECIFIC_BACKENDS to | - | | enable the Web Audio backend. | - +----------------------------------+--------------------------------------------------------------------+ - | MA_ENABLE_CUSTOM | Used in conjunction with MA_ENABLE_ONLY_SPECIFIC_BACKENDS to | - | | enable custom backends. | - +----------------------------------+--------------------------------------------------------------------+ - | MA_ENABLE_NULL | Used in conjunction with MA_ENABLE_ONLY_SPECIFIC_BACKENDS to | - | | enable the null backend. | - +----------------------------------+--------------------------------------------------------------------+ - | MA_NO_DECODING | Disables decoding APIs. | - +----------------------------------+--------------------------------------------------------------------+ - | MA_NO_ENCODING | Disables encoding APIs. | - +----------------------------------+--------------------------------------------------------------------+ - | MA_NO_WAV | Disables the built-in WAV decoder and encoder. | - +----------------------------------+--------------------------------------------------------------------+ - | MA_NO_FLAC | Disables the built-in FLAC decoder. | - +----------------------------------+--------------------------------------------------------------------+ - | MA_NO_MP3 | Disables the built-in MP3 decoder. | - +----------------------------------+--------------------------------------------------------------------+ - | MA_NO_DEVICE_IO | Disables playback and recording. This will disable `ma_context` | - | | and `ma_device` APIs. This is useful if you only want to use | - | | miniaudio's data conversion and/or decoding APIs. | - +----------------------------------+--------------------------------------------------------------------+ - | MA_NO_RESOURCE_MANAGER | Disables the resource manager. When using the engine this will | - | | also disable the following functions: | - | | | - | | ``` | - | | ma_sound_init_from_file() | - | | ma_sound_init_from_file_w() | - | | ma_sound_init_copy() | - | | ma_engine_play_sound_ex() | - | | ma_engine_play_sound() | - | | ``` | - | | | - | | The only way to initialize a `ma_sound` object is to initialize it | - | | from a data source. | - +----------------------------------+--------------------------------------------------------------------+ - | MA_NO_NODE_GRAPH | Disables the node graph API. This will also disable the engine API | - | | because it depends on the node graph. | - +----------------------------------+--------------------------------------------------------------------+ - | MA_NO_ENGINE | Disables the engine API. | - +----------------------------------+--------------------------------------------------------------------+ - | MA_NO_THREADING | Disables the `ma_thread`, `ma_mutex`, `ma_semaphore` and | - | | `ma_event` APIs. This option is useful if you only need to use | - | | miniaudio for data conversion, decoding and/or encoding. Some | - | | families of APIs require threading which means the following | - | | options must also be set: | - | | | - | | ``` | - | | MA_NO_DEVICE_IO | - | | ``` | - +----------------------------------+--------------------------------------------------------------------+ - | MA_NO_GENERATION | Disables generation APIs such a `ma_waveform` and `ma_noise`. | - +----------------------------------+--------------------------------------------------------------------+ - | MA_NO_SSE2 | Disables SSE2 optimizations. | - +----------------------------------+--------------------------------------------------------------------+ - | MA_NO_AVX2 | Disables AVX2 optimizations. | - +----------------------------------+--------------------------------------------------------------------+ - | MA_NO_NEON | Disables NEON optimizations. | - +----------------------------------+--------------------------------------------------------------------+ - | MA_NO_RUNTIME_LINKING | Disables runtime linking. This is useful for passing Apple's | - | | notarization process. When enabling this, you may need to avoid | - | | using `-std=c89` or `-std=c99` on Linux builds or else you may end | - | | up with compilation errors due to conflicts with `timespec` and | - | | `timeval` data types. | - | | | - | | You may need to enable this if your target platform does not allow | - | | runtime linking via `dlopen()`. | - +----------------------------------+--------------------------------------------------------------------+ - | MA_USE_STDINT | (Pass this in as a compiler flag. Do not `#define` this before | - | | miniaudio.c) Forces the use of stdint.h for sized types. | - +----------------------------------+--------------------------------------------------------------------+ - | MA_DEBUG_OUTPUT | Enable `printf()` output of debug logs (`MA_LOG_LEVEL_DEBUG`). | - +----------------------------------+--------------------------------------------------------------------+ - | MA_COINIT_VALUE | Windows only. The value to pass to internal calls to | - | | `CoInitializeEx()`. Defaults to `COINIT_MULTITHREADED`. | - +----------------------------------+--------------------------------------------------------------------+ - | MA_FORCE_UWP | Windows only. Affects only the WASAPI backend. Will force the | - | | WASAPI backend to use the UWP code path instead of the regular | - | | desktop path. This is normally auto-detected and should rarely be | - | | needed to be used explicitly, but can be useful for debugging. | - +----------------------------------+--------------------------------------------------------------------+ - | MA_ON_THREAD_ENTRY | Defines some code that will be executed as soon as an internal | - | | miniaudio-managed thread is created. This will be the first thing | - | | to be executed by the thread entry point. | - +----------------------------------+--------------------------------------------------------------------+ - | MA_ON_THREAD_EXIT | Defines some code that will be executed from the entry point of an | - | | internal miniaudio-managed thread upon exit. This will be the last | - | | thing to be executed before the thread's entry point exits. | - +----------------------------------+--------------------------------------------------------------------+ - | MA_THREAD_DEFAULT_STACK_SIZE | If set, specifies the default stack size used by miniaudio-managed | - | | threads. | - +----------------------------------+--------------------------------------------------------------------+ - | MA_API | Controls how public APIs should be decorated. Default is `extern`. | - +----------------------------------+--------------------------------------------------------------------+ - - -3. Definitions -============== -This section defines common terms used throughout miniaudio. Unfortunately there is often ambiguity -in the use of terms throughout the audio space, so this section is intended to clarify how miniaudio -uses each term. - -3.1. Sample ------------ -A sample is a single unit of audio data. If the sample format is f32, then one sample is one 32-bit -floating point number. - -3.2. Frame / PCM Frame ----------------------- -A frame is a group of samples equal to the number of channels. For a stereo stream a frame is 2 -samples, a mono frame is 1 sample, a 5.1 surround sound frame is 6 samples, etc. The terms "frame" -and "PCM frame" are the same thing in miniaudio. Note that this is different to a compressed frame. -If ever miniaudio needs to refer to a compressed frame, such as a FLAC frame, it will always -clarify what it's referring to with something like "FLAC frame". - -3.3. Channel ------------- -A stream of monaural audio that is emitted from an individual speaker in a speaker system, or -received from an individual microphone in a microphone system. A stereo stream has two channels (a -left channel, and a right channel), a 5.1 surround sound system has 6 channels, etc. Some audio -systems refer to a channel as a complex audio stream that's mixed with other channels to produce -the final mix - this is completely different to miniaudio's use of the term "channel" and should -not be confused. - -3.4. Sample Rate ----------------- -The sample rate in miniaudio is always expressed in Hz, such as 44100, 48000, etc. It's the number -of PCM frames that are processed per second. - -3.5. Formats ------------- -Throughout miniaudio you will see references to different sample formats: - - +---------------+----------------------------------------+---------------------------+ - | Symbol | Description | Range | - +---------------+----------------------------------------+---------------------------+ - | ma_format_f32 | 32-bit floating point | [-1, 1] | - | ma_format_s16 | 16-bit signed integer | [-32768, 32767] | - | ma_format_s24 | 24-bit signed integer (tightly packed) | [-8388608, 8388607] | - | ma_format_s32 | 32-bit signed integer | [-2147483648, 2147483647] | - | ma_format_u8 | 8-bit unsigned integer | [0, 255] | - +---------------+----------------------------------------+---------------------------+ - -All formats are native-endian. - - - -4. Data Sources -=============== -The data source abstraction in miniaudio is used for retrieving audio data from some source. A few -examples include `ma_decoder`, `ma_noise` and `ma_waveform`. You will need to be familiar with data -sources in order to make sense of some of the higher level concepts in miniaudio. - -The `ma_data_source` API is a generic interface for reading from a data source. Any object that -implements the data source interface can be plugged into any `ma_data_source` function. - -To read data from a data source: - - ```c - ma_result result; - ma_uint64 framesRead; - - result = ma_data_source_read_pcm_frames(pDataSource, pFramesOut, frameCount, &framesRead); - if (result != MA_SUCCESS) { - return result; // Failed to read data from the data source. - } - ``` - -If you don't need the number of frames that were successfully read you can pass in `NULL` to the -`pFramesRead` parameter. If this returns a value less than the number of frames requested it means -the end of the file has been reached. `MA_AT_END` will be returned only when the number of frames -read is 0. - -When calling any data source function, with the exception of `ma_data_source_init()` and -`ma_data_source_uninit()`, you can pass in any object that implements a data source. For example, -you could plug in a decoder like so: - - ```c - ma_result result; - ma_uint64 framesRead; - ma_decoder decoder; // <-- This would be initialized with `ma_decoder_init_*()`. - - result = ma_data_source_read_pcm_frames(&decoder, pFramesOut, frameCount, &framesRead); - if (result != MA_SUCCESS) { - return result; // Failed to read data from the decoder. - } - ``` - -If you want to seek forward you can pass in `NULL` to the `pFramesOut` parameter. Alternatively you -can use `ma_data_source_seek_pcm_frames()`. - -To seek to a specific PCM frame: - - ```c - result = ma_data_source_seek_to_pcm_frame(pDataSource, frameIndex); - if (result != MA_SUCCESS) { - return result; // Failed to seek to PCM frame. - } - ``` - -You can retrieve the total length of a data source in PCM frames, but note that some data sources -may not have the notion of a length, such as noise and waveforms, and others may just not have a -way of determining the length such as some decoders. To retrieve the length: - - ```c - ma_uint64 length; - - result = ma_data_source_get_length_in_pcm_frames(pDataSource, &length); - if (result != MA_SUCCESS) { - return result; // Failed to retrieve the length. - } - ``` - -Care should be taken when retrieving the length of a data source where the underlying decoder is -pulling data from a data stream with an undefined length, such as internet radio or some kind of -broadcast. If you do this, `ma_data_source_get_length_in_pcm_frames()` may never return. - -The current position of the cursor in PCM frames can also be retrieved: - - ```c - ma_uint64 cursor; - - result = ma_data_source_get_cursor_in_pcm_frames(pDataSource, &cursor); - if (result != MA_SUCCESS) { - return result; // Failed to retrieve the cursor. - } - ``` - -You will often need to know the data format that will be returned after reading. This can be -retrieved like so: - - ```c - ma_format format; - ma_uint32 channels; - ma_uint32 sampleRate; - ma_channel channelMap[MA_MAX_CHANNELS]; - - result = ma_data_source_get_data_format(pDataSource, &format, &channels, &sampleRate, channelMap, MA_MAX_CHANNELS); - if (result != MA_SUCCESS) { - return result; // Failed to retrieve data format. - } - ``` - -If you do not need a specific data format property, just pass in NULL to the respective parameter. - -There may be cases where you want to implement something like a sound bank where you only want to -read data within a certain range of the underlying data. To do this you can use a range: - - ```c - result = ma_data_source_set_range_in_pcm_frames(pDataSource, rangeBegInFrames, rangeEndInFrames); - if (result != MA_SUCCESS) { - return result; // Failed to set the range. - } - ``` - -This is useful if you have a sound bank where many sounds are stored in the same file and you want -the data source to only play one of those sub-sounds. Note that once the range is set, everything -that takes a position, such as cursors and loop points, should always be relatvie to the start of -the range. When the range is set, any previously defined loop point will be reset. - -Custom loop points can also be used with data sources. By default, data sources will loop after -they reach the end of the data source, but if you need to loop at a specific location, you can do -the following: - - ```c - result = ma_data_set_loop_point_in_pcm_frames(pDataSource, loopBegInFrames, loopEndInFrames); - if (result != MA_SUCCESS) { - return result; // Failed to set the loop point. - } - ``` - -The loop point is relative to the current range. - -It's sometimes useful to chain data sources together so that a seamless transition can be achieved. -To do this, you can use chaining: - - ```c - ma_decoder decoder1; - ma_decoder decoder2; - - // ... initialize decoders with ma_decoder_init_*() ... - - result = ma_data_source_set_next(&decoder1, &decoder2); - if (result != MA_SUCCESS) { - return result; // Failed to set the next data source. - } - - result = ma_data_source_read_pcm_frames(&decoder1, pFramesOut, frameCount, pFramesRead); - if (result != MA_SUCCESS) { - return result; // Failed to read from the decoder. - } - ``` - -In the example above we're using decoders. When reading from a chain, you always want to read from -the top level data source in the chain. In the example above, `decoder1` is the top level data -source in the chain. When `decoder1` reaches the end, `decoder2` will start seamlessly without any -gaps. - -Note that when looping is enabled, only the current data source will be looped. You can loop the -entire chain by linking in a loop like so: - - ```c - ma_data_source_set_next(&decoder1, &decoder2); // decoder1 -> decoder2 - ma_data_source_set_next(&decoder2, &decoder1); // decoder2 -> decoder1 (loop back to the start). - ``` - -Note that setting up chaining is not thread safe, so care needs to be taken if you're dynamically -changing links while the audio thread is in the middle of reading. - -Do not use `ma_decoder_seek_to_pcm_frame()` as a means to reuse a data source to play multiple -instances of the same sound simultaneously. This can be extremely inefficient depending on the type -of data source and can result in glitching due to subtle changes to the state of internal filters. -Instead, initialize multiple data sources for each instance. - - -4.1. Custom Data Sources ------------------------- -You can implement a custom data source by implementing the functions in `ma_data_source_vtable`. -Your custom object must have `ma_data_source_base` as it's first member: - - ```c - struct my_data_source - { - ma_data_source_base base; - ... - }; - ``` - -In your initialization routine, you need to call `ma_data_source_init()` in order to set up the -base object (`ma_data_source_base`): - - ```c - static ma_result my_data_source_read(ma_data_source* pDataSource, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead) - { - // Read data here. Output in the same format returned by my_data_source_get_data_format(). - } - - static ma_result my_data_source_seek(ma_data_source* pDataSource, ma_uint64 frameIndex) - { - // Seek to a specific PCM frame here. Return MA_NOT_IMPLEMENTED if seeking is not supported. - } - - static ma_result my_data_source_get_data_format(ma_data_source* pDataSource, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate, ma_channel* pChannelMap, size_t channelMapCap) - { - // Return the format of the data here. - } - - static ma_result my_data_source_get_cursor(ma_data_source* pDataSource, ma_uint64* pCursor) - { - // Retrieve the current position of the cursor here. Return MA_NOT_IMPLEMENTED and set *pCursor to 0 if there is no notion of a cursor. - } - - static ma_result my_data_source_get_length(ma_data_source* pDataSource, ma_uint64* pLength) - { - // Retrieve the length in PCM frames here. Return MA_NOT_IMPLEMENTED and set *pLength to 0 if there is no notion of a length or if the length is unknown. - } - - static ma_data_source_vtable g_my_data_source_vtable = - { - my_data_source_read, - my_data_source_seek, - my_data_source_get_data_format, - my_data_source_get_cursor, - my_data_source_get_length - }; - - ma_result my_data_source_init(my_data_source* pMyDataSource) - { - ma_result result; - ma_data_source_config baseConfig; - - baseConfig = ma_data_source_config_init(); - baseConfig.vtable = &g_my_data_source_vtable; - - result = ma_data_source_init(&baseConfig, &pMyDataSource->base); - if (result != MA_SUCCESS) { - return result; - } - - // ... do the initialization of your custom data source here ... - - return MA_SUCCESS; - } - - void my_data_source_uninit(my_data_source* pMyDataSource) - { - // ... do the uninitialization of your custom data source here ... - - // You must uninitialize the base data source. - ma_data_source_uninit(&pMyDataSource->base); - } - ``` - -Note that `ma_data_source_init()` and `ma_data_source_uninit()` are never called directly outside -of the custom data source. It's up to the custom data source itself to call these within their own -init/uninit functions. - - - -5. Engine -========= -The `ma_engine` API is a high level API for managing and mixing sounds and effect processing. The -`ma_engine` object encapsulates a resource manager and a node graph, both of which will be -explained in more detail later. - -Sounds are called `ma_sound` and are created from an engine. Sounds can be associated with a mixing -group called `ma_sound_group` which are also created from the engine. Both `ma_sound` and -`ma_sound_group` objects are nodes within the engine's node graph. - -When the engine is initialized, it will normally create a device internally. If you would rather -manage the device yourself, you can do so and just pass a pointer to it via the engine config when -you initialize the engine. You can also just use the engine without a device, which again can be -configured via the engine config. - -The most basic way to initialize the engine is with a default config, like so: - - ```c - ma_result result; - ma_engine engine; - - result = ma_engine_init(NULL, &engine); - if (result != MA_SUCCESS) { - return result; // Failed to initialize the engine. - } - ``` - -This will result in the engine initializing a playback device using the operating system's default -device. This will be sufficient for many use cases, but if you need more flexibility you'll want to -configure the engine with an engine config: - - ```c - ma_result result; - ma_engine engine; - ma_engine_config engineConfig; - - engineConfig = ma_engine_config_init(); - engineConfig.pDevice = &myDevice; - - result = ma_engine_init(&engineConfig, &engine); - if (result != MA_SUCCESS) { - return result; // Failed to initialize the engine. - } - ``` - -In the example above we're passing in a pre-initialized device. Since the caller is the one in -control of the device's data callback, it's their responsibility to manually call -`ma_engine_read_pcm_frames()` from inside their data callback: - - ```c - void playback_data_callback(ma_device* pDevice, void* pOutput, const void* pInput, ma_uint32 frameCount) - { - ma_engine_read_pcm_frames(&g_Engine, pOutput, frameCount, NULL); - } - ``` - -You can also use the engine independent of a device entirely: - - ```c - ma_result result; - ma_engine engine; - ma_engine_config engineConfig; - - engineConfig = ma_engine_config_init(); - engineConfig.noDevice = MA_TRUE; - engineConfig.channels = 2; // Must be set when not using a device. - engineConfig.sampleRate = 48000; // Must be set when not using a device. - - result = ma_engine_init(&engineConfig, &engine); - if (result != MA_SUCCESS) { - return result; // Failed to initialize the engine. - } - ``` - -Note that when you're not using a device, you must set the channel count and sample rate in the -config or else miniaudio won't know what to use (miniaudio will use the device to determine this -normally). When not using a device, you need to use `ma_engine_read_pcm_frames()` to process audio -data from the engine. This kind of setup is useful if you want to do something like offline -processing or want to use a different audio system for playback such as SDL. - -When a sound is loaded it goes through a resource manager. By default the engine will initialize a -resource manager internally, but you can also specify a pre-initialized resource manager: - - ```c - ma_result result; - ma_engine engine1; - ma_engine engine2; - ma_engine_config engineConfig; - - engineConfig = ma_engine_config_init(); - engineConfig.pResourceManager = &myResourceManager; - - ma_engine_init(&engineConfig, &engine1); - ma_engine_init(&engineConfig, &engine2); - ``` - -In this example we are initializing two engines, both of which are sharing the same resource -manager. This is especially useful for saving memory when loading the same file across multiple -engines. If you were not to use a shared resource manager, each engine instance would use their own -which would result in any sounds that are used between both engine's being loaded twice. By using -a shared resource manager, it would only be loaded once. Using multiple engine's is useful when you -need to output to multiple playback devices, such as in a local multiplayer game where each player -is using their own set of headphones. - -By default an engine will be in a started state. To make it so the engine is not automatically -started you can configure it as such: - - ```c - engineConfig.noAutoStart = MA_TRUE; - - // The engine will need to be started manually. - ma_engine_start(&engine); - - // Later on the engine can be stopped with ma_engine_stop(). - ma_engine_stop(&engine); - ``` - -The concept of starting or stopping an engine is only relevant when using the engine with a -device. Attempting to start or stop an engine that is not associated with a device will result in -`MA_INVALID_OPERATION`. - -The master volume of the engine can be controlled with `ma_engine_set_volume()` which takes a -linear scale, with 0 resulting in silence and anything above 1 resulting in amplification. If you -prefer decibel based volume control, use `ma_volume_db_to_linear()` to convert from dB to linear. - -When a sound is spatialized, it is done so relative to a listener. An engine can be configured to -have multiple listeners which can be configured via the config: - - ```c - engineConfig.listenerCount = 2; - ``` - -The maximum number of listeners is restricted to `MA_ENGINE_MAX_LISTENERS`. By default, when a -sound is spatialized, it will be done so relative to the closest listener. You can also pin a sound -to a specific listener which will be explained later. Listener's have a position, direction, cone, -and velocity (for doppler effect). A listener is referenced by an index, the meaning of which is up -to the caller (the index is 0 based and cannot go beyond the listener count, minus 1). The -position, direction and velocity are all specified in absolute terms: - - ```c - ma_engine_listener_set_position(&engine, listenerIndex, worldPosX, worldPosY, worldPosZ); - ``` - -The direction of the listener represents it's forward vector. The listener's up vector can also be -specified and defaults to +1 on the Y axis. - - ```c - ma_engine_listener_set_direction(&engine, listenerIndex, forwardX, forwardY, forwardZ); - ma_engine_listener_set_world_up(&engine, listenerIndex, 0, 1, 0); - ``` - -The engine supports directional attenuation. The listener can have a cone the controls how sound is -attenuated based on the listener's direction. When a sound is between the inner and outer cones, it -will be attenuated between 1 and the cone's outer gain: - - ```c - ma_engine_listener_set_cone(&engine, listenerIndex, innerAngleInRadians, outerAngleInRadians, outerGain); - ``` - -When a sound is inside the inner code, no directional attenuation is applied. When the sound is -outside of the outer cone, the attenuation will be set to `outerGain` in the example above. When -the sound is in between the inner and outer cones, the attenuation will be interpolated between 1 -and the outer gain. - -The engine's coordinate system follows the OpenGL coordinate system where positive X points right, -positive Y points up and negative Z points forward. - -The simplest and least flexible way to play a sound is like so: - - ```c - ma_engine_play_sound(&engine, "my_sound.wav", pGroup); - ``` - -This is a "fire and forget" style of function. The engine will manage the `ma_sound` object -internally. When the sound finishes playing, it'll be put up for recycling. For more flexibility -you'll want to initialize a sound object: - - ```c - ma_sound sound; - - result = ma_sound_init_from_file(&engine, "my_sound.wav", flags, pGroup, NULL, &sound); - if (result != MA_SUCCESS) { - return result; // Failed to load sound. - } - ``` - -Sounds need to be uninitialized with `ma_sound_uninit()`. - -The example above loads a sound from a file. If the resource manager has been disabled you will not -be able to use this function and instead you'll need to initialize a sound directly from a data -source: - - ```c - ma_sound sound; - - result = ma_sound_init_from_data_source(&engine, &dataSource, flags, pGroup, &sound); - if (result != MA_SUCCESS) { - return result; - } - ``` - -Each `ma_sound` object represents a single instance of the sound. If you want to play the same -sound multiple times at the same time, you need to initialize a separate `ma_sound` object. - -For the most flexibility when initializing sounds, use `ma_sound_init_ex()`. This uses miniaudio's -standard config/init pattern: - - ```c - ma_sound sound; - ma_sound_config soundConfig; - - soundConfig = ma_sound_config_init(); - soundConfig.pFilePath = NULL; // Set this to load from a file path. - soundConfig.pDataSource = NULL; // Set this to initialize from an existing data source. - soundConfig.pInitialAttachment = &someNodeInTheNodeGraph; - soundConfig.initialAttachmentInputBusIndex = 0; - soundConfig.channelsIn = 1; - soundConfig.channelsOut = 0; // Set to 0 to use the engine's native channel count. - - result = ma_sound_init_ex(&soundConfig, &sound); - if (result != MA_SUCCESS) { - return result; - } - ``` - -In the example above, the sound is being initialized without a file nor a data source. This is -valid, in which case the sound acts as a node in the middle of the node graph. This means you can -connect other sounds to this sound and allow it to act like a sound group. Indeed, this is exactly -what a `ma_sound_group` is. - -When loading a sound, you specify a set of flags that control how the sound is loaded and what -features are enabled for that sound. When no flags are set, the sound will be fully loaded into -memory in exactly the same format as how it's stored on the file system. The resource manager will -allocate a block of memory and then load the file directly into it. When reading audio data, it -will be decoded dynamically on the fly. In order to save processing time on the audio thread, it -might be beneficial to pre-decode the sound. You can do this with the `MA_SOUND_FLAG_DECODE` flag: - - ```c - ma_sound_init_from_file(&engine, "my_sound.wav", MA_SOUND_FLAG_DECODE, pGroup, NULL, &sound); - ``` - -By default, sounds will be loaded synchronously, meaning `ma_sound_init_*()` will not return until -the sound has been fully loaded. If this is prohibitive you can instead load sounds asynchronously -by specifying the `MA_SOUND_FLAG_ASYNC` flag: - - ```c - ma_sound_init_from_file(&engine, "my_sound.wav", MA_SOUND_FLAG_DECODE | MA_SOUND_FLAG_ASYNC, pGroup, NULL, &sound); - ``` - -This will result in `ma_sound_init_*()` returning quickly, but the sound won't yet have been fully -loaded. When you start the sound, it won't output anything until some sound is available. The sound -will start outputting audio before the sound has been fully decoded when the `MA_SOUND_FLAG_DECODE` -is specified. - -If you need to wait for an asynchronously loaded sound to be fully loaded, you can use a fence. A -fence in miniaudio is a simple synchronization mechanism which simply blocks until it's internal -counter hit's zero. You can specify a fence like so: - - ```c - ma_result result; - ma_fence fence; - ma_sound sounds[4]; - - result = ma_fence_init(&fence); - if (result != MA_SUCCESS) { - return result; - } - - // Load some sounds asynchronously. - for (int iSound = 0; iSound < 4; iSound += 1) { - ma_sound_init_from_file(&engine, mySoundFilesPaths[iSound], MA_SOUND_FLAG_DECODE | MA_SOUND_FLAG_ASYNC, pGroup, &fence, &sounds[iSound]); - } - - // ... do some other stuff here in the mean time ... - - // Wait for all sounds to finish loading. - ma_fence_wait(&fence); - ``` - -If loading the entire sound into memory is prohibitive, you can also configure the engine to stream -the audio data: - - ```c - ma_sound_init_from_file(&engine, "my_sound.wav", MA_SOUND_FLAG_STREAM, pGroup, NULL, &sound); - ``` - -When streaming sounds, 2 seconds worth of audio data is stored in memory. Although it should work -fine, it's inefficient to use streaming for short sounds. Streaming is useful for things like music -tracks in games. - -When loading a sound from a file path, the engine will reference count the file to prevent it from -being loaded if it's already in memory. When you uninitialize a sound, the reference count will be -decremented, and if it hits zero, the sound will be unloaded from memory. This reference counting -system is not used for streams. The engine will use a 64-bit hash of the file name when comparing -file paths which means there's a small chance you might encounter a name collision. If this is an -issue, you'll need to use a different name for one of the colliding file paths, or just not load -from files and instead load from a data source. - -You can use `ma_sound_init_copy()` to initialize a copy of another sound. Note, however, that this -only works for sounds that were initialized with `ma_sound_init_from_file()` and without the -`MA_SOUND_FLAG_STREAM` flag. - -When you initialize a sound, if you specify a sound group the sound will be attached to that group -automatically. If you set it to NULL, it will be automatically attached to the engine's endpoint. -If you would instead rather leave the sound unattached by default, you can specify the -`MA_SOUND_FLAG_NO_DEFAULT_ATTACHMENT` flag. This is useful if you want to set up a complex node -graph. - -Sounds are not started by default. To start a sound, use `ma_sound_start()`. Stop a sound with -`ma_sound_stop()`. - -Sounds can have their volume controlled with `ma_sound_set_volume()` in the same way as the -engine's master volume. - -Sounds support stereo panning and pitching. Set the pan with `ma_sound_set_pan()`. Setting the pan -to 0 will result in an unpanned sound. Setting it to -1 will shift everything to the left, whereas -+1 will shift it to the right. The pitch can be controlled with `ma_sound_set_pitch()`. A larger -value will result in a higher pitch. The pitch must be greater than 0. - -The engine supports 3D spatialization of sounds. By default sounds will have spatialization -enabled, but if a sound does not need to be spatialized it's best to disable it. There are two ways -to disable spatialization of a sound: - - ```c - // Disable spatialization at initialization time via a flag: - ma_sound_init_from_file(&engine, "my_sound.wav", MA_SOUND_FLAG_NO_SPATIALIZATION, NULL, NULL, &sound); - - // Dynamically disable or enable spatialization post-initialization: - ma_sound_set_spatialization_enabled(&sound, isSpatializationEnabled); - ``` - -By default sounds will be spatialized based on the closest listener. If a sound should always be -spatialized relative to a specific listener it can be pinned to one: - - ```c - ma_sound_set_pinned_listener_index(&sound, listenerIndex); - ``` - -Like listeners, sounds have a position. By default, the position of a sound is in absolute space, -but it can be changed to be relative to a listener: - - ```c - ma_sound_set_positioning(&sound, ma_positioning_relative); - ``` - -Note that relative positioning of a sound only makes sense if there is either only one listener, or -the sound is pinned to a specific listener. To set the position of a sound: - - ```c - ma_sound_set_position(&sound, posX, posY, posZ); - ``` - -The direction works the same way as a listener and represents the sound's forward direction: - - ```c - ma_sound_set_direction(&sound, forwardX, forwardY, forwardZ); - ``` - -Sound's also have a cone for controlling directional attenuation. This works exactly the same as -listeners: - - ```c - ma_sound_set_cone(&sound, innerAngleInRadians, outerAngleInRadians, outerGain); - ``` - -The velocity of a sound is used for doppler effect and can be set as such: - - ```c - ma_sound_set_velocity(&sound, velocityX, velocityY, velocityZ); - ``` - -The engine supports different attenuation models which can be configured on a per-sound basis. By -default the attenuation model is set to `ma_attenuation_model_inverse` which is the equivalent to -OpenAL's `AL_INVERSE_DISTANCE_CLAMPED`. Configure the attenuation model like so: - - ```c - ma_sound_set_attenuation_model(&sound, ma_attenuation_model_inverse); - ``` - -The supported attenuation models include the following: - - +----------------------------------+----------------------------------------------+ - | ma_attenuation_model_none | No distance attenuation. | - +----------------------------------+----------------------------------------------+ - | ma_attenuation_model_inverse | Equivalent to `AL_INVERSE_DISTANCE_CLAMPED`. | - +----------------------------------+----------------------------------------------+ - | ma_attenuation_model_linear | Linear attenuation. | - +----------------------------------+----------------------------------------------+ - | ma_attenuation_model_exponential | Exponential attenuation. | - +----------------------------------+----------------------------------------------+ - -To control how quickly a sound rolls off as it moves away from the listener, you need to configure -the rolloff: - - ```c - ma_sound_set_rolloff(&sound, rolloff); - ``` - -You can control the minimum and maximum gain to apply from spatialization: - - ```c - ma_sound_set_min_gain(&sound, minGain); - ma_sound_set_max_gain(&sound, maxGain); - ``` - -Likewise, in the calculation of attenuation, you can control the minimum and maximum distances for -the attenuation calculation. This is useful if you want to ensure sounds don't drop below a certain -volume after the listener moves further away and to have sounds play a maximum volume when the -listener is within a certain distance: - - ```c - ma_sound_set_min_distance(&sound, minDistance); - ma_sound_set_max_distance(&sound, maxDistance); - ``` - -The engine's spatialization system supports doppler effect. The doppler factor can be configure on -a per-sound basis like so: - - ```c - ma_sound_set_doppler_factor(&sound, dopplerFactor); - ``` - -You can fade sounds in and out with `ma_sound_set_fade_in_pcm_frames()` and -`ma_sound_set_fade_in_milliseconds()`. Set the volume to -1 to use the current volume as the -starting volume: - - ```c - // Fade in over 1 second. - ma_sound_set_fade_in_milliseconds(&sound, 0, 1, 1000); - - // ... sometime later ... - - // Fade out over 1 second, starting from the current volume. - ma_sound_set_fade_in_milliseconds(&sound, -1, 0, 1000); - ``` - -By default sounds will start immediately, but sometimes for timing and synchronization purposes it -can be useful to schedule a sound to start or stop: - - ```c - // Start the sound in 1 second from now. - ma_sound_set_start_time_in_pcm_frames(&sound, ma_engine_get_time_in_pcm_frames(&engine) + (ma_engine_get_sample_rate(&engine) * 1)); - - // Stop the sound in 2 seconds from now. - ma_sound_set_stop_time_in_pcm_frames(&sound, ma_engine_get_time_in_pcm_frames(&engine) + (ma_engine_get_sample_rate(&engine) * 2)); - ``` - -Note that scheduling a start time still requires an explicit call to `ma_sound_start()` before -anything will play. - -The time is specified in global time which is controlled by the engine. You can get the engine's -current time with `ma_engine_get_time_in_pcm_frames()`. The engine's global time is incremented -automatically as audio data is read, but it can be reset with `ma_engine_set_time_in_pcm_frames()` -in case it needs to be resynchronized for some reason. - -To determine whether or not a sound is currently playing, use `ma_sound_is_playing()`. This will -take the scheduled start and stop times into account. - -Whether or not a sound should loop can be controlled with `ma_sound_set_looping()`. Sounds will not -be looping by default. Use `ma_sound_is_looping()` to determine whether or not a sound is looping. - -Use `ma_sound_at_end()` to determine whether or not a sound is currently at the end. For a looping -sound this should never return true. Alternatively, you can configure a callback that will be fired -when the sound reaches the end. Note that the callback is fired from the audio thread which means -you cannot be uninitializing sound from the callback. To set the callback you can use -`ma_sound_set_end_callback()`. Alternatively, if you're using `ma_sound_init_ex()`, you can pass it -into the config like so: - - ```c - soundConfig.endCallback = my_end_callback; - soundConfig.pEndCallbackUserData = pMyEndCallbackUserData; - ``` - -The end callback is declared like so: - - ```c - void my_end_callback(void* pUserData, ma_sound* pSound) - { - ... - } - ``` - -Internally a sound wraps around a data source. Some APIs exist to control the underlying data -source, mainly for convenience: - - ```c - ma_sound_seek_to_pcm_frame(&sound, frameIndex); - ma_sound_get_data_format(&sound, &format, &channels, &sampleRate, pChannelMap, channelMapCapacity); - ma_sound_get_cursor_in_pcm_frames(&sound, &cursor); - ma_sound_get_length_in_pcm_frames(&sound, &length); - ``` - -Sound groups have the same API as sounds, only they are called `ma_sound_group`, and since they do -not have any notion of a data source, anything relating to a data source is unavailable. - -Internally, sound data is loaded via the `ma_decoder` API which means by default it only supports -file formats that have built-in support in miniaudio. You can extend this to support any kind of -file format through the use of custom decoders. To do this you'll need to use a self-managed -resource manager and configure it appropriately. See the "Resource Management" section below for -details on how to set this up. - - -6. Resource Management -====================== -Many programs will want to manage sound resources for things such as reference counting and -streaming. This is supported by miniaudio via the `ma_resource_manager` API. - -The resource manager is mainly responsible for the following: - - * Loading of sound files into memory with reference counting. - * Streaming of sound data. - -When loading a sound file, the resource manager will give you back a `ma_data_source` compatible -object called `ma_resource_manager_data_source`. This object can be passed into any -`ma_data_source` API which is how you can read and seek audio data. When loading a sound file, you -specify whether or not you want the sound to be fully loaded into memory (and optionally -pre-decoded) or streamed. When loading into memory, you can also specify whether or not you want -the data to be loaded asynchronously. - -The example below is how you can initialize a resource manager using it's default configuration: - - ```c - ma_resource_manager_config config; - ma_resource_manager resourceManager; - - config = ma_resource_manager_config_init(); - result = ma_resource_manager_init(&config, &resourceManager); - if (result != MA_SUCCESS) { - ma_device_uninit(&device); - printf("Failed to initialize the resource manager."); - return -1; - } - ``` - -You can configure the format, channels and sample rate of the decoded audio data. By default it -will use the file's native data format, but you can configure it to use a consistent format. This -is useful for offloading the cost of data conversion to load time rather than dynamically -converting at mixing time. To do this, you configure the decoded format, channels and sample rate -like the code below: - - ```c - config = ma_resource_manager_config_init(); - config.decodedFormat = device.playback.format; - config.decodedChannels = device.playback.channels; - config.decodedSampleRate = device.sampleRate; - ``` - -In the code above, the resource manager will be configured so that any decoded audio data will be -pre-converted at load time to the device's native data format. If instead you used defaults and -the data format of the file did not match the device's data format, you would need to convert the -data at mixing time which may be prohibitive in high-performance and large scale scenarios like -games. - -Internally the resource manager uses the `ma_decoder` API to load sounds. This means by default it -only supports decoders that are built into miniaudio. It's possible to support additional encoding -formats through the use of custom decoders. To do so, pass in your `ma_decoding_backend_vtable` -vtables into the resource manager config: - - ```c - ma_decoding_backend_vtable* pCustomBackendVTables[] = - { - &g_ma_decoding_backend_vtable_libvorbis, - &g_ma_decoding_backend_vtable_libopus - }; - - ... - - resourceManagerConfig.ppCustomDecodingBackendVTables = pCustomBackendVTables; - resourceManagerConfig.customDecodingBackendCount = sizeof(pCustomBackendVTables) / sizeof(pCustomBackendVTables[0]); - resourceManagerConfig.pCustomDecodingBackendUserData = NULL; - ``` - -This system can allow you to support any kind of file format. See the "Decoding" section for -details on how to implement custom decoders. The miniaudio repository includes examples for Opus -via libopus and libopusfile and Vorbis via libvorbis and libvorbisfile. - -Asynchronicity is achieved via a job system. When an operation needs to be performed, such as the -decoding of a page, a job will be posted to a queue which will then be processed by a job thread. -By default there will be only one job thread running, but this can be configured, like so: - - ```c - config = ma_resource_manager_config_init(); - config.jobThreadCount = MY_JOB_THREAD_COUNT; - ``` - -By default job threads are managed internally by the resource manager, however you can also self -manage your job threads if, for example, you want to integrate the job processing into your -existing job infrastructure, or if you simply don't like the way the resource manager does it. To -do this, just set the job thread count to 0 and process jobs manually. To process jobs, you first -need to retrieve a job using `ma_resource_manager_next_job()` and then process it using -`ma_job_process()`: - - ```c - config = ma_resource_manager_config_init(); - config.jobThreadCount = 0; // Don't manage any job threads internally. - config.flags = MA_RESOURCE_MANAGER_FLAG_NON_BLOCKING; // Optional. Makes `ma_resource_manager_next_job()` non-blocking. - - // ... Initialize your custom job threads ... - - void my_custom_job_thread(...) - { - for (;;) { - ma_job job; - ma_result result = ma_resource_manager_next_job(pMyResourceManager, &job); - if (result != MA_SUCCESS) { - if (result == MA_NO_DATA_AVAILABLE) { - // No jobs are available. Keep going. Will only get this if the resource manager was initialized - // with MA_RESOURCE_MANAGER_FLAG_NON_BLOCKING. - continue; - } else if (result == MA_CANCELLED) { - // MA_JOB_TYPE_QUIT was posted. Exit. - break; - } else { - // Some other error occurred. - break; - } - } - - ma_job_process(&job); - } - } - ``` - -In the example above, the `MA_JOB_TYPE_QUIT` event is the used as the termination -indicator, but you can use whatever you would like to terminate the thread. The call to -`ma_resource_manager_next_job()` is blocking by default, but can be configured to be non-blocking -by initializing the resource manager with the `MA_RESOURCE_MANAGER_FLAG_NON_BLOCKING` configuration -flag. Note that the `MA_JOB_TYPE_QUIT` will never be removed from the job queue. This -is to give every thread the opportunity to catch the event and terminate naturally. - -When loading a file, it's sometimes convenient to be able to customize how files are opened and -read instead of using standard `fopen()`, `fclose()`, etc. which is what miniaudio will use by -default. This can be done by setting `pVFS` member of the resource manager's config: - - ```c - // Initialize your custom VFS object. See documentation for VFS for information on how to do this. - my_custom_vfs vfs = my_custom_vfs_init(); - - config = ma_resource_manager_config_init(); - config.pVFS = &vfs; - ``` - -This is particularly useful in programs like games where you want to read straight from an archive -rather than the normal file system. If you do not specify a custom VFS, the resource manager will -use the operating system's normal file operations. - -To load a sound file and create a data source, call `ma_resource_manager_data_source_init()`. When -loading a sound you need to specify the file path and options for how the sounds should be loaded. -By default a sound will be loaded synchronously. The returned data source is owned by the caller -which means the caller is responsible for the allocation and freeing of the data source. Below is -an example for initializing a data source: - - ```c - ma_resource_manager_data_source dataSource; - ma_result result = ma_resource_manager_data_source_init(pResourceManager, pFilePath, flags, &dataSource); - if (result != MA_SUCCESS) { - // Error. - } - - // ... - - // A ma_resource_manager_data_source object is compatible with the `ma_data_source` API. To read data, just call - // the `ma_data_source_read_pcm_frames()` like you would with any normal data source. - result = ma_data_source_read_pcm_frames(&dataSource, pDecodedData, frameCount, &framesRead); - if (result != MA_SUCCESS) { - // Failed to read PCM frames. - } - - // ... - - ma_resource_manager_data_source_uninit(&dataSource); - ``` - -The `flags` parameter specifies how you want to perform loading of the sound file. It can be a -combination of the following flags: - - ``` - MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_STREAM - MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_DECODE - MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_ASYNC - MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_WAIT_INIT - MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_LOOPING - ``` - -When no flags are specified (set to 0), the sound will be fully loaded into memory, but not -decoded, meaning the raw file data will be stored in memory, and then dynamically decoded when -`ma_data_source_read_pcm_frames()` is called. To instead decode the audio data before storing it in -memory, use the `MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_DECODE` flag. By default, the sound file will -be loaded synchronously, meaning `ma_resource_manager_data_source_init()` will only return after -the entire file has been loaded. This is good for simplicity, but can be prohibitively slow. You -can instead load the sound asynchronously using the `MA_RESOURCE_MANAGER_DATA_SOURCE_ASYNC` flag. -This will result in `ma_resource_manager_data_source_init()` returning quickly, but no data will be -returned by `ma_data_source_read_pcm_frames()` until some data is available. When no data is -available because the asynchronous decoding hasn't caught up, `MA_BUSY` will be returned by -`ma_data_source_read_pcm_frames()`. - -For large sounds, it's often prohibitive to store the entire file in memory. To mitigate this, you -can instead stream audio data which you can do by specifying the -`MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_STREAM` flag. When streaming, data will be decoded in 1 -second pages. When a new page needs to be decoded, a job will be posted to the job queue and then -subsequently processed in a job thread. - -The `MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_LOOPING` flag can be used so that the sound will loop -when it reaches the end by default. It's recommended you use this flag when you want to have a -looping streaming sound. If you try loading a very short sound as a stream, you will get a glitch. -This is because the resource manager needs to pre-fill the initial buffer at initialization time, -and if you don't specify the `MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_LOOPING` flag, the resource -manager will assume the sound is not looping and will stop filling the buffer when it reaches the -end, therefore resulting in a discontinuous buffer. - -For in-memory sounds, reference counting is used to ensure the data is loaded only once. This means -multiple calls to `ma_resource_manager_data_source_init()` with the same file path will result in -the file data only being loaded once. Each call to `ma_resource_manager_data_source_init()` must be -matched up with a call to `ma_resource_manager_data_source_uninit()`. Sometimes it can be useful -for a program to register self-managed raw audio data and associate it with a file path. Use the -`ma_resource_manager_register_*()` and `ma_resource_manager_unregister_*()` APIs to do this. -`ma_resource_manager_register_decoded_data()` is used to associate a pointer to raw, self-managed -decoded audio data in the specified data format with the specified name. Likewise, -`ma_resource_manager_register_encoded_data()` is used to associate a pointer to raw self-managed -encoded audio data (the raw file data) with the specified name. Note that these names need not be -actual file paths. When `ma_resource_manager_data_source_init()` is called (without the -`MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_STREAM` flag), the resource manager will look for these -explicitly registered data buffers and, if found, will use it as the backing data for the data -source. Note that the resource manager does *not* make a copy of this data so it is up to the -caller to ensure the pointer stays valid for its lifetime. Use -`ma_resource_manager_unregister_data()` to unregister the self-managed data. You can also use -`ma_resource_manager_register_file()` and `ma_resource_manager_unregister_file()` to register and -unregister a file. It does not make sense to use the `MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_STREAM` -flag with a self-managed data pointer. - - -6.1. Asynchronous Loading and Synchronization ---------------------------------------------- -When loading asynchronously, it can be useful to poll whether or not loading has finished. Use -`ma_resource_manager_data_source_result()` to determine this. For in-memory sounds, this will -return `MA_SUCCESS` when the file has been *entirely* decoded. If the sound is still being decoded, -`MA_BUSY` will be returned. Otherwise, some other error code will be returned if the sound failed -to load. For streaming data sources, `MA_SUCCESS` will be returned when the first page has been -decoded and the sound is ready to be played. If the first page is still being decoded, `MA_BUSY` -will be returned. Otherwise, some other error code will be returned if the sound failed to load. - -In addition to polling, you can also use a simple synchronization object called a "fence" to wait -for asynchronously loaded sounds to finish. This is called `ma_fence`. The advantage to using a -fence is that it can be used to wait for a group of sounds to finish loading rather than waiting -for sounds on an individual basis. There are two stages to loading a sound: - - * Initialization of the internal decoder; and - * Completion of decoding of the file (the file is fully decoded) - -You can specify separate fences for each of the different stages. Waiting for the initialization -of the internal decoder is important for when you need to know the sample format, channels and -sample rate of the file. - -The example below shows how you could use a fence when loading a number of sounds: - - ```c - // This fence will be released when all sounds are finished loading entirely. - ma_fence fence; - ma_fence_init(&fence); - - // This will be passed into the initialization routine for each sound. - ma_resource_manager_pipeline_notifications notifications = ma_resource_manager_pipeline_notifications_init(); - notifications.done.pFence = &fence; - - // Now load a bunch of sounds: - for (iSound = 0; iSound < soundCount; iSound += 1) { - ma_resource_manager_data_source_init(pResourceManager, pSoundFilePaths[iSound], flags, ¬ifications, &pSoundSources[iSound]); - } - - // ... DO SOMETHING ELSE WHILE SOUNDS ARE LOADING ... - - // Wait for loading of sounds to finish. - ma_fence_wait(&fence); - ``` - -In the example above we used a fence for waiting until the entire file has been fully decoded. If -you only need to wait for the initialization of the internal decoder to complete, you can use the -`init` member of the `ma_resource_manager_pipeline_notifications` object: - - ```c - notifications.init.pFence = &fence; - ``` - -If a fence is not appropriate for your situation, you can instead use a callback that is fired on -an individual sound basis. This is done in a very similar way to fences: - - ```c - typedef struct - { - ma_async_notification_callbacks cb; - void* pMyData; - } my_notification; - - void my_notification_callback(ma_async_notification* pNotification) - { - my_notification* pMyNotification = (my_notification*)pNotification; - - // Do something in response to the sound finishing loading. - } - - ... - - my_notification myCallback; - myCallback.cb.onSignal = my_notification_callback; - myCallback.pMyData = pMyData; - - ma_resource_manager_pipeline_notifications notifications = ma_resource_manager_pipeline_notifications_init(); - notifications.done.pNotification = &myCallback; - - ma_resource_manager_data_source_init(pResourceManager, "my_sound.wav", flags, ¬ifications, &mySound); - ``` - -In the example above we just extend the `ma_async_notification_callbacks` object and pass an -instantiation into the `ma_resource_manager_pipeline_notifications` in the same way as we did with -the fence, only we set `pNotification` instead of `pFence`. You can set both of these at the same -time and they should both work as expected. If using the `pNotification` system, you need to ensure -your `ma_async_notification_callbacks` object stays valid. - - - -6.2. Resource Manager Implementation Details --------------------------------------------- -Resources are managed in two main ways: - - * By storing the entire sound inside an in-memory buffer (referred to as a data buffer) - * By streaming audio data on the fly (referred to as a data stream) - -A resource managed data source (`ma_resource_manager_data_source`) encapsulates a data buffer or -data stream, depending on whether or not the data source was initialized with the -`MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_STREAM` flag. If so, it will make use of a -`ma_resource_manager_data_stream` object. Otherwise it will use a `ma_resource_manager_data_buffer` -object. Both of these objects are data sources which means they can be used with any -`ma_data_source_*()` API. - -Another major feature of the resource manager is the ability to asynchronously decode audio files. -This relieves the audio thread of time-consuming decoding which can negatively affect scalability -due to the audio thread needing to complete it's work extremely quickly to avoid glitching. -Asynchronous decoding is achieved through a job system. There is a central multi-producer, -multi-consumer, fixed-capacity job queue. When some asynchronous work needs to be done, a job is -posted to the queue which is then read by a job thread. The number of job threads can be -configured for improved scalability, and job threads can all run in parallel without needing to -worry about the order of execution (how this is achieved is explained below). - -When a sound is being loaded asynchronously, playback can begin before the sound has been fully -decoded. This enables the application to start playback of the sound quickly, while at the same -time allowing to resource manager to keep loading in the background. Since there may be less -threads than the number of sounds being loaded at a given time, a simple scheduling system is used -to keep decoding time balanced and fair. The resource manager solves this by splitting decoding -into chunks called pages. By default, each page is 1 second long. When a page has been decoded, a -new job will be posted to start decoding the next page. By dividing up decoding into pages, an -individual sound shouldn't ever delay every other sound from having their first page decoded. Of -course, when loading many sounds at the same time, there will always be an amount of time required -to process jobs in the queue so in heavy load situations there will still be some delay. To -determine if a data source is ready to have some frames read, use -`ma_resource_manager_data_source_get_available_frames()`. This will return the number of frames -available starting from the current position. - - -6.2.1. Job Queue ----------------- -The resource manager uses a job queue which is multi-producer, multi-consumer, and fixed-capacity. -This job queue is not currently lock-free, and instead uses a spinlock to achieve thread-safety. -Only a fixed number of jobs can be allocated and inserted into the queue which is done through a -lock-free data structure for allocating an index into a fixed sized array, with reference counting -for mitigation of the ABA problem. The reference count is 32-bit. - -For many types of jobs it's important that they execute in a specific order. In these cases, jobs -are executed serially. For the resource manager, serial execution of jobs is only required on a -per-object basis (per data buffer or per data stream). Each of these objects stores an execution -counter. When a job is posted it is associated with an execution counter. When the job is -processed, it checks if the execution counter of the job equals the execution counter of the -owning object and if so, processes the job. If the counters are not equal, the job will be posted -back onto the job queue for later processing. When the job finishes processing the execution order -of the main object is incremented. This system means the no matter how many job threads are -executing, decoding of an individual sound will always get processed serially. The advantage to -having multiple threads comes into play when loading multiple sounds at the same time. - -The resource manager's job queue is not 100% lock-free and will use a spinlock to achieve -thread-safety for a very small section of code. This is only relevant when the resource manager -uses more than one job thread. If only using a single job thread, which is the default, the -lock should never actually wait in practice. The amount of time spent locking should be quite -short, but it's something to be aware of for those who have pedantic lock-free requirements and -need to use more than one job thread. There are plans to remove this lock in a future version. - -In addition, posting a job will release a semaphore, which on Win32 is implemented with -`ReleaseSemaphore` and on POSIX platforms via a condition variable: - - ```c - pthread_mutex_lock(&pSemaphore->lock); - { - pSemaphore->value += 1; - pthread_cond_signal(&pSemaphore->cond); - } - pthread_mutex_unlock(&pSemaphore->lock); - ``` - -Again, this is relevant for those with strict lock-free requirements in the audio thread. To avoid -this, you can use non-blocking mode (via the `MA_JOB_QUEUE_FLAG_NON_BLOCKING` -flag) and implement your own job processing routine (see the "Resource Manager" section above for -details on how to do this). - - - -6.2.2. Data Buffers -------------------- -When the `MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_STREAM` flag is excluded at initialization time, the -resource manager will try to load the data into an in-memory data buffer. Before doing so, however, -it will first check if the specified file is already loaded. If so, it will increment a reference -counter and just use the already loaded data. This saves both time and memory. When the data buffer -is uninitialized, the reference counter will be decremented. If the counter hits zero, the file -will be unloaded. This is a detail to keep in mind because it could result in excessive loading and -unloading of a sound. For example, the following sequence will result in a file be loaded twice, -once after the other: - - ```c - ma_resource_manager_data_source_init(pResourceManager, "my_file", ..., &myDataBuffer0); // Refcount = 1. Initial load. - ma_resource_manager_data_source_uninit(&myDataBuffer0); // Refcount = 0. Unloaded. - - ma_resource_manager_data_source_init(pResourceManager, "my_file", ..., &myDataBuffer1); // Refcount = 1. Reloaded because previous uninit() unloaded it. - ma_resource_manager_data_source_uninit(&myDataBuffer1); // Refcount = 0. Unloaded. - ``` - -A binary search tree (BST) is used for storing data buffers as it has good balance between -efficiency and simplicity. The key of the BST is a 64-bit hash of the file path that was passed -into `ma_resource_manager_data_source_init()`. The advantage of using a hash is that it saves -memory over storing the entire path, has faster comparisons, and results in a mostly balanced BST -due to the random nature of the hash. The disadvantages are that file names are case-sensitive and -there's a small chance of name collisions. If case-sensitivity is an issue, you should normalize -your file names to upper- or lower-case before initializing your data sources. If name collisions -become an issue, you'll need to change the name of one of the colliding names or just not use the -resource manager. - -When a sound file has not already been loaded and the `MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_ASYNC` -flag is excluded, the file will be decoded synchronously by the calling thread. There are two -options for controlling how the audio is stored in the data buffer - encoded or decoded. When the -`MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_DECODE` option is excluded, the raw file data will be stored -in memory. Otherwise the sound will be decoded before storing it in memory. Synchronous loading is -a very simple and standard process of simply adding an item to the BST, allocating a block of -memory and then decoding (if `MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_DECODE` is specified). - -When the `MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_ASYNC` flag is specified, loading of the data buffer -is done asynchronously. In this case, a job is posted to the queue to start loading and then the -function immediately returns, setting an internal result code to `MA_BUSY`. This result code is -returned when the program calls `ma_resource_manager_data_source_result()`. When decoding has fully -completed `MA_SUCCESS` will be returned. This can be used to know if loading has fully completed. - -When loading asynchronously, a single job is posted to the queue of the type -`MA_JOB_TYPE_RESOURCE_MANAGER_LOAD_DATA_BUFFER_NODE`. This involves making a copy of the file path and -associating it with job. When the job is processed by the job thread, it will first load the file -using the VFS associated with the resource manager. When using a custom VFS, it's important that it -be completely thread-safe because it will be used from one or more job threads at the same time. -Individual files should only ever be accessed by one thread at a time, however. After opening the -file via the VFS, the job will determine whether or not the file is being decoded. If not, it -simply allocates a block of memory and loads the raw file contents into it and returns. On the -other hand, when the file is being decoded, it will first allocate a decoder on the heap and -initialize it. Then it will check if the length of the file is known. If so it will allocate a -block of memory to store the decoded output and initialize it to silence. If the size is unknown, -it will allocate room for one page. After memory has been allocated, the first page will be -decoded. If the sound is shorter than a page, the result code will be set to `MA_SUCCESS` and the -completion event will be signalled and loading is now complete. If, however, there is more to -decode, a job with the code `MA_JOB_TYPE_RESOURCE_MANAGER_PAGE_DATA_BUFFER_NODE` is posted. This job -will decode the next page and perform the same process if it reaches the end. If there is more to -decode, the job will post another `MA_JOB_TYPE_RESOURCE_MANAGER_PAGE_DATA_BUFFER_NODE` job which will -keep on happening until the sound has been fully decoded. For sounds of an unknown length, each -page will be linked together as a linked list. Internally this is implemented via the -`ma_paged_audio_buffer` object. - - -6.2.3. Data Streams -------------------- -Data streams only ever store two pages worth of data for each instance. They are most useful for -large sounds like music tracks in games that would consume too much memory if fully decoded in -memory. After every frame from a page has been read, a job will be posted to load the next page -which is done from the VFS. - -For data streams, the `MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_ASYNC` flag will determine whether or -not initialization of the data source waits until the two pages have been decoded. When unset, -`ma_resource_manager_data_source_init()` will wait until the two pages have been loaded, otherwise -it will return immediately. - -When frames are read from a data stream using `ma_resource_manager_data_source_read_pcm_frames()`, -`MA_BUSY` will be returned if there are no frames available. If there are some frames available, -but less than the number requested, `MA_SUCCESS` will be returned, but the actual number of frames -read will be less than the number requested. Due to the asynchronous nature of data streams, -seeking is also asynchronous. If the data stream is in the middle of a seek, `MA_BUSY` will be -returned when trying to read frames. - -When `ma_resource_manager_data_source_read_pcm_frames()` results in a page getting fully consumed -a job is posted to load the next page. This will be posted from the same thread that called -`ma_resource_manager_data_source_read_pcm_frames()`. - -Data streams are uninitialized by posting a job to the queue, but the function won't return until -that job has been processed. The reason for this is that the caller owns the data stream object and -therefore miniaudio needs to ensure everything completes before handing back control to the caller. -Also, if the data stream is uninitialized while pages are in the middle of decoding, they must -complete before destroying any underlying object and the job system handles this cleanly. - -Note that when a new page needs to be loaded, a job will be posted to the resource manager's job -thread from the audio thread. You must keep in mind the details mentioned in the "Job Queue" -section above regarding locking when posting an event if you require a strictly lock-free audio -thread. - - - -7. Node Graph -============= -miniaudio's routing infrastructure follows a node graph paradigm. The idea is that you create a -node whose outputs are attached to inputs of another node, thereby creating a graph. There are -different types of nodes, with each node in the graph processing input data to produce output, -which is then fed through the chain. Each node in the graph can apply their own custom effects. At -the start of the graph will usually be one or more data source nodes which have no inputs and -instead pull their data from a data source. At the end of the graph is an endpoint which represents -the end of the chain and is where the final output is ultimately extracted from. - -Each node has a number of input buses and a number of output buses. An output bus from a node is -attached to an input bus of another. Multiple nodes can connect their output buses to another -node's input bus, in which case their outputs will be mixed before processing by the node. Below is -a diagram that illustrates a hypothetical node graph setup: - - ``` - >>>>>>>>>>>>>>>>>>>>>>>>>>>>>> Data flows left to right >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> - - +---------------+ +-----------------+ - | Data Source 1 =----+ +----------+ +----= Low Pass Filter =----+ - +---------------+ | | =----+ +-----------------+ | +----------+ - +----= Splitter | +----= ENDPOINT | - +---------------+ | | =----+ +-----------------+ | +----------+ - | Data Source 2 =----+ +----------+ +----= Echo / Delay =----+ - +---------------+ +-----------------+ - ``` - -In the above graph, it starts with two data sources whose outputs are attached to the input of a -splitter node. It's at this point that the two data sources are mixed. After mixing, the splitter -performs it's processing routine and produces two outputs which is simply a duplication of the -input stream. One output is attached to a low pass filter, whereas the other output is attached to -a echo/delay. The outputs of the low pass filter and the echo are attached to the endpoint, and -since they're both connected to the same input bus, they'll be mixed. - -Each input bus must be configured to accept the same number of channels, but the number of channels -used by input buses can be different to the number of channels for output buses in which case -miniaudio will automatically convert the input data to the output channel count before processing. -The number of channels of an output bus of one node must match the channel count of the input bus -it's attached to. The channel counts cannot be changed after the node has been initialized. If you -attempt to attach an output bus to an input bus with a different channel count, attachment will -fail. - -To use a node graph, you first need to initialize a `ma_node_graph` object. This is essentially a -container around the entire graph. The `ma_node_graph` object is required for some thread-safety -issues which will be explained later. A `ma_node_graph` object is initialized using miniaudio's -standard config/init system: - - ```c - ma_node_graph_config nodeGraphConfig = ma_node_graph_config_init(myChannelCount); - - result = ma_node_graph_init(&nodeGraphConfig, NULL, &nodeGraph); // Second parameter is a pointer to allocation callbacks. - if (result != MA_SUCCESS) { - // Failed to initialize node graph. - } - ``` - -When you initialize the node graph, you're specifying the channel count of the endpoint. The -endpoint is a special node which has one input bus and one output bus, both of which have the -same channel count, which is specified in the config. Any nodes that connect directly to the -endpoint must be configured such that their output buses have the same channel count. When you read -audio data from the node graph, it'll have the channel count you specified in the config. To read -data from the graph: - - ```c - ma_uint32 framesRead; - result = ma_node_graph_read_pcm_frames(&nodeGraph, pFramesOut, frameCount, &framesRead); - if (result != MA_SUCCESS) { - // Failed to read data from the node graph. - } - ``` - -When you read audio data, miniaudio starts at the node graph's endpoint node which then pulls in -data from its input attachments, which in turn recursively pull in data from their inputs, and so -on. At the start of the graph there will be some kind of data source node which will have zero -inputs and will instead read directly from a data source. The base nodes don't literally need to -read from a `ma_data_source` object, but they will always have some kind of underlying object that -sources some kind of audio. The `ma_data_source_node` node can be used to read from a -`ma_data_source`. Data is always in floating-point format and in the number of channels you -specified when the graph was initialized. The sample rate is defined by the underlying data sources. -It's up to you to ensure they use a consistent and appropriate sample rate. - -The `ma_node` API is designed to allow custom nodes to be implemented with relative ease, but -miniaudio includes a few stock nodes for common functionality. This is how you would initialize a -node which reads directly from a data source (`ma_data_source_node`) which is an example of one -of the stock nodes that comes with miniaudio: - - ```c - ma_data_source_node_config config = ma_data_source_node_config_init(pMyDataSource); - - ma_data_source_node dataSourceNode; - result = ma_data_source_node_init(&nodeGraph, &config, NULL, &dataSourceNode); - if (result != MA_SUCCESS) { - // Failed to create data source node. - } - ``` - -The data source node will use the output channel count to determine the channel count of the output -bus. There will be 1 output bus and 0 input buses (data will be drawn directly from the data -source). The data source must output to floating-point (`ma_format_f32`) or else an error will be -returned from `ma_data_source_node_init()`. - -By default the node will not be attached to the graph. To do so, use `ma_node_attach_output_bus()`: - - ```c - result = ma_node_attach_output_bus(&dataSourceNode, 0, ma_node_graph_get_endpoint(&nodeGraph), 0); - if (result != MA_SUCCESS) { - // Failed to attach node. - } - ``` - -The code above connects the data source node directly to the endpoint. Since the data source node -has only a single output bus, the index will always be 0. Likewise, the endpoint only has a single -input bus which means the input bus index will also always be 0. - -To detach a specific output bus, use `ma_node_detach_output_bus()`. To detach all output buses, use -`ma_node_detach_all_output_buses()`. If you want to just move the output bus from one attachment to -another, you do not need to detach first. You can just call `ma_node_attach_output_bus()` and it'll -deal with it for you. - -Less frequently you may want to create a specialized node. This will be a node where you implement -your own processing callback to apply a custom effect of some kind. This is similar to initializing -one of the stock node types, only this time you need to specify a pointer to a vtable containing a -pointer to the processing function and the number of input and output buses. Example: - - ```c - static void my_custom_node_process_pcm_frames(ma_node* pNode, const float** ppFramesIn, ma_uint32* pFrameCountIn, float** ppFramesOut, ma_uint32* pFrameCountOut) - { - // Do some processing of ppFramesIn (one stream of audio data per input bus) - const float* pFramesIn_0 = ppFramesIn[0]; // Input bus @ index 0. - const float* pFramesIn_1 = ppFramesIn[1]; // Input bus @ index 1. - float* pFramesOut_0 = ppFramesOut[0]; // Output bus @ index 0. - - // Do some processing. On input, `pFrameCountIn` will be the number of input frames in each - // buffer in `ppFramesIn` and `pFrameCountOut` will be the capacity of each of the buffers - // in `ppFramesOut`. On output, `pFrameCountIn` should be set to the number of input frames - // your node consumed and `pFrameCountOut` should be set the number of output frames that - // were produced. - // - // You should process as many frames as you can. If your effect consumes input frames at the - // same rate as output frames (always the case, unless you're doing resampling), you need - // only look at `ppFramesOut` and process that exact number of frames. If you're doing - // resampling, you'll need to be sure to set both `pFrameCountIn` and `pFrameCountOut` - // properly. - } - - static ma_node_vtable my_custom_node_vtable = - { - my_custom_node_process_pcm_frames, // The function that will be called to process your custom node. This is where you'd implement your effect processing. - NULL, // Optional. A callback for calculating the number of input frames that are required to process a specified number of output frames. - 2, // 2 input buses. - 1, // 1 output bus. - 0 // Default flags. - }; - - ... - - // Each bus needs to have a channel count specified. To do this you need to specify the channel - // counts in an array and then pass that into the node config. - ma_uint32 inputChannels[2]; // Equal in size to the number of input channels specified in the vtable. - ma_uint32 outputChannels[1]; // Equal in size to the number of output channels specified in the vtable. - - inputChannels[0] = channelsIn; - inputChannels[1] = channelsIn; - outputChannels[0] = channelsOut; - - ma_node_config nodeConfig = ma_node_config_init(); - nodeConfig.vtable = &my_custom_node_vtable; - nodeConfig.pInputChannels = inputChannels; - nodeConfig.pOutputChannels = outputChannels; - - ma_node_base node; - result = ma_node_init(&nodeGraph, &nodeConfig, NULL, &node); - if (result != MA_SUCCESS) { - // Failed to initialize node. - } - ``` - -When initializing a custom node, as in the code above, you'll normally just place your vtable in -static space. The number of input and output buses are specified as part of the vtable. If you need -a variable number of buses on a per-node bases, the vtable should have the relevant bus count set -to `MA_NODE_BUS_COUNT_UNKNOWN`. In this case, the bus count should be set in the node config: - - ```c - static ma_node_vtable my_custom_node_vtable = - { - my_custom_node_process_pcm_frames, // The function that will be called process your custom node. This is where you'd implement your effect processing. - NULL, // Optional. A callback for calculating the number of input frames that are required to process a specified number of output frames. - MA_NODE_BUS_COUNT_UNKNOWN, // The number of input buses is determined on a per-node basis. - 1, // 1 output bus. - 0 // Default flags. - }; - - ... - - ma_node_config nodeConfig = ma_node_config_init(); - nodeConfig.vtable = &my_custom_node_vtable; - nodeConfig.inputBusCount = myBusCount; // <-- Since the vtable specifies MA_NODE_BUS_COUNT_UNKNOWN, the input bus count should be set here. - nodeConfig.pInputChannels = inputChannels; // <-- Make sure there are nodeConfig.inputBusCount elements in this array. - nodeConfig.pOutputChannels = outputChannels; // <-- The vtable specifies 1 output bus, so there must be 1 element in this array. - ``` - -In the above example it's important to never set the `inputBusCount` and `outputBusCount` members -to anything other than their defaults if the vtable specifies an explicit count. They can only be -set if the vtable specifies MA_NODE_BUS_COUNT_UNKNOWN in the relevant bus count. - -Most often you'll want to create a structure to encapsulate your node with some extra data. You -need to make sure the `ma_node_base` object is your first member of the structure: - - ```c - typedef struct - { - ma_node_base base; // <-- Make sure this is always the first member. - float someCustomData; - } my_custom_node; - ``` - -By doing this, your object will be compatible with all `ma_node` APIs and you can attach it to the -graph just like any other node. - -In the custom processing callback (`my_custom_node_process_pcm_frames()` in the example above), the -number of channels for each bus is what was specified by the config when the node was initialized -with `ma_node_init()`. In addition, all attachments to each of the input buses will have been -pre-mixed by miniaudio. The config allows you to specify different channel counts for each -individual input and output bus. It's up to the effect to handle it appropriate, and if it can't, -return an error in it's initialization routine. - -Custom nodes can be assigned some flags to describe their behaviour. These are set via the vtable -and include the following: - - +-----------------------------------------+---------------------------------------------------+ - | Flag Name | Description | - +-----------------------------------------+---------------------------------------------------+ - | MA_NODE_FLAG_PASSTHROUGH | Useful for nodes that do not do any kind of audio | - | | processing, but are instead used for tracking | - | | time, handling events, etc. Also used by the | - | | internal endpoint node. It reads directly from | - | | the input bus to the output bus. Nodes with this | - | | flag must have exactly 1 input bus and 1 output | - | | bus, and both buses must have the same channel | - | | counts. | - +-----------------------------------------+---------------------------------------------------+ - | MA_NODE_FLAG_CONTINUOUS_PROCESSING | Causes the processing callback to be called even | - | | when no data is available to be read from input | - | | attachments. When a node has at least one input | - | | bus, but there are no inputs attached or the | - | | inputs do not deliver any data, the node's | - | | processing callback will not get fired. This flag | - | | will make it so the callback is always fired | - | | regardless of whether or not any input data is | - | | received. This is useful for effects like | - | | echos where there will be a tail of audio data | - | | that still needs to be processed even when the | - | | original data sources have reached their ends. It | - | | may also be useful for nodes that must always | - | | have their processing callback fired when there | - | | are no inputs attached. | - +-----------------------------------------+---------------------------------------------------+ - | MA_NODE_FLAG_ALLOW_NULL_INPUT | Used in conjunction with | - | | `MA_NODE_FLAG_CONTINUOUS_PROCESSING`. When this | - | | is set, the `ppFramesIn` parameter of the | - | | processing callback will be set to NULL when | - | | there are no input frames are available. When | - | | this is unset, silence will be posted to the | - | | processing callback. | - +-----------------------------------------+---------------------------------------------------+ - | MA_NODE_FLAG_DIFFERENT_PROCESSING_RATES | Used to tell miniaudio that input and output | - | | frames are processed at different rates. You | - | | should set this for any nodes that perform | - | | resampling. | - +-----------------------------------------+---------------------------------------------------+ - | MA_NODE_FLAG_SILENT_OUTPUT | Used to tell miniaudio that a node produces only | - | | silent output. This is useful for nodes where you | - | | don't want the output to contribute to the final | - | | mix. An example might be if you want split your | - | | stream and have one branch be output to a file. | - | | When using this flag, you should avoid writing to | - | | the output buffer of the node's processing | - | | callback because miniaudio will ignore it anyway. | - +-----------------------------------------+---------------------------------------------------+ - - -If you need to make a copy of an audio stream for effect processing you can use a splitter node -called `ma_splitter_node`. This takes has 1 input bus and splits the stream into 2 output buses. -You can use it like this: - - ```c - ma_splitter_node_config splitterNodeConfig = ma_splitter_node_config_init(channels); - - ma_splitter_node splitterNode; - result = ma_splitter_node_init(&nodeGraph, &splitterNodeConfig, NULL, &splitterNode); - if (result != MA_SUCCESS) { - // Failed to create node. - } - - // Attach your output buses to two different input buses (can be on two different nodes). - ma_node_attach_output_bus(&splitterNode, 0, ma_node_graph_get_endpoint(&nodeGraph), 0); // Attach directly to the endpoint. - ma_node_attach_output_bus(&splitterNode, 1, &myEffectNode, 0); // Attach to input bus 0 of some effect node. - ``` - -The volume of an output bus can be configured on a per-bus basis: - - ```c - ma_node_set_output_bus_volume(&splitterNode, 0, 0.5f); - ma_node_set_output_bus_volume(&splitterNode, 1, 0.5f); - ``` - -In the code above we're using the splitter node from before and changing the volume of each of the -copied streams. - -You can start and stop a node with the following: - - ```c - ma_node_set_state(&splitterNode, ma_node_state_started); // The default state. - ma_node_set_state(&splitterNode, ma_node_state_stopped); - ``` - -By default the node is in a started state, but since it won't be connected to anything won't -actually be invoked by the node graph until it's connected. When you stop a node, data will not be -read from any of its input connections. You can use this property to stop a group of sounds -atomically. - -You can configure the initial state of a node in it's config: - - ```c - nodeConfig.initialState = ma_node_state_stopped; - ``` - -Note that for the stock specialized nodes, all of their configs will have a `nodeConfig` member -which is the config to use with the base node. This is where the initial state can be configured -for specialized nodes: - - ```c - dataSourceNodeConfig.nodeConfig.initialState = ma_node_state_stopped; - ``` - -When using a specialized node like `ma_data_source_node` or `ma_splitter_node`, be sure to not -modify the `vtable` member of the `nodeConfig` object. - - -7.1. Timing ------------ -The node graph supports starting and stopping nodes at scheduled times. This is especially useful -for data source nodes where you want to get the node set up, but only start playback at a specific -time. There are two clocks: local and global. - -A local clock is per-node, whereas the global clock is per graph. Scheduling starts and stops can -only be done based on the global clock because the local clock will not be running while the node -is stopped. The global clocks advances whenever `ma_node_graph_read_pcm_frames()` is called. On the -other hand, the local clock only advances when the node's processing callback is fired, and is -advanced based on the output frame count. - -To retrieve the global time, use `ma_node_graph_get_time()`. The global time can be set with -`ma_node_graph_set_time()` which might be useful if you want to do seeking on a global timeline. -Getting and setting the local time is similar. Use `ma_node_get_time()` to retrieve the local time, -and `ma_node_set_time()` to set the local time. The global and local times will be advanced by the -audio thread, so care should be taken to avoid data races. Ideally you should avoid calling these -outside of the node processing callbacks which are always run on the audio thread. - -There is basic support for scheduling the starting and stopping of nodes. You can only schedule one -start and one stop at a time. This is mainly intended for putting nodes into a started or stopped -state in a frame-exact manner. Without this mechanism, starting and stopping of a node is limited -to the resolution of a call to `ma_node_graph_read_pcm_frames()` which would typically be in blocks -of several milliseconds. The following APIs can be used for scheduling node states: - - ```c - ma_node_set_state_time() - ma_node_get_state_time() - ``` - -The time is absolute and must be based on the global clock. An example is below: - - ```c - ma_node_set_state_time(&myNode, ma_node_state_started, sampleRate*1); // Delay starting to 1 second. - ma_node_set_state_time(&myNode, ma_node_state_stopped, sampleRate*5); // Delay stopping to 5 seconds. - ``` - -An example for changing the state using a relative time. - - ```c - ma_node_set_state_time(&myNode, ma_node_state_started, sampleRate*1 + ma_node_graph_get_time(&myNodeGraph)); - ma_node_set_state_time(&myNode, ma_node_state_stopped, sampleRate*5 + ma_node_graph_get_time(&myNodeGraph)); - ``` - -Note that due to the nature of multi-threading the times may not be 100% exact. If this is an -issue, consider scheduling state changes from within a processing callback. An idea might be to -have some kind of passthrough trigger node that is used specifically for tracking time and handling -events. - - - -7.2. Thread Safety and Locking ------------------------------- -When processing audio, it's ideal not to have any kind of locking in the audio thread. Since it's -expected that `ma_node_graph_read_pcm_frames()` would be run on the audio thread, it does so -without the use of any locks. This section discusses the implementation used by miniaudio and goes -over some of the compromises employed by miniaudio to achieve this goal. Note that the current -implementation may not be ideal - feedback and critiques are most welcome. - -The node graph API is not *entirely* lock-free. Only `ma_node_graph_read_pcm_frames()` is expected -to be lock-free. Attachment, detachment and uninitialization of nodes use locks to simplify the -implementation, but are crafted in a way such that such locking is not required when reading audio -data from the graph. Locking in these areas are achieved by means of spinlocks. - -The main complication with keeping `ma_node_graph_read_pcm_frames()` lock-free stems from the fact -that a node can be uninitialized, and it's memory potentially freed, while in the middle of being -processed on the audio thread. There are times when the audio thread will be referencing a node, -which means the uninitialization process of a node needs to make sure it delays returning until the -audio thread is finished so that control is not handed back to the caller thereby giving them a -chance to free the node's memory. - -When the audio thread is processing a node, it does so by reading from each of the output buses of -the node. In order for a node to process data for one of its output buses, it needs to read from -each of its input buses, and so on an so forth. It follows that once all output buses of a node -are detached, the node as a whole will be disconnected and no further processing will occur unless -it's output buses are reattached, which won't be happening when the node is being uninitialized. -By having `ma_node_detach_output_bus()` wait until the audio thread is finished with it, we can -simplify a few things, at the expense of making `ma_node_detach_output_bus()` a bit slower. By -doing this, the implementation of `ma_node_uninit()` becomes trivial - just detach all output -nodes, followed by each of the attachments to each of its input nodes, and then do any final clean -up. - -With the above design, the worst-case scenario is `ma_node_detach_output_bus()` taking as long as -it takes to process the output bus being detached. This will happen if it's called at just the -wrong moment where the audio thread has just iterated it and has just started processing. The -caller of `ma_node_detach_output_bus()` will stall until the audio thread is finished, which -includes the cost of recursively processing its inputs. This is the biggest compromise made with -the approach taken by miniaudio for its lock-free processing system. The cost of detaching nodes -earlier in the pipeline (data sources, for example) will be cheaper than the cost of detaching -higher level nodes, such as some kind of final post-processing endpoint. If you need to do mass -detachments, detach starting from the lowest level nodes and work your way towards the final -endpoint node (but don't try detaching the node graph's endpoint). If the audio thread is not -running, detachment will be fast and detachment in any order will be the same. The reason nodes -need to wait for their input attachments to complete is due to the potential for desyncs between -data sources. If the node was to terminate processing mid way through processing its inputs, -there's a chance that some of the underlying data sources will have been read, but then others not. -That will then result in a potential desynchronization when detaching and reattaching higher-level -nodes. A possible solution to this is to have an option when detaching to terminate processing -before processing all input attachments which should be fairly simple. - -Another compromise, albeit less significant, is locking when attaching and detaching nodes. This -locking is achieved by means of a spinlock in order to reduce memory overhead. A lock is present -for each input bus and output bus. When an output bus is connected to an input bus, both the output -bus and input bus is locked. This locking is specifically for attaching and detaching across -different threads and does not affect `ma_node_graph_read_pcm_frames()` in any way. The locking and -unlocking is mostly self-explanatory, but a slightly less intuitive aspect comes into it when -considering that iterating over attachments must not break as a result of attaching or detaching a -node while iteration is occurring. - -Attaching and detaching are both quite simple. When an output bus of a node is attached to an input -bus of another node, it's added to a linked list. Basically, an input bus is a linked list, where -each item in the list is and output bus. We have some intentional (and convenient) restrictions on -what can done with the linked list in order to simplify the implementation. First of all, whenever -something needs to iterate over the list, it must do so in a forward direction. Backwards iteration -is not supported. Also, items can only be added to the start of the list. - -The linked list is a doubly-linked list where each item in the list (an output bus) holds a pointer -to the next item in the list, and another to the previous item. A pointer to the previous item is -only required for fast detachment of the node - it is never used in iteration. This is an -important property because it means from the perspective of iteration, attaching and detaching of -an item can be done with a single atomic assignment. This is exploited by both the attachment and -detachment process. When attaching the node, the first thing that is done is the setting of the -local "next" and "previous" pointers of the node. After that, the item is "attached" to the list -by simply performing an atomic exchange with the head pointer. After that, the node is "attached" -to the list from the perspective of iteration. Even though the "previous" pointer of the next item -hasn't yet been set, from the perspective of iteration it's been attached because iteration will -only be happening in a forward direction which means the "previous" pointer won't actually ever get -used. The same general process applies to detachment. See `ma_node_attach_output_bus()` and -`ma_node_detach_output_bus()` for the implementation of this mechanism. - - - -8. Decoding -=========== -The `ma_decoder` API is used for reading audio files. Decoders are completely decoupled from -devices and can be used independently. Built-in support is included for the following formats: - - +---------+ - | Format | - +---------+ - | WAV | - | MP3 | - | FLAC | - +---------+ - -You can disable the built-in decoders by specifying one or more of the following options before the -miniaudio implementation: - - ```c - #define MA_NO_WAV - #define MA_NO_MP3 - #define MA_NO_FLAC - ``` - -miniaudio supports the ability to plug in custom decoders. See the section below for details on how -to use custom decoders. - -A decoder can be initialized from a file with `ma_decoder_init_file()`, a block of memory with -`ma_decoder_init_memory()`, or from data delivered via callbacks with `ma_decoder_init()`. Here is -an example for loading a decoder from a file: - - ```c - ma_decoder decoder; - ma_result result = ma_decoder_init_file("MySong.mp3", NULL, &decoder); - if (result != MA_SUCCESS) { - return false; // An error occurred. - } - - ... - - ma_decoder_uninit(&decoder); - ``` - -When initializing a decoder, you can optionally pass in a pointer to a `ma_decoder_config` object -(the `NULL` argument in the example above) which allows you to configure the output format, channel -count, sample rate and channel map: - - ```c - ma_decoder_config config = ma_decoder_config_init(ma_format_f32, 2, 48000); - ``` - -When passing in `NULL` for decoder config in `ma_decoder_init*()`, the output format will be the -same as that defined by the decoding backend. - -Data is read from the decoder as PCM frames. This will output the number of PCM frames actually -read. If this is less than the requested number of PCM frames it means you've reached the end. The -return value will be `MA_AT_END` if no samples have been read and the end has been reached. - - ```c - ma_result result = ma_decoder_read_pcm_frames(pDecoder, pFrames, framesToRead, &framesRead); - if (framesRead < framesToRead) { - // Reached the end. - } - ``` - -You can also seek to a specific frame like so: - - ```c - ma_result result = ma_decoder_seek_to_pcm_frame(pDecoder, targetFrame); - if (result != MA_SUCCESS) { - return false; // An error occurred. - } - ``` - -If you want to loop back to the start, you can simply seek back to the first PCM frame: - - ```c - ma_decoder_seek_to_pcm_frame(pDecoder, 0); - ``` - -When loading a decoder, miniaudio uses a trial and error technique to find the appropriate decoding -backend. This can be unnecessarily inefficient if the type is already known. In this case you can -use `encodingFormat` variable in the device config to specify a specific encoding format you want -to decode: - - ```c - decoderConfig.encodingFormat = ma_encoding_format_wav; - ``` - -See the `ma_encoding_format` enum for possible encoding formats. - -The `ma_decoder_init_file()` API will try using the file extension to determine which decoding -backend to prefer. - - -8.1. Custom Decoders --------------------- -It's possible to implement a custom decoder and plug it into miniaudio. This is extremely useful -when you want to use the `ma_decoder` API, but need to support an encoding format that's not one of -the stock formats supported by miniaudio. This can be put to particularly good use when using the -`ma_engine` and/or `ma_resource_manager` APIs because they use `ma_decoder` internally. If, for -example, you wanted to support Opus, you can do so with a custom decoder (there if a reference -Opus decoder in the "extras" folder of the miniaudio repository which uses libopus + libopusfile). - -A custom decoder must implement a data source. A vtable called `ma_decoding_backend_vtable` needs -to be implemented which is then passed into the decoder config: - - ```c - ma_decoding_backend_vtable* pCustomBackendVTables[] = - { - &g_ma_decoding_backend_vtable_libvorbis, - &g_ma_decoding_backend_vtable_libopus - }; - - ... - - decoderConfig = ma_decoder_config_init_default(); - decoderConfig.pCustomBackendUserData = NULL; - decoderConfig.ppCustomBackendVTables = pCustomBackendVTables; - decoderConfig.customBackendCount = sizeof(pCustomBackendVTables) / sizeof(pCustomBackendVTables[0]); - ``` - -The `ma_decoding_backend_vtable` vtable has the following functions: - - ``` - onInit - onInitFile - onInitFileW - onInitMemory - onUninit - ``` - -There are only two functions that must be implemented - `onInit` and `onUninit`. The other -functions can be implemented for a small optimization for loading from a file path or memory. If -these are not specified, miniaudio will deal with it for you via a generic implementation. - -When you initialize a custom data source (by implementing the `onInit` function in the vtable) you -will need to output a pointer to a `ma_data_source` which implements your custom decoder. See the -section about data sources for details on how to implement this. Alternatively, see the -"custom_decoders" example in the miniaudio repository. - -The `onInit` function takes a pointer to some callbacks for the purpose of reading raw audio data -from some arbitrary source. You'll use these functions to read from the raw data and perform the -decoding. When you call them, you will pass in the `pReadSeekTellUserData` pointer to the relevant -parameter. - -The `pConfig` parameter in `onInit` can be used to configure the backend if appropriate. It's only -used as a hint and can be ignored. However, if any of the properties are relevant to your decoder, -an optimal implementation will handle the relevant properties appropriately. - -If memory allocation is required, it should be done so via the specified allocation callbacks if -possible (the `pAllocationCallbacks` parameter). - -If an error occurs when initializing the decoder, you should leave `ppBackend` unset, or set to -NULL, and make sure everything is cleaned up appropriately and an appropriate result code returned. -When multiple custom backends are specified, miniaudio will cycle through the vtables in the order -they're listed in the array that's passed into the decoder config so it's important that your -initialization routine is clean. - -When a decoder is uninitialized, the `onUninit` callback will be fired which will give you an -opportunity to clean up and internal data. - - - -9. Encoding -=========== -The `ma_encoding` API is used for writing audio files. The only supported output format is WAV. -This can be disabled by specifying the following option before the implementation of miniaudio: - - ```c - #define MA_NO_WAV - ``` - -An encoder can be initialized to write to a file with `ma_encoder_init_file()` or from data -delivered via callbacks with `ma_encoder_init()`. Below is an example for initializing an encoder -to output to a file. - - ```c - ma_encoder_config config = ma_encoder_config_init(ma_encoding_format_wav, FORMAT, CHANNELS, SAMPLE_RATE); - ma_encoder encoder; - ma_result result = ma_encoder_init_file("my_file.wav", &config, &encoder); - if (result != MA_SUCCESS) { - // Error - } - - ... - - ma_encoder_uninit(&encoder); - ``` - -When initializing an encoder you must specify a config which is initialized with -`ma_encoder_config_init()`. Here you must specify the file type, the output sample format, output -channel count and output sample rate. The following file types are supported: - - +------------------------+-------------+ - | Enum | Description | - +------------------------+-------------+ - | ma_encoding_format_wav | WAV | - +------------------------+-------------+ - -If the format, channel count or sample rate is not supported by the output file type an error will -be returned. The encoder will not perform data conversion so you will need to convert it before -outputting any audio data. To output audio data, use `ma_encoder_write_pcm_frames()`, like in the -example below: - - ```c - ma_uint64 framesWritten; - result = ma_encoder_write_pcm_frames(&encoder, pPCMFramesToWrite, framesToWrite, &framesWritten); - if (result != MA_SUCCESS) { - ... handle error ... - } - ``` - -The `framesWritten` variable will contain the number of PCM frames that were actually written. This -is optionally and you can pass in `NULL` if you need this. - -Encoders must be uninitialized with `ma_encoder_uninit()`. - - - -10. Data Conversion -=================== -A data conversion API is included with miniaudio which supports the majority of data conversion -requirements. This supports conversion between sample formats, channel counts (with channel -mapping) and sample rates. - - -10.1. Sample Format Conversion ------------------------------- -Conversion between sample formats is achieved with the `ma_pcm_*_to_*()`, `ma_pcm_convert()` and -`ma_convert_pcm_frames_format()` APIs. Use `ma_pcm_*_to_*()` to convert between two specific -formats. Use `ma_pcm_convert()` to convert based on a `ma_format` variable. Use -`ma_convert_pcm_frames_format()` to convert PCM frames where you want to specify the frame count -and channel count as a variable instead of the total sample count. - - -10.1.1. Dithering ------------------ -Dithering can be set using the ditherMode parameter. - -The different dithering modes include the following, in order of efficiency: - - +-----------+--------------------------+ - | Type | Enum Token | - +-----------+--------------------------+ - | None | ma_dither_mode_none | - | Rectangle | ma_dither_mode_rectangle | - | Triangle | ma_dither_mode_triangle | - +-----------+--------------------------+ - -Note that even if the dither mode is set to something other than `ma_dither_mode_none`, it will be -ignored for conversions where dithering is not needed. Dithering is available for the following -conversions: - - ``` - s16 -> u8 - s24 -> u8 - s32 -> u8 - f32 -> u8 - s24 -> s16 - s32 -> s16 - f32 -> s16 - ``` - -Note that it is not an error to pass something other than ma_dither_mode_none for conversions where -dither is not used. It will just be ignored. - - - -10.2. Channel Conversion ------------------------- -Channel conversion is used for channel rearrangement and conversion from one channel count to -another. The `ma_channel_converter` API is used for channel conversion. Below is an example of -initializing a simple channel converter which converts from mono to stereo. - - ```c - ma_channel_converter_config config = ma_channel_converter_config_init( - ma_format, // Sample format - 1, // Input channels - NULL, // Input channel map - 2, // Output channels - NULL, // Output channel map - ma_channel_mix_mode_default); // The mixing algorithm to use when combining channels. - - result = ma_channel_converter_init(&config, NULL, &converter); - if (result != MA_SUCCESS) { - // Error. - } - ``` - -To perform the conversion simply call `ma_channel_converter_process_pcm_frames()` like so: - - ```c - ma_result result = ma_channel_converter_process_pcm_frames(&converter, pFramesOut, pFramesIn, frameCount); - if (result != MA_SUCCESS) { - // Error. - } - ``` - -It is up to the caller to ensure the output buffer is large enough to accommodate the new PCM -frames. - -Input and output PCM frames are always interleaved. Deinterleaved layouts are not supported. - - -10.2.1. Channel Mapping ------------------------ -In addition to converting from one channel count to another, like the example above, the channel -converter can also be used to rearrange channels. When initializing the channel converter, you can -optionally pass in channel maps for both the input and output frames. If the channel counts are the -same, and each channel map contains the same channel positions with the exception that they're in -a different order, a simple shuffling of the channels will be performed. If, however, there is not -a 1:1 mapping of channel positions, or the channel counts differ, the input channels will be mixed -based on a mixing mode which is specified when initializing the `ma_channel_converter_config` -object. - -When converting from mono to multi-channel, the mono channel is simply copied to each output -channel. When going the other way around, the audio of each output channel is simply averaged and -copied to the mono channel. - -In more complicated cases blending is used. The `ma_channel_mix_mode_simple` mode will drop excess -channels and silence extra channels. For example, converting from 4 to 2 channels, the 3rd and 4th -channels will be dropped, whereas converting from 2 to 4 channels will put silence into the 3rd and -4th channels. - -The `ma_channel_mix_mode_rectangle` mode uses spacial locality based on a rectangle to compute a -simple distribution between input and output. Imagine sitting in the middle of a room, with -speakers on the walls representing channel positions. The `MA_CHANNEL_FRONT_LEFT` position can be -thought of as being in the corner of the front and left walls. - -Finally, the `ma_channel_mix_mode_custom_weights` mode can be used to use custom user-defined -weights. Custom weights can be passed in as the last parameter of -`ma_channel_converter_config_init()`. - -Predefined channel maps can be retrieved with `ma_channel_map_init_standard()`. This takes a -`ma_standard_channel_map` enum as its first parameter, which can be one of the following: - - +-----------------------------------+-----------------------------------------------------------+ - | Name | Description | - +-----------------------------------+-----------------------------------------------------------+ - | ma_standard_channel_map_default | Default channel map used by miniaudio. See below. | - | ma_standard_channel_map_microsoft | Channel map used by Microsoft's bitfield channel maps. | - | ma_standard_channel_map_alsa | Default ALSA channel map. | - | ma_standard_channel_map_rfc3551 | RFC 3551. Based on AIFF. | - | ma_standard_channel_map_flac | FLAC channel map. | - | ma_standard_channel_map_vorbis | Vorbis channel map. | - | ma_standard_channel_map_sound4 | FreeBSD's sound(4). | - | ma_standard_channel_map_sndio | sndio channel map. http://www.sndio.org/tips.html. | - | ma_standard_channel_map_webaudio | https://webaudio.github.io/web-audio-api/#ChannelOrdering | - +-----------------------------------+-----------------------------------------------------------+ - -Below are the channel maps used by default in miniaudio (`ma_standard_channel_map_default`): - - +---------------+---------------------------------+ - | Channel Count | Mapping | - +---------------+---------------------------------+ - | 1 (Mono) | 0: MA_CHANNEL_MONO | - +---------------+---------------------------------+ - | 2 (Stereo) | 0: MA_CHANNEL_FRONT_LEFT
| - | | 1: MA_CHANNEL_FRONT_RIGHT | - +---------------+---------------------------------+ - | 3 | 0: MA_CHANNEL_FRONT_LEFT
| - | | 1: MA_CHANNEL_FRONT_RIGHT
| - | | 2: MA_CHANNEL_FRONT_CENTER | - +---------------+---------------------------------+ - | 4 (Surround) | 0: MA_CHANNEL_FRONT_LEFT
| - | | 1: MA_CHANNEL_FRONT_RIGHT
| - | | 2: MA_CHANNEL_FRONT_CENTER
| - | | 3: MA_CHANNEL_BACK_CENTER | - +---------------+---------------------------------+ - | 5 | 0: MA_CHANNEL_FRONT_LEFT
| - | | 1: MA_CHANNEL_FRONT_RIGHT
| - | | 2: MA_CHANNEL_FRONT_CENTER
| - | | 3: MA_CHANNEL_BACK_LEFT
| - | | 4: MA_CHANNEL_BACK_RIGHT | - +---------------+---------------------------------+ - | 6 (5.1) | 0: MA_CHANNEL_FRONT_LEFT
| - | | 1: MA_CHANNEL_FRONT_RIGHT
| - | | 2: MA_CHANNEL_FRONT_CENTER
| - | | 3: MA_CHANNEL_LFE
| - | | 4: MA_CHANNEL_SIDE_LEFT
| - | | 5: MA_CHANNEL_SIDE_RIGHT | - +---------------+---------------------------------+ - | 7 | 0: MA_CHANNEL_FRONT_LEFT
| - | | 1: MA_CHANNEL_FRONT_RIGHT
| - | | 2: MA_CHANNEL_FRONT_CENTER
| - | | 3: MA_CHANNEL_LFE
| - | | 4: MA_CHANNEL_BACK_CENTER
| - | | 4: MA_CHANNEL_SIDE_LEFT
| - | | 5: MA_CHANNEL_SIDE_RIGHT | - +---------------+---------------------------------+ - | 8 (7.1) | 0: MA_CHANNEL_FRONT_LEFT
| - | | 1: MA_CHANNEL_FRONT_RIGHT
| - | | 2: MA_CHANNEL_FRONT_CENTER
| - | | 3: MA_CHANNEL_LFE
| - | | 4: MA_CHANNEL_BACK_LEFT
| - | | 5: MA_CHANNEL_BACK_RIGHT
| - | | 6: MA_CHANNEL_SIDE_LEFT
| - | | 7: MA_CHANNEL_SIDE_RIGHT | - +---------------+---------------------------------+ - | Other | All channels set to 0. This | - | | is equivalent to the same | - | | mapping as the device. | - +---------------+---------------------------------+ - - - -10.3. Resampling ----------------- -Resampling is achieved with the `ma_resampler` object. To create a resampler object, do something -like the following: - - ```c - ma_resampler_config config = ma_resampler_config_init( - ma_format_s16, - channels, - sampleRateIn, - sampleRateOut, - ma_resample_algorithm_linear); - - ma_resampler resampler; - ma_result result = ma_resampler_init(&config, NULL, &resampler); - if (result != MA_SUCCESS) { - // An error occurred... - } - ``` - -Do the following to uninitialize the resampler: - - ```c - ma_resampler_uninit(&resampler); - ``` - -The following example shows how data can be processed - - ```c - ma_uint64 frameCountIn = 1000; - ma_uint64 frameCountOut = 2000; - ma_result result = ma_resampler_process_pcm_frames(&resampler, pFramesIn, &frameCountIn, pFramesOut, &frameCountOut); - if (result != MA_SUCCESS) { - // An error occurred... - } - - // At this point, frameCountIn contains the number of input frames that were consumed and frameCountOut contains the - // number of output frames written. - ``` - -To initialize the resampler you first need to set up a config (`ma_resampler_config`) with -`ma_resampler_config_init()`. You need to specify the sample format you want to use, the number of -channels, the input and output sample rate, and the algorithm. - -The sample format can be either `ma_format_s16` or `ma_format_f32`. If you need a different format -you will need to perform pre- and post-conversions yourself where necessary. Note that the format -is the same for both input and output. The format cannot be changed after initialization. - -The resampler supports multiple channels and is always interleaved (both input and output). The -channel count cannot be changed after initialization. - -The sample rates can be anything other than zero, and are always specified in hertz. They should be -set to something like 44100, etc. The sample rate is the only configuration property that can be -changed after initialization. - -The miniaudio resampler has built-in support for the following algorithms: - - +-----------+------------------------------+ - | Algorithm | Enum Token | - +-----------+------------------------------+ - | Linear | ma_resample_algorithm_linear | - | Custom | ma_resample_algorithm_custom | - +-----------+------------------------------+ - -The algorithm cannot be changed after initialization. - -Processing always happens on a per PCM frame basis and always assumes interleaved input and output. -De-interleaved processing is not supported. To process frames, use -`ma_resampler_process_pcm_frames()`. On input, this function takes the number of output frames you -can fit in the output buffer and the number of input frames contained in the input buffer. On -output these variables contain the number of output frames that were written to the output buffer -and the number of input frames that were consumed in the process. You can pass in NULL for the -input buffer in which case it will be treated as an infinitely large buffer of zeros. The output -buffer can also be NULL, in which case the processing will be treated as seek. - -The sample rate can be changed dynamically on the fly. You can change this with explicit sample -rates with `ma_resampler_set_rate()` and also with a decimal ratio with -`ma_resampler_set_rate_ratio()`. The ratio is in/out. - -Sometimes it's useful to know exactly how many input frames will be required to output a specific -number of frames. You can calculate this with `ma_resampler_get_required_input_frame_count()`. -Likewise, it's sometimes useful to know exactly how many frames would be output given a certain -number of input frames. You can do this with `ma_resampler_get_expected_output_frame_count()`. - -Due to the nature of how resampling works, the resampler introduces some latency. This can be -retrieved in terms of both the input rate and the output rate with -`ma_resampler_get_input_latency()` and `ma_resampler_get_output_latency()`. - - -10.3.1. Resampling Algorithms ------------------------------ -The choice of resampling algorithm depends on your situation and requirements. - - -10.3.1.1. Linear Resampling ---------------------------- -The linear resampler is the fastest, but comes at the expense of poorer quality. There is, however, -some control over the quality of the linear resampler which may make it a suitable option depending -on your requirements. - -The linear resampler performs low-pass filtering before or after downsampling or upsampling, -depending on the sample rates you're converting between. When decreasing the sample rate, the -low-pass filter will be applied before downsampling. When increasing the rate it will be performed -after upsampling. By default a fourth order low-pass filter will be applied. This can be configured -via the `lpfOrder` configuration variable. Setting this to 0 will disable filtering. - -The low-pass filter has a cutoff frequency which defaults to half the sample rate of the lowest of -the input and output sample rates (Nyquist Frequency). - -The API for the linear resampler is the same as the main resampler API, only it's called -`ma_linear_resampler`. - - -10.3.2. Custom Resamplers -------------------------- -You can implement a custom resampler by using the `ma_resample_algorithm_custom` resampling -algorithm and setting a vtable in the resampler config: - - ```c - ma_resampler_config config = ma_resampler_config_init(..., ma_resample_algorithm_custom); - config.pBackendVTable = &g_customResamplerVTable; - ``` - -Custom resamplers are useful if the stock algorithms are not appropriate for your use case. You -need to implement the required functions in `ma_resampling_backend_vtable`. Note that not all -functions in the vtable need to be implemented, but if it's possible to implement, they should be. - -You can use the `ma_linear_resampler` object for an example on how to implement the vtable. The -`onGetHeapSize` callback is used to calculate the size of any internal heap allocation the custom -resampler will need to make given the supplied config. When you initialize the resampler via the -`onInit` callback, you'll be given a pointer to a heap allocation which is where you should store -the heap allocated data. You should not free this data in `onUninit` because miniaudio will manage -it for you. - -The `onProcess` callback is where the actual resampling takes place. On input, `pFrameCountIn` -points to a variable containing the number of frames in the `pFramesIn` buffer and -`pFrameCountOut` points to a variable containing the capacity in frames of the `pFramesOut` buffer. -On output, `pFrameCountIn` should be set to the number of input frames that were fully consumed, -whereas `pFrameCountOut` should be set to the number of frames that were written to `pFramesOut`. - -The `onSetRate` callback is optional and is used for dynamically changing the sample rate. If -dynamic rate changes are not supported, you can set this callback to NULL. - -The `onGetInputLatency` and `onGetOutputLatency` functions are used for retrieving the latency in -input and output rates respectively. These can be NULL in which case latency calculations will be -assumed to be NULL. - -The `onGetRequiredInputFrameCount` callback is used to give miniaudio a hint as to how many input -frames are required to be available to produce the given number of output frames. Likewise, the -`onGetExpectedOutputFrameCount` callback is used to determine how many output frames will be -produced given the specified number of input frames. miniaudio will use these as a hint, but they -are optional and can be set to NULL if you're unable to implement them. - - - -10.4. General Data Conversion ------------------------------ -The `ma_data_converter` API can be used to wrap sample format conversion, channel conversion and -resampling into one operation. This is what miniaudio uses internally to convert between the format -requested when the device was initialized and the format of the backend's native device. The API -for general data conversion is very similar to the resampling API. Create a `ma_data_converter` -object like this: - - ```c - ma_data_converter_config config = ma_data_converter_config_init( - inputFormat, - outputFormat, - inputChannels, - outputChannels, - inputSampleRate, - outputSampleRate - ); - - ma_data_converter converter; - ma_result result = ma_data_converter_init(&config, NULL, &converter); - if (result != MA_SUCCESS) { - // An error occurred... - } - ``` - -In the example above we use `ma_data_converter_config_init()` to initialize the config, however -there's many more properties that can be configured, such as channel maps and resampling quality. -Something like the following may be more suitable depending on your requirements: - - ```c - ma_data_converter_config config = ma_data_converter_config_init_default(); - config.formatIn = inputFormat; - config.formatOut = outputFormat; - config.channelsIn = inputChannels; - config.channelsOut = outputChannels; - config.sampleRateIn = inputSampleRate; - config.sampleRateOut = outputSampleRate; - ma_channel_map_init_standard(ma_standard_channel_map_flac, config.channelMapIn, sizeof(config.channelMapIn)/sizeof(config.channelMapIn[0]), config.channelCountIn); - config.resampling.linear.lpfOrder = MA_MAX_FILTER_ORDER; - ``` - -Do the following to uninitialize the data converter: - - ```c - ma_data_converter_uninit(&converter, NULL); - ``` - -The following example shows how data can be processed - - ```c - ma_uint64 frameCountIn = 1000; - ma_uint64 frameCountOut = 2000; - ma_result result = ma_data_converter_process_pcm_frames(&converter, pFramesIn, &frameCountIn, pFramesOut, &frameCountOut); - if (result != MA_SUCCESS) { - // An error occurred... - } - - // At this point, frameCountIn contains the number of input frames that were consumed and frameCountOut contains the number - // of output frames written. - ``` - -The data converter supports multiple channels and is always interleaved (both input and output). -The channel count cannot be changed after initialization. - -Sample rates can be anything other than zero, and are always specified in hertz. They should be set -to something like 44100, etc. The sample rate is the only configuration property that can be -changed after initialization, but only if the `resampling.allowDynamicSampleRate` member of -`ma_data_converter_config` is set to `MA_TRUE`. To change the sample rate, use -`ma_data_converter_set_rate()` or `ma_data_converter_set_rate_ratio()`. The ratio must be in/out. -The resampling algorithm cannot be changed after initialization. - -Processing always happens on a per PCM frame basis and always assumes interleaved input and output. -De-interleaved processing is not supported. To process frames, use -`ma_data_converter_process_pcm_frames()`. On input, this function takes the number of output frames -you can fit in the output buffer and the number of input frames contained in the input buffer. On -output these variables contain the number of output frames that were written to the output buffer -and the number of input frames that were consumed in the process. You can pass in NULL for the -input buffer in which case it will be treated as an infinitely large -buffer of zeros. The output buffer can also be NULL, in which case the processing will be treated -as seek. - -Sometimes it's useful to know exactly how many input frames will be required to output a specific -number of frames. You can calculate this with `ma_data_converter_get_required_input_frame_count()`. -Likewise, it's sometimes useful to know exactly how many frames would be output given a certain -number of input frames. You can do this with `ma_data_converter_get_expected_output_frame_count()`. - -Due to the nature of how resampling works, the data converter introduces some latency if resampling -is required. This can be retrieved in terms of both the input rate and the output rate with -`ma_data_converter_get_input_latency()` and `ma_data_converter_get_output_latency()`. - - - -11. Filtering -============= - -11.1. Biquad Filtering ----------------------- -Biquad filtering is achieved with the `ma_biquad` API. Example: - - ```c - ma_biquad_config config = ma_biquad_config_init(ma_format_f32, channels, b0, b1, b2, a0, a1, a2); - ma_result result = ma_biquad_init(&config, NULL, &biquad); - if (result != MA_SUCCESS) { - // Error. - } - - ... - - ma_biquad_process_pcm_frames(&biquad, pFramesOut, pFramesIn, frameCount); - ``` - -Biquad filtering is implemented using transposed direct form 2. The numerator coefficients are b0, -b1 and b2, and the denominator coefficients are a0, a1 and a2. The a0 coefficient is required and -coefficients must not be pre-normalized. - -Supported formats are `ma_format_s16` and `ma_format_f32`. If you need to use a different format -you need to convert it yourself beforehand. When using `ma_format_s16` the biquad filter will use -fixed point arithmetic. When using `ma_format_f32`, floating point arithmetic will be used. - -Input and output frames are always interleaved. - -Filtering can be applied in-place by passing in the same pointer for both the input and output -buffers, like so: - - ```c - ma_biquad_process_pcm_frames(&biquad, pMyData, pMyData, frameCount); - ``` - -If you need to change the values of the coefficients, but maintain the values in the registers you -can do so with `ma_biquad_reinit()`. This is useful if you need to change the properties of the -filter while keeping the values of registers valid to avoid glitching. Do not use -`ma_biquad_init()` for this as it will do a full initialization which involves clearing the -registers to 0. Note that changing the format or channel count after initialization is invalid and -will result in an error. - - -11.2. Low-Pass Filtering ------------------------- -Low-pass filtering is achieved with the following APIs: - - +---------+------------------------------------------+ - | API | Description | - +---------+------------------------------------------+ - | ma_lpf1 | First order low-pass filter | - | ma_lpf2 | Second order low-pass filter | - | ma_lpf | High order low-pass filter (Butterworth) | - +---------+------------------------------------------+ - -Low-pass filter example: - - ```c - ma_lpf_config config = ma_lpf_config_init(ma_format_f32, channels, sampleRate, cutoffFrequency, order); - ma_result result = ma_lpf_init(&config, &lpf); - if (result != MA_SUCCESS) { - // Error. - } - - ... - - ma_lpf_process_pcm_frames(&lpf, pFramesOut, pFramesIn, frameCount); - ``` - -Supported formats are `ma_format_s16` and` ma_format_f32`. If you need to use a different format -you need to convert it yourself beforehand. Input and output frames are always interleaved. - -Filtering can be applied in-place by passing in the same pointer for both the input and output -buffers, like so: - - ```c - ma_lpf_process_pcm_frames(&lpf, pMyData, pMyData, frameCount); - ``` - -The maximum filter order is limited to `MA_MAX_FILTER_ORDER` which is set to 8. If you need more, -you can chain first and second order filters together. - - ```c - for (iFilter = 0; iFilter < filterCount; iFilter += 1) { - ma_lpf2_process_pcm_frames(&lpf2[iFilter], pMyData, pMyData, frameCount); - } - ``` - -If you need to change the configuration of the filter, but need to maintain the state of internal -registers you can do so with `ma_lpf_reinit()`. This may be useful if you need to change the sample -rate and/or cutoff frequency dynamically while maintaining smooth transitions. Note that changing the -format or channel count after initialization is invalid and will result in an error. - -The `ma_lpf` object supports a configurable order, but if you only need a first order filter you -may want to consider using `ma_lpf1`. Likewise, if you only need a second order filter you can use -`ma_lpf2`. The advantage of this is that they're lighter weight and a bit more efficient. - -If an even filter order is specified, a series of second order filters will be processed in a -chain. If an odd filter order is specified, a first order filter will be applied, followed by a -series of second order filters in a chain. - - -11.3. High-Pass Filtering -------------------------- -High-pass filtering is achieved with the following APIs: - - +---------+-------------------------------------------+ - | API | Description | - +---------+-------------------------------------------+ - | ma_hpf1 | First order high-pass filter | - | ma_hpf2 | Second order high-pass filter | - | ma_hpf | High order high-pass filter (Butterworth) | - +---------+-------------------------------------------+ - -High-pass filters work exactly the same as low-pass filters, only the APIs are called `ma_hpf1`, -`ma_hpf2` and `ma_hpf`. See example code for low-pass filters for example usage. - - -11.4. Band-Pass Filtering -------------------------- -Band-pass filtering is achieved with the following APIs: - - +---------+-------------------------------+ - | API | Description | - +---------+-------------------------------+ - | ma_bpf2 | Second order band-pass filter | - | ma_bpf | High order band-pass filter | - +---------+-------------------------------+ - -Band-pass filters work exactly the same as low-pass filters, only the APIs are called `ma_bpf2` and -`ma_hpf`. See example code for low-pass filters for example usage. Note that the order for -band-pass filters must be an even number which means there is no first order band-pass filter, -unlike low-pass and high-pass filters. - - -11.5. Notch Filtering ---------------------- -Notch filtering is achieved with the following APIs: - - +-----------+------------------------------------------+ - | API | Description | - +-----------+------------------------------------------+ - | ma_notch2 | Second order notching filter | - +-----------+------------------------------------------+ - - -11.6. Peaking EQ Filtering -------------------------- -Peaking filtering is achieved with the following APIs: - - +----------+------------------------------------------+ - | API | Description | - +----------+------------------------------------------+ - | ma_peak2 | Second order peaking filter | - +----------+------------------------------------------+ - - -11.7. Low Shelf Filtering -------------------------- -Low shelf filtering is achieved with the following APIs: - - +-------------+------------------------------------------+ - | API | Description | - +-------------+------------------------------------------+ - | ma_loshelf2 | Second order low shelf filter | - +-------------+------------------------------------------+ - -Where a high-pass filter is used to eliminate lower frequencies, a low shelf filter can be used to -just turn them down rather than eliminate them entirely. - - -11.8. High Shelf Filtering --------------------------- -High shelf filtering is achieved with the following APIs: - - +-------------+------------------------------------------+ - | API | Description | - +-------------+------------------------------------------+ - | ma_hishelf2 | Second order high shelf filter | - +-------------+------------------------------------------+ - -The high shelf filter has the same API as the low shelf filter, only you would use `ma_hishelf` -instead of `ma_loshelf`. Where a low shelf filter is used to adjust the volume of low frequencies, -the high shelf filter does the same thing for high frequencies. - - - - -12. Waveform and Noise Generation -================================= - -12.1. Waveforms ---------------- -miniaudio supports generation of sine, square, triangle and sawtooth waveforms. This is achieved -with the `ma_waveform` API. Example: - - ```c - ma_waveform_config config = ma_waveform_config_init( - FORMAT, - CHANNELS, - SAMPLE_RATE, - ma_waveform_type_sine, - amplitude, - frequency); - - ma_waveform waveform; - ma_result result = ma_waveform_init(&config, &waveform); - if (result != MA_SUCCESS) { - // Error. - } - - ... - - ma_waveform_read_pcm_frames(&waveform, pOutput, frameCount); - ``` - -The amplitude, frequency, type, and sample rate can be changed dynamically with -`ma_waveform_set_amplitude()`, `ma_waveform_set_frequency()`, `ma_waveform_set_type()`, and -`ma_waveform_set_sample_rate()` respectively. - -You can invert the waveform by setting the amplitude to a negative value. You can use this to -control whether or not a sawtooth has a positive or negative ramp, for example. - -Below are the supported waveform types: - - +---------------------------+ - | Enum Name | - +---------------------------+ - | ma_waveform_type_sine | - | ma_waveform_type_square | - | ma_waveform_type_triangle | - | ma_waveform_type_sawtooth | - +---------------------------+ - - - -12.2. Noise ------------ -miniaudio supports generation of white, pink and Brownian noise via the `ma_noise` API. Example: - - ```c - ma_noise_config config = ma_noise_config_init( - FORMAT, - CHANNELS, - ma_noise_type_white, - SEED, - amplitude); - - ma_noise noise; - ma_result result = ma_noise_init(&config, &noise); - if (result != MA_SUCCESS) { - // Error. - } - - ... - - ma_noise_read_pcm_frames(&noise, pOutput, frameCount); - ``` - -The noise API uses simple LCG random number generation. It supports a custom seed which is useful -for things like automated testing requiring reproducibility. Setting the seed to zero will default -to `MA_DEFAULT_LCG_SEED`. - -The amplitude and seed can be changed dynamically with `ma_noise_set_amplitude()` and -`ma_noise_set_seed()` respectively. - -By default, the noise API will use different values for different channels. So, for example, the -left side in a stereo stream will be different to the right side. To instead have each channel use -the same random value, set the `duplicateChannels` member of the noise config to true, like so: - - ```c - config.duplicateChannels = MA_TRUE; - ``` - -Below are the supported noise types. - - +------------------------+ - | Enum Name | - +------------------------+ - | ma_noise_type_white | - | ma_noise_type_pink | - | ma_noise_type_brownian | - +------------------------+ - - - -13. Audio Buffers -================= -miniaudio supports reading from a buffer of raw audio data via the `ma_audio_buffer` API. This can -read from memory that's managed by the application, but can also handle the memory management for -you internally. Memory management is flexible and should support most use cases. - -Audio buffers are initialized using the standard configuration system used everywhere in miniaudio: - - ```c - ma_audio_buffer_config config = ma_audio_buffer_config_init( - format, - channels, - sizeInFrames, - pExistingData, - &allocationCallbacks); - - ma_audio_buffer buffer; - result = ma_audio_buffer_init(&config, &buffer); - if (result != MA_SUCCESS) { - // Error. - } - - ... - - ma_audio_buffer_uninit(&buffer); - ``` - -In the example above, the memory pointed to by `pExistingData` will *not* be copied and is how an -application can do self-managed memory allocation. If you would rather make a copy of the data, use -`ma_audio_buffer_init_copy()`. To uninitialize the buffer, use `ma_audio_buffer_uninit()`. - -Sometimes it can be convenient to allocate the memory for the `ma_audio_buffer` structure and the -raw audio data in a contiguous block of memory. That is, the raw audio data will be located -immediately after the `ma_audio_buffer` structure. To do this, use -`ma_audio_buffer_alloc_and_init()`: - - ```c - ma_audio_buffer_config config = ma_audio_buffer_config_init( - format, - channels, - sizeInFrames, - pExistingData, - &allocationCallbacks); - - ma_audio_buffer* pBuffer - result = ma_audio_buffer_alloc_and_init(&config, &pBuffer); - if (result != MA_SUCCESS) { - // Error - } - - ... - - ma_audio_buffer_uninit_and_free(&buffer); - ``` - -If you initialize the buffer with `ma_audio_buffer_alloc_and_init()` you should uninitialize it -with `ma_audio_buffer_uninit_and_free()`. In the example above, the memory pointed to by -`pExistingData` will be copied into the buffer, which is contrary to the behavior of -`ma_audio_buffer_init()`. - -An audio buffer has a playback cursor just like a decoder. As you read frames from the buffer, the -cursor moves forward. The last parameter (`loop`) can be used to determine if the buffer should -loop. The return value is the number of frames actually read. If this is less than the number of -frames requested it means the end has been reached. This should never happen if the `loop` -parameter is set to true. If you want to manually loop back to the start, you can do so with with -`ma_audio_buffer_seek_to_pcm_frame(pAudioBuffer, 0)`. Below is an example for reading data from an -audio buffer. - - ```c - ma_uint64 framesRead = ma_audio_buffer_read_pcm_frames(pAudioBuffer, pFramesOut, desiredFrameCount, isLooping); - if (framesRead < desiredFrameCount) { - // If not looping, this means the end has been reached. This should never happen in looping mode with valid input. - } - ``` - -Sometimes you may want to avoid the cost of data movement between the internal buffer and the -output buffer. Instead you can use memory mapping to retrieve a pointer to a segment of data: - - ```c - void* pMappedFrames; - ma_uint64 frameCount = frameCountToTryMapping; - ma_result result = ma_audio_buffer_map(pAudioBuffer, &pMappedFrames, &frameCount); - if (result == MA_SUCCESS) { - // Map was successful. The value in frameCount will be how many frames were _actually_ mapped, which may be - // less due to the end of the buffer being reached. - ma_copy_pcm_frames(pFramesOut, pMappedFrames, frameCount, pAudioBuffer->format, pAudioBuffer->channels); - - // You must unmap the buffer. - ma_audio_buffer_unmap(pAudioBuffer, frameCount); - } - ``` - -When you use memory mapping, the read cursor is increment by the frame count passed in to -`ma_audio_buffer_unmap()`. If you decide not to process every frame you can pass in a value smaller -than the value returned by `ma_audio_buffer_map()`. The disadvantage to using memory mapping is -that it does not handle looping for you. You can determine if the buffer is at the end for the -purpose of looping with `ma_audio_buffer_at_end()` or by inspecting the return value of -`ma_audio_buffer_unmap()` and checking if it equals `MA_AT_END`. You should not treat `MA_AT_END` -as an error when returned by `ma_audio_buffer_unmap()`. - - - -14. Ring Buffers -================ -miniaudio supports lock free (single producer, single consumer) ring buffers which are exposed via -the `ma_rb` and `ma_pcm_rb` APIs. The `ma_rb` API operates on bytes, whereas the `ma_pcm_rb` -operates on PCM frames. They are otherwise identical as `ma_pcm_rb` is just a wrapper around -`ma_rb`. - -Unlike most other APIs in miniaudio, ring buffers support both interleaved and deinterleaved -streams. The caller can also allocate their own backing memory for the ring buffer to use -internally for added flexibility. Otherwise the ring buffer will manage it's internal memory for -you. - -The examples below use the PCM frame variant of the ring buffer since that's most likely the one -you will want to use. To initialize a ring buffer, do something like the following: - - ```c - ma_pcm_rb rb; - ma_result result = ma_pcm_rb_init(FORMAT, CHANNELS, BUFFER_SIZE_IN_FRAMES, NULL, NULL, &rb); - if (result != MA_SUCCESS) { - // Error - } - ``` - -The `ma_pcm_rb_init()` function takes the sample format and channel count as parameters because -it's the PCM variant of the ring buffer API. For the regular ring buffer that operates on bytes you -would call `ma_rb_init()` which leaves these out and just takes the size of the buffer in bytes -instead of frames. The fourth parameter is an optional pre-allocated buffer and the fifth parameter -is a pointer to a `ma_allocation_callbacks` structure for custom memory allocation routines. -Passing in `NULL` for this results in `MA_MALLOC()` and `MA_FREE()` being used. - -Use `ma_pcm_rb_init_ex()` if you need a deinterleaved buffer. The data for each sub-buffer is -offset from each other based on the stride. To manage your sub-buffers you can use -`ma_pcm_rb_get_subbuffer_stride()`, `ma_pcm_rb_get_subbuffer_offset()` and -`ma_pcm_rb_get_subbuffer_ptr()`. - -Use `ma_pcm_rb_acquire_read()` and `ma_pcm_rb_acquire_write()` to retrieve a pointer to a section -of the ring buffer. You specify the number of frames you need, and on output it will set to what -was actually acquired. If the read or write pointer is positioned such that the number of frames -requested will require a loop, it will be clamped to the end of the buffer. Therefore, the number -of frames you're given may be less than the number you requested. - -After calling `ma_pcm_rb_acquire_read()` or `ma_pcm_rb_acquire_write()`, you do your work on the -buffer and then "commit" it with `ma_pcm_rb_commit_read()` or `ma_pcm_rb_commit_write()`. This is -where the read/write pointers are updated. When you commit you need to pass in the buffer that was -returned by the earlier call to `ma_pcm_rb_acquire_read()` or `ma_pcm_rb_acquire_write()` and is -only used for validation. The number of frames passed to `ma_pcm_rb_commit_read()` and -`ma_pcm_rb_commit_write()` is what's used to increment the pointers, and can be less that what was -originally requested. - -If you want to correct for drift between the write pointer and the read pointer you can use a -combination of `ma_pcm_rb_pointer_distance()`, `ma_pcm_rb_seek_read()` and -`ma_pcm_rb_seek_write()`. Note that you can only move the pointers forward, and you should only -move the read pointer forward via the consumer thread, and the write pointer forward by the -producer thread. If there is too much space between the pointers, move the read pointer forward. If -there is too little space between the pointers, move the write pointer forward. - -You can use a ring buffer at the byte level instead of the PCM frame level by using the `ma_rb` -API. This is exactly the same, only you will use the `ma_rb` functions instead of `ma_pcm_rb` and -instead of frame counts you will pass around byte counts. - -The maximum size of the buffer in bytes is `0x7FFFFFFF-(MA_SIMD_ALIGNMENT-1)` due to the most -significant bit being used to encode a loop flag and the internally managed buffers always being -aligned to `MA_SIMD_ALIGNMENT`. - -Note that the ring buffer is only thread safe when used by a single consumer thread and single -producer thread. - - - -15. Backends -============ -The following backends are supported by miniaudio. These are listed in order of default priority. -When no backend is specified when initializing a context or device, miniaudio will attempt to use -each of these backends in the order listed in the table below. - -Note that backends that are not usable by the build target will not be included in the build. For -example, ALSA, which is specific to Linux, will not be included in the Windows build. - - +-------------+-----------------------+--------------------------------------------------------+ - | Name | Enum Name | Supported Operating Systems | - +-------------+-----------------------+--------------------------------------------------------+ - | WASAPI | ma_backend_wasapi | Windows Vista+ | - | DirectSound | ma_backend_dsound | Windows XP+ | - | WinMM | ma_backend_winmm | Windows 95+ | - | Core Audio | ma_backend_coreaudio | macOS, iOS | - | sndio | ma_backend_sndio | OpenBSD | - | audio(4) | ma_backend_audio4 | NetBSD, OpenBSD | - | OSS | ma_backend_oss | FreeBSD | - | PulseAudio | ma_backend_pulseaudio | Cross Platform (disabled on Windows, BSD and Android) | - | ALSA | ma_backend_alsa | Linux | - | JACK | ma_backend_jack | Cross Platform (disabled on BSD and Android) | - | AAudio | ma_backend_aaudio | Android 8+ | - | OpenSL ES | ma_backend_opensl | Android (API level 16+) | - | Web Audio | ma_backend_webaudio | Web (via Emscripten) | - | Custom | ma_backend_custom | Cross Platform | - | Null | ma_backend_null | Cross Platform (not used on Web) | - +-------------+-----------------------+--------------------------------------------------------+ - -Some backends have some nuance details you may want to be aware of. - -15.1. WASAPI ------------- -- Low-latency shared mode will be disabled when using an application-defined sample rate which is - different to the device's native sample rate. To work around this, set `wasapi.noAutoConvertSRC` - to true in the device config. This is due to IAudioClient3_InitializeSharedAudioStream() failing - when the `AUDCLNT_STREAMFLAGS_AUTOCONVERTPCM` flag is specified. Setting wasapi.noAutoConvertSRC - will result in miniaudio's internal resampler being used instead which will in turn enable the - use of low-latency shared mode. - -15.2. PulseAudio ----------------- -- If you experience bad glitching/noise on Arch Linux, consider this fix from the Arch wiki: - https://wiki.archlinux.org/index.php/PulseAudio/Troubleshooting#Glitches,_skips_or_crackling. - Alternatively, consider using a different backend such as ALSA. - -15.3. Android -------------- -- To capture audio on Android, remember to add the RECORD_AUDIO permission to your manifest: - `` -- With OpenSL|ES, only a single ma_context can be active at any given time. This is due to a - limitation with OpenSL|ES. -- With AAudio, only default devices are enumerated. This is due to AAudio not having an enumeration - API (devices are enumerated through Java). You can however perform your own device enumeration - through Java and then set the ID in the ma_device_id structure (ma_device_id.aaudio) and pass it - to ma_device_init(). -- The backend API will perform resampling where possible. The reason for this as opposed to using - miniaudio's built-in resampler is to take advantage of any potential device-specific - optimizations the driver may implement. - -BSD ---- -- The sndio backend is currently only enabled on OpenBSD builds. -- The audio(4) backend is supported on OpenBSD, but you may need to disable sndiod before you can - use it. - -15.4. UWP ---------- -- UWP only supports default playback and capture devices. -- UWP requires the Microphone capability to be enabled in the application's manifest (Package.appxmanifest): - - ``` - - ... - - - - - ``` - -15.5. Web Audio / Emscripten ----------------------------- -- You cannot use `-std=c*` compiler flags, nor `-ansi`. This only applies to the Emscripten build. -- The first time a context is initialized it will create a global object called "miniaudio" whose - primary purpose is to act as a factory for device objects. -- Currently the Web Audio backend uses ScriptProcessorNode's, but this may need to change later as - they've been deprecated. -- Google has implemented a policy in their browsers that prevent automatic media output without - first receiving some kind of user input. The following web page has additional details: - https://developers.google.com/web/updates/2017/09/autoplay-policy-changes. Starting the device - may fail if you try to start playback without first handling some kind of user input. - - - -16. Optimization Tips -===================== -See below for some tips on improving performance. - -16.1. Low Level API -------------------- -- In the data callback, if your data is already clipped prior to copying it into the output buffer, - set the `noClip` config option in the device config to true. This will disable miniaudio's built - in clipping function. -- By default, miniaudio will pre-silence the data callback's output buffer. If you know that you - will always write valid data to the output buffer you can disable pre-silencing by setting the - `noPreSilence` config option in the device config to true. - -16.2. High Level API --------------------- -- If a sound does not require doppler or pitch shifting, consider disabling pitching by - initializing the sound with the `MA_SOUND_FLAG_NO_PITCH` flag. -- If a sound does not require spatialization, disable it by initializing the sound with the - `MA_SOUND_FLAG_NO_SPATIALIZATION` flag. It can be re-enabled again post-initialization with - `ma_sound_set_spatialization_enabled()`. -- If you know all of your sounds will always be the same sample rate, set the engine's sample - rate to match that of the sounds. Likewise, if you're using a self-managed resource manager, - consider setting the decoded sample rate to match your sounds. By configuring everything to - use a consistent sample rate, sample rate conversion can be avoided. - - - -17. Miscellaneous Notes -======================= -- Automatic stream routing is enabled on a per-backend basis. Support is explicitly enabled for - WASAPI and Core Audio, however other backends such as PulseAudio may naturally support it, though - not all have been tested. -- When compiling with VC6 and earlier, decoding is restricted to files less than 2GB in size. This - is due to 64-bit file APIs not being available. -*/ - -#ifndef miniaudio_h -#define miniaudio_h - -#ifdef __cplusplus -extern "C" { -#endif - -#define MA_STRINGIFY(x) #x -#define MA_XSTRINGIFY(x) MA_STRINGIFY(x) - -#define MA_VERSION_MAJOR 0 -#define MA_VERSION_MINOR 11 -#define MA_VERSION_REVISION 22 -#define MA_VERSION_STRING MA_XSTRINGIFY(MA_VERSION_MAJOR) "." MA_XSTRINGIFY(MA_VERSION_MINOR) "." MA_XSTRINGIFY(MA_VERSION_REVISION) - -#if defined(_MSC_VER) && !defined(__clang__) - #pragma warning(push) - #pragma warning(disable:4201) /* nonstandard extension used: nameless struct/union */ - #pragma warning(disable:4214) /* nonstandard extension used: bit field types other than int */ - #pragma warning(disable:4324) /* structure was padded due to alignment specifier */ -#elif defined(__clang__) || (defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 8))) - #pragma GCC diagnostic push - #pragma GCC diagnostic ignored "-Wpedantic" /* For ISO C99 doesn't support unnamed structs/unions [-Wpedantic] */ - #if defined(__clang__) - #pragma GCC diagnostic ignored "-Wc11-extensions" /* anonymous unions are a C11 extension */ - #endif -#endif - - -#if defined(__LP64__) || defined(_WIN64) || (defined(__x86_64__) && !defined(__ILP32__)) || defined(_M_X64) || defined(__ia64) || defined(_M_IA64) || defined(__aarch64__) || defined(_M_ARM64) || defined(__powerpc64__) || defined(__ppc64__) - #define MA_SIZEOF_PTR 8 -#else - #define MA_SIZEOF_PTR 4 -#endif - -#include /* For size_t. */ - -/* Sized types. */ -#if defined(MA_USE_STDINT) - #include - typedef int8_t ma_int8; - typedef uint8_t ma_uint8; - typedef int16_t ma_int16; - typedef uint16_t ma_uint16; - typedef int32_t ma_int32; - typedef uint32_t ma_uint32; - typedef int64_t ma_int64; - typedef uint64_t ma_uint64; -#else - typedef signed char ma_int8; - typedef unsigned char ma_uint8; - typedef signed short ma_int16; - typedef unsigned short ma_uint16; - typedef signed int ma_int32; - typedef unsigned int ma_uint32; - #if defined(_MSC_VER) && !defined(__clang__) - typedef signed __int64 ma_int64; - typedef unsigned __int64 ma_uint64; - #else - #if defined(__clang__) || (defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6))) - #pragma GCC diagnostic push - #pragma GCC diagnostic ignored "-Wlong-long" - #if defined(__clang__) - #pragma GCC diagnostic ignored "-Wc++11-long-long" - #endif - #endif - typedef signed long long ma_int64; - typedef unsigned long long ma_uint64; - #if defined(__clang__) || (defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6))) - #pragma GCC diagnostic pop - #endif - #endif -#endif /* MA_USE_STDINT */ - -#if MA_SIZEOF_PTR == 8 - typedef ma_uint64 ma_uintptr; -#else - typedef ma_uint32 ma_uintptr; -#endif - -typedef ma_uint8 ma_bool8; -typedef ma_uint32 ma_bool32; -#define MA_TRUE 1 -#define MA_FALSE 0 - -/* These float types are not used universally by miniaudio. It's to simplify some macro expansion for atomic types. */ -typedef float ma_float; -typedef double ma_double; - -typedef void* ma_handle; -typedef void* ma_ptr; - -/* -ma_proc is annoying because when compiling with GCC we get pedantic warnings about converting -between `void*` and `void (*)()`. We can't use `void (*)()` with MSVC however, because we'll get -warning C4191 about "type cast between incompatible function types". To work around this I'm going -to use a different data type depending on the compiler. -*/ -#if defined(__GNUC__) -typedef void (*ma_proc)(void); -#else -typedef void* ma_proc; -#endif - -#if defined(_MSC_VER) && !defined(_WCHAR_T_DEFINED) -typedef ma_uint16 wchar_t; -#endif - -/* Define NULL for some compilers. */ -#ifndef NULL -#define NULL 0 -#endif - -#if defined(SIZE_MAX) - #define MA_SIZE_MAX SIZE_MAX -#else - #define MA_SIZE_MAX 0xFFFFFFFF /* When SIZE_MAX is not defined by the standard library just default to the maximum 32-bit unsigned integer. */ -#endif - - -/* Platform/backend detection. */ -#if defined(_WIN32) || defined(__COSMOPOLITAN__) - #define MA_WIN32 - #if defined(MA_FORCE_UWP) || (defined(WINAPI_FAMILY) && ((defined(WINAPI_FAMILY_PC_APP) && WINAPI_FAMILY == WINAPI_FAMILY_PC_APP) || (defined(WINAPI_FAMILY_PHONE_APP) && WINAPI_FAMILY == WINAPI_FAMILY_PHONE_APP))) - #define MA_WIN32_UWP - #elif defined(WINAPI_FAMILY) && (defined(WINAPI_FAMILY_GAMES) && WINAPI_FAMILY == WINAPI_FAMILY_GAMES) - #define MA_WIN32_GDK - #else - #define MA_WIN32_DESKTOP - #endif -#endif -#if !defined(_WIN32) /* If it's not Win32, assume POSIX. */ - #define MA_POSIX - - /* - Use the MA_NO_PTHREAD_IN_HEADER option at your own risk. This is intentionally undocumented. - You can use this to avoid including pthread.h in the header section. The downside is that it - results in some fixed sized structures being declared for the various types that are used in - miniaudio. The risk here is that these types might be too small for a given platform. This - risk is yours to take and no support will be offered if you enable this option. - */ - #ifndef MA_NO_PTHREAD_IN_HEADER - #include /* Unfortunate #include, but needed for pthread_t, pthread_mutex_t and pthread_cond_t types. */ - typedef pthread_t ma_pthread_t; - typedef pthread_mutex_t ma_pthread_mutex_t; - typedef pthread_cond_t ma_pthread_cond_t; - #else - typedef ma_uintptr ma_pthread_t; - typedef union ma_pthread_mutex_t { char __data[40]; ma_uint64 __alignment; } ma_pthread_mutex_t; - typedef union ma_pthread_cond_t { char __data[48]; ma_uint64 __alignment; } ma_pthread_cond_t; - #endif - - #if defined(__unix__) - #define MA_UNIX - #endif - #if defined(__linux__) - #define MA_LINUX - #endif - #if defined(__APPLE__) - #define MA_APPLE - #endif - #if defined(__DragonFly__) || defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__) - #define MA_BSD - #endif - #if defined(__ANDROID__) - #define MA_ANDROID - #endif - #if defined(__EMSCRIPTEN__) - #define MA_EMSCRIPTEN - #endif - #if defined(__ORBIS__) - #define MA_ORBIS - #endif - #if defined(__PROSPERO__) - #define MA_PROSPERO - #endif - #if defined(__NX__) - #define MA_NX - #endif - #if defined(__BEOS__) || defined(__HAIKU__) - #define MA_BEOS - #endif - #if defined(__HAIKU__) - #define MA_HAIKU - #endif -#endif - -#if defined(__has_c_attribute) - #if __has_c_attribute(fallthrough) - #define MA_FALLTHROUGH [[fallthrough]] - #endif -#endif -#if !defined(MA_FALLTHROUGH) && defined(__has_attribute) && (defined(__clang__) || defined(__GNUC__)) - #if __has_attribute(fallthrough) - #define MA_FALLTHROUGH __attribute__((fallthrough)) - #endif -#endif -#if !defined(MA_FALLTHROUGH) - #define MA_FALLTHROUGH ((void)0) -#endif - -#ifdef _MSC_VER - #define MA_INLINE __forceinline - - /* noinline was introduced in Visual Studio 2005. */ - #if _MSC_VER >= 1400 - #define MA_NO_INLINE __declspec(noinline) - #else - #define MA_NO_INLINE - #endif -#elif defined(__GNUC__) - /* - I've had a bug report where GCC is emitting warnings about functions possibly not being inlineable. This warning happens when - the __attribute__((always_inline)) attribute is defined without an "inline" statement. I think therefore there must be some - case where "__inline__" is not always defined, thus the compiler emitting these warnings. When using -std=c89 or -ansi on the - command line, we cannot use the "inline" keyword and instead need to use "__inline__". In an attempt to work around this issue - I am using "__inline__" only when we're compiling in strict ANSI mode. - */ - #if defined(__STRICT_ANSI__) - #define MA_GNUC_INLINE_HINT __inline__ - #else - #define MA_GNUC_INLINE_HINT inline - #endif - - #if (__GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 2)) || defined(__clang__) - #define MA_INLINE MA_GNUC_INLINE_HINT __attribute__((always_inline)) - #define MA_NO_INLINE __attribute__((noinline)) - #else - #define MA_INLINE MA_GNUC_INLINE_HINT - #define MA_NO_INLINE __attribute__((noinline)) - #endif -#elif defined(__WATCOMC__) - #define MA_INLINE __inline - #define MA_NO_INLINE -#else - #define MA_INLINE - #define MA_NO_INLINE -#endif - -/* MA_DLL is not officially supported. You're on your own if you want to use this. */ -#if defined(MA_DLL) - #if defined(_WIN32) - #define MA_DLL_IMPORT __declspec(dllimport) - #define MA_DLL_EXPORT __declspec(dllexport) - #define MA_DLL_PRIVATE static - #else - #if defined(__GNUC__) && __GNUC__ >= 4 - #define MA_DLL_IMPORT __attribute__((visibility("default"))) - #define MA_DLL_EXPORT __attribute__((visibility("default"))) - #define MA_DLL_PRIVATE __attribute__((visibility("hidden"))) - #else - #define MA_DLL_IMPORT - #define MA_DLL_EXPORT - #define MA_DLL_PRIVATE static - #endif - #endif -#endif - -#if !defined(MA_API) - #if defined(MA_DLL) - #if defined(MINIAUDIO_IMPLEMENTATION) || defined(MA_IMPLEMENTATION) - #define MA_API MA_DLL_EXPORT - #else - #define MA_API MA_DLL_IMPORT - #endif - #else - #define MA_API extern - #endif -#endif - -#if !defined(MA_STATIC) - #if defined(MA_DLL) - #define MA_PRIVATE MA_DLL_PRIVATE - #else - #define MA_PRIVATE static - #endif -#endif - - -/* SIMD alignment in bytes. Currently set to 32 bytes in preparation for future AVX optimizations. */ -#define MA_SIMD_ALIGNMENT 32 - -/* -Special wchar_t type to ensure any structures in the public sections that reference it have a -consistent size across all platforms. - -On Windows, wchar_t is 2 bytes, whereas everywhere else it's 4 bytes. Since Windows likes to use -wchar_t for its IDs, we need a special explicitly sized wchar type that is always 2 bytes on all -platforms. -*/ -#if !defined(MA_POSIX) && defined(MA_WIN32) -typedef wchar_t ma_wchar_win32; -#else -typedef ma_uint16 ma_wchar_win32; -#endif - - - -/* -Logging Levels -============== -Log levels are only used to give logging callbacks some context as to the severity of a log message -so they can do filtering. All log levels will be posted to registered logging callbacks. If you -don't want to output a certain log level you can discriminate against the log level in the callback. - -MA_LOG_LEVEL_DEBUG - Used for debugging. Useful for debug and test builds, but should be disabled in release builds. - -MA_LOG_LEVEL_INFO - Informational logging. Useful for debugging. This will never be called from within the data - callback. - -MA_LOG_LEVEL_WARNING - Warnings. You should enable this in you development builds and action them when encountered. These - logs usually indicate a potential problem or misconfiguration, but still allow you to keep - running. This will never be called from within the data callback. - -MA_LOG_LEVEL_ERROR - Error logging. This will be fired when an operation fails and is subsequently aborted. This can - be fired from within the data callback, in which case the device will be stopped. You should - always have this log level enabled. -*/ -typedef enum -{ - MA_LOG_LEVEL_DEBUG = 4, - MA_LOG_LEVEL_INFO = 3, - MA_LOG_LEVEL_WARNING = 2, - MA_LOG_LEVEL_ERROR = 1 -} ma_log_level; - -/* -Variables needing to be accessed atomically should be declared with this macro for two reasons: - - 1) It allows people who read the code to identify a variable as such; and - 2) It forces alignment on platforms where it's required or optimal. - -Note that for x86/64, alignment is not strictly necessary, but does have some performance -implications. Where supported by the compiler, alignment will be used, but otherwise if the CPU -architecture does not require it, it will simply leave it unaligned. This is the case with old -versions of Visual Studio, which I've confirmed with at least VC6. -*/ -#if !defined(_MSC_VER) && defined (__STDC_VERSION__) && (__STDC_VERSION__ >= 201112L) - #include - #define MA_ATOMIC(alignment, type) _Alignas(alignment) type -#else - #if defined(__GNUC__) - /* GCC-style compilers. */ - #define MA_ATOMIC(alignment, type) type __attribute__((aligned(alignment))) - #elif defined(_MSC_VER) && _MSC_VER > 1200 /* 1200 = VC6. Alignment not supported, but not necessary because x86 is the only supported target. */ - /* MSVC. */ - #define MA_ATOMIC(alignment, type) __declspec(align(alignment)) type - #else - /* Other compilers. */ - #define MA_ATOMIC(alignment, type) type - #endif -#endif - -typedef struct ma_context ma_context; -typedef struct ma_device ma_device; - -typedef ma_uint8 ma_channel; -typedef enum -{ - MA_CHANNEL_NONE = 0, - MA_CHANNEL_MONO = 1, - MA_CHANNEL_FRONT_LEFT = 2, - MA_CHANNEL_FRONT_RIGHT = 3, - MA_CHANNEL_FRONT_CENTER = 4, - MA_CHANNEL_LFE = 5, - MA_CHANNEL_BACK_LEFT = 6, - MA_CHANNEL_BACK_RIGHT = 7, - MA_CHANNEL_FRONT_LEFT_CENTER = 8, - MA_CHANNEL_FRONT_RIGHT_CENTER = 9, - MA_CHANNEL_BACK_CENTER = 10, - MA_CHANNEL_SIDE_LEFT = 11, - MA_CHANNEL_SIDE_RIGHT = 12, - MA_CHANNEL_TOP_CENTER = 13, - MA_CHANNEL_TOP_FRONT_LEFT = 14, - MA_CHANNEL_TOP_FRONT_CENTER = 15, - MA_CHANNEL_TOP_FRONT_RIGHT = 16, - MA_CHANNEL_TOP_BACK_LEFT = 17, - MA_CHANNEL_TOP_BACK_CENTER = 18, - MA_CHANNEL_TOP_BACK_RIGHT = 19, - MA_CHANNEL_AUX_0 = 20, - MA_CHANNEL_AUX_1 = 21, - MA_CHANNEL_AUX_2 = 22, - MA_CHANNEL_AUX_3 = 23, - MA_CHANNEL_AUX_4 = 24, - MA_CHANNEL_AUX_5 = 25, - MA_CHANNEL_AUX_6 = 26, - MA_CHANNEL_AUX_7 = 27, - MA_CHANNEL_AUX_8 = 28, - MA_CHANNEL_AUX_9 = 29, - MA_CHANNEL_AUX_10 = 30, - MA_CHANNEL_AUX_11 = 31, - MA_CHANNEL_AUX_12 = 32, - MA_CHANNEL_AUX_13 = 33, - MA_CHANNEL_AUX_14 = 34, - MA_CHANNEL_AUX_15 = 35, - MA_CHANNEL_AUX_16 = 36, - MA_CHANNEL_AUX_17 = 37, - MA_CHANNEL_AUX_18 = 38, - MA_CHANNEL_AUX_19 = 39, - MA_CHANNEL_AUX_20 = 40, - MA_CHANNEL_AUX_21 = 41, - MA_CHANNEL_AUX_22 = 42, - MA_CHANNEL_AUX_23 = 43, - MA_CHANNEL_AUX_24 = 44, - MA_CHANNEL_AUX_25 = 45, - MA_CHANNEL_AUX_26 = 46, - MA_CHANNEL_AUX_27 = 47, - MA_CHANNEL_AUX_28 = 48, - MA_CHANNEL_AUX_29 = 49, - MA_CHANNEL_AUX_30 = 50, - MA_CHANNEL_AUX_31 = 51, - MA_CHANNEL_LEFT = MA_CHANNEL_FRONT_LEFT, - MA_CHANNEL_RIGHT = MA_CHANNEL_FRONT_RIGHT, - MA_CHANNEL_POSITION_COUNT = (MA_CHANNEL_AUX_31 + 1) -} _ma_channel_position; /* Do not use `_ma_channel_position` directly. Use `ma_channel` instead. */ - -typedef enum -{ - MA_SUCCESS = 0, - MA_ERROR = -1, /* A generic error. */ - MA_INVALID_ARGS = -2, - MA_INVALID_OPERATION = -3, - MA_OUT_OF_MEMORY = -4, - MA_OUT_OF_RANGE = -5, - MA_ACCESS_DENIED = -6, - MA_DOES_NOT_EXIST = -7, - MA_ALREADY_EXISTS = -8, - MA_TOO_MANY_OPEN_FILES = -9, - MA_INVALID_FILE = -10, - MA_TOO_BIG = -11, - MA_PATH_TOO_LONG = -12, - MA_NAME_TOO_LONG = -13, - MA_NOT_DIRECTORY = -14, - MA_IS_DIRECTORY = -15, - MA_DIRECTORY_NOT_EMPTY = -16, - MA_AT_END = -17, - MA_NO_SPACE = -18, - MA_BUSY = -19, - MA_IO_ERROR = -20, - MA_INTERRUPT = -21, - MA_UNAVAILABLE = -22, - MA_ALREADY_IN_USE = -23, - MA_BAD_ADDRESS = -24, - MA_BAD_SEEK = -25, - MA_BAD_PIPE = -26, - MA_DEADLOCK = -27, - MA_TOO_MANY_LINKS = -28, - MA_NOT_IMPLEMENTED = -29, - MA_NO_MESSAGE = -30, - MA_BAD_MESSAGE = -31, - MA_NO_DATA_AVAILABLE = -32, - MA_INVALID_DATA = -33, - MA_TIMEOUT = -34, - MA_NO_NETWORK = -35, - MA_NOT_UNIQUE = -36, - MA_NOT_SOCKET = -37, - MA_NO_ADDRESS = -38, - MA_BAD_PROTOCOL = -39, - MA_PROTOCOL_UNAVAILABLE = -40, - MA_PROTOCOL_NOT_SUPPORTED = -41, - MA_PROTOCOL_FAMILY_NOT_SUPPORTED = -42, - MA_ADDRESS_FAMILY_NOT_SUPPORTED = -43, - MA_SOCKET_NOT_SUPPORTED = -44, - MA_CONNECTION_RESET = -45, - MA_ALREADY_CONNECTED = -46, - MA_NOT_CONNECTED = -47, - MA_CONNECTION_REFUSED = -48, - MA_NO_HOST = -49, - MA_IN_PROGRESS = -50, - MA_CANCELLED = -51, - MA_MEMORY_ALREADY_MAPPED = -52, - - /* General non-standard errors. */ - MA_CRC_MISMATCH = -100, - - /* General miniaudio-specific errors. */ - MA_FORMAT_NOT_SUPPORTED = -200, - MA_DEVICE_TYPE_NOT_SUPPORTED = -201, - MA_SHARE_MODE_NOT_SUPPORTED = -202, - MA_NO_BACKEND = -203, - MA_NO_DEVICE = -204, - MA_API_NOT_FOUND = -205, - MA_INVALID_DEVICE_CONFIG = -206, - MA_LOOP = -207, - MA_BACKEND_NOT_ENABLED = -208, - - /* State errors. */ - MA_DEVICE_NOT_INITIALIZED = -300, - MA_DEVICE_ALREADY_INITIALIZED = -301, - MA_DEVICE_NOT_STARTED = -302, - MA_DEVICE_NOT_STOPPED = -303, - - /* Operation errors. */ - MA_FAILED_TO_INIT_BACKEND = -400, - MA_FAILED_TO_OPEN_BACKEND_DEVICE = -401, - MA_FAILED_TO_START_BACKEND_DEVICE = -402, - MA_FAILED_TO_STOP_BACKEND_DEVICE = -403 -} ma_result; - - -#define MA_MIN_CHANNELS 1 -#ifndef MA_MAX_CHANNELS -#define MA_MAX_CHANNELS 254 -#endif - -#ifndef MA_MAX_FILTER_ORDER -#define MA_MAX_FILTER_ORDER 8 -#endif - -typedef enum -{ - ma_stream_format_pcm = 0 -} ma_stream_format; - -typedef enum -{ - ma_stream_layout_interleaved = 0, - ma_stream_layout_deinterleaved -} ma_stream_layout; - -typedef enum -{ - ma_dither_mode_none = 0, - ma_dither_mode_rectangle, - ma_dither_mode_triangle -} ma_dither_mode; - -typedef enum -{ - /* - I like to keep these explicitly defined because they're used as a key into a lookup table. When items are - added to this, make sure there are no gaps and that they're added to the lookup table in ma_get_bytes_per_sample(). - */ - ma_format_unknown = 0, /* Mainly used for indicating an error, but also used as the default for the output format for decoders. */ - ma_format_u8 = 1, - ma_format_s16 = 2, /* Seems to be the most widely supported format. */ - ma_format_s24 = 3, /* Tightly packed. 3 bytes per sample. */ - ma_format_s32 = 4, - ma_format_f32 = 5, - ma_format_count -} ma_format; - -typedef enum -{ - /* Standard rates need to be in priority order. */ - ma_standard_sample_rate_48000 = 48000, /* Most common */ - ma_standard_sample_rate_44100 = 44100, - - ma_standard_sample_rate_32000 = 32000, /* Lows */ - ma_standard_sample_rate_24000 = 24000, - ma_standard_sample_rate_22050 = 22050, - - ma_standard_sample_rate_88200 = 88200, /* Highs */ - ma_standard_sample_rate_96000 = 96000, - ma_standard_sample_rate_176400 = 176400, - ma_standard_sample_rate_192000 = 192000, - - ma_standard_sample_rate_16000 = 16000, /* Extreme lows */ - ma_standard_sample_rate_11025 = 11025, - ma_standard_sample_rate_8000 = 8000, - - ma_standard_sample_rate_352800 = 352800, /* Extreme highs */ - ma_standard_sample_rate_384000 = 384000, - - ma_standard_sample_rate_min = ma_standard_sample_rate_8000, - ma_standard_sample_rate_max = ma_standard_sample_rate_384000, - ma_standard_sample_rate_count = 14 /* Need to maintain the count manually. Make sure this is updated if items are added to enum. */ -} ma_standard_sample_rate; - - -typedef enum -{ - ma_channel_mix_mode_rectangular = 0, /* Simple averaging based on the plane(s) the channel is sitting on. */ - ma_channel_mix_mode_simple, /* Drop excess channels; zeroed out extra channels. */ - ma_channel_mix_mode_custom_weights, /* Use custom weights specified in ma_channel_converter_config. */ - ma_channel_mix_mode_default = ma_channel_mix_mode_rectangular -} ma_channel_mix_mode; - -typedef enum -{ - ma_standard_channel_map_microsoft, - ma_standard_channel_map_alsa, - ma_standard_channel_map_rfc3551, /* Based off AIFF. */ - ma_standard_channel_map_flac, - ma_standard_channel_map_vorbis, - ma_standard_channel_map_sound4, /* FreeBSD's sound(4). */ - ma_standard_channel_map_sndio, /* www.sndio.org/tips.html */ - ma_standard_channel_map_webaudio = ma_standard_channel_map_flac, /* https://webaudio.github.io/web-audio-api/#ChannelOrdering. Only 1, 2, 4 and 6 channels are defined, but can fill in the gaps with logical assumptions. */ - ma_standard_channel_map_default = ma_standard_channel_map_microsoft -} ma_standard_channel_map; - -typedef enum -{ - ma_performance_profile_low_latency = 0, - ma_performance_profile_conservative -} ma_performance_profile; - - -typedef struct -{ - void* pUserData; - void* (* onMalloc)(size_t sz, void* pUserData); - void* (* onRealloc)(void* p, size_t sz, void* pUserData); - void (* onFree)(void* p, void* pUserData); -} ma_allocation_callbacks; - -typedef struct -{ - ma_int32 state; -} ma_lcg; - - -/* -Atomics. - -These are typesafe structures to prevent errors as a result of forgetting to reference variables atomically. It's too -easy to introduce subtle bugs where you accidentally do a regular assignment instead of an atomic load/store, etc. By -using a struct we can enforce the use of atomics at compile time. - -These types are declared in the header section because we need to reference them in structs below, but functions for -using them are only exposed in the implementation section. I do not want these to be part of the public API. - -There's a few downsides to this system. The first is that you need to declare a new struct for each type. Below are -some macros to help with the declarations. They will be named like so: - - ma_atomic_uint32 - atomic ma_uint32 - ma_atomic_int32 - atomic ma_int32 - ma_atomic_uint64 - atomic ma_uint64 - ma_atomic_float - atomic float - ma_atomic_bool32 - atomic ma_bool32 - -The other downside is that atomic pointers are extremely messy. You need to declare a new struct for each specific -type of pointer you need to make atomic. For example, an atomic ma_node* will look like this: - - MA_ATOMIC_SAFE_TYPE_IMPL_PTR(node) - -Which will declare a type struct that's named like so: - - ma_atomic_ptr_node - -Functions to use the atomic types are declared in the implementation section. All atomic functions are prefixed with -the name of the struct. For example: - - ma_atomic_uint32_set() - Atomic store of ma_uint32 - ma_atomic_uint32_get() - Atomic load of ma_uint32 - etc. - -For pointer types it's the same, which makes them a bit messy to use due to the length of each function name, but in -return you get type safety and enforcement of atomic operations. -*/ -#define MA_ATOMIC_SAFE_TYPE_DECL(c89TypeExtension, typeSize, type) \ - typedef struct \ - { \ - MA_ATOMIC(typeSize, ma_##type) value; \ - } ma_atomic_##type; \ - -#define MA_ATOMIC_SAFE_TYPE_DECL_PTR(type) \ - typedef struct \ - { \ - MA_ATOMIC(MA_SIZEOF_PTR, ma_##type*) value; \ - } ma_atomic_ptr_##type; \ - -MA_ATOMIC_SAFE_TYPE_DECL(32, 4, uint32) -MA_ATOMIC_SAFE_TYPE_DECL(i32, 4, int32) -MA_ATOMIC_SAFE_TYPE_DECL(64, 8, uint64) -MA_ATOMIC_SAFE_TYPE_DECL(f32, 4, float) -MA_ATOMIC_SAFE_TYPE_DECL(32, 4, bool32) - - -/* Spinlocks are 32-bit for compatibility reasons. */ -typedef ma_uint32 ma_spinlock; - -#ifndef MA_NO_THREADING - /* Thread priorities should be ordered such that the default priority of the worker thread is 0. */ - typedef enum - { - ma_thread_priority_idle = -5, - ma_thread_priority_lowest = -4, - ma_thread_priority_low = -3, - ma_thread_priority_normal = -2, - ma_thread_priority_high = -1, - ma_thread_priority_highest = 0, - ma_thread_priority_realtime = 1, - ma_thread_priority_default = 0 - } ma_thread_priority; - - #if defined(MA_POSIX) - typedef ma_pthread_t ma_thread; - #elif defined(MA_WIN32) - typedef ma_handle ma_thread; - #endif - - #if defined(MA_POSIX) - typedef ma_pthread_mutex_t ma_mutex; - #elif defined(MA_WIN32) - typedef ma_handle ma_mutex; - #endif - - #if defined(MA_POSIX) - typedef struct - { - ma_uint32 value; - ma_pthread_mutex_t lock; - ma_pthread_cond_t cond; - } ma_event; - #elif defined(MA_WIN32) - typedef ma_handle ma_event; - #endif - - #if defined(MA_POSIX) - typedef struct - { - int value; - ma_pthread_mutex_t lock; - ma_pthread_cond_t cond; - } ma_semaphore; - #elif defined(MA_WIN32) - typedef ma_handle ma_semaphore; - #endif -#else - /* MA_NO_THREADING is set which means threading is disabled. Threading is required by some API families. If any of these are enabled we need to throw an error. */ - #ifndef MA_NO_DEVICE_IO - #error "MA_NO_THREADING cannot be used without MA_NO_DEVICE_IO"; - #endif -#endif /* MA_NO_THREADING */ - - -/* -Retrieves the version of miniaudio as separated integers. Each component can be NULL if it's not required. -*/ -MA_API void ma_version(ma_uint32* pMajor, ma_uint32* pMinor, ma_uint32* pRevision); - -/* -Retrieves the version of miniaudio as a string which can be useful for logging purposes. -*/ -MA_API const char* ma_version_string(void); - - -/************************************************************************************************************************************************************** - -Logging - -**************************************************************************************************************************************************************/ -#include /* For va_list. */ - -#if defined(__has_attribute) - #if __has_attribute(format) - #define MA_ATTRIBUTE_FORMAT(fmt, va) __attribute__((format(printf, fmt, va))) - #endif -#endif -#ifndef MA_ATTRIBUTE_FORMAT -#define MA_ATTRIBUTE_FORMAT(fmt, va) -#endif - -#ifndef MA_MAX_LOG_CALLBACKS -#define MA_MAX_LOG_CALLBACKS 4 -#endif - - -/* -The callback for handling log messages. - - -Parameters ----------- -pUserData (in) - The user data pointer that was passed into ma_log_register_callback(). - -logLevel (in) - The log level. This can be one of the following: - - +----------------------+ - | Log Level | - +----------------------+ - | MA_LOG_LEVEL_DEBUG | - | MA_LOG_LEVEL_INFO | - | MA_LOG_LEVEL_WARNING | - | MA_LOG_LEVEL_ERROR | - +----------------------+ - -pMessage (in) - The log message. -*/ -typedef void (* ma_log_callback_proc)(void* pUserData, ma_uint32 level, const char* pMessage); - -typedef struct -{ - ma_log_callback_proc onLog; - void* pUserData; -} ma_log_callback; - -MA_API ma_log_callback ma_log_callback_init(ma_log_callback_proc onLog, void* pUserData); - - -typedef struct -{ - ma_log_callback callbacks[MA_MAX_LOG_CALLBACKS]; - ma_uint32 callbackCount; - ma_allocation_callbacks allocationCallbacks; /* Need to store these persistently because ma_log_postv() might need to allocate a buffer on the heap. */ -#ifndef MA_NO_THREADING - ma_mutex lock; /* For thread safety just to make it easier and safer for the logging implementation. */ -#endif -} ma_log; - -MA_API ma_result ma_log_init(const ma_allocation_callbacks* pAllocationCallbacks, ma_log* pLog); -MA_API void ma_log_uninit(ma_log* pLog); -MA_API ma_result ma_log_register_callback(ma_log* pLog, ma_log_callback callback); -MA_API ma_result ma_log_unregister_callback(ma_log* pLog, ma_log_callback callback); -MA_API ma_result ma_log_post(ma_log* pLog, ma_uint32 level, const char* pMessage); -MA_API ma_result ma_log_postv(ma_log* pLog, ma_uint32 level, const char* pFormat, va_list args); -MA_API ma_result ma_log_postf(ma_log* pLog, ma_uint32 level, const char* pFormat, ...) MA_ATTRIBUTE_FORMAT(3, 4); - - -/************************************************************************************************************************************************************** - -Biquad Filtering - -**************************************************************************************************************************************************************/ -typedef union -{ - float f32; - ma_int32 s32; -} ma_biquad_coefficient; - -typedef struct -{ - ma_format format; - ma_uint32 channels; - double b0; - double b1; - double b2; - double a0; - double a1; - double a2; -} ma_biquad_config; - -MA_API ma_biquad_config ma_biquad_config_init(ma_format format, ma_uint32 channels, double b0, double b1, double b2, double a0, double a1, double a2); - -typedef struct -{ - ma_format format; - ma_uint32 channels; - ma_biquad_coefficient b0; - ma_biquad_coefficient b1; - ma_biquad_coefficient b2; - ma_biquad_coefficient a1; - ma_biquad_coefficient a2; - ma_biquad_coefficient* pR1; - ma_biquad_coefficient* pR2; - - /* Memory management. */ - void* _pHeap; - ma_bool32 _ownsHeap; -} ma_biquad; - -MA_API ma_result ma_biquad_get_heap_size(const ma_biquad_config* pConfig, size_t* pHeapSizeInBytes); -MA_API ma_result ma_biquad_init_preallocated(const ma_biquad_config* pConfig, void* pHeap, ma_biquad* pBQ); -MA_API ma_result ma_biquad_init(const ma_biquad_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_biquad* pBQ); -MA_API void ma_biquad_uninit(ma_biquad* pBQ, const ma_allocation_callbacks* pAllocationCallbacks); -MA_API ma_result ma_biquad_reinit(const ma_biquad_config* pConfig, ma_biquad* pBQ); -MA_API ma_result ma_biquad_clear_cache(ma_biquad* pBQ); -MA_API ma_result ma_biquad_process_pcm_frames(ma_biquad* pBQ, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount); -MA_API ma_uint32 ma_biquad_get_latency(const ma_biquad* pBQ); - - -/************************************************************************************************************************************************************** - -Low-Pass Filtering - -**************************************************************************************************************************************************************/ -typedef struct -{ - ma_format format; - ma_uint32 channels; - ma_uint32 sampleRate; - double cutoffFrequency; - double q; -} ma_lpf1_config, ma_lpf2_config; - -MA_API ma_lpf1_config ma_lpf1_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRate, double cutoffFrequency); -MA_API ma_lpf2_config ma_lpf2_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRate, double cutoffFrequency, double q); - -typedef struct -{ - ma_format format; - ma_uint32 channels; - ma_biquad_coefficient a; - ma_biquad_coefficient* pR1; - - /* Memory management. */ - void* _pHeap; - ma_bool32 _ownsHeap; -} ma_lpf1; - -MA_API ma_result ma_lpf1_get_heap_size(const ma_lpf1_config* pConfig, size_t* pHeapSizeInBytes); -MA_API ma_result ma_lpf1_init_preallocated(const ma_lpf1_config* pConfig, void* pHeap, ma_lpf1* pLPF); -MA_API ma_result ma_lpf1_init(const ma_lpf1_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_lpf1* pLPF); -MA_API void ma_lpf1_uninit(ma_lpf1* pLPF, const ma_allocation_callbacks* pAllocationCallbacks); -MA_API ma_result ma_lpf1_reinit(const ma_lpf1_config* pConfig, ma_lpf1* pLPF); -MA_API ma_result ma_lpf1_clear_cache(ma_lpf1* pLPF); -MA_API ma_result ma_lpf1_process_pcm_frames(ma_lpf1* pLPF, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount); -MA_API ma_uint32 ma_lpf1_get_latency(const ma_lpf1* pLPF); - -typedef struct -{ - ma_biquad bq; /* The second order low-pass filter is implemented as a biquad filter. */ -} ma_lpf2; - -MA_API ma_result ma_lpf2_get_heap_size(const ma_lpf2_config* pConfig, size_t* pHeapSizeInBytes); -MA_API ma_result ma_lpf2_init_preallocated(const ma_lpf2_config* pConfig, void* pHeap, ma_lpf2* pHPF); -MA_API ma_result ma_lpf2_init(const ma_lpf2_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_lpf2* pLPF); -MA_API void ma_lpf2_uninit(ma_lpf2* pLPF, const ma_allocation_callbacks* pAllocationCallbacks); -MA_API ma_result ma_lpf2_reinit(const ma_lpf2_config* pConfig, ma_lpf2* pLPF); -MA_API ma_result ma_lpf2_clear_cache(ma_lpf2* pLPF); -MA_API ma_result ma_lpf2_process_pcm_frames(ma_lpf2* pLPF, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount); -MA_API ma_uint32 ma_lpf2_get_latency(const ma_lpf2* pLPF); - - -typedef struct -{ - ma_format format; - ma_uint32 channels; - ma_uint32 sampleRate; - double cutoffFrequency; - ma_uint32 order; /* If set to 0, will be treated as a passthrough (no filtering will be applied). */ -} ma_lpf_config; - -MA_API ma_lpf_config ma_lpf_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRate, double cutoffFrequency, ma_uint32 order); - -typedef struct -{ - ma_format format; - ma_uint32 channels; - ma_uint32 sampleRate; - ma_uint32 lpf1Count; - ma_uint32 lpf2Count; - ma_lpf1* pLPF1; - ma_lpf2* pLPF2; - - /* Memory management. */ - void* _pHeap; - ma_bool32 _ownsHeap; -} ma_lpf; - -MA_API ma_result ma_lpf_get_heap_size(const ma_lpf_config* pConfig, size_t* pHeapSizeInBytes); -MA_API ma_result ma_lpf_init_preallocated(const ma_lpf_config* pConfig, void* pHeap, ma_lpf* pLPF); -MA_API ma_result ma_lpf_init(const ma_lpf_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_lpf* pLPF); -MA_API void ma_lpf_uninit(ma_lpf* pLPF, const ma_allocation_callbacks* pAllocationCallbacks); -MA_API ma_result ma_lpf_reinit(const ma_lpf_config* pConfig, ma_lpf* pLPF); -MA_API ma_result ma_lpf_clear_cache(ma_lpf* pLPF); -MA_API ma_result ma_lpf_process_pcm_frames(ma_lpf* pLPF, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount); -MA_API ma_uint32 ma_lpf_get_latency(const ma_lpf* pLPF); - - -/************************************************************************************************************************************************************** - -High-Pass Filtering - -**************************************************************************************************************************************************************/ -typedef struct -{ - ma_format format; - ma_uint32 channels; - ma_uint32 sampleRate; - double cutoffFrequency; - double q; -} ma_hpf1_config, ma_hpf2_config; - -MA_API ma_hpf1_config ma_hpf1_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRate, double cutoffFrequency); -MA_API ma_hpf2_config ma_hpf2_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRate, double cutoffFrequency, double q); - -typedef struct -{ - ma_format format; - ma_uint32 channels; - ma_biquad_coefficient a; - ma_biquad_coefficient* pR1; - - /* Memory management. */ - void* _pHeap; - ma_bool32 _ownsHeap; -} ma_hpf1; - -MA_API ma_result ma_hpf1_get_heap_size(const ma_hpf1_config* pConfig, size_t* pHeapSizeInBytes); -MA_API ma_result ma_hpf1_init_preallocated(const ma_hpf1_config* pConfig, void* pHeap, ma_hpf1* pLPF); -MA_API ma_result ma_hpf1_init(const ma_hpf1_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_hpf1* pHPF); -MA_API void ma_hpf1_uninit(ma_hpf1* pHPF, const ma_allocation_callbacks* pAllocationCallbacks); -MA_API ma_result ma_hpf1_reinit(const ma_hpf1_config* pConfig, ma_hpf1* pHPF); -MA_API ma_result ma_hpf1_process_pcm_frames(ma_hpf1* pHPF, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount); -MA_API ma_uint32 ma_hpf1_get_latency(const ma_hpf1* pHPF); - -typedef struct -{ - ma_biquad bq; /* The second order high-pass filter is implemented as a biquad filter. */ -} ma_hpf2; - -MA_API ma_result ma_hpf2_get_heap_size(const ma_hpf2_config* pConfig, size_t* pHeapSizeInBytes); -MA_API ma_result ma_hpf2_init_preallocated(const ma_hpf2_config* pConfig, void* pHeap, ma_hpf2* pHPF); -MA_API ma_result ma_hpf2_init(const ma_hpf2_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_hpf2* pHPF); -MA_API void ma_hpf2_uninit(ma_hpf2* pHPF, const ma_allocation_callbacks* pAllocationCallbacks); -MA_API ma_result ma_hpf2_reinit(const ma_hpf2_config* pConfig, ma_hpf2* pHPF); -MA_API ma_result ma_hpf2_process_pcm_frames(ma_hpf2* pHPF, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount); -MA_API ma_uint32 ma_hpf2_get_latency(const ma_hpf2* pHPF); - - -typedef struct -{ - ma_format format; - ma_uint32 channels; - ma_uint32 sampleRate; - double cutoffFrequency; - ma_uint32 order; /* If set to 0, will be treated as a passthrough (no filtering will be applied). */ -} ma_hpf_config; - -MA_API ma_hpf_config ma_hpf_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRate, double cutoffFrequency, ma_uint32 order); - -typedef struct -{ - ma_format format; - ma_uint32 channels; - ma_uint32 sampleRate; - ma_uint32 hpf1Count; - ma_uint32 hpf2Count; - ma_hpf1* pHPF1; - ma_hpf2* pHPF2; - - /* Memory management. */ - void* _pHeap; - ma_bool32 _ownsHeap; -} ma_hpf; - -MA_API ma_result ma_hpf_get_heap_size(const ma_hpf_config* pConfig, size_t* pHeapSizeInBytes); -MA_API ma_result ma_hpf_init_preallocated(const ma_hpf_config* pConfig, void* pHeap, ma_hpf* pLPF); -MA_API ma_result ma_hpf_init(const ma_hpf_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_hpf* pHPF); -MA_API void ma_hpf_uninit(ma_hpf* pHPF, const ma_allocation_callbacks* pAllocationCallbacks); -MA_API ma_result ma_hpf_reinit(const ma_hpf_config* pConfig, ma_hpf* pHPF); -MA_API ma_result ma_hpf_process_pcm_frames(ma_hpf* pHPF, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount); -MA_API ma_uint32 ma_hpf_get_latency(const ma_hpf* pHPF); - - -/************************************************************************************************************************************************************** - -Band-Pass Filtering - -**************************************************************************************************************************************************************/ -typedef struct -{ - ma_format format; - ma_uint32 channels; - ma_uint32 sampleRate; - double cutoffFrequency; - double q; -} ma_bpf2_config; - -MA_API ma_bpf2_config ma_bpf2_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRate, double cutoffFrequency, double q); - -typedef struct -{ - ma_biquad bq; /* The second order band-pass filter is implemented as a biquad filter. */ -} ma_bpf2; - -MA_API ma_result ma_bpf2_get_heap_size(const ma_bpf2_config* pConfig, size_t* pHeapSizeInBytes); -MA_API ma_result ma_bpf2_init_preallocated(const ma_bpf2_config* pConfig, void* pHeap, ma_bpf2* pBPF); -MA_API ma_result ma_bpf2_init(const ma_bpf2_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_bpf2* pBPF); -MA_API void ma_bpf2_uninit(ma_bpf2* pBPF, const ma_allocation_callbacks* pAllocationCallbacks); -MA_API ma_result ma_bpf2_reinit(const ma_bpf2_config* pConfig, ma_bpf2* pBPF); -MA_API ma_result ma_bpf2_process_pcm_frames(ma_bpf2* pBPF, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount); -MA_API ma_uint32 ma_bpf2_get_latency(const ma_bpf2* pBPF); - - -typedef struct -{ - ma_format format; - ma_uint32 channels; - ma_uint32 sampleRate; - double cutoffFrequency; - ma_uint32 order; /* If set to 0, will be treated as a passthrough (no filtering will be applied). */ -} ma_bpf_config; - -MA_API ma_bpf_config ma_bpf_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRate, double cutoffFrequency, ma_uint32 order); - -typedef struct -{ - ma_format format; - ma_uint32 channels; - ma_uint32 bpf2Count; - ma_bpf2* pBPF2; - - /* Memory management. */ - void* _pHeap; - ma_bool32 _ownsHeap; -} ma_bpf; - -MA_API ma_result ma_bpf_get_heap_size(const ma_bpf_config* pConfig, size_t* pHeapSizeInBytes); -MA_API ma_result ma_bpf_init_preallocated(const ma_bpf_config* pConfig, void* pHeap, ma_bpf* pBPF); -MA_API ma_result ma_bpf_init(const ma_bpf_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_bpf* pBPF); -MA_API void ma_bpf_uninit(ma_bpf* pBPF, const ma_allocation_callbacks* pAllocationCallbacks); -MA_API ma_result ma_bpf_reinit(const ma_bpf_config* pConfig, ma_bpf* pBPF); -MA_API ma_result ma_bpf_process_pcm_frames(ma_bpf* pBPF, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount); -MA_API ma_uint32 ma_bpf_get_latency(const ma_bpf* pBPF); - - -/************************************************************************************************************************************************************** - -Notching Filter - -**************************************************************************************************************************************************************/ -typedef struct -{ - ma_format format; - ma_uint32 channels; - ma_uint32 sampleRate; - double q; - double frequency; -} ma_notch2_config, ma_notch_config; - -MA_API ma_notch2_config ma_notch2_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRate, double q, double frequency); - -typedef struct -{ - ma_biquad bq; -} ma_notch2; - -MA_API ma_result ma_notch2_get_heap_size(const ma_notch2_config* pConfig, size_t* pHeapSizeInBytes); -MA_API ma_result ma_notch2_init_preallocated(const ma_notch2_config* pConfig, void* pHeap, ma_notch2* pFilter); -MA_API ma_result ma_notch2_init(const ma_notch2_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_notch2* pFilter); -MA_API void ma_notch2_uninit(ma_notch2* pFilter, const ma_allocation_callbacks* pAllocationCallbacks); -MA_API ma_result ma_notch2_reinit(const ma_notch2_config* pConfig, ma_notch2* pFilter); -MA_API ma_result ma_notch2_process_pcm_frames(ma_notch2* pFilter, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount); -MA_API ma_uint32 ma_notch2_get_latency(const ma_notch2* pFilter); - - -/************************************************************************************************************************************************************** - -Peaking EQ Filter - -**************************************************************************************************************************************************************/ -typedef struct -{ - ma_format format; - ma_uint32 channels; - ma_uint32 sampleRate; - double gainDB; - double q; - double frequency; -} ma_peak2_config, ma_peak_config; - -MA_API ma_peak2_config ma_peak2_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRate, double gainDB, double q, double frequency); - -typedef struct -{ - ma_biquad bq; -} ma_peak2; - -MA_API ma_result ma_peak2_get_heap_size(const ma_peak2_config* pConfig, size_t* pHeapSizeInBytes); -MA_API ma_result ma_peak2_init_preallocated(const ma_peak2_config* pConfig, void* pHeap, ma_peak2* pFilter); -MA_API ma_result ma_peak2_init(const ma_peak2_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_peak2* pFilter); -MA_API void ma_peak2_uninit(ma_peak2* pFilter, const ma_allocation_callbacks* pAllocationCallbacks); -MA_API ma_result ma_peak2_reinit(const ma_peak2_config* pConfig, ma_peak2* pFilter); -MA_API ma_result ma_peak2_process_pcm_frames(ma_peak2* pFilter, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount); -MA_API ma_uint32 ma_peak2_get_latency(const ma_peak2* pFilter); - - -/************************************************************************************************************************************************************** - -Low Shelf Filter - -**************************************************************************************************************************************************************/ -typedef struct -{ - ma_format format; - ma_uint32 channels; - ma_uint32 sampleRate; - double gainDB; - double shelfSlope; - double frequency; -} ma_loshelf2_config, ma_loshelf_config; - -MA_API ma_loshelf2_config ma_loshelf2_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRate, double gainDB, double shelfSlope, double frequency); - -typedef struct -{ - ma_biquad bq; -} ma_loshelf2; - -MA_API ma_result ma_loshelf2_get_heap_size(const ma_loshelf2_config* pConfig, size_t* pHeapSizeInBytes); -MA_API ma_result ma_loshelf2_init_preallocated(const ma_loshelf2_config* pConfig, void* pHeap, ma_loshelf2* pFilter); -MA_API ma_result ma_loshelf2_init(const ma_loshelf2_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_loshelf2* pFilter); -MA_API void ma_loshelf2_uninit(ma_loshelf2* pFilter, const ma_allocation_callbacks* pAllocationCallbacks); -MA_API ma_result ma_loshelf2_reinit(const ma_loshelf2_config* pConfig, ma_loshelf2* pFilter); -MA_API ma_result ma_loshelf2_process_pcm_frames(ma_loshelf2* pFilter, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount); -MA_API ma_uint32 ma_loshelf2_get_latency(const ma_loshelf2* pFilter); - - -/************************************************************************************************************************************************************** - -High Shelf Filter - -**************************************************************************************************************************************************************/ -typedef struct -{ - ma_format format; - ma_uint32 channels; - ma_uint32 sampleRate; - double gainDB; - double shelfSlope; - double frequency; -} ma_hishelf2_config, ma_hishelf_config; - -MA_API ma_hishelf2_config ma_hishelf2_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRate, double gainDB, double shelfSlope, double frequency); - -typedef struct -{ - ma_biquad bq; -} ma_hishelf2; - -MA_API ma_result ma_hishelf2_get_heap_size(const ma_hishelf2_config* pConfig, size_t* pHeapSizeInBytes); -MA_API ma_result ma_hishelf2_init_preallocated(const ma_hishelf2_config* pConfig, void* pHeap, ma_hishelf2* pFilter); -MA_API ma_result ma_hishelf2_init(const ma_hishelf2_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_hishelf2* pFilter); -MA_API void ma_hishelf2_uninit(ma_hishelf2* pFilter, const ma_allocation_callbacks* pAllocationCallbacks); -MA_API ma_result ma_hishelf2_reinit(const ma_hishelf2_config* pConfig, ma_hishelf2* pFilter); -MA_API ma_result ma_hishelf2_process_pcm_frames(ma_hishelf2* pFilter, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount); -MA_API ma_uint32 ma_hishelf2_get_latency(const ma_hishelf2* pFilter); - - - -/* -Delay -*/ -typedef struct -{ - ma_uint32 channels; - ma_uint32 sampleRate; - ma_uint32 delayInFrames; - ma_bool32 delayStart; /* Set to true to delay the start of the output; false otherwise. */ - float wet; /* 0..1. Default = 1. */ - float dry; /* 0..1. Default = 1. */ - float decay; /* 0..1. Default = 0 (no feedback). Feedback decay. Use this for echo. */ -} ma_delay_config; - -MA_API ma_delay_config ma_delay_config_init(ma_uint32 channels, ma_uint32 sampleRate, ma_uint32 delayInFrames, float decay); - - -typedef struct -{ - ma_delay_config config; - ma_uint32 cursor; /* Feedback is written to this cursor. Always equal or in front of the read cursor. */ - ma_uint32 bufferSizeInFrames; - float* pBuffer; -} ma_delay; - -MA_API ma_result ma_delay_init(const ma_delay_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_delay* pDelay); -MA_API void ma_delay_uninit(ma_delay* pDelay, const ma_allocation_callbacks* pAllocationCallbacks); -MA_API ma_result ma_delay_process_pcm_frames(ma_delay* pDelay, void* pFramesOut, const void* pFramesIn, ma_uint32 frameCount); -MA_API void ma_delay_set_wet(ma_delay* pDelay, float value); -MA_API float ma_delay_get_wet(const ma_delay* pDelay); -MA_API void ma_delay_set_dry(ma_delay* pDelay, float value); -MA_API float ma_delay_get_dry(const ma_delay* pDelay); -MA_API void ma_delay_set_decay(ma_delay* pDelay, float value); -MA_API float ma_delay_get_decay(const ma_delay* pDelay); - - -/* Gainer for smooth volume changes. */ -typedef struct -{ - ma_uint32 channels; - ma_uint32 smoothTimeInFrames; -} ma_gainer_config; - -MA_API ma_gainer_config ma_gainer_config_init(ma_uint32 channels, ma_uint32 smoothTimeInFrames); - - -typedef struct -{ - ma_gainer_config config; - ma_uint32 t; - float masterVolume; - float* pOldGains; - float* pNewGains; - - /* Memory management. */ - void* _pHeap; - ma_bool32 _ownsHeap; -} ma_gainer; - -MA_API ma_result ma_gainer_get_heap_size(const ma_gainer_config* pConfig, size_t* pHeapSizeInBytes); -MA_API ma_result ma_gainer_init_preallocated(const ma_gainer_config* pConfig, void* pHeap, ma_gainer* pGainer); -MA_API ma_result ma_gainer_init(const ma_gainer_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_gainer* pGainer); -MA_API void ma_gainer_uninit(ma_gainer* pGainer, const ma_allocation_callbacks* pAllocationCallbacks); -MA_API ma_result ma_gainer_process_pcm_frames(ma_gainer* pGainer, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount); -MA_API ma_result ma_gainer_set_gain(ma_gainer* pGainer, float newGain); -MA_API ma_result ma_gainer_set_gains(ma_gainer* pGainer, float* pNewGains); -MA_API ma_result ma_gainer_set_master_volume(ma_gainer* pGainer, float volume); -MA_API ma_result ma_gainer_get_master_volume(const ma_gainer* pGainer, float* pVolume); - - - -/* Stereo panner. */ -typedef enum -{ - ma_pan_mode_balance = 0, /* Does not blend one side with the other. Technically just a balance. Compatible with other popular audio engines and therefore the default. */ - ma_pan_mode_pan /* A true pan. The sound from one side will "move" to the other side and blend with it. */ -} ma_pan_mode; - -typedef struct -{ - ma_format format; - ma_uint32 channels; - ma_pan_mode mode; - float pan; -} ma_panner_config; - -MA_API ma_panner_config ma_panner_config_init(ma_format format, ma_uint32 channels); - - -typedef struct -{ - ma_format format; - ma_uint32 channels; - ma_pan_mode mode; - float pan; /* -1..1 where 0 is no pan, -1 is left side, +1 is right side. Defaults to 0. */ -} ma_panner; - -MA_API ma_result ma_panner_init(const ma_panner_config* pConfig, ma_panner* pPanner); -MA_API ma_result ma_panner_process_pcm_frames(ma_panner* pPanner, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount); -MA_API void ma_panner_set_mode(ma_panner* pPanner, ma_pan_mode mode); -MA_API ma_pan_mode ma_panner_get_mode(const ma_panner* pPanner); -MA_API void ma_panner_set_pan(ma_panner* pPanner, float pan); -MA_API float ma_panner_get_pan(const ma_panner* pPanner); - - - -/* Fader. */ -typedef struct -{ - ma_format format; - ma_uint32 channels; - ma_uint32 sampleRate; -} ma_fader_config; - -MA_API ma_fader_config ma_fader_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRate); - -typedef struct -{ - ma_fader_config config; - float volumeBeg; /* If volumeBeg and volumeEnd is equal to 1, no fading happens (ma_fader_process_pcm_frames() will run as a passthrough). */ - float volumeEnd; - ma_uint64 lengthInFrames; /* The total length of the fade. */ - ma_int64 cursorInFrames; /* The current time in frames. Incremented by ma_fader_process_pcm_frames(). Signed because it'll be offset by startOffsetInFrames in set_fade_ex(). */ -} ma_fader; - -MA_API ma_result ma_fader_init(const ma_fader_config* pConfig, ma_fader* pFader); -MA_API ma_result ma_fader_process_pcm_frames(ma_fader* pFader, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount); -MA_API void ma_fader_get_data_format(const ma_fader* pFader, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate); -MA_API void ma_fader_set_fade(ma_fader* pFader, float volumeBeg, float volumeEnd, ma_uint64 lengthInFrames); -MA_API void ma_fader_set_fade_ex(ma_fader* pFader, float volumeBeg, float volumeEnd, ma_uint64 lengthInFrames, ma_int64 startOffsetInFrames); -MA_API float ma_fader_get_current_volume(const ma_fader* pFader); - - - -/* Spatializer. */ -typedef struct -{ - float x; - float y; - float z; -} ma_vec3f; - -typedef struct -{ - ma_vec3f v; - ma_spinlock lock; -} ma_atomic_vec3f; - -typedef enum -{ - ma_attenuation_model_none, /* No distance attenuation and no spatialization. */ - ma_attenuation_model_inverse, /* Equivalent to OpenAL's AL_INVERSE_DISTANCE_CLAMPED. */ - ma_attenuation_model_linear, /* Linear attenuation. Equivalent to OpenAL's AL_LINEAR_DISTANCE_CLAMPED. */ - ma_attenuation_model_exponential /* Exponential attenuation. Equivalent to OpenAL's AL_EXPONENT_DISTANCE_CLAMPED. */ -} ma_attenuation_model; - -typedef enum -{ - ma_positioning_absolute, - ma_positioning_relative -} ma_positioning; - -typedef enum -{ - ma_handedness_right, - ma_handedness_left -} ma_handedness; - - -typedef struct -{ - ma_uint32 channelsOut; - ma_channel* pChannelMapOut; - ma_handedness handedness; /* Defaults to right. Forward is -1 on the Z axis. In a left handed system, forward is +1 on the Z axis. */ - float coneInnerAngleInRadians; - float coneOuterAngleInRadians; - float coneOuterGain; - float speedOfSound; - ma_vec3f worldUp; -} ma_spatializer_listener_config; - -MA_API ma_spatializer_listener_config ma_spatializer_listener_config_init(ma_uint32 channelsOut); - - -typedef struct -{ - ma_spatializer_listener_config config; - ma_atomic_vec3f position; /* The absolute position of the listener. */ - ma_atomic_vec3f direction; /* The direction the listener is facing. The world up vector is config.worldUp. */ - ma_atomic_vec3f velocity; - ma_bool32 isEnabled; - - /* Memory management. */ - ma_bool32 _ownsHeap; - void* _pHeap; -} ma_spatializer_listener; - -MA_API ma_result ma_spatializer_listener_get_heap_size(const ma_spatializer_listener_config* pConfig, size_t* pHeapSizeInBytes); -MA_API ma_result ma_spatializer_listener_init_preallocated(const ma_spatializer_listener_config* pConfig, void* pHeap, ma_spatializer_listener* pListener); -MA_API ma_result ma_spatializer_listener_init(const ma_spatializer_listener_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_spatializer_listener* pListener); -MA_API void ma_spatializer_listener_uninit(ma_spatializer_listener* pListener, const ma_allocation_callbacks* pAllocationCallbacks); -MA_API ma_channel* ma_spatializer_listener_get_channel_map(ma_spatializer_listener* pListener); -MA_API void ma_spatializer_listener_set_cone(ma_spatializer_listener* pListener, float innerAngleInRadians, float outerAngleInRadians, float outerGain); -MA_API void ma_spatializer_listener_get_cone(const ma_spatializer_listener* pListener, float* pInnerAngleInRadians, float* pOuterAngleInRadians, float* pOuterGain); -MA_API void ma_spatializer_listener_set_position(ma_spatializer_listener* pListener, float x, float y, float z); -MA_API ma_vec3f ma_spatializer_listener_get_position(const ma_spatializer_listener* pListener); -MA_API void ma_spatializer_listener_set_direction(ma_spatializer_listener* pListener, float x, float y, float z); -MA_API ma_vec3f ma_spatializer_listener_get_direction(const ma_spatializer_listener* pListener); -MA_API void ma_spatializer_listener_set_velocity(ma_spatializer_listener* pListener, float x, float y, float z); -MA_API ma_vec3f ma_spatializer_listener_get_velocity(const ma_spatializer_listener* pListener); -MA_API void ma_spatializer_listener_set_speed_of_sound(ma_spatializer_listener* pListener, float speedOfSound); -MA_API float ma_spatializer_listener_get_speed_of_sound(const ma_spatializer_listener* pListener); -MA_API void ma_spatializer_listener_set_world_up(ma_spatializer_listener* pListener, float x, float y, float z); -MA_API ma_vec3f ma_spatializer_listener_get_world_up(const ma_spatializer_listener* pListener); -MA_API void ma_spatializer_listener_set_enabled(ma_spatializer_listener* pListener, ma_bool32 isEnabled); -MA_API ma_bool32 ma_spatializer_listener_is_enabled(const ma_spatializer_listener* pListener); - - -typedef struct -{ - ma_uint32 channelsIn; - ma_uint32 channelsOut; - ma_channel* pChannelMapIn; - ma_attenuation_model attenuationModel; - ma_positioning positioning; - ma_handedness handedness; /* Defaults to right. Forward is -1 on the Z axis. In a left handed system, forward is +1 on the Z axis. */ - float minGain; - float maxGain; - float minDistance; - float maxDistance; - float rolloff; - float coneInnerAngleInRadians; - float coneOuterAngleInRadians; - float coneOuterGain; - float dopplerFactor; /* Set to 0 to disable doppler effect. */ - float directionalAttenuationFactor; /* Set to 0 to disable directional attenuation. */ - float minSpatializationChannelGain; /* The minimal scaling factor to apply to channel gains when accounting for the direction of the sound relative to the listener. Must be in the range of 0..1. Smaller values means more aggressive directional panning, larger values means more subtle directional panning. */ - ma_uint32 gainSmoothTimeInFrames; /* When the gain of a channel changes during spatialization, the transition will be linearly interpolated over this number of frames. */ -} ma_spatializer_config; - -MA_API ma_spatializer_config ma_spatializer_config_init(ma_uint32 channelsIn, ma_uint32 channelsOut); - - -typedef struct -{ - ma_uint32 channelsIn; - ma_uint32 channelsOut; - ma_channel* pChannelMapIn; - ma_attenuation_model attenuationModel; - ma_positioning positioning; - ma_handedness handedness; /* Defaults to right. Forward is -1 on the Z axis. In a left handed system, forward is +1 on the Z axis. */ - float minGain; - float maxGain; - float minDistance; - float maxDistance; - float rolloff; - float coneInnerAngleInRadians; - float coneOuterAngleInRadians; - float coneOuterGain; - float dopplerFactor; /* Set to 0 to disable doppler effect. */ - float directionalAttenuationFactor; /* Set to 0 to disable directional attenuation. */ - ma_uint32 gainSmoothTimeInFrames; /* When the gain of a channel changes during spatialization, the transition will be linearly interpolated over this number of frames. */ - ma_atomic_vec3f position; - ma_atomic_vec3f direction; - ma_atomic_vec3f velocity; /* For doppler effect. */ - float dopplerPitch; /* Will be updated by ma_spatializer_process_pcm_frames() and can be used by higher level functions to apply a pitch shift for doppler effect. */ - float minSpatializationChannelGain; - ma_gainer gainer; /* For smooth gain transitions. */ - float* pNewChannelGainsOut; /* An offset of _pHeap. Used by ma_spatializer_process_pcm_frames() to store new channel gains. The number of elements in this array is equal to config.channelsOut. */ - - /* Memory management. */ - void* _pHeap; - ma_bool32 _ownsHeap; -} ma_spatializer; - -MA_API ma_result ma_spatializer_get_heap_size(const ma_spatializer_config* pConfig, size_t* pHeapSizeInBytes); -MA_API ma_result ma_spatializer_init_preallocated(const ma_spatializer_config* pConfig, void* pHeap, ma_spatializer* pSpatializer); -MA_API ma_result ma_spatializer_init(const ma_spatializer_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_spatializer* pSpatializer); -MA_API void ma_spatializer_uninit(ma_spatializer* pSpatializer, const ma_allocation_callbacks* pAllocationCallbacks); -MA_API ma_result ma_spatializer_process_pcm_frames(ma_spatializer* pSpatializer, ma_spatializer_listener* pListener, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount); -MA_API ma_result ma_spatializer_set_master_volume(ma_spatializer* pSpatializer, float volume); -MA_API ma_result ma_spatializer_get_master_volume(const ma_spatializer* pSpatializer, float* pVolume); -MA_API ma_uint32 ma_spatializer_get_input_channels(const ma_spatializer* pSpatializer); -MA_API ma_uint32 ma_spatializer_get_output_channels(const ma_spatializer* pSpatializer); -MA_API void ma_spatializer_set_attenuation_model(ma_spatializer* pSpatializer, ma_attenuation_model attenuationModel); -MA_API ma_attenuation_model ma_spatializer_get_attenuation_model(const ma_spatializer* pSpatializer); -MA_API void ma_spatializer_set_positioning(ma_spatializer* pSpatializer, ma_positioning positioning); -MA_API ma_positioning ma_spatializer_get_positioning(const ma_spatializer* pSpatializer); -MA_API void ma_spatializer_set_rolloff(ma_spatializer* pSpatializer, float rolloff); -MA_API float ma_spatializer_get_rolloff(const ma_spatializer* pSpatializer); -MA_API void ma_spatializer_set_min_gain(ma_spatializer* pSpatializer, float minGain); -MA_API float ma_spatializer_get_min_gain(const ma_spatializer* pSpatializer); -MA_API void ma_spatializer_set_max_gain(ma_spatializer* pSpatializer, float maxGain); -MA_API float ma_spatializer_get_max_gain(const ma_spatializer* pSpatializer); -MA_API void ma_spatializer_set_min_distance(ma_spatializer* pSpatializer, float minDistance); -MA_API float ma_spatializer_get_min_distance(const ma_spatializer* pSpatializer); -MA_API void ma_spatializer_set_max_distance(ma_spatializer* pSpatializer, float maxDistance); -MA_API float ma_spatializer_get_max_distance(const ma_spatializer* pSpatializer); -MA_API void ma_spatializer_set_cone(ma_spatializer* pSpatializer, float innerAngleInRadians, float outerAngleInRadians, float outerGain); -MA_API void ma_spatializer_get_cone(const ma_spatializer* pSpatializer, float* pInnerAngleInRadians, float* pOuterAngleInRadians, float* pOuterGain); -MA_API void ma_spatializer_set_doppler_factor(ma_spatializer* pSpatializer, float dopplerFactor); -MA_API float ma_spatializer_get_doppler_factor(const ma_spatializer* pSpatializer); -MA_API void ma_spatializer_set_directional_attenuation_factor(ma_spatializer* pSpatializer, float directionalAttenuationFactor); -MA_API float ma_spatializer_get_directional_attenuation_factor(const ma_spatializer* pSpatializer); -MA_API void ma_spatializer_set_position(ma_spatializer* pSpatializer, float x, float y, float z); -MA_API ma_vec3f ma_spatializer_get_position(const ma_spatializer* pSpatializer); -MA_API void ma_spatializer_set_direction(ma_spatializer* pSpatializer, float x, float y, float z); -MA_API ma_vec3f ma_spatializer_get_direction(const ma_spatializer* pSpatializer); -MA_API void ma_spatializer_set_velocity(ma_spatializer* pSpatializer, float x, float y, float z); -MA_API ma_vec3f ma_spatializer_get_velocity(const ma_spatializer* pSpatializer); -MA_API void ma_spatializer_get_relative_position_and_direction(const ma_spatializer* pSpatializer, const ma_spatializer_listener* pListener, ma_vec3f* pRelativePos, ma_vec3f* pRelativeDir); - - - -/************************************************************************************************************************************************************ -************************************************************************************************************************************************************* - -DATA CONVERSION -=============== - -This section contains the APIs for data conversion. You will find everything here for channel mapping, sample format conversion, resampling, etc. - -************************************************************************************************************************************************************* -************************************************************************************************************************************************************/ - -/************************************************************************************************************************************************************** - -Resampling - -**************************************************************************************************************************************************************/ -typedef struct -{ - ma_format format; - ma_uint32 channels; - ma_uint32 sampleRateIn; - ma_uint32 sampleRateOut; - ma_uint32 lpfOrder; /* The low-pass filter order. Setting this to 0 will disable low-pass filtering. */ - double lpfNyquistFactor; /* 0..1. Defaults to 1. 1 = Half the sampling frequency (Nyquist Frequency), 0.5 = Quarter the sampling frequency (half Nyquest Frequency), etc. */ -} ma_linear_resampler_config; - -MA_API ma_linear_resampler_config ma_linear_resampler_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRateIn, ma_uint32 sampleRateOut); - -typedef struct -{ - ma_linear_resampler_config config; - ma_uint32 inAdvanceInt; - ma_uint32 inAdvanceFrac; - ma_uint32 inTimeInt; - ma_uint32 inTimeFrac; - union - { - float* f32; - ma_int16* s16; - } x0; /* The previous input frame. */ - union - { - float* f32; - ma_int16* s16; - } x1; /* The next input frame. */ - ma_lpf lpf; - - /* Memory management. */ - void* _pHeap; - ma_bool32 _ownsHeap; -} ma_linear_resampler; - -MA_API ma_result ma_linear_resampler_get_heap_size(const ma_linear_resampler_config* pConfig, size_t* pHeapSizeInBytes); -MA_API ma_result ma_linear_resampler_init_preallocated(const ma_linear_resampler_config* pConfig, void* pHeap, ma_linear_resampler* pResampler); -MA_API ma_result ma_linear_resampler_init(const ma_linear_resampler_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_linear_resampler* pResampler); -MA_API void ma_linear_resampler_uninit(ma_linear_resampler* pResampler, const ma_allocation_callbacks* pAllocationCallbacks); -MA_API ma_result ma_linear_resampler_process_pcm_frames(ma_linear_resampler* pResampler, const void* pFramesIn, ma_uint64* pFrameCountIn, void* pFramesOut, ma_uint64* pFrameCountOut); -MA_API ma_result ma_linear_resampler_set_rate(ma_linear_resampler* pResampler, ma_uint32 sampleRateIn, ma_uint32 sampleRateOut); -MA_API ma_result ma_linear_resampler_set_rate_ratio(ma_linear_resampler* pResampler, float ratioInOut); -MA_API ma_uint64 ma_linear_resampler_get_input_latency(const ma_linear_resampler* pResampler); -MA_API ma_uint64 ma_linear_resampler_get_output_latency(const ma_linear_resampler* pResampler); -MA_API ma_result ma_linear_resampler_get_required_input_frame_count(const ma_linear_resampler* pResampler, ma_uint64 outputFrameCount, ma_uint64* pInputFrameCount); -MA_API ma_result ma_linear_resampler_get_expected_output_frame_count(const ma_linear_resampler* pResampler, ma_uint64 inputFrameCount, ma_uint64* pOutputFrameCount); -MA_API ma_result ma_linear_resampler_reset(ma_linear_resampler* pResampler); - - -typedef struct ma_resampler_config ma_resampler_config; - -typedef void ma_resampling_backend; -typedef struct -{ - ma_result (* onGetHeapSize )(void* pUserData, const ma_resampler_config* pConfig, size_t* pHeapSizeInBytes); - ma_result (* onInit )(void* pUserData, const ma_resampler_config* pConfig, void* pHeap, ma_resampling_backend** ppBackend); - void (* onUninit )(void* pUserData, ma_resampling_backend* pBackend, const ma_allocation_callbacks* pAllocationCallbacks); - ma_result (* onProcess )(void* pUserData, ma_resampling_backend* pBackend, const void* pFramesIn, ma_uint64* pFrameCountIn, void* pFramesOut, ma_uint64* pFrameCountOut); - ma_result (* onSetRate )(void* pUserData, ma_resampling_backend* pBackend, ma_uint32 sampleRateIn, ma_uint32 sampleRateOut); /* Optional. Rate changes will be disabled. */ - ma_uint64 (* onGetInputLatency )(void* pUserData, const ma_resampling_backend* pBackend); /* Optional. Latency will be reported as 0. */ - ma_uint64 (* onGetOutputLatency )(void* pUserData, const ma_resampling_backend* pBackend); /* Optional. Latency will be reported as 0. */ - ma_result (* onGetRequiredInputFrameCount )(void* pUserData, const ma_resampling_backend* pBackend, ma_uint64 outputFrameCount, ma_uint64* pInputFrameCount); /* Optional. Latency mitigation will be disabled. */ - ma_result (* onGetExpectedOutputFrameCount)(void* pUserData, const ma_resampling_backend* pBackend, ma_uint64 inputFrameCount, ma_uint64* pOutputFrameCount); /* Optional. Latency mitigation will be disabled. */ - ma_result (* onReset )(void* pUserData, ma_resampling_backend* pBackend); -} ma_resampling_backend_vtable; - -typedef enum -{ - ma_resample_algorithm_linear = 0, /* Fastest, lowest quality. Optional low-pass filtering. Default. */ - ma_resample_algorithm_custom, -} ma_resample_algorithm; - -struct ma_resampler_config -{ - ma_format format; /* Must be either ma_format_f32 or ma_format_s16. */ - ma_uint32 channels; - ma_uint32 sampleRateIn; - ma_uint32 sampleRateOut; - ma_resample_algorithm algorithm; /* When set to ma_resample_algorithm_custom, pBackendVTable will be used. */ - ma_resampling_backend_vtable* pBackendVTable; - void* pBackendUserData; - struct - { - ma_uint32 lpfOrder; - } linear; -}; - -MA_API ma_resampler_config ma_resampler_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRateIn, ma_uint32 sampleRateOut, ma_resample_algorithm algorithm); - -typedef struct -{ - ma_resampling_backend* pBackend; - ma_resampling_backend_vtable* pBackendVTable; - void* pBackendUserData; - ma_format format; - ma_uint32 channels; - ma_uint32 sampleRateIn; - ma_uint32 sampleRateOut; - union - { - ma_linear_resampler linear; - } state; /* State for stock resamplers so we can avoid a malloc. For stock resamplers, pBackend will point here. */ - - /* Memory management. */ - void* _pHeap; - ma_bool32 _ownsHeap; -} ma_resampler; - -MA_API ma_result ma_resampler_get_heap_size(const ma_resampler_config* pConfig, size_t* pHeapSizeInBytes); -MA_API ma_result ma_resampler_init_preallocated(const ma_resampler_config* pConfig, void* pHeap, ma_resampler* pResampler); - -/* -Initializes a new resampler object from a config. -*/ -MA_API ma_result ma_resampler_init(const ma_resampler_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_resampler* pResampler); - -/* -Uninitializes a resampler. -*/ -MA_API void ma_resampler_uninit(ma_resampler* pResampler, const ma_allocation_callbacks* pAllocationCallbacks); - -/* -Converts the given input data. - -Both the input and output frames must be in the format specified in the config when the resampler was initialized. - -On input, [pFrameCountOut] contains the number of output frames to process. On output it contains the number of output frames that -were actually processed, which may be less than the requested amount which will happen if there's not enough input data. You can use -ma_resampler_get_expected_output_frame_count() to know how many output frames will be processed for a given number of input frames. - -On input, [pFrameCountIn] contains the number of input frames contained in [pFramesIn]. On output it contains the number of whole -input frames that were actually processed. You can use ma_resampler_get_required_input_frame_count() to know how many input frames -you should provide for a given number of output frames. [pFramesIn] can be NULL, in which case zeroes will be used instead. - -If [pFramesOut] is NULL, a seek is performed. In this case, if [pFrameCountOut] is not NULL it will seek by the specified number of -output frames. Otherwise, if [pFramesCountOut] is NULL and [pFrameCountIn] is not NULL, it will seek by the specified number of input -frames. When seeking, [pFramesIn] is allowed to NULL, in which case the internal timing state will be updated, but no input will be -processed. In this case, any internal filter state will be updated as if zeroes were passed in. - -It is an error for [pFramesOut] to be non-NULL and [pFrameCountOut] to be NULL. - -It is an error for both [pFrameCountOut] and [pFrameCountIn] to be NULL. -*/ -MA_API ma_result ma_resampler_process_pcm_frames(ma_resampler* pResampler, const void* pFramesIn, ma_uint64* pFrameCountIn, void* pFramesOut, ma_uint64* pFrameCountOut); - - -/* -Sets the input and output sample rate. -*/ -MA_API ma_result ma_resampler_set_rate(ma_resampler* pResampler, ma_uint32 sampleRateIn, ma_uint32 sampleRateOut); - -/* -Sets the input and output sample rate as a ratio. - -The ration is in/out. -*/ -MA_API ma_result ma_resampler_set_rate_ratio(ma_resampler* pResampler, float ratio); - -/* -Retrieves the latency introduced by the resampler in input frames. -*/ -MA_API ma_uint64 ma_resampler_get_input_latency(const ma_resampler* pResampler); - -/* -Retrieves the latency introduced by the resampler in output frames. -*/ -MA_API ma_uint64 ma_resampler_get_output_latency(const ma_resampler* pResampler); - -/* -Calculates the number of whole input frames that would need to be read from the client in order to output the specified -number of output frames. - -The returned value does not include cached input frames. It only returns the number of extra frames that would need to be -read from the input buffer in order to output the specified number of output frames. -*/ -MA_API ma_result ma_resampler_get_required_input_frame_count(const ma_resampler* pResampler, ma_uint64 outputFrameCount, ma_uint64* pInputFrameCount); - -/* -Calculates the number of whole output frames that would be output after fully reading and consuming the specified number of -input frames. -*/ -MA_API ma_result ma_resampler_get_expected_output_frame_count(const ma_resampler* pResampler, ma_uint64 inputFrameCount, ma_uint64* pOutputFrameCount); - -/* -Resets the resampler's timer and clears its internal cache. -*/ -MA_API ma_result ma_resampler_reset(ma_resampler* pResampler); - - -/************************************************************************************************************************************************************** - -Channel Conversion - -**************************************************************************************************************************************************************/ -typedef enum -{ - ma_channel_conversion_path_unknown, - ma_channel_conversion_path_passthrough, - ma_channel_conversion_path_mono_out, /* Converting to mono. */ - ma_channel_conversion_path_mono_in, /* Converting from mono. */ - ma_channel_conversion_path_shuffle, /* Simple shuffle. Will use this when all channels are present in both input and output channel maps, but just in a different order. */ - ma_channel_conversion_path_weights /* Blended based on weights. */ -} ma_channel_conversion_path; - -typedef enum -{ - ma_mono_expansion_mode_duplicate = 0, /* The default. */ - ma_mono_expansion_mode_average, /* Average the mono channel across all channels. */ - ma_mono_expansion_mode_stereo_only, /* Duplicate to the left and right channels only and ignore the others. */ - ma_mono_expansion_mode_default = ma_mono_expansion_mode_duplicate -} ma_mono_expansion_mode; - -typedef struct -{ - ma_format format; - ma_uint32 channelsIn; - ma_uint32 channelsOut; - const ma_channel* pChannelMapIn; - const ma_channel* pChannelMapOut; - ma_channel_mix_mode mixingMode; - ma_bool32 calculateLFEFromSpatialChannels; /* When an output LFE channel is present, but no input LFE, set to true to set the output LFE to the average of all spatial channels (LR, FR, etc.). Ignored when an input LFE is present. */ - float** ppWeights; /* [in][out]. Only used when mixingMode is set to ma_channel_mix_mode_custom_weights. */ -} ma_channel_converter_config; - -MA_API ma_channel_converter_config ma_channel_converter_config_init(ma_format format, ma_uint32 channelsIn, const ma_channel* pChannelMapIn, ma_uint32 channelsOut, const ma_channel* pChannelMapOut, ma_channel_mix_mode mixingMode); - -typedef struct -{ - ma_format format; - ma_uint32 channelsIn; - ma_uint32 channelsOut; - ma_channel_mix_mode mixingMode; - ma_channel_conversion_path conversionPath; - ma_channel* pChannelMapIn; - ma_channel* pChannelMapOut; - ma_uint8* pShuffleTable; /* Indexed by output channel index. */ - union - { - float** f32; - ma_int32** s16; - } weights; /* [in][out] */ - - /* Memory management. */ - void* _pHeap; - ma_bool32 _ownsHeap; -} ma_channel_converter; - -MA_API ma_result ma_channel_converter_get_heap_size(const ma_channel_converter_config* pConfig, size_t* pHeapSizeInBytes); -MA_API ma_result ma_channel_converter_init_preallocated(const ma_channel_converter_config* pConfig, void* pHeap, ma_channel_converter* pConverter); -MA_API ma_result ma_channel_converter_init(const ma_channel_converter_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_channel_converter* pConverter); -MA_API void ma_channel_converter_uninit(ma_channel_converter* pConverter, const ma_allocation_callbacks* pAllocationCallbacks); -MA_API ma_result ma_channel_converter_process_pcm_frames(ma_channel_converter* pConverter, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount); -MA_API ma_result ma_channel_converter_get_input_channel_map(const ma_channel_converter* pConverter, ma_channel* pChannelMap, size_t channelMapCap); -MA_API ma_result ma_channel_converter_get_output_channel_map(const ma_channel_converter* pConverter, ma_channel* pChannelMap, size_t channelMapCap); - - -/************************************************************************************************************************************************************** - -Data Conversion - -**************************************************************************************************************************************************************/ -typedef struct -{ - ma_format formatIn; - ma_format formatOut; - ma_uint32 channelsIn; - ma_uint32 channelsOut; - ma_uint32 sampleRateIn; - ma_uint32 sampleRateOut; - ma_channel* pChannelMapIn; - ma_channel* pChannelMapOut; - ma_dither_mode ditherMode; - ma_channel_mix_mode channelMixMode; - ma_bool32 calculateLFEFromSpatialChannels; /* When an output LFE channel is present, but no input LFE, set to true to set the output LFE to the average of all spatial channels (LR, FR, etc.). Ignored when an input LFE is present. */ - float** ppChannelWeights; /* [in][out]. Only used when mixingMode is set to ma_channel_mix_mode_custom_weights. */ - ma_bool32 allowDynamicSampleRate; - ma_resampler_config resampling; -} ma_data_converter_config; - -MA_API ma_data_converter_config ma_data_converter_config_init_default(void); -MA_API ma_data_converter_config ma_data_converter_config_init(ma_format formatIn, ma_format formatOut, ma_uint32 channelsIn, ma_uint32 channelsOut, ma_uint32 sampleRateIn, ma_uint32 sampleRateOut); - - -typedef enum -{ - ma_data_converter_execution_path_passthrough, /* No conversion. */ - ma_data_converter_execution_path_format_only, /* Only format conversion. */ - ma_data_converter_execution_path_channels_only, /* Only channel conversion. */ - ma_data_converter_execution_path_resample_only, /* Only resampling. */ - ma_data_converter_execution_path_resample_first, /* All conversions, but resample as the first step. */ - ma_data_converter_execution_path_channels_first /* All conversions, but channels as the first step. */ -} ma_data_converter_execution_path; - -typedef struct -{ - ma_format formatIn; - ma_format formatOut; - ma_uint32 channelsIn; - ma_uint32 channelsOut; - ma_uint32 sampleRateIn; - ma_uint32 sampleRateOut; - ma_dither_mode ditherMode; - ma_data_converter_execution_path executionPath; /* The execution path the data converter will follow when processing. */ - ma_channel_converter channelConverter; - ma_resampler resampler; - ma_bool8 hasPreFormatConversion; - ma_bool8 hasPostFormatConversion; - ma_bool8 hasChannelConverter; - ma_bool8 hasResampler; - ma_bool8 isPassthrough; - - /* Memory management. */ - ma_bool8 _ownsHeap; - void* _pHeap; -} ma_data_converter; - -MA_API ma_result ma_data_converter_get_heap_size(const ma_data_converter_config* pConfig, size_t* pHeapSizeInBytes); -MA_API ma_result ma_data_converter_init_preallocated(const ma_data_converter_config* pConfig, void* pHeap, ma_data_converter* pConverter); -MA_API ma_result ma_data_converter_init(const ma_data_converter_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_data_converter* pConverter); -MA_API void ma_data_converter_uninit(ma_data_converter* pConverter, const ma_allocation_callbacks* pAllocationCallbacks); -MA_API ma_result ma_data_converter_process_pcm_frames(ma_data_converter* pConverter, const void* pFramesIn, ma_uint64* pFrameCountIn, void* pFramesOut, ma_uint64* pFrameCountOut); -MA_API ma_result ma_data_converter_set_rate(ma_data_converter* pConverter, ma_uint32 sampleRateIn, ma_uint32 sampleRateOut); -MA_API ma_result ma_data_converter_set_rate_ratio(ma_data_converter* pConverter, float ratioInOut); -MA_API ma_uint64 ma_data_converter_get_input_latency(const ma_data_converter* pConverter); -MA_API ma_uint64 ma_data_converter_get_output_latency(const ma_data_converter* pConverter); -MA_API ma_result ma_data_converter_get_required_input_frame_count(const ma_data_converter* pConverter, ma_uint64 outputFrameCount, ma_uint64* pInputFrameCount); -MA_API ma_result ma_data_converter_get_expected_output_frame_count(const ma_data_converter* pConverter, ma_uint64 inputFrameCount, ma_uint64* pOutputFrameCount); -MA_API ma_result ma_data_converter_get_input_channel_map(const ma_data_converter* pConverter, ma_channel* pChannelMap, size_t channelMapCap); -MA_API ma_result ma_data_converter_get_output_channel_map(const ma_data_converter* pConverter, ma_channel* pChannelMap, size_t channelMapCap); -MA_API ma_result ma_data_converter_reset(ma_data_converter* pConverter); - - -/************************************************************************************************************************************************************ - -Format Conversion - -************************************************************************************************************************************************************/ -MA_API void ma_pcm_u8_to_s16(void* pOut, const void* pIn, ma_uint64 count, ma_dither_mode ditherMode); -MA_API void ma_pcm_u8_to_s24(void* pOut, const void* pIn, ma_uint64 count, ma_dither_mode ditherMode); -MA_API void ma_pcm_u8_to_s32(void* pOut, const void* pIn, ma_uint64 count, ma_dither_mode ditherMode); -MA_API void ma_pcm_u8_to_f32(void* pOut, const void* pIn, ma_uint64 count, ma_dither_mode ditherMode); -MA_API void ma_pcm_s16_to_u8(void* pOut, const void* pIn, ma_uint64 count, ma_dither_mode ditherMode); -MA_API void ma_pcm_s16_to_s24(void* pOut, const void* pIn, ma_uint64 count, ma_dither_mode ditherMode); -MA_API void ma_pcm_s16_to_s32(void* pOut, const void* pIn, ma_uint64 count, ma_dither_mode ditherMode); -MA_API void ma_pcm_s16_to_f32(void* pOut, const void* pIn, ma_uint64 count, ma_dither_mode ditherMode); -MA_API void ma_pcm_s24_to_u8(void* pOut, const void* pIn, ma_uint64 count, ma_dither_mode ditherMode); -MA_API void ma_pcm_s24_to_s16(void* pOut, const void* pIn, ma_uint64 count, ma_dither_mode ditherMode); -MA_API void ma_pcm_s24_to_s32(void* pOut, const void* pIn, ma_uint64 count, ma_dither_mode ditherMode); -MA_API void ma_pcm_s24_to_f32(void* pOut, const void* pIn, ma_uint64 count, ma_dither_mode ditherMode); -MA_API void ma_pcm_s32_to_u8(void* pOut, const void* pIn, ma_uint64 count, ma_dither_mode ditherMode); -MA_API void ma_pcm_s32_to_s16(void* pOut, const void* pIn, ma_uint64 count, ma_dither_mode ditherMode); -MA_API void ma_pcm_s32_to_s24(void* pOut, const void* pIn, ma_uint64 count, ma_dither_mode ditherMode); -MA_API void ma_pcm_s32_to_f32(void* pOut, const void* pIn, ma_uint64 count, ma_dither_mode ditherMode); -MA_API void ma_pcm_f32_to_u8(void* pOut, const void* pIn, ma_uint64 count, ma_dither_mode ditherMode); -MA_API void ma_pcm_f32_to_s16(void* pOut, const void* pIn, ma_uint64 count, ma_dither_mode ditherMode); -MA_API void ma_pcm_f32_to_s24(void* pOut, const void* pIn, ma_uint64 count, ma_dither_mode ditherMode); -MA_API void ma_pcm_f32_to_s32(void* pOut, const void* pIn, ma_uint64 count, ma_dither_mode ditherMode); -MA_API void ma_pcm_convert(void* pOut, ma_format formatOut, const void* pIn, ma_format formatIn, ma_uint64 sampleCount, ma_dither_mode ditherMode); -MA_API void ma_convert_pcm_frames_format(void* pOut, ma_format formatOut, const void* pIn, ma_format formatIn, ma_uint64 frameCount, ma_uint32 channels, ma_dither_mode ditherMode); - -/* -Deinterleaves an interleaved buffer. -*/ -MA_API void ma_deinterleave_pcm_frames(ma_format format, ma_uint32 channels, ma_uint64 frameCount, const void* pInterleavedPCMFrames, void** ppDeinterleavedPCMFrames); - -/* -Interleaves a group of deinterleaved buffers. -*/ -MA_API void ma_interleave_pcm_frames(ma_format format, ma_uint32 channels, ma_uint64 frameCount, const void** ppDeinterleavedPCMFrames, void* pInterleavedPCMFrames); - - -/************************************************************************************************************************************************************ - -Channel Maps - -************************************************************************************************************************************************************/ -/* -This is used in the shuffle table to indicate that the channel index is undefined and should be ignored. -*/ -#define MA_CHANNEL_INDEX_NULL 255 - -/* -Retrieves the channel position of the specified channel in the given channel map. - -The pChannelMap parameter can be null, in which case miniaudio's default channel map will be assumed. -*/ -MA_API ma_channel ma_channel_map_get_channel(const ma_channel* pChannelMap, ma_uint32 channelCount, ma_uint32 channelIndex); - -/* -Initializes a blank channel map. - -When a blank channel map is specified anywhere it indicates that the native channel map should be used. -*/ -MA_API void ma_channel_map_init_blank(ma_channel* pChannelMap, ma_uint32 channels); - -/* -Helper for retrieving a standard channel map. - -The output channel map buffer must have a capacity of at least `channelMapCap`. -*/ -MA_API void ma_channel_map_init_standard(ma_standard_channel_map standardChannelMap, ma_channel* pChannelMap, size_t channelMapCap, ma_uint32 channels); - -/* -Copies a channel map. - -Both input and output channel map buffers must have a capacity of at least `channels`. -*/ -MA_API void ma_channel_map_copy(ma_channel* pOut, const ma_channel* pIn, ma_uint32 channels); - -/* -Copies a channel map if one is specified, otherwise copies the default channel map. - -The output buffer must have a capacity of at least `channels`. If not NULL, the input channel map must also have a capacity of at least `channels`. -*/ -MA_API void ma_channel_map_copy_or_default(ma_channel* pOut, size_t channelMapCapOut, const ma_channel* pIn, ma_uint32 channels); - - -/* -Determines whether or not a channel map is valid. - -A blank channel map is valid (all channels set to MA_CHANNEL_NONE). The way a blank channel map is handled is context specific, but -is usually treated as a passthrough. - -Invalid channel maps: - - A channel map with no channels - - A channel map with more than one channel and a mono channel - -The channel map buffer must have a capacity of at least `channels`. -*/ -MA_API ma_bool32 ma_channel_map_is_valid(const ma_channel* pChannelMap, ma_uint32 channels); - -/* -Helper for comparing two channel maps for equality. - -This assumes the channel count is the same between the two. - -Both channels map buffers must have a capacity of at least `channels`. -*/ -MA_API ma_bool32 ma_channel_map_is_equal(const ma_channel* pChannelMapA, const ma_channel* pChannelMapB, ma_uint32 channels); - -/* -Helper for determining if a channel map is blank (all channels set to MA_CHANNEL_NONE). - -The channel map buffer must have a capacity of at least `channels`. -*/ -MA_API ma_bool32 ma_channel_map_is_blank(const ma_channel* pChannelMap, ma_uint32 channels); - -/* -Helper for determining whether or not a channel is present in the given channel map. - -The channel map buffer must have a capacity of at least `channels`. -*/ -MA_API ma_bool32 ma_channel_map_contains_channel_position(ma_uint32 channels, const ma_channel* pChannelMap, ma_channel channelPosition); - -/* -Find a channel position in the given channel map. Returns MA_TRUE if the channel is found; MA_FALSE otherwise. The -index of the channel is output to `pChannelIndex`. - -The channel map buffer must have a capacity of at least `channels`. -*/ -MA_API ma_bool32 ma_channel_map_find_channel_position(ma_uint32 channels, const ma_channel* pChannelMap, ma_channel channelPosition, ma_uint32* pChannelIndex); - -/* -Generates a string representing the given channel map. - -This is for printing and debugging purposes, not serialization/deserialization. - -Returns the length of the string, not including the null terminator. -*/ -MA_API size_t ma_channel_map_to_string(const ma_channel* pChannelMap, ma_uint32 channels, char* pBufferOut, size_t bufferCap); - -/* -Retrieves a human readable version of a channel position. -*/ -MA_API const char* ma_channel_position_to_string(ma_channel channel); - - -/************************************************************************************************************************************************************ - -Conversion Helpers - -************************************************************************************************************************************************************/ - -/* -High-level helper for doing a full format conversion in one go. Returns the number of output frames. Call this with pOut set to NULL to -determine the required size of the output buffer. frameCountOut should be set to the capacity of pOut. If pOut is NULL, frameCountOut is -ignored. - -A return value of 0 indicates an error. - -This function is useful for one-off bulk conversions, but if you're streaming data you should use the ma_data_converter APIs instead. -*/ -MA_API ma_uint64 ma_convert_frames(void* pOut, ma_uint64 frameCountOut, ma_format formatOut, ma_uint32 channelsOut, ma_uint32 sampleRateOut, const void* pIn, ma_uint64 frameCountIn, ma_format formatIn, ma_uint32 channelsIn, ma_uint32 sampleRateIn); -MA_API ma_uint64 ma_convert_frames_ex(void* pOut, ma_uint64 frameCountOut, const void* pIn, ma_uint64 frameCountIn, const ma_data_converter_config* pConfig); - - -/************************************************************************************************************************************************************ - -Data Source - -************************************************************************************************************************************************************/ -typedef void ma_data_source; - -#define MA_DATA_SOURCE_SELF_MANAGED_RANGE_AND_LOOP_POINT 0x00000001 - -typedef struct -{ - ma_result (* onRead)(ma_data_source* pDataSource, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead); - ma_result (* onSeek)(ma_data_source* pDataSource, ma_uint64 frameIndex); - ma_result (* onGetDataFormat)(ma_data_source* pDataSource, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate, ma_channel* pChannelMap, size_t channelMapCap); - ma_result (* onGetCursor)(ma_data_source* pDataSource, ma_uint64* pCursor); - ma_result (* onGetLength)(ma_data_source* pDataSource, ma_uint64* pLength); - ma_result (* onSetLooping)(ma_data_source* pDataSource, ma_bool32 isLooping); - ma_uint32 flags; -} ma_data_source_vtable; - -typedef ma_data_source* (* ma_data_source_get_next_proc)(ma_data_source* pDataSource); - -typedef struct -{ - const ma_data_source_vtable* vtable; -} ma_data_source_config; - -MA_API ma_data_source_config ma_data_source_config_init(void); - - -typedef struct -{ - const ma_data_source_vtable* vtable; - ma_uint64 rangeBegInFrames; - ma_uint64 rangeEndInFrames; /* Set to -1 for unranged (default). */ - ma_uint64 loopBegInFrames; /* Relative to rangeBegInFrames. */ - ma_uint64 loopEndInFrames; /* Relative to rangeBegInFrames. Set to -1 for the end of the range. */ - ma_data_source* pCurrent; /* When non-NULL, the data source being initialized will act as a proxy and will route all operations to pCurrent. Used in conjunction with pNext/onGetNext for seamless chaining. */ - ma_data_source* pNext; /* When set to NULL, onGetNext will be used. */ - ma_data_source_get_next_proc onGetNext; /* Will be used when pNext is NULL. If both are NULL, no next will be used. */ - MA_ATOMIC(4, ma_bool32) isLooping; -} ma_data_source_base; - -MA_API ma_result ma_data_source_init(const ma_data_source_config* pConfig, ma_data_source* pDataSource); -MA_API void ma_data_source_uninit(ma_data_source* pDataSource); -MA_API ma_result ma_data_source_read_pcm_frames(ma_data_source* pDataSource, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead); /* Must support pFramesOut = NULL in which case a forward seek should be performed. */ -MA_API ma_result ma_data_source_seek_pcm_frames(ma_data_source* pDataSource, ma_uint64 frameCount, ma_uint64* pFramesSeeked); /* Can only seek forward. Equivalent to ma_data_source_read_pcm_frames(pDataSource, NULL, frameCount, &framesRead); */ -MA_API ma_result ma_data_source_seek_to_pcm_frame(ma_data_source* pDataSource, ma_uint64 frameIndex); -MA_API ma_result ma_data_source_seek_seconds(ma_data_source* pDataSource, float secondCount, float* pSecondsSeeked); /* Can only seek forward. Abstraction to ma_data_source_seek_pcm_frames() */ -MA_API ma_result ma_data_source_seek_to_second(ma_data_source* pDataSource, float seekPointInSeconds); /* Abstraction to ma_data_source_seek_to_pcm_frame() */ -MA_API ma_result ma_data_source_get_data_format(ma_data_source* pDataSource, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate, ma_channel* pChannelMap, size_t channelMapCap); -MA_API ma_result ma_data_source_get_cursor_in_pcm_frames(ma_data_source* pDataSource, ma_uint64* pCursor); -MA_API ma_result ma_data_source_get_length_in_pcm_frames(ma_data_source* pDataSource, ma_uint64* pLength); /* Returns MA_NOT_IMPLEMENTED if the length is unknown or cannot be determined. Decoders can return this. */ -MA_API ma_result ma_data_source_get_cursor_in_seconds(ma_data_source* pDataSource, float* pCursor); -MA_API ma_result ma_data_source_get_length_in_seconds(ma_data_source* pDataSource, float* pLength); -MA_API ma_result ma_data_source_set_looping(ma_data_source* pDataSource, ma_bool32 isLooping); -MA_API ma_bool32 ma_data_source_is_looping(const ma_data_source* pDataSource); -MA_API ma_result ma_data_source_set_range_in_pcm_frames(ma_data_source* pDataSource, ma_uint64 rangeBegInFrames, ma_uint64 rangeEndInFrames); -MA_API void ma_data_source_get_range_in_pcm_frames(const ma_data_source* pDataSource, ma_uint64* pRangeBegInFrames, ma_uint64* pRangeEndInFrames); -MA_API ma_result ma_data_source_set_loop_point_in_pcm_frames(ma_data_source* pDataSource, ma_uint64 loopBegInFrames, ma_uint64 loopEndInFrames); -MA_API void ma_data_source_get_loop_point_in_pcm_frames(const ma_data_source* pDataSource, ma_uint64* pLoopBegInFrames, ma_uint64* pLoopEndInFrames); -MA_API ma_result ma_data_source_set_current(ma_data_source* pDataSource, ma_data_source* pCurrentDataSource); -MA_API ma_data_source* ma_data_source_get_current(const ma_data_source* pDataSource); -MA_API ma_result ma_data_source_set_next(ma_data_source* pDataSource, ma_data_source* pNextDataSource); -MA_API ma_data_source* ma_data_source_get_next(const ma_data_source* pDataSource); -MA_API ma_result ma_data_source_set_next_callback(ma_data_source* pDataSource, ma_data_source_get_next_proc onGetNext); -MA_API ma_data_source_get_next_proc ma_data_source_get_next_callback(const ma_data_source* pDataSource); - - -typedef struct -{ - ma_data_source_base ds; - ma_format format; - ma_uint32 channels; - ma_uint32 sampleRate; - ma_uint64 cursor; - ma_uint64 sizeInFrames; - const void* pData; -} ma_audio_buffer_ref; - -MA_API ma_result ma_audio_buffer_ref_init(ma_format format, ma_uint32 channels, const void* pData, ma_uint64 sizeInFrames, ma_audio_buffer_ref* pAudioBufferRef); -MA_API void ma_audio_buffer_ref_uninit(ma_audio_buffer_ref* pAudioBufferRef); -MA_API ma_result ma_audio_buffer_ref_set_data(ma_audio_buffer_ref* pAudioBufferRef, const void* pData, ma_uint64 sizeInFrames); -MA_API ma_uint64 ma_audio_buffer_ref_read_pcm_frames(ma_audio_buffer_ref* pAudioBufferRef, void* pFramesOut, ma_uint64 frameCount, ma_bool32 loop); -MA_API ma_result ma_audio_buffer_ref_seek_to_pcm_frame(ma_audio_buffer_ref* pAudioBufferRef, ma_uint64 frameIndex); -MA_API ma_result ma_audio_buffer_ref_map(ma_audio_buffer_ref* pAudioBufferRef, void** ppFramesOut, ma_uint64* pFrameCount); -MA_API ma_result ma_audio_buffer_ref_unmap(ma_audio_buffer_ref* pAudioBufferRef, ma_uint64 frameCount); /* Returns MA_AT_END if the end has been reached. This should be considered successful. */ -MA_API ma_bool32 ma_audio_buffer_ref_at_end(const ma_audio_buffer_ref* pAudioBufferRef); -MA_API ma_result ma_audio_buffer_ref_get_cursor_in_pcm_frames(const ma_audio_buffer_ref* pAudioBufferRef, ma_uint64* pCursor); -MA_API ma_result ma_audio_buffer_ref_get_length_in_pcm_frames(const ma_audio_buffer_ref* pAudioBufferRef, ma_uint64* pLength); -MA_API ma_result ma_audio_buffer_ref_get_available_frames(const ma_audio_buffer_ref* pAudioBufferRef, ma_uint64* pAvailableFrames); - - - -typedef struct -{ - ma_format format; - ma_uint32 channels; - ma_uint32 sampleRate; - ma_uint64 sizeInFrames; - const void* pData; /* If set to NULL, will allocate a block of memory for you. */ - ma_allocation_callbacks allocationCallbacks; -} ma_audio_buffer_config; - -MA_API ma_audio_buffer_config ma_audio_buffer_config_init(ma_format format, ma_uint32 channels, ma_uint64 sizeInFrames, const void* pData, const ma_allocation_callbacks* pAllocationCallbacks); - -typedef struct -{ - ma_audio_buffer_ref ref; - ma_allocation_callbacks allocationCallbacks; - ma_bool32 ownsData; /* Used to control whether or not miniaudio owns the data buffer. If set to true, pData will be freed in ma_audio_buffer_uninit(). */ - ma_uint8 _pExtraData[1]; /* For allocating a buffer with the memory located directly after the other memory of the structure. */ -} ma_audio_buffer; - -MA_API ma_result ma_audio_buffer_init(const ma_audio_buffer_config* pConfig, ma_audio_buffer* pAudioBuffer); -MA_API ma_result ma_audio_buffer_init_copy(const ma_audio_buffer_config* pConfig, ma_audio_buffer* pAudioBuffer); -MA_API ma_result ma_audio_buffer_alloc_and_init(const ma_audio_buffer_config* pConfig, ma_audio_buffer** ppAudioBuffer); /* Always copies the data. Doesn't make sense to use this otherwise. Use ma_audio_buffer_uninit_and_free() to uninit. */ -MA_API void ma_audio_buffer_uninit(ma_audio_buffer* pAudioBuffer); -MA_API void ma_audio_buffer_uninit_and_free(ma_audio_buffer* pAudioBuffer); -MA_API ma_uint64 ma_audio_buffer_read_pcm_frames(ma_audio_buffer* pAudioBuffer, void* pFramesOut, ma_uint64 frameCount, ma_bool32 loop); -MA_API ma_result ma_audio_buffer_seek_to_pcm_frame(ma_audio_buffer* pAudioBuffer, ma_uint64 frameIndex); -MA_API ma_result ma_audio_buffer_map(ma_audio_buffer* pAudioBuffer, void** ppFramesOut, ma_uint64* pFrameCount); -MA_API ma_result ma_audio_buffer_unmap(ma_audio_buffer* pAudioBuffer, ma_uint64 frameCount); /* Returns MA_AT_END if the end has been reached. This should be considered successful. */ -MA_API ma_bool32 ma_audio_buffer_at_end(const ma_audio_buffer* pAudioBuffer); -MA_API ma_result ma_audio_buffer_get_cursor_in_pcm_frames(const ma_audio_buffer* pAudioBuffer, ma_uint64* pCursor); -MA_API ma_result ma_audio_buffer_get_length_in_pcm_frames(const ma_audio_buffer* pAudioBuffer, ma_uint64* pLength); -MA_API ma_result ma_audio_buffer_get_available_frames(const ma_audio_buffer* pAudioBuffer, ma_uint64* pAvailableFrames); - - -/* -Paged Audio Buffer -================== -A paged audio buffer is made up of a linked list of pages. It's expandable, but not shrinkable. It -can be used for cases where audio data is streamed in asynchronously while allowing data to be read -at the same time. - -This is lock-free, but not 100% thread safe. You can append a page and read from the buffer across -simultaneously across different threads, however only one thread at a time can append, and only one -thread at a time can read and seek. -*/ -typedef struct ma_paged_audio_buffer_page ma_paged_audio_buffer_page; -struct ma_paged_audio_buffer_page -{ - MA_ATOMIC(MA_SIZEOF_PTR, ma_paged_audio_buffer_page*) pNext; - ma_uint64 sizeInFrames; - ma_uint8 pAudioData[1]; -}; - -typedef struct -{ - ma_format format; - ma_uint32 channels; - ma_paged_audio_buffer_page head; /* Dummy head for the lock-free algorithm. Always has a size of 0. */ - MA_ATOMIC(MA_SIZEOF_PTR, ma_paged_audio_buffer_page*) pTail; /* Never null. Initially set to &head. */ -} ma_paged_audio_buffer_data; - -MA_API ma_result ma_paged_audio_buffer_data_init(ma_format format, ma_uint32 channels, ma_paged_audio_buffer_data* pData); -MA_API void ma_paged_audio_buffer_data_uninit(ma_paged_audio_buffer_data* pData, const ma_allocation_callbacks* pAllocationCallbacks); -MA_API ma_paged_audio_buffer_page* ma_paged_audio_buffer_data_get_head(ma_paged_audio_buffer_data* pData); -MA_API ma_paged_audio_buffer_page* ma_paged_audio_buffer_data_get_tail(ma_paged_audio_buffer_data* pData); -MA_API ma_result ma_paged_audio_buffer_data_get_length_in_pcm_frames(ma_paged_audio_buffer_data* pData, ma_uint64* pLength); -MA_API ma_result ma_paged_audio_buffer_data_allocate_page(ma_paged_audio_buffer_data* pData, ma_uint64 pageSizeInFrames, const void* pInitialData, const ma_allocation_callbacks* pAllocationCallbacks, ma_paged_audio_buffer_page** ppPage); -MA_API ma_result ma_paged_audio_buffer_data_free_page(ma_paged_audio_buffer_data* pData, ma_paged_audio_buffer_page* pPage, const ma_allocation_callbacks* pAllocationCallbacks); -MA_API ma_result ma_paged_audio_buffer_data_append_page(ma_paged_audio_buffer_data* pData, ma_paged_audio_buffer_page* pPage); -MA_API ma_result ma_paged_audio_buffer_data_allocate_and_append_page(ma_paged_audio_buffer_data* pData, ma_uint32 pageSizeInFrames, const void* pInitialData, const ma_allocation_callbacks* pAllocationCallbacks); - - -typedef struct -{ - ma_paged_audio_buffer_data* pData; /* Must not be null. */ -} ma_paged_audio_buffer_config; - -MA_API ma_paged_audio_buffer_config ma_paged_audio_buffer_config_init(ma_paged_audio_buffer_data* pData); - - -typedef struct -{ - ma_data_source_base ds; - ma_paged_audio_buffer_data* pData; /* Audio data is read from here. Cannot be null. */ - ma_paged_audio_buffer_page* pCurrent; - ma_uint64 relativeCursor; /* Relative to the current page. */ - ma_uint64 absoluteCursor; -} ma_paged_audio_buffer; - -MA_API ma_result ma_paged_audio_buffer_init(const ma_paged_audio_buffer_config* pConfig, ma_paged_audio_buffer* pPagedAudioBuffer); -MA_API void ma_paged_audio_buffer_uninit(ma_paged_audio_buffer* pPagedAudioBuffer); -MA_API ma_result ma_paged_audio_buffer_read_pcm_frames(ma_paged_audio_buffer* pPagedAudioBuffer, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead); /* Returns MA_AT_END if no more pages available. */ -MA_API ma_result ma_paged_audio_buffer_seek_to_pcm_frame(ma_paged_audio_buffer* pPagedAudioBuffer, ma_uint64 frameIndex); -MA_API ma_result ma_paged_audio_buffer_get_cursor_in_pcm_frames(ma_paged_audio_buffer* pPagedAudioBuffer, ma_uint64* pCursor); -MA_API ma_result ma_paged_audio_buffer_get_length_in_pcm_frames(ma_paged_audio_buffer* pPagedAudioBuffer, ma_uint64* pLength); - - - -/************************************************************************************************************************************************************ - -Ring Buffer - -************************************************************************************************************************************************************/ -typedef struct -{ - void* pBuffer; - ma_uint32 subbufferSizeInBytes; - ma_uint32 subbufferCount; - ma_uint32 subbufferStrideInBytes; - MA_ATOMIC(4, ma_uint32) encodedReadOffset; /* Most significant bit is the loop flag. Lower 31 bits contains the actual offset in bytes. Must be used atomically. */ - MA_ATOMIC(4, ma_uint32) encodedWriteOffset; /* Most significant bit is the loop flag. Lower 31 bits contains the actual offset in bytes. Must be used atomically. */ - ma_bool8 ownsBuffer; /* Used to know whether or not miniaudio is responsible for free()-ing the buffer. */ - ma_bool8 clearOnWriteAcquire; /* When set, clears the acquired write buffer before returning from ma_rb_acquire_write(). */ - ma_allocation_callbacks allocationCallbacks; -} ma_rb; - -MA_API ma_result ma_rb_init_ex(size_t subbufferSizeInBytes, size_t subbufferCount, size_t subbufferStrideInBytes, void* pOptionalPreallocatedBuffer, const ma_allocation_callbacks* pAllocationCallbacks, ma_rb* pRB); -MA_API ma_result ma_rb_init(size_t bufferSizeInBytes, void* pOptionalPreallocatedBuffer, const ma_allocation_callbacks* pAllocationCallbacks, ma_rb* pRB); -MA_API void ma_rb_uninit(ma_rb* pRB); -MA_API void ma_rb_reset(ma_rb* pRB); -MA_API ma_result ma_rb_acquire_read(ma_rb* pRB, size_t* pSizeInBytes, void** ppBufferOut); -MA_API ma_result ma_rb_commit_read(ma_rb* pRB, size_t sizeInBytes); -MA_API ma_result ma_rb_acquire_write(ma_rb* pRB, size_t* pSizeInBytes, void** ppBufferOut); -MA_API ma_result ma_rb_commit_write(ma_rb* pRB, size_t sizeInBytes); -MA_API ma_result ma_rb_seek_read(ma_rb* pRB, size_t offsetInBytes); -MA_API ma_result ma_rb_seek_write(ma_rb* pRB, size_t offsetInBytes); -MA_API ma_int32 ma_rb_pointer_distance(ma_rb* pRB); /* Returns the distance between the write pointer and the read pointer. Should never be negative for a correct program. Will return the number of bytes that can be read before the read pointer hits the write pointer. */ -MA_API ma_uint32 ma_rb_available_read(ma_rb* pRB); -MA_API ma_uint32 ma_rb_available_write(ma_rb* pRB); -MA_API size_t ma_rb_get_subbuffer_size(ma_rb* pRB); -MA_API size_t ma_rb_get_subbuffer_stride(ma_rb* pRB); -MA_API size_t ma_rb_get_subbuffer_offset(ma_rb* pRB, size_t subbufferIndex); -MA_API void* ma_rb_get_subbuffer_ptr(ma_rb* pRB, size_t subbufferIndex, void* pBuffer); - - -typedef struct -{ - ma_data_source_base ds; - ma_rb rb; - ma_format format; - ma_uint32 channels; - ma_uint32 sampleRate; /* Not required for the ring buffer itself, but useful for associating the data with some sample rate, particularly for data sources. */ -} ma_pcm_rb; - -MA_API ma_result ma_pcm_rb_init_ex(ma_format format, ma_uint32 channels, ma_uint32 subbufferSizeInFrames, ma_uint32 subbufferCount, ma_uint32 subbufferStrideInFrames, void* pOptionalPreallocatedBuffer, const ma_allocation_callbacks* pAllocationCallbacks, ma_pcm_rb* pRB); -MA_API ma_result ma_pcm_rb_init(ma_format format, ma_uint32 channels, ma_uint32 bufferSizeInFrames, void* pOptionalPreallocatedBuffer, const ma_allocation_callbacks* pAllocationCallbacks, ma_pcm_rb* pRB); -MA_API void ma_pcm_rb_uninit(ma_pcm_rb* pRB); -MA_API void ma_pcm_rb_reset(ma_pcm_rb* pRB); -MA_API ma_result ma_pcm_rb_acquire_read(ma_pcm_rb* pRB, ma_uint32* pSizeInFrames, void** ppBufferOut); -MA_API ma_result ma_pcm_rb_commit_read(ma_pcm_rb* pRB, ma_uint32 sizeInFrames); -MA_API ma_result ma_pcm_rb_acquire_write(ma_pcm_rb* pRB, ma_uint32* pSizeInFrames, void** ppBufferOut); -MA_API ma_result ma_pcm_rb_commit_write(ma_pcm_rb* pRB, ma_uint32 sizeInFrames); -MA_API ma_result ma_pcm_rb_seek_read(ma_pcm_rb* pRB, ma_uint32 offsetInFrames); -MA_API ma_result ma_pcm_rb_seek_write(ma_pcm_rb* pRB, ma_uint32 offsetInFrames); -MA_API ma_int32 ma_pcm_rb_pointer_distance(ma_pcm_rb* pRB); /* Return value is in frames. */ -MA_API ma_uint32 ma_pcm_rb_available_read(ma_pcm_rb* pRB); -MA_API ma_uint32 ma_pcm_rb_available_write(ma_pcm_rb* pRB); -MA_API ma_uint32 ma_pcm_rb_get_subbuffer_size(ma_pcm_rb* pRB); -MA_API ma_uint32 ma_pcm_rb_get_subbuffer_stride(ma_pcm_rb* pRB); -MA_API ma_uint32 ma_pcm_rb_get_subbuffer_offset(ma_pcm_rb* pRB, ma_uint32 subbufferIndex); -MA_API void* ma_pcm_rb_get_subbuffer_ptr(ma_pcm_rb* pRB, ma_uint32 subbufferIndex, void* pBuffer); -MA_API ma_format ma_pcm_rb_get_format(const ma_pcm_rb* pRB); -MA_API ma_uint32 ma_pcm_rb_get_channels(const ma_pcm_rb* pRB); -MA_API ma_uint32 ma_pcm_rb_get_sample_rate(const ma_pcm_rb* pRB); -MA_API void ma_pcm_rb_set_sample_rate(ma_pcm_rb* pRB, ma_uint32 sampleRate); - - -/* -The idea of the duplex ring buffer is to act as the intermediary buffer when running two asynchronous devices in a duplex set up. The -capture device writes to it, and then a playback device reads from it. - -At the moment this is just a simple naive implementation, but in the future I want to implement some dynamic resampling to seamlessly -handle desyncs. Note that the API is work in progress and may change at any time in any version. - -The size of the buffer is based on the capture side since that's what'll be written to the buffer. It is based on the capture period size -in frames. The internal sample rate of the capture device is also needed in order to calculate the size. -*/ -typedef struct -{ - ma_pcm_rb rb; -} ma_duplex_rb; - -MA_API ma_result ma_duplex_rb_init(ma_format captureFormat, ma_uint32 captureChannels, ma_uint32 sampleRate, ma_uint32 captureInternalSampleRate, ma_uint32 captureInternalPeriodSizeInFrames, const ma_allocation_callbacks* pAllocationCallbacks, ma_duplex_rb* pRB); -MA_API ma_result ma_duplex_rb_uninit(ma_duplex_rb* pRB); - - -/************************************************************************************************************************************************************ - -Miscellaneous Helpers - -************************************************************************************************************************************************************/ -/* -Retrieves a human readable description of the given result code. -*/ -MA_API const char* ma_result_description(ma_result result); - -/* -malloc() -*/ -MA_API void* ma_malloc(size_t sz, const ma_allocation_callbacks* pAllocationCallbacks); - -/* -calloc() -*/ -MA_API void* ma_calloc(size_t sz, const ma_allocation_callbacks* pAllocationCallbacks); - -/* -realloc() -*/ -MA_API void* ma_realloc(void* p, size_t sz, const ma_allocation_callbacks* pAllocationCallbacks); - -/* -free() -*/ -MA_API void ma_free(void* p, const ma_allocation_callbacks* pAllocationCallbacks); - -/* -Performs an aligned malloc, with the assumption that the alignment is a power of 2. -*/ -MA_API void* ma_aligned_malloc(size_t sz, size_t alignment, const ma_allocation_callbacks* pAllocationCallbacks); - -/* -Free's an aligned malloc'd buffer. -*/ -MA_API void ma_aligned_free(void* p, const ma_allocation_callbacks* pAllocationCallbacks); - -/* -Retrieves a friendly name for a format. -*/ -MA_API const char* ma_get_format_name(ma_format format); - -/* -Blends two frames in floating point format. -*/ -MA_API void ma_blend_f32(float* pOut, float* pInA, float* pInB, float factor, ma_uint32 channels); - -/* -Retrieves the size of a sample in bytes for the given format. - -This API is efficient and is implemented using a lookup table. - -Thread Safety: SAFE - This API is pure. -*/ -MA_API ma_uint32 ma_get_bytes_per_sample(ma_format format); -static MA_INLINE ma_uint32 ma_get_bytes_per_frame(ma_format format, ma_uint32 channels) { return ma_get_bytes_per_sample(format) * channels; } - -/* -Converts a log level to a string. -*/ -MA_API const char* ma_log_level_to_string(ma_uint32 logLevel); - - - - -/************************************************************************************************************************************************************ - -Synchronization - -************************************************************************************************************************************************************/ -/* -Locks a spinlock. -*/ -MA_API ma_result ma_spinlock_lock(volatile ma_spinlock* pSpinlock); - -/* -Locks a spinlock, but does not yield() when looping. -*/ -MA_API ma_result ma_spinlock_lock_noyield(volatile ma_spinlock* pSpinlock); - -/* -Unlocks a spinlock. -*/ -MA_API ma_result ma_spinlock_unlock(volatile ma_spinlock* pSpinlock); - - -#ifndef MA_NO_THREADING - -/* -Creates a mutex. - -A mutex must be created from a valid context. A mutex is initially unlocked. -*/ -MA_API ma_result ma_mutex_init(ma_mutex* pMutex); - -/* -Deletes a mutex. -*/ -MA_API void ma_mutex_uninit(ma_mutex* pMutex); - -/* -Locks a mutex with an infinite timeout. -*/ -MA_API void ma_mutex_lock(ma_mutex* pMutex); - -/* -Unlocks a mutex. -*/ -MA_API void ma_mutex_unlock(ma_mutex* pMutex); - - -/* -Initializes an auto-reset event. -*/ -MA_API ma_result ma_event_init(ma_event* pEvent); - -/* -Uninitializes an auto-reset event. -*/ -MA_API void ma_event_uninit(ma_event* pEvent); - -/* -Waits for the specified auto-reset event to become signalled. -*/ -MA_API ma_result ma_event_wait(ma_event* pEvent); - -/* -Signals the specified auto-reset event. -*/ -MA_API ma_result ma_event_signal(ma_event* pEvent); - - -MA_API ma_result ma_semaphore_init(int initialValue, ma_semaphore* pSemaphore); -MA_API void ma_semaphore_uninit(ma_semaphore* pSemaphore); -MA_API ma_result ma_semaphore_wait(ma_semaphore* pSemaphore); -MA_API ma_result ma_semaphore_release(ma_semaphore* pSemaphore); -#endif /* MA_NO_THREADING */ - - -/* -Fence -===== -This locks while the counter is larger than 0. Counter can be incremented and decremented by any -thread, but care needs to be taken when waiting. It is possible for one thread to acquire the -fence just as another thread returns from ma_fence_wait(). - -The idea behind a fence is to allow you to wait for a group of operations to complete. When an -operation starts, the counter is incremented which locks the fence. When the operation completes, -the fence will be released which decrements the counter. ma_fence_wait() will block until the -counter hits zero. - -If threading is disabled, ma_fence_wait() will spin on the counter. -*/ -typedef struct -{ -#ifndef MA_NO_THREADING - ma_event e; -#endif - ma_uint32 counter; -} ma_fence; - -MA_API ma_result ma_fence_init(ma_fence* pFence); -MA_API void ma_fence_uninit(ma_fence* pFence); -MA_API ma_result ma_fence_acquire(ma_fence* pFence); /* Increment counter. */ -MA_API ma_result ma_fence_release(ma_fence* pFence); /* Decrement counter. */ -MA_API ma_result ma_fence_wait(ma_fence* pFence); /* Wait for counter to reach 0. */ - - - -/* -Notification callback for asynchronous operations. -*/ -typedef void ma_async_notification; - -typedef struct -{ - void (* onSignal)(ma_async_notification* pNotification); -} ma_async_notification_callbacks; - -MA_API ma_result ma_async_notification_signal(ma_async_notification* pNotification); - - -/* -Simple polling notification. - -This just sets a variable when the notification has been signalled which is then polled with ma_async_notification_poll_is_signalled() -*/ -typedef struct -{ - ma_async_notification_callbacks cb; - ma_bool32 signalled; -} ma_async_notification_poll; - -MA_API ma_result ma_async_notification_poll_init(ma_async_notification_poll* pNotificationPoll); -MA_API ma_bool32 ma_async_notification_poll_is_signalled(const ma_async_notification_poll* pNotificationPoll); - - -/* -Event Notification - -This uses an ma_event. If threading is disabled (MA_NO_THREADING), initialization will fail. -*/ -typedef struct -{ - ma_async_notification_callbacks cb; -#ifndef MA_NO_THREADING - ma_event e; -#endif -} ma_async_notification_event; - -MA_API ma_result ma_async_notification_event_init(ma_async_notification_event* pNotificationEvent); -MA_API ma_result ma_async_notification_event_uninit(ma_async_notification_event* pNotificationEvent); -MA_API ma_result ma_async_notification_event_wait(ma_async_notification_event* pNotificationEvent); -MA_API ma_result ma_async_notification_event_signal(ma_async_notification_event* pNotificationEvent); - - - - -/************************************************************************************************************************************************************ - -Job Queue - -************************************************************************************************************************************************************/ - -/* -Slot Allocator --------------- -The idea of the slot allocator is for it to be used in conjunction with a fixed sized buffer. You use the slot allocator to allocate an index that can be used -as the insertion point for an object. - -Slots are reference counted to help mitigate the ABA problem in the lock-free queue we use for tracking jobs. - -The slot index is stored in the low 32 bits. The reference counter is stored in the high 32 bits: - - +-----------------+-----------------+ - | 32 Bits | 32 Bits | - +-----------------+-----------------+ - | Reference Count | Slot Index | - +-----------------+-----------------+ -*/ -typedef struct -{ - ma_uint32 capacity; /* The number of slots to make available. */ -} ma_slot_allocator_config; - -MA_API ma_slot_allocator_config ma_slot_allocator_config_init(ma_uint32 capacity); - - -typedef struct -{ - MA_ATOMIC(4, ma_uint32) bitfield; /* Must be used atomically because the allocation and freeing routines need to make copies of this which must never be optimized away by the compiler. */ -} ma_slot_allocator_group; - -typedef struct -{ - ma_slot_allocator_group* pGroups; /* Slots are grouped in chunks of 32. */ - ma_uint32* pSlots; /* 32 bits for reference counting for ABA mitigation. */ - ma_uint32 count; /* Allocation count. */ - ma_uint32 capacity; - - /* Memory management. */ - ma_bool32 _ownsHeap; - void* _pHeap; -} ma_slot_allocator; - -MA_API ma_result ma_slot_allocator_get_heap_size(const ma_slot_allocator_config* pConfig, size_t* pHeapSizeInBytes); -MA_API ma_result ma_slot_allocator_init_preallocated(const ma_slot_allocator_config* pConfig, void* pHeap, ma_slot_allocator* pAllocator); -MA_API ma_result ma_slot_allocator_init(const ma_slot_allocator_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_slot_allocator* pAllocator); -MA_API void ma_slot_allocator_uninit(ma_slot_allocator* pAllocator, const ma_allocation_callbacks* pAllocationCallbacks); -MA_API ma_result ma_slot_allocator_alloc(ma_slot_allocator* pAllocator, ma_uint64* pSlot); -MA_API ma_result ma_slot_allocator_free(ma_slot_allocator* pAllocator, ma_uint64 slot); - - -typedef struct ma_job ma_job; - -/* -Callback for processing a job. Each job type will have their own processing callback which will be -called by ma_job_process(). -*/ -typedef ma_result (* ma_job_proc)(ma_job* pJob); - -/* When a job type is added here an callback needs to be added go "g_jobVTable" in the implementation section. */ -typedef enum -{ - /* Miscellaneous. */ - MA_JOB_TYPE_QUIT = 0, - MA_JOB_TYPE_CUSTOM, - - /* Resource Manager. */ - MA_JOB_TYPE_RESOURCE_MANAGER_LOAD_DATA_BUFFER_NODE, - MA_JOB_TYPE_RESOURCE_MANAGER_FREE_DATA_BUFFER_NODE, - MA_JOB_TYPE_RESOURCE_MANAGER_PAGE_DATA_BUFFER_NODE, - MA_JOB_TYPE_RESOURCE_MANAGER_LOAD_DATA_BUFFER, - MA_JOB_TYPE_RESOURCE_MANAGER_FREE_DATA_BUFFER, - MA_JOB_TYPE_RESOURCE_MANAGER_LOAD_DATA_STREAM, - MA_JOB_TYPE_RESOURCE_MANAGER_FREE_DATA_STREAM, - MA_JOB_TYPE_RESOURCE_MANAGER_PAGE_DATA_STREAM, - MA_JOB_TYPE_RESOURCE_MANAGER_SEEK_DATA_STREAM, - - /* Device. */ - MA_JOB_TYPE_DEVICE_AAUDIO_REROUTE, - - /* Count. Must always be last. */ - MA_JOB_TYPE_COUNT -} ma_job_type; - -struct ma_job -{ - union - { - struct - { - ma_uint16 code; /* Job type. */ - ma_uint16 slot; /* Index into a ma_slot_allocator. */ - ma_uint32 refcount; - } breakup; - ma_uint64 allocation; - } toc; /* 8 bytes. We encode the job code into the slot allocation data to save space. */ - MA_ATOMIC(8, ma_uint64) next; /* refcount + slot for the next item. Does not include the job code. */ - ma_uint32 order; /* Execution order. Used to create a data dependency and ensure a job is executed in order. Usage is contextual depending on the job type. */ - - union - { - /* Miscellaneous. */ - struct - { - ma_job_proc proc; - ma_uintptr data0; - ma_uintptr data1; - } custom; - - /* Resource Manager */ - union - { - struct - { - /*ma_resource_manager**/ void* pResourceManager; - /*ma_resource_manager_data_buffer_node**/ void* pDataBufferNode; - char* pFilePath; - wchar_t* pFilePathW; - ma_uint32 flags; /* Resource manager data source flags that were used when initializing the data buffer. */ - ma_async_notification* pInitNotification; /* Signalled when the data buffer has been initialized and the format/channels/rate can be retrieved. */ - ma_async_notification* pDoneNotification; /* Signalled when the data buffer has been fully decoded. Will be passed through to MA_JOB_TYPE_RESOURCE_MANAGER_PAGE_DATA_BUFFER_NODE when decoding. */ - ma_fence* pInitFence; /* Released when initialization of the decoder is complete. */ - ma_fence* pDoneFence; /* Released if initialization of the decoder fails. Passed through to PAGE_DATA_BUFFER_NODE untouched if init is successful. */ - } loadDataBufferNode; - struct - { - /*ma_resource_manager**/ void* pResourceManager; - /*ma_resource_manager_data_buffer_node**/ void* pDataBufferNode; - ma_async_notification* pDoneNotification; - ma_fence* pDoneFence; - } freeDataBufferNode; - struct - { - /*ma_resource_manager**/ void* pResourceManager; - /*ma_resource_manager_data_buffer_node**/ void* pDataBufferNode; - /*ma_decoder**/ void* pDecoder; - ma_async_notification* pDoneNotification; /* Signalled when the data buffer has been fully decoded. */ - ma_fence* pDoneFence; /* Passed through from LOAD_DATA_BUFFER_NODE and released when the data buffer completes decoding or an error occurs. */ - } pageDataBufferNode; - - struct - { - /*ma_resource_manager_data_buffer**/ void* pDataBuffer; - ma_async_notification* pInitNotification; /* Signalled when the data buffer has been initialized and the format/channels/rate can be retrieved. */ - ma_async_notification* pDoneNotification; /* Signalled when the data buffer has been fully decoded. */ - ma_fence* pInitFence; /* Released when the data buffer has been initialized and the format/channels/rate can be retrieved. */ - ma_fence* pDoneFence; /* Released when the data buffer has been fully decoded. */ - ma_uint64 rangeBegInPCMFrames; - ma_uint64 rangeEndInPCMFrames; - ma_uint64 loopPointBegInPCMFrames; - ma_uint64 loopPointEndInPCMFrames; - ma_uint32 isLooping; - } loadDataBuffer; - struct - { - /*ma_resource_manager_data_buffer**/ void* pDataBuffer; - ma_async_notification* pDoneNotification; - ma_fence* pDoneFence; - } freeDataBuffer; - - struct - { - /*ma_resource_manager_data_stream**/ void* pDataStream; - char* pFilePath; /* Allocated when the job is posted, freed by the job thread after loading. */ - wchar_t* pFilePathW; /* ^ As above ^. Only used if pFilePath is NULL. */ - ma_uint64 initialSeekPoint; - ma_async_notification* pInitNotification; /* Signalled after the first two pages have been decoded and frames can be read from the stream. */ - ma_fence* pInitFence; - } loadDataStream; - struct - { - /*ma_resource_manager_data_stream**/ void* pDataStream; - ma_async_notification* pDoneNotification; - ma_fence* pDoneFence; - } freeDataStream; - struct - { - /*ma_resource_manager_data_stream**/ void* pDataStream; - ma_uint32 pageIndex; /* The index of the page to decode into. */ - } pageDataStream; - struct - { - /*ma_resource_manager_data_stream**/ void* pDataStream; - ma_uint64 frameIndex; - } seekDataStream; - } resourceManager; - - /* Device. */ - union - { - union - { - struct - { - /*ma_device**/ void* pDevice; - /*ma_device_type*/ ma_uint32 deviceType; - } reroute; - } aaudio; - } device; - } data; -}; - -MA_API ma_job ma_job_init(ma_uint16 code); -MA_API ma_result ma_job_process(ma_job* pJob); - - -/* -When set, ma_job_queue_next() will not wait and no semaphore will be signaled in -ma_job_queue_post(). ma_job_queue_next() will return MA_NO_DATA_AVAILABLE if nothing is available. - -This flag should always be used for platforms that do not support multithreading. -*/ -typedef enum -{ - MA_JOB_QUEUE_FLAG_NON_BLOCKING = 0x00000001 -} ma_job_queue_flags; - -typedef struct -{ - ma_uint32 flags; - ma_uint32 capacity; /* The maximum number of jobs that can fit in the queue at a time. */ -} ma_job_queue_config; - -MA_API ma_job_queue_config ma_job_queue_config_init(ma_uint32 flags, ma_uint32 capacity); - - -typedef struct -{ - ma_uint32 flags; /* Flags passed in at initialization time. */ - ma_uint32 capacity; /* The maximum number of jobs that can fit in the queue at a time. Set by the config. */ - MA_ATOMIC(8, ma_uint64) head; /* The first item in the list. Required for removing from the top of the list. */ - MA_ATOMIC(8, ma_uint64) tail; /* The last item in the list. Required for appending to the end of the list. */ -#ifndef MA_NO_THREADING - ma_semaphore sem; /* Only used when MA_JOB_QUEUE_FLAG_NON_BLOCKING is unset. */ -#endif - ma_slot_allocator allocator; - ma_job* pJobs; -#ifndef MA_USE_EXPERIMENTAL_LOCK_FREE_JOB_QUEUE - ma_spinlock lock; -#endif - - /* Memory management. */ - void* _pHeap; - ma_bool32 _ownsHeap; -} ma_job_queue; - -MA_API ma_result ma_job_queue_get_heap_size(const ma_job_queue_config* pConfig, size_t* pHeapSizeInBytes); -MA_API ma_result ma_job_queue_init_preallocated(const ma_job_queue_config* pConfig, void* pHeap, ma_job_queue* pQueue); -MA_API ma_result ma_job_queue_init(const ma_job_queue_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_job_queue* pQueue); -MA_API void ma_job_queue_uninit(ma_job_queue* pQueue, const ma_allocation_callbacks* pAllocationCallbacks); -MA_API ma_result ma_job_queue_post(ma_job_queue* pQueue, const ma_job* pJob); -MA_API ma_result ma_job_queue_next(ma_job_queue* pQueue, ma_job* pJob); /* Returns MA_CANCELLED if the next job is a quit job. */ - - - -/************************************************************************************************************************************************************ -************************************************************************************************************************************************************* - -DEVICE I/O -========== - -This section contains the APIs for device playback and capture. Here is where you'll find ma_device_init(), etc. - -************************************************************************************************************************************************************* -************************************************************************************************************************************************************/ -#ifndef MA_NO_DEVICE_IO -/* Some backends are only supported on certain platforms. */ -#if defined(MA_WIN32) - #define MA_SUPPORT_WASAPI - - #if defined(MA_WIN32_DESKTOP) /* DirectSound and WinMM backends are only supported on desktops. */ - #define MA_SUPPORT_DSOUND - #define MA_SUPPORT_WINMM - - /* Don't enable JACK here if compiling with Cosmopolitan. It'll be enabled in the Linux section below. */ - #if !defined(__COSMOPOLITAN__) - #define MA_SUPPORT_JACK /* JACK is technically supported on Windows, but I don't know how many people use it in practice... */ - #endif - #endif -#endif -#if defined(MA_UNIX) && !defined(MA_ORBIS) && !defined(MA_PROSPERO) - #if defined(MA_LINUX) - #if !defined(MA_ANDROID) && !defined(__COSMOPOLITAN__) /* ALSA is not supported on Android. */ - #define MA_SUPPORT_ALSA - #endif - #endif - #if !defined(MA_BSD) && !defined(MA_ANDROID) && !defined(MA_EMSCRIPTEN) - #define MA_SUPPORT_PULSEAUDIO - #define MA_SUPPORT_JACK - #endif - #if defined(__OpenBSD__) /* <-- Change this to "#if defined(MA_BSD)" to enable sndio on all BSD flavors. */ - #define MA_SUPPORT_SNDIO /* sndio is only supported on OpenBSD for now. May be expanded later if there's demand. */ - #endif - #if defined(__NetBSD__) || defined(__OpenBSD__) - #define MA_SUPPORT_AUDIO4 /* Only support audio(4) on platforms with known support. */ - #endif - #if defined(__FreeBSD__) || defined(__DragonFly__) - #define MA_SUPPORT_OSS /* Only support OSS on specific platforms with known support. */ - #endif -#endif -#if defined(MA_ANDROID) - #define MA_SUPPORT_AAUDIO - #define MA_SUPPORT_OPENSL -#endif -#if defined(MA_APPLE) - #define MA_SUPPORT_COREAUDIO -#endif -#if defined(MA_EMSCRIPTEN) - #define MA_SUPPORT_WEBAUDIO -#endif - -/* All platforms should support custom backends. */ -#define MA_SUPPORT_CUSTOM - -/* Explicitly disable the Null backend for Emscripten because it uses a background thread which is not properly supported right now. */ -#if !defined(MA_EMSCRIPTEN) -#define MA_SUPPORT_NULL -#endif - - -#if defined(MA_SUPPORT_WASAPI) && !defined(MA_NO_WASAPI) && (!defined(MA_ENABLE_ONLY_SPECIFIC_BACKENDS) || defined(MA_ENABLE_WASAPI)) - #define MA_HAS_WASAPI -#endif -#if defined(MA_SUPPORT_DSOUND) && !defined(MA_NO_DSOUND) && (!defined(MA_ENABLE_ONLY_SPECIFIC_BACKENDS) || defined(MA_ENABLE_DSOUND)) - #define MA_HAS_DSOUND -#endif -#if defined(MA_SUPPORT_WINMM) && !defined(MA_NO_WINMM) && (!defined(MA_ENABLE_ONLY_SPECIFIC_BACKENDS) || defined(MA_ENABLE_WINMM)) - #define MA_HAS_WINMM -#endif -#if defined(MA_SUPPORT_ALSA) && !defined(MA_NO_ALSA) && (!defined(MA_ENABLE_ONLY_SPECIFIC_BACKENDS) || defined(MA_ENABLE_ALSA)) - #define MA_HAS_ALSA -#endif -#if defined(MA_SUPPORT_PULSEAUDIO) && !defined(MA_NO_PULSEAUDIO) && (!defined(MA_ENABLE_ONLY_SPECIFIC_BACKENDS) || defined(MA_ENABLE_PULSEAUDIO)) - #define MA_HAS_PULSEAUDIO -#endif -#if defined(MA_SUPPORT_JACK) && !defined(MA_NO_JACK) && (!defined(MA_ENABLE_ONLY_SPECIFIC_BACKENDS) || defined(MA_ENABLE_JACK)) - #define MA_HAS_JACK -#endif -#if defined(MA_SUPPORT_COREAUDIO) && !defined(MA_NO_COREAUDIO) && (!defined(MA_ENABLE_ONLY_SPECIFIC_BACKENDS) || defined(MA_ENABLE_COREAUDIO)) - #define MA_HAS_COREAUDIO -#endif -#if defined(MA_SUPPORT_SNDIO) && !defined(MA_NO_SNDIO) && (!defined(MA_ENABLE_ONLY_SPECIFIC_BACKENDS) || defined(MA_ENABLE_SNDIO)) - #define MA_HAS_SNDIO -#endif -#if defined(MA_SUPPORT_AUDIO4) && !defined(MA_NO_AUDIO4) && (!defined(MA_ENABLE_ONLY_SPECIFIC_BACKENDS) || defined(MA_ENABLE_AUDIO4)) - #define MA_HAS_AUDIO4 -#endif -#if defined(MA_SUPPORT_OSS) && !defined(MA_NO_OSS) && (!defined(MA_ENABLE_ONLY_SPECIFIC_BACKENDS) || defined(MA_ENABLE_OSS)) - #define MA_HAS_OSS -#endif -#if defined(MA_SUPPORT_AAUDIO) && !defined(MA_NO_AAUDIO) && (!defined(MA_ENABLE_ONLY_SPECIFIC_BACKENDS) || defined(MA_ENABLE_AAUDIO)) - #define MA_HAS_AAUDIO -#endif -#if defined(MA_SUPPORT_OPENSL) && !defined(MA_NO_OPENSL) && (!defined(MA_ENABLE_ONLY_SPECIFIC_BACKENDS) || defined(MA_ENABLE_OPENSL)) - #define MA_HAS_OPENSL -#endif -#if defined(MA_SUPPORT_WEBAUDIO) && !defined(MA_NO_WEBAUDIO) && (!defined(MA_ENABLE_ONLY_SPECIFIC_BACKENDS) || defined(MA_ENABLE_WEBAUDIO)) - #define MA_HAS_WEBAUDIO -#endif -#if defined(MA_SUPPORT_CUSTOM) && !defined(MA_NO_CUSTOM) && (!defined(MA_ENABLE_ONLY_SPECIFIC_BACKENDS) || defined(MA_ENABLE_CUSTOM)) - #define MA_HAS_CUSTOM -#endif -#if defined(MA_SUPPORT_NULL) && !defined(MA_NO_NULL) && (!defined(MA_ENABLE_ONLY_SPECIFIC_BACKENDS) || defined(MA_ENABLE_NULL)) - #define MA_HAS_NULL -#endif - -typedef enum -{ - ma_device_state_uninitialized = 0, - ma_device_state_stopped = 1, /* The device's default state after initialization. */ - ma_device_state_started = 2, /* The device is started and is requesting and/or delivering audio data. */ - ma_device_state_starting = 3, /* Transitioning from a stopped state to started. */ - ma_device_state_stopping = 4 /* Transitioning from a started state to stopped. */ -} ma_device_state; - -MA_ATOMIC_SAFE_TYPE_DECL(i32, 4, device_state) - - -#ifdef MA_SUPPORT_WASAPI -/* We need a IMMNotificationClient object for WASAPI. */ -typedef struct -{ - void* lpVtbl; - ma_uint32 counter; - ma_device* pDevice; -} ma_IMMNotificationClient; -#endif - -/* Backend enums must be in priority order. */ -typedef enum -{ - ma_backend_wasapi, - ma_backend_dsound, - ma_backend_winmm, - ma_backend_coreaudio, - ma_backend_sndio, - ma_backend_audio4, - ma_backend_oss, - ma_backend_pulseaudio, - ma_backend_alsa, - ma_backend_jack, - ma_backend_aaudio, - ma_backend_opensl, - ma_backend_webaudio, - ma_backend_custom, /* <-- Custom backend, with callbacks defined by the context config. */ - ma_backend_null /* <-- Must always be the last item. Lowest priority, and used as the terminator for backend enumeration. */ -} ma_backend; - -#define MA_BACKEND_COUNT (ma_backend_null+1) - - -/* -Device job thread. This is used by backends that require asynchronous processing of certain -operations. It is not used by all backends. - -The device job thread is made up of a thread and a job queue. You can post a job to the thread with -ma_device_job_thread_post(). The thread will do the processing of the job. -*/ -typedef struct -{ - ma_bool32 noThread; /* Set this to true if you want to process jobs yourself. */ - ma_uint32 jobQueueCapacity; - ma_uint32 jobQueueFlags; -} ma_device_job_thread_config; - -MA_API ma_device_job_thread_config ma_device_job_thread_config_init(void); - -typedef struct -{ - ma_thread thread; - ma_job_queue jobQueue; - ma_bool32 _hasThread; -} ma_device_job_thread; - -MA_API ma_result ma_device_job_thread_init(const ma_device_job_thread_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_device_job_thread* pJobThread); -MA_API void ma_device_job_thread_uninit(ma_device_job_thread* pJobThread, const ma_allocation_callbacks* pAllocationCallbacks); -MA_API ma_result ma_device_job_thread_post(ma_device_job_thread* pJobThread, const ma_job* pJob); -MA_API ma_result ma_device_job_thread_next(ma_device_job_thread* pJobThread, ma_job* pJob); - - - -/* Device notification types. */ -typedef enum -{ - ma_device_notification_type_started, - ma_device_notification_type_stopped, - ma_device_notification_type_rerouted, - ma_device_notification_type_interruption_began, - ma_device_notification_type_interruption_ended, - ma_device_notification_type_unlocked -} ma_device_notification_type; - -typedef struct -{ - ma_device* pDevice; - ma_device_notification_type type; - union - { - struct - { - int _unused; - } started; - struct - { - int _unused; - } stopped; - struct - { - int _unused; - } rerouted; - struct - { - int _unused; - } interruption; - } data; -} ma_device_notification; - -/* -The notification callback for when the application should be notified of a change to the device. - -This callback is used for notifying the application of changes such as when the device has started, -stopped, rerouted or an interruption has occurred. Note that not all backends will post all -notification types. For example, some backends will perform automatic stream routing without any -kind of notification to the host program which means miniaudio will never know about it and will -never be able to fire the rerouted notification. You should keep this in mind when designing your -program. - -The stopped notification will *not* get fired when a device is rerouted. - - -Parameters ----------- -pNotification (in) - A pointer to a structure containing information about the event. Use the `pDevice` member of - this object to retrieve the relevant device. The `type` member can be used to discriminate - against each of the notification types. - - -Remarks -------- -Do not restart or uninitialize the device from the callback. - -Not all notifications will be triggered by all backends, however the started and stopped events -should be reliable for all backends. Some backends do not have a good way to detect device -stoppages due to unplugging the device which may result in the stopped callback not getting -fired. This has been observed with at least one BSD variant. - -The rerouted notification is fired *after* the reroute has occurred. The stopped notification will -*not* get fired when a device is rerouted. The following backends are known to do automatic stream -rerouting, but do not have a way to be notified of the change: - - * DirectSound - -The interruption notifications are used on mobile platforms for detecting when audio is interrupted -due to things like an incoming phone call. Currently this is only implemented on iOS. None of the -Android backends will report this notification. -*/ -typedef void (* ma_device_notification_proc)(const ma_device_notification* pNotification); - - -/* -The callback for processing audio data from the device. - -The data callback is fired by miniaudio whenever the device needs to have more data delivered to a playback device, or when a capture device has some data -available. This is called as soon as the backend asks for more data which means it may be called with inconsistent frame counts. You cannot assume the -callback will be fired with a consistent frame count. - - -Parameters ----------- -pDevice (in) - A pointer to the relevant device. - -pOutput (out) - A pointer to the output buffer that will receive audio data that will later be played back through the speakers. This will be non-null for a playback or - full-duplex device and null for a capture and loopback device. - -pInput (in) - A pointer to the buffer containing input data from a recording device. This will be non-null for a capture, full-duplex or loopback device and null for a - playback device. - -frameCount (in) - The number of PCM frames to process. Note that this will not necessarily be equal to what you requested when you initialized the device. The - `periodSizeInFrames` and `periodSizeInMilliseconds` members of the device config are just hints, and are not necessarily exactly what you'll get. You must - not assume this will always be the same value each time the callback is fired. - - -Remarks -------- -You cannot stop and start the device from inside the callback or else you'll get a deadlock. You must also not uninitialize the device from inside the -callback. The following APIs cannot be called from inside the callback: - - ma_device_init() - ma_device_init_ex() - ma_device_uninit() - ma_device_start() - ma_device_stop() - -The proper way to stop the device is to call `ma_device_stop()` from a different thread, normally the main application thread. -*/ -typedef void (* ma_device_data_proc)(ma_device* pDevice, void* pOutput, const void* pInput, ma_uint32 frameCount); - - - - -/* -DEPRECATED. Use ma_device_notification_proc instead. - -The callback for when the device has been stopped. - -This will be called when the device is stopped explicitly with `ma_device_stop()` and also called implicitly when the device is stopped through external forces -such as being unplugged or an internal error occurring. - - -Parameters ----------- -pDevice (in) - A pointer to the device that has just stopped. - - -Remarks -------- -Do not restart or uninitialize the device from the callback. -*/ -typedef void (* ma_stop_proc)(ma_device* pDevice); /* DEPRECATED. Use ma_device_notification_proc instead. */ - -typedef enum -{ - ma_device_type_playback = 1, - ma_device_type_capture = 2, - ma_device_type_duplex = ma_device_type_playback | ma_device_type_capture, /* 3 */ - ma_device_type_loopback = 4 -} ma_device_type; - -typedef enum -{ - ma_share_mode_shared = 0, - ma_share_mode_exclusive -} ma_share_mode; - -/* iOS/tvOS/watchOS session categories. */ -typedef enum -{ - ma_ios_session_category_default = 0, /* AVAudioSessionCategoryPlayAndRecord. */ - ma_ios_session_category_none, /* Leave the session category unchanged. */ - ma_ios_session_category_ambient, /* AVAudioSessionCategoryAmbient */ - ma_ios_session_category_solo_ambient, /* AVAudioSessionCategorySoloAmbient */ - ma_ios_session_category_playback, /* AVAudioSessionCategoryPlayback */ - ma_ios_session_category_record, /* AVAudioSessionCategoryRecord */ - ma_ios_session_category_play_and_record, /* AVAudioSessionCategoryPlayAndRecord */ - ma_ios_session_category_multi_route /* AVAudioSessionCategoryMultiRoute */ -} ma_ios_session_category; - -/* iOS/tvOS/watchOS session category options */ -typedef enum -{ - ma_ios_session_category_option_mix_with_others = 0x01, /* AVAudioSessionCategoryOptionMixWithOthers */ - ma_ios_session_category_option_duck_others = 0x02, /* AVAudioSessionCategoryOptionDuckOthers */ - ma_ios_session_category_option_allow_bluetooth = 0x04, /* AVAudioSessionCategoryOptionAllowBluetooth */ - ma_ios_session_category_option_default_to_speaker = 0x08, /* AVAudioSessionCategoryOptionDefaultToSpeaker */ - ma_ios_session_category_option_interrupt_spoken_audio_and_mix_with_others = 0x11, /* AVAudioSessionCategoryOptionInterruptSpokenAudioAndMixWithOthers */ - ma_ios_session_category_option_allow_bluetooth_a2dp = 0x20, /* AVAudioSessionCategoryOptionAllowBluetoothA2DP */ - ma_ios_session_category_option_allow_air_play = 0x40, /* AVAudioSessionCategoryOptionAllowAirPlay */ -} ma_ios_session_category_option; - -/* OpenSL stream types. */ -typedef enum -{ - ma_opensl_stream_type_default = 0, /* Leaves the stream type unset. */ - ma_opensl_stream_type_voice, /* SL_ANDROID_STREAM_VOICE */ - ma_opensl_stream_type_system, /* SL_ANDROID_STREAM_SYSTEM */ - ma_opensl_stream_type_ring, /* SL_ANDROID_STREAM_RING */ - ma_opensl_stream_type_media, /* SL_ANDROID_STREAM_MEDIA */ - ma_opensl_stream_type_alarm, /* SL_ANDROID_STREAM_ALARM */ - ma_opensl_stream_type_notification /* SL_ANDROID_STREAM_NOTIFICATION */ -} ma_opensl_stream_type; - -/* OpenSL recording presets. */ -typedef enum -{ - ma_opensl_recording_preset_default = 0, /* Leaves the input preset unset. */ - ma_opensl_recording_preset_generic, /* SL_ANDROID_RECORDING_PRESET_GENERIC */ - ma_opensl_recording_preset_camcorder, /* SL_ANDROID_RECORDING_PRESET_CAMCORDER */ - ma_opensl_recording_preset_voice_recognition, /* SL_ANDROID_RECORDING_PRESET_VOICE_RECOGNITION */ - ma_opensl_recording_preset_voice_communication, /* SL_ANDROID_RECORDING_PRESET_VOICE_COMMUNICATION */ - ma_opensl_recording_preset_voice_unprocessed /* SL_ANDROID_RECORDING_PRESET_UNPROCESSED */ -} ma_opensl_recording_preset; - -/* WASAPI audio thread priority characteristics. */ -typedef enum -{ - ma_wasapi_usage_default = 0, - ma_wasapi_usage_games, - ma_wasapi_usage_pro_audio, -} ma_wasapi_usage; - -/* AAudio usage types. */ -typedef enum -{ - ma_aaudio_usage_default = 0, /* Leaves the usage type unset. */ - ma_aaudio_usage_media, /* AAUDIO_USAGE_MEDIA */ - ma_aaudio_usage_voice_communication, /* AAUDIO_USAGE_VOICE_COMMUNICATION */ - ma_aaudio_usage_voice_communication_signalling, /* AAUDIO_USAGE_VOICE_COMMUNICATION_SIGNALLING */ - ma_aaudio_usage_alarm, /* AAUDIO_USAGE_ALARM */ - ma_aaudio_usage_notification, /* AAUDIO_USAGE_NOTIFICATION */ - ma_aaudio_usage_notification_ringtone, /* AAUDIO_USAGE_NOTIFICATION_RINGTONE */ - ma_aaudio_usage_notification_event, /* AAUDIO_USAGE_NOTIFICATION_EVENT */ - ma_aaudio_usage_assistance_accessibility, /* AAUDIO_USAGE_ASSISTANCE_ACCESSIBILITY */ - ma_aaudio_usage_assistance_navigation_guidance, /* AAUDIO_USAGE_ASSISTANCE_NAVIGATION_GUIDANCE */ - ma_aaudio_usage_assistance_sonification, /* AAUDIO_USAGE_ASSISTANCE_SONIFICATION */ - ma_aaudio_usage_game, /* AAUDIO_USAGE_GAME */ - ma_aaudio_usage_assitant, /* AAUDIO_USAGE_ASSISTANT */ - ma_aaudio_usage_emergency, /* AAUDIO_SYSTEM_USAGE_EMERGENCY */ - ma_aaudio_usage_safety, /* AAUDIO_SYSTEM_USAGE_SAFETY */ - ma_aaudio_usage_vehicle_status, /* AAUDIO_SYSTEM_USAGE_VEHICLE_STATUS */ - ma_aaudio_usage_announcement /* AAUDIO_SYSTEM_USAGE_ANNOUNCEMENT */ -} ma_aaudio_usage; - -/* AAudio content types. */ -typedef enum -{ - ma_aaudio_content_type_default = 0, /* Leaves the content type unset. */ - ma_aaudio_content_type_speech, /* AAUDIO_CONTENT_TYPE_SPEECH */ - ma_aaudio_content_type_music, /* AAUDIO_CONTENT_TYPE_MUSIC */ - ma_aaudio_content_type_movie, /* AAUDIO_CONTENT_TYPE_MOVIE */ - ma_aaudio_content_type_sonification /* AAUDIO_CONTENT_TYPE_SONIFICATION */ -} ma_aaudio_content_type; - -/* AAudio input presets. */ -typedef enum -{ - ma_aaudio_input_preset_default = 0, /* Leaves the input preset unset. */ - ma_aaudio_input_preset_generic, /* AAUDIO_INPUT_PRESET_GENERIC */ - ma_aaudio_input_preset_camcorder, /* AAUDIO_INPUT_PRESET_CAMCORDER */ - ma_aaudio_input_preset_voice_recognition, /* AAUDIO_INPUT_PRESET_VOICE_RECOGNITION */ - ma_aaudio_input_preset_voice_communication, /* AAUDIO_INPUT_PRESET_VOICE_COMMUNICATION */ - ma_aaudio_input_preset_unprocessed, /* AAUDIO_INPUT_PRESET_UNPROCESSED */ - ma_aaudio_input_preset_voice_performance /* AAUDIO_INPUT_PRESET_VOICE_PERFORMANCE */ -} ma_aaudio_input_preset; - -typedef enum -{ - ma_aaudio_allow_capture_default = 0, /* Leaves the allowed capture policy unset. */ - ma_aaudio_allow_capture_by_all, /* AAUDIO_ALLOW_CAPTURE_BY_ALL */ - ma_aaudio_allow_capture_by_system, /* AAUDIO_ALLOW_CAPTURE_BY_SYSTEM */ - ma_aaudio_allow_capture_by_none /* AAUDIO_ALLOW_CAPTURE_BY_NONE */ -} ma_aaudio_allowed_capture_policy; - -typedef union -{ - ma_int64 counter; - double counterD; -} ma_timer; - -typedef union -{ - ma_wchar_win32 wasapi[64]; /* WASAPI uses a wchar_t string for identification. */ - ma_uint8 dsound[16]; /* DirectSound uses a GUID for identification. */ - /*UINT_PTR*/ ma_uint32 winmm; /* When creating a device, WinMM expects a Win32 UINT_PTR for device identification. In practice it's actually just a UINT. */ - char alsa[256]; /* ALSA uses a name string for identification. */ - char pulse[256]; /* PulseAudio uses a name string for identification. */ - int jack; /* JACK always uses default devices. */ - char coreaudio[256]; /* Core Audio uses a string for identification. */ - char sndio[256]; /* "snd/0", etc. */ - char audio4[256]; /* "/dev/audio", etc. */ - char oss[64]; /* "dev/dsp0", etc. "dev/dsp" for the default device. */ - ma_int32 aaudio; /* AAudio uses a 32-bit integer for identification. */ - ma_uint32 opensl; /* OpenSL|ES uses a 32-bit unsigned integer for identification. */ - char webaudio[32]; /* Web Audio always uses default devices for now, but if this changes it'll be a GUID. */ - union - { - int i; - char s[256]; - void* p; - } custom; /* The custom backend could be anything. Give them a few options. */ - int nullbackend; /* The null backend uses an integer for device IDs. */ -} ma_device_id; - -MA_API ma_bool32 ma_device_id_equal(const ma_device_id* pA, const ma_device_id* pB); - - -typedef struct ma_context_config ma_context_config; -typedef struct ma_device_config ma_device_config; -typedef struct ma_backend_callbacks ma_backend_callbacks; - -#define MA_DATA_FORMAT_FLAG_EXCLUSIVE_MODE (1U << 1) /* If set, this is supported in exclusive mode. Otherwise not natively supported by exclusive mode. */ - -#ifndef MA_MAX_DEVICE_NAME_LENGTH -#define MA_MAX_DEVICE_NAME_LENGTH 255 -#endif - -typedef struct -{ - /* Basic info. This is the only information guaranteed to be filled in during device enumeration. */ - ma_device_id id; - char name[MA_MAX_DEVICE_NAME_LENGTH + 1]; /* +1 for null terminator. */ - ma_bool32 isDefault; - - ma_uint32 nativeDataFormatCount; - struct - { - ma_format format; /* Sample format. If set to ma_format_unknown, all sample formats are supported. */ - ma_uint32 channels; /* If set to 0, all channels are supported. */ - ma_uint32 sampleRate; /* If set to 0, all sample rates are supported. */ - ma_uint32 flags; /* A combination of MA_DATA_FORMAT_FLAG_* flags. */ - } nativeDataFormats[/*ma_format_count * ma_standard_sample_rate_count * MA_MAX_CHANNELS*/ 64]; /* Not sure how big to make this. There can be *many* permutations for virtual devices which can support anything. */ -} ma_device_info; - -struct ma_device_config -{ - ma_device_type deviceType; - ma_uint32 sampleRate; - ma_uint32 periodSizeInFrames; - ma_uint32 periodSizeInMilliseconds; - ma_uint32 periods; - ma_performance_profile performanceProfile; - ma_bool8 noPreSilencedOutputBuffer; /* When set to true, the contents of the output buffer passed into the data callback will be left undefined rather than initialized to silence. */ - ma_bool8 noClip; /* When set to true, the contents of the output buffer passed into the data callback will not be clipped after returning. Only applies when the playback sample format is f32. */ - ma_bool8 noDisableDenormals; /* Do not disable denormals when firing the data callback. */ - ma_bool8 noFixedSizedCallback; /* Disables strict fixed-sized data callbacks. Setting this to true will result in the period size being treated only as a hint to the backend. This is an optimization for those who don't need fixed sized callbacks. */ - ma_device_data_proc dataCallback; - ma_device_notification_proc notificationCallback; - ma_stop_proc stopCallback; - void* pUserData; - ma_resampler_config resampling; - struct - { - const ma_device_id* pDeviceID; - ma_format format; - ma_uint32 channels; - ma_channel* pChannelMap; - ma_channel_mix_mode channelMixMode; - ma_bool32 calculateLFEFromSpatialChannels; /* When an output LFE channel is present, but no input LFE, set to true to set the output LFE to the average of all spatial channels (LR, FR, etc.). Ignored when an input LFE is present. */ - ma_share_mode shareMode; - } playback; - struct - { - const ma_device_id* pDeviceID; - ma_format format; - ma_uint32 channels; - ma_channel* pChannelMap; - ma_channel_mix_mode channelMixMode; - ma_bool32 calculateLFEFromSpatialChannels; /* When an output LFE channel is present, but no input LFE, set to true to set the output LFE to the average of all spatial channels (LR, FR, etc.). Ignored when an input LFE is present. */ - ma_share_mode shareMode; - } capture; - - struct - { - ma_wasapi_usage usage; /* When configured, uses Avrt APIs to set the thread characteristics. */ - ma_bool8 noAutoConvertSRC; /* When set to true, disables the use of AUDCLNT_STREAMFLAGS_AUTOCONVERTPCM. */ - ma_bool8 noDefaultQualitySRC; /* When set to true, disables the use of AUDCLNT_STREAMFLAGS_SRC_DEFAULT_QUALITY. */ - ma_bool8 noAutoStreamRouting; /* Disables automatic stream routing. */ - ma_bool8 noHardwareOffloading; /* Disables WASAPI's hardware offloading feature. */ - ma_uint32 loopbackProcessID; /* The process ID to include or exclude for loopback mode. Set to 0 to capture audio from all processes. Ignored when an explicit device ID is specified. */ - ma_bool8 loopbackProcessExclude; /* When set to true, excludes the process specified by loopbackProcessID. By default, the process will be included. */ - } wasapi; - struct - { - ma_bool32 noMMap; /* Disables MMap mode. */ - ma_bool32 noAutoFormat; /* Opens the ALSA device with SND_PCM_NO_AUTO_FORMAT. */ - ma_bool32 noAutoChannels; /* Opens the ALSA device with SND_PCM_NO_AUTO_CHANNELS. */ - ma_bool32 noAutoResample; /* Opens the ALSA device with SND_PCM_NO_AUTO_RESAMPLE. */ - } alsa; - struct - { - const char* pStreamNamePlayback; - const char* pStreamNameCapture; - int channelMap; - } pulse; - struct - { - ma_bool32 allowNominalSampleRateChange; /* Desktop only. When enabled, allows changing of the sample rate at the operating system level. */ - } coreaudio; - struct - { - ma_opensl_stream_type streamType; - ma_opensl_recording_preset recordingPreset; - ma_bool32 enableCompatibilityWorkarounds; - } opensl; - struct - { - ma_aaudio_usage usage; - ma_aaudio_content_type contentType; - ma_aaudio_input_preset inputPreset; - ma_aaudio_allowed_capture_policy allowedCapturePolicy; - ma_bool32 noAutoStartAfterReroute; - ma_bool32 enableCompatibilityWorkarounds; - ma_bool32 allowSetBufferCapacity; - } aaudio; -}; - - -/* -The callback for handling device enumeration. This is fired from `ma_context_enumerate_devices()`. - - -Parameters ----------- -pContext (in) - A pointer to the context performing the enumeration. - -deviceType (in) - The type of the device being enumerated. This will always be either `ma_device_type_playback` or `ma_device_type_capture`. - -pInfo (in) - A pointer to a `ma_device_info` containing the ID and name of the enumerated device. Note that this will not include detailed information about the device, - only basic information (ID and name). The reason for this is that it would otherwise require opening the backend device to probe for the information which - is too inefficient. - -pUserData (in) - The user data pointer passed into `ma_context_enumerate_devices()`. -*/ -typedef ma_bool32 (* ma_enum_devices_callback_proc)(ma_context* pContext, ma_device_type deviceType, const ma_device_info* pInfo, void* pUserData); - - -/* -Describes some basic details about a playback or capture device. -*/ -typedef struct -{ - const ma_device_id* pDeviceID; - ma_share_mode shareMode; - ma_format format; - ma_uint32 channels; - ma_uint32 sampleRate; - ma_channel channelMap[MA_MAX_CHANNELS]; - ma_uint32 periodSizeInFrames; - ma_uint32 periodSizeInMilliseconds; - ma_uint32 periodCount; -} ma_device_descriptor; - -/* -These are the callbacks required to be implemented for a backend. These callbacks are grouped into two parts: context and device. There is one context -to many devices. A device is created from a context. - -The general flow goes like this: - - 1) A context is created with `onContextInit()` - 1a) Available devices can be enumerated with `onContextEnumerateDevices()` if required. - 1b) Detailed information about a device can be queried with `onContextGetDeviceInfo()` if required. - 2) A device is created from the context that was created in the first step using `onDeviceInit()`, and optionally a device ID that was - selected from device enumeration via `onContextEnumerateDevices()`. - 3) A device is started or stopped with `onDeviceStart()` / `onDeviceStop()` - 4) Data is delivered to and from the device by the backend. This is always done based on the native format returned by the prior call - to `onDeviceInit()`. Conversion between the device's native format and the format requested by the application will be handled by - miniaudio internally. - -Initialization of the context is quite simple. You need to do any necessary initialization of internal objects and then output the -callbacks defined in this structure. - -Once the context has been initialized you can initialize a device. Before doing so, however, the application may want to know which -physical devices are available. This is where `onContextEnumerateDevices()` comes in. This is fairly simple. For each device, fire the -given callback with, at a minimum, the basic information filled out in `ma_device_info`. When the callback returns `MA_FALSE`, enumeration -needs to stop and the `onContextEnumerateDevices()` function returns with a success code. - -Detailed device information can be retrieved from a device ID using `onContextGetDeviceInfo()`. This takes as input the device type and ID, -and on output returns detailed information about the device in `ma_device_info`. The `onContextGetDeviceInfo()` callback must handle the -case when the device ID is NULL, in which case information about the default device needs to be retrieved. - -Once the context has been created and the device ID retrieved (if using anything other than the default device), the device can be created. -This is a little bit more complicated than initialization of the context due to its more complicated configuration. When initializing a -device, a duplex device may be requested. This means a separate data format needs to be specified for both playback and capture. On input, -the data format is set to what the application wants. On output it's set to the native format which should match as closely as possible to -the requested format. The conversion between the format requested by the application and the device's native format will be handled -internally by miniaudio. - -On input, if the sample format is set to `ma_format_unknown`, the backend is free to use whatever sample format it desires, so long as it's -supported by miniaudio. When the channel count is set to 0, the backend should use the device's native channel count. The same applies for -sample rate. For the channel map, the default should be used when `ma_channel_map_is_blank()` returns true (all channels set to -`MA_CHANNEL_NONE`). On input, the `periodSizeInFrames` or `periodSizeInMilliseconds` option should always be set. The backend should -inspect both of these variables. If `periodSizeInFrames` is set, it should take priority, otherwise it needs to be derived from the period -size in milliseconds (`periodSizeInMilliseconds`) and the sample rate, keeping in mind that the sample rate may be 0, in which case the -sample rate will need to be determined before calculating the period size in frames. On output, all members of the `ma_device_descriptor` -object should be set to a valid value, except for `periodSizeInMilliseconds` which is optional (`periodSizeInFrames` *must* be set). - -Starting and stopping of the device is done with `onDeviceStart()` and `onDeviceStop()` and should be self-explanatory. If the backend uses -asynchronous reading and writing, `onDeviceStart()` and `onDeviceStop()` should always be implemented. - -The handling of data delivery between the application and the device is the most complicated part of the process. To make this a bit -easier, some helper callbacks are available. If the backend uses a blocking read/write style of API, the `onDeviceRead()` and -`onDeviceWrite()` callbacks can optionally be implemented. These are blocking and work just like reading and writing from a file. If the -backend uses a callback for data delivery, that callback must call `ma_device_handle_backend_data_callback()` from within its callback. -This allows miniaudio to then process any necessary data conversion and then pass it to the miniaudio data callback. - -If the backend requires absolute flexibility with its data delivery, it can optionally implement the `onDeviceDataLoop()` callback -which will allow it to implement the logic that will run on the audio thread. This is much more advanced and is completely optional. - -The audio thread should run data delivery logic in a loop while `ma_device_get_state() == ma_device_state_started` and no errors have been -encountered. Do not start or stop the device here. That will be handled from outside the `onDeviceDataLoop()` callback. - -The invocation of the `onDeviceDataLoop()` callback will be handled by miniaudio. When you start the device, miniaudio will fire this -callback. When the device is stopped, the `ma_device_get_state() == ma_device_state_started` condition will fail and the loop will be terminated -which will then fall through to the part that stops the device. For an example on how to implement the `onDeviceDataLoop()` callback, -look at `ma_device_audio_thread__default_read_write()`. Implement the `onDeviceDataLoopWakeup()` callback if you need a mechanism to -wake up the audio thread. - -If the backend supports an optimized retrieval of device information from an initialized `ma_device` object, it should implement the -`onDeviceGetInfo()` callback. This is optional, in which case it will fall back to `onContextGetDeviceInfo()` which is less efficient. -*/ -struct ma_backend_callbacks -{ - ma_result (* onContextInit)(ma_context* pContext, const ma_context_config* pConfig, ma_backend_callbacks* pCallbacks); - ma_result (* onContextUninit)(ma_context* pContext); - ma_result (* onContextEnumerateDevices)(ma_context* pContext, ma_enum_devices_callback_proc callback, void* pUserData); - ma_result (* onContextGetDeviceInfo)(ma_context* pContext, ma_device_type deviceType, const ma_device_id* pDeviceID, ma_device_info* pDeviceInfo); - ma_result (* onDeviceInit)(ma_device* pDevice, const ma_device_config* pConfig, ma_device_descriptor* pDescriptorPlayback, ma_device_descriptor* pDescriptorCapture); - ma_result (* onDeviceUninit)(ma_device* pDevice); - ma_result (* onDeviceStart)(ma_device* pDevice); - ma_result (* onDeviceStop)(ma_device* pDevice); - ma_result (* onDeviceRead)(ma_device* pDevice, void* pFrames, ma_uint32 frameCount, ma_uint32* pFramesRead); - ma_result (* onDeviceWrite)(ma_device* pDevice, const void* pFrames, ma_uint32 frameCount, ma_uint32* pFramesWritten); - ma_result (* onDeviceDataLoop)(ma_device* pDevice); - ma_result (* onDeviceDataLoopWakeup)(ma_device* pDevice); - ma_result (* onDeviceGetInfo)(ma_device* pDevice, ma_device_type type, ma_device_info* pDeviceInfo); -}; - -struct ma_context_config -{ - ma_log* pLog; - ma_thread_priority threadPriority; - size_t threadStackSize; - void* pUserData; - ma_allocation_callbacks allocationCallbacks; - struct - { - ma_handle hWnd; /* HWND. Optional window handle to pass into SetCooperativeLevel(). Will default to the foreground window, and if that fails, the desktop window. */ - } dsound; - struct - { - ma_bool32 useVerboseDeviceEnumeration; - } alsa; - struct - { - const char* pApplicationName; - const char* pServerName; - ma_bool32 tryAutoSpawn; /* Enables autospawning of the PulseAudio daemon if necessary. */ - } pulse; - struct - { - ma_ios_session_category sessionCategory; - ma_uint32 sessionCategoryOptions; - ma_bool32 noAudioSessionActivate; /* iOS only. When set to true, does not perform an explicit [[AVAudioSession sharedInstace] setActive:true] on initialization. */ - ma_bool32 noAudioSessionDeactivate; /* iOS only. When set to true, does not perform an explicit [[AVAudioSession sharedInstace] setActive:false] on uninitialization. */ - } coreaudio; - struct - { - const char* pClientName; - ma_bool32 tryStartServer; - } jack; - ma_backend_callbacks custom; -}; - -/* WASAPI specific structure for some commands which must run on a common thread due to bugs in WASAPI. */ -typedef struct -{ - int code; - ma_event* pEvent; /* This will be signalled when the event is complete. */ - union - { - struct - { - int _unused; - } quit; - struct - { - ma_device_type deviceType; - void* pAudioClient; - void** ppAudioClientService; - ma_result* pResult; /* The result from creating the audio client service. */ - } createAudioClient; - struct - { - ma_device* pDevice; - ma_device_type deviceType; - } releaseAudioClient; - } data; -} ma_context_command__wasapi; - -struct ma_context -{ - ma_backend_callbacks callbacks; - ma_backend backend; /* DirectSound, ALSA, etc. */ - ma_log* pLog; - ma_log log; /* Only used if the log is owned by the context. The pLog member will be set to &log in this case. */ - ma_thread_priority threadPriority; - size_t threadStackSize; - void* pUserData; - ma_allocation_callbacks allocationCallbacks; - ma_mutex deviceEnumLock; /* Used to make ma_context_get_devices() thread safe. */ - ma_mutex deviceInfoLock; /* Used to make ma_context_get_device_info() thread safe. */ - ma_uint32 deviceInfoCapacity; /* Total capacity of pDeviceInfos. */ - ma_uint32 playbackDeviceInfoCount; - ma_uint32 captureDeviceInfoCount; - ma_device_info* pDeviceInfos; /* Playback devices first, then capture. */ - - union - { -#ifdef MA_SUPPORT_WASAPI - struct - { - ma_thread commandThread; - ma_mutex commandLock; - ma_semaphore commandSem; - ma_uint32 commandIndex; - ma_uint32 commandCount; - ma_context_command__wasapi commands[4]; - ma_handle hAvrt; - ma_proc AvSetMmThreadCharacteristicsA; - ma_proc AvRevertMmThreadcharacteristics; - ma_handle hMMDevapi; - ma_proc ActivateAudioInterfaceAsync; - } wasapi; -#endif -#ifdef MA_SUPPORT_DSOUND - struct - { - ma_handle hWnd; /* Can be null. */ - ma_handle hDSoundDLL; - ma_proc DirectSoundCreate; - ma_proc DirectSoundEnumerateA; - ma_proc DirectSoundCaptureCreate; - ma_proc DirectSoundCaptureEnumerateA; - } dsound; -#endif -#ifdef MA_SUPPORT_WINMM - struct - { - ma_handle hWinMM; - ma_proc waveOutGetNumDevs; - ma_proc waveOutGetDevCapsA; - ma_proc waveOutOpen; - ma_proc waveOutClose; - ma_proc waveOutPrepareHeader; - ma_proc waveOutUnprepareHeader; - ma_proc waveOutWrite; - ma_proc waveOutReset; - ma_proc waveInGetNumDevs; - ma_proc waveInGetDevCapsA; - ma_proc waveInOpen; - ma_proc waveInClose; - ma_proc waveInPrepareHeader; - ma_proc waveInUnprepareHeader; - ma_proc waveInAddBuffer; - ma_proc waveInStart; - ma_proc waveInReset; - } winmm; -#endif -#ifdef MA_SUPPORT_ALSA - struct - { - ma_handle asoundSO; - ma_proc snd_pcm_open; - ma_proc snd_pcm_close; - ma_proc snd_pcm_hw_params_sizeof; - ma_proc snd_pcm_hw_params_any; - ma_proc snd_pcm_hw_params_set_format; - ma_proc snd_pcm_hw_params_set_format_first; - ma_proc snd_pcm_hw_params_get_format_mask; - ma_proc snd_pcm_hw_params_set_channels; - ma_proc snd_pcm_hw_params_set_channels_near; - ma_proc snd_pcm_hw_params_set_channels_minmax; - ma_proc snd_pcm_hw_params_set_rate_resample; - ma_proc snd_pcm_hw_params_set_rate; - ma_proc snd_pcm_hw_params_set_rate_near; - ma_proc snd_pcm_hw_params_set_buffer_size_near; - ma_proc snd_pcm_hw_params_set_periods_near; - ma_proc snd_pcm_hw_params_set_access; - ma_proc snd_pcm_hw_params_get_format; - ma_proc snd_pcm_hw_params_get_channels; - ma_proc snd_pcm_hw_params_get_channels_min; - ma_proc snd_pcm_hw_params_get_channels_max; - ma_proc snd_pcm_hw_params_get_rate; - ma_proc snd_pcm_hw_params_get_rate_min; - ma_proc snd_pcm_hw_params_get_rate_max; - ma_proc snd_pcm_hw_params_get_buffer_size; - ma_proc snd_pcm_hw_params_get_periods; - ma_proc snd_pcm_hw_params_get_access; - ma_proc snd_pcm_hw_params_test_format; - ma_proc snd_pcm_hw_params_test_channels; - ma_proc snd_pcm_hw_params_test_rate; - ma_proc snd_pcm_hw_params; - ma_proc snd_pcm_sw_params_sizeof; - ma_proc snd_pcm_sw_params_current; - ma_proc snd_pcm_sw_params_get_boundary; - ma_proc snd_pcm_sw_params_set_avail_min; - ma_proc snd_pcm_sw_params_set_start_threshold; - ma_proc snd_pcm_sw_params_set_stop_threshold; - ma_proc snd_pcm_sw_params; - ma_proc snd_pcm_format_mask_sizeof; - ma_proc snd_pcm_format_mask_test; - ma_proc snd_pcm_get_chmap; - ma_proc snd_pcm_state; - ma_proc snd_pcm_prepare; - ma_proc snd_pcm_start; - ma_proc snd_pcm_drop; - ma_proc snd_pcm_drain; - ma_proc snd_pcm_reset; - ma_proc snd_device_name_hint; - ma_proc snd_device_name_get_hint; - ma_proc snd_card_get_index; - ma_proc snd_device_name_free_hint; - ma_proc snd_pcm_mmap_begin; - ma_proc snd_pcm_mmap_commit; - ma_proc snd_pcm_recover; - ma_proc snd_pcm_readi; - ma_proc snd_pcm_writei; - ma_proc snd_pcm_avail; - ma_proc snd_pcm_avail_update; - ma_proc snd_pcm_wait; - ma_proc snd_pcm_nonblock; - ma_proc snd_pcm_info; - ma_proc snd_pcm_info_sizeof; - ma_proc snd_pcm_info_get_name; - ma_proc snd_pcm_poll_descriptors; - ma_proc snd_pcm_poll_descriptors_count; - ma_proc snd_pcm_poll_descriptors_revents; - ma_proc snd_config_update_free_global; - - ma_mutex internalDeviceEnumLock; - ma_bool32 useVerboseDeviceEnumeration; - } alsa; -#endif -#ifdef MA_SUPPORT_PULSEAUDIO - struct - { - ma_handle pulseSO; - ma_proc pa_mainloop_new; - ma_proc pa_mainloop_free; - ma_proc pa_mainloop_quit; - ma_proc pa_mainloop_get_api; - ma_proc pa_mainloop_iterate; - ma_proc pa_mainloop_wakeup; - ma_proc pa_threaded_mainloop_new; - ma_proc pa_threaded_mainloop_free; - ma_proc pa_threaded_mainloop_start; - ma_proc pa_threaded_mainloop_stop; - ma_proc pa_threaded_mainloop_lock; - ma_proc pa_threaded_mainloop_unlock; - ma_proc pa_threaded_mainloop_wait; - ma_proc pa_threaded_mainloop_signal; - ma_proc pa_threaded_mainloop_accept; - ma_proc pa_threaded_mainloop_get_retval; - ma_proc pa_threaded_mainloop_get_api; - ma_proc pa_threaded_mainloop_in_thread; - ma_proc pa_threaded_mainloop_set_name; - ma_proc pa_context_new; - ma_proc pa_context_unref; - ma_proc pa_context_connect; - ma_proc pa_context_disconnect; - ma_proc pa_context_set_state_callback; - ma_proc pa_context_get_state; - ma_proc pa_context_get_sink_info_list; - ma_proc pa_context_get_source_info_list; - ma_proc pa_context_get_sink_info_by_name; - ma_proc pa_context_get_source_info_by_name; - ma_proc pa_operation_unref; - ma_proc pa_operation_get_state; - ma_proc pa_channel_map_init_extend; - ma_proc pa_channel_map_valid; - ma_proc pa_channel_map_compatible; - ma_proc pa_stream_new; - ma_proc pa_stream_unref; - ma_proc pa_stream_connect_playback; - ma_proc pa_stream_connect_record; - ma_proc pa_stream_disconnect; - ma_proc pa_stream_get_state; - ma_proc pa_stream_get_sample_spec; - ma_proc pa_stream_get_channel_map; - ma_proc pa_stream_get_buffer_attr; - ma_proc pa_stream_set_buffer_attr; - ma_proc pa_stream_get_device_name; - ma_proc pa_stream_set_write_callback; - ma_proc pa_stream_set_read_callback; - ma_proc pa_stream_set_suspended_callback; - ma_proc pa_stream_set_moved_callback; - ma_proc pa_stream_is_suspended; - ma_proc pa_stream_flush; - ma_proc pa_stream_drain; - ma_proc pa_stream_is_corked; - ma_proc pa_stream_cork; - ma_proc pa_stream_trigger; - ma_proc pa_stream_begin_write; - ma_proc pa_stream_write; - ma_proc pa_stream_peek; - ma_proc pa_stream_drop; - ma_proc pa_stream_writable_size; - ma_proc pa_stream_readable_size; - - /*pa_mainloop**/ ma_ptr pMainLoop; - /*pa_context**/ ma_ptr pPulseContext; - char* pApplicationName; /* Set when the context is initialized. Used by devices for their local pa_context objects. */ - char* pServerName; /* Set when the context is initialized. Used by devices for their local pa_context objects. */ - } pulse; -#endif -#ifdef MA_SUPPORT_JACK - struct - { - ma_handle jackSO; - ma_proc jack_client_open; - ma_proc jack_client_close; - ma_proc jack_client_name_size; - ma_proc jack_set_process_callback; - ma_proc jack_set_buffer_size_callback; - ma_proc jack_on_shutdown; - ma_proc jack_get_sample_rate; - ma_proc jack_get_buffer_size; - ma_proc jack_get_ports; - ma_proc jack_activate; - ma_proc jack_deactivate; - ma_proc jack_connect; - ma_proc jack_port_register; - ma_proc jack_port_name; - ma_proc jack_port_get_buffer; - ma_proc jack_free; - - char* pClientName; - ma_bool32 tryStartServer; - } jack; -#endif -#ifdef MA_SUPPORT_COREAUDIO - struct - { - ma_handle hCoreFoundation; - ma_proc CFStringGetCString; - ma_proc CFRelease; - - ma_handle hCoreAudio; - ma_proc AudioObjectGetPropertyData; - ma_proc AudioObjectGetPropertyDataSize; - ma_proc AudioObjectSetPropertyData; - ma_proc AudioObjectAddPropertyListener; - ma_proc AudioObjectRemovePropertyListener; - - ma_handle hAudioUnit; /* Could possibly be set to AudioToolbox on later versions of macOS. */ - ma_proc AudioComponentFindNext; - ma_proc AudioComponentInstanceDispose; - ma_proc AudioComponentInstanceNew; - ma_proc AudioOutputUnitStart; - ma_proc AudioOutputUnitStop; - ma_proc AudioUnitAddPropertyListener; - ma_proc AudioUnitGetPropertyInfo; - ma_proc AudioUnitGetProperty; - ma_proc AudioUnitSetProperty; - ma_proc AudioUnitInitialize; - ma_proc AudioUnitRender; - - /*AudioComponent*/ ma_ptr component; - ma_bool32 noAudioSessionDeactivate; /* For tracking whether or not the iOS audio session should be explicitly deactivated. Set from the config in ma_context_init__coreaudio(). */ - } coreaudio; -#endif -#ifdef MA_SUPPORT_SNDIO - struct - { - ma_handle sndioSO; - ma_proc sio_open; - ma_proc sio_close; - ma_proc sio_setpar; - ma_proc sio_getpar; - ma_proc sio_getcap; - ma_proc sio_start; - ma_proc sio_stop; - ma_proc sio_read; - ma_proc sio_write; - ma_proc sio_onmove; - ma_proc sio_nfds; - ma_proc sio_pollfd; - ma_proc sio_revents; - ma_proc sio_eof; - ma_proc sio_setvol; - ma_proc sio_onvol; - ma_proc sio_initpar; - } sndio; -#endif -#ifdef MA_SUPPORT_AUDIO4 - struct - { - int _unused; - } audio4; -#endif -#ifdef MA_SUPPORT_OSS - struct - { - int versionMajor; - int versionMinor; - } oss; -#endif -#ifdef MA_SUPPORT_AAUDIO - struct - { - ma_handle hAAudio; /* libaaudio.so */ - ma_proc AAudio_createStreamBuilder; - ma_proc AAudioStreamBuilder_delete; - ma_proc AAudioStreamBuilder_setDeviceId; - ma_proc AAudioStreamBuilder_setDirection; - ma_proc AAudioStreamBuilder_setSharingMode; - ma_proc AAudioStreamBuilder_setFormat; - ma_proc AAudioStreamBuilder_setChannelCount; - ma_proc AAudioStreamBuilder_setSampleRate; - ma_proc AAudioStreamBuilder_setBufferCapacityInFrames; - ma_proc AAudioStreamBuilder_setFramesPerDataCallback; - ma_proc AAudioStreamBuilder_setDataCallback; - ma_proc AAudioStreamBuilder_setErrorCallback; - ma_proc AAudioStreamBuilder_setPerformanceMode; - ma_proc AAudioStreamBuilder_setUsage; - ma_proc AAudioStreamBuilder_setContentType; - ma_proc AAudioStreamBuilder_setInputPreset; - ma_proc AAudioStreamBuilder_setAllowedCapturePolicy; - ma_proc AAudioStreamBuilder_openStream; - ma_proc AAudioStream_close; - ma_proc AAudioStream_getState; - ma_proc AAudioStream_waitForStateChange; - ma_proc AAudioStream_getFormat; - ma_proc AAudioStream_getChannelCount; - ma_proc AAudioStream_getSampleRate; - ma_proc AAudioStream_getBufferCapacityInFrames; - ma_proc AAudioStream_getFramesPerDataCallback; - ma_proc AAudioStream_getFramesPerBurst; - ma_proc AAudioStream_requestStart; - ma_proc AAudioStream_requestStop; - ma_device_job_thread jobThread; /* For processing operations outside of the error callback, specifically device disconnections and rerouting. */ - } aaudio; -#endif -#ifdef MA_SUPPORT_OPENSL - struct - { - ma_handle libOpenSLES; - ma_handle SL_IID_ENGINE; - ma_handle SL_IID_AUDIOIODEVICECAPABILITIES; - ma_handle SL_IID_ANDROIDSIMPLEBUFFERQUEUE; - ma_handle SL_IID_RECORD; - ma_handle SL_IID_PLAY; - ma_handle SL_IID_OUTPUTMIX; - ma_handle SL_IID_ANDROIDCONFIGURATION; - ma_proc slCreateEngine; - } opensl; -#endif -#ifdef MA_SUPPORT_WEBAUDIO - struct - { - int _unused; - } webaudio; -#endif -#ifdef MA_SUPPORT_NULL - struct - { - int _unused; - } null_backend; -#endif - }; - - union - { -#if defined(MA_WIN32) - struct - { - /*HMODULE*/ ma_handle hOle32DLL; - ma_proc CoInitialize; - ma_proc CoInitializeEx; - ma_proc CoUninitialize; - ma_proc CoCreateInstance; - ma_proc CoTaskMemFree; - ma_proc PropVariantClear; - ma_proc StringFromGUID2; - - /*HMODULE*/ ma_handle hUser32DLL; - ma_proc GetForegroundWindow; - ma_proc GetDesktopWindow; - - /*HMODULE*/ ma_handle hAdvapi32DLL; - ma_proc RegOpenKeyExA; - ma_proc RegCloseKey; - ma_proc RegQueryValueExA; - - /*HRESULT*/ long CoInitializeResult; - } win32; -#endif -#ifdef MA_POSIX - struct - { - int _unused; - } posix; -#endif - int _unused; - }; -}; - -struct ma_device -{ - ma_context* pContext; - ma_device_type type; - ma_uint32 sampleRate; - ma_atomic_device_state state; /* The state of the device is variable and can change at any time on any thread. Must be used atomically. */ - ma_device_data_proc onData; /* Set once at initialization time and should not be changed after. */ - ma_device_notification_proc onNotification; /* Set once at initialization time and should not be changed after. */ - ma_stop_proc onStop; /* DEPRECATED. Use the notification callback instead. Set once at initialization time and should not be changed after. */ - void* pUserData; /* Application defined data. */ - ma_mutex startStopLock; - ma_event wakeupEvent; - ma_event startEvent; - ma_event stopEvent; - ma_thread thread; - ma_result workResult; /* This is set by the worker thread after it's finished doing a job. */ - ma_bool8 isOwnerOfContext; /* When set to true, uninitializing the device will also uninitialize the context. Set to true when NULL is passed into ma_device_init(). */ - ma_bool8 noPreSilencedOutputBuffer; - ma_bool8 noClip; - ma_bool8 noDisableDenormals; - ma_bool8 noFixedSizedCallback; - ma_atomic_float masterVolumeFactor; /* Linear 0..1. Can be read and written simultaneously by different threads. Must be used atomically. */ - ma_duplex_rb duplexRB; /* Intermediary buffer for duplex device on asynchronous backends. */ - struct - { - ma_resample_algorithm algorithm; - ma_resampling_backend_vtable* pBackendVTable; - void* pBackendUserData; - struct - { - ma_uint32 lpfOrder; - } linear; - } resampling; - struct - { - ma_device_id* pID; /* Set to NULL if using default ID, otherwise set to the address of "id". */ - ma_device_id id; /* If using an explicit device, will be set to a copy of the ID used for initialization. Otherwise cleared to 0. */ - char name[MA_MAX_DEVICE_NAME_LENGTH + 1]; /* Maybe temporary. Likely to be replaced with a query API. */ - ma_share_mode shareMode; /* Set to whatever was passed in when the device was initialized. */ - ma_format format; - ma_uint32 channels; - ma_channel channelMap[MA_MAX_CHANNELS]; - ma_format internalFormat; - ma_uint32 internalChannels; - ma_uint32 internalSampleRate; - ma_channel internalChannelMap[MA_MAX_CHANNELS]; - ma_uint32 internalPeriodSizeInFrames; - ma_uint32 internalPeriods; - ma_channel_mix_mode channelMixMode; - ma_bool32 calculateLFEFromSpatialChannels; - ma_data_converter converter; - void* pIntermediaryBuffer; /* For implementing fixed sized buffer callbacks. Will be null if using variable sized callbacks. */ - ma_uint32 intermediaryBufferCap; - ma_uint32 intermediaryBufferLen; /* How many valid frames are sitting in the intermediary buffer. */ - void* pInputCache; /* In external format. Can be null. */ - ma_uint64 inputCacheCap; - ma_uint64 inputCacheConsumed; - ma_uint64 inputCacheRemaining; - } playback; - struct - { - ma_device_id* pID; /* Set to NULL if using default ID, otherwise set to the address of "id". */ - ma_device_id id; /* If using an explicit device, will be set to a copy of the ID used for initialization. Otherwise cleared to 0. */ - char name[MA_MAX_DEVICE_NAME_LENGTH + 1]; /* Maybe temporary. Likely to be replaced with a query API. */ - ma_share_mode shareMode; /* Set to whatever was passed in when the device was initialized. */ - ma_format format; - ma_uint32 channels; - ma_channel channelMap[MA_MAX_CHANNELS]; - ma_format internalFormat; - ma_uint32 internalChannels; - ma_uint32 internalSampleRate; - ma_channel internalChannelMap[MA_MAX_CHANNELS]; - ma_uint32 internalPeriodSizeInFrames; - ma_uint32 internalPeriods; - ma_channel_mix_mode channelMixMode; - ma_bool32 calculateLFEFromSpatialChannels; - ma_data_converter converter; - void* pIntermediaryBuffer; /* For implementing fixed sized buffer callbacks. Will be null if using variable sized callbacks. */ - ma_uint32 intermediaryBufferCap; - ma_uint32 intermediaryBufferLen; /* How many valid frames are sitting in the intermediary buffer. */ - } capture; - - union - { -#ifdef MA_SUPPORT_WASAPI - struct - { - /*IAudioClient**/ ma_ptr pAudioClientPlayback; - /*IAudioClient**/ ma_ptr pAudioClientCapture; - /*IAudioRenderClient**/ ma_ptr pRenderClient; - /*IAudioCaptureClient**/ ma_ptr pCaptureClient; - /*IMMDeviceEnumerator**/ ma_ptr pDeviceEnumerator; /* Used for IMMNotificationClient notifications. Required for detecting default device changes. */ - ma_IMMNotificationClient notificationClient; - /*HANDLE*/ ma_handle hEventPlayback; /* Auto reset. Initialized to signaled. */ - /*HANDLE*/ ma_handle hEventCapture; /* Auto reset. Initialized to unsignaled. */ - ma_uint32 actualBufferSizeInFramesPlayback; /* Value from GetBufferSize(). internalPeriodSizeInFrames is not set to the _actual_ buffer size when low-latency shared mode is being used due to the way the IAudioClient3 API works. */ - ma_uint32 actualBufferSizeInFramesCapture; - ma_uint32 originalPeriodSizeInFrames; - ma_uint32 originalPeriodSizeInMilliseconds; - ma_uint32 originalPeriods; - ma_performance_profile originalPerformanceProfile; - ma_uint32 periodSizeInFramesPlayback; - ma_uint32 periodSizeInFramesCapture; - void* pMappedBufferCapture; - ma_uint32 mappedBufferCaptureCap; - ma_uint32 mappedBufferCaptureLen; - void* pMappedBufferPlayback; - ma_uint32 mappedBufferPlaybackCap; - ma_uint32 mappedBufferPlaybackLen; - ma_atomic_bool32 isStartedCapture; /* Can be read and written simultaneously across different threads. Must be used atomically, and must be 32-bit. */ - ma_atomic_bool32 isStartedPlayback; /* Can be read and written simultaneously across different threads. Must be used atomically, and must be 32-bit. */ - ma_uint32 loopbackProcessID; - ma_bool8 loopbackProcessExclude; - ma_bool8 noAutoConvertSRC; /* When set to true, disables the use of AUDCLNT_STREAMFLAGS_AUTOCONVERTPCM. */ - ma_bool8 noDefaultQualitySRC; /* When set to true, disables the use of AUDCLNT_STREAMFLAGS_SRC_DEFAULT_QUALITY. */ - ma_bool8 noHardwareOffloading; - ma_bool8 allowCaptureAutoStreamRouting; - ma_bool8 allowPlaybackAutoStreamRouting; - ma_bool8 isDetachedPlayback; - ma_bool8 isDetachedCapture; - ma_wasapi_usage usage; - void* hAvrtHandle; - ma_mutex rerouteLock; - } wasapi; -#endif -#ifdef MA_SUPPORT_DSOUND - struct - { - /*LPDIRECTSOUND*/ ma_ptr pPlayback; - /*LPDIRECTSOUNDBUFFER*/ ma_ptr pPlaybackPrimaryBuffer; - /*LPDIRECTSOUNDBUFFER*/ ma_ptr pPlaybackBuffer; - /*LPDIRECTSOUNDCAPTURE*/ ma_ptr pCapture; - /*LPDIRECTSOUNDCAPTUREBUFFER*/ ma_ptr pCaptureBuffer; - } dsound; -#endif -#ifdef MA_SUPPORT_WINMM - struct - { - /*HWAVEOUT*/ ma_handle hDevicePlayback; - /*HWAVEIN*/ ma_handle hDeviceCapture; - /*HANDLE*/ ma_handle hEventPlayback; - /*HANDLE*/ ma_handle hEventCapture; - ma_uint32 fragmentSizeInFrames; - ma_uint32 iNextHeaderPlayback; /* [0,periods). Used as an index into pWAVEHDRPlayback. */ - ma_uint32 iNextHeaderCapture; /* [0,periods). Used as an index into pWAVEHDRCapture. */ - ma_uint32 headerFramesConsumedPlayback; /* The number of PCM frames consumed in the buffer in pWAVEHEADER[iNextHeader]. */ - ma_uint32 headerFramesConsumedCapture; /* ^^^ */ - /*WAVEHDR**/ ma_uint8* pWAVEHDRPlayback; /* One instantiation for each period. */ - /*WAVEHDR**/ ma_uint8* pWAVEHDRCapture; /* One instantiation for each period. */ - ma_uint8* pIntermediaryBufferPlayback; - ma_uint8* pIntermediaryBufferCapture; - ma_uint8* _pHeapData; /* Used internally and is used for the heap allocated data for the intermediary buffer and the WAVEHDR structures. */ - } winmm; -#endif -#ifdef MA_SUPPORT_ALSA - struct - { - /*snd_pcm_t**/ ma_ptr pPCMPlayback; - /*snd_pcm_t**/ ma_ptr pPCMCapture; - /*struct pollfd**/ void* pPollDescriptorsPlayback; - /*struct pollfd**/ void* pPollDescriptorsCapture; - int pollDescriptorCountPlayback; - int pollDescriptorCountCapture; - int wakeupfdPlayback; /* eventfd for waking up from poll() when the playback device is stopped. */ - int wakeupfdCapture; /* eventfd for waking up from poll() when the capture device is stopped. */ - ma_bool8 isUsingMMapPlayback; - ma_bool8 isUsingMMapCapture; - } alsa; -#endif -#ifdef MA_SUPPORT_PULSEAUDIO - struct - { - /*pa_mainloop**/ ma_ptr pMainLoop; - /*pa_context**/ ma_ptr pPulseContext; - /*pa_stream**/ ma_ptr pStreamPlayback; - /*pa_stream**/ ma_ptr pStreamCapture; - } pulse; -#endif -#ifdef MA_SUPPORT_JACK - struct - { - /*jack_client_t**/ ma_ptr pClient; - /*jack_port_t**/ ma_ptr* ppPortsPlayback; - /*jack_port_t**/ ma_ptr* ppPortsCapture; - float* pIntermediaryBufferPlayback; /* Typed as a float because JACK is always floating point. */ - float* pIntermediaryBufferCapture; - } jack; -#endif -#ifdef MA_SUPPORT_COREAUDIO - struct - { - ma_uint32 deviceObjectIDPlayback; - ma_uint32 deviceObjectIDCapture; - /*AudioUnit*/ ma_ptr audioUnitPlayback; - /*AudioUnit*/ ma_ptr audioUnitCapture; - /*AudioBufferList**/ ma_ptr pAudioBufferList; /* Only used for input devices. */ - ma_uint32 audioBufferCapInFrames; /* Only used for input devices. The capacity in frames of each buffer in pAudioBufferList. */ - ma_event stopEvent; - ma_uint32 originalPeriodSizeInFrames; - ma_uint32 originalPeriodSizeInMilliseconds; - ma_uint32 originalPeriods; - ma_performance_profile originalPerformanceProfile; - ma_bool32 isDefaultPlaybackDevice; - ma_bool32 isDefaultCaptureDevice; - ma_bool32 isSwitchingPlaybackDevice; /* <-- Set to true when the default device has changed and miniaudio is in the process of switching. */ - ma_bool32 isSwitchingCaptureDevice; /* <-- Set to true when the default device has changed and miniaudio is in the process of switching. */ - void* pNotificationHandler; /* Only used on mobile platforms. Obj-C object for handling route changes. */ - } coreaudio; -#endif -#ifdef MA_SUPPORT_SNDIO - struct - { - ma_ptr handlePlayback; - ma_ptr handleCapture; - ma_bool32 isStartedPlayback; - ma_bool32 isStartedCapture; - } sndio; -#endif -#ifdef MA_SUPPORT_AUDIO4 - struct - { - int fdPlayback; - int fdCapture; - } audio4; -#endif -#ifdef MA_SUPPORT_OSS - struct - { - int fdPlayback; - int fdCapture; - } oss; -#endif -#ifdef MA_SUPPORT_AAUDIO - struct - { - /*AAudioStream**/ ma_ptr pStreamPlayback; - /*AAudioStream**/ ma_ptr pStreamCapture; - ma_mutex rerouteLock; - ma_aaudio_usage usage; - ma_aaudio_content_type contentType; - ma_aaudio_input_preset inputPreset; - ma_aaudio_allowed_capture_policy allowedCapturePolicy; - ma_bool32 noAutoStartAfterReroute; - } aaudio; -#endif -#ifdef MA_SUPPORT_OPENSL - struct - { - /*SLObjectItf*/ ma_ptr pOutputMixObj; - /*SLOutputMixItf*/ ma_ptr pOutputMix; - /*SLObjectItf*/ ma_ptr pAudioPlayerObj; - /*SLPlayItf*/ ma_ptr pAudioPlayer; - /*SLObjectItf*/ ma_ptr pAudioRecorderObj; - /*SLRecordItf*/ ma_ptr pAudioRecorder; - /*SLAndroidSimpleBufferQueueItf*/ ma_ptr pBufferQueuePlayback; - /*SLAndroidSimpleBufferQueueItf*/ ma_ptr pBufferQueueCapture; - ma_bool32 isDrainingCapture; - ma_bool32 isDrainingPlayback; - ma_uint32 currentBufferIndexPlayback; - ma_uint32 currentBufferIndexCapture; - ma_uint8* pBufferPlayback; /* This is malloc()'d and is used for storing audio data. Typed as ma_uint8 for easy offsetting. */ - ma_uint8* pBufferCapture; - } opensl; -#endif -#ifdef MA_SUPPORT_WEBAUDIO - struct - { - /* AudioWorklets path. */ - /* EMSCRIPTEN_WEBAUDIO_T */ int audioContext; - /* EMSCRIPTEN_WEBAUDIO_T */ int audioWorklet; - float* pIntermediaryBuffer; - void* pStackBuffer; - ma_result initResult; /* Set to MA_BUSY while initialization is in progress. */ - int deviceIndex; /* We store the device in a list on the JavaScript side. This is used to map our C object to the JS object. */ - } webaudio; -#endif -#ifdef MA_SUPPORT_NULL - struct - { - ma_thread deviceThread; - ma_event operationEvent; - ma_event operationCompletionEvent; - ma_semaphore operationSemaphore; - ma_uint32 operation; - ma_result operationResult; - ma_timer timer; - double priorRunTime; - ma_uint32 currentPeriodFramesRemainingPlayback; - ma_uint32 currentPeriodFramesRemainingCapture; - ma_uint64 lastProcessedFramePlayback; - ma_uint64 lastProcessedFrameCapture; - ma_atomic_bool32 isStarted; /* Read and written by multiple threads. Must be used atomically, and must be 32-bit for compiler compatibility. */ - } null_device; -#endif - }; -}; -#if defined(_MSC_VER) && !defined(__clang__) - #pragma warning(pop) -#elif defined(__clang__) || (defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 8))) - #pragma GCC diagnostic pop /* For ISO C99 doesn't support unnamed structs/unions [-Wpedantic] */ -#endif - -/* -Initializes a `ma_context_config` object. - - -Return Value ------------- -A `ma_context_config` initialized to defaults. - - -Remarks -------- -You must always use this to initialize the default state of the `ma_context_config` object. Not using this will result in your program breaking when miniaudio -is updated and new members are added to `ma_context_config`. It also sets logical defaults. - -You can override members of the returned object by changing it's members directly. - - -See Also --------- -ma_context_init() -*/ -MA_API ma_context_config ma_context_config_init(void); - -/* -Initializes a context. - -The context is used for selecting and initializing an appropriate backend and to represent the backend at a more global level than that of an individual -device. There is one context to many devices, and a device is created from a context. A context is required to enumerate devices. - - -Parameters ----------- -backends (in, optional) - A list of backends to try initializing, in priority order. Can be NULL, in which case it uses default priority order. - -backendCount (in, optional) - The number of items in `backend`. Ignored if `backend` is NULL. - -pConfig (in, optional) - The context configuration. - -pContext (in) - A pointer to the context object being initialized. - - -Return Value ------------- -MA_SUCCESS if successful; any other error code otherwise. - - -Thread Safety -------------- -Unsafe. Do not call this function across multiple threads as some backends read and write to global state. - - -Remarks -------- -When `backends` is NULL, the default priority order will be used. Below is a list of backends in priority order: - - |-------------|-----------------------|--------------------------------------------------------| - | Name | Enum Name | Supported Operating Systems | - |-------------|-----------------------|--------------------------------------------------------| - | WASAPI | ma_backend_wasapi | Windows Vista+ | - | DirectSound | ma_backend_dsound | Windows XP+ | - | WinMM | ma_backend_winmm | Windows XP+ (may work on older versions, but untested) | - | Core Audio | ma_backend_coreaudio | macOS, iOS | - | ALSA | ma_backend_alsa | Linux | - | PulseAudio | ma_backend_pulseaudio | Cross Platform (disabled on Windows, BSD and Android) | - | JACK | ma_backend_jack | Cross Platform (disabled on BSD and Android) | - | sndio | ma_backend_sndio | OpenBSD | - | audio(4) | ma_backend_audio4 | NetBSD, OpenBSD | - | OSS | ma_backend_oss | FreeBSD | - | AAudio | ma_backend_aaudio | Android 8+ | - | OpenSL|ES | ma_backend_opensl | Android (API level 16+) | - | Web Audio | ma_backend_webaudio | Web (via Emscripten) | - | Null | ma_backend_null | Cross Platform (not used on Web) | - |-------------|-----------------------|--------------------------------------------------------| - -The context can be configured via the `pConfig` argument. The config object is initialized with `ma_context_config_init()`. Individual configuration settings -can then be set directly on the structure. Below are the members of the `ma_context_config` object. - - pLog - A pointer to the `ma_log` to post log messages to. Can be NULL if the application does not - require logging. See the `ma_log` API for details on how to use the logging system. - - threadPriority - The desired priority to use for the audio thread. Allowable values include the following: - - |--------------------------------------| - | Thread Priority | - |--------------------------------------| - | ma_thread_priority_idle | - | ma_thread_priority_lowest | - | ma_thread_priority_low | - | ma_thread_priority_normal | - | ma_thread_priority_high | - | ma_thread_priority_highest (default) | - | ma_thread_priority_realtime | - | ma_thread_priority_default | - |--------------------------------------| - - threadStackSize - The desired size of the stack for the audio thread. Defaults to the operating system's default. - - pUserData - A pointer to application-defined data. This can be accessed from the context object directly such as `context.pUserData`. - - allocationCallbacks - Structure containing custom allocation callbacks. Leaving this at defaults will cause it to use MA_MALLOC, MA_REALLOC and MA_FREE. These allocation - callbacks will be used for anything tied to the context, including devices. - - alsa.useVerboseDeviceEnumeration - ALSA will typically enumerate many different devices which can be intrusive and not user-friendly. To combat this, miniaudio will enumerate only unique - card/device pairs by default. The problem with this is that you lose a bit of flexibility and control. Setting alsa.useVerboseDeviceEnumeration makes - it so the ALSA backend includes all devices. Defaults to false. - - pulse.pApplicationName - PulseAudio only. The application name to use when initializing the PulseAudio context with `pa_context_new()`. - - pulse.pServerName - PulseAudio only. The name of the server to connect to with `pa_context_connect()`. - - pulse.tryAutoSpawn - PulseAudio only. Whether or not to try automatically starting the PulseAudio daemon. Defaults to false. If you set this to true, keep in mind that - miniaudio uses a trial and error method to find the most appropriate backend, and this will result in the PulseAudio daemon starting which may be - intrusive for the end user. - - coreaudio.sessionCategory - iOS only. The session category to use for the shared AudioSession instance. Below is a list of allowable values and their Core Audio equivalents. - - |-----------------------------------------|-------------------------------------| - | miniaudio Token | Core Audio Token | - |-----------------------------------------|-------------------------------------| - | ma_ios_session_category_ambient | AVAudioSessionCategoryAmbient | - | ma_ios_session_category_solo_ambient | AVAudioSessionCategorySoloAmbient | - | ma_ios_session_category_playback | AVAudioSessionCategoryPlayback | - | ma_ios_session_category_record | AVAudioSessionCategoryRecord | - | ma_ios_session_category_play_and_record | AVAudioSessionCategoryPlayAndRecord | - | ma_ios_session_category_multi_route | AVAudioSessionCategoryMultiRoute | - | ma_ios_session_category_none | AVAudioSessionCategoryAmbient | - | ma_ios_session_category_default | AVAudioSessionCategoryAmbient | - |-----------------------------------------|-------------------------------------| - - coreaudio.sessionCategoryOptions - iOS only. Session category options to use with the shared AudioSession instance. Below is a list of allowable values and their Core Audio equivalents. - - |---------------------------------------------------------------------------|------------------------------------------------------------------| - | miniaudio Token | Core Audio Token | - |---------------------------------------------------------------------------|------------------------------------------------------------------| - | ma_ios_session_category_option_mix_with_others | AVAudioSessionCategoryOptionMixWithOthers | - | ma_ios_session_category_option_duck_others | AVAudioSessionCategoryOptionDuckOthers | - | ma_ios_session_category_option_allow_bluetooth | AVAudioSessionCategoryOptionAllowBluetooth | - | ma_ios_session_category_option_default_to_speaker | AVAudioSessionCategoryOptionDefaultToSpeaker | - | ma_ios_session_category_option_interrupt_spoken_audio_and_mix_with_others | AVAudioSessionCategoryOptionInterruptSpokenAudioAndMixWithOthers | - | ma_ios_session_category_option_allow_bluetooth_a2dp | AVAudioSessionCategoryOptionAllowBluetoothA2DP | - | ma_ios_session_category_option_allow_air_play | AVAudioSessionCategoryOptionAllowAirPlay | - |---------------------------------------------------------------------------|------------------------------------------------------------------| - - coreaudio.noAudioSessionActivate - iOS only. When set to true, does not perform an explicit [[AVAudioSession sharedInstace] setActive:true] on initialization. - - coreaudio.noAudioSessionDeactivate - iOS only. When set to true, does not perform an explicit [[AVAudioSession sharedInstace] setActive:false] on uninitialization. - - jack.pClientName - The name of the client to pass to `jack_client_open()`. - - jack.tryStartServer - Whether or not to try auto-starting the JACK server. Defaults to false. - - -It is recommended that only a single context is active at any given time because it's a bulky data structure which performs run-time linking for the -relevant backends every time it's initialized. - -The location of the context cannot change throughout it's lifetime. Consider allocating the `ma_context` object with `malloc()` if this is an issue. The -reason for this is that a pointer to the context is stored in the `ma_device` structure. - - -Example 1 - Default Initialization ----------------------------------- -The example below shows how to initialize the context using the default configuration. - -```c -ma_context context; -ma_result result = ma_context_init(NULL, 0, NULL, &context); -if (result != MA_SUCCESS) { - // Error. -} -``` - - -Example 2 - Custom Configuration --------------------------------- -The example below shows how to initialize the context using custom backend priorities and a custom configuration. In this hypothetical example, the program -wants to prioritize ALSA over PulseAudio on Linux. They also want to avoid using the WinMM backend on Windows because it's latency is too high. They also -want an error to be returned if no valid backend is available which they achieve by excluding the Null backend. - -For the configuration, the program wants to capture any log messages so they can, for example, route it to a log file and user interface. - -```c -ma_backend backends[] = { - ma_backend_alsa, - ma_backend_pulseaudio, - ma_backend_wasapi, - ma_backend_dsound -}; - -ma_log log; -ma_log_init(&log); -ma_log_register_callback(&log, ma_log_callback_init(my_log_callbac, pMyLogUserData)); - -ma_context_config config = ma_context_config_init(); -config.pLog = &log; // Specify a custom log object in the config so any logs that are posted from ma_context_init() are captured. - -ma_context context; -ma_result result = ma_context_init(backends, sizeof(backends)/sizeof(backends[0]), &config, &context); -if (result != MA_SUCCESS) { - // Error. - if (result == MA_NO_BACKEND) { - // Couldn't find an appropriate backend. - } -} - -// You could also attach a log callback post-initialization: -ma_log_register_callback(ma_context_get_log(&context), ma_log_callback_init(my_log_callback, pMyLogUserData)); -``` - - -See Also --------- -ma_context_config_init() -ma_context_uninit() -*/ -MA_API ma_result ma_context_init(const ma_backend backends[], ma_uint32 backendCount, const ma_context_config* pConfig, ma_context* pContext); - -/* -Uninitializes a context. - - -Return Value ------------- -MA_SUCCESS if successful; any other error code otherwise. - - -Thread Safety -------------- -Unsafe. Do not call this function across multiple threads as some backends read and write to global state. - - -Remarks -------- -Results are undefined if you call this while any device created by this context is still active. - - -See Also --------- -ma_context_init() -*/ -MA_API ma_result ma_context_uninit(ma_context* pContext); - -/* -Retrieves the size of the ma_context object. - -This is mainly for the purpose of bindings to know how much memory to allocate. -*/ -MA_API size_t ma_context_sizeof(void); - -/* -Retrieves a pointer to the log object associated with this context. - - -Remarks -------- -Pass the returned pointer to `ma_log_post()`, `ma_log_postv()` or `ma_log_postf()` to post a log -message. - -You can attach your own logging callback to the log with `ma_log_register_callback()` - - -Return Value ------------- -A pointer to the `ma_log` object that the context uses to post log messages. If some error occurs, -NULL will be returned. -*/ -MA_API ma_log* ma_context_get_log(ma_context* pContext); - -/* -Enumerates over every device (both playback and capture). - -This is a lower-level enumeration function to the easier to use `ma_context_get_devices()`. Use `ma_context_enumerate_devices()` if you would rather not incur -an internal heap allocation, or it simply suits your code better. - -Note that this only retrieves the ID and name/description of the device. The reason for only retrieving basic information is that it would otherwise require -opening the backend device in order to probe it for more detailed information which can be inefficient. Consider using `ma_context_get_device_info()` for this, -but don't call it from within the enumeration callback. - -Returning false from the callback will stop enumeration. Returning true will continue enumeration. - - -Parameters ----------- -pContext (in) - A pointer to the context performing the enumeration. - -callback (in) - The callback to fire for each enumerated device. - -pUserData (in) - A pointer to application-defined data passed to the callback. - - -Return Value ------------- -MA_SUCCESS if successful; any other error code otherwise. - - -Thread Safety -------------- -Safe. This is guarded using a simple mutex lock. - - -Remarks -------- -Do _not_ assume the first enumerated device of a given type is the default device. - -Some backends and platforms may only support default playback and capture devices. - -In general, you should not do anything complicated from within the callback. In particular, do not try initializing a device from within the callback. Also, -do not try to call `ma_context_get_device_info()` from within the callback. - -Consider using `ma_context_get_devices()` for a simpler and safer API, albeit at the expense of an internal heap allocation. - - -Example 1 - Simple Enumeration ------------------------------- -ma_bool32 ma_device_enum_callback(ma_context* pContext, ma_device_type deviceType, const ma_device_info* pInfo, void* pUserData) -{ - printf("Device Name: %s\n", pInfo->name); - return MA_TRUE; -} - -ma_result result = ma_context_enumerate_devices(&context, my_device_enum_callback, pMyUserData); -if (result != MA_SUCCESS) { - // Error. -} - - -See Also --------- -ma_context_get_devices() -*/ -MA_API ma_result ma_context_enumerate_devices(ma_context* pContext, ma_enum_devices_callback_proc callback, void* pUserData); - -/* -Retrieves basic information about every active playback and/or capture device. - -This function will allocate memory internally for the device lists and return a pointer to them through the `ppPlaybackDeviceInfos` and `ppCaptureDeviceInfos` -parameters. If you do not want to incur the overhead of these allocations consider using `ma_context_enumerate_devices()` which will instead use a callback. - -Note that this only retrieves the ID and name/description of the device. The reason for only retrieving basic information is that it would otherwise require -opening the backend device in order to probe it for more detailed information which can be inefficient. Consider using `ma_context_get_device_info()` for this, -but don't call it from within the enumeration callback. - - -Parameters ----------- -pContext (in) - A pointer to the context performing the enumeration. - -ppPlaybackDeviceInfos (out) - A pointer to a pointer that will receive the address of a buffer containing the list of `ma_device_info` structures for playback devices. - -pPlaybackDeviceCount (out) - A pointer to an unsigned integer that will receive the number of playback devices. - -ppCaptureDeviceInfos (out) - A pointer to a pointer that will receive the address of a buffer containing the list of `ma_device_info` structures for capture devices. - -pCaptureDeviceCount (out) - A pointer to an unsigned integer that will receive the number of capture devices. - - -Return Value ------------- -MA_SUCCESS if successful; any other error code otherwise. - - -Thread Safety -------------- -Unsafe. Since each call to this function invalidates the pointers from the previous call, you should not be calling this simultaneously across multiple -threads. Instead, you need to make a copy of the returned data with your own higher level synchronization. - - -Remarks -------- -It is _not_ safe to assume the first device in the list is the default device. - -You can pass in NULL for the playback or capture lists in which case they'll be ignored. - -The returned pointers will become invalid upon the next call this this function, or when the context is uninitialized. Do not free the returned pointers. - - -See Also --------- -ma_context_enumerate_devices() -*/ -MA_API ma_result ma_context_get_devices(ma_context* pContext, ma_device_info** ppPlaybackDeviceInfos, ma_uint32* pPlaybackDeviceCount, ma_device_info** ppCaptureDeviceInfos, ma_uint32* pCaptureDeviceCount); - -/* -Retrieves information about a device of the given type, with the specified ID and share mode. - - -Parameters ----------- -pContext (in) - A pointer to the context performing the query. - -deviceType (in) - The type of the device being queried. Must be either `ma_device_type_playback` or `ma_device_type_capture`. - -pDeviceID (in) - The ID of the device being queried. - -pDeviceInfo (out) - A pointer to the `ma_device_info` structure that will receive the device information. - - -Return Value ------------- -MA_SUCCESS if successful; any other error code otherwise. - - -Thread Safety -------------- -Safe. This is guarded using a simple mutex lock. - - -Remarks -------- -Do _not_ call this from within the `ma_context_enumerate_devices()` callback. - -It's possible for a device to have different information and capabilities depending on whether or not it's opened in shared or exclusive mode. For example, in -shared mode, WASAPI always uses floating point samples for mixing, but in exclusive mode it can be anything. Therefore, this function allows you to specify -which share mode you want information for. Note that not all backends and devices support shared or exclusive mode, in which case this function will fail if -the requested share mode is unsupported. - -This leaves pDeviceInfo unmodified in the result of an error. -*/ -MA_API ma_result ma_context_get_device_info(ma_context* pContext, ma_device_type deviceType, const ma_device_id* pDeviceID, ma_device_info* pDeviceInfo); - -/* -Determines if the given context supports loopback mode. - - -Parameters ----------- -pContext (in) - A pointer to the context getting queried. - - -Return Value ------------- -MA_TRUE if the context supports loopback mode; MA_FALSE otherwise. -*/ -MA_API ma_bool32 ma_context_is_loopback_supported(ma_context* pContext); - - - -/* -Initializes a device config with default settings. - - -Parameters ----------- -deviceType (in) - The type of the device this config is being initialized for. This must set to one of the following: - - |-------------------------| - | Device Type | - |-------------------------| - | ma_device_type_playback | - | ma_device_type_capture | - | ma_device_type_duplex | - | ma_device_type_loopback | - |-------------------------| - - -Return Value ------------- -A new device config object with default settings. You will typically want to adjust the config after this function returns. See remarks. - - -Thread Safety -------------- -Safe. - - -Callback Safety ---------------- -Safe, but don't try initializing a device in a callback. - - -Remarks -------- -The returned config will be initialized to defaults. You will normally want to customize a few variables before initializing the device. See Example 1 for a -typical configuration which sets the sample format, channel count, sample rate, data callback and user data. These are usually things you will want to change -before initializing the device. - -See `ma_device_init()` for details on specific configuration options. - - -Example 1 - Simple Configuration --------------------------------- -The example below is what a program will typically want to configure for each device at a minimum. Notice how `ma_device_config_init()` is called first, and -then the returned object is modified directly. This is important because it ensures that your program continues to work as new configuration options are added -to the `ma_device_config` structure. - -```c -ma_device_config config = ma_device_config_init(ma_device_type_playback); -config.playback.format = ma_format_f32; -config.playback.channels = 2; -config.sampleRate = 48000; -config.dataCallback = ma_data_callback; -config.pUserData = pMyUserData; -``` - - -See Also --------- -ma_device_init() -ma_device_init_ex() -*/ -MA_API ma_device_config ma_device_config_init(ma_device_type deviceType); - - -/* -Initializes a device. - -A device represents a physical audio device. The idea is you send or receive audio data from the device to either play it back through a speaker, or capture it -from a microphone. Whether or not you should send or receive data from the device (or both) depends on the type of device you are initializing which can be -playback, capture, full-duplex or loopback. (Note that loopback mode is only supported on select backends.) Sending and receiving audio data to and from the -device is done via a callback which is fired by miniaudio at periodic time intervals. - -The frequency at which data is delivered to and from a device depends on the size of its period. The size of the period can be defined in terms of PCM frames -or milliseconds, whichever is more convenient. Generally speaking, the smaller the period, the lower the latency at the expense of higher CPU usage and -increased risk of glitching due to the more frequent and granular data deliver intervals. The size of a period will depend on your requirements, but -miniaudio's defaults should work fine for most scenarios. If you're building a game you should leave this fairly small, whereas if you're building a simple -media player you can make it larger. Note that the period size you request is actually just a hint - miniaudio will tell the backend what you want, but the -backend is ultimately responsible for what it gives you. You cannot assume you will get exactly what you ask for. - -When delivering data to and from a device you need to make sure it's in the correct format which you can set through the device configuration. You just set the -format that you want to use and miniaudio will perform all of the necessary conversion for you internally. When delivering data to and from the callback you -can assume the format is the same as what you requested when you initialized the device. See Remarks for more details on miniaudio's data conversion pipeline. - - -Parameters ----------- -pContext (in, optional) - A pointer to the context that owns the device. This can be null, in which case it creates a default context internally. - -pConfig (in) - A pointer to the device configuration. Cannot be null. See remarks for details. - -pDevice (out) - A pointer to the device object being initialized. - - -Return Value ------------- -MA_SUCCESS if successful; any other error code otherwise. - - -Thread Safety -------------- -Unsafe. It is not safe to call this function simultaneously for different devices because some backends depend on and mutate global state. The same applies to -calling this at the same time as `ma_device_uninit()`. - - -Callback Safety ---------------- -Unsafe. It is not safe to call this inside any callback. - - -Remarks -------- -Setting `pContext` to NULL will result in miniaudio creating a default context internally and is equivalent to passing in a context initialized like so: - - ```c - ma_context_init(NULL, 0, NULL, &context); - ``` - -Do not set `pContext` to NULL if you are needing to open multiple devices. You can, however, use NULL when initializing the first device, and then use -device.pContext for the initialization of other devices. - -The device can be configured via the `pConfig` argument. The config object is initialized with `ma_device_config_init()`. Individual configuration settings can -then be set directly on the structure. Below are the members of the `ma_device_config` object. - - deviceType - Must be `ma_device_type_playback`, `ma_device_type_capture`, `ma_device_type_duplex` of `ma_device_type_loopback`. - - sampleRate - The sample rate, in hertz. The most common sample rates are 48000 and 44100. Setting this to 0 will use the device's native sample rate. - - periodSizeInFrames - The desired size of a period in PCM frames. If this is 0, `periodSizeInMilliseconds` will be used instead. If both are 0 the default buffer size will - be used depending on the selected performance profile. This value affects latency. See below for details. - - periodSizeInMilliseconds - The desired size of a period in milliseconds. If this is 0, `periodSizeInFrames` will be used instead. If both are 0 the default buffer size will be - used depending on the selected performance profile. The value affects latency. See below for details. - - periods - The number of periods making up the device's entire buffer. The total buffer size is `periodSizeInFrames` or `periodSizeInMilliseconds` multiplied by - this value. This is just a hint as backends will be the ones who ultimately decide how your periods will be configured. - - performanceProfile - A hint to miniaudio as to the performance requirements of your program. Can be either `ma_performance_profile_low_latency` (default) or - `ma_performance_profile_conservative`. This mainly affects the size of default buffers and can usually be left at its default value. - - noPreSilencedOutputBuffer - When set to true, the contents of the output buffer passed into the data callback will be left undefined. When set to false (default), the contents of - the output buffer will be cleared the zero. You can use this to avoid the overhead of zeroing out the buffer if you can guarantee that your data - callback will write to every sample in the output buffer, or if you are doing your own clearing. - - noClip - When set to true, the contents of the output buffer are left alone after returning and it will be left up to the backend itself to decide whether or - not to clip. When set to false (default), the contents of the output buffer passed into the data callback will be clipped after returning. This only - applies when the playback sample format is f32. - - noDisableDenormals - By default, miniaudio will disable denormals when the data callback is called. Setting this to true will prevent the disabling of denormals. - - noFixedSizedCallback - Allows miniaudio to fire the data callback with any frame count. When this is set to false (the default), the data callback will be fired with a - consistent frame count as specified by `periodSizeInFrames` or `periodSizeInMilliseconds`. When set to true, miniaudio will fire the callback with - whatever the backend requests, which could be anything. - - dataCallback - The callback to fire whenever data is ready to be delivered to or from the device. - - notificationCallback - The callback to fire when something has changed with the device, such as whether or not it has been started or stopped. - - pUserData - The user data pointer to use with the device. You can access this directly from the device object like `device.pUserData`. - - resampling.algorithm - The resampling algorithm to use when miniaudio needs to perform resampling between the rate specified by `sampleRate` and the device's native rate. The - default value is `ma_resample_algorithm_linear`, and the quality can be configured with `resampling.linear.lpfOrder`. - - resampling.pBackendVTable - A pointer to an optional vtable that can be used for plugging in a custom resampler. - - resampling.pBackendUserData - A pointer that will passed to callbacks in pBackendVTable. - - resampling.linear.lpfOrder - The linear resampler applies a low-pass filter as part of its processing for anti-aliasing. This setting controls the order of the filter. The higher - the value, the better the quality, in general. Setting this to 0 will disable low-pass filtering altogether. The maximum value is - `MA_MAX_FILTER_ORDER`. The default value is `min(4, MA_MAX_FILTER_ORDER)`. - - playback.pDeviceID - A pointer to a `ma_device_id` structure containing the ID of the playback device to initialize. Setting this NULL (default) will use the system's - default playback device. Retrieve the device ID from the `ma_device_info` structure, which can be retrieved using device enumeration. - - playback.format - The sample format to use for playback. When set to `ma_format_unknown` the device's native format will be used. This can be retrieved after - initialization from the device object directly with `device.playback.format`. - - playback.channels - The number of channels to use for playback. When set to 0 the device's native channel count will be used. This can be retrieved after initialization - from the device object directly with `device.playback.channels`. - - playback.pChannelMap - The channel map to use for playback. When left empty, the device's native channel map will be used. This can be retrieved after initialization from the - device object direct with `device.playback.pChannelMap`. When set, the buffer should contain `channels` items. - - playback.shareMode - The preferred share mode to use for playback. Can be either `ma_share_mode_shared` (default) or `ma_share_mode_exclusive`. Note that if you specify - exclusive mode, but it's not supported by the backend, initialization will fail. You can then fall back to shared mode if desired by changing this to - ma_share_mode_shared and reinitializing. - - capture.pDeviceID - A pointer to a `ma_device_id` structure containing the ID of the capture device to initialize. Setting this NULL (default) will use the system's - default capture device. Retrieve the device ID from the `ma_device_info` structure, which can be retrieved using device enumeration. - - capture.format - The sample format to use for capture. When set to `ma_format_unknown` the device's native format will be used. This can be retrieved after - initialization from the device object directly with `device.capture.format`. - - capture.channels - The number of channels to use for capture. When set to 0 the device's native channel count will be used. This can be retrieved after initialization - from the device object directly with `device.capture.channels`. - - capture.pChannelMap - The channel map to use for capture. When left empty, the device's native channel map will be used. This can be retrieved after initialization from the - device object direct with `device.capture.pChannelMap`. When set, the buffer should contain `channels` items. - - capture.shareMode - The preferred share mode to use for capture. Can be either `ma_share_mode_shared` (default) or `ma_share_mode_exclusive`. Note that if you specify - exclusive mode, but it's not supported by the backend, initialization will fail. You can then fall back to shared mode if desired by changing this to - ma_share_mode_shared and reinitializing. - - wasapi.noAutoConvertSRC - WASAPI only. When set to true, disables WASAPI's automatic resampling and forces the use of miniaudio's resampler. Defaults to false. - - wasapi.noDefaultQualitySRC - WASAPI only. Only used when `wasapi.noAutoConvertSRC` is set to false. When set to true, disables the use of `AUDCLNT_STREAMFLAGS_SRC_DEFAULT_QUALITY`. - You should usually leave this set to false, which is the default. - - wasapi.noAutoStreamRouting - WASAPI only. When set to true, disables automatic stream routing on the WASAPI backend. Defaults to false. - - wasapi.noHardwareOffloading - WASAPI only. When set to true, disables the use of WASAPI's hardware offloading feature. Defaults to false. - - alsa.noMMap - ALSA only. When set to true, disables MMap mode. Defaults to false. - - alsa.noAutoFormat - ALSA only. When set to true, disables ALSA's automatic format conversion by including the SND_PCM_NO_AUTO_FORMAT flag. Defaults to false. - - alsa.noAutoChannels - ALSA only. When set to true, disables ALSA's automatic channel conversion by including the SND_PCM_NO_AUTO_CHANNELS flag. Defaults to false. - - alsa.noAutoResample - ALSA only. When set to true, disables ALSA's automatic resampling by including the SND_PCM_NO_AUTO_RESAMPLE flag. Defaults to false. - - pulse.pStreamNamePlayback - PulseAudio only. Sets the stream name for playback. - - pulse.pStreamNameCapture - PulseAudio only. Sets the stream name for capture. - - pulse.channelMap - PulseAudio only. Sets the channel map that is requested from PulseAudio. See MA_PA_CHANNEL_MAP_* constants. Defaults to MA_PA_CHANNEL_MAP_AIFF. - - coreaudio.allowNominalSampleRateChange - Core Audio only. Desktop only. When enabled, allows the sample rate of the device to be changed at the operating system level. This - is disabled by default in order to prevent intrusive changes to the user's system. This is useful if you want to use a sample rate - that is known to be natively supported by the hardware thereby avoiding the cost of resampling. When set to true, miniaudio will - find the closest match between the sample rate requested in the device config and the sample rates natively supported by the - hardware. When set to false, the sample rate currently set by the operating system will always be used. - - opensl.streamType - OpenSL only. Explicitly sets the stream type. If left unset (`ma_opensl_stream_type_default`), the - stream type will be left unset. Think of this as the type of audio you're playing. - - opensl.recordingPreset - OpenSL only. Explicitly sets the type of recording your program will be doing. When left - unset, the recording preset will be left unchanged. - - aaudio.usage - AAudio only. Explicitly sets the nature of the audio the program will be consuming. When - left unset, the usage will be left unchanged. - - aaudio.contentType - AAudio only. Sets the content type. When left unset, the content type will be left unchanged. - - aaudio.inputPreset - AAudio only. Explicitly sets the type of recording your program will be doing. When left - unset, the input preset will be left unchanged. - - aaudio.noAutoStartAfterReroute - AAudio only. Controls whether or not the device should be automatically restarted after a - stream reroute. When set to false (default) the device will be restarted automatically; - otherwise the device will be stopped. - - -Once initialized, the device's config is immutable. If you need to change the config you will need to initialize a new device. - -After initializing the device it will be in a stopped state. To start it, use `ma_device_start()`. - -If both `periodSizeInFrames` and `periodSizeInMilliseconds` are set to zero, it will default to `MA_DEFAULT_PERIOD_SIZE_IN_MILLISECONDS_LOW_LATENCY` or -`MA_DEFAULT_PERIOD_SIZE_IN_MILLISECONDS_CONSERVATIVE`, depending on whether or not `performanceProfile` is set to `ma_performance_profile_low_latency` or -`ma_performance_profile_conservative`. - -If you request exclusive mode and the backend does not support it an error will be returned. For robustness, you may want to first try initializing the device -in exclusive mode, and then fall back to shared mode if required. Alternatively you can just request shared mode (the default if you leave it unset in the -config) which is the most reliable option. Some backends do not have a practical way of choosing whether or not the device should be exclusive or not (ALSA, -for example) in which case it just acts as a hint. Unless you have special requirements you should try avoiding exclusive mode as it's intrusive to the user. -Starting with Windows 10, miniaudio will use low-latency shared mode where possible which may make exclusive mode unnecessary. - -When sending or receiving data to/from a device, miniaudio will internally perform a format conversion to convert between the format specified by the config -and the format used internally by the backend. If you pass in 0 for the sample format, channel count, sample rate _and_ channel map, data transmission will run -on an optimized pass-through fast path. You can retrieve the format, channel count and sample rate by inspecting the `playback/capture.format`, -`playback/capture.channels` and `sampleRate` members of the device object. - -When compiling for UWP you must ensure you call this function on the main UI thread because the operating system may need to present the user with a message -asking for permissions. Please refer to the official documentation for ActivateAudioInterfaceAsync() for more information. - -ALSA Specific: When initializing the default device, requesting shared mode will try using the "dmix" device for playback and the "dsnoop" device for capture. -If these fail it will try falling back to the "hw" device. - - -Example 1 - Simple Initialization ---------------------------------- -This example shows how to initialize a simple playback device using a standard configuration. If you are just needing to do simple playback from the default -playback device this is usually all you need. - -```c -ma_device_config config = ma_device_config_init(ma_device_type_playback); -config.playback.format = ma_format_f32; -config.playback.channels = 2; -config.sampleRate = 48000; -config.dataCallback = ma_data_callback; -config.pMyUserData = pMyUserData; - -ma_device device; -ma_result result = ma_device_init(NULL, &config, &device); -if (result != MA_SUCCESS) { - // Error -} -``` - - -Example 2 - Advanced Initialization ------------------------------------ -This example shows how you might do some more advanced initialization. In this hypothetical example we want to control the latency by setting the buffer size -and period count. We also want to allow the user to be able to choose which device to output from which means we need a context so we can perform device -enumeration. - -```c -ma_context context; -ma_result result = ma_context_init(NULL, 0, NULL, &context); -if (result != MA_SUCCESS) { - // Error -} - -ma_device_info* pPlaybackDeviceInfos; -ma_uint32 playbackDeviceCount; -result = ma_context_get_devices(&context, &pPlaybackDeviceInfos, &playbackDeviceCount, NULL, NULL); -if (result != MA_SUCCESS) { - // Error -} - -// ... choose a device from pPlaybackDeviceInfos ... - -ma_device_config config = ma_device_config_init(ma_device_type_playback); -config.playback.pDeviceID = pMyChosenDeviceID; // <-- Get this from the `id` member of one of the `ma_device_info` objects returned by ma_context_get_devices(). -config.playback.format = ma_format_f32; -config.playback.channels = 2; -config.sampleRate = 48000; -config.dataCallback = ma_data_callback; -config.pUserData = pMyUserData; -config.periodSizeInMilliseconds = 10; -config.periods = 3; - -ma_device device; -result = ma_device_init(&context, &config, &device); -if (result != MA_SUCCESS) { - // Error -} -``` - - -See Also --------- -ma_device_config_init() -ma_device_uninit() -ma_device_start() -ma_context_init() -ma_context_get_devices() -ma_context_enumerate_devices() -*/ -MA_API ma_result ma_device_init(ma_context* pContext, const ma_device_config* pConfig, ma_device* pDevice); - -/* -Initializes a device without a context, with extra parameters for controlling the configuration of the internal self-managed context. - -This is the same as `ma_device_init()`, only instead of a context being passed in, the parameters from `ma_context_init()` are passed in instead. This function -allows you to configure the internally created context. - - -Parameters ----------- -backends (in, optional) - A list of backends to try initializing, in priority order. Can be NULL, in which case it uses default priority order. - -backendCount (in, optional) - The number of items in `backend`. Ignored if `backend` is NULL. - -pContextConfig (in, optional) - The context configuration. - -pConfig (in) - A pointer to the device configuration. Cannot be null. See remarks for details. - -pDevice (out) - A pointer to the device object being initialized. - - -Return Value ------------- -MA_SUCCESS if successful; any other error code otherwise. - - -Thread Safety -------------- -Unsafe. It is not safe to call this function simultaneously for different devices because some backends depend on and mutate global state. The same applies to -calling this at the same time as `ma_device_uninit()`. - - -Callback Safety ---------------- -Unsafe. It is not safe to call this inside any callback. - - -Remarks -------- -You only need to use this function if you want to configure the context differently to its defaults. You should never use this function if you want to manage -your own context. - -See the documentation for `ma_context_init()` for information on the different context configuration options. - - -See Also --------- -ma_device_init() -ma_device_uninit() -ma_device_config_init() -ma_context_init() -*/ -MA_API ma_result ma_device_init_ex(const ma_backend backends[], ma_uint32 backendCount, const ma_context_config* pContextConfig, const ma_device_config* pConfig, ma_device* pDevice); - -/* -Uninitializes a device. - -This will explicitly stop the device. You do not need to call `ma_device_stop()` beforehand, but it's harmless if you do. - - -Parameters ----------- -pDevice (in) - A pointer to the device to stop. - - -Return Value ------------- -Nothing - - -Thread Safety -------------- -Unsafe. As soon as this API is called the device should be considered undefined. - - -Callback Safety ---------------- -Unsafe. It is not safe to call this inside any callback. Doing this will result in a deadlock. - - -See Also --------- -ma_device_init() -ma_device_stop() -*/ -MA_API void ma_device_uninit(ma_device* pDevice); - - -/* -Retrieves a pointer to the context that owns the given device. -*/ -MA_API ma_context* ma_device_get_context(ma_device* pDevice); - -/* -Helper function for retrieving the log object associated with the context that owns this device. -*/ -MA_API ma_log* ma_device_get_log(ma_device* pDevice); - - -/* -Retrieves information about the device. - - -Parameters ----------- -pDevice (in) - A pointer to the device whose information is being retrieved. - -type (in) - The device type. This parameter is required for duplex devices. When retrieving device - information, you are doing so for an individual playback or capture device. - -pDeviceInfo (out) - A pointer to the `ma_device_info` that will receive the device information. - - -Return Value ------------- -MA_SUCCESS if successful; any other error code otherwise. - - -Thread Safety -------------- -Unsafe. This should be considered unsafe because it may be calling into the backend which may or -may not be safe. - - -Callback Safety ---------------- -Unsafe. You should avoid calling this in the data callback because it may call into the backend -which may or may not be safe. -*/ -MA_API ma_result ma_device_get_info(ma_device* pDevice, ma_device_type type, ma_device_info* pDeviceInfo); - - -/* -Retrieves the name of the device. - - -Parameters ----------- -pDevice (in) - A pointer to the device whose information is being retrieved. - -type (in) - The device type. This parameter is required for duplex devices. When retrieving device - information, you are doing so for an individual playback or capture device. - -pName (out) - A pointer to the buffer that will receive the name. - -nameCap (in) - The capacity of the output buffer, including space for the null terminator. - -pLengthNotIncludingNullTerminator (out, optional) - A pointer to the variable that will receive the length of the name, not including the null - terminator. - - -Return Value ------------- -MA_SUCCESS if successful; any other error code otherwise. - - -Thread Safety -------------- -Unsafe. This should be considered unsafe because it may be calling into the backend which may or -may not be safe. - - -Callback Safety ---------------- -Unsafe. You should avoid calling this in the data callback because it may call into the backend -which may or may not be safe. - - -Remarks -------- -If the name does not fully fit into the output buffer, it'll be truncated. You can pass in NULL to -`pName` if you want to first get the length of the name for the purpose of memory allocation of the -output buffer. Allocating a buffer of size `MA_MAX_DEVICE_NAME_LENGTH + 1` should be enough for -most cases and will avoid the need for the inefficiency of calling this function twice. - -This is implemented in terms of `ma_device_get_info()`. -*/ -MA_API ma_result ma_device_get_name(ma_device* pDevice, ma_device_type type, char* pName, size_t nameCap, size_t* pLengthNotIncludingNullTerminator); - - -/* -Starts the device. For playback devices this begins playback. For capture devices it begins recording. - -Use `ma_device_stop()` to stop the device. - - -Parameters ----------- -pDevice (in) - A pointer to the device to start. - - -Return Value ------------- -MA_SUCCESS if successful; any other error code otherwise. - - -Thread Safety -------------- -Safe. It's safe to call this from any thread with the exception of the callback thread. - - -Callback Safety ---------------- -Unsafe. It is not safe to call this inside any callback. - - -Remarks -------- -For a playback device, this will retrieve an initial chunk of audio data from the client before returning. The reason for this is to ensure there is valid -audio data in the buffer, which needs to be done before the device begins playback. - -This API waits until the backend device has been started for real by the worker thread. It also waits on a mutex for thread-safety. - -Do not call this in any callback. - - -See Also --------- -ma_device_stop() -*/ -MA_API ma_result ma_device_start(ma_device* pDevice); - -/* -Stops the device. For playback devices this stops playback. For capture devices it stops recording. - -Use `ma_device_start()` to start the device again. - - -Parameters ----------- -pDevice (in) - A pointer to the device to stop. - - -Return Value ------------- -MA_SUCCESS if successful; any other error code otherwise. - - -Thread Safety -------------- -Safe. It's safe to call this from any thread with the exception of the callback thread. - - -Callback Safety ---------------- -Unsafe. It is not safe to call this inside any callback. Doing this will result in a deadlock. - - -Remarks -------- -This API needs to wait on the worker thread to stop the backend device properly before returning. It also waits on a mutex for thread-safety. In addition, some -backends need to wait for the device to finish playback/recording of the current fragment which can take some time (usually proportionate to the buffer size -that was specified at initialization time). - -Backends are required to either pause the stream in-place or drain the buffer if pausing is not possible. The reason for this is that stopping the device and -the resuming it with ma_device_start() (which you might do when your program loses focus) may result in a situation where those samples are never output to the -speakers or received from the microphone which can in turn result in de-syncs. - -Do not call this in any callback. - - -See Also --------- -ma_device_start() -*/ -MA_API ma_result ma_device_stop(ma_device* pDevice); - -/* -Determines whether or not the device is started. - - -Parameters ----------- -pDevice (in) - A pointer to the device whose start state is being retrieved. - - -Return Value ------------- -True if the device is started, false otherwise. - - -Thread Safety -------------- -Safe. If another thread calls `ma_device_start()` or `ma_device_stop()` at this same time as this function is called, there's a very small chance the return -value will be out of sync. - - -Callback Safety ---------------- -Safe. This is implemented as a simple accessor. - - -See Also --------- -ma_device_start() -ma_device_stop() -*/ -MA_API ma_bool32 ma_device_is_started(const ma_device* pDevice); - - -/* -Retrieves the state of the device. - - -Parameters ----------- -pDevice (in) - A pointer to the device whose state is being retrieved. - - -Return Value ------------- -The current state of the device. The return value will be one of the following: - - +-------------------------------+------------------------------------------------------------------------------+ - | ma_device_state_uninitialized | Will only be returned if the device is in the middle of initialization. | - +-------------------------------+------------------------------------------------------------------------------+ - | ma_device_state_stopped | The device is stopped. The initial state of the device after initialization. | - +-------------------------------+------------------------------------------------------------------------------+ - | ma_device_state_started | The device started and requesting and/or delivering audio data. | - +-------------------------------+------------------------------------------------------------------------------+ - | ma_device_state_starting | The device is in the process of starting. | - +-------------------------------+------------------------------------------------------------------------------+ - | ma_device_state_stopping | The device is in the process of stopping. | - +-------------------------------+------------------------------------------------------------------------------+ - - -Thread Safety -------------- -Safe. This is implemented as a simple accessor. Note that if the device is started or stopped at the same time as this function is called, -there's a possibility the return value could be out of sync. See remarks. - - -Callback Safety ---------------- -Safe. This is implemented as a simple accessor. - - -Remarks -------- -The general flow of a devices state goes like this: - - ``` - ma_device_init() -> ma_device_state_uninitialized -> ma_device_state_stopped - ma_device_start() -> ma_device_state_starting -> ma_device_state_started - ma_device_stop() -> ma_device_state_stopping -> ma_device_state_stopped - ``` - -When the state of the device is changed with `ma_device_start()` or `ma_device_stop()` at this same time as this function is called, the -value returned by this function could potentially be out of sync. If this is significant to your program you need to implement your own -synchronization. -*/ -MA_API ma_device_state ma_device_get_state(const ma_device* pDevice); - - -/* -Performs post backend initialization routines for setting up internal data conversion. - -This should be called whenever the backend is initialized. The only time this should be called from -outside of miniaudio is if you're implementing a custom backend, and you would only do it if you -are reinitializing the backend due to rerouting or reinitializing for some reason. - - -Parameters ----------- -pDevice [in] - A pointer to the device. - -deviceType [in] - The type of the device that was just reinitialized. - -pPlaybackDescriptor [in] - The descriptor of the playback device containing the internal data format and buffer sizes. - -pPlaybackDescriptor [in] - The descriptor of the capture device containing the internal data format and buffer sizes. - - -Return Value ------------- -MA_SUCCESS if successful; any other error otherwise. - - -Thread Safety -------------- -Unsafe. This will be reinitializing internal data converters which may be in use by another thread. - - -Callback Safety ---------------- -Unsafe. This will be reinitializing internal data converters which may be in use by the callback. - - -Remarks -------- -For a duplex device, you can call this for only one side of the system. This is why the deviceType -is specified as a parameter rather than deriving it from the device. - -You do not need to call this manually unless you are doing a custom backend, in which case you need -only do it if you're manually performing rerouting or reinitialization. -*/ -MA_API ma_result ma_device_post_init(ma_device* pDevice, ma_device_type deviceType, const ma_device_descriptor* pPlaybackDescriptor, const ma_device_descriptor* pCaptureDescriptor); - - -/* -Sets the master volume factor for the device. - -The volume factor must be between 0 (silence) and 1 (full volume). Use `ma_device_set_master_volume_db()` to use decibel notation, where 0 is full volume and -values less than 0 decreases the volume. - - -Parameters ----------- -pDevice (in) - A pointer to the device whose volume is being set. - -volume (in) - The new volume factor. Must be >= 0. - - -Return Value ------------- -MA_SUCCESS if the volume was set successfully. -MA_INVALID_ARGS if pDevice is NULL. -MA_INVALID_ARGS if volume is negative. - - -Thread Safety -------------- -Safe. This just sets a local member of the device object. - - -Callback Safety ---------------- -Safe. If you set the volume in the data callback, that data written to the output buffer will have the new volume applied. - - -Remarks -------- -This applies the volume factor across all channels. - -This does not change the operating system's volume. It only affects the volume for the given `ma_device` object's audio stream. - - -See Also --------- -ma_device_get_master_volume() -ma_device_set_master_volume_db() -ma_device_get_master_volume_db() -*/ -MA_API ma_result ma_device_set_master_volume(ma_device* pDevice, float volume); - -/* -Retrieves the master volume factor for the device. - - -Parameters ----------- -pDevice (in) - A pointer to the device whose volume factor is being retrieved. - -pVolume (in) - A pointer to the variable that will receive the volume factor. The returned value will be in the range of [0, 1]. - - -Return Value ------------- -MA_SUCCESS if successful. -MA_INVALID_ARGS if pDevice is NULL. -MA_INVALID_ARGS if pVolume is NULL. - - -Thread Safety -------------- -Safe. This just a simple member retrieval. - - -Callback Safety ---------------- -Safe. - - -Remarks -------- -If an error occurs, `*pVolume` will be set to 0. - - -See Also --------- -ma_device_set_master_volume() -ma_device_set_master_volume_gain_db() -ma_device_get_master_volume_gain_db() -*/ -MA_API ma_result ma_device_get_master_volume(ma_device* pDevice, float* pVolume); - -/* -Sets the master volume for the device as gain in decibels. - -A gain of 0 is full volume, whereas a gain of < 0 will decrease the volume. - - -Parameters ----------- -pDevice (in) - A pointer to the device whose gain is being set. - -gainDB (in) - The new volume as gain in decibels. Must be less than or equal to 0, where 0 is full volume and anything less than 0 decreases the volume. - - -Return Value ------------- -MA_SUCCESS if the volume was set successfully. -MA_INVALID_ARGS if pDevice is NULL. -MA_INVALID_ARGS if the gain is > 0. - - -Thread Safety -------------- -Safe. This just sets a local member of the device object. - - -Callback Safety ---------------- -Safe. If you set the volume in the data callback, that data written to the output buffer will have the new volume applied. - - -Remarks -------- -This applies the gain across all channels. - -This does not change the operating system's volume. It only affects the volume for the given `ma_device` object's audio stream. - - -See Also --------- -ma_device_get_master_volume_gain_db() -ma_device_set_master_volume() -ma_device_get_master_volume() -*/ -MA_API ma_result ma_device_set_master_volume_db(ma_device* pDevice, float gainDB); - -/* -Retrieves the master gain in decibels. - - -Parameters ----------- -pDevice (in) - A pointer to the device whose gain is being retrieved. - -pGainDB (in) - A pointer to the variable that will receive the gain in decibels. The returned value will be <= 0. - - -Return Value ------------- -MA_SUCCESS if successful. -MA_INVALID_ARGS if pDevice is NULL. -MA_INVALID_ARGS if pGainDB is NULL. - - -Thread Safety -------------- -Safe. This just a simple member retrieval. - - -Callback Safety ---------------- -Safe. - - -Remarks -------- -If an error occurs, `*pGainDB` will be set to 0. - - -See Also --------- -ma_device_set_master_volume_db() -ma_device_set_master_volume() -ma_device_get_master_volume() -*/ -MA_API ma_result ma_device_get_master_volume_db(ma_device* pDevice, float* pGainDB); - - -/* -Called from the data callback of asynchronous backends to allow miniaudio to process the data and fire the miniaudio data callback. - - -Parameters ----------- -pDevice (in) - A pointer to device whose processing the data callback. - -pOutput (out) - A pointer to the buffer that will receive the output PCM frame data. On a playback device this must not be NULL. On a duplex device - this can be NULL, in which case pInput must not be NULL. - -pInput (in) - A pointer to the buffer containing input PCM frame data. On a capture device this must not be NULL. On a duplex device this can be - NULL, in which case `pOutput` must not be NULL. - -frameCount (in) - The number of frames being processed. - - -Return Value ------------- -MA_SUCCESS if successful; any other result code otherwise. - - -Thread Safety -------------- -This function should only ever be called from the internal data callback of the backend. It is safe to call this simultaneously between a -playback and capture device in duplex setups. - - -Callback Safety ---------------- -Do not call this from the miniaudio data callback. It should only ever be called from the internal data callback of the backend. - - -Remarks -------- -If both `pOutput` and `pInput` are NULL, and error will be returned. In duplex scenarios, both `pOutput` and `pInput` can be non-NULL, in -which case `pInput` will be processed first, followed by `pOutput`. - -If you are implementing a custom backend, and that backend uses a callback for data delivery, you'll need to call this from inside that -callback. -*/ -MA_API ma_result ma_device_handle_backend_data_callback(ma_device* pDevice, void* pOutput, const void* pInput, ma_uint32 frameCount); - - -/* -Calculates an appropriate buffer size from a descriptor, native sample rate and performance profile. - -This function is used by backends for helping determine an appropriately sized buffer to use with -the device depending on the values of `periodSizeInFrames` and `periodSizeInMilliseconds` in the -`pDescriptor` object. Since buffer size calculations based on time depends on the sample rate, a -best guess at the device's native sample rate is also required which is where `nativeSampleRate` -comes in. In addition, the performance profile is also needed for cases where both the period size -in frames and milliseconds are both zero. - - -Parameters ----------- -pDescriptor (in) - A pointer to device descriptor whose `periodSizeInFrames` and `periodSizeInMilliseconds` members - will be used for the calculation of the buffer size. - -nativeSampleRate (in) - The device's native sample rate. This is only ever used when the `periodSizeInFrames` member of - `pDescriptor` is zero. In this case, `periodSizeInMilliseconds` will be used instead, in which - case a sample rate is required to convert to a size in frames. - -performanceProfile (in) - When both the `periodSizeInFrames` and `periodSizeInMilliseconds` members of `pDescriptor` are - zero, miniaudio will fall back to a buffer size based on the performance profile. The profile - to use for this calculation is determine by this parameter. - - -Return Value ------------- -The calculated buffer size in frames. - - -Thread Safety -------------- -This is safe so long as nothing modifies `pDescriptor` at the same time. However, this function -should only ever be called from within the backend's device initialization routine and therefore -shouldn't have any multithreading concerns. - - -Callback Safety ---------------- -This is safe to call within the data callback, but there is no reason to ever do this. - - -Remarks -------- -If `nativeSampleRate` is zero, this function will fall back to `pDescriptor->sampleRate`. If that -is also zero, `MA_DEFAULT_SAMPLE_RATE` will be used instead. -*/ -MA_API ma_uint32 ma_calculate_buffer_size_in_frames_from_descriptor(const ma_device_descriptor* pDescriptor, ma_uint32 nativeSampleRate, ma_performance_profile performanceProfile); - - - -/* -Retrieves a friendly name for a backend. -*/ -MA_API const char* ma_get_backend_name(ma_backend backend); - -/* -Retrieves the backend enum from the given name. -*/ -MA_API ma_result ma_get_backend_from_name(const char* pBackendName, ma_backend* pBackend); - -/* -Determines whether or not the given backend is available by the compilation environment. -*/ -MA_API ma_bool32 ma_is_backend_enabled(ma_backend backend); - -/* -Retrieves compile-time enabled backends. - - -Parameters ----------- -pBackends (out, optional) - A pointer to the buffer that will receive the enabled backends. Set to NULL to retrieve the backend count. Setting - the capacity of the buffer to `MA_BUFFER_COUNT` will guarantee it's large enough for all backends. - -backendCap (in) - The capacity of the `pBackends` buffer. - -pBackendCount (out) - A pointer to the variable that will receive the enabled backend count. - - -Return Value ------------- -MA_SUCCESS if successful. -MA_INVALID_ARGS if `pBackendCount` is NULL. -MA_NO_SPACE if the capacity of `pBackends` is not large enough. - -If `MA_NO_SPACE` is returned, the `pBackends` buffer will be filled with `*pBackendCount` values. - - -Thread Safety -------------- -Safe. - - -Callback Safety ---------------- -Safe. - - -Remarks -------- -If you want to retrieve the number of backends so you can determine the capacity of `pBackends` buffer, you can call -this function with `pBackends` set to NULL. - -This will also enumerate the null backend. If you don't want to include this you need to check for `ma_backend_null` -when you enumerate over the returned backends and handle it appropriately. Alternatively, you can disable it at -compile time with `MA_NO_NULL`. - -The returned backends are determined based on compile time settings, not the platform it's currently running on. For -example, PulseAudio will be returned if it was enabled at compile time, even when the user doesn't actually have -PulseAudio installed. - - -Example 1 ---------- -The example below retrieves the enabled backend count using a fixed sized buffer allocated on the stack. The buffer is -given a capacity of `MA_BACKEND_COUNT` which will guarantee it'll be large enough to store all available backends. -Since `MA_BACKEND_COUNT` is always a relatively small value, this should be suitable for most scenarios. - -``` -ma_backend enabledBackends[MA_BACKEND_COUNT]; -size_t enabledBackendCount; - -result = ma_get_enabled_backends(enabledBackends, MA_BACKEND_COUNT, &enabledBackendCount); -if (result != MA_SUCCESS) { - // Failed to retrieve enabled backends. Should never happen in this example since all inputs are valid. -} -``` - - -See Also --------- -ma_is_backend_enabled() -*/ -MA_API ma_result ma_get_enabled_backends(ma_backend* pBackends, size_t backendCap, size_t* pBackendCount); - -/* -Determines whether or not loopback mode is support by a backend. -*/ -MA_API ma_bool32 ma_is_loopback_supported(ma_backend backend); - -#endif /* MA_NO_DEVICE_IO */ - - - -/************************************************************************************************************************************************************ - -Utilities - -************************************************************************************************************************************************************/ - -/* -Calculates a buffer size in milliseconds (rounded up) from the specified number of frames and sample rate. -*/ -MA_API ma_uint32 ma_calculate_buffer_size_in_milliseconds_from_frames(ma_uint32 bufferSizeInFrames, ma_uint32 sampleRate); - -/* -Calculates a buffer size in frames from the specified number of milliseconds and sample rate. -*/ -MA_API ma_uint32 ma_calculate_buffer_size_in_frames_from_milliseconds(ma_uint32 bufferSizeInMilliseconds, ma_uint32 sampleRate); - -/* -Copies PCM frames from one buffer to another. -*/ -MA_API void ma_copy_pcm_frames(void* dst, const void* src, ma_uint64 frameCount, ma_format format, ma_uint32 channels); - -/* -Copies silent frames into the given buffer. - -Remarks -------- -For all formats except `ma_format_u8`, the output buffer will be filled with 0. For `ma_format_u8` it will be filled with 128. The reason for this is that it -makes more sense for the purpose of mixing to initialize it to the center point. -*/ -MA_API void ma_silence_pcm_frames(void* p, ma_uint64 frameCount, ma_format format, ma_uint32 channels); - - -/* -Offsets a pointer by the specified number of PCM frames. -*/ -MA_API void* ma_offset_pcm_frames_ptr(void* p, ma_uint64 offsetInFrames, ma_format format, ma_uint32 channels); -MA_API const void* ma_offset_pcm_frames_const_ptr(const void* p, ma_uint64 offsetInFrames, ma_format format, ma_uint32 channels); -static MA_INLINE float* ma_offset_pcm_frames_ptr_f32(float* p, ma_uint64 offsetInFrames, ma_uint32 channels) { return (float*)ma_offset_pcm_frames_ptr((void*)p, offsetInFrames, ma_format_f32, channels); } -static MA_INLINE const float* ma_offset_pcm_frames_const_ptr_f32(const float* p, ma_uint64 offsetInFrames, ma_uint32 channels) { return (const float*)ma_offset_pcm_frames_const_ptr((const void*)p, offsetInFrames, ma_format_f32, channels); } - - -/* -Clips samples. -*/ -MA_API void ma_clip_samples_u8(ma_uint8* pDst, const ma_int16* pSrc, ma_uint64 count); -MA_API void ma_clip_samples_s16(ma_int16* pDst, const ma_int32* pSrc, ma_uint64 count); -MA_API void ma_clip_samples_s24(ma_uint8* pDst, const ma_int64* pSrc, ma_uint64 count); -MA_API void ma_clip_samples_s32(ma_int32* pDst, const ma_int64* pSrc, ma_uint64 count); -MA_API void ma_clip_samples_f32(float* pDst, const float* pSrc, ma_uint64 count); -MA_API void ma_clip_pcm_frames(void* pDst, const void* pSrc, ma_uint64 frameCount, ma_format format, ma_uint32 channels); - -/* -Helper for applying a volume factor to samples. - -Note that the source and destination buffers can be the same, in which case it'll perform the operation in-place. -*/ -MA_API void ma_copy_and_apply_volume_factor_u8(ma_uint8* pSamplesOut, const ma_uint8* pSamplesIn, ma_uint64 sampleCount, float factor); -MA_API void ma_copy_and_apply_volume_factor_s16(ma_int16* pSamplesOut, const ma_int16* pSamplesIn, ma_uint64 sampleCount, float factor); -MA_API void ma_copy_and_apply_volume_factor_s24(void* pSamplesOut, const void* pSamplesIn, ma_uint64 sampleCount, float factor); -MA_API void ma_copy_and_apply_volume_factor_s32(ma_int32* pSamplesOut, const ma_int32* pSamplesIn, ma_uint64 sampleCount, float factor); -MA_API void ma_copy_and_apply_volume_factor_f32(float* pSamplesOut, const float* pSamplesIn, ma_uint64 sampleCount, float factor); - -MA_API void ma_apply_volume_factor_u8(ma_uint8* pSamples, ma_uint64 sampleCount, float factor); -MA_API void ma_apply_volume_factor_s16(ma_int16* pSamples, ma_uint64 sampleCount, float factor); -MA_API void ma_apply_volume_factor_s24(void* pSamples, ma_uint64 sampleCount, float factor); -MA_API void ma_apply_volume_factor_s32(ma_int32* pSamples, ma_uint64 sampleCount, float factor); -MA_API void ma_apply_volume_factor_f32(float* pSamples, ma_uint64 sampleCount, float factor); - -MA_API void ma_copy_and_apply_volume_factor_pcm_frames_u8(ma_uint8* pFramesOut, const ma_uint8* pFramesIn, ma_uint64 frameCount, ma_uint32 channels, float factor); -MA_API void ma_copy_and_apply_volume_factor_pcm_frames_s16(ma_int16* pFramesOut, const ma_int16* pFramesIn, ma_uint64 frameCount, ma_uint32 channels, float factor); -MA_API void ma_copy_and_apply_volume_factor_pcm_frames_s24(void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount, ma_uint32 channels, float factor); -MA_API void ma_copy_and_apply_volume_factor_pcm_frames_s32(ma_int32* pFramesOut, const ma_int32* pFramesIn, ma_uint64 frameCount, ma_uint32 channels, float factor); -MA_API void ma_copy_and_apply_volume_factor_pcm_frames_f32(float* pFramesOut, const float* pFramesIn, ma_uint64 frameCount, ma_uint32 channels, float factor); -MA_API void ma_copy_and_apply_volume_factor_pcm_frames(void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount, ma_format format, ma_uint32 channels, float factor); - -MA_API void ma_apply_volume_factor_pcm_frames_u8(ma_uint8* pFrames, ma_uint64 frameCount, ma_uint32 channels, float factor); -MA_API void ma_apply_volume_factor_pcm_frames_s16(ma_int16* pFrames, ma_uint64 frameCount, ma_uint32 channels, float factor); -MA_API void ma_apply_volume_factor_pcm_frames_s24(void* pFrames, ma_uint64 frameCount, ma_uint32 channels, float factor); -MA_API void ma_apply_volume_factor_pcm_frames_s32(ma_int32* pFrames, ma_uint64 frameCount, ma_uint32 channels, float factor); -MA_API void ma_apply_volume_factor_pcm_frames_f32(float* pFrames, ma_uint64 frameCount, ma_uint32 channels, float factor); -MA_API void ma_apply_volume_factor_pcm_frames(void* pFrames, ma_uint64 frameCount, ma_format format, ma_uint32 channels, float factor); - -MA_API void ma_copy_and_apply_volume_factor_per_channel_f32(float* pFramesOut, const float* pFramesIn, ma_uint64 frameCount, ma_uint32 channels, float* pChannelGains); - - -MA_API void ma_copy_and_apply_volume_and_clip_samples_u8(ma_uint8* pDst, const ma_int16* pSrc, ma_uint64 count, float volume); -MA_API void ma_copy_and_apply_volume_and_clip_samples_s16(ma_int16* pDst, const ma_int32* pSrc, ma_uint64 count, float volume); -MA_API void ma_copy_and_apply_volume_and_clip_samples_s24(ma_uint8* pDst, const ma_int64* pSrc, ma_uint64 count, float volume); -MA_API void ma_copy_and_apply_volume_and_clip_samples_s32(ma_int32* pDst, const ma_int64* pSrc, ma_uint64 count, float volume); -MA_API void ma_copy_and_apply_volume_and_clip_samples_f32(float* pDst, const float* pSrc, ma_uint64 count, float volume); -MA_API void ma_copy_and_apply_volume_and_clip_pcm_frames(void* pDst, const void* pSrc, ma_uint64 frameCount, ma_format format, ma_uint32 channels, float volume); - - -/* -Helper for converting a linear factor to gain in decibels. -*/ -MA_API float ma_volume_linear_to_db(float factor); - -/* -Helper for converting gain in decibels to a linear factor. -*/ -MA_API float ma_volume_db_to_linear(float gain); - - -/* -Mixes the specified number of frames in floating point format with a volume factor. - -This will run on an optimized path when the volume is equal to 1. -*/ -MA_API ma_result ma_mix_pcm_frames_f32(float* pDst, const float* pSrc, ma_uint64 frameCount, ma_uint32 channels, float volume); - - - - -/************************************************************************************************************************************************************ - -VFS -=== - -The VFS object (virtual file system) is what's used to customize file access. This is useful in cases where stdio FILE* based APIs may not be entirely -appropriate for a given situation. - -************************************************************************************************************************************************************/ -typedef void ma_vfs; -typedef ma_handle ma_vfs_file; - -typedef enum -{ - MA_OPEN_MODE_READ = 0x00000001, - MA_OPEN_MODE_WRITE = 0x00000002 -} ma_open_mode_flags; - -typedef enum -{ - ma_seek_origin_start, - ma_seek_origin_current, - ma_seek_origin_end /* Not used by decoders. */ -} ma_seek_origin; - -typedef struct -{ - ma_uint64 sizeInBytes; -} ma_file_info; - -typedef struct -{ - ma_result (* onOpen) (ma_vfs* pVFS, const char* pFilePath, ma_uint32 openMode, ma_vfs_file* pFile); - ma_result (* onOpenW)(ma_vfs* pVFS, const wchar_t* pFilePath, ma_uint32 openMode, ma_vfs_file* pFile); - ma_result (* onClose)(ma_vfs* pVFS, ma_vfs_file file); - ma_result (* onRead) (ma_vfs* pVFS, ma_vfs_file file, void* pDst, size_t sizeInBytes, size_t* pBytesRead); - ma_result (* onWrite)(ma_vfs* pVFS, ma_vfs_file file, const void* pSrc, size_t sizeInBytes, size_t* pBytesWritten); - ma_result (* onSeek) (ma_vfs* pVFS, ma_vfs_file file, ma_int64 offset, ma_seek_origin origin); - ma_result (* onTell) (ma_vfs* pVFS, ma_vfs_file file, ma_int64* pCursor); - ma_result (* onInfo) (ma_vfs* pVFS, ma_vfs_file file, ma_file_info* pInfo); -} ma_vfs_callbacks; - -MA_API ma_result ma_vfs_open(ma_vfs* pVFS, const char* pFilePath, ma_uint32 openMode, ma_vfs_file* pFile); -MA_API ma_result ma_vfs_open_w(ma_vfs* pVFS, const wchar_t* pFilePath, ma_uint32 openMode, ma_vfs_file* pFile); -MA_API ma_result ma_vfs_close(ma_vfs* pVFS, ma_vfs_file file); -MA_API ma_result ma_vfs_read(ma_vfs* pVFS, ma_vfs_file file, void* pDst, size_t sizeInBytes, size_t* pBytesRead); -MA_API ma_result ma_vfs_write(ma_vfs* pVFS, ma_vfs_file file, const void* pSrc, size_t sizeInBytes, size_t* pBytesWritten); -MA_API ma_result ma_vfs_seek(ma_vfs* pVFS, ma_vfs_file file, ma_int64 offset, ma_seek_origin origin); -MA_API ma_result ma_vfs_tell(ma_vfs* pVFS, ma_vfs_file file, ma_int64* pCursor); -MA_API ma_result ma_vfs_info(ma_vfs* pVFS, ma_vfs_file file, ma_file_info* pInfo); -MA_API ma_result ma_vfs_open_and_read_file(ma_vfs* pVFS, const char* pFilePath, void** ppData, size_t* pSize, const ma_allocation_callbacks* pAllocationCallbacks); - -typedef struct -{ - ma_vfs_callbacks cb; - ma_allocation_callbacks allocationCallbacks; /* Only used for the wchar_t version of open() on non-Windows platforms. */ -} ma_default_vfs; - -MA_API ma_result ma_default_vfs_init(ma_default_vfs* pVFS, const ma_allocation_callbacks* pAllocationCallbacks); - - - -typedef ma_result (* ma_read_proc)(void* pUserData, void* pBufferOut, size_t bytesToRead, size_t* pBytesRead); -typedef ma_result (* ma_seek_proc)(void* pUserData, ma_int64 offset, ma_seek_origin origin); -typedef ma_result (* ma_tell_proc)(void* pUserData, ma_int64* pCursor); - - - -#if !defined(MA_NO_DECODING) || !defined(MA_NO_ENCODING) -typedef enum -{ - ma_encoding_format_unknown = 0, - ma_encoding_format_wav, - ma_encoding_format_flac, - ma_encoding_format_mp3, - ma_encoding_format_vorbis -} ma_encoding_format; -#endif - -/************************************************************************************************************************************************************ - -Decoding -======== - -Decoders are independent of the main device API. Decoding APIs can be called freely inside the device's data callback, but they are not thread safe unless -you do your own synchronization. - -************************************************************************************************************************************************************/ -#ifndef MA_NO_DECODING -typedef struct ma_decoder ma_decoder; - - -typedef struct -{ - ma_format preferredFormat; - ma_uint32 seekPointCount; /* Set to > 0 to generate a seektable if the decoding backend supports it. */ -} ma_decoding_backend_config; - -MA_API ma_decoding_backend_config ma_decoding_backend_config_init(ma_format preferredFormat, ma_uint32 seekPointCount); - - -typedef struct -{ - ma_result (* onInit )(void* pUserData, ma_read_proc onRead, ma_seek_proc onSeek, ma_tell_proc onTell, void* pReadSeekTellUserData, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_data_source** ppBackend); - ma_result (* onInitFile )(void* pUserData, const char* pFilePath, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_data_source** ppBackend); /* Optional. */ - ma_result (* onInitFileW )(void* pUserData, const wchar_t* pFilePath, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_data_source** ppBackend); /* Optional. */ - ma_result (* onInitMemory)(void* pUserData, const void* pData, size_t dataSize, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_data_source** ppBackend); /* Optional. */ - void (* onUninit )(void* pUserData, ma_data_source* pBackend, const ma_allocation_callbacks* pAllocationCallbacks); -} ma_decoding_backend_vtable; - - -typedef ma_result (* ma_decoder_read_proc)(ma_decoder* pDecoder, void* pBufferOut, size_t bytesToRead, size_t* pBytesRead); /* Returns the number of bytes read. */ -typedef ma_result (* ma_decoder_seek_proc)(ma_decoder* pDecoder, ma_int64 byteOffset, ma_seek_origin origin); -typedef ma_result (* ma_decoder_tell_proc)(ma_decoder* pDecoder, ma_int64* pCursor); - -typedef struct -{ - ma_format format; /* Set to 0 or ma_format_unknown to use the stream's internal format. */ - ma_uint32 channels; /* Set to 0 to use the stream's internal channels. */ - ma_uint32 sampleRate; /* Set to 0 to use the stream's internal sample rate. */ - ma_channel* pChannelMap; - ma_channel_mix_mode channelMixMode; - ma_dither_mode ditherMode; - ma_resampler_config resampling; - ma_allocation_callbacks allocationCallbacks; - ma_encoding_format encodingFormat; - ma_uint32 seekPointCount; /* When set to > 0, specifies the number of seek points to use for the generation of a seek table. Not all decoding backends support this. */ - ma_decoding_backend_vtable** ppCustomBackendVTables; - ma_uint32 customBackendCount; - void* pCustomBackendUserData; -} ma_decoder_config; - -struct ma_decoder -{ - ma_data_source_base ds; - ma_data_source* pBackend; /* The decoding backend we'll be pulling data from. */ - const ma_decoding_backend_vtable* pBackendVTable; /* The vtable for the decoding backend. This needs to be stored so we can access the onUninit() callback. */ - void* pBackendUserData; - ma_decoder_read_proc onRead; - ma_decoder_seek_proc onSeek; - ma_decoder_tell_proc onTell; - void* pUserData; - ma_uint64 readPointerInPCMFrames; /* In output sample rate. Used for keeping track of how many frames are available for decoding. */ - ma_format outputFormat; - ma_uint32 outputChannels; - ma_uint32 outputSampleRate; - ma_data_converter converter; /* Data conversion is achieved by running frames through this. */ - void* pInputCache; /* In input format. Can be null if it's not needed. */ - ma_uint64 inputCacheCap; /* The capacity of the input cache. */ - ma_uint64 inputCacheConsumed; /* The number of frames that have been consumed in the cache. Used for determining the next valid frame. */ - ma_uint64 inputCacheRemaining; /* The number of valid frames remaining in the cache. */ - ma_allocation_callbacks allocationCallbacks; - union - { - struct - { - ma_vfs* pVFS; - ma_vfs_file file; - } vfs; - struct - { - const ma_uint8* pData; - size_t dataSize; - size_t currentReadPos; - } memory; /* Only used for decoders that were opened against a block of memory. */ - } data; -}; - -MA_API ma_decoder_config ma_decoder_config_init(ma_format outputFormat, ma_uint32 outputChannels, ma_uint32 outputSampleRate); -MA_API ma_decoder_config ma_decoder_config_init_default(void); - -MA_API ma_result ma_decoder_init(ma_decoder_read_proc onRead, ma_decoder_seek_proc onSeek, void* pUserData, const ma_decoder_config* pConfig, ma_decoder* pDecoder); -MA_API ma_result ma_decoder_init_memory(const void* pData, size_t dataSize, const ma_decoder_config* pConfig, ma_decoder* pDecoder); -MA_API ma_result ma_decoder_init_vfs(ma_vfs* pVFS, const char* pFilePath, const ma_decoder_config* pConfig, ma_decoder* pDecoder); -MA_API ma_result ma_decoder_init_vfs_w(ma_vfs* pVFS, const wchar_t* pFilePath, const ma_decoder_config* pConfig, ma_decoder* pDecoder); -MA_API ma_result ma_decoder_init_file(const char* pFilePath, const ma_decoder_config* pConfig, ma_decoder* pDecoder); -MA_API ma_result ma_decoder_init_file_w(const wchar_t* pFilePath, const ma_decoder_config* pConfig, ma_decoder* pDecoder); - -/* -Uninitializes a decoder. -*/ -MA_API ma_result ma_decoder_uninit(ma_decoder* pDecoder); - -/* -Reads PCM frames from the given decoder. - -This is not thread safe without your own synchronization. -*/ -MA_API ma_result ma_decoder_read_pcm_frames(ma_decoder* pDecoder, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead); - -/* -Seeks to a PCM frame based on its absolute index. - -This is not thread safe without your own synchronization. -*/ -MA_API ma_result ma_decoder_seek_to_pcm_frame(ma_decoder* pDecoder, ma_uint64 frameIndex); - -/* -Retrieves the decoder's output data format. -*/ -MA_API ma_result ma_decoder_get_data_format(ma_decoder* pDecoder, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate, ma_channel* pChannelMap, size_t channelMapCap); - -/* -Retrieves the current position of the read cursor in PCM frames. -*/ -MA_API ma_result ma_decoder_get_cursor_in_pcm_frames(ma_decoder* pDecoder, ma_uint64* pCursor); - -/* -Retrieves the length of the decoder in PCM frames. - -Do not call this on streams of an undefined length, such as internet radio. - -If the length is unknown or an error occurs, 0 will be returned. - -This will always return 0 for Vorbis decoders. This is due to a limitation with stb_vorbis in push mode which is what miniaudio -uses internally. - -For MP3's, this will decode the entire file. Do not call this in time critical scenarios. - -This function is not thread safe without your own synchronization. -*/ -MA_API ma_result ma_decoder_get_length_in_pcm_frames(ma_decoder* pDecoder, ma_uint64* pLength); - -/* -Retrieves the number of frames that can be read before reaching the end. - -This calls `ma_decoder_get_length_in_pcm_frames()` so you need to be aware of the rules for that function, in -particular ensuring you do not call it on streams of an undefined length, such as internet radio. - -If the total length of the decoder cannot be retrieved, such as with Vorbis decoders, `MA_NOT_IMPLEMENTED` will be -returned. -*/ -MA_API ma_result ma_decoder_get_available_frames(ma_decoder* pDecoder, ma_uint64* pAvailableFrames); - -/* -Helper for opening and decoding a file into a heap allocated block of memory. Free the returned pointer with ma_free(). On input, -pConfig should be set to what you want. On output it will be set to what you got. -*/ -MA_API ma_result ma_decode_from_vfs(ma_vfs* pVFS, const char* pFilePath, ma_decoder_config* pConfig, ma_uint64* pFrameCountOut, void** ppPCMFramesOut); -MA_API ma_result ma_decode_file(const char* pFilePath, ma_decoder_config* pConfig, ma_uint64* pFrameCountOut, void** ppPCMFramesOut); -MA_API ma_result ma_decode_memory(const void* pData, size_t dataSize, ma_decoder_config* pConfig, ma_uint64* pFrameCountOut, void** ppPCMFramesOut); - -#endif /* MA_NO_DECODING */ - - -/************************************************************************************************************************************************************ - -Encoding -======== - -Encoders do not perform any format conversion for you. If your target format does not support the format, and error will be returned. - -************************************************************************************************************************************************************/ -#ifndef MA_NO_ENCODING -typedef struct ma_encoder ma_encoder; - -typedef ma_result (* ma_encoder_write_proc) (ma_encoder* pEncoder, const void* pBufferIn, size_t bytesToWrite, size_t* pBytesWritten); -typedef ma_result (* ma_encoder_seek_proc) (ma_encoder* pEncoder, ma_int64 offset, ma_seek_origin origin); -typedef ma_result (* ma_encoder_init_proc) (ma_encoder* pEncoder); -typedef void (* ma_encoder_uninit_proc) (ma_encoder* pEncoder); -typedef ma_result (* ma_encoder_write_pcm_frames_proc)(ma_encoder* pEncoder, const void* pFramesIn, ma_uint64 frameCount, ma_uint64* pFramesWritten); - -typedef struct -{ - ma_encoding_format encodingFormat; - ma_format format; - ma_uint32 channels; - ma_uint32 sampleRate; - ma_allocation_callbacks allocationCallbacks; -} ma_encoder_config; - -MA_API ma_encoder_config ma_encoder_config_init(ma_encoding_format encodingFormat, ma_format format, ma_uint32 channels, ma_uint32 sampleRate); - -struct ma_encoder -{ - ma_encoder_config config; - ma_encoder_write_proc onWrite; - ma_encoder_seek_proc onSeek; - ma_encoder_init_proc onInit; - ma_encoder_uninit_proc onUninit; - ma_encoder_write_pcm_frames_proc onWritePCMFrames; - void* pUserData; - void* pInternalEncoder; - union - { - struct - { - ma_vfs* pVFS; - ma_vfs_file file; - } vfs; - } data; -}; - -MA_API ma_result ma_encoder_init(ma_encoder_write_proc onWrite, ma_encoder_seek_proc onSeek, void* pUserData, const ma_encoder_config* pConfig, ma_encoder* pEncoder); -MA_API ma_result ma_encoder_init_vfs(ma_vfs* pVFS, const char* pFilePath, const ma_encoder_config* pConfig, ma_encoder* pEncoder); -MA_API ma_result ma_encoder_init_vfs_w(ma_vfs* pVFS, const wchar_t* pFilePath, const ma_encoder_config* pConfig, ma_encoder* pEncoder); -MA_API ma_result ma_encoder_init_file(const char* pFilePath, const ma_encoder_config* pConfig, ma_encoder* pEncoder); -MA_API ma_result ma_encoder_init_file_w(const wchar_t* pFilePath, const ma_encoder_config* pConfig, ma_encoder* pEncoder); -MA_API void ma_encoder_uninit(ma_encoder* pEncoder); -MA_API ma_result ma_encoder_write_pcm_frames(ma_encoder* pEncoder, const void* pFramesIn, ma_uint64 frameCount, ma_uint64* pFramesWritten); - -#endif /* MA_NO_ENCODING */ - - -/************************************************************************************************************************************************************ - -Generation - -************************************************************************************************************************************************************/ -#ifndef MA_NO_GENERATION -typedef enum -{ - ma_waveform_type_sine, - ma_waveform_type_square, - ma_waveform_type_triangle, - ma_waveform_type_sawtooth -} ma_waveform_type; - -typedef struct -{ - ma_format format; - ma_uint32 channels; - ma_uint32 sampleRate; - ma_waveform_type type; - double amplitude; - double frequency; -} ma_waveform_config; - -MA_API ma_waveform_config ma_waveform_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRate, ma_waveform_type type, double amplitude, double frequency); - -typedef struct -{ - ma_data_source_base ds; - ma_waveform_config config; - double advance; - double time; -} ma_waveform; - -MA_API ma_result ma_waveform_init(const ma_waveform_config* pConfig, ma_waveform* pWaveform); -MA_API void ma_waveform_uninit(ma_waveform* pWaveform); -MA_API ma_result ma_waveform_read_pcm_frames(ma_waveform* pWaveform, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead); -MA_API ma_result ma_waveform_seek_to_pcm_frame(ma_waveform* pWaveform, ma_uint64 frameIndex); -MA_API ma_result ma_waveform_set_amplitude(ma_waveform* pWaveform, double amplitude); -MA_API ma_result ma_waveform_set_frequency(ma_waveform* pWaveform, double frequency); -MA_API ma_result ma_waveform_set_type(ma_waveform* pWaveform, ma_waveform_type type); -MA_API ma_result ma_waveform_set_sample_rate(ma_waveform* pWaveform, ma_uint32 sampleRate); - -typedef struct -{ - ma_format format; - ma_uint32 channels; - ma_uint32 sampleRate; - double dutyCycle; - double amplitude; - double frequency; -} ma_pulsewave_config; - -MA_API ma_pulsewave_config ma_pulsewave_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRate, double dutyCycle, double amplitude, double frequency); - -typedef struct -{ - ma_waveform waveform; - ma_pulsewave_config config; -} ma_pulsewave; - -MA_API ma_result ma_pulsewave_init(const ma_pulsewave_config* pConfig, ma_pulsewave* pWaveform); -MA_API void ma_pulsewave_uninit(ma_pulsewave* pWaveform); -MA_API ma_result ma_pulsewave_read_pcm_frames(ma_pulsewave* pWaveform, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead); -MA_API ma_result ma_pulsewave_seek_to_pcm_frame(ma_pulsewave* pWaveform, ma_uint64 frameIndex); -MA_API ma_result ma_pulsewave_set_amplitude(ma_pulsewave* pWaveform, double amplitude); -MA_API ma_result ma_pulsewave_set_frequency(ma_pulsewave* pWaveform, double frequency); -MA_API ma_result ma_pulsewave_set_sample_rate(ma_pulsewave* pWaveform, ma_uint32 sampleRate); -MA_API ma_result ma_pulsewave_set_duty_cycle(ma_pulsewave* pWaveform, double dutyCycle); - -typedef enum -{ - ma_noise_type_white, - ma_noise_type_pink, - ma_noise_type_brownian -} ma_noise_type; - - -typedef struct -{ - ma_format format; - ma_uint32 channels; - ma_noise_type type; - ma_int32 seed; - double amplitude; - ma_bool32 duplicateChannels; -} ma_noise_config; - -MA_API ma_noise_config ma_noise_config_init(ma_format format, ma_uint32 channels, ma_noise_type type, ma_int32 seed, double amplitude); - -typedef struct -{ - ma_data_source_base ds; - ma_noise_config config; - ma_lcg lcg; - union - { - struct - { - double** bin; - double* accumulation; - ma_uint32* counter; - } pink; - struct - { - double* accumulation; - } brownian; - } state; - - /* Memory management. */ - void* _pHeap; - ma_bool32 _ownsHeap; -} ma_noise; - -MA_API ma_result ma_noise_get_heap_size(const ma_noise_config* pConfig, size_t* pHeapSizeInBytes); -MA_API ma_result ma_noise_init_preallocated(const ma_noise_config* pConfig, void* pHeap, ma_noise* pNoise); -MA_API ma_result ma_noise_init(const ma_noise_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_noise* pNoise); -MA_API void ma_noise_uninit(ma_noise* pNoise, const ma_allocation_callbacks* pAllocationCallbacks); -MA_API ma_result ma_noise_read_pcm_frames(ma_noise* pNoise, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead); -MA_API ma_result ma_noise_set_amplitude(ma_noise* pNoise, double amplitude); -MA_API ma_result ma_noise_set_seed(ma_noise* pNoise, ma_int32 seed); -MA_API ma_result ma_noise_set_type(ma_noise* pNoise, ma_noise_type type); - -#endif /* MA_NO_GENERATION */ - - - -/************************************************************************************************************************************************************ - -Resource Manager - -************************************************************************************************************************************************************/ -/* The resource manager cannot be enabled if there is no decoder. */ -#if !defined(MA_NO_RESOURCE_MANAGER) && defined(MA_NO_DECODING) -#define MA_NO_RESOURCE_MANAGER -#endif - -#ifndef MA_NO_RESOURCE_MANAGER -typedef struct ma_resource_manager ma_resource_manager; -typedef struct ma_resource_manager_data_buffer_node ma_resource_manager_data_buffer_node; -typedef struct ma_resource_manager_data_buffer ma_resource_manager_data_buffer; -typedef struct ma_resource_manager_data_stream ma_resource_manager_data_stream; -typedef struct ma_resource_manager_data_source ma_resource_manager_data_source; - -typedef enum -{ - MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_STREAM = 0x00000001, /* When set, does not load the entire data source in memory. Disk I/O will happen on job threads. */ - MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_DECODE = 0x00000002, /* Decode data before storing in memory. When set, decoding is done at the resource manager level rather than the mixing thread. Results in faster mixing, but higher memory usage. */ - MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_ASYNC = 0x00000004, /* When set, the resource manager will load the data source asynchronously. */ - MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_WAIT_INIT = 0x00000008, /* When set, waits for initialization of the underlying data source before returning from ma_resource_manager_data_source_init(). */ - MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_UNKNOWN_LENGTH = 0x00000010, /* Gives the resource manager a hint that the length of the data source is unknown and calling `ma_data_source_get_length_in_pcm_frames()` should be avoided. */ - MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_LOOPING = 0x00000020 /* When set, configures the data source to loop by default. */ -} ma_resource_manager_data_source_flags; - - -/* -Pipeline notifications used by the resource manager. Made up of both an async notification and a fence, both of which are optional. -*/ -typedef struct -{ - ma_async_notification* pNotification; - ma_fence* pFence; -} ma_resource_manager_pipeline_stage_notification; - -typedef struct -{ - ma_resource_manager_pipeline_stage_notification init; /* Initialization of the decoder. */ - ma_resource_manager_pipeline_stage_notification done; /* Decoding fully completed. */ -} ma_resource_manager_pipeline_notifications; - -MA_API ma_resource_manager_pipeline_notifications ma_resource_manager_pipeline_notifications_init(void); - - - -/* BEGIN BACKWARDS COMPATIBILITY */ -/* TODO: Remove this block in version 0.12. */ -#if 1 -#define ma_resource_manager_job ma_job -#define ma_resource_manager_job_init ma_job_init -#define MA_JOB_TYPE_RESOURCE_MANAGER_QUEUE_FLAG_NON_BLOCKING MA_JOB_QUEUE_FLAG_NON_BLOCKING -#define ma_resource_manager_job_queue_config ma_job_queue_config -#define ma_resource_manager_job_queue_config_init ma_job_queue_config_init -#define ma_resource_manager_job_queue ma_job_queue -#define ma_resource_manager_job_queue_get_heap_size ma_job_queue_get_heap_size -#define ma_resource_manager_job_queue_init_preallocated ma_job_queue_init_preallocated -#define ma_resource_manager_job_queue_init ma_job_queue_init -#define ma_resource_manager_job_queue_uninit ma_job_queue_uninit -#define ma_resource_manager_job_queue_post ma_job_queue_post -#define ma_resource_manager_job_queue_next ma_job_queue_next -#endif -/* END BACKWARDS COMPATIBILITY */ - - - - -/* Maximum job thread count will be restricted to this, but this may be removed later and replaced with a heap allocation thereby removing any limitation. */ -#ifndef MA_RESOURCE_MANAGER_MAX_JOB_THREAD_COUNT -#define MA_RESOURCE_MANAGER_MAX_JOB_THREAD_COUNT 64 -#endif - -typedef enum -{ - /* Indicates ma_resource_manager_next_job() should not block. Only valid when the job thread count is 0. */ - MA_RESOURCE_MANAGER_FLAG_NON_BLOCKING = 0x00000001, - - /* Disables any kind of multithreading. Implicitly enables MA_RESOURCE_MANAGER_FLAG_NON_BLOCKING. */ - MA_RESOURCE_MANAGER_FLAG_NO_THREADING = 0x00000002 -} ma_resource_manager_flags; - -typedef struct -{ - const char* pFilePath; - const wchar_t* pFilePathW; - const ma_resource_manager_pipeline_notifications* pNotifications; - ma_uint64 initialSeekPointInPCMFrames; - ma_uint64 rangeBegInPCMFrames; - ma_uint64 rangeEndInPCMFrames; - ma_uint64 loopPointBegInPCMFrames; - ma_uint64 loopPointEndInPCMFrames; - ma_uint32 flags; - ma_bool32 isLooping; /* Deprecated. Use the MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_LOOPING flag in `flags` instead. */ -} ma_resource_manager_data_source_config; - -MA_API ma_resource_manager_data_source_config ma_resource_manager_data_source_config_init(void); - - -typedef enum -{ - ma_resource_manager_data_supply_type_unknown = 0, /* Used for determining whether or the data supply has been initialized. */ - ma_resource_manager_data_supply_type_encoded, /* Data supply is an encoded buffer. Connector is ma_decoder. */ - ma_resource_manager_data_supply_type_decoded, /* Data supply is a decoded buffer. Connector is ma_audio_buffer. */ - ma_resource_manager_data_supply_type_decoded_paged /* Data supply is a linked list of decoded buffers. Connector is ma_paged_audio_buffer. */ -} ma_resource_manager_data_supply_type; - -typedef struct -{ - MA_ATOMIC(4, ma_resource_manager_data_supply_type) type; /* Read and written from different threads so needs to be accessed atomically. */ - union - { - struct - { - const void* pData; - size_t sizeInBytes; - } encoded; - struct - { - const void* pData; - ma_uint64 totalFrameCount; - ma_uint64 decodedFrameCount; - ma_format format; - ma_uint32 channels; - ma_uint32 sampleRate; - } decoded; - struct - { - ma_paged_audio_buffer_data data; - ma_uint64 decodedFrameCount; - ma_uint32 sampleRate; - } decodedPaged; - } backend; -} ma_resource_manager_data_supply; - -struct ma_resource_manager_data_buffer_node -{ - ma_uint32 hashedName32; /* The hashed name. This is the key. */ - ma_uint32 refCount; - MA_ATOMIC(4, ma_result) result; /* Result from asynchronous loading. When loading set to MA_BUSY. When fully loaded set to MA_SUCCESS. When deleting set to MA_UNAVAILABLE. */ - MA_ATOMIC(4, ma_uint32) executionCounter; /* For allocating execution orders for jobs. */ - MA_ATOMIC(4, ma_uint32) executionPointer; /* For managing the order of execution for asynchronous jobs relating to this object. Incremented as jobs complete processing. */ - ma_bool32 isDataOwnedByResourceManager; /* Set to true when the underlying data buffer was allocated the resource manager. Set to false if it is owned by the application (via ma_resource_manager_register_*()). */ - ma_resource_manager_data_supply data; - ma_resource_manager_data_buffer_node* pParent; - ma_resource_manager_data_buffer_node* pChildLo; - ma_resource_manager_data_buffer_node* pChildHi; -}; - -struct ma_resource_manager_data_buffer -{ - ma_data_source_base ds; /* Base data source. A data buffer is a data source. */ - ma_resource_manager* pResourceManager; /* A pointer to the resource manager that owns this buffer. */ - ma_resource_manager_data_buffer_node* pNode; /* The data node. This is reference counted and is what supplies the data. */ - ma_uint32 flags; /* The flags that were passed used to initialize the buffer. */ - MA_ATOMIC(4, ma_uint32) executionCounter; /* For allocating execution orders for jobs. */ - MA_ATOMIC(4, ma_uint32) executionPointer; /* For managing the order of execution for asynchronous jobs relating to this object. Incremented as jobs complete processing. */ - ma_uint64 seekTargetInPCMFrames; /* Only updated by the public API. Never written nor read from the job thread. */ - ma_bool32 seekToCursorOnNextRead; /* On the next read we need to seek to the frame cursor. */ - MA_ATOMIC(4, ma_result) result; /* Keeps track of a result of decoding. Set to MA_BUSY while the buffer is still loading. Set to MA_SUCCESS when loading is finished successfully. Otherwise set to some other code. */ - MA_ATOMIC(4, ma_bool32) isLooping; /* Can be read and written by different threads at the same time. Must be used atomically. */ - ma_atomic_bool32 isConnectorInitialized; /* Used for asynchronous loading to ensure we don't try to initialize the connector multiple times while waiting for the node to fully load. */ - union - { - ma_decoder decoder; /* Supply type is ma_resource_manager_data_supply_type_encoded */ - ma_audio_buffer buffer; /* Supply type is ma_resource_manager_data_supply_type_decoded */ - ma_paged_audio_buffer pagedBuffer; /* Supply type is ma_resource_manager_data_supply_type_decoded_paged */ - } connector; /* Connects this object to the node's data supply. */ -}; - -struct ma_resource_manager_data_stream -{ - ma_data_source_base ds; /* Base data source. A data stream is a data source. */ - ma_resource_manager* pResourceManager; /* A pointer to the resource manager that owns this data stream. */ - ma_uint32 flags; /* The flags that were passed used to initialize the stream. */ - ma_decoder decoder; /* Used for filling pages with data. This is only ever accessed by the job thread. The public API should never touch this. */ - ma_bool32 isDecoderInitialized; /* Required for determining whether or not the decoder should be uninitialized in MA_JOB_TYPE_RESOURCE_MANAGER_FREE_DATA_STREAM. */ - ma_uint64 totalLengthInPCMFrames; /* This is calculated when first loaded by the MA_JOB_TYPE_RESOURCE_MANAGER_LOAD_DATA_STREAM. */ - ma_uint32 relativeCursor; /* The playback cursor, relative to the current page. Only ever accessed by the public API. Never accessed by the job thread. */ - MA_ATOMIC(8, ma_uint64) absoluteCursor; /* The playback cursor, in absolute position starting from the start of the file. */ - ma_uint32 currentPageIndex; /* Toggles between 0 and 1. Index 0 is the first half of pPageData. Index 1 is the second half. Only ever accessed by the public API. Never accessed by the job thread. */ - MA_ATOMIC(4, ma_uint32) executionCounter; /* For allocating execution orders for jobs. */ - MA_ATOMIC(4, ma_uint32) executionPointer; /* For managing the order of execution for asynchronous jobs relating to this object. Incremented as jobs complete processing. */ - - /* Written by the public API, read by the job thread. */ - MA_ATOMIC(4, ma_bool32) isLooping; /* Whether or not the stream is looping. It's important to set the looping flag at the data stream level for smooth loop transitions. */ - - /* Written by the job thread, read by the public API. */ - void* pPageData; /* Buffer containing the decoded data of each page. Allocated once at initialization time. */ - MA_ATOMIC(4, ma_uint32) pageFrameCount[2]; /* The number of valid PCM frames in each page. Used to determine the last valid frame. */ - - /* Written and read by both the public API and the job thread. These must be atomic. */ - MA_ATOMIC(4, ma_result) result; /* Result from asynchronous loading. When loading set to MA_BUSY. When initialized set to MA_SUCCESS. When deleting set to MA_UNAVAILABLE. If an error occurs when loading, set to an error code. */ - MA_ATOMIC(4, ma_bool32) isDecoderAtEnd; /* Whether or not the decoder has reached the end. */ - MA_ATOMIC(4, ma_bool32) isPageValid[2]; /* Booleans to indicate whether or not a page is valid. Set to false by the public API, set to true by the job thread. Set to false as the pages are consumed, true when they are filled. */ - MA_ATOMIC(4, ma_bool32) seekCounter; /* When 0, no seeking is being performed. When > 0, a seek is being performed and reading should be delayed with MA_BUSY. */ -}; - -struct ma_resource_manager_data_source -{ - union - { - ma_resource_manager_data_buffer buffer; - ma_resource_manager_data_stream stream; - } backend; /* Must be the first item because we need the first item to be the data source callbacks for the buffer or stream. */ - - ma_uint32 flags; /* The flags that were passed in to ma_resource_manager_data_source_init(). */ - MA_ATOMIC(4, ma_uint32) executionCounter; /* For allocating execution orders for jobs. */ - MA_ATOMIC(4, ma_uint32) executionPointer; /* For managing the order of execution for asynchronous jobs relating to this object. Incremented as jobs complete processing. */ -}; - -typedef struct -{ - ma_allocation_callbacks allocationCallbacks; - ma_log* pLog; - ma_format decodedFormat; /* The decoded format to use. Set to ma_format_unknown (default) to use the file's native format. */ - ma_uint32 decodedChannels; /* The decoded channel count to use. Set to 0 (default) to use the file's native channel count. */ - ma_uint32 decodedSampleRate; /* the decoded sample rate to use. Set to 0 (default) to use the file's native sample rate. */ - ma_uint32 jobThreadCount; /* Set to 0 if you want to self-manage your job threads. Defaults to 1. */ - size_t jobThreadStackSize; - ma_uint32 jobQueueCapacity; /* The maximum number of jobs that can fit in the queue at a time. Defaults to MA_JOB_TYPE_RESOURCE_MANAGER_QUEUE_CAPACITY. Cannot be zero. */ - ma_uint32 flags; - ma_vfs* pVFS; /* Can be NULL in which case defaults will be used. */ - ma_decoding_backend_vtable** ppCustomDecodingBackendVTables; - ma_uint32 customDecodingBackendCount; - void* pCustomDecodingBackendUserData; -} ma_resource_manager_config; - -MA_API ma_resource_manager_config ma_resource_manager_config_init(void); - -struct ma_resource_manager -{ - ma_resource_manager_config config; - ma_resource_manager_data_buffer_node* pRootDataBufferNode; /* The root buffer in the binary tree. */ -#ifndef MA_NO_THREADING - ma_mutex dataBufferBSTLock; /* For synchronizing access to the data buffer binary tree. */ - ma_thread jobThreads[MA_RESOURCE_MANAGER_MAX_JOB_THREAD_COUNT]; /* The threads for executing jobs. */ -#endif - ma_job_queue jobQueue; /* Multi-consumer, multi-producer job queue for managing jobs for asynchronous decoding and streaming. */ - ma_default_vfs defaultVFS; /* Only used if a custom VFS is not specified. */ - ma_log log; /* Only used if no log was specified in the config. */ -}; - -/* Init. */ -MA_API ma_result ma_resource_manager_init(const ma_resource_manager_config* pConfig, ma_resource_manager* pResourceManager); -MA_API void ma_resource_manager_uninit(ma_resource_manager* pResourceManager); -MA_API ma_log* ma_resource_manager_get_log(ma_resource_manager* pResourceManager); - -/* Registration. */ -MA_API ma_result ma_resource_manager_register_file(ma_resource_manager* pResourceManager, const char* pFilePath, ma_uint32 flags); -MA_API ma_result ma_resource_manager_register_file_w(ma_resource_manager* pResourceManager, const wchar_t* pFilePath, ma_uint32 flags); -MA_API ma_result ma_resource_manager_register_decoded_data(ma_resource_manager* pResourceManager, const char* pName, const void* pData, ma_uint64 frameCount, ma_format format, ma_uint32 channels, ma_uint32 sampleRate); /* Does not copy. Increments the reference count if already exists and returns MA_SUCCESS. */ -MA_API ma_result ma_resource_manager_register_decoded_data_w(ma_resource_manager* pResourceManager, const wchar_t* pName, const void* pData, ma_uint64 frameCount, ma_format format, ma_uint32 channels, ma_uint32 sampleRate); -MA_API ma_result ma_resource_manager_register_encoded_data(ma_resource_manager* pResourceManager, const char* pName, const void* pData, size_t sizeInBytes); /* Does not copy. Increments the reference count if already exists and returns MA_SUCCESS. */ -MA_API ma_result ma_resource_manager_register_encoded_data_w(ma_resource_manager* pResourceManager, const wchar_t* pName, const void* pData, size_t sizeInBytes); -MA_API ma_result ma_resource_manager_unregister_file(ma_resource_manager* pResourceManager, const char* pFilePath); -MA_API ma_result ma_resource_manager_unregister_file_w(ma_resource_manager* pResourceManager, const wchar_t* pFilePath); -MA_API ma_result ma_resource_manager_unregister_data(ma_resource_manager* pResourceManager, const char* pName); -MA_API ma_result ma_resource_manager_unregister_data_w(ma_resource_manager* pResourceManager, const wchar_t* pName); - -/* Data Buffers. */ -MA_API ma_result ma_resource_manager_data_buffer_init_ex(ma_resource_manager* pResourceManager, const ma_resource_manager_data_source_config* pConfig, ma_resource_manager_data_buffer* pDataBuffer); -MA_API ma_result ma_resource_manager_data_buffer_init(ma_resource_manager* pResourceManager, const char* pFilePath, ma_uint32 flags, const ma_resource_manager_pipeline_notifications* pNotifications, ma_resource_manager_data_buffer* pDataBuffer); -MA_API ma_result ma_resource_manager_data_buffer_init_w(ma_resource_manager* pResourceManager, const wchar_t* pFilePath, ma_uint32 flags, const ma_resource_manager_pipeline_notifications* pNotifications, ma_resource_manager_data_buffer* pDataBuffer); -MA_API ma_result ma_resource_manager_data_buffer_init_copy(ma_resource_manager* pResourceManager, const ma_resource_manager_data_buffer* pExistingDataBuffer, ma_resource_manager_data_buffer* pDataBuffer); -MA_API ma_result ma_resource_manager_data_buffer_uninit(ma_resource_manager_data_buffer* pDataBuffer); -MA_API ma_result ma_resource_manager_data_buffer_read_pcm_frames(ma_resource_manager_data_buffer* pDataBuffer, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead); -MA_API ma_result ma_resource_manager_data_buffer_seek_to_pcm_frame(ma_resource_manager_data_buffer* pDataBuffer, ma_uint64 frameIndex); -MA_API ma_result ma_resource_manager_data_buffer_get_data_format(ma_resource_manager_data_buffer* pDataBuffer, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate, ma_channel* pChannelMap, size_t channelMapCap); -MA_API ma_result ma_resource_manager_data_buffer_get_cursor_in_pcm_frames(ma_resource_manager_data_buffer* pDataBuffer, ma_uint64* pCursor); -MA_API ma_result ma_resource_manager_data_buffer_get_length_in_pcm_frames(ma_resource_manager_data_buffer* pDataBuffer, ma_uint64* pLength); -MA_API ma_result ma_resource_manager_data_buffer_result(const ma_resource_manager_data_buffer* pDataBuffer); -MA_API ma_result ma_resource_manager_data_buffer_set_looping(ma_resource_manager_data_buffer* pDataBuffer, ma_bool32 isLooping); -MA_API ma_bool32 ma_resource_manager_data_buffer_is_looping(const ma_resource_manager_data_buffer* pDataBuffer); -MA_API ma_result ma_resource_manager_data_buffer_get_available_frames(ma_resource_manager_data_buffer* pDataBuffer, ma_uint64* pAvailableFrames); - -/* Data Streams. */ -MA_API ma_result ma_resource_manager_data_stream_init_ex(ma_resource_manager* pResourceManager, const ma_resource_manager_data_source_config* pConfig, ma_resource_manager_data_stream* pDataStream); -MA_API ma_result ma_resource_manager_data_stream_init(ma_resource_manager* pResourceManager, const char* pFilePath, ma_uint32 flags, const ma_resource_manager_pipeline_notifications* pNotifications, ma_resource_manager_data_stream* pDataStream); -MA_API ma_result ma_resource_manager_data_stream_init_w(ma_resource_manager* pResourceManager, const wchar_t* pFilePath, ma_uint32 flags, const ma_resource_manager_pipeline_notifications* pNotifications, ma_resource_manager_data_stream* pDataStream); -MA_API ma_result ma_resource_manager_data_stream_uninit(ma_resource_manager_data_stream* pDataStream); -MA_API ma_result ma_resource_manager_data_stream_read_pcm_frames(ma_resource_manager_data_stream* pDataStream, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead); -MA_API ma_result ma_resource_manager_data_stream_seek_to_pcm_frame(ma_resource_manager_data_stream* pDataStream, ma_uint64 frameIndex); -MA_API ma_result ma_resource_manager_data_stream_get_data_format(ma_resource_manager_data_stream* pDataStream, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate, ma_channel* pChannelMap, size_t channelMapCap); -MA_API ma_result ma_resource_manager_data_stream_get_cursor_in_pcm_frames(ma_resource_manager_data_stream* pDataStream, ma_uint64* pCursor); -MA_API ma_result ma_resource_manager_data_stream_get_length_in_pcm_frames(ma_resource_manager_data_stream* pDataStream, ma_uint64* pLength); -MA_API ma_result ma_resource_manager_data_stream_result(const ma_resource_manager_data_stream* pDataStream); -MA_API ma_result ma_resource_manager_data_stream_set_looping(ma_resource_manager_data_stream* pDataStream, ma_bool32 isLooping); -MA_API ma_bool32 ma_resource_manager_data_stream_is_looping(const ma_resource_manager_data_stream* pDataStream); -MA_API ma_result ma_resource_manager_data_stream_get_available_frames(ma_resource_manager_data_stream* pDataStream, ma_uint64* pAvailableFrames); - -/* Data Sources. */ -MA_API ma_result ma_resource_manager_data_source_init_ex(ma_resource_manager* pResourceManager, const ma_resource_manager_data_source_config* pConfig, ma_resource_manager_data_source* pDataSource); -MA_API ma_result ma_resource_manager_data_source_init(ma_resource_manager* pResourceManager, const char* pName, ma_uint32 flags, const ma_resource_manager_pipeline_notifications* pNotifications, ma_resource_manager_data_source* pDataSource); -MA_API ma_result ma_resource_manager_data_source_init_w(ma_resource_manager* pResourceManager, const wchar_t* pName, ma_uint32 flags, const ma_resource_manager_pipeline_notifications* pNotifications, ma_resource_manager_data_source* pDataSource); -MA_API ma_result ma_resource_manager_data_source_init_copy(ma_resource_manager* pResourceManager, const ma_resource_manager_data_source* pExistingDataSource, ma_resource_manager_data_source* pDataSource); -MA_API ma_result ma_resource_manager_data_source_uninit(ma_resource_manager_data_source* pDataSource); -MA_API ma_result ma_resource_manager_data_source_read_pcm_frames(ma_resource_manager_data_source* pDataSource, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead); -MA_API ma_result ma_resource_manager_data_source_seek_to_pcm_frame(ma_resource_manager_data_source* pDataSource, ma_uint64 frameIndex); -MA_API ma_result ma_resource_manager_data_source_get_data_format(ma_resource_manager_data_source* pDataSource, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate, ma_channel* pChannelMap, size_t channelMapCap); -MA_API ma_result ma_resource_manager_data_source_get_cursor_in_pcm_frames(ma_resource_manager_data_source* pDataSource, ma_uint64* pCursor); -MA_API ma_result ma_resource_manager_data_source_get_length_in_pcm_frames(ma_resource_manager_data_source* pDataSource, ma_uint64* pLength); -MA_API ma_result ma_resource_manager_data_source_result(const ma_resource_manager_data_source* pDataSource); -MA_API ma_result ma_resource_manager_data_source_set_looping(ma_resource_manager_data_source* pDataSource, ma_bool32 isLooping); -MA_API ma_bool32 ma_resource_manager_data_source_is_looping(const ma_resource_manager_data_source* pDataSource); -MA_API ma_result ma_resource_manager_data_source_get_available_frames(ma_resource_manager_data_source* pDataSource, ma_uint64* pAvailableFrames); - -/* Job management. */ -MA_API ma_result ma_resource_manager_post_job(ma_resource_manager* pResourceManager, const ma_job* pJob); -MA_API ma_result ma_resource_manager_post_job_quit(ma_resource_manager* pResourceManager); /* Helper for posting a quit job. */ -MA_API ma_result ma_resource_manager_next_job(ma_resource_manager* pResourceManager, ma_job* pJob); -MA_API ma_result ma_resource_manager_process_job(ma_resource_manager* pResourceManager, ma_job* pJob); /* DEPRECATED. Use ma_job_process(). Will be removed in version 0.12. */ -MA_API ma_result ma_resource_manager_process_next_job(ma_resource_manager* pResourceManager); /* Returns MA_CANCELLED if a MA_JOB_TYPE_QUIT job is found. In non-blocking mode, returns MA_NO_DATA_AVAILABLE if no jobs are available. */ -#endif /* MA_NO_RESOURCE_MANAGER */ - - - -/************************************************************************************************************************************************************ - -Node Graph - -************************************************************************************************************************************************************/ -#ifndef MA_NO_NODE_GRAPH -/* Must never exceed 254. */ -#ifndef MA_MAX_NODE_BUS_COUNT -#define MA_MAX_NODE_BUS_COUNT 254 -#endif - -/* Used internally by miniaudio for memory management. Must never exceed MA_MAX_NODE_BUS_COUNT. */ -#ifndef MA_MAX_NODE_LOCAL_BUS_COUNT -#define MA_MAX_NODE_LOCAL_BUS_COUNT 2 -#endif - -/* Use this when the bus count is determined by the node instance rather than the vtable. */ -#define MA_NODE_BUS_COUNT_UNKNOWN 255 - - -/* For some internal memory management of ma_node_graph. */ -typedef struct -{ - size_t offset; - size_t sizeInBytes; - unsigned char _data[1]; -} ma_stack; - - -typedef struct ma_node_graph ma_node_graph; -typedef void ma_node; - - -/* Node flags. */ -typedef enum -{ - MA_NODE_FLAG_PASSTHROUGH = 0x00000001, - MA_NODE_FLAG_CONTINUOUS_PROCESSING = 0x00000002, - MA_NODE_FLAG_ALLOW_NULL_INPUT = 0x00000004, - MA_NODE_FLAG_DIFFERENT_PROCESSING_RATES = 0x00000008, - MA_NODE_FLAG_SILENT_OUTPUT = 0x00000010 -} ma_node_flags; - - -/* The playback state of a node. Either started or stopped. */ -typedef enum -{ - ma_node_state_started = 0, - ma_node_state_stopped = 1 -} ma_node_state; - - -typedef struct -{ - /* - Extended processing callback. This callback is used for effects that process input and output - at different rates (i.e. they perform resampling). This is similar to the simple version, only - they take two separate frame counts: one for input, and one for output. - - On input, `pFrameCountOut` is equal to the capacity of the output buffer for each bus, whereas - `pFrameCountIn` will be equal to the number of PCM frames in each of the buffers in `ppFramesIn`. - - On output, set `pFrameCountOut` to the number of PCM frames that were actually output and set - `pFrameCountIn` to the number of input frames that were consumed. - */ - void (* onProcess)(ma_node* pNode, const float** ppFramesIn, ma_uint32* pFrameCountIn, float** ppFramesOut, ma_uint32* pFrameCountOut); - - /* - A callback for retrieving the number of input frames that are required to output the - specified number of output frames. You would only want to implement this when the node performs - resampling. This is optional, even for nodes that perform resampling, but it does offer a - small reduction in latency as it allows miniaudio to calculate the exact number of input frames - to read at a time instead of having to estimate. - */ - ma_result (* onGetRequiredInputFrameCount)(ma_node* pNode, ma_uint32 outputFrameCount, ma_uint32* pInputFrameCount); - - /* - The number of input buses. This is how many sub-buffers will be contained in the `ppFramesIn` - parameters of the callbacks above. - */ - ma_uint8 inputBusCount; - - /* - The number of output buses. This is how many sub-buffers will be contained in the `ppFramesOut` - parameters of the callbacks above. - */ - ma_uint8 outputBusCount; - - /* - Flags describing characteristics of the node. This is currently just a placeholder for some - ideas for later on. - */ - ma_uint32 flags; -} ma_node_vtable; - -typedef struct -{ - const ma_node_vtable* vtable; /* Should never be null. Initialization of the node will fail if so. */ - ma_node_state initialState; /* Defaults to ma_node_state_started. */ - ma_uint32 inputBusCount; /* Only used if the vtable specifies an input bus count of `MA_NODE_BUS_COUNT_UNKNOWN`, otherwise must be set to `MA_NODE_BUS_COUNT_UNKNOWN` (default). */ - ma_uint32 outputBusCount; /* Only used if the vtable specifies an output bus count of `MA_NODE_BUS_COUNT_UNKNOWN`, otherwise be set to `MA_NODE_BUS_COUNT_UNKNOWN` (default). */ - const ma_uint32* pInputChannels; /* The number of elements are determined by the input bus count as determined by the vtable, or `inputBusCount` if the vtable specifies `MA_NODE_BUS_COUNT_UNKNOWN`. */ - const ma_uint32* pOutputChannels; /* The number of elements are determined by the output bus count as determined by the vtable, or `outputBusCount` if the vtable specifies `MA_NODE_BUS_COUNT_UNKNOWN`. */ -} ma_node_config; - -MA_API ma_node_config ma_node_config_init(void); - - -/* -A node has multiple output buses. An output bus is attached to an input bus as an item in a linked -list. Think of the input bus as a linked list, with the output bus being an item in that list. -*/ -typedef struct ma_node_output_bus ma_node_output_bus; -struct ma_node_output_bus -{ - /* Immutable. */ - ma_node* pNode; /* The node that owns this output bus. The input node. Will be null for dummy head and tail nodes. */ - ma_uint8 outputBusIndex; /* The index of the output bus on pNode that this output bus represents. */ - ma_uint8 channels; /* The number of channels in the audio stream for this bus. */ - - /* Mutable via multiple threads. Must be used atomically. The weird ordering here is for packing reasons. */ - ma_uint8 inputNodeInputBusIndex; /* The index of the input bus on the input. Required for detaching. Will only be used within the spinlock so does not need to be atomic. */ - MA_ATOMIC(4, ma_uint32) flags; /* Some state flags for tracking the read state of the output buffer. A combination of MA_NODE_OUTPUT_BUS_FLAG_*. */ - MA_ATOMIC(4, ma_uint32) refCount; /* Reference count for some thread-safety when detaching. */ - MA_ATOMIC(4, ma_bool32) isAttached; /* This is used to prevent iteration of nodes that are in the middle of being detached. Used for thread safety. */ - MA_ATOMIC(4, ma_spinlock) lock; /* Unfortunate lock, but significantly simplifies the implementation. Required for thread-safe attaching and detaching. */ - MA_ATOMIC(4, float) volume; /* Linear. */ - MA_ATOMIC(MA_SIZEOF_PTR, ma_node_output_bus*) pNext; /* If null, it's the tail node or detached. */ - MA_ATOMIC(MA_SIZEOF_PTR, ma_node_output_bus*) pPrev; /* If null, it's the head node or detached. */ - MA_ATOMIC(MA_SIZEOF_PTR, ma_node*) pInputNode; /* The node that this output bus is attached to. Required for detaching. */ -}; - -/* -A node has multiple input buses. The output buses of a node are connecting to the input busses of -another. An input bus is essentially just a linked list of output buses. -*/ -typedef struct ma_node_input_bus ma_node_input_bus; -struct ma_node_input_bus -{ - /* Mutable via multiple threads. */ - ma_node_output_bus head; /* Dummy head node for simplifying some lock-free thread-safety stuff. */ - MA_ATOMIC(4, ma_uint32) nextCounter; /* This is used to determine whether or not the input bus is finding the next node in the list. Used for thread safety when detaching output buses. */ - MA_ATOMIC(4, ma_spinlock) lock; /* Unfortunate lock, but significantly simplifies the implementation. Required for thread-safe attaching and detaching. */ - - /* Set once at startup. */ - ma_uint8 channels; /* The number of channels in the audio stream for this bus. */ -}; - - -typedef struct ma_node_base ma_node_base; -struct ma_node_base -{ - /* These variables are set once at startup. */ - ma_node_graph* pNodeGraph; /* The graph this node belongs to. */ - const ma_node_vtable* vtable; - ma_uint32 inputBusCount; - ma_uint32 outputBusCount; - ma_node_input_bus* pInputBuses; - ma_node_output_bus* pOutputBuses; - float* pCachedData; /* Allocated on the heap. Fixed size. Needs to be stored on the heap because reading from output buses is done in separate function calls. */ - ma_uint16 cachedDataCapInFramesPerBus; /* The capacity of the input data cache in frames, per bus. */ - - /* These variables are read and written only from the audio thread. */ - ma_uint16 cachedFrameCountOut; - ma_uint16 cachedFrameCountIn; - ma_uint16 consumedFrameCountIn; - - /* These variables are read and written between different threads. */ - MA_ATOMIC(4, ma_node_state) state; /* When set to stopped, nothing will be read, regardless of the times in stateTimes. */ - MA_ATOMIC(8, ma_uint64) stateTimes[2]; /* Indexed by ma_node_state. Specifies the time based on the global clock that a node should be considered to be in the relevant state. */ - MA_ATOMIC(8, ma_uint64) localTime; /* The node's local clock. This is just a running sum of the number of output frames that have been processed. Can be modified by any thread with `ma_node_set_time()`. */ - - /* Memory management. */ - ma_node_input_bus _inputBuses[MA_MAX_NODE_LOCAL_BUS_COUNT]; - ma_node_output_bus _outputBuses[MA_MAX_NODE_LOCAL_BUS_COUNT]; - void* _pHeap; /* A heap allocation for internal use only. pInputBuses and/or pOutputBuses will point to this if the bus count exceeds MA_MAX_NODE_LOCAL_BUS_COUNT. */ - ma_bool32 _ownsHeap; /* If set to true, the node owns the heap allocation and _pHeap will be freed in ma_node_uninit(). */ -}; - -MA_API ma_result ma_node_get_heap_size(ma_node_graph* pNodeGraph, const ma_node_config* pConfig, size_t* pHeapSizeInBytes); -MA_API ma_result ma_node_init_preallocated(ma_node_graph* pNodeGraph, const ma_node_config* pConfig, void* pHeap, ma_node* pNode); -MA_API ma_result ma_node_init(ma_node_graph* pNodeGraph, const ma_node_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_node* pNode); -MA_API void ma_node_uninit(ma_node* pNode, const ma_allocation_callbacks* pAllocationCallbacks); -MA_API ma_node_graph* ma_node_get_node_graph(const ma_node* pNode); -MA_API ma_uint32 ma_node_get_input_bus_count(const ma_node* pNode); -MA_API ma_uint32 ma_node_get_output_bus_count(const ma_node* pNode); -MA_API ma_uint32 ma_node_get_input_channels(const ma_node* pNode, ma_uint32 inputBusIndex); -MA_API ma_uint32 ma_node_get_output_channels(const ma_node* pNode, ma_uint32 outputBusIndex); -MA_API ma_result ma_node_attach_output_bus(ma_node* pNode, ma_uint32 outputBusIndex, ma_node* pOtherNode, ma_uint32 otherNodeInputBusIndex); -MA_API ma_result ma_node_detach_output_bus(ma_node* pNode, ma_uint32 outputBusIndex); -MA_API ma_result ma_node_detach_all_output_buses(ma_node* pNode); -MA_API ma_result ma_node_set_output_bus_volume(ma_node* pNode, ma_uint32 outputBusIndex, float volume); -MA_API float ma_node_get_output_bus_volume(const ma_node* pNode, ma_uint32 outputBusIndex); -MA_API ma_result ma_node_set_state(ma_node* pNode, ma_node_state state); -MA_API ma_node_state ma_node_get_state(const ma_node* pNode); -MA_API ma_result ma_node_set_state_time(ma_node* pNode, ma_node_state state, ma_uint64 globalTime); -MA_API ma_uint64 ma_node_get_state_time(const ma_node* pNode, ma_node_state state); -MA_API ma_node_state ma_node_get_state_by_time(const ma_node* pNode, ma_uint64 globalTime); -MA_API ma_node_state ma_node_get_state_by_time_range(const ma_node* pNode, ma_uint64 globalTimeBeg, ma_uint64 globalTimeEnd); -MA_API ma_uint64 ma_node_get_time(const ma_node* pNode); -MA_API ma_result ma_node_set_time(ma_node* pNode, ma_uint64 localTime); - - -typedef struct -{ - ma_uint32 channels; - ma_uint32 processingSizeInFrames; /* This is the preferred processing size for node processing callbacks unless overridden by a node itself. Can be 0 in which case it will be based on the frame count passed into ma_node_graph_read_pcm_frames(), but will not be well defined. */ - size_t preMixStackSizeInBytes; /* Defaults to 512KB per channel. Reducing this will save memory, but the depth of your node graph will be more restricted. */ -} ma_node_graph_config; - -MA_API ma_node_graph_config ma_node_graph_config_init(ma_uint32 channels); - - -struct ma_node_graph -{ - /* Immutable. */ - ma_node_base base; /* The node graph itself is a node so it can be connected as an input to different node graph. This has zero inputs and calls ma_node_graph_read_pcm_frames() to generate it's output. */ - ma_node_base endpoint; /* Special node that all nodes eventually connect to. Data is read from this node in ma_node_graph_read_pcm_frames(). */ - float* pProcessingCache; /* This will be allocated when processingSizeInFrames is non-zero. This is needed because ma_node_graph_read_pcm_frames() can be called with a variable number of frames, and we may need to do some buffering in situations where the caller requests a frame count that's not a multiple of processingSizeInFrames. */ - ma_uint32 processingCacheFramesRemaining; - ma_uint32 processingSizeInFrames; - - /* Read and written by multiple threads. */ - MA_ATOMIC(4, ma_bool32) isReading; - - /* Modified only by the audio thread. */ - ma_stack* pPreMixStack; -}; - -MA_API ma_result ma_node_graph_init(const ma_node_graph_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_node_graph* pNodeGraph); -MA_API void ma_node_graph_uninit(ma_node_graph* pNodeGraph, const ma_allocation_callbacks* pAllocationCallbacks); -MA_API ma_node* ma_node_graph_get_endpoint(ma_node_graph* pNodeGraph); -MA_API ma_result ma_node_graph_read_pcm_frames(ma_node_graph* pNodeGraph, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead); -MA_API ma_uint32 ma_node_graph_get_channels(const ma_node_graph* pNodeGraph); -MA_API ma_uint64 ma_node_graph_get_time(const ma_node_graph* pNodeGraph); -MA_API ma_result ma_node_graph_set_time(ma_node_graph* pNodeGraph, ma_uint64 globalTime); - - - -/* Data source node. 0 input buses, 1 output bus. Used for reading from a data source. */ -typedef struct -{ - ma_node_config nodeConfig; - ma_data_source* pDataSource; -} ma_data_source_node_config; - -MA_API ma_data_source_node_config ma_data_source_node_config_init(ma_data_source* pDataSource); - - -typedef struct -{ - ma_node_base base; - ma_data_source* pDataSource; -} ma_data_source_node; - -MA_API ma_result ma_data_source_node_init(ma_node_graph* pNodeGraph, const ma_data_source_node_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_data_source_node* pDataSourceNode); -MA_API void ma_data_source_node_uninit(ma_data_source_node* pDataSourceNode, const ma_allocation_callbacks* pAllocationCallbacks); -MA_API ma_result ma_data_source_node_set_looping(ma_data_source_node* pDataSourceNode, ma_bool32 isLooping); -MA_API ma_bool32 ma_data_source_node_is_looping(ma_data_source_node* pDataSourceNode); - - -/* Splitter Node. 1 input, many outputs. Used for splitting/copying a stream so it can be as input into two separate output nodes. */ -typedef struct -{ - ma_node_config nodeConfig; - ma_uint32 channels; - ma_uint32 outputBusCount; -} ma_splitter_node_config; - -MA_API ma_splitter_node_config ma_splitter_node_config_init(ma_uint32 channels); - - -typedef struct -{ - ma_node_base base; -} ma_splitter_node; - -MA_API ma_result ma_splitter_node_init(ma_node_graph* pNodeGraph, const ma_splitter_node_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_splitter_node* pSplitterNode); -MA_API void ma_splitter_node_uninit(ma_splitter_node* pSplitterNode, const ma_allocation_callbacks* pAllocationCallbacks); - - -/* -Biquad Node -*/ -typedef struct -{ - ma_node_config nodeConfig; - ma_biquad_config biquad; -} ma_biquad_node_config; - -MA_API ma_biquad_node_config ma_biquad_node_config_init(ma_uint32 channels, float b0, float b1, float b2, float a0, float a1, float a2); - - -typedef struct -{ - ma_node_base baseNode; - ma_biquad biquad; -} ma_biquad_node; - -MA_API ma_result ma_biquad_node_init(ma_node_graph* pNodeGraph, const ma_biquad_node_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_biquad_node* pNode); -MA_API ma_result ma_biquad_node_reinit(const ma_biquad_config* pConfig, ma_biquad_node* pNode); -MA_API void ma_biquad_node_uninit(ma_biquad_node* pNode, const ma_allocation_callbacks* pAllocationCallbacks); - - -/* -Low Pass Filter Node -*/ -typedef struct -{ - ma_node_config nodeConfig; - ma_lpf_config lpf; -} ma_lpf_node_config; - -MA_API ma_lpf_node_config ma_lpf_node_config_init(ma_uint32 channels, ma_uint32 sampleRate, double cutoffFrequency, ma_uint32 order); - - -typedef struct -{ - ma_node_base baseNode; - ma_lpf lpf; -} ma_lpf_node; - -MA_API ma_result ma_lpf_node_init(ma_node_graph* pNodeGraph, const ma_lpf_node_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_lpf_node* pNode); -MA_API ma_result ma_lpf_node_reinit(const ma_lpf_config* pConfig, ma_lpf_node* pNode); -MA_API void ma_lpf_node_uninit(ma_lpf_node* pNode, const ma_allocation_callbacks* pAllocationCallbacks); - - -/* -High Pass Filter Node -*/ -typedef struct -{ - ma_node_config nodeConfig; - ma_hpf_config hpf; -} ma_hpf_node_config; - -MA_API ma_hpf_node_config ma_hpf_node_config_init(ma_uint32 channels, ma_uint32 sampleRate, double cutoffFrequency, ma_uint32 order); - - -typedef struct -{ - ma_node_base baseNode; - ma_hpf hpf; -} ma_hpf_node; - -MA_API ma_result ma_hpf_node_init(ma_node_graph* pNodeGraph, const ma_hpf_node_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_hpf_node* pNode); -MA_API ma_result ma_hpf_node_reinit(const ma_hpf_config* pConfig, ma_hpf_node* pNode); -MA_API void ma_hpf_node_uninit(ma_hpf_node* pNode, const ma_allocation_callbacks* pAllocationCallbacks); - - -/* -Band Pass Filter Node -*/ -typedef struct -{ - ma_node_config nodeConfig; - ma_bpf_config bpf; -} ma_bpf_node_config; - -MA_API ma_bpf_node_config ma_bpf_node_config_init(ma_uint32 channels, ma_uint32 sampleRate, double cutoffFrequency, ma_uint32 order); - - -typedef struct -{ - ma_node_base baseNode; - ma_bpf bpf; -} ma_bpf_node; - -MA_API ma_result ma_bpf_node_init(ma_node_graph* pNodeGraph, const ma_bpf_node_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_bpf_node* pNode); -MA_API ma_result ma_bpf_node_reinit(const ma_bpf_config* pConfig, ma_bpf_node* pNode); -MA_API void ma_bpf_node_uninit(ma_bpf_node* pNode, const ma_allocation_callbacks* pAllocationCallbacks); - - -/* -Notching Filter Node -*/ -typedef struct -{ - ma_node_config nodeConfig; - ma_notch_config notch; -} ma_notch_node_config; - -MA_API ma_notch_node_config ma_notch_node_config_init(ma_uint32 channels, ma_uint32 sampleRate, double q, double frequency); - - -typedef struct -{ - ma_node_base baseNode; - ma_notch2 notch; -} ma_notch_node; - -MA_API ma_result ma_notch_node_init(ma_node_graph* pNodeGraph, const ma_notch_node_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_notch_node* pNode); -MA_API ma_result ma_notch_node_reinit(const ma_notch_config* pConfig, ma_notch_node* pNode); -MA_API void ma_notch_node_uninit(ma_notch_node* pNode, const ma_allocation_callbacks* pAllocationCallbacks); - - -/* -Peaking Filter Node -*/ -typedef struct -{ - ma_node_config nodeConfig; - ma_peak_config peak; -} ma_peak_node_config; - -MA_API ma_peak_node_config ma_peak_node_config_init(ma_uint32 channels, ma_uint32 sampleRate, double gainDB, double q, double frequency); - - -typedef struct -{ - ma_node_base baseNode; - ma_peak2 peak; -} ma_peak_node; - -MA_API ma_result ma_peak_node_init(ma_node_graph* pNodeGraph, const ma_peak_node_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_peak_node* pNode); -MA_API ma_result ma_peak_node_reinit(const ma_peak_config* pConfig, ma_peak_node* pNode); -MA_API void ma_peak_node_uninit(ma_peak_node* pNode, const ma_allocation_callbacks* pAllocationCallbacks); - - -/* -Low Shelf Filter Node -*/ -typedef struct -{ - ma_node_config nodeConfig; - ma_loshelf_config loshelf; -} ma_loshelf_node_config; - -MA_API ma_loshelf_node_config ma_loshelf_node_config_init(ma_uint32 channels, ma_uint32 sampleRate, double gainDB, double q, double frequency); - - -typedef struct -{ - ma_node_base baseNode; - ma_loshelf2 loshelf; -} ma_loshelf_node; - -MA_API ma_result ma_loshelf_node_init(ma_node_graph* pNodeGraph, const ma_loshelf_node_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_loshelf_node* pNode); -MA_API ma_result ma_loshelf_node_reinit(const ma_loshelf_config* pConfig, ma_loshelf_node* pNode); -MA_API void ma_loshelf_node_uninit(ma_loshelf_node* pNode, const ma_allocation_callbacks* pAllocationCallbacks); - - -/* -High Shelf Filter Node -*/ -typedef struct -{ - ma_node_config nodeConfig; - ma_hishelf_config hishelf; -} ma_hishelf_node_config; - -MA_API ma_hishelf_node_config ma_hishelf_node_config_init(ma_uint32 channels, ma_uint32 sampleRate, double gainDB, double q, double frequency); - - -typedef struct -{ - ma_node_base baseNode; - ma_hishelf2 hishelf; -} ma_hishelf_node; - -MA_API ma_result ma_hishelf_node_init(ma_node_graph* pNodeGraph, const ma_hishelf_node_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_hishelf_node* pNode); -MA_API ma_result ma_hishelf_node_reinit(const ma_hishelf_config* pConfig, ma_hishelf_node* pNode); -MA_API void ma_hishelf_node_uninit(ma_hishelf_node* pNode, const ma_allocation_callbacks* pAllocationCallbacks); - - -typedef struct -{ - ma_node_config nodeConfig; - ma_delay_config delay; -} ma_delay_node_config; - -MA_API ma_delay_node_config ma_delay_node_config_init(ma_uint32 channels, ma_uint32 sampleRate, ma_uint32 delayInFrames, float decay); - - -typedef struct -{ - ma_node_base baseNode; - ma_delay delay; -} ma_delay_node; - -MA_API ma_result ma_delay_node_init(ma_node_graph* pNodeGraph, const ma_delay_node_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_delay_node* pDelayNode); -MA_API void ma_delay_node_uninit(ma_delay_node* pDelayNode, const ma_allocation_callbacks* pAllocationCallbacks); -MA_API void ma_delay_node_set_wet(ma_delay_node* pDelayNode, float value); -MA_API float ma_delay_node_get_wet(const ma_delay_node* pDelayNode); -MA_API void ma_delay_node_set_dry(ma_delay_node* pDelayNode, float value); -MA_API float ma_delay_node_get_dry(const ma_delay_node* pDelayNode); -MA_API void ma_delay_node_set_decay(ma_delay_node* pDelayNode, float value); -MA_API float ma_delay_node_get_decay(const ma_delay_node* pDelayNode); -#endif /* MA_NO_NODE_GRAPH */ - - -/* SECTION: miniaudio_engine.h */ -/************************************************************************************************************************************************************ - -Engine - -************************************************************************************************************************************************************/ -#if !defined(MA_NO_ENGINE) && !defined(MA_NO_NODE_GRAPH) -typedef struct ma_engine ma_engine; -typedef struct ma_sound ma_sound; - - -/* Sound flags. */ -typedef enum -{ - /* Resource manager flags. */ - MA_SOUND_FLAG_STREAM = 0x00000001, /* MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_STREAM */ - MA_SOUND_FLAG_DECODE = 0x00000002, /* MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_DECODE */ - MA_SOUND_FLAG_ASYNC = 0x00000004, /* MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_ASYNC */ - MA_SOUND_FLAG_WAIT_INIT = 0x00000008, /* MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_WAIT_INIT */ - MA_SOUND_FLAG_UNKNOWN_LENGTH = 0x00000010, /* MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_UNKNOWN_LENGTH */ - MA_SOUND_FLAG_LOOPING = 0x00000020, /* MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_LOOPING */ - - /* ma_sound specific flags. */ - MA_SOUND_FLAG_NO_DEFAULT_ATTACHMENT = 0x00001000, /* Do not attach to the endpoint by default. Useful for when setting up nodes in a complex graph system. */ - MA_SOUND_FLAG_NO_PITCH = 0x00002000, /* Disable pitch shifting with ma_sound_set_pitch() and ma_sound_group_set_pitch(). This is an optimization. */ - MA_SOUND_FLAG_NO_SPATIALIZATION = 0x00004000 /* Disable spatialization. */ -} ma_sound_flags; - -#ifndef MA_ENGINE_MAX_LISTENERS -#define MA_ENGINE_MAX_LISTENERS 4 -#endif - -#define MA_LISTENER_INDEX_CLOSEST ((ma_uint8)-1) - -typedef enum -{ - ma_engine_node_type_sound, - ma_engine_node_type_group -} ma_engine_node_type; - -typedef struct -{ - ma_engine* pEngine; - ma_engine_node_type type; - ma_uint32 channelsIn; - ma_uint32 channelsOut; - ma_uint32 sampleRate; /* Only used when the type is set to ma_engine_node_type_sound. */ - ma_uint32 volumeSmoothTimeInPCMFrames; /* The number of frames to smooth over volume changes. Defaults to 0 in which case no smoothing is used. */ - ma_mono_expansion_mode monoExpansionMode; - ma_bool8 isPitchDisabled; /* Pitching can be explicitly disabled with MA_SOUND_FLAG_NO_PITCH to optimize processing. */ - ma_bool8 isSpatializationDisabled; /* Spatialization can be explicitly disabled with MA_SOUND_FLAG_NO_SPATIALIZATION. */ - ma_uint8 pinnedListenerIndex; /* The index of the listener this node should always use for spatialization. If set to MA_LISTENER_INDEX_CLOSEST the engine will use the closest listener. */ -} ma_engine_node_config; - -MA_API ma_engine_node_config ma_engine_node_config_init(ma_engine* pEngine, ma_engine_node_type type, ma_uint32 flags); - - -/* Base node object for both ma_sound and ma_sound_group. */ -typedef struct -{ - ma_node_base baseNode; /* Must be the first member for compatibility with the ma_node API. */ - ma_engine* pEngine; /* A pointer to the engine. Set based on the value from the config. */ - ma_uint32 sampleRate; /* The sample rate of the input data. For sounds backed by a data source, this will be the data source's sample rate. Otherwise it'll be the engine's sample rate. */ - ma_uint32 volumeSmoothTimeInPCMFrames; - ma_mono_expansion_mode monoExpansionMode; - ma_fader fader; - ma_linear_resampler resampler; /* For pitch shift. */ - ma_spatializer spatializer; - ma_panner panner; - ma_gainer volumeGainer; /* This will only be used if volumeSmoothTimeInPCMFrames is > 0. */ - ma_atomic_float volume; /* Defaults to 1. */ - MA_ATOMIC(4, float) pitch; - float oldPitch; /* For determining whether or not the resampler needs to be updated to reflect the new pitch. The resampler will be updated on the mixing thread. */ - float oldDopplerPitch; /* For determining whether or not the resampler needs to be updated to take a new doppler pitch into account. */ - MA_ATOMIC(4, ma_bool32) isPitchDisabled; /* When set to true, pitching will be disabled which will allow the resampler to be bypassed to save some computation. */ - MA_ATOMIC(4, ma_bool32) isSpatializationDisabled; /* Set to false by default. When set to false, will not have spatialisation applied. */ - MA_ATOMIC(4, ma_uint32) pinnedListenerIndex; /* The index of the listener this node should always use for spatialization. If set to MA_LISTENER_INDEX_CLOSEST the engine will use the closest listener. */ - - /* When setting a fade, it's not done immediately in ma_sound_set_fade(). It's deferred to the audio thread which means we need to store the settings here. */ - struct - { - ma_atomic_float volumeBeg; - ma_atomic_float volumeEnd; - ma_atomic_uint64 fadeLengthInFrames; /* <-- Defaults to (~(ma_uint64)0) which is used to indicate that no fade should be applied. */ - ma_atomic_uint64 absoluteGlobalTimeInFrames; /* <-- The time to start the fade. */ - } fadeSettings; - - /* Memory management. */ - ma_bool8 _ownsHeap; - void* _pHeap; -} ma_engine_node; - -MA_API ma_result ma_engine_node_get_heap_size(const ma_engine_node_config* pConfig, size_t* pHeapSizeInBytes); -MA_API ma_result ma_engine_node_init_preallocated(const ma_engine_node_config* pConfig, void* pHeap, ma_engine_node* pEngineNode); -MA_API ma_result ma_engine_node_init(const ma_engine_node_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_engine_node* pEngineNode); -MA_API void ma_engine_node_uninit(ma_engine_node* pEngineNode, const ma_allocation_callbacks* pAllocationCallbacks); - - -#define MA_SOUND_SOURCE_CHANNEL_COUNT 0xFFFFFFFF - -/* Callback for when a sound reaches the end. */ -typedef void (* ma_sound_end_proc)(void* pUserData, ma_sound* pSound); - -typedef struct -{ - const char* pFilePath; /* Set this to load from the resource manager. */ - const wchar_t* pFilePathW; /* Set this to load from the resource manager. */ - ma_data_source* pDataSource; /* Set this to load from an existing data source. */ - ma_node* pInitialAttachment; /* If set, the sound will be attached to an input of this node. This can be set to a ma_sound. If set to NULL, the sound will be attached directly to the endpoint unless MA_SOUND_FLAG_NO_DEFAULT_ATTACHMENT is set in `flags`. */ - ma_uint32 initialAttachmentInputBusIndex; /* The index of the input bus of pInitialAttachment to attach the sound to. */ - ma_uint32 channelsIn; /* Ignored if using a data source as input (the data source's channel count will be used always). Otherwise, setting to 0 will cause the engine's channel count to be used. */ - ma_uint32 channelsOut; /* Set this to 0 (default) to use the engine's channel count. Set to MA_SOUND_SOURCE_CHANNEL_COUNT to use the data source's channel count (only used if using a data source as input). */ - ma_mono_expansion_mode monoExpansionMode; /* Controls how the mono channel should be expanded to other channels when spatialization is disabled on a sound. */ - ma_uint32 flags; /* A combination of MA_SOUND_FLAG_* flags. */ - ma_uint32 volumeSmoothTimeInPCMFrames; /* The number of frames to smooth over volume changes. Defaults to 0 in which case no smoothing is used. */ - ma_uint64 initialSeekPointInPCMFrames; /* Initializes the sound such that it's seeked to this location by default. */ - ma_uint64 rangeBegInPCMFrames; - ma_uint64 rangeEndInPCMFrames; - ma_uint64 loopPointBegInPCMFrames; - ma_uint64 loopPointEndInPCMFrames; - ma_sound_end_proc endCallback; /* Fired when the sound reaches the end. Will be fired from the audio thread. Do not restart, uninitialize or otherwise change the state of the sound from here. Instead fire an event or set a variable to indicate to a different thread to change the start of the sound. Will not be fired in response to a scheduled stop with ma_sound_set_stop_time_*(). */ - void* pEndCallbackUserData; -#ifndef MA_NO_RESOURCE_MANAGER - ma_resource_manager_pipeline_notifications initNotifications; -#endif - ma_fence* pDoneFence; /* Deprecated. Use initNotifications instead. Released when the resource manager has finished decoding the entire sound. Not used with streams. */ - ma_bool32 isLooping; /* Deprecated. Use the MA_SOUND_FLAG_LOOPING flag in `flags` instead. */ -} ma_sound_config; - -MA_API ma_sound_config ma_sound_config_init(void); /* Deprecated. Will be removed in version 0.12. Use ma_sound_config_2() instead. */ -MA_API ma_sound_config ma_sound_config_init_2(ma_engine* pEngine); /* Will be renamed to ma_sound_config_init() in version 0.12. */ - -struct ma_sound -{ - ma_engine_node engineNode; /* Must be the first member for compatibility with the ma_node API. */ - ma_data_source* pDataSource; - MA_ATOMIC(8, ma_uint64) seekTarget; /* The PCM frame index to seek to in the mixing thread. Set to (~(ma_uint64)0) to not perform any seeking. */ - MA_ATOMIC(4, ma_bool32) atEnd; - ma_sound_end_proc endCallback; - void* pEndCallbackUserData; - ma_bool8 ownsDataSource; - - /* - We're declaring a resource manager data source object here to save us a malloc when loading a - sound via the resource manager, which I *think* will be the most common scenario. - */ -#ifndef MA_NO_RESOURCE_MANAGER - ma_resource_manager_data_source* pResourceManagerDataSource; -#endif -}; - -/* Structure specifically for sounds played with ma_engine_play_sound(). Making this a separate structure to reduce overhead. */ -typedef struct ma_sound_inlined ma_sound_inlined; -struct ma_sound_inlined -{ - ma_sound sound; - ma_sound_inlined* pNext; - ma_sound_inlined* pPrev; -}; - -/* A sound group is just a sound. */ -typedef ma_sound_config ma_sound_group_config; -typedef ma_sound ma_sound_group; - -MA_API ma_sound_group_config ma_sound_group_config_init(void); /* Deprecated. Will be removed in version 0.12. Use ma_sound_config_2() instead. */ -MA_API ma_sound_group_config ma_sound_group_config_init_2(ma_engine* pEngine); /* Will be renamed to ma_sound_config_init() in version 0.12. */ - -typedef void (* ma_engine_process_proc)(void* pUserData, float* pFramesOut, ma_uint64 frameCount); - -typedef struct -{ -#if !defined(MA_NO_RESOURCE_MANAGER) - ma_resource_manager* pResourceManager; /* Can be null in which case a resource manager will be created for you. */ -#endif -#if !defined(MA_NO_DEVICE_IO) - ma_context* pContext; - ma_device* pDevice; /* If set, the caller is responsible for calling ma_engine_data_callback() in the device's data callback. */ - ma_device_id* pPlaybackDeviceID; /* The ID of the playback device to use with the default listener. */ - ma_device_data_proc dataCallback; /* Can be null. Can be used to provide a custom device data callback. */ - ma_device_notification_proc notificationCallback; -#endif - ma_log* pLog; /* When set to NULL, will use the context's log. */ - ma_uint32 listenerCount; /* Must be between 1 and MA_ENGINE_MAX_LISTENERS. */ - ma_uint32 channels; /* The number of channels to use when mixing and spatializing. When set to 0, will use the native channel count of the device. */ - ma_uint32 sampleRate; /* The sample rate. When set to 0 will use the native channel count of the device. */ - ma_uint32 periodSizeInFrames; /* If set to something other than 0, updates will always be exactly this size. The underlying device may be a different size, but from the perspective of the mixer that won't matter.*/ - ma_uint32 periodSizeInMilliseconds; /* Used if periodSizeInFrames is unset. */ - ma_uint32 gainSmoothTimeInFrames; /* The number of frames to interpolate the gain of spatialized sounds across. If set to 0, will use gainSmoothTimeInMilliseconds. */ - ma_uint32 gainSmoothTimeInMilliseconds; /* When set to 0, gainSmoothTimeInFrames will be used. If both are set to 0, a default value will be used. */ - ma_uint32 defaultVolumeSmoothTimeInPCMFrames; /* Defaults to 0. Controls the default amount of smoothing to apply to volume changes to sounds. High values means more smoothing at the expense of high latency (will take longer to reach the new volume). */ - ma_uint32 preMixStackSizeInBytes; /* A stack is used for internal processing in the node graph. This allows you to configure the size of this stack. Smaller values will reduce the maximum depth of your node graph. You should rarely need to modify this. */ - ma_allocation_callbacks allocationCallbacks; - ma_bool32 noAutoStart; /* When set to true, requires an explicit call to ma_engine_start(). This is false by default, meaning the engine will be started automatically in ma_engine_init(). */ - ma_bool32 noDevice; /* When set to true, don't create a default device. ma_engine_read_pcm_frames() can be called manually to read data. */ - ma_mono_expansion_mode monoExpansionMode; /* Controls how the mono channel should be expanded to other channels when spatialization is disabled on a sound. */ - ma_vfs* pResourceManagerVFS; /* A pointer to a pre-allocated VFS object to use with the resource manager. This is ignored if pResourceManager is not NULL. */ - ma_engine_process_proc onProcess; /* Fired at the end of each call to ma_engine_read_pcm_frames(). For engine's that manage their own internal device (the default configuration), this will be fired from the audio thread, and you do not need to call ma_engine_read_pcm_frames() manually in order to trigger this. */ - void* pProcessUserData; /* User data that's passed into onProcess. */ -} ma_engine_config; - -MA_API ma_engine_config ma_engine_config_init(void); - - -struct ma_engine -{ - ma_node_graph nodeGraph; /* An engine is a node graph. It should be able to be plugged into any ma_node_graph API (with a cast) which means this must be the first member of this struct. */ -#if !defined(MA_NO_RESOURCE_MANAGER) - ma_resource_manager* pResourceManager; -#endif -#if !defined(MA_NO_DEVICE_IO) - ma_device* pDevice; /* Optionally set via the config, otherwise allocated by the engine in ma_engine_init(). */ -#endif - ma_log* pLog; - ma_uint32 sampleRate; - ma_uint32 listenerCount; - ma_spatializer_listener listeners[MA_ENGINE_MAX_LISTENERS]; - ma_allocation_callbacks allocationCallbacks; - ma_bool8 ownsResourceManager; - ma_bool8 ownsDevice; - ma_spinlock inlinedSoundLock; /* For synchronizing access to the inlined sound list. */ - ma_sound_inlined* pInlinedSoundHead; /* The first inlined sound. Inlined sounds are tracked in a linked list. */ - MA_ATOMIC(4, ma_uint32) inlinedSoundCount; /* The total number of allocated inlined sound objects. Used for debugging. */ - ma_uint32 gainSmoothTimeInFrames; /* The number of frames to interpolate the gain of spatialized sounds across. */ - ma_uint32 defaultVolumeSmoothTimeInPCMFrames; - ma_mono_expansion_mode monoExpansionMode; - ma_engine_process_proc onProcess; - void* pProcessUserData; -}; - -MA_API ma_result ma_engine_init(const ma_engine_config* pConfig, ma_engine* pEngine); -MA_API void ma_engine_uninit(ma_engine* pEngine); -MA_API ma_result ma_engine_read_pcm_frames(ma_engine* pEngine, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead); -MA_API ma_node_graph* ma_engine_get_node_graph(ma_engine* pEngine); -#if !defined(MA_NO_RESOURCE_MANAGER) -MA_API ma_resource_manager* ma_engine_get_resource_manager(ma_engine* pEngine); -#endif -MA_API ma_device* ma_engine_get_device(ma_engine* pEngine); -MA_API ma_log* ma_engine_get_log(ma_engine* pEngine); -MA_API ma_node* ma_engine_get_endpoint(ma_engine* pEngine); -MA_API ma_uint64 ma_engine_get_time_in_pcm_frames(const ma_engine* pEngine); -MA_API ma_uint64 ma_engine_get_time_in_milliseconds(const ma_engine* pEngine); -MA_API ma_result ma_engine_set_time_in_pcm_frames(ma_engine* pEngine, ma_uint64 globalTime); -MA_API ma_result ma_engine_set_time_in_milliseconds(ma_engine* pEngine, ma_uint64 globalTime); -MA_API ma_uint64 ma_engine_get_time(const ma_engine* pEngine); /* Deprecated. Use ma_engine_get_time_in_pcm_frames(). Will be removed in version 0.12. */ -MA_API ma_result ma_engine_set_time(ma_engine* pEngine, ma_uint64 globalTime); /* Deprecated. Use ma_engine_set_time_in_pcm_frames(). Will be removed in version 0.12. */ -MA_API ma_uint32 ma_engine_get_channels(const ma_engine* pEngine); -MA_API ma_uint32 ma_engine_get_sample_rate(const ma_engine* pEngine); - -MA_API ma_result ma_engine_start(ma_engine* pEngine); -MA_API ma_result ma_engine_stop(ma_engine* pEngine); -MA_API ma_result ma_engine_set_volume(ma_engine* pEngine, float volume); -MA_API float ma_engine_get_volume(ma_engine* pEngine); -MA_API ma_result ma_engine_set_gain_db(ma_engine* pEngine, float gainDB); -MA_API float ma_engine_get_gain_db(ma_engine* pEngine); - -MA_API ma_uint32 ma_engine_get_listener_count(const ma_engine* pEngine); -MA_API ma_uint32 ma_engine_find_closest_listener(const ma_engine* pEngine, float absolutePosX, float absolutePosY, float absolutePosZ); -MA_API void ma_engine_listener_set_position(ma_engine* pEngine, ma_uint32 listenerIndex, float x, float y, float z); -MA_API ma_vec3f ma_engine_listener_get_position(const ma_engine* pEngine, ma_uint32 listenerIndex); -MA_API void ma_engine_listener_set_direction(ma_engine* pEngine, ma_uint32 listenerIndex, float x, float y, float z); -MA_API ma_vec3f ma_engine_listener_get_direction(const ma_engine* pEngine, ma_uint32 listenerIndex); -MA_API void ma_engine_listener_set_velocity(ma_engine* pEngine, ma_uint32 listenerIndex, float x, float y, float z); -MA_API ma_vec3f ma_engine_listener_get_velocity(const ma_engine* pEngine, ma_uint32 listenerIndex); -MA_API void ma_engine_listener_set_cone(ma_engine* pEngine, ma_uint32 listenerIndex, float innerAngleInRadians, float outerAngleInRadians, float outerGain); -MA_API void ma_engine_listener_get_cone(const ma_engine* pEngine, ma_uint32 listenerIndex, float* pInnerAngleInRadians, float* pOuterAngleInRadians, float* pOuterGain); -MA_API void ma_engine_listener_set_world_up(ma_engine* pEngine, ma_uint32 listenerIndex, float x, float y, float z); -MA_API ma_vec3f ma_engine_listener_get_world_up(const ma_engine* pEngine, ma_uint32 listenerIndex); -MA_API void ma_engine_listener_set_enabled(ma_engine* pEngine, ma_uint32 listenerIndex, ma_bool32 isEnabled); -MA_API ma_bool32 ma_engine_listener_is_enabled(const ma_engine* pEngine, ma_uint32 listenerIndex); - -#ifndef MA_NO_RESOURCE_MANAGER -MA_API ma_result ma_engine_play_sound_ex(ma_engine* pEngine, const char* pFilePath, ma_node* pNode, ma_uint32 nodeInputBusIndex); -MA_API ma_result ma_engine_play_sound(ma_engine* pEngine, const char* pFilePath, ma_sound_group* pGroup); /* Fire and forget. */ -#endif - -#ifndef MA_NO_RESOURCE_MANAGER -MA_API ma_result ma_sound_init_from_file(ma_engine* pEngine, const char* pFilePath, ma_uint32 flags, ma_sound_group* pGroup, ma_fence* pDoneFence, ma_sound* pSound); -MA_API ma_result ma_sound_init_from_file_w(ma_engine* pEngine, const wchar_t* pFilePath, ma_uint32 flags, ma_sound_group* pGroup, ma_fence* pDoneFence, ma_sound* pSound); -MA_API ma_result ma_sound_init_copy(ma_engine* pEngine, const ma_sound* pExistingSound, ma_uint32 flags, ma_sound_group* pGroup, ma_sound* pSound); -#endif -MA_API ma_result ma_sound_init_from_data_source(ma_engine* pEngine, ma_data_source* pDataSource, ma_uint32 flags, ma_sound_group* pGroup, ma_sound* pSound); -MA_API ma_result ma_sound_init_ex(ma_engine* pEngine, const ma_sound_config* pConfig, ma_sound* pSound); -MA_API void ma_sound_uninit(ma_sound* pSound); -MA_API ma_engine* ma_sound_get_engine(const ma_sound* pSound); -MA_API ma_data_source* ma_sound_get_data_source(const ma_sound* pSound); -MA_API ma_result ma_sound_start(ma_sound* pSound); -MA_API ma_result ma_sound_stop(ma_sound* pSound); -MA_API ma_result ma_sound_stop_with_fade_in_pcm_frames(ma_sound* pSound, ma_uint64 fadeLengthInFrames); /* Will overwrite any scheduled stop and fade. */ -MA_API ma_result ma_sound_stop_with_fade_in_milliseconds(ma_sound* pSound, ma_uint64 fadeLengthInFrames); /* Will overwrite any scheduled stop and fade. */ -MA_API void ma_sound_set_volume(ma_sound* pSound, float volume); -MA_API float ma_sound_get_volume(const ma_sound* pSound); -MA_API void ma_sound_set_pan(ma_sound* pSound, float pan); -MA_API float ma_sound_get_pan(const ma_sound* pSound); -MA_API void ma_sound_set_pan_mode(ma_sound* pSound, ma_pan_mode panMode); -MA_API ma_pan_mode ma_sound_get_pan_mode(const ma_sound* pSound); -MA_API void ma_sound_set_pitch(ma_sound* pSound, float pitch); -MA_API float ma_sound_get_pitch(const ma_sound* pSound); -MA_API void ma_sound_set_spatialization_enabled(ma_sound* pSound, ma_bool32 enabled); -MA_API ma_bool32 ma_sound_is_spatialization_enabled(const ma_sound* pSound); -MA_API void ma_sound_set_pinned_listener_index(ma_sound* pSound, ma_uint32 listenerIndex); -MA_API ma_uint32 ma_sound_get_pinned_listener_index(const ma_sound* pSound); -MA_API ma_uint32 ma_sound_get_listener_index(const ma_sound* pSound); -MA_API ma_vec3f ma_sound_get_direction_to_listener(const ma_sound* pSound); -MA_API void ma_sound_set_position(ma_sound* pSound, float x, float y, float z); -MA_API ma_vec3f ma_sound_get_position(const ma_sound* pSound); -MA_API void ma_sound_set_direction(ma_sound* pSound, float x, float y, float z); -MA_API ma_vec3f ma_sound_get_direction(const ma_sound* pSound); -MA_API void ma_sound_set_velocity(ma_sound* pSound, float x, float y, float z); -MA_API ma_vec3f ma_sound_get_velocity(const ma_sound* pSound); -MA_API void ma_sound_set_attenuation_model(ma_sound* pSound, ma_attenuation_model attenuationModel); -MA_API ma_attenuation_model ma_sound_get_attenuation_model(const ma_sound* pSound); -MA_API void ma_sound_set_positioning(ma_sound* pSound, ma_positioning positioning); -MA_API ma_positioning ma_sound_get_positioning(const ma_sound* pSound); -MA_API void ma_sound_set_rolloff(ma_sound* pSound, float rolloff); -MA_API float ma_sound_get_rolloff(const ma_sound* pSound); -MA_API void ma_sound_set_min_gain(ma_sound* pSound, float minGain); -MA_API float ma_sound_get_min_gain(const ma_sound* pSound); -MA_API void ma_sound_set_max_gain(ma_sound* pSound, float maxGain); -MA_API float ma_sound_get_max_gain(const ma_sound* pSound); -MA_API void ma_sound_set_min_distance(ma_sound* pSound, float minDistance); -MA_API float ma_sound_get_min_distance(const ma_sound* pSound); -MA_API void ma_sound_set_max_distance(ma_sound* pSound, float maxDistance); -MA_API float ma_sound_get_max_distance(const ma_sound* pSound); -MA_API void ma_sound_set_cone(ma_sound* pSound, float innerAngleInRadians, float outerAngleInRadians, float outerGain); -MA_API void ma_sound_get_cone(const ma_sound* pSound, float* pInnerAngleInRadians, float* pOuterAngleInRadians, float* pOuterGain); -MA_API void ma_sound_set_doppler_factor(ma_sound* pSound, float dopplerFactor); -MA_API float ma_sound_get_doppler_factor(const ma_sound* pSound); -MA_API void ma_sound_set_directional_attenuation_factor(ma_sound* pSound, float directionalAttenuationFactor); -MA_API float ma_sound_get_directional_attenuation_factor(const ma_sound* pSound); -MA_API void ma_sound_set_fade_in_pcm_frames(ma_sound* pSound, float volumeBeg, float volumeEnd, ma_uint64 fadeLengthInFrames); -MA_API void ma_sound_set_fade_in_milliseconds(ma_sound* pSound, float volumeBeg, float volumeEnd, ma_uint64 fadeLengthInMilliseconds); -MA_API void ma_sound_set_fade_start_in_pcm_frames(ma_sound* pSound, float volumeBeg, float volumeEnd, ma_uint64 fadeLengthInFrames, ma_uint64 absoluteGlobalTimeInFrames); -MA_API void ma_sound_set_fade_start_in_milliseconds(ma_sound* pSound, float volumeBeg, float volumeEnd, ma_uint64 fadeLengthInMilliseconds, ma_uint64 absoluteGlobalTimeInMilliseconds); -MA_API float ma_sound_get_current_fade_volume(const ma_sound* pSound); -MA_API void ma_sound_set_start_time_in_pcm_frames(ma_sound* pSound, ma_uint64 absoluteGlobalTimeInFrames); -MA_API void ma_sound_set_start_time_in_milliseconds(ma_sound* pSound, ma_uint64 absoluteGlobalTimeInMilliseconds); -MA_API void ma_sound_set_stop_time_in_pcm_frames(ma_sound* pSound, ma_uint64 absoluteGlobalTimeInFrames); -MA_API void ma_sound_set_stop_time_in_milliseconds(ma_sound* pSound, ma_uint64 absoluteGlobalTimeInMilliseconds); -MA_API void ma_sound_set_stop_time_with_fade_in_pcm_frames(ma_sound* pSound, ma_uint64 stopAbsoluteGlobalTimeInFrames, ma_uint64 fadeLengthInFrames); -MA_API void ma_sound_set_stop_time_with_fade_in_milliseconds(ma_sound* pSound, ma_uint64 stopAbsoluteGlobalTimeInMilliseconds, ma_uint64 fadeLengthInMilliseconds); -MA_API ma_bool32 ma_sound_is_playing(const ma_sound* pSound); -MA_API ma_uint64 ma_sound_get_time_in_pcm_frames(const ma_sound* pSound); -MA_API ma_uint64 ma_sound_get_time_in_milliseconds(const ma_sound* pSound); -MA_API void ma_sound_set_looping(ma_sound* pSound, ma_bool32 isLooping); -MA_API ma_bool32 ma_sound_is_looping(const ma_sound* pSound); -MA_API ma_bool32 ma_sound_at_end(const ma_sound* pSound); -MA_API ma_result ma_sound_seek_to_pcm_frame(ma_sound* pSound, ma_uint64 frameIndex); /* Just a wrapper around ma_data_source_seek_to_pcm_frame(). */ -MA_API ma_result ma_sound_seek_to_second(ma_sound* pSound, float seekPointInSeconds); /* Abstraction to ma_sound_seek_to_pcm_frame() */ -MA_API ma_result ma_sound_get_data_format(ma_sound* pSound, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate, ma_channel* pChannelMap, size_t channelMapCap); -MA_API ma_result ma_sound_get_cursor_in_pcm_frames(ma_sound* pSound, ma_uint64* pCursor); -MA_API ma_result ma_sound_get_length_in_pcm_frames(ma_sound* pSound, ma_uint64* pLength); -MA_API ma_result ma_sound_get_cursor_in_seconds(ma_sound* pSound, float* pCursor); -MA_API ma_result ma_sound_get_length_in_seconds(ma_sound* pSound, float* pLength); -MA_API ma_result ma_sound_set_end_callback(ma_sound* pSound, ma_sound_end_proc callback, void* pUserData); - -MA_API ma_result ma_sound_group_init(ma_engine* pEngine, ma_uint32 flags, ma_sound_group* pParentGroup, ma_sound_group* pGroup); -MA_API ma_result ma_sound_group_init_ex(ma_engine* pEngine, const ma_sound_group_config* pConfig, ma_sound_group* pGroup); -MA_API void ma_sound_group_uninit(ma_sound_group* pGroup); -MA_API ma_engine* ma_sound_group_get_engine(const ma_sound_group* pGroup); -MA_API ma_result ma_sound_group_start(ma_sound_group* pGroup); -MA_API ma_result ma_sound_group_stop(ma_sound_group* pGroup); -MA_API void ma_sound_group_set_volume(ma_sound_group* pGroup, float volume); -MA_API float ma_sound_group_get_volume(const ma_sound_group* pGroup); -MA_API void ma_sound_group_set_pan(ma_sound_group* pGroup, float pan); -MA_API float ma_sound_group_get_pan(const ma_sound_group* pGroup); -MA_API void ma_sound_group_set_pan_mode(ma_sound_group* pGroup, ma_pan_mode panMode); -MA_API ma_pan_mode ma_sound_group_get_pan_mode(const ma_sound_group* pGroup); -MA_API void ma_sound_group_set_pitch(ma_sound_group* pGroup, float pitch); -MA_API float ma_sound_group_get_pitch(const ma_sound_group* pGroup); -MA_API void ma_sound_group_set_spatialization_enabled(ma_sound_group* pGroup, ma_bool32 enabled); -MA_API ma_bool32 ma_sound_group_is_spatialization_enabled(const ma_sound_group* pGroup); -MA_API void ma_sound_group_set_pinned_listener_index(ma_sound_group* pGroup, ma_uint32 listenerIndex); -MA_API ma_uint32 ma_sound_group_get_pinned_listener_index(const ma_sound_group* pGroup); -MA_API ma_uint32 ma_sound_group_get_listener_index(const ma_sound_group* pGroup); -MA_API ma_vec3f ma_sound_group_get_direction_to_listener(const ma_sound_group* pGroup); -MA_API void ma_sound_group_set_position(ma_sound_group* pGroup, float x, float y, float z); -MA_API ma_vec3f ma_sound_group_get_position(const ma_sound_group* pGroup); -MA_API void ma_sound_group_set_direction(ma_sound_group* pGroup, float x, float y, float z); -MA_API ma_vec3f ma_sound_group_get_direction(const ma_sound_group* pGroup); -MA_API void ma_sound_group_set_velocity(ma_sound_group* pGroup, float x, float y, float z); -MA_API ma_vec3f ma_sound_group_get_velocity(const ma_sound_group* pGroup); -MA_API void ma_sound_group_set_attenuation_model(ma_sound_group* pGroup, ma_attenuation_model attenuationModel); -MA_API ma_attenuation_model ma_sound_group_get_attenuation_model(const ma_sound_group* pGroup); -MA_API void ma_sound_group_set_positioning(ma_sound_group* pGroup, ma_positioning positioning); -MA_API ma_positioning ma_sound_group_get_positioning(const ma_sound_group* pGroup); -MA_API void ma_sound_group_set_rolloff(ma_sound_group* pGroup, float rolloff); -MA_API float ma_sound_group_get_rolloff(const ma_sound_group* pGroup); -MA_API void ma_sound_group_set_min_gain(ma_sound_group* pGroup, float minGain); -MA_API float ma_sound_group_get_min_gain(const ma_sound_group* pGroup); -MA_API void ma_sound_group_set_max_gain(ma_sound_group* pGroup, float maxGain); -MA_API float ma_sound_group_get_max_gain(const ma_sound_group* pGroup); -MA_API void ma_sound_group_set_min_distance(ma_sound_group* pGroup, float minDistance); -MA_API float ma_sound_group_get_min_distance(const ma_sound_group* pGroup); -MA_API void ma_sound_group_set_max_distance(ma_sound_group* pGroup, float maxDistance); -MA_API float ma_sound_group_get_max_distance(const ma_sound_group* pGroup); -MA_API void ma_sound_group_set_cone(ma_sound_group* pGroup, float innerAngleInRadians, float outerAngleInRadians, float outerGain); -MA_API void ma_sound_group_get_cone(const ma_sound_group* pGroup, float* pInnerAngleInRadians, float* pOuterAngleInRadians, float* pOuterGain); -MA_API void ma_sound_group_set_doppler_factor(ma_sound_group* pGroup, float dopplerFactor); -MA_API float ma_sound_group_get_doppler_factor(const ma_sound_group* pGroup); -MA_API void ma_sound_group_set_directional_attenuation_factor(ma_sound_group* pGroup, float directionalAttenuationFactor); -MA_API float ma_sound_group_get_directional_attenuation_factor(const ma_sound_group* pGroup); -MA_API void ma_sound_group_set_fade_in_pcm_frames(ma_sound_group* pGroup, float volumeBeg, float volumeEnd, ma_uint64 fadeLengthInFrames); -MA_API void ma_sound_group_set_fade_in_milliseconds(ma_sound_group* pGroup, float volumeBeg, float volumeEnd, ma_uint64 fadeLengthInMilliseconds); -MA_API float ma_sound_group_get_current_fade_volume(ma_sound_group* pGroup); -MA_API void ma_sound_group_set_start_time_in_pcm_frames(ma_sound_group* pGroup, ma_uint64 absoluteGlobalTimeInFrames); -MA_API void ma_sound_group_set_start_time_in_milliseconds(ma_sound_group* pGroup, ma_uint64 absoluteGlobalTimeInMilliseconds); -MA_API void ma_sound_group_set_stop_time_in_pcm_frames(ma_sound_group* pGroup, ma_uint64 absoluteGlobalTimeInFrames); -MA_API void ma_sound_group_set_stop_time_in_milliseconds(ma_sound_group* pGroup, ma_uint64 absoluteGlobalTimeInMilliseconds); -MA_API ma_bool32 ma_sound_group_is_playing(const ma_sound_group* pGroup); -MA_API ma_uint64 ma_sound_group_get_time_in_pcm_frames(const ma_sound_group* pGroup); -#endif /* MA_NO_ENGINE */ -/* END SECTION: miniaudio_engine.h */ - -#ifdef __cplusplus -} -#endif -#endif /* miniaudio_h */ - - -/* -This is for preventing greying out of the implementation section. -*/ -#if defined(Q_CREATOR_RUN) || defined(__INTELLISENSE__) || defined(__CDT_PARSER__) -#define MINIAUDIO_IMPLEMENTATION -#endif - -/************************************************************************************************************************************************************ -************************************************************************************************************************************************************* - -IMPLEMENTATION - -************************************************************************************************************************************************************* -************************************************************************************************************************************************************/ -#if defined(MINIAUDIO_IMPLEMENTATION) || defined(MA_IMPLEMENTATION) -#ifndef miniaudio_c -#define miniaudio_c - -#include -#include /* For INT_MAX */ -#include /* sin(), etc. */ -#include /* For malloc(), free(), wcstombs(). */ -#include /* For memset() */ - -#include -#include -#if !defined(_MSC_VER) && !defined(__DMC__) - #include /* For strcasecmp(). */ - #include /* For wcslen(), wcsrtombs() */ -#endif -#ifdef _MSC_VER - #include /* For _controlfp_s constants */ -#endif - -#if defined(MA_WIN32) - #include - - /* - There's a possibility that WIN32_LEAN_AND_MEAN has been defined which will exclude some symbols - such as STGM_READ and CLSCTL_ALL. We need to check these and define them ourselves if they're - unavailable. - */ - #ifndef STGM_READ - #define STGM_READ 0x00000000L - #endif - #ifndef CLSCTX_ALL - #define CLSCTX_ALL 23 - #endif - - /* IUnknown is used by both the WASAPI and DirectSound backends. It easier to just declare our version here. */ - typedef struct ma_IUnknown ma_IUnknown; -#endif - -#if !defined(MA_WIN32) -#include -#include /* select() (used for ma_sleep()). */ -#include -#endif - -#ifdef MA_NX -#include /* For nanosleep() */ -#endif - -#include /* For fstat(), etc. */ - -#ifdef MA_EMSCRIPTEN -#include -#endif - - -/* Architecture Detection */ -#if !defined(MA_64BIT) && !defined(MA_32BIT) -#ifdef _WIN32 -#ifdef _WIN64 -#define MA_64BIT -#else -#define MA_32BIT -#endif -#endif -#endif - -#if !defined(MA_64BIT) && !defined(MA_32BIT) -#ifdef __GNUC__ -#ifdef __LP64__ -#define MA_64BIT -#else -#define MA_32BIT -#endif -#endif -#endif - -#if !defined(MA_64BIT) && !defined(MA_32BIT) -#include -#if INTPTR_MAX == INT64_MAX -#define MA_64BIT -#else -#define MA_32BIT -#endif -#endif - -#if defined(__arm__) || defined(_M_ARM) -#define MA_ARM32 -#endif -#if defined(__arm64) || defined(__arm64__) || defined(__aarch64__) || defined(_M_ARM64) -#define MA_ARM64 -#endif - -#if defined(__x86_64__) || defined(_M_X64) -#define MA_X64 -#elif defined(__i386) || defined(_M_IX86) -#define MA_X86 -#elif defined(MA_ARM32) || defined(MA_ARM64) -#define MA_ARM -#endif - -/* Intrinsics Support */ -#if (defined(MA_X64) || defined(MA_X86)) && !defined(__COSMOPOLITAN__) - #if defined(_MSC_VER) && !defined(__clang__) - /* MSVC. */ - #if _MSC_VER >= 1400 && !defined(MA_NO_SSE2) /* 2005 */ - #define MA_SUPPORT_SSE2 - #endif - /*#if _MSC_VER >= 1600 && !defined(MA_NO_AVX)*/ /* 2010 */ - /* #define MA_SUPPORT_AVX*/ - /*#endif*/ - #if _MSC_VER >= 1700 && !defined(MA_NO_AVX2) /* 2012 */ - #define MA_SUPPORT_AVX2 - #endif - #else - /* Assume GNUC-style. */ - #if defined(__SSE2__) && !defined(MA_NO_SSE2) - #define MA_SUPPORT_SSE2 - #endif - /*#if defined(__AVX__) && !defined(MA_NO_AVX)*/ - /* #define MA_SUPPORT_AVX*/ - /*#endif*/ - #if defined(__AVX2__) && !defined(MA_NO_AVX2) - #define MA_SUPPORT_AVX2 - #endif - #endif - - /* If at this point we still haven't determined compiler support for the intrinsics just fall back to __has_include. */ - #if !defined(__GNUC__) && !defined(__clang__) && defined(__has_include) - #if !defined(MA_SUPPORT_SSE2) && !defined(MA_NO_SSE2) && __has_include() - #define MA_SUPPORT_SSE2 - #endif - /*#if !defined(MA_SUPPORT_AVX) && !defined(MA_NO_AVX) && __has_include()*/ - /* #define MA_SUPPORT_AVX*/ - /*#endif*/ - #if !defined(MA_SUPPORT_AVX2) && !defined(MA_NO_AVX2) && __has_include() - #define MA_SUPPORT_AVX2 - #endif - #endif - - #if defined(MA_SUPPORT_AVX2) || defined(MA_SUPPORT_AVX) - #include - #elif defined(MA_SUPPORT_SSE2) - #include - #endif -#endif - -#if defined(MA_ARM) - #if !defined(MA_NO_NEON) && (defined(__ARM_NEON) || defined(__aarch64__) || defined(_M_ARM64)) - #define MA_SUPPORT_NEON - #include - #endif -#endif - -/* Begin globally disabled warnings. */ -#if defined(_MSC_VER) - #pragma warning(push) - #pragma warning(disable:4752) /* found Intel(R) Advanced Vector Extensions; consider using /arch:AVX */ - #pragma warning(disable:4049) /* compiler limit : terminating line number emission */ -#endif - -#if defined(MA_X64) || defined(MA_X86) - #if defined(_MSC_VER) && !defined(__clang__) - #if _MSC_VER >= 1400 - #include - static MA_INLINE void ma_cpuid(int info[4], int fid) - { - __cpuid(info, fid); - } - #else - #define MA_NO_CPUID - #endif - - #if _MSC_VER >= 1600 && (defined(_MSC_FULL_VER) && _MSC_FULL_VER >= 160040219) - static MA_INLINE unsigned __int64 ma_xgetbv(int reg) - { - return _xgetbv(reg); - } - #else - #define MA_NO_XGETBV - #endif - #elif (defined(__GNUC__) || defined(__clang__)) && !defined(MA_ANDROID) - static MA_INLINE void ma_cpuid(int info[4], int fid) - { - /* - It looks like the -fPIC option uses the ebx register which GCC complains about. We can work around this by just using a different register, the - specific register of which I'm letting the compiler decide on. The "k" prefix is used to specify a 32-bit register. The {...} syntax is for - supporting different assembly dialects. - - What's basically happening is that we're saving and restoring the ebx register manually. - */ - #if defined(MA_X86) && defined(__PIC__) - __asm__ __volatile__ ( - "xchg{l} {%%}ebx, %k1;" - "cpuid;" - "xchg{l} {%%}ebx, %k1;" - : "=a"(info[0]), "=&r"(info[1]), "=c"(info[2]), "=d"(info[3]) : "a"(fid), "c"(0) - ); - #else - __asm__ __volatile__ ( - "cpuid" : "=a"(info[0]), "=b"(info[1]), "=c"(info[2]), "=d"(info[3]) : "a"(fid), "c"(0) - ); - #endif - } - - static MA_INLINE ma_uint64 ma_xgetbv(int reg) - { - unsigned int hi; - unsigned int lo; - - __asm__ __volatile__ ( - "xgetbv" : "=a"(lo), "=d"(hi) : "c"(reg) - ); - - return ((ma_uint64)hi << 32) | (ma_uint64)lo; - } - #else - #define MA_NO_CPUID - #define MA_NO_XGETBV - #endif -#else - #define MA_NO_CPUID - #define MA_NO_XGETBV -#endif - -static MA_INLINE ma_bool32 ma_has_sse2(void) -{ -#if defined(MA_SUPPORT_SSE2) - #if (defined(MA_X64) || defined(MA_X86)) && !defined(MA_NO_SSE2) - #if defined(MA_X64) - return MA_TRUE; /* 64-bit targets always support SSE2. */ - #elif (defined(_M_IX86_FP) && _M_IX86_FP == 2) || defined(__SSE2__) - return MA_TRUE; /* If the compiler is allowed to freely generate SSE2 code we can assume support. */ - #else - #if defined(MA_NO_CPUID) - return MA_FALSE; - #else - int info[4]; - ma_cpuid(info, 1); - return (info[3] & (1 << 26)) != 0; - #endif - #endif - #else - return MA_FALSE; /* SSE2 is only supported on x86 and x64 architectures. */ - #endif -#else - return MA_FALSE; /* No compiler support. */ -#endif -} - -#if 0 -static MA_INLINE ma_bool32 ma_has_avx() -{ -#if defined(MA_SUPPORT_AVX) - #if (defined(MA_X64) || defined(MA_X86)) && !defined(MA_NO_AVX) - #if defined(_AVX_) || defined(__AVX__) - return MA_TRUE; /* If the compiler is allowed to freely generate AVX code we can assume support. */ - #else - /* AVX requires both CPU and OS support. */ - #if defined(MA_NO_CPUID) || defined(MA_NO_XGETBV) - return MA_FALSE; - #else - int info[4]; - ma_cpuid(info, 1); - if (((info[2] & (1 << 27)) != 0) && ((info[2] & (1 << 28)) != 0)) { - ma_uint64 xrc = ma_xgetbv(0); - if ((xrc & 0x06) == 0x06) { - return MA_TRUE; - } else { - return MA_FALSE; - } - } else { - return MA_FALSE; - } - #endif - #endif - #else - return MA_FALSE; /* AVX is only supported on x86 and x64 architectures. */ - #endif -#else - return MA_FALSE; /* No compiler support. */ -#endif -} -#endif - -static MA_INLINE ma_bool32 ma_has_avx2(void) -{ -#if defined(MA_SUPPORT_AVX2) - #if (defined(MA_X64) || defined(MA_X86)) && !defined(MA_NO_AVX2) - #if defined(_AVX2_) || defined(__AVX2__) - return MA_TRUE; /* If the compiler is allowed to freely generate AVX2 code we can assume support. */ - #else - /* AVX2 requires both CPU and OS support. */ - #if defined(MA_NO_CPUID) || defined(MA_NO_XGETBV) - return MA_FALSE; - #else - int info1[4]; - int info7[4]; - ma_cpuid(info1, 1); - ma_cpuid(info7, 7); - if (((info1[2] & (1 << 27)) != 0) && ((info7[1] & (1 << 5)) != 0)) { - ma_uint64 xrc = ma_xgetbv(0); - if ((xrc & 0x06) == 0x06) { - return MA_TRUE; - } else { - return MA_FALSE; - } - } else { - return MA_FALSE; - } - #endif - #endif - #else - return MA_FALSE; /* AVX2 is only supported on x86 and x64 architectures. */ - #endif -#else - return MA_FALSE; /* No compiler support. */ -#endif -} - -static MA_INLINE ma_bool32 ma_has_neon(void) -{ -#if defined(MA_SUPPORT_NEON) - #if defined(MA_ARM) && !defined(MA_NO_NEON) - #if (defined(__ARM_NEON) || defined(__aarch64__) || defined(_M_ARM64)) - return MA_TRUE; /* If the compiler is allowed to freely generate NEON code we can assume support. */ - #else - /* TODO: Runtime check. */ - return MA_FALSE; - #endif - #else - return MA_FALSE; /* NEON is only supported on ARM architectures. */ - #endif -#else - return MA_FALSE; /* No compiler support. */ -#endif -} - -#if defined(__has_builtin) - #define MA_COMPILER_HAS_BUILTIN(x) __has_builtin(x) -#else - #define MA_COMPILER_HAS_BUILTIN(x) 0 -#endif - -#ifndef MA_ASSUME - #if MA_COMPILER_HAS_BUILTIN(__builtin_assume) - #define MA_ASSUME(x) __builtin_assume(x) - #elif MA_COMPILER_HAS_BUILTIN(__builtin_unreachable) - #define MA_ASSUME(x) do { if (!(x)) __builtin_unreachable(); } while (0) - #elif defined(_MSC_VER) - #define MA_ASSUME(x) __assume(x) - #else - #define MA_ASSUME(x) (void)(x) - #endif -#endif - -#ifndef MA_RESTRICT - #if defined(__clang__) || defined(__GNUC__) || defined(_MSC_VER) - #define MA_RESTRICT __restrict - #else - #define MA_RESTRICT - #endif -#endif - -#if defined(_MSC_VER) && _MSC_VER >= 1400 - #define MA_HAS_BYTESWAP16_INTRINSIC - #define MA_HAS_BYTESWAP32_INTRINSIC - #define MA_HAS_BYTESWAP64_INTRINSIC -#elif defined(__clang__) - #if MA_COMPILER_HAS_BUILTIN(__builtin_bswap16) - #define MA_HAS_BYTESWAP16_INTRINSIC - #endif - #if MA_COMPILER_HAS_BUILTIN(__builtin_bswap32) - #define MA_HAS_BYTESWAP32_INTRINSIC - #endif - #if MA_COMPILER_HAS_BUILTIN(__builtin_bswap64) - #define MA_HAS_BYTESWAP64_INTRINSIC - #endif -#elif defined(__GNUC__) - #if ((__GNUC__ > 4) || (__GNUC__ == 4 && __GNUC_MINOR__ >= 3)) - #define MA_HAS_BYTESWAP32_INTRINSIC - #define MA_HAS_BYTESWAP64_INTRINSIC - #endif - #if ((__GNUC__ > 4) || (__GNUC__ == 4 && __GNUC_MINOR__ >= 8)) - #define MA_HAS_BYTESWAP16_INTRINSIC - #endif -#endif - - -static MA_INLINE ma_bool32 ma_is_little_endian(void) -{ -#if defined(MA_X86) || defined(MA_X64) - return MA_TRUE; -#else - int n = 1; - return (*(char*)&n) == 1; -#endif -} - -static MA_INLINE ma_bool32 ma_is_big_endian(void) -{ - return !ma_is_little_endian(); -} - - -static MA_INLINE ma_uint32 ma_swap_endian_uint32(ma_uint32 n) -{ -#ifdef MA_HAS_BYTESWAP32_INTRINSIC - #if defined(_MSC_VER) - return _byteswap_ulong(n); - #elif defined(__GNUC__) || defined(__clang__) - #if defined(MA_ARM) && (defined(__ARM_ARCH) && __ARM_ARCH >= 6) && !defined(MA_64BIT) /* <-- 64-bit inline assembly has not been tested, so disabling for now. */ - /* Inline assembly optimized implementation for ARM. In my testing, GCC does not generate optimized code with __builtin_bswap32(). */ - ma_uint32 r; - __asm__ __volatile__ ( - #if defined(MA_64BIT) - "rev %w[out], %w[in]" : [out]"=r"(r) : [in]"r"(n) /* <-- This is untested. If someone in the community could test this, that would be appreciated! */ - #else - "rev %[out], %[in]" : [out]"=r"(r) : [in]"r"(n) - #endif - ); - return r; - #else - return __builtin_bswap32(n); - #endif - #else - #error "This compiler does not support the byte swap intrinsic." - #endif -#else - return ((n & 0xFF000000) >> 24) | - ((n & 0x00FF0000) >> 8) | - ((n & 0x0000FF00) << 8) | - ((n & 0x000000FF) << 24); -#endif -} - - -#if !defined(MA_EMSCRIPTEN) -#ifdef MA_WIN32 -static void ma_sleep__win32(ma_uint32 milliseconds) -{ - Sleep((DWORD)milliseconds); -} -#endif -#ifdef MA_POSIX -static void ma_sleep__posix(ma_uint32 milliseconds) -{ -#ifdef MA_EMSCRIPTEN - (void)milliseconds; - MA_ASSERT(MA_FALSE); /* The Emscripten build should never sleep. */ -#else - #if (defined(_POSIX_C_SOURCE) && _POSIX_C_SOURCE >= 199309L) || defined(MA_NX) - struct timespec ts; - ts.tv_sec = milliseconds / 1000; - ts.tv_nsec = milliseconds % 1000 * 1000000; - nanosleep(&ts, NULL); - #else - struct timeval tv; - tv.tv_sec = milliseconds / 1000; - tv.tv_usec = milliseconds % 1000 * 1000; - select(0, NULL, NULL, NULL, &tv); - #endif -#endif -} -#endif - -static MA_INLINE void ma_sleep(ma_uint32 milliseconds) -{ -#ifdef MA_WIN32 - ma_sleep__win32(milliseconds); -#endif -#ifdef MA_POSIX - ma_sleep__posix(milliseconds); -#endif -} -#endif - -static MA_INLINE void ma_yield(void) -{ -#if defined(__i386) || defined(_M_IX86) || defined(__x86_64__) || defined(_M_X64) - /* x86/x64 */ - #if (defined(_MSC_VER) || defined(__WATCOMC__) || defined(__DMC__)) && !defined(__clang__) - #if _MSC_VER >= 1400 - _mm_pause(); - #else - #if defined(__DMC__) - /* Digital Mars does not recognize the PAUSE opcode. Fall back to NOP. */ - __asm nop; - #else - __asm pause; - #endif - #endif - #else - __asm__ __volatile__ ("pause"); - #endif -#elif (defined(__arm__) && defined(__ARM_ARCH) && __ARM_ARCH >= 7) || defined(_M_ARM64) || (defined(_M_ARM) && _M_ARM >= 7) || defined(__ARM_ARCH_6K__) || defined(__ARM_ARCH_6T2__) - /* ARM */ - #if defined(_MSC_VER) - /* Apparently there is a __yield() intrinsic that's compatible with ARM, but I cannot find documentation for it nor can I find where it's declared. */ - __yield(); - #else - __asm__ __volatile__ ("yield"); /* ARMv6K/ARMv6T2 and above. */ - #endif -#else - /* Unknown or unsupported architecture. No-op. */ -#endif -} - - -#define MA_MM_DENORMALS_ZERO_MASK 0x0040 -#define MA_MM_FLUSH_ZERO_MASK 0x8000 - -static MA_INLINE unsigned int ma_disable_denormals(void) -{ - unsigned int prevState; - - #if defined(_MSC_VER) - { - /* - Older versions of Visual Studio don't support the "safe" versions of _controlfp_s(). I don't - know which version of Visual Studio first added support for _controlfp_s(), but I do know - that VC6 lacks support. _MSC_VER = 1200 is VC6, but if you get compilation errors on older - versions of Visual Studio, let me know and I'll make the necessary adjustment. - */ - #if _MSC_VER <= 1200 - { - prevState = _statusfp(); - _controlfp(prevState | _DN_FLUSH, _MCW_DN); - } - #else - { - unsigned int unused; - _controlfp_s(&prevState, 0, 0); - _controlfp_s(&unused, prevState | _DN_FLUSH, _MCW_DN); - } - #endif - } - #elif defined(MA_X86) || defined(MA_X64) - { - #if defined(__SSE2__) && !(defined(__TINYC__) || defined(__WATCOMC__) || defined(__COSMOPOLITAN__)) /* <-- Add compilers that lack support for _mm_getcsr() and _mm_setcsr() to this list. */ - { - prevState = _mm_getcsr(); - _mm_setcsr(prevState | MA_MM_DENORMALS_ZERO_MASK | MA_MM_FLUSH_ZERO_MASK); - } - #else - { - /* x88/64, but no support for _mm_getcsr()/_mm_setcsr(). May need to fall back to inlined assembly here. */ - prevState = 0; - } - #endif - } - #else - { - /* Unknown or unsupported architecture. No-op. */ - prevState = 0; - } - #endif - - return prevState; -} - -static MA_INLINE void ma_restore_denormals(unsigned int prevState) -{ - #if defined(_MSC_VER) - { - /* Older versions of Visual Studio do not support _controlfp_s(). See ma_disable_denormals(). */ - #if _MSC_VER <= 1200 - { - _controlfp(prevState, _MCW_DN); - } - #else - { - unsigned int unused; - _controlfp_s(&unused, prevState, _MCW_DN); - } - #endif - } - #elif defined(MA_X86) || defined(MA_X64) - { - #if defined(__SSE2__) && !(defined(__TINYC__) || defined(__WATCOMC__) || defined(__COSMOPOLITAN__)) /* <-- Add compilers that lack support for _mm_getcsr() and _mm_setcsr() to this list. */ - { - _mm_setcsr(prevState); - } - #else - { - /* x88/64, but no support for _mm_getcsr()/_mm_setcsr(). May need to fall back to inlined assembly here. */ - (void)prevState; - } - #endif - } - #else - { - /* Unknown or unsupported architecture. No-op. */ - (void)prevState; - } - #endif -} - - -#ifdef MA_ANDROID -#include - -int ma_android_sdk_version() -{ - char sdkVersion[PROP_VALUE_MAX + 1] = {0, }; - if (__system_property_get("ro.build.version.sdk", sdkVersion)) { - return atoi(sdkVersion); - } - - return 0; -} -#endif - - -#ifndef MA_COINIT_VALUE -#define MA_COINIT_VALUE 0 /* 0 = COINIT_MULTITHREADED */ -#endif - - -#ifndef MA_FLT_MAX - #ifdef FLT_MAX - #define MA_FLT_MAX FLT_MAX - #else - #define MA_FLT_MAX 3.402823466e+38F - #endif -#endif - - -#ifndef MA_PI -#define MA_PI 3.14159265358979323846264f -#endif -#ifndef MA_PI_D -#define MA_PI_D 3.14159265358979323846264 -#endif -#ifndef MA_TAU -#define MA_TAU 6.28318530717958647693f -#endif -#ifndef MA_TAU_D -#define MA_TAU_D 6.28318530717958647693 -#endif - - -/* The default format when ma_format_unknown (0) is requested when initializing a device. */ -#ifndef MA_DEFAULT_FORMAT -#define MA_DEFAULT_FORMAT ma_format_f32 -#endif - -/* The default channel count to use when 0 is used when initializing a device. */ -#ifndef MA_DEFAULT_CHANNELS -#define MA_DEFAULT_CHANNELS 2 -#endif - -/* The default sample rate to use when 0 is used when initializing a device. */ -#ifndef MA_DEFAULT_SAMPLE_RATE -#define MA_DEFAULT_SAMPLE_RATE 48000 -#endif - -/* Default periods when none is specified in ma_device_init(). More periods means more work on the CPU. */ -#ifndef MA_DEFAULT_PERIODS -#define MA_DEFAULT_PERIODS 3 -#endif - -/* The default period size in milliseconds for low latency mode. */ -#ifndef MA_DEFAULT_PERIOD_SIZE_IN_MILLISECONDS_LOW_LATENCY -#define MA_DEFAULT_PERIOD_SIZE_IN_MILLISECONDS_LOW_LATENCY 10 -#endif - -/* The default buffer size in milliseconds for conservative mode. */ -#ifndef MA_DEFAULT_PERIOD_SIZE_IN_MILLISECONDS_CONSERVATIVE -#define MA_DEFAULT_PERIOD_SIZE_IN_MILLISECONDS_CONSERVATIVE 100 -#endif - -/* The default LPF filter order for linear resampling. Note that this is clamped to MA_MAX_FILTER_ORDER. */ -#ifndef MA_DEFAULT_RESAMPLER_LPF_ORDER - #if MA_MAX_FILTER_ORDER >= 4 - #define MA_DEFAULT_RESAMPLER_LPF_ORDER 4 - #else - #define MA_DEFAULT_RESAMPLER_LPF_ORDER MA_MAX_FILTER_ORDER - #endif -#endif - - -#if defined(__clang__) || (defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6))) - #pragma GCC diagnostic push - #pragma GCC diagnostic ignored "-Wunused-variable" -#endif - -/* Standard sample rates, in order of priority. */ -static ma_uint32 g_maStandardSampleRatePriorities[] = { - (ma_uint32)ma_standard_sample_rate_48000, - (ma_uint32)ma_standard_sample_rate_44100, - - (ma_uint32)ma_standard_sample_rate_32000, - (ma_uint32)ma_standard_sample_rate_24000, - (ma_uint32)ma_standard_sample_rate_22050, - - (ma_uint32)ma_standard_sample_rate_88200, - (ma_uint32)ma_standard_sample_rate_96000, - (ma_uint32)ma_standard_sample_rate_176400, - (ma_uint32)ma_standard_sample_rate_192000, - - (ma_uint32)ma_standard_sample_rate_16000, - (ma_uint32)ma_standard_sample_rate_11025, - (ma_uint32)ma_standard_sample_rate_8000, - - (ma_uint32)ma_standard_sample_rate_352800, - (ma_uint32)ma_standard_sample_rate_384000 -}; - -static MA_INLINE ma_bool32 ma_is_standard_sample_rate(ma_uint32 sampleRate) -{ - ma_uint32 iSampleRate; - - for (iSampleRate = 0; iSampleRate < sizeof(g_maStandardSampleRatePriorities) / sizeof(g_maStandardSampleRatePriorities[0]); iSampleRate += 1) { - if (g_maStandardSampleRatePriorities[iSampleRate] == sampleRate) { - return MA_TRUE; - } - } - - /* Getting here means the sample rate is not supported. */ - return MA_FALSE; -} - - -static ma_format g_maFormatPriorities[] = { - ma_format_s16, /* Most common */ - ma_format_f32, - - /*ma_format_s24_32,*/ /* Clean alignment */ - ma_format_s32, - - ma_format_s24, /* Unclean alignment */ - - ma_format_u8 /* Low quality */ -}; -#if defined(__clang__) || (defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6))) - #pragma GCC diagnostic pop -#endif - - -MA_API void ma_version(ma_uint32* pMajor, ma_uint32* pMinor, ma_uint32* pRevision) -{ - if (pMajor) { - *pMajor = MA_VERSION_MAJOR; - } - - if (pMinor) { - *pMinor = MA_VERSION_MINOR; - } - - if (pRevision) { - *pRevision = MA_VERSION_REVISION; - } -} - -MA_API const char* ma_version_string(void) -{ - return MA_VERSION_STRING; -} - - -/****************************************************************************** - -Standard Library Stuff - -******************************************************************************/ -#ifndef MA_ASSERT -#define MA_ASSERT(condition) assert(condition) -#endif - -#ifndef MA_MALLOC -#define MA_MALLOC(sz) malloc((sz)) -#endif -#ifndef MA_REALLOC -#define MA_REALLOC(p, sz) realloc((p), (sz)) -#endif -#ifndef MA_FREE -#define MA_FREE(p) free((p)) -#endif - -static MA_INLINE void ma_zero_memory_default(void* p, size_t sz) -{ - if (p == NULL) { - MA_ASSERT(sz == 0); /* If this is triggered there's an error with the calling code. */ - return; - } - - if (sz > 0) { - memset(p, 0, sz); - } -} - - -#ifndef MA_ZERO_MEMORY -#define MA_ZERO_MEMORY(p, sz) ma_zero_memory_default((p), (sz)) -#endif -#ifndef MA_COPY_MEMORY -#define MA_COPY_MEMORY(dst, src, sz) memcpy((dst), (src), (sz)) -#endif -#ifndef MA_MOVE_MEMORY -#define MA_MOVE_MEMORY(dst, src, sz) memmove((dst), (src), (sz)) -#endif - -#define MA_ZERO_OBJECT(p) MA_ZERO_MEMORY((p), sizeof(*(p))) - -#define ma_countof(x) (sizeof(x) / sizeof(x[0])) -#define ma_max(x, y) (((x) > (y)) ? (x) : (y)) -#define ma_min(x, y) (((x) < (y)) ? (x) : (y)) -#define ma_abs(x) (((x) > 0) ? (x) : -(x)) -#define ma_clamp(x, lo, hi) (ma_max(lo, ma_min(x, hi))) -#define ma_offset_ptr(p, offset) (((ma_uint8*)(p)) + (offset)) -#define ma_align(x, a) (((x) + ((a)-1)) & ~((a)-1)) -#define ma_align_64(x) ma_align(x, 8) - -#define ma_buffer_frame_capacity(buffer, channels, format) (sizeof(buffer) / ma_get_bytes_per_sample(format) / (channels)) - -static MA_INLINE double ma_sind(double x) -{ - /* TODO: Implement custom sin(x). */ - return sin(x); -} - -static MA_INLINE double ma_expd(double x) -{ - /* TODO: Implement custom exp(x). */ - return exp(x); -} - -static MA_INLINE double ma_logd(double x) -{ - /* TODO: Implement custom log(x). */ - return log(x); -} - -static MA_INLINE double ma_powd(double x, double y) -{ - /* TODO: Implement custom pow(x, y). */ - return pow(x, y); -} - -static MA_INLINE double ma_sqrtd(double x) -{ - /* TODO: Implement custom sqrt(x). */ - return sqrt(x); -} - - -static MA_INLINE float ma_rsqrtf(float x) -{ - #if defined(MA_SUPPORT_SSE2) && !defined(MA_NO_SSE2) && (defined(MA_X64) || (defined(_M_IX86_FP) && _M_IX86_FP == 2) || defined(__SSE2__)) - { - /* - For SSE we can use RSQRTSS. - - This Stack Overflow post suggests that compilers don't necessarily generate optimal code - when using intrinsics: - - https://web.archive.org/web/20221211012522/https://stackoverflow.com/questions/32687079/getting-fewest-instructions-for-rsqrtss-wrapper - - I'm going to do something similar here, but a bit simpler. - */ - #if defined(__GNUC__) || defined(__clang__) - { - float result; - __asm__ __volatile__("rsqrtss %1, %0" : "=x"(result) : "x"(x)); - return result; - } - #else - { - return _mm_cvtss_f32(_mm_rsqrt_ss(_mm_set_ps1(x))); - } - #endif - } - #else - { - return 1 / (float)ma_sqrtd(x); - } - #endif -} - - -static MA_INLINE float ma_sinf(float x) -{ - return (float)ma_sind((float)x); -} - -static MA_INLINE double ma_cosd(double x) -{ - return ma_sind((MA_PI_D*0.5) - x); -} - -static MA_INLINE float ma_cosf(float x) -{ - return (float)ma_cosd((float)x); -} - -static MA_INLINE double ma_log10d(double x) -{ - return ma_logd(x) * 0.43429448190325182765; -} - -static MA_INLINE float ma_powf(float x, float y) -{ - return (float)ma_powd((double)x, (double)y); -} - -static MA_INLINE float ma_log10f(float x) -{ - return (float)ma_log10d((double)x); -} - - -static MA_INLINE double ma_degrees_to_radians(double degrees) -{ - return degrees * 0.01745329252; -} - -static MA_INLINE double ma_radians_to_degrees(double radians) -{ - return radians * 57.295779512896; -} - -static MA_INLINE float ma_degrees_to_radians_f(float degrees) -{ - return degrees * 0.01745329252f; -} - -static MA_INLINE float ma_radians_to_degrees_f(float radians) -{ - return radians * 57.295779512896f; -} - - -/* -Return Values: - 0: Success - 22: EINVAL - 34: ERANGE - -Not using symbolic constants for errors because I want to avoid #including errno.h - -These are marked as no-inline because of some bad code generation by Clang. None of these functions -are used in any performance-critical code within miniaudio. -*/ -MA_API MA_NO_INLINE int ma_strcpy_s(char* dst, size_t dstSizeInBytes, const char* src) -{ - size_t i; - - if (dst == 0) { - return 22; - } - if (dstSizeInBytes == 0) { - return 34; - } - if (src == 0) { - dst[0] = '\0'; - return 22; - } - - for (i = 0; i < dstSizeInBytes && src[i] != '\0'; ++i) { - dst[i] = src[i]; - } - - if (i < dstSizeInBytes) { - dst[i] = '\0'; - return 0; - } - - dst[0] = '\0'; - return 34; -} - -MA_API MA_NO_INLINE int ma_wcscpy_s(wchar_t* dst, size_t dstCap, const wchar_t* src) -{ - size_t i; - - if (dst == 0) { - return 22; - } - if (dstCap == 0) { - return 34; - } - if (src == 0) { - dst[0] = '\0'; - return 22; - } - - for (i = 0; i < dstCap && src[i] != '\0'; ++i) { - dst[i] = src[i]; - } - - if (i < dstCap) { - dst[i] = '\0'; - return 0; - } - - dst[0] = '\0'; - return 34; -} - - -MA_API MA_NO_INLINE int ma_strncpy_s(char* dst, size_t dstSizeInBytes, const char* src, size_t count) -{ - size_t maxcount; - size_t i; - - if (dst == 0) { - return 22; - } - if (dstSizeInBytes == 0) { - return 34; - } - if (src == 0) { - dst[0] = '\0'; - return 22; - } - - maxcount = count; - if (count == ((size_t)-1) || count >= dstSizeInBytes) { /* -1 = _TRUNCATE */ - maxcount = dstSizeInBytes - 1; - } - - for (i = 0; i < maxcount && src[i] != '\0'; ++i) { - dst[i] = src[i]; - } - - if (src[i] == '\0' || i == count || count == ((size_t)-1)) { - dst[i] = '\0'; - return 0; - } - - dst[0] = '\0'; - return 34; -} - -MA_API MA_NO_INLINE int ma_strcat_s(char* dst, size_t dstSizeInBytes, const char* src) -{ - char* dstorig; - - if (dst == 0) { - return 22; - } - if (dstSizeInBytes == 0) { - return 34; - } - if (src == 0) { - dst[0] = '\0'; - return 22; - } - - dstorig = dst; - - while (dstSizeInBytes > 0 && dst[0] != '\0') { - dst += 1; - dstSizeInBytes -= 1; - } - - if (dstSizeInBytes == 0) { - return 22; /* Unterminated. */ - } - - - while (dstSizeInBytes > 0 && src[0] != '\0') { - *dst++ = *src++; - dstSizeInBytes -= 1; - } - - if (dstSizeInBytes > 0) { - dst[0] = '\0'; - } else { - dstorig[0] = '\0'; - return 34; - } - - return 0; -} - -MA_API MA_NO_INLINE int ma_strncat_s(char* dst, size_t dstSizeInBytes, const char* src, size_t count) -{ - char* dstorig; - - if (dst == 0) { - return 22; - } - if (dstSizeInBytes == 0) { - return 34; - } - if (src == 0) { - return 22; - } - - dstorig = dst; - - while (dstSizeInBytes > 0 && dst[0] != '\0') { - dst += 1; - dstSizeInBytes -= 1; - } - - if (dstSizeInBytes == 0) { - return 22; /* Unterminated. */ - } - - - if (count == ((size_t)-1)) { /* _TRUNCATE */ - count = dstSizeInBytes - 1; - } - - while (dstSizeInBytes > 0 && src[0] != '\0' && count > 0) { - *dst++ = *src++; - dstSizeInBytes -= 1; - count -= 1; - } - - if (dstSizeInBytes > 0) { - dst[0] = '\0'; - } else { - dstorig[0] = '\0'; - return 34; - } - - return 0; -} - -MA_API MA_NO_INLINE int ma_itoa_s(int value, char* dst, size_t dstSizeInBytes, int radix) -{ - int sign; - unsigned int valueU; - char* dstEnd; - - if (dst == NULL || dstSizeInBytes == 0) { - return 22; - } - if (radix < 2 || radix > 36) { - dst[0] = '\0'; - return 22; - } - - sign = (value < 0 && radix == 10) ? -1 : 1; /* The negative sign is only used when the base is 10. */ - - if (value < 0) { - valueU = -value; - } else { - valueU = value; - } - - dstEnd = dst; - do - { - int remainder = valueU % radix; - if (remainder > 9) { - *dstEnd = (char)((remainder - 10) + 'a'); - } else { - *dstEnd = (char)(remainder + '0'); - } - - dstEnd += 1; - dstSizeInBytes -= 1; - valueU /= radix; - } while (dstSizeInBytes > 0 && valueU > 0); - - if (dstSizeInBytes == 0) { - dst[0] = '\0'; - return 22; /* Ran out of room in the output buffer. */ - } - - if (sign < 0) { - *dstEnd++ = '-'; - dstSizeInBytes -= 1; - } - - if (dstSizeInBytes == 0) { - dst[0] = '\0'; - return 22; /* Ran out of room in the output buffer. */ - } - - *dstEnd = '\0'; - - - /* At this point the string will be reversed. */ - dstEnd -= 1; - while (dst < dstEnd) { - char temp = *dst; - *dst = *dstEnd; - *dstEnd = temp; - - dst += 1; - dstEnd -= 1; - } - - return 0; -} - -MA_API MA_NO_INLINE int ma_strcmp(const char* str1, const char* str2) -{ - if (str1 == str2) return 0; - - /* These checks differ from the standard implementation. It's not important, but I prefer it just for sanity. */ - if (str1 == NULL) return -1; - if (str2 == NULL) return 1; - - for (;;) { - if (str1[0] == '\0') { - break; - } - if (str1[0] != str2[0]) { - break; - } - - str1 += 1; - str2 += 1; - } - - return ((unsigned char*)str1)[0] - ((unsigned char*)str2)[0]; -} - -MA_API MA_NO_INLINE int ma_strappend(char* dst, size_t dstSize, const char* srcA, const char* srcB) -{ - int result; - - result = ma_strncpy_s(dst, dstSize, srcA, (size_t)-1); - if (result != 0) { - return result; - } - - result = ma_strncat_s(dst, dstSize, srcB, (size_t)-1); - if (result != 0) { - return result; - } - - return result; -} - -MA_API MA_NO_INLINE char* ma_copy_string(const char* src, const ma_allocation_callbacks* pAllocationCallbacks) -{ - size_t sz; - char* dst; - - if (src == NULL) { - return NULL; - } - - sz = strlen(src)+1; - dst = (char*)ma_malloc(sz, pAllocationCallbacks); - if (dst == NULL) { - return NULL; - } - - ma_strcpy_s(dst, sz, src); - - return dst; -} - -MA_API MA_NO_INLINE wchar_t* ma_copy_string_w(const wchar_t* src, const ma_allocation_callbacks* pAllocationCallbacks) -{ - size_t sz = wcslen(src)+1; - wchar_t* dst = (wchar_t*)ma_malloc(sz * sizeof(*dst), pAllocationCallbacks); - if (dst == NULL) { - return NULL; - } - - ma_wcscpy_s(dst, sz, src); - - return dst; -} - - - -#include -static ma_result ma_result_from_errno(int e) -{ - if (e == 0) { - return MA_SUCCESS; - } -#ifdef EPERM - else if (e == EPERM) { return MA_INVALID_OPERATION; } -#endif -#ifdef ENOENT - else if (e == ENOENT) { return MA_DOES_NOT_EXIST; } -#endif -#ifdef ESRCH - else if (e == ESRCH) { return MA_DOES_NOT_EXIST; } -#endif -#ifdef EINTR - else if (e == EINTR) { return MA_INTERRUPT; } -#endif -#ifdef EIO - else if (e == EIO) { return MA_IO_ERROR; } -#endif -#ifdef ENXIO - else if (e == ENXIO) { return MA_DOES_NOT_EXIST; } -#endif -#ifdef E2BIG - else if (e == E2BIG) { return MA_INVALID_ARGS; } -#endif -#ifdef ENOEXEC - else if (e == ENOEXEC) { return MA_INVALID_FILE; } -#endif -#ifdef EBADF - else if (e == EBADF) { return MA_INVALID_FILE; } -#endif -#ifdef ECHILD - else if (e == ECHILD) { return MA_ERROR; } -#endif -#ifdef EAGAIN - else if (e == EAGAIN) { return MA_UNAVAILABLE; } -#endif -#ifdef ENOMEM - else if (e == ENOMEM) { return MA_OUT_OF_MEMORY; } -#endif -#ifdef EACCES - else if (e == EACCES) { return MA_ACCESS_DENIED; } -#endif -#ifdef EFAULT - else if (e == EFAULT) { return MA_BAD_ADDRESS; } -#endif -#ifdef ENOTBLK - else if (e == ENOTBLK) { return MA_ERROR; } -#endif -#ifdef EBUSY - else if (e == EBUSY) { return MA_BUSY; } -#endif -#ifdef EEXIST - else if (e == EEXIST) { return MA_ALREADY_EXISTS; } -#endif -#ifdef EXDEV - else if (e == EXDEV) { return MA_ERROR; } -#endif -#ifdef ENODEV - else if (e == ENODEV) { return MA_DOES_NOT_EXIST; } -#endif -#ifdef ENOTDIR - else if (e == ENOTDIR) { return MA_NOT_DIRECTORY; } -#endif -#ifdef EISDIR - else if (e == EISDIR) { return MA_IS_DIRECTORY; } -#endif -#ifdef EINVAL - else if (e == EINVAL) { return MA_INVALID_ARGS; } -#endif -#ifdef ENFILE - else if (e == ENFILE) { return MA_TOO_MANY_OPEN_FILES; } -#endif -#ifdef EMFILE - else if (e == EMFILE) { return MA_TOO_MANY_OPEN_FILES; } -#endif -#ifdef ENOTTY - else if (e == ENOTTY) { return MA_INVALID_OPERATION; } -#endif -#ifdef ETXTBSY - else if (e == ETXTBSY) { return MA_BUSY; } -#endif -#ifdef EFBIG - else if (e == EFBIG) { return MA_TOO_BIG; } -#endif -#ifdef ENOSPC - else if (e == ENOSPC) { return MA_NO_SPACE; } -#endif -#ifdef ESPIPE - else if (e == ESPIPE) { return MA_BAD_SEEK; } -#endif -#ifdef EROFS - else if (e == EROFS) { return MA_ACCESS_DENIED; } -#endif -#ifdef EMLINK - else if (e == EMLINK) { return MA_TOO_MANY_LINKS; } -#endif -#ifdef EPIPE - else if (e == EPIPE) { return MA_BAD_PIPE; } -#endif -#ifdef EDOM - else if (e == EDOM) { return MA_OUT_OF_RANGE; } -#endif -#ifdef ERANGE - else if (e == ERANGE) { return MA_OUT_OF_RANGE; } -#endif -#ifdef EDEADLK - else if (e == EDEADLK) { return MA_DEADLOCK; } -#endif -#ifdef ENAMETOOLONG - else if (e == ENAMETOOLONG) { return MA_PATH_TOO_LONG; } -#endif -#ifdef ENOLCK - else if (e == ENOLCK) { return MA_ERROR; } -#endif -#ifdef ENOSYS - else if (e == ENOSYS) { return MA_NOT_IMPLEMENTED; } -#endif -#ifdef ENOTEMPTY - else if (e == ENOTEMPTY) { return MA_DIRECTORY_NOT_EMPTY; } -#endif -#ifdef ELOOP - else if (e == ELOOP) { return MA_TOO_MANY_LINKS; } -#endif -#ifdef ENOMSG - else if (e == ENOMSG) { return MA_NO_MESSAGE; } -#endif -#ifdef EIDRM - else if (e == EIDRM) { return MA_ERROR; } -#endif -#ifdef ECHRNG - else if (e == ECHRNG) { return MA_ERROR; } -#endif -#ifdef EL2NSYNC - else if (e == EL2NSYNC) { return MA_ERROR; } -#endif -#ifdef EL3HLT - else if (e == EL3HLT) { return MA_ERROR; } -#endif -#ifdef EL3RST - else if (e == EL3RST) { return MA_ERROR; } -#endif -#ifdef ELNRNG - else if (e == ELNRNG) { return MA_OUT_OF_RANGE; } -#endif -#ifdef EUNATCH - else if (e == EUNATCH) { return MA_ERROR; } -#endif -#ifdef ENOCSI - else if (e == ENOCSI) { return MA_ERROR; } -#endif -#ifdef EL2HLT - else if (e == EL2HLT) { return MA_ERROR; } -#endif -#ifdef EBADE - else if (e == EBADE) { return MA_ERROR; } -#endif -#ifdef EBADR - else if (e == EBADR) { return MA_ERROR; } -#endif -#ifdef EXFULL - else if (e == EXFULL) { return MA_ERROR; } -#endif -#ifdef ENOANO - else if (e == ENOANO) { return MA_ERROR; } -#endif -#ifdef EBADRQC - else if (e == EBADRQC) { return MA_ERROR; } -#endif -#ifdef EBADSLT - else if (e == EBADSLT) { return MA_ERROR; } -#endif -#ifdef EBFONT - else if (e == EBFONT) { return MA_INVALID_FILE; } -#endif -#ifdef ENOSTR - else if (e == ENOSTR) { return MA_ERROR; } -#endif -#ifdef ENODATA - else if (e == ENODATA) { return MA_NO_DATA_AVAILABLE; } -#endif -#ifdef ETIME - else if (e == ETIME) { return MA_TIMEOUT; } -#endif -#ifdef ENOSR - else if (e == ENOSR) { return MA_NO_DATA_AVAILABLE; } -#endif -#ifdef ENONET - else if (e == ENONET) { return MA_NO_NETWORK; } -#endif -#ifdef ENOPKG - else if (e == ENOPKG) { return MA_ERROR; } -#endif -#ifdef EREMOTE - else if (e == EREMOTE) { return MA_ERROR; } -#endif -#ifdef ENOLINK - else if (e == ENOLINK) { return MA_ERROR; } -#endif -#ifdef EADV - else if (e == EADV) { return MA_ERROR; } -#endif -#ifdef ESRMNT - else if (e == ESRMNT) { return MA_ERROR; } -#endif -#ifdef ECOMM - else if (e == ECOMM) { return MA_ERROR; } -#endif -#ifdef EPROTO - else if (e == EPROTO) { return MA_ERROR; } -#endif -#ifdef EMULTIHOP - else if (e == EMULTIHOP) { return MA_ERROR; } -#endif -#ifdef EDOTDOT - else if (e == EDOTDOT) { return MA_ERROR; } -#endif -#ifdef EBADMSG - else if (e == EBADMSG) { return MA_BAD_MESSAGE; } -#endif -#ifdef EOVERFLOW - else if (e == EOVERFLOW) { return MA_TOO_BIG; } -#endif -#ifdef ENOTUNIQ - else if (e == ENOTUNIQ) { return MA_NOT_UNIQUE; } -#endif -#ifdef EBADFD - else if (e == EBADFD) { return MA_ERROR; } -#endif -#ifdef EREMCHG - else if (e == EREMCHG) { return MA_ERROR; } -#endif -#ifdef ELIBACC - else if (e == ELIBACC) { return MA_ACCESS_DENIED; } -#endif -#ifdef ELIBBAD - else if (e == ELIBBAD) { return MA_INVALID_FILE; } -#endif -#ifdef ELIBSCN - else if (e == ELIBSCN) { return MA_INVALID_FILE; } -#endif -#ifdef ELIBMAX - else if (e == ELIBMAX) { return MA_ERROR; } -#endif -#ifdef ELIBEXEC - else if (e == ELIBEXEC) { return MA_ERROR; } -#endif -#ifdef EILSEQ - else if (e == EILSEQ) { return MA_INVALID_DATA; } -#endif -#ifdef ERESTART - else if (e == ERESTART) { return MA_ERROR; } -#endif -#ifdef ESTRPIPE - else if (e == ESTRPIPE) { return MA_ERROR; } -#endif -#ifdef EUSERS - else if (e == EUSERS) { return MA_ERROR; } -#endif -#ifdef ENOTSOCK - else if (e == ENOTSOCK) { return MA_NOT_SOCKET; } -#endif -#ifdef EDESTADDRREQ - else if (e == EDESTADDRREQ) { return MA_NO_ADDRESS; } -#endif -#ifdef EMSGSIZE - else if (e == EMSGSIZE) { return MA_TOO_BIG; } -#endif -#ifdef EPROTOTYPE - else if (e == EPROTOTYPE) { return MA_BAD_PROTOCOL; } -#endif -#ifdef ENOPROTOOPT - else if (e == ENOPROTOOPT) { return MA_PROTOCOL_UNAVAILABLE; } -#endif -#ifdef EPROTONOSUPPORT - else if (e == EPROTONOSUPPORT) { return MA_PROTOCOL_NOT_SUPPORTED; } -#endif -#ifdef ESOCKTNOSUPPORT - else if (e == ESOCKTNOSUPPORT) { return MA_SOCKET_NOT_SUPPORTED; } -#endif -#ifdef EOPNOTSUPP - else if (e == EOPNOTSUPP) { return MA_INVALID_OPERATION; } -#endif -#ifdef EPFNOSUPPORT - else if (e == EPFNOSUPPORT) { return MA_PROTOCOL_FAMILY_NOT_SUPPORTED; } -#endif -#ifdef EAFNOSUPPORT - else if (e == EAFNOSUPPORT) { return MA_ADDRESS_FAMILY_NOT_SUPPORTED; } -#endif -#ifdef EADDRINUSE - else if (e == EADDRINUSE) { return MA_ALREADY_IN_USE; } -#endif -#ifdef EADDRNOTAVAIL - else if (e == EADDRNOTAVAIL) { return MA_ERROR; } -#endif -#ifdef ENETDOWN - else if (e == ENETDOWN) { return MA_NO_NETWORK; } -#endif -#ifdef ENETUNREACH - else if (e == ENETUNREACH) { return MA_NO_NETWORK; } -#endif -#ifdef ENETRESET - else if (e == ENETRESET) { return MA_NO_NETWORK; } -#endif -#ifdef ECONNABORTED - else if (e == ECONNABORTED) { return MA_NO_NETWORK; } -#endif -#ifdef ECONNRESET - else if (e == ECONNRESET) { return MA_CONNECTION_RESET; } -#endif -#ifdef ENOBUFS - else if (e == ENOBUFS) { return MA_NO_SPACE; } -#endif -#ifdef EISCONN - else if (e == EISCONN) { return MA_ALREADY_CONNECTED; } -#endif -#ifdef ENOTCONN - else if (e == ENOTCONN) { return MA_NOT_CONNECTED; } -#endif -#ifdef ESHUTDOWN - else if (e == ESHUTDOWN) { return MA_ERROR; } -#endif -#ifdef ETOOMANYREFS - else if (e == ETOOMANYREFS) { return MA_ERROR; } -#endif -#ifdef ETIMEDOUT - else if (e == ETIMEDOUT) { return MA_TIMEOUT; } -#endif -#ifdef ECONNREFUSED - else if (e == ECONNREFUSED) { return MA_CONNECTION_REFUSED; } -#endif -#ifdef EHOSTDOWN - else if (e == EHOSTDOWN) { return MA_NO_HOST; } -#endif -#ifdef EHOSTUNREACH - else if (e == EHOSTUNREACH) { return MA_NO_HOST; } -#endif -#ifdef EALREADY - else if (e == EALREADY) { return MA_IN_PROGRESS; } -#endif -#ifdef EINPROGRESS - else if (e == EINPROGRESS) { return MA_IN_PROGRESS; } -#endif -#ifdef ESTALE - else if (e == ESTALE) { return MA_INVALID_FILE; } -#endif -#ifdef EUCLEAN - else if (e == EUCLEAN) { return MA_ERROR; } -#endif -#ifdef ENOTNAM - else if (e == ENOTNAM) { return MA_ERROR; } -#endif -#ifdef ENAVAIL - else if (e == ENAVAIL) { return MA_ERROR; } -#endif -#ifdef EISNAM - else if (e == EISNAM) { return MA_ERROR; } -#endif -#ifdef EREMOTEIO - else if (e == EREMOTEIO) { return MA_IO_ERROR; } -#endif -#ifdef EDQUOT - else if (e == EDQUOT) { return MA_NO_SPACE; } -#endif -#ifdef ENOMEDIUM - else if (e == ENOMEDIUM) { return MA_DOES_NOT_EXIST; } -#endif -#ifdef EMEDIUMTYPE - else if (e == EMEDIUMTYPE) { return MA_ERROR; } -#endif -#ifdef ECANCELED - else if (e == ECANCELED) { return MA_CANCELLED; } -#endif -#ifdef ENOKEY - else if (e == ENOKEY) { return MA_ERROR; } -#endif -#ifdef EKEYEXPIRED - else if (e == EKEYEXPIRED) { return MA_ERROR; } -#endif -#ifdef EKEYREVOKED - else if (e == EKEYREVOKED) { return MA_ERROR; } -#endif -#ifdef EKEYREJECTED - else if (e == EKEYREJECTED) { return MA_ERROR; } -#endif -#ifdef EOWNERDEAD - else if (e == EOWNERDEAD) { return MA_ERROR; } -#endif -#ifdef ENOTRECOVERABLE - else if (e == ENOTRECOVERABLE) { return MA_ERROR; } -#endif -#ifdef ERFKILL - else if (e == ERFKILL) { return MA_ERROR; } -#endif -#ifdef EHWPOISON - else if (e == EHWPOISON) { return MA_ERROR; } -#endif - else { - return MA_ERROR; - } -} - -MA_API ma_result ma_fopen(FILE** ppFile, const char* pFilePath, const char* pOpenMode) -{ -#if defined(_MSC_VER) && _MSC_VER >= 1400 - errno_t err; -#endif - - if (ppFile != NULL) { - *ppFile = NULL; /* Safety. */ - } - - if (pFilePath == NULL || pOpenMode == NULL || ppFile == NULL) { - return MA_INVALID_ARGS; - } - -#if defined(_MSC_VER) && _MSC_VER >= 1400 - err = fopen_s(ppFile, pFilePath, pOpenMode); - if (err != 0) { - return ma_result_from_errno(err); - } -#else -#if defined(_WIN32) || defined(__APPLE__) - *ppFile = fopen(pFilePath, pOpenMode); -#else - #if defined(_FILE_OFFSET_BITS) && _FILE_OFFSET_BITS == 64 && defined(_LARGEFILE64_SOURCE) - *ppFile = fopen64(pFilePath, pOpenMode); - #else - *ppFile = fopen(pFilePath, pOpenMode); - #endif -#endif - if (*ppFile == NULL) { - ma_result result = ma_result_from_errno(errno); - if (result == MA_SUCCESS) { - result = MA_ERROR; /* Just a safety check to make sure we never ever return success when pFile == NULL. */ - } - - return result; - } -#endif - - return MA_SUCCESS; -} - - - -/* -_wfopen() isn't always available in all compilation environments. - - * Windows only. - * MSVC seems to support it universally as far back as VC6 from what I can tell (haven't checked further back). - * MinGW-64 (both 32- and 64-bit) seems to support it. - * MinGW wraps it in !defined(__STRICT_ANSI__). - * OpenWatcom wraps it in !defined(_NO_EXT_KEYS). - -This can be reviewed as compatibility issues arise. The preference is to use _wfopen_s() and _wfopen() as opposed to the wcsrtombs() -fallback, so if you notice your compiler not detecting this properly I'm happy to look at adding support. -*/ -#if defined(_WIN32) - #if defined(_MSC_VER) || defined(__MINGW64__) || (!defined(__STRICT_ANSI__) && !defined(_NO_EXT_KEYS)) - #define MA_HAS_WFOPEN - #endif -#endif - -MA_API ma_result ma_wfopen(FILE** ppFile, const wchar_t* pFilePath, const wchar_t* pOpenMode, const ma_allocation_callbacks* pAllocationCallbacks) -{ - if (ppFile != NULL) { - *ppFile = NULL; /* Safety. */ - } - - if (pFilePath == NULL || pOpenMode == NULL || ppFile == NULL) { - return MA_INVALID_ARGS; - } - -#if defined(MA_HAS_WFOPEN) - { - /* Use _wfopen() on Windows. */ - #if defined(_MSC_VER) && _MSC_VER >= 1400 - errno_t err = _wfopen_s(ppFile, pFilePath, pOpenMode); - if (err != 0) { - return ma_result_from_errno(err); - } - #else - *ppFile = _wfopen(pFilePath, pOpenMode); - if (*ppFile == NULL) { - return ma_result_from_errno(errno); - } - #endif - (void)pAllocationCallbacks; - } -#else - /* - Use fopen() on anything other than Windows. Requires a conversion. This is annoying because fopen() is locale specific. The only real way I can - think of to do this is with wcsrtombs(). Note that wcstombs() is apparently not thread-safe because it uses a static global mbstate_t object for - maintaining state. I've checked this with -std=c89 and it works, but if somebody get's a compiler error I'll look into improving compatibility. - */ - { - mbstate_t mbs; - size_t lenMB; - const wchar_t* pFilePathTemp = pFilePath; - char* pFilePathMB = NULL; - char pOpenModeMB[32] = {0}; - - /* Get the length first. */ - MA_ZERO_OBJECT(&mbs); - lenMB = wcsrtombs(NULL, &pFilePathTemp, 0, &mbs); - if (lenMB == (size_t)-1) { - return ma_result_from_errno(errno); - } - - pFilePathMB = (char*)ma_malloc(lenMB + 1, pAllocationCallbacks); - if (pFilePathMB == NULL) { - return MA_OUT_OF_MEMORY; - } - - pFilePathTemp = pFilePath; - MA_ZERO_OBJECT(&mbs); - wcsrtombs(pFilePathMB, &pFilePathTemp, lenMB + 1, &mbs); - - /* The open mode should always consist of ASCII characters so we should be able to do a trivial conversion. */ - { - size_t i = 0; - for (;;) { - if (pOpenMode[i] == 0) { - pOpenModeMB[i] = '\0'; - break; - } - - pOpenModeMB[i] = (char)pOpenMode[i]; - i += 1; - } - } - - *ppFile = fopen(pFilePathMB, pOpenModeMB); - - ma_free(pFilePathMB, pAllocationCallbacks); - } - - if (*ppFile == NULL) { - return MA_ERROR; - } -#endif - - return MA_SUCCESS; -} - - - -static MA_INLINE void ma_copy_memory_64(void* dst, const void* src, ma_uint64 sizeInBytes) -{ -#if 0xFFFFFFFFFFFFFFFF <= MA_SIZE_MAX - MA_COPY_MEMORY(dst, src, (size_t)sizeInBytes); -#else - while (sizeInBytes > 0) { - ma_uint64 bytesToCopyNow = sizeInBytes; - if (bytesToCopyNow > MA_SIZE_MAX) { - bytesToCopyNow = MA_SIZE_MAX; - } - - MA_COPY_MEMORY(dst, src, (size_t)bytesToCopyNow); /* Safe cast to size_t. */ - - sizeInBytes -= bytesToCopyNow; - dst = ( void*)(( ma_uint8*)dst + bytesToCopyNow); - src = (const void*)((const ma_uint8*)src + bytesToCopyNow); - } -#endif -} - -static MA_INLINE void ma_zero_memory_64(void* dst, ma_uint64 sizeInBytes) -{ -#if 0xFFFFFFFFFFFFFFFF <= MA_SIZE_MAX - MA_ZERO_MEMORY(dst, (size_t)sizeInBytes); -#else - while (sizeInBytes > 0) { - ma_uint64 bytesToZeroNow = sizeInBytes; - if (bytesToZeroNow > MA_SIZE_MAX) { - bytesToZeroNow = MA_SIZE_MAX; - } - - MA_ZERO_MEMORY(dst, (size_t)bytesToZeroNow); /* Safe cast to size_t. */ - - sizeInBytes -= bytesToZeroNow; - dst = (void*)((ma_uint8*)dst + bytesToZeroNow); - } -#endif -} - - -/* Thanks to good old Bit Twiddling Hacks for this one: http://graphics.stanford.edu/~seander/bithacks.html#RoundUpPowerOf2 */ -static MA_INLINE unsigned int ma_next_power_of_2(unsigned int x) -{ - x--; - x |= x >> 1; - x |= x >> 2; - x |= x >> 4; - x |= x >> 8; - x |= x >> 16; - x++; - - return x; -} - -static MA_INLINE unsigned int ma_prev_power_of_2(unsigned int x) -{ - return ma_next_power_of_2(x) >> 1; -} - -static MA_INLINE unsigned int ma_round_to_power_of_2(unsigned int x) -{ - unsigned int prev = ma_prev_power_of_2(x); - unsigned int next = ma_next_power_of_2(x); - if ((next - x) > (x - prev)) { - return prev; - } else { - return next; - } -} - -static MA_INLINE unsigned int ma_count_set_bits(unsigned int x) -{ - unsigned int count = 0; - while (x != 0) { - if (x & 1) { - count += 1; - } - - x = x >> 1; - } - - return count; -} - - - -/************************************************************************************************************************************************************** - -Allocation Callbacks - -**************************************************************************************************************************************************************/ -static void* ma__malloc_default(size_t sz, void* pUserData) -{ - (void)pUserData; - return MA_MALLOC(sz); -} - -static void* ma__realloc_default(void* p, size_t sz, void* pUserData) -{ - (void)pUserData; - return MA_REALLOC(p, sz); -} - -static void ma__free_default(void* p, void* pUserData) -{ - (void)pUserData; - MA_FREE(p); -} - -static ma_allocation_callbacks ma_allocation_callbacks_init_default(void) -{ - ma_allocation_callbacks callbacks; - callbacks.pUserData = NULL; - callbacks.onMalloc = ma__malloc_default; - callbacks.onRealloc = ma__realloc_default; - callbacks.onFree = ma__free_default; - - return callbacks; -} - -static ma_result ma_allocation_callbacks_init_copy(ma_allocation_callbacks* pDst, const ma_allocation_callbacks* pSrc) -{ - if (pDst == NULL) { - return MA_INVALID_ARGS; - } - - if (pSrc == NULL) { - *pDst = ma_allocation_callbacks_init_default(); - } else { - if (pSrc->pUserData == NULL && pSrc->onFree == NULL && pSrc->onMalloc == NULL && pSrc->onRealloc == NULL) { - *pDst = ma_allocation_callbacks_init_default(); - } else { - if (pSrc->onFree == NULL || (pSrc->onMalloc == NULL && pSrc->onRealloc == NULL)) { - return MA_INVALID_ARGS; /* Invalid allocation callbacks. */ - } else { - *pDst = *pSrc; - } - } - } - - return MA_SUCCESS; -} - - - - -/************************************************************************************************************************************************************** - -Logging - -**************************************************************************************************************************************************************/ -MA_API const char* ma_log_level_to_string(ma_uint32 logLevel) -{ - switch (logLevel) - { - case MA_LOG_LEVEL_DEBUG: return "DEBUG"; - case MA_LOG_LEVEL_INFO: return "INFO"; - case MA_LOG_LEVEL_WARNING: return "WARNING"; - case MA_LOG_LEVEL_ERROR: return "ERROR"; - default: return "ERROR"; - } -} - -#if defined(MA_DEBUG_OUTPUT) -#if defined(MA_ANDROID) - #include -#endif - -/* Customize this to use a specific tag in __android_log_print() for debug output messages. */ -#ifndef MA_ANDROID_LOG_TAG -#define MA_ANDROID_LOG_TAG "miniaudio" -#endif - -void ma_log_callback_debug(void* pUserData, ma_uint32 level, const char* pMessage) -{ - (void)pUserData; - - /* Special handling for some platforms. */ - #if defined(MA_ANDROID) - { - /* Android. */ - __android_log_print(ANDROID_LOG_DEBUG, MA_ANDROID_LOG_TAG, "%s: %s", ma_log_level_to_string(level), pMessage); - } - #else - { - /* Everything else. */ - printf("%s: %s", ma_log_level_to_string(level), pMessage); - } - #endif -} -#endif - -MA_API ma_log_callback ma_log_callback_init(ma_log_callback_proc onLog, void* pUserData) -{ - ma_log_callback callback; - - MA_ZERO_OBJECT(&callback); - callback.onLog = onLog; - callback.pUserData = pUserData; - - return callback; -} - - -MA_API ma_result ma_log_init(const ma_allocation_callbacks* pAllocationCallbacks, ma_log* pLog) -{ - if (pLog == NULL) { - return MA_INVALID_ARGS; - } - - MA_ZERO_OBJECT(pLog); - ma_allocation_callbacks_init_copy(&pLog->allocationCallbacks, pAllocationCallbacks); - - /* We need a mutex for thread safety. */ - #ifndef MA_NO_THREADING - { - ma_result result = ma_mutex_init(&pLog->lock); - if (result != MA_SUCCESS) { - return result; - } - } - #endif - - /* If we're using debug output, enable it. */ - #if defined(MA_DEBUG_OUTPUT) - { - ma_log_register_callback(pLog, ma_log_callback_init(ma_log_callback_debug, NULL)); /* Doesn't really matter if this fails. */ - } - #endif - - return MA_SUCCESS; -} - -MA_API void ma_log_uninit(ma_log* pLog) -{ - if (pLog == NULL) { - return; - } - -#ifndef MA_NO_THREADING - ma_mutex_uninit(&pLog->lock); -#endif -} - -static void ma_log_lock(ma_log* pLog) -{ -#ifndef MA_NO_THREADING - ma_mutex_lock(&pLog->lock); -#else - (void)pLog; -#endif -} - -static void ma_log_unlock(ma_log* pLog) -{ -#ifndef MA_NO_THREADING - ma_mutex_unlock(&pLog->lock); -#else - (void)pLog; -#endif -} - -MA_API ma_result ma_log_register_callback(ma_log* pLog, ma_log_callback callback) -{ - ma_result result = MA_SUCCESS; - - if (pLog == NULL || callback.onLog == NULL) { - return MA_INVALID_ARGS; - } - - ma_log_lock(pLog); - { - if (pLog->callbackCount == ma_countof(pLog->callbacks)) { - result = MA_OUT_OF_MEMORY; /* Reached the maximum allowed log callbacks. */ - } else { - pLog->callbacks[pLog->callbackCount] = callback; - pLog->callbackCount += 1; - } - } - ma_log_unlock(pLog); - - return result; -} - -MA_API ma_result ma_log_unregister_callback(ma_log* pLog, ma_log_callback callback) -{ - if (pLog == NULL) { - return MA_INVALID_ARGS; - } - - ma_log_lock(pLog); - { - ma_uint32 iLog; - for (iLog = 0; iLog < pLog->callbackCount; ) { - if (pLog->callbacks[iLog].onLog == callback.onLog) { - /* Found. Move everything down a slot. */ - ma_uint32 jLog; - for (jLog = iLog; jLog < pLog->callbackCount-1; jLog += 1) { - pLog->callbacks[jLog] = pLog->callbacks[jLog + 1]; - } - - pLog->callbackCount -= 1; - } else { - /* Not found. */ - iLog += 1; - } - } - } - ma_log_unlock(pLog); - - return MA_SUCCESS; -} - -MA_API ma_result ma_log_post(ma_log* pLog, ma_uint32 level, const char* pMessage) -{ - if (pLog == NULL || pMessage == NULL) { - return MA_INVALID_ARGS; - } - - ma_log_lock(pLog); - { - ma_uint32 iLog; - for (iLog = 0; iLog < pLog->callbackCount; iLog += 1) { - if (pLog->callbacks[iLog].onLog) { - pLog->callbacks[iLog].onLog(pLog->callbacks[iLog].pUserData, level, pMessage); - } - } - } - ma_log_unlock(pLog); - - return MA_SUCCESS; -} - - -/* -We need to emulate _vscprintf() for the VC6 build. This can be more efficient, but since it's only VC6, and it's just a -logging function, I'm happy to keep this simple. In the VC6 build we can implement this in terms of _vsnprintf(). -*/ -#if defined(_MSC_VER) && _MSC_VER < 1900 -static int ma_vscprintf(const ma_allocation_callbacks* pAllocationCallbacks, const char* format, va_list args) -{ -#if _MSC_VER > 1200 - return _vscprintf(format, args); -#else - int result; - char* pTempBuffer = NULL; - size_t tempBufferCap = 1024; - - if (format == NULL) { - errno = EINVAL; - return -1; - } - - for (;;) { - char* pNewTempBuffer = (char*)ma_realloc(pTempBuffer, tempBufferCap, pAllocationCallbacks); - if (pNewTempBuffer == NULL) { - ma_free(pTempBuffer, pAllocationCallbacks); - errno = ENOMEM; - return -1; /* Out of memory. */ - } - - pTempBuffer = pNewTempBuffer; - - result = _vsnprintf(pTempBuffer, tempBufferCap, format, args); - ma_free(pTempBuffer, NULL); - - if (result != -1) { - break; /* Got it. */ - } - - /* Buffer wasn't big enough. Ideally it'd be nice to use an error code to know the reason for sure, but this is reliable enough. */ - tempBufferCap *= 2; - } - - return result; -#endif -} -#endif - -MA_API ma_result ma_log_postv(ma_log* pLog, ma_uint32 level, const char* pFormat, va_list args) -{ - if (pLog == NULL || pFormat == NULL) { - return MA_INVALID_ARGS; - } - - #if (defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L) || ((!defined(_MSC_VER) || _MSC_VER >= 1900) && !defined(__STRICT_ANSI__) && !defined(_NO_EXT_KEYS)) || (defined(__cplusplus) && __cplusplus >= 201103L) - { - ma_result result; - int length; - char pFormattedMessageStack[1024]; - char* pFormattedMessageHeap = NULL; - - /* First try formatting into our fixed sized stack allocated buffer. If this is too small we'll fallback to a heap allocation. */ - length = vsnprintf(pFormattedMessageStack, sizeof(pFormattedMessageStack), pFormat, args); - if (length < 0) { - return MA_INVALID_OPERATION; /* An error occurred when trying to convert the buffer. */ - } - - if ((size_t)length < sizeof(pFormattedMessageStack)) { - /* The string was written to the stack. */ - result = ma_log_post(pLog, level, pFormattedMessageStack); - } else { - /* The stack buffer was too small, try the heap. */ - pFormattedMessageHeap = (char*)ma_malloc(length + 1, &pLog->allocationCallbacks); - if (pFormattedMessageHeap == NULL) { - return MA_OUT_OF_MEMORY; - } - - length = vsnprintf(pFormattedMessageHeap, length + 1, pFormat, args); - if (length < 0) { - ma_free(pFormattedMessageHeap, &pLog->allocationCallbacks); - return MA_INVALID_OPERATION; - } - - result = ma_log_post(pLog, level, pFormattedMessageHeap); - ma_free(pFormattedMessageHeap, &pLog->allocationCallbacks); - } - - return result; - } - #else - { - /* - Without snprintf() we need to first measure the string and then heap allocate it. I'm only aware of Visual Studio having support for this without snprintf(), so we'll - need to restrict this branch to Visual Studio. For other compilers we need to just not support formatted logging because I don't want the security risk of overflowing - a fixed sized stack allocated buffer. - */ - #if defined(_MSC_VER) && _MSC_VER >= 1200 /* 1200 = VC6 */ - { - ma_result result; - int formattedLen; - char* pFormattedMessage = NULL; - va_list args2; - - #if _MSC_VER >= 1800 - { - va_copy(args2, args); - } - #else - { - args2 = args; - } - #endif - - formattedLen = ma_vscprintf(&pLog->allocationCallbacks, pFormat, args2); - va_end(args2); - - if (formattedLen <= 0) { - return MA_INVALID_OPERATION; - } - - pFormattedMessage = (char*)ma_malloc(formattedLen + 1, &pLog->allocationCallbacks); - if (pFormattedMessage == NULL) { - return MA_OUT_OF_MEMORY; - } - - /* We'll get errors on newer versions of Visual Studio if we try to use vsprintf(). */ - #if _MSC_VER >= 1400 /* 1400 = Visual Studio 2005 */ - { - vsprintf_s(pFormattedMessage, formattedLen + 1, pFormat, args); - } - #else - { - vsprintf(pFormattedMessage, pFormat, args); - } - #endif - - result = ma_log_post(pLog, level, pFormattedMessage); - ma_free(pFormattedMessage, &pLog->allocationCallbacks); - - return result; - } - #else - { - /* Can't do anything because we don't have a safe way of to emulate vsnprintf() without a manual solution. */ - (void)level; - (void)args; - - return MA_INVALID_OPERATION; - } - #endif - } - #endif -} - -MA_API ma_result ma_log_postf(ma_log* pLog, ma_uint32 level, const char* pFormat, ...) -{ - ma_result result; - va_list args; - - if (pLog == NULL || pFormat == NULL) { - return MA_INVALID_ARGS; - } - - va_start(args, pFormat); - { - result = ma_log_postv(pLog, level, pFormat, args); - } - va_end(args); - - return result; -} - - - -static MA_INLINE ma_uint8 ma_clip_u8(ma_int32 x) -{ - return (ma_uint8)(ma_clamp(x, -128, 127) + 128); -} - -static MA_INLINE ma_int16 ma_clip_s16(ma_int32 x) -{ - return (ma_int16)ma_clamp(x, -32768, 32767); -} - -static MA_INLINE ma_int64 ma_clip_s24(ma_int64 x) -{ - return (ma_int64)ma_clamp(x, -8388608, 8388607); -} - -static MA_INLINE ma_int32 ma_clip_s32(ma_int64 x) -{ - /* This dance is to silence warnings with -std=c89. A good compiler should be able to optimize this away. */ - ma_int64 clipMin; - ma_int64 clipMax; - clipMin = -((ma_int64)2147483647 + 1); - clipMax = (ma_int64)2147483647; - - return (ma_int32)ma_clamp(x, clipMin, clipMax); -} - -static MA_INLINE float ma_clip_f32(float x) -{ - if (x < -1) return -1; - if (x > +1) return +1; - return x; -} - - -static MA_INLINE float ma_mix_f32(float x, float y, float a) -{ - return x*(1-a) + y*a; -} -static MA_INLINE float ma_mix_f32_fast(float x, float y, float a) -{ - float r0 = (y - x); - float r1 = r0*a; - return x + r1; - /*return x + (y - x)*a;*/ -} - -#if defined(MA_SUPPORT_SSE2) -static MA_INLINE __m128 ma_mix_f32_fast__sse2(__m128 x, __m128 y, __m128 a) -{ - return _mm_add_ps(x, _mm_mul_ps(_mm_sub_ps(y, x), a)); -} -#endif -#if defined(MA_SUPPORT_AVX2) -static MA_INLINE __m256 ma_mix_f32_fast__avx2(__m256 x, __m256 y, __m256 a) -{ - return _mm256_add_ps(x, _mm256_mul_ps(_mm256_sub_ps(y, x), a)); -} -#endif -#if defined(MA_SUPPORT_NEON) -static MA_INLINE float32x4_t ma_mix_f32_fast__neon(float32x4_t x, float32x4_t y, float32x4_t a) -{ - return vaddq_f32(x, vmulq_f32(vsubq_f32(y, x), a)); -} -#endif - - -static MA_INLINE double ma_mix_f64(double x, double y, double a) -{ - return x*(1-a) + y*a; -} -static MA_INLINE double ma_mix_f64_fast(double x, double y, double a) -{ - return x + (y - x)*a; -} - -static MA_INLINE float ma_scale_to_range_f32(float x, float lo, float hi) -{ - return lo + x*(hi-lo); -} - - -/* -Greatest common factor using Euclid's algorithm iteratively. -*/ -static MA_INLINE ma_uint32 ma_gcf_u32(ma_uint32 a, ma_uint32 b) -{ - for (;;) { - if (b == 0) { - break; - } else { - ma_uint32 t = a; - a = b; - b = t % a; - } - } - - return a; -} - - -static ma_uint32 ma_ffs_32(ma_uint32 x) -{ - ma_uint32 i; - - /* Just a naive implementation just to get things working for now. Will optimize this later. */ - for (i = 0; i < 32; i += 1) { - if ((x & (1U << i)) != 0) { - return i; - } - } - - return i; -} - -static MA_INLINE ma_int16 ma_float_to_fixed_16(float x) -{ - return (ma_int16)(x * (1 << 8)); -} - - - -/* -Random Number Generation - -miniaudio uses the LCG random number generation algorithm. This is good enough for audio. - -Note that miniaudio's global LCG implementation uses global state which is _not_ thread-local. When this is called across -multiple threads, results will be unpredictable. However, it won't crash and results will still be random enough for -miniaudio's purposes. -*/ -#ifndef MA_DEFAULT_LCG_SEED -#define MA_DEFAULT_LCG_SEED 4321 -#endif - -#define MA_LCG_M 2147483647 -#define MA_LCG_A 48271 -#define MA_LCG_C 0 - -static ma_lcg g_maLCG = {MA_DEFAULT_LCG_SEED}; /* Non-zero initial seed. Use ma_seed() to use an explicit seed. */ - -static MA_INLINE void ma_lcg_seed(ma_lcg* pLCG, ma_int32 seed) -{ - MA_ASSERT(pLCG != NULL); - pLCG->state = seed; -} - -static MA_INLINE ma_int32 ma_lcg_rand_s32(ma_lcg* pLCG) -{ - pLCG->state = (MA_LCG_A * pLCG->state + MA_LCG_C) % MA_LCG_M; - return pLCG->state; -} - -static MA_INLINE ma_uint32 ma_lcg_rand_u32(ma_lcg* pLCG) -{ - return (ma_uint32)ma_lcg_rand_s32(pLCG); -} - -static MA_INLINE ma_int16 ma_lcg_rand_s16(ma_lcg* pLCG) -{ - return (ma_int16)(ma_lcg_rand_s32(pLCG) & 0xFFFF); -} - -static MA_INLINE double ma_lcg_rand_f64(ma_lcg* pLCG) -{ - return ma_lcg_rand_s32(pLCG) / (double)0x7FFFFFFF; -} - -static MA_INLINE float ma_lcg_rand_f32(ma_lcg* pLCG) -{ - return (float)ma_lcg_rand_f64(pLCG); -} - -static MA_INLINE float ma_lcg_rand_range_f32(ma_lcg* pLCG, float lo, float hi) -{ - return ma_scale_to_range_f32(ma_lcg_rand_f32(pLCG), lo, hi); -} - -static MA_INLINE ma_int32 ma_lcg_rand_range_s32(ma_lcg* pLCG, ma_int32 lo, ma_int32 hi) -{ - if (lo == hi) { - return lo; - } - - return lo + ma_lcg_rand_u32(pLCG) / (0xFFFFFFFF / (hi - lo + 1) + 1); -} - - - -static MA_INLINE void ma_seed(ma_int32 seed) -{ - ma_lcg_seed(&g_maLCG, seed); -} - -static MA_INLINE ma_int32 ma_rand_s32(void) -{ - return ma_lcg_rand_s32(&g_maLCG); -} - -static MA_INLINE ma_uint32 ma_rand_u32(void) -{ - return ma_lcg_rand_u32(&g_maLCG); -} - -static MA_INLINE double ma_rand_f64(void) -{ - return ma_lcg_rand_f64(&g_maLCG); -} - -static MA_INLINE float ma_rand_f32(void) -{ - return ma_lcg_rand_f32(&g_maLCG); -} - -static MA_INLINE float ma_rand_range_f32(float lo, float hi) -{ - return ma_lcg_rand_range_f32(&g_maLCG, lo, hi); -} - -static MA_INLINE ma_int32 ma_rand_range_s32(ma_int32 lo, ma_int32 hi) -{ - return ma_lcg_rand_range_s32(&g_maLCG, lo, hi); -} - - -static MA_INLINE float ma_dither_f32_rectangle(float ditherMin, float ditherMax) -{ - return ma_rand_range_f32(ditherMin, ditherMax); -} - -static MA_INLINE float ma_dither_f32_triangle(float ditherMin, float ditherMax) -{ - float a = ma_rand_range_f32(ditherMin, 0); - float b = ma_rand_range_f32(0, ditherMax); - return a + b; -} - -static MA_INLINE float ma_dither_f32(ma_dither_mode ditherMode, float ditherMin, float ditherMax) -{ - if (ditherMode == ma_dither_mode_rectangle) { - return ma_dither_f32_rectangle(ditherMin, ditherMax); - } - if (ditherMode == ma_dither_mode_triangle) { - return ma_dither_f32_triangle(ditherMin, ditherMax); - } - - return 0; -} - -static MA_INLINE ma_int32 ma_dither_s32(ma_dither_mode ditherMode, ma_int32 ditherMin, ma_int32 ditherMax) -{ - if (ditherMode == ma_dither_mode_rectangle) { - ma_int32 a = ma_rand_range_s32(ditherMin, ditherMax); - return a; - } - if (ditherMode == ma_dither_mode_triangle) { - ma_int32 a = ma_rand_range_s32(ditherMin, 0); - ma_int32 b = ma_rand_range_s32(0, ditherMax); - return a + b; - } - - return 0; -} - - -/************************************************************************************************************************************************************** - -Atomics - -**************************************************************************************************************************************************************/ -/* c89atomic.h begin */ -#ifndef ma_atomic_h -#if defined(__cplusplus) -extern "C" { -#endif -#if defined(__clang__) || (defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6))) - #pragma GCC diagnostic push - #pragma GCC diagnostic ignored "-Wlong-long" - #if defined(__clang__) - #pragma GCC diagnostic ignored "-Wc++11-long-long" - #endif -#endif -typedef int ma_atomic_memory_order; -#define MA_ATOMIC_HAS_8 -#define MA_ATOMIC_HAS_16 -#define MA_ATOMIC_HAS_32 -#define MA_ATOMIC_HAS_64 -#if (defined(_MSC_VER) ) || defined(__WATCOMC__) || defined(__DMC__) - #define MA_ATOMIC_MSVC_ARM_INTRINSIC(dst, src, order, intrin, ma_atomicType, msvcType) \ - ma_atomicType result; \ - switch (order) \ - { \ - case ma_atomic_memory_order_relaxed: \ - { \ - result = (ma_atomicType)intrin##_nf((volatile msvcType*)dst, (msvcType)src); \ - } break; \ - case ma_atomic_memory_order_consume: \ - case ma_atomic_memory_order_acquire: \ - { \ - result = (ma_atomicType)intrin##_acq((volatile msvcType*)dst, (msvcType)src); \ - } break; \ - case ma_atomic_memory_order_release: \ - { \ - result = (ma_atomicType)intrin##_rel((volatile msvcType*)dst, (msvcType)src); \ - } break; \ - case ma_atomic_memory_order_acq_rel: \ - case ma_atomic_memory_order_seq_cst: \ - default: \ - { \ - result = (ma_atomicType)intrin((volatile msvcType*)dst, (msvcType)src); \ - } break; \ - } \ - return result; - #define MA_ATOMIC_MSVC_ARM_INTRINSIC_COMPARE_EXCHANGE(ptr, expected, desired, order, intrin, ma_atomicType, msvcType) \ - ma_atomicType result; \ - switch (order) \ - { \ - case ma_atomic_memory_order_relaxed: \ - { \ - result = (ma_atomicType)intrin##_nf((volatile msvcType*)ptr, (msvcType)expected, (msvcType)desired); \ - } break; \ - case ma_atomic_memory_order_consume: \ - case ma_atomic_memory_order_acquire: \ - { \ - result = (ma_atomicType)intrin##_acq((volatile msvcType*)ptr, (msvcType)expected, (msvcType)desired); \ - } break; \ - case ma_atomic_memory_order_release: \ - { \ - result = (ma_atomicType)intrin##_rel((volatile msvcType*)ptr, (msvcType)expected, (msvcType)desired); \ - } break; \ - case ma_atomic_memory_order_acq_rel: \ - case ma_atomic_memory_order_seq_cst: \ - default: \ - { \ - result = (ma_atomicType)intrin((volatile msvcType*)ptr, (msvcType)expected, (msvcType)desired); \ - } break; \ - } \ - return result; - #define ma_atomic_memory_order_relaxed 0 - #define ma_atomic_memory_order_consume 1 - #define ma_atomic_memory_order_acquire 2 - #define ma_atomic_memory_order_release 3 - #define ma_atomic_memory_order_acq_rel 4 - #define ma_atomic_memory_order_seq_cst 5 - #if _MSC_VER < 1600 && defined(MA_X86) - #define MA_ATOMIC_MSVC_USE_INLINED_ASSEMBLY - #endif - #if _MSC_VER < 1600 - #undef MA_ATOMIC_HAS_8 - #undef MA_ATOMIC_HAS_16 - #endif - #if !defined(MA_ATOMIC_MSVC_USE_INLINED_ASSEMBLY) - #include - #endif - #if defined(MA_ATOMIC_MSVC_USE_INLINED_ASSEMBLY) - #if defined(MA_ATOMIC_HAS_8) - static MA_INLINE ma_uint8 __stdcall ma_atomic_compare_and_swap_8(volatile ma_uint8* dst, ma_uint8 expected, ma_uint8 desired) - { - ma_uint8 result = 0; - __asm { - mov ecx, dst - mov al, expected - mov dl, desired - lock cmpxchg [ecx], dl - mov result, al - } - return result; - } - #endif - #if defined(MA_ATOMIC_HAS_16) - static MA_INLINE ma_uint16 __stdcall ma_atomic_compare_and_swap_16(volatile ma_uint16* dst, ma_uint16 expected, ma_uint16 desired) - { - ma_uint16 result = 0; - __asm { - mov ecx, dst - mov ax, expected - mov dx, desired - lock cmpxchg [ecx], dx - mov result, ax - } - return result; - } - #endif - #if defined(MA_ATOMIC_HAS_32) - static MA_INLINE ma_uint32 __stdcall ma_atomic_compare_and_swap_32(volatile ma_uint32* dst, ma_uint32 expected, ma_uint32 desired) - { - ma_uint32 result = 0; - __asm { - mov ecx, dst - mov eax, expected - mov edx, desired - lock cmpxchg [ecx], edx - mov result, eax - } - return result; - } - #endif - #if defined(MA_ATOMIC_HAS_64) - static MA_INLINE ma_uint64 __stdcall ma_atomic_compare_and_swap_64(volatile ma_uint64* dst, ma_uint64 expected, ma_uint64 desired) - { - ma_uint32 resultEAX = 0; - ma_uint32 resultEDX = 0; - __asm { - mov esi, dst - mov eax, dword ptr expected - mov edx, dword ptr expected + 4 - mov ebx, dword ptr desired - mov ecx, dword ptr desired + 4 - lock cmpxchg8b qword ptr [esi] - mov resultEAX, eax - mov resultEDX, edx - } - return ((ma_uint64)resultEDX << 32) | resultEAX; - } - #endif - #else - #if defined(MA_ATOMIC_HAS_8) - #define ma_atomic_compare_and_swap_8( dst, expected, desired) (ma_uint8 )_InterlockedCompareExchange8((volatile char*)dst, (char)desired, (char)expected) - #endif - #if defined(MA_ATOMIC_HAS_16) - #define ma_atomic_compare_and_swap_16(dst, expected, desired) (ma_uint16)_InterlockedCompareExchange16((volatile short*)dst, (short)desired, (short)expected) - #endif - #if defined(MA_ATOMIC_HAS_32) - #define ma_atomic_compare_and_swap_32(dst, expected, desired) (ma_uint32)_InterlockedCompareExchange((volatile long*)dst, (long)desired, (long)expected) - #endif - #if defined(MA_ATOMIC_HAS_64) - #define ma_atomic_compare_and_swap_64(dst, expected, desired) (ma_uint64)_InterlockedCompareExchange64((volatile ma_int64*)dst, (ma_int64)desired, (ma_int64)expected) - #endif - #endif - #if defined(MA_ATOMIC_MSVC_USE_INLINED_ASSEMBLY) - #if defined(MA_ATOMIC_HAS_8) - static MA_INLINE ma_uint8 __stdcall ma_atomic_exchange_explicit_8(volatile ma_uint8* dst, ma_uint8 src, ma_atomic_memory_order order) - { - ma_uint8 result = 0; - (void)order; - __asm { - mov ecx, dst - mov al, src - lock xchg [ecx], al - mov result, al - } - return result; - } - #endif - #if defined(MA_ATOMIC_HAS_16) - static MA_INLINE ma_uint16 __stdcall ma_atomic_exchange_explicit_16(volatile ma_uint16* dst, ma_uint16 src, ma_atomic_memory_order order) - { - ma_uint16 result = 0; - (void)order; - __asm { - mov ecx, dst - mov ax, src - lock xchg [ecx], ax - mov result, ax - } - return result; - } - #endif - #if defined(MA_ATOMIC_HAS_32) - static MA_INLINE ma_uint32 __stdcall ma_atomic_exchange_explicit_32(volatile ma_uint32* dst, ma_uint32 src, ma_atomic_memory_order order) - { - ma_uint32 result = 0; - (void)order; - __asm { - mov ecx, dst - mov eax, src - lock xchg [ecx], eax - mov result, eax - } - return result; - } - #endif - #else - #if defined(MA_ATOMIC_HAS_8) - static MA_INLINE ma_uint8 __stdcall ma_atomic_exchange_explicit_8(volatile ma_uint8* dst, ma_uint8 src, ma_atomic_memory_order order) - { - #if defined(MA_ARM) - MA_ATOMIC_MSVC_ARM_INTRINSIC(dst, src, order, _InterlockedExchange8, ma_uint8, char); - #else - (void)order; - return (ma_uint8)_InterlockedExchange8((volatile char*)dst, (char)src); - #endif - } - #endif - #if defined(MA_ATOMIC_HAS_16) - static MA_INLINE ma_uint16 __stdcall ma_atomic_exchange_explicit_16(volatile ma_uint16* dst, ma_uint16 src, ma_atomic_memory_order order) - { - #if defined(MA_ARM) - MA_ATOMIC_MSVC_ARM_INTRINSIC(dst, src, order, _InterlockedExchange16, ma_uint16, short); - #else - (void)order; - return (ma_uint16)_InterlockedExchange16((volatile short*)dst, (short)src); - #endif - } - #endif - #if defined(MA_ATOMIC_HAS_32) - static MA_INLINE ma_uint32 __stdcall ma_atomic_exchange_explicit_32(volatile ma_uint32* dst, ma_uint32 src, ma_atomic_memory_order order) - { - #if defined(MA_ARM) - MA_ATOMIC_MSVC_ARM_INTRINSIC(dst, src, order, _InterlockedExchange, ma_uint32, long); - #else - (void)order; - return (ma_uint32)_InterlockedExchange((volatile long*)dst, (long)src); - #endif - } - #endif - #if defined(MA_ATOMIC_HAS_64) && defined(MA_64BIT) - static MA_INLINE ma_uint64 __stdcall ma_atomic_exchange_explicit_64(volatile ma_uint64* dst, ma_uint64 src, ma_atomic_memory_order order) - { - #if defined(MA_ARM) - MA_ATOMIC_MSVC_ARM_INTRINSIC(dst, src, order, _InterlockedExchange64, ma_uint64, long long); - #else - (void)order; - return (ma_uint64)_InterlockedExchange64((volatile long long*)dst, (long long)src); - #endif - } - #else - #endif - #endif - #if defined(MA_ATOMIC_HAS_64) && !defined(MA_64BIT) - static MA_INLINE ma_uint64 __stdcall ma_atomic_exchange_explicit_64(volatile ma_uint64* dst, ma_uint64 src, ma_atomic_memory_order order) - { - ma_uint64 oldValue; - do { - oldValue = *dst; - } while (ma_atomic_compare_and_swap_64(dst, oldValue, src) != oldValue); - (void)order; - return oldValue; - } - #endif - #if defined(MA_ATOMIC_MSVC_USE_INLINED_ASSEMBLY) - #if defined(MA_ATOMIC_HAS_8) - static MA_INLINE ma_uint8 __stdcall ma_atomic_fetch_add_explicit_8(volatile ma_uint8* dst, ma_uint8 src, ma_atomic_memory_order order) - { - ma_uint8 result = 0; - (void)order; - __asm { - mov ecx, dst - mov al, src - lock xadd [ecx], al - mov result, al - } - return result; - } - #endif - #if defined(MA_ATOMIC_HAS_16) - static MA_INLINE ma_uint16 __stdcall ma_atomic_fetch_add_explicit_16(volatile ma_uint16* dst, ma_uint16 src, ma_atomic_memory_order order) - { - ma_uint16 result = 0; - (void)order; - __asm { - mov ecx, dst - mov ax, src - lock xadd [ecx], ax - mov result, ax - } - return result; - } - #endif - #if defined(MA_ATOMIC_HAS_32) - static MA_INLINE ma_uint32 __stdcall ma_atomic_fetch_add_explicit_32(volatile ma_uint32* dst, ma_uint32 src, ma_atomic_memory_order order) - { - ma_uint32 result = 0; - (void)order; - __asm { - mov ecx, dst - mov eax, src - lock xadd [ecx], eax - mov result, eax - } - return result; - } - #endif - #else - #if defined(MA_ATOMIC_HAS_8) - static MA_INLINE ma_uint8 __stdcall ma_atomic_fetch_add_explicit_8(volatile ma_uint8* dst, ma_uint8 src, ma_atomic_memory_order order) - { - #if defined(MA_ARM) - MA_ATOMIC_MSVC_ARM_INTRINSIC(dst, src, order, _InterlockedExchangeAdd8, ma_uint8, char); - #else - (void)order; - return (ma_uint8)_InterlockedExchangeAdd8((volatile char*)dst, (char)src); - #endif - } - #endif - #if defined(MA_ATOMIC_HAS_16) - static MA_INLINE ma_uint16 __stdcall ma_atomic_fetch_add_explicit_16(volatile ma_uint16* dst, ma_uint16 src, ma_atomic_memory_order order) - { - #if defined(MA_ARM) - MA_ATOMIC_MSVC_ARM_INTRINSIC(dst, src, order, _InterlockedExchangeAdd16, ma_uint16, short); - #else - (void)order; - return (ma_uint16)_InterlockedExchangeAdd16((volatile short*)dst, (short)src); - #endif - } - #endif - #if defined(MA_ATOMIC_HAS_32) - static MA_INLINE ma_uint32 __stdcall ma_atomic_fetch_add_explicit_32(volatile ma_uint32* dst, ma_uint32 src, ma_atomic_memory_order order) - { - #if defined(MA_ARM) - MA_ATOMIC_MSVC_ARM_INTRINSIC(dst, src, order, _InterlockedExchangeAdd, ma_uint32, long); - #else - (void)order; - return (ma_uint32)_InterlockedExchangeAdd((volatile long*)dst, (long)src); - #endif - } - #endif - #if defined(MA_ATOMIC_HAS_64) && defined(MA_64BIT) - static MA_INLINE ma_uint64 __stdcall ma_atomic_fetch_add_explicit_64(volatile ma_uint64* dst, ma_uint64 src, ma_atomic_memory_order order) - { - #if defined(MA_ARM) - MA_ATOMIC_MSVC_ARM_INTRINSIC(dst, src, order, _InterlockedExchangeAdd64, ma_uint64, long long); - #else - (void)order; - return (ma_uint64)_InterlockedExchangeAdd64((volatile long long*)dst, (long long)src); - #endif - } - #else - #endif - #endif - #if defined(MA_ATOMIC_HAS_64) && !defined(MA_64BIT) - static MA_INLINE ma_uint64 __stdcall ma_atomic_fetch_add_explicit_64(volatile ma_uint64* dst, ma_uint64 src, ma_atomic_memory_order order) - { - ma_uint64 oldValue; - ma_uint64 newValue; - do { - oldValue = *dst; - newValue = oldValue + src; - } while (ma_atomic_compare_and_swap_64(dst, oldValue, newValue) != oldValue); - (void)order; - return oldValue; - } - #endif - #if defined(MA_ATOMIC_MSVC_USE_INLINED_ASSEMBLY) - static MA_INLINE void __stdcall ma_atomic_thread_fence(ma_atomic_memory_order order) - { - (void)order; - __asm { - lock add [esp], 0 - } - } - #else - #if defined(MA_X64) - #define ma_atomic_thread_fence(order) __faststorefence(), (void)order - #elif defined(MA_ARM64) - #define ma_atomic_thread_fence(order) __dmb(_ARM64_BARRIER_ISH), (void)order - #else - static MA_INLINE void ma_atomic_thread_fence(ma_atomic_memory_order order) - { - volatile ma_uint32 barrier = 0; - ma_atomic_fetch_add_explicit_32(&barrier, 0, order); - } - #endif - #endif - #define ma_atomic_compiler_fence() ma_atomic_thread_fence(ma_atomic_memory_order_seq_cst) - #define ma_atomic_signal_fence(order) ma_atomic_thread_fence(order) - #if defined(MA_ATOMIC_HAS_8) - static MA_INLINE ma_uint8 ma_atomic_load_explicit_8(volatile const ma_uint8* ptr, ma_atomic_memory_order order) - { - #if defined(MA_ARM) - MA_ATOMIC_MSVC_ARM_INTRINSIC_COMPARE_EXCHANGE(ptr, 0, 0, order, _InterlockedCompareExchange8, ma_uint8, char); - #else - (void)order; - return ma_atomic_compare_and_swap_8((volatile ma_uint8*)ptr, 0, 0); - #endif - } - #endif - #if defined(MA_ATOMIC_HAS_16) - static MA_INLINE ma_uint16 ma_atomic_load_explicit_16(volatile const ma_uint16* ptr, ma_atomic_memory_order order) - { - #if defined(MA_ARM) - MA_ATOMIC_MSVC_ARM_INTRINSIC_COMPARE_EXCHANGE(ptr, 0, 0, order, _InterlockedCompareExchange16, ma_uint16, short); - #else - (void)order; - return ma_atomic_compare_and_swap_16((volatile ma_uint16*)ptr, 0, 0); - #endif - } - #endif - #if defined(MA_ATOMIC_HAS_32) - static MA_INLINE ma_uint32 ma_atomic_load_explicit_32(volatile const ma_uint32* ptr, ma_atomic_memory_order order) - { - #if defined(MA_ARM) - MA_ATOMIC_MSVC_ARM_INTRINSIC_COMPARE_EXCHANGE(ptr, 0, 0, order, _InterlockedCompareExchange, ma_uint32, long); - #else - (void)order; - return ma_atomic_compare_and_swap_32((volatile ma_uint32*)ptr, 0, 0); - #endif - } - #endif - #if defined(MA_ATOMIC_HAS_64) - static MA_INLINE ma_uint64 ma_atomic_load_explicit_64(volatile const ma_uint64* ptr, ma_atomic_memory_order order) - { - #if defined(MA_ARM) - MA_ATOMIC_MSVC_ARM_INTRINSIC_COMPARE_EXCHANGE(ptr, 0, 0, order, _InterlockedCompareExchange64, ma_uint64, long long); - #else - (void)order; - return ma_atomic_compare_and_swap_64((volatile ma_uint64*)ptr, 0, 0); - #endif - } - #endif - #if defined(MA_ATOMIC_HAS_8) - #define ma_atomic_store_explicit_8( dst, src, order) (void)ma_atomic_exchange_explicit_8 (dst, src, order) - #endif - #if defined(MA_ATOMIC_HAS_16) - #define ma_atomic_store_explicit_16(dst, src, order) (void)ma_atomic_exchange_explicit_16(dst, src, order) - #endif - #if defined(MA_ATOMIC_HAS_32) - #define ma_atomic_store_explicit_32(dst, src, order) (void)ma_atomic_exchange_explicit_32(dst, src, order) - #endif - #if defined(MA_ATOMIC_HAS_64) - #define ma_atomic_store_explicit_64(dst, src, order) (void)ma_atomic_exchange_explicit_64(dst, src, order) - #endif - #if defined(MA_ATOMIC_HAS_8) - static MA_INLINE ma_uint8 __stdcall ma_atomic_fetch_sub_explicit_8(volatile ma_uint8* dst, ma_uint8 src, ma_atomic_memory_order order) - { - ma_uint8 oldValue; - ma_uint8 newValue; - do { - oldValue = *dst; - newValue = (ma_uint8)(oldValue - src); - } while (ma_atomic_compare_and_swap_8(dst, oldValue, newValue) != oldValue); - (void)order; - return oldValue; - } - #endif - #if defined(MA_ATOMIC_HAS_16) - static MA_INLINE ma_uint16 __stdcall ma_atomic_fetch_sub_explicit_16(volatile ma_uint16* dst, ma_uint16 src, ma_atomic_memory_order order) - { - ma_uint16 oldValue; - ma_uint16 newValue; - do { - oldValue = *dst; - newValue = (ma_uint16)(oldValue - src); - } while (ma_atomic_compare_and_swap_16(dst, oldValue, newValue) != oldValue); - (void)order; - return oldValue; - } - #endif - #if defined(MA_ATOMIC_HAS_32) - static MA_INLINE ma_uint32 __stdcall ma_atomic_fetch_sub_explicit_32(volatile ma_uint32* dst, ma_uint32 src, ma_atomic_memory_order order) - { - ma_uint32 oldValue; - ma_uint32 newValue; - do { - oldValue = *dst; - newValue = oldValue - src; - } while (ma_atomic_compare_and_swap_32(dst, oldValue, newValue) != oldValue); - (void)order; - return oldValue; - } - #endif - #if defined(MA_ATOMIC_HAS_64) - static MA_INLINE ma_uint64 __stdcall ma_atomic_fetch_sub_explicit_64(volatile ma_uint64* dst, ma_uint64 src, ma_atomic_memory_order order) - { - ma_uint64 oldValue; - ma_uint64 newValue; - do { - oldValue = *dst; - newValue = oldValue - src; - } while (ma_atomic_compare_and_swap_64(dst, oldValue, newValue) != oldValue); - (void)order; - return oldValue; - } - #endif - #if defined(MA_ATOMIC_HAS_8) - static MA_INLINE ma_uint8 __stdcall ma_atomic_fetch_and_explicit_8(volatile ma_uint8* dst, ma_uint8 src, ma_atomic_memory_order order) - { - #if defined(MA_ARM) - MA_ATOMIC_MSVC_ARM_INTRINSIC(dst, src, order, _InterlockedAnd8, ma_uint8, char); - #else - ma_uint8 oldValue; - ma_uint8 newValue; - do { - oldValue = *dst; - newValue = (ma_uint8)(oldValue & src); - } while (ma_atomic_compare_and_swap_8(dst, oldValue, newValue) != oldValue); - (void)order; - return oldValue; - #endif - } - #endif - #if defined(MA_ATOMIC_HAS_16) - static MA_INLINE ma_uint16 __stdcall ma_atomic_fetch_and_explicit_16(volatile ma_uint16* dst, ma_uint16 src, ma_atomic_memory_order order) - { - #if defined(MA_ARM) - MA_ATOMIC_MSVC_ARM_INTRINSIC(dst, src, order, _InterlockedAnd16, ma_uint16, short); - #else - ma_uint16 oldValue; - ma_uint16 newValue; - do { - oldValue = *dst; - newValue = (ma_uint16)(oldValue & src); - } while (ma_atomic_compare_and_swap_16(dst, oldValue, newValue) != oldValue); - (void)order; - return oldValue; - #endif - } - #endif - #if defined(MA_ATOMIC_HAS_32) - static MA_INLINE ma_uint32 __stdcall ma_atomic_fetch_and_explicit_32(volatile ma_uint32* dst, ma_uint32 src, ma_atomic_memory_order order) - { - #if defined(MA_ARM) - MA_ATOMIC_MSVC_ARM_INTRINSIC(dst, src, order, _InterlockedAnd, ma_uint32, long); - #else - ma_uint32 oldValue; - ma_uint32 newValue; - do { - oldValue = *dst; - newValue = oldValue & src; - } while (ma_atomic_compare_and_swap_32(dst, oldValue, newValue) != oldValue); - (void)order; - return oldValue; - #endif - } - #endif - #if defined(MA_ATOMIC_HAS_64) - static MA_INLINE ma_uint64 __stdcall ma_atomic_fetch_and_explicit_64(volatile ma_uint64* dst, ma_uint64 src, ma_atomic_memory_order order) - { - #if defined(MA_ARM) - MA_ATOMIC_MSVC_ARM_INTRINSIC(dst, src, order, _InterlockedAnd64, ma_uint64, long long); - #else - ma_uint64 oldValue; - ma_uint64 newValue; - do { - oldValue = *dst; - newValue = oldValue & src; - } while (ma_atomic_compare_and_swap_64(dst, oldValue, newValue) != oldValue); - (void)order; - return oldValue; - #endif - } - #endif - #if defined(MA_ATOMIC_HAS_8) - static MA_INLINE ma_uint8 __stdcall ma_atomic_fetch_xor_explicit_8(volatile ma_uint8* dst, ma_uint8 src, ma_atomic_memory_order order) - { - #if defined(MA_ARM) - MA_ATOMIC_MSVC_ARM_INTRINSIC(dst, src, order, _InterlockedXor8, ma_uint8, char); - #else - ma_uint8 oldValue; - ma_uint8 newValue; - do { - oldValue = *dst; - newValue = (ma_uint8)(oldValue ^ src); - } while (ma_atomic_compare_and_swap_8(dst, oldValue, newValue) != oldValue); - (void)order; - return oldValue; - #endif - } - #endif - #if defined(MA_ATOMIC_HAS_16) - static MA_INLINE ma_uint16 __stdcall ma_atomic_fetch_xor_explicit_16(volatile ma_uint16* dst, ma_uint16 src, ma_atomic_memory_order order) - { - #if defined(MA_ARM) - MA_ATOMIC_MSVC_ARM_INTRINSIC(dst, src, order, _InterlockedXor16, ma_uint16, short); - #else - ma_uint16 oldValue; - ma_uint16 newValue; - do { - oldValue = *dst; - newValue = (ma_uint16)(oldValue ^ src); - } while (ma_atomic_compare_and_swap_16(dst, oldValue, newValue) != oldValue); - (void)order; - return oldValue; - #endif - } - #endif - #if defined(MA_ATOMIC_HAS_32) - static MA_INLINE ma_uint32 __stdcall ma_atomic_fetch_xor_explicit_32(volatile ma_uint32* dst, ma_uint32 src, ma_atomic_memory_order order) - { - #if defined(MA_ARM) - MA_ATOMIC_MSVC_ARM_INTRINSIC(dst, src, order, _InterlockedXor, ma_uint32, long); - #else - ma_uint32 oldValue; - ma_uint32 newValue; - do { - oldValue = *dst; - newValue = oldValue ^ src; - } while (ma_atomic_compare_and_swap_32(dst, oldValue, newValue) != oldValue); - (void)order; - return oldValue; - #endif - } - #endif - #if defined(MA_ATOMIC_HAS_64) - static MA_INLINE ma_uint64 __stdcall ma_atomic_fetch_xor_explicit_64(volatile ma_uint64* dst, ma_uint64 src, ma_atomic_memory_order order) - { - #if defined(MA_ARM) - MA_ATOMIC_MSVC_ARM_INTRINSIC(dst, src, order, _InterlockedXor64, ma_uint64, long long); - #else - ma_uint64 oldValue; - ma_uint64 newValue; - do { - oldValue = *dst; - newValue = oldValue ^ src; - } while (ma_atomic_compare_and_swap_64(dst, oldValue, newValue) != oldValue); - (void)order; - return oldValue; - #endif - } - #endif - #if defined(MA_ATOMIC_HAS_8) - static MA_INLINE ma_uint8 __stdcall ma_atomic_fetch_or_explicit_8(volatile ma_uint8* dst, ma_uint8 src, ma_atomic_memory_order order) - { - #if defined(MA_ARM) - MA_ATOMIC_MSVC_ARM_INTRINSIC(dst, src, order, _InterlockedOr8, ma_uint8, char); - #else - ma_uint8 oldValue; - ma_uint8 newValue; - do { - oldValue = *dst; - newValue = (ma_uint8)(oldValue | src); - } while (ma_atomic_compare_and_swap_8(dst, oldValue, newValue) != oldValue); - (void)order; - return oldValue; - #endif - } - #endif - #if defined(MA_ATOMIC_HAS_16) - static MA_INLINE ma_uint16 __stdcall ma_atomic_fetch_or_explicit_16(volatile ma_uint16* dst, ma_uint16 src, ma_atomic_memory_order order) - { - #if defined(MA_ARM) - MA_ATOMIC_MSVC_ARM_INTRINSIC(dst, src, order, _InterlockedOr16, ma_uint16, short); - #else - ma_uint16 oldValue; - ma_uint16 newValue; - do { - oldValue = *dst; - newValue = (ma_uint16)(oldValue | src); - } while (ma_atomic_compare_and_swap_16(dst, oldValue, newValue) != oldValue); - (void)order; - return oldValue; - #endif - } - #endif - #if defined(MA_ATOMIC_HAS_32) - static MA_INLINE ma_uint32 __stdcall ma_atomic_fetch_or_explicit_32(volatile ma_uint32* dst, ma_uint32 src, ma_atomic_memory_order order) - { - #if defined(MA_ARM) - MA_ATOMIC_MSVC_ARM_INTRINSIC(dst, src, order, _InterlockedOr, ma_uint32, long); - #else - ma_uint32 oldValue; - ma_uint32 newValue; - do { - oldValue = *dst; - newValue = oldValue | src; - } while (ma_atomic_compare_and_swap_32(dst, oldValue, newValue) != oldValue); - (void)order; - return oldValue; - #endif - } - #endif - #if defined(MA_ATOMIC_HAS_64) - static MA_INLINE ma_uint64 __stdcall ma_atomic_fetch_or_explicit_64(volatile ma_uint64* dst, ma_uint64 src, ma_atomic_memory_order order) - { - #if defined(MA_ARM) - MA_ATOMIC_MSVC_ARM_INTRINSIC(dst, src, order, _InterlockedOr64, ma_uint64, long long); - #else - ma_uint64 oldValue; - ma_uint64 newValue; - do { - oldValue = *dst; - newValue = oldValue | src; - } while (ma_atomic_compare_and_swap_64(dst, oldValue, newValue) != oldValue); - (void)order; - return oldValue; - #endif - } - #endif - #if defined(MA_ATOMIC_HAS_8) - #define ma_atomic_test_and_set_explicit_8( dst, order) ma_atomic_exchange_explicit_8 (dst, 1, order) - #endif - #if defined(MA_ATOMIC_HAS_16) - #define ma_atomic_test_and_set_explicit_16(dst, order) ma_atomic_exchange_explicit_16(dst, 1, order) - #endif - #if defined(MA_ATOMIC_HAS_32) - #define ma_atomic_test_and_set_explicit_32(dst, order) ma_atomic_exchange_explicit_32(dst, 1, order) - #endif - #if defined(MA_ATOMIC_HAS_64) - #define ma_atomic_test_and_set_explicit_64(dst, order) ma_atomic_exchange_explicit_64(dst, 1, order) - #endif - #if defined(MA_ATOMIC_HAS_8) - #define ma_atomic_clear_explicit_8( dst, order) ma_atomic_store_explicit_8 (dst, 0, order) - #endif - #if defined(MA_ATOMIC_HAS_16) - #define ma_atomic_clear_explicit_16(dst, order) ma_atomic_store_explicit_16(dst, 0, order) - #endif - #if defined(MA_ATOMIC_HAS_32) - #define ma_atomic_clear_explicit_32(dst, order) ma_atomic_store_explicit_32(dst, 0, order) - #endif - #if defined(MA_ATOMIC_HAS_64) - #define ma_atomic_clear_explicit_64(dst, order) ma_atomic_store_explicit_64(dst, 0, order) - #endif - #if defined(MA_ATOMIC_HAS_8) - typedef ma_uint8 ma_atomic_flag; - #define ma_atomic_flag_test_and_set_explicit(ptr, order) (ma_bool32)ma_atomic_test_and_set_explicit_8(ptr, order) - #define ma_atomic_flag_clear_explicit(ptr, order) ma_atomic_clear_explicit_8(ptr, order) - #define ma_atomic_flag_load_explicit(ptr, order) ma_atomic_load_explicit_8(ptr, order) - #else - typedef ma_uint32 ma_atomic_flag; - #define ma_atomic_flag_test_and_set_explicit(ptr, order) (ma_bool32)ma_atomic_test_and_set_explicit_32(ptr, order) - #define ma_atomic_flag_clear_explicit(ptr, order) ma_atomic_clear_explicit_32(ptr, order) - #define ma_atomic_flag_load_explicit(ptr, order) ma_atomic_load_explicit_32(ptr, order) - #endif -#elif defined(__clang__) || (defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 7))) - #define MA_ATOMIC_HAS_NATIVE_COMPARE_EXCHANGE - #define MA_ATOMIC_HAS_NATIVE_IS_LOCK_FREE - #define ma_atomic_memory_order_relaxed __ATOMIC_RELAXED - #define ma_atomic_memory_order_consume __ATOMIC_CONSUME - #define ma_atomic_memory_order_acquire __ATOMIC_ACQUIRE - #define ma_atomic_memory_order_release __ATOMIC_RELEASE - #define ma_atomic_memory_order_acq_rel __ATOMIC_ACQ_REL - #define ma_atomic_memory_order_seq_cst __ATOMIC_SEQ_CST - #define ma_atomic_compiler_fence() __asm__ __volatile__("":::"memory") - #define ma_atomic_thread_fence(order) __atomic_thread_fence(order) - #define ma_atomic_signal_fence(order) __atomic_signal_fence(order) - #define ma_atomic_is_lock_free_8(ptr) __atomic_is_lock_free(1, ptr) - #define ma_atomic_is_lock_free_16(ptr) __atomic_is_lock_free(2, ptr) - #define ma_atomic_is_lock_free_32(ptr) __atomic_is_lock_free(4, ptr) - #define ma_atomic_is_lock_free_64(ptr) __atomic_is_lock_free(8, ptr) - #define ma_atomic_test_and_set_explicit_8( dst, order) __atomic_exchange_n(dst, 1, order) - #define ma_atomic_test_and_set_explicit_16(dst, order) __atomic_exchange_n(dst, 1, order) - #define ma_atomic_test_and_set_explicit_32(dst, order) __atomic_exchange_n(dst, 1, order) - #define ma_atomic_test_and_set_explicit_64(dst, order) __atomic_exchange_n(dst, 1, order) - #define ma_atomic_clear_explicit_8( dst, order) __atomic_store_n(dst, 0, order) - #define ma_atomic_clear_explicit_16(dst, order) __atomic_store_n(dst, 0, order) - #define ma_atomic_clear_explicit_32(dst, order) __atomic_store_n(dst, 0, order) - #define ma_atomic_clear_explicit_64(dst, order) __atomic_store_n(dst, 0, order) - #define ma_atomic_store_explicit_8( dst, src, order) __atomic_store_n(dst, src, order) - #define ma_atomic_store_explicit_16(dst, src, order) __atomic_store_n(dst, src, order) - #define ma_atomic_store_explicit_32(dst, src, order) __atomic_store_n(dst, src, order) - #define ma_atomic_store_explicit_64(dst, src, order) __atomic_store_n(dst, src, order) - #define ma_atomic_load_explicit_8( dst, order) __atomic_load_n(dst, order) - #define ma_atomic_load_explicit_16(dst, order) __atomic_load_n(dst, order) - #define ma_atomic_load_explicit_32(dst, order) __atomic_load_n(dst, order) - #define ma_atomic_load_explicit_64(dst, order) __atomic_load_n(dst, order) - #define ma_atomic_exchange_explicit_8( dst, src, order) __atomic_exchange_n(dst, src, order) - #define ma_atomic_exchange_explicit_16(dst, src, order) __atomic_exchange_n(dst, src, order) - #define ma_atomic_exchange_explicit_32(dst, src, order) __atomic_exchange_n(dst, src, order) - #define ma_atomic_exchange_explicit_64(dst, src, order) __atomic_exchange_n(dst, src, order) - #define ma_atomic_compare_exchange_strong_explicit_8( dst, expected, desired, successOrder, failureOrder) __atomic_compare_exchange_n(dst, expected, desired, 0, successOrder, failureOrder) - #define ma_atomic_compare_exchange_strong_explicit_16(dst, expected, desired, successOrder, failureOrder) __atomic_compare_exchange_n(dst, expected, desired, 0, successOrder, failureOrder) - #define ma_atomic_compare_exchange_strong_explicit_32(dst, expected, desired, successOrder, failureOrder) __atomic_compare_exchange_n(dst, expected, desired, 0, successOrder, failureOrder) - #define ma_atomic_compare_exchange_strong_explicit_64(dst, expected, desired, successOrder, failureOrder) __atomic_compare_exchange_n(dst, expected, desired, 0, successOrder, failureOrder) - #define ma_atomic_compare_exchange_weak_explicit_8( dst, expected, desired, successOrder, failureOrder) __atomic_compare_exchange_n(dst, expected, desired, 1, successOrder, failureOrder) - #define ma_atomic_compare_exchange_weak_explicit_16(dst, expected, desired, successOrder, failureOrder) __atomic_compare_exchange_n(dst, expected, desired, 1, successOrder, failureOrder) - #define ma_atomic_compare_exchange_weak_explicit_32(dst, expected, desired, successOrder, failureOrder) __atomic_compare_exchange_n(dst, expected, desired, 1, successOrder, failureOrder) - #define ma_atomic_compare_exchange_weak_explicit_64(dst, expected, desired, successOrder, failureOrder) __atomic_compare_exchange_n(dst, expected, desired, 1, successOrder, failureOrder) - #define ma_atomic_fetch_add_explicit_8( dst, src, order) __atomic_fetch_add(dst, src, order) - #define ma_atomic_fetch_add_explicit_16(dst, src, order) __atomic_fetch_add(dst, src, order) - #define ma_atomic_fetch_add_explicit_32(dst, src, order) __atomic_fetch_add(dst, src, order) - #define ma_atomic_fetch_add_explicit_64(dst, src, order) __atomic_fetch_add(dst, src, order) - #define ma_atomic_fetch_sub_explicit_8( dst, src, order) __atomic_fetch_sub(dst, src, order) - #define ma_atomic_fetch_sub_explicit_16(dst, src, order) __atomic_fetch_sub(dst, src, order) - #define ma_atomic_fetch_sub_explicit_32(dst, src, order) __atomic_fetch_sub(dst, src, order) - #define ma_atomic_fetch_sub_explicit_64(dst, src, order) __atomic_fetch_sub(dst, src, order) - #define ma_atomic_fetch_or_explicit_8( dst, src, order) __atomic_fetch_or(dst, src, order) - #define ma_atomic_fetch_or_explicit_16(dst, src, order) __atomic_fetch_or(dst, src, order) - #define ma_atomic_fetch_or_explicit_32(dst, src, order) __atomic_fetch_or(dst, src, order) - #define ma_atomic_fetch_or_explicit_64(dst, src, order) __atomic_fetch_or(dst, src, order) - #define ma_atomic_fetch_xor_explicit_8( dst, src, order) __atomic_fetch_xor(dst, src, order) - #define ma_atomic_fetch_xor_explicit_16(dst, src, order) __atomic_fetch_xor(dst, src, order) - #define ma_atomic_fetch_xor_explicit_32(dst, src, order) __atomic_fetch_xor(dst, src, order) - #define ma_atomic_fetch_xor_explicit_64(dst, src, order) __atomic_fetch_xor(dst, src, order) - #define ma_atomic_fetch_and_explicit_8( dst, src, order) __atomic_fetch_and(dst, src, order) - #define ma_atomic_fetch_and_explicit_16(dst, src, order) __atomic_fetch_and(dst, src, order) - #define ma_atomic_fetch_and_explicit_32(dst, src, order) __atomic_fetch_and(dst, src, order) - #define ma_atomic_fetch_and_explicit_64(dst, src, order) __atomic_fetch_and(dst, src, order) - static MA_INLINE ma_uint8 ma_atomic_compare_and_swap_8(volatile ma_uint8* dst, ma_uint8 expected, ma_uint8 desired) - { - __atomic_compare_exchange_n(dst, &expected, desired, 0, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST); - return expected; - } - static MA_INLINE ma_uint16 ma_atomic_compare_and_swap_16(volatile ma_uint16* dst, ma_uint16 expected, ma_uint16 desired) - { - __atomic_compare_exchange_n(dst, &expected, desired, 0, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST); - return expected; - } - static MA_INLINE ma_uint32 ma_atomic_compare_and_swap_32(volatile ma_uint32* dst, ma_uint32 expected, ma_uint32 desired) - { - __atomic_compare_exchange_n(dst, &expected, desired, 0, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST); - return expected; - } - #if defined(__clang__) - #pragma clang diagnostic push - #if __clang_major__ >= 8 - #pragma clang diagnostic ignored "-Watomic-alignment" - #endif - #endif - static MA_INLINE ma_uint64 ma_atomic_compare_and_swap_64(volatile ma_uint64* dst, ma_uint64 expected, ma_uint64 desired) - { - __atomic_compare_exchange_n(dst, &expected, desired, 0, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST); - return expected; - } - #if defined(__clang__) - #pragma clang diagnostic pop - #endif - typedef ma_uint8 ma_atomic_flag; - #define ma_atomic_flag_test_and_set_explicit(dst, order) (ma_bool32)__atomic_test_and_set(dst, order) - #define ma_atomic_flag_clear_explicit(dst, order) __atomic_clear(dst, order) - #define ma_atomic_flag_load_explicit(ptr, order) ma_atomic_load_explicit_8(ptr, order) -#else - #define ma_atomic_memory_order_relaxed 1 - #define ma_atomic_memory_order_consume 2 - #define ma_atomic_memory_order_acquire 3 - #define ma_atomic_memory_order_release 4 - #define ma_atomic_memory_order_acq_rel 5 - #define ma_atomic_memory_order_seq_cst 6 - #define ma_atomic_compiler_fence() __asm__ __volatile__("":::"memory") - #if defined(__GNUC__) - #define ma_atomic_thread_fence(order) __sync_synchronize(), (void)order - static MA_INLINE ma_uint8 ma_atomic_exchange_explicit_8(volatile ma_uint8* dst, ma_uint8 src, ma_atomic_memory_order order) - { - if (order > ma_atomic_memory_order_acquire) { - __sync_synchronize(); - } - return __sync_lock_test_and_set(dst, src); - } - static MA_INLINE ma_uint16 ma_atomic_exchange_explicit_16(volatile ma_uint16* dst, ma_uint16 src, ma_atomic_memory_order order) - { - ma_uint16 oldValue; - do { - oldValue = *dst; - } while (__sync_val_compare_and_swap(dst, oldValue, src) != oldValue); - (void)order; - return oldValue; - } - static MA_INLINE ma_uint32 ma_atomic_exchange_explicit_32(volatile ma_uint32* dst, ma_uint32 src, ma_atomic_memory_order order) - { - ma_uint32 oldValue; - do { - oldValue = *dst; - } while (__sync_val_compare_and_swap(dst, oldValue, src) != oldValue); - (void)order; - return oldValue; - } - static MA_INLINE ma_uint64 ma_atomic_exchange_explicit_64(volatile ma_uint64* dst, ma_uint64 src, ma_atomic_memory_order order) - { - ma_uint64 oldValue; - do { - oldValue = *dst; - } while (__sync_val_compare_and_swap(dst, oldValue, src) != oldValue); - (void)order; - return oldValue; - } - static MA_INLINE ma_uint8 ma_atomic_fetch_add_explicit_8(volatile ma_uint8* dst, ma_uint8 src, ma_atomic_memory_order order) - { - (void)order; - return __sync_fetch_and_add(dst, src); - } - static MA_INLINE ma_uint16 ma_atomic_fetch_add_explicit_16(volatile ma_uint16* dst, ma_uint16 src, ma_atomic_memory_order order) - { - (void)order; - return __sync_fetch_and_add(dst, src); - } - static MA_INLINE ma_uint32 ma_atomic_fetch_add_explicit_32(volatile ma_uint32* dst, ma_uint32 src, ma_atomic_memory_order order) - { - (void)order; - return __sync_fetch_and_add(dst, src); - } - static MA_INLINE ma_uint64 ma_atomic_fetch_add_explicit_64(volatile ma_uint64* dst, ma_uint64 src, ma_atomic_memory_order order) - { - (void)order; - return __sync_fetch_and_add(dst, src); - } - static MA_INLINE ma_uint8 ma_atomic_fetch_sub_explicit_8(volatile ma_uint8* dst, ma_uint8 src, ma_atomic_memory_order order) - { - (void)order; - return __sync_fetch_and_sub(dst, src); - } - static MA_INLINE ma_uint16 ma_atomic_fetch_sub_explicit_16(volatile ma_uint16* dst, ma_uint16 src, ma_atomic_memory_order order) - { - (void)order; - return __sync_fetch_and_sub(dst, src); - } - static MA_INLINE ma_uint32 ma_atomic_fetch_sub_explicit_32(volatile ma_uint32* dst, ma_uint32 src, ma_atomic_memory_order order) - { - (void)order; - return __sync_fetch_and_sub(dst, src); - } - static MA_INLINE ma_uint64 ma_atomic_fetch_sub_explicit_64(volatile ma_uint64* dst, ma_uint64 src, ma_atomic_memory_order order) - { - (void)order; - return __sync_fetch_and_sub(dst, src); - } - static MA_INLINE ma_uint8 ma_atomic_fetch_or_explicit_8(volatile ma_uint8* dst, ma_uint8 src, ma_atomic_memory_order order) - { - (void)order; - return __sync_fetch_and_or(dst, src); - } - static MA_INLINE ma_uint16 ma_atomic_fetch_or_explicit_16(volatile ma_uint16* dst, ma_uint16 src, ma_atomic_memory_order order) - { - (void)order; - return __sync_fetch_and_or(dst, src); - } - static MA_INLINE ma_uint32 ma_atomic_fetch_or_explicit_32(volatile ma_uint32* dst, ma_uint32 src, ma_atomic_memory_order order) - { - (void)order; - return __sync_fetch_and_or(dst, src); - } - static MA_INLINE ma_uint64 ma_atomic_fetch_or_explicit_64(volatile ma_uint64* dst, ma_uint64 src, ma_atomic_memory_order order) - { - (void)order; - return __sync_fetch_and_or(dst, src); - } - static MA_INLINE ma_uint8 ma_atomic_fetch_xor_explicit_8(volatile ma_uint8* dst, ma_uint8 src, ma_atomic_memory_order order) - { - (void)order; - return __sync_fetch_and_xor(dst, src); - } - static MA_INLINE ma_uint16 ma_atomic_fetch_xor_explicit_16(volatile ma_uint16* dst, ma_uint16 src, ma_atomic_memory_order order) - { - (void)order; - return __sync_fetch_and_xor(dst, src); - } - static MA_INLINE ma_uint32 ma_atomic_fetch_xor_explicit_32(volatile ma_uint32* dst, ma_uint32 src, ma_atomic_memory_order order) - { - (void)order; - return __sync_fetch_and_xor(dst, src); - } - static MA_INLINE ma_uint64 ma_atomic_fetch_xor_explicit_64(volatile ma_uint64* dst, ma_uint64 src, ma_atomic_memory_order order) - { - (void)order; - return __sync_fetch_and_xor(dst, src); - } - static MA_INLINE ma_uint8 ma_atomic_fetch_and_explicit_8(volatile ma_uint8* dst, ma_uint8 src, ma_atomic_memory_order order) - { - (void)order; - return __sync_fetch_and_and(dst, src); - } - static MA_INLINE ma_uint16 ma_atomic_fetch_and_explicit_16(volatile ma_uint16* dst, ma_uint16 src, ma_atomic_memory_order order) - { - (void)order; - return __sync_fetch_and_and(dst, src); - } - static MA_INLINE ma_uint32 ma_atomic_fetch_and_explicit_32(volatile ma_uint32* dst, ma_uint32 src, ma_atomic_memory_order order) - { - (void)order; - return __sync_fetch_and_and(dst, src); - } - static MA_INLINE ma_uint64 ma_atomic_fetch_and_explicit_64(volatile ma_uint64* dst, ma_uint64 src, ma_atomic_memory_order order) - { - (void)order; - return __sync_fetch_and_and(dst, src); - } - #define ma_atomic_compare_and_swap_8( dst, expected, desired) __sync_val_compare_and_swap(dst, expected, desired) - #define ma_atomic_compare_and_swap_16(dst, expected, desired) __sync_val_compare_and_swap(dst, expected, desired) - #define ma_atomic_compare_and_swap_32(dst, expected, desired) __sync_val_compare_and_swap(dst, expected, desired) - #define ma_atomic_compare_and_swap_64(dst, expected, desired) __sync_val_compare_and_swap(dst, expected, desired) - #else - #if defined(MA_X86) - #define ma_atomic_thread_fence(order) __asm__ __volatile__("lock; addl $0, (%%esp)" ::: "memory", "cc") - #elif defined(MA_X64) - #define ma_atomic_thread_fence(order) __asm__ __volatile__("lock; addq $0, (%%rsp)" ::: "memory", "cc") - #else - #error Unsupported architecture. Please submit a feature request. - #endif - static MA_INLINE ma_uint8 ma_atomic_compare_and_swap_8(volatile ma_uint8* dst, ma_uint8 expected, ma_uint8 desired) - { - ma_uint8 result; - #if defined(MA_X86) || defined(MA_X64) - __asm__ __volatile__("lock; cmpxchg %3, %0" : "+m"(*dst), "=a"(result) : "a"(expected), "d"(desired) : "cc"); - #else - #error Unsupported architecture. Please submit a feature request. - #endif - return result; - } - static MA_INLINE ma_uint16 ma_atomic_compare_and_swap_16(volatile ma_uint16* dst, ma_uint16 expected, ma_uint16 desired) - { - ma_uint16 result; - #if defined(MA_X86) || defined(MA_X64) - __asm__ __volatile__("lock; cmpxchg %3, %0" : "+m"(*dst), "=a"(result) : "a"(expected), "d"(desired) : "cc"); - #else - #error Unsupported architecture. Please submit a feature request. - #endif - return result; - } - static MA_INLINE ma_uint32 ma_atomic_compare_and_swap_32(volatile ma_uint32* dst, ma_uint32 expected, ma_uint32 desired) - { - ma_uint32 result; - #if defined(MA_X86) || defined(MA_X64) - __asm__ __volatile__("lock; cmpxchg %3, %0" : "+m"(*dst), "=a"(result) : "a"(expected), "d"(desired) : "cc"); - #else - #error Unsupported architecture. Please submit a feature request. - #endif - return result; - } - static MA_INLINE ma_uint64 ma_atomic_compare_and_swap_64(volatile ma_uint64* dst, ma_uint64 expected, ma_uint64 desired) - { - volatile ma_uint64 result; - #if defined(MA_X86) - ma_uint32 resultEAX; - ma_uint32 resultEDX; - __asm__ __volatile__("push %%ebx; xchg %5, %%ebx; lock; cmpxchg8b %0; pop %%ebx" : "+m"(*dst), "=a"(resultEAX), "=d"(resultEDX) : "a"(expected & 0xFFFFFFFF), "d"(expected >> 32), "r"(desired & 0xFFFFFFFF), "c"(desired >> 32) : "cc"); - result = ((ma_uint64)resultEDX << 32) | resultEAX; - #elif defined(MA_X64) - __asm__ __volatile__("lock; cmpxchg %3, %0" : "+m"(*dst), "=a"(result) : "a"(expected), "d"(desired) : "cc"); - #else - #error Unsupported architecture. Please submit a feature request. - #endif - return result; - } - static MA_INLINE ma_uint8 ma_atomic_exchange_explicit_8(volatile ma_uint8* dst, ma_uint8 src, ma_atomic_memory_order order) - { - ma_uint8 result = 0; - (void)order; - #if defined(MA_X86) || defined(MA_X64) - __asm__ __volatile__("lock; xchg %1, %0" : "+m"(*dst), "=a"(result) : "a"(src)); - #else - #error Unsupported architecture. Please submit a feature request. - #endif - return result; - } - static MA_INLINE ma_uint16 ma_atomic_exchange_explicit_16(volatile ma_uint16* dst, ma_uint16 src, ma_atomic_memory_order order) - { - ma_uint16 result = 0; - (void)order; - #if defined(MA_X86) || defined(MA_X64) - __asm__ __volatile__("lock; xchg %1, %0" : "+m"(*dst), "=a"(result) : "a"(src)); - #else - #error Unsupported architecture. Please submit a feature request. - #endif - return result; - } - static MA_INLINE ma_uint32 ma_atomic_exchange_explicit_32(volatile ma_uint32* dst, ma_uint32 src, ma_atomic_memory_order order) - { - ma_uint32 result; - (void)order; - #if defined(MA_X86) || defined(MA_X64) - __asm__ __volatile__("lock; xchg %1, %0" : "+m"(*dst), "=a"(result) : "a"(src)); - #else - #error Unsupported architecture. Please submit a feature request. - #endif - return result; - } - static MA_INLINE ma_uint64 ma_atomic_exchange_explicit_64(volatile ma_uint64* dst, ma_uint64 src, ma_atomic_memory_order order) - { - ma_uint64 result; - (void)order; - #if defined(MA_X86) - do { - result = *dst; - } while (ma_atomic_compare_and_swap_64(dst, result, src) != result); - #elif defined(MA_X64) - __asm__ __volatile__("lock; xchg %1, %0" : "+m"(*dst), "=a"(result) : "a"(src)); - #else - #error Unsupported architecture. Please submit a feature request. - #endif - return result; - } - static MA_INLINE ma_uint8 ma_atomic_fetch_add_explicit_8(volatile ma_uint8* dst, ma_uint8 src, ma_atomic_memory_order order) - { - ma_uint8 result; - (void)order; - #if defined(MA_X86) || defined(MA_X64) - __asm__ __volatile__("lock; xadd %1, %0" : "+m"(*dst), "=a"(result) : "a"(src) : "cc"); - #else - #error Unsupported architecture. Please submit a feature request. - #endif - return result; - } - static MA_INLINE ma_uint16 ma_atomic_fetch_add_explicit_16(volatile ma_uint16* dst, ma_uint16 src, ma_atomic_memory_order order) - { - ma_uint16 result; - (void)order; - #if defined(MA_X86) || defined(MA_X64) - __asm__ __volatile__("lock; xadd %1, %0" : "+m"(*dst), "=a"(result) : "a"(src) : "cc"); - #else - #error Unsupported architecture. Please submit a feature request. - #endif - return result; - } - static MA_INLINE ma_uint32 ma_atomic_fetch_add_explicit_32(volatile ma_uint32* dst, ma_uint32 src, ma_atomic_memory_order order) - { - ma_uint32 result; - (void)order; - #if defined(MA_X86) || defined(MA_X64) - __asm__ __volatile__("lock; xadd %1, %0" : "+m"(*dst), "=a"(result) : "a"(src) : "cc"); - #else - #error Unsupported architecture. Please submit a feature request. - #endif - return result; - } - static MA_INLINE ma_uint64 ma_atomic_fetch_add_explicit_64(volatile ma_uint64* dst, ma_uint64 src, ma_atomic_memory_order order) - { - #if defined(MA_X86) - ma_uint64 oldValue; - ma_uint64 newValue; - (void)order; - do { - oldValue = *dst; - newValue = oldValue + src; - } while (ma_atomic_compare_and_swap_64(dst, oldValue, newValue) != oldValue); - return oldValue; - #elif defined(MA_X64) - ma_uint64 result; - (void)order; - __asm__ __volatile__("lock; xadd %1, %0" : "+m"(*dst), "=a"(result) : "a"(src) : "cc"); - return result; - #endif - } - static MA_INLINE ma_uint8 ma_atomic_fetch_sub_explicit_8(volatile ma_uint8* dst, ma_uint8 src, ma_atomic_memory_order order) - { - ma_uint8 oldValue; - ma_uint8 newValue; - do { - oldValue = *dst; - newValue = (ma_uint8)(oldValue - src); - } while (ma_atomic_compare_and_swap_8(dst, oldValue, newValue) != oldValue); - (void)order; - return oldValue; - } - static MA_INLINE ma_uint16 ma_atomic_fetch_sub_explicit_16(volatile ma_uint16* dst, ma_uint16 src, ma_atomic_memory_order order) - { - ma_uint16 oldValue; - ma_uint16 newValue; - do { - oldValue = *dst; - newValue = (ma_uint16)(oldValue - src); - } while (ma_atomic_compare_and_swap_16(dst, oldValue, newValue) != oldValue); - (void)order; - return oldValue; - } - static MA_INLINE ma_uint32 ma_atomic_fetch_sub_explicit_32(volatile ma_uint32* dst, ma_uint32 src, ma_atomic_memory_order order) - { - ma_uint32 oldValue; - ma_uint32 newValue; - do { - oldValue = *dst; - newValue = oldValue - src; - } while (ma_atomic_compare_and_swap_32(dst, oldValue, newValue) != oldValue); - (void)order; - return oldValue; - } - static MA_INLINE ma_uint64 ma_atomic_fetch_sub_explicit_64(volatile ma_uint64* dst, ma_uint64 src, ma_atomic_memory_order order) - { - ma_uint64 oldValue; - ma_uint64 newValue; - do { - oldValue = *dst; - newValue = oldValue - src; - } while (ma_atomic_compare_and_swap_64(dst, oldValue, newValue) != oldValue); - (void)order; - return oldValue; - } - static MA_INLINE ma_uint8 ma_atomic_fetch_and_explicit_8(volatile ma_uint8* dst, ma_uint8 src, ma_atomic_memory_order order) - { - ma_uint8 oldValue; - ma_uint8 newValue; - do { - oldValue = *dst; - newValue = (ma_uint8)(oldValue & src); - } while (ma_atomic_compare_and_swap_8(dst, oldValue, newValue) != oldValue); - (void)order; - return oldValue; - } - static MA_INLINE ma_uint16 ma_atomic_fetch_and_explicit_16(volatile ma_uint16* dst, ma_uint16 src, ma_atomic_memory_order order) - { - ma_uint16 oldValue; - ma_uint16 newValue; - do { - oldValue = *dst; - newValue = (ma_uint16)(oldValue & src); - } while (ma_atomic_compare_and_swap_16(dst, oldValue, newValue) != oldValue); - (void)order; - return oldValue; - } - static MA_INLINE ma_uint32 ma_atomic_fetch_and_explicit_32(volatile ma_uint32* dst, ma_uint32 src, ma_atomic_memory_order order) - { - ma_uint32 oldValue; - ma_uint32 newValue; - do { - oldValue = *dst; - newValue = oldValue & src; - } while (ma_atomic_compare_and_swap_32(dst, oldValue, newValue) != oldValue); - (void)order; - return oldValue; - } - static MA_INLINE ma_uint64 ma_atomic_fetch_and_explicit_64(volatile ma_uint64* dst, ma_uint64 src, ma_atomic_memory_order order) - { - ma_uint64 oldValue; - ma_uint64 newValue; - do { - oldValue = *dst; - newValue = oldValue & src; - } while (ma_atomic_compare_and_swap_64(dst, oldValue, newValue) != oldValue); - (void)order; - return oldValue; - } - static MA_INLINE ma_uint8 ma_atomic_fetch_xor_explicit_8(volatile ma_uint8* dst, ma_uint8 src, ma_atomic_memory_order order) - { - ma_uint8 oldValue; - ma_uint8 newValue; - do { - oldValue = *dst; - newValue = (ma_uint8)(oldValue ^ src); - } while (ma_atomic_compare_and_swap_8(dst, oldValue, newValue) != oldValue); - (void)order; - return oldValue; - } - static MA_INLINE ma_uint16 ma_atomic_fetch_xor_explicit_16(volatile ma_uint16* dst, ma_uint16 src, ma_atomic_memory_order order) - { - ma_uint16 oldValue; - ma_uint16 newValue; - do { - oldValue = *dst; - newValue = (ma_uint16)(oldValue ^ src); - } while (ma_atomic_compare_and_swap_16(dst, oldValue, newValue) != oldValue); - (void)order; - return oldValue; - } - static MA_INLINE ma_uint32 ma_atomic_fetch_xor_explicit_32(volatile ma_uint32* dst, ma_uint32 src, ma_atomic_memory_order order) - { - ma_uint32 oldValue; - ma_uint32 newValue; - do { - oldValue = *dst; - newValue = oldValue ^ src; - } while (ma_atomic_compare_and_swap_32(dst, oldValue, newValue) != oldValue); - (void)order; - return oldValue; - } - static MA_INLINE ma_uint64 ma_atomic_fetch_xor_explicit_64(volatile ma_uint64* dst, ma_uint64 src, ma_atomic_memory_order order) - { - ma_uint64 oldValue; - ma_uint64 newValue; - do { - oldValue = *dst; - newValue = oldValue ^ src; - } while (ma_atomic_compare_and_swap_64(dst, oldValue, newValue) != oldValue); - (void)order; - return oldValue; - } - static MA_INLINE ma_uint8 ma_atomic_fetch_or_explicit_8(volatile ma_uint8* dst, ma_uint8 src, ma_atomic_memory_order order) - { - ma_uint8 oldValue; - ma_uint8 newValue; - do { - oldValue = *dst; - newValue = (ma_uint8)(oldValue | src); - } while (ma_atomic_compare_and_swap_8(dst, oldValue, newValue) != oldValue); - (void)order; - return oldValue; - } - static MA_INLINE ma_uint16 ma_atomic_fetch_or_explicit_16(volatile ma_uint16* dst, ma_uint16 src, ma_atomic_memory_order order) - { - ma_uint16 oldValue; - ma_uint16 newValue; - do { - oldValue = *dst; - newValue = (ma_uint16)(oldValue | src); - } while (ma_atomic_compare_and_swap_16(dst, oldValue, newValue) != oldValue); - (void)order; - return oldValue; - } - static MA_INLINE ma_uint32 ma_atomic_fetch_or_explicit_32(volatile ma_uint32* dst, ma_uint32 src, ma_atomic_memory_order order) - { - ma_uint32 oldValue; - ma_uint32 newValue; - do { - oldValue = *dst; - newValue = oldValue | src; - } while (ma_atomic_compare_and_swap_32(dst, oldValue, newValue) != oldValue); - (void)order; - return oldValue; - } - static MA_INLINE ma_uint64 ma_atomic_fetch_or_explicit_64(volatile ma_uint64* dst, ma_uint64 src, ma_atomic_memory_order order) - { - ma_uint64 oldValue; - ma_uint64 newValue; - do { - oldValue = *dst; - newValue = oldValue | src; - } while (ma_atomic_compare_and_swap_64(dst, oldValue, newValue) != oldValue); - (void)order; - return oldValue; - } - #endif - #define ma_atomic_signal_fence(order) ma_atomic_thread_fence(order) - static MA_INLINE ma_uint8 ma_atomic_load_explicit_8(volatile const ma_uint8* ptr, ma_atomic_memory_order order) - { - (void)order; - return ma_atomic_compare_and_swap_8((ma_uint8*)ptr, 0, 0); - } - static MA_INLINE ma_uint16 ma_atomic_load_explicit_16(volatile const ma_uint16* ptr, ma_atomic_memory_order order) - { - (void)order; - return ma_atomic_compare_and_swap_16((ma_uint16*)ptr, 0, 0); - } - static MA_INLINE ma_uint32 ma_atomic_load_explicit_32(volatile const ma_uint32* ptr, ma_atomic_memory_order order) - { - (void)order; - return ma_atomic_compare_and_swap_32((ma_uint32*)ptr, 0, 0); - } - static MA_INLINE ma_uint64 ma_atomic_load_explicit_64(volatile const ma_uint64* ptr, ma_atomic_memory_order order) - { - (void)order; - return ma_atomic_compare_and_swap_64((ma_uint64*)ptr, 0, 0); - } - #define ma_atomic_store_explicit_8( dst, src, order) (void)ma_atomic_exchange_explicit_8 (dst, src, order) - #define ma_atomic_store_explicit_16(dst, src, order) (void)ma_atomic_exchange_explicit_16(dst, src, order) - #define ma_atomic_store_explicit_32(dst, src, order) (void)ma_atomic_exchange_explicit_32(dst, src, order) - #define ma_atomic_store_explicit_64(dst, src, order) (void)ma_atomic_exchange_explicit_64(dst, src, order) - #define ma_atomic_test_and_set_explicit_8( dst, order) ma_atomic_exchange_explicit_8 (dst, 1, order) - #define ma_atomic_test_and_set_explicit_16(dst, order) ma_atomic_exchange_explicit_16(dst, 1, order) - #define ma_atomic_test_and_set_explicit_32(dst, order) ma_atomic_exchange_explicit_32(dst, 1, order) - #define ma_atomic_test_and_set_explicit_64(dst, order) ma_atomic_exchange_explicit_64(dst, 1, order) - #define ma_atomic_clear_explicit_8( dst, order) ma_atomic_store_explicit_8 (dst, 0, order) - #define ma_atomic_clear_explicit_16(dst, order) ma_atomic_store_explicit_16(dst, 0, order) - #define ma_atomic_clear_explicit_32(dst, order) ma_atomic_store_explicit_32(dst, 0, order) - #define ma_atomic_clear_explicit_64(dst, order) ma_atomic_store_explicit_64(dst, 0, order) - typedef ma_uint8 ma_atomic_flag; - #define ma_atomic_flag_test_and_set_explicit(ptr, order) (ma_bool32)ma_atomic_test_and_set_explicit_8(ptr, order) - #define ma_atomic_flag_clear_explicit(ptr, order) ma_atomic_clear_explicit_8(ptr, order) - #define ma_atomic_flag_load_explicit(ptr, order) ma_atomic_load_explicit_8(ptr, order) -#endif -#if !defined(MA_ATOMIC_HAS_NATIVE_COMPARE_EXCHANGE) - #if defined(MA_ATOMIC_HAS_8) - static MA_INLINE ma_bool32 ma_atomic_compare_exchange_strong_explicit_8(volatile ma_uint8* dst, ma_uint8* expected, ma_uint8 desired, ma_atomic_memory_order successOrder, ma_atomic_memory_order failureOrder) - { - ma_uint8 expectedValue; - ma_uint8 result; - (void)successOrder; - (void)failureOrder; - expectedValue = ma_atomic_load_explicit_8(expected, ma_atomic_memory_order_seq_cst); - result = ma_atomic_compare_and_swap_8(dst, expectedValue, desired); - if (result == expectedValue) { - return 1; - } else { - ma_atomic_store_explicit_8(expected, result, failureOrder); - return 0; - } - } - #endif - #if defined(MA_ATOMIC_HAS_16) - static MA_INLINE ma_bool32 ma_atomic_compare_exchange_strong_explicit_16(volatile ma_uint16* dst, ma_uint16* expected, ma_uint16 desired, ma_atomic_memory_order successOrder, ma_atomic_memory_order failureOrder) - { - ma_uint16 expectedValue; - ma_uint16 result; - (void)successOrder; - (void)failureOrder; - expectedValue = ma_atomic_load_explicit_16(expected, ma_atomic_memory_order_seq_cst); - result = ma_atomic_compare_and_swap_16(dst, expectedValue, desired); - if (result == expectedValue) { - return 1; - } else { - ma_atomic_store_explicit_16(expected, result, failureOrder); - return 0; - } - } - #endif - #if defined(MA_ATOMIC_HAS_32) - static MA_INLINE ma_bool32 ma_atomic_compare_exchange_strong_explicit_32(volatile ma_uint32* dst, ma_uint32* expected, ma_uint32 desired, ma_atomic_memory_order successOrder, ma_atomic_memory_order failureOrder) - { - ma_uint32 expectedValue; - ma_uint32 result; - (void)successOrder; - (void)failureOrder; - expectedValue = ma_atomic_load_explicit_32(expected, ma_atomic_memory_order_seq_cst); - result = ma_atomic_compare_and_swap_32(dst, expectedValue, desired); - if (result == expectedValue) { - return 1; - } else { - ma_atomic_store_explicit_32(expected, result, failureOrder); - return 0; - } - } - #endif - #if defined(MA_ATOMIC_HAS_64) - static MA_INLINE ma_bool32 ma_atomic_compare_exchange_strong_explicit_64(volatile ma_uint64* dst, volatile ma_uint64* expected, ma_uint64 desired, ma_atomic_memory_order successOrder, ma_atomic_memory_order failureOrder) - { - ma_uint64 expectedValue; - ma_uint64 result; - (void)successOrder; - (void)failureOrder; - expectedValue = ma_atomic_load_explicit_64(expected, ma_atomic_memory_order_seq_cst); - result = ma_atomic_compare_and_swap_64(dst, expectedValue, desired); - if (result == expectedValue) { - return 1; - } else { - ma_atomic_store_explicit_64(expected, result, failureOrder); - return 0; - } - } - #endif - #define ma_atomic_compare_exchange_weak_explicit_8( dst, expected, desired, successOrder, failureOrder) ma_atomic_compare_exchange_strong_explicit_8 (dst, expected, desired, successOrder, failureOrder) - #define ma_atomic_compare_exchange_weak_explicit_16(dst, expected, desired, successOrder, failureOrder) ma_atomic_compare_exchange_strong_explicit_16(dst, expected, desired, successOrder, failureOrder) - #define ma_atomic_compare_exchange_weak_explicit_32(dst, expected, desired, successOrder, failureOrder) ma_atomic_compare_exchange_strong_explicit_32(dst, expected, desired, successOrder, failureOrder) - #define ma_atomic_compare_exchange_weak_explicit_64(dst, expected, desired, successOrder, failureOrder) ma_atomic_compare_exchange_strong_explicit_64(dst, expected, desired, successOrder, failureOrder) -#endif -#if !defined(MA_ATOMIC_HAS_NATIVE_IS_LOCK_FREE) - static MA_INLINE ma_bool32 ma_atomic_is_lock_free_8(volatile void* ptr) - { - (void)ptr; - return 1; - } - static MA_INLINE ma_bool32 ma_atomic_is_lock_free_16(volatile void* ptr) - { - (void)ptr; - return 1; - } - static MA_INLINE ma_bool32 ma_atomic_is_lock_free_32(volatile void* ptr) - { - (void)ptr; - return 1; - } - static MA_INLINE ma_bool32 ma_atomic_is_lock_free_64(volatile void* ptr) - { - (void)ptr; - #if defined(MA_64BIT) - return 1; - #else - #if defined(MA_X86) || defined(MA_X64) - return 1; - #else - return 0; - #endif - #endif - } -#endif -#if defined(MA_64BIT) - static MA_INLINE ma_bool32 ma_atomic_is_lock_free_ptr(volatile void** ptr) - { - return ma_atomic_is_lock_free_64((volatile ma_uint64*)ptr); - } - static MA_INLINE void* ma_atomic_load_explicit_ptr(volatile void** ptr, ma_atomic_memory_order order) - { - return (void*)ma_atomic_load_explicit_64((volatile ma_uint64*)ptr, order); - } - static MA_INLINE void ma_atomic_store_explicit_ptr(volatile void** dst, void* src, ma_atomic_memory_order order) - { - ma_atomic_store_explicit_64((volatile ma_uint64*)dst, (ma_uint64)src, order); - } - static MA_INLINE void* ma_atomic_exchange_explicit_ptr(volatile void** dst, void* src, ma_atomic_memory_order order) - { - return (void*)ma_atomic_exchange_explicit_64((volatile ma_uint64*)dst, (ma_uint64)src, order); - } - static MA_INLINE ma_bool32 ma_atomic_compare_exchange_strong_explicit_ptr(volatile void** dst, void** expected, void* desired, ma_atomic_memory_order successOrder, ma_atomic_memory_order failureOrder) - { - return ma_atomic_compare_exchange_strong_explicit_64((volatile ma_uint64*)dst, (ma_uint64*)expected, (ma_uint64)desired, successOrder, failureOrder); - } - static MA_INLINE ma_bool32 ma_atomic_compare_exchange_weak_explicit_ptr(volatile void** dst, void** expected, void* desired, ma_atomic_memory_order successOrder, ma_atomic_memory_order failureOrder) - { - return ma_atomic_compare_exchange_weak_explicit_64((volatile ma_uint64*)dst, (ma_uint64*)expected, (ma_uint64)desired, successOrder, failureOrder); - } - static MA_INLINE void* ma_atomic_compare_and_swap_ptr(volatile void** dst, void* expected, void* desired) - { - return (void*)ma_atomic_compare_and_swap_64((volatile ma_uint64*)dst, (ma_uint64)expected, (ma_uint64)desired); - } -#elif defined(MA_32BIT) - static MA_INLINE ma_bool32 ma_atomic_is_lock_free_ptr(volatile void** ptr) - { - return ma_atomic_is_lock_free_32((volatile ma_uint32*)ptr); - } - static MA_INLINE void* ma_atomic_load_explicit_ptr(volatile void** ptr, ma_atomic_memory_order order) - { - return (void*)ma_atomic_load_explicit_32((volatile ma_uint32*)ptr, order); - } - static MA_INLINE void ma_atomic_store_explicit_ptr(volatile void** dst, void* src, ma_atomic_memory_order order) - { - ma_atomic_store_explicit_32((volatile ma_uint32*)dst, (ma_uint32)src, order); - } - static MA_INLINE void* ma_atomic_exchange_explicit_ptr(volatile void** dst, void* src, ma_atomic_memory_order order) - { - return (void*)ma_atomic_exchange_explicit_32((volatile ma_uint32*)dst, (ma_uint32)src, order); - } - static MA_INLINE ma_bool32 ma_atomic_compare_exchange_strong_explicit_ptr(volatile void** dst, void** expected, void* desired, ma_atomic_memory_order successOrder, ma_atomic_memory_order failureOrder) - { - return ma_atomic_compare_exchange_strong_explicit_32((volatile ma_uint32*)dst, (ma_uint32*)expected, (ma_uint32)desired, successOrder, failureOrder); - } - static MA_INLINE ma_bool32 ma_atomic_compare_exchange_weak_explicit_ptr(volatile void** dst, void** expected, void* desired, ma_atomic_memory_order successOrder, ma_atomic_memory_order failureOrder) - { - return ma_atomic_compare_exchange_weak_explicit_32((volatile ma_uint32*)dst, (ma_uint32*)expected, (ma_uint32)desired, successOrder, failureOrder); - } - static MA_INLINE void* ma_atomic_compare_and_swap_ptr(volatile void** dst, void* expected, void* desired) - { - return (void*)ma_atomic_compare_and_swap_32((volatile ma_uint32*)dst, (ma_uint32)expected, (ma_uint32)desired); - } -#else - #error Unsupported architecture. -#endif -#define ma_atomic_flag_test_and_set(ptr) ma_atomic_flag_test_and_set_explicit(ptr, ma_atomic_memory_order_seq_cst) -#define ma_atomic_flag_clear(ptr) ma_atomic_flag_clear_explicit(ptr, ma_atomic_memory_order_seq_cst) -#define ma_atomic_store_ptr(dst, src) ma_atomic_store_explicit_ptr((volatile void**)dst, (void*)src, ma_atomic_memory_order_seq_cst) -#define ma_atomic_load_ptr(ptr) ma_atomic_load_explicit_ptr((volatile void**)ptr, ma_atomic_memory_order_seq_cst) -#define ma_atomic_exchange_ptr(dst, src) ma_atomic_exchange_explicit_ptr((volatile void**)dst, (void*)src, ma_atomic_memory_order_seq_cst) -#define ma_atomic_compare_exchange_strong_ptr(dst, expected, desired) ma_atomic_compare_exchange_strong_explicit_ptr((volatile void**)dst, (void**)expected, (void*)desired, ma_atomic_memory_order_seq_cst, ma_atomic_memory_order_seq_cst) -#define ma_atomic_compare_exchange_weak_ptr(dst, expected, desired) ma_atomic_compare_exchange_weak_explicit_ptr((volatile void**)dst, (void**)expected, (void*)desired, ma_atomic_memory_order_seq_cst, ma_atomic_memory_order_seq_cst) -#define ma_atomic_test_and_set_8( ptr) ma_atomic_test_and_set_explicit_8( ptr, ma_atomic_memory_order_seq_cst) -#define ma_atomic_test_and_set_16(ptr) ma_atomic_test_and_set_explicit_16(ptr, ma_atomic_memory_order_seq_cst) -#define ma_atomic_test_and_set_32(ptr) ma_atomic_test_and_set_explicit_32(ptr, ma_atomic_memory_order_seq_cst) -#define ma_atomic_test_and_set_64(ptr) ma_atomic_test_and_set_explicit_64(ptr, ma_atomic_memory_order_seq_cst) -#define ma_atomic_clear_8( ptr) ma_atomic_clear_explicit_8( ptr, ma_atomic_memory_order_seq_cst) -#define ma_atomic_clear_16(ptr) ma_atomic_clear_explicit_16(ptr, ma_atomic_memory_order_seq_cst) -#define ma_atomic_clear_32(ptr) ma_atomic_clear_explicit_32(ptr, ma_atomic_memory_order_seq_cst) -#define ma_atomic_clear_64(ptr) ma_atomic_clear_explicit_64(ptr, ma_atomic_memory_order_seq_cst) -#define ma_atomic_store_8( dst, src) ma_atomic_store_explicit_8( dst, src, ma_atomic_memory_order_seq_cst) -#define ma_atomic_store_16(dst, src) ma_atomic_store_explicit_16(dst, src, ma_atomic_memory_order_seq_cst) -#define ma_atomic_store_32(dst, src) ma_atomic_store_explicit_32(dst, src, ma_atomic_memory_order_seq_cst) -#define ma_atomic_store_64(dst, src) ma_atomic_store_explicit_64(dst, src, ma_atomic_memory_order_seq_cst) -#define ma_atomic_load_8( ptr) ma_atomic_load_explicit_8( ptr, ma_atomic_memory_order_seq_cst) -#define ma_atomic_load_16(ptr) ma_atomic_load_explicit_16(ptr, ma_atomic_memory_order_seq_cst) -#define ma_atomic_load_32(ptr) ma_atomic_load_explicit_32(ptr, ma_atomic_memory_order_seq_cst) -#define ma_atomic_load_64(ptr) ma_atomic_load_explicit_64(ptr, ma_atomic_memory_order_seq_cst) -#define ma_atomic_exchange_8( dst, src) ma_atomic_exchange_explicit_8( dst, src, ma_atomic_memory_order_seq_cst) -#define ma_atomic_exchange_16(dst, src) ma_atomic_exchange_explicit_16(dst, src, ma_atomic_memory_order_seq_cst) -#define ma_atomic_exchange_32(dst, src) ma_atomic_exchange_explicit_32(dst, src, ma_atomic_memory_order_seq_cst) -#define ma_atomic_exchange_64(dst, src) ma_atomic_exchange_explicit_64(dst, src, ma_atomic_memory_order_seq_cst) -#define ma_atomic_compare_exchange_strong_8( dst, expected, desired) ma_atomic_compare_exchange_strong_explicit_8( dst, expected, desired, ma_atomic_memory_order_seq_cst, ma_atomic_memory_order_seq_cst) -#define ma_atomic_compare_exchange_strong_16(dst, expected, desired) ma_atomic_compare_exchange_strong_explicit_16(dst, expected, desired, ma_atomic_memory_order_seq_cst, ma_atomic_memory_order_seq_cst) -#define ma_atomic_compare_exchange_strong_32(dst, expected, desired) ma_atomic_compare_exchange_strong_explicit_32(dst, expected, desired, ma_atomic_memory_order_seq_cst, ma_atomic_memory_order_seq_cst) -#define ma_atomic_compare_exchange_strong_64(dst, expected, desired) ma_atomic_compare_exchange_strong_explicit_64(dst, expected, desired, ma_atomic_memory_order_seq_cst, ma_atomic_memory_order_seq_cst) -#define ma_atomic_compare_exchange_weak_8( dst, expected, desired) ma_atomic_compare_exchange_weak_explicit_8( dst, expected, desired, ma_atomic_memory_order_seq_cst, ma_atomic_memory_order_seq_cst) -#define ma_atomic_compare_exchange_weak_16( dst, expected, desired) ma_atomic_compare_exchange_weak_explicit_16(dst, expected, desired, ma_atomic_memory_order_seq_cst, ma_atomic_memory_order_seq_cst) -#define ma_atomic_compare_exchange_weak_32( dst, expected, desired) ma_atomic_compare_exchange_weak_explicit_32(dst, expected, desired, ma_atomic_memory_order_seq_cst, ma_atomic_memory_order_seq_cst) -#define ma_atomic_compare_exchange_weak_64( dst, expected, desired) ma_atomic_compare_exchange_weak_explicit_64(dst, expected, desired, ma_atomic_memory_order_seq_cst, ma_atomic_memory_order_seq_cst) -#define ma_atomic_fetch_add_8( dst, src) ma_atomic_fetch_add_explicit_8( dst, src, ma_atomic_memory_order_seq_cst) -#define ma_atomic_fetch_add_16(dst, src) ma_atomic_fetch_add_explicit_16(dst, src, ma_atomic_memory_order_seq_cst) -#define ma_atomic_fetch_add_32(dst, src) ma_atomic_fetch_add_explicit_32(dst, src, ma_atomic_memory_order_seq_cst) -#define ma_atomic_fetch_add_64(dst, src) ma_atomic_fetch_add_explicit_64(dst, src, ma_atomic_memory_order_seq_cst) -#define ma_atomic_fetch_sub_8( dst, src) ma_atomic_fetch_sub_explicit_8( dst, src, ma_atomic_memory_order_seq_cst) -#define ma_atomic_fetch_sub_16(dst, src) ma_atomic_fetch_sub_explicit_16(dst, src, ma_atomic_memory_order_seq_cst) -#define ma_atomic_fetch_sub_32(dst, src) ma_atomic_fetch_sub_explicit_32(dst, src, ma_atomic_memory_order_seq_cst) -#define ma_atomic_fetch_sub_64(dst, src) ma_atomic_fetch_sub_explicit_64(dst, src, ma_atomic_memory_order_seq_cst) -#define ma_atomic_fetch_or_8( dst, src) ma_atomic_fetch_or_explicit_8( dst, src, ma_atomic_memory_order_seq_cst) -#define ma_atomic_fetch_or_16(dst, src) ma_atomic_fetch_or_explicit_16(dst, src, ma_atomic_memory_order_seq_cst) -#define ma_atomic_fetch_or_32(dst, src) ma_atomic_fetch_or_explicit_32(dst, src, ma_atomic_memory_order_seq_cst) -#define ma_atomic_fetch_or_64(dst, src) ma_atomic_fetch_or_explicit_64(dst, src, ma_atomic_memory_order_seq_cst) -#define ma_atomic_fetch_xor_8( dst, src) ma_atomic_fetch_xor_explicit_8( dst, src, ma_atomic_memory_order_seq_cst) -#define ma_atomic_fetch_xor_16(dst, src) ma_atomic_fetch_xor_explicit_16(dst, src, ma_atomic_memory_order_seq_cst) -#define ma_atomic_fetch_xor_32(dst, src) ma_atomic_fetch_xor_explicit_32(dst, src, ma_atomic_memory_order_seq_cst) -#define ma_atomic_fetch_xor_64(dst, src) ma_atomic_fetch_xor_explicit_64(dst, src, ma_atomic_memory_order_seq_cst) -#define ma_atomic_fetch_and_8( dst, src) ma_atomic_fetch_and_explicit_8 (dst, src, ma_atomic_memory_order_seq_cst) -#define ma_atomic_fetch_and_16(dst, src) ma_atomic_fetch_and_explicit_16(dst, src, ma_atomic_memory_order_seq_cst) -#define ma_atomic_fetch_and_32(dst, src) ma_atomic_fetch_and_explicit_32(dst, src, ma_atomic_memory_order_seq_cst) -#define ma_atomic_fetch_and_64(dst, src) ma_atomic_fetch_and_explicit_64(dst, src, ma_atomic_memory_order_seq_cst) -#define ma_atomic_test_and_set_explicit_i8( ptr, order) (ma_int8 )ma_atomic_test_and_set_explicit_8( (ma_uint8* )ptr, order) -#define ma_atomic_test_and_set_explicit_i16(ptr, order) (ma_int16)ma_atomic_test_and_set_explicit_16((ma_uint16*)ptr, order) -#define ma_atomic_test_and_set_explicit_i32(ptr, order) (ma_int32)ma_atomic_test_and_set_explicit_32((ma_uint32*)ptr, order) -#define ma_atomic_test_and_set_explicit_i64(ptr, order) (ma_int64)ma_atomic_test_and_set_explicit_64((ma_uint64*)ptr, order) -#define ma_atomic_clear_explicit_i8( ptr, order) ma_atomic_clear_explicit_8( (ma_uint8* )ptr, order) -#define ma_atomic_clear_explicit_i16(ptr, order) ma_atomic_clear_explicit_16((ma_uint16*)ptr, order) -#define ma_atomic_clear_explicit_i32(ptr, order) ma_atomic_clear_explicit_32((ma_uint32*)ptr, order) -#define ma_atomic_clear_explicit_i64(ptr, order) ma_atomic_clear_explicit_64((ma_uint64*)ptr, order) -#define ma_atomic_store_explicit_i8( dst, src, order) ma_atomic_store_explicit_8( (ma_uint8* )dst, (ma_uint8 )src, order) -#define ma_atomic_store_explicit_i16(dst, src, order) ma_atomic_store_explicit_16((ma_uint16*)dst, (ma_uint16)src, order) -#define ma_atomic_store_explicit_i32(dst, src, order) ma_atomic_store_explicit_32((ma_uint32*)dst, (ma_uint32)src, order) -#define ma_atomic_store_explicit_i64(dst, src, order) ma_atomic_store_explicit_64((ma_uint64*)dst, (ma_uint64)src, order) -#define ma_atomic_load_explicit_i8( ptr, order) (ma_int8 )ma_atomic_load_explicit_8( (ma_uint8* )ptr, order) -#define ma_atomic_load_explicit_i16(ptr, order) (ma_int16)ma_atomic_load_explicit_16((ma_uint16*)ptr, order) -#define ma_atomic_load_explicit_i32(ptr, order) (ma_int32)ma_atomic_load_explicit_32((ma_uint32*)ptr, order) -#define ma_atomic_load_explicit_i64(ptr, order) (ma_int64)ma_atomic_load_explicit_64((ma_uint64*)ptr, order) -#define ma_atomic_exchange_explicit_i8( dst, src, order) (ma_int8 )ma_atomic_exchange_explicit_8 ((ma_uint8* )dst, (ma_uint8 )src, order) -#define ma_atomic_exchange_explicit_i16(dst, src, order) (ma_int16)ma_atomic_exchange_explicit_16((ma_uint16*)dst, (ma_uint16)src, order) -#define ma_atomic_exchange_explicit_i32(dst, src, order) (ma_int32)ma_atomic_exchange_explicit_32((ma_uint32*)dst, (ma_uint32)src, order) -#define ma_atomic_exchange_explicit_i64(dst, src, order) (ma_int64)ma_atomic_exchange_explicit_64((ma_uint64*)dst, (ma_uint64)src, order) -#define ma_atomic_compare_exchange_strong_explicit_i8( dst, expected, desired, successOrder, failureOrder) ma_atomic_compare_exchange_strong_explicit_8( (ma_uint8* )dst, (ma_uint8* )expected, (ma_uint8 )desired, successOrder, failureOrder) -#define ma_atomic_compare_exchange_strong_explicit_i16(dst, expected, desired, successOrder, failureOrder) ma_atomic_compare_exchange_strong_explicit_16((ma_uint16*)dst, (ma_uint16*)expected, (ma_uint16)desired, successOrder, failureOrder) -#define ma_atomic_compare_exchange_strong_explicit_i32(dst, expected, desired, successOrder, failureOrder) ma_atomic_compare_exchange_strong_explicit_32((ma_uint32*)dst, (ma_uint32*)expected, (ma_uint32)desired, successOrder, failureOrder) -#define ma_atomic_compare_exchange_strong_explicit_i64(dst, expected, desired, successOrder, failureOrder) ma_atomic_compare_exchange_strong_explicit_64((ma_uint64*)dst, (ma_uint64*)expected, (ma_uint64)desired, successOrder, failureOrder) -#define ma_atomic_compare_exchange_weak_explicit_i8( dst, expected, desired, successOrder, failureOrder) ma_atomic_compare_exchange_weak_explicit_8( (ma_uint8* )dst, (ma_uint8* )expected, (ma_uint8 )desired, successOrder, failureOrder) -#define ma_atomic_compare_exchange_weak_explicit_i16(dst, expected, desired, successOrder, failureOrder) ma_atomic_compare_exchange_weak_explicit_16((ma_uint16*)dst, (ma_uint16*)expected, (ma_uint16)desired, successOrder, failureOrder) -#define ma_atomic_compare_exchange_weak_explicit_i32(dst, expected, desired, successOrder, failureOrder) ma_atomic_compare_exchange_weak_explicit_32((ma_uint32*)dst, (ma_uint32*)expected, (ma_uint32)desired, successOrder, failureOrder) -#define ma_atomic_compare_exchange_weak_explicit_i64(dst, expected, desired, successOrder, failureOrder) ma_atomic_compare_exchange_weak_explicit_64((ma_uint64*)dst, (ma_uint64*)expected, (ma_uint64)desired, successOrder, failureOrder) -#define ma_atomic_fetch_add_explicit_i8( dst, src, order) (ma_int8 )ma_atomic_fetch_add_explicit_8( (ma_uint8* )dst, (ma_uint8 )src, order) -#define ma_atomic_fetch_add_explicit_i16(dst, src, order) (ma_int16)ma_atomic_fetch_add_explicit_16((ma_uint16*)dst, (ma_uint16)src, order) -#define ma_atomic_fetch_add_explicit_i32(dst, src, order) (ma_int32)ma_atomic_fetch_add_explicit_32((ma_uint32*)dst, (ma_uint32)src, order) -#define ma_atomic_fetch_add_explicit_i64(dst, src, order) (ma_int64)ma_atomic_fetch_add_explicit_64((ma_uint64*)dst, (ma_uint64)src, order) -#define ma_atomic_fetch_sub_explicit_i8( dst, src, order) (ma_int8 )ma_atomic_fetch_sub_explicit_8( (ma_uint8* )dst, (ma_uint8 )src, order) -#define ma_atomic_fetch_sub_explicit_i16(dst, src, order) (ma_int16)ma_atomic_fetch_sub_explicit_16((ma_uint16*)dst, (ma_uint16)src, order) -#define ma_atomic_fetch_sub_explicit_i32(dst, src, order) (ma_int32)ma_atomic_fetch_sub_explicit_32((ma_uint32*)dst, (ma_uint32)src, order) -#define ma_atomic_fetch_sub_explicit_i64(dst, src, order) (ma_int64)ma_atomic_fetch_sub_explicit_64((ma_uint64*)dst, (ma_uint64)src, order) -#define ma_atomic_fetch_or_explicit_i8( dst, src, order) (ma_int8 )ma_atomic_fetch_or_explicit_8( (ma_uint8* )dst, (ma_uint8 )src, order) -#define ma_atomic_fetch_or_explicit_i16(dst, src, order) (ma_int16)ma_atomic_fetch_or_explicit_16((ma_uint16*)dst, (ma_uint16)src, order) -#define ma_atomic_fetch_or_explicit_i32(dst, src, order) (ma_int32)ma_atomic_fetch_or_explicit_32((ma_uint32*)dst, (ma_uint32)src, order) -#define ma_atomic_fetch_or_explicit_i64(dst, src, order) (ma_int64)ma_atomic_fetch_or_explicit_64((ma_uint64*)dst, (ma_uint64)src, order) -#define ma_atomic_fetch_xor_explicit_i8( dst, src, order) (ma_int8 )ma_atomic_fetch_xor_explicit_8( (ma_uint8* )dst, (ma_uint8 )src, order) -#define ma_atomic_fetch_xor_explicit_i16(dst, src, order) (ma_int16)ma_atomic_fetch_xor_explicit_16((ma_uint16*)dst, (ma_uint16)src, order) -#define ma_atomic_fetch_xor_explicit_i32(dst, src, order) (ma_int32)ma_atomic_fetch_xor_explicit_32((ma_uint32*)dst, (ma_uint32)src, order) -#define ma_atomic_fetch_xor_explicit_i64(dst, src, order) (ma_int64)ma_atomic_fetch_xor_explicit_64((ma_uint64*)dst, (ma_uint64)src, order) -#define ma_atomic_fetch_and_explicit_i8( dst, src, order) (ma_int8 )ma_atomic_fetch_and_explicit_8( (ma_uint8* )dst, (ma_uint8 )src, order) -#define ma_atomic_fetch_and_explicit_i16(dst, src, order) (ma_int16)ma_atomic_fetch_and_explicit_16((ma_uint16*)dst, (ma_uint16)src, order) -#define ma_atomic_fetch_and_explicit_i32(dst, src, order) (ma_int32)ma_atomic_fetch_and_explicit_32((ma_uint32*)dst, (ma_uint32)src, order) -#define ma_atomic_fetch_and_explicit_i64(dst, src, order) (ma_int64)ma_atomic_fetch_and_explicit_64((ma_uint64*)dst, (ma_uint64)src, order) -#define ma_atomic_test_and_set_i8( ptr) ma_atomic_test_and_set_explicit_i8( ptr, ma_atomic_memory_order_seq_cst) -#define ma_atomic_test_and_set_i16(ptr) ma_atomic_test_and_set_explicit_i16(ptr, ma_atomic_memory_order_seq_cst) -#define ma_atomic_test_and_set_i32(ptr) ma_atomic_test_and_set_explicit_i32(ptr, ma_atomic_memory_order_seq_cst) -#define ma_atomic_test_and_set_i64(ptr) ma_atomic_test_and_set_explicit_i64(ptr, ma_atomic_memory_order_seq_cst) -#define ma_atomic_clear_i8( ptr) ma_atomic_clear_explicit_i8( ptr, ma_atomic_memory_order_seq_cst) -#define ma_atomic_clear_i16(ptr) ma_atomic_clear_explicit_i16(ptr, ma_atomic_memory_order_seq_cst) -#define ma_atomic_clear_i32(ptr) ma_atomic_clear_explicit_i32(ptr, ma_atomic_memory_order_seq_cst) -#define ma_atomic_clear_i64(ptr) ma_atomic_clear_explicit_i64(ptr, ma_atomic_memory_order_seq_cst) -#define ma_atomic_store_i8( dst, src) ma_atomic_store_explicit_i8( dst, src, ma_atomic_memory_order_seq_cst) -#define ma_atomic_store_i16(dst, src) ma_atomic_store_explicit_i16(dst, src, ma_atomic_memory_order_seq_cst) -#define ma_atomic_store_i32(dst, src) ma_atomic_store_explicit_i32(dst, src, ma_atomic_memory_order_seq_cst) -#define ma_atomic_store_i64(dst, src) ma_atomic_store_explicit_i64(dst, src, ma_atomic_memory_order_seq_cst) -#define ma_atomic_load_i8( ptr) ma_atomic_load_explicit_i8( ptr, ma_atomic_memory_order_seq_cst) -#define ma_atomic_load_i16(ptr) ma_atomic_load_explicit_i16(ptr, ma_atomic_memory_order_seq_cst) -#define ma_atomic_load_i32(ptr) ma_atomic_load_explicit_i32(ptr, ma_atomic_memory_order_seq_cst) -#define ma_atomic_load_i64(ptr) ma_atomic_load_explicit_i64(ptr, ma_atomic_memory_order_seq_cst) -#define ma_atomic_exchange_i8( dst, src) ma_atomic_exchange_explicit_i8( dst, src, ma_atomic_memory_order_seq_cst) -#define ma_atomic_exchange_i16(dst, src) ma_atomic_exchange_explicit_i16(dst, src, ma_atomic_memory_order_seq_cst) -#define ma_atomic_exchange_i32(dst, src) ma_atomic_exchange_explicit_i32(dst, src, ma_atomic_memory_order_seq_cst) -#define ma_atomic_exchange_i64(dst, src) ma_atomic_exchange_explicit_i64(dst, src, ma_atomic_memory_order_seq_cst) -#define ma_atomic_compare_exchange_strong_i8( dst, expected, desired) ma_atomic_compare_exchange_strong_explicit_i8( dst, expected, desired, ma_atomic_memory_order_seq_cst, ma_atomic_memory_order_seq_cst) -#define ma_atomic_compare_exchange_strong_i16(dst, expected, desired) ma_atomic_compare_exchange_strong_explicit_i16(dst, expected, desired, ma_atomic_memory_order_seq_cst, ma_atomic_memory_order_seq_cst) -#define ma_atomic_compare_exchange_strong_i32(dst, expected, desired) ma_atomic_compare_exchange_strong_explicit_i32(dst, expected, desired, ma_atomic_memory_order_seq_cst, ma_atomic_memory_order_seq_cst) -#define ma_atomic_compare_exchange_strong_i64(dst, expected, desired) ma_atomic_compare_exchange_strong_explicit_i64(dst, expected, desired, ma_atomic_memory_order_seq_cst, ma_atomic_memory_order_seq_cst) -#define ma_atomic_compare_exchange_weak_i8( dst, expected, desired) ma_atomic_compare_exchange_weak_explicit_i8( dst, expected, desired, ma_atomic_memory_order_seq_cst, ma_atomic_memory_order_seq_cst) -#define ma_atomic_compare_exchange_weak_i16(dst, expected, desired) ma_atomic_compare_exchange_weak_explicit_i16(dst, expected, desired, ma_atomic_memory_order_seq_cst, ma_atomic_memory_order_seq_cst) -#define ma_atomic_compare_exchange_weak_i32(dst, expected, desired) ma_atomic_compare_exchange_weak_explicit_i32(dst, expected, desired, ma_atomic_memory_order_seq_cst, ma_atomic_memory_order_seq_cst) -#define ma_atomic_compare_exchange_weak_i64(dst, expected, desired) ma_atomic_compare_exchange_weak_explicit_i64(dst, expected, desired, ma_atomic_memory_order_seq_cst, ma_atomic_memory_order_seq_cst) -#define ma_atomic_fetch_add_i8( dst, src) ma_atomic_fetch_add_explicit_i8( dst, src, ma_atomic_memory_order_seq_cst) -#define ma_atomic_fetch_add_i16(dst, src) ma_atomic_fetch_add_explicit_i16(dst, src, ma_atomic_memory_order_seq_cst) -#define ma_atomic_fetch_add_i32(dst, src) ma_atomic_fetch_add_explicit_i32(dst, src, ma_atomic_memory_order_seq_cst) -#define ma_atomic_fetch_add_i64(dst, src) ma_atomic_fetch_add_explicit_i64(dst, src, ma_atomic_memory_order_seq_cst) -#define ma_atomic_fetch_sub_i8( dst, src) ma_atomic_fetch_sub_explicit_i8( dst, src, ma_atomic_memory_order_seq_cst) -#define ma_atomic_fetch_sub_i16(dst, src) ma_atomic_fetch_sub_explicit_i16(dst, src, ma_atomic_memory_order_seq_cst) -#define ma_atomic_fetch_sub_i32(dst, src) ma_atomic_fetch_sub_explicit_i32(dst, src, ma_atomic_memory_order_seq_cst) -#define ma_atomic_fetch_sub_i64(dst, src) ma_atomic_fetch_sub_explicit_i64(dst, src, ma_atomic_memory_order_seq_cst) -#define ma_atomic_fetch_or_i8( dst, src) ma_atomic_fetch_or_explicit_i8( dst, src, ma_atomic_memory_order_seq_cst) -#define ma_atomic_fetch_or_i16(dst, src) ma_atomic_fetch_or_explicit_i16(dst, src, ma_atomic_memory_order_seq_cst) -#define ma_atomic_fetch_or_i32(dst, src) ma_atomic_fetch_or_explicit_i32(dst, src, ma_atomic_memory_order_seq_cst) -#define ma_atomic_fetch_or_i64(dst, src) ma_atomic_fetch_or_explicit_i64(dst, src, ma_atomic_memory_order_seq_cst) -#define ma_atomic_fetch_xor_i8( dst, src) ma_atomic_fetch_xor_explicit_i8( dst, src, ma_atomic_memory_order_seq_cst) -#define ma_atomic_fetch_xor_i16(dst, src) ma_atomic_fetch_xor_explicit_i16(dst, src, ma_atomic_memory_order_seq_cst) -#define ma_atomic_fetch_xor_i32(dst, src) ma_atomic_fetch_xor_explicit_i32(dst, src, ma_atomic_memory_order_seq_cst) -#define ma_atomic_fetch_xor_i64(dst, src) ma_atomic_fetch_xor_explicit_i64(dst, src, ma_atomic_memory_order_seq_cst) -#define ma_atomic_fetch_and_i8( dst, src) ma_atomic_fetch_and_explicit_i8( dst, src, ma_atomic_memory_order_seq_cst) -#define ma_atomic_fetch_and_i16(dst, src) ma_atomic_fetch_and_explicit_i16(dst, src, ma_atomic_memory_order_seq_cst) -#define ma_atomic_fetch_and_i32(dst, src) ma_atomic_fetch_and_explicit_i32(dst, src, ma_atomic_memory_order_seq_cst) -#define ma_atomic_fetch_and_i64(dst, src) ma_atomic_fetch_and_explicit_i64(dst, src, ma_atomic_memory_order_seq_cst) -#define ma_atomic_compare_and_swap_i8( dst, expected, dedsired) (ma_int8 )ma_atomic_compare_and_swap_8( (ma_uint8* )dst, (ma_uint8 )expected, (ma_uint8 )dedsired) -#define ma_atomic_compare_and_swap_i16(dst, expected, dedsired) (ma_int16)ma_atomic_compare_and_swap_16((ma_uint16*)dst, (ma_uint16)expected, (ma_uint16)dedsired) -#define ma_atomic_compare_and_swap_i32(dst, expected, dedsired) (ma_int32)ma_atomic_compare_and_swap_32((ma_uint32*)dst, (ma_uint32)expected, (ma_uint32)dedsired) -#define ma_atomic_compare_and_swap_i64(dst, expected, dedsired) (ma_int64)ma_atomic_compare_and_swap_64((ma_uint64*)dst, (ma_uint64)expected, (ma_uint64)dedsired) -typedef union -{ - ma_uint32 i; - float f; -} ma_atomic_if32; -typedef union -{ - ma_uint64 i; - double f; -} ma_atomic_if64; -#define ma_atomic_clear_explicit_f32(ptr, order) ma_atomic_clear_explicit_32((ma_uint32*)ptr, order) -#define ma_atomic_clear_explicit_f64(ptr, order) ma_atomic_clear_explicit_64((ma_uint64*)ptr, order) -static MA_INLINE void ma_atomic_store_explicit_f32(volatile float* dst, float src, ma_atomic_memory_order order) -{ - ma_atomic_if32 x; - x.f = src; - ma_atomic_store_explicit_32((volatile ma_uint32*)dst, x.i, order); -} -static MA_INLINE void ma_atomic_store_explicit_f64(volatile double* dst, double src, ma_atomic_memory_order order) -{ - ma_atomic_if64 x; - x.f = src; - ma_atomic_store_explicit_64((volatile ma_uint64*)dst, x.i, order); -} -static MA_INLINE float ma_atomic_load_explicit_f32(volatile const float* ptr, ma_atomic_memory_order order) -{ - ma_atomic_if32 r; - r.i = ma_atomic_load_explicit_32((volatile const ma_uint32*)ptr, order); - return r.f; -} -static MA_INLINE double ma_atomic_load_explicit_f64(volatile const double* ptr, ma_atomic_memory_order order) -{ - ma_atomic_if64 r; - r.i = ma_atomic_load_explicit_64((volatile const ma_uint64*)ptr, order); - return r.f; -} -static MA_INLINE float ma_atomic_exchange_explicit_f32(volatile float* dst, float src, ma_atomic_memory_order order) -{ - ma_atomic_if32 r; - ma_atomic_if32 x; - x.f = src; - r.i = ma_atomic_exchange_explicit_32((volatile ma_uint32*)dst, x.i, order); - return r.f; -} -static MA_INLINE double ma_atomic_exchange_explicit_f64(volatile double* dst, double src, ma_atomic_memory_order order) -{ - ma_atomic_if64 r; - ma_atomic_if64 x; - x.f = src; - r.i = ma_atomic_exchange_explicit_64((volatile ma_uint64*)dst, x.i, order); - return r.f; -} -static MA_INLINE ma_bool32 ma_atomic_compare_exchange_strong_explicit_f32(volatile float* dst, float* expected, float desired, ma_atomic_memory_order successOrder, ma_atomic_memory_order failureOrder) -{ - ma_atomic_if32 d; - d.f = desired; - return ma_atomic_compare_exchange_strong_explicit_32((volatile ma_uint32*)dst, (ma_uint32*)expected, d.i, successOrder, failureOrder); -} -static MA_INLINE ma_bool32 ma_atomic_compare_exchange_strong_explicit_f64(volatile double* dst, double* expected, double desired, ma_atomic_memory_order successOrder, ma_atomic_memory_order failureOrder) -{ - ma_atomic_if64 d; - d.f = desired; - return ma_atomic_compare_exchange_strong_explicit_64((volatile ma_uint64*)dst, (ma_uint64*)expected, d.i, successOrder, failureOrder); -} -static MA_INLINE ma_bool32 ma_atomic_compare_exchange_weak_explicit_f32(volatile float* dst, float* expected, float desired, ma_atomic_memory_order successOrder, ma_atomic_memory_order failureOrder) -{ - ma_atomic_if32 d; - d.f = desired; - return ma_atomic_compare_exchange_weak_explicit_32((volatile ma_uint32*)dst, (ma_uint32*)expected, d.i, successOrder, failureOrder); -} -static MA_INLINE ma_bool32 ma_atomic_compare_exchange_weak_explicit_f64(volatile double* dst, double* expected, double desired, ma_atomic_memory_order successOrder, ma_atomic_memory_order failureOrder) -{ - ma_atomic_if64 d; - d.f = desired; - return ma_atomic_compare_exchange_weak_explicit_64((volatile ma_uint64*)dst, (ma_uint64*)expected, d.i, successOrder, failureOrder); -} -static MA_INLINE float ma_atomic_fetch_add_explicit_f32(volatile float* dst, float src, ma_atomic_memory_order order) -{ - ma_atomic_if32 r; - ma_atomic_if32 x; - x.f = src; - r.i = ma_atomic_fetch_add_explicit_32((volatile ma_uint32*)dst, x.i, order); - return r.f; -} -static MA_INLINE double ma_atomic_fetch_add_explicit_f64(volatile double* dst, double src, ma_atomic_memory_order order) -{ - ma_atomic_if64 r; - ma_atomic_if64 x; - x.f = src; - r.i = ma_atomic_fetch_add_explicit_64((volatile ma_uint64*)dst, x.i, order); - return r.f; -} -static MA_INLINE float ma_atomic_fetch_sub_explicit_f32(volatile float* dst, float src, ma_atomic_memory_order order) -{ - ma_atomic_if32 r; - ma_atomic_if32 x; - x.f = src; - r.i = ma_atomic_fetch_sub_explicit_32((volatile ma_uint32*)dst, x.i, order); - return r.f; -} -static MA_INLINE double ma_atomic_fetch_sub_explicit_f64(volatile double* dst, double src, ma_atomic_memory_order order) -{ - ma_atomic_if64 r; - ma_atomic_if64 x; - x.f = src; - r.i = ma_atomic_fetch_sub_explicit_64((volatile ma_uint64*)dst, x.i, order); - return r.f; -} -static MA_INLINE float ma_atomic_fetch_or_explicit_f32(volatile float* dst, float src, ma_atomic_memory_order order) -{ - ma_atomic_if32 r; - ma_atomic_if32 x; - x.f = src; - r.i = ma_atomic_fetch_or_explicit_32((volatile ma_uint32*)dst, x.i, order); - return r.f; -} -static MA_INLINE double ma_atomic_fetch_or_explicit_f64(volatile double* dst, double src, ma_atomic_memory_order order) -{ - ma_atomic_if64 r; - ma_atomic_if64 x; - x.f = src; - r.i = ma_atomic_fetch_or_explicit_64((volatile ma_uint64*)dst, x.i, order); - return r.f; -} -static MA_INLINE float ma_atomic_fetch_xor_explicit_f32(volatile float* dst, float src, ma_atomic_memory_order order) -{ - ma_atomic_if32 r; - ma_atomic_if32 x; - x.f = src; - r.i = ma_atomic_fetch_xor_explicit_32((volatile ma_uint32*)dst, x.i, order); - return r.f; -} -static MA_INLINE double ma_atomic_fetch_xor_explicit_f64(volatile double* dst, double src, ma_atomic_memory_order order) -{ - ma_atomic_if64 r; - ma_atomic_if64 x; - x.f = src; - r.i = ma_atomic_fetch_xor_explicit_64((volatile ma_uint64*)dst, x.i, order); - return r.f; -} -static MA_INLINE float ma_atomic_fetch_and_explicit_f32(volatile float* dst, float src, ma_atomic_memory_order order) -{ - ma_atomic_if32 r; - ma_atomic_if32 x; - x.f = src; - r.i = ma_atomic_fetch_and_explicit_32((volatile ma_uint32*)dst, x.i, order); - return r.f; -} -static MA_INLINE double ma_atomic_fetch_and_explicit_f64(volatile double* dst, double src, ma_atomic_memory_order order) -{ - ma_atomic_if64 r; - ma_atomic_if64 x; - x.f = src; - r.i = ma_atomic_fetch_and_explicit_64((volatile ma_uint64*)dst, x.i, order); - return r.f; -} -#define ma_atomic_clear_f32(ptr) (float )ma_atomic_clear_explicit_f32(ptr, ma_atomic_memory_order_seq_cst) -#define ma_atomic_clear_f64(ptr) (double)ma_atomic_clear_explicit_f64(ptr, ma_atomic_memory_order_seq_cst) -#define ma_atomic_store_f32(dst, src) ma_atomic_store_explicit_f32(dst, src, ma_atomic_memory_order_seq_cst) -#define ma_atomic_store_f64(dst, src) ma_atomic_store_explicit_f64(dst, src, ma_atomic_memory_order_seq_cst) -#define ma_atomic_load_f32(ptr) (float )ma_atomic_load_explicit_f32(ptr, ma_atomic_memory_order_seq_cst) -#define ma_atomic_load_f64(ptr) (double)ma_atomic_load_explicit_f64(ptr, ma_atomic_memory_order_seq_cst) -#define ma_atomic_exchange_f32(dst, src) (float )ma_atomic_exchange_explicit_f32(dst, src, ma_atomic_memory_order_seq_cst) -#define ma_atomic_exchange_f64(dst, src) (double)ma_atomic_exchange_explicit_f64(dst, src, ma_atomic_memory_order_seq_cst) -#define ma_atomic_compare_exchange_strong_f32(dst, expected, desired) ma_atomic_compare_exchange_strong_explicit_f32(dst, expected, desired, ma_atomic_memory_order_seq_cst, ma_atomic_memory_order_seq_cst) -#define ma_atomic_compare_exchange_strong_f64(dst, expected, desired) ma_atomic_compare_exchange_strong_explicit_f64(dst, expected, desired, ma_atomic_memory_order_seq_cst, ma_atomic_memory_order_seq_cst) -#define ma_atomic_compare_exchange_weak_f32(dst, expected, desired) ma_atomic_compare_exchange_weak_explicit_f32(dst, expected, desired, ma_atomic_memory_order_seq_cst, ma_atomic_memory_order_seq_cst) -#define ma_atomic_compare_exchange_weak_f64(dst, expected, desired) ma_atomic_compare_exchange_weak_explicit_f64(dst, expected, desired, ma_atomic_memory_order_seq_cst, ma_atomic_memory_order_seq_cst) -#define ma_atomic_fetch_add_f32(dst, src) ma_atomic_fetch_add_explicit_f32(dst, src, ma_atomic_memory_order_seq_cst) -#define ma_atomic_fetch_add_f64(dst, src) ma_atomic_fetch_add_explicit_f64(dst, src, ma_atomic_memory_order_seq_cst) -#define ma_atomic_fetch_sub_f32(dst, src) ma_atomic_fetch_sub_explicit_f32(dst, src, ma_atomic_memory_order_seq_cst) -#define ma_atomic_fetch_sub_f64(dst, src) ma_atomic_fetch_sub_explicit_f64(dst, src, ma_atomic_memory_order_seq_cst) -#define ma_atomic_fetch_or_f32(dst, src) ma_atomic_fetch_or_explicit_f32(dst, src, ma_atomic_memory_order_seq_cst) -#define ma_atomic_fetch_or_f64(dst, src) ma_atomic_fetch_or_explicit_f64(dst, src, ma_atomic_memory_order_seq_cst) -#define ma_atomic_fetch_xor_f32(dst, src) ma_atomic_fetch_xor_explicit_f32(dst, src, ma_atomic_memory_order_seq_cst) -#define ma_atomic_fetch_xor_f64(dst, src) ma_atomic_fetch_xor_explicit_f64(dst, src, ma_atomic_memory_order_seq_cst) -#define ma_atomic_fetch_and_f32(dst, src) ma_atomic_fetch_and_explicit_f32(dst, src, ma_atomic_memory_order_seq_cst) -#define ma_atomic_fetch_and_f64(dst, src) ma_atomic_fetch_and_explicit_f64(dst, src, ma_atomic_memory_order_seq_cst) -static MA_INLINE float ma_atomic_compare_and_swap_f32(volatile float* dst, float expected, float desired) -{ - ma_atomic_if32 r; - ma_atomic_if32 e, d; - e.f = expected; - d.f = desired; - r.i = ma_atomic_compare_and_swap_32((volatile ma_uint32*)dst, e.i, d.i); - return r.f; -} -static MA_INLINE double ma_atomic_compare_and_swap_f64(volatile double* dst, double expected, double desired) -{ - ma_atomic_if64 r; - ma_atomic_if64 e, d; - e.f = expected; - d.f = desired; - r.i = ma_atomic_compare_and_swap_64((volatile ma_uint64*)dst, e.i, d.i); - return r.f; -} -typedef ma_atomic_flag ma_atomic_spinlock; -static MA_INLINE void ma_atomic_spinlock_lock(volatile ma_atomic_spinlock* pSpinlock) -{ - for (;;) { - if (ma_atomic_flag_test_and_set_explicit(pSpinlock, ma_atomic_memory_order_acquire) == 0) { - break; - } - while (ma_atomic_flag_load_explicit(pSpinlock, ma_atomic_memory_order_relaxed) == 1) { - } - } -} -static MA_INLINE void ma_atomic_spinlock_unlock(volatile ma_atomic_spinlock* pSpinlock) -{ - ma_atomic_flag_clear_explicit(pSpinlock, ma_atomic_memory_order_release); -} -#if defined(__clang__) || (defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6))) - #pragma GCC diagnostic pop -#endif -#if defined(__cplusplus) -} -#endif -#endif -/* c89atomic.h end */ - -#define MA_ATOMIC_SAFE_TYPE_IMPL(c89TypeExtension, type) \ - static MA_INLINE ma_##type ma_atomic_##type##_get(ma_atomic_##type* x) \ - { \ - return (ma_##type)ma_atomic_load_##c89TypeExtension(&x->value); \ - } \ - static MA_INLINE void ma_atomic_##type##_set(ma_atomic_##type* x, ma_##type value) \ - { \ - ma_atomic_store_##c89TypeExtension(&x->value, value); \ - } \ - static MA_INLINE ma_##type ma_atomic_##type##_exchange(ma_atomic_##type* x, ma_##type value) \ - { \ - return (ma_##type)ma_atomic_exchange_##c89TypeExtension(&x->value, value); \ - } \ - static MA_INLINE ma_bool32 ma_atomic_##type##_compare_exchange(ma_atomic_##type* x, ma_##type* expected, ma_##type desired) \ - { \ - return ma_atomic_compare_exchange_weak_##c89TypeExtension(&x->value, expected, desired); \ - } \ - static MA_INLINE ma_##type ma_atomic_##type##_fetch_add(ma_atomic_##type* x, ma_##type y) \ - { \ - return (ma_##type)ma_atomic_fetch_add_##c89TypeExtension(&x->value, y); \ - } \ - static MA_INLINE ma_##type ma_atomic_##type##_fetch_sub(ma_atomic_##type* x, ma_##type y) \ - { \ - return (ma_##type)ma_atomic_fetch_sub_##c89TypeExtension(&x->value, y); \ - } \ - static MA_INLINE ma_##type ma_atomic_##type##_fetch_or(ma_atomic_##type* x, ma_##type y) \ - { \ - return (ma_##type)ma_atomic_fetch_or_##c89TypeExtension(&x->value, y); \ - } \ - static MA_INLINE ma_##type ma_atomic_##type##_fetch_xor(ma_atomic_##type* x, ma_##type y) \ - { \ - return (ma_##type)ma_atomic_fetch_xor_##c89TypeExtension(&x->value, y); \ - } \ - static MA_INLINE ma_##type ma_atomic_##type##_fetch_and(ma_atomic_##type* x, ma_##type y) \ - { \ - return (ma_##type)ma_atomic_fetch_and_##c89TypeExtension(&x->value, y); \ - } \ - static MA_INLINE ma_##type ma_atomic_##type##_compare_and_swap(ma_atomic_##type* x, ma_##type expected, ma_##type desired) \ - { \ - return (ma_##type)ma_atomic_compare_and_swap_##c89TypeExtension(&x->value, expected, desired); \ - } \ - -#define MA_ATOMIC_SAFE_TYPE_IMPL_PTR(type) \ - static MA_INLINE ma_##type* ma_atomic_ptr_##type##_get(ma_atomic_ptr_##type* x) \ - { \ - return ma_atomic_load_ptr((void**)&x->value); \ - } \ - static MA_INLINE void ma_atomic_ptr_##type##_set(ma_atomic_ptr_##type* x, ma_##type* value) \ - { \ - ma_atomic_store_ptr((void**)&x->value, (void*)value); \ - } \ - static MA_INLINE ma_##type* ma_atomic_ptr_##type##_exchange(ma_atomic_ptr_##type* x, ma_##type* value) \ - { \ - return ma_atomic_exchange_ptr((void**)&x->value, (void*)value); \ - } \ - static MA_INLINE ma_bool32 ma_atomic_ptr_##type##_compare_exchange(ma_atomic_ptr_##type* x, ma_##type** expected, ma_##type* desired) \ - { \ - return ma_atomic_compare_exchange_weak_ptr((void**)&x->value, (void*)expected, (void*)desired); \ - } \ - static MA_INLINE ma_##type* ma_atomic_ptr_##type##_compare_and_swap(ma_atomic_ptr_##type* x, ma_##type* expected, ma_##type* desired) \ - { \ - return (ma_##type*)ma_atomic_compare_and_swap_ptr((void**)&x->value, (void*)expected, (void*)desired); \ - } \ - -MA_ATOMIC_SAFE_TYPE_IMPL(32, uint32) -MA_ATOMIC_SAFE_TYPE_IMPL(i32, int32) -MA_ATOMIC_SAFE_TYPE_IMPL(64, uint64) -MA_ATOMIC_SAFE_TYPE_IMPL(f32, float) -MA_ATOMIC_SAFE_TYPE_IMPL(32, bool32) - -#if !defined(MA_NO_DEVICE_IO) -MA_ATOMIC_SAFE_TYPE_IMPL(i32, device_state) -#endif - - -MA_API ma_uint64 ma_calculate_frame_count_after_resampling(ma_uint32 sampleRateOut, ma_uint32 sampleRateIn, ma_uint64 frameCountIn) -{ - /* This is based on the calculation in ma_linear_resampler_get_expected_output_frame_count(). */ - ma_uint64 outputFrameCount; - ma_uint64 preliminaryInputFrameCountFromFrac; - ma_uint64 preliminaryInputFrameCount; - - if (sampleRateIn == 0 || sampleRateOut == 0 || frameCountIn == 0) { - return 0; - } - - if (sampleRateOut == sampleRateIn) { - return frameCountIn; - } - - outputFrameCount = (frameCountIn * sampleRateOut) / sampleRateIn; - - preliminaryInputFrameCountFromFrac = (outputFrameCount * (sampleRateIn / sampleRateOut)) / sampleRateOut; - preliminaryInputFrameCount = (outputFrameCount * (sampleRateIn % sampleRateOut)) + preliminaryInputFrameCountFromFrac; - - if (preliminaryInputFrameCount <= frameCountIn) { - outputFrameCount += 1; - } - - return outputFrameCount; -} - -#ifndef MA_DATA_CONVERTER_STACK_BUFFER_SIZE -#define MA_DATA_CONVERTER_STACK_BUFFER_SIZE 4096 -#endif - - - -#if defined(MA_WIN32) -static ma_result ma_result_from_GetLastError(DWORD error) -{ - switch (error) - { - case ERROR_SUCCESS: return MA_SUCCESS; - case ERROR_PATH_NOT_FOUND: return MA_DOES_NOT_EXIST; - case ERROR_TOO_MANY_OPEN_FILES: return MA_TOO_MANY_OPEN_FILES; - case ERROR_NOT_ENOUGH_MEMORY: return MA_OUT_OF_MEMORY; - case ERROR_DISK_FULL: return MA_NO_SPACE; - case ERROR_HANDLE_EOF: return MA_AT_END; - case ERROR_NEGATIVE_SEEK: return MA_BAD_SEEK; - case ERROR_INVALID_PARAMETER: return MA_INVALID_ARGS; - case ERROR_ACCESS_DENIED: return MA_ACCESS_DENIED; - case ERROR_SEM_TIMEOUT: return MA_TIMEOUT; - case ERROR_FILE_NOT_FOUND: return MA_DOES_NOT_EXIST; - default: break; - } - - return MA_ERROR; -} -#endif /* MA_WIN32 */ - - -/******************************************************************************* - -Threading - -*******************************************************************************/ -static MA_INLINE ma_result ma_spinlock_lock_ex(volatile ma_spinlock* pSpinlock, ma_bool32 yield) -{ - if (pSpinlock == NULL) { - return MA_INVALID_ARGS; - } - - for (;;) { - if (ma_atomic_exchange_explicit_32(pSpinlock, 1, ma_atomic_memory_order_acquire) == 0) { - break; - } - - while (ma_atomic_load_explicit_32(pSpinlock, ma_atomic_memory_order_relaxed) == 1) { - if (yield) { - ma_yield(); - } - } - } - - return MA_SUCCESS; -} - -MA_API ma_result ma_spinlock_lock(volatile ma_spinlock* pSpinlock) -{ - return ma_spinlock_lock_ex(pSpinlock, MA_TRUE); -} - -MA_API ma_result ma_spinlock_lock_noyield(volatile ma_spinlock* pSpinlock) -{ - return ma_spinlock_lock_ex(pSpinlock, MA_FALSE); -} - -MA_API ma_result ma_spinlock_unlock(volatile ma_spinlock* pSpinlock) -{ - if (pSpinlock == NULL) { - return MA_INVALID_ARGS; - } - - ma_atomic_store_explicit_32(pSpinlock, 0, ma_atomic_memory_order_release); - return MA_SUCCESS; -} - - -#ifndef MA_NO_THREADING -#if defined(MA_POSIX) - #define MA_THREADCALL - typedef void* ma_thread_result; -#elif defined(MA_WIN32) - #define MA_THREADCALL WINAPI - typedef unsigned long ma_thread_result; -#endif - -typedef ma_thread_result (MA_THREADCALL * ma_thread_entry_proc)(void* pData); - -#ifdef MA_POSIX -static ma_result ma_thread_create__posix(ma_thread* pThread, ma_thread_priority priority, size_t stackSize, ma_thread_entry_proc entryProc, void* pData) -{ - int result; - pthread_attr_t* pAttr = NULL; - -#if !defined(__EMSCRIPTEN__) && !defined(__3DS__) - /* Try setting the thread priority. It's not critical if anything fails here. */ - pthread_attr_t attr; - if (pthread_attr_init(&attr) == 0) { - int scheduler = -1; - - /* We successfully initialized our attributes object so we can assign the pointer so it's passed into pthread_create(). */ - pAttr = &attr; - - /* We need to set the scheduler policy. Only do this if the OS supports pthread_attr_setschedpolicy() */ - #if !defined(MA_BEOS) - { - if (priority == ma_thread_priority_idle) { - #ifdef SCHED_IDLE - if (pthread_attr_setschedpolicy(&attr, SCHED_IDLE) == 0) { - scheduler = SCHED_IDLE; - } - #endif - } else if (priority == ma_thread_priority_realtime) { - #ifdef SCHED_FIFO - if (pthread_attr_setschedpolicy(&attr, SCHED_FIFO) == 0) { - scheduler = SCHED_FIFO; - } - #endif - #ifdef MA_LINUX - } else { - scheduler = sched_getscheduler(0); - #endif - } - } - #endif - - if (stackSize > 0) { - pthread_attr_setstacksize(&attr, stackSize); - } - - if (scheduler != -1) { - int priorityMin = sched_get_priority_min(scheduler); - int priorityMax = sched_get_priority_max(scheduler); - int priorityStep = (priorityMax - priorityMin) / 7; /* 7 = number of priorities supported by miniaudio. */ - - struct sched_param sched; - if (pthread_attr_getschedparam(&attr, &sched) == 0) { - if (priority == ma_thread_priority_idle) { - sched.sched_priority = priorityMin; - } else if (priority == ma_thread_priority_realtime) { - #if defined(MA_PTHREAD_REALTIME_THREAD_PRIORITY) - { - sched.sched_priority = MA_PTHREAD_REALTIME_THREAD_PRIORITY; - } - #else - { - sched.sched_priority = priorityMax; - } - #endif - } else { - sched.sched_priority += ((int)priority + 5) * priorityStep; /* +5 because the lowest priority is -5. */ - } - - if (sched.sched_priority < priorityMin) { - sched.sched_priority = priorityMin; - } - if (sched.sched_priority > priorityMax) { - sched.sched_priority = priorityMax; - } - - /* I'm not treating a failure of setting the priority as a critical error so not aborting on failure here. */ - if (pthread_attr_setschedparam(&attr, &sched) == 0) { - #if !defined(MA_ANDROID) || (defined(__ANDROID_API__) && __ANDROID_API__ >= 28) - { - pthread_attr_setinheritsched(&attr, PTHREAD_EXPLICIT_SCHED); - } - #endif - } - } - } - } -#else - /* It's the emscripten build. We'll have a few unused parameters. */ - (void)priority; - (void)stackSize; -#endif - - result = pthread_create((pthread_t*)pThread, pAttr, entryProc, pData); - - /* The thread attributes object is no longer required. */ - if (pAttr != NULL) { - pthread_attr_destroy(pAttr); - } - - if (result != 0) { - return ma_result_from_errno(result); - } - - return MA_SUCCESS; -} - -static void ma_thread_wait__posix(ma_thread* pThread) -{ - pthread_join((pthread_t)*pThread, NULL); -} - - -static ma_result ma_mutex_init__posix(ma_mutex* pMutex) -{ - int result; - - if (pMutex == NULL) { - return MA_INVALID_ARGS; - } - - MA_ZERO_OBJECT(pMutex); - - result = pthread_mutex_init((pthread_mutex_t*)pMutex, NULL); - if (result != 0) { - return ma_result_from_errno(result); - } - - return MA_SUCCESS; -} - -static void ma_mutex_uninit__posix(ma_mutex* pMutex) -{ - pthread_mutex_destroy((pthread_mutex_t*)pMutex); -} - -static void ma_mutex_lock__posix(ma_mutex* pMutex) -{ - pthread_mutex_lock((pthread_mutex_t*)pMutex); -} - -static void ma_mutex_unlock__posix(ma_mutex* pMutex) -{ - pthread_mutex_unlock((pthread_mutex_t*)pMutex); -} - - -static ma_result ma_event_init__posix(ma_event* pEvent) -{ - int result; - - result = pthread_mutex_init((pthread_mutex_t*)&pEvent->lock, NULL); - if (result != 0) { - return ma_result_from_errno(result); - } - - result = pthread_cond_init((pthread_cond_t*)&pEvent->cond, NULL); - if (result != 0) { - pthread_mutex_destroy((pthread_mutex_t*)&pEvent->lock); - return ma_result_from_errno(result); - } - - pEvent->value = 0; - return MA_SUCCESS; -} - -static void ma_event_uninit__posix(ma_event* pEvent) -{ - pthread_cond_destroy((pthread_cond_t*)&pEvent->cond); - pthread_mutex_destroy((pthread_mutex_t*)&pEvent->lock); -} - -static ma_result ma_event_wait__posix(ma_event* pEvent) -{ - pthread_mutex_lock((pthread_mutex_t*)&pEvent->lock); - { - while (pEvent->value == 0) { - pthread_cond_wait((pthread_cond_t*)&pEvent->cond, (pthread_mutex_t*)&pEvent->lock); - } - pEvent->value = 0; /* Auto-reset. */ - } - pthread_mutex_unlock((pthread_mutex_t*)&pEvent->lock); - - return MA_SUCCESS; -} - -static ma_result ma_event_signal__posix(ma_event* pEvent) -{ - pthread_mutex_lock((pthread_mutex_t*)&pEvent->lock); - { - pEvent->value = 1; - pthread_cond_signal((pthread_cond_t*)&pEvent->cond); - } - pthread_mutex_unlock((pthread_mutex_t*)&pEvent->lock); - - return MA_SUCCESS; -} - - -static ma_result ma_semaphore_init__posix(int initialValue, ma_semaphore* pSemaphore) -{ - int result; - - if (pSemaphore == NULL) { - return MA_INVALID_ARGS; - } - - pSemaphore->value = initialValue; - - result = pthread_mutex_init((pthread_mutex_t*)&pSemaphore->lock, NULL); - if (result != 0) { - return ma_result_from_errno(result); /* Failed to create mutex. */ - } - - result = pthread_cond_init((pthread_cond_t*)&pSemaphore->cond, NULL); - if (result != 0) { - pthread_mutex_destroy((pthread_mutex_t*)&pSemaphore->lock); - return ma_result_from_errno(result); /* Failed to create condition variable. */ - } - - return MA_SUCCESS; -} - -static void ma_semaphore_uninit__posix(ma_semaphore* pSemaphore) -{ - if (pSemaphore == NULL) { - return; - } - - pthread_cond_destroy((pthread_cond_t*)&pSemaphore->cond); - pthread_mutex_destroy((pthread_mutex_t*)&pSemaphore->lock); -} - -static ma_result ma_semaphore_wait__posix(ma_semaphore* pSemaphore) -{ - if (pSemaphore == NULL) { - return MA_INVALID_ARGS; - } - - pthread_mutex_lock((pthread_mutex_t*)&pSemaphore->lock); - { - /* We need to wait on a condition variable before escaping. We can't return from this function until the semaphore has been signaled. */ - while (pSemaphore->value == 0) { - pthread_cond_wait((pthread_cond_t*)&pSemaphore->cond, (pthread_mutex_t*)&pSemaphore->lock); - } - - pSemaphore->value -= 1; - } - pthread_mutex_unlock((pthread_mutex_t*)&pSemaphore->lock); - - return MA_SUCCESS; -} - -static ma_result ma_semaphore_release__posix(ma_semaphore* pSemaphore) -{ - if (pSemaphore == NULL) { - return MA_INVALID_ARGS; - } - - pthread_mutex_lock((pthread_mutex_t*)&pSemaphore->lock); - { - pSemaphore->value += 1; - pthread_cond_signal((pthread_cond_t*)&pSemaphore->cond); - } - pthread_mutex_unlock((pthread_mutex_t*)&pSemaphore->lock); - - return MA_SUCCESS; -} -#elif defined(MA_WIN32) -static int ma_thread_priority_to_win32(ma_thread_priority priority) -{ - switch (priority) { - case ma_thread_priority_idle: return THREAD_PRIORITY_IDLE; - case ma_thread_priority_lowest: return THREAD_PRIORITY_LOWEST; - case ma_thread_priority_low: return THREAD_PRIORITY_BELOW_NORMAL; - case ma_thread_priority_normal: return THREAD_PRIORITY_NORMAL; - case ma_thread_priority_high: return THREAD_PRIORITY_ABOVE_NORMAL; - case ma_thread_priority_highest: return THREAD_PRIORITY_HIGHEST; - case ma_thread_priority_realtime: return THREAD_PRIORITY_TIME_CRITICAL; - default: return THREAD_PRIORITY_NORMAL; - } -} - -static ma_result ma_thread_create__win32(ma_thread* pThread, ma_thread_priority priority, size_t stackSize, ma_thread_entry_proc entryProc, void* pData) -{ - DWORD threadID; /* Not used. Only used for passing into CreateThread() so it doesn't fail on Windows 98. */ - - *pThread = CreateThread(NULL, stackSize, entryProc, pData, 0, &threadID); - if (*pThread == NULL) { - return ma_result_from_GetLastError(GetLastError()); - } - - SetThreadPriority((HANDLE)*pThread, ma_thread_priority_to_win32(priority)); - - return MA_SUCCESS; -} - -static void ma_thread_wait__win32(ma_thread* pThread) -{ - WaitForSingleObject((HANDLE)*pThread, INFINITE); - CloseHandle((HANDLE)*pThread); -} - - -static ma_result ma_mutex_init__win32(ma_mutex* pMutex) -{ - *pMutex = CreateEventA(NULL, FALSE, TRUE, NULL); - if (*pMutex == NULL) { - return ma_result_from_GetLastError(GetLastError()); - } - - return MA_SUCCESS; -} - -static void ma_mutex_uninit__win32(ma_mutex* pMutex) -{ - CloseHandle((HANDLE)*pMutex); -} - -static void ma_mutex_lock__win32(ma_mutex* pMutex) -{ - WaitForSingleObject((HANDLE)*pMutex, INFINITE); -} - -static void ma_mutex_unlock__win32(ma_mutex* pMutex) -{ - SetEvent((HANDLE)*pMutex); -} - - -static ma_result ma_event_init__win32(ma_event* pEvent) -{ - *pEvent = CreateEventA(NULL, FALSE, FALSE, NULL); - if (*pEvent == NULL) { - return ma_result_from_GetLastError(GetLastError()); - } - - return MA_SUCCESS; -} - -static void ma_event_uninit__win32(ma_event* pEvent) -{ - CloseHandle((HANDLE)*pEvent); -} - -static ma_result ma_event_wait__win32(ma_event* pEvent) -{ - DWORD result = WaitForSingleObject((HANDLE)*pEvent, INFINITE); - if (result == WAIT_OBJECT_0) { - return MA_SUCCESS; - } - - if (result == WAIT_TIMEOUT) { - return MA_TIMEOUT; - } - - return ma_result_from_GetLastError(GetLastError()); -} - -static ma_result ma_event_signal__win32(ma_event* pEvent) -{ - BOOL result = SetEvent((HANDLE)*pEvent); - if (result == 0) { - return ma_result_from_GetLastError(GetLastError()); - } - - return MA_SUCCESS; -} - - -static ma_result ma_semaphore_init__win32(int initialValue, ma_semaphore* pSemaphore) -{ - *pSemaphore = CreateSemaphoreW(NULL, (LONG)initialValue, LONG_MAX, NULL); - if (*pSemaphore == NULL) { - return ma_result_from_GetLastError(GetLastError()); - } - - return MA_SUCCESS; -} - -static void ma_semaphore_uninit__win32(ma_semaphore* pSemaphore) -{ - CloseHandle((HANDLE)*pSemaphore); -} - -static ma_result ma_semaphore_wait__win32(ma_semaphore* pSemaphore) -{ - DWORD result = WaitForSingleObject((HANDLE)*pSemaphore, INFINITE); - if (result == WAIT_OBJECT_0) { - return MA_SUCCESS; - } - - if (result == WAIT_TIMEOUT) { - return MA_TIMEOUT; - } - - return ma_result_from_GetLastError(GetLastError()); -} - -static ma_result ma_semaphore_release__win32(ma_semaphore* pSemaphore) -{ - BOOL result = ReleaseSemaphore((HANDLE)*pSemaphore, 1, NULL); - if (result == 0) { - return ma_result_from_GetLastError(GetLastError()); - } - - return MA_SUCCESS; -} -#endif - -typedef struct -{ - ma_thread_entry_proc entryProc; - void* pData; - ma_allocation_callbacks allocationCallbacks; -} ma_thread_proxy_data; - -static ma_thread_result MA_THREADCALL ma_thread_entry_proxy(void* pData) -{ - ma_thread_proxy_data* pProxyData = (ma_thread_proxy_data*)pData; - ma_thread_entry_proc entryProc; - void* pEntryProcData; - ma_thread_result result; - - #if defined(MA_ON_THREAD_ENTRY) - MA_ON_THREAD_ENTRY - #endif - - entryProc = pProxyData->entryProc; - pEntryProcData = pProxyData->pData; - - /* Free the proxy data before getting into the real thread entry proc. */ - ma_free(pProxyData, &pProxyData->allocationCallbacks); - - result = entryProc(pEntryProcData); - - #if defined(MA_ON_THREAD_EXIT) - MA_ON_THREAD_EXIT - #endif - - return result; -} - -static ma_result ma_thread_create(ma_thread* pThread, ma_thread_priority priority, size_t stackSize, ma_thread_entry_proc entryProc, void* pData, const ma_allocation_callbacks* pAllocationCallbacks) -{ - ma_result result; - ma_thread_proxy_data* pProxyData; - - if (pThread == NULL || entryProc == NULL) { - return MA_INVALID_ARGS; - } - - pProxyData = (ma_thread_proxy_data*)ma_malloc(sizeof(*pProxyData), pAllocationCallbacks); /* Will be freed by the proxy entry proc. */ - if (pProxyData == NULL) { - return MA_OUT_OF_MEMORY; - } - -#if defined(MA_THREAD_DEFAULT_STACK_SIZE) - if (stackSize == 0) { - stackSize = MA_THREAD_DEFAULT_STACK_SIZE; - } -#endif - - pProxyData->entryProc = entryProc; - pProxyData->pData = pData; - ma_allocation_callbacks_init_copy(&pProxyData->allocationCallbacks, pAllocationCallbacks); - -#if defined(MA_POSIX) - result = ma_thread_create__posix(pThread, priority, stackSize, ma_thread_entry_proxy, pProxyData); -#elif defined(MA_WIN32) - result = ma_thread_create__win32(pThread, priority, stackSize, ma_thread_entry_proxy, pProxyData); -#endif - - if (result != MA_SUCCESS) { - ma_free(pProxyData, pAllocationCallbacks); - return result; - } - - return MA_SUCCESS; -} - -static void ma_thread_wait(ma_thread* pThread) -{ - if (pThread == NULL) { - return; - } - -#if defined(MA_POSIX) - ma_thread_wait__posix(pThread); -#elif defined(MA_WIN32) - ma_thread_wait__win32(pThread); -#endif -} - - -MA_API ma_result ma_mutex_init(ma_mutex* pMutex) -{ - if (pMutex == NULL) { - MA_ASSERT(MA_FALSE); /* Fire an assert so the caller is aware of this bug. */ - return MA_INVALID_ARGS; - } - -#if defined(MA_POSIX) - return ma_mutex_init__posix(pMutex); -#elif defined(MA_WIN32) - return ma_mutex_init__win32(pMutex); -#endif -} - -MA_API void ma_mutex_uninit(ma_mutex* pMutex) -{ - if (pMutex == NULL) { - return; - } - -#if defined(MA_POSIX) - ma_mutex_uninit__posix(pMutex); -#elif defined(MA_WIN32) - ma_mutex_uninit__win32(pMutex); -#endif -} - -MA_API void ma_mutex_lock(ma_mutex* pMutex) -{ - if (pMutex == NULL) { - MA_ASSERT(MA_FALSE); /* Fire an assert so the caller is aware of this bug. */ - return; - } - -#if defined(MA_POSIX) - ma_mutex_lock__posix(pMutex); -#elif defined(MA_WIN32) - ma_mutex_lock__win32(pMutex); -#endif -} - -MA_API void ma_mutex_unlock(ma_mutex* pMutex) -{ - if (pMutex == NULL) { - MA_ASSERT(MA_FALSE); /* Fire an assert so the caller is aware of this bug. */ - return; - } - -#if defined(MA_POSIX) - ma_mutex_unlock__posix(pMutex); -#elif defined(MA_WIN32) - ma_mutex_unlock__win32(pMutex); -#endif -} - - -MA_API ma_result ma_event_init(ma_event* pEvent) -{ - if (pEvent == NULL) { - MA_ASSERT(MA_FALSE); /* Fire an assert so the caller is aware of this bug. */ - return MA_INVALID_ARGS; - } - -#if defined(MA_POSIX) - return ma_event_init__posix(pEvent); -#elif defined(MA_WIN32) - return ma_event_init__win32(pEvent); -#endif -} - -#if 0 -static ma_result ma_event_alloc_and_init(ma_event** ppEvent, ma_allocation_callbacks* pAllocationCallbacks) -{ - ma_result result; - ma_event* pEvent; - - if (ppEvent == NULL) { - return MA_INVALID_ARGS; - } - - *ppEvent = NULL; - - pEvent = ma_malloc(sizeof(*pEvent), pAllocationCallbacks); - if (pEvent == NULL) { - return MA_OUT_OF_MEMORY; - } - - result = ma_event_init(pEvent); - if (result != MA_SUCCESS) { - ma_free(pEvent, pAllocationCallbacks); - return result; - } - - *ppEvent = pEvent; - return result; -} -#endif - -MA_API void ma_event_uninit(ma_event* pEvent) -{ - if (pEvent == NULL) { - return; - } - -#if defined(MA_POSIX) - ma_event_uninit__posix(pEvent); -#elif defined(MA_WIN32) - ma_event_uninit__win32(pEvent); -#endif -} - -#if 0 -static void ma_event_uninit_and_free(ma_event* pEvent, ma_allocation_callbacks* pAllocationCallbacks) -{ - if (pEvent == NULL) { - return; - } - - ma_event_uninit(pEvent); - ma_free(pEvent, pAllocationCallbacks); -} -#endif - -MA_API ma_result ma_event_wait(ma_event* pEvent) -{ - if (pEvent == NULL) { - MA_ASSERT(MA_FALSE); /* Fire an assert to the caller is aware of this bug. */ - return MA_INVALID_ARGS; - } - -#if defined(MA_POSIX) - return ma_event_wait__posix(pEvent); -#elif defined(MA_WIN32) - return ma_event_wait__win32(pEvent); -#endif -} - -MA_API ma_result ma_event_signal(ma_event* pEvent) -{ - if (pEvent == NULL) { - MA_ASSERT(MA_FALSE); /* Fire an assert to the caller is aware of this bug. */ - return MA_INVALID_ARGS; - } - -#if defined(MA_POSIX) - return ma_event_signal__posix(pEvent); -#elif defined(MA_WIN32) - return ma_event_signal__win32(pEvent); -#endif -} - - -MA_API ma_result ma_semaphore_init(int initialValue, ma_semaphore* pSemaphore) -{ - if (pSemaphore == NULL) { - MA_ASSERT(MA_FALSE); /* Fire an assert so the caller is aware of this bug. */ - return MA_INVALID_ARGS; - } - -#if defined(MA_POSIX) - return ma_semaphore_init__posix(initialValue, pSemaphore); -#elif defined(MA_WIN32) - return ma_semaphore_init__win32(initialValue, pSemaphore); -#endif -} - -MA_API void ma_semaphore_uninit(ma_semaphore* pSemaphore) -{ - if (pSemaphore == NULL) { - MA_ASSERT(MA_FALSE); /* Fire an assert so the caller is aware of this bug. */ - return; - } - -#if defined(MA_POSIX) - ma_semaphore_uninit__posix(pSemaphore); -#elif defined(MA_WIN32) - ma_semaphore_uninit__win32(pSemaphore); -#endif -} - -MA_API ma_result ma_semaphore_wait(ma_semaphore* pSemaphore) -{ - if (pSemaphore == NULL) { - MA_ASSERT(MA_FALSE); /* Fire an assert so the caller is aware of this bug. */ - return MA_INVALID_ARGS; - } - -#if defined(MA_POSIX) - return ma_semaphore_wait__posix(pSemaphore); -#elif defined(MA_WIN32) - return ma_semaphore_wait__win32(pSemaphore); -#endif -} - -MA_API ma_result ma_semaphore_release(ma_semaphore* pSemaphore) -{ - if (pSemaphore == NULL) { - MA_ASSERT(MA_FALSE); /* Fire an assert so the caller is aware of this bug. */ - return MA_INVALID_ARGS; - } - -#if defined(MA_POSIX) - return ma_semaphore_release__posix(pSemaphore); -#elif defined(MA_WIN32) - return ma_semaphore_release__win32(pSemaphore); -#endif -} -#else -/* MA_NO_THREADING is set which means threading is disabled. Threading is required by some API families. If any of these are enabled we need to throw an error. */ -#ifndef MA_NO_DEVICE_IO -#error "MA_NO_THREADING cannot be used without MA_NO_DEVICE_IO"; -#endif -#endif /* MA_NO_THREADING */ - - - -#define MA_FENCE_COUNTER_MAX 0x7FFFFFFF - -MA_API ma_result ma_fence_init(ma_fence* pFence) -{ - if (pFence == NULL) { - return MA_INVALID_ARGS; - } - - MA_ZERO_OBJECT(pFence); - pFence->counter = 0; - - #ifndef MA_NO_THREADING - { - ma_result result; - - result = ma_event_init(&pFence->e); - if (result != MA_SUCCESS) { - return result; - } - } - #endif - - return MA_SUCCESS; -} - -MA_API void ma_fence_uninit(ma_fence* pFence) -{ - if (pFence == NULL) { - return; - } - - #ifndef MA_NO_THREADING - { - ma_event_uninit(&pFence->e); - } - #endif - - MA_ZERO_OBJECT(pFence); -} - -MA_API ma_result ma_fence_acquire(ma_fence* pFence) -{ - if (pFence == NULL) { - return MA_INVALID_ARGS; - } - - for (;;) { - ma_uint32 oldCounter = ma_atomic_load_32(&pFence->counter); - ma_uint32 newCounter = oldCounter + 1; - - /* Make sure we're not about to exceed our maximum value. */ - if (newCounter > MA_FENCE_COUNTER_MAX) { - MA_ASSERT(MA_FALSE); - return MA_OUT_OF_RANGE; - } - - if (ma_atomic_compare_exchange_weak_32(&pFence->counter, &oldCounter, newCounter)) { - return MA_SUCCESS; - } else { - if (oldCounter == MA_FENCE_COUNTER_MAX) { - MA_ASSERT(MA_FALSE); - return MA_OUT_OF_RANGE; /* The other thread took the last available slot. Abort. */ - } - } - } - - /* Should never get here. */ - /*return MA_SUCCESS;*/ -} - -MA_API ma_result ma_fence_release(ma_fence* pFence) -{ - if (pFence == NULL) { - return MA_INVALID_ARGS; - } - - for (;;) { - ma_uint32 oldCounter = ma_atomic_load_32(&pFence->counter); - ma_uint32 newCounter = oldCounter - 1; - - if (oldCounter == 0) { - MA_ASSERT(MA_FALSE); - return MA_INVALID_OPERATION; /* Acquire/release mismatch. */ - } - - if (ma_atomic_compare_exchange_weak_32(&pFence->counter, &oldCounter, newCounter)) { - #ifndef MA_NO_THREADING - { - if (newCounter == 0) { - ma_event_signal(&pFence->e); /* <-- ma_fence_wait() will be waiting on this. */ - } - } - #endif - - return MA_SUCCESS; - } else { - if (oldCounter == 0) { - MA_ASSERT(MA_FALSE); - return MA_INVALID_OPERATION; /* Another thread has taken the 0 slot. Acquire/release mismatch. */ - } - } - } - - /* Should never get here. */ - /*return MA_SUCCESS;*/ -} - -MA_API ma_result ma_fence_wait(ma_fence* pFence) -{ - if (pFence == NULL) { - return MA_INVALID_ARGS; - } - - for (;;) { - ma_uint32 counter; - - counter = ma_atomic_load_32(&pFence->counter); - if (counter == 0) { - /* - Counter has hit zero. By the time we get here some other thread may have acquired the - fence again, but that is where the caller needs to take care with how they se the fence. - */ - return MA_SUCCESS; - } - - /* Getting here means the counter is > 0. We'll need to wait for something to happen. */ - #ifndef MA_NO_THREADING - { - ma_result result; - - result = ma_event_wait(&pFence->e); - if (result != MA_SUCCESS) { - return result; - } - } - #endif - } - - /* Should never get here. */ - /*return MA_INVALID_OPERATION;*/ -} - - -MA_API ma_result ma_async_notification_signal(ma_async_notification* pNotification) -{ - ma_async_notification_callbacks* pNotificationCallbacks = (ma_async_notification_callbacks*)pNotification; - - if (pNotification == NULL) { - return MA_INVALID_ARGS; - } - - if (pNotificationCallbacks->onSignal == NULL) { - return MA_NOT_IMPLEMENTED; - } - - pNotificationCallbacks->onSignal(pNotification); - return MA_INVALID_ARGS; -} - - -static void ma_async_notification_poll__on_signal(ma_async_notification* pNotification) -{ - ((ma_async_notification_poll*)pNotification)->signalled = MA_TRUE; -} - -MA_API ma_result ma_async_notification_poll_init(ma_async_notification_poll* pNotificationPoll) -{ - if (pNotificationPoll == NULL) { - return MA_INVALID_ARGS; - } - - pNotificationPoll->cb.onSignal = ma_async_notification_poll__on_signal; - pNotificationPoll->signalled = MA_FALSE; - - return MA_SUCCESS; -} - -MA_API ma_bool32 ma_async_notification_poll_is_signalled(const ma_async_notification_poll* pNotificationPoll) -{ - if (pNotificationPoll == NULL) { - return MA_FALSE; - } - - return pNotificationPoll->signalled; -} - - -static void ma_async_notification_event__on_signal(ma_async_notification* pNotification) -{ - ma_async_notification_event_signal((ma_async_notification_event*)pNotification); -} - -MA_API ma_result ma_async_notification_event_init(ma_async_notification_event* pNotificationEvent) -{ - if (pNotificationEvent == NULL) { - return MA_INVALID_ARGS; - } - - pNotificationEvent->cb.onSignal = ma_async_notification_event__on_signal; - - #ifndef MA_NO_THREADING - { - ma_result result; - - result = ma_event_init(&pNotificationEvent->e); - if (result != MA_SUCCESS) { - return result; - } - - return MA_SUCCESS; - } - #else - { - return MA_NOT_IMPLEMENTED; /* Threading is disabled. */ - } - #endif -} - -MA_API ma_result ma_async_notification_event_uninit(ma_async_notification_event* pNotificationEvent) -{ - if (pNotificationEvent == NULL) { - return MA_INVALID_ARGS; - } - - #ifndef MA_NO_THREADING - { - ma_event_uninit(&pNotificationEvent->e); - return MA_SUCCESS; - } - #else - { - return MA_NOT_IMPLEMENTED; /* Threading is disabled. */ - } - #endif -} - -MA_API ma_result ma_async_notification_event_wait(ma_async_notification_event* pNotificationEvent) -{ - if (pNotificationEvent == NULL) { - return MA_INVALID_ARGS; - } - - #ifndef MA_NO_THREADING - { - return ma_event_wait(&pNotificationEvent->e); - } - #else - { - return MA_NOT_IMPLEMENTED; /* Threading is disabled. */ - } - #endif -} - -MA_API ma_result ma_async_notification_event_signal(ma_async_notification_event* pNotificationEvent) -{ - if (pNotificationEvent == NULL) { - return MA_INVALID_ARGS; - } - - #ifndef MA_NO_THREADING - { - return ma_event_signal(&pNotificationEvent->e); - } - #else - { - return MA_NOT_IMPLEMENTED; /* Threading is disabled. */ - } - #endif -} - - - -/************************************************************************************************************************************************************ - -Job Queue - -************************************************************************************************************************************************************/ -MA_API ma_slot_allocator_config ma_slot_allocator_config_init(ma_uint32 capacity) -{ - ma_slot_allocator_config config; - - MA_ZERO_OBJECT(&config); - config.capacity = capacity; - - return config; -} - - -static MA_INLINE ma_uint32 ma_slot_allocator_calculate_group_capacity(ma_uint32 slotCapacity) -{ - ma_uint32 cap = slotCapacity / 32; - if ((slotCapacity % 32) != 0) { - cap += 1; - } - - return cap; -} - -static MA_INLINE ma_uint32 ma_slot_allocator_group_capacity(const ma_slot_allocator* pAllocator) -{ - return ma_slot_allocator_calculate_group_capacity(pAllocator->capacity); -} - - -typedef struct -{ - size_t sizeInBytes; - size_t groupsOffset; - size_t slotsOffset; -} ma_slot_allocator_heap_layout; - -static ma_result ma_slot_allocator_get_heap_layout(const ma_slot_allocator_config* pConfig, ma_slot_allocator_heap_layout* pHeapLayout) -{ - MA_ASSERT(pHeapLayout != NULL); - - MA_ZERO_OBJECT(pHeapLayout); - - if (pConfig == NULL) { - return MA_INVALID_ARGS; - } - - if (pConfig->capacity == 0) { - return MA_INVALID_ARGS; - } - - pHeapLayout->sizeInBytes = 0; - - /* Groups. */ - pHeapLayout->groupsOffset = pHeapLayout->sizeInBytes; - pHeapLayout->sizeInBytes += ma_align_64(ma_slot_allocator_calculate_group_capacity(pConfig->capacity) * sizeof(ma_slot_allocator_group)); - - /* Slots. */ - pHeapLayout->slotsOffset = pHeapLayout->sizeInBytes; - pHeapLayout->sizeInBytes += ma_align_64(pConfig->capacity * sizeof(ma_uint32)); - - return MA_SUCCESS; -} - -MA_API ma_result ma_slot_allocator_get_heap_size(const ma_slot_allocator_config* pConfig, size_t* pHeapSizeInBytes) -{ - ma_result result; - ma_slot_allocator_heap_layout layout; - - if (pHeapSizeInBytes == NULL) { - return MA_INVALID_ARGS; - } - - *pHeapSizeInBytes = 0; - - result = ma_slot_allocator_get_heap_layout(pConfig, &layout); - if (result != MA_SUCCESS) { - return result; - } - - *pHeapSizeInBytes = layout.sizeInBytes; - - return result; -} - -MA_API ma_result ma_slot_allocator_init_preallocated(const ma_slot_allocator_config* pConfig, void* pHeap, ma_slot_allocator* pAllocator) -{ - ma_result result; - ma_slot_allocator_heap_layout heapLayout; - - if (pAllocator == NULL) { - return MA_INVALID_ARGS; - } - - MA_ZERO_OBJECT(pAllocator); - - if (pHeap == NULL) { - return MA_INVALID_ARGS; - } - - result = ma_slot_allocator_get_heap_layout(pConfig, &heapLayout); - if (result != MA_SUCCESS) { - return result; - } - - pAllocator->_pHeap = pHeap; - MA_ZERO_MEMORY(pHeap, heapLayout.sizeInBytes); - - pAllocator->pGroups = (ma_slot_allocator_group*)ma_offset_ptr(pHeap, heapLayout.groupsOffset); - pAllocator->pSlots = (ma_uint32*)ma_offset_ptr(pHeap, heapLayout.slotsOffset); - pAllocator->capacity = pConfig->capacity; - - return MA_SUCCESS; -} - -MA_API ma_result ma_slot_allocator_init(const ma_slot_allocator_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_slot_allocator* pAllocator) -{ - ma_result result; - size_t heapSizeInBytes; - void* pHeap; - - result = ma_slot_allocator_get_heap_size(pConfig, &heapSizeInBytes); - if (result != MA_SUCCESS) { - return result; /* Failed to retrieve the size of the heap allocation. */ - } - - if (heapSizeInBytes > 0) { - pHeap = ma_malloc(heapSizeInBytes, pAllocationCallbacks); - if (pHeap == NULL) { - return MA_OUT_OF_MEMORY; - } - } else { - pHeap = NULL; - } - - result = ma_slot_allocator_init_preallocated(pConfig, pHeap, pAllocator); - if (result != MA_SUCCESS) { - ma_free(pHeap, pAllocationCallbacks); - return result; - } - - pAllocator->_ownsHeap = MA_TRUE; - return MA_SUCCESS; -} - -MA_API void ma_slot_allocator_uninit(ma_slot_allocator* pAllocator, const ma_allocation_callbacks* pAllocationCallbacks) -{ - if (pAllocator == NULL) { - return; - } - - if (pAllocator->_ownsHeap) { - ma_free(pAllocator->_pHeap, pAllocationCallbacks); - } -} - -MA_API ma_result ma_slot_allocator_alloc(ma_slot_allocator* pAllocator, ma_uint64* pSlot) -{ - ma_uint32 iAttempt; - const ma_uint32 maxAttempts = 2; /* The number of iterations to perform until returning MA_OUT_OF_MEMORY if no slots can be found. */ - - if (pAllocator == NULL || pSlot == NULL) { - return MA_INVALID_ARGS; - } - - for (iAttempt = 0; iAttempt < maxAttempts; iAttempt += 1) { - /* We need to acquire a suitable bitfield first. This is a bitfield that's got an available slot within it. */ - ma_uint32 iGroup; - for (iGroup = 0; iGroup < ma_slot_allocator_group_capacity(pAllocator); iGroup += 1) { - /* CAS */ - for (;;) { - ma_uint32 oldBitfield; - ma_uint32 newBitfield; - ma_uint32 bitOffset; - - oldBitfield = ma_atomic_load_32(&pAllocator->pGroups[iGroup].bitfield); /* <-- This copy must happen. The compiler must not optimize this away. */ - - /* Fast check to see if anything is available. */ - if (oldBitfield == 0xFFFFFFFF) { - break; /* No available bits in this bitfield. */ - } - - bitOffset = ma_ffs_32(~oldBitfield); - MA_ASSERT(bitOffset < 32); - - newBitfield = oldBitfield | (1 << bitOffset); - - if (ma_atomic_compare_and_swap_32(&pAllocator->pGroups[iGroup].bitfield, oldBitfield, newBitfield) == oldBitfield) { - ma_uint32 slotIndex; - - /* Increment the counter as soon as possible to have other threads report out-of-memory sooner than later. */ - ma_atomic_fetch_add_32(&pAllocator->count, 1); - - /* The slot index is required for constructing the output value. */ - slotIndex = (iGroup << 5) + bitOffset; /* iGroup << 5 = iGroup * 32 */ - if (slotIndex >= pAllocator->capacity) { - return MA_OUT_OF_MEMORY; - } - - /* Increment the reference count before constructing the output value. */ - pAllocator->pSlots[slotIndex] += 1; - - /* Construct the output value. */ - *pSlot = (((ma_uint64)pAllocator->pSlots[slotIndex] << 32) | slotIndex); - - return MA_SUCCESS; - } - } - } - - /* We weren't able to find a slot. If it's because we've reached our capacity we need to return MA_OUT_OF_MEMORY. Otherwise we need to do another iteration and try again. */ - if (pAllocator->count < pAllocator->capacity) { - ma_yield(); - } else { - return MA_OUT_OF_MEMORY; - } - } - - /* We couldn't find a slot within the maximum number of attempts. */ - return MA_OUT_OF_MEMORY; -} - -MA_API ma_result ma_slot_allocator_free(ma_slot_allocator* pAllocator, ma_uint64 slot) -{ - ma_uint32 iGroup; - ma_uint32 iBit; - - if (pAllocator == NULL) { - return MA_INVALID_ARGS; - } - - iGroup = (ma_uint32)((slot & 0xFFFFFFFF) >> 5); /* slot / 32 */ - iBit = (ma_uint32)((slot & 0xFFFFFFFF) & 31); /* slot % 32 */ - - if (iGroup >= ma_slot_allocator_group_capacity(pAllocator)) { - return MA_INVALID_ARGS; - } - - MA_ASSERT(iBit < 32); /* This must be true due to the logic we used to actually calculate it. */ - - while (ma_atomic_load_32(&pAllocator->count) > 0) { - /* CAS */ - ma_uint32 oldBitfield; - ma_uint32 newBitfield; - - oldBitfield = ma_atomic_load_32(&pAllocator->pGroups[iGroup].bitfield); /* <-- This copy must happen. The compiler must not optimize this away. */ - newBitfield = oldBitfield & ~(1 << iBit); - - /* Debugging for checking for double-frees. */ - #if defined(MA_DEBUG_OUTPUT) - { - if ((oldBitfield & (1 << iBit)) == 0) { - MA_ASSERT(MA_FALSE); /* Double free detected.*/ - } - } - #endif - - if (ma_atomic_compare_and_swap_32(&pAllocator->pGroups[iGroup].bitfield, oldBitfield, newBitfield) == oldBitfield) { - ma_atomic_fetch_sub_32(&pAllocator->count, 1); - return MA_SUCCESS; - } - } - - /* Getting here means there are no allocations available for freeing. */ - return MA_INVALID_OPERATION; -} - - -#define MA_JOB_ID_NONE ~((ma_uint64)0) -#define MA_JOB_SLOT_NONE (ma_uint16)(~0) - -static MA_INLINE ma_uint32 ma_job_extract_refcount(ma_uint64 toc) -{ - return (ma_uint32)(toc >> 32); -} - -static MA_INLINE ma_uint16 ma_job_extract_slot(ma_uint64 toc) -{ - return (ma_uint16)(toc & 0x0000FFFF); -} - -static MA_INLINE ma_uint16 ma_job_extract_code(ma_uint64 toc) -{ - return (ma_uint16)((toc & 0xFFFF0000) >> 16); -} - -static MA_INLINE ma_uint64 ma_job_toc_to_allocation(ma_uint64 toc) -{ - return ((ma_uint64)ma_job_extract_refcount(toc) << 32) | (ma_uint64)ma_job_extract_slot(toc); -} - -static MA_INLINE ma_uint64 ma_job_set_refcount(ma_uint64 toc, ma_uint32 refcount) -{ - /* Clear the reference count first. */ - toc = toc & ~((ma_uint64)0xFFFFFFFF << 32); - toc = toc | ((ma_uint64)refcount << 32); - - return toc; -} - - -MA_API ma_job ma_job_init(ma_uint16 code) -{ - ma_job job; - - MA_ZERO_OBJECT(&job); - job.toc.breakup.code = code; - job.toc.breakup.slot = MA_JOB_SLOT_NONE; /* Temp value. Will be allocated when posted to a queue. */ - job.next = MA_JOB_ID_NONE; - - return job; -} - - -static ma_result ma_job_process__noop(ma_job* pJob); -static ma_result ma_job_process__quit(ma_job* pJob); -static ma_result ma_job_process__custom(ma_job* pJob); -static ma_result ma_job_process__resource_manager__load_data_buffer_node(ma_job* pJob); -static ma_result ma_job_process__resource_manager__free_data_buffer_node(ma_job* pJob); -static ma_result ma_job_process__resource_manager__page_data_buffer_node(ma_job* pJob); -static ma_result ma_job_process__resource_manager__load_data_buffer(ma_job* pJob); -static ma_result ma_job_process__resource_manager__free_data_buffer(ma_job* pJob); -static ma_result ma_job_process__resource_manager__load_data_stream(ma_job* pJob); -static ma_result ma_job_process__resource_manager__free_data_stream(ma_job* pJob); -static ma_result ma_job_process__resource_manager__page_data_stream(ma_job* pJob); -static ma_result ma_job_process__resource_manager__seek_data_stream(ma_job* pJob); - -#if !defined(MA_NO_DEVICE_IO) -static ma_result ma_job_process__device__aaudio_reroute(ma_job* pJob); -#endif - -static ma_job_proc g_jobVTable[MA_JOB_TYPE_COUNT] = -{ - /* Miscellaneous. */ - ma_job_process__quit, /* MA_JOB_TYPE_QUIT */ - ma_job_process__custom, /* MA_JOB_TYPE_CUSTOM */ - - /* Resource Manager. */ - ma_job_process__resource_manager__load_data_buffer_node, /* MA_JOB_TYPE_RESOURCE_MANAGER_LOAD_DATA_BUFFER_NODE */ - ma_job_process__resource_manager__free_data_buffer_node, /* MA_JOB_TYPE_RESOURCE_MANAGER_FREE_DATA_BUFFER_NODE */ - ma_job_process__resource_manager__page_data_buffer_node, /* MA_JOB_TYPE_RESOURCE_MANAGER_PAGE_DATA_BUFFER_NODE */ - ma_job_process__resource_manager__load_data_buffer, /* MA_JOB_TYPE_RESOURCE_MANAGER_LOAD_DATA_BUFFER */ - ma_job_process__resource_manager__free_data_buffer, /* MA_JOB_TYPE_RESOURCE_MANAGER_FREE_DATA_BUFFER */ - ma_job_process__resource_manager__load_data_stream, /* MA_JOB_TYPE_RESOURCE_MANAGER_LOAD_DATA_STREAM */ - ma_job_process__resource_manager__free_data_stream, /* MA_JOB_TYPE_RESOURCE_MANAGER_FREE_DATA_STREAM */ - ma_job_process__resource_manager__page_data_stream, /* MA_JOB_TYPE_RESOURCE_MANAGER_PAGE_DATA_STREAM */ - ma_job_process__resource_manager__seek_data_stream, /* MA_JOB_TYPE_RESOURCE_MANAGER_SEEK_DATA_STREAM */ - - /* Device. */ -#if !defined(MA_NO_DEVICE_IO) - ma_job_process__device__aaudio_reroute /* MA_JOB_TYPE_DEVICE_AAUDIO_REROUTE */ -#endif -}; - -MA_API ma_result ma_job_process(ma_job* pJob) -{ - if (pJob == NULL) { - return MA_INVALID_ARGS; - } - - if (pJob->toc.breakup.code >= MA_JOB_TYPE_COUNT) { - return MA_INVALID_OPERATION; - } - - return g_jobVTable[pJob->toc.breakup.code](pJob); -} - -static ma_result ma_job_process__noop(ma_job* pJob) -{ - MA_ASSERT(pJob != NULL); - - /* No-op. */ - (void)pJob; - - return MA_SUCCESS; -} - -static ma_result ma_job_process__quit(ma_job* pJob) -{ - return ma_job_process__noop(pJob); -} - -static ma_result ma_job_process__custom(ma_job* pJob) -{ - MA_ASSERT(pJob != NULL); - - /* No-op if there's no callback. */ - if (pJob->data.custom.proc == NULL) { - return MA_SUCCESS; - } - - return pJob->data.custom.proc(pJob); -} - - - -MA_API ma_job_queue_config ma_job_queue_config_init(ma_uint32 flags, ma_uint32 capacity) -{ - ma_job_queue_config config; - - config.flags = flags; - config.capacity = capacity; - - return config; -} - - -typedef struct -{ - size_t sizeInBytes; - size_t allocatorOffset; - size_t jobsOffset; -} ma_job_queue_heap_layout; - -static ma_result ma_job_queue_get_heap_layout(const ma_job_queue_config* pConfig, ma_job_queue_heap_layout* pHeapLayout) -{ - ma_result result; - - MA_ASSERT(pHeapLayout != NULL); - - MA_ZERO_OBJECT(pHeapLayout); - - if (pConfig == NULL) { - return MA_INVALID_ARGS; - } - - if (pConfig->capacity == 0) { - return MA_INVALID_ARGS; - } - - pHeapLayout->sizeInBytes = 0; - - /* Allocator. */ - { - ma_slot_allocator_config allocatorConfig; - size_t allocatorHeapSizeInBytes; - - allocatorConfig = ma_slot_allocator_config_init(pConfig->capacity); - result = ma_slot_allocator_get_heap_size(&allocatorConfig, &allocatorHeapSizeInBytes); - if (result != MA_SUCCESS) { - return result; - } - - pHeapLayout->allocatorOffset = pHeapLayout->sizeInBytes; - pHeapLayout->sizeInBytes += allocatorHeapSizeInBytes; - } - - /* Jobs. */ - pHeapLayout->jobsOffset = pHeapLayout->sizeInBytes; - pHeapLayout->sizeInBytes += ma_align_64(pConfig->capacity * sizeof(ma_job)); - - return MA_SUCCESS; -} - -MA_API ma_result ma_job_queue_get_heap_size(const ma_job_queue_config* pConfig, size_t* pHeapSizeInBytes) -{ - ma_result result; - ma_job_queue_heap_layout layout; - - if (pHeapSizeInBytes == NULL) { - return MA_INVALID_ARGS; - } - - *pHeapSizeInBytes = 0; - - result = ma_job_queue_get_heap_layout(pConfig, &layout); - if (result != MA_SUCCESS) { - return result; - } - - *pHeapSizeInBytes = layout.sizeInBytes; - - return MA_SUCCESS; -} - -MA_API ma_result ma_job_queue_init_preallocated(const ma_job_queue_config* pConfig, void* pHeap, ma_job_queue* pQueue) -{ - ma_result result; - ma_job_queue_heap_layout heapLayout; - ma_slot_allocator_config allocatorConfig; - - if (pQueue == NULL) { - return MA_INVALID_ARGS; - } - - MA_ZERO_OBJECT(pQueue); - - result = ma_job_queue_get_heap_layout(pConfig, &heapLayout); - if (result != MA_SUCCESS) { - return result; - } - - pQueue->_pHeap = pHeap; - MA_ZERO_MEMORY(pHeap, heapLayout.sizeInBytes); - - pQueue->flags = pConfig->flags; - pQueue->capacity = pConfig->capacity; - pQueue->pJobs = (ma_job*)ma_offset_ptr(pHeap, heapLayout.jobsOffset); - - allocatorConfig = ma_slot_allocator_config_init(pConfig->capacity); - result = ma_slot_allocator_init_preallocated(&allocatorConfig, ma_offset_ptr(pHeap, heapLayout.allocatorOffset), &pQueue->allocator); - if (result != MA_SUCCESS) { - return result; - } - - /* We need a semaphore if we're running in non-blocking mode. If threading is disabled we need to return an error. */ - if ((pQueue->flags & MA_JOB_QUEUE_FLAG_NON_BLOCKING) == 0) { - #ifndef MA_NO_THREADING - { - ma_semaphore_init(0, &pQueue->sem); - } - #else - { - /* Threading is disabled and we've requested non-blocking mode. */ - return MA_INVALID_OPERATION; - } - #endif - } - - /* - Our queue needs to be initialized with a free standing node. This should always be slot 0. Required for the lock free algorithm. The first job in the queue is - just a dummy item for giving us the first item in the list which is stored in the "next" member. - */ - ma_slot_allocator_alloc(&pQueue->allocator, &pQueue->head); /* Will never fail. */ - pQueue->pJobs[ma_job_extract_slot(pQueue->head)].next = MA_JOB_ID_NONE; - pQueue->tail = pQueue->head; - - return MA_SUCCESS; -} - -MA_API ma_result ma_job_queue_init(const ma_job_queue_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_job_queue* pQueue) -{ - ma_result result; - size_t heapSizeInBytes; - void* pHeap; - - result = ma_job_queue_get_heap_size(pConfig, &heapSizeInBytes); - if (result != MA_SUCCESS) { - return result; - } - - if (heapSizeInBytes > 0) { - pHeap = ma_malloc(heapSizeInBytes, pAllocationCallbacks); - if (pHeap == NULL) { - return MA_OUT_OF_MEMORY; - } - } else { - pHeap = NULL; - } - - result = ma_job_queue_init_preallocated(pConfig, pHeap, pQueue); - if (result != MA_SUCCESS) { - ma_free(pHeap, pAllocationCallbacks); - return result; - } - - pQueue->_ownsHeap = MA_TRUE; - return MA_SUCCESS; -} - -MA_API void ma_job_queue_uninit(ma_job_queue* pQueue, const ma_allocation_callbacks* pAllocationCallbacks) -{ - if (pQueue == NULL) { - return; - } - - /* All we need to do is uninitialize the semaphore. */ - if ((pQueue->flags & MA_JOB_QUEUE_FLAG_NON_BLOCKING) == 0) { - #ifndef MA_NO_THREADING - { - ma_semaphore_uninit(&pQueue->sem); - } - #else - { - MA_ASSERT(MA_FALSE); /* Should never get here. Should have been checked at initialization time. */ - } - #endif - } - - ma_slot_allocator_uninit(&pQueue->allocator, pAllocationCallbacks); - - if (pQueue->_ownsHeap) { - ma_free(pQueue->_pHeap, pAllocationCallbacks); - } -} - -static ma_bool32 ma_job_queue_cas(volatile ma_uint64* dst, ma_uint64 expected, ma_uint64 desired) -{ - /* The new counter is taken from the expected value. */ - return ma_atomic_compare_and_swap_64(dst, expected, ma_job_set_refcount(desired, ma_job_extract_refcount(expected) + 1)) == expected; -} - -MA_API ma_result ma_job_queue_post(ma_job_queue* pQueue, const ma_job* pJob) -{ - /* - Lock free queue implementation based on the paper by Michael and Scott: Nonblocking Algorithms and Preemption-Safe Locking on Multiprogrammed Shared Memory Multiprocessors - */ - ma_result result; - ma_uint64 slot; - ma_uint64 tail; - ma_uint64 next; - - if (pQueue == NULL || pJob == NULL) { - return MA_INVALID_ARGS; - } - - /* We need a new slot. */ - result = ma_slot_allocator_alloc(&pQueue->allocator, &slot); - if (result != MA_SUCCESS) { - return result; /* Probably ran out of slots. If so, MA_OUT_OF_MEMORY will be returned. */ - } - - /* At this point we should have a slot to place the job. */ - MA_ASSERT(ma_job_extract_slot(slot) < pQueue->capacity); - - /* We need to put the job into memory before we do anything. */ - pQueue->pJobs[ma_job_extract_slot(slot)] = *pJob; - pQueue->pJobs[ma_job_extract_slot(slot)].toc.allocation = slot; /* This will overwrite the job code. */ - pQueue->pJobs[ma_job_extract_slot(slot)].toc.breakup.code = pJob->toc.breakup.code; /* The job code needs to be applied again because the line above overwrote it. */ - pQueue->pJobs[ma_job_extract_slot(slot)].next = MA_JOB_ID_NONE; /* Reset for safety. */ - - #ifndef MA_USE_EXPERIMENTAL_LOCK_FREE_JOB_QUEUE - ma_spinlock_lock(&pQueue->lock); - #endif - { - /* The job is stored in memory so now we need to add it to our linked list. We only ever add items to the end of the list. */ - for (;;) { - tail = ma_atomic_load_64(&pQueue->tail); - next = ma_atomic_load_64(&pQueue->pJobs[ma_job_extract_slot(tail)].next); - - if (ma_job_toc_to_allocation(tail) == ma_job_toc_to_allocation(ma_atomic_load_64(&pQueue->tail))) { - if (ma_job_extract_slot(next) == 0xFFFF) { - if (ma_job_queue_cas(&pQueue->pJobs[ma_job_extract_slot(tail)].next, next, slot)) { - break; - } - } else { - ma_job_queue_cas(&pQueue->tail, tail, ma_job_extract_slot(next)); - } - } - } - ma_job_queue_cas(&pQueue->tail, tail, slot); - } - #ifndef MA_USE_EXPERIMENTAL_LOCK_FREE_JOB_QUEUE - ma_spinlock_unlock(&pQueue->lock); - #endif - - - /* Signal the semaphore as the last step if we're using synchronous mode. */ - if ((pQueue->flags & MA_JOB_QUEUE_FLAG_NON_BLOCKING) == 0) { - #ifndef MA_NO_THREADING - { - ma_semaphore_release(&pQueue->sem); - } - #else - { - MA_ASSERT(MA_FALSE); /* Should never get here. Should have been checked at initialization time. */ - } - #endif - } - - return MA_SUCCESS; -} - -MA_API ma_result ma_job_queue_next(ma_job_queue* pQueue, ma_job* pJob) -{ - ma_uint64 head; - ma_uint64 tail; - ma_uint64 next; - - if (pQueue == NULL || pJob == NULL) { - return MA_INVALID_ARGS; - } - - /* If we're running in synchronous mode we'll need to wait on a semaphore. */ - if ((pQueue->flags & MA_JOB_QUEUE_FLAG_NON_BLOCKING) == 0) { - #ifndef MA_NO_THREADING - { - ma_semaphore_wait(&pQueue->sem); - } - #else - { - MA_ASSERT(MA_FALSE); /* Should never get here. Should have been checked at initialization time. */ - } - #endif - } - - #ifndef MA_USE_EXPERIMENTAL_LOCK_FREE_JOB_QUEUE - ma_spinlock_lock(&pQueue->lock); - #endif - { - /* - BUG: In lock-free mode, multiple threads can be in this section of code. The "head" variable in the loop below - is stored. One thread can fall through to the freeing of this item while another is still using "head" for the - retrieval of the "next" variable. - - The slot allocator might need to make use of some reference counting to ensure it's only truly freed when - there are no more references to the item. This must be fixed before removing these locks. - */ - - /* Now we need to remove the root item from the list. */ - for (;;) { - head = ma_atomic_load_64(&pQueue->head); - tail = ma_atomic_load_64(&pQueue->tail); - next = ma_atomic_load_64(&pQueue->pJobs[ma_job_extract_slot(head)].next); - - if (ma_job_toc_to_allocation(head) == ma_job_toc_to_allocation(ma_atomic_load_64(&pQueue->head))) { - if (ma_job_extract_slot(head) == ma_job_extract_slot(tail)) { - if (ma_job_extract_slot(next) == 0xFFFF) { - #ifndef MA_USE_EXPERIMENTAL_LOCK_FREE_JOB_QUEUE - ma_spinlock_unlock(&pQueue->lock); - #endif - return MA_NO_DATA_AVAILABLE; - } - ma_job_queue_cas(&pQueue->tail, tail, ma_job_extract_slot(next)); - } else { - *pJob = pQueue->pJobs[ma_job_extract_slot(next)]; - if (ma_job_queue_cas(&pQueue->head, head, ma_job_extract_slot(next))) { - break; - } - } - } - } - } - #ifndef MA_USE_EXPERIMENTAL_LOCK_FREE_JOB_QUEUE - ma_spinlock_unlock(&pQueue->lock); - #endif - - ma_slot_allocator_free(&pQueue->allocator, head); - - /* - If it's a quit job make sure it's put back on the queue to ensure other threads have an opportunity to detect it and terminate naturally. We - could instead just leave it on the queue, but that would involve fiddling with the lock-free code above and I want to keep that as simple as - possible. - */ - if (pJob->toc.breakup.code == MA_JOB_TYPE_QUIT) { - ma_job_queue_post(pQueue, pJob); - return MA_CANCELLED; /* Return a cancelled status just in case the thread is checking return codes and not properly checking for a quit job. */ - } - - return MA_SUCCESS; -} - - - -/******************************************************************************* - -Dynamic Linking - -*******************************************************************************/ -#ifdef MA_POSIX - /* No need for dlfcn.h if we're not using runtime linking. */ - #ifndef MA_NO_RUNTIME_LINKING - #include - #endif -#endif - -MA_API ma_handle ma_dlopen(ma_log* pLog, const char* filename) -{ -#ifndef MA_NO_RUNTIME_LINKING - ma_handle handle; - - ma_log_postf(pLog, MA_LOG_LEVEL_DEBUG, "Loading library: %s\n", filename); - - #ifdef MA_WIN32 - /* From MSDN: Desktop applications cannot use LoadPackagedLibrary; if a desktop application calls this function it fails with APPMODEL_ERROR_NO_PACKAGE.*/ - #if !defined(MA_WIN32_UWP) || !(defined(WINAPI_FAMILY) && ((defined(WINAPI_FAMILY_PHONE_APP) && WINAPI_FAMILY == WINAPI_FAMILY_PHONE_APP))) - handle = (ma_handle)LoadLibraryA(filename); - #else - /* *sigh* It appears there is no ANSI version of LoadPackagedLibrary()... */ - WCHAR filenameW[4096]; - if (MultiByteToWideChar(CP_UTF8, 0, filename, -1, filenameW, sizeof(filenameW)) == 0) { - handle = NULL; - } else { - handle = (ma_handle)LoadPackagedLibrary(filenameW, 0); - } - #endif - #else - handle = (ma_handle)dlopen(filename, RTLD_NOW); - #endif - - /* - I'm not considering failure to load a library an error nor a warning because seamlessly falling through to a lower-priority - backend is a deliberate design choice. Instead I'm logging it as an informational message. - */ - if (handle == NULL) { - ma_log_postf(pLog, MA_LOG_LEVEL_INFO, "Failed to load library: %s\n", filename); - } - - return handle; -#else - /* Runtime linking is disabled. */ - (void)pLog; - (void)filename; - return NULL; -#endif -} - -MA_API void ma_dlclose(ma_log* pLog, ma_handle handle) -{ -#ifndef MA_NO_RUNTIME_LINKING - #ifdef MA_WIN32 - FreeLibrary((HMODULE)handle); - #else - /* Hack for Android bug (see https://github.com/android/ndk/issues/360). Calling dlclose() pre-API 28 may segfault. */ - #if !defined(MA_ANDROID) || (defined(__ANDROID_API__) && __ANDROID_API__ >= 28) - { - dlclose((void*)handle); - } - #else - { - (void)handle; - } - #endif - #endif - - (void)pLog; -#else - /* Runtime linking is disabled. */ - (void)pLog; - (void)handle; -#endif -} - -MA_API ma_proc ma_dlsym(ma_log* pLog, ma_handle handle, const char* symbol) -{ -#ifndef MA_NO_RUNTIME_LINKING - ma_proc proc; - - ma_log_postf(pLog, MA_LOG_LEVEL_DEBUG, "Loading symbol: %s\n", symbol); - -#ifdef _WIN32 - proc = (ma_proc)GetProcAddress((HMODULE)handle, symbol); -#else -#if (defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 8))) || defined(__clang__) - #pragma GCC diagnostic push - #pragma GCC diagnostic ignored "-Wpedantic" -#endif - proc = (ma_proc)dlsym((void*)handle, symbol); -#if (defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 8))) || defined(__clang__) - #pragma GCC diagnostic pop -#endif -#endif - - if (proc == NULL) { - ma_log_postf(pLog, MA_LOG_LEVEL_WARNING, "Failed to load symbol: %s\n", symbol); - } - - (void)pLog; /* It's possible for pContext to be unused. */ - return proc; -#else - /* Runtime linking is disabled. */ - (void)pLog; - (void)handle; - (void)symbol; - return NULL; -#endif -} - - - -/************************************************************************************************************************************************************ -************************************************************************************************************************************************************* - -DEVICE I/O -========== - -************************************************************************************************************************************************************* -************************************************************************************************************************************************************/ - -/* Disable run-time linking on certain backends and platforms. */ -#ifndef MA_NO_RUNTIME_LINKING - #if defined(MA_EMSCRIPTEN) || defined(MA_ORBIS) || defined(MA_PROSPERO) - #define MA_NO_RUNTIME_LINKING - #endif -#endif - -#ifdef MA_APPLE - #include -#endif - -#ifndef MA_NO_DEVICE_IO - -#if defined(MA_APPLE) && (MAC_OS_X_VERSION_MIN_REQUIRED < 101200) - #include /* For mach_absolute_time() */ -#endif - -#ifdef MA_POSIX - #include - #include - - /* No need for dlfcn.h if we're not using runtime linking. */ - #ifndef MA_NO_RUNTIME_LINKING - #include - #endif -#endif - -/* This must be set to at least 26. */ -#ifndef MA_AAUDIO_MIN_ANDROID_SDK_VERSION -#define MA_AAUDIO_MIN_ANDROID_SDK_VERSION 27 -#endif - - -MA_API void ma_device_info_add_native_data_format(ma_device_info* pDeviceInfo, ma_format format, ma_uint32 channels, ma_uint32 sampleRate, ma_uint32 flags) -{ - if (pDeviceInfo == NULL) { - return; - } - - if (pDeviceInfo->nativeDataFormatCount < ma_countof(pDeviceInfo->nativeDataFormats)) { - pDeviceInfo->nativeDataFormats[pDeviceInfo->nativeDataFormatCount].format = format; - pDeviceInfo->nativeDataFormats[pDeviceInfo->nativeDataFormatCount].channels = channels; - pDeviceInfo->nativeDataFormats[pDeviceInfo->nativeDataFormatCount].sampleRate = sampleRate; - pDeviceInfo->nativeDataFormats[pDeviceInfo->nativeDataFormatCount].flags = flags; - pDeviceInfo->nativeDataFormatCount += 1; - } -} - - -typedef struct -{ - ma_backend backend; - const char* pName; -} ma_backend_info; - -static ma_backend_info gBackendInfo[] = /* Indexed by the backend enum. Must be in the order backends are declared in the ma_backend enum. */ -{ - {ma_backend_wasapi, "WASAPI"}, - {ma_backend_dsound, "DirectSound"}, - {ma_backend_winmm, "WinMM"}, - {ma_backend_coreaudio, "Core Audio"}, - {ma_backend_sndio, "sndio"}, - {ma_backend_audio4, "audio(4)"}, - {ma_backend_oss, "OSS"}, - {ma_backend_pulseaudio, "PulseAudio"}, - {ma_backend_alsa, "ALSA"}, - {ma_backend_jack, "JACK"}, - {ma_backend_aaudio, "AAudio"}, - {ma_backend_opensl, "OpenSL|ES"}, - {ma_backend_webaudio, "Web Audio"}, - {ma_backend_custom, "Custom"}, - {ma_backend_null, "Null"} -}; - -MA_API const char* ma_get_backend_name(ma_backend backend) -{ - if (backend < 0 || backend >= (int)ma_countof(gBackendInfo)) { - return "Unknown"; - } - - return gBackendInfo[backend].pName; -} - -MA_API ma_result ma_get_backend_from_name(const char* pBackendName, ma_backend* pBackend) -{ - size_t iBackend; - - if (pBackendName == NULL) { - return MA_INVALID_ARGS; - } - - for (iBackend = 0; iBackend < ma_countof(gBackendInfo); iBackend += 1) { - if (ma_strcmp(pBackendName, gBackendInfo[iBackend].pName) == 0) { - if (pBackend != NULL) { - *pBackend = gBackendInfo[iBackend].backend; - } - - return MA_SUCCESS; - } - } - - /* Getting here means the backend name is unknown. */ - return MA_INVALID_ARGS; -} - -MA_API ma_bool32 ma_is_backend_enabled(ma_backend backend) -{ - /* - This looks a little bit gross, but we want all backends to be included in the switch to avoid warnings on some compilers - about some enums not being handled by the switch statement. - */ - switch (backend) - { - case ma_backend_wasapi: - #if defined(MA_HAS_WASAPI) - return MA_TRUE; - #else - return MA_FALSE; - #endif - case ma_backend_dsound: - #if defined(MA_HAS_DSOUND) - return MA_TRUE; - #else - return MA_FALSE; - #endif - case ma_backend_winmm: - #if defined(MA_HAS_WINMM) - return MA_TRUE; - #else - return MA_FALSE; - #endif - case ma_backend_coreaudio: - #if defined(MA_HAS_COREAUDIO) - return MA_TRUE; - #else - return MA_FALSE; - #endif - case ma_backend_sndio: - #if defined(MA_HAS_SNDIO) - return MA_TRUE; - #else - return MA_FALSE; - #endif - case ma_backend_audio4: - #if defined(MA_HAS_AUDIO4) - return MA_TRUE; - #else - return MA_FALSE; - #endif - case ma_backend_oss: - #if defined(MA_HAS_OSS) - return MA_TRUE; - #else - return MA_FALSE; - #endif - case ma_backend_pulseaudio: - #if defined(MA_HAS_PULSEAUDIO) - return MA_TRUE; - #else - return MA_FALSE; - #endif - case ma_backend_alsa: - #if defined(MA_HAS_ALSA) - return MA_TRUE; - #else - return MA_FALSE; - #endif - case ma_backend_jack: - #if defined(MA_HAS_JACK) - return MA_TRUE; - #else - return MA_FALSE; - #endif - case ma_backend_aaudio: - #if defined(MA_HAS_AAUDIO) - #if defined(MA_ANDROID) - { - return ma_android_sdk_version() >= MA_AAUDIO_MIN_ANDROID_SDK_VERSION; - } - #else - return MA_FALSE; - #endif - #else - return MA_FALSE; - #endif - case ma_backend_opensl: - #if defined(MA_HAS_OPENSL) - #if defined(MA_ANDROID) - { - return ma_android_sdk_version() >= 9; - } - #else - return MA_TRUE; - #endif - #else - return MA_FALSE; - #endif - case ma_backend_webaudio: - #if defined(MA_HAS_WEBAUDIO) - return MA_TRUE; - #else - return MA_FALSE; - #endif - case ma_backend_custom: - #if defined(MA_HAS_CUSTOM) - return MA_TRUE; - #else - return MA_FALSE; - #endif - case ma_backend_null: - #if defined(MA_HAS_NULL) - return MA_TRUE; - #else - return MA_FALSE; - #endif - - default: return MA_FALSE; - } -} - -MA_API ma_result ma_get_enabled_backends(ma_backend* pBackends, size_t backendCap, size_t* pBackendCount) -{ - size_t backendCount; - size_t iBackend; - ma_result result = MA_SUCCESS; - - if (pBackendCount == NULL) { - return MA_INVALID_ARGS; - } - - backendCount = 0; - - for (iBackend = 0; iBackend <= ma_backend_null; iBackend += 1) { - ma_backend backend = (ma_backend)iBackend; - - if (ma_is_backend_enabled(backend)) { - /* The backend is enabled. Try adding it to the list. If there's no room, MA_NO_SPACE needs to be returned. */ - if (backendCount == backendCap) { - result = MA_NO_SPACE; - break; - } else { - pBackends[backendCount] = backend; - backendCount += 1; - } - } - } - - if (pBackendCount != NULL) { - *pBackendCount = backendCount; - } - - return result; -} - -MA_API ma_bool32 ma_is_loopback_supported(ma_backend backend) -{ - switch (backend) - { - case ma_backend_wasapi: return MA_TRUE; - case ma_backend_dsound: return MA_FALSE; - case ma_backend_winmm: return MA_FALSE; - case ma_backend_coreaudio: return MA_FALSE; - case ma_backend_sndio: return MA_FALSE; - case ma_backend_audio4: return MA_FALSE; - case ma_backend_oss: return MA_FALSE; - case ma_backend_pulseaudio: return MA_FALSE; - case ma_backend_alsa: return MA_FALSE; - case ma_backend_jack: return MA_FALSE; - case ma_backend_aaudio: return MA_FALSE; - case ma_backend_opensl: return MA_FALSE; - case ma_backend_webaudio: return MA_FALSE; - case ma_backend_custom: return MA_FALSE; /* <-- Will depend on the implementation of the backend. */ - case ma_backend_null: return MA_FALSE; - default: return MA_FALSE; - } -} - - - -#if defined(MA_WIN32) -/* WASAPI error codes. */ -#define MA_AUDCLNT_E_NOT_INITIALIZED ((HRESULT)0x88890001) -#define MA_AUDCLNT_E_ALREADY_INITIALIZED ((HRESULT)0x88890002) -#define MA_AUDCLNT_E_WRONG_ENDPOINT_TYPE ((HRESULT)0x88890003) -#define MA_AUDCLNT_E_DEVICE_INVALIDATED ((HRESULT)0x88890004) -#define MA_AUDCLNT_E_NOT_STOPPED ((HRESULT)0x88890005) -#define MA_AUDCLNT_E_BUFFER_TOO_LARGE ((HRESULT)0x88890006) -#define MA_AUDCLNT_E_OUT_OF_ORDER ((HRESULT)0x88890007) -#define MA_AUDCLNT_E_UNSUPPORTED_FORMAT ((HRESULT)0x88890008) -#define MA_AUDCLNT_E_INVALID_SIZE ((HRESULT)0x88890009) -#define MA_AUDCLNT_E_DEVICE_IN_USE ((HRESULT)0x8889000A) -#define MA_AUDCLNT_E_BUFFER_OPERATION_PENDING ((HRESULT)0x8889000B) -#define MA_AUDCLNT_E_THREAD_NOT_REGISTERED ((HRESULT)0x8889000C) -#define MA_AUDCLNT_E_NO_SINGLE_PROCESS ((HRESULT)0x8889000D) -#define MA_AUDCLNT_E_EXCLUSIVE_MODE_NOT_ALLOWED ((HRESULT)0x8889000E) -#define MA_AUDCLNT_E_ENDPOINT_CREATE_FAILED ((HRESULT)0x8889000F) -#define MA_AUDCLNT_E_SERVICE_NOT_RUNNING ((HRESULT)0x88890010) -#define MA_AUDCLNT_E_EVENTHANDLE_NOT_EXPECTED ((HRESULT)0x88890011) -#define MA_AUDCLNT_E_EXCLUSIVE_MODE_ONLY ((HRESULT)0x88890012) -#define MA_AUDCLNT_E_BUFDURATION_PERIOD_NOT_EQUAL ((HRESULT)0x88890013) -#define MA_AUDCLNT_E_EVENTHANDLE_NOT_SET ((HRESULT)0x88890014) -#define MA_AUDCLNT_E_INCORRECT_BUFFER_SIZE ((HRESULT)0x88890015) -#define MA_AUDCLNT_E_BUFFER_SIZE_ERROR ((HRESULT)0x88890016) -#define MA_AUDCLNT_E_CPUUSAGE_EXCEEDED ((HRESULT)0x88890017) -#define MA_AUDCLNT_E_BUFFER_ERROR ((HRESULT)0x88890018) -#define MA_AUDCLNT_E_BUFFER_SIZE_NOT_ALIGNED ((HRESULT)0x88890019) -#define MA_AUDCLNT_E_INVALID_DEVICE_PERIOD ((HRESULT)0x88890020) -#define MA_AUDCLNT_E_INVALID_STREAM_FLAG ((HRESULT)0x88890021) -#define MA_AUDCLNT_E_ENDPOINT_OFFLOAD_NOT_CAPABLE ((HRESULT)0x88890022) -#define MA_AUDCLNT_E_OUT_OF_OFFLOAD_RESOURCES ((HRESULT)0x88890023) -#define MA_AUDCLNT_E_OFFLOAD_MODE_ONLY ((HRESULT)0x88890024) -#define MA_AUDCLNT_E_NONOFFLOAD_MODE_ONLY ((HRESULT)0x88890025) -#define MA_AUDCLNT_E_RESOURCES_INVALIDATED ((HRESULT)0x88890026) -#define MA_AUDCLNT_E_RAW_MODE_UNSUPPORTED ((HRESULT)0x88890027) -#define MA_AUDCLNT_E_ENGINE_PERIODICITY_LOCKED ((HRESULT)0x88890028) -#define MA_AUDCLNT_E_ENGINE_FORMAT_LOCKED ((HRESULT)0x88890029) -#define MA_AUDCLNT_E_HEADTRACKING_ENABLED ((HRESULT)0x88890030) -#define MA_AUDCLNT_E_HEADTRACKING_UNSUPPORTED ((HRESULT)0x88890040) -#define MA_AUDCLNT_S_BUFFER_EMPTY ((HRESULT)0x08890001) -#define MA_AUDCLNT_S_THREAD_ALREADY_REGISTERED ((HRESULT)0x08890002) -#define MA_AUDCLNT_S_POSITION_STALLED ((HRESULT)0x08890003) - -#define MA_DS_OK ((HRESULT)0) -#define MA_DS_NO_VIRTUALIZATION ((HRESULT)0x0878000A) -#define MA_DSERR_ALLOCATED ((HRESULT)0x8878000A) -#define MA_DSERR_CONTROLUNAVAIL ((HRESULT)0x8878001E) -#define MA_DSERR_INVALIDPARAM ((HRESULT)0x80070057) /*E_INVALIDARG*/ -#define MA_DSERR_INVALIDCALL ((HRESULT)0x88780032) -#define MA_DSERR_GENERIC ((HRESULT)0x80004005) /*E_FAIL*/ -#define MA_DSERR_PRIOLEVELNEEDED ((HRESULT)0x88780046) -#define MA_DSERR_OUTOFMEMORY ((HRESULT)0x8007000E) /*E_OUTOFMEMORY*/ -#define MA_DSERR_BADFORMAT ((HRESULT)0x88780064) -#define MA_DSERR_UNSUPPORTED ((HRESULT)0x80004001) /*E_NOTIMPL*/ -#define MA_DSERR_NODRIVER ((HRESULT)0x88780078) -#define MA_DSERR_ALREADYINITIALIZED ((HRESULT)0x88780082) -#define MA_DSERR_NOAGGREGATION ((HRESULT)0x80040110) /*CLASS_E_NOAGGREGATION*/ -#define MA_DSERR_BUFFERLOST ((HRESULT)0x88780096) -#define MA_DSERR_OTHERAPPHASPRIO ((HRESULT)0x887800A0) -#define MA_DSERR_UNINITIALIZED ((HRESULT)0x887800AA) -#define MA_DSERR_NOINTERFACE ((HRESULT)0x80004002) /*E_NOINTERFACE*/ -#define MA_DSERR_ACCESSDENIED ((HRESULT)0x80070005) /*E_ACCESSDENIED*/ -#define MA_DSERR_BUFFERTOOSMALL ((HRESULT)0x887800B4) -#define MA_DSERR_DS8_REQUIRED ((HRESULT)0x887800BE) -#define MA_DSERR_SENDLOOP ((HRESULT)0x887800C8) -#define MA_DSERR_BADSENDBUFFERGUID ((HRESULT)0x887800D2) -#define MA_DSERR_OBJECTNOTFOUND ((HRESULT)0x88781161) -#define MA_DSERR_FXUNAVAILABLE ((HRESULT)0x887800DC) - -static ma_result ma_result_from_HRESULT(HRESULT hr) -{ - switch (hr) - { - case NOERROR: return MA_SUCCESS; - /*case S_OK: return MA_SUCCESS;*/ - - case E_POINTER: return MA_INVALID_ARGS; - case E_UNEXPECTED: return MA_ERROR; - case E_NOTIMPL: return MA_NOT_IMPLEMENTED; - case E_OUTOFMEMORY: return MA_OUT_OF_MEMORY; - case E_INVALIDARG: return MA_INVALID_ARGS; - case E_NOINTERFACE: return MA_API_NOT_FOUND; - case E_HANDLE: return MA_INVALID_ARGS; - case E_ABORT: return MA_ERROR; - case E_FAIL: return MA_ERROR; - case E_ACCESSDENIED: return MA_ACCESS_DENIED; - - /* WASAPI */ - case MA_AUDCLNT_E_NOT_INITIALIZED: return MA_DEVICE_NOT_INITIALIZED; - case MA_AUDCLNT_E_ALREADY_INITIALIZED: return MA_DEVICE_ALREADY_INITIALIZED; - case MA_AUDCLNT_E_WRONG_ENDPOINT_TYPE: return MA_INVALID_ARGS; - case MA_AUDCLNT_E_DEVICE_INVALIDATED: return MA_UNAVAILABLE; - case MA_AUDCLNT_E_NOT_STOPPED: return MA_DEVICE_NOT_STOPPED; - case MA_AUDCLNT_E_BUFFER_TOO_LARGE: return MA_TOO_BIG; - case MA_AUDCLNT_E_OUT_OF_ORDER: return MA_INVALID_OPERATION; - case MA_AUDCLNT_E_UNSUPPORTED_FORMAT: return MA_FORMAT_NOT_SUPPORTED; - case MA_AUDCLNT_E_INVALID_SIZE: return MA_INVALID_ARGS; - case MA_AUDCLNT_E_DEVICE_IN_USE: return MA_BUSY; - case MA_AUDCLNT_E_BUFFER_OPERATION_PENDING: return MA_INVALID_OPERATION; - case MA_AUDCLNT_E_THREAD_NOT_REGISTERED: return MA_DOES_NOT_EXIST; - case MA_AUDCLNT_E_NO_SINGLE_PROCESS: return MA_INVALID_OPERATION; - case MA_AUDCLNT_E_EXCLUSIVE_MODE_NOT_ALLOWED: return MA_SHARE_MODE_NOT_SUPPORTED; - case MA_AUDCLNT_E_ENDPOINT_CREATE_FAILED: return MA_FAILED_TO_OPEN_BACKEND_DEVICE; - case MA_AUDCLNT_E_SERVICE_NOT_RUNNING: return MA_NOT_CONNECTED; - case MA_AUDCLNT_E_EVENTHANDLE_NOT_EXPECTED: return MA_INVALID_ARGS; - case MA_AUDCLNT_E_EXCLUSIVE_MODE_ONLY: return MA_SHARE_MODE_NOT_SUPPORTED; - case MA_AUDCLNT_E_BUFDURATION_PERIOD_NOT_EQUAL: return MA_INVALID_ARGS; - case MA_AUDCLNT_E_EVENTHANDLE_NOT_SET: return MA_INVALID_ARGS; - case MA_AUDCLNT_E_INCORRECT_BUFFER_SIZE: return MA_INVALID_ARGS; - case MA_AUDCLNT_E_BUFFER_SIZE_ERROR: return MA_INVALID_ARGS; - case MA_AUDCLNT_E_CPUUSAGE_EXCEEDED: return MA_ERROR; - case MA_AUDCLNT_E_BUFFER_ERROR: return MA_ERROR; - case MA_AUDCLNT_E_BUFFER_SIZE_NOT_ALIGNED: return MA_INVALID_ARGS; - case MA_AUDCLNT_E_INVALID_DEVICE_PERIOD: return MA_INVALID_ARGS; - case MA_AUDCLNT_E_INVALID_STREAM_FLAG: return MA_INVALID_ARGS; - case MA_AUDCLNT_E_ENDPOINT_OFFLOAD_NOT_CAPABLE: return MA_INVALID_OPERATION; - case MA_AUDCLNT_E_OUT_OF_OFFLOAD_RESOURCES: return MA_OUT_OF_MEMORY; - case MA_AUDCLNT_E_OFFLOAD_MODE_ONLY: return MA_INVALID_OPERATION; - case MA_AUDCLNT_E_NONOFFLOAD_MODE_ONLY: return MA_INVALID_OPERATION; - case MA_AUDCLNT_E_RESOURCES_INVALIDATED: return MA_INVALID_DATA; - case MA_AUDCLNT_E_RAW_MODE_UNSUPPORTED: return MA_INVALID_OPERATION; - case MA_AUDCLNT_E_ENGINE_PERIODICITY_LOCKED: return MA_INVALID_OPERATION; - case MA_AUDCLNT_E_ENGINE_FORMAT_LOCKED: return MA_INVALID_OPERATION; - case MA_AUDCLNT_E_HEADTRACKING_ENABLED: return MA_INVALID_OPERATION; - case MA_AUDCLNT_E_HEADTRACKING_UNSUPPORTED: return MA_INVALID_OPERATION; - case MA_AUDCLNT_S_BUFFER_EMPTY: return MA_NO_SPACE; - case MA_AUDCLNT_S_THREAD_ALREADY_REGISTERED: return MA_ALREADY_EXISTS; - case MA_AUDCLNT_S_POSITION_STALLED: return MA_ERROR; - - /* DirectSound */ - /*case MA_DS_OK: return MA_SUCCESS;*/ /* S_OK */ - case MA_DS_NO_VIRTUALIZATION: return MA_SUCCESS; - case MA_DSERR_ALLOCATED: return MA_ALREADY_IN_USE; - case MA_DSERR_CONTROLUNAVAIL: return MA_INVALID_OPERATION; - /*case MA_DSERR_INVALIDPARAM: return MA_INVALID_ARGS;*/ /* E_INVALIDARG */ - case MA_DSERR_INVALIDCALL: return MA_INVALID_OPERATION; - /*case MA_DSERR_GENERIC: return MA_ERROR;*/ /* E_FAIL */ - case MA_DSERR_PRIOLEVELNEEDED: return MA_INVALID_OPERATION; - /*case MA_DSERR_OUTOFMEMORY: return MA_OUT_OF_MEMORY;*/ /* E_OUTOFMEMORY */ - case MA_DSERR_BADFORMAT: return MA_FORMAT_NOT_SUPPORTED; - /*case MA_DSERR_UNSUPPORTED: return MA_NOT_IMPLEMENTED;*/ /* E_NOTIMPL */ - case MA_DSERR_NODRIVER: return MA_FAILED_TO_INIT_BACKEND; - case MA_DSERR_ALREADYINITIALIZED: return MA_DEVICE_ALREADY_INITIALIZED; - case MA_DSERR_NOAGGREGATION: return MA_ERROR; - case MA_DSERR_BUFFERLOST: return MA_UNAVAILABLE; - case MA_DSERR_OTHERAPPHASPRIO: return MA_ACCESS_DENIED; - case MA_DSERR_UNINITIALIZED: return MA_DEVICE_NOT_INITIALIZED; - /*case MA_DSERR_NOINTERFACE: return MA_API_NOT_FOUND;*/ /* E_NOINTERFACE */ - /*case MA_DSERR_ACCESSDENIED: return MA_ACCESS_DENIED;*/ /* E_ACCESSDENIED */ - case MA_DSERR_BUFFERTOOSMALL: return MA_NO_SPACE; - case MA_DSERR_DS8_REQUIRED: return MA_INVALID_OPERATION; - case MA_DSERR_SENDLOOP: return MA_DEADLOCK; - case MA_DSERR_BADSENDBUFFERGUID: return MA_INVALID_ARGS; - case MA_DSERR_OBJECTNOTFOUND: return MA_NO_DEVICE; - case MA_DSERR_FXUNAVAILABLE: return MA_UNAVAILABLE; - - default: return MA_ERROR; - } -} - -/* PROPVARIANT */ -#define MA_VT_LPWSTR 31 -#define MA_VT_BLOB 65 - -#if defined(_MSC_VER) && !defined(__clang__) - #pragma warning(push) - #pragma warning(disable:4201) /* nonstandard extension used: nameless struct/union */ -#elif defined(__clang__) || (defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 8))) - #pragma GCC diagnostic push - #pragma GCC diagnostic ignored "-Wpedantic" /* For ISO C99 doesn't support unnamed structs/unions [-Wpedantic] */ - #if defined(__clang__) - #pragma GCC diagnostic ignored "-Wc11-extensions" /* anonymous unions are a C11 extension */ - #endif -#endif -typedef struct -{ - WORD vt; - WORD wReserved1; - WORD wReserved2; - WORD wReserved3; - union - { - struct - { - ULONG cbSize; - BYTE* pBlobData; - } blob; - WCHAR* pwszVal; - char pad[16]; /* Just to ensure the size of the struct matches the official version. */ - }; -} MA_PROPVARIANT; -#if defined(_MSC_VER) && !defined(__clang__) - #pragma warning(pop) -#elif defined(__clang__) || (defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 8))) - #pragma GCC diagnostic pop -#endif - -typedef HRESULT (WINAPI * MA_PFN_CoInitialize)(void* pvReserved); -typedef HRESULT (WINAPI * MA_PFN_CoInitializeEx)(void* pvReserved, DWORD dwCoInit); -typedef void (WINAPI * MA_PFN_CoUninitialize)(void); -typedef HRESULT (WINAPI * MA_PFN_CoCreateInstance)(const IID* rclsid, void* pUnkOuter, DWORD dwClsContext, const IID* riid, void* ppv); -typedef void (WINAPI * MA_PFN_CoTaskMemFree)(void* pv); -typedef HRESULT (WINAPI * MA_PFN_PropVariantClear)(MA_PROPVARIANT *pvar); -typedef int (WINAPI * MA_PFN_StringFromGUID2)(const GUID* const rguid, WCHAR* lpsz, int cchMax); - -typedef HWND (WINAPI * MA_PFN_GetForegroundWindow)(void); -typedef HWND (WINAPI * MA_PFN_GetDesktopWindow)(void); - -#if defined(MA_WIN32_DESKTOP) -/* Microsoft documents these APIs as returning LSTATUS, but the Win32 API shipping with some compilers do not define it. It's just a LONG. */ -typedef LONG (WINAPI * MA_PFN_RegOpenKeyExA)(HKEY hKey, const char* lpSubKey, DWORD ulOptions, DWORD samDesired, HKEY* phkResult); -typedef LONG (WINAPI * MA_PFN_RegCloseKey)(HKEY hKey); -typedef LONG (WINAPI * MA_PFN_RegQueryValueExA)(HKEY hKey, const char* lpValueName, DWORD* lpReserved, DWORD* lpType, BYTE* lpData, DWORD* lpcbData); -#endif /* MA_WIN32_DESKTOP */ - -MA_API size_t ma_strlen_WCHAR(const WCHAR* str) -{ - size_t len = 0; - while (str[len] != '\0') { - len += 1; - } - - return len; -} - -MA_API int ma_strcmp_WCHAR(const WCHAR *s1, const WCHAR *s2) -{ - while (*s1 != '\0' && *s1 == *s2) { - s1 += 1; - s2 += 1; - } - - return *s1 - *s2; -} - -MA_API int ma_strcpy_s_WCHAR(WCHAR* dst, size_t dstCap, const WCHAR* src) -{ - size_t i; - - if (dst == 0) { - return 22; - } - if (dstCap == 0) { - return 34; - } - if (src == 0) { - dst[0] = '\0'; - return 22; - } - - for (i = 0; i < dstCap && src[i] != '\0'; ++i) { - dst[i] = src[i]; - } - - if (i < dstCap) { - dst[i] = '\0'; - return 0; - } - - dst[0] = '\0'; - return 34; -} -#endif /* MA_WIN32 */ - - -#define MA_DEFAULT_PLAYBACK_DEVICE_NAME "Default Playback Device" -#define MA_DEFAULT_CAPTURE_DEVICE_NAME "Default Capture Device" - - - - -/******************************************************************************* - -Timing - -*******************************************************************************/ -#if defined(MA_WIN32) && !defined(MA_POSIX) - static LARGE_INTEGER g_ma_TimerFrequency; /* <-- Initialized to zero since it's static. */ - static void ma_timer_init(ma_timer* pTimer) - { - LARGE_INTEGER counter; - - if (g_ma_TimerFrequency.QuadPart == 0) { - QueryPerformanceFrequency(&g_ma_TimerFrequency); - } - - QueryPerformanceCounter(&counter); - pTimer->counter = counter.QuadPart; - } - - static double ma_timer_get_time_in_seconds(ma_timer* pTimer) - { - LARGE_INTEGER counter; - if (!QueryPerformanceCounter(&counter)) { - return 0; - } - - return (double)(counter.QuadPart - pTimer->counter) / g_ma_TimerFrequency.QuadPart; - } -#elif defined(MA_APPLE) && (MAC_OS_X_VERSION_MIN_REQUIRED < 101200) - static ma_uint64 g_ma_TimerFrequency = 0; - static void ma_timer_init(ma_timer* pTimer) - { - mach_timebase_info_data_t baseTime; - mach_timebase_info(&baseTime); - g_ma_TimerFrequency = (baseTime.denom * 1e9) / baseTime.numer; - - pTimer->counter = mach_absolute_time(); - } - - static double ma_timer_get_time_in_seconds(ma_timer* pTimer) - { - ma_uint64 newTimeCounter = mach_absolute_time(); - ma_uint64 oldTimeCounter = pTimer->counter; - - return (newTimeCounter - oldTimeCounter) / g_ma_TimerFrequency; - } -#elif defined(MA_EMSCRIPTEN) - static MA_INLINE void ma_timer_init(ma_timer* pTimer) - { - pTimer->counterD = emscripten_get_now(); - } - - static MA_INLINE double ma_timer_get_time_in_seconds(ma_timer* pTimer) - { - return (emscripten_get_now() - pTimer->counterD) / 1000; /* Emscripten is in milliseconds. */ - } -#else - #if defined(_POSIX_C_SOURCE) && _POSIX_C_SOURCE >= 199309L - #if defined(CLOCK_MONOTONIC) - #define MA_CLOCK_ID CLOCK_MONOTONIC - #else - #define MA_CLOCK_ID CLOCK_REALTIME - #endif - - static void ma_timer_init(ma_timer* pTimer) - { - struct timespec newTime; - clock_gettime(MA_CLOCK_ID, &newTime); - - pTimer->counter = (newTime.tv_sec * 1000000000) + newTime.tv_nsec; - } - - static double ma_timer_get_time_in_seconds(ma_timer* pTimer) - { - ma_uint64 newTimeCounter; - ma_uint64 oldTimeCounter; - - struct timespec newTime; - clock_gettime(MA_CLOCK_ID, &newTime); - - newTimeCounter = (newTime.tv_sec * 1000000000) + newTime.tv_nsec; - oldTimeCounter = pTimer->counter; - - return (newTimeCounter - oldTimeCounter) / 1000000000.0; - } - #else - static void ma_timer_init(ma_timer* pTimer) - { - struct timeval newTime; - gettimeofday(&newTime, NULL); - - pTimer->counter = (newTime.tv_sec * 1000000) + newTime.tv_usec; - } - - static double ma_timer_get_time_in_seconds(ma_timer* pTimer) - { - ma_uint64 newTimeCounter; - ma_uint64 oldTimeCounter; - - struct timeval newTime; - gettimeofday(&newTime, NULL); - - newTimeCounter = (newTime.tv_sec * 1000000) + newTime.tv_usec; - oldTimeCounter = pTimer->counter; - - return (newTimeCounter - oldTimeCounter) / 1000000.0; - } - #endif -#endif - - - -#if 0 -static ma_uint32 ma_get_closest_standard_sample_rate(ma_uint32 sampleRateIn) -{ - ma_uint32 closestRate = 0; - ma_uint32 closestDiff = 0xFFFFFFFF; - size_t iStandardRate; - - for (iStandardRate = 0; iStandardRate < ma_countof(g_maStandardSampleRatePriorities); ++iStandardRate) { - ma_uint32 standardRate = g_maStandardSampleRatePriorities[iStandardRate]; - ma_uint32 diff; - - if (sampleRateIn > standardRate) { - diff = sampleRateIn - standardRate; - } else { - diff = standardRate - sampleRateIn; - } - - if (diff == 0) { - return standardRate; /* The input sample rate is a standard rate. */ - } - - if (closestDiff > diff) { - closestDiff = diff; - closestRate = standardRate; - } - } - - return closestRate; -} -#endif - - -static MA_INLINE unsigned int ma_device_disable_denormals(ma_device* pDevice) -{ - MA_ASSERT(pDevice != NULL); - - if (!pDevice->noDisableDenormals) { - return ma_disable_denormals(); - } else { - return 0; - } -} - -static MA_INLINE void ma_device_restore_denormals(ma_device* pDevice, unsigned int prevState) -{ - MA_ASSERT(pDevice != NULL); - - if (!pDevice->noDisableDenormals) { - ma_restore_denormals(prevState); - } else { - /* Do nothing. */ - (void)prevState; - } -} - -static ma_device_notification ma_device_notification_init(ma_device* pDevice, ma_device_notification_type type) -{ - ma_device_notification notification; - - MA_ZERO_OBJECT(¬ification); - notification.pDevice = pDevice; - notification.type = type; - - return notification; -} - -static void ma_device__on_notification(ma_device_notification notification) -{ - MA_ASSERT(notification.pDevice != NULL); - - if (notification.pDevice->onNotification != NULL) { - notification.pDevice->onNotification(¬ification); - } - - /* TEMP FOR COMPATIBILITY: If it's a stopped notification, fire the onStop callback as well. This is only for backwards compatibility and will be removed. */ - if (notification.pDevice->onStop != NULL && notification.type == ma_device_notification_type_stopped) { - notification.pDevice->onStop(notification.pDevice); - } -} - -static void ma_device__on_notification_started(ma_device* pDevice) -{ - ma_device__on_notification(ma_device_notification_init(pDevice, ma_device_notification_type_started)); -} - -static void ma_device__on_notification_stopped(ma_device* pDevice) -{ - ma_device__on_notification(ma_device_notification_init(pDevice, ma_device_notification_type_stopped)); -} - -/* Not all platforms support reroute notifications. */ -#if !defined(MA_EMSCRIPTEN) -static void ma_device__on_notification_rerouted(ma_device* pDevice) -{ - ma_device__on_notification(ma_device_notification_init(pDevice, ma_device_notification_type_rerouted)); -} -#endif - -#if defined(MA_EMSCRIPTEN) -#ifdef __cplusplus -extern "C" { -#endif -void EMSCRIPTEN_KEEPALIVE ma_device__on_notification_unlocked(ma_device* pDevice) -{ - ma_device__on_notification(ma_device_notification_init(pDevice, ma_device_notification_type_unlocked)); -} -#ifdef __cplusplus -} -#endif -#endif - - -static void ma_device__on_data_inner(ma_device* pDevice, void* pFramesOut, const void* pFramesIn, ma_uint32 frameCount) -{ - MA_ASSERT(pDevice != NULL); - MA_ASSERT(pDevice->onData != NULL); - - if (!pDevice->noPreSilencedOutputBuffer && pFramesOut != NULL) { - ma_silence_pcm_frames(pFramesOut, frameCount, pDevice->playback.format, pDevice->playback.channels); - } - - pDevice->onData(pDevice, pFramesOut, pFramesIn, frameCount); -} - -static void ma_device__on_data(ma_device* pDevice, void* pFramesOut, const void* pFramesIn, ma_uint32 frameCount) -{ - MA_ASSERT(pDevice != NULL); - - /* Don't read more data from the client if we're in the process of stopping. */ - if (ma_device_get_state(pDevice) == ma_device_state_stopping) { - return; - } - - if (pDevice->noFixedSizedCallback) { - /* Fast path. Not using a fixed sized callback. Process directly from the specified buffers. */ - ma_device__on_data_inner(pDevice, pFramesOut, pFramesIn, frameCount); - } else { - /* Slow path. Using a fixed sized callback. Need to use the intermediary buffer. */ - ma_uint32 totalFramesProcessed = 0; - - while (totalFramesProcessed < frameCount) { - ma_uint32 totalFramesRemaining = frameCount - totalFramesProcessed; - ma_uint32 framesToProcessThisIteration = 0; - - if (pFramesIn != NULL) { - /* Capturing. Write to the intermediary buffer. If there's no room, fire the callback to empty it. */ - if (pDevice->capture.intermediaryBufferLen < pDevice->capture.intermediaryBufferCap) { - /* There's some room left in the intermediary buffer. Write to it without firing the callback. */ - framesToProcessThisIteration = totalFramesRemaining; - if (framesToProcessThisIteration > pDevice->capture.intermediaryBufferCap - pDevice->capture.intermediaryBufferLen) { - framesToProcessThisIteration = pDevice->capture.intermediaryBufferCap - pDevice->capture.intermediaryBufferLen; - } - - ma_copy_pcm_frames( - ma_offset_pcm_frames_ptr(pDevice->capture.pIntermediaryBuffer, pDevice->capture.intermediaryBufferLen, pDevice->capture.format, pDevice->capture.channels), - ma_offset_pcm_frames_const_ptr(pFramesIn, totalFramesProcessed, pDevice->capture.format, pDevice->capture.channels), - framesToProcessThisIteration, - pDevice->capture.format, pDevice->capture.channels); - - pDevice->capture.intermediaryBufferLen += framesToProcessThisIteration; - } - - if (pDevice->capture.intermediaryBufferLen == pDevice->capture.intermediaryBufferCap) { - /* No room left in the intermediary buffer. Fire the data callback. */ - if (pDevice->type == ma_device_type_duplex) { - /* We'll do the duplex data callback later after we've processed the playback data. */ - } else { - ma_device__on_data_inner(pDevice, NULL, pDevice->capture.pIntermediaryBuffer, pDevice->capture.intermediaryBufferCap); - - /* The intermediary buffer has just been drained. */ - pDevice->capture.intermediaryBufferLen = 0; - } - } - } - - if (pFramesOut != NULL) { - /* Playing back. Read from the intermediary buffer. If there's nothing in it, fire the callback to fill it. */ - if (pDevice->playback.intermediaryBufferLen > 0) { - /* There's some content in the intermediary buffer. Read from that without firing the callback. */ - if (pDevice->type == ma_device_type_duplex) { - /* The frames processed this iteration for a duplex device will always be based on the capture side. Leave it unmodified. */ - } else { - framesToProcessThisIteration = totalFramesRemaining; - if (framesToProcessThisIteration > pDevice->playback.intermediaryBufferLen) { - framesToProcessThisIteration = pDevice->playback.intermediaryBufferLen; - } - } - - ma_copy_pcm_frames( - ma_offset_pcm_frames_ptr(pFramesOut, totalFramesProcessed, pDevice->playback.format, pDevice->playback.channels), - ma_offset_pcm_frames_ptr(pDevice->playback.pIntermediaryBuffer, pDevice->playback.intermediaryBufferCap - pDevice->playback.intermediaryBufferLen, pDevice->playback.format, pDevice->playback.channels), - framesToProcessThisIteration, - pDevice->playback.format, pDevice->playback.channels); - - pDevice->playback.intermediaryBufferLen -= framesToProcessThisIteration; - } - - if (pDevice->playback.intermediaryBufferLen == 0) { - /* There's nothing in the intermediary buffer. Fire the data callback to fill it. */ - if (pDevice->type == ma_device_type_duplex) { - /* In duplex mode, the data callback will be fired later. Nothing to do here. */ - } else { - ma_device__on_data_inner(pDevice, pDevice->playback.pIntermediaryBuffer, NULL, pDevice->playback.intermediaryBufferCap); - - /* The intermediary buffer has just been filled. */ - pDevice->playback.intermediaryBufferLen = pDevice->playback.intermediaryBufferCap; - } - } - } - - /* If we're in duplex mode we might need to do a refill of the data. */ - if (pDevice->type == ma_device_type_duplex) { - if (pDevice->capture.intermediaryBufferLen == pDevice->capture.intermediaryBufferCap) { - ma_device__on_data_inner(pDevice, pDevice->playback.pIntermediaryBuffer, pDevice->capture.pIntermediaryBuffer, pDevice->capture.intermediaryBufferCap); - - pDevice->playback.intermediaryBufferLen = pDevice->playback.intermediaryBufferCap; /* The playback buffer will have just been filled. */ - pDevice->capture.intermediaryBufferLen = 0; /* The intermediary buffer has just been drained. */ - } - } - - /* Make sure this is only incremented once in the duplex case. */ - totalFramesProcessed += framesToProcessThisIteration; - } - } -} - -static void ma_device__handle_data_callback(ma_device* pDevice, void* pFramesOut, const void* pFramesIn, ma_uint32 frameCount) -{ - float masterVolumeFactor; - - ma_device_get_master_volume(pDevice, &masterVolumeFactor); /* Use ma_device_get_master_volume() to ensure the volume is loaded atomically. */ - - if (pDevice->onData) { - unsigned int prevDenormalState = ma_device_disable_denormals(pDevice); - { - /* Volume control of input makes things a bit awkward because the input buffer is read-only. We'll need to use a temp buffer and loop in this case. */ - if (pFramesIn != NULL && masterVolumeFactor != 1) { - ma_uint8 tempFramesIn[MA_DATA_CONVERTER_STACK_BUFFER_SIZE]; - ma_uint32 bpfCapture = ma_get_bytes_per_frame(pDevice->capture.format, pDevice->capture.channels); - ma_uint32 bpfPlayback = ma_get_bytes_per_frame(pDevice->playback.format, pDevice->playback.channels); - ma_uint32 totalFramesProcessed = 0; - while (totalFramesProcessed < frameCount) { - ma_uint32 framesToProcessThisIteration = frameCount - totalFramesProcessed; - if (framesToProcessThisIteration > sizeof(tempFramesIn)/bpfCapture) { - framesToProcessThisIteration = sizeof(tempFramesIn)/bpfCapture; - } - - ma_copy_and_apply_volume_factor_pcm_frames(tempFramesIn, ma_offset_ptr(pFramesIn, totalFramesProcessed*bpfCapture), framesToProcessThisIteration, pDevice->capture.format, pDevice->capture.channels, masterVolumeFactor); - - ma_device__on_data(pDevice, ma_offset_ptr(pFramesOut, totalFramesProcessed*bpfPlayback), tempFramesIn, framesToProcessThisIteration); - - totalFramesProcessed += framesToProcessThisIteration; - } - } else { - ma_device__on_data(pDevice, pFramesOut, pFramesIn, frameCount); - } - - /* Volume control and clipping for playback devices. */ - if (pFramesOut != NULL) { - if (masterVolumeFactor != 1) { - if (pFramesIn == NULL) { /* <-- In full-duplex situations, the volume will have been applied to the input samples before the data callback. Applying it again post-callback will incorrectly compound it. */ - ma_apply_volume_factor_pcm_frames(pFramesOut, frameCount, pDevice->playback.format, pDevice->playback.channels, masterVolumeFactor); - } - } - - if (!pDevice->noClip && pDevice->playback.format == ma_format_f32) { - ma_clip_samples_f32((float*)pFramesOut, (const float*)pFramesOut, frameCount * pDevice->playback.channels); /* Intentionally specifying the same pointer for both input and output for in-place processing. */ - } - } - } - ma_device_restore_denormals(pDevice, prevDenormalState); - } else { - /* No data callback. Just silence the output. */ - if (pFramesOut != NULL) { - ma_silence_pcm_frames(pFramesOut, frameCount, pDevice->playback.format, pDevice->playback.channels); - } - } -} - - - -/* A helper function for reading sample data from the client. */ -static void ma_device__read_frames_from_client(ma_device* pDevice, ma_uint32 frameCount, void* pFramesOut) -{ - MA_ASSERT(pDevice != NULL); - MA_ASSERT(frameCount > 0); - MA_ASSERT(pFramesOut != NULL); - - if (pDevice->playback.converter.isPassthrough) { - ma_device__handle_data_callback(pDevice, pFramesOut, NULL, frameCount); - } else { - ma_result result; - ma_uint64 totalFramesReadOut; - void* pRunningFramesOut; - - totalFramesReadOut = 0; - pRunningFramesOut = pFramesOut; - - /* - We run slightly different logic depending on whether or not we're using a heap-allocated - buffer for caching input data. This will be the case if the data converter does not have - the ability to retrieve the required input frame count for a given output frame count. - */ - if (pDevice->playback.pInputCache != NULL) { - while (totalFramesReadOut < frameCount) { - ma_uint64 framesToReadThisIterationIn; - ma_uint64 framesToReadThisIterationOut; - - /* If there's any data available in the cache, that needs to get processed first. */ - if (pDevice->playback.inputCacheRemaining > 0) { - framesToReadThisIterationOut = (frameCount - totalFramesReadOut); - framesToReadThisIterationIn = framesToReadThisIterationOut; - if (framesToReadThisIterationIn > pDevice->playback.inputCacheRemaining) { - framesToReadThisIterationIn = pDevice->playback.inputCacheRemaining; - } - - result = ma_data_converter_process_pcm_frames(&pDevice->playback.converter, ma_offset_pcm_frames_ptr(pDevice->playback.pInputCache, pDevice->playback.inputCacheConsumed, pDevice->playback.format, pDevice->playback.channels), &framesToReadThisIterationIn, pRunningFramesOut, &framesToReadThisIterationOut); - if (result != MA_SUCCESS) { - break; - } - - pDevice->playback.inputCacheConsumed += framesToReadThisIterationIn; - pDevice->playback.inputCacheRemaining -= framesToReadThisIterationIn; - - totalFramesReadOut += framesToReadThisIterationOut; - pRunningFramesOut = ma_offset_ptr(pRunningFramesOut, framesToReadThisIterationOut * ma_get_bytes_per_frame(pDevice->playback.internalFormat, pDevice->playback.internalChannels)); - - if (framesToReadThisIterationIn == 0 && framesToReadThisIterationOut == 0) { - break; /* We're done. */ - } - } - - /* Getting here means there's no data in the cache and we need to fill it up with data from the client. */ - if (pDevice->playback.inputCacheRemaining == 0) { - ma_device__handle_data_callback(pDevice, pDevice->playback.pInputCache, NULL, (ma_uint32)pDevice->playback.inputCacheCap); - - pDevice->playback.inputCacheConsumed = 0; - pDevice->playback.inputCacheRemaining = pDevice->playback.inputCacheCap; - } - } - } else { - while (totalFramesReadOut < frameCount) { - ma_uint8 pIntermediaryBuffer[MA_DATA_CONVERTER_STACK_BUFFER_SIZE]; /* In client format. */ - ma_uint64 intermediaryBufferCap = sizeof(pIntermediaryBuffer) / ma_get_bytes_per_frame(pDevice->playback.format, pDevice->playback.channels); - ma_uint64 framesToReadThisIterationIn; - ma_uint64 framesReadThisIterationIn; - ma_uint64 framesToReadThisIterationOut; - ma_uint64 framesReadThisIterationOut; - ma_uint64 requiredInputFrameCount; - - framesToReadThisIterationOut = (frameCount - totalFramesReadOut); - framesToReadThisIterationIn = framesToReadThisIterationOut; - if (framesToReadThisIterationIn > intermediaryBufferCap) { - framesToReadThisIterationIn = intermediaryBufferCap; - } - - ma_data_converter_get_required_input_frame_count(&pDevice->playback.converter, framesToReadThisIterationOut, &requiredInputFrameCount); - if (framesToReadThisIterationIn > requiredInputFrameCount) { - framesToReadThisIterationIn = requiredInputFrameCount; - } - - ma_device__handle_data_callback(pDevice, pIntermediaryBuffer, NULL, (ma_uint32)framesToReadThisIterationIn); - - /* - At this point we have our decoded data in input format and now we need to convert to output format. Note that even if we didn't read any - input frames, we still want to try processing frames because there may some output frames generated from cached input data. - */ - framesReadThisIterationIn = framesToReadThisIterationIn; - framesReadThisIterationOut = framesToReadThisIterationOut; - result = ma_data_converter_process_pcm_frames(&pDevice->playback.converter, pIntermediaryBuffer, &framesReadThisIterationIn, pRunningFramesOut, &framesReadThisIterationOut); - if (result != MA_SUCCESS) { - break; - } - - totalFramesReadOut += framesReadThisIterationOut; - pRunningFramesOut = ma_offset_ptr(pRunningFramesOut, framesReadThisIterationOut * ma_get_bytes_per_frame(pDevice->playback.internalFormat, pDevice->playback.internalChannels)); - - if (framesReadThisIterationIn == 0 && framesReadThisIterationOut == 0) { - break; /* We're done. */ - } - } - } - } -} - -/* A helper for sending sample data to the client. */ -static void ma_device__send_frames_to_client(ma_device* pDevice, ma_uint32 frameCountInDeviceFormat, const void* pFramesInDeviceFormat) -{ - MA_ASSERT(pDevice != NULL); - MA_ASSERT(frameCountInDeviceFormat > 0); - MA_ASSERT(pFramesInDeviceFormat != NULL); - - if (pDevice->capture.converter.isPassthrough) { - ma_device__handle_data_callback(pDevice, NULL, pFramesInDeviceFormat, frameCountInDeviceFormat); - } else { - ma_result result; - ma_uint8 pFramesInClientFormat[MA_DATA_CONVERTER_STACK_BUFFER_SIZE]; - ma_uint64 framesInClientFormatCap = sizeof(pFramesInClientFormat) / ma_get_bytes_per_frame(pDevice->capture.format, pDevice->capture.channels); - ma_uint64 totalDeviceFramesProcessed = 0; - ma_uint64 totalClientFramesProcessed = 0; - const void* pRunningFramesInDeviceFormat = pFramesInDeviceFormat; - - /* We just keep going until we've exhausted all of our input frames and cannot generate any more output frames. */ - for (;;) { - ma_uint64 deviceFramesProcessedThisIteration; - ma_uint64 clientFramesProcessedThisIteration; - - deviceFramesProcessedThisIteration = (frameCountInDeviceFormat - totalDeviceFramesProcessed); - clientFramesProcessedThisIteration = framesInClientFormatCap; - - result = ma_data_converter_process_pcm_frames(&pDevice->capture.converter, pRunningFramesInDeviceFormat, &deviceFramesProcessedThisIteration, pFramesInClientFormat, &clientFramesProcessedThisIteration); - if (result != MA_SUCCESS) { - break; - } - - if (clientFramesProcessedThisIteration > 0) { - ma_device__handle_data_callback(pDevice, NULL, pFramesInClientFormat, (ma_uint32)clientFramesProcessedThisIteration); /* Safe cast. */ - } - - pRunningFramesInDeviceFormat = ma_offset_ptr(pRunningFramesInDeviceFormat, deviceFramesProcessedThisIteration * ma_get_bytes_per_frame(pDevice->capture.internalFormat, pDevice->capture.internalChannels)); - totalDeviceFramesProcessed += deviceFramesProcessedThisIteration; - totalClientFramesProcessed += clientFramesProcessedThisIteration; - - /* This is just to silence a warning. I might want to use this variable later so leaving in place for now. */ - (void)totalClientFramesProcessed; - - if (deviceFramesProcessedThisIteration == 0 && clientFramesProcessedThisIteration == 0) { - break; /* We're done. */ - } - } - } -} - -static ma_result ma_device__handle_duplex_callback_capture(ma_device* pDevice, ma_uint32 frameCountInDeviceFormat, const void* pFramesInDeviceFormat, ma_pcm_rb* pRB) -{ - ma_result result; - ma_uint32 totalDeviceFramesProcessed = 0; - const void* pRunningFramesInDeviceFormat = pFramesInDeviceFormat; - - MA_ASSERT(pDevice != NULL); - MA_ASSERT(frameCountInDeviceFormat > 0); - MA_ASSERT(pFramesInDeviceFormat != NULL); - MA_ASSERT(pRB != NULL); - - /* Write to the ring buffer. The ring buffer is in the client format which means we need to convert. */ - for (;;) { - ma_uint32 framesToProcessInDeviceFormat = (frameCountInDeviceFormat - totalDeviceFramesProcessed); - ma_uint32 framesToProcessInClientFormat = MA_DATA_CONVERTER_STACK_BUFFER_SIZE / ma_get_bytes_per_frame(pDevice->capture.format, pDevice->capture.channels); - ma_uint64 framesProcessedInDeviceFormat; - ma_uint64 framesProcessedInClientFormat; - void* pFramesInClientFormat; - - result = ma_pcm_rb_acquire_write(pRB, &framesToProcessInClientFormat, &pFramesInClientFormat); - if (result != MA_SUCCESS) { - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "Failed to acquire capture PCM frames from ring buffer."); - break; - } - - if (framesToProcessInClientFormat == 0) { - if (ma_pcm_rb_pointer_distance(pRB) == (ma_int32)ma_pcm_rb_get_subbuffer_size(pRB)) { - break; /* Overrun. Not enough room in the ring buffer for input frame. Excess frames are dropped. */ - } - } - - /* Convert. */ - framesProcessedInDeviceFormat = framesToProcessInDeviceFormat; - framesProcessedInClientFormat = framesToProcessInClientFormat; - result = ma_data_converter_process_pcm_frames(&pDevice->capture.converter, pRunningFramesInDeviceFormat, &framesProcessedInDeviceFormat, pFramesInClientFormat, &framesProcessedInClientFormat); - if (result != MA_SUCCESS) { - break; - } - - result = ma_pcm_rb_commit_write(pRB, (ma_uint32)framesProcessedInClientFormat); /* Safe cast. */ - if (result != MA_SUCCESS) { - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "Failed to commit capture PCM frames to ring buffer."); - break; - } - - pRunningFramesInDeviceFormat = ma_offset_ptr(pRunningFramesInDeviceFormat, framesProcessedInDeviceFormat * ma_get_bytes_per_frame(pDevice->capture.internalFormat, pDevice->capture.internalChannels)); - totalDeviceFramesProcessed += (ma_uint32)framesProcessedInDeviceFormat; /* Safe cast. */ - - /* We're done when we're unable to process any client nor device frames. */ - if (framesProcessedInClientFormat == 0 && framesProcessedInDeviceFormat == 0) { - break; /* Done. */ - } - } - - return MA_SUCCESS; -} - -static ma_result ma_device__handle_duplex_callback_playback(ma_device* pDevice, ma_uint32 frameCount, void* pFramesInInternalFormat, ma_pcm_rb* pRB) -{ - ma_result result; - ma_uint8 silentInputFrames[MA_DATA_CONVERTER_STACK_BUFFER_SIZE]; - ma_uint32 totalFramesReadOut = 0; - - MA_ASSERT(pDevice != NULL); - MA_ASSERT(frameCount > 0); - MA_ASSERT(pFramesInInternalFormat != NULL); - MA_ASSERT(pRB != NULL); - MA_ASSERT(pDevice->playback.pInputCache != NULL); - - /* - Sitting in the ring buffer should be captured data from the capture callback in external format. If there's not enough data in there for - the whole frameCount frames we just use silence instead for the input data. - */ - MA_ZERO_MEMORY(silentInputFrames, sizeof(silentInputFrames)); - - while (totalFramesReadOut < frameCount && ma_device_is_started(pDevice)) { - /* - We should have a buffer allocated on the heap. Any playback frames still sitting in there - need to be sent to the internal device before we process any more data from the client. - */ - if (pDevice->playback.inputCacheRemaining > 0) { - ma_uint64 framesConvertedIn = pDevice->playback.inputCacheRemaining; - ma_uint64 framesConvertedOut = (frameCount - totalFramesReadOut); - ma_data_converter_process_pcm_frames(&pDevice->playback.converter, ma_offset_pcm_frames_ptr(pDevice->playback.pInputCache, pDevice->playback.inputCacheConsumed, pDevice->playback.format, pDevice->playback.channels), &framesConvertedIn, pFramesInInternalFormat, &framesConvertedOut); - - pDevice->playback.inputCacheConsumed += framesConvertedIn; - pDevice->playback.inputCacheRemaining -= framesConvertedIn; - - totalFramesReadOut += (ma_uint32)framesConvertedOut; /* Safe cast. */ - pFramesInInternalFormat = ma_offset_ptr(pFramesInInternalFormat, framesConvertedOut * ma_get_bytes_per_frame(pDevice->playback.internalFormat, pDevice->playback.internalChannels)); - } - - /* If there's no more data in the cache we'll need to fill it with some. */ - if (totalFramesReadOut < frameCount && pDevice->playback.inputCacheRemaining == 0) { - ma_uint32 inputFrameCount; - void* pInputFrames; - - inputFrameCount = (ma_uint32)pDevice->playback.inputCacheCap; - result = ma_pcm_rb_acquire_read(pRB, &inputFrameCount, &pInputFrames); - if (result == MA_SUCCESS) { - if (inputFrameCount > 0) { - ma_device__handle_data_callback(pDevice, pDevice->playback.pInputCache, pInputFrames, inputFrameCount); - } else { - if (ma_pcm_rb_pointer_distance(pRB) == 0) { - break; /* Underrun. */ - } - } - } else { - /* No capture data available. Feed in silence. */ - inputFrameCount = (ma_uint32)ma_min(pDevice->playback.inputCacheCap, sizeof(silentInputFrames) / ma_get_bytes_per_frame(pDevice->capture.format, pDevice->capture.channels)); - ma_device__handle_data_callback(pDevice, pDevice->playback.pInputCache, silentInputFrames, inputFrameCount); - } - - pDevice->playback.inputCacheConsumed = 0; - pDevice->playback.inputCacheRemaining = inputFrameCount; - - result = ma_pcm_rb_commit_read(pRB, inputFrameCount); - if (result != MA_SUCCESS) { - return result; /* Should never happen. */ - } - } - } - - return MA_SUCCESS; -} - -/* A helper for changing the state of the device. */ -static MA_INLINE void ma_device__set_state(ma_device* pDevice, ma_device_state newState) -{ - ma_atomic_device_state_set(&pDevice->state, newState); -} - - -#if defined(MA_WIN32) - static GUID MA_GUID_KSDATAFORMAT_SUBTYPE_PCM = {0x00000001, 0x0000, 0x0010, {0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71}}; - static GUID MA_GUID_KSDATAFORMAT_SUBTYPE_IEEE_FLOAT = {0x00000003, 0x0000, 0x0010, {0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71}}; - /*static GUID MA_GUID_KSDATAFORMAT_SUBTYPE_ALAW = {0x00000006, 0x0000, 0x0010, {0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71}};*/ - /*static GUID MA_GUID_KSDATAFORMAT_SUBTYPE_MULAW = {0x00000007, 0x0000, 0x0010, {0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71}};*/ -#endif - - - -MA_API ma_uint32 ma_get_format_priority_index(ma_format format) /* Lower = better. */ -{ - ma_uint32 i; - for (i = 0; i < ma_countof(g_maFormatPriorities); ++i) { - if (g_maFormatPriorities[i] == format) { - return i; - } - } - - /* Getting here means the format could not be found or is equal to ma_format_unknown. */ - return (ma_uint32)-1; -} - -static ma_result ma_device__post_init_setup(ma_device* pDevice, ma_device_type deviceType); - -static ma_bool32 ma_device_descriptor_is_valid(const ma_device_descriptor* pDeviceDescriptor) -{ - if (pDeviceDescriptor == NULL) { - return MA_FALSE; - } - - if (pDeviceDescriptor->format == ma_format_unknown) { - return MA_FALSE; - } - - if (pDeviceDescriptor->channels == 0 || pDeviceDescriptor->channels > MA_MAX_CHANNELS) { - return MA_FALSE; - } - - if (pDeviceDescriptor->sampleRate == 0) { - return MA_FALSE; - } - - return MA_TRUE; -} - - -static ma_result ma_device_audio_thread__default_read_write(ma_device* pDevice) -{ - ma_result result = MA_SUCCESS; - ma_bool32 exitLoop = MA_FALSE; - ma_uint8 capturedDeviceData[MA_DATA_CONVERTER_STACK_BUFFER_SIZE]; - ma_uint8 playbackDeviceData[MA_DATA_CONVERTER_STACK_BUFFER_SIZE]; - ma_uint32 capturedDeviceDataCapInFrames = 0; - ma_uint32 playbackDeviceDataCapInFrames = 0; - - MA_ASSERT(pDevice != NULL); - - /* Just some quick validation on the device type and the available callbacks. */ - if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex || pDevice->type == ma_device_type_loopback) { - if (pDevice->pContext->callbacks.onDeviceRead == NULL) { - return MA_NOT_IMPLEMENTED; - } - - capturedDeviceDataCapInFrames = sizeof(capturedDeviceData) / ma_get_bytes_per_frame(pDevice->capture.internalFormat, pDevice->capture.internalChannels); - } - - if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) { - if (pDevice->pContext->callbacks.onDeviceWrite == NULL) { - return MA_NOT_IMPLEMENTED; - } - - playbackDeviceDataCapInFrames = sizeof(playbackDeviceData) / ma_get_bytes_per_frame(pDevice->playback.internalFormat, pDevice->playback.internalChannels); - } - - /* NOTE: The device was started outside of this function, in the worker thread. */ - - while (ma_device_get_state(pDevice) == ma_device_state_started && !exitLoop) { - switch (pDevice->type) { - case ma_device_type_duplex: - { - /* The process is: onDeviceRead() -> convert -> callback -> convert -> onDeviceWrite() */ - ma_uint32 totalCapturedDeviceFramesProcessed = 0; - ma_uint32 capturedDevicePeriodSizeInFrames = ma_min(pDevice->capture.internalPeriodSizeInFrames, pDevice->playback.internalPeriodSizeInFrames); - - while (totalCapturedDeviceFramesProcessed < capturedDevicePeriodSizeInFrames) { - ma_uint32 capturedDeviceFramesRemaining; - ma_uint32 capturedDeviceFramesProcessed; - ma_uint32 capturedDeviceFramesToProcess; - ma_uint32 capturedDeviceFramesToTryProcessing = capturedDevicePeriodSizeInFrames - totalCapturedDeviceFramesProcessed; - if (capturedDeviceFramesToTryProcessing > capturedDeviceDataCapInFrames) { - capturedDeviceFramesToTryProcessing = capturedDeviceDataCapInFrames; - } - - result = pDevice->pContext->callbacks.onDeviceRead(pDevice, capturedDeviceData, capturedDeviceFramesToTryProcessing, &capturedDeviceFramesToProcess); - if (result != MA_SUCCESS) { - exitLoop = MA_TRUE; - break; - } - - capturedDeviceFramesRemaining = capturedDeviceFramesToProcess; - capturedDeviceFramesProcessed = 0; - - /* At this point we have our captured data in device format and we now need to convert it to client format. */ - for (;;) { - ma_uint8 capturedClientData[MA_DATA_CONVERTER_STACK_BUFFER_SIZE]; - ma_uint8 playbackClientData[MA_DATA_CONVERTER_STACK_BUFFER_SIZE]; - ma_uint32 capturedClientDataCapInFrames = sizeof(capturedClientData) / ma_get_bytes_per_frame(pDevice->capture.format, pDevice->capture.channels); - ma_uint32 playbackClientDataCapInFrames = sizeof(playbackClientData) / ma_get_bytes_per_frame(pDevice->playback.format, pDevice->playback.channels); - ma_uint64 capturedClientFramesToProcessThisIteration = ma_min(capturedClientDataCapInFrames, playbackClientDataCapInFrames); - ma_uint64 capturedDeviceFramesToProcessThisIteration = capturedDeviceFramesRemaining; - ma_uint8* pRunningCapturedDeviceFrames = ma_offset_ptr(capturedDeviceData, capturedDeviceFramesProcessed * ma_get_bytes_per_frame(pDevice->capture.internalFormat, pDevice->capture.internalChannels)); - - /* Convert capture data from device format to client format. */ - result = ma_data_converter_process_pcm_frames(&pDevice->capture.converter, pRunningCapturedDeviceFrames, &capturedDeviceFramesToProcessThisIteration, capturedClientData, &capturedClientFramesToProcessThisIteration); - if (result != MA_SUCCESS) { - break; - } - - /* - If we weren't able to generate any output frames it must mean we've exhausted all of our input. The only time this would not be the case is if capturedClientData was too small - which should never be the case when it's of the size MA_DATA_CONVERTER_STACK_BUFFER_SIZE. - */ - if (capturedClientFramesToProcessThisIteration == 0) { - break; - } - - ma_device__handle_data_callback(pDevice, playbackClientData, capturedClientData, (ma_uint32)capturedClientFramesToProcessThisIteration); /* Safe cast .*/ - - capturedDeviceFramesProcessed += (ma_uint32)capturedDeviceFramesToProcessThisIteration; /* Safe cast. */ - capturedDeviceFramesRemaining -= (ma_uint32)capturedDeviceFramesToProcessThisIteration; /* Safe cast. */ - - /* At this point the playbackClientData buffer should be holding data that needs to be written to the device. */ - for (;;) { - ma_uint64 convertedClientFrameCount = capturedClientFramesToProcessThisIteration; - ma_uint64 convertedDeviceFrameCount = playbackDeviceDataCapInFrames; - result = ma_data_converter_process_pcm_frames(&pDevice->playback.converter, playbackClientData, &convertedClientFrameCount, playbackDeviceData, &convertedDeviceFrameCount); - if (result != MA_SUCCESS) { - break; - } - - result = pDevice->pContext->callbacks.onDeviceWrite(pDevice, playbackDeviceData, (ma_uint32)convertedDeviceFrameCount, NULL); /* Safe cast. */ - if (result != MA_SUCCESS) { - exitLoop = MA_TRUE; - break; - } - - capturedClientFramesToProcessThisIteration -= (ma_uint32)convertedClientFrameCount; /* Safe cast. */ - if (capturedClientFramesToProcessThisIteration == 0) { - break; - } - } - - /* In case an error happened from ma_device_write__null()... */ - if (result != MA_SUCCESS) { - exitLoop = MA_TRUE; - break; - } - } - - /* Make sure we don't get stuck in the inner loop. */ - if (capturedDeviceFramesProcessed == 0) { - break; - } - - totalCapturedDeviceFramesProcessed += capturedDeviceFramesProcessed; - } - } break; - - case ma_device_type_capture: - case ma_device_type_loopback: - { - ma_uint32 periodSizeInFrames = pDevice->capture.internalPeriodSizeInFrames; - ma_uint32 framesReadThisPeriod = 0; - while (framesReadThisPeriod < periodSizeInFrames) { - ma_uint32 framesRemainingInPeriod = periodSizeInFrames - framesReadThisPeriod; - ma_uint32 framesProcessed; - ma_uint32 framesToReadThisIteration = framesRemainingInPeriod; - if (framesToReadThisIteration > capturedDeviceDataCapInFrames) { - framesToReadThisIteration = capturedDeviceDataCapInFrames; - } - - result = pDevice->pContext->callbacks.onDeviceRead(pDevice, capturedDeviceData, framesToReadThisIteration, &framesProcessed); - if (result != MA_SUCCESS) { - exitLoop = MA_TRUE; - break; - } - - /* Make sure we don't get stuck in the inner loop. */ - if (framesProcessed == 0) { - break; - } - - ma_device__send_frames_to_client(pDevice, framesProcessed, capturedDeviceData); - - framesReadThisPeriod += framesProcessed; - } - } break; - - case ma_device_type_playback: - { - /* We write in chunks of the period size, but use a stack allocated buffer for the intermediary. */ - ma_uint32 periodSizeInFrames = pDevice->playback.internalPeriodSizeInFrames; - ma_uint32 framesWrittenThisPeriod = 0; - while (framesWrittenThisPeriod < periodSizeInFrames) { - ma_uint32 framesRemainingInPeriod = periodSizeInFrames - framesWrittenThisPeriod; - ma_uint32 framesProcessed; - ma_uint32 framesToWriteThisIteration = framesRemainingInPeriod; - if (framesToWriteThisIteration > playbackDeviceDataCapInFrames) { - framesToWriteThisIteration = playbackDeviceDataCapInFrames; - } - - ma_device__read_frames_from_client(pDevice, framesToWriteThisIteration, playbackDeviceData); - - result = pDevice->pContext->callbacks.onDeviceWrite(pDevice, playbackDeviceData, framesToWriteThisIteration, &framesProcessed); - if (result != MA_SUCCESS) { - exitLoop = MA_TRUE; - break; - } - - /* Make sure we don't get stuck in the inner loop. */ - if (framesProcessed == 0) { - break; - } - - framesWrittenThisPeriod += framesProcessed; - } - } break; - - /* Should never get here. */ - default: break; - } - } - - return result; -} - - - -/******************************************************************************* - -Null Backend - -*******************************************************************************/ -#ifdef MA_HAS_NULL - -#define MA_DEVICE_OP_NONE__NULL 0 -#define MA_DEVICE_OP_START__NULL 1 -#define MA_DEVICE_OP_SUSPEND__NULL 2 -#define MA_DEVICE_OP_KILL__NULL 3 - -static ma_thread_result MA_THREADCALL ma_device_thread__null(void* pData) -{ - ma_device* pDevice = (ma_device*)pData; - MA_ASSERT(pDevice != NULL); - - for (;;) { /* Keep the thread alive until the device is uninitialized. */ - ma_uint32 operation; - - /* Wait for an operation to be requested. */ - ma_event_wait(&pDevice->null_device.operationEvent); - - /* At this point an event should have been triggered. */ - operation = pDevice->null_device.operation; - - /* Starting the device needs to put the thread into a loop. */ - if (operation == MA_DEVICE_OP_START__NULL) { - /* Reset the timer just in case. */ - ma_timer_init(&pDevice->null_device.timer); - - /* Getting here means a suspend or kill operation has been requested. */ - pDevice->null_device.operationResult = MA_SUCCESS; - ma_event_signal(&pDevice->null_device.operationCompletionEvent); - ma_semaphore_release(&pDevice->null_device.operationSemaphore); - continue; - } - - /* Suspending the device means we need to stop the timer and just continue the loop. */ - if (operation == MA_DEVICE_OP_SUSPEND__NULL) { - /* We need to add the current run time to the prior run time, then reset the timer. */ - pDevice->null_device.priorRunTime += ma_timer_get_time_in_seconds(&pDevice->null_device.timer); - ma_timer_init(&pDevice->null_device.timer); - - /* We're done. */ - pDevice->null_device.operationResult = MA_SUCCESS; - ma_event_signal(&pDevice->null_device.operationCompletionEvent); - ma_semaphore_release(&pDevice->null_device.operationSemaphore); - continue; - } - - /* Killing the device means we need to get out of this loop so that this thread can terminate. */ - if (operation == MA_DEVICE_OP_KILL__NULL) { - pDevice->null_device.operationResult = MA_SUCCESS; - ma_event_signal(&pDevice->null_device.operationCompletionEvent); - ma_semaphore_release(&pDevice->null_device.operationSemaphore); - break; - } - - /* Getting a signal on a "none" operation probably means an error. Return invalid operation. */ - if (operation == MA_DEVICE_OP_NONE__NULL) { - MA_ASSERT(MA_FALSE); /* <-- Trigger this in debug mode to ensure developers are aware they're doing something wrong (or there's a bug in a miniaudio). */ - pDevice->null_device.operationResult = MA_INVALID_OPERATION; - ma_event_signal(&pDevice->null_device.operationCompletionEvent); - ma_semaphore_release(&pDevice->null_device.operationSemaphore); - continue; /* Continue the loop. Don't terminate. */ - } - } - - return (ma_thread_result)0; -} - -static ma_result ma_device_do_operation__null(ma_device* pDevice, ma_uint32 operation) -{ - ma_result result; - - /* - TODO: Need to review this and consider just using mutual exclusion. I think the original motivation - for this was to just post the event to a queue and return immediately, but that has since changed - and now this function is synchronous. I think this can be simplified to just use a mutex. - */ - - /* - The first thing to do is wait for an operation slot to become available. We only have a single slot for this, but we could extend this later - to support queuing of operations. - */ - result = ma_semaphore_wait(&pDevice->null_device.operationSemaphore); - if (result != MA_SUCCESS) { - return result; /* Failed to wait for the event. */ - } - - /* - When we get here it means the background thread is not referencing the operation code and it can be changed. After changing this we need to - signal an event to the worker thread to let it know that it can start work. - */ - pDevice->null_device.operation = operation; - - /* Once the operation code has been set, the worker thread can start work. */ - if (ma_event_signal(&pDevice->null_device.operationEvent) != MA_SUCCESS) { - return MA_ERROR; - } - - /* We want everything to be synchronous so we're going to wait for the worker thread to complete it's operation. */ - if (ma_event_wait(&pDevice->null_device.operationCompletionEvent) != MA_SUCCESS) { - return MA_ERROR; - } - - return pDevice->null_device.operationResult; -} - -static ma_uint64 ma_device_get_total_run_time_in_frames__null(ma_device* pDevice) -{ - ma_uint32 internalSampleRate; - if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) { - internalSampleRate = pDevice->capture.internalSampleRate; - } else { - internalSampleRate = pDevice->playback.internalSampleRate; - } - - return (ma_uint64)((pDevice->null_device.priorRunTime + ma_timer_get_time_in_seconds(&pDevice->null_device.timer)) * internalSampleRate); -} - -static ma_result ma_context_enumerate_devices__null(ma_context* pContext, ma_enum_devices_callback_proc callback, void* pUserData) -{ - ma_bool32 cbResult = MA_TRUE; - - MA_ASSERT(pContext != NULL); - MA_ASSERT(callback != NULL); - - /* Playback. */ - if (cbResult) { - ma_device_info deviceInfo; - MA_ZERO_OBJECT(&deviceInfo); - ma_strncpy_s(deviceInfo.name, sizeof(deviceInfo.name), "NULL Playback Device", (size_t)-1); - deviceInfo.isDefault = MA_TRUE; /* Only one playback and capture device for the null backend, so might as well mark as default. */ - cbResult = callback(pContext, ma_device_type_playback, &deviceInfo, pUserData); - } - - /* Capture. */ - if (cbResult) { - ma_device_info deviceInfo; - MA_ZERO_OBJECT(&deviceInfo); - ma_strncpy_s(deviceInfo.name, sizeof(deviceInfo.name), "NULL Capture Device", (size_t)-1); - deviceInfo.isDefault = MA_TRUE; /* Only one playback and capture device for the null backend, so might as well mark as default. */ - cbResult = callback(pContext, ma_device_type_capture, &deviceInfo, pUserData); - } - - (void)cbResult; /* Silence a static analysis warning. */ - - return MA_SUCCESS; -} - -static ma_result ma_context_get_device_info__null(ma_context* pContext, ma_device_type deviceType, const ma_device_id* pDeviceID, ma_device_info* pDeviceInfo) -{ - MA_ASSERT(pContext != NULL); - - if (pDeviceID != NULL && pDeviceID->nullbackend != 0) { - return MA_NO_DEVICE; /* Don't know the device. */ - } - - /* Name / Description */ - if (deviceType == ma_device_type_playback) { - ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), "NULL Playback Device", (size_t)-1); - } else { - ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), "NULL Capture Device", (size_t)-1); - } - - pDeviceInfo->isDefault = MA_TRUE; /* Only one playback and capture device for the null backend, so might as well mark as default. */ - - /* Support everything on the null backend. */ - pDeviceInfo->nativeDataFormats[0].format = ma_format_unknown; - pDeviceInfo->nativeDataFormats[0].channels = 0; - pDeviceInfo->nativeDataFormats[0].sampleRate = 0; - pDeviceInfo->nativeDataFormats[0].flags = 0; - pDeviceInfo->nativeDataFormatCount = 1; - - (void)pContext; - return MA_SUCCESS; -} - - -static ma_result ma_device_uninit__null(ma_device* pDevice) -{ - MA_ASSERT(pDevice != NULL); - - /* Keep it clean and wait for the device thread to finish before returning. */ - ma_device_do_operation__null(pDevice, MA_DEVICE_OP_KILL__NULL); - - /* Wait for the thread to finish before continuing. */ - ma_thread_wait(&pDevice->null_device.deviceThread); - - /* At this point the loop in the device thread is as good as terminated so we can uninitialize our events. */ - ma_semaphore_uninit(&pDevice->null_device.operationSemaphore); - ma_event_uninit(&pDevice->null_device.operationCompletionEvent); - ma_event_uninit(&pDevice->null_device.operationEvent); - - return MA_SUCCESS; -} - -static ma_result ma_device_init__null(ma_device* pDevice, const ma_device_config* pConfig, ma_device_descriptor* pDescriptorPlayback, ma_device_descriptor* pDescriptorCapture) -{ - ma_result result; - - MA_ASSERT(pDevice != NULL); - - MA_ZERO_OBJECT(&pDevice->null_device); - - if (pConfig->deviceType == ma_device_type_loopback) { - return MA_DEVICE_TYPE_NOT_SUPPORTED; - } - - /* The null backend supports everything exactly as we specify it. */ - if (pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) { - pDescriptorCapture->format = (pDescriptorCapture->format != ma_format_unknown) ? pDescriptorCapture->format : MA_DEFAULT_FORMAT; - pDescriptorCapture->channels = (pDescriptorCapture->channels != 0) ? pDescriptorCapture->channels : MA_DEFAULT_CHANNELS; - pDescriptorCapture->sampleRate = (pDescriptorCapture->sampleRate != 0) ? pDescriptorCapture->sampleRate : MA_DEFAULT_SAMPLE_RATE; - - if (pDescriptorCapture->channelMap[0] == MA_CHANNEL_NONE) { - ma_channel_map_init_standard(ma_standard_channel_map_default, pDescriptorCapture->channelMap, ma_countof(pDescriptorCapture->channelMap), pDescriptorCapture->channels); - } - - pDescriptorCapture->periodSizeInFrames = ma_calculate_buffer_size_in_frames_from_descriptor(pDescriptorCapture, pDescriptorCapture->sampleRate, pConfig->performanceProfile); - } - - if (pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) { - pDescriptorPlayback->format = (pDescriptorPlayback->format != ma_format_unknown) ? pDescriptorPlayback->format : MA_DEFAULT_FORMAT; - pDescriptorPlayback->channels = (pDescriptorPlayback->channels != 0) ? pDescriptorPlayback->channels : MA_DEFAULT_CHANNELS; - pDescriptorPlayback->sampleRate = (pDescriptorPlayback->sampleRate != 0) ? pDescriptorPlayback->sampleRate : MA_DEFAULT_SAMPLE_RATE; - - if (pDescriptorPlayback->channelMap[0] == MA_CHANNEL_NONE) { - ma_channel_map_init_standard(ma_standard_channel_map_default, pDescriptorPlayback->channelMap, ma_countof(pDescriptorCapture->channelMap), pDescriptorPlayback->channels); - } - - pDescriptorPlayback->periodSizeInFrames = ma_calculate_buffer_size_in_frames_from_descriptor(pDescriptorPlayback, pDescriptorPlayback->sampleRate, pConfig->performanceProfile); - } - - /* - In order to get timing right, we need to create a thread that does nothing but keeps track of the timer. This timer is started when the - first period is "written" to it, and then stopped in ma_device_stop__null(). - */ - result = ma_event_init(&pDevice->null_device.operationEvent); - if (result != MA_SUCCESS) { - return result; - } - - result = ma_event_init(&pDevice->null_device.operationCompletionEvent); - if (result != MA_SUCCESS) { - return result; - } - - result = ma_semaphore_init(1, &pDevice->null_device.operationSemaphore); /* <-- It's important that the initial value is set to 1. */ - if (result != MA_SUCCESS) { - return result; - } - - result = ma_thread_create(&pDevice->null_device.deviceThread, pDevice->pContext->threadPriority, 0, ma_device_thread__null, pDevice, &pDevice->pContext->allocationCallbacks); - if (result != MA_SUCCESS) { - return result; - } - - return MA_SUCCESS; -} - -static ma_result ma_device_start__null(ma_device* pDevice) -{ - MA_ASSERT(pDevice != NULL); - - ma_device_do_operation__null(pDevice, MA_DEVICE_OP_START__NULL); - - ma_atomic_bool32_set(&pDevice->null_device.isStarted, MA_TRUE); - return MA_SUCCESS; -} - -static ma_result ma_device_stop__null(ma_device* pDevice) -{ - MA_ASSERT(pDevice != NULL); - - ma_device_do_operation__null(pDevice, MA_DEVICE_OP_SUSPEND__NULL); - - ma_atomic_bool32_set(&pDevice->null_device.isStarted, MA_FALSE); - return MA_SUCCESS; -} - -static ma_bool32 ma_device_is_started__null(ma_device* pDevice) -{ - MA_ASSERT(pDevice != NULL); - - return ma_atomic_bool32_get(&pDevice->null_device.isStarted); -} - -static ma_result ma_device_write__null(ma_device* pDevice, const void* pPCMFrames, ma_uint32 frameCount, ma_uint32* pFramesWritten) -{ - ma_result result = MA_SUCCESS; - ma_uint32 totalPCMFramesProcessed; - ma_bool32 wasStartedOnEntry; - - if (pFramesWritten != NULL) { - *pFramesWritten = 0; - } - - wasStartedOnEntry = ma_device_is_started__null(pDevice); - - /* Keep going until everything has been read. */ - totalPCMFramesProcessed = 0; - while (totalPCMFramesProcessed < frameCount) { - ma_uint64 targetFrame; - - /* If there are any frames remaining in the current period, consume those first. */ - if (pDevice->null_device.currentPeriodFramesRemainingPlayback > 0) { - ma_uint32 framesRemaining = (frameCount - totalPCMFramesProcessed); - ma_uint32 framesToProcess = pDevice->null_device.currentPeriodFramesRemainingPlayback; - if (framesToProcess > framesRemaining) { - framesToProcess = framesRemaining; - } - - /* We don't actually do anything with pPCMFrames, so just mark it as unused to prevent a warning. */ - (void)pPCMFrames; - - pDevice->null_device.currentPeriodFramesRemainingPlayback -= framesToProcess; - totalPCMFramesProcessed += framesToProcess; - } - - /* If we've consumed the current period we'll need to mark it as such an ensure the device is started if it's not already. */ - if (pDevice->null_device.currentPeriodFramesRemainingPlayback == 0) { - pDevice->null_device.currentPeriodFramesRemainingPlayback = 0; - - if (!ma_device_is_started__null(pDevice) && !wasStartedOnEntry) { - result = ma_device_start__null(pDevice); - if (result != MA_SUCCESS) { - break; - } - } - } - - /* If we've consumed the whole buffer we can return now. */ - MA_ASSERT(totalPCMFramesProcessed <= frameCount); - if (totalPCMFramesProcessed == frameCount) { - break; - } - - /* Getting here means we've still got more frames to consume, we but need to wait for it to become available. */ - targetFrame = pDevice->null_device.lastProcessedFramePlayback; - for (;;) { - ma_uint64 currentFrame; - - /* Stop waiting if the device has been stopped. */ - if (!ma_device_is_started__null(pDevice)) { - break; - } - - currentFrame = ma_device_get_total_run_time_in_frames__null(pDevice); - if (currentFrame >= targetFrame) { - break; - } - - /* Getting here means we haven't yet reached the target sample, so continue waiting. */ - ma_sleep(10); - } - - pDevice->null_device.lastProcessedFramePlayback += pDevice->playback.internalPeriodSizeInFrames; - pDevice->null_device.currentPeriodFramesRemainingPlayback = pDevice->playback.internalPeriodSizeInFrames; - } - - if (pFramesWritten != NULL) { - *pFramesWritten = totalPCMFramesProcessed; - } - - return result; -} - -static ma_result ma_device_read__null(ma_device* pDevice, void* pPCMFrames, ma_uint32 frameCount, ma_uint32* pFramesRead) -{ - ma_result result = MA_SUCCESS; - ma_uint32 totalPCMFramesProcessed; - - if (pFramesRead != NULL) { - *pFramesRead = 0; - } - - /* Keep going until everything has been read. */ - totalPCMFramesProcessed = 0; - while (totalPCMFramesProcessed < frameCount) { - ma_uint64 targetFrame; - - /* If there are any frames remaining in the current period, consume those first. */ - if (pDevice->null_device.currentPeriodFramesRemainingCapture > 0) { - ma_uint32 bpf = ma_get_bytes_per_frame(pDevice->capture.internalFormat, pDevice->capture.internalChannels); - ma_uint32 framesRemaining = (frameCount - totalPCMFramesProcessed); - ma_uint32 framesToProcess = pDevice->null_device.currentPeriodFramesRemainingCapture; - if (framesToProcess > framesRemaining) { - framesToProcess = framesRemaining; - } - - /* We need to ensure the output buffer is zeroed. */ - MA_ZERO_MEMORY(ma_offset_ptr(pPCMFrames, totalPCMFramesProcessed*bpf), framesToProcess*bpf); - - pDevice->null_device.currentPeriodFramesRemainingCapture -= framesToProcess; - totalPCMFramesProcessed += framesToProcess; - } - - /* If we've consumed the current period we'll need to mark it as such an ensure the device is started if it's not already. */ - if (pDevice->null_device.currentPeriodFramesRemainingCapture == 0) { - pDevice->null_device.currentPeriodFramesRemainingCapture = 0; - } - - /* If we've consumed the whole buffer we can return now. */ - MA_ASSERT(totalPCMFramesProcessed <= frameCount); - if (totalPCMFramesProcessed == frameCount) { - break; - } - - /* Getting here means we've still got more frames to consume, we but need to wait for it to become available. */ - targetFrame = pDevice->null_device.lastProcessedFrameCapture + pDevice->capture.internalPeriodSizeInFrames; - for (;;) { - ma_uint64 currentFrame; - - /* Stop waiting if the device has been stopped. */ - if (!ma_device_is_started__null(pDevice)) { - break; - } - - currentFrame = ma_device_get_total_run_time_in_frames__null(pDevice); - if (currentFrame >= targetFrame) { - break; - } - - /* Getting here means we haven't yet reached the target sample, so continue waiting. */ - ma_sleep(10); - } - - pDevice->null_device.lastProcessedFrameCapture += pDevice->capture.internalPeriodSizeInFrames; - pDevice->null_device.currentPeriodFramesRemainingCapture = pDevice->capture.internalPeriodSizeInFrames; - } - - if (pFramesRead != NULL) { - *pFramesRead = totalPCMFramesProcessed; - } - - return result; -} - -static ma_result ma_context_uninit__null(ma_context* pContext) -{ - MA_ASSERT(pContext != NULL); - MA_ASSERT(pContext->backend == ma_backend_null); - - (void)pContext; - return MA_SUCCESS; -} - -static ma_result ma_context_init__null(ma_context* pContext, const ma_context_config* pConfig, ma_backend_callbacks* pCallbacks) -{ - MA_ASSERT(pContext != NULL); - - (void)pConfig; - (void)pContext; - - pCallbacks->onContextInit = ma_context_init__null; - pCallbacks->onContextUninit = ma_context_uninit__null; - pCallbacks->onContextEnumerateDevices = ma_context_enumerate_devices__null; - pCallbacks->onContextGetDeviceInfo = ma_context_get_device_info__null; - pCallbacks->onDeviceInit = ma_device_init__null; - pCallbacks->onDeviceUninit = ma_device_uninit__null; - pCallbacks->onDeviceStart = ma_device_start__null; - pCallbacks->onDeviceStop = ma_device_stop__null; - pCallbacks->onDeviceRead = ma_device_read__null; - pCallbacks->onDeviceWrite = ma_device_write__null; - pCallbacks->onDeviceDataLoop = NULL; /* Our backend is asynchronous with a blocking read-write API which means we can get miniaudio to deal with the audio thread. */ - - /* The null backend always works. */ - return MA_SUCCESS; -} -#endif - - - -/******************************************************************************* - -WIN32 COMMON - -*******************************************************************************/ -#if defined(MA_WIN32) -#if defined(MA_WIN32_DESKTOP) || defined(MA_WIN32_GDK) - #define ma_CoInitializeEx(pContext, pvReserved, dwCoInit) ((pContext->win32.CoInitializeEx) ? ((MA_PFN_CoInitializeEx)pContext->win32.CoInitializeEx)(pvReserved, dwCoInit) : ((MA_PFN_CoInitialize)pContext->win32.CoInitialize)(pvReserved)) - #define ma_CoUninitialize(pContext) ((MA_PFN_CoUninitialize)pContext->win32.CoUninitialize)() - #define ma_CoCreateInstance(pContext, rclsid, pUnkOuter, dwClsContext, riid, ppv) ((MA_PFN_CoCreateInstance)pContext->win32.CoCreateInstance)(rclsid, pUnkOuter, dwClsContext, riid, ppv) - #define ma_CoTaskMemFree(pContext, pv) ((MA_PFN_CoTaskMemFree)pContext->win32.CoTaskMemFree)(pv) - #define ma_PropVariantClear(pContext, pvar) ((MA_PFN_PropVariantClear)pContext->win32.PropVariantClear)(pvar) -#else - #define ma_CoInitializeEx(pContext, pvReserved, dwCoInit) CoInitializeEx(pvReserved, dwCoInit) - #define ma_CoUninitialize(pContext) CoUninitialize() - #define ma_CoCreateInstance(pContext, rclsid, pUnkOuter, dwClsContext, riid, ppv) CoCreateInstance(rclsid, pUnkOuter, dwClsContext, riid, ppv) - #define ma_CoTaskMemFree(pContext, pv) CoTaskMemFree(pv) - #define ma_PropVariantClear(pContext, pvar) PropVariantClear(pvar) -#endif - -#if !defined(MAXULONG_PTR) && !defined(__WATCOMC__) -typedef size_t DWORD_PTR; -#endif - -#if !defined(WAVE_FORMAT_1M08) -#define WAVE_FORMAT_1M08 0x00000001 -#define WAVE_FORMAT_1S08 0x00000002 -#define WAVE_FORMAT_1M16 0x00000004 -#define WAVE_FORMAT_1S16 0x00000008 -#define WAVE_FORMAT_2M08 0x00000010 -#define WAVE_FORMAT_2S08 0x00000020 -#define WAVE_FORMAT_2M16 0x00000040 -#define WAVE_FORMAT_2S16 0x00000080 -#define WAVE_FORMAT_4M08 0x00000100 -#define WAVE_FORMAT_4S08 0x00000200 -#define WAVE_FORMAT_4M16 0x00000400 -#define WAVE_FORMAT_4S16 0x00000800 -#endif - -#if !defined(WAVE_FORMAT_44M08) -#define WAVE_FORMAT_44M08 0x00000100 -#define WAVE_FORMAT_44S08 0x00000200 -#define WAVE_FORMAT_44M16 0x00000400 -#define WAVE_FORMAT_44S16 0x00000800 -#define WAVE_FORMAT_48M08 0x00001000 -#define WAVE_FORMAT_48S08 0x00002000 -#define WAVE_FORMAT_48M16 0x00004000 -#define WAVE_FORMAT_48S16 0x00008000 -#define WAVE_FORMAT_96M08 0x00010000 -#define WAVE_FORMAT_96S08 0x00020000 -#define WAVE_FORMAT_96M16 0x00040000 -#define WAVE_FORMAT_96S16 0x00080000 -#endif - -#ifndef SPEAKER_FRONT_LEFT -#define SPEAKER_FRONT_LEFT 0x1 -#define SPEAKER_FRONT_RIGHT 0x2 -#define SPEAKER_FRONT_CENTER 0x4 -#define SPEAKER_LOW_FREQUENCY 0x8 -#define SPEAKER_BACK_LEFT 0x10 -#define SPEAKER_BACK_RIGHT 0x20 -#define SPEAKER_FRONT_LEFT_OF_CENTER 0x40 -#define SPEAKER_FRONT_RIGHT_OF_CENTER 0x80 -#define SPEAKER_BACK_CENTER 0x100 -#define SPEAKER_SIDE_LEFT 0x200 -#define SPEAKER_SIDE_RIGHT 0x400 -#define SPEAKER_TOP_CENTER 0x800 -#define SPEAKER_TOP_FRONT_LEFT 0x1000 -#define SPEAKER_TOP_FRONT_CENTER 0x2000 -#define SPEAKER_TOP_FRONT_RIGHT 0x4000 -#define SPEAKER_TOP_BACK_LEFT 0x8000 -#define SPEAKER_TOP_BACK_CENTER 0x10000 -#define SPEAKER_TOP_BACK_RIGHT 0x20000 -#endif - -/* -Implement our own version of MA_WAVEFORMATEXTENSIBLE so we can avoid a header. Be careful with this -because MA_WAVEFORMATEX has an extra two bytes over standard WAVEFORMATEX due to padding. The -standard version uses tight packing, but for compiler compatibility we're not doing that with ours. -*/ -typedef struct -{ - WORD wFormatTag; - WORD nChannels; - DWORD nSamplesPerSec; - DWORD nAvgBytesPerSec; - WORD nBlockAlign; - WORD wBitsPerSample; - WORD cbSize; -} MA_WAVEFORMATEX; - -typedef struct -{ - WORD wFormatTag; - WORD nChannels; - DWORD nSamplesPerSec; - DWORD nAvgBytesPerSec; - WORD nBlockAlign; - WORD wBitsPerSample; - WORD cbSize; - union - { - WORD wValidBitsPerSample; - WORD wSamplesPerBlock; - WORD wReserved; - } Samples; - DWORD dwChannelMask; - GUID SubFormat; -} MA_WAVEFORMATEXTENSIBLE; - - - -#ifndef WAVE_FORMAT_EXTENSIBLE -#define WAVE_FORMAT_EXTENSIBLE 0xFFFE -#endif - -#ifndef WAVE_FORMAT_PCM -#define WAVE_FORMAT_PCM 1 -#endif - -#ifndef WAVE_FORMAT_IEEE_FLOAT -#define WAVE_FORMAT_IEEE_FLOAT 0x0003 -#endif - -/* Converts an individual Win32-style channel identifier (SPEAKER_FRONT_LEFT, etc.) to miniaudio. */ -static ma_uint8 ma_channel_id_to_ma__win32(DWORD id) -{ - switch (id) - { - case SPEAKER_FRONT_LEFT: return MA_CHANNEL_FRONT_LEFT; - case SPEAKER_FRONT_RIGHT: return MA_CHANNEL_FRONT_RIGHT; - case SPEAKER_FRONT_CENTER: return MA_CHANNEL_FRONT_CENTER; - case SPEAKER_LOW_FREQUENCY: return MA_CHANNEL_LFE; - case SPEAKER_BACK_LEFT: return MA_CHANNEL_BACK_LEFT; - case SPEAKER_BACK_RIGHT: return MA_CHANNEL_BACK_RIGHT; - case SPEAKER_FRONT_LEFT_OF_CENTER: return MA_CHANNEL_FRONT_LEFT_CENTER; - case SPEAKER_FRONT_RIGHT_OF_CENTER: return MA_CHANNEL_FRONT_RIGHT_CENTER; - case SPEAKER_BACK_CENTER: return MA_CHANNEL_BACK_CENTER; - case SPEAKER_SIDE_LEFT: return MA_CHANNEL_SIDE_LEFT; - case SPEAKER_SIDE_RIGHT: return MA_CHANNEL_SIDE_RIGHT; - case SPEAKER_TOP_CENTER: return MA_CHANNEL_TOP_CENTER; - case SPEAKER_TOP_FRONT_LEFT: return MA_CHANNEL_TOP_FRONT_LEFT; - case SPEAKER_TOP_FRONT_CENTER: return MA_CHANNEL_TOP_FRONT_CENTER; - case SPEAKER_TOP_FRONT_RIGHT: return MA_CHANNEL_TOP_FRONT_RIGHT; - case SPEAKER_TOP_BACK_LEFT: return MA_CHANNEL_TOP_BACK_LEFT; - case SPEAKER_TOP_BACK_CENTER: return MA_CHANNEL_TOP_BACK_CENTER; - case SPEAKER_TOP_BACK_RIGHT: return MA_CHANNEL_TOP_BACK_RIGHT; - default: return 0; - } -} - -/* Converts an individual miniaudio channel identifier (MA_CHANNEL_FRONT_LEFT, etc.) to Win32-style. */ -static DWORD ma_channel_id_to_win32(DWORD id) -{ - switch (id) - { - case MA_CHANNEL_MONO: return SPEAKER_FRONT_CENTER; - case MA_CHANNEL_FRONT_LEFT: return SPEAKER_FRONT_LEFT; - case MA_CHANNEL_FRONT_RIGHT: return SPEAKER_FRONT_RIGHT; - case MA_CHANNEL_FRONT_CENTER: return SPEAKER_FRONT_CENTER; - case MA_CHANNEL_LFE: return SPEAKER_LOW_FREQUENCY; - case MA_CHANNEL_BACK_LEFT: return SPEAKER_BACK_LEFT; - case MA_CHANNEL_BACK_RIGHT: return SPEAKER_BACK_RIGHT; - case MA_CHANNEL_FRONT_LEFT_CENTER: return SPEAKER_FRONT_LEFT_OF_CENTER; - case MA_CHANNEL_FRONT_RIGHT_CENTER: return SPEAKER_FRONT_RIGHT_OF_CENTER; - case MA_CHANNEL_BACK_CENTER: return SPEAKER_BACK_CENTER; - case MA_CHANNEL_SIDE_LEFT: return SPEAKER_SIDE_LEFT; - case MA_CHANNEL_SIDE_RIGHT: return SPEAKER_SIDE_RIGHT; - case MA_CHANNEL_TOP_CENTER: return SPEAKER_TOP_CENTER; - case MA_CHANNEL_TOP_FRONT_LEFT: return SPEAKER_TOP_FRONT_LEFT; - case MA_CHANNEL_TOP_FRONT_CENTER: return SPEAKER_TOP_FRONT_CENTER; - case MA_CHANNEL_TOP_FRONT_RIGHT: return SPEAKER_TOP_FRONT_RIGHT; - case MA_CHANNEL_TOP_BACK_LEFT: return SPEAKER_TOP_BACK_LEFT; - case MA_CHANNEL_TOP_BACK_CENTER: return SPEAKER_TOP_BACK_CENTER; - case MA_CHANNEL_TOP_BACK_RIGHT: return SPEAKER_TOP_BACK_RIGHT; - default: return 0; - } -} - -/* Converts a channel mapping to a Win32-style channel mask. */ -static DWORD ma_channel_map_to_channel_mask__win32(const ma_channel* pChannelMap, ma_uint32 channels) -{ - DWORD dwChannelMask = 0; - ma_uint32 iChannel; - - for (iChannel = 0; iChannel < channels; ++iChannel) { - dwChannelMask |= ma_channel_id_to_win32(pChannelMap[iChannel]); - } - - return dwChannelMask; -} - -/* Converts a Win32-style channel mask to a miniaudio channel map. */ -static void ma_channel_mask_to_channel_map__win32(DWORD dwChannelMask, ma_uint32 channels, ma_channel* pChannelMap) -{ - /* If the channel mask is set to 0, just assume a default Win32 channel map. */ - if (dwChannelMask == 0) { - ma_channel_map_init_standard(ma_standard_channel_map_microsoft, pChannelMap, channels, channels); - } else { - if (channels == 1 && (dwChannelMask & SPEAKER_FRONT_CENTER) != 0) { - pChannelMap[0] = MA_CHANNEL_MONO; - } else { - /* Just iterate over each bit. */ - ma_uint32 iChannel = 0; - ma_uint32 iBit; - - for (iBit = 0; iBit < 32 && iChannel < channels; ++iBit) { - DWORD bitValue = (dwChannelMask & (1UL << iBit)); - if (bitValue != 0) { - /* The bit is set. */ - pChannelMap[iChannel] = ma_channel_id_to_ma__win32(bitValue); - iChannel += 1; - } - } - } - } -} - -#ifdef __cplusplus -static ma_bool32 ma_is_guid_equal(const void* a, const void* b) -{ - return IsEqualGUID(*(const GUID*)a, *(const GUID*)b); -} -#else -#define ma_is_guid_equal(a, b) IsEqualGUID((const GUID*)a, (const GUID*)b) -#endif - -static MA_INLINE ma_bool32 ma_is_guid_null(const void* guid) -{ - static GUID nullguid = {0x00000000, 0x0000, 0x0000, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}; - return ma_is_guid_equal(guid, &nullguid); -} - -static ma_format ma_format_from_WAVEFORMATEX(const MA_WAVEFORMATEX* pWF) -{ - MA_ASSERT(pWF != NULL); - - if (pWF->wFormatTag == WAVE_FORMAT_EXTENSIBLE) { - const MA_WAVEFORMATEXTENSIBLE* pWFEX = (const MA_WAVEFORMATEXTENSIBLE*)pWF; - if (ma_is_guid_equal(&pWFEX->SubFormat, &MA_GUID_KSDATAFORMAT_SUBTYPE_PCM)) { - if (pWFEX->Samples.wValidBitsPerSample == 32) { - return ma_format_s32; - } - if (pWFEX->Samples.wValidBitsPerSample == 24) { - if (pWFEX->wBitsPerSample == 32) { - return ma_format_s32; - } - if (pWFEX->wBitsPerSample == 24) { - return ma_format_s24; - } - } - if (pWFEX->Samples.wValidBitsPerSample == 16) { - return ma_format_s16; - } - if (pWFEX->Samples.wValidBitsPerSample == 8) { - return ma_format_u8; - } - } - if (ma_is_guid_equal(&pWFEX->SubFormat, &MA_GUID_KSDATAFORMAT_SUBTYPE_IEEE_FLOAT)) { - if (pWFEX->Samples.wValidBitsPerSample == 32) { - return ma_format_f32; - } - /* - if (pWFEX->Samples.wValidBitsPerSample == 64) { - return ma_format_f64; - } - */ - } - } else { - if (pWF->wFormatTag == WAVE_FORMAT_PCM) { - if (pWF->wBitsPerSample == 32) { - return ma_format_s32; - } - if (pWF->wBitsPerSample == 24) { - return ma_format_s24; - } - if (pWF->wBitsPerSample == 16) { - return ma_format_s16; - } - if (pWF->wBitsPerSample == 8) { - return ma_format_u8; - } - } - if (pWF->wFormatTag == WAVE_FORMAT_IEEE_FLOAT) { - if (pWF->wBitsPerSample == 32) { - return ma_format_f32; - } - if (pWF->wBitsPerSample == 64) { - /*return ma_format_f64;*/ - } - } - } - - return ma_format_unknown; -} -#endif - - -/******************************************************************************* - -WASAPI Backend - -*******************************************************************************/ -#ifdef MA_HAS_WASAPI -#if 0 -#if defined(_MSC_VER) - #pragma warning(push) - #pragma warning(disable:4091) /* 'typedef ': ignored on left of '' when no variable is declared */ -#endif -#include -#include -#if defined(_MSC_VER) - #pragma warning(pop) -#endif -#endif /* 0 */ - -static ma_result ma_device_reroute__wasapi(ma_device* pDevice, ma_device_type deviceType); - -/* Some compilers don't define VerifyVersionInfoW. Need to write this ourselves. */ -#define MA_WIN32_WINNT_VISTA 0x0600 -#define MA_VER_MINORVERSION 0x01 -#define MA_VER_MAJORVERSION 0x02 -#define MA_VER_SERVICEPACKMAJOR 0x20 -#define MA_VER_GREATER_EQUAL 0x03 - -typedef struct { - DWORD dwOSVersionInfoSize; - DWORD dwMajorVersion; - DWORD dwMinorVersion; - DWORD dwBuildNumber; - DWORD dwPlatformId; - WCHAR szCSDVersion[128]; - WORD wServicePackMajor; - WORD wServicePackMinor; - WORD wSuiteMask; - BYTE wProductType; - BYTE wReserved; -} ma_OSVERSIONINFOEXW; - -typedef BOOL (WINAPI * ma_PFNVerifyVersionInfoW) (ma_OSVERSIONINFOEXW* lpVersionInfo, DWORD dwTypeMask, DWORDLONG dwlConditionMask); -typedef ULONGLONG (WINAPI * ma_PFNVerSetConditionMask)(ULONGLONG dwlConditionMask, DWORD dwTypeBitMask, BYTE dwConditionMask); - - -#ifndef PROPERTYKEY_DEFINED -#define PROPERTYKEY_DEFINED -#ifndef __WATCOMC__ -typedef struct -{ - GUID fmtid; - DWORD pid; -} PROPERTYKEY; -#endif -#endif - -/* Some compilers don't define PropVariantInit(). We just do this ourselves since it's just a memset(). */ -static MA_INLINE void ma_PropVariantInit(MA_PROPVARIANT* pProp) -{ - MA_ZERO_OBJECT(pProp); -} - - -static const PROPERTYKEY MA_PKEY_Device_FriendlyName = {{0xA45C254E, 0xDF1C, 0x4EFD, {0x80, 0x20, 0x67, 0xD1, 0x46, 0xA8, 0x50, 0xE0}}, 14}; -static const PROPERTYKEY MA_PKEY_AudioEngine_DeviceFormat = {{0xF19F064D, 0x82C, 0x4E27, {0xBC, 0x73, 0x68, 0x82, 0xA1, 0xBB, 0x8E, 0x4C}}, 0}; - -static const IID MA_IID_IUnknown = {0x00000000, 0x0000, 0x0000, {0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46}}; /* 00000000-0000-0000-C000-000000000046 */ -#if !defined(MA_WIN32_DESKTOP) && !defined(MA_WIN32_GDK) -static const IID MA_IID_IAgileObject = {0x94EA2B94, 0xE9CC, 0x49E0, {0xC0, 0xFF, 0xEE, 0x64, 0xCA, 0x8F, 0x5B, 0x90}}; /* 94EA2B94-E9CC-49E0-C0FF-EE64CA8F5B90 */ -#endif - -static const IID MA_IID_IAudioClient = {0x1CB9AD4C, 0xDBFA, 0x4C32, {0xB1, 0x78, 0xC2, 0xF5, 0x68, 0xA7, 0x03, 0xB2}}; /* 1CB9AD4C-DBFA-4C32-B178-C2F568A703B2 = __uuidof(IAudioClient) */ -static const IID MA_IID_IAudioClient2 = {0x726778CD, 0xF60A, 0x4EDA, {0x82, 0xDE, 0xE4, 0x76, 0x10, 0xCD, 0x78, 0xAA}}; /* 726778CD-F60A-4EDA-82DE-E47610CD78AA = __uuidof(IAudioClient2) */ -static const IID MA_IID_IAudioClient3 = {0x7ED4EE07, 0x8E67, 0x4CD4, {0x8C, 0x1A, 0x2B, 0x7A, 0x59, 0x87, 0xAD, 0x42}}; /* 7ED4EE07-8E67-4CD4-8C1A-2B7A5987AD42 = __uuidof(IAudioClient3) */ -static const IID MA_IID_IAudioRenderClient = {0xF294ACFC, 0x3146, 0x4483, {0xA7, 0xBF, 0xAD, 0xDC, 0xA7, 0xC2, 0x60, 0xE2}}; /* F294ACFC-3146-4483-A7BF-ADDCA7C260E2 = __uuidof(IAudioRenderClient) */ -static const IID MA_IID_IAudioCaptureClient = {0xC8ADBD64, 0xE71E, 0x48A0, {0xA4, 0xDE, 0x18, 0x5C, 0x39, 0x5C, 0xD3, 0x17}}; /* C8ADBD64-E71E-48A0-A4DE-185C395CD317 = __uuidof(IAudioCaptureClient) */ -static const IID MA_IID_IMMNotificationClient = {0x7991EEC9, 0x7E89, 0x4D85, {0x83, 0x90, 0x6C, 0x70, 0x3C, 0xEC, 0x60, 0xC0}}; /* 7991EEC9-7E89-4D85-8390-6C703CEC60C0 = __uuidof(IMMNotificationClient) */ -#if !defined(MA_WIN32_DESKTOP) && !defined(MA_WIN32_GDK) -static const IID MA_IID_DEVINTERFACE_AUDIO_RENDER = {0xE6327CAD, 0xDCEC, 0x4949, {0xAE, 0x8A, 0x99, 0x1E, 0x97, 0x6A, 0x79, 0xD2}}; /* E6327CAD-DCEC-4949-AE8A-991E976A79D2 */ -static const IID MA_IID_DEVINTERFACE_AUDIO_CAPTURE = {0x2EEF81BE, 0x33FA, 0x4800, {0x96, 0x70, 0x1C, 0xD4, 0x74, 0x97, 0x2C, 0x3F}}; /* 2EEF81BE-33FA-4800-9670-1CD474972C3F */ -static const IID MA_IID_IActivateAudioInterfaceCompletionHandler = {0x41D949AB, 0x9862, 0x444A, {0x80, 0xF6, 0xC2, 0x61, 0x33, 0x4D, 0xA5, 0xEB}}; /* 41D949AB-9862-444A-80F6-C261334DA5EB */ -#endif - -static const IID MA_CLSID_MMDeviceEnumerator = {0xBCDE0395, 0xE52F, 0x467C, {0x8E, 0x3D, 0xC4, 0x57, 0x92, 0x91, 0x69, 0x2E}}; /* BCDE0395-E52F-467C-8E3D-C4579291692E = __uuidof(MMDeviceEnumerator) */ -static const IID MA_IID_IMMDeviceEnumerator = {0xA95664D2, 0x9614, 0x4F35, {0xA7, 0x46, 0xDE, 0x8D, 0xB6, 0x36, 0x17, 0xE6}}; /* A95664D2-9614-4F35-A746-DE8DB63617E6 = __uuidof(IMMDeviceEnumerator) */ - -#if defined(MA_WIN32_DESKTOP) || defined(MA_WIN32_GDK) -#define MA_MM_DEVICE_STATE_ACTIVE 1 -#define MA_MM_DEVICE_STATE_DISABLED 2 -#define MA_MM_DEVICE_STATE_NOTPRESENT 4 -#define MA_MM_DEVICE_STATE_UNPLUGGED 8 - -typedef struct ma_IMMDeviceEnumerator ma_IMMDeviceEnumerator; -typedef struct ma_IMMDeviceCollection ma_IMMDeviceCollection; -typedef struct ma_IMMDevice ma_IMMDevice; -#else -typedef struct ma_IActivateAudioInterfaceCompletionHandler ma_IActivateAudioInterfaceCompletionHandler; -typedef struct ma_IActivateAudioInterfaceAsyncOperation ma_IActivateAudioInterfaceAsyncOperation; -#endif -typedef struct ma_IPropertyStore ma_IPropertyStore; -typedef struct ma_IAudioClient ma_IAudioClient; -typedef struct ma_IAudioClient2 ma_IAudioClient2; -typedef struct ma_IAudioClient3 ma_IAudioClient3; -typedef struct ma_IAudioRenderClient ma_IAudioRenderClient; -typedef struct ma_IAudioCaptureClient ma_IAudioCaptureClient; - -typedef ma_int64 MA_REFERENCE_TIME; - -#define MA_AUDCLNT_STREAMFLAGS_CROSSPROCESS 0x00010000 -#define MA_AUDCLNT_STREAMFLAGS_LOOPBACK 0x00020000 -#define MA_AUDCLNT_STREAMFLAGS_EVENTCALLBACK 0x00040000 -#define MA_AUDCLNT_STREAMFLAGS_NOPERSIST 0x00080000 -#define MA_AUDCLNT_STREAMFLAGS_RATEADJUST 0x00100000 -#define MA_AUDCLNT_STREAMFLAGS_SRC_DEFAULT_QUALITY 0x08000000 -#define MA_AUDCLNT_STREAMFLAGS_AUTOCONVERTPCM 0x80000000 -#define MA_AUDCLNT_SESSIONFLAGS_EXPIREWHENUNOWNED 0x10000000 -#define MA_AUDCLNT_SESSIONFLAGS_DISPLAY_HIDE 0x20000000 -#define MA_AUDCLNT_SESSIONFLAGS_DISPLAY_HIDEWHENEXPIRED 0x40000000 - -/* Buffer flags. */ -#define MA_AUDCLNT_BUFFERFLAGS_DATA_DISCONTINUITY 1 -#define MA_AUDCLNT_BUFFERFLAGS_SILENT 2 -#define MA_AUDCLNT_BUFFERFLAGS_TIMESTAMP_ERROR 4 - -typedef enum -{ - ma_eRender = 0, - ma_eCapture = 1, - ma_eAll = 2 -} ma_EDataFlow; - -typedef enum -{ - ma_eConsole = 0, - ma_eMultimedia = 1, - ma_eCommunications = 2 -} ma_ERole; - -typedef enum -{ - MA_AUDCLNT_SHAREMODE_SHARED, - MA_AUDCLNT_SHAREMODE_EXCLUSIVE -} MA_AUDCLNT_SHAREMODE; - -typedef enum -{ - MA_AudioCategory_Other = 0 /* <-- miniaudio is only caring about Other. */ -} MA_AUDIO_STREAM_CATEGORY; - -typedef struct -{ - ma_uint32 cbSize; - BOOL bIsOffload; - MA_AUDIO_STREAM_CATEGORY eCategory; -} ma_AudioClientProperties; - -/* IUnknown */ -typedef struct -{ - /* IUnknown */ - HRESULT (STDMETHODCALLTYPE * QueryInterface)(ma_IUnknown* pThis, const IID* const riid, void** ppObject); - ULONG (STDMETHODCALLTYPE * AddRef) (ma_IUnknown* pThis); - ULONG (STDMETHODCALLTYPE * Release) (ma_IUnknown* pThis); -} ma_IUnknownVtbl; -struct ma_IUnknown -{ - ma_IUnknownVtbl* lpVtbl; -}; -static MA_INLINE HRESULT ma_IUnknown_QueryInterface(ma_IUnknown* pThis, const IID* const riid, void** ppObject) { return pThis->lpVtbl->QueryInterface(pThis, riid, ppObject); } -static MA_INLINE ULONG ma_IUnknown_AddRef(ma_IUnknown* pThis) { return pThis->lpVtbl->AddRef(pThis); } -static MA_INLINE ULONG ma_IUnknown_Release(ma_IUnknown* pThis) { return pThis->lpVtbl->Release(pThis); } - -#if defined(MA_WIN32_DESKTOP) || defined(MA_WIN32_GDK) - /* IMMNotificationClient */ - typedef struct - { - /* IUnknown */ - HRESULT (STDMETHODCALLTYPE * QueryInterface)(ma_IMMNotificationClient* pThis, const IID* const riid, void** ppObject); - ULONG (STDMETHODCALLTYPE * AddRef) (ma_IMMNotificationClient* pThis); - ULONG (STDMETHODCALLTYPE * Release) (ma_IMMNotificationClient* pThis); - - /* IMMNotificationClient */ - HRESULT (STDMETHODCALLTYPE * OnDeviceStateChanged) (ma_IMMNotificationClient* pThis, const WCHAR* pDeviceID, DWORD dwNewState); - HRESULT (STDMETHODCALLTYPE * OnDeviceAdded) (ma_IMMNotificationClient* pThis, const WCHAR* pDeviceID); - HRESULT (STDMETHODCALLTYPE * OnDeviceRemoved) (ma_IMMNotificationClient* pThis, const WCHAR* pDeviceID); - HRESULT (STDMETHODCALLTYPE * OnDefaultDeviceChanged)(ma_IMMNotificationClient* pThis, ma_EDataFlow dataFlow, ma_ERole role, const WCHAR* pDefaultDeviceID); - HRESULT (STDMETHODCALLTYPE * OnPropertyValueChanged)(ma_IMMNotificationClient* pThis, const WCHAR* pDeviceID, const PROPERTYKEY key); - } ma_IMMNotificationClientVtbl; - - /* IMMDeviceEnumerator */ - typedef struct - { - /* IUnknown */ - HRESULT (STDMETHODCALLTYPE * QueryInterface)(ma_IMMDeviceEnumerator* pThis, const IID* const riid, void** ppObject); - ULONG (STDMETHODCALLTYPE * AddRef) (ma_IMMDeviceEnumerator* pThis); - ULONG (STDMETHODCALLTYPE * Release) (ma_IMMDeviceEnumerator* pThis); - - /* IMMDeviceEnumerator */ - HRESULT (STDMETHODCALLTYPE * EnumAudioEndpoints) (ma_IMMDeviceEnumerator* pThis, ma_EDataFlow dataFlow, DWORD dwStateMask, ma_IMMDeviceCollection** ppDevices); - HRESULT (STDMETHODCALLTYPE * GetDefaultAudioEndpoint) (ma_IMMDeviceEnumerator* pThis, ma_EDataFlow dataFlow, ma_ERole role, ma_IMMDevice** ppEndpoint); - HRESULT (STDMETHODCALLTYPE * GetDevice) (ma_IMMDeviceEnumerator* pThis, const WCHAR* pID, ma_IMMDevice** ppDevice); - HRESULT (STDMETHODCALLTYPE * RegisterEndpointNotificationCallback) (ma_IMMDeviceEnumerator* pThis, ma_IMMNotificationClient* pClient); - HRESULT (STDMETHODCALLTYPE * UnregisterEndpointNotificationCallback)(ma_IMMDeviceEnumerator* pThis, ma_IMMNotificationClient* pClient); - } ma_IMMDeviceEnumeratorVtbl; - struct ma_IMMDeviceEnumerator - { - ma_IMMDeviceEnumeratorVtbl* lpVtbl; - }; - static MA_INLINE HRESULT ma_IMMDeviceEnumerator_QueryInterface(ma_IMMDeviceEnumerator* pThis, const IID* const riid, void** ppObject) { return pThis->lpVtbl->QueryInterface(pThis, riid, ppObject); } - static MA_INLINE ULONG ma_IMMDeviceEnumerator_AddRef(ma_IMMDeviceEnumerator* pThis) { return pThis->lpVtbl->AddRef(pThis); } - static MA_INLINE ULONG ma_IMMDeviceEnumerator_Release(ma_IMMDeviceEnumerator* pThis) { return pThis->lpVtbl->Release(pThis); } - static MA_INLINE HRESULT ma_IMMDeviceEnumerator_EnumAudioEndpoints(ma_IMMDeviceEnumerator* pThis, ma_EDataFlow dataFlow, DWORD dwStateMask, ma_IMMDeviceCollection** ppDevices) { return pThis->lpVtbl->EnumAudioEndpoints(pThis, dataFlow, dwStateMask, ppDevices); } - static MA_INLINE HRESULT ma_IMMDeviceEnumerator_GetDefaultAudioEndpoint(ma_IMMDeviceEnumerator* pThis, ma_EDataFlow dataFlow, ma_ERole role, ma_IMMDevice** ppEndpoint) { return pThis->lpVtbl->GetDefaultAudioEndpoint(pThis, dataFlow, role, ppEndpoint); } - static MA_INLINE HRESULT ma_IMMDeviceEnumerator_GetDevice(ma_IMMDeviceEnumerator* pThis, const WCHAR* pID, ma_IMMDevice** ppDevice) { return pThis->lpVtbl->GetDevice(pThis, pID, ppDevice); } - static MA_INLINE HRESULT ma_IMMDeviceEnumerator_RegisterEndpointNotificationCallback(ma_IMMDeviceEnumerator* pThis, ma_IMMNotificationClient* pClient) { return pThis->lpVtbl->RegisterEndpointNotificationCallback(pThis, pClient); } - static MA_INLINE HRESULT ma_IMMDeviceEnumerator_UnregisterEndpointNotificationCallback(ma_IMMDeviceEnumerator* pThis, ma_IMMNotificationClient* pClient) { return pThis->lpVtbl->UnregisterEndpointNotificationCallback(pThis, pClient); } - - - /* IMMDeviceCollection */ - typedef struct - { - /* IUnknown */ - HRESULT (STDMETHODCALLTYPE * QueryInterface)(ma_IMMDeviceCollection* pThis, const IID* const riid, void** ppObject); - ULONG (STDMETHODCALLTYPE * AddRef) (ma_IMMDeviceCollection* pThis); - ULONG (STDMETHODCALLTYPE * Release) (ma_IMMDeviceCollection* pThis); - - /* IMMDeviceCollection */ - HRESULT (STDMETHODCALLTYPE * GetCount)(ma_IMMDeviceCollection* pThis, UINT* pDevices); - HRESULT (STDMETHODCALLTYPE * Item) (ma_IMMDeviceCollection* pThis, UINT nDevice, ma_IMMDevice** ppDevice); - } ma_IMMDeviceCollectionVtbl; - struct ma_IMMDeviceCollection - { - ma_IMMDeviceCollectionVtbl* lpVtbl; - }; - static MA_INLINE HRESULT ma_IMMDeviceCollection_QueryInterface(ma_IMMDeviceCollection* pThis, const IID* const riid, void** ppObject) { return pThis->lpVtbl->QueryInterface(pThis, riid, ppObject); } - static MA_INLINE ULONG ma_IMMDeviceCollection_AddRef(ma_IMMDeviceCollection* pThis) { return pThis->lpVtbl->AddRef(pThis); } - static MA_INLINE ULONG ma_IMMDeviceCollection_Release(ma_IMMDeviceCollection* pThis) { return pThis->lpVtbl->Release(pThis); } - static MA_INLINE HRESULT ma_IMMDeviceCollection_GetCount(ma_IMMDeviceCollection* pThis, UINT* pDevices) { return pThis->lpVtbl->GetCount(pThis, pDevices); } - static MA_INLINE HRESULT ma_IMMDeviceCollection_Item(ma_IMMDeviceCollection* pThis, UINT nDevice, ma_IMMDevice** ppDevice) { return pThis->lpVtbl->Item(pThis, nDevice, ppDevice); } - - - /* IMMDevice */ - typedef struct - { - /* IUnknown */ - HRESULT (STDMETHODCALLTYPE * QueryInterface)(ma_IMMDevice* pThis, const IID* const riid, void** ppObject); - ULONG (STDMETHODCALLTYPE * AddRef) (ma_IMMDevice* pThis); - ULONG (STDMETHODCALLTYPE * Release) (ma_IMMDevice* pThis); - - /* IMMDevice */ - HRESULT (STDMETHODCALLTYPE * Activate) (ma_IMMDevice* pThis, const IID* const iid, DWORD dwClsCtx, MA_PROPVARIANT* pActivationParams, void** ppInterface); - HRESULT (STDMETHODCALLTYPE * OpenPropertyStore)(ma_IMMDevice* pThis, DWORD stgmAccess, ma_IPropertyStore** ppProperties); - HRESULT (STDMETHODCALLTYPE * GetId) (ma_IMMDevice* pThis, WCHAR** pID); - HRESULT (STDMETHODCALLTYPE * GetState) (ma_IMMDevice* pThis, DWORD *pState); - } ma_IMMDeviceVtbl; - struct ma_IMMDevice - { - ma_IMMDeviceVtbl* lpVtbl; - }; - static MA_INLINE HRESULT ma_IMMDevice_QueryInterface(ma_IMMDevice* pThis, const IID* const riid, void** ppObject) { return pThis->lpVtbl->QueryInterface(pThis, riid, ppObject); } - static MA_INLINE ULONG ma_IMMDevice_AddRef(ma_IMMDevice* pThis) { return pThis->lpVtbl->AddRef(pThis); } - static MA_INLINE ULONG ma_IMMDevice_Release(ma_IMMDevice* pThis) { return pThis->lpVtbl->Release(pThis); } - static MA_INLINE HRESULT ma_IMMDevice_Activate(ma_IMMDevice* pThis, const IID* const iid, DWORD dwClsCtx, MA_PROPVARIANT* pActivationParams, void** ppInterface) { return pThis->lpVtbl->Activate(pThis, iid, dwClsCtx, pActivationParams, ppInterface); } - static MA_INLINE HRESULT ma_IMMDevice_OpenPropertyStore(ma_IMMDevice* pThis, DWORD stgmAccess, ma_IPropertyStore** ppProperties) { return pThis->lpVtbl->OpenPropertyStore(pThis, stgmAccess, ppProperties); } - static MA_INLINE HRESULT ma_IMMDevice_GetId(ma_IMMDevice* pThis, WCHAR** pID) { return pThis->lpVtbl->GetId(pThis, pID); } - static MA_INLINE HRESULT ma_IMMDevice_GetState(ma_IMMDevice* pThis, DWORD *pState) { return pThis->lpVtbl->GetState(pThis, pState); } -#else - /* IActivateAudioInterfaceAsyncOperation */ - typedef struct - { - /* IUnknown */ - HRESULT (STDMETHODCALLTYPE * QueryInterface)(ma_IActivateAudioInterfaceAsyncOperation* pThis, const IID* const riid, void** ppObject); - ULONG (STDMETHODCALLTYPE * AddRef) (ma_IActivateAudioInterfaceAsyncOperation* pThis); - ULONG (STDMETHODCALLTYPE * Release) (ma_IActivateAudioInterfaceAsyncOperation* pThis); - - /* IActivateAudioInterfaceAsyncOperation */ - HRESULT (STDMETHODCALLTYPE * GetActivateResult)(ma_IActivateAudioInterfaceAsyncOperation* pThis, HRESULT *pActivateResult, ma_IUnknown** ppActivatedInterface); - } ma_IActivateAudioInterfaceAsyncOperationVtbl; - struct ma_IActivateAudioInterfaceAsyncOperation - { - ma_IActivateAudioInterfaceAsyncOperationVtbl* lpVtbl; - }; - static MA_INLINE HRESULT ma_IActivateAudioInterfaceAsyncOperation_QueryInterface(ma_IActivateAudioInterfaceAsyncOperation* pThis, const IID* const riid, void** ppObject) { return pThis->lpVtbl->QueryInterface(pThis, riid, ppObject); } - static MA_INLINE ULONG ma_IActivateAudioInterfaceAsyncOperation_AddRef(ma_IActivateAudioInterfaceAsyncOperation* pThis) { return pThis->lpVtbl->AddRef(pThis); } - static MA_INLINE ULONG ma_IActivateAudioInterfaceAsyncOperation_Release(ma_IActivateAudioInterfaceAsyncOperation* pThis) { return pThis->lpVtbl->Release(pThis); } - static MA_INLINE HRESULT ma_IActivateAudioInterfaceAsyncOperation_GetActivateResult(ma_IActivateAudioInterfaceAsyncOperation* pThis, HRESULT *pActivateResult, ma_IUnknown** ppActivatedInterface) { return pThis->lpVtbl->GetActivateResult(pThis, pActivateResult, ppActivatedInterface); } -#endif - -/* IPropertyStore */ -typedef struct -{ - /* IUnknown */ - HRESULT (STDMETHODCALLTYPE * QueryInterface)(ma_IPropertyStore* pThis, const IID* const riid, void** ppObject); - ULONG (STDMETHODCALLTYPE * AddRef) (ma_IPropertyStore* pThis); - ULONG (STDMETHODCALLTYPE * Release) (ma_IPropertyStore* pThis); - - /* IPropertyStore */ - HRESULT (STDMETHODCALLTYPE * GetCount)(ma_IPropertyStore* pThis, DWORD* pPropCount); - HRESULT (STDMETHODCALLTYPE * GetAt) (ma_IPropertyStore* pThis, DWORD propIndex, PROPERTYKEY* pPropKey); - HRESULT (STDMETHODCALLTYPE * GetValue)(ma_IPropertyStore* pThis, const PROPERTYKEY* const pKey, MA_PROPVARIANT* pPropVar); - HRESULT (STDMETHODCALLTYPE * SetValue)(ma_IPropertyStore* pThis, const PROPERTYKEY* const pKey, const MA_PROPVARIANT* const pPropVar); - HRESULT (STDMETHODCALLTYPE * Commit) (ma_IPropertyStore* pThis); -} ma_IPropertyStoreVtbl; -struct ma_IPropertyStore -{ - ma_IPropertyStoreVtbl* lpVtbl; -}; -static MA_INLINE HRESULT ma_IPropertyStore_QueryInterface(ma_IPropertyStore* pThis, const IID* const riid, void** ppObject) { return pThis->lpVtbl->QueryInterface(pThis, riid, ppObject); } -static MA_INLINE ULONG ma_IPropertyStore_AddRef(ma_IPropertyStore* pThis) { return pThis->lpVtbl->AddRef(pThis); } -static MA_INLINE ULONG ma_IPropertyStore_Release(ma_IPropertyStore* pThis) { return pThis->lpVtbl->Release(pThis); } -static MA_INLINE HRESULT ma_IPropertyStore_GetCount(ma_IPropertyStore* pThis, DWORD* pPropCount) { return pThis->lpVtbl->GetCount(pThis, pPropCount); } -static MA_INLINE HRESULT ma_IPropertyStore_GetAt(ma_IPropertyStore* pThis, DWORD propIndex, PROPERTYKEY* pPropKey) { return pThis->lpVtbl->GetAt(pThis, propIndex, pPropKey); } -static MA_INLINE HRESULT ma_IPropertyStore_GetValue(ma_IPropertyStore* pThis, const PROPERTYKEY* const pKey, MA_PROPVARIANT* pPropVar) { return pThis->lpVtbl->GetValue(pThis, pKey, pPropVar); } -static MA_INLINE HRESULT ma_IPropertyStore_SetValue(ma_IPropertyStore* pThis, const PROPERTYKEY* const pKey, const MA_PROPVARIANT* const pPropVar) { return pThis->lpVtbl->SetValue(pThis, pKey, pPropVar); } -static MA_INLINE HRESULT ma_IPropertyStore_Commit(ma_IPropertyStore* pThis) { return pThis->lpVtbl->Commit(pThis); } - - -/* IAudioClient */ -typedef struct -{ - /* IUnknown */ - HRESULT (STDMETHODCALLTYPE * QueryInterface)(ma_IAudioClient* pThis, const IID* const riid, void** ppObject); - ULONG (STDMETHODCALLTYPE * AddRef) (ma_IAudioClient* pThis); - ULONG (STDMETHODCALLTYPE * Release) (ma_IAudioClient* pThis); - - /* IAudioClient */ - HRESULT (STDMETHODCALLTYPE * Initialize) (ma_IAudioClient* pThis, MA_AUDCLNT_SHAREMODE shareMode, DWORD streamFlags, MA_REFERENCE_TIME bufferDuration, MA_REFERENCE_TIME periodicity, const MA_WAVEFORMATEX* pFormat, const GUID* pAudioSessionGuid); - HRESULT (STDMETHODCALLTYPE * GetBufferSize) (ma_IAudioClient* pThis, ma_uint32* pNumBufferFrames); - HRESULT (STDMETHODCALLTYPE * GetStreamLatency) (ma_IAudioClient* pThis, MA_REFERENCE_TIME* pLatency); - HRESULT (STDMETHODCALLTYPE * GetCurrentPadding)(ma_IAudioClient* pThis, ma_uint32* pNumPaddingFrames); - HRESULT (STDMETHODCALLTYPE * IsFormatSupported)(ma_IAudioClient* pThis, MA_AUDCLNT_SHAREMODE shareMode, const MA_WAVEFORMATEX* pFormat, MA_WAVEFORMATEX** ppClosestMatch); - HRESULT (STDMETHODCALLTYPE * GetMixFormat) (ma_IAudioClient* pThis, MA_WAVEFORMATEX** ppDeviceFormat); - HRESULT (STDMETHODCALLTYPE * GetDevicePeriod) (ma_IAudioClient* pThis, MA_REFERENCE_TIME* pDefaultDevicePeriod, MA_REFERENCE_TIME* pMinimumDevicePeriod); - HRESULT (STDMETHODCALLTYPE * Start) (ma_IAudioClient* pThis); - HRESULT (STDMETHODCALLTYPE * Stop) (ma_IAudioClient* pThis); - HRESULT (STDMETHODCALLTYPE * Reset) (ma_IAudioClient* pThis); - HRESULT (STDMETHODCALLTYPE * SetEventHandle) (ma_IAudioClient* pThis, HANDLE eventHandle); - HRESULT (STDMETHODCALLTYPE * GetService) (ma_IAudioClient* pThis, const IID* const riid, void** pp); -} ma_IAudioClientVtbl; -struct ma_IAudioClient -{ - ma_IAudioClientVtbl* lpVtbl; -}; -static MA_INLINE HRESULT ma_IAudioClient_QueryInterface(ma_IAudioClient* pThis, const IID* const riid, void** ppObject) { return pThis->lpVtbl->QueryInterface(pThis, riid, ppObject); } -static MA_INLINE ULONG ma_IAudioClient_AddRef(ma_IAudioClient* pThis) { return pThis->lpVtbl->AddRef(pThis); } -static MA_INLINE ULONG ma_IAudioClient_Release(ma_IAudioClient* pThis) { return pThis->lpVtbl->Release(pThis); } -static MA_INLINE HRESULT ma_IAudioClient_Initialize(ma_IAudioClient* pThis, MA_AUDCLNT_SHAREMODE shareMode, DWORD streamFlags, MA_REFERENCE_TIME bufferDuration, MA_REFERENCE_TIME periodicity, const MA_WAVEFORMATEX* pFormat, const GUID* pAudioSessionGuid) { return pThis->lpVtbl->Initialize(pThis, shareMode, streamFlags, bufferDuration, periodicity, pFormat, pAudioSessionGuid); } -static MA_INLINE HRESULT ma_IAudioClient_GetBufferSize(ma_IAudioClient* pThis, ma_uint32* pNumBufferFrames) { return pThis->lpVtbl->GetBufferSize(pThis, pNumBufferFrames); } -static MA_INLINE HRESULT ma_IAudioClient_GetStreamLatency(ma_IAudioClient* pThis, MA_REFERENCE_TIME* pLatency) { return pThis->lpVtbl->GetStreamLatency(pThis, pLatency); } -static MA_INLINE HRESULT ma_IAudioClient_GetCurrentPadding(ma_IAudioClient* pThis, ma_uint32* pNumPaddingFrames) { return pThis->lpVtbl->GetCurrentPadding(pThis, pNumPaddingFrames); } -static MA_INLINE HRESULT ma_IAudioClient_IsFormatSupported(ma_IAudioClient* pThis, MA_AUDCLNT_SHAREMODE shareMode, const MA_WAVEFORMATEX* pFormat, MA_WAVEFORMATEX** ppClosestMatch) { return pThis->lpVtbl->IsFormatSupported(pThis, shareMode, pFormat, ppClosestMatch); } -static MA_INLINE HRESULT ma_IAudioClient_GetMixFormat(ma_IAudioClient* pThis, MA_WAVEFORMATEX** ppDeviceFormat) { return pThis->lpVtbl->GetMixFormat(pThis, ppDeviceFormat); } -static MA_INLINE HRESULT ma_IAudioClient_GetDevicePeriod(ma_IAudioClient* pThis, MA_REFERENCE_TIME* pDefaultDevicePeriod, MA_REFERENCE_TIME* pMinimumDevicePeriod) { return pThis->lpVtbl->GetDevicePeriod(pThis, pDefaultDevicePeriod, pMinimumDevicePeriod); } -static MA_INLINE HRESULT ma_IAudioClient_Start(ma_IAudioClient* pThis) { return pThis->lpVtbl->Start(pThis); } -static MA_INLINE HRESULT ma_IAudioClient_Stop(ma_IAudioClient* pThis) { return pThis->lpVtbl->Stop(pThis); } -static MA_INLINE HRESULT ma_IAudioClient_Reset(ma_IAudioClient* pThis) { return pThis->lpVtbl->Reset(pThis); } -static MA_INLINE HRESULT ma_IAudioClient_SetEventHandle(ma_IAudioClient* pThis, HANDLE eventHandle) { return pThis->lpVtbl->SetEventHandle(pThis, eventHandle); } -static MA_INLINE HRESULT ma_IAudioClient_GetService(ma_IAudioClient* pThis, const IID* const riid, void** pp) { return pThis->lpVtbl->GetService(pThis, riid, pp); } - -/* IAudioClient2 */ -typedef struct -{ - /* IUnknown */ - HRESULT (STDMETHODCALLTYPE * QueryInterface)(ma_IAudioClient2* pThis, const IID* const riid, void** ppObject); - ULONG (STDMETHODCALLTYPE * AddRef) (ma_IAudioClient2* pThis); - ULONG (STDMETHODCALLTYPE * Release) (ma_IAudioClient2* pThis); - - /* IAudioClient */ - HRESULT (STDMETHODCALLTYPE * Initialize) (ma_IAudioClient2* pThis, MA_AUDCLNT_SHAREMODE shareMode, DWORD streamFlags, MA_REFERENCE_TIME bufferDuration, MA_REFERENCE_TIME periodicity, const MA_WAVEFORMATEX* pFormat, const GUID* pAudioSessionGuid); - HRESULT (STDMETHODCALLTYPE * GetBufferSize) (ma_IAudioClient2* pThis, ma_uint32* pNumBufferFrames); - HRESULT (STDMETHODCALLTYPE * GetStreamLatency) (ma_IAudioClient2* pThis, MA_REFERENCE_TIME* pLatency); - HRESULT (STDMETHODCALLTYPE * GetCurrentPadding)(ma_IAudioClient2* pThis, ma_uint32* pNumPaddingFrames); - HRESULT (STDMETHODCALLTYPE * IsFormatSupported)(ma_IAudioClient2* pThis, MA_AUDCLNT_SHAREMODE shareMode, const MA_WAVEFORMATEX* pFormat, MA_WAVEFORMATEX** ppClosestMatch); - HRESULT (STDMETHODCALLTYPE * GetMixFormat) (ma_IAudioClient2* pThis, MA_WAVEFORMATEX** ppDeviceFormat); - HRESULT (STDMETHODCALLTYPE * GetDevicePeriod) (ma_IAudioClient2* pThis, MA_REFERENCE_TIME* pDefaultDevicePeriod, MA_REFERENCE_TIME* pMinimumDevicePeriod); - HRESULT (STDMETHODCALLTYPE * Start) (ma_IAudioClient2* pThis); - HRESULT (STDMETHODCALLTYPE * Stop) (ma_IAudioClient2* pThis); - HRESULT (STDMETHODCALLTYPE * Reset) (ma_IAudioClient2* pThis); - HRESULT (STDMETHODCALLTYPE * SetEventHandle) (ma_IAudioClient2* pThis, HANDLE eventHandle); - HRESULT (STDMETHODCALLTYPE * GetService) (ma_IAudioClient2* pThis, const IID* const riid, void** pp); - - /* IAudioClient2 */ - HRESULT (STDMETHODCALLTYPE * IsOffloadCapable) (ma_IAudioClient2* pThis, MA_AUDIO_STREAM_CATEGORY category, BOOL* pOffloadCapable); - HRESULT (STDMETHODCALLTYPE * SetClientProperties)(ma_IAudioClient2* pThis, const ma_AudioClientProperties* pProperties); - HRESULT (STDMETHODCALLTYPE * GetBufferSizeLimits)(ma_IAudioClient2* pThis, const MA_WAVEFORMATEX* pFormat, BOOL eventDriven, MA_REFERENCE_TIME* pMinBufferDuration, MA_REFERENCE_TIME* pMaxBufferDuration); -} ma_IAudioClient2Vtbl; -struct ma_IAudioClient2 -{ - ma_IAudioClient2Vtbl* lpVtbl; -}; -static MA_INLINE HRESULT ma_IAudioClient2_QueryInterface(ma_IAudioClient2* pThis, const IID* const riid, void** ppObject) { return pThis->lpVtbl->QueryInterface(pThis, riid, ppObject); } -static MA_INLINE ULONG ma_IAudioClient2_AddRef(ma_IAudioClient2* pThis) { return pThis->lpVtbl->AddRef(pThis); } -static MA_INLINE ULONG ma_IAudioClient2_Release(ma_IAudioClient2* pThis) { return pThis->lpVtbl->Release(pThis); } -static MA_INLINE HRESULT ma_IAudioClient2_Initialize(ma_IAudioClient2* pThis, MA_AUDCLNT_SHAREMODE shareMode, DWORD streamFlags, MA_REFERENCE_TIME bufferDuration, MA_REFERENCE_TIME periodicity, const MA_WAVEFORMATEX* pFormat, const GUID* pAudioSessionGuid) { return pThis->lpVtbl->Initialize(pThis, shareMode, streamFlags, bufferDuration, periodicity, pFormat, pAudioSessionGuid); } -static MA_INLINE HRESULT ma_IAudioClient2_GetBufferSize(ma_IAudioClient2* pThis, ma_uint32* pNumBufferFrames) { return pThis->lpVtbl->GetBufferSize(pThis, pNumBufferFrames); } -static MA_INLINE HRESULT ma_IAudioClient2_GetStreamLatency(ma_IAudioClient2* pThis, MA_REFERENCE_TIME* pLatency) { return pThis->lpVtbl->GetStreamLatency(pThis, pLatency); } -static MA_INLINE HRESULT ma_IAudioClient2_GetCurrentPadding(ma_IAudioClient2* pThis, ma_uint32* pNumPaddingFrames) { return pThis->lpVtbl->GetCurrentPadding(pThis, pNumPaddingFrames); } -static MA_INLINE HRESULT ma_IAudioClient2_IsFormatSupported(ma_IAudioClient2* pThis, MA_AUDCLNT_SHAREMODE shareMode, const MA_WAVEFORMATEX* pFormat, MA_WAVEFORMATEX** ppClosestMatch) { return pThis->lpVtbl->IsFormatSupported(pThis, shareMode, pFormat, ppClosestMatch); } -static MA_INLINE HRESULT ma_IAudioClient2_GetMixFormat(ma_IAudioClient2* pThis, MA_WAVEFORMATEX** ppDeviceFormat) { return pThis->lpVtbl->GetMixFormat(pThis, ppDeviceFormat); } -static MA_INLINE HRESULT ma_IAudioClient2_GetDevicePeriod(ma_IAudioClient2* pThis, MA_REFERENCE_TIME* pDefaultDevicePeriod, MA_REFERENCE_TIME* pMinimumDevicePeriod) { return pThis->lpVtbl->GetDevicePeriod(pThis, pDefaultDevicePeriod, pMinimumDevicePeriod); } -static MA_INLINE HRESULT ma_IAudioClient2_Start(ma_IAudioClient2* pThis) { return pThis->lpVtbl->Start(pThis); } -static MA_INLINE HRESULT ma_IAudioClient2_Stop(ma_IAudioClient2* pThis) { return pThis->lpVtbl->Stop(pThis); } -static MA_INLINE HRESULT ma_IAudioClient2_Reset(ma_IAudioClient2* pThis) { return pThis->lpVtbl->Reset(pThis); } -static MA_INLINE HRESULT ma_IAudioClient2_SetEventHandle(ma_IAudioClient2* pThis, HANDLE eventHandle) { return pThis->lpVtbl->SetEventHandle(pThis, eventHandle); } -static MA_INLINE HRESULT ma_IAudioClient2_GetService(ma_IAudioClient2* pThis, const IID* const riid, void** pp) { return pThis->lpVtbl->GetService(pThis, riid, pp); } -static MA_INLINE HRESULT ma_IAudioClient2_IsOffloadCapable(ma_IAudioClient2* pThis, MA_AUDIO_STREAM_CATEGORY category, BOOL* pOffloadCapable) { return pThis->lpVtbl->IsOffloadCapable(pThis, category, pOffloadCapable); } -static MA_INLINE HRESULT ma_IAudioClient2_SetClientProperties(ma_IAudioClient2* pThis, const ma_AudioClientProperties* pProperties) { return pThis->lpVtbl->SetClientProperties(pThis, pProperties); } -static MA_INLINE HRESULT ma_IAudioClient2_GetBufferSizeLimits(ma_IAudioClient2* pThis, const MA_WAVEFORMATEX* pFormat, BOOL eventDriven, MA_REFERENCE_TIME* pMinBufferDuration, MA_REFERENCE_TIME* pMaxBufferDuration) { return pThis->lpVtbl->GetBufferSizeLimits(pThis, pFormat, eventDriven, pMinBufferDuration, pMaxBufferDuration); } - - -/* IAudioClient3 */ -typedef struct -{ - /* IUnknown */ - HRESULT (STDMETHODCALLTYPE * QueryInterface)(ma_IAudioClient3* pThis, const IID* const riid, void** ppObject); - ULONG (STDMETHODCALLTYPE * AddRef) (ma_IAudioClient3* pThis); - ULONG (STDMETHODCALLTYPE * Release) (ma_IAudioClient3* pThis); - - /* IAudioClient */ - HRESULT (STDMETHODCALLTYPE * Initialize) (ma_IAudioClient3* pThis, MA_AUDCLNT_SHAREMODE shareMode, DWORD streamFlags, MA_REFERENCE_TIME bufferDuration, MA_REFERENCE_TIME periodicity, const MA_WAVEFORMATEX* pFormat, const GUID* pAudioSessionGuid); - HRESULT (STDMETHODCALLTYPE * GetBufferSize) (ma_IAudioClient3* pThis, ma_uint32* pNumBufferFrames); - HRESULT (STDMETHODCALLTYPE * GetStreamLatency) (ma_IAudioClient3* pThis, MA_REFERENCE_TIME* pLatency); - HRESULT (STDMETHODCALLTYPE * GetCurrentPadding)(ma_IAudioClient3* pThis, ma_uint32* pNumPaddingFrames); - HRESULT (STDMETHODCALLTYPE * IsFormatSupported)(ma_IAudioClient3* pThis, MA_AUDCLNT_SHAREMODE shareMode, const MA_WAVEFORMATEX* pFormat, MA_WAVEFORMATEX** ppClosestMatch); - HRESULT (STDMETHODCALLTYPE * GetMixFormat) (ma_IAudioClient3* pThis, MA_WAVEFORMATEX** ppDeviceFormat); - HRESULT (STDMETHODCALLTYPE * GetDevicePeriod) (ma_IAudioClient3* pThis, MA_REFERENCE_TIME* pDefaultDevicePeriod, MA_REFERENCE_TIME* pMinimumDevicePeriod); - HRESULT (STDMETHODCALLTYPE * Start) (ma_IAudioClient3* pThis); - HRESULT (STDMETHODCALLTYPE * Stop) (ma_IAudioClient3* pThis); - HRESULT (STDMETHODCALLTYPE * Reset) (ma_IAudioClient3* pThis); - HRESULT (STDMETHODCALLTYPE * SetEventHandle) (ma_IAudioClient3* pThis, HANDLE eventHandle); - HRESULT (STDMETHODCALLTYPE * GetService) (ma_IAudioClient3* pThis, const IID* const riid, void** pp); - - /* IAudioClient2 */ - HRESULT (STDMETHODCALLTYPE * IsOffloadCapable) (ma_IAudioClient3* pThis, MA_AUDIO_STREAM_CATEGORY category, BOOL* pOffloadCapable); - HRESULT (STDMETHODCALLTYPE * SetClientProperties)(ma_IAudioClient3* pThis, const ma_AudioClientProperties* pProperties); - HRESULT (STDMETHODCALLTYPE * GetBufferSizeLimits)(ma_IAudioClient3* pThis, const MA_WAVEFORMATEX* pFormat, BOOL eventDriven, MA_REFERENCE_TIME* pMinBufferDuration, MA_REFERENCE_TIME* pMaxBufferDuration); - - /* IAudioClient3 */ - HRESULT (STDMETHODCALLTYPE * GetSharedModeEnginePeriod) (ma_IAudioClient3* pThis, const MA_WAVEFORMATEX* pFormat, ma_uint32* pDefaultPeriodInFrames, ma_uint32* pFundamentalPeriodInFrames, ma_uint32* pMinPeriodInFrames, ma_uint32* pMaxPeriodInFrames); - HRESULT (STDMETHODCALLTYPE * GetCurrentSharedModeEnginePeriod)(ma_IAudioClient3* pThis, MA_WAVEFORMATEX** ppFormat, ma_uint32* pCurrentPeriodInFrames); - HRESULT (STDMETHODCALLTYPE * InitializeSharedAudioStream) (ma_IAudioClient3* pThis, DWORD streamFlags, ma_uint32 periodInFrames, const MA_WAVEFORMATEX* pFormat, const GUID* pAudioSessionGuid); -} ma_IAudioClient3Vtbl; -struct ma_IAudioClient3 -{ - ma_IAudioClient3Vtbl* lpVtbl; -}; -static MA_INLINE HRESULT ma_IAudioClient3_QueryInterface(ma_IAudioClient3* pThis, const IID* const riid, void** ppObject) { return pThis->lpVtbl->QueryInterface(pThis, riid, ppObject); } -static MA_INLINE ULONG ma_IAudioClient3_AddRef(ma_IAudioClient3* pThis) { return pThis->lpVtbl->AddRef(pThis); } -static MA_INLINE ULONG ma_IAudioClient3_Release(ma_IAudioClient3* pThis) { return pThis->lpVtbl->Release(pThis); } -static MA_INLINE HRESULT ma_IAudioClient3_Initialize(ma_IAudioClient3* pThis, MA_AUDCLNT_SHAREMODE shareMode, DWORD streamFlags, MA_REFERENCE_TIME bufferDuration, MA_REFERENCE_TIME periodicity, const MA_WAVEFORMATEX* pFormat, const GUID* pAudioSessionGuid) { return pThis->lpVtbl->Initialize(pThis, shareMode, streamFlags, bufferDuration, periodicity, pFormat, pAudioSessionGuid); } -static MA_INLINE HRESULT ma_IAudioClient3_GetBufferSize(ma_IAudioClient3* pThis, ma_uint32* pNumBufferFrames) { return pThis->lpVtbl->GetBufferSize(pThis, pNumBufferFrames); } -static MA_INLINE HRESULT ma_IAudioClient3_GetStreamLatency(ma_IAudioClient3* pThis, MA_REFERENCE_TIME* pLatency) { return pThis->lpVtbl->GetStreamLatency(pThis, pLatency); } -static MA_INLINE HRESULT ma_IAudioClient3_GetCurrentPadding(ma_IAudioClient3* pThis, ma_uint32* pNumPaddingFrames) { return pThis->lpVtbl->GetCurrentPadding(pThis, pNumPaddingFrames); } -static MA_INLINE HRESULT ma_IAudioClient3_IsFormatSupported(ma_IAudioClient3* pThis, MA_AUDCLNT_SHAREMODE shareMode, const MA_WAVEFORMATEX* pFormat, MA_WAVEFORMATEX** ppClosestMatch) { return pThis->lpVtbl->IsFormatSupported(pThis, shareMode, pFormat, ppClosestMatch); } -static MA_INLINE HRESULT ma_IAudioClient3_GetMixFormat(ma_IAudioClient3* pThis, MA_WAVEFORMATEX** ppDeviceFormat) { return pThis->lpVtbl->GetMixFormat(pThis, ppDeviceFormat); } -static MA_INLINE HRESULT ma_IAudioClient3_GetDevicePeriod(ma_IAudioClient3* pThis, MA_REFERENCE_TIME* pDefaultDevicePeriod, MA_REFERENCE_TIME* pMinimumDevicePeriod) { return pThis->lpVtbl->GetDevicePeriod(pThis, pDefaultDevicePeriod, pMinimumDevicePeriod); } -static MA_INLINE HRESULT ma_IAudioClient3_Start(ma_IAudioClient3* pThis) { return pThis->lpVtbl->Start(pThis); } -static MA_INLINE HRESULT ma_IAudioClient3_Stop(ma_IAudioClient3* pThis) { return pThis->lpVtbl->Stop(pThis); } -static MA_INLINE HRESULT ma_IAudioClient3_Reset(ma_IAudioClient3* pThis) { return pThis->lpVtbl->Reset(pThis); } -static MA_INLINE HRESULT ma_IAudioClient3_SetEventHandle(ma_IAudioClient3* pThis, HANDLE eventHandle) { return pThis->lpVtbl->SetEventHandle(pThis, eventHandle); } -static MA_INLINE HRESULT ma_IAudioClient3_GetService(ma_IAudioClient3* pThis, const IID* const riid, void** pp) { return pThis->lpVtbl->GetService(pThis, riid, pp); } -static MA_INLINE HRESULT ma_IAudioClient3_IsOffloadCapable(ma_IAudioClient3* pThis, MA_AUDIO_STREAM_CATEGORY category, BOOL* pOffloadCapable) { return pThis->lpVtbl->IsOffloadCapable(pThis, category, pOffloadCapable); } -static MA_INLINE HRESULT ma_IAudioClient3_SetClientProperties(ma_IAudioClient3* pThis, const ma_AudioClientProperties* pProperties) { return pThis->lpVtbl->SetClientProperties(pThis, pProperties); } -static MA_INLINE HRESULT ma_IAudioClient3_GetBufferSizeLimits(ma_IAudioClient3* pThis, const MA_WAVEFORMATEX* pFormat, BOOL eventDriven, MA_REFERENCE_TIME* pMinBufferDuration, MA_REFERENCE_TIME* pMaxBufferDuration) { return pThis->lpVtbl->GetBufferSizeLimits(pThis, pFormat, eventDriven, pMinBufferDuration, pMaxBufferDuration); } -static MA_INLINE HRESULT ma_IAudioClient3_GetSharedModeEnginePeriod(ma_IAudioClient3* pThis, const MA_WAVEFORMATEX* pFormat, ma_uint32* pDefaultPeriodInFrames, ma_uint32* pFundamentalPeriodInFrames, ma_uint32* pMinPeriodInFrames, ma_uint32* pMaxPeriodInFrames) { return pThis->lpVtbl->GetSharedModeEnginePeriod(pThis, pFormat, pDefaultPeriodInFrames, pFundamentalPeriodInFrames, pMinPeriodInFrames, pMaxPeriodInFrames); } -static MA_INLINE HRESULT ma_IAudioClient3_GetCurrentSharedModeEnginePeriod(ma_IAudioClient3* pThis, MA_WAVEFORMATEX** ppFormat, ma_uint32* pCurrentPeriodInFrames) { return pThis->lpVtbl->GetCurrentSharedModeEnginePeriod(pThis, ppFormat, pCurrentPeriodInFrames); } -static MA_INLINE HRESULT ma_IAudioClient3_InitializeSharedAudioStream(ma_IAudioClient3* pThis, DWORD streamFlags, ma_uint32 periodInFrames, const MA_WAVEFORMATEX* pFormat, const GUID* pAudioSessionGUID) { return pThis->lpVtbl->InitializeSharedAudioStream(pThis, streamFlags, periodInFrames, pFormat, pAudioSessionGUID); } - - -/* IAudioRenderClient */ -typedef struct -{ - /* IUnknown */ - HRESULT (STDMETHODCALLTYPE * QueryInterface)(ma_IAudioRenderClient* pThis, const IID* const riid, void** ppObject); - ULONG (STDMETHODCALLTYPE * AddRef) (ma_IAudioRenderClient* pThis); - ULONG (STDMETHODCALLTYPE * Release) (ma_IAudioRenderClient* pThis); - - /* IAudioRenderClient */ - HRESULT (STDMETHODCALLTYPE * GetBuffer) (ma_IAudioRenderClient* pThis, ma_uint32 numFramesRequested, BYTE** ppData); - HRESULT (STDMETHODCALLTYPE * ReleaseBuffer)(ma_IAudioRenderClient* pThis, ma_uint32 numFramesWritten, DWORD dwFlags); -} ma_IAudioRenderClientVtbl; -struct ma_IAudioRenderClient -{ - ma_IAudioRenderClientVtbl* lpVtbl; -}; -static MA_INLINE HRESULT ma_IAudioRenderClient_QueryInterface(ma_IAudioRenderClient* pThis, const IID* const riid, void** ppObject) { return pThis->lpVtbl->QueryInterface(pThis, riid, ppObject); } -static MA_INLINE ULONG ma_IAudioRenderClient_AddRef(ma_IAudioRenderClient* pThis) { return pThis->lpVtbl->AddRef(pThis); } -static MA_INLINE ULONG ma_IAudioRenderClient_Release(ma_IAudioRenderClient* pThis) { return pThis->lpVtbl->Release(pThis); } -static MA_INLINE HRESULT ma_IAudioRenderClient_GetBuffer(ma_IAudioRenderClient* pThis, ma_uint32 numFramesRequested, BYTE** ppData) { return pThis->lpVtbl->GetBuffer(pThis, numFramesRequested, ppData); } -static MA_INLINE HRESULT ma_IAudioRenderClient_ReleaseBuffer(ma_IAudioRenderClient* pThis, ma_uint32 numFramesWritten, DWORD dwFlags) { return pThis->lpVtbl->ReleaseBuffer(pThis, numFramesWritten, dwFlags); } - - -/* IAudioCaptureClient */ -typedef struct -{ - /* IUnknown */ - HRESULT (STDMETHODCALLTYPE * QueryInterface)(ma_IAudioCaptureClient* pThis, const IID* const riid, void** ppObject); - ULONG (STDMETHODCALLTYPE * AddRef) (ma_IAudioCaptureClient* pThis); - ULONG (STDMETHODCALLTYPE * Release) (ma_IAudioCaptureClient* pThis); - - /* IAudioRenderClient */ - HRESULT (STDMETHODCALLTYPE * GetBuffer) (ma_IAudioCaptureClient* pThis, BYTE** ppData, ma_uint32* pNumFramesToRead, DWORD* pFlags, ma_uint64* pDevicePosition, ma_uint64* pQPCPosition); - HRESULT (STDMETHODCALLTYPE * ReleaseBuffer) (ma_IAudioCaptureClient* pThis, ma_uint32 numFramesRead); - HRESULT (STDMETHODCALLTYPE * GetNextPacketSize)(ma_IAudioCaptureClient* pThis, ma_uint32* pNumFramesInNextPacket); -} ma_IAudioCaptureClientVtbl; -struct ma_IAudioCaptureClient -{ - ma_IAudioCaptureClientVtbl* lpVtbl; -}; -static MA_INLINE HRESULT ma_IAudioCaptureClient_QueryInterface(ma_IAudioCaptureClient* pThis, const IID* const riid, void** ppObject) { return pThis->lpVtbl->QueryInterface(pThis, riid, ppObject); } -static MA_INLINE ULONG ma_IAudioCaptureClient_AddRef(ma_IAudioCaptureClient* pThis) { return pThis->lpVtbl->AddRef(pThis); } -static MA_INLINE ULONG ma_IAudioCaptureClient_Release(ma_IAudioCaptureClient* pThis) { return pThis->lpVtbl->Release(pThis); } -static MA_INLINE HRESULT ma_IAudioCaptureClient_GetBuffer(ma_IAudioCaptureClient* pThis, BYTE** ppData, ma_uint32* pNumFramesToRead, DWORD* pFlags, ma_uint64* pDevicePosition, ma_uint64* pQPCPosition) { return pThis->lpVtbl->GetBuffer(pThis, ppData, pNumFramesToRead, pFlags, pDevicePosition, pQPCPosition); } -static MA_INLINE HRESULT ma_IAudioCaptureClient_ReleaseBuffer(ma_IAudioCaptureClient* pThis, ma_uint32 numFramesRead) { return pThis->lpVtbl->ReleaseBuffer(pThis, numFramesRead); } -static MA_INLINE HRESULT ma_IAudioCaptureClient_GetNextPacketSize(ma_IAudioCaptureClient* pThis, ma_uint32* pNumFramesInNextPacket) { return pThis->lpVtbl->GetNextPacketSize(pThis, pNumFramesInNextPacket); } - -#if defined(MA_WIN32_UWP) -/* mmdevapi Functions */ -typedef HRESULT (WINAPI * MA_PFN_ActivateAudioInterfaceAsync)(const wchar_t* deviceInterfacePath, const IID* riid, MA_PROPVARIANT* activationParams, ma_IActivateAudioInterfaceCompletionHandler* completionHandler, ma_IActivateAudioInterfaceAsyncOperation** activationOperation); -#endif - -/* Avrt Functions */ -typedef HANDLE (WINAPI * MA_PFN_AvSetMmThreadCharacteristicsA)(const char* TaskName, DWORD* TaskIndex); -typedef BOOL (WINAPI * MA_PFN_AvRevertMmThreadCharacteristics)(HANDLE AvrtHandle); - -#if !defined(MA_WIN32_DESKTOP) && !defined(MA_WIN32_GDK) -typedef struct ma_completion_handler_uwp ma_completion_handler_uwp; - -typedef struct -{ - /* IUnknown */ - HRESULT (STDMETHODCALLTYPE * QueryInterface)(ma_completion_handler_uwp* pThis, const IID* const riid, void** ppObject); - ULONG (STDMETHODCALLTYPE * AddRef) (ma_completion_handler_uwp* pThis); - ULONG (STDMETHODCALLTYPE * Release) (ma_completion_handler_uwp* pThis); - - /* IActivateAudioInterfaceCompletionHandler */ - HRESULT (STDMETHODCALLTYPE * ActivateCompleted)(ma_completion_handler_uwp* pThis, ma_IActivateAudioInterfaceAsyncOperation* pActivateOperation); -} ma_completion_handler_uwp_vtbl; -struct ma_completion_handler_uwp -{ - ma_completion_handler_uwp_vtbl* lpVtbl; - MA_ATOMIC(4, ma_uint32) counter; - HANDLE hEvent; -}; - -static HRESULT STDMETHODCALLTYPE ma_completion_handler_uwp_QueryInterface(ma_completion_handler_uwp* pThis, const IID* const riid, void** ppObject) -{ - /* - We need to "implement" IAgileObject which is just an indicator that's used internally by WASAPI for some multithreading management. To - "implement" this, we just make sure we return pThis when the IAgileObject is requested. - */ - if (!ma_is_guid_equal(riid, &MA_IID_IUnknown) && !ma_is_guid_equal(riid, &MA_IID_IActivateAudioInterfaceCompletionHandler) && !ma_is_guid_equal(riid, &MA_IID_IAgileObject)) { - *ppObject = NULL; - return E_NOINTERFACE; - } - - /* Getting here means the IID is IUnknown or IMMNotificationClient. */ - *ppObject = (void*)pThis; - ((ma_completion_handler_uwp_vtbl*)pThis->lpVtbl)->AddRef(pThis); - return S_OK; -} - -static ULONG STDMETHODCALLTYPE ma_completion_handler_uwp_AddRef(ma_completion_handler_uwp* pThis) -{ - return (ULONG)ma_atomic_fetch_add_32(&pThis->counter, 1) + 1; -} - -static ULONG STDMETHODCALLTYPE ma_completion_handler_uwp_Release(ma_completion_handler_uwp* pThis) -{ - ma_uint32 newRefCount = ma_atomic_fetch_sub_32(&pThis->counter, 1) - 1; - if (newRefCount == 0) { - return 0; /* We don't free anything here because we never allocate the object on the heap. */ - } - - return (ULONG)newRefCount; -} - -static HRESULT STDMETHODCALLTYPE ma_completion_handler_uwp_ActivateCompleted(ma_completion_handler_uwp* pThis, ma_IActivateAudioInterfaceAsyncOperation* pActivateOperation) -{ - (void)pActivateOperation; - SetEvent(pThis->hEvent); - return S_OK; -} - - -static ma_completion_handler_uwp_vtbl g_maCompletionHandlerVtblInstance = { - ma_completion_handler_uwp_QueryInterface, - ma_completion_handler_uwp_AddRef, - ma_completion_handler_uwp_Release, - ma_completion_handler_uwp_ActivateCompleted -}; - -static ma_result ma_completion_handler_uwp_init(ma_completion_handler_uwp* pHandler) -{ - MA_ASSERT(pHandler != NULL); - MA_ZERO_OBJECT(pHandler); - - pHandler->lpVtbl = &g_maCompletionHandlerVtblInstance; - pHandler->counter = 1; - pHandler->hEvent = CreateEventA(NULL, FALSE, FALSE, NULL); - if (pHandler->hEvent == NULL) { - return ma_result_from_GetLastError(GetLastError()); - } - - return MA_SUCCESS; -} - -static void ma_completion_handler_uwp_uninit(ma_completion_handler_uwp* pHandler) -{ - if (pHandler->hEvent != NULL) { - CloseHandle(pHandler->hEvent); - } -} - -static void ma_completion_handler_uwp_wait(ma_completion_handler_uwp* pHandler) -{ - WaitForSingleObject((HANDLE)pHandler->hEvent, INFINITE); -} -#endif /* !MA_WIN32_DESKTOP */ - -/* We need a virtual table for our notification client object that's used for detecting changes to the default device. */ -#if defined(MA_WIN32_DESKTOP) || defined(MA_WIN32_GDK) -static HRESULT STDMETHODCALLTYPE ma_IMMNotificationClient_QueryInterface(ma_IMMNotificationClient* pThis, const IID* const riid, void** ppObject) -{ - /* - We care about two interfaces - IUnknown and IMMNotificationClient. If the requested IID is something else - we just return E_NOINTERFACE. Otherwise we need to increment the reference counter and return S_OK. - */ - if (!ma_is_guid_equal(riid, &MA_IID_IUnknown) && !ma_is_guid_equal(riid, &MA_IID_IMMNotificationClient)) { - *ppObject = NULL; - return E_NOINTERFACE; - } - - /* Getting here means the IID is IUnknown or IMMNotificationClient. */ - *ppObject = (void*)pThis; - ((ma_IMMNotificationClientVtbl*)pThis->lpVtbl)->AddRef(pThis); - return S_OK; -} - -static ULONG STDMETHODCALLTYPE ma_IMMNotificationClient_AddRef(ma_IMMNotificationClient* pThis) -{ - return (ULONG)ma_atomic_fetch_add_32(&pThis->counter, 1) + 1; -} - -static ULONG STDMETHODCALLTYPE ma_IMMNotificationClient_Release(ma_IMMNotificationClient* pThis) -{ - ma_uint32 newRefCount = ma_atomic_fetch_sub_32(&pThis->counter, 1) - 1; - if (newRefCount == 0) { - return 0; /* We don't free anything here because we never allocate the object on the heap. */ - } - - return (ULONG)newRefCount; -} - -static HRESULT STDMETHODCALLTYPE ma_IMMNotificationClient_OnDeviceStateChanged(ma_IMMNotificationClient* pThis, const WCHAR* pDeviceID, DWORD dwNewState) -{ - ma_bool32 isThisDevice = MA_FALSE; - ma_bool32 isCapture = MA_FALSE; - ma_bool32 isPlayback = MA_FALSE; - -#ifdef MA_DEBUG_OUTPUT - /*ma_log_postf(ma_device_get_log(pThis->pDevice), MA_LOG_LEVEL_DEBUG, "IMMNotificationClient_OnDeviceStateChanged(pDeviceID=%S, dwNewState=%u)\n", (pDeviceID != NULL) ? pDeviceID : L"(NULL)", (unsigned int)dwNewState);*/ -#endif - - /* - There have been reports of a hang when a playback device is disconnected. The idea with this code is to explicitly stop the device if we detect - that the device is disabled or has been unplugged. - */ - if (pThis->pDevice->wasapi.allowCaptureAutoStreamRouting && (pThis->pDevice->type == ma_device_type_capture || pThis->pDevice->type == ma_device_type_duplex || pThis->pDevice->type == ma_device_type_loopback)) { - isCapture = MA_TRUE; - if (ma_strcmp_WCHAR(pThis->pDevice->capture.id.wasapi, pDeviceID) == 0) { - isThisDevice = MA_TRUE; - } - } - - if (pThis->pDevice->wasapi.allowPlaybackAutoStreamRouting && (pThis->pDevice->type == ma_device_type_playback || pThis->pDevice->type == ma_device_type_duplex)) { - isPlayback = MA_TRUE; - if (ma_strcmp_WCHAR(pThis->pDevice->playback.id.wasapi, pDeviceID) == 0) { - isThisDevice = MA_TRUE; - } - } - - - /* - If the device ID matches our device we need to mark our device as detached and stop it. When a - device is added in OnDeviceAdded(), we'll restart it. We only mark it as detached if the device - was started at the time of being removed. - */ - if (isThisDevice) { - if ((dwNewState & MA_MM_DEVICE_STATE_ACTIVE) == 0) { - /* - Unplugged or otherwise unavailable. Mark as detached if we were in a playing state. We'll - use this to determine whether or not we need to automatically start the device when it's - plugged back in again. - */ - if (ma_device_get_state(pThis->pDevice) == ma_device_state_started) { - if (isPlayback) { - pThis->pDevice->wasapi.isDetachedPlayback = MA_TRUE; - } - if (isCapture) { - pThis->pDevice->wasapi.isDetachedCapture = MA_TRUE; - } - - ma_device_stop(pThis->pDevice); - } - } - - if ((dwNewState & MA_MM_DEVICE_STATE_ACTIVE) != 0) { - /* The device was activated. If we were detached, we need to start it again. */ - ma_bool8 tryRestartingDevice = MA_FALSE; - - if (isPlayback) { - if (pThis->pDevice->wasapi.isDetachedPlayback) { - pThis->pDevice->wasapi.isDetachedPlayback = MA_FALSE; - ma_device_reroute__wasapi(pThis->pDevice, ma_device_type_playback); - tryRestartingDevice = MA_TRUE; - } - } - - if (isCapture) { - if (pThis->pDevice->wasapi.isDetachedCapture) { - pThis->pDevice->wasapi.isDetachedCapture = MA_FALSE; - ma_device_reroute__wasapi(pThis->pDevice, (pThis->pDevice->type == ma_device_type_loopback) ? ma_device_type_loopback : ma_device_type_capture); - tryRestartingDevice = MA_TRUE; - } - } - - if (tryRestartingDevice) { - if (pThis->pDevice->wasapi.isDetachedPlayback == MA_FALSE && pThis->pDevice->wasapi.isDetachedCapture == MA_FALSE) { - ma_device_start(pThis->pDevice); - } - } - } - } - - return S_OK; -} - -static HRESULT STDMETHODCALLTYPE ma_IMMNotificationClient_OnDeviceAdded(ma_IMMNotificationClient* pThis, const WCHAR* pDeviceID) -{ -#ifdef MA_DEBUG_OUTPUT - /*ma_log_postf(ma_device_get_log(pThis->pDevice), MA_LOG_LEVEL_DEBUG, "IMMNotificationClient_OnDeviceAdded(pDeviceID=%S)\n", (pDeviceID != NULL) ? pDeviceID : L"(NULL)");*/ -#endif - - /* We don't need to worry about this event for our purposes. */ - (void)pThis; - (void)pDeviceID; - return S_OK; -} - -static HRESULT STDMETHODCALLTYPE ma_IMMNotificationClient_OnDeviceRemoved(ma_IMMNotificationClient* pThis, const WCHAR* pDeviceID) -{ -#ifdef MA_DEBUG_OUTPUT - /*ma_log_postf(ma_device_get_log(pThis->pDevice), MA_LOG_LEVEL_DEBUG, "IMMNotificationClient_OnDeviceRemoved(pDeviceID=%S)\n", (pDeviceID != NULL) ? pDeviceID : L"(NULL)");*/ -#endif - - /* We don't need to worry about this event for our purposes. */ - (void)pThis; - (void)pDeviceID; - return S_OK; -} - -static HRESULT STDMETHODCALLTYPE ma_IMMNotificationClient_OnDefaultDeviceChanged(ma_IMMNotificationClient* pThis, ma_EDataFlow dataFlow, ma_ERole role, const WCHAR* pDefaultDeviceID) -{ -#ifdef MA_DEBUG_OUTPUT - /*ma_log_postf(ma_device_get_log(pThis->pDevice), MA_LOG_LEVEL_DEBUG, "IMMNotificationClient_OnDefaultDeviceChanged(dataFlow=%d, role=%d, pDefaultDeviceID=%S)\n", dataFlow, role, (pDefaultDeviceID != NULL) ? pDefaultDeviceID : L"(NULL)");*/ -#endif - - (void)role; - - /* We only care about devices with the same data flow as the current device. */ - if ((pThis->pDevice->type == ma_device_type_playback && dataFlow != ma_eRender) || - (pThis->pDevice->type == ma_device_type_capture && dataFlow != ma_eCapture) || - (pThis->pDevice->type == ma_device_type_loopback && dataFlow != ma_eRender)) { - ma_log_postf(ma_device_get_log(pThis->pDevice), MA_LOG_LEVEL_DEBUG, "[WASAPI] Stream rerouting abandoned because dataFlow does match device type.\n"); - return S_OK; - } - - /* We need to consider dataFlow as ma_eCapture if device is ma_device_type_loopback */ - if (pThis->pDevice->type == ma_device_type_loopback) { - dataFlow = ma_eCapture; - } - - /* Don't do automatic stream routing if we're not allowed. */ - if ((dataFlow == ma_eRender && pThis->pDevice->wasapi.allowPlaybackAutoStreamRouting == MA_FALSE) || - (dataFlow == ma_eCapture && pThis->pDevice->wasapi.allowCaptureAutoStreamRouting == MA_FALSE)) { - ma_log_postf(ma_device_get_log(pThis->pDevice), MA_LOG_LEVEL_DEBUG, "[WASAPI] Stream rerouting abandoned because automatic stream routing has been disabled by the device config.\n"); - return S_OK; - } - - /* - Not currently supporting automatic stream routing in exclusive mode. This is not working correctly on my machine due to - AUDCLNT_E_DEVICE_IN_USE errors when reinitializing the device. If this is a bug in miniaudio, we can try re-enabling this once - it's fixed. - */ - if ((dataFlow == ma_eRender && pThis->pDevice->playback.shareMode == ma_share_mode_exclusive) || - (dataFlow == ma_eCapture && pThis->pDevice->capture.shareMode == ma_share_mode_exclusive)) { - ma_log_postf(ma_device_get_log(pThis->pDevice), MA_LOG_LEVEL_DEBUG, "[WASAPI] Stream rerouting abandoned because the device shared mode is exclusive.\n"); - return S_OK; - } - - - - /* - Second attempt at device rerouting. We're going to retrieve the device's state at the time of - the route change. We're then going to stop the device, reinitialize the device, and then start - it again if the state before stopping was ma_device_state_started. - */ - { - ma_uint32 previousState = ma_device_get_state(pThis->pDevice); - ma_bool8 restartDevice = MA_FALSE; - - if (previousState == ma_device_state_uninitialized || previousState == ma_device_state_starting) { - ma_log_postf(ma_device_get_log(pThis->pDevice), MA_LOG_LEVEL_DEBUG, "[WASAPI] Stream rerouting abandoned because the device is in the process of starting.\n"); - return S_OK; - } - - if (previousState == ma_device_state_started) { - ma_device_stop(pThis->pDevice); - restartDevice = MA_TRUE; - } - - if (pDefaultDeviceID != NULL) { /* <-- The input device ID will be null if there's no other device available. */ - ma_mutex_lock(&pThis->pDevice->wasapi.rerouteLock); - { - if (dataFlow == ma_eRender) { - ma_device_reroute__wasapi(pThis->pDevice, ma_device_type_playback); - - if (pThis->pDevice->wasapi.isDetachedPlayback) { - pThis->pDevice->wasapi.isDetachedPlayback = MA_FALSE; - - if (pThis->pDevice->type == ma_device_type_duplex && pThis->pDevice->wasapi.isDetachedCapture) { - restartDevice = MA_FALSE; /* It's a duplex device and the capture side is detached. We cannot be restarting the device just yet. */ - } - else { - restartDevice = MA_TRUE; /* It's not a duplex device, or the capture side is also attached so we can go ahead and restart the device. */ - } - } - } - else { - ma_device_reroute__wasapi(pThis->pDevice, (pThis->pDevice->type == ma_device_type_loopback) ? ma_device_type_loopback : ma_device_type_capture); - - if (pThis->pDevice->wasapi.isDetachedCapture) { - pThis->pDevice->wasapi.isDetachedCapture = MA_FALSE; - - if (pThis->pDevice->type == ma_device_type_duplex && pThis->pDevice->wasapi.isDetachedPlayback) { - restartDevice = MA_FALSE; /* It's a duplex device and the playback side is detached. We cannot be restarting the device just yet. */ - } - else { - restartDevice = MA_TRUE; /* It's not a duplex device, or the playback side is also attached so we can go ahead and restart the device. */ - } - } - } - } - ma_mutex_unlock(&pThis->pDevice->wasapi.rerouteLock); - - if (restartDevice) { - ma_device_start(pThis->pDevice); - } - } - } - - return S_OK; -} - -static HRESULT STDMETHODCALLTYPE ma_IMMNotificationClient_OnPropertyValueChanged(ma_IMMNotificationClient* pThis, const WCHAR* pDeviceID, const PROPERTYKEY key) -{ -#ifdef MA_DEBUG_OUTPUT - /*ma_log_postf(ma_device_get_log(pThis->pDevice), MA_LOG_LEVEL_DEBUG, "IMMNotificationClient_OnPropertyValueChanged(pDeviceID=%S)\n", (pDeviceID != NULL) ? pDeviceID : L"(NULL)");*/ -#endif - - (void)pThis; - (void)pDeviceID; - (void)key; - return S_OK; -} - -static ma_IMMNotificationClientVtbl g_maNotificationCientVtbl = { - ma_IMMNotificationClient_QueryInterface, - ma_IMMNotificationClient_AddRef, - ma_IMMNotificationClient_Release, - ma_IMMNotificationClient_OnDeviceStateChanged, - ma_IMMNotificationClient_OnDeviceAdded, - ma_IMMNotificationClient_OnDeviceRemoved, - ma_IMMNotificationClient_OnDefaultDeviceChanged, - ma_IMMNotificationClient_OnPropertyValueChanged -}; -#endif /* MA_WIN32_DESKTOP */ - -static const char* ma_to_usage_string__wasapi(ma_wasapi_usage usage) -{ - switch (usage) - { - case ma_wasapi_usage_default: return NULL; - case ma_wasapi_usage_games: return "Games"; - case ma_wasapi_usage_pro_audio: return "Pro Audio"; - default: break; - } - - return NULL; -} - -#if defined(MA_WIN32_DESKTOP) || defined(MA_WIN32_GDK) -typedef ma_IMMDevice ma_WASAPIDeviceInterface; -#else -typedef ma_IUnknown ma_WASAPIDeviceInterface; -#endif - - -#define MA_CONTEXT_COMMAND_QUIT__WASAPI 1 -#define MA_CONTEXT_COMMAND_CREATE_IAUDIOCLIENT__WASAPI 2 -#define MA_CONTEXT_COMMAND_RELEASE_IAUDIOCLIENT__WASAPI 3 - -static ma_context_command__wasapi ma_context_init_command__wasapi(int code) -{ - ma_context_command__wasapi cmd; - - MA_ZERO_OBJECT(&cmd); - cmd.code = code; - - return cmd; -} - -static ma_result ma_context_post_command__wasapi(ma_context* pContext, const ma_context_command__wasapi* pCmd) -{ - /* For now we are doing everything synchronously, but I might relax this later if the need arises. */ - ma_result result; - ma_bool32 isUsingLocalEvent = MA_FALSE; - ma_event localEvent; - - MA_ASSERT(pContext != NULL); - MA_ASSERT(pCmd != NULL); - - if (pCmd->pEvent == NULL) { - isUsingLocalEvent = MA_TRUE; - - result = ma_event_init(&localEvent); - if (result != MA_SUCCESS) { - return result; /* Failed to create the event for this command. */ - } - } - - /* Here is where we add the command to the list. If there's not enough room we'll spin until there is. */ - ma_mutex_lock(&pContext->wasapi.commandLock); - { - ma_uint32 index; - - /* Spin until we've got some space available. */ - while (pContext->wasapi.commandCount == ma_countof(pContext->wasapi.commands)) { - ma_yield(); - } - - /* Space is now available. Can safely add to the list. */ - index = (pContext->wasapi.commandIndex + pContext->wasapi.commandCount) % ma_countof(pContext->wasapi.commands); - pContext->wasapi.commands[index] = *pCmd; - pContext->wasapi.commands[index].pEvent = &localEvent; - pContext->wasapi.commandCount += 1; - - /* Now that the command has been added, release the semaphore so ma_context_next_command__wasapi() can return. */ - ma_semaphore_release(&pContext->wasapi.commandSem); - } - ma_mutex_unlock(&pContext->wasapi.commandLock); - - if (isUsingLocalEvent) { - ma_event_wait(&localEvent); - ma_event_uninit(&localEvent); - } - - return MA_SUCCESS; -} - -static ma_result ma_context_next_command__wasapi(ma_context* pContext, ma_context_command__wasapi* pCmd) -{ - ma_result result = MA_SUCCESS; - - MA_ASSERT(pContext != NULL); - MA_ASSERT(pCmd != NULL); - - result = ma_semaphore_wait(&pContext->wasapi.commandSem); - if (result == MA_SUCCESS) { - ma_mutex_lock(&pContext->wasapi.commandLock); - { - *pCmd = pContext->wasapi.commands[pContext->wasapi.commandIndex]; - pContext->wasapi.commandIndex = (pContext->wasapi.commandIndex + 1) % ma_countof(pContext->wasapi.commands); - pContext->wasapi.commandCount -= 1; - } - ma_mutex_unlock(&pContext->wasapi.commandLock); - } - - return result; -} - -static ma_thread_result MA_THREADCALL ma_context_command_thread__wasapi(void* pUserData) -{ - ma_result result; - ma_context* pContext = (ma_context*)pUserData; - MA_ASSERT(pContext != NULL); - - for (;;) { - ma_context_command__wasapi cmd; - result = ma_context_next_command__wasapi(pContext, &cmd); - if (result != MA_SUCCESS) { - break; - } - - switch (cmd.code) - { - case MA_CONTEXT_COMMAND_QUIT__WASAPI: - { - /* Do nothing. Handled after the switch. */ - } break; - - case MA_CONTEXT_COMMAND_CREATE_IAUDIOCLIENT__WASAPI: - { - if (cmd.data.createAudioClient.deviceType == ma_device_type_playback) { - *cmd.data.createAudioClient.pResult = ma_result_from_HRESULT(ma_IAudioClient_GetService((ma_IAudioClient*)cmd.data.createAudioClient.pAudioClient, &MA_IID_IAudioRenderClient, cmd.data.createAudioClient.ppAudioClientService)); - } else { - *cmd.data.createAudioClient.pResult = ma_result_from_HRESULT(ma_IAudioClient_GetService((ma_IAudioClient*)cmd.data.createAudioClient.pAudioClient, &MA_IID_IAudioCaptureClient, cmd.data.createAudioClient.ppAudioClientService)); - } - } break; - - case MA_CONTEXT_COMMAND_RELEASE_IAUDIOCLIENT__WASAPI: - { - if (cmd.data.releaseAudioClient.deviceType == ma_device_type_playback) { - if (cmd.data.releaseAudioClient.pDevice->wasapi.pAudioClientPlayback != NULL) { - ma_IAudioClient_Release((ma_IAudioClient*)cmd.data.releaseAudioClient.pDevice->wasapi.pAudioClientPlayback); - cmd.data.releaseAudioClient.pDevice->wasapi.pAudioClientPlayback = NULL; - } - } - - if (cmd.data.releaseAudioClient.deviceType == ma_device_type_capture) { - if (cmd.data.releaseAudioClient.pDevice->wasapi.pAudioClientCapture != NULL) { - ma_IAudioClient_Release((ma_IAudioClient*)cmd.data.releaseAudioClient.pDevice->wasapi.pAudioClientCapture); - cmd.data.releaseAudioClient.pDevice->wasapi.pAudioClientCapture = NULL; - } - } - } break; - - default: - { - /* Unknown command. Ignore it, but trigger an assert in debug mode so we're aware of it. */ - MA_ASSERT(MA_FALSE); - } break; - } - - if (cmd.pEvent != NULL) { - ma_event_signal(cmd.pEvent); - } - - if (cmd.code == MA_CONTEXT_COMMAND_QUIT__WASAPI) { - break; /* Received a quit message. Get out of here. */ - } - } - - return (ma_thread_result)0; -} - -static ma_result ma_device_create_IAudioClient_service__wasapi(ma_context* pContext, ma_device_type deviceType, ma_IAudioClient* pAudioClient, void** ppAudioClientService) -{ - ma_result result; - ma_result cmdResult; - ma_context_command__wasapi cmd = ma_context_init_command__wasapi(MA_CONTEXT_COMMAND_CREATE_IAUDIOCLIENT__WASAPI); - cmd.data.createAudioClient.deviceType = deviceType; - cmd.data.createAudioClient.pAudioClient = (void*)pAudioClient; - cmd.data.createAudioClient.ppAudioClientService = ppAudioClientService; - cmd.data.createAudioClient.pResult = &cmdResult; /* Declared locally, but won't be dereferenced after this function returns since execution of the command will wait here. */ - - result = ma_context_post_command__wasapi(pContext, &cmd); /* This will not return until the command has actually been run. */ - if (result != MA_SUCCESS) { - return result; - } - - return *cmd.data.createAudioClient.pResult; -} - -#if 0 /* Not used at the moment, but leaving here for future use. */ -static ma_result ma_device_release_IAudioClient_service__wasapi(ma_device* pDevice, ma_device_type deviceType) -{ - ma_result result; - ma_context_command__wasapi cmd = ma_context_init_command__wasapi(MA_CONTEXT_COMMAND_RELEASE_IAUDIOCLIENT__WASAPI); - cmd.data.releaseAudioClient.pDevice = pDevice; - cmd.data.releaseAudioClient.deviceType = deviceType; - - result = ma_context_post_command__wasapi(pDevice->pContext, &cmd); /* This will not return until the command has actually been run. */ - if (result != MA_SUCCESS) { - return result; - } - - return MA_SUCCESS; -} -#endif - - -static void ma_add_native_data_format_to_device_info_from_WAVEFORMATEX(const MA_WAVEFORMATEX* pWF, ma_share_mode shareMode, ma_device_info* pInfo) -{ - MA_ASSERT(pWF != NULL); - MA_ASSERT(pInfo != NULL); - - if (pInfo->nativeDataFormatCount >= ma_countof(pInfo->nativeDataFormats)) { - return; /* Too many data formats. Need to ignore this one. Don't think this should ever happen with WASAPI. */ - } - - pInfo->nativeDataFormats[pInfo->nativeDataFormatCount].format = ma_format_from_WAVEFORMATEX(pWF); - pInfo->nativeDataFormats[pInfo->nativeDataFormatCount].channels = pWF->nChannels; - pInfo->nativeDataFormats[pInfo->nativeDataFormatCount].sampleRate = pWF->nSamplesPerSec; - pInfo->nativeDataFormats[pInfo->nativeDataFormatCount].flags = (shareMode == ma_share_mode_exclusive) ? MA_DATA_FORMAT_FLAG_EXCLUSIVE_MODE : 0; - pInfo->nativeDataFormatCount += 1; -} - -static ma_result ma_context_get_device_info_from_IAudioClient__wasapi(ma_context* pContext, /*ma_IMMDevice**/void* pMMDevice, ma_IAudioClient* pAudioClient, ma_device_info* pInfo) -{ - HRESULT hr; - MA_WAVEFORMATEX* pWF = NULL; - - MA_ASSERT(pAudioClient != NULL); - MA_ASSERT(pInfo != NULL); - - /* Shared Mode. We use GetMixFormat() here. */ - hr = ma_IAudioClient_GetMixFormat((ma_IAudioClient*)pAudioClient, (MA_WAVEFORMATEX**)&pWF); - if (SUCCEEDED(hr)) { - ma_add_native_data_format_to_device_info_from_WAVEFORMATEX(pWF, ma_share_mode_shared, pInfo); - } else { - ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to retrieve mix format for device info retrieval."); - return ma_result_from_HRESULT(hr); - } - - /* - Exclusive Mode. We repeatedly call IsFormatSupported() here. This is not currently supported on - UWP. Failure to retrieve the exclusive mode format is not considered an error, so from here on - out, MA_SUCCESS is guaranteed to be returned. - */ - #if defined(MA_WIN32_DESKTOP) || defined(MA_WIN32_GDK) - { - ma_IPropertyStore *pProperties; - - /* - The first thing to do is get the format from PKEY_AudioEngine_DeviceFormat. This should give us a channel count we assume is - correct which will simplify our searching. - */ - hr = ma_IMMDevice_OpenPropertyStore((ma_IMMDevice*)pMMDevice, STGM_READ, &pProperties); - if (SUCCEEDED(hr)) { - MA_PROPVARIANT var; - ma_PropVariantInit(&var); - - hr = ma_IPropertyStore_GetValue(pProperties, &MA_PKEY_AudioEngine_DeviceFormat, &var); - if (SUCCEEDED(hr)) { - pWF = (MA_WAVEFORMATEX*)var.blob.pBlobData; - - /* - In my testing, the format returned by PKEY_AudioEngine_DeviceFormat is suitable for exclusive mode so we check this format - first. If this fails, fall back to a search. - */ - hr = ma_IAudioClient_IsFormatSupported((ma_IAudioClient*)pAudioClient, MA_AUDCLNT_SHAREMODE_EXCLUSIVE, pWF, NULL); - if (SUCCEEDED(hr)) { - /* The format returned by PKEY_AudioEngine_DeviceFormat is supported. */ - ma_add_native_data_format_to_device_info_from_WAVEFORMATEX(pWF, ma_share_mode_exclusive, pInfo); - } else { - /* - The format returned by PKEY_AudioEngine_DeviceFormat is not supported, so fall back to a search. We assume the channel - count returned by MA_PKEY_AudioEngine_DeviceFormat is valid and correct. For simplicity we're only returning one format. - */ - ma_uint32 channels = pWF->nChannels; - ma_channel defaultChannelMap[MA_MAX_CHANNELS]; - MA_WAVEFORMATEXTENSIBLE wf; - ma_bool32 found; - ma_uint32 iFormat; - - /* Make sure we don't overflow the channel map. */ - if (channels > MA_MAX_CHANNELS) { - channels = MA_MAX_CHANNELS; - } - - ma_channel_map_init_standard(ma_standard_channel_map_microsoft, defaultChannelMap, ma_countof(defaultChannelMap), channels); - - MA_ZERO_OBJECT(&wf); - wf.cbSize = sizeof(wf); - wf.wFormatTag = WAVE_FORMAT_EXTENSIBLE; - wf.nChannels = (WORD)channels; - wf.dwChannelMask = ma_channel_map_to_channel_mask__win32(defaultChannelMap, channels); - - found = MA_FALSE; - for (iFormat = 0; iFormat < ma_countof(g_maFormatPriorities); ++iFormat) { - ma_format format = g_maFormatPriorities[iFormat]; - ma_uint32 iSampleRate; - - wf.wBitsPerSample = (WORD)(ma_get_bytes_per_sample(format)*8); - wf.nBlockAlign = (WORD)(wf.nChannels * wf.wBitsPerSample / 8); - wf.nAvgBytesPerSec = wf.nBlockAlign * wf.nSamplesPerSec; - wf.Samples.wValidBitsPerSample = /*(format == ma_format_s24_32) ? 24 :*/ wf.wBitsPerSample; - if (format == ma_format_f32) { - wf.SubFormat = MA_GUID_KSDATAFORMAT_SUBTYPE_IEEE_FLOAT; - } else { - wf.SubFormat = MA_GUID_KSDATAFORMAT_SUBTYPE_PCM; - } - - for (iSampleRate = 0; iSampleRate < ma_countof(g_maStandardSampleRatePriorities); ++iSampleRate) { - wf.nSamplesPerSec = g_maStandardSampleRatePriorities[iSampleRate]; - - hr = ma_IAudioClient_IsFormatSupported((ma_IAudioClient*)pAudioClient, MA_AUDCLNT_SHAREMODE_EXCLUSIVE, (MA_WAVEFORMATEX*)&wf, NULL); - if (SUCCEEDED(hr)) { - ma_add_native_data_format_to_device_info_from_WAVEFORMATEX((MA_WAVEFORMATEX*)&wf, ma_share_mode_exclusive, pInfo); - found = MA_TRUE; - break; - } - } - - if (found) { - break; - } - } - - ma_PropVariantClear(pContext, &var); - - if (!found) { - ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_WARNING, "[WASAPI] Failed to find suitable device format for device info retrieval."); - } - } - } else { - ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_WARNING, "[WASAPI] Failed to retrieve device format for device info retrieval."); - } - - ma_IPropertyStore_Release(pProperties); - } else { - ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_WARNING, "[WASAPI] Failed to open property store for device info retrieval."); - } - } - #else - { - (void)pMMDevice; /* Unused. */ - } - #endif - - return MA_SUCCESS; -} - -#if defined(MA_WIN32_DESKTOP) || defined(MA_WIN32_GDK) -static ma_EDataFlow ma_device_type_to_EDataFlow(ma_device_type deviceType) -{ - if (deviceType == ma_device_type_playback) { - return ma_eRender; - } else if (deviceType == ma_device_type_capture) { - return ma_eCapture; - } else { - MA_ASSERT(MA_FALSE); - return ma_eRender; /* Should never hit this. */ - } -} - -static ma_result ma_context_create_IMMDeviceEnumerator__wasapi(ma_context* pContext, ma_IMMDeviceEnumerator** ppDeviceEnumerator) -{ - HRESULT hr; - ma_IMMDeviceEnumerator* pDeviceEnumerator; - - MA_ASSERT(pContext != NULL); - MA_ASSERT(ppDeviceEnumerator != NULL); - - *ppDeviceEnumerator = NULL; /* Safety. */ - - hr = ma_CoCreateInstance(pContext, &MA_CLSID_MMDeviceEnumerator, NULL, CLSCTX_ALL, &MA_IID_IMMDeviceEnumerator, (void**)&pDeviceEnumerator); - if (FAILED(hr)) { - ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to create device enumerator."); - return ma_result_from_HRESULT(hr); - } - - *ppDeviceEnumerator = pDeviceEnumerator; - - return MA_SUCCESS; -} - -static WCHAR* ma_context_get_default_device_id_from_IMMDeviceEnumerator__wasapi(ma_context* pContext, ma_IMMDeviceEnumerator* pDeviceEnumerator, ma_device_type deviceType) -{ - HRESULT hr; - ma_IMMDevice* pMMDefaultDevice = NULL; - WCHAR* pDefaultDeviceID = NULL; - ma_EDataFlow dataFlow; - ma_ERole role; - - MA_ASSERT(pContext != NULL); - MA_ASSERT(pDeviceEnumerator != NULL); - - (void)pContext; - - /* Grab the EDataFlow type from the device type. */ - dataFlow = ma_device_type_to_EDataFlow(deviceType); - - /* The role is always eConsole, but we may make this configurable later. */ - role = ma_eConsole; - - hr = ma_IMMDeviceEnumerator_GetDefaultAudioEndpoint(pDeviceEnumerator, dataFlow, role, &pMMDefaultDevice); - if (FAILED(hr)) { - return NULL; - } - - hr = ma_IMMDevice_GetId(pMMDefaultDevice, &pDefaultDeviceID); - - ma_IMMDevice_Release(pMMDefaultDevice); - pMMDefaultDevice = NULL; - - if (FAILED(hr)) { - return NULL; - } - - return pDefaultDeviceID; -} - -static WCHAR* ma_context_get_default_device_id__wasapi(ma_context* pContext, ma_device_type deviceType) /* Free the returned pointer with ma_CoTaskMemFree() */ -{ - ma_result result; - ma_IMMDeviceEnumerator* pDeviceEnumerator; - WCHAR* pDefaultDeviceID = NULL; - - MA_ASSERT(pContext != NULL); - - result = ma_context_create_IMMDeviceEnumerator__wasapi(pContext, &pDeviceEnumerator); - if (result != MA_SUCCESS) { - return NULL; - } - - pDefaultDeviceID = ma_context_get_default_device_id_from_IMMDeviceEnumerator__wasapi(pContext, pDeviceEnumerator, deviceType); - - ma_IMMDeviceEnumerator_Release(pDeviceEnumerator); - return pDefaultDeviceID; -} - -static ma_result ma_context_get_MMDevice__wasapi(ma_context* pContext, ma_device_type deviceType, const ma_device_id* pDeviceID, ma_IMMDevice** ppMMDevice) -{ - ma_IMMDeviceEnumerator* pDeviceEnumerator; - HRESULT hr; - - MA_ASSERT(pContext != NULL); - MA_ASSERT(ppMMDevice != NULL); - - /* - This weird COM init/uninit here is a hack to work around a crash when changing devices. What is happening is - WASAPI fires a callback from another thread when the device is changed. It's from that thread where this - function is getting called. What I'm suspecting is that the other thread is not initializing COM which in turn - results in CoCreateInstance() failing. - - The community has reported that this seems to fix the crash. There are future plans to move all WASAPI operation - over to a single thread to make everything safer, but in the meantime while we wait for that to come online I'm - happy enough to use this hack instead. - */ - ma_CoInitializeEx(pContext, NULL, MA_COINIT_VALUE); - { - hr = ma_CoCreateInstance(pContext, &MA_CLSID_MMDeviceEnumerator, NULL, CLSCTX_ALL, &MA_IID_IMMDeviceEnumerator, (void**)&pDeviceEnumerator); - } - ma_CoUninitialize(pContext); - - if (FAILED(hr)) { /* <-- This is checking the call above to ma_CoCreateInstance(). */ - ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to create IMMDeviceEnumerator.\n"); - return ma_result_from_HRESULT(hr); - } - - if (pDeviceID == NULL) { - hr = ma_IMMDeviceEnumerator_GetDefaultAudioEndpoint(pDeviceEnumerator, (deviceType == ma_device_type_capture) ? ma_eCapture : ma_eRender, ma_eConsole, ppMMDevice); - } else { - hr = ma_IMMDeviceEnumerator_GetDevice(pDeviceEnumerator, pDeviceID->wasapi, ppMMDevice); - } - - ma_IMMDeviceEnumerator_Release(pDeviceEnumerator); - if (FAILED(hr)) { - ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to retrieve IMMDevice.\n"); - return ma_result_from_HRESULT(hr); - } - - return MA_SUCCESS; -} - -static ma_result ma_context_get_device_id_from_MMDevice__wasapi(ma_context* pContext, ma_IMMDevice* pMMDevice, ma_device_id* pDeviceID) -{ - WCHAR* pDeviceIDString; - HRESULT hr; - - MA_ASSERT(pDeviceID != NULL); - - hr = ma_IMMDevice_GetId(pMMDevice, &pDeviceIDString); - if (SUCCEEDED(hr)) { - size_t idlen = ma_strlen_WCHAR(pDeviceIDString); - if (idlen+1 > ma_countof(pDeviceID->wasapi)) { - ma_CoTaskMemFree(pContext, pDeviceIDString); - MA_ASSERT(MA_FALSE); /* NOTE: If this is triggered, please report it. It means the format of the ID must have changed and is too long to fit in our fixed sized buffer. */ - return MA_ERROR; - } - - MA_COPY_MEMORY(pDeviceID->wasapi, pDeviceIDString, idlen * sizeof(wchar_t)); - pDeviceID->wasapi[idlen] = '\0'; - - ma_CoTaskMemFree(pContext, pDeviceIDString); - - return MA_SUCCESS; - } - - return MA_ERROR; -} - -static ma_result ma_context_get_device_info_from_MMDevice__wasapi(ma_context* pContext, ma_IMMDevice* pMMDevice, WCHAR* pDefaultDeviceID, ma_bool32 onlySimpleInfo, ma_device_info* pInfo) -{ - ma_result result; - HRESULT hr; - - MA_ASSERT(pContext != NULL); - MA_ASSERT(pMMDevice != NULL); - MA_ASSERT(pInfo != NULL); - - /* ID. */ - result = ma_context_get_device_id_from_MMDevice__wasapi(pContext, pMMDevice, &pInfo->id); - if (result == MA_SUCCESS) { - if (pDefaultDeviceID != NULL) { - if (ma_strcmp_WCHAR(pInfo->id.wasapi, pDefaultDeviceID) == 0) { - pInfo->isDefault = MA_TRUE; - } - } - } - - /* Description / Friendly Name */ - { - ma_IPropertyStore *pProperties; - hr = ma_IMMDevice_OpenPropertyStore(pMMDevice, STGM_READ, &pProperties); - if (SUCCEEDED(hr)) { - MA_PROPVARIANT var; - - ma_PropVariantInit(&var); - hr = ma_IPropertyStore_GetValue(pProperties, &MA_PKEY_Device_FriendlyName, &var); - if (SUCCEEDED(hr)) { - WideCharToMultiByte(CP_UTF8, 0, var.pwszVal, -1, pInfo->name, sizeof(pInfo->name), 0, FALSE); - ma_PropVariantClear(pContext, &var); - } - - ma_IPropertyStore_Release(pProperties); - } - } - - /* Format */ - if (!onlySimpleInfo) { - ma_IAudioClient* pAudioClient; - hr = ma_IMMDevice_Activate(pMMDevice, &MA_IID_IAudioClient, CLSCTX_ALL, NULL, (void**)&pAudioClient); - if (SUCCEEDED(hr)) { - result = ma_context_get_device_info_from_IAudioClient__wasapi(pContext, pMMDevice, pAudioClient, pInfo); - - ma_IAudioClient_Release(pAudioClient); - return result; - } else { - ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to activate audio client for device info retrieval."); - return ma_result_from_HRESULT(hr); - } - } - - return MA_SUCCESS; -} - -static ma_result ma_context_enumerate_devices_by_type__wasapi(ma_context* pContext, ma_IMMDeviceEnumerator* pDeviceEnumerator, ma_device_type deviceType, ma_enum_devices_callback_proc callback, void* pUserData) -{ - ma_result result = MA_SUCCESS; - UINT deviceCount; - HRESULT hr; - ma_uint32 iDevice; - WCHAR* pDefaultDeviceID = NULL; - ma_IMMDeviceCollection* pDeviceCollection = NULL; - - MA_ASSERT(pContext != NULL); - MA_ASSERT(callback != NULL); - - /* Grab the default device. We use this to know whether or not flag the returned device info as being the default. */ - pDefaultDeviceID = ma_context_get_default_device_id_from_IMMDeviceEnumerator__wasapi(pContext, pDeviceEnumerator, deviceType); - - /* We need to enumerate the devices which returns a device collection. */ - hr = ma_IMMDeviceEnumerator_EnumAudioEndpoints(pDeviceEnumerator, ma_device_type_to_EDataFlow(deviceType), MA_MM_DEVICE_STATE_ACTIVE, &pDeviceCollection); - if (SUCCEEDED(hr)) { - hr = ma_IMMDeviceCollection_GetCount(pDeviceCollection, &deviceCount); - if (FAILED(hr)) { - ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to get device count.\n"); - result = ma_result_from_HRESULT(hr); - goto done; - } - - for (iDevice = 0; iDevice < deviceCount; ++iDevice) { - ma_device_info deviceInfo; - ma_IMMDevice* pMMDevice; - - MA_ZERO_OBJECT(&deviceInfo); - - hr = ma_IMMDeviceCollection_Item(pDeviceCollection, iDevice, &pMMDevice); - if (SUCCEEDED(hr)) { - result = ma_context_get_device_info_from_MMDevice__wasapi(pContext, pMMDevice, pDefaultDeviceID, MA_TRUE, &deviceInfo); /* MA_TRUE = onlySimpleInfo. */ - - ma_IMMDevice_Release(pMMDevice); - if (result == MA_SUCCESS) { - ma_bool32 cbResult = callback(pContext, deviceType, &deviceInfo, pUserData); - if (cbResult == MA_FALSE) { - break; - } - } - } - } - } - -done: - if (pDefaultDeviceID != NULL) { - ma_CoTaskMemFree(pContext, pDefaultDeviceID); - pDefaultDeviceID = NULL; - } - - if (pDeviceCollection != NULL) { - ma_IMMDeviceCollection_Release(pDeviceCollection); - pDeviceCollection = NULL; - } - - return result; -} - -static ma_result ma_context_get_IAudioClient_Desktop__wasapi(ma_context* pContext, ma_device_type deviceType, const ma_device_id* pDeviceID, MA_PROPVARIANT* pActivationParams, ma_IAudioClient** ppAudioClient, ma_IMMDevice** ppMMDevice) -{ - ma_result result; - HRESULT hr; - - MA_ASSERT(pContext != NULL); - MA_ASSERT(ppAudioClient != NULL); - MA_ASSERT(ppMMDevice != NULL); - - result = ma_context_get_MMDevice__wasapi(pContext, deviceType, pDeviceID, ppMMDevice); - if (result != MA_SUCCESS) { - return result; - } - - hr = ma_IMMDevice_Activate(*ppMMDevice, &MA_IID_IAudioClient, CLSCTX_ALL, pActivationParams, (void**)ppAudioClient); - if (FAILED(hr)) { - return ma_result_from_HRESULT(hr); - } - - return MA_SUCCESS; -} -#else -static ma_result ma_context_get_IAudioClient_UWP__wasapi(ma_context* pContext, ma_device_type deviceType, const ma_device_id* pDeviceID, MA_PROPVARIANT* pActivationParams, ma_IAudioClient** ppAudioClient, ma_IUnknown** ppActivatedInterface) -{ - ma_IActivateAudioInterfaceAsyncOperation *pAsyncOp = NULL; - ma_completion_handler_uwp completionHandler; - IID iid; - WCHAR* iidStr; - HRESULT hr; - ma_result result; - HRESULT activateResult; - ma_IUnknown* pActivatedInterface; - - MA_ASSERT(pContext != NULL); - MA_ASSERT(ppAudioClient != NULL); - - if (pDeviceID != NULL) { - iidStr = (WCHAR*)pDeviceID->wasapi; - } else { - if (deviceType == ma_device_type_capture) { - iid = MA_IID_DEVINTERFACE_AUDIO_CAPTURE; - } else { - iid = MA_IID_DEVINTERFACE_AUDIO_RENDER; - } - - #if defined(__cplusplus) - hr = StringFromIID(iid, &iidStr); - #else - hr = StringFromIID(&iid, &iidStr); - #endif - if (FAILED(hr)) { - ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to convert device IID to string for ActivateAudioInterfaceAsync(). Out of memory.\n"); - return ma_result_from_HRESULT(hr); - } - } - - result = ma_completion_handler_uwp_init(&completionHandler); - if (result != MA_SUCCESS) { - ma_CoTaskMemFree(pContext, iidStr); - ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to create event for waiting for ActivateAudioInterfaceAsync().\n"); - return result; - } - - hr = ((MA_PFN_ActivateAudioInterfaceAsync)pContext->wasapi.ActivateAudioInterfaceAsync)(iidStr, &MA_IID_IAudioClient, pActivationParams, (ma_IActivateAudioInterfaceCompletionHandler*)&completionHandler, (ma_IActivateAudioInterfaceAsyncOperation**)&pAsyncOp); - if (FAILED(hr)) { - ma_completion_handler_uwp_uninit(&completionHandler); - ma_CoTaskMemFree(pContext, iidStr); - ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[WASAPI] ActivateAudioInterfaceAsync() failed.\n"); - return ma_result_from_HRESULT(hr); - } - - if (pDeviceID == NULL) { - ma_CoTaskMemFree(pContext, iidStr); - } - - /* Wait for the async operation for finish. */ - ma_completion_handler_uwp_wait(&completionHandler); - ma_completion_handler_uwp_uninit(&completionHandler); - - hr = ma_IActivateAudioInterfaceAsyncOperation_GetActivateResult(pAsyncOp, &activateResult, &pActivatedInterface); - ma_IActivateAudioInterfaceAsyncOperation_Release(pAsyncOp); - - if (FAILED(hr) || FAILED(activateResult)) { - ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to activate device.\n"); - return FAILED(hr) ? ma_result_from_HRESULT(hr) : ma_result_from_HRESULT(activateResult); - } - - /* Here is where we grab the IAudioClient interface. */ - hr = ma_IUnknown_QueryInterface(pActivatedInterface, &MA_IID_IAudioClient, (void**)ppAudioClient); - if (FAILED(hr)) { - ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to query IAudioClient interface.\n"); - return ma_result_from_HRESULT(hr); - } - - if (ppActivatedInterface) { - *ppActivatedInterface = pActivatedInterface; - } else { - ma_IUnknown_Release(pActivatedInterface); - } - - return MA_SUCCESS; -} -#endif - - -/* https://docs.microsoft.com/en-us/windows/win32/api/audioclientactivationparams/ne-audioclientactivationparams-audioclient_activation_type */ -typedef enum -{ - MA_AUDIOCLIENT_ACTIVATION_TYPE_DEFAULT, - MA_AUDIOCLIENT_ACTIVATION_TYPE_PROCESS_LOOPBACK -} MA_AUDIOCLIENT_ACTIVATION_TYPE; - -/* https://docs.microsoft.com/en-us/windows/win32/api/audioclientactivationparams/ne-audioclientactivationparams-process_loopback_mode */ -typedef enum -{ - MA_PROCESS_LOOPBACK_MODE_INCLUDE_TARGET_PROCESS_TREE, - MA_PROCESS_LOOPBACK_MODE_EXCLUDE_TARGET_PROCESS_TREE -} MA_PROCESS_LOOPBACK_MODE; - -/* https://docs.microsoft.com/en-us/windows/win32/api/audioclientactivationparams/ns-audioclientactivationparams-audioclient_process_loopback_params */ -typedef struct -{ - DWORD TargetProcessId; - MA_PROCESS_LOOPBACK_MODE ProcessLoopbackMode; -} MA_AUDIOCLIENT_PROCESS_LOOPBACK_PARAMS; - -#if defined(_MSC_VER) && !defined(__clang__) - #pragma warning(push) - #pragma warning(disable:4201) /* nonstandard extension used: nameless struct/union */ -#elif defined(__clang__) || (defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 8))) - #pragma GCC diagnostic push - #pragma GCC diagnostic ignored "-Wpedantic" /* For ISO C99 doesn't support unnamed structs/unions [-Wpedantic] */ - #if defined(__clang__) - #pragma GCC diagnostic ignored "-Wc11-extensions" /* anonymous unions are a C11 extension */ - #endif -#endif -/* https://docs.microsoft.com/en-us/windows/win32/api/audioclientactivationparams/ns-audioclientactivationparams-audioclient_activation_params */ -typedef struct -{ - MA_AUDIOCLIENT_ACTIVATION_TYPE ActivationType; - union - { - MA_AUDIOCLIENT_PROCESS_LOOPBACK_PARAMS ProcessLoopbackParams; - }; -} MA_AUDIOCLIENT_ACTIVATION_PARAMS; -#if defined(_MSC_VER) && !defined(__clang__) - #pragma warning(pop) -#elif defined(__clang__) || (defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 8))) - #pragma GCC diagnostic pop -#endif - -#define MA_VIRTUAL_AUDIO_DEVICE_PROCESS_LOOPBACK L"VAD\\Process_Loopback" - -static ma_result ma_context_get_IAudioClient__wasapi(ma_context* pContext, ma_device_type deviceType, const ma_device_id* pDeviceID, ma_uint32 loopbackProcessID, ma_bool32 loopbackProcessExclude, ma_IAudioClient** ppAudioClient, ma_WASAPIDeviceInterface** ppDeviceInterface) -{ - ma_result result; - ma_bool32 usingProcessLoopback = MA_FALSE; - MA_AUDIOCLIENT_ACTIVATION_PARAMS audioclientActivationParams; - MA_PROPVARIANT activationParams; - MA_PROPVARIANT* pActivationParams = NULL; - ma_device_id virtualDeviceID; - - /* Activation parameters specific to loopback mode. Note that process-specific loopback will only work when a default device ID is specified. */ - if (deviceType == ma_device_type_loopback && loopbackProcessID != 0 && pDeviceID == NULL) { - usingProcessLoopback = MA_TRUE; - } - - if (usingProcessLoopback) { - MA_ZERO_OBJECT(&audioclientActivationParams); - audioclientActivationParams.ActivationType = MA_AUDIOCLIENT_ACTIVATION_TYPE_PROCESS_LOOPBACK; - audioclientActivationParams.ProcessLoopbackParams.ProcessLoopbackMode = (loopbackProcessExclude) ? MA_PROCESS_LOOPBACK_MODE_EXCLUDE_TARGET_PROCESS_TREE : MA_PROCESS_LOOPBACK_MODE_INCLUDE_TARGET_PROCESS_TREE; - audioclientActivationParams.ProcessLoopbackParams.TargetProcessId = (DWORD)loopbackProcessID; - - ma_PropVariantInit(&activationParams); - activationParams.vt = MA_VT_BLOB; - activationParams.blob.cbSize = sizeof(audioclientActivationParams); - activationParams.blob.pBlobData = (BYTE*)&audioclientActivationParams; - pActivationParams = &activationParams; - - /* When requesting a specific device ID we need to use a special device ID. */ - MA_COPY_MEMORY(virtualDeviceID.wasapi, MA_VIRTUAL_AUDIO_DEVICE_PROCESS_LOOPBACK, (wcslen(MA_VIRTUAL_AUDIO_DEVICE_PROCESS_LOOPBACK) + 1) * sizeof(wchar_t)); /* +1 for the null terminator. */ - pDeviceID = &virtualDeviceID; - } else { - pActivationParams = NULL; /* No activation parameters required. */ - } - -#if defined(MA_WIN32_DESKTOP) || defined(MA_WIN32_GDK) - result = ma_context_get_IAudioClient_Desktop__wasapi(pContext, deviceType, pDeviceID, pActivationParams, ppAudioClient, ppDeviceInterface); -#else - result = ma_context_get_IAudioClient_UWP__wasapi(pContext, deviceType, pDeviceID, pActivationParams, ppAudioClient, ppDeviceInterface); -#endif - - /* - If loopback mode was requested with a process ID and initialization failed, it could be because it's - trying to run on an older version of Windows where it's not supported. We need to let the caller - know about this with a log message. - */ - if (result != MA_SUCCESS) { - if (usingProcessLoopback) { - ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[WASAPI] Loopback mode requested to %s process ID %u, but initialization failed. Support for this feature begins with Windows 10 Build 20348. Confirm your version of Windows or consider not using process-specific loopback.\n", (loopbackProcessExclude) ? "exclude" : "include", loopbackProcessID); - } - } - - return result; -} - - -static ma_result ma_context_enumerate_devices__wasapi(ma_context* pContext, ma_enum_devices_callback_proc callback, void* pUserData) -{ - /* Different enumeration for desktop and UWP. */ -#if defined(MA_WIN32_DESKTOP) || defined(MA_WIN32_GDK) - /* Desktop */ - HRESULT hr; - ma_IMMDeviceEnumerator* pDeviceEnumerator; - - hr = ma_CoCreateInstance(pContext, &MA_CLSID_MMDeviceEnumerator, NULL, CLSCTX_ALL, &MA_IID_IMMDeviceEnumerator, (void**)&pDeviceEnumerator); - if (FAILED(hr)) { - ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to create device enumerator."); - return ma_result_from_HRESULT(hr); - } - - ma_context_enumerate_devices_by_type__wasapi(pContext, pDeviceEnumerator, ma_device_type_playback, callback, pUserData); - ma_context_enumerate_devices_by_type__wasapi(pContext, pDeviceEnumerator, ma_device_type_capture, callback, pUserData); - - ma_IMMDeviceEnumerator_Release(pDeviceEnumerator); -#else - /* - UWP - - The MMDevice API is only supported on desktop applications. For now, while I'm still figuring out how to properly enumerate - over devices without using MMDevice, I'm restricting devices to defaults. - - Hint: DeviceInformation::FindAllAsync() with DeviceClass.AudioCapture/AudioRender. https://blogs.windows.com/buildingapps/2014/05/15/real-time-audio-in-windows-store-and-windows-phone-apps/ - */ - if (callback) { - ma_bool32 cbResult = MA_TRUE; - - /* Playback. */ - if (cbResult) { - ma_device_info deviceInfo; - MA_ZERO_OBJECT(&deviceInfo); - ma_strncpy_s(deviceInfo.name, sizeof(deviceInfo.name), MA_DEFAULT_PLAYBACK_DEVICE_NAME, (size_t)-1); - deviceInfo.isDefault = MA_TRUE; - cbResult = callback(pContext, ma_device_type_playback, &deviceInfo, pUserData); - } - - /* Capture. */ - if (cbResult) { - ma_device_info deviceInfo; - MA_ZERO_OBJECT(&deviceInfo); - ma_strncpy_s(deviceInfo.name, sizeof(deviceInfo.name), MA_DEFAULT_CAPTURE_DEVICE_NAME, (size_t)-1); - deviceInfo.isDefault = MA_TRUE; - cbResult = callback(pContext, ma_device_type_capture, &deviceInfo, pUserData); - } - } -#endif - - return MA_SUCCESS; -} - -static ma_result ma_context_get_device_info__wasapi(ma_context* pContext, ma_device_type deviceType, const ma_device_id* pDeviceID, ma_device_info* pDeviceInfo) -{ -#if defined(MA_WIN32_DESKTOP) || defined(MA_WIN32_GDK) - ma_result result; - ma_IMMDevice* pMMDevice = NULL; - WCHAR* pDefaultDeviceID = NULL; - - result = ma_context_get_MMDevice__wasapi(pContext, deviceType, pDeviceID, &pMMDevice); - if (result != MA_SUCCESS) { - return result; - } - - /* We need the default device ID so we can set the isDefault flag in the device info. */ - pDefaultDeviceID = ma_context_get_default_device_id__wasapi(pContext, deviceType); - - result = ma_context_get_device_info_from_MMDevice__wasapi(pContext, pMMDevice, pDefaultDeviceID, MA_FALSE, pDeviceInfo); /* MA_FALSE = !onlySimpleInfo. */ - - if (pDefaultDeviceID != NULL) { - ma_CoTaskMemFree(pContext, pDefaultDeviceID); - pDefaultDeviceID = NULL; - } - - ma_IMMDevice_Release(pMMDevice); - - return result; -#else - ma_IAudioClient* pAudioClient; - ma_result result; - - /* UWP currently only uses default devices. */ - if (deviceType == ma_device_type_playback) { - ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), MA_DEFAULT_PLAYBACK_DEVICE_NAME, (size_t)-1); - } else { - ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), MA_DEFAULT_CAPTURE_DEVICE_NAME, (size_t)-1); - } - - result = ma_context_get_IAudioClient_UWP__wasapi(pContext, deviceType, pDeviceID, NULL, &pAudioClient, NULL); - if (result != MA_SUCCESS) { - return result; - } - - result = ma_context_get_device_info_from_IAudioClient__wasapi(pContext, NULL, pAudioClient, pDeviceInfo); - - pDeviceInfo->isDefault = MA_TRUE; /* UWP only supports default devices. */ - - ma_IAudioClient_Release(pAudioClient); - return result; -#endif -} - -static ma_result ma_device_uninit__wasapi(ma_device* pDevice) -{ - MA_ASSERT(pDevice != NULL); - - #if defined(MA_WIN32_DESKTOP) || defined(MA_WIN32_GDK) - { - if (pDevice->wasapi.pDeviceEnumerator) { - ((ma_IMMDeviceEnumerator*)pDevice->wasapi.pDeviceEnumerator)->lpVtbl->UnregisterEndpointNotificationCallback((ma_IMMDeviceEnumerator*)pDevice->wasapi.pDeviceEnumerator, &pDevice->wasapi.notificationClient); - ma_IMMDeviceEnumerator_Release((ma_IMMDeviceEnumerator*)pDevice->wasapi.pDeviceEnumerator); - } - - ma_mutex_uninit(&pDevice->wasapi.rerouteLock); - } - #endif - - if (pDevice->wasapi.pRenderClient) { - if (pDevice->wasapi.pMappedBufferPlayback != NULL) { - ma_IAudioRenderClient_ReleaseBuffer((ma_IAudioRenderClient*)pDevice->wasapi.pRenderClient, pDevice->wasapi.mappedBufferPlaybackCap, 0); - pDevice->wasapi.pMappedBufferPlayback = NULL; - pDevice->wasapi.mappedBufferPlaybackCap = 0; - pDevice->wasapi.mappedBufferPlaybackLen = 0; - } - - ma_IAudioRenderClient_Release((ma_IAudioRenderClient*)pDevice->wasapi.pRenderClient); - } - if (pDevice->wasapi.pCaptureClient) { - if (pDevice->wasapi.pMappedBufferCapture != NULL) { - ma_IAudioCaptureClient_ReleaseBuffer((ma_IAudioCaptureClient*)pDevice->wasapi.pCaptureClient, pDevice->wasapi.mappedBufferCaptureCap); - pDevice->wasapi.pMappedBufferCapture = NULL; - pDevice->wasapi.mappedBufferCaptureCap = 0; - pDevice->wasapi.mappedBufferCaptureLen = 0; - } - - ma_IAudioCaptureClient_Release((ma_IAudioCaptureClient*)pDevice->wasapi.pCaptureClient); - } - - if (pDevice->wasapi.pAudioClientPlayback) { - ma_IAudioClient_Release((ma_IAudioClient*)pDevice->wasapi.pAudioClientPlayback); - } - if (pDevice->wasapi.pAudioClientCapture) { - ma_IAudioClient_Release((ma_IAudioClient*)pDevice->wasapi.pAudioClientCapture); - } - - if (pDevice->wasapi.hEventPlayback) { - CloseHandle((HANDLE)pDevice->wasapi.hEventPlayback); - } - if (pDevice->wasapi.hEventCapture) { - CloseHandle((HANDLE)pDevice->wasapi.hEventCapture); - } - - return MA_SUCCESS; -} - - -typedef struct -{ - /* Input. */ - ma_format formatIn; - ma_uint32 channelsIn; - ma_uint32 sampleRateIn; - ma_channel channelMapIn[MA_MAX_CHANNELS]; - ma_uint32 periodSizeInFramesIn; - ma_uint32 periodSizeInMillisecondsIn; - ma_uint32 periodsIn; - ma_share_mode shareMode; - ma_performance_profile performanceProfile; - ma_bool32 noAutoConvertSRC; - ma_bool32 noDefaultQualitySRC; - ma_bool32 noHardwareOffloading; - ma_uint32 loopbackProcessID; - ma_bool32 loopbackProcessExclude; - - /* Output. */ - ma_IAudioClient* pAudioClient; - ma_IAudioRenderClient* pRenderClient; - ma_IAudioCaptureClient* pCaptureClient; - ma_format formatOut; - ma_uint32 channelsOut; - ma_uint32 sampleRateOut; - ma_channel channelMapOut[MA_MAX_CHANNELS]; - ma_uint32 periodSizeInFramesOut; - ma_uint32 periodsOut; - ma_bool32 usingAudioClient3; - char deviceName[256]; - ma_device_id id; -} ma_device_init_internal_data__wasapi; - -static ma_result ma_device_init_internal__wasapi(ma_context* pContext, ma_device_type deviceType, const ma_device_id* pDeviceID, ma_device_init_internal_data__wasapi* pData) -{ - HRESULT hr; - ma_result result = MA_SUCCESS; - const char* errorMsg = ""; - MA_AUDCLNT_SHAREMODE shareMode = MA_AUDCLNT_SHAREMODE_SHARED; - DWORD streamFlags = 0; - MA_REFERENCE_TIME periodDurationInMicroseconds; - ma_bool32 wasInitializedUsingIAudioClient3 = MA_FALSE; - MA_WAVEFORMATEXTENSIBLE wf; - ma_WASAPIDeviceInterface* pDeviceInterface = NULL; - ma_IAudioClient2* pAudioClient2; - ma_uint32 nativeSampleRate; - ma_bool32 usingProcessLoopback = MA_FALSE; - - MA_ASSERT(pContext != NULL); - MA_ASSERT(pData != NULL); - - /* This function is only used to initialize one device type: either playback, capture or loopback. Never full-duplex. */ - if (deviceType == ma_device_type_duplex) { - return MA_INVALID_ARGS; - } - - usingProcessLoopback = deviceType == ma_device_type_loopback && pData->loopbackProcessID != 0 && pDeviceID == NULL; - - pData->pAudioClient = NULL; - pData->pRenderClient = NULL; - pData->pCaptureClient = NULL; - - streamFlags = MA_AUDCLNT_STREAMFLAGS_EVENTCALLBACK; - if (!pData->noAutoConvertSRC && pData->sampleRateIn != 0 && pData->shareMode != ma_share_mode_exclusive) { /* <-- Exclusive streams must use the native sample rate. */ - streamFlags |= MA_AUDCLNT_STREAMFLAGS_AUTOCONVERTPCM; - } - if (!pData->noDefaultQualitySRC && pData->sampleRateIn != 0 && (streamFlags & MA_AUDCLNT_STREAMFLAGS_AUTOCONVERTPCM) != 0) { - streamFlags |= MA_AUDCLNT_STREAMFLAGS_SRC_DEFAULT_QUALITY; - } - if (deviceType == ma_device_type_loopback) { - streamFlags |= MA_AUDCLNT_STREAMFLAGS_LOOPBACK; - } - - result = ma_context_get_IAudioClient__wasapi(pContext, deviceType, pDeviceID, pData->loopbackProcessID, pData->loopbackProcessExclude, &pData->pAudioClient, &pDeviceInterface); - if (result != MA_SUCCESS) { - goto done; - } - - MA_ZERO_OBJECT(&wf); - - /* Try enabling hardware offloading. */ - if (!pData->noHardwareOffloading) { - hr = ma_IAudioClient_QueryInterface(pData->pAudioClient, &MA_IID_IAudioClient2, (void**)&pAudioClient2); - if (SUCCEEDED(hr)) { - BOOL isHardwareOffloadingSupported = 0; - hr = ma_IAudioClient2_IsOffloadCapable(pAudioClient2, MA_AudioCategory_Other, &isHardwareOffloadingSupported); - if (SUCCEEDED(hr) && isHardwareOffloadingSupported) { - ma_AudioClientProperties clientProperties; - MA_ZERO_OBJECT(&clientProperties); - clientProperties.cbSize = sizeof(clientProperties); - clientProperties.bIsOffload = 1; - clientProperties.eCategory = MA_AudioCategory_Other; - ma_IAudioClient2_SetClientProperties(pAudioClient2, &clientProperties); - } - - pAudioClient2->lpVtbl->Release(pAudioClient2); - } - } - - /* Here is where we try to determine the best format to use with the device. If the client if wanting exclusive mode, first try finding the best format for that. If this fails, fall back to shared mode. */ - result = MA_FORMAT_NOT_SUPPORTED; - if (pData->shareMode == ma_share_mode_exclusive) { - #if defined(MA_WIN32_DESKTOP) || defined(MA_WIN32_GDK) - /* In exclusive mode on desktop we always use the backend's native format. */ - ma_IPropertyStore* pStore = NULL; - hr = ma_IMMDevice_OpenPropertyStore(pDeviceInterface, STGM_READ, &pStore); - if (SUCCEEDED(hr)) { - MA_PROPVARIANT prop; - ma_PropVariantInit(&prop); - hr = ma_IPropertyStore_GetValue(pStore, &MA_PKEY_AudioEngine_DeviceFormat, &prop); - if (SUCCEEDED(hr)) { - MA_WAVEFORMATEX* pActualFormat = (MA_WAVEFORMATEX*)prop.blob.pBlobData; - hr = ma_IAudioClient_IsFormatSupported((ma_IAudioClient*)pData->pAudioClient, MA_AUDCLNT_SHAREMODE_EXCLUSIVE, pActualFormat, NULL); - if (SUCCEEDED(hr)) { - MA_COPY_MEMORY(&wf, pActualFormat, sizeof(MA_WAVEFORMATEXTENSIBLE)); - } - - ma_PropVariantClear(pContext, &prop); - } - - ma_IPropertyStore_Release(pStore); - } - #else - /* - I do not know how to query the device's native format on UWP so for now I'm just disabling support for - exclusive mode. The alternative is to enumerate over different formats and check IsFormatSupported() - until you find one that works. - - TODO: Add support for exclusive mode to UWP. - */ - hr = S_FALSE; - #endif - - if (hr == S_OK) { - shareMode = MA_AUDCLNT_SHAREMODE_EXCLUSIVE; - result = MA_SUCCESS; - } else { - result = MA_SHARE_MODE_NOT_SUPPORTED; - } - } else { - /* In shared mode we are always using the format reported by the operating system. */ - MA_WAVEFORMATEXTENSIBLE* pNativeFormat = NULL; - hr = ma_IAudioClient_GetMixFormat((ma_IAudioClient*)pData->pAudioClient, (MA_WAVEFORMATEX**)&pNativeFormat); - if (hr != S_OK) { - /* When using process-specific loopback, GetMixFormat() seems to always fail. */ - if (usingProcessLoopback) { - wf.wFormatTag = WAVE_FORMAT_IEEE_FLOAT; - wf.nChannels = 2; - wf.nSamplesPerSec = 44100; - wf.wBitsPerSample = 32; - wf.nBlockAlign = wf.nChannels * wf.wBitsPerSample / 8; - wf.nAvgBytesPerSec = wf.nSamplesPerSec * wf.nBlockAlign; - wf.cbSize = sizeof(MA_WAVEFORMATEX); - - result = MA_SUCCESS; - } else { - result = MA_FORMAT_NOT_SUPPORTED; - } - } else { - /* - I've seen cases where cbSize will be set to sizeof(WAVEFORMATEX) even though the structure itself - is given the format tag of WAVE_FORMAT_EXTENSIBLE. If the format tag is WAVE_FORMAT_EXTENSIBLE - want to make sure we copy the whole WAVEFORMATEXTENSIBLE structure. Otherwise we'll have to be - safe and only copy the WAVEFORMATEX part. - */ - if (pNativeFormat->wFormatTag == WAVE_FORMAT_EXTENSIBLE) { - MA_COPY_MEMORY(&wf, pNativeFormat, sizeof(MA_WAVEFORMATEXTENSIBLE)); - } else { - /* I've seen a case where cbSize was set to 0. Assume sizeof(WAVEFORMATEX) in this case. */ - size_t cbSize = pNativeFormat->cbSize; - if (cbSize == 0) { - cbSize = sizeof(MA_WAVEFORMATEX); - } - - /* Make sure we don't copy more than the capacity of `wf`. */ - if (cbSize > sizeof(wf)) { - cbSize = sizeof(wf); - } - - MA_COPY_MEMORY(&wf, pNativeFormat, cbSize); - } - - result = MA_SUCCESS; - } - - ma_CoTaskMemFree(pContext, pNativeFormat); - - shareMode = MA_AUDCLNT_SHAREMODE_SHARED; - } - - /* Return an error if we still haven't found a format. */ - if (result != MA_SUCCESS) { - errorMsg = "[WASAPI] Failed to find best device mix format."; - goto done; - } - - /* - Override the native sample rate with the one requested by the caller, but only if we're not using the default sample rate. We'll use - WASAPI to perform the sample rate conversion. - */ - nativeSampleRate = wf.nSamplesPerSec; - if (streamFlags & MA_AUDCLNT_STREAMFLAGS_AUTOCONVERTPCM) { - wf.nSamplesPerSec = (pData->sampleRateIn != 0) ? pData->sampleRateIn : MA_DEFAULT_SAMPLE_RATE; - wf.nAvgBytesPerSec = wf.nSamplesPerSec * wf.nBlockAlign; - } - - pData->formatOut = ma_format_from_WAVEFORMATEX((MA_WAVEFORMATEX*)&wf); - if (pData->formatOut == ma_format_unknown) { - /* - The format isn't supported. This is almost certainly because the exclusive mode format isn't supported by miniaudio. We need to return MA_SHARE_MODE_NOT_SUPPORTED - in this case so that the caller can detect it and fall back to shared mode if desired. We should never get here if shared mode was requested, but just for - completeness we'll check for it and return MA_FORMAT_NOT_SUPPORTED. - */ - if (shareMode == MA_AUDCLNT_SHAREMODE_EXCLUSIVE) { - result = MA_SHARE_MODE_NOT_SUPPORTED; - } else { - result = MA_FORMAT_NOT_SUPPORTED; - } - - errorMsg = "[WASAPI] Native format not supported."; - goto done; - } - - pData->channelsOut = wf.nChannels; - pData->sampleRateOut = wf.nSamplesPerSec; - - /* - Get the internal channel map based on the channel mask. There is a possibility that GetMixFormat() returns - a WAVEFORMATEX instead of a WAVEFORMATEXTENSIBLE, in which case the channel mask will be undefined. In this - case we'll just use the default channel map. - */ - if (wf.wFormatTag == WAVE_FORMAT_EXTENSIBLE || wf.cbSize >= sizeof(MA_WAVEFORMATEXTENSIBLE)) { - ma_channel_mask_to_channel_map__win32(wf.dwChannelMask, pData->channelsOut, pData->channelMapOut); - } else { - ma_channel_map_init_standard(ma_standard_channel_map_microsoft, pData->channelMapOut, ma_countof(pData->channelMapOut), pData->channelsOut); - } - - /* Period size. */ - pData->periodsOut = (pData->periodsIn != 0) ? pData->periodsIn : MA_DEFAULT_PERIODS; - pData->periodSizeInFramesOut = pData->periodSizeInFramesIn; - if (pData->periodSizeInFramesOut == 0) { - if (pData->periodSizeInMillisecondsIn == 0) { - if (pData->performanceProfile == ma_performance_profile_low_latency) { - pData->periodSizeInFramesOut = ma_calculate_buffer_size_in_frames_from_milliseconds(MA_DEFAULT_PERIOD_SIZE_IN_MILLISECONDS_LOW_LATENCY, wf.nSamplesPerSec); - } else { - pData->periodSizeInFramesOut = ma_calculate_buffer_size_in_frames_from_milliseconds(MA_DEFAULT_PERIOD_SIZE_IN_MILLISECONDS_CONSERVATIVE, wf.nSamplesPerSec); - } - } else { - pData->periodSizeInFramesOut = ma_calculate_buffer_size_in_frames_from_milliseconds(pData->periodSizeInMillisecondsIn, wf.nSamplesPerSec); - } - } - - periodDurationInMicroseconds = ((ma_uint64)pData->periodSizeInFramesOut * 1000 * 1000) / wf.nSamplesPerSec; - - - /* Slightly different initialization for shared and exclusive modes. We try exclusive mode first, and if it fails, fall back to shared mode. */ - if (shareMode == MA_AUDCLNT_SHAREMODE_EXCLUSIVE) { - MA_REFERENCE_TIME bufferDuration = periodDurationInMicroseconds * pData->periodsOut * 10; - - /* - If the periodicity is too small, Initialize() will fail with AUDCLNT_E_INVALID_DEVICE_PERIOD. In this case we should just keep increasing - it and trying it again. - */ - hr = E_FAIL; - for (;;) { - hr = ma_IAudioClient_Initialize((ma_IAudioClient*)pData->pAudioClient, shareMode, streamFlags, bufferDuration, bufferDuration, (MA_WAVEFORMATEX*)&wf, NULL); - if (hr == MA_AUDCLNT_E_INVALID_DEVICE_PERIOD) { - if (bufferDuration > 500*10000) { - break; - } else { - if (bufferDuration == 0) { /* <-- Just a sanity check to prevent an infinite loop. Should never happen, but it makes me feel better. */ - break; - } - - bufferDuration = bufferDuration * 2; - continue; - } - } else { - break; - } - } - - if (hr == MA_AUDCLNT_E_BUFFER_SIZE_NOT_ALIGNED) { - ma_uint32 bufferSizeInFrames; - hr = ma_IAudioClient_GetBufferSize((ma_IAudioClient*)pData->pAudioClient, &bufferSizeInFrames); - if (SUCCEEDED(hr)) { - bufferDuration = (MA_REFERENCE_TIME)((10000.0 * 1000 / wf.nSamplesPerSec * bufferSizeInFrames) + 0.5); - - /* Unfortunately we need to release and re-acquire the audio client according to MSDN. Seems silly - why not just call IAudioClient_Initialize() again?! */ - ma_IAudioClient_Release((ma_IAudioClient*)pData->pAudioClient); - - #if defined(MA_WIN32_DESKTOP) || defined(MA_WIN32_GDK) - hr = ma_IMMDevice_Activate(pDeviceInterface, &MA_IID_IAudioClient, CLSCTX_ALL, NULL, (void**)&pData->pAudioClient); - #else - hr = ma_IUnknown_QueryInterface(pDeviceInterface, &MA_IID_IAudioClient, (void**)&pData->pAudioClient); - #endif - - if (SUCCEEDED(hr)) { - hr = ma_IAudioClient_Initialize((ma_IAudioClient*)pData->pAudioClient, shareMode, streamFlags, bufferDuration, bufferDuration, (MA_WAVEFORMATEX*)&wf, NULL); - } - } - } - - if (FAILED(hr)) { - /* Failed to initialize in exclusive mode. Don't fall back to shared mode - instead tell the client about it. They can reinitialize in shared mode if they want. */ - if (hr == E_ACCESSDENIED) { - errorMsg = "[WASAPI] Failed to initialize device in exclusive mode. Access denied.", result = MA_ACCESS_DENIED; - } else if (hr == MA_AUDCLNT_E_DEVICE_IN_USE) { - errorMsg = "[WASAPI] Failed to initialize device in exclusive mode. Device in use.", result = MA_BUSY; - } else { - errorMsg = "[WASAPI] Failed to initialize device in exclusive mode."; result = ma_result_from_HRESULT(hr); - } - goto done; - } - } - - if (shareMode == MA_AUDCLNT_SHAREMODE_SHARED) { - /* - Low latency shared mode via IAudioClient3. - - NOTE - ==== - Contrary to the documentation on MSDN (https://docs.microsoft.com/en-us/windows/win32/api/audioclient/nf-audioclient-iaudioclient3-initializesharedaudiostream), the - use of AUDCLNT_STREAMFLAGS_AUTOCONVERTPCM and AUDCLNT_STREAMFLAGS_SRC_DEFAULT_QUALITY with IAudioClient3_InitializeSharedAudioStream() absolutely does not work. Using - any of these flags will result in HRESULT code 0x88890021. The other problem is that calling IAudioClient3_GetSharedModeEnginePeriod() with a sample rate different to - that returned by IAudioClient_GetMixFormat() also results in an error. I'm therefore disabling low-latency shared mode with AUDCLNT_STREAMFLAGS_AUTOCONVERTPCM. - */ - #ifndef MA_WASAPI_NO_LOW_LATENCY_SHARED_MODE - { - if ((streamFlags & MA_AUDCLNT_STREAMFLAGS_AUTOCONVERTPCM) == 0 || nativeSampleRate == wf.nSamplesPerSec) { - ma_IAudioClient3* pAudioClient3 = NULL; - hr = ma_IAudioClient_QueryInterface(pData->pAudioClient, &MA_IID_IAudioClient3, (void**)&pAudioClient3); - if (SUCCEEDED(hr)) { - ma_uint32 defaultPeriodInFrames; - ma_uint32 fundamentalPeriodInFrames; - ma_uint32 minPeriodInFrames; - ma_uint32 maxPeriodInFrames; - hr = ma_IAudioClient3_GetSharedModeEnginePeriod(pAudioClient3, (MA_WAVEFORMATEX*)&wf, &defaultPeriodInFrames, &fundamentalPeriodInFrames, &minPeriodInFrames, &maxPeriodInFrames); - if (SUCCEEDED(hr)) { - ma_uint32 desiredPeriodInFrames = pData->periodSizeInFramesOut; - ma_uint32 actualPeriodInFrames = desiredPeriodInFrames; - - /* Make sure the period size is a multiple of fundamentalPeriodInFrames. */ - actualPeriodInFrames = actualPeriodInFrames / fundamentalPeriodInFrames; - actualPeriodInFrames = actualPeriodInFrames * fundamentalPeriodInFrames; - - /* The period needs to be clamped between minPeriodInFrames and maxPeriodInFrames. */ - actualPeriodInFrames = ma_clamp(actualPeriodInFrames, minPeriodInFrames, maxPeriodInFrames); - - ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_DEBUG, "[WASAPI] Trying IAudioClient3_InitializeSharedAudioStream(actualPeriodInFrames=%d)\n", actualPeriodInFrames); - ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_DEBUG, " defaultPeriodInFrames=%d\n", defaultPeriodInFrames); - ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_DEBUG, " fundamentalPeriodInFrames=%d\n", fundamentalPeriodInFrames); - ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_DEBUG, " minPeriodInFrames=%d\n", minPeriodInFrames); - ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_DEBUG, " maxPeriodInFrames=%d\n", maxPeriodInFrames); - - /* If the client requested a largish buffer than we don't actually want to use low latency shared mode because it forces small buffers. */ - if (actualPeriodInFrames >= desiredPeriodInFrames) { - /* - MA_AUDCLNT_STREAMFLAGS_AUTOCONVERTPCM | MA_AUDCLNT_STREAMFLAGS_SRC_DEFAULT_QUALITY must not be in the stream flags. If either of these are specified, - IAudioClient3_InitializeSharedAudioStream() will fail. - */ - hr = ma_IAudioClient3_InitializeSharedAudioStream(pAudioClient3, streamFlags & ~(MA_AUDCLNT_STREAMFLAGS_AUTOCONVERTPCM | MA_AUDCLNT_STREAMFLAGS_SRC_DEFAULT_QUALITY), actualPeriodInFrames, (MA_WAVEFORMATEX*)&wf, NULL); - if (SUCCEEDED(hr)) { - wasInitializedUsingIAudioClient3 = MA_TRUE; - pData->periodSizeInFramesOut = actualPeriodInFrames; - - ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_DEBUG, "[WASAPI] Using IAudioClient3\n"); - ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_DEBUG, " periodSizeInFramesOut=%d\n", pData->periodSizeInFramesOut); - } else { - ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_DEBUG, "[WASAPI] IAudioClient3_InitializeSharedAudioStream failed. Falling back to IAudioClient.\n"); - } - } else { - ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_DEBUG, "[WASAPI] Not using IAudioClient3 because the desired period size is larger than the maximum supported by IAudioClient3.\n"); - } - } else { - ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_DEBUG, "[WASAPI] IAudioClient3_GetSharedModeEnginePeriod failed. Falling back to IAudioClient.\n"); - } - - ma_IAudioClient3_Release(pAudioClient3); - pAudioClient3 = NULL; - } - } - } - #else - { - ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_DEBUG, "[WASAPI] Not using IAudioClient3 because MA_WASAPI_NO_LOW_LATENCY_SHARED_MODE is enabled.\n"); - } - #endif - - /* If we don't have an IAudioClient3 then we need to use the normal initialization routine. */ - if (!wasInitializedUsingIAudioClient3) { - MA_REFERENCE_TIME bufferDuration = periodDurationInMicroseconds * pData->periodsOut * 10; /* <-- Multiply by 10 for microseconds to 100-nanoseconds. */ - hr = ma_IAudioClient_Initialize((ma_IAudioClient*)pData->pAudioClient, shareMode, streamFlags, bufferDuration, 0, (const MA_WAVEFORMATEX*)&wf, NULL); - if (FAILED(hr)) { - if (hr == E_ACCESSDENIED) { - errorMsg = "[WASAPI] Failed to initialize device. Access denied.", result = MA_ACCESS_DENIED; - } else if (hr == MA_AUDCLNT_E_DEVICE_IN_USE) { - errorMsg = "[WASAPI] Failed to initialize device. Device in use.", result = MA_BUSY; - } else { - errorMsg = "[WASAPI] Failed to initialize device.", result = ma_result_from_HRESULT(hr); - } - - goto done; - } - } - } - - if (!wasInitializedUsingIAudioClient3) { - ma_uint32 bufferSizeInFrames = 0; - hr = ma_IAudioClient_GetBufferSize((ma_IAudioClient*)pData->pAudioClient, &bufferSizeInFrames); - if (FAILED(hr)) { - errorMsg = "[WASAPI] Failed to get audio client's actual buffer size.", result = ma_result_from_HRESULT(hr); - goto done; - } - - /* - When using process loopback mode, retrieval of the buffer size seems to result in totally - incorrect values. In this case we'll just assume it's the same size as what we requested - when we initialized the client. - */ - if (usingProcessLoopback) { - bufferSizeInFrames = (ma_uint32)((periodDurationInMicroseconds * pData->periodsOut) * pData->sampleRateOut / 1000000); - } - - pData->periodSizeInFramesOut = bufferSizeInFrames / pData->periodsOut; - } - - pData->usingAudioClient3 = wasInitializedUsingIAudioClient3; - - - if (deviceType == ma_device_type_playback) { - result = ma_device_create_IAudioClient_service__wasapi(pContext, deviceType, (ma_IAudioClient*)pData->pAudioClient, (void**)&pData->pRenderClient); - } else { - result = ma_device_create_IAudioClient_service__wasapi(pContext, deviceType, (ma_IAudioClient*)pData->pAudioClient, (void**)&pData->pCaptureClient); - } - - /*if (FAILED(hr)) {*/ - if (result != MA_SUCCESS) { - errorMsg = "[WASAPI] Failed to get audio client service."; - goto done; - } - - - /* Grab the name of the device. */ - #if defined(MA_WIN32_DESKTOP) || defined(MA_WIN32_GDK) - { - ma_IPropertyStore *pProperties; - hr = ma_IMMDevice_OpenPropertyStore(pDeviceInterface, STGM_READ, &pProperties); - if (SUCCEEDED(hr)) { - MA_PROPVARIANT varName; - ma_PropVariantInit(&varName); - hr = ma_IPropertyStore_GetValue(pProperties, &MA_PKEY_Device_FriendlyName, &varName); - if (SUCCEEDED(hr)) { - WideCharToMultiByte(CP_UTF8, 0, varName.pwszVal, -1, pData->deviceName, sizeof(pData->deviceName), 0, FALSE); - ma_PropVariantClear(pContext, &varName); - } - - ma_IPropertyStore_Release(pProperties); - } - } - #endif - - /* - For the WASAPI backend we need to know the actual IDs of the device in order to do automatic - stream routing so that IDs can be compared and we can determine which device has been detached - and whether or not it matches with our ma_device. - */ - #if defined(MA_WIN32_DESKTOP) || defined(MA_WIN32_GDK) - { - /* Desktop */ - ma_context_get_device_id_from_MMDevice__wasapi(pContext, pDeviceInterface, &pData->id); - } - #else - { - /* UWP */ - /* TODO: Implement me. Need to figure out how to get the ID of the default device. */ - } - #endif - -done: - /* Clean up. */ -#if defined(MA_WIN32_DESKTOP) || defined(MA_WIN32_GDK) - if (pDeviceInterface != NULL) { - ma_IMMDevice_Release(pDeviceInterface); - } -#else - if (pDeviceInterface != NULL) { - ma_IUnknown_Release(pDeviceInterface); - } -#endif - - if (result != MA_SUCCESS) { - if (pData->pRenderClient) { - ma_IAudioRenderClient_Release((ma_IAudioRenderClient*)pData->pRenderClient); - pData->pRenderClient = NULL; - } - if (pData->pCaptureClient) { - ma_IAudioCaptureClient_Release((ma_IAudioCaptureClient*)pData->pCaptureClient); - pData->pCaptureClient = NULL; - } - if (pData->pAudioClient) { - ma_IAudioClient_Release((ma_IAudioClient*)pData->pAudioClient); - pData->pAudioClient = NULL; - } - - if (errorMsg != NULL && errorMsg[0] != '\0') { - ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "%s\n", errorMsg); - } - - return result; - } else { - return MA_SUCCESS; - } -} - -static ma_result ma_device_reinit__wasapi(ma_device* pDevice, ma_device_type deviceType) -{ - ma_device_init_internal_data__wasapi data; - ma_result result; - - MA_ASSERT(pDevice != NULL); - - /* We only re-initialize the playback or capture device. Never a full-duplex device. */ - if (deviceType == ma_device_type_duplex) { - return MA_INVALID_ARGS; - } - - - /* - Before reinitializing the device we need to free the previous audio clients. - - There's a known memory leak here. We will be calling this from the routing change callback that - is fired by WASAPI. If we attempt to release the IAudioClient we will deadlock. In my opinion - this is a bug. I'm not sure what I need to do to handle this cleanly, but I think we'll probably - need some system where we post an event, but delay the execution of it until the callback has - returned. I'm not sure how to do this reliably, however. I have set up some infrastructure for - a command thread which might be useful for this. - */ - if (deviceType == ma_device_type_capture || deviceType == ma_device_type_loopback) { - if (pDevice->wasapi.pCaptureClient) { - ma_IAudioCaptureClient_Release((ma_IAudioCaptureClient*)pDevice->wasapi.pCaptureClient); - pDevice->wasapi.pCaptureClient = NULL; - } - - if (pDevice->wasapi.pAudioClientCapture) { - /*ma_device_release_IAudioClient_service__wasapi(pDevice, ma_device_type_capture);*/ - pDevice->wasapi.pAudioClientCapture = NULL; - } - } - - if (deviceType == ma_device_type_playback) { - if (pDevice->wasapi.pRenderClient) { - ma_IAudioRenderClient_Release((ma_IAudioRenderClient*)pDevice->wasapi.pRenderClient); - pDevice->wasapi.pRenderClient = NULL; - } - - if (pDevice->wasapi.pAudioClientPlayback) { - /*ma_device_release_IAudioClient_service__wasapi(pDevice, ma_device_type_playback);*/ - pDevice->wasapi.pAudioClientPlayback = NULL; - } - } - - - if (deviceType == ma_device_type_playback) { - data.formatIn = pDevice->playback.format; - data.channelsIn = pDevice->playback.channels; - MA_COPY_MEMORY(data.channelMapIn, pDevice->playback.channelMap, sizeof(pDevice->playback.channelMap)); - data.shareMode = pDevice->playback.shareMode; - } else { - data.formatIn = pDevice->capture.format; - data.channelsIn = pDevice->capture.channels; - MA_COPY_MEMORY(data.channelMapIn, pDevice->capture.channelMap, sizeof(pDevice->capture.channelMap)); - data.shareMode = pDevice->capture.shareMode; - } - - data.sampleRateIn = pDevice->sampleRate; - data.periodSizeInFramesIn = pDevice->wasapi.originalPeriodSizeInFrames; - data.periodSizeInMillisecondsIn = pDevice->wasapi.originalPeriodSizeInMilliseconds; - data.periodsIn = pDevice->wasapi.originalPeriods; - data.performanceProfile = pDevice->wasapi.originalPerformanceProfile; - data.noAutoConvertSRC = pDevice->wasapi.noAutoConvertSRC; - data.noDefaultQualitySRC = pDevice->wasapi.noDefaultQualitySRC; - data.noHardwareOffloading = pDevice->wasapi.noHardwareOffloading; - data.loopbackProcessID = pDevice->wasapi.loopbackProcessID; - data.loopbackProcessExclude = pDevice->wasapi.loopbackProcessExclude; - result = ma_device_init_internal__wasapi(pDevice->pContext, deviceType, NULL, &data); - if (result != MA_SUCCESS) { - return result; - } - - /* At this point we have some new objects ready to go. We need to uninitialize the previous ones and then set the new ones. */ - if (deviceType == ma_device_type_capture || deviceType == ma_device_type_loopback) { - pDevice->wasapi.pAudioClientCapture = data.pAudioClient; - pDevice->wasapi.pCaptureClient = data.pCaptureClient; - - pDevice->capture.internalFormat = data.formatOut; - pDevice->capture.internalChannels = data.channelsOut; - pDevice->capture.internalSampleRate = data.sampleRateOut; - MA_COPY_MEMORY(pDevice->capture.internalChannelMap, data.channelMapOut, sizeof(data.channelMapOut)); - pDevice->capture.internalPeriodSizeInFrames = data.periodSizeInFramesOut; - pDevice->capture.internalPeriods = data.periodsOut; - ma_strcpy_s(pDevice->capture.name, sizeof(pDevice->capture.name), data.deviceName); - - ma_IAudioClient_SetEventHandle((ma_IAudioClient*)pDevice->wasapi.pAudioClientCapture, (HANDLE)pDevice->wasapi.hEventCapture); - - pDevice->wasapi.periodSizeInFramesCapture = data.periodSizeInFramesOut; - ma_IAudioClient_GetBufferSize((ma_IAudioClient*)pDevice->wasapi.pAudioClientCapture, &pDevice->wasapi.actualBufferSizeInFramesCapture); - - /* We must always have a valid ID. */ - ma_strcpy_s_WCHAR(pDevice->capture.id.wasapi, sizeof(pDevice->capture.id.wasapi), data.id.wasapi); - } - - if (deviceType == ma_device_type_playback) { - pDevice->wasapi.pAudioClientPlayback = data.pAudioClient; - pDevice->wasapi.pRenderClient = data.pRenderClient; - - pDevice->playback.internalFormat = data.formatOut; - pDevice->playback.internalChannels = data.channelsOut; - pDevice->playback.internalSampleRate = data.sampleRateOut; - MA_COPY_MEMORY(pDevice->playback.internalChannelMap, data.channelMapOut, sizeof(data.channelMapOut)); - pDevice->playback.internalPeriodSizeInFrames = data.periodSizeInFramesOut; - pDevice->playback.internalPeriods = data.periodsOut; - ma_strcpy_s(pDevice->playback.name, sizeof(pDevice->playback.name), data.deviceName); - - ma_IAudioClient_SetEventHandle((ma_IAudioClient*)pDevice->wasapi.pAudioClientPlayback, (HANDLE)pDevice->wasapi.hEventPlayback); - - pDevice->wasapi.periodSizeInFramesPlayback = data.periodSizeInFramesOut; - ma_IAudioClient_GetBufferSize((ma_IAudioClient*)pDevice->wasapi.pAudioClientPlayback, &pDevice->wasapi.actualBufferSizeInFramesPlayback); - - /* We must always have a valid ID because rerouting will look at it. */ - ma_strcpy_s_WCHAR(pDevice->playback.id.wasapi, sizeof(pDevice->playback.id.wasapi), data.id.wasapi); - } - - return MA_SUCCESS; -} - -static ma_result ma_device_init__wasapi(ma_device* pDevice, const ma_device_config* pConfig, ma_device_descriptor* pDescriptorPlayback, ma_device_descriptor* pDescriptorCapture) -{ - ma_result result = MA_SUCCESS; - -#if defined(MA_WIN32_DESKTOP) || defined(MA_WIN32_GDK) - HRESULT hr; - ma_IMMDeviceEnumerator* pDeviceEnumerator; -#endif - - MA_ASSERT(pDevice != NULL); - - MA_ZERO_OBJECT(&pDevice->wasapi); - pDevice->wasapi.usage = pConfig->wasapi.usage; - pDevice->wasapi.noAutoConvertSRC = pConfig->wasapi.noAutoConvertSRC; - pDevice->wasapi.noDefaultQualitySRC = pConfig->wasapi.noDefaultQualitySRC; - pDevice->wasapi.noHardwareOffloading = pConfig->wasapi.noHardwareOffloading; - pDevice->wasapi.loopbackProcessID = pConfig->wasapi.loopbackProcessID; - pDevice->wasapi.loopbackProcessExclude = pConfig->wasapi.loopbackProcessExclude; - - /* Exclusive mode is not allowed with loopback. */ - if (pConfig->deviceType == ma_device_type_loopback && pConfig->playback.shareMode == ma_share_mode_exclusive) { - return MA_INVALID_DEVICE_CONFIG; - } - - if (pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex || pConfig->deviceType == ma_device_type_loopback) { - ma_device_init_internal_data__wasapi data; - data.formatIn = pDescriptorCapture->format; - data.channelsIn = pDescriptorCapture->channels; - data.sampleRateIn = pDescriptorCapture->sampleRate; - MA_COPY_MEMORY(data.channelMapIn, pDescriptorCapture->channelMap, sizeof(pDescriptorCapture->channelMap)); - data.periodSizeInFramesIn = pDescriptorCapture->periodSizeInFrames; - data.periodSizeInMillisecondsIn = pDescriptorCapture->periodSizeInMilliseconds; - data.periodsIn = pDescriptorCapture->periodCount; - data.shareMode = pDescriptorCapture->shareMode; - data.performanceProfile = pConfig->performanceProfile; - data.noAutoConvertSRC = pConfig->wasapi.noAutoConvertSRC; - data.noDefaultQualitySRC = pConfig->wasapi.noDefaultQualitySRC; - data.noHardwareOffloading = pConfig->wasapi.noHardwareOffloading; - data.loopbackProcessID = pConfig->wasapi.loopbackProcessID; - data.loopbackProcessExclude = pConfig->wasapi.loopbackProcessExclude; - - result = ma_device_init_internal__wasapi(pDevice->pContext, (pConfig->deviceType == ma_device_type_loopback) ? ma_device_type_loopback : ma_device_type_capture, pDescriptorCapture->pDeviceID, &data); - if (result != MA_SUCCESS) { - return result; - } - - pDevice->wasapi.pAudioClientCapture = data.pAudioClient; - pDevice->wasapi.pCaptureClient = data.pCaptureClient; - pDevice->wasapi.originalPeriodSizeInMilliseconds = pDescriptorCapture->periodSizeInMilliseconds; - pDevice->wasapi.originalPeriodSizeInFrames = pDescriptorCapture->periodSizeInFrames; - pDevice->wasapi.originalPeriods = pDescriptorCapture->periodCount; - pDevice->wasapi.originalPerformanceProfile = pConfig->performanceProfile; - - /* - The event for capture needs to be manual reset for the same reason as playback. We keep the initial state set to unsignaled, - however, because we want to block until we actually have something for the first call to ma_device_read(). - */ - pDevice->wasapi.hEventCapture = (ma_handle)CreateEventA(NULL, FALSE, FALSE, NULL); /* Auto reset, unsignaled by default. */ - if (pDevice->wasapi.hEventCapture == NULL) { - result = ma_result_from_GetLastError(GetLastError()); - - if (pDevice->wasapi.pCaptureClient != NULL) { - ma_IAudioCaptureClient_Release((ma_IAudioCaptureClient*)pDevice->wasapi.pCaptureClient); - pDevice->wasapi.pCaptureClient = NULL; - } - if (pDevice->wasapi.pAudioClientCapture != NULL) { - ma_IAudioClient_Release((ma_IAudioClient*)pDevice->wasapi.pAudioClientCapture); - pDevice->wasapi.pAudioClientCapture = NULL; - } - - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to create event for capture."); - return result; - } - ma_IAudioClient_SetEventHandle((ma_IAudioClient*)pDevice->wasapi.pAudioClientCapture, (HANDLE)pDevice->wasapi.hEventCapture); - - pDevice->wasapi.periodSizeInFramesCapture = data.periodSizeInFramesOut; - ma_IAudioClient_GetBufferSize((ma_IAudioClient*)pDevice->wasapi.pAudioClientCapture, &pDevice->wasapi.actualBufferSizeInFramesCapture); - - /* We must always have a valid ID. */ - ma_strcpy_s_WCHAR(pDevice->capture.id.wasapi, sizeof(pDevice->capture.id.wasapi), data.id.wasapi); - - /* The descriptor needs to be updated with actual values. */ - pDescriptorCapture->format = data.formatOut; - pDescriptorCapture->channels = data.channelsOut; - pDescriptorCapture->sampleRate = data.sampleRateOut; - MA_COPY_MEMORY(pDescriptorCapture->channelMap, data.channelMapOut, sizeof(data.channelMapOut)); - pDescriptorCapture->periodSizeInFrames = data.periodSizeInFramesOut; - pDescriptorCapture->periodCount = data.periodsOut; - } - - if (pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) { - ma_device_init_internal_data__wasapi data; - data.formatIn = pDescriptorPlayback->format; - data.channelsIn = pDescriptorPlayback->channels; - data.sampleRateIn = pDescriptorPlayback->sampleRate; - MA_COPY_MEMORY(data.channelMapIn, pDescriptorPlayback->channelMap, sizeof(pDescriptorPlayback->channelMap)); - data.periodSizeInFramesIn = pDescriptorPlayback->periodSizeInFrames; - data.periodSizeInMillisecondsIn = pDescriptorPlayback->periodSizeInMilliseconds; - data.periodsIn = pDescriptorPlayback->periodCount; - data.shareMode = pDescriptorPlayback->shareMode; - data.performanceProfile = pConfig->performanceProfile; - data.noAutoConvertSRC = pConfig->wasapi.noAutoConvertSRC; - data.noDefaultQualitySRC = pConfig->wasapi.noDefaultQualitySRC; - data.noHardwareOffloading = pConfig->wasapi.noHardwareOffloading; - data.loopbackProcessID = pConfig->wasapi.loopbackProcessID; - data.loopbackProcessExclude = pConfig->wasapi.loopbackProcessExclude; - - result = ma_device_init_internal__wasapi(pDevice->pContext, ma_device_type_playback, pDescriptorPlayback->pDeviceID, &data); - if (result != MA_SUCCESS) { - if (pConfig->deviceType == ma_device_type_duplex) { - if (pDevice->wasapi.pCaptureClient != NULL) { - ma_IAudioCaptureClient_Release((ma_IAudioCaptureClient*)pDevice->wasapi.pCaptureClient); - pDevice->wasapi.pCaptureClient = NULL; - } - if (pDevice->wasapi.pAudioClientCapture != NULL) { - ma_IAudioClient_Release((ma_IAudioClient*)pDevice->wasapi.pAudioClientCapture); - pDevice->wasapi.pAudioClientCapture = NULL; - } - - CloseHandle((HANDLE)pDevice->wasapi.hEventCapture); - pDevice->wasapi.hEventCapture = NULL; - } - return result; - } - - pDevice->wasapi.pAudioClientPlayback = data.pAudioClient; - pDevice->wasapi.pRenderClient = data.pRenderClient; - pDevice->wasapi.originalPeriodSizeInMilliseconds = pDescriptorPlayback->periodSizeInMilliseconds; - pDevice->wasapi.originalPeriodSizeInFrames = pDescriptorPlayback->periodSizeInFrames; - pDevice->wasapi.originalPeriods = pDescriptorPlayback->periodCount; - pDevice->wasapi.originalPerformanceProfile = pConfig->performanceProfile; - - /* - The event for playback is needs to be manual reset because we want to explicitly control the fact that it becomes signalled - only after the whole available space has been filled, never before. - - The playback event also needs to be initially set to a signaled state so that the first call to ma_device_write() is able - to get passed WaitForMultipleObjects(). - */ - pDevice->wasapi.hEventPlayback = (ma_handle)CreateEventA(NULL, FALSE, TRUE, NULL); /* Auto reset, signaled by default. */ - if (pDevice->wasapi.hEventPlayback == NULL) { - result = ma_result_from_GetLastError(GetLastError()); - - if (pConfig->deviceType == ma_device_type_duplex) { - if (pDevice->wasapi.pCaptureClient != NULL) { - ma_IAudioCaptureClient_Release((ma_IAudioCaptureClient*)pDevice->wasapi.pCaptureClient); - pDevice->wasapi.pCaptureClient = NULL; - } - if (pDevice->wasapi.pAudioClientCapture != NULL) { - ma_IAudioClient_Release((ma_IAudioClient*)pDevice->wasapi.pAudioClientCapture); - pDevice->wasapi.pAudioClientCapture = NULL; - } - - CloseHandle((HANDLE)pDevice->wasapi.hEventCapture); - pDevice->wasapi.hEventCapture = NULL; - } - - if (pDevice->wasapi.pRenderClient != NULL) { - ma_IAudioRenderClient_Release((ma_IAudioRenderClient*)pDevice->wasapi.pRenderClient); - pDevice->wasapi.pRenderClient = NULL; - } - if (pDevice->wasapi.pAudioClientPlayback != NULL) { - ma_IAudioClient_Release((ma_IAudioClient*)pDevice->wasapi.pAudioClientPlayback); - pDevice->wasapi.pAudioClientPlayback = NULL; - } - - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to create event for playback."); - return result; - } - ma_IAudioClient_SetEventHandle((ma_IAudioClient*)pDevice->wasapi.pAudioClientPlayback, (HANDLE)pDevice->wasapi.hEventPlayback); - - pDevice->wasapi.periodSizeInFramesPlayback = data.periodSizeInFramesOut; - ma_IAudioClient_GetBufferSize((ma_IAudioClient*)pDevice->wasapi.pAudioClientPlayback, &pDevice->wasapi.actualBufferSizeInFramesPlayback); - - /* We must always have a valid ID because rerouting will look at it. */ - ma_strcpy_s_WCHAR(pDevice->playback.id.wasapi, sizeof(pDevice->playback.id.wasapi), data.id.wasapi); - - /* The descriptor needs to be updated with actual values. */ - pDescriptorPlayback->format = data.formatOut; - pDescriptorPlayback->channels = data.channelsOut; - pDescriptorPlayback->sampleRate = data.sampleRateOut; - MA_COPY_MEMORY(pDescriptorPlayback->channelMap, data.channelMapOut, sizeof(data.channelMapOut)); - pDescriptorPlayback->periodSizeInFrames = data.periodSizeInFramesOut; - pDescriptorPlayback->periodCount = data.periodsOut; - } - - /* - We need to register a notification client to detect when the device has been disabled, unplugged or re-routed (when the default device changes). When - we are connecting to the default device we want to do automatic stream routing when the device is disabled or unplugged. Otherwise we want to just - stop the device outright and let the application handle it. - */ -#if defined(MA_WIN32_DESKTOP) || defined(MA_WIN32_GDK) - if (pConfig->wasapi.noAutoStreamRouting == MA_FALSE) { - if ((pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex || pConfig->deviceType == ma_device_type_loopback) && pConfig->capture.pDeviceID == NULL) { - pDevice->wasapi.allowCaptureAutoStreamRouting = MA_TRUE; - } - if ((pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) && pConfig->playback.pDeviceID == NULL) { - pDevice->wasapi.allowPlaybackAutoStreamRouting = MA_TRUE; - } - } - - ma_mutex_init(&pDevice->wasapi.rerouteLock); - - hr = ma_CoCreateInstance(pDevice->pContext, &MA_CLSID_MMDeviceEnumerator, NULL, CLSCTX_ALL, &MA_IID_IMMDeviceEnumerator, (void**)&pDeviceEnumerator); - if (FAILED(hr)) { - ma_device_uninit__wasapi(pDevice); - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to create device enumerator."); - return ma_result_from_HRESULT(hr); - } - - pDevice->wasapi.notificationClient.lpVtbl = (void*)&g_maNotificationCientVtbl; - pDevice->wasapi.notificationClient.counter = 1; - pDevice->wasapi.notificationClient.pDevice = pDevice; - - hr = pDeviceEnumerator->lpVtbl->RegisterEndpointNotificationCallback(pDeviceEnumerator, &pDevice->wasapi.notificationClient); - if (SUCCEEDED(hr)) { - pDevice->wasapi.pDeviceEnumerator = (ma_ptr)pDeviceEnumerator; - } else { - /* Not the end of the world if we fail to register the notification callback. We just won't support automatic stream routing. */ - ma_IMMDeviceEnumerator_Release(pDeviceEnumerator); - } -#endif - - ma_atomic_bool32_set(&pDevice->wasapi.isStartedCapture, MA_FALSE); - ma_atomic_bool32_set(&pDevice->wasapi.isStartedPlayback, MA_FALSE); - - return MA_SUCCESS; -} - -static ma_result ma_device__get_available_frames__wasapi(ma_device* pDevice, ma_IAudioClient* pAudioClient, ma_uint32* pFrameCount) -{ - ma_uint32 paddingFramesCount; - HRESULT hr; - ma_share_mode shareMode; - - MA_ASSERT(pDevice != NULL); - MA_ASSERT(pFrameCount != NULL); - - *pFrameCount = 0; - - if ((ma_ptr)pAudioClient != pDevice->wasapi.pAudioClientPlayback && (ma_ptr)pAudioClient != pDevice->wasapi.pAudioClientCapture) { - return MA_INVALID_OPERATION; - } - - /* - I've had a report that GetCurrentPadding() is returning a frame count of 0 which is preventing - higher level function calls from doing anything because it thinks nothing is available. I have - taken a look at the documentation and it looks like this is unnecessary in exclusive mode. - - From Microsoft's documentation: - - For an exclusive-mode rendering or capture stream that was initialized with the - AUDCLNT_STREAMFLAGS_EVENTCALLBACK flag, the client typically has no use for the padding - value reported by GetCurrentPadding. Instead, the client accesses an entire buffer during - each processing pass. - - Considering this, I'm going to skip GetCurrentPadding() for exclusive mode and just report the - entire buffer. This depends on the caller making sure they wait on the event handler. - */ - shareMode = ((ma_ptr)pAudioClient == pDevice->wasapi.pAudioClientPlayback) ? pDevice->playback.shareMode : pDevice->capture.shareMode; - if (shareMode == ma_share_mode_shared) { - /* Shared mode. */ - hr = ma_IAudioClient_GetCurrentPadding(pAudioClient, &paddingFramesCount); - if (FAILED(hr)) { - return ma_result_from_HRESULT(hr); - } - - if ((ma_ptr)pAudioClient == pDevice->wasapi.pAudioClientPlayback) { - *pFrameCount = pDevice->wasapi.actualBufferSizeInFramesPlayback - paddingFramesCount; - } else { - *pFrameCount = paddingFramesCount; - } - } else { - /* Exclusive mode. */ - if ((ma_ptr)pAudioClient == pDevice->wasapi.pAudioClientPlayback) { - *pFrameCount = pDevice->wasapi.actualBufferSizeInFramesPlayback; - } else { - *pFrameCount = pDevice->wasapi.actualBufferSizeInFramesCapture; - } - } - - return MA_SUCCESS; -} - - -static ma_result ma_device_reroute__wasapi(ma_device* pDevice, ma_device_type deviceType) -{ - ma_result result; - - if (deviceType == ma_device_type_duplex) { - return MA_INVALID_ARGS; - } - - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "=== CHANGING DEVICE ===\n"); - - result = ma_device_reinit__wasapi(pDevice, deviceType); - if (result != MA_SUCCESS) { - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_WARNING, "[WASAPI] Reinitializing device after route change failed.\n"); - return result; - } - - ma_device__post_init_setup(pDevice, deviceType); - ma_device__on_notification_rerouted(pDevice); - - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "=== DEVICE CHANGED ===\n"); - - return MA_SUCCESS; -} - -static ma_result ma_device_start__wasapi_nolock(ma_device* pDevice) -{ - HRESULT hr; - - if (pDevice->pContext->wasapi.hAvrt) { - const char* pTaskName = ma_to_usage_string__wasapi(pDevice->wasapi.usage); - if (pTaskName) { - DWORD idx = 0; - pDevice->wasapi.hAvrtHandle = (ma_handle)((MA_PFN_AvSetMmThreadCharacteristicsA)pDevice->pContext->wasapi.AvSetMmThreadCharacteristicsA)(pTaskName, &idx); - } - } - - if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex || pDevice->type == ma_device_type_loopback) { - hr = ma_IAudioClient_Start((ma_IAudioClient*)pDevice->wasapi.pAudioClientCapture); - if (FAILED(hr)) { - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to start internal capture device. HRESULT = %d.", (int)hr); - return ma_result_from_HRESULT(hr); - } - - ma_atomic_bool32_set(&pDevice->wasapi.isStartedCapture, MA_TRUE); - } - - if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) { - hr = ma_IAudioClient_Start((ma_IAudioClient*)pDevice->wasapi.pAudioClientPlayback); - if (FAILED(hr)) { - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to start internal playback device. HRESULT = %d.", (int)hr); - return ma_result_from_HRESULT(hr); - } - - ma_atomic_bool32_set(&pDevice->wasapi.isStartedPlayback, MA_TRUE); - } - - return MA_SUCCESS; -} - -static ma_result ma_device_start__wasapi(ma_device* pDevice) -{ - ma_result result; - - MA_ASSERT(pDevice != NULL); - - /* Wait for any rerouting to finish before attempting to start the device. */ - ma_mutex_lock(&pDevice->wasapi.rerouteLock); - { - result = ma_device_start__wasapi_nolock(pDevice); - } - ma_mutex_unlock(&pDevice->wasapi.rerouteLock); - - return result; -} - -static ma_result ma_device_stop__wasapi_nolock(ma_device* pDevice) -{ - ma_result result; - HRESULT hr; - - MA_ASSERT(pDevice != NULL); - - if (pDevice->wasapi.hAvrtHandle) { - ((MA_PFN_AvRevertMmThreadCharacteristics)pDevice->pContext->wasapi.AvRevertMmThreadcharacteristics)((HANDLE)pDevice->wasapi.hAvrtHandle); - pDevice->wasapi.hAvrtHandle = NULL; - } - - if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex || pDevice->type == ma_device_type_loopback) { - /* If we have a mapped buffer we need to release it. */ - if (pDevice->wasapi.pMappedBufferCapture != NULL) { - ma_IAudioCaptureClient_ReleaseBuffer((ma_IAudioCaptureClient*)pDevice->wasapi.pCaptureClient, pDevice->wasapi.mappedBufferCaptureCap); - pDevice->wasapi.pMappedBufferCapture = NULL; - pDevice->wasapi.mappedBufferCaptureCap = 0; - pDevice->wasapi.mappedBufferCaptureLen = 0; - } - - hr = ma_IAudioClient_Stop((ma_IAudioClient*)pDevice->wasapi.pAudioClientCapture); - if (FAILED(hr)) { - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to stop internal capture device."); - return ma_result_from_HRESULT(hr); - } - - /* The audio client needs to be reset otherwise restarting will fail. */ - hr = ma_IAudioClient_Reset((ma_IAudioClient*)pDevice->wasapi.pAudioClientCapture); - if (FAILED(hr)) { - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to reset internal capture device."); - return ma_result_from_HRESULT(hr); - } - - ma_atomic_bool32_set(&pDevice->wasapi.isStartedCapture, MA_FALSE); - } - - if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) { - if (pDevice->wasapi.pMappedBufferPlayback != NULL) { - ma_silence_pcm_frames( - ma_offset_pcm_frames_ptr(pDevice->wasapi.pMappedBufferPlayback, pDevice->wasapi.mappedBufferPlaybackLen, pDevice->playback.internalFormat, pDevice->playback.internalChannels), - pDevice->wasapi.mappedBufferPlaybackCap - pDevice->wasapi.mappedBufferPlaybackLen, - pDevice->playback.internalFormat, pDevice->playback.internalChannels - ); - ma_IAudioRenderClient_ReleaseBuffer((ma_IAudioRenderClient*)pDevice->wasapi.pRenderClient, pDevice->wasapi.mappedBufferPlaybackCap, 0); - pDevice->wasapi.pMappedBufferPlayback = NULL; - pDevice->wasapi.mappedBufferPlaybackCap = 0; - pDevice->wasapi.mappedBufferPlaybackLen = 0; - } - - /* - The buffer needs to be drained before stopping the device. Not doing this will result in the last few frames not getting output to - the speakers. This is a problem for very short sounds because it'll result in a significant portion of it not getting played. - */ - if (ma_atomic_bool32_get(&pDevice->wasapi.isStartedPlayback)) { - /* We need to make sure we put a timeout here or else we'll risk getting stuck in a deadlock in some cases. */ - DWORD waitTime = (pDevice->wasapi.actualBufferSizeInFramesPlayback * 1000) / pDevice->playback.internalSampleRate; - - if (pDevice->playback.shareMode == ma_share_mode_exclusive) { - WaitForSingleObject((HANDLE)pDevice->wasapi.hEventPlayback, waitTime); - } else { - ma_uint32 prevFramesAvailablePlayback = (ma_uint32)-1; - ma_uint32 framesAvailablePlayback; - for (;;) { - result = ma_device__get_available_frames__wasapi(pDevice, (ma_IAudioClient*)pDevice->wasapi.pAudioClientPlayback, &framesAvailablePlayback); - if (result != MA_SUCCESS) { - break; - } - - if (framesAvailablePlayback >= pDevice->wasapi.actualBufferSizeInFramesPlayback) { - break; - } - - /* - Just a safety check to avoid an infinite loop. If this iteration results in a situation where the number of available frames - has not changed, get out of the loop. I don't think this should ever happen, but I think it's nice to have just in case. - */ - if (framesAvailablePlayback == prevFramesAvailablePlayback) { - break; - } - prevFramesAvailablePlayback = framesAvailablePlayback; - - ResetEvent((HANDLE)pDevice->wasapi.hEventPlayback); /* Manual reset. */ - WaitForSingleObject((HANDLE)pDevice->wasapi.hEventPlayback, waitTime); - } - } - } - - hr = ma_IAudioClient_Stop((ma_IAudioClient*)pDevice->wasapi.pAudioClientPlayback); - if (FAILED(hr)) { - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to stop internal playback device."); - return ma_result_from_HRESULT(hr); - } - - /* The audio client needs to be reset otherwise restarting will fail. */ - { - ma_int32 retries = 5; - - while ((hr = ma_IAudioClient_Reset((ma_IAudioClient*)pDevice->wasapi.pAudioClientPlayback)) == MA_AUDCLNT_E_BUFFER_OPERATION_PENDING && retries > 0) { - ma_sleep(10); - retries -= 1; - } - } - - if (FAILED(hr)) { - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to reset internal playback device."); - return ma_result_from_HRESULT(hr); - } - - ma_atomic_bool32_set(&pDevice->wasapi.isStartedPlayback, MA_FALSE); - } - - return MA_SUCCESS; -} - -static ma_result ma_device_stop__wasapi(ma_device* pDevice) -{ - ma_result result; - - MA_ASSERT(pDevice != NULL); - - /* Wait for any rerouting to finish before attempting to stop the device. */ - ma_mutex_lock(&pDevice->wasapi.rerouteLock); - { - result = ma_device_stop__wasapi_nolock(pDevice); - } - ma_mutex_unlock(&pDevice->wasapi.rerouteLock); - - return result; -} - - -#ifndef MA_WASAPI_WAIT_TIMEOUT_MILLISECONDS -#define MA_WASAPI_WAIT_TIMEOUT_MILLISECONDS 5000 -#endif - -static ma_result ma_device_read__wasapi(ma_device* pDevice, void* pFrames, ma_uint32 frameCount, ma_uint32* pFramesRead) -{ - ma_result result = MA_SUCCESS; - ma_uint32 totalFramesProcessed = 0; - - /* - When reading, we need to get a buffer and process all of it before releasing it. Because the - frame count (frameCount) can be different to the size of the buffer, we'll need to cache the - pointer to the buffer. - */ - - /* Keep running until we've processed the requested number of frames. */ - while (ma_device_get_state(pDevice) == ma_device_state_started && totalFramesProcessed < frameCount) { - ma_uint32 framesRemaining = frameCount - totalFramesProcessed; - - /* If we have a mapped data buffer, consume that first. */ - if (pDevice->wasapi.pMappedBufferCapture != NULL) { - /* We have a cached data pointer so consume that before grabbing another one from WASAPI. */ - ma_uint32 framesToProcessNow = framesRemaining; - if (framesToProcessNow > pDevice->wasapi.mappedBufferCaptureLen) { - framesToProcessNow = pDevice->wasapi.mappedBufferCaptureLen; - } - - /* Now just copy the data over to the output buffer. */ - ma_copy_pcm_frames( - ma_offset_pcm_frames_ptr(pFrames, totalFramesProcessed, pDevice->capture.internalFormat, pDevice->capture.internalChannels), - ma_offset_pcm_frames_const_ptr(pDevice->wasapi.pMappedBufferCapture, pDevice->wasapi.mappedBufferCaptureCap - pDevice->wasapi.mappedBufferCaptureLen, pDevice->capture.internalFormat, pDevice->capture.internalChannels), - framesToProcessNow, - pDevice->capture.internalFormat, pDevice->capture.internalChannels - ); - - totalFramesProcessed += framesToProcessNow; - pDevice->wasapi.mappedBufferCaptureLen -= framesToProcessNow; - - /* If the data buffer has been fully consumed we need to release it. */ - if (pDevice->wasapi.mappedBufferCaptureLen == 0) { - ma_IAudioCaptureClient_ReleaseBuffer((ma_IAudioCaptureClient*)pDevice->wasapi.pCaptureClient, pDevice->wasapi.mappedBufferCaptureCap); - pDevice->wasapi.pMappedBufferCapture = NULL; - pDevice->wasapi.mappedBufferCaptureCap = 0; - } - } else { - /* We don't have any cached data pointer, so grab another one. */ - HRESULT hr; - DWORD flags = 0; - - /* First just ask WASAPI for a data buffer. If it's not available, we'll wait for more. */ - hr = ma_IAudioCaptureClient_GetBuffer((ma_IAudioCaptureClient*)pDevice->wasapi.pCaptureClient, (BYTE**)&pDevice->wasapi.pMappedBufferCapture, &pDevice->wasapi.mappedBufferCaptureCap, &flags, NULL, NULL); - if (hr == S_OK) { - /* We got a data buffer. Continue to the next loop iteration which will then read from the mapped pointer. */ - pDevice->wasapi.mappedBufferCaptureLen = pDevice->wasapi.mappedBufferCaptureCap; - - /* - There have been reports that indicate that at times the AUDCLNT_BUFFERFLAGS_DATA_DISCONTINUITY is reported for every - call to IAudioCaptureClient_GetBuffer() above which results in spamming of the debug messages below. To partially - work around this, I'm only outputting these messages when MA_DEBUG_OUTPUT is explicitly defined. The better solution - would be to figure out why the flag is always getting reported. - */ - #if defined(MA_DEBUG_OUTPUT) - { - if (flags != 0) { - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "[WASAPI] Capture Flags: %ld\n", flags); - - if ((flags & MA_AUDCLNT_BUFFERFLAGS_DATA_DISCONTINUITY) != 0) { - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "[WASAPI] Data discontinuity (possible overrun). Attempting recovery. mappedBufferCaptureCap=%d\n", pDevice->wasapi.mappedBufferCaptureCap); - } - } - } - #endif - - /* Overrun detection. */ - if ((flags & MA_AUDCLNT_BUFFERFLAGS_DATA_DISCONTINUITY) != 0) { - /* Glitched. Probably due to an overrun. */ - - /* - If we got an overrun it probably means we're straddling the end of the buffer. In normal capture - mode this is the fault of the client application because they're responsible for ensuring data is - processed fast enough. In duplex mode, however, the processing of audio is tied to the playback - device, so this can possibly be the result of a timing de-sync. - - In capture mode we're not going to do any kind of recovery because the real fix is for the client - application to process faster. In duplex mode, we'll treat this as a desync and reset the buffers - to prevent a never-ending sequence of glitches due to straddling the end of the buffer. - */ - if (pDevice->type == ma_device_type_duplex) { - /* - Experiment: - - If we empty out the *entire* buffer we may end up putting ourselves into an underrun position - which isn't really any better than the overrun we're probably in right now. Instead we'll just - empty out about half. - */ - ma_uint32 i; - ma_uint32 periodCount = (pDevice->wasapi.actualBufferSizeInFramesCapture / pDevice->wasapi.periodSizeInFramesCapture); - ma_uint32 iterationCount = periodCount / 2; - if ((periodCount % 2) > 0) { - iterationCount += 1; - } - - for (i = 0; i < iterationCount; i += 1) { - hr = ma_IAudioCaptureClient_ReleaseBuffer((ma_IAudioCaptureClient*)pDevice->wasapi.pCaptureClient, pDevice->wasapi.mappedBufferCaptureCap); - if (FAILED(hr)) { - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "[WASAPI] Data discontinuity recovery: IAudioCaptureClient_ReleaseBuffer() failed with %ld.\n", hr); - break; - } - - flags = 0; - hr = ma_IAudioCaptureClient_GetBuffer((ma_IAudioCaptureClient*)pDevice->wasapi.pCaptureClient, (BYTE**)&pDevice->wasapi.pMappedBufferCapture, &pDevice->wasapi.mappedBufferCaptureCap, &flags, NULL, NULL); - if (hr == MA_AUDCLNT_S_BUFFER_EMPTY || FAILED(hr)) { - /* - The buffer has been completely emptied or an error occurred. In this case we'll need - to reset the state of the mapped buffer which will trigger the next iteration to get - a fresh buffer from WASAPI. - */ - pDevice->wasapi.pMappedBufferCapture = NULL; - pDevice->wasapi.mappedBufferCaptureCap = 0; - pDevice->wasapi.mappedBufferCaptureLen = 0; - - if (hr == MA_AUDCLNT_S_BUFFER_EMPTY) { - if ((flags & MA_AUDCLNT_BUFFERFLAGS_DATA_DISCONTINUITY) != 0) { - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "[WASAPI] Data discontinuity recovery: Buffer emptied, and data discontinuity still reported.\n"); - } else { - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "[WASAPI] Data discontinuity recovery: Buffer emptied.\n"); - } - } - - if (FAILED(hr)) { - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "[WASAPI] Data discontinuity recovery: IAudioCaptureClient_GetBuffer() failed with %ld.\n", hr); - } - - break; - } - } - - /* If at this point we have a valid buffer mapped, make sure the buffer length is set appropriately. */ - if (pDevice->wasapi.pMappedBufferCapture != NULL) { - pDevice->wasapi.mappedBufferCaptureLen = pDevice->wasapi.mappedBufferCaptureCap; - } - } - } - - continue; - } else { - if (hr == MA_AUDCLNT_S_BUFFER_EMPTY || hr == MA_AUDCLNT_E_BUFFER_ERROR) { - /* - No data is available. We need to wait for more. There's two situations to consider - here. The first is normal capture mode. If this times out it probably means the - microphone isn't delivering data for whatever reason. In this case we'll just - abort the read and return whatever we were able to get. The other situations is - loopback mode, in which case a timeout probably just means the nothing is playing - through the speakers. - */ - - /* Experiment: Use a shorter timeout for loopback mode. */ - DWORD timeoutInMilliseconds = MA_WASAPI_WAIT_TIMEOUT_MILLISECONDS; - if (pDevice->type == ma_device_type_loopback) { - timeoutInMilliseconds = 10; - } - - if (WaitForSingleObject((HANDLE)pDevice->wasapi.hEventCapture, timeoutInMilliseconds) != WAIT_OBJECT_0) { - if (pDevice->type == ma_device_type_loopback) { - continue; /* Keep waiting in loopback mode. */ - } else { - result = MA_ERROR; - break; /* Wait failed. */ - } - } - - /* At this point we should be able to loop back to the start of the loop and try retrieving a data buffer again. */ - } else { - /* An error occurred and we need to abort. */ - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to retrieve internal buffer from capture device in preparation for reading from the device. HRESULT = %d. Stopping device.\n", (int)hr); - result = ma_result_from_HRESULT(hr); - break; - } - } - } - } - - /* - If we were unable to process the entire requested frame count, but we still have a mapped buffer, - there's a good chance either an error occurred or the device was stopped mid-read. In this case - we'll need to make sure the buffer is released. - */ - if (totalFramesProcessed < frameCount && pDevice->wasapi.pMappedBufferCapture != NULL) { - ma_IAudioCaptureClient_ReleaseBuffer((ma_IAudioCaptureClient*)pDevice->wasapi.pCaptureClient, pDevice->wasapi.mappedBufferCaptureCap); - pDevice->wasapi.pMappedBufferCapture = NULL; - pDevice->wasapi.mappedBufferCaptureCap = 0; - pDevice->wasapi.mappedBufferCaptureLen = 0; - } - - if (pFramesRead != NULL) { - *pFramesRead = totalFramesProcessed; - } - - return result; -} - -static ma_result ma_device_write__wasapi(ma_device* pDevice, const void* pFrames, ma_uint32 frameCount, ma_uint32* pFramesWritten) -{ - ma_result result = MA_SUCCESS; - ma_uint32 totalFramesProcessed = 0; - - /* Keep writing to the device until it's stopped or we've consumed all of our input. */ - while (ma_device_get_state(pDevice) == ma_device_state_started && totalFramesProcessed < frameCount) { - ma_uint32 framesRemaining = frameCount - totalFramesProcessed; - - /* - We're going to do this in a similar way to capture. We'll first check if the cached data pointer - is valid, and if so, read from that. Otherwise We will call IAudioRenderClient_GetBuffer() with - a requested buffer size equal to our actual period size. If it returns AUDCLNT_E_BUFFER_TOO_LARGE - it means we need to wait for some data to become available. - */ - if (pDevice->wasapi.pMappedBufferPlayback != NULL) { - /* We still have some space available in the mapped data buffer. Write to it. */ - ma_uint32 framesToProcessNow = framesRemaining; - if (framesToProcessNow > (pDevice->wasapi.mappedBufferPlaybackCap - pDevice->wasapi.mappedBufferPlaybackLen)) { - framesToProcessNow = (pDevice->wasapi.mappedBufferPlaybackCap - pDevice->wasapi.mappedBufferPlaybackLen); - } - - /* Now just copy the data over to the output buffer. */ - ma_copy_pcm_frames( - ma_offset_pcm_frames_ptr(pDevice->wasapi.pMappedBufferPlayback, pDevice->wasapi.mappedBufferPlaybackLen, pDevice->playback.internalFormat, pDevice->playback.internalChannels), - ma_offset_pcm_frames_const_ptr(pFrames, totalFramesProcessed, pDevice->playback.internalFormat, pDevice->playback.internalChannels), - framesToProcessNow, - pDevice->playback.internalFormat, pDevice->playback.internalChannels - ); - - totalFramesProcessed += framesToProcessNow; - pDevice->wasapi.mappedBufferPlaybackLen += framesToProcessNow; - - /* If the data buffer has been fully consumed we need to release it. */ - if (pDevice->wasapi.mappedBufferPlaybackLen == pDevice->wasapi.mappedBufferPlaybackCap) { - ma_IAudioRenderClient_ReleaseBuffer((ma_IAudioRenderClient*)pDevice->wasapi.pRenderClient, pDevice->wasapi.mappedBufferPlaybackCap, 0); - pDevice->wasapi.pMappedBufferPlayback = NULL; - pDevice->wasapi.mappedBufferPlaybackCap = 0; - pDevice->wasapi.mappedBufferPlaybackLen = 0; - - /* - In exclusive mode we need to wait here. Exclusive mode is weird because GetBuffer() never - seems to return AUDCLNT_E_BUFFER_TOO_LARGE, which is what we normally use to determine - whether or not we need to wait for more data. - */ - if (pDevice->playback.shareMode == ma_share_mode_exclusive) { - if (WaitForSingleObject((HANDLE)pDevice->wasapi.hEventPlayback, MA_WASAPI_WAIT_TIMEOUT_MILLISECONDS) != WAIT_OBJECT_0) { - result = MA_ERROR; - break; /* Wait failed. Probably timed out. */ - } - } - } - } else { - /* We don't have a mapped data buffer so we'll need to get one. */ - HRESULT hr; - ma_uint32 bufferSizeInFrames; - - /* Special rules for exclusive mode. */ - if (pDevice->playback.shareMode == ma_share_mode_exclusive) { - bufferSizeInFrames = pDevice->wasapi.actualBufferSizeInFramesPlayback; - } else { - bufferSizeInFrames = pDevice->wasapi.periodSizeInFramesPlayback; - } - - hr = ma_IAudioRenderClient_GetBuffer((ma_IAudioRenderClient*)pDevice->wasapi.pRenderClient, bufferSizeInFrames, (BYTE**)&pDevice->wasapi.pMappedBufferPlayback); - if (hr == S_OK) { - /* We have data available. */ - pDevice->wasapi.mappedBufferPlaybackCap = bufferSizeInFrames; - pDevice->wasapi.mappedBufferPlaybackLen = 0; - } else { - if (hr == MA_AUDCLNT_E_BUFFER_TOO_LARGE || hr == MA_AUDCLNT_E_BUFFER_ERROR) { - /* Not enough data available. We need to wait for more. */ - if (WaitForSingleObject((HANDLE)pDevice->wasapi.hEventPlayback, MA_WASAPI_WAIT_TIMEOUT_MILLISECONDS) != WAIT_OBJECT_0) { - result = MA_ERROR; - break; /* Wait failed. Probably timed out. */ - } - } else { - /* Some error occurred. We'll need to abort. */ - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to retrieve internal buffer from playback device in preparation for writing to the device. HRESULT = %d. Stopping device.\n", (int)hr); - result = ma_result_from_HRESULT(hr); - break; - } - } - } - } - - if (pFramesWritten != NULL) { - *pFramesWritten = totalFramesProcessed; - } - - return result; -} - -static ma_result ma_device_data_loop_wakeup__wasapi(ma_device* pDevice) -{ - MA_ASSERT(pDevice != NULL); - - if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex || pDevice->type == ma_device_type_loopback) { - SetEvent((HANDLE)pDevice->wasapi.hEventCapture); - } - - if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) { - SetEvent((HANDLE)pDevice->wasapi.hEventPlayback); - } - - return MA_SUCCESS; -} - - -static ma_result ma_context_uninit__wasapi(ma_context* pContext) -{ - ma_context_command__wasapi cmd = ma_context_init_command__wasapi(MA_CONTEXT_COMMAND_QUIT__WASAPI); - - MA_ASSERT(pContext != NULL); - MA_ASSERT(pContext->backend == ma_backend_wasapi); - - ma_context_post_command__wasapi(pContext, &cmd); - ma_thread_wait(&pContext->wasapi.commandThread); - - if (pContext->wasapi.hAvrt) { - ma_dlclose(ma_context_get_log(pContext), pContext->wasapi.hAvrt); - pContext->wasapi.hAvrt = NULL; - } - - #if defined(MA_WIN32_UWP) - { - if (pContext->wasapi.hMMDevapi) { - ma_dlclose(ma_context_get_log(pContext), pContext->wasapi.hMMDevapi); - pContext->wasapi.hMMDevapi = NULL; - } - } - #endif - - /* Only after the thread has been terminated can we uninitialize the sync objects for the command thread. */ - ma_semaphore_uninit(&pContext->wasapi.commandSem); - ma_mutex_uninit(&pContext->wasapi.commandLock); - - return MA_SUCCESS; -} - -static ma_result ma_context_init__wasapi(ma_context* pContext, const ma_context_config* pConfig, ma_backend_callbacks* pCallbacks) -{ - ma_result result = MA_SUCCESS; - - MA_ASSERT(pContext != NULL); - - (void)pConfig; - -#ifdef MA_WIN32_DESKTOP - /* - WASAPI is only supported in Vista SP1 and newer. The reason for SP1 and not the base version of Vista is that event-driven - exclusive mode does not work until SP1. - - Unfortunately older compilers don't define these functions so we need to dynamically load them in order to avoid a link error. - */ - { - ma_OSVERSIONINFOEXW osvi; - ma_handle kernel32DLL; - ma_PFNVerifyVersionInfoW _VerifyVersionInfoW; - ma_PFNVerSetConditionMask _VerSetConditionMask; - - kernel32DLL = ma_dlopen(ma_context_get_log(pContext), "kernel32.dll"); - if (kernel32DLL == NULL) { - return MA_NO_BACKEND; - } - - _VerifyVersionInfoW = (ma_PFNVerifyVersionInfoW )ma_dlsym(ma_context_get_log(pContext), kernel32DLL, "VerifyVersionInfoW"); - _VerSetConditionMask = (ma_PFNVerSetConditionMask)ma_dlsym(ma_context_get_log(pContext), kernel32DLL, "VerSetConditionMask"); - if (_VerifyVersionInfoW == NULL || _VerSetConditionMask == NULL) { - ma_dlclose(ma_context_get_log(pContext), kernel32DLL); - return MA_NO_BACKEND; - } - - MA_ZERO_OBJECT(&osvi); - osvi.dwOSVersionInfoSize = sizeof(osvi); - osvi.dwMajorVersion = ((MA_WIN32_WINNT_VISTA >> 8) & 0xFF); - osvi.dwMinorVersion = ((MA_WIN32_WINNT_VISTA >> 0) & 0xFF); - osvi.wServicePackMajor = 1; - if (_VerifyVersionInfoW(&osvi, MA_VER_MAJORVERSION | MA_VER_MINORVERSION | MA_VER_SERVICEPACKMAJOR, _VerSetConditionMask(_VerSetConditionMask(_VerSetConditionMask(0, MA_VER_MAJORVERSION, MA_VER_GREATER_EQUAL), MA_VER_MINORVERSION, MA_VER_GREATER_EQUAL), MA_VER_SERVICEPACKMAJOR, MA_VER_GREATER_EQUAL))) { - result = MA_SUCCESS; - } else { - result = MA_NO_BACKEND; - } - - ma_dlclose(ma_context_get_log(pContext), kernel32DLL); - } -#endif - - if (result != MA_SUCCESS) { - return result; - } - - MA_ZERO_OBJECT(&pContext->wasapi); - - - #if defined(MA_WIN32_UWP) - { - /* Link to mmdevapi so we can get access to ActivateAudioInterfaceAsync(). */ - pContext->wasapi.hMMDevapi = ma_dlopen(ma_context_get_log(pContext), "mmdevapi.dll"); - if (pContext->wasapi.hMMDevapi) { - pContext->wasapi.ActivateAudioInterfaceAsync = ma_dlsym(ma_context_get_log(pContext), pContext->wasapi.hMMDevapi, "ActivateAudioInterfaceAsync"); - if (pContext->wasapi.ActivateAudioInterfaceAsync == NULL) { - ma_dlclose(ma_context_get_log(pContext), pContext->wasapi.hMMDevapi); - return MA_NO_BACKEND; /* ActivateAudioInterfaceAsync() could not be loaded. */ - } - } else { - return MA_NO_BACKEND; /* Failed to load mmdevapi.dll which is required for ActivateAudioInterfaceAsync() */ - } - } - #endif - - /* Optionally use the Avrt API to specify the audio thread's latency sensitivity requirements */ - pContext->wasapi.hAvrt = ma_dlopen(ma_context_get_log(pContext), "avrt.dll"); - if (pContext->wasapi.hAvrt) { - pContext->wasapi.AvSetMmThreadCharacteristicsA = ma_dlsym(ma_context_get_log(pContext), pContext->wasapi.hAvrt, "AvSetMmThreadCharacteristicsA"); - pContext->wasapi.AvRevertMmThreadcharacteristics = ma_dlsym(ma_context_get_log(pContext), pContext->wasapi.hAvrt, "AvRevertMmThreadCharacteristics"); - - /* If either function could not be found, disable use of avrt entirely. */ - if (!pContext->wasapi.AvSetMmThreadCharacteristicsA || !pContext->wasapi.AvRevertMmThreadcharacteristics) { - pContext->wasapi.AvSetMmThreadCharacteristicsA = NULL; - pContext->wasapi.AvRevertMmThreadcharacteristics = NULL; - ma_dlclose(ma_context_get_log(pContext), pContext->wasapi.hAvrt); - pContext->wasapi.hAvrt = NULL; - } - } - - - /* - Annoyingly, WASAPI does not allow you to release an IAudioClient object from a different thread - than the one that retrieved it with GetService(). This can result in a deadlock in two - situations: - - 1) When calling ma_device_uninit() from a different thread to ma_device_init(); and - 2) When uninitializing and reinitializing the internal IAudioClient object in response to - automatic stream routing. - - We could define ma_device_uninit() such that it must be called on the same thread as - ma_device_init(). We could also just not release the IAudioClient when performing automatic - stream routing to avoid the deadlock. Neither of these are acceptable solutions in my view so - we're going to have to work around this with a worker thread. This is not ideal, but I can't - think of a better way to do this. - - More information about this can be found here: - - https://docs.microsoft.com/en-us/windows/win32/api/audioclient/nn-audioclient-iaudiorenderclient - - Note this section: - - When releasing an IAudioRenderClient interface instance, the client must call the interface's - Release method from the same thread as the call to IAudioClient::GetService that created the - object. - */ - { - result = ma_mutex_init(&pContext->wasapi.commandLock); - if (result != MA_SUCCESS) { - return result; - } - - result = ma_semaphore_init(0, &pContext->wasapi.commandSem); - if (result != MA_SUCCESS) { - ma_mutex_uninit(&pContext->wasapi.commandLock); - return result; - } - - result = ma_thread_create(&pContext->wasapi.commandThread, ma_thread_priority_normal, 0, ma_context_command_thread__wasapi, pContext, &pContext->allocationCallbacks); - if (result != MA_SUCCESS) { - ma_semaphore_uninit(&pContext->wasapi.commandSem); - ma_mutex_uninit(&pContext->wasapi.commandLock); - return result; - } - } - - - pCallbacks->onContextInit = ma_context_init__wasapi; - pCallbacks->onContextUninit = ma_context_uninit__wasapi; - pCallbacks->onContextEnumerateDevices = ma_context_enumerate_devices__wasapi; - pCallbacks->onContextGetDeviceInfo = ma_context_get_device_info__wasapi; - pCallbacks->onDeviceInit = ma_device_init__wasapi; - pCallbacks->onDeviceUninit = ma_device_uninit__wasapi; - pCallbacks->onDeviceStart = ma_device_start__wasapi; - pCallbacks->onDeviceStop = ma_device_stop__wasapi; - pCallbacks->onDeviceRead = ma_device_read__wasapi; - pCallbacks->onDeviceWrite = ma_device_write__wasapi; - pCallbacks->onDeviceDataLoop = NULL; - pCallbacks->onDeviceDataLoopWakeup = ma_device_data_loop_wakeup__wasapi; - - return MA_SUCCESS; -} -#endif - -/****************************************************************************** - -DirectSound Backend - -******************************************************************************/ -#ifdef MA_HAS_DSOUND -/*#include */ - -/*static const GUID MA_GUID_IID_DirectSoundNotify = {0xb0210783, 0x89cd, 0x11d0, {0xaf, 0x08, 0x00, 0xa0, 0xc9, 0x25, 0xcd, 0x16}};*/ - -/* miniaudio only uses priority or exclusive modes. */ -#define MA_DSSCL_NORMAL 1 -#define MA_DSSCL_PRIORITY 2 -#define MA_DSSCL_EXCLUSIVE 3 -#define MA_DSSCL_WRITEPRIMARY 4 - -#define MA_DSCAPS_PRIMARYMONO 0x00000001 -#define MA_DSCAPS_PRIMARYSTEREO 0x00000002 -#define MA_DSCAPS_PRIMARY8BIT 0x00000004 -#define MA_DSCAPS_PRIMARY16BIT 0x00000008 -#define MA_DSCAPS_CONTINUOUSRATE 0x00000010 -#define MA_DSCAPS_EMULDRIVER 0x00000020 -#define MA_DSCAPS_CERTIFIED 0x00000040 -#define MA_DSCAPS_SECONDARYMONO 0x00000100 -#define MA_DSCAPS_SECONDARYSTEREO 0x00000200 -#define MA_DSCAPS_SECONDARY8BIT 0x00000400 -#define MA_DSCAPS_SECONDARY16BIT 0x00000800 - -#define MA_DSBCAPS_PRIMARYBUFFER 0x00000001 -#define MA_DSBCAPS_STATIC 0x00000002 -#define MA_DSBCAPS_LOCHARDWARE 0x00000004 -#define MA_DSBCAPS_LOCSOFTWARE 0x00000008 -#define MA_DSBCAPS_CTRL3D 0x00000010 -#define MA_DSBCAPS_CTRLFREQUENCY 0x00000020 -#define MA_DSBCAPS_CTRLPAN 0x00000040 -#define MA_DSBCAPS_CTRLVOLUME 0x00000080 -#define MA_DSBCAPS_CTRLPOSITIONNOTIFY 0x00000100 -#define MA_DSBCAPS_CTRLFX 0x00000200 -#define MA_DSBCAPS_STICKYFOCUS 0x00004000 -#define MA_DSBCAPS_GLOBALFOCUS 0x00008000 -#define MA_DSBCAPS_GETCURRENTPOSITION2 0x00010000 -#define MA_DSBCAPS_MUTE3DATMAXDISTANCE 0x00020000 -#define MA_DSBCAPS_LOCDEFER 0x00040000 -#define MA_DSBCAPS_TRUEPLAYPOSITION 0x00080000 - -#define MA_DSBPLAY_LOOPING 0x00000001 -#define MA_DSBPLAY_LOCHARDWARE 0x00000002 -#define MA_DSBPLAY_LOCSOFTWARE 0x00000004 -#define MA_DSBPLAY_TERMINATEBY_TIME 0x00000008 -#define MA_DSBPLAY_TERMINATEBY_DISTANCE 0x00000010 -#define MA_DSBPLAY_TERMINATEBY_PRIORITY 0x00000020 - -#define MA_DSBSTATUS_PLAYING 0x00000001 -#define MA_DSBSTATUS_BUFFERLOST 0x00000002 -#define MA_DSBSTATUS_LOOPING 0x00000004 -#define MA_DSBSTATUS_LOCHARDWARE 0x00000008 -#define MA_DSBSTATUS_LOCSOFTWARE 0x00000010 -#define MA_DSBSTATUS_TERMINATED 0x00000020 - -#define MA_DSCBSTART_LOOPING 0x00000001 - -typedef struct -{ - DWORD dwSize; - DWORD dwFlags; - DWORD dwBufferBytes; - DWORD dwReserved; - MA_WAVEFORMATEX* lpwfxFormat; - GUID guid3DAlgorithm; -} MA_DSBUFFERDESC; - -typedef struct -{ - DWORD dwSize; - DWORD dwFlags; - DWORD dwBufferBytes; - DWORD dwReserved; - MA_WAVEFORMATEX* lpwfxFormat; - DWORD dwFXCount; - void* lpDSCFXDesc; /* <-- miniaudio doesn't use this, so set to void*. */ -} MA_DSCBUFFERDESC; - -typedef struct -{ - DWORD dwSize; - DWORD dwFlags; - DWORD dwMinSecondarySampleRate; - DWORD dwMaxSecondarySampleRate; - DWORD dwPrimaryBuffers; - DWORD dwMaxHwMixingAllBuffers; - DWORD dwMaxHwMixingStaticBuffers; - DWORD dwMaxHwMixingStreamingBuffers; - DWORD dwFreeHwMixingAllBuffers; - DWORD dwFreeHwMixingStaticBuffers; - DWORD dwFreeHwMixingStreamingBuffers; - DWORD dwMaxHw3DAllBuffers; - DWORD dwMaxHw3DStaticBuffers; - DWORD dwMaxHw3DStreamingBuffers; - DWORD dwFreeHw3DAllBuffers; - DWORD dwFreeHw3DStaticBuffers; - DWORD dwFreeHw3DStreamingBuffers; - DWORD dwTotalHwMemBytes; - DWORD dwFreeHwMemBytes; - DWORD dwMaxContigFreeHwMemBytes; - DWORD dwUnlockTransferRateHwBuffers; - DWORD dwPlayCpuOverheadSwBuffers; - DWORD dwReserved1; - DWORD dwReserved2; -} MA_DSCAPS; - -typedef struct -{ - DWORD dwSize; - DWORD dwFlags; - DWORD dwBufferBytes; - DWORD dwUnlockTransferRate; - DWORD dwPlayCpuOverhead; -} MA_DSBCAPS; - -typedef struct -{ - DWORD dwSize; - DWORD dwFlags; - DWORD dwFormats; - DWORD dwChannels; -} MA_DSCCAPS; - -typedef struct -{ - DWORD dwSize; - DWORD dwFlags; - DWORD dwBufferBytes; - DWORD dwReserved; -} MA_DSCBCAPS; - -typedef struct -{ - DWORD dwOffset; - HANDLE hEventNotify; -} MA_DSBPOSITIONNOTIFY; - -typedef struct ma_IDirectSound ma_IDirectSound; -typedef struct ma_IDirectSoundBuffer ma_IDirectSoundBuffer; -typedef struct ma_IDirectSoundCapture ma_IDirectSoundCapture; -typedef struct ma_IDirectSoundCaptureBuffer ma_IDirectSoundCaptureBuffer; -typedef struct ma_IDirectSoundNotify ma_IDirectSoundNotify; - - -/* -COM objects. The way these work is that you have a vtable (a list of function pointers, kind of -like how C++ works internally), and then you have a structure with a single member, which is a -pointer to the vtable. The vtable is where the methods of the object are defined. Methods need -to be in a specific order, and parent classes need to have their methods declared first. -*/ - -/* IDirectSound */ -typedef struct -{ - /* IUnknown */ - HRESULT (STDMETHODCALLTYPE * QueryInterface)(ma_IDirectSound* pThis, const IID* const riid, void** ppObject); - ULONG (STDMETHODCALLTYPE * AddRef) (ma_IDirectSound* pThis); - ULONG (STDMETHODCALLTYPE * Release) (ma_IDirectSound* pThis); - - /* IDirectSound */ - HRESULT (STDMETHODCALLTYPE * CreateSoundBuffer) (ma_IDirectSound* pThis, const MA_DSBUFFERDESC* pDSBufferDesc, ma_IDirectSoundBuffer** ppDSBuffer, void* pUnkOuter); - HRESULT (STDMETHODCALLTYPE * GetCaps) (ma_IDirectSound* pThis, MA_DSCAPS* pDSCaps); - HRESULT (STDMETHODCALLTYPE * DuplicateSoundBuffer)(ma_IDirectSound* pThis, ma_IDirectSoundBuffer* pDSBufferOriginal, ma_IDirectSoundBuffer** ppDSBufferDuplicate); - HRESULT (STDMETHODCALLTYPE * SetCooperativeLevel) (ma_IDirectSound* pThis, HWND hwnd, DWORD dwLevel); - HRESULT (STDMETHODCALLTYPE * Compact) (ma_IDirectSound* pThis); - HRESULT (STDMETHODCALLTYPE * GetSpeakerConfig) (ma_IDirectSound* pThis, DWORD* pSpeakerConfig); - HRESULT (STDMETHODCALLTYPE * SetSpeakerConfig) (ma_IDirectSound* pThis, DWORD dwSpeakerConfig); - HRESULT (STDMETHODCALLTYPE * Initialize) (ma_IDirectSound* pThis, const GUID* pGuidDevice); -} ma_IDirectSoundVtbl; -struct ma_IDirectSound -{ - ma_IDirectSoundVtbl* lpVtbl; -}; -static MA_INLINE HRESULT ma_IDirectSound_QueryInterface(ma_IDirectSound* pThis, const IID* const riid, void** ppObject) { return pThis->lpVtbl->QueryInterface(pThis, riid, ppObject); } -static MA_INLINE ULONG ma_IDirectSound_AddRef(ma_IDirectSound* pThis) { return pThis->lpVtbl->AddRef(pThis); } -static MA_INLINE ULONG ma_IDirectSound_Release(ma_IDirectSound* pThis) { return pThis->lpVtbl->Release(pThis); } -static MA_INLINE HRESULT ma_IDirectSound_CreateSoundBuffer(ma_IDirectSound* pThis, const MA_DSBUFFERDESC* pDSBufferDesc, ma_IDirectSoundBuffer** ppDSBuffer, void* pUnkOuter) { return pThis->lpVtbl->CreateSoundBuffer(pThis, pDSBufferDesc, ppDSBuffer, pUnkOuter); } -static MA_INLINE HRESULT ma_IDirectSound_GetCaps(ma_IDirectSound* pThis, MA_DSCAPS* pDSCaps) { return pThis->lpVtbl->GetCaps(pThis, pDSCaps); } -static MA_INLINE HRESULT ma_IDirectSound_DuplicateSoundBuffer(ma_IDirectSound* pThis, ma_IDirectSoundBuffer* pDSBufferOriginal, ma_IDirectSoundBuffer** ppDSBufferDuplicate) { return pThis->lpVtbl->DuplicateSoundBuffer(pThis, pDSBufferOriginal, ppDSBufferDuplicate); } -static MA_INLINE HRESULT ma_IDirectSound_SetCooperativeLevel(ma_IDirectSound* pThis, HWND hwnd, DWORD dwLevel) { return pThis->lpVtbl->SetCooperativeLevel(pThis, hwnd, dwLevel); } -static MA_INLINE HRESULT ma_IDirectSound_Compact(ma_IDirectSound* pThis) { return pThis->lpVtbl->Compact(pThis); } -static MA_INLINE HRESULT ma_IDirectSound_GetSpeakerConfig(ma_IDirectSound* pThis, DWORD* pSpeakerConfig) { return pThis->lpVtbl->GetSpeakerConfig(pThis, pSpeakerConfig); } -static MA_INLINE HRESULT ma_IDirectSound_SetSpeakerConfig(ma_IDirectSound* pThis, DWORD dwSpeakerConfig) { return pThis->lpVtbl->SetSpeakerConfig(pThis, dwSpeakerConfig); } -static MA_INLINE HRESULT ma_IDirectSound_Initialize(ma_IDirectSound* pThis, const GUID* pGuidDevice) { return pThis->lpVtbl->Initialize(pThis, pGuidDevice); } - - -/* IDirectSoundBuffer */ -typedef struct -{ - /* IUnknown */ - HRESULT (STDMETHODCALLTYPE * QueryInterface)(ma_IDirectSoundBuffer* pThis, const IID* const riid, void** ppObject); - ULONG (STDMETHODCALLTYPE * AddRef) (ma_IDirectSoundBuffer* pThis); - ULONG (STDMETHODCALLTYPE * Release) (ma_IDirectSoundBuffer* pThis); - - /* IDirectSoundBuffer */ - HRESULT (STDMETHODCALLTYPE * GetCaps) (ma_IDirectSoundBuffer* pThis, MA_DSBCAPS* pDSBufferCaps); - HRESULT (STDMETHODCALLTYPE * GetCurrentPosition)(ma_IDirectSoundBuffer* pThis, DWORD* pCurrentPlayCursor, DWORD* pCurrentWriteCursor); - HRESULT (STDMETHODCALLTYPE * GetFormat) (ma_IDirectSoundBuffer* pThis, MA_WAVEFORMATEX* pFormat, DWORD dwSizeAllocated, DWORD* pSizeWritten); - HRESULT (STDMETHODCALLTYPE * GetVolume) (ma_IDirectSoundBuffer* pThis, LONG* pVolume); - HRESULT (STDMETHODCALLTYPE * GetPan) (ma_IDirectSoundBuffer* pThis, LONG* pPan); - HRESULT (STDMETHODCALLTYPE * GetFrequency) (ma_IDirectSoundBuffer* pThis, DWORD* pFrequency); - HRESULT (STDMETHODCALLTYPE * GetStatus) (ma_IDirectSoundBuffer* pThis, DWORD* pStatus); - HRESULT (STDMETHODCALLTYPE * Initialize) (ma_IDirectSoundBuffer* pThis, ma_IDirectSound* pDirectSound, const MA_DSBUFFERDESC* pDSBufferDesc); - HRESULT (STDMETHODCALLTYPE * Lock) (ma_IDirectSoundBuffer* pThis, DWORD dwOffset, DWORD dwBytes, void** ppAudioPtr1, DWORD* pAudioBytes1, void** ppAudioPtr2, DWORD* pAudioBytes2, DWORD dwFlags); - HRESULT (STDMETHODCALLTYPE * Play) (ma_IDirectSoundBuffer* pThis, DWORD dwReserved1, DWORD dwPriority, DWORD dwFlags); - HRESULT (STDMETHODCALLTYPE * SetCurrentPosition)(ma_IDirectSoundBuffer* pThis, DWORD dwNewPosition); - HRESULT (STDMETHODCALLTYPE * SetFormat) (ma_IDirectSoundBuffer* pThis, const MA_WAVEFORMATEX* pFormat); - HRESULT (STDMETHODCALLTYPE * SetVolume) (ma_IDirectSoundBuffer* pThis, LONG volume); - HRESULT (STDMETHODCALLTYPE * SetPan) (ma_IDirectSoundBuffer* pThis, LONG pan); - HRESULT (STDMETHODCALLTYPE * SetFrequency) (ma_IDirectSoundBuffer* pThis, DWORD dwFrequency); - HRESULT (STDMETHODCALLTYPE * Stop) (ma_IDirectSoundBuffer* pThis); - HRESULT (STDMETHODCALLTYPE * Unlock) (ma_IDirectSoundBuffer* pThis, void* pAudioPtr1, DWORD dwAudioBytes1, void* pAudioPtr2, DWORD dwAudioBytes2); - HRESULT (STDMETHODCALLTYPE * Restore) (ma_IDirectSoundBuffer* pThis); -} ma_IDirectSoundBufferVtbl; -struct ma_IDirectSoundBuffer -{ - ma_IDirectSoundBufferVtbl* lpVtbl; -}; -static MA_INLINE HRESULT ma_IDirectSoundBuffer_QueryInterface(ma_IDirectSoundBuffer* pThis, const IID* const riid, void** ppObject) { return pThis->lpVtbl->QueryInterface(pThis, riid, ppObject); } -static MA_INLINE ULONG ma_IDirectSoundBuffer_AddRef(ma_IDirectSoundBuffer* pThis) { return pThis->lpVtbl->AddRef(pThis); } -static MA_INLINE ULONG ma_IDirectSoundBuffer_Release(ma_IDirectSoundBuffer* pThis) { return pThis->lpVtbl->Release(pThis); } -static MA_INLINE HRESULT ma_IDirectSoundBuffer_GetCaps(ma_IDirectSoundBuffer* pThis, MA_DSBCAPS* pDSBufferCaps) { return pThis->lpVtbl->GetCaps(pThis, pDSBufferCaps); } -static MA_INLINE HRESULT ma_IDirectSoundBuffer_GetCurrentPosition(ma_IDirectSoundBuffer* pThis, DWORD* pCurrentPlayCursor, DWORD* pCurrentWriteCursor) { return pThis->lpVtbl->GetCurrentPosition(pThis, pCurrentPlayCursor, pCurrentWriteCursor); } -static MA_INLINE HRESULT ma_IDirectSoundBuffer_GetFormat(ma_IDirectSoundBuffer* pThis, MA_WAVEFORMATEX* pFormat, DWORD dwSizeAllocated, DWORD* pSizeWritten) { return pThis->lpVtbl->GetFormat(pThis, pFormat, dwSizeAllocated, pSizeWritten); } -static MA_INLINE HRESULT ma_IDirectSoundBuffer_GetVolume(ma_IDirectSoundBuffer* pThis, LONG* pVolume) { return pThis->lpVtbl->GetVolume(pThis, pVolume); } -static MA_INLINE HRESULT ma_IDirectSoundBuffer_GetPan(ma_IDirectSoundBuffer* pThis, LONG* pPan) { return pThis->lpVtbl->GetPan(pThis, pPan); } -static MA_INLINE HRESULT ma_IDirectSoundBuffer_GetFrequency(ma_IDirectSoundBuffer* pThis, DWORD* pFrequency) { return pThis->lpVtbl->GetFrequency(pThis, pFrequency); } -static MA_INLINE HRESULT ma_IDirectSoundBuffer_GetStatus(ma_IDirectSoundBuffer* pThis, DWORD* pStatus) { return pThis->lpVtbl->GetStatus(pThis, pStatus); } -static MA_INLINE HRESULT ma_IDirectSoundBuffer_Initialize(ma_IDirectSoundBuffer* pThis, ma_IDirectSound* pDirectSound, const MA_DSBUFFERDESC* pDSBufferDesc) { return pThis->lpVtbl->Initialize(pThis, pDirectSound, pDSBufferDesc); } -static MA_INLINE HRESULT ma_IDirectSoundBuffer_Lock(ma_IDirectSoundBuffer* pThis, DWORD dwOffset, DWORD dwBytes, void** ppAudioPtr1, DWORD* pAudioBytes1, void** ppAudioPtr2, DWORD* pAudioBytes2, DWORD dwFlags) { return pThis->lpVtbl->Lock(pThis, dwOffset, dwBytes, ppAudioPtr1, pAudioBytes1, ppAudioPtr2, pAudioBytes2, dwFlags); } -static MA_INLINE HRESULT ma_IDirectSoundBuffer_Play(ma_IDirectSoundBuffer* pThis, DWORD dwReserved1, DWORD dwPriority, DWORD dwFlags) { return pThis->lpVtbl->Play(pThis, dwReserved1, dwPriority, dwFlags); } -static MA_INLINE HRESULT ma_IDirectSoundBuffer_SetCurrentPosition(ma_IDirectSoundBuffer* pThis, DWORD dwNewPosition) { return pThis->lpVtbl->SetCurrentPosition(pThis, dwNewPosition); } -static MA_INLINE HRESULT ma_IDirectSoundBuffer_SetFormat(ma_IDirectSoundBuffer* pThis, const MA_WAVEFORMATEX* pFormat) { return pThis->lpVtbl->SetFormat(pThis, pFormat); } -static MA_INLINE HRESULT ma_IDirectSoundBuffer_SetVolume(ma_IDirectSoundBuffer* pThis, LONG volume) { return pThis->lpVtbl->SetVolume(pThis, volume); } -static MA_INLINE HRESULT ma_IDirectSoundBuffer_SetPan(ma_IDirectSoundBuffer* pThis, LONG pan) { return pThis->lpVtbl->SetPan(pThis, pan); } -static MA_INLINE HRESULT ma_IDirectSoundBuffer_SetFrequency(ma_IDirectSoundBuffer* pThis, DWORD dwFrequency) { return pThis->lpVtbl->SetFrequency(pThis, dwFrequency); } -static MA_INLINE HRESULT ma_IDirectSoundBuffer_Stop(ma_IDirectSoundBuffer* pThis) { return pThis->lpVtbl->Stop(pThis); } -static MA_INLINE HRESULT ma_IDirectSoundBuffer_Unlock(ma_IDirectSoundBuffer* pThis, void* pAudioPtr1, DWORD dwAudioBytes1, void* pAudioPtr2, DWORD dwAudioBytes2) { return pThis->lpVtbl->Unlock(pThis, pAudioPtr1, dwAudioBytes1, pAudioPtr2, dwAudioBytes2); } -static MA_INLINE HRESULT ma_IDirectSoundBuffer_Restore(ma_IDirectSoundBuffer* pThis) { return pThis->lpVtbl->Restore(pThis); } - - -/* IDirectSoundCapture */ -typedef struct -{ - /* IUnknown */ - HRESULT (STDMETHODCALLTYPE * QueryInterface)(ma_IDirectSoundCapture* pThis, const IID* const riid, void** ppObject); - ULONG (STDMETHODCALLTYPE * AddRef) (ma_IDirectSoundCapture* pThis); - ULONG (STDMETHODCALLTYPE * Release) (ma_IDirectSoundCapture* pThis); - - /* IDirectSoundCapture */ - HRESULT (STDMETHODCALLTYPE * CreateCaptureBuffer)(ma_IDirectSoundCapture* pThis, const MA_DSCBUFFERDESC* pDSCBufferDesc, ma_IDirectSoundCaptureBuffer** ppDSCBuffer, void* pUnkOuter); - HRESULT (STDMETHODCALLTYPE * GetCaps) (ma_IDirectSoundCapture* pThis, MA_DSCCAPS* pDSCCaps); - HRESULT (STDMETHODCALLTYPE * Initialize) (ma_IDirectSoundCapture* pThis, const GUID* pGuidDevice); -} ma_IDirectSoundCaptureVtbl; -struct ma_IDirectSoundCapture -{ - ma_IDirectSoundCaptureVtbl* lpVtbl; -}; -static MA_INLINE HRESULT ma_IDirectSoundCapture_QueryInterface (ma_IDirectSoundCapture* pThis, const IID* const riid, void** ppObject) { return pThis->lpVtbl->QueryInterface(pThis, riid, ppObject); } -static MA_INLINE ULONG ma_IDirectSoundCapture_AddRef (ma_IDirectSoundCapture* pThis) { return pThis->lpVtbl->AddRef(pThis); } -static MA_INLINE ULONG ma_IDirectSoundCapture_Release (ma_IDirectSoundCapture* pThis) { return pThis->lpVtbl->Release(pThis); } -static MA_INLINE HRESULT ma_IDirectSoundCapture_CreateCaptureBuffer(ma_IDirectSoundCapture* pThis, const MA_DSCBUFFERDESC* pDSCBufferDesc, ma_IDirectSoundCaptureBuffer** ppDSCBuffer, void* pUnkOuter) { return pThis->lpVtbl->CreateCaptureBuffer(pThis, pDSCBufferDesc, ppDSCBuffer, pUnkOuter); } -static MA_INLINE HRESULT ma_IDirectSoundCapture_GetCaps (ma_IDirectSoundCapture* pThis, MA_DSCCAPS* pDSCCaps) { return pThis->lpVtbl->GetCaps(pThis, pDSCCaps); } -static MA_INLINE HRESULT ma_IDirectSoundCapture_Initialize (ma_IDirectSoundCapture* pThis, const GUID* pGuidDevice) { return pThis->lpVtbl->Initialize(pThis, pGuidDevice); } - - -/* IDirectSoundCaptureBuffer */ -typedef struct -{ - /* IUnknown */ - HRESULT (STDMETHODCALLTYPE * QueryInterface)(ma_IDirectSoundCaptureBuffer* pThis, const IID* const riid, void** ppObject); - ULONG (STDMETHODCALLTYPE * AddRef) (ma_IDirectSoundCaptureBuffer* pThis); - ULONG (STDMETHODCALLTYPE * Release) (ma_IDirectSoundCaptureBuffer* pThis); - - /* IDirectSoundCaptureBuffer */ - HRESULT (STDMETHODCALLTYPE * GetCaps) (ma_IDirectSoundCaptureBuffer* pThis, MA_DSCBCAPS* pDSCBCaps); - HRESULT (STDMETHODCALLTYPE * GetCurrentPosition)(ma_IDirectSoundCaptureBuffer* pThis, DWORD* pCapturePosition, DWORD* pReadPosition); - HRESULT (STDMETHODCALLTYPE * GetFormat) (ma_IDirectSoundCaptureBuffer* pThis, MA_WAVEFORMATEX* pFormat, DWORD dwSizeAllocated, DWORD* pSizeWritten); - HRESULT (STDMETHODCALLTYPE * GetStatus) (ma_IDirectSoundCaptureBuffer* pThis, DWORD* pStatus); - HRESULT (STDMETHODCALLTYPE * Initialize) (ma_IDirectSoundCaptureBuffer* pThis, ma_IDirectSoundCapture* pDirectSoundCapture, const MA_DSCBUFFERDESC* pDSCBufferDesc); - HRESULT (STDMETHODCALLTYPE * Lock) (ma_IDirectSoundCaptureBuffer* pThis, DWORD dwOffset, DWORD dwBytes, void** ppAudioPtr1, DWORD* pAudioBytes1, void** ppAudioPtr2, DWORD* pAudioBytes2, DWORD dwFlags); - HRESULT (STDMETHODCALLTYPE * Start) (ma_IDirectSoundCaptureBuffer* pThis, DWORD dwFlags); - HRESULT (STDMETHODCALLTYPE * Stop) (ma_IDirectSoundCaptureBuffer* pThis); - HRESULT (STDMETHODCALLTYPE * Unlock) (ma_IDirectSoundCaptureBuffer* pThis, void* pAudioPtr1, DWORD dwAudioBytes1, void* pAudioPtr2, DWORD dwAudioBytes2); -} ma_IDirectSoundCaptureBufferVtbl; -struct ma_IDirectSoundCaptureBuffer -{ - ma_IDirectSoundCaptureBufferVtbl* lpVtbl; -}; -static MA_INLINE HRESULT ma_IDirectSoundCaptureBuffer_QueryInterface(ma_IDirectSoundCaptureBuffer* pThis, const IID* const riid, void** ppObject) { return pThis->lpVtbl->QueryInterface(pThis, riid, ppObject); } -static MA_INLINE ULONG ma_IDirectSoundCaptureBuffer_AddRef(ma_IDirectSoundCaptureBuffer* pThis) { return pThis->lpVtbl->AddRef(pThis); } -static MA_INLINE ULONG ma_IDirectSoundCaptureBuffer_Release(ma_IDirectSoundCaptureBuffer* pThis) { return pThis->lpVtbl->Release(pThis); } -static MA_INLINE HRESULT ma_IDirectSoundCaptureBuffer_GetCaps(ma_IDirectSoundCaptureBuffer* pThis, MA_DSCBCAPS* pDSCBCaps) { return pThis->lpVtbl->GetCaps(pThis, pDSCBCaps); } -static MA_INLINE HRESULT ma_IDirectSoundCaptureBuffer_GetCurrentPosition(ma_IDirectSoundCaptureBuffer* pThis, DWORD* pCapturePosition, DWORD* pReadPosition) { return pThis->lpVtbl->GetCurrentPosition(pThis, pCapturePosition, pReadPosition); } -static MA_INLINE HRESULT ma_IDirectSoundCaptureBuffer_GetFormat(ma_IDirectSoundCaptureBuffer* pThis, MA_WAVEFORMATEX* pFormat, DWORD dwSizeAllocated, DWORD* pSizeWritten) { return pThis->lpVtbl->GetFormat(pThis, pFormat, dwSizeAllocated, pSizeWritten); } -static MA_INLINE HRESULT ma_IDirectSoundCaptureBuffer_GetStatus(ma_IDirectSoundCaptureBuffer* pThis, DWORD* pStatus) { return pThis->lpVtbl->GetStatus(pThis, pStatus); } -static MA_INLINE HRESULT ma_IDirectSoundCaptureBuffer_Initialize(ma_IDirectSoundCaptureBuffer* pThis, ma_IDirectSoundCapture* pDirectSoundCapture, const MA_DSCBUFFERDESC* pDSCBufferDesc) { return pThis->lpVtbl->Initialize(pThis, pDirectSoundCapture, pDSCBufferDesc); } -static MA_INLINE HRESULT ma_IDirectSoundCaptureBuffer_Lock(ma_IDirectSoundCaptureBuffer* pThis, DWORD dwOffset, DWORD dwBytes, void** ppAudioPtr1, DWORD* pAudioBytes1, void** ppAudioPtr2, DWORD* pAudioBytes2, DWORD dwFlags) { return pThis->lpVtbl->Lock(pThis, dwOffset, dwBytes, ppAudioPtr1, pAudioBytes1, ppAudioPtr2, pAudioBytes2, dwFlags); } -static MA_INLINE HRESULT ma_IDirectSoundCaptureBuffer_Start(ma_IDirectSoundCaptureBuffer* pThis, DWORD dwFlags) { return pThis->lpVtbl->Start(pThis, dwFlags); } -static MA_INLINE HRESULT ma_IDirectSoundCaptureBuffer_Stop(ma_IDirectSoundCaptureBuffer* pThis) { return pThis->lpVtbl->Stop(pThis); } -static MA_INLINE HRESULT ma_IDirectSoundCaptureBuffer_Unlock(ma_IDirectSoundCaptureBuffer* pThis, void* pAudioPtr1, DWORD dwAudioBytes1, void* pAudioPtr2, DWORD dwAudioBytes2) { return pThis->lpVtbl->Unlock(pThis, pAudioPtr1, dwAudioBytes1, pAudioPtr2, dwAudioBytes2); } - - -/* IDirectSoundNotify */ -typedef struct -{ - /* IUnknown */ - HRESULT (STDMETHODCALLTYPE * QueryInterface)(ma_IDirectSoundNotify* pThis, const IID* const riid, void** ppObject); - ULONG (STDMETHODCALLTYPE * AddRef) (ma_IDirectSoundNotify* pThis); - ULONG (STDMETHODCALLTYPE * Release) (ma_IDirectSoundNotify* pThis); - - /* IDirectSoundNotify */ - HRESULT (STDMETHODCALLTYPE * SetNotificationPositions)(ma_IDirectSoundNotify* pThis, DWORD dwPositionNotifies, const MA_DSBPOSITIONNOTIFY* pPositionNotifies); -} ma_IDirectSoundNotifyVtbl; -struct ma_IDirectSoundNotify -{ - ma_IDirectSoundNotifyVtbl* lpVtbl; -}; -static MA_INLINE HRESULT ma_IDirectSoundNotify_QueryInterface(ma_IDirectSoundNotify* pThis, const IID* const riid, void** ppObject) { return pThis->lpVtbl->QueryInterface(pThis, riid, ppObject); } -static MA_INLINE ULONG ma_IDirectSoundNotify_AddRef(ma_IDirectSoundNotify* pThis) { return pThis->lpVtbl->AddRef(pThis); } -static MA_INLINE ULONG ma_IDirectSoundNotify_Release(ma_IDirectSoundNotify* pThis) { return pThis->lpVtbl->Release(pThis); } -static MA_INLINE HRESULT ma_IDirectSoundNotify_SetNotificationPositions(ma_IDirectSoundNotify* pThis, DWORD dwPositionNotifies, const MA_DSBPOSITIONNOTIFY* pPositionNotifies) { return pThis->lpVtbl->SetNotificationPositions(pThis, dwPositionNotifies, pPositionNotifies); } - - -typedef BOOL (CALLBACK * ma_DSEnumCallbackAProc) (GUID* pDeviceGUID, const char* pDeviceDescription, const char* pModule, void* pContext); -typedef HRESULT (WINAPI * ma_DirectSoundCreateProc) (const GUID* pcGuidDevice, ma_IDirectSound** ppDS8, ma_IUnknown* pUnkOuter); -typedef HRESULT (WINAPI * ma_DirectSoundEnumerateAProc) (ma_DSEnumCallbackAProc pDSEnumCallback, void* pContext); -typedef HRESULT (WINAPI * ma_DirectSoundCaptureCreateProc) (const GUID* pcGuidDevice, ma_IDirectSoundCapture** ppDSC8, ma_IUnknown* pUnkOuter); -typedef HRESULT (WINAPI * ma_DirectSoundCaptureEnumerateAProc)(ma_DSEnumCallbackAProc pDSEnumCallback, void* pContext); - -static ma_uint32 ma_get_best_sample_rate_within_range(ma_uint32 sampleRateMin, ma_uint32 sampleRateMax) -{ - /* Normalize the range in case we were given something stupid. */ - if (sampleRateMin < (ma_uint32)ma_standard_sample_rate_min) { - sampleRateMin = (ma_uint32)ma_standard_sample_rate_min; - } - if (sampleRateMax > (ma_uint32)ma_standard_sample_rate_max) { - sampleRateMax = (ma_uint32)ma_standard_sample_rate_max; - } - if (sampleRateMin > sampleRateMax) { - sampleRateMin = sampleRateMax; - } - - if (sampleRateMin == sampleRateMax) { - return sampleRateMax; - } else { - size_t iStandardRate; - for (iStandardRate = 0; iStandardRate < ma_countof(g_maStandardSampleRatePriorities); ++iStandardRate) { - ma_uint32 standardRate = g_maStandardSampleRatePriorities[iStandardRate]; - if (standardRate >= sampleRateMin && standardRate <= sampleRateMax) { - return standardRate; - } - } - } - - /* Should never get here. */ - MA_ASSERT(MA_FALSE); - return 0; -} - -/* -Retrieves the channel count and channel map for the given speaker configuration. If the speaker configuration is unknown, -the channel count and channel map will be left unmodified. -*/ -static void ma_get_channels_from_speaker_config__dsound(DWORD speakerConfig, WORD* pChannelsOut, DWORD* pChannelMapOut) -{ - WORD channels; - DWORD channelMap; - - channels = 0; - if (pChannelsOut != NULL) { - channels = *pChannelsOut; - } - - channelMap = 0; - if (pChannelMapOut != NULL) { - channelMap = *pChannelMapOut; - } - - /* - The speaker configuration is a combination of speaker config and speaker geometry. The lower 8 bits is what we care about. The upper - 16 bits is for the geometry. - */ - switch ((BYTE)(speakerConfig)) { - case 1 /*DSSPEAKER_HEADPHONE*/: channels = 2; channelMap = SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT; break; - case 2 /*DSSPEAKER_MONO*/: channels = 1; channelMap = SPEAKER_FRONT_CENTER; break; - case 3 /*DSSPEAKER_QUAD*/: channels = 4; channelMap = SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT | SPEAKER_BACK_LEFT | SPEAKER_BACK_RIGHT; break; - case 4 /*DSSPEAKER_STEREO*/: channels = 2; channelMap = SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT; break; - case 5 /*DSSPEAKER_SURROUND*/: channels = 4; channelMap = SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT | SPEAKER_FRONT_CENTER | SPEAKER_BACK_CENTER; break; - case 6 /*DSSPEAKER_5POINT1_BACK*/ /*DSSPEAKER_5POINT1*/: channels = 6; channelMap = SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT | SPEAKER_FRONT_CENTER | SPEAKER_LOW_FREQUENCY | SPEAKER_BACK_LEFT | SPEAKER_BACK_RIGHT; break; - case 7 /*DSSPEAKER_7POINT1_WIDE*/ /*DSSPEAKER_7POINT1*/: channels = 8; channelMap = SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT | SPEAKER_FRONT_CENTER | SPEAKER_LOW_FREQUENCY | SPEAKER_BACK_LEFT | SPEAKER_BACK_RIGHT | SPEAKER_FRONT_LEFT_OF_CENTER | SPEAKER_FRONT_RIGHT_OF_CENTER; break; - case 8 /*DSSPEAKER_7POINT1_SURROUND*/: channels = 8; channelMap = SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT | SPEAKER_FRONT_CENTER | SPEAKER_LOW_FREQUENCY | SPEAKER_BACK_LEFT | SPEAKER_BACK_RIGHT | SPEAKER_SIDE_LEFT | SPEAKER_SIDE_RIGHT; break; - case 9 /*DSSPEAKER_5POINT1_SURROUND*/: channels = 6; channelMap = SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT | SPEAKER_FRONT_CENTER | SPEAKER_LOW_FREQUENCY | SPEAKER_SIDE_LEFT | SPEAKER_SIDE_RIGHT; break; - default: break; - } - - if (pChannelsOut != NULL) { - *pChannelsOut = channels; - } - - if (pChannelMapOut != NULL) { - *pChannelMapOut = channelMap; - } -} - - -static ma_result ma_context_create_IDirectSound__dsound(ma_context* pContext, ma_share_mode shareMode, const ma_device_id* pDeviceID, ma_IDirectSound** ppDirectSound) -{ - ma_IDirectSound* pDirectSound; - HWND hWnd; - HRESULT hr; - - MA_ASSERT(pContext != NULL); - MA_ASSERT(ppDirectSound != NULL); - - *ppDirectSound = NULL; - pDirectSound = NULL; - - if (FAILED(((ma_DirectSoundCreateProc)pContext->dsound.DirectSoundCreate)((pDeviceID == NULL) ? NULL : (const GUID*)pDeviceID->dsound, &pDirectSound, NULL))) { - ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[DirectSound] DirectSoundCreate() failed for playback device."); - return MA_FAILED_TO_OPEN_BACKEND_DEVICE; - } - - /* The cooperative level must be set before doing anything else. */ - hWnd = (HWND)pContext->dsound.hWnd; - if (hWnd == 0) { - hWnd = ((MA_PFN_GetForegroundWindow)pContext->win32.GetForegroundWindow)(); - if (hWnd == 0) { - hWnd = ((MA_PFN_GetDesktopWindow)pContext->win32.GetDesktopWindow)(); - } - } - - hr = ma_IDirectSound_SetCooperativeLevel(pDirectSound, hWnd, (shareMode == ma_share_mode_exclusive) ? MA_DSSCL_EXCLUSIVE : MA_DSSCL_PRIORITY); - if (FAILED(hr)) { - ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[DirectSound] IDirectSound_SetCooperateiveLevel() failed for playback device."); - return ma_result_from_HRESULT(hr); - } - - *ppDirectSound = pDirectSound; - return MA_SUCCESS; -} - -static ma_result ma_context_create_IDirectSoundCapture__dsound(ma_context* pContext, ma_share_mode shareMode, const ma_device_id* pDeviceID, ma_IDirectSoundCapture** ppDirectSoundCapture) -{ - ma_IDirectSoundCapture* pDirectSoundCapture; - HRESULT hr; - - MA_ASSERT(pContext != NULL); - MA_ASSERT(ppDirectSoundCapture != NULL); - - /* DirectSound does not support exclusive mode for capture. */ - if (shareMode == ma_share_mode_exclusive) { - return MA_SHARE_MODE_NOT_SUPPORTED; - } - - *ppDirectSoundCapture = NULL; - pDirectSoundCapture = NULL; - - hr = ((ma_DirectSoundCaptureCreateProc)pContext->dsound.DirectSoundCaptureCreate)((pDeviceID == NULL) ? NULL : (const GUID*)pDeviceID->dsound, &pDirectSoundCapture, NULL); - if (FAILED(hr)) { - ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[DirectSound] DirectSoundCaptureCreate() failed for capture device."); - return ma_result_from_HRESULT(hr); - } - - *ppDirectSoundCapture = pDirectSoundCapture; - return MA_SUCCESS; -} - -static ma_result ma_context_get_format_info_for_IDirectSoundCapture__dsound(ma_context* pContext, ma_IDirectSoundCapture* pDirectSoundCapture, WORD* pChannels, WORD* pBitsPerSample, DWORD* pSampleRate) -{ - HRESULT hr; - MA_DSCCAPS caps; - WORD bitsPerSample; - DWORD sampleRate; - - MA_ASSERT(pContext != NULL); - MA_ASSERT(pDirectSoundCapture != NULL); - - if (pChannels) { - *pChannels = 0; - } - if (pBitsPerSample) { - *pBitsPerSample = 0; - } - if (pSampleRate) { - *pSampleRate = 0; - } - - MA_ZERO_OBJECT(&caps); - caps.dwSize = sizeof(caps); - hr = ma_IDirectSoundCapture_GetCaps(pDirectSoundCapture, &caps); - if (FAILED(hr)) { - ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[DirectSound] IDirectSoundCapture_GetCaps() failed for capture device."); - return ma_result_from_HRESULT(hr); - } - - if (pChannels) { - *pChannels = (WORD)caps.dwChannels; - } - - /* The device can support multiple formats. We just go through the different formats in order of priority and pick the first one. This the same type of system as the WinMM backend. */ - bitsPerSample = 16; - sampleRate = 48000; - - if (caps.dwChannels == 1) { - if ((caps.dwFormats & WAVE_FORMAT_48M16) != 0) { - sampleRate = 48000; - } else if ((caps.dwFormats & WAVE_FORMAT_44M16) != 0) { - sampleRate = 44100; - } else if ((caps.dwFormats & WAVE_FORMAT_2M16) != 0) { - sampleRate = 22050; - } else if ((caps.dwFormats & WAVE_FORMAT_1M16) != 0) { - sampleRate = 11025; - } else if ((caps.dwFormats & WAVE_FORMAT_96M16) != 0) { - sampleRate = 96000; - } else { - bitsPerSample = 8; - if ((caps.dwFormats & WAVE_FORMAT_48M08) != 0) { - sampleRate = 48000; - } else if ((caps.dwFormats & WAVE_FORMAT_44M08) != 0) { - sampleRate = 44100; - } else if ((caps.dwFormats & WAVE_FORMAT_2M08) != 0) { - sampleRate = 22050; - } else if ((caps.dwFormats & WAVE_FORMAT_1M08) != 0) { - sampleRate = 11025; - } else if ((caps.dwFormats & WAVE_FORMAT_96M08) != 0) { - sampleRate = 96000; - } else { - bitsPerSample = 16; /* Didn't find it. Just fall back to 16-bit. */ - } - } - } else if (caps.dwChannels == 2) { - if ((caps.dwFormats & WAVE_FORMAT_48S16) != 0) { - sampleRate = 48000; - } else if ((caps.dwFormats & WAVE_FORMAT_44S16) != 0) { - sampleRate = 44100; - } else if ((caps.dwFormats & WAVE_FORMAT_2S16) != 0) { - sampleRate = 22050; - } else if ((caps.dwFormats & WAVE_FORMAT_1S16) != 0) { - sampleRate = 11025; - } else if ((caps.dwFormats & WAVE_FORMAT_96S16) != 0) { - sampleRate = 96000; - } else { - bitsPerSample = 8; - if ((caps.dwFormats & WAVE_FORMAT_48S08) != 0) { - sampleRate = 48000; - } else if ((caps.dwFormats & WAVE_FORMAT_44S08) != 0) { - sampleRate = 44100; - } else if ((caps.dwFormats & WAVE_FORMAT_2S08) != 0) { - sampleRate = 22050; - } else if ((caps.dwFormats & WAVE_FORMAT_1S08) != 0) { - sampleRate = 11025; - } else if ((caps.dwFormats & WAVE_FORMAT_96S08) != 0) { - sampleRate = 96000; - } else { - bitsPerSample = 16; /* Didn't find it. Just fall back to 16-bit. */ - } - } - } - - if (pBitsPerSample) { - *pBitsPerSample = bitsPerSample; - } - if (pSampleRate) { - *pSampleRate = sampleRate; - } - - return MA_SUCCESS; -} - - -typedef struct -{ - ma_context* pContext; - ma_device_type deviceType; - ma_enum_devices_callback_proc callback; - void* pUserData; - ma_bool32 terminated; -} ma_context_enumerate_devices_callback_data__dsound; - -static BOOL CALLBACK ma_context_enumerate_devices_callback__dsound(GUID* lpGuid, const char* lpcstrDescription, const char* lpcstrModule, void* lpContext) -{ - ma_context_enumerate_devices_callback_data__dsound* pData = (ma_context_enumerate_devices_callback_data__dsound*)lpContext; - ma_device_info deviceInfo; - - (void)lpcstrModule; - - MA_ZERO_OBJECT(&deviceInfo); - - /* ID. */ - if (lpGuid != NULL) { - MA_COPY_MEMORY(deviceInfo.id.dsound, lpGuid, 16); - } else { - MA_ZERO_MEMORY(deviceInfo.id.dsound, 16); - deviceInfo.isDefault = MA_TRUE; - } - - /* Name / Description */ - ma_strncpy_s(deviceInfo.name, sizeof(deviceInfo.name), lpcstrDescription, (size_t)-1); - - - /* Call the callback function, but make sure we stop enumerating if the callee requested so. */ - MA_ASSERT(pData != NULL); - pData->terminated = (pData->callback(pData->pContext, pData->deviceType, &deviceInfo, pData->pUserData) == MA_FALSE); - if (pData->terminated) { - return FALSE; /* Stop enumeration. */ - } else { - return TRUE; /* Continue enumeration. */ - } -} - -static ma_result ma_context_enumerate_devices__dsound(ma_context* pContext, ma_enum_devices_callback_proc callback, void* pUserData) -{ - ma_context_enumerate_devices_callback_data__dsound data; - - MA_ASSERT(pContext != NULL); - MA_ASSERT(callback != NULL); - - data.pContext = pContext; - data.callback = callback; - data.pUserData = pUserData; - data.terminated = MA_FALSE; - - /* Playback. */ - if (!data.terminated) { - data.deviceType = ma_device_type_playback; - ((ma_DirectSoundEnumerateAProc)pContext->dsound.DirectSoundEnumerateA)(ma_context_enumerate_devices_callback__dsound, &data); - } - - /* Capture. */ - if (!data.terminated) { - data.deviceType = ma_device_type_capture; - ((ma_DirectSoundCaptureEnumerateAProc)pContext->dsound.DirectSoundCaptureEnumerateA)(ma_context_enumerate_devices_callback__dsound, &data); - } - - return MA_SUCCESS; -} - - -typedef struct -{ - const ma_device_id* pDeviceID; - ma_device_info* pDeviceInfo; - ma_bool32 found; -} ma_context_get_device_info_callback_data__dsound; - -static BOOL CALLBACK ma_context_get_device_info_callback__dsound(GUID* lpGuid, const char* lpcstrDescription, const char* lpcstrModule, void* lpContext) -{ - ma_context_get_device_info_callback_data__dsound* pData = (ma_context_get_device_info_callback_data__dsound*)lpContext; - MA_ASSERT(pData != NULL); - - if ((pData->pDeviceID == NULL || ma_is_guid_null(pData->pDeviceID->dsound)) && (lpGuid == NULL || ma_is_guid_null(lpGuid))) { - /* Default device. */ - ma_strncpy_s(pData->pDeviceInfo->name, sizeof(pData->pDeviceInfo->name), lpcstrDescription, (size_t)-1); - pData->pDeviceInfo->isDefault = MA_TRUE; - pData->found = MA_TRUE; - return FALSE; /* Stop enumeration. */ - } else { - /* Not the default device. */ - if (lpGuid != NULL && pData->pDeviceID != NULL) { - if (memcmp(pData->pDeviceID->dsound, lpGuid, sizeof(pData->pDeviceID->dsound)) == 0) { - ma_strncpy_s(pData->pDeviceInfo->name, sizeof(pData->pDeviceInfo->name), lpcstrDescription, (size_t)-1); - pData->found = MA_TRUE; - return FALSE; /* Stop enumeration. */ - } - } - } - - (void)lpcstrModule; - return TRUE; -} - -static ma_result ma_context_get_device_info__dsound(ma_context* pContext, ma_device_type deviceType, const ma_device_id* pDeviceID, ma_device_info* pDeviceInfo) -{ - ma_result result; - HRESULT hr; - - if (pDeviceID != NULL) { - ma_context_get_device_info_callback_data__dsound data; - - /* ID. */ - MA_COPY_MEMORY(pDeviceInfo->id.dsound, pDeviceID->dsound, 16); - - /* Name / Description. This is retrieved by enumerating over each device until we find that one that matches the input ID. */ - data.pDeviceID = pDeviceID; - data.pDeviceInfo = pDeviceInfo; - data.found = MA_FALSE; - if (deviceType == ma_device_type_playback) { - ((ma_DirectSoundEnumerateAProc)pContext->dsound.DirectSoundEnumerateA)(ma_context_get_device_info_callback__dsound, &data); - } else { - ((ma_DirectSoundCaptureEnumerateAProc)pContext->dsound.DirectSoundCaptureEnumerateA)(ma_context_get_device_info_callback__dsound, &data); - } - - if (!data.found) { - return MA_NO_DEVICE; - } - } else { - /* I don't think there's a way to get the name of the default device with DirectSound. In this case we just need to use defaults. */ - - /* ID */ - MA_ZERO_MEMORY(pDeviceInfo->id.dsound, 16); - - /* Name / Description */ - if (deviceType == ma_device_type_playback) { - ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), MA_DEFAULT_PLAYBACK_DEVICE_NAME, (size_t)-1); - } else { - ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), MA_DEFAULT_CAPTURE_DEVICE_NAME, (size_t)-1); - } - - pDeviceInfo->isDefault = MA_TRUE; - } - - /* Retrieving detailed information is slightly different depending on the device type. */ - if (deviceType == ma_device_type_playback) { - /* Playback. */ - ma_IDirectSound* pDirectSound; - MA_DSCAPS caps; - WORD channels; - - result = ma_context_create_IDirectSound__dsound(pContext, ma_share_mode_shared, pDeviceID, &pDirectSound); - if (result != MA_SUCCESS) { - return result; - } - - MA_ZERO_OBJECT(&caps); - caps.dwSize = sizeof(caps); - hr = ma_IDirectSound_GetCaps(pDirectSound, &caps); - if (FAILED(hr)) { - ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[DirectSound] IDirectSound_GetCaps() failed for playback device."); - return ma_result_from_HRESULT(hr); - } - - - /* Channels. Only a single channel count is reported for DirectSound. */ - if ((caps.dwFlags & MA_DSCAPS_PRIMARYSTEREO) != 0) { - /* It supports at least stereo, but could support more. */ - DWORD speakerConfig; - - channels = 2; - - /* Look at the speaker configuration to get a better idea on the channel count. */ - hr = ma_IDirectSound_GetSpeakerConfig(pDirectSound, &speakerConfig); - if (SUCCEEDED(hr)) { - ma_get_channels_from_speaker_config__dsound(speakerConfig, &channels, NULL); - } - } else { - /* It does not support stereo, which means we are stuck with mono. */ - channels = 1; - } - - - /* - In DirectSound, our native formats are centered around sample rates. All formats are supported, and we're only reporting a single channel - count. However, DirectSound can report a range of supported sample rates. We're only going to include standard rates known by miniaudio - in order to keep the size of this within reason. - */ - if ((caps.dwFlags & MA_DSCAPS_CONTINUOUSRATE) != 0) { - /* Multiple sample rates are supported. We'll report in order of our preferred sample rates. */ - size_t iStandardSampleRate; - for (iStandardSampleRate = 0; iStandardSampleRate < ma_countof(g_maStandardSampleRatePriorities); iStandardSampleRate += 1) { - ma_uint32 sampleRate = g_maStandardSampleRatePriorities[iStandardSampleRate]; - if (sampleRate >= caps.dwMinSecondarySampleRate && sampleRate <= caps.dwMaxSecondarySampleRate) { - pDeviceInfo->nativeDataFormats[pDeviceInfo->nativeDataFormatCount].format = ma_format_unknown; - pDeviceInfo->nativeDataFormats[pDeviceInfo->nativeDataFormatCount].channels = channels; - pDeviceInfo->nativeDataFormats[pDeviceInfo->nativeDataFormatCount].sampleRate = sampleRate; - pDeviceInfo->nativeDataFormats[pDeviceInfo->nativeDataFormatCount].flags = 0; - pDeviceInfo->nativeDataFormatCount += 1; - } - } - } else { - /* Only a single sample rate is supported. */ - pDeviceInfo->nativeDataFormats[pDeviceInfo->nativeDataFormatCount].format = ma_format_unknown; - pDeviceInfo->nativeDataFormats[pDeviceInfo->nativeDataFormatCount].channels = channels; - pDeviceInfo->nativeDataFormats[pDeviceInfo->nativeDataFormatCount].sampleRate = caps.dwMaxSecondarySampleRate; - pDeviceInfo->nativeDataFormats[pDeviceInfo->nativeDataFormatCount].flags = 0; - pDeviceInfo->nativeDataFormatCount += 1; - } - - ma_IDirectSound_Release(pDirectSound); - } else { - /* - Capture. This is a little different to playback due to the say the supported formats are reported. Technically capture - devices can support a number of different formats, but for simplicity and consistency with ma_device_init() I'm just - reporting the best format. - */ - ma_IDirectSoundCapture* pDirectSoundCapture; - WORD channels; - WORD bitsPerSample; - DWORD sampleRate; - - result = ma_context_create_IDirectSoundCapture__dsound(pContext, ma_share_mode_shared, pDeviceID, &pDirectSoundCapture); - if (result != MA_SUCCESS) { - return result; - } - - result = ma_context_get_format_info_for_IDirectSoundCapture__dsound(pContext, pDirectSoundCapture, &channels, &bitsPerSample, &sampleRate); - if (result != MA_SUCCESS) { - ma_IDirectSoundCapture_Release(pDirectSoundCapture); - return result; - } - - ma_IDirectSoundCapture_Release(pDirectSoundCapture); - - /* The format is always an integer format and is based on the bits per sample. */ - if (bitsPerSample == 8) { - pDeviceInfo->nativeDataFormats[0].format = ma_format_u8; - } else if (bitsPerSample == 16) { - pDeviceInfo->nativeDataFormats[0].format = ma_format_s16; - } else if (bitsPerSample == 24) { - pDeviceInfo->nativeDataFormats[0].format = ma_format_s24; - } else if (bitsPerSample == 32) { - pDeviceInfo->nativeDataFormats[0].format = ma_format_s32; - } else { - return MA_FORMAT_NOT_SUPPORTED; - } - - pDeviceInfo->nativeDataFormats[0].channels = channels; - pDeviceInfo->nativeDataFormats[0].sampleRate = sampleRate; - pDeviceInfo->nativeDataFormats[0].flags = 0; - pDeviceInfo->nativeDataFormatCount = 1; - } - - return MA_SUCCESS; -} - - - -static ma_result ma_device_uninit__dsound(ma_device* pDevice) -{ - MA_ASSERT(pDevice != NULL); - - if (pDevice->dsound.pCaptureBuffer != NULL) { - ma_IDirectSoundCaptureBuffer_Release((ma_IDirectSoundCaptureBuffer*)pDevice->dsound.pCaptureBuffer); - } - if (pDevice->dsound.pCapture != NULL) { - ma_IDirectSoundCapture_Release((ma_IDirectSoundCapture*)pDevice->dsound.pCapture); - } - - if (pDevice->dsound.pPlaybackBuffer != NULL) { - ma_IDirectSoundBuffer_Release((ma_IDirectSoundBuffer*)pDevice->dsound.pPlaybackBuffer); - } - if (pDevice->dsound.pPlaybackPrimaryBuffer != NULL) { - ma_IDirectSoundBuffer_Release((ma_IDirectSoundBuffer*)pDevice->dsound.pPlaybackPrimaryBuffer); - } - if (pDevice->dsound.pPlayback != NULL) { - ma_IDirectSound_Release((ma_IDirectSound*)pDevice->dsound.pPlayback); - } - - return MA_SUCCESS; -} - -static ma_result ma_config_to_WAVEFORMATEXTENSIBLE(ma_format format, ma_uint32 channels, ma_uint32 sampleRate, const ma_channel* pChannelMap, MA_WAVEFORMATEXTENSIBLE* pWF) -{ - GUID subformat; - - if (format == ma_format_unknown) { - format = MA_DEFAULT_FORMAT; - } - - if (channels == 0) { - channels = MA_DEFAULT_CHANNELS; - } - - if (sampleRate == 0) { - sampleRate = MA_DEFAULT_SAMPLE_RATE; - } - - switch (format) - { - case ma_format_u8: - case ma_format_s16: - case ma_format_s24: - /*case ma_format_s24_32:*/ - case ma_format_s32: - { - subformat = MA_GUID_KSDATAFORMAT_SUBTYPE_PCM; - } break; - - case ma_format_f32: - { - subformat = MA_GUID_KSDATAFORMAT_SUBTYPE_IEEE_FLOAT; - } break; - - default: - return MA_FORMAT_NOT_SUPPORTED; - } - - MA_ZERO_OBJECT(pWF); - pWF->cbSize = sizeof(*pWF); - pWF->wFormatTag = WAVE_FORMAT_EXTENSIBLE; - pWF->nChannels = (WORD)channels; - pWF->nSamplesPerSec = (DWORD)sampleRate; - pWF->wBitsPerSample = (WORD)(ma_get_bytes_per_sample(format)*8); - pWF->nBlockAlign = (WORD)(pWF->nChannels * pWF->wBitsPerSample / 8); - pWF->nAvgBytesPerSec = pWF->nBlockAlign * pWF->nSamplesPerSec; - pWF->Samples.wValidBitsPerSample = pWF->wBitsPerSample; - pWF->dwChannelMask = ma_channel_map_to_channel_mask__win32(pChannelMap, channels); - pWF->SubFormat = subformat; - - return MA_SUCCESS; -} - -static ma_uint32 ma_calculate_period_size_in_frames_from_descriptor__dsound(const ma_device_descriptor* pDescriptor, ma_uint32 nativeSampleRate, ma_performance_profile performanceProfile) -{ - /* - DirectSound has a minimum period size of 20ms. In practice, this doesn't seem to be enough for - reliable glitch-free processing so going to use 30ms instead. - */ - ma_uint32 minPeriodSizeInFrames = ma_calculate_buffer_size_in_frames_from_milliseconds(30, nativeSampleRate); - ma_uint32 periodSizeInFrames; - - periodSizeInFrames = ma_calculate_buffer_size_in_frames_from_descriptor(pDescriptor, nativeSampleRate, performanceProfile); - if (periodSizeInFrames < minPeriodSizeInFrames) { - periodSizeInFrames = minPeriodSizeInFrames; - } - - return periodSizeInFrames; -} - -static ma_result ma_device_init__dsound(ma_device* pDevice, const ma_device_config* pConfig, ma_device_descriptor* pDescriptorPlayback, ma_device_descriptor* pDescriptorCapture) -{ - ma_result result; - HRESULT hr; - - MA_ASSERT(pDevice != NULL); - - MA_ZERO_OBJECT(&pDevice->dsound); - - if (pConfig->deviceType == ma_device_type_loopback) { - return MA_DEVICE_TYPE_NOT_SUPPORTED; - } - - /* - Unfortunately DirectSound uses different APIs and data structures for playback and capture devices. We need to initialize - the capture device first because we'll want to match its buffer size and period count on the playback side if we're using - full-duplex mode. - */ - if (pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) { - MA_WAVEFORMATEXTENSIBLE wf; - MA_DSCBUFFERDESC descDS; - ma_uint32 periodSizeInFrames; - ma_uint32 periodCount; - char rawdata[1024]; /* <-- Ugly hack to avoid a malloc() due to a crappy DirectSound API. */ - MA_WAVEFORMATEXTENSIBLE* pActualFormat; - - result = ma_config_to_WAVEFORMATEXTENSIBLE(pDescriptorCapture->format, pDescriptorCapture->channels, pDescriptorCapture->sampleRate, pDescriptorCapture->channelMap, &wf); - if (result != MA_SUCCESS) { - return result; - } - - result = ma_context_create_IDirectSoundCapture__dsound(pDevice->pContext, pDescriptorCapture->shareMode, pDescriptorCapture->pDeviceID, (ma_IDirectSoundCapture**)&pDevice->dsound.pCapture); - if (result != MA_SUCCESS) { - ma_device_uninit__dsound(pDevice); - return result; - } - - result = ma_context_get_format_info_for_IDirectSoundCapture__dsound(pDevice->pContext, (ma_IDirectSoundCapture*)pDevice->dsound.pCapture, &wf.nChannels, &wf.wBitsPerSample, &wf.nSamplesPerSec); - if (result != MA_SUCCESS) { - ma_device_uninit__dsound(pDevice); - return result; - } - - wf.nBlockAlign = (WORD)(wf.nChannels * wf.wBitsPerSample / 8); - wf.nAvgBytesPerSec = wf.nBlockAlign * wf.nSamplesPerSec; - wf.Samples.wValidBitsPerSample = wf.wBitsPerSample; - wf.SubFormat = MA_GUID_KSDATAFORMAT_SUBTYPE_PCM; - - /* The size of the buffer must be a clean multiple of the period count. */ - periodSizeInFrames = ma_calculate_period_size_in_frames_from_descriptor__dsound(pDescriptorCapture, wf.nSamplesPerSec, pConfig->performanceProfile); - periodCount = (pDescriptorCapture->periodCount > 0) ? pDescriptorCapture->periodCount : MA_DEFAULT_PERIODS; - - MA_ZERO_OBJECT(&descDS); - descDS.dwSize = sizeof(descDS); - descDS.dwFlags = 0; - descDS.dwBufferBytes = periodSizeInFrames * periodCount * wf.nBlockAlign; - descDS.lpwfxFormat = (MA_WAVEFORMATEX*)&wf; - hr = ma_IDirectSoundCapture_CreateCaptureBuffer((ma_IDirectSoundCapture*)pDevice->dsound.pCapture, &descDS, (ma_IDirectSoundCaptureBuffer**)&pDevice->dsound.pCaptureBuffer, NULL); - if (FAILED(hr)) { - ma_device_uninit__dsound(pDevice); - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[DirectSound] IDirectSoundCapture_CreateCaptureBuffer() failed for capture device."); - return ma_result_from_HRESULT(hr); - } - - /* Get the _actual_ properties of the buffer. */ - pActualFormat = (MA_WAVEFORMATEXTENSIBLE*)rawdata; - hr = ma_IDirectSoundCaptureBuffer_GetFormat((ma_IDirectSoundCaptureBuffer*)pDevice->dsound.pCaptureBuffer, (MA_WAVEFORMATEX*)pActualFormat, sizeof(rawdata), NULL); - if (FAILED(hr)) { - ma_device_uninit__dsound(pDevice); - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[DirectSound] Failed to retrieve the actual format of the capture device's buffer."); - return ma_result_from_HRESULT(hr); - } - - /* We can now start setting the output data formats. */ - pDescriptorCapture->format = ma_format_from_WAVEFORMATEX((MA_WAVEFORMATEX*)pActualFormat); - pDescriptorCapture->channels = pActualFormat->nChannels; - pDescriptorCapture->sampleRate = pActualFormat->nSamplesPerSec; - - /* Get the native channel map based on the channel mask. */ - if (pActualFormat->wFormatTag == WAVE_FORMAT_EXTENSIBLE) { - ma_channel_mask_to_channel_map__win32(pActualFormat->dwChannelMask, pDescriptorCapture->channels, pDescriptorCapture->channelMap); - } else { - ma_channel_mask_to_channel_map__win32(wf.dwChannelMask, pDescriptorCapture->channels, pDescriptorCapture->channelMap); - } - - /* - After getting the actual format the size of the buffer in frames may have actually changed. However, we want this to be as close to what the - user has asked for as possible, so let's go ahead and release the old capture buffer and create a new one in this case. - */ - if (periodSizeInFrames != (descDS.dwBufferBytes / ma_get_bytes_per_frame(pDescriptorCapture->format, pDescriptorCapture->channels) / periodCount)) { - descDS.dwBufferBytes = periodSizeInFrames * ma_get_bytes_per_frame(pDescriptorCapture->format, pDescriptorCapture->channels) * periodCount; - ma_IDirectSoundCaptureBuffer_Release((ma_IDirectSoundCaptureBuffer*)pDevice->dsound.pCaptureBuffer); - - hr = ma_IDirectSoundCapture_CreateCaptureBuffer((ma_IDirectSoundCapture*)pDevice->dsound.pCapture, &descDS, (ma_IDirectSoundCaptureBuffer**)&pDevice->dsound.pCaptureBuffer, NULL); - if (FAILED(hr)) { - ma_device_uninit__dsound(pDevice); - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[DirectSound] Second attempt at IDirectSoundCapture_CreateCaptureBuffer() failed for capture device."); - return ma_result_from_HRESULT(hr); - } - } - - /* DirectSound should give us a buffer exactly the size we asked for. */ - pDescriptorCapture->periodSizeInFrames = periodSizeInFrames; - pDescriptorCapture->periodCount = periodCount; - } - - if (pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) { - MA_WAVEFORMATEXTENSIBLE wf; - MA_DSBUFFERDESC descDSPrimary; - MA_DSCAPS caps; - char rawdata[1024]; /* <-- Ugly hack to avoid a malloc() due to a crappy DirectSound API. */ - MA_WAVEFORMATEXTENSIBLE* pActualFormat; - ma_uint32 periodSizeInFrames; - ma_uint32 periodCount; - MA_DSBUFFERDESC descDS; - WORD nativeChannelCount; - DWORD nativeChannelMask = 0; - - result = ma_config_to_WAVEFORMATEXTENSIBLE(pDescriptorPlayback->format, pDescriptorPlayback->channels, pDescriptorPlayback->sampleRate, pDescriptorPlayback->channelMap, &wf); - if (result != MA_SUCCESS) { - return result; - } - - result = ma_context_create_IDirectSound__dsound(pDevice->pContext, pDescriptorPlayback->shareMode, pDescriptorPlayback->pDeviceID, (ma_IDirectSound**)&pDevice->dsound.pPlayback); - if (result != MA_SUCCESS) { - ma_device_uninit__dsound(pDevice); - return result; - } - - MA_ZERO_OBJECT(&descDSPrimary); - descDSPrimary.dwSize = sizeof(MA_DSBUFFERDESC); - descDSPrimary.dwFlags = MA_DSBCAPS_PRIMARYBUFFER | MA_DSBCAPS_CTRLVOLUME; - hr = ma_IDirectSound_CreateSoundBuffer((ma_IDirectSound*)pDevice->dsound.pPlayback, &descDSPrimary, (ma_IDirectSoundBuffer**)&pDevice->dsound.pPlaybackPrimaryBuffer, NULL); - if (FAILED(hr)) { - ma_device_uninit__dsound(pDevice); - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[DirectSound] IDirectSound_CreateSoundBuffer() failed for playback device's primary buffer."); - return ma_result_from_HRESULT(hr); - } - - - /* We may want to make some adjustments to the format if we are using defaults. */ - MA_ZERO_OBJECT(&caps); - caps.dwSize = sizeof(caps); - hr = ma_IDirectSound_GetCaps((ma_IDirectSound*)pDevice->dsound.pPlayback, &caps); - if (FAILED(hr)) { - ma_device_uninit__dsound(pDevice); - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[DirectSound] IDirectSound_GetCaps() failed for playback device."); - return ma_result_from_HRESULT(hr); - } - - if ((caps.dwFlags & MA_DSCAPS_PRIMARYSTEREO) != 0) { - DWORD speakerConfig; - - /* It supports at least stereo, but could support more. */ - nativeChannelCount = 2; - - /* Look at the speaker configuration to get a better idea on the channel count. */ - if (SUCCEEDED(ma_IDirectSound_GetSpeakerConfig((ma_IDirectSound*)pDevice->dsound.pPlayback, &speakerConfig))) { - ma_get_channels_from_speaker_config__dsound(speakerConfig, &nativeChannelCount, &nativeChannelMask); - } - } else { - /* It does not support stereo, which means we are stuck with mono. */ - nativeChannelCount = 1; - nativeChannelMask = 0x00000001; - } - - if (pDescriptorPlayback->channels == 0) { - wf.nChannels = nativeChannelCount; - wf.dwChannelMask = nativeChannelMask; - } - - if (pDescriptorPlayback->sampleRate == 0) { - /* We base the sample rate on the values returned by GetCaps(). */ - if ((caps.dwFlags & MA_DSCAPS_CONTINUOUSRATE) != 0) { - wf.nSamplesPerSec = ma_get_best_sample_rate_within_range(caps.dwMinSecondarySampleRate, caps.dwMaxSecondarySampleRate); - } else { - wf.nSamplesPerSec = caps.dwMaxSecondarySampleRate; - } - } - - wf.nBlockAlign = (WORD)(wf.nChannels * wf.wBitsPerSample / 8); - wf.nAvgBytesPerSec = wf.nBlockAlign * wf.nSamplesPerSec; - - /* - From MSDN: - - The method succeeds even if the hardware does not support the requested format; DirectSound sets the buffer to the closest - supported format. To determine whether this has happened, an application can call the GetFormat method for the primary buffer - and compare the result with the format that was requested with the SetFormat method. - */ - hr = ma_IDirectSoundBuffer_SetFormat((ma_IDirectSoundBuffer*)pDevice->dsound.pPlaybackPrimaryBuffer, (MA_WAVEFORMATEX*)&wf); - if (FAILED(hr)) { - /* - If setting of the format failed we'll try again with some fallback settings. On Windows 98 I have - observed that IEEE_FLOAT does not work. We'll therefore enforce PCM. I also had issues where a - sample rate of 48000 did not work correctly. Not sure if it was a driver issue or not, but will - use 44100 for the sample rate. - */ - wf.cbSize = 18; /* NOTE: Don't use sizeof(MA_WAVEFORMATEX) here because it's got an extra 2 bytes due to padding. */ - wf.wFormatTag = WAVE_FORMAT_PCM; - wf.wBitsPerSample = 16; - wf.nChannels = nativeChannelCount; - wf.nSamplesPerSec = 44100; - wf.nBlockAlign = wf.nChannels * (wf.wBitsPerSample / 8); - wf.nAvgBytesPerSec = wf.nSamplesPerSec * wf.nBlockAlign; - - hr = ma_IDirectSoundBuffer_SetFormat((ma_IDirectSoundBuffer*)pDevice->dsound.pPlaybackPrimaryBuffer, (MA_WAVEFORMATEX*)&wf); - if (FAILED(hr)) { - ma_device_uninit__dsound(pDevice); - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[DirectSound] Failed to set format of playback device's primary buffer."); - return ma_result_from_HRESULT(hr); - } - } - - /* Get the _actual_ properties of the buffer. */ - pActualFormat = (MA_WAVEFORMATEXTENSIBLE*)rawdata; - hr = ma_IDirectSoundBuffer_GetFormat((ma_IDirectSoundBuffer*)pDevice->dsound.pPlaybackPrimaryBuffer, (MA_WAVEFORMATEX*)pActualFormat, sizeof(rawdata), NULL); - if (FAILED(hr)) { - ma_device_uninit__dsound(pDevice); - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[DirectSound] Failed to retrieve the actual format of the playback device's primary buffer."); - return ma_result_from_HRESULT(hr); - } - - /* We now have enough information to start setting some output properties. */ - pDescriptorPlayback->format = ma_format_from_WAVEFORMATEX((MA_WAVEFORMATEX*)pActualFormat); - pDescriptorPlayback->channels = pActualFormat->nChannels; - pDescriptorPlayback->sampleRate = pActualFormat->nSamplesPerSec; - - /* Get the internal channel map based on the channel mask. */ - if (pActualFormat->wFormatTag == WAVE_FORMAT_EXTENSIBLE) { - ma_channel_mask_to_channel_map__win32(pActualFormat->dwChannelMask, pDescriptorPlayback->channels, pDescriptorPlayback->channelMap); - } else { - ma_channel_mask_to_channel_map__win32(wf.dwChannelMask, pDescriptorPlayback->channels, pDescriptorPlayback->channelMap); - } - - /* The size of the buffer must be a clean multiple of the period count. */ - periodSizeInFrames = ma_calculate_period_size_in_frames_from_descriptor__dsound(pDescriptorPlayback, pDescriptorPlayback->sampleRate, pConfig->performanceProfile); - periodCount = (pDescriptorPlayback->periodCount > 0) ? pDescriptorPlayback->periodCount : MA_DEFAULT_PERIODS; - - /* - Meaning of dwFlags (from MSDN): - - DSBCAPS_CTRLPOSITIONNOTIFY - The buffer has position notification capability. - - DSBCAPS_GLOBALFOCUS - With this flag set, an application using DirectSound can continue to play its buffers if the user switches focus to - another application, even if the new application uses DirectSound. - - DSBCAPS_GETCURRENTPOSITION2 - In the first version of DirectSound, the play cursor was significantly ahead of the actual playing sound on emulated - sound cards; it was directly behind the write cursor. Now, if the DSBCAPS_GETCURRENTPOSITION2 flag is specified, the - application can get a more accurate play cursor. - */ - MA_ZERO_OBJECT(&descDS); - descDS.dwSize = sizeof(descDS); - descDS.dwFlags = MA_DSBCAPS_CTRLPOSITIONNOTIFY | MA_DSBCAPS_GLOBALFOCUS | MA_DSBCAPS_GETCURRENTPOSITION2; - descDS.dwBufferBytes = periodSizeInFrames * periodCount * ma_get_bytes_per_frame(pDescriptorPlayback->format, pDescriptorPlayback->channels); - descDS.lpwfxFormat = (MA_WAVEFORMATEX*)pActualFormat; - hr = ma_IDirectSound_CreateSoundBuffer((ma_IDirectSound*)pDevice->dsound.pPlayback, &descDS, (ma_IDirectSoundBuffer**)&pDevice->dsound.pPlaybackBuffer, NULL); - if (FAILED(hr)) { - ma_device_uninit__dsound(pDevice); - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[DirectSound] IDirectSound_CreateSoundBuffer() failed for playback device's secondary buffer."); - return ma_result_from_HRESULT(hr); - } - - /* DirectSound should give us a buffer exactly the size we asked for. */ - pDescriptorPlayback->periodSizeInFrames = periodSizeInFrames; - pDescriptorPlayback->periodCount = periodCount; - } - - return MA_SUCCESS; -} - - -static ma_result ma_device_data_loop__dsound(ma_device* pDevice) -{ - ma_result result = MA_SUCCESS; - ma_uint32 bpfDeviceCapture = ma_get_bytes_per_frame(pDevice->capture.internalFormat, pDevice->capture.internalChannels); - ma_uint32 bpfDevicePlayback = ma_get_bytes_per_frame(pDevice->playback.internalFormat, pDevice->playback.internalChannels); - HRESULT hr; - DWORD lockOffsetInBytesCapture; - DWORD lockSizeInBytesCapture; - DWORD mappedSizeInBytesCapture; - DWORD mappedDeviceFramesProcessedCapture; - void* pMappedDeviceBufferCapture; - DWORD lockOffsetInBytesPlayback; - DWORD lockSizeInBytesPlayback; - DWORD mappedSizeInBytesPlayback; - void* pMappedDeviceBufferPlayback; - DWORD prevReadCursorInBytesCapture = 0; - DWORD prevPlayCursorInBytesPlayback = 0; - ma_bool32 physicalPlayCursorLoopFlagPlayback = 0; - DWORD virtualWriteCursorInBytesPlayback = 0; - ma_bool32 virtualWriteCursorLoopFlagPlayback = 0; - ma_bool32 isPlaybackDeviceStarted = MA_FALSE; - ma_uint32 framesWrittenToPlaybackDevice = 0; /* For knowing whether or not the playback device needs to be started. */ - ma_uint32 waitTimeInMilliseconds = 1; - DWORD playbackBufferStatus = 0; - - MA_ASSERT(pDevice != NULL); - - /* The first thing to do is start the capture device. The playback device is only started after the first period is written. */ - if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) { - hr = ma_IDirectSoundCaptureBuffer_Start((ma_IDirectSoundCaptureBuffer*)pDevice->dsound.pCaptureBuffer, MA_DSCBSTART_LOOPING); - if (FAILED(hr)) { - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[DirectSound] IDirectSoundCaptureBuffer_Start() failed."); - return ma_result_from_HRESULT(hr); - } - } - - while (ma_device_get_state(pDevice) == ma_device_state_started) { - switch (pDevice->type) - { - case ma_device_type_duplex: - { - DWORD physicalCaptureCursorInBytes; - DWORD physicalReadCursorInBytes; - hr = ma_IDirectSoundCaptureBuffer_GetCurrentPosition((ma_IDirectSoundCaptureBuffer*)pDevice->dsound.pCaptureBuffer, &physicalCaptureCursorInBytes, &physicalReadCursorInBytes); - if (FAILED(hr)) { - return ma_result_from_HRESULT(hr); - } - - /* If nothing is available we just sleep for a bit and return from this iteration. */ - if (physicalReadCursorInBytes == prevReadCursorInBytesCapture) { - ma_sleep(waitTimeInMilliseconds); - continue; /* Nothing is available in the capture buffer. */ - } - - /* - The current position has moved. We need to map all of the captured samples and write them to the playback device, making sure - we don't return until every frame has been copied over. - */ - if (prevReadCursorInBytesCapture < physicalReadCursorInBytes) { - /* The capture position has not looped. This is the simple case. */ - lockOffsetInBytesCapture = prevReadCursorInBytesCapture; - lockSizeInBytesCapture = (physicalReadCursorInBytes - prevReadCursorInBytesCapture); - } else { - /* - The capture position has looped. This is the more complex case. Map to the end of the buffer. If this does not return anything, - do it again from the start. - */ - if (prevReadCursorInBytesCapture < pDevice->capture.internalPeriodSizeInFrames*pDevice->capture.internalPeriods*bpfDeviceCapture) { - /* Lock up to the end of the buffer. */ - lockOffsetInBytesCapture = prevReadCursorInBytesCapture; - lockSizeInBytesCapture = (pDevice->capture.internalPeriodSizeInFrames*pDevice->capture.internalPeriods*bpfDeviceCapture) - prevReadCursorInBytesCapture; - } else { - /* Lock starting from the start of the buffer. */ - lockOffsetInBytesCapture = 0; - lockSizeInBytesCapture = physicalReadCursorInBytes; - } - } - - if (lockSizeInBytesCapture == 0) { - ma_sleep(waitTimeInMilliseconds); - continue; /* Nothing is available in the capture buffer. */ - } - - hr = ma_IDirectSoundCaptureBuffer_Lock((ma_IDirectSoundCaptureBuffer*)pDevice->dsound.pCaptureBuffer, lockOffsetInBytesCapture, lockSizeInBytesCapture, &pMappedDeviceBufferCapture, &mappedSizeInBytesCapture, NULL, NULL, 0); - if (FAILED(hr)) { - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[DirectSound] Failed to map buffer from capture device in preparation for writing to the device."); - return ma_result_from_HRESULT(hr); - } - - - /* At this point we have some input data that we need to output. We do not return until every mapped frame of the input data is written to the playback device. */ - mappedDeviceFramesProcessedCapture = 0; - - for (;;) { /* Keep writing to the playback device. */ - ma_uint8 inputFramesInClientFormat[MA_DATA_CONVERTER_STACK_BUFFER_SIZE]; - ma_uint32 inputFramesInClientFormatCap = sizeof(inputFramesInClientFormat) / ma_get_bytes_per_frame(pDevice->capture.format, pDevice->capture.channels); - ma_uint8 outputFramesInClientFormat[MA_DATA_CONVERTER_STACK_BUFFER_SIZE]; - ma_uint32 outputFramesInClientFormatCap = sizeof(outputFramesInClientFormat) / ma_get_bytes_per_frame(pDevice->playback.format, pDevice->playback.channels); - ma_uint32 outputFramesInClientFormatCount; - ma_uint32 outputFramesInClientFormatConsumed = 0; - ma_uint64 clientCapturedFramesToProcess = ma_min(inputFramesInClientFormatCap, outputFramesInClientFormatCap); - ma_uint64 deviceCapturedFramesToProcess = (mappedSizeInBytesCapture / bpfDeviceCapture) - mappedDeviceFramesProcessedCapture; - void* pRunningMappedDeviceBufferCapture = ma_offset_ptr(pMappedDeviceBufferCapture, mappedDeviceFramesProcessedCapture * bpfDeviceCapture); - - result = ma_data_converter_process_pcm_frames(&pDevice->capture.converter, pRunningMappedDeviceBufferCapture, &deviceCapturedFramesToProcess, inputFramesInClientFormat, &clientCapturedFramesToProcess); - if (result != MA_SUCCESS) { - break; - } - - outputFramesInClientFormatCount = (ma_uint32)clientCapturedFramesToProcess; - mappedDeviceFramesProcessedCapture += (ma_uint32)deviceCapturedFramesToProcess; - - ma_device__handle_data_callback(pDevice, outputFramesInClientFormat, inputFramesInClientFormat, (ma_uint32)clientCapturedFramesToProcess); - - /* At this point we have input and output data in client format. All we need to do now is convert it to the output device format. This may take a few passes. */ - for (;;) { - ma_uint32 framesWrittenThisIteration; - DWORD physicalPlayCursorInBytes; - DWORD physicalWriteCursorInBytes; - DWORD availableBytesPlayback; - DWORD silentPaddingInBytes = 0; /* <-- Must be initialized to 0. */ - - /* We need the physical play and write cursors. */ - if (FAILED(ma_IDirectSoundBuffer_GetCurrentPosition((ma_IDirectSoundBuffer*)pDevice->dsound.pPlaybackBuffer, &physicalPlayCursorInBytes, &physicalWriteCursorInBytes))) { - break; - } - - if (physicalPlayCursorInBytes < prevPlayCursorInBytesPlayback) { - physicalPlayCursorLoopFlagPlayback = !physicalPlayCursorLoopFlagPlayback; - } - prevPlayCursorInBytesPlayback = physicalPlayCursorInBytes; - - /* If there's any bytes available for writing we can do that now. The space between the virtual cursor position and play cursor. */ - if (physicalPlayCursorLoopFlagPlayback == virtualWriteCursorLoopFlagPlayback) { - /* Same loop iteration. The available bytes wraps all the way around from the virtual write cursor to the physical play cursor. */ - if (physicalPlayCursorInBytes <= virtualWriteCursorInBytesPlayback) { - availableBytesPlayback = (pDevice->playback.internalPeriodSizeInFrames*pDevice->playback.internalPeriods*bpfDevicePlayback) - virtualWriteCursorInBytesPlayback; - availableBytesPlayback += physicalPlayCursorInBytes; /* Wrap around. */ - } else { - /* This is an error. */ - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_WARNING, "[DirectSound] (Duplex/Playback): Play cursor has moved in front of the write cursor (same loop iteration). physicalPlayCursorInBytes=%ld, virtualWriteCursorInBytes=%ld.\n", physicalPlayCursorInBytes, virtualWriteCursorInBytesPlayback); - availableBytesPlayback = 0; - } - } else { - /* Different loop iterations. The available bytes only goes from the virtual write cursor to the physical play cursor. */ - if (physicalPlayCursorInBytes >= virtualWriteCursorInBytesPlayback) { - availableBytesPlayback = physicalPlayCursorInBytes - virtualWriteCursorInBytesPlayback; - } else { - /* This is an error. */ - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_WARNING, "[DirectSound] (Duplex/Playback): Write cursor has moved behind the play cursor (different loop iterations). physicalPlayCursorInBytes=%ld, virtualWriteCursorInBytes=%ld.\n", physicalPlayCursorInBytes, virtualWriteCursorInBytesPlayback); - availableBytesPlayback = 0; - } - } - - /* If there's no room available for writing we need to wait for more. */ - if (availableBytesPlayback == 0) { - /* If we haven't started the device yet, this will never get beyond 0. In this case we need to get the device started. */ - if (!isPlaybackDeviceStarted) { - hr = ma_IDirectSoundBuffer_Play((ma_IDirectSoundBuffer*)pDevice->dsound.pPlaybackBuffer, 0, 0, MA_DSBPLAY_LOOPING); - if (FAILED(hr)) { - ma_IDirectSoundCaptureBuffer_Stop((ma_IDirectSoundCaptureBuffer*)pDevice->dsound.pCaptureBuffer); - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[DirectSound] IDirectSoundBuffer_Play() failed."); - return ma_result_from_HRESULT(hr); - } - isPlaybackDeviceStarted = MA_TRUE; - } else { - ma_sleep(waitTimeInMilliseconds); - continue; - } - } - - - /* Getting here means there room available somewhere. We limit this to either the end of the buffer or the physical play cursor, whichever is closest. */ - lockOffsetInBytesPlayback = virtualWriteCursorInBytesPlayback; - if (physicalPlayCursorLoopFlagPlayback == virtualWriteCursorLoopFlagPlayback) { - /* Same loop iteration. Go up to the end of the buffer. */ - lockSizeInBytesPlayback = (pDevice->playback.internalPeriodSizeInFrames*pDevice->playback.internalPeriods*bpfDevicePlayback) - virtualWriteCursorInBytesPlayback; - } else { - /* Different loop iterations. Go up to the physical play cursor. */ - lockSizeInBytesPlayback = physicalPlayCursorInBytes - virtualWriteCursorInBytesPlayback; - } - - hr = ma_IDirectSoundBuffer_Lock((ma_IDirectSoundBuffer*)pDevice->dsound.pPlaybackBuffer, lockOffsetInBytesPlayback, lockSizeInBytesPlayback, &pMappedDeviceBufferPlayback, &mappedSizeInBytesPlayback, NULL, NULL, 0); - if (FAILED(hr)) { - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[DirectSound] Failed to map buffer from playback device in preparation for writing to the device."); - result = ma_result_from_HRESULT(hr); - break; - } - - /* - Experiment: If the playback buffer is being starved, pad it with some silence to get it back in sync. This will cause a glitch, but it may prevent - endless glitching due to it constantly running out of data. - */ - if (isPlaybackDeviceStarted) { - DWORD bytesQueuedForPlayback = (pDevice->playback.internalPeriodSizeInFrames*pDevice->playback.internalPeriods*bpfDevicePlayback) - availableBytesPlayback; - if (bytesQueuedForPlayback < (pDevice->playback.internalPeriodSizeInFrames*bpfDevicePlayback)) { - silentPaddingInBytes = (pDevice->playback.internalPeriodSizeInFrames*2*bpfDevicePlayback) - bytesQueuedForPlayback; - if (silentPaddingInBytes > lockSizeInBytesPlayback) { - silentPaddingInBytes = lockSizeInBytesPlayback; - } - - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_WARNING, "[DirectSound] (Duplex/Playback) Playback buffer starved. availableBytesPlayback=%ld, silentPaddingInBytes=%ld\n", availableBytesPlayback, silentPaddingInBytes); - } - } - - /* At this point we have a buffer for output. */ - if (silentPaddingInBytes > 0) { - MA_ZERO_MEMORY(pMappedDeviceBufferPlayback, silentPaddingInBytes); - framesWrittenThisIteration = silentPaddingInBytes/bpfDevicePlayback; - } else { - ma_uint64 convertedFrameCountIn = (outputFramesInClientFormatCount - outputFramesInClientFormatConsumed); - ma_uint64 convertedFrameCountOut = mappedSizeInBytesPlayback/bpfDevicePlayback; - void* pConvertedFramesIn = ma_offset_ptr(outputFramesInClientFormat, outputFramesInClientFormatConsumed * bpfDevicePlayback); - void* pConvertedFramesOut = pMappedDeviceBufferPlayback; - - result = ma_data_converter_process_pcm_frames(&pDevice->playback.converter, pConvertedFramesIn, &convertedFrameCountIn, pConvertedFramesOut, &convertedFrameCountOut); - if (result != MA_SUCCESS) { - break; - } - - outputFramesInClientFormatConsumed += (ma_uint32)convertedFrameCountOut; - framesWrittenThisIteration = (ma_uint32)convertedFrameCountOut; - } - - - hr = ma_IDirectSoundBuffer_Unlock((ma_IDirectSoundBuffer*)pDevice->dsound.pPlaybackBuffer, pMappedDeviceBufferPlayback, framesWrittenThisIteration*bpfDevicePlayback, NULL, 0); - if (FAILED(hr)) { - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[DirectSound] Failed to unlock internal buffer from playback device after writing to the device."); - result = ma_result_from_HRESULT(hr); - break; - } - - virtualWriteCursorInBytesPlayback += framesWrittenThisIteration*bpfDevicePlayback; - if ((virtualWriteCursorInBytesPlayback/bpfDevicePlayback) == pDevice->playback.internalPeriodSizeInFrames*pDevice->playback.internalPeriods) { - virtualWriteCursorInBytesPlayback = 0; - virtualWriteCursorLoopFlagPlayback = !virtualWriteCursorLoopFlagPlayback; - } - - /* - We may need to start the device. We want two full periods to be written before starting the playback device. Having an extra period adds - a bit of a buffer to prevent the playback buffer from getting starved. - */ - framesWrittenToPlaybackDevice += framesWrittenThisIteration; - if (!isPlaybackDeviceStarted && framesWrittenToPlaybackDevice >= (pDevice->playback.internalPeriodSizeInFrames*2)) { - hr = ma_IDirectSoundBuffer_Play((ma_IDirectSoundBuffer*)pDevice->dsound.pPlaybackBuffer, 0, 0, MA_DSBPLAY_LOOPING); - if (FAILED(hr)) { - ma_IDirectSoundCaptureBuffer_Stop((ma_IDirectSoundCaptureBuffer*)pDevice->dsound.pCaptureBuffer); - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[DirectSound] IDirectSoundBuffer_Play() failed."); - return ma_result_from_HRESULT(hr); - } - isPlaybackDeviceStarted = MA_TRUE; - } - - if (framesWrittenThisIteration < mappedSizeInBytesPlayback/bpfDevicePlayback) { - break; /* We're finished with the output data.*/ - } - } - - if (clientCapturedFramesToProcess == 0) { - break; /* We just consumed every input sample. */ - } - } - - - /* At this point we're done with the mapped portion of the capture buffer. */ - hr = ma_IDirectSoundCaptureBuffer_Unlock((ma_IDirectSoundCaptureBuffer*)pDevice->dsound.pCaptureBuffer, pMappedDeviceBufferCapture, mappedSizeInBytesCapture, NULL, 0); - if (FAILED(hr)) { - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[DirectSound] Failed to unlock internal buffer from capture device after reading from the device."); - return ma_result_from_HRESULT(hr); - } - prevReadCursorInBytesCapture = (lockOffsetInBytesCapture + mappedSizeInBytesCapture); - } break; - - - - case ma_device_type_capture: - { - DWORD physicalCaptureCursorInBytes; - DWORD physicalReadCursorInBytes; - hr = ma_IDirectSoundCaptureBuffer_GetCurrentPosition((ma_IDirectSoundCaptureBuffer*)pDevice->dsound.pCaptureBuffer, &physicalCaptureCursorInBytes, &physicalReadCursorInBytes); - if (FAILED(hr)) { - return MA_ERROR; - } - - /* If the previous capture position is the same as the current position we need to wait a bit longer. */ - if (prevReadCursorInBytesCapture == physicalReadCursorInBytes) { - ma_sleep(waitTimeInMilliseconds); - continue; - } - - /* Getting here means we have capture data available. */ - if (prevReadCursorInBytesCapture < physicalReadCursorInBytes) { - /* The capture position has not looped. This is the simple case. */ - lockOffsetInBytesCapture = prevReadCursorInBytesCapture; - lockSizeInBytesCapture = (physicalReadCursorInBytes - prevReadCursorInBytesCapture); - } else { - /* - The capture position has looped. This is the more complex case. Map to the end of the buffer. If this does not return anything, - do it again from the start. - */ - if (prevReadCursorInBytesCapture < pDevice->capture.internalPeriodSizeInFrames*pDevice->capture.internalPeriods*bpfDeviceCapture) { - /* Lock up to the end of the buffer. */ - lockOffsetInBytesCapture = prevReadCursorInBytesCapture; - lockSizeInBytesCapture = (pDevice->capture.internalPeriodSizeInFrames*pDevice->capture.internalPeriods*bpfDeviceCapture) - prevReadCursorInBytesCapture; - } else { - /* Lock starting from the start of the buffer. */ - lockOffsetInBytesCapture = 0; - lockSizeInBytesCapture = physicalReadCursorInBytes; - } - } - - if (lockSizeInBytesCapture < pDevice->capture.internalPeriodSizeInFrames) { - ma_sleep(waitTimeInMilliseconds); - continue; /* Nothing is available in the capture buffer. */ - } - - hr = ma_IDirectSoundCaptureBuffer_Lock((ma_IDirectSoundCaptureBuffer*)pDevice->dsound.pCaptureBuffer, lockOffsetInBytesCapture, lockSizeInBytesCapture, &pMappedDeviceBufferCapture, &mappedSizeInBytesCapture, NULL, NULL, 0); - if (FAILED(hr)) { - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[DirectSound] Failed to map buffer from capture device in preparation for writing to the device."); - result = ma_result_from_HRESULT(hr); - } - - if (lockSizeInBytesCapture != mappedSizeInBytesCapture) { - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "[DirectSound] (Capture) lockSizeInBytesCapture=%ld != mappedSizeInBytesCapture=%ld\n", lockSizeInBytesCapture, mappedSizeInBytesCapture); - } - - ma_device__send_frames_to_client(pDevice, mappedSizeInBytesCapture/bpfDeviceCapture, pMappedDeviceBufferCapture); - - hr = ma_IDirectSoundCaptureBuffer_Unlock((ma_IDirectSoundCaptureBuffer*)pDevice->dsound.pCaptureBuffer, pMappedDeviceBufferCapture, mappedSizeInBytesCapture, NULL, 0); - if (FAILED(hr)) { - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[DirectSound] Failed to unlock internal buffer from capture device after reading from the device."); - return ma_result_from_HRESULT(hr); - } - prevReadCursorInBytesCapture = lockOffsetInBytesCapture + mappedSizeInBytesCapture; - - if (prevReadCursorInBytesCapture == (pDevice->capture.internalPeriodSizeInFrames*pDevice->capture.internalPeriods*bpfDeviceCapture)) { - prevReadCursorInBytesCapture = 0; - } - } break; - - - - case ma_device_type_playback: - { - DWORD availableBytesPlayback; - DWORD physicalPlayCursorInBytes; - DWORD physicalWriteCursorInBytes; - hr = ma_IDirectSoundBuffer_GetCurrentPosition((ma_IDirectSoundBuffer*)pDevice->dsound.pPlaybackBuffer, &physicalPlayCursorInBytes, &physicalWriteCursorInBytes); - if (FAILED(hr)) { - break; - } - - hr = ma_IDirectSoundBuffer_GetStatus((ma_IDirectSoundBuffer*)pDevice->dsound.pPlaybackBuffer, &playbackBufferStatus); - if (SUCCEEDED(hr) && (playbackBufferStatus & MA_DSBSTATUS_PLAYING) == 0 && isPlaybackDeviceStarted) { - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, "[DirectSound] Attempting to resume audio due to state: %d.", (int)playbackBufferStatus); - hr = ma_IDirectSoundBuffer_Play((ma_IDirectSoundBuffer*)pDevice->dsound.pPlaybackBuffer, 0, 0, MA_DSBPLAY_LOOPING); - if (FAILED(hr)) { - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[DirectSound] IDirectSoundBuffer_Play() failed after attempting to resume from state %d.", (int)playbackBufferStatus); - return ma_result_from_HRESULT(hr); - } - - isPlaybackDeviceStarted = MA_TRUE; - ma_sleep(waitTimeInMilliseconds); - continue; - } - - if (physicalPlayCursorInBytes < prevPlayCursorInBytesPlayback) { - physicalPlayCursorLoopFlagPlayback = !physicalPlayCursorLoopFlagPlayback; - } - prevPlayCursorInBytesPlayback = physicalPlayCursorInBytes; - - /* If there's any bytes available for writing we can do that now. The space between the virtual cursor position and play cursor. */ - if (physicalPlayCursorLoopFlagPlayback == virtualWriteCursorLoopFlagPlayback) { - /* Same loop iteration. The available bytes wraps all the way around from the virtual write cursor to the physical play cursor. */ - if (physicalPlayCursorInBytes <= virtualWriteCursorInBytesPlayback) { - availableBytesPlayback = (pDevice->playback.internalPeriodSizeInFrames*pDevice->playback.internalPeriods*bpfDevicePlayback) - virtualWriteCursorInBytesPlayback; - availableBytesPlayback += physicalPlayCursorInBytes; /* Wrap around. */ - } else { - /* This is an error. */ - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_WARNING, "[DirectSound] (Playback): Play cursor has moved in front of the write cursor (same loop iterations). physicalPlayCursorInBytes=%ld, virtualWriteCursorInBytes=%ld.\n", physicalPlayCursorInBytes, virtualWriteCursorInBytesPlayback); - availableBytesPlayback = 0; - } - } else { - /* Different loop iterations. The available bytes only goes from the virtual write cursor to the physical play cursor. */ - if (physicalPlayCursorInBytes >= virtualWriteCursorInBytesPlayback) { - availableBytesPlayback = physicalPlayCursorInBytes - virtualWriteCursorInBytesPlayback; - } else { - /* This is an error. */ - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_WARNING, "[DirectSound] (Playback): Write cursor has moved behind the play cursor (different loop iterations). physicalPlayCursorInBytes=%ld, virtualWriteCursorInBytes=%ld.\n", physicalPlayCursorInBytes, virtualWriteCursorInBytesPlayback); - availableBytesPlayback = 0; - } - } - - /* If there's no room available for writing we need to wait for more. */ - if (availableBytesPlayback < pDevice->playback.internalPeriodSizeInFrames) { - /* If we haven't started the device yet, this will never get beyond 0. In this case we need to get the device started. */ - if (availableBytesPlayback == 0 && !isPlaybackDeviceStarted) { - hr = ma_IDirectSoundBuffer_Play((ma_IDirectSoundBuffer*)pDevice->dsound.pPlaybackBuffer, 0, 0, MA_DSBPLAY_LOOPING); - if (FAILED(hr)) { - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[DirectSound] IDirectSoundBuffer_Play() failed."); - return ma_result_from_HRESULT(hr); - } - isPlaybackDeviceStarted = MA_TRUE; - } else { - ma_sleep(waitTimeInMilliseconds); - continue; - } - } - - /* Getting here means there room available somewhere. We limit this to either the end of the buffer or the physical play cursor, whichever is closest. */ - lockOffsetInBytesPlayback = virtualWriteCursorInBytesPlayback; - if (physicalPlayCursorLoopFlagPlayback == virtualWriteCursorLoopFlagPlayback) { - /* Same loop iteration. Go up to the end of the buffer. */ - lockSizeInBytesPlayback = (pDevice->playback.internalPeriodSizeInFrames*pDevice->playback.internalPeriods*bpfDevicePlayback) - virtualWriteCursorInBytesPlayback; - } else { - /* Different loop iterations. Go up to the physical play cursor. */ - lockSizeInBytesPlayback = physicalPlayCursorInBytes - virtualWriteCursorInBytesPlayback; - } - - hr = ma_IDirectSoundBuffer_Lock((ma_IDirectSoundBuffer*)pDevice->dsound.pPlaybackBuffer, lockOffsetInBytesPlayback, lockSizeInBytesPlayback, &pMappedDeviceBufferPlayback, &mappedSizeInBytesPlayback, NULL, NULL, 0); - if (FAILED(hr)) { - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[DirectSound] Failed to map buffer from playback device in preparation for writing to the device."); - result = ma_result_from_HRESULT(hr); - break; - } - - /* At this point we have a buffer for output. */ - ma_device__read_frames_from_client(pDevice, (mappedSizeInBytesPlayback/bpfDevicePlayback), pMappedDeviceBufferPlayback); - - hr = ma_IDirectSoundBuffer_Unlock((ma_IDirectSoundBuffer*)pDevice->dsound.pPlaybackBuffer, pMappedDeviceBufferPlayback, mappedSizeInBytesPlayback, NULL, 0); - if (FAILED(hr)) { - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[DirectSound] Failed to unlock internal buffer from playback device after writing to the device."); - result = ma_result_from_HRESULT(hr); - break; - } - - virtualWriteCursorInBytesPlayback += mappedSizeInBytesPlayback; - if (virtualWriteCursorInBytesPlayback == pDevice->playback.internalPeriodSizeInFrames*pDevice->playback.internalPeriods*bpfDevicePlayback) { - virtualWriteCursorInBytesPlayback = 0; - virtualWriteCursorLoopFlagPlayback = !virtualWriteCursorLoopFlagPlayback; - } - - /* - We may need to start the device. We want two full periods to be written before starting the playback device. Having an extra period adds - a bit of a buffer to prevent the playback buffer from getting starved. - */ - framesWrittenToPlaybackDevice += mappedSizeInBytesPlayback/bpfDevicePlayback; - if (!isPlaybackDeviceStarted && framesWrittenToPlaybackDevice >= pDevice->playback.internalPeriodSizeInFrames) { - hr = ma_IDirectSoundBuffer_Play((ma_IDirectSoundBuffer*)pDevice->dsound.pPlaybackBuffer, 0, 0, MA_DSBPLAY_LOOPING); - if (FAILED(hr)) { - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[DirectSound] IDirectSoundBuffer_Play() failed."); - return ma_result_from_HRESULT(hr); - } - isPlaybackDeviceStarted = MA_TRUE; - } - } break; - - - default: return MA_INVALID_ARGS; /* Invalid device type. */ - } - - if (result != MA_SUCCESS) { - return result; - } - } - - /* Getting here means the device is being stopped. */ - if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) { - hr = ma_IDirectSoundCaptureBuffer_Stop((ma_IDirectSoundCaptureBuffer*)pDevice->dsound.pCaptureBuffer); - if (FAILED(hr)) { - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[DirectSound] IDirectSoundCaptureBuffer_Stop() failed."); - return ma_result_from_HRESULT(hr); - } - } - - if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) { - /* The playback device should be drained before stopping. All we do is wait until the available bytes is equal to the size of the buffer. */ - if (isPlaybackDeviceStarted) { - for (;;) { - DWORD availableBytesPlayback = 0; - DWORD physicalPlayCursorInBytes; - DWORD physicalWriteCursorInBytes; - hr = ma_IDirectSoundBuffer_GetCurrentPosition((ma_IDirectSoundBuffer*)pDevice->dsound.pPlaybackBuffer, &physicalPlayCursorInBytes, &physicalWriteCursorInBytes); - if (FAILED(hr)) { - break; - } - - if (physicalPlayCursorInBytes < prevPlayCursorInBytesPlayback) { - physicalPlayCursorLoopFlagPlayback = !physicalPlayCursorLoopFlagPlayback; - } - prevPlayCursorInBytesPlayback = physicalPlayCursorInBytes; - - if (physicalPlayCursorLoopFlagPlayback == virtualWriteCursorLoopFlagPlayback) { - /* Same loop iteration. The available bytes wraps all the way around from the virtual write cursor to the physical play cursor. */ - if (physicalPlayCursorInBytes <= virtualWriteCursorInBytesPlayback) { - availableBytesPlayback = (pDevice->playback.internalPeriodSizeInFrames*pDevice->playback.internalPeriods*bpfDevicePlayback) - virtualWriteCursorInBytesPlayback; - availableBytesPlayback += physicalPlayCursorInBytes; /* Wrap around. */ - } else { - break; - } - } else { - /* Different loop iterations. The available bytes only goes from the virtual write cursor to the physical play cursor. */ - if (physicalPlayCursorInBytes >= virtualWriteCursorInBytesPlayback) { - availableBytesPlayback = physicalPlayCursorInBytes - virtualWriteCursorInBytesPlayback; - } else { - break; - } - } - - if (availableBytesPlayback >= (pDevice->playback.internalPeriodSizeInFrames*pDevice->playback.internalPeriods*bpfDevicePlayback)) { - break; - } - - ma_sleep(waitTimeInMilliseconds); - } - } - - hr = ma_IDirectSoundBuffer_Stop((ma_IDirectSoundBuffer*)pDevice->dsound.pPlaybackBuffer); - if (FAILED(hr)) { - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[DirectSound] IDirectSoundBuffer_Stop() failed."); - return ma_result_from_HRESULT(hr); - } - - ma_IDirectSoundBuffer_SetCurrentPosition((ma_IDirectSoundBuffer*)pDevice->dsound.pPlaybackBuffer, 0); - } - - return MA_SUCCESS; -} - -static ma_result ma_context_uninit__dsound(ma_context* pContext) -{ - MA_ASSERT(pContext != NULL); - MA_ASSERT(pContext->backend == ma_backend_dsound); - - ma_dlclose(ma_context_get_log(pContext), pContext->dsound.hDSoundDLL); - - return MA_SUCCESS; -} - -static ma_result ma_context_init__dsound(ma_context* pContext, const ma_context_config* pConfig, ma_backend_callbacks* pCallbacks) -{ - MA_ASSERT(pContext != NULL); - - (void)pConfig; - - pContext->dsound.hDSoundDLL = ma_dlopen(ma_context_get_log(pContext), "dsound.dll"); - if (pContext->dsound.hDSoundDLL == NULL) { - return MA_API_NOT_FOUND; - } - - pContext->dsound.DirectSoundCreate = ma_dlsym(ma_context_get_log(pContext), pContext->dsound.hDSoundDLL, "DirectSoundCreate"); - pContext->dsound.DirectSoundEnumerateA = ma_dlsym(ma_context_get_log(pContext), pContext->dsound.hDSoundDLL, "DirectSoundEnumerateA"); - pContext->dsound.DirectSoundCaptureCreate = ma_dlsym(ma_context_get_log(pContext), pContext->dsound.hDSoundDLL, "DirectSoundCaptureCreate"); - pContext->dsound.DirectSoundCaptureEnumerateA = ma_dlsym(ma_context_get_log(pContext), pContext->dsound.hDSoundDLL, "DirectSoundCaptureEnumerateA"); - - /* - We need to support all functions or nothing. DirectSound with Windows 95 seems to not work too - well in my testing. For example, it's missing DirectSoundCaptureEnumerateA(). This is a convenient - place to just disable the DirectSound backend for Windows 95. - */ - if (pContext->dsound.DirectSoundCreate == NULL || - pContext->dsound.DirectSoundEnumerateA == NULL || - pContext->dsound.DirectSoundCaptureCreate == NULL || - pContext->dsound.DirectSoundCaptureEnumerateA == NULL) { - return MA_API_NOT_FOUND; - } - - pContext->dsound.hWnd = pConfig->dsound.hWnd; - - pCallbacks->onContextInit = ma_context_init__dsound; - pCallbacks->onContextUninit = ma_context_uninit__dsound; - pCallbacks->onContextEnumerateDevices = ma_context_enumerate_devices__dsound; - pCallbacks->onContextGetDeviceInfo = ma_context_get_device_info__dsound; - pCallbacks->onDeviceInit = ma_device_init__dsound; - pCallbacks->onDeviceUninit = ma_device_uninit__dsound; - pCallbacks->onDeviceStart = NULL; /* Not used. Started in onDeviceDataLoop. */ - pCallbacks->onDeviceStop = NULL; /* Not used. Stopped in onDeviceDataLoop. */ - pCallbacks->onDeviceRead = NULL; /* Not used. Data is read directly in onDeviceDataLoop. */ - pCallbacks->onDeviceWrite = NULL; /* Not used. Data is written directly in onDeviceDataLoop. */ - pCallbacks->onDeviceDataLoop = ma_device_data_loop__dsound; - - return MA_SUCCESS; -} -#endif - - - -/****************************************************************************** - -WinMM Backend - -******************************************************************************/ -#ifdef MA_HAS_WINMM - -/* -Some build configurations will exclude the WinMM API. An example is when WIN32_LEAN_AND_MEAN -is defined. We need to define the types and functions we need manually. -*/ -#define MA_MMSYSERR_NOERROR 0 -#define MA_MMSYSERR_ERROR 1 -#define MA_MMSYSERR_BADDEVICEID 2 -#define MA_MMSYSERR_INVALHANDLE 5 -#define MA_MMSYSERR_NOMEM 7 -#define MA_MMSYSERR_INVALFLAG 10 -#define MA_MMSYSERR_INVALPARAM 11 -#define MA_MMSYSERR_HANDLEBUSY 12 - -#define MA_CALLBACK_EVENT 0x00050000 -#define MA_WAVE_ALLOWSYNC 0x0002 - -#define MA_WHDR_DONE 0x00000001 -#define MA_WHDR_PREPARED 0x00000002 -#define MA_WHDR_BEGINLOOP 0x00000004 -#define MA_WHDR_ENDLOOP 0x00000008 -#define MA_WHDR_INQUEUE 0x00000010 - -#define MA_MAXPNAMELEN 32 - -typedef void* MA_HWAVEIN; -typedef void* MA_HWAVEOUT; -typedef UINT MA_MMRESULT; -typedef UINT MA_MMVERSION; - -typedef struct -{ - WORD wMid; - WORD wPid; - MA_MMVERSION vDriverVersion; - CHAR szPname[MA_MAXPNAMELEN]; - DWORD dwFormats; - WORD wChannels; - WORD wReserved1; -} MA_WAVEINCAPSA; - -typedef struct -{ - WORD wMid; - WORD wPid; - MA_MMVERSION vDriverVersion; - CHAR szPname[MA_MAXPNAMELEN]; - DWORD dwFormats; - WORD wChannels; - WORD wReserved1; - DWORD dwSupport; -} MA_WAVEOUTCAPSA; - -typedef struct tagWAVEHDR -{ - char* lpData; - DWORD dwBufferLength; - DWORD dwBytesRecorded; - DWORD_PTR dwUser; - DWORD dwFlags; - DWORD dwLoops; - struct tagWAVEHDR* lpNext; - DWORD_PTR reserved; -} MA_WAVEHDR; - -typedef struct -{ - WORD wMid; - WORD wPid; - MA_MMVERSION vDriverVersion; - CHAR szPname[MA_MAXPNAMELEN]; - DWORD dwFormats; - WORD wChannels; - WORD wReserved1; - DWORD dwSupport; - GUID ManufacturerGuid; - GUID ProductGuid; - GUID NameGuid; -} MA_WAVEOUTCAPS2A; - -typedef struct -{ - WORD wMid; - WORD wPid; - MA_MMVERSION vDriverVersion; - CHAR szPname[MA_MAXPNAMELEN]; - DWORD dwFormats; - WORD wChannels; - WORD wReserved1; - GUID ManufacturerGuid; - GUID ProductGuid; - GUID NameGuid; -} MA_WAVEINCAPS2A; - -typedef UINT (WINAPI * MA_PFN_waveOutGetNumDevs)(void); -typedef MA_MMRESULT (WINAPI * MA_PFN_waveOutGetDevCapsA)(ma_uintptr uDeviceID, MA_WAVEOUTCAPSA* pwoc, UINT cbwoc); -typedef MA_MMRESULT (WINAPI * MA_PFN_waveOutOpen)(MA_HWAVEOUT* phwo, UINT uDeviceID, const MA_WAVEFORMATEX* pwfx, DWORD_PTR dwCallback, DWORD_PTR dwInstance, DWORD fdwOpen); -typedef MA_MMRESULT (WINAPI * MA_PFN_waveOutClose)(MA_HWAVEOUT hwo); -typedef MA_MMRESULT (WINAPI * MA_PFN_waveOutPrepareHeader)(MA_HWAVEOUT hwo, MA_WAVEHDR* pwh, UINT cbwh); -typedef MA_MMRESULT (WINAPI * MA_PFN_waveOutUnprepareHeader)(MA_HWAVEOUT hwo, MA_WAVEHDR* pwh, UINT cbwh); -typedef MA_MMRESULT (WINAPI * MA_PFN_waveOutWrite)(MA_HWAVEOUT hwo, MA_WAVEHDR* pwh, UINT cbwh); -typedef MA_MMRESULT (WINAPI * MA_PFN_waveOutReset)(MA_HWAVEOUT hwo); -typedef UINT (WINAPI * MA_PFN_waveInGetNumDevs)(void); -typedef MA_MMRESULT (WINAPI * MA_PFN_waveInGetDevCapsA)(ma_uintptr uDeviceID, MA_WAVEINCAPSA* pwic, UINT cbwic); -typedef MA_MMRESULT (WINAPI * MA_PFN_waveInOpen)(MA_HWAVEIN* phwi, UINT uDeviceID, const MA_WAVEFORMATEX* pwfx, DWORD_PTR dwCallback, DWORD_PTR dwInstance, DWORD fdwOpen); -typedef MA_MMRESULT (WINAPI * MA_PFN_waveInClose)(MA_HWAVEIN hwi); -typedef MA_MMRESULT (WINAPI * MA_PFN_waveInPrepareHeader)(MA_HWAVEIN hwi, MA_WAVEHDR* pwh, UINT cbwh); -typedef MA_MMRESULT (WINAPI * MA_PFN_waveInUnprepareHeader)(MA_HWAVEIN hwi, MA_WAVEHDR* pwh, UINT cbwh); -typedef MA_MMRESULT (WINAPI * MA_PFN_waveInAddBuffer)(MA_HWAVEIN hwi, MA_WAVEHDR* pwh, UINT cbwh); -typedef MA_MMRESULT (WINAPI * MA_PFN_waveInStart)(MA_HWAVEIN hwi); -typedef MA_MMRESULT (WINAPI * MA_PFN_waveInReset)(MA_HWAVEIN hwi); - -static ma_result ma_result_from_MMRESULT(MA_MMRESULT resultMM) -{ - switch (resultMM) - { - case MA_MMSYSERR_NOERROR: return MA_SUCCESS; - case MA_MMSYSERR_BADDEVICEID: return MA_INVALID_ARGS; - case MA_MMSYSERR_INVALHANDLE: return MA_INVALID_ARGS; - case MA_MMSYSERR_NOMEM: return MA_OUT_OF_MEMORY; - case MA_MMSYSERR_INVALFLAG: return MA_INVALID_ARGS; - case MA_MMSYSERR_INVALPARAM: return MA_INVALID_ARGS; - case MA_MMSYSERR_HANDLEBUSY: return MA_BUSY; - case MA_MMSYSERR_ERROR: return MA_ERROR; - default: return MA_ERROR; - } -} - -static char* ma_find_last_character(char* str, char ch) -{ - char* last; - - if (str == NULL) { - return NULL; - } - - last = NULL; - while (*str != '\0') { - if (*str == ch) { - last = str; - } - - str += 1; - } - - return last; -} - -static ma_uint32 ma_get_period_size_in_bytes(ma_uint32 periodSizeInFrames, ma_format format, ma_uint32 channels) -{ - return periodSizeInFrames * ma_get_bytes_per_frame(format, channels); -} - - -/* -Our own "WAVECAPS" structure that contains generic information shared between WAVEOUTCAPS2 and WAVEINCAPS2 so -we can do things generically and typesafely. Names are being kept the same for consistency. -*/ -typedef struct -{ - CHAR szPname[MA_MAXPNAMELEN]; - DWORD dwFormats; - WORD wChannels; - GUID NameGuid; -} MA_WAVECAPSA; - -static ma_result ma_get_best_info_from_formats_flags__winmm(DWORD dwFormats, WORD channels, WORD* pBitsPerSample, DWORD* pSampleRate) -{ - WORD bitsPerSample = 0; - DWORD sampleRate = 0; - - if (pBitsPerSample) { - *pBitsPerSample = 0; - } - if (pSampleRate) { - *pSampleRate = 0; - } - - if (channels == 1) { - bitsPerSample = 16; - if ((dwFormats & WAVE_FORMAT_48M16) != 0) { - sampleRate = 48000; - } else if ((dwFormats & WAVE_FORMAT_44M16) != 0) { - sampleRate = 44100; - } else if ((dwFormats & WAVE_FORMAT_2M16) != 0) { - sampleRate = 22050; - } else if ((dwFormats & WAVE_FORMAT_1M16) != 0) { - sampleRate = 11025; - } else if ((dwFormats & WAVE_FORMAT_96M16) != 0) { - sampleRate = 96000; - } else { - bitsPerSample = 8; - if ((dwFormats & WAVE_FORMAT_48M08) != 0) { - sampleRate = 48000; - } else if ((dwFormats & WAVE_FORMAT_44M08) != 0) { - sampleRate = 44100; - } else if ((dwFormats & WAVE_FORMAT_2M08) != 0) { - sampleRate = 22050; - } else if ((dwFormats & WAVE_FORMAT_1M08) != 0) { - sampleRate = 11025; - } else if ((dwFormats & WAVE_FORMAT_96M08) != 0) { - sampleRate = 96000; - } else { - return MA_FORMAT_NOT_SUPPORTED; - } - } - } else { - bitsPerSample = 16; - if ((dwFormats & WAVE_FORMAT_48S16) != 0) { - sampleRate = 48000; - } else if ((dwFormats & WAVE_FORMAT_44S16) != 0) { - sampleRate = 44100; - } else if ((dwFormats & WAVE_FORMAT_2S16) != 0) { - sampleRate = 22050; - } else if ((dwFormats & WAVE_FORMAT_1S16) != 0) { - sampleRate = 11025; - } else if ((dwFormats & WAVE_FORMAT_96S16) != 0) { - sampleRate = 96000; - } else { - bitsPerSample = 8; - if ((dwFormats & WAVE_FORMAT_48S08) != 0) { - sampleRate = 48000; - } else if ((dwFormats & WAVE_FORMAT_44S08) != 0) { - sampleRate = 44100; - } else if ((dwFormats & WAVE_FORMAT_2S08) != 0) { - sampleRate = 22050; - } else if ((dwFormats & WAVE_FORMAT_1S08) != 0) { - sampleRate = 11025; - } else if ((dwFormats & WAVE_FORMAT_96S08) != 0) { - sampleRate = 96000; - } else { - return MA_FORMAT_NOT_SUPPORTED; - } - } - } - - if (pBitsPerSample) { - *pBitsPerSample = bitsPerSample; - } - if (pSampleRate) { - *pSampleRate = sampleRate; - } - - return MA_SUCCESS; -} - -static ma_result ma_formats_flags_to_WAVEFORMATEX__winmm(DWORD dwFormats, WORD channels, MA_WAVEFORMATEX* pWF) -{ - ma_result result; - - MA_ASSERT(pWF != NULL); - - MA_ZERO_OBJECT(pWF); - pWF->cbSize = sizeof(*pWF); - pWF->wFormatTag = WAVE_FORMAT_PCM; - pWF->nChannels = (WORD)channels; - if (pWF->nChannels > 2) { - pWF->nChannels = 2; - } - - result = ma_get_best_info_from_formats_flags__winmm(dwFormats, channels, &pWF->wBitsPerSample, &pWF->nSamplesPerSec); - if (result != MA_SUCCESS) { - return result; - } - - pWF->nBlockAlign = (WORD)(pWF->nChannels * pWF->wBitsPerSample / 8); - pWF->nAvgBytesPerSec = pWF->nBlockAlign * pWF->nSamplesPerSec; - - return MA_SUCCESS; -} - -static ma_result ma_context_get_device_info_from_WAVECAPS(ma_context* pContext, MA_WAVECAPSA* pCaps, ma_device_info* pDeviceInfo) -{ - WORD bitsPerSample; - DWORD sampleRate; - ma_result result; - - MA_ASSERT(pContext != NULL); - MA_ASSERT(pCaps != NULL); - MA_ASSERT(pDeviceInfo != NULL); - - /* - Name / Description - - Unfortunately the name specified in WAVE(OUT/IN)CAPS2 is limited to 31 characters. This results in an unprofessional looking - situation where the names of the devices are truncated. To help work around this, we need to look at the name GUID and try - looking in the registry for the full name. If we can't find it there, we need to just fall back to the default name. - */ - - /* Set the default to begin with. */ - ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), pCaps->szPname, (size_t)-1); - - /* - Now try the registry. There's a few things to consider here: - - The name GUID can be null, in which we case we just need to stick to the original 31 characters. - - If the name GUID is not present in the registry we'll also need to stick to the original 31 characters. - - I like consistency, so I want the returned device names to be consistent with those returned by WASAPI and DirectSound. The - problem, however is that WASAPI and DirectSound use " ()" format (such as "Speakers (High Definition Audio)"), - but WinMM does not specify the component name. From my admittedly limited testing, I've notice the component name seems to - usually fit within the 31 characters of the fixed sized buffer, so what I'm going to do is parse that string for the component - name, and then concatenate the name from the registry. - */ - if (!ma_is_guid_null(&pCaps->NameGuid)) { - WCHAR guidStrW[256]; - if (((MA_PFN_StringFromGUID2)pContext->win32.StringFromGUID2)(&pCaps->NameGuid, guidStrW, ma_countof(guidStrW)) > 0) { - char guidStr[256]; - char keyStr[1024]; - HKEY hKey; - - WideCharToMultiByte(CP_UTF8, 0, guidStrW, -1, guidStr, sizeof(guidStr), 0, FALSE); - - ma_strcpy_s(keyStr, sizeof(keyStr), "SYSTEM\\CurrentControlSet\\Control\\MediaCategories\\"); - ma_strcat_s(keyStr, sizeof(keyStr), guidStr); - - if (((MA_PFN_RegOpenKeyExA)pContext->win32.RegOpenKeyExA)(HKEY_LOCAL_MACHINE, keyStr, 0, KEY_READ, &hKey) == ERROR_SUCCESS) { - BYTE nameFromReg[512]; - DWORD nameFromRegSize = sizeof(nameFromReg); - LONG resultWin32 = ((MA_PFN_RegQueryValueExA)pContext->win32.RegQueryValueExA)(hKey, "Name", 0, NULL, (BYTE*)nameFromReg, (DWORD*)&nameFromRegSize); - ((MA_PFN_RegCloseKey)pContext->win32.RegCloseKey)(hKey); - - if (resultWin32 == ERROR_SUCCESS) { - /* We have the value from the registry, so now we need to construct the name string. */ - char name[1024]; - if (ma_strcpy_s(name, sizeof(name), pDeviceInfo->name) == 0) { - char* nameBeg = ma_find_last_character(name, '('); - if (nameBeg != NULL) { - size_t leadingLen = (nameBeg - name); - ma_strncpy_s(nameBeg + 1, sizeof(name) - leadingLen, (const char*)nameFromReg, (size_t)-1); - - /* The closing ")", if it can fit. */ - if (leadingLen + nameFromRegSize < sizeof(name)-1) { - ma_strcat_s(name, sizeof(name), ")"); - } - - ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), name, (size_t)-1); - } - } - } - } - } - } - - - result = ma_get_best_info_from_formats_flags__winmm(pCaps->dwFormats, pCaps->wChannels, &bitsPerSample, &sampleRate); - if (result != MA_SUCCESS) { - return result; - } - - if (bitsPerSample == 8) { - pDeviceInfo->nativeDataFormats[0].format = ma_format_u8; - } else if (bitsPerSample == 16) { - pDeviceInfo->nativeDataFormats[0].format = ma_format_s16; - } else if (bitsPerSample == 24) { - pDeviceInfo->nativeDataFormats[0].format = ma_format_s24; - } else if (bitsPerSample == 32) { - pDeviceInfo->nativeDataFormats[0].format = ma_format_s32; - } else { - return MA_FORMAT_NOT_SUPPORTED; - } - pDeviceInfo->nativeDataFormats[0].channels = pCaps->wChannels; - pDeviceInfo->nativeDataFormats[0].sampleRate = sampleRate; - pDeviceInfo->nativeDataFormats[0].flags = 0; - pDeviceInfo->nativeDataFormatCount = 1; - - return MA_SUCCESS; -} - -static ma_result ma_context_get_device_info_from_WAVEOUTCAPS2(ma_context* pContext, MA_WAVEOUTCAPS2A* pCaps, ma_device_info* pDeviceInfo) -{ - MA_WAVECAPSA caps; - - MA_ASSERT(pContext != NULL); - MA_ASSERT(pCaps != NULL); - MA_ASSERT(pDeviceInfo != NULL); - - MA_COPY_MEMORY(caps.szPname, pCaps->szPname, sizeof(caps.szPname)); - caps.dwFormats = pCaps->dwFormats; - caps.wChannels = pCaps->wChannels; - caps.NameGuid = pCaps->NameGuid; - return ma_context_get_device_info_from_WAVECAPS(pContext, &caps, pDeviceInfo); -} - -static ma_result ma_context_get_device_info_from_WAVEINCAPS2(ma_context* pContext, MA_WAVEINCAPS2A* pCaps, ma_device_info* pDeviceInfo) -{ - MA_WAVECAPSA caps; - - MA_ASSERT(pContext != NULL); - MA_ASSERT(pCaps != NULL); - MA_ASSERT(pDeviceInfo != NULL); - - MA_COPY_MEMORY(caps.szPname, pCaps->szPname, sizeof(caps.szPname)); - caps.dwFormats = pCaps->dwFormats; - caps.wChannels = pCaps->wChannels; - caps.NameGuid = pCaps->NameGuid; - return ma_context_get_device_info_from_WAVECAPS(pContext, &caps, pDeviceInfo); -} - - -static ma_result ma_context_enumerate_devices__winmm(ma_context* pContext, ma_enum_devices_callback_proc callback, void* pUserData) -{ - UINT playbackDeviceCount; - UINT captureDeviceCount; - UINT iPlaybackDevice; - UINT iCaptureDevice; - - MA_ASSERT(pContext != NULL); - MA_ASSERT(callback != NULL); - - /* Playback. */ - playbackDeviceCount = ((MA_PFN_waveOutGetNumDevs)pContext->winmm.waveOutGetNumDevs)(); - for (iPlaybackDevice = 0; iPlaybackDevice < playbackDeviceCount; ++iPlaybackDevice) { - MA_MMRESULT result; - MA_WAVEOUTCAPS2A caps; - - MA_ZERO_OBJECT(&caps); - - result = ((MA_PFN_waveOutGetDevCapsA)pContext->winmm.waveOutGetDevCapsA)(iPlaybackDevice, (MA_WAVEOUTCAPSA*)&caps, sizeof(caps)); - if (result == MA_MMSYSERR_NOERROR) { - ma_device_info deviceInfo; - - MA_ZERO_OBJECT(&deviceInfo); - deviceInfo.id.winmm = iPlaybackDevice; - - /* The first enumerated device is the default device. */ - if (iPlaybackDevice == 0) { - deviceInfo.isDefault = MA_TRUE; - } - - if (ma_context_get_device_info_from_WAVEOUTCAPS2(pContext, &caps, &deviceInfo) == MA_SUCCESS) { - ma_bool32 cbResult = callback(pContext, ma_device_type_playback, &deviceInfo, pUserData); - if (cbResult == MA_FALSE) { - return MA_SUCCESS; /* Enumeration was stopped. */ - } - } - } - } - - /* Capture. */ - captureDeviceCount = ((MA_PFN_waveInGetNumDevs)pContext->winmm.waveInGetNumDevs)(); - for (iCaptureDevice = 0; iCaptureDevice < captureDeviceCount; ++iCaptureDevice) { - MA_MMRESULT result; - MA_WAVEINCAPS2A caps; - - MA_ZERO_OBJECT(&caps); - - result = ((MA_PFN_waveInGetDevCapsA)pContext->winmm.waveInGetDevCapsA)(iCaptureDevice, (MA_WAVEINCAPSA*)&caps, sizeof(caps)); - if (result == MA_MMSYSERR_NOERROR) { - ma_device_info deviceInfo; - - MA_ZERO_OBJECT(&deviceInfo); - deviceInfo.id.winmm = iCaptureDevice; - - /* The first enumerated device is the default device. */ - if (iCaptureDevice == 0) { - deviceInfo.isDefault = MA_TRUE; - } - - if (ma_context_get_device_info_from_WAVEINCAPS2(pContext, &caps, &deviceInfo) == MA_SUCCESS) { - ma_bool32 cbResult = callback(pContext, ma_device_type_capture, &deviceInfo, pUserData); - if (cbResult == MA_FALSE) { - return MA_SUCCESS; /* Enumeration was stopped. */ - } - } - } - } - - return MA_SUCCESS; -} - -static ma_result ma_context_get_device_info__winmm(ma_context* pContext, ma_device_type deviceType, const ma_device_id* pDeviceID, ma_device_info* pDeviceInfo) -{ - UINT winMMDeviceID; - - MA_ASSERT(pContext != NULL); - - winMMDeviceID = 0; - if (pDeviceID != NULL) { - winMMDeviceID = (UINT)pDeviceID->winmm; - } - - pDeviceInfo->id.winmm = winMMDeviceID; - - /* The first ID is the default device. */ - if (winMMDeviceID == 0) { - pDeviceInfo->isDefault = MA_TRUE; - } - - if (deviceType == ma_device_type_playback) { - MA_MMRESULT result; - MA_WAVEOUTCAPS2A caps; - - MA_ZERO_OBJECT(&caps); - - result = ((MA_PFN_waveOutGetDevCapsA)pContext->winmm.waveOutGetDevCapsA)(winMMDeviceID, (MA_WAVEOUTCAPSA*)&caps, sizeof(caps)); - if (result == MA_MMSYSERR_NOERROR) { - return ma_context_get_device_info_from_WAVEOUTCAPS2(pContext, &caps, pDeviceInfo); - } - } else { - MA_MMRESULT result; - MA_WAVEINCAPS2A caps; - - MA_ZERO_OBJECT(&caps); - - result = ((MA_PFN_waveInGetDevCapsA)pContext->winmm.waveInGetDevCapsA)(winMMDeviceID, (MA_WAVEINCAPSA*)&caps, sizeof(caps)); - if (result == MA_MMSYSERR_NOERROR) { - return ma_context_get_device_info_from_WAVEINCAPS2(pContext, &caps, pDeviceInfo); - } - } - - return MA_NO_DEVICE; -} - - -static ma_result ma_device_uninit__winmm(ma_device* pDevice) -{ - MA_ASSERT(pDevice != NULL); - - if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) { - ((MA_PFN_waveInClose)pDevice->pContext->winmm.waveInClose)((MA_HWAVEIN)pDevice->winmm.hDeviceCapture); - CloseHandle((HANDLE)pDevice->winmm.hEventCapture); - } - - if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) { - ((MA_PFN_waveOutReset)pDevice->pContext->winmm.waveOutReset)((MA_HWAVEOUT)pDevice->winmm.hDevicePlayback); - ((MA_PFN_waveOutClose)pDevice->pContext->winmm.waveOutClose)((MA_HWAVEOUT)pDevice->winmm.hDevicePlayback); - CloseHandle((HANDLE)pDevice->winmm.hEventPlayback); - } - - ma_free(pDevice->winmm._pHeapData, &pDevice->pContext->allocationCallbacks); - - MA_ZERO_OBJECT(&pDevice->winmm); /* Safety. */ - - return MA_SUCCESS; -} - -static ma_uint32 ma_calculate_period_size_in_frames_from_descriptor__winmm(const ma_device_descriptor* pDescriptor, ma_uint32 nativeSampleRate, ma_performance_profile performanceProfile) -{ - /* WinMM has a minimum period size of 40ms. */ - ma_uint32 minPeriodSizeInFrames = ma_calculate_buffer_size_in_frames_from_milliseconds(40, nativeSampleRate); - ma_uint32 periodSizeInFrames; - - periodSizeInFrames = ma_calculate_buffer_size_in_frames_from_descriptor(pDescriptor, nativeSampleRate, performanceProfile); - if (periodSizeInFrames < minPeriodSizeInFrames) { - periodSizeInFrames = minPeriodSizeInFrames; - } - - return periodSizeInFrames; -} - -static ma_result ma_device_init__winmm(ma_device* pDevice, const ma_device_config* pConfig, ma_device_descriptor* pDescriptorPlayback, ma_device_descriptor* pDescriptorCapture) -{ - const char* errorMsg = ""; - ma_result errorCode = MA_ERROR; - ma_result result = MA_SUCCESS; - ma_uint32 heapSize; - UINT winMMDeviceIDPlayback = 0; - UINT winMMDeviceIDCapture = 0; - - MA_ASSERT(pDevice != NULL); - - MA_ZERO_OBJECT(&pDevice->winmm); - - if (pConfig->deviceType == ma_device_type_loopback) { - return MA_DEVICE_TYPE_NOT_SUPPORTED; - } - - /* No exclusive mode with WinMM. */ - if (((pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) && pDescriptorPlayback->shareMode == ma_share_mode_exclusive) || - ((pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) && pDescriptorCapture->shareMode == ma_share_mode_exclusive)) { - return MA_SHARE_MODE_NOT_SUPPORTED; - } - - if (pDescriptorPlayback->pDeviceID != NULL) { - winMMDeviceIDPlayback = (UINT)pDescriptorPlayback->pDeviceID->winmm; - } - if (pDescriptorCapture->pDeviceID != NULL) { - winMMDeviceIDCapture = (UINT)pDescriptorCapture->pDeviceID->winmm; - } - - /* The capture device needs to be initialized first. */ - if (pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) { - MA_WAVEINCAPSA caps; - MA_WAVEFORMATEX wf; - MA_MMRESULT resultMM; - - /* We use an event to know when a new fragment needs to be enqueued. */ - pDevice->winmm.hEventCapture = (ma_handle)CreateEventA(NULL, TRUE, TRUE, NULL); - if (pDevice->winmm.hEventCapture == NULL) { - errorMsg = "[WinMM] Failed to create event for fragment enqueuing for the capture device.", errorCode = ma_result_from_GetLastError(GetLastError()); - goto on_error; - } - - /* The format should be based on the device's actual format. */ - if (((MA_PFN_waveInGetDevCapsA)pDevice->pContext->winmm.waveInGetDevCapsA)(winMMDeviceIDCapture, &caps, sizeof(caps)) != MA_MMSYSERR_NOERROR) { - errorMsg = "[WinMM] Failed to retrieve internal device caps.", errorCode = MA_FORMAT_NOT_SUPPORTED; - goto on_error; - } - - result = ma_formats_flags_to_WAVEFORMATEX__winmm(caps.dwFormats, caps.wChannels, &wf); - if (result != MA_SUCCESS) { - errorMsg = "[WinMM] Could not find appropriate format for internal device.", errorCode = result; - goto on_error; - } - - resultMM = ((MA_PFN_waveInOpen)pDevice->pContext->winmm.waveInOpen)((MA_HWAVEIN*)&pDevice->winmm.hDeviceCapture, winMMDeviceIDCapture, &wf, (DWORD_PTR)pDevice->winmm.hEventCapture, (DWORD_PTR)pDevice, MA_CALLBACK_EVENT | MA_WAVE_ALLOWSYNC); - if (resultMM != MA_MMSYSERR_NOERROR) { - errorMsg = "[WinMM] Failed to open capture device.", errorCode = MA_FAILED_TO_OPEN_BACKEND_DEVICE; - goto on_error; - } - - pDescriptorCapture->format = ma_format_from_WAVEFORMATEX(&wf); - pDescriptorCapture->channels = wf.nChannels; - pDescriptorCapture->sampleRate = wf.nSamplesPerSec; - ma_channel_map_init_standard(ma_standard_channel_map_microsoft, pDescriptorCapture->channelMap, ma_countof(pDescriptorCapture->channelMap), pDescriptorCapture->channels); - pDescriptorCapture->periodCount = pDescriptorCapture->periodCount; - pDescriptorCapture->periodSizeInFrames = ma_calculate_period_size_in_frames_from_descriptor__winmm(pDescriptorCapture, pDescriptorCapture->sampleRate, pConfig->performanceProfile); - } - - if (pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) { - MA_WAVEOUTCAPSA caps; - MA_WAVEFORMATEX wf; - MA_MMRESULT resultMM; - - /* We use an event to know when a new fragment needs to be enqueued. */ - pDevice->winmm.hEventPlayback = (ma_handle)CreateEventA(NULL, TRUE, TRUE, NULL); - if (pDevice->winmm.hEventPlayback == NULL) { - errorMsg = "[WinMM] Failed to create event for fragment enqueuing for the playback device.", errorCode = ma_result_from_GetLastError(GetLastError()); - goto on_error; - } - - /* The format should be based on the device's actual format. */ - if (((MA_PFN_waveOutGetDevCapsA)pDevice->pContext->winmm.waveOutGetDevCapsA)(winMMDeviceIDPlayback, &caps, sizeof(caps)) != MA_MMSYSERR_NOERROR) { - errorMsg = "[WinMM] Failed to retrieve internal device caps.", errorCode = MA_FORMAT_NOT_SUPPORTED; - goto on_error; - } - - result = ma_formats_flags_to_WAVEFORMATEX__winmm(caps.dwFormats, caps.wChannels, &wf); - if (result != MA_SUCCESS) { - errorMsg = "[WinMM] Could not find appropriate format for internal device.", errorCode = result; - goto on_error; - } - - resultMM = ((MA_PFN_waveOutOpen)pDevice->pContext->winmm.waveOutOpen)((MA_HWAVEOUT*)&pDevice->winmm.hDevicePlayback, winMMDeviceIDPlayback, &wf, (DWORD_PTR)pDevice->winmm.hEventPlayback, (DWORD_PTR)pDevice, MA_CALLBACK_EVENT | MA_WAVE_ALLOWSYNC); - if (resultMM != MA_MMSYSERR_NOERROR) { - errorMsg = "[WinMM] Failed to open playback device.", errorCode = MA_FAILED_TO_OPEN_BACKEND_DEVICE; - goto on_error; - } - - pDescriptorPlayback->format = ma_format_from_WAVEFORMATEX(&wf); - pDescriptorPlayback->channels = wf.nChannels; - pDescriptorPlayback->sampleRate = wf.nSamplesPerSec; - ma_channel_map_init_standard(ma_standard_channel_map_microsoft, pDescriptorPlayback->channelMap, ma_countof(pDescriptorPlayback->channelMap), pDescriptorPlayback->channels); - pDescriptorPlayback->periodCount = pDescriptorPlayback->periodCount; - pDescriptorPlayback->periodSizeInFrames = ma_calculate_period_size_in_frames_from_descriptor__winmm(pDescriptorPlayback, pDescriptorPlayback->sampleRate, pConfig->performanceProfile); - } - - /* - The heap allocated data is allocated like so: - - [Capture WAVEHDRs][Playback WAVEHDRs][Capture Intermediary Buffer][Playback Intermediary Buffer] - */ - heapSize = 0; - if (pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) { - heapSize += sizeof(MA_WAVEHDR)*pDescriptorCapture->periodCount + (pDescriptorCapture->periodSizeInFrames * pDescriptorCapture->periodCount * ma_get_bytes_per_frame(pDescriptorCapture->format, pDescriptorCapture->channels)); - } - if (pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) { - heapSize += sizeof(MA_WAVEHDR)*pDescriptorPlayback->periodCount + (pDescriptorPlayback->periodSizeInFrames * pDescriptorPlayback->periodCount * ma_get_bytes_per_frame(pDescriptorPlayback->format, pDescriptorPlayback->channels)); - } - - pDevice->winmm._pHeapData = (ma_uint8*)ma_calloc(heapSize, &pDevice->pContext->allocationCallbacks); - if (pDevice->winmm._pHeapData == NULL) { - errorMsg = "[WinMM] Failed to allocate memory for the intermediary buffer.", errorCode = MA_OUT_OF_MEMORY; - goto on_error; - } - - MA_ZERO_MEMORY(pDevice->winmm._pHeapData, heapSize); - - if (pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) { - ma_uint32 iPeriod; - - if (pConfig->deviceType == ma_device_type_capture) { - pDevice->winmm.pWAVEHDRCapture = pDevice->winmm._pHeapData; - pDevice->winmm.pIntermediaryBufferCapture = pDevice->winmm._pHeapData + (sizeof(MA_WAVEHDR)*(pDescriptorCapture->periodCount)); - } else { - pDevice->winmm.pWAVEHDRCapture = pDevice->winmm._pHeapData; - pDevice->winmm.pIntermediaryBufferCapture = pDevice->winmm._pHeapData + (sizeof(MA_WAVEHDR)*(pDescriptorCapture->periodCount + pDescriptorPlayback->periodCount)); - } - - /* Prepare headers. */ - for (iPeriod = 0; iPeriod < pDescriptorCapture->periodCount; ++iPeriod) { - ma_uint32 periodSizeInBytes = ma_get_period_size_in_bytes(pDescriptorCapture->periodSizeInFrames, pDescriptorCapture->format, pDescriptorCapture->channels); - - ((MA_WAVEHDR*)pDevice->winmm.pWAVEHDRCapture)[iPeriod].lpData = (char*)(pDevice->winmm.pIntermediaryBufferCapture + (periodSizeInBytes*iPeriod)); - ((MA_WAVEHDR*)pDevice->winmm.pWAVEHDRCapture)[iPeriod].dwBufferLength = periodSizeInBytes; - ((MA_WAVEHDR*)pDevice->winmm.pWAVEHDRCapture)[iPeriod].dwFlags = 0L; - ((MA_WAVEHDR*)pDevice->winmm.pWAVEHDRCapture)[iPeriod].dwLoops = 0L; - ((MA_PFN_waveInPrepareHeader)pDevice->pContext->winmm.waveInPrepareHeader)((MA_HWAVEIN)pDevice->winmm.hDeviceCapture, &((MA_WAVEHDR*)pDevice->winmm.pWAVEHDRCapture)[iPeriod], sizeof(MA_WAVEHDR)); - - /* - The user data of the MA_WAVEHDR structure is a single flag the controls whether or not it is ready for writing. Consider it to be named "isLocked". A value of 0 means - it's unlocked and available for writing. A value of 1 means it's locked. - */ - ((MA_WAVEHDR*)pDevice->winmm.pWAVEHDRCapture)[iPeriod].dwUser = 0; - } - } - - if (pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) { - ma_uint32 iPeriod; - - if (pConfig->deviceType == ma_device_type_playback) { - pDevice->winmm.pWAVEHDRPlayback = pDevice->winmm._pHeapData; - pDevice->winmm.pIntermediaryBufferPlayback = pDevice->winmm._pHeapData + (sizeof(MA_WAVEHDR)*pDescriptorPlayback->periodCount); - } else { - pDevice->winmm.pWAVEHDRPlayback = pDevice->winmm._pHeapData + (sizeof(MA_WAVEHDR)*(pDescriptorCapture->periodCount)); - pDevice->winmm.pIntermediaryBufferPlayback = pDevice->winmm._pHeapData + (sizeof(MA_WAVEHDR)*(pDescriptorCapture->periodCount + pDescriptorPlayback->periodCount)) + (pDescriptorCapture->periodSizeInFrames*pDescriptorCapture->periodCount*ma_get_bytes_per_frame(pDescriptorCapture->format, pDescriptorCapture->channels)); - } - - /* Prepare headers. */ - for (iPeriod = 0; iPeriod < pDescriptorPlayback->periodCount; ++iPeriod) { - ma_uint32 periodSizeInBytes = ma_get_period_size_in_bytes(pDescriptorPlayback->periodSizeInFrames, pDescriptorPlayback->format, pDescriptorPlayback->channels); - - ((MA_WAVEHDR*)pDevice->winmm.pWAVEHDRPlayback)[iPeriod].lpData = (char*)(pDevice->winmm.pIntermediaryBufferPlayback + (periodSizeInBytes*iPeriod)); - ((MA_WAVEHDR*)pDevice->winmm.pWAVEHDRPlayback)[iPeriod].dwBufferLength = periodSizeInBytes; - ((MA_WAVEHDR*)pDevice->winmm.pWAVEHDRPlayback)[iPeriod].dwFlags = 0L; - ((MA_WAVEHDR*)pDevice->winmm.pWAVEHDRPlayback)[iPeriod].dwLoops = 0L; - ((MA_PFN_waveOutPrepareHeader)pDevice->pContext->winmm.waveOutPrepareHeader)((MA_HWAVEOUT)pDevice->winmm.hDevicePlayback, &((MA_WAVEHDR*)pDevice->winmm.pWAVEHDRPlayback)[iPeriod], sizeof(MA_WAVEHDR)); - - /* - The user data of the MA_WAVEHDR structure is a single flag the controls whether or not it is ready for writing. Consider it to be named "isLocked". A value of 0 means - it's unlocked and available for writing. A value of 1 means it's locked. - */ - ((MA_WAVEHDR*)pDevice->winmm.pWAVEHDRPlayback)[iPeriod].dwUser = 0; - } - } - - return MA_SUCCESS; - -on_error: - if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) { - if (pDevice->winmm.pWAVEHDRCapture != NULL) { - ma_uint32 iPeriod; - for (iPeriod = 0; iPeriod < pDescriptorCapture->periodCount; ++iPeriod) { - ((MA_PFN_waveInUnprepareHeader)pDevice->pContext->winmm.waveInUnprepareHeader)((MA_HWAVEIN)pDevice->winmm.hDeviceCapture, &((MA_WAVEHDR*)pDevice->winmm.pWAVEHDRCapture)[iPeriod], sizeof(MA_WAVEHDR)); - } - } - - ((MA_PFN_waveInClose)pDevice->pContext->winmm.waveInClose)((MA_HWAVEIN)pDevice->winmm.hDeviceCapture); - } - - if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) { - if (pDevice->winmm.pWAVEHDRCapture != NULL) { - ma_uint32 iPeriod; - for (iPeriod = 0; iPeriod < pDescriptorPlayback->periodCount; ++iPeriod) { - ((MA_PFN_waveOutUnprepareHeader)pDevice->pContext->winmm.waveOutUnprepareHeader)((MA_HWAVEOUT)pDevice->winmm.hDevicePlayback, &((MA_WAVEHDR*)pDevice->winmm.pWAVEHDRPlayback)[iPeriod], sizeof(MA_WAVEHDR)); - } - } - - ((MA_PFN_waveOutClose)pDevice->pContext->winmm.waveOutClose)((MA_HWAVEOUT)pDevice->winmm.hDevicePlayback); - } - - ma_free(pDevice->winmm._pHeapData, &pDevice->pContext->allocationCallbacks); - - if (errorMsg != NULL && errorMsg[0] != '\0') { - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "%s", errorMsg); - } - - return errorCode; -} - -static ma_result ma_device_start__winmm(ma_device* pDevice) -{ - MA_ASSERT(pDevice != NULL); - - if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) { - MA_MMRESULT resultMM; - MA_WAVEHDR* pWAVEHDR; - ma_uint32 iPeriod; - - pWAVEHDR = (MA_WAVEHDR*)pDevice->winmm.pWAVEHDRCapture; - - /* Make sure the event is reset to a non-signaled state to ensure we don't prematurely return from WaitForSingleObject(). */ - ResetEvent((HANDLE)pDevice->winmm.hEventCapture); - - /* To start the device we attach all of the buffers and then start it. As the buffers are filled with data we will get notifications. */ - for (iPeriod = 0; iPeriod < pDevice->capture.internalPeriods; ++iPeriod) { - resultMM = ((MA_PFN_waveInAddBuffer)pDevice->pContext->winmm.waveInAddBuffer)((MA_HWAVEIN)pDevice->winmm.hDeviceCapture, &((MA_WAVEHDR*)pDevice->winmm.pWAVEHDRCapture)[iPeriod], sizeof(MA_WAVEHDR)); - if (resultMM != MA_MMSYSERR_NOERROR) { - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[WinMM] Failed to attach input buffers to capture device in preparation for capture."); - return ma_result_from_MMRESULT(resultMM); - } - - /* Make sure all of the buffers start out locked. We don't want to access them until the backend tells us we can. */ - pWAVEHDR[iPeriod].dwUser = 1; /* 1 = locked. */ - } - - /* Capture devices need to be explicitly started, unlike playback devices. */ - resultMM = ((MA_PFN_waveInStart)pDevice->pContext->winmm.waveInStart)((MA_HWAVEIN)pDevice->winmm.hDeviceCapture); - if (resultMM != MA_MMSYSERR_NOERROR) { - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[WinMM] Failed to start backend device."); - return ma_result_from_MMRESULT(resultMM); - } - } - - if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) { - /* Don't need to do anything for playback. It'll be started automatically in ma_device_start__winmm(). */ - } - - return MA_SUCCESS; -} - -static ma_result ma_device_stop__winmm(ma_device* pDevice) -{ - MA_MMRESULT resultMM; - - MA_ASSERT(pDevice != NULL); - - if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) { - if (pDevice->winmm.hDeviceCapture == NULL) { - return MA_INVALID_ARGS; - } - - resultMM = ((MA_PFN_waveInReset)pDevice->pContext->winmm.waveInReset)((MA_HWAVEIN)pDevice->winmm.hDeviceCapture); - if (resultMM != MA_MMSYSERR_NOERROR) { - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_WARNING, "[WinMM] WARNING: Failed to reset capture device."); - } - } - - if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) { - ma_uint32 iPeriod; - MA_WAVEHDR* pWAVEHDR; - - if (pDevice->winmm.hDevicePlayback == NULL) { - return MA_INVALID_ARGS; - } - - /* We need to drain the device. To do this we just loop over each header and if it's locked just wait for the event. */ - pWAVEHDR = (MA_WAVEHDR*)pDevice->winmm.pWAVEHDRPlayback; - for (iPeriod = 0; iPeriod < pDevice->playback.internalPeriods; iPeriod += 1) { - if (pWAVEHDR[iPeriod].dwUser == 1) { /* 1 = locked. */ - if (WaitForSingleObject((HANDLE)pDevice->winmm.hEventPlayback, INFINITE) != WAIT_OBJECT_0) { - break; /* An error occurred so just abandon ship and stop the device without draining. */ - } - - pWAVEHDR[iPeriod].dwUser = 0; - } - } - - resultMM = ((MA_PFN_waveOutReset)pDevice->pContext->winmm.waveOutReset)((MA_HWAVEOUT)pDevice->winmm.hDevicePlayback); - if (resultMM != MA_MMSYSERR_NOERROR) { - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_WARNING, "[WinMM] WARNING: Failed to reset playback device."); - } - } - - return MA_SUCCESS; -} - -static ma_result ma_device_write__winmm(ma_device* pDevice, const void* pPCMFrames, ma_uint32 frameCount, ma_uint32* pFramesWritten) -{ - ma_result result = MA_SUCCESS; - MA_MMRESULT resultMM; - ma_uint32 totalFramesWritten; - MA_WAVEHDR* pWAVEHDR; - - MA_ASSERT(pDevice != NULL); - MA_ASSERT(pPCMFrames != NULL); - - if (pFramesWritten != NULL) { - *pFramesWritten = 0; - } - - pWAVEHDR = (MA_WAVEHDR*)pDevice->winmm.pWAVEHDRPlayback; - - /* Keep processing as much data as possible. */ - totalFramesWritten = 0; - while (totalFramesWritten < frameCount) { - /* If the current header has some space available we need to write part of it. */ - if (pWAVEHDR[pDevice->winmm.iNextHeaderPlayback].dwUser == 0) { /* 0 = unlocked. */ - /* - This header has room in it. We copy as much of it as we can. If we end up fully consuming the buffer we need to - write it out and move on to the next iteration. - */ - ma_uint32 bpf = ma_get_bytes_per_frame(pDevice->playback.internalFormat, pDevice->playback.internalChannels); - ma_uint32 framesRemainingInHeader = (pWAVEHDR[pDevice->winmm.iNextHeaderPlayback].dwBufferLength/bpf) - pDevice->winmm.headerFramesConsumedPlayback; - - ma_uint32 framesToCopy = ma_min(framesRemainingInHeader, (frameCount - totalFramesWritten)); - const void* pSrc = ma_offset_ptr(pPCMFrames, totalFramesWritten*bpf); - void* pDst = ma_offset_ptr(pWAVEHDR[pDevice->winmm.iNextHeaderPlayback].lpData, pDevice->winmm.headerFramesConsumedPlayback*bpf); - MA_COPY_MEMORY(pDst, pSrc, framesToCopy*bpf); - - pDevice->winmm.headerFramesConsumedPlayback += framesToCopy; - totalFramesWritten += framesToCopy; - - /* If we've consumed the buffer entirely we need to write it out to the device. */ - if (pDevice->winmm.headerFramesConsumedPlayback == (pWAVEHDR[pDevice->winmm.iNextHeaderPlayback].dwBufferLength/bpf)) { - pWAVEHDR[pDevice->winmm.iNextHeaderPlayback].dwUser = 1; /* 1 = locked. */ - pWAVEHDR[pDevice->winmm.iNextHeaderPlayback].dwFlags &= ~MA_WHDR_DONE; /* <-- Need to make sure the WHDR_DONE flag is unset. */ - - /* Make sure the event is reset to a non-signaled state to ensure we don't prematurely return from WaitForSingleObject(). */ - ResetEvent((HANDLE)pDevice->winmm.hEventPlayback); - - /* The device will be started here. */ - resultMM = ((MA_PFN_waveOutWrite)pDevice->pContext->winmm.waveOutWrite)((MA_HWAVEOUT)pDevice->winmm.hDevicePlayback, &pWAVEHDR[pDevice->winmm.iNextHeaderPlayback], sizeof(MA_WAVEHDR)); - if (resultMM != MA_MMSYSERR_NOERROR) { - result = ma_result_from_MMRESULT(resultMM); - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[WinMM] waveOutWrite() failed."); - break; - } - - /* Make sure we move to the next header. */ - pDevice->winmm.iNextHeaderPlayback = (pDevice->winmm.iNextHeaderPlayback + 1) % pDevice->playback.internalPeriods; - pDevice->winmm.headerFramesConsumedPlayback = 0; - } - - /* If at this point we have consumed the entire input buffer we can return. */ - MA_ASSERT(totalFramesWritten <= frameCount); - if (totalFramesWritten == frameCount) { - break; - } - - /* Getting here means there's more to process. */ - continue; - } - - /* Getting here means there isn't enough room in the buffer and we need to wait for one to become available. */ - if (WaitForSingleObject((HANDLE)pDevice->winmm.hEventPlayback, INFINITE) != WAIT_OBJECT_0) { - result = MA_ERROR; - break; - } - - /* Something happened. If the next buffer has been marked as done we need to reset a bit of state. */ - if ((pWAVEHDR[pDevice->winmm.iNextHeaderPlayback].dwFlags & MA_WHDR_DONE) != 0) { - pWAVEHDR[pDevice->winmm.iNextHeaderPlayback].dwUser = 0; /* 0 = unlocked (make it available for writing). */ - pDevice->winmm.headerFramesConsumedPlayback = 0; - } - - /* If the device has been stopped we need to break. */ - if (ma_device_get_state(pDevice) != ma_device_state_started) { - break; - } - } - - if (pFramesWritten != NULL) { - *pFramesWritten = totalFramesWritten; - } - - return result; -} - -static ma_result ma_device_read__winmm(ma_device* pDevice, void* pPCMFrames, ma_uint32 frameCount, ma_uint32* pFramesRead) -{ - ma_result result = MA_SUCCESS; - MA_MMRESULT resultMM; - ma_uint32 totalFramesRead; - MA_WAVEHDR* pWAVEHDR; - - MA_ASSERT(pDevice != NULL); - MA_ASSERT(pPCMFrames != NULL); - - if (pFramesRead != NULL) { - *pFramesRead = 0; - } - - pWAVEHDR = (MA_WAVEHDR*)pDevice->winmm.pWAVEHDRCapture; - - /* Keep processing as much data as possible. */ - totalFramesRead = 0; - while (totalFramesRead < frameCount) { - /* If the current header has some space available we need to write part of it. */ - if (pWAVEHDR[pDevice->winmm.iNextHeaderCapture].dwUser == 0) { /* 0 = unlocked. */ - /* The buffer is available for reading. If we fully consume it we need to add it back to the buffer. */ - ma_uint32 bpf = ma_get_bytes_per_frame(pDevice->capture.internalFormat, pDevice->capture.internalChannels); - ma_uint32 framesRemainingInHeader = (pWAVEHDR[pDevice->winmm.iNextHeaderCapture].dwBufferLength/bpf) - pDevice->winmm.headerFramesConsumedCapture; - - ma_uint32 framesToCopy = ma_min(framesRemainingInHeader, (frameCount - totalFramesRead)); - const void* pSrc = ma_offset_ptr(pWAVEHDR[pDevice->winmm.iNextHeaderCapture].lpData, pDevice->winmm.headerFramesConsumedCapture*bpf); - void* pDst = ma_offset_ptr(pPCMFrames, totalFramesRead*bpf); - MA_COPY_MEMORY(pDst, pSrc, framesToCopy*bpf); - - pDevice->winmm.headerFramesConsumedCapture += framesToCopy; - totalFramesRead += framesToCopy; - - /* If we've consumed the buffer entirely we need to add it back to the device. */ - if (pDevice->winmm.headerFramesConsumedCapture == (pWAVEHDR[pDevice->winmm.iNextHeaderCapture].dwBufferLength/bpf)) { - pWAVEHDR[pDevice->winmm.iNextHeaderCapture].dwUser = 1; /* 1 = locked. */ - pWAVEHDR[pDevice->winmm.iNextHeaderCapture].dwFlags &= ~MA_WHDR_DONE; /* <-- Need to make sure the WHDR_DONE flag is unset. */ - - /* Make sure the event is reset to a non-signaled state to ensure we don't prematurely return from WaitForSingleObject(). */ - ResetEvent((HANDLE)pDevice->winmm.hEventCapture); - - /* The device will be started here. */ - resultMM = ((MA_PFN_waveInAddBuffer)pDevice->pContext->winmm.waveInAddBuffer)((MA_HWAVEIN)pDevice->winmm.hDeviceCapture, &((MA_WAVEHDR*)pDevice->winmm.pWAVEHDRCapture)[pDevice->winmm.iNextHeaderCapture], sizeof(MA_WAVEHDR)); - if (resultMM != MA_MMSYSERR_NOERROR) { - result = ma_result_from_MMRESULT(resultMM); - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[WinMM] waveInAddBuffer() failed."); - break; - } - - /* Make sure we move to the next header. */ - pDevice->winmm.iNextHeaderCapture = (pDevice->winmm.iNextHeaderCapture + 1) % pDevice->capture.internalPeriods; - pDevice->winmm.headerFramesConsumedCapture = 0; - } - - /* If at this point we have filled the entire input buffer we can return. */ - MA_ASSERT(totalFramesRead <= frameCount); - if (totalFramesRead == frameCount) { - break; - } - - /* Getting here means there's more to process. */ - continue; - } - - /* Getting here means there isn't enough any data left to send to the client which means we need to wait for more. */ - if (WaitForSingleObject((HANDLE)pDevice->winmm.hEventCapture, INFINITE) != WAIT_OBJECT_0) { - result = MA_ERROR; - break; - } - - /* Something happened. If the next buffer has been marked as done we need to reset a bit of state. */ - if ((pWAVEHDR[pDevice->winmm.iNextHeaderCapture].dwFlags & MA_WHDR_DONE) != 0) { - pWAVEHDR[pDevice->winmm.iNextHeaderCapture].dwUser = 0; /* 0 = unlocked (make it available for reading). */ - pDevice->winmm.headerFramesConsumedCapture = 0; - } - - /* If the device has been stopped we need to break. */ - if (ma_device_get_state(pDevice) != ma_device_state_started) { - break; - } - } - - if (pFramesRead != NULL) { - *pFramesRead = totalFramesRead; - } - - return result; -} - -static ma_result ma_context_uninit__winmm(ma_context* pContext) -{ - MA_ASSERT(pContext != NULL); - MA_ASSERT(pContext->backend == ma_backend_winmm); - - ma_dlclose(ma_context_get_log(pContext), pContext->winmm.hWinMM); - return MA_SUCCESS; -} - -static ma_result ma_context_init__winmm(ma_context* pContext, const ma_context_config* pConfig, ma_backend_callbacks* pCallbacks) -{ - MA_ASSERT(pContext != NULL); - - (void)pConfig; - - pContext->winmm.hWinMM = ma_dlopen(ma_context_get_log(pContext), "winmm.dll"); - if (pContext->winmm.hWinMM == NULL) { - return MA_NO_BACKEND; - } - - pContext->winmm.waveOutGetNumDevs = ma_dlsym(ma_context_get_log(pContext), pContext->winmm.hWinMM, "waveOutGetNumDevs"); - pContext->winmm.waveOutGetDevCapsA = ma_dlsym(ma_context_get_log(pContext), pContext->winmm.hWinMM, "waveOutGetDevCapsA"); - pContext->winmm.waveOutOpen = ma_dlsym(ma_context_get_log(pContext), pContext->winmm.hWinMM, "waveOutOpen"); - pContext->winmm.waveOutClose = ma_dlsym(ma_context_get_log(pContext), pContext->winmm.hWinMM, "waveOutClose"); - pContext->winmm.waveOutPrepareHeader = ma_dlsym(ma_context_get_log(pContext), pContext->winmm.hWinMM, "waveOutPrepareHeader"); - pContext->winmm.waveOutUnprepareHeader = ma_dlsym(ma_context_get_log(pContext), pContext->winmm.hWinMM, "waveOutUnprepareHeader"); - pContext->winmm.waveOutWrite = ma_dlsym(ma_context_get_log(pContext), pContext->winmm.hWinMM, "waveOutWrite"); - pContext->winmm.waveOutReset = ma_dlsym(ma_context_get_log(pContext), pContext->winmm.hWinMM, "waveOutReset"); - pContext->winmm.waveInGetNumDevs = ma_dlsym(ma_context_get_log(pContext), pContext->winmm.hWinMM, "waveInGetNumDevs"); - pContext->winmm.waveInGetDevCapsA = ma_dlsym(ma_context_get_log(pContext), pContext->winmm.hWinMM, "waveInGetDevCapsA"); - pContext->winmm.waveInOpen = ma_dlsym(ma_context_get_log(pContext), pContext->winmm.hWinMM, "waveInOpen"); - pContext->winmm.waveInClose = ma_dlsym(ma_context_get_log(pContext), pContext->winmm.hWinMM, "waveInClose"); - pContext->winmm.waveInPrepareHeader = ma_dlsym(ma_context_get_log(pContext), pContext->winmm.hWinMM, "waveInPrepareHeader"); - pContext->winmm.waveInUnprepareHeader = ma_dlsym(ma_context_get_log(pContext), pContext->winmm.hWinMM, "waveInUnprepareHeader"); - pContext->winmm.waveInAddBuffer = ma_dlsym(ma_context_get_log(pContext), pContext->winmm.hWinMM, "waveInAddBuffer"); - pContext->winmm.waveInStart = ma_dlsym(ma_context_get_log(pContext), pContext->winmm.hWinMM, "waveInStart"); - pContext->winmm.waveInReset = ma_dlsym(ma_context_get_log(pContext), pContext->winmm.hWinMM, "waveInReset"); - - pCallbacks->onContextInit = ma_context_init__winmm; - pCallbacks->onContextUninit = ma_context_uninit__winmm; - pCallbacks->onContextEnumerateDevices = ma_context_enumerate_devices__winmm; - pCallbacks->onContextGetDeviceInfo = ma_context_get_device_info__winmm; - pCallbacks->onDeviceInit = ma_device_init__winmm; - pCallbacks->onDeviceUninit = ma_device_uninit__winmm; - pCallbacks->onDeviceStart = ma_device_start__winmm; - pCallbacks->onDeviceStop = ma_device_stop__winmm; - pCallbacks->onDeviceRead = ma_device_read__winmm; - pCallbacks->onDeviceWrite = ma_device_write__winmm; - pCallbacks->onDeviceDataLoop = NULL; /* This is a blocking read-write API, so this can be NULL since miniaudio will manage the audio thread for us. */ - - return MA_SUCCESS; -} -#endif - - - - -/****************************************************************************** - -ALSA Backend - -******************************************************************************/ -#ifdef MA_HAS_ALSA - -#include /* poll(), struct pollfd */ -#include /* eventfd() */ - -#ifdef MA_NO_RUNTIME_LINKING - -/* asoundlib.h marks some functions with "inline" which isn't always supported. Need to emulate it. */ -#if !defined(__cplusplus) - #if defined(__STRICT_ANSI__) - #if !defined(inline) - #define inline __inline__ __attribute__((always_inline)) - #define MA_INLINE_DEFINED - #endif - #endif -#endif -#include -#if defined(MA_INLINE_DEFINED) - #undef inline - #undef MA_INLINE_DEFINED -#endif - -typedef snd_pcm_uframes_t ma_snd_pcm_uframes_t; -typedef snd_pcm_sframes_t ma_snd_pcm_sframes_t; -typedef snd_pcm_stream_t ma_snd_pcm_stream_t; -typedef snd_pcm_format_t ma_snd_pcm_format_t; -typedef snd_pcm_access_t ma_snd_pcm_access_t; -typedef snd_pcm_t ma_snd_pcm_t; -typedef snd_pcm_hw_params_t ma_snd_pcm_hw_params_t; -typedef snd_pcm_sw_params_t ma_snd_pcm_sw_params_t; -typedef snd_pcm_format_mask_t ma_snd_pcm_format_mask_t; -typedef snd_pcm_info_t ma_snd_pcm_info_t; -typedef snd_pcm_channel_area_t ma_snd_pcm_channel_area_t; -typedef snd_pcm_chmap_t ma_snd_pcm_chmap_t; -typedef snd_pcm_state_t ma_snd_pcm_state_t; - -/* snd_pcm_stream_t */ -#define MA_SND_PCM_STREAM_PLAYBACK SND_PCM_STREAM_PLAYBACK -#define MA_SND_PCM_STREAM_CAPTURE SND_PCM_STREAM_CAPTURE - -/* snd_pcm_format_t */ -#define MA_SND_PCM_FORMAT_UNKNOWN SND_PCM_FORMAT_UNKNOWN -#define MA_SND_PCM_FORMAT_U8 SND_PCM_FORMAT_U8 -#define MA_SND_PCM_FORMAT_S16_LE SND_PCM_FORMAT_S16_LE -#define MA_SND_PCM_FORMAT_S16_BE SND_PCM_FORMAT_S16_BE -#define MA_SND_PCM_FORMAT_S24_LE SND_PCM_FORMAT_S24_LE -#define MA_SND_PCM_FORMAT_S24_BE SND_PCM_FORMAT_S24_BE -#define MA_SND_PCM_FORMAT_S32_LE SND_PCM_FORMAT_S32_LE -#define MA_SND_PCM_FORMAT_S32_BE SND_PCM_FORMAT_S32_BE -#define MA_SND_PCM_FORMAT_FLOAT_LE SND_PCM_FORMAT_FLOAT_LE -#define MA_SND_PCM_FORMAT_FLOAT_BE SND_PCM_FORMAT_FLOAT_BE -#define MA_SND_PCM_FORMAT_FLOAT64_LE SND_PCM_FORMAT_FLOAT64_LE -#define MA_SND_PCM_FORMAT_FLOAT64_BE SND_PCM_FORMAT_FLOAT64_BE -#define MA_SND_PCM_FORMAT_MU_LAW SND_PCM_FORMAT_MU_LAW -#define MA_SND_PCM_FORMAT_A_LAW SND_PCM_FORMAT_A_LAW -#define MA_SND_PCM_FORMAT_S24_3LE SND_PCM_FORMAT_S24_3LE -#define MA_SND_PCM_FORMAT_S24_3BE SND_PCM_FORMAT_S24_3BE - -/* ma_snd_pcm_access_t */ -#define MA_SND_PCM_ACCESS_MMAP_INTERLEAVED SND_PCM_ACCESS_MMAP_INTERLEAVED -#define MA_SND_PCM_ACCESS_MMAP_NONINTERLEAVED SND_PCM_ACCESS_MMAP_NONINTERLEAVED -#define MA_SND_PCM_ACCESS_MMAP_COMPLEX SND_PCM_ACCESS_MMAP_COMPLEX -#define MA_SND_PCM_ACCESS_RW_INTERLEAVED SND_PCM_ACCESS_RW_INTERLEAVED -#define MA_SND_PCM_ACCESS_RW_NONINTERLEAVED SND_PCM_ACCESS_RW_NONINTERLEAVED - -/* Channel positions. */ -#define MA_SND_CHMAP_UNKNOWN SND_CHMAP_UNKNOWN -#define MA_SND_CHMAP_NA SND_CHMAP_NA -#define MA_SND_CHMAP_MONO SND_CHMAP_MONO -#define MA_SND_CHMAP_FL SND_CHMAP_FL -#define MA_SND_CHMAP_FR SND_CHMAP_FR -#define MA_SND_CHMAP_RL SND_CHMAP_RL -#define MA_SND_CHMAP_RR SND_CHMAP_RR -#define MA_SND_CHMAP_FC SND_CHMAP_FC -#define MA_SND_CHMAP_LFE SND_CHMAP_LFE -#define MA_SND_CHMAP_SL SND_CHMAP_SL -#define MA_SND_CHMAP_SR SND_CHMAP_SR -#define MA_SND_CHMAP_RC SND_CHMAP_RC -#define MA_SND_CHMAP_FLC SND_CHMAP_FLC -#define MA_SND_CHMAP_FRC SND_CHMAP_FRC -#define MA_SND_CHMAP_RLC SND_CHMAP_RLC -#define MA_SND_CHMAP_RRC SND_CHMAP_RRC -#define MA_SND_CHMAP_FLW SND_CHMAP_FLW -#define MA_SND_CHMAP_FRW SND_CHMAP_FRW -#define MA_SND_CHMAP_FLH SND_CHMAP_FLH -#define MA_SND_CHMAP_FCH SND_CHMAP_FCH -#define MA_SND_CHMAP_FRH SND_CHMAP_FRH -#define MA_SND_CHMAP_TC SND_CHMAP_TC -#define MA_SND_CHMAP_TFL SND_CHMAP_TFL -#define MA_SND_CHMAP_TFR SND_CHMAP_TFR -#define MA_SND_CHMAP_TFC SND_CHMAP_TFC -#define MA_SND_CHMAP_TRL SND_CHMAP_TRL -#define MA_SND_CHMAP_TRR SND_CHMAP_TRR -#define MA_SND_CHMAP_TRC SND_CHMAP_TRC -#define MA_SND_CHMAP_TFLC SND_CHMAP_TFLC -#define MA_SND_CHMAP_TFRC SND_CHMAP_TFRC -#define MA_SND_CHMAP_TSL SND_CHMAP_TSL -#define MA_SND_CHMAP_TSR SND_CHMAP_TSR -#define MA_SND_CHMAP_LLFE SND_CHMAP_LLFE -#define MA_SND_CHMAP_RLFE SND_CHMAP_RLFE -#define MA_SND_CHMAP_BC SND_CHMAP_BC -#define MA_SND_CHMAP_BLC SND_CHMAP_BLC -#define MA_SND_CHMAP_BRC SND_CHMAP_BRC - -/* Open mode flags. */ -#define MA_SND_PCM_NO_AUTO_RESAMPLE SND_PCM_NO_AUTO_RESAMPLE -#define MA_SND_PCM_NO_AUTO_CHANNELS SND_PCM_NO_AUTO_CHANNELS -#define MA_SND_PCM_NO_AUTO_FORMAT SND_PCM_NO_AUTO_FORMAT -#else -#include /* For EPIPE, etc. */ -typedef unsigned long ma_snd_pcm_uframes_t; -typedef long ma_snd_pcm_sframes_t; -typedef int ma_snd_pcm_stream_t; -typedef int ma_snd_pcm_format_t; -typedef int ma_snd_pcm_access_t; -typedef int ma_snd_pcm_state_t; -typedef struct ma_snd_pcm_t ma_snd_pcm_t; -typedef struct ma_snd_pcm_hw_params_t ma_snd_pcm_hw_params_t; -typedef struct ma_snd_pcm_sw_params_t ma_snd_pcm_sw_params_t; -typedef struct ma_snd_pcm_format_mask_t ma_snd_pcm_format_mask_t; -typedef struct ma_snd_pcm_info_t ma_snd_pcm_info_t; -typedef struct -{ - void* addr; - unsigned int first; - unsigned int step; -} ma_snd_pcm_channel_area_t; -typedef struct -{ - unsigned int channels; - unsigned int pos[1]; -} ma_snd_pcm_chmap_t; - -/* snd_pcm_state_t */ -#define MA_SND_PCM_STATE_OPEN 0 -#define MA_SND_PCM_STATE_SETUP 1 -#define MA_SND_PCM_STATE_PREPARED 2 -#define MA_SND_PCM_STATE_RUNNING 3 -#define MA_SND_PCM_STATE_XRUN 4 -#define MA_SND_PCM_STATE_DRAINING 5 -#define MA_SND_PCM_STATE_PAUSED 6 -#define MA_SND_PCM_STATE_SUSPENDED 7 -#define MA_SND_PCM_STATE_DISCONNECTED 8 - -/* snd_pcm_stream_t */ -#define MA_SND_PCM_STREAM_PLAYBACK 0 -#define MA_SND_PCM_STREAM_CAPTURE 1 - -/* snd_pcm_format_t */ -#define MA_SND_PCM_FORMAT_UNKNOWN -1 -#define MA_SND_PCM_FORMAT_U8 1 -#define MA_SND_PCM_FORMAT_S16_LE 2 -#define MA_SND_PCM_FORMAT_S16_BE 3 -#define MA_SND_PCM_FORMAT_S24_LE 6 -#define MA_SND_PCM_FORMAT_S24_BE 7 -#define MA_SND_PCM_FORMAT_S32_LE 10 -#define MA_SND_PCM_FORMAT_S32_BE 11 -#define MA_SND_PCM_FORMAT_FLOAT_LE 14 -#define MA_SND_PCM_FORMAT_FLOAT_BE 15 -#define MA_SND_PCM_FORMAT_FLOAT64_LE 16 -#define MA_SND_PCM_FORMAT_FLOAT64_BE 17 -#define MA_SND_PCM_FORMAT_MU_LAW 20 -#define MA_SND_PCM_FORMAT_A_LAW 21 -#define MA_SND_PCM_FORMAT_S24_3LE 32 -#define MA_SND_PCM_FORMAT_S24_3BE 33 - -/* snd_pcm_access_t */ -#define MA_SND_PCM_ACCESS_MMAP_INTERLEAVED 0 -#define MA_SND_PCM_ACCESS_MMAP_NONINTERLEAVED 1 -#define MA_SND_PCM_ACCESS_MMAP_COMPLEX 2 -#define MA_SND_PCM_ACCESS_RW_INTERLEAVED 3 -#define MA_SND_PCM_ACCESS_RW_NONINTERLEAVED 4 - -/* Channel positions. */ -#define MA_SND_CHMAP_UNKNOWN 0 -#define MA_SND_CHMAP_NA 1 -#define MA_SND_CHMAP_MONO 2 -#define MA_SND_CHMAP_FL 3 -#define MA_SND_CHMAP_FR 4 -#define MA_SND_CHMAP_RL 5 -#define MA_SND_CHMAP_RR 6 -#define MA_SND_CHMAP_FC 7 -#define MA_SND_CHMAP_LFE 8 -#define MA_SND_CHMAP_SL 9 -#define MA_SND_CHMAP_SR 10 -#define MA_SND_CHMAP_RC 11 -#define MA_SND_CHMAP_FLC 12 -#define MA_SND_CHMAP_FRC 13 -#define MA_SND_CHMAP_RLC 14 -#define MA_SND_CHMAP_RRC 15 -#define MA_SND_CHMAP_FLW 16 -#define MA_SND_CHMAP_FRW 17 -#define MA_SND_CHMAP_FLH 18 -#define MA_SND_CHMAP_FCH 19 -#define MA_SND_CHMAP_FRH 20 -#define MA_SND_CHMAP_TC 21 -#define MA_SND_CHMAP_TFL 22 -#define MA_SND_CHMAP_TFR 23 -#define MA_SND_CHMAP_TFC 24 -#define MA_SND_CHMAP_TRL 25 -#define MA_SND_CHMAP_TRR 26 -#define MA_SND_CHMAP_TRC 27 -#define MA_SND_CHMAP_TFLC 28 -#define MA_SND_CHMAP_TFRC 29 -#define MA_SND_CHMAP_TSL 30 -#define MA_SND_CHMAP_TSR 31 -#define MA_SND_CHMAP_LLFE 32 -#define MA_SND_CHMAP_RLFE 33 -#define MA_SND_CHMAP_BC 34 -#define MA_SND_CHMAP_BLC 35 -#define MA_SND_CHMAP_BRC 36 - -/* Open mode flags. */ -#define MA_SND_PCM_NO_AUTO_RESAMPLE 0x00010000 -#define MA_SND_PCM_NO_AUTO_CHANNELS 0x00020000 -#define MA_SND_PCM_NO_AUTO_FORMAT 0x00040000 -#endif - -typedef int (* ma_snd_pcm_open_proc) (ma_snd_pcm_t **pcm, const char *name, ma_snd_pcm_stream_t stream, int mode); -typedef int (* ma_snd_pcm_close_proc) (ma_snd_pcm_t *pcm); -typedef size_t (* ma_snd_pcm_hw_params_sizeof_proc) (void); -typedef int (* ma_snd_pcm_hw_params_any_proc) (ma_snd_pcm_t *pcm, ma_snd_pcm_hw_params_t *params); -typedef int (* ma_snd_pcm_hw_params_set_format_proc) (ma_snd_pcm_t *pcm, ma_snd_pcm_hw_params_t *params, ma_snd_pcm_format_t val); -typedef int (* ma_snd_pcm_hw_params_set_format_first_proc) (ma_snd_pcm_t *pcm, ma_snd_pcm_hw_params_t *params, ma_snd_pcm_format_t *format); -typedef void (* ma_snd_pcm_hw_params_get_format_mask_proc) (ma_snd_pcm_hw_params_t *params, ma_snd_pcm_format_mask_t *mask); -typedef int (* ma_snd_pcm_hw_params_set_channels_proc) (ma_snd_pcm_t *pcm, ma_snd_pcm_hw_params_t *params, unsigned int val); -typedef int (* ma_snd_pcm_hw_params_set_channels_near_proc) (ma_snd_pcm_t *pcm, ma_snd_pcm_hw_params_t *params, unsigned int *val); -typedef int (* ma_snd_pcm_hw_params_set_channels_minmax_proc) (ma_snd_pcm_t *pcm, ma_snd_pcm_hw_params_t *params, unsigned int *minimum, unsigned int *maximum); -typedef int (* ma_snd_pcm_hw_params_set_rate_resample_proc) (ma_snd_pcm_t *pcm, ma_snd_pcm_hw_params_t *params, unsigned int val); -typedef int (* ma_snd_pcm_hw_params_set_rate_proc) (ma_snd_pcm_t *pcm, ma_snd_pcm_hw_params_t *params, unsigned int val, int dir); -typedef int (* ma_snd_pcm_hw_params_set_rate_near_proc) (ma_snd_pcm_t *pcm, ma_snd_pcm_hw_params_t *params, unsigned int *val, int *dir); -typedef int (* ma_snd_pcm_hw_params_set_buffer_size_near_proc)(ma_snd_pcm_t *pcm, ma_snd_pcm_hw_params_t *params, ma_snd_pcm_uframes_t *val); -typedef int (* ma_snd_pcm_hw_params_set_periods_near_proc) (ma_snd_pcm_t *pcm, ma_snd_pcm_hw_params_t *params, unsigned int *val, int *dir); -typedef int (* ma_snd_pcm_hw_params_set_access_proc) (ma_snd_pcm_t *pcm, ma_snd_pcm_hw_params_t *params, ma_snd_pcm_access_t _access); -typedef int (* ma_snd_pcm_hw_params_get_format_proc) (const ma_snd_pcm_hw_params_t *params, ma_snd_pcm_format_t *format); -typedef int (* ma_snd_pcm_hw_params_get_channels_proc) (const ma_snd_pcm_hw_params_t *params, unsigned int *val); -typedef int (* ma_snd_pcm_hw_params_get_channels_min_proc) (const ma_snd_pcm_hw_params_t *params, unsigned int *val); -typedef int (* ma_snd_pcm_hw_params_get_channels_max_proc) (const ma_snd_pcm_hw_params_t *params, unsigned int *val); -typedef int (* ma_snd_pcm_hw_params_get_rate_proc) (const ma_snd_pcm_hw_params_t *params, unsigned int *rate, int *dir); -typedef int (* ma_snd_pcm_hw_params_get_rate_min_proc) (const ma_snd_pcm_hw_params_t *params, unsigned int *rate, int *dir); -typedef int (* ma_snd_pcm_hw_params_get_rate_max_proc) (const ma_snd_pcm_hw_params_t *params, unsigned int *rate, int *dir); -typedef int (* ma_snd_pcm_hw_params_get_buffer_size_proc) (const ma_snd_pcm_hw_params_t *params, ma_snd_pcm_uframes_t *val); -typedef int (* ma_snd_pcm_hw_params_get_periods_proc) (const ma_snd_pcm_hw_params_t *params, unsigned int *val, int *dir); -typedef int (* ma_snd_pcm_hw_params_get_access_proc) (const ma_snd_pcm_hw_params_t *params, ma_snd_pcm_access_t *_access); -typedef int (* ma_snd_pcm_hw_params_test_format_proc) (ma_snd_pcm_t *pcm, ma_snd_pcm_hw_params_t *params, ma_snd_pcm_format_t val); -typedef int (* ma_snd_pcm_hw_params_test_channels_proc) (ma_snd_pcm_t *pcm, ma_snd_pcm_hw_params_t *params, unsigned int val); -typedef int (* ma_snd_pcm_hw_params_test_rate_proc) (ma_snd_pcm_t *pcm, ma_snd_pcm_hw_params_t *params, unsigned int val, int dir); -typedef int (* ma_snd_pcm_hw_params_proc) (ma_snd_pcm_t *pcm, ma_snd_pcm_hw_params_t *params); -typedef size_t (* ma_snd_pcm_sw_params_sizeof_proc) (void); -typedef int (* ma_snd_pcm_sw_params_current_proc) (ma_snd_pcm_t *pcm, ma_snd_pcm_sw_params_t *params); -typedef int (* ma_snd_pcm_sw_params_get_boundary_proc) (const ma_snd_pcm_sw_params_t *params, ma_snd_pcm_uframes_t* val); -typedef int (* ma_snd_pcm_sw_params_set_avail_min_proc) (ma_snd_pcm_t *pcm, ma_snd_pcm_sw_params_t *params, ma_snd_pcm_uframes_t val); -typedef int (* ma_snd_pcm_sw_params_set_start_threshold_proc) (ma_snd_pcm_t *pcm, ma_snd_pcm_sw_params_t *params, ma_snd_pcm_uframes_t val); -typedef int (* ma_snd_pcm_sw_params_set_stop_threshold_proc) (ma_snd_pcm_t *pcm, ma_snd_pcm_sw_params_t *params, ma_snd_pcm_uframes_t val); -typedef int (* ma_snd_pcm_sw_params_proc) (ma_snd_pcm_t *pcm, ma_snd_pcm_sw_params_t *params); -typedef size_t (* ma_snd_pcm_format_mask_sizeof_proc) (void); -typedef int (* ma_snd_pcm_format_mask_test_proc) (const ma_snd_pcm_format_mask_t *mask, ma_snd_pcm_format_t val); -typedef ma_snd_pcm_chmap_t * (* ma_snd_pcm_get_chmap_proc) (ma_snd_pcm_t *pcm); -typedef ma_snd_pcm_state_t (* ma_snd_pcm_state_proc) (ma_snd_pcm_t *pcm); -typedef int (* ma_snd_pcm_prepare_proc) (ma_snd_pcm_t *pcm); -typedef int (* ma_snd_pcm_start_proc) (ma_snd_pcm_t *pcm); -typedef int (* ma_snd_pcm_drop_proc) (ma_snd_pcm_t *pcm); -typedef int (* ma_snd_pcm_drain_proc) (ma_snd_pcm_t *pcm); -typedef int (* ma_snd_pcm_reset_proc) (ma_snd_pcm_t *pcm); -typedef int (* ma_snd_device_name_hint_proc) (int card, const char *iface, void ***hints); -typedef char * (* ma_snd_device_name_get_hint_proc) (const void *hint, const char *id); -typedef int (* ma_snd_card_get_index_proc) (const char *name); -typedef int (* ma_snd_device_name_free_hint_proc) (void **hints); -typedef int (* ma_snd_pcm_mmap_begin_proc) (ma_snd_pcm_t *pcm, const ma_snd_pcm_channel_area_t **areas, ma_snd_pcm_uframes_t *offset, ma_snd_pcm_uframes_t *frames); -typedef ma_snd_pcm_sframes_t (* ma_snd_pcm_mmap_commit_proc) (ma_snd_pcm_t *pcm, ma_snd_pcm_uframes_t offset, ma_snd_pcm_uframes_t frames); -typedef int (* ma_snd_pcm_recover_proc) (ma_snd_pcm_t *pcm, int err, int silent); -typedef ma_snd_pcm_sframes_t (* ma_snd_pcm_readi_proc) (ma_snd_pcm_t *pcm, void *buffer, ma_snd_pcm_uframes_t size); -typedef ma_snd_pcm_sframes_t (* ma_snd_pcm_writei_proc) (ma_snd_pcm_t *pcm, const void *buffer, ma_snd_pcm_uframes_t size); -typedef ma_snd_pcm_sframes_t (* ma_snd_pcm_avail_proc) (ma_snd_pcm_t *pcm); -typedef ma_snd_pcm_sframes_t (* ma_snd_pcm_avail_update_proc) (ma_snd_pcm_t *pcm); -typedef int (* ma_snd_pcm_wait_proc) (ma_snd_pcm_t *pcm, int timeout); -typedef int (* ma_snd_pcm_nonblock_proc) (ma_snd_pcm_t *pcm, int nonblock); -typedef int (* ma_snd_pcm_info_proc) (ma_snd_pcm_t *pcm, ma_snd_pcm_info_t* info); -typedef size_t (* ma_snd_pcm_info_sizeof_proc) (void); -typedef const char* (* ma_snd_pcm_info_get_name_proc) (const ma_snd_pcm_info_t* info); -typedef int (* ma_snd_pcm_poll_descriptors_proc) (ma_snd_pcm_t *pcm, struct pollfd *pfds, unsigned int space); -typedef int (* ma_snd_pcm_poll_descriptors_count_proc) (ma_snd_pcm_t *pcm); -typedef int (* ma_snd_pcm_poll_descriptors_revents_proc) (ma_snd_pcm_t *pcm, struct pollfd *pfds, unsigned int nfds, unsigned short *revents); -typedef int (* ma_snd_config_update_free_global_proc) (void); - -/* This array specifies each of the common devices that can be used for both playback and capture. */ -static const char* g_maCommonDeviceNamesALSA[] = { - "default", - "null", - "pulse", - "jack" -}; - -/* This array allows us to blacklist specific playback devices. */ -static const char* g_maBlacklistedPlaybackDeviceNamesALSA[] = { - "" -}; - -/* This array allows us to blacklist specific capture devices. */ -static const char* g_maBlacklistedCaptureDeviceNamesALSA[] = { - "" -}; - - -static ma_snd_pcm_format_t ma_convert_ma_format_to_alsa_format(ma_format format) -{ - ma_snd_pcm_format_t ALSAFormats[] = { - MA_SND_PCM_FORMAT_UNKNOWN, /* ma_format_unknown */ - MA_SND_PCM_FORMAT_U8, /* ma_format_u8 */ - MA_SND_PCM_FORMAT_S16_LE, /* ma_format_s16 */ - MA_SND_PCM_FORMAT_S24_3LE, /* ma_format_s24 */ - MA_SND_PCM_FORMAT_S32_LE, /* ma_format_s32 */ - MA_SND_PCM_FORMAT_FLOAT_LE /* ma_format_f32 */ - }; - - if (ma_is_big_endian()) { - ALSAFormats[0] = MA_SND_PCM_FORMAT_UNKNOWN; - ALSAFormats[1] = MA_SND_PCM_FORMAT_U8; - ALSAFormats[2] = MA_SND_PCM_FORMAT_S16_BE; - ALSAFormats[3] = MA_SND_PCM_FORMAT_S24_3BE; - ALSAFormats[4] = MA_SND_PCM_FORMAT_S32_BE; - ALSAFormats[5] = MA_SND_PCM_FORMAT_FLOAT_BE; - } - - return ALSAFormats[format]; -} - -static ma_format ma_format_from_alsa(ma_snd_pcm_format_t formatALSA) -{ - if (ma_is_little_endian()) { - switch (formatALSA) { - case MA_SND_PCM_FORMAT_S16_LE: return ma_format_s16; - case MA_SND_PCM_FORMAT_S24_3LE: return ma_format_s24; - case MA_SND_PCM_FORMAT_S32_LE: return ma_format_s32; - case MA_SND_PCM_FORMAT_FLOAT_LE: return ma_format_f32; - default: break; - } - } else { - switch (formatALSA) { - case MA_SND_PCM_FORMAT_S16_BE: return ma_format_s16; - case MA_SND_PCM_FORMAT_S24_3BE: return ma_format_s24; - case MA_SND_PCM_FORMAT_S32_BE: return ma_format_s32; - case MA_SND_PCM_FORMAT_FLOAT_BE: return ma_format_f32; - default: break; - } - } - - /* Endian agnostic. */ - switch (formatALSA) { - case MA_SND_PCM_FORMAT_U8: return ma_format_u8; - default: return ma_format_unknown; - } -} - -static ma_channel ma_convert_alsa_channel_position_to_ma_channel(unsigned int alsaChannelPos) -{ - switch (alsaChannelPos) - { - case MA_SND_CHMAP_MONO: return MA_CHANNEL_MONO; - case MA_SND_CHMAP_FL: return MA_CHANNEL_FRONT_LEFT; - case MA_SND_CHMAP_FR: return MA_CHANNEL_FRONT_RIGHT; - case MA_SND_CHMAP_RL: return MA_CHANNEL_BACK_LEFT; - case MA_SND_CHMAP_RR: return MA_CHANNEL_BACK_RIGHT; - case MA_SND_CHMAP_FC: return MA_CHANNEL_FRONT_CENTER; - case MA_SND_CHMAP_LFE: return MA_CHANNEL_LFE; - case MA_SND_CHMAP_SL: return MA_CHANNEL_SIDE_LEFT; - case MA_SND_CHMAP_SR: return MA_CHANNEL_SIDE_RIGHT; - case MA_SND_CHMAP_RC: return MA_CHANNEL_BACK_CENTER; - case MA_SND_CHMAP_FLC: return MA_CHANNEL_FRONT_LEFT_CENTER; - case MA_SND_CHMAP_FRC: return MA_CHANNEL_FRONT_RIGHT_CENTER; - case MA_SND_CHMAP_RLC: return 0; - case MA_SND_CHMAP_RRC: return 0; - case MA_SND_CHMAP_FLW: return 0; - case MA_SND_CHMAP_FRW: return 0; - case MA_SND_CHMAP_FLH: return 0; - case MA_SND_CHMAP_FCH: return 0; - case MA_SND_CHMAP_FRH: return 0; - case MA_SND_CHMAP_TC: return MA_CHANNEL_TOP_CENTER; - case MA_SND_CHMAP_TFL: return MA_CHANNEL_TOP_FRONT_LEFT; - case MA_SND_CHMAP_TFR: return MA_CHANNEL_TOP_FRONT_RIGHT; - case MA_SND_CHMAP_TFC: return MA_CHANNEL_TOP_FRONT_CENTER; - case MA_SND_CHMAP_TRL: return MA_CHANNEL_TOP_BACK_LEFT; - case MA_SND_CHMAP_TRR: return MA_CHANNEL_TOP_BACK_RIGHT; - case MA_SND_CHMAP_TRC: return MA_CHANNEL_TOP_BACK_CENTER; - default: break; - } - - return 0; -} - -static ma_bool32 ma_is_common_device_name__alsa(const char* name) -{ - size_t iName; - for (iName = 0; iName < ma_countof(g_maCommonDeviceNamesALSA); ++iName) { - if (ma_strcmp(name, g_maCommonDeviceNamesALSA[iName]) == 0) { - return MA_TRUE; - } - } - - return MA_FALSE; -} - - -static ma_bool32 ma_is_playback_device_blacklisted__alsa(const char* name) -{ - size_t iName; - for (iName = 0; iName < ma_countof(g_maBlacklistedPlaybackDeviceNamesALSA); ++iName) { - if (ma_strcmp(name, g_maBlacklistedPlaybackDeviceNamesALSA[iName]) == 0) { - return MA_TRUE; - } - } - - return MA_FALSE; -} - -static ma_bool32 ma_is_capture_device_blacklisted__alsa(const char* name) -{ - size_t iName; - for (iName = 0; iName < ma_countof(g_maBlacklistedCaptureDeviceNamesALSA); ++iName) { - if (ma_strcmp(name, g_maBlacklistedCaptureDeviceNamesALSA[iName]) == 0) { - return MA_TRUE; - } - } - - return MA_FALSE; -} - -static ma_bool32 ma_is_device_blacklisted__alsa(ma_device_type deviceType, const char* name) -{ - if (deviceType == ma_device_type_playback) { - return ma_is_playback_device_blacklisted__alsa(name); - } else { - return ma_is_capture_device_blacklisted__alsa(name); - } -} - - -static const char* ma_find_char(const char* str, char c, int* index) -{ - int i = 0; - for (;;) { - if (str[i] == '\0') { - if (index) *index = -1; - return NULL; - } - - if (str[i] == c) { - if (index) *index = i; - return str + i; - } - - i += 1; - } - - /* Should never get here, but treat it as though the character was not found to make me feel better inside. */ - if (index) *index = -1; - return NULL; -} - -static ma_bool32 ma_is_device_name_in_hw_format__alsa(const char* hwid) -{ - /* This function is just checking whether or not hwid is in "hw:%d,%d" format. */ - - int commaPos; - const char* dev; - int i; - - if (hwid == NULL) { - return MA_FALSE; - } - - if (hwid[0] != 'h' || hwid[1] != 'w' || hwid[2] != ':') { - return MA_FALSE; - } - - hwid += 3; - - dev = ma_find_char(hwid, ',', &commaPos); - if (dev == NULL) { - return MA_FALSE; - } else { - dev += 1; /* Skip past the ",". */ - } - - /* Check if the part between the ":" and the "," contains only numbers. If not, return false. */ - for (i = 0; i < commaPos; ++i) { - if (hwid[i] < '0' || hwid[i] > '9') { - return MA_FALSE; - } - } - - /* Check if everything after the "," is numeric. If not, return false. */ - i = 0; - while (dev[i] != '\0') { - if (dev[i] < '0' || dev[i] > '9') { - return MA_FALSE; - } - i += 1; - } - - return MA_TRUE; -} - -static int ma_convert_device_name_to_hw_format__alsa(ma_context* pContext, char* dst, size_t dstSize, const char* src) /* Returns 0 on success, non-0 on error. */ -{ - /* src should look something like this: "hw:CARD=I82801AAICH,DEV=0" */ - - int colonPos; - int commaPos; - char card[256]; - const char* dev; - int cardIndex; - - if (dst == NULL) { - return -1; - } - if (dstSize < 7) { - return -1; /* Absolute minimum size of the output buffer is 7 bytes. */ - } - - *dst = '\0'; /* Safety. */ - if (src == NULL) { - return -1; - } - - /* If the input name is already in "hw:%d,%d" format, just return that verbatim. */ - if (ma_is_device_name_in_hw_format__alsa(src)) { - return ma_strcpy_s(dst, dstSize, src); - } - - src = ma_find_char(src, ':', &colonPos); - if (src == NULL) { - return -1; /* Couldn't find a colon */ - } - - dev = ma_find_char(src, ',', &commaPos); - if (dev == NULL) { - dev = "0"; - ma_strncpy_s(card, sizeof(card), src+6, (size_t)-1); /* +6 = ":CARD=" */ - } else { - dev = dev + 5; /* +5 = ",DEV=" */ - ma_strncpy_s(card, sizeof(card), src+6, commaPos-6); /* +6 = ":CARD=" */ - } - - cardIndex = ((ma_snd_card_get_index_proc)pContext->alsa.snd_card_get_index)(card); - if (cardIndex < 0) { - return -2; /* Failed to retrieve the card index. */ - } - - - /* Construction. */ - dst[0] = 'h'; dst[1] = 'w'; dst[2] = ':'; - if (ma_itoa_s(cardIndex, dst+3, dstSize-3, 10) != 0) { - return -3; - } - if (ma_strcat_s(dst, dstSize, ",") != 0) { - return -3; - } - if (ma_strcat_s(dst, dstSize, dev) != 0) { - return -3; - } - - return 0; -} - -static ma_bool32 ma_does_id_exist_in_list__alsa(ma_device_id* pUniqueIDs, ma_uint32 count, const char* pHWID) -{ - ma_uint32 i; - - MA_ASSERT(pHWID != NULL); - - for (i = 0; i < count; ++i) { - if (ma_strcmp(pUniqueIDs[i].alsa, pHWID) == 0) { - return MA_TRUE; - } - } - - return MA_FALSE; -} - - -static ma_result ma_context_open_pcm__alsa(ma_context* pContext, ma_share_mode shareMode, ma_device_type deviceType, const ma_device_id* pDeviceID, int openMode, ma_snd_pcm_t** ppPCM) -{ - ma_snd_pcm_t* pPCM; - ma_snd_pcm_stream_t stream; - - MA_ASSERT(pContext != NULL); - MA_ASSERT(ppPCM != NULL); - - *ppPCM = NULL; - pPCM = NULL; - - stream = (deviceType == ma_device_type_playback) ? MA_SND_PCM_STREAM_PLAYBACK : MA_SND_PCM_STREAM_CAPTURE; - - if (pDeviceID == NULL) { - ma_bool32 isDeviceOpen; - size_t i; - - /* - We're opening the default device. I don't know if trying anything other than "default" is necessary, but it makes - me feel better to try as hard as we can get to get _something_ working. - */ - const char* defaultDeviceNames[] = { - "default", - NULL, - NULL, - NULL, - NULL, - NULL, - NULL - }; - - if (shareMode == ma_share_mode_exclusive) { - defaultDeviceNames[1] = "hw"; - defaultDeviceNames[2] = "hw:0"; - defaultDeviceNames[3] = "hw:0,0"; - } else { - if (deviceType == ma_device_type_playback) { - defaultDeviceNames[1] = "dmix"; - defaultDeviceNames[2] = "dmix:0"; - defaultDeviceNames[3] = "dmix:0,0"; - } else { - defaultDeviceNames[1] = "dsnoop"; - defaultDeviceNames[2] = "dsnoop:0"; - defaultDeviceNames[3] = "dsnoop:0,0"; - } - defaultDeviceNames[4] = "hw"; - defaultDeviceNames[5] = "hw:0"; - defaultDeviceNames[6] = "hw:0,0"; - } - - isDeviceOpen = MA_FALSE; - for (i = 0; i < ma_countof(defaultDeviceNames); ++i) { - if (defaultDeviceNames[i] != NULL && defaultDeviceNames[i][0] != '\0') { - if (((ma_snd_pcm_open_proc)pContext->alsa.snd_pcm_open)(&pPCM, defaultDeviceNames[i], stream, openMode) == 0) { - isDeviceOpen = MA_TRUE; - break; - } - } - } - - if (!isDeviceOpen) { - ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[ALSA] snd_pcm_open() failed when trying to open an appropriate default device."); - return MA_FAILED_TO_OPEN_BACKEND_DEVICE; - } - } else { - /* - We're trying to open a specific device. There's a few things to consider here: - - miniaudio recognizes a special format of device id that excludes the "hw", "dmix", etc. prefix. It looks like this: ":0,0", ":0,1", etc. When - an ID of this format is specified, it indicates to miniaudio that it can try different combinations of plugins ("hw", "dmix", etc.) until it - finds an appropriate one that works. This comes in very handy when trying to open a device in shared mode ("dmix"), vs exclusive mode ("hw"). - */ - - /* May end up needing to make small adjustments to the ID, so make a copy. */ - ma_device_id deviceID = *pDeviceID; - int resultALSA = -ENODEV; - - if (deviceID.alsa[0] != ':') { - /* The ID is not in ":0,0" format. Use the ID exactly as-is. */ - resultALSA = ((ma_snd_pcm_open_proc)pContext->alsa.snd_pcm_open)(&pPCM, deviceID.alsa, stream, openMode); - } else { - char hwid[256]; - - /* The ID is in ":0,0" format. Try different plugins depending on the shared mode. */ - if (deviceID.alsa[1] == '\0') { - deviceID.alsa[0] = '\0'; /* An ID of ":" should be converted to "". */ - } - - if (shareMode == ma_share_mode_shared) { - if (deviceType == ma_device_type_playback) { - ma_strcpy_s(hwid, sizeof(hwid), "dmix"); - } else { - ma_strcpy_s(hwid, sizeof(hwid), "dsnoop"); - } - - if (ma_strcat_s(hwid, sizeof(hwid), deviceID.alsa) == 0) { - resultALSA = ((ma_snd_pcm_open_proc)pContext->alsa.snd_pcm_open)(&pPCM, hwid, stream, openMode); - } - } - - /* If at this point we still don't have an open device it means we're either preferencing exclusive mode or opening with "dmix"/"dsnoop" failed. */ - if (resultALSA != 0) { - ma_strcpy_s(hwid, sizeof(hwid), "hw"); - if (ma_strcat_s(hwid, sizeof(hwid), deviceID.alsa) == 0) { - resultALSA = ((ma_snd_pcm_open_proc)pContext->alsa.snd_pcm_open)(&pPCM, hwid, stream, openMode); - } - } - } - - if (resultALSA < 0) { - ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[ALSA] snd_pcm_open() failed."); - return ma_result_from_errno(-resultALSA); - } - } - - *ppPCM = pPCM; - return MA_SUCCESS; -} - - -static ma_result ma_context_enumerate_devices__alsa(ma_context* pContext, ma_enum_devices_callback_proc callback, void* pUserData) -{ - int resultALSA; - ma_bool32 cbResult = MA_TRUE; - char** ppDeviceHints; - ma_device_id* pUniqueIDs = NULL; - ma_uint32 uniqueIDCount = 0; - char** ppNextDeviceHint; - - MA_ASSERT(pContext != NULL); - MA_ASSERT(callback != NULL); - - ma_mutex_lock(&pContext->alsa.internalDeviceEnumLock); - - resultALSA = ((ma_snd_device_name_hint_proc)pContext->alsa.snd_device_name_hint)(-1, "pcm", (void***)&ppDeviceHints); - if (resultALSA < 0) { - ma_mutex_unlock(&pContext->alsa.internalDeviceEnumLock); - return ma_result_from_errno(-resultALSA); - } - - ppNextDeviceHint = ppDeviceHints; - while (*ppNextDeviceHint != NULL) { - char* NAME = ((ma_snd_device_name_get_hint_proc)pContext->alsa.snd_device_name_get_hint)(*ppNextDeviceHint, "NAME"); - char* DESC = ((ma_snd_device_name_get_hint_proc)pContext->alsa.snd_device_name_get_hint)(*ppNextDeviceHint, "DESC"); - char* IOID = ((ma_snd_device_name_get_hint_proc)pContext->alsa.snd_device_name_get_hint)(*ppNextDeviceHint, "IOID"); - ma_device_type deviceType = ma_device_type_playback; - ma_bool32 stopEnumeration = MA_FALSE; - char hwid[sizeof(pUniqueIDs->alsa)]; - ma_device_info deviceInfo; - - if ((IOID == NULL || ma_strcmp(IOID, "Output") == 0)) { - deviceType = ma_device_type_playback; - } - if ((IOID != NULL && ma_strcmp(IOID, "Input" ) == 0)) { - deviceType = ma_device_type_capture; - } - - if (NAME != NULL) { - if (pContext->alsa.useVerboseDeviceEnumeration) { - /* Verbose mode. Use the name exactly as-is. */ - ma_strncpy_s(hwid, sizeof(hwid), NAME, (size_t)-1); - } else { - /* Simplified mode. Use ":%d,%d" format. */ - if (ma_convert_device_name_to_hw_format__alsa(pContext, hwid, sizeof(hwid), NAME) == 0) { - /* - At this point, hwid looks like "hw:0,0". In simplified enumeration mode, we actually want to strip off the - plugin name so it looks like ":0,0". The reason for this is that this special format is detected at device - initialization time and is used as an indicator to try to use the most appropriate plugin depending on the - device type and sharing mode. - */ - char* dst = hwid; - char* src = hwid+2; - while ((*dst++ = *src++)); - } else { - /* Conversion to "hw:%d,%d" failed. Just use the name as-is. */ - ma_strncpy_s(hwid, sizeof(hwid), NAME, (size_t)-1); - } - - if (ma_does_id_exist_in_list__alsa(pUniqueIDs, uniqueIDCount, hwid)) { - goto next_device; /* The device has already been enumerated. Move on to the next one. */ - } else { - /* The device has not yet been enumerated. Make sure it's added to our list so that it's not enumerated again. */ - size_t newCapacity = sizeof(*pUniqueIDs) * (uniqueIDCount + 1); - ma_device_id* pNewUniqueIDs = (ma_device_id*)ma_realloc(pUniqueIDs, newCapacity, &pContext->allocationCallbacks); - if (pNewUniqueIDs == NULL) { - goto next_device; /* Failed to allocate memory. */ - } - - pUniqueIDs = pNewUniqueIDs; - MA_COPY_MEMORY(pUniqueIDs[uniqueIDCount].alsa, hwid, sizeof(hwid)); - uniqueIDCount += 1; - } - } - } else { - MA_ZERO_MEMORY(hwid, sizeof(hwid)); - } - - MA_ZERO_OBJECT(&deviceInfo); - ma_strncpy_s(deviceInfo.id.alsa, sizeof(deviceInfo.id.alsa), hwid, (size_t)-1); - - /* - There's no good way to determine whether or not a device is the default on Linux. We're just going to do something simple and - just use the name of "default" as the indicator. - */ - if (ma_strcmp(deviceInfo.id.alsa, "default") == 0) { - deviceInfo.isDefault = MA_TRUE; - } - - - /* - DESC is the friendly name. We treat this slightly differently depending on whether or not we are using verbose - device enumeration. In verbose mode we want to take the entire description so that the end-user can distinguish - between the subdevices of each card/dev pair. In simplified mode, however, we only want the first part of the - description. - - The value in DESC seems to be split into two lines, with the first line being the name of the device and the - second line being a description of the device. I don't like having the description be across two lines because - it makes formatting ugly and annoying. I'm therefore deciding to put it all on a single line with the second line - being put into parentheses. In simplified mode I'm just stripping the second line entirely. - */ - if (DESC != NULL) { - int lfPos; - const char* line2 = ma_find_char(DESC, '\n', &lfPos); - if (line2 != NULL) { - line2 += 1; /* Skip past the new-line character. */ - - if (pContext->alsa.useVerboseDeviceEnumeration) { - /* Verbose mode. Put the second line in brackets. */ - ma_strncpy_s(deviceInfo.name, sizeof(deviceInfo.name), DESC, lfPos); - ma_strcat_s (deviceInfo.name, sizeof(deviceInfo.name), " ("); - ma_strcat_s (deviceInfo.name, sizeof(deviceInfo.name), line2); - ma_strcat_s (deviceInfo.name, sizeof(deviceInfo.name), ")"); - } else { - /* Simplified mode. Strip the second line entirely. */ - ma_strncpy_s(deviceInfo.name, sizeof(deviceInfo.name), DESC, lfPos); - } - } else { - /* There's no second line. Just copy the whole description. */ - ma_strncpy_s(deviceInfo.name, sizeof(deviceInfo.name), DESC, (size_t)-1); - } - } - - if (!ma_is_device_blacklisted__alsa(deviceType, NAME)) { - cbResult = callback(pContext, deviceType, &deviceInfo, pUserData); - } - - /* - Some devices are both playback and capture, but they are only enumerated by ALSA once. We need to fire the callback - again for the other device type in this case. We do this for known devices and where the IOID hint is NULL, which - means both Input and Output. - */ - if (cbResult) { - if (ma_is_common_device_name__alsa(NAME) || IOID == NULL) { - if (deviceType == ma_device_type_playback) { - if (!ma_is_capture_device_blacklisted__alsa(NAME)) { - cbResult = callback(pContext, ma_device_type_capture, &deviceInfo, pUserData); - } - } else { - if (!ma_is_playback_device_blacklisted__alsa(NAME)) { - cbResult = callback(pContext, ma_device_type_playback, &deviceInfo, pUserData); - } - } - } - } - - if (cbResult == MA_FALSE) { - stopEnumeration = MA_TRUE; - } - - next_device: - free(NAME); - free(DESC); - free(IOID); - ppNextDeviceHint += 1; - - /* We need to stop enumeration if the callback returned false. */ - if (stopEnumeration) { - break; - } - } - - ma_free(pUniqueIDs, &pContext->allocationCallbacks); - ((ma_snd_device_name_free_hint_proc)pContext->alsa.snd_device_name_free_hint)((void**)ppDeviceHints); - - ma_mutex_unlock(&pContext->alsa.internalDeviceEnumLock); - - return MA_SUCCESS; -} - - -typedef struct -{ - ma_device_type deviceType; - const ma_device_id* pDeviceID; - ma_share_mode shareMode; - ma_device_info* pDeviceInfo; - ma_bool32 foundDevice; -} ma_context_get_device_info_enum_callback_data__alsa; - -static ma_bool32 ma_context_get_device_info_enum_callback__alsa(ma_context* pContext, ma_device_type deviceType, const ma_device_info* pDeviceInfo, void* pUserData) -{ - ma_context_get_device_info_enum_callback_data__alsa* pData = (ma_context_get_device_info_enum_callback_data__alsa*)pUserData; - MA_ASSERT(pData != NULL); - - (void)pContext; - - if (pData->pDeviceID == NULL && ma_strcmp(pDeviceInfo->id.alsa, "default") == 0) { - ma_strncpy_s(pData->pDeviceInfo->name, sizeof(pData->pDeviceInfo->name), pDeviceInfo->name, (size_t)-1); - pData->foundDevice = MA_TRUE; - } else { - if (pData->deviceType == deviceType && (pData->pDeviceID != NULL && ma_strcmp(pData->pDeviceID->alsa, pDeviceInfo->id.alsa) == 0)) { - ma_strncpy_s(pData->pDeviceInfo->name, sizeof(pData->pDeviceInfo->name), pDeviceInfo->name, (size_t)-1); - pData->foundDevice = MA_TRUE; - } - } - - /* Keep enumerating until we have found the device. */ - return !pData->foundDevice; -} - -static void ma_context_test_rate_and_add_native_data_format__alsa(ma_context* pContext, ma_snd_pcm_t* pPCM, ma_snd_pcm_hw_params_t* pHWParams, ma_format format, ma_uint32 channels, ma_uint32 sampleRate, ma_uint32 flags, ma_device_info* pDeviceInfo) -{ - MA_ASSERT(pPCM != NULL); - MA_ASSERT(pHWParams != NULL); - MA_ASSERT(pDeviceInfo != NULL); - - if (pDeviceInfo->nativeDataFormatCount < ma_countof(pDeviceInfo->nativeDataFormats) && ((ma_snd_pcm_hw_params_test_rate_proc)pContext->alsa.snd_pcm_hw_params_test_rate)(pPCM, pHWParams, sampleRate, 0) == 0) { - pDeviceInfo->nativeDataFormats[pDeviceInfo->nativeDataFormatCount].format = format; - pDeviceInfo->nativeDataFormats[pDeviceInfo->nativeDataFormatCount].channels = channels; - pDeviceInfo->nativeDataFormats[pDeviceInfo->nativeDataFormatCount].sampleRate = sampleRate; - pDeviceInfo->nativeDataFormats[pDeviceInfo->nativeDataFormatCount].flags = flags; - pDeviceInfo->nativeDataFormatCount += 1; - } -} - -static void ma_context_iterate_rates_and_add_native_data_format__alsa(ma_context* pContext, ma_snd_pcm_t* pPCM, ma_snd_pcm_hw_params_t* pHWParams, ma_format format, ma_uint32 channels, ma_uint32 flags, ma_device_info* pDeviceInfo) -{ - ma_uint32 iSampleRate; - unsigned int minSampleRate; - unsigned int maxSampleRate; - int sampleRateDir; /* Not used. Just passed into snd_pcm_hw_params_get_rate_min/max(). */ - - /* There could be a range. */ - ((ma_snd_pcm_hw_params_get_rate_min_proc)pContext->alsa.snd_pcm_hw_params_get_rate_min)(pHWParams, &minSampleRate, &sampleRateDir); - ((ma_snd_pcm_hw_params_get_rate_max_proc)pContext->alsa.snd_pcm_hw_params_get_rate_max)(pHWParams, &maxSampleRate, &sampleRateDir); - - /* Make sure our sample rates are clamped to sane values. Stupid devices like "pulse" will reports rates like "1" which is ridiculous. */ - minSampleRate = ma_clamp(minSampleRate, (unsigned int)ma_standard_sample_rate_min, (unsigned int)ma_standard_sample_rate_max); - maxSampleRate = ma_clamp(maxSampleRate, (unsigned int)ma_standard_sample_rate_min, (unsigned int)ma_standard_sample_rate_max); - - for (iSampleRate = 0; iSampleRate < ma_countof(g_maStandardSampleRatePriorities); iSampleRate += 1) { - ma_uint32 standardSampleRate = g_maStandardSampleRatePriorities[iSampleRate]; - - if (standardSampleRate >= minSampleRate && standardSampleRate <= maxSampleRate) { - ma_context_test_rate_and_add_native_data_format__alsa(pContext, pPCM, pHWParams, format, channels, standardSampleRate, flags, pDeviceInfo); - } - } - - /* Now make sure our min and max rates are included just in case they aren't in the range of our standard rates. */ - if (!ma_is_standard_sample_rate(minSampleRate)) { - ma_context_test_rate_and_add_native_data_format__alsa(pContext, pPCM, pHWParams, format, channels, minSampleRate, flags, pDeviceInfo); - } - - if (!ma_is_standard_sample_rate(maxSampleRate) && maxSampleRate != minSampleRate) { - ma_context_test_rate_and_add_native_data_format__alsa(pContext, pPCM, pHWParams, format, channels, maxSampleRate, flags, pDeviceInfo); - } -} - -static ma_result ma_context_get_device_info__alsa(ma_context* pContext, ma_device_type deviceType, const ma_device_id* pDeviceID, ma_device_info* pDeviceInfo) -{ - ma_context_get_device_info_enum_callback_data__alsa data; - ma_result result; - int resultALSA; - ma_snd_pcm_t* pPCM; - ma_snd_pcm_hw_params_t* pHWParams; - ma_uint32 iFormat; - ma_uint32 iChannel; - - MA_ASSERT(pContext != NULL); - - /* We just enumerate to find basic information about the device. */ - data.deviceType = deviceType; - data.pDeviceID = pDeviceID; - data.pDeviceInfo = pDeviceInfo; - data.foundDevice = MA_FALSE; - result = ma_context_enumerate_devices__alsa(pContext, ma_context_get_device_info_enum_callback__alsa, &data); - if (result != MA_SUCCESS) { - return result; - } - - if (!data.foundDevice) { - return MA_NO_DEVICE; - } - - if (ma_strcmp(pDeviceInfo->id.alsa, "default") == 0) { - pDeviceInfo->isDefault = MA_TRUE; - } - - /* For detailed info we need to open the device. */ - result = ma_context_open_pcm__alsa(pContext, ma_share_mode_shared, deviceType, pDeviceID, 0, &pPCM); - if (result != MA_SUCCESS) { - return result; - } - - /* We need to initialize a HW parameters object in order to know what formats are supported. */ - pHWParams = (ma_snd_pcm_hw_params_t*)ma_calloc(((ma_snd_pcm_hw_params_sizeof_proc)pContext->alsa.snd_pcm_hw_params_sizeof)(), &pContext->allocationCallbacks); - if (pHWParams == NULL) { - ((ma_snd_pcm_close_proc)pContext->alsa.snd_pcm_close)(pPCM); - return MA_OUT_OF_MEMORY; - } - - resultALSA = ((ma_snd_pcm_hw_params_any_proc)pContext->alsa.snd_pcm_hw_params_any)(pPCM, pHWParams); - if (resultALSA < 0) { - ma_free(pHWParams, &pContext->allocationCallbacks); - ((ma_snd_pcm_close_proc)pContext->alsa.snd_pcm_close)(pPCM); - ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[ALSA] Failed to initialize hardware parameters. snd_pcm_hw_params_any() failed."); - return ma_result_from_errno(-resultALSA); - } - - /* - Some ALSA devices can support many permutations of formats, channels and rates. We only support - a fixed number of permutations which means we need to employ some strategies to ensure the best - combinations are returned. An example is the "pulse" device which can do its own data conversion - in software and as a result can support any combination of format, channels and rate. - - We want to ensure that the first data formats are the best. We have a list of favored sample - formats and sample rates, so these will be the basis of our iteration. - */ - - /* Formats. We just iterate over our standard formats and test them, making sure we reset the configuration space each iteration. */ - for (iFormat = 0; iFormat < ma_countof(g_maFormatPriorities); iFormat += 1) { - ma_format format = g_maFormatPriorities[iFormat]; - - /* - For each format we need to make sure we reset the configuration space so we don't return - channel counts and rates that aren't compatible with a format. - */ - ((ma_snd_pcm_hw_params_any_proc)pContext->alsa.snd_pcm_hw_params_any)(pPCM, pHWParams); - - /* Test the format first. If this fails it means the format is not supported and we can skip it. */ - if (((ma_snd_pcm_hw_params_test_format_proc)pContext->alsa.snd_pcm_hw_params_test_format)(pPCM, pHWParams, ma_convert_ma_format_to_alsa_format(format)) == 0) { - /* The format is supported. */ - unsigned int minChannels; - unsigned int maxChannels; - - /* - The configuration space needs to be restricted to this format so we can get an accurate - picture of which sample rates and channel counts are support with this format. - */ - ((ma_snd_pcm_hw_params_set_format_proc)pContext->alsa.snd_pcm_hw_params_set_format)(pPCM, pHWParams, ma_convert_ma_format_to_alsa_format(format)); - - /* Now we need to check for supported channels. */ - ((ma_snd_pcm_hw_params_get_channels_min_proc)pContext->alsa.snd_pcm_hw_params_get_channels_min)(pHWParams, &minChannels); - ((ma_snd_pcm_hw_params_get_channels_max_proc)pContext->alsa.snd_pcm_hw_params_get_channels_max)(pHWParams, &maxChannels); - - if (minChannels > MA_MAX_CHANNELS) { - continue; /* Too many channels. */ - } - if (maxChannels < MA_MIN_CHANNELS) { - continue; /* Not enough channels. */ - } - - /* - Make sure the channel count is clamped. This is mainly intended for the max channels - because some devices can report an unbound maximum. - */ - minChannels = ma_clamp(minChannels, MA_MIN_CHANNELS, MA_MAX_CHANNELS); - maxChannels = ma_clamp(maxChannels, MA_MIN_CHANNELS, MA_MAX_CHANNELS); - - if (minChannels == MA_MIN_CHANNELS && maxChannels == MA_MAX_CHANNELS) { - /* The device supports all channels. Don't iterate over every single one. Instead just set the channels to 0 which means all channels are supported. */ - ma_context_iterate_rates_and_add_native_data_format__alsa(pContext, pPCM, pHWParams, format, 0, 0, pDeviceInfo); /* Intentionally setting the channel count to 0 as that means all channels are supported. */ - } else { - /* The device only supports a specific set of channels. We need to iterate over all of them. */ - for (iChannel = minChannels; iChannel <= maxChannels; iChannel += 1) { - /* Test the channel before applying it to the configuration space. */ - unsigned int channels = iChannel; - - /* Make sure our channel range is reset before testing again or else we'll always fail the test. */ - ((ma_snd_pcm_hw_params_any_proc)pContext->alsa.snd_pcm_hw_params_any)(pPCM, pHWParams); - ((ma_snd_pcm_hw_params_set_format_proc)pContext->alsa.snd_pcm_hw_params_set_format)(pPCM, pHWParams, ma_convert_ma_format_to_alsa_format(format)); - - if (((ma_snd_pcm_hw_params_test_channels_proc)pContext->alsa.snd_pcm_hw_params_test_channels)(pPCM, pHWParams, channels) == 0) { - /* The channel count is supported. */ - - /* The configuration space now needs to be restricted to the channel count before extracting the sample rate. */ - ((ma_snd_pcm_hw_params_set_channels_proc)pContext->alsa.snd_pcm_hw_params_set_channels)(pPCM, pHWParams, channels); - - /* Only after the configuration space has been restricted to the specific channel count should we iterate over our sample rates. */ - ma_context_iterate_rates_and_add_native_data_format__alsa(pContext, pPCM, pHWParams, format, channels, 0, pDeviceInfo); - } else { - /* The channel count is not supported. Skip. */ - } - } - } - } else { - /* The format is not supported. Skip. */ - } - } - - ma_free(pHWParams, &pContext->allocationCallbacks); - - ((ma_snd_pcm_close_proc)pContext->alsa.snd_pcm_close)(pPCM); - return MA_SUCCESS; -} - -static ma_result ma_device_uninit__alsa(ma_device* pDevice) -{ - MA_ASSERT(pDevice != NULL); - - if ((ma_snd_pcm_t*)pDevice->alsa.pPCMCapture) { - ((ma_snd_pcm_close_proc)pDevice->pContext->alsa.snd_pcm_close)((ma_snd_pcm_t*)pDevice->alsa.pPCMCapture); - close(pDevice->alsa.wakeupfdCapture); - ma_free(pDevice->alsa.pPollDescriptorsCapture, &pDevice->pContext->allocationCallbacks); - } - - if ((ma_snd_pcm_t*)pDevice->alsa.pPCMPlayback) { - ((ma_snd_pcm_close_proc)pDevice->pContext->alsa.snd_pcm_close)((ma_snd_pcm_t*)pDevice->alsa.pPCMPlayback); - close(pDevice->alsa.wakeupfdPlayback); - ma_free(pDevice->alsa.pPollDescriptorsPlayback, &pDevice->pContext->allocationCallbacks); - } - - return MA_SUCCESS; -} - -static ma_result ma_device_init_by_type__alsa(ma_device* pDevice, const ma_device_config* pConfig, ma_device_descriptor* pDescriptor, ma_device_type deviceType) -{ - ma_result result; - int resultALSA; - ma_snd_pcm_t* pPCM; - ma_bool32 isUsingMMap; - ma_snd_pcm_format_t formatALSA; - ma_format internalFormat; - ma_uint32 internalChannels; - ma_uint32 internalSampleRate; - ma_channel internalChannelMap[MA_MAX_CHANNELS]; - ma_uint32 internalPeriodSizeInFrames; - ma_uint32 internalPeriods; - int openMode; - ma_snd_pcm_hw_params_t* pHWParams; - ma_snd_pcm_sw_params_t* pSWParams; - ma_snd_pcm_uframes_t bufferBoundary; - int pollDescriptorCount; - struct pollfd* pPollDescriptors; - int wakeupfd; - - MA_ASSERT(pConfig != NULL); - MA_ASSERT(deviceType != ma_device_type_duplex); /* This function should only be called for playback _or_ capture, never duplex. */ - MA_ASSERT(pDevice != NULL); - - formatALSA = ma_convert_ma_format_to_alsa_format(pDescriptor->format); - - openMode = 0; - if (pConfig->alsa.noAutoResample) { - openMode |= MA_SND_PCM_NO_AUTO_RESAMPLE; - } - if (pConfig->alsa.noAutoChannels) { - openMode |= MA_SND_PCM_NO_AUTO_CHANNELS; - } - if (pConfig->alsa.noAutoFormat) { - openMode |= MA_SND_PCM_NO_AUTO_FORMAT; - } - - result = ma_context_open_pcm__alsa(pDevice->pContext, pDescriptor->shareMode, deviceType, pDescriptor->pDeviceID, openMode, &pPCM); - if (result != MA_SUCCESS) { - return result; - } - - - /* Hardware parameters. */ - pHWParams = (ma_snd_pcm_hw_params_t*)ma_calloc(((ma_snd_pcm_hw_params_sizeof_proc)pDevice->pContext->alsa.snd_pcm_hw_params_sizeof)(), &pDevice->pContext->allocationCallbacks); - if (pHWParams == NULL) { - ((ma_snd_pcm_close_proc)pDevice->pContext->alsa.snd_pcm_close)(pPCM); - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[ALSA] Failed to allocate memory for hardware parameters."); - return MA_OUT_OF_MEMORY; - } - - resultALSA = ((ma_snd_pcm_hw_params_any_proc)pDevice->pContext->alsa.snd_pcm_hw_params_any)(pPCM, pHWParams); - if (resultALSA < 0) { - ma_free(pHWParams, &pDevice->pContext->allocationCallbacks); - ((ma_snd_pcm_close_proc)pDevice->pContext->alsa.snd_pcm_close)(pPCM); - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[ALSA] Failed to initialize hardware parameters. snd_pcm_hw_params_any() failed."); - return ma_result_from_errno(-resultALSA); - } - - /* MMAP Mode. Try using interleaved MMAP access. If this fails, fall back to standard readi/writei. */ - isUsingMMap = MA_FALSE; -#if 0 /* NOTE: MMAP mode temporarily disabled. */ - if (deviceType != ma_device_type_capture) { /* <-- Disabling MMAP mode for capture devices because I apparently do not have a device that supports it which means I can't test it... Contributions welcome. */ - if (!pConfig->alsa.noMMap) { - if (((ma_snd_pcm_hw_params_set_access_proc)pDevice->pContext->alsa.snd_pcm_hw_params_set_access)(pPCM, pHWParams, MA_SND_PCM_ACCESS_MMAP_INTERLEAVED) == 0) { - pDevice->alsa.isUsingMMap = MA_TRUE; - } - } - } -#endif - - if (!isUsingMMap) { - resultALSA = ((ma_snd_pcm_hw_params_set_access_proc)pDevice->pContext->alsa.snd_pcm_hw_params_set_access)(pPCM, pHWParams, MA_SND_PCM_ACCESS_RW_INTERLEAVED); - if (resultALSA < 0) { - ma_free(pHWParams, &pDevice->pContext->allocationCallbacks); - ((ma_snd_pcm_close_proc)pDevice->pContext->alsa.snd_pcm_close)(pPCM); - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[ALSA] Failed to set access mode to neither SND_PCM_ACCESS_MMAP_INTERLEAVED nor SND_PCM_ACCESS_RW_INTERLEAVED. snd_pcm_hw_params_set_access() failed."); - return ma_result_from_errno(-resultALSA); - } - } - - /* - Most important properties first. The documentation for OSS (yes, I know this is ALSA!) recommends format, channels, then sample rate. I can't - find any documentation for ALSA specifically, so I'm going to copy the recommendation for OSS. - */ - - /* Format. */ - { - /* - At this point we should have a list of supported formats, so now we need to find the best one. We first check if the requested format is - supported, and if so, use that one. If it's not supported, we just run though a list of formats and try to find the best one. - */ - if (formatALSA == MA_SND_PCM_FORMAT_UNKNOWN || ((ma_snd_pcm_hw_params_test_format_proc)pDevice->pContext->alsa.snd_pcm_hw_params_test_format)(pPCM, pHWParams, formatALSA) != 0) { - /* We're either requesting the native format or the specified format is not supported. */ - size_t iFormat; - - formatALSA = MA_SND_PCM_FORMAT_UNKNOWN; - for (iFormat = 0; iFormat < ma_countof(g_maFormatPriorities); ++iFormat) { - if (((ma_snd_pcm_hw_params_test_format_proc)pDevice->pContext->alsa.snd_pcm_hw_params_test_format)(pPCM, pHWParams, ma_convert_ma_format_to_alsa_format(g_maFormatPriorities[iFormat])) == 0) { - formatALSA = ma_convert_ma_format_to_alsa_format(g_maFormatPriorities[iFormat]); - break; - } - } - - if (formatALSA == MA_SND_PCM_FORMAT_UNKNOWN) { - ma_free(pHWParams, &pDevice->pContext->allocationCallbacks); - ((ma_snd_pcm_close_proc)pDevice->pContext->alsa.snd_pcm_close)(pPCM); - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[ALSA] Format not supported. The device does not support any miniaudio formats."); - return MA_FORMAT_NOT_SUPPORTED; - } - } - - resultALSA = ((ma_snd_pcm_hw_params_set_format_proc)pDevice->pContext->alsa.snd_pcm_hw_params_set_format)(pPCM, pHWParams, formatALSA); - if (resultALSA < 0) { - ma_free(pHWParams, &pDevice->pContext->allocationCallbacks); - ((ma_snd_pcm_close_proc)pDevice->pContext->alsa.snd_pcm_close)(pPCM); - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[ALSA] Format not supported. snd_pcm_hw_params_set_format() failed."); - return ma_result_from_errno(-resultALSA); - } - - internalFormat = ma_format_from_alsa(formatALSA); - if (internalFormat == ma_format_unknown) { - ma_free(pHWParams, &pDevice->pContext->allocationCallbacks); - ((ma_snd_pcm_close_proc)pDevice->pContext->alsa.snd_pcm_close)(pPCM); - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[ALSA] The chosen format is not supported by miniaudio."); - return MA_FORMAT_NOT_SUPPORTED; - } - } - - /* Channels. */ - { - unsigned int channels = pDescriptor->channels; - if (channels == 0) { - channels = MA_DEFAULT_CHANNELS; - } - - resultALSA = ((ma_snd_pcm_hw_params_set_channels_near_proc)pDevice->pContext->alsa.snd_pcm_hw_params_set_channels_near)(pPCM, pHWParams, &channels); - if (resultALSA < 0) { - ma_free(pHWParams, &pDevice->pContext->allocationCallbacks); - ((ma_snd_pcm_close_proc)pDevice->pContext->alsa.snd_pcm_close)(pPCM); - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[ALSA] Failed to set channel count. snd_pcm_hw_params_set_channels_near() failed."); - return ma_result_from_errno(-resultALSA); - } - - internalChannels = (ma_uint32)channels; - } - - /* Sample Rate */ - { - unsigned int sampleRate; - - /* - It appears there's either a bug in ALSA, a bug in some drivers, or I'm doing something silly; but having resampling enabled causes - problems with some device configurations when used in conjunction with MMAP access mode. To fix this problem we need to disable - resampling. - - To reproduce this problem, open the "plug:dmix" device, and set the sample rate to 44100. Internally, it looks like dmix uses a - sample rate of 48000. The hardware parameters will get set correctly with no errors, but it looks like the 44100 -> 48000 resampling - doesn't work properly - but only with MMAP access mode. You will notice skipping/crackling in the audio, and it'll run at a slightly - faster rate. - - miniaudio has built-in support for sample rate conversion (albeit low quality at the moment), so disabling resampling should be fine - for us. The only problem is that it won't be taking advantage of any kind of hardware-accelerated resampling and it won't be very - good quality until I get a chance to improve the quality of miniaudio's software sample rate conversion. - - I don't currently know if the dmix plugin is the only one with this error. Indeed, this is the only one I've been able to reproduce - this error with. In the future, we may want to restrict the disabling of resampling to only known bad plugins. - */ - ((ma_snd_pcm_hw_params_set_rate_resample_proc)pDevice->pContext->alsa.snd_pcm_hw_params_set_rate_resample)(pPCM, pHWParams, 0); - - sampleRate = pDescriptor->sampleRate; - if (sampleRate == 0) { - sampleRate = MA_DEFAULT_SAMPLE_RATE; - } - - resultALSA = ((ma_snd_pcm_hw_params_set_rate_near_proc)pDevice->pContext->alsa.snd_pcm_hw_params_set_rate_near)(pPCM, pHWParams, &sampleRate, 0); - if (resultALSA < 0) { - ma_free(pHWParams, &pDevice->pContext->allocationCallbacks); - ((ma_snd_pcm_close_proc)pDevice->pContext->alsa.snd_pcm_close)(pPCM); - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[ALSA] Sample rate not supported. snd_pcm_hw_params_set_rate_near() failed."); - return ma_result_from_errno(-resultALSA); - } - - internalSampleRate = (ma_uint32)sampleRate; - } - - /* Periods. */ - { - ma_uint32 periods = pDescriptor->periodCount; - - resultALSA = ((ma_snd_pcm_hw_params_set_periods_near_proc)pDevice->pContext->alsa.snd_pcm_hw_params_set_periods_near)(pPCM, pHWParams, &periods, NULL); - if (resultALSA < 0) { - ma_free(pHWParams, &pDevice->pContext->allocationCallbacks); - ((ma_snd_pcm_close_proc)pDevice->pContext->alsa.snd_pcm_close)(pPCM); - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[ALSA] Failed to set period count. snd_pcm_hw_params_set_periods_near() failed."); - return ma_result_from_errno(-resultALSA); - } - - internalPeriods = periods; - } - - /* Buffer Size */ - { - ma_snd_pcm_uframes_t actualBufferSizeInFrames = ma_calculate_buffer_size_in_frames_from_descriptor(pDescriptor, internalSampleRate, pConfig->performanceProfile) * internalPeriods; - - resultALSA = ((ma_snd_pcm_hw_params_set_buffer_size_near_proc)pDevice->pContext->alsa.snd_pcm_hw_params_set_buffer_size_near)(pPCM, pHWParams, &actualBufferSizeInFrames); - if (resultALSA < 0) { - ma_free(pHWParams, &pDevice->pContext->allocationCallbacks); - ((ma_snd_pcm_close_proc)pDevice->pContext->alsa.snd_pcm_close)(pPCM); - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[ALSA] Failed to set buffer size for device. snd_pcm_hw_params_set_buffer_size() failed."); - return ma_result_from_errno(-resultALSA); - } - - internalPeriodSizeInFrames = actualBufferSizeInFrames / internalPeriods; - } - - /* Apply hardware parameters. */ - resultALSA = ((ma_snd_pcm_hw_params_proc)pDevice->pContext->alsa.snd_pcm_hw_params)(pPCM, pHWParams); - if (resultALSA < 0) { - ma_free(pHWParams, &pDevice->pContext->allocationCallbacks); - ((ma_snd_pcm_close_proc)pDevice->pContext->alsa.snd_pcm_close)(pPCM); - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[ALSA] Failed to set hardware parameters. snd_pcm_hw_params() failed."); - return ma_result_from_errno(-resultALSA); - } - - ma_free(pHWParams, &pDevice->pContext->allocationCallbacks); - pHWParams = NULL; - - - /* Software parameters. */ - pSWParams = (ma_snd_pcm_sw_params_t*)ma_calloc(((ma_snd_pcm_sw_params_sizeof_proc)pDevice->pContext->alsa.snd_pcm_sw_params_sizeof)(), &pDevice->pContext->allocationCallbacks); - if (pSWParams == NULL) { - ((ma_snd_pcm_close_proc)pDevice->pContext->alsa.snd_pcm_close)(pPCM); - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[ALSA] Failed to allocate memory for software parameters."); - return MA_OUT_OF_MEMORY; - } - - resultALSA = ((ma_snd_pcm_sw_params_current_proc)pDevice->pContext->alsa.snd_pcm_sw_params_current)(pPCM, pSWParams); - if (resultALSA < 0) { - ma_free(pSWParams, &pDevice->pContext->allocationCallbacks); - ((ma_snd_pcm_close_proc)pDevice->pContext->alsa.snd_pcm_close)(pPCM); - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[ALSA] Failed to initialize software parameters. snd_pcm_sw_params_current() failed."); - return ma_result_from_errno(-resultALSA); - } - - resultALSA = ((ma_snd_pcm_sw_params_set_avail_min_proc)pDevice->pContext->alsa.snd_pcm_sw_params_set_avail_min)(pPCM, pSWParams, ma_prev_power_of_2(internalPeriodSizeInFrames)); - if (resultALSA < 0) { - ma_free(pSWParams, &pDevice->pContext->allocationCallbacks); - ((ma_snd_pcm_close_proc)pDevice->pContext->alsa.snd_pcm_close)(pPCM); - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[ALSA] snd_pcm_sw_params_set_avail_min() failed."); - return ma_result_from_errno(-resultALSA); - } - - resultALSA = ((ma_snd_pcm_sw_params_get_boundary_proc)pDevice->pContext->alsa.snd_pcm_sw_params_get_boundary)(pSWParams, &bufferBoundary); - if (resultALSA < 0) { - bufferBoundary = internalPeriodSizeInFrames * internalPeriods; - } - - if (deviceType == ma_device_type_playback && !isUsingMMap) { /* Only playback devices in writei/readi mode need a start threshold. */ - /* - Subtle detail here with the start threshold. When in playback-only mode (no full-duplex) we can set the start threshold to - the size of a period. But for full-duplex we need to set it such that it is at least two periods. - */ - resultALSA = ((ma_snd_pcm_sw_params_set_start_threshold_proc)pDevice->pContext->alsa.snd_pcm_sw_params_set_start_threshold)(pPCM, pSWParams, internalPeriodSizeInFrames*2); - if (resultALSA < 0) { - ma_free(pSWParams, &pDevice->pContext->allocationCallbacks); - ((ma_snd_pcm_close_proc)pDevice->pContext->alsa.snd_pcm_close)(pPCM); - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[ALSA] Failed to set start threshold for playback device. snd_pcm_sw_params_set_start_threshold() failed."); - return ma_result_from_errno(-resultALSA); - } - - resultALSA = ((ma_snd_pcm_sw_params_set_stop_threshold_proc)pDevice->pContext->alsa.snd_pcm_sw_params_set_stop_threshold)(pPCM, pSWParams, bufferBoundary); - if (resultALSA < 0) { /* Set to boundary to loop instead of stop in the event of an xrun. */ - ma_free(pSWParams, &pDevice->pContext->allocationCallbacks); - ((ma_snd_pcm_close_proc)pDevice->pContext->alsa.snd_pcm_close)(pPCM); - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[ALSA] Failed to set stop threshold for playback device. snd_pcm_sw_params_set_stop_threshold() failed."); - return ma_result_from_errno(-resultALSA); - } - } - - resultALSA = ((ma_snd_pcm_sw_params_proc)pDevice->pContext->alsa.snd_pcm_sw_params)(pPCM, pSWParams); - if (resultALSA < 0) { - ma_free(pSWParams, &pDevice->pContext->allocationCallbacks); - ((ma_snd_pcm_close_proc)pDevice->pContext->alsa.snd_pcm_close)(pPCM); - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[ALSA] Failed to set software parameters. snd_pcm_sw_params() failed."); - return ma_result_from_errno(-resultALSA); - } - - ma_free(pSWParams, &pDevice->pContext->allocationCallbacks); - pSWParams = NULL; - - - /* Grab the internal channel map. For now we're not going to bother trying to change the channel map and instead just do it ourselves. */ - { - ma_snd_pcm_chmap_t* pChmap = NULL; - if (pDevice->pContext->alsa.snd_pcm_get_chmap != NULL) { - pChmap = ((ma_snd_pcm_get_chmap_proc)pDevice->pContext->alsa.snd_pcm_get_chmap)(pPCM); - } - - if (pChmap != NULL) { - ma_uint32 iChannel; - - /* There are cases where the returned channel map can have a different channel count than was returned by snd_pcm_hw_params_set_channels_near(). */ - if (pChmap->channels >= internalChannels) { - /* Drop excess channels. */ - for (iChannel = 0; iChannel < internalChannels; ++iChannel) { - internalChannelMap[iChannel] = ma_convert_alsa_channel_position_to_ma_channel(pChmap->pos[iChannel]); - } - } else { - ma_uint32 i; - - /* - Excess channels use defaults. Do an initial fill with defaults, overwrite the first pChmap->channels, validate to ensure there are no duplicate - channels. If validation fails, fall back to defaults. - */ - ma_bool32 isValid = MA_TRUE; - - /* Fill with defaults. */ - ma_channel_map_init_standard(ma_standard_channel_map_alsa, internalChannelMap, ma_countof(internalChannelMap), internalChannels); - - /* Overwrite first pChmap->channels channels. */ - for (iChannel = 0; iChannel < pChmap->channels; ++iChannel) { - internalChannelMap[iChannel] = ma_convert_alsa_channel_position_to_ma_channel(pChmap->pos[iChannel]); - } - - /* Validate. */ - for (i = 0; i < internalChannels && isValid; ++i) { - ma_uint32 j; - for (j = i+1; j < internalChannels; ++j) { - if (internalChannelMap[i] == internalChannelMap[j]) { - isValid = MA_FALSE; - break; - } - } - } - - /* If our channel map is invalid, fall back to defaults. */ - if (!isValid) { - ma_channel_map_init_standard(ma_standard_channel_map_alsa, internalChannelMap, ma_countof(internalChannelMap), internalChannels); - } - } - - free(pChmap); - pChmap = NULL; - } else { - /* Could not retrieve the channel map. Fall back to a hard-coded assumption. */ - ma_channel_map_init_standard(ma_standard_channel_map_alsa, internalChannelMap, ma_countof(internalChannelMap), internalChannels); - } - } - - - /* - We need to retrieve the poll descriptors so we can use poll() to wait for data to become - available for reading or writing. There's no well defined maximum for this so we're just going - to allocate this on the heap. - */ - pollDescriptorCount = ((ma_snd_pcm_poll_descriptors_count_proc)pDevice->pContext->alsa.snd_pcm_poll_descriptors_count)(pPCM); - if (pollDescriptorCount <= 0) { - ((ma_snd_pcm_close_proc)pDevice->pContext->alsa.snd_pcm_close)(pPCM); - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[ALSA] Failed to retrieve poll descriptors count."); - return MA_ERROR; - } - - pPollDescriptors = (struct pollfd*)ma_malloc(sizeof(*pPollDescriptors) * (pollDescriptorCount + 1), &pDevice->pContext->allocationCallbacks); /* +1 because we want room for the wakeup descriptor. */ - if (pPollDescriptors == NULL) { - ((ma_snd_pcm_close_proc)pDevice->pContext->alsa.snd_pcm_close)(pPCM); - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[ALSA] Failed to allocate memory for poll descriptors."); - return MA_OUT_OF_MEMORY; - } - - /* - We need an eventfd to wakeup from poll() and avoid a deadlock in situations where the driver - never returns from writei() and readi(). This has been observed with the "pulse" device. - */ - wakeupfd = eventfd(0, 0); - if (wakeupfd < 0) { - ma_free(pPollDescriptors, &pDevice->pContext->allocationCallbacks); - ((ma_snd_pcm_close_proc)pDevice->pContext->alsa.snd_pcm_close)(pPCM); - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[ALSA] Failed to create eventfd for poll wakeup."); - return ma_result_from_errno(errno); - } - - /* We'll place the wakeup fd at the start of the buffer. */ - pPollDescriptors[0].fd = wakeupfd; - pPollDescriptors[0].events = POLLIN; /* We only care about waiting to read from the wakeup file descriptor. */ - pPollDescriptors[0].revents = 0; - - /* We can now extract the PCM poll descriptors which we place after the wakeup descriptor. */ - pollDescriptorCount = ((ma_snd_pcm_poll_descriptors_proc)pDevice->pContext->alsa.snd_pcm_poll_descriptors)(pPCM, pPollDescriptors + 1, pollDescriptorCount); /* +1 because we want to place these descriptors after the wakeup descriptor. */ - if (pollDescriptorCount <= 0) { - close(wakeupfd); - ma_free(pPollDescriptors, &pDevice->pContext->allocationCallbacks); - ((ma_snd_pcm_close_proc)pDevice->pContext->alsa.snd_pcm_close)(pPCM); - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[ALSA] Failed to retrieve poll descriptors."); - return MA_ERROR; - } - - if (deviceType == ma_device_type_capture) { - pDevice->alsa.pollDescriptorCountCapture = pollDescriptorCount; - pDevice->alsa.pPollDescriptorsCapture = pPollDescriptors; - pDevice->alsa.wakeupfdCapture = wakeupfd; - } else { - pDevice->alsa.pollDescriptorCountPlayback = pollDescriptorCount; - pDevice->alsa.pPollDescriptorsPlayback = pPollDescriptors; - pDevice->alsa.wakeupfdPlayback = wakeupfd; - } - - - /* We're done. Prepare the device. */ - resultALSA = ((ma_snd_pcm_prepare_proc)pDevice->pContext->alsa.snd_pcm_prepare)(pPCM); - if (resultALSA < 0) { - close(wakeupfd); - ma_free(pPollDescriptors, &pDevice->pContext->allocationCallbacks); - ((ma_snd_pcm_close_proc)pDevice->pContext->alsa.snd_pcm_close)(pPCM); - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[ALSA] Failed to prepare device."); - return ma_result_from_errno(-resultALSA); - } - - - if (deviceType == ma_device_type_capture) { - pDevice->alsa.pPCMCapture = (ma_ptr)pPCM; - pDevice->alsa.isUsingMMapCapture = isUsingMMap; - } else { - pDevice->alsa.pPCMPlayback = (ma_ptr)pPCM; - pDevice->alsa.isUsingMMapPlayback = isUsingMMap; - } - - pDescriptor->format = internalFormat; - pDescriptor->channels = internalChannels; - pDescriptor->sampleRate = internalSampleRate; - ma_channel_map_copy(pDescriptor->channelMap, internalChannelMap, ma_min(internalChannels, MA_MAX_CHANNELS)); - pDescriptor->periodSizeInFrames = internalPeriodSizeInFrames; - pDescriptor->periodCount = internalPeriods; - - return MA_SUCCESS; -} - -static ma_result ma_device_init__alsa(ma_device* pDevice, const ma_device_config* pConfig, ma_device_descriptor* pDescriptorPlayback, ma_device_descriptor* pDescriptorCapture) -{ - MA_ASSERT(pDevice != NULL); - - MA_ZERO_OBJECT(&pDevice->alsa); - - if (pConfig->deviceType == ma_device_type_loopback) { - return MA_DEVICE_TYPE_NOT_SUPPORTED; - } - - if (pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) { - ma_result result = ma_device_init_by_type__alsa(pDevice, pConfig, pDescriptorCapture, ma_device_type_capture); - if (result != MA_SUCCESS) { - return result; - } - } - - if (pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) { - ma_result result = ma_device_init_by_type__alsa(pDevice, pConfig, pDescriptorPlayback, ma_device_type_playback); - if (result != MA_SUCCESS) { - return result; - } - } - - return MA_SUCCESS; -} - -static ma_result ma_device_start__alsa(ma_device* pDevice) -{ - int resultALSA; - - if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) { - resultALSA = ((ma_snd_pcm_start_proc)pDevice->pContext->alsa.snd_pcm_start)((ma_snd_pcm_t*)pDevice->alsa.pPCMCapture); - if (resultALSA < 0) { - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[ALSA] Failed to start capture device."); - return ma_result_from_errno(-resultALSA); - } - } - - if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) { - /* - When data is written to the device we wait for the device to get ready to receive data with poll(). In my testing - I have observed that poll() can sometimes block forever unless the device is started explicitly with snd_pcm_start() - or some data is written with snd_pcm_writei(). - - To resolve this I've decided to do an explicit start with snd_pcm_start(). The problem with this is that the device - is started without any data in the internal buffer which will result in an immediate underrun. If instead we were - to call into snd_pcm_writei() in an attempt to prevent the underrun, we would run the risk of a weird deadlock - issue as documented inside ma_device_write__alsa(). - */ - resultALSA = ((ma_snd_pcm_start_proc)pDevice->pContext->alsa.snd_pcm_start)((ma_snd_pcm_t*)pDevice->alsa.pPCMPlayback); - if (resultALSA < 0) { - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[ALSA] Failed to start playback device."); - return ma_result_from_errno(-resultALSA); - } - } - - return MA_SUCCESS; -} - -static ma_result ma_device_stop__alsa(ma_device* pDevice) -{ - /* - The stop callback will get called on the worker thread after read/write__alsa() has returned. At this point there is - a small chance that our wakeupfd has not been cleared. We'll clear that out now if applicable. - */ - int resultPoll; - int resultRead; - - if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) { - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "[ALSA] Dropping capture device...\n"); - ((ma_snd_pcm_drop_proc)pDevice->pContext->alsa.snd_pcm_drop)((ma_snd_pcm_t*)pDevice->alsa.pPCMCapture); - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "[ALSA] Dropping capture device successful.\n"); - - /* We need to prepare the device again, otherwise we won't be able to restart the device. */ - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "[ALSA] Preparing capture device...\n"); - if (((ma_snd_pcm_prepare_proc)pDevice->pContext->alsa.snd_pcm_prepare)((ma_snd_pcm_t*)pDevice->alsa.pPCMCapture) < 0) { - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "[ALSA] Preparing capture device failed.\n"); - } else { - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "[ALSA] Preparing capture device successful.\n"); - } - - /* Clear the wakeupfd. */ - resultPoll = poll((struct pollfd*)pDevice->alsa.pPollDescriptorsCapture, 1, 0); - if (resultPoll > 0) { - ma_uint64 t; - resultRead = read(((struct pollfd*)pDevice->alsa.pPollDescriptorsCapture)[0].fd, &t, sizeof(t)); - if (resultRead != sizeof(t)) { - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "[ALSA] Failed to read from capture wakeupfd. read() = %d\n", resultRead); - } - } - } - - if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) { - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "[ALSA] Dropping playback device...\n"); - ((ma_snd_pcm_drop_proc)pDevice->pContext->alsa.snd_pcm_drop)((ma_snd_pcm_t*)pDevice->alsa.pPCMPlayback); - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "[ALSA] Dropping playback device successful.\n"); - - /* We need to prepare the device again, otherwise we won't be able to restart the device. */ - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "[ALSA] Preparing playback device...\n"); - if (((ma_snd_pcm_prepare_proc)pDevice->pContext->alsa.snd_pcm_prepare)((ma_snd_pcm_t*)pDevice->alsa.pPCMPlayback) < 0) { - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "[ALSA] Preparing playback device failed.\n"); - } else { - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "[ALSA] Preparing playback device successful.\n"); - } - - /* Clear the wakeupfd. */ - resultPoll = poll((struct pollfd*)pDevice->alsa.pPollDescriptorsPlayback, 1, 0); - if (resultPoll > 0) { - ma_uint64 t; - resultRead = read(((struct pollfd*)pDevice->alsa.pPollDescriptorsPlayback)[0].fd, &t, sizeof(t)); - if (resultRead != sizeof(t)) { - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "[ALSA] Failed to read from playback wakeupfd. read() = %d\n", resultRead); - } - } - } - - return MA_SUCCESS; -} - -static ma_result ma_device_wait__alsa(ma_device* pDevice, ma_snd_pcm_t* pPCM, struct pollfd* pPollDescriptors, int pollDescriptorCount, short requiredEvent) -{ - for (;;) { - unsigned short revents; - int resultALSA; - int resultPoll = poll(pPollDescriptors, pollDescriptorCount, -1); - if (resultPoll < 0) { - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_WARNING, "[ALSA] poll() failed.\n"); - - /* - There have been reports that poll() is returning an error randomly and that instead of - returning an error, simply trying again will work. I'm experimenting with adopting this - advice. - */ - continue; - /*return ma_result_from_errno(errno);*/ - } - - /* - Before checking the ALSA poll descriptor flag we need to check if the wakeup descriptor - has had it's POLLIN flag set. If so, we need to actually read the data and then exit the - function. The wakeup descriptor will be the first item in the descriptors buffer. - */ - if ((pPollDescriptors[0].revents & POLLIN) != 0) { - ma_uint64 t; - int resultRead = read(pPollDescriptors[0].fd, &t, sizeof(t)); /* <-- Important that we read here so that the next write() does not block. */ - if (resultRead < 0) { - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[ALSA] read() failed.\n"); - return ma_result_from_errno(errno); - } - - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "[ALSA] POLLIN set for wakeupfd\n"); - return MA_DEVICE_NOT_STARTED; - } - - /* - Getting here means that some data should be able to be read. We need to use ALSA to - translate the revents flags for us. - */ - resultALSA = ((ma_snd_pcm_poll_descriptors_revents_proc)pDevice->pContext->alsa.snd_pcm_poll_descriptors_revents)(pPCM, pPollDescriptors + 1, pollDescriptorCount - 1, &revents); /* +1, -1 to ignore the wakeup descriptor. */ - if (resultALSA < 0) { - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[ALSA] snd_pcm_poll_descriptors_revents() failed.\n"); - return ma_result_from_errno(-resultALSA); - } - - if ((revents & POLLERR) != 0) { - ma_snd_pcm_state_t state = ((ma_snd_pcm_state_proc)pDevice->pContext->alsa.snd_pcm_state)(pPCM); - if (state == MA_SND_PCM_STATE_XRUN) { - /* The PCM is in a xrun state. This will be recovered from at a higher level. We can disregard this. */ - } else { - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_WARNING, "[ALSA] POLLERR detected. status = %d\n", ((ma_snd_pcm_state_proc)pDevice->pContext->alsa.snd_pcm_state)(pPCM)); - } - } - - if ((revents & requiredEvent) == requiredEvent) { - break; /* We're done. Data available for reading or writing. */ - } - } - - return MA_SUCCESS; -} - -static ma_result ma_device_wait_read__alsa(ma_device* pDevice) -{ - return ma_device_wait__alsa(pDevice, (ma_snd_pcm_t*)pDevice->alsa.pPCMCapture, (struct pollfd*)pDevice->alsa.pPollDescriptorsCapture, pDevice->alsa.pollDescriptorCountCapture + 1, POLLIN); /* +1 to account for the wakeup descriptor. */ -} - -static ma_result ma_device_wait_write__alsa(ma_device* pDevice) -{ - return ma_device_wait__alsa(pDevice, (ma_snd_pcm_t*)pDevice->alsa.pPCMPlayback, (struct pollfd*)pDevice->alsa.pPollDescriptorsPlayback, pDevice->alsa.pollDescriptorCountPlayback + 1, POLLOUT); /* +1 to account for the wakeup descriptor. */ -} - -static ma_result ma_device_read__alsa(ma_device* pDevice, void* pFramesOut, ma_uint32 frameCount, ma_uint32* pFramesRead) -{ - ma_snd_pcm_sframes_t resultALSA = 0; - - MA_ASSERT(pDevice != NULL); - MA_ASSERT(pFramesOut != NULL); - - if (pFramesRead != NULL) { - *pFramesRead = 0; - } - - while (ma_device_get_state(pDevice) == ma_device_state_started) { - ma_result result; - - /* The first thing to do is wait for data to become available for reading. This will return an error code if the device has been stopped. */ - result = ma_device_wait_read__alsa(pDevice); - if (result != MA_SUCCESS) { - return result; - } - - /* Getting here means we should have data available. */ - resultALSA = ((ma_snd_pcm_readi_proc)pDevice->pContext->alsa.snd_pcm_readi)((ma_snd_pcm_t*)pDevice->alsa.pPCMCapture, pFramesOut, frameCount); - if (resultALSA >= 0) { - break; /* Success. */ - } else { - if (resultALSA == -EAGAIN) { - /*ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "EGAIN (read)\n");*/ - continue; /* Try again. */ - } else if (resultALSA == -EPIPE) { - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "EPIPE (read)\n"); - - /* Overrun. Recover and try again. If this fails we need to return an error. */ - resultALSA = ((ma_snd_pcm_recover_proc)pDevice->pContext->alsa.snd_pcm_recover)((ma_snd_pcm_t*)pDevice->alsa.pPCMCapture, resultALSA, MA_TRUE); - if (resultALSA < 0) { - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[ALSA] Failed to recover device after overrun."); - return ma_result_from_errno((int)-resultALSA); - } - - resultALSA = ((ma_snd_pcm_start_proc)pDevice->pContext->alsa.snd_pcm_start)((ma_snd_pcm_t*)pDevice->alsa.pPCMCapture); - if (resultALSA < 0) { - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[ALSA] Failed to start device after underrun."); - return ma_result_from_errno((int)-resultALSA); - } - - continue; /* Try reading again. */ - } - } - } - - if (pFramesRead != NULL) { - *pFramesRead = resultALSA; - } - - return MA_SUCCESS; -} - -static ma_result ma_device_write__alsa(ma_device* pDevice, const void* pFrames, ma_uint32 frameCount, ma_uint32* pFramesWritten) -{ - ma_snd_pcm_sframes_t resultALSA = 0; - - MA_ASSERT(pDevice != NULL); - MA_ASSERT(pFrames != NULL); - - if (pFramesWritten != NULL) { - *pFramesWritten = 0; - } - - while (ma_device_get_state(pDevice) == ma_device_state_started) { - ma_result result; - - /* The first thing to do is wait for space to become available for writing. This will return an error code if the device has been stopped. */ - result = ma_device_wait_write__alsa(pDevice); - if (result != MA_SUCCESS) { - return result; - } - - resultALSA = ((ma_snd_pcm_writei_proc)pDevice->pContext->alsa.snd_pcm_writei)((ma_snd_pcm_t*)pDevice->alsa.pPCMPlayback, pFrames, frameCount); - if (resultALSA >= 0) { - break; /* Success. */ - } else { - if (resultALSA == -EAGAIN) { - /*ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "EGAIN (write)\n");*/ - continue; /* Try again. */ - } else if (resultALSA == -EPIPE) { - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "EPIPE (write)\n"); - - /* Underrun. Recover and try again. If this fails we need to return an error. */ - resultALSA = ((ma_snd_pcm_recover_proc)pDevice->pContext->alsa.snd_pcm_recover)((ma_snd_pcm_t*)pDevice->alsa.pPCMPlayback, resultALSA, MA_TRUE); /* MA_TRUE=silent (don't print anything on error). */ - if (resultALSA < 0) { - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[ALSA] Failed to recover device after underrun."); - return ma_result_from_errno((int)-resultALSA); - } - - /* - In my testing I have had a situation where writei() does not automatically restart the device even though I've set it - up as such in the software parameters. What will happen is writei() will block indefinitely even though the number of - frames is well beyond the auto-start threshold. To work around this I've needed to add an explicit start here. Not sure - if this is me just being stupid and not recovering the device properly, but this definitely feels like something isn't - quite right here. - */ - resultALSA = ((ma_snd_pcm_start_proc)pDevice->pContext->alsa.snd_pcm_start)((ma_snd_pcm_t*)pDevice->alsa.pPCMPlayback); - if (resultALSA < 0) { - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[ALSA] Failed to start device after underrun."); - return ma_result_from_errno((int)-resultALSA); - } - - continue; /* Try writing again. */ - } - } - } - - if (pFramesWritten != NULL) { - *pFramesWritten = resultALSA; - } - - return MA_SUCCESS; -} - -static ma_result ma_device_data_loop_wakeup__alsa(ma_device* pDevice) -{ - ma_uint64 t = 1; - int resultWrite = 0; - - MA_ASSERT(pDevice != NULL); - - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "[ALSA] Waking up...\n"); - - /* Write to an eventfd to trigger a wakeup from poll() and abort any reading or writing. */ - if (pDevice->alsa.pPollDescriptorsCapture != NULL) { - resultWrite = write(pDevice->alsa.wakeupfdCapture, &t, sizeof(t)); - } - if (pDevice->alsa.pPollDescriptorsPlayback != NULL) { - resultWrite = write(pDevice->alsa.wakeupfdPlayback, &t, sizeof(t)); - } - - if (resultWrite < 0) { - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[ALSA] write() failed.\n"); - return ma_result_from_errno(errno); - } - - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "[ALSA] Waking up completed successfully.\n"); - - return MA_SUCCESS; -} - -static ma_result ma_context_uninit__alsa(ma_context* pContext) -{ - MA_ASSERT(pContext != NULL); - MA_ASSERT(pContext->backend == ma_backend_alsa); - - /* Clean up memory for memory leak checkers. */ - ((ma_snd_config_update_free_global_proc)pContext->alsa.snd_config_update_free_global)(); - -#ifndef MA_NO_RUNTIME_LINKING - ma_dlclose(ma_context_get_log(pContext), pContext->alsa.asoundSO); -#endif - - ma_mutex_uninit(&pContext->alsa.internalDeviceEnumLock); - - return MA_SUCCESS; -} - -static ma_result ma_context_init__alsa(ma_context* pContext, const ma_context_config* pConfig, ma_backend_callbacks* pCallbacks) -{ - ma_result result; -#ifndef MA_NO_RUNTIME_LINKING - const char* libasoundNames[] = { - "libasound.so.2", - "libasound.so" - }; - size_t i; - - for (i = 0; i < ma_countof(libasoundNames); ++i) { - pContext->alsa.asoundSO = ma_dlopen(ma_context_get_log(pContext), libasoundNames[i]); - if (pContext->alsa.asoundSO != NULL) { - break; - } - } - - if (pContext->alsa.asoundSO == NULL) { - ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_DEBUG, "[ALSA] Failed to open shared object.\n"); - return MA_NO_BACKEND; - } - - pContext->alsa.snd_pcm_open = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_open"); - pContext->alsa.snd_pcm_close = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_close"); - pContext->alsa.snd_pcm_hw_params_sizeof = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_hw_params_sizeof"); - pContext->alsa.snd_pcm_hw_params_any = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_hw_params_any"); - pContext->alsa.snd_pcm_hw_params_set_format = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_hw_params_set_format"); - pContext->alsa.snd_pcm_hw_params_set_format_first = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_hw_params_set_format_first"); - pContext->alsa.snd_pcm_hw_params_get_format_mask = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_hw_params_get_format_mask"); - pContext->alsa.snd_pcm_hw_params_set_channels = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_hw_params_set_channels"); - pContext->alsa.snd_pcm_hw_params_set_channels_near = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_hw_params_set_channels_near"); - pContext->alsa.snd_pcm_hw_params_set_channels_minmax = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_hw_params_set_channels_minmax"); - pContext->alsa.snd_pcm_hw_params_set_rate_resample = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_hw_params_set_rate_resample"); - pContext->alsa.snd_pcm_hw_params_set_rate = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_hw_params_set_rate"); - pContext->alsa.snd_pcm_hw_params_set_rate_near = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_hw_params_set_rate_near"); - pContext->alsa.snd_pcm_hw_params_set_buffer_size_near = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_hw_params_set_buffer_size_near"); - pContext->alsa.snd_pcm_hw_params_set_periods_near = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_hw_params_set_periods_near"); - pContext->alsa.snd_pcm_hw_params_set_access = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_hw_params_set_access"); - pContext->alsa.snd_pcm_hw_params_get_format = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_hw_params_get_format"); - pContext->alsa.snd_pcm_hw_params_get_channels = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_hw_params_get_channels"); - pContext->alsa.snd_pcm_hw_params_get_channels_min = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_hw_params_get_channels_min"); - pContext->alsa.snd_pcm_hw_params_get_channels_max = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_hw_params_get_channels_max"); - pContext->alsa.snd_pcm_hw_params_get_rate = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_hw_params_get_rate"); - pContext->alsa.snd_pcm_hw_params_get_rate_min = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_hw_params_get_rate_min"); - pContext->alsa.snd_pcm_hw_params_get_rate_max = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_hw_params_get_rate_max"); - pContext->alsa.snd_pcm_hw_params_get_buffer_size = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_hw_params_get_buffer_size"); - pContext->alsa.snd_pcm_hw_params_get_periods = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_hw_params_get_periods"); - pContext->alsa.snd_pcm_hw_params_get_access = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_hw_params_get_access"); - pContext->alsa.snd_pcm_hw_params_test_format = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_hw_params_test_format"); - pContext->alsa.snd_pcm_hw_params_test_channels = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_hw_params_test_channels"); - pContext->alsa.snd_pcm_hw_params_test_rate = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_hw_params_test_rate"); - pContext->alsa.snd_pcm_hw_params = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_hw_params"); - pContext->alsa.snd_pcm_sw_params_sizeof = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_sw_params_sizeof"); - pContext->alsa.snd_pcm_sw_params_current = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_sw_params_current"); - pContext->alsa.snd_pcm_sw_params_get_boundary = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_sw_params_get_boundary"); - pContext->alsa.snd_pcm_sw_params_set_avail_min = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_sw_params_set_avail_min"); - pContext->alsa.snd_pcm_sw_params_set_start_threshold = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_sw_params_set_start_threshold"); - pContext->alsa.snd_pcm_sw_params_set_stop_threshold = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_sw_params_set_stop_threshold"); - pContext->alsa.snd_pcm_sw_params = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_sw_params"); - pContext->alsa.snd_pcm_format_mask_sizeof = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_format_mask_sizeof"); - pContext->alsa.snd_pcm_format_mask_test = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_format_mask_test"); - pContext->alsa.snd_pcm_get_chmap = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_get_chmap"); - pContext->alsa.snd_pcm_state = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_state"); - pContext->alsa.snd_pcm_prepare = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_prepare"); - pContext->alsa.snd_pcm_start = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_start"); - pContext->alsa.snd_pcm_drop = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_drop"); - pContext->alsa.snd_pcm_drain = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_drain"); - pContext->alsa.snd_pcm_reset = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_reset"); - pContext->alsa.snd_device_name_hint = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_device_name_hint"); - pContext->alsa.snd_device_name_get_hint = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_device_name_get_hint"); - pContext->alsa.snd_card_get_index = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_card_get_index"); - pContext->alsa.snd_device_name_free_hint = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_device_name_free_hint"); - pContext->alsa.snd_pcm_mmap_begin = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_mmap_begin"); - pContext->alsa.snd_pcm_mmap_commit = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_mmap_commit"); - pContext->alsa.snd_pcm_recover = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_recover"); - pContext->alsa.snd_pcm_readi = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_readi"); - pContext->alsa.snd_pcm_writei = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_writei"); - pContext->alsa.snd_pcm_avail = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_avail"); - pContext->alsa.snd_pcm_avail_update = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_avail_update"); - pContext->alsa.snd_pcm_wait = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_wait"); - pContext->alsa.snd_pcm_nonblock = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_nonblock"); - pContext->alsa.snd_pcm_info = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_info"); - pContext->alsa.snd_pcm_info_sizeof = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_info_sizeof"); - pContext->alsa.snd_pcm_info_get_name = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_info_get_name"); - pContext->alsa.snd_pcm_poll_descriptors = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_poll_descriptors"); - pContext->alsa.snd_pcm_poll_descriptors_count = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_poll_descriptors_count"); - pContext->alsa.snd_pcm_poll_descriptors_revents = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_poll_descriptors_revents"); - pContext->alsa.snd_config_update_free_global = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_config_update_free_global"); -#else - /* The system below is just for type safety. */ - ma_snd_pcm_open_proc _snd_pcm_open = snd_pcm_open; - ma_snd_pcm_close_proc _snd_pcm_close = snd_pcm_close; - ma_snd_pcm_hw_params_sizeof_proc _snd_pcm_hw_params_sizeof = snd_pcm_hw_params_sizeof; - ma_snd_pcm_hw_params_any_proc _snd_pcm_hw_params_any = snd_pcm_hw_params_any; - ma_snd_pcm_hw_params_set_format_proc _snd_pcm_hw_params_set_format = snd_pcm_hw_params_set_format; - ma_snd_pcm_hw_params_set_format_first_proc _snd_pcm_hw_params_set_format_first = snd_pcm_hw_params_set_format_first; - ma_snd_pcm_hw_params_get_format_mask_proc _snd_pcm_hw_params_get_format_mask = snd_pcm_hw_params_get_format_mask; - ma_snd_pcm_hw_params_set_channels_proc _snd_pcm_hw_params_set_channels = snd_pcm_hw_params_set_channels; - ma_snd_pcm_hw_params_set_channels_near_proc _snd_pcm_hw_params_set_channels_near = snd_pcm_hw_params_set_channels_near; - ma_snd_pcm_hw_params_set_rate_resample_proc _snd_pcm_hw_params_set_rate_resample = snd_pcm_hw_params_set_rate_resample; - ma_snd_pcm_hw_params_set_rate_near _snd_pcm_hw_params_set_rate = snd_pcm_hw_params_set_rate; - ma_snd_pcm_hw_params_set_rate_near_proc _snd_pcm_hw_params_set_rate_near = snd_pcm_hw_params_set_rate_near; - ma_snd_pcm_hw_params_set_rate_minmax_proc _snd_pcm_hw_params_set_rate_minmax = snd_pcm_hw_params_set_rate_minmax; - ma_snd_pcm_hw_params_set_buffer_size_near_proc _snd_pcm_hw_params_set_buffer_size_near = snd_pcm_hw_params_set_buffer_size_near; - ma_snd_pcm_hw_params_set_periods_near_proc _snd_pcm_hw_params_set_periods_near = snd_pcm_hw_params_set_periods_near; - ma_snd_pcm_hw_params_set_access_proc _snd_pcm_hw_params_set_access = snd_pcm_hw_params_set_access; - ma_snd_pcm_hw_params_get_format_proc _snd_pcm_hw_params_get_format = snd_pcm_hw_params_get_format; - ma_snd_pcm_hw_params_get_channels_proc _snd_pcm_hw_params_get_channels = snd_pcm_hw_params_get_channels; - ma_snd_pcm_hw_params_get_channels_min_proc _snd_pcm_hw_params_get_channels_min = snd_pcm_hw_params_get_channels_min; - ma_snd_pcm_hw_params_get_channels_max_proc _snd_pcm_hw_params_get_channels_max = snd_pcm_hw_params_get_channels_max; - ma_snd_pcm_hw_params_get_rate_proc _snd_pcm_hw_params_get_rate = snd_pcm_hw_params_get_rate; - ma_snd_pcm_hw_params_get_rate_min_proc _snd_pcm_hw_params_get_rate_min = snd_pcm_hw_params_get_rate_min; - ma_snd_pcm_hw_params_get_rate_max_proc _snd_pcm_hw_params_get_rate_max = snd_pcm_hw_params_get_rate_max; - ma_snd_pcm_hw_params_get_buffer_size_proc _snd_pcm_hw_params_get_buffer_size = snd_pcm_hw_params_get_buffer_size; - ma_snd_pcm_hw_params_get_periods_proc _snd_pcm_hw_params_get_periods = snd_pcm_hw_params_get_periods; - ma_snd_pcm_hw_params_get_access_proc _snd_pcm_hw_params_get_access = snd_pcm_hw_params_get_access; - ma_snd_pcm_hw_params_test_format_proc _snd_pcm_hw_params_test_format = snd_pcm_hw_params_test_format; - ma_snd_pcm_hw_params_test_channels_proc _snd_pcm_hw_params_test_channels = snd_pcm_hw_params_test_channels; - ma_snd_pcm_hw_params_test_rate_proc _snd_pcm_hw_params_test_rate = snd_pcm_hw_params_test_rate; - ma_snd_pcm_hw_params_proc _snd_pcm_hw_params = snd_pcm_hw_params; - ma_snd_pcm_sw_params_sizeof_proc _snd_pcm_sw_params_sizeof = snd_pcm_sw_params_sizeof; - ma_snd_pcm_sw_params_current_proc _snd_pcm_sw_params_current = snd_pcm_sw_params_current; - ma_snd_pcm_sw_params_get_boundary_proc _snd_pcm_sw_params_get_boundary = snd_pcm_sw_params_get_boundary; - ma_snd_pcm_sw_params_set_avail_min_proc _snd_pcm_sw_params_set_avail_min = snd_pcm_sw_params_set_avail_min; - ma_snd_pcm_sw_params_set_start_threshold_proc _snd_pcm_sw_params_set_start_threshold = snd_pcm_sw_params_set_start_threshold; - ma_snd_pcm_sw_params_set_stop_threshold_proc _snd_pcm_sw_params_set_stop_threshold = snd_pcm_sw_params_set_stop_threshold; - ma_snd_pcm_sw_params_proc _snd_pcm_sw_params = snd_pcm_sw_params; - ma_snd_pcm_format_mask_sizeof_proc _snd_pcm_format_mask_sizeof = snd_pcm_format_mask_sizeof; - ma_snd_pcm_format_mask_test_proc _snd_pcm_format_mask_test = snd_pcm_format_mask_test; - ma_snd_pcm_get_chmap_proc _snd_pcm_get_chmap = snd_pcm_get_chmap; - ma_snd_pcm_state_proc _snd_pcm_state = snd_pcm_state; - ma_snd_pcm_prepare_proc _snd_pcm_prepare = snd_pcm_prepare; - ma_snd_pcm_start_proc _snd_pcm_start = snd_pcm_start; - ma_snd_pcm_drop_proc _snd_pcm_drop = snd_pcm_drop; - ma_snd_pcm_drain_proc _snd_pcm_drain = snd_pcm_drain; - ma_snd_pcm_reset_proc _snd_pcm_reset = snd_pcm_reset; - ma_snd_device_name_hint_proc _snd_device_name_hint = snd_device_name_hint; - ma_snd_device_name_get_hint_proc _snd_device_name_get_hint = snd_device_name_get_hint; - ma_snd_card_get_index_proc _snd_card_get_index = snd_card_get_index; - ma_snd_device_name_free_hint_proc _snd_device_name_free_hint = snd_device_name_free_hint; - ma_snd_pcm_mmap_begin_proc _snd_pcm_mmap_begin = snd_pcm_mmap_begin; - ma_snd_pcm_mmap_commit_proc _snd_pcm_mmap_commit = snd_pcm_mmap_commit; - ma_snd_pcm_recover_proc _snd_pcm_recover = snd_pcm_recover; - ma_snd_pcm_readi_proc _snd_pcm_readi = snd_pcm_readi; - ma_snd_pcm_writei_proc _snd_pcm_writei = snd_pcm_writei; - ma_snd_pcm_avail_proc _snd_pcm_avail = snd_pcm_avail; - ma_snd_pcm_avail_update_proc _snd_pcm_avail_update = snd_pcm_avail_update; - ma_snd_pcm_wait_proc _snd_pcm_wait = snd_pcm_wait; - ma_snd_pcm_nonblock_proc _snd_pcm_nonblock = snd_pcm_nonblock; - ma_snd_pcm_info_proc _snd_pcm_info = snd_pcm_info; - ma_snd_pcm_info_sizeof_proc _snd_pcm_info_sizeof = snd_pcm_info_sizeof; - ma_snd_pcm_info_get_name_proc _snd_pcm_info_get_name = snd_pcm_info_get_name; - ma_snd_pcm_poll_descriptors _snd_pcm_poll_descriptors = snd_pcm_poll_descriptors; - ma_snd_pcm_poll_descriptors_count _snd_pcm_poll_descriptors_count = snd_pcm_poll_descriptors_count; - ma_snd_pcm_poll_descriptors_revents _snd_pcm_poll_descriptors_revents = snd_pcm_poll_descriptors_revents; - ma_snd_config_update_free_global_proc _snd_config_update_free_global = snd_config_update_free_global; - - pContext->alsa.snd_pcm_open = (ma_proc)_snd_pcm_open; - pContext->alsa.snd_pcm_close = (ma_proc)_snd_pcm_close; - pContext->alsa.snd_pcm_hw_params_sizeof = (ma_proc)_snd_pcm_hw_params_sizeof; - pContext->alsa.snd_pcm_hw_params_any = (ma_proc)_snd_pcm_hw_params_any; - pContext->alsa.snd_pcm_hw_params_set_format = (ma_proc)_snd_pcm_hw_params_set_format; - pContext->alsa.snd_pcm_hw_params_set_format_first = (ma_proc)_snd_pcm_hw_params_set_format_first; - pContext->alsa.snd_pcm_hw_params_get_format_mask = (ma_proc)_snd_pcm_hw_params_get_format_mask; - pContext->alsa.snd_pcm_hw_params_set_channels = (ma_proc)_snd_pcm_hw_params_set_channels; - pContext->alsa.snd_pcm_hw_params_set_channels_near = (ma_proc)_snd_pcm_hw_params_set_channels_near; - pContext->alsa.snd_pcm_hw_params_set_channels_minmax = (ma_proc)_snd_pcm_hw_params_set_channels_minmax; - pContext->alsa.snd_pcm_hw_params_set_rate_resample = (ma_proc)_snd_pcm_hw_params_set_rate_resample; - pContext->alsa.snd_pcm_hw_params_set_rate = (ma_proc)_snd_pcm_hw_params_set_rate; - pContext->alsa.snd_pcm_hw_params_set_rate_near = (ma_proc)_snd_pcm_hw_params_set_rate_near; - pContext->alsa.snd_pcm_hw_params_set_buffer_size_near = (ma_proc)_snd_pcm_hw_params_set_buffer_size_near; - pContext->alsa.snd_pcm_hw_params_set_periods_near = (ma_proc)_snd_pcm_hw_params_set_periods_near; - pContext->alsa.snd_pcm_hw_params_set_access = (ma_proc)_snd_pcm_hw_params_set_access; - pContext->alsa.snd_pcm_hw_params_get_format = (ma_proc)_snd_pcm_hw_params_get_format; - pContext->alsa.snd_pcm_hw_params_get_channels = (ma_proc)_snd_pcm_hw_params_get_channels; - pContext->alsa.snd_pcm_hw_params_get_channels_min = (ma_proc)_snd_pcm_hw_params_get_channels_min; - pContext->alsa.snd_pcm_hw_params_get_channels_max = (ma_proc)_snd_pcm_hw_params_get_channels_max; - pContext->alsa.snd_pcm_hw_params_get_rate = (ma_proc)_snd_pcm_hw_params_get_rate; - pContext->alsa.snd_pcm_hw_params_get_rate_min = (ma_proc)_snd_pcm_hw_params_get_rate_min; - pContext->alsa.snd_pcm_hw_params_get_rate_max = (ma_proc)_snd_pcm_hw_params_get_rate_max; - pContext->alsa.snd_pcm_hw_params_get_buffer_size = (ma_proc)_snd_pcm_hw_params_get_buffer_size; - pContext->alsa.snd_pcm_hw_params_get_periods = (ma_proc)_snd_pcm_hw_params_get_periods; - pContext->alsa.snd_pcm_hw_params_get_access = (ma_proc)_snd_pcm_hw_params_get_access; - pContext->alsa.snd_pcm_hw_params_test_format = (ma_proc)_snd_pcm_hw_params_test_format; - pContext->alsa.snd_pcm_hw_params_test_channels = (ma_proc)_snd_pcm_hw_params_test_channels; - pContext->alsa.snd_pcm_hw_params_test_rate = (ma_proc)_snd_pcm_hw_params_test_rate; - pContext->alsa.snd_pcm_hw_params = (ma_proc)_snd_pcm_hw_params; - pContext->alsa.snd_pcm_sw_params_sizeof = (ma_proc)_snd_pcm_sw_params_sizeof; - pContext->alsa.snd_pcm_sw_params_current = (ma_proc)_snd_pcm_sw_params_current; - pContext->alsa.snd_pcm_sw_params_get_boundary = (ma_proc)_snd_pcm_sw_params_get_boundary; - pContext->alsa.snd_pcm_sw_params_set_avail_min = (ma_proc)_snd_pcm_sw_params_set_avail_min; - pContext->alsa.snd_pcm_sw_params_set_start_threshold = (ma_proc)_snd_pcm_sw_params_set_start_threshold; - pContext->alsa.snd_pcm_sw_params_set_stop_threshold = (ma_proc)_snd_pcm_sw_params_set_stop_threshold; - pContext->alsa.snd_pcm_sw_params = (ma_proc)_snd_pcm_sw_params; - pContext->alsa.snd_pcm_format_mask_sizeof = (ma_proc)_snd_pcm_format_mask_sizeof; - pContext->alsa.snd_pcm_format_mask_test = (ma_proc)_snd_pcm_format_mask_test; - pContext->alsa.snd_pcm_get_chmap = (ma_proc)_snd_pcm_get_chmap; - pContext->alsa.snd_pcm_state = (ma_proc)_snd_pcm_state; - pContext->alsa.snd_pcm_prepare = (ma_proc)_snd_pcm_prepare; - pContext->alsa.snd_pcm_start = (ma_proc)_snd_pcm_start; - pContext->alsa.snd_pcm_drop = (ma_proc)_snd_pcm_drop; - pContext->alsa.snd_pcm_drain = (ma_proc)_snd_pcm_drain; - pContext->alsa.snd_pcm_reset = (ma_proc)_snd_pcm_reset; - pContext->alsa.snd_device_name_hint = (ma_proc)_snd_device_name_hint; - pContext->alsa.snd_device_name_get_hint = (ma_proc)_snd_device_name_get_hint; - pContext->alsa.snd_card_get_index = (ma_proc)_snd_card_get_index; - pContext->alsa.snd_device_name_free_hint = (ma_proc)_snd_device_name_free_hint; - pContext->alsa.snd_pcm_mmap_begin = (ma_proc)_snd_pcm_mmap_begin; - pContext->alsa.snd_pcm_mmap_commit = (ma_proc)_snd_pcm_mmap_commit; - pContext->alsa.snd_pcm_recover = (ma_proc)_snd_pcm_recover; - pContext->alsa.snd_pcm_readi = (ma_proc)_snd_pcm_readi; - pContext->alsa.snd_pcm_writei = (ma_proc)_snd_pcm_writei; - pContext->alsa.snd_pcm_avail = (ma_proc)_snd_pcm_avail; - pContext->alsa.snd_pcm_avail_update = (ma_proc)_snd_pcm_avail_update; - pContext->alsa.snd_pcm_wait = (ma_proc)_snd_pcm_wait; - pContext->alsa.snd_pcm_nonblock = (ma_proc)_snd_pcm_nonblock; - pContext->alsa.snd_pcm_info = (ma_proc)_snd_pcm_info; - pContext->alsa.snd_pcm_info_sizeof = (ma_proc)_snd_pcm_info_sizeof; - pContext->alsa.snd_pcm_info_get_name = (ma_proc)_snd_pcm_info_get_name; - pContext->alsa.snd_pcm_poll_descriptors = (ma_proc)_snd_pcm_poll_descriptors; - pContext->alsa.snd_pcm_poll_descriptors_count = (ma_proc)_snd_pcm_poll_descriptors_count; - pContext->alsa.snd_pcm_poll_descriptors_revents = (ma_proc)_snd_pcm_poll_descriptors_revents; - pContext->alsa.snd_config_update_free_global = (ma_proc)_snd_config_update_free_global; -#endif - - pContext->alsa.useVerboseDeviceEnumeration = pConfig->alsa.useVerboseDeviceEnumeration; - - result = ma_mutex_init(&pContext->alsa.internalDeviceEnumLock); - if (result != MA_SUCCESS) { - ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[ALSA] WARNING: Failed to initialize mutex for internal device enumeration."); - return result; - } - - pCallbacks->onContextInit = ma_context_init__alsa; - pCallbacks->onContextUninit = ma_context_uninit__alsa; - pCallbacks->onContextEnumerateDevices = ma_context_enumerate_devices__alsa; - pCallbacks->onContextGetDeviceInfo = ma_context_get_device_info__alsa; - pCallbacks->onDeviceInit = ma_device_init__alsa; - pCallbacks->onDeviceUninit = ma_device_uninit__alsa; - pCallbacks->onDeviceStart = ma_device_start__alsa; - pCallbacks->onDeviceStop = ma_device_stop__alsa; - pCallbacks->onDeviceRead = ma_device_read__alsa; - pCallbacks->onDeviceWrite = ma_device_write__alsa; - pCallbacks->onDeviceDataLoop = NULL; - pCallbacks->onDeviceDataLoopWakeup = ma_device_data_loop_wakeup__alsa; - - return MA_SUCCESS; -} -#endif /* MA_HAS_ALSA */ - - - -/****************************************************************************** - -PulseAudio Backend - -******************************************************************************/ -#ifdef MA_HAS_PULSEAUDIO -/* -The PulseAudio API, along with Apple's Core Audio, is the worst of the mainstream audio APIs. This is a brief description of what's going on -in the PulseAudio backend. I apologize if this gets a bit ranty for your liking - you might want to skip this discussion. - -PulseAudio has something they call the "Simple API", which unfortunately isn't suitable for miniaudio. I've not seen anywhere where it -allows you to enumerate over devices, nor does it seem to support the ability to stop and start streams. Looking at the documentation, it -appears as though the stream is constantly running and you prevent sound from being emitted or captured by simply not calling the read or -write functions. This is not a professional solution as it would be much better to *actually* stop the underlying stream. Perhaps the -simple API has some smarts to do this automatically, but I'm not sure. Another limitation with the simple API is that it seems inefficient -when you want to have multiple streams to a single context. For these reasons, miniaudio is not using the simple API. - -Since we're not using the simple API, we're left with the asynchronous API as our only other option. And boy, is this where it starts to -get fun, and I don't mean that in a good way... - -The problems start with the very name of the API - "asynchronous". Yes, this is an asynchronous oriented API which means your commands -don't immediately take effect. You instead need to issue your commands, and then wait for them to complete. The waiting mechanism is -enabled through the use of a "main loop". In the asynchronous API you cannot get away from the main loop, and the main loop is where almost -all of PulseAudio's problems stem from. - -When you first initialize PulseAudio you need an object referred to as "main loop". You can implement this yourself by defining your own -vtable, but it's much easier to just use one of the built-in main loop implementations. There's two generic implementations called -pa_mainloop and pa_threaded_mainloop, and another implementation specific to GLib called pa_glib_mainloop. We're using pa_threaded_mainloop -because it simplifies management of the worker thread. The idea of the main loop object is pretty self explanatory - you're supposed to use -it to implement a worker thread which runs in a loop. The main loop is where operations are actually executed. - -To initialize the main loop, you just use `pa_threaded_mainloop_new()`. This is the first function you'll call. You can then get a pointer -to the vtable with `pa_threaded_mainloop_get_api()` (the main loop vtable is called `pa_mainloop_api`). Again, you can bypass the threaded -main loop object entirely and just implement `pa_mainloop_api` directly, but there's no need for it unless you're doing something extremely -specialized such as if you want to integrate it into your application's existing main loop infrastructure. - -(EDIT 2021-01-26: miniaudio is no longer using `pa_threaded_mainloop` due to this issue: https://github.com/mackron/miniaudio/issues/262. -It is now using `pa_mainloop` which turns out to be a simpler solution anyway. The rest of this rant still applies, however.) - -Once you have your main loop vtable (the `pa_mainloop_api` object) you can create the PulseAudio context. This is very similar to -miniaudio's context and they map to each other quite well. You have one context to many streams, which is basically the same as miniaudio's -one `ma_context` to many `ma_device`s. Here's where it starts to get annoying, however. When you first create the PulseAudio context, which -is done with `pa_context_new()`, it's not actually connected to anything. When you connect, you call `pa_context_connect()`. However, if -you remember, PulseAudio is an asynchronous API. That means you cannot just assume the context is connected after `pa_context_context()` -has returned. You instead need to wait for it to connect. To do this, you need to either wait for a callback to get fired, which you can -set with `pa_context_set_state_callback()`, or you can continuously poll the context's state. Either way, you need to run this in a loop. -All objects from here out are created from the context, and, I believe, you can't be creating these objects until the context is connected. -This waiting loop is therefore unavoidable. In order for the waiting to ever complete, however, the main loop needs to be running. Before -attempting to connect the context, the main loop needs to be started with `pa_threaded_mainloop_start()`. - -The reason for this asynchronous design is to support cases where you're connecting to a remote server, say through a local network or an -internet connection. However, the *VAST* majority of cases don't involve this at all - they just connect to a local "server" running on the -host machine. The fact that this would be the default rather than making `pa_context_connect()` synchronous tends to boggle the mind. - -Once the context has been created and connected you can start creating a stream. A PulseAudio stream is analogous to miniaudio's device. -The initialization of a stream is fairly standard - you configure some attributes (analogous to miniaudio's device config) and then call -`pa_stream_new()` to actually create it. Here is where we start to get into "operations". When configuring the stream, you can get -information about the source (such as sample format, sample rate, etc.), however it's not synchronous. Instead, a `pa_operation` object -is returned from `pa_context_get_source_info_by_name()` (capture) or `pa_context_get_sink_info_by_name()` (playback). Then, you need to -run a loop (again!) to wait for the operation to complete which you can determine via a callback or polling, just like we did with the -context. Then, as an added bonus, you need to decrement the reference counter of the `pa_operation` object to ensure memory is cleaned up. -All of that just to retrieve basic information about a device! - -Once the basic information about the device has been retrieved, miniaudio can now create the stream with `ma_stream_new()`. Like the -context, this needs to be connected. But we need to be careful here, because we're now about to introduce one of the most horrific design -choices in PulseAudio. - -PulseAudio allows you to specify a callback that is fired when data can be written to or read from a stream. The language is important here -because PulseAudio takes it literally, specifically the "can be". You would think these callbacks would be appropriate as the place for -writing and reading data to and from the stream, and that would be right, except when it's not. When you initialize the stream, you can -set a flag that tells PulseAudio to not start the stream automatically. This is required because miniaudio does not auto-start devices -straight after initialization - you need to call `ma_device_start()` manually. The problem is that even when this flag is specified, -PulseAudio will immediately fire its write or read callback. This is *technically* correct (based on the wording in the documentation) -because indeed, data *can* be written at this point. The problem is that it's not *practical*. It makes sense that the write/read callback -would be where a program will want to write or read data to or from the stream, but when it's called before the application has even -requested that the stream be started, it's just not practical because the program probably isn't ready for any kind of data delivery at -that point (it may still need to load files or whatnot). Instead, this callback should only be fired when the application requests the -stream be started which is how it works with literally *every* other callback-based audio API. Since miniaudio forbids firing of the data -callback until the device has been started (as it should be with *all* callback based APIs), logic needs to be added to ensure miniaudio -doesn't just blindly fire the application-defined data callback from within the PulseAudio callback before the stream has actually been -started. The device state is used for this - if the state is anything other than `ma_device_state_starting` or `ma_device_state_started`, the main data -callback is not fired. - -This, unfortunately, is not the end of the problems with the PulseAudio write callback. Any normal callback based audio API will -continuously fire the callback at regular intervals based on the size of the internal buffer. This will only ever be fired when the device -is running, and will be fired regardless of whether or not the user actually wrote anything to the device/stream. This not the case in -PulseAudio. In PulseAudio, the data callback will *only* be called if you wrote something to it previously. That means, if you don't call -`pa_stream_write()`, the callback will not get fired. On the surface you wouldn't think this would matter because you should be always -writing data, and if you don't have anything to write, just write silence. That's fine until you want to drain the stream. You see, if -you're continuously writing data to the stream, the stream will never get drained! That means in order to drain the stream, you need to -*not* write data to it! But remember, when you don't write data to the stream, the callback won't get fired again! Why is draining -important? Because that's how we've defined stopping to work in miniaudio. In miniaudio, stopping the device requires it to be drained -before returning from ma_device_stop(). So we've stopped the device, which requires us to drain, but draining requires us to *not* write -data to the stream (or else it won't ever complete draining), but not writing to the stream means the callback won't get fired again! - -This becomes a problem when stopping and then restarting the device. When the device is stopped, it's drained, which requires us to *not* -write anything to the stream. But then, since we didn't write anything to it, the write callback will *never* get called again if we just -resume the stream naively. This means that starting the stream requires us to write data to the stream from outside the callback. This -disconnect is something PulseAudio has got seriously wrong - there should only ever be a single source of data delivery, that being the -callback. (I have tried using `pa_stream_flush()` to trigger the write callback to fire, but this just doesn't work for some reason.) - -Once you've created the stream, you need to connect it which involves the whole waiting procedure. This is the same process as the context, -only this time you'll poll for the state with `pa_stream_get_status()`. The starting and stopping of a streaming is referred to as -"corking" in PulseAudio. The analogy is corking a barrel. To start the stream, you uncork it, to stop it you cork it. Personally I think -it's silly - why would you not just call it "starting" and "stopping" like any other normal audio API? Anyway, the act of corking is, you -guessed it, asynchronous. This means you'll need our waiting loop as usual. Again, why this asynchronous design is the default is -absolutely beyond me. Would it really be that hard to just make it run synchronously? - -Teardown is pretty simple (what?!). It's just a matter of calling the relevant `_unref()` function on each object in reverse order that -they were initialized in. - -That's about it from the PulseAudio side. A bit ranty, I know, but they really need to fix that main loop and callback system. They're -embarrassingly unpractical. The main loop thing is an easy fix - have synchronous versions of all APIs. If an application wants these to -run asynchronously, they can execute them in a separate thread themselves. The desire to run these asynchronously is such a niche -requirement - it makes no sense to make it the default. The stream write callback needs to be change, or an alternative provided, that is -constantly fired, regardless of whether or not `pa_stream_write()` has been called, and it needs to take a pointer to a buffer as a -parameter which the program just writes to directly rather than having to call `pa_stream_writable_size()` and `pa_stream_write()`. These -changes alone will change PulseAudio from one of the worst audio APIs to one of the best. -*/ - - -/* -It is assumed pulseaudio.h is available when linking at compile time. When linking at compile time, we use the declarations in the header -to check for type safety. We cannot do this when linking at run time because the header might not be available. -*/ -#ifdef MA_NO_RUNTIME_LINKING - -/* pulseaudio.h marks some functions with "inline" which isn't always supported. Need to emulate it. */ -#if !defined(__cplusplus) - #if defined(__STRICT_ANSI__) - #if !defined(inline) - #define inline __inline__ __attribute__((always_inline)) - #define MA_INLINE_DEFINED - #endif - #endif -#endif -#include -#if defined(MA_INLINE_DEFINED) - #undef inline - #undef MA_INLINE_DEFINED -#endif - -#define MA_PA_OK PA_OK -#define MA_PA_ERR_ACCESS PA_ERR_ACCESS -#define MA_PA_ERR_INVALID PA_ERR_INVALID -#define MA_PA_ERR_NOENTITY PA_ERR_NOENTITY -#define MA_PA_ERR_NOTSUPPORTED PA_ERR_NOTSUPPORTED - -#define MA_PA_CHANNELS_MAX PA_CHANNELS_MAX -#define MA_PA_RATE_MAX PA_RATE_MAX - -typedef pa_context_flags_t ma_pa_context_flags_t; -#define MA_PA_CONTEXT_NOFLAGS PA_CONTEXT_NOFLAGS -#define MA_PA_CONTEXT_NOAUTOSPAWN PA_CONTEXT_NOAUTOSPAWN -#define MA_PA_CONTEXT_NOFAIL PA_CONTEXT_NOFAIL - -typedef pa_stream_flags_t ma_pa_stream_flags_t; -#define MA_PA_STREAM_NOFLAGS PA_STREAM_NOFLAGS -#define MA_PA_STREAM_START_CORKED PA_STREAM_START_CORKED -#define MA_PA_STREAM_INTERPOLATE_TIMING PA_STREAM_INTERPOLATE_TIMING -#define MA_PA_STREAM_NOT_MONOTONIC PA_STREAM_NOT_MONOTONIC -#define MA_PA_STREAM_AUTO_TIMING_UPDATE PA_STREAM_AUTO_TIMING_UPDATE -#define MA_PA_STREAM_NO_REMAP_CHANNELS PA_STREAM_NO_REMAP_CHANNELS -#define MA_PA_STREAM_NO_REMIX_CHANNELS PA_STREAM_NO_REMIX_CHANNELS -#define MA_PA_STREAM_FIX_FORMAT PA_STREAM_FIX_FORMAT -#define MA_PA_STREAM_FIX_RATE PA_STREAM_FIX_RATE -#define MA_PA_STREAM_FIX_CHANNELS PA_STREAM_FIX_CHANNELS -#define MA_PA_STREAM_DONT_MOVE PA_STREAM_DONT_MOVE -#define MA_PA_STREAM_VARIABLE_RATE PA_STREAM_VARIABLE_RATE -#define MA_PA_STREAM_PEAK_DETECT PA_STREAM_PEAK_DETECT -#define MA_PA_STREAM_START_MUTED PA_STREAM_START_MUTED -#define MA_PA_STREAM_ADJUST_LATENCY PA_STREAM_ADJUST_LATENCY -#define MA_PA_STREAM_EARLY_REQUESTS PA_STREAM_EARLY_REQUESTS -#define MA_PA_STREAM_DONT_INHIBIT_AUTO_SUSPEND PA_STREAM_DONT_INHIBIT_AUTO_SUSPEND -#define MA_PA_STREAM_START_UNMUTED PA_STREAM_START_UNMUTED -#define MA_PA_STREAM_FAIL_ON_SUSPEND PA_STREAM_FAIL_ON_SUSPEND -#define MA_PA_STREAM_RELATIVE_VOLUME PA_STREAM_RELATIVE_VOLUME -#define MA_PA_STREAM_PASSTHROUGH PA_STREAM_PASSTHROUGH - -typedef pa_sink_flags_t ma_pa_sink_flags_t; -#define MA_PA_SINK_NOFLAGS PA_SINK_NOFLAGS -#define MA_PA_SINK_HW_VOLUME_CTRL PA_SINK_HW_VOLUME_CTRL -#define MA_PA_SINK_LATENCY PA_SINK_LATENCY -#define MA_PA_SINK_HARDWARE PA_SINK_HARDWARE -#define MA_PA_SINK_NETWORK PA_SINK_NETWORK -#define MA_PA_SINK_HW_MUTE_CTRL PA_SINK_HW_MUTE_CTRL -#define MA_PA_SINK_DECIBEL_VOLUME PA_SINK_DECIBEL_VOLUME -#define MA_PA_SINK_FLAT_VOLUME PA_SINK_FLAT_VOLUME -#define MA_PA_SINK_DYNAMIC_LATENCY PA_SINK_DYNAMIC_LATENCY -#define MA_PA_SINK_SET_FORMATS PA_SINK_SET_FORMATS - -typedef pa_source_flags_t ma_pa_source_flags_t; -#define MA_PA_SOURCE_NOFLAGS PA_SOURCE_NOFLAGS -#define MA_PA_SOURCE_HW_VOLUME_CTRL PA_SOURCE_HW_VOLUME_CTRL -#define MA_PA_SOURCE_LATENCY PA_SOURCE_LATENCY -#define MA_PA_SOURCE_HARDWARE PA_SOURCE_HARDWARE -#define MA_PA_SOURCE_NETWORK PA_SOURCE_NETWORK -#define MA_PA_SOURCE_HW_MUTE_CTRL PA_SOURCE_HW_MUTE_CTRL -#define MA_PA_SOURCE_DECIBEL_VOLUME PA_SOURCE_DECIBEL_VOLUME -#define MA_PA_SOURCE_DYNAMIC_LATENCY PA_SOURCE_DYNAMIC_LATENCY -#define MA_PA_SOURCE_FLAT_VOLUME PA_SOURCE_FLAT_VOLUME - -typedef pa_context_state_t ma_pa_context_state_t; -#define MA_PA_CONTEXT_UNCONNECTED PA_CONTEXT_UNCONNECTED -#define MA_PA_CONTEXT_CONNECTING PA_CONTEXT_CONNECTING -#define MA_PA_CONTEXT_AUTHORIZING PA_CONTEXT_AUTHORIZING -#define MA_PA_CONTEXT_SETTING_NAME PA_CONTEXT_SETTING_NAME -#define MA_PA_CONTEXT_READY PA_CONTEXT_READY -#define MA_PA_CONTEXT_FAILED PA_CONTEXT_FAILED -#define MA_PA_CONTEXT_TERMINATED PA_CONTEXT_TERMINATED - -typedef pa_stream_state_t ma_pa_stream_state_t; -#define MA_PA_STREAM_UNCONNECTED PA_STREAM_UNCONNECTED -#define MA_PA_STREAM_CREATING PA_STREAM_CREATING -#define MA_PA_STREAM_READY PA_STREAM_READY -#define MA_PA_STREAM_FAILED PA_STREAM_FAILED -#define MA_PA_STREAM_TERMINATED PA_STREAM_TERMINATED - -typedef pa_operation_state_t ma_pa_operation_state_t; -#define MA_PA_OPERATION_RUNNING PA_OPERATION_RUNNING -#define MA_PA_OPERATION_DONE PA_OPERATION_DONE -#define MA_PA_OPERATION_CANCELLED PA_OPERATION_CANCELLED - -typedef pa_sink_state_t ma_pa_sink_state_t; -#define MA_PA_SINK_INVALID_STATE PA_SINK_INVALID_STATE -#define MA_PA_SINK_RUNNING PA_SINK_RUNNING -#define MA_PA_SINK_IDLE PA_SINK_IDLE -#define MA_PA_SINK_SUSPENDED PA_SINK_SUSPENDED - -typedef pa_source_state_t ma_pa_source_state_t; -#define MA_PA_SOURCE_INVALID_STATE PA_SOURCE_INVALID_STATE -#define MA_PA_SOURCE_RUNNING PA_SOURCE_RUNNING -#define MA_PA_SOURCE_IDLE PA_SOURCE_IDLE -#define MA_PA_SOURCE_SUSPENDED PA_SOURCE_SUSPENDED - -typedef pa_seek_mode_t ma_pa_seek_mode_t; -#define MA_PA_SEEK_RELATIVE PA_SEEK_RELATIVE -#define MA_PA_SEEK_ABSOLUTE PA_SEEK_ABSOLUTE -#define MA_PA_SEEK_RELATIVE_ON_READ PA_SEEK_RELATIVE_ON_READ -#define MA_PA_SEEK_RELATIVE_END PA_SEEK_RELATIVE_END - -typedef pa_channel_position_t ma_pa_channel_position_t; -#define MA_PA_CHANNEL_POSITION_INVALID PA_CHANNEL_POSITION_INVALID -#define MA_PA_CHANNEL_POSITION_MONO PA_CHANNEL_POSITION_MONO -#define MA_PA_CHANNEL_POSITION_FRONT_LEFT PA_CHANNEL_POSITION_FRONT_LEFT -#define MA_PA_CHANNEL_POSITION_FRONT_RIGHT PA_CHANNEL_POSITION_FRONT_RIGHT -#define MA_PA_CHANNEL_POSITION_FRONT_CENTER PA_CHANNEL_POSITION_FRONT_CENTER -#define MA_PA_CHANNEL_POSITION_REAR_CENTER PA_CHANNEL_POSITION_REAR_CENTER -#define MA_PA_CHANNEL_POSITION_REAR_LEFT PA_CHANNEL_POSITION_REAR_LEFT -#define MA_PA_CHANNEL_POSITION_REAR_RIGHT PA_CHANNEL_POSITION_REAR_RIGHT -#define MA_PA_CHANNEL_POSITION_LFE PA_CHANNEL_POSITION_LFE -#define MA_PA_CHANNEL_POSITION_FRONT_LEFT_OF_CENTER PA_CHANNEL_POSITION_FRONT_LEFT_OF_CENTER -#define MA_PA_CHANNEL_POSITION_FRONT_RIGHT_OF_CENTER PA_CHANNEL_POSITION_FRONT_RIGHT_OF_CENTER -#define MA_PA_CHANNEL_POSITION_SIDE_LEFT PA_CHANNEL_POSITION_SIDE_LEFT -#define MA_PA_CHANNEL_POSITION_SIDE_RIGHT PA_CHANNEL_POSITION_SIDE_RIGHT -#define MA_PA_CHANNEL_POSITION_AUX0 PA_CHANNEL_POSITION_AUX0 -#define MA_PA_CHANNEL_POSITION_AUX1 PA_CHANNEL_POSITION_AUX1 -#define MA_PA_CHANNEL_POSITION_AUX2 PA_CHANNEL_POSITION_AUX2 -#define MA_PA_CHANNEL_POSITION_AUX3 PA_CHANNEL_POSITION_AUX3 -#define MA_PA_CHANNEL_POSITION_AUX4 PA_CHANNEL_POSITION_AUX4 -#define MA_PA_CHANNEL_POSITION_AUX5 PA_CHANNEL_POSITION_AUX5 -#define MA_PA_CHANNEL_POSITION_AUX6 PA_CHANNEL_POSITION_AUX6 -#define MA_PA_CHANNEL_POSITION_AUX7 PA_CHANNEL_POSITION_AUX7 -#define MA_PA_CHANNEL_POSITION_AUX8 PA_CHANNEL_POSITION_AUX8 -#define MA_PA_CHANNEL_POSITION_AUX9 PA_CHANNEL_POSITION_AUX9 -#define MA_PA_CHANNEL_POSITION_AUX10 PA_CHANNEL_POSITION_AUX10 -#define MA_PA_CHANNEL_POSITION_AUX11 PA_CHANNEL_POSITION_AUX11 -#define MA_PA_CHANNEL_POSITION_AUX12 PA_CHANNEL_POSITION_AUX12 -#define MA_PA_CHANNEL_POSITION_AUX13 PA_CHANNEL_POSITION_AUX13 -#define MA_PA_CHANNEL_POSITION_AUX14 PA_CHANNEL_POSITION_AUX14 -#define MA_PA_CHANNEL_POSITION_AUX15 PA_CHANNEL_POSITION_AUX15 -#define MA_PA_CHANNEL_POSITION_AUX16 PA_CHANNEL_POSITION_AUX16 -#define MA_PA_CHANNEL_POSITION_AUX17 PA_CHANNEL_POSITION_AUX17 -#define MA_PA_CHANNEL_POSITION_AUX18 PA_CHANNEL_POSITION_AUX18 -#define MA_PA_CHANNEL_POSITION_AUX19 PA_CHANNEL_POSITION_AUX19 -#define MA_PA_CHANNEL_POSITION_AUX20 PA_CHANNEL_POSITION_AUX20 -#define MA_PA_CHANNEL_POSITION_AUX21 PA_CHANNEL_POSITION_AUX21 -#define MA_PA_CHANNEL_POSITION_AUX22 PA_CHANNEL_POSITION_AUX22 -#define MA_PA_CHANNEL_POSITION_AUX23 PA_CHANNEL_POSITION_AUX23 -#define MA_PA_CHANNEL_POSITION_AUX24 PA_CHANNEL_POSITION_AUX24 -#define MA_PA_CHANNEL_POSITION_AUX25 PA_CHANNEL_POSITION_AUX25 -#define MA_PA_CHANNEL_POSITION_AUX26 PA_CHANNEL_POSITION_AUX26 -#define MA_PA_CHANNEL_POSITION_AUX27 PA_CHANNEL_POSITION_AUX27 -#define MA_PA_CHANNEL_POSITION_AUX28 PA_CHANNEL_POSITION_AUX28 -#define MA_PA_CHANNEL_POSITION_AUX29 PA_CHANNEL_POSITION_AUX29 -#define MA_PA_CHANNEL_POSITION_AUX30 PA_CHANNEL_POSITION_AUX30 -#define MA_PA_CHANNEL_POSITION_AUX31 PA_CHANNEL_POSITION_AUX31 -#define MA_PA_CHANNEL_POSITION_TOP_CENTER PA_CHANNEL_POSITION_TOP_CENTER -#define MA_PA_CHANNEL_POSITION_TOP_FRONT_LEFT PA_CHANNEL_POSITION_TOP_FRONT_LEFT -#define MA_PA_CHANNEL_POSITION_TOP_FRONT_RIGHT PA_CHANNEL_POSITION_TOP_FRONT_RIGHT -#define MA_PA_CHANNEL_POSITION_TOP_FRONT_CENTER PA_CHANNEL_POSITION_TOP_FRONT_CENTER -#define MA_PA_CHANNEL_POSITION_TOP_REAR_LEFT PA_CHANNEL_POSITION_TOP_REAR_LEFT -#define MA_PA_CHANNEL_POSITION_TOP_REAR_RIGHT PA_CHANNEL_POSITION_TOP_REAR_RIGHT -#define MA_PA_CHANNEL_POSITION_TOP_REAR_CENTER PA_CHANNEL_POSITION_TOP_REAR_CENTER -#define MA_PA_CHANNEL_POSITION_LEFT PA_CHANNEL_POSITION_LEFT -#define MA_PA_CHANNEL_POSITION_RIGHT PA_CHANNEL_POSITION_RIGHT -#define MA_PA_CHANNEL_POSITION_CENTER PA_CHANNEL_POSITION_CENTER -#define MA_PA_CHANNEL_POSITION_SUBWOOFER PA_CHANNEL_POSITION_SUBWOOFER - -typedef pa_channel_map_def_t ma_pa_channel_map_def_t; -#define MA_PA_CHANNEL_MAP_AIFF PA_CHANNEL_MAP_AIFF -#define MA_PA_CHANNEL_MAP_ALSA PA_CHANNEL_MAP_ALSA -#define MA_PA_CHANNEL_MAP_AUX PA_CHANNEL_MAP_AUX -#define MA_PA_CHANNEL_MAP_WAVEEX PA_CHANNEL_MAP_WAVEEX -#define MA_PA_CHANNEL_MAP_OSS PA_CHANNEL_MAP_OSS -#define MA_PA_CHANNEL_MAP_DEFAULT PA_CHANNEL_MAP_DEFAULT - -typedef pa_sample_format_t ma_pa_sample_format_t; -#define MA_PA_SAMPLE_INVALID PA_SAMPLE_INVALID -#define MA_PA_SAMPLE_U8 PA_SAMPLE_U8 -#define MA_PA_SAMPLE_ALAW PA_SAMPLE_ALAW -#define MA_PA_SAMPLE_ULAW PA_SAMPLE_ULAW -#define MA_PA_SAMPLE_S16LE PA_SAMPLE_S16LE -#define MA_PA_SAMPLE_S16BE PA_SAMPLE_S16BE -#define MA_PA_SAMPLE_FLOAT32LE PA_SAMPLE_FLOAT32LE -#define MA_PA_SAMPLE_FLOAT32BE PA_SAMPLE_FLOAT32BE -#define MA_PA_SAMPLE_S32LE PA_SAMPLE_S32LE -#define MA_PA_SAMPLE_S32BE PA_SAMPLE_S32BE -#define MA_PA_SAMPLE_S24LE PA_SAMPLE_S24LE -#define MA_PA_SAMPLE_S24BE PA_SAMPLE_S24BE -#define MA_PA_SAMPLE_S24_32LE PA_SAMPLE_S24_32LE -#define MA_PA_SAMPLE_S24_32BE PA_SAMPLE_S24_32BE - -typedef pa_mainloop ma_pa_mainloop; -typedef pa_threaded_mainloop ma_pa_threaded_mainloop; -typedef pa_mainloop_api ma_pa_mainloop_api; -typedef pa_context ma_pa_context; -typedef pa_operation ma_pa_operation; -typedef pa_stream ma_pa_stream; -typedef pa_spawn_api ma_pa_spawn_api; -typedef pa_buffer_attr ma_pa_buffer_attr; -typedef pa_channel_map ma_pa_channel_map; -typedef pa_cvolume ma_pa_cvolume; -typedef pa_sample_spec ma_pa_sample_spec; -typedef pa_sink_info ma_pa_sink_info; -typedef pa_source_info ma_pa_source_info; - -typedef pa_context_notify_cb_t ma_pa_context_notify_cb_t; -typedef pa_sink_info_cb_t ma_pa_sink_info_cb_t; -typedef pa_source_info_cb_t ma_pa_source_info_cb_t; -typedef pa_stream_success_cb_t ma_pa_stream_success_cb_t; -typedef pa_stream_request_cb_t ma_pa_stream_request_cb_t; -typedef pa_stream_notify_cb_t ma_pa_stream_notify_cb_t; -typedef pa_free_cb_t ma_pa_free_cb_t; -#else -#define MA_PA_OK 0 -#define MA_PA_ERR_ACCESS 1 -#define MA_PA_ERR_INVALID 2 -#define MA_PA_ERR_NOENTITY 5 -#define MA_PA_ERR_NOTSUPPORTED 19 - -#define MA_PA_CHANNELS_MAX 32 -#define MA_PA_RATE_MAX 384000 - -typedef int ma_pa_context_flags_t; -#define MA_PA_CONTEXT_NOFLAGS 0x00000000 -#define MA_PA_CONTEXT_NOAUTOSPAWN 0x00000001 -#define MA_PA_CONTEXT_NOFAIL 0x00000002 - -typedef int ma_pa_stream_flags_t; -#define MA_PA_STREAM_NOFLAGS 0x00000000 -#define MA_PA_STREAM_START_CORKED 0x00000001 -#define MA_PA_STREAM_INTERPOLATE_TIMING 0x00000002 -#define MA_PA_STREAM_NOT_MONOTONIC 0x00000004 -#define MA_PA_STREAM_AUTO_TIMING_UPDATE 0x00000008 -#define MA_PA_STREAM_NO_REMAP_CHANNELS 0x00000010 -#define MA_PA_STREAM_NO_REMIX_CHANNELS 0x00000020 -#define MA_PA_STREAM_FIX_FORMAT 0x00000040 -#define MA_PA_STREAM_FIX_RATE 0x00000080 -#define MA_PA_STREAM_FIX_CHANNELS 0x00000100 -#define MA_PA_STREAM_DONT_MOVE 0x00000200 -#define MA_PA_STREAM_VARIABLE_RATE 0x00000400 -#define MA_PA_STREAM_PEAK_DETECT 0x00000800 -#define MA_PA_STREAM_START_MUTED 0x00001000 -#define MA_PA_STREAM_ADJUST_LATENCY 0x00002000 -#define MA_PA_STREAM_EARLY_REQUESTS 0x00004000 -#define MA_PA_STREAM_DONT_INHIBIT_AUTO_SUSPEND 0x00008000 -#define MA_PA_STREAM_START_UNMUTED 0x00010000 -#define MA_PA_STREAM_FAIL_ON_SUSPEND 0x00020000 -#define MA_PA_STREAM_RELATIVE_VOLUME 0x00040000 -#define MA_PA_STREAM_PASSTHROUGH 0x00080000 - -typedef int ma_pa_sink_flags_t; -#define MA_PA_SINK_NOFLAGS 0x00000000 -#define MA_PA_SINK_HW_VOLUME_CTRL 0x00000001 -#define MA_PA_SINK_LATENCY 0x00000002 -#define MA_PA_SINK_HARDWARE 0x00000004 -#define MA_PA_SINK_NETWORK 0x00000008 -#define MA_PA_SINK_HW_MUTE_CTRL 0x00000010 -#define MA_PA_SINK_DECIBEL_VOLUME 0x00000020 -#define MA_PA_SINK_FLAT_VOLUME 0x00000040 -#define MA_PA_SINK_DYNAMIC_LATENCY 0x00000080 -#define MA_PA_SINK_SET_FORMATS 0x00000100 - -typedef int ma_pa_source_flags_t; -#define MA_PA_SOURCE_NOFLAGS 0x00000000 -#define MA_PA_SOURCE_HW_VOLUME_CTRL 0x00000001 -#define MA_PA_SOURCE_LATENCY 0x00000002 -#define MA_PA_SOURCE_HARDWARE 0x00000004 -#define MA_PA_SOURCE_NETWORK 0x00000008 -#define MA_PA_SOURCE_HW_MUTE_CTRL 0x00000010 -#define MA_PA_SOURCE_DECIBEL_VOLUME 0x00000020 -#define MA_PA_SOURCE_DYNAMIC_LATENCY 0x00000040 -#define MA_PA_SOURCE_FLAT_VOLUME 0x00000080 - -typedef int ma_pa_context_state_t; -#define MA_PA_CONTEXT_UNCONNECTED 0 -#define MA_PA_CONTEXT_CONNECTING 1 -#define MA_PA_CONTEXT_AUTHORIZING 2 -#define MA_PA_CONTEXT_SETTING_NAME 3 -#define MA_PA_CONTEXT_READY 4 -#define MA_PA_CONTEXT_FAILED 5 -#define MA_PA_CONTEXT_TERMINATED 6 - -typedef int ma_pa_stream_state_t; -#define MA_PA_STREAM_UNCONNECTED 0 -#define MA_PA_STREAM_CREATING 1 -#define MA_PA_STREAM_READY 2 -#define MA_PA_STREAM_FAILED 3 -#define MA_PA_STREAM_TERMINATED 4 - -typedef int ma_pa_operation_state_t; -#define MA_PA_OPERATION_RUNNING 0 -#define MA_PA_OPERATION_DONE 1 -#define MA_PA_OPERATION_CANCELLED 2 - -typedef int ma_pa_sink_state_t; -#define MA_PA_SINK_INVALID_STATE -1 -#define MA_PA_SINK_RUNNING 0 -#define MA_PA_SINK_IDLE 1 -#define MA_PA_SINK_SUSPENDED 2 - -typedef int ma_pa_source_state_t; -#define MA_PA_SOURCE_INVALID_STATE -1 -#define MA_PA_SOURCE_RUNNING 0 -#define MA_PA_SOURCE_IDLE 1 -#define MA_PA_SOURCE_SUSPENDED 2 - -typedef int ma_pa_seek_mode_t; -#define MA_PA_SEEK_RELATIVE 0 -#define MA_PA_SEEK_ABSOLUTE 1 -#define MA_PA_SEEK_RELATIVE_ON_READ 2 -#define MA_PA_SEEK_RELATIVE_END 3 - -typedef int ma_pa_channel_position_t; -#define MA_PA_CHANNEL_POSITION_INVALID -1 -#define MA_PA_CHANNEL_POSITION_MONO 0 -#define MA_PA_CHANNEL_POSITION_FRONT_LEFT 1 -#define MA_PA_CHANNEL_POSITION_FRONT_RIGHT 2 -#define MA_PA_CHANNEL_POSITION_FRONT_CENTER 3 -#define MA_PA_CHANNEL_POSITION_REAR_CENTER 4 -#define MA_PA_CHANNEL_POSITION_REAR_LEFT 5 -#define MA_PA_CHANNEL_POSITION_REAR_RIGHT 6 -#define MA_PA_CHANNEL_POSITION_LFE 7 -#define MA_PA_CHANNEL_POSITION_FRONT_LEFT_OF_CENTER 8 -#define MA_PA_CHANNEL_POSITION_FRONT_RIGHT_OF_CENTER 9 -#define MA_PA_CHANNEL_POSITION_SIDE_LEFT 10 -#define MA_PA_CHANNEL_POSITION_SIDE_RIGHT 11 -#define MA_PA_CHANNEL_POSITION_AUX0 12 -#define MA_PA_CHANNEL_POSITION_AUX1 13 -#define MA_PA_CHANNEL_POSITION_AUX2 14 -#define MA_PA_CHANNEL_POSITION_AUX3 15 -#define MA_PA_CHANNEL_POSITION_AUX4 16 -#define MA_PA_CHANNEL_POSITION_AUX5 17 -#define MA_PA_CHANNEL_POSITION_AUX6 18 -#define MA_PA_CHANNEL_POSITION_AUX7 19 -#define MA_PA_CHANNEL_POSITION_AUX8 20 -#define MA_PA_CHANNEL_POSITION_AUX9 21 -#define MA_PA_CHANNEL_POSITION_AUX10 22 -#define MA_PA_CHANNEL_POSITION_AUX11 23 -#define MA_PA_CHANNEL_POSITION_AUX12 24 -#define MA_PA_CHANNEL_POSITION_AUX13 25 -#define MA_PA_CHANNEL_POSITION_AUX14 26 -#define MA_PA_CHANNEL_POSITION_AUX15 27 -#define MA_PA_CHANNEL_POSITION_AUX16 28 -#define MA_PA_CHANNEL_POSITION_AUX17 29 -#define MA_PA_CHANNEL_POSITION_AUX18 30 -#define MA_PA_CHANNEL_POSITION_AUX19 31 -#define MA_PA_CHANNEL_POSITION_AUX20 32 -#define MA_PA_CHANNEL_POSITION_AUX21 33 -#define MA_PA_CHANNEL_POSITION_AUX22 34 -#define MA_PA_CHANNEL_POSITION_AUX23 35 -#define MA_PA_CHANNEL_POSITION_AUX24 36 -#define MA_PA_CHANNEL_POSITION_AUX25 37 -#define MA_PA_CHANNEL_POSITION_AUX26 38 -#define MA_PA_CHANNEL_POSITION_AUX27 39 -#define MA_PA_CHANNEL_POSITION_AUX28 40 -#define MA_PA_CHANNEL_POSITION_AUX29 41 -#define MA_PA_CHANNEL_POSITION_AUX30 42 -#define MA_PA_CHANNEL_POSITION_AUX31 43 -#define MA_PA_CHANNEL_POSITION_TOP_CENTER 44 -#define MA_PA_CHANNEL_POSITION_TOP_FRONT_LEFT 45 -#define MA_PA_CHANNEL_POSITION_TOP_FRONT_RIGHT 46 -#define MA_PA_CHANNEL_POSITION_TOP_FRONT_CENTER 47 -#define MA_PA_CHANNEL_POSITION_TOP_REAR_LEFT 48 -#define MA_PA_CHANNEL_POSITION_TOP_REAR_RIGHT 49 -#define MA_PA_CHANNEL_POSITION_TOP_REAR_CENTER 50 -#define MA_PA_CHANNEL_POSITION_LEFT MA_PA_CHANNEL_POSITION_FRONT_LEFT -#define MA_PA_CHANNEL_POSITION_RIGHT MA_PA_CHANNEL_POSITION_FRONT_RIGHT -#define MA_PA_CHANNEL_POSITION_CENTER MA_PA_CHANNEL_POSITION_FRONT_CENTER -#define MA_PA_CHANNEL_POSITION_SUBWOOFER MA_PA_CHANNEL_POSITION_LFE - -typedef int ma_pa_channel_map_def_t; -#define MA_PA_CHANNEL_MAP_AIFF 0 -#define MA_PA_CHANNEL_MAP_ALSA 1 -#define MA_PA_CHANNEL_MAP_AUX 2 -#define MA_PA_CHANNEL_MAP_WAVEEX 3 -#define MA_PA_CHANNEL_MAP_OSS 4 -#define MA_PA_CHANNEL_MAP_DEFAULT MA_PA_CHANNEL_MAP_AIFF - -typedef int ma_pa_sample_format_t; -#define MA_PA_SAMPLE_INVALID -1 -#define MA_PA_SAMPLE_U8 0 -#define MA_PA_SAMPLE_ALAW 1 -#define MA_PA_SAMPLE_ULAW 2 -#define MA_PA_SAMPLE_S16LE 3 -#define MA_PA_SAMPLE_S16BE 4 -#define MA_PA_SAMPLE_FLOAT32LE 5 -#define MA_PA_SAMPLE_FLOAT32BE 6 -#define MA_PA_SAMPLE_S32LE 7 -#define MA_PA_SAMPLE_S32BE 8 -#define MA_PA_SAMPLE_S24LE 9 -#define MA_PA_SAMPLE_S24BE 10 -#define MA_PA_SAMPLE_S24_32LE 11 -#define MA_PA_SAMPLE_S24_32BE 12 - -typedef struct ma_pa_mainloop ma_pa_mainloop; -typedef struct ma_pa_threaded_mainloop ma_pa_threaded_mainloop; -typedef struct ma_pa_mainloop_api ma_pa_mainloop_api; -typedef struct ma_pa_context ma_pa_context; -typedef struct ma_pa_operation ma_pa_operation; -typedef struct ma_pa_stream ma_pa_stream; -typedef struct ma_pa_spawn_api ma_pa_spawn_api; - -typedef struct -{ - ma_uint32 maxlength; - ma_uint32 tlength; - ma_uint32 prebuf; - ma_uint32 minreq; - ma_uint32 fragsize; -} ma_pa_buffer_attr; - -typedef struct -{ - ma_uint8 channels; - ma_pa_channel_position_t map[MA_PA_CHANNELS_MAX]; -} ma_pa_channel_map; - -typedef struct -{ - ma_uint8 channels; - ma_uint32 values[MA_PA_CHANNELS_MAX]; -} ma_pa_cvolume; - -typedef struct -{ - ma_pa_sample_format_t format; - ma_uint32 rate; - ma_uint8 channels; -} ma_pa_sample_spec; - -typedef struct -{ - const char* name; - ma_uint32 index; - const char* description; - ma_pa_sample_spec sample_spec; - ma_pa_channel_map channel_map; - ma_uint32 owner_module; - ma_pa_cvolume volume; - int mute; - ma_uint32 monitor_source; - const char* monitor_source_name; - ma_uint64 latency; - const char* driver; - ma_pa_sink_flags_t flags; - void* proplist; - ma_uint64 configured_latency; - ma_uint32 base_volume; - ma_pa_sink_state_t state; - ma_uint32 n_volume_steps; - ma_uint32 card; - ma_uint32 n_ports; - void** ports; - void* active_port; - ma_uint8 n_formats; - void** formats; -} ma_pa_sink_info; - -typedef struct -{ - const char *name; - ma_uint32 index; - const char *description; - ma_pa_sample_spec sample_spec; - ma_pa_channel_map channel_map; - ma_uint32 owner_module; - ma_pa_cvolume volume; - int mute; - ma_uint32 monitor_of_sink; - const char *monitor_of_sink_name; - ma_uint64 latency; - const char *driver; - ma_pa_source_flags_t flags; - void* proplist; - ma_uint64 configured_latency; - ma_uint32 base_volume; - ma_pa_source_state_t state; - ma_uint32 n_volume_steps; - ma_uint32 card; - ma_uint32 n_ports; - void** ports; - void* active_port; - ma_uint8 n_formats; - void** formats; -} ma_pa_source_info; - -typedef void (* ma_pa_context_notify_cb_t)(ma_pa_context* c, void* userdata); -typedef void (* ma_pa_sink_info_cb_t) (ma_pa_context* c, const ma_pa_sink_info* i, int eol, void* userdata); -typedef void (* ma_pa_source_info_cb_t) (ma_pa_context* c, const ma_pa_source_info* i, int eol, void* userdata); -typedef void (* ma_pa_stream_success_cb_t)(ma_pa_stream* s, int success, void* userdata); -typedef void (* ma_pa_stream_request_cb_t)(ma_pa_stream* s, size_t nbytes, void* userdata); -typedef void (* ma_pa_stream_notify_cb_t) (ma_pa_stream* s, void* userdata); -typedef void (* ma_pa_free_cb_t) (void* p); -#endif - - -typedef ma_pa_mainloop* (* ma_pa_mainloop_new_proc) (void); -typedef void (* ma_pa_mainloop_free_proc) (ma_pa_mainloop* m); -typedef void (* ma_pa_mainloop_quit_proc) (ma_pa_mainloop* m, int retval); -typedef ma_pa_mainloop_api* (* ma_pa_mainloop_get_api_proc) (ma_pa_mainloop* m); -typedef int (* ma_pa_mainloop_iterate_proc) (ma_pa_mainloop* m, int block, int* retval); -typedef void (* ma_pa_mainloop_wakeup_proc) (ma_pa_mainloop* m); -typedef ma_pa_threaded_mainloop* (* ma_pa_threaded_mainloop_new_proc) (void); -typedef void (* ma_pa_threaded_mainloop_free_proc) (ma_pa_threaded_mainloop* m); -typedef int (* ma_pa_threaded_mainloop_start_proc) (ma_pa_threaded_mainloop* m); -typedef void (* ma_pa_threaded_mainloop_stop_proc) (ma_pa_threaded_mainloop* m); -typedef void (* ma_pa_threaded_mainloop_lock_proc) (ma_pa_threaded_mainloop* m); -typedef void (* ma_pa_threaded_mainloop_unlock_proc) (ma_pa_threaded_mainloop* m); -typedef void (* ma_pa_threaded_mainloop_wait_proc) (ma_pa_threaded_mainloop* m); -typedef void (* ma_pa_threaded_mainloop_signal_proc) (ma_pa_threaded_mainloop* m, int wait_for_accept); -typedef void (* ma_pa_threaded_mainloop_accept_proc) (ma_pa_threaded_mainloop* m); -typedef int (* ma_pa_threaded_mainloop_get_retval_proc) (ma_pa_threaded_mainloop* m); -typedef ma_pa_mainloop_api* (* ma_pa_threaded_mainloop_get_api_proc) (ma_pa_threaded_mainloop* m); -typedef int (* ma_pa_threaded_mainloop_in_thread_proc) (ma_pa_threaded_mainloop* m); -typedef void (* ma_pa_threaded_mainloop_set_name_proc) (ma_pa_threaded_mainloop* m, const char* name); -typedef ma_pa_context* (* ma_pa_context_new_proc) (ma_pa_mainloop_api* mainloop, const char* name); -typedef void (* ma_pa_context_unref_proc) (ma_pa_context* c); -typedef int (* ma_pa_context_connect_proc) (ma_pa_context* c, const char* server, ma_pa_context_flags_t flags, const ma_pa_spawn_api* api); -typedef void (* ma_pa_context_disconnect_proc) (ma_pa_context* c); -typedef void (* ma_pa_context_set_state_callback_proc) (ma_pa_context* c, ma_pa_context_notify_cb_t cb, void* userdata); -typedef ma_pa_context_state_t (* ma_pa_context_get_state_proc) (ma_pa_context* c); -typedef ma_pa_operation* (* ma_pa_context_get_sink_info_list_proc) (ma_pa_context* c, ma_pa_sink_info_cb_t cb, void* userdata); -typedef ma_pa_operation* (* ma_pa_context_get_source_info_list_proc) (ma_pa_context* c, ma_pa_source_info_cb_t cb, void* userdata); -typedef ma_pa_operation* (* ma_pa_context_get_sink_info_by_name_proc) (ma_pa_context* c, const char* name, ma_pa_sink_info_cb_t cb, void* userdata); -typedef ma_pa_operation* (* ma_pa_context_get_source_info_by_name_proc)(ma_pa_context* c, const char* name, ma_pa_source_info_cb_t cb, void* userdata); -typedef void (* ma_pa_operation_unref_proc) (ma_pa_operation* o); -typedef ma_pa_operation_state_t (* ma_pa_operation_get_state_proc) (ma_pa_operation* o); -typedef ma_pa_channel_map* (* ma_pa_channel_map_init_extend_proc) (ma_pa_channel_map* m, unsigned channels, ma_pa_channel_map_def_t def); -typedef int (* ma_pa_channel_map_valid_proc) (const ma_pa_channel_map* m); -typedef int (* ma_pa_channel_map_compatible_proc) (const ma_pa_channel_map* m, const ma_pa_sample_spec* ss); -typedef ma_pa_stream* (* ma_pa_stream_new_proc) (ma_pa_context* c, const char* name, const ma_pa_sample_spec* ss, const ma_pa_channel_map* map); -typedef void (* ma_pa_stream_unref_proc) (ma_pa_stream* s); -typedef int (* ma_pa_stream_connect_playback_proc) (ma_pa_stream* s, const char* dev, const ma_pa_buffer_attr* attr, ma_pa_stream_flags_t flags, const ma_pa_cvolume* volume, ma_pa_stream* sync_stream); -typedef int (* ma_pa_stream_connect_record_proc) (ma_pa_stream* s, const char* dev, const ma_pa_buffer_attr* attr, ma_pa_stream_flags_t flags); -typedef int (* ma_pa_stream_disconnect_proc) (ma_pa_stream* s); -typedef ma_pa_stream_state_t (* ma_pa_stream_get_state_proc) (ma_pa_stream* s); -typedef const ma_pa_sample_spec* (* ma_pa_stream_get_sample_spec_proc) (ma_pa_stream* s); -typedef const ma_pa_channel_map* (* ma_pa_stream_get_channel_map_proc) (ma_pa_stream* s); -typedef const ma_pa_buffer_attr* (* ma_pa_stream_get_buffer_attr_proc) (ma_pa_stream* s); -typedef ma_pa_operation* (* ma_pa_stream_set_buffer_attr_proc) (ma_pa_stream* s, const ma_pa_buffer_attr* attr, ma_pa_stream_success_cb_t cb, void* userdata); -typedef const char* (* ma_pa_stream_get_device_name_proc) (ma_pa_stream* s); -typedef void (* ma_pa_stream_set_write_callback_proc) (ma_pa_stream* s, ma_pa_stream_request_cb_t cb, void* userdata); -typedef void (* ma_pa_stream_set_read_callback_proc) (ma_pa_stream* s, ma_pa_stream_request_cb_t cb, void* userdata); -typedef void (* ma_pa_stream_set_suspended_callback_proc) (ma_pa_stream* s, ma_pa_stream_notify_cb_t cb, void* userdata); -typedef void (* ma_pa_stream_set_moved_callback_proc) (ma_pa_stream* s, ma_pa_stream_notify_cb_t cb, void* userdata); -typedef int (* ma_pa_stream_is_suspended_proc) (const ma_pa_stream* s); -typedef ma_pa_operation* (* ma_pa_stream_flush_proc) (ma_pa_stream* s, ma_pa_stream_success_cb_t cb, void* userdata); -typedef ma_pa_operation* (* ma_pa_stream_drain_proc) (ma_pa_stream* s, ma_pa_stream_success_cb_t cb, void* userdata); -typedef int (* ma_pa_stream_is_corked_proc) (ma_pa_stream* s); -typedef ma_pa_operation* (* ma_pa_stream_cork_proc) (ma_pa_stream* s, int b, ma_pa_stream_success_cb_t cb, void* userdata); -typedef ma_pa_operation* (* ma_pa_stream_trigger_proc) (ma_pa_stream* s, ma_pa_stream_success_cb_t cb, void* userdata); -typedef int (* ma_pa_stream_begin_write_proc) (ma_pa_stream* s, void** data, size_t* nbytes); -typedef int (* ma_pa_stream_write_proc) (ma_pa_stream* s, const void* data, size_t nbytes, ma_pa_free_cb_t free_cb, int64_t offset, ma_pa_seek_mode_t seek); -typedef int (* ma_pa_stream_peek_proc) (ma_pa_stream* s, const void** data, size_t* nbytes); -typedef int (* ma_pa_stream_drop_proc) (ma_pa_stream* s); -typedef size_t (* ma_pa_stream_writable_size_proc) (ma_pa_stream* s); -typedef size_t (* ma_pa_stream_readable_size_proc) (ma_pa_stream* s); - -typedef struct -{ - ma_uint32 count; - ma_uint32 capacity; - ma_device_info* pInfo; -} ma_pulse_device_enum_data; - -static ma_result ma_result_from_pulse(int result) -{ - if (result < 0) { - return MA_ERROR; - } - - switch (result) { - case MA_PA_OK: return MA_SUCCESS; - case MA_PA_ERR_ACCESS: return MA_ACCESS_DENIED; - case MA_PA_ERR_INVALID: return MA_INVALID_ARGS; - case MA_PA_ERR_NOENTITY: return MA_NO_DEVICE; - default: return MA_ERROR; - } -} - -#if 0 -static ma_pa_sample_format_t ma_format_to_pulse(ma_format format) -{ - if (ma_is_little_endian()) { - switch (format) { - case ma_format_s16: return MA_PA_SAMPLE_S16LE; - case ma_format_s24: return MA_PA_SAMPLE_S24LE; - case ma_format_s32: return MA_PA_SAMPLE_S32LE; - case ma_format_f32: return MA_PA_SAMPLE_FLOAT32LE; - default: break; - } - } else { - switch (format) { - case ma_format_s16: return MA_PA_SAMPLE_S16BE; - case ma_format_s24: return MA_PA_SAMPLE_S24BE; - case ma_format_s32: return MA_PA_SAMPLE_S32BE; - case ma_format_f32: return MA_PA_SAMPLE_FLOAT32BE; - default: break; - } - } - - /* Endian agnostic. */ - switch (format) { - case ma_format_u8: return MA_PA_SAMPLE_U8; - default: return MA_PA_SAMPLE_INVALID; - } -} -#endif - -static ma_format ma_format_from_pulse(ma_pa_sample_format_t format) -{ - if (ma_is_little_endian()) { - switch (format) { - case MA_PA_SAMPLE_S16LE: return ma_format_s16; - case MA_PA_SAMPLE_S24LE: return ma_format_s24; - case MA_PA_SAMPLE_S32LE: return ma_format_s32; - case MA_PA_SAMPLE_FLOAT32LE: return ma_format_f32; - default: break; - } - } else { - switch (format) { - case MA_PA_SAMPLE_S16BE: return ma_format_s16; - case MA_PA_SAMPLE_S24BE: return ma_format_s24; - case MA_PA_SAMPLE_S32BE: return ma_format_s32; - case MA_PA_SAMPLE_FLOAT32BE: return ma_format_f32; - default: break; - } - } - - /* Endian agnostic. */ - switch (format) { - case MA_PA_SAMPLE_U8: return ma_format_u8; - default: return ma_format_unknown; - } -} - -static ma_channel ma_channel_position_from_pulse(ma_pa_channel_position_t position) -{ - switch (position) - { - case MA_PA_CHANNEL_POSITION_INVALID: return MA_CHANNEL_NONE; - case MA_PA_CHANNEL_POSITION_MONO: return MA_CHANNEL_MONO; - case MA_PA_CHANNEL_POSITION_FRONT_LEFT: return MA_CHANNEL_FRONT_LEFT; - case MA_PA_CHANNEL_POSITION_FRONT_RIGHT: return MA_CHANNEL_FRONT_RIGHT; - case MA_PA_CHANNEL_POSITION_FRONT_CENTER: return MA_CHANNEL_FRONT_CENTER; - case MA_PA_CHANNEL_POSITION_REAR_CENTER: return MA_CHANNEL_BACK_CENTER; - case MA_PA_CHANNEL_POSITION_REAR_LEFT: return MA_CHANNEL_BACK_LEFT; - case MA_PA_CHANNEL_POSITION_REAR_RIGHT: return MA_CHANNEL_BACK_RIGHT; - case MA_PA_CHANNEL_POSITION_LFE: return MA_CHANNEL_LFE; - case MA_PA_CHANNEL_POSITION_FRONT_LEFT_OF_CENTER: return MA_CHANNEL_FRONT_LEFT_CENTER; - case MA_PA_CHANNEL_POSITION_FRONT_RIGHT_OF_CENTER: return MA_CHANNEL_FRONT_RIGHT_CENTER; - case MA_PA_CHANNEL_POSITION_SIDE_LEFT: return MA_CHANNEL_SIDE_LEFT; - case MA_PA_CHANNEL_POSITION_SIDE_RIGHT: return MA_CHANNEL_SIDE_RIGHT; - case MA_PA_CHANNEL_POSITION_AUX0: return MA_CHANNEL_AUX_0; - case MA_PA_CHANNEL_POSITION_AUX1: return MA_CHANNEL_AUX_1; - case MA_PA_CHANNEL_POSITION_AUX2: return MA_CHANNEL_AUX_2; - case MA_PA_CHANNEL_POSITION_AUX3: return MA_CHANNEL_AUX_3; - case MA_PA_CHANNEL_POSITION_AUX4: return MA_CHANNEL_AUX_4; - case MA_PA_CHANNEL_POSITION_AUX5: return MA_CHANNEL_AUX_5; - case MA_PA_CHANNEL_POSITION_AUX6: return MA_CHANNEL_AUX_6; - case MA_PA_CHANNEL_POSITION_AUX7: return MA_CHANNEL_AUX_7; - case MA_PA_CHANNEL_POSITION_AUX8: return MA_CHANNEL_AUX_8; - case MA_PA_CHANNEL_POSITION_AUX9: return MA_CHANNEL_AUX_9; - case MA_PA_CHANNEL_POSITION_AUX10: return MA_CHANNEL_AUX_10; - case MA_PA_CHANNEL_POSITION_AUX11: return MA_CHANNEL_AUX_11; - case MA_PA_CHANNEL_POSITION_AUX12: return MA_CHANNEL_AUX_12; - case MA_PA_CHANNEL_POSITION_AUX13: return MA_CHANNEL_AUX_13; - case MA_PA_CHANNEL_POSITION_AUX14: return MA_CHANNEL_AUX_14; - case MA_PA_CHANNEL_POSITION_AUX15: return MA_CHANNEL_AUX_15; - case MA_PA_CHANNEL_POSITION_AUX16: return MA_CHANNEL_AUX_16; - case MA_PA_CHANNEL_POSITION_AUX17: return MA_CHANNEL_AUX_17; - case MA_PA_CHANNEL_POSITION_AUX18: return MA_CHANNEL_AUX_18; - case MA_PA_CHANNEL_POSITION_AUX19: return MA_CHANNEL_AUX_19; - case MA_PA_CHANNEL_POSITION_AUX20: return MA_CHANNEL_AUX_20; - case MA_PA_CHANNEL_POSITION_AUX21: return MA_CHANNEL_AUX_21; - case MA_PA_CHANNEL_POSITION_AUX22: return MA_CHANNEL_AUX_22; - case MA_PA_CHANNEL_POSITION_AUX23: return MA_CHANNEL_AUX_23; - case MA_PA_CHANNEL_POSITION_AUX24: return MA_CHANNEL_AUX_24; - case MA_PA_CHANNEL_POSITION_AUX25: return MA_CHANNEL_AUX_25; - case MA_PA_CHANNEL_POSITION_AUX26: return MA_CHANNEL_AUX_26; - case MA_PA_CHANNEL_POSITION_AUX27: return MA_CHANNEL_AUX_27; - case MA_PA_CHANNEL_POSITION_AUX28: return MA_CHANNEL_AUX_28; - case MA_PA_CHANNEL_POSITION_AUX29: return MA_CHANNEL_AUX_29; - case MA_PA_CHANNEL_POSITION_AUX30: return MA_CHANNEL_AUX_30; - case MA_PA_CHANNEL_POSITION_AUX31: return MA_CHANNEL_AUX_31; - case MA_PA_CHANNEL_POSITION_TOP_CENTER: return MA_CHANNEL_TOP_CENTER; - case MA_PA_CHANNEL_POSITION_TOP_FRONT_LEFT: return MA_CHANNEL_TOP_FRONT_LEFT; - case MA_PA_CHANNEL_POSITION_TOP_FRONT_RIGHT: return MA_CHANNEL_TOP_FRONT_RIGHT; - case MA_PA_CHANNEL_POSITION_TOP_FRONT_CENTER: return MA_CHANNEL_TOP_FRONT_CENTER; - case MA_PA_CHANNEL_POSITION_TOP_REAR_LEFT: return MA_CHANNEL_TOP_BACK_LEFT; - case MA_PA_CHANNEL_POSITION_TOP_REAR_RIGHT: return MA_CHANNEL_TOP_BACK_RIGHT; - case MA_PA_CHANNEL_POSITION_TOP_REAR_CENTER: return MA_CHANNEL_TOP_BACK_CENTER; - default: return MA_CHANNEL_NONE; - } -} - -#if 0 -static ma_pa_channel_position_t ma_channel_position_to_pulse(ma_channel position) -{ - switch (position) - { - case MA_CHANNEL_NONE: return MA_PA_CHANNEL_POSITION_INVALID; - case MA_CHANNEL_FRONT_LEFT: return MA_PA_CHANNEL_POSITION_FRONT_LEFT; - case MA_CHANNEL_FRONT_RIGHT: return MA_PA_CHANNEL_POSITION_FRONT_RIGHT; - case MA_CHANNEL_FRONT_CENTER: return MA_PA_CHANNEL_POSITION_FRONT_CENTER; - case MA_CHANNEL_LFE: return MA_PA_CHANNEL_POSITION_LFE; - case MA_CHANNEL_BACK_LEFT: return MA_PA_CHANNEL_POSITION_REAR_LEFT; - case MA_CHANNEL_BACK_RIGHT: return MA_PA_CHANNEL_POSITION_REAR_RIGHT; - case MA_CHANNEL_FRONT_LEFT_CENTER: return MA_PA_CHANNEL_POSITION_FRONT_LEFT_OF_CENTER; - case MA_CHANNEL_FRONT_RIGHT_CENTER: return MA_PA_CHANNEL_POSITION_FRONT_RIGHT_OF_CENTER; - case MA_CHANNEL_BACK_CENTER: return MA_PA_CHANNEL_POSITION_REAR_CENTER; - case MA_CHANNEL_SIDE_LEFT: return MA_PA_CHANNEL_POSITION_SIDE_LEFT; - case MA_CHANNEL_SIDE_RIGHT: return MA_PA_CHANNEL_POSITION_SIDE_RIGHT; - case MA_CHANNEL_TOP_CENTER: return MA_PA_CHANNEL_POSITION_TOP_CENTER; - case MA_CHANNEL_TOP_FRONT_LEFT: return MA_PA_CHANNEL_POSITION_TOP_FRONT_LEFT; - case MA_CHANNEL_TOP_FRONT_CENTER: return MA_PA_CHANNEL_POSITION_TOP_FRONT_CENTER; - case MA_CHANNEL_TOP_FRONT_RIGHT: return MA_PA_CHANNEL_POSITION_TOP_FRONT_RIGHT; - case MA_CHANNEL_TOP_BACK_LEFT: return MA_PA_CHANNEL_POSITION_TOP_REAR_LEFT; - case MA_CHANNEL_TOP_BACK_CENTER: return MA_PA_CHANNEL_POSITION_TOP_REAR_CENTER; - case MA_CHANNEL_TOP_BACK_RIGHT: return MA_PA_CHANNEL_POSITION_TOP_REAR_RIGHT; - case MA_CHANNEL_19: return MA_PA_CHANNEL_POSITION_AUX18; - case MA_CHANNEL_20: return MA_PA_CHANNEL_POSITION_AUX19; - case MA_CHANNEL_21: return MA_PA_CHANNEL_POSITION_AUX20; - case MA_CHANNEL_22: return MA_PA_CHANNEL_POSITION_AUX21; - case MA_CHANNEL_23: return MA_PA_CHANNEL_POSITION_AUX22; - case MA_CHANNEL_24: return MA_PA_CHANNEL_POSITION_AUX23; - case MA_CHANNEL_25: return MA_PA_CHANNEL_POSITION_AUX24; - case MA_CHANNEL_26: return MA_PA_CHANNEL_POSITION_AUX25; - case MA_CHANNEL_27: return MA_PA_CHANNEL_POSITION_AUX26; - case MA_CHANNEL_28: return MA_PA_CHANNEL_POSITION_AUX27; - case MA_CHANNEL_29: return MA_PA_CHANNEL_POSITION_AUX28; - case MA_CHANNEL_30: return MA_PA_CHANNEL_POSITION_AUX29; - case MA_CHANNEL_31: return MA_PA_CHANNEL_POSITION_AUX30; - case MA_CHANNEL_32: return MA_PA_CHANNEL_POSITION_AUX31; - default: return (ma_pa_channel_position_t)position; - } -} -#endif - -static ma_result ma_wait_for_operation__pulse(ma_context* pContext, ma_ptr pMainLoop, ma_pa_operation* pOP) -{ - int resultPA; - ma_pa_operation_state_t state; - - MA_ASSERT(pContext != NULL); - MA_ASSERT(pOP != NULL); - - for (;;) { - state = ((ma_pa_operation_get_state_proc)pContext->pulse.pa_operation_get_state)(pOP); - if (state != MA_PA_OPERATION_RUNNING) { - break; /* Done. */ - } - - resultPA = ((ma_pa_mainloop_iterate_proc)pContext->pulse.pa_mainloop_iterate)((ma_pa_mainloop*)pMainLoop, 1, NULL); - if (resultPA < 0) { - return ma_result_from_pulse(resultPA); - } - } - - return MA_SUCCESS; -} - -static ma_result ma_wait_for_operation_and_unref__pulse(ma_context* pContext, ma_ptr pMainLoop, ma_pa_operation* pOP) -{ - ma_result result; - - if (pOP == NULL) { - return MA_INVALID_ARGS; - } - - result = ma_wait_for_operation__pulse(pContext, pMainLoop, pOP); - ((ma_pa_operation_unref_proc)pContext->pulse.pa_operation_unref)(pOP); - - return result; -} - -static ma_result ma_wait_for_pa_context_to_connect__pulse(ma_context* pContext, ma_ptr pMainLoop, ma_ptr pPulseContext) -{ - int resultPA; - ma_pa_context_state_t state; - - for (;;) { - state = ((ma_pa_context_get_state_proc)pContext->pulse.pa_context_get_state)((ma_pa_context*)pPulseContext); - if (state == MA_PA_CONTEXT_READY) { - break; /* Done. */ - } - - if (state == MA_PA_CONTEXT_FAILED || state == MA_PA_CONTEXT_TERMINATED) { - ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[PulseAudio] An error occurred while connecting the PulseAudio context."); - return MA_ERROR; - } - - resultPA = ((ma_pa_mainloop_iterate_proc)pContext->pulse.pa_mainloop_iterate)((ma_pa_mainloop*)pMainLoop, 1, NULL); - if (resultPA < 0) { - return ma_result_from_pulse(resultPA); - } - } - - /* Should never get here. */ - return MA_SUCCESS; -} - -static ma_result ma_wait_for_pa_stream_to_connect__pulse(ma_context* pContext, ma_ptr pMainLoop, ma_ptr pStream) -{ - int resultPA; - ma_pa_stream_state_t state; - - for (;;) { - state = ((ma_pa_stream_get_state_proc)pContext->pulse.pa_stream_get_state)((ma_pa_stream*)pStream); - if (state == MA_PA_STREAM_READY) { - break; /* Done. */ - } - - if (state == MA_PA_STREAM_FAILED || state == MA_PA_STREAM_TERMINATED) { - ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[PulseAudio] An error occurred while connecting the PulseAudio stream."); - return MA_ERROR; - } - - resultPA = ((ma_pa_mainloop_iterate_proc)pContext->pulse.pa_mainloop_iterate)((ma_pa_mainloop*)pMainLoop, 1, NULL); - if (resultPA < 0) { - return ma_result_from_pulse(resultPA); - } - } - - return MA_SUCCESS; -} - - -static ma_result ma_init_pa_mainloop_and_pa_context__pulse(ma_context* pContext, const char* pApplicationName, const char* pServerName, ma_bool32 tryAutoSpawn, ma_ptr* ppMainLoop, ma_ptr* ppPulseContext) -{ - ma_result result; - ma_ptr pMainLoop; - ma_ptr pPulseContext; - - MA_ASSERT(ppMainLoop != NULL); - MA_ASSERT(ppPulseContext != NULL); - - /* The PulseAudio context maps well to miniaudio's notion of a context. The pa_context object will be initialized as part of the ma_context. */ - pMainLoop = ((ma_pa_mainloop_new_proc)pContext->pulse.pa_mainloop_new)(); - if (pMainLoop == NULL) { - ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[PulseAudio] Failed to create mainloop."); - return MA_FAILED_TO_INIT_BACKEND; - } - - pPulseContext = ((ma_pa_context_new_proc)pContext->pulse.pa_context_new)(((ma_pa_mainloop_get_api_proc)pContext->pulse.pa_mainloop_get_api)((ma_pa_mainloop*)pMainLoop), pApplicationName); - if (pPulseContext == NULL) { - ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[PulseAudio] Failed to create PulseAudio context."); - ((ma_pa_mainloop_free_proc)pContext->pulse.pa_mainloop_free)((ma_pa_mainloop*)(pMainLoop)); - return MA_FAILED_TO_INIT_BACKEND; - } - - /* Now we need to connect to the context. Everything is asynchronous so we need to wait for it to connect before returning. */ - result = ma_result_from_pulse(((ma_pa_context_connect_proc)pContext->pulse.pa_context_connect)((ma_pa_context*)pPulseContext, pServerName, (tryAutoSpawn) ? 0 : MA_PA_CONTEXT_NOAUTOSPAWN, NULL)); - if (result != MA_SUCCESS) { - ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[PulseAudio] Failed to connect PulseAudio context."); - ((ma_pa_mainloop_free_proc)pContext->pulse.pa_mainloop_free)((ma_pa_mainloop*)(pMainLoop)); - return result; - } - - /* Since ma_context_init() runs synchronously we need to wait for the PulseAudio context to connect before we return. */ - result = ma_wait_for_pa_context_to_connect__pulse(pContext, pMainLoop, pPulseContext); - if (result != MA_SUCCESS) { - ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[PulseAudio] Waiting for connection failed."); - ((ma_pa_mainloop_free_proc)pContext->pulse.pa_mainloop_free)((ma_pa_mainloop*)(pMainLoop)); - return result; - } - - *ppMainLoop = pMainLoop; - *ppPulseContext = pPulseContext; - - return MA_SUCCESS; -} - - -static void ma_device_sink_info_callback(ma_pa_context* pPulseContext, const ma_pa_sink_info* pInfo, int endOfList, void* pUserData) -{ - ma_pa_sink_info* pInfoOut; - - if (endOfList > 0) { - return; - } - - /* - There has been a report that indicates that pInfo can be null which results - in a null pointer dereference below. We'll check for this for safety. - */ - if (pInfo == NULL) { - return; - } - - pInfoOut = (ma_pa_sink_info*)pUserData; - MA_ASSERT(pInfoOut != NULL); - - *pInfoOut = *pInfo; - - (void)pPulseContext; /* Unused. */ -} - -static void ma_device_source_info_callback(ma_pa_context* pPulseContext, const ma_pa_source_info* pInfo, int endOfList, void* pUserData) -{ - ma_pa_source_info* pInfoOut; - - if (endOfList > 0) { - return; - } - - /* - There has been a report that indicates that pInfo can be null which results - in a null pointer dereference below. We'll check for this for safety. - */ - if (pInfo == NULL) { - return; - } - - pInfoOut = (ma_pa_source_info*)pUserData; - MA_ASSERT(pInfoOut != NULL); - - *pInfoOut = *pInfo; - - (void)pPulseContext; /* Unused. */ -} - -#if 0 -static void ma_device_sink_name_callback(ma_pa_context* pPulseContext, const ma_pa_sink_info* pInfo, int endOfList, void* pUserData) -{ - ma_device* pDevice; - - if (endOfList > 0) { - return; - } - - pDevice = (ma_device*)pUserData; - MA_ASSERT(pDevice != NULL); - - ma_strncpy_s(pDevice->playback.name, sizeof(pDevice->playback.name), pInfo->description, (size_t)-1); - - (void)pPulseContext; /* Unused. */ -} - -static void ma_device_source_name_callback(ma_pa_context* pPulseContext, const ma_pa_source_info* pInfo, int endOfList, void* pUserData) -{ - ma_device* pDevice; - - if (endOfList > 0) { - return; - } - - pDevice = (ma_device*)pUserData; - MA_ASSERT(pDevice != NULL); - - ma_strncpy_s(pDevice->capture.name, sizeof(pDevice->capture.name), pInfo->description, (size_t)-1); - - (void)pPulseContext; /* Unused. */ -} -#endif - -static ma_result ma_context_get_sink_info__pulse(ma_context* pContext, const char* pDeviceName, ma_pa_sink_info* pSinkInfo) -{ - ma_pa_operation* pOP; - - pOP = ((ma_pa_context_get_sink_info_by_name_proc)pContext->pulse.pa_context_get_sink_info_by_name)((ma_pa_context*)pContext->pulse.pPulseContext, pDeviceName, ma_device_sink_info_callback, pSinkInfo); - if (pOP == NULL) { - return MA_ERROR; - } - - return ma_wait_for_operation_and_unref__pulse(pContext, pContext->pulse.pMainLoop, pOP); -} - -static ma_result ma_context_get_source_info__pulse(ma_context* pContext, const char* pDeviceName, ma_pa_source_info* pSourceInfo) -{ - ma_pa_operation* pOP; - - pOP = ((ma_pa_context_get_source_info_by_name_proc)pContext->pulse.pa_context_get_source_info_by_name)((ma_pa_context*)pContext->pulse.pPulseContext, pDeviceName, ma_device_source_info_callback, pSourceInfo); - if (pOP == NULL) { - return MA_ERROR; - } - - return ma_wait_for_operation_and_unref__pulse(pContext, pContext->pulse.pMainLoop, pOP); -} - -static ma_result ma_context_get_default_device_index__pulse(ma_context* pContext, ma_device_type deviceType, ma_uint32* pIndex) -{ - ma_result result; - - MA_ASSERT(pContext != NULL); - MA_ASSERT(pIndex != NULL); - - if (pIndex != NULL) { - *pIndex = (ma_uint32)-1; - } - - if (deviceType == ma_device_type_playback) { - ma_pa_sink_info sinkInfo; - result = ma_context_get_sink_info__pulse(pContext, NULL, &sinkInfo); - if (result != MA_SUCCESS) { - return result; - } - - if (pIndex != NULL) { - *pIndex = sinkInfo.index; - } - } - - if (deviceType == ma_device_type_capture) { - ma_pa_source_info sourceInfo; - result = ma_context_get_source_info__pulse(pContext, NULL, &sourceInfo); - if (result != MA_SUCCESS) { - return result; - } - - if (pIndex != NULL) { - *pIndex = sourceInfo.index; - } - } - - return MA_SUCCESS; -} - - -typedef struct -{ - ma_context* pContext; - ma_enum_devices_callback_proc callback; - void* pUserData; - ma_bool32 isTerminated; - ma_uint32 defaultDeviceIndexPlayback; - ma_uint32 defaultDeviceIndexCapture; -} ma_context_enumerate_devices_callback_data__pulse; - -static void ma_context_enumerate_devices_sink_callback__pulse(ma_pa_context* pPulseContext, const ma_pa_sink_info* pSinkInfo, int endOfList, void* pUserData) -{ - ma_context_enumerate_devices_callback_data__pulse* pData = (ma_context_enumerate_devices_callback_data__pulse*)pUserData; - ma_device_info deviceInfo; - - MA_ASSERT(pData != NULL); - - if (endOfList || pData->isTerminated) { - return; - } - - MA_ZERO_OBJECT(&deviceInfo); - - /* The name from PulseAudio is the ID for miniaudio. */ - if (pSinkInfo->name != NULL) { - ma_strncpy_s(deviceInfo.id.pulse, sizeof(deviceInfo.id.pulse), pSinkInfo->name, (size_t)-1); - } - - /* The description from PulseAudio is the name for miniaudio. */ - if (pSinkInfo->description != NULL) { - ma_strncpy_s(deviceInfo.name, sizeof(deviceInfo.name), pSinkInfo->description, (size_t)-1); - } - - if (pSinkInfo->index == pData->defaultDeviceIndexPlayback) { - deviceInfo.isDefault = MA_TRUE; - } - - pData->isTerminated = !pData->callback(pData->pContext, ma_device_type_playback, &deviceInfo, pData->pUserData); - - (void)pPulseContext; /* Unused. */ -} - -static void ma_context_enumerate_devices_source_callback__pulse(ma_pa_context* pPulseContext, const ma_pa_source_info* pSourceInfo, int endOfList, void* pUserData) -{ - ma_context_enumerate_devices_callback_data__pulse* pData = (ma_context_enumerate_devices_callback_data__pulse*)pUserData; - ma_device_info deviceInfo; - - MA_ASSERT(pData != NULL); - - if (endOfList || pData->isTerminated) { - return; - } - - MA_ZERO_OBJECT(&deviceInfo); - - /* The name from PulseAudio is the ID for miniaudio. */ - if (pSourceInfo->name != NULL) { - ma_strncpy_s(deviceInfo.id.pulse, sizeof(deviceInfo.id.pulse), pSourceInfo->name, (size_t)-1); - } - - /* The description from PulseAudio is the name for miniaudio. */ - if (pSourceInfo->description != NULL) { - ma_strncpy_s(deviceInfo.name, sizeof(deviceInfo.name), pSourceInfo->description, (size_t)-1); - } - - if (pSourceInfo->index == pData->defaultDeviceIndexCapture) { - deviceInfo.isDefault = MA_TRUE; - } - - pData->isTerminated = !pData->callback(pData->pContext, ma_device_type_capture, &deviceInfo, pData->pUserData); - - (void)pPulseContext; /* Unused. */ -} - -static ma_result ma_context_enumerate_devices__pulse(ma_context* pContext, ma_enum_devices_callback_proc callback, void* pUserData) -{ - ma_result result = MA_SUCCESS; - ma_context_enumerate_devices_callback_data__pulse callbackData; - ma_pa_operation* pOP = NULL; - - MA_ASSERT(pContext != NULL); - MA_ASSERT(callback != NULL); - - callbackData.pContext = pContext; - callbackData.callback = callback; - callbackData.pUserData = pUserData; - callbackData.isTerminated = MA_FALSE; - callbackData.defaultDeviceIndexPlayback = (ma_uint32)-1; - callbackData.defaultDeviceIndexCapture = (ma_uint32)-1; - - /* We need to get the index of the default devices. */ - ma_context_get_default_device_index__pulse(pContext, ma_device_type_playback, &callbackData.defaultDeviceIndexPlayback); - ma_context_get_default_device_index__pulse(pContext, ma_device_type_capture, &callbackData.defaultDeviceIndexCapture); - - /* Playback. */ - if (!callbackData.isTerminated) { - pOP = ((ma_pa_context_get_sink_info_list_proc)pContext->pulse.pa_context_get_sink_info_list)((ma_pa_context*)(pContext->pulse.pPulseContext), ma_context_enumerate_devices_sink_callback__pulse, &callbackData); - if (pOP == NULL) { - result = MA_ERROR; - goto done; - } - - result = ma_wait_for_operation__pulse(pContext, pContext->pulse.pMainLoop, pOP); - ((ma_pa_operation_unref_proc)pContext->pulse.pa_operation_unref)(pOP); - - if (result != MA_SUCCESS) { - goto done; - } - } - - - /* Capture. */ - if (!callbackData.isTerminated) { - pOP = ((ma_pa_context_get_source_info_list_proc)pContext->pulse.pa_context_get_source_info_list)((ma_pa_context*)(pContext->pulse.pPulseContext), ma_context_enumerate_devices_source_callback__pulse, &callbackData); - if (pOP == NULL) { - result = MA_ERROR; - goto done; - } - - result = ma_wait_for_operation__pulse(pContext, pContext->pulse.pMainLoop, pOP); - ((ma_pa_operation_unref_proc)pContext->pulse.pa_operation_unref)(pOP); - - if (result != MA_SUCCESS) { - goto done; - } - } - -done: - return result; -} - - -typedef struct -{ - ma_device_info* pDeviceInfo; - ma_uint32 defaultDeviceIndex; - ma_bool32 foundDevice; -} ma_context_get_device_info_callback_data__pulse; - -static void ma_context_get_device_info_sink_callback__pulse(ma_pa_context* pPulseContext, const ma_pa_sink_info* pInfo, int endOfList, void* pUserData) -{ - ma_context_get_device_info_callback_data__pulse* pData = (ma_context_get_device_info_callback_data__pulse*)pUserData; - - if (endOfList > 0) { - return; - } - - MA_ASSERT(pData != NULL); - pData->foundDevice = MA_TRUE; - - if (pInfo->name != NULL) { - ma_strncpy_s(pData->pDeviceInfo->id.pulse, sizeof(pData->pDeviceInfo->id.pulse), pInfo->name, (size_t)-1); - } - - if (pInfo->description != NULL) { - ma_strncpy_s(pData->pDeviceInfo->name, sizeof(pData->pDeviceInfo->name), pInfo->description, (size_t)-1); - } - - /* - We're just reporting a single data format here. I think technically PulseAudio might support - all formats, but I don't trust that PulseAudio will do *anything* right, so I'm just going to - report the "native" device format. - */ - pData->pDeviceInfo->nativeDataFormats[0].format = ma_format_from_pulse(pInfo->sample_spec.format); - pData->pDeviceInfo->nativeDataFormats[0].channels = pInfo->sample_spec.channels; - pData->pDeviceInfo->nativeDataFormats[0].sampleRate = pInfo->sample_spec.rate; - pData->pDeviceInfo->nativeDataFormats[0].flags = 0; - pData->pDeviceInfo->nativeDataFormatCount = 1; - - if (pData->defaultDeviceIndex == pInfo->index) { - pData->pDeviceInfo->isDefault = MA_TRUE; - } - - (void)pPulseContext; /* Unused. */ -} - -static void ma_context_get_device_info_source_callback__pulse(ma_pa_context* pPulseContext, const ma_pa_source_info* pInfo, int endOfList, void* pUserData) -{ - ma_context_get_device_info_callback_data__pulse* pData = (ma_context_get_device_info_callback_data__pulse*)pUserData; - - if (endOfList > 0) { - return; - } - - MA_ASSERT(pData != NULL); - pData->foundDevice = MA_TRUE; - - if (pInfo->name != NULL) { - ma_strncpy_s(pData->pDeviceInfo->id.pulse, sizeof(pData->pDeviceInfo->id.pulse), pInfo->name, (size_t)-1); - } - - if (pInfo->description != NULL) { - ma_strncpy_s(pData->pDeviceInfo->name, sizeof(pData->pDeviceInfo->name), pInfo->description, (size_t)-1); - } - - /* - We're just reporting a single data format here. I think technically PulseAudio might support - all formats, but I don't trust that PulseAudio will do *anything* right, so I'm just going to - report the "native" device format. - */ - pData->pDeviceInfo->nativeDataFormats[0].format = ma_format_from_pulse(pInfo->sample_spec.format); - pData->pDeviceInfo->nativeDataFormats[0].channels = pInfo->sample_spec.channels; - pData->pDeviceInfo->nativeDataFormats[0].sampleRate = pInfo->sample_spec.rate; - pData->pDeviceInfo->nativeDataFormats[0].flags = 0; - pData->pDeviceInfo->nativeDataFormatCount = 1; - - if (pData->defaultDeviceIndex == pInfo->index) { - pData->pDeviceInfo->isDefault = MA_TRUE; - } - - (void)pPulseContext; /* Unused. */ -} - -static ma_result ma_context_get_device_info__pulse(ma_context* pContext, ma_device_type deviceType, const ma_device_id* pDeviceID, ma_device_info* pDeviceInfo) -{ - ma_result result = MA_SUCCESS; - ma_context_get_device_info_callback_data__pulse callbackData; - ma_pa_operation* pOP = NULL; - const char* pDeviceName = NULL; - - MA_ASSERT(pContext != NULL); - - callbackData.pDeviceInfo = pDeviceInfo; - callbackData.foundDevice = MA_FALSE; - - if (pDeviceID != NULL) { - pDeviceName = pDeviceID->pulse; - } else { - pDeviceName = NULL; - } - - result = ma_context_get_default_device_index__pulse(pContext, deviceType, &callbackData.defaultDeviceIndex); - - if (deviceType == ma_device_type_playback) { - pOP = ((ma_pa_context_get_sink_info_by_name_proc)pContext->pulse.pa_context_get_sink_info_by_name)((ma_pa_context*)(pContext->pulse.pPulseContext), pDeviceName, ma_context_get_device_info_sink_callback__pulse, &callbackData); - } else { - pOP = ((ma_pa_context_get_source_info_by_name_proc)pContext->pulse.pa_context_get_source_info_by_name)((ma_pa_context*)(pContext->pulse.pPulseContext), pDeviceName, ma_context_get_device_info_source_callback__pulse, &callbackData); - } - - if (pOP != NULL) { - ma_wait_for_operation_and_unref__pulse(pContext, pContext->pulse.pMainLoop, pOP); - } else { - result = MA_ERROR; - goto done; - } - - if (!callbackData.foundDevice) { - result = MA_NO_DEVICE; - goto done; - } - -done: - return result; -} - -static ma_result ma_device_uninit__pulse(ma_device* pDevice) -{ - ma_context* pContext; - - MA_ASSERT(pDevice != NULL); - - pContext = pDevice->pContext; - MA_ASSERT(pContext != NULL); - - if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) { - ((ma_pa_stream_disconnect_proc)pContext->pulse.pa_stream_disconnect)((ma_pa_stream*)pDevice->pulse.pStreamCapture); - ((ma_pa_stream_unref_proc)pContext->pulse.pa_stream_unref)((ma_pa_stream*)pDevice->pulse.pStreamCapture); - } - - if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) { - ((ma_pa_stream_disconnect_proc)pContext->pulse.pa_stream_disconnect)((ma_pa_stream*)pDevice->pulse.pStreamPlayback); - ((ma_pa_stream_unref_proc)pContext->pulse.pa_stream_unref)((ma_pa_stream*)pDevice->pulse.pStreamPlayback); - } - - if (pDevice->type == ma_device_type_duplex) { - ma_duplex_rb_uninit(&pDevice->duplexRB); - } - - ((ma_pa_context_disconnect_proc)pContext->pulse.pa_context_disconnect)((ma_pa_context*)pDevice->pulse.pPulseContext); - ((ma_pa_context_unref_proc)pContext->pulse.pa_context_unref)((ma_pa_context*)pDevice->pulse.pPulseContext); - ((ma_pa_mainloop_free_proc)pContext->pulse.pa_mainloop_free)((ma_pa_mainloop*)pDevice->pulse.pMainLoop); - - return MA_SUCCESS; -} - -static ma_pa_buffer_attr ma_device__pa_buffer_attr_new(ma_uint32 periodSizeInFrames, ma_uint32 periods, const ma_pa_sample_spec* ss) -{ - ma_pa_buffer_attr attr; - attr.maxlength = periodSizeInFrames * periods * ma_get_bytes_per_frame(ma_format_from_pulse(ss->format), ss->channels); - attr.tlength = attr.maxlength / periods; - attr.prebuf = (ma_uint32)-1; - attr.minreq = (ma_uint32)-1; - attr.fragsize = attr.maxlength / periods; - - return attr; -} - -static ma_pa_stream* ma_device__pa_stream_new__pulse(ma_device* pDevice, const char* pStreamName, const ma_pa_sample_spec* ss, const ma_pa_channel_map* cmap) -{ - static ma_atomic_uint32 g_StreamCounter = { 0 }; - char actualStreamName[256]; - - if (pStreamName != NULL) { - ma_strncpy_s(actualStreamName, sizeof(actualStreamName), pStreamName, (size_t)-1); - } else { - const char* pBaseName = "miniaudio:"; - size_t baseNameLen = strlen(pBaseName); - ma_strcpy_s(actualStreamName, sizeof(actualStreamName), pBaseName); - ma_itoa_s((int)ma_atomic_uint32_get(&g_StreamCounter), actualStreamName + baseNameLen, sizeof(actualStreamName)-baseNameLen, 10); - } - ma_atomic_uint32_fetch_add(&g_StreamCounter, 1); - - return ((ma_pa_stream_new_proc)pDevice->pContext->pulse.pa_stream_new)((ma_pa_context*)pDevice->pulse.pPulseContext, actualStreamName, ss, cmap); -} - - -static void ma_device_on_read__pulse(ma_pa_stream* pStream, size_t byteCount, void* pUserData) -{ - ma_device* pDevice = (ma_device*)pUserData; - ma_uint32 bpf; - ma_uint32 deviceState; - ma_uint64 frameCount; - ma_uint64 framesProcessed; - - MA_ASSERT(pDevice != NULL); - - /* - Don't do anything if the device isn't initialized yet. Yes, this can happen because PulseAudio - can fire this callback before the stream has even started. Ridiculous. - */ - deviceState = ma_device_get_state(pDevice); - if (deviceState != ma_device_state_starting && deviceState != ma_device_state_started) { - return; - } - - bpf = ma_get_bytes_per_frame(pDevice->capture.internalFormat, pDevice->capture.internalChannels); - MA_ASSERT(bpf > 0); - - frameCount = byteCount / bpf; - framesProcessed = 0; - - while (ma_device_get_state(pDevice) == ma_device_state_started && framesProcessed < frameCount) { - const void* pMappedPCMFrames; - size_t bytesMapped; - ma_uint64 framesMapped; - - int pulseResult = ((ma_pa_stream_peek_proc)pDevice->pContext->pulse.pa_stream_peek)(pStream, &pMappedPCMFrames, &bytesMapped); - if (pulseResult < 0) { - break; /* Failed to map. Abort. */ - } - - framesMapped = bytesMapped / bpf; - if (framesMapped > 0) { - if (pMappedPCMFrames != NULL) { - ma_device_handle_backend_data_callback(pDevice, NULL, pMappedPCMFrames, framesMapped); - } else { - /* It's a hole. */ - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "[PulseAudio] ma_device_on_read__pulse: Hole.\n"); - } - - pulseResult = ((ma_pa_stream_drop_proc)pDevice->pContext->pulse.pa_stream_drop)(pStream); - if (pulseResult < 0) { - break; /* Failed to drop the buffer. */ - } - - framesProcessed += framesMapped; - - } else { - /* Nothing was mapped. Just abort. */ - break; - } - } -} - -static ma_result ma_device_write_to_stream__pulse(ma_device* pDevice, ma_pa_stream* pStream, ma_uint64* pFramesProcessed) -{ - ma_result result = MA_SUCCESS; - ma_uint64 framesProcessed = 0; - size_t bytesMapped; - ma_uint32 bpf; - ma_uint32 deviceState; - - MA_ASSERT(pDevice != NULL); - MA_ASSERT(pStream != NULL); - - bpf = ma_get_bytes_per_frame(pDevice->playback.internalFormat, pDevice->playback.internalChannels); - MA_ASSERT(bpf > 0); - - deviceState = ma_device_get_state(pDevice); - - bytesMapped = ((ma_pa_stream_writable_size_proc)pDevice->pContext->pulse.pa_stream_writable_size)(pStream); - if (bytesMapped != (size_t)-1) { - if (bytesMapped > 0) { - ma_uint64 framesMapped; - void* pMappedPCMFrames; - int pulseResult = ((ma_pa_stream_begin_write_proc)pDevice->pContext->pulse.pa_stream_begin_write)(pStream, &pMappedPCMFrames, &bytesMapped); - if (pulseResult < 0) { - result = ma_result_from_pulse(pulseResult); - goto done; - } - - framesMapped = bytesMapped / bpf; - - if (deviceState == ma_device_state_started || deviceState == ma_device_state_starting) { /* Check for starting state just in case this is being used to do the initial fill. */ - ma_device_handle_backend_data_callback(pDevice, pMappedPCMFrames, NULL, framesMapped); - } else { - /* Device is not started. Write silence. */ - ma_silence_pcm_frames(pMappedPCMFrames, framesMapped, pDevice->playback.format, pDevice->playback.channels); - } - - pulseResult = ((ma_pa_stream_write_proc)pDevice->pContext->pulse.pa_stream_write)(pStream, pMappedPCMFrames, bytesMapped, NULL, 0, MA_PA_SEEK_RELATIVE); - if (pulseResult < 0) { - result = ma_result_from_pulse(pulseResult); - goto done; /* Failed to write data to stream. */ - } - - framesProcessed += framesMapped; - } else { - result = MA_SUCCESS; /* No data available for writing. */ - goto done; - } - } else { - result = MA_ERROR; /* Failed to retrieve the writable size. Abort. */ - goto done; - } - -done: - if (pFramesProcessed != NULL) { - *pFramesProcessed = framesProcessed; - } - - return result; -} - -static void ma_device_on_write__pulse(ma_pa_stream* pStream, size_t byteCount, void* pUserData) -{ - ma_device* pDevice = (ma_device*)pUserData; - ma_uint32 bpf; - ma_uint64 frameCount; - ma_uint64 framesProcessed; - ma_uint32 deviceState; - ma_result result; - - MA_ASSERT(pDevice != NULL); - - /* - Don't do anything if the device isn't initialized yet. Yes, this can happen because PulseAudio - can fire this callback before the stream has even started. Ridiculous. - */ - deviceState = ma_device_get_state(pDevice); - if (deviceState != ma_device_state_starting && deviceState != ma_device_state_started) { - return; - } - - bpf = ma_get_bytes_per_frame(pDevice->playback.internalFormat, pDevice->playback.internalChannels); - MA_ASSERT(bpf > 0); - - frameCount = byteCount / bpf; - framesProcessed = 0; - - while (framesProcessed < frameCount) { - ma_uint64 framesProcessedThisIteration; - - /* Don't keep trying to process frames if the device isn't started. */ - deviceState = ma_device_get_state(pDevice); - if (deviceState != ma_device_state_starting && deviceState != ma_device_state_started) { - break; - } - - result = ma_device_write_to_stream__pulse(pDevice, pStream, &framesProcessedThisIteration); - if (result != MA_SUCCESS) { - break; - } - - framesProcessed += framesProcessedThisIteration; - } -} - -static void ma_device_on_suspended__pulse(ma_pa_stream* pStream, void* pUserData) -{ - ma_device* pDevice = (ma_device*)pUserData; - int suspended; - - (void)pStream; - - suspended = ((ma_pa_stream_is_suspended_proc)pDevice->pContext->pulse.pa_stream_is_suspended)(pStream); - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "[Pulse] Device suspended state changed. pa_stream_is_suspended() returned %d.\n", suspended); - - if (suspended < 0) { - return; - } - - if (suspended == 1) { - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "[Pulse] Device suspended state changed. Suspended.\n"); - ma_device__on_notification_stopped(pDevice); - } else { - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "[Pulse] Device suspended state changed. Resumed.\n"); - ma_device__on_notification_started(pDevice); - } -} - -static void ma_device_on_rerouted__pulse(ma_pa_stream* pStream, void* pUserData) -{ - ma_device* pDevice = (ma_device*)pUserData; - - (void)pStream; - (void)pUserData; - - ma_device__on_notification_rerouted(pDevice); -} - -static ma_uint32 ma_calculate_period_size_in_frames_from_descriptor__pulse(const ma_device_descriptor* pDescriptor, ma_uint32 nativeSampleRate, ma_performance_profile performanceProfile) -{ - /* - There have been reports from users where buffers of < ~20ms result glitches when running through - PipeWire. To work around this we're going to have to use a different default buffer size. - */ - const ma_uint32 defaultPeriodSizeInMilliseconds_LowLatency = 25; - const ma_uint32 defaultPeriodSizeInMilliseconds_Conservative = MA_DEFAULT_PERIOD_SIZE_IN_MILLISECONDS_CONSERVATIVE; - - MA_ASSERT(nativeSampleRate != 0); - - if (pDescriptor->periodSizeInFrames == 0) { - if (pDescriptor->periodSizeInMilliseconds == 0) { - if (performanceProfile == ma_performance_profile_low_latency) { - return ma_calculate_buffer_size_in_frames_from_milliseconds(defaultPeriodSizeInMilliseconds_LowLatency, nativeSampleRate); - } else { - return ma_calculate_buffer_size_in_frames_from_milliseconds(defaultPeriodSizeInMilliseconds_Conservative, nativeSampleRate); - } - } else { - return ma_calculate_buffer_size_in_frames_from_milliseconds(pDescriptor->periodSizeInMilliseconds, nativeSampleRate); - } - } else { - return pDescriptor->periodSizeInFrames; - } -} - -static ma_result ma_device_init__pulse(ma_device* pDevice, const ma_device_config* pConfig, ma_device_descriptor* pDescriptorPlayback, ma_device_descriptor* pDescriptorCapture) -{ - /* - Notes for PulseAudio: - - - When both the period size in frames and milliseconds are 0, we default to miniaudio's - default buffer sizes rather than leaving it up to PulseAudio because I don't trust - PulseAudio to give us any kind of reasonable latency by default. - - - Do not ever, *ever* forget to use MA_PA_STREAM_ADJUST_LATENCY. If you don't specify this - flag, capture mode will just not work properly until you open another PulseAudio app. - */ - - ma_result result = MA_SUCCESS; - int error = 0; - const char* devPlayback = NULL; - const char* devCapture = NULL; - ma_format format = ma_format_unknown; - ma_uint32 channels = 0; - ma_uint32 sampleRate = 0; - ma_pa_sink_info sinkInfo; - ma_pa_source_info sourceInfo; - ma_pa_sample_spec ss; - ma_pa_channel_map cmap; - ma_pa_buffer_attr attr; - const ma_pa_sample_spec* pActualSS = NULL; - const ma_pa_buffer_attr* pActualAttr = NULL; - const ma_pa_channel_map* pActualChannelMap = NULL; - ma_uint32 iChannel; - ma_pa_stream_flags_t streamFlags; - - MA_ASSERT(pDevice != NULL); - MA_ZERO_OBJECT(&pDevice->pulse); - - if (pConfig->deviceType == ma_device_type_loopback) { - return MA_DEVICE_TYPE_NOT_SUPPORTED; - } - - /* No exclusive mode with the PulseAudio backend. */ - if (((pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) && pConfig->playback.shareMode == ma_share_mode_exclusive) || - ((pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) && pConfig->capture.shareMode == ma_share_mode_exclusive)) { - return MA_SHARE_MODE_NOT_SUPPORTED; - } - - if (pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) { - if (pDescriptorPlayback->pDeviceID != NULL) { - devPlayback = pDescriptorPlayback->pDeviceID->pulse; - } - - format = pDescriptorPlayback->format; - channels = pDescriptorPlayback->channels; - sampleRate = pDescriptorPlayback->sampleRate; - } - - if (pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) { - if (pDescriptorCapture->pDeviceID != NULL) { - devCapture = pDescriptorCapture->pDeviceID->pulse; - } - - format = pDescriptorCapture->format; - channels = pDescriptorCapture->channels; - sampleRate = pDescriptorCapture->sampleRate; - } - - - - result = ma_init_pa_mainloop_and_pa_context__pulse(pDevice->pContext, pDevice->pContext->pulse.pApplicationName, pDevice->pContext->pulse.pServerName, MA_FALSE, &pDevice->pulse.pMainLoop, &pDevice->pulse.pPulseContext); - if (result != MA_SUCCESS) { - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[PulseAudio] Failed to initialize PA mainloop and context for device.\n"); - return result; - } - - if (pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) { - result = ma_context_get_source_info__pulse(pDevice->pContext, devCapture, &sourceInfo); - if (result != MA_SUCCESS) { - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[PulseAudio] Failed to retrieve source info for capture device."); - goto on_error0; - } - - ss = sourceInfo.sample_spec; - cmap = sourceInfo.channel_map; - - /* Use the requested channel count if we have one. */ - if (pDescriptorCapture->channels != 0) { - ss.channels = pDescriptorCapture->channels; - } - - /* Use a default channel map. */ - ((ma_pa_channel_map_init_extend_proc)pDevice->pContext->pulse.pa_channel_map_init_extend)(&cmap, ss.channels, pConfig->pulse.channelMap); - - /* Use the requested sample rate if one was specified. */ - if (pDescriptorCapture->sampleRate != 0) { - ss.rate = pDescriptorCapture->sampleRate; - } - streamFlags = MA_PA_STREAM_START_CORKED | MA_PA_STREAM_ADJUST_LATENCY; - - if (ma_format_from_pulse(ss.format) == ma_format_unknown) { - if (ma_is_little_endian()) { - ss.format = MA_PA_SAMPLE_FLOAT32LE; - } else { - ss.format = MA_PA_SAMPLE_FLOAT32BE; - } - streamFlags |= MA_PA_STREAM_FIX_FORMAT; - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, "[PulseAudio] sample_spec.format not supported by miniaudio. Defaulting to PA_SAMPLE_FLOAT32.\n"); - } - if (ss.rate == 0) { - ss.rate = MA_DEFAULT_SAMPLE_RATE; - streamFlags |= MA_PA_STREAM_FIX_RATE; - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, "[PulseAudio] sample_spec.rate = 0. Defaulting to %d.\n", ss.rate); - } - if (ss.channels == 0) { - ss.channels = MA_DEFAULT_CHANNELS; - streamFlags |= MA_PA_STREAM_FIX_CHANNELS; - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, "[PulseAudio] sample_spec.channels = 0. Defaulting to %d.\n", ss.channels); - } - - /* We now have enough information to calculate our actual period size in frames. */ - pDescriptorCapture->periodSizeInFrames = ma_calculate_period_size_in_frames_from_descriptor__pulse(pDescriptorCapture, ss.rate, pConfig->performanceProfile); - - attr = ma_device__pa_buffer_attr_new(pDescriptorCapture->periodSizeInFrames, pDescriptorCapture->periodCount, &ss); - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, "[PulseAudio] Capture attr: maxlength=%d, tlength=%d, prebuf=%d, minreq=%d, fragsize=%d; periodSizeInFrames=%d\n", attr.maxlength, attr.tlength, attr.prebuf, attr.minreq, attr.fragsize, pDescriptorCapture->periodSizeInFrames); - - pDevice->pulse.pStreamCapture = ma_device__pa_stream_new__pulse(pDevice, pConfig->pulse.pStreamNameCapture, &ss, &cmap); - if (pDevice->pulse.pStreamCapture == NULL) { - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[PulseAudio] Failed to create PulseAudio capture stream.\n"); - result = MA_ERROR; - goto on_error0; - } - - - /* The callback needs to be set before connecting the stream. */ - ((ma_pa_stream_set_read_callback_proc)pDevice->pContext->pulse.pa_stream_set_read_callback)((ma_pa_stream*)pDevice->pulse.pStreamCapture, ma_device_on_read__pulse, pDevice); - - /* State callback for checking when the device has been corked. */ - ((ma_pa_stream_set_suspended_callback_proc)pDevice->pContext->pulse.pa_stream_set_suspended_callback)((ma_pa_stream*)pDevice->pulse.pStreamCapture, ma_device_on_suspended__pulse, pDevice); - - /* Rerouting notification. */ - ((ma_pa_stream_set_moved_callback_proc)pDevice->pContext->pulse.pa_stream_set_moved_callback)((ma_pa_stream*)pDevice->pulse.pStreamCapture, ma_device_on_rerouted__pulse, pDevice); - - - /* Connect after we've got all of our internal state set up. */ - if (devCapture != NULL) { - streamFlags |= MA_PA_STREAM_DONT_MOVE; - } - - error = ((ma_pa_stream_connect_record_proc)pDevice->pContext->pulse.pa_stream_connect_record)((ma_pa_stream*)pDevice->pulse.pStreamCapture, devCapture, &attr, streamFlags); - if (error != MA_PA_OK) { - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[PulseAudio] Failed to connect PulseAudio capture stream."); - result = ma_result_from_pulse(error); - goto on_error1; - } - - result = ma_wait_for_pa_stream_to_connect__pulse(pDevice->pContext, pDevice->pulse.pMainLoop, (ma_pa_stream*)pDevice->pulse.pStreamCapture); - if (result != MA_SUCCESS) { - goto on_error2; - } - - - /* Internal format. */ - pActualSS = ((ma_pa_stream_get_sample_spec_proc)pDevice->pContext->pulse.pa_stream_get_sample_spec)((ma_pa_stream*)pDevice->pulse.pStreamCapture); - if (pActualSS != NULL) { - ss = *pActualSS; - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, "[PulseAudio] Capture sample spec: format=%s, channels=%d, rate=%d\n", ma_get_format_name(ma_format_from_pulse(ss.format)), ss.channels, ss.rate); - } else { - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, "[PulseAudio] Failed to retrieve capture sample spec.\n"); - } - - pDescriptorCapture->format = ma_format_from_pulse(ss.format); - pDescriptorCapture->channels = ss.channels; - pDescriptorCapture->sampleRate = ss.rate; - - if (pDescriptorCapture->format == ma_format_unknown || pDescriptorCapture->channels == 0 || pDescriptorCapture->sampleRate == 0) { - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[PulseAudio] Capture sample spec is invalid. Device unusable by miniaudio. format=%s, channels=%d, sampleRate=%d.\n", ma_get_format_name(pDescriptorCapture->format), pDescriptorCapture->channels, pDescriptorCapture->sampleRate); - result = MA_ERROR; - goto on_error4; - } - - - /* Internal channel map. */ - pActualChannelMap = ((ma_pa_stream_get_channel_map_proc)pDevice->pContext->pulse.pa_stream_get_channel_map)((ma_pa_stream*)pDevice->pulse.pStreamCapture); - if (pActualChannelMap == NULL) { - pActualChannelMap = &cmap; /* Fallback just in case. */ - } - - /* - Bug in PipeWire. There have been reports that PipeWire is returning AUX channels when reporting - the channel map. To somewhat workaround this, I'm hacking in a hard coded channel map for mono - and stereo. In this case it should be safe to assume mono = MONO and stereo = LEFT/RIGHT. For - all other channel counts we need to just put up with whatever PipeWire reports and hope it gets - fixed sooner than later. I might remove this hack later. - */ - if (pDescriptorCapture->channels > 2) { - for (iChannel = 0; iChannel < pDescriptorCapture->channels; iChannel += 1) { - pDescriptorCapture->channelMap[iChannel] = ma_channel_position_from_pulse(pActualChannelMap->map[iChannel]); - } - } else { - /* Hack for mono and stereo. */ - if (pDescriptorCapture->channels == 1) { - pDescriptorCapture->channelMap[0] = MA_CHANNEL_MONO; - } else if (pDescriptorCapture->channels == 2) { - pDescriptorCapture->channelMap[0] = MA_CHANNEL_FRONT_LEFT; - pDescriptorCapture->channelMap[1] = MA_CHANNEL_FRONT_RIGHT; - } else { - MA_ASSERT(MA_FALSE); /* Should never hit this. */ - } - } - - - /* Buffer. */ - pActualAttr = ((ma_pa_stream_get_buffer_attr_proc)pDevice->pContext->pulse.pa_stream_get_buffer_attr)((ma_pa_stream*)pDevice->pulse.pStreamCapture); - if (pActualAttr != NULL) { - attr = *pActualAttr; - } - - if (attr.fragsize > 0) { - pDescriptorCapture->periodCount = ma_max(attr.maxlength / attr.fragsize, 1); - } else { - pDescriptorCapture->periodCount = 1; - } - - pDescriptorCapture->periodSizeInFrames = attr.maxlength / ma_get_bytes_per_frame(pDescriptorCapture->format, pDescriptorCapture->channels) / pDescriptorCapture->periodCount; - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, "[PulseAudio] Capture actual attr: maxlength=%d, tlength=%d, prebuf=%d, minreq=%d, fragsize=%d; periodSizeInFrames=%d\n", attr.maxlength, attr.tlength, attr.prebuf, attr.minreq, attr.fragsize, pDescriptorCapture->periodSizeInFrames); - } - - if (pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) { - result = ma_context_get_sink_info__pulse(pDevice->pContext, devPlayback, &sinkInfo); - if (result != MA_SUCCESS) { - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[PulseAudio] Failed to retrieve sink info for playback device.\n"); - goto on_error2; - } - - ss = sinkInfo.sample_spec; - cmap = sinkInfo.channel_map; - - /* Use the requested channel count if we have one. */ - if (pDescriptorPlayback->channels != 0) { - ss.channels = pDescriptorPlayback->channels; - } - - /* Use a default channel map. */ - ((ma_pa_channel_map_init_extend_proc)pDevice->pContext->pulse.pa_channel_map_init_extend)(&cmap, ss.channels, pConfig->pulse.channelMap); - - - /* Use the requested sample rate if one was specified. */ - if (pDescriptorPlayback->sampleRate != 0) { - ss.rate = pDescriptorPlayback->sampleRate; - } - - streamFlags = MA_PA_STREAM_START_CORKED | MA_PA_STREAM_ADJUST_LATENCY; - if (ma_format_from_pulse(ss.format) == ma_format_unknown) { - if (ma_is_little_endian()) { - ss.format = MA_PA_SAMPLE_FLOAT32LE; - } else { - ss.format = MA_PA_SAMPLE_FLOAT32BE; - } - streamFlags |= MA_PA_STREAM_FIX_FORMAT; - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, "[PulseAudio] sample_spec.format not supported by miniaudio. Defaulting to PA_SAMPLE_FLOAT32.\n"); - } - if (ss.rate == 0) { - ss.rate = MA_DEFAULT_SAMPLE_RATE; - streamFlags |= MA_PA_STREAM_FIX_RATE; - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, "[PulseAudio] sample_spec.rate = 0. Defaulting to %d.\n", ss.rate); - } - if (ss.channels == 0) { - ss.channels = MA_DEFAULT_CHANNELS; - streamFlags |= MA_PA_STREAM_FIX_CHANNELS; - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, "[PulseAudio] sample_spec.channels = 0. Defaulting to %d.\n", ss.channels); - } - - /* We now have enough information to calculate the actual buffer size in frames. */ - pDescriptorPlayback->periodSizeInFrames = ma_calculate_period_size_in_frames_from_descriptor__pulse(pDescriptorPlayback, ss.rate, pConfig->performanceProfile); - - attr = ma_device__pa_buffer_attr_new(pDescriptorPlayback->periodSizeInFrames, pDescriptorPlayback->periodCount, &ss); - - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, "[PulseAudio] Playback attr: maxlength=%d, tlength=%d, prebuf=%d, minreq=%d, fragsize=%d; periodSizeInFrames=%d\n", attr.maxlength, attr.tlength, attr.prebuf, attr.minreq, attr.fragsize, pDescriptorPlayback->periodSizeInFrames); - - pDevice->pulse.pStreamPlayback = ma_device__pa_stream_new__pulse(pDevice, pConfig->pulse.pStreamNamePlayback, &ss, &cmap); - if (pDevice->pulse.pStreamPlayback == NULL) { - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[PulseAudio] Failed to create PulseAudio playback stream.\n"); - result = MA_ERROR; - goto on_error2; - } - - - /* - Note that this callback will be fired as soon as the stream is connected, even though it's started as corked. The callback needs to handle a - device state of ma_device_state_uninitialized. - */ - ((ma_pa_stream_set_write_callback_proc)pDevice->pContext->pulse.pa_stream_set_write_callback)((ma_pa_stream*)pDevice->pulse.pStreamPlayback, ma_device_on_write__pulse, pDevice); - - /* State callback for checking when the device has been corked. */ - ((ma_pa_stream_set_suspended_callback_proc)pDevice->pContext->pulse.pa_stream_set_suspended_callback)((ma_pa_stream*)pDevice->pulse.pStreamPlayback, ma_device_on_suspended__pulse, pDevice); - - /* Rerouting notification. */ - ((ma_pa_stream_set_moved_callback_proc)pDevice->pContext->pulse.pa_stream_set_moved_callback)((ma_pa_stream*)pDevice->pulse.pStreamPlayback, ma_device_on_rerouted__pulse, pDevice); - - - /* Connect after we've got all of our internal state set up. */ - if (devPlayback != NULL) { - streamFlags |= MA_PA_STREAM_DONT_MOVE; - } - - error = ((ma_pa_stream_connect_playback_proc)pDevice->pContext->pulse.pa_stream_connect_playback)((ma_pa_stream*)pDevice->pulse.pStreamPlayback, devPlayback, &attr, streamFlags, NULL, NULL); - if (error != MA_PA_OK) { - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[PulseAudio] Failed to connect PulseAudio playback stream."); - result = ma_result_from_pulse(error); - goto on_error3; - } - - result = ma_wait_for_pa_stream_to_connect__pulse(pDevice->pContext, pDevice->pulse.pMainLoop, (ma_pa_stream*)pDevice->pulse.pStreamPlayback); - if (result != MA_SUCCESS) { - goto on_error3; - } - - - /* Internal format. */ - pActualSS = ((ma_pa_stream_get_sample_spec_proc)pDevice->pContext->pulse.pa_stream_get_sample_spec)((ma_pa_stream*)pDevice->pulse.pStreamPlayback); - if (pActualSS != NULL) { - ss = *pActualSS; - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, "[PulseAudio] Playback sample spec: format=%s, channels=%d, rate=%d\n", ma_get_format_name(ma_format_from_pulse(ss.format)), ss.channels, ss.rate); - } else { - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, "[PulseAudio] Failed to retrieve playback sample spec.\n"); - } - - pDescriptorPlayback->format = ma_format_from_pulse(ss.format); - pDescriptorPlayback->channels = ss.channels; - pDescriptorPlayback->sampleRate = ss.rate; - - if (pDescriptorPlayback->format == ma_format_unknown || pDescriptorPlayback->channels == 0 || pDescriptorPlayback->sampleRate == 0) { - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[PulseAudio] Playback sample spec is invalid. Device unusable by miniaudio. format=%s, channels=%d, sampleRate=%d.\n", ma_get_format_name(pDescriptorPlayback->format), pDescriptorPlayback->channels, pDescriptorPlayback->sampleRate); - result = MA_ERROR; - goto on_error4; - } - - - /* Internal channel map. */ - pActualChannelMap = ((ma_pa_stream_get_channel_map_proc)pDevice->pContext->pulse.pa_stream_get_channel_map)((ma_pa_stream*)pDevice->pulse.pStreamPlayback); - if (pActualChannelMap == NULL) { - pActualChannelMap = &cmap; /* Fallback just in case. */ - } - - /* - Bug in PipeWire. There have been reports that PipeWire is returning AUX channels when reporting - the channel map. To somewhat workaround this, I'm hacking in a hard coded channel map for mono - and stereo. In this case it should be safe to assume mono = MONO and stereo = LEFT/RIGHT. For - all other channel counts we need to just put up with whatever PipeWire reports and hope it gets - fixed sooner than later. I might remove this hack later. - */ - if (pDescriptorPlayback->channels > 2) { - for (iChannel = 0; iChannel < pDescriptorPlayback->channels; iChannel += 1) { - pDescriptorPlayback->channelMap[iChannel] = ma_channel_position_from_pulse(pActualChannelMap->map[iChannel]); - } - } else { - /* Hack for mono and stereo. */ - if (pDescriptorPlayback->channels == 1) { - pDescriptorPlayback->channelMap[0] = MA_CHANNEL_MONO; - } else if (pDescriptorPlayback->channels == 2) { - pDescriptorPlayback->channelMap[0] = MA_CHANNEL_FRONT_LEFT; - pDescriptorPlayback->channelMap[1] = MA_CHANNEL_FRONT_RIGHT; - } else { - MA_ASSERT(MA_FALSE); /* Should never hit this. */ - } - } - - - /* Buffer. */ - pActualAttr = ((ma_pa_stream_get_buffer_attr_proc)pDevice->pContext->pulse.pa_stream_get_buffer_attr)((ma_pa_stream*)pDevice->pulse.pStreamPlayback); - if (pActualAttr != NULL) { - attr = *pActualAttr; - } - - if (attr.tlength > 0) { - pDescriptorPlayback->periodCount = ma_max(attr.maxlength / attr.tlength, 1); - } else { - pDescriptorPlayback->periodCount = 1; - } - - pDescriptorPlayback->periodSizeInFrames = attr.maxlength / ma_get_bytes_per_frame(pDescriptorPlayback->format, pDescriptorPlayback->channels) / pDescriptorPlayback->periodCount; - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, "[PulseAudio] Playback actual attr: maxlength=%d, tlength=%d, prebuf=%d, minreq=%d, fragsize=%d; internalPeriodSizeInFrames=%d\n", attr.maxlength, attr.tlength, attr.prebuf, attr.minreq, attr.fragsize, pDescriptorPlayback->periodSizeInFrames); - } - - - /* - We need a ring buffer for handling duplex mode. We can use the main duplex ring buffer in the main - part of the ma_device struct. We cannot, however, depend on ma_device_init() initializing this for - us later on because that will only do it if it's a fully asynchronous backend - i.e. the - onDeviceDataLoop callback is NULL, which is not the case for PulseAudio. - */ - if (pConfig->deviceType == ma_device_type_duplex) { - ma_format rbFormat = (format != ma_format_unknown) ? format : pDescriptorCapture->format; - ma_uint32 rbChannels = (channels > 0) ? channels : pDescriptorCapture->channels; - ma_uint32 rbSampleRate = (sampleRate > 0) ? sampleRate : pDescriptorCapture->sampleRate; - - result = ma_duplex_rb_init(rbFormat, rbChannels, rbSampleRate, pDescriptorCapture->sampleRate, pDescriptorCapture->periodSizeInFrames, &pDevice->pContext->allocationCallbacks, &pDevice->duplexRB); - if (result != MA_SUCCESS) { - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[PulseAudio] Failed to initialize ring buffer. %s.\n", ma_result_description(result)); - goto on_error4; - } - } - - return MA_SUCCESS; - - -on_error4: - if (pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) { - ((ma_pa_stream_disconnect_proc)pDevice->pContext->pulse.pa_stream_disconnect)((ma_pa_stream*)pDevice->pulse.pStreamPlayback); - } -on_error3: - if (pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) { - ((ma_pa_stream_unref_proc)pDevice->pContext->pulse.pa_stream_unref)((ma_pa_stream*)pDevice->pulse.pStreamPlayback); - } -on_error2: - if (pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) { - ((ma_pa_stream_disconnect_proc)pDevice->pContext->pulse.pa_stream_disconnect)((ma_pa_stream*)pDevice->pulse.pStreamCapture); - } -on_error1: - if (pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) { - ((ma_pa_stream_unref_proc)pDevice->pContext->pulse.pa_stream_unref)((ma_pa_stream*)pDevice->pulse.pStreamCapture); - } -on_error0: - return result; -} - - -static void ma_pulse_operation_complete_callback(ma_pa_stream* pStream, int success, void* pUserData) -{ - ma_bool32* pIsSuccessful = (ma_bool32*)pUserData; - MA_ASSERT(pIsSuccessful != NULL); - - *pIsSuccessful = (ma_bool32)success; - - (void)pStream; /* Unused. */ -} - -static ma_result ma_device__cork_stream__pulse(ma_device* pDevice, ma_device_type deviceType, int cork) -{ - ma_context* pContext = pDevice->pContext; - ma_bool32 wasSuccessful; - ma_pa_stream* pStream; - ma_pa_operation* pOP; - ma_result result; - - /* This should not be called with a duplex device type. */ - if (deviceType == ma_device_type_duplex) { - return MA_INVALID_ARGS; - } - - wasSuccessful = MA_FALSE; - - pStream = (ma_pa_stream*)((deviceType == ma_device_type_capture) ? pDevice->pulse.pStreamCapture : pDevice->pulse.pStreamPlayback); - MA_ASSERT(pStream != NULL); - - pOP = ((ma_pa_stream_cork_proc)pContext->pulse.pa_stream_cork)(pStream, cork, ma_pulse_operation_complete_callback, &wasSuccessful); - if (pOP == NULL) { - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[PulseAudio] Failed to cork PulseAudio stream."); - return MA_ERROR; - } - - result = ma_wait_for_operation_and_unref__pulse(pDevice->pContext, pDevice->pulse.pMainLoop, pOP); - if (result != MA_SUCCESS) { - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[PulseAudio] An error occurred while waiting for the PulseAudio stream to cork."); - return result; - } - - if (!wasSuccessful) { - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[PulseAudio] Failed to %s PulseAudio stream.", (cork) ? "stop" : "start"); - return MA_ERROR; - } - - return MA_SUCCESS; -} - -static ma_result ma_device_start__pulse(ma_device* pDevice) -{ - ma_result result; - - MA_ASSERT(pDevice != NULL); - - if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) { - result = ma_device__cork_stream__pulse(pDevice, ma_device_type_capture, 0); - if (result != MA_SUCCESS) { - return result; - } - } - - if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) { - /* - We need to fill some data before uncorking. Not doing this will result in the write callback - never getting fired. We're not going to abort if writing fails because I still want the device - to get uncorked. - */ - ma_device_write_to_stream__pulse(pDevice, (ma_pa_stream*)(pDevice->pulse.pStreamPlayback), NULL); /* No need to check the result here. Always want to fall through an uncork.*/ - - result = ma_device__cork_stream__pulse(pDevice, ma_device_type_playback, 0); - if (result != MA_SUCCESS) { - return result; - } - } - - return MA_SUCCESS; -} - -static ma_result ma_device_stop__pulse(ma_device* pDevice) -{ - ma_result result; - - MA_ASSERT(pDevice != NULL); - - if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) { - result = ma_device__cork_stream__pulse(pDevice, ma_device_type_capture, 1); - if (result != MA_SUCCESS) { - return result; - } - } - - if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) { - /* - Ideally we would drain the device here, but there's been cases where PulseAudio seems to be - broken on some systems to the point where no audio processing seems to happen. When this - happens, draining never completes and we get stuck here. For now I'm disabling draining of - the device so we don't just freeze the application. - */ - #if 0 - ma_pa_operation* pOP = ((ma_pa_stream_drain_proc)pDevice->pContext->pulse.pa_stream_drain)((ma_pa_stream*)pDevice->pulse.pStreamPlayback, ma_pulse_operation_complete_callback, &wasSuccessful); - ma_wait_for_operation_and_unref__pulse(pDevice->pContext, pDevice->pulse.pMainLoop, pOP); - #endif - - result = ma_device__cork_stream__pulse(pDevice, ma_device_type_playback, 1); - if (result != MA_SUCCESS) { - return result; - } - } - - return MA_SUCCESS; -} - -static ma_result ma_device_data_loop__pulse(ma_device* pDevice) -{ - int resultPA; - - MA_ASSERT(pDevice != NULL); - - /* NOTE: Don't start the device here. It'll be done at a higher level. */ - - /* - All data is handled through callbacks. All we need to do is iterate over the main loop and let - the callbacks deal with it. - */ - while (ma_device_get_state(pDevice) == ma_device_state_started) { - resultPA = ((ma_pa_mainloop_iterate_proc)pDevice->pContext->pulse.pa_mainloop_iterate)((ma_pa_mainloop*)pDevice->pulse.pMainLoop, 1, NULL); - if (resultPA < 0) { - break; - } - } - - /* NOTE: Don't stop the device here. It'll be done at a higher level. */ - return MA_SUCCESS; -} - -static ma_result ma_device_data_loop_wakeup__pulse(ma_device* pDevice) -{ - MA_ASSERT(pDevice != NULL); - - ((ma_pa_mainloop_wakeup_proc)pDevice->pContext->pulse.pa_mainloop_wakeup)((ma_pa_mainloop*)pDevice->pulse.pMainLoop); - - return MA_SUCCESS; -} - -static ma_result ma_context_uninit__pulse(ma_context* pContext) -{ - MA_ASSERT(pContext != NULL); - MA_ASSERT(pContext->backend == ma_backend_pulseaudio); - - ((ma_pa_context_disconnect_proc)pContext->pulse.pa_context_disconnect)((ma_pa_context*)pContext->pulse.pPulseContext); - ((ma_pa_context_unref_proc)pContext->pulse.pa_context_unref)((ma_pa_context*)pContext->pulse.pPulseContext); - ((ma_pa_mainloop_free_proc)pContext->pulse.pa_mainloop_free)((ma_pa_mainloop*)pContext->pulse.pMainLoop); - - ma_free(pContext->pulse.pServerName, &pContext->allocationCallbacks); - ma_free(pContext->pulse.pApplicationName, &pContext->allocationCallbacks); - -#ifndef MA_NO_RUNTIME_LINKING - ma_dlclose(ma_context_get_log(pContext), pContext->pulse.pulseSO); -#endif - - return MA_SUCCESS; -} - -static ma_result ma_context_init__pulse(ma_context* pContext, const ma_context_config* pConfig, ma_backend_callbacks* pCallbacks) -{ - ma_result result; -#ifndef MA_NO_RUNTIME_LINKING - const char* libpulseNames[] = { - "libpulse.so", - "libpulse.so.0" - }; - size_t i; - - for (i = 0; i < ma_countof(libpulseNames); ++i) { - pContext->pulse.pulseSO = ma_dlopen(ma_context_get_log(pContext), libpulseNames[i]); - if (pContext->pulse.pulseSO != NULL) { - break; - } - } - - if (pContext->pulse.pulseSO == NULL) { - return MA_NO_BACKEND; - } - - pContext->pulse.pa_mainloop_new = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_mainloop_new"); - pContext->pulse.pa_mainloop_free = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_mainloop_free"); - pContext->pulse.pa_mainloop_quit = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_mainloop_quit"); - pContext->pulse.pa_mainloop_get_api = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_mainloop_get_api"); - pContext->pulse.pa_mainloop_iterate = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_mainloop_iterate"); - pContext->pulse.pa_mainloop_wakeup = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_mainloop_wakeup"); - pContext->pulse.pa_threaded_mainloop_new = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_threaded_mainloop_new"); - pContext->pulse.pa_threaded_mainloop_free = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_threaded_mainloop_free"); - pContext->pulse.pa_threaded_mainloop_start = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_threaded_mainloop_start"); - pContext->pulse.pa_threaded_mainloop_stop = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_threaded_mainloop_stop"); - pContext->pulse.pa_threaded_mainloop_lock = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_threaded_mainloop_lock"); - pContext->pulse.pa_threaded_mainloop_unlock = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_threaded_mainloop_unlock"); - pContext->pulse.pa_threaded_mainloop_wait = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_threaded_mainloop_wait"); - pContext->pulse.pa_threaded_mainloop_signal = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_threaded_mainloop_signal"); - pContext->pulse.pa_threaded_mainloop_accept = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_threaded_mainloop_accept"); - pContext->pulse.pa_threaded_mainloop_get_retval = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_threaded_mainloop_get_retval"); - pContext->pulse.pa_threaded_mainloop_get_api = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_threaded_mainloop_get_api"); - pContext->pulse.pa_threaded_mainloop_in_thread = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_threaded_mainloop_in_thread"); - pContext->pulse.pa_threaded_mainloop_set_name = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_threaded_mainloop_set_name"); - pContext->pulse.pa_context_new = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_context_new"); - pContext->pulse.pa_context_unref = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_context_unref"); - pContext->pulse.pa_context_connect = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_context_connect"); - pContext->pulse.pa_context_disconnect = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_context_disconnect"); - pContext->pulse.pa_context_set_state_callback = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_context_set_state_callback"); - pContext->pulse.pa_context_get_state = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_context_get_state"); - pContext->pulse.pa_context_get_sink_info_list = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_context_get_sink_info_list"); - pContext->pulse.pa_context_get_source_info_list = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_context_get_source_info_list"); - pContext->pulse.pa_context_get_sink_info_by_name = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_context_get_sink_info_by_name"); - pContext->pulse.pa_context_get_source_info_by_name = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_context_get_source_info_by_name"); - pContext->pulse.pa_operation_unref = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_operation_unref"); - pContext->pulse.pa_operation_get_state = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_operation_get_state"); - pContext->pulse.pa_channel_map_init_extend = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_channel_map_init_extend"); - pContext->pulse.pa_channel_map_valid = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_channel_map_valid"); - pContext->pulse.pa_channel_map_compatible = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_channel_map_compatible"); - pContext->pulse.pa_stream_new = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_stream_new"); - pContext->pulse.pa_stream_unref = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_stream_unref"); - pContext->pulse.pa_stream_connect_playback = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_stream_connect_playback"); - pContext->pulse.pa_stream_connect_record = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_stream_connect_record"); - pContext->pulse.pa_stream_disconnect = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_stream_disconnect"); - pContext->pulse.pa_stream_get_state = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_stream_get_state"); - pContext->pulse.pa_stream_get_sample_spec = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_stream_get_sample_spec"); - pContext->pulse.pa_stream_get_channel_map = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_stream_get_channel_map"); - pContext->pulse.pa_stream_get_buffer_attr = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_stream_get_buffer_attr"); - pContext->pulse.pa_stream_set_buffer_attr = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_stream_set_buffer_attr"); - pContext->pulse.pa_stream_get_device_name = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_stream_get_device_name"); - pContext->pulse.pa_stream_set_write_callback = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_stream_set_write_callback"); - pContext->pulse.pa_stream_set_read_callback = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_stream_set_read_callback"); - pContext->pulse.pa_stream_set_suspended_callback = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_stream_set_suspended_callback"); - pContext->pulse.pa_stream_set_moved_callback = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_stream_set_moved_callback"); - pContext->pulse.pa_stream_is_suspended = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_stream_is_suspended"); - pContext->pulse.pa_stream_flush = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_stream_flush"); - pContext->pulse.pa_stream_drain = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_stream_drain"); - pContext->pulse.pa_stream_is_corked = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_stream_is_corked"); - pContext->pulse.pa_stream_cork = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_stream_cork"); - pContext->pulse.pa_stream_trigger = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_stream_trigger"); - pContext->pulse.pa_stream_begin_write = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_stream_begin_write"); - pContext->pulse.pa_stream_write = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_stream_write"); - pContext->pulse.pa_stream_peek = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_stream_peek"); - pContext->pulse.pa_stream_drop = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_stream_drop"); - pContext->pulse.pa_stream_writable_size = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_stream_writable_size"); - pContext->pulse.pa_stream_readable_size = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_stream_readable_size"); -#else - /* This strange assignment system is just for type safety. */ - ma_pa_mainloop_new_proc _pa_mainloop_new = pa_mainloop_new; - ma_pa_mainloop_free_proc _pa_mainloop_free = pa_mainloop_free; - ma_pa_mainloop_quit_proc _pa_mainloop_quit = pa_mainloop_quit; - ma_pa_mainloop_get_api_proc _pa_mainloop_get_api = pa_mainloop_get_api; - ma_pa_mainloop_iterate_proc _pa_mainloop_iterate = pa_mainloop_iterate; - ma_pa_mainloop_wakeup_proc _pa_mainloop_wakeup = pa_mainloop_wakeup; - ma_pa_threaded_mainloop_new_proc _pa_threaded_mainloop_new = pa_threaded_mainloop_new; - ma_pa_threaded_mainloop_free_proc _pa_threaded_mainloop_free = pa_threaded_mainloop_free; - ma_pa_threaded_mainloop_start_proc _pa_threaded_mainloop_start = pa_threaded_mainloop_start; - ma_pa_threaded_mainloop_stop_proc _pa_threaded_mainloop_stop = pa_threaded_mainloop_stop; - ma_pa_threaded_mainloop_lock_proc _pa_threaded_mainloop_lock = pa_threaded_mainloop_lock; - ma_pa_threaded_mainloop_unlock_proc _pa_threaded_mainloop_unlock = pa_threaded_mainloop_unlock; - ma_pa_threaded_mainloop_wait_proc _pa_threaded_mainloop_wait = pa_threaded_mainloop_wait; - ma_pa_threaded_mainloop_signal_proc _pa_threaded_mainloop_signal = pa_threaded_mainloop_signal; - ma_pa_threaded_mainloop_accept_proc _pa_threaded_mainloop_accept = pa_threaded_mainloop_accept; - ma_pa_threaded_mainloop_get_retval_proc _pa_threaded_mainloop_get_retval = pa_threaded_mainloop_get_retval; - ma_pa_threaded_mainloop_get_api_proc _pa_threaded_mainloop_get_api = pa_threaded_mainloop_get_api; - ma_pa_threaded_mainloop_in_thread_proc _pa_threaded_mainloop_in_thread = pa_threaded_mainloop_in_thread; - ma_pa_threaded_mainloop_set_name_proc _pa_threaded_mainloop_set_name = pa_threaded_mainloop_set_name; - ma_pa_context_new_proc _pa_context_new = pa_context_new; - ma_pa_context_unref_proc _pa_context_unref = pa_context_unref; - ma_pa_context_connect_proc _pa_context_connect = pa_context_connect; - ma_pa_context_disconnect_proc _pa_context_disconnect = pa_context_disconnect; - ma_pa_context_set_state_callback_proc _pa_context_set_state_callback = pa_context_set_state_callback; - ma_pa_context_get_state_proc _pa_context_get_state = pa_context_get_state; - ma_pa_context_get_sink_info_list_proc _pa_context_get_sink_info_list = pa_context_get_sink_info_list; - ma_pa_context_get_source_info_list_proc _pa_context_get_source_info_list = pa_context_get_source_info_list; - ma_pa_context_get_sink_info_by_name_proc _pa_context_get_sink_info_by_name = pa_context_get_sink_info_by_name; - ma_pa_context_get_source_info_by_name_proc _pa_context_get_source_info_by_name= pa_context_get_source_info_by_name; - ma_pa_operation_unref_proc _pa_operation_unref = pa_operation_unref; - ma_pa_operation_get_state_proc _pa_operation_get_state = pa_operation_get_state; - ma_pa_channel_map_init_extend_proc _pa_channel_map_init_extend = pa_channel_map_init_extend; - ma_pa_channel_map_valid_proc _pa_channel_map_valid = pa_channel_map_valid; - ma_pa_channel_map_compatible_proc _pa_channel_map_compatible = pa_channel_map_compatible; - ma_pa_stream_new_proc _pa_stream_new = pa_stream_new; - ma_pa_stream_unref_proc _pa_stream_unref = pa_stream_unref; - ma_pa_stream_connect_playback_proc _pa_stream_connect_playback = pa_stream_connect_playback; - ma_pa_stream_connect_record_proc _pa_stream_connect_record = pa_stream_connect_record; - ma_pa_stream_disconnect_proc _pa_stream_disconnect = pa_stream_disconnect; - ma_pa_stream_get_state_proc _pa_stream_get_state = pa_stream_get_state; - ma_pa_stream_get_sample_spec_proc _pa_stream_get_sample_spec = pa_stream_get_sample_spec; - ma_pa_stream_get_channel_map_proc _pa_stream_get_channel_map = pa_stream_get_channel_map; - ma_pa_stream_get_buffer_attr_proc _pa_stream_get_buffer_attr = pa_stream_get_buffer_attr; - ma_pa_stream_set_buffer_attr_proc _pa_stream_set_buffer_attr = pa_stream_set_buffer_attr; - ma_pa_stream_get_device_name_proc _pa_stream_get_device_name = pa_stream_get_device_name; - ma_pa_stream_set_write_callback_proc _pa_stream_set_write_callback = pa_stream_set_write_callback; - ma_pa_stream_set_read_callback_proc _pa_stream_set_read_callback = pa_stream_set_read_callback; - ma_pa_stream_set_suspended_callback_proc _pa_stream_set_suspended_callback = pa_stream_set_suspended_callback; - ma_pa_stream_set_moved_callback_proc _pa_stream_set_moved_callback = pa_stream_set_moved_callback; - ma_pa_stream_is_suspended_proc _pa_stream_is_suspended = pa_stream_is_suspended; - ma_pa_stream_flush_proc _pa_stream_flush = pa_stream_flush; - ma_pa_stream_drain_proc _pa_stream_drain = pa_stream_drain; - ma_pa_stream_is_corked_proc _pa_stream_is_corked = pa_stream_is_corked; - ma_pa_stream_cork_proc _pa_stream_cork = pa_stream_cork; - ma_pa_stream_trigger_proc _pa_stream_trigger = pa_stream_trigger; - ma_pa_stream_begin_write_proc _pa_stream_begin_write = pa_stream_begin_write; - ma_pa_stream_write_proc _pa_stream_write = pa_stream_write; - ma_pa_stream_peek_proc _pa_stream_peek = pa_stream_peek; - ma_pa_stream_drop_proc _pa_stream_drop = pa_stream_drop; - ma_pa_stream_writable_size_proc _pa_stream_writable_size = pa_stream_writable_size; - ma_pa_stream_readable_size_proc _pa_stream_readable_size = pa_stream_readable_size; - - pContext->pulse.pa_mainloop_new = (ma_proc)_pa_mainloop_new; - pContext->pulse.pa_mainloop_free = (ma_proc)_pa_mainloop_free; - pContext->pulse.pa_mainloop_quit = (ma_proc)_pa_mainloop_quit; - pContext->pulse.pa_mainloop_get_api = (ma_proc)_pa_mainloop_get_api; - pContext->pulse.pa_mainloop_iterate = (ma_proc)_pa_mainloop_iterate; - pContext->pulse.pa_mainloop_wakeup = (ma_proc)_pa_mainloop_wakeup; - pContext->pulse.pa_threaded_mainloop_new = (ma_proc)_pa_threaded_mainloop_new; - pContext->pulse.pa_threaded_mainloop_free = (ma_proc)_pa_threaded_mainloop_free; - pContext->pulse.pa_threaded_mainloop_start = (ma_proc)_pa_threaded_mainloop_start; - pContext->pulse.pa_threaded_mainloop_stop = (ma_proc)_pa_threaded_mainloop_stop; - pContext->pulse.pa_threaded_mainloop_lock = (ma_proc)_pa_threaded_mainloop_lock; - pContext->pulse.pa_threaded_mainloop_unlock = (ma_proc)_pa_threaded_mainloop_unlock; - pContext->pulse.pa_threaded_mainloop_wait = (ma_proc)_pa_threaded_mainloop_wait; - pContext->pulse.pa_threaded_mainloop_signal = (ma_proc)_pa_threaded_mainloop_signal; - pContext->pulse.pa_threaded_mainloop_accept = (ma_proc)_pa_threaded_mainloop_accept; - pContext->pulse.pa_threaded_mainloop_get_retval = (ma_proc)_pa_threaded_mainloop_get_retval; - pContext->pulse.pa_threaded_mainloop_get_api = (ma_proc)_pa_threaded_mainloop_get_api; - pContext->pulse.pa_threaded_mainloop_in_thread = (ma_proc)_pa_threaded_mainloop_in_thread; - pContext->pulse.pa_threaded_mainloop_set_name = (ma_proc)_pa_threaded_mainloop_set_name; - pContext->pulse.pa_context_new = (ma_proc)_pa_context_new; - pContext->pulse.pa_context_unref = (ma_proc)_pa_context_unref; - pContext->pulse.pa_context_connect = (ma_proc)_pa_context_connect; - pContext->pulse.pa_context_disconnect = (ma_proc)_pa_context_disconnect; - pContext->pulse.pa_context_set_state_callback = (ma_proc)_pa_context_set_state_callback; - pContext->pulse.pa_context_get_state = (ma_proc)_pa_context_get_state; - pContext->pulse.pa_context_get_sink_info_list = (ma_proc)_pa_context_get_sink_info_list; - pContext->pulse.pa_context_get_source_info_list = (ma_proc)_pa_context_get_source_info_list; - pContext->pulse.pa_context_get_sink_info_by_name = (ma_proc)_pa_context_get_sink_info_by_name; - pContext->pulse.pa_context_get_source_info_by_name = (ma_proc)_pa_context_get_source_info_by_name; - pContext->pulse.pa_operation_unref = (ma_proc)_pa_operation_unref; - pContext->pulse.pa_operation_get_state = (ma_proc)_pa_operation_get_state; - pContext->pulse.pa_channel_map_init_extend = (ma_proc)_pa_channel_map_init_extend; - pContext->pulse.pa_channel_map_valid = (ma_proc)_pa_channel_map_valid; - pContext->pulse.pa_channel_map_compatible = (ma_proc)_pa_channel_map_compatible; - pContext->pulse.pa_stream_new = (ma_proc)_pa_stream_new; - pContext->pulse.pa_stream_unref = (ma_proc)_pa_stream_unref; - pContext->pulse.pa_stream_connect_playback = (ma_proc)_pa_stream_connect_playback; - pContext->pulse.pa_stream_connect_record = (ma_proc)_pa_stream_connect_record; - pContext->pulse.pa_stream_disconnect = (ma_proc)_pa_stream_disconnect; - pContext->pulse.pa_stream_get_state = (ma_proc)_pa_stream_get_state; - pContext->pulse.pa_stream_get_sample_spec = (ma_proc)_pa_stream_get_sample_spec; - pContext->pulse.pa_stream_get_channel_map = (ma_proc)_pa_stream_get_channel_map; - pContext->pulse.pa_stream_get_buffer_attr = (ma_proc)_pa_stream_get_buffer_attr; - pContext->pulse.pa_stream_set_buffer_attr = (ma_proc)_pa_stream_set_buffer_attr; - pContext->pulse.pa_stream_get_device_name = (ma_proc)_pa_stream_get_device_name; - pContext->pulse.pa_stream_set_write_callback = (ma_proc)_pa_stream_set_write_callback; - pContext->pulse.pa_stream_set_read_callback = (ma_proc)_pa_stream_set_read_callback; - pContext->pulse.pa_stream_set_suspended_callback = (ma_proc)_pa_stream_set_suspended_callback; - pContext->pulse.pa_stream_set_moved_callback = (ma_proc)_pa_stream_set_moved_callback; - pContext->pulse.pa_stream_is_suspended = (ma_proc)_pa_stream_is_suspended; - pContext->pulse.pa_stream_flush = (ma_proc)_pa_stream_flush; - pContext->pulse.pa_stream_drain = (ma_proc)_pa_stream_drain; - pContext->pulse.pa_stream_is_corked = (ma_proc)_pa_stream_is_corked; - pContext->pulse.pa_stream_cork = (ma_proc)_pa_stream_cork; - pContext->pulse.pa_stream_trigger = (ma_proc)_pa_stream_trigger; - pContext->pulse.pa_stream_begin_write = (ma_proc)_pa_stream_begin_write; - pContext->pulse.pa_stream_write = (ma_proc)_pa_stream_write; - pContext->pulse.pa_stream_peek = (ma_proc)_pa_stream_peek; - pContext->pulse.pa_stream_drop = (ma_proc)_pa_stream_drop; - pContext->pulse.pa_stream_writable_size = (ma_proc)_pa_stream_writable_size; - pContext->pulse.pa_stream_readable_size = (ma_proc)_pa_stream_readable_size; -#endif - - /* We need to make a copy of the application and server names so we can pass them to the pa_context of each device. */ - pContext->pulse.pApplicationName = ma_copy_string(pConfig->pulse.pApplicationName, &pContext->allocationCallbacks); - if (pContext->pulse.pApplicationName == NULL && pConfig->pulse.pApplicationName != NULL) { - return MA_OUT_OF_MEMORY; - } - - pContext->pulse.pServerName = ma_copy_string(pConfig->pulse.pServerName, &pContext->allocationCallbacks); - if (pContext->pulse.pServerName == NULL && pConfig->pulse.pServerName != NULL) { - ma_free(pContext->pulse.pApplicationName, &pContext->allocationCallbacks); - return MA_OUT_OF_MEMORY; - } - - result = ma_init_pa_mainloop_and_pa_context__pulse(pContext, pConfig->pulse.pApplicationName, pConfig->pulse.pServerName, pConfig->pulse.tryAutoSpawn, &pContext->pulse.pMainLoop, &pContext->pulse.pPulseContext); - if (result != MA_SUCCESS) { - ma_free(pContext->pulse.pServerName, &pContext->allocationCallbacks); - ma_free(pContext->pulse.pApplicationName, &pContext->allocationCallbacks); - #ifndef MA_NO_RUNTIME_LINKING - ma_dlclose(ma_context_get_log(pContext), pContext->pulse.pulseSO); - #endif - return result; - } - - /* With pa_mainloop we run a synchronous backend, but we implement our own main loop. */ - pCallbacks->onContextInit = ma_context_init__pulse; - pCallbacks->onContextUninit = ma_context_uninit__pulse; - pCallbacks->onContextEnumerateDevices = ma_context_enumerate_devices__pulse; - pCallbacks->onContextGetDeviceInfo = ma_context_get_device_info__pulse; - pCallbacks->onDeviceInit = ma_device_init__pulse; - pCallbacks->onDeviceUninit = ma_device_uninit__pulse; - pCallbacks->onDeviceStart = ma_device_start__pulse; - pCallbacks->onDeviceStop = ma_device_stop__pulse; - pCallbacks->onDeviceRead = NULL; /* Not used because we're implementing onDeviceDataLoop. */ - pCallbacks->onDeviceWrite = NULL; /* Not used because we're implementing onDeviceDataLoop. */ - pCallbacks->onDeviceDataLoop = ma_device_data_loop__pulse; - pCallbacks->onDeviceDataLoopWakeup = ma_device_data_loop_wakeup__pulse; - - return MA_SUCCESS; -} -#endif - - -/****************************************************************************** - -JACK Backend - -******************************************************************************/ -#ifdef MA_HAS_JACK - -/* It is assumed jack.h is available when compile-time linking is being used. */ -#ifdef MA_NO_RUNTIME_LINKING -#include - -typedef jack_nframes_t ma_jack_nframes_t; -typedef jack_options_t ma_jack_options_t; -typedef jack_status_t ma_jack_status_t; -typedef jack_client_t ma_jack_client_t; -typedef jack_port_t ma_jack_port_t; -typedef JackProcessCallback ma_JackProcessCallback; -typedef JackBufferSizeCallback ma_JackBufferSizeCallback; -typedef JackShutdownCallback ma_JackShutdownCallback; -#define MA_JACK_DEFAULT_AUDIO_TYPE JACK_DEFAULT_AUDIO_TYPE -#define ma_JackNoStartServer JackNoStartServer -#define ma_JackPortIsInput JackPortIsInput -#define ma_JackPortIsOutput JackPortIsOutput -#define ma_JackPortIsPhysical JackPortIsPhysical -#else -typedef ma_uint32 ma_jack_nframes_t; -typedef int ma_jack_options_t; -typedef int ma_jack_status_t; -typedef struct ma_jack_client_t ma_jack_client_t; -typedef struct ma_jack_port_t ma_jack_port_t; -typedef int (* ma_JackProcessCallback) (ma_jack_nframes_t nframes, void* arg); -typedef int (* ma_JackBufferSizeCallback)(ma_jack_nframes_t nframes, void* arg); -typedef void (* ma_JackShutdownCallback) (void* arg); -#define MA_JACK_DEFAULT_AUDIO_TYPE "32 bit float mono audio" -#define ma_JackNoStartServer 1 -#define ma_JackPortIsInput 1 -#define ma_JackPortIsOutput 2 -#define ma_JackPortIsPhysical 4 -#endif - -typedef ma_jack_client_t* (* ma_jack_client_open_proc) (const char* client_name, ma_jack_options_t options, ma_jack_status_t* status, ...); -typedef int (* ma_jack_client_close_proc) (ma_jack_client_t* client); -typedef int (* ma_jack_client_name_size_proc) (void); -typedef int (* ma_jack_set_process_callback_proc) (ma_jack_client_t* client, ma_JackProcessCallback process_callback, void* arg); -typedef int (* ma_jack_set_buffer_size_callback_proc)(ma_jack_client_t* client, ma_JackBufferSizeCallback bufsize_callback, void* arg); -typedef void (* ma_jack_on_shutdown_proc) (ma_jack_client_t* client, ma_JackShutdownCallback function, void* arg); -typedef ma_jack_nframes_t (* ma_jack_get_sample_rate_proc) (ma_jack_client_t* client); -typedef ma_jack_nframes_t (* ma_jack_get_buffer_size_proc) (ma_jack_client_t* client); -typedef const char** (* ma_jack_get_ports_proc) (ma_jack_client_t* client, const char* port_name_pattern, const char* type_name_pattern, unsigned long flags); -typedef int (* ma_jack_activate_proc) (ma_jack_client_t* client); -typedef int (* ma_jack_deactivate_proc) (ma_jack_client_t* client); -typedef int (* ma_jack_connect_proc) (ma_jack_client_t* client, const char* source_port, const char* destination_port); -typedef ma_jack_port_t* (* ma_jack_port_register_proc) (ma_jack_client_t* client, const char* port_name, const char* port_type, unsigned long flags, unsigned long buffer_size); -typedef const char* (* ma_jack_port_name_proc) (const ma_jack_port_t* port); -typedef void* (* ma_jack_port_get_buffer_proc) (ma_jack_port_t* port, ma_jack_nframes_t nframes); -typedef void (* ma_jack_free_proc) (void* ptr); - -static ma_result ma_context_open_client__jack(ma_context* pContext, ma_jack_client_t** ppClient) -{ - size_t maxClientNameSize; - char clientName[256]; - ma_jack_status_t status; - ma_jack_client_t* pClient; - - MA_ASSERT(pContext != NULL); - MA_ASSERT(ppClient != NULL); - - if (ppClient) { - *ppClient = NULL; - } - - maxClientNameSize = ((ma_jack_client_name_size_proc)pContext->jack.jack_client_name_size)(); /* Includes null terminator. */ - ma_strncpy_s(clientName, ma_min(sizeof(clientName), maxClientNameSize), (pContext->jack.pClientName != NULL) ? pContext->jack.pClientName : "miniaudio", (size_t)-1); - - pClient = ((ma_jack_client_open_proc)pContext->jack.jack_client_open)(clientName, (pContext->jack.tryStartServer) ? 0 : ma_JackNoStartServer, &status, NULL); - if (pClient == NULL) { - return MA_FAILED_TO_OPEN_BACKEND_DEVICE; - } - - if (ppClient) { - *ppClient = pClient; - } - - return MA_SUCCESS; -} - - -static ma_result ma_context_enumerate_devices__jack(ma_context* pContext, ma_enum_devices_callback_proc callback, void* pUserData) -{ - ma_bool32 cbResult = MA_TRUE; - - MA_ASSERT(pContext != NULL); - MA_ASSERT(callback != NULL); - - /* Playback. */ - if (cbResult) { - ma_device_info deviceInfo; - MA_ZERO_OBJECT(&deviceInfo); - ma_strncpy_s(deviceInfo.name, sizeof(deviceInfo.name), MA_DEFAULT_PLAYBACK_DEVICE_NAME, (size_t)-1); - deviceInfo.isDefault = MA_TRUE; /* JACK only uses default devices. */ - cbResult = callback(pContext, ma_device_type_playback, &deviceInfo, pUserData); - } - - /* Capture. */ - if (cbResult) { - ma_device_info deviceInfo; - MA_ZERO_OBJECT(&deviceInfo); - ma_strncpy_s(deviceInfo.name, sizeof(deviceInfo.name), MA_DEFAULT_CAPTURE_DEVICE_NAME, (size_t)-1); - deviceInfo.isDefault = MA_TRUE; /* JACK only uses default devices. */ - cbResult = callback(pContext, ma_device_type_capture, &deviceInfo, pUserData); - } - - (void)cbResult; /* For silencing a static analysis warning. */ - - return MA_SUCCESS; -} - -static ma_result ma_context_get_device_info__jack(ma_context* pContext, ma_device_type deviceType, const ma_device_id* pDeviceID, ma_device_info* pDeviceInfo) -{ - ma_jack_client_t* pClient; - ma_result result; - const char** ppPorts; - - MA_ASSERT(pContext != NULL); - - if (pDeviceID != NULL && pDeviceID->jack != 0) { - return MA_NO_DEVICE; /* Don't know the device. */ - } - - /* Name / Description */ - if (deviceType == ma_device_type_playback) { - ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), MA_DEFAULT_PLAYBACK_DEVICE_NAME, (size_t)-1); - } else { - ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), MA_DEFAULT_CAPTURE_DEVICE_NAME, (size_t)-1); - } - - /* Jack only uses default devices. */ - pDeviceInfo->isDefault = MA_TRUE; - - /* Jack only supports f32 and has a specific channel count and sample rate. */ - pDeviceInfo->nativeDataFormats[0].format = ma_format_f32; - - /* The channel count and sample rate can only be determined by opening the device. */ - result = ma_context_open_client__jack(pContext, &pClient); - if (result != MA_SUCCESS) { - ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[JACK] Failed to open client."); - return result; - } - - pDeviceInfo->nativeDataFormats[0].sampleRate = ((ma_jack_get_sample_rate_proc)pContext->jack.jack_get_sample_rate)((ma_jack_client_t*)pClient); - pDeviceInfo->nativeDataFormats[0].channels = 0; - - ppPorts = ((ma_jack_get_ports_proc)pContext->jack.jack_get_ports)((ma_jack_client_t*)pClient, NULL, MA_JACK_DEFAULT_AUDIO_TYPE, ma_JackPortIsPhysical | ((deviceType == ma_device_type_playback) ? ma_JackPortIsInput : ma_JackPortIsOutput)); - if (ppPorts == NULL) { - ((ma_jack_client_close_proc)pContext->jack.jack_client_close)((ma_jack_client_t*)pClient); - ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[JACK] Failed to query physical ports."); - return MA_FAILED_TO_OPEN_BACKEND_DEVICE; - } - - while (ppPorts[pDeviceInfo->nativeDataFormats[0].channels] != NULL) { - pDeviceInfo->nativeDataFormats[0].channels += 1; - } - - pDeviceInfo->nativeDataFormats[0].flags = 0; - pDeviceInfo->nativeDataFormatCount = 1; - - ((ma_jack_free_proc)pContext->jack.jack_free)((void*)ppPorts); - ((ma_jack_client_close_proc)pContext->jack.jack_client_close)((ma_jack_client_t*)pClient); - - (void)pContext; - return MA_SUCCESS; -} - - -static ma_result ma_device_uninit__jack(ma_device* pDevice) -{ - ma_context* pContext; - - MA_ASSERT(pDevice != NULL); - - pContext = pDevice->pContext; - MA_ASSERT(pContext != NULL); - - if (pDevice->jack.pClient != NULL) { - ((ma_jack_client_close_proc)pContext->jack.jack_client_close)((ma_jack_client_t*)pDevice->jack.pClient); - } - - if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) { - ma_free(pDevice->jack.pIntermediaryBufferCapture, &pDevice->pContext->allocationCallbacks); - ma_free(pDevice->jack.ppPortsCapture, &pDevice->pContext->allocationCallbacks); - } - - if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) { - ma_free(pDevice->jack.pIntermediaryBufferPlayback, &pDevice->pContext->allocationCallbacks); - ma_free(pDevice->jack.ppPortsPlayback, &pDevice->pContext->allocationCallbacks); - } - - return MA_SUCCESS; -} - -static void ma_device__jack_shutdown_callback(void* pUserData) -{ - /* JACK died. Stop the device. */ - ma_device* pDevice = (ma_device*)pUserData; - MA_ASSERT(pDevice != NULL); - - ma_device_stop(pDevice); -} - -static int ma_device__jack_buffer_size_callback(ma_jack_nframes_t frameCount, void* pUserData) -{ - ma_device* pDevice = (ma_device*)pUserData; - MA_ASSERT(pDevice != NULL); - - if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) { - size_t newBufferSize = frameCount * (pDevice->capture.internalChannels * ma_get_bytes_per_sample(pDevice->capture.internalFormat)); - float* pNewBuffer = (float*)ma_calloc(newBufferSize, &pDevice->pContext->allocationCallbacks); - if (pNewBuffer == NULL) { - return MA_OUT_OF_MEMORY; - } - - ma_free(pDevice->jack.pIntermediaryBufferCapture, &pDevice->pContext->allocationCallbacks); - - pDevice->jack.pIntermediaryBufferCapture = pNewBuffer; - pDevice->playback.internalPeriodSizeInFrames = frameCount; - } - - if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) { - size_t newBufferSize = frameCount * (pDevice->playback.internalChannels * ma_get_bytes_per_sample(pDevice->playback.internalFormat)); - float* pNewBuffer = (float*)ma_calloc(newBufferSize, &pDevice->pContext->allocationCallbacks); - if (pNewBuffer == NULL) { - return MA_OUT_OF_MEMORY; - } - - ma_free(pDevice->jack.pIntermediaryBufferPlayback, &pDevice->pContext->allocationCallbacks); - - pDevice->jack.pIntermediaryBufferPlayback = pNewBuffer; - pDevice->playback.internalPeriodSizeInFrames = frameCount; - } - - return 0; -} - -static int ma_device__jack_process_callback(ma_jack_nframes_t frameCount, void* pUserData) -{ - ma_device* pDevice; - ma_context* pContext; - ma_uint32 iChannel; - - pDevice = (ma_device*)pUserData; - MA_ASSERT(pDevice != NULL); - - pContext = pDevice->pContext; - MA_ASSERT(pContext != NULL); - - if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) { - /* Channels need to be interleaved. */ - for (iChannel = 0; iChannel < pDevice->capture.internalChannels; ++iChannel) { - const float* pSrc = (const float*)((ma_jack_port_get_buffer_proc)pContext->jack.jack_port_get_buffer)((ma_jack_port_t*)pDevice->jack.ppPortsCapture[iChannel], frameCount); - if (pSrc != NULL) { - float* pDst = pDevice->jack.pIntermediaryBufferCapture + iChannel; - ma_jack_nframes_t iFrame; - for (iFrame = 0; iFrame < frameCount; ++iFrame) { - *pDst = *pSrc; - - pDst += pDevice->capture.internalChannels; - pSrc += 1; - } - } - } - - ma_device_handle_backend_data_callback(pDevice, NULL, pDevice->jack.pIntermediaryBufferCapture, frameCount); - } - - if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) { - ma_device_handle_backend_data_callback(pDevice, pDevice->jack.pIntermediaryBufferPlayback, NULL, frameCount); - - /* Channels need to be deinterleaved. */ - for (iChannel = 0; iChannel < pDevice->playback.internalChannels; ++iChannel) { - float* pDst = (float*)((ma_jack_port_get_buffer_proc)pContext->jack.jack_port_get_buffer)((ma_jack_port_t*)pDevice->jack.ppPortsPlayback[iChannel], frameCount); - if (pDst != NULL) { - const float* pSrc = pDevice->jack.pIntermediaryBufferPlayback + iChannel; - ma_jack_nframes_t iFrame; - for (iFrame = 0; iFrame < frameCount; ++iFrame) { - *pDst = *pSrc; - - pDst += 1; - pSrc += pDevice->playback.internalChannels; - } - } - } - } - - return 0; -} - -static ma_result ma_device_init__jack(ma_device* pDevice, const ma_device_config* pConfig, ma_device_descriptor* pDescriptorPlayback, ma_device_descriptor* pDescriptorCapture) -{ - ma_result result; - ma_uint32 periodSizeInFrames; - - MA_ASSERT(pConfig != NULL); - MA_ASSERT(pDevice != NULL); - - if (pConfig->deviceType == ma_device_type_loopback) { - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[JACK] Loopback mode not supported."); - return MA_DEVICE_TYPE_NOT_SUPPORTED; - } - - /* Only supporting default devices with JACK. */ - if (((pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) && pDescriptorPlayback->pDeviceID != NULL && pDescriptorPlayback->pDeviceID->jack != 0) || - ((pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) && pDescriptorCapture->pDeviceID != NULL && pDescriptorCapture->pDeviceID->jack != 0)) { - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[JACK] Only default devices are supported."); - return MA_NO_DEVICE; - } - - /* No exclusive mode with the JACK backend. */ - if (((pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) && pDescriptorPlayback->shareMode == ma_share_mode_exclusive) || - ((pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) && pDescriptorCapture->shareMode == ma_share_mode_exclusive)) { - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[JACK] Exclusive mode not supported."); - return MA_SHARE_MODE_NOT_SUPPORTED; - } - - /* Open the client. */ - result = ma_context_open_client__jack(pDevice->pContext, (ma_jack_client_t**)&pDevice->jack.pClient); - if (result != MA_SUCCESS) { - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[JACK] Failed to open client."); - return result; - } - - /* Callbacks. */ - if (((ma_jack_set_process_callback_proc)pDevice->pContext->jack.jack_set_process_callback)((ma_jack_client_t*)pDevice->jack.pClient, ma_device__jack_process_callback, pDevice) != 0) { - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[JACK] Failed to set process callback."); - return MA_FAILED_TO_OPEN_BACKEND_DEVICE; - } - if (((ma_jack_set_buffer_size_callback_proc)pDevice->pContext->jack.jack_set_buffer_size_callback)((ma_jack_client_t*)pDevice->jack.pClient, ma_device__jack_buffer_size_callback, pDevice) != 0) { - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[JACK] Failed to set buffer size callback."); - return MA_FAILED_TO_OPEN_BACKEND_DEVICE; - } - - ((ma_jack_on_shutdown_proc)pDevice->pContext->jack.jack_on_shutdown)((ma_jack_client_t*)pDevice->jack.pClient, ma_device__jack_shutdown_callback, pDevice); - - - /* The buffer size in frames can change. */ - periodSizeInFrames = ((ma_jack_get_buffer_size_proc)pDevice->pContext->jack.jack_get_buffer_size)((ma_jack_client_t*)pDevice->jack.pClient); - - if (pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) { - ma_uint32 iPort; - const char** ppPorts; - - pDescriptorCapture->format = ma_format_f32; - pDescriptorCapture->channels = 0; - pDescriptorCapture->sampleRate = ((ma_jack_get_sample_rate_proc)pDevice->pContext->jack.jack_get_sample_rate)((ma_jack_client_t*)pDevice->jack.pClient); - ma_channel_map_init_standard(ma_standard_channel_map_alsa, pDescriptorCapture->channelMap, ma_countof(pDescriptorCapture->channelMap), pDescriptorCapture->channels); - - ppPorts = ((ma_jack_get_ports_proc)pDevice->pContext->jack.jack_get_ports)((ma_jack_client_t*)pDevice->jack.pClient, NULL, MA_JACK_DEFAULT_AUDIO_TYPE, ma_JackPortIsPhysical | ma_JackPortIsOutput); - if (ppPorts == NULL) { - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[JACK] Failed to query physical ports."); - return MA_FAILED_TO_OPEN_BACKEND_DEVICE; - } - - /* Need to count the number of ports first so we can allocate some memory. */ - while (ppPorts[pDescriptorCapture->channels] != NULL) { - pDescriptorCapture->channels += 1; - } - - pDevice->jack.ppPortsCapture = (ma_ptr*)ma_malloc(sizeof(*pDevice->jack.ppPortsCapture) * pDescriptorCapture->channels, &pDevice->pContext->allocationCallbacks); - if (pDevice->jack.ppPortsCapture == NULL) { - return MA_OUT_OF_MEMORY; - } - - for (iPort = 0; iPort < pDescriptorCapture->channels; iPort += 1) { - char name[64]; - ma_strcpy_s(name, sizeof(name), "capture"); - ma_itoa_s((int)iPort, name+7, sizeof(name)-7, 10); /* 7 = length of "capture" */ - - pDevice->jack.ppPortsCapture[iPort] = ((ma_jack_port_register_proc)pDevice->pContext->jack.jack_port_register)((ma_jack_client_t*)pDevice->jack.pClient, name, MA_JACK_DEFAULT_AUDIO_TYPE, ma_JackPortIsInput, 0); - if (pDevice->jack.ppPortsCapture[iPort] == NULL) { - ((ma_jack_free_proc)pDevice->pContext->jack.jack_free)((void*)ppPorts); - ma_device_uninit__jack(pDevice); - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[JACK] Failed to register ports."); - return MA_FAILED_TO_OPEN_BACKEND_DEVICE; - } - } - - ((ma_jack_free_proc)pDevice->pContext->jack.jack_free)((void*)ppPorts); - - pDescriptorCapture->periodSizeInFrames = periodSizeInFrames; - pDescriptorCapture->periodCount = 1; /* There's no notion of a period in JACK. Just set to 1. */ - - pDevice->jack.pIntermediaryBufferCapture = (float*)ma_calloc(pDescriptorCapture->periodSizeInFrames * ma_get_bytes_per_frame(pDescriptorCapture->format, pDescriptorCapture->channels), &pDevice->pContext->allocationCallbacks); - if (pDevice->jack.pIntermediaryBufferCapture == NULL) { - ma_device_uninit__jack(pDevice); - return MA_OUT_OF_MEMORY; - } - } - - if (pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) { - ma_uint32 iPort; - const char** ppPorts; - - pDescriptorPlayback->format = ma_format_f32; - pDescriptorPlayback->channels = 0; - pDescriptorPlayback->sampleRate = ((ma_jack_get_sample_rate_proc)pDevice->pContext->jack.jack_get_sample_rate)((ma_jack_client_t*)pDevice->jack.pClient); - ma_channel_map_init_standard(ma_standard_channel_map_alsa, pDescriptorPlayback->channelMap, ma_countof(pDescriptorPlayback->channelMap), pDescriptorPlayback->channels); - - ppPorts = ((ma_jack_get_ports_proc)pDevice->pContext->jack.jack_get_ports)((ma_jack_client_t*)pDevice->jack.pClient, NULL, MA_JACK_DEFAULT_AUDIO_TYPE, ma_JackPortIsPhysical | ma_JackPortIsInput); - if (ppPorts == NULL) { - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[JACK] Failed to query physical ports."); - return MA_FAILED_TO_OPEN_BACKEND_DEVICE; - } - - /* Need to count the number of ports first so we can allocate some memory. */ - while (ppPorts[pDescriptorPlayback->channels] != NULL) { - pDescriptorPlayback->channels += 1; - } - - pDevice->jack.ppPortsPlayback = (ma_ptr*)ma_malloc(sizeof(*pDevice->jack.ppPortsPlayback) * pDescriptorPlayback->channels, &pDevice->pContext->allocationCallbacks); - if (pDevice->jack.ppPortsPlayback == NULL) { - ma_free(pDevice->jack.ppPortsCapture, &pDevice->pContext->allocationCallbacks); - return MA_OUT_OF_MEMORY; - } - - for (iPort = 0; iPort < pDescriptorPlayback->channels; iPort += 1) { - char name[64]; - ma_strcpy_s(name, sizeof(name), "playback"); - ma_itoa_s((int)iPort, name+8, sizeof(name)-8, 10); /* 8 = length of "playback" */ - - pDevice->jack.ppPortsPlayback[iPort] = ((ma_jack_port_register_proc)pDevice->pContext->jack.jack_port_register)((ma_jack_client_t*)pDevice->jack.pClient, name, MA_JACK_DEFAULT_AUDIO_TYPE, ma_JackPortIsOutput, 0); - if (pDevice->jack.ppPortsPlayback[iPort] == NULL) { - ((ma_jack_free_proc)pDevice->pContext->jack.jack_free)((void*)ppPorts); - ma_device_uninit__jack(pDevice); - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[JACK] Failed to register ports."); - return MA_FAILED_TO_OPEN_BACKEND_DEVICE; - } - } - - ((ma_jack_free_proc)pDevice->pContext->jack.jack_free)((void*)ppPorts); - - pDescriptorPlayback->periodSizeInFrames = periodSizeInFrames; - pDescriptorPlayback->periodCount = 1; /* There's no notion of a period in JACK. Just set to 1. */ - - pDevice->jack.pIntermediaryBufferPlayback = (float*)ma_calloc(pDescriptorPlayback->periodSizeInFrames * ma_get_bytes_per_frame(pDescriptorPlayback->format, pDescriptorPlayback->channels), &pDevice->pContext->allocationCallbacks); - if (pDevice->jack.pIntermediaryBufferPlayback == NULL) { - ma_device_uninit__jack(pDevice); - return MA_OUT_OF_MEMORY; - } - } - - return MA_SUCCESS; -} - - -static ma_result ma_device_start__jack(ma_device* pDevice) -{ - ma_context* pContext = pDevice->pContext; - int resultJACK; - size_t i; - - resultJACK = ((ma_jack_activate_proc)pContext->jack.jack_activate)((ma_jack_client_t*)pDevice->jack.pClient); - if (resultJACK != 0) { - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[JACK] Failed to activate the JACK client."); - return MA_FAILED_TO_START_BACKEND_DEVICE; - } - - if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) { - const char** ppServerPorts = ((ma_jack_get_ports_proc)pContext->jack.jack_get_ports)((ma_jack_client_t*)pDevice->jack.pClient, NULL, MA_JACK_DEFAULT_AUDIO_TYPE, ma_JackPortIsPhysical | ma_JackPortIsOutput); - if (ppServerPorts == NULL) { - ((ma_jack_deactivate_proc)pContext->jack.jack_deactivate)((ma_jack_client_t*)pDevice->jack.pClient); - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[JACK] Failed to retrieve physical ports."); - return MA_ERROR; - } - - for (i = 0; ppServerPorts[i] != NULL; ++i) { - const char* pServerPort = ppServerPorts[i]; - const char* pClientPort = ((ma_jack_port_name_proc)pContext->jack.jack_port_name)((ma_jack_port_t*)pDevice->jack.ppPortsCapture[i]); - - resultJACK = ((ma_jack_connect_proc)pContext->jack.jack_connect)((ma_jack_client_t*)pDevice->jack.pClient, pServerPort, pClientPort); - if (resultJACK != 0) { - ((ma_jack_free_proc)pContext->jack.jack_free)((void*)ppServerPorts); - ((ma_jack_deactivate_proc)pContext->jack.jack_deactivate)((ma_jack_client_t*)pDevice->jack.pClient); - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[JACK] Failed to connect ports."); - return MA_ERROR; - } - } - - ((ma_jack_free_proc)pContext->jack.jack_free)((void*)ppServerPorts); - } - - if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) { - const char** ppServerPorts = ((ma_jack_get_ports_proc)pContext->jack.jack_get_ports)((ma_jack_client_t*)pDevice->jack.pClient, NULL, MA_JACK_DEFAULT_AUDIO_TYPE, ma_JackPortIsPhysical | ma_JackPortIsInput); - if (ppServerPorts == NULL) { - ((ma_jack_deactivate_proc)pContext->jack.jack_deactivate)((ma_jack_client_t*)pDevice->jack.pClient); - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[JACK] Failed to retrieve physical ports."); - return MA_ERROR; - } - - for (i = 0; ppServerPorts[i] != NULL; ++i) { - const char* pServerPort = ppServerPorts[i]; - const char* pClientPort = ((ma_jack_port_name_proc)pContext->jack.jack_port_name)((ma_jack_port_t*)pDevice->jack.ppPortsPlayback[i]); - - resultJACK = ((ma_jack_connect_proc)pContext->jack.jack_connect)((ma_jack_client_t*)pDevice->jack.pClient, pClientPort, pServerPort); - if (resultJACK != 0) { - ((ma_jack_free_proc)pContext->jack.jack_free)((void*)ppServerPorts); - ((ma_jack_deactivate_proc)pContext->jack.jack_deactivate)((ma_jack_client_t*)pDevice->jack.pClient); - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[JACK] Failed to connect ports."); - return MA_ERROR; - } - } - - ((ma_jack_free_proc)pContext->jack.jack_free)((void*)ppServerPorts); - } - - return MA_SUCCESS; -} - -static ma_result ma_device_stop__jack(ma_device* pDevice) -{ - ma_context* pContext = pDevice->pContext; - - if (((ma_jack_deactivate_proc)pContext->jack.jack_deactivate)((ma_jack_client_t*)pDevice->jack.pClient) != 0) { - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[JACK] An error occurred when deactivating the JACK client."); - return MA_ERROR; - } - - ma_device__on_notification_stopped(pDevice); - - return MA_SUCCESS; -} - - -static ma_result ma_context_uninit__jack(ma_context* pContext) -{ - MA_ASSERT(pContext != NULL); - MA_ASSERT(pContext->backend == ma_backend_jack); - - ma_free(pContext->jack.pClientName, &pContext->allocationCallbacks); - pContext->jack.pClientName = NULL; - -#ifndef MA_NO_RUNTIME_LINKING - ma_dlclose(ma_context_get_log(pContext), pContext->jack.jackSO); -#endif - - return MA_SUCCESS; -} - -static ma_result ma_context_init__jack(ma_context* pContext, const ma_context_config* pConfig, ma_backend_callbacks* pCallbacks) -{ -#ifndef MA_NO_RUNTIME_LINKING - const char* libjackNames[] = { -#if defined(MA_WIN32) - "libjack.dll", - "libjack64.dll" -#endif -#if defined(MA_UNIX) - "libjack.so", - "libjack.so.0" -#endif - }; - size_t i; - - for (i = 0; i < ma_countof(libjackNames); ++i) { - pContext->jack.jackSO = ma_dlopen(ma_context_get_log(pContext), libjackNames[i]); - if (pContext->jack.jackSO != NULL) { - break; - } - } - - if (pContext->jack.jackSO == NULL) { - return MA_NO_BACKEND; - } - - pContext->jack.jack_client_open = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->jack.jackSO, "jack_client_open"); - pContext->jack.jack_client_close = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->jack.jackSO, "jack_client_close"); - pContext->jack.jack_client_name_size = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->jack.jackSO, "jack_client_name_size"); - pContext->jack.jack_set_process_callback = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->jack.jackSO, "jack_set_process_callback"); - pContext->jack.jack_set_buffer_size_callback = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->jack.jackSO, "jack_set_buffer_size_callback"); - pContext->jack.jack_on_shutdown = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->jack.jackSO, "jack_on_shutdown"); - pContext->jack.jack_get_sample_rate = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->jack.jackSO, "jack_get_sample_rate"); - pContext->jack.jack_get_buffer_size = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->jack.jackSO, "jack_get_buffer_size"); - pContext->jack.jack_get_ports = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->jack.jackSO, "jack_get_ports"); - pContext->jack.jack_activate = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->jack.jackSO, "jack_activate"); - pContext->jack.jack_deactivate = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->jack.jackSO, "jack_deactivate"); - pContext->jack.jack_connect = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->jack.jackSO, "jack_connect"); - pContext->jack.jack_port_register = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->jack.jackSO, "jack_port_register"); - pContext->jack.jack_port_name = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->jack.jackSO, "jack_port_name"); - pContext->jack.jack_port_get_buffer = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->jack.jackSO, "jack_port_get_buffer"); - pContext->jack.jack_free = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->jack.jackSO, "jack_free"); -#else - /* - This strange assignment system is here just to ensure type safety of miniaudio's function pointer - types. If anything differs slightly the compiler should throw a warning. - */ - ma_jack_client_open_proc _jack_client_open = jack_client_open; - ma_jack_client_close_proc _jack_client_close = jack_client_close; - ma_jack_client_name_size_proc _jack_client_name_size = jack_client_name_size; - ma_jack_set_process_callback_proc _jack_set_process_callback = jack_set_process_callback; - ma_jack_set_buffer_size_callback_proc _jack_set_buffer_size_callback = jack_set_buffer_size_callback; - ma_jack_on_shutdown_proc _jack_on_shutdown = jack_on_shutdown; - ma_jack_get_sample_rate_proc _jack_get_sample_rate = jack_get_sample_rate; - ma_jack_get_buffer_size_proc _jack_get_buffer_size = jack_get_buffer_size; - ma_jack_get_ports_proc _jack_get_ports = jack_get_ports; - ma_jack_activate_proc _jack_activate = jack_activate; - ma_jack_deactivate_proc _jack_deactivate = jack_deactivate; - ma_jack_connect_proc _jack_connect = jack_connect; - ma_jack_port_register_proc _jack_port_register = jack_port_register; - ma_jack_port_name_proc _jack_port_name = jack_port_name; - ma_jack_port_get_buffer_proc _jack_port_get_buffer = jack_port_get_buffer; - ma_jack_free_proc _jack_free = jack_free; - - pContext->jack.jack_client_open = (ma_proc)_jack_client_open; - pContext->jack.jack_client_close = (ma_proc)_jack_client_close; - pContext->jack.jack_client_name_size = (ma_proc)_jack_client_name_size; - pContext->jack.jack_set_process_callback = (ma_proc)_jack_set_process_callback; - pContext->jack.jack_set_buffer_size_callback = (ma_proc)_jack_set_buffer_size_callback; - pContext->jack.jack_on_shutdown = (ma_proc)_jack_on_shutdown; - pContext->jack.jack_get_sample_rate = (ma_proc)_jack_get_sample_rate; - pContext->jack.jack_get_buffer_size = (ma_proc)_jack_get_buffer_size; - pContext->jack.jack_get_ports = (ma_proc)_jack_get_ports; - pContext->jack.jack_activate = (ma_proc)_jack_activate; - pContext->jack.jack_deactivate = (ma_proc)_jack_deactivate; - pContext->jack.jack_connect = (ma_proc)_jack_connect; - pContext->jack.jack_port_register = (ma_proc)_jack_port_register; - pContext->jack.jack_port_name = (ma_proc)_jack_port_name; - pContext->jack.jack_port_get_buffer = (ma_proc)_jack_port_get_buffer; - pContext->jack.jack_free = (ma_proc)_jack_free; -#endif - - if (pConfig->jack.pClientName != NULL) { - pContext->jack.pClientName = ma_copy_string(pConfig->jack.pClientName, &pContext->allocationCallbacks); - } - pContext->jack.tryStartServer = pConfig->jack.tryStartServer; - - /* - Getting here means the JACK library is installed, but it doesn't necessarily mean it's usable. We need to quickly test this by connecting - a temporary client. - */ - { - ma_jack_client_t* pDummyClient; - ma_result result = ma_context_open_client__jack(pContext, &pDummyClient); - if (result != MA_SUCCESS) { - ma_free(pContext->jack.pClientName, &pContext->allocationCallbacks); - #ifndef MA_NO_RUNTIME_LINKING - ma_dlclose(ma_context_get_log(pContext), pContext->jack.jackSO); - #endif - return MA_NO_BACKEND; - } - - ((ma_jack_client_close_proc)pContext->jack.jack_client_close)((ma_jack_client_t*)pDummyClient); - } - - - pCallbacks->onContextInit = ma_context_init__jack; - pCallbacks->onContextUninit = ma_context_uninit__jack; - pCallbacks->onContextEnumerateDevices = ma_context_enumerate_devices__jack; - pCallbacks->onContextGetDeviceInfo = ma_context_get_device_info__jack; - pCallbacks->onDeviceInit = ma_device_init__jack; - pCallbacks->onDeviceUninit = ma_device_uninit__jack; - pCallbacks->onDeviceStart = ma_device_start__jack; - pCallbacks->onDeviceStop = ma_device_stop__jack; - pCallbacks->onDeviceRead = NULL; /* Not used because JACK is asynchronous. */ - pCallbacks->onDeviceWrite = NULL; /* Not used because JACK is asynchronous. */ - pCallbacks->onDeviceDataLoop = NULL; /* Not used because JACK is asynchronous. */ - - return MA_SUCCESS; -} -#endif /* MA_HAS_JACK */ - - - -/****************************************************************************** - -Core Audio Backend - -References -========== -- Technical Note TN2091: Device input using the HAL Output Audio Unit - https://developer.apple.com/library/archive/technotes/tn2091/_index.html - -******************************************************************************/ -#ifdef MA_HAS_COREAUDIO -#include - -#if defined(TARGET_OS_IPHONE) && TARGET_OS_IPHONE == 1 - #define MA_APPLE_MOBILE - #if defined(TARGET_OS_TV) && TARGET_OS_TV == 1 - #define MA_APPLE_TV - #endif - #if defined(TARGET_OS_WATCH) && TARGET_OS_WATCH == 1 - #define MA_APPLE_WATCH - #endif - #if __has_feature(objc_arc) - #define MA_BRIDGE_TRANSFER __bridge_transfer - #define MA_BRIDGE_RETAINED __bridge_retained - #else - #define MA_BRIDGE_TRANSFER - #define MA_BRIDGE_RETAINED - #endif -#else - #define MA_APPLE_DESKTOP -#endif - -#if defined(MA_APPLE_DESKTOP) -#include -#else -#include -#endif - -#include - -/* CoreFoundation */ -typedef Boolean (* ma_CFStringGetCString_proc)(CFStringRef theString, char* buffer, CFIndex bufferSize, CFStringEncoding encoding); -typedef void (* ma_CFRelease_proc)(CFTypeRef cf); - -/* CoreAudio */ -#if defined(MA_APPLE_DESKTOP) -typedef OSStatus (* ma_AudioObjectGetPropertyData_proc)(AudioObjectID inObjectID, const AudioObjectPropertyAddress* inAddress, UInt32 inQualifierDataSize, const void* inQualifierData, UInt32* ioDataSize, void* outData); -typedef OSStatus (* ma_AudioObjectGetPropertyDataSize_proc)(AudioObjectID inObjectID, const AudioObjectPropertyAddress* inAddress, UInt32 inQualifierDataSize, const void* inQualifierData, UInt32* outDataSize); -typedef OSStatus (* ma_AudioObjectSetPropertyData_proc)(AudioObjectID inObjectID, const AudioObjectPropertyAddress* inAddress, UInt32 inQualifierDataSize, const void* inQualifierData, UInt32 inDataSize, const void* inData); -typedef OSStatus (* ma_AudioObjectAddPropertyListener_proc)(AudioObjectID inObjectID, const AudioObjectPropertyAddress* inAddress, AudioObjectPropertyListenerProc inListener, void* inClientData); -typedef OSStatus (* ma_AudioObjectRemovePropertyListener_proc)(AudioObjectID inObjectID, const AudioObjectPropertyAddress* inAddress, AudioObjectPropertyListenerProc inListener, void* inClientData); -#endif - -/* AudioToolbox */ -typedef AudioComponent (* ma_AudioComponentFindNext_proc)(AudioComponent inComponent, const AudioComponentDescription* inDesc); -typedef OSStatus (* ma_AudioComponentInstanceDispose_proc)(AudioComponentInstance inInstance); -typedef OSStatus (* ma_AudioComponentInstanceNew_proc)(AudioComponent inComponent, AudioComponentInstance* outInstance); -typedef OSStatus (* ma_AudioOutputUnitStart_proc)(AudioUnit inUnit); -typedef OSStatus (* ma_AudioOutputUnitStop_proc)(AudioUnit inUnit); -typedef OSStatus (* ma_AudioUnitAddPropertyListener_proc)(AudioUnit inUnit, AudioUnitPropertyID inID, AudioUnitPropertyListenerProc inProc, void* inProcUserData); -typedef OSStatus (* ma_AudioUnitGetPropertyInfo_proc)(AudioUnit inUnit, AudioUnitPropertyID inID, AudioUnitScope inScope, AudioUnitElement inElement, UInt32* outDataSize, Boolean* outWriteable); -typedef OSStatus (* ma_AudioUnitGetProperty_proc)(AudioUnit inUnit, AudioUnitPropertyID inID, AudioUnitScope inScope, AudioUnitElement inElement, void* outData, UInt32* ioDataSize); -typedef OSStatus (* ma_AudioUnitSetProperty_proc)(AudioUnit inUnit, AudioUnitPropertyID inID, AudioUnitScope inScope, AudioUnitElement inElement, const void* inData, UInt32 inDataSize); -typedef OSStatus (* ma_AudioUnitInitialize_proc)(AudioUnit inUnit); -typedef OSStatus (* ma_AudioUnitRender_proc)(AudioUnit inUnit, AudioUnitRenderActionFlags* ioActionFlags, const AudioTimeStamp* inTimeStamp, UInt32 inOutputBusNumber, UInt32 inNumberFrames, AudioBufferList* ioData); - - -#define MA_COREAUDIO_OUTPUT_BUS 0 -#define MA_COREAUDIO_INPUT_BUS 1 - -#if defined(MA_APPLE_DESKTOP) -static ma_result ma_device_reinit_internal__coreaudio(ma_device* pDevice, ma_device_type deviceType, ma_bool32 disposePreviousAudioUnit); -#endif - -/* -Core Audio - -So far, Core Audio has been the worst backend to work with due to being both unintuitive and having almost no documentation -apart from comments in the headers (which admittedly are quite good). For my own purposes, and for anybody out there whose -needing to figure out how this darn thing works, I'm going to outline a few things here. - -Since miniaudio is a fairly low-level API, one of the things it needs is control over specific devices, and it needs to be -able to identify whether or not it can be used as playback and/or capture. The AudioObject API is the only one I've seen -that supports this level of detail. There was some public domain sample code I stumbled across that used the AudioComponent -and AudioUnit APIs, but I couldn't see anything that gave low-level control over device selection and capabilities (the -distinction between playback and capture in particular). Therefore, miniaudio is using the AudioObject API. - -Most (all?) functions in the AudioObject API take a AudioObjectID as its input. This is the device identifier. When -retrieving global information, such as the device list, you use kAudioObjectSystemObject. When retrieving device-specific -data, you pass in the ID for that device. In order to retrieve device-specific IDs you need to enumerate over each of the -devices. This is done using the AudioObjectGetPropertyDataSize() and AudioObjectGetPropertyData() APIs which seem to be -the central APIs for retrieving information about the system and specific devices. - -To use the AudioObjectGetPropertyData() API you need to use the notion of a property address. A property address is a -structure with three variables and is used to identify which property you are getting or setting. The first is the "selector" -which is basically the specific property that you're wanting to retrieve or set. The second is the "scope", which is -typically set to kAudioObjectPropertyScopeGlobal, kAudioObjectPropertyScopeInput for input-specific properties and -kAudioObjectPropertyScopeOutput for output-specific properties. The last is the "element" which is always set to -kAudioObjectPropertyElementMain in miniaudio's case. I don't know of any cases where this would be set to anything different. - -Back to the earlier issue of device retrieval, you first use the AudioObjectGetPropertyDataSize() API to retrieve the size -of the raw data which is just a list of AudioDeviceID's. You use the kAudioObjectSystemObject AudioObjectID, and a property -address with the kAudioHardwarePropertyDevices selector and the kAudioObjectPropertyScopeGlobal scope. Once you have the -size, allocate a block of memory of that size and then call AudioObjectGetPropertyData(). The data is just a list of -AudioDeviceID's so just do "dataSize/sizeof(AudioDeviceID)" to know the device count. -*/ - -#if defined(MA_APPLE_MOBILE) -static void ma_device__on_notification_interruption_began(ma_device* pDevice) -{ - ma_device__on_notification(ma_device_notification_init(pDevice, ma_device_notification_type_interruption_began)); -} - -static void ma_device__on_notification_interruption_ended(ma_device* pDevice) -{ - ma_device__on_notification(ma_device_notification_init(pDevice, ma_device_notification_type_interruption_ended)); -} -#endif - -static ma_result ma_result_from_OSStatus(OSStatus status) -{ - switch (status) - { - case noErr: return MA_SUCCESS; - #if defined(MA_APPLE_DESKTOP) - case kAudioHardwareNotRunningError: return MA_DEVICE_NOT_STARTED; - case kAudioHardwareUnspecifiedError: return MA_ERROR; - case kAudioHardwareUnknownPropertyError: return MA_INVALID_ARGS; - case kAudioHardwareBadPropertySizeError: return MA_INVALID_OPERATION; - case kAudioHardwareIllegalOperationError: return MA_INVALID_OPERATION; - case kAudioHardwareBadObjectError: return MA_INVALID_ARGS; - case kAudioHardwareBadDeviceError: return MA_INVALID_ARGS; - case kAudioHardwareBadStreamError: return MA_INVALID_ARGS; - case kAudioHardwareUnsupportedOperationError: return MA_INVALID_OPERATION; - case kAudioDeviceUnsupportedFormatError: return MA_FORMAT_NOT_SUPPORTED; - case kAudioDevicePermissionsError: return MA_ACCESS_DENIED; - #endif - default: return MA_ERROR; - } -} - -#if 0 -static ma_channel ma_channel_from_AudioChannelBitmap(AudioChannelBitmap bit) -{ - switch (bit) - { - case kAudioChannelBit_Left: return MA_CHANNEL_LEFT; - case kAudioChannelBit_Right: return MA_CHANNEL_RIGHT; - case kAudioChannelBit_Center: return MA_CHANNEL_FRONT_CENTER; - case kAudioChannelBit_LFEScreen: return MA_CHANNEL_LFE; - case kAudioChannelBit_LeftSurround: return MA_CHANNEL_BACK_LEFT; - case kAudioChannelBit_RightSurround: return MA_CHANNEL_BACK_RIGHT; - case kAudioChannelBit_LeftCenter: return MA_CHANNEL_FRONT_LEFT_CENTER; - case kAudioChannelBit_RightCenter: return MA_CHANNEL_FRONT_RIGHT_CENTER; - case kAudioChannelBit_CenterSurround: return MA_CHANNEL_BACK_CENTER; - case kAudioChannelBit_LeftSurroundDirect: return MA_CHANNEL_SIDE_LEFT; - case kAudioChannelBit_RightSurroundDirect: return MA_CHANNEL_SIDE_RIGHT; - case kAudioChannelBit_TopCenterSurround: return MA_CHANNEL_TOP_CENTER; - case kAudioChannelBit_VerticalHeightLeft: return MA_CHANNEL_TOP_FRONT_LEFT; - case kAudioChannelBit_VerticalHeightCenter: return MA_CHANNEL_TOP_FRONT_CENTER; - case kAudioChannelBit_VerticalHeightRight: return MA_CHANNEL_TOP_FRONT_RIGHT; - case kAudioChannelBit_TopBackLeft: return MA_CHANNEL_TOP_BACK_LEFT; - case kAudioChannelBit_TopBackCenter: return MA_CHANNEL_TOP_BACK_CENTER; - case kAudioChannelBit_TopBackRight: return MA_CHANNEL_TOP_BACK_RIGHT; - default: return MA_CHANNEL_NONE; - } -} -#endif - -static ma_result ma_format_from_AudioStreamBasicDescription(const AudioStreamBasicDescription* pDescription, ma_format* pFormatOut) -{ - MA_ASSERT(pDescription != NULL); - MA_ASSERT(pFormatOut != NULL); - - *pFormatOut = ma_format_unknown; /* Safety. */ - - /* There's a few things miniaudio doesn't support. */ - if (pDescription->mFormatID != kAudioFormatLinearPCM) { - return MA_FORMAT_NOT_SUPPORTED; - } - - /* We don't support any non-packed formats that are aligned high. */ - if ((pDescription->mFormatFlags & kLinearPCMFormatFlagIsAlignedHigh) != 0) { - return MA_FORMAT_NOT_SUPPORTED; - } - - /* Only supporting native-endian. */ - if ((ma_is_little_endian() && (pDescription->mFormatFlags & kAudioFormatFlagIsBigEndian) != 0) || (ma_is_big_endian() && (pDescription->mFormatFlags & kAudioFormatFlagIsBigEndian) == 0)) { - return MA_FORMAT_NOT_SUPPORTED; - } - - /* We are not currently supporting non-interleaved formats (this will be added in a future version of miniaudio). */ - /*if ((pDescription->mFormatFlags & kAudioFormatFlagIsNonInterleaved) != 0) { - return MA_FORMAT_NOT_SUPPORTED; - }*/ - - if ((pDescription->mFormatFlags & kLinearPCMFormatFlagIsFloat) != 0) { - if (pDescription->mBitsPerChannel == 32) { - *pFormatOut = ma_format_f32; - return MA_SUCCESS; - } - } else { - if ((pDescription->mFormatFlags & kLinearPCMFormatFlagIsSignedInteger) != 0) { - if (pDescription->mBitsPerChannel == 16) { - *pFormatOut = ma_format_s16; - return MA_SUCCESS; - } else if (pDescription->mBitsPerChannel == 24) { - if (pDescription->mBytesPerFrame == (pDescription->mBitsPerChannel/8 * pDescription->mChannelsPerFrame)) { - *pFormatOut = ma_format_s24; - return MA_SUCCESS; - } else { - if (pDescription->mBytesPerFrame/pDescription->mChannelsPerFrame == sizeof(ma_int32)) { - /* TODO: Implement ma_format_s24_32. */ - /**pFormatOut = ma_format_s24_32;*/ - /*return MA_SUCCESS;*/ - return MA_FORMAT_NOT_SUPPORTED; - } - } - } else if (pDescription->mBitsPerChannel == 32) { - *pFormatOut = ma_format_s32; - return MA_SUCCESS; - } - } else { - if (pDescription->mBitsPerChannel == 8) { - *pFormatOut = ma_format_u8; - return MA_SUCCESS; - } - } - } - - /* Getting here means the format is not supported. */ - return MA_FORMAT_NOT_SUPPORTED; -} - -#if defined(MA_APPLE_DESKTOP) -static ma_channel ma_channel_from_AudioChannelLabel(AudioChannelLabel label) -{ - switch (label) - { - case kAudioChannelLabel_Unknown: return MA_CHANNEL_NONE; - case kAudioChannelLabel_Unused: return MA_CHANNEL_NONE; - case kAudioChannelLabel_UseCoordinates: return MA_CHANNEL_NONE; - case kAudioChannelLabel_Left: return MA_CHANNEL_LEFT; - case kAudioChannelLabel_Right: return MA_CHANNEL_RIGHT; - case kAudioChannelLabel_Center: return MA_CHANNEL_FRONT_CENTER; - case kAudioChannelLabel_LFEScreen: return MA_CHANNEL_LFE; - case kAudioChannelLabel_LeftSurround: return MA_CHANNEL_BACK_LEFT; - case kAudioChannelLabel_RightSurround: return MA_CHANNEL_BACK_RIGHT; - case kAudioChannelLabel_LeftCenter: return MA_CHANNEL_FRONT_LEFT_CENTER; - case kAudioChannelLabel_RightCenter: return MA_CHANNEL_FRONT_RIGHT_CENTER; - case kAudioChannelLabel_CenterSurround: return MA_CHANNEL_BACK_CENTER; - case kAudioChannelLabel_LeftSurroundDirect: return MA_CHANNEL_SIDE_LEFT; - case kAudioChannelLabel_RightSurroundDirect: return MA_CHANNEL_SIDE_RIGHT; - case kAudioChannelLabel_TopCenterSurround: return MA_CHANNEL_TOP_CENTER; - case kAudioChannelLabel_VerticalHeightLeft: return MA_CHANNEL_TOP_FRONT_LEFT; - case kAudioChannelLabel_VerticalHeightCenter: return MA_CHANNEL_TOP_FRONT_CENTER; - case kAudioChannelLabel_VerticalHeightRight: return MA_CHANNEL_TOP_FRONT_RIGHT; - case kAudioChannelLabel_TopBackLeft: return MA_CHANNEL_TOP_BACK_LEFT; - case kAudioChannelLabel_TopBackCenter: return MA_CHANNEL_TOP_BACK_CENTER; - case kAudioChannelLabel_TopBackRight: return MA_CHANNEL_TOP_BACK_RIGHT; - case kAudioChannelLabel_RearSurroundLeft: return MA_CHANNEL_BACK_LEFT; - case kAudioChannelLabel_RearSurroundRight: return MA_CHANNEL_BACK_RIGHT; - case kAudioChannelLabel_LeftWide: return MA_CHANNEL_SIDE_LEFT; - case kAudioChannelLabel_RightWide: return MA_CHANNEL_SIDE_RIGHT; - case kAudioChannelLabel_LFE2: return MA_CHANNEL_LFE; - case kAudioChannelLabel_LeftTotal: return MA_CHANNEL_LEFT; - case kAudioChannelLabel_RightTotal: return MA_CHANNEL_RIGHT; - case kAudioChannelLabel_HearingImpaired: return MA_CHANNEL_NONE; - case kAudioChannelLabel_Narration: return MA_CHANNEL_MONO; - case kAudioChannelLabel_Mono: return MA_CHANNEL_MONO; - case kAudioChannelLabel_DialogCentricMix: return MA_CHANNEL_MONO; - case kAudioChannelLabel_CenterSurroundDirect: return MA_CHANNEL_BACK_CENTER; - case kAudioChannelLabel_Haptic: return MA_CHANNEL_NONE; - case kAudioChannelLabel_Ambisonic_W: return MA_CHANNEL_NONE; - case kAudioChannelLabel_Ambisonic_X: return MA_CHANNEL_NONE; - case kAudioChannelLabel_Ambisonic_Y: return MA_CHANNEL_NONE; - case kAudioChannelLabel_Ambisonic_Z: return MA_CHANNEL_NONE; - case kAudioChannelLabel_MS_Mid: return MA_CHANNEL_LEFT; - case kAudioChannelLabel_MS_Side: return MA_CHANNEL_RIGHT; - case kAudioChannelLabel_XY_X: return MA_CHANNEL_LEFT; - case kAudioChannelLabel_XY_Y: return MA_CHANNEL_RIGHT; - case kAudioChannelLabel_HeadphonesLeft: return MA_CHANNEL_LEFT; - case kAudioChannelLabel_HeadphonesRight: return MA_CHANNEL_RIGHT; - case kAudioChannelLabel_ClickTrack: return MA_CHANNEL_NONE; - case kAudioChannelLabel_ForeignLanguage: return MA_CHANNEL_NONE; - case kAudioChannelLabel_Discrete: return MA_CHANNEL_NONE; - case kAudioChannelLabel_Discrete_0: return MA_CHANNEL_AUX_0; - case kAudioChannelLabel_Discrete_1: return MA_CHANNEL_AUX_1; - case kAudioChannelLabel_Discrete_2: return MA_CHANNEL_AUX_2; - case kAudioChannelLabel_Discrete_3: return MA_CHANNEL_AUX_3; - case kAudioChannelLabel_Discrete_4: return MA_CHANNEL_AUX_4; - case kAudioChannelLabel_Discrete_5: return MA_CHANNEL_AUX_5; - case kAudioChannelLabel_Discrete_6: return MA_CHANNEL_AUX_6; - case kAudioChannelLabel_Discrete_7: return MA_CHANNEL_AUX_7; - case kAudioChannelLabel_Discrete_8: return MA_CHANNEL_AUX_8; - case kAudioChannelLabel_Discrete_9: return MA_CHANNEL_AUX_9; - case kAudioChannelLabel_Discrete_10: return MA_CHANNEL_AUX_10; - case kAudioChannelLabel_Discrete_11: return MA_CHANNEL_AUX_11; - case kAudioChannelLabel_Discrete_12: return MA_CHANNEL_AUX_12; - case kAudioChannelLabel_Discrete_13: return MA_CHANNEL_AUX_13; - case kAudioChannelLabel_Discrete_14: return MA_CHANNEL_AUX_14; - case kAudioChannelLabel_Discrete_15: return MA_CHANNEL_AUX_15; - case kAudioChannelLabel_Discrete_65535: return MA_CHANNEL_NONE; - - #if 0 /* Introduced in a later version of macOS. */ - case kAudioChannelLabel_HOA_ACN: return MA_CHANNEL_NONE; - case kAudioChannelLabel_HOA_ACN_0: return MA_CHANNEL_AUX_0; - case kAudioChannelLabel_HOA_ACN_1: return MA_CHANNEL_AUX_1; - case kAudioChannelLabel_HOA_ACN_2: return MA_CHANNEL_AUX_2; - case kAudioChannelLabel_HOA_ACN_3: return MA_CHANNEL_AUX_3; - case kAudioChannelLabel_HOA_ACN_4: return MA_CHANNEL_AUX_4; - case kAudioChannelLabel_HOA_ACN_5: return MA_CHANNEL_AUX_5; - case kAudioChannelLabel_HOA_ACN_6: return MA_CHANNEL_AUX_6; - case kAudioChannelLabel_HOA_ACN_7: return MA_CHANNEL_AUX_7; - case kAudioChannelLabel_HOA_ACN_8: return MA_CHANNEL_AUX_8; - case kAudioChannelLabel_HOA_ACN_9: return MA_CHANNEL_AUX_9; - case kAudioChannelLabel_HOA_ACN_10: return MA_CHANNEL_AUX_10; - case kAudioChannelLabel_HOA_ACN_11: return MA_CHANNEL_AUX_11; - case kAudioChannelLabel_HOA_ACN_12: return MA_CHANNEL_AUX_12; - case kAudioChannelLabel_HOA_ACN_13: return MA_CHANNEL_AUX_13; - case kAudioChannelLabel_HOA_ACN_14: return MA_CHANNEL_AUX_14; - case kAudioChannelLabel_HOA_ACN_15: return MA_CHANNEL_AUX_15; - case kAudioChannelLabel_HOA_ACN_65024: return MA_CHANNEL_NONE; - #endif - - default: return MA_CHANNEL_NONE; - } -} - -static ma_result ma_get_channel_map_from_AudioChannelLayout(AudioChannelLayout* pChannelLayout, ma_channel* pChannelMap, size_t channelMapCap) -{ - MA_ASSERT(pChannelLayout != NULL); - - if (pChannelLayout->mChannelLayoutTag == kAudioChannelLayoutTag_UseChannelDescriptions) { - UInt32 iChannel; - for (iChannel = 0; iChannel < pChannelLayout->mNumberChannelDescriptions && iChannel < channelMapCap; ++iChannel) { - pChannelMap[iChannel] = ma_channel_from_AudioChannelLabel(pChannelLayout->mChannelDescriptions[iChannel].mChannelLabel); - } - } else -#if 0 - if (pChannelLayout->mChannelLayoutTag == kAudioChannelLayoutTag_UseChannelBitmap) { - /* This is the same kind of system that's used by Windows audio APIs. */ - UInt32 iChannel = 0; - UInt32 iBit; - AudioChannelBitmap bitmap = pChannelLayout->mChannelBitmap; - for (iBit = 0; iBit < 32 && iChannel < channelMapCap; ++iBit) { - AudioChannelBitmap bit = bitmap & (1 << iBit); - if (bit != 0) { - pChannelMap[iChannel++] = ma_channel_from_AudioChannelBit(bit); - } - } - } else -#endif - { - /* - Need to use the tag to determine the channel map. For now I'm just assuming a default channel map, but later on this should - be updated to determine the mapping based on the tag. - */ - UInt32 channelCount; - - /* Our channel map retrieval APIs below take 32-bit integers, so we'll want to clamp the channel map capacity. */ - if (channelMapCap > 0xFFFFFFFF) { - channelMapCap = 0xFFFFFFFF; - } - - channelCount = ma_min(AudioChannelLayoutTag_GetNumberOfChannels(pChannelLayout->mChannelLayoutTag), (UInt32)channelMapCap); - - switch (pChannelLayout->mChannelLayoutTag) - { - case kAudioChannelLayoutTag_Mono: - case kAudioChannelLayoutTag_Stereo: - case kAudioChannelLayoutTag_StereoHeadphones: - case kAudioChannelLayoutTag_MatrixStereo: - case kAudioChannelLayoutTag_MidSide: - case kAudioChannelLayoutTag_XY: - case kAudioChannelLayoutTag_Binaural: - case kAudioChannelLayoutTag_Ambisonic_B_Format: - { - ma_channel_map_init_standard(ma_standard_channel_map_default, pChannelMap, channelMapCap, channelCount); - } break; - - case kAudioChannelLayoutTag_Octagonal: - { - pChannelMap[7] = MA_CHANNEL_SIDE_RIGHT; - pChannelMap[6] = MA_CHANNEL_SIDE_LEFT; - } MA_FALLTHROUGH; /* Intentional fallthrough. */ - case kAudioChannelLayoutTag_Hexagonal: - { - pChannelMap[5] = MA_CHANNEL_BACK_CENTER; - } MA_FALLTHROUGH; /* Intentional fallthrough. */ - case kAudioChannelLayoutTag_Pentagonal: - { - pChannelMap[4] = MA_CHANNEL_FRONT_CENTER; - } MA_FALLTHROUGH; /* Intentional fallthrough. */ - case kAudioChannelLayoutTag_Quadraphonic: - { - pChannelMap[3] = MA_CHANNEL_BACK_RIGHT; - pChannelMap[2] = MA_CHANNEL_BACK_LEFT; - pChannelMap[1] = MA_CHANNEL_RIGHT; - pChannelMap[0] = MA_CHANNEL_LEFT; - } break; - - /* TODO: Add support for more tags here. */ - - default: - { - ma_channel_map_init_standard(ma_standard_channel_map_default, pChannelMap, channelMapCap, channelCount); - } break; - } - } - - return MA_SUCCESS; -} - -#if (defined(MAC_OS_VERSION_12_0) && MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_VERSION_12_0) || \ - (defined(__IPHONE_15_0) && __IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_15_0) -#define AUDIO_OBJECT_PROPERTY_ELEMENT kAudioObjectPropertyElementMain -#else -/* kAudioObjectPropertyElementMaster is deprecated. */ -#define AUDIO_OBJECT_PROPERTY_ELEMENT kAudioObjectPropertyElementMaster -#endif - -/* kAudioDevicePropertyScope* were renamed to kAudioObjectPropertyScope* in 10.8. */ -#if !defined(MAC_OS_X_VERSION_10_8) || (MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_8) -#define kAudioObjectPropertyScopeInput kAudioDevicePropertyScopeInput -#define kAudioObjectPropertyScopeOutput kAudioDevicePropertyScopeOutput -#endif - -static ma_result ma_get_device_object_ids__coreaudio(ma_context* pContext, UInt32* pDeviceCount, AudioObjectID** ppDeviceObjectIDs) /* NOTE: Free the returned buffer with ma_free(). */ -{ - AudioObjectPropertyAddress propAddressDevices; - UInt32 deviceObjectsDataSize; - OSStatus status; - AudioObjectID* pDeviceObjectIDs; - - MA_ASSERT(pContext != NULL); - MA_ASSERT(pDeviceCount != NULL); - MA_ASSERT(ppDeviceObjectIDs != NULL); - - /* Safety. */ - *pDeviceCount = 0; - *ppDeviceObjectIDs = NULL; - - propAddressDevices.mSelector = kAudioHardwarePropertyDevices; - propAddressDevices.mScope = kAudioObjectPropertyScopeGlobal; - propAddressDevices.mElement = AUDIO_OBJECT_PROPERTY_ELEMENT; - - status = ((ma_AudioObjectGetPropertyDataSize_proc)pContext->coreaudio.AudioObjectGetPropertyDataSize)(kAudioObjectSystemObject, &propAddressDevices, 0, NULL, &deviceObjectsDataSize); - if (status != noErr) { - return ma_result_from_OSStatus(status); - } - - pDeviceObjectIDs = (AudioObjectID*)ma_malloc(deviceObjectsDataSize, &pContext->allocationCallbacks); - if (pDeviceObjectIDs == NULL) { - return MA_OUT_OF_MEMORY; - } - - status = ((ma_AudioObjectGetPropertyData_proc)pContext->coreaudio.AudioObjectGetPropertyData)(kAudioObjectSystemObject, &propAddressDevices, 0, NULL, &deviceObjectsDataSize, pDeviceObjectIDs); - if (status != noErr) { - ma_free(pDeviceObjectIDs, &pContext->allocationCallbacks); - return ma_result_from_OSStatus(status); - } - - *pDeviceCount = deviceObjectsDataSize / sizeof(AudioObjectID); - *ppDeviceObjectIDs = pDeviceObjectIDs; - - return MA_SUCCESS; -} - -static ma_result ma_get_AudioObject_uid_as_CFStringRef(ma_context* pContext, AudioObjectID objectID, CFStringRef* pUID) -{ - AudioObjectPropertyAddress propAddress; - UInt32 dataSize; - OSStatus status; - - MA_ASSERT(pContext != NULL); - - propAddress.mSelector = kAudioDevicePropertyDeviceUID; - propAddress.mScope = kAudioObjectPropertyScopeGlobal; - propAddress.mElement = AUDIO_OBJECT_PROPERTY_ELEMENT; - - dataSize = sizeof(*pUID); - status = ((ma_AudioObjectGetPropertyData_proc)pContext->coreaudio.AudioObjectGetPropertyData)(objectID, &propAddress, 0, NULL, &dataSize, pUID); - if (status != noErr) { - return ma_result_from_OSStatus(status); - } - - return MA_SUCCESS; -} - -static ma_result ma_get_AudioObject_uid(ma_context* pContext, AudioObjectID objectID, size_t bufferSize, char* bufferOut) -{ - CFStringRef uid; - ma_result result; - - MA_ASSERT(pContext != NULL); - - result = ma_get_AudioObject_uid_as_CFStringRef(pContext, objectID, &uid); - if (result != MA_SUCCESS) { - return result; - } - - if (!((ma_CFStringGetCString_proc)pContext->coreaudio.CFStringGetCString)(uid, bufferOut, bufferSize, kCFStringEncodingUTF8)) { - return MA_ERROR; - } - - ((ma_CFRelease_proc)pContext->coreaudio.CFRelease)(uid); - return MA_SUCCESS; -} - -static ma_result ma_get_AudioObject_name(ma_context* pContext, AudioObjectID objectID, size_t bufferSize, char* bufferOut) -{ - AudioObjectPropertyAddress propAddress; - CFStringRef deviceName = NULL; - UInt32 dataSize; - OSStatus status; - - MA_ASSERT(pContext != NULL); - - propAddress.mSelector = kAudioDevicePropertyDeviceNameCFString; - propAddress.mScope = kAudioObjectPropertyScopeGlobal; - propAddress.mElement = AUDIO_OBJECT_PROPERTY_ELEMENT; - - dataSize = sizeof(deviceName); - status = ((ma_AudioObjectGetPropertyData_proc)pContext->coreaudio.AudioObjectGetPropertyData)(objectID, &propAddress, 0, NULL, &dataSize, &deviceName); - if (status != noErr) { - return ma_result_from_OSStatus(status); - } - - if (!((ma_CFStringGetCString_proc)pContext->coreaudio.CFStringGetCString)(deviceName, bufferOut, bufferSize, kCFStringEncodingUTF8)) { - return MA_ERROR; - } - - ((ma_CFRelease_proc)pContext->coreaudio.CFRelease)(deviceName); - return MA_SUCCESS; -} - -static ma_bool32 ma_does_AudioObject_support_scope(ma_context* pContext, AudioObjectID deviceObjectID, AudioObjectPropertyScope scope) -{ - AudioObjectPropertyAddress propAddress; - UInt32 dataSize; - OSStatus status; - AudioBufferList* pBufferList; - ma_bool32 isSupported; - - MA_ASSERT(pContext != NULL); - - /* To know whether or not a device is an input device we need ot look at the stream configuration. If it has an output channel it's a playback device. */ - propAddress.mSelector = kAudioDevicePropertyStreamConfiguration; - propAddress.mScope = scope; - propAddress.mElement = AUDIO_OBJECT_PROPERTY_ELEMENT; - - status = ((ma_AudioObjectGetPropertyDataSize_proc)pContext->coreaudio.AudioObjectGetPropertyDataSize)(deviceObjectID, &propAddress, 0, NULL, &dataSize); - if (status != noErr) { - return MA_FALSE; - } - - pBufferList = (AudioBufferList*)ma_malloc(dataSize, &pContext->allocationCallbacks); - if (pBufferList == NULL) { - return MA_FALSE; /* Out of memory. */ - } - - status = ((ma_AudioObjectGetPropertyData_proc)pContext->coreaudio.AudioObjectGetPropertyData)(deviceObjectID, &propAddress, 0, NULL, &dataSize, pBufferList); - if (status != noErr) { - ma_free(pBufferList, &pContext->allocationCallbacks); - return MA_FALSE; - } - - isSupported = MA_FALSE; - if (pBufferList->mNumberBuffers > 0) { - isSupported = MA_TRUE; - } - - ma_free(pBufferList, &pContext->allocationCallbacks); - return isSupported; -} - -static ma_bool32 ma_does_AudioObject_support_playback(ma_context* pContext, AudioObjectID deviceObjectID) -{ - return ma_does_AudioObject_support_scope(pContext, deviceObjectID, kAudioObjectPropertyScopeOutput); -} - -static ma_bool32 ma_does_AudioObject_support_capture(ma_context* pContext, AudioObjectID deviceObjectID) -{ - return ma_does_AudioObject_support_scope(pContext, deviceObjectID, kAudioObjectPropertyScopeInput); -} - - -static ma_result ma_get_AudioObject_stream_descriptions(ma_context* pContext, AudioObjectID deviceObjectID, ma_device_type deviceType, UInt32* pDescriptionCount, AudioStreamRangedDescription** ppDescriptions) /* NOTE: Free the returned pointer with ma_free(). */ -{ - AudioObjectPropertyAddress propAddress; - UInt32 dataSize; - OSStatus status; - AudioStreamRangedDescription* pDescriptions; - - MA_ASSERT(pContext != NULL); - MA_ASSERT(pDescriptionCount != NULL); - MA_ASSERT(ppDescriptions != NULL); - - /* - TODO: Experiment with kAudioStreamPropertyAvailablePhysicalFormats instead of (or in addition to) kAudioStreamPropertyAvailableVirtualFormats. My - MacBook Pro uses s24/32 format, however, which miniaudio does not currently support. - */ - propAddress.mSelector = kAudioStreamPropertyAvailableVirtualFormats; /*kAudioStreamPropertyAvailablePhysicalFormats;*/ - propAddress.mScope = (deviceType == ma_device_type_playback) ? kAudioObjectPropertyScopeOutput : kAudioObjectPropertyScopeInput; - propAddress.mElement = AUDIO_OBJECT_PROPERTY_ELEMENT; - - status = ((ma_AudioObjectGetPropertyDataSize_proc)pContext->coreaudio.AudioObjectGetPropertyDataSize)(deviceObjectID, &propAddress, 0, NULL, &dataSize); - if (status != noErr) { - return ma_result_from_OSStatus(status); - } - - pDescriptions = (AudioStreamRangedDescription*)ma_malloc(dataSize, &pContext->allocationCallbacks); - if (pDescriptions == NULL) { - return MA_OUT_OF_MEMORY; - } - - status = ((ma_AudioObjectGetPropertyData_proc)pContext->coreaudio.AudioObjectGetPropertyData)(deviceObjectID, &propAddress, 0, NULL, &dataSize, pDescriptions); - if (status != noErr) { - ma_free(pDescriptions, &pContext->allocationCallbacks); - return ma_result_from_OSStatus(status); - } - - *pDescriptionCount = dataSize / sizeof(*pDescriptions); - *ppDescriptions = pDescriptions; - return MA_SUCCESS; -} - - -static ma_result ma_get_AudioObject_channel_layout(ma_context* pContext, AudioObjectID deviceObjectID, ma_device_type deviceType, AudioChannelLayout** ppChannelLayout) /* NOTE: Free the returned pointer with ma_free(). */ -{ - AudioObjectPropertyAddress propAddress; - UInt32 dataSize; - OSStatus status; - AudioChannelLayout* pChannelLayout; - - MA_ASSERT(pContext != NULL); - MA_ASSERT(ppChannelLayout != NULL); - - *ppChannelLayout = NULL; /* Safety. */ - - propAddress.mSelector = kAudioDevicePropertyPreferredChannelLayout; - propAddress.mScope = (deviceType == ma_device_type_playback) ? kAudioObjectPropertyScopeOutput : kAudioObjectPropertyScopeInput; - propAddress.mElement = AUDIO_OBJECT_PROPERTY_ELEMENT; - - status = ((ma_AudioObjectGetPropertyDataSize_proc)pContext->coreaudio.AudioObjectGetPropertyDataSize)(deviceObjectID, &propAddress, 0, NULL, &dataSize); - if (status != noErr) { - return ma_result_from_OSStatus(status); - } - - pChannelLayout = (AudioChannelLayout*)ma_malloc(dataSize, &pContext->allocationCallbacks); - if (pChannelLayout == NULL) { - return MA_OUT_OF_MEMORY; - } - - status = ((ma_AudioObjectGetPropertyData_proc)pContext->coreaudio.AudioObjectGetPropertyData)(deviceObjectID, &propAddress, 0, NULL, &dataSize, pChannelLayout); - if (status != noErr) { - ma_free(pChannelLayout, &pContext->allocationCallbacks); - return ma_result_from_OSStatus(status); - } - - *ppChannelLayout = pChannelLayout; - return MA_SUCCESS; -} - -static ma_result ma_get_AudioObject_channel_count(ma_context* pContext, AudioObjectID deviceObjectID, ma_device_type deviceType, ma_uint32* pChannelCount) -{ - AudioChannelLayout* pChannelLayout; - ma_result result; - - MA_ASSERT(pContext != NULL); - MA_ASSERT(pChannelCount != NULL); - - *pChannelCount = 0; /* Safety. */ - - result = ma_get_AudioObject_channel_layout(pContext, deviceObjectID, deviceType, &pChannelLayout); - if (result != MA_SUCCESS) { - return result; - } - - if (pChannelLayout->mChannelLayoutTag == kAudioChannelLayoutTag_UseChannelDescriptions) { - *pChannelCount = pChannelLayout->mNumberChannelDescriptions; - } else if (pChannelLayout->mChannelLayoutTag == kAudioChannelLayoutTag_UseChannelBitmap) { - *pChannelCount = ma_count_set_bits(pChannelLayout->mChannelBitmap); - } else { - *pChannelCount = AudioChannelLayoutTag_GetNumberOfChannels(pChannelLayout->mChannelLayoutTag); - } - - ma_free(pChannelLayout, &pContext->allocationCallbacks); - return MA_SUCCESS; -} - -#if 0 -static ma_result ma_get_AudioObject_channel_map(ma_context* pContext, AudioObjectID deviceObjectID, ma_device_type deviceType, ma_channel* pChannelMap, size_t channelMapCap) -{ - AudioChannelLayout* pChannelLayout; - ma_result result; - - MA_ASSERT(pContext != NULL); - - result = ma_get_AudioObject_channel_layout(pContext, deviceObjectID, deviceType, &pChannelLayout); - if (result != MA_SUCCESS) { - return result; /* Rather than always failing here, would it be more robust to simply assume a default? */ - } - - result = ma_get_channel_map_from_AudioChannelLayout(pChannelLayout, pChannelMap, channelMapCap); - if (result != MA_SUCCESS) { - ma_free(pChannelLayout, &pContext->allocationCallbacks); - return result; - } - - ma_free(pChannelLayout, &pContext->allocationCallbacks); - return result; -} -#endif - -static ma_result ma_get_AudioObject_sample_rates(ma_context* pContext, AudioObjectID deviceObjectID, ma_device_type deviceType, UInt32* pSampleRateRangesCount, AudioValueRange** ppSampleRateRanges) /* NOTE: Free the returned pointer with ma_free(). */ -{ - AudioObjectPropertyAddress propAddress; - UInt32 dataSize; - OSStatus status; - AudioValueRange* pSampleRateRanges; - - MA_ASSERT(pContext != NULL); - MA_ASSERT(pSampleRateRangesCount != NULL); - MA_ASSERT(ppSampleRateRanges != NULL); - - /* Safety. */ - *pSampleRateRangesCount = 0; - *ppSampleRateRanges = NULL; - - propAddress.mSelector = kAudioDevicePropertyAvailableNominalSampleRates; - propAddress.mScope = (deviceType == ma_device_type_playback) ? kAudioObjectPropertyScopeOutput : kAudioObjectPropertyScopeInput; - propAddress.mElement = AUDIO_OBJECT_PROPERTY_ELEMENT; - - status = ((ma_AudioObjectGetPropertyDataSize_proc)pContext->coreaudio.AudioObjectGetPropertyDataSize)(deviceObjectID, &propAddress, 0, NULL, &dataSize); - if (status != noErr) { - return ma_result_from_OSStatus(status); - } - - pSampleRateRanges = (AudioValueRange*)ma_malloc(dataSize, &pContext->allocationCallbacks); - if (pSampleRateRanges == NULL) { - return MA_OUT_OF_MEMORY; - } - - status = ((ma_AudioObjectGetPropertyData_proc)pContext->coreaudio.AudioObjectGetPropertyData)(deviceObjectID, &propAddress, 0, NULL, &dataSize, pSampleRateRanges); - if (status != noErr) { - ma_free(pSampleRateRanges, &pContext->allocationCallbacks); - return ma_result_from_OSStatus(status); - } - - *pSampleRateRangesCount = dataSize / sizeof(*pSampleRateRanges); - *ppSampleRateRanges = pSampleRateRanges; - return MA_SUCCESS; -} - -#if 0 -static ma_result ma_get_AudioObject_get_closest_sample_rate(ma_context* pContext, AudioObjectID deviceObjectID, ma_device_type deviceType, ma_uint32 sampleRateIn, ma_uint32* pSampleRateOut) -{ - UInt32 sampleRateRangeCount; - AudioValueRange* pSampleRateRanges; - ma_result result; - - MA_ASSERT(pContext != NULL); - MA_ASSERT(pSampleRateOut != NULL); - - *pSampleRateOut = 0; /* Safety. */ - - result = ma_get_AudioObject_sample_rates(pContext, deviceObjectID, deviceType, &sampleRateRangeCount, &pSampleRateRanges); - if (result != MA_SUCCESS) { - return result; - } - - if (sampleRateRangeCount == 0) { - ma_free(pSampleRateRanges, &pContext->allocationCallbacks); - return MA_ERROR; /* Should never hit this case should we? */ - } - - if (sampleRateIn == 0) { - /* Search in order of miniaudio's preferred priority. */ - UInt32 iMALSampleRate; - for (iMALSampleRate = 0; iMALSampleRate < ma_countof(g_maStandardSampleRatePriorities); ++iMALSampleRate) { - ma_uint32 malSampleRate = g_maStandardSampleRatePriorities[iMALSampleRate]; - UInt32 iCASampleRate; - for (iCASampleRate = 0; iCASampleRate < sampleRateRangeCount; ++iCASampleRate) { - AudioValueRange caSampleRate = pSampleRateRanges[iCASampleRate]; - if (caSampleRate.mMinimum <= malSampleRate && caSampleRate.mMaximum >= malSampleRate) { - *pSampleRateOut = malSampleRate; - ma_free(pSampleRateRanges, &pContext->allocationCallbacks); - return MA_SUCCESS; - } - } - } - - /* - If we get here it means none of miniaudio's standard sample rates matched any of the supported sample rates from the device. In this - case we just fall back to the first one reported by Core Audio. - */ - MA_ASSERT(sampleRateRangeCount > 0); - - *pSampleRateOut = pSampleRateRanges[0].mMinimum; - ma_free(pSampleRateRanges, &pContext->allocationCallbacks); - return MA_SUCCESS; - } else { - /* Find the closest match to this sample rate. */ - UInt32 currentAbsoluteDifference = INT32_MAX; - UInt32 iCurrentClosestRange = (UInt32)-1; - UInt32 iRange; - for (iRange = 0; iRange < sampleRateRangeCount; ++iRange) { - if (pSampleRateRanges[iRange].mMinimum <= sampleRateIn && pSampleRateRanges[iRange].mMaximum >= sampleRateIn) { - *pSampleRateOut = sampleRateIn; - ma_free(pSampleRateRanges, &pContext->allocationCallbacks); - return MA_SUCCESS; - } else { - UInt32 absoluteDifference; - if (pSampleRateRanges[iRange].mMinimum > sampleRateIn) { - absoluteDifference = pSampleRateRanges[iRange].mMinimum - sampleRateIn; - } else { - absoluteDifference = sampleRateIn - pSampleRateRanges[iRange].mMaximum; - } - - if (currentAbsoluteDifference > absoluteDifference) { - currentAbsoluteDifference = absoluteDifference; - iCurrentClosestRange = iRange; - } - } - } - - MA_ASSERT(iCurrentClosestRange != (UInt32)-1); - - *pSampleRateOut = pSampleRateRanges[iCurrentClosestRange].mMinimum; - ma_free(pSampleRateRanges, &pContext->allocationCallbacks); - return MA_SUCCESS; - } - - /* Should never get here, but it would mean we weren't able to find any suitable sample rates. */ - /*ma_free(pSampleRateRanges, &pContext->allocationCallbacks);*/ - /*return MA_ERROR;*/ -} -#endif - -static ma_result ma_get_AudioObject_closest_buffer_size_in_frames(ma_context* pContext, AudioObjectID deviceObjectID, ma_device_type deviceType, ma_uint32 bufferSizeInFramesIn, ma_uint32* pBufferSizeInFramesOut) -{ - AudioObjectPropertyAddress propAddress; - AudioValueRange bufferSizeRange; - UInt32 dataSize; - OSStatus status; - - MA_ASSERT(pContext != NULL); - MA_ASSERT(pBufferSizeInFramesOut != NULL); - - *pBufferSizeInFramesOut = 0; /* Safety. */ - - propAddress.mSelector = kAudioDevicePropertyBufferFrameSizeRange; - propAddress.mScope = (deviceType == ma_device_type_playback) ? kAudioObjectPropertyScopeOutput : kAudioObjectPropertyScopeInput; - propAddress.mElement = AUDIO_OBJECT_PROPERTY_ELEMENT; - - dataSize = sizeof(bufferSizeRange); - status = ((ma_AudioObjectGetPropertyData_proc)pContext->coreaudio.AudioObjectGetPropertyData)(deviceObjectID, &propAddress, 0, NULL, &dataSize, &bufferSizeRange); - if (status != noErr) { - return ma_result_from_OSStatus(status); - } - - /* This is just a clamp. */ - if (bufferSizeInFramesIn < bufferSizeRange.mMinimum) { - *pBufferSizeInFramesOut = (ma_uint32)bufferSizeRange.mMinimum; - } else if (bufferSizeInFramesIn > bufferSizeRange.mMaximum) { - *pBufferSizeInFramesOut = (ma_uint32)bufferSizeRange.mMaximum; - } else { - *pBufferSizeInFramesOut = bufferSizeInFramesIn; - } - - return MA_SUCCESS; -} - -static ma_result ma_set_AudioObject_buffer_size_in_frames(ma_context* pContext, AudioObjectID deviceObjectID, ma_device_type deviceType, ma_uint32* pPeriodSizeInOut) -{ - ma_result result; - ma_uint32 chosenBufferSizeInFrames; - AudioObjectPropertyAddress propAddress; - UInt32 dataSize; - OSStatus status; - - MA_ASSERT(pContext != NULL); - - result = ma_get_AudioObject_closest_buffer_size_in_frames(pContext, deviceObjectID, deviceType, *pPeriodSizeInOut, &chosenBufferSizeInFrames); - if (result != MA_SUCCESS) { - return result; - } - - /* Try setting the size of the buffer... If this fails we just use whatever is currently set. */ - propAddress.mSelector = kAudioDevicePropertyBufferFrameSize; - propAddress.mScope = (deviceType == ma_device_type_playback) ? kAudioObjectPropertyScopeOutput : kAudioObjectPropertyScopeInput; - propAddress.mElement = AUDIO_OBJECT_PROPERTY_ELEMENT; - - ((ma_AudioObjectSetPropertyData_proc)pContext->coreaudio.AudioObjectSetPropertyData)(deviceObjectID, &propAddress, 0, NULL, sizeof(chosenBufferSizeInFrames), &chosenBufferSizeInFrames); - - /* Get the actual size of the buffer. */ - dataSize = sizeof(*pPeriodSizeInOut); - status = ((ma_AudioObjectGetPropertyData_proc)pContext->coreaudio.AudioObjectGetPropertyData)(deviceObjectID, &propAddress, 0, NULL, &dataSize, &chosenBufferSizeInFrames); - if (status != noErr) { - return ma_result_from_OSStatus(status); - } - - *pPeriodSizeInOut = chosenBufferSizeInFrames; - return MA_SUCCESS; -} - -static ma_result ma_find_default_AudioObjectID(ma_context* pContext, ma_device_type deviceType, AudioObjectID* pDeviceObjectID) -{ - AudioObjectPropertyAddress propAddressDefaultDevice; - UInt32 defaultDeviceObjectIDSize = sizeof(AudioObjectID); - AudioObjectID defaultDeviceObjectID; - OSStatus status; - - MA_ASSERT(pContext != NULL); - MA_ASSERT(pDeviceObjectID != NULL); - - /* Safety. */ - *pDeviceObjectID = 0; - - propAddressDefaultDevice.mScope = kAudioObjectPropertyScopeGlobal; - propAddressDefaultDevice.mElement = AUDIO_OBJECT_PROPERTY_ELEMENT; - if (deviceType == ma_device_type_playback) { - propAddressDefaultDevice.mSelector = kAudioHardwarePropertyDefaultOutputDevice; - } else { - propAddressDefaultDevice.mSelector = kAudioHardwarePropertyDefaultInputDevice; - } - - defaultDeviceObjectIDSize = sizeof(AudioObjectID); - status = ((ma_AudioObjectGetPropertyData_proc)pContext->coreaudio.AudioObjectGetPropertyData)(kAudioObjectSystemObject, &propAddressDefaultDevice, 0, NULL, &defaultDeviceObjectIDSize, &defaultDeviceObjectID); - if (status == noErr) { - *pDeviceObjectID = defaultDeviceObjectID; - return MA_SUCCESS; - } - - /* If we get here it means we couldn't find the device. */ - return MA_NO_DEVICE; -} - -static ma_result ma_find_AudioObjectID(ma_context* pContext, ma_device_type deviceType, const ma_device_id* pDeviceID, AudioObjectID* pDeviceObjectID) -{ - MA_ASSERT(pContext != NULL); - MA_ASSERT(pDeviceObjectID != NULL); - - /* Safety. */ - *pDeviceObjectID = 0; - - if (pDeviceID == NULL) { - /* Default device. */ - return ma_find_default_AudioObjectID(pContext, deviceType, pDeviceObjectID); - } else { - /* Explicit device. */ - UInt32 deviceCount; - AudioObjectID* pDeviceObjectIDs; - ma_result result; - UInt32 iDevice; - - result = ma_get_device_object_ids__coreaudio(pContext, &deviceCount, &pDeviceObjectIDs); - if (result != MA_SUCCESS) { - return result; - } - - for (iDevice = 0; iDevice < deviceCount; ++iDevice) { - AudioObjectID deviceObjectID = pDeviceObjectIDs[iDevice]; - - char uid[256]; - if (ma_get_AudioObject_uid(pContext, deviceObjectID, sizeof(uid), uid) != MA_SUCCESS) { - continue; - } - - if (deviceType == ma_device_type_playback) { - if (ma_does_AudioObject_support_playback(pContext, deviceObjectID)) { - if (strcmp(uid, pDeviceID->coreaudio) == 0) { - *pDeviceObjectID = deviceObjectID; - ma_free(pDeviceObjectIDs, &pContext->allocationCallbacks); - return MA_SUCCESS; - } - } - } else { - if (ma_does_AudioObject_support_capture(pContext, deviceObjectID)) { - if (strcmp(uid, pDeviceID->coreaudio) == 0) { - *pDeviceObjectID = deviceObjectID; - ma_free(pDeviceObjectIDs, &pContext->allocationCallbacks); - return MA_SUCCESS; - } - } - } - } - - ma_free(pDeviceObjectIDs, &pContext->allocationCallbacks); - } - - /* If we get here it means we couldn't find the device. */ - return MA_NO_DEVICE; -} - - -static ma_result ma_find_best_format__coreaudio(ma_context* pContext, AudioObjectID deviceObjectID, ma_device_type deviceType, ma_format format, ma_uint32 channels, ma_uint32 sampleRate, const AudioStreamBasicDescription* pOrigFormat, AudioStreamBasicDescription* pFormat) -{ - UInt32 deviceFormatDescriptionCount; - AudioStreamRangedDescription* pDeviceFormatDescriptions; - ma_result result; - ma_uint32 desiredSampleRate; - ma_uint32 desiredChannelCount; - ma_format desiredFormat; - AudioStreamBasicDescription bestDeviceFormatSoFar; - ma_bool32 hasSupportedFormat; - UInt32 iFormat; - - result = ma_get_AudioObject_stream_descriptions(pContext, deviceObjectID, deviceType, &deviceFormatDescriptionCount, &pDeviceFormatDescriptions); - if (result != MA_SUCCESS) { - return result; - } - - desiredSampleRate = sampleRate; - if (desiredSampleRate == 0) { - desiredSampleRate = (ma_uint32)pOrigFormat->mSampleRate; - } - - desiredChannelCount = channels; - if (desiredChannelCount == 0) { - desiredChannelCount = pOrigFormat->mChannelsPerFrame; - } - - desiredFormat = format; - if (desiredFormat == ma_format_unknown) { - result = ma_format_from_AudioStreamBasicDescription(pOrigFormat, &desiredFormat); - if (result != MA_SUCCESS || desiredFormat == ma_format_unknown) { - desiredFormat = g_maFormatPriorities[0]; - } - } - - /* - If we get here it means we don't have an exact match to what the client is asking for. We'll need to find the closest one. The next - loop will check for formats that have the same sample rate to what we're asking for. If there is, we prefer that one in all cases. - */ - MA_ZERO_OBJECT(&bestDeviceFormatSoFar); - - hasSupportedFormat = MA_FALSE; - for (iFormat = 0; iFormat < deviceFormatDescriptionCount; ++iFormat) { - ma_format formatFromDescription; - ma_result formatResult = ma_format_from_AudioStreamBasicDescription(&pDeviceFormatDescriptions[iFormat].mFormat, &formatFromDescription); - if (formatResult == MA_SUCCESS && formatFromDescription != ma_format_unknown) { - hasSupportedFormat = MA_TRUE; - bestDeviceFormatSoFar = pDeviceFormatDescriptions[iFormat].mFormat; - break; - } - } - - if (!hasSupportedFormat) { - ma_free(pDeviceFormatDescriptions, &pContext->allocationCallbacks); - return MA_FORMAT_NOT_SUPPORTED; - } - - - for (iFormat = 0; iFormat < deviceFormatDescriptionCount; ++iFormat) { - AudioStreamBasicDescription thisDeviceFormat = pDeviceFormatDescriptions[iFormat].mFormat; - ma_format thisSampleFormat; - ma_result formatResult; - ma_format bestSampleFormatSoFar; - - /* If the format is not supported by miniaudio we need to skip this one entirely. */ - formatResult = ma_format_from_AudioStreamBasicDescription(&pDeviceFormatDescriptions[iFormat].mFormat, &thisSampleFormat); - if (formatResult != MA_SUCCESS || thisSampleFormat == ma_format_unknown) { - continue; /* The format is not supported by miniaudio. Skip. */ - } - - ma_format_from_AudioStreamBasicDescription(&bestDeviceFormatSoFar, &bestSampleFormatSoFar); - - /* Getting here means the format is supported by miniaudio which makes this format a candidate. */ - if (thisDeviceFormat.mSampleRate != desiredSampleRate) { - /* - The sample rate does not match, but this format could still be usable, although it's a very low priority. If the best format - so far has an equal sample rate we can just ignore this one. - */ - if (bestDeviceFormatSoFar.mSampleRate == desiredSampleRate) { - continue; /* The best sample rate so far has the same sample rate as what we requested which means it's still the best so far. Skip this format. */ - } else { - /* In this case, neither the best format so far nor this one have the same sample rate. Check the channel count next. */ - if (thisDeviceFormat.mChannelsPerFrame != desiredChannelCount) { - /* This format has a different sample rate _and_ a different channel count. */ - if (bestDeviceFormatSoFar.mChannelsPerFrame == desiredChannelCount) { - continue; /* No change to the best format. */ - } else { - /* - Both this format and the best so far have different sample rates and different channel counts. Whichever has the - best format is the new best. - */ - if (ma_get_format_priority_index(thisSampleFormat) < ma_get_format_priority_index(bestSampleFormatSoFar)) { - bestDeviceFormatSoFar = thisDeviceFormat; - continue; - } else { - continue; /* No change to the best format. */ - } - } - } else { - /* This format has a different sample rate but the desired channel count. */ - if (bestDeviceFormatSoFar.mChannelsPerFrame == desiredChannelCount) { - /* Both this format and the best so far have the desired channel count. Whichever has the best format is the new best. */ - if (ma_get_format_priority_index(thisSampleFormat) < ma_get_format_priority_index(bestSampleFormatSoFar)) { - bestDeviceFormatSoFar = thisDeviceFormat; - continue; - } else { - continue; /* No change to the best format for now. */ - } - } else { - /* This format has the desired channel count, but the best so far does not. We have a new best. */ - bestDeviceFormatSoFar = thisDeviceFormat; - continue; - } - } - } - } else { - /* - The sample rates match which makes this format a very high priority contender. If the best format so far has a different - sample rate it needs to be replaced with this one. - */ - if (bestDeviceFormatSoFar.mSampleRate != desiredSampleRate) { - bestDeviceFormatSoFar = thisDeviceFormat; - continue; - } else { - /* In this case both this format and the best format so far have the same sample rate. Check the channel count next. */ - if (thisDeviceFormat.mChannelsPerFrame == desiredChannelCount) { - /* - In this case this format has the same channel count as what the client is requesting. If the best format so far has - a different count, this one becomes the new best. - */ - if (bestDeviceFormatSoFar.mChannelsPerFrame != desiredChannelCount) { - bestDeviceFormatSoFar = thisDeviceFormat; - continue; - } else { - /* In this case both this format and the best so far have the ideal sample rate and channel count. Check the format. */ - if (thisSampleFormat == desiredFormat) { - bestDeviceFormatSoFar = thisDeviceFormat; - break; /* Found the exact match. */ - } else { - /* The formats are different. The new best format is the one with the highest priority format according to miniaudio. */ - if (ma_get_format_priority_index(thisSampleFormat) < ma_get_format_priority_index(bestSampleFormatSoFar)) { - bestDeviceFormatSoFar = thisDeviceFormat; - continue; - } else { - continue; /* No change to the best format for now. */ - } - } - } - } else { - /* - In this case the channel count is different to what the client has requested. If the best so far has the same channel - count as the requested count then it remains the best. - */ - if (bestDeviceFormatSoFar.mChannelsPerFrame == desiredChannelCount) { - continue; - } else { - /* - This is the case where both have the same sample rate (good) but different channel counts. Right now both have about - the same priority, but we need to compare the format now. - */ - if (thisSampleFormat == bestSampleFormatSoFar) { - if (ma_get_format_priority_index(thisSampleFormat) < ma_get_format_priority_index(bestSampleFormatSoFar)) { - bestDeviceFormatSoFar = thisDeviceFormat; - continue; - } else { - continue; /* No change to the best format for now. */ - } - } - } - } - } - } - } - - *pFormat = bestDeviceFormatSoFar; - - ma_free(pDeviceFormatDescriptions, &pContext->allocationCallbacks); - return MA_SUCCESS; -} - -static ma_result ma_get_AudioUnit_channel_map(ma_context* pContext, AudioUnit audioUnit, ma_device_type deviceType, ma_channel* pChannelMap, size_t channelMapCap) -{ - AudioUnitScope deviceScope; - AudioUnitElement deviceBus; - UInt32 channelLayoutSize; - OSStatus status; - AudioChannelLayout* pChannelLayout; - ma_result result; - - MA_ASSERT(pContext != NULL); - - if (deviceType == ma_device_type_playback) { - deviceScope = kAudioUnitScope_Input; - deviceBus = MA_COREAUDIO_OUTPUT_BUS; - } else { - deviceScope = kAudioUnitScope_Output; - deviceBus = MA_COREAUDIO_INPUT_BUS; - } - - status = ((ma_AudioUnitGetPropertyInfo_proc)pContext->coreaudio.AudioUnitGetPropertyInfo)(audioUnit, kAudioUnitProperty_AudioChannelLayout, deviceScope, deviceBus, &channelLayoutSize, NULL); - if (status != noErr) { - return ma_result_from_OSStatus(status); - } - - pChannelLayout = (AudioChannelLayout*)ma_malloc(channelLayoutSize, &pContext->allocationCallbacks); - if (pChannelLayout == NULL) { - return MA_OUT_OF_MEMORY; - } - - status = ((ma_AudioUnitGetProperty_proc)pContext->coreaudio.AudioUnitGetProperty)(audioUnit, kAudioUnitProperty_AudioChannelLayout, deviceScope, deviceBus, pChannelLayout, &channelLayoutSize); - if (status != noErr) { - ma_free(pChannelLayout, &pContext->allocationCallbacks); - return ma_result_from_OSStatus(status); - } - - result = ma_get_channel_map_from_AudioChannelLayout(pChannelLayout, pChannelMap, channelMapCap); - if (result != MA_SUCCESS) { - ma_free(pChannelLayout, &pContext->allocationCallbacks); - return result; - } - - ma_free(pChannelLayout, &pContext->allocationCallbacks); - return MA_SUCCESS; -} -#endif /* MA_APPLE_DESKTOP */ - - -#if !defined(MA_APPLE_DESKTOP) -static void ma_AVAudioSessionPortDescription_to_device_info(AVAudioSessionPortDescription* pPortDesc, ma_device_info* pInfo) -{ - MA_ZERO_OBJECT(pInfo); - ma_strncpy_s(pInfo->name, sizeof(pInfo->name), [pPortDesc.portName UTF8String], (size_t)-1); - ma_strncpy_s(pInfo->id.coreaudio, sizeof(pInfo->id.coreaudio), [pPortDesc.UID UTF8String], (size_t)-1); -} -#endif - -static ma_result ma_context_enumerate_devices__coreaudio(ma_context* pContext, ma_enum_devices_callback_proc callback, void* pUserData) -{ -#if defined(MA_APPLE_DESKTOP) - UInt32 deviceCount; - AudioObjectID* pDeviceObjectIDs; - AudioObjectID defaultDeviceObjectIDPlayback; - AudioObjectID defaultDeviceObjectIDCapture; - ma_result result; - UInt32 iDevice; - - ma_find_default_AudioObjectID(pContext, ma_device_type_playback, &defaultDeviceObjectIDPlayback); /* OK if this fails. */ - ma_find_default_AudioObjectID(pContext, ma_device_type_capture, &defaultDeviceObjectIDCapture); /* OK if this fails. */ - - result = ma_get_device_object_ids__coreaudio(pContext, &deviceCount, &pDeviceObjectIDs); - if (result != MA_SUCCESS) { - return result; - } - - for (iDevice = 0; iDevice < deviceCount; ++iDevice) { - AudioObjectID deviceObjectID = pDeviceObjectIDs[iDevice]; - ma_device_info info; - - MA_ZERO_OBJECT(&info); - if (ma_get_AudioObject_uid(pContext, deviceObjectID, sizeof(info.id.coreaudio), info.id.coreaudio) != MA_SUCCESS) { - continue; - } - if (ma_get_AudioObject_name(pContext, deviceObjectID, sizeof(info.name), info.name) != MA_SUCCESS) { - continue; - } - - if (ma_does_AudioObject_support_playback(pContext, deviceObjectID)) { - if (deviceObjectID == defaultDeviceObjectIDPlayback) { - info.isDefault = MA_TRUE; - } - - if (!callback(pContext, ma_device_type_playback, &info, pUserData)) { - break; - } - } - if (ma_does_AudioObject_support_capture(pContext, deviceObjectID)) { - if (deviceObjectID == defaultDeviceObjectIDCapture) { - info.isDefault = MA_TRUE; - } - - if (!callback(pContext, ma_device_type_capture, &info, pUserData)) { - break; - } - } - } - - ma_free(pDeviceObjectIDs, &pContext->allocationCallbacks); -#else - ma_device_info info; - NSArray *pInputs = [[[AVAudioSession sharedInstance] currentRoute] inputs]; - NSArray *pOutputs = [[[AVAudioSession sharedInstance] currentRoute] outputs]; - - for (AVAudioSessionPortDescription* pPortDesc in pOutputs) { - ma_AVAudioSessionPortDescription_to_device_info(pPortDesc, &info); - if (!callback(pContext, ma_device_type_playback, &info, pUserData)) { - return MA_SUCCESS; - } - } - - for (AVAudioSessionPortDescription* pPortDesc in pInputs) { - ma_AVAudioSessionPortDescription_to_device_info(pPortDesc, &info); - if (!callback(pContext, ma_device_type_capture, &info, pUserData)) { - return MA_SUCCESS; - } - } -#endif - - return MA_SUCCESS; -} - -static ma_result ma_context_get_device_info__coreaudio(ma_context* pContext, ma_device_type deviceType, const ma_device_id* pDeviceID, ma_device_info* pDeviceInfo) -{ - ma_result result; - - MA_ASSERT(pContext != NULL); - -#if defined(MA_APPLE_DESKTOP) - /* Desktop */ - { - AudioObjectID deviceObjectID; - AudioObjectID defaultDeviceObjectID; - UInt32 streamDescriptionCount; - AudioStreamRangedDescription* pStreamDescriptions; - UInt32 iStreamDescription; - UInt32 sampleRateRangeCount; - AudioValueRange* pSampleRateRanges; - - ma_find_default_AudioObjectID(pContext, deviceType, &defaultDeviceObjectID); /* OK if this fails. */ - - result = ma_find_AudioObjectID(pContext, deviceType, pDeviceID, &deviceObjectID); - if (result != MA_SUCCESS) { - return result; - } - - result = ma_get_AudioObject_uid(pContext, deviceObjectID, sizeof(pDeviceInfo->id.coreaudio), pDeviceInfo->id.coreaudio); - if (result != MA_SUCCESS) { - return result; - } - - result = ma_get_AudioObject_name(pContext, deviceObjectID, sizeof(pDeviceInfo->name), pDeviceInfo->name); - if (result != MA_SUCCESS) { - return result; - } - - if (deviceObjectID == defaultDeviceObjectID) { - pDeviceInfo->isDefault = MA_TRUE; - } - - /* - There could be a large number of permutations here. Fortunately there is only a single channel count - being reported which reduces this quite a bit. For sample rates we're only reporting those that are - one of miniaudio's recognized "standard" rates. If there are still more formats than can fit into - our fixed sized array we'll just need to truncate them. This is unlikely and will probably only happen - if some driver performs software data conversion and therefore reports every possible format and - sample rate. - */ - pDeviceInfo->nativeDataFormatCount = 0; - - /* Formats. */ - { - ma_format uniqueFormats[ma_format_count]; - ma_uint32 uniqueFormatCount = 0; - ma_uint32 channels; - - /* Channels. */ - result = ma_get_AudioObject_channel_count(pContext, deviceObjectID, deviceType, &channels); - if (result != MA_SUCCESS) { - return result; - } - - /* Formats. */ - result = ma_get_AudioObject_stream_descriptions(pContext, deviceObjectID, deviceType, &streamDescriptionCount, &pStreamDescriptions); - if (result != MA_SUCCESS) { - return result; - } - - for (iStreamDescription = 0; iStreamDescription < streamDescriptionCount; ++iStreamDescription) { - ma_format format; - ma_bool32 hasFormatBeenHandled = MA_FALSE; - ma_uint32 iOutputFormat; - ma_uint32 iSampleRate; - - result = ma_format_from_AudioStreamBasicDescription(&pStreamDescriptions[iStreamDescription].mFormat, &format); - if (result != MA_SUCCESS) { - continue; - } - - MA_ASSERT(format != ma_format_unknown); - - /* Make sure the format isn't already in the output list. */ - for (iOutputFormat = 0; iOutputFormat < uniqueFormatCount; ++iOutputFormat) { - if (uniqueFormats[iOutputFormat] == format) { - hasFormatBeenHandled = MA_TRUE; - break; - } - } - - /* If we've already handled this format just skip it. */ - if (hasFormatBeenHandled) { - continue; - } - - uniqueFormats[uniqueFormatCount] = format; - uniqueFormatCount += 1; - - /* Sample Rates */ - result = ma_get_AudioObject_sample_rates(pContext, deviceObjectID, deviceType, &sampleRateRangeCount, &pSampleRateRanges); - if (result != MA_SUCCESS) { - return result; - } - - /* - Annoyingly Core Audio reports a sample rate range. We just get all the standard rates that are - between this range. - */ - for (iSampleRate = 0; iSampleRate < sampleRateRangeCount; ++iSampleRate) { - ma_uint32 iStandardSampleRate; - for (iStandardSampleRate = 0; iStandardSampleRate < ma_countof(g_maStandardSampleRatePriorities); iStandardSampleRate += 1) { - ma_uint32 standardSampleRate = g_maStandardSampleRatePriorities[iStandardSampleRate]; - if (standardSampleRate >= pSampleRateRanges[iSampleRate].mMinimum && standardSampleRate <= pSampleRateRanges[iSampleRate].mMaximum) { - /* We have a new data format. Add it to the list. */ - pDeviceInfo->nativeDataFormats[pDeviceInfo->nativeDataFormatCount].format = format; - pDeviceInfo->nativeDataFormats[pDeviceInfo->nativeDataFormatCount].channels = channels; - pDeviceInfo->nativeDataFormats[pDeviceInfo->nativeDataFormatCount].sampleRate = standardSampleRate; - pDeviceInfo->nativeDataFormats[pDeviceInfo->nativeDataFormatCount].flags = 0; - pDeviceInfo->nativeDataFormatCount += 1; - - if (pDeviceInfo->nativeDataFormatCount >= ma_countof(pDeviceInfo->nativeDataFormats)) { - break; /* No more room for any more formats. */ - } - } - } - } - - ma_free(pSampleRateRanges, &pContext->allocationCallbacks); - - if (pDeviceInfo->nativeDataFormatCount >= ma_countof(pDeviceInfo->nativeDataFormats)) { - break; /* No more room for any more formats. */ - } - } - - ma_free(pStreamDescriptions, &pContext->allocationCallbacks); - } - } -#else - /* Mobile */ - { - AudioComponentDescription desc; - AudioComponent component; - AudioUnit audioUnit; - OSStatus status; - AudioUnitScope formatScope; - AudioUnitElement formatElement; - AudioStreamBasicDescription bestFormat; - UInt32 propSize; - - /* We want to ensure we use a consistent device name to device enumeration. */ - if (pDeviceID != NULL && pDeviceID->coreaudio[0] != '\0') { - ma_bool32 found = MA_FALSE; - if (deviceType == ma_device_type_playback) { - NSArray *pOutputs = [[[AVAudioSession sharedInstance] currentRoute] outputs]; - for (AVAudioSessionPortDescription* pPortDesc in pOutputs) { - if (strcmp(pDeviceID->coreaudio, [pPortDesc.UID UTF8String]) == 0) { - ma_AVAudioSessionPortDescription_to_device_info(pPortDesc, pDeviceInfo); - found = MA_TRUE; - break; - } - } - } else { - NSArray *pInputs = [[[AVAudioSession sharedInstance] currentRoute] inputs]; - for (AVAudioSessionPortDescription* pPortDesc in pInputs) { - if (strcmp(pDeviceID->coreaudio, [pPortDesc.UID UTF8String]) == 0) { - ma_AVAudioSessionPortDescription_to_device_info(pPortDesc, pDeviceInfo); - found = MA_TRUE; - break; - } - } - } - - if (!found) { - return MA_DOES_NOT_EXIST; - } - } else { - if (deviceType == ma_device_type_playback) { - ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), MA_DEFAULT_PLAYBACK_DEVICE_NAME, (size_t)-1); - } else { - ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), MA_DEFAULT_CAPTURE_DEVICE_NAME, (size_t)-1); - } - } - - - /* - Retrieving device information is more annoying on mobile than desktop. For simplicity I'm locking this down to whatever format is - reported on a temporary I/O unit. The problem, however, is that this doesn't return a value for the sample rate which we need to - retrieve from the AVAudioSession shared instance. - */ - desc.componentType = kAudioUnitType_Output; - desc.componentSubType = kAudioUnitSubType_RemoteIO; - desc.componentManufacturer = kAudioUnitManufacturer_Apple; - desc.componentFlags = 0; - desc.componentFlagsMask = 0; - - component = ((ma_AudioComponentFindNext_proc)pContext->coreaudio.AudioComponentFindNext)(NULL, &desc); - if (component == NULL) { - return MA_FAILED_TO_INIT_BACKEND; - } - - status = ((ma_AudioComponentInstanceNew_proc)pContext->coreaudio.AudioComponentInstanceNew)(component, &audioUnit); - if (status != noErr) { - return ma_result_from_OSStatus(status); - } - - formatScope = (deviceType == ma_device_type_playback) ? kAudioUnitScope_Input : kAudioUnitScope_Output; - formatElement = (deviceType == ma_device_type_playback) ? MA_COREAUDIO_OUTPUT_BUS : MA_COREAUDIO_INPUT_BUS; - - propSize = sizeof(bestFormat); - status = ((ma_AudioUnitGetProperty_proc)pContext->coreaudio.AudioUnitGetProperty)(audioUnit, kAudioUnitProperty_StreamFormat, formatScope, formatElement, &bestFormat, &propSize); - if (status != noErr) { - ((ma_AudioComponentInstanceDispose_proc)pContext->coreaudio.AudioComponentInstanceDispose)(audioUnit); - return ma_result_from_OSStatus(status); - } - - ((ma_AudioComponentInstanceDispose_proc)pContext->coreaudio.AudioComponentInstanceDispose)(audioUnit); - audioUnit = NULL; - - /* Only a single format is being reported for iOS. */ - pDeviceInfo->nativeDataFormatCount = 1; - - result = ma_format_from_AudioStreamBasicDescription(&bestFormat, &pDeviceInfo->nativeDataFormats[0].format); - if (result != MA_SUCCESS) { - return result; - } - - pDeviceInfo->nativeDataFormats[0].channels = bestFormat.mChannelsPerFrame; - - /* - It looks like Apple are wanting to push the whole AVAudioSession thing. Thus, we need to use that to determine device settings. To do - this we just get the shared instance and inspect. - */ - @autoreleasepool { - AVAudioSession* pAudioSession = [AVAudioSession sharedInstance]; - MA_ASSERT(pAudioSession != NULL); - - pDeviceInfo->nativeDataFormats[0].sampleRate = (ma_uint32)pAudioSession.sampleRate; - } - } -#endif - - (void)pDeviceInfo; /* Unused. */ - return MA_SUCCESS; -} - -static AudioBufferList* ma_allocate_AudioBufferList__coreaudio(ma_uint32 sizeInFrames, ma_format format, ma_uint32 channels, ma_stream_layout layout, const ma_allocation_callbacks* pAllocationCallbacks) -{ - AudioBufferList* pBufferList; - UInt32 audioBufferSizeInBytes; - size_t allocationSize; - - MA_ASSERT(sizeInFrames > 0); - MA_ASSERT(format != ma_format_unknown); - MA_ASSERT(channels > 0); - - allocationSize = sizeof(AudioBufferList) - sizeof(AudioBuffer); /* Subtract sizeof(AudioBuffer) because that part is dynamically sized. */ - if (layout == ma_stream_layout_interleaved) { - /* Interleaved case. This is the simple case because we just have one buffer. */ - allocationSize += sizeof(AudioBuffer) * 1; - } else { - /* Non-interleaved case. This is the more complex case because there's more than one buffer. */ - allocationSize += sizeof(AudioBuffer) * channels; - } - - allocationSize += sizeInFrames * ma_get_bytes_per_frame(format, channels); - - pBufferList = (AudioBufferList*)ma_malloc(allocationSize, pAllocationCallbacks); - if (pBufferList == NULL) { - return NULL; - } - - audioBufferSizeInBytes = (UInt32)(sizeInFrames * ma_get_bytes_per_sample(format)); - - if (layout == ma_stream_layout_interleaved) { - pBufferList->mNumberBuffers = 1; - pBufferList->mBuffers[0].mNumberChannels = channels; - pBufferList->mBuffers[0].mDataByteSize = audioBufferSizeInBytes * channels; - pBufferList->mBuffers[0].mData = (ma_uint8*)pBufferList + sizeof(AudioBufferList); - } else { - ma_uint32 iBuffer; - pBufferList->mNumberBuffers = channels; - for (iBuffer = 0; iBuffer < pBufferList->mNumberBuffers; ++iBuffer) { - pBufferList->mBuffers[iBuffer].mNumberChannels = 1; - pBufferList->mBuffers[iBuffer].mDataByteSize = audioBufferSizeInBytes; - pBufferList->mBuffers[iBuffer].mData = (ma_uint8*)pBufferList + ((sizeof(AudioBufferList) - sizeof(AudioBuffer)) + (sizeof(AudioBuffer) * channels)) + (audioBufferSizeInBytes * iBuffer); - } - } - - return pBufferList; -} - -static ma_result ma_device_realloc_AudioBufferList__coreaudio(ma_device* pDevice, ma_uint32 sizeInFrames, ma_format format, ma_uint32 channels, ma_stream_layout layout) -{ - MA_ASSERT(pDevice != NULL); - MA_ASSERT(format != ma_format_unknown); - MA_ASSERT(channels > 0); - - /* Only resize the buffer if necessary. */ - if (pDevice->coreaudio.audioBufferCapInFrames < sizeInFrames) { - AudioBufferList* pNewAudioBufferList; - - pNewAudioBufferList = ma_allocate_AudioBufferList__coreaudio(sizeInFrames, format, channels, layout, &pDevice->pContext->allocationCallbacks); - if (pNewAudioBufferList == NULL) { - return MA_OUT_OF_MEMORY; - } - - /* At this point we'll have a new AudioBufferList and we can free the old one. */ - ma_free(pDevice->coreaudio.pAudioBufferList, &pDevice->pContext->allocationCallbacks); - pDevice->coreaudio.pAudioBufferList = pNewAudioBufferList; - pDevice->coreaudio.audioBufferCapInFrames = sizeInFrames; - } - - /* Getting here means the capacity of the audio is fine. */ - return MA_SUCCESS; -} - - -static OSStatus ma_on_output__coreaudio(void* pUserData, AudioUnitRenderActionFlags* pActionFlags, const AudioTimeStamp* pTimeStamp, UInt32 busNumber, UInt32 frameCount, AudioBufferList* pBufferList) -{ - ma_device* pDevice = (ma_device*)pUserData; - ma_stream_layout layout; - - MA_ASSERT(pDevice != NULL); - - /*ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "INFO: Output Callback: busNumber=%d, frameCount=%d, mNumberBuffers=%d\n", (int)busNumber, (int)frameCount, (int)pBufferList->mNumberBuffers);*/ - - /* We need to check whether or not we are outputting interleaved or non-interleaved samples. The way we do this is slightly different for each type. */ - layout = ma_stream_layout_interleaved; - if (pBufferList->mBuffers[0].mNumberChannels != pDevice->playback.internalChannels) { - layout = ma_stream_layout_deinterleaved; - } - - if (layout == ma_stream_layout_interleaved) { - /* For now we can assume everything is interleaved. */ - UInt32 iBuffer; - for (iBuffer = 0; iBuffer < pBufferList->mNumberBuffers; ++iBuffer) { - if (pBufferList->mBuffers[iBuffer].mNumberChannels == pDevice->playback.internalChannels) { - ma_uint32 frameCountForThisBuffer = pBufferList->mBuffers[iBuffer].mDataByteSize / ma_get_bytes_per_frame(pDevice->playback.internalFormat, pDevice->playback.internalChannels); - if (frameCountForThisBuffer > 0) { - ma_device_handle_backend_data_callback(pDevice, pBufferList->mBuffers[iBuffer].mData, NULL, frameCountForThisBuffer); - } - - /*a_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, " frameCount=%d, mNumberChannels=%d, mDataByteSize=%d\n", (int)frameCount, (int)pBufferList->mBuffers[iBuffer].mNumberChannels, (int)pBufferList->mBuffers[iBuffer].mDataByteSize);*/ - } else { - /* - This case is where the number of channels in the output buffer do not match our internal channels. It could mean that it's - not interleaved, in which case we can't handle right now since miniaudio does not yet support non-interleaved streams. We just - output silence here. - */ - MA_ZERO_MEMORY(pBufferList->mBuffers[iBuffer].mData, pBufferList->mBuffers[iBuffer].mDataByteSize); - /*ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, " WARNING: Outputting silence. frameCount=%d, mNumberChannels=%d, mDataByteSize=%d\n", (int)frameCount, (int)pBufferList->mBuffers[iBuffer].mNumberChannels, (int)pBufferList->mBuffers[iBuffer].mDataByteSize);*/ - } - } - } else { - /* This is the deinterleaved case. We need to update each buffer in groups of internalChannels. This assumes each buffer is the same size. */ - MA_ASSERT(pDevice->playback.internalChannels <= MA_MAX_CHANNELS); /* This should have been validated at initialization time. */ - - /* - For safety we'll check that the internal channels is a multiple of the buffer count. If it's not it means something - very strange has happened and we're not going to support it. - */ - if ((pBufferList->mNumberBuffers % pDevice->playback.internalChannels) == 0) { - ma_uint8 tempBuffer[4096]; - UInt32 iBuffer; - - for (iBuffer = 0; iBuffer < pBufferList->mNumberBuffers; iBuffer += pDevice->playback.internalChannels) { - ma_uint32 frameCountPerBuffer = pBufferList->mBuffers[iBuffer].mDataByteSize / ma_get_bytes_per_sample(pDevice->playback.internalFormat); - ma_uint32 framesRemaining = frameCountPerBuffer; - - while (framesRemaining > 0) { - void* ppDeinterleavedBuffers[MA_MAX_CHANNELS]; - ma_uint32 iChannel; - ma_uint32 framesToRead = sizeof(tempBuffer) / ma_get_bytes_per_frame(pDevice->playback.internalFormat, pDevice->playback.internalChannels); - if (framesToRead > framesRemaining) { - framesToRead = framesRemaining; - } - - ma_device_handle_backend_data_callback(pDevice, tempBuffer, NULL, framesToRead); - - for (iChannel = 0; iChannel < pDevice->playback.internalChannels; ++iChannel) { - ppDeinterleavedBuffers[iChannel] = (void*)ma_offset_ptr(pBufferList->mBuffers[iBuffer+iChannel].mData, (frameCountPerBuffer - framesRemaining) * ma_get_bytes_per_sample(pDevice->playback.internalFormat)); - } - - ma_deinterleave_pcm_frames(pDevice->playback.internalFormat, pDevice->playback.internalChannels, framesToRead, tempBuffer, ppDeinterleavedBuffers); - - framesRemaining -= framesToRead; - } - } - } - } - - (void)pActionFlags; - (void)pTimeStamp; - (void)busNumber; - (void)frameCount; - - return noErr; -} - -static OSStatus ma_on_input__coreaudio(void* pUserData, AudioUnitRenderActionFlags* pActionFlags, const AudioTimeStamp* pTimeStamp, UInt32 busNumber, UInt32 frameCount, AudioBufferList* pUnusedBufferList) -{ - ma_device* pDevice = (ma_device*)pUserData; - AudioBufferList* pRenderedBufferList; - ma_result result; - ma_stream_layout layout; - ma_uint32 iBuffer; - OSStatus status; - - MA_ASSERT(pDevice != NULL); - - pRenderedBufferList = (AudioBufferList*)pDevice->coreaudio.pAudioBufferList; - MA_ASSERT(pRenderedBufferList); - - /* We need to check whether or not we are outputting interleaved or non-interleaved samples. The way we do this is slightly different for each type. */ - layout = ma_stream_layout_interleaved; - if (pRenderedBufferList->mBuffers[0].mNumberChannels != pDevice->capture.internalChannels) { - layout = ma_stream_layout_deinterleaved; - } - - /*ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "INFO: Input Callback: busNumber=%d, frameCount=%d, mNumberBuffers=%d\n", (int)busNumber, (int)frameCount, (int)pRenderedBufferList->mNumberBuffers);*/ - - /* - There has been a situation reported where frame count passed into this function is greater than the capacity of - our capture buffer. There doesn't seem to be a reliable way to determine what the maximum frame count will be, - so we need to instead resort to dynamically reallocating our buffer to ensure it's large enough to capture the - number of frames requested by this callback. - */ - result = ma_device_realloc_AudioBufferList__coreaudio(pDevice, frameCount, pDevice->capture.internalFormat, pDevice->capture.internalChannels, layout); - if (result != MA_SUCCESS) { - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "Failed to allocate AudioBufferList for capture.\n"); - return noErr; - } - - pRenderedBufferList = (AudioBufferList*)pDevice->coreaudio.pAudioBufferList; - MA_ASSERT(pRenderedBufferList); - - /* - When you call AudioUnitRender(), Core Audio tries to be helpful by setting the mDataByteSize to the number of bytes - that were actually rendered. The problem with this is that the next call can fail with -50 due to the size no longer - being set to the capacity of the buffer, but instead the size in bytes of the previous render. This will cause a - problem when a future call to this callback specifies a larger number of frames. - - To work around this we need to explicitly set the size of each buffer to their respective size in bytes. - */ - for (iBuffer = 0; iBuffer < pRenderedBufferList->mNumberBuffers; ++iBuffer) { - pRenderedBufferList->mBuffers[iBuffer].mDataByteSize = pDevice->coreaudio.audioBufferCapInFrames * ma_get_bytes_per_sample(pDevice->capture.internalFormat) * pRenderedBufferList->mBuffers[iBuffer].mNumberChannels; - /*printf("DEBUG: nDataByteSize = %d\n", (int)pRenderedBufferList->mBuffers[iBuffer].mDataByteSize);*/ - } - - status = ((ma_AudioUnitRender_proc)pDevice->pContext->coreaudio.AudioUnitRender)((AudioUnit)pDevice->coreaudio.audioUnitCapture, pActionFlags, pTimeStamp, busNumber, frameCount, pRenderedBufferList); - if (status != noErr) { - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "ERROR: AudioUnitRender() failed with %d.\n", (int)status); - return status; - } - - if (layout == ma_stream_layout_interleaved) { - for (iBuffer = 0; iBuffer < pRenderedBufferList->mNumberBuffers; ++iBuffer) { - if (pRenderedBufferList->mBuffers[iBuffer].mNumberChannels == pDevice->capture.internalChannels) { - ma_device_handle_backend_data_callback(pDevice, NULL, pRenderedBufferList->mBuffers[iBuffer].mData, frameCount); - /*ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, " mDataByteSize=%d.\n", (int)pRenderedBufferList->mBuffers[iBuffer].mDataByteSize);*/ - } else { - /* - This case is where the number of channels in the output buffer do not match our internal channels. It could mean that it's - not interleaved, in which case we can't handle right now since miniaudio does not yet support non-interleaved streams. - */ - ma_uint8 silentBuffer[4096]; - ma_uint32 framesRemaining; - - MA_ZERO_MEMORY(silentBuffer, sizeof(silentBuffer)); - - framesRemaining = frameCount; - while (framesRemaining > 0) { - ma_uint32 framesToSend = sizeof(silentBuffer) / ma_get_bytes_per_frame(pDevice->capture.internalFormat, pDevice->capture.internalChannels); - if (framesToSend > framesRemaining) { - framesToSend = framesRemaining; - } - - ma_device_handle_backend_data_callback(pDevice, NULL, silentBuffer, framesToSend); - - framesRemaining -= framesToSend; - } - - /*ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, " WARNING: Outputting silence. frameCount=%d, mNumberChannels=%d, mDataByteSize=%d\n", (int)frameCount, (int)pRenderedBufferList->mBuffers[iBuffer].mNumberChannels, (int)pRenderedBufferList->mBuffers[iBuffer].mDataByteSize);*/ - } - } - } else { - /* This is the deinterleaved case. We need to interleave the audio data before sending it to the client. This assumes each buffer is the same size. */ - MA_ASSERT(pDevice->capture.internalChannels <= MA_MAX_CHANNELS); /* This should have been validated at initialization time. */ - - /* - For safety we'll check that the internal channels is a multiple of the buffer count. If it's not it means something - very strange has happened and we're not going to support it. - */ - if ((pRenderedBufferList->mNumberBuffers % pDevice->capture.internalChannels) == 0) { - ma_uint8 tempBuffer[4096]; - for (iBuffer = 0; iBuffer < pRenderedBufferList->mNumberBuffers; iBuffer += pDevice->capture.internalChannels) { - ma_uint32 framesRemaining = frameCount; - while (framesRemaining > 0) { - void* ppDeinterleavedBuffers[MA_MAX_CHANNELS]; - ma_uint32 iChannel; - ma_uint32 framesToSend = sizeof(tempBuffer) / ma_get_bytes_per_frame(pDevice->capture.internalFormat, pDevice->capture.internalChannels); - if (framesToSend > framesRemaining) { - framesToSend = framesRemaining; - } - - for (iChannel = 0; iChannel < pDevice->capture.internalChannels; ++iChannel) { - ppDeinterleavedBuffers[iChannel] = (void*)ma_offset_ptr(pRenderedBufferList->mBuffers[iBuffer+iChannel].mData, (frameCount - framesRemaining) * ma_get_bytes_per_sample(pDevice->capture.internalFormat)); - } - - ma_interleave_pcm_frames(pDevice->capture.internalFormat, pDevice->capture.internalChannels, framesToSend, (const void**)ppDeinterleavedBuffers, tempBuffer); - ma_device_handle_backend_data_callback(pDevice, NULL, tempBuffer, framesToSend); - - framesRemaining -= framesToSend; - } - } - } - } - - (void)pActionFlags; - (void)pTimeStamp; - (void)busNumber; - (void)frameCount; - (void)pUnusedBufferList; - - return noErr; -} - -static void on_start_stop__coreaudio(void* pUserData, AudioUnit audioUnit, AudioUnitPropertyID propertyID, AudioUnitScope scope, AudioUnitElement element) -{ - ma_device* pDevice = (ma_device*)pUserData; - MA_ASSERT(pDevice != NULL); - - /* Don't do anything if it looks like we're just reinitializing due to a device switch. */ - if (((audioUnit == pDevice->coreaudio.audioUnitPlayback) && pDevice->coreaudio.isSwitchingPlaybackDevice) || - ((audioUnit == pDevice->coreaudio.audioUnitCapture) && pDevice->coreaudio.isSwitchingCaptureDevice)) { - return; - } - - /* - There's been a report of a deadlock here when triggered by ma_device_uninit(). It looks like - AudioUnitGetProprty (called below) and AudioComponentInstanceDispose (called in ma_device_uninit) - can try waiting on the same lock. I'm going to try working around this by not calling any Core - Audio APIs in the callback when the device has been stopped or uninitialized. - */ - if (ma_device_get_state(pDevice) == ma_device_state_uninitialized || ma_device_get_state(pDevice) == ma_device_state_stopping || ma_device_get_state(pDevice) == ma_device_state_stopped) { - ma_device__on_notification_stopped(pDevice); - } else { - UInt32 isRunning; - UInt32 isRunningSize = sizeof(isRunning); - OSStatus status = ((ma_AudioUnitGetProperty_proc)pDevice->pContext->coreaudio.AudioUnitGetProperty)(audioUnit, kAudioOutputUnitProperty_IsRunning, scope, element, &isRunning, &isRunningSize); - if (status != noErr) { - goto done; /* Don't really know what to do in this case... just ignore it, I suppose... */ - } - - if (!isRunning) { - /* - The stop event is a bit annoying in Core Audio because it will be called when we automatically switch the default device. Some scenarios to consider: - - 1) When the device is unplugged, this will be called _before_ the default device change notification. - 2) When the device is changed via the default device change notification, this will be called _after_ the switch. - - For case #1, we just check if there's a new default device available. If so, we just ignore the stop event. For case #2 we check a flag. - */ - if (((audioUnit == pDevice->coreaudio.audioUnitPlayback) && pDevice->coreaudio.isDefaultPlaybackDevice) || - ((audioUnit == pDevice->coreaudio.audioUnitCapture) && pDevice->coreaudio.isDefaultCaptureDevice)) { - /* - It looks like the device is switching through an external event, such as the user unplugging the device or changing the default device - via the operating system's sound settings. If we're re-initializing the device, we just terminate because we want the stopping of the - device to be seamless to the client (we don't want them receiving the stopped event and thinking that the device has stopped when it - hasn't!). - */ - if (((audioUnit == pDevice->coreaudio.audioUnitPlayback) && pDevice->coreaudio.isSwitchingPlaybackDevice) || - ((audioUnit == pDevice->coreaudio.audioUnitCapture) && pDevice->coreaudio.isSwitchingCaptureDevice)) { - goto done; - } - - /* - Getting here means the device is not reinitializing which means it may have been unplugged. From what I can see, it looks like Core Audio - will try switching to the new default device seamlessly. We need to somehow find a way to determine whether or not Core Audio will most - likely be successful in switching to the new device. - - TODO: Try to predict if Core Audio will switch devices. If not, the stopped callback needs to be posted. - */ - goto done; - } - - /* Getting here means we need to stop the device. */ - ma_device__on_notification_stopped(pDevice); - } - } - - (void)propertyID; /* Unused. */ - -done: - /* Always signal the stop event. It's possible for the "else" case to get hit which can happen during an interruption. */ - ma_event_signal(&pDevice->coreaudio.stopEvent); -} - -#if defined(MA_APPLE_DESKTOP) -static ma_spinlock g_DeviceTrackingInitLock_CoreAudio = 0; /* A spinlock for mutal exclusion of the init/uninit of the global tracking data. Initialization to 0 is what we need. */ -static ma_uint32 g_DeviceTrackingInitCounter_CoreAudio = 0; -static ma_mutex g_DeviceTrackingMutex_CoreAudio; -static ma_device** g_ppTrackedDevices_CoreAudio = NULL; -static ma_uint32 g_TrackedDeviceCap_CoreAudio = 0; -static ma_uint32 g_TrackedDeviceCount_CoreAudio = 0; - -static OSStatus ma_default_device_changed__coreaudio(AudioObjectID objectID, UInt32 addressCount, const AudioObjectPropertyAddress* pAddresses, void* pUserData) -{ - ma_device_type deviceType; - - /* Not sure if I really need to check this, but it makes me feel better. */ - if (addressCount == 0) { - return noErr; - } - - if (pAddresses[0].mSelector == kAudioHardwarePropertyDefaultOutputDevice) { - deviceType = ma_device_type_playback; - } else if (pAddresses[0].mSelector == kAudioHardwarePropertyDefaultInputDevice) { - deviceType = ma_device_type_capture; - } else { - return noErr; /* Should never hit this. */ - } - - ma_mutex_lock(&g_DeviceTrackingMutex_CoreAudio); - { - ma_uint32 iDevice; - for (iDevice = 0; iDevice < g_TrackedDeviceCount_CoreAudio; iDevice += 1) { - ma_result reinitResult; - ma_device* pDevice; - - pDevice = g_ppTrackedDevices_CoreAudio[iDevice]; - if (pDevice->type == deviceType || pDevice->type == ma_device_type_duplex) { - if (deviceType == ma_device_type_playback) { - pDevice->coreaudio.isSwitchingPlaybackDevice = MA_TRUE; - reinitResult = ma_device_reinit_internal__coreaudio(pDevice, deviceType, MA_TRUE); - pDevice->coreaudio.isSwitchingPlaybackDevice = MA_FALSE; - } else { - pDevice->coreaudio.isSwitchingCaptureDevice = MA_TRUE; - reinitResult = ma_device_reinit_internal__coreaudio(pDevice, deviceType, MA_TRUE); - pDevice->coreaudio.isSwitchingCaptureDevice = MA_FALSE; - } - - if (reinitResult == MA_SUCCESS) { - ma_device__post_init_setup(pDevice, deviceType); - - /* Restart the device if required. If this fails we need to stop the device entirely. */ - if (ma_device_get_state(pDevice) == ma_device_state_started) { - OSStatus status; - if (deviceType == ma_device_type_playback) { - status = ((ma_AudioOutputUnitStart_proc)pDevice->pContext->coreaudio.AudioOutputUnitStart)((AudioUnit)pDevice->coreaudio.audioUnitPlayback); - if (status != noErr) { - if (pDevice->type == ma_device_type_duplex) { - ((ma_AudioOutputUnitStop_proc)pDevice->pContext->coreaudio.AudioOutputUnitStop)((AudioUnit)pDevice->coreaudio.audioUnitCapture); - } - ma_device__set_state(pDevice, ma_device_state_stopped); - } - } else if (deviceType == ma_device_type_capture) { - status = ((ma_AudioOutputUnitStart_proc)pDevice->pContext->coreaudio.AudioOutputUnitStart)((AudioUnit)pDevice->coreaudio.audioUnitCapture); - if (status != noErr) { - if (pDevice->type == ma_device_type_duplex) { - ((ma_AudioOutputUnitStop_proc)pDevice->pContext->coreaudio.AudioOutputUnitStop)((AudioUnit)pDevice->coreaudio.audioUnitPlayback); - } - ma_device__set_state(pDevice, ma_device_state_stopped); - } - } - } - - ma_device__on_notification_rerouted(pDevice); - } - } - } - } - ma_mutex_unlock(&g_DeviceTrackingMutex_CoreAudio); - - /* Unused parameters. */ - (void)objectID; - (void)pUserData; - - return noErr; -} - -static ma_result ma_context__init_device_tracking__coreaudio(ma_context* pContext) -{ - MA_ASSERT(pContext != NULL); - - ma_spinlock_lock(&g_DeviceTrackingInitLock_CoreAudio); - { - /* Don't do anything if we've already initialized device tracking. */ - if (g_DeviceTrackingInitCounter_CoreAudio == 0) { - AudioObjectPropertyAddress propAddress; - propAddress.mScope = kAudioObjectPropertyScopeGlobal; - propAddress.mElement = AUDIO_OBJECT_PROPERTY_ELEMENT; - - ma_mutex_init(&g_DeviceTrackingMutex_CoreAudio); - - propAddress.mSelector = kAudioHardwarePropertyDefaultInputDevice; - ((ma_AudioObjectAddPropertyListener_proc)pContext->coreaudio.AudioObjectAddPropertyListener)(kAudioObjectSystemObject, &propAddress, &ma_default_device_changed__coreaudio, NULL); - - propAddress.mSelector = kAudioHardwarePropertyDefaultOutputDevice; - ((ma_AudioObjectAddPropertyListener_proc)pContext->coreaudio.AudioObjectAddPropertyListener)(kAudioObjectSystemObject, &propAddress, &ma_default_device_changed__coreaudio, NULL); - - } - g_DeviceTrackingInitCounter_CoreAudio += 1; - } - ma_spinlock_unlock(&g_DeviceTrackingInitLock_CoreAudio); - - return MA_SUCCESS; -} - -static ma_result ma_context__uninit_device_tracking__coreaudio(ma_context* pContext) -{ - MA_ASSERT(pContext != NULL); - - ma_spinlock_lock(&g_DeviceTrackingInitLock_CoreAudio); - { - if (g_DeviceTrackingInitCounter_CoreAudio > 0) - g_DeviceTrackingInitCounter_CoreAudio -= 1; - - if (g_DeviceTrackingInitCounter_CoreAudio == 0) { - AudioObjectPropertyAddress propAddress; - propAddress.mScope = kAudioObjectPropertyScopeGlobal; - propAddress.mElement = AUDIO_OBJECT_PROPERTY_ELEMENT; - - propAddress.mSelector = kAudioHardwarePropertyDefaultInputDevice; - ((ma_AudioObjectRemovePropertyListener_proc)pContext->coreaudio.AudioObjectRemovePropertyListener)(kAudioObjectSystemObject, &propAddress, &ma_default_device_changed__coreaudio, NULL); - - propAddress.mSelector = kAudioHardwarePropertyDefaultOutputDevice; - ((ma_AudioObjectRemovePropertyListener_proc)pContext->coreaudio.AudioObjectRemovePropertyListener)(kAudioObjectSystemObject, &propAddress, &ma_default_device_changed__coreaudio, NULL); - - /* At this point there should be no tracked devices. If not there's an error somewhere. */ - if (g_ppTrackedDevices_CoreAudio != NULL) { - ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_WARNING, "You have uninitialized all contexts while an associated device is still active."); - ma_spinlock_unlock(&g_DeviceTrackingInitLock_CoreAudio); - return MA_INVALID_OPERATION; - } - - ma_mutex_uninit(&g_DeviceTrackingMutex_CoreAudio); - } - } - ma_spinlock_unlock(&g_DeviceTrackingInitLock_CoreAudio); - - return MA_SUCCESS; -} - -static ma_result ma_device__track__coreaudio(ma_device* pDevice) -{ - MA_ASSERT(pDevice != NULL); - - ma_mutex_lock(&g_DeviceTrackingMutex_CoreAudio); - { - /* Allocate memory if required. */ - if (g_TrackedDeviceCap_CoreAudio <= g_TrackedDeviceCount_CoreAudio) { - ma_uint32 newCap; - ma_device** ppNewDevices; - - newCap = g_TrackedDeviceCap_CoreAudio * 2; - if (newCap == 0) { - newCap = 1; - } - - ppNewDevices = (ma_device**)ma_realloc(g_ppTrackedDevices_CoreAudio, sizeof(*g_ppTrackedDevices_CoreAudio)*newCap, &pDevice->pContext->allocationCallbacks); - if (ppNewDevices == NULL) { - ma_mutex_unlock(&g_DeviceTrackingMutex_CoreAudio); - return MA_OUT_OF_MEMORY; - } - - g_ppTrackedDevices_CoreAudio = ppNewDevices; - g_TrackedDeviceCap_CoreAudio = newCap; - } - - g_ppTrackedDevices_CoreAudio[g_TrackedDeviceCount_CoreAudio] = pDevice; - g_TrackedDeviceCount_CoreAudio += 1; - } - ma_mutex_unlock(&g_DeviceTrackingMutex_CoreAudio); - - return MA_SUCCESS; -} - -static ma_result ma_device__untrack__coreaudio(ma_device* pDevice) -{ - MA_ASSERT(pDevice != NULL); - - ma_mutex_lock(&g_DeviceTrackingMutex_CoreAudio); - { - ma_uint32 iDevice; - for (iDevice = 0; iDevice < g_TrackedDeviceCount_CoreAudio; iDevice += 1) { - if (g_ppTrackedDevices_CoreAudio[iDevice] == pDevice) { - /* We've found the device. We now need to remove it from the list. */ - ma_uint32 jDevice; - for (jDevice = iDevice; jDevice < g_TrackedDeviceCount_CoreAudio-1; jDevice += 1) { - g_ppTrackedDevices_CoreAudio[jDevice] = g_ppTrackedDevices_CoreAudio[jDevice+1]; - } - - g_TrackedDeviceCount_CoreAudio -= 1; - - /* If there's nothing else in the list we need to free memory. */ - if (g_TrackedDeviceCount_CoreAudio == 0) { - ma_free(g_ppTrackedDevices_CoreAudio, &pDevice->pContext->allocationCallbacks); - g_ppTrackedDevices_CoreAudio = NULL; - g_TrackedDeviceCap_CoreAudio = 0; - } - - break; - } - } - } - ma_mutex_unlock(&g_DeviceTrackingMutex_CoreAudio); - - return MA_SUCCESS; -} -#endif - -#if defined(MA_APPLE_MOBILE) -@interface ma_ios_notification_handler:NSObject { - ma_device* m_pDevice; -} -@end - -@implementation ma_ios_notification_handler --(id)init:(ma_device*)pDevice -{ - self = [super init]; - m_pDevice = pDevice; - - /* For route changes. */ - [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(handle_route_change:) name:AVAudioSessionRouteChangeNotification object:[AVAudioSession sharedInstance]]; - - /* For interruptions. */ - [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(handle_interruption:) name:AVAudioSessionInterruptionNotification object:[AVAudioSession sharedInstance]]; - - return self; -} - --(void)dealloc -{ - [self remove_handler]; - - #if defined(__has_feature) - #if !__has_feature(objc_arc) - [super dealloc]; - #endif - #endif -} - --(void)remove_handler -{ - [[NSNotificationCenter defaultCenter] removeObserver:self name:AVAudioSessionRouteChangeNotification object:nil]; - [[NSNotificationCenter defaultCenter] removeObserver:self name:AVAudioSessionInterruptionNotification object:nil]; -} - --(void)handle_interruption:(NSNotification*)pNotification -{ - NSInteger type = [[[pNotification userInfo] objectForKey:AVAudioSessionInterruptionTypeKey] integerValue]; - switch (type) - { - case AVAudioSessionInterruptionTypeBegan: - { - ma_log_postf(ma_device_get_log(m_pDevice), MA_LOG_LEVEL_INFO, "[Core Audio] Interruption: AVAudioSessionInterruptionTypeBegan\n"); - - /* - Core Audio will have stopped the internal device automatically, but we need explicitly - stop it at a higher level to ensure miniaudio-specific state is updated for consistency. - */ - ma_device_stop(m_pDevice); - - /* - Fire the notification after the device has been stopped to ensure it's in the correct - state when the notification handler is invoked. - */ - ma_device__on_notification_interruption_began(m_pDevice); - } break; - - case AVAudioSessionInterruptionTypeEnded: - { - ma_log_postf(ma_device_get_log(m_pDevice), MA_LOG_LEVEL_INFO, "[Core Audio] Interruption: AVAudioSessionInterruptionTypeEnded\n"); - ma_device__on_notification_interruption_ended(m_pDevice); - } break; - } -} - --(void)handle_route_change:(NSNotification*)pNotification -{ - AVAudioSession* pSession = [AVAudioSession sharedInstance]; - - NSInteger reason = [[[pNotification userInfo] objectForKey:AVAudioSessionRouteChangeReasonKey] integerValue]; - switch (reason) - { - case AVAudioSessionRouteChangeReasonOldDeviceUnavailable: - { - ma_log_postf(ma_device_get_log(m_pDevice), MA_LOG_LEVEL_INFO, "[Core Audio] Route Changed: AVAudioSessionRouteChangeReasonOldDeviceUnavailable\n"); - } break; - - case AVAudioSessionRouteChangeReasonNewDeviceAvailable: - { - ma_log_postf(ma_device_get_log(m_pDevice), MA_LOG_LEVEL_INFO, "[Core Audio] Route Changed: AVAudioSessionRouteChangeReasonNewDeviceAvailable\n"); - } break; - - case AVAudioSessionRouteChangeReasonNoSuitableRouteForCategory: - { - ma_log_postf(ma_device_get_log(m_pDevice), MA_LOG_LEVEL_INFO, "[Core Audio] Route Changed: AVAudioSessionRouteChangeReasonNoSuitableRouteForCategory\n"); - } break; - - case AVAudioSessionRouteChangeReasonWakeFromSleep: - { - ma_log_postf(ma_device_get_log(m_pDevice), MA_LOG_LEVEL_INFO, "[Core Audio] Route Changed: AVAudioSessionRouteChangeReasonWakeFromSleep\n"); - } break; - - case AVAudioSessionRouteChangeReasonOverride: - { - ma_log_postf(ma_device_get_log(m_pDevice), MA_LOG_LEVEL_INFO, "[Core Audio] Route Changed: AVAudioSessionRouteChangeReasonOverride\n"); - } break; - - case AVAudioSessionRouteChangeReasonCategoryChange: - { - ma_log_postf(ma_device_get_log(m_pDevice), MA_LOG_LEVEL_INFO, "[Core Audio] Route Changed: AVAudioSessionRouteChangeReasonCategoryChange\n"); - } break; - - case AVAudioSessionRouteChangeReasonUnknown: - default: - { - ma_log_postf(ma_device_get_log(m_pDevice), MA_LOG_LEVEL_INFO, "[Core Audio] Route Changed: AVAudioSessionRouteChangeReasonUnknown\n"); - } break; - } - - ma_log_postf(ma_device_get_log(m_pDevice), MA_LOG_LEVEL_DEBUG, "[Core Audio] Changing Route. inputNumberChannels=%d; outputNumberOfChannels=%d\n", (int)pSession.inputNumberOfChannels, (int)pSession.outputNumberOfChannels); - - /* Let the application know about the route change. */ - ma_device__on_notification_rerouted(m_pDevice); -} -@end -#endif - -static ma_result ma_device_uninit__coreaudio(ma_device* pDevice) -{ - MA_ASSERT(pDevice != NULL); - MA_ASSERT(ma_device_get_state(pDevice) == ma_device_state_uninitialized); - -#if defined(MA_APPLE_DESKTOP) - /* - Make sure we're no longer tracking the device. It doesn't matter if we call this for a non-default device because it'll - just gracefully ignore it. - */ - ma_device__untrack__coreaudio(pDevice); -#endif -#if defined(MA_APPLE_MOBILE) - if (pDevice->coreaudio.pNotificationHandler != NULL) { - ma_ios_notification_handler* pNotificationHandler = (MA_BRIDGE_TRANSFER ma_ios_notification_handler*)pDevice->coreaudio.pNotificationHandler; - [pNotificationHandler remove_handler]; - } -#endif - - if (pDevice->coreaudio.audioUnitCapture != NULL) { - ((ma_AudioComponentInstanceDispose_proc)pDevice->pContext->coreaudio.AudioComponentInstanceDispose)((AudioUnit)pDevice->coreaudio.audioUnitCapture); - } - if (pDevice->coreaudio.audioUnitPlayback != NULL) { - ((ma_AudioComponentInstanceDispose_proc)pDevice->pContext->coreaudio.AudioComponentInstanceDispose)((AudioUnit)pDevice->coreaudio.audioUnitPlayback); - } - - if (pDevice->coreaudio.pAudioBufferList) { - ma_free(pDevice->coreaudio.pAudioBufferList, &pDevice->pContext->allocationCallbacks); - } - - return MA_SUCCESS; -} - -typedef struct -{ - ma_bool32 allowNominalSampleRateChange; - - /* Input. */ - ma_format formatIn; - ma_uint32 channelsIn; - ma_uint32 sampleRateIn; - ma_channel channelMapIn[MA_MAX_CHANNELS]; - ma_uint32 periodSizeInFramesIn; - ma_uint32 periodSizeInMillisecondsIn; - ma_uint32 periodsIn; - ma_share_mode shareMode; - ma_performance_profile performanceProfile; - ma_bool32 registerStopEvent; - - /* Output. */ -#if defined(MA_APPLE_DESKTOP) - AudioObjectID deviceObjectID; -#endif - AudioComponent component; - AudioUnit audioUnit; - AudioBufferList* pAudioBufferList; /* Only used for input devices. */ - ma_format formatOut; - ma_uint32 channelsOut; - ma_uint32 sampleRateOut; - ma_channel channelMapOut[MA_MAX_CHANNELS]; - ma_uint32 periodSizeInFramesOut; - ma_uint32 periodsOut; - char deviceName[256]; -} ma_device_init_internal_data__coreaudio; - -static ma_result ma_device_init_internal__coreaudio(ma_context* pContext, ma_device_type deviceType, const ma_device_id* pDeviceID, ma_device_init_internal_data__coreaudio* pData, void* pDevice_DoNotReference) /* <-- pDevice is typed as void* intentionally so as to avoid accidentally referencing it. */ -{ - ma_result result = MA_SUCCESS; - OSStatus status; - UInt32 enableIOFlag; - AudioStreamBasicDescription bestFormat; - ma_uint32 actualPeriodSizeInFrames; - AURenderCallbackStruct callbackInfo; -#if defined(MA_APPLE_DESKTOP) - AudioObjectID deviceObjectID; -#endif - - /* This API should only be used for a single device type: playback or capture. No full-duplex mode. */ - if (deviceType == ma_device_type_duplex) { - return MA_INVALID_ARGS; - } - - MA_ASSERT(pContext != NULL); - MA_ASSERT(deviceType == ma_device_type_playback || deviceType == ma_device_type_capture); - -#if defined(MA_APPLE_DESKTOP) - pData->deviceObjectID = 0; -#endif - pData->component = NULL; - pData->audioUnit = NULL; - pData->pAudioBufferList = NULL; - -#if defined(MA_APPLE_DESKTOP) - result = ma_find_AudioObjectID(pContext, deviceType, pDeviceID, &deviceObjectID); - if (result != MA_SUCCESS) { - return result; - } - - pData->deviceObjectID = deviceObjectID; -#endif - - /* Core audio doesn't really use the notion of a period so we can leave this unmodified, but not too over the top. */ - pData->periodsOut = pData->periodsIn; - if (pData->periodsOut == 0) { - pData->periodsOut = MA_DEFAULT_PERIODS; - } - if (pData->periodsOut > 16) { - pData->periodsOut = 16; - } - - - /* Audio unit. */ - status = ((ma_AudioComponentInstanceNew_proc)pContext->coreaudio.AudioComponentInstanceNew)((AudioComponent)pContext->coreaudio.component, (AudioUnit*)&pData->audioUnit); - if (status != noErr) { - return ma_result_from_OSStatus(status); - } - - - /* The input/output buses need to be explicitly enabled and disabled. We set the flag based on the output unit first, then we just swap it for input. */ - enableIOFlag = 1; - if (deviceType == ma_device_type_capture) { - enableIOFlag = 0; - } - - status = ((ma_AudioUnitSetProperty_proc)pContext->coreaudio.AudioUnitSetProperty)(pData->audioUnit, kAudioOutputUnitProperty_EnableIO, kAudioUnitScope_Output, MA_COREAUDIO_OUTPUT_BUS, &enableIOFlag, sizeof(enableIOFlag)); - if (status != noErr) { - ((ma_AudioComponentInstanceDispose_proc)pContext->coreaudio.AudioComponentInstanceDispose)(pData->audioUnit); - return ma_result_from_OSStatus(status); - } - - enableIOFlag = (enableIOFlag == 0) ? 1 : 0; - status = ((ma_AudioUnitSetProperty_proc)pContext->coreaudio.AudioUnitSetProperty)(pData->audioUnit, kAudioOutputUnitProperty_EnableIO, kAudioUnitScope_Input, MA_COREAUDIO_INPUT_BUS, &enableIOFlag, sizeof(enableIOFlag)); - if (status != noErr) { - ((ma_AudioComponentInstanceDispose_proc)pContext->coreaudio.AudioComponentInstanceDispose)(pData->audioUnit); - return ma_result_from_OSStatus(status); - } - - - /* Set the device to use with this audio unit. This is only used on desktop since we are using defaults on mobile. */ -#if defined(MA_APPLE_DESKTOP) - status = ((ma_AudioUnitSetProperty_proc)pContext->coreaudio.AudioUnitSetProperty)(pData->audioUnit, kAudioOutputUnitProperty_CurrentDevice, kAudioUnitScope_Global, 0, &deviceObjectID, sizeof(deviceObjectID)); - if (status != noErr) { - ((ma_AudioComponentInstanceDispose_proc)pContext->coreaudio.AudioComponentInstanceDispose)(pData->audioUnit); - return ma_result_from_OSStatus(result); - } -#else - /* - For some reason it looks like Apple is only allowing selection of the input device. There does not appear to be any way to change - the default output route. I have no idea why this is like this, but for now we'll only be able to configure capture devices. - */ - if (pDeviceID != NULL) { - if (deviceType == ma_device_type_capture) { - ma_bool32 found = MA_FALSE; - NSArray *pInputs = [[[AVAudioSession sharedInstance] currentRoute] inputs]; - for (AVAudioSessionPortDescription* pPortDesc in pInputs) { - if (strcmp(pDeviceID->coreaudio, [pPortDesc.UID UTF8String]) == 0) { - [[AVAudioSession sharedInstance] setPreferredInput:pPortDesc error:nil]; - found = MA_TRUE; - break; - } - } - - if (found == MA_FALSE) { - return MA_DOES_NOT_EXIST; - } - } - } -#endif - - /* - Format. This is the hardest part of initialization because there's a few variables to take into account. - 1) The format must be supported by the device. - 2) The format must be supported miniaudio. - 3) There's a priority that miniaudio prefers. - - Ideally we would like to use a format that's as close to the hardware as possible so we can get as close to a passthrough as possible. The - most important property is the sample rate. miniaudio can do format conversion for any sample rate and channel count, but cannot do the same - for the sample data format. If the sample data format is not supported by miniaudio it must be ignored completely. - - On mobile platforms this is a bit different. We just force the use of whatever the audio unit's current format is set to. - */ - { - AudioStreamBasicDescription origFormat; - UInt32 origFormatSize = sizeof(origFormat); - AudioUnitScope formatScope = (deviceType == ma_device_type_playback) ? kAudioUnitScope_Input : kAudioUnitScope_Output; - AudioUnitElement formatElement = (deviceType == ma_device_type_playback) ? MA_COREAUDIO_OUTPUT_BUS : MA_COREAUDIO_INPUT_BUS; - - if (deviceType == ma_device_type_playback) { - status = ((ma_AudioUnitGetProperty_proc)pContext->coreaudio.AudioUnitGetProperty)(pData->audioUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Output, MA_COREAUDIO_OUTPUT_BUS, &origFormat, &origFormatSize); - } else { - status = ((ma_AudioUnitGetProperty_proc)pContext->coreaudio.AudioUnitGetProperty)(pData->audioUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Input, MA_COREAUDIO_INPUT_BUS, &origFormat, &origFormatSize); - } - if (status != noErr) { - ((ma_AudioComponentInstanceDispose_proc)pContext->coreaudio.AudioComponentInstanceDispose)(pData->audioUnit); - return ma_result_from_OSStatus(status); - } - - #if defined(MA_APPLE_DESKTOP) - result = ma_find_best_format__coreaudio(pContext, deviceObjectID, deviceType, pData->formatIn, pData->channelsIn, pData->sampleRateIn, &origFormat, &bestFormat); - if (result != MA_SUCCESS) { - ((ma_AudioComponentInstanceDispose_proc)pContext->coreaudio.AudioComponentInstanceDispose)(pData->audioUnit); - return result; - } - - /* - Technical Note TN2091: Device input using the HAL Output Audio Unit - https://developer.apple.com/library/archive/technotes/tn2091/_index.html - - This documentation says the following: - - The internal AudioConverter can handle any *simple* conversion. Typically, this means that a client can specify ANY - variant of the PCM formats. Consequently, the device's sample rate should match the desired sample rate. If sample rate - conversion is needed, it can be accomplished by buffering the input and converting the data on a separate thread with - another AudioConverter. - - The important part here is the mention that it can handle *simple* conversions, which does *not* include sample rate. We - therefore want to ensure the sample rate stays consistent. This document is specifically for input, but I'm going to play it - safe and apply the same rule to output as well. - - I have tried going against the documentation by setting the sample rate anyway, but this just results in AudioUnitRender() - returning a result code of -10863. I have also tried changing the format directly on the input scope on the input bus, but - this just results in `ca_require: IsStreamFormatWritable(inScope, inElement) NotWritable` when trying to set the format. - - Something that does seem to work, however, has been setting the nominal sample rate on the device object. The problem with - this, however, is that it actually changes the sample rate at the operating system level and not just the application. This - could be intrusive to the user, however, so I don't think it's wise to make this the default. Instead I'm making this a - configuration option. When the `coreaudio.allowNominalSampleRateChange` config option is set to true, changing the sample - rate will be allowed. Otherwise it'll be fixed to the current sample rate. To check the system-defined sample rate, run - the Audio MIDI Setup program that comes installed on macOS and observe how the sample rate changes as the sample rate is - changed by miniaudio. - */ - if (pData->allowNominalSampleRateChange) { - AudioValueRange sampleRateRange; - AudioObjectPropertyAddress propAddress; - - sampleRateRange.mMinimum = bestFormat.mSampleRate; - sampleRateRange.mMaximum = bestFormat.mSampleRate; - - propAddress.mSelector = kAudioDevicePropertyNominalSampleRate; - propAddress.mScope = (deviceType == ma_device_type_playback) ? kAudioObjectPropertyScopeOutput : kAudioObjectPropertyScopeInput; - propAddress.mElement = AUDIO_OBJECT_PROPERTY_ELEMENT; - - status = ((ma_AudioObjectSetPropertyData_proc)pContext->coreaudio.AudioObjectSetPropertyData)(deviceObjectID, &propAddress, 0, NULL, sizeof(sampleRateRange), &sampleRateRange); - if (status != noErr) { - bestFormat.mSampleRate = origFormat.mSampleRate; - } - } else { - bestFormat.mSampleRate = origFormat.mSampleRate; - } - - status = ((ma_AudioUnitSetProperty_proc)pContext->coreaudio.AudioUnitSetProperty)(pData->audioUnit, kAudioUnitProperty_StreamFormat, formatScope, formatElement, &bestFormat, sizeof(bestFormat)); - if (status != noErr) { - /* We failed to set the format, so fall back to the current format of the audio unit. */ - bestFormat = origFormat; - } - #else - bestFormat = origFormat; - - /* - Sample rate is a little different here because for some reason kAudioUnitProperty_StreamFormat returns 0... Oh well. We need to instead try - setting the sample rate to what the user has requested and then just see the results of it. Need to use some Objective-C here for this since - it depends on Apple's AVAudioSession API. To do this we just get the shared AVAudioSession instance and then set it. Note that from what I - can tell, it looks like the sample rate is shared between playback and capture for everything. - */ - @autoreleasepool { - AVAudioSession* pAudioSession = [AVAudioSession sharedInstance]; - MA_ASSERT(pAudioSession != NULL); - - [pAudioSession setPreferredSampleRate:(double)pData->sampleRateIn error:nil]; - bestFormat.mSampleRate = pAudioSession.sampleRate; - - /* - I've had a report that the channel count returned by AudioUnitGetProperty above is inconsistent with - AVAudioSession outputNumberOfChannels. I'm going to try using the AVAudioSession values instead. - - UPDATE 20/02/2025: - When testing on the simulator with an iPhone 15 and iOS 17 I get an error when initializing the audio - unit if set the input channels to pAudioSession.inputNumberOfChannels. What is happening is the channel - count returned from AudioUnitGetProperty() above is set to 2, but pAudioSession is reporting a channel - count of 1. When this happens, the call to AudioUnitSetProprty() below just down below will succeed, but - AudioUnitInitialize() further down will fail. The only solution I have come up with is to not set the - channel count to pAudioSession.inputNumberOfChannels. - */ - if (deviceType == ma_device_type_playback) { - bestFormat.mChannelsPerFrame = (UInt32)pAudioSession.outputNumberOfChannels; - } - - #if 0 - if (deviceType == ma_device_type_capture) { - /*printf("DEBUG: bestFormat.mChannelsPerFrame = %d; pAudioSession.inputNumberOfChannels = %d\n", (int)bestFormat.mChannelsPerFrame, (int)pAudioSession.inputNumberOfChannels);*/ - bestFormat.mChannelsPerFrame = (UInt32)pAudioSession.inputNumberOfChannels; - } - #endif - } - - - status = ((ma_AudioUnitSetProperty_proc)pContext->coreaudio.AudioUnitSetProperty)(pData->audioUnit, kAudioUnitProperty_StreamFormat, formatScope, formatElement, &bestFormat, sizeof(bestFormat)); - if (status != noErr) { - ((ma_AudioComponentInstanceDispose_proc)pContext->coreaudio.AudioComponentInstanceDispose)(pData->audioUnit); - return ma_result_from_OSStatus(status); - } - #endif - - result = ma_format_from_AudioStreamBasicDescription(&bestFormat, &pData->formatOut); - if (result != MA_SUCCESS) { - ((ma_AudioComponentInstanceDispose_proc)pContext->coreaudio.AudioComponentInstanceDispose)(pData->audioUnit); - return result; - } - - if (pData->formatOut == ma_format_unknown) { - ((ma_AudioComponentInstanceDispose_proc)pContext->coreaudio.AudioComponentInstanceDispose)(pData->audioUnit); - return MA_FORMAT_NOT_SUPPORTED; - } - - pData->channelsOut = bestFormat.mChannelsPerFrame; - pData->sampleRateOut = (ma_uint32)bestFormat.mSampleRate; - } - - /* Clamp the channel count for safety. */ - if (pData->channelsOut > MA_MAX_CHANNELS) { - pData->channelsOut = MA_MAX_CHANNELS; - } - - /* - Internal channel map. This is weird in my testing. If I use the AudioObject to get the - channel map, the channel descriptions are set to "Unknown" for some reason. To work around - this it looks like retrieving it from the AudioUnit will work. However, and this is where - it gets weird, it doesn't seem to work with capture devices, nor at all on iOS... Therefore - I'm going to fall back to a default assumption in these cases. - */ -#if defined(MA_APPLE_DESKTOP) - result = ma_get_AudioUnit_channel_map(pContext, pData->audioUnit, deviceType, pData->channelMapOut, pData->channelsOut); - if (result != MA_SUCCESS) { - #if 0 - /* Try falling back to the channel map from the AudioObject. */ - result = ma_get_AudioObject_channel_map(pContext, deviceObjectID, deviceType, pData->channelMapOut, pData->channelsOut); - if (result != MA_SUCCESS) { - return result; - } - #else - /* Fall back to default assumptions. */ - ma_channel_map_init_standard(ma_standard_channel_map_default, pData->channelMapOut, ma_countof(pData->channelMapOut), pData->channelsOut); - #endif - } -#else - /* TODO: Figure out how to get the channel map using AVAudioSession. */ - ma_channel_map_init_standard(ma_standard_channel_map_default, pData->channelMapOut, ma_countof(pData->channelMapOut), pData->channelsOut); -#endif - - - /* Buffer size. Not allowing this to be configurable on iOS. */ - if (pData->periodSizeInFramesIn == 0) { - if (pData->periodSizeInMillisecondsIn == 0) { - if (pData->performanceProfile == ma_performance_profile_low_latency) { - actualPeriodSizeInFrames = ma_calculate_buffer_size_in_frames_from_milliseconds(MA_DEFAULT_PERIOD_SIZE_IN_MILLISECONDS_LOW_LATENCY, pData->sampleRateOut); - } else { - actualPeriodSizeInFrames = ma_calculate_buffer_size_in_frames_from_milliseconds(MA_DEFAULT_PERIOD_SIZE_IN_MILLISECONDS_CONSERVATIVE, pData->sampleRateOut); - } - } else { - actualPeriodSizeInFrames = ma_calculate_buffer_size_in_frames_from_milliseconds(pData->periodSizeInMillisecondsIn, pData->sampleRateOut); - } - } else { - actualPeriodSizeInFrames = pData->periodSizeInFramesIn; - } - -#if defined(MA_APPLE_DESKTOP) - result = ma_set_AudioObject_buffer_size_in_frames(pContext, deviceObjectID, deviceType, &actualPeriodSizeInFrames); - if (result != MA_SUCCESS) { - return result; - } -#else - /* - On iOS, the size of the IO buffer needs to be specified in seconds and is a floating point - number. I don't trust any potential truncation errors due to converting from float to integer - so I'm going to explicitly set the actual period size to the next power of 2. - */ - @autoreleasepool { - AVAudioSession* pAudioSession = [AVAudioSession sharedInstance]; - MA_ASSERT(pAudioSession != NULL); - - [pAudioSession setPreferredIOBufferDuration:((float)actualPeriodSizeInFrames / pAudioSession.sampleRate) error:nil]; - actualPeriodSizeInFrames = ma_next_power_of_2((ma_uint32)(pAudioSession.IOBufferDuration * pAudioSession.sampleRate)); - } -#endif - - - /* - During testing I discovered that the buffer size can be too big. You'll get an error like this: - - kAudioUnitErr_TooManyFramesToProcess : inFramesToProcess=4096, mMaxFramesPerSlice=512 - - Note how inFramesToProcess is smaller than mMaxFramesPerSlice. To fix, we need to set kAudioUnitProperty_MaximumFramesPerSlice to that - of the size of our buffer, or do it the other way around and set our buffer size to the kAudioUnitProperty_MaximumFramesPerSlice. - */ - status = ((ma_AudioUnitSetProperty_proc)pContext->coreaudio.AudioUnitSetProperty)(pData->audioUnit, kAudioUnitProperty_MaximumFramesPerSlice, kAudioUnitScope_Global, 0, &actualPeriodSizeInFrames, sizeof(actualPeriodSizeInFrames)); - if (status != noErr) { - ((ma_AudioComponentInstanceDispose_proc)pContext->coreaudio.AudioComponentInstanceDispose)(pData->audioUnit); - return ma_result_from_OSStatus(status); - } - - pData->periodSizeInFramesOut = (ma_uint32)actualPeriodSizeInFrames; - - /* We need a buffer list if this is an input device. We render into this in the input callback. */ - if (deviceType == ma_device_type_capture) { - ma_bool32 isInterleaved = (bestFormat.mFormatFlags & kAudioFormatFlagIsNonInterleaved) == 0; - AudioBufferList* pBufferList; - - pBufferList = ma_allocate_AudioBufferList__coreaudio(pData->periodSizeInFramesOut, pData->formatOut, pData->channelsOut, (isInterleaved) ? ma_stream_layout_interleaved : ma_stream_layout_deinterleaved, &pContext->allocationCallbacks); - if (pBufferList == NULL) { - ((ma_AudioComponentInstanceDispose_proc)pContext->coreaudio.AudioComponentInstanceDispose)(pData->audioUnit); - return MA_OUT_OF_MEMORY; - } - - pData->pAudioBufferList = pBufferList; - } - - /* Callbacks. */ - callbackInfo.inputProcRefCon = pDevice_DoNotReference; - if (deviceType == ma_device_type_playback) { - callbackInfo.inputProc = ma_on_output__coreaudio; - status = ((ma_AudioUnitSetProperty_proc)pContext->coreaudio.AudioUnitSetProperty)(pData->audioUnit, kAudioUnitProperty_SetRenderCallback, kAudioUnitScope_Global, 0, &callbackInfo, sizeof(callbackInfo)); - if (status != noErr) { - ((ma_AudioComponentInstanceDispose_proc)pContext->coreaudio.AudioComponentInstanceDispose)(pData->audioUnit); - return ma_result_from_OSStatus(status); - } - } else { - callbackInfo.inputProc = ma_on_input__coreaudio; - status = ((ma_AudioUnitSetProperty_proc)pContext->coreaudio.AudioUnitSetProperty)(pData->audioUnit, kAudioOutputUnitProperty_SetInputCallback, kAudioUnitScope_Global, 0, &callbackInfo, sizeof(callbackInfo)); - if (status != noErr) { - ((ma_AudioComponentInstanceDispose_proc)pContext->coreaudio.AudioComponentInstanceDispose)(pData->audioUnit); - return ma_result_from_OSStatus(status); - } - } - - /* We need to listen for stop events. */ - if (pData->registerStopEvent) { - status = ((ma_AudioUnitAddPropertyListener_proc)pContext->coreaudio.AudioUnitAddPropertyListener)(pData->audioUnit, kAudioOutputUnitProperty_IsRunning, on_start_stop__coreaudio, pDevice_DoNotReference); - if (status != noErr) { - ((ma_AudioComponentInstanceDispose_proc)pContext->coreaudio.AudioComponentInstanceDispose)(pData->audioUnit); - return ma_result_from_OSStatus(status); - } - } - - /* Initialize the audio unit. */ - status = ((ma_AudioUnitInitialize_proc)pContext->coreaudio.AudioUnitInitialize)(pData->audioUnit); - if (status != noErr) { - ma_free(pData->pAudioBufferList, &pContext->allocationCallbacks); - pData->pAudioBufferList = NULL; - ((ma_AudioComponentInstanceDispose_proc)pContext->coreaudio.AudioComponentInstanceDispose)(pData->audioUnit); - return ma_result_from_OSStatus(status); - } - - /* Grab the name. */ -#if defined(MA_APPLE_DESKTOP) - ma_get_AudioObject_name(pContext, deviceObjectID, sizeof(pData->deviceName), pData->deviceName); -#else - if (deviceType == ma_device_type_playback) { - ma_strcpy_s(pData->deviceName, sizeof(pData->deviceName), MA_DEFAULT_PLAYBACK_DEVICE_NAME); - } else { - ma_strcpy_s(pData->deviceName, sizeof(pData->deviceName), MA_DEFAULT_CAPTURE_DEVICE_NAME); - } -#endif - - return result; -} - -#if defined(MA_APPLE_DESKTOP) -static ma_result ma_device_reinit_internal__coreaudio(ma_device* pDevice, ma_device_type deviceType, ma_bool32 disposePreviousAudioUnit) -{ - ma_device_init_internal_data__coreaudio data; - ma_result result; - - /* This should only be called for playback or capture, not duplex. */ - if (deviceType == ma_device_type_duplex) { - return MA_INVALID_ARGS; - } - - data.allowNominalSampleRateChange = MA_FALSE; /* Don't change the nominal sample rate when switching devices. */ - - if (deviceType == ma_device_type_capture) { - data.formatIn = pDevice->capture.format; - data.channelsIn = pDevice->capture.channels; - data.sampleRateIn = pDevice->sampleRate; - MA_COPY_MEMORY(data.channelMapIn, pDevice->capture.channelMap, sizeof(pDevice->capture.channelMap)); - data.shareMode = pDevice->capture.shareMode; - data.performanceProfile = pDevice->coreaudio.originalPerformanceProfile; - data.registerStopEvent = MA_TRUE; - - if (disposePreviousAudioUnit) { - ((ma_AudioOutputUnitStop_proc)pDevice->pContext->coreaudio.AudioOutputUnitStop)((AudioUnit)pDevice->coreaudio.audioUnitCapture); - ((ma_AudioComponentInstanceDispose_proc)pDevice->pContext->coreaudio.AudioComponentInstanceDispose)((AudioUnit)pDevice->coreaudio.audioUnitCapture); - } - if (pDevice->coreaudio.pAudioBufferList) { - ma_free(pDevice->coreaudio.pAudioBufferList, &pDevice->pContext->allocationCallbacks); - } - } else if (deviceType == ma_device_type_playback) { - data.formatIn = pDevice->playback.format; - data.channelsIn = pDevice->playback.channels; - data.sampleRateIn = pDevice->sampleRate; - MA_COPY_MEMORY(data.channelMapIn, pDevice->playback.channelMap, sizeof(pDevice->playback.channelMap)); - data.shareMode = pDevice->playback.shareMode; - data.performanceProfile = pDevice->coreaudio.originalPerformanceProfile; - data.registerStopEvent = (pDevice->type != ma_device_type_duplex); - - if (disposePreviousAudioUnit) { - ((ma_AudioOutputUnitStop_proc)pDevice->pContext->coreaudio.AudioOutputUnitStop)((AudioUnit)pDevice->coreaudio.audioUnitPlayback); - ((ma_AudioComponentInstanceDispose_proc)pDevice->pContext->coreaudio.AudioComponentInstanceDispose)((AudioUnit)pDevice->coreaudio.audioUnitPlayback); - } - } - data.periodSizeInFramesIn = pDevice->coreaudio.originalPeriodSizeInFrames; - data.periodSizeInMillisecondsIn = pDevice->coreaudio.originalPeriodSizeInMilliseconds; - data.periodsIn = pDevice->coreaudio.originalPeriods; - - /* Need at least 3 periods for duplex. */ - if (data.periodsIn < 3 && pDevice->type == ma_device_type_duplex) { - data.periodsIn = 3; - } - - result = ma_device_init_internal__coreaudio(pDevice->pContext, deviceType, NULL, &data, (void*)pDevice); - if (result != MA_SUCCESS) { - return result; - } - - if (deviceType == ma_device_type_capture) { - #if defined(MA_APPLE_DESKTOP) - pDevice->coreaudio.deviceObjectIDCapture = (ma_uint32)data.deviceObjectID; - ma_get_AudioObject_uid(pDevice->pContext, pDevice->coreaudio.deviceObjectIDCapture, sizeof(pDevice->capture.id.coreaudio), pDevice->capture.id.coreaudio); - #endif - pDevice->coreaudio.audioUnitCapture = (ma_ptr)data.audioUnit; - pDevice->coreaudio.pAudioBufferList = (ma_ptr)data.pAudioBufferList; - pDevice->coreaudio.audioBufferCapInFrames = data.periodSizeInFramesOut; - - pDevice->capture.internalFormat = data.formatOut; - pDevice->capture.internalChannels = data.channelsOut; - pDevice->capture.internalSampleRate = data.sampleRateOut; - MA_COPY_MEMORY(pDevice->capture.internalChannelMap, data.channelMapOut, sizeof(data.channelMapOut)); - pDevice->capture.internalPeriodSizeInFrames = data.periodSizeInFramesOut; - pDevice->capture.internalPeriods = data.periodsOut; - } else if (deviceType == ma_device_type_playback) { - #if defined(MA_APPLE_DESKTOP) - pDevice->coreaudio.deviceObjectIDPlayback = (ma_uint32)data.deviceObjectID; - ma_get_AudioObject_uid(pDevice->pContext, pDevice->coreaudio.deviceObjectIDPlayback, sizeof(pDevice->playback.id.coreaudio), pDevice->playback.id.coreaudio); - #endif - pDevice->coreaudio.audioUnitPlayback = (ma_ptr)data.audioUnit; - - pDevice->playback.internalFormat = data.formatOut; - pDevice->playback.internalChannels = data.channelsOut; - pDevice->playback.internalSampleRate = data.sampleRateOut; - MA_COPY_MEMORY(pDevice->playback.internalChannelMap, data.channelMapOut, sizeof(data.channelMapOut)); - pDevice->playback.internalPeriodSizeInFrames = data.periodSizeInFramesOut; - pDevice->playback.internalPeriods = data.periodsOut; - } - - return MA_SUCCESS; -} -#endif /* MA_APPLE_DESKTOP */ - -static ma_result ma_device_init__coreaudio(ma_device* pDevice, const ma_device_config* pConfig, ma_device_descriptor* pDescriptorPlayback, ma_device_descriptor* pDescriptorCapture) -{ - ma_result result; - - MA_ASSERT(pDevice != NULL); - MA_ASSERT(pConfig != NULL); - - if (pConfig->deviceType == ma_device_type_loopback) { - return MA_DEVICE_TYPE_NOT_SUPPORTED; - } - - /* No exclusive mode with the Core Audio backend for now. */ - if (((pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) && pDescriptorCapture->shareMode == ma_share_mode_exclusive) || - ((pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) && pDescriptorPlayback->shareMode == ma_share_mode_exclusive)) { - return MA_SHARE_MODE_NOT_SUPPORTED; - } - - /* Capture needs to be initialized first. */ - if (pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) { - ma_device_init_internal_data__coreaudio data; - data.allowNominalSampleRateChange = pConfig->coreaudio.allowNominalSampleRateChange; - data.formatIn = pDescriptorCapture->format; - data.channelsIn = pDescriptorCapture->channels; - data.sampleRateIn = pDescriptorCapture->sampleRate; - MA_COPY_MEMORY(data.channelMapIn, pDescriptorCapture->channelMap, sizeof(pDescriptorCapture->channelMap)); - data.periodSizeInFramesIn = pDescriptorCapture->periodSizeInFrames; - data.periodSizeInMillisecondsIn = pDescriptorCapture->periodSizeInMilliseconds; - data.periodsIn = pDescriptorCapture->periodCount; - data.shareMode = pDescriptorCapture->shareMode; - data.performanceProfile = pConfig->performanceProfile; - data.registerStopEvent = MA_TRUE; - - /* Need at least 3 periods for duplex. */ - if (data.periodsIn < 3 && pConfig->deviceType == ma_device_type_duplex) { - data.periodsIn = 3; - } - - result = ma_device_init_internal__coreaudio(pDevice->pContext, ma_device_type_capture, pDescriptorCapture->pDeviceID, &data, (void*)pDevice); - if (result != MA_SUCCESS) { - return result; - } - - pDevice->coreaudio.isDefaultCaptureDevice = (pConfig->capture.pDeviceID == NULL); - #if defined(MA_APPLE_DESKTOP) - pDevice->coreaudio.deviceObjectIDCapture = (ma_uint32)data.deviceObjectID; - #endif - pDevice->coreaudio.audioUnitCapture = (ma_ptr)data.audioUnit; - pDevice->coreaudio.pAudioBufferList = (ma_ptr)data.pAudioBufferList; - pDevice->coreaudio.audioBufferCapInFrames = data.periodSizeInFramesOut; - pDevice->coreaudio.originalPeriodSizeInFrames = pDescriptorCapture->periodSizeInFrames; - pDevice->coreaudio.originalPeriodSizeInMilliseconds = pDescriptorCapture->periodSizeInMilliseconds; - pDevice->coreaudio.originalPeriods = pDescriptorCapture->periodCount; - pDevice->coreaudio.originalPerformanceProfile = pConfig->performanceProfile; - - pDescriptorCapture->format = data.formatOut; - pDescriptorCapture->channels = data.channelsOut; - pDescriptorCapture->sampleRate = data.sampleRateOut; - MA_COPY_MEMORY(pDescriptorCapture->channelMap, data.channelMapOut, sizeof(data.channelMapOut)); - pDescriptorCapture->periodSizeInFrames = data.periodSizeInFramesOut; - pDescriptorCapture->periodCount = data.periodsOut; - - #if defined(MA_APPLE_DESKTOP) - ma_get_AudioObject_uid(pDevice->pContext, pDevice->coreaudio.deviceObjectIDCapture, sizeof(pDevice->capture.id.coreaudio), pDevice->capture.id.coreaudio); - - /* - If we are using the default device we'll need to listen for changes to the system's default device so we can seamlessly - switch the device in the background. - */ - if (pConfig->capture.pDeviceID == NULL) { - ma_device__track__coreaudio(pDevice); - } - #endif - } - - /* Playback. */ - if (pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) { - ma_device_init_internal_data__coreaudio data; - data.allowNominalSampleRateChange = pConfig->coreaudio.allowNominalSampleRateChange; - data.formatIn = pDescriptorPlayback->format; - data.channelsIn = pDescriptorPlayback->channels; - data.sampleRateIn = pDescriptorPlayback->sampleRate; - MA_COPY_MEMORY(data.channelMapIn, pDescriptorPlayback->channelMap, sizeof(pDescriptorPlayback->channelMap)); - data.shareMode = pDescriptorPlayback->shareMode; - data.performanceProfile = pConfig->performanceProfile; - - /* In full-duplex mode we want the playback buffer to be the same size as the capture buffer. */ - if (pConfig->deviceType == ma_device_type_duplex) { - data.periodSizeInFramesIn = pDescriptorCapture->periodSizeInFrames; - data.periodsIn = pDescriptorCapture->periodCount; - data.registerStopEvent = MA_FALSE; - } else { - data.periodSizeInFramesIn = pDescriptorPlayback->periodSizeInFrames; - data.periodSizeInMillisecondsIn = pDescriptorPlayback->periodSizeInMilliseconds; - data.periodsIn = pDescriptorPlayback->periodCount; - data.registerStopEvent = MA_TRUE; - } - - result = ma_device_init_internal__coreaudio(pDevice->pContext, ma_device_type_playback, pDescriptorPlayback->pDeviceID, &data, (void*)pDevice); - if (result != MA_SUCCESS) { - if (pConfig->deviceType == ma_device_type_duplex) { - ((ma_AudioComponentInstanceDispose_proc)pDevice->pContext->coreaudio.AudioComponentInstanceDispose)((AudioUnit)pDevice->coreaudio.audioUnitCapture); - if (pDevice->coreaudio.pAudioBufferList) { - ma_free(pDevice->coreaudio.pAudioBufferList, &pDevice->pContext->allocationCallbacks); - } - } - return result; - } - - pDevice->coreaudio.isDefaultPlaybackDevice = (pConfig->playback.pDeviceID == NULL); - #if defined(MA_APPLE_DESKTOP) - pDevice->coreaudio.deviceObjectIDPlayback = (ma_uint32)data.deviceObjectID; - #endif - pDevice->coreaudio.audioUnitPlayback = (ma_ptr)data.audioUnit; - pDevice->coreaudio.originalPeriodSizeInFrames = pDescriptorPlayback->periodSizeInFrames; - pDevice->coreaudio.originalPeriodSizeInMilliseconds = pDescriptorPlayback->periodSizeInMilliseconds; - pDevice->coreaudio.originalPeriods = pDescriptorPlayback->periodCount; - pDevice->coreaudio.originalPerformanceProfile = pConfig->performanceProfile; - - pDescriptorPlayback->format = data.formatOut; - pDescriptorPlayback->channels = data.channelsOut; - pDescriptorPlayback->sampleRate = data.sampleRateOut; - MA_COPY_MEMORY(pDescriptorPlayback->channelMap, data.channelMapOut, sizeof(data.channelMapOut)); - pDescriptorPlayback->periodSizeInFrames = data.periodSizeInFramesOut; - pDescriptorPlayback->periodCount = data.periodsOut; - - #if defined(MA_APPLE_DESKTOP) - ma_get_AudioObject_uid(pDevice->pContext, pDevice->coreaudio.deviceObjectIDPlayback, sizeof(pDevice->playback.id.coreaudio), pDevice->playback.id.coreaudio); - - /* - If we are using the default device we'll need to listen for changes to the system's default device so we can seamlessly - switch the device in the background. - */ - if (pDescriptorPlayback->pDeviceID == NULL && (pConfig->deviceType != ma_device_type_duplex || pDescriptorCapture->pDeviceID != NULL)) { - ma_device__track__coreaudio(pDevice); - } - #endif - } - - - - /* - When stopping the device, a callback is called on another thread. We need to wait for this callback - before returning from ma_device_stop(). This event is used for this. - */ - ma_event_init(&pDevice->coreaudio.stopEvent); - - /* - We need to detect when a route has changed so we can update the data conversion pipeline accordingly. This is done - differently on non-Desktop Apple platforms. - */ -#if defined(MA_APPLE_MOBILE) - pDevice->coreaudio.pNotificationHandler = (MA_BRIDGE_RETAINED void*)[[ma_ios_notification_handler alloc] init:pDevice]; -#endif - - return MA_SUCCESS; -} - - -static ma_result ma_device_start__coreaudio(ma_device* pDevice) -{ - MA_ASSERT(pDevice != NULL); - - if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) { - OSStatus status = ((ma_AudioOutputUnitStart_proc)pDevice->pContext->coreaudio.AudioOutputUnitStart)((AudioUnit)pDevice->coreaudio.audioUnitCapture); - if (status != noErr) { - return ma_result_from_OSStatus(status); - } - } - - if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) { - OSStatus status = ((ma_AudioOutputUnitStart_proc)pDevice->pContext->coreaudio.AudioOutputUnitStart)((AudioUnit)pDevice->coreaudio.audioUnitPlayback); - if (status != noErr) { - if (pDevice->type == ma_device_type_duplex) { - ((ma_AudioOutputUnitStop_proc)pDevice->pContext->coreaudio.AudioOutputUnitStop)((AudioUnit)pDevice->coreaudio.audioUnitCapture); - } - return ma_result_from_OSStatus(status); - } - } - - return MA_SUCCESS; -} - -static ma_result ma_device_stop__coreaudio(ma_device* pDevice) -{ - MA_ASSERT(pDevice != NULL); - - /* It's not clear from the documentation whether or not AudioOutputUnitStop() actually drains the device or not. */ - - if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) { - OSStatus status = ((ma_AudioOutputUnitStop_proc)pDevice->pContext->coreaudio.AudioOutputUnitStop)((AudioUnit)pDevice->coreaudio.audioUnitCapture); - if (status != noErr) { - return ma_result_from_OSStatus(status); - } - } - - if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) { - OSStatus status = ((ma_AudioOutputUnitStop_proc)pDevice->pContext->coreaudio.AudioOutputUnitStop)((AudioUnit)pDevice->coreaudio.audioUnitPlayback); - if (status != noErr) { - return ma_result_from_OSStatus(status); - } - } - - /* We need to wait for the callback to finish before returning. */ - ma_event_wait(&pDevice->coreaudio.stopEvent); - return MA_SUCCESS; -} - - -static ma_result ma_context_uninit__coreaudio(ma_context* pContext) -{ - MA_ASSERT(pContext != NULL); - MA_ASSERT(pContext->backend == ma_backend_coreaudio); - -#if defined(MA_APPLE_MOBILE) - if (!pContext->coreaudio.noAudioSessionDeactivate) { - if (![[AVAudioSession sharedInstance] setActive:false error:nil]) { - ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "Failed to deactivate audio session."); - return MA_FAILED_TO_INIT_BACKEND; - } - } -#endif - -#if !defined(MA_NO_RUNTIME_LINKING) && !defined(MA_APPLE_MOBILE) - ma_dlclose(ma_context_get_log(pContext), pContext->coreaudio.hAudioUnit); - ma_dlclose(ma_context_get_log(pContext), pContext->coreaudio.hCoreAudio); - ma_dlclose(ma_context_get_log(pContext), pContext->coreaudio.hCoreFoundation); -#endif - -#if !defined(MA_APPLE_MOBILE) - ma_context__uninit_device_tracking__coreaudio(pContext); -#endif - - (void)pContext; - return MA_SUCCESS; -} - -#if defined(MA_APPLE_MOBILE) && defined(__IPHONE_12_0) -static AVAudioSessionCategory ma_to_AVAudioSessionCategory(ma_ios_session_category category) -{ - /* The "default" and "none" categories are treated different and should not be used as an input into this function. */ - MA_ASSERT(category != ma_ios_session_category_default); - MA_ASSERT(category != ma_ios_session_category_none); - - switch (category) { - case ma_ios_session_category_ambient: return AVAudioSessionCategoryAmbient; - case ma_ios_session_category_solo_ambient: return AVAudioSessionCategorySoloAmbient; - case ma_ios_session_category_playback: return AVAudioSessionCategoryPlayback; - case ma_ios_session_category_record: return AVAudioSessionCategoryRecord; - case ma_ios_session_category_play_and_record: return AVAudioSessionCategoryPlayAndRecord; - case ma_ios_session_category_multi_route: return AVAudioSessionCategoryMultiRoute; - case ma_ios_session_category_none: return AVAudioSessionCategoryAmbient; - case ma_ios_session_category_default: return AVAudioSessionCategoryAmbient; - default: return AVAudioSessionCategoryAmbient; - } -} -#endif - -static ma_result ma_context_init__coreaudio(ma_context* pContext, const ma_context_config* pConfig, ma_backend_callbacks* pCallbacks) -{ -#if !defined(MA_APPLE_MOBILE) - ma_result result; -#endif - - MA_ASSERT(pConfig != NULL); - MA_ASSERT(pContext != NULL); - -#if defined(MA_APPLE_MOBILE) - @autoreleasepool { - AVAudioSession* pAudioSession = [AVAudioSession sharedInstance]; - AVAudioSessionCategoryOptions options = pConfig->coreaudio.sessionCategoryOptions; - - MA_ASSERT(pAudioSession != NULL); - - if (pConfig->coreaudio.sessionCategory == ma_ios_session_category_default) { - /* - I'm going to use trial and error to determine our default session category. First we'll try PlayAndRecord. If that fails - we'll try Playback and if that fails we'll try record. If all of these fail we'll just not set the category. - */ - #if !defined(MA_APPLE_TV) && !defined(MA_APPLE_WATCH) - options |= AVAudioSessionCategoryOptionDefaultToSpeaker; - #endif - - if ([pAudioSession setCategory: AVAudioSessionCategoryPlayAndRecord withOptions:options error:nil]) { - /* Using PlayAndRecord */ - } else if ([pAudioSession setCategory: AVAudioSessionCategoryPlayback withOptions:options error:nil]) { - /* Using Playback */ - } else if ([pAudioSession setCategory: AVAudioSessionCategoryRecord withOptions:options error:nil]) { - /* Using Record */ - } else { - /* Leave as default? */ - } - } else { - if (pConfig->coreaudio.sessionCategory != ma_ios_session_category_none) { - #if defined(__IPHONE_12_0) - if (![pAudioSession setCategory: ma_to_AVAudioSessionCategory(pConfig->coreaudio.sessionCategory) withOptions:options error:nil]) { - return MA_INVALID_OPERATION; /* Failed to set session category. */ - } - #else - /* Ignore the session category on version 11 and older, but post a warning. */ - ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_WARNING, "Session category only supported in iOS 12 and newer."); - #endif - } - } - - if (!pConfig->coreaudio.noAudioSessionActivate) { - if (![pAudioSession setActive:true error:nil]) { - ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "Failed to activate audio session."); - return MA_FAILED_TO_INIT_BACKEND; - } - } - } -#endif - -#if !defined(MA_NO_RUNTIME_LINKING) && !defined(MA_APPLE_MOBILE) - pContext->coreaudio.hCoreFoundation = ma_dlopen(ma_context_get_log(pContext), "/System/Library/Frameworks/CoreFoundation.framework/CoreFoundation"); - if (pContext->coreaudio.hCoreFoundation == NULL) { - return MA_API_NOT_FOUND; - } - - pContext->coreaudio.CFStringGetCString = ma_dlsym(ma_context_get_log(pContext), pContext->coreaudio.hCoreFoundation, "CFStringGetCString"); - pContext->coreaudio.CFRelease = ma_dlsym(ma_context_get_log(pContext), pContext->coreaudio.hCoreFoundation, "CFRelease"); - - - pContext->coreaudio.hCoreAudio = ma_dlopen(ma_context_get_log(pContext), "/System/Library/Frameworks/CoreAudio.framework/CoreAudio"); - if (pContext->coreaudio.hCoreAudio == NULL) { - ma_dlclose(ma_context_get_log(pContext), pContext->coreaudio.hCoreFoundation); - return MA_API_NOT_FOUND; - } - - pContext->coreaudio.AudioObjectGetPropertyData = ma_dlsym(ma_context_get_log(pContext), pContext->coreaudio.hCoreAudio, "AudioObjectGetPropertyData"); - pContext->coreaudio.AudioObjectGetPropertyDataSize = ma_dlsym(ma_context_get_log(pContext), pContext->coreaudio.hCoreAudio, "AudioObjectGetPropertyDataSize"); - pContext->coreaudio.AudioObjectSetPropertyData = ma_dlsym(ma_context_get_log(pContext), pContext->coreaudio.hCoreAudio, "AudioObjectSetPropertyData"); - pContext->coreaudio.AudioObjectAddPropertyListener = ma_dlsym(ma_context_get_log(pContext), pContext->coreaudio.hCoreAudio, "AudioObjectAddPropertyListener"); - pContext->coreaudio.AudioObjectRemovePropertyListener = ma_dlsym(ma_context_get_log(pContext), pContext->coreaudio.hCoreAudio, "AudioObjectRemovePropertyListener"); - - /* - It looks like Apple has moved some APIs from AudioUnit into AudioToolbox on more recent versions of macOS. They are still - defined in AudioUnit, but just in case they decide to remove them from there entirely I'm going to implement a fallback. - The way it'll work is that it'll first try AudioUnit, and if the required symbols are not present there we'll fall back to - AudioToolbox. - */ - pContext->coreaudio.hAudioUnit = ma_dlopen(ma_context_get_log(pContext), "/System/Library/Frameworks/AudioUnit.framework/AudioUnit"); - if (pContext->coreaudio.hAudioUnit == NULL) { - ma_dlclose(ma_context_get_log(pContext), pContext->coreaudio.hCoreAudio); - ma_dlclose(ma_context_get_log(pContext), pContext->coreaudio.hCoreFoundation); - return MA_API_NOT_FOUND; - } - - if (ma_dlsym(ma_context_get_log(pContext), pContext->coreaudio.hAudioUnit, "AudioComponentFindNext") == NULL) { - /* Couldn't find the required symbols in AudioUnit, so fall back to AudioToolbox. */ - ma_dlclose(ma_context_get_log(pContext), pContext->coreaudio.hAudioUnit); - pContext->coreaudio.hAudioUnit = ma_dlopen(ma_context_get_log(pContext), "/System/Library/Frameworks/AudioToolbox.framework/AudioToolbox"); - if (pContext->coreaudio.hAudioUnit == NULL) { - ma_dlclose(ma_context_get_log(pContext), pContext->coreaudio.hCoreAudio); - ma_dlclose(ma_context_get_log(pContext), pContext->coreaudio.hCoreFoundation); - return MA_API_NOT_FOUND; - } - } - - pContext->coreaudio.AudioComponentFindNext = ma_dlsym(ma_context_get_log(pContext), pContext->coreaudio.hAudioUnit, "AudioComponentFindNext"); - pContext->coreaudio.AudioComponentInstanceDispose = ma_dlsym(ma_context_get_log(pContext), pContext->coreaudio.hAudioUnit, "AudioComponentInstanceDispose"); - pContext->coreaudio.AudioComponentInstanceNew = ma_dlsym(ma_context_get_log(pContext), pContext->coreaudio.hAudioUnit, "AudioComponentInstanceNew"); - pContext->coreaudio.AudioOutputUnitStart = ma_dlsym(ma_context_get_log(pContext), pContext->coreaudio.hAudioUnit, "AudioOutputUnitStart"); - pContext->coreaudio.AudioOutputUnitStop = ma_dlsym(ma_context_get_log(pContext), pContext->coreaudio.hAudioUnit, "AudioOutputUnitStop"); - pContext->coreaudio.AudioUnitAddPropertyListener = ma_dlsym(ma_context_get_log(pContext), pContext->coreaudio.hAudioUnit, "AudioUnitAddPropertyListener"); - pContext->coreaudio.AudioUnitGetPropertyInfo = ma_dlsym(ma_context_get_log(pContext), pContext->coreaudio.hAudioUnit, "AudioUnitGetPropertyInfo"); - pContext->coreaudio.AudioUnitGetProperty = ma_dlsym(ma_context_get_log(pContext), pContext->coreaudio.hAudioUnit, "AudioUnitGetProperty"); - pContext->coreaudio.AudioUnitSetProperty = ma_dlsym(ma_context_get_log(pContext), pContext->coreaudio.hAudioUnit, "AudioUnitSetProperty"); - pContext->coreaudio.AudioUnitInitialize = ma_dlsym(ma_context_get_log(pContext), pContext->coreaudio.hAudioUnit, "AudioUnitInitialize"); - pContext->coreaudio.AudioUnitRender = ma_dlsym(ma_context_get_log(pContext), pContext->coreaudio.hAudioUnit, "AudioUnitRender"); -#else - pContext->coreaudio.CFStringGetCString = (ma_proc)CFStringGetCString; - pContext->coreaudio.CFRelease = (ma_proc)CFRelease; - - #if defined(MA_APPLE_DESKTOP) - pContext->coreaudio.AudioObjectGetPropertyData = (ma_proc)AudioObjectGetPropertyData; - pContext->coreaudio.AudioObjectGetPropertyDataSize = (ma_proc)AudioObjectGetPropertyDataSize; - pContext->coreaudio.AudioObjectSetPropertyData = (ma_proc)AudioObjectSetPropertyData; - pContext->coreaudio.AudioObjectAddPropertyListener = (ma_proc)AudioObjectAddPropertyListener; - pContext->coreaudio.AudioObjectRemovePropertyListener = (ma_proc)AudioObjectRemovePropertyListener; - #endif - - pContext->coreaudio.AudioComponentFindNext = (ma_proc)AudioComponentFindNext; - pContext->coreaudio.AudioComponentInstanceDispose = (ma_proc)AudioComponentInstanceDispose; - pContext->coreaudio.AudioComponentInstanceNew = (ma_proc)AudioComponentInstanceNew; - pContext->coreaudio.AudioOutputUnitStart = (ma_proc)AudioOutputUnitStart; - pContext->coreaudio.AudioOutputUnitStop = (ma_proc)AudioOutputUnitStop; - pContext->coreaudio.AudioUnitAddPropertyListener = (ma_proc)AudioUnitAddPropertyListener; - pContext->coreaudio.AudioUnitGetPropertyInfo = (ma_proc)AudioUnitGetPropertyInfo; - pContext->coreaudio.AudioUnitGetProperty = (ma_proc)AudioUnitGetProperty; - pContext->coreaudio.AudioUnitSetProperty = (ma_proc)AudioUnitSetProperty; - pContext->coreaudio.AudioUnitInitialize = (ma_proc)AudioUnitInitialize; - pContext->coreaudio.AudioUnitRender = (ma_proc)AudioUnitRender; -#endif - - /* Audio component. */ - { - AudioComponentDescription desc; - desc.componentType = kAudioUnitType_Output; - #if defined(MA_APPLE_DESKTOP) - desc.componentSubType = kAudioUnitSubType_HALOutput; - #else - desc.componentSubType = kAudioUnitSubType_RemoteIO; - #endif - desc.componentManufacturer = kAudioUnitManufacturer_Apple; - desc.componentFlags = 0; - desc.componentFlagsMask = 0; - - pContext->coreaudio.component = ((ma_AudioComponentFindNext_proc)pContext->coreaudio.AudioComponentFindNext)(NULL, &desc); - if (pContext->coreaudio.component == NULL) { - #if !defined(MA_NO_RUNTIME_LINKING) && !defined(MA_APPLE_MOBILE) - ma_dlclose(ma_context_get_log(pContext), pContext->coreaudio.hAudioUnit); - ma_dlclose(ma_context_get_log(pContext), pContext->coreaudio.hCoreAudio); - ma_dlclose(ma_context_get_log(pContext), pContext->coreaudio.hCoreFoundation); - #endif - return MA_FAILED_TO_INIT_BACKEND; - } - } - -#if !defined(MA_APPLE_MOBILE) - result = ma_context__init_device_tracking__coreaudio(pContext); - if (result != MA_SUCCESS) { - #if !defined(MA_NO_RUNTIME_LINKING) && !defined(MA_APPLE_MOBILE) - ma_dlclose(ma_context_get_log(pContext), pContext->coreaudio.hAudioUnit); - ma_dlclose(ma_context_get_log(pContext), pContext->coreaudio.hCoreAudio); - ma_dlclose(ma_context_get_log(pContext), pContext->coreaudio.hCoreFoundation); - #endif - return result; - } -#endif - - pContext->coreaudio.noAudioSessionDeactivate = pConfig->coreaudio.noAudioSessionDeactivate; - - pCallbacks->onContextInit = ma_context_init__coreaudio; - pCallbacks->onContextUninit = ma_context_uninit__coreaudio; - pCallbacks->onContextEnumerateDevices = ma_context_enumerate_devices__coreaudio; - pCallbacks->onContextGetDeviceInfo = ma_context_get_device_info__coreaudio; - pCallbacks->onDeviceInit = ma_device_init__coreaudio; - pCallbacks->onDeviceUninit = ma_device_uninit__coreaudio; - pCallbacks->onDeviceStart = ma_device_start__coreaudio; - pCallbacks->onDeviceStop = ma_device_stop__coreaudio; - pCallbacks->onDeviceRead = NULL; - pCallbacks->onDeviceWrite = NULL; - pCallbacks->onDeviceDataLoop = NULL; - - return MA_SUCCESS; -} -#endif /* MA_HAS_COREAUDIO */ - - - -/****************************************************************************** - -sndio Backend - -******************************************************************************/ -#ifdef MA_HAS_SNDIO -#include - -/* -Only supporting OpenBSD. This did not work very well at all on FreeBSD when I tried it. Not sure if this is due -to miniaudio's implementation or if it's some kind of system configuration issue, but basically the default device -just doesn't emit any sound, or at times you'll hear tiny pieces. I will consider enabling this when there's -demand for it or if I can get it tested and debugged more thoroughly. -*/ -#if 0 -#if defined(__NetBSD__) || defined(__OpenBSD__) -#include -#endif -#if defined(__FreeBSD__) || defined(__DragonFly__) -#include -#endif -#endif - -#define MA_SIO_DEVANY "default" -#define MA_SIO_PLAY 1 -#define MA_SIO_REC 2 -#define MA_SIO_NENC 8 -#define MA_SIO_NCHAN 8 -#define MA_SIO_NRATE 16 -#define MA_SIO_NCONF 4 - -struct ma_sio_hdl; /* <-- Opaque */ - -struct ma_sio_par -{ - unsigned int bits; - unsigned int bps; - unsigned int sig; - unsigned int le; - unsigned int msb; - unsigned int rchan; - unsigned int pchan; - unsigned int rate; - unsigned int bufsz; - unsigned int xrun; - unsigned int round; - unsigned int appbufsz; - int __pad[3]; - unsigned int __magic; -}; - -struct ma_sio_enc -{ - unsigned int bits; - unsigned int bps; - unsigned int sig; - unsigned int le; - unsigned int msb; -}; - -struct ma_sio_conf -{ - unsigned int enc; - unsigned int rchan; - unsigned int pchan; - unsigned int rate; -}; - -struct ma_sio_cap -{ - struct ma_sio_enc enc[MA_SIO_NENC]; - unsigned int rchan[MA_SIO_NCHAN]; - unsigned int pchan[MA_SIO_NCHAN]; - unsigned int rate[MA_SIO_NRATE]; - int __pad[7]; - unsigned int nconf; - struct ma_sio_conf confs[MA_SIO_NCONF]; -}; - -typedef struct ma_sio_hdl* (* ma_sio_open_proc) (const char*, unsigned int, int); -typedef void (* ma_sio_close_proc) (struct ma_sio_hdl*); -typedef int (* ma_sio_setpar_proc) (struct ma_sio_hdl*, struct ma_sio_par*); -typedef int (* ma_sio_getpar_proc) (struct ma_sio_hdl*, struct ma_sio_par*); -typedef int (* ma_sio_getcap_proc) (struct ma_sio_hdl*, struct ma_sio_cap*); -typedef size_t (* ma_sio_write_proc) (struct ma_sio_hdl*, const void*, size_t); -typedef size_t (* ma_sio_read_proc) (struct ma_sio_hdl*, void*, size_t); -typedef int (* ma_sio_start_proc) (struct ma_sio_hdl*); -typedef int (* ma_sio_stop_proc) (struct ma_sio_hdl*); -typedef int (* ma_sio_initpar_proc)(struct ma_sio_par*); - -static ma_uint32 ma_get_standard_sample_rate_priority_index__sndio(ma_uint32 sampleRate) /* Lower = higher priority */ -{ - ma_uint32 i; - for (i = 0; i < ma_countof(g_maStandardSampleRatePriorities); ++i) { - if (g_maStandardSampleRatePriorities[i] == sampleRate) { - return i; - } - } - - return (ma_uint32)-1; -} - -static ma_format ma_format_from_sio_enc__sndio(unsigned int bits, unsigned int bps, unsigned int sig, unsigned int le, unsigned int msb) -{ - /* We only support native-endian right now. */ - if ((ma_is_little_endian() && le == 0) || (ma_is_big_endian() && le == 1)) { - return ma_format_unknown; - } - - if (bits == 8 && bps == 1 && sig == 0) { - return ma_format_u8; - } - if (bits == 16 && bps == 2 && sig == 1) { - return ma_format_s16; - } - if (bits == 24 && bps == 3 && sig == 1) { - return ma_format_s24; - } - if (bits == 24 && bps == 4 && sig == 1 && msb == 0) { - /*return ma_format_s24_32;*/ - } - if (bits == 32 && bps == 4 && sig == 1) { - return ma_format_s32; - } - - return ma_format_unknown; -} - -static ma_format ma_find_best_format_from_sio_cap__sndio(struct ma_sio_cap* caps) -{ - ma_format bestFormat; - unsigned int iConfig; - - MA_ASSERT(caps != NULL); - - bestFormat = ma_format_unknown; - for (iConfig = 0; iConfig < caps->nconf; iConfig += 1) { - unsigned int iEncoding; - for (iEncoding = 0; iEncoding < MA_SIO_NENC; iEncoding += 1) { - unsigned int bits; - unsigned int bps; - unsigned int sig; - unsigned int le; - unsigned int msb; - ma_format format; - - if ((caps->confs[iConfig].enc & (1UL << iEncoding)) == 0) { - continue; - } - - bits = caps->enc[iEncoding].bits; - bps = caps->enc[iEncoding].bps; - sig = caps->enc[iEncoding].sig; - le = caps->enc[iEncoding].le; - msb = caps->enc[iEncoding].msb; - format = ma_format_from_sio_enc__sndio(bits, bps, sig, le, msb); - if (format == ma_format_unknown) { - continue; /* Format not supported. */ - } - - if (bestFormat == ma_format_unknown) { - bestFormat = format; - } else { - if (ma_get_format_priority_index(bestFormat) > ma_get_format_priority_index(format)) { /* <-- Lower = better. */ - bestFormat = format; - } - } - } - } - - return bestFormat; -} - -static ma_uint32 ma_find_best_channels_from_sio_cap__sndio(struct ma_sio_cap* caps, ma_device_type deviceType, ma_format requiredFormat) -{ - ma_uint32 maxChannels; - unsigned int iConfig; - - MA_ASSERT(caps != NULL); - MA_ASSERT(requiredFormat != ma_format_unknown); - - /* Just pick whatever configuration has the most channels. */ - maxChannels = 0; - for (iConfig = 0; iConfig < caps->nconf; iConfig += 1) { - /* The encoding should be of requiredFormat. */ - unsigned int iEncoding; - for (iEncoding = 0; iEncoding < MA_SIO_NENC; iEncoding += 1) { - unsigned int iChannel; - unsigned int bits; - unsigned int bps; - unsigned int sig; - unsigned int le; - unsigned int msb; - ma_format format; - - if ((caps->confs[iConfig].enc & (1UL << iEncoding)) == 0) { - continue; - } - - bits = caps->enc[iEncoding].bits; - bps = caps->enc[iEncoding].bps; - sig = caps->enc[iEncoding].sig; - le = caps->enc[iEncoding].le; - msb = caps->enc[iEncoding].msb; - format = ma_format_from_sio_enc__sndio(bits, bps, sig, le, msb); - if (format != requiredFormat) { - continue; - } - - /* Getting here means the format is supported. Iterate over each channel count and grab the biggest one. */ - for (iChannel = 0; iChannel < MA_SIO_NCHAN; iChannel += 1) { - unsigned int chan = 0; - unsigned int channels; - - if (deviceType == ma_device_type_playback) { - chan = caps->confs[iConfig].pchan; - } else { - chan = caps->confs[iConfig].rchan; - } - - if ((chan & (1UL << iChannel)) == 0) { - continue; - } - - if (deviceType == ma_device_type_playback) { - channels = caps->pchan[iChannel]; - } else { - channels = caps->rchan[iChannel]; - } - - if (maxChannels < channels) { - maxChannels = channels; - } - } - } - } - - return maxChannels; -} - -static ma_uint32 ma_find_best_sample_rate_from_sio_cap__sndio(struct ma_sio_cap* caps, ma_device_type deviceType, ma_format requiredFormat, ma_uint32 requiredChannels) -{ - ma_uint32 firstSampleRate; - ma_uint32 bestSampleRate; - unsigned int iConfig; - - MA_ASSERT(caps != NULL); - MA_ASSERT(requiredFormat != ma_format_unknown); - MA_ASSERT(requiredChannels > 0); - MA_ASSERT(requiredChannels <= MA_MAX_CHANNELS); - - firstSampleRate = 0; /* <-- If the device does not support a standard rate we'll fall back to the first one that's found. */ - bestSampleRate = 0; - - for (iConfig = 0; iConfig < caps->nconf; iConfig += 1) { - /* The encoding should be of requiredFormat. */ - unsigned int iEncoding; - for (iEncoding = 0; iEncoding < MA_SIO_NENC; iEncoding += 1) { - unsigned int iChannel; - unsigned int bits; - unsigned int bps; - unsigned int sig; - unsigned int le; - unsigned int msb; - ma_format format; - - if ((caps->confs[iConfig].enc & (1UL << iEncoding)) == 0) { - continue; - } - - bits = caps->enc[iEncoding].bits; - bps = caps->enc[iEncoding].bps; - sig = caps->enc[iEncoding].sig; - le = caps->enc[iEncoding].le; - msb = caps->enc[iEncoding].msb; - format = ma_format_from_sio_enc__sndio(bits, bps, sig, le, msb); - if (format != requiredFormat) { - continue; - } - - /* Getting here means the format is supported. Iterate over each channel count and grab the biggest one. */ - for (iChannel = 0; iChannel < MA_SIO_NCHAN; iChannel += 1) { - unsigned int chan = 0; - unsigned int channels; - unsigned int iRate; - - if (deviceType == ma_device_type_playback) { - chan = caps->confs[iConfig].pchan; - } else { - chan = caps->confs[iConfig].rchan; - } - - if ((chan & (1UL << iChannel)) == 0) { - continue; - } - - if (deviceType == ma_device_type_playback) { - channels = caps->pchan[iChannel]; - } else { - channels = caps->rchan[iChannel]; - } - - if (channels != requiredChannels) { - continue; - } - - /* Getting here means we have found a compatible encoding/channel pair. */ - for (iRate = 0; iRate < MA_SIO_NRATE; iRate += 1) { - ma_uint32 rate = (ma_uint32)caps->rate[iRate]; - ma_uint32 ratePriority; - - if (firstSampleRate == 0) { - firstSampleRate = rate; - } - - /* Disregard this rate if it's not a standard one. */ - ratePriority = ma_get_standard_sample_rate_priority_index__sndio(rate); - if (ratePriority == (ma_uint32)-1) { - continue; - } - - if (ma_get_standard_sample_rate_priority_index__sndio(bestSampleRate) > ratePriority) { /* Lower = better. */ - bestSampleRate = rate; - } - } - } - } - } - - /* If a standard sample rate was not found just fall back to the first one that was iterated. */ - if (bestSampleRate == 0) { - bestSampleRate = firstSampleRate; - } - - return bestSampleRate; -} - - -static ma_result ma_context_enumerate_devices__sndio(ma_context* pContext, ma_enum_devices_callback_proc callback, void* pUserData) -{ - ma_bool32 isTerminating = MA_FALSE; - struct ma_sio_hdl* handle; - - MA_ASSERT(pContext != NULL); - MA_ASSERT(callback != NULL); - - /* sndio doesn't seem to have a good device enumeration API, so I'm therefore only enumerating over default devices for now. */ - - /* Playback. */ - if (!isTerminating) { - handle = ((ma_sio_open_proc)pContext->sndio.sio_open)(MA_SIO_DEVANY, MA_SIO_PLAY, 0); - if (handle != NULL) { - /* Supports playback. */ - ma_device_info deviceInfo; - MA_ZERO_OBJECT(&deviceInfo); - ma_strcpy_s(deviceInfo.id.sndio, sizeof(deviceInfo.id.sndio), MA_SIO_DEVANY); - ma_strcpy_s(deviceInfo.name, sizeof(deviceInfo.name), MA_DEFAULT_PLAYBACK_DEVICE_NAME); - - isTerminating = !callback(pContext, ma_device_type_playback, &deviceInfo, pUserData); - - ((ma_sio_close_proc)pContext->sndio.sio_close)(handle); - } - } - - /* Capture. */ - if (!isTerminating) { - handle = ((ma_sio_open_proc)pContext->sndio.sio_open)(MA_SIO_DEVANY, MA_SIO_REC, 0); - if (handle != NULL) { - /* Supports capture. */ - ma_device_info deviceInfo; - MA_ZERO_OBJECT(&deviceInfo); - ma_strcpy_s(deviceInfo.id.sndio, sizeof(deviceInfo.id.sndio), "default"); - ma_strcpy_s(deviceInfo.name, sizeof(deviceInfo.name), MA_DEFAULT_CAPTURE_DEVICE_NAME); - - isTerminating = !callback(pContext, ma_device_type_capture, &deviceInfo, pUserData); - - ((ma_sio_close_proc)pContext->sndio.sio_close)(handle); - } - } - - return MA_SUCCESS; -} - -static ma_result ma_context_get_device_info__sndio(ma_context* pContext, ma_device_type deviceType, const ma_device_id* pDeviceID, ma_device_info* pDeviceInfo) -{ - char devid[256]; - struct ma_sio_hdl* handle; - struct ma_sio_cap caps; - unsigned int iConfig; - - MA_ASSERT(pContext != NULL); - - /* We need to open the device before we can get information about it. */ - if (pDeviceID == NULL) { - ma_strcpy_s(devid, sizeof(devid), MA_SIO_DEVANY); - ma_strcpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), (deviceType == ma_device_type_playback) ? MA_DEFAULT_PLAYBACK_DEVICE_NAME : MA_DEFAULT_CAPTURE_DEVICE_NAME); - } else { - ma_strcpy_s(devid, sizeof(devid), pDeviceID->sndio); - ma_strcpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), devid); - } - - handle = ((ma_sio_open_proc)pContext->sndio.sio_open)(devid, (deviceType == ma_device_type_playback) ? MA_SIO_PLAY : MA_SIO_REC, 0); - if (handle == NULL) { - return MA_NO_DEVICE; - } - - if (((ma_sio_getcap_proc)pContext->sndio.sio_getcap)(handle, &caps) == 0) { - return MA_ERROR; - } - - pDeviceInfo->nativeDataFormatCount = 0; - - for (iConfig = 0; iConfig < caps.nconf; iConfig += 1) { - /* - The main thing we care about is that the encoding is supported by miniaudio. If it is, we want to give - preference to some formats over others. - */ - unsigned int iEncoding; - unsigned int iChannel; - unsigned int iRate; - - for (iEncoding = 0; iEncoding < MA_SIO_NENC; iEncoding += 1) { - unsigned int bits; - unsigned int bps; - unsigned int sig; - unsigned int le; - unsigned int msb; - ma_format format; - - if ((caps.confs[iConfig].enc & (1UL << iEncoding)) == 0) { - continue; - } - - bits = caps.enc[iEncoding].bits; - bps = caps.enc[iEncoding].bps; - sig = caps.enc[iEncoding].sig; - le = caps.enc[iEncoding].le; - msb = caps.enc[iEncoding].msb; - format = ma_format_from_sio_enc__sndio(bits, bps, sig, le, msb); - if (format == ma_format_unknown) { - continue; /* Format not supported. */ - } - - - /* Channels. */ - for (iChannel = 0; iChannel < MA_SIO_NCHAN; iChannel += 1) { - unsigned int chan = 0; - unsigned int channels; - - if (deviceType == ma_device_type_playback) { - chan = caps.confs[iConfig].pchan; - } else { - chan = caps.confs[iConfig].rchan; - } - - if ((chan & (1UL << iChannel)) == 0) { - continue; - } - - if (deviceType == ma_device_type_playback) { - channels = caps.pchan[iChannel]; - } else { - channels = caps.rchan[iChannel]; - } - - - /* Sample Rates. */ - for (iRate = 0; iRate < MA_SIO_NRATE; iRate += 1) { - if ((caps.confs[iConfig].rate & (1UL << iRate)) != 0) { - ma_device_info_add_native_data_format(pDeviceInfo, format, channels, caps.rate[iRate], 0); - } - } - } - } - } - - ((ma_sio_close_proc)pContext->sndio.sio_close)(handle); - return MA_SUCCESS; -} - -static ma_result ma_device_uninit__sndio(ma_device* pDevice) -{ - MA_ASSERT(pDevice != NULL); - - if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) { - ((ma_sio_close_proc)pDevice->pContext->sndio.sio_close)((struct ma_sio_hdl*)pDevice->sndio.handleCapture); - } - - if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) { - ((ma_sio_close_proc)pDevice->pContext->sndio.sio_close)((struct ma_sio_hdl*)pDevice->sndio.handlePlayback); - } - - return MA_SUCCESS; -} - -static ma_result ma_device_init_handle__sndio(ma_device* pDevice, const ma_device_config* pConfig, ma_device_descriptor* pDescriptor, ma_device_type deviceType) -{ - const char* pDeviceName; - ma_ptr handle; - int openFlags = 0; - struct ma_sio_cap caps; - struct ma_sio_par par; - const ma_device_id* pDeviceID; - ma_format format; - ma_uint32 channels; - ma_uint32 sampleRate; - ma_format internalFormat; - ma_uint32 internalChannels; - ma_uint32 internalSampleRate; - ma_uint32 internalPeriodSizeInFrames; - ma_uint32 internalPeriods; - - MA_ASSERT(pConfig != NULL); - MA_ASSERT(deviceType != ma_device_type_duplex); - MA_ASSERT(pDevice != NULL); - - if (deviceType == ma_device_type_capture) { - openFlags = MA_SIO_REC; - } else { - openFlags = MA_SIO_PLAY; - } - - pDeviceID = pDescriptor->pDeviceID; - format = pDescriptor->format; - channels = pDescriptor->channels; - sampleRate = pDescriptor->sampleRate; - - pDeviceName = MA_SIO_DEVANY; - if (pDeviceID != NULL) { - pDeviceName = pDeviceID->sndio; - } - - handle = (ma_ptr)((ma_sio_open_proc)pDevice->pContext->sndio.sio_open)(pDeviceName, openFlags, 0); - if (handle == NULL) { - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[sndio] Failed to open device."); - return MA_FAILED_TO_OPEN_BACKEND_DEVICE; - } - - /* We need to retrieve the device caps to determine the most appropriate format to use. */ - if (((ma_sio_getcap_proc)pDevice->pContext->sndio.sio_getcap)((struct ma_sio_hdl*)handle, &caps) == 0) { - ((ma_sio_close_proc)pDevice->pContext->sndio.sio_close)((struct ma_sio_hdl*)handle); - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[sndio] Failed to retrieve device caps."); - return MA_ERROR; - } - - /* - Note: sndio reports a huge range of available channels. This is inconvenient for us because there's no real - way, as far as I can tell, to get the _actual_ channel count of the device. I'm therefore restricting this - to the requested channels, regardless of whether or not the default channel count is requested. - - For hardware devices, I'm suspecting only a single channel count will be reported and we can safely use the - value returned by ma_find_best_channels_from_sio_cap__sndio(). - */ - if (deviceType == ma_device_type_capture) { - if (format == ma_format_unknown) { - format = ma_find_best_format_from_sio_cap__sndio(&caps); - } - - if (channels == 0) { - if (strlen(pDeviceName) > strlen("rsnd/") && strncmp(pDeviceName, "rsnd/", strlen("rsnd/")) == 0) { - channels = ma_find_best_channels_from_sio_cap__sndio(&caps, deviceType, format); - } else { - channels = MA_DEFAULT_CHANNELS; - } - } - } else { - if (format == ma_format_unknown) { - format = ma_find_best_format_from_sio_cap__sndio(&caps); - } - - if (channels == 0) { - if (strlen(pDeviceName) > strlen("rsnd/") && strncmp(pDeviceName, "rsnd/", strlen("rsnd/")) == 0) { - channels = ma_find_best_channels_from_sio_cap__sndio(&caps, deviceType, format); - } else { - channels = MA_DEFAULT_CHANNELS; - } - } - } - - if (sampleRate == 0) { - sampleRate = ma_find_best_sample_rate_from_sio_cap__sndio(&caps, pConfig->deviceType, format, channels); - } - - - ((ma_sio_initpar_proc)pDevice->pContext->sndio.sio_initpar)(&par); - par.msb = 0; - par.le = ma_is_little_endian(); - - switch (format) { - case ma_format_u8: - { - par.bits = 8; - par.bps = 1; - par.sig = 0; - } break; - - case ma_format_s24: - { - par.bits = 24; - par.bps = 3; - par.sig = 1; - } break; - - case ma_format_s32: - { - par.bits = 32; - par.bps = 4; - par.sig = 1; - } break; - - case ma_format_s16: - case ma_format_f32: - case ma_format_unknown: - default: - { - par.bits = 16; - par.bps = 2; - par.sig = 1; - } break; - } - - if (deviceType == ma_device_type_capture) { - par.rchan = channels; - } else { - par.pchan = channels; - } - - par.rate = sampleRate; - - internalPeriodSizeInFrames = ma_calculate_buffer_size_in_frames_from_descriptor(pDescriptor, par.rate, pConfig->performanceProfile); - - par.round = internalPeriodSizeInFrames; - par.appbufsz = par.round * pDescriptor->periodCount; - - if (((ma_sio_setpar_proc)pDevice->pContext->sndio.sio_setpar)((struct ma_sio_hdl*)handle, &par) == 0) { - ((ma_sio_close_proc)pDevice->pContext->sndio.sio_close)((struct ma_sio_hdl*)handle); - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[sndio] Failed to set buffer size."); - return MA_ERROR; - } - - if (((ma_sio_getpar_proc)pDevice->pContext->sndio.sio_getpar)((struct ma_sio_hdl*)handle, &par) == 0) { - ((ma_sio_close_proc)pDevice->pContext->sndio.sio_close)((struct ma_sio_hdl*)handle); - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[sndio] Failed to retrieve buffer size."); - return MA_ERROR; - } - - internalFormat = ma_format_from_sio_enc__sndio(par.bits, par.bps, par.sig, par.le, par.msb); - internalChannels = (deviceType == ma_device_type_capture) ? par.rchan : par.pchan; - internalSampleRate = par.rate; - internalPeriods = par.appbufsz / par.round; - internalPeriodSizeInFrames = par.round; - - if (deviceType == ma_device_type_capture) { - pDevice->sndio.handleCapture = handle; - } else { - pDevice->sndio.handlePlayback = handle; - } - - pDescriptor->format = internalFormat; - pDescriptor->channels = internalChannels; - pDescriptor->sampleRate = internalSampleRate; - ma_channel_map_init_standard(ma_standard_channel_map_sndio, pDescriptor->channelMap, ma_countof(pDescriptor->channelMap), internalChannels); - pDescriptor->periodSizeInFrames = internalPeriodSizeInFrames; - pDescriptor->periodCount = internalPeriods; - - return MA_SUCCESS; -} - -static ma_result ma_device_init__sndio(ma_device* pDevice, const ma_device_config* pConfig, ma_device_descriptor* pDescriptorPlayback, ma_device_descriptor* pDescriptorCapture) -{ - MA_ASSERT(pDevice != NULL); - - MA_ZERO_OBJECT(&pDevice->sndio); - - if (pConfig->deviceType == ma_device_type_loopback) { - return MA_DEVICE_TYPE_NOT_SUPPORTED; - } - - if (pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) { - ma_result result = ma_device_init_handle__sndio(pDevice, pConfig, pDescriptorCapture, ma_device_type_capture); - if (result != MA_SUCCESS) { - return result; - } - } - - if (pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) { - ma_result result = ma_device_init_handle__sndio(pDevice, pConfig, pDescriptorPlayback, ma_device_type_playback); - if (result != MA_SUCCESS) { - return result; - } - } - - return MA_SUCCESS; -} - -static ma_result ma_device_start__sndio(ma_device* pDevice) -{ - MA_ASSERT(pDevice != NULL); - - if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) { - ((ma_sio_start_proc)pDevice->pContext->sndio.sio_start)((struct ma_sio_hdl*)pDevice->sndio.handleCapture); - } - - if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) { - ((ma_sio_start_proc)pDevice->pContext->sndio.sio_start)((struct ma_sio_hdl*)pDevice->sndio.handlePlayback); /* <-- Doesn't actually playback until data is written. */ - } - - return MA_SUCCESS; -} - -static ma_result ma_device_stop__sndio(ma_device* pDevice) -{ - MA_ASSERT(pDevice != NULL); - - /* - From the documentation: - - The sio_stop() function puts the audio subsystem in the same state as before sio_start() is called. It stops recording, drains the play buffer and then - stops playback. If samples to play are queued but playback hasn't started yet then playback is forced immediately; playback will actually stop once the - buffer is drained. In no case are samples in the play buffer discarded. - - Therefore, sio_stop() performs all of the necessary draining for us. - */ - - if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) { - ((ma_sio_stop_proc)pDevice->pContext->sndio.sio_stop)((struct ma_sio_hdl*)pDevice->sndio.handleCapture); - } - - if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) { - ((ma_sio_stop_proc)pDevice->pContext->sndio.sio_stop)((struct ma_sio_hdl*)pDevice->sndio.handlePlayback); - } - - return MA_SUCCESS; -} - -static ma_result ma_device_write__sndio(ma_device* pDevice, const void* pPCMFrames, ma_uint32 frameCount, ma_uint32* pFramesWritten) -{ - int result; - - if (pFramesWritten != NULL) { - *pFramesWritten = 0; - } - - result = ((ma_sio_write_proc)pDevice->pContext->sndio.sio_write)((struct ma_sio_hdl*)pDevice->sndio.handlePlayback, pPCMFrames, frameCount * ma_get_bytes_per_frame(pDevice->playback.internalFormat, pDevice->playback.internalChannels)); - if (result == 0) { - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[sndio] Failed to send data from the client to the device."); - return MA_IO_ERROR; - } - - if (pFramesWritten != NULL) { - *pFramesWritten = frameCount; - } - - return MA_SUCCESS; -} - -static ma_result ma_device_read__sndio(ma_device* pDevice, void* pPCMFrames, ma_uint32 frameCount, ma_uint32* pFramesRead) -{ - int result; - - if (pFramesRead != NULL) { - *pFramesRead = 0; - } - - result = ((ma_sio_read_proc)pDevice->pContext->sndio.sio_read)((struct ma_sio_hdl*)pDevice->sndio.handleCapture, pPCMFrames, frameCount * ma_get_bytes_per_frame(pDevice->capture.internalFormat, pDevice->capture.internalChannels)); - if (result == 0) { - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[sndio] Failed to read data from the device to be sent to the device."); - return MA_IO_ERROR; - } - - if (pFramesRead != NULL) { - *pFramesRead = frameCount; - } - - return MA_SUCCESS; -} - -static ma_result ma_context_uninit__sndio(ma_context* pContext) -{ - MA_ASSERT(pContext != NULL); - MA_ASSERT(pContext->backend == ma_backend_sndio); - - (void)pContext; - return MA_SUCCESS; -} - -static ma_result ma_context_init__sndio(ma_context* pContext, const ma_context_config* pConfig, ma_backend_callbacks* pCallbacks) -{ -#ifndef MA_NO_RUNTIME_LINKING - const char* libsndioNames[] = { - "libsndio.so" - }; - size_t i; - - for (i = 0; i < ma_countof(libsndioNames); ++i) { - pContext->sndio.sndioSO = ma_dlopen(ma_context_get_log(pContext), libsndioNames[i]); - if (pContext->sndio.sndioSO != NULL) { - break; - } - } - - if (pContext->sndio.sndioSO == NULL) { - return MA_NO_BACKEND; - } - - pContext->sndio.sio_open = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->sndio.sndioSO, "sio_open"); - pContext->sndio.sio_close = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->sndio.sndioSO, "sio_close"); - pContext->sndio.sio_setpar = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->sndio.sndioSO, "sio_setpar"); - pContext->sndio.sio_getpar = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->sndio.sndioSO, "sio_getpar"); - pContext->sndio.sio_getcap = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->sndio.sndioSO, "sio_getcap"); - pContext->sndio.sio_write = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->sndio.sndioSO, "sio_write"); - pContext->sndio.sio_read = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->sndio.sndioSO, "sio_read"); - pContext->sndio.sio_start = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->sndio.sndioSO, "sio_start"); - pContext->sndio.sio_stop = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->sndio.sndioSO, "sio_stop"); - pContext->sndio.sio_initpar = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->sndio.sndioSO, "sio_initpar"); -#else - pContext->sndio.sio_open = sio_open; - pContext->sndio.sio_close = sio_close; - pContext->sndio.sio_setpar = sio_setpar; - pContext->sndio.sio_getpar = sio_getpar; - pContext->sndio.sio_getcap = sio_getcap; - pContext->sndio.sio_write = sio_write; - pContext->sndio.sio_read = sio_read; - pContext->sndio.sio_start = sio_start; - pContext->sndio.sio_stop = sio_stop; - pContext->sndio.sio_initpar = sio_initpar; -#endif - - pCallbacks->onContextInit = ma_context_init__sndio; - pCallbacks->onContextUninit = ma_context_uninit__sndio; - pCallbacks->onContextEnumerateDevices = ma_context_enumerate_devices__sndio; - pCallbacks->onContextGetDeviceInfo = ma_context_get_device_info__sndio; - pCallbacks->onDeviceInit = ma_device_init__sndio; - pCallbacks->onDeviceUninit = ma_device_uninit__sndio; - pCallbacks->onDeviceStart = ma_device_start__sndio; - pCallbacks->onDeviceStop = ma_device_stop__sndio; - pCallbacks->onDeviceRead = ma_device_read__sndio; - pCallbacks->onDeviceWrite = ma_device_write__sndio; - pCallbacks->onDeviceDataLoop = NULL; - - (void)pConfig; - return MA_SUCCESS; -} -#endif /* MA_HAS_SNDIO */ - - - -/****************************************************************************** - -audio(4) Backend - -******************************************************************************/ -#ifdef MA_HAS_AUDIO4 -#include -#include -#include -#include -#include -#include -#include - -#ifdef __NetBSD__ -#include -#endif - -#if defined(__OpenBSD__) - #include - #if defined(OpenBSD) && OpenBSD >= 201709 - #define MA_AUDIO4_USE_NEW_API - #endif -#endif - -static void ma_construct_device_id__audio4(char* id, size_t idSize, const char* base, int deviceIndex) -{ - size_t baseLen; - - MA_ASSERT(id != NULL); - MA_ASSERT(idSize > 0); - MA_ASSERT(deviceIndex >= 0); - - baseLen = strlen(base); - MA_ASSERT(idSize > baseLen); - - ma_strcpy_s(id, idSize, base); - ma_itoa_s(deviceIndex, id+baseLen, idSize-baseLen, 10); -} - -static ma_result ma_extract_device_index_from_id__audio4(const char* id, const char* base, int* pIndexOut) -{ - size_t idLen; - size_t baseLen; - const char* deviceIndexStr; - - MA_ASSERT(id != NULL); - MA_ASSERT(base != NULL); - MA_ASSERT(pIndexOut != NULL); - - idLen = strlen(id); - baseLen = strlen(base); - if (idLen <= baseLen) { - return MA_ERROR; /* Doesn't look like the id starts with the base. */ - } - - if (strncmp(id, base, baseLen) != 0) { - return MA_ERROR; /* ID does not begin with base. */ - } - - deviceIndexStr = id + baseLen; - if (deviceIndexStr[0] == '\0') { - return MA_ERROR; /* No index specified in the ID. */ - } - - if (pIndexOut) { - *pIndexOut = atoi(deviceIndexStr); - } - - return MA_SUCCESS; -} - - -#if !defined(MA_AUDIO4_USE_NEW_API) /* Old API */ -static ma_format ma_format_from_encoding__audio4(unsigned int encoding, unsigned int precision) -{ - if (precision == 8 && (encoding == AUDIO_ENCODING_ULINEAR || encoding == AUDIO_ENCODING_ULINEAR || encoding == AUDIO_ENCODING_ULINEAR_LE || encoding == AUDIO_ENCODING_ULINEAR_BE)) { - return ma_format_u8; - } else { - if (ma_is_little_endian() && encoding == AUDIO_ENCODING_SLINEAR_LE) { - if (precision == 16) { - return ma_format_s16; - } else if (precision == 24) { - return ma_format_s24; - } else if (precision == 32) { - return ma_format_s32; - } - } else if (ma_is_big_endian() && encoding == AUDIO_ENCODING_SLINEAR_BE) { - if (precision == 16) { - return ma_format_s16; - } else if (precision == 24) { - return ma_format_s24; - } else if (precision == 32) { - return ma_format_s32; - } - } - } - - return ma_format_unknown; /* Encoding not supported. */ -} - -static void ma_encoding_from_format__audio4(ma_format format, unsigned int* pEncoding, unsigned int* pPrecision) -{ - MA_ASSERT(pEncoding != NULL); - MA_ASSERT(pPrecision != NULL); - - switch (format) - { - case ma_format_u8: - { - *pEncoding = AUDIO_ENCODING_ULINEAR; - *pPrecision = 8; - } break; - - case ma_format_s24: - { - *pEncoding = (ma_is_little_endian()) ? AUDIO_ENCODING_SLINEAR_LE : AUDIO_ENCODING_SLINEAR_BE; - *pPrecision = 24; - } break; - - case ma_format_s32: - { - *pEncoding = (ma_is_little_endian()) ? AUDIO_ENCODING_SLINEAR_LE : AUDIO_ENCODING_SLINEAR_BE; - *pPrecision = 32; - } break; - - case ma_format_s16: - case ma_format_f32: - case ma_format_unknown: - default: - { - *pEncoding = (ma_is_little_endian()) ? AUDIO_ENCODING_SLINEAR_LE : AUDIO_ENCODING_SLINEAR_BE; - *pPrecision = 16; - } break; - } -} - -static ma_format ma_format_from_prinfo__audio4(struct audio_prinfo* prinfo) -{ - return ma_format_from_encoding__audio4(prinfo->encoding, prinfo->precision); -} - -static ma_format ma_best_format_from_fd__audio4(int fd, ma_format preferredFormat) -{ - audio_encoding_t encoding; - ma_uint32 iFormat; - int counter = 0; - - /* First check to see if the preferred format is supported. */ - if (preferredFormat != ma_format_unknown) { - counter = 0; - for (;;) { - MA_ZERO_OBJECT(&encoding); - encoding.index = counter; - if (ioctl(fd, AUDIO_GETENC, &encoding) < 0) { - break; - } - - if (preferredFormat == ma_format_from_encoding__audio4(encoding.encoding, encoding.precision)) { - return preferredFormat; /* Found the preferred format. */ - } - - /* Getting here means this encoding does not match our preferred format so we need to more on to the next encoding. */ - counter += 1; - } - } - - /* Getting here means our preferred format is not supported, so fall back to our standard priorities. */ - for (iFormat = 0; iFormat < ma_countof(g_maFormatPriorities); iFormat += 1) { - ma_format format = g_maFormatPriorities[iFormat]; - - counter = 0; - for (;;) { - MA_ZERO_OBJECT(&encoding); - encoding.index = counter; - if (ioctl(fd, AUDIO_GETENC, &encoding) < 0) { - break; - } - - if (format == ma_format_from_encoding__audio4(encoding.encoding, encoding.precision)) { - return format; /* Found a workable format. */ - } - - /* Getting here means this encoding does not match our preferred format so we need to more on to the next encoding. */ - counter += 1; - } - } - - /* Getting here means not appropriate format was found. */ - return ma_format_unknown; -} -#else -static ma_format ma_format_from_swpar__audio4(struct audio_swpar* par) -{ - if (par->bits == 8 && par->bps == 1 && par->sig == 0) { - return ma_format_u8; - } - if (par->bits == 16 && par->bps == 2 && par->sig == 1 && par->le == ma_is_little_endian()) { - return ma_format_s16; - } - if (par->bits == 24 && par->bps == 3 && par->sig == 1 && par->le == ma_is_little_endian()) { - return ma_format_s24; - } - if (par->bits == 32 && par->bps == 4 && par->sig == 1 && par->le == ma_is_little_endian()) { - return ma_format_f32; - } - - /* Format not supported. */ - return ma_format_unknown; -} -#endif - -static ma_result ma_context_get_device_info_from_fd__audio4(ma_context* pContext, ma_device_type deviceType, int fd, ma_device_info* pDeviceInfo) -{ - audio_device_t fdDevice; - - MA_ASSERT(pContext != NULL); - MA_ASSERT(fd >= 0); - MA_ASSERT(pDeviceInfo != NULL); - - (void)pContext; - (void)deviceType; - - if (ioctl(fd, AUDIO_GETDEV, &fdDevice) < 0) { - return MA_ERROR; /* Failed to retrieve device info. */ - } - - /* Name. */ - ma_strcpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), fdDevice.name); - - #if !defined(MA_AUDIO4_USE_NEW_API) - { - audio_info_t fdInfo; - int counter = 0; - ma_uint32 channels; - ma_uint32 sampleRate; - -#if defined(__NetBSD__) && (__NetBSD_Version__ >= 900000000) - if (ioctl(fd, AUDIO_GETFORMAT, &fdInfo) < 0) { - return MA_ERROR; - } -#else - if (ioctl(fd, AUDIO_GETINFO, &fdInfo) < 0) { - return MA_ERROR; - } -#endif - - if (deviceType == ma_device_type_playback) { - channels = fdInfo.play.channels; - sampleRate = fdInfo.play.sample_rate; - } else { - channels = fdInfo.record.channels; - sampleRate = fdInfo.record.sample_rate; - } - - /* Supported formats. We get this by looking at the encodings. */ - pDeviceInfo->nativeDataFormatCount = 0; - for (;;) { - audio_encoding_t encoding; - ma_format format; - - MA_ZERO_OBJECT(&encoding); - encoding.index = counter; - if (ioctl(fd, AUDIO_GETENC, &encoding) < 0) { - break; - } - - format = ma_format_from_encoding__audio4(encoding.encoding, encoding.precision); - if (format != ma_format_unknown) { - ma_device_info_add_native_data_format(pDeviceInfo, format, channels, sampleRate, 0); - } - - counter += 1; - } - } - #else - { - struct audio_swpar fdPar; - ma_format format; - ma_uint32 channels; - ma_uint32 sampleRate; - - if (ioctl(fd, AUDIO_GETPAR, &fdPar) < 0) { - return MA_ERROR; - } - - format = ma_format_from_swpar__audio4(&fdPar); - if (format == ma_format_unknown) { - return MA_FORMAT_NOT_SUPPORTED; - } - - if (deviceType == ma_device_type_playback) { - channels = fdPar.pchan; - } else { - channels = fdPar.rchan; - } - - sampleRate = fdPar.rate; - - pDeviceInfo->nativeDataFormatCount = 0; - ma_device_info_add_native_data_format(pDeviceInfo, format, channels, sampleRate, 0); - } - #endif - - return MA_SUCCESS; -} - -static ma_result ma_context_enumerate_devices__audio4(ma_context* pContext, ma_enum_devices_callback_proc callback, void* pUserData) -{ - const int maxDevices = 64; - char devpath[256]; - int iDevice; - - MA_ASSERT(pContext != NULL); - MA_ASSERT(callback != NULL); - - /* - Every device will be named "/dev/audioN", with a "/dev/audioctlN" equivalent. We use the "/dev/audioctlN" - version here since we can open it even when another process has control of the "/dev/audioN" device. - */ - for (iDevice = 0; iDevice < maxDevices; ++iDevice) { - struct stat st; - int fd; - ma_bool32 isTerminating = MA_FALSE; - - ma_strcpy_s(devpath, sizeof(devpath), "/dev/audioctl"); - ma_itoa_s(iDevice, devpath+strlen(devpath), sizeof(devpath)-strlen(devpath), 10); - - if (stat(devpath, &st) < 0) { - break; - } - - /* The device exists, but we need to check if it's usable as playback and/or capture. */ - - /* Playback. */ - if (!isTerminating) { - fd = open(devpath, O_RDONLY, 0); - if (fd >= 0) { - /* Supports playback. */ - ma_device_info deviceInfo; - MA_ZERO_OBJECT(&deviceInfo); - ma_construct_device_id__audio4(deviceInfo.id.audio4, sizeof(deviceInfo.id.audio4), "/dev/audio", iDevice); - if (ma_context_get_device_info_from_fd__audio4(pContext, ma_device_type_playback, fd, &deviceInfo) == MA_SUCCESS) { - isTerminating = !callback(pContext, ma_device_type_playback, &deviceInfo, pUserData); - } - - close(fd); - } - } - - /* Capture. */ - if (!isTerminating) { - fd = open(devpath, O_WRONLY, 0); - if (fd >= 0) { - /* Supports capture. */ - ma_device_info deviceInfo; - MA_ZERO_OBJECT(&deviceInfo); - ma_construct_device_id__audio4(deviceInfo.id.audio4, sizeof(deviceInfo.id.audio4), "/dev/audio", iDevice); - if (ma_context_get_device_info_from_fd__audio4(pContext, ma_device_type_capture, fd, &deviceInfo) == MA_SUCCESS) { - isTerminating = !callback(pContext, ma_device_type_capture, &deviceInfo, pUserData); - } - - close(fd); - } - } - - if (isTerminating) { - break; - } - } - - return MA_SUCCESS; -} - -static ma_result ma_context_get_device_info__audio4(ma_context* pContext, ma_device_type deviceType, const ma_device_id* pDeviceID, ma_device_info* pDeviceInfo) -{ - int fd = -1; - int deviceIndex = -1; - char ctlid[256]; - ma_result result; - - MA_ASSERT(pContext != NULL); - - /* - We need to open the "/dev/audioctlN" device to get the info. To do this we need to extract the number - from the device ID which will be in "/dev/audioN" format. - */ - if (pDeviceID == NULL) { - /* Default device. */ - ma_strcpy_s(ctlid, sizeof(ctlid), "/dev/audioctl"); - } else { - /* Specific device. We need to convert from "/dev/audioN" to "/dev/audioctlN". */ - result = ma_extract_device_index_from_id__audio4(pDeviceID->audio4, "/dev/audio", &deviceIndex); - if (result != MA_SUCCESS) { - return result; - } - - ma_construct_device_id__audio4(ctlid, sizeof(ctlid), "/dev/audioctl", deviceIndex); - } - - fd = open(ctlid, (deviceType == ma_device_type_playback) ? O_WRONLY : O_RDONLY, 0); - if (fd == -1) { - return MA_NO_DEVICE; - } - - if (deviceIndex == -1) { - ma_strcpy_s(pDeviceInfo->id.audio4, sizeof(pDeviceInfo->id.audio4), "/dev/audio"); - } else { - ma_construct_device_id__audio4(pDeviceInfo->id.audio4, sizeof(pDeviceInfo->id.audio4), "/dev/audio", deviceIndex); - } - - result = ma_context_get_device_info_from_fd__audio4(pContext, deviceType, fd, pDeviceInfo); - - close(fd); - return result; -} - -static ma_result ma_device_uninit__audio4(ma_device* pDevice) -{ - MA_ASSERT(pDevice != NULL); - - if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) { - close(pDevice->audio4.fdCapture); - } - - if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) { - close(pDevice->audio4.fdPlayback); - } - - return MA_SUCCESS; -} - -static ma_result ma_device_init_fd__audio4(ma_device* pDevice, const ma_device_config* pConfig, ma_device_descriptor* pDescriptor, ma_device_type deviceType) -{ - const char* pDefaultDeviceNames[] = { - "/dev/audio", - "/dev/audio0" - }; - const char* pDefaultDeviceCtlNames[] = { - "/dev/audioctl", - "/dev/audioctl0" - }; - int fd; - int fdFlags = 0; - size_t iDefaultDevice = (size_t)-1; - ma_format internalFormat; - ma_uint32 internalChannels; - ma_uint32 internalSampleRate; - ma_uint32 internalPeriodSizeInFrames; - ma_uint32 internalPeriods; - - MA_ASSERT(pConfig != NULL); - MA_ASSERT(deviceType != ma_device_type_duplex); - MA_ASSERT(pDevice != NULL); - - /* The first thing to do is open the file. */ - if (deviceType == ma_device_type_capture) { - fdFlags = O_RDONLY; - } else { - fdFlags = O_WRONLY; - } - /*fdFlags |= O_NONBLOCK;*/ - - /* Find the index of the default device as a start. We'll use this index later. Set it to (size_t)-1 otherwise. */ - if (pDescriptor->pDeviceID == NULL) { - /* Default device. */ - for (iDefaultDevice = 0; iDefaultDevice < ma_countof(pDefaultDeviceNames); ++iDefaultDevice) { - fd = open(pDefaultDeviceNames[iDefaultDevice], fdFlags, 0); - if (fd != -1) { - break; - } - } - } else { - /* Specific device. */ - fd = open(pDescriptor->pDeviceID->audio4, fdFlags, 0); - - for (iDefaultDevice = 0; iDefaultDevice < ma_countof(pDefaultDeviceNames); iDefaultDevice += 1) { - if (ma_strcmp(pDefaultDeviceNames[iDefaultDevice], pDescriptor->pDeviceID->audio4) == 0) { - break; - } - } - - if (iDefaultDevice == ma_countof(pDefaultDeviceNames)) { - iDefaultDevice = (size_t)-1; - } - } - - if (fd == -1) { - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[audio4] Failed to open device."); - return ma_result_from_errno(errno); - } - - #if !defined(MA_AUDIO4_USE_NEW_API) /* Old API */ - { - audio_info_t fdInfo; - int fdInfoResult = -1; - - /* - The documentation is a little bit unclear to me as to how it handles formats. It says the - following: - - Regardless of formats supported by underlying driver, the audio driver accepts the - following formats. - - By then the next sentence says this: - - `encoding` and `precision` are one of the values obtained by AUDIO_GETENC. - - It sounds like a direct contradiction to me. I'm going to play this safe any only use the - best sample format returned by AUDIO_GETENC. If the requested format is supported we'll - use that, but otherwise we'll just use our standard format priorities to pick an - appropriate one. - */ - AUDIO_INITINFO(&fdInfo); - - /* - Get the default format from the audioctl file if we're asking for a default device. If we - retrieve it from /dev/audio it'll default to mono 8000Hz. - */ - if (iDefaultDevice != (size_t)-1) { - /* We're using a default device. Get the info from the /dev/audioctl file instead of /dev/audio. */ - int fdctl = open(pDefaultDeviceCtlNames[iDefaultDevice], fdFlags, 0); - if (fdctl != -1) { -#if defined(__NetBSD__) && (__NetBSD_Version__ >= 900000000) - fdInfoResult = ioctl(fdctl, AUDIO_GETFORMAT, &fdInfo); -#else - fdInfoResult = ioctl(fdctl, AUDIO_GETINFO, &fdInfo); -#endif - close(fdctl); - } - } - - if (fdInfoResult == -1) { - /* We still don't have the default device info so just retrieve it from the main audio device. */ - if (ioctl(fd, AUDIO_GETINFO, &fdInfo) < 0) { - close(fd); - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[audio4] AUDIO_GETINFO failed."); - return ma_result_from_errno(errno); - } - } - - /* We get the driver to do as much of the data conversion as possible. */ - if (deviceType == ma_device_type_capture) { - fdInfo.mode = AUMODE_RECORD; - ma_encoding_from_format__audio4(ma_best_format_from_fd__audio4(fd, pDescriptor->format), &fdInfo.record.encoding, &fdInfo.record.precision); - - if (pDescriptor->channels != 0) { - fdInfo.record.channels = ma_clamp(pDescriptor->channels, 1, 12); /* From the documentation: `channels` ranges from 1 to 12. */ - } - - if (pDescriptor->sampleRate != 0) { - fdInfo.record.sample_rate = ma_clamp(pDescriptor->sampleRate, 1000, 192000); /* From the documentation: `frequency` ranges from 1000Hz to 192000Hz. (They mean `sample_rate` instead of `frequency`.) */ - } - } else { - fdInfo.mode = AUMODE_PLAY; - ma_encoding_from_format__audio4(ma_best_format_from_fd__audio4(fd, pDescriptor->format), &fdInfo.play.encoding, &fdInfo.play.precision); - - if (pDescriptor->channels != 0) { - fdInfo.play.channels = ma_clamp(pDescriptor->channels, 1, 12); /* From the documentation: `channels` ranges from 1 to 12. */ - } - - if (pDescriptor->sampleRate != 0) { - fdInfo.play.sample_rate = ma_clamp(pDescriptor->sampleRate, 1000, 192000); /* From the documentation: `frequency` ranges from 1000Hz to 192000Hz. (They mean `sample_rate` instead of `frequency`.) */ - } - } - - if (ioctl(fd, AUDIO_SETINFO, &fdInfo) < 0) { - close(fd); - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[audio4] Failed to set device format. AUDIO_SETINFO failed."); - return ma_result_from_errno(errno); - } - - if (ioctl(fd, AUDIO_GETINFO, &fdInfo) < 0) { - close(fd); - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[audio4] AUDIO_GETINFO failed."); - return ma_result_from_errno(errno); - } - - if (deviceType == ma_device_type_capture) { - internalFormat = ma_format_from_prinfo__audio4(&fdInfo.record); - internalChannels = fdInfo.record.channels; - internalSampleRate = fdInfo.record.sample_rate; - } else { - internalFormat = ma_format_from_prinfo__audio4(&fdInfo.play); - internalChannels = fdInfo.play.channels; - internalSampleRate = fdInfo.play.sample_rate; - } - - if (internalFormat == ma_format_unknown) { - close(fd); - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[audio4] The device's internal device format is not supported by miniaudio. The device is unusable."); - return MA_FORMAT_NOT_SUPPORTED; - } - - /* Buffer. */ - { - ma_uint32 internalPeriodSizeInBytes; - - internalPeriodSizeInFrames = ma_calculate_buffer_size_in_frames_from_descriptor(pDescriptor, internalSampleRate, pConfig->performanceProfile); - - internalPeriodSizeInBytes = internalPeriodSizeInFrames * ma_get_bytes_per_frame(internalFormat, internalChannels); - if (internalPeriodSizeInBytes < 16) { - internalPeriodSizeInBytes = 16; - } - - internalPeriods = pDescriptor->periodCount; - if (internalPeriods < 2) { - internalPeriods = 2; - } - - /* What miniaudio calls a period, audio4 calls a block. */ - AUDIO_INITINFO(&fdInfo); - fdInfo.hiwat = internalPeriods; - fdInfo.lowat = internalPeriods-1; - fdInfo.blocksize = internalPeriodSizeInBytes; - if (ioctl(fd, AUDIO_SETINFO, &fdInfo) < 0) { - close(fd); - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[audio4] Failed to set internal buffer size. AUDIO_SETINFO failed."); - return ma_result_from_errno(errno); - } - - internalPeriods = fdInfo.hiwat; - internalPeriodSizeInFrames = fdInfo.blocksize / ma_get_bytes_per_frame(internalFormat, internalChannels); - } - } - #else - { - struct audio_swpar fdPar; - - /* We need to retrieve the format of the device so we can know the channel count and sample rate. Then we can calculate the buffer size. */ - if (ioctl(fd, AUDIO_GETPAR, &fdPar) < 0) { - close(fd); - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[audio4] Failed to retrieve initial device parameters."); - return ma_result_from_errno(errno); - } - - internalFormat = ma_format_from_swpar__audio4(&fdPar); - internalChannels = (deviceType == ma_device_type_capture) ? fdPar.rchan : fdPar.pchan; - internalSampleRate = fdPar.rate; - - if (internalFormat == ma_format_unknown) { - close(fd); - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[audio4] The device's internal device format is not supported by miniaudio. The device is unusable."); - return MA_FORMAT_NOT_SUPPORTED; - } - - /* Buffer. */ - { - ma_uint32 internalPeriodSizeInBytes; - - internalPeriodSizeInFrames = ma_calculate_buffer_size_in_frames_from_descriptor(pDescriptor, internalSampleRate, pConfig->performanceProfile); - - /* What miniaudio calls a period, audio4 calls a block. */ - internalPeriodSizeInBytes = internalPeriodSizeInFrames * ma_get_bytes_per_frame(internalFormat, internalChannels); - if (internalPeriodSizeInBytes < 16) { - internalPeriodSizeInBytes = 16; - } - - fdPar.nblks = pDescriptor->periodCount; - fdPar.round = internalPeriodSizeInBytes; - - if (ioctl(fd, AUDIO_SETPAR, &fdPar) < 0) { - close(fd); - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[audio4] Failed to set device parameters."); - return ma_result_from_errno(errno); - } - - if (ioctl(fd, AUDIO_GETPAR, &fdPar) < 0) { - close(fd); - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[audio4] Failed to retrieve actual device parameters."); - return ma_result_from_errno(errno); - } - } - - internalFormat = ma_format_from_swpar__audio4(&fdPar); - internalChannels = (deviceType == ma_device_type_capture) ? fdPar.rchan : fdPar.pchan; - internalSampleRate = fdPar.rate; - internalPeriods = fdPar.nblks; - internalPeriodSizeInFrames = fdPar.round / ma_get_bytes_per_frame(internalFormat, internalChannels); - } - #endif - - if (internalFormat == ma_format_unknown) { - close(fd); - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[audio4] The device's internal device format is not supported by miniaudio. The device is unusable."); - return MA_FORMAT_NOT_SUPPORTED; - } - - if (deviceType == ma_device_type_capture) { - pDevice->audio4.fdCapture = fd; - } else { - pDevice->audio4.fdPlayback = fd; - } - - pDescriptor->format = internalFormat; - pDescriptor->channels = internalChannels; - pDescriptor->sampleRate = internalSampleRate; - ma_channel_map_init_standard(ma_standard_channel_map_sound4, pDescriptor->channelMap, ma_countof(pDescriptor->channelMap), internalChannels); - pDescriptor->periodSizeInFrames = internalPeriodSizeInFrames; - pDescriptor->periodCount = internalPeriods; - - return MA_SUCCESS; -} - -static ma_result ma_device_init__audio4(ma_device* pDevice, const ma_device_config* pConfig, ma_device_descriptor* pDescriptorPlayback, ma_device_descriptor* pDescriptorCapture) -{ - MA_ASSERT(pDevice != NULL); - - MA_ZERO_OBJECT(&pDevice->audio4); - - if (pConfig->deviceType == ma_device_type_loopback) { - return MA_DEVICE_TYPE_NOT_SUPPORTED; - } - - pDevice->audio4.fdCapture = -1; - pDevice->audio4.fdPlayback = -1; - - /* - The version of the operating system dictates whether or not the device is exclusive or shared. NetBSD - introduced in-kernel mixing which means it's shared. All other BSD flavours are exclusive as far as - I'm aware. - */ -#if defined(__NetBSD_Version__) && __NetBSD_Version__ >= 800000000 - /* NetBSD 8.0+ */ - if (((pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) && pDescriptorPlayback->shareMode == ma_share_mode_exclusive) || - ((pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) && pDescriptorCapture->shareMode == ma_share_mode_exclusive)) { - return MA_SHARE_MODE_NOT_SUPPORTED; - } -#else - /* All other flavors. */ -#endif - - if (pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) { - ma_result result = ma_device_init_fd__audio4(pDevice, pConfig, pDescriptorCapture, ma_device_type_capture); - if (result != MA_SUCCESS) { - return result; - } - } - - if (pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) { - ma_result result = ma_device_init_fd__audio4(pDevice, pConfig, pDescriptorPlayback, ma_device_type_playback); - if (result != MA_SUCCESS) { - if (pConfig->deviceType == ma_device_type_duplex) { - close(pDevice->audio4.fdCapture); - } - return result; - } - } - - return MA_SUCCESS; -} - -static ma_result ma_device_start__audio4(ma_device* pDevice) -{ - MA_ASSERT(pDevice != NULL); - - if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) { - if (pDevice->audio4.fdCapture == -1) { - return MA_INVALID_ARGS; - } - } - - if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) { - if (pDevice->audio4.fdPlayback == -1) { - return MA_INVALID_ARGS; - } - } - - return MA_SUCCESS; -} - -static ma_result ma_device_stop_fd__audio4(ma_device* pDevice, int fd) -{ - if (fd == -1) { - return MA_INVALID_ARGS; - } - -#if !defined(MA_AUDIO4_USE_NEW_API) - if (ioctl(fd, AUDIO_FLUSH, 0) < 0) { - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[audio4] Failed to stop device. AUDIO_FLUSH failed."); - return ma_result_from_errno(errno); - } -#else - if (ioctl(fd, AUDIO_STOP, 0) < 0) { - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[audio4] Failed to stop device. AUDIO_STOP failed."); - return ma_result_from_errno(errno); - } -#endif - - return MA_SUCCESS; -} - -static ma_result ma_device_stop__audio4(ma_device* pDevice) -{ - MA_ASSERT(pDevice != NULL); - - if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) { - ma_result result; - - result = ma_device_stop_fd__audio4(pDevice, pDevice->audio4.fdCapture); - if (result != MA_SUCCESS) { - return result; - } - } - - if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) { - ma_result result; - - /* Drain the device first. If this fails we'll just need to flush without draining. Unfortunately draining isn't available on newer version of OpenBSD. */ - #if !defined(MA_AUDIO4_USE_NEW_API) - ioctl(pDevice->audio4.fdPlayback, AUDIO_DRAIN, 0); - #endif - - /* Here is where the device is stopped immediately. */ - result = ma_device_stop_fd__audio4(pDevice, pDevice->audio4.fdPlayback); - if (result != MA_SUCCESS) { - return result; - } - } - - return MA_SUCCESS; -} - -static ma_result ma_device_write__audio4(ma_device* pDevice, const void* pPCMFrames, ma_uint32 frameCount, ma_uint32* pFramesWritten) -{ - int result; - - if (pFramesWritten != NULL) { - *pFramesWritten = 0; - } - - result = write(pDevice->audio4.fdPlayback, pPCMFrames, frameCount * ma_get_bytes_per_frame(pDevice->playback.internalFormat, pDevice->playback.internalChannels)); - if (result < 0) { - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[audio4] Failed to write data to the device."); - return ma_result_from_errno(errno); - } - - if (pFramesWritten != NULL) { - *pFramesWritten = (ma_uint32)result / ma_get_bytes_per_frame(pDevice->playback.internalFormat, pDevice->playback.internalChannels); - } - - return MA_SUCCESS; -} - -static ma_result ma_device_read__audio4(ma_device* pDevice, void* pPCMFrames, ma_uint32 frameCount, ma_uint32* pFramesRead) -{ - int result; - - if (pFramesRead != NULL) { - *pFramesRead = 0; - } - - result = read(pDevice->audio4.fdCapture, pPCMFrames, frameCount * ma_get_bytes_per_frame(pDevice->capture.internalFormat, pDevice->capture.internalChannels)); - if (result < 0) { - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[audio4] Failed to read data from the device."); - return ma_result_from_errno(errno); - } - - if (pFramesRead != NULL) { - *pFramesRead = (ma_uint32)result / ma_get_bytes_per_frame(pDevice->capture.internalFormat, pDevice->capture.internalChannels); - } - - return MA_SUCCESS; -} - -static ma_result ma_context_uninit__audio4(ma_context* pContext) -{ - MA_ASSERT(pContext != NULL); - MA_ASSERT(pContext->backend == ma_backend_audio4); - - (void)pContext; - return MA_SUCCESS; -} - -static ma_result ma_context_init__audio4(ma_context* pContext, const ma_context_config* pConfig, ma_backend_callbacks* pCallbacks) -{ - MA_ASSERT(pContext != NULL); - - (void)pConfig; - - pCallbacks->onContextInit = ma_context_init__audio4; - pCallbacks->onContextUninit = ma_context_uninit__audio4; - pCallbacks->onContextEnumerateDevices = ma_context_enumerate_devices__audio4; - pCallbacks->onContextGetDeviceInfo = ma_context_get_device_info__audio4; - pCallbacks->onDeviceInit = ma_device_init__audio4; - pCallbacks->onDeviceUninit = ma_device_uninit__audio4; - pCallbacks->onDeviceStart = ma_device_start__audio4; - pCallbacks->onDeviceStop = ma_device_stop__audio4; - pCallbacks->onDeviceRead = ma_device_read__audio4; - pCallbacks->onDeviceWrite = ma_device_write__audio4; - pCallbacks->onDeviceDataLoop = NULL; - - return MA_SUCCESS; -} -#endif /* MA_HAS_AUDIO4 */ - - -/****************************************************************************** - -OSS Backend - -******************************************************************************/ -#ifdef MA_HAS_OSS -#include -#include -#include -#include - -#ifndef SNDCTL_DSP_HALT -#define SNDCTL_DSP_HALT SNDCTL_DSP_RESET -#endif - -#define MA_OSS_DEFAULT_DEVICE_NAME "/dev/dsp" - -static int ma_open_temp_device__oss() -{ - /* The OSS sample code uses "/dev/mixer" as the device for getting system properties so I'm going to do the same. */ - int fd = open("/dev/mixer", O_RDONLY, 0); - if (fd >= 0) { - return fd; - } - - return -1; -} - -static ma_result ma_context_open_device__oss(ma_context* pContext, ma_device_type deviceType, const ma_device_id* pDeviceID, ma_share_mode shareMode, int* pfd) -{ - const char* deviceName; - int flags; - - MA_ASSERT(pContext != NULL); - MA_ASSERT(pfd != NULL); - (void)pContext; - - *pfd = -1; - - /* This function should only be called for playback or capture, not duplex. */ - if (deviceType == ma_device_type_duplex) { - return MA_INVALID_ARGS; - } - - deviceName = MA_OSS_DEFAULT_DEVICE_NAME; - if (pDeviceID != NULL) { - deviceName = pDeviceID->oss; - } - - flags = (deviceType == ma_device_type_playback) ? O_WRONLY : O_RDONLY; - if (shareMode == ma_share_mode_exclusive) { - flags |= O_EXCL; - } - - *pfd = open(deviceName, flags, 0); - if (*pfd == -1) { - return ma_result_from_errno(errno); - } - - return MA_SUCCESS; -} - -static ma_result ma_context_enumerate_devices__oss(ma_context* pContext, ma_enum_devices_callback_proc callback, void* pUserData) -{ - int fd; - oss_sysinfo si; - int result; - - MA_ASSERT(pContext != NULL); - MA_ASSERT(callback != NULL); - - fd = ma_open_temp_device__oss(); - if (fd == -1) { - ma_log_post(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[OSS] Failed to open a temporary device for retrieving system information used for device enumeration."); - return MA_NO_BACKEND; - } - - result = ioctl(fd, SNDCTL_SYSINFO, &si); - if (result != -1) { - int iAudioDevice; - for (iAudioDevice = 0; iAudioDevice < si.numaudios; ++iAudioDevice) { - oss_audioinfo ai; - ai.dev = iAudioDevice; - result = ioctl(fd, SNDCTL_AUDIOINFO, &ai); - if (result != -1) { - if (ai.devnode[0] != '\0') { /* <-- Can be blank, according to documentation. */ - ma_device_info deviceInfo; - ma_bool32 isTerminating = MA_FALSE; - - MA_ZERO_OBJECT(&deviceInfo); - - /* ID */ - ma_strncpy_s(deviceInfo.id.oss, sizeof(deviceInfo.id.oss), ai.devnode, (size_t)-1); - - /* - The human readable device name should be in the "ai.handle" variable, but it can - sometimes be empty in which case we just fall back to "ai.name" which is less user - friendly, but usually has a value. - */ - if (ai.handle[0] != '\0') { - ma_strncpy_s(deviceInfo.name, sizeof(deviceInfo.name), ai.handle, (size_t)-1); - } else { - ma_strncpy_s(deviceInfo.name, sizeof(deviceInfo.name), ai.name, (size_t)-1); - } - - /* The device can be both playback and capture. */ - if (!isTerminating && (ai.caps & PCM_CAP_OUTPUT) != 0) { - isTerminating = !callback(pContext, ma_device_type_playback, &deviceInfo, pUserData); - } - if (!isTerminating && (ai.caps & PCM_CAP_INPUT) != 0) { - isTerminating = !callback(pContext, ma_device_type_capture, &deviceInfo, pUserData); - } - - if (isTerminating) { - break; - } - } - } - } - } else { - close(fd); - ma_log_post(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[OSS] Failed to retrieve system information for device enumeration."); - return MA_NO_BACKEND; - } - - close(fd); - return MA_SUCCESS; -} - -static void ma_context_add_native_data_format__oss(ma_context* pContext, oss_audioinfo* pAudioInfo, ma_format format, ma_device_info* pDeviceInfo) -{ - unsigned int minChannels; - unsigned int maxChannels; - unsigned int iRate; - - MA_ASSERT(pContext != NULL); - MA_ASSERT(pAudioInfo != NULL); - MA_ASSERT(pDeviceInfo != NULL); - - /* If we support all channels we just report 0. */ - minChannels = ma_clamp(pAudioInfo->min_channels, MA_MIN_CHANNELS, MA_MAX_CHANNELS); - maxChannels = ma_clamp(pAudioInfo->max_channels, MA_MIN_CHANNELS, MA_MAX_CHANNELS); - - /* - OSS has this annoying thing where sample rates can be reported in two ways. We prefer explicitness, - which OSS has in the form of nrates/rates, however there are times where nrates can be 0, in which - case we'll need to use min_rate and max_rate and report only standard rates. - */ - if (pAudioInfo->nrates > 0) { - for (iRate = 0; iRate < pAudioInfo->nrates; iRate += 1) { - unsigned int rate = pAudioInfo->rates[iRate]; - - if (minChannels == MA_MIN_CHANNELS && maxChannels == MA_MAX_CHANNELS) { - ma_device_info_add_native_data_format(pDeviceInfo, format, 0, rate, 0); /* Set the channel count to 0 to indicate that all channel counts are supported. */ - } else { - unsigned int iChannel; - for (iChannel = minChannels; iChannel <= maxChannels; iChannel += 1) { - ma_device_info_add_native_data_format(pDeviceInfo, format, iChannel, rate, 0); - } - } - } - } else { - for (iRate = 0; iRate < ma_countof(g_maStandardSampleRatePriorities); iRate += 1) { - ma_uint32 standardRate = g_maStandardSampleRatePriorities[iRate]; - - if (standardRate >= (ma_uint32)pAudioInfo->min_rate && standardRate <= (ma_uint32)pAudioInfo->max_rate) { - if (minChannels == MA_MIN_CHANNELS && maxChannels == MA_MAX_CHANNELS) { - ma_device_info_add_native_data_format(pDeviceInfo, format, 0, standardRate, 0); /* Set the channel count to 0 to indicate that all channel counts are supported. */ - } else { - unsigned int iChannel; - for (iChannel = minChannels; iChannel <= maxChannels; iChannel += 1) { - ma_device_info_add_native_data_format(pDeviceInfo, format, iChannel, standardRate, 0); - } - } - } - } - } -} - -static ma_result ma_context_get_device_info__oss(ma_context* pContext, ma_device_type deviceType, const ma_device_id* pDeviceID, ma_device_info* pDeviceInfo) -{ - ma_bool32 foundDevice; - int fdTemp; - oss_sysinfo si; - int result; - - MA_ASSERT(pContext != NULL); - - /* Handle the default device a little differently. */ - if (pDeviceID == NULL) { - if (deviceType == ma_device_type_playback) { - ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), MA_DEFAULT_PLAYBACK_DEVICE_NAME, (size_t)-1); - } else { - ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), MA_DEFAULT_CAPTURE_DEVICE_NAME, (size_t)-1); - } - - return MA_SUCCESS; - } - - - /* If we get here it means we are _not_ using the default device. */ - foundDevice = MA_FALSE; - - fdTemp = ma_open_temp_device__oss(); - if (fdTemp == -1) { - ma_log_post(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[OSS] Failed to open a temporary device for retrieving system information used for device enumeration."); - return MA_NO_BACKEND; - } - - result = ioctl(fdTemp, SNDCTL_SYSINFO, &si); - if (result != -1) { - int iAudioDevice; - for (iAudioDevice = 0; iAudioDevice < si.numaudios; ++iAudioDevice) { - oss_audioinfo ai; - ai.dev = iAudioDevice; - result = ioctl(fdTemp, SNDCTL_AUDIOINFO, &ai); - if (result != -1) { - if (ma_strcmp(ai.devnode, pDeviceID->oss) == 0) { - /* It has the same name, so now just confirm the type. */ - if ((deviceType == ma_device_type_playback && ((ai.caps & PCM_CAP_OUTPUT) != 0)) || - (deviceType == ma_device_type_capture && ((ai.caps & PCM_CAP_INPUT) != 0))) { - unsigned int formatMask; - - /* ID */ - ma_strncpy_s(pDeviceInfo->id.oss, sizeof(pDeviceInfo->id.oss), ai.devnode, (size_t)-1); - - /* - The human readable device name should be in the "ai.handle" variable, but it can - sometimes be empty in which case we just fall back to "ai.name" which is less user - friendly, but usually has a value. - */ - if (ai.handle[0] != '\0') { - ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), ai.handle, (size_t)-1); - } else { - ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), ai.name, (size_t)-1); - } - - - pDeviceInfo->nativeDataFormatCount = 0; - - if (deviceType == ma_device_type_playback) { - formatMask = ai.oformats; - } else { - formatMask = ai.iformats; - } - - if (((formatMask & AFMT_S16_LE) != 0 && ma_is_little_endian()) || (AFMT_S16_BE && ma_is_big_endian())) { - ma_context_add_native_data_format__oss(pContext, &ai, ma_format_s16, pDeviceInfo); - } - if (((formatMask & AFMT_S32_LE) != 0 && ma_is_little_endian()) || (AFMT_S32_BE && ma_is_big_endian())) { - ma_context_add_native_data_format__oss(pContext, &ai, ma_format_s32, pDeviceInfo); - } - if ((formatMask & AFMT_U8) != 0) { - ma_context_add_native_data_format__oss(pContext, &ai, ma_format_u8, pDeviceInfo); - } - - foundDevice = MA_TRUE; - break; - } - } - } - } - } else { - close(fdTemp); - ma_log_post(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[OSS] Failed to retrieve system information for device enumeration."); - return MA_NO_BACKEND; - } - - - close(fdTemp); - - if (!foundDevice) { - return MA_NO_DEVICE; - } - - return MA_SUCCESS; -} - -static ma_result ma_device_uninit__oss(ma_device* pDevice) -{ - MA_ASSERT(pDevice != NULL); - - if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) { - close(pDevice->oss.fdCapture); - } - - if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) { - close(pDevice->oss.fdPlayback); - } - - return MA_SUCCESS; -} - -static int ma_format_to_oss(ma_format format) -{ - int ossFormat = AFMT_U8; - switch (format) { - case ma_format_s16: ossFormat = (ma_is_little_endian()) ? AFMT_S16_LE : AFMT_S16_BE; break; - case ma_format_s24: ossFormat = (ma_is_little_endian()) ? AFMT_S32_LE : AFMT_S32_BE; break; - case ma_format_s32: ossFormat = (ma_is_little_endian()) ? AFMT_S32_LE : AFMT_S32_BE; break; - case ma_format_f32: ossFormat = (ma_is_little_endian()) ? AFMT_S16_LE : AFMT_S16_BE; break; - case ma_format_u8: - default: ossFormat = AFMT_U8; break; - } - - return ossFormat; -} - -static ma_format ma_format_from_oss(int ossFormat) -{ - if (ossFormat == AFMT_U8) { - return ma_format_u8; - } else { - if (ma_is_little_endian()) { - switch (ossFormat) { - case AFMT_S16_LE: return ma_format_s16; - case AFMT_S32_LE: return ma_format_s32; - default: return ma_format_unknown; - } - } else { - switch (ossFormat) { - case AFMT_S16_BE: return ma_format_s16; - case AFMT_S32_BE: return ma_format_s32; - default: return ma_format_unknown; - } - } - } - - return ma_format_unknown; -} - -static ma_result ma_device_init_fd__oss(ma_device* pDevice, const ma_device_config* pConfig, ma_device_descriptor* pDescriptor, ma_device_type deviceType) -{ - ma_result result; - int ossResult; - int fd; - const ma_device_id* pDeviceID = NULL; - ma_share_mode shareMode; - int ossFormat; - int ossChannels; - int ossSampleRate; - int ossFragment; - - MA_ASSERT(pDevice != NULL); - MA_ASSERT(pConfig != NULL); - MA_ASSERT(deviceType != ma_device_type_duplex); - - pDeviceID = pDescriptor->pDeviceID; - shareMode = pDescriptor->shareMode; - ossFormat = ma_format_to_oss((pDescriptor->format != ma_format_unknown) ? pDescriptor->format : ma_format_s16); /* Use s16 by default because OSS doesn't like floating point. */ - ossChannels = (int)(pDescriptor->channels > 0) ? pDescriptor->channels : MA_DEFAULT_CHANNELS; - ossSampleRate = (int)(pDescriptor->sampleRate > 0) ? pDescriptor->sampleRate : MA_DEFAULT_SAMPLE_RATE; - - result = ma_context_open_device__oss(pDevice->pContext, deviceType, pDeviceID, shareMode, &fd); - if (result != MA_SUCCESS) { - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[OSS] Failed to open device."); - return result; - } - - /* - The OSS documentation is very clear about the order we should be initializing the device's properties: - 1) Format - 2) Channels - 3) Sample rate. - */ - - /* Format. */ - ossResult = ioctl(fd, SNDCTL_DSP_SETFMT, &ossFormat); - if (ossResult == -1) { - close(fd); - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[OSS] Failed to set format."); - return ma_result_from_errno(errno); - } - - /* Channels. */ - ossResult = ioctl(fd, SNDCTL_DSP_CHANNELS, &ossChannels); - if (ossResult == -1) { - close(fd); - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[OSS] Failed to set channel count."); - return ma_result_from_errno(errno); - } - - /* Sample Rate. */ - ossResult = ioctl(fd, SNDCTL_DSP_SPEED, &ossSampleRate); - if (ossResult == -1) { - close(fd); - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[OSS] Failed to set sample rate."); - return ma_result_from_errno(errno); - } - - /* - Buffer. - - The documentation says that the fragment settings should be set as soon as possible, but I'm not sure if - it should be done before or after format/channels/rate. - - OSS wants the fragment size in bytes and a power of 2. When setting, we specify the power, not the actual - value. - */ - { - ma_uint32 periodSizeInFrames; - ma_uint32 periodSizeInBytes; - ma_uint32 ossFragmentSizePower; - - periodSizeInFrames = ma_calculate_buffer_size_in_frames_from_descriptor(pDescriptor, (ma_uint32)ossSampleRate, pConfig->performanceProfile); - - periodSizeInBytes = ma_round_to_power_of_2(periodSizeInFrames * ma_get_bytes_per_frame(ma_format_from_oss(ossFormat), ossChannels)); - if (periodSizeInBytes < 16) { - periodSizeInBytes = 16; - } - - ossFragmentSizePower = 4; - periodSizeInBytes >>= 4; - while (periodSizeInBytes >>= 1) { - ossFragmentSizePower += 1; - } - - ossFragment = (int)((pConfig->periods << 16) | ossFragmentSizePower); - ossResult = ioctl(fd, SNDCTL_DSP_SETFRAGMENT, &ossFragment); - if (ossResult == -1) { - close(fd); - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[OSS] Failed to set fragment size and period count."); - return ma_result_from_errno(errno); - } - } - - /* Internal settings. */ - if (deviceType == ma_device_type_capture) { - pDevice->oss.fdCapture = fd; - } else { - pDevice->oss.fdPlayback = fd; - } - - pDescriptor->format = ma_format_from_oss(ossFormat); - pDescriptor->channels = ossChannels; - pDescriptor->sampleRate = ossSampleRate; - ma_channel_map_init_standard(ma_standard_channel_map_sound4, pDescriptor->channelMap, ma_countof(pDescriptor->channelMap), pDescriptor->channels); - pDescriptor->periodCount = (ma_uint32)(ossFragment >> 16); - pDescriptor->periodSizeInFrames = (ma_uint32)(1 << (ossFragment & 0xFFFF)) / ma_get_bytes_per_frame(pDescriptor->format, pDescriptor->channels); - - if (pDescriptor->format == ma_format_unknown) { - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[OSS] The device's internal format is not supported by miniaudio."); - return MA_FORMAT_NOT_SUPPORTED; - } - - return MA_SUCCESS; -} - -static ma_result ma_device_init__oss(ma_device* pDevice, const ma_device_config* pConfig, ma_device_descriptor* pDescriptorPlayback, ma_device_descriptor* pDescriptorCapture) -{ - MA_ASSERT(pDevice != NULL); - MA_ASSERT(pConfig != NULL); - - MA_ZERO_OBJECT(&pDevice->oss); - - if (pConfig->deviceType == ma_device_type_loopback) { - return MA_DEVICE_TYPE_NOT_SUPPORTED; - } - - if (pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) { - ma_result result = ma_device_init_fd__oss(pDevice, pConfig, pDescriptorCapture, ma_device_type_capture); - if (result != MA_SUCCESS) { - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[OSS] Failed to open device."); - return result; - } - } - - if (pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) { - ma_result result = ma_device_init_fd__oss(pDevice, pConfig, pDescriptorPlayback, ma_device_type_playback); - if (result != MA_SUCCESS) { - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[OSS] Failed to open device."); - return result; - } - } - - return MA_SUCCESS; -} - -/* -Note on Starting and Stopping -============================= -In the past I was using SNDCTL_DSP_HALT to stop the device, however this results in issues when -trying to resume the device again. If we use SNDCTL_DSP_HALT, the next write() or read() will -fail. Instead what we need to do is just not write or read to and from the device when the -device is not running. - -As a result, both the start and stop functions for OSS are just empty stubs. The starting and -stopping logic is handled by ma_device_write__oss() and ma_device_read__oss(). These will check -the device state, and if the device is stopped they will simply not do any kind of processing. - -The downside to this technique is that I've noticed a fairly lengthy delay in stopping the -device, up to a second. This is on a virtual machine, and as such might just be due to the -virtual drivers, but I'm not fully sure. I am not sure how to work around this problem so for -the moment that's just how it's going to have to be. - -When starting the device, OSS will automatically start it when write() or read() is called. -*/ -static ma_result ma_device_start__oss(ma_device* pDevice) -{ - MA_ASSERT(pDevice != NULL); - - /* The device is automatically started with reading and writing. */ - (void)pDevice; - - return MA_SUCCESS; -} - -static ma_result ma_device_stop__oss(ma_device* pDevice) -{ - MA_ASSERT(pDevice != NULL); - - /* See note above on why this is empty. */ - (void)pDevice; - - return MA_SUCCESS; -} - -static ma_result ma_device_write__oss(ma_device* pDevice, const void* pPCMFrames, ma_uint32 frameCount, ma_uint32* pFramesWritten) -{ - int resultOSS; - ma_uint32 deviceState; - - if (pFramesWritten != NULL) { - *pFramesWritten = 0; - } - - /* Don't do any processing if the device is stopped. */ - deviceState = ma_device_get_state(pDevice); - if (deviceState != ma_device_state_started && deviceState != ma_device_state_starting) { - return MA_SUCCESS; - } - - resultOSS = write(pDevice->oss.fdPlayback, pPCMFrames, frameCount * ma_get_bytes_per_frame(pDevice->playback.internalFormat, pDevice->playback.internalChannels)); - if (resultOSS < 0) { - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[OSS] Failed to send data from the client to the device."); - return ma_result_from_errno(errno); - } - - if (pFramesWritten != NULL) { - *pFramesWritten = (ma_uint32)resultOSS / ma_get_bytes_per_frame(pDevice->playback.internalFormat, pDevice->playback.internalChannels); - } - - return MA_SUCCESS; -} - -static ma_result ma_device_read__oss(ma_device* pDevice, void* pPCMFrames, ma_uint32 frameCount, ma_uint32* pFramesRead) -{ - int resultOSS; - ma_uint32 deviceState; - - if (pFramesRead != NULL) { - *pFramesRead = 0; - } - - /* Don't do any processing if the device is stopped. */ - deviceState = ma_device_get_state(pDevice); - if (deviceState != ma_device_state_started && deviceState != ma_device_state_starting) { - return MA_SUCCESS; - } - - resultOSS = read(pDevice->oss.fdCapture, pPCMFrames, frameCount * ma_get_bytes_per_frame(pDevice->capture.internalFormat, pDevice->capture.internalChannels)); - if (resultOSS < 0) { - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[OSS] Failed to read data from the device to be sent to the client."); - return ma_result_from_errno(errno); - } - - if (pFramesRead != NULL) { - *pFramesRead = (ma_uint32)resultOSS / ma_get_bytes_per_frame(pDevice->capture.internalFormat, pDevice->capture.internalChannels); - } - - return MA_SUCCESS; -} - -static ma_result ma_context_uninit__oss(ma_context* pContext) -{ - MA_ASSERT(pContext != NULL); - MA_ASSERT(pContext->backend == ma_backend_oss); - - (void)pContext; - return MA_SUCCESS; -} - -static ma_result ma_context_init__oss(ma_context* pContext, const ma_context_config* pConfig, ma_backend_callbacks* pCallbacks) -{ - int fd; - int ossVersion; - int result; - - MA_ASSERT(pContext != NULL); - - (void)pConfig; - - /* Try opening a temporary device first so we can get version information. This is closed at the end. */ - fd = ma_open_temp_device__oss(); - if (fd == -1) { - ma_log_post(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[OSS] Failed to open temporary device for retrieving system properties."); /* Looks liks OSS isn't installed, or there are no available devices. */ - return MA_NO_BACKEND; - } - - /* Grab the OSS version. */ - ossVersion = 0; - result = ioctl(fd, OSS_GETVERSION, &ossVersion); - if (result == -1) { - close(fd); - ma_log_post(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[OSS] Failed to retrieve OSS version."); - return MA_NO_BACKEND; - } - - /* The file handle to temp device is no longer needed. Close ASAP. */ - close(fd); - - pContext->oss.versionMajor = ((ossVersion & 0xFF0000) >> 16); - pContext->oss.versionMinor = ((ossVersion & 0x00FF00) >> 8); - - pCallbacks->onContextInit = ma_context_init__oss; - pCallbacks->onContextUninit = ma_context_uninit__oss; - pCallbacks->onContextEnumerateDevices = ma_context_enumerate_devices__oss; - pCallbacks->onContextGetDeviceInfo = ma_context_get_device_info__oss; - pCallbacks->onDeviceInit = ma_device_init__oss; - pCallbacks->onDeviceUninit = ma_device_uninit__oss; - pCallbacks->onDeviceStart = ma_device_start__oss; - pCallbacks->onDeviceStop = ma_device_stop__oss; - pCallbacks->onDeviceRead = ma_device_read__oss; - pCallbacks->onDeviceWrite = ma_device_write__oss; - pCallbacks->onDeviceDataLoop = NULL; - - return MA_SUCCESS; -} -#endif /* MA_HAS_OSS */ - - - - - -/****************************************************************************** - -AAudio Backend - -******************************************************************************/ -#ifdef MA_HAS_AAUDIO - -#ifdef MA_NO_RUNTIME_LINKING - #include -#endif - -typedef int32_t ma_aaudio_result_t; -typedef int32_t ma_aaudio_direction_t; -typedef int32_t ma_aaudio_sharing_mode_t; -typedef int32_t ma_aaudio_format_t; -typedef int32_t ma_aaudio_stream_state_t; -typedef int32_t ma_aaudio_performance_mode_t; -typedef int32_t ma_aaudio_usage_t; -typedef int32_t ma_aaudio_content_type_t; -typedef int32_t ma_aaudio_input_preset_t; -typedef int32_t ma_aaudio_allowed_capture_policy_t; -typedef int32_t ma_aaudio_data_callback_result_t; -typedef struct ma_AAudioStreamBuilder_t* ma_AAudioStreamBuilder; -typedef struct ma_AAudioStream_t* ma_AAudioStream; - -#define MA_AAUDIO_UNSPECIFIED 0 - -/* Result codes. miniaudio only cares about the success code. */ -#define MA_AAUDIO_OK 0 - -/* Directions. */ -#define MA_AAUDIO_DIRECTION_OUTPUT 0 -#define MA_AAUDIO_DIRECTION_INPUT 1 - -/* Sharing modes. */ -#define MA_AAUDIO_SHARING_MODE_EXCLUSIVE 0 -#define MA_AAUDIO_SHARING_MODE_SHARED 1 - -/* Formats. */ -#define MA_AAUDIO_FORMAT_PCM_I16 1 -#define MA_AAUDIO_FORMAT_PCM_FLOAT 2 - -/* Stream states. */ -#define MA_AAUDIO_STREAM_STATE_UNINITIALIZED 0 -#define MA_AAUDIO_STREAM_STATE_UNKNOWN 1 -#define MA_AAUDIO_STREAM_STATE_OPEN 2 -#define MA_AAUDIO_STREAM_STATE_STARTING 3 -#define MA_AAUDIO_STREAM_STATE_STARTED 4 -#define MA_AAUDIO_STREAM_STATE_PAUSING 5 -#define MA_AAUDIO_STREAM_STATE_PAUSED 6 -#define MA_AAUDIO_STREAM_STATE_FLUSHING 7 -#define MA_AAUDIO_STREAM_STATE_FLUSHED 8 -#define MA_AAUDIO_STREAM_STATE_STOPPING 9 -#define MA_AAUDIO_STREAM_STATE_STOPPED 10 -#define MA_AAUDIO_STREAM_STATE_CLOSING 11 -#define MA_AAUDIO_STREAM_STATE_CLOSED 12 -#define MA_AAUDIO_STREAM_STATE_DISCONNECTED 13 - -/* Performance modes. */ -#define MA_AAUDIO_PERFORMANCE_MODE_NONE 10 -#define MA_AAUDIO_PERFORMANCE_MODE_POWER_SAVING 11 -#define MA_AAUDIO_PERFORMANCE_MODE_LOW_LATENCY 12 - -/* Usage types. */ -#define MA_AAUDIO_USAGE_MEDIA 1 -#define MA_AAUDIO_USAGE_VOICE_COMMUNICATION 2 -#define MA_AAUDIO_USAGE_VOICE_COMMUNICATION_SIGNALLING 3 -#define MA_AAUDIO_USAGE_ALARM 4 -#define MA_AAUDIO_USAGE_NOTIFICATION 5 -#define MA_AAUDIO_USAGE_NOTIFICATION_RINGTONE 6 -#define MA_AAUDIO_USAGE_NOTIFICATION_EVENT 10 -#define MA_AAUDIO_USAGE_ASSISTANCE_ACCESSIBILITY 11 -#define MA_AAUDIO_USAGE_ASSISTANCE_NAVIGATION_GUIDANCE 12 -#define MA_AAUDIO_USAGE_ASSISTANCE_SONIFICATION 13 -#define MA_AAUDIO_USAGE_GAME 14 -#define MA_AAUDIO_USAGE_ASSISTANT 16 -#define MA_AAUDIO_SYSTEM_USAGE_EMERGENCY 1000 -#define MA_AAUDIO_SYSTEM_USAGE_SAFETY 1001 -#define MA_AAUDIO_SYSTEM_USAGE_VEHICLE_STATUS 1002 -#define MA_AAUDIO_SYSTEM_USAGE_ANNOUNCEMENT 1003 - -/* Content types. */ -#define MA_AAUDIO_CONTENT_TYPE_SPEECH 1 -#define MA_AAUDIO_CONTENT_TYPE_MUSIC 2 -#define MA_AAUDIO_CONTENT_TYPE_MOVIE 3 -#define MA_AAUDIO_CONTENT_TYPE_SONIFICATION 4 - -/* Input presets. */ -#define MA_AAUDIO_INPUT_PRESET_GENERIC 1 -#define MA_AAUDIO_INPUT_PRESET_CAMCORDER 5 -#define MA_AAUDIO_INPUT_PRESET_VOICE_RECOGNITION 6 -#define MA_AAUDIO_INPUT_PRESET_VOICE_COMMUNICATION 7 -#define MA_AAUDIO_INPUT_PRESET_UNPROCESSED 9 -#define MA_AAUDIO_INPUT_PRESET_VOICE_PERFORMANCE 10 - -/* Allowed Capture Policies */ -#define MA_AAUDIO_ALLOW_CAPTURE_BY_ALL 1 -#define MA_AAUDIO_ALLOW_CAPTURE_BY_SYSTEM 2 -#define MA_AAUDIO_ALLOW_CAPTURE_BY_NONE 3 - -/* Callback results. */ -#define MA_AAUDIO_CALLBACK_RESULT_CONTINUE 0 -#define MA_AAUDIO_CALLBACK_RESULT_STOP 1 - - -typedef ma_aaudio_data_callback_result_t (* ma_AAudioStream_dataCallback) (ma_AAudioStream* pStream, void* pUserData, void* pAudioData, int32_t numFrames); -typedef void (* ma_AAudioStream_errorCallback)(ma_AAudioStream *pStream, void *pUserData, ma_aaudio_result_t error); - -typedef ma_aaudio_result_t (* MA_PFN_AAudio_createStreamBuilder) (ma_AAudioStreamBuilder** ppBuilder); -typedef ma_aaudio_result_t (* MA_PFN_AAudioStreamBuilder_delete) (ma_AAudioStreamBuilder* pBuilder); -typedef void (* MA_PFN_AAudioStreamBuilder_setDeviceId) (ma_AAudioStreamBuilder* pBuilder, int32_t deviceId); -typedef void (* MA_PFN_AAudioStreamBuilder_setDirection) (ma_AAudioStreamBuilder* pBuilder, ma_aaudio_direction_t direction); -typedef void (* MA_PFN_AAudioStreamBuilder_setSharingMode) (ma_AAudioStreamBuilder* pBuilder, ma_aaudio_sharing_mode_t sharingMode); -typedef void (* MA_PFN_AAudioStreamBuilder_setFormat) (ma_AAudioStreamBuilder* pBuilder, ma_aaudio_format_t format); -typedef void (* MA_PFN_AAudioStreamBuilder_setChannelCount) (ma_AAudioStreamBuilder* pBuilder, int32_t channelCount); -typedef void (* MA_PFN_AAudioStreamBuilder_setSampleRate) (ma_AAudioStreamBuilder* pBuilder, int32_t sampleRate); -typedef void (* MA_PFN_AAudioStreamBuilder_setBufferCapacityInFrames)(ma_AAudioStreamBuilder* pBuilder, int32_t numFrames); -typedef void (* MA_PFN_AAudioStreamBuilder_setFramesPerDataCallback) (ma_AAudioStreamBuilder* pBuilder, int32_t numFrames); -typedef void (* MA_PFN_AAudioStreamBuilder_setDataCallback) (ma_AAudioStreamBuilder* pBuilder, ma_AAudioStream_dataCallback callback, void* pUserData); -typedef void (* MA_PFN_AAudioStreamBuilder_setErrorCallback) (ma_AAudioStreamBuilder* pBuilder, ma_AAudioStream_errorCallback callback, void* pUserData); -typedef void (* MA_PFN_AAudioStreamBuilder_setPerformanceMode) (ma_AAudioStreamBuilder* pBuilder, ma_aaudio_performance_mode_t mode); -typedef void (* MA_PFN_AAudioStreamBuilder_setUsage) (ma_AAudioStreamBuilder* pBuilder, ma_aaudio_usage_t contentType); -typedef void (* MA_PFN_AAudioStreamBuilder_setContentType) (ma_AAudioStreamBuilder* pBuilder, ma_aaudio_content_type_t contentType); -typedef void (* MA_PFN_AAudioStreamBuilder_setInputPreset) (ma_AAudioStreamBuilder* pBuilder, ma_aaudio_input_preset_t inputPreset); -typedef void (* MA_PFN_AAudioStreamBuilder_setAllowedCapturePolicy) (ma_AAudioStreamBuilder* pBuilder, ma_aaudio_allowed_capture_policy_t policy); -typedef ma_aaudio_result_t (* MA_PFN_AAudioStreamBuilder_openStream) (ma_AAudioStreamBuilder* pBuilder, ma_AAudioStream** ppStream); -typedef ma_aaudio_result_t (* MA_PFN_AAudioStream_close) (ma_AAudioStream* pStream); -typedef ma_aaudio_stream_state_t (* MA_PFN_AAudioStream_getState) (ma_AAudioStream* pStream); -typedef ma_aaudio_result_t (* MA_PFN_AAudioStream_waitForStateChange) (ma_AAudioStream* pStream, ma_aaudio_stream_state_t inputState, ma_aaudio_stream_state_t* pNextState, int64_t timeoutInNanoseconds); -typedef ma_aaudio_format_t (* MA_PFN_AAudioStream_getFormat) (ma_AAudioStream* pStream); -typedef int32_t (* MA_PFN_AAudioStream_getChannelCount) (ma_AAudioStream* pStream); -typedef int32_t (* MA_PFN_AAudioStream_getSampleRate) (ma_AAudioStream* pStream); -typedef int32_t (* MA_PFN_AAudioStream_getBufferCapacityInFrames) (ma_AAudioStream* pStream); -typedef int32_t (* MA_PFN_AAudioStream_getFramesPerDataCallback) (ma_AAudioStream* pStream); -typedef int32_t (* MA_PFN_AAudioStream_getFramesPerBurst) (ma_AAudioStream* pStream); -typedef ma_aaudio_result_t (* MA_PFN_AAudioStream_requestStart) (ma_AAudioStream* pStream); -typedef ma_aaudio_result_t (* MA_PFN_AAudioStream_requestStop) (ma_AAudioStream* pStream); - -static ma_result ma_result_from_aaudio(ma_aaudio_result_t resultAA) -{ - switch (resultAA) - { - case MA_AAUDIO_OK: return MA_SUCCESS; - default: break; - } - - return MA_ERROR; -} - -static ma_aaudio_usage_t ma_to_usage__aaudio(ma_aaudio_usage usage) -{ - switch (usage) { - case ma_aaudio_usage_media: return MA_AAUDIO_USAGE_MEDIA; - case ma_aaudio_usage_voice_communication: return MA_AAUDIO_USAGE_VOICE_COMMUNICATION; - case ma_aaudio_usage_voice_communication_signalling: return MA_AAUDIO_USAGE_VOICE_COMMUNICATION_SIGNALLING; - case ma_aaudio_usage_alarm: return MA_AAUDIO_USAGE_ALARM; - case ma_aaudio_usage_notification: return MA_AAUDIO_USAGE_NOTIFICATION; - case ma_aaudio_usage_notification_ringtone: return MA_AAUDIO_USAGE_NOTIFICATION_RINGTONE; - case ma_aaudio_usage_notification_event: return MA_AAUDIO_USAGE_NOTIFICATION_EVENT; - case ma_aaudio_usage_assistance_accessibility: return MA_AAUDIO_USAGE_ASSISTANCE_ACCESSIBILITY; - case ma_aaudio_usage_assistance_navigation_guidance: return MA_AAUDIO_USAGE_ASSISTANCE_NAVIGATION_GUIDANCE; - case ma_aaudio_usage_assistance_sonification: return MA_AAUDIO_USAGE_ASSISTANCE_SONIFICATION; - case ma_aaudio_usage_game: return MA_AAUDIO_USAGE_GAME; - case ma_aaudio_usage_assitant: return MA_AAUDIO_USAGE_ASSISTANT; - case ma_aaudio_usage_emergency: return MA_AAUDIO_SYSTEM_USAGE_EMERGENCY; - case ma_aaudio_usage_safety: return MA_AAUDIO_SYSTEM_USAGE_SAFETY; - case ma_aaudio_usage_vehicle_status: return MA_AAUDIO_SYSTEM_USAGE_VEHICLE_STATUS; - case ma_aaudio_usage_announcement: return MA_AAUDIO_SYSTEM_USAGE_ANNOUNCEMENT; - default: break; - } - - return MA_AAUDIO_USAGE_MEDIA; -} - -static ma_aaudio_content_type_t ma_to_content_type__aaudio(ma_aaudio_content_type contentType) -{ - switch (contentType) { - case ma_aaudio_content_type_speech: return MA_AAUDIO_CONTENT_TYPE_SPEECH; - case ma_aaudio_content_type_music: return MA_AAUDIO_CONTENT_TYPE_MUSIC; - case ma_aaudio_content_type_movie: return MA_AAUDIO_CONTENT_TYPE_MOVIE; - case ma_aaudio_content_type_sonification: return MA_AAUDIO_CONTENT_TYPE_SONIFICATION; - default: break; - } - - return MA_AAUDIO_CONTENT_TYPE_SPEECH; -} - -static ma_aaudio_input_preset_t ma_to_input_preset__aaudio(ma_aaudio_input_preset inputPreset) -{ - switch (inputPreset) { - case ma_aaudio_input_preset_generic: return MA_AAUDIO_INPUT_PRESET_GENERIC; - case ma_aaudio_input_preset_camcorder: return MA_AAUDIO_INPUT_PRESET_CAMCORDER; - case ma_aaudio_input_preset_voice_recognition: return MA_AAUDIO_INPUT_PRESET_VOICE_RECOGNITION; - case ma_aaudio_input_preset_voice_communication: return MA_AAUDIO_INPUT_PRESET_VOICE_COMMUNICATION; - case ma_aaudio_input_preset_unprocessed: return MA_AAUDIO_INPUT_PRESET_UNPROCESSED; - case ma_aaudio_input_preset_voice_performance: return MA_AAUDIO_INPUT_PRESET_VOICE_PERFORMANCE; - default: break; - } - - return MA_AAUDIO_INPUT_PRESET_GENERIC; -} - -static ma_aaudio_allowed_capture_policy_t ma_to_allowed_capture_policy__aaudio(ma_aaudio_allowed_capture_policy allowedCapturePolicy) -{ - switch (allowedCapturePolicy) { - case ma_aaudio_allow_capture_by_all: return MA_AAUDIO_ALLOW_CAPTURE_BY_ALL; - case ma_aaudio_allow_capture_by_system: return MA_AAUDIO_ALLOW_CAPTURE_BY_SYSTEM; - case ma_aaudio_allow_capture_by_none: return MA_AAUDIO_ALLOW_CAPTURE_BY_NONE; - default: break; - } - - return MA_AAUDIO_ALLOW_CAPTURE_BY_ALL; -} - -static void ma_stream_error_callback__aaudio(ma_AAudioStream* pStream, void* pUserData, ma_aaudio_result_t error) -{ - ma_result result; - ma_job job; - ma_device* pDevice = (ma_device*)pUserData; - MA_ASSERT(pDevice != NULL); - - (void)error; - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, "[AAudio] ERROR CALLBACK: error=%d, AAudioStream_getState()=%d\n", error, ((MA_PFN_AAudioStream_getState)pDevice->pContext->aaudio.AAudioStream_getState)(pStream)); - /* - When we get an error, we'll assume that the stream is in an erroneous state and needs to be restarted. From the documentation, - we cannot do this from the error callback. Therefore we are going to use an event thread for the AAudio backend to do this - cleanly and safely. - */ - job = ma_job_init(MA_JOB_TYPE_DEVICE_AAUDIO_REROUTE); - job.data.device.aaudio.reroute.pDevice = pDevice; - - if (pStream == pDevice->aaudio.pStreamCapture) { - job.data.device.aaudio.reroute.deviceType = ma_device_type_capture; - } - else { - job.data.device.aaudio.reroute.deviceType = ma_device_type_playback; - } - - result = ma_device_job_thread_post(&pDevice->pContext->aaudio.jobThread, &job); - if (result != MA_SUCCESS) { - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, "[AAudio] Device Disconnected. Failed to post job for rerouting.\n"); - return; - } -} - -static ma_aaudio_data_callback_result_t ma_stream_data_callback_capture__aaudio(ma_AAudioStream* pStream, void* pUserData, void* pAudioData, int32_t frameCount) -{ - ma_device* pDevice = (ma_device*)pUserData; - MA_ASSERT(pDevice != NULL); - - if (frameCount > 0) { - ma_device_handle_backend_data_callback(pDevice, NULL, pAudioData, (ma_uint32)frameCount); - } - - (void)pStream; - return MA_AAUDIO_CALLBACK_RESULT_CONTINUE; -} - -static ma_aaudio_data_callback_result_t ma_stream_data_callback_playback__aaudio(ma_AAudioStream* pStream, void* pUserData, void* pAudioData, int32_t frameCount) -{ - ma_device* pDevice = (ma_device*)pUserData; - MA_ASSERT(pDevice != NULL); - - /* - I've had a report that AAudio can sometimes post a frame count of 0. We need to check for that here - so we don't get any errors at a deeper level. I'm doing the same with the capture side for safety, - though I've not yet had any reports about that one. - */ - if (frameCount > 0) { - ma_device_handle_backend_data_callback(pDevice, pAudioData, NULL, (ma_uint32)frameCount); - } - - (void)pStream; - return MA_AAUDIO_CALLBACK_RESULT_CONTINUE; -} - -static ma_result ma_create_and_configure_AAudioStreamBuilder__aaudio(ma_context* pContext, const ma_device_id* pDeviceID, ma_device_type deviceType, ma_share_mode shareMode, const ma_device_descriptor* pDescriptor, const ma_device_config* pConfig, ma_device* pDevice, ma_AAudioStreamBuilder** ppBuilder) -{ - ma_AAudioStreamBuilder* pBuilder; - ma_aaudio_result_t resultAA; - - /* Safety. */ - *ppBuilder = NULL; - - resultAA = ((MA_PFN_AAudio_createStreamBuilder)pContext->aaudio.AAudio_createStreamBuilder)(&pBuilder); - if (resultAA != MA_AAUDIO_OK) { - return ma_result_from_aaudio(resultAA); - } - - if (pDeviceID != NULL) { - ((MA_PFN_AAudioStreamBuilder_setDeviceId)pContext->aaudio.AAudioStreamBuilder_setDeviceId)(pBuilder, pDeviceID->aaudio); - } - - ((MA_PFN_AAudioStreamBuilder_setDirection)pContext->aaudio.AAudioStreamBuilder_setDirection)(pBuilder, (deviceType == ma_device_type_playback) ? MA_AAUDIO_DIRECTION_OUTPUT : MA_AAUDIO_DIRECTION_INPUT); - ((MA_PFN_AAudioStreamBuilder_setSharingMode)pContext->aaudio.AAudioStreamBuilder_setSharingMode)(pBuilder, (shareMode == ma_share_mode_shared) ? MA_AAUDIO_SHARING_MODE_SHARED : MA_AAUDIO_SHARING_MODE_EXCLUSIVE); - - - /* If we have a device descriptor make sure we configure the stream builder to take our requested parameters. */ - if (pDescriptor != NULL) { - MA_ASSERT(pConfig != NULL); /* We must have a device config if we also have a descriptor. The config is required for AAudio specific configuration options. */ - - if (pDescriptor->sampleRate != 0) { - ((MA_PFN_AAudioStreamBuilder_setSampleRate)pContext->aaudio.AAudioStreamBuilder_setSampleRate)(pBuilder, pDescriptor->sampleRate); - } - - if (pDescriptor->channels != 0) { - ((MA_PFN_AAudioStreamBuilder_setChannelCount)pContext->aaudio.AAudioStreamBuilder_setChannelCount)(pBuilder, pDescriptor->channels); - } - - if (pDescriptor->format != ma_format_unknown) { - ((MA_PFN_AAudioStreamBuilder_setFormat)pContext->aaudio.AAudioStreamBuilder_setFormat)(pBuilder, (pDescriptor->format == ma_format_s16) ? MA_AAUDIO_FORMAT_PCM_I16 : MA_AAUDIO_FORMAT_PCM_FLOAT); - } - - - /* - There have been reports where setting the frames per data callback results in an error. - In particular, re-routing may inadvertently switch from low-latency mode, resulting in a less stable - stream from the legacy path (AudioStreamLegacy). To address this, we simply don't set the value. It - can still be set if it's explicitly requested via the aaudio.allowSetBufferCapacity variable in the - device config. - */ - if ((!pConfig->aaudio.enableCompatibilityWorkarounds || ma_android_sdk_version() > 30) && pConfig->aaudio.allowSetBufferCapacity) { - /* - AAudio is annoying when it comes to its buffer calculation stuff because it doesn't let you - retrieve the actual sample rate until after you've opened the stream. But you need to configure - the buffer capacity before you open the stream... :/ - - To solve, we're just going to assume MA_DEFAULT_SAMPLE_RATE (48000) and move on. - */ - ma_uint32 bufferCapacityInFrames = ma_calculate_buffer_size_in_frames_from_descriptor(pDescriptor, pDescriptor->sampleRate, pConfig->performanceProfile) * pDescriptor->periodCount; - - ((MA_PFN_AAudioStreamBuilder_setBufferCapacityInFrames)pContext->aaudio.AAudioStreamBuilder_setBufferCapacityInFrames)(pBuilder, bufferCapacityInFrames); - ((MA_PFN_AAudioStreamBuilder_setFramesPerDataCallback)pContext->aaudio.AAudioStreamBuilder_setFramesPerDataCallback)(pBuilder, bufferCapacityInFrames / pDescriptor->periodCount); - } - - if (deviceType == ma_device_type_capture) { - if (pConfig->aaudio.inputPreset != ma_aaudio_input_preset_default && pContext->aaudio.AAudioStreamBuilder_setInputPreset != NULL) { - ((MA_PFN_AAudioStreamBuilder_setInputPreset)pContext->aaudio.AAudioStreamBuilder_setInputPreset)(pBuilder, ma_to_input_preset__aaudio(pConfig->aaudio.inputPreset)); - } - - ((MA_PFN_AAudioStreamBuilder_setDataCallback)pContext->aaudio.AAudioStreamBuilder_setDataCallback)(pBuilder, ma_stream_data_callback_capture__aaudio, (void*)pDevice); - } else { - if (pConfig->aaudio.usage != ma_aaudio_usage_default && pContext->aaudio.AAudioStreamBuilder_setUsage != NULL) { - ((MA_PFN_AAudioStreamBuilder_setUsage)pContext->aaudio.AAudioStreamBuilder_setUsage)(pBuilder, ma_to_usage__aaudio(pConfig->aaudio.usage)); - } - - if (pConfig->aaudio.contentType != ma_aaudio_content_type_default && pContext->aaudio.AAudioStreamBuilder_setContentType != NULL) { - ((MA_PFN_AAudioStreamBuilder_setContentType)pContext->aaudio.AAudioStreamBuilder_setContentType)(pBuilder, ma_to_content_type__aaudio(pConfig->aaudio.contentType)); - } - - if (pConfig->aaudio.allowedCapturePolicy != ma_aaudio_allow_capture_default && pContext->aaudio.AAudioStreamBuilder_setAllowedCapturePolicy != NULL) { - ((MA_PFN_AAudioStreamBuilder_setAllowedCapturePolicy)pContext->aaudio.AAudioStreamBuilder_setAllowedCapturePolicy)(pBuilder, ma_to_allowed_capture_policy__aaudio(pConfig->aaudio.allowedCapturePolicy)); - } - - ((MA_PFN_AAudioStreamBuilder_setDataCallback)pContext->aaudio.AAudioStreamBuilder_setDataCallback)(pBuilder, ma_stream_data_callback_playback__aaudio, (void*)pDevice); - } - - /* - If we set AAUDIO_PERFORMANCE_MODE_LOW_LATENCY, we allow for MMAP (non-legacy path). - Since there's a mapping between miniaudio's performance profiles and AAudio's performance modes, let's use it. - Beware though, with a conservative performance profile, AAudio will indeed take the legacy path. - */ - ((MA_PFN_AAudioStreamBuilder_setPerformanceMode)pContext->aaudio.AAudioStreamBuilder_setPerformanceMode)(pBuilder, (pConfig->performanceProfile == ma_performance_profile_low_latency) ? MA_AAUDIO_PERFORMANCE_MODE_LOW_LATENCY : MA_AAUDIO_PERFORMANCE_MODE_NONE); - - /* We need to set an error callback to detect device changes. */ - if (pDevice != NULL) { /* <-- pDevice should never be null if pDescriptor is not null, which is always the case if we hit this branch. Check anyway for safety. */ - ((MA_PFN_AAudioStreamBuilder_setErrorCallback)pContext->aaudio.AAudioStreamBuilder_setErrorCallback)(pBuilder, ma_stream_error_callback__aaudio, (void*)pDevice); - } - } - - *ppBuilder = pBuilder; - - return MA_SUCCESS; -} - -static ma_result ma_open_stream_and_close_builder__aaudio(ma_context* pContext, ma_AAudioStreamBuilder* pBuilder, ma_AAudioStream** ppStream) -{ - ma_result result; - - result = ma_result_from_aaudio(((MA_PFN_AAudioStreamBuilder_openStream)pContext->aaudio.AAudioStreamBuilder_openStream)(pBuilder, ppStream)); - ((MA_PFN_AAudioStreamBuilder_delete)pContext->aaudio.AAudioStreamBuilder_delete)(pBuilder); - - return result; -} - -static ma_result ma_open_stream_basic__aaudio(ma_context* pContext, const ma_device_id* pDeviceID, ma_device_type deviceType, ma_share_mode shareMode, ma_AAudioStream** ppStream) -{ - ma_result result; - ma_AAudioStreamBuilder* pBuilder; - - *ppStream = NULL; - - result = ma_create_and_configure_AAudioStreamBuilder__aaudio(pContext, pDeviceID, deviceType, shareMode, NULL, NULL, NULL, &pBuilder); - if (result != MA_SUCCESS) { - return result; - } - - /* Let's give AAudio a hint to avoid the legacy path (AudioStreamLegacy). */ - ((MA_PFN_AAudioStreamBuilder_setPerformanceMode)pContext->aaudio.AAudioStreamBuilder_setPerformanceMode)(pBuilder, MA_AAUDIO_PERFORMANCE_MODE_LOW_LATENCY); - - return ma_open_stream_and_close_builder__aaudio(pContext, pBuilder, ppStream); -} - -static ma_result ma_open_stream__aaudio(ma_device* pDevice, const ma_device_config* pConfig, ma_device_type deviceType, const ma_device_descriptor* pDescriptor, ma_AAudioStream** ppStream) -{ - ma_result result; - ma_AAudioStreamBuilder* pBuilder; - - MA_ASSERT(pDevice != NULL); - MA_ASSERT(pDescriptor != NULL); - MA_ASSERT(deviceType != ma_device_type_duplex); /* This function should not be called for a full-duplex device type. */ - - *ppStream = NULL; - - result = ma_create_and_configure_AAudioStreamBuilder__aaudio(pDevice->pContext, pDescriptor->pDeviceID, deviceType, pDescriptor->shareMode, pDescriptor, pConfig, pDevice, &pBuilder); - if (result != MA_SUCCESS) { - return result; - } - - return ma_open_stream_and_close_builder__aaudio(pDevice->pContext, pBuilder, ppStream); -} - -static ma_result ma_close_stream__aaudio(ma_context* pContext, ma_AAudioStream* pStream) -{ - if (pStream == NULL) { - return MA_INVALID_ARGS; - } - - return ma_result_from_aaudio(((MA_PFN_AAudioStream_close)pContext->aaudio.AAudioStream_close)(pStream)); -} - -static ma_bool32 ma_has_default_device__aaudio(ma_context* pContext, ma_device_type deviceType) -{ - /* The only way to know this is to try creating a stream. */ - ma_AAudioStream* pStream; - ma_result result = ma_open_stream_basic__aaudio(pContext, NULL, deviceType, ma_share_mode_shared, &pStream); - if (result != MA_SUCCESS) { - return MA_FALSE; - } - - ma_close_stream__aaudio(pContext, pStream); - return MA_TRUE; -} - -static ma_result ma_wait_for_simple_state_transition__aaudio(ma_context* pContext, ma_AAudioStream* pStream, ma_aaudio_stream_state_t oldState, ma_aaudio_stream_state_t newState) -{ - ma_aaudio_stream_state_t actualNewState; - ma_aaudio_result_t resultAA = ((MA_PFN_AAudioStream_waitForStateChange)pContext->aaudio.AAudioStream_waitForStateChange)(pStream, oldState, &actualNewState, 5000000000); /* 5 second timeout. */ - if (resultAA != MA_AAUDIO_OK) { - return ma_result_from_aaudio(resultAA); - } - - if (newState != actualNewState) { - return MA_ERROR; /* Failed to transition into the expected state. */ - } - - return MA_SUCCESS; -} - - -static ma_result ma_context_enumerate_devices__aaudio(ma_context* pContext, ma_enum_devices_callback_proc callback, void* pUserData) -{ - ma_bool32 cbResult = MA_TRUE; - - MA_ASSERT(pContext != NULL); - MA_ASSERT(callback != NULL); - - /* Unfortunately AAudio does not have an enumeration API. Therefore I'm only going to report default devices, but only if it can instantiate a stream. */ - - /* Playback. */ - if (cbResult) { - ma_device_info deviceInfo; - MA_ZERO_OBJECT(&deviceInfo); - deviceInfo.id.aaudio = MA_AAUDIO_UNSPECIFIED; - ma_strncpy_s(deviceInfo.name, sizeof(deviceInfo.name), MA_DEFAULT_PLAYBACK_DEVICE_NAME, (size_t)-1); - - if (ma_has_default_device__aaudio(pContext, ma_device_type_playback)) { - cbResult = callback(pContext, ma_device_type_playback, &deviceInfo, pUserData); - } - } - - /* Capture. */ - if (cbResult) { - ma_device_info deviceInfo; - MA_ZERO_OBJECT(&deviceInfo); - deviceInfo.id.aaudio = MA_AAUDIO_UNSPECIFIED; - ma_strncpy_s(deviceInfo.name, sizeof(deviceInfo.name), MA_DEFAULT_CAPTURE_DEVICE_NAME, (size_t)-1); - - if (ma_has_default_device__aaudio(pContext, ma_device_type_capture)) { - cbResult = callback(pContext, ma_device_type_capture, &deviceInfo, pUserData); - } - } - - return MA_SUCCESS; -} - -static void ma_context_add_native_data_format_from_AAudioStream_ex__aaudio(ma_context* pContext, ma_AAudioStream* pStream, ma_format format, ma_uint32 flags, ma_device_info* pDeviceInfo) -{ - MA_ASSERT(pContext != NULL); - MA_ASSERT(pStream != NULL); - MA_ASSERT(pDeviceInfo != NULL); - - pDeviceInfo->nativeDataFormats[pDeviceInfo->nativeDataFormatCount].format = format; - pDeviceInfo->nativeDataFormats[pDeviceInfo->nativeDataFormatCount].channels = ((MA_PFN_AAudioStream_getChannelCount)pContext->aaudio.AAudioStream_getChannelCount)(pStream); - pDeviceInfo->nativeDataFormats[pDeviceInfo->nativeDataFormatCount].sampleRate = ((MA_PFN_AAudioStream_getSampleRate)pContext->aaudio.AAudioStream_getSampleRate)(pStream); - pDeviceInfo->nativeDataFormats[pDeviceInfo->nativeDataFormatCount].flags = flags; - pDeviceInfo->nativeDataFormatCount += 1; -} - -static void ma_context_add_native_data_format_from_AAudioStream__aaudio(ma_context* pContext, ma_AAudioStream* pStream, ma_uint32 flags, ma_device_info* pDeviceInfo) -{ - /* AAudio supports s16 and f32. */ - ma_context_add_native_data_format_from_AAudioStream_ex__aaudio(pContext, pStream, ma_format_f32, flags, pDeviceInfo); - ma_context_add_native_data_format_from_AAudioStream_ex__aaudio(pContext, pStream, ma_format_s16, flags, pDeviceInfo); -} - -static ma_result ma_context_get_device_info__aaudio(ma_context* pContext, ma_device_type deviceType, const ma_device_id* pDeviceID, ma_device_info* pDeviceInfo) -{ - ma_AAudioStream* pStream; - ma_result result; - - MA_ASSERT(pContext != NULL); - - /* ID */ - if (pDeviceID != NULL) { - pDeviceInfo->id.aaudio = pDeviceID->aaudio; - } else { - pDeviceInfo->id.aaudio = MA_AAUDIO_UNSPECIFIED; - } - - /* Name */ - if (deviceType == ma_device_type_playback) { - ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), MA_DEFAULT_PLAYBACK_DEVICE_NAME, (size_t)-1); - } else { - ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), MA_DEFAULT_CAPTURE_DEVICE_NAME, (size_t)-1); - } - - - pDeviceInfo->nativeDataFormatCount = 0; - - /* We'll need to open the device to get accurate sample rate and channel count information. */ - result = ma_open_stream_basic__aaudio(pContext, pDeviceID, deviceType, ma_share_mode_shared, &pStream); - if (result != MA_SUCCESS) { - return result; - } - - ma_context_add_native_data_format_from_AAudioStream__aaudio(pContext, pStream, 0, pDeviceInfo); - - ma_close_stream__aaudio(pContext, pStream); - pStream = NULL; - - return MA_SUCCESS; -} - -static ma_result ma_close_streams__aaudio(ma_device* pDevice) -{ - MA_ASSERT(pDevice != NULL); - - /* When re-routing, streams may have been closed and never re-opened. Hence the extra checks below. */ - if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) { - ma_close_stream__aaudio(pDevice->pContext, (ma_AAudioStream*)pDevice->aaudio.pStreamCapture); - pDevice->aaudio.pStreamCapture = NULL; - } - if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) { - ma_close_stream__aaudio(pDevice->pContext, (ma_AAudioStream*)pDevice->aaudio.pStreamPlayback); - pDevice->aaudio.pStreamPlayback = NULL; - } - - return MA_SUCCESS; -} - -static ma_result ma_device_uninit__aaudio(ma_device* pDevice) -{ - MA_ASSERT(pDevice != NULL); - - /* Wait for any rerouting to finish before attempting to close the streams. */ - ma_mutex_lock(&pDevice->aaudio.rerouteLock); - { - ma_close_streams__aaudio(pDevice); - } - ma_mutex_unlock(&pDevice->aaudio.rerouteLock); - - /* Destroy re-routing lock. */ - ma_mutex_uninit(&pDevice->aaudio.rerouteLock); - - return MA_SUCCESS; -} - -static ma_result ma_device_init_by_type__aaudio(ma_device* pDevice, const ma_device_config* pConfig, ma_device_type deviceType, ma_device_descriptor* pDescriptor, ma_AAudioStream** ppStream) -{ - ma_result result; - int32_t bufferCapacityInFrames; - int32_t framesPerDataCallback; - ma_AAudioStream* pStream; - - MA_ASSERT(pDevice != NULL); - MA_ASSERT(pConfig != NULL); - MA_ASSERT(pDescriptor != NULL); - - *ppStream = NULL; /* Safety. */ - - /* First step is to open the stream. From there we'll be able to extract the internal configuration. */ - result = ma_open_stream__aaudio(pDevice, pConfig, deviceType, pDescriptor, &pStream); - if (result != MA_SUCCESS) { - return result; /* Failed to open the AAudio stream. */ - } - - /* Now extract the internal configuration. */ - pDescriptor->format = (((MA_PFN_AAudioStream_getFormat)pDevice->pContext->aaudio.AAudioStream_getFormat)(pStream) == MA_AAUDIO_FORMAT_PCM_I16) ? ma_format_s16 : ma_format_f32; - pDescriptor->channels = ((MA_PFN_AAudioStream_getChannelCount)pDevice->pContext->aaudio.AAudioStream_getChannelCount)(pStream); - pDescriptor->sampleRate = ((MA_PFN_AAudioStream_getSampleRate)pDevice->pContext->aaudio.AAudioStream_getSampleRate)(pStream); - - /* For the channel map we need to be sure we don't overflow any buffers. */ - if (pDescriptor->channels <= MA_MAX_CHANNELS) { - ma_channel_map_init_standard(ma_standard_channel_map_default, pDescriptor->channelMap, ma_countof(pDescriptor->channelMap), pDescriptor->channels); /* <-- Cannot find info on channel order, so assuming a default. */ - } else { - ma_channel_map_init_blank(pDescriptor->channelMap, MA_MAX_CHANNELS); /* Too many channels. Use a blank channel map. */ - } - - bufferCapacityInFrames = ((MA_PFN_AAudioStream_getBufferCapacityInFrames)pDevice->pContext->aaudio.AAudioStream_getBufferCapacityInFrames)(pStream); - framesPerDataCallback = ((MA_PFN_AAudioStream_getFramesPerDataCallback)pDevice->pContext->aaudio.AAudioStream_getFramesPerDataCallback)(pStream); - - if (framesPerDataCallback > 0) { - pDescriptor->periodSizeInFrames = framesPerDataCallback; - pDescriptor->periodCount = bufferCapacityInFrames / framesPerDataCallback; - } else { - pDescriptor->periodSizeInFrames = bufferCapacityInFrames; - pDescriptor->periodCount = 1; - } - - *ppStream = pStream; - - return MA_SUCCESS; -} - -static ma_result ma_device_init_streams__aaudio(ma_device* pDevice, const ma_device_config* pConfig, ma_device_descriptor* pDescriptorPlayback, ma_device_descriptor* pDescriptorCapture) -{ - ma_result result; - - MA_ASSERT(pDevice != NULL); - - if (pConfig->deviceType == ma_device_type_loopback) { - return MA_DEVICE_TYPE_NOT_SUPPORTED; - } - - pDevice->aaudio.usage = pConfig->aaudio.usage; - pDevice->aaudio.contentType = pConfig->aaudio.contentType; - pDevice->aaudio.inputPreset = pConfig->aaudio.inputPreset; - pDevice->aaudio.allowedCapturePolicy = pConfig->aaudio.allowedCapturePolicy; - pDevice->aaudio.noAutoStartAfterReroute = pConfig->aaudio.noAutoStartAfterReroute; - - if (pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) { - result = ma_device_init_by_type__aaudio(pDevice, pConfig, ma_device_type_capture, pDescriptorCapture, (ma_AAudioStream**)&pDevice->aaudio.pStreamCapture); - if (result != MA_SUCCESS) { - return result; - } - } - - if (pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) { - result = ma_device_init_by_type__aaudio(pDevice, pConfig, ma_device_type_playback, pDescriptorPlayback, (ma_AAudioStream**)&pDevice->aaudio.pStreamPlayback); - if (result != MA_SUCCESS) { - return result; - } - } - - return MA_SUCCESS; -} - -static ma_result ma_device_init__aaudio(ma_device* pDevice, const ma_device_config* pConfig, ma_device_descriptor* pDescriptorPlayback, ma_device_descriptor* pDescriptorCapture) -{ - ma_result result; - - MA_ASSERT(pDevice != NULL); - - result = ma_device_init_streams__aaudio(pDevice, pConfig, pDescriptorPlayback, pDescriptorCapture); - if (result != MA_SUCCESS) { - return result; - } - - result = ma_mutex_init(&pDevice->aaudio.rerouteLock); - if (result != MA_SUCCESS) { - return result; - } - - return MA_SUCCESS; -} - -static ma_result ma_device_start_stream__aaudio(ma_device* pDevice, ma_AAudioStream* pStream) -{ - ma_aaudio_result_t resultAA; - ma_aaudio_stream_state_t currentState; - - MA_ASSERT(pDevice != NULL); - - if (pStream == NULL) { - return MA_INVALID_ARGS; - } - - resultAA = ((MA_PFN_AAudioStream_requestStart)pDevice->pContext->aaudio.AAudioStream_requestStart)(pStream); - if (resultAA != MA_AAUDIO_OK) { - return ma_result_from_aaudio(resultAA); - } - - /* Do we actually need to wait for the device to transition into its started state? */ - - /* The device should be in either a starting or started state. If it's not set to started we need to wait for it to transition. It should go from starting to started. */ - currentState = ((MA_PFN_AAudioStream_getState)pDevice->pContext->aaudio.AAudioStream_getState)(pStream); - if (currentState != MA_AAUDIO_STREAM_STATE_STARTED) { - ma_result result; - - if (currentState != MA_AAUDIO_STREAM_STATE_STARTING) { - return MA_ERROR; /* Expecting the stream to be a starting or started state. */ - } - - result = ma_wait_for_simple_state_transition__aaudio(pDevice->pContext, pStream, currentState, MA_AAUDIO_STREAM_STATE_STARTED); - if (result != MA_SUCCESS) { - return result; - } - } - - return MA_SUCCESS; -} - -static ma_result ma_device_stop_stream__aaudio(ma_device* pDevice, ma_AAudioStream* pStream) -{ - ma_aaudio_result_t resultAA; - ma_aaudio_stream_state_t currentState; - - MA_ASSERT(pDevice != NULL); - - if (pStream == NULL) { - return MA_INVALID_ARGS; - } - - /* - From the AAudio documentation: - - The stream will stop after all of the data currently buffered has been played. - - This maps with miniaudio's requirement that device's be drained which means we don't need to implement any draining logic. - */ - currentState = ((MA_PFN_AAudioStream_getState)pDevice->pContext->aaudio.AAudioStream_getState)(pStream); - if (currentState == MA_AAUDIO_STREAM_STATE_DISCONNECTED) { - return MA_SUCCESS; /* The device is disconnected. Don't try stopping it. */ - } - - resultAA = ((MA_PFN_AAudioStream_requestStop)pDevice->pContext->aaudio.AAudioStream_requestStop)(pStream); - if (resultAA != MA_AAUDIO_OK) { - return ma_result_from_aaudio(resultAA); - } - - /* The device should be in either a stopping or stopped state. If it's not set to started we need to wait for it to transition. It should go from stopping to stopped. */ - currentState = ((MA_PFN_AAudioStream_getState)pDevice->pContext->aaudio.AAudioStream_getState)(pStream); - if (currentState != MA_AAUDIO_STREAM_STATE_STOPPED) { - ma_result result; - - if (currentState != MA_AAUDIO_STREAM_STATE_STOPPING) { - return MA_ERROR; /* Expecting the stream to be a stopping or stopped state. */ - } - - result = ma_wait_for_simple_state_transition__aaudio(pDevice->pContext, pStream, currentState, MA_AAUDIO_STREAM_STATE_STOPPED); - if (result != MA_SUCCESS) { - return result; - } - } - - return MA_SUCCESS; -} - -static ma_result ma_device_start__aaudio(ma_device* pDevice) -{ - MA_ASSERT(pDevice != NULL); - - if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) { - ma_result result = ma_device_start_stream__aaudio(pDevice, (ma_AAudioStream*)pDevice->aaudio.pStreamCapture); - if (result != MA_SUCCESS) { - return result; - } - } - - if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) { - ma_result result = ma_device_start_stream__aaudio(pDevice, (ma_AAudioStream*)pDevice->aaudio.pStreamPlayback); - if (result != MA_SUCCESS) { - if (pDevice->type == ma_device_type_duplex) { - ma_device_stop_stream__aaudio(pDevice, (ma_AAudioStream*)pDevice->aaudio.pStreamCapture); - } - return result; - } - } - - return MA_SUCCESS; -} - -static ma_result ma_device_stop__aaudio(ma_device* pDevice) -{ - MA_ASSERT(pDevice != NULL); - - if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) { - ma_result result = ma_device_stop_stream__aaudio(pDevice, (ma_AAudioStream*)pDevice->aaudio.pStreamCapture); - if (result != MA_SUCCESS) { - return result; - } - } - - if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) { - ma_result result = ma_device_stop_stream__aaudio(pDevice, (ma_AAudioStream*)pDevice->aaudio.pStreamPlayback); - if (result != MA_SUCCESS) { - return result; - } - } - - ma_device__on_notification_stopped(pDevice); - - return MA_SUCCESS; -} - -static ma_result ma_device_reinit__aaudio(ma_device* pDevice, ma_device_type deviceType) -{ - ma_result result; - int32_t retries = 0; - - MA_ASSERT(pDevice != NULL); - - /* - TODO: Stop retrying if main thread is about to uninit device. - */ - ma_mutex_lock(&pDevice->aaudio.rerouteLock); - { -error_disconnected: - /* The first thing to do is close the streams. */ - ma_close_streams__aaudio(pDevice); - - /* Now we need to reinitialize each streams. The hardest part with this is just filling output the config and descriptors. */ - ma_device_config deviceConfig; - ma_device_descriptor descriptorPlayback; - ma_device_descriptor descriptorCapture; - - deviceConfig = ma_device_config_init(deviceType); - deviceConfig.playback.pDeviceID = NULL; /* Only doing rerouting with default devices. */ - deviceConfig.playback.shareMode = pDevice->playback.shareMode; - deviceConfig.playback.format = pDevice->playback.format; - deviceConfig.playback.channels = pDevice->playback.channels; - deviceConfig.capture.pDeviceID = NULL; /* Only doing rerouting with default devices. */ - deviceConfig.capture.shareMode = pDevice->capture.shareMode; - deviceConfig.capture.format = pDevice->capture.format; - deviceConfig.capture.channels = pDevice->capture.channels; - deviceConfig.sampleRate = pDevice->sampleRate; - deviceConfig.aaudio.usage = pDevice->aaudio.usage; - deviceConfig.aaudio.contentType = pDevice->aaudio.contentType; - deviceConfig.aaudio.inputPreset = pDevice->aaudio.inputPreset; - deviceConfig.aaudio.allowedCapturePolicy = pDevice->aaudio.allowedCapturePolicy; - deviceConfig.aaudio.noAutoStartAfterReroute = pDevice->aaudio.noAutoStartAfterReroute; - deviceConfig.periods = 1; - - /* Try to get an accurate period size. */ - if (deviceType == ma_device_type_playback || deviceType == ma_device_type_duplex) { - deviceConfig.periodSizeInFrames = pDevice->playback.internalPeriodSizeInFrames; - } else { - deviceConfig.periodSizeInFrames = pDevice->capture.internalPeriodSizeInFrames; - } - - if (deviceType == ma_device_type_capture || deviceType == ma_device_type_duplex || deviceType == ma_device_type_loopback) { - descriptorCapture.pDeviceID = deviceConfig.capture.pDeviceID; - descriptorCapture.shareMode = deviceConfig.capture.shareMode; - descriptorCapture.format = deviceConfig.capture.format; - descriptorCapture.channels = deviceConfig.capture.channels; - descriptorCapture.sampleRate = deviceConfig.sampleRate; - descriptorCapture.periodSizeInFrames = deviceConfig.periodSizeInFrames; - descriptorCapture.periodCount = deviceConfig.periods; - } - - if (deviceType == ma_device_type_playback || deviceType == ma_device_type_duplex) { - descriptorPlayback.pDeviceID = deviceConfig.playback.pDeviceID; - descriptorPlayback.shareMode = deviceConfig.playback.shareMode; - descriptorPlayback.format = deviceConfig.playback.format; - descriptorPlayback.channels = deviceConfig.playback.channels; - descriptorPlayback.sampleRate = deviceConfig.sampleRate; - descriptorPlayback.periodSizeInFrames = deviceConfig.periodSizeInFrames; - descriptorPlayback.periodCount = deviceConfig.periods; - } - - result = ma_device_init_streams__aaudio(pDevice, &deviceConfig, &descriptorPlayback, &descriptorCapture); - if (result != MA_SUCCESS) { - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_WARNING, "[AAudio] Failed to create stream after route change."); - goto done; - } - - result = ma_device_post_init(pDevice, deviceType, &descriptorPlayback, &descriptorCapture); - if (result != MA_SUCCESS) { - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_WARNING, "[AAudio] Failed to initialize device after route change."); - ma_close_streams__aaudio(pDevice); - goto done; - } - - /* We'll only ever do this in response to a reroute. */ - ma_device__on_notification_rerouted(pDevice); - - /* If the device is started, start the streams. Maybe make this configurable? */ - if (ma_device_get_state(pDevice) == ma_device_state_started) { - if (pDevice->aaudio.noAutoStartAfterReroute == MA_FALSE) { - result = ma_device_start__aaudio(pDevice); - if (result != MA_SUCCESS) { - /* We got disconnected! Retry a few times, until we find a connected device! */ - retries += 1; - if (retries <= 3) { - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, "[AAudio] Failed to start stream after route change, retrying(%d)", retries); - goto error_disconnected; - } - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, "[AAudio] Failed to start stream after route change."); - goto done; - } - } else { - ma_device_stop(pDevice); /* Do a full device stop so we set internal state correctly. */ - } - } - - result = MA_SUCCESS; - } -done: - /* Re-routing done */ - ma_mutex_unlock(&pDevice->aaudio.rerouteLock); - - return result; -} - -static ma_result ma_device_get_info__aaudio(ma_device* pDevice, ma_device_type type, ma_device_info* pDeviceInfo) -{ - ma_AAudioStream* pStream = NULL; - - MA_ASSERT(pDevice != NULL); - MA_ASSERT(type != ma_device_type_duplex); - MA_ASSERT(pDeviceInfo != NULL); - - if (type == ma_device_type_capture) { - pStream = (ma_AAudioStream*)pDevice->aaudio.pStreamCapture; - pDeviceInfo->id.aaudio = pDevice->capture.id.aaudio; - ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), MA_DEFAULT_CAPTURE_DEVICE_NAME, (size_t)-1); /* Only supporting default devices. */ - } - if (type == ma_device_type_playback) { - pStream = (ma_AAudioStream*)pDevice->aaudio.pStreamPlayback; - pDeviceInfo->id.aaudio = pDevice->playback.id.aaudio; - ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), MA_DEFAULT_PLAYBACK_DEVICE_NAME, (size_t)-1); /* Only supporting default devices. */ - } - - /* Safety. Should never happen. */ - if (pStream == NULL) { - return MA_INVALID_OPERATION; - } - - pDeviceInfo->nativeDataFormatCount = 0; - ma_context_add_native_data_format_from_AAudioStream__aaudio(pDevice->pContext, pStream, 0, pDeviceInfo); - - return MA_SUCCESS; -} - - -static ma_result ma_context_uninit__aaudio(ma_context* pContext) -{ - MA_ASSERT(pContext != NULL); - MA_ASSERT(pContext->backend == ma_backend_aaudio); - - ma_device_job_thread_uninit(&pContext->aaudio.jobThread, &pContext->allocationCallbacks); - - ma_dlclose(ma_context_get_log(pContext), pContext->aaudio.hAAudio); - pContext->aaudio.hAAudio = NULL; - - return MA_SUCCESS; -} - -static ma_result ma_context_init__aaudio(ma_context* pContext, const ma_context_config* pConfig, ma_backend_callbacks* pCallbacks) -{ -#if !defined(MA_NO_RUNTIME_LINKING) - size_t i; - const char* libNames[] = { - "libaaudio.so" - }; - - for (i = 0; i < ma_countof(libNames); ++i) { - pContext->aaudio.hAAudio = ma_dlopen(ma_context_get_log(pContext), libNames[i]); - if (pContext->aaudio.hAAudio != NULL) { - break; - } - } - - if (pContext->aaudio.hAAudio == NULL) { - return MA_FAILED_TO_INIT_BACKEND; - } - - pContext->aaudio.AAudio_createStreamBuilder = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->aaudio.hAAudio, "AAudio_createStreamBuilder"); - pContext->aaudio.AAudioStreamBuilder_delete = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->aaudio.hAAudio, "AAudioStreamBuilder_delete"); - pContext->aaudio.AAudioStreamBuilder_setDeviceId = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->aaudio.hAAudio, "AAudioStreamBuilder_setDeviceId"); - pContext->aaudio.AAudioStreamBuilder_setDirection = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->aaudio.hAAudio, "AAudioStreamBuilder_setDirection"); - pContext->aaudio.AAudioStreamBuilder_setSharingMode = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->aaudio.hAAudio, "AAudioStreamBuilder_setSharingMode"); - pContext->aaudio.AAudioStreamBuilder_setFormat = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->aaudio.hAAudio, "AAudioStreamBuilder_setFormat"); - pContext->aaudio.AAudioStreamBuilder_setChannelCount = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->aaudio.hAAudio, "AAudioStreamBuilder_setChannelCount"); - pContext->aaudio.AAudioStreamBuilder_setSampleRate = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->aaudio.hAAudio, "AAudioStreamBuilder_setSampleRate"); - pContext->aaudio.AAudioStreamBuilder_setBufferCapacityInFrames = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->aaudio.hAAudio, "AAudioStreamBuilder_setBufferCapacityInFrames"); - pContext->aaudio.AAudioStreamBuilder_setFramesPerDataCallback = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->aaudio.hAAudio, "AAudioStreamBuilder_setFramesPerDataCallback"); - pContext->aaudio.AAudioStreamBuilder_setDataCallback = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->aaudio.hAAudio, "AAudioStreamBuilder_setDataCallback"); - pContext->aaudio.AAudioStreamBuilder_setErrorCallback = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->aaudio.hAAudio, "AAudioStreamBuilder_setErrorCallback"); - pContext->aaudio.AAudioStreamBuilder_setPerformanceMode = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->aaudio.hAAudio, "AAudioStreamBuilder_setPerformanceMode"); - pContext->aaudio.AAudioStreamBuilder_setUsage = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->aaudio.hAAudio, "AAudioStreamBuilder_setUsage"); - pContext->aaudio.AAudioStreamBuilder_setContentType = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->aaudio.hAAudio, "AAudioStreamBuilder_setContentType"); - pContext->aaudio.AAudioStreamBuilder_setInputPreset = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->aaudio.hAAudio, "AAudioStreamBuilder_setInputPreset"); - pContext->aaudio.AAudioStreamBuilder_setAllowedCapturePolicy = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->aaudio.hAAudio, "AAudioStreamBuilder_setAllowedCapturePolicy"); - pContext->aaudio.AAudioStreamBuilder_openStream = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->aaudio.hAAudio, "AAudioStreamBuilder_openStream"); - pContext->aaudio.AAudioStream_close = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->aaudio.hAAudio, "AAudioStream_close"); - pContext->aaudio.AAudioStream_getState = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->aaudio.hAAudio, "AAudioStream_getState"); - pContext->aaudio.AAudioStream_waitForStateChange = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->aaudio.hAAudio, "AAudioStream_waitForStateChange"); - pContext->aaudio.AAudioStream_getFormat = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->aaudio.hAAudio, "AAudioStream_getFormat"); - pContext->aaudio.AAudioStream_getChannelCount = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->aaudio.hAAudio, "AAudioStream_getChannelCount"); - pContext->aaudio.AAudioStream_getSampleRate = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->aaudio.hAAudio, "AAudioStream_getSampleRate"); - pContext->aaudio.AAudioStream_getBufferCapacityInFrames = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->aaudio.hAAudio, "AAudioStream_getBufferCapacityInFrames"); - pContext->aaudio.AAudioStream_getFramesPerDataCallback = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->aaudio.hAAudio, "AAudioStream_getFramesPerDataCallback"); - pContext->aaudio.AAudioStream_getFramesPerBurst = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->aaudio.hAAudio, "AAudioStream_getFramesPerBurst"); - pContext->aaudio.AAudioStream_requestStart = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->aaudio.hAAudio, "AAudioStream_requestStart"); - pContext->aaudio.AAudioStream_requestStop = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->aaudio.hAAudio, "AAudioStream_requestStop"); -#else - pContext->aaudio.AAudio_createStreamBuilder = (ma_proc)AAudio_createStreamBuilder; - pContext->aaudio.AAudioStreamBuilder_delete = (ma_proc)AAudioStreamBuilder_delete; - pContext->aaudio.AAudioStreamBuilder_setDeviceId = (ma_proc)AAudioStreamBuilder_setDeviceId; - pContext->aaudio.AAudioStreamBuilder_setDirection = (ma_proc)AAudioStreamBuilder_setDirection; - pContext->aaudio.AAudioStreamBuilder_setSharingMode = (ma_proc)AAudioStreamBuilder_setSharingMode; - pContext->aaudio.AAudioStreamBuilder_setFormat = (ma_proc)AAudioStreamBuilder_setFormat; - pContext->aaudio.AAudioStreamBuilder_setChannelCount = (ma_proc)AAudioStreamBuilder_setChannelCount; - pContext->aaudio.AAudioStreamBuilder_setSampleRate = (ma_proc)AAudioStreamBuilder_setSampleRate; - pContext->aaudio.AAudioStreamBuilder_setBufferCapacityInFrames = (ma_proc)AAudioStreamBuilder_setBufferCapacityInFrames; - pContext->aaudio.AAudioStreamBuilder_setFramesPerDataCallback = (ma_proc)AAudioStreamBuilder_setFramesPerDataCallback; - pContext->aaudio.AAudioStreamBuilder_setDataCallback = (ma_proc)AAudioStreamBuilder_setDataCallback; - pContext->aaudio.AAudioStreamBuilder_setErrorCallback = (ma_proc)AAudioStreamBuilder_setErrorCallback; - pContext->aaudio.AAudioStreamBuilder_setPerformanceMode = (ma_proc)AAudioStreamBuilder_setPerformanceMode; - pContext->aaudio.AAudioStreamBuilder_setUsage = (ma_proc)AAudioStreamBuilder_setUsage; - pContext->aaudio.AAudioStreamBuilder_setContentType = (ma_proc)AAudioStreamBuilder_setContentType; - pContext->aaudio.AAudioStreamBuilder_setInputPreset = (ma_proc)AAudioStreamBuilder_setInputPreset; - #if defined(__ANDROID_API__) && __ANDROID_API__ >= 29 - pContext->aaudio.AAudioStreamBuilder_setAllowedCapturePolicy = (ma_proc)AAudioStreamBuilder_setAllowedCapturePolicy; - #endif - pContext->aaudio.AAudioStreamBuilder_openStream = (ma_proc)AAudioStreamBuilder_openStream; - pContext->aaudio.AAudioStream_close = (ma_proc)AAudioStream_close; - pContext->aaudio.AAudioStream_getState = (ma_proc)AAudioStream_getState; - pContext->aaudio.AAudioStream_waitForStateChange = (ma_proc)AAudioStream_waitForStateChange; - pContext->aaudio.AAudioStream_getFormat = (ma_proc)AAudioStream_getFormat; - pContext->aaudio.AAudioStream_getChannelCount = (ma_proc)AAudioStream_getChannelCount; - pContext->aaudio.AAudioStream_getSampleRate = (ma_proc)AAudioStream_getSampleRate; - pContext->aaudio.AAudioStream_getBufferCapacityInFrames = (ma_proc)AAudioStream_getBufferCapacityInFrames; - pContext->aaudio.AAudioStream_getFramesPerDataCallback = (ma_proc)AAudioStream_getFramesPerDataCallback; - pContext->aaudio.AAudioStream_getFramesPerBurst = (ma_proc)AAudioStream_getFramesPerBurst; - pContext->aaudio.AAudioStream_requestStart = (ma_proc)AAudioStream_requestStart; - pContext->aaudio.AAudioStream_requestStop = (ma_proc)AAudioStream_requestStop; -#endif - - pCallbacks->onContextInit = ma_context_init__aaudio; - pCallbacks->onContextUninit = ma_context_uninit__aaudio; - pCallbacks->onContextEnumerateDevices = ma_context_enumerate_devices__aaudio; - pCallbacks->onContextGetDeviceInfo = ma_context_get_device_info__aaudio; - pCallbacks->onDeviceInit = ma_device_init__aaudio; - pCallbacks->onDeviceUninit = ma_device_uninit__aaudio; - pCallbacks->onDeviceStart = ma_device_start__aaudio; - pCallbacks->onDeviceStop = ma_device_stop__aaudio; - pCallbacks->onDeviceRead = NULL; /* Not used because AAudio is asynchronous. */ - pCallbacks->onDeviceWrite = NULL; /* Not used because AAudio is asynchronous. */ - pCallbacks->onDeviceDataLoop = NULL; /* Not used because AAudio is asynchronous. */ - pCallbacks->onDeviceGetInfo = ma_device_get_info__aaudio; - - - /* We need a job thread so we can deal with rerouting. */ - { - ma_result result; - ma_device_job_thread_config jobThreadConfig; - - jobThreadConfig = ma_device_job_thread_config_init(); - - result = ma_device_job_thread_init(&jobThreadConfig, &pContext->allocationCallbacks, &pContext->aaudio.jobThread); - if (result != MA_SUCCESS) { - ma_dlclose(ma_context_get_log(pContext), pContext->aaudio.hAAudio); - pContext->aaudio.hAAudio = NULL; - return result; - } - } - - - (void)pConfig; - return MA_SUCCESS; -} - -static ma_result ma_job_process__device__aaudio_reroute(ma_job* pJob) -{ - ma_result result; - ma_device* pDevice; - - MA_ASSERT(pJob != NULL); - - pDevice = (ma_device*)pJob->data.device.aaudio.reroute.pDevice; - MA_ASSERT(pDevice != NULL); - - /* Here is where we need to reroute the device. To do this we need to uninitialize the stream and reinitialize it. */ - result = ma_device_reinit__aaudio(pDevice, (ma_device_type)pJob->data.device.aaudio.reroute.deviceType); - if (result != MA_SUCCESS) { - /* - Getting here means we failed to reroute the device. The best thing I can think of here is to - just stop the device. - */ - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[AAudio] Stopping device due to reroute failure."); - ma_device_stop(pDevice); - return result; - } - - return MA_SUCCESS; -} -#else -/* Getting here means there is no AAudio backend so we need a no-op job implementation. */ -static ma_result ma_job_process__device__aaudio_reroute(ma_job* pJob) -{ - return ma_job_process__noop(pJob); -} -#endif /* AAudio */ - - -/****************************************************************************** - -OpenSL|ES Backend - -******************************************************************************/ -#ifdef MA_HAS_OPENSL -#include -#ifdef MA_ANDROID -#include -#endif - -typedef SLresult (SLAPIENTRY * ma_slCreateEngine_proc)(SLObjectItf* pEngine, SLuint32 numOptions, SLEngineOption* pEngineOptions, SLuint32 numInterfaces, SLInterfaceID* pInterfaceIds, SLboolean* pInterfaceRequired); - -/* OpenSL|ES has one-per-application objects :( */ -static SLObjectItf g_maEngineObjectSL = NULL; -static SLEngineItf g_maEngineSL = NULL; -static ma_uint32 g_maOpenSLInitCounter = 0; -static ma_spinlock g_maOpenSLSpinlock = 0; /* For init/uninit. */ - -#define MA_OPENSL_OBJ(p) (*((SLObjectItf)(p))) -#define MA_OPENSL_OUTPUTMIX(p) (*((SLOutputMixItf)(p))) -#define MA_OPENSL_PLAY(p) (*((SLPlayItf)(p))) -#define MA_OPENSL_RECORD(p) (*((SLRecordItf)(p))) - -#ifdef MA_ANDROID -#define MA_OPENSL_BUFFERQUEUE(p) (*((SLAndroidSimpleBufferQueueItf)(p))) -#else -#define MA_OPENSL_BUFFERQUEUE(p) (*((SLBufferQueueItf)(p))) -#endif - -static ma_result ma_result_from_OpenSL(SLuint32 result) -{ - switch (result) - { - case SL_RESULT_SUCCESS: return MA_SUCCESS; - case SL_RESULT_PRECONDITIONS_VIOLATED: return MA_ERROR; - case SL_RESULT_PARAMETER_INVALID: return MA_INVALID_ARGS; - case SL_RESULT_MEMORY_FAILURE: return MA_OUT_OF_MEMORY; - case SL_RESULT_RESOURCE_ERROR: return MA_INVALID_DATA; - case SL_RESULT_RESOURCE_LOST: return MA_ERROR; - case SL_RESULT_IO_ERROR: return MA_IO_ERROR; - case SL_RESULT_BUFFER_INSUFFICIENT: return MA_NO_SPACE; - case SL_RESULT_CONTENT_CORRUPTED: return MA_INVALID_DATA; - case SL_RESULT_CONTENT_UNSUPPORTED: return MA_FORMAT_NOT_SUPPORTED; - case SL_RESULT_CONTENT_NOT_FOUND: return MA_ERROR; - case SL_RESULT_PERMISSION_DENIED: return MA_ACCESS_DENIED; - case SL_RESULT_FEATURE_UNSUPPORTED: return MA_NOT_IMPLEMENTED; - case SL_RESULT_INTERNAL_ERROR: return MA_ERROR; - case SL_RESULT_UNKNOWN_ERROR: return MA_ERROR; - case SL_RESULT_OPERATION_ABORTED: return MA_ERROR; - case SL_RESULT_CONTROL_LOST: return MA_ERROR; - default: return MA_ERROR; - } -} - -/* Converts an individual OpenSL-style channel identifier (SL_SPEAKER_FRONT_LEFT, etc.) to miniaudio. */ -static ma_uint8 ma_channel_id_to_ma__opensl(SLuint32 id) -{ - switch (id) - { - case SL_SPEAKER_FRONT_LEFT: return MA_CHANNEL_FRONT_LEFT; - case SL_SPEAKER_FRONT_RIGHT: return MA_CHANNEL_FRONT_RIGHT; - case SL_SPEAKER_FRONT_CENTER: return MA_CHANNEL_FRONT_CENTER; - case SL_SPEAKER_LOW_FREQUENCY: return MA_CHANNEL_LFE; - case SL_SPEAKER_BACK_LEFT: return MA_CHANNEL_BACK_LEFT; - case SL_SPEAKER_BACK_RIGHT: return MA_CHANNEL_BACK_RIGHT; - case SL_SPEAKER_FRONT_LEFT_OF_CENTER: return MA_CHANNEL_FRONT_LEFT_CENTER; - case SL_SPEAKER_FRONT_RIGHT_OF_CENTER: return MA_CHANNEL_FRONT_RIGHT_CENTER; - case SL_SPEAKER_BACK_CENTER: return MA_CHANNEL_BACK_CENTER; - case SL_SPEAKER_SIDE_LEFT: return MA_CHANNEL_SIDE_LEFT; - case SL_SPEAKER_SIDE_RIGHT: return MA_CHANNEL_SIDE_RIGHT; - case SL_SPEAKER_TOP_CENTER: return MA_CHANNEL_TOP_CENTER; - case SL_SPEAKER_TOP_FRONT_LEFT: return MA_CHANNEL_TOP_FRONT_LEFT; - case SL_SPEAKER_TOP_FRONT_CENTER: return MA_CHANNEL_TOP_FRONT_CENTER; - case SL_SPEAKER_TOP_FRONT_RIGHT: return MA_CHANNEL_TOP_FRONT_RIGHT; - case SL_SPEAKER_TOP_BACK_LEFT: return MA_CHANNEL_TOP_BACK_LEFT; - case SL_SPEAKER_TOP_BACK_CENTER: return MA_CHANNEL_TOP_BACK_CENTER; - case SL_SPEAKER_TOP_BACK_RIGHT: return MA_CHANNEL_TOP_BACK_RIGHT; - default: return 0; - } -} - -/* Converts an individual miniaudio channel identifier (MA_CHANNEL_FRONT_LEFT, etc.) to OpenSL-style. */ -static SLuint32 ma_channel_id_to_opensl(ma_uint8 id) -{ - switch (id) - { - case MA_CHANNEL_MONO: return SL_SPEAKER_FRONT_CENTER; - case MA_CHANNEL_FRONT_LEFT: return SL_SPEAKER_FRONT_LEFT; - case MA_CHANNEL_FRONT_RIGHT: return SL_SPEAKER_FRONT_RIGHT; - case MA_CHANNEL_FRONT_CENTER: return SL_SPEAKER_FRONT_CENTER; - case MA_CHANNEL_LFE: return SL_SPEAKER_LOW_FREQUENCY; - case MA_CHANNEL_BACK_LEFT: return SL_SPEAKER_BACK_LEFT; - case MA_CHANNEL_BACK_RIGHT: return SL_SPEAKER_BACK_RIGHT; - case MA_CHANNEL_FRONT_LEFT_CENTER: return SL_SPEAKER_FRONT_LEFT_OF_CENTER; - case MA_CHANNEL_FRONT_RIGHT_CENTER: return SL_SPEAKER_FRONT_RIGHT_OF_CENTER; - case MA_CHANNEL_BACK_CENTER: return SL_SPEAKER_BACK_CENTER; - case MA_CHANNEL_SIDE_LEFT: return SL_SPEAKER_SIDE_LEFT; - case MA_CHANNEL_SIDE_RIGHT: return SL_SPEAKER_SIDE_RIGHT; - case MA_CHANNEL_TOP_CENTER: return SL_SPEAKER_TOP_CENTER; - case MA_CHANNEL_TOP_FRONT_LEFT: return SL_SPEAKER_TOP_FRONT_LEFT; - case MA_CHANNEL_TOP_FRONT_CENTER: return SL_SPEAKER_TOP_FRONT_CENTER; - case MA_CHANNEL_TOP_FRONT_RIGHT: return SL_SPEAKER_TOP_FRONT_RIGHT; - case MA_CHANNEL_TOP_BACK_LEFT: return SL_SPEAKER_TOP_BACK_LEFT; - case MA_CHANNEL_TOP_BACK_CENTER: return SL_SPEAKER_TOP_BACK_CENTER; - case MA_CHANNEL_TOP_BACK_RIGHT: return SL_SPEAKER_TOP_BACK_RIGHT; - default: return 0; - } -} - -/* Converts a channel mapping to an OpenSL-style channel mask. */ -static SLuint32 ma_channel_map_to_channel_mask__opensl(const ma_channel* pChannelMap, ma_uint32 channels) -{ - SLuint32 channelMask = 0; - ma_uint32 iChannel; - for (iChannel = 0; iChannel < channels; ++iChannel) { - channelMask |= ma_channel_id_to_opensl(pChannelMap[iChannel]); - } - - return channelMask; -} - -/* Converts an OpenSL-style channel mask to a miniaudio channel map. */ -static void ma_channel_mask_to_channel_map__opensl(SLuint32 channelMask, ma_uint32 channels, ma_channel* pChannelMap) -{ - if (channels == 1 && channelMask == 0) { - pChannelMap[0] = MA_CHANNEL_MONO; - } else if (channels == 2 && channelMask == 0) { - pChannelMap[0] = MA_CHANNEL_FRONT_LEFT; - pChannelMap[1] = MA_CHANNEL_FRONT_RIGHT; - } else { - if (channels == 1 && (channelMask & SL_SPEAKER_FRONT_CENTER) != 0) { - pChannelMap[0] = MA_CHANNEL_MONO; - } else { - /* Just iterate over each bit. */ - ma_uint32 iChannel = 0; - ma_uint32 iBit; - for (iBit = 0; iBit < 32 && iChannel < channels; ++iBit) { - SLuint32 bitValue = (channelMask & (1UL << iBit)); - if (bitValue != 0) { - /* The bit is set. */ - pChannelMap[iChannel] = ma_channel_id_to_ma__opensl(bitValue); - iChannel += 1; - } - } - } - } -} - -static SLuint32 ma_round_to_standard_sample_rate__opensl(SLuint32 samplesPerSec) -{ - if (samplesPerSec <= SL_SAMPLINGRATE_8) { - return SL_SAMPLINGRATE_8; - } - if (samplesPerSec <= SL_SAMPLINGRATE_11_025) { - return SL_SAMPLINGRATE_11_025; - } - if (samplesPerSec <= SL_SAMPLINGRATE_12) { - return SL_SAMPLINGRATE_12; - } - if (samplesPerSec <= SL_SAMPLINGRATE_16) { - return SL_SAMPLINGRATE_16; - } - if (samplesPerSec <= SL_SAMPLINGRATE_22_05) { - return SL_SAMPLINGRATE_22_05; - } - if (samplesPerSec <= SL_SAMPLINGRATE_24) { - return SL_SAMPLINGRATE_24; - } - if (samplesPerSec <= SL_SAMPLINGRATE_32) { - return SL_SAMPLINGRATE_32; - } - if (samplesPerSec <= SL_SAMPLINGRATE_44_1) { - return SL_SAMPLINGRATE_44_1; - } - if (samplesPerSec <= SL_SAMPLINGRATE_48) { - return SL_SAMPLINGRATE_48; - } - - /* Android doesn't support more than 48000. */ -#ifndef MA_ANDROID - if (samplesPerSec <= SL_SAMPLINGRATE_64) { - return SL_SAMPLINGRATE_64; - } - if (samplesPerSec <= SL_SAMPLINGRATE_88_2) { - return SL_SAMPLINGRATE_88_2; - } - if (samplesPerSec <= SL_SAMPLINGRATE_96) { - return SL_SAMPLINGRATE_96; - } - if (samplesPerSec <= SL_SAMPLINGRATE_192) { - return SL_SAMPLINGRATE_192; - } -#endif - - return SL_SAMPLINGRATE_16; -} - - -static SLint32 ma_to_stream_type__opensl(ma_opensl_stream_type streamType) -{ - switch (streamType) { - case ma_opensl_stream_type_voice: return SL_ANDROID_STREAM_VOICE; - case ma_opensl_stream_type_system: return SL_ANDROID_STREAM_SYSTEM; - case ma_opensl_stream_type_ring: return SL_ANDROID_STREAM_RING; - case ma_opensl_stream_type_media: return SL_ANDROID_STREAM_MEDIA; - case ma_opensl_stream_type_alarm: return SL_ANDROID_STREAM_ALARM; - case ma_opensl_stream_type_notification: return SL_ANDROID_STREAM_NOTIFICATION; - default: break; - } - - return SL_ANDROID_STREAM_VOICE; -} - -static SLint32 ma_to_recording_preset__opensl(ma_opensl_recording_preset recordingPreset) -{ - switch (recordingPreset) { - case ma_opensl_recording_preset_generic: return SL_ANDROID_RECORDING_PRESET_GENERIC; - case ma_opensl_recording_preset_camcorder: return SL_ANDROID_RECORDING_PRESET_CAMCORDER; - case ma_opensl_recording_preset_voice_recognition: return SL_ANDROID_RECORDING_PRESET_VOICE_RECOGNITION; - case ma_opensl_recording_preset_voice_communication: return SL_ANDROID_RECORDING_PRESET_VOICE_COMMUNICATION; - case ma_opensl_recording_preset_voice_unprocessed: return SL_ANDROID_RECORDING_PRESET_UNPROCESSED; - default: break; - } - - return SL_ANDROID_RECORDING_PRESET_NONE; -} - - -static ma_result ma_context_enumerate_devices__opensl(ma_context* pContext, ma_enum_devices_callback_proc callback, void* pUserData) -{ - ma_bool32 cbResult; - - MA_ASSERT(pContext != NULL); - MA_ASSERT(callback != NULL); - - MA_ASSERT(g_maOpenSLInitCounter > 0); /* <-- If you trigger this it means you've either not initialized the context, or you've uninitialized it and then attempted to enumerate devices. */ - if (g_maOpenSLInitCounter == 0) { - return MA_INVALID_OPERATION; - } - - /* - TODO: Test Me. - - This is currently untested, so for now we are just returning default devices. - */ -#if 0 && !defined(MA_ANDROID) - ma_bool32 isTerminated = MA_FALSE; - - SLuint32 pDeviceIDs[128]; - SLint32 deviceCount = sizeof(pDeviceIDs) / sizeof(pDeviceIDs[0]); - - SLAudioIODeviceCapabilitiesItf deviceCaps; - SLresult resultSL = (*g_maEngineObjectSL)->GetInterface(g_maEngineObjectSL, (SLInterfaceID)pContext->opensl.SL_IID_AUDIOIODEVICECAPABILITIES, &deviceCaps); - if (resultSL != SL_RESULT_SUCCESS) { - /* The interface may not be supported so just report a default device. */ - goto return_default_device; - } - - /* Playback */ - if (!isTerminated) { - resultSL = (*deviceCaps)->GetAvailableAudioOutputs(deviceCaps, &deviceCount, pDeviceIDs); - if (resultSL != SL_RESULT_SUCCESS) { - return ma_result_from_OpenSL(resultSL); - } - - for (SLint32 iDevice = 0; iDevice < deviceCount; ++iDevice) { - ma_device_info deviceInfo; - MA_ZERO_OBJECT(&deviceInfo); - deviceInfo.id.opensl = pDeviceIDs[iDevice]; - - SLAudioOutputDescriptor desc; - resultSL = (*deviceCaps)->QueryAudioOutputCapabilities(deviceCaps, deviceInfo.id.opensl, &desc); - if (resultSL == SL_RESULT_SUCCESS) { - ma_strncpy_s(deviceInfo.name, sizeof(deviceInfo.name), (const char*)desc.pDeviceName, (size_t)-1); - - ma_bool32 cbResult = callback(pContext, ma_device_type_playback, &deviceInfo, pUserData); - if (cbResult == MA_FALSE) { - isTerminated = MA_TRUE; - break; - } - } - } - } - - /* Capture */ - if (!isTerminated) { - resultSL = (*deviceCaps)->GetAvailableAudioInputs(deviceCaps, &deviceCount, pDeviceIDs); - if (resultSL != SL_RESULT_SUCCESS) { - return ma_result_from_OpenSL(resultSL); - } - - for (SLint32 iDevice = 0; iDevice < deviceCount; ++iDevice) { - ma_device_info deviceInfo; - MA_ZERO_OBJECT(&deviceInfo); - deviceInfo.id.opensl = pDeviceIDs[iDevice]; - - SLAudioInputDescriptor desc; - resultSL = (*deviceCaps)->QueryAudioInputCapabilities(deviceCaps, deviceInfo.id.opensl, &desc); - if (resultSL == SL_RESULT_SUCCESS) { - ma_strncpy_s(deviceInfo.name, sizeof(deviceInfo.name), (const char*)desc.deviceName, (size_t)-1); - - ma_bool32 cbResult = callback(pContext, ma_device_type_capture, &deviceInfo, pUserData); - if (cbResult == MA_FALSE) { - isTerminated = MA_TRUE; - break; - } - } - } - } - - return MA_SUCCESS; -#else - goto return_default_device; -#endif - -return_default_device:; - cbResult = MA_TRUE; - - /* Playback. */ - if (cbResult) { - ma_device_info deviceInfo; - MA_ZERO_OBJECT(&deviceInfo); - deviceInfo.id.opensl = SL_DEFAULTDEVICEID_AUDIOOUTPUT; - ma_strncpy_s(deviceInfo.name, sizeof(deviceInfo.name), MA_DEFAULT_PLAYBACK_DEVICE_NAME, (size_t)-1); - cbResult = callback(pContext, ma_device_type_playback, &deviceInfo, pUserData); - } - - /* Capture. */ - if (cbResult) { - ma_device_info deviceInfo; - MA_ZERO_OBJECT(&deviceInfo); - deviceInfo.id.opensl = SL_DEFAULTDEVICEID_AUDIOINPUT; - ma_strncpy_s(deviceInfo.name, sizeof(deviceInfo.name), MA_DEFAULT_CAPTURE_DEVICE_NAME, (size_t)-1); - cbResult = callback(pContext, ma_device_type_capture, &deviceInfo, pUserData); - } - - return MA_SUCCESS; -} - -static void ma_context_add_data_format_ex__opensl(ma_context* pContext, ma_format format, ma_uint32 channels, ma_uint32 sampleRate, ma_device_info* pDeviceInfo) -{ - MA_ASSERT(pContext != NULL); - MA_ASSERT(pDeviceInfo != NULL); - - pDeviceInfo->nativeDataFormats[pDeviceInfo->nativeDataFormatCount].format = format; - pDeviceInfo->nativeDataFormats[pDeviceInfo->nativeDataFormatCount].channels = channels; - pDeviceInfo->nativeDataFormats[pDeviceInfo->nativeDataFormatCount].sampleRate = sampleRate; - pDeviceInfo->nativeDataFormats[pDeviceInfo->nativeDataFormatCount].flags = 0; - pDeviceInfo->nativeDataFormatCount += 1; -} - -static void ma_context_add_data_format__opensl(ma_context* pContext, ma_format format, ma_device_info* pDeviceInfo) -{ - ma_uint32 minChannels = 1; - ma_uint32 maxChannels = 2; - ma_uint32 minSampleRate = (ma_uint32)ma_standard_sample_rate_8000; - ma_uint32 maxSampleRate = (ma_uint32)ma_standard_sample_rate_48000; - ma_uint32 iChannel; - ma_uint32 iSampleRate; - - MA_ASSERT(pContext != NULL); - MA_ASSERT(pDeviceInfo != NULL); - - /* - Each sample format can support mono and stereo, and we'll support a small subset of standard - rates (up to 48000). A better solution would be to somehow find a native sample rate. - */ - for (iChannel = minChannels; iChannel < maxChannels; iChannel += 1) { - for (iSampleRate = 0; iSampleRate < ma_countof(g_maStandardSampleRatePriorities); iSampleRate += 1) { - ma_uint32 standardSampleRate = g_maStandardSampleRatePriorities[iSampleRate]; - if (standardSampleRate >= minSampleRate && standardSampleRate <= maxSampleRate) { - ma_context_add_data_format_ex__opensl(pContext, format, iChannel, standardSampleRate, pDeviceInfo); - } - } - } -} - -static ma_result ma_context_get_device_info__opensl(ma_context* pContext, ma_device_type deviceType, const ma_device_id* pDeviceID, ma_device_info* pDeviceInfo) -{ - MA_ASSERT(pContext != NULL); - - MA_ASSERT(g_maOpenSLInitCounter > 0); /* <-- If you trigger this it means you've either not initialized the context, or you've uninitialized it and then attempted to get device info. */ - if (g_maOpenSLInitCounter == 0) { - return MA_INVALID_OPERATION; - } - - /* - TODO: Test Me. - - This is currently untested, so for now we are just returning default devices. - */ -#if 0 && !defined(MA_ANDROID) - SLAudioIODeviceCapabilitiesItf deviceCaps; - SLresult resultSL = (*g_maEngineObjectSL)->GetInterface(g_maEngineObjectSL, (SLInterfaceID)pContext->opensl.SL_IID_AUDIOIODEVICECAPABILITIES, &deviceCaps); - if (resultSL != SL_RESULT_SUCCESS) { - /* The interface may not be supported so just report a default device. */ - goto return_default_device; - } - - if (deviceType == ma_device_type_playback) { - SLAudioOutputDescriptor desc; - resultSL = (*deviceCaps)->QueryAudioOutputCapabilities(deviceCaps, pDeviceID->opensl, &desc); - if (resultSL != SL_RESULT_SUCCESS) { - return ma_result_from_OpenSL(resultSL); - } - - ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), (const char*)desc.pDeviceName, (size_t)-1); - } else { - SLAudioInputDescriptor desc; - resultSL = (*deviceCaps)->QueryAudioInputCapabilities(deviceCaps, pDeviceID->opensl, &desc); - if (resultSL != SL_RESULT_SUCCESS) { - return ma_result_from_OpenSL(resultSL); - } - - ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), (const char*)desc.deviceName, (size_t)-1); - } - - goto return_detailed_info; -#else - goto return_default_device; -#endif - -return_default_device: - if (pDeviceID != NULL) { - if ((deviceType == ma_device_type_playback && pDeviceID->opensl != SL_DEFAULTDEVICEID_AUDIOOUTPUT) || - (deviceType == ma_device_type_capture && pDeviceID->opensl != SL_DEFAULTDEVICEID_AUDIOINPUT)) { - return MA_NO_DEVICE; /* Don't know the device. */ - } - } - - /* ID and Name / Description */ - if (deviceType == ma_device_type_playback) { - pDeviceInfo->id.opensl = SL_DEFAULTDEVICEID_AUDIOOUTPUT; - ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), MA_DEFAULT_PLAYBACK_DEVICE_NAME, (size_t)-1); - } else { - pDeviceInfo->id.opensl = SL_DEFAULTDEVICEID_AUDIOINPUT; - ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), MA_DEFAULT_CAPTURE_DEVICE_NAME, (size_t)-1); - } - - pDeviceInfo->isDefault = MA_TRUE; - - goto return_detailed_info; - - -return_detailed_info: - - /* - For now we're just outputting a set of values that are supported by the API but not necessarily supported - by the device natively. Later on we should work on this so that it more closely reflects the device's - actual native format. - */ - pDeviceInfo->nativeDataFormatCount = 0; -#if defined(MA_ANDROID) && __ANDROID_API__ >= 21 - ma_context_add_data_format__opensl(pContext, ma_format_f32, pDeviceInfo); -#endif - ma_context_add_data_format__opensl(pContext, ma_format_s16, pDeviceInfo); - ma_context_add_data_format__opensl(pContext, ma_format_u8, pDeviceInfo); - - return MA_SUCCESS; -} - - -#ifdef MA_ANDROID -/*void ma_buffer_queue_callback_capture__opensl_android(SLAndroidSimpleBufferQueueItf pBufferQueue, SLuint32 eventFlags, const void* pBuffer, SLuint32 bufferSize, SLuint32 dataUsed, void* pContext)*/ -static void ma_buffer_queue_callback_capture__opensl_android(SLAndroidSimpleBufferQueueItf pBufferQueue, void* pUserData) -{ - ma_device* pDevice = (ma_device*)pUserData; - size_t periodSizeInBytes; - ma_uint8* pBuffer; - SLresult resultSL; - - MA_ASSERT(pDevice != NULL); - - (void)pBufferQueue; - - /* - For now, don't do anything unless the buffer was fully processed. From what I can tell, it looks like - OpenSL|ES 1.1 improves on buffer queues to the point that we could much more intelligently handle this, - but unfortunately it looks like Android is only supporting OpenSL|ES 1.0.1 for now :( - */ - - /* Don't do anything if the device is not started. */ - if (ma_device_get_state(pDevice) != ma_device_state_started) { - return; - } - - /* Don't do anything if the device is being drained. */ - if (pDevice->opensl.isDrainingCapture) { - return; - } - - periodSizeInBytes = pDevice->capture.internalPeriodSizeInFrames * ma_get_bytes_per_frame(pDevice->capture.internalFormat, pDevice->capture.internalChannels); - pBuffer = pDevice->opensl.pBufferCapture + (pDevice->opensl.currentBufferIndexCapture * periodSizeInBytes); - - ma_device_handle_backend_data_callback(pDevice, NULL, pBuffer, pDevice->capture.internalPeriodSizeInFrames); - - resultSL = MA_OPENSL_BUFFERQUEUE(pDevice->opensl.pBufferQueueCapture)->Enqueue((SLAndroidSimpleBufferQueueItf)pDevice->opensl.pBufferQueueCapture, pBuffer, periodSizeInBytes); - if (resultSL != SL_RESULT_SUCCESS) { - return; - } - - pDevice->opensl.currentBufferIndexCapture = (pDevice->opensl.currentBufferIndexCapture + 1) % pDevice->capture.internalPeriods; -} - -static void ma_buffer_queue_callback_playback__opensl_android(SLAndroidSimpleBufferQueueItf pBufferQueue, void* pUserData) -{ - ma_device* pDevice = (ma_device*)pUserData; - size_t periodSizeInBytes; - ma_uint8* pBuffer; - SLresult resultSL; - - MA_ASSERT(pDevice != NULL); - - (void)pBufferQueue; - - /* Don't do anything if the device is not started. */ - if (ma_device_get_state(pDevice) != ma_device_state_started) { - return; - } - - /* Don't do anything if the device is being drained. */ - if (pDevice->opensl.isDrainingPlayback) { - return; - } - - periodSizeInBytes = pDevice->playback.internalPeriodSizeInFrames * ma_get_bytes_per_frame(pDevice->playback.internalFormat, pDevice->playback.internalChannels); - pBuffer = pDevice->opensl.pBufferPlayback + (pDevice->opensl.currentBufferIndexPlayback * periodSizeInBytes); - - ma_device_handle_backend_data_callback(pDevice, pBuffer, NULL, pDevice->playback.internalPeriodSizeInFrames); - - resultSL = MA_OPENSL_BUFFERQUEUE(pDevice->opensl.pBufferQueuePlayback)->Enqueue((SLAndroidSimpleBufferQueueItf)pDevice->opensl.pBufferQueuePlayback, pBuffer, periodSizeInBytes); - if (resultSL != SL_RESULT_SUCCESS) { - return; - } - - pDevice->opensl.currentBufferIndexPlayback = (pDevice->opensl.currentBufferIndexPlayback + 1) % pDevice->playback.internalPeriods; -} -#endif - -static ma_result ma_device_uninit__opensl(ma_device* pDevice) -{ - MA_ASSERT(pDevice != NULL); - - MA_ASSERT(g_maOpenSLInitCounter > 0); /* <-- If you trigger this it means you've either not initialized the context, or you've uninitialized it before uninitializing the device. */ - if (g_maOpenSLInitCounter == 0) { - return MA_INVALID_OPERATION; - } - - if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) { - if (pDevice->opensl.pAudioRecorderObj) { - MA_OPENSL_OBJ(pDevice->opensl.pAudioRecorderObj)->Destroy((SLObjectItf)pDevice->opensl.pAudioRecorderObj); - } - - ma_free(pDevice->opensl.pBufferCapture, &pDevice->pContext->allocationCallbacks); - } - - if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) { - if (pDevice->opensl.pAudioPlayerObj) { - MA_OPENSL_OBJ(pDevice->opensl.pAudioPlayerObj)->Destroy((SLObjectItf)pDevice->opensl.pAudioPlayerObj); - } - if (pDevice->opensl.pOutputMixObj) { - MA_OPENSL_OBJ(pDevice->opensl.pOutputMixObj)->Destroy((SLObjectItf)pDevice->opensl.pOutputMixObj); - } - - ma_free(pDevice->opensl.pBufferPlayback, &pDevice->pContext->allocationCallbacks); - } - - return MA_SUCCESS; -} - -#if defined(MA_ANDROID) && __ANDROID_API__ >= 21 -typedef SLAndroidDataFormat_PCM_EX ma_SLDataFormat_PCM; -#else -typedef SLDataFormat_PCM ma_SLDataFormat_PCM; -#endif - -static ma_result ma_SLDataFormat_PCM_init__opensl(ma_format format, ma_uint32 channels, ma_uint32 sampleRate, const ma_channel* channelMap, ma_SLDataFormat_PCM* pDataFormat) -{ - /* We need to convert our format/channels/rate so that they aren't set to default. */ - if (format == ma_format_unknown) { - format = MA_DEFAULT_FORMAT; - } - if (channels == 0) { - channels = MA_DEFAULT_CHANNELS; - } - if (sampleRate == 0) { - sampleRate = MA_DEFAULT_SAMPLE_RATE; - } - -#if defined(MA_ANDROID) && __ANDROID_API__ >= 21 - if (format == ma_format_f32) { - pDataFormat->formatType = SL_ANDROID_DATAFORMAT_PCM_EX; - pDataFormat->representation = SL_ANDROID_PCM_REPRESENTATION_FLOAT; - } else { - pDataFormat->formatType = SL_DATAFORMAT_PCM; - } -#else - pDataFormat->formatType = SL_DATAFORMAT_PCM; -#endif - - pDataFormat->numChannels = channels; - ((SLDataFormat_PCM*)pDataFormat)->samplesPerSec = ma_round_to_standard_sample_rate__opensl(sampleRate * 1000); /* In millihertz. Annoyingly, the sample rate variable is named differently between SLAndroidDataFormat_PCM_EX and SLDataFormat_PCM */ - pDataFormat->bitsPerSample = ma_get_bytes_per_sample(format) * 8; - pDataFormat->channelMask = ma_channel_map_to_channel_mask__opensl(channelMap, channels); - pDataFormat->endianness = (ma_is_little_endian()) ? SL_BYTEORDER_LITTLEENDIAN : SL_BYTEORDER_BIGENDIAN; - - /* - Android has a few restrictions on the format as documented here: https://developer.android.com/ndk/guides/audio/opensl-for-android.html - - Only mono and stereo is supported. - - Only u8 and s16 formats are supported. - - Maximum sample rate of 48000. - */ -#ifdef MA_ANDROID - if (pDataFormat->numChannels > 2) { - pDataFormat->numChannels = 2; - } -#if __ANDROID_API__ >= 21 - if (pDataFormat->formatType == SL_ANDROID_DATAFORMAT_PCM_EX) { - /* It's floating point. */ - MA_ASSERT(pDataFormat->representation == SL_ANDROID_PCM_REPRESENTATION_FLOAT); - if (pDataFormat->bitsPerSample > 32) { - pDataFormat->bitsPerSample = 32; - } - } else { - if (pDataFormat->bitsPerSample > 16) { - pDataFormat->bitsPerSample = 16; - } - } -#else - if (pDataFormat->bitsPerSample > 16) { - pDataFormat->bitsPerSample = 16; - } -#endif - if (((SLDataFormat_PCM*)pDataFormat)->samplesPerSec > SL_SAMPLINGRATE_48) { - ((SLDataFormat_PCM*)pDataFormat)->samplesPerSec = SL_SAMPLINGRATE_48; - } -#endif - - pDataFormat->containerSize = pDataFormat->bitsPerSample; /* Always tightly packed for now. */ - - return MA_SUCCESS; -} - -static ma_result ma_deconstruct_SLDataFormat_PCM__opensl(ma_SLDataFormat_PCM* pDataFormat, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate, ma_channel* pChannelMap, size_t channelMapCap) -{ - ma_bool32 isFloatingPoint = MA_FALSE; -#if defined(MA_ANDROID) && __ANDROID_API__ >= 21 - if (pDataFormat->formatType == SL_ANDROID_DATAFORMAT_PCM_EX) { - MA_ASSERT(pDataFormat->representation == SL_ANDROID_PCM_REPRESENTATION_FLOAT); - isFloatingPoint = MA_TRUE; - } -#endif - if (isFloatingPoint) { - if (pDataFormat->bitsPerSample == 32) { - *pFormat = ma_format_f32; - } - } else { - if (pDataFormat->bitsPerSample == 8) { - *pFormat = ma_format_u8; - } else if (pDataFormat->bitsPerSample == 16) { - *pFormat = ma_format_s16; - } else if (pDataFormat->bitsPerSample == 24) { - *pFormat = ma_format_s24; - } else if (pDataFormat->bitsPerSample == 32) { - *pFormat = ma_format_s32; - } - } - - *pChannels = pDataFormat->numChannels; - *pSampleRate = ((SLDataFormat_PCM*)pDataFormat)->samplesPerSec / 1000; - ma_channel_mask_to_channel_map__opensl(pDataFormat->channelMask, ma_min(pDataFormat->numChannels, channelMapCap), pChannelMap); - - return MA_SUCCESS; -} - -static ma_result ma_device_init__opensl(ma_device* pDevice, const ma_device_config* pConfig, ma_device_descriptor* pDescriptorPlayback, ma_device_descriptor* pDescriptorCapture) -{ -#ifdef MA_ANDROID - SLDataLocator_AndroidSimpleBufferQueue queue; - SLresult resultSL; - size_t bufferSizeInBytes; - SLInterfaceID itfIDs[2]; - const SLboolean itfIDsRequired[] = { - SL_BOOLEAN_TRUE, /* SL_IID_ANDROIDSIMPLEBUFFERQUEUE */ - SL_BOOLEAN_FALSE /* SL_IID_ANDROIDCONFIGURATION */ - }; -#endif - - MA_ASSERT(g_maOpenSLInitCounter > 0); /* <-- If you trigger this it means you've either not initialized the context, or you've uninitialized it and then attempted to initialize a new device. */ - if (g_maOpenSLInitCounter == 0) { - return MA_INVALID_OPERATION; - } - - if (pConfig->deviceType == ma_device_type_loopback) { - return MA_DEVICE_TYPE_NOT_SUPPORTED; - } - - /* - For now, only supporting Android implementations of OpenSL|ES since that's the only one I've - been able to test with and I currently depend on Android-specific extensions (simple buffer - queues). - */ -#ifdef MA_ANDROID - itfIDs[0] = (SLInterfaceID)pDevice->pContext->opensl.SL_IID_ANDROIDSIMPLEBUFFERQUEUE; - itfIDs[1] = (SLInterfaceID)pDevice->pContext->opensl.SL_IID_ANDROIDCONFIGURATION; - - /* No exclusive mode with OpenSL|ES. */ - if (((pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) && pDescriptorPlayback->shareMode == ma_share_mode_exclusive) || - ((pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) && pDescriptorCapture->shareMode == ma_share_mode_exclusive)) { - return MA_SHARE_MODE_NOT_SUPPORTED; - } - - /* Now we can start initializing the device properly. */ - MA_ASSERT(pDevice != NULL); - MA_ZERO_OBJECT(&pDevice->opensl); - - queue.locatorType = SL_DATALOCATOR_ANDROIDSIMPLEBUFFERQUEUE; - - if (pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) { - ma_SLDataFormat_PCM pcm; - SLDataLocator_IODevice locatorDevice; - SLDataSource source; - SLDataSink sink; - SLAndroidConfigurationItf pRecorderConfig; - - ma_SLDataFormat_PCM_init__opensl(pDescriptorCapture->format, pDescriptorCapture->channels, pDescriptorCapture->sampleRate, pDescriptorCapture->channelMap, &pcm); - - locatorDevice.locatorType = SL_DATALOCATOR_IODEVICE; - locatorDevice.deviceType = SL_IODEVICE_AUDIOINPUT; - locatorDevice.deviceID = SL_DEFAULTDEVICEID_AUDIOINPUT; /* Must always use the default device with Android. */ - locatorDevice.device = NULL; - - source.pLocator = &locatorDevice; - source.pFormat = NULL; - - queue.numBuffers = pDescriptorCapture->periodCount; - - sink.pLocator = &queue; - sink.pFormat = (SLDataFormat_PCM*)&pcm; - - resultSL = (*g_maEngineSL)->CreateAudioRecorder(g_maEngineSL, (SLObjectItf*)&pDevice->opensl.pAudioRecorderObj, &source, &sink, ma_countof(itfIDs), itfIDs, itfIDsRequired); - if (resultSL == SL_RESULT_CONTENT_UNSUPPORTED || resultSL == SL_RESULT_PARAMETER_INVALID) { - /* Unsupported format. Fall back to something safer and try again. If this fails, just abort. */ - pcm.formatType = SL_DATAFORMAT_PCM; - pcm.numChannels = 1; - ((SLDataFormat_PCM*)&pcm)->samplesPerSec = SL_SAMPLINGRATE_16; /* The name of the sample rate variable is different between SLAndroidDataFormat_PCM_EX and SLDataFormat_PCM. */ - pcm.bitsPerSample = 16; - pcm.containerSize = pcm.bitsPerSample; /* Always tightly packed for now. */ - pcm.channelMask = 0; - resultSL = (*g_maEngineSL)->CreateAudioRecorder(g_maEngineSL, (SLObjectItf*)&pDevice->opensl.pAudioRecorderObj, &source, &sink, ma_countof(itfIDs), itfIDs, itfIDsRequired); - } - - if (resultSL != SL_RESULT_SUCCESS) { - ma_device_uninit__opensl(pDevice); - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[OpenSL] Failed to create audio recorder."); - return ma_result_from_OpenSL(resultSL); - } - - - /* Set the recording preset before realizing the player. */ - if (pConfig->opensl.recordingPreset != ma_opensl_recording_preset_default) { - resultSL = MA_OPENSL_OBJ(pDevice->opensl.pAudioRecorderObj)->GetInterface((SLObjectItf)pDevice->opensl.pAudioRecorderObj, (SLInterfaceID)pDevice->pContext->opensl.SL_IID_ANDROIDCONFIGURATION, &pRecorderConfig); - if (resultSL == SL_RESULT_SUCCESS) { - SLint32 recordingPreset = ma_to_recording_preset__opensl(pConfig->opensl.recordingPreset); - resultSL = (*pRecorderConfig)->SetConfiguration(pRecorderConfig, SL_ANDROID_KEY_RECORDING_PRESET, &recordingPreset, sizeof(SLint32)); - if (resultSL != SL_RESULT_SUCCESS) { - /* Failed to set the configuration. Just keep going. */ - } - } - } - - resultSL = MA_OPENSL_OBJ(pDevice->opensl.pAudioRecorderObj)->Realize((SLObjectItf)pDevice->opensl.pAudioRecorderObj, SL_BOOLEAN_FALSE); - if (resultSL != SL_RESULT_SUCCESS) { - ma_device_uninit__opensl(pDevice); - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[OpenSL] Failed to realize audio recorder."); - return ma_result_from_OpenSL(resultSL); - } - - resultSL = MA_OPENSL_OBJ(pDevice->opensl.pAudioRecorderObj)->GetInterface((SLObjectItf)pDevice->opensl.pAudioRecorderObj, (SLInterfaceID)pDevice->pContext->opensl.SL_IID_RECORD, &pDevice->opensl.pAudioRecorder); - if (resultSL != SL_RESULT_SUCCESS) { - ma_device_uninit__opensl(pDevice); - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[OpenSL] Failed to retrieve SL_IID_RECORD interface."); - return ma_result_from_OpenSL(resultSL); - } - - resultSL = MA_OPENSL_OBJ(pDevice->opensl.pAudioRecorderObj)->GetInterface((SLObjectItf)pDevice->opensl.pAudioRecorderObj, (SLInterfaceID)pDevice->pContext->opensl.SL_IID_ANDROIDSIMPLEBUFFERQUEUE, &pDevice->opensl.pBufferQueueCapture); - if (resultSL != SL_RESULT_SUCCESS) { - ma_device_uninit__opensl(pDevice); - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[OpenSL] Failed to retrieve SL_IID_ANDROIDSIMPLEBUFFERQUEUE interface."); - return ma_result_from_OpenSL(resultSL); - } - - resultSL = MA_OPENSL_BUFFERQUEUE(pDevice->opensl.pBufferQueueCapture)->RegisterCallback((SLAndroidSimpleBufferQueueItf)pDevice->opensl.pBufferQueueCapture, ma_buffer_queue_callback_capture__opensl_android, pDevice); - if (resultSL != SL_RESULT_SUCCESS) { - ma_device_uninit__opensl(pDevice); - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[OpenSL] Failed to register buffer queue callback."); - return ma_result_from_OpenSL(resultSL); - } - - /* The internal format is determined by the "pcm" object. */ - ma_deconstruct_SLDataFormat_PCM__opensl(&pcm, &pDescriptorCapture->format, &pDescriptorCapture->channels, &pDescriptorCapture->sampleRate, pDescriptorCapture->channelMap, ma_countof(pDescriptorCapture->channelMap)); - - /* Buffer. */ - pDescriptorCapture->periodSizeInFrames = ma_calculate_buffer_size_in_frames_from_descriptor(pDescriptorCapture, pDescriptorCapture->sampleRate, pConfig->performanceProfile); - pDevice->opensl.currentBufferIndexCapture = 0; - - bufferSizeInBytes = pDescriptorCapture->periodSizeInFrames * ma_get_bytes_per_frame(pDescriptorCapture->format, pDescriptorCapture->channels) * pDescriptorCapture->periodCount; - pDevice->opensl.pBufferCapture = (ma_uint8*)ma_calloc(bufferSizeInBytes, &pDevice->pContext->allocationCallbacks); - if (pDevice->opensl.pBufferCapture == NULL) { - ma_device_uninit__opensl(pDevice); - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[OpenSL] Failed to allocate memory for data buffer."); - return MA_OUT_OF_MEMORY; - } - MA_ZERO_MEMORY(pDevice->opensl.pBufferCapture, bufferSizeInBytes); - } - - if (pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) { - ma_SLDataFormat_PCM pcm; - SLDataSource source; - SLDataLocator_OutputMix outmixLocator; - SLDataSink sink; - SLAndroidConfigurationItf pPlayerConfig; - - ma_SLDataFormat_PCM_init__opensl(pDescriptorPlayback->format, pDescriptorPlayback->channels, pDescriptorPlayback->sampleRate, pDescriptorPlayback->channelMap, &pcm); - - resultSL = (*g_maEngineSL)->CreateOutputMix(g_maEngineSL, (SLObjectItf*)&pDevice->opensl.pOutputMixObj, 0, NULL, NULL); - if (resultSL != SL_RESULT_SUCCESS) { - ma_device_uninit__opensl(pDevice); - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[OpenSL] Failed to create output mix."); - return ma_result_from_OpenSL(resultSL); - } - - resultSL = MA_OPENSL_OBJ(pDevice->opensl.pOutputMixObj)->Realize((SLObjectItf)pDevice->opensl.pOutputMixObj, SL_BOOLEAN_FALSE); - if (resultSL != SL_RESULT_SUCCESS) { - ma_device_uninit__opensl(pDevice); - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[OpenSL] Failed to realize output mix object."); - return ma_result_from_OpenSL(resultSL); - } - - resultSL = MA_OPENSL_OBJ(pDevice->opensl.pOutputMixObj)->GetInterface((SLObjectItf)pDevice->opensl.pOutputMixObj, (SLInterfaceID)pDevice->pContext->opensl.SL_IID_OUTPUTMIX, &pDevice->opensl.pOutputMix); - if (resultSL != SL_RESULT_SUCCESS) { - ma_device_uninit__opensl(pDevice); - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[OpenSL] Failed to retrieve SL_IID_OUTPUTMIX interface."); - return ma_result_from_OpenSL(resultSL); - } - - /* Set the output device. */ - if (pDescriptorPlayback->pDeviceID != NULL) { - SLuint32 deviceID_OpenSL = pDescriptorPlayback->pDeviceID->opensl; - MA_OPENSL_OUTPUTMIX(pDevice->opensl.pOutputMix)->ReRoute((SLOutputMixItf)pDevice->opensl.pOutputMix, 1, &deviceID_OpenSL); - } - - queue.numBuffers = pDescriptorPlayback->periodCount; - - source.pLocator = &queue; - source.pFormat = (SLDataFormat_PCM*)&pcm; - - outmixLocator.locatorType = SL_DATALOCATOR_OUTPUTMIX; - outmixLocator.outputMix = (SLObjectItf)pDevice->opensl.pOutputMixObj; - - sink.pLocator = &outmixLocator; - sink.pFormat = NULL; - - resultSL = (*g_maEngineSL)->CreateAudioPlayer(g_maEngineSL, (SLObjectItf*)&pDevice->opensl.pAudioPlayerObj, &source, &sink, ma_countof(itfIDs), itfIDs, itfIDsRequired); - if (resultSL == SL_RESULT_CONTENT_UNSUPPORTED || resultSL == SL_RESULT_PARAMETER_INVALID) { - /* Unsupported format. Fall back to something safer and try again. If this fails, just abort. */ - pcm.formatType = SL_DATAFORMAT_PCM; - pcm.numChannels = 2; - ((SLDataFormat_PCM*)&pcm)->samplesPerSec = SL_SAMPLINGRATE_16; - pcm.bitsPerSample = 16; - pcm.containerSize = pcm.bitsPerSample; /* Always tightly packed for now. */ - pcm.channelMask = SL_SPEAKER_FRONT_LEFT | SL_SPEAKER_FRONT_RIGHT; - resultSL = (*g_maEngineSL)->CreateAudioPlayer(g_maEngineSL, (SLObjectItf*)&pDevice->opensl.pAudioPlayerObj, &source, &sink, ma_countof(itfIDs), itfIDs, itfIDsRequired); - } - - if (resultSL != SL_RESULT_SUCCESS) { - ma_device_uninit__opensl(pDevice); - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[OpenSL] Failed to create audio player."); - return ma_result_from_OpenSL(resultSL); - } - - - /* Set the stream type before realizing the player. */ - if (pConfig->opensl.streamType != ma_opensl_stream_type_default) { - resultSL = MA_OPENSL_OBJ(pDevice->opensl.pAudioPlayerObj)->GetInterface((SLObjectItf)pDevice->opensl.pAudioPlayerObj, (SLInterfaceID)pDevice->pContext->opensl.SL_IID_ANDROIDCONFIGURATION, &pPlayerConfig); - if (resultSL == SL_RESULT_SUCCESS) { - SLint32 streamType = ma_to_stream_type__opensl(pConfig->opensl.streamType); - resultSL = (*pPlayerConfig)->SetConfiguration(pPlayerConfig, SL_ANDROID_KEY_STREAM_TYPE, &streamType, sizeof(SLint32)); - if (resultSL != SL_RESULT_SUCCESS) { - /* Failed to set the configuration. Just keep going. */ - } - } - } - - resultSL = MA_OPENSL_OBJ(pDevice->opensl.pAudioPlayerObj)->Realize((SLObjectItf)pDevice->opensl.pAudioPlayerObj, SL_BOOLEAN_FALSE); - if (resultSL != SL_RESULT_SUCCESS) { - ma_device_uninit__opensl(pDevice); - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[OpenSL] Failed to realize audio player."); - return ma_result_from_OpenSL(resultSL); - } - - resultSL = MA_OPENSL_OBJ(pDevice->opensl.pAudioPlayerObj)->GetInterface((SLObjectItf)pDevice->opensl.pAudioPlayerObj, (SLInterfaceID)pDevice->pContext->opensl.SL_IID_PLAY, &pDevice->opensl.pAudioPlayer); - if (resultSL != SL_RESULT_SUCCESS) { - ma_device_uninit__opensl(pDevice); - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[OpenSL] Failed to retrieve SL_IID_PLAY interface."); - return ma_result_from_OpenSL(resultSL); - } - - resultSL = MA_OPENSL_OBJ(pDevice->opensl.pAudioPlayerObj)->GetInterface((SLObjectItf)pDevice->opensl.pAudioPlayerObj, (SLInterfaceID)pDevice->pContext->opensl.SL_IID_ANDROIDSIMPLEBUFFERQUEUE, &pDevice->opensl.pBufferQueuePlayback); - if (resultSL != SL_RESULT_SUCCESS) { - ma_device_uninit__opensl(pDevice); - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[OpenSL] Failed to retrieve SL_IID_ANDROIDSIMPLEBUFFERQUEUE interface."); - return ma_result_from_OpenSL(resultSL); - } - - resultSL = MA_OPENSL_BUFFERQUEUE(pDevice->opensl.pBufferQueuePlayback)->RegisterCallback((SLAndroidSimpleBufferQueueItf)pDevice->opensl.pBufferQueuePlayback, ma_buffer_queue_callback_playback__opensl_android, pDevice); - if (resultSL != SL_RESULT_SUCCESS) { - ma_device_uninit__opensl(pDevice); - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[OpenSL] Failed to register buffer queue callback."); - return ma_result_from_OpenSL(resultSL); - } - - /* The internal format is determined by the "pcm" object. */ - ma_deconstruct_SLDataFormat_PCM__opensl(&pcm, &pDescriptorPlayback->format, &pDescriptorPlayback->channels, &pDescriptorPlayback->sampleRate, pDescriptorPlayback->channelMap, ma_countof(pDescriptorPlayback->channelMap)); - - /* Buffer. */ - pDescriptorPlayback->periodSizeInFrames = ma_calculate_buffer_size_in_frames_from_descriptor(pDescriptorPlayback, pDescriptorPlayback->sampleRate, pConfig->performanceProfile); - pDevice->opensl.currentBufferIndexPlayback = 0; - - bufferSizeInBytes = pDescriptorPlayback->periodSizeInFrames * ma_get_bytes_per_frame(pDescriptorPlayback->format, pDescriptorPlayback->channels) * pDescriptorPlayback->periodCount; - pDevice->opensl.pBufferPlayback = (ma_uint8*)ma_calloc(bufferSizeInBytes, &pDevice->pContext->allocationCallbacks); - if (pDevice->opensl.pBufferPlayback == NULL) { - ma_device_uninit__opensl(pDevice); - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[OpenSL] Failed to allocate memory for data buffer."); - return MA_OUT_OF_MEMORY; - } - MA_ZERO_MEMORY(pDevice->opensl.pBufferPlayback, bufferSizeInBytes); - } - - return MA_SUCCESS; -#else - return MA_NO_BACKEND; /* Non-Android implementations are not supported. */ -#endif -} - -static ma_result ma_device_start__opensl(ma_device* pDevice) -{ - SLresult resultSL; - size_t periodSizeInBytes; - ma_uint32 iPeriod; - - MA_ASSERT(pDevice != NULL); - - MA_ASSERT(g_maOpenSLInitCounter > 0); /* <-- If you trigger this it means you've either not initialized the context, or you've uninitialized it and then attempted to start the device. */ - if (g_maOpenSLInitCounter == 0) { - return MA_INVALID_OPERATION; - } - - if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) { - resultSL = MA_OPENSL_RECORD(pDevice->opensl.pAudioRecorder)->SetRecordState((SLRecordItf)pDevice->opensl.pAudioRecorder, SL_RECORDSTATE_RECORDING); - if (resultSL != SL_RESULT_SUCCESS) { - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[OpenSL] Failed to start internal capture device."); - return ma_result_from_OpenSL(resultSL); - } - - periodSizeInBytes = pDevice->capture.internalPeriodSizeInFrames * ma_get_bytes_per_frame(pDevice->capture.internalFormat, pDevice->capture.internalChannels); - for (iPeriod = 0; iPeriod < pDevice->capture.internalPeriods; ++iPeriod) { - resultSL = MA_OPENSL_BUFFERQUEUE(pDevice->opensl.pBufferQueueCapture)->Enqueue((SLAndroidSimpleBufferQueueItf)pDevice->opensl.pBufferQueueCapture, pDevice->opensl.pBufferCapture + (periodSizeInBytes * iPeriod), periodSizeInBytes); - if (resultSL != SL_RESULT_SUCCESS) { - MA_OPENSL_RECORD(pDevice->opensl.pAudioRecorder)->SetRecordState((SLRecordItf)pDevice->opensl.pAudioRecorder, SL_RECORDSTATE_STOPPED); - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[OpenSL] Failed to enqueue buffer for capture device."); - return ma_result_from_OpenSL(resultSL); - } - } - } - - if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) { - resultSL = MA_OPENSL_PLAY(pDevice->opensl.pAudioPlayer)->SetPlayState((SLPlayItf)pDevice->opensl.pAudioPlayer, SL_PLAYSTATE_PLAYING); - if (resultSL != SL_RESULT_SUCCESS) { - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[OpenSL] Failed to start internal playback device."); - return ma_result_from_OpenSL(resultSL); - } - - /* In playback mode (no duplex) we need to load some initial buffers. In duplex mode we need to enqueue silent buffers. */ - if (pDevice->type == ma_device_type_duplex) { - MA_ZERO_MEMORY(pDevice->opensl.pBufferPlayback, pDevice->playback.internalPeriodSizeInFrames * pDevice->playback.internalPeriods * ma_get_bytes_per_frame(pDevice->playback.internalFormat, pDevice->playback.internalChannels)); - } else { - ma_device__read_frames_from_client(pDevice, pDevice->playback.internalPeriodSizeInFrames * pDevice->playback.internalPeriods, pDevice->opensl.pBufferPlayback); - } - - periodSizeInBytes = pDevice->playback.internalPeriodSizeInFrames * ma_get_bytes_per_frame(pDevice->playback.internalFormat, pDevice->playback.internalChannels); - for (iPeriod = 0; iPeriod < pDevice->playback.internalPeriods; ++iPeriod) { - resultSL = MA_OPENSL_BUFFERQUEUE(pDevice->opensl.pBufferQueuePlayback)->Enqueue((SLAndroidSimpleBufferQueueItf)pDevice->opensl.pBufferQueuePlayback, pDevice->opensl.pBufferPlayback + (periodSizeInBytes * iPeriod), periodSizeInBytes); - if (resultSL != SL_RESULT_SUCCESS) { - MA_OPENSL_PLAY(pDevice->opensl.pAudioPlayer)->SetPlayState((SLPlayItf)pDevice->opensl.pAudioPlayer, SL_PLAYSTATE_STOPPED); - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[OpenSL] Failed to enqueue buffer for playback device."); - return ma_result_from_OpenSL(resultSL); - } - } - } - - return MA_SUCCESS; -} - -static ma_result ma_device_drain__opensl(ma_device* pDevice, ma_device_type deviceType) -{ - SLAndroidSimpleBufferQueueItf pBufferQueue; - - MA_ASSERT(deviceType == ma_device_type_capture || deviceType == ma_device_type_playback); - - if (pDevice->type == ma_device_type_capture) { - pBufferQueue = (SLAndroidSimpleBufferQueueItf)pDevice->opensl.pBufferQueueCapture; - pDevice->opensl.isDrainingCapture = MA_TRUE; - } else { - pBufferQueue = (SLAndroidSimpleBufferQueueItf)pDevice->opensl.pBufferQueuePlayback; - pDevice->opensl.isDrainingPlayback = MA_TRUE; - } - - for (;;) { - SLAndroidSimpleBufferQueueState state; - - MA_OPENSL_BUFFERQUEUE(pBufferQueue)->GetState(pBufferQueue, &state); - if (state.count == 0) { - break; - } - - ma_sleep(10); - } - - if (pDevice->type == ma_device_type_capture) { - pDevice->opensl.isDrainingCapture = MA_FALSE; - } else { - pDevice->opensl.isDrainingPlayback = MA_FALSE; - } - - return MA_SUCCESS; -} - -static ma_result ma_device_stop__opensl(ma_device* pDevice) -{ - SLresult resultSL; - - MA_ASSERT(pDevice != NULL); - - MA_ASSERT(g_maOpenSLInitCounter > 0); /* <-- If you trigger this it means you've either not initialized the context, or you've uninitialized it before stopping/uninitializing the device. */ - if (g_maOpenSLInitCounter == 0) { - return MA_INVALID_OPERATION; - } - - if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) { - ma_device_drain__opensl(pDevice, ma_device_type_capture); - - resultSL = MA_OPENSL_RECORD(pDevice->opensl.pAudioRecorder)->SetRecordState((SLRecordItf)pDevice->opensl.pAudioRecorder, SL_RECORDSTATE_STOPPED); - if (resultSL != SL_RESULT_SUCCESS) { - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[OpenSL] Failed to stop internal capture device."); - return ma_result_from_OpenSL(resultSL); - } - - MA_OPENSL_BUFFERQUEUE(pDevice->opensl.pBufferQueueCapture)->Clear((SLAndroidSimpleBufferQueueItf)pDevice->opensl.pBufferQueueCapture); - } - - if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) { - ma_device_drain__opensl(pDevice, ma_device_type_playback); - - resultSL = MA_OPENSL_PLAY(pDevice->opensl.pAudioPlayer)->SetPlayState((SLPlayItf)pDevice->opensl.pAudioPlayer, SL_PLAYSTATE_STOPPED); - if (resultSL != SL_RESULT_SUCCESS) { - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[OpenSL] Failed to stop internal playback device."); - return ma_result_from_OpenSL(resultSL); - } - - MA_OPENSL_BUFFERQUEUE(pDevice->opensl.pBufferQueuePlayback)->Clear((SLAndroidSimpleBufferQueueItf)pDevice->opensl.pBufferQueuePlayback); - } - - /* Make sure the client is aware that the device has stopped. There may be an OpenSL|ES callback for this, but I haven't found it. */ - ma_device__on_notification_stopped(pDevice); - - return MA_SUCCESS; -} - - -static ma_result ma_context_uninit__opensl(ma_context* pContext) -{ - MA_ASSERT(pContext != NULL); - MA_ASSERT(pContext->backend == ma_backend_opensl); - (void)pContext; - - /* Uninit global data. */ - ma_spinlock_lock(&g_maOpenSLSpinlock); - { - MA_ASSERT(g_maOpenSLInitCounter > 0); /* If you've triggered this, it means you have ma_context_init/uninit mismatch. Each successful call to ma_context_init() must be matched up with a call to ma_context_uninit(). */ - - g_maOpenSLInitCounter -= 1; - if (g_maOpenSLInitCounter == 0) { - (*g_maEngineObjectSL)->Destroy(g_maEngineObjectSL); - } - } - ma_spinlock_unlock(&g_maOpenSLSpinlock); - - return MA_SUCCESS; -} - -static ma_result ma_dlsym_SLInterfaceID__opensl(ma_context* pContext, const char* pName, ma_handle* pHandle) -{ - /* We need to return an error if the symbol cannot be found. This is important because there have been reports that some symbols do not exist. */ - ma_handle* p = (ma_handle*)ma_dlsym(ma_context_get_log(pContext), pContext->opensl.libOpenSLES, pName); - if (p == NULL) { - ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_INFO, "[OpenSL] Cannot find symbol %s", pName); - return MA_NO_BACKEND; - } - - *pHandle = *p; - return MA_SUCCESS; -} - -static ma_result ma_context_init_engine_nolock__opensl(ma_context* pContext) -{ - g_maOpenSLInitCounter += 1; - if (g_maOpenSLInitCounter == 1) { - SLresult resultSL; - - resultSL = ((ma_slCreateEngine_proc)pContext->opensl.slCreateEngine)(&g_maEngineObjectSL, 0, NULL, 0, NULL, NULL); - if (resultSL != SL_RESULT_SUCCESS) { - g_maOpenSLInitCounter -= 1; - return ma_result_from_OpenSL(resultSL); - } - - (*g_maEngineObjectSL)->Realize(g_maEngineObjectSL, SL_BOOLEAN_FALSE); - - resultSL = (*g_maEngineObjectSL)->GetInterface(g_maEngineObjectSL, (SLInterfaceID)pContext->opensl.SL_IID_ENGINE, &g_maEngineSL); - if (resultSL != SL_RESULT_SUCCESS) { - (*g_maEngineObjectSL)->Destroy(g_maEngineObjectSL); - g_maOpenSLInitCounter -= 1; - return ma_result_from_OpenSL(resultSL); - } - } - - return MA_SUCCESS; -} - -static ma_result ma_context_init__opensl(ma_context* pContext, const ma_context_config* pConfig, ma_backend_callbacks* pCallbacks) -{ - ma_result result; - -#if !defined(MA_NO_RUNTIME_LINKING) - size_t i; - const char* libOpenSLESNames[] = { - "libOpenSLES.so" - }; -#endif - - MA_ASSERT(pContext != NULL); - - (void)pConfig; - -#if !defined(MA_NO_RUNTIME_LINKING) - /* - Dynamically link against libOpenSLES.so. I have now had multiple reports that SL_IID_ANDROIDSIMPLEBUFFERQUEUE cannot be found. One - report was happening at compile time and another at runtime. To try working around this, I'm going to link to libOpenSLES at runtime - and extract the symbols rather than reference them directly. This should, hopefully, fix these issues as the compiler won't see any - references to the symbols and will hopefully skip the checks. - */ - for (i = 0; i < ma_countof(libOpenSLESNames); i += 1) { - pContext->opensl.libOpenSLES = ma_dlopen(ma_context_get_log(pContext), libOpenSLESNames[i]); - if (pContext->opensl.libOpenSLES != NULL) { - break; - } - } - - if (pContext->opensl.libOpenSLES == NULL) { - ma_log_post(ma_context_get_log(pContext), MA_LOG_LEVEL_INFO, "[OpenSL] Could not find libOpenSLES.so"); - return MA_NO_BACKEND; - } - - result = ma_dlsym_SLInterfaceID__opensl(pContext, "SL_IID_ENGINE", &pContext->opensl.SL_IID_ENGINE); - if (result != MA_SUCCESS) { - ma_dlclose(ma_context_get_log(pContext), pContext->opensl.libOpenSLES); - return result; - } - - result = ma_dlsym_SLInterfaceID__opensl(pContext, "SL_IID_AUDIOIODEVICECAPABILITIES", &pContext->opensl.SL_IID_AUDIOIODEVICECAPABILITIES); - if (result != MA_SUCCESS) { - ma_dlclose(ma_context_get_log(pContext), pContext->opensl.libOpenSLES); - return result; - } - - result = ma_dlsym_SLInterfaceID__opensl(pContext, "SL_IID_ANDROIDSIMPLEBUFFERQUEUE", &pContext->opensl.SL_IID_ANDROIDSIMPLEBUFFERQUEUE); - if (result != MA_SUCCESS) { - ma_dlclose(ma_context_get_log(pContext), pContext->opensl.libOpenSLES); - return result; - } - - result = ma_dlsym_SLInterfaceID__opensl(pContext, "SL_IID_RECORD", &pContext->opensl.SL_IID_RECORD); - if (result != MA_SUCCESS) { - ma_dlclose(ma_context_get_log(pContext), pContext->opensl.libOpenSLES); - return result; - } - - result = ma_dlsym_SLInterfaceID__opensl(pContext, "SL_IID_PLAY", &pContext->opensl.SL_IID_PLAY); - if (result != MA_SUCCESS) { - ma_dlclose(ma_context_get_log(pContext), pContext->opensl.libOpenSLES); - return result; - } - - result = ma_dlsym_SLInterfaceID__opensl(pContext, "SL_IID_OUTPUTMIX", &pContext->opensl.SL_IID_OUTPUTMIX); - if (result != MA_SUCCESS) { - ma_dlclose(ma_context_get_log(pContext), pContext->opensl.libOpenSLES); - return result; - } - - result = ma_dlsym_SLInterfaceID__opensl(pContext, "SL_IID_ANDROIDCONFIGURATION", &pContext->opensl.SL_IID_ANDROIDCONFIGURATION); - if (result != MA_SUCCESS) { - ma_dlclose(ma_context_get_log(pContext), pContext->opensl.libOpenSLES); - return result; - } - - pContext->opensl.slCreateEngine = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->opensl.libOpenSLES, "slCreateEngine"); - if (pContext->opensl.slCreateEngine == NULL) { - ma_dlclose(ma_context_get_log(pContext), pContext->opensl.libOpenSLES); - ma_log_post(ma_context_get_log(pContext), MA_LOG_LEVEL_INFO, "[OpenSL] Cannot find symbol slCreateEngine."); - return MA_NO_BACKEND; - } -#else - pContext->opensl.SL_IID_ENGINE = (ma_handle)SL_IID_ENGINE; - pContext->opensl.SL_IID_AUDIOIODEVICECAPABILITIES = (ma_handle)SL_IID_AUDIOIODEVICECAPABILITIES; - pContext->opensl.SL_IID_ANDROIDSIMPLEBUFFERQUEUE = (ma_handle)SL_IID_ANDROIDSIMPLEBUFFERQUEUE; - pContext->opensl.SL_IID_RECORD = (ma_handle)SL_IID_RECORD; - pContext->opensl.SL_IID_PLAY = (ma_handle)SL_IID_PLAY; - pContext->opensl.SL_IID_OUTPUTMIX = (ma_handle)SL_IID_OUTPUTMIX; - pContext->opensl.SL_IID_ANDROIDCONFIGURATION = (ma_handle)SL_IID_ANDROIDCONFIGURATION; - pContext->opensl.slCreateEngine = (ma_proc)slCreateEngine; -#endif - - - /* Initialize global data first if applicable. */ - ma_spinlock_lock(&g_maOpenSLSpinlock); - { - result = ma_context_init_engine_nolock__opensl(pContext); - } - ma_spinlock_unlock(&g_maOpenSLSpinlock); - - if (result != MA_SUCCESS) { - ma_dlclose(ma_context_get_log(pContext), pContext->opensl.libOpenSLES); - ma_log_post(ma_context_get_log(pContext), MA_LOG_LEVEL_INFO, "[OpenSL] Failed to initialize OpenSL engine."); - return result; - } - - pCallbacks->onContextInit = ma_context_init__opensl; - pCallbacks->onContextUninit = ma_context_uninit__opensl; - pCallbacks->onContextEnumerateDevices = ma_context_enumerate_devices__opensl; - pCallbacks->onContextGetDeviceInfo = ma_context_get_device_info__opensl; - pCallbacks->onDeviceInit = ma_device_init__opensl; - pCallbacks->onDeviceUninit = ma_device_uninit__opensl; - pCallbacks->onDeviceStart = ma_device_start__opensl; - pCallbacks->onDeviceStop = ma_device_stop__opensl; - pCallbacks->onDeviceRead = NULL; /* Not needed because OpenSL|ES is asynchronous. */ - pCallbacks->onDeviceWrite = NULL; /* Not needed because OpenSL|ES is asynchronous. */ - pCallbacks->onDeviceDataLoop = NULL; /* Not needed because OpenSL|ES is asynchronous. */ - - return MA_SUCCESS; -} -#endif /* OpenSL|ES */ - - -/****************************************************************************** - -Web Audio Backend - -******************************************************************************/ -#ifdef MA_HAS_WEBAUDIO -#include - -#if (__EMSCRIPTEN_major__ > 3) || (__EMSCRIPTEN_major__ == 3 && (__EMSCRIPTEN_minor__ > 1 || (__EMSCRIPTEN_minor__ == 1 && __EMSCRIPTEN_tiny__ >= 32))) - #include - #define MA_SUPPORT_AUDIO_WORKLETS - - #if (__EMSCRIPTEN_major__ > 3) || (__EMSCRIPTEN_major__ == 3 && (__EMSCRIPTEN_minor__ > 1 || (__EMSCRIPTEN_minor__ == 1 && __EMSCRIPTEN_tiny__ >= 70))) - #define MA_SUPPORT_AUDIO_WORKLETS_VARIABLE_BUFFER_SIZE - #endif -#endif - -/* -TODO: Version 0.12: Swap this logic around so that AudioWorklets are used by default. Add MA_NO_AUDIO_WORKLETS. -*/ -#if defined(MA_ENABLE_AUDIO_WORKLETS) && defined(MA_SUPPORT_AUDIO_WORKLETS) - #define MA_USE_AUDIO_WORKLETS -#endif - -/* The thread stack size must be a multiple of 16. */ -#ifndef MA_AUDIO_WORKLETS_THREAD_STACK_SIZE -#define MA_AUDIO_WORKLETS_THREAD_STACK_SIZE 131072 -#endif - -#if defined(MA_USE_AUDIO_WORKLETS) -#define MA_WEBAUDIO_LATENCY_HINT_BALANCED "balanced" -#define MA_WEBAUDIO_LATENCY_HINT_INTERACTIVE "interactive" -#define MA_WEBAUDIO_LATENCY_HINT_PLAYBACK "playback" -#endif - -static ma_bool32 ma_is_capture_supported__webaudio() -{ - return EM_ASM_INT({ - return (navigator.mediaDevices !== undefined && navigator.mediaDevices.getUserMedia !== undefined); - }, 0) != 0; /* Must pass in a dummy argument for C99 compatibility. */ -} - -#ifdef __cplusplus -extern "C" { -#endif -void* EMSCRIPTEN_KEEPALIVE ma_malloc_emscripten(size_t sz, const ma_allocation_callbacks* pAllocationCallbacks) -{ - return ma_malloc(sz, pAllocationCallbacks); -} - -void EMSCRIPTEN_KEEPALIVE ma_free_emscripten(void* p, const ma_allocation_callbacks* pAllocationCallbacks) -{ - ma_free(p, pAllocationCallbacks); -} - -void EMSCRIPTEN_KEEPALIVE ma_device_process_pcm_frames_capture__webaudio(ma_device* pDevice, int frameCount, float* pFrames) -{ - ma_device_handle_backend_data_callback(pDevice, NULL, pFrames, (ma_uint32)frameCount); -} - -void EMSCRIPTEN_KEEPALIVE ma_device_process_pcm_frames_playback__webaudio(ma_device* pDevice, int frameCount, float* pFrames) -{ - ma_device_handle_backend_data_callback(pDevice, pFrames, NULL, (ma_uint32)frameCount); -} -#ifdef __cplusplus -} -#endif - -static ma_result ma_context_enumerate_devices__webaudio(ma_context* pContext, ma_enum_devices_callback_proc callback, void* pUserData) -{ - ma_bool32 cbResult = MA_TRUE; - - MA_ASSERT(pContext != NULL); - MA_ASSERT(callback != NULL); - - /* Only supporting default devices for now. */ - - /* Playback. */ - if (cbResult) { - ma_device_info deviceInfo; - MA_ZERO_OBJECT(&deviceInfo); - ma_strncpy_s(deviceInfo.name, sizeof(deviceInfo.name), MA_DEFAULT_PLAYBACK_DEVICE_NAME, (size_t)-1); - deviceInfo.isDefault = MA_TRUE; /* Only supporting default devices. */ - cbResult = callback(pContext, ma_device_type_playback, &deviceInfo, pUserData); - } - - /* Capture. */ - if (cbResult) { - if (ma_is_capture_supported__webaudio()) { - ma_device_info deviceInfo; - MA_ZERO_OBJECT(&deviceInfo); - ma_strncpy_s(deviceInfo.name, sizeof(deviceInfo.name), MA_DEFAULT_CAPTURE_DEVICE_NAME, (size_t)-1); - deviceInfo.isDefault = MA_TRUE; /* Only supporting default devices. */ - cbResult = callback(pContext, ma_device_type_capture, &deviceInfo, pUserData); - } - } - - return MA_SUCCESS; -} - -static ma_result ma_context_get_device_info__webaudio(ma_context* pContext, ma_device_type deviceType, const ma_device_id* pDeviceID, ma_device_info* pDeviceInfo) -{ - MA_ASSERT(pContext != NULL); - - if (deviceType == ma_device_type_capture && !ma_is_capture_supported__webaudio()) { - return MA_NO_DEVICE; - } - - MA_ZERO_MEMORY(pDeviceInfo->id.webaudio, sizeof(pDeviceInfo->id.webaudio)); - - /* Only supporting default devices for now. */ - (void)pDeviceID; - if (deviceType == ma_device_type_playback) { - ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), MA_DEFAULT_PLAYBACK_DEVICE_NAME, (size_t)-1); - } else { - ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), MA_DEFAULT_CAPTURE_DEVICE_NAME, (size_t)-1); - } - - /* Only supporting default devices. */ - pDeviceInfo->isDefault = MA_TRUE; - - /* Web Audio can support any number of channels and sample rates. It only supports f32 formats, however. */ - pDeviceInfo->nativeDataFormats[0].flags = 0; - pDeviceInfo->nativeDataFormats[0].format = ma_format_unknown; - pDeviceInfo->nativeDataFormats[0].channels = 0; /* All channels are supported. */ - pDeviceInfo->nativeDataFormats[0].sampleRate = EM_ASM_INT({ - try { - var temp = new (window.AudioContext || window.webkitAudioContext)(); - var sampleRate = temp.sampleRate; - temp.close(); - return sampleRate; - } catch(e) { - return 0; - } - }, 0); /* Must pass in a dummy argument for C99 compatibility. */ - - if (pDeviceInfo->nativeDataFormats[0].sampleRate == 0) { - return MA_NO_DEVICE; - } - - pDeviceInfo->nativeDataFormatCount = 1; - - return MA_SUCCESS; -} - -static ma_result ma_device_uninit__webaudio(ma_device* pDevice) -{ - MA_ASSERT(pDevice != NULL); - - #if defined(MA_USE_AUDIO_WORKLETS) - { - EM_ASM({ - var device = window.miniaudio.get_device_by_index($0); - - if (device.streamNode !== undefined) { - device.streamNode.disconnect(); - device.streamNode = undefined; - } - - device.pDevice = undefined; - }, pDevice->webaudio.deviceIndex); - - emscripten_destroy_web_audio_node(pDevice->webaudio.audioWorklet); - emscripten_destroy_audio_context(pDevice->webaudio.audioContext); - ma_free(pDevice->webaudio.pStackBuffer, &pDevice->pContext->allocationCallbacks); - } - #else - { - EM_ASM({ - var device = window.miniaudio.get_device_by_index($0); - - /* Make sure all nodes are disconnected and marked for collection. */ - if (device.scriptNode !== undefined) { - device.scriptNode.onaudioprocess = function(e) {}; /* We want to reset the callback to ensure it doesn't get called after AudioContext.close() has returned. Shouldn't happen since we're disconnecting, but just to be safe... */ - device.scriptNode.disconnect(); - device.scriptNode = undefined; - } - - if (device.streamNode !== undefined) { - device.streamNode.disconnect(); - device.streamNode = undefined; - } - - /* - Stop the device. I think there is a chance the callback could get fired after calling this, hence why we want - to clear the callback before closing. - */ - device.webaudio.close(); - device.webaudio = undefined; - device.pDevice = undefined; - }, pDevice->webaudio.deviceIndex); - } - #endif - - /* Clean up the device on the JS side. */ - EM_ASM({ - window.miniaudio.untrack_device_by_index($0); - }, pDevice->webaudio.deviceIndex); - - ma_free(pDevice->webaudio.pIntermediaryBuffer, &pDevice->pContext->allocationCallbacks); - - return MA_SUCCESS; -} - -#if !defined(MA_USE_AUDIO_WORKLETS) -static ma_uint32 ma_calculate_period_size_in_frames_from_descriptor__webaudio(const ma_device_descriptor* pDescriptor, ma_uint32 nativeSampleRate, ma_performance_profile performanceProfile) -{ - /* - There have been reports of the default buffer size being too small on some browsers. If we're using - the default buffer size, we'll make sure the period size is bigger than our standard defaults. - */ - ma_uint32 periodSizeInFrames; - - if (nativeSampleRate == 0) { - nativeSampleRate = MA_DEFAULT_SAMPLE_RATE; - } - - if (pDescriptor->periodSizeInFrames == 0) { - if (pDescriptor->periodSizeInMilliseconds == 0) { - if (performanceProfile == ma_performance_profile_low_latency) { - periodSizeInFrames = ma_calculate_buffer_size_in_frames_from_milliseconds(33, nativeSampleRate); /* 1 frame @ 30 FPS */ - } else { - periodSizeInFrames = ma_calculate_buffer_size_in_frames_from_milliseconds(333, nativeSampleRate); - } - } else { - periodSizeInFrames = ma_calculate_buffer_size_in_frames_from_milliseconds(pDescriptor->periodSizeInMilliseconds, nativeSampleRate); - } - } else { - periodSizeInFrames = pDescriptor->periodSizeInFrames; - } - - /* The size of the buffer must be a power of 2 and between 256 and 16384. */ - if (periodSizeInFrames < 256) { - periodSizeInFrames = 256; - } else if (periodSizeInFrames > 16384) { - periodSizeInFrames = 16384; - } else { - periodSizeInFrames = ma_next_power_of_2(periodSizeInFrames); - } - - return periodSizeInFrames; -} -#endif - - -#if defined(MA_USE_AUDIO_WORKLETS) -typedef struct -{ - ma_device* pDevice; - const ma_device_config* pConfig; - ma_device_descriptor* pDescriptorPlayback; - ma_device_descriptor* pDescriptorCapture; -} ma_audio_worklet_thread_initialized_data; - -static EM_BOOL ma_audio_worklet_process_callback__webaudio(int inputCount, const AudioSampleFrame* pInputs, int outputCount, AudioSampleFrame* pOutputs, int paramCount, const AudioParamFrame* pParams, void* pUserData) -{ - ma_device* pDevice = (ma_device*)pUserData; - ma_uint32 frameCount; - - (void)paramCount; - (void)pParams; - - /* - The Emscripten documentation says that it'll always be 128 frames being passed in. Hard coding it like that feels - like a very bad idea to me. Even if it's hard coded in the backend, the API and documentation should always refer - to variables instead of a hard coded number. In any case, will follow along for the time being. - - Unfortunately the audio data is not interleaved so we'll need to convert it before we give the data to miniaudio - for further processing. - */ - if (pDevice->type == ma_device_type_playback) { - frameCount = pDevice->playback.internalPeriodSizeInFrames; - } else { - frameCount = pDevice->capture.internalPeriodSizeInFrames; - } - - if (ma_device_get_state(pDevice) != ma_device_state_started) { - /* Fill the output buffer with zero to avoid a noise sound */ - for (int i = 0; i < outputCount; i += 1) { - MA_ZERO_MEMORY(pOutputs[i].data, pOutputs[i].numberOfChannels * frameCount * sizeof(float)); - } - - return EM_TRUE; - } - - if (inputCount > 0) { - /* Input data needs to be interleaved before we hand it to the client. */ - for (ma_uint32 iChannel = 0; iChannel < pDevice->capture.internalChannels; iChannel += 1) { - for (ma_uint32 iFrame = 0; iFrame < frameCount; iFrame += 1) { - pDevice->webaudio.pIntermediaryBuffer[iFrame*pDevice->capture.internalChannels + iChannel] = pInputs[0].data[frameCount*iChannel + iFrame]; - } - } - - ma_device_process_pcm_frames_capture__webaudio(pDevice, frameCount, pDevice->webaudio.pIntermediaryBuffer); - } - - if (outputCount > 0) { - /* If it's a capture-only device, we'll need to output silence. */ - if (pDevice->type == ma_device_type_capture) { - MA_ZERO_MEMORY(pOutputs[0].data, frameCount * pDevice->playback.internalChannels * sizeof(float)); - } else { - ma_device_process_pcm_frames_playback__webaudio(pDevice, frameCount, pDevice->webaudio.pIntermediaryBuffer); - - /* We've read the data from the client. Now we need to deinterleave the buffer and output to the output buffer. */ - for (ma_uint32 iChannel = 0; iChannel < pDevice->playback.internalChannels; iChannel += 1) { - for (ma_uint32 iFrame = 0; iFrame < frameCount; iFrame += 1) { - pOutputs[0].data[frameCount*iChannel + iFrame] = pDevice->webaudio.pIntermediaryBuffer[iFrame*pDevice->playback.internalChannels + iChannel]; - } - } - } - } - - return EM_TRUE; -} - - -static void ma_audio_worklet_processor_created__webaudio(EMSCRIPTEN_WEBAUDIO_T audioContext, EM_BOOL success, void* pUserData) -{ - ma_audio_worklet_thread_initialized_data* pParameters = (ma_audio_worklet_thread_initialized_data*)pUserData; - EmscriptenAudioWorkletNodeCreateOptions audioWorkletOptions; - int channels = 0; - size_t intermediaryBufferSizeInFrames; - int sampleRate; - - if (success == EM_FALSE) { - pParameters->pDevice->webaudio.initResult = MA_ERROR; - ma_free(pParameters, &pParameters->pDevice->pContext->allocationCallbacks); - return; - } - - /* The next step is to initialize the audio worklet node. */ - MA_ZERO_OBJECT(&audioWorkletOptions); - - /* - The way channel counts work with Web Audio is confusing. As far as I can tell, there's no way to know the channel - count from MediaStreamAudioSourceNode (what we use for capture)? The only way to have control is to configure an - output channel count on the capture side. This is slightly confusing for capture mode because intuitively you - wouldn't actually connect an output to an input-only node, but this is what we'll have to do in order to have - proper control over the channel count. In the capture case, we'll have to output silence to its output node. - */ - if (pParameters->pConfig->deviceType == ma_device_type_capture) { - channels = (int)((pParameters->pDescriptorCapture->channels > 0) ? pParameters->pDescriptorCapture->channels : MA_DEFAULT_CHANNELS); - audioWorkletOptions.numberOfInputs = 1; - } else { - channels = (int)((pParameters->pDescriptorPlayback->channels > 0) ? pParameters->pDescriptorPlayback->channels : MA_DEFAULT_CHANNELS); - - if (pParameters->pConfig->deviceType == ma_device_type_duplex) { - audioWorkletOptions.numberOfInputs = 1; - } else { - audioWorkletOptions.numberOfInputs = 0; - } - } - - audioWorkletOptions.numberOfOutputs = 1; - audioWorkletOptions.outputChannelCounts = &channels; - - - /* - Now that we know the channel count to use we can allocate the intermediary buffer. The - intermediary buffer is used for interleaving and deinterleaving. - */ - #if defined(MA_SUPPORT_AUDIO_WORKLETS_VARIABLE_BUFFER_SIZE) - { - intermediaryBufferSizeInFrames = (size_t)emscripten_audio_context_quantum_size(audioContext); - } - #else - { - intermediaryBufferSizeInFrames = 128; - } - #endif - - pParameters->pDevice->webaudio.pIntermediaryBuffer = (float*)ma_malloc(intermediaryBufferSizeInFrames * (ma_uint32)channels * sizeof(float), &pParameters->pDevice->pContext->allocationCallbacks); - if (pParameters->pDevice->webaudio.pIntermediaryBuffer == NULL) { - pParameters->pDevice->webaudio.initResult = MA_OUT_OF_MEMORY; - ma_free(pParameters, &pParameters->pDevice->pContext->allocationCallbacks); - return; - } - - pParameters->pDevice->webaudio.audioWorklet = emscripten_create_wasm_audio_worklet_node(audioContext, "miniaudio", &audioWorkletOptions, &ma_audio_worklet_process_callback__webaudio, pParameters->pDevice); - - /* With the audio worklet initialized we can now attach it to the graph. */ - if (pParameters->pConfig->deviceType == ma_device_type_capture || pParameters->pConfig->deviceType == ma_device_type_duplex) { - ma_result attachmentResult = (ma_result)EM_ASM_INT({ - var getUserMediaResult = 0; - var audioWorklet = emscriptenGetAudioObject($0); - var audioContext = emscriptenGetAudioObject($1); - - navigator.mediaDevices.getUserMedia({audio:true, video:false}) - .then(function(stream) { - audioContext.streamNode = audioContext.createMediaStreamSource(stream); - audioContext.streamNode.connect(audioWorklet); - audioWorklet.connect(audioContext.destination); - getUserMediaResult = 0; /* 0 = MA_SUCCESS */ - }) - .catch(function(error) { - console.log("navigator.mediaDevices.getUserMedia Failed: " + error); - getUserMediaResult = -1; /* -1 = MA_ERROR */ - }); - - return getUserMediaResult; - }, pParameters->pDevice->webaudio.audioWorklet, audioContext); - - if (attachmentResult != MA_SUCCESS) { - ma_log_postf(ma_device_get_log(pParameters->pDevice), MA_LOG_LEVEL_ERROR, "Web Audio: Failed to connect capture node."); - emscripten_destroy_web_audio_node(pParameters->pDevice->webaudio.audioWorklet); - pParameters->pDevice->webaudio.initResult = attachmentResult; - ma_free(pParameters, &pParameters->pDevice->pContext->allocationCallbacks); - return; - } - } - - /* If it's playback only we can now attach the worklet node to the graph. This has already been done for the duplex case. */ - if (pParameters->pConfig->deviceType == ma_device_type_playback) { - ma_result attachmentResult = (ma_result)EM_ASM_INT({ - var audioWorklet = emscriptenGetAudioObject($0); - var audioContext = emscriptenGetAudioObject($1); - audioWorklet.connect(audioContext.destination); - return 0; /* 0 = MA_SUCCESS */ - }, pParameters->pDevice->webaudio.audioWorklet, audioContext); - - if (attachmentResult != MA_SUCCESS) { - ma_log_postf(ma_device_get_log(pParameters->pDevice), MA_LOG_LEVEL_ERROR, "Web Audio: Failed to connect playback node."); - pParameters->pDevice->webaudio.initResult = attachmentResult; - ma_free(pParameters, &pParameters->pDevice->pContext->allocationCallbacks); - return; - } - } - - /* We need to update the descriptors so that they reflect the internal data format. Both capture and playback should be the same. */ - sampleRate = EM_ASM_INT({ return emscriptenGetAudioObject($0).sampleRate; }, audioContext); - - if (pParameters->pDescriptorCapture != NULL) { - pParameters->pDescriptorCapture->format = ma_format_f32; - pParameters->pDescriptorCapture->channels = (ma_uint32)channels; - pParameters->pDescriptorCapture->sampleRate = (ma_uint32)sampleRate; - ma_channel_map_init_standard(ma_standard_channel_map_webaudio, pParameters->pDescriptorCapture->channelMap, ma_countof(pParameters->pDescriptorCapture->channelMap), pParameters->pDescriptorCapture->channels); - pParameters->pDescriptorCapture->periodSizeInFrames = intermediaryBufferSizeInFrames; - pParameters->pDescriptorCapture->periodCount = 1; - } - - if (pParameters->pDescriptorPlayback != NULL) { - pParameters->pDescriptorPlayback->format = ma_format_f32; - pParameters->pDescriptorPlayback->channels = (ma_uint32)channels; - pParameters->pDescriptorPlayback->sampleRate = (ma_uint32)sampleRate; - ma_channel_map_init_standard(ma_standard_channel_map_webaudio, pParameters->pDescriptorPlayback->channelMap, ma_countof(pParameters->pDescriptorPlayback->channelMap), pParameters->pDescriptorPlayback->channels); - pParameters->pDescriptorPlayback->periodSizeInFrames = intermediaryBufferSizeInFrames; - pParameters->pDescriptorPlayback->periodCount = 1; - } - - /* At this point we're done and we can return. */ - ma_log_postf(ma_device_get_log(pParameters->pDevice), MA_LOG_LEVEL_DEBUG, "AudioWorklets: Created worklet node: %d\n", pParameters->pDevice->webaudio.audioWorklet); - pParameters->pDevice->webaudio.initResult = MA_SUCCESS; - ma_free(pParameters, &pParameters->pDevice->pContext->allocationCallbacks); -} - -static void ma_audio_worklet_thread_initialized__webaudio(EMSCRIPTEN_WEBAUDIO_T audioContext, EM_BOOL success, void* pUserData) -{ - ma_audio_worklet_thread_initialized_data* pParameters = (ma_audio_worklet_thread_initialized_data*)pUserData; - WebAudioWorkletProcessorCreateOptions workletProcessorOptions; - - MA_ASSERT(pParameters != NULL); - - if (success == EM_FALSE) { - pParameters->pDevice->webaudio.initResult = MA_ERROR; - return; - } - - MA_ZERO_OBJECT(&workletProcessorOptions); - workletProcessorOptions.name = "miniaudio"; /* I'm not entirely sure what to call this. Does this need to be globally unique, or does it need only be unique for a given AudioContext? */ - - emscripten_create_wasm_audio_worklet_processor_async(audioContext, &workletProcessorOptions, ma_audio_worklet_processor_created__webaudio, pParameters); -} -#endif - -static ma_result ma_device_init__webaudio(ma_device* pDevice, const ma_device_config* pConfig, ma_device_descriptor* pDescriptorPlayback, ma_device_descriptor* pDescriptorCapture) -{ - if (pConfig->deviceType == ma_device_type_loopback) { - return MA_DEVICE_TYPE_NOT_SUPPORTED; - } - - /* No exclusive mode with Web Audio. */ - if (((pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) && pDescriptorPlayback->shareMode == ma_share_mode_exclusive) || - ((pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) && pDescriptorCapture->shareMode == ma_share_mode_exclusive)) { - return MA_SHARE_MODE_NOT_SUPPORTED; - } - - /* - With AudioWorklets we'll have just a single AudioContext. I'm not sure why I'm not doing this for ScriptProcessorNode so - it might be worthwhile to look into that as well. - */ - #if defined(MA_USE_AUDIO_WORKLETS) - { - EmscriptenWebAudioCreateAttributes audioContextAttributes; - ma_audio_worklet_thread_initialized_data* pInitParameters; - void* pStackBuffer; - - if (pConfig->performanceProfile == ma_performance_profile_conservative) { - audioContextAttributes.latencyHint = MA_WEBAUDIO_LATENCY_HINT_PLAYBACK; - } else { - audioContextAttributes.latencyHint = MA_WEBAUDIO_LATENCY_HINT_INTERACTIVE; - } - - /* - In my testing, Firefox does not seem to capture audio data properly if the sample rate is set - to anything other than 48K. This does not seem to be the case for other browsers. For this reason, - if the device type is anything other than playback, we'll leave the sample rate as-is and let the - browser pick the appropriate rate for us. - */ - if (pConfig->deviceType == ma_device_type_playback) { - audioContextAttributes.sampleRate = pDescriptorPlayback->sampleRate; - } else { - audioContextAttributes.sampleRate = 0; - } - - /* It's not clear if this can return an error. None of the tests in the Emscripten repository check for this, so neither am I for now. */ - pDevice->webaudio.audioContext = emscripten_create_audio_context(&audioContextAttributes); - - /* - With the context created we can now create the worklet. We can only have a single worklet per audio - context which means we'll need to craft this appropriately to handle duplex devices correctly. - */ - - /* - We now need to create a worker thread. This is a bit weird because we need to allocate our - own buffer for the thread's stack. The stack needs to be aligned to 16 bytes. I'm going to - allocate this on the heap to keep it simple. - */ - pStackBuffer = ma_aligned_malloc(MA_AUDIO_WORKLETS_THREAD_STACK_SIZE, 16, &pDevice->pContext->allocationCallbacks); - if (pStackBuffer == NULL) { - emscripten_destroy_audio_context(pDevice->webaudio.audioContext); - return MA_OUT_OF_MEMORY; - } - - /* Our thread initialization parameters need to be allocated on the heap so they don't go out of scope. */ - pInitParameters = (ma_audio_worklet_thread_initialized_data*)ma_malloc(sizeof(*pInitParameters), &pDevice->pContext->allocationCallbacks); - if (pInitParameters == NULL) { - ma_free(pStackBuffer, &pDevice->pContext->allocationCallbacks); - emscripten_destroy_audio_context(pDevice->webaudio.audioContext); - return MA_OUT_OF_MEMORY; - } - - pInitParameters->pDevice = pDevice; - pInitParameters->pConfig = pConfig; - pInitParameters->pDescriptorPlayback = pDescriptorPlayback; - pInitParameters->pDescriptorCapture = pDescriptorCapture; - - /* - We need to flag the device as not yet initialized so we can wait on it later. Unfortunately all of - the Emscripten WebAudio stuff is asynchronous. - */ - pDevice->webaudio.initResult = MA_BUSY; - { - emscripten_start_wasm_audio_worklet_thread_async(pDevice->webaudio.audioContext, pStackBuffer, MA_AUDIO_WORKLETS_THREAD_STACK_SIZE, ma_audio_worklet_thread_initialized__webaudio, pInitParameters); - } - while (pDevice->webaudio.initResult == MA_BUSY) { emscripten_sleep(1); } /* We must wait for initialization to complete. We're just spinning here. The emscripten_sleep() call is why we need to build with `-sASYNCIFY`. */ - - /* Initialization is now complete. Descriptors were updated when the worklet was initialized. */ - if (pDevice->webaudio.initResult != MA_SUCCESS) { - ma_free(pStackBuffer, &pDevice->pContext->allocationCallbacks); - emscripten_destroy_audio_context(pDevice->webaudio.audioContext); - return pDevice->webaudio.initResult; - } - - /* We need to add an entry to the miniaudio.devices list on the JS side so we can do some JS/C interop. */ - pDevice->webaudio.deviceIndex = EM_ASM_INT({ - return window.miniaudio.track_device({ - webaudio: emscriptenGetAudioObject($0), - state: 1, /* 1 = ma_device_state_stopped */ - pDevice: $1 - }); - }, pDevice->webaudio.audioContext, pDevice); - - return MA_SUCCESS; - } - #else - { - /* ScriptProcessorNode. This path requires us to do almost everything in JS, but we'll do as much as we can in C. */ - ma_uint32 deviceIndex; - ma_uint32 channels; - ma_uint32 sampleRate; - ma_uint32 periodSizeInFrames; - - /* The channel count will depend on the device type. If it's a capture, use its, otherwise use the playback side. */ - if (pConfig->deviceType == ma_device_type_capture) { - channels = (pDescriptorCapture->channels > 0) ? pDescriptorCapture->channels : MA_DEFAULT_CHANNELS; - } else { - channels = (pDescriptorPlayback->channels > 0) ? pDescriptorPlayback->channels : MA_DEFAULT_CHANNELS; - } - - /* - When testing in Firefox, I've seen it where capture mode fails if the sample rate is changed to anything other than it's - native rate. For this reason we're leaving the sample rate untouched for capture devices. - */ - if (pConfig->deviceType == ma_device_type_playback) { - sampleRate = pDescriptorPlayback->sampleRate; - } else { - sampleRate = 0; /* Let the browser decide when capturing. */ - } - - /* The period size needs to be a power of 2. */ - if (pConfig->deviceType == ma_device_type_capture) { - periodSizeInFrames = ma_calculate_period_size_in_frames_from_descriptor__webaudio(pDescriptorCapture, sampleRate, pConfig->performanceProfile); - } else { - periodSizeInFrames = ma_calculate_period_size_in_frames_from_descriptor__webaudio(pDescriptorPlayback, sampleRate, pConfig->performanceProfile); - } - - /* We need an intermediary buffer for doing interleaving and deinterleaving. */ - pDevice->webaudio.pIntermediaryBuffer = (float*)ma_malloc(periodSizeInFrames * channels * sizeof(float), &pDevice->pContext->allocationCallbacks); - if (pDevice->webaudio.pIntermediaryBuffer == NULL) { - return MA_OUT_OF_MEMORY; - } - - deviceIndex = EM_ASM_INT({ - var deviceType = $0; - var channels = $1; - var sampleRate = $2; - var bufferSize = $3; - var pIntermediaryBuffer = $4; - var pDevice = $5; - - if (typeof(window.miniaudio) === 'undefined') { - return -1; /* Context not initialized. */ - } - - var device = {}; - - /* First thing we need is an AudioContext. */ - var audioContextOptions = {}; - if (deviceType == window.miniaudio.device_type.playback && sampleRate != 0) { - audioContextOptions.sampleRate = sampleRate; - } - - device.webaudio = new (window.AudioContext || window.webkitAudioContext)(audioContextOptions); - device.webaudio.suspend(); /* The AudioContext must be created in a suspended state. */ - device.state = window.miniaudio.device_state.stopped; - - /* - We need to create a ScriptProcessorNode. The channel situation is the same as the AudioWorklet path in that we - need to specify an output and configure the channel count there. - */ - var channelCountIn = 0; - var channelCountOut = channels; - if (deviceType != window.miniaudio.device_type.playback) { - channelCountIn = channels; - } - - device.scriptNode = device.webaudio.createScriptProcessor(bufferSize, channelCountIn, channelCountOut); - - /* The node processing callback. */ - device.scriptNode.onaudioprocess = function(e) { - if (device.intermediaryBufferView == null || device.intermediaryBufferView.length == 0) { - device.intermediaryBufferView = new Float32Array(HEAPF32.buffer, pIntermediaryBuffer, bufferSize * channels); - } - - /* Do the capture side first. */ - if (deviceType == window.miniaudio.device_type.capture || deviceType == window.miniaudio.device_type.duplex) { - /* The data must be interleaved before being processed miniaudio. */ - for (var iChannel = 0; iChannel < channels; iChannel += 1) { - var inputBuffer = e.inputBuffer.getChannelData(iChannel); - var intermediaryBuffer = device.intermediaryBufferView; - - for (var iFrame = 0; iFrame < bufferSize; iFrame += 1) { - intermediaryBuffer[iFrame*channels + iChannel] = inputBuffer[iFrame]; - } - } - - _ma_device_process_pcm_frames_capture__webaudio(pDevice, bufferSize, pIntermediaryBuffer); - } - - if (deviceType == window.miniaudio.device_type.playback || deviceType == window.miniaudio.device_type.duplex) { - _ma_device_process_pcm_frames_playback__webaudio(pDevice, bufferSize, pIntermediaryBuffer); - - for (var iChannel = 0; iChannel < e.outputBuffer.numberOfChannels; ++iChannel) { - var outputBuffer = e.outputBuffer.getChannelData(iChannel); - var intermediaryBuffer = device.intermediaryBufferView; - - for (var iFrame = 0; iFrame < bufferSize; iFrame += 1) { - outputBuffer[iFrame] = intermediaryBuffer[iFrame*channels + iChannel]; - } - } - } else { - /* It's a capture-only device. Make sure the output is silenced. */ - for (var iChannel = 0; iChannel < e.outputBuffer.numberOfChannels; ++iChannel) { - e.outputBuffer.getChannelData(iChannel).fill(0.0); - } - } - }; - - /* Now we need to connect our node to the graph. */ - if (deviceType == window.miniaudio.device_type.capture || deviceType == window.miniaudio.device_type.duplex) { - navigator.mediaDevices.getUserMedia({audio:true, video:false}) - .then(function(stream) { - device.streamNode = device.webaudio.createMediaStreamSource(stream); - device.streamNode.connect(device.scriptNode); - device.scriptNode.connect(device.webaudio.destination); - }) - .catch(function(error) { - console.log("Failed to get user media: " + error); - }); - } - - if (deviceType == window.miniaudio.device_type.playback) { - device.scriptNode.connect(device.webaudio.destination); - } - - device.pDevice = pDevice; - - return window.miniaudio.track_device(device); - }, pConfig->deviceType, channels, sampleRate, periodSizeInFrames, pDevice->webaudio.pIntermediaryBuffer, pDevice); - - if (deviceIndex < 0) { - return MA_FAILED_TO_OPEN_BACKEND_DEVICE; - } - - pDevice->webaudio.deviceIndex = deviceIndex; - - /* Grab the sample rate from the audio context directly. */ - sampleRate = (ma_uint32)EM_ASM_INT({ return window.miniaudio.get_device_by_index($0).webaudio.sampleRate; }, deviceIndex); - - if (pDescriptorCapture != NULL) { - pDescriptorCapture->format = ma_format_f32; - pDescriptorCapture->channels = channels; - pDescriptorCapture->sampleRate = sampleRate; - ma_channel_map_init_standard(ma_standard_channel_map_webaudio, pDescriptorCapture->channelMap, ma_countof(pDescriptorCapture->channelMap), pDescriptorCapture->channels); - pDescriptorCapture->periodSizeInFrames = periodSizeInFrames; - pDescriptorCapture->periodCount = 1; - } - - if (pDescriptorPlayback != NULL) { - pDescriptorPlayback->format = ma_format_f32; - pDescriptorPlayback->channels = channels; - pDescriptorPlayback->sampleRate = sampleRate; - ma_channel_map_init_standard(ma_standard_channel_map_webaudio, pDescriptorPlayback->channelMap, ma_countof(pDescriptorPlayback->channelMap), pDescriptorPlayback->channels); - pDescriptorPlayback->periodSizeInFrames = periodSizeInFrames; - pDescriptorPlayback->periodCount = 1; - } - - return MA_SUCCESS; - } - #endif -} - -static ma_result ma_device_start__webaudio(ma_device* pDevice) -{ - MA_ASSERT(pDevice != NULL); - - EM_ASM({ - var device = window.miniaudio.get_device_by_index($0); - device.webaudio.resume(); - device.state = window.miniaudio.device_state.started; - }, pDevice->webaudio.deviceIndex); - - return MA_SUCCESS; -} - -static ma_result ma_device_stop__webaudio(ma_device* pDevice) -{ - MA_ASSERT(pDevice != NULL); - - /* - From the WebAudio API documentation for AudioContext.suspend(): - - Suspends the progression of AudioContext's currentTime, allows any current context processing blocks that are already processed to be played to the - destination, and then allows the system to release its claim on audio hardware. - - I read this to mean that "any current context processing blocks" are processed by suspend() - i.e. They they are drained. We therefore shouldn't need to - do any kind of explicit draining. - */ - EM_ASM({ - var device = window.miniaudio.get_device_by_index($0); - device.webaudio.suspend(); - device.state = window.miniaudio.device_state.stopped; - }, pDevice->webaudio.deviceIndex); - - ma_device__on_notification_stopped(pDevice); - - return MA_SUCCESS; -} - -static ma_result ma_context_uninit__webaudio(ma_context* pContext) -{ - MA_ASSERT(pContext != NULL); - MA_ASSERT(pContext->backend == ma_backend_webaudio); - - (void)pContext; /* Unused. */ - - /* Remove the global miniaudio object from window if there are no more references to it. */ - EM_ASM({ - if (typeof(window.miniaudio) !== 'undefined') { - miniaudio.unlock_event_types.map(function(event_type) { - document.removeEventListener(event_type, miniaudio.unlock, true); - }); - - window.miniaudio.referenceCount -= 1; - if (window.miniaudio.referenceCount === 0) { - delete window.miniaudio; - } - } - }); - - return MA_SUCCESS; -} - -static ma_result ma_context_init__webaudio(ma_context* pContext, const ma_context_config* pConfig, ma_backend_callbacks* pCallbacks) -{ - int resultFromJS; - - MA_ASSERT(pContext != NULL); - - (void)pConfig; /* Unused. */ - - /* Here is where our global JavaScript object is initialized. */ - resultFromJS = EM_ASM_INT({ - if (typeof window === 'undefined' || (window.AudioContext || window.webkitAudioContext) === undefined) { - return 0; /* Web Audio not supported. */ - } - - if (typeof(window.miniaudio) === 'undefined') { - window.miniaudio = { - referenceCount: 0 - }; - - /* Device types. */ - window.miniaudio.device_type = {}; - window.miniaudio.device_type.playback = $0; - window.miniaudio.device_type.capture = $1; - window.miniaudio.device_type.duplex = $2; - - /* Device states. */ - window.miniaudio.device_state = {}; - window.miniaudio.device_state.stopped = $3; - window.miniaudio.device_state.started = $4; - - /* Device cache for mapping devices to indexes for JavaScript/C interop. */ - let miniaudio = window.miniaudio; - miniaudio.devices = []; - - miniaudio.track_device = function(device) { - /* Try inserting into a free slot first. */ - for (var iDevice = 0; iDevice < miniaudio.devices.length; ++iDevice) { - if (miniaudio.devices[iDevice] == null) { - miniaudio.devices[iDevice] = device; - return iDevice; - } - } - - /* Getting here means there is no empty slots in the array so we just push to the end. */ - miniaudio.devices.push(device); - return miniaudio.devices.length - 1; - }; - - miniaudio.untrack_device_by_index = function(deviceIndex) { - /* We just set the device's slot to null. The slot will get reused in the next call to ma_track_device. */ - miniaudio.devices[deviceIndex] = null; - - /* Trim the array if possible. */ - while (miniaudio.devices.length > 0) { - if (miniaudio.devices[miniaudio.devices.length-1] == null) { - miniaudio.devices.pop(); - } else { - break; - } - } - }; - - miniaudio.untrack_device = function(device) { - for (var iDevice = 0; iDevice < miniaudio.devices.length; ++iDevice) { - if (miniaudio.devices[iDevice] == device) { - return miniaudio.untrack_device_by_index(iDevice); - } - } - }; - - miniaudio.get_device_by_index = function(deviceIndex) { - return miniaudio.devices[deviceIndex]; - }; - - miniaudio.unlock_event_types = (function(){ - return ['touchend', 'click']; - })(); - - miniaudio.unlock = function() { - for(var i = 0; i < miniaudio.devices.length; ++i) { - var device = miniaudio.devices[i]; - if (device != null && - device.webaudio != null && - device.state === miniaudio.device_state.started) { - - device.webaudio.resume().then(() => { - _ma_device__on_notification_unlocked(device.pDevice); - }, - (error) => {console.error("Failed to resume audiocontext", error); - }); - } - } - miniaudio.unlock_event_types.map(function(event_type) { - document.removeEventListener(event_type, miniaudio.unlock, true); - }); - }; - - miniaudio.unlock_event_types.map(function(event_type) { - document.addEventListener(event_type, miniaudio.unlock, true); - }); - } - - window.miniaudio.referenceCount += 1; - - return 1; - }, ma_device_type_playback, ma_device_type_capture, ma_device_type_duplex, ma_device_state_stopped, ma_device_state_started); - - if (resultFromJS != 1) { - return MA_FAILED_TO_INIT_BACKEND; - } - - pCallbacks->onContextInit = ma_context_init__webaudio; - pCallbacks->onContextUninit = ma_context_uninit__webaudio; - pCallbacks->onContextEnumerateDevices = ma_context_enumerate_devices__webaudio; - pCallbacks->onContextGetDeviceInfo = ma_context_get_device_info__webaudio; - pCallbacks->onDeviceInit = ma_device_init__webaudio; - pCallbacks->onDeviceUninit = ma_device_uninit__webaudio; - pCallbacks->onDeviceStart = ma_device_start__webaudio; - pCallbacks->onDeviceStop = ma_device_stop__webaudio; - pCallbacks->onDeviceRead = NULL; /* Not needed because WebAudio is asynchronous. */ - pCallbacks->onDeviceWrite = NULL; /* Not needed because WebAudio is asynchronous. */ - pCallbacks->onDeviceDataLoop = NULL; /* Not needed because WebAudio is asynchronous. */ - - return MA_SUCCESS; -} -#endif /* MA_HAS_WEBAUDIO */ - - - -static ma_bool32 ma__is_channel_map_valid(const ma_channel* pChannelMap, ma_uint32 channels) -{ - /* A blank channel map should be allowed, in which case it should use an appropriate default which will depend on context. */ - if (pChannelMap != NULL && pChannelMap[0] != MA_CHANNEL_NONE) { - ma_uint32 iChannel; - - if (channels == 0 || channels > MA_MAX_CHANNELS) { - return MA_FALSE; /* Channel count out of range. */ - } - - /* A channel cannot be present in the channel map more than once. */ - for (iChannel = 0; iChannel < channels; ++iChannel) { - ma_uint32 jChannel; - for (jChannel = iChannel + 1; jChannel < channels; ++jChannel) { - if (pChannelMap[iChannel] == pChannelMap[jChannel]) { - return MA_FALSE; - } - } - } - } - - return MA_TRUE; -} - - -static ma_bool32 ma_context_is_backend_asynchronous(ma_context* pContext) -{ - MA_ASSERT(pContext != NULL); - - if (pContext->callbacks.onDeviceRead == NULL && pContext->callbacks.onDeviceWrite == NULL) { - if (pContext->callbacks.onDeviceDataLoop == NULL) { - return MA_TRUE; - } else { - return MA_FALSE; - } - } else { - return MA_FALSE; - } -} - - -static ma_result ma_device__post_init_setup(ma_device* pDevice, ma_device_type deviceType) -{ - ma_result result; - - MA_ASSERT(pDevice != NULL); - - if (deviceType == ma_device_type_capture || deviceType == ma_device_type_duplex || deviceType == ma_device_type_loopback) { - if (pDevice->capture.format == ma_format_unknown) { - pDevice->capture.format = pDevice->capture.internalFormat; - } - if (pDevice->capture.channels == 0) { - pDevice->capture.channels = pDevice->capture.internalChannels; - } - if (pDevice->capture.channelMap[0] == MA_CHANNEL_NONE) { - MA_ASSERT(pDevice->capture.channels <= MA_MAX_CHANNELS); - if (pDevice->capture.internalChannels == pDevice->capture.channels) { - ma_channel_map_copy(pDevice->capture.channelMap, pDevice->capture.internalChannelMap, pDevice->capture.channels); - } else { - if (pDevice->capture.channelMixMode == ma_channel_mix_mode_simple) { - ma_channel_map_init_blank(pDevice->capture.channelMap, pDevice->capture.channels); - } else { - ma_channel_map_init_standard(ma_standard_channel_map_default, pDevice->capture.channelMap, ma_countof(pDevice->capture.channelMap), pDevice->capture.channels); - } - } - } - } - - if (deviceType == ma_device_type_playback || deviceType == ma_device_type_duplex) { - if (pDevice->playback.format == ma_format_unknown) { - pDevice->playback.format = pDevice->playback.internalFormat; - } - if (pDevice->playback.channels == 0) { - pDevice->playback.channels = pDevice->playback.internalChannels; - } - if (pDevice->playback.channelMap[0] == MA_CHANNEL_NONE) { - MA_ASSERT(pDevice->playback.channels <= MA_MAX_CHANNELS); - if (pDevice->playback.internalChannels == pDevice->playback.channels) { - ma_channel_map_copy(pDevice->playback.channelMap, pDevice->playback.internalChannelMap, pDevice->playback.channels); - } else { - if (pDevice->playback.channelMixMode == ma_channel_mix_mode_simple) { - ma_channel_map_init_blank(pDevice->playback.channelMap, pDevice->playback.channels); - } else { - ma_channel_map_init_standard(ma_standard_channel_map_default, pDevice->playback.channelMap, ma_countof(pDevice->playback.channelMap), pDevice->playback.channels); - } - } - } - } - - if (pDevice->sampleRate == 0) { - if (deviceType == ma_device_type_capture || deviceType == ma_device_type_duplex || deviceType == ma_device_type_loopback) { - pDevice->sampleRate = pDevice->capture.internalSampleRate; - } else { - pDevice->sampleRate = pDevice->playback.internalSampleRate; - } - } - - /* Data converters. */ - if (deviceType == ma_device_type_capture || deviceType == ma_device_type_duplex || deviceType == ma_device_type_loopback) { - /* Converting from internal device format to client format. */ - ma_data_converter_config converterConfig = ma_data_converter_config_init_default(); - converterConfig.formatIn = pDevice->capture.internalFormat; - converterConfig.channelsIn = pDevice->capture.internalChannels; - converterConfig.sampleRateIn = pDevice->capture.internalSampleRate; - converterConfig.pChannelMapIn = pDevice->capture.internalChannelMap; - converterConfig.formatOut = pDevice->capture.format; - converterConfig.channelsOut = pDevice->capture.channels; - converterConfig.sampleRateOut = pDevice->sampleRate; - converterConfig.pChannelMapOut = pDevice->capture.channelMap; - converterConfig.channelMixMode = pDevice->capture.channelMixMode; - converterConfig.calculateLFEFromSpatialChannels = pDevice->capture.calculateLFEFromSpatialChannels; - converterConfig.allowDynamicSampleRate = MA_FALSE; - converterConfig.resampling.algorithm = pDevice->resampling.algorithm; - converterConfig.resampling.linear.lpfOrder = pDevice->resampling.linear.lpfOrder; - converterConfig.resampling.pBackendVTable = pDevice->resampling.pBackendVTable; - converterConfig.resampling.pBackendUserData = pDevice->resampling.pBackendUserData; - - /* Make sure the old converter is uninitialized first. */ - if (ma_device_get_state(pDevice) != ma_device_state_uninitialized) { - ma_data_converter_uninit(&pDevice->capture.converter, &pDevice->pContext->allocationCallbacks); - } - - result = ma_data_converter_init(&converterConfig, &pDevice->pContext->allocationCallbacks, &pDevice->capture.converter); - if (result != MA_SUCCESS) { - return result; - } - } - - if (deviceType == ma_device_type_playback || deviceType == ma_device_type_duplex) { - /* Converting from client format to device format. */ - ma_data_converter_config converterConfig = ma_data_converter_config_init_default(); - converterConfig.formatIn = pDevice->playback.format; - converterConfig.channelsIn = pDevice->playback.channels; - converterConfig.sampleRateIn = pDevice->sampleRate; - converterConfig.pChannelMapIn = pDevice->playback.channelMap; - converterConfig.formatOut = pDevice->playback.internalFormat; - converterConfig.channelsOut = pDevice->playback.internalChannels; - converterConfig.sampleRateOut = pDevice->playback.internalSampleRate; - converterConfig.pChannelMapOut = pDevice->playback.internalChannelMap; - converterConfig.channelMixMode = pDevice->playback.channelMixMode; - converterConfig.calculateLFEFromSpatialChannels = pDevice->playback.calculateLFEFromSpatialChannels; - converterConfig.allowDynamicSampleRate = MA_FALSE; - converterConfig.resampling.algorithm = pDevice->resampling.algorithm; - converterConfig.resampling.linear.lpfOrder = pDevice->resampling.linear.lpfOrder; - converterConfig.resampling.pBackendVTable = pDevice->resampling.pBackendVTable; - converterConfig.resampling.pBackendUserData = pDevice->resampling.pBackendUserData; - - /* Make sure the old converter is uninitialized first. */ - if (ma_device_get_state(pDevice) != ma_device_state_uninitialized) { - ma_data_converter_uninit(&pDevice->playback.converter, &pDevice->pContext->allocationCallbacks); - } - - result = ma_data_converter_init(&converterConfig, &pDevice->pContext->allocationCallbacks, &pDevice->playback.converter); - if (result != MA_SUCCESS) { - return result; - } - } - - - /* - If the device is doing playback (ma_device_type_playback or ma_device_type_duplex), there's - a couple of situations where we'll need a heap allocated cache. - - The first is a duplex device for backends that use a callback for data delivery. The reason - this is needed is that the input stage needs to have a buffer to place the input data while it - waits for the playback stage, after which the miniaudio data callback will get fired. This is - not needed for backends that use a blocking API because miniaudio manages temporary buffers on - the stack to achieve this. - - The other situation is when the data converter does not have the ability to query the number - of input frames that are required in order to process a given number of output frames. When - performing data conversion, it's useful if miniaudio know exactly how many frames it needs - from the client in order to generate a given number of output frames. This way, only exactly - the number of frames are needed to be read from the client which means no cache is necessary. - On the other hand, if miniaudio doesn't know how many frames to read, it is forced to read - in fixed sized chunks and then cache any residual unused input frames, those of which will be - processed at a later stage. - */ - if (deviceType == ma_device_type_playback || deviceType == ma_device_type_duplex) { - ma_uint64 unused; - - pDevice->playback.inputCacheConsumed = 0; - pDevice->playback.inputCacheRemaining = 0; - - if (pDevice->type == ma_device_type_duplex || /* Duplex. backend may decide to use ma_device_handle_backend_data_callback() which will require this cache. */ - ma_data_converter_get_required_input_frame_count(&pDevice->playback.converter, 1, &unused) != MA_SUCCESS) /* Data conversion required input frame calculation not supported. */ - { - /* We need a heap allocated cache. We want to size this based on the period size. */ - void* pNewInputCache; - ma_uint64 newInputCacheCap; - ma_uint64 newInputCacheSizeInBytes; - - newInputCacheCap = ma_calculate_frame_count_after_resampling(pDevice->playback.internalSampleRate, pDevice->sampleRate, pDevice->playback.internalPeriodSizeInFrames); - - newInputCacheSizeInBytes = newInputCacheCap * ma_get_bytes_per_frame(pDevice->playback.format, pDevice->playback.channels); - if (newInputCacheSizeInBytes > MA_SIZE_MAX) { - ma_free(pDevice->playback.pInputCache, &pDevice->pContext->allocationCallbacks); - pDevice->playback.pInputCache = NULL; - pDevice->playback.inputCacheCap = 0; - return MA_OUT_OF_MEMORY; /* Allocation too big. Should never hit this, but makes the cast below safer for 32-bit builds. */ - } - - pNewInputCache = ma_realloc(pDevice->playback.pInputCache, (size_t)newInputCacheSizeInBytes, &pDevice->pContext->allocationCallbacks); - if (pNewInputCache == NULL) { - ma_free(pDevice->playback.pInputCache, &pDevice->pContext->allocationCallbacks); - pDevice->playback.pInputCache = NULL; - pDevice->playback.inputCacheCap = 0; - return MA_OUT_OF_MEMORY; - } - - pDevice->playback.pInputCache = pNewInputCache; - pDevice->playback.inputCacheCap = newInputCacheCap; - } else { - /* Heap allocation not required. Make sure we clear out the old cache just in case this function was called in response to a route change. */ - ma_free(pDevice->playback.pInputCache, &pDevice->pContext->allocationCallbacks); - pDevice->playback.pInputCache = NULL; - pDevice->playback.inputCacheCap = 0; - } - } - - return MA_SUCCESS; -} - -MA_API ma_result ma_device_post_init(ma_device* pDevice, ma_device_type deviceType, const ma_device_descriptor* pDescriptorPlayback, const ma_device_descriptor* pDescriptorCapture) -{ - ma_result result; - - if (pDevice == NULL) { - return MA_INVALID_ARGS; - } - - /* Capture. */ - if (deviceType == ma_device_type_capture || deviceType == ma_device_type_duplex || deviceType == ma_device_type_loopback) { - if (ma_device_descriptor_is_valid(pDescriptorCapture) == MA_FALSE) { - return MA_INVALID_ARGS; - } - - pDevice->capture.internalFormat = pDescriptorCapture->format; - pDevice->capture.internalChannels = pDescriptorCapture->channels; - pDevice->capture.internalSampleRate = pDescriptorCapture->sampleRate; - MA_COPY_MEMORY(pDevice->capture.internalChannelMap, pDescriptorCapture->channelMap, sizeof(pDescriptorCapture->channelMap)); - pDevice->capture.internalPeriodSizeInFrames = pDescriptorCapture->periodSizeInFrames; - pDevice->capture.internalPeriods = pDescriptorCapture->periodCount; - - if (pDevice->capture.internalPeriodSizeInFrames == 0) { - pDevice->capture.internalPeriodSizeInFrames = ma_calculate_buffer_size_in_frames_from_milliseconds(pDescriptorCapture->periodSizeInMilliseconds, pDescriptorCapture->sampleRate); - } - } - - /* Playback. */ - if (deviceType == ma_device_type_playback || deviceType == ma_device_type_duplex) { - if (ma_device_descriptor_is_valid(pDescriptorPlayback) == MA_FALSE) { - return MA_INVALID_ARGS; - } - - pDevice->playback.internalFormat = pDescriptorPlayback->format; - pDevice->playback.internalChannels = pDescriptorPlayback->channels; - pDevice->playback.internalSampleRate = pDescriptorPlayback->sampleRate; - MA_COPY_MEMORY(pDevice->playback.internalChannelMap, pDescriptorPlayback->channelMap, sizeof(pDescriptorPlayback->channelMap)); - pDevice->playback.internalPeriodSizeInFrames = pDescriptorPlayback->periodSizeInFrames; - pDevice->playback.internalPeriods = pDescriptorPlayback->periodCount; - - if (pDevice->playback.internalPeriodSizeInFrames == 0) { - pDevice->playback.internalPeriodSizeInFrames = ma_calculate_buffer_size_in_frames_from_milliseconds(pDescriptorPlayback->periodSizeInMilliseconds, pDescriptorPlayback->sampleRate); - } - } - - /* - The name of the device can be retrieved from device info. This may be temporary and replaced with a `ma_device_get_info(pDevice, deviceType)` instead. - For loopback devices, we need to retrieve the name of the playback device. - */ - { - ma_device_info deviceInfo; - - if (deviceType == ma_device_type_capture || deviceType == ma_device_type_duplex || deviceType == ma_device_type_loopback) { - result = ma_device_get_info(pDevice, ma_device_type_capture, &deviceInfo); - if (result == MA_SUCCESS) { - ma_strncpy_s(pDevice->capture.name, sizeof(pDevice->capture.name), deviceInfo.name, (size_t)-1); - } else { - /* We failed to retrieve the device info. Fall back to a default name. */ - if (pDescriptorCapture->pDeviceID == NULL) { - ma_strncpy_s(pDevice->capture.name, sizeof(pDevice->capture.name), MA_DEFAULT_CAPTURE_DEVICE_NAME, (size_t)-1); - } else { - ma_strncpy_s(pDevice->capture.name, sizeof(pDevice->capture.name), "Capture Device", (size_t)-1); - } - } - } - - if (deviceType == ma_device_type_playback || deviceType == ma_device_type_duplex) { - result = ma_device_get_info(pDevice, ma_device_type_playback, &deviceInfo); - if (result == MA_SUCCESS) { - ma_strncpy_s(pDevice->playback.name, sizeof(pDevice->playback.name), deviceInfo.name, (size_t)-1); - } else { - /* We failed to retrieve the device info. Fall back to a default name. */ - if (pDescriptorPlayback->pDeviceID == NULL) { - ma_strncpy_s(pDevice->playback.name, sizeof(pDevice->playback.name), MA_DEFAULT_PLAYBACK_DEVICE_NAME, (size_t)-1); - } else { - ma_strncpy_s(pDevice->playback.name, sizeof(pDevice->playback.name), "Playback Device", (size_t)-1); - } - } - } - } - - /* Update data conversion. */ - return ma_device__post_init_setup(pDevice, deviceType); /* TODO: Should probably rename ma_device__post_init_setup() to something better. */ -} - - -static ma_thread_result MA_THREADCALL ma_worker_thread(void* pData) -{ - ma_device* pDevice = (ma_device*)pData; -#ifdef MA_WIN32 - HRESULT CoInitializeResult; -#endif - - MA_ASSERT(pDevice != NULL); - -#ifdef MA_WIN32 - CoInitializeResult = ma_CoInitializeEx(pDevice->pContext, NULL, MA_COINIT_VALUE); -#endif - - /* - When the device is being initialized its initial state is set to ma_device_state_uninitialized. Before returning from - ma_device_init(), the state needs to be set to something valid. In miniaudio the device's default state immediately - after initialization is stopped, so therefore we need to mark the device as such. miniaudio will wait on the worker - thread to signal an event to know when the worker thread is ready for action. - */ - ma_device__set_state(pDevice, ma_device_state_stopped); - ma_event_signal(&pDevice->stopEvent); - - for (;;) { /* <-- This loop just keeps the thread alive. The main audio loop is inside. */ - ma_result startResult; - ma_result stopResult; /* <-- This will store the result from onDeviceStop(). If it returns an error, we don't fire the stopped notification callback. */ - - /* We wait on an event to know when something has requested that the device be started and the main loop entered. */ - ma_event_wait(&pDevice->wakeupEvent); - - /* Default result code. */ - pDevice->workResult = MA_SUCCESS; - - /* If the reason for the wake up is that we are terminating, just break from the loop. */ - if (ma_device_get_state(pDevice) == ma_device_state_uninitialized) { - break; - } - - /* - Getting to this point means the device is wanting to get started. The function that has requested that the device - be started will be waiting on an event (pDevice->startEvent) which means we need to make sure we signal the event - in both the success and error case. It's important that the state of the device is set _before_ signaling the event. - */ - MA_ASSERT(ma_device_get_state(pDevice) == ma_device_state_starting); - - /* If the device has a start callback, start it now. */ - if (pDevice->pContext->callbacks.onDeviceStart != NULL) { - startResult = pDevice->pContext->callbacks.onDeviceStart(pDevice); - } else { - startResult = MA_SUCCESS; - } - - /* - If starting was not successful we'll need to loop back to the start and wait for something - to happen (pDevice->wakeupEvent). - */ - if (startResult != MA_SUCCESS) { - pDevice->workResult = startResult; - ma_event_signal(&pDevice->startEvent); /* <-- Always signal the start event so ma_device_start() can return as it'll be waiting on it. */ - continue; - } - - /* Make sure the state is set appropriately. */ - ma_device__set_state(pDevice, ma_device_state_started); /* <-- Set this before signaling the event so that the state is always guaranteed to be good after ma_device_start() has returned. */ - ma_event_signal(&pDevice->startEvent); - - ma_device__on_notification_started(pDevice); - - if (pDevice->pContext->callbacks.onDeviceDataLoop != NULL) { - pDevice->pContext->callbacks.onDeviceDataLoop(pDevice); - } else { - /* The backend is not using a custom main loop implementation, so now fall back to the blocking read-write implementation. */ - ma_device_audio_thread__default_read_write(pDevice); - } - - /* Getting here means we have broken from the main loop which happens the application has requested that device be stopped. */ - if (pDevice->pContext->callbacks.onDeviceStop != NULL) { - stopResult = pDevice->pContext->callbacks.onDeviceStop(pDevice); - } else { - stopResult = MA_SUCCESS; /* No stop callback with the backend. Just assume successful. */ - } - - /* - After the device has stopped, make sure an event is posted. Don't post a stopped event if - stopping failed. This can happen on some backends when the underlying stream has been - stopped due to the device being physically unplugged or disabled via an OS setting. - */ - if (stopResult == MA_SUCCESS) { - ma_device__on_notification_stopped(pDevice); - } - - /* If we stopped because the device has been uninitialized, abort now. */ - if (ma_device_get_state(pDevice) == ma_device_state_uninitialized) { - break; - } - - /* A function somewhere is waiting for the device to have stopped for real so we need to signal an event to allow it to continue. */ - ma_device__set_state(pDevice, ma_device_state_stopped); - ma_event_signal(&pDevice->stopEvent); - } - -#ifdef MA_WIN32 - if (CoInitializeResult == S_OK) { - ma_CoUninitialize(pDevice->pContext); - } -#endif - - return (ma_thread_result)0; -} - - -/* Helper for determining whether or not the given device is initialized. */ -static ma_bool32 ma_device__is_initialized(ma_device* pDevice) -{ - if (pDevice == NULL) { - return MA_FALSE; - } - - return ma_device_get_state(pDevice) != ma_device_state_uninitialized; -} - - -#ifdef MA_WIN32 -static ma_result ma_context_uninit_backend_apis__win32(ma_context* pContext) -{ - /* For some reason UWP complains when CoUninitialize() is called. I'm just not going to call it on UWP. */ -#if defined(MA_WIN32_DESKTOP) || defined(MA_WIN32_GDK) - if (pContext->win32.CoInitializeResult == S_OK) { - ma_CoUninitialize(pContext); - } - - #if defined(MA_WIN32_DESKTOP) - ma_dlclose(ma_context_get_log(pContext), pContext->win32.hUser32DLL); - ma_dlclose(ma_context_get_log(pContext), pContext->win32.hAdvapi32DLL); - #endif - - ma_dlclose(ma_context_get_log(pContext), pContext->win32.hOle32DLL); -#else - (void)pContext; -#endif - - return MA_SUCCESS; -} - -static ma_result ma_context_init_backend_apis__win32(ma_context* pContext) -{ -#if defined(MA_WIN32_DESKTOP) || defined(MA_WIN32_GDK) - #if defined(MA_WIN32_DESKTOP) - /* User32.dll */ - pContext->win32.hUser32DLL = ma_dlopen(ma_context_get_log(pContext), "user32.dll"); - if (pContext->win32.hUser32DLL == NULL) { - return MA_FAILED_TO_INIT_BACKEND; - } - - pContext->win32.GetForegroundWindow = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->win32.hUser32DLL, "GetForegroundWindow"); - pContext->win32.GetDesktopWindow = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->win32.hUser32DLL, "GetDesktopWindow"); - - - /* Advapi32.dll */ - pContext->win32.hAdvapi32DLL = ma_dlopen(ma_context_get_log(pContext), "advapi32.dll"); - if (pContext->win32.hAdvapi32DLL == NULL) { - return MA_FAILED_TO_INIT_BACKEND; - } - - pContext->win32.RegOpenKeyExA = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->win32.hAdvapi32DLL, "RegOpenKeyExA"); - pContext->win32.RegCloseKey = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->win32.hAdvapi32DLL, "RegCloseKey"); - pContext->win32.RegQueryValueExA = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->win32.hAdvapi32DLL, "RegQueryValueExA"); - #endif - - /* Ole32.dll */ - pContext->win32.hOle32DLL = ma_dlopen(ma_context_get_log(pContext), "ole32.dll"); - if (pContext->win32.hOle32DLL == NULL) { - return MA_FAILED_TO_INIT_BACKEND; - } - - pContext->win32.CoInitialize = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->win32.hOle32DLL, "CoInitialize"); - pContext->win32.CoInitializeEx = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->win32.hOle32DLL, "CoInitializeEx"); - pContext->win32.CoUninitialize = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->win32.hOle32DLL, "CoUninitialize"); - pContext->win32.CoCreateInstance = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->win32.hOle32DLL, "CoCreateInstance"); - pContext->win32.CoTaskMemFree = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->win32.hOle32DLL, "CoTaskMemFree"); - pContext->win32.PropVariantClear = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->win32.hOle32DLL, "PropVariantClear"); - pContext->win32.StringFromGUID2 = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->win32.hOle32DLL, "StringFromGUID2"); -#else - (void)pContext; /* Unused. */ -#endif - - pContext->win32.CoInitializeResult = ma_CoInitializeEx(pContext, NULL, MA_COINIT_VALUE); - return MA_SUCCESS; -} -#else -static ma_result ma_context_uninit_backend_apis__nix(ma_context* pContext) -{ - (void)pContext; - - return MA_SUCCESS; -} - -static ma_result ma_context_init_backend_apis__nix(ma_context* pContext) -{ - (void)pContext; - - return MA_SUCCESS; -} -#endif - -static ma_result ma_context_init_backend_apis(ma_context* pContext) -{ - ma_result result; -#ifdef MA_WIN32 - result = ma_context_init_backend_apis__win32(pContext); -#else - result = ma_context_init_backend_apis__nix(pContext); -#endif - - return result; -} - -static ma_result ma_context_uninit_backend_apis(ma_context* pContext) -{ - ma_result result; -#ifdef MA_WIN32 - result = ma_context_uninit_backend_apis__win32(pContext); -#else - result = ma_context_uninit_backend_apis__nix(pContext); -#endif - - return result; -} - - -/* The default capacity doesn't need to be too big. */ -#ifndef MA_DEFAULT_DEVICE_JOB_QUEUE_CAPACITY -#define MA_DEFAULT_DEVICE_JOB_QUEUE_CAPACITY 32 -#endif - -MA_API ma_device_job_thread_config ma_device_job_thread_config_init(void) -{ - ma_device_job_thread_config config; - - MA_ZERO_OBJECT(&config); - config.noThread = MA_FALSE; - config.jobQueueCapacity = MA_DEFAULT_DEVICE_JOB_QUEUE_CAPACITY; - config.jobQueueFlags = 0; - - return config; -} - - -static ma_thread_result MA_THREADCALL ma_device_job_thread_entry(void* pUserData) -{ - ma_device_job_thread* pJobThread = (ma_device_job_thread*)pUserData; - MA_ASSERT(pJobThread != NULL); - - for (;;) { - ma_result result; - ma_job job; - - result = ma_device_job_thread_next(pJobThread, &job); - if (result != MA_SUCCESS) { - break; - } - - if (job.toc.breakup.code == MA_JOB_TYPE_QUIT) { - break; - } - - ma_job_process(&job); - } - - return (ma_thread_result)0; -} - -MA_API ma_result ma_device_job_thread_init(const ma_device_job_thread_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_device_job_thread* pJobThread) -{ - ma_result result; - ma_job_queue_config jobQueueConfig; - - if (pJobThread == NULL) { - return MA_INVALID_ARGS; - } - - MA_ZERO_OBJECT(pJobThread); - - if (pConfig == NULL) { - return MA_INVALID_ARGS; - } - - - /* Initialize the job queue before the thread to ensure it's in a valid state. */ - jobQueueConfig = ma_job_queue_config_init(pConfig->jobQueueFlags, pConfig->jobQueueCapacity); - - result = ma_job_queue_init(&jobQueueConfig, pAllocationCallbacks, &pJobThread->jobQueue); - if (result != MA_SUCCESS) { - return result; /* Failed to initialize job queue. */ - } - - - /* The thread needs to be initialized after the job queue to ensure the thread doesn't try to access it prematurely. */ - if (pConfig->noThread == MA_FALSE) { - result = ma_thread_create(&pJobThread->thread, ma_thread_priority_normal, 0, ma_device_job_thread_entry, pJobThread, pAllocationCallbacks); - if (result != MA_SUCCESS) { - ma_job_queue_uninit(&pJobThread->jobQueue, pAllocationCallbacks); - return result; /* Failed to create the job thread. */ - } - - pJobThread->_hasThread = MA_TRUE; - } else { - pJobThread->_hasThread = MA_FALSE; - } - - - return MA_SUCCESS; -} - -MA_API void ma_device_job_thread_uninit(ma_device_job_thread* pJobThread, const ma_allocation_callbacks* pAllocationCallbacks) -{ - if (pJobThread == NULL) { - return; - } - - /* The first thing to do is post a quit message to the job queue. If we're using a thread we'll need to wait for it. */ - { - ma_job job = ma_job_init(MA_JOB_TYPE_QUIT); - ma_device_job_thread_post(pJobThread, &job); - } - - /* Wait for the thread to terminate naturally. */ - if (pJobThread->_hasThread) { - ma_thread_wait(&pJobThread->thread); - } - - /* At this point the thread should be terminated so we can safely uninitialize the job queue. */ - ma_job_queue_uninit(&pJobThread->jobQueue, pAllocationCallbacks); -} - -MA_API ma_result ma_device_job_thread_post(ma_device_job_thread* pJobThread, const ma_job* pJob) -{ - if (pJobThread == NULL || pJob == NULL) { - return MA_INVALID_ARGS; - } - - return ma_job_queue_post(&pJobThread->jobQueue, pJob); -} - -MA_API ma_result ma_device_job_thread_next(ma_device_job_thread* pJobThread, ma_job* pJob) -{ - if (pJob == NULL) { - return MA_INVALID_ARGS; - } - - MA_ZERO_OBJECT(pJob); - - if (pJobThread == NULL) { - return MA_INVALID_ARGS; - } - - return ma_job_queue_next(&pJobThread->jobQueue, pJob); -} - - -MA_API ma_bool32 ma_device_id_equal(const ma_device_id* pA, const ma_device_id* pB) -{ - size_t i; - - if (pA == NULL || pB == NULL) { - return MA_FALSE; - } - - for (i = 0; i < sizeof(ma_device_id); i += 1) { - if (((const char*)pA)[i] != ((const char*)pB)[i]) { - return MA_FALSE; - } - } - - return MA_TRUE; -} - - - -MA_API ma_context_config ma_context_config_init(void) -{ - ma_context_config config; - MA_ZERO_OBJECT(&config); - - return config; -} - -MA_API ma_result ma_context_init(const ma_backend backends[], ma_uint32 backendCount, const ma_context_config* pConfig, ma_context* pContext) -{ - ma_result result; - ma_context_config defaultConfig; - ma_backend defaultBackends[ma_backend_null+1]; - ma_uint32 iBackend; - ma_backend* pBackendsToIterate; - ma_uint32 backendsToIterateCount; - - if (pContext == NULL) { - return MA_INVALID_ARGS; - } - - MA_ZERO_OBJECT(pContext); - - /* Always make sure the config is set first to ensure properties are available as soon as possible. */ - if (pConfig == NULL) { - defaultConfig = ma_context_config_init(); - pConfig = &defaultConfig; - } - - /* Allocation callbacks need to come first because they'll be passed around to other areas. */ - result = ma_allocation_callbacks_init_copy(&pContext->allocationCallbacks, &pConfig->allocationCallbacks); - if (result != MA_SUCCESS) { - return result; - } - - /* Get a lot set up first so we can start logging ASAP. */ - if (pConfig->pLog != NULL) { - pContext->pLog = pConfig->pLog; - } else { - result = ma_log_init(&pContext->allocationCallbacks, &pContext->log); - if (result == MA_SUCCESS) { - pContext->pLog = &pContext->log; - } else { - pContext->pLog = NULL; /* Logging is not available. */ - } - } - - pContext->threadPriority = pConfig->threadPriority; - pContext->threadStackSize = pConfig->threadStackSize; - pContext->pUserData = pConfig->pUserData; - - /* Backend APIs need to be initialized first. This is where external libraries will be loaded and linked. */ - result = ma_context_init_backend_apis(pContext); - if (result != MA_SUCCESS) { - return result; - } - - for (iBackend = 0; iBackend <= ma_backend_null; ++iBackend) { - defaultBackends[iBackend] = (ma_backend)iBackend; - } - - pBackendsToIterate = (ma_backend*)backends; - backendsToIterateCount = backendCount; - if (pBackendsToIterate == NULL) { - pBackendsToIterate = (ma_backend*)defaultBackends; - backendsToIterateCount = ma_countof(defaultBackends); - } - - MA_ASSERT(pBackendsToIterate != NULL); - - for (iBackend = 0; iBackend < backendsToIterateCount; iBackend += 1) { - ma_backend backend = pBackendsToIterate[iBackend]; - - /* Make sure all callbacks are reset so we don't accidentally drag in any from previously failed initialization attempts. */ - MA_ZERO_OBJECT(&pContext->callbacks); - - /* These backends are using the new callback system. */ - switch (backend) { - #ifdef MA_HAS_WASAPI - case ma_backend_wasapi: - { - pContext->callbacks.onContextInit = ma_context_init__wasapi; - } break; - #endif - #ifdef MA_HAS_DSOUND - case ma_backend_dsound: - { - pContext->callbacks.onContextInit = ma_context_init__dsound; - } break; - #endif - #ifdef MA_HAS_WINMM - case ma_backend_winmm: - { - pContext->callbacks.onContextInit = ma_context_init__winmm; - } break; - #endif - #ifdef MA_HAS_COREAUDIO - case ma_backend_coreaudio: - { - pContext->callbacks.onContextInit = ma_context_init__coreaudio; - } break; - #endif - #ifdef MA_HAS_SNDIO - case ma_backend_sndio: - { - pContext->callbacks.onContextInit = ma_context_init__sndio; - } break; - #endif - #ifdef MA_HAS_AUDIO4 - case ma_backend_audio4: - { - pContext->callbacks.onContextInit = ma_context_init__audio4; - } break; - #endif - #ifdef MA_HAS_OSS - case ma_backend_oss: - { - pContext->callbacks.onContextInit = ma_context_init__oss; - } break; - #endif - #ifdef MA_HAS_PULSEAUDIO - case ma_backend_pulseaudio: - { - pContext->callbacks.onContextInit = ma_context_init__pulse; - } break; - #endif - #ifdef MA_HAS_ALSA - case ma_backend_alsa: - { - pContext->callbacks.onContextInit = ma_context_init__alsa; - } break; - #endif - #ifdef MA_HAS_JACK - case ma_backend_jack: - { - pContext->callbacks.onContextInit = ma_context_init__jack; - } break; - #endif - #ifdef MA_HAS_AAUDIO - case ma_backend_aaudio: - { - if (ma_is_backend_enabled(backend)) { - pContext->callbacks.onContextInit = ma_context_init__aaudio; - } - } break; - #endif - #ifdef MA_HAS_OPENSL - case ma_backend_opensl: - { - if (ma_is_backend_enabled(backend)) { - pContext->callbacks.onContextInit = ma_context_init__opensl; - } - } break; - #endif - #ifdef MA_HAS_WEBAUDIO - case ma_backend_webaudio: - { - pContext->callbacks.onContextInit = ma_context_init__webaudio; - } break; - #endif - #ifdef MA_HAS_CUSTOM - case ma_backend_custom: - { - /* Slightly different logic for custom backends. Custom backends can optionally set all of their callbacks in the config. */ - pContext->callbacks = pConfig->custom; - } break; - #endif - #ifdef MA_HAS_NULL - case ma_backend_null: - { - pContext->callbacks.onContextInit = ma_context_init__null; - } break; - #endif - - default: break; - } - - if (pContext->callbacks.onContextInit != NULL) { - ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_DEBUG, "Attempting to initialize %s backend...\n", ma_get_backend_name(backend)); - result = pContext->callbacks.onContextInit(pContext, pConfig, &pContext->callbacks); - } else { - /* Getting here means the onContextInit callback is not set which means the backend is not enabled. Special case for the custom backend. */ - if (backend != ma_backend_custom) { - result = MA_BACKEND_NOT_ENABLED; - } else { - #if !defined(MA_HAS_CUSTOM) - result = MA_BACKEND_NOT_ENABLED; - #else - result = MA_NO_BACKEND; - #endif - } - } - - /* If this iteration was successful, return. */ - if (result == MA_SUCCESS) { - result = ma_mutex_init(&pContext->deviceEnumLock); - if (result != MA_SUCCESS) { - ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_WARNING, "Failed to initialize mutex for device enumeration. ma_context_get_devices() is not thread safe.\n"); - } - - result = ma_mutex_init(&pContext->deviceInfoLock); - if (result != MA_SUCCESS) { - ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_WARNING, "Failed to initialize mutex for device info retrieval. ma_context_get_device_info() is not thread safe.\n"); - } - - ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_DEBUG, "System Architecture:\n"); - ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_DEBUG, " Endian: %s\n", ma_is_little_endian() ? "LE" : "BE"); - ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_DEBUG, " SSE2: %s\n", ma_has_sse2() ? "YES" : "NO"); - ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_DEBUG, " AVX2: %s\n", ma_has_avx2() ? "YES" : "NO"); - ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_DEBUG, " NEON: %s\n", ma_has_neon() ? "YES" : "NO"); - - pContext->backend = backend; - return result; - } else { - if (result == MA_BACKEND_NOT_ENABLED) { - ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_DEBUG, "%s backend is disabled.\n", ma_get_backend_name(backend)); - } else { - ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_DEBUG, "Failed to initialize %s backend.\n", ma_get_backend_name(backend)); - } - } - } - - /* If we get here it means an error occurred. */ - MA_ZERO_OBJECT(pContext); /* Safety. */ - return MA_NO_BACKEND; -} - -MA_API ma_result ma_context_uninit(ma_context* pContext) -{ - if (pContext == NULL) { - return MA_INVALID_ARGS; - } - - if (pContext->callbacks.onContextUninit != NULL) { - pContext->callbacks.onContextUninit(pContext); - } - - ma_mutex_uninit(&pContext->deviceEnumLock); - ma_mutex_uninit(&pContext->deviceInfoLock); - ma_free(pContext->pDeviceInfos, &pContext->allocationCallbacks); - ma_context_uninit_backend_apis(pContext); - - if (pContext->pLog == &pContext->log) { - ma_log_uninit(&pContext->log); - } - - return MA_SUCCESS; -} - -MA_API size_t ma_context_sizeof(void) -{ - return sizeof(ma_context); -} - - -MA_API ma_log* ma_context_get_log(ma_context* pContext) -{ - if (pContext == NULL) { - return NULL; - } - - return pContext->pLog; -} - - -MA_API ma_result ma_context_enumerate_devices(ma_context* pContext, ma_enum_devices_callback_proc callback, void* pUserData) -{ - ma_result result; - - if (pContext == NULL || callback == NULL) { - return MA_INVALID_ARGS; - } - - if (pContext->callbacks.onContextEnumerateDevices == NULL) { - return MA_INVALID_OPERATION; - } - - ma_mutex_lock(&pContext->deviceEnumLock); - { - result = pContext->callbacks.onContextEnumerateDevices(pContext, callback, pUserData); - } - ma_mutex_unlock(&pContext->deviceEnumLock); - - return result; -} - - -static ma_bool32 ma_context_get_devices__enum_callback(ma_context* pContext, ma_device_type deviceType, const ma_device_info* pInfo, void* pUserData) -{ - /* - We need to insert the device info into our main internal buffer. Where it goes depends on the device type. If it's a capture device - it's just appended to the end. If it's a playback device it's inserted just before the first capture device. - */ - - /* - First make sure we have room. Since the number of devices we add to the list is usually relatively small I've decided to use a - simple fixed size increment for buffer expansion. - */ - const ma_uint32 bufferExpansionCount = 2; - const ma_uint32 totalDeviceInfoCount = pContext->playbackDeviceInfoCount + pContext->captureDeviceInfoCount; - - if (totalDeviceInfoCount >= pContext->deviceInfoCapacity) { - ma_uint32 newCapacity = pContext->deviceInfoCapacity + bufferExpansionCount; - ma_device_info* pNewInfos = (ma_device_info*)ma_realloc(pContext->pDeviceInfos, sizeof(*pContext->pDeviceInfos)*newCapacity, &pContext->allocationCallbacks); - if (pNewInfos == NULL) { - return MA_FALSE; /* Out of memory. */ - } - - pContext->pDeviceInfos = pNewInfos; - pContext->deviceInfoCapacity = newCapacity; - } - - if (deviceType == ma_device_type_playback) { - /* Playback. Insert just before the first capture device. */ - - /* The first thing to do is move all of the capture devices down a slot. */ - ma_uint32 iFirstCaptureDevice = pContext->playbackDeviceInfoCount; - size_t iCaptureDevice; - for (iCaptureDevice = totalDeviceInfoCount; iCaptureDevice > iFirstCaptureDevice; --iCaptureDevice) { - pContext->pDeviceInfos[iCaptureDevice] = pContext->pDeviceInfos[iCaptureDevice-1]; - } - - /* Now just insert where the first capture device was before moving it down a slot. */ - pContext->pDeviceInfos[iFirstCaptureDevice] = *pInfo; - pContext->playbackDeviceInfoCount += 1; - } else { - /* Capture. Insert at the end. */ - pContext->pDeviceInfos[totalDeviceInfoCount] = *pInfo; - pContext->captureDeviceInfoCount += 1; - } - - (void)pUserData; - return MA_TRUE; -} - -MA_API ma_result ma_context_get_devices(ma_context* pContext, ma_device_info** ppPlaybackDeviceInfos, ma_uint32* pPlaybackDeviceCount, ma_device_info** ppCaptureDeviceInfos, ma_uint32* pCaptureDeviceCount) -{ - ma_result result; - - /* Safety. */ - if (ppPlaybackDeviceInfos != NULL) *ppPlaybackDeviceInfos = NULL; - if (pPlaybackDeviceCount != NULL) *pPlaybackDeviceCount = 0; - if (ppCaptureDeviceInfos != NULL) *ppCaptureDeviceInfos = NULL; - if (pCaptureDeviceCount != NULL) *pCaptureDeviceCount = 0; - - if (pContext == NULL) { - return MA_INVALID_ARGS; - } - - if (pContext->callbacks.onContextEnumerateDevices == NULL) { - return MA_INVALID_OPERATION; - } - - /* Note that we don't use ma_context_enumerate_devices() here because we want to do locking at a higher level. */ - ma_mutex_lock(&pContext->deviceEnumLock); - { - /* Reset everything first. */ - pContext->playbackDeviceInfoCount = 0; - pContext->captureDeviceInfoCount = 0; - - /* Now enumerate over available devices. */ - result = pContext->callbacks.onContextEnumerateDevices(pContext, ma_context_get_devices__enum_callback, NULL); - if (result == MA_SUCCESS) { - /* Playback devices. */ - if (ppPlaybackDeviceInfos != NULL) { - *ppPlaybackDeviceInfos = pContext->pDeviceInfos; - } - if (pPlaybackDeviceCount != NULL) { - *pPlaybackDeviceCount = pContext->playbackDeviceInfoCount; - } - - /* Capture devices. */ - if (ppCaptureDeviceInfos != NULL) { - *ppCaptureDeviceInfos = pContext->pDeviceInfos; - /* Capture devices come after playback devices. */ - if (pContext->playbackDeviceInfoCount > 0) { - /* Conditional, because NULL+0 is undefined behavior. */ - *ppCaptureDeviceInfos += pContext->playbackDeviceInfoCount; - } - } - if (pCaptureDeviceCount != NULL) { - *pCaptureDeviceCount = pContext->captureDeviceInfoCount; - } - } - } - ma_mutex_unlock(&pContext->deviceEnumLock); - - return result; -} - -MA_API ma_result ma_context_get_device_info(ma_context* pContext, ma_device_type deviceType, const ma_device_id* pDeviceID, ma_device_info* pDeviceInfo) -{ - ma_result result; - ma_device_info deviceInfo; - - /* NOTE: Do not clear pDeviceInfo on entry. The reason is the pDeviceID may actually point to pDeviceInfo->id which will break things. */ - if (pContext == NULL || pDeviceInfo == NULL) { - return MA_INVALID_ARGS; - } - - MA_ZERO_OBJECT(&deviceInfo); - - /* Help the backend out by copying over the device ID if we have one. */ - if (pDeviceID != NULL) { - MA_COPY_MEMORY(&deviceInfo.id, pDeviceID, sizeof(*pDeviceID)); - } - - if (pContext->callbacks.onContextGetDeviceInfo == NULL) { - return MA_INVALID_OPERATION; - } - - ma_mutex_lock(&pContext->deviceInfoLock); - { - result = pContext->callbacks.onContextGetDeviceInfo(pContext, deviceType, pDeviceID, &deviceInfo); - } - ma_mutex_unlock(&pContext->deviceInfoLock); - - *pDeviceInfo = deviceInfo; - return result; -} - -MA_API ma_bool32 ma_context_is_loopback_supported(ma_context* pContext) -{ - if (pContext == NULL) { - return MA_FALSE; - } - - return ma_is_loopback_supported(pContext->backend); -} - - -MA_API ma_device_config ma_device_config_init(ma_device_type deviceType) -{ - ma_device_config config; - MA_ZERO_OBJECT(&config); - config.deviceType = deviceType; - config.resampling = ma_resampler_config_init(ma_format_unknown, 0, 0, 0, ma_resample_algorithm_linear); /* Format/channels/rate don't matter here. */ - - return config; -} - -MA_API ma_result ma_device_init(ma_context* pContext, const ma_device_config* pConfig, ma_device* pDevice) -{ - ma_result result; - ma_device_descriptor descriptorPlayback; - ma_device_descriptor descriptorCapture; - - /* The context can be null, in which case we self-manage it. */ - if (pContext == NULL) { - return ma_device_init_ex(NULL, 0, NULL, pConfig, pDevice); - } - - if (pDevice == NULL) { - return MA_INVALID_ARGS; - } - - MA_ZERO_OBJECT(pDevice); - - if (pConfig == NULL) { - return MA_INVALID_ARGS; - } - - /* Check that we have our callbacks defined. */ - if (pContext->callbacks.onDeviceInit == NULL) { - return MA_INVALID_OPERATION; - } - - /* Basic config validation. */ - if (pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) { - if (pConfig->capture.channels > MA_MAX_CHANNELS) { - return MA_INVALID_ARGS; - } - - if (!ma__is_channel_map_valid(pConfig->capture.pChannelMap, pConfig->capture.channels)) { - return MA_INVALID_ARGS; - } - } - - if (pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex || pConfig->deviceType == ma_device_type_loopback) { - if (pConfig->playback.channels > MA_MAX_CHANNELS) { - return MA_INVALID_ARGS; - } - - if (!ma__is_channel_map_valid(pConfig->playback.pChannelMap, pConfig->playback.channels)) { - return MA_INVALID_ARGS; - } - } - - pDevice->pContext = pContext; - - /* Set the user data and log callback ASAP to ensure it is available for the entire initialization process. */ - pDevice->pUserData = pConfig->pUserData; - pDevice->onData = pConfig->dataCallback; - pDevice->onNotification = pConfig->notificationCallback; - pDevice->onStop = pConfig->stopCallback; - - if (pConfig->playback.pDeviceID != NULL) { - MA_COPY_MEMORY(&pDevice->playback.id, pConfig->playback.pDeviceID, sizeof(pDevice->playback.id)); - pDevice->playback.pID = &pDevice->playback.id; - } else { - pDevice->playback.pID = NULL; - } - - if (pConfig->capture.pDeviceID != NULL) { - MA_COPY_MEMORY(&pDevice->capture.id, pConfig->capture.pDeviceID, sizeof(pDevice->capture.id)); - pDevice->capture.pID = &pDevice->capture.id; - } else { - pDevice->capture.pID = NULL; - } - - pDevice->noPreSilencedOutputBuffer = pConfig->noPreSilencedOutputBuffer; - pDevice->noClip = pConfig->noClip; - pDevice->noDisableDenormals = pConfig->noDisableDenormals; - pDevice->noFixedSizedCallback = pConfig->noFixedSizedCallback; - ma_atomic_float_set(&pDevice->masterVolumeFactor, 1); - - pDevice->type = pConfig->deviceType; - pDevice->sampleRate = pConfig->sampleRate; - pDevice->resampling.algorithm = pConfig->resampling.algorithm; - pDevice->resampling.linear.lpfOrder = pConfig->resampling.linear.lpfOrder; - pDevice->resampling.pBackendVTable = pConfig->resampling.pBackendVTable; - pDevice->resampling.pBackendUserData = pConfig->resampling.pBackendUserData; - - pDevice->capture.shareMode = pConfig->capture.shareMode; - pDevice->capture.format = pConfig->capture.format; - pDevice->capture.channels = pConfig->capture.channels; - ma_channel_map_copy_or_default(pDevice->capture.channelMap, ma_countof(pDevice->capture.channelMap), pConfig->capture.pChannelMap, pConfig->capture.channels); - pDevice->capture.channelMixMode = pConfig->capture.channelMixMode; - pDevice->capture.calculateLFEFromSpatialChannels = pConfig->capture.calculateLFEFromSpatialChannels; - - pDevice->playback.shareMode = pConfig->playback.shareMode; - pDevice->playback.format = pConfig->playback.format; - pDevice->playback.channels = pConfig->playback.channels; - ma_channel_map_copy_or_default(pDevice->playback.channelMap, ma_countof(pDevice->playback.channelMap), pConfig->playback.pChannelMap, pConfig->playback.channels); - pDevice->playback.channelMixMode = pConfig->playback.channelMixMode; - pDevice->playback.calculateLFEFromSpatialChannels = pConfig->playback.calculateLFEFromSpatialChannels; - - result = ma_mutex_init(&pDevice->startStopLock); - if (result != MA_SUCCESS) { - return result; - } - - /* - When the device is started, the worker thread is the one that does the actual startup of the backend device. We - use a semaphore to wait for the background thread to finish the work. The same applies for stopping the device. - - Each of these semaphores is released internally by the worker thread when the work is completed. The start - semaphore is also used to wake up the worker thread. - */ - result = ma_event_init(&pDevice->wakeupEvent); - if (result != MA_SUCCESS) { - ma_mutex_uninit(&pDevice->startStopLock); - return result; - } - - result = ma_event_init(&pDevice->startEvent); - if (result != MA_SUCCESS) { - ma_event_uninit(&pDevice->wakeupEvent); - ma_mutex_uninit(&pDevice->startStopLock); - return result; - } - - result = ma_event_init(&pDevice->stopEvent); - if (result != MA_SUCCESS) { - ma_event_uninit(&pDevice->startEvent); - ma_event_uninit(&pDevice->wakeupEvent); - ma_mutex_uninit(&pDevice->startStopLock); - return result; - } - - - MA_ZERO_OBJECT(&descriptorPlayback); - descriptorPlayback.pDeviceID = pConfig->playback.pDeviceID; - descriptorPlayback.shareMode = pConfig->playback.shareMode; - descriptorPlayback.format = pConfig->playback.format; - descriptorPlayback.channels = pConfig->playback.channels; - descriptorPlayback.sampleRate = pConfig->sampleRate; - ma_channel_map_copy_or_default(descriptorPlayback.channelMap, ma_countof(descriptorPlayback.channelMap), pConfig->playback.pChannelMap, pConfig->playback.channels); - descriptorPlayback.periodSizeInFrames = pConfig->periodSizeInFrames; - descriptorPlayback.periodSizeInMilliseconds = pConfig->periodSizeInMilliseconds; - descriptorPlayback.periodCount = pConfig->periods; - - if (descriptorPlayback.periodCount == 0) { - descriptorPlayback.periodCount = MA_DEFAULT_PERIODS; - } - - - MA_ZERO_OBJECT(&descriptorCapture); - descriptorCapture.pDeviceID = pConfig->capture.pDeviceID; - descriptorCapture.shareMode = pConfig->capture.shareMode; - descriptorCapture.format = pConfig->capture.format; - descriptorCapture.channels = pConfig->capture.channels; - descriptorCapture.sampleRate = pConfig->sampleRate; - ma_channel_map_copy_or_default(descriptorCapture.channelMap, ma_countof(descriptorCapture.channelMap), pConfig->capture.pChannelMap, pConfig->capture.channels); - descriptorCapture.periodSizeInFrames = pConfig->periodSizeInFrames; - descriptorCapture.periodSizeInMilliseconds = pConfig->periodSizeInMilliseconds; - descriptorCapture.periodCount = pConfig->periods; - - if (descriptorCapture.periodCount == 0) { - descriptorCapture.periodCount = MA_DEFAULT_PERIODS; - } - - - result = pContext->callbacks.onDeviceInit(pDevice, pConfig, &descriptorPlayback, &descriptorCapture); - if (result != MA_SUCCESS) { - ma_event_uninit(&pDevice->startEvent); - ma_event_uninit(&pDevice->wakeupEvent); - ma_mutex_uninit(&pDevice->startStopLock); - return result; - } - -#if 0 - /* - On output the descriptors will contain the *actual* data format of the device. We need this to know how to convert the data between - the requested format and the internal format. - */ - if (pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex || pConfig->deviceType == ma_device_type_loopback) { - if (!ma_device_descriptor_is_valid(&descriptorCapture)) { - ma_device_uninit(pDevice); - return MA_INVALID_ARGS; - } - - pDevice->capture.internalFormat = descriptorCapture.format; - pDevice->capture.internalChannels = descriptorCapture.channels; - pDevice->capture.internalSampleRate = descriptorCapture.sampleRate; - ma_channel_map_copy(pDevice->capture.internalChannelMap, descriptorCapture.channelMap, descriptorCapture.channels); - pDevice->capture.internalPeriodSizeInFrames = descriptorCapture.periodSizeInFrames; - pDevice->capture.internalPeriods = descriptorCapture.periodCount; - - if (pDevice->capture.internalPeriodSizeInFrames == 0) { - pDevice->capture.internalPeriodSizeInFrames = ma_calculate_buffer_size_in_frames_from_milliseconds(descriptorCapture.periodSizeInMilliseconds, descriptorCapture.sampleRate); - } - } - - if (pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) { - if (!ma_device_descriptor_is_valid(&descriptorPlayback)) { - ma_device_uninit(pDevice); - return MA_INVALID_ARGS; - } - - pDevice->playback.internalFormat = descriptorPlayback.format; - pDevice->playback.internalChannels = descriptorPlayback.channels; - pDevice->playback.internalSampleRate = descriptorPlayback.sampleRate; - ma_channel_map_copy(pDevice->playback.internalChannelMap, descriptorPlayback.channelMap, descriptorPlayback.channels); - pDevice->playback.internalPeriodSizeInFrames = descriptorPlayback.periodSizeInFrames; - pDevice->playback.internalPeriods = descriptorPlayback.periodCount; - - if (pDevice->playback.internalPeriodSizeInFrames == 0) { - pDevice->playback.internalPeriodSizeInFrames = ma_calculate_buffer_size_in_frames_from_milliseconds(descriptorPlayback.periodSizeInMilliseconds, descriptorPlayback.sampleRate); - } - } - - - /* - The name of the device can be retrieved from device info. This may be temporary and replaced with a `ma_device_get_info(pDevice, deviceType)` instead. - For loopback devices, we need to retrieve the name of the playback device. - */ - { - ma_device_info deviceInfo; - - if (pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex || pConfig->deviceType == ma_device_type_loopback) { - result = ma_device_get_info(pDevice, (pConfig->deviceType == ma_device_type_loopback) ? ma_device_type_playback : ma_device_type_capture, &deviceInfo); - if (result == MA_SUCCESS) { - ma_strncpy_s(pDevice->capture.name, sizeof(pDevice->capture.name), deviceInfo.name, (size_t)-1); - } else { - /* We failed to retrieve the device info. Fall back to a default name. */ - if (descriptorCapture.pDeviceID == NULL) { - ma_strncpy_s(pDevice->capture.name, sizeof(pDevice->capture.name), MA_DEFAULT_CAPTURE_DEVICE_NAME, (size_t)-1); - } else { - ma_strncpy_s(pDevice->capture.name, sizeof(pDevice->capture.name), "Capture Device", (size_t)-1); - } - } - } - - if (pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) { - result = ma_device_get_info(pDevice, ma_device_type_playback, &deviceInfo); - if (result == MA_SUCCESS) { - ma_strncpy_s(pDevice->playback.name, sizeof(pDevice->playback.name), deviceInfo.name, (size_t)-1); - } else { - /* We failed to retrieve the device info. Fall back to a default name. */ - if (descriptorPlayback.pDeviceID == NULL) { - ma_strncpy_s(pDevice->playback.name, sizeof(pDevice->playback.name), MA_DEFAULT_PLAYBACK_DEVICE_NAME, (size_t)-1); - } else { - ma_strncpy_s(pDevice->playback.name, sizeof(pDevice->playback.name), "Playback Device", (size_t)-1); - } - } - } - } - - - ma_device__post_init_setup(pDevice, pConfig->deviceType); -#endif - - result = ma_device_post_init(pDevice, pConfig->deviceType, &descriptorPlayback, &descriptorCapture); - if (result != MA_SUCCESS) { - ma_device_uninit(pDevice); - return result; - } - - - /* - If we're using fixed sized callbacks we'll need to make use of an intermediary buffer. Needs to - be done after post_init_setup() because we'll need access to the sample rate. - */ - if (pConfig->noFixedSizedCallback == MA_FALSE) { - /* We're using a fixed sized data callback so we'll need an intermediary buffer. */ - ma_uint32 intermediaryBufferCap = pConfig->periodSizeInFrames; - if (intermediaryBufferCap == 0) { - intermediaryBufferCap = ma_calculate_buffer_size_in_frames_from_milliseconds(pConfig->periodSizeInMilliseconds, pDevice->sampleRate); - } - - if (pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex || pConfig->deviceType == ma_device_type_loopback) { - ma_uint32 intermediaryBufferSizeInBytes; - - pDevice->capture.intermediaryBufferLen = 0; - pDevice->capture.intermediaryBufferCap = intermediaryBufferCap; - if (pDevice->capture.intermediaryBufferCap == 0) { - pDevice->capture.intermediaryBufferCap = pDevice->capture.internalPeriodSizeInFrames; - } - - intermediaryBufferSizeInBytes = pDevice->capture.intermediaryBufferCap * ma_get_bytes_per_frame(pDevice->capture.format, pDevice->capture.channels); - - pDevice->capture.pIntermediaryBuffer = ma_malloc((size_t)intermediaryBufferSizeInBytes, &pContext->allocationCallbacks); - if (pDevice->capture.pIntermediaryBuffer == NULL) { - ma_device_uninit(pDevice); - return MA_OUT_OF_MEMORY; - } - - /* Silence the buffer for safety. */ - ma_silence_pcm_frames(pDevice->capture.pIntermediaryBuffer, pDevice->capture.intermediaryBufferCap, pDevice->capture.format, pDevice->capture.channels); - pDevice->capture.intermediaryBufferLen = pDevice->capture.intermediaryBufferCap; - } - - if (pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) { - ma_uint64 intermediaryBufferSizeInBytes; - - pDevice->playback.intermediaryBufferLen = 0; - if (pConfig->deviceType == ma_device_type_duplex) { - pDevice->playback.intermediaryBufferCap = pDevice->capture.intermediaryBufferCap; /* In duplex mode, make sure the intermediary buffer is always the same size as the capture side. */ - } else { - pDevice->playback.intermediaryBufferCap = intermediaryBufferCap; - if (pDevice->playback.intermediaryBufferCap == 0) { - pDevice->playback.intermediaryBufferCap = pDevice->playback.internalPeriodSizeInFrames; - } - } - - intermediaryBufferSizeInBytes = pDevice->playback.intermediaryBufferCap * ma_get_bytes_per_frame(pDevice->playback.format, pDevice->playback.channels); - - pDevice->playback.pIntermediaryBuffer = ma_malloc((size_t)intermediaryBufferSizeInBytes, &pContext->allocationCallbacks); - if (pDevice->playback.pIntermediaryBuffer == NULL) { - ma_device_uninit(pDevice); - return MA_OUT_OF_MEMORY; - } - - /* Silence the buffer for safety. */ - ma_silence_pcm_frames(pDevice->playback.pIntermediaryBuffer, pDevice->playback.intermediaryBufferCap, pDevice->playback.format, pDevice->playback.channels); - pDevice->playback.intermediaryBufferLen = 0; - } - } else { - /* Not using a fixed sized data callback so no need for an intermediary buffer. */ - } - - - /* Some backends don't require the worker thread. */ - if (!ma_context_is_backend_asynchronous(pContext)) { - /* The worker thread. */ - result = ma_thread_create(&pDevice->thread, pContext->threadPriority, pContext->threadStackSize, ma_worker_thread, pDevice, &pContext->allocationCallbacks); - if (result != MA_SUCCESS) { - ma_device_uninit(pDevice); - return result; - } - - /* Wait for the worker thread to put the device into its stopped state for real. */ - ma_event_wait(&pDevice->stopEvent); - MA_ASSERT(ma_device_get_state(pDevice) == ma_device_state_stopped); - } else { - /* - If the backend is asynchronous and the device is duplex, we'll need an intermediary ring buffer. Note that this needs to be done - after ma_device__post_init_setup(). - */ - if (ma_context_is_backend_asynchronous(pContext)) { - if (pConfig->deviceType == ma_device_type_duplex) { - result = ma_duplex_rb_init(pDevice->capture.format, pDevice->capture.channels, pDevice->sampleRate, pDevice->capture.internalSampleRate, pDevice->capture.internalPeriodSizeInFrames, &pDevice->pContext->allocationCallbacks, &pDevice->duplexRB); - if (result != MA_SUCCESS) { - ma_device_uninit(pDevice); - return result; - } - } - } - - ma_device__set_state(pDevice, ma_device_state_stopped); - } - - /* Log device information. */ - { - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, "[%s]\n", ma_get_backend_name(pDevice->pContext->backend)); - if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex || pDevice->type == ma_device_type_loopback) { - char name[MA_MAX_DEVICE_NAME_LENGTH + 1]; - ma_device_get_name(pDevice, ma_device_type_capture, name, sizeof(name), NULL); - - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, " %s (%s)\n", name, "Capture"); - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, " Format: %s -> %s\n", ma_get_format_name(pDevice->capture.internalFormat), ma_get_format_name(pDevice->capture.format)); - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, " Channels: %d -> %d\n", pDevice->capture.internalChannels, pDevice->capture.channels); - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, " Sample Rate: %d -> %d\n", pDevice->capture.internalSampleRate, pDevice->sampleRate); - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, " Buffer Size: %d*%d (%d)\n", pDevice->capture.internalPeriodSizeInFrames, pDevice->capture.internalPeriods, (pDevice->capture.internalPeriodSizeInFrames * pDevice->capture.internalPeriods)); - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, " Conversion:\n"); - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, " Pre Format Conversion: %s\n", pDevice->capture.converter.hasPreFormatConversion ? "YES" : "NO"); - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, " Post Format Conversion: %s\n", pDevice->capture.converter.hasPostFormatConversion ? "YES" : "NO"); - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, " Channel Routing: %s\n", pDevice->capture.converter.hasChannelConverter ? "YES" : "NO"); - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, " Resampling: %s\n", pDevice->capture.converter.hasResampler ? "YES" : "NO"); - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, " Passthrough: %s\n", pDevice->capture.converter.isPassthrough ? "YES" : "NO"); - { - char channelMapStr[1024]; - ma_channel_map_to_string(pDevice->capture.internalChannelMap, pDevice->capture.internalChannels, channelMapStr, sizeof(channelMapStr)); - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, " Channel Map In: {%s}\n", channelMapStr); - - ma_channel_map_to_string(pDevice->capture.channelMap, pDevice->capture.channels, channelMapStr, sizeof(channelMapStr)); - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, " Channel Map Out: {%s}\n", channelMapStr); - } - } - if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) { - char name[MA_MAX_DEVICE_NAME_LENGTH + 1]; - ma_device_get_name(pDevice, ma_device_type_playback, name, sizeof(name), NULL); - - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, " %s (%s)\n", name, "Playback"); - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, " Format: %s -> %s\n", ma_get_format_name(pDevice->playback.format), ma_get_format_name(pDevice->playback.internalFormat)); - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, " Channels: %d -> %d\n", pDevice->playback.channels, pDevice->playback.internalChannels); - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, " Sample Rate: %d -> %d\n", pDevice->sampleRate, pDevice->playback.internalSampleRate); - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, " Buffer Size: %d*%d (%d)\n", pDevice->playback.internalPeriodSizeInFrames, pDevice->playback.internalPeriods, (pDevice->playback.internalPeriodSizeInFrames * pDevice->playback.internalPeriods)); - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, " Conversion:\n"); - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, " Pre Format Conversion: %s\n", pDevice->playback.converter.hasPreFormatConversion ? "YES" : "NO"); - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, " Post Format Conversion: %s\n", pDevice->playback.converter.hasPostFormatConversion ? "YES" : "NO"); - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, " Channel Routing: %s\n", pDevice->playback.converter.hasChannelConverter ? "YES" : "NO"); - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, " Resampling: %s\n", pDevice->playback.converter.hasResampler ? "YES" : "NO"); - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, " Passthrough: %s\n", pDevice->playback.converter.isPassthrough ? "YES" : "NO"); - { - char channelMapStr[1024]; - ma_channel_map_to_string(pDevice->playback.channelMap, pDevice->playback.channels, channelMapStr, sizeof(channelMapStr)); - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, " Channel Map In: {%s}\n", channelMapStr); - - ma_channel_map_to_string(pDevice->playback.internalChannelMap, pDevice->playback.internalChannels, channelMapStr, sizeof(channelMapStr)); - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, " Channel Map Out: {%s}\n", channelMapStr); - } - } - } - - MA_ASSERT(ma_device_get_state(pDevice) == ma_device_state_stopped); - return MA_SUCCESS; -} - -MA_API ma_result ma_device_init_ex(const ma_backend backends[], ma_uint32 backendCount, const ma_context_config* pContextConfig, const ma_device_config* pConfig, ma_device* pDevice) -{ - ma_result result; - ma_context* pContext; - ma_backend defaultBackends[ma_backend_null+1]; - ma_uint32 iBackend; - ma_backend* pBackendsToIterate; - ma_uint32 backendsToIterateCount; - ma_allocation_callbacks allocationCallbacks; - - if (pConfig == NULL) { - return MA_INVALID_ARGS; - } - - if (pContextConfig != NULL) { - result = ma_allocation_callbacks_init_copy(&allocationCallbacks, &pContextConfig->allocationCallbacks); - if (result != MA_SUCCESS) { - return result; - } - } else { - allocationCallbacks = ma_allocation_callbacks_init_default(); - } - - pContext = (ma_context*)ma_malloc(sizeof(*pContext), &allocationCallbacks); - if (pContext == NULL) { - return MA_OUT_OF_MEMORY; - } - - for (iBackend = 0; iBackend <= ma_backend_null; ++iBackend) { - defaultBackends[iBackend] = (ma_backend)iBackend; - } - - pBackendsToIterate = (ma_backend*)backends; - backendsToIterateCount = backendCount; - if (pBackendsToIterate == NULL) { - pBackendsToIterate = (ma_backend*)defaultBackends; - backendsToIterateCount = ma_countof(defaultBackends); - } - - result = MA_NO_BACKEND; - - for (iBackend = 0; iBackend < backendsToIterateCount; ++iBackend) { - /* - This is a hack for iOS. If the context config is null, there's a good chance the - `ma_device_init(NULL, &deviceConfig, pDevice);` pattern is being used. In this - case, set the session category based on the device type. - */ - #if defined(MA_APPLE_MOBILE) - ma_context_config contextConfig; - - if (pContextConfig == NULL) { - contextConfig = ma_context_config_init(); - switch (pConfig->deviceType) { - case ma_device_type_duplex: { - contextConfig.coreaudio.sessionCategory = ma_ios_session_category_play_and_record; - } break; - case ma_device_type_capture: { - contextConfig.coreaudio.sessionCategory = ma_ios_session_category_record; - } break; - case ma_device_type_playback: - default: { - contextConfig.coreaudio.sessionCategory = ma_ios_session_category_playback; - } break; - } - - pContextConfig = &contextConfig; - } - #endif - - result = ma_context_init(&pBackendsToIterate[iBackend], 1, pContextConfig, pContext); - if (result == MA_SUCCESS) { - result = ma_device_init(pContext, pConfig, pDevice); - if (result == MA_SUCCESS) { - break; /* Success. */ - } else { - ma_context_uninit(pContext); /* Failure. */ - } - } - } - - if (result != MA_SUCCESS) { - ma_free(pContext, &allocationCallbacks); - return result; - } - - pDevice->isOwnerOfContext = MA_TRUE; - return result; -} - -MA_API void ma_device_uninit(ma_device* pDevice) -{ - if (!ma_device__is_initialized(pDevice)) { - return; - } - - /* - It's possible for the miniaudio side of the device and the backend to not be in sync due to - system-level situations such as the computer being put into sleep mode and the backend not - notifying miniaudio of the fact the device has stopped. It's possible for this to result in a - deadlock due to miniaudio thinking the device is in a running state, when in fact it's not - running at all. For this reason I am no longer explicitly stopping the device. I don't think - this should affect anyone in practice since uninitializing the backend will naturally stop the - device anyway. - */ - #if 0 - { - /* Make sure the device is stopped first. The backends will probably handle this naturally, but I like to do it explicitly for my own sanity. */ - if (ma_device_is_started(pDevice)) { - ma_device_stop(pDevice); - } - } - #endif - - /* Putting the device into an uninitialized state will make the worker thread return. */ - ma_device__set_state(pDevice, ma_device_state_uninitialized); - - /* Wake up the worker thread and wait for it to properly terminate. */ - if (!ma_context_is_backend_asynchronous(pDevice->pContext)) { - ma_event_signal(&pDevice->wakeupEvent); - ma_thread_wait(&pDevice->thread); - } - - if (pDevice->pContext->callbacks.onDeviceUninit != NULL) { - pDevice->pContext->callbacks.onDeviceUninit(pDevice); - } - - - ma_event_uninit(&pDevice->stopEvent); - ma_event_uninit(&pDevice->startEvent); - ma_event_uninit(&pDevice->wakeupEvent); - ma_mutex_uninit(&pDevice->startStopLock); - - if (ma_context_is_backend_asynchronous(pDevice->pContext)) { - if (pDevice->type == ma_device_type_duplex) { - ma_duplex_rb_uninit(&pDevice->duplexRB); - } - } - - if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex || pDevice->type == ma_device_type_loopback) { - ma_data_converter_uninit(&pDevice->capture.converter, &pDevice->pContext->allocationCallbacks); - } - if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) { - ma_data_converter_uninit(&pDevice->playback.converter, &pDevice->pContext->allocationCallbacks); - } - - if (pDevice->playback.pInputCache != NULL) { - ma_free(pDevice->playback.pInputCache, &pDevice->pContext->allocationCallbacks); - } - - if (pDevice->capture.pIntermediaryBuffer != NULL) { - ma_free(pDevice->capture.pIntermediaryBuffer, &pDevice->pContext->allocationCallbacks); - } - if (pDevice->playback.pIntermediaryBuffer != NULL) { - ma_free(pDevice->playback.pIntermediaryBuffer, &pDevice->pContext->allocationCallbacks); - } - - if (pDevice->isOwnerOfContext) { - ma_allocation_callbacks allocationCallbacks = pDevice->pContext->allocationCallbacks; - - ma_context_uninit(pDevice->pContext); - ma_free(pDevice->pContext, &allocationCallbacks); - } - - MA_ZERO_OBJECT(pDevice); -} - -MA_API ma_context* ma_device_get_context(ma_device* pDevice) -{ - if (pDevice == NULL) { - return NULL; - } - - return pDevice->pContext; -} - -MA_API ma_log* ma_device_get_log(ma_device* pDevice) -{ - return ma_context_get_log(ma_device_get_context(pDevice)); -} - -MA_API ma_result ma_device_get_info(ma_device* pDevice, ma_device_type type, ma_device_info* pDeviceInfo) -{ - if (pDeviceInfo == NULL) { - return MA_INVALID_ARGS; - } - - MA_ZERO_OBJECT(pDeviceInfo); - - if (pDevice == NULL) { - return MA_INVALID_ARGS; - } - - /* If the onDeviceGetInfo() callback is set, use that. Otherwise we'll fall back to ma_context_get_device_info(). */ - if (pDevice->pContext->callbacks.onDeviceGetInfo != NULL) { - return pDevice->pContext->callbacks.onDeviceGetInfo(pDevice, type, pDeviceInfo); - } - - /* Getting here means onDeviceGetInfo is not implemented so we need to fall back to an alternative. */ - if (type == ma_device_type_playback) { - return ma_context_get_device_info(pDevice->pContext, type, pDevice->playback.pID, pDeviceInfo); - } else { - /* - Here we're getting the capture side, which is the branch we'll be entering for a loopback - device, since loopback is capturing. However, if the device is using the default device ID, - it won't get the correct information because it'll think we're asking for the default - capture device, where in fact for loopback we want the default *playback* device. We'll do - a bit of a hack here to make sure we get the correct info. - */ - if (pDevice->type == ma_device_type_loopback && pDevice->capture.pID == NULL) { - type = ma_device_type_playback; - } - - return ma_context_get_device_info(pDevice->pContext, type, pDevice->capture.pID, pDeviceInfo); - } -} - -MA_API ma_result ma_device_get_name(ma_device* pDevice, ma_device_type type, char* pName, size_t nameCap, size_t* pLengthNotIncludingNullTerminator) -{ - ma_result result; - ma_device_info deviceInfo; - - if (pLengthNotIncludingNullTerminator != NULL) { - *pLengthNotIncludingNullTerminator = 0; - } - - if (pName != NULL && nameCap > 0) { - pName[0] = '\0'; - } - - result = ma_device_get_info(pDevice, type, &deviceInfo); - if (result != MA_SUCCESS) { - return result; - } - - if (pName != NULL) { - ma_strncpy_s(pName, nameCap, deviceInfo.name, (size_t)-1); - - /* - For safety, make sure the length is based on the truncated output string rather than the - source. Otherwise the caller might assume the output buffer contains more content than it - actually does. - */ - if (pLengthNotIncludingNullTerminator != NULL) { - *pLengthNotIncludingNullTerminator = strlen(pName); - } - } else { - /* Name not specified. Just report the length of the source string. */ - if (pLengthNotIncludingNullTerminator != NULL) { - *pLengthNotIncludingNullTerminator = strlen(deviceInfo.name); - } - } - - return MA_SUCCESS; -} - -MA_API ma_result ma_device_start(ma_device* pDevice) -{ - ma_result result; - - if (pDevice == NULL) { - return MA_INVALID_ARGS; - } - - if (ma_device_get_state(pDevice) == ma_device_state_uninitialized) { - return MA_INVALID_OPERATION; /* Not initialized. */ - } - - if (ma_device_get_state(pDevice) == ma_device_state_started) { - return MA_SUCCESS; /* Already started. */ - } - - ma_mutex_lock(&pDevice->startStopLock); - { - /* - We need to check again if the device is in a started state because it's possible for one thread to have started the device - while another was waiting on the mutex. - */ - if (ma_device_get_state(pDevice) == ma_device_state_started) { - ma_mutex_unlock(&pDevice->startStopLock); - return MA_SUCCESS; /* Already started. */ - } - - /* Starting and stopping are wrapped in a mutex which means we can assert that the device is in a stopped or paused state. */ - MA_ASSERT(ma_device_get_state(pDevice) == ma_device_state_stopped); - - ma_device__set_state(pDevice, ma_device_state_starting); - - /* Asynchronous backends need to be handled differently. */ - if (ma_context_is_backend_asynchronous(pDevice->pContext)) { - if (pDevice->pContext->callbacks.onDeviceStart != NULL) { - result = pDevice->pContext->callbacks.onDeviceStart(pDevice); - } else { - result = MA_INVALID_OPERATION; - } - - if (result == MA_SUCCESS) { - ma_device__set_state(pDevice, ma_device_state_started); - ma_device__on_notification_started(pDevice); - } - } else { - /* - Synchronous backends are started by signaling an event that's being waited on in the worker thread. We first wake up the - thread and then wait for the start event. - */ - ma_event_signal(&pDevice->wakeupEvent); - - /* - Wait for the worker thread to finish starting the device. Note that the worker thread will be the one who puts the device - into the started state. Don't call ma_device__set_state() here. - */ - ma_event_wait(&pDevice->startEvent); - result = pDevice->workResult; - } - - /* We changed the state from stopped to started, so if we failed, make sure we put the state back to stopped. */ - if (result != MA_SUCCESS) { - ma_device__set_state(pDevice, ma_device_state_stopped); - } - } - ma_mutex_unlock(&pDevice->startStopLock); - - return result; -} - -MA_API ma_result ma_device_stop(ma_device* pDevice) -{ - ma_result result; - - if (pDevice == NULL) { - return MA_INVALID_ARGS; - } - - if (ma_device_get_state(pDevice) == ma_device_state_uninitialized) { - return MA_INVALID_OPERATION; /* Not initialized. */ - } - - if (ma_device_get_state(pDevice) == ma_device_state_stopped) { - return MA_SUCCESS; /* Already stopped. */ - } - - ma_mutex_lock(&pDevice->startStopLock); - { - /* - We need to check again if the device is in a stopped state because it's possible for one thread to have stopped the device - while another was waiting on the mutex. - */ - if (ma_device_get_state(pDevice) == ma_device_state_stopped) { - ma_mutex_unlock(&pDevice->startStopLock); - return MA_SUCCESS; /* Already stopped. */ - } - - /* Starting and stopping are wrapped in a mutex which means we can assert that the device is in a started or paused state. */ - MA_ASSERT(ma_device_get_state(pDevice) == ma_device_state_started); - - ma_device__set_state(pDevice, ma_device_state_stopping); - - /* Asynchronous backends need to be handled differently. */ - if (ma_context_is_backend_asynchronous(pDevice->pContext)) { - /* Asynchronous backends must have a stop operation. */ - if (pDevice->pContext->callbacks.onDeviceStop != NULL) { - result = pDevice->pContext->callbacks.onDeviceStop(pDevice); - } else { - result = MA_INVALID_OPERATION; - } - - ma_device__set_state(pDevice, ma_device_state_stopped); - } else { - /* - Synchronous backends. The stop callback is always called from the worker thread. Do not call the stop callback here. If - the backend is implementing its own audio thread loop we'll need to wake it up if required. Note that we need to make - sure the state of the device is *not* playing right now, which it shouldn't be since we set it above. This is super - important though, so I'm asserting it here as well for extra safety in case we accidentally change something later. - */ - MA_ASSERT(ma_device_get_state(pDevice) != ma_device_state_started); - - if (pDevice->pContext->callbacks.onDeviceDataLoopWakeup != NULL) { - pDevice->pContext->callbacks.onDeviceDataLoopWakeup(pDevice); - } - - /* - We need to wait for the worker thread to become available for work before returning. Note that the worker thread will be - the one who puts the device into the stopped state. Don't call ma_device__set_state() here. - */ - ma_event_wait(&pDevice->stopEvent); - result = MA_SUCCESS; - } - - /* - This is a safety measure to ensure the internal buffer has been cleared so any leftover - does not get played the next time the device starts. Ideally this should be drained by - the backend first. - */ - pDevice->playback.intermediaryBufferLen = 0; - pDevice->playback.inputCacheConsumed = 0; - pDevice->playback.inputCacheRemaining = 0; - } - ma_mutex_unlock(&pDevice->startStopLock); - - return result; -} - -MA_API ma_bool32 ma_device_is_started(const ma_device* pDevice) -{ - return ma_device_get_state(pDevice) == ma_device_state_started; -} - -MA_API ma_device_state ma_device_get_state(const ma_device* pDevice) -{ - if (pDevice == NULL) { - return ma_device_state_uninitialized; - } - - return ma_atomic_device_state_get((ma_atomic_device_state*)&pDevice->state); /* Naughty cast to get rid of a const warning. */ -} - -MA_API ma_result ma_device_set_master_volume(ma_device* pDevice, float volume) -{ - if (pDevice == NULL) { - return MA_INVALID_ARGS; - } - - if (volume < 0.0f) { - return MA_INVALID_ARGS; - } - - ma_atomic_float_set(&pDevice->masterVolumeFactor, volume); - - return MA_SUCCESS; -} - -MA_API ma_result ma_device_get_master_volume(ma_device* pDevice, float* pVolume) -{ - if (pVolume == NULL) { - return MA_INVALID_ARGS; - } - - if (pDevice == NULL) { - *pVolume = 0; - return MA_INVALID_ARGS; - } - - *pVolume = ma_atomic_float_get(&pDevice->masterVolumeFactor); - - return MA_SUCCESS; -} - -MA_API ma_result ma_device_set_master_volume_db(ma_device* pDevice, float gainDB) -{ - if (gainDB > 0) { - return MA_INVALID_ARGS; - } - - return ma_device_set_master_volume(pDevice, ma_volume_db_to_linear(gainDB)); -} - -MA_API ma_result ma_device_get_master_volume_db(ma_device* pDevice, float* pGainDB) -{ - float factor; - ma_result result; - - if (pGainDB == NULL) { - return MA_INVALID_ARGS; - } - - result = ma_device_get_master_volume(pDevice, &factor); - if (result != MA_SUCCESS) { - *pGainDB = 0; - return result; - } - - *pGainDB = ma_volume_linear_to_db(factor); - - return MA_SUCCESS; -} - - -MA_API ma_result ma_device_handle_backend_data_callback(ma_device* pDevice, void* pOutput, const void* pInput, ma_uint32 frameCount) -{ - if (pDevice == NULL) { - return MA_INVALID_ARGS; - } - - if (pOutput == NULL && pInput == NULL) { - return MA_INVALID_ARGS; - } - - /* - There is an assert deeper in the code that checks that frameCount > 0. Since this is a public facing - API we'll need to check for that here. I've had reports that AAudio can sometimes post a frame count - of 0. - */ - if (frameCount == 0) { - return MA_INVALID_ARGS; - } - - if (pDevice->type == ma_device_type_duplex) { - if (pInput != NULL) { - ma_device__handle_duplex_callback_capture(pDevice, frameCount, pInput, &pDevice->duplexRB.rb); - } - - if (pOutput != NULL) { - ma_device__handle_duplex_callback_playback(pDevice, frameCount, pOutput, &pDevice->duplexRB.rb); - } - } else { - if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_loopback) { - if (pInput == NULL) { - return MA_INVALID_ARGS; - } - - ma_device__send_frames_to_client(pDevice, frameCount, pInput); - } - - if (pDevice->type == ma_device_type_playback) { - if (pOutput == NULL) { - return MA_INVALID_ARGS; - } - - ma_device__read_frames_from_client(pDevice, frameCount, pOutput); - } - } - - return MA_SUCCESS; -} - -MA_API ma_uint32 ma_calculate_buffer_size_in_frames_from_descriptor(const ma_device_descriptor* pDescriptor, ma_uint32 nativeSampleRate, ma_performance_profile performanceProfile) -{ - if (pDescriptor == NULL) { - return 0; - } - - /* - We must have a non-0 native sample rate, but some backends don't allow retrieval of this at the - time when the size of the buffer needs to be determined. In this case we need to just take a best - guess and move on. We'll try using the sample rate in pDescriptor first. If that's not set we'll - just fall back to MA_DEFAULT_SAMPLE_RATE. - */ - if (nativeSampleRate == 0) { - nativeSampleRate = pDescriptor->sampleRate; - } - if (nativeSampleRate == 0) { - nativeSampleRate = MA_DEFAULT_SAMPLE_RATE; - } - - MA_ASSERT(nativeSampleRate != 0); - - if (pDescriptor->periodSizeInFrames == 0) { - if (pDescriptor->periodSizeInMilliseconds == 0) { - if (performanceProfile == ma_performance_profile_low_latency) { - return ma_calculate_buffer_size_in_frames_from_milliseconds(MA_DEFAULT_PERIOD_SIZE_IN_MILLISECONDS_LOW_LATENCY, nativeSampleRate); - } else { - return ma_calculate_buffer_size_in_frames_from_milliseconds(MA_DEFAULT_PERIOD_SIZE_IN_MILLISECONDS_CONSERVATIVE, nativeSampleRate); - } - } else { - return ma_calculate_buffer_size_in_frames_from_milliseconds(pDescriptor->periodSizeInMilliseconds, nativeSampleRate); - } - } else { - return pDescriptor->periodSizeInFrames; - } -} -#endif /* MA_NO_DEVICE_IO */ - - -MA_API ma_uint32 ma_calculate_buffer_size_in_milliseconds_from_frames(ma_uint32 bufferSizeInFrames, ma_uint32 sampleRate) -{ - /* Prevent a division by zero. */ - if (sampleRate == 0) { - return 0; - } - - return (bufferSizeInFrames*1000 + (sampleRate - 1)) / sampleRate; -} - -MA_API ma_uint32 ma_calculate_buffer_size_in_frames_from_milliseconds(ma_uint32 bufferSizeInMilliseconds, ma_uint32 sampleRate) -{ - /* Prevent a division by zero. */ - if (sampleRate == 0) { - return 0; - } - - return bufferSizeInMilliseconds*sampleRate / 1000; -} - -MA_API void ma_copy_pcm_frames(void* dst, const void* src, ma_uint64 frameCount, ma_format format, ma_uint32 channels) -{ - if (dst == src) { - return; /* No-op. */ - } - - ma_copy_memory_64(dst, src, frameCount * ma_get_bytes_per_frame(format, channels)); -} - -MA_API void ma_silence_pcm_frames(void* p, ma_uint64 frameCount, ma_format format, ma_uint32 channels) -{ - if (format == ma_format_u8) { - ma_uint64 sampleCount = frameCount * channels; - ma_uint64 iSample; - for (iSample = 0; iSample < sampleCount; iSample += 1) { - ((ma_uint8*)p)[iSample] = 128; - } - } else { - ma_zero_memory_64(p, frameCount * ma_get_bytes_per_frame(format, channels)); - } -} - -MA_API void* ma_offset_pcm_frames_ptr(void* p, ma_uint64 offsetInFrames, ma_format format, ma_uint32 channels) -{ - return ma_offset_ptr(p, offsetInFrames * ma_get_bytes_per_frame(format, channels)); -} - -MA_API const void* ma_offset_pcm_frames_const_ptr(const void* p, ma_uint64 offsetInFrames, ma_format format, ma_uint32 channels) -{ - return ma_offset_ptr(p, offsetInFrames * ma_get_bytes_per_frame(format, channels)); -} - - -MA_API void ma_clip_samples_u8(ma_uint8* pDst, const ma_int16* pSrc, ma_uint64 count) -{ - ma_uint64 iSample; - - MA_ASSERT(pDst != NULL); - MA_ASSERT(pSrc != NULL); - - for (iSample = 0; iSample < count; iSample += 1) { - pDst[iSample] = ma_clip_u8(pSrc[iSample]); - } -} - -MA_API void ma_clip_samples_s16(ma_int16* pDst, const ma_int32* pSrc, ma_uint64 count) -{ - ma_uint64 iSample; - - MA_ASSERT(pDst != NULL); - MA_ASSERT(pSrc != NULL); - - for (iSample = 0; iSample < count; iSample += 1) { - pDst[iSample] = ma_clip_s16(pSrc[iSample]); - } -} - -MA_API void ma_clip_samples_s24(ma_uint8* pDst, const ma_int64* pSrc, ma_uint64 count) -{ - ma_uint64 iSample; - - MA_ASSERT(pDst != NULL); - MA_ASSERT(pSrc != NULL); - - for (iSample = 0; iSample < count; iSample += 1) { - ma_int64 s = ma_clip_s24(pSrc[iSample]); - pDst[iSample*3 + 0] = (ma_uint8)((s & 0x000000FF) >> 0); - pDst[iSample*3 + 1] = (ma_uint8)((s & 0x0000FF00) >> 8); - pDst[iSample*3 + 2] = (ma_uint8)((s & 0x00FF0000) >> 16); - } -} - -MA_API void ma_clip_samples_s32(ma_int32* pDst, const ma_int64* pSrc, ma_uint64 count) -{ - ma_uint64 iSample; - - MA_ASSERT(pDst != NULL); - MA_ASSERT(pSrc != NULL); - - for (iSample = 0; iSample < count; iSample += 1) { - pDst[iSample] = ma_clip_s32(pSrc[iSample]); - } -} - -MA_API void ma_clip_samples_f32(float* pDst, const float* pSrc, ma_uint64 count) -{ - ma_uint64 iSample; - - MA_ASSERT(pDst != NULL); - MA_ASSERT(pSrc != NULL); - - for (iSample = 0; iSample < count; iSample += 1) { - pDst[iSample] = ma_clip_f32(pSrc[iSample]); - } -} - -MA_API void ma_clip_pcm_frames(void* pDst, const void* pSrc, ma_uint64 frameCount, ma_format format, ma_uint32 channels) -{ - ma_uint64 sampleCount; - - MA_ASSERT(pDst != NULL); - MA_ASSERT(pSrc != NULL); - - sampleCount = frameCount * channels; - - switch (format) { - case ma_format_u8: ma_clip_samples_u8( (ma_uint8*)pDst, (const ma_int16*)pSrc, sampleCount); break; - case ma_format_s16: ma_clip_samples_s16((ma_int16*)pDst, (const ma_int32*)pSrc, sampleCount); break; - case ma_format_s24: ma_clip_samples_s24((ma_uint8*)pDst, (const ma_int64*)pSrc, sampleCount); break; - case ma_format_s32: ma_clip_samples_s32((ma_int32*)pDst, (const ma_int64*)pSrc, sampleCount); break; - case ma_format_f32: ma_clip_samples_f32(( float*)pDst, (const float*)pSrc, sampleCount); break; - - /* Do nothing if we don't know the format. We're including these here to silence a compiler warning about enums not being handled by the switch. */ - case ma_format_unknown: - case ma_format_count: - break; - } -} - - -MA_API void ma_copy_and_apply_volume_factor_u8(ma_uint8* pSamplesOut, const ma_uint8* pSamplesIn, ma_uint64 sampleCount, float factor) -{ - ma_uint64 iSample; - - if (pSamplesOut == NULL || pSamplesIn == NULL) { - return; - } - - for (iSample = 0; iSample < sampleCount; iSample += 1) { - pSamplesOut[iSample] = (ma_uint8)(pSamplesIn[iSample] * factor); - } -} - -MA_API void ma_copy_and_apply_volume_factor_s16(ma_int16* pSamplesOut, const ma_int16* pSamplesIn, ma_uint64 sampleCount, float factor) -{ - ma_uint64 iSample; - - if (pSamplesOut == NULL || pSamplesIn == NULL) { - return; - } - - for (iSample = 0; iSample < sampleCount; iSample += 1) { - pSamplesOut[iSample] = (ma_int16)(pSamplesIn[iSample] * factor); - } -} - -MA_API void ma_copy_and_apply_volume_factor_s24(void* pSamplesOut, const void* pSamplesIn, ma_uint64 sampleCount, float factor) -{ - ma_uint64 iSample; - ma_uint8* pSamplesOut8; - ma_uint8* pSamplesIn8; - - if (pSamplesOut == NULL || pSamplesIn == NULL) { - return; - } - - pSamplesOut8 = (ma_uint8*)pSamplesOut; - pSamplesIn8 = (ma_uint8*)pSamplesIn; - - for (iSample = 0; iSample < sampleCount; iSample += 1) { - ma_int32 sampleS32; - - sampleS32 = (ma_int32)(((ma_uint32)(pSamplesIn8[iSample*3+0]) << 8) | ((ma_uint32)(pSamplesIn8[iSample*3+1]) << 16) | ((ma_uint32)(pSamplesIn8[iSample*3+2])) << 24); - sampleS32 = (ma_int32)(sampleS32 * factor); - - pSamplesOut8[iSample*3+0] = (ma_uint8)(((ma_uint32)sampleS32 & 0x0000FF00) >> 8); - pSamplesOut8[iSample*3+1] = (ma_uint8)(((ma_uint32)sampleS32 & 0x00FF0000) >> 16); - pSamplesOut8[iSample*3+2] = (ma_uint8)(((ma_uint32)sampleS32 & 0xFF000000) >> 24); - } -} - -MA_API void ma_copy_and_apply_volume_factor_s32(ma_int32* pSamplesOut, const ma_int32* pSamplesIn, ma_uint64 sampleCount, float factor) -{ - ma_uint64 iSample; - - if (pSamplesOut == NULL || pSamplesIn == NULL) { - return; - } - - for (iSample = 0; iSample < sampleCount; iSample += 1) { - pSamplesOut[iSample] = (ma_int32)(pSamplesIn[iSample] * factor); - } -} - -MA_API void ma_copy_and_apply_volume_factor_f32(float* pSamplesOut, const float* pSamplesIn, ma_uint64 sampleCount, float factor) -{ - ma_uint64 iSample; - - if (pSamplesOut == NULL || pSamplesIn == NULL) { - return; - } - - if (factor == 1) { - if (pSamplesOut == pSamplesIn) { - /* In place. No-op. */ - } else { - /* Just a copy. */ - for (iSample = 0; iSample < sampleCount; iSample += 1) { - pSamplesOut[iSample] = pSamplesIn[iSample]; - } - } - } else { - for (iSample = 0; iSample < sampleCount; iSample += 1) { - pSamplesOut[iSample] = pSamplesIn[iSample] * factor; - } - } -} - -MA_API void ma_apply_volume_factor_u8(ma_uint8* pSamples, ma_uint64 sampleCount, float factor) -{ - ma_copy_and_apply_volume_factor_u8(pSamples, pSamples, sampleCount, factor); -} - -MA_API void ma_apply_volume_factor_s16(ma_int16* pSamples, ma_uint64 sampleCount, float factor) -{ - ma_copy_and_apply_volume_factor_s16(pSamples, pSamples, sampleCount, factor); -} - -MA_API void ma_apply_volume_factor_s24(void* pSamples, ma_uint64 sampleCount, float factor) -{ - ma_copy_and_apply_volume_factor_s24(pSamples, pSamples, sampleCount, factor); -} - -MA_API void ma_apply_volume_factor_s32(ma_int32* pSamples, ma_uint64 sampleCount, float factor) -{ - ma_copy_and_apply_volume_factor_s32(pSamples, pSamples, sampleCount, factor); -} - -MA_API void ma_apply_volume_factor_f32(float* pSamples, ma_uint64 sampleCount, float factor) -{ - ma_copy_and_apply_volume_factor_f32(pSamples, pSamples, sampleCount, factor); -} - -MA_API void ma_copy_and_apply_volume_factor_pcm_frames_u8(ma_uint8* pFramesOut, const ma_uint8* pFramesIn, ma_uint64 frameCount, ma_uint32 channels, float factor) -{ - ma_copy_and_apply_volume_factor_u8(pFramesOut, pFramesIn, frameCount*channels, factor); -} - -MA_API void ma_copy_and_apply_volume_factor_pcm_frames_s16(ma_int16* pFramesOut, const ma_int16* pFramesIn, ma_uint64 frameCount, ma_uint32 channels, float factor) -{ - ma_copy_and_apply_volume_factor_s16(pFramesOut, pFramesIn, frameCount*channels, factor); -} - -MA_API void ma_copy_and_apply_volume_factor_pcm_frames_s24(void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount, ma_uint32 channels, float factor) -{ - ma_copy_and_apply_volume_factor_s24(pFramesOut, pFramesIn, frameCount*channels, factor); -} - -MA_API void ma_copy_and_apply_volume_factor_pcm_frames_s32(ma_int32* pFramesOut, const ma_int32* pFramesIn, ma_uint64 frameCount, ma_uint32 channels, float factor) -{ - ma_copy_and_apply_volume_factor_s32(pFramesOut, pFramesIn, frameCount*channels, factor); -} - -MA_API void ma_copy_and_apply_volume_factor_pcm_frames_f32(float* pFramesOut, const float* pFramesIn, ma_uint64 frameCount, ma_uint32 channels, float factor) -{ - ma_copy_and_apply_volume_factor_f32(pFramesOut, pFramesIn, frameCount*channels, factor); -} - -MA_API void ma_copy_and_apply_volume_factor_pcm_frames(void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount, ma_format format, ma_uint32 channels, float factor) -{ - switch (format) - { - case ma_format_u8: ma_copy_and_apply_volume_factor_pcm_frames_u8 ((ma_uint8*)pFramesOut, (const ma_uint8*)pFramesIn, frameCount, channels, factor); return; - case ma_format_s16: ma_copy_and_apply_volume_factor_pcm_frames_s16((ma_int16*)pFramesOut, (const ma_int16*)pFramesIn, frameCount, channels, factor); return; - case ma_format_s24: ma_copy_and_apply_volume_factor_pcm_frames_s24( pFramesOut, pFramesIn, frameCount, channels, factor); return; - case ma_format_s32: ma_copy_and_apply_volume_factor_pcm_frames_s32((ma_int32*)pFramesOut, (const ma_int32*)pFramesIn, frameCount, channels, factor); return; - case ma_format_f32: ma_copy_and_apply_volume_factor_pcm_frames_f32( (float*)pFramesOut, (const float*)pFramesIn, frameCount, channels, factor); return; - default: return; /* Do nothing. */ - } -} - -MA_API void ma_apply_volume_factor_pcm_frames_u8(ma_uint8* pFrames, ma_uint64 frameCount, ma_uint32 channels, float factor) -{ - ma_copy_and_apply_volume_factor_pcm_frames_u8(pFrames, pFrames, frameCount, channels, factor); -} - -MA_API void ma_apply_volume_factor_pcm_frames_s16(ma_int16* pFrames, ma_uint64 frameCount, ma_uint32 channels, float factor) -{ - ma_copy_and_apply_volume_factor_pcm_frames_s16(pFrames, pFrames, frameCount, channels, factor); -} - -MA_API void ma_apply_volume_factor_pcm_frames_s24(void* pFrames, ma_uint64 frameCount, ma_uint32 channels, float factor) -{ - ma_copy_and_apply_volume_factor_pcm_frames_s24(pFrames, pFrames, frameCount, channels, factor); -} - -MA_API void ma_apply_volume_factor_pcm_frames_s32(ma_int32* pFrames, ma_uint64 frameCount, ma_uint32 channels, float factor) -{ - ma_copy_and_apply_volume_factor_pcm_frames_s32(pFrames, pFrames, frameCount, channels, factor); -} - -MA_API void ma_apply_volume_factor_pcm_frames_f32(float* pFrames, ma_uint64 frameCount, ma_uint32 channels, float factor) -{ - ma_copy_and_apply_volume_factor_pcm_frames_f32(pFrames, pFrames, frameCount, channels, factor); -} - -MA_API void ma_apply_volume_factor_pcm_frames(void* pFramesOut, ma_uint64 frameCount, ma_format format, ma_uint32 channels, float factor) -{ - ma_copy_and_apply_volume_factor_pcm_frames(pFramesOut, pFramesOut, frameCount, format, channels, factor); -} - - -MA_API void ma_copy_and_apply_volume_factor_per_channel_f32(float* pFramesOut, const float* pFramesIn, ma_uint64 frameCount, ma_uint32 channels, float* pChannelGains) -{ - ma_uint64 iFrame; - - if (channels == 2) { - /* TODO: Do an optimized implementation for stereo and mono. Can do a SIMD optimized implementation as well. */ - } - - for (iFrame = 0; iFrame < frameCount; iFrame += 1) { - ma_uint32 iChannel; - for (iChannel = 0; iChannel < channels; iChannel += 1) { - pFramesOut[iFrame * channels + iChannel] = pFramesIn[iFrame * channels + iChannel] * pChannelGains[iChannel]; - } - } -} - - - -static MA_INLINE ma_int16 ma_apply_volume_unclipped_u8(ma_int16 x, ma_int16 volume) -{ - return (ma_int16)(((ma_int32)x * (ma_int32)volume) >> 8); -} - -static MA_INLINE ma_int32 ma_apply_volume_unclipped_s16(ma_int32 x, ma_int16 volume) -{ - return (ma_int32)((x * volume) >> 8); -} - -static MA_INLINE ma_int64 ma_apply_volume_unclipped_s24(ma_int64 x, ma_int16 volume) -{ - return (ma_int64)((x * volume) >> 8); -} - -static MA_INLINE ma_int64 ma_apply_volume_unclipped_s32(ma_int64 x, ma_int16 volume) -{ - return (ma_int64)((x * volume) >> 8); -} - -static MA_INLINE float ma_apply_volume_unclipped_f32(float x, float volume) -{ - return x * volume; -} - - -MA_API void ma_copy_and_apply_volume_and_clip_samples_u8(ma_uint8* pDst, const ma_int16* pSrc, ma_uint64 count, float volume) -{ - ma_uint64 iSample; - ma_int16 volumeFixed; - - MA_ASSERT(pDst != NULL); - MA_ASSERT(pSrc != NULL); - - volumeFixed = ma_float_to_fixed_16(volume); - - for (iSample = 0; iSample < count; iSample += 1) { - pDst[iSample] = ma_clip_u8(ma_apply_volume_unclipped_u8(pSrc[iSample], volumeFixed)); - } -} - -MA_API void ma_copy_and_apply_volume_and_clip_samples_s16(ma_int16* pDst, const ma_int32* pSrc, ma_uint64 count, float volume) -{ - ma_uint64 iSample; - ma_int16 volumeFixed; - - MA_ASSERT(pDst != NULL); - MA_ASSERT(pSrc != NULL); - - volumeFixed = ma_float_to_fixed_16(volume); - - for (iSample = 0; iSample < count; iSample += 1) { - pDst[iSample] = ma_clip_s16(ma_apply_volume_unclipped_s16(pSrc[iSample], volumeFixed)); - } -} - -MA_API void ma_copy_and_apply_volume_and_clip_samples_s24(ma_uint8* pDst, const ma_int64* pSrc, ma_uint64 count, float volume) -{ - ma_uint64 iSample; - ma_int16 volumeFixed; - - MA_ASSERT(pDst != NULL); - MA_ASSERT(pSrc != NULL); - - volumeFixed = ma_float_to_fixed_16(volume); - - for (iSample = 0; iSample < count; iSample += 1) { - ma_int64 s = ma_clip_s24(ma_apply_volume_unclipped_s24(pSrc[iSample], volumeFixed)); - pDst[iSample*3 + 0] = (ma_uint8)((s & 0x000000FF) >> 0); - pDst[iSample*3 + 1] = (ma_uint8)((s & 0x0000FF00) >> 8); - pDst[iSample*3 + 2] = (ma_uint8)((s & 0x00FF0000) >> 16); - } -} - -MA_API void ma_copy_and_apply_volume_and_clip_samples_s32(ma_int32* pDst, const ma_int64* pSrc, ma_uint64 count, float volume) -{ - ma_uint64 iSample; - ma_int16 volumeFixed; - - MA_ASSERT(pDst != NULL); - MA_ASSERT(pSrc != NULL); - - volumeFixed = ma_float_to_fixed_16(volume); - - for (iSample = 0; iSample < count; iSample += 1) { - pDst[iSample] = ma_clip_s32(ma_apply_volume_unclipped_s32(pSrc[iSample], volumeFixed)); - } -} - -MA_API void ma_copy_and_apply_volume_and_clip_samples_f32(float* pDst, const float* pSrc, ma_uint64 count, float volume) -{ - ma_uint64 iSample; - - MA_ASSERT(pDst != NULL); - MA_ASSERT(pSrc != NULL); - - /* For the f32 case we need to make sure this supports in-place processing where the input and output buffers are the same. */ - - for (iSample = 0; iSample < count; iSample += 1) { - pDst[iSample] = ma_clip_f32(ma_apply_volume_unclipped_f32(pSrc[iSample], volume)); - } -} - -MA_API void ma_copy_and_apply_volume_and_clip_pcm_frames(void* pDst, const void* pSrc, ma_uint64 frameCount, ma_format format, ma_uint32 channels, float volume) -{ - MA_ASSERT(pDst != NULL); - MA_ASSERT(pSrc != NULL); - - if (volume == 1) { - ma_clip_pcm_frames(pDst, pSrc, frameCount, format, channels); /* Optimized case for volume = 1. */ - } else if (volume == 0) { - ma_silence_pcm_frames(pDst, frameCount, format, channels); /* Optimized case for volume = 0. */ - } else { - ma_uint64 sampleCount = frameCount * channels; - - switch (format) { - case ma_format_u8: ma_copy_and_apply_volume_and_clip_samples_u8( (ma_uint8*)pDst, (const ma_int16*)pSrc, sampleCount, volume); break; - case ma_format_s16: ma_copy_and_apply_volume_and_clip_samples_s16((ma_int16*)pDst, (const ma_int32*)pSrc, sampleCount, volume); break; - case ma_format_s24: ma_copy_and_apply_volume_and_clip_samples_s24((ma_uint8*)pDst, (const ma_int64*)pSrc, sampleCount, volume); break; - case ma_format_s32: ma_copy_and_apply_volume_and_clip_samples_s32((ma_int32*)pDst, (const ma_int64*)pSrc, sampleCount, volume); break; - case ma_format_f32: ma_copy_and_apply_volume_and_clip_samples_f32(( float*)pDst, (const float*)pSrc, sampleCount, volume); break; - - /* Do nothing if we don't know the format. We're including these here to silence a compiler warning about enums not being handled by the switch. */ - case ma_format_unknown: - case ma_format_count: - break; - } - } -} - - - -MA_API float ma_volume_linear_to_db(float factor) -{ - return 20*ma_log10f(factor); -} - -MA_API float ma_volume_db_to_linear(float gain) -{ - return ma_powf(10, gain/20.0f); -} - - -MA_API ma_result ma_mix_pcm_frames_f32(float* pDst, const float* pSrc, ma_uint64 frameCount, ma_uint32 channels, float volume) -{ - ma_uint64 iSample; - ma_uint64 sampleCount; - - if (pDst == NULL || pSrc == NULL || channels == 0) { - return MA_INVALID_ARGS; - } - - if (volume == 0) { - return MA_SUCCESS; /* No changes if the volume is 0. */ - } - - sampleCount = frameCount * channels; - - if (volume == 1) { - for (iSample = 0; iSample < sampleCount; iSample += 1) { - pDst[iSample] += pSrc[iSample]; - } - } else { - for (iSample = 0; iSample < sampleCount; iSample += 1) { - pDst[iSample] += ma_apply_volume_unclipped_f32(pSrc[iSample], volume); - } - } - - return MA_SUCCESS; -} - - - -/************************************************************************************************************************************************************** - -Format Conversion - -**************************************************************************************************************************************************************/ - -static MA_INLINE ma_int16 ma_pcm_sample_f32_to_s16(float x) -{ - return (ma_int16)(x * 32767.0f); -} - -static MA_INLINE ma_int16 ma_pcm_sample_u8_to_s16_no_scale(ma_uint8 x) -{ - return (ma_int16)((ma_int16)x - 128); -} - -static MA_INLINE ma_int64 ma_pcm_sample_s24_to_s32_no_scale(const ma_uint8* x) -{ - return (ma_int64)(((ma_uint64)x[0] << 40) | ((ma_uint64)x[1] << 48) | ((ma_uint64)x[2] << 56)) >> 40; /* Make sure the sign bits are maintained. */ -} - -static MA_INLINE void ma_pcm_sample_s32_to_s24_no_scale(ma_int64 x, ma_uint8* s24) -{ - s24[0] = (ma_uint8)((x & 0x000000FF) >> 0); - s24[1] = (ma_uint8)((x & 0x0000FF00) >> 8); - s24[2] = (ma_uint8)((x & 0x00FF0000) >> 16); -} - - -/* u8 */ -MA_API void ma_pcm_u8_to_u8(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) -{ - (void)ditherMode; - ma_copy_memory_64(dst, src, count * sizeof(ma_uint8)); -} - - -static MA_INLINE void ma_pcm_u8_to_s16__reference(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) -{ - ma_int16* dst_s16 = (ma_int16*)dst; - const ma_uint8* src_u8 = (const ma_uint8*)src; - - ma_uint64 i; - for (i = 0; i < count; i += 1) { - ma_int16 x = src_u8[i]; - x = (ma_int16)(x - 128); - x = (ma_int16)(x << 8); - dst_s16[i] = x; - } - - (void)ditherMode; -} - -static MA_INLINE void ma_pcm_u8_to_s16__optimized(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) -{ - ma_pcm_u8_to_s16__reference(dst, src, count, ditherMode); -} - -#if defined(MA_SUPPORT_SSE2) -static MA_INLINE void ma_pcm_u8_to_s16__sse2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) -{ - ma_pcm_u8_to_s16__optimized(dst, src, count, ditherMode); -} -#endif -#if defined(MA_SUPPORT_NEON) -static MA_INLINE void ma_pcm_u8_to_s16__neon(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) -{ - ma_pcm_u8_to_s16__optimized(dst, src, count, ditherMode); -} -#endif - -MA_API void ma_pcm_u8_to_s16(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) -{ -#ifdef MA_USE_REFERENCE_CONVERSION_APIS - ma_pcm_u8_to_s16__reference(dst, src, count, ditherMode); -#else - # if defined(MA_SUPPORT_SSE2) - if (ma_has_sse2()) { - ma_pcm_u8_to_s16__sse2(dst, src, count, ditherMode); - } else - #elif defined(MA_SUPPORT_NEON) - if (ma_has_neon()) { - ma_pcm_u8_to_s16__neon(dst, src, count, ditherMode); - } else - #endif - { - ma_pcm_u8_to_s16__optimized(dst, src, count, ditherMode); - } -#endif -} - - -static MA_INLINE void ma_pcm_u8_to_s24__reference(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) -{ - ma_uint8* dst_s24 = (ma_uint8*)dst; - const ma_uint8* src_u8 = (const ma_uint8*)src; - - ma_uint64 i; - for (i = 0; i < count; i += 1) { - ma_int16 x = src_u8[i]; - x = (ma_int16)(x - 128); - - dst_s24[i*3+0] = 0; - dst_s24[i*3+1] = 0; - dst_s24[i*3+2] = (ma_uint8)((ma_int8)x); - } - - (void)ditherMode; -} - -static MA_INLINE void ma_pcm_u8_to_s24__optimized(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) -{ - ma_pcm_u8_to_s24__reference(dst, src, count, ditherMode); -} - -#if defined(MA_SUPPORT_SSE2) -static MA_INLINE void ma_pcm_u8_to_s24__sse2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) -{ - ma_pcm_u8_to_s24__optimized(dst, src, count, ditherMode); -} -#endif -#if defined(MA_SUPPORT_NEON) -static MA_INLINE void ma_pcm_u8_to_s24__neon(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) -{ - ma_pcm_u8_to_s24__optimized(dst, src, count, ditherMode); -} -#endif - -MA_API void ma_pcm_u8_to_s24(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) -{ -#ifdef MA_USE_REFERENCE_CONVERSION_APIS - ma_pcm_u8_to_s24__reference(dst, src, count, ditherMode); -#else - # if defined(MA_SUPPORT_SSE2) - if (ma_has_sse2()) { - ma_pcm_u8_to_s24__sse2(dst, src, count, ditherMode); - } else - #elif defined(MA_SUPPORT_NEON) - if (ma_has_neon()) { - ma_pcm_u8_to_s24__neon(dst, src, count, ditherMode); - } else - #endif - { - ma_pcm_u8_to_s24__optimized(dst, src, count, ditherMode); - } -#endif -} - - -static MA_INLINE void ma_pcm_u8_to_s32__reference(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) -{ - ma_int32* dst_s32 = (ma_int32*)dst; - const ma_uint8* src_u8 = (const ma_uint8*)src; - - ma_uint64 i; - for (i = 0; i < count; i += 1) { - ma_int32 x = src_u8[i]; - x = x - 128; - x = x << 24; - dst_s32[i] = x; - } - - (void)ditherMode; -} - -static MA_INLINE void ma_pcm_u8_to_s32__optimized(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) -{ - ma_pcm_u8_to_s32__reference(dst, src, count, ditherMode); -} - -#if defined(MA_SUPPORT_SSE2) -static MA_INLINE void ma_pcm_u8_to_s32__sse2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) -{ - ma_pcm_u8_to_s32__optimized(dst, src, count, ditherMode); -} -#endif -#if defined(MA_SUPPORT_NEON) -static MA_INLINE void ma_pcm_u8_to_s32__neon(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) -{ - ma_pcm_u8_to_s32__optimized(dst, src, count, ditherMode); -} -#endif - -MA_API void ma_pcm_u8_to_s32(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) -{ -#ifdef MA_USE_REFERENCE_CONVERSION_APIS - ma_pcm_u8_to_s32__reference(dst, src, count, ditherMode); -#else - # if defined(MA_SUPPORT_SSE2) - if (ma_has_sse2()) { - ma_pcm_u8_to_s32__sse2(dst, src, count, ditherMode); - } else - #elif defined(MA_SUPPORT_NEON) - if (ma_has_neon()) { - ma_pcm_u8_to_s32__neon(dst, src, count, ditherMode); - } else - #endif - { - ma_pcm_u8_to_s32__optimized(dst, src, count, ditherMode); - } -#endif -} - - -static MA_INLINE void ma_pcm_u8_to_f32__reference(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) -{ - float* dst_f32 = (float*)dst; - const ma_uint8* src_u8 = (const ma_uint8*)src; - - ma_uint64 i; - for (i = 0; i < count; i += 1) { - float x = (float)src_u8[i]; - x = x * 0.00784313725490196078f; /* 0..255 to 0..2 */ - x = x - 1; /* 0..2 to -1..1 */ - - dst_f32[i] = x; - } - - (void)ditherMode; -} - -static MA_INLINE void ma_pcm_u8_to_f32__optimized(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) -{ - ma_pcm_u8_to_f32__reference(dst, src, count, ditherMode); -} - -#if defined(MA_SUPPORT_SSE2) -static MA_INLINE void ma_pcm_u8_to_f32__sse2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) -{ - ma_pcm_u8_to_f32__optimized(dst, src, count, ditherMode); -} -#endif -#if defined(MA_SUPPORT_NEON) -static MA_INLINE void ma_pcm_u8_to_f32__neon(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) -{ - ma_pcm_u8_to_f32__optimized(dst, src, count, ditherMode); -} -#endif - -MA_API void ma_pcm_u8_to_f32(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) -{ -#ifdef MA_USE_REFERENCE_CONVERSION_APIS - ma_pcm_u8_to_f32__reference(dst, src, count, ditherMode); -#else - # if defined(MA_SUPPORT_SSE2) - if (ma_has_sse2()) { - ma_pcm_u8_to_f32__sse2(dst, src, count, ditherMode); - } else - #elif defined(MA_SUPPORT_NEON) - if (ma_has_neon()) { - ma_pcm_u8_to_f32__neon(dst, src, count, ditherMode); - } else - #endif - { - ma_pcm_u8_to_f32__optimized(dst, src, count, ditherMode); - } -#endif -} - - -#ifdef MA_USE_REFERENCE_CONVERSION_APIS -static MA_INLINE void ma_pcm_interleave_u8__reference(void* dst, const void** src, ma_uint64 frameCount, ma_uint32 channels) -{ - ma_uint8* dst_u8 = (ma_uint8*)dst; - const ma_uint8** src_u8 = (const ma_uint8**)src; - - ma_uint64 iFrame; - for (iFrame = 0; iFrame < frameCount; iFrame += 1) { - ma_uint32 iChannel; - for (iChannel = 0; iChannel < channels; iChannel += 1) { - dst_u8[iFrame*channels + iChannel] = src_u8[iChannel][iFrame]; - } - } -} -#else -static MA_INLINE void ma_pcm_interleave_u8__optimized(void* dst, const void** src, ma_uint64 frameCount, ma_uint32 channels) -{ - ma_uint8* dst_u8 = (ma_uint8*)dst; - const ma_uint8** src_u8 = (const ma_uint8**)src; - - if (channels == 1) { - ma_copy_memory_64(dst, src[0], frameCount * sizeof(ma_uint8)); - } else if (channels == 2) { - ma_uint64 iFrame; - for (iFrame = 0; iFrame < frameCount; iFrame += 1) { - dst_u8[iFrame*2 + 0] = src_u8[0][iFrame]; - dst_u8[iFrame*2 + 1] = src_u8[1][iFrame]; - } - } else { - ma_uint64 iFrame; - for (iFrame = 0; iFrame < frameCount; iFrame += 1) { - ma_uint32 iChannel; - for (iChannel = 0; iChannel < channels; iChannel += 1) { - dst_u8[iFrame*channels + iChannel] = src_u8[iChannel][iFrame]; - } - } - } -} -#endif - -MA_API void ma_pcm_interleave_u8(void* dst, const void** src, ma_uint64 frameCount, ma_uint32 channels) -{ -#ifdef MA_USE_REFERENCE_CONVERSION_APIS - ma_pcm_interleave_u8__reference(dst, src, frameCount, channels); -#else - ma_pcm_interleave_u8__optimized(dst, src, frameCount, channels); -#endif -} - - -static MA_INLINE void ma_pcm_deinterleave_u8__reference(void** dst, const void* src, ma_uint64 frameCount, ma_uint32 channels) -{ - ma_uint8** dst_u8 = (ma_uint8**)dst; - const ma_uint8* src_u8 = (const ma_uint8*)src; - - ma_uint64 iFrame; - for (iFrame = 0; iFrame < frameCount; iFrame += 1) { - ma_uint32 iChannel; - for (iChannel = 0; iChannel < channels; iChannel += 1) { - dst_u8[iChannel][iFrame] = src_u8[iFrame*channels + iChannel]; - } - } -} - -static MA_INLINE void ma_pcm_deinterleave_u8__optimized(void** dst, const void* src, ma_uint64 frameCount, ma_uint32 channels) -{ - ma_pcm_deinterleave_u8__reference(dst, src, frameCount, channels); -} - -MA_API void ma_pcm_deinterleave_u8(void** dst, const void* src, ma_uint64 frameCount, ma_uint32 channels) -{ -#ifdef MA_USE_REFERENCE_CONVERSION_APIS - ma_pcm_deinterleave_u8__reference(dst, src, frameCount, channels); -#else - ma_pcm_deinterleave_u8__optimized(dst, src, frameCount, channels); -#endif -} - - -/* s16 */ -static MA_INLINE void ma_pcm_s16_to_u8__reference(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) -{ - ma_uint8* dst_u8 = (ma_uint8*)dst; - const ma_int16* src_s16 = (const ma_int16*)src; - - if (ditherMode == ma_dither_mode_none) { - ma_uint64 i; - for (i = 0; i < count; i += 1) { - ma_int16 x = src_s16[i]; - x = (ma_int16)(x >> 8); - x = (ma_int16)(x + 128); - dst_u8[i] = (ma_uint8)x; - } - } else { - ma_uint64 i; - for (i = 0; i < count; i += 1) { - ma_int16 x = src_s16[i]; - - /* Dither. Don't overflow. */ - ma_int32 dither = ma_dither_s32(ditherMode, -0x80, 0x7F); - if ((x + dither) <= 0x7FFF) { - x = (ma_int16)(x + dither); - } else { - x = 0x7FFF; - } - - x = (ma_int16)(x >> 8); - x = (ma_int16)(x + 128); - dst_u8[i] = (ma_uint8)x; - } - } -} - -static MA_INLINE void ma_pcm_s16_to_u8__optimized(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) -{ - ma_pcm_s16_to_u8__reference(dst, src, count, ditherMode); -} - -#if defined(MA_SUPPORT_SSE2) -static MA_INLINE void ma_pcm_s16_to_u8__sse2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) -{ - ma_pcm_s16_to_u8__optimized(dst, src, count, ditherMode); -} -#endif -#if defined(MA_SUPPORT_NEON) -static MA_INLINE void ma_pcm_s16_to_u8__neon(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) -{ - ma_pcm_s16_to_u8__optimized(dst, src, count, ditherMode); -} -#endif - -MA_API void ma_pcm_s16_to_u8(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) -{ -#ifdef MA_USE_REFERENCE_CONVERSION_APIS - ma_pcm_s16_to_u8__reference(dst, src, count, ditherMode); -#else - # if defined(MA_SUPPORT_SSE2) - if (ma_has_sse2()) { - ma_pcm_s16_to_u8__sse2(dst, src, count, ditherMode); - } else - #elif defined(MA_SUPPORT_NEON) - if (ma_has_neon()) { - ma_pcm_s16_to_u8__neon(dst, src, count, ditherMode); - } else - #endif - { - ma_pcm_s16_to_u8__optimized(dst, src, count, ditherMode); - } -#endif -} - - -MA_API void ma_pcm_s16_to_s16(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) -{ - (void)ditherMode; - ma_copy_memory_64(dst, src, count * sizeof(ma_int16)); -} - - -static MA_INLINE void ma_pcm_s16_to_s24__reference(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) -{ - ma_uint8* dst_s24 = (ma_uint8*)dst; - const ma_int16* src_s16 = (const ma_int16*)src; - - ma_uint64 i; - for (i = 0; i < count; i += 1) { - dst_s24[i*3+0] = 0; - dst_s24[i*3+1] = (ma_uint8)(src_s16[i] & 0xFF); - dst_s24[i*3+2] = (ma_uint8)(src_s16[i] >> 8); - } - - (void)ditherMode; -} - -static MA_INLINE void ma_pcm_s16_to_s24__optimized(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) -{ - ma_pcm_s16_to_s24__reference(dst, src, count, ditherMode); -} - -#if defined(MA_SUPPORT_SSE2) -static MA_INLINE void ma_pcm_s16_to_s24__sse2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) -{ - ma_pcm_s16_to_s24__optimized(dst, src, count, ditherMode); -} -#endif -#if defined(MA_SUPPORT_NEON) -static MA_INLINE void ma_pcm_s16_to_s24__neon(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) -{ - ma_pcm_s16_to_s24__optimized(dst, src, count, ditherMode); -} -#endif - -MA_API void ma_pcm_s16_to_s24(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) -{ -#ifdef MA_USE_REFERENCE_CONVERSION_APIS - ma_pcm_s16_to_s24__reference(dst, src, count, ditherMode); -#else - # if defined(MA_SUPPORT_SSE2) - if (ma_has_sse2()) { - ma_pcm_s16_to_s24__sse2(dst, src, count, ditherMode); - } else - #elif defined(MA_SUPPORT_NEON) - if (ma_has_neon()) { - ma_pcm_s16_to_s24__neon(dst, src, count, ditherMode); - } else - #endif - { - ma_pcm_s16_to_s24__optimized(dst, src, count, ditherMode); - } -#endif -} - - -static MA_INLINE void ma_pcm_s16_to_s32__reference(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) -{ - ma_int32* dst_s32 = (ma_int32*)dst; - const ma_int16* src_s16 = (const ma_int16*)src; - - ma_uint64 i; - for (i = 0; i < count; i += 1) { - dst_s32[i] = src_s16[i] << 16; - } - - (void)ditherMode; -} - -static MA_INLINE void ma_pcm_s16_to_s32__optimized(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) -{ - ma_pcm_s16_to_s32__reference(dst, src, count, ditherMode); -} - -#if defined(MA_SUPPORT_SSE2) -static MA_INLINE void ma_pcm_s16_to_s32__sse2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) -{ - ma_pcm_s16_to_s32__optimized(dst, src, count, ditherMode); -} -#endif -#if defined(MA_SUPPORT_NEON) -static MA_INLINE void ma_pcm_s16_to_s32__neon(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) -{ - ma_pcm_s16_to_s32__optimized(dst, src, count, ditherMode); -} -#endif - -MA_API void ma_pcm_s16_to_s32(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) -{ -#ifdef MA_USE_REFERENCE_CONVERSION_APIS - ma_pcm_s16_to_s32__reference(dst, src, count, ditherMode); -#else - # if defined(MA_SUPPORT_SSE2) - if (ma_has_sse2()) { - ma_pcm_s16_to_s32__sse2(dst, src, count, ditherMode); - } else - #elif defined(MA_SUPPORT_NEON) - if (ma_has_neon()) { - ma_pcm_s16_to_s32__neon(dst, src, count, ditherMode); - } else - #endif - { - ma_pcm_s16_to_s32__optimized(dst, src, count, ditherMode); - } -#endif -} - - -static MA_INLINE void ma_pcm_s16_to_f32__reference(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) -{ - float* dst_f32 = (float*)dst; - const ma_int16* src_s16 = (const ma_int16*)src; - - ma_uint64 i; - for (i = 0; i < count; i += 1) { - float x = (float)src_s16[i]; - -#if 0 - /* The accurate way. */ - x = x + 32768.0f; /* -32768..32767 to 0..65535 */ - x = x * 0.00003051804379339284f; /* 0..65535 to 0..2 */ - x = x - 1; /* 0..2 to -1..1 */ -#else - /* The fast way. */ - x = x * 0.000030517578125f; /* -32768..32767 to -1..0.999969482421875 */ -#endif - - dst_f32[i] = x; - } - - (void)ditherMode; -} - -static MA_INLINE void ma_pcm_s16_to_f32__optimized(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) -{ - ma_pcm_s16_to_f32__reference(dst, src, count, ditherMode); -} - -#if defined(MA_SUPPORT_SSE2) -static MA_INLINE void ma_pcm_s16_to_f32__sse2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) -{ - ma_pcm_s16_to_f32__optimized(dst, src, count, ditherMode); -} -#endif -#if defined(MA_SUPPORT_NEON) -static MA_INLINE void ma_pcm_s16_to_f32__neon(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) -{ - ma_pcm_s16_to_f32__optimized(dst, src, count, ditherMode); -} -#endif - -MA_API void ma_pcm_s16_to_f32(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) -{ -#ifdef MA_USE_REFERENCE_CONVERSION_APIS - ma_pcm_s16_to_f32__reference(dst, src, count, ditherMode); -#else - # if defined(MA_SUPPORT_SSE2) - if (ma_has_sse2()) { - ma_pcm_s16_to_f32__sse2(dst, src, count, ditherMode); - } else - #elif defined(MA_SUPPORT_NEON) - if (ma_has_neon()) { - ma_pcm_s16_to_f32__neon(dst, src, count, ditherMode); - } else - #endif - { - ma_pcm_s16_to_f32__optimized(dst, src, count, ditherMode); - } -#endif -} - - -static MA_INLINE void ma_pcm_interleave_s16__reference(void* dst, const void** src, ma_uint64 frameCount, ma_uint32 channels) -{ - ma_int16* dst_s16 = (ma_int16*)dst; - const ma_int16** src_s16 = (const ma_int16**)src; - - ma_uint64 iFrame; - for (iFrame = 0; iFrame < frameCount; iFrame += 1) { - ma_uint32 iChannel; - for (iChannel = 0; iChannel < channels; iChannel += 1) { - dst_s16[iFrame*channels + iChannel] = src_s16[iChannel][iFrame]; - } - } -} - -static MA_INLINE void ma_pcm_interleave_s16__optimized(void* dst, const void** src, ma_uint64 frameCount, ma_uint32 channels) -{ - ma_pcm_interleave_s16__reference(dst, src, frameCount, channels); -} - -MA_API void ma_pcm_interleave_s16(void* dst, const void** src, ma_uint64 frameCount, ma_uint32 channels) -{ -#ifdef MA_USE_REFERENCE_CONVERSION_APIS - ma_pcm_interleave_s16__reference(dst, src, frameCount, channels); -#else - ma_pcm_interleave_s16__optimized(dst, src, frameCount, channels); -#endif -} - - -static MA_INLINE void ma_pcm_deinterleave_s16__reference(void** dst, const void* src, ma_uint64 frameCount, ma_uint32 channels) -{ - ma_int16** dst_s16 = (ma_int16**)dst; - const ma_int16* src_s16 = (const ma_int16*)src; - - ma_uint64 iFrame; - for (iFrame = 0; iFrame < frameCount; iFrame += 1) { - ma_uint32 iChannel; - for (iChannel = 0; iChannel < channels; iChannel += 1) { - dst_s16[iChannel][iFrame] = src_s16[iFrame*channels + iChannel]; - } - } -} - -static MA_INLINE void ma_pcm_deinterleave_s16__optimized(void** dst, const void* src, ma_uint64 frameCount, ma_uint32 channels) -{ - ma_pcm_deinterleave_s16__reference(dst, src, frameCount, channels); -} - -MA_API void ma_pcm_deinterleave_s16(void** dst, const void* src, ma_uint64 frameCount, ma_uint32 channels) -{ -#ifdef MA_USE_REFERENCE_CONVERSION_APIS - ma_pcm_deinterleave_s16__reference(dst, src, frameCount, channels); -#else - ma_pcm_deinterleave_s16__optimized(dst, src, frameCount, channels); -#endif -} - - -/* s24 */ -static MA_INLINE void ma_pcm_s24_to_u8__reference(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) -{ - ma_uint8* dst_u8 = (ma_uint8*)dst; - const ma_uint8* src_s24 = (const ma_uint8*)src; - - if (ditherMode == ma_dither_mode_none) { - ma_uint64 i; - for (i = 0; i < count; i += 1) { - dst_u8[i] = (ma_uint8)((ma_int8)src_s24[i*3 + 2] + 128); - } - } else { - ma_uint64 i; - for (i = 0; i < count; i += 1) { - ma_int32 x = (ma_int32)(((ma_uint32)(src_s24[i*3+0]) << 8) | ((ma_uint32)(src_s24[i*3+1]) << 16) | ((ma_uint32)(src_s24[i*3+2])) << 24); - - /* Dither. Don't overflow. */ - ma_int32 dither = ma_dither_s32(ditherMode, -0x800000, 0x7FFFFF); - if ((ma_int64)x + dither <= 0x7FFFFFFF) { - x = x + dither; - } else { - x = 0x7FFFFFFF; - } - - x = x >> 24; - x = x + 128; - dst_u8[i] = (ma_uint8)x; - } - } -} - -static MA_INLINE void ma_pcm_s24_to_u8__optimized(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) -{ - ma_pcm_s24_to_u8__reference(dst, src, count, ditherMode); -} - -#if defined(MA_SUPPORT_SSE2) -static MA_INLINE void ma_pcm_s24_to_u8__sse2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) -{ - ma_pcm_s24_to_u8__optimized(dst, src, count, ditherMode); -} -#endif -#if defined(MA_SUPPORT_NEON) -static MA_INLINE void ma_pcm_s24_to_u8__neon(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) -{ - ma_pcm_s24_to_u8__optimized(dst, src, count, ditherMode); -} -#endif - -MA_API void ma_pcm_s24_to_u8(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) -{ -#ifdef MA_USE_REFERENCE_CONVERSION_APIS - ma_pcm_s24_to_u8__reference(dst, src, count, ditherMode); -#else - # if defined(MA_SUPPORT_SSE2) - if (ma_has_sse2()) { - ma_pcm_s24_to_u8__sse2(dst, src, count, ditherMode); - } else - #elif defined(MA_SUPPORT_NEON) - if (ma_has_neon()) { - ma_pcm_s24_to_u8__neon(dst, src, count, ditherMode); - } else - #endif - { - ma_pcm_s24_to_u8__optimized(dst, src, count, ditherMode); - } -#endif -} - - -static MA_INLINE void ma_pcm_s24_to_s16__reference(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) -{ - ma_int16* dst_s16 = (ma_int16*)dst; - const ma_uint8* src_s24 = (const ma_uint8*)src; - - if (ditherMode == ma_dither_mode_none) { - ma_uint64 i; - for (i = 0; i < count; i += 1) { - ma_uint16 dst_lo = ((ma_uint16)src_s24[i*3 + 1]); - ma_uint16 dst_hi = (ma_uint16)((ma_uint16)src_s24[i*3 + 2] << 8); - dst_s16[i] = (ma_int16)(dst_lo | dst_hi); - } - } else { - ma_uint64 i; - for (i = 0; i < count; i += 1) { - ma_int32 x = (ma_int32)(((ma_uint32)(src_s24[i*3+0]) << 8) | ((ma_uint32)(src_s24[i*3+1]) << 16) | ((ma_uint32)(src_s24[i*3+2])) << 24); - - /* Dither. Don't overflow. */ - ma_int32 dither = ma_dither_s32(ditherMode, -0x8000, 0x7FFF); - if ((ma_int64)x + dither <= 0x7FFFFFFF) { - x = x + dither; - } else { - x = 0x7FFFFFFF; - } - - x = x >> 16; - dst_s16[i] = (ma_int16)x; - } - } -} - -static MA_INLINE void ma_pcm_s24_to_s16__optimized(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) -{ - ma_pcm_s24_to_s16__reference(dst, src, count, ditherMode); -} - -#if defined(MA_SUPPORT_SSE2) -static MA_INLINE void ma_pcm_s24_to_s16__sse2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) -{ - ma_pcm_s24_to_s16__optimized(dst, src, count, ditherMode); -} -#endif -#if defined(MA_SUPPORT_NEON) -static MA_INLINE void ma_pcm_s24_to_s16__neon(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) -{ - ma_pcm_s24_to_s16__optimized(dst, src, count, ditherMode); -} -#endif - -MA_API void ma_pcm_s24_to_s16(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) -{ -#ifdef MA_USE_REFERENCE_CONVERSION_APIS - ma_pcm_s24_to_s16__reference(dst, src, count, ditherMode); -#else - # if defined(MA_SUPPORT_SSE2) - if (ma_has_sse2()) { - ma_pcm_s24_to_s16__sse2(dst, src, count, ditherMode); - } else - #elif defined(MA_SUPPORT_NEON) - if (ma_has_neon()) { - ma_pcm_s24_to_s16__neon(dst, src, count, ditherMode); - } else - #endif - { - ma_pcm_s24_to_s16__optimized(dst, src, count, ditherMode); - } -#endif -} - - -MA_API void ma_pcm_s24_to_s24(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) -{ - (void)ditherMode; - - ma_copy_memory_64(dst, src, count * 3); -} - - -static MA_INLINE void ma_pcm_s24_to_s32__reference(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) -{ - ma_int32* dst_s32 = (ma_int32*)dst; - const ma_uint8* src_s24 = (const ma_uint8*)src; - - ma_uint64 i; - for (i = 0; i < count; i += 1) { - dst_s32[i] = (ma_int32)(((ma_uint32)(src_s24[i*3+0]) << 8) | ((ma_uint32)(src_s24[i*3+1]) << 16) | ((ma_uint32)(src_s24[i*3+2])) << 24); - } - - (void)ditherMode; -} - -static MA_INLINE void ma_pcm_s24_to_s32__optimized(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) -{ - ma_pcm_s24_to_s32__reference(dst, src, count, ditherMode); -} - -#if defined(MA_SUPPORT_SSE2) -static MA_INLINE void ma_pcm_s24_to_s32__sse2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) -{ - ma_pcm_s24_to_s32__optimized(dst, src, count, ditherMode); -} -#endif -#if defined(MA_SUPPORT_NEON) -static MA_INLINE void ma_pcm_s24_to_s32__neon(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) -{ - ma_pcm_s24_to_s32__optimized(dst, src, count, ditherMode); -} -#endif - -MA_API void ma_pcm_s24_to_s32(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) -{ -#ifdef MA_USE_REFERENCE_CONVERSION_APIS - ma_pcm_s24_to_s32__reference(dst, src, count, ditherMode); -#else - # if defined(MA_SUPPORT_SSE2) - if (ma_has_sse2()) { - ma_pcm_s24_to_s32__sse2(dst, src, count, ditherMode); - } else - #elif defined(MA_SUPPORT_NEON) - if (ma_has_neon()) { - ma_pcm_s24_to_s32__neon(dst, src, count, ditherMode); - } else - #endif - { - ma_pcm_s24_to_s32__optimized(dst, src, count, ditherMode); - } -#endif -} - - -static MA_INLINE void ma_pcm_s24_to_f32__reference(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) -{ - float* dst_f32 = (float*)dst; - const ma_uint8* src_s24 = (const ma_uint8*)src; - - ma_uint64 i; - for (i = 0; i < count; i += 1) { - float x = (float)(((ma_int32)(((ma_uint32)(src_s24[i*3+0]) << 8) | ((ma_uint32)(src_s24[i*3+1]) << 16) | ((ma_uint32)(src_s24[i*3+2])) << 24)) >> 8); - -#if 0 - /* The accurate way. */ - x = x + 8388608.0f; /* -8388608..8388607 to 0..16777215 */ - x = x * 0.00000011920929665621f; /* 0..16777215 to 0..2 */ - x = x - 1; /* 0..2 to -1..1 */ -#else - /* The fast way. */ - x = x * 0.00000011920928955078125f; /* -8388608..8388607 to -1..0.999969482421875 */ -#endif - - dst_f32[i] = x; - } - - (void)ditherMode; -} - -static MA_INLINE void ma_pcm_s24_to_f32__optimized(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) -{ - ma_pcm_s24_to_f32__reference(dst, src, count, ditherMode); -} - -#if defined(MA_SUPPORT_SSE2) -static MA_INLINE void ma_pcm_s24_to_f32__sse2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) -{ - ma_pcm_s24_to_f32__optimized(dst, src, count, ditherMode); -} -#endif -#if defined(MA_SUPPORT_NEON) -static MA_INLINE void ma_pcm_s24_to_f32__neon(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) -{ - ma_pcm_s24_to_f32__optimized(dst, src, count, ditherMode); -} -#endif - -MA_API void ma_pcm_s24_to_f32(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) -{ -#ifdef MA_USE_REFERENCE_CONVERSION_APIS - ma_pcm_s24_to_f32__reference(dst, src, count, ditherMode); -#else - # if defined(MA_SUPPORT_SSE2) - if (ma_has_sse2()) { - ma_pcm_s24_to_f32__sse2(dst, src, count, ditherMode); - } else - #elif defined(MA_SUPPORT_NEON) - if (ma_has_neon()) { - ma_pcm_s24_to_f32__neon(dst, src, count, ditherMode); - } else - #endif - { - ma_pcm_s24_to_f32__optimized(dst, src, count, ditherMode); - } -#endif -} - - -static MA_INLINE void ma_pcm_interleave_s24__reference(void* dst, const void** src, ma_uint64 frameCount, ma_uint32 channels) -{ - ma_uint8* dst8 = (ma_uint8*)dst; - const ma_uint8** src8 = (const ma_uint8**)src; - - ma_uint64 iFrame; - for (iFrame = 0; iFrame < frameCount; iFrame += 1) { - ma_uint32 iChannel; - for (iChannel = 0; iChannel < channels; iChannel += 1) { - dst8[iFrame*3*channels + iChannel*3 + 0] = src8[iChannel][iFrame*3 + 0]; - dst8[iFrame*3*channels + iChannel*3 + 1] = src8[iChannel][iFrame*3 + 1]; - dst8[iFrame*3*channels + iChannel*3 + 2] = src8[iChannel][iFrame*3 + 2]; - } - } -} - -static MA_INLINE void ma_pcm_interleave_s24__optimized(void* dst, const void** src, ma_uint64 frameCount, ma_uint32 channels) -{ - ma_pcm_interleave_s24__reference(dst, src, frameCount, channels); -} - -MA_API void ma_pcm_interleave_s24(void* dst, const void** src, ma_uint64 frameCount, ma_uint32 channels) -{ -#ifdef MA_USE_REFERENCE_CONVERSION_APIS - ma_pcm_interleave_s24__reference(dst, src, frameCount, channels); -#else - ma_pcm_interleave_s24__optimized(dst, src, frameCount, channels); -#endif -} - - -static MA_INLINE void ma_pcm_deinterleave_s24__reference(void** dst, const void* src, ma_uint64 frameCount, ma_uint32 channels) -{ - ma_uint8** dst8 = (ma_uint8**)dst; - const ma_uint8* src8 = (const ma_uint8*)src; - - ma_uint32 iFrame; - for (iFrame = 0; iFrame < frameCount; iFrame += 1) { - ma_uint32 iChannel; - for (iChannel = 0; iChannel < channels; iChannel += 1) { - dst8[iChannel][iFrame*3 + 0] = src8[iFrame*3*channels + iChannel*3 + 0]; - dst8[iChannel][iFrame*3 + 1] = src8[iFrame*3*channels + iChannel*3 + 1]; - dst8[iChannel][iFrame*3 + 2] = src8[iFrame*3*channels + iChannel*3 + 2]; - } - } -} - -static MA_INLINE void ma_pcm_deinterleave_s24__optimized(void** dst, const void* src, ma_uint64 frameCount, ma_uint32 channels) -{ - ma_pcm_deinterleave_s24__reference(dst, src, frameCount, channels); -} - -MA_API void ma_pcm_deinterleave_s24(void** dst, const void* src, ma_uint64 frameCount, ma_uint32 channels) -{ -#ifdef MA_USE_REFERENCE_CONVERSION_APIS - ma_pcm_deinterleave_s24__reference(dst, src, frameCount, channels); -#else - ma_pcm_deinterleave_s24__optimized(dst, src, frameCount, channels); -#endif -} - - - -/* s32 */ -static MA_INLINE void ma_pcm_s32_to_u8__reference(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) -{ - ma_uint8* dst_u8 = (ma_uint8*)dst; - const ma_int32* src_s32 = (const ma_int32*)src; - - if (ditherMode == ma_dither_mode_none) { - ma_uint64 i; - for (i = 0; i < count; i += 1) { - ma_int32 x = src_s32[i]; - x = x >> 24; - x = x + 128; - dst_u8[i] = (ma_uint8)x; - } - } else { - ma_uint64 i; - for (i = 0; i < count; i += 1) { - ma_int32 x = src_s32[i]; - - /* Dither. Don't overflow. */ - ma_int32 dither = ma_dither_s32(ditherMode, -0x800000, 0x7FFFFF); - if ((ma_int64)x + dither <= 0x7FFFFFFF) { - x = x + dither; - } else { - x = 0x7FFFFFFF; - } - - x = x >> 24; - x = x + 128; - dst_u8[i] = (ma_uint8)x; - } - } -} - -static MA_INLINE void ma_pcm_s32_to_u8__optimized(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) -{ - ma_pcm_s32_to_u8__reference(dst, src, count, ditherMode); -} - -#if defined(MA_SUPPORT_SSE2) -static MA_INLINE void ma_pcm_s32_to_u8__sse2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) -{ - ma_pcm_s32_to_u8__optimized(dst, src, count, ditherMode); -} -#endif -#if defined(MA_SUPPORT_NEON) -static MA_INLINE void ma_pcm_s32_to_u8__neon(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) -{ - ma_pcm_s32_to_u8__optimized(dst, src, count, ditherMode); -} -#endif - -MA_API void ma_pcm_s32_to_u8(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) -{ -#ifdef MA_USE_REFERENCE_CONVERSION_APIS - ma_pcm_s32_to_u8__reference(dst, src, count, ditherMode); -#else - # if defined(MA_SUPPORT_SSE2) - if (ma_has_sse2()) { - ma_pcm_s32_to_u8__sse2(dst, src, count, ditherMode); - } else - #elif defined(MA_SUPPORT_NEON) - if (ma_has_neon()) { - ma_pcm_s32_to_u8__neon(dst, src, count, ditherMode); - } else - #endif - { - ma_pcm_s32_to_u8__optimized(dst, src, count, ditherMode); - } -#endif -} - - -static MA_INLINE void ma_pcm_s32_to_s16__reference(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) -{ - ma_int16* dst_s16 = (ma_int16*)dst; - const ma_int32* src_s32 = (const ma_int32*)src; - - if (ditherMode == ma_dither_mode_none) { - ma_uint64 i; - for (i = 0; i < count; i += 1) { - ma_int32 x = src_s32[i]; - x = x >> 16; - dst_s16[i] = (ma_int16)x; - } - } else { - ma_uint64 i; - for (i = 0; i < count; i += 1) { - ma_int32 x = src_s32[i]; - - /* Dither. Don't overflow. */ - ma_int32 dither = ma_dither_s32(ditherMode, -0x8000, 0x7FFF); - if ((ma_int64)x + dither <= 0x7FFFFFFF) { - x = x + dither; - } else { - x = 0x7FFFFFFF; - } - - x = x >> 16; - dst_s16[i] = (ma_int16)x; - } - } -} - -static MA_INLINE void ma_pcm_s32_to_s16__optimized(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) -{ - ma_pcm_s32_to_s16__reference(dst, src, count, ditherMode); -} - -#if defined(MA_SUPPORT_SSE2) -static MA_INLINE void ma_pcm_s32_to_s16__sse2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) -{ - ma_pcm_s32_to_s16__optimized(dst, src, count, ditherMode); -} -#endif -#if defined(MA_SUPPORT_NEON) -static MA_INLINE void ma_pcm_s32_to_s16__neon(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) -{ - ma_pcm_s32_to_s16__optimized(dst, src, count, ditherMode); -} -#endif - -MA_API void ma_pcm_s32_to_s16(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) -{ -#ifdef MA_USE_REFERENCE_CONVERSION_APIS - ma_pcm_s32_to_s16__reference(dst, src, count, ditherMode); -#else - # if defined(MA_SUPPORT_SSE2) - if (ma_has_sse2()) { - ma_pcm_s32_to_s16__sse2(dst, src, count, ditherMode); - } else - #elif defined(MA_SUPPORT_NEON) - if (ma_has_neon()) { - ma_pcm_s32_to_s16__neon(dst, src, count, ditherMode); - } else - #endif - { - ma_pcm_s32_to_s16__optimized(dst, src, count, ditherMode); - } -#endif -} - - -static MA_INLINE void ma_pcm_s32_to_s24__reference(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) -{ - ma_uint8* dst_s24 = (ma_uint8*)dst; - const ma_int32* src_s32 = (const ma_int32*)src; - - ma_uint64 i; - for (i = 0; i < count; i += 1) { - ma_uint32 x = (ma_uint32)src_s32[i]; - dst_s24[i*3+0] = (ma_uint8)((x & 0x0000FF00) >> 8); - dst_s24[i*3+1] = (ma_uint8)((x & 0x00FF0000) >> 16); - dst_s24[i*3+2] = (ma_uint8)((x & 0xFF000000) >> 24); - } - - (void)ditherMode; /* No dithering for s32 -> s24. */ -} - -static MA_INLINE void ma_pcm_s32_to_s24__optimized(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) -{ - ma_pcm_s32_to_s24__reference(dst, src, count, ditherMode); -} - -#if defined(MA_SUPPORT_SSE2) -static MA_INLINE void ma_pcm_s32_to_s24__sse2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) -{ - ma_pcm_s32_to_s24__optimized(dst, src, count, ditherMode); -} -#endif -#if defined(MA_SUPPORT_NEON) -static MA_INLINE void ma_pcm_s32_to_s24__neon(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) -{ - ma_pcm_s32_to_s24__optimized(dst, src, count, ditherMode); -} -#endif - -MA_API void ma_pcm_s32_to_s24(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) -{ -#ifdef MA_USE_REFERENCE_CONVERSION_APIS - ma_pcm_s32_to_s24__reference(dst, src, count, ditherMode); -#else - # if defined(MA_SUPPORT_SSE2) - if (ma_has_sse2()) { - ma_pcm_s32_to_s24__sse2(dst, src, count, ditherMode); - } else - #elif defined(MA_SUPPORT_NEON) - if (ma_has_neon()) { - ma_pcm_s32_to_s24__neon(dst, src, count, ditherMode); - } else - #endif - { - ma_pcm_s32_to_s24__optimized(dst, src, count, ditherMode); - } -#endif -} - - -MA_API void ma_pcm_s32_to_s32(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) -{ - (void)ditherMode; - - ma_copy_memory_64(dst, src, count * sizeof(ma_int32)); -} - - -static MA_INLINE void ma_pcm_s32_to_f32__reference(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) -{ - float* dst_f32 = (float*)dst; - const ma_int32* src_s32 = (const ma_int32*)src; - - ma_uint64 i; - for (i = 0; i < count; i += 1) { - double x = src_s32[i]; - -#if 0 - x = x + 2147483648.0; - x = x * 0.0000000004656612873077392578125; - x = x - 1; -#else - x = x / 2147483648.0; -#endif - - dst_f32[i] = (float)x; - } - - (void)ditherMode; /* No dithering for s32 -> f32. */ -} - -static MA_INLINE void ma_pcm_s32_to_f32__optimized(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) -{ - ma_pcm_s32_to_f32__reference(dst, src, count, ditherMode); -} - -#if defined(MA_SUPPORT_SSE2) -static MA_INLINE void ma_pcm_s32_to_f32__sse2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) -{ - ma_pcm_s32_to_f32__optimized(dst, src, count, ditherMode); -} -#endif -#if defined(MA_SUPPORT_NEON) -static MA_INLINE void ma_pcm_s32_to_f32__neon(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) -{ - ma_pcm_s32_to_f32__optimized(dst, src, count, ditherMode); -} -#endif - -MA_API void ma_pcm_s32_to_f32(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) -{ -#ifdef MA_USE_REFERENCE_CONVERSION_APIS - ma_pcm_s32_to_f32__reference(dst, src, count, ditherMode); -#else - # if defined(MA_SUPPORT_SSE2) - if (ma_has_sse2()) { - ma_pcm_s32_to_f32__sse2(dst, src, count, ditherMode); - } else - #elif defined(MA_SUPPORT_NEON) - if (ma_has_neon()) { - ma_pcm_s32_to_f32__neon(dst, src, count, ditherMode); - } else - #endif - { - ma_pcm_s32_to_f32__optimized(dst, src, count, ditherMode); - } -#endif -} - - -static MA_INLINE void ma_pcm_interleave_s32__reference(void* dst, const void** src, ma_uint64 frameCount, ma_uint32 channels) -{ - ma_int32* dst_s32 = (ma_int32*)dst; - const ma_int32** src_s32 = (const ma_int32**)src; - - ma_uint64 iFrame; - for (iFrame = 0; iFrame < frameCount; iFrame += 1) { - ma_uint32 iChannel; - for (iChannel = 0; iChannel < channels; iChannel += 1) { - dst_s32[iFrame*channels + iChannel] = src_s32[iChannel][iFrame]; - } - } -} - -static MA_INLINE void ma_pcm_interleave_s32__optimized(void* dst, const void** src, ma_uint64 frameCount, ma_uint32 channels) -{ - ma_pcm_interleave_s32__reference(dst, src, frameCount, channels); -} - -MA_API void ma_pcm_interleave_s32(void* dst, const void** src, ma_uint64 frameCount, ma_uint32 channels) -{ -#ifdef MA_USE_REFERENCE_CONVERSION_APIS - ma_pcm_interleave_s32__reference(dst, src, frameCount, channels); -#else - ma_pcm_interleave_s32__optimized(dst, src, frameCount, channels); -#endif -} - - -static MA_INLINE void ma_pcm_deinterleave_s32__reference(void** dst, const void* src, ma_uint64 frameCount, ma_uint32 channels) -{ - ma_int32** dst_s32 = (ma_int32**)dst; - const ma_int32* src_s32 = (const ma_int32*)src; - - ma_uint64 iFrame; - for (iFrame = 0; iFrame < frameCount; iFrame += 1) { - ma_uint32 iChannel; - for (iChannel = 0; iChannel < channels; iChannel += 1) { - dst_s32[iChannel][iFrame] = src_s32[iFrame*channels + iChannel]; - } - } -} - -static MA_INLINE void ma_pcm_deinterleave_s32__optimized(void** dst, const void* src, ma_uint64 frameCount, ma_uint32 channels) -{ - ma_pcm_deinterleave_s32__reference(dst, src, frameCount, channels); -} - -MA_API void ma_pcm_deinterleave_s32(void** dst, const void* src, ma_uint64 frameCount, ma_uint32 channels) -{ -#ifdef MA_USE_REFERENCE_CONVERSION_APIS - ma_pcm_deinterleave_s32__reference(dst, src, frameCount, channels); -#else - ma_pcm_deinterleave_s32__optimized(dst, src, frameCount, channels); -#endif -} - - -/* f32 */ -static MA_INLINE void ma_pcm_f32_to_u8__reference(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) -{ - ma_uint64 i; - - ma_uint8* dst_u8 = (ma_uint8*)dst; - const float* src_f32 = (const float*)src; - - float ditherMin = 0; - float ditherMax = 0; - if (ditherMode != ma_dither_mode_none) { - ditherMin = 1.0f / -128; - ditherMax = 1.0f / 127; - } - - for (i = 0; i < count; i += 1) { - float x = src_f32[i]; - x = x + ma_dither_f32(ditherMode, ditherMin, ditherMax); - x = ((x < -1) ? -1 : ((x > 1) ? 1 : x)); /* clip */ - x = x + 1; /* -1..1 to 0..2 */ - x = x * 127.5f; /* 0..2 to 0..255 */ - - dst_u8[i] = (ma_uint8)x; - } -} - -static MA_INLINE void ma_pcm_f32_to_u8__optimized(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) -{ - ma_pcm_f32_to_u8__reference(dst, src, count, ditherMode); -} - -#if defined(MA_SUPPORT_SSE2) -static MA_INLINE void ma_pcm_f32_to_u8__sse2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) -{ - ma_pcm_f32_to_u8__optimized(dst, src, count, ditherMode); -} -#endif -#if defined(MA_SUPPORT_NEON) -static MA_INLINE void ma_pcm_f32_to_u8__neon(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) -{ - ma_pcm_f32_to_u8__optimized(dst, src, count, ditherMode); -} -#endif - -MA_API void ma_pcm_f32_to_u8(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) -{ -#ifdef MA_USE_REFERENCE_CONVERSION_APIS - ma_pcm_f32_to_u8__reference(dst, src, count, ditherMode); -#else - # if defined(MA_SUPPORT_SSE2) - if (ma_has_sse2()) { - ma_pcm_f32_to_u8__sse2(dst, src, count, ditherMode); - } else - #elif defined(MA_SUPPORT_NEON) - if (ma_has_neon()) { - ma_pcm_f32_to_u8__neon(dst, src, count, ditherMode); - } else - #endif - { - ma_pcm_f32_to_u8__optimized(dst, src, count, ditherMode); - } -#endif -} - -#ifdef MA_USE_REFERENCE_CONVERSION_APIS -static MA_INLINE void ma_pcm_f32_to_s16__reference(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) -{ - ma_uint64 i; - - ma_int16* dst_s16 = (ma_int16*)dst; - const float* src_f32 = (const float*)src; - - float ditherMin = 0; - float ditherMax = 0; - if (ditherMode != ma_dither_mode_none) { - ditherMin = 1.0f / -32768; - ditherMax = 1.0f / 32767; - } - - for (i = 0; i < count; i += 1) { - float x = src_f32[i]; - x = x + ma_dither_f32(ditherMode, ditherMin, ditherMax); - x = ((x < -1) ? -1 : ((x > 1) ? 1 : x)); /* clip */ - -#if 0 - /* The accurate way. */ - x = x + 1; /* -1..1 to 0..2 */ - x = x * 32767.5f; /* 0..2 to 0..65535 */ - x = x - 32768.0f; /* 0...65535 to -32768..32767 */ -#else - /* The fast way. */ - x = x * 32767.0f; /* -1..1 to -32767..32767 */ -#endif - - dst_s16[i] = (ma_int16)x; - } -} -#else -static MA_INLINE void ma_pcm_f32_to_s16__optimized(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) -{ - ma_uint64 i; - ma_uint64 i4; - ma_uint64 count4; - - ma_int16* dst_s16 = (ma_int16*)dst; - const float* src_f32 = (const float*)src; - - float ditherMin = 0; - float ditherMax = 0; - if (ditherMode != ma_dither_mode_none) { - ditherMin = 1.0f / -32768; - ditherMax = 1.0f / 32767; - } - - /* Unrolled. */ - i = 0; - count4 = count >> 2; - for (i4 = 0; i4 < count4; i4 += 1) { - float d0 = ma_dither_f32(ditherMode, ditherMin, ditherMax); - float d1 = ma_dither_f32(ditherMode, ditherMin, ditherMax); - float d2 = ma_dither_f32(ditherMode, ditherMin, ditherMax); - float d3 = ma_dither_f32(ditherMode, ditherMin, ditherMax); - - float x0 = src_f32[i+0]; - float x1 = src_f32[i+1]; - float x2 = src_f32[i+2]; - float x3 = src_f32[i+3]; - - x0 = x0 + d0; - x1 = x1 + d1; - x2 = x2 + d2; - x3 = x3 + d3; - - x0 = ((x0 < -1) ? -1 : ((x0 > 1) ? 1 : x0)); - x1 = ((x1 < -1) ? -1 : ((x1 > 1) ? 1 : x1)); - x2 = ((x2 < -1) ? -1 : ((x2 > 1) ? 1 : x2)); - x3 = ((x3 < -1) ? -1 : ((x3 > 1) ? 1 : x3)); - - x0 = x0 * 32767.0f; - x1 = x1 * 32767.0f; - x2 = x2 * 32767.0f; - x3 = x3 * 32767.0f; - - dst_s16[i+0] = (ma_int16)x0; - dst_s16[i+1] = (ma_int16)x1; - dst_s16[i+2] = (ma_int16)x2; - dst_s16[i+3] = (ma_int16)x3; - - i += 4; - } - - /* Leftover. */ - for (; i < count; i += 1) { - float x = src_f32[i]; - x = x + ma_dither_f32(ditherMode, ditherMin, ditherMax); - x = ((x < -1) ? -1 : ((x > 1) ? 1 : x)); /* clip */ - x = x * 32767.0f; /* -1..1 to -32767..32767 */ - - dst_s16[i] = (ma_int16)x; - } -} - -#if defined(MA_SUPPORT_SSE2) -static MA_INLINE void ma_pcm_f32_to_s16__sse2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) -{ - ma_uint64 i; - ma_uint64 i8; - ma_uint64 count8; - ma_int16* dst_s16; - const float* src_f32; - float ditherMin; - float ditherMax; - - /* Both the input and output buffers need to be aligned to 16 bytes. */ - if ((((ma_uintptr)dst & 15) != 0) || (((ma_uintptr)src & 15) != 0)) { - ma_pcm_f32_to_s16__optimized(dst, src, count, ditherMode); - return; - } - - dst_s16 = (ma_int16*)dst; - src_f32 = (const float*)src; - - ditherMin = 0; - ditherMax = 0; - if (ditherMode != ma_dither_mode_none) { - ditherMin = 1.0f / -32768; - ditherMax = 1.0f / 32767; - } - - i = 0; - - /* SSE2. SSE allows us to output 8 s16's at a time which means our loop is unrolled 8 times. */ - count8 = count >> 3; - for (i8 = 0; i8 < count8; i8 += 1) { - __m128 d0; - __m128 d1; - __m128 x0; - __m128 x1; - - if (ditherMode == ma_dither_mode_none) { - d0 = _mm_set1_ps(0); - d1 = _mm_set1_ps(0); - } else if (ditherMode == ma_dither_mode_rectangle) { - d0 = _mm_set_ps( - ma_dither_f32_rectangle(ditherMin, ditherMax), - ma_dither_f32_rectangle(ditherMin, ditherMax), - ma_dither_f32_rectangle(ditherMin, ditherMax), - ma_dither_f32_rectangle(ditherMin, ditherMax) - ); - d1 = _mm_set_ps( - ma_dither_f32_rectangle(ditherMin, ditherMax), - ma_dither_f32_rectangle(ditherMin, ditherMax), - ma_dither_f32_rectangle(ditherMin, ditherMax), - ma_dither_f32_rectangle(ditherMin, ditherMax) - ); - } else { - d0 = _mm_set_ps( - ma_dither_f32_triangle(ditherMin, ditherMax), - ma_dither_f32_triangle(ditherMin, ditherMax), - ma_dither_f32_triangle(ditherMin, ditherMax), - ma_dither_f32_triangle(ditherMin, ditherMax) - ); - d1 = _mm_set_ps( - ma_dither_f32_triangle(ditherMin, ditherMax), - ma_dither_f32_triangle(ditherMin, ditherMax), - ma_dither_f32_triangle(ditherMin, ditherMax), - ma_dither_f32_triangle(ditherMin, ditherMax) - ); - } - - x0 = *((__m128*)(src_f32 + i) + 0); - x1 = *((__m128*)(src_f32 + i) + 1); - - x0 = _mm_add_ps(x0, d0); - x1 = _mm_add_ps(x1, d1); - - x0 = _mm_mul_ps(x0, _mm_set1_ps(32767.0f)); - x1 = _mm_mul_ps(x1, _mm_set1_ps(32767.0f)); - - _mm_stream_si128(((__m128i*)(dst_s16 + i)), _mm_packs_epi32(_mm_cvttps_epi32(x0), _mm_cvttps_epi32(x1))); - - i += 8; - } - - - /* Leftover. */ - for (; i < count; i += 1) { - float x = src_f32[i]; - x = x + ma_dither_f32(ditherMode, ditherMin, ditherMax); - x = ((x < -1) ? -1 : ((x > 1) ? 1 : x)); /* clip */ - x = x * 32767.0f; /* -1..1 to -32767..32767 */ - - dst_s16[i] = (ma_int16)x; - } -} -#endif /* SSE2 */ - -#if defined(MA_SUPPORT_NEON) -static MA_INLINE void ma_pcm_f32_to_s16__neon(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) -{ - ma_uint64 i; - ma_uint64 i8; - ma_uint64 count8; - ma_int16* dst_s16; - const float* src_f32; - float ditherMin; - float ditherMax; - - if (!ma_has_neon()) { - ma_pcm_f32_to_s16__optimized(dst, src, count, ditherMode); - return; - } - - /* Both the input and output buffers need to be aligned to 16 bytes. */ - if ((((ma_uintptr)dst & 15) != 0) || (((ma_uintptr)src & 15) != 0)) { - ma_pcm_f32_to_s16__optimized(dst, src, count, ditherMode); - return; - } - - dst_s16 = (ma_int16*)dst; - src_f32 = (const float*)src; - - ditherMin = 0; - ditherMax = 0; - if (ditherMode != ma_dither_mode_none) { - ditherMin = 1.0f / -32768; - ditherMax = 1.0f / 32767; - } - - i = 0; - - /* NEON. NEON allows us to output 8 s16's at a time which means our loop is unrolled 8 times. */ - count8 = count >> 3; - for (i8 = 0; i8 < count8; i8 += 1) { - float32x4_t d0; - float32x4_t d1; - float32x4_t x0; - float32x4_t x1; - int32x4_t i0; - int32x4_t i1; - - if (ditherMode == ma_dither_mode_none) { - d0 = vmovq_n_f32(0); - d1 = vmovq_n_f32(0); - } else if (ditherMode == ma_dither_mode_rectangle) { - float d0v[4]; - float d1v[4]; - - d0v[0] = ma_dither_f32_rectangle(ditherMin, ditherMax); - d0v[1] = ma_dither_f32_rectangle(ditherMin, ditherMax); - d0v[2] = ma_dither_f32_rectangle(ditherMin, ditherMax); - d0v[3] = ma_dither_f32_rectangle(ditherMin, ditherMax); - d0 = vld1q_f32(d0v); - - d1v[0] = ma_dither_f32_rectangle(ditherMin, ditherMax); - d1v[1] = ma_dither_f32_rectangle(ditherMin, ditherMax); - d1v[2] = ma_dither_f32_rectangle(ditherMin, ditherMax); - d1v[3] = ma_dither_f32_rectangle(ditherMin, ditherMax); - d1 = vld1q_f32(d1v); - } else { - float d0v[4]; - float d1v[4]; - - d0v[0] = ma_dither_f32_triangle(ditherMin, ditherMax); - d0v[1] = ma_dither_f32_triangle(ditherMin, ditherMax); - d0v[2] = ma_dither_f32_triangle(ditherMin, ditherMax); - d0v[3] = ma_dither_f32_triangle(ditherMin, ditherMax); - d0 = vld1q_f32(d0v); - - d1v[0] = ma_dither_f32_triangle(ditherMin, ditherMax); - d1v[1] = ma_dither_f32_triangle(ditherMin, ditherMax); - d1v[2] = ma_dither_f32_triangle(ditherMin, ditherMax); - d1v[3] = ma_dither_f32_triangle(ditherMin, ditherMax); - d1 = vld1q_f32(d1v); - } - - x0 = *((float32x4_t*)(src_f32 + i) + 0); - x1 = *((float32x4_t*)(src_f32 + i) + 1); - - x0 = vaddq_f32(x0, d0); - x1 = vaddq_f32(x1, d1); - - x0 = vmulq_n_f32(x0, 32767.0f); - x1 = vmulq_n_f32(x1, 32767.0f); - - i0 = vcvtq_s32_f32(x0); - i1 = vcvtq_s32_f32(x1); - *((int16x8_t*)(dst_s16 + i)) = vcombine_s16(vqmovn_s32(i0), vqmovn_s32(i1)); - - i += 8; - } - - - /* Leftover. */ - for (; i < count; i += 1) { - float x = src_f32[i]; - x = x + ma_dither_f32(ditherMode, ditherMin, ditherMax); - x = ((x < -1) ? -1 : ((x > 1) ? 1 : x)); /* clip */ - x = x * 32767.0f; /* -1..1 to -32767..32767 */ - - dst_s16[i] = (ma_int16)x; - } -} -#endif /* Neon */ -#endif /* MA_USE_REFERENCE_CONVERSION_APIS */ - -MA_API void ma_pcm_f32_to_s16(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) -{ -#ifdef MA_USE_REFERENCE_CONVERSION_APIS - ma_pcm_f32_to_s16__reference(dst, src, count, ditherMode); -#else - # if defined(MA_SUPPORT_SSE2) - if (ma_has_sse2()) { - ma_pcm_f32_to_s16__sse2(dst, src, count, ditherMode); - } else - #elif defined(MA_SUPPORT_NEON) - if (ma_has_neon()) { - ma_pcm_f32_to_s16__neon(dst, src, count, ditherMode); - } else - #endif - { - ma_pcm_f32_to_s16__optimized(dst, src, count, ditherMode); - } -#endif -} - - -static MA_INLINE void ma_pcm_f32_to_s24__reference(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) -{ - ma_uint8* dst_s24 = (ma_uint8*)dst; - const float* src_f32 = (const float*)src; - - ma_uint64 i; - for (i = 0; i < count; i += 1) { - ma_int32 r; - float x = src_f32[i]; - x = ((x < -1) ? -1 : ((x > 1) ? 1 : x)); /* clip */ - -#if 0 - /* The accurate way. */ - x = x + 1; /* -1..1 to 0..2 */ - x = x * 8388607.5f; /* 0..2 to 0..16777215 */ - x = x - 8388608.0f; /* 0..16777215 to -8388608..8388607 */ -#else - /* The fast way. */ - x = x * 8388607.0f; /* -1..1 to -8388607..8388607 */ -#endif - - r = (ma_int32)x; - dst_s24[(i*3)+0] = (ma_uint8)((r & 0x0000FF) >> 0); - dst_s24[(i*3)+1] = (ma_uint8)((r & 0x00FF00) >> 8); - dst_s24[(i*3)+2] = (ma_uint8)((r & 0xFF0000) >> 16); - } - - (void)ditherMode; /* No dithering for f32 -> s24. */ -} - -static MA_INLINE void ma_pcm_f32_to_s24__optimized(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) -{ - ma_pcm_f32_to_s24__reference(dst, src, count, ditherMode); -} - -#if defined(MA_SUPPORT_SSE2) -static MA_INLINE void ma_pcm_f32_to_s24__sse2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) -{ - ma_pcm_f32_to_s24__optimized(dst, src, count, ditherMode); -} -#endif -#if defined(MA_SUPPORT_NEON) -static MA_INLINE void ma_pcm_f32_to_s24__neon(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) -{ - ma_pcm_f32_to_s24__optimized(dst, src, count, ditherMode); -} -#endif - -MA_API void ma_pcm_f32_to_s24(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) -{ -#ifdef MA_USE_REFERENCE_CONVERSION_APIS - ma_pcm_f32_to_s24__reference(dst, src, count, ditherMode); -#else - # if defined(MA_SUPPORT_SSE2) - if (ma_has_sse2()) { - ma_pcm_f32_to_s24__sse2(dst, src, count, ditherMode); - } else - #elif defined(MA_SUPPORT_NEON) - if (ma_has_neon()) { - ma_pcm_f32_to_s24__neon(dst, src, count, ditherMode); - } else - #endif - { - ma_pcm_f32_to_s24__optimized(dst, src, count, ditherMode); - } -#endif -} - - -static MA_INLINE void ma_pcm_f32_to_s32__reference(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) -{ - ma_int32* dst_s32 = (ma_int32*)dst; - const float* src_f32 = (const float*)src; - - ma_uint32 i; - for (i = 0; i < count; i += 1) { - double x = src_f32[i]; - x = ((x < -1) ? -1 : ((x > 1) ? 1 : x)); /* clip */ - -#if 0 - /* The accurate way. */ - x = x + 1; /* -1..1 to 0..2 */ - x = x * 2147483647.5; /* 0..2 to 0..4294967295 */ - x = x - 2147483648.0; /* 0...4294967295 to -2147483648..2147483647 */ -#else - /* The fast way. */ - x = x * 2147483647.0; /* -1..1 to -2147483647..2147483647 */ -#endif - - dst_s32[i] = (ma_int32)x; - } - - (void)ditherMode; /* No dithering for f32 -> s32. */ -} - -static MA_INLINE void ma_pcm_f32_to_s32__optimized(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) -{ - ma_pcm_f32_to_s32__reference(dst, src, count, ditherMode); -} - -#if defined(MA_SUPPORT_SSE2) -static MA_INLINE void ma_pcm_f32_to_s32__sse2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) -{ - ma_pcm_f32_to_s32__optimized(dst, src, count, ditherMode); -} -#endif -#if defined(MA_SUPPORT_NEON) -static MA_INLINE void ma_pcm_f32_to_s32__neon(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) -{ - ma_pcm_f32_to_s32__optimized(dst, src, count, ditherMode); -} -#endif - -MA_API void ma_pcm_f32_to_s32(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) -{ -#ifdef MA_USE_REFERENCE_CONVERSION_APIS - ma_pcm_f32_to_s32__reference(dst, src, count, ditherMode); -#else - # if defined(MA_SUPPORT_SSE2) - if (ma_has_sse2()) { - ma_pcm_f32_to_s32__sse2(dst, src, count, ditherMode); - } else - #elif defined(MA_SUPPORT_NEON) - if (ma_has_neon()) { - ma_pcm_f32_to_s32__neon(dst, src, count, ditherMode); - } else - #endif - { - ma_pcm_f32_to_s32__optimized(dst, src, count, ditherMode); - } -#endif -} - - -MA_API void ma_pcm_f32_to_f32(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) -{ - (void)ditherMode; - - ma_copy_memory_64(dst, src, count * sizeof(float)); -} - - -static void ma_pcm_interleave_f32__reference(void* dst, const void** src, ma_uint64 frameCount, ma_uint32 channels) -{ - float* dst_f32 = (float*)dst; - const float** src_f32 = (const float**)src; - - ma_uint64 iFrame; - for (iFrame = 0; iFrame < frameCount; iFrame += 1) { - ma_uint32 iChannel; - for (iChannel = 0; iChannel < channels; iChannel += 1) { - dst_f32[iFrame*channels + iChannel] = src_f32[iChannel][iFrame]; - } - } -} - -static void ma_pcm_interleave_f32__optimized(void* dst, const void** src, ma_uint64 frameCount, ma_uint32 channels) -{ - ma_pcm_interleave_f32__reference(dst, src, frameCount, channels); -} - -MA_API void ma_pcm_interleave_f32(void* dst, const void** src, ma_uint64 frameCount, ma_uint32 channels) -{ -#ifdef MA_USE_REFERENCE_CONVERSION_APIS - ma_pcm_interleave_f32__reference(dst, src, frameCount, channels); -#else - ma_pcm_interleave_f32__optimized(dst, src, frameCount, channels); -#endif -} - - -static void ma_pcm_deinterleave_f32__reference(void** dst, const void* src, ma_uint64 frameCount, ma_uint32 channels) -{ - float** dst_f32 = (float**)dst; - const float* src_f32 = (const float*)src; - - ma_uint64 iFrame; - for (iFrame = 0; iFrame < frameCount; iFrame += 1) { - ma_uint32 iChannel; - for (iChannel = 0; iChannel < channels; iChannel += 1) { - dst_f32[iChannel][iFrame] = src_f32[iFrame*channels + iChannel]; - } - } -} - -static void ma_pcm_deinterleave_f32__optimized(void** dst, const void* src, ma_uint64 frameCount, ma_uint32 channels) -{ - ma_pcm_deinterleave_f32__reference(dst, src, frameCount, channels); -} - -MA_API void ma_pcm_deinterleave_f32(void** dst, const void* src, ma_uint64 frameCount, ma_uint32 channels) -{ -#ifdef MA_USE_REFERENCE_CONVERSION_APIS - ma_pcm_deinterleave_f32__reference(dst, src, frameCount, channels); -#else - ma_pcm_deinterleave_f32__optimized(dst, src, frameCount, channels); -#endif -} - - -MA_API void ma_pcm_convert(void* pOut, ma_format formatOut, const void* pIn, ma_format formatIn, ma_uint64 sampleCount, ma_dither_mode ditherMode) -{ - if (formatOut == formatIn) { - ma_copy_memory_64(pOut, pIn, sampleCount * ma_get_bytes_per_sample(formatOut)); - return; - } - - switch (formatIn) - { - case ma_format_u8: - { - switch (formatOut) - { - case ma_format_s16: ma_pcm_u8_to_s16(pOut, pIn, sampleCount, ditherMode); return; - case ma_format_s24: ma_pcm_u8_to_s24(pOut, pIn, sampleCount, ditherMode); return; - case ma_format_s32: ma_pcm_u8_to_s32(pOut, pIn, sampleCount, ditherMode); return; - case ma_format_f32: ma_pcm_u8_to_f32(pOut, pIn, sampleCount, ditherMode); return; - default: break; - } - } break; - - case ma_format_s16: - { - switch (formatOut) - { - case ma_format_u8: ma_pcm_s16_to_u8( pOut, pIn, sampleCount, ditherMode); return; - case ma_format_s24: ma_pcm_s16_to_s24(pOut, pIn, sampleCount, ditherMode); return; - case ma_format_s32: ma_pcm_s16_to_s32(pOut, pIn, sampleCount, ditherMode); return; - case ma_format_f32: ma_pcm_s16_to_f32(pOut, pIn, sampleCount, ditherMode); return; - default: break; - } - } break; - - case ma_format_s24: - { - switch (formatOut) - { - case ma_format_u8: ma_pcm_s24_to_u8( pOut, pIn, sampleCount, ditherMode); return; - case ma_format_s16: ma_pcm_s24_to_s16(pOut, pIn, sampleCount, ditherMode); return; - case ma_format_s32: ma_pcm_s24_to_s32(pOut, pIn, sampleCount, ditherMode); return; - case ma_format_f32: ma_pcm_s24_to_f32(pOut, pIn, sampleCount, ditherMode); return; - default: break; - } - } break; - - case ma_format_s32: - { - switch (formatOut) - { - case ma_format_u8: ma_pcm_s32_to_u8( pOut, pIn, sampleCount, ditherMode); return; - case ma_format_s16: ma_pcm_s32_to_s16(pOut, pIn, sampleCount, ditherMode); return; - case ma_format_s24: ma_pcm_s32_to_s24(pOut, pIn, sampleCount, ditherMode); return; - case ma_format_f32: ma_pcm_s32_to_f32(pOut, pIn, sampleCount, ditherMode); return; - default: break; - } - } break; - - case ma_format_f32: - { - switch (formatOut) - { - case ma_format_u8: ma_pcm_f32_to_u8( pOut, pIn, sampleCount, ditherMode); return; - case ma_format_s16: ma_pcm_f32_to_s16(pOut, pIn, sampleCount, ditherMode); return; - case ma_format_s24: ma_pcm_f32_to_s24(pOut, pIn, sampleCount, ditherMode); return; - case ma_format_s32: ma_pcm_f32_to_s32(pOut, pIn, sampleCount, ditherMode); return; - default: break; - } - } break; - - default: break; - } -} - -MA_API void ma_convert_pcm_frames_format(void* pOut, ma_format formatOut, const void* pIn, ma_format formatIn, ma_uint64 frameCount, ma_uint32 channels, ma_dither_mode ditherMode) -{ - ma_pcm_convert(pOut, formatOut, pIn, formatIn, frameCount * channels, ditherMode); -} - -MA_API void ma_deinterleave_pcm_frames(ma_format format, ma_uint32 channels, ma_uint64 frameCount, const void* pInterleavedPCMFrames, void** ppDeinterleavedPCMFrames) -{ - if (pInterleavedPCMFrames == NULL || ppDeinterleavedPCMFrames == NULL) { - return; /* Invalid args. */ - } - - /* For efficiency we do this per format. */ - switch (format) { - case ma_format_s16: - { - const ma_int16* pSrcS16 = (const ma_int16*)pInterleavedPCMFrames; - ma_uint64 iPCMFrame; - for (iPCMFrame = 0; iPCMFrame < frameCount; ++iPCMFrame) { - ma_uint32 iChannel; - for (iChannel = 0; iChannel < channels; ++iChannel) { - ma_int16* pDstS16 = (ma_int16*)ppDeinterleavedPCMFrames[iChannel]; - pDstS16[iPCMFrame] = pSrcS16[iPCMFrame*channels+iChannel]; - } - } - } break; - - case ma_format_f32: - { - const float* pSrcF32 = (const float*)pInterleavedPCMFrames; - ma_uint64 iPCMFrame; - for (iPCMFrame = 0; iPCMFrame < frameCount; ++iPCMFrame) { - ma_uint32 iChannel; - for (iChannel = 0; iChannel < channels; ++iChannel) { - float* pDstF32 = (float*)ppDeinterleavedPCMFrames[iChannel]; - pDstF32[iPCMFrame] = pSrcF32[iPCMFrame*channels+iChannel]; - } - } - } break; - - default: - { - ma_uint32 sampleSizeInBytes = ma_get_bytes_per_sample(format); - ma_uint64 iPCMFrame; - for (iPCMFrame = 0; iPCMFrame < frameCount; ++iPCMFrame) { - ma_uint32 iChannel; - for (iChannel = 0; iChannel < channels; ++iChannel) { - void* pDst = ma_offset_ptr(ppDeinterleavedPCMFrames[iChannel], iPCMFrame*sampleSizeInBytes); - const void* pSrc = ma_offset_ptr(pInterleavedPCMFrames, (iPCMFrame*channels+iChannel)*sampleSizeInBytes); - memcpy(pDst, pSrc, sampleSizeInBytes); - } - } - } break; - } -} - -MA_API void ma_interleave_pcm_frames(ma_format format, ma_uint32 channels, ma_uint64 frameCount, const void** ppDeinterleavedPCMFrames, void* pInterleavedPCMFrames) -{ - switch (format) - { - case ma_format_s16: - { - ma_int16* pDstS16 = (ma_int16*)pInterleavedPCMFrames; - ma_uint64 iPCMFrame; - for (iPCMFrame = 0; iPCMFrame < frameCount; ++iPCMFrame) { - ma_uint32 iChannel; - for (iChannel = 0; iChannel < channels; ++iChannel) { - const ma_int16* pSrcS16 = (const ma_int16*)ppDeinterleavedPCMFrames[iChannel]; - pDstS16[iPCMFrame*channels+iChannel] = pSrcS16[iPCMFrame]; - } - } - } break; - - case ma_format_f32: - { - float* pDstF32 = (float*)pInterleavedPCMFrames; - ma_uint64 iPCMFrame; - for (iPCMFrame = 0; iPCMFrame < frameCount; ++iPCMFrame) { - ma_uint32 iChannel; - for (iChannel = 0; iChannel < channels; ++iChannel) { - const float* pSrcF32 = (const float*)ppDeinterleavedPCMFrames[iChannel]; - pDstF32[iPCMFrame*channels+iChannel] = pSrcF32[iPCMFrame]; - } - } - } break; - - default: - { - ma_uint32 sampleSizeInBytes = ma_get_bytes_per_sample(format); - ma_uint64 iPCMFrame; - for (iPCMFrame = 0; iPCMFrame < frameCount; ++iPCMFrame) { - ma_uint32 iChannel; - for (iChannel = 0; iChannel < channels; ++iChannel) { - void* pDst = ma_offset_ptr(pInterleavedPCMFrames, (iPCMFrame*channels+iChannel)*sampleSizeInBytes); - const void* pSrc = ma_offset_ptr(ppDeinterleavedPCMFrames[iChannel], iPCMFrame*sampleSizeInBytes); - memcpy(pDst, pSrc, sampleSizeInBytes); - } - } - } break; - } -} - - -/************************************************************************************************************************************************************** - -Biquad Filter - -**************************************************************************************************************************************************************/ -#ifndef MA_BIQUAD_FIXED_POINT_SHIFT -#define MA_BIQUAD_FIXED_POINT_SHIFT 14 -#endif - -static ma_int32 ma_biquad_float_to_fp(double x) -{ - return (ma_int32)(x * (1 << MA_BIQUAD_FIXED_POINT_SHIFT)); -} - -MA_API ma_biquad_config ma_biquad_config_init(ma_format format, ma_uint32 channels, double b0, double b1, double b2, double a0, double a1, double a2) -{ - ma_biquad_config config; - - MA_ZERO_OBJECT(&config); - config.format = format; - config.channels = channels; - config.b0 = b0; - config.b1 = b1; - config.b2 = b2; - config.a0 = a0; - config.a1 = a1; - config.a2 = a2; - - return config; -} - - -typedef struct -{ - size_t sizeInBytes; - size_t r1Offset; - size_t r2Offset; -} ma_biquad_heap_layout; - -static ma_result ma_biquad_get_heap_layout(const ma_biquad_config* pConfig, ma_biquad_heap_layout* pHeapLayout) -{ - MA_ASSERT(pHeapLayout != NULL); - - MA_ZERO_OBJECT(pHeapLayout); - - if (pConfig == NULL) { - return MA_INVALID_ARGS; - } - - if (pConfig->channels == 0) { - return MA_INVALID_ARGS; - } - - pHeapLayout->sizeInBytes = 0; - - /* R0 */ - pHeapLayout->r1Offset = pHeapLayout->sizeInBytes; - pHeapLayout->sizeInBytes += sizeof(ma_biquad_coefficient) * pConfig->channels; - - /* R1 */ - pHeapLayout->r2Offset = pHeapLayout->sizeInBytes; - pHeapLayout->sizeInBytes += sizeof(ma_biquad_coefficient) * pConfig->channels; - - /* Make sure allocation size is aligned. */ - pHeapLayout->sizeInBytes = ma_align_64(pHeapLayout->sizeInBytes); - - return MA_SUCCESS; -} - -MA_API ma_result ma_biquad_get_heap_size(const ma_biquad_config* pConfig, size_t* pHeapSizeInBytes) -{ - ma_result result; - ma_biquad_heap_layout heapLayout; - - if (pHeapSizeInBytes == NULL) { - return MA_INVALID_ARGS; - } - - *pHeapSizeInBytes = 0; - - result = ma_biquad_get_heap_layout(pConfig, &heapLayout); - if (result != MA_SUCCESS) { - return result; - } - - *pHeapSizeInBytes = heapLayout.sizeInBytes; - - return MA_SUCCESS; -} - -MA_API ma_result ma_biquad_init_preallocated(const ma_biquad_config* pConfig, void* pHeap, ma_biquad* pBQ) -{ - ma_result result; - ma_biquad_heap_layout heapLayout; - - if (pBQ == NULL) { - return MA_INVALID_ARGS; - } - - MA_ZERO_OBJECT(pBQ); - - result = ma_biquad_get_heap_layout(pConfig, &heapLayout); - if (result != MA_SUCCESS) { - return result; - } - - pBQ->_pHeap = pHeap; - MA_ZERO_MEMORY(pHeap, heapLayout.sizeInBytes); - - pBQ->pR1 = (ma_biquad_coefficient*)ma_offset_ptr(pHeap, heapLayout.r1Offset); - pBQ->pR2 = (ma_biquad_coefficient*)ma_offset_ptr(pHeap, heapLayout.r2Offset); - - return ma_biquad_reinit(pConfig, pBQ); -} - -MA_API ma_result ma_biquad_init(const ma_biquad_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_biquad* pBQ) -{ - ma_result result; - size_t heapSizeInBytes; - void* pHeap; - - result = ma_biquad_get_heap_size(pConfig, &heapSizeInBytes); - if (result != MA_SUCCESS) { - return result; - } - - if (heapSizeInBytes > 0) { - pHeap = ma_malloc(heapSizeInBytes, pAllocationCallbacks); - if (pHeap == NULL) { - return MA_OUT_OF_MEMORY; - } - } else { - pHeap = NULL; - } - - result = ma_biquad_init_preallocated(pConfig, pHeap, pBQ); - if (result != MA_SUCCESS) { - ma_free(pHeap, pAllocationCallbacks); - return result; - } - - pBQ->_ownsHeap = MA_TRUE; - return MA_SUCCESS; -} - -MA_API void ma_biquad_uninit(ma_biquad* pBQ, const ma_allocation_callbacks* pAllocationCallbacks) -{ - if (pBQ == NULL) { - return; - } - - if (pBQ->_ownsHeap) { - ma_free(pBQ->_pHeap, pAllocationCallbacks); - } -} - -MA_API ma_result ma_biquad_reinit(const ma_biquad_config* pConfig, ma_biquad* pBQ) -{ - if (pBQ == NULL || pConfig == NULL) { - return MA_INVALID_ARGS; - } - - if (pConfig->a0 == 0) { - return MA_INVALID_ARGS; /* Division by zero. */ - } - - /* Only supporting f32 and s16. */ - if (pConfig->format != ma_format_f32 && pConfig->format != ma_format_s16) { - return MA_INVALID_ARGS; - } - - /* The format cannot be changed after initialization. */ - if (pBQ->format != ma_format_unknown && pBQ->format != pConfig->format) { - return MA_INVALID_OPERATION; - } - - /* The channel count cannot be changed after initialization. */ - if (pBQ->channels != 0 && pBQ->channels != pConfig->channels) { - return MA_INVALID_OPERATION; - } - - - pBQ->format = pConfig->format; - pBQ->channels = pConfig->channels; - - /* Normalize. */ - if (pConfig->format == ma_format_f32) { - pBQ->b0.f32 = (float)(pConfig->b0 / pConfig->a0); - pBQ->b1.f32 = (float)(pConfig->b1 / pConfig->a0); - pBQ->b2.f32 = (float)(pConfig->b2 / pConfig->a0); - pBQ->a1.f32 = (float)(pConfig->a1 / pConfig->a0); - pBQ->a2.f32 = (float)(pConfig->a2 / pConfig->a0); - } else { - pBQ->b0.s32 = ma_biquad_float_to_fp(pConfig->b0 / pConfig->a0); - pBQ->b1.s32 = ma_biquad_float_to_fp(pConfig->b1 / pConfig->a0); - pBQ->b2.s32 = ma_biquad_float_to_fp(pConfig->b2 / pConfig->a0); - pBQ->a1.s32 = ma_biquad_float_to_fp(pConfig->a1 / pConfig->a0); - pBQ->a2.s32 = ma_biquad_float_to_fp(pConfig->a2 / pConfig->a0); - } - - return MA_SUCCESS; -} - -MA_API ma_result ma_biquad_clear_cache(ma_biquad* pBQ) -{ - if (pBQ == NULL) { - return MA_INVALID_ARGS; - } - - if (pBQ->format == ma_format_f32) { - pBQ->pR1->f32 = 0; - pBQ->pR2->f32 = 0; - } else { - pBQ->pR1->s32 = 0; - pBQ->pR2->s32 = 0; - } - - return MA_SUCCESS; -} - -static MA_INLINE void ma_biquad_process_pcm_frame_f32__direct_form_2_transposed(ma_biquad* pBQ, float* pY, const float* pX) -{ - ma_uint32 c; - const ma_uint32 channels = pBQ->channels; - const float b0 = pBQ->b0.f32; - const float b1 = pBQ->b1.f32; - const float b2 = pBQ->b2.f32; - const float a1 = pBQ->a1.f32; - const float a2 = pBQ->a2.f32; - - MA_ASSUME(channels > 0); - for (c = 0; c < channels; c += 1) { - float r1 = pBQ->pR1[c].f32; - float r2 = pBQ->pR2[c].f32; - float x = pX[c]; - float y; - - y = b0*x + r1; - r1 = b1*x - a1*y + r2; - r2 = b2*x - a2*y; - - pY[c] = y; - pBQ->pR1[c].f32 = r1; - pBQ->pR2[c].f32 = r2; - } -} - -static MA_INLINE void ma_biquad_process_pcm_frame_f32(ma_biquad* pBQ, float* pY, const float* pX) -{ - ma_biquad_process_pcm_frame_f32__direct_form_2_transposed(pBQ, pY, pX); -} - -static MA_INLINE void ma_biquad_process_pcm_frame_s16__direct_form_2_transposed(ma_biquad* pBQ, ma_int16* pY, const ma_int16* pX) -{ - ma_uint32 c; - const ma_uint32 channels = pBQ->channels; - const ma_int32 b0 = pBQ->b0.s32; - const ma_int32 b1 = pBQ->b1.s32; - const ma_int32 b2 = pBQ->b2.s32; - const ma_int32 a1 = pBQ->a1.s32; - const ma_int32 a2 = pBQ->a2.s32; - - MA_ASSUME(channels > 0); - for (c = 0; c < channels; c += 1) { - ma_int32 r1 = pBQ->pR1[c].s32; - ma_int32 r2 = pBQ->pR2[c].s32; - ma_int32 x = pX[c]; - ma_int32 y; - - y = (b0*x + r1) >> MA_BIQUAD_FIXED_POINT_SHIFT; - r1 = (b1*x - a1*y + r2); - r2 = (b2*x - a2*y); - - pY[c] = (ma_int16)ma_clamp(y, -32768, 32767); - pBQ->pR1[c].s32 = r1; - pBQ->pR2[c].s32 = r2; - } -} - -static MA_INLINE void ma_biquad_process_pcm_frame_s16(ma_biquad* pBQ, ma_int16* pY, const ma_int16* pX) -{ - ma_biquad_process_pcm_frame_s16__direct_form_2_transposed(pBQ, pY, pX); -} - -MA_API ma_result ma_biquad_process_pcm_frames(ma_biquad* pBQ, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount) -{ - ma_uint32 n; - - if (pBQ == NULL || pFramesOut == NULL || pFramesIn == NULL) { - return MA_INVALID_ARGS; - } - - /* Note that the logic below needs to support in-place filtering. That is, it must support the case where pFramesOut and pFramesIn are the same. */ - - if (pBQ->format == ma_format_f32) { - /* */ float* pY = ( float*)pFramesOut; - const float* pX = (const float*)pFramesIn; - - for (n = 0; n < frameCount; n += 1) { - ma_biquad_process_pcm_frame_f32__direct_form_2_transposed(pBQ, pY, pX); - pY += pBQ->channels; - pX += pBQ->channels; - } - } else if (pBQ->format == ma_format_s16) { - /* */ ma_int16* pY = ( ma_int16*)pFramesOut; - const ma_int16* pX = (const ma_int16*)pFramesIn; - - for (n = 0; n < frameCount; n += 1) { - ma_biquad_process_pcm_frame_s16__direct_form_2_transposed(pBQ, pY, pX); - pY += pBQ->channels; - pX += pBQ->channels; - } - } else { - MA_ASSERT(MA_FALSE); - return MA_INVALID_ARGS; /* Format not supported. Should never hit this because it's checked in ma_biquad_init() and ma_biquad_reinit(). */ - } - - return MA_SUCCESS; -} - -MA_API ma_uint32 ma_biquad_get_latency(const ma_biquad* pBQ) -{ - if (pBQ == NULL) { - return 0; - } - - return 2; -} - - -/************************************************************************************************************************************************************** - -Low-Pass Filter - -**************************************************************************************************************************************************************/ -MA_API ma_lpf1_config ma_lpf1_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRate, double cutoffFrequency) -{ - ma_lpf1_config config; - - MA_ZERO_OBJECT(&config); - config.format = format; - config.channels = channels; - config.sampleRate = sampleRate; - config.cutoffFrequency = cutoffFrequency; - config.q = 0.5; - - return config; -} - -MA_API ma_lpf2_config ma_lpf2_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRate, double cutoffFrequency, double q) -{ - ma_lpf2_config config; - - MA_ZERO_OBJECT(&config); - config.format = format; - config.channels = channels; - config.sampleRate = sampleRate; - config.cutoffFrequency = cutoffFrequency; - config.q = q; - - /* Q cannot be 0 or else it'll result in a division by 0. In this case just default to 0.707107. */ - if (config.q == 0) { - config.q = 0.707107; - } - - return config; -} - - -typedef struct -{ - size_t sizeInBytes; - size_t r1Offset; -} ma_lpf1_heap_layout; - -static ma_result ma_lpf1_get_heap_layout(const ma_lpf1_config* pConfig, ma_lpf1_heap_layout* pHeapLayout) -{ - MA_ASSERT(pHeapLayout != NULL); - - MA_ZERO_OBJECT(pHeapLayout); - - if (pConfig == NULL) { - return MA_INVALID_ARGS; - } - - if (pConfig->channels == 0) { - return MA_INVALID_ARGS; - } - - pHeapLayout->sizeInBytes = 0; - - /* R1 */ - pHeapLayout->r1Offset = pHeapLayout->sizeInBytes; - pHeapLayout->sizeInBytes += sizeof(ma_biquad_coefficient) * pConfig->channels; - - /* Make sure allocation size is aligned. */ - pHeapLayout->sizeInBytes = ma_align_64(pHeapLayout->sizeInBytes); - - return MA_SUCCESS; -} - -MA_API ma_result ma_lpf1_get_heap_size(const ma_lpf1_config* pConfig, size_t* pHeapSizeInBytes) -{ - ma_result result; - ma_lpf1_heap_layout heapLayout; - - if (pHeapSizeInBytes == NULL) { - return MA_INVALID_ARGS; - } - - result = ma_lpf1_get_heap_layout(pConfig, &heapLayout); - if (result != MA_SUCCESS) { - return result; - } - - *pHeapSizeInBytes = heapLayout.sizeInBytes; - - return MA_SUCCESS; -} - -MA_API ma_result ma_lpf1_init_preallocated(const ma_lpf1_config* pConfig, void* pHeap, ma_lpf1* pLPF) -{ - ma_result result; - ma_lpf1_heap_layout heapLayout; - - if (pLPF == NULL) { - return MA_INVALID_ARGS; - } - - MA_ZERO_OBJECT(pLPF); - - result = ma_lpf1_get_heap_layout(pConfig, &heapLayout); - if (result != MA_SUCCESS) { - return result; - } - - pLPF->_pHeap = pHeap; - MA_ZERO_MEMORY(pHeap, heapLayout.sizeInBytes); - - pLPF->pR1 = (ma_biquad_coefficient*)ma_offset_ptr(pHeap, heapLayout.r1Offset); - - return ma_lpf1_reinit(pConfig, pLPF); -} - -MA_API ma_result ma_lpf1_init(const ma_lpf1_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_lpf1* pLPF) -{ - ma_result result; - size_t heapSizeInBytes; - void* pHeap; - - result = ma_lpf1_get_heap_size(pConfig, &heapSizeInBytes); - if (result != MA_SUCCESS) { - return result; - } - - if (heapSizeInBytes > 0) { - pHeap = ma_malloc(heapSizeInBytes, pAllocationCallbacks); - if (pHeap == NULL) { - return MA_OUT_OF_MEMORY; - } - } else { - pHeap = NULL; - } - - result = ma_lpf1_init_preallocated(pConfig, pHeap, pLPF); - if (result != MA_SUCCESS) { - ma_free(pHeap, pAllocationCallbacks); - return result; - } - - pLPF->_ownsHeap = MA_TRUE; - return MA_SUCCESS; -} - -MA_API void ma_lpf1_uninit(ma_lpf1* pLPF, const ma_allocation_callbacks* pAllocationCallbacks) -{ - if (pLPF == NULL) { - return; - } - - if (pLPF->_ownsHeap) { - ma_free(pLPF->_pHeap, pAllocationCallbacks); - } -} - -MA_API ma_result ma_lpf1_reinit(const ma_lpf1_config* pConfig, ma_lpf1* pLPF) -{ - double a; - - if (pLPF == NULL || pConfig == NULL) { - return MA_INVALID_ARGS; - } - - /* Only supporting f32 and s16. */ - if (pConfig->format != ma_format_f32 && pConfig->format != ma_format_s16) { - return MA_INVALID_ARGS; - } - - /* The format cannot be changed after initialization. */ - if (pLPF->format != ma_format_unknown && pLPF->format != pConfig->format) { - return MA_INVALID_OPERATION; - } - - /* The channel count cannot be changed after initialization. */ - if (pLPF->channels != 0 && pLPF->channels != pConfig->channels) { - return MA_INVALID_OPERATION; - } - - pLPF->format = pConfig->format; - pLPF->channels = pConfig->channels; - - a = ma_expd(-2 * MA_PI_D * pConfig->cutoffFrequency / pConfig->sampleRate); - if (pConfig->format == ma_format_f32) { - pLPF->a.f32 = (float)a; - } else { - pLPF->a.s32 = ma_biquad_float_to_fp(a); - } - - return MA_SUCCESS; -} - -MA_API ma_result ma_lpf1_clear_cache(ma_lpf1* pLPF) -{ - if (pLPF == NULL) { - return MA_INVALID_ARGS; - } - - if (pLPF->format == ma_format_f32) { - pLPF->a.f32 = 0; - } else { - pLPF->a.s32 = 0; - } - - return MA_SUCCESS; -} - -static MA_INLINE void ma_lpf1_process_pcm_frame_f32(ma_lpf1* pLPF, float* pY, const float* pX) -{ - ma_uint32 c; - const ma_uint32 channels = pLPF->channels; - const float a = pLPF->a.f32; - const float b = 1 - a; - - MA_ASSUME(channels > 0); - for (c = 0; c < channels; c += 1) { - float r1 = pLPF->pR1[c].f32; - float x = pX[c]; - float y; - - y = b*x + a*r1; - - pY[c] = y; - pLPF->pR1[c].f32 = y; - } -} - -static MA_INLINE void ma_lpf1_process_pcm_frame_s16(ma_lpf1* pLPF, ma_int16* pY, const ma_int16* pX) -{ - ma_uint32 c; - const ma_uint32 channels = pLPF->channels; - const ma_int32 a = pLPF->a.s32; - const ma_int32 b = ((1 << MA_BIQUAD_FIXED_POINT_SHIFT) - a); - - MA_ASSUME(channels > 0); - for (c = 0; c < channels; c += 1) { - ma_int32 r1 = pLPF->pR1[c].s32; - ma_int32 x = pX[c]; - ma_int32 y; - - y = (b*x + a*r1) >> MA_BIQUAD_FIXED_POINT_SHIFT; - - pY[c] = (ma_int16)y; - pLPF->pR1[c].s32 = (ma_int32)y; - } -} - -MA_API ma_result ma_lpf1_process_pcm_frames(ma_lpf1* pLPF, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount) -{ - ma_uint32 n; - - if (pLPF == NULL || pFramesOut == NULL || pFramesIn == NULL) { - return MA_INVALID_ARGS; - } - - /* Note that the logic below needs to support in-place filtering. That is, it must support the case where pFramesOut and pFramesIn are the same. */ - - if (pLPF->format == ma_format_f32) { - /* */ float* pY = ( float*)pFramesOut; - const float* pX = (const float*)pFramesIn; - - for (n = 0; n < frameCount; n += 1) { - ma_lpf1_process_pcm_frame_f32(pLPF, pY, pX); - pY += pLPF->channels; - pX += pLPF->channels; - } - } else if (pLPF->format == ma_format_s16) { - /* */ ma_int16* pY = ( ma_int16*)pFramesOut; - const ma_int16* pX = (const ma_int16*)pFramesIn; - - for (n = 0; n < frameCount; n += 1) { - ma_lpf1_process_pcm_frame_s16(pLPF, pY, pX); - pY += pLPF->channels; - pX += pLPF->channels; - } - } else { - MA_ASSERT(MA_FALSE); - return MA_INVALID_ARGS; /* Format not supported. Should never hit this because it's checked in ma_biquad_init() and ma_biquad_reinit(). */ - } - - return MA_SUCCESS; -} - -MA_API ma_uint32 ma_lpf1_get_latency(const ma_lpf1* pLPF) -{ - if (pLPF == NULL) { - return 0; - } - - return 1; -} - - -static MA_INLINE ma_biquad_config ma_lpf2__get_biquad_config(const ma_lpf2_config* pConfig) -{ - ma_biquad_config bqConfig; - double q; - double w; - double s; - double c; - double a; - - MA_ASSERT(pConfig != NULL); - - q = pConfig->q; - w = 2 * MA_PI_D * pConfig->cutoffFrequency / pConfig->sampleRate; - s = ma_sind(w); - c = ma_cosd(w); - a = s / (2*q); - - bqConfig.b0 = (1 - c) / 2; - bqConfig.b1 = 1 - c; - bqConfig.b2 = (1 - c) / 2; - bqConfig.a0 = 1 + a; - bqConfig.a1 = -2 * c; - bqConfig.a2 = 1 - a; - - bqConfig.format = pConfig->format; - bqConfig.channels = pConfig->channels; - - return bqConfig; -} - -MA_API ma_result ma_lpf2_get_heap_size(const ma_lpf2_config* pConfig, size_t* pHeapSizeInBytes) -{ - ma_biquad_config bqConfig; - bqConfig = ma_lpf2__get_biquad_config(pConfig); - - return ma_biquad_get_heap_size(&bqConfig, pHeapSizeInBytes); -} - -MA_API ma_result ma_lpf2_init_preallocated(const ma_lpf2_config* pConfig, void* pHeap, ma_lpf2* pLPF) -{ - ma_result result; - ma_biquad_config bqConfig; - - if (pLPF == NULL) { - return MA_INVALID_ARGS; - } - - MA_ZERO_OBJECT(pLPF); - - if (pConfig == NULL) { - return MA_INVALID_ARGS; - } - - bqConfig = ma_lpf2__get_biquad_config(pConfig); - result = ma_biquad_init_preallocated(&bqConfig, pHeap, &pLPF->bq); - if (result != MA_SUCCESS) { - return result; - } - - return MA_SUCCESS; -} - -MA_API ma_result ma_lpf2_init(const ma_lpf2_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_lpf2* pLPF) -{ - ma_result result; - size_t heapSizeInBytes; - void* pHeap; - - result = ma_lpf2_get_heap_size(pConfig, &heapSizeInBytes); - if (result != MA_SUCCESS) { - return result; - } - - if (heapSizeInBytes > 0) { - pHeap = ma_malloc(heapSizeInBytes, pAllocationCallbacks); - if (pHeap == NULL) { - return MA_OUT_OF_MEMORY; - } - } else { - pHeap = NULL; - } - - result = ma_lpf2_init_preallocated(pConfig, pHeap, pLPF); - if (result != MA_SUCCESS) { - ma_free(pHeap, pAllocationCallbacks); - return result; - } - - pLPF->bq._ownsHeap = MA_TRUE; /* <-- This will cause the biquad to take ownership of the heap and free it when it's uninitialized. */ - return MA_SUCCESS; -} - -MA_API void ma_lpf2_uninit(ma_lpf2* pLPF, const ma_allocation_callbacks* pAllocationCallbacks) -{ - if (pLPF == NULL) { - return; - } - - ma_biquad_uninit(&pLPF->bq, pAllocationCallbacks); /* <-- This will free the heap allocation. */ -} - -MA_API ma_result ma_lpf2_reinit(const ma_lpf2_config* pConfig, ma_lpf2* pLPF) -{ - ma_result result; - ma_biquad_config bqConfig; - - if (pLPF == NULL || pConfig == NULL) { - return MA_INVALID_ARGS; - } - - bqConfig = ma_lpf2__get_biquad_config(pConfig); - result = ma_biquad_reinit(&bqConfig, &pLPF->bq); - if (result != MA_SUCCESS) { - return result; - } - - return MA_SUCCESS; -} - -MA_API ma_result ma_lpf2_clear_cache(ma_lpf2* pLPF) -{ - if (pLPF == NULL) { - return MA_INVALID_ARGS; - } - - ma_biquad_clear_cache(&pLPF->bq); - - return MA_SUCCESS; -} - -static MA_INLINE void ma_lpf2_process_pcm_frame_s16(ma_lpf2* pLPF, ma_int16* pFrameOut, const ma_int16* pFrameIn) -{ - ma_biquad_process_pcm_frame_s16(&pLPF->bq, pFrameOut, pFrameIn); -} - -static MA_INLINE void ma_lpf2_process_pcm_frame_f32(ma_lpf2* pLPF, float* pFrameOut, const float* pFrameIn) -{ - ma_biquad_process_pcm_frame_f32(&pLPF->bq, pFrameOut, pFrameIn); -} - -MA_API ma_result ma_lpf2_process_pcm_frames(ma_lpf2* pLPF, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount) -{ - if (pLPF == NULL) { - return MA_INVALID_ARGS; - } - - return ma_biquad_process_pcm_frames(&pLPF->bq, pFramesOut, pFramesIn, frameCount); -} - -MA_API ma_uint32 ma_lpf2_get_latency(const ma_lpf2* pLPF) -{ - if (pLPF == NULL) { - return 0; - } - - return ma_biquad_get_latency(&pLPF->bq); -} - - -MA_API ma_lpf_config ma_lpf_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRate, double cutoffFrequency, ma_uint32 order) -{ - ma_lpf_config config; - - MA_ZERO_OBJECT(&config); - config.format = format; - config.channels = channels; - config.sampleRate = sampleRate; - config.cutoffFrequency = cutoffFrequency; - config.order = ma_min(order, MA_MAX_FILTER_ORDER); - - return config; -} - - -typedef struct -{ - size_t sizeInBytes; - size_t lpf1Offset; - size_t lpf2Offset; /* Offset of the first second order filter. Subsequent filters will come straight after, and will each have the same heap size. */ -} ma_lpf_heap_layout; - -static void ma_lpf_calculate_sub_lpf_counts(ma_uint32 order, ma_uint32* pLPF1Count, ma_uint32* pLPF2Count) -{ - MA_ASSERT(pLPF1Count != NULL); - MA_ASSERT(pLPF2Count != NULL); - - *pLPF1Count = order % 2; - *pLPF2Count = order / 2; -} - -static ma_result ma_lpf_get_heap_layout(const ma_lpf_config* pConfig, ma_lpf_heap_layout* pHeapLayout) -{ - ma_result result; - ma_uint32 lpf1Count; - ma_uint32 lpf2Count; - ma_uint32 ilpf1; - ma_uint32 ilpf2; - - MA_ASSERT(pHeapLayout != NULL); - - MA_ZERO_OBJECT(pHeapLayout); - - if (pConfig == NULL) { - return MA_INVALID_ARGS; - } - - if (pConfig->channels == 0) { - return MA_INVALID_ARGS; - } - - if (pConfig->order > MA_MAX_FILTER_ORDER) { - return MA_INVALID_ARGS; - } - - ma_lpf_calculate_sub_lpf_counts(pConfig->order, &lpf1Count, &lpf2Count); - - pHeapLayout->sizeInBytes = 0; - - /* LPF 1 */ - pHeapLayout->lpf1Offset = pHeapLayout->sizeInBytes; - for (ilpf1 = 0; ilpf1 < lpf1Count; ilpf1 += 1) { - size_t lpf1HeapSizeInBytes; - ma_lpf1_config lpf1Config = ma_lpf1_config_init(pConfig->format, pConfig->channels, pConfig->sampleRate, pConfig->cutoffFrequency); - - result = ma_lpf1_get_heap_size(&lpf1Config, &lpf1HeapSizeInBytes); - if (result != MA_SUCCESS) { - return result; - } - - pHeapLayout->sizeInBytes += sizeof(ma_lpf1) + lpf1HeapSizeInBytes; - } - - /* LPF 2*/ - pHeapLayout->lpf2Offset = pHeapLayout->sizeInBytes; - for (ilpf2 = 0; ilpf2 < lpf2Count; ilpf2 += 1) { - size_t lpf2HeapSizeInBytes; - ma_lpf2_config lpf2Config = ma_lpf2_config_init(pConfig->format, pConfig->channels, pConfig->sampleRate, pConfig->cutoffFrequency, 0.707107); /* <-- The "q" parameter does not matter for the purpose of calculating the heap size. */ - - result = ma_lpf2_get_heap_size(&lpf2Config, &lpf2HeapSizeInBytes); - if (result != MA_SUCCESS) { - return result; - } - - pHeapLayout->sizeInBytes += sizeof(ma_lpf2) + lpf2HeapSizeInBytes; - } - - /* Make sure allocation size is aligned. */ - pHeapLayout->sizeInBytes = ma_align_64(pHeapLayout->sizeInBytes); - - return MA_SUCCESS; -} - -static ma_result ma_lpf_reinit__internal(const ma_lpf_config* pConfig, void* pHeap, ma_lpf* pLPF, ma_bool32 isNew) -{ - ma_result result; - ma_uint32 lpf1Count; - ma_uint32 lpf2Count; - ma_uint32 ilpf1; - ma_uint32 ilpf2; - ma_lpf_heap_layout heapLayout; /* Only used if isNew is true. */ - - if (pLPF == NULL || pConfig == NULL) { - return MA_INVALID_ARGS; - } - - /* Only supporting f32 and s16. */ - if (pConfig->format != ma_format_f32 && pConfig->format != ma_format_s16) { - return MA_INVALID_ARGS; - } - - /* The format cannot be changed after initialization. */ - if (pLPF->format != ma_format_unknown && pLPF->format != pConfig->format) { - return MA_INVALID_OPERATION; - } - - /* The channel count cannot be changed after initialization. */ - if (pLPF->channels != 0 && pLPF->channels != pConfig->channels) { - return MA_INVALID_OPERATION; - } - - if (pConfig->order > MA_MAX_FILTER_ORDER) { - return MA_INVALID_ARGS; - } - - ma_lpf_calculate_sub_lpf_counts(pConfig->order, &lpf1Count, &lpf2Count); - - /* The filter order can't change between reinits. */ - if (!isNew) { - if (pLPF->lpf1Count != lpf1Count || pLPF->lpf2Count != lpf2Count) { - return MA_INVALID_OPERATION; - } - } - - if (isNew) { - result = ma_lpf_get_heap_layout(pConfig, &heapLayout); - if (result != MA_SUCCESS) { - return result; - } - - pLPF->_pHeap = pHeap; - MA_ZERO_MEMORY(pHeap, heapLayout.sizeInBytes); - - pLPF->pLPF1 = (ma_lpf1*)ma_offset_ptr(pHeap, heapLayout.lpf1Offset); - pLPF->pLPF2 = (ma_lpf2*)ma_offset_ptr(pHeap, heapLayout.lpf2Offset); - } else { - MA_ZERO_OBJECT(&heapLayout); /* To silence a compiler warning. */ - } - - for (ilpf1 = 0; ilpf1 < lpf1Count; ilpf1 += 1) { - ma_lpf1_config lpf1Config = ma_lpf1_config_init(pConfig->format, pConfig->channels, pConfig->sampleRate, pConfig->cutoffFrequency); - - if (isNew) { - size_t lpf1HeapSizeInBytes; - - result = ma_lpf1_get_heap_size(&lpf1Config, &lpf1HeapSizeInBytes); - if (result == MA_SUCCESS) { - result = ma_lpf1_init_preallocated(&lpf1Config, ma_offset_ptr(pHeap, heapLayout.lpf1Offset + (sizeof(ma_lpf1) * lpf1Count) + (ilpf1 * lpf1HeapSizeInBytes)), &pLPF->pLPF1[ilpf1]); - } - } else { - result = ma_lpf1_reinit(&lpf1Config, &pLPF->pLPF1[ilpf1]); - } - - if (result != MA_SUCCESS) { - ma_uint32 jlpf1; - - for (jlpf1 = 0; jlpf1 < ilpf1; jlpf1 += 1) { - ma_lpf1_uninit(&pLPF->pLPF1[jlpf1], NULL); /* No need for allocation callbacks here since we used a preallocated heap allocation. */ - } - - return result; - } - } - - for (ilpf2 = 0; ilpf2 < lpf2Count; ilpf2 += 1) { - ma_lpf2_config lpf2Config; - double q; - double a; - - /* Tempting to use 0.707107, but won't result in a Butterworth filter if the order is > 2. */ - if (lpf1Count == 1) { - a = (1 + ilpf2*1) * (MA_PI_D/(pConfig->order*1)); /* Odd order. */ - } else { - a = (1 + ilpf2*2) * (MA_PI_D/(pConfig->order*2)); /* Even order. */ - } - q = 1 / (2*ma_cosd(a)); - - lpf2Config = ma_lpf2_config_init(pConfig->format, pConfig->channels, pConfig->sampleRate, pConfig->cutoffFrequency, q); - - if (isNew) { - size_t lpf2HeapSizeInBytes; - - result = ma_lpf2_get_heap_size(&lpf2Config, &lpf2HeapSizeInBytes); - if (result == MA_SUCCESS) { - result = ma_lpf2_init_preallocated(&lpf2Config, ma_offset_ptr(pHeap, heapLayout.lpf2Offset + (sizeof(ma_lpf2) * lpf2Count) + (ilpf2 * lpf2HeapSizeInBytes)), &pLPF->pLPF2[ilpf2]); - } - } else { - result = ma_lpf2_reinit(&lpf2Config, &pLPF->pLPF2[ilpf2]); - } - - if (result != MA_SUCCESS) { - ma_uint32 jlpf1; - ma_uint32 jlpf2; - - for (jlpf1 = 0; jlpf1 < lpf1Count; jlpf1 += 1) { - ma_lpf1_uninit(&pLPF->pLPF1[jlpf1], NULL); /* No need for allocation callbacks here since we used a preallocated heap allocation. */ - } - - for (jlpf2 = 0; jlpf2 < ilpf2; jlpf2 += 1) { - ma_lpf2_uninit(&pLPF->pLPF2[jlpf2], NULL); /* No need for allocation callbacks here since we used a preallocated heap allocation. */ - } - - return result; - } - } - - pLPF->lpf1Count = lpf1Count; - pLPF->lpf2Count = lpf2Count; - pLPF->format = pConfig->format; - pLPF->channels = pConfig->channels; - pLPF->sampleRate = pConfig->sampleRate; - - return MA_SUCCESS; -} - -MA_API ma_result ma_lpf_get_heap_size(const ma_lpf_config* pConfig, size_t* pHeapSizeInBytes) -{ - ma_result result; - ma_lpf_heap_layout heapLayout; - - if (pHeapSizeInBytes == NULL) { - return MA_INVALID_ARGS; - } - - *pHeapSizeInBytes = 0; - - result = ma_lpf_get_heap_layout(pConfig, &heapLayout); - if (result != MA_SUCCESS) { - return result; - } - - *pHeapSizeInBytes = heapLayout.sizeInBytes; - - return result; -} - -MA_API ma_result ma_lpf_init_preallocated(const ma_lpf_config* pConfig, void* pHeap, ma_lpf* pLPF) -{ - if (pLPF == NULL) { - return MA_INVALID_ARGS; - } - - MA_ZERO_OBJECT(pLPF); - - return ma_lpf_reinit__internal(pConfig, pHeap, pLPF, /*isNew*/MA_TRUE); -} - -MA_API ma_result ma_lpf_init(const ma_lpf_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_lpf* pLPF) -{ - ma_result result; - size_t heapSizeInBytes; - void* pHeap; - - result = ma_lpf_get_heap_size(pConfig, &heapSizeInBytes); - if (result != MA_SUCCESS) { - return result; - } - - if (heapSizeInBytes > 0) { - pHeap = ma_malloc(heapSizeInBytes, pAllocationCallbacks); - if (pHeap == NULL) { - return MA_OUT_OF_MEMORY; - } - } else { - pHeap = NULL; - } - - result = ma_lpf_init_preallocated(pConfig, pHeap, pLPF); - if (result != MA_SUCCESS) { - ma_free(pHeap, pAllocationCallbacks); - return result; - } - - pLPF->_ownsHeap = MA_TRUE; - return MA_SUCCESS; -} - -MA_API void ma_lpf_uninit(ma_lpf* pLPF, const ma_allocation_callbacks* pAllocationCallbacks) -{ - ma_uint32 ilpf1; - ma_uint32 ilpf2; - - if (pLPF == NULL) { - return; - } - - for (ilpf1 = 0; ilpf1 < pLPF->lpf1Count; ilpf1 += 1) { - ma_lpf1_uninit(&pLPF->pLPF1[ilpf1], pAllocationCallbacks); - } - - for (ilpf2 = 0; ilpf2 < pLPF->lpf2Count; ilpf2 += 1) { - ma_lpf2_uninit(&pLPF->pLPF2[ilpf2], pAllocationCallbacks); - } - - if (pLPF->_ownsHeap) { - ma_free(pLPF->_pHeap, pAllocationCallbacks); - } -} - -MA_API ma_result ma_lpf_reinit(const ma_lpf_config* pConfig, ma_lpf* pLPF) -{ - return ma_lpf_reinit__internal(pConfig, NULL, pLPF, /*isNew*/MA_FALSE); -} - -MA_API ma_result ma_lpf_clear_cache(ma_lpf* pLPF) -{ - ma_uint32 ilpf1; - ma_uint32 ilpf2; - - if (pLPF == NULL) { - return MA_INVALID_ARGS; - } - - for (ilpf1 = 0; ilpf1 < pLPF->lpf1Count; ilpf1 += 1) { - ma_lpf1_clear_cache(&pLPF->pLPF1[ilpf1]); - } - - for (ilpf2 = 0; ilpf2 < pLPF->lpf2Count; ilpf2 += 1) { - ma_lpf2_clear_cache(&pLPF->pLPF2[ilpf2]); - } - - return MA_SUCCESS; -} - -static MA_INLINE void ma_lpf_process_pcm_frame_f32(ma_lpf* pLPF, float* pY, const void* pX) -{ - ma_uint32 ilpf1; - ma_uint32 ilpf2; - - MA_ASSERT(pLPF->format == ma_format_f32); - - MA_MOVE_MEMORY(pY, pX, ma_get_bytes_per_frame(pLPF->format, pLPF->channels)); - - for (ilpf1 = 0; ilpf1 < pLPF->lpf1Count; ilpf1 += 1) { - ma_lpf1_process_pcm_frame_f32(&pLPF->pLPF1[ilpf1], pY, pY); - } - - for (ilpf2 = 0; ilpf2 < pLPF->lpf2Count; ilpf2 += 1) { - ma_lpf2_process_pcm_frame_f32(&pLPF->pLPF2[ilpf2], pY, pY); - } -} - -static MA_INLINE void ma_lpf_process_pcm_frame_s16(ma_lpf* pLPF, ma_int16* pY, const ma_int16* pX) -{ - ma_uint32 ilpf1; - ma_uint32 ilpf2; - - MA_ASSERT(pLPF->format == ma_format_s16); - - MA_MOVE_MEMORY(pY, pX, ma_get_bytes_per_frame(pLPF->format, pLPF->channels)); - - for (ilpf1 = 0; ilpf1 < pLPF->lpf1Count; ilpf1 += 1) { - ma_lpf1_process_pcm_frame_s16(&pLPF->pLPF1[ilpf1], pY, pY); - } - - for (ilpf2 = 0; ilpf2 < pLPF->lpf2Count; ilpf2 += 1) { - ma_lpf2_process_pcm_frame_s16(&pLPF->pLPF2[ilpf2], pY, pY); - } -} - -MA_API ma_result ma_lpf_process_pcm_frames(ma_lpf* pLPF, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount) -{ - ma_result result; - ma_uint32 ilpf1; - ma_uint32 ilpf2; - - if (pLPF == NULL) { - return MA_INVALID_ARGS; - } - - /* Faster path for in-place. */ - if (pFramesOut == pFramesIn) { - for (ilpf1 = 0; ilpf1 < pLPF->lpf1Count; ilpf1 += 1) { - result = ma_lpf1_process_pcm_frames(&pLPF->pLPF1[ilpf1], pFramesOut, pFramesOut, frameCount); - if (result != MA_SUCCESS) { - return result; - } - } - - for (ilpf2 = 0; ilpf2 < pLPF->lpf2Count; ilpf2 += 1) { - result = ma_lpf2_process_pcm_frames(&pLPF->pLPF2[ilpf2], pFramesOut, pFramesOut, frameCount); - if (result != MA_SUCCESS) { - return result; - } - } - } - - /* Slightly slower path for copying. */ - if (pFramesOut != pFramesIn) { - ma_uint32 iFrame; - - /* */ if (pLPF->format == ma_format_f32) { - /* */ float* pFramesOutF32 = ( float*)pFramesOut; - const float* pFramesInF32 = (const float*)pFramesIn; - - for (iFrame = 0; iFrame < frameCount; iFrame += 1) { - ma_lpf_process_pcm_frame_f32(pLPF, pFramesOutF32, pFramesInF32); - pFramesOutF32 += pLPF->channels; - pFramesInF32 += pLPF->channels; - } - } else if (pLPF->format == ma_format_s16) { - /* */ ma_int16* pFramesOutS16 = ( ma_int16*)pFramesOut; - const ma_int16* pFramesInS16 = (const ma_int16*)pFramesIn; - - for (iFrame = 0; iFrame < frameCount; iFrame += 1) { - ma_lpf_process_pcm_frame_s16(pLPF, pFramesOutS16, pFramesInS16); - pFramesOutS16 += pLPF->channels; - pFramesInS16 += pLPF->channels; - } - } else { - MA_ASSERT(MA_FALSE); - return MA_INVALID_OPERATION; /* Should never hit this. */ - } - } - - return MA_SUCCESS; -} - -MA_API ma_uint32 ma_lpf_get_latency(const ma_lpf* pLPF) -{ - if (pLPF == NULL) { - return 0; - } - - return pLPF->lpf2Count*2 + pLPF->lpf1Count; -} - - -/************************************************************************************************************************************************************** - -High-Pass Filtering - -**************************************************************************************************************************************************************/ -MA_API ma_hpf1_config ma_hpf1_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRate, double cutoffFrequency) -{ - ma_hpf1_config config; - - MA_ZERO_OBJECT(&config); - config.format = format; - config.channels = channels; - config.sampleRate = sampleRate; - config.cutoffFrequency = cutoffFrequency; - - return config; -} - -MA_API ma_hpf2_config ma_hpf2_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRate, double cutoffFrequency, double q) -{ - ma_hpf2_config config; - - MA_ZERO_OBJECT(&config); - config.format = format; - config.channels = channels; - config.sampleRate = sampleRate; - config.cutoffFrequency = cutoffFrequency; - config.q = q; - - /* Q cannot be 0 or else it'll result in a division by 0. In this case just default to 0.707107. */ - if (config.q == 0) { - config.q = 0.707107; - } - - return config; -} - - -typedef struct -{ - size_t sizeInBytes; - size_t r1Offset; -} ma_hpf1_heap_layout; - -static ma_result ma_hpf1_get_heap_layout(const ma_hpf1_config* pConfig, ma_hpf1_heap_layout* pHeapLayout) -{ - MA_ASSERT(pHeapLayout != NULL); - - MA_ZERO_OBJECT(pHeapLayout); - - if (pConfig == NULL) { - return MA_INVALID_ARGS; - } - - if (pConfig->channels == 0) { - return MA_INVALID_ARGS; - } - - pHeapLayout->sizeInBytes = 0; - - /* R1 */ - pHeapLayout->r1Offset = pHeapLayout->sizeInBytes; - pHeapLayout->sizeInBytes += sizeof(ma_biquad_coefficient) * pConfig->channels; - - /* Make sure allocation size is aligned. */ - pHeapLayout->sizeInBytes = ma_align_64(pHeapLayout->sizeInBytes); - - return MA_SUCCESS; -} - -MA_API ma_result ma_hpf1_get_heap_size(const ma_hpf1_config* pConfig, size_t* pHeapSizeInBytes) -{ - ma_result result; - ma_hpf1_heap_layout heapLayout; - - if (pHeapSizeInBytes == NULL) { - return MA_INVALID_ARGS; - } - - result = ma_hpf1_get_heap_layout(pConfig, &heapLayout); - if (result != MA_SUCCESS) { - return result; - } - - *pHeapSizeInBytes = heapLayout.sizeInBytes; - - return MA_SUCCESS; -} - -MA_API ma_result ma_hpf1_init_preallocated(const ma_hpf1_config* pConfig, void* pHeap, ma_hpf1* pLPF) -{ - ma_result result; - ma_hpf1_heap_layout heapLayout; - - if (pLPF == NULL) { - return MA_INVALID_ARGS; - } - - MA_ZERO_OBJECT(pLPF); - - result = ma_hpf1_get_heap_layout(pConfig, &heapLayout); - if (result != MA_SUCCESS) { - return result; - } - - pLPF->_pHeap = pHeap; - MA_ZERO_MEMORY(pHeap, heapLayout.sizeInBytes); - - pLPF->pR1 = (ma_biquad_coefficient*)ma_offset_ptr(pHeap, heapLayout.r1Offset); - - return ma_hpf1_reinit(pConfig, pLPF); -} - -MA_API ma_result ma_hpf1_init(const ma_hpf1_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_hpf1* pLPF) -{ - ma_result result; - size_t heapSizeInBytes; - void* pHeap; - - result = ma_hpf1_get_heap_size(pConfig, &heapSizeInBytes); - if (result != MA_SUCCESS) { - return result; - } - - if (heapSizeInBytes > 0) { - pHeap = ma_malloc(heapSizeInBytes, pAllocationCallbacks); - if (pHeap == NULL) { - return MA_OUT_OF_MEMORY; - } - } else { - pHeap = NULL; - } - - result = ma_hpf1_init_preallocated(pConfig, pHeap, pLPF); - if (result != MA_SUCCESS) { - ma_free(pHeap, pAllocationCallbacks); - return result; - } - - pLPF->_ownsHeap = MA_TRUE; - return MA_SUCCESS; -} - -MA_API void ma_hpf1_uninit(ma_hpf1* pHPF, const ma_allocation_callbacks* pAllocationCallbacks) -{ - if (pHPF == NULL) { - return; - } - - if (pHPF->_ownsHeap) { - ma_free(pHPF->_pHeap, pAllocationCallbacks); - } -} - -MA_API ma_result ma_hpf1_reinit(const ma_hpf1_config* pConfig, ma_hpf1* pHPF) -{ - double a; - - if (pHPF == NULL || pConfig == NULL) { - return MA_INVALID_ARGS; - } - - /* Only supporting f32 and s16. */ - if (pConfig->format != ma_format_f32 && pConfig->format != ma_format_s16) { - return MA_INVALID_ARGS; - } - - /* The format cannot be changed after initialization. */ - if (pHPF->format != ma_format_unknown && pHPF->format != pConfig->format) { - return MA_INVALID_OPERATION; - } - - /* The channel count cannot be changed after initialization. */ - if (pHPF->channels != 0 && pHPF->channels != pConfig->channels) { - return MA_INVALID_OPERATION; - } - - pHPF->format = pConfig->format; - pHPF->channels = pConfig->channels; - - a = ma_expd(-2 * MA_PI_D * pConfig->cutoffFrequency / pConfig->sampleRate); - if (pConfig->format == ma_format_f32) { - pHPF->a.f32 = (float)a; - } else { - pHPF->a.s32 = ma_biquad_float_to_fp(a); - } - - return MA_SUCCESS; -} - -static MA_INLINE void ma_hpf1_process_pcm_frame_f32(ma_hpf1* pHPF, float* pY, const float* pX) -{ - ma_uint32 c; - const ma_uint32 channels = pHPF->channels; - const float a = 1 - pHPF->a.f32; - const float b = 1 - a; - - MA_ASSUME(channels > 0); - for (c = 0; c < channels; c += 1) { - float r1 = pHPF->pR1[c].f32; - float x = pX[c]; - float y; - - y = b*x - a*r1; - - pY[c] = y; - pHPF->pR1[c].f32 = y; - } -} - -static MA_INLINE void ma_hpf1_process_pcm_frame_s16(ma_hpf1* pHPF, ma_int16* pY, const ma_int16* pX) -{ - ma_uint32 c; - const ma_uint32 channels = pHPF->channels; - const ma_int32 a = ((1 << MA_BIQUAD_FIXED_POINT_SHIFT) - pHPF->a.s32); - const ma_int32 b = ((1 << MA_BIQUAD_FIXED_POINT_SHIFT) - a); - - MA_ASSUME(channels > 0); - for (c = 0; c < channels; c += 1) { - ma_int32 r1 = pHPF->pR1[c].s32; - ma_int32 x = pX[c]; - ma_int32 y; - - y = (b*x - a*r1) >> MA_BIQUAD_FIXED_POINT_SHIFT; - - pY[c] = (ma_int16)y; - pHPF->pR1[c].s32 = (ma_int32)y; - } -} - -MA_API ma_result ma_hpf1_process_pcm_frames(ma_hpf1* pHPF, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount) -{ - ma_uint32 n; - - if (pHPF == NULL || pFramesOut == NULL || pFramesIn == NULL) { - return MA_INVALID_ARGS; - } - - /* Note that the logic below needs to support in-place filtering. That is, it must support the case where pFramesOut and pFramesIn are the same. */ - - if (pHPF->format == ma_format_f32) { - /* */ float* pY = ( float*)pFramesOut; - const float* pX = (const float*)pFramesIn; - - for (n = 0; n < frameCount; n += 1) { - ma_hpf1_process_pcm_frame_f32(pHPF, pY, pX); - pY += pHPF->channels; - pX += pHPF->channels; - } - } else if (pHPF->format == ma_format_s16) { - /* */ ma_int16* pY = ( ma_int16*)pFramesOut; - const ma_int16* pX = (const ma_int16*)pFramesIn; - - for (n = 0; n < frameCount; n += 1) { - ma_hpf1_process_pcm_frame_s16(pHPF, pY, pX); - pY += pHPF->channels; - pX += pHPF->channels; - } - } else { - MA_ASSERT(MA_FALSE); - return MA_INVALID_ARGS; /* Format not supported. Should never hit this because it's checked in ma_biquad_init() and ma_biquad_reinit(). */ - } - - return MA_SUCCESS; -} - -MA_API ma_uint32 ma_hpf1_get_latency(const ma_hpf1* pHPF) -{ - if (pHPF == NULL) { - return 0; - } - - return 1; -} - - -static MA_INLINE ma_biquad_config ma_hpf2__get_biquad_config(const ma_hpf2_config* pConfig) -{ - ma_biquad_config bqConfig; - double q; - double w; - double s; - double c; - double a; - - MA_ASSERT(pConfig != NULL); - - q = pConfig->q; - w = 2 * MA_PI_D * pConfig->cutoffFrequency / pConfig->sampleRate; - s = ma_sind(w); - c = ma_cosd(w); - a = s / (2*q); - - bqConfig.b0 = (1 + c) / 2; - bqConfig.b1 = -(1 + c); - bqConfig.b2 = (1 + c) / 2; - bqConfig.a0 = 1 + a; - bqConfig.a1 = -2 * c; - bqConfig.a2 = 1 - a; - - bqConfig.format = pConfig->format; - bqConfig.channels = pConfig->channels; - - return bqConfig; -} - -MA_API ma_result ma_hpf2_get_heap_size(const ma_hpf2_config* pConfig, size_t* pHeapSizeInBytes) -{ - ma_biquad_config bqConfig; - bqConfig = ma_hpf2__get_biquad_config(pConfig); - - return ma_biquad_get_heap_size(&bqConfig, pHeapSizeInBytes); -} - -MA_API ma_result ma_hpf2_init_preallocated(const ma_hpf2_config* pConfig, void* pHeap, ma_hpf2* pHPF) -{ - ma_result result; - ma_biquad_config bqConfig; - - if (pHPF == NULL) { - return MA_INVALID_ARGS; - } - - MA_ZERO_OBJECT(pHPF); - - if (pConfig == NULL) { - return MA_INVALID_ARGS; - } - - bqConfig = ma_hpf2__get_biquad_config(pConfig); - result = ma_biquad_init_preallocated(&bqConfig, pHeap, &pHPF->bq); - if (result != MA_SUCCESS) { - return result; - } - - return MA_SUCCESS; -} - -MA_API ma_result ma_hpf2_init(const ma_hpf2_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_hpf2* pHPF) -{ - ma_result result; - size_t heapSizeInBytes; - void* pHeap; - - result = ma_hpf2_get_heap_size(pConfig, &heapSizeInBytes); - if (result != MA_SUCCESS) { - return result; - } - - if (heapSizeInBytes > 0) { - pHeap = ma_malloc(heapSizeInBytes, pAllocationCallbacks); - if (pHeap == NULL) { - return MA_OUT_OF_MEMORY; - } - } else { - pHeap = NULL; - } - - result = ma_hpf2_init_preallocated(pConfig, pHeap, pHPF); - if (result != MA_SUCCESS) { - ma_free(pHeap, pAllocationCallbacks); - return result; - } - - pHPF->bq._ownsHeap = MA_TRUE; /* <-- This will cause the biquad to take ownership of the heap and free it when it's uninitialized. */ - return MA_SUCCESS; -} - -MA_API void ma_hpf2_uninit(ma_hpf2* pHPF, const ma_allocation_callbacks* pAllocationCallbacks) -{ - if (pHPF == NULL) { - return; - } - - ma_biquad_uninit(&pHPF->bq, pAllocationCallbacks); /* <-- This will free the heap allocation. */ -} - -MA_API ma_result ma_hpf2_reinit(const ma_hpf2_config* pConfig, ma_hpf2* pHPF) -{ - ma_result result; - ma_biquad_config bqConfig; - - if (pHPF == NULL || pConfig == NULL) { - return MA_INVALID_ARGS; - } - - bqConfig = ma_hpf2__get_biquad_config(pConfig); - result = ma_biquad_reinit(&bqConfig, &pHPF->bq); - if (result != MA_SUCCESS) { - return result; - } - - return MA_SUCCESS; -} - -static MA_INLINE void ma_hpf2_process_pcm_frame_s16(ma_hpf2* pHPF, ma_int16* pFrameOut, const ma_int16* pFrameIn) -{ - ma_biquad_process_pcm_frame_s16(&pHPF->bq, pFrameOut, pFrameIn); -} - -static MA_INLINE void ma_hpf2_process_pcm_frame_f32(ma_hpf2* pHPF, float* pFrameOut, const float* pFrameIn) -{ - ma_biquad_process_pcm_frame_f32(&pHPF->bq, pFrameOut, pFrameIn); -} - -MA_API ma_result ma_hpf2_process_pcm_frames(ma_hpf2* pHPF, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount) -{ - if (pHPF == NULL) { - return MA_INVALID_ARGS; - } - - return ma_biquad_process_pcm_frames(&pHPF->bq, pFramesOut, pFramesIn, frameCount); -} - -MA_API ma_uint32 ma_hpf2_get_latency(const ma_hpf2* pHPF) -{ - if (pHPF == NULL) { - return 0; - } - - return ma_biquad_get_latency(&pHPF->bq); -} - - -MA_API ma_hpf_config ma_hpf_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRate, double cutoffFrequency, ma_uint32 order) -{ - ma_hpf_config config; - - MA_ZERO_OBJECT(&config); - config.format = format; - config.channels = channels; - config.sampleRate = sampleRate; - config.cutoffFrequency = cutoffFrequency; - config.order = ma_min(order, MA_MAX_FILTER_ORDER); - - return config; -} - - -typedef struct -{ - size_t sizeInBytes; - size_t hpf1Offset; - size_t hpf2Offset; /* Offset of the first second order filter. Subsequent filters will come straight after, and will each have the same heap size. */ -} ma_hpf_heap_layout; - -static void ma_hpf_calculate_sub_hpf_counts(ma_uint32 order, ma_uint32* pHPF1Count, ma_uint32* pHPF2Count) -{ - MA_ASSERT(pHPF1Count != NULL); - MA_ASSERT(pHPF2Count != NULL); - - *pHPF1Count = order % 2; - *pHPF2Count = order / 2; -} - -static ma_result ma_hpf_get_heap_layout(const ma_hpf_config* pConfig, ma_hpf_heap_layout* pHeapLayout) -{ - ma_result result; - ma_uint32 hpf1Count; - ma_uint32 hpf2Count; - ma_uint32 ihpf1; - ma_uint32 ihpf2; - - MA_ASSERT(pHeapLayout != NULL); - - MA_ZERO_OBJECT(pHeapLayout); - - if (pConfig == NULL) { - return MA_INVALID_ARGS; - } - - if (pConfig->channels == 0) { - return MA_INVALID_ARGS; - } - - if (pConfig->order > MA_MAX_FILTER_ORDER) { - return MA_INVALID_ARGS; - } - - ma_hpf_calculate_sub_hpf_counts(pConfig->order, &hpf1Count, &hpf2Count); - - pHeapLayout->sizeInBytes = 0; - - /* HPF 1 */ - pHeapLayout->hpf1Offset = pHeapLayout->sizeInBytes; - for (ihpf1 = 0; ihpf1 < hpf1Count; ihpf1 += 1) { - size_t hpf1HeapSizeInBytes; - ma_hpf1_config hpf1Config = ma_hpf1_config_init(pConfig->format, pConfig->channels, pConfig->sampleRate, pConfig->cutoffFrequency); - - result = ma_hpf1_get_heap_size(&hpf1Config, &hpf1HeapSizeInBytes); - if (result != MA_SUCCESS) { - return result; - } - - pHeapLayout->sizeInBytes += sizeof(ma_hpf1) + hpf1HeapSizeInBytes; - } - - /* HPF 2*/ - pHeapLayout->hpf2Offset = pHeapLayout->sizeInBytes; - for (ihpf2 = 0; ihpf2 < hpf2Count; ihpf2 += 1) { - size_t hpf2HeapSizeInBytes; - ma_hpf2_config hpf2Config = ma_hpf2_config_init(pConfig->format, pConfig->channels, pConfig->sampleRate, pConfig->cutoffFrequency, 0.707107); /* <-- The "q" parameter does not matter for the purpose of calculating the heap size. */ - - result = ma_hpf2_get_heap_size(&hpf2Config, &hpf2HeapSizeInBytes); - if (result != MA_SUCCESS) { - return result; - } - - pHeapLayout->sizeInBytes += sizeof(ma_hpf2) + hpf2HeapSizeInBytes; - } - - /* Make sure allocation size is aligned. */ - pHeapLayout->sizeInBytes = ma_align_64(pHeapLayout->sizeInBytes); - - return MA_SUCCESS; -} - -static ma_result ma_hpf_reinit__internal(const ma_hpf_config* pConfig, void* pHeap, ma_hpf* pHPF, ma_bool32 isNew) -{ - ma_result result; - ma_uint32 hpf1Count; - ma_uint32 hpf2Count; - ma_uint32 ihpf1; - ma_uint32 ihpf2; - ma_hpf_heap_layout heapLayout; /* Only used if isNew is true. */ - - if (pHPF == NULL || pConfig == NULL) { - return MA_INVALID_ARGS; - } - - /* Only supporting f32 and s16. */ - if (pConfig->format != ma_format_f32 && pConfig->format != ma_format_s16) { - return MA_INVALID_ARGS; - } - - /* The format cannot be changed after initialization. */ - if (pHPF->format != ma_format_unknown && pHPF->format != pConfig->format) { - return MA_INVALID_OPERATION; - } - - /* The channel count cannot be changed after initialization. */ - if (pHPF->channels != 0 && pHPF->channels != pConfig->channels) { - return MA_INVALID_OPERATION; - } - - if (pConfig->order > MA_MAX_FILTER_ORDER) { - return MA_INVALID_ARGS; - } - - ma_hpf_calculate_sub_hpf_counts(pConfig->order, &hpf1Count, &hpf2Count); - - /* The filter order can't change between reinits. */ - if (!isNew) { - if (pHPF->hpf1Count != hpf1Count || pHPF->hpf2Count != hpf2Count) { - return MA_INVALID_OPERATION; - } - } - - if (isNew) { - result = ma_hpf_get_heap_layout(pConfig, &heapLayout); - if (result != MA_SUCCESS) { - return result; - } - - pHPF->_pHeap = pHeap; - MA_ZERO_MEMORY(pHeap, heapLayout.sizeInBytes); - - pHPF->pHPF1 = (ma_hpf1*)ma_offset_ptr(pHeap, heapLayout.hpf1Offset); - pHPF->pHPF2 = (ma_hpf2*)ma_offset_ptr(pHeap, heapLayout.hpf2Offset); - } else { - MA_ZERO_OBJECT(&heapLayout); /* To silence a compiler warning. */ - } - - for (ihpf1 = 0; ihpf1 < hpf1Count; ihpf1 += 1) { - ma_hpf1_config hpf1Config = ma_hpf1_config_init(pConfig->format, pConfig->channels, pConfig->sampleRate, pConfig->cutoffFrequency); - - if (isNew) { - size_t hpf1HeapSizeInBytes; - - result = ma_hpf1_get_heap_size(&hpf1Config, &hpf1HeapSizeInBytes); - if (result == MA_SUCCESS) { - result = ma_hpf1_init_preallocated(&hpf1Config, ma_offset_ptr(pHeap, heapLayout.hpf1Offset + (sizeof(ma_hpf1) * hpf1Count) + (ihpf1 * hpf1HeapSizeInBytes)), &pHPF->pHPF1[ihpf1]); - } - } else { - result = ma_hpf1_reinit(&hpf1Config, &pHPF->pHPF1[ihpf1]); - } - - if (result != MA_SUCCESS) { - ma_uint32 jhpf1; - - for (jhpf1 = 0; jhpf1 < ihpf1; jhpf1 += 1) { - ma_hpf1_uninit(&pHPF->pHPF1[jhpf1], NULL); /* No need for allocation callbacks here since we used a preallocated heap allocation. */ - } - - return result; - } - } - - for (ihpf2 = 0; ihpf2 < hpf2Count; ihpf2 += 1) { - ma_hpf2_config hpf2Config; - double q; - double a; - - /* Tempting to use 0.707107, but won't result in a Butterworth filter if the order is > 2. */ - if (hpf1Count == 1) { - a = (1 + ihpf2*1) * (MA_PI_D/(pConfig->order*1)); /* Odd order. */ - } else { - a = (1 + ihpf2*2) * (MA_PI_D/(pConfig->order*2)); /* Even order. */ - } - q = 1 / (2*ma_cosd(a)); - - hpf2Config = ma_hpf2_config_init(pConfig->format, pConfig->channels, pConfig->sampleRate, pConfig->cutoffFrequency, q); - - if (isNew) { - size_t hpf2HeapSizeInBytes; - - result = ma_hpf2_get_heap_size(&hpf2Config, &hpf2HeapSizeInBytes); - if (result == MA_SUCCESS) { - result = ma_hpf2_init_preallocated(&hpf2Config, ma_offset_ptr(pHeap, heapLayout.hpf2Offset + (sizeof(ma_hpf2) * hpf2Count) + (ihpf2 * hpf2HeapSizeInBytes)), &pHPF->pHPF2[ihpf2]); - } - } else { - result = ma_hpf2_reinit(&hpf2Config, &pHPF->pHPF2[ihpf2]); - } - - if (result != MA_SUCCESS) { - ma_uint32 jhpf1; - ma_uint32 jhpf2; - - for (jhpf1 = 0; jhpf1 < hpf1Count; jhpf1 += 1) { - ma_hpf1_uninit(&pHPF->pHPF1[jhpf1], NULL); /* No need for allocation callbacks here since we used a preallocated heap allocation. */ - } - - for (jhpf2 = 0; jhpf2 < ihpf2; jhpf2 += 1) { - ma_hpf2_uninit(&pHPF->pHPF2[jhpf2], NULL); /* No need for allocation callbacks here since we used a preallocated heap allocation. */ - } - - return result; - } - } - - pHPF->hpf1Count = hpf1Count; - pHPF->hpf2Count = hpf2Count; - pHPF->format = pConfig->format; - pHPF->channels = pConfig->channels; - pHPF->sampleRate = pConfig->sampleRate; - - return MA_SUCCESS; -} - -MA_API ma_result ma_hpf_get_heap_size(const ma_hpf_config* pConfig, size_t* pHeapSizeInBytes) -{ - ma_result result; - ma_hpf_heap_layout heapLayout; - - if (pHeapSizeInBytes == NULL) { - return MA_INVALID_ARGS; - } - - *pHeapSizeInBytes = 0; - - result = ma_hpf_get_heap_layout(pConfig, &heapLayout); - if (result != MA_SUCCESS) { - return result; - } - - *pHeapSizeInBytes = heapLayout.sizeInBytes; - - return result; -} - -MA_API ma_result ma_hpf_init_preallocated(const ma_hpf_config* pConfig, void* pHeap, ma_hpf* pLPF) -{ - if (pLPF == NULL) { - return MA_INVALID_ARGS; - } - - MA_ZERO_OBJECT(pLPF); - - return ma_hpf_reinit__internal(pConfig, pHeap, pLPF, /*isNew*/MA_TRUE); -} - -MA_API ma_result ma_hpf_init(const ma_hpf_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_hpf* pHPF) -{ - ma_result result; - size_t heapSizeInBytes; - void* pHeap; - - result = ma_hpf_get_heap_size(pConfig, &heapSizeInBytes); - if (result != MA_SUCCESS) { - return result; - } - - if (heapSizeInBytes > 0) { - pHeap = ma_malloc(heapSizeInBytes, pAllocationCallbacks); - if (pHeap == NULL) { - return MA_OUT_OF_MEMORY; - } - } else { - pHeap = NULL; - } - - result = ma_hpf_init_preallocated(pConfig, pHeap, pHPF); - if (result != MA_SUCCESS) { - ma_free(pHeap, pAllocationCallbacks); - return result; - } - - pHPF->_ownsHeap = MA_TRUE; - return MA_SUCCESS; -} - -MA_API void ma_hpf_uninit(ma_hpf* pHPF, const ma_allocation_callbacks* pAllocationCallbacks) -{ - ma_uint32 ihpf1; - ma_uint32 ihpf2; - - if (pHPF == NULL) { - return; - } - - for (ihpf1 = 0; ihpf1 < pHPF->hpf1Count; ihpf1 += 1) { - ma_hpf1_uninit(&pHPF->pHPF1[ihpf1], pAllocationCallbacks); - } - - for (ihpf2 = 0; ihpf2 < pHPF->hpf2Count; ihpf2 += 1) { - ma_hpf2_uninit(&pHPF->pHPF2[ihpf2], pAllocationCallbacks); - } - - if (pHPF->_ownsHeap) { - ma_free(pHPF->_pHeap, pAllocationCallbacks); - } -} - -MA_API ma_result ma_hpf_reinit(const ma_hpf_config* pConfig, ma_hpf* pHPF) -{ - return ma_hpf_reinit__internal(pConfig, NULL, pHPF, /*isNew*/MA_FALSE); -} - -MA_API ma_result ma_hpf_process_pcm_frames(ma_hpf* pHPF, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount) -{ - ma_result result; - ma_uint32 ihpf1; - ma_uint32 ihpf2; - - if (pHPF == NULL) { - return MA_INVALID_ARGS; - } - - /* Faster path for in-place. */ - if (pFramesOut == pFramesIn) { - for (ihpf1 = 0; ihpf1 < pHPF->hpf1Count; ihpf1 += 1) { - result = ma_hpf1_process_pcm_frames(&pHPF->pHPF1[ihpf1], pFramesOut, pFramesOut, frameCount); - if (result != MA_SUCCESS) { - return result; - } - } - - for (ihpf2 = 0; ihpf2 < pHPF->hpf2Count; ihpf2 += 1) { - result = ma_hpf2_process_pcm_frames(&pHPF->pHPF2[ihpf2], pFramesOut, pFramesOut, frameCount); - if (result != MA_SUCCESS) { - return result; - } - } - } - - /* Slightly slower path for copying. */ - if (pFramesOut != pFramesIn) { - ma_uint32 iFrame; - - /* */ if (pHPF->format == ma_format_f32) { - /* */ float* pFramesOutF32 = ( float*)pFramesOut; - const float* pFramesInF32 = (const float*)pFramesIn; - - for (iFrame = 0; iFrame < frameCount; iFrame += 1) { - MA_COPY_MEMORY(pFramesOutF32, pFramesInF32, ma_get_bytes_per_frame(pHPF->format, pHPF->channels)); - - for (ihpf1 = 0; ihpf1 < pHPF->hpf1Count; ihpf1 += 1) { - ma_hpf1_process_pcm_frame_f32(&pHPF->pHPF1[ihpf1], pFramesOutF32, pFramesOutF32); - } - - for (ihpf2 = 0; ihpf2 < pHPF->hpf2Count; ihpf2 += 1) { - ma_hpf2_process_pcm_frame_f32(&pHPF->pHPF2[ihpf2], pFramesOutF32, pFramesOutF32); - } - - pFramesOutF32 += pHPF->channels; - pFramesInF32 += pHPF->channels; - } - } else if (pHPF->format == ma_format_s16) { - /* */ ma_int16* pFramesOutS16 = ( ma_int16*)pFramesOut; - const ma_int16* pFramesInS16 = (const ma_int16*)pFramesIn; - - for (iFrame = 0; iFrame < frameCount; iFrame += 1) { - MA_COPY_MEMORY(pFramesOutS16, pFramesInS16, ma_get_bytes_per_frame(pHPF->format, pHPF->channels)); - - for (ihpf1 = 0; ihpf1 < pHPF->hpf1Count; ihpf1 += 1) { - ma_hpf1_process_pcm_frame_s16(&pHPF->pHPF1[ihpf1], pFramesOutS16, pFramesOutS16); - } - - for (ihpf2 = 0; ihpf2 < pHPF->hpf2Count; ihpf2 += 1) { - ma_hpf2_process_pcm_frame_s16(&pHPF->pHPF2[ihpf2], pFramesOutS16, pFramesOutS16); - } - - pFramesOutS16 += pHPF->channels; - pFramesInS16 += pHPF->channels; - } - } else { - MA_ASSERT(MA_FALSE); - return MA_INVALID_OPERATION; /* Should never hit this. */ - } - } - - return MA_SUCCESS; -} - -MA_API ma_uint32 ma_hpf_get_latency(const ma_hpf* pHPF) -{ - if (pHPF == NULL) { - return 0; - } - - return pHPF->hpf2Count*2 + pHPF->hpf1Count; -} - - -/************************************************************************************************************************************************************** - -Band-Pass Filtering - -**************************************************************************************************************************************************************/ -MA_API ma_bpf2_config ma_bpf2_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRate, double cutoffFrequency, double q) -{ - ma_bpf2_config config; - - MA_ZERO_OBJECT(&config); - config.format = format; - config.channels = channels; - config.sampleRate = sampleRate; - config.cutoffFrequency = cutoffFrequency; - config.q = q; - - /* Q cannot be 0 or else it'll result in a division by 0. In this case just default to 0.707107. */ - if (config.q == 0) { - config.q = 0.707107; - } - - return config; -} - - -static MA_INLINE ma_biquad_config ma_bpf2__get_biquad_config(const ma_bpf2_config* pConfig) -{ - ma_biquad_config bqConfig; - double q; - double w; - double s; - double c; - double a; - - MA_ASSERT(pConfig != NULL); - - q = pConfig->q; - w = 2 * MA_PI_D * pConfig->cutoffFrequency / pConfig->sampleRate; - s = ma_sind(w); - c = ma_cosd(w); - a = s / (2*q); - - bqConfig.b0 = q * a; - bqConfig.b1 = 0; - bqConfig.b2 = -q * a; - bqConfig.a0 = 1 + a; - bqConfig.a1 = -2 * c; - bqConfig.a2 = 1 - a; - - bqConfig.format = pConfig->format; - bqConfig.channels = pConfig->channels; - - return bqConfig; -} - -MA_API ma_result ma_bpf2_get_heap_size(const ma_bpf2_config* pConfig, size_t* pHeapSizeInBytes) -{ - ma_biquad_config bqConfig; - bqConfig = ma_bpf2__get_biquad_config(pConfig); - - return ma_biquad_get_heap_size(&bqConfig, pHeapSizeInBytes); -} - -MA_API ma_result ma_bpf2_init_preallocated(const ma_bpf2_config* pConfig, void* pHeap, ma_bpf2* pBPF) -{ - ma_result result; - ma_biquad_config bqConfig; - - if (pBPF == NULL) { - return MA_INVALID_ARGS; - } - - MA_ZERO_OBJECT(pBPF); - - if (pConfig == NULL) { - return MA_INVALID_ARGS; - } - - bqConfig = ma_bpf2__get_biquad_config(pConfig); - result = ma_biquad_init_preallocated(&bqConfig, pHeap, &pBPF->bq); - if (result != MA_SUCCESS) { - return result; - } - - return MA_SUCCESS; -} - -MA_API ma_result ma_bpf2_init(const ma_bpf2_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_bpf2* pBPF) -{ - ma_result result; - size_t heapSizeInBytes; - void* pHeap; - - result = ma_bpf2_get_heap_size(pConfig, &heapSizeInBytes); - if (result != MA_SUCCESS) { - return result; - } - - if (heapSizeInBytes > 0) { - pHeap = ma_malloc(heapSizeInBytes, pAllocationCallbacks); - if (pHeap == NULL) { - return MA_OUT_OF_MEMORY; - } - } else { - pHeap = NULL; - } - - result = ma_bpf2_init_preallocated(pConfig, pHeap, pBPF); - if (result != MA_SUCCESS) { - ma_free(pHeap, pAllocationCallbacks); - return result; - } - - pBPF->bq._ownsHeap = MA_TRUE; /* <-- This will cause the biquad to take ownership of the heap and free it when it's uninitialized. */ - return MA_SUCCESS; -} - -MA_API void ma_bpf2_uninit(ma_bpf2* pBPF, const ma_allocation_callbacks* pAllocationCallbacks) -{ - if (pBPF == NULL) { - return; - } - - ma_biquad_uninit(&pBPF->bq, pAllocationCallbacks); /* <-- This will free the heap allocation. */ -} - -MA_API ma_result ma_bpf2_reinit(const ma_bpf2_config* pConfig, ma_bpf2* pBPF) -{ - ma_result result; - ma_biquad_config bqConfig; - - if (pBPF == NULL || pConfig == NULL) { - return MA_INVALID_ARGS; - } - - bqConfig = ma_bpf2__get_biquad_config(pConfig); - result = ma_biquad_reinit(&bqConfig, &pBPF->bq); - if (result != MA_SUCCESS) { - return result; - } - - return MA_SUCCESS; -} - -static MA_INLINE void ma_bpf2_process_pcm_frame_s16(ma_bpf2* pBPF, ma_int16* pFrameOut, const ma_int16* pFrameIn) -{ - ma_biquad_process_pcm_frame_s16(&pBPF->bq, pFrameOut, pFrameIn); -} - -static MA_INLINE void ma_bpf2_process_pcm_frame_f32(ma_bpf2* pBPF, float* pFrameOut, const float* pFrameIn) -{ - ma_biquad_process_pcm_frame_f32(&pBPF->bq, pFrameOut, pFrameIn); -} - -MA_API ma_result ma_bpf2_process_pcm_frames(ma_bpf2* pBPF, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount) -{ - if (pBPF == NULL) { - return MA_INVALID_ARGS; - } - - return ma_biquad_process_pcm_frames(&pBPF->bq, pFramesOut, pFramesIn, frameCount); -} - -MA_API ma_uint32 ma_bpf2_get_latency(const ma_bpf2* pBPF) -{ - if (pBPF == NULL) { - return 0; - } - - return ma_biquad_get_latency(&pBPF->bq); -} - - -MA_API ma_bpf_config ma_bpf_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRate, double cutoffFrequency, ma_uint32 order) -{ - ma_bpf_config config; - - MA_ZERO_OBJECT(&config); - config.format = format; - config.channels = channels; - config.sampleRate = sampleRate; - config.cutoffFrequency = cutoffFrequency; - config.order = ma_min(order, MA_MAX_FILTER_ORDER); - - return config; -} - - -typedef struct -{ - size_t sizeInBytes; - size_t bpf2Offset; -} ma_bpf_heap_layout; - -static ma_result ma_bpf_get_heap_layout(const ma_bpf_config* pConfig, ma_bpf_heap_layout* pHeapLayout) -{ - ma_result result; - ma_uint32 bpf2Count; - ma_uint32 ibpf2; - - MA_ASSERT(pHeapLayout != NULL); - - MA_ZERO_OBJECT(pHeapLayout); - - if (pConfig == NULL) { - return MA_INVALID_ARGS; - } - - if (pConfig->order > MA_MAX_FILTER_ORDER) { - return MA_INVALID_ARGS; - } - - /* We must have an even number of order. */ - if ((pConfig->order & 0x1) != 0) { - return MA_INVALID_ARGS; - } - - bpf2Count = pConfig->order / 2; - - pHeapLayout->sizeInBytes = 0; - - /* BPF 2 */ - pHeapLayout->bpf2Offset = pHeapLayout->sizeInBytes; - for (ibpf2 = 0; ibpf2 < bpf2Count; ibpf2 += 1) { - size_t bpf2HeapSizeInBytes; - ma_bpf2_config bpf2Config = ma_bpf2_config_init(pConfig->format, pConfig->channels, pConfig->sampleRate, pConfig->cutoffFrequency, 0.707107); /* <-- The "q" parameter does not matter for the purpose of calculating the heap size. */ - - result = ma_bpf2_get_heap_size(&bpf2Config, &bpf2HeapSizeInBytes); - if (result != MA_SUCCESS) { - return result; - } - - pHeapLayout->sizeInBytes += sizeof(ma_bpf2) + bpf2HeapSizeInBytes; - } - - /* Make sure allocation size is aligned. */ - pHeapLayout->sizeInBytes = ma_align_64(pHeapLayout->sizeInBytes); - - return MA_SUCCESS; -} - -static ma_result ma_bpf_reinit__internal(const ma_bpf_config* pConfig, void* pHeap, ma_bpf* pBPF, ma_bool32 isNew) -{ - ma_result result; - ma_uint32 bpf2Count; - ma_uint32 ibpf2; - ma_bpf_heap_layout heapLayout; /* Only used if isNew is true. */ - - if (pBPF == NULL || pConfig == NULL) { - return MA_INVALID_ARGS; - } - - /* Only supporting f32 and s16. */ - if (pConfig->format != ma_format_f32 && pConfig->format != ma_format_s16) { - return MA_INVALID_ARGS; - } - - /* The format cannot be changed after initialization. */ - if (pBPF->format != ma_format_unknown && pBPF->format != pConfig->format) { - return MA_INVALID_OPERATION; - } - - /* The channel count cannot be changed after initialization. */ - if (pBPF->channels != 0 && pBPF->channels != pConfig->channels) { - return MA_INVALID_OPERATION; - } - - if (pConfig->order > MA_MAX_FILTER_ORDER) { - return MA_INVALID_ARGS; - } - - /* We must have an even number of order. */ - if ((pConfig->order & 0x1) != 0) { - return MA_INVALID_ARGS; - } - - bpf2Count = pConfig->order / 2; - - /* The filter order can't change between reinits. */ - if (!isNew) { - if (pBPF->bpf2Count != bpf2Count) { - return MA_INVALID_OPERATION; - } - } - - if (isNew) { - result = ma_bpf_get_heap_layout(pConfig, &heapLayout); - if (result != MA_SUCCESS) { - return result; - } - - pBPF->_pHeap = pHeap; - MA_ZERO_MEMORY(pHeap, heapLayout.sizeInBytes); - - pBPF->pBPF2 = (ma_bpf2*)ma_offset_ptr(pHeap, heapLayout.bpf2Offset); - } else { - MA_ZERO_OBJECT(&heapLayout); - } - - for (ibpf2 = 0; ibpf2 < bpf2Count; ibpf2 += 1) { - ma_bpf2_config bpf2Config; - double q; - - /* TODO: Calculate Q to make this a proper Butterworth filter. */ - q = 0.707107; - - bpf2Config = ma_bpf2_config_init(pConfig->format, pConfig->channels, pConfig->sampleRate, pConfig->cutoffFrequency, q); - - if (isNew) { - size_t bpf2HeapSizeInBytes; - - result = ma_bpf2_get_heap_size(&bpf2Config, &bpf2HeapSizeInBytes); - if (result == MA_SUCCESS) { - result = ma_bpf2_init_preallocated(&bpf2Config, ma_offset_ptr(pHeap, heapLayout.bpf2Offset + (sizeof(ma_bpf2) * bpf2Count) + (ibpf2 * bpf2HeapSizeInBytes)), &pBPF->pBPF2[ibpf2]); - } - } else { - result = ma_bpf2_reinit(&bpf2Config, &pBPF->pBPF2[ibpf2]); - } - - if (result != MA_SUCCESS) { - return result; - } - } - - pBPF->bpf2Count = bpf2Count; - pBPF->format = pConfig->format; - pBPF->channels = pConfig->channels; - - return MA_SUCCESS; -} - - -MA_API ma_result ma_bpf_get_heap_size(const ma_bpf_config* pConfig, size_t* pHeapSizeInBytes) -{ - ma_result result; - ma_bpf_heap_layout heapLayout; - - if (pHeapSizeInBytes == NULL) { - return MA_INVALID_ARGS; - } - - *pHeapSizeInBytes = 0; - - result = ma_bpf_get_heap_layout(pConfig, &heapLayout); - if (result != MA_SUCCESS) { - return result; - } - - *pHeapSizeInBytes = heapLayout.sizeInBytes; - - return MA_SUCCESS; -} - -MA_API ma_result ma_bpf_init_preallocated(const ma_bpf_config* pConfig, void* pHeap, ma_bpf* pBPF) -{ - if (pBPF == NULL) { - return MA_INVALID_ARGS; - } - - MA_ZERO_OBJECT(pBPF); - - return ma_bpf_reinit__internal(pConfig, pHeap, pBPF, /*isNew*/MA_TRUE); -} - -MA_API ma_result ma_bpf_init(const ma_bpf_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_bpf* pBPF) -{ - ma_result result; - size_t heapSizeInBytes; - void* pHeap; - - result = ma_bpf_get_heap_size(pConfig, &heapSizeInBytes); - if (result != MA_SUCCESS) { - return result; - } - - if (heapSizeInBytes > 0) { - pHeap = ma_malloc(heapSizeInBytes, pAllocationCallbacks); - if (pHeap == NULL) { - return MA_OUT_OF_MEMORY; - } - } else { - pHeap = NULL; - } - - result = ma_bpf_init_preallocated(pConfig, pHeap, pBPF); - if (result != MA_SUCCESS) { - ma_free(pHeap, pAllocationCallbacks); - return result; - } - - pBPF->_ownsHeap = MA_TRUE; - return MA_SUCCESS; -} - -MA_API void ma_bpf_uninit(ma_bpf* pBPF, const ma_allocation_callbacks* pAllocationCallbacks) -{ - ma_uint32 ibpf2; - - if (pBPF == NULL) { - return; - } - - for (ibpf2 = 0; ibpf2 < pBPF->bpf2Count; ibpf2 += 1) { - ma_bpf2_uninit(&pBPF->pBPF2[ibpf2], pAllocationCallbacks); - } - - if (pBPF->_ownsHeap) { - ma_free(pBPF->_pHeap, pAllocationCallbacks); - } -} - -MA_API ma_result ma_bpf_reinit(const ma_bpf_config* pConfig, ma_bpf* pBPF) -{ - return ma_bpf_reinit__internal(pConfig, NULL, pBPF, /*isNew*/MA_FALSE); -} - -MA_API ma_result ma_bpf_process_pcm_frames(ma_bpf* pBPF, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount) -{ - ma_result result; - ma_uint32 ibpf2; - - if (pBPF == NULL) { - return MA_INVALID_ARGS; - } - - /* Faster path for in-place. */ - if (pFramesOut == pFramesIn) { - for (ibpf2 = 0; ibpf2 < pBPF->bpf2Count; ibpf2 += 1) { - result = ma_bpf2_process_pcm_frames(&pBPF->pBPF2[ibpf2], pFramesOut, pFramesOut, frameCount); - if (result != MA_SUCCESS) { - return result; - } - } - } - - /* Slightly slower path for copying. */ - if (pFramesOut != pFramesIn) { - ma_uint32 iFrame; - - /* */ if (pBPF->format == ma_format_f32) { - /* */ float* pFramesOutF32 = ( float*)pFramesOut; - const float* pFramesInF32 = (const float*)pFramesIn; - - for (iFrame = 0; iFrame < frameCount; iFrame += 1) { - MA_COPY_MEMORY(pFramesOutF32, pFramesInF32, ma_get_bytes_per_frame(pBPF->format, pBPF->channels)); - - for (ibpf2 = 0; ibpf2 < pBPF->bpf2Count; ibpf2 += 1) { - ma_bpf2_process_pcm_frame_f32(&pBPF->pBPF2[ibpf2], pFramesOutF32, pFramesOutF32); - } - - pFramesOutF32 += pBPF->channels; - pFramesInF32 += pBPF->channels; - } - } else if (pBPF->format == ma_format_s16) { - /* */ ma_int16* pFramesOutS16 = ( ma_int16*)pFramesOut; - const ma_int16* pFramesInS16 = (const ma_int16*)pFramesIn; - - for (iFrame = 0; iFrame < frameCount; iFrame += 1) { - MA_COPY_MEMORY(pFramesOutS16, pFramesInS16, ma_get_bytes_per_frame(pBPF->format, pBPF->channels)); - - for (ibpf2 = 0; ibpf2 < pBPF->bpf2Count; ibpf2 += 1) { - ma_bpf2_process_pcm_frame_s16(&pBPF->pBPF2[ibpf2], pFramesOutS16, pFramesOutS16); - } - - pFramesOutS16 += pBPF->channels; - pFramesInS16 += pBPF->channels; - } - } else { - MA_ASSERT(MA_FALSE); - return MA_INVALID_OPERATION; /* Should never hit this. */ - } - } - - return MA_SUCCESS; -} - -MA_API ma_uint32 ma_bpf_get_latency(const ma_bpf* pBPF) -{ - if (pBPF == NULL) { - return 0; - } - - return pBPF->bpf2Count*2; -} - - -/************************************************************************************************************************************************************** - -Notching Filter - -**************************************************************************************************************************************************************/ -MA_API ma_notch2_config ma_notch2_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRate, double q, double frequency) -{ - ma_notch2_config config; - - MA_ZERO_OBJECT(&config); - config.format = format; - config.channels = channels; - config.sampleRate = sampleRate; - config.q = q; - config.frequency = frequency; - - if (config.q == 0) { - config.q = 0.707107; - } - - return config; -} - - -static MA_INLINE ma_biquad_config ma_notch2__get_biquad_config(const ma_notch2_config* pConfig) -{ - ma_biquad_config bqConfig; - double q; - double w; - double s; - double c; - double a; - - MA_ASSERT(pConfig != NULL); - - q = pConfig->q; - w = 2 * MA_PI_D * pConfig->frequency / pConfig->sampleRate; - s = ma_sind(w); - c = ma_cosd(w); - a = s / (2*q); - - bqConfig.b0 = 1; - bqConfig.b1 = -2 * c; - bqConfig.b2 = 1; - bqConfig.a0 = 1 + a; - bqConfig.a1 = -2 * c; - bqConfig.a2 = 1 - a; - - bqConfig.format = pConfig->format; - bqConfig.channels = pConfig->channels; - - return bqConfig; -} - -MA_API ma_result ma_notch2_get_heap_size(const ma_notch2_config* pConfig, size_t* pHeapSizeInBytes) -{ - ma_biquad_config bqConfig; - bqConfig = ma_notch2__get_biquad_config(pConfig); - - return ma_biquad_get_heap_size(&bqConfig, pHeapSizeInBytes); -} - -MA_API ma_result ma_notch2_init_preallocated(const ma_notch2_config* pConfig, void* pHeap, ma_notch2* pFilter) -{ - ma_result result; - ma_biquad_config bqConfig; - - if (pFilter == NULL) { - return MA_INVALID_ARGS; - } - - MA_ZERO_OBJECT(pFilter); - - if (pConfig == NULL) { - return MA_INVALID_ARGS; - } - - bqConfig = ma_notch2__get_biquad_config(pConfig); - result = ma_biquad_init_preallocated(&bqConfig, pHeap, &pFilter->bq); - if (result != MA_SUCCESS) { - return result; - } - - return MA_SUCCESS; -} - -MA_API ma_result ma_notch2_init(const ma_notch2_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_notch2* pFilter) -{ - ma_result result; - size_t heapSizeInBytes; - void* pHeap; - - result = ma_notch2_get_heap_size(pConfig, &heapSizeInBytes); - if (result != MA_SUCCESS) { - return result; - } - - if (heapSizeInBytes > 0) { - pHeap = ma_malloc(heapSizeInBytes, pAllocationCallbacks); - if (pHeap == NULL) { - return MA_OUT_OF_MEMORY; - } - } else { - pHeap = NULL; - } - - result = ma_notch2_init_preallocated(pConfig, pHeap, pFilter); - if (result != MA_SUCCESS) { - ma_free(pHeap, pAllocationCallbacks); - return result; - } - - pFilter->bq._ownsHeap = MA_TRUE; /* <-- This will cause the biquad to take ownership of the heap and free it when it's uninitialized. */ - return MA_SUCCESS; -} - -MA_API void ma_notch2_uninit(ma_notch2* pFilter, const ma_allocation_callbacks* pAllocationCallbacks) -{ - if (pFilter == NULL) { - return; - } - - ma_biquad_uninit(&pFilter->bq, pAllocationCallbacks); /* <-- This will free the heap allocation. */ -} - -MA_API ma_result ma_notch2_reinit(const ma_notch2_config* pConfig, ma_notch2* pFilter) -{ - ma_result result; - ma_biquad_config bqConfig; - - if (pFilter == NULL || pConfig == NULL) { - return MA_INVALID_ARGS; - } - - bqConfig = ma_notch2__get_biquad_config(pConfig); - result = ma_biquad_reinit(&bqConfig, &pFilter->bq); - if (result != MA_SUCCESS) { - return result; - } - - return MA_SUCCESS; -} - -static MA_INLINE void ma_notch2_process_pcm_frame_s16(ma_notch2* pFilter, ma_int16* pFrameOut, const ma_int16* pFrameIn) -{ - ma_biquad_process_pcm_frame_s16(&pFilter->bq, pFrameOut, pFrameIn); -} - -static MA_INLINE void ma_notch2_process_pcm_frame_f32(ma_notch2* pFilter, float* pFrameOut, const float* pFrameIn) -{ - ma_biquad_process_pcm_frame_f32(&pFilter->bq, pFrameOut, pFrameIn); -} - -MA_API ma_result ma_notch2_process_pcm_frames(ma_notch2* pFilter, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount) -{ - if (pFilter == NULL) { - return MA_INVALID_ARGS; - } - - return ma_biquad_process_pcm_frames(&pFilter->bq, pFramesOut, pFramesIn, frameCount); -} - -MA_API ma_uint32 ma_notch2_get_latency(const ma_notch2* pFilter) -{ - if (pFilter == NULL) { - return 0; - } - - return ma_biquad_get_latency(&pFilter->bq); -} - - - -/************************************************************************************************************************************************************** - -Peaking EQ Filter - -**************************************************************************************************************************************************************/ -MA_API ma_peak2_config ma_peak2_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRate, double gainDB, double q, double frequency) -{ - ma_peak2_config config; - - MA_ZERO_OBJECT(&config); - config.format = format; - config.channels = channels; - config.sampleRate = sampleRate; - config.gainDB = gainDB; - config.q = q; - config.frequency = frequency; - - if (config.q == 0) { - config.q = 0.707107; - } - - return config; -} - - -static MA_INLINE ma_biquad_config ma_peak2__get_biquad_config(const ma_peak2_config* pConfig) -{ - ma_biquad_config bqConfig; - double q; - double w; - double s; - double c; - double a; - double A; - - MA_ASSERT(pConfig != NULL); - - q = pConfig->q; - w = 2 * MA_PI_D * pConfig->frequency / pConfig->sampleRate; - s = ma_sind(w); - c = ma_cosd(w); - a = s / (2*q); - A = ma_powd(10, (pConfig->gainDB / 40)); - - bqConfig.b0 = 1 + (a * A); - bqConfig.b1 = -2 * c; - bqConfig.b2 = 1 - (a * A); - bqConfig.a0 = 1 + (a / A); - bqConfig.a1 = -2 * c; - bqConfig.a2 = 1 - (a / A); - - bqConfig.format = pConfig->format; - bqConfig.channels = pConfig->channels; - - return bqConfig; -} - -MA_API ma_result ma_peak2_get_heap_size(const ma_peak2_config* pConfig, size_t* pHeapSizeInBytes) -{ - ma_biquad_config bqConfig; - bqConfig = ma_peak2__get_biquad_config(pConfig); - - return ma_biquad_get_heap_size(&bqConfig, pHeapSizeInBytes); -} - -MA_API ma_result ma_peak2_init_preallocated(const ma_peak2_config* pConfig, void* pHeap, ma_peak2* pFilter) -{ - ma_result result; - ma_biquad_config bqConfig; - - if (pFilter == NULL) { - return MA_INVALID_ARGS; - } - - MA_ZERO_OBJECT(pFilter); - - if (pConfig == NULL) { - return MA_INVALID_ARGS; - } - - bqConfig = ma_peak2__get_biquad_config(pConfig); - result = ma_biquad_init_preallocated(&bqConfig, pHeap, &pFilter->bq); - if (result != MA_SUCCESS) { - return result; - } - - return MA_SUCCESS; -} - -MA_API ma_result ma_peak2_init(const ma_peak2_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_peak2* pFilter) -{ - ma_result result; - size_t heapSizeInBytes; - void* pHeap; - - result = ma_peak2_get_heap_size(pConfig, &heapSizeInBytes); - if (result != MA_SUCCESS) { - return result; - } - - if (heapSizeInBytes > 0) { - pHeap = ma_malloc(heapSizeInBytes, pAllocationCallbacks); - if (pHeap == NULL) { - return MA_OUT_OF_MEMORY; - } - } else { - pHeap = NULL; - } - - result = ma_peak2_init_preallocated(pConfig, pHeap, pFilter); - if (result != MA_SUCCESS) { - ma_free(pHeap, pAllocationCallbacks); - return result; - } - - pFilter->bq._ownsHeap = MA_TRUE; /* <-- This will cause the biquad to take ownership of the heap and free it when it's uninitialized. */ - return MA_SUCCESS; -} - -MA_API void ma_peak2_uninit(ma_peak2* pFilter, const ma_allocation_callbacks* pAllocationCallbacks) -{ - if (pFilter == NULL) { - return; - } - - ma_biquad_uninit(&pFilter->bq, pAllocationCallbacks); /* <-- This will free the heap allocation. */ -} - -MA_API ma_result ma_peak2_reinit(const ma_peak2_config* pConfig, ma_peak2* pFilter) -{ - ma_result result; - ma_biquad_config bqConfig; - - if (pFilter == NULL || pConfig == NULL) { - return MA_INVALID_ARGS; - } - - bqConfig = ma_peak2__get_biquad_config(pConfig); - result = ma_biquad_reinit(&bqConfig, &pFilter->bq); - if (result != MA_SUCCESS) { - return result; - } - - return MA_SUCCESS; -} - -static MA_INLINE void ma_peak2_process_pcm_frame_s16(ma_peak2* pFilter, ma_int16* pFrameOut, const ma_int16* pFrameIn) -{ - ma_biquad_process_pcm_frame_s16(&pFilter->bq, pFrameOut, pFrameIn); -} - -static MA_INLINE void ma_peak2_process_pcm_frame_f32(ma_peak2* pFilter, float* pFrameOut, const float* pFrameIn) -{ - ma_biquad_process_pcm_frame_f32(&pFilter->bq, pFrameOut, pFrameIn); -} - -MA_API ma_result ma_peak2_process_pcm_frames(ma_peak2* pFilter, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount) -{ - if (pFilter == NULL) { - return MA_INVALID_ARGS; - } - - return ma_biquad_process_pcm_frames(&pFilter->bq, pFramesOut, pFramesIn, frameCount); -} - -MA_API ma_uint32 ma_peak2_get_latency(const ma_peak2* pFilter) -{ - if (pFilter == NULL) { - return 0; - } - - return ma_biquad_get_latency(&pFilter->bq); -} - - -/************************************************************************************************************************************************************** - -Low Shelf Filter - -**************************************************************************************************************************************************************/ -MA_API ma_loshelf2_config ma_loshelf2_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRate, double gainDB, double shelfSlope, double frequency) -{ - ma_loshelf2_config config; - - MA_ZERO_OBJECT(&config); - config.format = format; - config.channels = channels; - config.sampleRate = sampleRate; - config.gainDB = gainDB; - config.shelfSlope = shelfSlope; - config.frequency = frequency; - - return config; -} - - -static MA_INLINE ma_biquad_config ma_loshelf2__get_biquad_config(const ma_loshelf2_config* pConfig) -{ - ma_biquad_config bqConfig; - double w; - double s; - double c; - double A; - double S; - double a; - double sqrtA; - - MA_ASSERT(pConfig != NULL); - - w = 2 * MA_PI_D * pConfig->frequency / pConfig->sampleRate; - s = ma_sind(w); - c = ma_cosd(w); - A = ma_powd(10, (pConfig->gainDB / 40)); - S = pConfig->shelfSlope; - a = s/2 * ma_sqrtd((A + 1/A) * (1/S - 1) + 2); - sqrtA = 2*ma_sqrtd(A)*a; - - bqConfig.b0 = A * ((A + 1) - (A - 1)*c + sqrtA); - bqConfig.b1 = 2 * A * ((A - 1) - (A + 1)*c); - bqConfig.b2 = A * ((A + 1) - (A - 1)*c - sqrtA); - bqConfig.a0 = (A + 1) + (A - 1)*c + sqrtA; - bqConfig.a1 = -2 * ((A - 1) + (A + 1)*c); - bqConfig.a2 = (A + 1) + (A - 1)*c - sqrtA; - - bqConfig.format = pConfig->format; - bqConfig.channels = pConfig->channels; - - return bqConfig; -} - -MA_API ma_result ma_loshelf2_get_heap_size(const ma_loshelf2_config* pConfig, size_t* pHeapSizeInBytes) -{ - ma_biquad_config bqConfig; - bqConfig = ma_loshelf2__get_biquad_config(pConfig); - - return ma_biquad_get_heap_size(&bqConfig, pHeapSizeInBytes); -} - -MA_API ma_result ma_loshelf2_init_preallocated(const ma_loshelf2_config* pConfig, void* pHeap, ma_loshelf2* pFilter) -{ - ma_result result; - ma_biquad_config bqConfig; - - if (pFilter == NULL) { - return MA_INVALID_ARGS; - } - - MA_ZERO_OBJECT(pFilter); - - if (pConfig == NULL) { - return MA_INVALID_ARGS; - } - - bqConfig = ma_loshelf2__get_biquad_config(pConfig); - result = ma_biquad_init_preallocated(&bqConfig, pHeap, &pFilter->bq); - if (result != MA_SUCCESS) { - return result; - } - - return MA_SUCCESS; -} - -MA_API ma_result ma_loshelf2_init(const ma_loshelf2_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_loshelf2* pFilter) -{ - ma_result result; - size_t heapSizeInBytes; - void* pHeap; - - result = ma_loshelf2_get_heap_size(pConfig, &heapSizeInBytes); - if (result != MA_SUCCESS) { - return result; - } - - if (heapSizeInBytes > 0) { - pHeap = ma_malloc(heapSizeInBytes, pAllocationCallbacks); - if (pHeap == NULL) { - return MA_OUT_OF_MEMORY; - } - } else { - pHeap = NULL; - } - - result = ma_loshelf2_init_preallocated(pConfig, pHeap, pFilter); - if (result != MA_SUCCESS) { - ma_free(pHeap, pAllocationCallbacks); - return result; - } - - pFilter->bq._ownsHeap = MA_TRUE; /* <-- This will cause the biquad to take ownership of the heap and free it when it's uninitialized. */ - return MA_SUCCESS; -} - -MA_API void ma_loshelf2_uninit(ma_loshelf2* pFilter, const ma_allocation_callbacks* pAllocationCallbacks) -{ - if (pFilter == NULL) { - return; - } - - ma_biquad_uninit(&pFilter->bq, pAllocationCallbacks); /* <-- This will free the heap allocation. */ -} - -MA_API ma_result ma_loshelf2_reinit(const ma_loshelf2_config* pConfig, ma_loshelf2* pFilter) -{ - ma_result result; - ma_biquad_config bqConfig; - - if (pFilter == NULL || pConfig == NULL) { - return MA_INVALID_ARGS; - } - - bqConfig = ma_loshelf2__get_biquad_config(pConfig); - result = ma_biquad_reinit(&bqConfig, &pFilter->bq); - if (result != MA_SUCCESS) { - return result; - } - - return MA_SUCCESS; -} - -static MA_INLINE void ma_loshelf2_process_pcm_frame_s16(ma_loshelf2* pFilter, ma_int16* pFrameOut, const ma_int16* pFrameIn) -{ - ma_biquad_process_pcm_frame_s16(&pFilter->bq, pFrameOut, pFrameIn); -} - -static MA_INLINE void ma_loshelf2_process_pcm_frame_f32(ma_loshelf2* pFilter, float* pFrameOut, const float* pFrameIn) -{ - ma_biquad_process_pcm_frame_f32(&pFilter->bq, pFrameOut, pFrameIn); -} - -MA_API ma_result ma_loshelf2_process_pcm_frames(ma_loshelf2* pFilter, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount) -{ - if (pFilter == NULL) { - return MA_INVALID_ARGS; - } - - return ma_biquad_process_pcm_frames(&pFilter->bq, pFramesOut, pFramesIn, frameCount); -} - -MA_API ma_uint32 ma_loshelf2_get_latency(const ma_loshelf2* pFilter) -{ - if (pFilter == NULL) { - return 0; - } - - return ma_biquad_get_latency(&pFilter->bq); -} - - -/************************************************************************************************************************************************************** - -High Shelf Filter - -**************************************************************************************************************************************************************/ -MA_API ma_hishelf2_config ma_hishelf2_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRate, double gainDB, double shelfSlope, double frequency) -{ - ma_hishelf2_config config; - - MA_ZERO_OBJECT(&config); - config.format = format; - config.channels = channels; - config.sampleRate = sampleRate; - config.gainDB = gainDB; - config.shelfSlope = shelfSlope; - config.frequency = frequency; - - return config; -} - - -static MA_INLINE ma_biquad_config ma_hishelf2__get_biquad_config(const ma_hishelf2_config* pConfig) -{ - ma_biquad_config bqConfig; - double w; - double s; - double c; - double A; - double S; - double a; - double sqrtA; - - MA_ASSERT(pConfig != NULL); - - w = 2 * MA_PI_D * pConfig->frequency / pConfig->sampleRate; - s = ma_sind(w); - c = ma_cosd(w); - A = ma_powd(10, (pConfig->gainDB / 40)); - S = pConfig->shelfSlope; - a = s/2 * ma_sqrtd((A + 1/A) * (1/S - 1) + 2); - sqrtA = 2*ma_sqrtd(A)*a; - - bqConfig.b0 = A * ((A + 1) + (A - 1)*c + sqrtA); - bqConfig.b1 = -2 * A * ((A - 1) + (A + 1)*c); - bqConfig.b2 = A * ((A + 1) + (A - 1)*c - sqrtA); - bqConfig.a0 = (A + 1) - (A - 1)*c + sqrtA; - bqConfig.a1 = 2 * ((A - 1) - (A + 1)*c); - bqConfig.a2 = (A + 1) - (A - 1)*c - sqrtA; - - bqConfig.format = pConfig->format; - bqConfig.channels = pConfig->channels; - - return bqConfig; -} - -MA_API ma_result ma_hishelf2_get_heap_size(const ma_hishelf2_config* pConfig, size_t* pHeapSizeInBytes) -{ - ma_biquad_config bqConfig; - bqConfig = ma_hishelf2__get_biquad_config(pConfig); - - return ma_biquad_get_heap_size(&bqConfig, pHeapSizeInBytes); -} - -MA_API ma_result ma_hishelf2_init_preallocated(const ma_hishelf2_config* pConfig, void* pHeap, ma_hishelf2* pFilter) -{ - ma_result result; - ma_biquad_config bqConfig; - - if (pFilter == NULL) { - return MA_INVALID_ARGS; - } - - MA_ZERO_OBJECT(pFilter); - - if (pConfig == NULL) { - return MA_INVALID_ARGS; - } - - bqConfig = ma_hishelf2__get_biquad_config(pConfig); - result = ma_biquad_init_preallocated(&bqConfig, pHeap, &pFilter->bq); - if (result != MA_SUCCESS) { - return result; - } - - return MA_SUCCESS; -} - -MA_API ma_result ma_hishelf2_init(const ma_hishelf2_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_hishelf2* pFilter) -{ - ma_result result; - size_t heapSizeInBytes; - void* pHeap; - - result = ma_hishelf2_get_heap_size(pConfig, &heapSizeInBytes); - if (result != MA_SUCCESS) { - return result; - } - - if (heapSizeInBytes > 0) { - pHeap = ma_malloc(heapSizeInBytes, pAllocationCallbacks); - if (pHeap == NULL) { - return MA_OUT_OF_MEMORY; - } - } else { - pHeap = NULL; - } - - result = ma_hishelf2_init_preallocated(pConfig, pHeap, pFilter); - if (result != MA_SUCCESS) { - ma_free(pHeap, pAllocationCallbacks); - return result; - } - - pFilter->bq._ownsHeap = MA_TRUE; /* <-- This will cause the biquad to take ownership of the heap and free it when it's uninitialized. */ - return MA_SUCCESS; -} - -MA_API void ma_hishelf2_uninit(ma_hishelf2* pFilter, const ma_allocation_callbacks* pAllocationCallbacks) -{ - if (pFilter == NULL) { - return; - } - - ma_biquad_uninit(&pFilter->bq, pAllocationCallbacks); /* <-- This will free the heap allocation. */ -} - -MA_API ma_result ma_hishelf2_reinit(const ma_hishelf2_config* pConfig, ma_hishelf2* pFilter) -{ - ma_result result; - ma_biquad_config bqConfig; - - if (pFilter == NULL || pConfig == NULL) { - return MA_INVALID_ARGS; - } - - bqConfig = ma_hishelf2__get_biquad_config(pConfig); - result = ma_biquad_reinit(&bqConfig, &pFilter->bq); - if (result != MA_SUCCESS) { - return result; - } - - return MA_SUCCESS; -} - -static MA_INLINE void ma_hishelf2_process_pcm_frame_s16(ma_hishelf2* pFilter, ma_int16* pFrameOut, const ma_int16* pFrameIn) -{ - ma_biquad_process_pcm_frame_s16(&pFilter->bq, pFrameOut, pFrameIn); -} - -static MA_INLINE void ma_hishelf2_process_pcm_frame_f32(ma_hishelf2* pFilter, float* pFrameOut, const float* pFrameIn) -{ - ma_biquad_process_pcm_frame_f32(&pFilter->bq, pFrameOut, pFrameIn); -} - -MA_API ma_result ma_hishelf2_process_pcm_frames(ma_hishelf2* pFilter, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount) -{ - if (pFilter == NULL) { - return MA_INVALID_ARGS; - } - - return ma_biquad_process_pcm_frames(&pFilter->bq, pFramesOut, pFramesIn, frameCount); -} - -MA_API ma_uint32 ma_hishelf2_get_latency(const ma_hishelf2* pFilter) -{ - if (pFilter == NULL) { - return 0; - } - - return ma_biquad_get_latency(&pFilter->bq); -} - - - -/* -Delay -*/ -MA_API ma_delay_config ma_delay_config_init(ma_uint32 channels, ma_uint32 sampleRate, ma_uint32 delayInFrames, float decay) -{ - ma_delay_config config; - - MA_ZERO_OBJECT(&config); - config.channels = channels; - config.sampleRate = sampleRate; - config.delayInFrames = delayInFrames; - config.delayStart = (decay == 0) ? MA_TRUE : MA_FALSE; /* Delay the start if it looks like we're not configuring an echo. */ - config.wet = 1; - config.dry = 1; - config.decay = decay; - - return config; -} - - -MA_API ma_result ma_delay_init(const ma_delay_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_delay* pDelay) -{ - if (pDelay == NULL) { - return MA_INVALID_ARGS; - } - - MA_ZERO_OBJECT(pDelay); - - if (pConfig == NULL) { - return MA_INVALID_ARGS; - } - - if (pConfig->decay < 0 || pConfig->decay > 1) { - return MA_INVALID_ARGS; - } - - pDelay->config = *pConfig; - pDelay->bufferSizeInFrames = pConfig->delayInFrames; - pDelay->cursor = 0; - - pDelay->pBuffer = (float*)ma_malloc((size_t)(pDelay->bufferSizeInFrames * ma_get_bytes_per_frame(ma_format_f32, pConfig->channels)), pAllocationCallbacks); - if (pDelay->pBuffer == NULL) { - return MA_OUT_OF_MEMORY; - } - - ma_silence_pcm_frames(pDelay->pBuffer, pDelay->bufferSizeInFrames, ma_format_f32, pConfig->channels); - - return MA_SUCCESS; -} - -MA_API void ma_delay_uninit(ma_delay* pDelay, const ma_allocation_callbacks* pAllocationCallbacks) -{ - if (pDelay == NULL) { - return; - } - - ma_free(pDelay->pBuffer, pAllocationCallbacks); -} - -MA_API ma_result ma_delay_process_pcm_frames(ma_delay* pDelay, void* pFramesOut, const void* pFramesIn, ma_uint32 frameCount) -{ - ma_uint32 iFrame; - ma_uint32 iChannel; - float* pFramesOutF32 = (float*)pFramesOut; - const float* pFramesInF32 = (const float*)pFramesIn; - - if (pDelay == NULL || pFramesOut == NULL || pFramesIn == NULL) { - return MA_INVALID_ARGS; - } - - for (iFrame = 0; iFrame < frameCount; iFrame += 1) { - for (iChannel = 0; iChannel < pDelay->config.channels; iChannel += 1) { - ma_uint32 iBuffer = (pDelay->cursor * pDelay->config.channels) + iChannel; - - if (pDelay->config.delayStart) { - /* Delayed start. */ - - /* Read */ - pFramesOutF32[iChannel] = pDelay->pBuffer[iBuffer] * pDelay->config.wet; - - /* Feedback */ - pDelay->pBuffer[iBuffer] = (pDelay->pBuffer[iBuffer] * pDelay->config.decay) + (pFramesInF32[iChannel] * pDelay->config.dry); - } else { - /* Immediate start */ - - /* Feedback */ - pDelay->pBuffer[iBuffer] = (pDelay->pBuffer[iBuffer] * pDelay->config.decay) + (pFramesInF32[iChannel] * pDelay->config.dry); - - /* Read */ - pFramesOutF32[iChannel] = pDelay->pBuffer[iBuffer] * pDelay->config.wet; - } - } - - pDelay->cursor = (pDelay->cursor + 1) % pDelay->bufferSizeInFrames; - - pFramesOutF32 += pDelay->config.channels; - pFramesInF32 += pDelay->config.channels; - } - - return MA_SUCCESS; -} - -MA_API void ma_delay_set_wet(ma_delay* pDelay, float value) -{ - if (pDelay == NULL) { - return; - } - - pDelay->config.wet = value; -} - -MA_API float ma_delay_get_wet(const ma_delay* pDelay) -{ - if (pDelay == NULL) { - return 0; - } - - return pDelay->config.wet; -} - -MA_API void ma_delay_set_dry(ma_delay* pDelay, float value) -{ - if (pDelay == NULL) { - return; - } - - pDelay->config.dry = value; -} - -MA_API float ma_delay_get_dry(const ma_delay* pDelay) -{ - if (pDelay == NULL) { - return 0; - } - - return pDelay->config.dry; -} - -MA_API void ma_delay_set_decay(ma_delay* pDelay, float value) -{ - if (pDelay == NULL) { - return; - } - - pDelay->config.decay = value; -} - -MA_API float ma_delay_get_decay(const ma_delay* pDelay) -{ - if (pDelay == NULL) { - return 0; - } - - return pDelay->config.decay; -} - - -MA_API ma_gainer_config ma_gainer_config_init(ma_uint32 channels, ma_uint32 smoothTimeInFrames) -{ - ma_gainer_config config; - - MA_ZERO_OBJECT(&config); - config.channels = channels; - config.smoothTimeInFrames = smoothTimeInFrames; - - return config; -} - - -typedef struct -{ - size_t sizeInBytes; - size_t oldGainsOffset; - size_t newGainsOffset; -} ma_gainer_heap_layout; - -static ma_result ma_gainer_get_heap_layout(const ma_gainer_config* pConfig, ma_gainer_heap_layout* pHeapLayout) -{ - MA_ASSERT(pHeapLayout != NULL); - - MA_ZERO_OBJECT(pHeapLayout); - - if (pConfig == NULL) { - return MA_INVALID_ARGS; - } - - if (pConfig->channels == 0) { - return MA_INVALID_ARGS; - } - - pHeapLayout->sizeInBytes = 0; - - /* Old gains. */ - pHeapLayout->oldGainsOffset = pHeapLayout->sizeInBytes; - pHeapLayout->sizeInBytes += sizeof(float) * pConfig->channels; - - /* New gains. */ - pHeapLayout->newGainsOffset = pHeapLayout->sizeInBytes; - pHeapLayout->sizeInBytes += sizeof(float) * pConfig->channels; - - /* Alignment. */ - pHeapLayout->sizeInBytes = ma_align_64(pHeapLayout->sizeInBytes); - - return MA_SUCCESS; -} - - -MA_API ma_result ma_gainer_get_heap_size(const ma_gainer_config* pConfig, size_t* pHeapSizeInBytes) -{ - ma_result result; - ma_gainer_heap_layout heapLayout; - - if (pHeapSizeInBytes == NULL) { - return MA_INVALID_ARGS; - } - - *pHeapSizeInBytes = 0; - - result = ma_gainer_get_heap_layout(pConfig, &heapLayout); - if (result != MA_SUCCESS) { - return MA_INVALID_ARGS; - } - - *pHeapSizeInBytes = heapLayout.sizeInBytes; - - return MA_SUCCESS; -} - - -MA_API ma_result ma_gainer_init_preallocated(const ma_gainer_config* pConfig, void* pHeap, ma_gainer* pGainer) -{ - ma_result result; - ma_gainer_heap_layout heapLayout; - ma_uint32 iChannel; - - if (pGainer == NULL) { - return MA_INVALID_ARGS; - } - - MA_ZERO_OBJECT(pGainer); - - if (pConfig == NULL || pHeap == NULL) { - return MA_INVALID_ARGS; - } - - result = ma_gainer_get_heap_layout(pConfig, &heapLayout); - if (result != MA_SUCCESS) { - return result; - } - - pGainer->_pHeap = pHeap; - MA_ZERO_MEMORY(pHeap, heapLayout.sizeInBytes); - - pGainer->pOldGains = (float*)ma_offset_ptr(pHeap, heapLayout.oldGainsOffset); - pGainer->pNewGains = (float*)ma_offset_ptr(pHeap, heapLayout.newGainsOffset); - pGainer->masterVolume = 1; - - pGainer->config = *pConfig; - pGainer->t = (ma_uint32)-1; /* No interpolation by default. */ - - for (iChannel = 0; iChannel < pConfig->channels; iChannel += 1) { - pGainer->pOldGains[iChannel] = 1; - pGainer->pNewGains[iChannel] = 1; - } - - return MA_SUCCESS; -} - -MA_API ma_result ma_gainer_init(const ma_gainer_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_gainer* pGainer) -{ - ma_result result; - size_t heapSizeInBytes; - void* pHeap; - - result = ma_gainer_get_heap_size(pConfig, &heapSizeInBytes); - if (result != MA_SUCCESS) { - return result; /* Failed to retrieve the size of the heap allocation. */ - } - - if (heapSizeInBytes > 0) { - pHeap = ma_malloc(heapSizeInBytes, pAllocationCallbacks); - if (pHeap == NULL) { - return MA_OUT_OF_MEMORY; - } - } else { - pHeap = NULL; - } - - result = ma_gainer_init_preallocated(pConfig, pHeap, pGainer); - if (result != MA_SUCCESS) { - ma_free(pHeap, pAllocationCallbacks); - return result; - } - - pGainer->_ownsHeap = MA_TRUE; - return MA_SUCCESS; -} - -MA_API void ma_gainer_uninit(ma_gainer* pGainer, const ma_allocation_callbacks* pAllocationCallbacks) -{ - if (pGainer == NULL) { - return; - } - - if (pGainer->_ownsHeap) { - ma_free(pGainer->_pHeap, pAllocationCallbacks); - } -} - -static float ma_gainer_calculate_current_gain(const ma_gainer* pGainer, ma_uint32 channel) -{ - float a = (float)pGainer->t / pGainer->config.smoothTimeInFrames; - return ma_mix_f32_fast(pGainer->pOldGains[channel], pGainer->pNewGains[channel], a); -} - -static /*__attribute__((noinline))*/ ma_result ma_gainer_process_pcm_frames_internal(ma_gainer * pGainer, void* MA_RESTRICT pFramesOut, const void* MA_RESTRICT pFramesIn, ma_uint64 frameCount) -{ - ma_uint64 iFrame; - ma_uint32 iChannel; - ma_uint64 interpolatedFrameCount; - - MA_ASSERT(pGainer != NULL); - - /* - We don't necessarily need to apply a linear interpolation for the entire frameCount frames. When - linear interpolation is not needed we can do a simple volume adjustment which will be more - efficient than a lerp with an alpha value of 1. - - To do this, all we need to do is determine how many frames need to have a lerp applied. Then we - just process that number of frames with linear interpolation. After that we run on an optimized - path which just applies the new gains without a lerp. - */ - if (pGainer->t >= pGainer->config.smoothTimeInFrames) { - interpolatedFrameCount = 0; - } else { - interpolatedFrameCount = pGainer->t - pGainer->config.smoothTimeInFrames; - if (interpolatedFrameCount > frameCount) { - interpolatedFrameCount = frameCount; - } - } - - /* - Start off with our interpolated frames. When we do this, we'll adjust frameCount and our pointers - so that the fast path can work naturally without consideration of the interpolated path. - */ - if (interpolatedFrameCount > 0) { - /* We can allow the input and output buffers to be null in which case we'll just update the internal timer. */ - if (pFramesOut != NULL && pFramesIn != NULL) { - /* - All we're really doing here is moving the old gains towards the new gains. We don't want to - be modifying the gains inside the ma_gainer object because that will break things. Instead - we can make a copy here on the stack. For extreme channel counts we can fall back to a slower - implementation which just uses a standard lerp. - */ - float* pFramesOutF32 = (float*)pFramesOut; - const float* pFramesInF32 = (const float*)pFramesIn; - float a = (float)pGainer->t / pGainer->config.smoothTimeInFrames; - float d = 1.0f / pGainer->config.smoothTimeInFrames; - - if (pGainer->config.channels <= 32) { - float pRunningGain[32]; - float pRunningGainDelta[32]; /* Could this be heap-allocated as part of the ma_gainer object? */ - - /* Initialize the running gain. */ - for (iChannel = 0; iChannel < pGainer->config.channels; iChannel += 1) { - float t = (pGainer->pNewGains[iChannel] - pGainer->pOldGains[iChannel]) * pGainer->masterVolume; - pRunningGainDelta[iChannel] = t * d; - pRunningGain[iChannel] = (pGainer->pOldGains[iChannel] * pGainer->masterVolume) + (t * a); - } - - iFrame = 0; - - /* Optimized paths for common channel counts. This is mostly just experimenting with some SIMD ideas. It's not necessarily final. */ - if (pGainer->config.channels == 2) { - #if defined(MA_SUPPORT_SSE2) - if (ma_has_sse2()) { - ma_uint64 unrolledLoopCount = interpolatedFrameCount >> 1; - - /* Expand some arrays so we can have a clean SIMD loop below. */ - __m128 runningGainDelta0 = _mm_set_ps(pRunningGainDelta[1], pRunningGainDelta[0], pRunningGainDelta[1], pRunningGainDelta[0]); - __m128 runningGain0 = _mm_set_ps(pRunningGain[1] + pRunningGainDelta[1], pRunningGain[0] + pRunningGainDelta[0], pRunningGain[1], pRunningGain[0]); - - for (; iFrame < unrolledLoopCount; iFrame += 1) { - _mm_storeu_ps(&pFramesOutF32[iFrame*4 + 0], _mm_mul_ps(_mm_loadu_ps(&pFramesInF32[iFrame*4 + 0]), runningGain0)); - runningGain0 = _mm_add_ps(runningGain0, runningGainDelta0); - } - - iFrame = unrolledLoopCount << 1; - } else - #endif - { - /* - Two different scalar implementations here. Clang (and I assume GCC) will vectorize - both of these, but the bottom version results in a nicer vectorization with less - instructions emitted. The problem, however, is that the bottom version runs slower - when compiled with MSVC. The top version will be partially vectorized by MSVC. - */ - #if defined(_MSC_VER) && !defined(__clang__) - ma_uint64 unrolledLoopCount = interpolatedFrameCount >> 1; - - /* Expand some arrays so we can have a clean 4x SIMD operation in the loop. */ - pRunningGainDelta[2] = pRunningGainDelta[0]; - pRunningGainDelta[3] = pRunningGainDelta[1]; - pRunningGain[2] = pRunningGain[0] + pRunningGainDelta[0]; - pRunningGain[3] = pRunningGain[1] + pRunningGainDelta[1]; - - for (; iFrame < unrolledLoopCount; iFrame += 1) { - pFramesOutF32[iFrame*4 + 0] = pFramesInF32[iFrame*4 + 0] * pRunningGain[0]; - pFramesOutF32[iFrame*4 + 1] = pFramesInF32[iFrame*4 + 1] * pRunningGain[1]; - pFramesOutF32[iFrame*4 + 2] = pFramesInF32[iFrame*4 + 2] * pRunningGain[2]; - pFramesOutF32[iFrame*4 + 3] = pFramesInF32[iFrame*4 + 3] * pRunningGain[3]; - - /* Move the running gain forward towards the new gain. */ - pRunningGain[0] += pRunningGainDelta[0]; - pRunningGain[1] += pRunningGainDelta[1]; - pRunningGain[2] += pRunningGainDelta[2]; - pRunningGain[3] += pRunningGainDelta[3]; - } - - iFrame = unrolledLoopCount << 1; - #else - for (; iFrame < interpolatedFrameCount; iFrame += 1) { - for (iChannel = 0; iChannel < 2; iChannel += 1) { - pFramesOutF32[iFrame*2 + iChannel] = pFramesInF32[iFrame*2 + iChannel] * pRunningGain[iChannel]; - } - - for (iChannel = 0; iChannel < 2; iChannel += 1) { - pRunningGain[iChannel] += pRunningGainDelta[iChannel]; - } - } - #endif - } - } else if (pGainer->config.channels == 6) { - #if defined(MA_SUPPORT_SSE2) - if (ma_has_sse2()) { - /* - For 6 channels things are a bit more complicated because 6 isn't cleanly divisible by 4. We need to do 2 frames - at a time, meaning we'll be doing 12 samples in a group. Like the stereo case we'll need to expand some arrays - so we can do clean 4x SIMD operations. - */ - ma_uint64 unrolledLoopCount = interpolatedFrameCount >> 1; - - /* Expand some arrays so we can have a clean SIMD loop below. */ - __m128 runningGainDelta0 = _mm_set_ps(pRunningGainDelta[3], pRunningGainDelta[2], pRunningGainDelta[1], pRunningGainDelta[0]); - __m128 runningGainDelta1 = _mm_set_ps(pRunningGainDelta[1], pRunningGainDelta[0], pRunningGainDelta[5], pRunningGainDelta[4]); - __m128 runningGainDelta2 = _mm_set_ps(pRunningGainDelta[5], pRunningGainDelta[4], pRunningGainDelta[3], pRunningGainDelta[2]); - - __m128 runningGain0 = _mm_set_ps(pRunningGain[3], pRunningGain[2], pRunningGain[1], pRunningGain[0]); - __m128 runningGain1 = _mm_set_ps(pRunningGain[1] + pRunningGainDelta[1], pRunningGain[0] + pRunningGainDelta[0], pRunningGain[5], pRunningGain[4]); - __m128 runningGain2 = _mm_set_ps(pRunningGain[5] + pRunningGainDelta[5], pRunningGain[4] + pRunningGainDelta[4], pRunningGain[3] + pRunningGainDelta[3], pRunningGain[2] + pRunningGainDelta[2]); - - for (; iFrame < unrolledLoopCount; iFrame += 1) { - _mm_storeu_ps(&pFramesOutF32[iFrame*12 + 0], _mm_mul_ps(_mm_loadu_ps(&pFramesInF32[iFrame*12 + 0]), runningGain0)); - _mm_storeu_ps(&pFramesOutF32[iFrame*12 + 4], _mm_mul_ps(_mm_loadu_ps(&pFramesInF32[iFrame*12 + 4]), runningGain1)); - _mm_storeu_ps(&pFramesOutF32[iFrame*12 + 8], _mm_mul_ps(_mm_loadu_ps(&pFramesInF32[iFrame*12 + 8]), runningGain2)); - - runningGain0 = _mm_add_ps(runningGain0, runningGainDelta0); - runningGain1 = _mm_add_ps(runningGain1, runningGainDelta1); - runningGain2 = _mm_add_ps(runningGain2, runningGainDelta2); - } - - iFrame = unrolledLoopCount << 1; - } else - #endif - { - for (; iFrame < interpolatedFrameCount; iFrame += 1) { - for (iChannel = 0; iChannel < 6; iChannel += 1) { - pFramesOutF32[iFrame*6 + iChannel] = pFramesInF32[iFrame*6 + iChannel] * pRunningGain[iChannel]; - } - - /* Move the running gain forward towards the new gain. */ - for (iChannel = 0; iChannel < 6; iChannel += 1) { - pRunningGain[iChannel] += pRunningGainDelta[iChannel]; - } - } - } - } else if (pGainer->config.channels == 8) { - /* For 8 channels we can just go over frame by frame and do all eight channels as 2 separate 4x SIMD operations. */ - #if defined(MA_SUPPORT_SSE2) - if (ma_has_sse2()) { - __m128 runningGainDelta0 = _mm_loadu_ps(&pRunningGainDelta[0]); - __m128 runningGainDelta1 = _mm_loadu_ps(&pRunningGainDelta[4]); - __m128 runningGain0 = _mm_loadu_ps(&pRunningGain[0]); - __m128 runningGain1 = _mm_loadu_ps(&pRunningGain[4]); - - for (; iFrame < interpolatedFrameCount; iFrame += 1) { - _mm_storeu_ps(&pFramesOutF32[iFrame*8 + 0], _mm_mul_ps(_mm_loadu_ps(&pFramesInF32[iFrame*8 + 0]), runningGain0)); - _mm_storeu_ps(&pFramesOutF32[iFrame*8 + 4], _mm_mul_ps(_mm_loadu_ps(&pFramesInF32[iFrame*8 + 4]), runningGain1)); - - runningGain0 = _mm_add_ps(runningGain0, runningGainDelta0); - runningGain1 = _mm_add_ps(runningGain1, runningGainDelta1); - } - } else - #endif - { - /* This is crafted so that it auto-vectorizes when compiled with Clang. */ - for (; iFrame < interpolatedFrameCount; iFrame += 1) { - for (iChannel = 0; iChannel < 8; iChannel += 1) { - pFramesOutF32[iFrame*8 + iChannel] = pFramesInF32[iFrame*8 + iChannel] * pRunningGain[iChannel]; - } - - /* Move the running gain forward towards the new gain. */ - for (iChannel = 0; iChannel < 8; iChannel += 1) { - pRunningGain[iChannel] += pRunningGainDelta[iChannel]; - } - } - } - } - - for (; iFrame < interpolatedFrameCount; iFrame += 1) { - for (iChannel = 0; iChannel < pGainer->config.channels; iChannel += 1) { - pFramesOutF32[iFrame*pGainer->config.channels + iChannel] = pFramesInF32[iFrame*pGainer->config.channels + iChannel] * pRunningGain[iChannel]; - pRunningGain[iChannel] += pRunningGainDelta[iChannel]; - } - } - } else { - /* Slower path for extreme channel counts where we can't fit enough on the stack. We could also move this to the heap as part of the ma_gainer object which might even be better since it'll only be updated when the gains actually change. */ - for (iFrame = 0; iFrame < interpolatedFrameCount; iFrame += 1) { - for (iChannel = 0; iChannel < pGainer->config.channels; iChannel += 1) { - pFramesOutF32[iFrame*pGainer->config.channels + iChannel] = pFramesInF32[iFrame*pGainer->config.channels + iChannel] * ma_mix_f32_fast(pGainer->pOldGains[iChannel], pGainer->pNewGains[iChannel], a) * pGainer->masterVolume; - } - - a += d; - } - } - } - - /* Make sure the timer is updated. */ - pGainer->t = (ma_uint32)ma_min(pGainer->t + interpolatedFrameCount, pGainer->config.smoothTimeInFrames); - - /* Adjust our arguments so the next part can work normally. */ - frameCount -= interpolatedFrameCount; - pFramesOut = ma_offset_ptr(pFramesOut, interpolatedFrameCount * sizeof(float)); - pFramesIn = ma_offset_ptr(pFramesIn, interpolatedFrameCount * sizeof(float)); - } - - /* All we need to do here is apply the new gains using an optimized path. */ - if (pFramesOut != NULL && pFramesIn != NULL) { - if (pGainer->config.channels <= 32) { - float gains[32]; - for (iChannel = 0; iChannel < pGainer->config.channels; iChannel += 1) { - gains[iChannel] = pGainer->pNewGains[iChannel] * pGainer->masterVolume; - } - - ma_copy_and_apply_volume_factor_per_channel_f32((float*)pFramesOut, (const float*)pFramesIn, frameCount, pGainer->config.channels, gains); - } else { - /* Slow path. Too many channels to fit on the stack. Need to apply a master volume as a separate path. */ - for (iFrame = 0; iFrame < frameCount; iFrame += 1) { - for (iChannel = 0; iChannel < pGainer->config.channels; iChannel += 1) { - ((float*)pFramesOut)[iFrame*pGainer->config.channels + iChannel] = ((const float*)pFramesIn)[iFrame*pGainer->config.channels + iChannel] * pGainer->pNewGains[iChannel] * pGainer->masterVolume; - } - } - } - } - - /* Now that some frames have been processed we need to make sure future changes to the gain are interpolated. */ - if (pGainer->t == (ma_uint32)-1) { - pGainer->t = (ma_uint32)ma_min(pGainer->config.smoothTimeInFrames, frameCount); - } - -#if 0 - if (pGainer->t >= pGainer->config.smoothTimeInFrames) { - /* Fast path. No gain calculation required. */ - ma_copy_and_apply_volume_factor_per_channel_f32(pFramesOutF32, pFramesInF32, frameCount, pGainer->config.channels, pGainer->pNewGains); - ma_apply_volume_factor_f32(pFramesOutF32, frameCount * pGainer->config.channels, pGainer->masterVolume); - - /* Now that some frames have been processed we need to make sure future changes to the gain are interpolated. */ - if (pGainer->t == (ma_uint32)-1) { - pGainer->t = pGainer->config.smoothTimeInFrames; - } - } else { - /* Slow path. Need to interpolate the gain for each channel individually. */ - - /* We can allow the input and output buffers to be null in which case we'll just update the internal timer. */ - if (pFramesOut != NULL && pFramesIn != NULL) { - float a = (float)pGainer->t / pGainer->config.smoothTimeInFrames; - float d = 1.0f / pGainer->config.smoothTimeInFrames; - ma_uint32 channelCount = pGainer->config.channels; - - for (iFrame = 0; iFrame < frameCount; iFrame += 1) { - for (iChannel = 0; iChannel < channelCount; iChannel += 1) { - pFramesOutF32[iChannel] = pFramesInF32[iChannel] * ma_mix_f32_fast(pGainer->pOldGains[iChannel], pGainer->pNewGains[iChannel], a) * pGainer->masterVolume; - } - - pFramesOutF32 += channelCount; - pFramesInF32 += channelCount; - - a += d; - if (a > 1) { - a = 1; - } - } - } - - pGainer->t = (ma_uint32)ma_min(pGainer->t + frameCount, pGainer->config.smoothTimeInFrames); - - #if 0 /* Reference implementation. */ - for (iFrame = 0; iFrame < frameCount; iFrame += 1) { - /* We can allow the input and output buffers to be null in which case we'll just update the internal timer. */ - if (pFramesOut != NULL && pFramesIn != NULL) { - for (iChannel = 0; iChannel < pGainer->config.channels; iChannel += 1) { - pFramesOutF32[iFrame * pGainer->config.channels + iChannel] = pFramesInF32[iFrame * pGainer->config.channels + iChannel] * ma_gainer_calculate_current_gain(pGainer, iChannel) * pGainer->masterVolume; - } - } - - /* Move interpolation time forward, but don't go beyond our smoothing time. */ - pGainer->t = ma_min(pGainer->t + 1, pGainer->config.smoothTimeInFrames); - } - #endif - } -#endif - - return MA_SUCCESS; -} - -MA_API ma_result ma_gainer_process_pcm_frames(ma_gainer* pGainer, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount) -{ - if (pGainer == NULL) { - return MA_INVALID_ARGS; - } - - /* - ma_gainer_process_pcm_frames_internal() marks pFramesOut and pFramesIn with MA_RESTRICT which - helps with auto-vectorization. - */ - return ma_gainer_process_pcm_frames_internal(pGainer, pFramesOut, pFramesIn, frameCount); -} - -static void ma_gainer_set_gain_by_index(ma_gainer* pGainer, float newGain, ma_uint32 iChannel) -{ - pGainer->pOldGains[iChannel] = ma_gainer_calculate_current_gain(pGainer, iChannel); - pGainer->pNewGains[iChannel] = newGain; -} - -static void ma_gainer_reset_smoothing_time(ma_gainer* pGainer) -{ - if (pGainer->t == (ma_uint32)-1) { - pGainer->t = pGainer->config.smoothTimeInFrames; /* No smoothing required for initial gains setting. */ - } else { - pGainer->t = 0; - } -} - -MA_API ma_result ma_gainer_set_gain(ma_gainer* pGainer, float newGain) -{ - ma_uint32 iChannel; - - if (pGainer == NULL) { - return MA_INVALID_ARGS; - } - - for (iChannel = 0; iChannel < pGainer->config.channels; iChannel += 1) { - ma_gainer_set_gain_by_index(pGainer, newGain, iChannel); - } - - /* The smoothing time needs to be reset to ensure we always interpolate by the configured smoothing time, but only if it's not the first setting. */ - ma_gainer_reset_smoothing_time(pGainer); - - return MA_SUCCESS; -} - -MA_API ma_result ma_gainer_set_gains(ma_gainer* pGainer, float* pNewGains) -{ - ma_uint32 iChannel; - - if (pGainer == NULL || pNewGains == NULL) { - return MA_INVALID_ARGS; - } - - for (iChannel = 0; iChannel < pGainer->config.channels; iChannel += 1) { - ma_gainer_set_gain_by_index(pGainer, pNewGains[iChannel], iChannel); - } - - /* The smoothing time needs to be reset to ensure we always interpolate by the configured smoothing time, but only if it's not the first setting. */ - ma_gainer_reset_smoothing_time(pGainer); - - return MA_SUCCESS; -} - -MA_API ma_result ma_gainer_set_master_volume(ma_gainer* pGainer, float volume) -{ - if (pGainer == NULL) { - return MA_INVALID_ARGS; - } - - pGainer->masterVolume = volume; - - return MA_SUCCESS; -} - -MA_API ma_result ma_gainer_get_master_volume(const ma_gainer* pGainer, float* pVolume) -{ - if (pGainer == NULL || pVolume == NULL) { - return MA_INVALID_ARGS; - } - - *pVolume = pGainer->masterVolume; - - return MA_SUCCESS; -} - - -MA_API ma_panner_config ma_panner_config_init(ma_format format, ma_uint32 channels) -{ - ma_panner_config config; - - MA_ZERO_OBJECT(&config); - config.format = format; - config.channels = channels; - config.mode = ma_pan_mode_balance; /* Set to balancing mode by default because it's consistent with other audio engines and most likely what the caller is expecting. */ - config.pan = 0; - - return config; -} - - -MA_API ma_result ma_panner_init(const ma_panner_config* pConfig, ma_panner* pPanner) -{ - if (pPanner == NULL) { - return MA_INVALID_ARGS; - } - - MA_ZERO_OBJECT(pPanner); - - if (pConfig == NULL) { - return MA_INVALID_ARGS; - } - - pPanner->format = pConfig->format; - pPanner->channels = pConfig->channels; - pPanner->mode = pConfig->mode; - pPanner->pan = pConfig->pan; - - return MA_SUCCESS; -} - -static void ma_stereo_balance_pcm_frames_f32(float* pFramesOut, const float* pFramesIn, ma_uint64 frameCount, float pan) -{ - ma_uint64 iFrame; - - if (pan > 0) { - float factor = 1.0f - pan; - if (pFramesOut == pFramesIn) { - for (iFrame = 0; iFrame < frameCount; iFrame += 1) { - pFramesOut[iFrame*2 + 0] = pFramesIn[iFrame*2 + 0] * factor; - } - } else { - for (iFrame = 0; iFrame < frameCount; iFrame += 1) { - pFramesOut[iFrame*2 + 0] = pFramesIn[iFrame*2 + 0] * factor; - pFramesOut[iFrame*2 + 1] = pFramesIn[iFrame*2 + 1]; - } - } - } else { - float factor = 1.0f + pan; - if (pFramesOut == pFramesIn) { - for (iFrame = 0; iFrame < frameCount; iFrame += 1) { - pFramesOut[iFrame*2 + 1] = pFramesIn[iFrame*2 + 1] * factor; - } - } else { - for (iFrame = 0; iFrame < frameCount; iFrame += 1) { - pFramesOut[iFrame*2 + 0] = pFramesIn[iFrame*2 + 0]; - pFramesOut[iFrame*2 + 1] = pFramesIn[iFrame*2 + 1] * factor; - } - } - } -} - -static void ma_stereo_balance_pcm_frames(void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount, ma_format format, float pan) -{ - if (pan == 0) { - /* Fast path. No panning required. */ - if (pFramesOut == pFramesIn) { - /* No-op */ - } else { - ma_copy_pcm_frames(pFramesOut, pFramesIn, frameCount, format, 2); - } - - return; - } - - switch (format) { - case ma_format_f32: ma_stereo_balance_pcm_frames_f32((float*)pFramesOut, (float*)pFramesIn, frameCount, pan); break; - - /* Unknown format. Just copy. */ - default: - { - ma_copy_pcm_frames(pFramesOut, pFramesIn, frameCount, format, 2); - } break; - } -} - - -static void ma_stereo_pan_pcm_frames_f32(float* pFramesOut, const float* pFramesIn, ma_uint64 frameCount, float pan) -{ - ma_uint64 iFrame; - - if (pan > 0) { - float factorL0 = 1.0f - pan; - float factorL1 = 0.0f + pan; - - for (iFrame = 0; iFrame < frameCount; iFrame += 1) { - float sample0 = (pFramesIn[iFrame*2 + 0] * factorL0); - float sample1 = (pFramesIn[iFrame*2 + 0] * factorL1) + pFramesIn[iFrame*2 + 1]; - - pFramesOut[iFrame*2 + 0] = sample0; - pFramesOut[iFrame*2 + 1] = sample1; - } - } else { - float factorR0 = 0.0f - pan; - float factorR1 = 1.0f + pan; - - for (iFrame = 0; iFrame < frameCount; iFrame += 1) { - float sample0 = pFramesIn[iFrame*2 + 0] + (pFramesIn[iFrame*2 + 1] * factorR0); - float sample1 = (pFramesIn[iFrame*2 + 1] * factorR1); - - pFramesOut[iFrame*2 + 0] = sample0; - pFramesOut[iFrame*2 + 1] = sample1; - } - } -} - -static void ma_stereo_pan_pcm_frames(void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount, ma_format format, float pan) -{ - if (pan == 0) { - /* Fast path. No panning required. */ - if (pFramesOut == pFramesIn) { - /* No-op */ - } else { - ma_copy_pcm_frames(pFramesOut, pFramesIn, frameCount, format, 2); - } - - return; - } - - switch (format) { - case ma_format_f32: ma_stereo_pan_pcm_frames_f32((float*)pFramesOut, (float*)pFramesIn, frameCount, pan); break; - - /* Unknown format. Just copy. */ - default: - { - ma_copy_pcm_frames(pFramesOut, pFramesIn, frameCount, format, 2); - } break; - } -} - -MA_API ma_result ma_panner_process_pcm_frames(ma_panner* pPanner, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount) -{ - if (pPanner == NULL || pFramesOut == NULL || pFramesIn == NULL) { - return MA_INVALID_ARGS; - } - - if (pPanner->channels == 2) { - /* Stereo case. For now assume channel 0 is left and channel right is 1, but should probably add support for a channel map. */ - if (pPanner->mode == ma_pan_mode_balance) { - ma_stereo_balance_pcm_frames(pFramesOut, pFramesIn, frameCount, pPanner->format, pPanner->pan); - } else { - ma_stereo_pan_pcm_frames(pFramesOut, pFramesIn, frameCount, pPanner->format, pPanner->pan); - } - } else { - if (pPanner->channels == 1) { - /* Panning has no effect on mono streams. */ - ma_copy_pcm_frames(pFramesOut, pFramesIn, frameCount, pPanner->format, pPanner->channels); - } else { - /* For now we're not going to support non-stereo set ups. Not sure how I want to handle this case just yet. */ - ma_copy_pcm_frames(pFramesOut, pFramesIn, frameCount, pPanner->format, pPanner->channels); - } - } - - return MA_SUCCESS; -} - -MA_API void ma_panner_set_mode(ma_panner* pPanner, ma_pan_mode mode) -{ - if (pPanner == NULL) { - return; - } - - pPanner->mode = mode; -} - -MA_API ma_pan_mode ma_panner_get_mode(const ma_panner* pPanner) -{ - if (pPanner == NULL) { - return ma_pan_mode_balance; - } - - return pPanner->mode; -} - -MA_API void ma_panner_set_pan(ma_panner* pPanner, float pan) -{ - if (pPanner == NULL) { - return; - } - - pPanner->pan = ma_clamp(pan, -1.0f, 1.0f); -} - -MA_API float ma_panner_get_pan(const ma_panner* pPanner) -{ - if (pPanner == NULL) { - return 0; - } - - return pPanner->pan; -} - - - - -MA_API ma_fader_config ma_fader_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRate) -{ - ma_fader_config config; - - MA_ZERO_OBJECT(&config); - config.format = format; - config.channels = channels; - config.sampleRate = sampleRate; - - return config; -} - - -MA_API ma_result ma_fader_init(const ma_fader_config* pConfig, ma_fader* pFader) -{ - if (pFader == NULL) { - return MA_INVALID_ARGS; - } - - MA_ZERO_OBJECT(pFader); - - if (pConfig == NULL) { - return MA_INVALID_ARGS; - } - - /* Only f32 is supported for now. */ - if (pConfig->format != ma_format_f32) { - return MA_INVALID_ARGS; - } - - pFader->config = *pConfig; - pFader->volumeBeg = 1; - pFader->volumeEnd = 1; - pFader->lengthInFrames = 0; - pFader->cursorInFrames = 0; - - return MA_SUCCESS; -} - -MA_API ma_result ma_fader_process_pcm_frames(ma_fader* pFader, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount) -{ - if (pFader == NULL) { - return MA_INVALID_ARGS; - } - - /* If the cursor is still negative we need to just copy the absolute number of those frames, but no more than frameCount. */ - if (pFader->cursorInFrames < 0) { - ma_uint64 absCursorInFrames = (ma_uint64)0 - pFader->cursorInFrames; - if (absCursorInFrames > frameCount) { - absCursorInFrames = frameCount; - } - - ma_copy_pcm_frames(pFramesOut, pFramesIn, absCursorInFrames, pFader->config.format, pFader->config.channels); - - pFader->cursorInFrames += absCursorInFrames; - frameCount -= absCursorInFrames; - pFramesOut = ma_offset_ptr(pFramesOut, ma_get_bytes_per_frame(pFader->config.format, pFader->config.channels)*absCursorInFrames); - pFramesIn = ma_offset_ptr(pFramesIn, ma_get_bytes_per_frame(pFader->config.format, pFader->config.channels)*absCursorInFrames); - } - - if (pFader->cursorInFrames >= 0) { - /* - For now we need to clamp frameCount so that the cursor never overflows 32-bits. This is required for - the conversion to a float which we use for the linear interpolation. This might be changed later. - */ - if (frameCount + pFader->cursorInFrames > UINT_MAX) { - frameCount = UINT_MAX - pFader->cursorInFrames; - } - - /* Optimized path if volumeBeg and volumeEnd are equal. */ - if (pFader->volumeBeg == pFader->volumeEnd) { - if (pFader->volumeBeg == 1) { - /* Straight copy. */ - ma_copy_pcm_frames(pFramesOut, pFramesIn, frameCount, pFader->config.format, pFader->config.channels); - } else { - /* Copy with volume. */ - ma_copy_and_apply_volume_and_clip_pcm_frames(pFramesOut, pFramesIn, frameCount, pFader->config.format, pFader->config.channels, pFader->volumeBeg); - } - } else { - /* Slower path. Volumes are different, so may need to do an interpolation. */ - if ((ma_uint64)pFader->cursorInFrames >= pFader->lengthInFrames) { - /* Fast path. We've gone past the end of the fade period so just apply the end volume to all samples. */ - ma_copy_and_apply_volume_and_clip_pcm_frames(pFramesOut, pFramesIn, frameCount, pFader->config.format, pFader->config.channels, pFader->volumeEnd); - } else { - /* Slow path. This is where we do the actual fading. */ - ma_uint64 iFrame; - ma_uint32 iChannel; - - /* For now we only support f32. Support for other formats might be added later. */ - if (pFader->config.format == ma_format_f32) { - const float* pFramesInF32 = (const float*)pFramesIn; - /* */ float* pFramesOutF32 = ( float*)pFramesOut; - - for (iFrame = 0; iFrame < frameCount; iFrame += 1) { - float a = (ma_uint32)ma_min(pFader->cursorInFrames + iFrame, pFader->lengthInFrames) / (float)((ma_uint32)pFader->lengthInFrames); /* Safe cast due to the frameCount clamp at the top of this function. */ - float volume = ma_mix_f32_fast(pFader->volumeBeg, pFader->volumeEnd, a); - - for (iChannel = 0; iChannel < pFader->config.channels; iChannel += 1) { - pFramesOutF32[iFrame*pFader->config.channels + iChannel] = pFramesInF32[iFrame*pFader->config.channels + iChannel] * volume; - } - } - } else { - return MA_NOT_IMPLEMENTED; - } - } - } - } - - pFader->cursorInFrames += frameCount; - - return MA_SUCCESS; -} - -MA_API void ma_fader_get_data_format(const ma_fader* pFader, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate) -{ - if (pFader == NULL) { - return; - } - - if (pFormat != NULL) { - *pFormat = pFader->config.format; - } - - if (pChannels != NULL) { - *pChannels = pFader->config.channels; - } - - if (pSampleRate != NULL) { - *pSampleRate = pFader->config.sampleRate; - } -} - -MA_API void ma_fader_set_fade(ma_fader* pFader, float volumeBeg, float volumeEnd, ma_uint64 lengthInFrames) -{ - ma_fader_set_fade_ex(pFader, volumeBeg, volumeEnd, lengthInFrames, 0); -} - -MA_API void ma_fader_set_fade_ex(ma_fader* pFader, float volumeBeg, float volumeEnd, ma_uint64 lengthInFrames, ma_int64 startOffsetInFrames) -{ - if (pFader == NULL) { - return; - } - - /* If the volume is negative, use current volume. */ - if (volumeBeg < 0) { - volumeBeg = ma_fader_get_current_volume(pFader); - } - - /* - The length needs to be clamped to 32-bits due to how we convert it to a float for linear - interpolation reasons. I might change this requirement later, but for now it's not important. - */ - if (lengthInFrames > UINT_MAX) { - lengthInFrames = UINT_MAX; - } - - /* The start offset needs to be clamped to ensure it doesn't overflow a signed number. */ - if (startOffsetInFrames > INT_MAX) { - startOffsetInFrames = INT_MAX; - } - - pFader->volumeBeg = volumeBeg; - pFader->volumeEnd = volumeEnd; - pFader->lengthInFrames = lengthInFrames; - pFader->cursorInFrames = -startOffsetInFrames; -} - -MA_API float ma_fader_get_current_volume(const ma_fader* pFader) -{ - if (pFader == NULL) { - return 0.0f; - } - - /* Any frames prior to the start of the fade period will be at unfaded volume. */ - if (pFader->cursorInFrames < 0) { - return 1.0f; - } - - /* The current volume depends on the position of the cursor. */ - if (pFader->cursorInFrames == 0) { - return pFader->volumeBeg; - } else if ((ma_uint64)pFader->cursorInFrames >= pFader->lengthInFrames) { /* Safe case because the < 0 case was checked above. */ - return pFader->volumeEnd; - } else { - /* The cursor is somewhere inside the fading period. We can figure this out with a simple linear interpolation between volumeBeg and volumeEnd based on our cursor position. */ - return ma_mix_f32_fast(pFader->volumeBeg, pFader->volumeEnd, (ma_uint32)pFader->cursorInFrames / (float)((ma_uint32)pFader->lengthInFrames)); /* Safe cast to uint32 because we clamp it in ma_fader_process_pcm_frames(). */ - } -} - - - - - -MA_API ma_vec3f ma_vec3f_init_3f(float x, float y, float z) -{ - ma_vec3f v; - - v.x = x; - v.y = y; - v.z = z; - - return v; -} - -MA_API ma_vec3f ma_vec3f_sub(ma_vec3f a, ma_vec3f b) -{ - return ma_vec3f_init_3f( - a.x - b.x, - a.y - b.y, - a.z - b.z - ); -} - -MA_API ma_vec3f ma_vec3f_neg(ma_vec3f a) -{ - return ma_vec3f_init_3f( - -a.x, - -a.y, - -a.z - ); -} - -MA_API float ma_vec3f_dot(ma_vec3f a, ma_vec3f b) -{ - return a.x*b.x + a.y*b.y + a.z*b.z; -} - -MA_API float ma_vec3f_len2(ma_vec3f v) -{ - return ma_vec3f_dot(v, v); -} - -MA_API float ma_vec3f_len(ma_vec3f v) -{ - return (float)ma_sqrtd(ma_vec3f_len2(v)); -} - - - -MA_API float ma_vec3f_dist(ma_vec3f a, ma_vec3f b) -{ - return ma_vec3f_len(ma_vec3f_sub(a, b)); -} - -MA_API ma_vec3f ma_vec3f_normalize(ma_vec3f v) -{ - float invLen; - float len2 = ma_vec3f_len2(v); - if (len2 == 0) { - return ma_vec3f_init_3f(0, 0, 0); - } - - invLen = ma_rsqrtf(len2); - v.x *= invLen; - v.y *= invLen; - v.z *= invLen; - - return v; -} - -MA_API ma_vec3f ma_vec3f_cross(ma_vec3f a, ma_vec3f b) -{ - return ma_vec3f_init_3f( - a.y*b.z - a.z*b.y, - a.z*b.x - a.x*b.z, - a.x*b.y - a.y*b.x - ); -} - - -MA_API void ma_atomic_vec3f_init(ma_atomic_vec3f* v, ma_vec3f value) -{ - v->v = value; - v->lock = 0; /* Important this is initialized to 0. */ -} - -MA_API void ma_atomic_vec3f_set(ma_atomic_vec3f* v, ma_vec3f value) -{ - ma_spinlock_lock(&v->lock); - { - v->v = value; - } - ma_spinlock_unlock(&v->lock); -} - -MA_API ma_vec3f ma_atomic_vec3f_get(ma_atomic_vec3f* v) -{ - ma_vec3f r; - - ma_spinlock_lock(&v->lock); - { - r = v->v; - } - ma_spinlock_unlock(&v->lock); - - return r; -} - - - -static void ma_channel_map_apply_f32(float* pFramesOut, const ma_channel* pChannelMapOut, ma_uint32 channelsOut, const float* pFramesIn, const ma_channel* pChannelMapIn, ma_uint32 channelsIn, ma_uint64 frameCount, ma_channel_mix_mode mode, ma_mono_expansion_mode monoExpansionMode); -static ma_bool32 ma_is_spatial_channel_position(ma_channel channelPosition); - - -#ifndef MA_DEFAULT_SPEED_OF_SOUND -#define MA_DEFAULT_SPEED_OF_SOUND 343.3f -#endif - -/* -These vectors represent the direction that speakers are facing from the center point. They're used -for panning in the spatializer. Must be normalized. -*/ -static ma_vec3f g_maChannelDirections[MA_CHANNEL_POSITION_COUNT] = { - { 0.0f, 0.0f, -1.0f }, /* MA_CHANNEL_NONE */ - { 0.0f, 0.0f, -1.0f }, /* MA_CHANNEL_MONO */ - {-0.7071f, 0.0f, -0.7071f }, /* MA_CHANNEL_FRONT_LEFT */ - {+0.7071f, 0.0f, -0.7071f }, /* MA_CHANNEL_FRONT_RIGHT */ - { 0.0f, 0.0f, -1.0f }, /* MA_CHANNEL_FRONT_CENTER */ - { 0.0f, 0.0f, -1.0f }, /* MA_CHANNEL_LFE */ - {-0.7071f, 0.0f, +0.7071f }, /* MA_CHANNEL_BACK_LEFT */ - {+0.7071f, 0.0f, +0.7071f }, /* MA_CHANNEL_BACK_RIGHT */ - {-0.3162f, 0.0f, -0.9487f }, /* MA_CHANNEL_FRONT_LEFT_CENTER */ - {+0.3162f, 0.0f, -0.9487f }, /* MA_CHANNEL_FRONT_RIGHT_CENTER */ - { 0.0f, 0.0f, +1.0f }, /* MA_CHANNEL_BACK_CENTER */ - {-1.0f, 0.0f, 0.0f }, /* MA_CHANNEL_SIDE_LEFT */ - {+1.0f, 0.0f, 0.0f }, /* MA_CHANNEL_SIDE_RIGHT */ - { 0.0f, +1.0f, 0.0f }, /* MA_CHANNEL_TOP_CENTER */ - {-0.5774f, +0.5774f, -0.5774f }, /* MA_CHANNEL_TOP_FRONT_LEFT */ - { 0.0f, +0.7071f, -0.7071f }, /* MA_CHANNEL_TOP_FRONT_CENTER */ - {+0.5774f, +0.5774f, -0.5774f }, /* MA_CHANNEL_TOP_FRONT_RIGHT */ - {-0.5774f, +0.5774f, +0.5774f }, /* MA_CHANNEL_TOP_BACK_LEFT */ - { 0.0f, +0.7071f, +0.7071f }, /* MA_CHANNEL_TOP_BACK_CENTER */ - {+0.5774f, +0.5774f, +0.5774f }, /* MA_CHANNEL_TOP_BACK_RIGHT */ - { 0.0f, 0.0f, -1.0f }, /* MA_CHANNEL_AUX_0 */ - { 0.0f, 0.0f, -1.0f }, /* MA_CHANNEL_AUX_1 */ - { 0.0f, 0.0f, -1.0f }, /* MA_CHANNEL_AUX_2 */ - { 0.0f, 0.0f, -1.0f }, /* MA_CHANNEL_AUX_3 */ - { 0.0f, 0.0f, -1.0f }, /* MA_CHANNEL_AUX_4 */ - { 0.0f, 0.0f, -1.0f }, /* MA_CHANNEL_AUX_5 */ - { 0.0f, 0.0f, -1.0f }, /* MA_CHANNEL_AUX_6 */ - { 0.0f, 0.0f, -1.0f }, /* MA_CHANNEL_AUX_7 */ - { 0.0f, 0.0f, -1.0f }, /* MA_CHANNEL_AUX_8 */ - { 0.0f, 0.0f, -1.0f }, /* MA_CHANNEL_AUX_9 */ - { 0.0f, 0.0f, -1.0f }, /* MA_CHANNEL_AUX_10 */ - { 0.0f, 0.0f, -1.0f }, /* MA_CHANNEL_AUX_11 */ - { 0.0f, 0.0f, -1.0f }, /* MA_CHANNEL_AUX_12 */ - { 0.0f, 0.0f, -1.0f }, /* MA_CHANNEL_AUX_13 */ - { 0.0f, 0.0f, -1.0f }, /* MA_CHANNEL_AUX_14 */ - { 0.0f, 0.0f, -1.0f }, /* MA_CHANNEL_AUX_15 */ - { 0.0f, 0.0f, -1.0f }, /* MA_CHANNEL_AUX_16 */ - { 0.0f, 0.0f, -1.0f }, /* MA_CHANNEL_AUX_17 */ - { 0.0f, 0.0f, -1.0f }, /* MA_CHANNEL_AUX_18 */ - { 0.0f, 0.0f, -1.0f }, /* MA_CHANNEL_AUX_19 */ - { 0.0f, 0.0f, -1.0f }, /* MA_CHANNEL_AUX_20 */ - { 0.0f, 0.0f, -1.0f }, /* MA_CHANNEL_AUX_21 */ - { 0.0f, 0.0f, -1.0f }, /* MA_CHANNEL_AUX_22 */ - { 0.0f, 0.0f, -1.0f }, /* MA_CHANNEL_AUX_23 */ - { 0.0f, 0.0f, -1.0f }, /* MA_CHANNEL_AUX_24 */ - { 0.0f, 0.0f, -1.0f }, /* MA_CHANNEL_AUX_25 */ - { 0.0f, 0.0f, -1.0f }, /* MA_CHANNEL_AUX_26 */ - { 0.0f, 0.0f, -1.0f }, /* MA_CHANNEL_AUX_27 */ - { 0.0f, 0.0f, -1.0f }, /* MA_CHANNEL_AUX_28 */ - { 0.0f, 0.0f, -1.0f }, /* MA_CHANNEL_AUX_29 */ - { 0.0f, 0.0f, -1.0f }, /* MA_CHANNEL_AUX_30 */ - { 0.0f, 0.0f, -1.0f } /* MA_CHANNEL_AUX_31 */ -}; - -static ma_vec3f ma_get_channel_direction(ma_channel channel) -{ - if (channel >= MA_CHANNEL_POSITION_COUNT) { - return ma_vec3f_init_3f(0, 0, -1); - } else { - return g_maChannelDirections[channel]; - } -} - - - -static float ma_attenuation_inverse(float distance, float minDistance, float maxDistance, float rolloff) -{ - if (minDistance >= maxDistance) { - return 1; /* To avoid division by zero. Do not attenuate. */ - } - - return minDistance / (minDistance + rolloff * (ma_clamp(distance, minDistance, maxDistance) - minDistance)); -} - -static float ma_attenuation_linear(float distance, float minDistance, float maxDistance, float rolloff) -{ - if (minDistance >= maxDistance) { - return 1; /* To avoid division by zero. Do not attenuate. */ - } - - return 1 - rolloff * (ma_clamp(distance, minDistance, maxDistance) - minDistance) / (maxDistance - minDistance); -} - -static float ma_attenuation_exponential(float distance, float minDistance, float maxDistance, float rolloff) -{ - if (minDistance >= maxDistance) { - return 1; /* To avoid division by zero. Do not attenuate. */ - } - - return (float)ma_powd(ma_clamp(distance, minDistance, maxDistance) / minDistance, -rolloff); -} - - -/* -Doppler Effect calculation taken from the OpenAL spec, with two main differences: - - 1) The source to listener vector will have already been calculated at an earlier step so we can - just use that directly. We need only the position of the source relative to the origin. - - 2) We don't scale by a frequency because we actually just want the ratio which we'll plug straight - into the resampler directly. -*/ -static float ma_doppler_pitch(ma_vec3f relativePosition, ma_vec3f sourceVelocity, ma_vec3f listenVelocity, float speedOfSound, float dopplerFactor) -{ - float len; - float vls; - float vss; - - len = ma_vec3f_len(relativePosition); - - /* - There's a case where the position of the source will be right on top of the listener in which - case the length will be 0 and we'll end up with a division by zero. We can just return a ratio - of 1.0 in this case. This is not considered in the OpenAL spec, but is necessary. - */ - if (len == 0) { - return 1.0; - } - - vls = ma_vec3f_dot(relativePosition, listenVelocity) / len; - vss = ma_vec3f_dot(relativePosition, sourceVelocity) / len; - - vls = ma_min(vls, speedOfSound / dopplerFactor); - vss = ma_min(vss, speedOfSound / dopplerFactor); - - return (speedOfSound - dopplerFactor*vls) / (speedOfSound - dopplerFactor*vss); -} - - -static void ma_get_default_channel_map_for_spatializer(ma_channel* pChannelMap, size_t channelMapCap, ma_uint32 channelCount) -{ - /* - Special case for stereo. Want to default the left and right speakers to side left and side - right so that they're facing directly down the X axis rather than slightly forward. Not - doing this will result in sounds being quieter when behind the listener. This might - actually be good for some scenarios, but I don't think it's an appropriate default because - it can be a bit unexpected. - */ - if (channelCount == 2) { - pChannelMap[0] = MA_CHANNEL_SIDE_LEFT; - pChannelMap[1] = MA_CHANNEL_SIDE_RIGHT; - } else { - ma_channel_map_init_standard(ma_standard_channel_map_default, pChannelMap, channelMapCap, channelCount); - } -} - - -MA_API ma_spatializer_listener_config ma_spatializer_listener_config_init(ma_uint32 channelsOut) -{ - ma_spatializer_listener_config config; - - MA_ZERO_OBJECT(&config); - config.channelsOut = channelsOut; - config.pChannelMapOut = NULL; - config.handedness = ma_handedness_right; - config.worldUp = ma_vec3f_init_3f(0, 1, 0); - config.coneInnerAngleInRadians = 6.283185f; /* 360 degrees. */ - config.coneOuterAngleInRadians = 6.283185f; /* 360 degrees. */ - config.coneOuterGain = 0; - config.speedOfSound = 343.3f; /* Same as OpenAL. Used for doppler effect. */ - - return config; -} - - -typedef struct -{ - size_t sizeInBytes; - size_t channelMapOutOffset; -} ma_spatializer_listener_heap_layout; - -static ma_result ma_spatializer_listener_get_heap_layout(const ma_spatializer_listener_config* pConfig, ma_spatializer_listener_heap_layout* pHeapLayout) -{ - MA_ASSERT(pHeapLayout != NULL); - - MA_ZERO_OBJECT(pHeapLayout); - - if (pConfig == NULL) { - return MA_INVALID_ARGS; - } - - if (pConfig->channelsOut == 0) { - return MA_INVALID_ARGS; - } - - pHeapLayout->sizeInBytes = 0; - - /* Channel map. We always need this, even for passthroughs. */ - pHeapLayout->channelMapOutOffset = pHeapLayout->sizeInBytes; - pHeapLayout->sizeInBytes += ma_align_64(sizeof(*pConfig->pChannelMapOut) * pConfig->channelsOut); - - return MA_SUCCESS; -} - - -MA_API ma_result ma_spatializer_listener_get_heap_size(const ma_spatializer_listener_config* pConfig, size_t* pHeapSizeInBytes) -{ - ma_result result; - ma_spatializer_listener_heap_layout heapLayout; - - if (pHeapSizeInBytes == NULL) { - return MA_INVALID_ARGS; - } - - *pHeapSizeInBytes = 0; - - result = ma_spatializer_listener_get_heap_layout(pConfig, &heapLayout); - if (result != MA_SUCCESS) { - return result; - } - - *pHeapSizeInBytes = heapLayout.sizeInBytes; - - return MA_SUCCESS; -} - -MA_API ma_result ma_spatializer_listener_init_preallocated(const ma_spatializer_listener_config* pConfig, void* pHeap, ma_spatializer_listener* pListener) -{ - ma_result result; - ma_spatializer_listener_heap_layout heapLayout; - - if (pListener == NULL) { - return MA_INVALID_ARGS; - } - - MA_ZERO_OBJECT(pListener); - - result = ma_spatializer_listener_get_heap_layout(pConfig, &heapLayout); - if (result != MA_SUCCESS) { - return result; - } - - pListener->_pHeap = pHeap; - MA_ZERO_MEMORY(pHeap, heapLayout.sizeInBytes); - - pListener->config = *pConfig; - ma_atomic_vec3f_init(&pListener->position, ma_vec3f_init_3f(0, 0, 0)); - ma_atomic_vec3f_init(&pListener->direction, ma_vec3f_init_3f(0, 0, -1)); - ma_atomic_vec3f_init(&pListener->velocity, ma_vec3f_init_3f(0, 0, 0)); - pListener->isEnabled = MA_TRUE; - - /* Swap the forward direction if we're left handed (it was initialized based on right handed). */ - if (pListener->config.handedness == ma_handedness_left) { - ma_vec3f negDir = ma_vec3f_neg(ma_spatializer_listener_get_direction(pListener)); - ma_spatializer_listener_set_direction(pListener, negDir.x, negDir.y, negDir.z); - } - - - /* We must always have a valid channel map. */ - pListener->config.pChannelMapOut = (ma_channel*)ma_offset_ptr(pHeap, heapLayout.channelMapOutOffset); - - /* Use a slightly different default channel map for stereo. */ - if (pConfig->pChannelMapOut == NULL) { - ma_get_default_channel_map_for_spatializer(pListener->config.pChannelMapOut, pConfig->channelsOut, pConfig->channelsOut); - } else { - ma_channel_map_copy_or_default(pListener->config.pChannelMapOut, pConfig->channelsOut, pConfig->pChannelMapOut, pConfig->channelsOut); - } - - return MA_SUCCESS; -} - -MA_API ma_result ma_spatializer_listener_init(const ma_spatializer_listener_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_spatializer_listener* pListener) -{ - ma_result result; - size_t heapSizeInBytes; - void* pHeap; - - result = ma_spatializer_listener_get_heap_size(pConfig, &heapSizeInBytes); - if (result != MA_SUCCESS) { - return result; - } - - if (heapSizeInBytes > 0) { - pHeap = ma_malloc(heapSizeInBytes, pAllocationCallbacks); - if (pHeap == NULL) { - return MA_OUT_OF_MEMORY; - } - } else { - pHeap = NULL; - } - - result = ma_spatializer_listener_init_preallocated(pConfig, pHeap, pListener); - if (result != MA_SUCCESS) { - ma_free(pHeap, pAllocationCallbacks); - return result; - } - - pListener->_ownsHeap = MA_TRUE; - return MA_SUCCESS; -} - -MA_API void ma_spatializer_listener_uninit(ma_spatializer_listener* pListener, const ma_allocation_callbacks* pAllocationCallbacks) -{ - if (pListener == NULL) { - return; - } - - if (pListener->_ownsHeap) { - ma_free(pListener->_pHeap, pAllocationCallbacks); - } -} - -MA_API ma_channel* ma_spatializer_listener_get_channel_map(ma_spatializer_listener* pListener) -{ - if (pListener == NULL) { - return NULL; - } - - return pListener->config.pChannelMapOut; -} - -MA_API void ma_spatializer_listener_set_cone(ma_spatializer_listener* pListener, float innerAngleInRadians, float outerAngleInRadians, float outerGain) -{ - if (pListener == NULL) { - return; - } - - pListener->config.coneInnerAngleInRadians = innerAngleInRadians; - pListener->config.coneOuterAngleInRadians = outerAngleInRadians; - pListener->config.coneOuterGain = outerGain; -} - -MA_API void ma_spatializer_listener_get_cone(const ma_spatializer_listener* pListener, float* pInnerAngleInRadians, float* pOuterAngleInRadians, float* pOuterGain) -{ - if (pListener == NULL) { - return; - } - - if (pInnerAngleInRadians != NULL) { - *pInnerAngleInRadians = pListener->config.coneInnerAngleInRadians; - } - - if (pOuterAngleInRadians != NULL) { - *pOuterAngleInRadians = pListener->config.coneOuterAngleInRadians; - } - - if (pOuterGain != NULL) { - *pOuterGain = pListener->config.coneOuterGain; - } -} - -MA_API void ma_spatializer_listener_set_position(ma_spatializer_listener* pListener, float x, float y, float z) -{ - if (pListener == NULL) { - return; - } - - ma_atomic_vec3f_set(&pListener->position, ma_vec3f_init_3f(x, y, z)); -} - -MA_API ma_vec3f ma_spatializer_listener_get_position(const ma_spatializer_listener* pListener) -{ - if (pListener == NULL) { - return ma_vec3f_init_3f(0, 0, 0); - } - - return ma_atomic_vec3f_get((ma_atomic_vec3f*)&pListener->position); /* Naughty const-cast. It's just for atomically loading the vec3 which should be safe. */ -} - -MA_API void ma_spatializer_listener_set_direction(ma_spatializer_listener* pListener, float x, float y, float z) -{ - if (pListener == NULL) { - return; - } - - ma_atomic_vec3f_set(&pListener->direction, ma_vec3f_init_3f(x, y, z)); -} - -MA_API ma_vec3f ma_spatializer_listener_get_direction(const ma_spatializer_listener* pListener) -{ - if (pListener == NULL) { - return ma_vec3f_init_3f(0, 0, -1); - } - - return ma_atomic_vec3f_get((ma_atomic_vec3f*)&pListener->direction); /* Naughty const-cast. It's just for atomically loading the vec3 which should be safe. */ -} - -MA_API void ma_spatializer_listener_set_velocity(ma_spatializer_listener* pListener, float x, float y, float z) -{ - if (pListener == NULL) { - return; - } - - ma_atomic_vec3f_set(&pListener->velocity, ma_vec3f_init_3f(x, y, z)); -} - -MA_API ma_vec3f ma_spatializer_listener_get_velocity(const ma_spatializer_listener* pListener) -{ - if (pListener == NULL) { - return ma_vec3f_init_3f(0, 0, 0); - } - - return ma_atomic_vec3f_get((ma_atomic_vec3f*)&pListener->velocity); /* Naughty const-cast. It's just for atomically loading the vec3 which should be safe. */ -} - -MA_API void ma_spatializer_listener_set_speed_of_sound(ma_spatializer_listener* pListener, float speedOfSound) -{ - if (pListener == NULL) { - return; - } - - pListener->config.speedOfSound = speedOfSound; -} - -MA_API float ma_spatializer_listener_get_speed_of_sound(const ma_spatializer_listener* pListener) -{ - if (pListener == NULL) { - return 0; - } - - return pListener->config.speedOfSound; -} - -MA_API void ma_spatializer_listener_set_world_up(ma_spatializer_listener* pListener, float x, float y, float z) -{ - if (pListener == NULL) { - return; - } - - pListener->config.worldUp = ma_vec3f_init_3f(x, y, z); -} - -MA_API ma_vec3f ma_spatializer_listener_get_world_up(const ma_spatializer_listener* pListener) -{ - if (pListener == NULL) { - return ma_vec3f_init_3f(0, 1, 0); - } - - return pListener->config.worldUp; -} - -MA_API void ma_spatializer_listener_set_enabled(ma_spatializer_listener* pListener, ma_bool32 isEnabled) -{ - if (pListener == NULL) { - return; - } - - pListener->isEnabled = isEnabled; -} - -MA_API ma_bool32 ma_spatializer_listener_is_enabled(const ma_spatializer_listener* pListener) -{ - if (pListener == NULL) { - return MA_FALSE; - } - - return pListener->isEnabled; -} - - - - -MA_API ma_spatializer_config ma_spatializer_config_init(ma_uint32 channelsIn, ma_uint32 channelsOut) -{ - ma_spatializer_config config; - - MA_ZERO_OBJECT(&config); - config.channelsIn = channelsIn; - config.channelsOut = channelsOut; - config.pChannelMapIn = NULL; - config.attenuationModel = ma_attenuation_model_inverse; - config.positioning = ma_positioning_absolute; - config.handedness = ma_handedness_right; - config.minGain = 0; - config.maxGain = 1; - config.minDistance = 1; - config.maxDistance = MA_FLT_MAX; - config.rolloff = 1; - config.coneInnerAngleInRadians = 6.283185f; /* 360 degrees. */ - config.coneOuterAngleInRadians = 6.283185f; /* 360 degrees. */ - config.coneOuterGain = 0.0f; - config.dopplerFactor = 1; - config.directionalAttenuationFactor = 1; - config.minSpatializationChannelGain = 0.2f; - config.gainSmoothTimeInFrames = 360; /* 7.5ms @ 48K. */ - - return config; -} - - -static ma_gainer_config ma_spatializer_gainer_config_init(const ma_spatializer_config* pConfig) -{ - MA_ASSERT(pConfig != NULL); - return ma_gainer_config_init(pConfig->channelsOut, pConfig->gainSmoothTimeInFrames); -} - -static ma_result ma_spatializer_validate_config(const ma_spatializer_config* pConfig) -{ - MA_ASSERT(pConfig != NULL); - - if (pConfig->channelsIn == 0 || pConfig->channelsOut == 0) { - return MA_INVALID_ARGS; - } - - return MA_SUCCESS; -} - -typedef struct -{ - size_t sizeInBytes; - size_t channelMapInOffset; - size_t newChannelGainsOffset; - size_t gainerOffset; -} ma_spatializer_heap_layout; - -static ma_result ma_spatializer_get_heap_layout(const ma_spatializer_config* pConfig, ma_spatializer_heap_layout* pHeapLayout) -{ - ma_result result; - - MA_ASSERT(pHeapLayout != NULL); - - MA_ZERO_OBJECT(pHeapLayout); - - if (pConfig == NULL) { - return MA_INVALID_ARGS; - } - - result = ma_spatializer_validate_config(pConfig); - if (result != MA_SUCCESS) { - return result; - } - - pHeapLayout->sizeInBytes = 0; - - /* Channel map. */ - pHeapLayout->channelMapInOffset = MA_SIZE_MAX; /* <-- MA_SIZE_MAX indicates no allocation necessary. */ - if (pConfig->pChannelMapIn != NULL) { - pHeapLayout->channelMapInOffset = pHeapLayout->sizeInBytes; - pHeapLayout->sizeInBytes += ma_align_64(sizeof(*pConfig->pChannelMapIn) * pConfig->channelsIn); - } - - /* New channel gains for output. */ - pHeapLayout->newChannelGainsOffset = pHeapLayout->sizeInBytes; - pHeapLayout->sizeInBytes += ma_align_64(sizeof(float) * pConfig->channelsOut); - - /* Gainer. */ - { - size_t gainerHeapSizeInBytes; - ma_gainer_config gainerConfig; - - gainerConfig = ma_spatializer_gainer_config_init(pConfig); - - result = ma_gainer_get_heap_size(&gainerConfig, &gainerHeapSizeInBytes); - if (result != MA_SUCCESS) { - return result; - } - - pHeapLayout->gainerOffset = pHeapLayout->sizeInBytes; - pHeapLayout->sizeInBytes += ma_align_64(gainerHeapSizeInBytes); - } - - return MA_SUCCESS; -} - -MA_API ma_result ma_spatializer_get_heap_size(const ma_spatializer_config* pConfig, size_t* pHeapSizeInBytes) -{ - ma_result result; - ma_spatializer_heap_layout heapLayout; - - if (pHeapSizeInBytes == NULL) { - return MA_INVALID_ARGS; - } - - *pHeapSizeInBytes = 0; /* Safety. */ - - result = ma_spatializer_get_heap_layout(pConfig, &heapLayout); - if (result != MA_SUCCESS) { - return result; - } - - *pHeapSizeInBytes = heapLayout.sizeInBytes; - - return MA_SUCCESS; -} - - -MA_API ma_result ma_spatializer_init_preallocated(const ma_spatializer_config* pConfig, void* pHeap, ma_spatializer* pSpatializer) -{ - ma_result result; - ma_spatializer_heap_layout heapLayout; - ma_gainer_config gainerConfig; - - if (pSpatializer == NULL) { - return MA_INVALID_ARGS; - } - - MA_ZERO_OBJECT(pSpatializer); - - if (pConfig == NULL || pHeap == NULL) { - return MA_INVALID_ARGS; - } - - result = ma_spatializer_get_heap_layout(pConfig, &heapLayout); - if (result != MA_SUCCESS) { - return result; - } - - pSpatializer->_pHeap = pHeap; - MA_ZERO_MEMORY(pHeap, heapLayout.sizeInBytes); - - pSpatializer->channelsIn = pConfig->channelsIn; - pSpatializer->channelsOut = pConfig->channelsOut; - pSpatializer->attenuationModel = pConfig->attenuationModel; - pSpatializer->positioning = pConfig->positioning; - pSpatializer->handedness = pConfig->handedness; - pSpatializer->minGain = pConfig->minGain; - pSpatializer->maxGain = pConfig->maxGain; - pSpatializer->minDistance = pConfig->minDistance; - pSpatializer->maxDistance = pConfig->maxDistance; - pSpatializer->rolloff = pConfig->rolloff; - pSpatializer->coneInnerAngleInRadians = pConfig->coneInnerAngleInRadians; - pSpatializer->coneOuterAngleInRadians = pConfig->coneOuterAngleInRadians; - pSpatializer->coneOuterGain = pConfig->coneOuterGain; - pSpatializer->dopplerFactor = pConfig->dopplerFactor; - pSpatializer->minSpatializationChannelGain = pConfig->minSpatializationChannelGain; - pSpatializer->directionalAttenuationFactor = pConfig->directionalAttenuationFactor; - pSpatializer->gainSmoothTimeInFrames = pConfig->gainSmoothTimeInFrames; - ma_atomic_vec3f_init(&pSpatializer->position, ma_vec3f_init_3f(0, 0, 0)); - ma_atomic_vec3f_init(&pSpatializer->direction, ma_vec3f_init_3f(0, 0, -1)); - ma_atomic_vec3f_init(&pSpatializer->velocity, ma_vec3f_init_3f(0, 0, 0)); - pSpatializer->dopplerPitch = 1; - - /* Swap the forward direction if we're left handed (it was initialized based on right handed). */ - if (pSpatializer->handedness == ma_handedness_left) { - ma_vec3f negDir = ma_vec3f_neg(ma_spatializer_get_direction(pSpatializer)); - ma_spatializer_set_direction(pSpatializer, negDir.x, negDir.y, negDir.z); - } - - /* Channel map. This will be on the heap. */ - if (pConfig->pChannelMapIn != NULL) { - pSpatializer->pChannelMapIn = (ma_channel*)ma_offset_ptr(pHeap, heapLayout.channelMapInOffset); - ma_channel_map_copy_or_default(pSpatializer->pChannelMapIn, pSpatializer->channelsIn, pConfig->pChannelMapIn, pSpatializer->channelsIn); - } - - /* New channel gains for output channels. */ - pSpatializer->pNewChannelGainsOut = (float*)ma_offset_ptr(pHeap, heapLayout.newChannelGainsOffset); - - /* Gainer. */ - gainerConfig = ma_spatializer_gainer_config_init(pConfig); - - result = ma_gainer_init_preallocated(&gainerConfig, ma_offset_ptr(pHeap, heapLayout.gainerOffset), &pSpatializer->gainer); - if (result != MA_SUCCESS) { - return result; /* Failed to initialize the gainer. */ - } - - return MA_SUCCESS; -} - -MA_API ma_result ma_spatializer_init(const ma_spatializer_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_spatializer* pSpatializer) -{ - ma_result result; - size_t heapSizeInBytes; - void* pHeap; - - /* We'll need a heap allocation to retrieve the size. */ - result = ma_spatializer_get_heap_size(pConfig, &heapSizeInBytes); - if (result != MA_SUCCESS) { - return result; - } - - if (heapSizeInBytes > 0) { - pHeap = ma_malloc(heapSizeInBytes, pAllocationCallbacks); - if (pHeap == NULL) { - return MA_OUT_OF_MEMORY; - } - } else { - pHeap = NULL; - } - - result = ma_spatializer_init_preallocated(pConfig, pHeap, pSpatializer); - if (result != MA_SUCCESS) { - ma_free(pHeap, pAllocationCallbacks); - return result; - } - - pSpatializer->_ownsHeap = MA_TRUE; - return MA_SUCCESS; -} - -MA_API void ma_spatializer_uninit(ma_spatializer* pSpatializer, const ma_allocation_callbacks* pAllocationCallbacks) -{ - if (pSpatializer == NULL) { - return; - } - - ma_gainer_uninit(&pSpatializer->gainer, pAllocationCallbacks); - - if (pSpatializer->_ownsHeap) { - ma_free(pSpatializer->_pHeap, pAllocationCallbacks); - } -} - -static float ma_calculate_angular_gain(ma_vec3f dirA, ma_vec3f dirB, float coneInnerAngleInRadians, float coneOuterAngleInRadians, float coneOuterGain) -{ - /* - Angular attenuation. - - Unlike distance gain, the math for this is not specified by the OpenAL spec so we'll just go ahead and figure - this out for ourselves at the expense of possibly being inconsistent with other implementations. - - To do cone attenuation, I'm just using the same math that we'd use to implement a basic spotlight in OpenGL. We - just need to get the direction from the source to the listener and then do a dot product against that and the - direction of the spotlight. Then we just compare that dot product against the cosine of the inner and outer - angles. If the dot product is greater than the outer angle, we just use coneOuterGain. If it's less than - the inner angle, we just use a gain of 1. Otherwise we linearly interpolate between 1 and coneOuterGain. - */ - if (coneInnerAngleInRadians < 6.283185f) { - float angularGain = 1; - float cutoffInner = (float)ma_cosd(coneInnerAngleInRadians*0.5f); - float cutoffOuter = (float)ma_cosd(coneOuterAngleInRadians*0.5f); - float d; - - d = ma_vec3f_dot(dirA, dirB); - - if (d > cutoffInner) { - /* It's inside the inner angle. */ - angularGain = 1; - } else { - /* It's outside the inner angle. */ - if (d > cutoffOuter) { - /* It's between the inner and outer angle. We need to linearly interpolate between 1 and coneOuterGain. */ - angularGain = ma_mix_f32(coneOuterGain, 1, (d - cutoffOuter) / (cutoffInner - cutoffOuter)); - } else { - /* It's outside the outer angle. */ - angularGain = coneOuterGain; - } - } - - /*printf("d = %f; cutoffInner = %f; cutoffOuter = %f; angularGain = %f\n", d, cutoffInner, cutoffOuter, angularGain);*/ - return angularGain; - } else { - /* Inner angle is 360 degrees so no need to do any attenuation. */ - return 1; - } -} - -MA_API ma_result ma_spatializer_process_pcm_frames(ma_spatializer* pSpatializer, ma_spatializer_listener* pListener, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount) -{ - ma_channel* pChannelMapIn = pSpatializer->pChannelMapIn; - ma_channel* pChannelMapOut = pListener->config.pChannelMapOut; - - if (pSpatializer == NULL) { - return MA_INVALID_ARGS; - } - - /* If we're not spatializing we need to run an optimized path. */ - if (ma_atomic_load_i32(&pSpatializer->attenuationModel) == ma_attenuation_model_none) { - if (ma_spatializer_listener_is_enabled(pListener)) { - /* No attenuation is required, but we'll need to do some channel conversion. */ - if (pSpatializer->channelsIn == pSpatializer->channelsOut) { - ma_copy_pcm_frames(pFramesOut, pFramesIn, frameCount, ma_format_f32, pSpatializer->channelsIn); - } else { - ma_channel_map_apply_f32((float*)pFramesOut, pChannelMapOut, pSpatializer->channelsOut, (const float*)pFramesIn, pChannelMapIn, pSpatializer->channelsIn, frameCount, ma_channel_mix_mode_rectangular, ma_mono_expansion_mode_default); /* Safe casts to float* because f32 is the only supported format. */ - } - } else { - /* The listener is disabled. Output silence. */ - ma_silence_pcm_frames(pFramesOut, frameCount, ma_format_f32, pSpatializer->channelsOut); - } - - /* - We're not doing attenuation so don't bother with doppler for now. I'm not sure if this is - the correct thinking so might need to review this later. - */ - pSpatializer->dopplerPitch = 1; - } else { - /* - Let's first determine which listener the sound is closest to. Need to keep in mind that we - might not have a world or any listeners, in which case we just spatializer based on the - listener being positioned at the origin (0, 0, 0). - */ - ma_vec3f relativePosNormalized; - ma_vec3f relativePos; /* The position relative to the listener. */ - ma_vec3f relativeDir; /* The direction of the sound, relative to the listener. */ - ma_vec3f listenerVel; /* The velocity of the listener. For doppler pitch calculation. */ - float speedOfSound; - float distance = 0; - float gain = 1; - ma_uint32 iChannel; - const ma_uint32 channelsOut = pSpatializer->channelsOut; - const ma_uint32 channelsIn = pSpatializer->channelsIn; - float minDistance = ma_spatializer_get_min_distance(pSpatializer); - float maxDistance = ma_spatializer_get_max_distance(pSpatializer); - float rolloff = ma_spatializer_get_rolloff(pSpatializer); - float dopplerFactor = ma_spatializer_get_doppler_factor(pSpatializer); - - /* - We'll need the listener velocity for doppler pitch calculations. The speed of sound is - defined by the listener, so we'll grab that here too. - */ - if (pListener != NULL) { - listenerVel = ma_spatializer_listener_get_velocity(pListener); - speedOfSound = pListener->config.speedOfSound; - } else { - listenerVel = ma_vec3f_init_3f(0, 0, 0); - speedOfSound = MA_DEFAULT_SPEED_OF_SOUND; - } - - if (pListener == NULL || ma_spatializer_get_positioning(pSpatializer) == ma_positioning_relative) { - /* There's no listener or we're using relative positioning. */ - relativePos = ma_spatializer_get_position(pSpatializer); - relativeDir = ma_spatializer_get_direction(pSpatializer); - } else { - /* - We've found a listener and we're using absolute positioning. We need to transform the - sound's position and direction so that it's relative to listener. Later on we'll use - this for determining the factors to apply to each channel to apply the panning effect. - */ - ma_spatializer_get_relative_position_and_direction(pSpatializer, pListener, &relativePos, &relativeDir); - } - - distance = ma_vec3f_len(relativePos); - - /* We've gathered the data, so now we can apply some spatialization. */ - switch (ma_spatializer_get_attenuation_model(pSpatializer)) { - case ma_attenuation_model_inverse: - { - gain = ma_attenuation_inverse(distance, minDistance, maxDistance, rolloff); - } break; - case ma_attenuation_model_linear: - { - gain = ma_attenuation_linear(distance, minDistance, maxDistance, rolloff); - } break; - case ma_attenuation_model_exponential: - { - gain = ma_attenuation_exponential(distance, minDistance, maxDistance, rolloff); - } break; - case ma_attenuation_model_none: - default: - { - gain = 1; - } break; - } - - /* Normalize the position. */ - if (distance > 0.001f) { - float distanceInv = 1/distance; - relativePosNormalized = relativePos; - relativePosNormalized.x *= distanceInv; - relativePosNormalized.y *= distanceInv; - relativePosNormalized.z *= distanceInv; - } else { - distance = 0; - relativePosNormalized = ma_vec3f_init_3f(0, 0, 0); - } - - /* - Angular attenuation. - - Unlike distance gain, the math for this is not specified by the OpenAL spec so we'll just go ahead and figure - this out for ourselves at the expense of possibly being inconsistent with other implementations. - - To do cone attenuation, I'm just using the same math that we'd use to implement a basic spotlight in OpenGL. We - just need to get the direction from the source to the listener and then do a dot product against that and the - direction of the spotlight. Then we just compare that dot product against the cosine of the inner and outer - angles. If the dot product is greater than the outer angle, we just use coneOuterGain. If it's less than - the inner angle, we just use a gain of 1. Otherwise we linearly interpolate between 1 and coneOuterGain. - */ - if (distance > 0) { - /* Source angular gain. */ - float spatializerConeInnerAngle; - float spatializerConeOuterAngle; - float spatializerConeOuterGain; - ma_spatializer_get_cone(pSpatializer, &spatializerConeInnerAngle, &spatializerConeOuterAngle, &spatializerConeOuterGain); - - gain *= ma_calculate_angular_gain(relativeDir, ma_vec3f_neg(relativePosNormalized), spatializerConeInnerAngle, spatializerConeOuterAngle, spatializerConeOuterGain); - - /* - We're supporting angular gain on the listener as well for those who want to reduce the volume of sounds that - are positioned behind the listener. On default settings, this will have no effect. - */ - if (pListener != NULL && pListener->config.coneInnerAngleInRadians < 6.283185f) { - ma_vec3f listenerDirection; - float listenerInnerAngle; - float listenerOuterAngle; - float listenerOuterGain; - - if (pListener->config.handedness == ma_handedness_right) { - listenerDirection = ma_vec3f_init_3f(0, 0, -1); - } else { - listenerDirection = ma_vec3f_init_3f(0, 0, +1); - } - - listenerInnerAngle = pListener->config.coneInnerAngleInRadians; - listenerOuterAngle = pListener->config.coneOuterAngleInRadians; - listenerOuterGain = pListener->config.coneOuterGain; - - gain *= ma_calculate_angular_gain(listenerDirection, relativePosNormalized, listenerInnerAngle, listenerOuterAngle, listenerOuterGain); - } - } else { - /* The sound is right on top of the listener. Don't do any angular attenuation. */ - } - - - /* Clamp the gain. */ - gain = ma_clamp(gain, ma_spatializer_get_min_gain(pSpatializer), ma_spatializer_get_max_gain(pSpatializer)); - - /* - The gain needs to be applied per-channel here. The spatialization code below will be changing the per-channel - gains which will then eventually be passed into the gainer which will deal with smoothing the gain transitions - to avoid harsh changes in gain. - */ - for (iChannel = 0; iChannel < channelsOut; iChannel += 1) { - pSpatializer->pNewChannelGainsOut[iChannel] = gain; - } - - /* - Convert to our output channel count. If the listener is disabled we just output silence here. We cannot ignore - the whole section of code here because we need to update some internal spatialization state. - */ - if (ma_spatializer_listener_is_enabled(pListener)) { - ma_channel_map_apply_f32((float*)pFramesOut, pChannelMapOut, channelsOut, (const float*)pFramesIn, pChannelMapIn, channelsIn, frameCount, ma_channel_mix_mode_rectangular, ma_mono_expansion_mode_default); - } else { - ma_silence_pcm_frames(pFramesOut, frameCount, ma_format_f32, pSpatializer->channelsOut); - } - - - /* - Panning. This is where we'll apply the gain and convert to the output channel count. We have an optimized path for - when we're converting to a mono stream. In that case we don't really need to do any panning - we just apply the - gain to the final output. - */ - /*printf("distance=%f; gain=%f\n", distance, gain);*/ - - /* We must have a valid channel map here to ensure we spatialize properly. */ - MA_ASSERT(pChannelMapOut != NULL); - - /* - We're not converting to mono so we'll want to apply some panning. This is where the feeling of something being - to the left, right, infront or behind the listener is calculated. I'm just using a basic model here. Note that - the code below is not based on any specific algorithm. I'm just implementing this off the top of my head and - seeing how it goes. There might be better ways to do this. - - To determine the direction of the sound relative to a speaker I'm using dot products. Each speaker is given a - direction. For example, the left channel in a stereo system will be -1 on the X axis and the right channel will - be +1 on the X axis. A dot product is performed against the direction vector of the channel and the normalized - position of the sound. - */ - - /* - Calculate our per-channel gains. We do this based on the normalized relative position of the sound and it's - relation to the direction of the channel. - */ - if (distance > 0) { - ma_vec3f unitPos = relativePos; - float distanceInv = 1/distance; - unitPos.x *= distanceInv; - unitPos.y *= distanceInv; - unitPos.z *= distanceInv; - - for (iChannel = 0; iChannel < channelsOut; iChannel += 1) { - ma_channel channelOut; - float d; - float dMin; - - channelOut = ma_channel_map_get_channel(pChannelMapOut, channelsOut, iChannel); - if (ma_is_spatial_channel_position(channelOut)) { - d = ma_mix_f32_fast(1, ma_vec3f_dot(unitPos, ma_get_channel_direction(channelOut)), ma_spatializer_get_directional_attenuation_factor(pSpatializer)); - } else { - d = 1; /* It's not a spatial channel so there's no real notion of direction. */ - } - - /* - In my testing, if the panning effect is too aggressive it makes spatialization feel uncomfortable. - The "dMin" variable below is used to control the aggressiveness of the panning effect. When set to - 0, panning will be most extreme and any sounds that are positioned on the opposite side of the - speaker will be completely silent from that speaker. Not only does this feel uncomfortable, it - doesn't even remotely represent the real world at all because sounds that come from your right side - are still clearly audible from your left side. Setting "dMin" to 1 will result in no panning at - all, which is also not ideal. By setting it to something greater than 0, the spatialization effect - becomes much less dramatic and a lot more bearable. - - Summary: 0 = more extreme panning; 1 = no panning. - */ - dMin = pSpatializer->minSpatializationChannelGain; - - /* - At this point, "d" will be positive if the sound is on the same side as the channel and negative if - it's on the opposite side. It will be in the range of -1..1. There's two ways I can think of to - calculate a panning value. The first is to simply convert it to 0..1, however this has a problem - which I'm not entirely happy with. Considering a stereo system, when a sound is positioned right - in front of the listener it'll result in each speaker getting a gain of 0.5. I don't know if I like - the idea of having a scaling factor of 0.5 being applied to a sound when it's sitting right in front - of the listener. I would intuitively expect that to be played at full volume, or close to it. - - The second idea I think of is to only apply a reduction in gain when the sound is on the opposite - side of the speaker. That is, reduce the gain only when the dot product is negative. The problem - with this is that there will not be any attenuation as the sound sweeps around the 180 degrees - where the dot product is positive. The idea with this option is that you leave the gain at 1 when - the sound is being played on the same side as the speaker and then you just reduce the volume when - the sound is on the other side. - - The summarize, I think the first option should give a better sense of spatialization, but the second - option is better for preserving the sound's power. - - UPDATE: In my testing, I find the first option to sound better. You can feel the sense of space a - bit better, but you can also hear the reduction in volume when it's right in front. - */ - #if 1 - { - /* - Scale the dot product from -1..1 to 0..1. Will result in a sound directly in front losing power - by being played at 0.5 gain. - */ - d = (d + 1) * 0.5f; /* -1..1 to 0..1 */ - d = ma_max(d, dMin); - pSpatializer->pNewChannelGainsOut[iChannel] *= d; - } - #else - { - /* - Only reduce the volume of the sound if it's on the opposite side. This path keeps the volume more - consistent, but comes at the expense of a worse sense of space and positioning. - */ - if (d < 0) { - d += 1; /* Move into the positive range. */ - d = ma_max(d, dMin); - channelGainsOut[iChannel] *= d; - } - } - #endif - } - } else { - /* Assume the sound is right on top of us. Don't do any panning. */ - } - - /* Now we need to apply the volume to each channel. This needs to run through the gainer to ensure we get a smooth volume transition. */ - ma_gainer_set_gains(&pSpatializer->gainer, pSpatializer->pNewChannelGainsOut); - ma_gainer_process_pcm_frames(&pSpatializer->gainer, pFramesOut, pFramesOut, frameCount); - - /* - Before leaving we'll want to update our doppler pitch so that the caller can apply some - pitch shifting if they desire. Note that we need to negate the relative position here - because the doppler calculation needs to be source-to-listener, but ours is listener-to- - source. - */ - if (dopplerFactor > 0) { - pSpatializer->dopplerPitch = ma_doppler_pitch(ma_vec3f_sub(ma_spatializer_listener_get_position(pListener), ma_spatializer_get_position(pSpatializer)), ma_spatializer_get_velocity(pSpatializer), listenerVel, speedOfSound, dopplerFactor); - } else { - pSpatializer->dopplerPitch = 1; - } - } - - return MA_SUCCESS; -} - -MA_API ma_result ma_spatializer_set_master_volume(ma_spatializer* pSpatializer, float volume) -{ - if (pSpatializer == NULL) { - return MA_INVALID_ARGS; - } - - return ma_gainer_set_master_volume(&pSpatializer->gainer, volume); -} - -MA_API ma_result ma_spatializer_get_master_volume(const ma_spatializer* pSpatializer, float* pVolume) -{ - if (pSpatializer == NULL) { - return MA_INVALID_ARGS; - } - - return ma_gainer_get_master_volume(&pSpatializer->gainer, pVolume); -} - -MA_API ma_uint32 ma_spatializer_get_input_channels(const ma_spatializer* pSpatializer) -{ - if (pSpatializer == NULL) { - return 0; - } - - return pSpatializer->channelsIn; -} - -MA_API ma_uint32 ma_spatializer_get_output_channels(const ma_spatializer* pSpatializer) -{ - if (pSpatializer == NULL) { - return 0; - } - - return pSpatializer->channelsOut; -} - -MA_API void ma_spatializer_set_attenuation_model(ma_spatializer* pSpatializer, ma_attenuation_model attenuationModel) -{ - if (pSpatializer == NULL) { - return; - } - - ma_atomic_exchange_i32(&pSpatializer->attenuationModel, attenuationModel); -} - -MA_API ma_attenuation_model ma_spatializer_get_attenuation_model(const ma_spatializer* pSpatializer) -{ - if (pSpatializer == NULL) { - return ma_attenuation_model_none; - } - - return (ma_attenuation_model)ma_atomic_load_i32(&pSpatializer->attenuationModel); -} - -MA_API void ma_spatializer_set_positioning(ma_spatializer* pSpatializer, ma_positioning positioning) -{ - if (pSpatializer == NULL) { - return; - } - - ma_atomic_exchange_i32(&pSpatializer->positioning, positioning); -} - -MA_API ma_positioning ma_spatializer_get_positioning(const ma_spatializer* pSpatializer) -{ - if (pSpatializer == NULL) { - return ma_positioning_absolute; - } - - return (ma_positioning)ma_atomic_load_i32(&pSpatializer->positioning); -} - -MA_API void ma_spatializer_set_rolloff(ma_spatializer* pSpatializer, float rolloff) -{ - if (pSpatializer == NULL) { - return; - } - - ma_atomic_exchange_f32(&pSpatializer->rolloff, rolloff); -} - -MA_API float ma_spatializer_get_rolloff(const ma_spatializer* pSpatializer) -{ - if (pSpatializer == NULL) { - return 0; - } - - return ma_atomic_load_f32(&pSpatializer->rolloff); -} - -MA_API void ma_spatializer_set_min_gain(ma_spatializer* pSpatializer, float minGain) -{ - if (pSpatializer == NULL) { - return; - } - - ma_atomic_exchange_f32(&pSpatializer->minGain, minGain); -} - -MA_API float ma_spatializer_get_min_gain(const ma_spatializer* pSpatializer) -{ - if (pSpatializer == NULL) { - return 0; - } - - return ma_atomic_load_f32(&pSpatializer->minGain); -} - -MA_API void ma_spatializer_set_max_gain(ma_spatializer* pSpatializer, float maxGain) -{ - if (pSpatializer == NULL) { - return; - } - - ma_atomic_exchange_f32(&pSpatializer->maxGain, maxGain); -} - -MA_API float ma_spatializer_get_max_gain(const ma_spatializer* pSpatializer) -{ - if (pSpatializer == NULL) { - return 0; - } - - return ma_atomic_load_f32(&pSpatializer->maxGain); -} - -MA_API void ma_spatializer_set_min_distance(ma_spatializer* pSpatializer, float minDistance) -{ - if (pSpatializer == NULL) { - return; - } - - ma_atomic_exchange_f32(&pSpatializer->minDistance, minDistance); -} - -MA_API float ma_spatializer_get_min_distance(const ma_spatializer* pSpatializer) -{ - if (pSpatializer == NULL) { - return 0; - } - - return ma_atomic_load_f32(&pSpatializer->minDistance); -} - -MA_API void ma_spatializer_set_max_distance(ma_spatializer* pSpatializer, float maxDistance) -{ - if (pSpatializer == NULL) { - return; - } - - ma_atomic_exchange_f32(&pSpatializer->maxDistance, maxDistance); -} - -MA_API float ma_spatializer_get_max_distance(const ma_spatializer* pSpatializer) -{ - if (pSpatializer == NULL) { - return 0; - } - - return ma_atomic_load_f32(&pSpatializer->maxDistance); -} - -MA_API void ma_spatializer_set_cone(ma_spatializer* pSpatializer, float innerAngleInRadians, float outerAngleInRadians, float outerGain) -{ - if (pSpatializer == NULL) { - return; - } - - ma_atomic_exchange_f32(&pSpatializer->coneInnerAngleInRadians, innerAngleInRadians); - ma_atomic_exchange_f32(&pSpatializer->coneOuterAngleInRadians, outerAngleInRadians); - ma_atomic_exchange_f32(&pSpatializer->coneOuterGain, outerGain); -} - -MA_API void ma_spatializer_get_cone(const ma_spatializer* pSpatializer, float* pInnerAngleInRadians, float* pOuterAngleInRadians, float* pOuterGain) -{ - if (pSpatializer == NULL) { - return; - } - - if (pInnerAngleInRadians != NULL) { - *pInnerAngleInRadians = ma_atomic_load_f32(&pSpatializer->coneInnerAngleInRadians); - } - - if (pOuterAngleInRadians != NULL) { - *pOuterAngleInRadians = ma_atomic_load_f32(&pSpatializer->coneOuterAngleInRadians); - } - - if (pOuterGain != NULL) { - *pOuterGain = ma_atomic_load_f32(&pSpatializer->coneOuterGain); - } -} - -MA_API void ma_spatializer_set_doppler_factor(ma_spatializer* pSpatializer, float dopplerFactor) -{ - if (pSpatializer == NULL) { - return; - } - - ma_atomic_exchange_f32(&pSpatializer->dopplerFactor, dopplerFactor); -} - -MA_API float ma_spatializer_get_doppler_factor(const ma_spatializer* pSpatializer) -{ - if (pSpatializer == NULL) { - return 1; - } - - return ma_atomic_load_f32(&pSpatializer->dopplerFactor); -} - -MA_API void ma_spatializer_set_directional_attenuation_factor(ma_spatializer* pSpatializer, float directionalAttenuationFactor) -{ - if (pSpatializer == NULL) { - return; - } - - ma_atomic_exchange_f32(&pSpatializer->directionalAttenuationFactor, directionalAttenuationFactor); -} - -MA_API float ma_spatializer_get_directional_attenuation_factor(const ma_spatializer* pSpatializer) -{ - if (pSpatializer == NULL) { - return 1; - } - - return ma_atomic_load_f32(&pSpatializer->directionalAttenuationFactor); -} - -MA_API void ma_spatializer_set_position(ma_spatializer* pSpatializer, float x, float y, float z) -{ - if (pSpatializer == NULL) { - return; - } - - ma_atomic_vec3f_set(&pSpatializer->position, ma_vec3f_init_3f(x, y, z)); -} - -MA_API ma_vec3f ma_spatializer_get_position(const ma_spatializer* pSpatializer) -{ - if (pSpatializer == NULL) { - return ma_vec3f_init_3f(0, 0, 0); - } - - return ma_atomic_vec3f_get((ma_atomic_vec3f*)&pSpatializer->position); /* Naughty const-cast. It's just for atomically loading the vec3 which should be safe. */ -} - -MA_API void ma_spatializer_set_direction(ma_spatializer* pSpatializer, float x, float y, float z) -{ - if (pSpatializer == NULL) { - return; - } - - ma_atomic_vec3f_set(&pSpatializer->direction, ma_vec3f_init_3f(x, y, z)); -} - -MA_API ma_vec3f ma_spatializer_get_direction(const ma_spatializer* pSpatializer) -{ - if (pSpatializer == NULL) { - return ma_vec3f_init_3f(0, 0, -1); - } - - return ma_atomic_vec3f_get((ma_atomic_vec3f*)&pSpatializer->direction); /* Naughty const-cast. It's just for atomically loading the vec3 which should be safe. */ -} - -MA_API void ma_spatializer_set_velocity(ma_spatializer* pSpatializer, float x, float y, float z) -{ - if (pSpatializer == NULL) { - return; - } - - ma_atomic_vec3f_set(&pSpatializer->velocity, ma_vec3f_init_3f(x, y, z)); -} - -MA_API ma_vec3f ma_spatializer_get_velocity(const ma_spatializer* pSpatializer) -{ - if (pSpatializer == NULL) { - return ma_vec3f_init_3f(0, 0, 0); - } - - return ma_atomic_vec3f_get((ma_atomic_vec3f*)&pSpatializer->velocity); /* Naughty const-cast. It's just for atomically loading the vec3 which should be safe. */ -} - -MA_API void ma_spatializer_get_relative_position_and_direction(const ma_spatializer* pSpatializer, const ma_spatializer_listener* pListener, ma_vec3f* pRelativePos, ma_vec3f* pRelativeDir) -{ - if (pRelativePos != NULL) { - pRelativePos->x = 0; - pRelativePos->y = 0; - pRelativePos->z = 0; - } - - if (pRelativeDir != NULL) { - pRelativeDir->x = 0; - pRelativeDir->y = 0; - pRelativeDir->z = -1; - } - - if (pSpatializer == NULL) { - return; - } - - if (pListener == NULL || ma_spatializer_get_positioning(pSpatializer) == ma_positioning_relative) { - /* There's no listener or we're using relative positioning. */ - if (pRelativePos != NULL) { - *pRelativePos = ma_spatializer_get_position(pSpatializer); - } - if (pRelativeDir != NULL) { - *pRelativeDir = ma_spatializer_get_direction(pSpatializer); - } - } else { - ma_vec3f spatializerPosition; - ma_vec3f spatializerDirection; - ma_vec3f listenerPosition; - ma_vec3f listenerDirection; - ma_vec3f v; - ma_vec3f axisX; - ma_vec3f axisY; - ma_vec3f axisZ; - float m[4][4]; - - spatializerPosition = ma_spatializer_get_position(pSpatializer); - spatializerDirection = ma_spatializer_get_direction(pSpatializer); - listenerPosition = ma_spatializer_listener_get_position(pListener); - listenerDirection = ma_spatializer_listener_get_direction(pListener); - - /* - We need to calculate the right vector from our forward and up vectors. This is done with - a cross product. - */ - axisZ = ma_vec3f_normalize(listenerDirection); /* Normalization required here because we can't trust the caller. */ - axisX = ma_vec3f_normalize(ma_vec3f_cross(axisZ, pListener->config.worldUp)); /* Normalization required here because the world up vector may not be perpendicular with the forward vector. */ - - /* - The calculation of axisX above can result in a zero-length vector if the listener is - looking straight up on the Y axis. We'll need to fall back to a +X in this case so that - the calculations below don't fall apart. This is where a quaternion based listener and - sound orientation would come in handy. - */ - if (ma_vec3f_len2(axisX) == 0) { - axisX = ma_vec3f_init_3f(1, 0, 0); - } - - axisY = ma_vec3f_cross(axisX, axisZ); /* No normalization is required here because axisX and axisZ are unit length and perpendicular. */ - - /* - We need to swap the X axis if we're left handed because otherwise the cross product above - will have resulted in it pointing in the wrong direction (right handed was assumed in the - cross products above). - */ - if (pListener->config.handedness == ma_handedness_left) { - axisX = ma_vec3f_neg(axisX); - } - - /* Lookat. */ - m[0][0] = axisX.x; m[1][0] = axisX.y; m[2][0] = axisX.z; m[3][0] = -ma_vec3f_dot(axisX, listenerPosition); - m[0][1] = axisY.x; m[1][1] = axisY.y; m[2][1] = axisY.z; m[3][1] = -ma_vec3f_dot(axisY, listenerPosition); - m[0][2] = -axisZ.x; m[1][2] = -axisZ.y; m[2][2] = -axisZ.z; m[3][2] = -ma_vec3f_dot(ma_vec3f_neg(axisZ), listenerPosition); - m[0][3] = 0; m[1][3] = 0; m[2][3] = 0; m[3][3] = 1; - - /* - Multiply the lookat matrix by the spatializer position to transform it to listener - space. This allows calculations to work based on the sound being relative to the - origin which makes things simpler. - */ - if (pRelativePos != NULL) { - v = spatializerPosition; - pRelativePos->x = m[0][0] * v.x + m[1][0] * v.y + m[2][0] * v.z + m[3][0] * 1; - pRelativePos->y = m[0][1] * v.x + m[1][1] * v.y + m[2][1] * v.z + m[3][1] * 1; - pRelativePos->z = m[0][2] * v.x + m[1][2] * v.y + m[2][2] * v.z + m[3][2] * 1; - } - - /* - The direction of the sound needs to also be transformed so that it's relative to the - rotation of the listener. - */ - if (pRelativeDir != NULL) { - v = spatializerDirection; - pRelativeDir->x = m[0][0] * v.x + m[1][0] * v.y + m[2][0] * v.z; - pRelativeDir->y = m[0][1] * v.x + m[1][1] * v.y + m[2][1] * v.z; - pRelativeDir->z = m[0][2] * v.x + m[1][2] * v.y + m[2][2] * v.z; - } - } -} - - - - -/************************************************************************************************************************************************************** - -Resampling - -**************************************************************************************************************************************************************/ -MA_API ma_linear_resampler_config ma_linear_resampler_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRateIn, ma_uint32 sampleRateOut) -{ - ma_linear_resampler_config config; - MA_ZERO_OBJECT(&config); - config.format = format; - config.channels = channels; - config.sampleRateIn = sampleRateIn; - config.sampleRateOut = sampleRateOut; - config.lpfOrder = ma_min(MA_DEFAULT_RESAMPLER_LPF_ORDER, MA_MAX_FILTER_ORDER); - config.lpfNyquistFactor = 1; - - return config; -} - - -typedef struct -{ - size_t sizeInBytes; - size_t x0Offset; - size_t x1Offset; - size_t lpfOffset; -} ma_linear_resampler_heap_layout; - - -static void ma_linear_resampler_adjust_timer_for_new_rate(ma_linear_resampler* pResampler, ma_uint32 oldSampleRateOut, ma_uint32 newSampleRateOut) -{ - /* - So what's happening here? Basically we need to adjust the fractional component of the time advance based on the new rate. The old time advance will - be based on the old sample rate, but we are needing to adjust it to that it's based on the new sample rate. - */ - ma_uint32 oldRateTimeWhole = pResampler->inTimeFrac / oldSampleRateOut; /* <-- This should almost never be anything other than 0, but leaving it here to make this more general and robust just in case. */ - ma_uint32 oldRateTimeFract = pResampler->inTimeFrac % oldSampleRateOut; - - pResampler->inTimeFrac = - (oldRateTimeWhole * newSampleRateOut) + - ((oldRateTimeFract * newSampleRateOut) / oldSampleRateOut); - - /* Make sure the fractional part is less than the output sample rate. */ - pResampler->inTimeInt += pResampler->inTimeFrac / pResampler->config.sampleRateOut; - pResampler->inTimeFrac = pResampler->inTimeFrac % pResampler->config.sampleRateOut; -} - -static ma_result ma_linear_resampler_set_rate_internal(ma_linear_resampler* pResampler, void* pHeap, ma_linear_resampler_heap_layout* pHeapLayout, ma_uint32 sampleRateIn, ma_uint32 sampleRateOut, ma_bool32 isResamplerAlreadyInitialized) -{ - ma_result result; - ma_uint32 gcf; - ma_uint32 lpfSampleRate; - double lpfCutoffFrequency; - ma_lpf_config lpfConfig; - ma_uint32 oldSampleRateOut; /* Required for adjusting time advance down the bottom. */ - - if (pResampler == NULL) { - return MA_INVALID_ARGS; - } - - if (sampleRateIn == 0 || sampleRateOut == 0) { - return MA_INVALID_ARGS; - } - - oldSampleRateOut = pResampler->config.sampleRateOut; - - pResampler->config.sampleRateIn = sampleRateIn; - pResampler->config.sampleRateOut = sampleRateOut; - - /* Simplify the sample rate. */ - gcf = ma_gcf_u32(pResampler->config.sampleRateIn, pResampler->config.sampleRateOut); - pResampler->config.sampleRateIn /= gcf; - pResampler->config.sampleRateOut /= gcf; - - /* Always initialize the low-pass filter, even when the order is 0. */ - if (pResampler->config.lpfOrder > MA_MAX_FILTER_ORDER) { - return MA_INVALID_ARGS; - } - - lpfSampleRate = (ma_uint32)(ma_max(pResampler->config.sampleRateIn, pResampler->config.sampleRateOut)); - lpfCutoffFrequency = ( double)(ma_min(pResampler->config.sampleRateIn, pResampler->config.sampleRateOut) * 0.5 * pResampler->config.lpfNyquistFactor); - - lpfConfig = ma_lpf_config_init(pResampler->config.format, pResampler->config.channels, lpfSampleRate, lpfCutoffFrequency, pResampler->config.lpfOrder); - - /* - If the resampler is already initialized we don't want to do a fresh initialization of the low-pass filter because it will result in the cached frames - getting cleared. Instead we re-initialize the filter which will maintain any cached frames. - */ - if (isResamplerAlreadyInitialized) { - result = ma_lpf_reinit(&lpfConfig, &pResampler->lpf); - } else { - result = ma_lpf_init_preallocated(&lpfConfig, ma_offset_ptr(pHeap, pHeapLayout->lpfOffset), &pResampler->lpf); - } - - if (result != MA_SUCCESS) { - return result; - } - - - pResampler->inAdvanceInt = pResampler->config.sampleRateIn / pResampler->config.sampleRateOut; - pResampler->inAdvanceFrac = pResampler->config.sampleRateIn % pResampler->config.sampleRateOut; - - /* Our timer was based on the old rate. We need to adjust it so that it's based on the new rate. */ - ma_linear_resampler_adjust_timer_for_new_rate(pResampler, oldSampleRateOut, pResampler->config.sampleRateOut); - - return MA_SUCCESS; -} - -static ma_result ma_linear_resampler_get_heap_layout(const ma_linear_resampler_config* pConfig, ma_linear_resampler_heap_layout* pHeapLayout) -{ - MA_ASSERT(pHeapLayout != NULL); - - MA_ZERO_OBJECT(pHeapLayout); - - if (pConfig == NULL) { - return MA_INVALID_ARGS; - } - - if (pConfig->format != ma_format_f32 && pConfig->format != ma_format_s16) { - return MA_INVALID_ARGS; - } - - if (pConfig->channels == 0) { - return MA_INVALID_ARGS; - } - - pHeapLayout->sizeInBytes = 0; - - /* x0 */ - pHeapLayout->x0Offset = pHeapLayout->sizeInBytes; - if (pConfig->format == ma_format_f32) { - pHeapLayout->sizeInBytes += sizeof(float) * pConfig->channels; - } else { - pHeapLayout->sizeInBytes += sizeof(ma_int16) * pConfig->channels; - } - - /* x1 */ - pHeapLayout->x1Offset = pHeapLayout->sizeInBytes; - if (pConfig->format == ma_format_f32) { - pHeapLayout->sizeInBytes += sizeof(float) * pConfig->channels; - } else { - pHeapLayout->sizeInBytes += sizeof(ma_int16) * pConfig->channels; - } - - /* LPF */ - pHeapLayout->lpfOffset = ma_align_64(pHeapLayout->sizeInBytes); - { - ma_result result; - size_t lpfHeapSizeInBytes; - ma_lpf_config lpfConfig = ma_lpf_config_init(pConfig->format, pConfig->channels, 1, 1, pConfig->lpfOrder); /* Sample rate and cutoff frequency do not matter. */ - - result = ma_lpf_get_heap_size(&lpfConfig, &lpfHeapSizeInBytes); - if (result != MA_SUCCESS) { - return result; - } - - pHeapLayout->sizeInBytes += lpfHeapSizeInBytes; - } - - /* Make sure allocation size is aligned. */ - pHeapLayout->sizeInBytes = ma_align_64(pHeapLayout->sizeInBytes); - - return MA_SUCCESS; -} - -MA_API ma_result ma_linear_resampler_get_heap_size(const ma_linear_resampler_config* pConfig, size_t* pHeapSizeInBytes) -{ - ma_result result; - ma_linear_resampler_heap_layout heapLayout; - - if (pHeapSizeInBytes == NULL) { - return MA_INVALID_ARGS; - } - - *pHeapSizeInBytes = 0; - - result = ma_linear_resampler_get_heap_layout(pConfig, &heapLayout); - if (result != MA_SUCCESS) { - return result; - } - - *pHeapSizeInBytes = heapLayout.sizeInBytes; - - return MA_SUCCESS; -} - -MA_API ma_result ma_linear_resampler_init_preallocated(const ma_linear_resampler_config* pConfig, void* pHeap, ma_linear_resampler* pResampler) -{ - ma_result result; - ma_linear_resampler_heap_layout heapLayout; - - if (pResampler == NULL) { - return MA_INVALID_ARGS; - } - - MA_ZERO_OBJECT(pResampler); - - result = ma_linear_resampler_get_heap_layout(pConfig, &heapLayout); - if (result != MA_SUCCESS) { - return result; - } - - pResampler->config = *pConfig; - - pResampler->_pHeap = pHeap; - MA_ZERO_MEMORY(pHeap, heapLayout.sizeInBytes); - - if (pConfig->format == ma_format_f32) { - pResampler->x0.f32 = (float*)ma_offset_ptr(pHeap, heapLayout.x0Offset); - pResampler->x1.f32 = (float*)ma_offset_ptr(pHeap, heapLayout.x1Offset); - } else { - pResampler->x0.s16 = (ma_int16*)ma_offset_ptr(pHeap, heapLayout.x0Offset); - pResampler->x1.s16 = (ma_int16*)ma_offset_ptr(pHeap, heapLayout.x1Offset); - } - - /* Setting the rate will set up the filter and time advances for us. */ - result = ma_linear_resampler_set_rate_internal(pResampler, pHeap, &heapLayout, pConfig->sampleRateIn, pConfig->sampleRateOut, /* isResamplerAlreadyInitialized = */ MA_FALSE); - if (result != MA_SUCCESS) { - return result; - } - - pResampler->inTimeInt = 1; /* Set this to one to force an input sample to always be loaded for the first output frame. */ - pResampler->inTimeFrac = 0; - - return MA_SUCCESS; -} - -MA_API ma_result ma_linear_resampler_init(const ma_linear_resampler_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_linear_resampler* pResampler) -{ - ma_result result; - size_t heapSizeInBytes; - void* pHeap; - - result = ma_linear_resampler_get_heap_size(pConfig, &heapSizeInBytes); - if (result != MA_SUCCESS) { - return result; - } - - if (heapSizeInBytes > 0) { - pHeap = ma_malloc(heapSizeInBytes, pAllocationCallbacks); - if (pHeap == NULL) { - return MA_OUT_OF_MEMORY; - } - } else { - pHeap = NULL; - } - - result = ma_linear_resampler_init_preallocated(pConfig, pHeap, pResampler); - if (result != MA_SUCCESS) { - ma_free(pHeap, pAllocationCallbacks); - return result; - } - - pResampler->_ownsHeap = MA_TRUE; - return MA_SUCCESS; -} - -MA_API void ma_linear_resampler_uninit(ma_linear_resampler* pResampler, const ma_allocation_callbacks* pAllocationCallbacks) -{ - if (pResampler == NULL) { - return; - } - - ma_lpf_uninit(&pResampler->lpf, pAllocationCallbacks); - - if (pResampler->_ownsHeap) { - ma_free(pResampler->_pHeap, pAllocationCallbacks); - } -} - -static MA_INLINE ma_int16 ma_linear_resampler_mix_s16(ma_int16 x, ma_int16 y, ma_int32 a, const ma_int32 shift) -{ - ma_int32 b; - ma_int32 c; - ma_int32 r; - - MA_ASSERT(a <= (1<> shift); -} - -static void ma_linear_resampler_interpolate_frame_s16(ma_linear_resampler* pResampler, ma_int16* MA_RESTRICT pFrameOut) -{ - ma_uint32 c; - ma_uint32 a; - const ma_uint32 channels = pResampler->config.channels; - const ma_uint32 shift = 12; - - MA_ASSERT(pResampler != NULL); - MA_ASSERT(pFrameOut != NULL); - - a = (pResampler->inTimeFrac << shift) / pResampler->config.sampleRateOut; - - MA_ASSUME(channels > 0); - for (c = 0; c < channels; c += 1) { - ma_int16 s = ma_linear_resampler_mix_s16(pResampler->x0.s16[c], pResampler->x1.s16[c], a, shift); - pFrameOut[c] = s; - } -} - - -static void ma_linear_resampler_interpolate_frame_f32(ma_linear_resampler* pResampler, float* MA_RESTRICT pFrameOut) -{ - ma_uint32 c; - float a; - const ma_uint32 channels = pResampler->config.channels; - - MA_ASSERT(pResampler != NULL); - MA_ASSERT(pFrameOut != NULL); - - a = (float)pResampler->inTimeFrac / pResampler->config.sampleRateOut; - - MA_ASSUME(channels > 0); - for (c = 0; c < channels; c += 1) { - float s = ma_mix_f32_fast(pResampler->x0.f32[c], pResampler->x1.f32[c], a); - pFrameOut[c] = s; - } -} - -static ma_result ma_linear_resampler_process_pcm_frames_s16_downsample(ma_linear_resampler* pResampler, const void* pFramesIn, ma_uint64* pFrameCountIn, void* pFramesOut, ma_uint64* pFrameCountOut) -{ - const ma_int16* pFramesInS16; - /* */ ma_int16* pFramesOutS16; - ma_uint64 frameCountIn; - ma_uint64 frameCountOut; - ma_uint64 framesProcessedIn; - ma_uint64 framesProcessedOut; - - MA_ASSERT(pResampler != NULL); - MA_ASSERT(pFrameCountIn != NULL); - MA_ASSERT(pFrameCountOut != NULL); - - pFramesInS16 = (const ma_int16*)pFramesIn; - pFramesOutS16 = ( ma_int16*)pFramesOut; - frameCountIn = *pFrameCountIn; - frameCountOut = *pFrameCountOut; - framesProcessedIn = 0; - framesProcessedOut = 0; - - while (framesProcessedOut < frameCountOut) { - /* Before interpolating we need to load the buffers. When doing this we need to ensure we run every input sample through the filter. */ - while (pResampler->inTimeInt > 0 && frameCountIn > framesProcessedIn) { - ma_uint32 iChannel; - - if (pFramesInS16 != NULL) { - for (iChannel = 0; iChannel < pResampler->config.channels; iChannel += 1) { - pResampler->x0.s16[iChannel] = pResampler->x1.s16[iChannel]; - pResampler->x1.s16[iChannel] = pFramesInS16[iChannel]; - } - pFramesInS16 += pResampler->config.channels; - } else { - for (iChannel = 0; iChannel < pResampler->config.channels; iChannel += 1) { - pResampler->x0.s16[iChannel] = pResampler->x1.s16[iChannel]; - pResampler->x1.s16[iChannel] = 0; - } - } - - /* Filter. Do not apply filtering if sample rates are the same or else you'll get dangerous glitching. */ - if (pResampler->config.sampleRateIn != pResampler->config.sampleRateOut) { - ma_lpf_process_pcm_frame_s16(&pResampler->lpf, pResampler->x1.s16, pResampler->x1.s16); - } - - framesProcessedIn += 1; - pResampler->inTimeInt -= 1; - } - - if (pResampler->inTimeInt > 0) { - break; /* Ran out of input data. */ - } - - /* Getting here means the frames have been loaded and filtered and we can generate the next output frame. */ - if (pFramesOutS16 != NULL) { - MA_ASSERT(pResampler->inTimeInt == 0); - ma_linear_resampler_interpolate_frame_s16(pResampler, pFramesOutS16); - - pFramesOutS16 += pResampler->config.channels; - } - - framesProcessedOut += 1; - - /* Advance time forward. */ - pResampler->inTimeInt += pResampler->inAdvanceInt; - pResampler->inTimeFrac += pResampler->inAdvanceFrac; - if (pResampler->inTimeFrac >= pResampler->config.sampleRateOut) { - pResampler->inTimeFrac -= pResampler->config.sampleRateOut; - pResampler->inTimeInt += 1; - } - } - - *pFrameCountIn = framesProcessedIn; - *pFrameCountOut = framesProcessedOut; - - return MA_SUCCESS; -} - -static ma_result ma_linear_resampler_process_pcm_frames_s16_upsample(ma_linear_resampler* pResampler, const void* pFramesIn, ma_uint64* pFrameCountIn, void* pFramesOut, ma_uint64* pFrameCountOut) -{ - const ma_int16* pFramesInS16; - /* */ ma_int16* pFramesOutS16; - ma_uint64 frameCountIn; - ma_uint64 frameCountOut; - ma_uint64 framesProcessedIn; - ma_uint64 framesProcessedOut; - - MA_ASSERT(pResampler != NULL); - MA_ASSERT(pFrameCountIn != NULL); - MA_ASSERT(pFrameCountOut != NULL); - - pFramesInS16 = (const ma_int16*)pFramesIn; - pFramesOutS16 = ( ma_int16*)pFramesOut; - frameCountIn = *pFrameCountIn; - frameCountOut = *pFrameCountOut; - framesProcessedIn = 0; - framesProcessedOut = 0; - - while (framesProcessedOut < frameCountOut) { - /* Before interpolating we need to load the buffers. */ - while (pResampler->inTimeInt > 0 && frameCountIn > framesProcessedIn) { - ma_uint32 iChannel; - - if (pFramesInS16 != NULL) { - for (iChannel = 0; iChannel < pResampler->config.channels; iChannel += 1) { - pResampler->x0.s16[iChannel] = pResampler->x1.s16[iChannel]; - pResampler->x1.s16[iChannel] = pFramesInS16[iChannel]; - } - pFramesInS16 += pResampler->config.channels; - } else { - for (iChannel = 0; iChannel < pResampler->config.channels; iChannel += 1) { - pResampler->x0.s16[iChannel] = pResampler->x1.s16[iChannel]; - pResampler->x1.s16[iChannel] = 0; - } - } - - framesProcessedIn += 1; - pResampler->inTimeInt -= 1; - } - - if (pResampler->inTimeInt > 0) { - break; /* Ran out of input data. */ - } - - /* Getting here means the frames have been loaded and we can generate the next output frame. */ - if (pFramesOutS16 != NULL) { - MA_ASSERT(pResampler->inTimeInt == 0); - ma_linear_resampler_interpolate_frame_s16(pResampler, pFramesOutS16); - - /* Filter. Do not apply filtering if sample rates are the same or else you'll get dangerous glitching. */ - if (pResampler->config.sampleRateIn != pResampler->config.sampleRateOut) { - ma_lpf_process_pcm_frame_s16(&pResampler->lpf, pFramesOutS16, pFramesOutS16); - } - - pFramesOutS16 += pResampler->config.channels; - } - - framesProcessedOut += 1; - - /* Advance time forward. */ - pResampler->inTimeInt += pResampler->inAdvanceInt; - pResampler->inTimeFrac += pResampler->inAdvanceFrac; - if (pResampler->inTimeFrac >= pResampler->config.sampleRateOut) { - pResampler->inTimeFrac -= pResampler->config.sampleRateOut; - pResampler->inTimeInt += 1; - } - } - - *pFrameCountIn = framesProcessedIn; - *pFrameCountOut = framesProcessedOut; - - return MA_SUCCESS; -} - -static ma_result ma_linear_resampler_process_pcm_frames_s16(ma_linear_resampler* pResampler, const void* pFramesIn, ma_uint64* pFrameCountIn, void* pFramesOut, ma_uint64* pFrameCountOut) -{ - MA_ASSERT(pResampler != NULL); - - if (pResampler->config.sampleRateIn > pResampler->config.sampleRateOut) { - return ma_linear_resampler_process_pcm_frames_s16_downsample(pResampler, pFramesIn, pFrameCountIn, pFramesOut, pFrameCountOut); - } else { - return ma_linear_resampler_process_pcm_frames_s16_upsample(pResampler, pFramesIn, pFrameCountIn, pFramesOut, pFrameCountOut); - } -} - - -static ma_result ma_linear_resampler_process_pcm_frames_f32_downsample(ma_linear_resampler* pResampler, const void* pFramesIn, ma_uint64* pFrameCountIn, void* pFramesOut, ma_uint64* pFrameCountOut) -{ - const float* pFramesInF32; - /* */ float* pFramesOutF32; - ma_uint64 frameCountIn; - ma_uint64 frameCountOut; - ma_uint64 framesProcessedIn; - ma_uint64 framesProcessedOut; - - MA_ASSERT(pResampler != NULL); - MA_ASSERT(pFrameCountIn != NULL); - MA_ASSERT(pFrameCountOut != NULL); - - pFramesInF32 = (const float*)pFramesIn; - pFramesOutF32 = ( float*)pFramesOut; - frameCountIn = *pFrameCountIn; - frameCountOut = *pFrameCountOut; - framesProcessedIn = 0; - framesProcessedOut = 0; - - while (framesProcessedOut < frameCountOut) { - /* Before interpolating we need to load the buffers. When doing this we need to ensure we run every input sample through the filter. */ - while (pResampler->inTimeInt > 0 && frameCountIn > framesProcessedIn) { - ma_uint32 iChannel; - - if (pFramesInF32 != NULL) { - for (iChannel = 0; iChannel < pResampler->config.channels; iChannel += 1) { - pResampler->x0.f32[iChannel] = pResampler->x1.f32[iChannel]; - pResampler->x1.f32[iChannel] = pFramesInF32[iChannel]; - } - pFramesInF32 += pResampler->config.channels; - } else { - for (iChannel = 0; iChannel < pResampler->config.channels; iChannel += 1) { - pResampler->x0.f32[iChannel] = pResampler->x1.f32[iChannel]; - pResampler->x1.f32[iChannel] = 0; - } - } - - /* Filter. Do not apply filtering if sample rates are the same or else you'll get dangerous glitching. */ - if (pResampler->config.sampleRateIn != pResampler->config.sampleRateOut) { - ma_lpf_process_pcm_frame_f32(&pResampler->lpf, pResampler->x1.f32, pResampler->x1.f32); - } - - framesProcessedIn += 1; - pResampler->inTimeInt -= 1; - } - - if (pResampler->inTimeInt > 0) { - break; /* Ran out of input data. */ - } - - /* Getting here means the frames have been loaded and filtered and we can generate the next output frame. */ - if (pFramesOutF32 != NULL) { - MA_ASSERT(pResampler->inTimeInt == 0); - ma_linear_resampler_interpolate_frame_f32(pResampler, pFramesOutF32); - - pFramesOutF32 += pResampler->config.channels; - } - - framesProcessedOut += 1; - - /* Advance time forward. */ - pResampler->inTimeInt += pResampler->inAdvanceInt; - pResampler->inTimeFrac += pResampler->inAdvanceFrac; - if (pResampler->inTimeFrac >= pResampler->config.sampleRateOut) { - pResampler->inTimeFrac -= pResampler->config.sampleRateOut; - pResampler->inTimeInt += 1; - } - } - - *pFrameCountIn = framesProcessedIn; - *pFrameCountOut = framesProcessedOut; - - return MA_SUCCESS; -} - -static ma_result ma_linear_resampler_process_pcm_frames_f32_upsample(ma_linear_resampler* pResampler, const void* pFramesIn, ma_uint64* pFrameCountIn, void* pFramesOut, ma_uint64* pFrameCountOut) -{ - const float* pFramesInF32; - /* */ float* pFramesOutF32; - ma_uint64 frameCountIn; - ma_uint64 frameCountOut; - ma_uint64 framesProcessedIn; - ma_uint64 framesProcessedOut; - - MA_ASSERT(pResampler != NULL); - MA_ASSERT(pFrameCountIn != NULL); - MA_ASSERT(pFrameCountOut != NULL); - - pFramesInF32 = (const float*)pFramesIn; - pFramesOutF32 = ( float*)pFramesOut; - frameCountIn = *pFrameCountIn; - frameCountOut = *pFrameCountOut; - framesProcessedIn = 0; - framesProcessedOut = 0; - - while (framesProcessedOut < frameCountOut) { - /* Before interpolating we need to load the buffers. */ - while (pResampler->inTimeInt > 0 && frameCountIn > framesProcessedIn) { - ma_uint32 iChannel; - - if (pFramesInF32 != NULL) { - for (iChannel = 0; iChannel < pResampler->config.channels; iChannel += 1) { - pResampler->x0.f32[iChannel] = pResampler->x1.f32[iChannel]; - pResampler->x1.f32[iChannel] = pFramesInF32[iChannel]; - } - pFramesInF32 += pResampler->config.channels; - } else { - for (iChannel = 0; iChannel < pResampler->config.channels; iChannel += 1) { - pResampler->x0.f32[iChannel] = pResampler->x1.f32[iChannel]; - pResampler->x1.f32[iChannel] = 0; - } - } - - framesProcessedIn += 1; - pResampler->inTimeInt -= 1; - } - - if (pResampler->inTimeInt > 0) { - break; /* Ran out of input data. */ - } - - /* Getting here means the frames have been loaded and we can generate the next output frame. */ - if (pFramesOutF32 != NULL) { - MA_ASSERT(pResampler->inTimeInt == 0); - ma_linear_resampler_interpolate_frame_f32(pResampler, pFramesOutF32); - - /* Filter. Do not apply filtering if sample rates are the same or else you'll get dangerous glitching. */ - if (pResampler->config.sampleRateIn != pResampler->config.sampleRateOut) { - ma_lpf_process_pcm_frame_f32(&pResampler->lpf, pFramesOutF32, pFramesOutF32); - } - - pFramesOutF32 += pResampler->config.channels; - } - - framesProcessedOut += 1; - - /* Advance time forward. */ - pResampler->inTimeInt += pResampler->inAdvanceInt; - pResampler->inTimeFrac += pResampler->inAdvanceFrac; - if (pResampler->inTimeFrac >= pResampler->config.sampleRateOut) { - pResampler->inTimeFrac -= pResampler->config.sampleRateOut; - pResampler->inTimeInt += 1; - } - } - - *pFrameCountIn = framesProcessedIn; - *pFrameCountOut = framesProcessedOut; - - return MA_SUCCESS; -} - -static ma_result ma_linear_resampler_process_pcm_frames_f32(ma_linear_resampler* pResampler, const void* pFramesIn, ma_uint64* pFrameCountIn, void* pFramesOut, ma_uint64* pFrameCountOut) -{ - MA_ASSERT(pResampler != NULL); - - if (pResampler->config.sampleRateIn > pResampler->config.sampleRateOut) { - return ma_linear_resampler_process_pcm_frames_f32_downsample(pResampler, pFramesIn, pFrameCountIn, pFramesOut, pFrameCountOut); - } else { - return ma_linear_resampler_process_pcm_frames_f32_upsample(pResampler, pFramesIn, pFrameCountIn, pFramesOut, pFrameCountOut); - } -} - - -MA_API ma_result ma_linear_resampler_process_pcm_frames(ma_linear_resampler* pResampler, const void* pFramesIn, ma_uint64* pFrameCountIn, void* pFramesOut, ma_uint64* pFrameCountOut) -{ - if (pResampler == NULL) { - return MA_INVALID_ARGS; - } - - /* */ if (pResampler->config.format == ma_format_s16) { - return ma_linear_resampler_process_pcm_frames_s16(pResampler, pFramesIn, pFrameCountIn, pFramesOut, pFrameCountOut); - } else if (pResampler->config.format == ma_format_f32) { - return ma_linear_resampler_process_pcm_frames_f32(pResampler, pFramesIn, pFrameCountIn, pFramesOut, pFrameCountOut); - } else { - /* Should never get here. Getting here means the format is not supported and you didn't check the return value of ma_linear_resampler_init(). */ - MA_ASSERT(MA_FALSE); - return MA_INVALID_ARGS; - } -} - - -MA_API ma_result ma_linear_resampler_set_rate(ma_linear_resampler* pResampler, ma_uint32 sampleRateIn, ma_uint32 sampleRateOut) -{ - return ma_linear_resampler_set_rate_internal(pResampler, NULL, NULL, sampleRateIn, sampleRateOut, /* isResamplerAlreadyInitialized = */ MA_TRUE); -} - -MA_API ma_result ma_linear_resampler_set_rate_ratio(ma_linear_resampler* pResampler, float ratioInOut) -{ - ma_uint32 n; - ma_uint32 d; - - if (pResampler == NULL) { - return MA_INVALID_ARGS; - } - - if (ratioInOut <= 0) { - return MA_INVALID_ARGS; - } - - d = 1000000; - n = (ma_uint32)(ratioInOut * d); - - if (n == 0) { - return MA_INVALID_ARGS; /* Ratio too small. */ - } - - MA_ASSERT(n != 0); - - return ma_linear_resampler_set_rate(pResampler, n, d); -} - -MA_API ma_uint64 ma_linear_resampler_get_input_latency(const ma_linear_resampler* pResampler) -{ - if (pResampler == NULL) { - return 0; - } - - return 1 + ma_lpf_get_latency(&pResampler->lpf); -} - -MA_API ma_uint64 ma_linear_resampler_get_output_latency(const ma_linear_resampler* pResampler) -{ - if (pResampler == NULL) { - return 0; - } - - return ma_linear_resampler_get_input_latency(pResampler) * pResampler->config.sampleRateOut / pResampler->config.sampleRateIn; -} - -MA_API ma_result ma_linear_resampler_get_required_input_frame_count(const ma_linear_resampler* pResampler, ma_uint64 outputFrameCount, ma_uint64* pInputFrameCount) -{ - ma_uint64 inputFrameCount; - - if (pInputFrameCount == NULL) { - return MA_INVALID_ARGS; - } - - *pInputFrameCount = 0; - - if (pResampler == NULL) { - return MA_INVALID_ARGS; - } - - if (outputFrameCount == 0) { - return MA_SUCCESS; - } - - /* Any whole input frames are consumed before the first output frame is generated. */ - inputFrameCount = pResampler->inTimeInt; - outputFrameCount -= 1; - - /* The rest of the output frames can be calculated in constant time. */ - inputFrameCount += outputFrameCount * pResampler->inAdvanceInt; - inputFrameCount += (pResampler->inTimeFrac + (outputFrameCount * pResampler->inAdvanceFrac)) / pResampler->config.sampleRateOut; - - *pInputFrameCount = inputFrameCount; - - return MA_SUCCESS; -} - -MA_API ma_result ma_linear_resampler_get_expected_output_frame_count(const ma_linear_resampler* pResampler, ma_uint64 inputFrameCount, ma_uint64* pOutputFrameCount) -{ - ma_uint64 outputFrameCount; - ma_uint64 preliminaryInputFrameCountFromFrac; - ma_uint64 preliminaryInputFrameCount; - - if (pOutputFrameCount == NULL) { - return MA_INVALID_ARGS; - } - - *pOutputFrameCount = 0; - - if (pResampler == NULL) { - return MA_INVALID_ARGS; - } - - /* - The first step is to get a preliminary output frame count. This will either be exactly equal to what we need, or less by 1. We need to - determine how many input frames will be consumed by this value. If it's greater than our original input frame count it means we won't - be able to generate an extra frame because we will have run out of input data. Otherwise we will have enough input for the generation - of an extra output frame. This add-by-one logic is necessary due to how the data loading logic works when processing frames. - */ - outputFrameCount = (inputFrameCount * pResampler->config.sampleRateOut) / pResampler->config.sampleRateIn; - - /* - We need to determine how many *whole* input frames will have been processed to generate our preliminary output frame count. This is - used in the logic below to determine whether or not we need to add an extra output frame. - */ - preliminaryInputFrameCountFromFrac = (pResampler->inTimeFrac + outputFrameCount*pResampler->inAdvanceFrac) / pResampler->config.sampleRateOut; - preliminaryInputFrameCount = (pResampler->inTimeInt + outputFrameCount*pResampler->inAdvanceInt ) + preliminaryInputFrameCountFromFrac; - - /* - If the total number of *whole* input frames that would be required to generate our preliminary output frame count is greater than - the amount of whole input frames we have available as input we need to *not* add an extra output frame as there won't be enough data - to actually process. Otherwise we need to add the extra output frame. - */ - if (preliminaryInputFrameCount <= inputFrameCount) { - outputFrameCount += 1; - } - - *pOutputFrameCount = outputFrameCount; - - return MA_SUCCESS; -} - -MA_API ma_result ma_linear_resampler_reset(ma_linear_resampler* pResampler) -{ - ma_uint32 iChannel; - - if (pResampler == NULL) { - return MA_INVALID_ARGS; - } - - /* Timers need to be cleared back to zero. */ - pResampler->inTimeInt = 1; /* Set this to one to force an input sample to always be loaded for the first output frame. */ - pResampler->inTimeFrac = 0; - - /* Cached samples need to be cleared. */ - if (pResampler->config.format == ma_format_f32) { - for (iChannel = 0; iChannel < pResampler->config.channels; iChannel += 1) { - pResampler->x0.f32[iChannel] = 0; - pResampler->x1.f32[iChannel] = 0; - } - } else { - for (iChannel = 0; iChannel < pResampler->config.channels; iChannel += 1) { - pResampler->x0.s16[iChannel] = 0; - pResampler->x1.s16[iChannel] = 0; - } - } - - /* The low pass filter needs to have its cache reset. */ - ma_lpf_clear_cache(&pResampler->lpf); - - return MA_SUCCESS; -} - - - -/* Linear resampler backend vtable. */ -static ma_linear_resampler_config ma_resampling_backend_get_config__linear(const ma_resampler_config* pConfig) -{ - ma_linear_resampler_config linearConfig; - - linearConfig = ma_linear_resampler_config_init(pConfig->format, pConfig->channels, pConfig->sampleRateIn, pConfig->sampleRateOut); - linearConfig.lpfOrder = pConfig->linear.lpfOrder; - - return linearConfig; -} - -static ma_result ma_resampling_backend_get_heap_size__linear(void* pUserData, const ma_resampler_config* pConfig, size_t* pHeapSizeInBytes) -{ - ma_linear_resampler_config linearConfig; - - (void)pUserData; - - linearConfig = ma_resampling_backend_get_config__linear(pConfig); - - return ma_linear_resampler_get_heap_size(&linearConfig, pHeapSizeInBytes); -} - -static ma_result ma_resampling_backend_init__linear(void* pUserData, const ma_resampler_config* pConfig, void* pHeap, ma_resampling_backend** ppBackend) -{ - ma_resampler* pResampler = (ma_resampler*)pUserData; - ma_result result; - ma_linear_resampler_config linearConfig; - - (void)pUserData; - - linearConfig = ma_resampling_backend_get_config__linear(pConfig); - - result = ma_linear_resampler_init_preallocated(&linearConfig, pHeap, &pResampler->state.linear); - if (result != MA_SUCCESS) { - return result; - } - - *ppBackend = &pResampler->state.linear; - - return MA_SUCCESS; -} - -static void ma_resampling_backend_uninit__linear(void* pUserData, ma_resampling_backend* pBackend, const ma_allocation_callbacks* pAllocationCallbacks) -{ - (void)pUserData; - - ma_linear_resampler_uninit((ma_linear_resampler*)pBackend, pAllocationCallbacks); -} - -static ma_result ma_resampling_backend_process__linear(void* pUserData, ma_resampling_backend* pBackend, const void* pFramesIn, ma_uint64* pFrameCountIn, void* pFramesOut, ma_uint64* pFrameCountOut) -{ - (void)pUserData; - - return ma_linear_resampler_process_pcm_frames((ma_linear_resampler*)pBackend, pFramesIn, pFrameCountIn, pFramesOut, pFrameCountOut); -} - -static ma_result ma_resampling_backend_set_rate__linear(void* pUserData, ma_resampling_backend* pBackend, ma_uint32 sampleRateIn, ma_uint32 sampleRateOut) -{ - (void)pUserData; - - return ma_linear_resampler_set_rate((ma_linear_resampler*)pBackend, sampleRateIn, sampleRateOut); -} - -static ma_uint64 ma_resampling_backend_get_input_latency__linear(void* pUserData, const ma_resampling_backend* pBackend) -{ - (void)pUserData; - - return ma_linear_resampler_get_input_latency((const ma_linear_resampler*)pBackend); -} - -static ma_uint64 ma_resampling_backend_get_output_latency__linear(void* pUserData, const ma_resampling_backend* pBackend) -{ - (void)pUserData; - - return ma_linear_resampler_get_output_latency((const ma_linear_resampler*)pBackend); -} - -static ma_result ma_resampling_backend_get_required_input_frame_count__linear(void* pUserData, const ma_resampling_backend* pBackend, ma_uint64 outputFrameCount, ma_uint64* pInputFrameCount) -{ - (void)pUserData; - - return ma_linear_resampler_get_required_input_frame_count((const ma_linear_resampler*)pBackend, outputFrameCount, pInputFrameCount); -} - -static ma_result ma_resampling_backend_get_expected_output_frame_count__linear(void* pUserData, const ma_resampling_backend* pBackend, ma_uint64 inputFrameCount, ma_uint64* pOutputFrameCount) -{ - (void)pUserData; - - return ma_linear_resampler_get_expected_output_frame_count((const ma_linear_resampler*)pBackend, inputFrameCount, pOutputFrameCount); -} - -static ma_result ma_resampling_backend_reset__linear(void* pUserData, ma_resampling_backend* pBackend) -{ - (void)pUserData; - - return ma_linear_resampler_reset((ma_linear_resampler*)pBackend); -} - -static ma_resampling_backend_vtable g_ma_linear_resampler_vtable = -{ - ma_resampling_backend_get_heap_size__linear, - ma_resampling_backend_init__linear, - ma_resampling_backend_uninit__linear, - ma_resampling_backend_process__linear, - ma_resampling_backend_set_rate__linear, - ma_resampling_backend_get_input_latency__linear, - ma_resampling_backend_get_output_latency__linear, - ma_resampling_backend_get_required_input_frame_count__linear, - ma_resampling_backend_get_expected_output_frame_count__linear, - ma_resampling_backend_reset__linear -}; - - - -MA_API ma_resampler_config ma_resampler_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRateIn, ma_uint32 sampleRateOut, ma_resample_algorithm algorithm) -{ - ma_resampler_config config; - - MA_ZERO_OBJECT(&config); - config.format = format; - config.channels = channels; - config.sampleRateIn = sampleRateIn; - config.sampleRateOut = sampleRateOut; - config.algorithm = algorithm; - - /* Linear. */ - config.linear.lpfOrder = ma_min(MA_DEFAULT_RESAMPLER_LPF_ORDER, MA_MAX_FILTER_ORDER); - - return config; -} - -static ma_result ma_resampler_get_vtable(const ma_resampler_config* pConfig, ma_resampler* pResampler, ma_resampling_backend_vtable** ppVTable, void** ppUserData) -{ - MA_ASSERT(pConfig != NULL); - MA_ASSERT(ppVTable != NULL); - MA_ASSERT(ppUserData != NULL); - - /* Safety. */ - *ppVTable = NULL; - *ppUserData = NULL; - - switch (pConfig->algorithm) - { - case ma_resample_algorithm_linear: - { - *ppVTable = &g_ma_linear_resampler_vtable; - *ppUserData = pResampler; - } break; - - case ma_resample_algorithm_custom: - { - *ppVTable = pConfig->pBackendVTable; - *ppUserData = pConfig->pBackendUserData; - } break; - - default: return MA_INVALID_ARGS; - } - - return MA_SUCCESS; -} - -MA_API ma_result ma_resampler_get_heap_size(const ma_resampler_config* pConfig, size_t* pHeapSizeInBytes) -{ - ma_result result; - ma_resampling_backend_vtable* pVTable; - void* pVTableUserData; - - if (pHeapSizeInBytes == NULL) { - return MA_INVALID_ARGS; - } - - *pHeapSizeInBytes = 0; - - if (pConfig == NULL) { - return MA_INVALID_ARGS; - } - - result = ma_resampler_get_vtable(pConfig, NULL, &pVTable, &pVTableUserData); - if (result != MA_SUCCESS) { - return result; - } - - if (pVTable == NULL || pVTable->onGetHeapSize == NULL) { - return MA_NOT_IMPLEMENTED; - } - - result = pVTable->onGetHeapSize(pVTableUserData, pConfig, pHeapSizeInBytes); - if (result != MA_SUCCESS) { - return result; - } - - return MA_SUCCESS; -} - -MA_API ma_result ma_resampler_init_preallocated(const ma_resampler_config* pConfig, void* pHeap, ma_resampler* pResampler) -{ - ma_result result; - - if (pResampler == NULL) { - return MA_INVALID_ARGS; - } - - MA_ZERO_OBJECT(pResampler); - - if (pConfig == NULL) { - return MA_INVALID_ARGS; - } - - pResampler->_pHeap = pHeap; - pResampler->format = pConfig->format; - pResampler->channels = pConfig->channels; - pResampler->sampleRateIn = pConfig->sampleRateIn; - pResampler->sampleRateOut = pConfig->sampleRateOut; - - result = ma_resampler_get_vtable(pConfig, pResampler, &pResampler->pBackendVTable, &pResampler->pBackendUserData); - if (result != MA_SUCCESS) { - return result; - } - - if (pResampler->pBackendVTable == NULL || pResampler->pBackendVTable->onInit == NULL) { - return MA_NOT_IMPLEMENTED; /* onInit not implemented. */ - } - - result = pResampler->pBackendVTable->onInit(pResampler->pBackendUserData, pConfig, pHeap, &pResampler->pBackend); - if (result != MA_SUCCESS) { - return result; - } - - return MA_SUCCESS; -} - -MA_API ma_result ma_resampler_init(const ma_resampler_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_resampler* pResampler) -{ - ma_result result; - size_t heapSizeInBytes; - void* pHeap; - - result = ma_resampler_get_heap_size(pConfig, &heapSizeInBytes); - if (result != MA_SUCCESS) { - return result; - } - - if (heapSizeInBytes > 0) { - pHeap = ma_malloc(heapSizeInBytes, pAllocationCallbacks); - if (pHeap == NULL) { - return MA_OUT_OF_MEMORY; - } - } else { - pHeap = NULL; - } - - result = ma_resampler_init_preallocated(pConfig, pHeap, pResampler); - if (result != MA_SUCCESS) { - ma_free(pHeap, pAllocationCallbacks); - return result; - } - - pResampler->_ownsHeap = MA_TRUE; - return MA_SUCCESS; -} - -MA_API void ma_resampler_uninit(ma_resampler* pResampler, const ma_allocation_callbacks* pAllocationCallbacks) -{ - if (pResampler == NULL) { - return; - } - - if (pResampler->pBackendVTable == NULL || pResampler->pBackendVTable->onUninit == NULL) { - return; - } - - pResampler->pBackendVTable->onUninit(pResampler->pBackendUserData, pResampler->pBackend, pAllocationCallbacks); - - if (pResampler->_ownsHeap) { - ma_free(pResampler->_pHeap, pAllocationCallbacks); - } -} - -MA_API ma_result ma_resampler_process_pcm_frames(ma_resampler* pResampler, const void* pFramesIn, ma_uint64* pFrameCountIn, void* pFramesOut, ma_uint64* pFrameCountOut) -{ - if (pResampler == NULL) { - return MA_INVALID_ARGS; - } - - if (pFrameCountOut == NULL && pFrameCountIn == NULL) { - return MA_INVALID_ARGS; - } - - if (pResampler->pBackendVTable == NULL || pResampler->pBackendVTable->onProcess == NULL) { - return MA_NOT_IMPLEMENTED; - } - - return pResampler->pBackendVTable->onProcess(pResampler->pBackendUserData, pResampler->pBackend, pFramesIn, pFrameCountIn, pFramesOut, pFrameCountOut); -} - -MA_API ma_result ma_resampler_set_rate(ma_resampler* pResampler, ma_uint32 sampleRateIn, ma_uint32 sampleRateOut) -{ - ma_result result; - - if (pResampler == NULL) { - return MA_INVALID_ARGS; - } - - if (sampleRateIn == 0 || sampleRateOut == 0) { - return MA_INVALID_ARGS; - } - - if (pResampler->pBackendVTable == NULL || pResampler->pBackendVTable->onSetRate == NULL) { - return MA_NOT_IMPLEMENTED; - } - - result = pResampler->pBackendVTable->onSetRate(pResampler->pBackendUserData, pResampler->pBackend, sampleRateIn, sampleRateOut); - if (result != MA_SUCCESS) { - return result; - } - - pResampler->sampleRateIn = sampleRateIn; - pResampler->sampleRateOut = sampleRateOut; - - return MA_SUCCESS; -} - -MA_API ma_result ma_resampler_set_rate_ratio(ma_resampler* pResampler, float ratio) -{ - ma_uint32 n; - ma_uint32 d; - - if (pResampler == NULL) { - return MA_INVALID_ARGS; - } - - if (ratio <= 0) { - return MA_INVALID_ARGS; - } - - d = 1000; - n = (ma_uint32)(ratio * d); - - if (n == 0) { - return MA_INVALID_ARGS; /* Ratio too small. */ - } - - MA_ASSERT(n != 0); - - return ma_resampler_set_rate(pResampler, n, d); -} - -MA_API ma_uint64 ma_resampler_get_input_latency(const ma_resampler* pResampler) -{ - if (pResampler == NULL) { - return 0; - } - - if (pResampler->pBackendVTable == NULL || pResampler->pBackendVTable->onGetInputLatency == NULL) { - return 0; - } - - return pResampler->pBackendVTable->onGetInputLatency(pResampler->pBackendUserData, pResampler->pBackend); -} - -MA_API ma_uint64 ma_resampler_get_output_latency(const ma_resampler* pResampler) -{ - if (pResampler == NULL) { - return 0; - } - - if (pResampler->pBackendVTable == NULL || pResampler->pBackendVTable->onGetOutputLatency == NULL) { - return 0; - } - - return pResampler->pBackendVTable->onGetOutputLatency(pResampler->pBackendUserData, pResampler->pBackend); -} - -MA_API ma_result ma_resampler_get_required_input_frame_count(const ma_resampler* pResampler, ma_uint64 outputFrameCount, ma_uint64* pInputFrameCount) -{ - if (pInputFrameCount == NULL) { - return MA_INVALID_ARGS; - } - - *pInputFrameCount = 0; - - if (pResampler == NULL) { - return MA_INVALID_ARGS; - } - - if (pResampler->pBackendVTable == NULL || pResampler->pBackendVTable->onGetRequiredInputFrameCount == NULL) { - return MA_NOT_IMPLEMENTED; - } - - return pResampler->pBackendVTable->onGetRequiredInputFrameCount(pResampler->pBackendUserData, pResampler->pBackend, outputFrameCount, pInputFrameCount); -} - -MA_API ma_result ma_resampler_get_expected_output_frame_count(const ma_resampler* pResampler, ma_uint64 inputFrameCount, ma_uint64* pOutputFrameCount) -{ - if (pOutputFrameCount == NULL) { - return MA_INVALID_ARGS; - } - - *pOutputFrameCount = 0; - - if (pResampler == NULL) { - return MA_INVALID_ARGS; - } - - if (pResampler->pBackendVTable == NULL || pResampler->pBackendVTable->onGetExpectedOutputFrameCount == NULL) { - return MA_NOT_IMPLEMENTED; - } - - return pResampler->pBackendVTable->onGetExpectedOutputFrameCount(pResampler->pBackendUserData, pResampler->pBackend, inputFrameCount, pOutputFrameCount); -} - -MA_API ma_result ma_resampler_reset(ma_resampler* pResampler) -{ - if (pResampler == NULL) { - return MA_INVALID_ARGS; - } - - if (pResampler->pBackendVTable == NULL || pResampler->pBackendVTable->onReset == NULL) { - return MA_NOT_IMPLEMENTED; - } - - return pResampler->pBackendVTable->onReset(pResampler->pBackendUserData, pResampler->pBackend); -} - -/************************************************************************************************************************************************************** - -Channel Conversion - -**************************************************************************************************************************************************************/ -#ifndef MA_CHANNEL_CONVERTER_FIXED_POINT_SHIFT -#define MA_CHANNEL_CONVERTER_FIXED_POINT_SHIFT 12 -#endif - -#define MA_PLANE_LEFT 0 -#define MA_PLANE_RIGHT 1 -#define MA_PLANE_FRONT 2 -#define MA_PLANE_BACK 3 -#define MA_PLANE_BOTTOM 4 -#define MA_PLANE_TOP 5 - -static float g_maChannelPlaneRatios[MA_CHANNEL_POSITION_COUNT][6] = { - { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_NONE */ - { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_MONO */ - { 0.5f, 0.0f, 0.5f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_FRONT_LEFT */ - { 0.0f, 0.5f, 0.5f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_FRONT_RIGHT */ - { 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_FRONT_CENTER */ - { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_LFE */ - { 0.5f, 0.0f, 0.0f, 0.5f, 0.0f, 0.0f}, /* MA_CHANNEL_BACK_LEFT */ - { 0.0f, 0.5f, 0.0f, 0.5f, 0.0f, 0.0f}, /* MA_CHANNEL_BACK_RIGHT */ - { 0.25f, 0.0f, 0.75f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_FRONT_LEFT_CENTER */ - { 0.0f, 0.25f, 0.75f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_FRONT_RIGHT_CENTER */ - { 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f}, /* MA_CHANNEL_BACK_CENTER */ - { 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_SIDE_LEFT */ - { 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_SIDE_RIGHT */ - { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f}, /* MA_CHANNEL_TOP_CENTER */ - { 0.33f, 0.0f, 0.33f, 0.0f, 0.0f, 0.34f}, /* MA_CHANNEL_TOP_FRONT_LEFT */ - { 0.0f, 0.0f, 0.5f, 0.0f, 0.0f, 0.5f}, /* MA_CHANNEL_TOP_FRONT_CENTER */ - { 0.0f, 0.33f, 0.33f, 0.0f, 0.0f, 0.34f}, /* MA_CHANNEL_TOP_FRONT_RIGHT */ - { 0.33f, 0.0f, 0.0f, 0.33f, 0.0f, 0.34f}, /* MA_CHANNEL_TOP_BACK_LEFT */ - { 0.0f, 0.0f, 0.0f, 0.5f, 0.0f, 0.5f}, /* MA_CHANNEL_TOP_BACK_CENTER */ - { 0.0f, 0.33f, 0.0f, 0.33f, 0.0f, 0.34f}, /* MA_CHANNEL_TOP_BACK_RIGHT */ - { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_0 */ - { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_1 */ - { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_2 */ - { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_3 */ - { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_4 */ - { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_5 */ - { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_6 */ - { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_7 */ - { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_8 */ - { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_9 */ - { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_10 */ - { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_11 */ - { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_12 */ - { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_13 */ - { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_14 */ - { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_15 */ - { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_16 */ - { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_17 */ - { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_18 */ - { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_19 */ - { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_20 */ - { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_21 */ - { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_22 */ - { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_23 */ - { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_24 */ - { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_25 */ - { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_26 */ - { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_27 */ - { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_28 */ - { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_29 */ - { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_30 */ - { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_31 */ -}; - -static float ma_calculate_channel_position_rectangular_weight(ma_channel channelPositionA, ma_channel channelPositionB) -{ - /* - Imagine the following simplified example: You have a single input speaker which is the front/left speaker which you want to convert to - the following output configuration: - - - front/left - - side/left - - back/left - - The front/left output is easy - it the same speaker position so it receives the full contribution of the front/left input. The amount - of contribution to apply to the side/left and back/left speakers, however, is a bit more complicated. - - Imagine the front/left speaker as emitting audio from two planes - the front plane and the left plane. You can think of the front/left - speaker emitting half of its total volume from the front, and the other half from the left. Since part of its volume is being emitted - from the left side, and the side/left and back/left channels also emit audio from the left plane, one would expect that they would - receive some amount of contribution from front/left speaker. The amount of contribution depends on how many planes are shared between - the two speakers. Note that in the examples below I've added a top/front/left speaker as an example just to show how the math works - across 3 spatial dimensions. - - The first thing to do is figure out how each speaker's volume is spread over each of plane: - - front/left: 2 planes (front and left) = 1/2 = half its total volume on each plane - - side/left: 1 plane (left only) = 1/1 = entire volume from left plane - - back/left: 2 planes (back and left) = 1/2 = half its total volume on each plane - - top/front/left: 3 planes (top, front and left) = 1/3 = one third its total volume on each plane - - The amount of volume each channel contributes to each of its planes is what controls how much it is willing to given and take to other - channels on the same plane. The volume that is willing to the given by one channel is multiplied by the volume that is willing to be - taken by the other to produce the final contribution. - */ - - /* Contribution = Sum(Volume to Give * Volume to Take) */ - float contribution = - g_maChannelPlaneRatios[channelPositionA][0] * g_maChannelPlaneRatios[channelPositionB][0] + - g_maChannelPlaneRatios[channelPositionA][1] * g_maChannelPlaneRatios[channelPositionB][1] + - g_maChannelPlaneRatios[channelPositionA][2] * g_maChannelPlaneRatios[channelPositionB][2] + - g_maChannelPlaneRatios[channelPositionA][3] * g_maChannelPlaneRatios[channelPositionB][3] + - g_maChannelPlaneRatios[channelPositionA][4] * g_maChannelPlaneRatios[channelPositionB][4] + - g_maChannelPlaneRatios[channelPositionA][5] * g_maChannelPlaneRatios[channelPositionB][5]; - - return contribution; -} - -MA_API ma_channel_converter_config ma_channel_converter_config_init(ma_format format, ma_uint32 channelsIn, const ma_channel* pChannelMapIn, ma_uint32 channelsOut, const ma_channel* pChannelMapOut, ma_channel_mix_mode mixingMode) -{ - ma_channel_converter_config config; - - MA_ZERO_OBJECT(&config); - config.format = format; - config.channelsIn = channelsIn; - config.channelsOut = channelsOut; - config.pChannelMapIn = pChannelMapIn; - config.pChannelMapOut = pChannelMapOut; - config.mixingMode = mixingMode; - - return config; -} - -static ma_int32 ma_channel_converter_float_to_fixed(float x) -{ - return (ma_int32)(x * (1< 0); - - for (iChannel = 0; iChannel < channels; ++iChannel) { - if (ma_is_spatial_channel_position(ma_channel_map_get_channel(pChannelMap, channels, iChannel))) { - spatialChannelCount++; - } - } - - return spatialChannelCount; -} - -static ma_bool32 ma_is_spatial_channel_position(ma_channel channelPosition) -{ - int i; - - if (channelPosition == MA_CHANNEL_NONE || channelPosition == MA_CHANNEL_MONO || channelPosition == MA_CHANNEL_LFE) { - return MA_FALSE; - } - - if (channelPosition >= MA_CHANNEL_AUX_0 && channelPosition <= MA_CHANNEL_AUX_31) { - return MA_FALSE; - } - - for (i = 0; i < 6; ++i) { /* Each side of a cube. */ - if (g_maChannelPlaneRatios[channelPosition][i] != 0) { - return MA_TRUE; - } - } - - return MA_FALSE; -} - - -static ma_bool32 ma_channel_map_is_passthrough(const ma_channel* pChannelMapIn, ma_uint32 channelsIn, const ma_channel* pChannelMapOut, ma_uint32 channelsOut) -{ - if (channelsOut == channelsIn) { - return ma_channel_map_is_equal(pChannelMapOut, pChannelMapIn, channelsOut); - } else { - return MA_FALSE; /* Channel counts differ, so cannot be a passthrough. */ - } -} - -static ma_channel_conversion_path ma_channel_map_get_conversion_path(const ma_channel* pChannelMapIn, ma_uint32 channelsIn, const ma_channel* pChannelMapOut, ma_uint32 channelsOut, ma_channel_mix_mode mode) -{ - if (ma_channel_map_is_passthrough(pChannelMapIn, channelsIn, pChannelMapOut, channelsOut)) { - return ma_channel_conversion_path_passthrough; - } - - if (channelsOut == 1 && (pChannelMapOut == NULL || pChannelMapOut[0] == MA_CHANNEL_MONO)) { - return ma_channel_conversion_path_mono_out; - } - - if (channelsIn == 1 && (pChannelMapIn == NULL || pChannelMapIn[0] == MA_CHANNEL_MONO)) { - return ma_channel_conversion_path_mono_in; - } - - if (mode == ma_channel_mix_mode_custom_weights) { - return ma_channel_conversion_path_weights; - } - - /* - We can use a simple shuffle if both channel maps have the same channel count and all channel - positions are present in both. - */ - if (channelsIn == channelsOut) { - ma_uint32 iChannelIn; - ma_bool32 areAllChannelPositionsPresent = MA_TRUE; - for (iChannelIn = 0; iChannelIn < channelsIn; ++iChannelIn) { - ma_bool32 isInputChannelPositionInOutput = ma_channel_map_contains_channel_position(channelsOut, pChannelMapOut, ma_channel_map_get_channel(pChannelMapIn, channelsIn, iChannelIn)); - if (!isInputChannelPositionInOutput) { - areAllChannelPositionsPresent = MA_FALSE; - break; - } - } - - if (areAllChannelPositionsPresent) { - return ma_channel_conversion_path_shuffle; - } - } - - /* Getting here means we'll need to use weights. */ - return ma_channel_conversion_path_weights; -} - - -static ma_result ma_channel_map_build_shuffle_table(const ma_channel* pChannelMapIn, ma_uint32 channelCountIn, const ma_channel* pChannelMapOut, ma_uint32 channelCountOut, ma_uint8* pShuffleTable) -{ - ma_uint32 iChannelIn; - ma_uint32 iChannelOut; - - if (pShuffleTable == NULL || channelCountIn == 0 || channelCountOut == 0) { - return MA_INVALID_ARGS; - } - - /* - When building the shuffle table we just do a 1:1 mapping based on the first occurrence of a channel. If the - input channel has more than one occurrence of a channel position, the second one will be ignored. - */ - for (iChannelOut = 0; iChannelOut < channelCountOut; iChannelOut += 1) { - ma_channel channelOut; - - /* Default to MA_CHANNEL_INDEX_NULL so that if a mapping is not found it'll be set appropriately. */ - pShuffleTable[iChannelOut] = MA_CHANNEL_INDEX_NULL; - - channelOut = ma_channel_map_get_channel(pChannelMapOut, channelCountOut, iChannelOut); - for (iChannelIn = 0; iChannelIn < channelCountIn; iChannelIn += 1) { - ma_channel channelIn; - - channelIn = ma_channel_map_get_channel(pChannelMapIn, channelCountIn, iChannelIn); - if (channelOut == channelIn) { - pShuffleTable[iChannelOut] = (ma_uint8)iChannelIn; - break; - } - - /* - Getting here means the channels don't exactly match, but we are going to support some - relaxed matching for practicality. If, for example, there are two stereo channel maps, - but one uses front left/right and the other uses side left/right, it makes logical - sense to just map these. The way we'll do it is we'll check if there is a logical - corresponding mapping, and if so, apply it, but we will *not* break from the loop, - thereby giving the loop a chance to find an exact match later which will take priority. - */ - switch (channelOut) - { - /* Left channels. */ - case MA_CHANNEL_FRONT_LEFT: - case MA_CHANNEL_SIDE_LEFT: - { - switch (channelIn) { - case MA_CHANNEL_FRONT_LEFT: - case MA_CHANNEL_SIDE_LEFT: - { - pShuffleTable[iChannelOut] = (ma_uint8)iChannelIn; - } break; - } - } break; - - /* Right channels. */ - case MA_CHANNEL_FRONT_RIGHT: - case MA_CHANNEL_SIDE_RIGHT: - { - switch (channelIn) { - case MA_CHANNEL_FRONT_RIGHT: - case MA_CHANNEL_SIDE_RIGHT: - { - pShuffleTable[iChannelOut] = (ma_uint8)iChannelIn; - } break; - } - } break; - - default: break; - } - } - } - - return MA_SUCCESS; -} - - -static void ma_channel_map_apply_shuffle_table_u8(ma_uint8* pFramesOut, ma_uint32 channelsOut, const ma_uint8* pFramesIn, ma_uint32 channelsIn, ma_uint64 frameCount, const ma_uint8* pShuffleTable) -{ - ma_uint64 iFrame; - ma_uint32 iChannelOut; - - for (iFrame = 0; iFrame < frameCount; iFrame += 1) { - for (iChannelOut = 0; iChannelOut < channelsOut; iChannelOut += 1) { - ma_uint8 iChannelIn = pShuffleTable[iChannelOut]; - if (iChannelIn < channelsIn) { /* For safety, and to deal with MA_CHANNEL_INDEX_NULL. */ - pFramesOut[iChannelOut] = pFramesIn[iChannelIn]; - } else { - pFramesOut[iChannelOut] = 0; - } - } - - pFramesOut += channelsOut; - pFramesIn += channelsIn; - } -} - -static void ma_channel_map_apply_shuffle_table_s16(ma_int16* pFramesOut, ma_uint32 channelsOut, const ma_int16* pFramesIn, ma_uint32 channelsIn, ma_uint64 frameCount, const ma_uint8* pShuffleTable) -{ - ma_uint64 iFrame; - ma_uint32 iChannelOut; - - for (iFrame = 0; iFrame < frameCount; iFrame += 1) { - for (iChannelOut = 0; iChannelOut < channelsOut; iChannelOut += 1) { - ma_uint8 iChannelIn = pShuffleTable[iChannelOut]; - if (iChannelIn < channelsIn) { /* For safety, and to deal with MA_CHANNEL_INDEX_NULL. */ - pFramesOut[iChannelOut] = pFramesIn[iChannelIn]; - } else { - pFramesOut[iChannelOut] = 0; - } - } - - pFramesOut += channelsOut; - pFramesIn += channelsIn; - } -} - -static void ma_channel_map_apply_shuffle_table_s24(ma_uint8* pFramesOut, ma_uint32 channelsOut, const ma_uint8* pFramesIn, ma_uint32 channelsIn, ma_uint64 frameCount, const ma_uint8* pShuffleTable) -{ - ma_uint64 iFrame; - ma_uint32 iChannelOut; - - for (iFrame = 0; iFrame < frameCount; iFrame += 1) { - for (iChannelOut = 0; iChannelOut < channelsOut; iChannelOut += 1) { - ma_uint8 iChannelIn = pShuffleTable[iChannelOut]; - if (iChannelIn < channelsIn) { /* For safety, and to deal with MA_CHANNEL_INDEX_NULL. */ - pFramesOut[iChannelOut*3 + 0] = pFramesIn[iChannelIn*3 + 0]; - pFramesOut[iChannelOut*3 + 1] = pFramesIn[iChannelIn*3 + 1]; - pFramesOut[iChannelOut*3 + 2] = pFramesIn[iChannelIn*3 + 2]; - } else { - pFramesOut[iChannelOut*3 + 0] = 0; - } pFramesOut[iChannelOut*3 + 1] = 0; - } pFramesOut[iChannelOut*3 + 2] = 0; - - pFramesOut += channelsOut*3; - pFramesIn += channelsIn*3; - } -} - -static void ma_channel_map_apply_shuffle_table_s32(ma_int32* pFramesOut, ma_uint32 channelsOut, const ma_int32* pFramesIn, ma_uint32 channelsIn, ma_uint64 frameCount, const ma_uint8* pShuffleTable) -{ - ma_uint64 iFrame; - ma_uint32 iChannelOut; - - for (iFrame = 0; iFrame < frameCount; iFrame += 1) { - for (iChannelOut = 0; iChannelOut < channelsOut; iChannelOut += 1) { - ma_uint8 iChannelIn = pShuffleTable[iChannelOut]; - if (iChannelIn < channelsIn) { /* For safety, and to deal with MA_CHANNEL_INDEX_NULL. */ - pFramesOut[iChannelOut] = pFramesIn[iChannelIn]; - } else { - pFramesOut[iChannelOut] = 0; - } - } - - pFramesOut += channelsOut; - pFramesIn += channelsIn; - } -} - -static void ma_channel_map_apply_shuffle_table_f32(float* pFramesOut, ma_uint32 channelsOut, const float* pFramesIn, ma_uint32 channelsIn, ma_uint64 frameCount, const ma_uint8* pShuffleTable) -{ - ma_uint64 iFrame; - ma_uint32 iChannelOut; - - for (iFrame = 0; iFrame < frameCount; iFrame += 1) { - for (iChannelOut = 0; iChannelOut < channelsOut; iChannelOut += 1) { - ma_uint8 iChannelIn = pShuffleTable[iChannelOut]; - if (iChannelIn < channelsIn) { /* For safety, and to deal with MA_CHANNEL_INDEX_NULL. */ - pFramesOut[iChannelOut] = pFramesIn[iChannelIn]; - } else { - pFramesOut[iChannelOut] = 0; - } - } - - pFramesOut += channelsOut; - pFramesIn += channelsIn; - } -} - -static ma_result ma_channel_map_apply_shuffle_table(void* pFramesOut, ma_uint32 channelsOut, const void* pFramesIn, ma_uint32 channelsIn, ma_uint64 frameCount, const ma_uint8* pShuffleTable, ma_format format) -{ - if (pFramesOut == NULL || pFramesIn == NULL || channelsOut == 0 || pShuffleTable == NULL) { - return MA_INVALID_ARGS; - } - - switch (format) - { - case ma_format_u8: - { - ma_channel_map_apply_shuffle_table_u8((ma_uint8*)pFramesOut, channelsOut, (const ma_uint8*)pFramesIn, channelsIn, frameCount, pShuffleTable); - } break; - - case ma_format_s16: - { - ma_channel_map_apply_shuffle_table_s16((ma_int16*)pFramesOut, channelsOut, (const ma_int16*)pFramesIn, channelsIn, frameCount, pShuffleTable); - } break; - - case ma_format_s24: - { - ma_channel_map_apply_shuffle_table_s24((ma_uint8*)pFramesOut, channelsOut, (const ma_uint8*)pFramesIn, channelsIn, frameCount, pShuffleTable); - } break; - - case ma_format_s32: - { - ma_channel_map_apply_shuffle_table_s32((ma_int32*)pFramesOut, channelsOut, (const ma_int32*)pFramesIn, channelsIn, frameCount, pShuffleTable); - } break; - - case ma_format_f32: - { - ma_channel_map_apply_shuffle_table_f32((float*)pFramesOut, channelsOut, (const float*)pFramesIn, channelsIn, frameCount, pShuffleTable); - } break; - - default: return MA_INVALID_ARGS; /* Unknown format. */ - } - - return MA_SUCCESS; -} - -static ma_result ma_channel_map_apply_mono_out_f32(float* pFramesOut, const float* pFramesIn, const ma_channel* pChannelMapIn, ma_uint32 channelsIn, ma_uint64 frameCount) -{ - ma_uint64 iFrame; - ma_uint32 iChannelIn; - ma_uint32 accumulationCount; - - if (pFramesOut == NULL || pFramesIn == NULL || channelsIn == 0) { - return MA_INVALID_ARGS; - } - - /* In this case the output stream needs to be the average of all channels, ignoring NONE. */ - - /* A quick pre-processing step to get the accumulation counter since we're ignoring NONE channels. */ - accumulationCount = 0; - for (iChannelIn = 0; iChannelIn < channelsIn; iChannelIn += 1) { - if (ma_channel_map_get_channel(pChannelMapIn, channelsIn, iChannelIn) != MA_CHANNEL_NONE) { - accumulationCount += 1; - } - } - - if (accumulationCount > 0) { /* <-- Prevent a division by zero. */ - for (iFrame = 0; iFrame < frameCount; iFrame += 1) { - float accumulation = 0; - - for (iChannelIn = 0; iChannelIn < channelsIn; iChannelIn += 1) { - ma_channel channelIn = ma_channel_map_get_channel(pChannelMapIn, channelsIn, iChannelIn); - if (channelIn != MA_CHANNEL_NONE) { - accumulation += pFramesIn[iChannelIn]; - } - } - - pFramesOut[0] = accumulation / accumulationCount; - pFramesOut += 1; - pFramesIn += channelsIn; - } - } else { - ma_silence_pcm_frames(pFramesOut, frameCount, ma_format_f32, 1); - } - - return MA_SUCCESS; -} - -static ma_result ma_channel_map_apply_mono_in_f32(float* MA_RESTRICT pFramesOut, const ma_channel* pChannelMapOut, ma_uint32 channelsOut, const float* MA_RESTRICT pFramesIn, ma_uint64 frameCount, ma_mono_expansion_mode monoExpansionMode) -{ - ma_uint64 iFrame; - ma_uint32 iChannelOut; - - if (pFramesOut == NULL || channelsOut == 0 || pFramesIn == NULL) { - return MA_INVALID_ARGS; - } - - /* Note that the MA_CHANNEL_NONE channel must be ignored in all cases. */ - switch (monoExpansionMode) - { - case ma_mono_expansion_mode_average: - { - float weight; - ma_uint32 validChannelCount = 0; - - for (iChannelOut = 0; iChannelOut < channelsOut; iChannelOut += 1) { - ma_channel channelOut = ma_channel_map_get_channel(pChannelMapOut, channelsOut, iChannelOut); - if (channelOut != MA_CHANNEL_NONE) { - validChannelCount += 1; - } - } - - weight = 1.0f / validChannelCount; - - for (iFrame = 0; iFrame < frameCount; iFrame += 1) { - for (iChannelOut = 0; iChannelOut < channelsOut; iChannelOut += 1) { - ma_channel channelOut = ma_channel_map_get_channel(pChannelMapOut, channelsOut, iChannelOut); - if (channelOut != MA_CHANNEL_NONE) { - pFramesOut[iChannelOut] = pFramesIn[0] * weight; - } - } - - pFramesOut += channelsOut; - pFramesIn += 1; - } - } break; - - case ma_mono_expansion_mode_stereo_only: - { - if (channelsOut >= 2) { - ma_uint32 iChannelLeft = (ma_uint32)-1; - ma_uint32 iChannelRight = (ma_uint32)-1; - - /* - We first need to find our stereo channels. We prefer front-left and front-right, but - if they're not available, we'll also try side-left and side-right. If neither are - available we'll fall through to the default case below. - */ - for (iChannelOut = 0; iChannelOut < channelsOut; iChannelOut += 1) { - ma_channel channelOut = ma_channel_map_get_channel(pChannelMapOut, channelsOut, iChannelOut); - if (channelOut == MA_CHANNEL_SIDE_LEFT) { - iChannelLeft = iChannelOut; - } - if (channelOut == MA_CHANNEL_SIDE_RIGHT) { - iChannelRight = iChannelOut; - } - } - - for (iChannelOut = 0; iChannelOut < channelsOut; iChannelOut += 1) { - ma_channel channelOut = ma_channel_map_get_channel(pChannelMapOut, channelsOut, iChannelOut); - if (channelOut == MA_CHANNEL_FRONT_LEFT) { - iChannelLeft = iChannelOut; - } - if (channelOut == MA_CHANNEL_FRONT_RIGHT) { - iChannelRight = iChannelOut; - } - } - - - if (iChannelLeft != (ma_uint32)-1 && iChannelRight != (ma_uint32)-1) { - /* We found our stereo channels so we can duplicate the signal across those channels. */ - for (iFrame = 0; iFrame < frameCount; iFrame += 1) { - for (iChannelOut = 0; iChannelOut < channelsOut; iChannelOut += 1) { - ma_channel channelOut = ma_channel_map_get_channel(pChannelMapOut, channelsOut, iChannelOut); - if (channelOut != MA_CHANNEL_NONE) { - if (iChannelOut == iChannelLeft || iChannelOut == iChannelRight) { - pFramesOut[iChannelOut] = pFramesIn[0]; - } else { - pFramesOut[iChannelOut] = 0.0f; - } - } - } - - pFramesOut += channelsOut; - pFramesIn += 1; - } - - break; /* Get out of the switch. */ - } else { - /* Fallthrough. Does not have left and right channels. */ - goto default_handler; - } - } else { - /* Fallthrough. Does not have stereo channels. */ - goto default_handler; - } - }; /* Fallthrough. See comments above. */ - - case ma_mono_expansion_mode_duplicate: - default: - { - default_handler: - { - if (channelsOut <= MA_MAX_CHANNELS) { - ma_bool32 hasEmptyChannel = MA_FALSE; - ma_channel channelPositions[MA_MAX_CHANNELS]; - for (iChannelOut = 0; iChannelOut < channelsOut; iChannelOut += 1) { - channelPositions[iChannelOut] = ma_channel_map_get_channel(pChannelMapOut, channelsOut, iChannelOut); - if (channelPositions[iChannelOut] == MA_CHANNEL_NONE) { - hasEmptyChannel = MA_TRUE; - } - } - - if (hasEmptyChannel == MA_FALSE) { - /* - Faster path when there's no MA_CHANNEL_NONE channel positions. This should hopefully - help the compiler with auto-vectorization.m - */ - if (channelsOut == 2) { - #if defined(MA_SUPPORT_SSE2) - if (ma_has_sse2()) { - /* We want to do two frames in each iteration. */ - ma_uint64 unrolledFrameCount = frameCount >> 1; - - for (iFrame = 0; iFrame < unrolledFrameCount; iFrame += 1) { - __m128 in0 = _mm_set1_ps(pFramesIn[iFrame*2 + 0]); - __m128 in1 = _mm_set1_ps(pFramesIn[iFrame*2 + 1]); - _mm_storeu_ps(&pFramesOut[iFrame*4 + 0], _mm_shuffle_ps(in0, in1, _MM_SHUFFLE(0, 0, 0, 0))); - } - - /* Tail. */ - iFrame = unrolledFrameCount << 1; - goto generic_on_fastpath; - } else - #endif - { - for (iFrame = 0; iFrame < frameCount; iFrame += 1) { - for (iChannelOut = 0; iChannelOut < 2; iChannelOut += 1) { - pFramesOut[iFrame*2 + iChannelOut] = pFramesIn[iFrame]; - } - } - } - } else if (channelsOut == 6) { - #if defined(MA_SUPPORT_SSE2) - if (ma_has_sse2()) { - /* We want to do two frames in each iteration so we can have a multiple of 4 samples. */ - ma_uint64 unrolledFrameCount = frameCount >> 1; - - for (iFrame = 0; iFrame < unrolledFrameCount; iFrame += 1) { - __m128 in0 = _mm_set1_ps(pFramesIn[iFrame*2 + 0]); - __m128 in1 = _mm_set1_ps(pFramesIn[iFrame*2 + 1]); - - _mm_storeu_ps(&pFramesOut[iFrame*12 + 0], in0); - _mm_storeu_ps(&pFramesOut[iFrame*12 + 4], _mm_shuffle_ps(in0, in1, _MM_SHUFFLE(0, 0, 0, 0))); - _mm_storeu_ps(&pFramesOut[iFrame*12 + 8], in1); - } - - /* Tail. */ - iFrame = unrolledFrameCount << 1; - goto generic_on_fastpath; - } else - #endif - { - for (iFrame = 0; iFrame < frameCount; iFrame += 1) { - for (iChannelOut = 0; iChannelOut < 6; iChannelOut += 1) { - pFramesOut[iFrame*6 + iChannelOut] = pFramesIn[iFrame]; - } - } - } - } else if (channelsOut == 8) { - #if defined(MA_SUPPORT_SSE2) - if (ma_has_sse2()) { - for (iFrame = 0; iFrame < frameCount; iFrame += 1) { - __m128 in = _mm_set1_ps(pFramesIn[iFrame]); - _mm_storeu_ps(&pFramesOut[iFrame*8 + 0], in); - _mm_storeu_ps(&pFramesOut[iFrame*8 + 4], in); - } - } else - #endif - { - for (iFrame = 0; iFrame < frameCount; iFrame += 1) { - for (iChannelOut = 0; iChannelOut < 8; iChannelOut += 1) { - pFramesOut[iFrame*8 + iChannelOut] = pFramesIn[iFrame]; - } - } - } - } else { - iFrame = 0; - - #if defined(MA_SUPPORT_SSE2) /* For silencing a warning with non-x86 builds. */ - generic_on_fastpath: - #endif - { - for (; iFrame < frameCount; iFrame += 1) { - for (iChannelOut = 0; iChannelOut < channelsOut; iChannelOut += 1) { - pFramesOut[iFrame*channelsOut + iChannelOut] = pFramesIn[iFrame]; - } - } - } - } - } else { - /* Slow path. Need to handle MA_CHANNEL_NONE. */ - for (iFrame = 0; iFrame < frameCount; iFrame += 1) { - for (iChannelOut = 0; iChannelOut < channelsOut; iChannelOut += 1) { - if (channelPositions[iChannelOut] != MA_CHANNEL_NONE) { - pFramesOut[iFrame*channelsOut + iChannelOut] = pFramesIn[iFrame]; - } - } - } - } - } else { - /* Slow path. Too many channels to store on the stack. */ - for (iFrame = 0; iFrame < frameCount; iFrame += 1) { - for (iChannelOut = 0; iChannelOut < channelsOut; iChannelOut += 1) { - ma_channel channelOut = ma_channel_map_get_channel(pChannelMapOut, channelsOut, iChannelOut); - if (channelOut != MA_CHANNEL_NONE) { - pFramesOut[iFrame*channelsOut + iChannelOut] = pFramesIn[iFrame]; - } - } - } - } - } - } break; - } - - return MA_SUCCESS; -} - -static void ma_channel_map_apply_f32(float* pFramesOut, const ma_channel* pChannelMapOut, ma_uint32 channelsOut, const float* pFramesIn, const ma_channel* pChannelMapIn, ma_uint32 channelsIn, ma_uint64 frameCount, ma_channel_mix_mode mode, ma_mono_expansion_mode monoExpansionMode) -{ - ma_channel_conversion_path conversionPath = ma_channel_map_get_conversion_path(pChannelMapIn, channelsIn, pChannelMapOut, channelsOut, mode); - - /* Optimized Path: Passthrough */ - if (conversionPath == ma_channel_conversion_path_passthrough) { - ma_copy_pcm_frames(pFramesOut, pFramesIn, frameCount, ma_format_f32, channelsOut); - return; - } - - /* Special Path: Mono Output. */ - if (conversionPath == ma_channel_conversion_path_mono_out) { - ma_channel_map_apply_mono_out_f32(pFramesOut, pFramesIn, pChannelMapIn, channelsIn, frameCount); - return; - } - - /* Special Path: Mono Input. */ - if (conversionPath == ma_channel_conversion_path_mono_in) { - ma_channel_map_apply_mono_in_f32(pFramesOut, pChannelMapOut, channelsOut, pFramesIn, frameCount, monoExpansionMode); - return; - } - - /* Getting here means we aren't running on an optimized conversion path. */ - if (channelsOut <= MA_MAX_CHANNELS) { - ma_result result; - - if (mode == ma_channel_mix_mode_simple) { - ma_channel shuffleTable[MA_MAX_CHANNELS]; - - result = ma_channel_map_build_shuffle_table(pChannelMapIn, channelsIn, pChannelMapOut, channelsOut, shuffleTable); - if (result != MA_SUCCESS) { - return; - } - - result = ma_channel_map_apply_shuffle_table(pFramesOut, channelsOut, pFramesIn, channelsIn, frameCount, shuffleTable, ma_format_f32); - if (result != MA_SUCCESS) { - return; - } - } else { - ma_uint32 iFrame; - ma_uint32 iChannelOut; - ma_uint32 iChannelIn; - float weights[32][32]; /* Do not use MA_MAX_CHANNELS here! */ - - /* - If we have a small enough number of channels, pre-compute the weights. Otherwise we'll just need to - fall back to a slower path because otherwise we'll run out of stack space. - */ - if (channelsIn <= ma_countof(weights) && channelsOut <= ma_countof(weights)) { - /* Pre-compute weights. */ - for (iChannelOut = 0; iChannelOut < channelsOut; iChannelOut += 1) { - ma_channel channelOut = ma_channel_map_get_channel(pChannelMapOut, channelsOut, iChannelOut); - for (iChannelIn = 0; iChannelIn < channelsIn; iChannelIn += 1) { - ma_channel channelIn = ma_channel_map_get_channel(pChannelMapIn, channelsIn, iChannelIn); - weights[iChannelOut][iChannelIn] = ma_calculate_channel_position_rectangular_weight(channelOut, channelIn); - } - } - - iFrame = 0; - - /* Experiment: Try an optimized unroll for some specific cases to see how it improves performance. RESULT: Good gains. */ - if (channelsOut == 8) { - /* Experiment 2: Expand the inner loop to see what kind of different it makes. RESULT: Small, but worthwhile gain. */ - if (channelsIn == 2) { - for (; iFrame < frameCount; iFrame += 1) { - float accumulation[8] = { 0, 0, 0, 0, 0, 0, 0, 0 }; - - accumulation[0] += pFramesIn[iFrame*2 + 0] * weights[0][0]; - accumulation[1] += pFramesIn[iFrame*2 + 0] * weights[1][0]; - accumulation[2] += pFramesIn[iFrame*2 + 0] * weights[2][0]; - accumulation[3] += pFramesIn[iFrame*2 + 0] * weights[3][0]; - accumulation[4] += pFramesIn[iFrame*2 + 0] * weights[4][0]; - accumulation[5] += pFramesIn[iFrame*2 + 0] * weights[5][0]; - accumulation[6] += pFramesIn[iFrame*2 + 0] * weights[6][0]; - accumulation[7] += pFramesIn[iFrame*2 + 0] * weights[7][0]; - - accumulation[0] += pFramesIn[iFrame*2 + 1] * weights[0][1]; - accumulation[1] += pFramesIn[iFrame*2 + 1] * weights[1][1]; - accumulation[2] += pFramesIn[iFrame*2 + 1] * weights[2][1]; - accumulation[3] += pFramesIn[iFrame*2 + 1] * weights[3][1]; - accumulation[4] += pFramesIn[iFrame*2 + 1] * weights[4][1]; - accumulation[5] += pFramesIn[iFrame*2 + 1] * weights[5][1]; - accumulation[6] += pFramesIn[iFrame*2 + 1] * weights[6][1]; - accumulation[7] += pFramesIn[iFrame*2 + 1] * weights[7][1]; - - pFramesOut[iFrame*8 + 0] = accumulation[0]; - pFramesOut[iFrame*8 + 1] = accumulation[1]; - pFramesOut[iFrame*8 + 2] = accumulation[2]; - pFramesOut[iFrame*8 + 3] = accumulation[3]; - pFramesOut[iFrame*8 + 4] = accumulation[4]; - pFramesOut[iFrame*8 + 5] = accumulation[5]; - pFramesOut[iFrame*8 + 6] = accumulation[6]; - pFramesOut[iFrame*8 + 7] = accumulation[7]; - } - } else { - /* When outputting to 8 channels, we can do everything in groups of two 4x SIMD operations. */ - for (; iFrame < frameCount; iFrame += 1) { - float accumulation[8] = { 0, 0, 0, 0, 0, 0, 0, 0 }; - - for (iChannelIn = 0; iChannelIn < channelsIn; iChannelIn += 1) { - accumulation[0] += pFramesIn[iFrame*channelsIn + iChannelIn] * weights[0][iChannelIn]; - accumulation[1] += pFramesIn[iFrame*channelsIn + iChannelIn] * weights[1][iChannelIn]; - accumulation[2] += pFramesIn[iFrame*channelsIn + iChannelIn] * weights[2][iChannelIn]; - accumulation[3] += pFramesIn[iFrame*channelsIn + iChannelIn] * weights[3][iChannelIn]; - accumulation[4] += pFramesIn[iFrame*channelsIn + iChannelIn] * weights[4][iChannelIn]; - accumulation[5] += pFramesIn[iFrame*channelsIn + iChannelIn] * weights[5][iChannelIn]; - accumulation[6] += pFramesIn[iFrame*channelsIn + iChannelIn] * weights[6][iChannelIn]; - accumulation[7] += pFramesIn[iFrame*channelsIn + iChannelIn] * weights[7][iChannelIn]; - } - - pFramesOut[iFrame*8 + 0] = accumulation[0]; - pFramesOut[iFrame*8 + 1] = accumulation[1]; - pFramesOut[iFrame*8 + 2] = accumulation[2]; - pFramesOut[iFrame*8 + 3] = accumulation[3]; - pFramesOut[iFrame*8 + 4] = accumulation[4]; - pFramesOut[iFrame*8 + 5] = accumulation[5]; - pFramesOut[iFrame*8 + 6] = accumulation[6]; - pFramesOut[iFrame*8 + 7] = accumulation[7]; - } - } - } else if (channelsOut == 6) { - /* - When outputting to 6 channels we unfortunately don't have a nice multiple of 4 to do 4x SIMD operations. Instead we'll - expand our weights and do two frames at a time. - */ - for (; iFrame < frameCount; iFrame += 1) { - float accumulation[12] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; - - for (iChannelIn = 0; iChannelIn < channelsIn; iChannelIn += 1) { - accumulation[0] += pFramesIn[iFrame*channelsIn + iChannelIn] * weights[0][iChannelIn]; - accumulation[1] += pFramesIn[iFrame*channelsIn + iChannelIn] * weights[1][iChannelIn]; - accumulation[2] += pFramesIn[iFrame*channelsIn + iChannelIn] * weights[2][iChannelIn]; - accumulation[3] += pFramesIn[iFrame*channelsIn + iChannelIn] * weights[3][iChannelIn]; - accumulation[4] += pFramesIn[iFrame*channelsIn + iChannelIn] * weights[4][iChannelIn]; - accumulation[5] += pFramesIn[iFrame*channelsIn + iChannelIn] * weights[5][iChannelIn]; - } - - pFramesOut[iFrame*6 + 0] = accumulation[0]; - pFramesOut[iFrame*6 + 1] = accumulation[1]; - pFramesOut[iFrame*6 + 2] = accumulation[2]; - pFramesOut[iFrame*6 + 3] = accumulation[3]; - pFramesOut[iFrame*6 + 4] = accumulation[4]; - pFramesOut[iFrame*6 + 5] = accumulation[5]; - } - } - - /* Leftover frames. */ - for (; iFrame < frameCount; iFrame += 1) { - for (iChannelOut = 0; iChannelOut < channelsOut; iChannelOut += 1) { - float accumulation = 0; - - for (iChannelIn = 0; iChannelIn < channelsIn; iChannelIn += 1) { - accumulation += pFramesIn[iFrame*channelsIn + iChannelIn] * weights[iChannelOut][iChannelIn]; - } - - pFramesOut[iFrame*channelsOut + iChannelOut] = accumulation; - } - } - } else { - /* Cannot pre-compute weights because not enough room in stack-allocated buffer. */ - for (iFrame = 0; iFrame < frameCount; iFrame += 1) { - for (iChannelOut = 0; iChannelOut < channelsOut; iChannelOut += 1) { - float accumulation = 0; - ma_channel channelOut = ma_channel_map_get_channel(pChannelMapOut, channelsOut, iChannelOut); - - for (iChannelIn = 0; iChannelIn < channelsIn; iChannelIn += 1) { - ma_channel channelIn = ma_channel_map_get_channel(pChannelMapIn, channelsIn, iChannelIn); - accumulation += pFramesIn[iFrame*channelsIn + iChannelIn] * ma_calculate_channel_position_rectangular_weight(channelOut, channelIn); - } - - pFramesOut[iFrame*channelsOut + iChannelOut] = accumulation; - } - } - } - } - } else { - /* Fall back to silence. If you hit this, what are you doing with so many channels?! */ - ma_silence_pcm_frames(pFramesOut, frameCount, ma_format_f32, channelsOut); - } -} - - -typedef struct -{ - size_t sizeInBytes; - size_t channelMapInOffset; - size_t channelMapOutOffset; - size_t shuffleTableOffset; - size_t weightsOffset; -} ma_channel_converter_heap_layout; - -static ma_channel_conversion_path ma_channel_converter_config_get_conversion_path(const ma_channel_converter_config* pConfig) -{ - return ma_channel_map_get_conversion_path(pConfig->pChannelMapIn, pConfig->channelsIn, pConfig->pChannelMapOut, pConfig->channelsOut, pConfig->mixingMode); -} - -static ma_result ma_channel_converter_get_heap_layout(const ma_channel_converter_config* pConfig, ma_channel_converter_heap_layout* pHeapLayout) -{ - ma_channel_conversion_path conversionPath; - - MA_ASSERT(pHeapLayout != NULL); - - if (pConfig == NULL) { - return MA_INVALID_ARGS; - } - - if (pConfig->channelsIn == 0 || pConfig->channelsOut == 0) { - return MA_INVALID_ARGS; - } - - if (!ma_channel_map_is_valid(pConfig->pChannelMapIn, pConfig->channelsIn)) { - return MA_INVALID_ARGS; - } - - if (!ma_channel_map_is_valid(pConfig->pChannelMapOut, pConfig->channelsOut)) { - return MA_INVALID_ARGS; - } - - pHeapLayout->sizeInBytes = 0; - - /* Input channel map. Only need to allocate this if we have an input channel map (otherwise default channel map is assumed). */ - pHeapLayout->channelMapInOffset = pHeapLayout->sizeInBytes; - if (pConfig->pChannelMapIn != NULL) { - pHeapLayout->sizeInBytes += sizeof(ma_channel) * pConfig->channelsIn; - } - - /* Output channel map. Only need to allocate this if we have an output channel map (otherwise default channel map is assumed). */ - pHeapLayout->channelMapOutOffset = pHeapLayout->sizeInBytes; - if (pConfig->pChannelMapOut != NULL) { - pHeapLayout->sizeInBytes += sizeof(ma_channel) * pConfig->channelsOut; - } - - /* Alignment for the next section. */ - pHeapLayout->sizeInBytes = ma_align_64(pHeapLayout->sizeInBytes); - - /* Whether or not we use weights of a shuffle table depends on the channel map themselves and the algorithm we've chosen. */ - conversionPath = ma_channel_converter_config_get_conversion_path(pConfig); - - /* Shuffle table */ - pHeapLayout->shuffleTableOffset = pHeapLayout->sizeInBytes; - if (conversionPath == ma_channel_conversion_path_shuffle) { - pHeapLayout->sizeInBytes += sizeof(ma_uint8) * pConfig->channelsOut; - } - - /* Weights */ - pHeapLayout->weightsOffset = pHeapLayout->sizeInBytes; - if (conversionPath == ma_channel_conversion_path_weights) { - pHeapLayout->sizeInBytes += sizeof(float*) * pConfig->channelsIn; - pHeapLayout->sizeInBytes += sizeof(float ) * pConfig->channelsIn * pConfig->channelsOut; - } - - /* Make sure allocation size is aligned. */ - pHeapLayout->sizeInBytes = ma_align_64(pHeapLayout->sizeInBytes); - - return MA_SUCCESS; -} - -MA_API ma_result ma_channel_converter_get_heap_size(const ma_channel_converter_config* pConfig, size_t* pHeapSizeInBytes) -{ - ma_result result; - ma_channel_converter_heap_layout heapLayout; - - if (pHeapSizeInBytes == NULL) { - return MA_INVALID_ARGS; - } - - *pHeapSizeInBytes = 0; - - result = ma_channel_converter_get_heap_layout(pConfig, &heapLayout); - if (result != MA_SUCCESS) { - return result; - } - - *pHeapSizeInBytes = heapLayout.sizeInBytes; - - return MA_SUCCESS; -} - -MA_API ma_result ma_channel_converter_init_preallocated(const ma_channel_converter_config* pConfig, void* pHeap, ma_channel_converter* pConverter) -{ - ma_result result; - ma_channel_converter_heap_layout heapLayout; - - if (pConverter == NULL) { - return MA_INVALID_ARGS; - } - - MA_ZERO_OBJECT(pConverter); - - result = ma_channel_converter_get_heap_layout(pConfig, &heapLayout); - if (result != MA_SUCCESS) { - return result; - } - - pConverter->_pHeap = pHeap; - MA_ZERO_MEMORY(pConverter->_pHeap, heapLayout.sizeInBytes); - - pConverter->format = pConfig->format; - pConverter->channelsIn = pConfig->channelsIn; - pConverter->channelsOut = pConfig->channelsOut; - pConverter->mixingMode = pConfig->mixingMode; - - if (pConfig->pChannelMapIn != NULL) { - pConverter->pChannelMapIn = (ma_channel*)ma_offset_ptr(pHeap, heapLayout.channelMapInOffset); - ma_channel_map_copy_or_default(pConverter->pChannelMapIn, pConfig->channelsIn, pConfig->pChannelMapIn, pConfig->channelsIn); - } else { - pConverter->pChannelMapIn = NULL; /* Use default channel map. */ - } - - if (pConfig->pChannelMapOut != NULL) { - pConverter->pChannelMapOut = (ma_channel*)ma_offset_ptr(pHeap, heapLayout.channelMapOutOffset); - ma_channel_map_copy_or_default(pConverter->pChannelMapOut, pConfig->channelsOut, pConfig->pChannelMapOut, pConfig->channelsOut); - } else { - pConverter->pChannelMapOut = NULL; /* Use default channel map. */ - } - - pConverter->conversionPath = ma_channel_converter_config_get_conversion_path(pConfig); - - if (pConverter->conversionPath == ma_channel_conversion_path_shuffle) { - pConverter->pShuffleTable = (ma_uint8*)ma_offset_ptr(pHeap, heapLayout.shuffleTableOffset); - ma_channel_map_build_shuffle_table(pConverter->pChannelMapIn, pConverter->channelsIn, pConverter->pChannelMapOut, pConverter->channelsOut, pConverter->pShuffleTable); - } - - if (pConverter->conversionPath == ma_channel_conversion_path_weights) { - ma_uint32 iChannelIn; - ma_uint32 iChannelOut; - - if (pConverter->format == ma_format_f32) { - pConverter->weights.f32 = (float** )ma_offset_ptr(pHeap, heapLayout.weightsOffset); - for (iChannelIn = 0; iChannelIn < pConverter->channelsIn; iChannelIn += 1) { - pConverter->weights.f32[iChannelIn] = (float*)ma_offset_ptr(pHeap, heapLayout.weightsOffset + ((sizeof(float*) * pConverter->channelsIn) + (sizeof(float) * pConverter->channelsOut * iChannelIn))); - } - } else { - pConverter->weights.s16 = (ma_int32**)ma_offset_ptr(pHeap, heapLayout.weightsOffset); - for (iChannelIn = 0; iChannelIn < pConverter->channelsIn; iChannelIn += 1) { - pConverter->weights.s16[iChannelIn] = (ma_int32*)ma_offset_ptr(pHeap, heapLayout.weightsOffset + ((sizeof(ma_int32*) * pConverter->channelsIn) + (sizeof(ma_int32) * pConverter->channelsOut * iChannelIn))); - } - } - - /* Silence our weights by default. */ - for (iChannelIn = 0; iChannelIn < pConverter->channelsIn; iChannelIn += 1) { - for (iChannelOut = 0; iChannelOut < pConverter->channelsOut; iChannelOut += 1) { - if (pConverter->format == ma_format_f32) { - pConverter->weights.f32[iChannelIn][iChannelOut] = 0.0f; - } else { - pConverter->weights.s16[iChannelIn][iChannelOut] = 0; - } - } - } - - /* - We now need to fill out our weights table. This is determined by the mixing mode. - */ - - /* In all cases we need to make sure all channels that are present in both channel maps have a 1:1 mapping. */ - for (iChannelIn = 0; iChannelIn < pConverter->channelsIn; ++iChannelIn) { - ma_channel channelPosIn = ma_channel_map_get_channel(pConverter->pChannelMapIn, pConverter->channelsIn, iChannelIn); - - for (iChannelOut = 0; iChannelOut < pConverter->channelsOut; ++iChannelOut) { - ma_channel channelPosOut = ma_channel_map_get_channel(pConverter->pChannelMapOut, pConverter->channelsOut, iChannelOut); - - if (channelPosIn == channelPosOut) { - float weight = 1; - - if (pConverter->format == ma_format_f32) { - pConverter->weights.f32[iChannelIn][iChannelOut] = weight; - } else { - pConverter->weights.s16[iChannelIn][iChannelOut] = ma_channel_converter_float_to_fixed(weight); - } - } - } - } - - switch (pConverter->mixingMode) - { - case ma_channel_mix_mode_custom_weights: - { - if (pConfig->ppWeights == NULL) { - return MA_INVALID_ARGS; /* Config specified a custom weights mixing mode, but no custom weights have been specified. */ - } - - for (iChannelIn = 0; iChannelIn < pConverter->channelsIn; iChannelIn += 1) { - for (iChannelOut = 0; iChannelOut < pConverter->channelsOut; iChannelOut += 1) { - float weight = pConfig->ppWeights[iChannelIn][iChannelOut]; - - if (pConverter->format == ma_format_f32) { - pConverter->weights.f32[iChannelIn][iChannelOut] = weight; - } else { - pConverter->weights.s16[iChannelIn][iChannelOut] = ma_channel_converter_float_to_fixed(weight); - } - } - } - } break; - - case ma_channel_mix_mode_simple: - { - /* - In simple mode, only set weights for channels that have exactly matching types, leave the rest at - zero. The 1:1 mappings have already been covered before this switch statement. - */ - } break; - - case ma_channel_mix_mode_rectangular: - default: - { - /* Unmapped input channels. */ - for (iChannelIn = 0; iChannelIn < pConverter->channelsIn; ++iChannelIn) { - ma_channel channelPosIn = ma_channel_map_get_channel(pConverter->pChannelMapIn, pConverter->channelsIn, iChannelIn); - - if (ma_is_spatial_channel_position(channelPosIn)) { - if (!ma_channel_map_contains_channel_position(pConverter->channelsOut, pConverter->pChannelMapOut, channelPosIn)) { - for (iChannelOut = 0; iChannelOut < pConverter->channelsOut; ++iChannelOut) { - ma_channel channelPosOut = ma_channel_map_get_channel(pConverter->pChannelMapOut, pConverter->channelsOut, iChannelOut); - - if (ma_is_spatial_channel_position(channelPosOut)) { - float weight = 0; - if (pConverter->mixingMode == ma_channel_mix_mode_rectangular) { - weight = ma_calculate_channel_position_rectangular_weight(channelPosIn, channelPosOut); - } - - /* Only apply the weight if we haven't already got some contribution from the respective channels. */ - if (pConverter->format == ma_format_f32) { - if (pConverter->weights.f32[iChannelIn][iChannelOut] == 0) { - pConverter->weights.f32[iChannelIn][iChannelOut] = weight; - } - } else { - if (pConverter->weights.s16[iChannelIn][iChannelOut] == 0) { - pConverter->weights.s16[iChannelIn][iChannelOut] = ma_channel_converter_float_to_fixed(weight); - } - } - } - } - } - } - } - - /* Unmapped output channels. */ - for (iChannelOut = 0; iChannelOut < pConverter->channelsOut; ++iChannelOut) { - ma_channel channelPosOut = ma_channel_map_get_channel(pConverter->pChannelMapOut, pConverter->channelsOut, iChannelOut); - - if (ma_is_spatial_channel_position(channelPosOut)) { - if (!ma_channel_map_contains_channel_position(pConverter->channelsIn, pConverter->pChannelMapIn, channelPosOut)) { - for (iChannelIn = 0; iChannelIn < pConverter->channelsIn; ++iChannelIn) { - ma_channel channelPosIn = ma_channel_map_get_channel(pConverter->pChannelMapIn, pConverter->channelsIn, iChannelIn); - - if (ma_is_spatial_channel_position(channelPosIn)) { - float weight = 0; - if (pConverter->mixingMode == ma_channel_mix_mode_rectangular) { - weight = ma_calculate_channel_position_rectangular_weight(channelPosIn, channelPosOut); - } - - /* Only apply the weight if we haven't already got some contribution from the respective channels. */ - if (pConverter->format == ma_format_f32) { - if (pConverter->weights.f32[iChannelIn][iChannelOut] == 0) { - pConverter->weights.f32[iChannelIn][iChannelOut] = weight; - } - } else { - if (pConverter->weights.s16[iChannelIn][iChannelOut] == 0) { - pConverter->weights.s16[iChannelIn][iChannelOut] = ma_channel_converter_float_to_fixed(weight); - } - } - } - } - } - } - } - - /* If LFE is in the output channel map but was not present in the input channel map, configure its weight now */ - if (pConfig->calculateLFEFromSpatialChannels) { - if (!ma_channel_map_contains_channel_position(pConverter->channelsIn, pConverter->pChannelMapIn, MA_CHANNEL_LFE)) { - ma_uint32 spatialChannelCount = ma_channel_map_get_spatial_channel_count(pConverter->pChannelMapIn, pConverter->channelsIn); - ma_uint32 iChannelOutLFE; - - if (spatialChannelCount > 0 && ma_channel_map_find_channel_position(pConverter->channelsOut, pConverter->pChannelMapOut, MA_CHANNEL_LFE, &iChannelOutLFE)) { - const float weightForLFE = 1.0f / spatialChannelCount; - for (iChannelIn = 0; iChannelIn < pConverter->channelsIn; ++iChannelIn) { - const ma_channel channelPosIn = ma_channel_map_get_channel(pConverter->pChannelMapIn, pConverter->channelsIn, iChannelIn); - if (ma_is_spatial_channel_position(channelPosIn)) { - if (pConverter->format == ma_format_f32) { - if (pConverter->weights.f32[iChannelIn][iChannelOutLFE] == 0) { - pConverter->weights.f32[iChannelIn][iChannelOutLFE] = weightForLFE; - } - } else { - if (pConverter->weights.s16[iChannelIn][iChannelOutLFE] == 0) { - pConverter->weights.s16[iChannelIn][iChannelOutLFE] = ma_channel_converter_float_to_fixed(weightForLFE); - } - } - } - } - } - } - } - } break; - } - } - - return MA_SUCCESS; -} - -MA_API ma_result ma_channel_converter_init(const ma_channel_converter_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_channel_converter* pConverter) -{ - ma_result result; - size_t heapSizeInBytes; - void* pHeap; - - result = ma_channel_converter_get_heap_size(pConfig, &heapSizeInBytes); - if (result != MA_SUCCESS) { - return result; - } - - if (heapSizeInBytes > 0) { - pHeap = ma_malloc(heapSizeInBytes, pAllocationCallbacks); - if (pHeap == NULL) { - return MA_OUT_OF_MEMORY; - } - } else { - pHeap = NULL; - } - - result = ma_channel_converter_init_preallocated(pConfig, pHeap, pConverter); - if (result != MA_SUCCESS) { - ma_free(pHeap, pAllocationCallbacks); - return result; - } - - pConverter->_ownsHeap = MA_TRUE; - return MA_SUCCESS; -} - -MA_API void ma_channel_converter_uninit(ma_channel_converter* pConverter, const ma_allocation_callbacks* pAllocationCallbacks) -{ - if (pConverter == NULL) { - return; - } - - if (pConverter->_ownsHeap) { - ma_free(pConverter->_pHeap, pAllocationCallbacks); - } -} - -static ma_result ma_channel_converter_process_pcm_frames__passthrough(ma_channel_converter* pConverter, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount) -{ - MA_ASSERT(pConverter != NULL); - MA_ASSERT(pFramesOut != NULL); - MA_ASSERT(pFramesIn != NULL); - - ma_copy_memory_64(pFramesOut, pFramesIn, frameCount * ma_get_bytes_per_frame(pConverter->format, pConverter->channelsOut)); - return MA_SUCCESS; -} - -static ma_result ma_channel_converter_process_pcm_frames__shuffle(ma_channel_converter* pConverter, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount) -{ - MA_ASSERT(pConverter != NULL); - MA_ASSERT(pFramesOut != NULL); - MA_ASSERT(pFramesIn != NULL); - MA_ASSERT(pConverter->channelsIn == pConverter->channelsOut); - - return ma_channel_map_apply_shuffle_table(pFramesOut, pConverter->channelsOut, pFramesIn, pConverter->channelsIn, frameCount, pConverter->pShuffleTable, pConverter->format); -} - -static ma_result ma_channel_converter_process_pcm_frames__mono_in(ma_channel_converter* pConverter, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount) -{ - ma_uint64 iFrame; - - MA_ASSERT(pConverter != NULL); - MA_ASSERT(pFramesOut != NULL); - MA_ASSERT(pFramesIn != NULL); - MA_ASSERT(pConverter->channelsIn == 1); - - switch (pConverter->format) - { - case ma_format_u8: - { - /* */ ma_uint8* pFramesOutU8 = ( ma_uint8*)pFramesOut; - const ma_uint8* pFramesInU8 = (const ma_uint8*)pFramesIn; - - for (iFrame = 0; iFrame < frameCount; ++iFrame) { - ma_uint32 iChannel; - for (iChannel = 0; iChannel < pConverter->channelsOut; iChannel += 1) { - pFramesOutU8[iFrame*pConverter->channelsOut + iChannel] = pFramesInU8[iFrame]; - } - } - } break; - - case ma_format_s16: - { - /* */ ma_int16* pFramesOutS16 = ( ma_int16*)pFramesOut; - const ma_int16* pFramesInS16 = (const ma_int16*)pFramesIn; - - if (pConverter->channelsOut == 2) { - for (iFrame = 0; iFrame < frameCount; ++iFrame) { - pFramesOutS16[iFrame*2 + 0] = pFramesInS16[iFrame]; - pFramesOutS16[iFrame*2 + 1] = pFramesInS16[iFrame]; - } - } else { - for (iFrame = 0; iFrame < frameCount; ++iFrame) { - ma_uint32 iChannel; - for (iChannel = 0; iChannel < pConverter->channelsOut; iChannel += 1) { - pFramesOutS16[iFrame*pConverter->channelsOut + iChannel] = pFramesInS16[iFrame]; - } - } - } - } break; - - case ma_format_s24: - { - /* */ ma_uint8* pFramesOutS24 = ( ma_uint8*)pFramesOut; - const ma_uint8* pFramesInS24 = (const ma_uint8*)pFramesIn; - - for (iFrame = 0; iFrame < frameCount; ++iFrame) { - ma_uint32 iChannel; - for (iChannel = 0; iChannel < pConverter->channelsOut; iChannel += 1) { - ma_uint64 iSampleOut = iFrame*pConverter->channelsOut + iChannel; - ma_uint64 iSampleIn = iFrame; - pFramesOutS24[iSampleOut*3 + 0] = pFramesInS24[iSampleIn*3 + 0]; - pFramesOutS24[iSampleOut*3 + 1] = pFramesInS24[iSampleIn*3 + 1]; - pFramesOutS24[iSampleOut*3 + 2] = pFramesInS24[iSampleIn*3 + 2]; - } - } - } break; - - case ma_format_s32: - { - /* */ ma_int32* pFramesOutS32 = ( ma_int32*)pFramesOut; - const ma_int32* pFramesInS32 = (const ma_int32*)pFramesIn; - - for (iFrame = 0; iFrame < frameCount; ++iFrame) { - ma_uint32 iChannel; - for (iChannel = 0; iChannel < pConverter->channelsOut; iChannel += 1) { - pFramesOutS32[iFrame*pConverter->channelsOut + iChannel] = pFramesInS32[iFrame]; - } - } - } break; - - case ma_format_f32: - { - /* */ float* pFramesOutF32 = ( float*)pFramesOut; - const float* pFramesInF32 = (const float*)pFramesIn; - - if (pConverter->channelsOut == 2) { - for (iFrame = 0; iFrame < frameCount; ++iFrame) { - pFramesOutF32[iFrame*2 + 0] = pFramesInF32[iFrame]; - pFramesOutF32[iFrame*2 + 1] = pFramesInF32[iFrame]; - } - } else { - for (iFrame = 0; iFrame < frameCount; ++iFrame) { - ma_uint32 iChannel; - for (iChannel = 0; iChannel < pConverter->channelsOut; iChannel += 1) { - pFramesOutF32[iFrame*pConverter->channelsOut + iChannel] = pFramesInF32[iFrame]; - } - } - } - } break; - - default: return MA_INVALID_OPERATION; /* Unknown format. */ - } - - return MA_SUCCESS; -} - -static ma_result ma_channel_converter_process_pcm_frames__mono_out(ma_channel_converter* pConverter, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount) -{ - ma_uint64 iFrame; - ma_uint32 iChannel; - - MA_ASSERT(pConverter != NULL); - MA_ASSERT(pFramesOut != NULL); - MA_ASSERT(pFramesIn != NULL); - MA_ASSERT(pConverter->channelsOut == 1); - - switch (pConverter->format) - { - case ma_format_u8: - { - /* */ ma_uint8* pFramesOutU8 = ( ma_uint8*)pFramesOut; - const ma_uint8* pFramesInU8 = (const ma_uint8*)pFramesIn; - - for (iFrame = 0; iFrame < frameCount; ++iFrame) { - ma_int32 t = 0; - for (iChannel = 0; iChannel < pConverter->channelsIn; iChannel += 1) { - t += ma_pcm_sample_u8_to_s16_no_scale(pFramesInU8[iFrame*pConverter->channelsIn + iChannel]); - } - - pFramesOutU8[iFrame] = ma_clip_u8(t / pConverter->channelsOut); - } - } break; - - case ma_format_s16: - { - /* */ ma_int16* pFramesOutS16 = ( ma_int16*)pFramesOut; - const ma_int16* pFramesInS16 = (const ma_int16*)pFramesIn; - - for (iFrame = 0; iFrame < frameCount; ++iFrame) { - ma_int32 t = 0; - for (iChannel = 0; iChannel < pConverter->channelsIn; iChannel += 1) { - t += pFramesInS16[iFrame*pConverter->channelsIn + iChannel]; - } - - pFramesOutS16[iFrame] = (ma_int16)(t / pConverter->channelsIn); - } - } break; - - case ma_format_s24: - { - /* */ ma_uint8* pFramesOutS24 = ( ma_uint8*)pFramesOut; - const ma_uint8* pFramesInS24 = (const ma_uint8*)pFramesIn; - - for (iFrame = 0; iFrame < frameCount; ++iFrame) { - ma_int64 t = 0; - for (iChannel = 0; iChannel < pConverter->channelsIn; iChannel += 1) { - t += ma_pcm_sample_s24_to_s32_no_scale(&pFramesInS24[(iFrame*pConverter->channelsIn + iChannel)*3]); - } - - ma_pcm_sample_s32_to_s24_no_scale(t / pConverter->channelsIn, &pFramesOutS24[iFrame*3]); - } - } break; - - case ma_format_s32: - { - /* */ ma_int32* pFramesOutS32 = ( ma_int32*)pFramesOut; - const ma_int32* pFramesInS32 = (const ma_int32*)pFramesIn; - - for (iFrame = 0; iFrame < frameCount; ++iFrame) { - ma_int64 t = 0; - for (iChannel = 0; iChannel < pConverter->channelsIn; iChannel += 1) { - t += pFramesInS32[iFrame*pConverter->channelsIn + iChannel]; - } - - pFramesOutS32[iFrame] = (ma_int32)(t / pConverter->channelsIn); - } - } break; - - case ma_format_f32: - { - /* */ float* pFramesOutF32 = ( float*)pFramesOut; - const float* pFramesInF32 = (const float*)pFramesIn; - - for (iFrame = 0; iFrame < frameCount; ++iFrame) { - float t = 0; - for (iChannel = 0; iChannel < pConverter->channelsIn; iChannel += 1) { - t += pFramesInF32[iFrame*pConverter->channelsIn + iChannel]; - } - - pFramesOutF32[iFrame] = t / pConverter->channelsIn; - } - } break; - - default: return MA_INVALID_OPERATION; /* Unknown format. */ - } - - return MA_SUCCESS; -} - -static ma_result ma_channel_converter_process_pcm_frames__weights(ma_channel_converter* pConverter, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount) -{ - ma_uint32 iFrame; - ma_uint32 iChannelIn; - ma_uint32 iChannelOut; - - MA_ASSERT(pConverter != NULL); - MA_ASSERT(pFramesOut != NULL); - MA_ASSERT(pFramesIn != NULL); - - /* This is the more complicated case. Each of the output channels is accumulated with 0 or more input channels. */ - - /* Clear. */ - ma_zero_memory_64(pFramesOut, frameCount * ma_get_bytes_per_frame(pConverter->format, pConverter->channelsOut)); - - /* Accumulate. */ - switch (pConverter->format) - { - case ma_format_u8: - { - /* */ ma_uint8* pFramesOutU8 = ( ma_uint8*)pFramesOut; - const ma_uint8* pFramesInU8 = (const ma_uint8*)pFramesIn; - - for (iFrame = 0; iFrame < frameCount; iFrame += 1) { - for (iChannelIn = 0; iChannelIn < pConverter->channelsIn; ++iChannelIn) { - for (iChannelOut = 0; iChannelOut < pConverter->channelsOut; ++iChannelOut) { - ma_int16 u8_O = ma_pcm_sample_u8_to_s16_no_scale(pFramesOutU8[iFrame*pConverter->channelsOut + iChannelOut]); - ma_int16 u8_I = ma_pcm_sample_u8_to_s16_no_scale(pFramesInU8 [iFrame*pConverter->channelsIn + iChannelIn ]); - ma_int32 s = (ma_int32)ma_clamp(u8_O + ((u8_I * pConverter->weights.s16[iChannelIn][iChannelOut]) >> MA_CHANNEL_CONVERTER_FIXED_POINT_SHIFT), -128, 127); - pFramesOutU8[iFrame*pConverter->channelsOut + iChannelOut] = ma_clip_u8((ma_int16)s); - } - } - } - } break; - - case ma_format_s16: - { - /* */ ma_int16* pFramesOutS16 = ( ma_int16*)pFramesOut; - const ma_int16* pFramesInS16 = (const ma_int16*)pFramesIn; - - for (iFrame = 0; iFrame < frameCount; iFrame += 1) { - for (iChannelIn = 0; iChannelIn < pConverter->channelsIn; ++iChannelIn) { - for (iChannelOut = 0; iChannelOut < pConverter->channelsOut; ++iChannelOut) { - ma_int32 s = pFramesOutS16[iFrame*pConverter->channelsOut + iChannelOut]; - s += (pFramesInS16[iFrame*pConverter->channelsIn + iChannelIn] * pConverter->weights.s16[iChannelIn][iChannelOut]) >> MA_CHANNEL_CONVERTER_FIXED_POINT_SHIFT; - - pFramesOutS16[iFrame*pConverter->channelsOut + iChannelOut] = (ma_int16)ma_clamp(s, -32768, 32767); - } - } - } - } break; - - case ma_format_s24: - { - /* */ ma_uint8* pFramesOutS24 = ( ma_uint8*)pFramesOut; - const ma_uint8* pFramesInS24 = (const ma_uint8*)pFramesIn; - - for (iFrame = 0; iFrame < frameCount; iFrame += 1) { - for (iChannelIn = 0; iChannelIn < pConverter->channelsIn; ++iChannelIn) { - for (iChannelOut = 0; iChannelOut < pConverter->channelsOut; ++iChannelOut) { - ma_int64 s24_O = ma_pcm_sample_s24_to_s32_no_scale(&pFramesOutS24[(iFrame*pConverter->channelsOut + iChannelOut)*3]); - ma_int64 s24_I = ma_pcm_sample_s24_to_s32_no_scale(&pFramesInS24 [(iFrame*pConverter->channelsIn + iChannelIn )*3]); - ma_int64 s24 = (ma_int32)ma_clamp(s24_O + ((s24_I * pConverter->weights.s16[iChannelIn][iChannelOut]) >> MA_CHANNEL_CONVERTER_FIXED_POINT_SHIFT), -8388608, 8388607); - ma_pcm_sample_s32_to_s24_no_scale(s24, &pFramesOutS24[(iFrame*pConverter->channelsOut + iChannelOut)*3]); - } - } - } - } break; - - case ma_format_s32: - { - /* */ ma_int32* pFramesOutS32 = ( ma_int32*)pFramesOut; - const ma_int32* pFramesInS32 = (const ma_int32*)pFramesIn; - - for (iFrame = 0; iFrame < frameCount; iFrame += 1) { - for (iChannelIn = 0; iChannelIn < pConverter->channelsIn; ++iChannelIn) { - for (iChannelOut = 0; iChannelOut < pConverter->channelsOut; ++iChannelOut) { - ma_int64 s = pFramesOutS32[iFrame*pConverter->channelsOut + iChannelOut]; - s += ((ma_int64)pFramesInS32[iFrame*pConverter->channelsIn + iChannelIn] * pConverter->weights.s16[iChannelIn][iChannelOut]) >> MA_CHANNEL_CONVERTER_FIXED_POINT_SHIFT; - - pFramesOutS32[iFrame*pConverter->channelsOut + iChannelOut] = ma_clip_s32(s); - } - } - } - } break; - - case ma_format_f32: - { - /* */ float* pFramesOutF32 = ( float*)pFramesOut; - const float* pFramesInF32 = (const float*)pFramesIn; - - for (iFrame = 0; iFrame < frameCount; iFrame += 1) { - for (iChannelIn = 0; iChannelIn < pConverter->channelsIn; ++iChannelIn) { - for (iChannelOut = 0; iChannelOut < pConverter->channelsOut; ++iChannelOut) { - pFramesOutF32[iFrame*pConverter->channelsOut + iChannelOut] += pFramesInF32[iFrame*pConverter->channelsIn + iChannelIn] * pConverter->weights.f32[iChannelIn][iChannelOut]; - } - } - } - } break; - - default: return MA_INVALID_OPERATION; /* Unknown format. */ - } - - return MA_SUCCESS; -} - -MA_API ma_result ma_channel_converter_process_pcm_frames(ma_channel_converter* pConverter, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount) -{ - if (pConverter == NULL) { - return MA_INVALID_ARGS; - } - - if (pFramesOut == NULL) { - return MA_INVALID_ARGS; - } - - if (pFramesIn == NULL) { - ma_zero_memory_64(pFramesOut, frameCount * ma_get_bytes_per_frame(pConverter->format, pConverter->channelsOut)); - return MA_SUCCESS; - } - - switch (pConverter->conversionPath) - { - case ma_channel_conversion_path_passthrough: return ma_channel_converter_process_pcm_frames__passthrough(pConverter, pFramesOut, pFramesIn, frameCount); - case ma_channel_conversion_path_mono_out: return ma_channel_converter_process_pcm_frames__mono_out(pConverter, pFramesOut, pFramesIn, frameCount); - case ma_channel_conversion_path_mono_in: return ma_channel_converter_process_pcm_frames__mono_in(pConverter, pFramesOut, pFramesIn, frameCount); - case ma_channel_conversion_path_shuffle: return ma_channel_converter_process_pcm_frames__shuffle(pConverter, pFramesOut, pFramesIn, frameCount); - case ma_channel_conversion_path_weights: - default: - { - return ma_channel_converter_process_pcm_frames__weights(pConverter, pFramesOut, pFramesIn, frameCount); - } - } -} - -MA_API ma_result ma_channel_converter_get_input_channel_map(const ma_channel_converter* pConverter, ma_channel* pChannelMap, size_t channelMapCap) -{ - if (pConverter == NULL || pChannelMap == NULL) { - return MA_INVALID_ARGS; - } - - ma_channel_map_copy_or_default(pChannelMap, channelMapCap, pConverter->pChannelMapIn, pConverter->channelsIn); - - return MA_SUCCESS; -} - -MA_API ma_result ma_channel_converter_get_output_channel_map(const ma_channel_converter* pConverter, ma_channel* pChannelMap, size_t channelMapCap) -{ - if (pConverter == NULL || pChannelMap == NULL) { - return MA_INVALID_ARGS; - } - - ma_channel_map_copy_or_default(pChannelMap, channelMapCap, pConverter->pChannelMapOut, pConverter->channelsOut); - - return MA_SUCCESS; -} - - -/************************************************************************************************************************************************************** - -Data Conversion - -**************************************************************************************************************************************************************/ -MA_API ma_data_converter_config ma_data_converter_config_init_default(void) -{ - ma_data_converter_config config; - MA_ZERO_OBJECT(&config); - - config.ditherMode = ma_dither_mode_none; - config.resampling.algorithm = ma_resample_algorithm_linear; - config.allowDynamicSampleRate = MA_FALSE; /* Disable dynamic sample rates by default because dynamic rate adjustments should be quite rare and it allows an optimization for cases when the in and out sample rates are the same. */ - - /* Linear resampling defaults. */ - config.resampling.linear.lpfOrder = 1; - - return config; -} - -MA_API ma_data_converter_config ma_data_converter_config_init(ma_format formatIn, ma_format formatOut, ma_uint32 channelsIn, ma_uint32 channelsOut, ma_uint32 sampleRateIn, ma_uint32 sampleRateOut) -{ - ma_data_converter_config config = ma_data_converter_config_init_default(); - config.formatIn = formatIn; - config.formatOut = formatOut; - config.channelsIn = channelsIn; - config.channelsOut = channelsOut; - config.sampleRateIn = sampleRateIn; - config.sampleRateOut = sampleRateOut; - - return config; -} - - -typedef struct -{ - size_t sizeInBytes; - size_t channelConverterOffset; - size_t resamplerOffset; -} ma_data_converter_heap_layout; - -static ma_bool32 ma_data_converter_config_is_resampler_required(const ma_data_converter_config* pConfig) -{ - MA_ASSERT(pConfig != NULL); - - return pConfig->allowDynamicSampleRate || pConfig->sampleRateIn != pConfig->sampleRateOut; -} - -static ma_format ma_data_converter_config_get_mid_format(const ma_data_converter_config* pConfig) -{ - MA_ASSERT(pConfig != NULL); - - /* - We want to avoid as much data conversion as possible. The channel converter and linear - resampler both support s16 and f32 natively. We need to decide on the format to use for this - stage. We call this the mid format because it's used in the middle stage of the conversion - pipeline. If the output format is either s16 or f32 we use that one. If that is not the case it - will do the same thing for the input format. If it's neither we just use f32. If we are using a - custom resampling backend, we can only guarantee that f32 will be supported so we'll be forced - to use that if resampling is required. - */ - if (ma_data_converter_config_is_resampler_required(pConfig) && pConfig->resampling.algorithm != ma_resample_algorithm_linear) { - return ma_format_f32; /* <-- Force f32 since that is the only one we can guarantee will be supported by the resampler. */ - } else { - /* */ if (pConfig->formatOut == ma_format_s16 || pConfig->formatOut == ma_format_f32) { - return pConfig->formatOut; - } else if (pConfig->formatIn == ma_format_s16 || pConfig->formatIn == ma_format_f32) { - return pConfig->formatIn; - } else { - return ma_format_f32; - } - } -} - -static ma_channel_converter_config ma_channel_converter_config_init_from_data_converter_config(const ma_data_converter_config* pConfig) -{ - ma_channel_converter_config channelConverterConfig; - - MA_ASSERT(pConfig != NULL); - - channelConverterConfig = ma_channel_converter_config_init(ma_data_converter_config_get_mid_format(pConfig), pConfig->channelsIn, pConfig->pChannelMapIn, pConfig->channelsOut, pConfig->pChannelMapOut, pConfig->channelMixMode); - channelConverterConfig.ppWeights = pConfig->ppChannelWeights; - channelConverterConfig.calculateLFEFromSpatialChannels = pConfig->calculateLFEFromSpatialChannels; - - return channelConverterConfig; -} - -static ma_resampler_config ma_resampler_config_init_from_data_converter_config(const ma_data_converter_config* pConfig) -{ - ma_resampler_config resamplerConfig; - ma_uint32 resamplerChannels; - - MA_ASSERT(pConfig != NULL); - - /* The resampler is the most expensive part of the conversion process, so we need to do it at the stage where the channel count is at it's lowest. */ - if (pConfig->channelsIn < pConfig->channelsOut) { - resamplerChannels = pConfig->channelsIn; - } else { - resamplerChannels = pConfig->channelsOut; - } - - resamplerConfig = ma_resampler_config_init(ma_data_converter_config_get_mid_format(pConfig), resamplerChannels, pConfig->sampleRateIn, pConfig->sampleRateOut, pConfig->resampling.algorithm); - resamplerConfig.linear = pConfig->resampling.linear; - resamplerConfig.pBackendVTable = pConfig->resampling.pBackendVTable; - resamplerConfig.pBackendUserData = pConfig->resampling.pBackendUserData; - - return resamplerConfig; -} - -static ma_result ma_data_converter_get_heap_layout(const ma_data_converter_config* pConfig, ma_data_converter_heap_layout* pHeapLayout) -{ - ma_result result; - - MA_ASSERT(pHeapLayout != NULL); - - MA_ZERO_OBJECT(pHeapLayout); - - if (pConfig == NULL) { - return MA_INVALID_ARGS; - } - - if (pConfig->channelsIn == 0 || pConfig->channelsOut == 0) { - return MA_INVALID_ARGS; - } - - pHeapLayout->sizeInBytes = 0; - - /* Channel converter. */ - pHeapLayout->channelConverterOffset = pHeapLayout->sizeInBytes; - { - size_t heapSizeInBytes; - ma_channel_converter_config channelConverterConfig = ma_channel_converter_config_init_from_data_converter_config(pConfig); - - result = ma_channel_converter_get_heap_size(&channelConverterConfig, &heapSizeInBytes); - if (result != MA_SUCCESS) { - return result; - } - - pHeapLayout->sizeInBytes += heapSizeInBytes; - } - - /* Resampler. */ - pHeapLayout->resamplerOffset = pHeapLayout->sizeInBytes; - if (ma_data_converter_config_is_resampler_required(pConfig)) { - size_t heapSizeInBytes; - ma_resampler_config resamplerConfig = ma_resampler_config_init_from_data_converter_config(pConfig); - - result = ma_resampler_get_heap_size(&resamplerConfig, &heapSizeInBytes); - if (result != MA_SUCCESS) { - return result; - } - - pHeapLayout->sizeInBytes += heapSizeInBytes; - } - - /* Make sure allocation size is aligned. */ - pHeapLayout->sizeInBytes = ma_align_64(pHeapLayout->sizeInBytes); - - return MA_SUCCESS; -} - -MA_API ma_result ma_data_converter_get_heap_size(const ma_data_converter_config* pConfig, size_t* pHeapSizeInBytes) -{ - ma_result result; - ma_data_converter_heap_layout heapLayout; - - if (pHeapSizeInBytes == NULL) { - return MA_INVALID_ARGS; - } - - *pHeapSizeInBytes = 0; - - result = ma_data_converter_get_heap_layout(pConfig, &heapLayout); - if (result != MA_SUCCESS) { - return result; - } - - *pHeapSizeInBytes = heapLayout.sizeInBytes; - - return MA_SUCCESS; -} - -MA_API ma_result ma_data_converter_init_preallocated(const ma_data_converter_config* pConfig, void* pHeap, ma_data_converter* pConverter) -{ - ma_result result; - ma_data_converter_heap_layout heapLayout; - ma_format midFormat; - ma_bool32 isResamplingRequired; - - if (pConverter == NULL) { - return MA_INVALID_ARGS; - } - - MA_ZERO_OBJECT(pConverter); - - result = ma_data_converter_get_heap_layout(pConfig, &heapLayout); - if (result != MA_SUCCESS) { - return result; - } - - pConverter->_pHeap = pHeap; - MA_ZERO_MEMORY(pHeap, heapLayout.sizeInBytes); - - pConverter->formatIn = pConfig->formatIn; - pConverter->formatOut = pConfig->formatOut; - pConverter->channelsIn = pConfig->channelsIn; - pConverter->channelsOut = pConfig->channelsOut; - pConverter->sampleRateIn = pConfig->sampleRateIn; - pConverter->sampleRateOut = pConfig->sampleRateOut; - pConverter->ditherMode = pConfig->ditherMode; - - /* - Determine if resampling is required. We need to do this so we can determine an appropriate - mid format to use. If resampling is required, the mid format must be ma_format_f32 since - that is the only one that is guaranteed to supported by custom resampling backends. - */ - isResamplingRequired = ma_data_converter_config_is_resampler_required(pConfig); - midFormat = ma_data_converter_config_get_mid_format(pConfig); - - - /* Channel converter. We always initialize this, but we check if it configures itself as a passthrough to determine whether or not it's needed. */ - { - ma_channel_converter_config channelConverterConfig = ma_channel_converter_config_init_from_data_converter_config(pConfig); - - result = ma_channel_converter_init_preallocated(&channelConverterConfig, ma_offset_ptr(pHeap, heapLayout.channelConverterOffset), &pConverter->channelConverter); - if (result != MA_SUCCESS) { - return result; - } - - /* If the channel converter is not a passthrough we need to enable it. Otherwise we can skip it. */ - if (pConverter->channelConverter.conversionPath != ma_channel_conversion_path_passthrough) { - pConverter->hasChannelConverter = MA_TRUE; - } - } - - - /* Resampler. */ - if (isResamplingRequired) { - ma_resampler_config resamplerConfig = ma_resampler_config_init_from_data_converter_config(pConfig); - - result = ma_resampler_init_preallocated(&resamplerConfig, ma_offset_ptr(pHeap, heapLayout.resamplerOffset), &pConverter->resampler); - if (result != MA_SUCCESS) { - return result; - } - - pConverter->hasResampler = MA_TRUE; - } - - - /* We can simplify pre- and post-format conversion if we have neither channel conversion nor resampling. */ - if (pConverter->hasChannelConverter == MA_FALSE && pConverter->hasResampler == MA_FALSE) { - /* We have neither channel conversion nor resampling so we'll only need one of pre- or post-format conversion, or none if the input and output formats are the same. */ - if (pConverter->formatIn == pConverter->formatOut) { - /* The formats are the same so we can just pass through. */ - pConverter->hasPreFormatConversion = MA_FALSE; - pConverter->hasPostFormatConversion = MA_FALSE; - } else { - /* The formats are different so we need to do either pre- or post-format conversion. It doesn't matter which. */ - pConverter->hasPreFormatConversion = MA_FALSE; - pConverter->hasPostFormatConversion = MA_TRUE; - } - } else { - /* We have a channel converter and/or resampler so we'll need channel conversion based on the mid format. */ - if (pConverter->formatIn != midFormat) { - pConverter->hasPreFormatConversion = MA_TRUE; - } - if (pConverter->formatOut != midFormat) { - pConverter->hasPostFormatConversion = MA_TRUE; - } - } - - /* We can enable passthrough optimizations if applicable. Note that we'll only be able to do this if the sample rate is static. */ - if (pConverter->hasPreFormatConversion == MA_FALSE && - pConverter->hasPostFormatConversion == MA_FALSE && - pConverter->hasChannelConverter == MA_FALSE && - pConverter->hasResampler == MA_FALSE) { - pConverter->isPassthrough = MA_TRUE; - } - - - /* We now need to determine our execution path. */ - if (pConverter->isPassthrough) { - pConverter->executionPath = ma_data_converter_execution_path_passthrough; - } else { - if (pConverter->channelsIn < pConverter->channelsOut) { - /* Do resampling first, if necessary. */ - MA_ASSERT(pConverter->hasChannelConverter == MA_TRUE); - - if (pConverter->hasResampler) { - pConverter->executionPath = ma_data_converter_execution_path_resample_first; - } else { - pConverter->executionPath = ma_data_converter_execution_path_channels_only; - } - } else { - /* Do channel conversion first, if necessary. */ - if (pConverter->hasChannelConverter) { - if (pConverter->hasResampler) { - pConverter->executionPath = ma_data_converter_execution_path_channels_first; - } else { - pConverter->executionPath = ma_data_converter_execution_path_channels_only; - } - } else { - /* Channel routing not required. */ - if (pConverter->hasResampler) { - pConverter->executionPath = ma_data_converter_execution_path_resample_only; - } else { - pConverter->executionPath = ma_data_converter_execution_path_format_only; - } - } - } - } - - return MA_SUCCESS; -} - -MA_API ma_result ma_data_converter_init(const ma_data_converter_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_data_converter* pConverter) -{ - ma_result result; - size_t heapSizeInBytes; - void* pHeap; - - result = ma_data_converter_get_heap_size(pConfig, &heapSizeInBytes); - if (result != MA_SUCCESS) { - return result; - } - - if (heapSizeInBytes > 0) { - pHeap = ma_malloc(heapSizeInBytes, pAllocationCallbacks); - if (pHeap == NULL) { - return MA_OUT_OF_MEMORY; - } - } else { - pHeap = NULL; - } - - result = ma_data_converter_init_preallocated(pConfig, pHeap, pConverter); - if (result != MA_SUCCESS) { - ma_free(pHeap, pAllocationCallbacks); - return result; - } - - pConverter->_ownsHeap = MA_TRUE; - return MA_SUCCESS; -} - -MA_API void ma_data_converter_uninit(ma_data_converter* pConverter, const ma_allocation_callbacks* pAllocationCallbacks) -{ - if (pConverter == NULL) { - return; - } - - if (pConverter->hasResampler) { - ma_resampler_uninit(&pConverter->resampler, pAllocationCallbacks); - } - - ma_channel_converter_uninit(&pConverter->channelConverter, pAllocationCallbacks); - - if (pConverter->_ownsHeap) { - ma_free(pConverter->_pHeap, pAllocationCallbacks); - } -} - -static ma_result ma_data_converter_process_pcm_frames__passthrough(ma_data_converter* pConverter, const void* pFramesIn, ma_uint64* pFrameCountIn, void* pFramesOut, ma_uint64* pFrameCountOut) -{ - ma_uint64 frameCountIn; - ma_uint64 frameCountOut; - ma_uint64 frameCount; - - MA_ASSERT(pConverter != NULL); - - frameCountIn = 0; - if (pFrameCountIn != NULL) { - frameCountIn = *pFrameCountIn; - } - - frameCountOut = 0; - if (pFrameCountOut != NULL) { - frameCountOut = *pFrameCountOut; - } - - frameCount = ma_min(frameCountIn, frameCountOut); - - if (pFramesOut != NULL) { - if (pFramesIn != NULL) { - ma_copy_memory_64(pFramesOut, pFramesIn, frameCount * ma_get_bytes_per_frame(pConverter->formatOut, pConverter->channelsOut)); - } else { - ma_zero_memory_64(pFramesOut, frameCount * ma_get_bytes_per_frame(pConverter->formatOut, pConverter->channelsOut)); - } - } - - if (pFrameCountIn != NULL) { - *pFrameCountIn = frameCount; - } - if (pFrameCountOut != NULL) { - *pFrameCountOut = frameCount; - } - - return MA_SUCCESS; -} - -static ma_result ma_data_converter_process_pcm_frames__format_only(ma_data_converter* pConverter, const void* pFramesIn, ma_uint64* pFrameCountIn, void* pFramesOut, ma_uint64* pFrameCountOut) -{ - ma_uint64 frameCountIn; - ma_uint64 frameCountOut; - ma_uint64 frameCount; - - MA_ASSERT(pConverter != NULL); - - frameCountIn = 0; - if (pFrameCountIn != NULL) { - frameCountIn = *pFrameCountIn; - } - - frameCountOut = 0; - if (pFrameCountOut != NULL) { - frameCountOut = *pFrameCountOut; - } - - frameCount = ma_min(frameCountIn, frameCountOut); - - if (pFramesOut != NULL) { - if (pFramesIn != NULL) { - ma_convert_pcm_frames_format(pFramesOut, pConverter->formatOut, pFramesIn, pConverter->formatIn, frameCount, pConverter->channelsIn, pConverter->ditherMode); - } else { - ma_zero_memory_64(pFramesOut, frameCount * ma_get_bytes_per_frame(pConverter->formatOut, pConverter->channelsOut)); - } - } - - if (pFrameCountIn != NULL) { - *pFrameCountIn = frameCount; - } - if (pFrameCountOut != NULL) { - *pFrameCountOut = frameCount; - } - - return MA_SUCCESS; -} - - -static ma_result ma_data_converter_process_pcm_frames__resample_with_format_conversion(ma_data_converter* pConverter, const void* pFramesIn, ma_uint64* pFrameCountIn, void* pFramesOut, ma_uint64* pFrameCountOut) -{ - ma_result result = MA_SUCCESS; - ma_uint64 frameCountIn; - ma_uint64 frameCountOut; - ma_uint64 framesProcessedIn; - ma_uint64 framesProcessedOut; - - MA_ASSERT(pConverter != NULL); - - frameCountIn = 0; - if (pFrameCountIn != NULL) { - frameCountIn = *pFrameCountIn; - } - - frameCountOut = 0; - if (pFrameCountOut != NULL) { - frameCountOut = *pFrameCountOut; - } - - framesProcessedIn = 0; - framesProcessedOut = 0; - - while (framesProcessedOut < frameCountOut) { - ma_uint8 pTempBufferOut[MA_DATA_CONVERTER_STACK_BUFFER_SIZE]; - const ma_uint32 tempBufferOutCap = sizeof(pTempBufferOut) / ma_get_bytes_per_frame(pConverter->resampler.format, pConverter->resampler.channels); - const void* pFramesInThisIteration; - /* */ void* pFramesOutThisIteration; - ma_uint64 frameCountInThisIteration; - ma_uint64 frameCountOutThisIteration; - - if (pFramesIn != NULL) { - pFramesInThisIteration = ma_offset_ptr(pFramesIn, framesProcessedIn * ma_get_bytes_per_frame(pConverter->formatIn, pConverter->channelsIn)); - } else { - pFramesInThisIteration = NULL; - } - - if (pFramesOut != NULL) { - pFramesOutThisIteration = ma_offset_ptr(pFramesOut, framesProcessedOut * ma_get_bytes_per_frame(pConverter->formatOut, pConverter->channelsOut)); - } else { - pFramesOutThisIteration = NULL; - } - - /* Do a pre format conversion if necessary. */ - if (pConverter->hasPreFormatConversion) { - ma_uint8 pTempBufferIn[MA_DATA_CONVERTER_STACK_BUFFER_SIZE]; - const ma_uint32 tempBufferInCap = sizeof(pTempBufferIn) / ma_get_bytes_per_frame(pConverter->resampler.format, pConverter->resampler.channels); - - frameCountInThisIteration = (frameCountIn - framesProcessedIn); - if (frameCountInThisIteration > tempBufferInCap) { - frameCountInThisIteration = tempBufferInCap; - } - - if (pConverter->hasPostFormatConversion) { - if (frameCountInThisIteration > tempBufferOutCap) { - frameCountInThisIteration = tempBufferOutCap; - } - } - - if (pFramesInThisIteration != NULL) { - ma_convert_pcm_frames_format(pTempBufferIn, pConverter->resampler.format, pFramesInThisIteration, pConverter->formatIn, frameCountInThisIteration, pConverter->channelsIn, pConverter->ditherMode); - } else { - MA_ZERO_MEMORY(pTempBufferIn, sizeof(pTempBufferIn)); - } - - frameCountOutThisIteration = (frameCountOut - framesProcessedOut); - - if (pConverter->hasPostFormatConversion) { - /* Both input and output conversion required. Output to the temp buffer. */ - if (frameCountOutThisIteration > tempBufferOutCap) { - frameCountOutThisIteration = tempBufferOutCap; - } - - result = ma_resampler_process_pcm_frames(&pConverter->resampler, pTempBufferIn, &frameCountInThisIteration, pTempBufferOut, &frameCountOutThisIteration); - } else { - /* Only pre-format required. Output straight to the output buffer. */ - result = ma_resampler_process_pcm_frames(&pConverter->resampler, pTempBufferIn, &frameCountInThisIteration, pFramesOutThisIteration, &frameCountOutThisIteration); - } - - if (result != MA_SUCCESS) { - break; - } - } else { - /* No pre-format required. Just read straight from the input buffer. */ - MA_ASSERT(pConverter->hasPostFormatConversion == MA_TRUE); - - frameCountInThisIteration = (frameCountIn - framesProcessedIn); - frameCountOutThisIteration = (frameCountOut - framesProcessedOut); - if (frameCountOutThisIteration > tempBufferOutCap) { - frameCountOutThisIteration = tempBufferOutCap; - } - - result = ma_resampler_process_pcm_frames(&pConverter->resampler, pFramesInThisIteration, &frameCountInThisIteration, pTempBufferOut, &frameCountOutThisIteration); - if (result != MA_SUCCESS) { - break; - } - } - - /* If we are doing a post format conversion we need to do that now. */ - if (pConverter->hasPostFormatConversion) { - if (pFramesOutThisIteration != NULL) { - ma_convert_pcm_frames_format(pFramesOutThisIteration, pConverter->formatOut, pTempBufferOut, pConverter->resampler.format, frameCountOutThisIteration, pConverter->resampler.channels, pConverter->ditherMode); - } - } - - framesProcessedIn += frameCountInThisIteration; - framesProcessedOut += frameCountOutThisIteration; - - MA_ASSERT(framesProcessedIn <= frameCountIn); - MA_ASSERT(framesProcessedOut <= frameCountOut); - - if (frameCountOutThisIteration == 0) { - break; /* Consumed all of our input data. */ - } - } - - if (pFrameCountIn != NULL) { - *pFrameCountIn = framesProcessedIn; - } - if (pFrameCountOut != NULL) { - *pFrameCountOut = framesProcessedOut; - } - - return result; -} - -static ma_result ma_data_converter_process_pcm_frames__resample_only(ma_data_converter* pConverter, const void* pFramesIn, ma_uint64* pFrameCountIn, void* pFramesOut, ma_uint64* pFrameCountOut) -{ - MA_ASSERT(pConverter != NULL); - - if (pConverter->hasPreFormatConversion == MA_FALSE && pConverter->hasPostFormatConversion == MA_FALSE) { - /* Neither pre- nor post-format required. This is simple case where only resampling is required. */ - return ma_resampler_process_pcm_frames(&pConverter->resampler, pFramesIn, pFrameCountIn, pFramesOut, pFrameCountOut); - } else { - /* Format conversion required. */ - return ma_data_converter_process_pcm_frames__resample_with_format_conversion(pConverter, pFramesIn, pFrameCountIn, pFramesOut, pFrameCountOut); - } -} - -static ma_result ma_data_converter_process_pcm_frames__channels_only(ma_data_converter* pConverter, const void* pFramesIn, ma_uint64* pFrameCountIn, void* pFramesOut, ma_uint64* pFrameCountOut) -{ - ma_result result; - ma_uint64 frameCountIn; - ma_uint64 frameCountOut; - ma_uint64 frameCount; - - MA_ASSERT(pConverter != NULL); - - frameCountIn = 0; - if (pFrameCountIn != NULL) { - frameCountIn = *pFrameCountIn; - } - - frameCountOut = 0; - if (pFrameCountOut != NULL) { - frameCountOut = *pFrameCountOut; - } - - frameCount = ma_min(frameCountIn, frameCountOut); - - if (pConverter->hasPreFormatConversion == MA_FALSE && pConverter->hasPostFormatConversion == MA_FALSE) { - /* No format conversion required. */ - result = ma_channel_converter_process_pcm_frames(&pConverter->channelConverter, pFramesOut, pFramesIn, frameCount); - if (result != MA_SUCCESS) { - return result; - } - } else { - /* Format conversion required. */ - ma_uint64 framesProcessed = 0; - - while (framesProcessed < frameCount) { - ma_uint8 pTempBufferOut[MA_DATA_CONVERTER_STACK_BUFFER_SIZE]; - const ma_uint32 tempBufferOutCap = sizeof(pTempBufferOut) / ma_get_bytes_per_frame(pConverter->channelConverter.format, pConverter->channelConverter.channelsOut); - const void* pFramesInThisIteration; - /* */ void* pFramesOutThisIteration; - ma_uint64 frameCountThisIteration; - - if (pFramesIn != NULL) { - pFramesInThisIteration = ma_offset_ptr(pFramesIn, framesProcessed * ma_get_bytes_per_frame(pConverter->formatIn, pConverter->channelsIn)); - } else { - pFramesInThisIteration = NULL; - } - - if (pFramesOut != NULL) { - pFramesOutThisIteration = ma_offset_ptr(pFramesOut, framesProcessed * ma_get_bytes_per_frame(pConverter->formatOut, pConverter->channelsOut)); - } else { - pFramesOutThisIteration = NULL; - } - - /* Do a pre format conversion if necessary. */ - if (pConverter->hasPreFormatConversion) { - ma_uint8 pTempBufferIn[MA_DATA_CONVERTER_STACK_BUFFER_SIZE]; - const ma_uint32 tempBufferInCap = sizeof(pTempBufferIn) / ma_get_bytes_per_frame(pConverter->channelConverter.format, pConverter->channelConverter.channelsIn); - - frameCountThisIteration = (frameCount - framesProcessed); - if (frameCountThisIteration > tempBufferInCap) { - frameCountThisIteration = tempBufferInCap; - } - - if (pConverter->hasPostFormatConversion) { - if (frameCountThisIteration > tempBufferOutCap) { - frameCountThisIteration = tempBufferOutCap; - } - } - - if (pFramesInThisIteration != NULL) { - ma_convert_pcm_frames_format(pTempBufferIn, pConverter->channelConverter.format, pFramesInThisIteration, pConverter->formatIn, frameCountThisIteration, pConverter->channelsIn, pConverter->ditherMode); - } else { - MA_ZERO_MEMORY(pTempBufferIn, sizeof(pTempBufferIn)); - } - - if (pConverter->hasPostFormatConversion) { - /* Both input and output conversion required. Output to the temp buffer. */ - result = ma_channel_converter_process_pcm_frames(&pConverter->channelConverter, pTempBufferOut, pTempBufferIn, frameCountThisIteration); - } else { - /* Only pre-format required. Output straight to the output buffer. */ - result = ma_channel_converter_process_pcm_frames(&pConverter->channelConverter, pFramesOutThisIteration, pTempBufferIn, frameCountThisIteration); - } - - if (result != MA_SUCCESS) { - break; - } - } else { - /* No pre-format required. Just read straight from the input buffer. */ - MA_ASSERT(pConverter->hasPostFormatConversion == MA_TRUE); - - frameCountThisIteration = (frameCount - framesProcessed); - if (frameCountThisIteration > tempBufferOutCap) { - frameCountThisIteration = tempBufferOutCap; - } - - result = ma_channel_converter_process_pcm_frames(&pConverter->channelConverter, pTempBufferOut, pFramesInThisIteration, frameCountThisIteration); - if (result != MA_SUCCESS) { - break; - } - } - - /* If we are doing a post format conversion we need to do that now. */ - if (pConverter->hasPostFormatConversion) { - if (pFramesOutThisIteration != NULL) { - ma_convert_pcm_frames_format(pFramesOutThisIteration, pConverter->formatOut, pTempBufferOut, pConverter->channelConverter.format, frameCountThisIteration, pConverter->channelConverter.channelsOut, pConverter->ditherMode); - } - } - - framesProcessed += frameCountThisIteration; - } - } - - if (pFrameCountIn != NULL) { - *pFrameCountIn = frameCount; - } - if (pFrameCountOut != NULL) { - *pFrameCountOut = frameCount; - } - - return MA_SUCCESS; -} - -static ma_result ma_data_converter_process_pcm_frames__resample_first(ma_data_converter* pConverter, const void* pFramesIn, ma_uint64* pFrameCountIn, void* pFramesOut, ma_uint64* pFrameCountOut) -{ - ma_result result; - ma_uint64 frameCountIn; - ma_uint64 frameCountOut; - ma_uint64 framesProcessedIn; - ma_uint64 framesProcessedOut; - ma_uint8 pTempBufferIn[MA_DATA_CONVERTER_STACK_BUFFER_SIZE]; /* In resampler format. */ - ma_uint64 tempBufferInCap; - ma_uint8 pTempBufferMid[MA_DATA_CONVERTER_STACK_BUFFER_SIZE]; /* In resampler format, channel converter input format. */ - ma_uint64 tempBufferMidCap; - ma_uint8 pTempBufferOut[MA_DATA_CONVERTER_STACK_BUFFER_SIZE]; /* In channel converter output format. */ - ma_uint64 tempBufferOutCap; - - MA_ASSERT(pConverter != NULL); - MA_ASSERT(pConverter->resampler.format == pConverter->channelConverter.format); - MA_ASSERT(pConverter->resampler.channels == pConverter->channelConverter.channelsIn); - MA_ASSERT(pConverter->resampler.channels < pConverter->channelConverter.channelsOut); - - frameCountIn = 0; - if (pFrameCountIn != NULL) { - frameCountIn = *pFrameCountIn; - } - - frameCountOut = 0; - if (pFrameCountOut != NULL) { - frameCountOut = *pFrameCountOut; - } - - framesProcessedIn = 0; - framesProcessedOut = 0; - - tempBufferInCap = sizeof(pTempBufferIn) / ma_get_bytes_per_frame(pConverter->resampler.format, pConverter->resampler.channels); - tempBufferMidCap = sizeof(pTempBufferIn) / ma_get_bytes_per_frame(pConverter->resampler.format, pConverter->resampler.channels); - tempBufferOutCap = sizeof(pTempBufferOut) / ma_get_bytes_per_frame(pConverter->channelConverter.format, pConverter->channelConverter.channelsOut); - - while (framesProcessedOut < frameCountOut) { - ma_uint64 frameCountInThisIteration; - ma_uint64 frameCountOutThisIteration; - const void* pRunningFramesIn = NULL; - void* pRunningFramesOut = NULL; - const void* pResampleBufferIn; - void* pChannelsBufferOut; - - if (pFramesIn != NULL) { - pRunningFramesIn = ma_offset_ptr(pFramesIn, framesProcessedIn * ma_get_bytes_per_frame(pConverter->formatIn, pConverter->channelsIn)); - } - if (pFramesOut != NULL) { - pRunningFramesOut = ma_offset_ptr(pFramesOut, framesProcessedOut * ma_get_bytes_per_frame(pConverter->formatOut, pConverter->channelsOut)); - } - - /* Run input data through the resampler and output it to the temporary buffer. */ - frameCountInThisIteration = (frameCountIn - framesProcessedIn); - - if (pConverter->hasPreFormatConversion) { - if (frameCountInThisIteration > tempBufferInCap) { - frameCountInThisIteration = tempBufferInCap; - } - } - - frameCountOutThisIteration = (frameCountOut - framesProcessedOut); - if (frameCountOutThisIteration > tempBufferMidCap) { - frameCountOutThisIteration = tempBufferMidCap; - } - - /* We can't read more frames than can fit in the output buffer. */ - if (pConverter->hasPostFormatConversion) { - if (frameCountOutThisIteration > tempBufferOutCap) { - frameCountOutThisIteration = tempBufferOutCap; - } - } - - /* We need to ensure we don't try to process too many input frames that we run out of room in the output buffer. If this happens we'll end up glitching. */ - - /* - We need to try to predict how many input frames will be required for the resampler. If the - resampler can tell us, we'll use that. Otherwise we'll need to make a best guess. The further - off we are from this, the more wasted format conversions we'll end up doing. - */ - #if 1 - { - ma_uint64 requiredInputFrameCount; - - result = ma_resampler_get_required_input_frame_count(&pConverter->resampler, frameCountOutThisIteration, &requiredInputFrameCount); - if (result != MA_SUCCESS) { - /* Fall back to a best guess. */ - requiredInputFrameCount = (frameCountOutThisIteration * pConverter->resampler.sampleRateIn) / pConverter->resampler.sampleRateOut; - } - - if (frameCountInThisIteration > requiredInputFrameCount) { - frameCountInThisIteration = requiredInputFrameCount; - } - } - #endif - - if (pConverter->hasPreFormatConversion) { - if (pFramesIn != NULL) { - ma_convert_pcm_frames_format(pTempBufferIn, pConverter->resampler.format, pRunningFramesIn, pConverter->formatIn, frameCountInThisIteration, pConverter->channelsIn, pConverter->ditherMode); - pResampleBufferIn = pTempBufferIn; - } else { - pResampleBufferIn = NULL; - } - } else { - pResampleBufferIn = pRunningFramesIn; - } - - result = ma_resampler_process_pcm_frames(&pConverter->resampler, pResampleBufferIn, &frameCountInThisIteration, pTempBufferMid, &frameCountOutThisIteration); - if (result != MA_SUCCESS) { - return result; - } - - - /* - The input data has been resampled so now we need to run it through the channel converter. The input data is always contained in pTempBufferMid. We only need to do - this part if we have an output buffer. - */ - if (pFramesOut != NULL) { - if (pConverter->hasPostFormatConversion) { - pChannelsBufferOut = pTempBufferOut; - } else { - pChannelsBufferOut = pRunningFramesOut; - } - - result = ma_channel_converter_process_pcm_frames(&pConverter->channelConverter, pChannelsBufferOut, pTempBufferMid, frameCountOutThisIteration); - if (result != MA_SUCCESS) { - return result; - } - - /* Finally we do post format conversion. */ - if (pConverter->hasPostFormatConversion) { - ma_convert_pcm_frames_format(pRunningFramesOut, pConverter->formatOut, pChannelsBufferOut, pConverter->channelConverter.format, frameCountOutThisIteration, pConverter->channelConverter.channelsOut, pConverter->ditherMode); - } - } - - - framesProcessedIn += frameCountInThisIteration; - framesProcessedOut += frameCountOutThisIteration; - - MA_ASSERT(framesProcessedIn <= frameCountIn); - MA_ASSERT(framesProcessedOut <= frameCountOut); - - if (frameCountOutThisIteration == 0) { - break; /* Consumed all of our input data. */ - } - } - - if (pFrameCountIn != NULL) { - *pFrameCountIn = framesProcessedIn; - } - if (pFrameCountOut != NULL) { - *pFrameCountOut = framesProcessedOut; - } - - return MA_SUCCESS; -} - -static ma_result ma_data_converter_process_pcm_frames__channels_first(ma_data_converter* pConverter, const void* pFramesIn, ma_uint64* pFrameCountIn, void* pFramesOut, ma_uint64* pFrameCountOut) -{ - ma_result result; - ma_uint64 frameCountIn; - ma_uint64 frameCountOut; - ma_uint64 framesProcessedIn; - ma_uint64 framesProcessedOut; - ma_uint8 pTempBufferIn[MA_DATA_CONVERTER_STACK_BUFFER_SIZE]; /* In resampler format. */ - ma_uint64 tempBufferInCap; - ma_uint8 pTempBufferMid[MA_DATA_CONVERTER_STACK_BUFFER_SIZE]; /* In resampler format, channel converter input format. */ - ma_uint64 tempBufferMidCap; - ma_uint8 pTempBufferOut[MA_DATA_CONVERTER_STACK_BUFFER_SIZE]; /* In channel converter output format. */ - ma_uint64 tempBufferOutCap; - - MA_ASSERT(pConverter != NULL); - MA_ASSERT(pConverter->resampler.format == pConverter->channelConverter.format); - MA_ASSERT(pConverter->resampler.channels == pConverter->channelConverter.channelsOut); - MA_ASSERT(pConverter->resampler.channels <= pConverter->channelConverter.channelsIn); - - frameCountIn = 0; - if (pFrameCountIn != NULL) { - frameCountIn = *pFrameCountIn; - } - - frameCountOut = 0; - if (pFrameCountOut != NULL) { - frameCountOut = *pFrameCountOut; - } - - framesProcessedIn = 0; - framesProcessedOut = 0; - - tempBufferInCap = sizeof(pTempBufferIn) / ma_get_bytes_per_frame(pConverter->channelConverter.format, pConverter->channelConverter.channelsIn); - tempBufferMidCap = sizeof(pTempBufferIn) / ma_get_bytes_per_frame(pConverter->channelConverter.format, pConverter->channelConverter.channelsOut); - tempBufferOutCap = sizeof(pTempBufferOut) / ma_get_bytes_per_frame(pConverter->resampler.format, pConverter->resampler.channels); - - while (framesProcessedOut < frameCountOut) { - ma_uint64 frameCountInThisIteration; - ma_uint64 frameCountOutThisIteration; - const void* pRunningFramesIn = NULL; - void* pRunningFramesOut = NULL; - const void* pChannelsBufferIn; - void* pResampleBufferOut; - - if (pFramesIn != NULL) { - pRunningFramesIn = ma_offset_ptr(pFramesIn, framesProcessedIn * ma_get_bytes_per_frame(pConverter->formatIn, pConverter->channelsIn)); - } - if (pFramesOut != NULL) { - pRunningFramesOut = ma_offset_ptr(pFramesOut, framesProcessedOut * ma_get_bytes_per_frame(pConverter->formatOut, pConverter->channelsOut)); - } - - /* - Before doing any processing we need to determine how many frames we should try processing - this iteration, for both input and output. The resampler requires us to perform format and - channel conversion before passing any data into it. If we get our input count wrong, we'll - end up performing redundant pre-processing. This isn't the end of the world, but it does - result in some inefficiencies proportionate to how far our estimates are off. - - If the resampler has a means to calculate exactly how much we'll need, we'll use that. - Otherwise we'll make a best guess. In order to do this, we'll need to calculate the output - frame count first. - */ - frameCountOutThisIteration = (frameCountOut - framesProcessedOut); - if (frameCountOutThisIteration > tempBufferMidCap) { - frameCountOutThisIteration = tempBufferMidCap; - } - - if (pConverter->hasPostFormatConversion) { - if (frameCountOutThisIteration > tempBufferOutCap) { - frameCountOutThisIteration = tempBufferOutCap; - } - } - - /* Now that we have the output frame count we can determine the input frame count. */ - frameCountInThisIteration = (frameCountIn - framesProcessedIn); - if (pConverter->hasPreFormatConversion) { - if (frameCountInThisIteration > tempBufferInCap) { - frameCountInThisIteration = tempBufferInCap; - } - } - - if (frameCountInThisIteration > tempBufferMidCap) { - frameCountInThisIteration = tempBufferMidCap; - } - - #if 1 - { - ma_uint64 requiredInputFrameCount; - - result = ma_resampler_get_required_input_frame_count(&pConverter->resampler, frameCountOutThisIteration, &requiredInputFrameCount); - if (result != MA_SUCCESS) { - /* Fall back to a best guess. */ - requiredInputFrameCount = (frameCountOutThisIteration * pConverter->resampler.sampleRateIn) / pConverter->resampler.sampleRateOut; - } - - if (frameCountInThisIteration > requiredInputFrameCount) { - frameCountInThisIteration = requiredInputFrameCount; - } - } - #endif - - - /* Pre format conversion. */ - if (pConverter->hasPreFormatConversion) { - if (pRunningFramesIn != NULL) { - ma_convert_pcm_frames_format(pTempBufferIn, pConverter->channelConverter.format, pRunningFramesIn, pConverter->formatIn, frameCountInThisIteration, pConverter->channelsIn, pConverter->ditherMode); - pChannelsBufferIn = pTempBufferIn; - } else { - pChannelsBufferIn = NULL; - } - } else { - pChannelsBufferIn = pRunningFramesIn; - } - - - /* Channel conversion. */ - result = ma_channel_converter_process_pcm_frames(&pConverter->channelConverter, pTempBufferMid, pChannelsBufferIn, frameCountInThisIteration); - if (result != MA_SUCCESS) { - return result; - } - - - /* Resampling. */ - if (pConverter->hasPostFormatConversion) { - pResampleBufferOut = pTempBufferOut; - } else { - pResampleBufferOut = pRunningFramesOut; - } - - result = ma_resampler_process_pcm_frames(&pConverter->resampler, pTempBufferMid, &frameCountInThisIteration, pResampleBufferOut, &frameCountOutThisIteration); - if (result != MA_SUCCESS) { - return result; - } - - - /* Post format conversion. */ - if (pConverter->hasPostFormatConversion) { - if (pRunningFramesOut != NULL) { - ma_convert_pcm_frames_format(pRunningFramesOut, pConverter->formatOut, pResampleBufferOut, pConverter->resampler.format, frameCountOutThisIteration, pConverter->channelsOut, pConverter->ditherMode); - } - } - - - framesProcessedIn += frameCountInThisIteration; - framesProcessedOut += frameCountOutThisIteration; - - MA_ASSERT(framesProcessedIn <= frameCountIn); - MA_ASSERT(framesProcessedOut <= frameCountOut); - - if (frameCountOutThisIteration == 0) { - break; /* Consumed all of our input data. */ - } - } - - if (pFrameCountIn != NULL) { - *pFrameCountIn = framesProcessedIn; - } - if (pFrameCountOut != NULL) { - *pFrameCountOut = framesProcessedOut; - } - - return MA_SUCCESS; -} - -MA_API ma_result ma_data_converter_process_pcm_frames(ma_data_converter* pConverter, const void* pFramesIn, ma_uint64* pFrameCountIn, void* pFramesOut, ma_uint64* pFrameCountOut) -{ - if (pConverter == NULL) { - return MA_INVALID_ARGS; - } - - switch (pConverter->executionPath) - { - case ma_data_converter_execution_path_passthrough: return ma_data_converter_process_pcm_frames__passthrough(pConverter, pFramesIn, pFrameCountIn, pFramesOut, pFrameCountOut); - case ma_data_converter_execution_path_format_only: return ma_data_converter_process_pcm_frames__format_only(pConverter, pFramesIn, pFrameCountIn, pFramesOut, pFrameCountOut); - case ma_data_converter_execution_path_channels_only: return ma_data_converter_process_pcm_frames__channels_only(pConverter, pFramesIn, pFrameCountIn, pFramesOut, pFrameCountOut); - case ma_data_converter_execution_path_resample_only: return ma_data_converter_process_pcm_frames__resample_only(pConverter, pFramesIn, pFrameCountIn, pFramesOut, pFrameCountOut); - case ma_data_converter_execution_path_resample_first: return ma_data_converter_process_pcm_frames__resample_first(pConverter, pFramesIn, pFrameCountIn, pFramesOut, pFrameCountOut); - case ma_data_converter_execution_path_channels_first: return ma_data_converter_process_pcm_frames__channels_first(pConverter, pFramesIn, pFrameCountIn, pFramesOut, pFrameCountOut); - default: return MA_INVALID_OPERATION; /* Should never hit this. */ - } -} - -MA_API ma_result ma_data_converter_set_rate(ma_data_converter* pConverter, ma_uint32 sampleRateIn, ma_uint32 sampleRateOut) -{ - if (pConverter == NULL) { - return MA_INVALID_ARGS; - } - - if (pConverter->hasResampler == MA_FALSE) { - return MA_INVALID_OPERATION; /* Dynamic resampling not enabled. */ - } - - return ma_resampler_set_rate(&pConverter->resampler, sampleRateIn, sampleRateOut); -} - -MA_API ma_result ma_data_converter_set_rate_ratio(ma_data_converter* pConverter, float ratioInOut) -{ - if (pConverter == NULL) { - return MA_INVALID_ARGS; - } - - if (pConverter->hasResampler == MA_FALSE) { - return MA_INVALID_OPERATION; /* Dynamic resampling not enabled. */ - } - - return ma_resampler_set_rate_ratio(&pConverter->resampler, ratioInOut); -} - -MA_API ma_uint64 ma_data_converter_get_input_latency(const ma_data_converter* pConverter) -{ - if (pConverter == NULL) { - return 0; - } - - if (pConverter->hasResampler) { - return ma_resampler_get_input_latency(&pConverter->resampler); - } - - return 0; /* No latency without a resampler. */ -} - -MA_API ma_uint64 ma_data_converter_get_output_latency(const ma_data_converter* pConverter) -{ - if (pConverter == NULL) { - return 0; - } - - if (pConverter->hasResampler) { - return ma_resampler_get_output_latency(&pConverter->resampler); - } - - return 0; /* No latency without a resampler. */ -} - -MA_API ma_result ma_data_converter_get_required_input_frame_count(const ma_data_converter* pConverter, ma_uint64 outputFrameCount, ma_uint64* pInputFrameCount) -{ - if (pInputFrameCount == NULL) { - return MA_INVALID_ARGS; - } - - *pInputFrameCount = 0; - - if (pConverter == NULL) { - return MA_INVALID_ARGS; - } - - if (pConverter->hasResampler) { - return ma_resampler_get_required_input_frame_count(&pConverter->resampler, outputFrameCount, pInputFrameCount); - } else { - *pInputFrameCount = outputFrameCount; /* 1:1 */ - return MA_SUCCESS; - } -} - -MA_API ma_result ma_data_converter_get_expected_output_frame_count(const ma_data_converter* pConverter, ma_uint64 inputFrameCount, ma_uint64* pOutputFrameCount) -{ - if (pOutputFrameCount == NULL) { - return MA_INVALID_ARGS; - } - - *pOutputFrameCount = 0; - - if (pConverter == NULL) { - return MA_INVALID_ARGS; - } - - if (pConverter->hasResampler) { - return ma_resampler_get_expected_output_frame_count(&pConverter->resampler, inputFrameCount, pOutputFrameCount); - } else { - *pOutputFrameCount = inputFrameCount; /* 1:1 */ - return MA_SUCCESS; - } -} - -MA_API ma_result ma_data_converter_get_input_channel_map(const ma_data_converter* pConverter, ma_channel* pChannelMap, size_t channelMapCap) -{ - if (pConverter == NULL || pChannelMap == NULL) { - return MA_INVALID_ARGS; - } - - if (pConverter->hasChannelConverter) { - ma_channel_converter_get_output_channel_map(&pConverter->channelConverter, pChannelMap, channelMapCap); - } else { - ma_channel_map_init_standard(ma_standard_channel_map_default, pChannelMap, channelMapCap, pConverter->channelsOut); - } - - return MA_SUCCESS; -} - -MA_API ma_result ma_data_converter_get_output_channel_map(const ma_data_converter* pConverter, ma_channel* pChannelMap, size_t channelMapCap) -{ - if (pConverter == NULL || pChannelMap == NULL) { - return MA_INVALID_ARGS; - } - - if (pConverter->hasChannelConverter) { - ma_channel_converter_get_input_channel_map(&pConverter->channelConverter, pChannelMap, channelMapCap); - } else { - ma_channel_map_init_standard(ma_standard_channel_map_default, pChannelMap, channelMapCap, pConverter->channelsIn); - } - - return MA_SUCCESS; -} - -MA_API ma_result ma_data_converter_reset(ma_data_converter* pConverter) -{ - if (pConverter == NULL) { - return MA_INVALID_ARGS; - } - - /* There's nothing to do if we're not resampling. */ - if (pConverter->hasResampler == MA_FALSE) { - return MA_SUCCESS; - } - - return ma_resampler_reset(&pConverter->resampler); -} - - - -/************************************************************************************************************************************************************** - -Channel Maps - -**************************************************************************************************************************************************************/ -static ma_channel ma_channel_map_init_standard_channel(ma_standard_channel_map standardChannelMap, ma_uint32 channelCount, ma_uint32 channelIndex); - -MA_API ma_channel ma_channel_map_get_channel(const ma_channel* pChannelMap, ma_uint32 channelCount, ma_uint32 channelIndex) -{ - if (pChannelMap == NULL) { - return ma_channel_map_init_standard_channel(ma_standard_channel_map_default, channelCount, channelIndex); - } else { - if (channelIndex >= channelCount) { - return MA_CHANNEL_NONE; - } - - return pChannelMap[channelIndex]; - } -} - -MA_API void ma_channel_map_init_blank(ma_channel* pChannelMap, ma_uint32 channels) -{ - if (pChannelMap == NULL) { - return; - } - - MA_ZERO_MEMORY(pChannelMap, sizeof(*pChannelMap) * channels); -} - - -static ma_channel ma_channel_map_init_standard_channel_microsoft(ma_uint32 channelCount, ma_uint32 channelIndex) -{ - if (channelCount == 0 || channelIndex >= channelCount) { - return MA_CHANNEL_NONE; - } - - /* This is the Microsoft channel map. Based off the speaker configurations mentioned here: https://docs.microsoft.com/en-us/windows-hardware/drivers/ddi/content/ksmedia/ns-ksmedia-ksaudio_channel_config */ - switch (channelCount) - { - case 0: return MA_CHANNEL_NONE; - - case 1: - { - return MA_CHANNEL_MONO; - } break; - - case 2: - { - switch (channelIndex) { - case 0: return MA_CHANNEL_FRONT_LEFT; - case 1: return MA_CHANNEL_FRONT_RIGHT; - } - } break; - - case 3: /* No defined, but best guess. */ - { - switch (channelIndex) { - case 0: return MA_CHANNEL_FRONT_LEFT; - case 1: return MA_CHANNEL_FRONT_RIGHT; - case 2: return MA_CHANNEL_FRONT_CENTER; - } - } break; - - case 4: - { - switch (channelIndex) { - #ifndef MA_USE_QUAD_MICROSOFT_CHANNEL_MAP - /* Surround. Using the Surround profile has the advantage of the 3rd channel (MA_CHANNEL_FRONT_CENTER) mapping nicely with higher channel counts. */ - case 0: return MA_CHANNEL_FRONT_LEFT; - case 1: return MA_CHANNEL_FRONT_RIGHT; - case 2: return MA_CHANNEL_FRONT_CENTER; - case 3: return MA_CHANNEL_BACK_CENTER; - #else - /* Quad. */ - case 0: return MA_CHANNEL_FRONT_LEFT; - case 1: return MA_CHANNEL_FRONT_RIGHT; - case 2: return MA_CHANNEL_BACK_LEFT; - case 3: return MA_CHANNEL_BACK_RIGHT; - #endif - } - } break; - - case 5: /* Not defined, but best guess. */ - { - switch (channelIndex) { - case 0: return MA_CHANNEL_FRONT_LEFT; - case 1: return MA_CHANNEL_FRONT_RIGHT; - case 2: return MA_CHANNEL_FRONT_CENTER; - case 3: return MA_CHANNEL_BACK_LEFT; - case 4: return MA_CHANNEL_BACK_RIGHT; - } - } break; - - case 6: - { - switch (channelIndex) { - case 0: return MA_CHANNEL_FRONT_LEFT; - case 1: return MA_CHANNEL_FRONT_RIGHT; - case 2: return MA_CHANNEL_FRONT_CENTER; - case 3: return MA_CHANNEL_LFE; - case 4: return MA_CHANNEL_SIDE_LEFT; - case 5: return MA_CHANNEL_SIDE_RIGHT; - } - } break; - - case 7: /* Not defined, but best guess. */ - { - switch (channelIndex) { - case 0: return MA_CHANNEL_FRONT_LEFT; - case 1: return MA_CHANNEL_FRONT_RIGHT; - case 2: return MA_CHANNEL_FRONT_CENTER; - case 3: return MA_CHANNEL_LFE; - case 4: return MA_CHANNEL_BACK_CENTER; - case 5: return MA_CHANNEL_SIDE_LEFT; - case 6: return MA_CHANNEL_SIDE_RIGHT; - } - } break; - - case 8: - default: - { - switch (channelIndex) { - case 0: return MA_CHANNEL_FRONT_LEFT; - case 1: return MA_CHANNEL_FRONT_RIGHT; - case 2: return MA_CHANNEL_FRONT_CENTER; - case 3: return MA_CHANNEL_LFE; - case 4: return MA_CHANNEL_BACK_LEFT; - case 5: return MA_CHANNEL_BACK_RIGHT; - case 6: return MA_CHANNEL_SIDE_LEFT; - case 7: return MA_CHANNEL_SIDE_RIGHT; - } - } break; - } - - if (channelCount > 8) { - if (channelIndex < 32) { /* We have 32 AUX channels. */ - return (ma_channel)(MA_CHANNEL_AUX_0 + (channelIndex - 8)); - } - } - - /* Getting here means we don't know how to map the channel position so just return MA_CHANNEL_NONE. */ - return MA_CHANNEL_NONE; -} - -static ma_channel ma_channel_map_init_standard_channel_alsa(ma_uint32 channelCount, ma_uint32 channelIndex) -{ - switch (channelCount) - { - case 0: return MA_CHANNEL_NONE; - - case 1: - { - return MA_CHANNEL_MONO; - } break; - - case 2: - { - switch (channelIndex) { - case 0: return MA_CHANNEL_FRONT_LEFT; - case 1: return MA_CHANNEL_FRONT_RIGHT; - } - } break; - - case 3: - { - switch (channelIndex) { - case 0: return MA_CHANNEL_FRONT_LEFT; - case 1: return MA_CHANNEL_FRONT_RIGHT; - case 2: return MA_CHANNEL_FRONT_CENTER; - } - } break; - - case 4: - { - switch (channelIndex) { - case 0: return MA_CHANNEL_FRONT_LEFT; - case 1: return MA_CHANNEL_FRONT_RIGHT; - case 2: return MA_CHANNEL_BACK_LEFT; - case 3: return MA_CHANNEL_BACK_RIGHT; - } - } break; - - case 5: - { - switch (channelIndex) { - case 0: return MA_CHANNEL_FRONT_LEFT; - case 1: return MA_CHANNEL_FRONT_RIGHT; - case 2: return MA_CHANNEL_BACK_LEFT; - case 3: return MA_CHANNEL_BACK_RIGHT; - case 4: return MA_CHANNEL_FRONT_CENTER; - } - } break; - - case 6: - { - switch (channelIndex) { - case 0: return MA_CHANNEL_FRONT_LEFT; - case 1: return MA_CHANNEL_FRONT_RIGHT; - case 2: return MA_CHANNEL_BACK_LEFT; - case 3: return MA_CHANNEL_BACK_RIGHT; - case 4: return MA_CHANNEL_FRONT_CENTER; - case 5: return MA_CHANNEL_LFE; - } - } break; - - case 7: - { - switch (channelIndex) { - case 0: return MA_CHANNEL_FRONT_LEFT; - case 1: return MA_CHANNEL_FRONT_RIGHT; - case 2: return MA_CHANNEL_BACK_LEFT; - case 3: return MA_CHANNEL_BACK_RIGHT; - case 4: return MA_CHANNEL_FRONT_CENTER; - case 5: return MA_CHANNEL_LFE; - case 6: return MA_CHANNEL_BACK_CENTER; - } - } break; - - case 8: - default: - { - switch (channelIndex) { - case 0: return MA_CHANNEL_FRONT_LEFT; - case 1: return MA_CHANNEL_FRONT_RIGHT; - case 2: return MA_CHANNEL_BACK_LEFT; - case 3: return MA_CHANNEL_BACK_RIGHT; - case 4: return MA_CHANNEL_FRONT_CENTER; - case 5: return MA_CHANNEL_LFE; - case 6: return MA_CHANNEL_SIDE_LEFT; - case 7: return MA_CHANNEL_SIDE_RIGHT; - } - } break; - } - - if (channelCount > 8) { - if (channelIndex < 32) { /* We have 32 AUX channels. */ - return (ma_channel)(MA_CHANNEL_AUX_0 + (channelIndex - 8)); - } - } - - /* Getting here means we don't know how to map the channel position so just return MA_CHANNEL_NONE. */ - return MA_CHANNEL_NONE; -} - -static ma_channel ma_channel_map_init_standard_channel_rfc3551(ma_uint32 channelCount, ma_uint32 channelIndex) -{ - switch (channelCount) - { - case 0: return MA_CHANNEL_NONE; - - case 1: - { - return MA_CHANNEL_MONO; - } break; - - case 2: - { - switch (channelIndex) { - case 0: return MA_CHANNEL_FRONT_LEFT; - case 1: return MA_CHANNEL_FRONT_RIGHT; - } - } break; - - case 3: - { - switch (channelIndex) { - case 0: return MA_CHANNEL_FRONT_LEFT; - case 1: return MA_CHANNEL_FRONT_RIGHT; - case 2: return MA_CHANNEL_FRONT_CENTER; - } - } break; - - case 4: - { - switch (channelIndex) { - case 0: return MA_CHANNEL_FRONT_LEFT; - case 2: return MA_CHANNEL_FRONT_CENTER; - case 1: return MA_CHANNEL_FRONT_RIGHT; - case 3: return MA_CHANNEL_BACK_CENTER; - } - } break; - - case 5: - { - switch (channelIndex) { - case 0: return MA_CHANNEL_FRONT_LEFT; - case 1: return MA_CHANNEL_FRONT_RIGHT; - case 2: return MA_CHANNEL_FRONT_CENTER; - case 3: return MA_CHANNEL_BACK_LEFT; - case 4: return MA_CHANNEL_BACK_RIGHT; - } - } break; - - case 6: - default: - { - switch (channelIndex) { - case 0: return MA_CHANNEL_FRONT_LEFT; - case 1: return MA_CHANNEL_SIDE_LEFT; - case 2: return MA_CHANNEL_FRONT_CENTER; - case 3: return MA_CHANNEL_FRONT_RIGHT; - case 4: return MA_CHANNEL_SIDE_RIGHT; - case 5: return MA_CHANNEL_BACK_CENTER; - } - } break; - } - - if (channelCount > 6) { - if (channelIndex < 32) { /* We have 32 AUX channels. */ - return (ma_channel)(MA_CHANNEL_AUX_0 + (channelIndex - 6)); - } - } - - /* Getting here means we don't know how to map the channel position so just return MA_CHANNEL_NONE. */ - return MA_CHANNEL_NONE; -} - -static ma_channel ma_channel_map_init_standard_channel_flac(ma_uint32 channelCount, ma_uint32 channelIndex) -{ - switch (channelCount) - { - case 0: return MA_CHANNEL_NONE; - - case 1: - { - return MA_CHANNEL_MONO; - } break; - - case 2: - { - switch (channelIndex) { - case 0: return MA_CHANNEL_FRONT_LEFT; - case 1: return MA_CHANNEL_FRONT_RIGHT; - } - } break; - - case 3: - { - switch (channelIndex) { - case 0: return MA_CHANNEL_FRONT_LEFT; - case 1: return MA_CHANNEL_FRONT_RIGHT; - case 2: return MA_CHANNEL_FRONT_CENTER; - } - } break; - - case 4: - { - switch (channelIndex) { - case 0: return MA_CHANNEL_FRONT_LEFT; - case 1: return MA_CHANNEL_FRONT_RIGHT; - case 2: return MA_CHANNEL_BACK_LEFT; - case 3: return MA_CHANNEL_BACK_RIGHT; - } - } break; - - case 5: - { - switch (channelIndex) { - case 0: return MA_CHANNEL_FRONT_LEFT; - case 1: return MA_CHANNEL_FRONT_RIGHT; - case 2: return MA_CHANNEL_FRONT_CENTER; - case 3: return MA_CHANNEL_BACK_LEFT; - case 4: return MA_CHANNEL_BACK_RIGHT; - } - } break; - - case 6: - { - switch (channelIndex) { - case 0: return MA_CHANNEL_FRONT_LEFT; - case 1: return MA_CHANNEL_FRONT_RIGHT; - case 2: return MA_CHANNEL_FRONT_CENTER; - case 3: return MA_CHANNEL_LFE; - case 4: return MA_CHANNEL_BACK_LEFT; - case 5: return MA_CHANNEL_BACK_RIGHT; - } - } break; - - case 7: - { - switch (channelIndex) { - case 0: return MA_CHANNEL_FRONT_LEFT; - case 1: return MA_CHANNEL_FRONT_RIGHT; - case 2: return MA_CHANNEL_FRONT_CENTER; - case 3: return MA_CHANNEL_LFE; - case 4: return MA_CHANNEL_BACK_CENTER; - case 5: return MA_CHANNEL_SIDE_LEFT; - case 6: return MA_CHANNEL_SIDE_RIGHT; - } - } break; - - case 8: - default: - { - switch (channelIndex) { - case 0: return MA_CHANNEL_FRONT_LEFT; - case 1: return MA_CHANNEL_FRONT_RIGHT; - case 2: return MA_CHANNEL_FRONT_CENTER; - case 3: return MA_CHANNEL_LFE; - case 4: return MA_CHANNEL_BACK_LEFT; - case 5: return MA_CHANNEL_BACK_RIGHT; - case 6: return MA_CHANNEL_SIDE_LEFT; - case 7: return MA_CHANNEL_SIDE_RIGHT; - } - } break; - } - - if (channelCount > 8) { - if (channelIndex < 32) { /* We have 32 AUX channels. */ - return (ma_channel)(MA_CHANNEL_AUX_0 + (channelIndex - 8)); - } - } - - /* Getting here means we don't know how to map the channel position so just return MA_CHANNEL_NONE. */ - return MA_CHANNEL_NONE; -} - -static ma_channel ma_channel_map_init_standard_channel_vorbis(ma_uint32 channelCount, ma_uint32 channelIndex) -{ - switch (channelCount) - { - case 0: return MA_CHANNEL_NONE; - - case 1: - { - return MA_CHANNEL_MONO; - } break; - - case 2: - { - switch (channelIndex) { - case 0: return MA_CHANNEL_FRONT_LEFT; - case 1: return MA_CHANNEL_FRONT_RIGHT; - } - } break; - - case 3: - { - switch (channelIndex) { - case 0: return MA_CHANNEL_FRONT_LEFT; - case 1: return MA_CHANNEL_FRONT_CENTER; - case 2: return MA_CHANNEL_FRONT_RIGHT; - } - } break; - - case 4: - { - switch (channelIndex) { - case 0: return MA_CHANNEL_FRONT_LEFT; - case 1: return MA_CHANNEL_FRONT_RIGHT; - case 2: return MA_CHANNEL_BACK_LEFT; - case 3: return MA_CHANNEL_BACK_RIGHT; - } - } break; - - case 5: - { - switch (channelIndex) { - case 0: return MA_CHANNEL_FRONT_LEFT; - case 1: return MA_CHANNEL_FRONT_CENTER; - case 2: return MA_CHANNEL_FRONT_RIGHT; - case 3: return MA_CHANNEL_BACK_LEFT; - case 4: return MA_CHANNEL_BACK_RIGHT; - } - } break; - - case 6: - { - switch (channelIndex) { - case 0: return MA_CHANNEL_FRONT_LEFT; - case 1: return MA_CHANNEL_FRONT_CENTER; - case 2: return MA_CHANNEL_FRONT_RIGHT; - case 3: return MA_CHANNEL_BACK_LEFT; - case 4: return MA_CHANNEL_BACK_RIGHT; - case 5: return MA_CHANNEL_LFE; - } - } break; - - case 7: - { - switch (channelIndex) { - case 0: return MA_CHANNEL_FRONT_LEFT; - case 1: return MA_CHANNEL_FRONT_CENTER; - case 2: return MA_CHANNEL_FRONT_RIGHT; - case 3: return MA_CHANNEL_SIDE_LEFT; - case 4: return MA_CHANNEL_SIDE_RIGHT; - case 5: return MA_CHANNEL_BACK_CENTER; - case 6: return MA_CHANNEL_LFE; - } - } break; - - case 8: - default: - { - switch (channelIndex) { - case 0: return MA_CHANNEL_FRONT_LEFT; - case 1: return MA_CHANNEL_FRONT_CENTER; - case 2: return MA_CHANNEL_FRONT_RIGHT; - case 3: return MA_CHANNEL_SIDE_LEFT; - case 4: return MA_CHANNEL_SIDE_RIGHT; - case 5: return MA_CHANNEL_BACK_LEFT; - case 6: return MA_CHANNEL_BACK_RIGHT; - case 7: return MA_CHANNEL_LFE; - } - } break; - } - - if (channelCount > 8) { - if (channelIndex < 32) { /* We have 32 AUX channels. */ - return (ma_channel)(MA_CHANNEL_AUX_0 + (channelIndex - 8)); - } - } - - /* Getting here means we don't know how to map the channel position so just return MA_CHANNEL_NONE. */ - return MA_CHANNEL_NONE; -} - -static ma_channel ma_channel_map_init_standard_channel_sound4(ma_uint32 channelCount, ma_uint32 channelIndex) -{ - switch (channelCount) - { - case 0: return MA_CHANNEL_NONE; - - case 1: - { - return MA_CHANNEL_MONO; - } break; - - case 2: - { - switch (channelIndex) { - case 0: return MA_CHANNEL_FRONT_LEFT; - case 1: return MA_CHANNEL_FRONT_RIGHT; - } - } break; - - case 3: - { - switch (channelIndex) { - case 0: return MA_CHANNEL_FRONT_LEFT; - case 1: return MA_CHANNEL_FRONT_RIGHT; - case 2: return MA_CHANNEL_FRONT_CENTER; - } - } break; - - case 4: - { - switch (channelIndex) { - case 0: return MA_CHANNEL_FRONT_LEFT; - case 1: return MA_CHANNEL_FRONT_RIGHT; - case 2: return MA_CHANNEL_BACK_LEFT; - case 3: return MA_CHANNEL_BACK_RIGHT; - } - } break; - - case 5: - { - switch (channelIndex) { - case 0: return MA_CHANNEL_FRONT_LEFT; - case 1: return MA_CHANNEL_FRONT_RIGHT; - case 2: return MA_CHANNEL_FRONT_CENTER; - case 3: return MA_CHANNEL_BACK_LEFT; - case 4: return MA_CHANNEL_BACK_RIGHT; - } - } break; - - case 6: - { - switch (channelIndex) { - case 0: return MA_CHANNEL_FRONT_LEFT; - case 1: return MA_CHANNEL_FRONT_CENTER; - case 2: return MA_CHANNEL_FRONT_RIGHT; - case 3: return MA_CHANNEL_BACK_LEFT; - case 4: return MA_CHANNEL_BACK_RIGHT; - case 5: return MA_CHANNEL_LFE; - } - } break; - - case 7: - { - switch (channelIndex) { - case 0: return MA_CHANNEL_FRONT_LEFT; - case 1: return MA_CHANNEL_FRONT_CENTER; - case 2: return MA_CHANNEL_FRONT_RIGHT; - case 3: return MA_CHANNEL_SIDE_LEFT; - case 4: return MA_CHANNEL_SIDE_RIGHT; - case 5: return MA_CHANNEL_BACK_CENTER; - case 6: return MA_CHANNEL_LFE; - } - } break; - - case 8: - default: - { - switch (channelIndex) { - case 0: return MA_CHANNEL_FRONT_LEFT; - case 1: return MA_CHANNEL_FRONT_CENTER; - case 2: return MA_CHANNEL_FRONT_RIGHT; - case 3: return MA_CHANNEL_SIDE_LEFT; - case 4: return MA_CHANNEL_SIDE_RIGHT; - case 5: return MA_CHANNEL_BACK_LEFT; - case 6: return MA_CHANNEL_BACK_RIGHT; - case 7: return MA_CHANNEL_LFE; - } - } break; - } - - if (channelCount > 8) { - if (channelIndex < 32) { /* We have 32 AUX channels. */ - return (ma_channel)(MA_CHANNEL_AUX_0 + (channelIndex - 8)); - } - } - - /* Getting here means we don't know how to map the channel position so just return MA_CHANNEL_NONE. */ - return MA_CHANNEL_NONE; -} - -static ma_channel ma_channel_map_init_standard_channel_sndio(ma_uint32 channelCount, ma_uint32 channelIndex) -{ - switch (channelCount) - { - case 0: return MA_CHANNEL_NONE; - - case 1: - { - return MA_CHANNEL_MONO; - } break; - - case 2: - { - switch (channelIndex) { - case 0: return MA_CHANNEL_FRONT_LEFT; - case 1: return MA_CHANNEL_FRONT_RIGHT; - } - } break; - - case 3: /* No defined, but best guess. */ - { - switch (channelIndex) { - case 0: return MA_CHANNEL_FRONT_LEFT; - case 1: return MA_CHANNEL_FRONT_RIGHT; - case 2: return MA_CHANNEL_FRONT_CENTER; - } - } break; - - case 4: - { - switch (channelIndex) { - case 0: return MA_CHANNEL_FRONT_LEFT; - case 1: return MA_CHANNEL_FRONT_RIGHT; - case 2: return MA_CHANNEL_BACK_LEFT; - case 3: return MA_CHANNEL_BACK_RIGHT; - } - } break; - - case 5: /* Not defined, but best guess. */ - { - switch (channelIndex) { - case 0: return MA_CHANNEL_FRONT_LEFT; - case 1: return MA_CHANNEL_FRONT_RIGHT; - case 2: return MA_CHANNEL_BACK_LEFT; - case 3: return MA_CHANNEL_BACK_RIGHT; - case 4: return MA_CHANNEL_FRONT_CENTER; - } - } break; - - case 6: - default: - { - switch (channelIndex) { - case 0: return MA_CHANNEL_FRONT_LEFT; - case 1: return MA_CHANNEL_FRONT_RIGHT; - case 2: return MA_CHANNEL_BACK_LEFT; - case 3: return MA_CHANNEL_BACK_RIGHT; - case 4: return MA_CHANNEL_FRONT_CENTER; - case 5: return MA_CHANNEL_LFE; - } - } break; - } - - if (channelCount > 6) { - if (channelIndex < 32) { /* We have 32 AUX channels. */ - return (ma_channel)(MA_CHANNEL_AUX_0 + (channelIndex - 6)); - } - } - - /* Getting here means we don't know how to map the channel position so just return MA_CHANNEL_NONE. */ - return MA_CHANNEL_NONE; -} - - -static ma_channel ma_channel_map_init_standard_channel(ma_standard_channel_map standardChannelMap, ma_uint32 channelCount, ma_uint32 channelIndex) -{ - if (channelCount == 0 || channelIndex >= channelCount) { - return MA_CHANNEL_NONE; - } - - switch (standardChannelMap) - { - case ma_standard_channel_map_alsa: - { - return ma_channel_map_init_standard_channel_alsa(channelCount, channelIndex); - } break; - - case ma_standard_channel_map_rfc3551: - { - return ma_channel_map_init_standard_channel_rfc3551(channelCount, channelIndex); - } break; - - case ma_standard_channel_map_flac: - { - return ma_channel_map_init_standard_channel_flac(channelCount, channelIndex); - } break; - - case ma_standard_channel_map_vorbis: - { - return ma_channel_map_init_standard_channel_vorbis(channelCount, channelIndex); - } break; - - case ma_standard_channel_map_sound4: - { - return ma_channel_map_init_standard_channel_sound4(channelCount, channelIndex); - } break; - - case ma_standard_channel_map_sndio: - { - return ma_channel_map_init_standard_channel_sndio(channelCount, channelIndex); - } break; - - case ma_standard_channel_map_microsoft: /* Also default. */ - /*case ma_standard_channel_map_default;*/ - default: - { - return ma_channel_map_init_standard_channel_microsoft(channelCount, channelIndex); - } break; - } -} - -MA_API void ma_channel_map_init_standard(ma_standard_channel_map standardChannelMap, ma_channel* pChannelMap, size_t channelMapCap, ma_uint32 channels) -{ - ma_uint32 iChannel; - - if (pChannelMap == NULL || channelMapCap == 0 || channels == 0) { - return; - } - - for (iChannel = 0; iChannel < channels; iChannel += 1) { - if (channelMapCap == 0) { - break; /* Ran out of room. */ - } - - pChannelMap[0] = ma_channel_map_init_standard_channel(standardChannelMap, channels, iChannel); - pChannelMap += 1; - channelMapCap -= 1; - } -} - -MA_API void ma_channel_map_copy(ma_channel* pOut, const ma_channel* pIn, ma_uint32 channels) -{ - if (pOut != NULL && pIn != NULL && channels > 0) { - MA_COPY_MEMORY(pOut, pIn, sizeof(*pOut) * channels); - } -} - -MA_API void ma_channel_map_copy_or_default(ma_channel* pOut, size_t channelMapCapOut, const ma_channel* pIn, ma_uint32 channels) -{ - if (pOut == NULL || channels == 0) { - return; - } - - if (pIn != NULL) { - ma_channel_map_copy(pOut, pIn, channels); - } else { - ma_channel_map_init_standard(ma_standard_channel_map_default, pOut, channelMapCapOut, channels); - } -} - -MA_API ma_bool32 ma_channel_map_is_valid(const ma_channel* pChannelMap, ma_uint32 channels) -{ - /* A channel count of 0 is invalid. */ - if (channels == 0) { - return MA_FALSE; - } - - /* It does not make sense to have a mono channel when there is more than 1 channel. */ - if (channels > 1) { - ma_uint32 iChannel; - for (iChannel = 0; iChannel < channels; ++iChannel) { - if (ma_channel_map_get_channel(pChannelMap, channels, iChannel) == MA_CHANNEL_MONO) { - return MA_FALSE; - } - } - } - - return MA_TRUE; -} - -MA_API ma_bool32 ma_channel_map_is_equal(const ma_channel* pChannelMapA, const ma_channel* pChannelMapB, ma_uint32 channels) -{ - ma_uint32 iChannel; - - if (pChannelMapA == pChannelMapB) { - return MA_TRUE; - } - - for (iChannel = 0; iChannel < channels; ++iChannel) { - if (ma_channel_map_get_channel(pChannelMapA, channels, iChannel) != ma_channel_map_get_channel(pChannelMapB, channels, iChannel)) { - return MA_FALSE; - } - } - - return MA_TRUE; -} - -MA_API ma_bool32 ma_channel_map_is_blank(const ma_channel* pChannelMap, ma_uint32 channels) -{ - ma_uint32 iChannel; - - /* A null channel map is equivalent to the default channel map. */ - if (pChannelMap == NULL) { - return MA_FALSE; - } - - for (iChannel = 0; iChannel < channels; ++iChannel) { - if (pChannelMap[iChannel] != MA_CHANNEL_NONE) { - return MA_FALSE; - } - } - - return MA_TRUE; -} - -MA_API ma_bool32 ma_channel_map_contains_channel_position(ma_uint32 channels, const ma_channel* pChannelMap, ma_channel channelPosition) -{ - return ma_channel_map_find_channel_position(channels, pChannelMap, channelPosition, NULL); -} - -MA_API ma_bool32 ma_channel_map_find_channel_position(ma_uint32 channels, const ma_channel* pChannelMap, ma_channel channelPosition, ma_uint32* pChannelIndex) -{ - ma_uint32 iChannel; - - if (pChannelIndex != NULL) { - *pChannelIndex = (ma_uint32)-1; - } - - for (iChannel = 0; iChannel < channels; ++iChannel) { - if (ma_channel_map_get_channel(pChannelMap, channels, iChannel) == channelPosition) { - if (pChannelIndex != NULL) { - *pChannelIndex = iChannel; - } - - return MA_TRUE; - } - } - - /* Getting here means the channel position was not found. */ - return MA_FALSE; -} - -MA_API size_t ma_channel_map_to_string(const ma_channel* pChannelMap, ma_uint32 channels, char* pBufferOut, size_t bufferCap) -{ - size_t len; - ma_uint32 iChannel; - - len = 0; - - for (iChannel = 0; iChannel < channels; iChannel += 1) { - const char* pChannelStr = ma_channel_position_to_string(ma_channel_map_get_channel(pChannelMap, channels, iChannel)); - size_t channelStrLen = strlen(pChannelStr); - - /* Append the string if necessary. */ - if (pBufferOut != NULL && bufferCap > len + channelStrLen) { - MA_COPY_MEMORY(pBufferOut + len, pChannelStr, channelStrLen); - } - len += channelStrLen; - - /* Append a space if it's not the last item. */ - if (iChannel+1 < channels) { - if (pBufferOut != NULL && bufferCap > len + 1) { - pBufferOut[len] = ' '; - } - len += 1; - } - } - - /* Null terminate. Don't increment the length here. */ - if (pBufferOut != NULL && bufferCap > len + 1) { - pBufferOut[len] = '\0'; - } - - return len; -} - -MA_API const char* ma_channel_position_to_string(ma_channel channel) -{ - switch (channel) - { - case MA_CHANNEL_NONE : return "CHANNEL_NONE"; - case MA_CHANNEL_MONO : return "CHANNEL_MONO"; - case MA_CHANNEL_FRONT_LEFT : return "CHANNEL_FRONT_LEFT"; - case MA_CHANNEL_FRONT_RIGHT : return "CHANNEL_FRONT_RIGHT"; - case MA_CHANNEL_FRONT_CENTER : return "CHANNEL_FRONT_CENTER"; - case MA_CHANNEL_LFE : return "CHANNEL_LFE"; - case MA_CHANNEL_BACK_LEFT : return "CHANNEL_BACK_LEFT"; - case MA_CHANNEL_BACK_RIGHT : return "CHANNEL_BACK_RIGHT"; - case MA_CHANNEL_FRONT_LEFT_CENTER : return "CHANNEL_FRONT_LEFT_CENTER"; - case MA_CHANNEL_FRONT_RIGHT_CENTER: return "CHANNEL_FRONT_RIGHT_CENTER"; - case MA_CHANNEL_BACK_CENTER : return "CHANNEL_BACK_CENTER"; - case MA_CHANNEL_SIDE_LEFT : return "CHANNEL_SIDE_LEFT"; - case MA_CHANNEL_SIDE_RIGHT : return "CHANNEL_SIDE_RIGHT"; - case MA_CHANNEL_TOP_CENTER : return "CHANNEL_TOP_CENTER"; - case MA_CHANNEL_TOP_FRONT_LEFT : return "CHANNEL_TOP_FRONT_LEFT"; - case MA_CHANNEL_TOP_FRONT_CENTER : return "CHANNEL_TOP_FRONT_CENTER"; - case MA_CHANNEL_TOP_FRONT_RIGHT : return "CHANNEL_TOP_FRONT_RIGHT"; - case MA_CHANNEL_TOP_BACK_LEFT : return "CHANNEL_TOP_BACK_LEFT"; - case MA_CHANNEL_TOP_BACK_CENTER : return "CHANNEL_TOP_BACK_CENTER"; - case MA_CHANNEL_TOP_BACK_RIGHT : return "CHANNEL_TOP_BACK_RIGHT"; - case MA_CHANNEL_AUX_0 : return "CHANNEL_AUX_0"; - case MA_CHANNEL_AUX_1 : return "CHANNEL_AUX_1"; - case MA_CHANNEL_AUX_2 : return "CHANNEL_AUX_2"; - case MA_CHANNEL_AUX_3 : return "CHANNEL_AUX_3"; - case MA_CHANNEL_AUX_4 : return "CHANNEL_AUX_4"; - case MA_CHANNEL_AUX_5 : return "CHANNEL_AUX_5"; - case MA_CHANNEL_AUX_6 : return "CHANNEL_AUX_6"; - case MA_CHANNEL_AUX_7 : return "CHANNEL_AUX_7"; - case MA_CHANNEL_AUX_8 : return "CHANNEL_AUX_8"; - case MA_CHANNEL_AUX_9 : return "CHANNEL_AUX_9"; - case MA_CHANNEL_AUX_10 : return "CHANNEL_AUX_10"; - case MA_CHANNEL_AUX_11 : return "CHANNEL_AUX_11"; - case MA_CHANNEL_AUX_12 : return "CHANNEL_AUX_12"; - case MA_CHANNEL_AUX_13 : return "CHANNEL_AUX_13"; - case MA_CHANNEL_AUX_14 : return "CHANNEL_AUX_14"; - case MA_CHANNEL_AUX_15 : return "CHANNEL_AUX_15"; - case MA_CHANNEL_AUX_16 : return "CHANNEL_AUX_16"; - case MA_CHANNEL_AUX_17 : return "CHANNEL_AUX_17"; - case MA_CHANNEL_AUX_18 : return "CHANNEL_AUX_18"; - case MA_CHANNEL_AUX_19 : return "CHANNEL_AUX_19"; - case MA_CHANNEL_AUX_20 : return "CHANNEL_AUX_20"; - case MA_CHANNEL_AUX_21 : return "CHANNEL_AUX_21"; - case MA_CHANNEL_AUX_22 : return "CHANNEL_AUX_22"; - case MA_CHANNEL_AUX_23 : return "CHANNEL_AUX_23"; - case MA_CHANNEL_AUX_24 : return "CHANNEL_AUX_24"; - case MA_CHANNEL_AUX_25 : return "CHANNEL_AUX_25"; - case MA_CHANNEL_AUX_26 : return "CHANNEL_AUX_26"; - case MA_CHANNEL_AUX_27 : return "CHANNEL_AUX_27"; - case MA_CHANNEL_AUX_28 : return "CHANNEL_AUX_28"; - case MA_CHANNEL_AUX_29 : return "CHANNEL_AUX_29"; - case MA_CHANNEL_AUX_30 : return "CHANNEL_AUX_30"; - case MA_CHANNEL_AUX_31 : return "CHANNEL_AUX_31"; - default: break; - } - - return "UNKNOWN"; -} - - - -/************************************************************************************************************************************************************** - -Conversion Helpers - -**************************************************************************************************************************************************************/ -MA_API ma_uint64 ma_convert_frames(void* pOut, ma_uint64 frameCountOut, ma_format formatOut, ma_uint32 channelsOut, ma_uint32 sampleRateOut, const void* pIn, ma_uint64 frameCountIn, ma_format formatIn, ma_uint32 channelsIn, ma_uint32 sampleRateIn) -{ - ma_data_converter_config config; - - config = ma_data_converter_config_init(formatIn, formatOut, channelsIn, channelsOut, sampleRateIn, sampleRateOut); - config.resampling.linear.lpfOrder = ma_min(MA_DEFAULT_RESAMPLER_LPF_ORDER, MA_MAX_FILTER_ORDER); - - return ma_convert_frames_ex(pOut, frameCountOut, pIn, frameCountIn, &config); -} - -MA_API ma_uint64 ma_convert_frames_ex(void* pOut, ma_uint64 frameCountOut, const void* pIn, ma_uint64 frameCountIn, const ma_data_converter_config* pConfig) -{ - ma_result result; - ma_data_converter converter; - - if (frameCountIn == 0 || pConfig == NULL) { - return 0; - } - - result = ma_data_converter_init(pConfig, NULL, &converter); - if (result != MA_SUCCESS) { - return 0; /* Failed to initialize the data converter. */ - } - - if (pOut == NULL) { - result = ma_data_converter_get_expected_output_frame_count(&converter, frameCountIn, &frameCountOut); - if (result != MA_SUCCESS) { - if (result == MA_NOT_IMPLEMENTED) { - /* No way to calculate the number of frames, so we'll need to brute force it and loop. */ - frameCountOut = 0; - - while (frameCountIn > 0) { - ma_uint64 framesProcessedIn = frameCountIn; - ma_uint64 framesProcessedOut = 0xFFFFFFFF; - - result = ma_data_converter_process_pcm_frames(&converter, pIn, &framesProcessedIn, NULL, &framesProcessedOut); - if (result != MA_SUCCESS) { - break; - } - - frameCountIn -= framesProcessedIn; - } - } - } - } else { - result = ma_data_converter_process_pcm_frames(&converter, pIn, &frameCountIn, pOut, &frameCountOut); - if (result != MA_SUCCESS) { - frameCountOut = 0; - } - } - - ma_data_converter_uninit(&converter, NULL); - return frameCountOut; -} - - -/************************************************************************************************************************************************************** - -Ring Buffer - -**************************************************************************************************************************************************************/ -static MA_INLINE ma_uint32 ma_rb__extract_offset_in_bytes(ma_uint32 encodedOffset) -{ - return encodedOffset & 0x7FFFFFFF; -} - -static MA_INLINE ma_uint32 ma_rb__extract_offset_loop_flag(ma_uint32 encodedOffset) -{ - return encodedOffset & 0x80000000; -} - -static MA_INLINE void* ma_rb__get_read_ptr(ma_rb* pRB) -{ - MA_ASSERT(pRB != NULL); - return ma_offset_ptr(pRB->pBuffer, ma_rb__extract_offset_in_bytes(ma_atomic_load_32(&pRB->encodedReadOffset))); -} - -static MA_INLINE void* ma_rb__get_write_ptr(ma_rb* pRB) -{ - MA_ASSERT(pRB != NULL); - return ma_offset_ptr(pRB->pBuffer, ma_rb__extract_offset_in_bytes(ma_atomic_load_32(&pRB->encodedWriteOffset))); -} - -static MA_INLINE ma_uint32 ma_rb__construct_offset(ma_uint32 offsetInBytes, ma_uint32 offsetLoopFlag) -{ - return offsetLoopFlag | offsetInBytes; -} - -static MA_INLINE void ma_rb__deconstruct_offset(ma_uint32 encodedOffset, ma_uint32* pOffsetInBytes, ma_uint32* pOffsetLoopFlag) -{ - MA_ASSERT(pOffsetInBytes != NULL); - MA_ASSERT(pOffsetLoopFlag != NULL); - - *pOffsetInBytes = ma_rb__extract_offset_in_bytes(encodedOffset); - *pOffsetLoopFlag = ma_rb__extract_offset_loop_flag(encodedOffset); -} - - -MA_API ma_result ma_rb_init_ex(size_t subbufferSizeInBytes, size_t subbufferCount, size_t subbufferStrideInBytes, void* pOptionalPreallocatedBuffer, const ma_allocation_callbacks* pAllocationCallbacks, ma_rb* pRB) -{ - ma_result result; - const ma_uint32 maxSubBufferSize = 0x7FFFFFFF - (MA_SIMD_ALIGNMENT-1); - - if (pRB == NULL) { - return MA_INVALID_ARGS; - } - - if (subbufferSizeInBytes == 0 || subbufferCount == 0) { - return MA_INVALID_ARGS; - } - - if (subbufferSizeInBytes > maxSubBufferSize) { - return MA_INVALID_ARGS; /* Maximum buffer size is ~2GB. The most significant bit is a flag for use internally. */ - } - - - MA_ZERO_OBJECT(pRB); - - result = ma_allocation_callbacks_init_copy(&pRB->allocationCallbacks, pAllocationCallbacks); - if (result != MA_SUCCESS) { - return result; - } - - pRB->subbufferSizeInBytes = (ma_uint32)subbufferSizeInBytes; - pRB->subbufferCount = (ma_uint32)subbufferCount; - - if (pOptionalPreallocatedBuffer != NULL) { - pRB->subbufferStrideInBytes = (ma_uint32)subbufferStrideInBytes; - pRB->pBuffer = pOptionalPreallocatedBuffer; - } else { - size_t bufferSizeInBytes; - - /* - Here is where we allocate our own buffer. We always want to align this to MA_SIMD_ALIGNMENT for future SIMD optimization opportunity. To do this - we need to make sure the stride is a multiple of MA_SIMD_ALIGNMENT. - */ - pRB->subbufferStrideInBytes = (pRB->subbufferSizeInBytes + (MA_SIMD_ALIGNMENT-1)) & ~MA_SIMD_ALIGNMENT; - - bufferSizeInBytes = (size_t)pRB->subbufferCount*pRB->subbufferStrideInBytes; - pRB->pBuffer = ma_aligned_malloc(bufferSizeInBytes, MA_SIMD_ALIGNMENT, &pRB->allocationCallbacks); - if (pRB->pBuffer == NULL) { - return MA_OUT_OF_MEMORY; - } - - MA_ZERO_MEMORY(pRB->pBuffer, bufferSizeInBytes); - pRB->ownsBuffer = MA_TRUE; - } - - return MA_SUCCESS; -} - -MA_API ma_result ma_rb_init(size_t bufferSizeInBytes, void* pOptionalPreallocatedBuffer, const ma_allocation_callbacks* pAllocationCallbacks, ma_rb* pRB) -{ - return ma_rb_init_ex(bufferSizeInBytes, 1, 0, pOptionalPreallocatedBuffer, pAllocationCallbacks, pRB); -} - -MA_API void ma_rb_uninit(ma_rb* pRB) -{ - if (pRB == NULL) { - return; - } - - if (pRB->ownsBuffer) { - ma_aligned_free(pRB->pBuffer, &pRB->allocationCallbacks); - } -} - -MA_API void ma_rb_reset(ma_rb* pRB) -{ - if (pRB == NULL) { - return; - } - - ma_atomic_exchange_32(&pRB->encodedReadOffset, 0); - ma_atomic_exchange_32(&pRB->encodedWriteOffset, 0); -} - -MA_API ma_result ma_rb_acquire_read(ma_rb* pRB, size_t* pSizeInBytes, void** ppBufferOut) -{ - ma_uint32 writeOffset; - ma_uint32 writeOffsetInBytes; - ma_uint32 writeOffsetLoopFlag; - ma_uint32 readOffset; - ma_uint32 readOffsetInBytes; - ma_uint32 readOffsetLoopFlag; - size_t bytesAvailable; - size_t bytesRequested; - - if (pRB == NULL || pSizeInBytes == NULL || ppBufferOut == NULL) { - return MA_INVALID_ARGS; - } - - /* The returned buffer should never move ahead of the write pointer. */ - writeOffset = ma_atomic_load_32(&pRB->encodedWriteOffset); - ma_rb__deconstruct_offset(writeOffset, &writeOffsetInBytes, &writeOffsetLoopFlag); - - readOffset = ma_atomic_load_32(&pRB->encodedReadOffset); - ma_rb__deconstruct_offset(readOffset, &readOffsetInBytes, &readOffsetLoopFlag); - - /* - The number of bytes available depends on whether or not the read and write pointers are on the same loop iteration. If so, we - can only read up to the write pointer. If not, we can only read up to the end of the buffer. - */ - if (readOffsetLoopFlag == writeOffsetLoopFlag) { - bytesAvailable = writeOffsetInBytes - readOffsetInBytes; - } else { - bytesAvailable = pRB->subbufferSizeInBytes - readOffsetInBytes; - } - - bytesRequested = *pSizeInBytes; - if (bytesRequested > bytesAvailable) { - bytesRequested = bytesAvailable; - } - - *pSizeInBytes = bytesRequested; - (*ppBufferOut) = ma_rb__get_read_ptr(pRB); - - return MA_SUCCESS; -} - -MA_API ma_result ma_rb_commit_read(ma_rb* pRB, size_t sizeInBytes) -{ - ma_uint32 readOffset; - ma_uint32 readOffsetInBytes; - ma_uint32 readOffsetLoopFlag; - ma_uint32 newReadOffsetInBytes; - ma_uint32 newReadOffsetLoopFlag; - - if (pRB == NULL) { - return MA_INVALID_ARGS; - } - - readOffset = ma_atomic_load_32(&pRB->encodedReadOffset); - ma_rb__deconstruct_offset(readOffset, &readOffsetInBytes, &readOffsetLoopFlag); - - /* Check that sizeInBytes is correct. It should never go beyond the end of the buffer. */ - newReadOffsetInBytes = (ma_uint32)(readOffsetInBytes + sizeInBytes); - if (newReadOffsetInBytes > pRB->subbufferSizeInBytes) { - return MA_INVALID_ARGS; /* <-- sizeInBytes will cause the read offset to overflow. */ - } - - /* Move the read pointer back to the start if necessary. */ - newReadOffsetLoopFlag = readOffsetLoopFlag; - if (newReadOffsetInBytes == pRB->subbufferSizeInBytes) { - newReadOffsetInBytes = 0; - newReadOffsetLoopFlag ^= 0x80000000; - } - - ma_atomic_exchange_32(&pRB->encodedReadOffset, ma_rb__construct_offset(newReadOffsetInBytes, newReadOffsetLoopFlag)); - - return MA_SUCCESS; -} - -MA_API ma_result ma_rb_acquire_write(ma_rb* pRB, size_t* pSizeInBytes, void** ppBufferOut) -{ - ma_uint32 readOffset; - ma_uint32 readOffsetInBytes; - ma_uint32 readOffsetLoopFlag; - ma_uint32 writeOffset; - ma_uint32 writeOffsetInBytes; - ma_uint32 writeOffsetLoopFlag; - size_t bytesAvailable; - size_t bytesRequested; - - if (pRB == NULL || pSizeInBytes == NULL || ppBufferOut == NULL) { - return MA_INVALID_ARGS; - } - - /* The returned buffer should never overtake the read buffer. */ - readOffset = ma_atomic_load_32(&pRB->encodedReadOffset); - ma_rb__deconstruct_offset(readOffset, &readOffsetInBytes, &readOffsetLoopFlag); - - writeOffset = ma_atomic_load_32(&pRB->encodedWriteOffset); - ma_rb__deconstruct_offset(writeOffset, &writeOffsetInBytes, &writeOffsetLoopFlag); - - /* - In the case of writing, if the write pointer and the read pointer are on the same loop iteration we can only - write up to the end of the buffer. Otherwise we can only write up to the read pointer. The write pointer should - never overtake the read pointer. - */ - if (writeOffsetLoopFlag == readOffsetLoopFlag) { - bytesAvailable = pRB->subbufferSizeInBytes - writeOffsetInBytes; - } else { - bytesAvailable = readOffsetInBytes - writeOffsetInBytes; - } - - bytesRequested = *pSizeInBytes; - if (bytesRequested > bytesAvailable) { - bytesRequested = bytesAvailable; - } - - *pSizeInBytes = bytesRequested; - *ppBufferOut = ma_rb__get_write_ptr(pRB); - - /* Clear the buffer if desired. */ - if (pRB->clearOnWriteAcquire) { - MA_ZERO_MEMORY(*ppBufferOut, *pSizeInBytes); - } - - return MA_SUCCESS; -} - -MA_API ma_result ma_rb_commit_write(ma_rb* pRB, size_t sizeInBytes) -{ - ma_uint32 writeOffset; - ma_uint32 writeOffsetInBytes; - ma_uint32 writeOffsetLoopFlag; - ma_uint32 newWriteOffsetInBytes; - ma_uint32 newWriteOffsetLoopFlag; - - if (pRB == NULL) { - return MA_INVALID_ARGS; - } - - writeOffset = ma_atomic_load_32(&pRB->encodedWriteOffset); - ma_rb__deconstruct_offset(writeOffset, &writeOffsetInBytes, &writeOffsetLoopFlag); - - /* Check that sizeInBytes is correct. It should never go beyond the end of the buffer. */ - newWriteOffsetInBytes = (ma_uint32)(writeOffsetInBytes + sizeInBytes); - if (newWriteOffsetInBytes > pRB->subbufferSizeInBytes) { - return MA_INVALID_ARGS; /* <-- sizeInBytes will cause the read offset to overflow. */ - } - - /* Move the read pointer back to the start if necessary. */ - newWriteOffsetLoopFlag = writeOffsetLoopFlag; - if (newWriteOffsetInBytes == pRB->subbufferSizeInBytes) { - newWriteOffsetInBytes = 0; - newWriteOffsetLoopFlag ^= 0x80000000; - } - - ma_atomic_exchange_32(&pRB->encodedWriteOffset, ma_rb__construct_offset(newWriteOffsetInBytes, newWriteOffsetLoopFlag)); - - return MA_SUCCESS; -} - -MA_API ma_result ma_rb_seek_read(ma_rb* pRB, size_t offsetInBytes) -{ - ma_uint32 readOffset; - ma_uint32 readOffsetInBytes; - ma_uint32 readOffsetLoopFlag; - ma_uint32 writeOffset; - ma_uint32 writeOffsetInBytes; - ma_uint32 writeOffsetLoopFlag; - ma_uint32 newReadOffsetInBytes; - ma_uint32 newReadOffsetLoopFlag; - - if (pRB == NULL || offsetInBytes > pRB->subbufferSizeInBytes) { - return MA_INVALID_ARGS; - } - - readOffset = ma_atomic_load_32(&pRB->encodedReadOffset); - ma_rb__deconstruct_offset(readOffset, &readOffsetInBytes, &readOffsetLoopFlag); - - writeOffset = ma_atomic_load_32(&pRB->encodedWriteOffset); - ma_rb__deconstruct_offset(writeOffset, &writeOffsetInBytes, &writeOffsetLoopFlag); - - newReadOffsetLoopFlag = readOffsetLoopFlag; - - /* We cannot go past the write buffer. */ - if (readOffsetLoopFlag == writeOffsetLoopFlag) { - if ((readOffsetInBytes + offsetInBytes) > writeOffsetInBytes) { - newReadOffsetInBytes = writeOffsetInBytes; - } else { - newReadOffsetInBytes = (ma_uint32)(readOffsetInBytes + offsetInBytes); - } - } else { - /* May end up looping. */ - if ((readOffsetInBytes + offsetInBytes) >= pRB->subbufferSizeInBytes) { - newReadOffsetInBytes = (ma_uint32)(readOffsetInBytes + offsetInBytes) - pRB->subbufferSizeInBytes; - newReadOffsetLoopFlag ^= 0x80000000; /* <-- Looped. */ - } else { - newReadOffsetInBytes = (ma_uint32)(readOffsetInBytes + offsetInBytes); - } - } - - ma_atomic_exchange_32(&pRB->encodedReadOffset, ma_rb__construct_offset(newReadOffsetInBytes, newReadOffsetLoopFlag)); - return MA_SUCCESS; -} - -MA_API ma_result ma_rb_seek_write(ma_rb* pRB, size_t offsetInBytes) -{ - ma_uint32 readOffset; - ma_uint32 readOffsetInBytes; - ma_uint32 readOffsetLoopFlag; - ma_uint32 writeOffset; - ma_uint32 writeOffsetInBytes; - ma_uint32 writeOffsetLoopFlag; - ma_uint32 newWriteOffsetInBytes; - ma_uint32 newWriteOffsetLoopFlag; - - if (pRB == NULL) { - return MA_INVALID_ARGS; - } - - readOffset = ma_atomic_load_32(&pRB->encodedReadOffset); - ma_rb__deconstruct_offset(readOffset, &readOffsetInBytes, &readOffsetLoopFlag); - - writeOffset = ma_atomic_load_32(&pRB->encodedWriteOffset); - ma_rb__deconstruct_offset(writeOffset, &writeOffsetInBytes, &writeOffsetLoopFlag); - - newWriteOffsetLoopFlag = writeOffsetLoopFlag; - - /* We cannot go past the write buffer. */ - if (readOffsetLoopFlag == writeOffsetLoopFlag) { - /* May end up looping. */ - if ((writeOffsetInBytes + offsetInBytes) >= pRB->subbufferSizeInBytes) { - newWriteOffsetInBytes = (ma_uint32)(writeOffsetInBytes + offsetInBytes) - pRB->subbufferSizeInBytes; - newWriteOffsetLoopFlag ^= 0x80000000; /* <-- Looped. */ - } else { - newWriteOffsetInBytes = (ma_uint32)(writeOffsetInBytes + offsetInBytes); - } - } else { - if ((writeOffsetInBytes + offsetInBytes) > readOffsetInBytes) { - newWriteOffsetInBytes = readOffsetInBytes; - } else { - newWriteOffsetInBytes = (ma_uint32)(writeOffsetInBytes + offsetInBytes); - } - } - - ma_atomic_exchange_32(&pRB->encodedWriteOffset, ma_rb__construct_offset(newWriteOffsetInBytes, newWriteOffsetLoopFlag)); - return MA_SUCCESS; -} - -MA_API ma_int32 ma_rb_pointer_distance(ma_rb* pRB) -{ - ma_uint32 readOffset; - ma_uint32 readOffsetInBytes; - ma_uint32 readOffsetLoopFlag; - ma_uint32 writeOffset; - ma_uint32 writeOffsetInBytes; - ma_uint32 writeOffsetLoopFlag; - - if (pRB == NULL) { - return 0; - } - - readOffset = ma_atomic_load_32(&pRB->encodedReadOffset); - ma_rb__deconstruct_offset(readOffset, &readOffsetInBytes, &readOffsetLoopFlag); - - writeOffset = ma_atomic_load_32(&pRB->encodedWriteOffset); - ma_rb__deconstruct_offset(writeOffset, &writeOffsetInBytes, &writeOffsetLoopFlag); - - if (readOffsetLoopFlag == writeOffsetLoopFlag) { - return writeOffsetInBytes - readOffsetInBytes; - } else { - return writeOffsetInBytes + (pRB->subbufferSizeInBytes - readOffsetInBytes); - } -} - -MA_API ma_uint32 ma_rb_available_read(ma_rb* pRB) -{ - ma_int32 dist; - - if (pRB == NULL) { - return 0; - } - - dist = ma_rb_pointer_distance(pRB); - if (dist < 0) { - return 0; - } - - return dist; -} - -MA_API ma_uint32 ma_rb_available_write(ma_rb* pRB) -{ - if (pRB == NULL) { - return 0; - } - - return (ma_uint32)(ma_rb_get_subbuffer_size(pRB) - ma_rb_pointer_distance(pRB)); -} - -MA_API size_t ma_rb_get_subbuffer_size(ma_rb* pRB) -{ - if (pRB == NULL) { - return 0; - } - - return pRB->subbufferSizeInBytes; -} - -MA_API size_t ma_rb_get_subbuffer_stride(ma_rb* pRB) -{ - if (pRB == NULL) { - return 0; - } - - if (pRB->subbufferStrideInBytes == 0) { - return (size_t)pRB->subbufferSizeInBytes; - } - - return (size_t)pRB->subbufferStrideInBytes; -} - -MA_API size_t ma_rb_get_subbuffer_offset(ma_rb* pRB, size_t subbufferIndex) -{ - if (pRB == NULL) { - return 0; - } - - return subbufferIndex * ma_rb_get_subbuffer_stride(pRB); -} - -MA_API void* ma_rb_get_subbuffer_ptr(ma_rb* pRB, size_t subbufferIndex, void* pBuffer) -{ - if (pRB == NULL) { - return NULL; - } - - return ma_offset_ptr(pBuffer, ma_rb_get_subbuffer_offset(pRB, subbufferIndex)); -} - - - -static ma_result ma_pcm_rb_data_source__on_read(ma_data_source* pDataSource, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead) -{ - /* Since there's no notion of an end, we don't ever want to return MA_AT_END here. But it is possible to return 0. */ - ma_pcm_rb* pRB = (ma_pcm_rb*)pDataSource; - ma_result result; - ma_uint64 totalFramesRead; - - MA_ASSERT(pRB != NULL); - - /* We need to run this in a loop since the ring buffer itself may loop. */ - totalFramesRead = 0; - while (totalFramesRead < frameCount) { - void* pMappedBuffer; - ma_uint32 mappedFrameCount; - ma_uint64 framesToRead = frameCount - totalFramesRead; - if (framesToRead > 0xFFFFFFFF) { - framesToRead = 0xFFFFFFFF; - } - - mappedFrameCount = (ma_uint32)framesToRead; - result = ma_pcm_rb_acquire_read(pRB, &mappedFrameCount, &pMappedBuffer); - if (result != MA_SUCCESS) { - break; - } - - if (mappedFrameCount == 0) { - break; /* <-- End of ring buffer. */ - } - - ma_copy_pcm_frames(ma_offset_pcm_frames_ptr(pFramesOut, totalFramesRead, pRB->format, pRB->channels), pMappedBuffer, mappedFrameCount, pRB->format, pRB->channels); - - result = ma_pcm_rb_commit_read(pRB, mappedFrameCount); - if (result != MA_SUCCESS) { - break; - } - - totalFramesRead += mappedFrameCount; - } - - /* - There is no notion of an "end" in a ring buffer. If we didn't have enough data to fill the requested frame - count we'll need to pad with silence. If we don't do this, totalFramesRead might equal 0 which will result - in the data source layer at a higher level translating this to MA_AT_END which is incorrect for a ring buffer. - */ - if (totalFramesRead < frameCount) { - ma_silence_pcm_frames(ma_offset_pcm_frames_ptr(pFramesOut, totalFramesRead, pRB->format, pRB->channels), (frameCount - totalFramesRead), pRB->format, pRB->channels); - totalFramesRead = frameCount; - } - - *pFramesRead = totalFramesRead; - return MA_SUCCESS; -} - -static ma_result ma_pcm_rb_data_source__on_get_data_format(ma_data_source* pDataSource, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate, ma_channel* pChannelMap, size_t channelMapCap) -{ - ma_pcm_rb* pRB = (ma_pcm_rb*)pDataSource; - MA_ASSERT(pRB != NULL); - - if (pFormat != NULL) { - *pFormat = pRB->format; - } - - if (pChannels != NULL) { - *pChannels = pRB->channels; - } - - if (pSampleRate != NULL) { - *pSampleRate = pRB->sampleRate; - } - - /* Just assume the default channel map. */ - if (pChannelMap != NULL) { - ma_channel_map_init_standard(ma_standard_channel_map_default, pChannelMap, channelMapCap, pRB->channels); - } - - return MA_SUCCESS; -} - -static ma_data_source_vtable ma_gRBDataSourceVTable = -{ - ma_pcm_rb_data_source__on_read, - NULL, /* onSeek */ - ma_pcm_rb_data_source__on_get_data_format, - NULL, /* onGetCursor */ - NULL, /* onGetLength */ - NULL, /* onSetLooping */ - 0 -}; - -static MA_INLINE ma_uint32 ma_pcm_rb_get_bpf(ma_pcm_rb* pRB) -{ - MA_ASSERT(pRB != NULL); - - return ma_get_bytes_per_frame(pRB->format, pRB->channels); -} - -MA_API ma_result ma_pcm_rb_init_ex(ma_format format, ma_uint32 channels, ma_uint32 subbufferSizeInFrames, ma_uint32 subbufferCount, ma_uint32 subbufferStrideInFrames, void* pOptionalPreallocatedBuffer, const ma_allocation_callbacks* pAllocationCallbacks, ma_pcm_rb* pRB) -{ - ma_uint32 bpf; - ma_result result; - - if (pRB == NULL) { - return MA_INVALID_ARGS; - } - - MA_ZERO_OBJECT(pRB); - - bpf = ma_get_bytes_per_frame(format, channels); - if (bpf == 0) { - return MA_INVALID_ARGS; - } - - result = ma_rb_init_ex(subbufferSizeInFrames*bpf, subbufferCount, subbufferStrideInFrames*bpf, pOptionalPreallocatedBuffer, pAllocationCallbacks, &pRB->rb); - if (result != MA_SUCCESS) { - return result; - } - - pRB->format = format; - pRB->channels = channels; - pRB->sampleRate = 0; /* The sample rate is not passed in as a parameter. */ - - /* The PCM ring buffer is a data source. We need to get that set up as well. */ - { - ma_data_source_config dataSourceConfig = ma_data_source_config_init(); - dataSourceConfig.vtable = &ma_gRBDataSourceVTable; - - result = ma_data_source_init(&dataSourceConfig, &pRB->ds); - if (result != MA_SUCCESS) { - ma_rb_uninit(&pRB->rb); - return result; - } - } - - return MA_SUCCESS; -} - -MA_API ma_result ma_pcm_rb_init(ma_format format, ma_uint32 channels, ma_uint32 bufferSizeInFrames, void* pOptionalPreallocatedBuffer, const ma_allocation_callbacks* pAllocationCallbacks, ma_pcm_rb* pRB) -{ - return ma_pcm_rb_init_ex(format, channels, bufferSizeInFrames, 1, 0, pOptionalPreallocatedBuffer, pAllocationCallbacks, pRB); -} - -MA_API void ma_pcm_rb_uninit(ma_pcm_rb* pRB) -{ - if (pRB == NULL) { - return; - } - - ma_data_source_uninit(&pRB->ds); - ma_rb_uninit(&pRB->rb); -} - -MA_API void ma_pcm_rb_reset(ma_pcm_rb* pRB) -{ - if (pRB == NULL) { - return; - } - - ma_rb_reset(&pRB->rb); -} - -MA_API ma_result ma_pcm_rb_acquire_read(ma_pcm_rb* pRB, ma_uint32* pSizeInFrames, void** ppBufferOut) -{ - size_t sizeInBytes; - ma_result result; - - if (pRB == NULL || pSizeInFrames == NULL) { - return MA_INVALID_ARGS; - } - - sizeInBytes = *pSizeInFrames * ma_pcm_rb_get_bpf(pRB); - - result = ma_rb_acquire_read(&pRB->rb, &sizeInBytes, ppBufferOut); - if (result != MA_SUCCESS) { - return result; - } - - *pSizeInFrames = (ma_uint32)(sizeInBytes / (size_t)ma_pcm_rb_get_bpf(pRB)); - return MA_SUCCESS; -} - -MA_API ma_result ma_pcm_rb_commit_read(ma_pcm_rb* pRB, ma_uint32 sizeInFrames) -{ - if (pRB == NULL) { - return MA_INVALID_ARGS; - } - - return ma_rb_commit_read(&pRB->rb, sizeInFrames * ma_pcm_rb_get_bpf(pRB)); -} - -MA_API ma_result ma_pcm_rb_acquire_write(ma_pcm_rb* pRB, ma_uint32* pSizeInFrames, void** ppBufferOut) -{ - size_t sizeInBytes; - ma_result result; - - if (pRB == NULL) { - return MA_INVALID_ARGS; - } - - sizeInBytes = *pSizeInFrames * ma_pcm_rb_get_bpf(pRB); - - result = ma_rb_acquire_write(&pRB->rb, &sizeInBytes, ppBufferOut); - if (result != MA_SUCCESS) { - return result; - } - - *pSizeInFrames = (ma_uint32)(sizeInBytes / ma_pcm_rb_get_bpf(pRB)); - return MA_SUCCESS; -} - -MA_API ma_result ma_pcm_rb_commit_write(ma_pcm_rb* pRB, ma_uint32 sizeInFrames) -{ - if (pRB == NULL) { - return MA_INVALID_ARGS; - } - - return ma_rb_commit_write(&pRB->rb, sizeInFrames * ma_pcm_rb_get_bpf(pRB)); -} - -MA_API ma_result ma_pcm_rb_seek_read(ma_pcm_rb* pRB, ma_uint32 offsetInFrames) -{ - if (pRB == NULL) { - return MA_INVALID_ARGS; - } - - return ma_rb_seek_read(&pRB->rb, offsetInFrames * ma_pcm_rb_get_bpf(pRB)); -} - -MA_API ma_result ma_pcm_rb_seek_write(ma_pcm_rb* pRB, ma_uint32 offsetInFrames) -{ - if (pRB == NULL) { - return MA_INVALID_ARGS; - } - - return ma_rb_seek_write(&pRB->rb, offsetInFrames * ma_pcm_rb_get_bpf(pRB)); -} - -MA_API ma_int32 ma_pcm_rb_pointer_distance(ma_pcm_rb* pRB) -{ - if (pRB == NULL) { - return 0; - } - - return ma_rb_pointer_distance(&pRB->rb) / ma_pcm_rb_get_bpf(pRB); -} - -MA_API ma_uint32 ma_pcm_rb_available_read(ma_pcm_rb* pRB) -{ - if (pRB == NULL) { - return 0; - } - - return ma_rb_available_read(&pRB->rb) / ma_pcm_rb_get_bpf(pRB); -} - -MA_API ma_uint32 ma_pcm_rb_available_write(ma_pcm_rb* pRB) -{ - if (pRB == NULL) { - return 0; - } - - return ma_rb_available_write(&pRB->rb) / ma_pcm_rb_get_bpf(pRB); -} - -MA_API ma_uint32 ma_pcm_rb_get_subbuffer_size(ma_pcm_rb* pRB) -{ - if (pRB == NULL) { - return 0; - } - - return (ma_uint32)(ma_rb_get_subbuffer_size(&pRB->rb) / ma_pcm_rb_get_bpf(pRB)); -} - -MA_API ma_uint32 ma_pcm_rb_get_subbuffer_stride(ma_pcm_rb* pRB) -{ - if (pRB == NULL) { - return 0; - } - - return (ma_uint32)(ma_rb_get_subbuffer_stride(&pRB->rb) / ma_pcm_rb_get_bpf(pRB)); -} - -MA_API ma_uint32 ma_pcm_rb_get_subbuffer_offset(ma_pcm_rb* pRB, ma_uint32 subbufferIndex) -{ - if (pRB == NULL) { - return 0; - } - - return (ma_uint32)(ma_rb_get_subbuffer_offset(&pRB->rb, subbufferIndex) / ma_pcm_rb_get_bpf(pRB)); -} - -MA_API void* ma_pcm_rb_get_subbuffer_ptr(ma_pcm_rb* pRB, ma_uint32 subbufferIndex, void* pBuffer) -{ - if (pRB == NULL) { - return NULL; - } - - return ma_rb_get_subbuffer_ptr(&pRB->rb, subbufferIndex, pBuffer); -} - -MA_API ma_format ma_pcm_rb_get_format(const ma_pcm_rb* pRB) -{ - if (pRB == NULL) { - return ma_format_unknown; - } - - return pRB->format; -} - -MA_API ma_uint32 ma_pcm_rb_get_channels(const ma_pcm_rb* pRB) -{ - if (pRB == NULL) { - return 0; - } - - return pRB->channels; -} - -MA_API ma_uint32 ma_pcm_rb_get_sample_rate(const ma_pcm_rb* pRB) -{ - if (pRB == NULL) { - return 0; - } - - return pRB->sampleRate; -} - -MA_API void ma_pcm_rb_set_sample_rate(ma_pcm_rb* pRB, ma_uint32 sampleRate) -{ - if (pRB == NULL) { - return; - } - - pRB->sampleRate = sampleRate; -} - - - -MA_API ma_result ma_duplex_rb_init(ma_format captureFormat, ma_uint32 captureChannels, ma_uint32 sampleRate, ma_uint32 captureInternalSampleRate, ma_uint32 captureInternalPeriodSizeInFrames, const ma_allocation_callbacks* pAllocationCallbacks, ma_duplex_rb* pRB) -{ - ma_result result; - ma_uint32 sizeInFrames; - - sizeInFrames = (ma_uint32)ma_calculate_frame_count_after_resampling(sampleRate, captureInternalSampleRate, captureInternalPeriodSizeInFrames * 5); - if (sizeInFrames == 0) { - return MA_INVALID_ARGS; - } - - result = ma_pcm_rb_init(captureFormat, captureChannels, sizeInFrames, NULL, pAllocationCallbacks, &pRB->rb); - if (result != MA_SUCCESS) { - return result; - } - - /* Seek forward a bit so we have a bit of a buffer in case of desyncs. */ - ma_pcm_rb_seek_write((ma_pcm_rb*)pRB, captureInternalPeriodSizeInFrames * 2); - - return MA_SUCCESS; -} - -MA_API ma_result ma_duplex_rb_uninit(ma_duplex_rb* pRB) -{ - ma_pcm_rb_uninit((ma_pcm_rb*)pRB); - return MA_SUCCESS; -} - - - -/************************************************************************************************************************************************************** - -Miscellaneous Helpers - -**************************************************************************************************************************************************************/ -MA_API const char* ma_result_description(ma_result result) -{ - switch (result) - { - case MA_SUCCESS: return "No error"; - case MA_ERROR: return "Unknown error"; - case MA_INVALID_ARGS: return "Invalid argument"; - case MA_INVALID_OPERATION: return "Invalid operation"; - case MA_OUT_OF_MEMORY: return "Out of memory"; - case MA_OUT_OF_RANGE: return "Out of range"; - case MA_ACCESS_DENIED: return "Permission denied"; - case MA_DOES_NOT_EXIST: return "Resource does not exist"; - case MA_ALREADY_EXISTS: return "Resource already exists"; - case MA_TOO_MANY_OPEN_FILES: return "Too many open files"; - case MA_INVALID_FILE: return "Invalid file"; - case MA_TOO_BIG: return "Too large"; - case MA_PATH_TOO_LONG: return "Path too long"; - case MA_NAME_TOO_LONG: return "Name too long"; - case MA_NOT_DIRECTORY: return "Not a directory"; - case MA_IS_DIRECTORY: return "Is a directory"; - case MA_DIRECTORY_NOT_EMPTY: return "Directory not empty"; - case MA_AT_END: return "At end"; - case MA_NO_SPACE: return "No space available"; - case MA_BUSY: return "Device or resource busy"; - case MA_IO_ERROR: return "Input/output error"; - case MA_INTERRUPT: return "Interrupted"; - case MA_UNAVAILABLE: return "Resource unavailable"; - case MA_ALREADY_IN_USE: return "Resource already in use"; - case MA_BAD_ADDRESS: return "Bad address"; - case MA_BAD_SEEK: return "Illegal seek"; - case MA_BAD_PIPE: return "Broken pipe"; - case MA_DEADLOCK: return "Deadlock"; - case MA_TOO_MANY_LINKS: return "Too many links"; - case MA_NOT_IMPLEMENTED: return "Not implemented"; - case MA_NO_MESSAGE: return "No message of desired type"; - case MA_BAD_MESSAGE: return "Invalid message"; - case MA_NO_DATA_AVAILABLE: return "No data available"; - case MA_INVALID_DATA: return "Invalid data"; - case MA_TIMEOUT: return "Timeout"; - case MA_NO_NETWORK: return "Network unavailable"; - case MA_NOT_UNIQUE: return "Not unique"; - case MA_NOT_SOCKET: return "Socket operation on non-socket"; - case MA_NO_ADDRESS: return "Destination address required"; - case MA_BAD_PROTOCOL: return "Protocol wrong type for socket"; - case MA_PROTOCOL_UNAVAILABLE: return "Protocol not available"; - case MA_PROTOCOL_NOT_SUPPORTED: return "Protocol not supported"; - case MA_PROTOCOL_FAMILY_NOT_SUPPORTED: return "Protocol family not supported"; - case MA_ADDRESS_FAMILY_NOT_SUPPORTED: return "Address family not supported"; - case MA_SOCKET_NOT_SUPPORTED: return "Socket type not supported"; - case MA_CONNECTION_RESET: return "Connection reset"; - case MA_ALREADY_CONNECTED: return "Already connected"; - case MA_NOT_CONNECTED: return "Not connected"; - case MA_CONNECTION_REFUSED: return "Connection refused"; - case MA_NO_HOST: return "No host"; - case MA_IN_PROGRESS: return "Operation in progress"; - case MA_CANCELLED: return "Operation cancelled"; - case MA_MEMORY_ALREADY_MAPPED: return "Memory already mapped"; - - case MA_FORMAT_NOT_SUPPORTED: return "Format not supported"; - case MA_DEVICE_TYPE_NOT_SUPPORTED: return "Device type not supported"; - case MA_SHARE_MODE_NOT_SUPPORTED: return "Share mode not supported"; - case MA_NO_BACKEND: return "No backend"; - case MA_NO_DEVICE: return "No device"; - case MA_API_NOT_FOUND: return "API not found"; - case MA_INVALID_DEVICE_CONFIG: return "Invalid device config"; - - case MA_DEVICE_NOT_INITIALIZED: return "Device not initialized"; - case MA_DEVICE_NOT_STARTED: return "Device not started"; - - case MA_FAILED_TO_INIT_BACKEND: return "Failed to initialize backend"; - case MA_FAILED_TO_OPEN_BACKEND_DEVICE: return "Failed to open backend device"; - case MA_FAILED_TO_START_BACKEND_DEVICE: return "Failed to start backend device"; - case MA_FAILED_TO_STOP_BACKEND_DEVICE: return "Failed to stop backend device"; - - default: return "Unknown error"; - } -} - -MA_API void* ma_malloc(size_t sz, const ma_allocation_callbacks* pAllocationCallbacks) -{ - if (pAllocationCallbacks != NULL) { - if (pAllocationCallbacks->onMalloc != NULL) { - return pAllocationCallbacks->onMalloc(sz, pAllocationCallbacks->pUserData); - } else { - return NULL; /* Do not fall back to the default implementation. */ - } - } else { - return ma__malloc_default(sz, NULL); - } -} - -MA_API void* ma_calloc(size_t sz, const ma_allocation_callbacks* pAllocationCallbacks) -{ - void* p = ma_malloc(sz, pAllocationCallbacks); - if (p != NULL) { - MA_ZERO_MEMORY(p, sz); - } - - return p; -} - -MA_API void* ma_realloc(void* p, size_t sz, const ma_allocation_callbacks* pAllocationCallbacks) -{ - if (pAllocationCallbacks != NULL) { - if (pAllocationCallbacks->onRealloc != NULL) { - return pAllocationCallbacks->onRealloc(p, sz, pAllocationCallbacks->pUserData); - } else { - return NULL; /* Do not fall back to the default implementation. */ - } - } else { - return ma__realloc_default(p, sz, NULL); - } -} - -MA_API void ma_free(void* p, const ma_allocation_callbacks* pAllocationCallbacks) -{ - if (p == NULL) { - return; - } - - if (pAllocationCallbacks != NULL) { - if (pAllocationCallbacks->onFree != NULL) { - pAllocationCallbacks->onFree(p, pAllocationCallbacks->pUserData); - } else { - return; /* Do no fall back to the default implementation. */ - } - } else { - ma__free_default(p, NULL); - } -} - -MA_API void* ma_aligned_malloc(size_t sz, size_t alignment, const ma_allocation_callbacks* pAllocationCallbacks) -{ - size_t extraBytes; - void* pUnaligned; - void* pAligned; - - if (alignment == 0) { - return 0; - } - - extraBytes = alignment-1 + sizeof(void*); - - pUnaligned = ma_malloc(sz + extraBytes, pAllocationCallbacks); - if (pUnaligned == NULL) { - return NULL; - } - - pAligned = (void*)(((ma_uintptr)pUnaligned + extraBytes) & ~((ma_uintptr)(alignment-1))); - ((void**)pAligned)[-1] = pUnaligned; - - return pAligned; -} - -MA_API void ma_aligned_free(void* p, const ma_allocation_callbacks* pAllocationCallbacks) -{ - ma_free(((void**)p)[-1], pAllocationCallbacks); -} - -MA_API const char* ma_get_format_name(ma_format format) -{ - switch (format) - { - case ma_format_unknown: return "Unknown"; - case ma_format_u8: return "8-bit Unsigned Integer"; - case ma_format_s16: return "16-bit Signed Integer"; - case ma_format_s24: return "24-bit Signed Integer (Tightly Packed)"; - case ma_format_s32: return "32-bit Signed Integer"; - case ma_format_f32: return "32-bit IEEE Floating Point"; - default: return "Invalid"; - } -} - -MA_API void ma_blend_f32(float* pOut, float* pInA, float* pInB, float factor, ma_uint32 channels) -{ - ma_uint32 i; - for (i = 0; i < channels; ++i) { - pOut[i] = ma_mix_f32(pInA[i], pInB[i], factor); - } -} - - -MA_API ma_uint32 ma_get_bytes_per_sample(ma_format format) -{ - ma_uint32 sizes[] = { - 0, /* unknown */ - 1, /* u8 */ - 2, /* s16 */ - 3, /* s24 */ - 4, /* s32 */ - 4, /* f32 */ - }; - return sizes[format]; -} - - - -#define MA_DATA_SOURCE_DEFAULT_RANGE_BEG 0 -#define MA_DATA_SOURCE_DEFAULT_RANGE_END ~((ma_uint64)0) -#define MA_DATA_SOURCE_DEFAULT_LOOP_POINT_BEG 0 -#define MA_DATA_SOURCE_DEFAULT_LOOP_POINT_END ~((ma_uint64)0) - -MA_API ma_data_source_config ma_data_source_config_init(void) -{ - ma_data_source_config config; - - MA_ZERO_OBJECT(&config); - - return config; -} - - -MA_API ma_result ma_data_source_init(const ma_data_source_config* pConfig, ma_data_source* pDataSource) -{ - ma_data_source_base* pDataSourceBase = (ma_data_source_base*)pDataSource; - - if (pDataSource == NULL) { - return MA_INVALID_ARGS; - } - - MA_ZERO_OBJECT(pDataSourceBase); - - if (pConfig == NULL) { - return MA_INVALID_ARGS; - } - - if (pConfig->vtable == NULL) { - return MA_INVALID_ARGS; - } - - pDataSourceBase->vtable = pConfig->vtable; - pDataSourceBase->rangeBegInFrames = MA_DATA_SOURCE_DEFAULT_RANGE_BEG; - pDataSourceBase->rangeEndInFrames = MA_DATA_SOURCE_DEFAULT_RANGE_END; - pDataSourceBase->loopBegInFrames = MA_DATA_SOURCE_DEFAULT_LOOP_POINT_BEG; - pDataSourceBase->loopEndInFrames = MA_DATA_SOURCE_DEFAULT_LOOP_POINT_END; - pDataSourceBase->pCurrent = pDataSource; /* Always read from ourself by default. */ - pDataSourceBase->pNext = NULL; - pDataSourceBase->onGetNext = NULL; - - return MA_SUCCESS; -} - -MA_API void ma_data_source_uninit(ma_data_source* pDataSource) -{ - if (pDataSource == NULL) { - return; - } - - /* - This is placeholder in case we need this later. Data sources need to call this in their - uninitialization routine to ensure things work later on if something is added here. - */ -} - -static ma_result ma_data_source_resolve_current(ma_data_source* pDataSource, ma_data_source** ppCurrentDataSource) -{ - ma_data_source_base* pCurrentDataSource = (ma_data_source_base*)pDataSource; - - MA_ASSERT(pDataSource != NULL); - MA_ASSERT(ppCurrentDataSource != NULL); - - if (pCurrentDataSource->pCurrent == NULL) { - /* - The current data source is NULL. If we're using this in the context of a chain we need to return NULL - here so that we don't end up looping. Otherwise we just return the data source itself. - */ - if (pCurrentDataSource->pNext != NULL || pCurrentDataSource->onGetNext != NULL) { - pCurrentDataSource = NULL; - } else { - pCurrentDataSource = (ma_data_source_base*)pDataSource; /* Not being used in a chain. Make sure we just always read from the data source itself at all times. */ - } - } else { - pCurrentDataSource = (ma_data_source_base*)pCurrentDataSource->pCurrent; - } - - *ppCurrentDataSource = pCurrentDataSource; - - return MA_SUCCESS; -} - -static ma_result ma_data_source_read_pcm_frames_from_backend(ma_data_source* pDataSource, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead) -{ - ma_data_source_base* pDataSourceBase = (ma_data_source_base*)pDataSource; - - MA_ASSERT(pDataSourceBase != NULL); - MA_ASSERT(pDataSourceBase->vtable != NULL); - MA_ASSERT(pDataSourceBase->vtable->onRead != NULL); - MA_ASSERT(pFramesRead != NULL); - - if (pFramesOut != NULL) { - return pDataSourceBase->vtable->onRead(pDataSourceBase, pFramesOut, frameCount, pFramesRead); - } else { - /* - No output buffer. Probably seeking forward. Read and discard. Can probably optimize this in terms of - onSeek and onGetCursor, but need to keep in mind that the data source may not implement these functions. - */ - ma_result result; - ma_uint64 framesRead; - ma_format format; - ma_uint32 channels; - ma_uint64 discardBufferCapInFrames; - ma_uint8 pDiscardBuffer[4096]; - - result = ma_data_source_get_data_format(pDataSource, &format, &channels, NULL, NULL, 0); - if (result != MA_SUCCESS) { - return result; - } - - discardBufferCapInFrames = sizeof(pDiscardBuffer) / ma_get_bytes_per_frame(format, channels); - - framesRead = 0; - while (framesRead < frameCount) { - ma_uint64 framesReadThisIteration = 0; - ma_uint64 framesToRead = frameCount - framesRead; - if (framesToRead > discardBufferCapInFrames) { - framesToRead = discardBufferCapInFrames; - } - - result = pDataSourceBase->vtable->onRead(pDataSourceBase, pDiscardBuffer, framesToRead, &framesReadThisIteration); - if (result != MA_SUCCESS) { - return result; - } - - framesRead += framesReadThisIteration; - } - - *pFramesRead = framesRead; - - return MA_SUCCESS; - } -} - -static ma_result ma_data_source_read_pcm_frames_within_range(ma_data_source* pDataSource, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead) -{ - ma_data_source_base* pDataSourceBase = (ma_data_source_base*)pDataSource; - ma_result result; - ma_uint64 framesRead = 0; - ma_bool32 loop = ma_data_source_is_looping(pDataSource); - - if (pDataSourceBase == NULL) { - return MA_AT_END; - } - - if (frameCount == 0) { - return MA_INVALID_ARGS; - } - - MA_ASSERT(pDataSourceBase->vtable != NULL); - - if ((pDataSourceBase->vtable->flags & MA_DATA_SOURCE_SELF_MANAGED_RANGE_AND_LOOP_POINT) != 0 || (pDataSourceBase->rangeEndInFrames == ~((ma_uint64)0) && (pDataSourceBase->loopEndInFrames == ~((ma_uint64)0) || loop == MA_FALSE))) { - /* Either the data source is self-managing the range, or no range is set - just read like normal. The data source itself will tell us when the end is reached. */ - result = ma_data_source_read_pcm_frames_from_backend(pDataSource, pFramesOut, frameCount, &framesRead); - } else { - /* Need to clamp to within the range. */ - ma_uint64 relativeCursor; - ma_uint64 absoluteCursor; - - result = ma_data_source_get_cursor_in_pcm_frames(pDataSourceBase, &relativeCursor); - if (result != MA_SUCCESS) { - /* Failed to retrieve the cursor. Cannot read within a range or loop points. Just read like normal - this may happen for things like noise data sources where it doesn't really matter. */ - result = ma_data_source_read_pcm_frames_from_backend(pDataSource, pFramesOut, frameCount, &framesRead); - } else { - ma_uint64 rangeBeg; - ma_uint64 rangeEnd; - - /* We have the cursor. We need to make sure we don't read beyond our range. */ - rangeBeg = pDataSourceBase->rangeBegInFrames; - rangeEnd = pDataSourceBase->rangeEndInFrames; - - absoluteCursor = rangeBeg + relativeCursor; - - /* If looping, make sure we're within range. */ - if (loop) { - if (pDataSourceBase->loopEndInFrames != ~((ma_uint64)0)) { - rangeEnd = ma_min(rangeEnd, pDataSourceBase->rangeBegInFrames + pDataSourceBase->loopEndInFrames); - } - } - - if (frameCount > (rangeEnd - absoluteCursor) && rangeEnd != ~((ma_uint64)0)) { - frameCount = (rangeEnd - absoluteCursor); - } - - /* - If the cursor is sitting on the end of the range the frame count will be set to 0 which can - result in MA_INVALID_ARGS. In this case, we don't want to try reading, but instead return - MA_AT_END so the higher level function can know about it. - */ - if (frameCount > 0) { - result = ma_data_source_read_pcm_frames_from_backend(pDataSource, pFramesOut, frameCount, &framesRead); - } else { - result = MA_AT_END; /* The cursor is sitting on the end of the range which means we're at the end. */ - } - } - } - - if (pFramesRead != NULL) { - *pFramesRead = framesRead; - } - - /* We need to make sure MA_AT_END is returned if we hit the end of the range. */ - if (result == MA_SUCCESS && framesRead == 0) { - result = MA_AT_END; - } - - return result; -} - -MA_API ma_result ma_data_source_read_pcm_frames(ma_data_source* pDataSource, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead) -{ - ma_result result = MA_SUCCESS; - ma_data_source_base* pDataSourceBase = (ma_data_source_base*)pDataSource; - ma_data_source_base* pCurrentDataSource; - void* pRunningFramesOut = pFramesOut; - ma_uint64 totalFramesProcessed = 0; - ma_format format; - ma_uint32 channels; - ma_uint32 emptyLoopCounter = 0; /* Keeps track of how many times 0 frames have been read. For infinite loop detection of sounds with no audio data. */ - ma_bool32 loop; - - if (pFramesRead != NULL) { - *pFramesRead = 0; - } - - if (frameCount == 0) { - return MA_INVALID_ARGS; - } - - if (pDataSourceBase == NULL) { - return MA_INVALID_ARGS; - } - - loop = ma_data_source_is_looping(pDataSource); - - /* - We need to know the data format so we can advance the output buffer as we read frames. If this - fails, chaining will not work and we'll just read as much as we can from the current source. - */ - if (ma_data_source_get_data_format(pDataSource, &format, &channels, NULL, NULL, 0) != MA_SUCCESS) { - result = ma_data_source_resolve_current(pDataSource, (ma_data_source**)&pCurrentDataSource); - if (result != MA_SUCCESS) { - return result; - } - - return ma_data_source_read_pcm_frames_within_range(pCurrentDataSource, pFramesOut, frameCount, pFramesRead); - } - - /* - Looping is a bit of a special case. When the `loop` argument is true, chaining will not work and - only the current data source will be read from. - */ - - /* Keep reading until we've read as many frames as possible. */ - while (totalFramesProcessed < frameCount) { - ma_uint64 framesProcessed; - ma_uint64 framesRemaining = frameCount - totalFramesProcessed; - - /* We need to resolve the data source that we'll actually be reading from. */ - result = ma_data_source_resolve_current(pDataSource, (ma_data_source**)&pCurrentDataSource); - if (result != MA_SUCCESS) { - break; - } - - if (pCurrentDataSource == NULL) { - break; - } - - result = ma_data_source_read_pcm_frames_within_range(pCurrentDataSource, pRunningFramesOut, framesRemaining, &framesProcessed); - totalFramesProcessed += framesProcessed; - - /* - If we encountered an error from the read callback, make sure it's propagated to the caller. The caller may need to know whether or not MA_BUSY is returned which is - not necessarily considered an error. - */ - if (result != MA_SUCCESS && result != MA_AT_END) { - break; - } - - /* - We can determine if we've reached the end by checking if ma_data_source_read_pcm_frames_within_range() returned - MA_AT_END. To loop back to the start, all we need to do is seek back to the first frame. - */ - if (result == MA_AT_END) { - /* - The result needs to be reset back to MA_SUCCESS (from MA_AT_END) so that we don't - accidentally return MA_AT_END when data has been read in prior loop iterations. at the - end of this function, the result will be checked for MA_SUCCESS, and if the total - number of frames processed is 0, will be explicitly set to MA_AT_END. - */ - result = MA_SUCCESS; - - /* - We reached the end. If we're looping, we just loop back to the start of the current - data source. If we're not looping we need to check if we have another in the chain, and - if so, switch to it. - */ - if (loop) { - if (framesProcessed == 0) { - emptyLoopCounter += 1; - if (emptyLoopCounter > 1) { - break; /* Infinite loop detected. Get out. */ - } - } else { - emptyLoopCounter = 0; - } - - result = ma_data_source_seek_to_pcm_frame(pCurrentDataSource, pCurrentDataSource->loopBegInFrames); - if (result != MA_SUCCESS) { - break; /* Failed to loop. Abort. */ - } - - /* Don't return MA_AT_END for looping sounds. */ - result = MA_SUCCESS; - } else { - if (pCurrentDataSource->pNext != NULL) { - pDataSourceBase->pCurrent = pCurrentDataSource->pNext; - } else if (pCurrentDataSource->onGetNext != NULL) { - pDataSourceBase->pCurrent = pCurrentDataSource->onGetNext(pCurrentDataSource); - if (pDataSourceBase->pCurrent == NULL) { - break; /* Our callback did not return a next data source. We're done. */ - } - } else { - /* Reached the end of the chain. We're done. */ - break; - } - - /* The next data source needs to be rewound to ensure data is read in looping scenarios. */ - result = ma_data_source_seek_to_pcm_frame(pDataSourceBase->pCurrent, 0); - if (result != MA_SUCCESS) { - break; - } - } - } - - if (pRunningFramesOut != NULL) { - pRunningFramesOut = ma_offset_ptr(pRunningFramesOut, framesProcessed * ma_get_bytes_per_frame(format, channels)); - } - } - - if (pFramesRead != NULL) { - *pFramesRead = totalFramesProcessed; - } - - MA_ASSERT(!(result == MA_AT_END && totalFramesProcessed > 0)); /* We should never be returning MA_AT_END if we read some data. */ - - if (result == MA_SUCCESS && totalFramesProcessed == 0) { - result = MA_AT_END; - } - - return result; -} - -MA_API ma_result ma_data_source_seek_pcm_frames(ma_data_source* pDataSource, ma_uint64 frameCount, ma_uint64* pFramesSeeked) -{ - return ma_data_source_read_pcm_frames(pDataSource, NULL, frameCount, pFramesSeeked); -} - -MA_API ma_result ma_data_source_seek_to_pcm_frame(ma_data_source* pDataSource, ma_uint64 frameIndex) -{ - ma_data_source_base* pDataSourceBase = (ma_data_source_base*)pDataSource; - - if (pDataSourceBase == NULL) { - return MA_INVALID_ARGS; - } - - if (pDataSourceBase->vtable->onSeek == NULL) { - return MA_NOT_IMPLEMENTED; - } - - if (frameIndex > pDataSourceBase->rangeEndInFrames) { - return MA_INVALID_OPERATION; /* Trying to seek too far forward. */ - } - - MA_ASSERT(pDataSourceBase->vtable != NULL); - - return pDataSourceBase->vtable->onSeek(pDataSource, pDataSourceBase->rangeBegInFrames + frameIndex); -} - -MA_API ma_result ma_data_source_seek_seconds(ma_data_source* pDataSource, float secondCount, float* pSecondsSeeked) -{ - ma_uint64 frameCount; - ma_uint64 framesSeeked = 0; - ma_uint32 sampleRate; - ma_result result; - - if (pDataSource == NULL) { - return MA_INVALID_ARGS; - } - - result = ma_data_source_get_data_format(pDataSource, NULL, NULL, &sampleRate, NULL, 0); - if (result != MA_SUCCESS) { - return result; - } - - /* We need PCM frames instead of seconds */ - frameCount = (ma_uint64)(secondCount * sampleRate); - - result = ma_data_source_seek_pcm_frames(pDataSource, frameCount, &framesSeeked); - - /* VC6 doesn't support division between unsigned 64-bit integer and floating point number. Signed integer needed. This shouldn't affect anything in practice */ - *pSecondsSeeked = (ma_int64)framesSeeked / (float)sampleRate; - return result; -} - -MA_API ma_result ma_data_source_seek_to_second(ma_data_source* pDataSource, float seekPointInSeconds) -{ - ma_uint64 frameIndex; - ma_uint32 sampleRate; - ma_result result; - - if (pDataSource == NULL) { - return MA_INVALID_ARGS; - } - - result = ma_data_source_get_data_format(pDataSource, NULL, NULL, &sampleRate, NULL, 0); - if (result != MA_SUCCESS) { - return result; - } - - /* We need PCM frames instead of seconds */ - frameIndex = (ma_uint64)(seekPointInSeconds * sampleRate); - - return ma_data_source_seek_to_pcm_frame(pDataSource, frameIndex); -} - -MA_API ma_result ma_data_source_get_data_format(ma_data_source* pDataSource, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate, ma_channel* pChannelMap, size_t channelMapCap) -{ - ma_data_source_base* pDataSourceBase = (ma_data_source_base*)pDataSource; - ma_result result; - ma_format format; - ma_uint32 channels; - ma_uint32 sampleRate; - - /* Initialize to defaults for safety just in case the data source does not implement this callback. */ - if (pFormat != NULL) { - *pFormat = ma_format_unknown; - } - if (pChannels != NULL) { - *pChannels = 0; - } - if (pSampleRate != NULL) { - *pSampleRate = 0; - } - if (pChannelMap != NULL) { - MA_ZERO_MEMORY(pChannelMap, sizeof(*pChannelMap) * channelMapCap); - } - - if (pDataSourceBase == NULL) { - return MA_INVALID_ARGS; - } - - MA_ASSERT(pDataSourceBase->vtable != NULL); - - if (pDataSourceBase->vtable->onGetDataFormat == NULL) { - return MA_NOT_IMPLEMENTED; - } - - result = pDataSourceBase->vtable->onGetDataFormat(pDataSource, &format, &channels, &sampleRate, pChannelMap, channelMapCap); - if (result != MA_SUCCESS) { - return result; - } - - if (pFormat != NULL) { - *pFormat = format; - } - if (pChannels != NULL) { - *pChannels = channels; - } - if (pSampleRate != NULL) { - *pSampleRate = sampleRate; - } - - /* Channel map was passed in directly to the callback. This is safe due to the channelMapCap parameter. */ - - return MA_SUCCESS; -} - -MA_API ma_result ma_data_source_get_cursor_in_pcm_frames(ma_data_source* pDataSource, ma_uint64* pCursor) -{ - ma_data_source_base* pDataSourceBase = (ma_data_source_base*)pDataSource; - ma_result result; - ma_uint64 cursor; - - if (pCursor == NULL) { - return MA_INVALID_ARGS; - } - - *pCursor = 0; - - if (pDataSourceBase == NULL) { - return MA_SUCCESS; - } - - MA_ASSERT(pDataSourceBase->vtable != NULL); - - if (pDataSourceBase->vtable->onGetCursor == NULL) { - return MA_NOT_IMPLEMENTED; - } - - result = pDataSourceBase->vtable->onGetCursor(pDataSourceBase, &cursor); - if (result != MA_SUCCESS) { - return result; - } - - /* The cursor needs to be made relative to the start of the range. */ - if (cursor < pDataSourceBase->rangeBegInFrames) { /* Safety check so we don't return some huge number. */ - *pCursor = 0; - } else { - *pCursor = cursor - pDataSourceBase->rangeBegInFrames; - } - - return MA_SUCCESS; -} - -MA_API ma_result ma_data_source_get_length_in_pcm_frames(ma_data_source* pDataSource, ma_uint64* pLength) -{ - ma_data_source_base* pDataSourceBase = (ma_data_source_base*)pDataSource; - - if (pLength == NULL) { - return MA_INVALID_ARGS; - } - - *pLength = 0; - - if (pDataSourceBase == NULL) { - return MA_INVALID_ARGS; - } - - MA_ASSERT(pDataSourceBase->vtable != NULL); - - /* - If we have a range defined we'll use that to determine the length. This is one of rare times - where we'll actually trust the caller. If they've set the range, I think it's mostly safe to - assume they've set it based on some higher level knowledge of the structure of the sound bank. - */ - if (pDataSourceBase->rangeEndInFrames != ~((ma_uint64)0)) { - *pLength = pDataSourceBase->rangeEndInFrames - pDataSourceBase->rangeBegInFrames; - return MA_SUCCESS; - } - - /* - Getting here means a range is not defined so we'll need to get the data source itself to tell - us the length. - */ - if (pDataSourceBase->vtable->onGetLength == NULL) { - return MA_NOT_IMPLEMENTED; - } - - return pDataSourceBase->vtable->onGetLength(pDataSource, pLength); -} - -MA_API ma_result ma_data_source_get_cursor_in_seconds(ma_data_source* pDataSource, float* pCursor) -{ - ma_result result; - ma_uint64 cursorInPCMFrames; - ma_uint32 sampleRate; - - if (pCursor == NULL) { - return MA_INVALID_ARGS; - } - - *pCursor = 0; - - result = ma_data_source_get_cursor_in_pcm_frames(pDataSource, &cursorInPCMFrames); - if (result != MA_SUCCESS) { - return result; - } - - result = ma_data_source_get_data_format(pDataSource, NULL, NULL, &sampleRate, NULL, 0); - if (result != MA_SUCCESS) { - return result; - } - - /* VC6 does not support division of unsigned 64-bit integers with floating point numbers. Need to use a signed number. This shouldn't effect anything in practice. */ - *pCursor = (ma_int64)cursorInPCMFrames / (float)sampleRate; - - return MA_SUCCESS; -} - -MA_API ma_result ma_data_source_get_length_in_seconds(ma_data_source* pDataSource, float* pLength) -{ - ma_result result; - ma_uint64 lengthInPCMFrames; - ma_uint32 sampleRate; - - if (pLength == NULL) { - return MA_INVALID_ARGS; - } - - *pLength = 0; - - result = ma_data_source_get_length_in_pcm_frames(pDataSource, &lengthInPCMFrames); - if (result != MA_SUCCESS) { - return result; - } - - result = ma_data_source_get_data_format(pDataSource, NULL, NULL, &sampleRate, NULL, 0); - if (result != MA_SUCCESS) { - return result; - } - - /* VC6 does not support division of unsigned 64-bit integers with floating point numbers. Need to use a signed number. This shouldn't effect anything in practice. */ - *pLength = (ma_int64)lengthInPCMFrames / (float)sampleRate; - - return MA_SUCCESS; -} - -MA_API ma_result ma_data_source_set_looping(ma_data_source* pDataSource, ma_bool32 isLooping) -{ - ma_data_source_base* pDataSourceBase = (ma_data_source_base*)pDataSource; - - if (pDataSource == NULL) { - return MA_INVALID_ARGS; - } - - ma_atomic_exchange_32(&pDataSourceBase->isLooping, isLooping); - - MA_ASSERT(pDataSourceBase->vtable != NULL); - - /* If there's no callback for this just treat it as a successful no-op. */ - if (pDataSourceBase->vtable->onSetLooping == NULL) { - return MA_SUCCESS; - } - - return pDataSourceBase->vtable->onSetLooping(pDataSource, isLooping); -} - -MA_API ma_bool32 ma_data_source_is_looping(const ma_data_source* pDataSource) -{ - const ma_data_source_base* pDataSourceBase = (const ma_data_source_base*)pDataSource; - - if (pDataSource == NULL) { - return MA_FALSE; - } - - return ma_atomic_load_32(&pDataSourceBase->isLooping); -} - -MA_API ma_result ma_data_source_set_range_in_pcm_frames(ma_data_source* pDataSource, ma_uint64 rangeBegInFrames, ma_uint64 rangeEndInFrames) -{ - ma_data_source_base* pDataSourceBase = (ma_data_source_base*)pDataSource; - ma_result result; - ma_uint64 relativeCursor; - ma_uint64 absoluteCursor; - ma_bool32 doSeekAdjustment = MA_FALSE; - - if (pDataSource == NULL) { - return MA_INVALID_ARGS; - } - - if (rangeEndInFrames < rangeBegInFrames) { - return MA_INVALID_ARGS; /* The end of the range must come after the beginning. */ - } - - /* - We may need to adjust the position of the cursor to ensure it's clamped to the range. Grab it now - so we can calculate its absolute position before we change the range. - */ - result = ma_data_source_get_cursor_in_pcm_frames(pDataSource, &relativeCursor); - if (result == MA_SUCCESS) { - doSeekAdjustment = MA_TRUE; - absoluteCursor = relativeCursor + pDataSourceBase->rangeBegInFrames; - } else { - /* - We couldn't get the position of the cursor. It probably means the data source has no notion - of a cursor. We'll just leave it at position 0. Don't treat this as an error. - */ - doSeekAdjustment = MA_FALSE; - relativeCursor = 0; - absoluteCursor = 0; - } - - pDataSourceBase->rangeBegInFrames = rangeBegInFrames; - pDataSourceBase->rangeEndInFrames = rangeEndInFrames; - - /* - The commented out logic below was intended to maintain loop points in response to a change in the - range. However, this is not useful because it results in the sound breaking when you move the range - outside of the old loop points. I'm simplifying this by simply resetting the loop points. The - caller is expected to update their loop points if they change the range. - - In practice this should be mostly a non-issue because the majority of the time the range will be - set once right after initialization. - */ - pDataSourceBase->loopBegInFrames = 0; - pDataSourceBase->loopEndInFrames = ~((ma_uint64)0); - - - /* - Seek to within range. Note that our seek positions here are relative to the new range. We don't want - to do this if we failed to retrieve the cursor earlier on because it probably means the data source - has no notion of a cursor. In practice the seek would probably fail (which we silently ignore), but - I'm just not even going to attempt it. - */ - if (doSeekAdjustment) { - if (absoluteCursor < rangeBegInFrames) { - ma_data_source_seek_to_pcm_frame(pDataSource, 0); - } else if (absoluteCursor > rangeEndInFrames) { - ma_data_source_seek_to_pcm_frame(pDataSource, rangeEndInFrames - rangeBegInFrames); - } - } - - return MA_SUCCESS; -} - -MA_API void ma_data_source_get_range_in_pcm_frames(const ma_data_source* pDataSource, ma_uint64* pRangeBegInFrames, ma_uint64* pRangeEndInFrames) -{ - const ma_data_source_base* pDataSourceBase = (const ma_data_source_base*)pDataSource; - - if (pRangeBegInFrames != NULL) { - *pRangeBegInFrames = 0; - } - if (pRangeEndInFrames != NULL) { - *pRangeEndInFrames = 0; - } - - if (pDataSource == NULL) { - return; - } - - if (pRangeBegInFrames != NULL) { - *pRangeBegInFrames = pDataSourceBase->rangeBegInFrames; - } - - if (pRangeEndInFrames != NULL) { - *pRangeEndInFrames = pDataSourceBase->rangeEndInFrames; - } -} - -MA_API ma_result ma_data_source_set_loop_point_in_pcm_frames(ma_data_source* pDataSource, ma_uint64 loopBegInFrames, ma_uint64 loopEndInFrames) -{ - ma_data_source_base* pDataSourceBase = (ma_data_source_base*)pDataSource; - - if (pDataSource == NULL) { - return MA_INVALID_ARGS; - } - - if (loopEndInFrames < loopBegInFrames) { - return MA_INVALID_ARGS; /* The end of the loop point must come after the beginning. */ - } - - if (loopEndInFrames > pDataSourceBase->rangeEndInFrames && loopEndInFrames != ~((ma_uint64)0)) { - return MA_INVALID_ARGS; /* The end of the loop point must not go beyond the range. */ - } - - pDataSourceBase->loopBegInFrames = loopBegInFrames; - pDataSourceBase->loopEndInFrames = loopEndInFrames; - - /* The end cannot exceed the range. */ - if (pDataSourceBase->loopEndInFrames > (pDataSourceBase->rangeEndInFrames - pDataSourceBase->rangeBegInFrames) && pDataSourceBase->loopEndInFrames != ~((ma_uint64)0)) { - pDataSourceBase->loopEndInFrames = (pDataSourceBase->rangeEndInFrames - pDataSourceBase->rangeBegInFrames); - } - - return MA_SUCCESS; -} - -MA_API void ma_data_source_get_loop_point_in_pcm_frames(const ma_data_source* pDataSource, ma_uint64* pLoopBegInFrames, ma_uint64* pLoopEndInFrames) -{ - const ma_data_source_base* pDataSourceBase = (const ma_data_source_base*)pDataSource; - - if (pLoopBegInFrames != NULL) { - *pLoopBegInFrames = 0; - } - if (pLoopEndInFrames != NULL) { - *pLoopEndInFrames = 0; - } - - if (pDataSource == NULL) { - return; - } - - if (pLoopBegInFrames != NULL) { - *pLoopBegInFrames = pDataSourceBase->loopBegInFrames; - } - - if (pLoopEndInFrames != NULL) { - *pLoopEndInFrames = pDataSourceBase->loopEndInFrames; - } -} - -MA_API ma_result ma_data_source_set_current(ma_data_source* pDataSource, ma_data_source* pCurrentDataSource) -{ - ma_data_source_base* pDataSourceBase = (ma_data_source_base*)pDataSource; - - if (pDataSource == NULL) { - return MA_INVALID_ARGS; - } - - pDataSourceBase->pCurrent = pCurrentDataSource; - - return MA_SUCCESS; -} - -MA_API ma_data_source* ma_data_source_get_current(const ma_data_source* pDataSource) -{ - const ma_data_source_base* pDataSourceBase = (const ma_data_source_base*)pDataSource; - - if (pDataSource == NULL) { - return NULL; - } - - return pDataSourceBase->pCurrent; -} - -MA_API ma_result ma_data_source_set_next(ma_data_source* pDataSource, ma_data_source* pNextDataSource) -{ - ma_data_source_base* pDataSourceBase = (ma_data_source_base*)pDataSource; - - if (pDataSource == NULL) { - return MA_INVALID_ARGS; - } - - pDataSourceBase->pNext = pNextDataSource; - - return MA_SUCCESS; -} - -MA_API ma_data_source* ma_data_source_get_next(const ma_data_source* pDataSource) -{ - const ma_data_source_base* pDataSourceBase = (const ma_data_source_base*)pDataSource; - - if (pDataSource == NULL) { - return NULL; - } - - return pDataSourceBase->pNext; -} - -MA_API ma_result ma_data_source_set_next_callback(ma_data_source* pDataSource, ma_data_source_get_next_proc onGetNext) -{ - ma_data_source_base* pDataSourceBase = (ma_data_source_base*)pDataSource; - - if (pDataSource == NULL) { - return MA_INVALID_ARGS; - } - - pDataSourceBase->onGetNext = onGetNext; - - return MA_SUCCESS; -} - -MA_API ma_data_source_get_next_proc ma_data_source_get_next_callback(const ma_data_source* pDataSource) -{ - const ma_data_source_base* pDataSourceBase = (const ma_data_source_base*)pDataSource; - - if (pDataSource == NULL) { - return NULL; - } - - return pDataSourceBase->onGetNext; -} - - -static ma_result ma_audio_buffer_ref__data_source_on_read(ma_data_source* pDataSource, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead) -{ - ma_audio_buffer_ref* pAudioBufferRef = (ma_audio_buffer_ref*)pDataSource; - ma_uint64 framesRead = ma_audio_buffer_ref_read_pcm_frames(pAudioBufferRef, pFramesOut, frameCount, MA_FALSE); - - if (pFramesRead != NULL) { - *pFramesRead = framesRead; - } - - if (framesRead < frameCount || framesRead == 0) { - return MA_AT_END; - } - - return MA_SUCCESS; -} - -static ma_result ma_audio_buffer_ref__data_source_on_seek(ma_data_source* pDataSource, ma_uint64 frameIndex) -{ - return ma_audio_buffer_ref_seek_to_pcm_frame((ma_audio_buffer_ref*)pDataSource, frameIndex); -} - -static ma_result ma_audio_buffer_ref__data_source_on_get_data_format(ma_data_source* pDataSource, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate, ma_channel* pChannelMap, size_t channelMapCap) -{ - ma_audio_buffer_ref* pAudioBufferRef = (ma_audio_buffer_ref*)pDataSource; - - *pFormat = pAudioBufferRef->format; - *pChannels = pAudioBufferRef->channels; - *pSampleRate = pAudioBufferRef->sampleRate; - ma_channel_map_init_standard(ma_standard_channel_map_default, pChannelMap, channelMapCap, pAudioBufferRef->channels); - - return MA_SUCCESS; -} - -static ma_result ma_audio_buffer_ref__data_source_on_get_cursor(ma_data_source* pDataSource, ma_uint64* pCursor) -{ - ma_audio_buffer_ref* pAudioBufferRef = (ma_audio_buffer_ref*)pDataSource; - - *pCursor = pAudioBufferRef->cursor; - - return MA_SUCCESS; -} - -static ma_result ma_audio_buffer_ref__data_source_on_get_length(ma_data_source* pDataSource, ma_uint64* pLength) -{ - ma_audio_buffer_ref* pAudioBufferRef = (ma_audio_buffer_ref*)pDataSource; - - *pLength = pAudioBufferRef->sizeInFrames; - - return MA_SUCCESS; -} - -static ma_data_source_vtable g_ma_audio_buffer_ref_data_source_vtable = -{ - ma_audio_buffer_ref__data_source_on_read, - ma_audio_buffer_ref__data_source_on_seek, - ma_audio_buffer_ref__data_source_on_get_data_format, - ma_audio_buffer_ref__data_source_on_get_cursor, - ma_audio_buffer_ref__data_source_on_get_length, - NULL, /* onSetLooping */ - 0 -}; - -MA_API ma_result ma_audio_buffer_ref_init(ma_format format, ma_uint32 channels, const void* pData, ma_uint64 sizeInFrames, ma_audio_buffer_ref* pAudioBufferRef) -{ - ma_result result; - ma_data_source_config dataSourceConfig; - - if (pAudioBufferRef == NULL) { - return MA_INVALID_ARGS; - } - - MA_ZERO_OBJECT(pAudioBufferRef); - - dataSourceConfig = ma_data_source_config_init(); - dataSourceConfig.vtable = &g_ma_audio_buffer_ref_data_source_vtable; - - result = ma_data_source_init(&dataSourceConfig, &pAudioBufferRef->ds); - if (result != MA_SUCCESS) { - return result; - } - - pAudioBufferRef->format = format; - pAudioBufferRef->channels = channels; - pAudioBufferRef->sampleRate = 0; /* TODO: Version 0.12. Set this to sampleRate. */ - pAudioBufferRef->cursor = 0; - pAudioBufferRef->sizeInFrames = sizeInFrames; - pAudioBufferRef->pData = pData; - - return MA_SUCCESS; -} - -MA_API void ma_audio_buffer_ref_uninit(ma_audio_buffer_ref* pAudioBufferRef) -{ - if (pAudioBufferRef == NULL) { - return; - } - - ma_data_source_uninit(&pAudioBufferRef->ds); -} - -MA_API ma_result ma_audio_buffer_ref_set_data(ma_audio_buffer_ref* pAudioBufferRef, const void* pData, ma_uint64 sizeInFrames) -{ - if (pAudioBufferRef == NULL) { - return MA_INVALID_ARGS; - } - - pAudioBufferRef->cursor = 0; - pAudioBufferRef->sizeInFrames = sizeInFrames; - pAudioBufferRef->pData = pData; - - return MA_SUCCESS; -} - -MA_API ma_uint64 ma_audio_buffer_ref_read_pcm_frames(ma_audio_buffer_ref* pAudioBufferRef, void* pFramesOut, ma_uint64 frameCount, ma_bool32 loop) -{ - ma_uint64 totalFramesRead = 0; - - if (pAudioBufferRef == NULL) { - return 0; - } - - if (frameCount == 0) { - return 0; - } - - while (totalFramesRead < frameCount) { - ma_uint64 framesAvailable = pAudioBufferRef->sizeInFrames - pAudioBufferRef->cursor; - ma_uint64 framesRemaining = frameCount - totalFramesRead; - ma_uint64 framesToRead; - - framesToRead = framesRemaining; - if (framesToRead > framesAvailable) { - framesToRead = framesAvailable; - } - - if (pFramesOut != NULL) { - ma_copy_pcm_frames(ma_offset_ptr(pFramesOut, totalFramesRead * ma_get_bytes_per_frame(pAudioBufferRef->format, pAudioBufferRef->channels)), ma_offset_ptr(pAudioBufferRef->pData, pAudioBufferRef->cursor * ma_get_bytes_per_frame(pAudioBufferRef->format, pAudioBufferRef->channels)), framesToRead, pAudioBufferRef->format, pAudioBufferRef->channels); - } - - totalFramesRead += framesToRead; - - pAudioBufferRef->cursor += framesToRead; - if (pAudioBufferRef->cursor == pAudioBufferRef->sizeInFrames) { - if (loop) { - pAudioBufferRef->cursor = 0; - } else { - break; /* We've reached the end and we're not looping. Done. */ - } - } - - MA_ASSERT(pAudioBufferRef->cursor < pAudioBufferRef->sizeInFrames); - } - - return totalFramesRead; -} - -MA_API ma_result ma_audio_buffer_ref_seek_to_pcm_frame(ma_audio_buffer_ref* pAudioBufferRef, ma_uint64 frameIndex) -{ - if (pAudioBufferRef == NULL) { - return MA_INVALID_ARGS; - } - - if (frameIndex > pAudioBufferRef->sizeInFrames) { - return MA_INVALID_ARGS; - } - - pAudioBufferRef->cursor = (size_t)frameIndex; - - return MA_SUCCESS; -} - -MA_API ma_result ma_audio_buffer_ref_map(ma_audio_buffer_ref* pAudioBufferRef, void** ppFramesOut, ma_uint64* pFrameCount) -{ - ma_uint64 framesAvailable; - ma_uint64 frameCount = 0; - - if (ppFramesOut != NULL) { - *ppFramesOut = NULL; /* Safety. */ - } - - if (pFrameCount != NULL) { - frameCount = *pFrameCount; - *pFrameCount = 0; /* Safety. */ - } - - if (pAudioBufferRef == NULL || ppFramesOut == NULL || pFrameCount == NULL) { - return MA_INVALID_ARGS; - } - - framesAvailable = pAudioBufferRef->sizeInFrames - pAudioBufferRef->cursor; - if (frameCount > framesAvailable) { - frameCount = framesAvailable; - } - - *ppFramesOut = ma_offset_ptr(pAudioBufferRef->pData, pAudioBufferRef->cursor * ma_get_bytes_per_frame(pAudioBufferRef->format, pAudioBufferRef->channels)); - *pFrameCount = frameCount; - - return MA_SUCCESS; -} - -MA_API ma_result ma_audio_buffer_ref_unmap(ma_audio_buffer_ref* pAudioBufferRef, ma_uint64 frameCount) -{ - ma_uint64 framesAvailable; - - if (pAudioBufferRef == NULL) { - return MA_INVALID_ARGS; - } - - framesAvailable = pAudioBufferRef->sizeInFrames - pAudioBufferRef->cursor; - if (frameCount > framesAvailable) { - return MA_INVALID_ARGS; /* The frame count was too big. This should never happen in an unmapping. Need to make sure the caller is aware of this. */ - } - - pAudioBufferRef->cursor += frameCount; - - if (pAudioBufferRef->cursor == pAudioBufferRef->sizeInFrames) { - return MA_AT_END; /* Successful. Need to tell the caller that the end has been reached so that it can loop if desired. */ - } else { - return MA_SUCCESS; - } -} - -MA_API ma_bool32 ma_audio_buffer_ref_at_end(const ma_audio_buffer_ref* pAudioBufferRef) -{ - if (pAudioBufferRef == NULL) { - return MA_FALSE; - } - - return pAudioBufferRef->cursor == pAudioBufferRef->sizeInFrames; -} - -MA_API ma_result ma_audio_buffer_ref_get_cursor_in_pcm_frames(const ma_audio_buffer_ref* pAudioBufferRef, ma_uint64* pCursor) -{ - if (pCursor == NULL) { - return MA_INVALID_ARGS; - } - - *pCursor = 0; - - if (pAudioBufferRef == NULL) { - return MA_INVALID_ARGS; - } - - *pCursor = pAudioBufferRef->cursor; - - return MA_SUCCESS; -} - -MA_API ma_result ma_audio_buffer_ref_get_length_in_pcm_frames(const ma_audio_buffer_ref* pAudioBufferRef, ma_uint64* pLength) -{ - if (pLength == NULL) { - return MA_INVALID_ARGS; - } - - *pLength = 0; - - if (pAudioBufferRef == NULL) { - return MA_INVALID_ARGS; - } - - *pLength = pAudioBufferRef->sizeInFrames; - - return MA_SUCCESS; -} - -MA_API ma_result ma_audio_buffer_ref_get_available_frames(const ma_audio_buffer_ref* pAudioBufferRef, ma_uint64* pAvailableFrames) -{ - if (pAvailableFrames == NULL) { - return MA_INVALID_ARGS; - } - - *pAvailableFrames = 0; - - if (pAudioBufferRef == NULL) { - return MA_INVALID_ARGS; - } - - if (pAudioBufferRef->sizeInFrames <= pAudioBufferRef->cursor) { - *pAvailableFrames = 0; - } else { - *pAvailableFrames = pAudioBufferRef->sizeInFrames - pAudioBufferRef->cursor; - } - - return MA_SUCCESS; -} - - - - -MA_API ma_audio_buffer_config ma_audio_buffer_config_init(ma_format format, ma_uint32 channels, ma_uint64 sizeInFrames, const void* pData, const ma_allocation_callbacks* pAllocationCallbacks) -{ - ma_audio_buffer_config config; - - MA_ZERO_OBJECT(&config); - config.format = format; - config.channels = channels; - config.sampleRate = 0; /* TODO: Version 0.12. Set this to sampleRate. */ - config.sizeInFrames = sizeInFrames; - config.pData = pData; - ma_allocation_callbacks_init_copy(&config.allocationCallbacks, pAllocationCallbacks); - - return config; -} - -static ma_result ma_audio_buffer_init_ex(const ma_audio_buffer_config* pConfig, ma_bool32 doCopy, ma_audio_buffer* pAudioBuffer) -{ - ma_result result; - - if (pAudioBuffer == NULL) { - return MA_INVALID_ARGS; - } - - MA_ZERO_MEMORY(pAudioBuffer, sizeof(*pAudioBuffer) - sizeof(pAudioBuffer->_pExtraData)); /* Safety. Don't overwrite the extra data. */ - - if (pConfig == NULL) { - return MA_INVALID_ARGS; - } - - if (pConfig->sizeInFrames == 0) { - return MA_INVALID_ARGS; /* Not allowing buffer sizes of 0 frames. */ - } - - result = ma_audio_buffer_ref_init(pConfig->format, pConfig->channels, NULL, 0, &pAudioBuffer->ref); - if (result != MA_SUCCESS) { - return result; - } - - /* TODO: Version 0.12. Set this in ma_audio_buffer_ref_init() instead of here. */ - pAudioBuffer->ref.sampleRate = pConfig->sampleRate; - - ma_allocation_callbacks_init_copy(&pAudioBuffer->allocationCallbacks, &pConfig->allocationCallbacks); - - if (doCopy) { - ma_uint64 allocationSizeInBytes; - void* pData; - - allocationSizeInBytes = pConfig->sizeInFrames * ma_get_bytes_per_frame(pConfig->format, pConfig->channels); - if (allocationSizeInBytes > MA_SIZE_MAX) { - return MA_OUT_OF_MEMORY; /* Too big. */ - } - - pData = ma_malloc((size_t)allocationSizeInBytes, &pAudioBuffer->allocationCallbacks); /* Safe cast to size_t. */ - if (pData == NULL) { - return MA_OUT_OF_MEMORY; - } - - if (pConfig->pData != NULL) { - ma_copy_pcm_frames(pData, pConfig->pData, pConfig->sizeInFrames, pConfig->format, pConfig->channels); - } else { - ma_silence_pcm_frames(pData, pConfig->sizeInFrames, pConfig->format, pConfig->channels); - } - - ma_audio_buffer_ref_set_data(&pAudioBuffer->ref, pData, pConfig->sizeInFrames); - pAudioBuffer->ownsData = MA_TRUE; - } else { - ma_audio_buffer_ref_set_data(&pAudioBuffer->ref, pConfig->pData, pConfig->sizeInFrames); - pAudioBuffer->ownsData = MA_FALSE; - } - - return MA_SUCCESS; -} - -static void ma_audio_buffer_uninit_ex(ma_audio_buffer* pAudioBuffer, ma_bool32 doFree) -{ - if (pAudioBuffer == NULL) { - return; - } - - if (pAudioBuffer->ownsData && pAudioBuffer->ref.pData != &pAudioBuffer->_pExtraData[0]) { - ma_free((void*)pAudioBuffer->ref.pData, &pAudioBuffer->allocationCallbacks); /* Naugty const cast, but OK in this case since we've guarded it with the ownsData check. */ - } - - if (doFree) { - ma_free(pAudioBuffer, &pAudioBuffer->allocationCallbacks); - } - - ma_audio_buffer_ref_uninit(&pAudioBuffer->ref); -} - -MA_API ma_result ma_audio_buffer_init(const ma_audio_buffer_config* pConfig, ma_audio_buffer* pAudioBuffer) -{ - return ma_audio_buffer_init_ex(pConfig, MA_FALSE, pAudioBuffer); -} - -MA_API ma_result ma_audio_buffer_init_copy(const ma_audio_buffer_config* pConfig, ma_audio_buffer* pAudioBuffer) -{ - return ma_audio_buffer_init_ex(pConfig, MA_TRUE, pAudioBuffer); -} - -MA_API ma_result ma_audio_buffer_alloc_and_init(const ma_audio_buffer_config* pConfig, ma_audio_buffer** ppAudioBuffer) -{ - ma_result result; - ma_audio_buffer* pAudioBuffer; - ma_audio_buffer_config innerConfig; /* We'll be making some changes to the config, so need to make a copy. */ - ma_uint64 allocationSizeInBytes; - - if (ppAudioBuffer == NULL) { - return MA_INVALID_ARGS; - } - - *ppAudioBuffer = NULL; /* Safety. */ - - if (pConfig == NULL) { - return MA_INVALID_ARGS; - } - - innerConfig = *pConfig; - ma_allocation_callbacks_init_copy(&innerConfig.allocationCallbacks, &pConfig->allocationCallbacks); - - allocationSizeInBytes = sizeof(*pAudioBuffer) - sizeof(pAudioBuffer->_pExtraData) + (pConfig->sizeInFrames * ma_get_bytes_per_frame(pConfig->format, pConfig->channels)); - if (allocationSizeInBytes > MA_SIZE_MAX) { - return MA_OUT_OF_MEMORY; /* Too big. */ - } - - pAudioBuffer = (ma_audio_buffer*)ma_malloc((size_t)allocationSizeInBytes, &innerConfig.allocationCallbacks); /* Safe cast to size_t. */ - if (pAudioBuffer == NULL) { - return MA_OUT_OF_MEMORY; - } - - if (pConfig->pData != NULL) { - ma_copy_pcm_frames(&pAudioBuffer->_pExtraData[0], pConfig->pData, pConfig->sizeInFrames, pConfig->format, pConfig->channels); - } else { - ma_silence_pcm_frames(&pAudioBuffer->_pExtraData[0], pConfig->sizeInFrames, pConfig->format, pConfig->channels); - } - - innerConfig.pData = &pAudioBuffer->_pExtraData[0]; - - result = ma_audio_buffer_init_ex(&innerConfig, MA_FALSE, pAudioBuffer); - if (result != MA_SUCCESS) { - ma_free(pAudioBuffer, &innerConfig.allocationCallbacks); - return result; - } - - *ppAudioBuffer = pAudioBuffer; - - return MA_SUCCESS; -} - -MA_API void ma_audio_buffer_uninit(ma_audio_buffer* pAudioBuffer) -{ - ma_audio_buffer_uninit_ex(pAudioBuffer, MA_FALSE); -} - -MA_API void ma_audio_buffer_uninit_and_free(ma_audio_buffer* pAudioBuffer) -{ - ma_audio_buffer_uninit_ex(pAudioBuffer, MA_TRUE); -} - -MA_API ma_uint64 ma_audio_buffer_read_pcm_frames(ma_audio_buffer* pAudioBuffer, void* pFramesOut, ma_uint64 frameCount, ma_bool32 loop) -{ - if (pAudioBuffer == NULL) { - return 0; - } - - return ma_audio_buffer_ref_read_pcm_frames(&pAudioBuffer->ref, pFramesOut, frameCount, loop); -} - -MA_API ma_result ma_audio_buffer_seek_to_pcm_frame(ma_audio_buffer* pAudioBuffer, ma_uint64 frameIndex) -{ - if (pAudioBuffer == NULL) { - return MA_INVALID_ARGS; - } - - return ma_audio_buffer_ref_seek_to_pcm_frame(&pAudioBuffer->ref, frameIndex); -} - -MA_API ma_result ma_audio_buffer_map(ma_audio_buffer* pAudioBuffer, void** ppFramesOut, ma_uint64* pFrameCount) -{ - if (ppFramesOut != NULL) { - *ppFramesOut = NULL; /* Safety. */ - } - - if (pAudioBuffer == NULL) { - if (pFrameCount != NULL) { - *pFrameCount = 0; - } - - return MA_INVALID_ARGS; - } - - return ma_audio_buffer_ref_map(&pAudioBuffer->ref, ppFramesOut, pFrameCount); -} - -MA_API ma_result ma_audio_buffer_unmap(ma_audio_buffer* pAudioBuffer, ma_uint64 frameCount) -{ - if (pAudioBuffer == NULL) { - return MA_INVALID_ARGS; - } - - return ma_audio_buffer_ref_unmap(&pAudioBuffer->ref, frameCount); -} - -MA_API ma_bool32 ma_audio_buffer_at_end(const ma_audio_buffer* pAudioBuffer) -{ - if (pAudioBuffer == NULL) { - return MA_FALSE; - } - - return ma_audio_buffer_ref_at_end(&pAudioBuffer->ref); -} - -MA_API ma_result ma_audio_buffer_get_cursor_in_pcm_frames(const ma_audio_buffer* pAudioBuffer, ma_uint64* pCursor) -{ - if (pAudioBuffer == NULL) { - return MA_INVALID_ARGS; - } - - return ma_audio_buffer_ref_get_cursor_in_pcm_frames(&pAudioBuffer->ref, pCursor); -} - -MA_API ma_result ma_audio_buffer_get_length_in_pcm_frames(const ma_audio_buffer* pAudioBuffer, ma_uint64* pLength) -{ - if (pAudioBuffer == NULL) { - return MA_INVALID_ARGS; - } - - return ma_audio_buffer_ref_get_length_in_pcm_frames(&pAudioBuffer->ref, pLength); -} - -MA_API ma_result ma_audio_buffer_get_available_frames(const ma_audio_buffer* pAudioBuffer, ma_uint64* pAvailableFrames) -{ - if (pAvailableFrames == NULL) { - return MA_INVALID_ARGS; - } - - *pAvailableFrames = 0; - - if (pAudioBuffer == NULL) { - return MA_INVALID_ARGS; - } - - return ma_audio_buffer_ref_get_available_frames(&pAudioBuffer->ref, pAvailableFrames); -} - - - - - -MA_API ma_result ma_paged_audio_buffer_data_init(ma_format format, ma_uint32 channels, ma_paged_audio_buffer_data* pData) -{ - if (pData == NULL) { - return MA_INVALID_ARGS; - } - - MA_ZERO_OBJECT(pData); - - pData->format = format; - pData->channels = channels; - pData->pTail = &pData->head; - - return MA_SUCCESS; -} - -MA_API void ma_paged_audio_buffer_data_uninit(ma_paged_audio_buffer_data* pData, const ma_allocation_callbacks* pAllocationCallbacks) -{ - ma_paged_audio_buffer_page* pPage; - - if (pData == NULL) { - return; - } - - /* All pages need to be freed. */ - pPage = (ma_paged_audio_buffer_page*)ma_atomic_load_ptr(&pData->head.pNext); - while (pPage != NULL) { - ma_paged_audio_buffer_page* pNext = (ma_paged_audio_buffer_page*)ma_atomic_load_ptr(&pPage->pNext); - - ma_free(pPage, pAllocationCallbacks); - pPage = pNext; - } -} - -MA_API ma_paged_audio_buffer_page* ma_paged_audio_buffer_data_get_head(ma_paged_audio_buffer_data* pData) -{ - if (pData == NULL) { - return NULL; - } - - return &pData->head; -} - -MA_API ma_paged_audio_buffer_page* ma_paged_audio_buffer_data_get_tail(ma_paged_audio_buffer_data* pData) -{ - if (pData == NULL) { - return NULL; - } - - return pData->pTail; -} - -MA_API ma_result ma_paged_audio_buffer_data_get_length_in_pcm_frames(ma_paged_audio_buffer_data* pData, ma_uint64* pLength) -{ - ma_paged_audio_buffer_page* pPage; - - if (pLength == NULL) { - return MA_INVALID_ARGS; - } - - *pLength = 0; - - if (pData == NULL) { - return MA_INVALID_ARGS; - } - - /* Calculate the length from the linked list. */ - for (pPage = (ma_paged_audio_buffer_page*)ma_atomic_load_ptr(&pData->head.pNext); pPage != NULL; pPage = (ma_paged_audio_buffer_page*)ma_atomic_load_ptr(&pPage->pNext)) { - *pLength += pPage->sizeInFrames; - } - - return MA_SUCCESS; -} - -MA_API ma_result ma_paged_audio_buffer_data_allocate_page(ma_paged_audio_buffer_data* pData, ma_uint64 pageSizeInFrames, const void* pInitialData, const ma_allocation_callbacks* pAllocationCallbacks, ma_paged_audio_buffer_page** ppPage) -{ - ma_paged_audio_buffer_page* pPage; - ma_uint64 allocationSize; - - if (ppPage == NULL) { - return MA_INVALID_ARGS; - } - - *ppPage = NULL; - - if (pData == NULL) { - return MA_INVALID_ARGS; - } - - allocationSize = sizeof(*pPage) + (pageSizeInFrames * ma_get_bytes_per_frame(pData->format, pData->channels)); - if (allocationSize > MA_SIZE_MAX) { - return MA_OUT_OF_MEMORY; /* Too big. */ - } - - pPage = (ma_paged_audio_buffer_page*)ma_malloc((size_t)allocationSize, pAllocationCallbacks); /* Safe cast to size_t. */ - if (pPage == NULL) { - return MA_OUT_OF_MEMORY; - } - - pPage->pNext = NULL; - pPage->sizeInFrames = pageSizeInFrames; - - if (pInitialData != NULL) { - ma_copy_pcm_frames(pPage->pAudioData, pInitialData, pageSizeInFrames, pData->format, pData->channels); - } - - *ppPage = pPage; - - return MA_SUCCESS; -} - -MA_API ma_result ma_paged_audio_buffer_data_free_page(ma_paged_audio_buffer_data* pData, ma_paged_audio_buffer_page* pPage, const ma_allocation_callbacks* pAllocationCallbacks) -{ - if (pData == NULL || pPage == NULL) { - return MA_INVALID_ARGS; - } - - /* It's assumed the page is not attached to the list. */ - ma_free(pPage, pAllocationCallbacks); - - return MA_SUCCESS; -} - -MA_API ma_result ma_paged_audio_buffer_data_append_page(ma_paged_audio_buffer_data* pData, ma_paged_audio_buffer_page* pPage) -{ - if (pData == NULL || pPage == NULL) { - return MA_INVALID_ARGS; - } - - /* This function assumes the page has been filled with audio data by this point. As soon as we append, the page will be available for reading. */ - - /* First thing to do is update the tail. */ - for (;;) { - ma_paged_audio_buffer_page* pOldTail = (ma_paged_audio_buffer_page*)ma_atomic_load_ptr(&pData->pTail); - ma_paged_audio_buffer_page* pNewTail = pPage; - - if (ma_atomic_compare_exchange_weak_ptr((volatile void**)&pData->pTail, (void**)&pOldTail, pNewTail)) { - /* Here is where we append the page to the list. After this, the page is attached to the list and ready to be read from. */ - ma_atomic_exchange_ptr(&pOldTail->pNext, pPage); - break; /* Done. */ - } - } - - return MA_SUCCESS; -} - -MA_API ma_result ma_paged_audio_buffer_data_allocate_and_append_page(ma_paged_audio_buffer_data* pData, ma_uint32 pageSizeInFrames, const void* pInitialData, const ma_allocation_callbacks* pAllocationCallbacks) -{ - ma_result result; - ma_paged_audio_buffer_page* pPage; - - result = ma_paged_audio_buffer_data_allocate_page(pData, pageSizeInFrames, pInitialData, pAllocationCallbacks, &pPage); - if (result != MA_SUCCESS) { - return result; - } - - return ma_paged_audio_buffer_data_append_page(pData, pPage); /* <-- Should never fail. */ -} - - -MA_API ma_paged_audio_buffer_config ma_paged_audio_buffer_config_init(ma_paged_audio_buffer_data* pData) -{ - ma_paged_audio_buffer_config config; - - MA_ZERO_OBJECT(&config); - config.pData = pData; - - return config; -} - - -static ma_result ma_paged_audio_buffer__data_source_on_read(ma_data_source* pDataSource, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead) -{ - return ma_paged_audio_buffer_read_pcm_frames((ma_paged_audio_buffer*)pDataSource, pFramesOut, frameCount, pFramesRead); -} - -static ma_result ma_paged_audio_buffer__data_source_on_seek(ma_data_source* pDataSource, ma_uint64 frameIndex) -{ - return ma_paged_audio_buffer_seek_to_pcm_frame((ma_paged_audio_buffer*)pDataSource, frameIndex); -} - -static ma_result ma_paged_audio_buffer__data_source_on_get_data_format(ma_data_source* pDataSource, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate, ma_channel* pChannelMap, size_t channelMapCap) -{ - ma_paged_audio_buffer* pPagedAudioBuffer = (ma_paged_audio_buffer*)pDataSource; - - *pFormat = pPagedAudioBuffer->pData->format; - *pChannels = pPagedAudioBuffer->pData->channels; - *pSampleRate = 0; /* There is no notion of a sample rate with audio buffers. */ - ma_channel_map_init_standard(ma_standard_channel_map_default, pChannelMap, channelMapCap, pPagedAudioBuffer->pData->channels); - - return MA_SUCCESS; -} - -static ma_result ma_paged_audio_buffer__data_source_on_get_cursor(ma_data_source* pDataSource, ma_uint64* pCursor) -{ - return ma_paged_audio_buffer_get_cursor_in_pcm_frames((ma_paged_audio_buffer*)pDataSource, pCursor); -} - -static ma_result ma_paged_audio_buffer__data_source_on_get_length(ma_data_source* pDataSource, ma_uint64* pLength) -{ - return ma_paged_audio_buffer_get_length_in_pcm_frames((ma_paged_audio_buffer*)pDataSource, pLength); -} - -static ma_data_source_vtable g_ma_paged_audio_buffer_data_source_vtable = -{ - ma_paged_audio_buffer__data_source_on_read, - ma_paged_audio_buffer__data_source_on_seek, - ma_paged_audio_buffer__data_source_on_get_data_format, - ma_paged_audio_buffer__data_source_on_get_cursor, - ma_paged_audio_buffer__data_source_on_get_length, - NULL, /* onSetLooping */ - 0 -}; - -MA_API ma_result ma_paged_audio_buffer_init(const ma_paged_audio_buffer_config* pConfig, ma_paged_audio_buffer* pPagedAudioBuffer) -{ - ma_result result; - ma_data_source_config dataSourceConfig; - - if (pPagedAudioBuffer == NULL) { - return MA_INVALID_ARGS; - } - - MA_ZERO_OBJECT(pPagedAudioBuffer); - - /* A config is required for the format and channel count. */ - if (pConfig == NULL) { - return MA_INVALID_ARGS; - } - - if (pConfig->pData == NULL) { - return MA_INVALID_ARGS; /* No underlying data specified. */ - } - - dataSourceConfig = ma_data_source_config_init(); - dataSourceConfig.vtable = &g_ma_paged_audio_buffer_data_source_vtable; - - result = ma_data_source_init(&dataSourceConfig, &pPagedAudioBuffer->ds); - if (result != MA_SUCCESS) { - return result; - } - - pPagedAudioBuffer->pData = pConfig->pData; - pPagedAudioBuffer->pCurrent = ma_paged_audio_buffer_data_get_head(pConfig->pData); - pPagedAudioBuffer->relativeCursor = 0; - pPagedAudioBuffer->absoluteCursor = 0; - - return MA_SUCCESS; -} - -MA_API void ma_paged_audio_buffer_uninit(ma_paged_audio_buffer* pPagedAudioBuffer) -{ - if (pPagedAudioBuffer == NULL) { - return; - } - - /* Nothing to do. The data needs to be deleted separately. */ -} - -MA_API ma_result ma_paged_audio_buffer_read_pcm_frames(ma_paged_audio_buffer* pPagedAudioBuffer, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead) -{ - ma_result result = MA_SUCCESS; - ma_uint64 totalFramesRead = 0; - ma_format format; - ma_uint32 channels; - - if (pPagedAudioBuffer == NULL) { - return MA_INVALID_ARGS; - } - - format = pPagedAudioBuffer->pData->format; - channels = pPagedAudioBuffer->pData->channels; - - while (totalFramesRead < frameCount) { - /* Read from the current page. The buffer should never be in a state where this is NULL. */ - ma_uint64 framesRemainingInCurrentPage; - ma_uint64 framesRemainingToRead = frameCount - totalFramesRead; - ma_uint64 framesToReadThisIteration; - - MA_ASSERT(pPagedAudioBuffer->pCurrent != NULL); - - framesRemainingInCurrentPage = pPagedAudioBuffer->pCurrent->sizeInFrames - pPagedAudioBuffer->relativeCursor; - - framesToReadThisIteration = ma_min(framesRemainingInCurrentPage, framesRemainingToRead); - ma_copy_pcm_frames(ma_offset_pcm_frames_ptr(pFramesOut, totalFramesRead, format, channels), ma_offset_pcm_frames_ptr(pPagedAudioBuffer->pCurrent->pAudioData, pPagedAudioBuffer->relativeCursor, format, channels), framesToReadThisIteration, format, channels); - totalFramesRead += framesToReadThisIteration; - - pPagedAudioBuffer->absoluteCursor += framesToReadThisIteration; - pPagedAudioBuffer->relativeCursor += framesToReadThisIteration; - - /* Move to the next page if necessary. If there's no more pages, we need to return MA_AT_END. */ - MA_ASSERT(pPagedAudioBuffer->relativeCursor <= pPagedAudioBuffer->pCurrent->sizeInFrames); - - if (pPagedAudioBuffer->relativeCursor == pPagedAudioBuffer->pCurrent->sizeInFrames) { - /* We reached the end of the page. Need to move to the next. If there's no more pages, we're done. */ - ma_paged_audio_buffer_page* pNext = (ma_paged_audio_buffer_page*)ma_atomic_load_ptr(&pPagedAudioBuffer->pCurrent->pNext); - if (pNext == NULL) { - result = MA_AT_END; - break; /* We've reached the end. */ - } else { - pPagedAudioBuffer->pCurrent = pNext; - pPagedAudioBuffer->relativeCursor = 0; - } - } - } - - if (pFramesRead != NULL) { - *pFramesRead = totalFramesRead; - } - - return result; -} - -MA_API ma_result ma_paged_audio_buffer_seek_to_pcm_frame(ma_paged_audio_buffer* pPagedAudioBuffer, ma_uint64 frameIndex) -{ - if (pPagedAudioBuffer == NULL) { - return MA_INVALID_ARGS; - } - - if (frameIndex == pPagedAudioBuffer->absoluteCursor) { - return MA_SUCCESS; /* Nothing to do. */ - } - - if (frameIndex < pPagedAudioBuffer->absoluteCursor) { - /* Moving backwards. Need to move the cursor back to the start, and then move forward. */ - pPagedAudioBuffer->pCurrent = ma_paged_audio_buffer_data_get_head(pPagedAudioBuffer->pData); - pPagedAudioBuffer->absoluteCursor = 0; - pPagedAudioBuffer->relativeCursor = 0; - - /* Fall through to the forward seeking section below. */ - } - - if (frameIndex > pPagedAudioBuffer->absoluteCursor) { - /* Moving forward. */ - ma_paged_audio_buffer_page* pPage; - ma_uint64 runningCursor = 0; - - for (pPage = (ma_paged_audio_buffer_page*)ma_atomic_load_ptr(&ma_paged_audio_buffer_data_get_head(pPagedAudioBuffer->pData)->pNext); pPage != NULL; pPage = (ma_paged_audio_buffer_page*)ma_atomic_load_ptr(&pPage->pNext)) { - ma_uint64 pageRangeBeg = runningCursor; - ma_uint64 pageRangeEnd = pageRangeBeg + pPage->sizeInFrames; - - if (frameIndex >= pageRangeBeg) { - if (frameIndex < pageRangeEnd || (frameIndex == pageRangeEnd && pPage == (ma_paged_audio_buffer_page*)ma_atomic_load_ptr(ma_paged_audio_buffer_data_get_tail(pPagedAudioBuffer->pData)))) { /* A small edge case - allow seeking to the very end of the buffer. */ - /* We found the page. */ - pPagedAudioBuffer->pCurrent = pPage; - pPagedAudioBuffer->absoluteCursor = frameIndex; - pPagedAudioBuffer->relativeCursor = frameIndex - pageRangeBeg; - return MA_SUCCESS; - } - } - - runningCursor = pageRangeEnd; - } - - /* Getting here means we tried seeking too far forward. Don't change any state. */ - return MA_BAD_SEEK; - } - - return MA_SUCCESS; -} - -MA_API ma_result ma_paged_audio_buffer_get_cursor_in_pcm_frames(ma_paged_audio_buffer* pPagedAudioBuffer, ma_uint64* pCursor) -{ - if (pCursor == NULL) { - return MA_INVALID_ARGS; - } - - *pCursor = 0; /* Safety. */ - - if (pPagedAudioBuffer == NULL) { - return MA_INVALID_ARGS; - } - - *pCursor = pPagedAudioBuffer->absoluteCursor; - - return MA_SUCCESS; -} - -MA_API ma_result ma_paged_audio_buffer_get_length_in_pcm_frames(ma_paged_audio_buffer* pPagedAudioBuffer, ma_uint64* pLength) -{ - return ma_paged_audio_buffer_data_get_length_in_pcm_frames(pPagedAudioBuffer->pData, pLength); -} - - - -/************************************************************************************************************************************************************** - -VFS - -**************************************************************************************************************************************************************/ -MA_API ma_result ma_vfs_open(ma_vfs* pVFS, const char* pFilePath, ma_uint32 openMode, ma_vfs_file* pFile) -{ - ma_vfs_callbacks* pCallbacks = (ma_vfs_callbacks*)pVFS; - - if (pFile == NULL) { - return MA_INVALID_ARGS; - } - - *pFile = NULL; - - if (pVFS == NULL || pFilePath == NULL || openMode == 0) { - return MA_INVALID_ARGS; - } - - if (pCallbacks->onOpen == NULL) { - return MA_NOT_IMPLEMENTED; - } - - return pCallbacks->onOpen(pVFS, pFilePath, openMode, pFile); -} - -MA_API ma_result ma_vfs_open_w(ma_vfs* pVFS, const wchar_t* pFilePath, ma_uint32 openMode, ma_vfs_file* pFile) -{ - ma_vfs_callbacks* pCallbacks = (ma_vfs_callbacks*)pVFS; - - if (pFile == NULL) { - return MA_INVALID_ARGS; - } - - *pFile = NULL; - - if (pVFS == NULL || pFilePath == NULL || openMode == 0) { - return MA_INVALID_ARGS; - } - - if (pCallbacks->onOpenW == NULL) { - return MA_NOT_IMPLEMENTED; - } - - return pCallbacks->onOpenW(pVFS, pFilePath, openMode, pFile); -} - -MA_API ma_result ma_vfs_close(ma_vfs* pVFS, ma_vfs_file file) -{ - ma_vfs_callbacks* pCallbacks = (ma_vfs_callbacks*)pVFS; - - if (pVFS == NULL || file == NULL) { - return MA_INVALID_ARGS; - } - - if (pCallbacks->onClose == NULL) { - return MA_NOT_IMPLEMENTED; - } - - return pCallbacks->onClose(pVFS, file); -} - -MA_API ma_result ma_vfs_read(ma_vfs* pVFS, ma_vfs_file file, void* pDst, size_t sizeInBytes, size_t* pBytesRead) -{ - ma_vfs_callbacks* pCallbacks = (ma_vfs_callbacks*)pVFS; - ma_result result; - size_t bytesRead = 0; - - if (pBytesRead != NULL) { - *pBytesRead = 0; - } - - if (pVFS == NULL || file == NULL || pDst == NULL) { - return MA_INVALID_ARGS; - } - - if (pCallbacks->onRead == NULL) { - return MA_NOT_IMPLEMENTED; - } - - result = pCallbacks->onRead(pVFS, file, pDst, sizeInBytes, &bytesRead); - - if (pBytesRead != NULL) { - *pBytesRead = bytesRead; - } - - if (result == MA_SUCCESS && bytesRead == 0 && sizeInBytes > 0) { - result = MA_AT_END; - } - - return result; -} - -MA_API ma_result ma_vfs_write(ma_vfs* pVFS, ma_vfs_file file, const void* pSrc, size_t sizeInBytes, size_t* pBytesWritten) -{ - ma_vfs_callbacks* pCallbacks = (ma_vfs_callbacks*)pVFS; - - if (pBytesWritten != NULL) { - *pBytesWritten = 0; - } - - if (pVFS == NULL || file == NULL || pSrc == NULL) { - return MA_INVALID_ARGS; - } - - if (pCallbacks->onWrite == NULL) { - return MA_NOT_IMPLEMENTED; - } - - return pCallbacks->onWrite(pVFS, file, pSrc, sizeInBytes, pBytesWritten); -} - -MA_API ma_result ma_vfs_seek(ma_vfs* pVFS, ma_vfs_file file, ma_int64 offset, ma_seek_origin origin) -{ - ma_vfs_callbacks* pCallbacks = (ma_vfs_callbacks*)pVFS; - - if (pVFS == NULL || file == NULL) { - return MA_INVALID_ARGS; - } - - if (pCallbacks->onSeek == NULL) { - return MA_NOT_IMPLEMENTED; - } - - return pCallbacks->onSeek(pVFS, file, offset, origin); -} - -MA_API ma_result ma_vfs_tell(ma_vfs* pVFS, ma_vfs_file file, ma_int64* pCursor) -{ - ma_vfs_callbacks* pCallbacks = (ma_vfs_callbacks*)pVFS; - - if (pCursor == NULL) { - return MA_INVALID_ARGS; - } - - *pCursor = 0; - - if (pVFS == NULL || file == NULL) { - return MA_INVALID_ARGS; - } - - if (pCallbacks->onTell == NULL) { - return MA_NOT_IMPLEMENTED; - } - - return pCallbacks->onTell(pVFS, file, pCursor); -} - -MA_API ma_result ma_vfs_info(ma_vfs* pVFS, ma_vfs_file file, ma_file_info* pInfo) -{ - ma_vfs_callbacks* pCallbacks = (ma_vfs_callbacks*)pVFS; - - if (pInfo == NULL) { - return MA_INVALID_ARGS; - } - - MA_ZERO_OBJECT(pInfo); - - if (pVFS == NULL || file == NULL) { - return MA_INVALID_ARGS; - } - - if (pCallbacks->onInfo == NULL) { - return MA_NOT_IMPLEMENTED; - } - - return pCallbacks->onInfo(pVFS, file, pInfo); -} - - -#if !defined(MA_USE_WIN32_FILEIO) && (defined(MA_WIN32) && defined(MA_WIN32_DESKTOP) && !defined(MA_NO_WIN32_FILEIO) && !defined(MA_POSIX)) - #define MA_USE_WIN32_FILEIO -#endif - -#if defined(MA_USE_WIN32_FILEIO) -/* -We need to dynamically load SetFilePointer or SetFilePointerEx because older versions of Windows do -not have the Ex version. We therefore need to do some dynamic branching depending on what's available. - -We load these when we load our first file from the default VFS. It's left open for the life of the -program and is left to the OS to uninitialize when the program terminates. -*/ -typedef DWORD (__stdcall * ma_SetFilePointer_proc)(HANDLE hFile, LONG lDistanceToMove, LONG* lpDistanceToMoveHigh, DWORD dwMoveMethod); -typedef BOOL (__stdcall * ma_SetFilePointerEx_proc)(HANDLE hFile, LARGE_INTEGER liDistanceToMove, LARGE_INTEGER* lpNewFilePointer, DWORD dwMoveMethod); - -static ma_handle hKernel32DLL = NULL; -static ma_SetFilePointer_proc ma_SetFilePointer = NULL; -static ma_SetFilePointerEx_proc ma_SetFilePointerEx = NULL; - -static void ma_win32_fileio_init(void) -{ - if (hKernel32DLL == NULL) { - hKernel32DLL = ma_dlopen(NULL, "kernel32.dll"); - if (hKernel32DLL != NULL) { - ma_SetFilePointer = (ma_SetFilePointer_proc) ma_dlsym(NULL, hKernel32DLL, "SetFilePointer"); - ma_SetFilePointerEx = (ma_SetFilePointerEx_proc)ma_dlsym(NULL, hKernel32DLL, "SetFilePointerEx"); - } - } -} - -static void ma_default_vfs__get_open_settings_win32(ma_uint32 openMode, DWORD* pDesiredAccess, DWORD* pShareMode, DWORD* pCreationDisposition) -{ - *pDesiredAccess = 0; - if ((openMode & MA_OPEN_MODE_READ) != 0) { - *pDesiredAccess |= GENERIC_READ; - } - if ((openMode & MA_OPEN_MODE_WRITE) != 0) { - *pDesiredAccess |= GENERIC_WRITE; - } - - *pShareMode = 0; - if ((openMode & MA_OPEN_MODE_READ) != 0) { - *pShareMode |= FILE_SHARE_READ; - } - - if ((openMode & MA_OPEN_MODE_WRITE) != 0) { - *pCreationDisposition = CREATE_ALWAYS; /* Opening in write mode. Truncate. */ - } else { - *pCreationDisposition = OPEN_EXISTING; /* Opening in read mode. File must exist. */ - } -} - -static ma_result ma_default_vfs_open__win32(ma_vfs* pVFS, const char* pFilePath, ma_uint32 openMode, ma_vfs_file* pFile) -{ - HANDLE hFile; - DWORD dwDesiredAccess; - DWORD dwShareMode; - DWORD dwCreationDisposition; - - (void)pVFS; - - /* Load some Win32 symbols dynamically so we can dynamically check for the existence of SetFilePointerEx. */ - ma_win32_fileio_init(); - - ma_default_vfs__get_open_settings_win32(openMode, &dwDesiredAccess, &dwShareMode, &dwCreationDisposition); - - hFile = CreateFileA(pFilePath, dwDesiredAccess, dwShareMode, NULL, dwCreationDisposition, FILE_ATTRIBUTE_NORMAL, NULL); - if (hFile == INVALID_HANDLE_VALUE) { - return ma_result_from_GetLastError(GetLastError()); - } - - *pFile = hFile; - return MA_SUCCESS; -} - -static ma_result ma_default_vfs_open_w__win32(ma_vfs* pVFS, const wchar_t* pFilePath, ma_uint32 openMode, ma_vfs_file* pFile) -{ - HANDLE hFile; - DWORD dwDesiredAccess; - DWORD dwShareMode; - DWORD dwCreationDisposition; - - (void)pVFS; - - /* Load some Win32 symbols dynamically so we can dynamically check for the existence of SetFilePointerEx. */ - ma_win32_fileio_init(); - - ma_default_vfs__get_open_settings_win32(openMode, &dwDesiredAccess, &dwShareMode, &dwCreationDisposition); - - hFile = CreateFileW(pFilePath, dwDesiredAccess, dwShareMode, NULL, dwCreationDisposition, FILE_ATTRIBUTE_NORMAL, NULL); - if (hFile == INVALID_HANDLE_VALUE) { - return ma_result_from_GetLastError(GetLastError()); - } - - *pFile = hFile; - return MA_SUCCESS; -} - -static ma_result ma_default_vfs_close__win32(ma_vfs* pVFS, ma_vfs_file file) -{ - (void)pVFS; - - if (CloseHandle((HANDLE)file) == 0) { - return ma_result_from_GetLastError(GetLastError()); - } - - return MA_SUCCESS; -} - - -static ma_result ma_default_vfs_read__win32(ma_vfs* pVFS, ma_vfs_file file, void* pDst, size_t sizeInBytes, size_t* pBytesRead) -{ - ma_result result = MA_SUCCESS; - size_t totalBytesRead; - - (void)pVFS; - - totalBytesRead = 0; - while (totalBytesRead < sizeInBytes) { - size_t bytesRemaining; - DWORD bytesToRead; - DWORD bytesRead; - BOOL readResult; - - bytesRemaining = sizeInBytes - totalBytesRead; - if (bytesRemaining >= 0xFFFFFFFF) { - bytesToRead = 0xFFFFFFFF; - } else { - bytesToRead = (DWORD)bytesRemaining; - } - - readResult = ReadFile((HANDLE)file, ma_offset_ptr(pDst, totalBytesRead), bytesToRead, &bytesRead, NULL); - if (readResult == 1 && bytesRead == 0) { - result = MA_AT_END; - break; /* EOF */ - } - - totalBytesRead += bytesRead; - - if (bytesRead < bytesToRead) { - break; /* EOF */ - } - - if (readResult == 0) { - result = ma_result_from_GetLastError(GetLastError()); - break; - } - } - - if (pBytesRead != NULL) { - *pBytesRead = totalBytesRead; - } - - return result; -} - -static ma_result ma_default_vfs_write__win32(ma_vfs* pVFS, ma_vfs_file file, const void* pSrc, size_t sizeInBytes, size_t* pBytesWritten) -{ - ma_result result = MA_SUCCESS; - size_t totalBytesWritten; - - (void)pVFS; - - totalBytesWritten = 0; - while (totalBytesWritten < sizeInBytes) { - size_t bytesRemaining; - DWORD bytesToWrite; - DWORD bytesWritten; - BOOL writeResult; - - bytesRemaining = sizeInBytes - totalBytesWritten; - if (bytesRemaining >= 0xFFFFFFFF) { - bytesToWrite = 0xFFFFFFFF; - } else { - bytesToWrite = (DWORD)bytesRemaining; - } - - writeResult = WriteFile((HANDLE)file, ma_offset_ptr(pSrc, totalBytesWritten), bytesToWrite, &bytesWritten, NULL); - totalBytesWritten += bytesWritten; - - if (writeResult == 0) { - result = ma_result_from_GetLastError(GetLastError()); - break; - } - } - - if (pBytesWritten != NULL) { - *pBytesWritten = totalBytesWritten; - } - - return result; -} - - -static ma_result ma_default_vfs_seek__win32(ma_vfs* pVFS, ma_vfs_file file, ma_int64 offset, ma_seek_origin origin) -{ - LARGE_INTEGER liDistanceToMove; - DWORD dwMoveMethod; - BOOL result; - - (void)pVFS; - - liDistanceToMove.QuadPart = offset; - - /* */ if (origin == ma_seek_origin_current) { - dwMoveMethod = FILE_CURRENT; - } else if (origin == ma_seek_origin_end) { - dwMoveMethod = FILE_END; - } else { - dwMoveMethod = FILE_BEGIN; - } - - if (ma_SetFilePointerEx != NULL) { - result = ma_SetFilePointerEx((HANDLE)file, liDistanceToMove, NULL, dwMoveMethod); - } else if (ma_SetFilePointer != NULL) { - /* No SetFilePointerEx() so restrict to 31 bits. */ - if (offset > 0x7FFFFFFF) { - return MA_OUT_OF_RANGE; - } - - result = ma_SetFilePointer((HANDLE)file, (LONG)liDistanceToMove.QuadPart, NULL, dwMoveMethod); - } else { - return MA_NOT_IMPLEMENTED; - } - - if (result == 0) { - return ma_result_from_GetLastError(GetLastError()); - } - - return MA_SUCCESS; -} - -static ma_result ma_default_vfs_tell__win32(ma_vfs* pVFS, ma_vfs_file file, ma_int64* pCursor) -{ - LARGE_INTEGER liZero; - LARGE_INTEGER liTell; - BOOL result; - - (void)pVFS; - - liZero.QuadPart = 0; - - if (ma_SetFilePointerEx != NULL) { - result = ma_SetFilePointerEx((HANDLE)file, liZero, &liTell, FILE_CURRENT); - } else if (ma_SetFilePointer != NULL) { - LONG tell; - - result = ma_SetFilePointer((HANDLE)file, (LONG)liZero.QuadPart, &tell, FILE_CURRENT); - liTell.QuadPart = tell; - } else { - return MA_NOT_IMPLEMENTED; - } - - if (result == 0) { - return ma_result_from_GetLastError(GetLastError()); - } - - if (pCursor != NULL) { - *pCursor = liTell.QuadPart; - } - - return MA_SUCCESS; -} - -static ma_result ma_default_vfs_info__win32(ma_vfs* pVFS, ma_vfs_file file, ma_file_info* pInfo) -{ - BY_HANDLE_FILE_INFORMATION fi; - BOOL result; - - (void)pVFS; - - result = GetFileInformationByHandle((HANDLE)file, &fi); - if (result == 0) { - return ma_result_from_GetLastError(GetLastError()); - } - - pInfo->sizeInBytes = ((ma_uint64)fi.nFileSizeHigh << 32) | ((ma_uint64)fi.nFileSizeLow); - - return MA_SUCCESS; -} -#else -static ma_result ma_default_vfs_open__stdio(ma_vfs* pVFS, const char* pFilePath, ma_uint32 openMode, ma_vfs_file* pFile) -{ - ma_result result; - FILE* pFileStd; - const char* pOpenModeStr; - - MA_ASSERT(pFilePath != NULL); - MA_ASSERT(openMode != 0); - MA_ASSERT(pFile != NULL); - - (void)pVFS; - - if ((openMode & MA_OPEN_MODE_READ) != 0) { - if ((openMode & MA_OPEN_MODE_WRITE) != 0) { - pOpenModeStr = "r+"; - } else { - pOpenModeStr = "rb"; - } - } else { - pOpenModeStr = "wb"; - } - - result = ma_fopen(&pFileStd, pFilePath, pOpenModeStr); - if (result != MA_SUCCESS) { - return result; - } - - *pFile = pFileStd; - - return MA_SUCCESS; -} - -static ma_result ma_default_vfs_open_w__stdio(ma_vfs* pVFS, const wchar_t* pFilePath, ma_uint32 openMode, ma_vfs_file* pFile) -{ - ma_result result; - FILE* pFileStd; - const wchar_t* pOpenModeStr; - - MA_ASSERT(pFilePath != NULL); - MA_ASSERT(openMode != 0); - MA_ASSERT(pFile != NULL); - - (void)pVFS; - - if ((openMode & MA_OPEN_MODE_READ) != 0) { - if ((openMode & MA_OPEN_MODE_WRITE) != 0) { - pOpenModeStr = L"r+"; - } else { - pOpenModeStr = L"rb"; - } - } else { - pOpenModeStr = L"wb"; - } - - result = ma_wfopen(&pFileStd, pFilePath, pOpenModeStr, (pVFS != NULL) ? &((ma_default_vfs*)pVFS)->allocationCallbacks : NULL); - if (result != MA_SUCCESS) { - return result; - } - - *pFile = pFileStd; - - return MA_SUCCESS; -} - -static ma_result ma_default_vfs_close__stdio(ma_vfs* pVFS, ma_vfs_file file) -{ - MA_ASSERT(file != NULL); - - (void)pVFS; - - fclose((FILE*)file); - - return MA_SUCCESS; -} - -static ma_result ma_default_vfs_read__stdio(ma_vfs* pVFS, ma_vfs_file file, void* pDst, size_t sizeInBytes, size_t* pBytesRead) -{ - size_t result; - - MA_ASSERT(file != NULL); - MA_ASSERT(pDst != NULL); - - (void)pVFS; - - result = fread(pDst, 1, sizeInBytes, (FILE*)file); - - if (pBytesRead != NULL) { - *pBytesRead = result; - } - - if (result != sizeInBytes) { - if (result == 0 && feof((FILE*)file)) { - return MA_AT_END; - } else { - return ma_result_from_errno(ferror((FILE*)file)); - } - } - - return MA_SUCCESS; -} - -static ma_result ma_default_vfs_write__stdio(ma_vfs* pVFS, ma_vfs_file file, const void* pSrc, size_t sizeInBytes, size_t* pBytesWritten) -{ - size_t result; - - MA_ASSERT(file != NULL); - MA_ASSERT(pSrc != NULL); - - (void)pVFS; - - result = fwrite(pSrc, 1, sizeInBytes, (FILE*)file); - - if (pBytesWritten != NULL) { - *pBytesWritten = result; - } - - if (result != sizeInBytes) { - return ma_result_from_errno(ferror((FILE*)file)); - } - - return MA_SUCCESS; -} - -static ma_result ma_default_vfs_seek__stdio(ma_vfs* pVFS, ma_vfs_file file, ma_int64 offset, ma_seek_origin origin) -{ - int result; - int whence; - - MA_ASSERT(file != NULL); - - (void)pVFS; - - if (origin == ma_seek_origin_start) { - whence = SEEK_SET; - } else if (origin == ma_seek_origin_end) { - whence = SEEK_END; - } else { - whence = SEEK_CUR; - } - -#if defined(_WIN32) - #if defined(_MSC_VER) && _MSC_VER > 1200 - result = _fseeki64((FILE*)file, offset, whence); - #else - /* No _fseeki64() so restrict to 31 bits. */ - if (offset > 0x7FFFFFFF) { - return MA_OUT_OF_RANGE; - } - - result = fseek((FILE*)file, (int)offset, whence); - #endif -#else - result = fseek((FILE*)file, (long int)offset, whence); -#endif - if (result != 0) { - return MA_ERROR; - } - - return MA_SUCCESS; -} - -static ma_result ma_default_vfs_tell__stdio(ma_vfs* pVFS, ma_vfs_file file, ma_int64* pCursor) -{ - ma_int64 result; - - MA_ASSERT(file != NULL); - MA_ASSERT(pCursor != NULL); - - (void)pVFS; - -#if defined(_WIN32) - #if defined(_MSC_VER) && _MSC_VER > 1200 - result = _ftelli64((FILE*)file); - #else - result = ftell((FILE*)file); - #endif -#else - result = ftell((FILE*)file); -#endif - - *pCursor = result; - - return MA_SUCCESS; -} - -#if !defined(_MSC_VER) && !((defined(_POSIX_C_SOURCE) && _POSIX_C_SOURCE >= 1) || defined(_XOPEN_SOURCE) || defined(_POSIX_SOURCE)) && !defined(MA_BSD) -int fileno(FILE *stream); -#endif - -static ma_result ma_default_vfs_info__stdio(ma_vfs* pVFS, ma_vfs_file file, ma_file_info* pInfo) -{ - int fd; - struct stat info; - - MA_ASSERT(file != NULL); - MA_ASSERT(pInfo != NULL); - - (void)pVFS; - -#if defined(_MSC_VER) - fd = _fileno((FILE*)file); -#else - fd = fileno((FILE*)file); -#endif - - if (fstat(fd, &info) != 0) { - return ma_result_from_errno(errno); - } - - pInfo->sizeInBytes = info.st_size; - - return MA_SUCCESS; -} -#endif - - -static ma_result ma_default_vfs_open(ma_vfs* pVFS, const char* pFilePath, ma_uint32 openMode, ma_vfs_file* pFile) -{ - if (pFile == NULL) { - return MA_INVALID_ARGS; - } - - *pFile = NULL; - - if (pFilePath == NULL || openMode == 0) { - return MA_INVALID_ARGS; - } - -#if defined(MA_USE_WIN32_FILEIO) - return ma_default_vfs_open__win32(pVFS, pFilePath, openMode, pFile); -#else - return ma_default_vfs_open__stdio(pVFS, pFilePath, openMode, pFile); -#endif -} - -static ma_result ma_default_vfs_open_w(ma_vfs* pVFS, const wchar_t* pFilePath, ma_uint32 openMode, ma_vfs_file* pFile) -{ - if (pFile == NULL) { - return MA_INVALID_ARGS; - } - - *pFile = NULL; - - if (pFilePath == NULL || openMode == 0) { - return MA_INVALID_ARGS; - } - -#if defined(MA_USE_WIN32_FILEIO) - return ma_default_vfs_open_w__win32(pVFS, pFilePath, openMode, pFile); -#else - return ma_default_vfs_open_w__stdio(pVFS, pFilePath, openMode, pFile); -#endif -} - -static ma_result ma_default_vfs_close(ma_vfs* pVFS, ma_vfs_file file) -{ - if (file == NULL) { - return MA_INVALID_ARGS; - } - -#if defined(MA_USE_WIN32_FILEIO) - return ma_default_vfs_close__win32(pVFS, file); -#else - return ma_default_vfs_close__stdio(pVFS, file); -#endif -} - -static ma_result ma_default_vfs_read(ma_vfs* pVFS, ma_vfs_file file, void* pDst, size_t sizeInBytes, size_t* pBytesRead) -{ - if (pBytesRead != NULL) { - *pBytesRead = 0; - } - - if (file == NULL || pDst == NULL) { - return MA_INVALID_ARGS; - } - -#if defined(MA_USE_WIN32_FILEIO) - return ma_default_vfs_read__win32(pVFS, file, pDst, sizeInBytes, pBytesRead); -#else - return ma_default_vfs_read__stdio(pVFS, file, pDst, sizeInBytes, pBytesRead); -#endif -} - -static ma_result ma_default_vfs_write(ma_vfs* pVFS, ma_vfs_file file, const void* pSrc, size_t sizeInBytes, size_t* pBytesWritten) -{ - if (pBytesWritten != NULL) { - *pBytesWritten = 0; - } - - if (file == NULL || pSrc == NULL) { - return MA_INVALID_ARGS; - } - -#if defined(MA_USE_WIN32_FILEIO) - return ma_default_vfs_write__win32(pVFS, file, pSrc, sizeInBytes, pBytesWritten); -#else - return ma_default_vfs_write__stdio(pVFS, file, pSrc, sizeInBytes, pBytesWritten); -#endif -} - -static ma_result ma_default_vfs_seek(ma_vfs* pVFS, ma_vfs_file file, ma_int64 offset, ma_seek_origin origin) -{ - if (file == NULL) { - return MA_INVALID_ARGS; - } - -#if defined(MA_USE_WIN32_FILEIO) - return ma_default_vfs_seek__win32(pVFS, file, offset, origin); -#else - return ma_default_vfs_seek__stdio(pVFS, file, offset, origin); -#endif -} - -static ma_result ma_default_vfs_tell(ma_vfs* pVFS, ma_vfs_file file, ma_int64* pCursor) -{ - if (pCursor == NULL) { - return MA_INVALID_ARGS; - } - - *pCursor = 0; - - if (file == NULL) { - return MA_INVALID_ARGS; - } - -#if defined(MA_USE_WIN32_FILEIO) - return ma_default_vfs_tell__win32(pVFS, file, pCursor); -#else - return ma_default_vfs_tell__stdio(pVFS, file, pCursor); -#endif -} - -static ma_result ma_default_vfs_info(ma_vfs* pVFS, ma_vfs_file file, ma_file_info* pInfo) -{ - if (pInfo == NULL) { - return MA_INVALID_ARGS; - } - - MA_ZERO_OBJECT(pInfo); - - if (file == NULL) { - return MA_INVALID_ARGS; - } - -#if defined(MA_USE_WIN32_FILEIO) - return ma_default_vfs_info__win32(pVFS, file, pInfo); -#else - return ma_default_vfs_info__stdio(pVFS, file, pInfo); -#endif -} - - -MA_API ma_result ma_default_vfs_init(ma_default_vfs* pVFS, const ma_allocation_callbacks* pAllocationCallbacks) -{ - if (pVFS == NULL) { - return MA_INVALID_ARGS; - } - - pVFS->cb.onOpen = ma_default_vfs_open; - pVFS->cb.onOpenW = ma_default_vfs_open_w; - pVFS->cb.onClose = ma_default_vfs_close; - pVFS->cb.onRead = ma_default_vfs_read; - pVFS->cb.onWrite = ma_default_vfs_write; - pVFS->cb.onSeek = ma_default_vfs_seek; - pVFS->cb.onTell = ma_default_vfs_tell; - pVFS->cb.onInfo = ma_default_vfs_info; - ma_allocation_callbacks_init_copy(&pVFS->allocationCallbacks, pAllocationCallbacks); - - return MA_SUCCESS; -} - - -MA_API ma_result ma_vfs_or_default_open(ma_vfs* pVFS, const char* pFilePath, ma_uint32 openMode, ma_vfs_file* pFile) -{ - if (pVFS != NULL) { - return ma_vfs_open(pVFS, pFilePath, openMode, pFile); - } else { - return ma_default_vfs_open(pVFS, pFilePath, openMode, pFile); - } -} - -MA_API ma_result ma_vfs_or_default_open_w(ma_vfs* pVFS, const wchar_t* pFilePath, ma_uint32 openMode, ma_vfs_file* pFile) -{ - if (pVFS != NULL) { - return ma_vfs_open_w(pVFS, pFilePath, openMode, pFile); - } else { - return ma_default_vfs_open_w(pVFS, pFilePath, openMode, pFile); - } -} - -MA_API ma_result ma_vfs_or_default_close(ma_vfs* pVFS, ma_vfs_file file) -{ - if (pVFS != NULL) { - return ma_vfs_close(pVFS, file); - } else { - return ma_default_vfs_close(pVFS, file); - } -} - -MA_API ma_result ma_vfs_or_default_read(ma_vfs* pVFS, ma_vfs_file file, void* pDst, size_t sizeInBytes, size_t* pBytesRead) -{ - if (pVFS != NULL) { - return ma_vfs_read(pVFS, file, pDst, sizeInBytes, pBytesRead); - } else { - return ma_default_vfs_read(pVFS, file, pDst, sizeInBytes, pBytesRead); - } -} - -MA_API ma_result ma_vfs_or_default_write(ma_vfs* pVFS, ma_vfs_file file, const void* pSrc, size_t sizeInBytes, size_t* pBytesWritten) -{ - if (pVFS != NULL) { - return ma_vfs_write(pVFS, file, pSrc, sizeInBytes, pBytesWritten); - } else { - return ma_default_vfs_write(pVFS, file, pSrc, sizeInBytes, pBytesWritten); - } -} - -MA_API ma_result ma_vfs_or_default_seek(ma_vfs* pVFS, ma_vfs_file file, ma_int64 offset, ma_seek_origin origin) -{ - if (pVFS != NULL) { - return ma_vfs_seek(pVFS, file, offset, origin); - } else { - return ma_default_vfs_seek(pVFS, file, offset, origin); - } -} - -MA_API ma_result ma_vfs_or_default_tell(ma_vfs* pVFS, ma_vfs_file file, ma_int64* pCursor) -{ - if (pVFS != NULL) { - return ma_vfs_tell(pVFS, file, pCursor); - } else { - return ma_default_vfs_tell(pVFS, file, pCursor); - } -} - -MA_API ma_result ma_vfs_or_default_info(ma_vfs* pVFS, ma_vfs_file file, ma_file_info* pInfo) -{ - if (pVFS != NULL) { - return ma_vfs_info(pVFS, file, pInfo); - } else { - return ma_default_vfs_info(pVFS, file, pInfo); - } -} - - - -static ma_result ma_vfs_open_and_read_file_ex(ma_vfs* pVFS, const char* pFilePath, const wchar_t* pFilePathW, void** ppData, size_t* pSize, const ma_allocation_callbacks* pAllocationCallbacks) -{ - ma_result result; - ma_vfs_file file; - ma_file_info info; - void* pData; - size_t bytesRead; - - if (ppData != NULL) { - *ppData = NULL; - } - if (pSize != NULL) { - *pSize = 0; - } - - if (ppData == NULL) { - return MA_INVALID_ARGS; - } - - if (pFilePath != NULL) { - result = ma_vfs_or_default_open(pVFS, pFilePath, MA_OPEN_MODE_READ, &file); - } else { - result = ma_vfs_or_default_open_w(pVFS, pFilePathW, MA_OPEN_MODE_READ, &file); - } - if (result != MA_SUCCESS) { - return result; - } - - result = ma_vfs_or_default_info(pVFS, file, &info); - if (result != MA_SUCCESS) { - ma_vfs_or_default_close(pVFS, file); - return result; - } - - if (info.sizeInBytes > MA_SIZE_MAX) { - ma_vfs_or_default_close(pVFS, file); - return MA_TOO_BIG; - } - - pData = ma_malloc((size_t)info.sizeInBytes, pAllocationCallbacks); /* Safe cast. */ - if (pData == NULL) { - ma_vfs_or_default_close(pVFS, file); - return result; - } - - result = ma_vfs_or_default_read(pVFS, file, pData, (size_t)info.sizeInBytes, &bytesRead); /* Safe cast. */ - ma_vfs_or_default_close(pVFS, file); - - if (result != MA_SUCCESS) { - ma_free(pData, pAllocationCallbacks); - return result; - } - - if (pSize != NULL) { - *pSize = bytesRead; - } - - MA_ASSERT(ppData != NULL); - *ppData = pData; - - return MA_SUCCESS; -} - -MA_API ma_result ma_vfs_open_and_read_file(ma_vfs* pVFS, const char* pFilePath, void** ppData, size_t* pSize, const ma_allocation_callbacks* pAllocationCallbacks) -{ - return ma_vfs_open_and_read_file_ex(pVFS, pFilePath, NULL, ppData, pSize, pAllocationCallbacks); -} - -MA_API ma_result ma_vfs_open_and_read_file_w(ma_vfs* pVFS, const wchar_t* pFilePath, void** ppData, size_t* pSize, const ma_allocation_callbacks* pAllocationCallbacks) -{ - return ma_vfs_open_and_read_file_ex(pVFS, NULL, pFilePath, ppData, pSize, pAllocationCallbacks); -} - - - -/************************************************************************************************************************************************************** - -Decoding and Encoding Headers. These are auto-generated from a tool. - -**************************************************************************************************************************************************************/ -#if !defined(MA_NO_WAV) && (!defined(MA_NO_DECODING) || !defined(MA_NO_ENCODING)) -/* dr_wav_h begin */ -#ifndef ma_dr_wav_h -#define ma_dr_wav_h -#ifdef __cplusplus -extern "C" { -#endif -#define MA_DR_WAV_STRINGIFY(x) #x -#define MA_DR_WAV_XSTRINGIFY(x) MA_DR_WAV_STRINGIFY(x) -#define MA_DR_WAV_VERSION_MAJOR 0 -#define MA_DR_WAV_VERSION_MINOR 13 -#define MA_DR_WAV_VERSION_REVISION 18 -#define MA_DR_WAV_VERSION_STRING MA_DR_WAV_XSTRINGIFY(MA_DR_WAV_VERSION_MAJOR) "." MA_DR_WAV_XSTRINGIFY(MA_DR_WAV_VERSION_MINOR) "." MA_DR_WAV_XSTRINGIFY(MA_DR_WAV_VERSION_REVISION) -#include -#define MA_DR_WAVE_FORMAT_PCM 0x1 -#define MA_DR_WAVE_FORMAT_ADPCM 0x2 -#define MA_DR_WAVE_FORMAT_IEEE_FLOAT 0x3 -#define MA_DR_WAVE_FORMAT_ALAW 0x6 -#define MA_DR_WAVE_FORMAT_MULAW 0x7 -#define MA_DR_WAVE_FORMAT_DVI_ADPCM 0x11 -#define MA_DR_WAVE_FORMAT_EXTENSIBLE 0xFFFE -#define MA_DR_WAV_SEQUENTIAL 0x00000001 -#define MA_DR_WAV_WITH_METADATA 0x00000002 -MA_API void ma_dr_wav_version(ma_uint32* pMajor, ma_uint32* pMinor, ma_uint32* pRevision); -MA_API const char* ma_dr_wav_version_string(void); -typedef enum -{ - ma_dr_wav_seek_origin_start, - ma_dr_wav_seek_origin_current -} ma_dr_wav_seek_origin; -typedef enum -{ - ma_dr_wav_container_riff, - ma_dr_wav_container_rifx, - ma_dr_wav_container_w64, - ma_dr_wav_container_rf64, - ma_dr_wav_container_aiff -} ma_dr_wav_container; -typedef struct -{ - union - { - ma_uint8 fourcc[4]; - ma_uint8 guid[16]; - } id; - ma_uint64 sizeInBytes; - unsigned int paddingSize; -} ma_dr_wav_chunk_header; -typedef struct -{ - ma_uint16 formatTag; - ma_uint16 channels; - ma_uint32 sampleRate; - ma_uint32 avgBytesPerSec; - ma_uint16 blockAlign; - ma_uint16 bitsPerSample; - ma_uint16 extendedSize; - ma_uint16 validBitsPerSample; - ma_uint32 channelMask; - ma_uint8 subFormat[16]; -} ma_dr_wav_fmt; -MA_API ma_uint16 ma_dr_wav_fmt_get_format(const ma_dr_wav_fmt* pFMT); -typedef size_t (* ma_dr_wav_read_proc)(void* pUserData, void* pBufferOut, size_t bytesToRead); -typedef size_t (* ma_dr_wav_write_proc)(void* pUserData, const void* pData, size_t bytesToWrite); -typedef ma_bool32 (* ma_dr_wav_seek_proc)(void* pUserData, int offset, ma_dr_wav_seek_origin origin); -typedef ma_uint64 (* ma_dr_wav_chunk_proc)(void* pChunkUserData, ma_dr_wav_read_proc onRead, ma_dr_wav_seek_proc onSeek, void* pReadSeekUserData, const ma_dr_wav_chunk_header* pChunkHeader, ma_dr_wav_container container, const ma_dr_wav_fmt* pFMT); -typedef struct -{ - const ma_uint8* data; - size_t dataSize; - size_t currentReadPos; -} ma_dr_wav__memory_stream; -typedef struct -{ - void** ppData; - size_t* pDataSize; - size_t dataSize; - size_t dataCapacity; - size_t currentWritePos; -} ma_dr_wav__memory_stream_write; -typedef struct -{ - ma_dr_wav_container container; - ma_uint32 format; - ma_uint32 channels; - ma_uint32 sampleRate; - ma_uint32 bitsPerSample; -} ma_dr_wav_data_format; -typedef enum -{ - ma_dr_wav_metadata_type_none = 0, - ma_dr_wav_metadata_type_unknown = 1 << 0, - ma_dr_wav_metadata_type_smpl = 1 << 1, - ma_dr_wav_metadata_type_inst = 1 << 2, - ma_dr_wav_metadata_type_cue = 1 << 3, - ma_dr_wav_metadata_type_acid = 1 << 4, - ma_dr_wav_metadata_type_bext = 1 << 5, - ma_dr_wav_metadata_type_list_label = 1 << 6, - ma_dr_wav_metadata_type_list_note = 1 << 7, - ma_dr_wav_metadata_type_list_labelled_cue_region = 1 << 8, - ma_dr_wav_metadata_type_list_info_software = 1 << 9, - ma_dr_wav_metadata_type_list_info_copyright = 1 << 10, - ma_dr_wav_metadata_type_list_info_title = 1 << 11, - ma_dr_wav_metadata_type_list_info_artist = 1 << 12, - ma_dr_wav_metadata_type_list_info_comment = 1 << 13, - ma_dr_wav_metadata_type_list_info_date = 1 << 14, - ma_dr_wav_metadata_type_list_info_genre = 1 << 15, - ma_dr_wav_metadata_type_list_info_album = 1 << 16, - ma_dr_wav_metadata_type_list_info_tracknumber = 1 << 17, - ma_dr_wav_metadata_type_list_all_info_strings = ma_dr_wav_metadata_type_list_info_software - | ma_dr_wav_metadata_type_list_info_copyright - | ma_dr_wav_metadata_type_list_info_title - | ma_dr_wav_metadata_type_list_info_artist - | ma_dr_wav_metadata_type_list_info_comment - | ma_dr_wav_metadata_type_list_info_date - | ma_dr_wav_metadata_type_list_info_genre - | ma_dr_wav_metadata_type_list_info_album - | ma_dr_wav_metadata_type_list_info_tracknumber, - ma_dr_wav_metadata_type_list_all_adtl = ma_dr_wav_metadata_type_list_label - | ma_dr_wav_metadata_type_list_note - | ma_dr_wav_metadata_type_list_labelled_cue_region, - ma_dr_wav_metadata_type_all = -2, - ma_dr_wav_metadata_type_all_including_unknown = -1 -} ma_dr_wav_metadata_type; -typedef enum -{ - ma_dr_wav_smpl_loop_type_forward = 0, - ma_dr_wav_smpl_loop_type_pingpong = 1, - ma_dr_wav_smpl_loop_type_backward = 2 -} ma_dr_wav_smpl_loop_type; -typedef struct -{ - ma_uint32 cuePointId; - ma_uint32 type; - ma_uint32 firstSampleByteOffset; - ma_uint32 lastSampleByteOffset; - ma_uint32 sampleFraction; - ma_uint32 playCount; -} ma_dr_wav_smpl_loop; -typedef struct -{ - ma_uint32 manufacturerId; - ma_uint32 productId; - ma_uint32 samplePeriodNanoseconds; - ma_uint32 midiUnityNote; - ma_uint32 midiPitchFraction; - ma_uint32 smpteFormat; - ma_uint32 smpteOffset; - ma_uint32 sampleLoopCount; - ma_uint32 samplerSpecificDataSizeInBytes; - ma_dr_wav_smpl_loop* pLoops; - ma_uint8* pSamplerSpecificData; -} ma_dr_wav_smpl; -typedef struct -{ - ma_int8 midiUnityNote; - ma_int8 fineTuneCents; - ma_int8 gainDecibels; - ma_int8 lowNote; - ma_int8 highNote; - ma_int8 lowVelocity; - ma_int8 highVelocity; -} ma_dr_wav_inst; -typedef struct -{ - ma_uint32 id; - ma_uint32 playOrderPosition; - ma_uint8 dataChunkId[4]; - ma_uint32 chunkStart; - ma_uint32 blockStart; - ma_uint32 sampleByteOffset; -} ma_dr_wav_cue_point; -typedef struct -{ - ma_uint32 cuePointCount; - ma_dr_wav_cue_point *pCuePoints; -} ma_dr_wav_cue; -typedef enum -{ - ma_dr_wav_acid_flag_one_shot = 1, - ma_dr_wav_acid_flag_root_note_set = 2, - ma_dr_wav_acid_flag_stretch = 4, - ma_dr_wav_acid_flag_disk_based = 8, - ma_dr_wav_acid_flag_acidizer = 16 -} ma_dr_wav_acid_flag; -typedef struct -{ - ma_uint32 flags; - ma_uint16 midiUnityNote; - ma_uint16 reserved1; - float reserved2; - ma_uint32 numBeats; - ma_uint16 meterDenominator; - ma_uint16 meterNumerator; - float tempo; -} ma_dr_wav_acid; -typedef struct -{ - ma_uint32 cuePointId; - ma_uint32 stringLength; - char* pString; -} ma_dr_wav_list_label_or_note; -typedef struct -{ - char* pDescription; - char* pOriginatorName; - char* pOriginatorReference; - char pOriginationDate[10]; - char pOriginationTime[8]; - ma_uint64 timeReference; - ma_uint16 version; - char* pCodingHistory; - ma_uint32 codingHistorySize; - ma_uint8* pUMID; - ma_uint16 loudnessValue; - ma_uint16 loudnessRange; - ma_uint16 maxTruePeakLevel; - ma_uint16 maxMomentaryLoudness; - ma_uint16 maxShortTermLoudness; -} ma_dr_wav_bext; -typedef struct -{ - ma_uint32 stringLength; - char* pString; -} ma_dr_wav_list_info_text; -typedef struct -{ - ma_uint32 cuePointId; - ma_uint32 sampleLength; - ma_uint8 purposeId[4]; - ma_uint16 country; - ma_uint16 language; - ma_uint16 dialect; - ma_uint16 codePage; - ma_uint32 stringLength; - char* pString; -} ma_dr_wav_list_labelled_cue_region; -typedef enum -{ - ma_dr_wav_metadata_location_invalid, - ma_dr_wav_metadata_location_top_level, - ma_dr_wav_metadata_location_inside_info_list, - ma_dr_wav_metadata_location_inside_adtl_list -} ma_dr_wav_metadata_location; -typedef struct -{ - ma_uint8 id[4]; - ma_dr_wav_metadata_location chunkLocation; - ma_uint32 dataSizeInBytes; - ma_uint8* pData; -} ma_dr_wav_unknown_metadata; -typedef struct -{ - ma_dr_wav_metadata_type type; - union - { - ma_dr_wav_cue cue; - ma_dr_wav_smpl smpl; - ma_dr_wav_acid acid; - ma_dr_wav_inst inst; - ma_dr_wav_bext bext; - ma_dr_wav_list_label_or_note labelOrNote; - ma_dr_wav_list_labelled_cue_region labelledCueRegion; - ma_dr_wav_list_info_text infoText; - ma_dr_wav_unknown_metadata unknown; - } data; -} ma_dr_wav_metadata; -typedef struct -{ - ma_dr_wav_read_proc onRead; - ma_dr_wav_write_proc onWrite; - ma_dr_wav_seek_proc onSeek; - void* pUserData; - ma_allocation_callbacks allocationCallbacks; - ma_dr_wav_container container; - ma_dr_wav_fmt fmt; - ma_uint32 sampleRate; - ma_uint16 channels; - ma_uint16 bitsPerSample; - ma_uint16 translatedFormatTag; - ma_uint64 totalPCMFrameCount; - ma_uint64 dataChunkDataSize; - ma_uint64 dataChunkDataPos; - ma_uint64 bytesRemaining; - ma_uint64 readCursorInPCMFrames; - ma_uint64 dataChunkDataSizeTargetWrite; - ma_bool32 isSequentialWrite; - ma_dr_wav_metadata* pMetadata; - ma_uint32 metadataCount; - ma_dr_wav__memory_stream memoryStream; - ma_dr_wav__memory_stream_write memoryStreamWrite; - struct - { - ma_uint32 bytesRemainingInBlock; - ma_uint16 predictor[2]; - ma_int32 delta[2]; - ma_int32 cachedFrames[4]; - ma_uint32 cachedFrameCount; - ma_int32 prevFrames[2][2]; - } msadpcm; - struct - { - ma_uint32 bytesRemainingInBlock; - ma_int32 predictor[2]; - ma_int32 stepIndex[2]; - ma_int32 cachedFrames[16]; - ma_uint32 cachedFrameCount; - } ima; - struct - { - ma_bool8 isLE; - ma_bool8 isUnsigned; - } aiff; -} ma_dr_wav; -MA_API ma_bool32 ma_dr_wav_init(ma_dr_wav* pWav, ma_dr_wav_read_proc onRead, ma_dr_wav_seek_proc onSeek, void* pUserData, const ma_allocation_callbacks* pAllocationCallbacks); -MA_API ma_bool32 ma_dr_wav_init_ex(ma_dr_wav* pWav, ma_dr_wav_read_proc onRead, ma_dr_wav_seek_proc onSeek, ma_dr_wav_chunk_proc onChunk, void* pReadSeekUserData, void* pChunkUserData, ma_uint32 flags, const ma_allocation_callbacks* pAllocationCallbacks); -MA_API ma_bool32 ma_dr_wav_init_with_metadata(ma_dr_wav* pWav, ma_dr_wav_read_proc onRead, ma_dr_wav_seek_proc onSeek, void* pUserData, ma_uint32 flags, const ma_allocation_callbacks* pAllocationCallbacks); -MA_API ma_bool32 ma_dr_wav_init_write(ma_dr_wav* pWav, const ma_dr_wav_data_format* pFormat, ma_dr_wav_write_proc onWrite, ma_dr_wav_seek_proc onSeek, void* pUserData, const ma_allocation_callbacks* pAllocationCallbacks); -MA_API ma_bool32 ma_dr_wav_init_write_sequential(ma_dr_wav* pWav, const ma_dr_wav_data_format* pFormat, ma_uint64 totalSampleCount, ma_dr_wav_write_proc onWrite, void* pUserData, const ma_allocation_callbacks* pAllocationCallbacks); -MA_API ma_bool32 ma_dr_wav_init_write_sequential_pcm_frames(ma_dr_wav* pWav, const ma_dr_wav_data_format* pFormat, ma_uint64 totalPCMFrameCount, ma_dr_wav_write_proc onWrite, void* pUserData, const ma_allocation_callbacks* pAllocationCallbacks); -MA_API ma_bool32 ma_dr_wav_init_write_with_metadata(ma_dr_wav* pWav, const ma_dr_wav_data_format* pFormat, ma_dr_wav_write_proc onWrite, ma_dr_wav_seek_proc onSeek, void* pUserData, const ma_allocation_callbacks* pAllocationCallbacks, ma_dr_wav_metadata* pMetadata, ma_uint32 metadataCount); -MA_API ma_uint64 ma_dr_wav_target_write_size_bytes(const ma_dr_wav_data_format* pFormat, ma_uint64 totalFrameCount, ma_dr_wav_metadata* pMetadata, ma_uint32 metadataCount); -MA_API ma_dr_wav_metadata* ma_dr_wav_take_ownership_of_metadata(ma_dr_wav* pWav); -MA_API ma_result ma_dr_wav_uninit(ma_dr_wav* pWav); -MA_API size_t ma_dr_wav_read_raw(ma_dr_wav* pWav, size_t bytesToRead, void* pBufferOut); -MA_API ma_uint64 ma_dr_wav_read_pcm_frames(ma_dr_wav* pWav, ma_uint64 framesToRead, void* pBufferOut); -MA_API ma_uint64 ma_dr_wav_read_pcm_frames_le(ma_dr_wav* pWav, ma_uint64 framesToRead, void* pBufferOut); -MA_API ma_uint64 ma_dr_wav_read_pcm_frames_be(ma_dr_wav* pWav, ma_uint64 framesToRead, void* pBufferOut); -MA_API ma_bool32 ma_dr_wav_seek_to_pcm_frame(ma_dr_wav* pWav, ma_uint64 targetFrameIndex); -MA_API ma_result ma_dr_wav_get_cursor_in_pcm_frames(ma_dr_wav* pWav, ma_uint64* pCursor); -MA_API ma_result ma_dr_wav_get_length_in_pcm_frames(ma_dr_wav* pWav, ma_uint64* pLength); -MA_API size_t ma_dr_wav_write_raw(ma_dr_wav* pWav, size_t bytesToWrite, const void* pData); -MA_API ma_uint64 ma_dr_wav_write_pcm_frames(ma_dr_wav* pWav, ma_uint64 framesToWrite, const void* pData); -MA_API ma_uint64 ma_dr_wav_write_pcm_frames_le(ma_dr_wav* pWav, ma_uint64 framesToWrite, const void* pData); -MA_API ma_uint64 ma_dr_wav_write_pcm_frames_be(ma_dr_wav* pWav, ma_uint64 framesToWrite, const void* pData); -#ifndef MA_DR_WAV_NO_CONVERSION_API -MA_API ma_uint64 ma_dr_wav_read_pcm_frames_s16(ma_dr_wav* pWav, ma_uint64 framesToRead, ma_int16* pBufferOut); -MA_API ma_uint64 ma_dr_wav_read_pcm_frames_s16le(ma_dr_wav* pWav, ma_uint64 framesToRead, ma_int16* pBufferOut); -MA_API ma_uint64 ma_dr_wav_read_pcm_frames_s16be(ma_dr_wav* pWav, ma_uint64 framesToRead, ma_int16* pBufferOut); -MA_API void ma_dr_wav_u8_to_s16(ma_int16* pOut, const ma_uint8* pIn, size_t sampleCount); -MA_API void ma_dr_wav_s24_to_s16(ma_int16* pOut, const ma_uint8* pIn, size_t sampleCount); -MA_API void ma_dr_wav_s32_to_s16(ma_int16* pOut, const ma_int32* pIn, size_t sampleCount); -MA_API void ma_dr_wav_f32_to_s16(ma_int16* pOut, const float* pIn, size_t sampleCount); -MA_API void ma_dr_wav_f64_to_s16(ma_int16* pOut, const double* pIn, size_t sampleCount); -MA_API void ma_dr_wav_alaw_to_s16(ma_int16* pOut, const ma_uint8* pIn, size_t sampleCount); -MA_API void ma_dr_wav_mulaw_to_s16(ma_int16* pOut, const ma_uint8* pIn, size_t sampleCount); -MA_API ma_uint64 ma_dr_wav_read_pcm_frames_f32(ma_dr_wav* pWav, ma_uint64 framesToRead, float* pBufferOut); -MA_API ma_uint64 ma_dr_wav_read_pcm_frames_f32le(ma_dr_wav* pWav, ma_uint64 framesToRead, float* pBufferOut); -MA_API ma_uint64 ma_dr_wav_read_pcm_frames_f32be(ma_dr_wav* pWav, ma_uint64 framesToRead, float* pBufferOut); -MA_API void ma_dr_wav_u8_to_f32(float* pOut, const ma_uint8* pIn, size_t sampleCount); -MA_API void ma_dr_wav_s16_to_f32(float* pOut, const ma_int16* pIn, size_t sampleCount); -MA_API void ma_dr_wav_s24_to_f32(float* pOut, const ma_uint8* pIn, size_t sampleCount); -MA_API void ma_dr_wav_s32_to_f32(float* pOut, const ma_int32* pIn, size_t sampleCount); -MA_API void ma_dr_wav_f64_to_f32(float* pOut, const double* pIn, size_t sampleCount); -MA_API void ma_dr_wav_alaw_to_f32(float* pOut, const ma_uint8* pIn, size_t sampleCount); -MA_API void ma_dr_wav_mulaw_to_f32(float* pOut, const ma_uint8* pIn, size_t sampleCount); -MA_API ma_uint64 ma_dr_wav_read_pcm_frames_s32(ma_dr_wav* pWav, ma_uint64 framesToRead, ma_int32* pBufferOut); -MA_API ma_uint64 ma_dr_wav_read_pcm_frames_s32le(ma_dr_wav* pWav, ma_uint64 framesToRead, ma_int32* pBufferOut); -MA_API ma_uint64 ma_dr_wav_read_pcm_frames_s32be(ma_dr_wav* pWav, ma_uint64 framesToRead, ma_int32* pBufferOut); -MA_API void ma_dr_wav_u8_to_s32(ma_int32* pOut, const ma_uint8* pIn, size_t sampleCount); -MA_API void ma_dr_wav_s16_to_s32(ma_int32* pOut, const ma_int16* pIn, size_t sampleCount); -MA_API void ma_dr_wav_s24_to_s32(ma_int32* pOut, const ma_uint8* pIn, size_t sampleCount); -MA_API void ma_dr_wav_f32_to_s32(ma_int32* pOut, const float* pIn, size_t sampleCount); -MA_API void ma_dr_wav_f64_to_s32(ma_int32* pOut, const double* pIn, size_t sampleCount); -MA_API void ma_dr_wav_alaw_to_s32(ma_int32* pOut, const ma_uint8* pIn, size_t sampleCount); -MA_API void ma_dr_wav_mulaw_to_s32(ma_int32* pOut, const ma_uint8* pIn, size_t sampleCount); -#endif -#ifndef MA_DR_WAV_NO_STDIO -MA_API ma_bool32 ma_dr_wav_init_file(ma_dr_wav* pWav, const char* filename, const ma_allocation_callbacks* pAllocationCallbacks); -MA_API ma_bool32 ma_dr_wav_init_file_ex(ma_dr_wav* pWav, const char* filename, ma_dr_wav_chunk_proc onChunk, void* pChunkUserData, ma_uint32 flags, const ma_allocation_callbacks* pAllocationCallbacks); -MA_API ma_bool32 ma_dr_wav_init_file_w(ma_dr_wav* pWav, const wchar_t* filename, const ma_allocation_callbacks* pAllocationCallbacks); -MA_API ma_bool32 ma_dr_wav_init_file_ex_w(ma_dr_wav* pWav, const wchar_t* filename, ma_dr_wav_chunk_proc onChunk, void* pChunkUserData, ma_uint32 flags, const ma_allocation_callbacks* pAllocationCallbacks); -MA_API ma_bool32 ma_dr_wav_init_file_with_metadata(ma_dr_wav* pWav, const char* filename, ma_uint32 flags, const ma_allocation_callbacks* pAllocationCallbacks); -MA_API ma_bool32 ma_dr_wav_init_file_with_metadata_w(ma_dr_wav* pWav, const wchar_t* filename, ma_uint32 flags, const ma_allocation_callbacks* pAllocationCallbacks); -MA_API ma_bool32 ma_dr_wav_init_file_write(ma_dr_wav* pWav, const char* filename, const ma_dr_wav_data_format* pFormat, const ma_allocation_callbacks* pAllocationCallbacks); -MA_API ma_bool32 ma_dr_wav_init_file_write_sequential(ma_dr_wav* pWav, const char* filename, const ma_dr_wav_data_format* pFormat, ma_uint64 totalSampleCount, const ma_allocation_callbacks* pAllocationCallbacks); -MA_API ma_bool32 ma_dr_wav_init_file_write_sequential_pcm_frames(ma_dr_wav* pWav, const char* filename, const ma_dr_wav_data_format* pFormat, ma_uint64 totalPCMFrameCount, const ma_allocation_callbacks* pAllocationCallbacks); -MA_API ma_bool32 ma_dr_wav_init_file_write_w(ma_dr_wav* pWav, const wchar_t* filename, const ma_dr_wav_data_format* pFormat, const ma_allocation_callbacks* pAllocationCallbacks); -MA_API ma_bool32 ma_dr_wav_init_file_write_sequential_w(ma_dr_wav* pWav, const wchar_t* filename, const ma_dr_wav_data_format* pFormat, ma_uint64 totalSampleCount, const ma_allocation_callbacks* pAllocationCallbacks); -MA_API ma_bool32 ma_dr_wav_init_file_write_sequential_pcm_frames_w(ma_dr_wav* pWav, const wchar_t* filename, const ma_dr_wav_data_format* pFormat, ma_uint64 totalPCMFrameCount, const ma_allocation_callbacks* pAllocationCallbacks); -#endif -MA_API ma_bool32 ma_dr_wav_init_memory(ma_dr_wav* pWav, const void* data, size_t dataSize, const ma_allocation_callbacks* pAllocationCallbacks); -MA_API ma_bool32 ma_dr_wav_init_memory_ex(ma_dr_wav* pWav, const void* data, size_t dataSize, ma_dr_wav_chunk_proc onChunk, void* pChunkUserData, ma_uint32 flags, const ma_allocation_callbacks* pAllocationCallbacks); -MA_API ma_bool32 ma_dr_wav_init_memory_with_metadata(ma_dr_wav* pWav, const void* data, size_t dataSize, ma_uint32 flags, const ma_allocation_callbacks* pAllocationCallbacks); -MA_API ma_bool32 ma_dr_wav_init_memory_write(ma_dr_wav* pWav, void** ppData, size_t* pDataSize, const ma_dr_wav_data_format* pFormat, const ma_allocation_callbacks* pAllocationCallbacks); -MA_API ma_bool32 ma_dr_wav_init_memory_write_sequential(ma_dr_wav* pWav, void** ppData, size_t* pDataSize, const ma_dr_wav_data_format* pFormat, ma_uint64 totalSampleCount, const ma_allocation_callbacks* pAllocationCallbacks); -MA_API ma_bool32 ma_dr_wav_init_memory_write_sequential_pcm_frames(ma_dr_wav* pWav, void** ppData, size_t* pDataSize, const ma_dr_wav_data_format* pFormat, ma_uint64 totalPCMFrameCount, const ma_allocation_callbacks* pAllocationCallbacks); -#ifndef MA_DR_WAV_NO_CONVERSION_API -MA_API ma_int16* ma_dr_wav_open_and_read_pcm_frames_s16(ma_dr_wav_read_proc onRead, ma_dr_wav_seek_proc onSeek, void* pUserData, unsigned int* channelsOut, unsigned int* sampleRateOut, ma_uint64* totalFrameCountOut, const ma_allocation_callbacks* pAllocationCallbacks); -MA_API float* ma_dr_wav_open_and_read_pcm_frames_f32(ma_dr_wav_read_proc onRead, ma_dr_wav_seek_proc onSeek, void* pUserData, unsigned int* channelsOut, unsigned int* sampleRateOut, ma_uint64* totalFrameCountOut, const ma_allocation_callbacks* pAllocationCallbacks); -MA_API ma_int32* ma_dr_wav_open_and_read_pcm_frames_s32(ma_dr_wav_read_proc onRead, ma_dr_wav_seek_proc onSeek, void* pUserData, unsigned int* channelsOut, unsigned int* sampleRateOut, ma_uint64* totalFrameCountOut, const ma_allocation_callbacks* pAllocationCallbacks); -#ifndef MA_DR_WAV_NO_STDIO -MA_API ma_int16* ma_dr_wav_open_file_and_read_pcm_frames_s16(const char* filename, unsigned int* channelsOut, unsigned int* sampleRateOut, ma_uint64* totalFrameCountOut, const ma_allocation_callbacks* pAllocationCallbacks); -MA_API float* ma_dr_wav_open_file_and_read_pcm_frames_f32(const char* filename, unsigned int* channelsOut, unsigned int* sampleRateOut, ma_uint64* totalFrameCountOut, const ma_allocation_callbacks* pAllocationCallbacks); -MA_API ma_int32* ma_dr_wav_open_file_and_read_pcm_frames_s32(const char* filename, unsigned int* channelsOut, unsigned int* sampleRateOut, ma_uint64* totalFrameCountOut, const ma_allocation_callbacks* pAllocationCallbacks); -MA_API ma_int16* ma_dr_wav_open_file_and_read_pcm_frames_s16_w(const wchar_t* filename, unsigned int* channelsOut, unsigned int* sampleRateOut, ma_uint64* totalFrameCountOut, const ma_allocation_callbacks* pAllocationCallbacks); -MA_API float* ma_dr_wav_open_file_and_read_pcm_frames_f32_w(const wchar_t* filename, unsigned int* channelsOut, unsigned int* sampleRateOut, ma_uint64* totalFrameCountOut, const ma_allocation_callbacks* pAllocationCallbacks); -MA_API ma_int32* ma_dr_wav_open_file_and_read_pcm_frames_s32_w(const wchar_t* filename, unsigned int* channelsOut, unsigned int* sampleRateOut, ma_uint64* totalFrameCountOut, const ma_allocation_callbacks* pAllocationCallbacks); -#endif -MA_API ma_int16* ma_dr_wav_open_memory_and_read_pcm_frames_s16(const void* data, size_t dataSize, unsigned int* channelsOut, unsigned int* sampleRateOut, ma_uint64* totalFrameCountOut, const ma_allocation_callbacks* pAllocationCallbacks); -MA_API float* ma_dr_wav_open_memory_and_read_pcm_frames_f32(const void* data, size_t dataSize, unsigned int* channelsOut, unsigned int* sampleRateOut, ma_uint64* totalFrameCountOut, const ma_allocation_callbacks* pAllocationCallbacks); -MA_API ma_int32* ma_dr_wav_open_memory_and_read_pcm_frames_s32(const void* data, size_t dataSize, unsigned int* channelsOut, unsigned int* sampleRateOut, ma_uint64* totalFrameCountOut, const ma_allocation_callbacks* pAllocationCallbacks); -#endif -MA_API void ma_dr_wav_free(void* p, const ma_allocation_callbacks* pAllocationCallbacks); -MA_API ma_uint16 ma_dr_wav_bytes_to_u16(const ma_uint8* data); -MA_API ma_int16 ma_dr_wav_bytes_to_s16(const ma_uint8* data); -MA_API ma_uint32 ma_dr_wav_bytes_to_u32(const ma_uint8* data); -MA_API ma_int32 ma_dr_wav_bytes_to_s32(const ma_uint8* data); -MA_API ma_uint64 ma_dr_wav_bytes_to_u64(const ma_uint8* data); -MA_API ma_int64 ma_dr_wav_bytes_to_s64(const ma_uint8* data); -MA_API float ma_dr_wav_bytes_to_f32(const ma_uint8* data); -MA_API ma_bool32 ma_dr_wav_guid_equal(const ma_uint8 a[16], const ma_uint8 b[16]); -MA_API ma_bool32 ma_dr_wav_fourcc_equal(const ma_uint8* a, const char* b); -#ifdef __cplusplus -} -#endif -#endif -/* dr_wav_h end */ -#endif /* MA_NO_WAV */ - -#if !defined(MA_NO_FLAC) && !defined(MA_NO_DECODING) -/* dr_flac_h begin */ -#ifndef ma_dr_flac_h -#define ma_dr_flac_h -#ifdef __cplusplus -extern "C" { -#endif -#define MA_DR_FLAC_STRINGIFY(x) #x -#define MA_DR_FLAC_XSTRINGIFY(x) MA_DR_FLAC_STRINGIFY(x) -#define MA_DR_FLAC_VERSION_MAJOR 0 -#define MA_DR_FLAC_VERSION_MINOR 12 -#define MA_DR_FLAC_VERSION_REVISION 43 -#define MA_DR_FLAC_VERSION_STRING MA_DR_FLAC_XSTRINGIFY(MA_DR_FLAC_VERSION_MAJOR) "." MA_DR_FLAC_XSTRINGIFY(MA_DR_FLAC_VERSION_MINOR) "." MA_DR_FLAC_XSTRINGIFY(MA_DR_FLAC_VERSION_REVISION) -#include -#if defined(_MSC_VER) && _MSC_VER >= 1700 - #define MA_DR_FLAC_DEPRECATED __declspec(deprecated) -#elif (defined(__GNUC__) && __GNUC__ >= 4) - #define MA_DR_FLAC_DEPRECATED __attribute__((deprecated)) -#elif defined(__has_feature) - #if __has_feature(attribute_deprecated) - #define MA_DR_FLAC_DEPRECATED __attribute__((deprecated)) - #else - #define MA_DR_FLAC_DEPRECATED - #endif -#else - #define MA_DR_FLAC_DEPRECATED -#endif -MA_API void ma_dr_flac_version(ma_uint32* pMajor, ma_uint32* pMinor, ma_uint32* pRevision); -MA_API const char* ma_dr_flac_version_string(void); -#ifndef MA_DR_FLAC_BUFFER_SIZE -#define MA_DR_FLAC_BUFFER_SIZE 4096 -#endif -#ifdef MA_64BIT -typedef ma_uint64 ma_dr_flac_cache_t; -#else -typedef ma_uint32 ma_dr_flac_cache_t; -#endif -#define MA_DR_FLAC_METADATA_BLOCK_TYPE_STREAMINFO 0 -#define MA_DR_FLAC_METADATA_BLOCK_TYPE_PADDING 1 -#define MA_DR_FLAC_METADATA_BLOCK_TYPE_APPLICATION 2 -#define MA_DR_FLAC_METADATA_BLOCK_TYPE_SEEKTABLE 3 -#define MA_DR_FLAC_METADATA_BLOCK_TYPE_VORBIS_COMMENT 4 -#define MA_DR_FLAC_METADATA_BLOCK_TYPE_CUESHEET 5 -#define MA_DR_FLAC_METADATA_BLOCK_TYPE_PICTURE 6 -#define MA_DR_FLAC_METADATA_BLOCK_TYPE_INVALID 127 -#define MA_DR_FLAC_PICTURE_TYPE_OTHER 0 -#define MA_DR_FLAC_PICTURE_TYPE_FILE_ICON 1 -#define MA_DR_FLAC_PICTURE_TYPE_OTHER_FILE_ICON 2 -#define MA_DR_FLAC_PICTURE_TYPE_COVER_FRONT 3 -#define MA_DR_FLAC_PICTURE_TYPE_COVER_BACK 4 -#define MA_DR_FLAC_PICTURE_TYPE_LEAFLET_PAGE 5 -#define MA_DR_FLAC_PICTURE_TYPE_MEDIA 6 -#define MA_DR_FLAC_PICTURE_TYPE_LEAD_ARTIST 7 -#define MA_DR_FLAC_PICTURE_TYPE_ARTIST 8 -#define MA_DR_FLAC_PICTURE_TYPE_CONDUCTOR 9 -#define MA_DR_FLAC_PICTURE_TYPE_BAND 10 -#define MA_DR_FLAC_PICTURE_TYPE_COMPOSER 11 -#define MA_DR_FLAC_PICTURE_TYPE_LYRICIST 12 -#define MA_DR_FLAC_PICTURE_TYPE_RECORDING_LOCATION 13 -#define MA_DR_FLAC_PICTURE_TYPE_DURING_RECORDING 14 -#define MA_DR_FLAC_PICTURE_TYPE_DURING_PERFORMANCE 15 -#define MA_DR_FLAC_PICTURE_TYPE_SCREEN_CAPTURE 16 -#define MA_DR_FLAC_PICTURE_TYPE_BRIGHT_COLORED_FISH 17 -#define MA_DR_FLAC_PICTURE_TYPE_ILLUSTRATION 18 -#define MA_DR_FLAC_PICTURE_TYPE_BAND_LOGOTYPE 19 -#define MA_DR_FLAC_PICTURE_TYPE_PUBLISHER_LOGOTYPE 20 -typedef enum -{ - ma_dr_flac_container_native, - ma_dr_flac_container_ogg, - ma_dr_flac_container_unknown -} ma_dr_flac_container; -typedef enum -{ - ma_dr_flac_seek_origin_start, - ma_dr_flac_seek_origin_current -} ma_dr_flac_seek_origin; -typedef struct -{ - ma_uint64 firstPCMFrame; - ma_uint64 flacFrameOffset; - ma_uint16 pcmFrameCount; -} ma_dr_flac_seekpoint; -typedef struct -{ - ma_uint16 minBlockSizeInPCMFrames; - ma_uint16 maxBlockSizeInPCMFrames; - ma_uint32 minFrameSizeInPCMFrames; - ma_uint32 maxFrameSizeInPCMFrames; - ma_uint32 sampleRate; - ma_uint8 channels; - ma_uint8 bitsPerSample; - ma_uint64 totalPCMFrameCount; - ma_uint8 md5[16]; -} ma_dr_flac_streaminfo; -typedef struct -{ - ma_uint32 type; - const void* pRawData; - ma_uint32 rawDataSize; - union - { - ma_dr_flac_streaminfo streaminfo; - struct - { - int unused; - } padding; - struct - { - ma_uint32 id; - const void* pData; - ma_uint32 dataSize; - } application; - struct - { - ma_uint32 seekpointCount; - const ma_dr_flac_seekpoint* pSeekpoints; - } seektable; - struct - { - ma_uint32 vendorLength; - const char* vendor; - ma_uint32 commentCount; - const void* pComments; - } vorbis_comment; - struct - { - char catalog[128]; - ma_uint64 leadInSampleCount; - ma_bool32 isCD; - ma_uint8 trackCount; - const void* pTrackData; - } cuesheet; - struct - { - ma_uint32 type; - ma_uint32 mimeLength; - const char* mime; - ma_uint32 descriptionLength; - const char* description; - ma_uint32 width; - ma_uint32 height; - ma_uint32 colorDepth; - ma_uint32 indexColorCount; - ma_uint32 pictureDataSize; - const ma_uint8* pPictureData; - } picture; - } data; -} ma_dr_flac_metadata; -typedef size_t (* ma_dr_flac_read_proc)(void* pUserData, void* pBufferOut, size_t bytesToRead); -typedef ma_bool32 (* ma_dr_flac_seek_proc)(void* pUserData, int offset, ma_dr_flac_seek_origin origin); -typedef void (* ma_dr_flac_meta_proc)(void* pUserData, ma_dr_flac_metadata* pMetadata); -typedef struct -{ - const ma_uint8* data; - size_t dataSize; - size_t currentReadPos; -} ma_dr_flac__memory_stream; -typedef struct -{ - ma_dr_flac_read_proc onRead; - ma_dr_flac_seek_proc onSeek; - void* pUserData; - size_t unalignedByteCount; - ma_dr_flac_cache_t unalignedCache; - ma_uint32 nextL2Line; - ma_uint32 consumedBits; - ma_dr_flac_cache_t cacheL2[MA_DR_FLAC_BUFFER_SIZE/sizeof(ma_dr_flac_cache_t)]; - ma_dr_flac_cache_t cache; - ma_uint16 crc16; - ma_dr_flac_cache_t crc16Cache; - ma_uint32 crc16CacheIgnoredBytes; -} ma_dr_flac_bs; -typedef struct -{ - ma_uint8 subframeType; - ma_uint8 wastedBitsPerSample; - ma_uint8 lpcOrder; - ma_int32* pSamplesS32; -} ma_dr_flac_subframe; -typedef struct -{ - ma_uint64 pcmFrameNumber; - ma_uint32 flacFrameNumber; - ma_uint32 sampleRate; - ma_uint16 blockSizeInPCMFrames; - ma_uint8 channelAssignment; - ma_uint8 bitsPerSample; - ma_uint8 crc8; -} ma_dr_flac_frame_header; -typedef struct -{ - ma_dr_flac_frame_header header; - ma_uint32 pcmFramesRemaining; - ma_dr_flac_subframe subframes[8]; -} ma_dr_flac_frame; -typedef struct -{ - ma_dr_flac_meta_proc onMeta; - void* pUserDataMD; - ma_allocation_callbacks allocationCallbacks; - ma_uint32 sampleRate; - ma_uint8 channels; - ma_uint8 bitsPerSample; - ma_uint16 maxBlockSizeInPCMFrames; - ma_uint64 totalPCMFrameCount; - ma_dr_flac_container container; - ma_uint32 seekpointCount; - ma_dr_flac_frame currentFLACFrame; - ma_uint64 currentPCMFrame; - ma_uint64 firstFLACFramePosInBytes; - ma_dr_flac__memory_stream memoryStream; - ma_int32* pDecodedSamples; - ma_dr_flac_seekpoint* pSeekpoints; - void* _oggbs; - ma_bool32 _noSeekTableSeek : 1; - ma_bool32 _noBinarySearchSeek : 1; - ma_bool32 _noBruteForceSeek : 1; - ma_dr_flac_bs bs; - ma_uint8 pExtraData[1]; -} ma_dr_flac; -MA_API ma_dr_flac* ma_dr_flac_open(ma_dr_flac_read_proc onRead, ma_dr_flac_seek_proc onSeek, void* pUserData, const ma_allocation_callbacks* pAllocationCallbacks); -MA_API ma_dr_flac* ma_dr_flac_open_relaxed(ma_dr_flac_read_proc onRead, ma_dr_flac_seek_proc onSeek, ma_dr_flac_container container, void* pUserData, const ma_allocation_callbacks* pAllocationCallbacks); -MA_API ma_dr_flac* ma_dr_flac_open_with_metadata(ma_dr_flac_read_proc onRead, ma_dr_flac_seek_proc onSeek, ma_dr_flac_meta_proc onMeta, void* pUserData, const ma_allocation_callbacks* pAllocationCallbacks); -MA_API ma_dr_flac* ma_dr_flac_open_with_metadata_relaxed(ma_dr_flac_read_proc onRead, ma_dr_flac_seek_proc onSeek, ma_dr_flac_meta_proc onMeta, ma_dr_flac_container container, void* pUserData, const ma_allocation_callbacks* pAllocationCallbacks); -MA_API void ma_dr_flac_close(ma_dr_flac* pFlac); -MA_API ma_uint64 ma_dr_flac_read_pcm_frames_s32(ma_dr_flac* pFlac, ma_uint64 framesToRead, ma_int32* pBufferOut); -MA_API ma_uint64 ma_dr_flac_read_pcm_frames_s16(ma_dr_flac* pFlac, ma_uint64 framesToRead, ma_int16* pBufferOut); -MA_API ma_uint64 ma_dr_flac_read_pcm_frames_f32(ma_dr_flac* pFlac, ma_uint64 framesToRead, float* pBufferOut); -MA_API ma_bool32 ma_dr_flac_seek_to_pcm_frame(ma_dr_flac* pFlac, ma_uint64 pcmFrameIndex); -#ifndef MA_DR_FLAC_NO_STDIO -MA_API ma_dr_flac* ma_dr_flac_open_file(const char* pFileName, const ma_allocation_callbacks* pAllocationCallbacks); -MA_API ma_dr_flac* ma_dr_flac_open_file_w(const wchar_t* pFileName, const ma_allocation_callbacks* pAllocationCallbacks); -MA_API ma_dr_flac* ma_dr_flac_open_file_with_metadata(const char* pFileName, ma_dr_flac_meta_proc onMeta, void* pUserData, const ma_allocation_callbacks* pAllocationCallbacks); -MA_API ma_dr_flac* ma_dr_flac_open_file_with_metadata_w(const wchar_t* pFileName, ma_dr_flac_meta_proc onMeta, void* pUserData, const ma_allocation_callbacks* pAllocationCallbacks); -#endif -MA_API ma_dr_flac* ma_dr_flac_open_memory(const void* pData, size_t dataSize, const ma_allocation_callbacks* pAllocationCallbacks); -MA_API ma_dr_flac* ma_dr_flac_open_memory_with_metadata(const void* pData, size_t dataSize, ma_dr_flac_meta_proc onMeta, void* pUserData, const ma_allocation_callbacks* pAllocationCallbacks); -MA_API ma_int32* ma_dr_flac_open_and_read_pcm_frames_s32(ma_dr_flac_read_proc onRead, ma_dr_flac_seek_proc onSeek, void* pUserData, unsigned int* channels, unsigned int* sampleRate, ma_uint64* totalPCMFrameCount, const ma_allocation_callbacks* pAllocationCallbacks); -MA_API ma_int16* ma_dr_flac_open_and_read_pcm_frames_s16(ma_dr_flac_read_proc onRead, ma_dr_flac_seek_proc onSeek, void* pUserData, unsigned int* channels, unsigned int* sampleRate, ma_uint64* totalPCMFrameCount, const ma_allocation_callbacks* pAllocationCallbacks); -MA_API float* ma_dr_flac_open_and_read_pcm_frames_f32(ma_dr_flac_read_proc onRead, ma_dr_flac_seek_proc onSeek, void* pUserData, unsigned int* channels, unsigned int* sampleRate, ma_uint64* totalPCMFrameCount, const ma_allocation_callbacks* pAllocationCallbacks); -#ifndef MA_DR_FLAC_NO_STDIO -MA_API ma_int32* ma_dr_flac_open_file_and_read_pcm_frames_s32(const char* filename, unsigned int* channels, unsigned int* sampleRate, ma_uint64* totalPCMFrameCount, const ma_allocation_callbacks* pAllocationCallbacks); -MA_API ma_int16* ma_dr_flac_open_file_and_read_pcm_frames_s16(const char* filename, unsigned int* channels, unsigned int* sampleRate, ma_uint64* totalPCMFrameCount, const ma_allocation_callbacks* pAllocationCallbacks); -MA_API float* ma_dr_flac_open_file_and_read_pcm_frames_f32(const char* filename, unsigned int* channels, unsigned int* sampleRate, ma_uint64* totalPCMFrameCount, const ma_allocation_callbacks* pAllocationCallbacks); -#endif -MA_API ma_int32* ma_dr_flac_open_memory_and_read_pcm_frames_s32(const void* data, size_t dataSize, unsigned int* channels, unsigned int* sampleRate, ma_uint64* totalPCMFrameCount, const ma_allocation_callbacks* pAllocationCallbacks); -MA_API ma_int16* ma_dr_flac_open_memory_and_read_pcm_frames_s16(const void* data, size_t dataSize, unsigned int* channels, unsigned int* sampleRate, ma_uint64* totalPCMFrameCount, const ma_allocation_callbacks* pAllocationCallbacks); -MA_API float* ma_dr_flac_open_memory_and_read_pcm_frames_f32(const void* data, size_t dataSize, unsigned int* channels, unsigned int* sampleRate, ma_uint64* totalPCMFrameCount, const ma_allocation_callbacks* pAllocationCallbacks); -MA_API void ma_dr_flac_free(void* p, const ma_allocation_callbacks* pAllocationCallbacks); -typedef struct -{ - ma_uint32 countRemaining; - const char* pRunningData; -} ma_dr_flac_vorbis_comment_iterator; -MA_API void ma_dr_flac_init_vorbis_comment_iterator(ma_dr_flac_vorbis_comment_iterator* pIter, ma_uint32 commentCount, const void* pComments); -MA_API const char* ma_dr_flac_next_vorbis_comment(ma_dr_flac_vorbis_comment_iterator* pIter, ma_uint32* pCommentLengthOut); -typedef struct -{ - ma_uint32 countRemaining; - const char* pRunningData; -} ma_dr_flac_cuesheet_track_iterator; -typedef struct -{ - ma_uint64 offset; - ma_uint8 index; - ma_uint8 reserved[3]; -} ma_dr_flac_cuesheet_track_index; -typedef struct -{ - ma_uint64 offset; - ma_uint8 trackNumber; - char ISRC[12]; - ma_bool8 isAudio; - ma_bool8 preEmphasis; - ma_uint8 indexCount; - const ma_dr_flac_cuesheet_track_index* pIndexPoints; -} ma_dr_flac_cuesheet_track; -MA_API void ma_dr_flac_init_cuesheet_track_iterator(ma_dr_flac_cuesheet_track_iterator* pIter, ma_uint32 trackCount, const void* pTrackData); -MA_API ma_bool32 ma_dr_flac_next_cuesheet_track(ma_dr_flac_cuesheet_track_iterator* pIter, ma_dr_flac_cuesheet_track* pCuesheetTrack); -#ifdef __cplusplus -} -#endif -#endif -/* dr_flac_h end */ -#endif /* MA_NO_FLAC */ - -#if !defined(MA_NO_MP3) && !defined(MA_NO_DECODING) -/* dr_mp3_h begin */ -#ifndef ma_dr_mp3_h -#define ma_dr_mp3_h -#ifdef __cplusplus -extern "C" { -#endif -#define MA_DR_MP3_STRINGIFY(x) #x -#define MA_DR_MP3_XSTRINGIFY(x) MA_DR_MP3_STRINGIFY(x) -#define MA_DR_MP3_VERSION_MAJOR 0 -#define MA_DR_MP3_VERSION_MINOR 6 -#define MA_DR_MP3_VERSION_REVISION 40 -#define MA_DR_MP3_VERSION_STRING MA_DR_MP3_XSTRINGIFY(MA_DR_MP3_VERSION_MAJOR) "." MA_DR_MP3_XSTRINGIFY(MA_DR_MP3_VERSION_MINOR) "." MA_DR_MP3_XSTRINGIFY(MA_DR_MP3_VERSION_REVISION) -#include -#define MA_DR_MP3_MAX_PCM_FRAMES_PER_MP3_FRAME 1152 -#define MA_DR_MP3_MAX_SAMPLES_PER_FRAME (MA_DR_MP3_MAX_PCM_FRAMES_PER_MP3_FRAME*2) -MA_API void ma_dr_mp3_version(ma_uint32* pMajor, ma_uint32* pMinor, ma_uint32* pRevision); -MA_API const char* ma_dr_mp3_version_string(void); -typedef struct -{ - int frame_bytes, channels, hz, layer, bitrate_kbps; -} ma_dr_mp3dec_frame_info; -typedef struct -{ - float mdct_overlap[2][9*32], qmf_state[15*2*32]; - int reserv, free_format_bytes; - ma_uint8 header[4], reserv_buf[511]; -} ma_dr_mp3dec; -MA_API void ma_dr_mp3dec_init(ma_dr_mp3dec *dec); -MA_API int ma_dr_mp3dec_decode_frame(ma_dr_mp3dec *dec, const ma_uint8 *mp3, int mp3_bytes, void *pcm, ma_dr_mp3dec_frame_info *info); -MA_API void ma_dr_mp3dec_f32_to_s16(const float *in, ma_int16 *out, size_t num_samples); -typedef enum -{ - ma_dr_mp3_seek_origin_start, - ma_dr_mp3_seek_origin_current -} ma_dr_mp3_seek_origin; -typedef struct -{ - ma_uint64 seekPosInBytes; - ma_uint64 pcmFrameIndex; - ma_uint16 mp3FramesToDiscard; - ma_uint16 pcmFramesToDiscard; -} ma_dr_mp3_seek_point; -typedef size_t (* ma_dr_mp3_read_proc)(void* pUserData, void* pBufferOut, size_t bytesToRead); -typedef ma_bool32 (* ma_dr_mp3_seek_proc)(void* pUserData, int offset, ma_dr_mp3_seek_origin origin); -typedef struct -{ - ma_uint32 channels; - ma_uint32 sampleRate; -} ma_dr_mp3_config; -typedef struct -{ - ma_dr_mp3dec decoder; - ma_uint32 channels; - ma_uint32 sampleRate; - ma_dr_mp3_read_proc onRead; - ma_dr_mp3_seek_proc onSeek; - void* pUserData; - ma_allocation_callbacks allocationCallbacks; - ma_uint32 mp3FrameChannels; - ma_uint32 mp3FrameSampleRate; - ma_uint32 pcmFramesConsumedInMP3Frame; - ma_uint32 pcmFramesRemainingInMP3Frame; - ma_uint8 pcmFrames[sizeof(float)*MA_DR_MP3_MAX_SAMPLES_PER_FRAME]; - ma_uint64 currentPCMFrame; - ma_uint64 streamCursor; - ma_dr_mp3_seek_point* pSeekPoints; - ma_uint32 seekPointCount; - size_t dataSize; - size_t dataCapacity; - size_t dataConsumed; - ma_uint8* pData; - ma_bool32 atEnd : 1; - struct - { - const ma_uint8* pData; - size_t dataSize; - size_t currentReadPos; - } memory; -} ma_dr_mp3; -MA_API ma_bool32 ma_dr_mp3_init(ma_dr_mp3* pMP3, ma_dr_mp3_read_proc onRead, ma_dr_mp3_seek_proc onSeek, void* pUserData, const ma_allocation_callbacks* pAllocationCallbacks); -MA_API ma_bool32 ma_dr_mp3_init_memory(ma_dr_mp3* pMP3, const void* pData, size_t dataSize, const ma_allocation_callbacks* pAllocationCallbacks); -#ifndef MA_DR_MP3_NO_STDIO -MA_API ma_bool32 ma_dr_mp3_init_file(ma_dr_mp3* pMP3, const char* pFilePath, const ma_allocation_callbacks* pAllocationCallbacks); -MA_API ma_bool32 ma_dr_mp3_init_file_w(ma_dr_mp3* pMP3, const wchar_t* pFilePath, const ma_allocation_callbacks* pAllocationCallbacks); -#endif -MA_API void ma_dr_mp3_uninit(ma_dr_mp3* pMP3); -MA_API ma_uint64 ma_dr_mp3_read_pcm_frames_f32(ma_dr_mp3* pMP3, ma_uint64 framesToRead, float* pBufferOut); -MA_API ma_uint64 ma_dr_mp3_read_pcm_frames_s16(ma_dr_mp3* pMP3, ma_uint64 framesToRead, ma_int16* pBufferOut); -MA_API ma_bool32 ma_dr_mp3_seek_to_pcm_frame(ma_dr_mp3* pMP3, ma_uint64 frameIndex); -MA_API ma_uint64 ma_dr_mp3_get_pcm_frame_count(ma_dr_mp3* pMP3); -MA_API ma_uint64 ma_dr_mp3_get_mp3_frame_count(ma_dr_mp3* pMP3); -MA_API ma_bool32 ma_dr_mp3_get_mp3_and_pcm_frame_count(ma_dr_mp3* pMP3, ma_uint64* pMP3FrameCount, ma_uint64* pPCMFrameCount); -MA_API ma_bool32 ma_dr_mp3_calculate_seek_points(ma_dr_mp3* pMP3, ma_uint32* pSeekPointCount, ma_dr_mp3_seek_point* pSeekPoints); -MA_API ma_bool32 ma_dr_mp3_bind_seek_table(ma_dr_mp3* pMP3, ma_uint32 seekPointCount, ma_dr_mp3_seek_point* pSeekPoints); -MA_API float* ma_dr_mp3_open_and_read_pcm_frames_f32(ma_dr_mp3_read_proc onRead, ma_dr_mp3_seek_proc onSeek, void* pUserData, ma_dr_mp3_config* pConfig, ma_uint64* pTotalFrameCount, const ma_allocation_callbacks* pAllocationCallbacks); -MA_API ma_int16* ma_dr_mp3_open_and_read_pcm_frames_s16(ma_dr_mp3_read_proc onRead, ma_dr_mp3_seek_proc onSeek, void* pUserData, ma_dr_mp3_config* pConfig, ma_uint64* pTotalFrameCount, const ma_allocation_callbacks* pAllocationCallbacks); -MA_API float* ma_dr_mp3_open_memory_and_read_pcm_frames_f32(const void* pData, size_t dataSize, ma_dr_mp3_config* pConfig, ma_uint64* pTotalFrameCount, const ma_allocation_callbacks* pAllocationCallbacks); -MA_API ma_int16* ma_dr_mp3_open_memory_and_read_pcm_frames_s16(const void* pData, size_t dataSize, ma_dr_mp3_config* pConfig, ma_uint64* pTotalFrameCount, const ma_allocation_callbacks* pAllocationCallbacks); -#ifndef MA_DR_MP3_NO_STDIO -MA_API float* ma_dr_mp3_open_file_and_read_pcm_frames_f32(const char* filePath, ma_dr_mp3_config* pConfig, ma_uint64* pTotalFrameCount, const ma_allocation_callbacks* pAllocationCallbacks); -MA_API ma_int16* ma_dr_mp3_open_file_and_read_pcm_frames_s16(const char* filePath, ma_dr_mp3_config* pConfig, ma_uint64* pTotalFrameCount, const ma_allocation_callbacks* pAllocationCallbacks); -#endif -MA_API void* ma_dr_mp3_malloc(size_t sz, const ma_allocation_callbacks* pAllocationCallbacks); -MA_API void ma_dr_mp3_free(void* p, const ma_allocation_callbacks* pAllocationCallbacks); -#ifdef __cplusplus -} -#endif -#endif -/* dr_mp3_h end */ -#endif /* MA_NO_MP3 */ - - -/************************************************************************************************************************************************************** - -Decoding - -**************************************************************************************************************************************************************/ -#ifndef MA_NO_DECODING - -static ma_result ma_decoder_read_bytes(ma_decoder* pDecoder, void* pBufferOut, size_t bytesToRead, size_t* pBytesRead) -{ - MA_ASSERT(pDecoder != NULL); - - return pDecoder->onRead(pDecoder, pBufferOut, bytesToRead, pBytesRead); -} - -static ma_result ma_decoder_seek_bytes(ma_decoder* pDecoder, ma_int64 byteOffset, ma_seek_origin origin) -{ - MA_ASSERT(pDecoder != NULL); - - return pDecoder->onSeek(pDecoder, byteOffset, origin); -} - -static ma_result ma_decoder_tell_bytes(ma_decoder* pDecoder, ma_int64* pCursor) -{ - MA_ASSERT(pDecoder != NULL); - - if (pDecoder->onTell == NULL) { - return MA_NOT_IMPLEMENTED; - } - - return pDecoder->onTell(pDecoder, pCursor); -} - - -MA_API ma_decoding_backend_config ma_decoding_backend_config_init(ma_format preferredFormat, ma_uint32 seekPointCount) -{ - ma_decoding_backend_config config; - - MA_ZERO_OBJECT(&config); - config.preferredFormat = preferredFormat; - config.seekPointCount = seekPointCount; - - return config; -} - - -MA_API ma_decoder_config ma_decoder_config_init(ma_format outputFormat, ma_uint32 outputChannels, ma_uint32 outputSampleRate) -{ - ma_decoder_config config; - MA_ZERO_OBJECT(&config); - config.format = outputFormat; - config.channels = outputChannels; - config.sampleRate = outputSampleRate; - config.resampling = ma_resampler_config_init(ma_format_unknown, 0, 0, 0, ma_resample_algorithm_linear); /* Format/channels/rate doesn't matter here. */ - config.encodingFormat = ma_encoding_format_unknown; - - /* Note that we are intentionally leaving the channel map empty here which will cause the default channel map to be used. */ - - return config; -} - -MA_API ma_decoder_config ma_decoder_config_init_default(void) -{ - return ma_decoder_config_init(ma_format_unknown, 0, 0); -} - -MA_API ma_decoder_config ma_decoder_config_init_copy(const ma_decoder_config* pConfig) -{ - ma_decoder_config config; - if (pConfig != NULL) { - config = *pConfig; - } else { - MA_ZERO_OBJECT(&config); - } - - return config; -} - -static ma_result ma_decoder__init_data_converter(ma_decoder* pDecoder, const ma_decoder_config* pConfig) -{ - ma_result result; - ma_data_converter_config converterConfig; - ma_format internalFormat; - ma_uint32 internalChannels; - ma_uint32 internalSampleRate; - ma_channel internalChannelMap[MA_MAX_CHANNELS]; - - MA_ASSERT(pDecoder != NULL); - MA_ASSERT(pConfig != NULL); - - result = ma_data_source_get_data_format(pDecoder->pBackend, &internalFormat, &internalChannels, &internalSampleRate, internalChannelMap, ma_countof(internalChannelMap)); - if (result != MA_SUCCESS) { - return result; /* Failed to retrieve the internal data format. */ - } - - - /* Make sure we're not asking for too many channels. */ - if (pConfig->channels > MA_MAX_CHANNELS) { - return MA_INVALID_ARGS; - } - - /* The internal channels should have already been validated at a higher level, but we'll do it again explicitly here for safety. */ - if (internalChannels > MA_MAX_CHANNELS) { - return MA_INVALID_ARGS; - } - - - /* Output format. */ - if (pConfig->format == ma_format_unknown) { - pDecoder->outputFormat = internalFormat; - } else { - pDecoder->outputFormat = pConfig->format; - } - - if (pConfig->channels == 0) { - pDecoder->outputChannels = internalChannels; - } else { - pDecoder->outputChannels = pConfig->channels; - } - - if (pConfig->sampleRate == 0) { - pDecoder->outputSampleRate = internalSampleRate; - } else { - pDecoder->outputSampleRate = pConfig->sampleRate; - } - - converterConfig = ma_data_converter_config_init( - internalFormat, pDecoder->outputFormat, - internalChannels, pDecoder->outputChannels, - internalSampleRate, pDecoder->outputSampleRate - ); - converterConfig.pChannelMapIn = internalChannelMap; - converterConfig.pChannelMapOut = pConfig->pChannelMap; - converterConfig.channelMixMode = pConfig->channelMixMode; - converterConfig.ditherMode = pConfig->ditherMode; - converterConfig.allowDynamicSampleRate = MA_FALSE; /* Never allow dynamic sample rate conversion. Setting this to true will disable passthrough optimizations. */ - converterConfig.resampling = pConfig->resampling; - - result = ma_data_converter_init(&converterConfig, &pDecoder->allocationCallbacks, &pDecoder->converter); - if (result != MA_SUCCESS) { - return result; - } - - /* - Now that we have the decoder we need to determine whether or not we need a heap-allocated cache. We'll - need this if the data converter does not support calculation of the required input frame count. To - determine support for this we'll just run a test. - */ - { - ma_uint64 unused; - - result = ma_data_converter_get_required_input_frame_count(&pDecoder->converter, 1, &unused); - if (result != MA_SUCCESS) { - /* - We were unable to calculate the required input frame count which means we'll need to use - a heap-allocated cache. - */ - ma_uint64 inputCacheCapSizeInBytes; - - pDecoder->inputCacheCap = MA_DATA_CONVERTER_STACK_BUFFER_SIZE / ma_get_bytes_per_frame(internalFormat, internalChannels); - - /* Not strictly necessary, but keeping here for safety in case we change the default value of pDecoder->inputCacheCap. */ - inputCacheCapSizeInBytes = pDecoder->inputCacheCap * ma_get_bytes_per_frame(internalFormat, internalChannels); - if (inputCacheCapSizeInBytes > MA_SIZE_MAX) { - ma_data_converter_uninit(&pDecoder->converter, &pDecoder->allocationCallbacks); - return MA_OUT_OF_MEMORY; - } - - pDecoder->pInputCache = ma_malloc((size_t)inputCacheCapSizeInBytes, &pDecoder->allocationCallbacks); /* Safe cast to size_t. */ - if (pDecoder->pInputCache == NULL) { - ma_data_converter_uninit(&pDecoder->converter, &pDecoder->allocationCallbacks); - return MA_OUT_OF_MEMORY; - } - } - } - - return MA_SUCCESS; -} - - - -static ma_result ma_decoder_internal_on_read__custom(void* pUserData, void* pBufferOut, size_t bytesToRead, size_t* pBytesRead) -{ - ma_decoder* pDecoder = (ma_decoder*)pUserData; - MA_ASSERT(pDecoder != NULL); - - return ma_decoder_read_bytes(pDecoder, pBufferOut, bytesToRead, pBytesRead); -} - -static ma_result ma_decoder_internal_on_seek__custom(void* pUserData, ma_int64 offset, ma_seek_origin origin) -{ - ma_decoder* pDecoder = (ma_decoder*)pUserData; - MA_ASSERT(pDecoder != NULL); - - return ma_decoder_seek_bytes(pDecoder, offset, origin); -} - -static ma_result ma_decoder_internal_on_tell__custom(void* pUserData, ma_int64* pCursor) -{ - ma_decoder* pDecoder = (ma_decoder*)pUserData; - MA_ASSERT(pDecoder != NULL); - - return ma_decoder_tell_bytes(pDecoder, pCursor); -} - - -static ma_result ma_decoder_init_from_vtable__internal(const ma_decoding_backend_vtable* pVTable, void* pVTableUserData, const ma_decoder_config* pConfig, ma_decoder* pDecoder) -{ - ma_result result; - ma_decoding_backend_config backendConfig; - ma_data_source* pBackend; - - MA_ASSERT(pVTable != NULL); - MA_ASSERT(pConfig != NULL); - MA_ASSERT(pDecoder != NULL); - - if (pVTable->onInit == NULL) { - return MA_NOT_IMPLEMENTED; - } - - backendConfig = ma_decoding_backend_config_init(pConfig->format, pConfig->seekPointCount); - - result = pVTable->onInit(pVTableUserData, ma_decoder_internal_on_read__custom, ma_decoder_internal_on_seek__custom, ma_decoder_internal_on_tell__custom, pDecoder, &backendConfig, &pDecoder->allocationCallbacks, &pBackend); - if (result != MA_SUCCESS) { - return result; /* Failed to initialize the backend from this vtable. */ - } - - /* Getting here means we were able to initialize the backend so we can now initialize the decoder. */ - pDecoder->pBackend = pBackend; - pDecoder->pBackendVTable = pVTable; - pDecoder->pBackendUserData = pConfig->pCustomBackendUserData; - - return MA_SUCCESS; -} - -static ma_result ma_decoder_init_from_file__internal(const ma_decoding_backend_vtable* pVTable, void* pVTableUserData, const char* pFilePath, const ma_decoder_config* pConfig, ma_decoder* pDecoder) -{ - ma_result result; - ma_decoding_backend_config backendConfig; - ma_data_source* pBackend; - - MA_ASSERT(pVTable != NULL); - MA_ASSERT(pConfig != NULL); - MA_ASSERT(pDecoder != NULL); - - if (pVTable->onInitFile == NULL) { - return MA_NOT_IMPLEMENTED; - } - - backendConfig = ma_decoding_backend_config_init(pConfig->format, pConfig->seekPointCount); - - result = pVTable->onInitFile(pVTableUserData, pFilePath, &backendConfig, &pDecoder->allocationCallbacks, &pBackend); - if (result != MA_SUCCESS) { - return result; /* Failed to initialize the backend from this vtable. */ - } - - /* Getting here means we were able to initialize the backend so we can now initialize the decoder. */ - pDecoder->pBackend = pBackend; - pDecoder->pBackendVTable = pVTable; - pDecoder->pBackendUserData = pConfig->pCustomBackendUserData; - - return MA_SUCCESS; -} - -static ma_result ma_decoder_init_from_file_w__internal(const ma_decoding_backend_vtable* pVTable, void* pVTableUserData, const wchar_t* pFilePath, const ma_decoder_config* pConfig, ma_decoder* pDecoder) -{ - ma_result result; - ma_decoding_backend_config backendConfig; - ma_data_source* pBackend; - - MA_ASSERT(pVTable != NULL); - MA_ASSERT(pConfig != NULL); - MA_ASSERT(pDecoder != NULL); - - if (pVTable->onInitFileW == NULL) { - return MA_NOT_IMPLEMENTED; - } - - backendConfig = ma_decoding_backend_config_init(pConfig->format, pConfig->seekPointCount); - - result = pVTable->onInitFileW(pVTableUserData, pFilePath, &backendConfig, &pDecoder->allocationCallbacks, &pBackend); - if (result != MA_SUCCESS) { - return result; /* Failed to initialize the backend from this vtable. */ - } - - /* Getting here means we were able to initialize the backend so we can now initialize the decoder. */ - pDecoder->pBackend = pBackend; - pDecoder->pBackendVTable = pVTable; - pDecoder->pBackendUserData = pConfig->pCustomBackendUserData; - - return MA_SUCCESS; -} - -static ma_result ma_decoder_init_from_memory__internal(const ma_decoding_backend_vtable* pVTable, void* pVTableUserData, const void* pData, size_t dataSize, const ma_decoder_config* pConfig, ma_decoder* pDecoder) -{ - ma_result result; - ma_decoding_backend_config backendConfig; - ma_data_source* pBackend; - - MA_ASSERT(pVTable != NULL); - MA_ASSERT(pConfig != NULL); - MA_ASSERT(pDecoder != NULL); - - if (pVTable->onInitMemory == NULL) { - return MA_NOT_IMPLEMENTED; - } - - backendConfig = ma_decoding_backend_config_init(pConfig->format, pConfig->seekPointCount); - - result = pVTable->onInitMemory(pVTableUserData, pData, dataSize, &backendConfig, &pDecoder->allocationCallbacks, &pBackend); - if (result != MA_SUCCESS) { - return result; /* Failed to initialize the backend from this vtable. */ - } - - /* Getting here means we were able to initialize the backend so we can now initialize the decoder. */ - pDecoder->pBackend = pBackend; - pDecoder->pBackendVTable = pVTable; - pDecoder->pBackendUserData = pConfig->pCustomBackendUserData; - - return MA_SUCCESS; -} - - - -static ma_result ma_decoder_init_custom__internal(const ma_decoder_config* pConfig, ma_decoder* pDecoder) -{ - ma_result result = MA_NO_BACKEND; - size_t ivtable; - - MA_ASSERT(pConfig != NULL); - MA_ASSERT(pDecoder != NULL); - - if (pConfig->ppCustomBackendVTables == NULL) { - return MA_NO_BACKEND; - } - - /* The order each backend is listed is what defines the priority. */ - for (ivtable = 0; ivtable < pConfig->customBackendCount; ivtable += 1) { - const ma_decoding_backend_vtable* pVTable = pConfig->ppCustomBackendVTables[ivtable]; - if (pVTable != NULL) { - result = ma_decoder_init_from_vtable__internal(pVTable, pConfig->pCustomBackendUserData, pConfig, pDecoder); - if (result == MA_SUCCESS) { - return MA_SUCCESS; - } else { - /* Initialization failed. Move on to the next one, but seek back to the start first so the next vtable starts from the first byte of the file. */ - result = ma_decoder_seek_bytes(pDecoder, 0, ma_seek_origin_start); - if (result != MA_SUCCESS) { - return result; /* Failed to seek back to the start. */ - } - } - } else { - /* No vtable. */ - } - } - - /* Getting here means we couldn't find a backend. */ - return MA_NO_BACKEND; -} - -static ma_result ma_decoder_init_custom_from_file__internal(const char* pFilePath, const ma_decoder_config* pConfig, ma_decoder* pDecoder) -{ - ma_result result = MA_NO_BACKEND; - size_t ivtable; - - MA_ASSERT(pConfig != NULL); - MA_ASSERT(pDecoder != NULL); - - if (pConfig->ppCustomBackendVTables == NULL) { - return MA_NO_BACKEND; - } - - /* The order each backend is listed is what defines the priority. */ - for (ivtable = 0; ivtable < pConfig->customBackendCount; ivtable += 1) { - const ma_decoding_backend_vtable* pVTable = pConfig->ppCustomBackendVTables[ivtable]; - if (pVTable != NULL) { - result = ma_decoder_init_from_file__internal(pVTable, pConfig->pCustomBackendUserData, pFilePath, pConfig, pDecoder); - if (result == MA_SUCCESS) { - return MA_SUCCESS; - } - } else { - /* No vtable. */ - } - } - - /* Getting here means we couldn't find a backend. */ - return MA_NO_BACKEND; -} - -static ma_result ma_decoder_init_custom_from_file_w__internal(const wchar_t* pFilePath, const ma_decoder_config* pConfig, ma_decoder* pDecoder) -{ - ma_result result = MA_NO_BACKEND; - size_t ivtable; - - MA_ASSERT(pConfig != NULL); - MA_ASSERT(pDecoder != NULL); - - if (pConfig->ppCustomBackendVTables == NULL) { - return MA_NO_BACKEND; - } - - /* The order each backend is listed is what defines the priority. */ - for (ivtable = 0; ivtable < pConfig->customBackendCount; ivtable += 1) { - const ma_decoding_backend_vtable* pVTable = pConfig->ppCustomBackendVTables[ivtable]; - if (pVTable != NULL) { - result = ma_decoder_init_from_file_w__internal(pVTable, pConfig->pCustomBackendUserData, pFilePath, pConfig, pDecoder); - if (result == MA_SUCCESS) { - return MA_SUCCESS; - } - } else { - /* No vtable. */ - } - } - - /* Getting here means we couldn't find a backend. */ - return MA_NO_BACKEND; -} - -static ma_result ma_decoder_init_custom_from_memory__internal(const void* pData, size_t dataSize, const ma_decoder_config* pConfig, ma_decoder* pDecoder) -{ - ma_result result = MA_NO_BACKEND; - size_t ivtable; - - MA_ASSERT(pConfig != NULL); - MA_ASSERT(pDecoder != NULL); - - if (pConfig->ppCustomBackendVTables == NULL) { - return MA_NO_BACKEND; - } - - /* The order each backend is listed is what defines the priority. */ - for (ivtable = 0; ivtable < pConfig->customBackendCount; ivtable += 1) { - const ma_decoding_backend_vtable* pVTable = pConfig->ppCustomBackendVTables[ivtable]; - if (pVTable != NULL) { - result = ma_decoder_init_from_memory__internal(pVTable, pConfig->pCustomBackendUserData, pData, dataSize, pConfig, pDecoder); - if (result == MA_SUCCESS) { - return MA_SUCCESS; - } - } else { - /* No vtable. */ - } - } - - /* Getting here means we couldn't find a backend. */ - return MA_NO_BACKEND; -} - - -/* WAV */ -#ifdef ma_dr_wav_h -#define MA_HAS_WAV - -typedef struct -{ - ma_data_source_base ds; - ma_read_proc onRead; - ma_seek_proc onSeek; - ma_tell_proc onTell; - void* pReadSeekTellUserData; - ma_format format; /* Can be f32, s16 or s32. */ -#if !defined(MA_NO_WAV) - ma_dr_wav dr; -#endif -} ma_wav; - -MA_API ma_result ma_wav_init(ma_read_proc onRead, ma_seek_proc onSeek, ma_tell_proc onTell, void* pReadSeekTellUserData, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_wav* pWav); -MA_API ma_result ma_wav_init_file(const char* pFilePath, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_wav* pWav); -MA_API ma_result ma_wav_init_file_w(const wchar_t* pFilePath, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_wav* pWav); -MA_API ma_result ma_wav_init_memory(const void* pData, size_t dataSize, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_wav* pWav); -MA_API void ma_wav_uninit(ma_wav* pWav, const ma_allocation_callbacks* pAllocationCallbacks); -MA_API ma_result ma_wav_read_pcm_frames(ma_wav* pWav, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead); -MA_API ma_result ma_wav_seek_to_pcm_frame(ma_wav* pWav, ma_uint64 frameIndex); -MA_API ma_result ma_wav_get_data_format(ma_wav* pWav, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate, ma_channel* pChannelMap, size_t channelMapCap); -MA_API ma_result ma_wav_get_cursor_in_pcm_frames(ma_wav* pWav, ma_uint64* pCursor); -MA_API ma_result ma_wav_get_length_in_pcm_frames(ma_wav* pWav, ma_uint64* pLength); - - -static ma_result ma_wav_ds_read(ma_data_source* pDataSource, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead) -{ - return ma_wav_read_pcm_frames((ma_wav*)pDataSource, pFramesOut, frameCount, pFramesRead); -} - -static ma_result ma_wav_ds_seek(ma_data_source* pDataSource, ma_uint64 frameIndex) -{ - return ma_wav_seek_to_pcm_frame((ma_wav*)pDataSource, frameIndex); -} - -static ma_result ma_wav_ds_get_data_format(ma_data_source* pDataSource, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate, ma_channel* pChannelMap, size_t channelMapCap) -{ - return ma_wav_get_data_format((ma_wav*)pDataSource, pFormat, pChannels, pSampleRate, pChannelMap, channelMapCap); -} - -static ma_result ma_wav_ds_get_cursor(ma_data_source* pDataSource, ma_uint64* pCursor) -{ - return ma_wav_get_cursor_in_pcm_frames((ma_wav*)pDataSource, pCursor); -} - -static ma_result ma_wav_ds_get_length(ma_data_source* pDataSource, ma_uint64* pLength) -{ - return ma_wav_get_length_in_pcm_frames((ma_wav*)pDataSource, pLength); -} - -static ma_data_source_vtable g_ma_wav_ds_vtable = -{ - ma_wav_ds_read, - ma_wav_ds_seek, - ma_wav_ds_get_data_format, - ma_wav_ds_get_cursor, - ma_wav_ds_get_length, - NULL, /* onSetLooping */ - 0 -}; - - -#if !defined(MA_NO_WAV) -static size_t ma_wav_dr_callback__read(void* pUserData, void* pBufferOut, size_t bytesToRead) -{ - ma_wav* pWav = (ma_wav*)pUserData; - ma_result result; - size_t bytesRead; - - MA_ASSERT(pWav != NULL); - - result = pWav->onRead(pWav->pReadSeekTellUserData, pBufferOut, bytesToRead, &bytesRead); - (void)result; - - return bytesRead; -} - -static ma_bool32 ma_wav_dr_callback__seek(void* pUserData, int offset, ma_dr_wav_seek_origin origin) -{ - ma_wav* pWav = (ma_wav*)pUserData; - ma_result result; - ma_seek_origin maSeekOrigin; - - MA_ASSERT(pWav != NULL); - - maSeekOrigin = ma_seek_origin_start; - if (origin == ma_dr_wav_seek_origin_current) { - maSeekOrigin = ma_seek_origin_current; - } - - result = pWav->onSeek(pWav->pReadSeekTellUserData, offset, maSeekOrigin); - if (result != MA_SUCCESS) { - return MA_FALSE; - } - - return MA_TRUE; -} -#endif - -static ma_result ma_wav_init_internal(const ma_decoding_backend_config* pConfig, ma_wav* pWav) -{ - ma_result result; - ma_data_source_config dataSourceConfig; - - if (pWav == NULL) { - return MA_INVALID_ARGS; - } - - MA_ZERO_OBJECT(pWav); - pWav->format = ma_format_unknown; /* Use closest match to source file by default. */ - - if (pConfig != NULL && (pConfig->preferredFormat == ma_format_f32 || pConfig->preferredFormat == ma_format_s16 || pConfig->preferredFormat == ma_format_s32)) { - pWav->format = pConfig->preferredFormat; - } else { - /* Getting here means something other than f32 and s16 was specified. Just leave this unset to use the default format. */ - } - - dataSourceConfig = ma_data_source_config_init(); - dataSourceConfig.vtable = &g_ma_wav_ds_vtable; - - result = ma_data_source_init(&dataSourceConfig, &pWav->ds); - if (result != MA_SUCCESS) { - return result; /* Failed to initialize the base data source. */ - } - - return MA_SUCCESS; -} - -static ma_result ma_wav_post_init(ma_wav* pWav) -{ - /* - If an explicit format was not specified, try picking the closest match based on the internal - format. The format needs to be supported by miniaudio. - */ - if (pWav->format == ma_format_unknown) { - switch (pWav->dr.translatedFormatTag) - { - case MA_DR_WAVE_FORMAT_PCM: - { - if (pWav->dr.bitsPerSample == 8) { - pWav->format = ma_format_u8; - } else if (pWav->dr.bitsPerSample == 16) { - pWav->format = ma_format_s16; - } else if (pWav->dr.bitsPerSample == 24) { - pWav->format = ma_format_s24; - } else if (pWav->dr.bitsPerSample == 32) { - pWav->format = ma_format_s32; - } - } break; - - case MA_DR_WAVE_FORMAT_IEEE_FLOAT: - { - if (pWav->dr.bitsPerSample == 32) { - pWav->format = ma_format_f32; - } - } break; - - default: break; - } - - /* Fall back to f32 if we couldn't find anything. */ - if (pWav->format == ma_format_unknown) { - pWav->format = ma_format_f32; - } - } - - return MA_SUCCESS; -} - -MA_API ma_result ma_wav_init(ma_read_proc onRead, ma_seek_proc onSeek, ma_tell_proc onTell, void* pReadSeekTellUserData, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_wav* pWav) -{ - ma_result result; - - result = ma_wav_init_internal(pConfig, pWav); - if (result != MA_SUCCESS) { - return result; - } - - if (onRead == NULL || onSeek == NULL) { - return MA_INVALID_ARGS; /* onRead and onSeek are mandatory. */ - } - - pWav->onRead = onRead; - pWav->onSeek = onSeek; - pWav->onTell = onTell; - pWav->pReadSeekTellUserData = pReadSeekTellUserData; - - #if !defined(MA_NO_WAV) - { - ma_bool32 wavResult; - - wavResult = ma_dr_wav_init(&pWav->dr, ma_wav_dr_callback__read, ma_wav_dr_callback__seek, pWav, pAllocationCallbacks); - if (wavResult != MA_TRUE) { - return MA_INVALID_FILE; - } - - ma_wav_post_init(pWav); - - return MA_SUCCESS; - } - #else - { - /* wav is disabled. */ - (void)pAllocationCallbacks; - return MA_NOT_IMPLEMENTED; - } - #endif -} - -MA_API ma_result ma_wav_init_file(const char* pFilePath, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_wav* pWav) -{ - ma_result result; - - result = ma_wav_init_internal(pConfig, pWav); - if (result != MA_SUCCESS) { - return result; - } - - #if !defined(MA_NO_WAV) - { - ma_bool32 wavResult; - - wavResult = ma_dr_wav_init_file(&pWav->dr, pFilePath, pAllocationCallbacks); - if (wavResult != MA_TRUE) { - return MA_INVALID_FILE; - } - - ma_wav_post_init(pWav); - - return MA_SUCCESS; - } - #else - { - /* wav is disabled. */ - (void)pFilePath; - (void)pAllocationCallbacks; - return MA_NOT_IMPLEMENTED; - } - #endif -} - -MA_API ma_result ma_wav_init_file_w(const wchar_t* pFilePath, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_wav* pWav) -{ - ma_result result; - - result = ma_wav_init_internal(pConfig, pWav); - if (result != MA_SUCCESS) { - return result; - } - - #if !defined(MA_NO_WAV) - { - ma_bool32 wavResult; - - wavResult = ma_dr_wav_init_file_w(&pWav->dr, pFilePath, pAllocationCallbacks); - if (wavResult != MA_TRUE) { - return MA_INVALID_FILE; - } - - ma_wav_post_init(pWav); - - return MA_SUCCESS; - } - #else - { - /* wav is disabled. */ - (void)pFilePath; - (void)pAllocationCallbacks; - return MA_NOT_IMPLEMENTED; - } - #endif -} - -MA_API ma_result ma_wav_init_memory(const void* pData, size_t dataSize, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_wav* pWav) -{ - ma_result result; - - result = ma_wav_init_internal(pConfig, pWav); - if (result != MA_SUCCESS) { - return result; - } - - #if !defined(MA_NO_WAV) - { - ma_bool32 wavResult; - - wavResult = ma_dr_wav_init_memory(&pWav->dr, pData, dataSize, pAllocationCallbacks); - if (wavResult != MA_TRUE) { - return MA_INVALID_FILE; - } - - ma_wav_post_init(pWav); - - return MA_SUCCESS; - } - #else - { - /* wav is disabled. */ - (void)pData; - (void)dataSize; - (void)pAllocationCallbacks; - return MA_NOT_IMPLEMENTED; - } - #endif -} - -MA_API void ma_wav_uninit(ma_wav* pWav, const ma_allocation_callbacks* pAllocationCallbacks) -{ - if (pWav == NULL) { - return; - } - - (void)pAllocationCallbacks; - - #if !defined(MA_NO_WAV) - { - ma_dr_wav_uninit(&pWav->dr); - } - #else - { - /* wav is disabled. Should never hit this since initialization would have failed. */ - MA_ASSERT(MA_FALSE); - } - #endif - - ma_data_source_uninit(&pWav->ds); -} - -MA_API ma_result ma_wav_read_pcm_frames(ma_wav* pWav, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead) -{ - if (pFramesRead != NULL) { - *pFramesRead = 0; - } - - if (frameCount == 0) { - return MA_INVALID_ARGS; - } - - if (pWav == NULL) { - return MA_INVALID_ARGS; - } - - #if !defined(MA_NO_WAV) - { - /* We always use floating point format. */ - ma_result result = MA_SUCCESS; /* Must be initialized to MA_SUCCESS. */ - ma_uint64 totalFramesRead = 0; - ma_format format; - - ma_wav_get_data_format(pWav, &format, NULL, NULL, NULL, 0); - - switch (format) - { - case ma_format_f32: - { - totalFramesRead = ma_dr_wav_read_pcm_frames_f32(&pWav->dr, frameCount, (float*)pFramesOut); - } break; - - case ma_format_s16: - { - totalFramesRead = ma_dr_wav_read_pcm_frames_s16(&pWav->dr, frameCount, (ma_int16*)pFramesOut); - } break; - - case ma_format_s32: - { - totalFramesRead = ma_dr_wav_read_pcm_frames_s32(&pWav->dr, frameCount, (ma_int32*)pFramesOut); - } break; - - /* Fallback to a raw read. */ - case ma_format_unknown: return MA_INVALID_OPERATION; /* <-- this should never be hit because initialization would just fall back to a supported format. */ - default: - { - totalFramesRead = ma_dr_wav_read_pcm_frames(&pWav->dr, frameCount, pFramesOut); - } break; - } - - /* In the future we'll update ma_dr_wav to return MA_AT_END for us. */ - if (totalFramesRead == 0) { - result = MA_AT_END; - } - - if (pFramesRead != NULL) { - *pFramesRead = totalFramesRead; - } - - if (result == MA_SUCCESS && totalFramesRead == 0) { - result = MA_AT_END; - } - - return result; - } - #else - { - /* wav is disabled. Should never hit this since initialization would have failed. */ - MA_ASSERT(MA_FALSE); - - (void)pFramesOut; - (void)frameCount; - (void)pFramesRead; - - return MA_NOT_IMPLEMENTED; - } - #endif -} - -MA_API ma_result ma_wav_seek_to_pcm_frame(ma_wav* pWav, ma_uint64 frameIndex) -{ - if (pWav == NULL) { - return MA_INVALID_ARGS; - } - - #if !defined(MA_NO_WAV) - { - ma_bool32 wavResult; - - wavResult = ma_dr_wav_seek_to_pcm_frame(&pWav->dr, frameIndex); - if (wavResult != MA_TRUE) { - return MA_ERROR; - } - - return MA_SUCCESS; - } - #else - { - /* wav is disabled. Should never hit this since initialization would have failed. */ - MA_ASSERT(MA_FALSE); - - (void)frameIndex; - - return MA_NOT_IMPLEMENTED; - } - #endif -} - -MA_API ma_result ma_wav_get_data_format(ma_wav* pWav, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate, ma_channel* pChannelMap, size_t channelMapCap) -{ - /* Defaults for safety. */ - if (pFormat != NULL) { - *pFormat = ma_format_unknown; - } - if (pChannels != NULL) { - *pChannels = 0; - } - if (pSampleRate != NULL) { - *pSampleRate = 0; - } - if (pChannelMap != NULL) { - MA_ZERO_MEMORY(pChannelMap, sizeof(*pChannelMap) * channelMapCap); - } - - if (pWav == NULL) { - return MA_INVALID_OPERATION; - } - - if (pFormat != NULL) { - *pFormat = pWav->format; - } - - #if !defined(MA_NO_WAV) - { - if (pChannels != NULL) { - *pChannels = pWav->dr.channels; - } - - if (pSampleRate != NULL) { - *pSampleRate = pWav->dr.sampleRate; - } - - if (pChannelMap != NULL) { - ma_channel_map_init_standard(ma_standard_channel_map_microsoft, pChannelMap, channelMapCap, pWav->dr.channels); - } - - return MA_SUCCESS; - } - #else - { - /* wav is disabled. Should never hit this since initialization would have failed. */ - MA_ASSERT(MA_FALSE); - return MA_NOT_IMPLEMENTED; - } - #endif -} - -MA_API ma_result ma_wav_get_cursor_in_pcm_frames(ma_wav* pWav, ma_uint64* pCursor) -{ - if (pCursor == NULL) { - return MA_INVALID_ARGS; - } - - *pCursor = 0; /* Safety. */ - - if (pWav == NULL) { - return MA_INVALID_ARGS; - } - - #if !defined(MA_NO_WAV) - { - ma_result wavResult = ma_dr_wav_get_cursor_in_pcm_frames(&pWav->dr, pCursor); - if (wavResult != MA_SUCCESS) { - return (ma_result)wavResult; /* ma_dr_wav result codes map to miniaudio's. */ - } - - return MA_SUCCESS; - } - #else - { - /* wav is disabled. Should never hit this since initialization would have failed. */ - MA_ASSERT(MA_FALSE); - return MA_NOT_IMPLEMENTED; - } - #endif -} - -MA_API ma_result ma_wav_get_length_in_pcm_frames(ma_wav* pWav, ma_uint64* pLength) -{ - if (pLength == NULL) { - return MA_INVALID_ARGS; - } - - *pLength = 0; /* Safety. */ - - if (pWav == NULL) { - return MA_INVALID_ARGS; - } - - #if !defined(MA_NO_WAV) - { - ma_result wavResult = ma_dr_wav_get_length_in_pcm_frames(&pWav->dr, pLength); - if (wavResult != MA_SUCCESS) { - return (ma_result)wavResult; /* ma_dr_wav result codes map to miniaudio's. */ - } - - return MA_SUCCESS; - } - #else - { - /* wav is disabled. Should never hit this since initialization would have failed. */ - MA_ASSERT(MA_FALSE); - return MA_NOT_IMPLEMENTED; - } - #endif -} - - -static ma_result ma_decoding_backend_init__wav(void* pUserData, ma_read_proc onRead, ma_seek_proc onSeek, ma_tell_proc onTell, void* pReadSeekTellUserData, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_data_source** ppBackend) -{ - ma_result result; - ma_wav* pWav; - - (void)pUserData; /* For now not using pUserData, but once we start storing the vorbis decoder state within the ma_decoder structure this will be set to the decoder so we can avoid a malloc. */ - - /* For now we're just allocating the decoder backend on the heap. */ - pWav = (ma_wav*)ma_malloc(sizeof(*pWav), pAllocationCallbacks); - if (pWav == NULL) { - return MA_OUT_OF_MEMORY; - } - - result = ma_wav_init(onRead, onSeek, onTell, pReadSeekTellUserData, pConfig, pAllocationCallbacks, pWav); - if (result != MA_SUCCESS) { - ma_free(pWav, pAllocationCallbacks); - return result; - } - - *ppBackend = pWav; - - return MA_SUCCESS; -} - -static ma_result ma_decoding_backend_init_file__wav(void* pUserData, const char* pFilePath, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_data_source** ppBackend) -{ - ma_result result; - ma_wav* pWav; - - (void)pUserData; /* For now not using pUserData, but once we start storing the vorbis decoder state within the ma_decoder structure this will be set to the decoder so we can avoid a malloc. */ - - /* For now we're just allocating the decoder backend on the heap. */ - pWav = (ma_wav*)ma_malloc(sizeof(*pWav), pAllocationCallbacks); - if (pWav == NULL) { - return MA_OUT_OF_MEMORY; - } - - result = ma_wav_init_file(pFilePath, pConfig, pAllocationCallbacks, pWav); - if (result != MA_SUCCESS) { - ma_free(pWav, pAllocationCallbacks); - return result; - } - - *ppBackend = pWav; - - return MA_SUCCESS; -} - -static ma_result ma_decoding_backend_init_file_w__wav(void* pUserData, const wchar_t* pFilePath, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_data_source** ppBackend) -{ - ma_result result; - ma_wav* pWav; - - (void)pUserData; /* For now not using pUserData, but once we start storing the vorbis decoder state within the ma_decoder structure this will be set to the decoder so we can avoid a malloc. */ - - /* For now we're just allocating the decoder backend on the heap. */ - pWav = (ma_wav*)ma_malloc(sizeof(*pWav), pAllocationCallbacks); - if (pWav == NULL) { - return MA_OUT_OF_MEMORY; - } - - result = ma_wav_init_file_w(pFilePath, pConfig, pAllocationCallbacks, pWav); - if (result != MA_SUCCESS) { - ma_free(pWav, pAllocationCallbacks); - return result; - } - - *ppBackend = pWav; - - return MA_SUCCESS; -} - -static ma_result ma_decoding_backend_init_memory__wav(void* pUserData, const void* pData, size_t dataSize, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_data_source** ppBackend) -{ - ma_result result; - ma_wav* pWav; - - (void)pUserData; /* For now not using pUserData, but once we start storing the vorbis decoder state within the ma_decoder structure this will be set to the decoder so we can avoid a malloc. */ - - /* For now we're just allocating the decoder backend on the heap. */ - pWav = (ma_wav*)ma_malloc(sizeof(*pWav), pAllocationCallbacks); - if (pWav == NULL) { - return MA_OUT_OF_MEMORY; - } - - result = ma_wav_init_memory(pData, dataSize, pConfig, pAllocationCallbacks, pWav); - if (result != MA_SUCCESS) { - ma_free(pWav, pAllocationCallbacks); - return result; - } - - *ppBackend = pWav; - - return MA_SUCCESS; -} - -static void ma_decoding_backend_uninit__wav(void* pUserData, ma_data_source* pBackend, const ma_allocation_callbacks* pAllocationCallbacks) -{ - ma_wav* pWav = (ma_wav*)pBackend; - - (void)pUserData; - - ma_wav_uninit(pWav, pAllocationCallbacks); - ma_free(pWav, pAllocationCallbacks); -} - -static ma_decoding_backend_vtable g_ma_decoding_backend_vtable_wav = -{ - ma_decoding_backend_init__wav, - ma_decoding_backend_init_file__wav, - ma_decoding_backend_init_file_w__wav, - ma_decoding_backend_init_memory__wav, - ma_decoding_backend_uninit__wav -}; - -static ma_result ma_decoder_init_wav__internal(const ma_decoder_config* pConfig, ma_decoder* pDecoder) -{ - return ma_decoder_init_from_vtable__internal(&g_ma_decoding_backend_vtable_wav, NULL, pConfig, pDecoder); -} - -static ma_result ma_decoder_init_wav_from_file__internal(const char* pFilePath, const ma_decoder_config* pConfig, ma_decoder* pDecoder) -{ - return ma_decoder_init_from_file__internal(&g_ma_decoding_backend_vtable_wav, NULL, pFilePath, pConfig, pDecoder); -} - -static ma_result ma_decoder_init_wav_from_file_w__internal(const wchar_t* pFilePath, const ma_decoder_config* pConfig, ma_decoder* pDecoder) -{ - return ma_decoder_init_from_file_w__internal(&g_ma_decoding_backend_vtable_wav, NULL, pFilePath, pConfig, pDecoder); -} - -static ma_result ma_decoder_init_wav_from_memory__internal(const void* pData, size_t dataSize, const ma_decoder_config* pConfig, ma_decoder* pDecoder) -{ - return ma_decoder_init_from_memory__internal(&g_ma_decoding_backend_vtable_wav, NULL, pData, dataSize, pConfig, pDecoder); -} -#endif /* ma_dr_wav_h */ - -/* FLAC */ -#ifdef ma_dr_flac_h -#define MA_HAS_FLAC - -typedef struct -{ - ma_data_source_base ds; - ma_read_proc onRead; - ma_seek_proc onSeek; - ma_tell_proc onTell; - void* pReadSeekTellUserData; - ma_format format; /* Can be f32, s16 or s32. */ -#if !defined(MA_NO_FLAC) - ma_dr_flac* dr; -#endif -} ma_flac; - -MA_API ma_result ma_flac_init(ma_read_proc onRead, ma_seek_proc onSeek, ma_tell_proc onTell, void* pReadSeekTellUserData, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_flac* pFlac); -MA_API ma_result ma_flac_init_file(const char* pFilePath, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_flac* pFlac); -MA_API ma_result ma_flac_init_file_w(const wchar_t* pFilePath, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_flac* pFlac); -MA_API ma_result ma_flac_init_memory(const void* pData, size_t dataSize, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_flac* pFlac); -MA_API void ma_flac_uninit(ma_flac* pFlac, const ma_allocation_callbacks* pAllocationCallbacks); -MA_API ma_result ma_flac_read_pcm_frames(ma_flac* pFlac, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead); -MA_API ma_result ma_flac_seek_to_pcm_frame(ma_flac* pFlac, ma_uint64 frameIndex); -MA_API ma_result ma_flac_get_data_format(ma_flac* pFlac, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate, ma_channel* pChannelMap, size_t channelMapCap); -MA_API ma_result ma_flac_get_cursor_in_pcm_frames(ma_flac* pFlac, ma_uint64* pCursor); -MA_API ma_result ma_flac_get_length_in_pcm_frames(ma_flac* pFlac, ma_uint64* pLength); - - -static ma_result ma_flac_ds_read(ma_data_source* pDataSource, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead) -{ - return ma_flac_read_pcm_frames((ma_flac*)pDataSource, pFramesOut, frameCount, pFramesRead); -} - -static ma_result ma_flac_ds_seek(ma_data_source* pDataSource, ma_uint64 frameIndex) -{ - return ma_flac_seek_to_pcm_frame((ma_flac*)pDataSource, frameIndex); -} - -static ma_result ma_flac_ds_get_data_format(ma_data_source* pDataSource, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate, ma_channel* pChannelMap, size_t channelMapCap) -{ - return ma_flac_get_data_format((ma_flac*)pDataSource, pFormat, pChannels, pSampleRate, pChannelMap, channelMapCap); -} - -static ma_result ma_flac_ds_get_cursor(ma_data_source* pDataSource, ma_uint64* pCursor) -{ - return ma_flac_get_cursor_in_pcm_frames((ma_flac*)pDataSource, pCursor); -} - -static ma_result ma_flac_ds_get_length(ma_data_source* pDataSource, ma_uint64* pLength) -{ - return ma_flac_get_length_in_pcm_frames((ma_flac*)pDataSource, pLength); -} - -static ma_data_source_vtable g_ma_flac_ds_vtable = -{ - ma_flac_ds_read, - ma_flac_ds_seek, - ma_flac_ds_get_data_format, - ma_flac_ds_get_cursor, - ma_flac_ds_get_length, - NULL, /* onSetLooping */ - 0 -}; - - -#if !defined(MA_NO_FLAC) -static size_t ma_flac_dr_callback__read(void* pUserData, void* pBufferOut, size_t bytesToRead) -{ - ma_flac* pFlac = (ma_flac*)pUserData; - ma_result result; - size_t bytesRead; - - MA_ASSERT(pFlac != NULL); - - result = pFlac->onRead(pFlac->pReadSeekTellUserData, pBufferOut, bytesToRead, &bytesRead); - (void)result; - - return bytesRead; -} - -static ma_bool32 ma_flac_dr_callback__seek(void* pUserData, int offset, ma_dr_flac_seek_origin origin) -{ - ma_flac* pFlac = (ma_flac*)pUserData; - ma_result result; - ma_seek_origin maSeekOrigin; - - MA_ASSERT(pFlac != NULL); - - maSeekOrigin = ma_seek_origin_start; - if (origin == ma_dr_flac_seek_origin_current) { - maSeekOrigin = ma_seek_origin_current; - } - - result = pFlac->onSeek(pFlac->pReadSeekTellUserData, offset, maSeekOrigin); - if (result != MA_SUCCESS) { - return MA_FALSE; - } - - return MA_TRUE; -} -#endif - -static ma_result ma_flac_init_internal(const ma_decoding_backend_config* pConfig, ma_flac* pFlac) -{ - ma_result result; - ma_data_source_config dataSourceConfig; - - if (pFlac == NULL) { - return MA_INVALID_ARGS; - } - - MA_ZERO_OBJECT(pFlac); - pFlac->format = ma_format_f32; /* f32 by default. */ - - if (pConfig != NULL && (pConfig->preferredFormat == ma_format_f32 || pConfig->preferredFormat == ma_format_s16 || pConfig->preferredFormat == ma_format_s32)) { - pFlac->format = pConfig->preferredFormat; - } else { - /* Getting here means something other than f32 and s16 was specified. Just leave this unset to use the default format. */ - } - - dataSourceConfig = ma_data_source_config_init(); - dataSourceConfig.vtable = &g_ma_flac_ds_vtable; - - result = ma_data_source_init(&dataSourceConfig, &pFlac->ds); - if (result != MA_SUCCESS) { - return result; /* Failed to initialize the base data source. */ - } - - return MA_SUCCESS; -} - -MA_API ma_result ma_flac_init(ma_read_proc onRead, ma_seek_proc onSeek, ma_tell_proc onTell, void* pReadSeekTellUserData, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_flac* pFlac) -{ - ma_result result; - - result = ma_flac_init_internal(pConfig, pFlac); - if (result != MA_SUCCESS) { - return result; - } - - if (onRead == NULL || onSeek == NULL) { - return MA_INVALID_ARGS; /* onRead and onSeek are mandatory. */ - } - - pFlac->onRead = onRead; - pFlac->onSeek = onSeek; - pFlac->onTell = onTell; - pFlac->pReadSeekTellUserData = pReadSeekTellUserData; - - #if !defined(MA_NO_FLAC) - { - pFlac->dr = ma_dr_flac_open(ma_flac_dr_callback__read, ma_flac_dr_callback__seek, pFlac, pAllocationCallbacks); - if (pFlac->dr == NULL) { - return MA_INVALID_FILE; - } - - return MA_SUCCESS; - } - #else - { - /* flac is disabled. */ - (void)pAllocationCallbacks; - return MA_NOT_IMPLEMENTED; - } - #endif -} - -MA_API ma_result ma_flac_init_file(const char* pFilePath, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_flac* pFlac) -{ - ma_result result; - - result = ma_flac_init_internal(pConfig, pFlac); - if (result != MA_SUCCESS) { - return result; - } - - #if !defined(MA_NO_FLAC) - { - pFlac->dr = ma_dr_flac_open_file(pFilePath, pAllocationCallbacks); - if (pFlac->dr == NULL) { - return MA_INVALID_FILE; - } - - return MA_SUCCESS; - } - #else - { - /* flac is disabled. */ - (void)pFilePath; - (void)pAllocationCallbacks; - return MA_NOT_IMPLEMENTED; - } - #endif -} - -MA_API ma_result ma_flac_init_file_w(const wchar_t* pFilePath, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_flac* pFlac) -{ - ma_result result; - - result = ma_flac_init_internal(pConfig, pFlac); - if (result != MA_SUCCESS) { - return result; - } - - #if !defined(MA_NO_FLAC) - { - pFlac->dr = ma_dr_flac_open_file_w(pFilePath, pAllocationCallbacks); - if (pFlac->dr == NULL) { - return MA_INVALID_FILE; - } - - return MA_SUCCESS; - } - #else - { - /* flac is disabled. */ - (void)pFilePath; - (void)pAllocationCallbacks; - return MA_NOT_IMPLEMENTED; - } - #endif -} - -MA_API ma_result ma_flac_init_memory(const void* pData, size_t dataSize, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_flac* pFlac) -{ - ma_result result; - - result = ma_flac_init_internal(pConfig, pFlac); - if (result != MA_SUCCESS) { - return result; - } - - #if !defined(MA_NO_FLAC) - { - pFlac->dr = ma_dr_flac_open_memory(pData, dataSize, pAllocationCallbacks); - if (pFlac->dr == NULL) { - return MA_INVALID_FILE; - } - - return MA_SUCCESS; - } - #else - { - /* flac is disabled. */ - (void)pData; - (void)dataSize; - (void)pAllocationCallbacks; - return MA_NOT_IMPLEMENTED; - } - #endif -} - -MA_API void ma_flac_uninit(ma_flac* pFlac, const ma_allocation_callbacks* pAllocationCallbacks) -{ - if (pFlac == NULL) { - return; - } - - (void)pAllocationCallbacks; - - #if !defined(MA_NO_FLAC) - { - ma_dr_flac_close(pFlac->dr); - } - #else - { - /* flac is disabled. Should never hit this since initialization would have failed. */ - MA_ASSERT(MA_FALSE); - } - #endif - - ma_data_source_uninit(&pFlac->ds); -} - -MA_API ma_result ma_flac_read_pcm_frames(ma_flac* pFlac, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead) -{ - if (pFramesRead != NULL) { - *pFramesRead = 0; - } - - if (frameCount == 0) { - return MA_INVALID_ARGS; - } - - if (pFlac == NULL) { - return MA_INVALID_ARGS; - } - - #if !defined(MA_NO_FLAC) - { - /* We always use floating point format. */ - ma_result result = MA_SUCCESS; /* Must be initialized to MA_SUCCESS. */ - ma_uint64 totalFramesRead = 0; - ma_format format; - - ma_flac_get_data_format(pFlac, &format, NULL, NULL, NULL, 0); - - switch (format) - { - case ma_format_f32: - { - totalFramesRead = ma_dr_flac_read_pcm_frames_f32(pFlac->dr, frameCount, (float*)pFramesOut); - } break; - - case ma_format_s16: - { - totalFramesRead = ma_dr_flac_read_pcm_frames_s16(pFlac->dr, frameCount, (ma_int16*)pFramesOut); - } break; - - case ma_format_s32: - { - totalFramesRead = ma_dr_flac_read_pcm_frames_s32(pFlac->dr, frameCount, (ma_int32*)pFramesOut); - } break; - - case ma_format_u8: - case ma_format_s24: - case ma_format_unknown: - default: - { - return MA_INVALID_OPERATION; - }; - } - - /* In the future we'll update ma_dr_flac to return MA_AT_END for us. */ - if (totalFramesRead == 0) { - result = MA_AT_END; - } - - if (pFramesRead != NULL) { - *pFramesRead = totalFramesRead; - } - - if (result == MA_SUCCESS && totalFramesRead == 0) { - result = MA_AT_END; - } - - return result; - } - #else - { - /* flac is disabled. Should never hit this since initialization would have failed. */ - MA_ASSERT(MA_FALSE); - - (void)pFramesOut; - (void)frameCount; - (void)pFramesRead; - - return MA_NOT_IMPLEMENTED; - } - #endif -} - -MA_API ma_result ma_flac_seek_to_pcm_frame(ma_flac* pFlac, ma_uint64 frameIndex) -{ - if (pFlac == NULL) { - return MA_INVALID_ARGS; - } - - #if !defined(MA_NO_FLAC) - { - ma_bool32 flacResult; - - flacResult = ma_dr_flac_seek_to_pcm_frame(pFlac->dr, frameIndex); - if (flacResult != MA_TRUE) { - return MA_ERROR; - } - - return MA_SUCCESS; - } - #else - { - /* flac is disabled. Should never hit this since initialization would have failed. */ - MA_ASSERT(MA_FALSE); - - (void)frameIndex; - - return MA_NOT_IMPLEMENTED; - } - #endif -} - -MA_API ma_result ma_flac_get_data_format(ma_flac* pFlac, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate, ma_channel* pChannelMap, size_t channelMapCap) -{ - /* Defaults for safety. */ - if (pFormat != NULL) { - *pFormat = ma_format_unknown; - } - if (pChannels != NULL) { - *pChannels = 0; - } - if (pSampleRate != NULL) { - *pSampleRate = 0; - } - if (pChannelMap != NULL) { - MA_ZERO_MEMORY(pChannelMap, sizeof(*pChannelMap) * channelMapCap); - } - - if (pFlac == NULL) { - return MA_INVALID_OPERATION; - } - - if (pFormat != NULL) { - *pFormat = pFlac->format; - } - - #if !defined(MA_NO_FLAC) - { - if (pChannels != NULL) { - *pChannels = pFlac->dr->channels; - } - - if (pSampleRate != NULL) { - *pSampleRate = pFlac->dr->sampleRate; - } - - if (pChannelMap != NULL) { - ma_channel_map_init_standard(ma_standard_channel_map_microsoft, pChannelMap, channelMapCap, pFlac->dr->channels); - } - - return MA_SUCCESS; - } - #else - { - /* flac is disabled. Should never hit this since initialization would have failed. */ - MA_ASSERT(MA_FALSE); - return MA_NOT_IMPLEMENTED; - } - #endif -} - -MA_API ma_result ma_flac_get_cursor_in_pcm_frames(ma_flac* pFlac, ma_uint64* pCursor) -{ - if (pCursor == NULL) { - return MA_INVALID_ARGS; - } - - *pCursor = 0; /* Safety. */ - - if (pFlac == NULL) { - return MA_INVALID_ARGS; - } - - #if !defined(MA_NO_FLAC) - { - *pCursor = pFlac->dr->currentPCMFrame; - - return MA_SUCCESS; - } - #else - { - /* flac is disabled. Should never hit this since initialization would have failed. */ - MA_ASSERT(MA_FALSE); - return MA_NOT_IMPLEMENTED; - } - #endif -} - -MA_API ma_result ma_flac_get_length_in_pcm_frames(ma_flac* pFlac, ma_uint64* pLength) -{ - if (pLength == NULL) { - return MA_INVALID_ARGS; - } - - *pLength = 0; /* Safety. */ - - if (pFlac == NULL) { - return MA_INVALID_ARGS; - } - - #if !defined(MA_NO_FLAC) - { - *pLength = pFlac->dr->totalPCMFrameCount; - - return MA_SUCCESS; - } - #else - { - /* flac is disabled. Should never hit this since initialization would have failed. */ - MA_ASSERT(MA_FALSE); - return MA_NOT_IMPLEMENTED; - } - #endif -} - - -static ma_result ma_decoding_backend_init__flac(void* pUserData, ma_read_proc onRead, ma_seek_proc onSeek, ma_tell_proc onTell, void* pReadSeekTellUserData, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_data_source** ppBackend) -{ - ma_result result; - ma_flac* pFlac; - - (void)pUserData; /* For now not using pUserData, but once we start storing the vorbis decoder state within the ma_decoder structure this will be set to the decoder so we can avoid a malloc. */ - - /* For now we're just allocating the decoder backend on the heap. */ - pFlac = (ma_flac*)ma_malloc(sizeof(*pFlac), pAllocationCallbacks); - if (pFlac == NULL) { - return MA_OUT_OF_MEMORY; - } - - result = ma_flac_init(onRead, onSeek, onTell, pReadSeekTellUserData, pConfig, pAllocationCallbacks, pFlac); - if (result != MA_SUCCESS) { - ma_free(pFlac, pAllocationCallbacks); - return result; - } - - *ppBackend = pFlac; - - return MA_SUCCESS; -} - -static ma_result ma_decoding_backend_init_file__flac(void* pUserData, const char* pFilePath, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_data_source** ppBackend) -{ - ma_result result; - ma_flac* pFlac; - - (void)pUserData; /* For now not using pUserData, but once we start storing the vorbis decoder state within the ma_decoder structure this will be set to the decoder so we can avoid a malloc. */ - - /* For now we're just allocating the decoder backend on the heap. */ - pFlac = (ma_flac*)ma_malloc(sizeof(*pFlac), pAllocationCallbacks); - if (pFlac == NULL) { - return MA_OUT_OF_MEMORY; - } - - result = ma_flac_init_file(pFilePath, pConfig, pAllocationCallbacks, pFlac); - if (result != MA_SUCCESS) { - ma_free(pFlac, pAllocationCallbacks); - return result; - } - - *ppBackend = pFlac; - - return MA_SUCCESS; -} - -static ma_result ma_decoding_backend_init_file_w__flac(void* pUserData, const wchar_t* pFilePath, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_data_source** ppBackend) -{ - ma_result result; - ma_flac* pFlac; - - (void)pUserData; /* For now not using pUserData, but once we start storing the vorbis decoder state within the ma_decoder structure this will be set to the decoder so we can avoid a malloc. */ - - /* For now we're just allocating the decoder backend on the heap. */ - pFlac = (ma_flac*)ma_malloc(sizeof(*pFlac), pAllocationCallbacks); - if (pFlac == NULL) { - return MA_OUT_OF_MEMORY; - } - - result = ma_flac_init_file_w(pFilePath, pConfig, pAllocationCallbacks, pFlac); - if (result != MA_SUCCESS) { - ma_free(pFlac, pAllocationCallbacks); - return result; - } - - *ppBackend = pFlac; - - return MA_SUCCESS; -} - -static ma_result ma_decoding_backend_init_memory__flac(void* pUserData, const void* pData, size_t dataSize, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_data_source** ppBackend) -{ - ma_result result; - ma_flac* pFlac; - - (void)pUserData; /* For now not using pUserData, but once we start storing the vorbis decoder state within the ma_decoder structure this will be set to the decoder so we can avoid a malloc. */ - - /* For now we're just allocating the decoder backend on the heap. */ - pFlac = (ma_flac*)ma_malloc(sizeof(*pFlac), pAllocationCallbacks); - if (pFlac == NULL) { - return MA_OUT_OF_MEMORY; - } - - result = ma_flac_init_memory(pData, dataSize, pConfig, pAllocationCallbacks, pFlac); - if (result != MA_SUCCESS) { - ma_free(pFlac, pAllocationCallbacks); - return result; - } - - *ppBackend = pFlac; - - return MA_SUCCESS; -} - -static void ma_decoding_backend_uninit__flac(void* pUserData, ma_data_source* pBackend, const ma_allocation_callbacks* pAllocationCallbacks) -{ - ma_flac* pFlac = (ma_flac*)pBackend; - - (void)pUserData; - - ma_flac_uninit(pFlac, pAllocationCallbacks); - ma_free(pFlac, pAllocationCallbacks); -} - -static ma_decoding_backend_vtable g_ma_decoding_backend_vtable_flac = -{ - ma_decoding_backend_init__flac, - ma_decoding_backend_init_file__flac, - ma_decoding_backend_init_file_w__flac, - ma_decoding_backend_init_memory__flac, - ma_decoding_backend_uninit__flac -}; - -static ma_result ma_decoder_init_flac__internal(const ma_decoder_config* pConfig, ma_decoder* pDecoder) -{ - return ma_decoder_init_from_vtable__internal(&g_ma_decoding_backend_vtable_flac, NULL, pConfig, pDecoder); -} - -static ma_result ma_decoder_init_flac_from_file__internal(const char* pFilePath, const ma_decoder_config* pConfig, ma_decoder* pDecoder) -{ - return ma_decoder_init_from_file__internal(&g_ma_decoding_backend_vtable_flac, NULL, pFilePath, pConfig, pDecoder); -} - -static ma_result ma_decoder_init_flac_from_file_w__internal(const wchar_t* pFilePath, const ma_decoder_config* pConfig, ma_decoder* pDecoder) -{ - return ma_decoder_init_from_file_w__internal(&g_ma_decoding_backend_vtable_flac, NULL, pFilePath, pConfig, pDecoder); -} - -static ma_result ma_decoder_init_flac_from_memory__internal(const void* pData, size_t dataSize, const ma_decoder_config* pConfig, ma_decoder* pDecoder) -{ - return ma_decoder_init_from_memory__internal(&g_ma_decoding_backend_vtable_flac, NULL, pData, dataSize, pConfig, pDecoder); -} -#endif /* ma_dr_flac_h */ - -/* MP3 */ -#ifdef ma_dr_mp3_h -#define MA_HAS_MP3 - -typedef struct -{ - ma_data_source_base ds; - ma_read_proc onRead; - ma_seek_proc onSeek; - ma_tell_proc onTell; - void* pReadSeekTellUserData; - ma_format format; /* Can be f32 or s16. */ -#if !defined(MA_NO_MP3) - ma_dr_mp3 dr; - ma_uint32 seekPointCount; - ma_dr_mp3_seek_point* pSeekPoints; /* Only used if seek table generation is used. */ -#endif -} ma_mp3; - -MA_API ma_result ma_mp3_init(ma_read_proc onRead, ma_seek_proc onSeek, ma_tell_proc onTell, void* pReadSeekTellUserData, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_mp3* pMP3); -MA_API ma_result ma_mp3_init_file(const char* pFilePath, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_mp3* pMP3); -MA_API ma_result ma_mp3_init_file_w(const wchar_t* pFilePath, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_mp3* pMP3); -MA_API ma_result ma_mp3_init_memory(const void* pData, size_t dataSize, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_mp3* pMP3); -MA_API void ma_mp3_uninit(ma_mp3* pMP3, const ma_allocation_callbacks* pAllocationCallbacks); -MA_API ma_result ma_mp3_read_pcm_frames(ma_mp3* pMP3, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead); -MA_API ma_result ma_mp3_seek_to_pcm_frame(ma_mp3* pMP3, ma_uint64 frameIndex); -MA_API ma_result ma_mp3_get_data_format(ma_mp3* pMP3, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate, ma_channel* pChannelMap, size_t channelMapCap); -MA_API ma_result ma_mp3_get_cursor_in_pcm_frames(ma_mp3* pMP3, ma_uint64* pCursor); -MA_API ma_result ma_mp3_get_length_in_pcm_frames(ma_mp3* pMP3, ma_uint64* pLength); - - -static ma_result ma_mp3_ds_read(ma_data_source* pDataSource, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead) -{ - return ma_mp3_read_pcm_frames((ma_mp3*)pDataSource, pFramesOut, frameCount, pFramesRead); -} - -static ma_result ma_mp3_ds_seek(ma_data_source* pDataSource, ma_uint64 frameIndex) -{ - return ma_mp3_seek_to_pcm_frame((ma_mp3*)pDataSource, frameIndex); -} - -static ma_result ma_mp3_ds_get_data_format(ma_data_source* pDataSource, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate, ma_channel* pChannelMap, size_t channelMapCap) -{ - return ma_mp3_get_data_format((ma_mp3*)pDataSource, pFormat, pChannels, pSampleRate, pChannelMap, channelMapCap); -} - -static ma_result ma_mp3_ds_get_cursor(ma_data_source* pDataSource, ma_uint64* pCursor) -{ - return ma_mp3_get_cursor_in_pcm_frames((ma_mp3*)pDataSource, pCursor); -} - -static ma_result ma_mp3_ds_get_length(ma_data_source* pDataSource, ma_uint64* pLength) -{ - return ma_mp3_get_length_in_pcm_frames((ma_mp3*)pDataSource, pLength); -} - -static ma_data_source_vtable g_ma_mp3_ds_vtable = -{ - ma_mp3_ds_read, - ma_mp3_ds_seek, - ma_mp3_ds_get_data_format, - ma_mp3_ds_get_cursor, - ma_mp3_ds_get_length, - NULL, /* onSetLooping */ - 0 -}; - - -#if !defined(MA_NO_MP3) -static size_t ma_mp3_dr_callback__read(void* pUserData, void* pBufferOut, size_t bytesToRead) -{ - ma_mp3* pMP3 = (ma_mp3*)pUserData; - ma_result result; - size_t bytesRead; - - MA_ASSERT(pMP3 != NULL); - - result = pMP3->onRead(pMP3->pReadSeekTellUserData, pBufferOut, bytesToRead, &bytesRead); - (void)result; - - return bytesRead; -} - -static ma_bool32 ma_mp3_dr_callback__seek(void* pUserData, int offset, ma_dr_mp3_seek_origin origin) -{ - ma_mp3* pMP3 = (ma_mp3*)pUserData; - ma_result result; - ma_seek_origin maSeekOrigin; - - MA_ASSERT(pMP3 != NULL); - - maSeekOrigin = ma_seek_origin_start; - if (origin == ma_dr_mp3_seek_origin_current) { - maSeekOrigin = ma_seek_origin_current; - } - - result = pMP3->onSeek(pMP3->pReadSeekTellUserData, offset, maSeekOrigin); - if (result != MA_SUCCESS) { - return MA_FALSE; - } - - return MA_TRUE; -} -#endif - -static ma_result ma_mp3_init_internal(const ma_decoding_backend_config* pConfig, ma_mp3* pMP3) -{ - ma_result result; - ma_data_source_config dataSourceConfig; - - if (pMP3 == NULL) { - return MA_INVALID_ARGS; - } - - MA_ZERO_OBJECT(pMP3); - pMP3->format = ma_format_f32; /* f32 by default. */ - - if (pConfig != NULL && (pConfig->preferredFormat == ma_format_f32 || pConfig->preferredFormat == ma_format_s16)) { - pMP3->format = pConfig->preferredFormat; - } else { - /* Getting here means something other than f32 and s16 was specified. Just leave this unset to use the default format. */ - } - - dataSourceConfig = ma_data_source_config_init(); - dataSourceConfig.vtable = &g_ma_mp3_ds_vtable; - - result = ma_data_source_init(&dataSourceConfig, &pMP3->ds); - if (result != MA_SUCCESS) { - return result; /* Failed to initialize the base data source. */ - } - - return MA_SUCCESS; -} - -static ma_result ma_mp3_generate_seek_table(ma_mp3* pMP3, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks) -{ - ma_bool32 mp3Result; - ma_uint32 seekPointCount = 0; - ma_dr_mp3_seek_point* pSeekPoints = NULL; - - MA_ASSERT(pMP3 != NULL); - MA_ASSERT(pConfig != NULL); - - seekPointCount = pConfig->seekPointCount; - if (seekPointCount > 0) { - pSeekPoints = (ma_dr_mp3_seek_point*)ma_malloc(sizeof(*pMP3->pSeekPoints) * seekPointCount, pAllocationCallbacks); - if (pSeekPoints == NULL) { - return MA_OUT_OF_MEMORY; - } - } - - mp3Result = ma_dr_mp3_calculate_seek_points(&pMP3->dr, &seekPointCount, pSeekPoints); - if (mp3Result != MA_TRUE) { - ma_free(pSeekPoints, pAllocationCallbacks); - return MA_ERROR; - } - - mp3Result = ma_dr_mp3_bind_seek_table(&pMP3->dr, seekPointCount, pSeekPoints); - if (mp3Result != MA_TRUE) { - ma_free(pSeekPoints, pAllocationCallbacks); - return MA_ERROR; - } - - pMP3->seekPointCount = seekPointCount; - pMP3->pSeekPoints = pSeekPoints; - - return MA_SUCCESS; -} - -static ma_result ma_mp3_post_init(ma_mp3* pMP3, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks) -{ - ma_result result; - - result = ma_mp3_generate_seek_table(pMP3, pConfig, pAllocationCallbacks); - if (result != MA_SUCCESS) { - return result; - } - - return MA_SUCCESS; -} - -MA_API ma_result ma_mp3_init(ma_read_proc onRead, ma_seek_proc onSeek, ma_tell_proc onTell, void* pReadSeekTellUserData, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_mp3* pMP3) -{ - ma_result result; - - result = ma_mp3_init_internal(pConfig, pMP3); - if (result != MA_SUCCESS) { - return result; - } - - if (onRead == NULL || onSeek == NULL) { - return MA_INVALID_ARGS; /* onRead and onSeek are mandatory. */ - } - - pMP3->onRead = onRead; - pMP3->onSeek = onSeek; - pMP3->onTell = onTell; - pMP3->pReadSeekTellUserData = pReadSeekTellUserData; - - #if !defined(MA_NO_MP3) - { - ma_bool32 mp3Result; - - mp3Result = ma_dr_mp3_init(&pMP3->dr, ma_mp3_dr_callback__read, ma_mp3_dr_callback__seek, pMP3, pAllocationCallbacks); - if (mp3Result != MA_TRUE) { - return MA_INVALID_FILE; - } - - ma_mp3_post_init(pMP3, pConfig, pAllocationCallbacks); - - return MA_SUCCESS; - } - #else - { - /* mp3 is disabled. */ - (void)pAllocationCallbacks; - return MA_NOT_IMPLEMENTED; - } - #endif -} - -MA_API ma_result ma_mp3_init_file(const char* pFilePath, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_mp3* pMP3) -{ - ma_result result; - - result = ma_mp3_init_internal(pConfig, pMP3); - if (result != MA_SUCCESS) { - return result; - } - - #if !defined(MA_NO_MP3) - { - ma_bool32 mp3Result; - - mp3Result = ma_dr_mp3_init_file(&pMP3->dr, pFilePath, pAllocationCallbacks); - if (mp3Result != MA_TRUE) { - return MA_INVALID_FILE; - } - - ma_mp3_post_init(pMP3, pConfig, pAllocationCallbacks); - - return MA_SUCCESS; - } - #else - { - /* mp3 is disabled. */ - (void)pFilePath; - (void)pAllocationCallbacks; - return MA_NOT_IMPLEMENTED; - } - #endif -} - -MA_API ma_result ma_mp3_init_file_w(const wchar_t* pFilePath, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_mp3* pMP3) -{ - ma_result result; - - result = ma_mp3_init_internal(pConfig, pMP3); - if (result != MA_SUCCESS) { - return result; - } - - #if !defined(MA_NO_MP3) - { - ma_bool32 mp3Result; - - mp3Result = ma_dr_mp3_init_file_w(&pMP3->dr, pFilePath, pAllocationCallbacks); - if (mp3Result != MA_TRUE) { - return MA_INVALID_FILE; - } - - ma_mp3_post_init(pMP3, pConfig, pAllocationCallbacks); - - return MA_SUCCESS; - } - #else - { - /* mp3 is disabled. */ - (void)pFilePath; - (void)pAllocationCallbacks; - return MA_NOT_IMPLEMENTED; - } - #endif -} - -MA_API ma_result ma_mp3_init_memory(const void* pData, size_t dataSize, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_mp3* pMP3) -{ - ma_result result; - - result = ma_mp3_init_internal(pConfig, pMP3); - if (result != MA_SUCCESS) { - return result; - } - - #if !defined(MA_NO_MP3) - { - ma_bool32 mp3Result; - - mp3Result = ma_dr_mp3_init_memory(&pMP3->dr, pData, dataSize, pAllocationCallbacks); - if (mp3Result != MA_TRUE) { - return MA_INVALID_FILE; - } - - ma_mp3_post_init(pMP3, pConfig, pAllocationCallbacks); - - return MA_SUCCESS; - } - #else - { - /* mp3 is disabled. */ - (void)pData; - (void)dataSize; - (void)pAllocationCallbacks; - return MA_NOT_IMPLEMENTED; - } - #endif -} - -MA_API void ma_mp3_uninit(ma_mp3* pMP3, const ma_allocation_callbacks* pAllocationCallbacks) -{ - if (pMP3 == NULL) { - return; - } - - #if !defined(MA_NO_MP3) - { - ma_dr_mp3_uninit(&pMP3->dr); - } - #else - { - /* mp3 is disabled. Should never hit this since initialization would have failed. */ - MA_ASSERT(MA_FALSE); - } - #endif - - /* Seek points need to be freed after the MP3 decoder has been uninitialized to ensure they're no longer being referenced. */ - ma_free(pMP3->pSeekPoints, pAllocationCallbacks); - - ma_data_source_uninit(&pMP3->ds); -} - -MA_API ma_result ma_mp3_read_pcm_frames(ma_mp3* pMP3, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead) -{ - if (pFramesRead != NULL) { - *pFramesRead = 0; - } - - if (frameCount == 0) { - return MA_INVALID_ARGS; - } - - if (pMP3 == NULL) { - return MA_INVALID_ARGS; - } - - #if !defined(MA_NO_MP3) - { - /* We always use floating point format. */ - ma_result result = MA_SUCCESS; /* Must be initialized to MA_SUCCESS. */ - ma_uint64 totalFramesRead = 0; - ma_format format; - - ma_mp3_get_data_format(pMP3, &format, NULL, NULL, NULL, 0); - - switch (format) - { - case ma_format_f32: - { - totalFramesRead = ma_dr_mp3_read_pcm_frames_f32(&pMP3->dr, frameCount, (float*)pFramesOut); - } break; - - case ma_format_s16: - { - totalFramesRead = ma_dr_mp3_read_pcm_frames_s16(&pMP3->dr, frameCount, (ma_int16*)pFramesOut); - } break; - - case ma_format_u8: - case ma_format_s24: - case ma_format_s32: - case ma_format_unknown: - default: - { - return MA_INVALID_OPERATION; - }; - } - - /* In the future we'll update ma_dr_mp3 to return MA_AT_END for us. */ - if (totalFramesRead == 0) { - result = MA_AT_END; - } - - if (pFramesRead != NULL) { - *pFramesRead = totalFramesRead; - } - - return result; - } - #else - { - /* mp3 is disabled. Should never hit this since initialization would have failed. */ - MA_ASSERT(MA_FALSE); - - (void)pFramesOut; - (void)frameCount; - (void)pFramesRead; - - return MA_NOT_IMPLEMENTED; - } - #endif -} - -MA_API ma_result ma_mp3_seek_to_pcm_frame(ma_mp3* pMP3, ma_uint64 frameIndex) -{ - if (pMP3 == NULL) { - return MA_INVALID_ARGS; - } - - #if !defined(MA_NO_MP3) - { - ma_bool32 mp3Result; - - mp3Result = ma_dr_mp3_seek_to_pcm_frame(&pMP3->dr, frameIndex); - if (mp3Result != MA_TRUE) { - return MA_ERROR; - } - - return MA_SUCCESS; - } - #else - { - /* mp3 is disabled. Should never hit this since initialization would have failed. */ - MA_ASSERT(MA_FALSE); - - (void)frameIndex; - - return MA_NOT_IMPLEMENTED; - } - #endif -} - -MA_API ma_result ma_mp3_get_data_format(ma_mp3* pMP3, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate, ma_channel* pChannelMap, size_t channelMapCap) -{ - /* Defaults for safety. */ - if (pFormat != NULL) { - *pFormat = ma_format_unknown; - } - if (pChannels != NULL) { - *pChannels = 0; - } - if (pSampleRate != NULL) { - *pSampleRate = 0; - } - if (pChannelMap != NULL) { - MA_ZERO_MEMORY(pChannelMap, sizeof(*pChannelMap) * channelMapCap); - } - - if (pMP3 == NULL) { - return MA_INVALID_OPERATION; - } - - if (pFormat != NULL) { - *pFormat = pMP3->format; - } - - #if !defined(MA_NO_MP3) - { - if (pChannels != NULL) { - *pChannels = pMP3->dr.channels; - } - - if (pSampleRate != NULL) { - *pSampleRate = pMP3->dr.sampleRate; - } - - if (pChannelMap != NULL) { - ma_channel_map_init_standard(ma_standard_channel_map_default, pChannelMap, channelMapCap, pMP3->dr.channels); - } - - return MA_SUCCESS; - } - #else - { - /* mp3 is disabled. Should never hit this since initialization would have failed. */ - MA_ASSERT(MA_FALSE); - return MA_NOT_IMPLEMENTED; - } - #endif -} - -MA_API ma_result ma_mp3_get_cursor_in_pcm_frames(ma_mp3* pMP3, ma_uint64* pCursor) -{ - if (pCursor == NULL) { - return MA_INVALID_ARGS; - } - - *pCursor = 0; /* Safety. */ - - if (pMP3 == NULL) { - return MA_INVALID_ARGS; - } - - #if !defined(MA_NO_MP3) - { - *pCursor = pMP3->dr.currentPCMFrame; - - return MA_SUCCESS; - } - #else - { - /* mp3 is disabled. Should never hit this since initialization would have failed. */ - MA_ASSERT(MA_FALSE); - return MA_NOT_IMPLEMENTED; - } - #endif -} - -MA_API ma_result ma_mp3_get_length_in_pcm_frames(ma_mp3* pMP3, ma_uint64* pLength) -{ - if (pLength == NULL) { - return MA_INVALID_ARGS; - } - - *pLength = 0; /* Safety. */ - - if (pMP3 == NULL) { - return MA_INVALID_ARGS; - } - - #if !defined(MA_NO_MP3) - { - *pLength = ma_dr_mp3_get_pcm_frame_count(&pMP3->dr); - - return MA_SUCCESS; - } - #else - { - /* mp3 is disabled. Should never hit this since initialization would have failed. */ - MA_ASSERT(MA_FALSE); - return MA_NOT_IMPLEMENTED; - } - #endif -} - - -static ma_result ma_decoding_backend_init__mp3(void* pUserData, ma_read_proc onRead, ma_seek_proc onSeek, ma_tell_proc onTell, void* pReadSeekTellUserData, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_data_source** ppBackend) -{ - ma_result result; - ma_mp3* pMP3; - - (void)pUserData; /* For now not using pUserData, but once we start storing the vorbis decoder state within the ma_decoder structure this will be set to the decoder so we can avoid a malloc. */ - - /* For now we're just allocating the decoder backend on the heap. */ - pMP3 = (ma_mp3*)ma_malloc(sizeof(*pMP3), pAllocationCallbacks); - if (pMP3 == NULL) { - return MA_OUT_OF_MEMORY; - } - - result = ma_mp3_init(onRead, onSeek, onTell, pReadSeekTellUserData, pConfig, pAllocationCallbacks, pMP3); - if (result != MA_SUCCESS) { - ma_free(pMP3, pAllocationCallbacks); - return result; - } - - *ppBackend = pMP3; - - return MA_SUCCESS; -} - -static ma_result ma_decoding_backend_init_file__mp3(void* pUserData, const char* pFilePath, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_data_source** ppBackend) -{ - ma_result result; - ma_mp3* pMP3; - - (void)pUserData; /* For now not using pUserData, but once we start storing the vorbis decoder state within the ma_decoder structure this will be set to the decoder so we can avoid a malloc. */ - - /* For now we're just allocating the decoder backend on the heap. */ - pMP3 = (ma_mp3*)ma_malloc(sizeof(*pMP3), pAllocationCallbacks); - if (pMP3 == NULL) { - return MA_OUT_OF_MEMORY; - } - - result = ma_mp3_init_file(pFilePath, pConfig, pAllocationCallbacks, pMP3); - if (result != MA_SUCCESS) { - ma_free(pMP3, pAllocationCallbacks); - return result; - } - - *ppBackend = pMP3; - - return MA_SUCCESS; -} - -static ma_result ma_decoding_backend_init_file_w__mp3(void* pUserData, const wchar_t* pFilePath, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_data_source** ppBackend) -{ - ma_result result; - ma_mp3* pMP3; - - (void)pUserData; /* For now not using pUserData, but once we start storing the vorbis decoder state within the ma_decoder structure this will be set to the decoder so we can avoid a malloc. */ - - /* For now we're just allocating the decoder backend on the heap. */ - pMP3 = (ma_mp3*)ma_malloc(sizeof(*pMP3), pAllocationCallbacks); - if (pMP3 == NULL) { - return MA_OUT_OF_MEMORY; - } - - result = ma_mp3_init_file_w(pFilePath, pConfig, pAllocationCallbacks, pMP3); - if (result != MA_SUCCESS) { - ma_free(pMP3, pAllocationCallbacks); - return result; - } - - *ppBackend = pMP3; - - return MA_SUCCESS; -} - -static ma_result ma_decoding_backend_init_memory__mp3(void* pUserData, const void* pData, size_t dataSize, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_data_source** ppBackend) -{ - ma_result result; - ma_mp3* pMP3; - - (void)pUserData; /* For now not using pUserData, but once we start storing the vorbis decoder state within the ma_decoder structure this will be set to the decoder so we can avoid a malloc. */ - - /* For now we're just allocating the decoder backend on the heap. */ - pMP3 = (ma_mp3*)ma_malloc(sizeof(*pMP3), pAllocationCallbacks); - if (pMP3 == NULL) { - return MA_OUT_OF_MEMORY; - } - - result = ma_mp3_init_memory(pData, dataSize, pConfig, pAllocationCallbacks, pMP3); - if (result != MA_SUCCESS) { - ma_free(pMP3, pAllocationCallbacks); - return result; - } - - *ppBackend = pMP3; - - return MA_SUCCESS; -} - -static void ma_decoding_backend_uninit__mp3(void* pUserData, ma_data_source* pBackend, const ma_allocation_callbacks* pAllocationCallbacks) -{ - ma_mp3* pMP3 = (ma_mp3*)pBackend; - - (void)pUserData; - - ma_mp3_uninit(pMP3, pAllocationCallbacks); - ma_free(pMP3, pAllocationCallbacks); -} - -static ma_decoding_backend_vtable g_ma_decoding_backend_vtable_mp3 = -{ - ma_decoding_backend_init__mp3, - ma_decoding_backend_init_file__mp3, - ma_decoding_backend_init_file_w__mp3, - ma_decoding_backend_init_memory__mp3, - ma_decoding_backend_uninit__mp3 -}; - -static ma_result ma_decoder_init_mp3__internal(const ma_decoder_config* pConfig, ma_decoder* pDecoder) -{ - return ma_decoder_init_from_vtable__internal(&g_ma_decoding_backend_vtable_mp3, NULL, pConfig, pDecoder); -} - -static ma_result ma_decoder_init_mp3_from_file__internal(const char* pFilePath, const ma_decoder_config* pConfig, ma_decoder* pDecoder) -{ - return ma_decoder_init_from_file__internal(&g_ma_decoding_backend_vtable_mp3, NULL, pFilePath, pConfig, pDecoder); -} - -static ma_result ma_decoder_init_mp3_from_file_w__internal(const wchar_t* pFilePath, const ma_decoder_config* pConfig, ma_decoder* pDecoder) -{ - return ma_decoder_init_from_file_w__internal(&g_ma_decoding_backend_vtable_mp3, NULL, pFilePath, pConfig, pDecoder); -} - -static ma_result ma_decoder_init_mp3_from_memory__internal(const void* pData, size_t dataSize, const ma_decoder_config* pConfig, ma_decoder* pDecoder) -{ - return ma_decoder_init_from_memory__internal(&g_ma_decoding_backend_vtable_mp3, NULL, pData, dataSize, pConfig, pDecoder); -} -#endif /* ma_dr_mp3_h */ - -/* Vorbis */ -#ifdef STB_VORBIS_INCLUDE_STB_VORBIS_H -#define MA_HAS_VORBIS - -/* The size in bytes of each chunk of data to read from the Vorbis stream. */ -#define MA_VORBIS_DATA_CHUNK_SIZE 4096 - -typedef struct -{ - ma_data_source_base ds; - ma_read_proc onRead; - ma_seek_proc onSeek; - ma_tell_proc onTell; - void* pReadSeekTellUserData; - ma_allocation_callbacks allocationCallbacks; /* Store the allocation callbacks within the structure because we may need to dynamically expand a buffer in ma_stbvorbis_read_pcm_frames() when using push mode. */ - ma_format format; /* Only f32 is allowed with stb_vorbis. */ - ma_uint32 channels; - ma_uint32 sampleRate; - ma_uint64 cursor; -#if !defined(MA_NO_VORBIS) - stb_vorbis* stb; - ma_bool32 usingPushMode; - struct - { - ma_uint8* pData; - size_t dataSize; - size_t dataCapacity; - size_t audioStartOffsetInBytes; - ma_uint32 framesConsumed; /* The number of frames consumed in ppPacketData. */ - ma_uint32 framesRemaining; /* The number of frames remaining in ppPacketData. */ - float** ppPacketData; - } push; -#endif -} ma_stbvorbis; - -MA_API ma_result ma_stbvorbis_init(ma_read_proc onRead, ma_seek_proc onSeek, ma_tell_proc onTell, void* pReadSeekTellUserData, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_stbvorbis* pVorbis); -MA_API ma_result ma_stbvorbis_init_file(const char* pFilePath, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_stbvorbis* pVorbis); -MA_API ma_result ma_stbvorbis_init_memory(const void* pData, size_t dataSize, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_stbvorbis* pVorbis); -MA_API void ma_stbvorbis_uninit(ma_stbvorbis* pVorbis, const ma_allocation_callbacks* pAllocationCallbacks); -MA_API ma_result ma_stbvorbis_read_pcm_frames(ma_stbvorbis* pVorbis, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead); -MA_API ma_result ma_stbvorbis_seek_to_pcm_frame(ma_stbvorbis* pVorbis, ma_uint64 frameIndex); -MA_API ma_result ma_stbvorbis_get_data_format(ma_stbvorbis* pVorbis, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate, ma_channel* pChannelMap, size_t channelMapCap); -MA_API ma_result ma_stbvorbis_get_cursor_in_pcm_frames(ma_stbvorbis* pVorbis, ma_uint64* pCursor); -MA_API ma_result ma_stbvorbis_get_length_in_pcm_frames(ma_stbvorbis* pVorbis, ma_uint64* pLength); - - -static ma_result ma_stbvorbis_ds_read(ma_data_source* pDataSource, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead) -{ - return ma_stbvorbis_read_pcm_frames((ma_stbvorbis*)pDataSource, pFramesOut, frameCount, pFramesRead); -} - -static ma_result ma_stbvorbis_ds_seek(ma_data_source* pDataSource, ma_uint64 frameIndex) -{ - return ma_stbvorbis_seek_to_pcm_frame((ma_stbvorbis*)pDataSource, frameIndex); -} - -static ma_result ma_stbvorbis_ds_get_data_format(ma_data_source* pDataSource, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate, ma_channel* pChannelMap, size_t channelMapCap) -{ - return ma_stbvorbis_get_data_format((ma_stbvorbis*)pDataSource, pFormat, pChannels, pSampleRate, pChannelMap, channelMapCap); -} - -static ma_result ma_stbvorbis_ds_get_cursor(ma_data_source* pDataSource, ma_uint64* pCursor) -{ - return ma_stbvorbis_get_cursor_in_pcm_frames((ma_stbvorbis*)pDataSource, pCursor); -} - -static ma_result ma_stbvorbis_ds_get_length(ma_data_source* pDataSource, ma_uint64* pLength) -{ - return ma_stbvorbis_get_length_in_pcm_frames((ma_stbvorbis*)pDataSource, pLength); -} - -static ma_data_source_vtable g_ma_stbvorbis_ds_vtable = -{ - ma_stbvorbis_ds_read, - ma_stbvorbis_ds_seek, - ma_stbvorbis_ds_get_data_format, - ma_stbvorbis_ds_get_cursor, - ma_stbvorbis_ds_get_length, - NULL, /* onSetLooping */ - 0 -}; - - -static ma_result ma_stbvorbis_init_internal(const ma_decoding_backend_config* pConfig, ma_stbvorbis* pVorbis) -{ - ma_result result; - ma_data_source_config dataSourceConfig; - - (void)pConfig; - - if (pVorbis == NULL) { - return MA_INVALID_ARGS; - } - - MA_ZERO_OBJECT(pVorbis); - pVorbis->format = ma_format_f32; /* Only supporting f32. */ - - dataSourceConfig = ma_data_source_config_init(); - dataSourceConfig.vtable = &g_ma_stbvorbis_ds_vtable; - - result = ma_data_source_init(&dataSourceConfig, &pVorbis->ds); - if (result != MA_SUCCESS) { - return result; /* Failed to initialize the base data source. */ - } - - return MA_SUCCESS; -} - -#if !defined(MA_NO_VORBIS) -static ma_result ma_stbvorbis_post_init(ma_stbvorbis* pVorbis) -{ - stb_vorbis_info info; - - MA_ASSERT(pVorbis != NULL); - - info = stb_vorbis_get_info(pVorbis->stb); - - pVorbis->channels = info.channels; - pVorbis->sampleRate = info.sample_rate; - - return MA_SUCCESS; -} - -static ma_result ma_stbvorbis_init_internal_decoder_push(ma_stbvorbis* pVorbis) -{ - ma_result result; - stb_vorbis* stb; - size_t dataSize = 0; - size_t dataCapacity = 0; - ma_uint8* pData = NULL; /* <-- Must be initialized to NULL. */ - - for (;;) { - int vorbisError; - int consumedDataSize; /* <-- Fill by stb_vorbis_open_pushdata(). */ - size_t bytesRead; - ma_uint8* pNewData; - - /* Allocate memory for the new chunk. */ - dataCapacity += MA_VORBIS_DATA_CHUNK_SIZE; - pNewData = (ma_uint8*)ma_realloc(pData, dataCapacity, &pVorbis->allocationCallbacks); - if (pNewData == NULL) { - ma_free(pData, &pVorbis->allocationCallbacks); - return MA_OUT_OF_MEMORY; - } - - pData = pNewData; - - /* Read in the next chunk. */ - result = pVorbis->onRead(pVorbis->pReadSeekTellUserData, ma_offset_ptr(pData, dataSize), (dataCapacity - dataSize), &bytesRead); - dataSize += bytesRead; - - if (result != MA_SUCCESS) { - ma_free(pData, &pVorbis->allocationCallbacks); - return result; - } - - /* We have a maximum of 31 bits with stb_vorbis. */ - if (dataSize > INT_MAX) { - ma_free(pData, &pVorbis->allocationCallbacks); - return MA_TOO_BIG; - } - - stb = stb_vorbis_open_pushdata(pData, (int)dataSize, &consumedDataSize, &vorbisError, NULL); - if (stb != NULL) { - /* - Successfully opened the Vorbis decoder. We might have some leftover unprocessed - data so we'll need to move that down to the front. - */ - dataSize -= (size_t)consumedDataSize; /* Consume the data. */ - MA_MOVE_MEMORY(pData, ma_offset_ptr(pData, consumedDataSize), dataSize); - - /* - We need to track the start point so we can seek back to the start of the audio - data when seeking. - */ - pVorbis->push.audioStartOffsetInBytes = consumedDataSize; - - break; - } else { - /* Failed to open the decoder. */ - if (vorbisError == VORBIS_need_more_data) { - continue; - } else { - ma_free(pData, &pVorbis->allocationCallbacks); - return MA_ERROR; /* Failed to open the stb_vorbis decoder. */ - } - } - } - - MA_ASSERT(stb != NULL); - pVorbis->stb = stb; - pVorbis->push.pData = pData; - pVorbis->push.dataSize = dataSize; - pVorbis->push.dataCapacity = dataCapacity; - - return MA_SUCCESS; -} -#endif - -MA_API ma_result ma_stbvorbis_init(ma_read_proc onRead, ma_seek_proc onSeek, ma_tell_proc onTell, void* pReadSeekTellUserData, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_stbvorbis* pVorbis) -{ - ma_result result; - - result = ma_stbvorbis_init_internal(pConfig, pVorbis); - if (result != MA_SUCCESS) { - return result; - } - - if (onRead == NULL || onSeek == NULL) { - return MA_INVALID_ARGS; /* onRead and onSeek are mandatory. */ - } - - pVorbis->onRead = onRead; - pVorbis->onSeek = onSeek; - pVorbis->onTell = onTell; - pVorbis->pReadSeekTellUserData = pReadSeekTellUserData; - ma_allocation_callbacks_init_copy(&pVorbis->allocationCallbacks, pAllocationCallbacks); - - #if !defined(MA_NO_VORBIS) - { - /* - stb_vorbis lacks a callback based API for its pulling API which means we're stuck with the - pushing API. In order for us to be able to successfully initialize the decoder we need to - supply it with enough data. We need to keep loading data until we have enough. - */ - result = ma_stbvorbis_init_internal_decoder_push(pVorbis); - if (result != MA_SUCCESS) { - return result; - } - - pVorbis->usingPushMode = MA_TRUE; - - result = ma_stbvorbis_post_init(pVorbis); - if (result != MA_SUCCESS) { - stb_vorbis_close(pVorbis->stb); - ma_free(pVorbis->push.pData, pAllocationCallbacks); - return result; - } - - return MA_SUCCESS; - } - #else - { - /* vorbis is disabled. */ - (void)pAllocationCallbacks; - return MA_NOT_IMPLEMENTED; - } - #endif -} - -MA_API ma_result ma_stbvorbis_init_file(const char* pFilePath, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_stbvorbis* pVorbis) -{ - ma_result result; - - result = ma_stbvorbis_init_internal(pConfig, pVorbis); - if (result != MA_SUCCESS) { - return result; - } - - #if !defined(MA_NO_VORBIS) - { - (void)pAllocationCallbacks; /* Don't know how to make use of this with stb_vorbis. */ - - /* We can use stb_vorbis' pull mode for file based streams. */ - pVorbis->stb = stb_vorbis_open_filename(pFilePath, NULL, NULL); - if (pVorbis->stb == NULL) { - return MA_INVALID_FILE; - } - - pVorbis->usingPushMode = MA_FALSE; - - result = ma_stbvorbis_post_init(pVorbis); - if (result != MA_SUCCESS) { - stb_vorbis_close(pVorbis->stb); - return result; - } - - return MA_SUCCESS; - } - #else - { - /* vorbis is disabled. */ - (void)pFilePath; - (void)pAllocationCallbacks; - return MA_NOT_IMPLEMENTED; - } - #endif -} - -MA_API ma_result ma_stbvorbis_init_memory(const void* pData, size_t dataSize, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_stbvorbis* pVorbis) -{ - ma_result result; - - result = ma_stbvorbis_init_internal(pConfig, pVorbis); - if (result != MA_SUCCESS) { - return result; - } - - #if !defined(MA_NO_VORBIS) - { - (void)pAllocationCallbacks; - - /* stb_vorbis uses an int as its size specifier, restricting it to 32-bit even on 64-bit systems. *sigh*. */ - if (dataSize > INT_MAX) { - return MA_TOO_BIG; - } - - pVorbis->stb = stb_vorbis_open_memory((const unsigned char*)pData, (int)dataSize, NULL, NULL); - if (pVorbis->stb == NULL) { - return MA_INVALID_FILE; - } - - pVorbis->usingPushMode = MA_FALSE; - - result = ma_stbvorbis_post_init(pVorbis); - if (result != MA_SUCCESS) { - stb_vorbis_close(pVorbis->stb); - return result; - } - - return MA_SUCCESS; - } - #else - { - /* vorbis is disabled. */ - (void)pData; - (void)dataSize; - (void)pAllocationCallbacks; - return MA_NOT_IMPLEMENTED; - } - #endif -} - -MA_API void ma_stbvorbis_uninit(ma_stbvorbis* pVorbis, const ma_allocation_callbacks* pAllocationCallbacks) -{ - if (pVorbis == NULL) { - return; - } - - #if !defined(MA_NO_VORBIS) - { - stb_vorbis_close(pVorbis->stb); - - /* We'll have to clear some memory if we're using push mode. */ - if (pVorbis->usingPushMode) { - ma_free(pVorbis->push.pData, pAllocationCallbacks); - } - } - #else - { - /* vorbis is disabled. Should never hit this since initialization would have failed. */ - MA_ASSERT(MA_FALSE); - } - #endif - - ma_data_source_uninit(&pVorbis->ds); -} - -MA_API ma_result ma_stbvorbis_read_pcm_frames(ma_stbvorbis* pVorbis, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead) -{ - if (pFramesRead != NULL) { - *pFramesRead = 0; - } - - if (frameCount == 0) { - return MA_INVALID_ARGS; - } - - if (pVorbis == NULL) { - return MA_INVALID_ARGS; - } - - #if !defined(MA_NO_VORBIS) - { - /* We always use floating point format. */ - ma_result result = MA_SUCCESS; /* Must be initialized to MA_SUCCESS. */ - ma_uint64 totalFramesRead = 0; - ma_format format; - ma_uint32 channels; - - ma_stbvorbis_get_data_format(pVorbis, &format, &channels, NULL, NULL, 0); - - if (format == ma_format_f32) { - /* We read differently depending on whether or not we're using push mode. */ - if (pVorbis->usingPushMode) { - /* Push mode. This is the complex case. */ - float* pFramesOutF32 = (float*)pFramesOut; - - while (totalFramesRead < frameCount) { - /* The first thing to do is read from any already-cached frames. */ - ma_uint32 framesToReadFromCache = (ma_uint32)ma_min(pVorbis->push.framesRemaining, (frameCount - totalFramesRead)); /* Safe cast because pVorbis->framesRemaining is 32-bit. */ - - /* The output pointer can be null in which case we just treat it as a seek. */ - if (pFramesOut != NULL) { - ma_uint64 iFrame; - for (iFrame = 0; iFrame < framesToReadFromCache; iFrame += 1) { - ma_uint32 iChannel; - for (iChannel = 0; iChannel < pVorbis->channels; iChannel += 1) { - pFramesOutF32[iChannel] = pVorbis->push.ppPacketData[iChannel][pVorbis->push.framesConsumed + iFrame]; - } - - pFramesOutF32 += pVorbis->channels; - } - } - - /* Update pointers and counters. */ - pVorbis->push.framesConsumed += framesToReadFromCache; - pVorbis->push.framesRemaining -= framesToReadFromCache; - totalFramesRead += framesToReadFromCache; - - /* Don't bother reading any more frames right now if we've just finished loading. */ - if (totalFramesRead == frameCount) { - break; - } - - MA_ASSERT(pVorbis->push.framesRemaining == 0); - - /* Getting here means we've run out of cached frames. We'll need to load some more. */ - for (;;) { - int samplesRead = 0; - int consumedDataSize; - - /* We need to case dataSize to an int, so make sure we can do it safely. */ - if (pVorbis->push.dataSize > INT_MAX) { - break; /* Too big. */ - } - - consumedDataSize = stb_vorbis_decode_frame_pushdata(pVorbis->stb, pVorbis->push.pData, (int)pVorbis->push.dataSize, NULL, &pVorbis->push.ppPacketData, &samplesRead); - if (consumedDataSize != 0) { - /* Successfully decoded a Vorbis frame. Consume the data. */ - pVorbis->push.dataSize -= (size_t)consumedDataSize; - MA_MOVE_MEMORY(pVorbis->push.pData, ma_offset_ptr(pVorbis->push.pData, consumedDataSize), pVorbis->push.dataSize); - - pVorbis->push.framesConsumed = 0; - pVorbis->push.framesRemaining = samplesRead; - - break; - } else { - /* Not enough data. Read more. */ - size_t bytesRead; - - /* Expand the data buffer if necessary. */ - if (pVorbis->push.dataCapacity == pVorbis->push.dataSize) { - size_t newCap = pVorbis->push.dataCapacity + MA_VORBIS_DATA_CHUNK_SIZE; - ma_uint8* pNewData; - - pNewData = (ma_uint8*)ma_realloc(pVorbis->push.pData, newCap, &pVorbis->allocationCallbacks); - if (pNewData == NULL) { - result = MA_OUT_OF_MEMORY; - break; - } - - pVorbis->push.pData = pNewData; - pVorbis->push.dataCapacity = newCap; - } - - /* We should have enough room to load some data. */ - result = pVorbis->onRead(pVorbis->pReadSeekTellUserData, ma_offset_ptr(pVorbis->push.pData, pVorbis->push.dataSize), (pVorbis->push.dataCapacity - pVorbis->push.dataSize), &bytesRead); - pVorbis->push.dataSize += bytesRead; - - if (result != MA_SUCCESS) { - break; /* Failed to read any data. Get out. */ - } - } - } - - /* If we don't have a success code at this point it means we've encountered an error or the end of the file has been reached (probably the latter). */ - if (result != MA_SUCCESS) { - break; - } - } - } else { - /* Pull mode. This is the simple case, but we still need to run in a loop because stb_vorbis loves using 32-bit instead of 64-bit. */ - while (totalFramesRead < frameCount) { - ma_uint64 framesRemaining = (frameCount - totalFramesRead); - int framesRead; - - if (framesRemaining > INT_MAX) { - framesRemaining = INT_MAX; - } - - framesRead = stb_vorbis_get_samples_float_interleaved(pVorbis->stb, channels, (float*)ma_offset_pcm_frames_ptr(pFramesOut, totalFramesRead, format, channels), (int)framesRemaining * channels); /* Safe cast. */ - totalFramesRead += framesRead; - - if (framesRead < (int)framesRemaining) { - break; /* Nothing left to read. Get out. */ - } - } - } - } else { - result = MA_INVALID_ARGS; - } - - pVorbis->cursor += totalFramesRead; - - if (totalFramesRead == 0) { - result = MA_AT_END; - } - - if (pFramesRead != NULL) { - *pFramesRead = totalFramesRead; - } - - if (result == MA_SUCCESS && totalFramesRead == 0) { - result = MA_AT_END; - } - - return result; - } - #else - { - /* vorbis is disabled. Should never hit this since initialization would have failed. */ - MA_ASSERT(MA_FALSE); - - (void)pFramesOut; - (void)frameCount; - (void)pFramesRead; - - return MA_NOT_IMPLEMENTED; - } - #endif -} - -MA_API ma_result ma_stbvorbis_seek_to_pcm_frame(ma_stbvorbis* pVorbis, ma_uint64 frameIndex) -{ - if (pVorbis == NULL) { - return MA_INVALID_ARGS; - } - - #if !defined(MA_NO_VORBIS) - { - /* Different seeking methods depending on whether or not we're using push mode. */ - if (pVorbis->usingPushMode) { - /* Push mode. This is the complex case. */ - ma_result result; - float buffer[4096]; - - /* If we're seeking backwards, we need to seek back to the start and then brute-force forward. */ - if (frameIndex < pVorbis->cursor) { - if (frameIndex > 0x7FFFFFFF) { - return MA_INVALID_ARGS; /* Trying to seek beyond the 32-bit maximum of stb_vorbis. */ - } - - /* - This is wildly inefficient due to me having trouble getting sample exact seeking working - robustly with stb_vorbis_flush_pushdata(). The only way I can think to make this work - perfectly is to reinitialize the decoder. Note that we only enter this path when seeking - backwards. This will hopefully be removed once we get our own Vorbis decoder implemented. - */ - stb_vorbis_close(pVorbis->stb); - ma_free(pVorbis->push.pData, &pVorbis->allocationCallbacks); - - MA_ZERO_OBJECT(&pVorbis->push); - - /* Seek to the start of the file. */ - result = pVorbis->onSeek(pVorbis->pReadSeekTellUserData, 0, ma_seek_origin_start); - if (result != MA_SUCCESS) { - return result; - } - - result = ma_stbvorbis_init_internal_decoder_push(pVorbis); - if (result != MA_SUCCESS) { - return result; - } - - /* At this point we should be sitting on the first frame. */ - pVorbis->cursor = 0; - } - - /* We're just brute-forcing this for now. */ - while (pVorbis->cursor < frameIndex) { - ma_uint64 framesRead; - ma_uint64 framesToRead = ma_countof(buffer)/pVorbis->channels; - if (framesToRead > (frameIndex - pVorbis->cursor)) { - framesToRead = (frameIndex - pVorbis->cursor); - } - - result = ma_stbvorbis_read_pcm_frames(pVorbis, buffer, framesToRead, &framesRead); - if (result != MA_SUCCESS) { - return result; - } - } - } else { - /* Pull mode. This is the simple case. */ - int vorbisResult; - - if (frameIndex > UINT_MAX) { - return MA_INVALID_ARGS; /* Trying to seek beyond the 32-bit maximum of stb_vorbis. */ - } - - vorbisResult = stb_vorbis_seek(pVorbis->stb, (unsigned int)frameIndex); /* Safe cast. */ - if (vorbisResult == 0) { - return MA_ERROR; /* See failed. */ - } - - pVorbis->cursor = frameIndex; - } - - return MA_SUCCESS; - } - #else - { - /* vorbis is disabled. Should never hit this since initialization would have failed. */ - MA_ASSERT(MA_FALSE); - - (void)frameIndex; - - return MA_NOT_IMPLEMENTED; - } - #endif -} - -MA_API ma_result ma_stbvorbis_get_data_format(ma_stbvorbis* pVorbis, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate, ma_channel* pChannelMap, size_t channelMapCap) -{ - /* Defaults for safety. */ - if (pFormat != NULL) { - *pFormat = ma_format_unknown; - } - if (pChannels != NULL) { - *pChannels = 0; - } - if (pSampleRate != NULL) { - *pSampleRate = 0; - } - if (pChannelMap != NULL) { - MA_ZERO_MEMORY(pChannelMap, sizeof(*pChannelMap) * channelMapCap); - } - - if (pVorbis == NULL) { - return MA_INVALID_OPERATION; - } - - if (pFormat != NULL) { - *pFormat = pVorbis->format; - } - - #if !defined(MA_NO_VORBIS) - { - if (pChannels != NULL) { - *pChannels = pVorbis->channels; - } - - if (pSampleRate != NULL) { - *pSampleRate = pVorbis->sampleRate; - } - - if (pChannelMap != NULL) { - ma_channel_map_init_standard(ma_standard_channel_map_vorbis, pChannelMap, channelMapCap, pVorbis->channels); - } - - return MA_SUCCESS; - } - #else - { - /* vorbis is disabled. Should never hit this since initialization would have failed. */ - MA_ASSERT(MA_FALSE); - return MA_NOT_IMPLEMENTED; - } - #endif -} - -MA_API ma_result ma_stbvorbis_get_cursor_in_pcm_frames(ma_stbvorbis* pVorbis, ma_uint64* pCursor) -{ - if (pCursor == NULL) { - return MA_INVALID_ARGS; - } - - *pCursor = 0; /* Safety. */ - - if (pVorbis == NULL) { - return MA_INVALID_ARGS; - } - - #if !defined(MA_NO_VORBIS) - { - *pCursor = pVorbis->cursor; - - return MA_SUCCESS; - } - #else - { - /* vorbis is disabled. Should never hit this since initialization would have failed. */ - MA_ASSERT(MA_FALSE); - return MA_NOT_IMPLEMENTED; - } - #endif -} - -MA_API ma_result ma_stbvorbis_get_length_in_pcm_frames(ma_stbvorbis* pVorbis, ma_uint64* pLength) -{ - if (pLength == NULL) { - return MA_INVALID_ARGS; - } - - *pLength = 0; /* Safety. */ - - if (pVorbis == NULL) { - return MA_INVALID_ARGS; - } - - #if !defined(MA_NO_VORBIS) - { - if (pVorbis->usingPushMode) { - *pLength = 0; /* I don't know of a good way to determine this reliably with stb_vorbis and push mode. */ - } else { - *pLength = stb_vorbis_stream_length_in_samples(pVorbis->stb); - } - - return MA_SUCCESS; - } - #else - { - /* vorbis is disabled. Should never hit this since initialization would have failed. */ - MA_ASSERT(MA_FALSE); - return MA_NOT_IMPLEMENTED; - } - #endif -} - - -static ma_result ma_decoding_backend_init__stbvorbis(void* pUserData, ma_read_proc onRead, ma_seek_proc onSeek, ma_tell_proc onTell, void* pReadSeekTellUserData, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_data_source** ppBackend) -{ - ma_result result; - ma_stbvorbis* pVorbis; - - (void)pUserData; /* For now not using pUserData, but once we start storing the vorbis decoder state within the ma_decoder structure this will be set to the decoder so we can avoid a malloc. */ - - /* For now we're just allocating the decoder backend on the heap. */ - pVorbis = (ma_stbvorbis*)ma_malloc(sizeof(*pVorbis), pAllocationCallbacks); - if (pVorbis == NULL) { - return MA_OUT_OF_MEMORY; - } - - result = ma_stbvorbis_init(onRead, onSeek, onTell, pReadSeekTellUserData, pConfig, pAllocationCallbacks, pVorbis); - if (result != MA_SUCCESS) { - ma_free(pVorbis, pAllocationCallbacks); - return result; - } - - *ppBackend = pVorbis; - - return MA_SUCCESS; -} - -static ma_result ma_decoding_backend_init_file__stbvorbis(void* pUserData, const char* pFilePath, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_data_source** ppBackend) -{ - ma_result result; - ma_stbvorbis* pVorbis; - - (void)pUserData; /* For now not using pUserData, but once we start storing the vorbis decoder state within the ma_decoder structure this will be set to the decoder so we can avoid a malloc. */ - - /* For now we're just allocating the decoder backend on the heap. */ - pVorbis = (ma_stbvorbis*)ma_malloc(sizeof(*pVorbis), pAllocationCallbacks); - if (pVorbis == NULL) { - return MA_OUT_OF_MEMORY; - } - - result = ma_stbvorbis_init_file(pFilePath, pConfig, pAllocationCallbacks, pVorbis); - if (result != MA_SUCCESS) { - ma_free(pVorbis, pAllocationCallbacks); - return result; - } - - *ppBackend = pVorbis; - - return MA_SUCCESS; -} - -static ma_result ma_decoding_backend_init_memory__stbvorbis(void* pUserData, const void* pData, size_t dataSize, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_data_source** ppBackend) -{ - ma_result result; - ma_stbvorbis* pVorbis; - - (void)pUserData; /* For now not using pUserData, but once we start storing the vorbis decoder state within the ma_decoder structure this will be set to the decoder so we can avoid a malloc. */ - - /* For now we're just allocating the decoder backend on the heap. */ - pVorbis = (ma_stbvorbis*)ma_malloc(sizeof(*pVorbis), pAllocationCallbacks); - if (pVorbis == NULL) { - return MA_OUT_OF_MEMORY; - } - - result = ma_stbvorbis_init_memory(pData, dataSize, pConfig, pAllocationCallbacks, pVorbis); - if (result != MA_SUCCESS) { - ma_free(pVorbis, pAllocationCallbacks); - return result; - } - - *ppBackend = pVorbis; - - return MA_SUCCESS; -} - -static void ma_decoding_backend_uninit__stbvorbis(void* pUserData, ma_data_source* pBackend, const ma_allocation_callbacks* pAllocationCallbacks) -{ - ma_stbvorbis* pVorbis = (ma_stbvorbis*)pBackend; - - (void)pUserData; - - ma_stbvorbis_uninit(pVorbis, pAllocationCallbacks); - ma_free(pVorbis, pAllocationCallbacks); -} - -static ma_decoding_backend_vtable g_ma_decoding_backend_vtable_stbvorbis = -{ - ma_decoding_backend_init__stbvorbis, - ma_decoding_backend_init_file__stbvorbis, - NULL, /* onInitFileW() */ - ma_decoding_backend_init_memory__stbvorbis, - ma_decoding_backend_uninit__stbvorbis -}; - -static ma_result ma_decoder_init_vorbis__internal(const ma_decoder_config* pConfig, ma_decoder* pDecoder) -{ - return ma_decoder_init_from_vtable__internal(&g_ma_decoding_backend_vtable_stbvorbis, NULL, pConfig, pDecoder); -} - -static ma_result ma_decoder_init_vorbis_from_file__internal(const char* pFilePath, const ma_decoder_config* pConfig, ma_decoder* pDecoder) -{ - return ma_decoder_init_from_file__internal(&g_ma_decoding_backend_vtable_stbvorbis, NULL, pFilePath, pConfig, pDecoder); -} - -static ma_result ma_decoder_init_vorbis_from_file_w__internal(const wchar_t* pFilePath, const ma_decoder_config* pConfig, ma_decoder* pDecoder) -{ - return ma_decoder_init_from_file_w__internal(&g_ma_decoding_backend_vtable_stbvorbis, NULL, pFilePath, pConfig, pDecoder); -} - -static ma_result ma_decoder_init_vorbis_from_memory__internal(const void* pData, size_t dataSize, const ma_decoder_config* pConfig, ma_decoder* pDecoder) -{ - return ma_decoder_init_from_memory__internal(&g_ma_decoding_backend_vtable_stbvorbis, NULL, pData, dataSize, pConfig, pDecoder); -} -#endif /* STB_VORBIS_INCLUDE_STB_VORBIS_H */ - - - -static ma_result ma_decoder__init_allocation_callbacks(const ma_decoder_config* pConfig, ma_decoder* pDecoder) -{ - MA_ASSERT(pDecoder != NULL); - - if (pConfig != NULL) { - return ma_allocation_callbacks_init_copy(&pDecoder->allocationCallbacks, &pConfig->allocationCallbacks); - } else { - pDecoder->allocationCallbacks = ma_allocation_callbacks_init_default(); - return MA_SUCCESS; - } -} - -static ma_result ma_decoder__data_source_on_read(ma_data_source* pDataSource, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead) -{ - return ma_decoder_read_pcm_frames((ma_decoder*)pDataSource, pFramesOut, frameCount, pFramesRead); -} - -static ma_result ma_decoder__data_source_on_seek(ma_data_source* pDataSource, ma_uint64 frameIndex) -{ - return ma_decoder_seek_to_pcm_frame((ma_decoder*)pDataSource, frameIndex); -} - -static ma_result ma_decoder__data_source_on_get_data_format(ma_data_source* pDataSource, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate, ma_channel* pChannelMap, size_t channelMapCap) -{ - return ma_decoder_get_data_format((ma_decoder*)pDataSource, pFormat, pChannels, pSampleRate, pChannelMap, channelMapCap); -} - -static ma_result ma_decoder__data_source_on_get_cursor(ma_data_source* pDataSource, ma_uint64* pCursor) -{ - return ma_decoder_get_cursor_in_pcm_frames((ma_decoder*)pDataSource, pCursor); -} - -static ma_result ma_decoder__data_source_on_get_length(ma_data_source* pDataSource, ma_uint64* pLength) -{ - return ma_decoder_get_length_in_pcm_frames((ma_decoder*)pDataSource, pLength); -} - -static ma_data_source_vtable g_ma_decoder_data_source_vtable = -{ - ma_decoder__data_source_on_read, - ma_decoder__data_source_on_seek, - ma_decoder__data_source_on_get_data_format, - ma_decoder__data_source_on_get_cursor, - ma_decoder__data_source_on_get_length, - NULL, /* onSetLooping */ - 0 -}; - -static ma_result ma_decoder__preinit(ma_decoder_read_proc onRead, ma_decoder_seek_proc onSeek, ma_decoder_tell_proc onTell, void* pUserData, const ma_decoder_config* pConfig, ma_decoder* pDecoder) -{ - ma_result result; - ma_data_source_config dataSourceConfig; - - MA_ASSERT(pConfig != NULL); - - if (pDecoder == NULL) { - return MA_INVALID_ARGS; - } - - MA_ZERO_OBJECT(pDecoder); - - dataSourceConfig = ma_data_source_config_init(); - dataSourceConfig.vtable = &g_ma_decoder_data_source_vtable; - - result = ma_data_source_init(&dataSourceConfig, &pDecoder->ds); - if (result != MA_SUCCESS) { - return result; - } - - pDecoder->onRead = onRead; - pDecoder->onSeek = onSeek; - pDecoder->onTell = onTell; - pDecoder->pUserData = pUserData; - - result = ma_decoder__init_allocation_callbacks(pConfig, pDecoder); - if (result != MA_SUCCESS) { - ma_data_source_uninit(&pDecoder->ds); - return result; - } - - return MA_SUCCESS; -} - -static ma_result ma_decoder__postinit(const ma_decoder_config* pConfig, ma_decoder* pDecoder) -{ - ma_result result; - - result = ma_decoder__init_data_converter(pDecoder, pConfig); - - /* If we failed post initialization we need to uninitialize the decoder before returning to prevent a memory leak. */ - if (result != MA_SUCCESS) { - ma_decoder_uninit(pDecoder); - return result; - } - - return result; -} - - -static ma_result ma_decoder_init__internal(ma_decoder_read_proc onRead, ma_decoder_seek_proc onSeek, void* pUserData, const ma_decoder_config* pConfig, ma_decoder* pDecoder) -{ - ma_result result = MA_NO_BACKEND; - - MA_ASSERT(pConfig != NULL); - MA_ASSERT(pDecoder != NULL); - - /* Silence some warnings in the case that we don't have any decoder backends enabled. */ - (void)onRead; - (void)onSeek; - (void)pUserData; - - - /* If we've specified a specific encoding type, try that first. */ - if (pConfig->encodingFormat != ma_encoding_format_unknown) { - #ifdef MA_HAS_WAV - if (pConfig->encodingFormat == ma_encoding_format_wav) { - result = ma_decoder_init_wav__internal(pConfig, pDecoder); - } - #endif - #ifdef MA_HAS_FLAC - if (pConfig->encodingFormat == ma_encoding_format_flac) { - result = ma_decoder_init_flac__internal(pConfig, pDecoder); - } - #endif - #ifdef MA_HAS_MP3 - if (pConfig->encodingFormat == ma_encoding_format_mp3) { - result = ma_decoder_init_mp3__internal(pConfig, pDecoder); - } - #endif - #ifdef MA_HAS_VORBIS - if (pConfig->encodingFormat == ma_encoding_format_vorbis) { - result = ma_decoder_init_vorbis__internal(pConfig, pDecoder); - } - #endif - - /* If we weren't able to initialize the decoder, seek back to the start to give the next attempts a clean start. */ - if (result != MA_SUCCESS) { - onSeek(pDecoder, 0, ma_seek_origin_start); - } - } - - if (result != MA_SUCCESS) { - /* Getting here means we couldn't load a specific decoding backend based on the encoding format. */ - - /* - We use trial and error to open a decoder. We prioritize custom decoders so that if they - implement the same encoding format they take priority over the built-in decoders. - */ - if (result != MA_SUCCESS) { - result = ma_decoder_init_custom__internal(pConfig, pDecoder); - if (result != MA_SUCCESS) { - onSeek(pDecoder, 0, ma_seek_origin_start); - } - } - - /* - If we get to this point and we still haven't found a decoder, and the caller has requested a - specific encoding format, there's no hope for it. Abort. - */ - if (pConfig->encodingFormat != ma_encoding_format_unknown) { - return MA_NO_BACKEND; - } - - #ifdef MA_HAS_WAV - if (result != MA_SUCCESS) { - result = ma_decoder_init_wav__internal(pConfig, pDecoder); - if (result != MA_SUCCESS) { - onSeek(pDecoder, 0, ma_seek_origin_start); - } - } - #endif - #ifdef MA_HAS_FLAC - if (result != MA_SUCCESS) { - result = ma_decoder_init_flac__internal(pConfig, pDecoder); - if (result != MA_SUCCESS) { - onSeek(pDecoder, 0, ma_seek_origin_start); - } - } - #endif - #ifdef MA_HAS_MP3 - if (result != MA_SUCCESS) { - result = ma_decoder_init_mp3__internal(pConfig, pDecoder); - if (result != MA_SUCCESS) { - onSeek(pDecoder, 0, ma_seek_origin_start); - } - } - #endif - #ifdef MA_HAS_VORBIS - if (result != MA_SUCCESS) { - result = ma_decoder_init_vorbis__internal(pConfig, pDecoder); - if (result != MA_SUCCESS) { - onSeek(pDecoder, 0, ma_seek_origin_start); - } - } - #endif - } - - if (result != MA_SUCCESS) { - return result; - } - - return ma_decoder__postinit(pConfig, pDecoder); -} - -MA_API ma_result ma_decoder_init(ma_decoder_read_proc onRead, ma_decoder_seek_proc onSeek, void* pUserData, const ma_decoder_config* pConfig, ma_decoder* pDecoder) -{ - ma_decoder_config config; - ma_result result; - - config = ma_decoder_config_init_copy(pConfig); - - result = ma_decoder__preinit(onRead, onSeek, NULL, pUserData, &config, pDecoder); - if (result != MA_SUCCESS) { - return result; - } - - return ma_decoder_init__internal(onRead, onSeek, pUserData, &config, pDecoder); -} - - -static ma_result ma_decoder__on_read_memory(ma_decoder* pDecoder, void* pBufferOut, size_t bytesToRead, size_t* pBytesRead) -{ - size_t bytesRemaining; - - MA_ASSERT(pDecoder->data.memory.dataSize >= pDecoder->data.memory.currentReadPos); - - if (pBytesRead != NULL) { - *pBytesRead = 0; - } - - bytesRemaining = pDecoder->data.memory.dataSize - pDecoder->data.memory.currentReadPos; - if (bytesToRead > bytesRemaining) { - bytesToRead = bytesRemaining; - } - - if (bytesRemaining == 0) { - return MA_AT_END; - } - - if (bytesToRead > 0) { - MA_COPY_MEMORY(pBufferOut, pDecoder->data.memory.pData + pDecoder->data.memory.currentReadPos, bytesToRead); - pDecoder->data.memory.currentReadPos += bytesToRead; - } - - if (pBytesRead != NULL) { - *pBytesRead = bytesToRead; - } - - return MA_SUCCESS; -} - -static ma_result ma_decoder__on_seek_memory(ma_decoder* pDecoder, ma_int64 byteOffset, ma_seek_origin origin) -{ - if (byteOffset > 0 && (ma_uint64)byteOffset > MA_SIZE_MAX) { - return MA_BAD_SEEK; - } - - if (origin == ma_seek_origin_current) { - if (byteOffset > 0) { - if (pDecoder->data.memory.currentReadPos + byteOffset > pDecoder->data.memory.dataSize) { - byteOffset = (ma_int64)(pDecoder->data.memory.dataSize - pDecoder->data.memory.currentReadPos); /* Trying to seek too far forward. */ - } - - pDecoder->data.memory.currentReadPos += (size_t)byteOffset; - } else { - if (pDecoder->data.memory.currentReadPos < (size_t)-byteOffset) { - byteOffset = -(ma_int64)pDecoder->data.memory.currentReadPos; /* Trying to seek too far backwards. */ - } - - pDecoder->data.memory.currentReadPos -= (size_t)-byteOffset; - } - } else { - if (origin == ma_seek_origin_end) { - if (byteOffset < 0) { - byteOffset = -byteOffset; - } - - if (byteOffset > (ma_int64)pDecoder->data.memory.dataSize) { - pDecoder->data.memory.currentReadPos = 0; /* Trying to seek too far back. */ - } else { - pDecoder->data.memory.currentReadPos = pDecoder->data.memory.dataSize - (size_t)byteOffset; - } - } else { - if ((size_t)byteOffset <= pDecoder->data.memory.dataSize) { - pDecoder->data.memory.currentReadPos = (size_t)byteOffset; - } else { - pDecoder->data.memory.currentReadPos = pDecoder->data.memory.dataSize; /* Trying to seek too far forward. */ - } - } - } - - return MA_SUCCESS; -} - -static ma_result ma_decoder__on_tell_memory(ma_decoder* pDecoder, ma_int64* pCursor) -{ - MA_ASSERT(pDecoder != NULL); - MA_ASSERT(pCursor != NULL); - - *pCursor = (ma_int64)pDecoder->data.memory.currentReadPos; - - return MA_SUCCESS; -} - -static ma_result ma_decoder__preinit_memory_wrapper(const void* pData, size_t dataSize, const ma_decoder_config* pConfig, ma_decoder* pDecoder) -{ - ma_result result = ma_decoder__preinit(ma_decoder__on_read_memory, ma_decoder__on_seek_memory, ma_decoder__on_tell_memory, NULL, pConfig, pDecoder); - if (result != MA_SUCCESS) { - return result; - } - - if (pData == NULL || dataSize == 0) { - return MA_INVALID_ARGS; - } - - pDecoder->data.memory.pData = (const ma_uint8*)pData; - pDecoder->data.memory.dataSize = dataSize; - pDecoder->data.memory.currentReadPos = 0; - - (void)pConfig; - return MA_SUCCESS; -} - -MA_API ma_result ma_decoder_init_memory(const void* pData, size_t dataSize, const ma_decoder_config* pConfig, ma_decoder* pDecoder) -{ - ma_result result; - ma_decoder_config config; - - config = ma_decoder_config_init_copy(pConfig); - - result = ma_decoder__preinit(NULL, NULL, NULL, NULL, &config, pDecoder); - if (result != MA_SUCCESS) { - return result; - } - - if (pData == NULL || dataSize == 0) { - return MA_INVALID_ARGS; - } - - /* If the backend has support for loading from a file path we'll want to use that. If that all fails we'll fall back to the VFS path. */ - result = MA_NO_BACKEND; - - if (config.encodingFormat != ma_encoding_format_unknown) { - #ifdef MA_HAS_WAV - if (config.encodingFormat == ma_encoding_format_wav) { - result = ma_decoder_init_wav_from_memory__internal(pData, dataSize, &config, pDecoder); - } - #endif - #ifdef MA_HAS_FLAC - if (config.encodingFormat == ma_encoding_format_flac) { - result = ma_decoder_init_flac_from_memory__internal(pData, dataSize, &config, pDecoder); - } - #endif - #ifdef MA_HAS_MP3 - if (config.encodingFormat == ma_encoding_format_mp3) { - result = ma_decoder_init_mp3_from_memory__internal(pData, dataSize, &config, pDecoder); - } - #endif - #ifdef MA_HAS_VORBIS - if (config.encodingFormat == ma_encoding_format_vorbis) { - result = ma_decoder_init_vorbis_from_memory__internal(pData, dataSize, &config, pDecoder); - } - #endif - } - - if (result != MA_SUCCESS) { - /* Getting here means we weren't able to initialize a decoder of a specific encoding format. */ - - /* - We use trial and error to open a decoder. We prioritize custom decoders so that if they - implement the same encoding format they take priority over the built-in decoders. - */ - result = ma_decoder_init_custom_from_memory__internal(pData, dataSize, &config, pDecoder); - - /* - If we get to this point and we still haven't found a decoder, and the caller has requested a - specific encoding format, there's no hope for it. Abort. - */ - if (result != MA_SUCCESS && config.encodingFormat != ma_encoding_format_unknown) { - return MA_NO_BACKEND; - } - - /* Use trial and error for stock decoders. */ - if (result != MA_SUCCESS) { - #ifdef MA_HAS_WAV - if (result != MA_SUCCESS) { - result = ma_decoder_init_wav_from_memory__internal(pData, dataSize, &config, pDecoder); - } - #endif - #ifdef MA_HAS_FLAC - if (result != MA_SUCCESS) { - result = ma_decoder_init_flac_from_memory__internal(pData, dataSize, &config, pDecoder); - } - #endif - #ifdef MA_HAS_MP3 - if (result != MA_SUCCESS) { - result = ma_decoder_init_mp3_from_memory__internal(pData, dataSize, &config, pDecoder); - } - #endif - #ifdef MA_HAS_VORBIS - if (result != MA_SUCCESS) { - result = ma_decoder_init_vorbis_from_memory__internal(pData, dataSize, &config, pDecoder); - } - #endif - } - } - - /* - If at this point we still haven't successfully initialized the decoder it most likely means - the backend doesn't have an implementation for loading from a file path. We'll try using - miniaudio's built-in file IO for loading file. - */ - if (result == MA_SUCCESS) { - /* Initialization was successful. Finish up. */ - result = ma_decoder__postinit(&config, pDecoder); - if (result != MA_SUCCESS) { - /* - The backend was initialized successfully, but for some reason post-initialization failed. This is most likely - due to an out of memory error. We're going to abort with an error here and not try to recover. - */ - if (pDecoder->pBackendVTable != NULL && pDecoder->pBackendVTable->onUninit != NULL) { - pDecoder->pBackendVTable->onUninit(pDecoder->pBackendUserData, &pDecoder->pBackend, &pDecoder->allocationCallbacks); - } - - return result; - } - } else { - /* Probably no implementation for loading from a block of memory. Use miniaudio's abstraction instead. */ - result = ma_decoder__preinit_memory_wrapper(pData, dataSize, &config, pDecoder); - if (result != MA_SUCCESS) { - return result; - } - - result = ma_decoder_init__internal(ma_decoder__on_read_memory, ma_decoder__on_seek_memory, NULL, &config, pDecoder); - if (result != MA_SUCCESS) { - return result; - } - } - - return MA_SUCCESS; -} - - -#if defined(MA_HAS_WAV) || \ - defined(MA_HAS_MP3) || \ - defined(MA_HAS_FLAC) || \ - defined(MA_HAS_VORBIS) -#define MA_HAS_PATH_API -#endif - -#if defined(MA_HAS_PATH_API) -static const char* ma_path_file_name(const char* path) -{ - const char* fileName; - - if (path == NULL) { - return NULL; - } - - fileName = path; - - /* We just loop through the path until we find the last slash. */ - while (path[0] != '\0') { - if (path[0] == '/' || path[0] == '\\') { - fileName = path; - } - - path += 1; - } - - /* At this point the file name is sitting on a slash, so just move forward. */ - while (fileName[0] != '\0' && (fileName[0] == '/' || fileName[0] == '\\')) { - fileName += 1; - } - - return fileName; -} - -static const wchar_t* ma_path_file_name_w(const wchar_t* path) -{ - const wchar_t* fileName; - - if (path == NULL) { - return NULL; - } - - fileName = path; - - /* We just loop through the path until we find the last slash. */ - while (path[0] != '\0') { - if (path[0] == '/' || path[0] == '\\') { - fileName = path; - } - - path += 1; - } - - /* At this point the file name is sitting on a slash, so just move forward. */ - while (fileName[0] != '\0' && (fileName[0] == '/' || fileName[0] == '\\')) { - fileName += 1; - } - - return fileName; -} - - -static const char* ma_path_extension(const char* path) -{ - const char* extension; - const char* lastOccurance; - - if (path == NULL) { - path = ""; - } - - extension = ma_path_file_name(path); - lastOccurance = NULL; - - /* Just find the last '.' and return. */ - while (extension[0] != '\0') { - if (extension[0] == '.') { - extension += 1; - lastOccurance = extension; - } - - extension += 1; - } - - return (lastOccurance != NULL) ? lastOccurance : extension; -} - -static const wchar_t* ma_path_extension_w(const wchar_t* path) -{ - const wchar_t* extension; - const wchar_t* lastOccurance; - - if (path == NULL) { - path = L""; - } - - extension = ma_path_file_name_w(path); - lastOccurance = NULL; - - /* Just find the last '.' and return. */ - while (extension[0] != '\0') { - if (extension[0] == '.') { - extension += 1; - lastOccurance = extension; - } - - extension += 1; - } - - return (lastOccurance != NULL) ? lastOccurance : extension; -} - - -static ma_bool32 ma_path_extension_equal(const char* path, const char* extension) -{ - const char* ext1; - const char* ext2; - - if (path == NULL || extension == NULL) { - return MA_FALSE; - } - - ext1 = extension; - ext2 = ma_path_extension(path); - -#if defined(_MSC_VER) || defined(__DMC__) - return _stricmp(ext1, ext2) == 0; -#else - return strcasecmp(ext1, ext2) == 0; -#endif -} - -static ma_bool32 ma_path_extension_equal_w(const wchar_t* path, const wchar_t* extension) -{ - const wchar_t* ext1; - const wchar_t* ext2; - - if (path == NULL || extension == NULL) { - return MA_FALSE; - } - - ext1 = extension; - ext2 = ma_path_extension_w(path); - -#if defined(_MSC_VER) || defined(__WATCOMC__) || defined(__DMC__) - return _wcsicmp(ext1, ext2) == 0; -#else - /* - I'm not aware of a wide character version of strcasecmp(). I'm therefore converting the extensions to multibyte strings and comparing those. This - isn't the most efficient way to do it, but it should work OK. - */ - { - char ext1MB[4096]; - char ext2MB[4096]; - const wchar_t* pext1 = ext1; - const wchar_t* pext2 = ext2; - mbstate_t mbs1; - mbstate_t mbs2; - - MA_ZERO_OBJECT(&mbs1); - MA_ZERO_OBJECT(&mbs2); - - if (wcsrtombs(ext1MB, &pext1, sizeof(ext1MB), &mbs1) == (size_t)-1) { - return MA_FALSE; - } - if (wcsrtombs(ext2MB, &pext2, sizeof(ext2MB), &mbs2) == (size_t)-1) { - return MA_FALSE; - } - - return strcasecmp(ext1MB, ext2MB) == 0; - } -#endif -} -#endif /* MA_HAS_PATH_API */ - - - -static ma_result ma_decoder__on_read_vfs(ma_decoder* pDecoder, void* pBufferOut, size_t bytesToRead, size_t* pBytesRead) -{ - MA_ASSERT(pDecoder != NULL); - MA_ASSERT(pBufferOut != NULL); - - return ma_vfs_or_default_read(pDecoder->data.vfs.pVFS, pDecoder->data.vfs.file, pBufferOut, bytesToRead, pBytesRead); -} - -static ma_result ma_decoder__on_seek_vfs(ma_decoder* pDecoder, ma_int64 offset, ma_seek_origin origin) -{ - MA_ASSERT(pDecoder != NULL); - - return ma_vfs_or_default_seek(pDecoder->data.vfs.pVFS, pDecoder->data.vfs.file, offset, origin); -} - -static ma_result ma_decoder__on_tell_vfs(ma_decoder* pDecoder, ma_int64* pCursor) -{ - MA_ASSERT(pDecoder != NULL); - - return ma_vfs_or_default_tell(pDecoder->data.vfs.pVFS, pDecoder->data.vfs.file, pCursor); -} - -static ma_result ma_decoder__preinit_vfs(ma_vfs* pVFS, const char* pFilePath, const ma_decoder_config* pConfig, ma_decoder* pDecoder) -{ - ma_result result; - ma_vfs_file file; - - result = ma_decoder__preinit(ma_decoder__on_read_vfs, ma_decoder__on_seek_vfs, ma_decoder__on_tell_vfs, NULL, pConfig, pDecoder); - if (result != MA_SUCCESS) { - return result; - } - - if (pFilePath == NULL || pFilePath[0] == '\0') { - return MA_INVALID_ARGS; - } - - result = ma_vfs_or_default_open(pVFS, pFilePath, MA_OPEN_MODE_READ, &file); - if (result != MA_SUCCESS) { - return result; - } - - pDecoder->data.vfs.pVFS = pVFS; - pDecoder->data.vfs.file = file; - - return MA_SUCCESS; -} - -MA_API ma_result ma_decoder_init_vfs(ma_vfs* pVFS, const char* pFilePath, const ma_decoder_config* pConfig, ma_decoder* pDecoder) -{ - ma_result result; - ma_decoder_config config; - - config = ma_decoder_config_init_copy(pConfig); - result = ma_decoder__preinit_vfs(pVFS, pFilePath, &config, pDecoder); - if (result != MA_SUCCESS) { - return result; - } - - result = MA_NO_BACKEND; - - if (config.encodingFormat != ma_encoding_format_unknown) { - #ifdef MA_HAS_WAV - if (config.encodingFormat == ma_encoding_format_wav) { - result = ma_decoder_init_wav__internal(&config, pDecoder); - } - #endif - #ifdef MA_HAS_FLAC - if (config.encodingFormat == ma_encoding_format_flac) { - result = ma_decoder_init_flac__internal(&config, pDecoder); - } - #endif - #ifdef MA_HAS_MP3 - if (config.encodingFormat == ma_encoding_format_mp3) { - result = ma_decoder_init_mp3__internal(&config, pDecoder); - } - #endif - #ifdef MA_HAS_VORBIS - if (config.encodingFormat == ma_encoding_format_vorbis) { - result = ma_decoder_init_vorbis__internal(&config, pDecoder); - } - #endif - - /* Make sure we seek back to the start if we didn't initialize a decoder successfully so the next attempts have a fresh start. */ - if (result != MA_SUCCESS) { - ma_decoder__on_seek_vfs(pDecoder, 0, ma_seek_origin_start); - } - } - - if (result != MA_SUCCESS) { - /* Getting here means we weren't able to initialize a decoder of a specific encoding format. */ - - /* - We use trial and error to open a decoder. We prioritize custom decoders so that if they - implement the same encoding format they take priority over the built-in decoders. - */ - if (result != MA_SUCCESS) { - result = ma_decoder_init_custom__internal(&config, pDecoder); - if (result != MA_SUCCESS) { - ma_decoder__on_seek_vfs(pDecoder, 0, ma_seek_origin_start); - } - } - - /* - If we get to this point and we still haven't found a decoder, and the caller has requested a - specific encoding format, there's no hope for it. Abort. - */ - if (config.encodingFormat != ma_encoding_format_unknown) { - return MA_NO_BACKEND; - } - - #ifdef MA_HAS_WAV - if (result != MA_SUCCESS && ma_path_extension_equal(pFilePath, "wav")) { - result = ma_decoder_init_wav__internal(&config, pDecoder); - if (result != MA_SUCCESS) { - ma_decoder__on_seek_vfs(pDecoder, 0, ma_seek_origin_start); - } - } - #endif - #ifdef MA_HAS_FLAC - if (result != MA_SUCCESS && ma_path_extension_equal(pFilePath, "flac")) { - result = ma_decoder_init_flac__internal(&config, pDecoder); - if (result != MA_SUCCESS) { - ma_decoder__on_seek_vfs(pDecoder, 0, ma_seek_origin_start); - } - } - #endif - #ifdef MA_HAS_MP3 - if (result != MA_SUCCESS && ma_path_extension_equal(pFilePath, "mp3")) { - result = ma_decoder_init_mp3__internal(&config, pDecoder); - if (result != MA_SUCCESS) { - ma_decoder__on_seek_vfs(pDecoder, 0, ma_seek_origin_start); - } - } - #endif - } - - /* If we still haven't got a result just use trial and error. Otherwise we can finish up. */ - if (result != MA_SUCCESS) { - result = ma_decoder_init__internal(ma_decoder__on_read_vfs, ma_decoder__on_seek_vfs, NULL, &config, pDecoder); - } else { - result = ma_decoder__postinit(&config, pDecoder); - } - - if (result != MA_SUCCESS) { - if (pDecoder->data.vfs.file != NULL) { /* <-- Will be reset to NULL if ma_decoder_uninit() is called in one of the steps above which allows us to avoid a double close of the file. */ - ma_vfs_or_default_close(pVFS, pDecoder->data.vfs.file); - } - - return result; - } - - return MA_SUCCESS; -} - - -static ma_result ma_decoder__preinit_vfs_w(ma_vfs* pVFS, const wchar_t* pFilePath, const ma_decoder_config* pConfig, ma_decoder* pDecoder) -{ - ma_result result; - ma_vfs_file file; - - result = ma_decoder__preinit(ma_decoder__on_read_vfs, ma_decoder__on_seek_vfs, ma_decoder__on_tell_vfs, NULL, pConfig, pDecoder); - if (result != MA_SUCCESS) { - return result; - } - - if (pFilePath == NULL || pFilePath[0] == '\0') { - return MA_INVALID_ARGS; - } - - result = ma_vfs_or_default_open_w(pVFS, pFilePath, MA_OPEN_MODE_READ, &file); - if (result != MA_SUCCESS) { - return result; - } - - pDecoder->data.vfs.pVFS = pVFS; - pDecoder->data.vfs.file = file; - - return MA_SUCCESS; -} - -MA_API ma_result ma_decoder_init_vfs_w(ma_vfs* pVFS, const wchar_t* pFilePath, const ma_decoder_config* pConfig, ma_decoder* pDecoder) -{ - ma_result result; - ma_decoder_config config; - - config = ma_decoder_config_init_copy(pConfig); - result = ma_decoder__preinit_vfs_w(pVFS, pFilePath, &config, pDecoder); - if (result != MA_SUCCESS) { - return result; - } - - result = MA_NO_BACKEND; - - if (config.encodingFormat != ma_encoding_format_unknown) { - #ifdef MA_HAS_WAV - if (config.encodingFormat == ma_encoding_format_wav) { - result = ma_decoder_init_wav__internal(&config, pDecoder); - } - #endif - #ifdef MA_HAS_FLAC - if (config.encodingFormat == ma_encoding_format_flac) { - result = ma_decoder_init_flac__internal(&config, pDecoder); - } - #endif - #ifdef MA_HAS_MP3 - if (config.encodingFormat == ma_encoding_format_mp3) { - result = ma_decoder_init_mp3__internal(&config, pDecoder); - } - #endif - #ifdef MA_HAS_VORBIS - if (config.encodingFormat == ma_encoding_format_vorbis) { - result = ma_decoder_init_vorbis__internal(&config, pDecoder); - } - #endif - - /* Make sure we seek back to the start if we didn't initialize a decoder successfully so the next attempts have a fresh start. */ - if (result != MA_SUCCESS) { - ma_decoder__on_seek_vfs(pDecoder, 0, ma_seek_origin_start); - } - } - - if (result != MA_SUCCESS) { - /* Getting here means we weren't able to initialize a decoder of a specific encoding format. */ - - /* - We use trial and error to open a decoder. We prioritize custom decoders so that if they - implement the same encoding format they take priority over the built-in decoders. - */ - if (result != MA_SUCCESS) { - result = ma_decoder_init_custom__internal(&config, pDecoder); - if (result != MA_SUCCESS) { - ma_decoder__on_seek_vfs(pDecoder, 0, ma_seek_origin_start); - } - } - - /* - If we get to this point and we still haven't found a decoder, and the caller has requested a - specific encoding format, there's no hope for it. Abort. - */ - if (config.encodingFormat != ma_encoding_format_unknown) { - return MA_NO_BACKEND; - } - - #ifdef MA_HAS_WAV - if (result != MA_SUCCESS && ma_path_extension_equal_w(pFilePath, L"wav")) { - result = ma_decoder_init_wav__internal(&config, pDecoder); - if (result != MA_SUCCESS) { - ma_decoder__on_seek_vfs(pDecoder, 0, ma_seek_origin_start); - } - } - #endif - #ifdef MA_HAS_FLAC - if (result != MA_SUCCESS && ma_path_extension_equal_w(pFilePath, L"flac")) { - result = ma_decoder_init_flac__internal(&config, pDecoder); - if (result != MA_SUCCESS) { - ma_decoder__on_seek_vfs(pDecoder, 0, ma_seek_origin_start); - } - } - #endif - #ifdef MA_HAS_MP3 - if (result != MA_SUCCESS && ma_path_extension_equal_w(pFilePath, L"mp3")) { - result = ma_decoder_init_mp3__internal(&config, pDecoder); - if (result != MA_SUCCESS) { - ma_decoder__on_seek_vfs(pDecoder, 0, ma_seek_origin_start); - } - } - #endif - } - - /* If we still haven't got a result just use trial and error. Otherwise we can finish up. */ - if (result != MA_SUCCESS) { - result = ma_decoder_init__internal(ma_decoder__on_read_vfs, ma_decoder__on_seek_vfs, NULL, &config, pDecoder); - } else { - result = ma_decoder__postinit(&config, pDecoder); - } - - if (result != MA_SUCCESS) { - ma_vfs_or_default_close(pVFS, pDecoder->data.vfs.file); - return result; - } - - return MA_SUCCESS; -} - - -static ma_result ma_decoder__preinit_file(const char* pFilePath, const ma_decoder_config* pConfig, ma_decoder* pDecoder) -{ - ma_result result; - - result = ma_decoder__preinit(NULL, NULL, NULL, NULL, pConfig, pDecoder); - if (result != MA_SUCCESS) { - return result; - } - - if (pFilePath == NULL || pFilePath[0] == '\0') { - return MA_INVALID_ARGS; - } - - return MA_SUCCESS; -} - -MA_API ma_result ma_decoder_init_file(const char* pFilePath, const ma_decoder_config* pConfig, ma_decoder* pDecoder) -{ - ma_result result; - ma_decoder_config config; - - config = ma_decoder_config_init_copy(pConfig); - result = ma_decoder__preinit_file(pFilePath, &config, pDecoder); - if (result != MA_SUCCESS) { - return result; - } - - /* If the backend has support for loading from a file path we'll want to use that. If that all fails we'll fall back to the VFS path. */ - result = MA_NO_BACKEND; - - if (config.encodingFormat != ma_encoding_format_unknown) { - #ifdef MA_HAS_WAV - if (config.encodingFormat == ma_encoding_format_wav) { - result = ma_decoder_init_wav_from_file__internal(pFilePath, &config, pDecoder); - } - #endif - #ifdef MA_HAS_FLAC - if (config.encodingFormat == ma_encoding_format_flac) { - result = ma_decoder_init_flac_from_file__internal(pFilePath, &config, pDecoder); - } - #endif - #ifdef MA_HAS_MP3 - if (config.encodingFormat == ma_encoding_format_mp3) { - result = ma_decoder_init_mp3_from_file__internal(pFilePath, &config, pDecoder); - } - #endif - #ifdef MA_HAS_VORBIS - if (config.encodingFormat == ma_encoding_format_vorbis) { - result = ma_decoder_init_vorbis_from_file__internal(pFilePath, &config, pDecoder); - } - #endif - } - - if (result != MA_SUCCESS) { - /* Getting here means we weren't able to initialize a decoder of a specific encoding format. */ - - /* - We use trial and error to open a decoder. We prioritize custom decoders so that if they - implement the same encoding format they take priority over the built-in decoders. - */ - result = ma_decoder_init_custom_from_file__internal(pFilePath, &config, pDecoder); - - /* - If we get to this point and we still haven't found a decoder, and the caller has requested a - specific encoding format, there's no hope for it. Abort. - */ - if (result != MA_SUCCESS && config.encodingFormat != ma_encoding_format_unknown) { - return MA_NO_BACKEND; - } - - /* First try loading based on the file extension so we don't waste time opening and closing files. */ - #ifdef MA_HAS_WAV - if (result != MA_SUCCESS && ma_path_extension_equal(pFilePath, "wav")) { - result = ma_decoder_init_wav_from_file__internal(pFilePath, &config, pDecoder); - } - #endif - #ifdef MA_HAS_FLAC - if (result != MA_SUCCESS && ma_path_extension_equal(pFilePath, "flac")) { - result = ma_decoder_init_flac_from_file__internal(pFilePath, &config, pDecoder); - } - #endif - #ifdef MA_HAS_MP3 - if (result != MA_SUCCESS && ma_path_extension_equal(pFilePath, "mp3")) { - result = ma_decoder_init_mp3_from_file__internal(pFilePath, &config, pDecoder); - } - #endif - #ifdef MA_HAS_VORBIS - if (result != MA_SUCCESS && ma_path_extension_equal(pFilePath, "ogg")) { - result = ma_decoder_init_vorbis_from_file__internal(pFilePath, &config, pDecoder); - } - #endif - - /* - If we still haven't got a result just use trial and error. Custom decoders have already been attempted, so here we - need only iterate over our stock decoders. - */ - if (result != MA_SUCCESS) { - #ifdef MA_HAS_WAV - if (result != MA_SUCCESS) { - result = ma_decoder_init_wav_from_file__internal(pFilePath, &config, pDecoder); - } - #endif - #ifdef MA_HAS_FLAC - if (result != MA_SUCCESS) { - result = ma_decoder_init_flac_from_file__internal(pFilePath, &config, pDecoder); - } - #endif - #ifdef MA_HAS_MP3 - if (result != MA_SUCCESS) { - result = ma_decoder_init_mp3_from_file__internal(pFilePath, &config, pDecoder); - } - #endif - #ifdef MA_HAS_VORBIS - if (result != MA_SUCCESS) { - result = ma_decoder_init_vorbis_from_file__internal(pFilePath, &config, pDecoder); - } - #endif - } - } - - /* - If at this point we still haven't successfully initialized the decoder it most likely means - the backend doesn't have an implementation for loading from a file path. We'll try using - miniaudio's built-in file IO for loading file. - */ - if (result == MA_SUCCESS) { - /* Initialization was successful. Finish up. */ - result = ma_decoder__postinit(&config, pDecoder); - if (result != MA_SUCCESS) { - /* - The backend was initialized successfully, but for some reason post-initialization failed. This is most likely - due to an out of memory error. We're going to abort with an error here and not try to recover. - */ - if (pDecoder->pBackendVTable != NULL && pDecoder->pBackendVTable->onUninit != NULL) { - pDecoder->pBackendVTable->onUninit(pDecoder->pBackendUserData, &pDecoder->pBackend, &pDecoder->allocationCallbacks); - } - - return result; - } - } else { - /* Probably no implementation for loading from a file path. Use miniaudio's file IO instead. */ - result = ma_decoder_init_vfs(NULL, pFilePath, pConfig, pDecoder); - if (result != MA_SUCCESS) { - return result; - } - } - - return MA_SUCCESS; -} - -static ma_result ma_decoder__preinit_file_w(const wchar_t* pFilePath, const ma_decoder_config* pConfig, ma_decoder* pDecoder) -{ - ma_result result; - - result = ma_decoder__preinit(NULL, NULL, NULL, NULL, pConfig, pDecoder); - if (result != MA_SUCCESS) { - return result; - } - - if (pFilePath == NULL || pFilePath[0] == '\0') { - return MA_INVALID_ARGS; - } - - return MA_SUCCESS; -} - -MA_API ma_result ma_decoder_init_file_w(const wchar_t* pFilePath, const ma_decoder_config* pConfig, ma_decoder* pDecoder) -{ - ma_result result; - ma_decoder_config config; - - config = ma_decoder_config_init_copy(pConfig); - result = ma_decoder__preinit_file_w(pFilePath, &config, pDecoder); - if (result != MA_SUCCESS) { - return result; - } - - /* If the backend has support for loading from a file path we'll want to use that. If that all fails we'll fall back to the VFS path. */ - result = MA_NO_BACKEND; - - if (config.encodingFormat != ma_encoding_format_unknown) { - #ifdef MA_HAS_WAV - if (config.encodingFormat == ma_encoding_format_wav) { - result = ma_decoder_init_wav_from_file_w__internal(pFilePath, &config, pDecoder); - } - #endif - #ifdef MA_HAS_FLAC - if (config.encodingFormat == ma_encoding_format_flac) { - result = ma_decoder_init_flac_from_file_w__internal(pFilePath, &config, pDecoder); - } - #endif - #ifdef MA_HAS_MP3 - if (config.encodingFormat == ma_encoding_format_mp3) { - result = ma_decoder_init_mp3_from_file_w__internal(pFilePath, &config, pDecoder); - } - #endif - #ifdef MA_HAS_VORBIS - if (config.encodingFormat == ma_encoding_format_vorbis) { - result = ma_decoder_init_vorbis_from_file_w__internal(pFilePath, &config, pDecoder); - } - #endif - } - - if (result != MA_SUCCESS) { - /* Getting here means we weren't able to initialize a decoder of a specific encoding format. */ - - /* - We use trial and error to open a decoder. We prioritize custom decoders so that if they - implement the same encoding format they take priority over the built-in decoders. - */ - result = ma_decoder_init_custom_from_file_w__internal(pFilePath, &config, pDecoder); - - /* - If we get to this point and we still haven't found a decoder, and the caller has requested a - specific encoding format, there's no hope for it. Abort. - */ - if (result != MA_SUCCESS && config.encodingFormat != ma_encoding_format_unknown) { - return MA_NO_BACKEND; - } - - /* First try loading based on the file extension so we don't waste time opening and closing files. */ - #ifdef MA_HAS_WAV - if (result != MA_SUCCESS && ma_path_extension_equal_w(pFilePath, L"wav")) { - result = ma_decoder_init_wav_from_file_w__internal(pFilePath, &config, pDecoder); - } - #endif - #ifdef MA_HAS_FLAC - if (result != MA_SUCCESS && ma_path_extension_equal_w(pFilePath, L"flac")) { - result = ma_decoder_init_flac_from_file_w__internal(pFilePath, &config, pDecoder); - } - #endif - #ifdef MA_HAS_MP3 - if (result != MA_SUCCESS && ma_path_extension_equal_w(pFilePath, L"mp3")) { - result = ma_decoder_init_mp3_from_file_w__internal(pFilePath, &config, pDecoder); - } - #endif - #ifdef MA_HAS_VORBIS - if (result != MA_SUCCESS && ma_path_extension_equal_w(pFilePath, L"ogg")) { - result = ma_decoder_init_vorbis_from_file_w__internal(pFilePath, &config, pDecoder); - } - #endif - - /* - If we still haven't got a result just use trial and error. Custom decoders have already been attempted, so here we - need only iterate over our stock decoders. - */ - if (result != MA_SUCCESS) { - #ifdef MA_HAS_WAV - if (result != MA_SUCCESS) { - result = ma_decoder_init_wav_from_file_w__internal(pFilePath, &config, pDecoder); - } - #endif - #ifdef MA_HAS_FLAC - if (result != MA_SUCCESS) { - result = ma_decoder_init_flac_from_file_w__internal(pFilePath, &config, pDecoder); - } - #endif - #ifdef MA_HAS_MP3 - if (result != MA_SUCCESS) { - result = ma_decoder_init_mp3_from_file_w__internal(pFilePath, &config, pDecoder); - } - #endif - #ifdef MA_HAS_VORBIS - if (result != MA_SUCCESS) { - result = ma_decoder_init_vorbis_from_file_w__internal(pFilePath, &config, pDecoder); - } - #endif - } - } - - /* - If at this point we still haven't successfully initialized the decoder it most likely means - the backend doesn't have an implementation for loading from a file path. We'll try using - miniaudio's built-in file IO for loading file. - */ - if (result == MA_SUCCESS) { - /* Initialization was successful. Finish up. */ - result = ma_decoder__postinit(&config, pDecoder); - if (result != MA_SUCCESS) { - /* - The backend was initialized successfully, but for some reason post-initialization failed. This is most likely - due to an out of memory error. We're going to abort with an error here and not try to recover. - */ - if (pDecoder->pBackendVTable != NULL && pDecoder->pBackendVTable->onUninit != NULL) { - pDecoder->pBackendVTable->onUninit(pDecoder->pBackendUserData, &pDecoder->pBackend, &pDecoder->allocationCallbacks); - } - - return result; - } - } else { - /* Probably no implementation for loading from a file path. Use miniaudio's file IO instead. */ - result = ma_decoder_init_vfs_w(NULL, pFilePath, pConfig, pDecoder); - if (result != MA_SUCCESS) { - return result; - } - } - - return MA_SUCCESS; -} - -MA_API ma_result ma_decoder_uninit(ma_decoder* pDecoder) -{ - if (pDecoder == NULL) { - return MA_INVALID_ARGS; - } - - if (pDecoder->pBackend != NULL) { - if (pDecoder->pBackendVTable != NULL && pDecoder->pBackendVTable->onUninit != NULL) { - pDecoder->pBackendVTable->onUninit(pDecoder->pBackendUserData, pDecoder->pBackend, &pDecoder->allocationCallbacks); - } - } - - if (pDecoder->onRead == ma_decoder__on_read_vfs) { - ma_vfs_or_default_close(pDecoder->data.vfs.pVFS, pDecoder->data.vfs.file); - pDecoder->data.vfs.file = NULL; - } - - ma_data_converter_uninit(&pDecoder->converter, &pDecoder->allocationCallbacks); - ma_data_source_uninit(&pDecoder->ds); - - if (pDecoder->pInputCache != NULL) { - ma_free(pDecoder->pInputCache, &pDecoder->allocationCallbacks); - } - - return MA_SUCCESS; -} - -MA_API ma_result ma_decoder_read_pcm_frames(ma_decoder* pDecoder, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead) -{ - ma_result result = MA_SUCCESS; - ma_uint64 totalFramesReadOut; - void* pRunningFramesOut; - - if (pFramesRead != NULL) { - *pFramesRead = 0; /* Safety. */ - } - - if (frameCount == 0) { - return MA_INVALID_ARGS; - } - - if (pDecoder == NULL) { - return MA_INVALID_ARGS; - } - - if (pDecoder->pBackend == NULL) { - return MA_INVALID_OPERATION; - } - - /* Fast path. */ - if (pDecoder->converter.isPassthrough) { - result = ma_data_source_read_pcm_frames(pDecoder->pBackend, pFramesOut, frameCount, &totalFramesReadOut); - } else { - /* - Getting here means we need to do data conversion. If we're seeking forward and are _not_ doing resampling we can run this in a fast path. If we're doing resampling we - need to run through each sample because we need to ensure its internal cache is updated. - */ - if (pFramesOut == NULL && pDecoder->converter.hasResampler == MA_FALSE) { - result = ma_data_source_read_pcm_frames(pDecoder->pBackend, NULL, frameCount, &totalFramesReadOut); - } else { - /* Slow path. Need to run everything through the data converter. */ - ma_format internalFormat; - ma_uint32 internalChannels; - - totalFramesReadOut = 0; - pRunningFramesOut = pFramesOut; - - result = ma_data_source_get_data_format(pDecoder->pBackend, &internalFormat, &internalChannels, NULL, NULL, 0); - if (result != MA_SUCCESS) { - return result; /* Failed to retrieve the internal format and channel count. */ - } - - /* - We run a different path depending on whether or not we are using a heap-allocated - intermediary buffer or not. If the data converter does not support the calculation of - the required number of input frames, we'll use the heap-allocated path. Otherwise we'll - use the stack-allocated path. - */ - if (pDecoder->pInputCache != NULL) { - /* We don't have a way of determining the required number of input frames, so need to persistently store input data in a cache. */ - while (totalFramesReadOut < frameCount) { - ma_uint64 framesToReadThisIterationIn; - ma_uint64 framesToReadThisIterationOut; - - /* If there's any data available in the cache, that needs to get processed first. */ - if (pDecoder->inputCacheRemaining > 0) { - framesToReadThisIterationOut = (frameCount - totalFramesReadOut); - framesToReadThisIterationIn = framesToReadThisIterationOut; - if (framesToReadThisIterationIn > pDecoder->inputCacheRemaining) { - framesToReadThisIterationIn = pDecoder->inputCacheRemaining; - } - - result = ma_data_converter_process_pcm_frames(&pDecoder->converter, ma_offset_pcm_frames_ptr(pDecoder->pInputCache, pDecoder->inputCacheConsumed, internalFormat, internalChannels), &framesToReadThisIterationIn, pRunningFramesOut, &framesToReadThisIterationOut); - if (result != MA_SUCCESS) { - break; - } - - pDecoder->inputCacheConsumed += framesToReadThisIterationIn; - pDecoder->inputCacheRemaining -= framesToReadThisIterationIn; - - totalFramesReadOut += framesToReadThisIterationOut; - - if (pRunningFramesOut != NULL) { - pRunningFramesOut = ma_offset_ptr(pRunningFramesOut, framesToReadThisIterationOut * ma_get_bytes_per_frame(pDecoder->outputFormat, pDecoder->outputChannels)); - } - - if (framesToReadThisIterationIn == 0 && framesToReadThisIterationOut == 0) { - break; /* We're done. */ - } - } - - /* Getting here means there's no data in the cache and we need to fill it up from the data source. */ - if (pDecoder->inputCacheRemaining == 0) { - pDecoder->inputCacheConsumed = 0; - - result = ma_data_source_read_pcm_frames(pDecoder->pBackend, pDecoder->pInputCache, pDecoder->inputCacheCap, &pDecoder->inputCacheRemaining); - if (result != MA_SUCCESS) { - break; - } - } - } - } else { - /* We have a way of determining the required number of input frames so just use the stack. */ - while (totalFramesReadOut < frameCount) { - ma_uint8 pIntermediaryBuffer[MA_DATA_CONVERTER_STACK_BUFFER_SIZE]; /* In internal format. */ - ma_uint64 intermediaryBufferCap = sizeof(pIntermediaryBuffer) / ma_get_bytes_per_frame(internalFormat, internalChannels); - ma_uint64 framesToReadThisIterationIn; - ma_uint64 framesReadThisIterationIn; - ma_uint64 framesToReadThisIterationOut; - ma_uint64 framesReadThisIterationOut; - ma_uint64 requiredInputFrameCount; - - framesToReadThisIterationOut = (frameCount - totalFramesReadOut); - framesToReadThisIterationIn = framesToReadThisIterationOut; - if (framesToReadThisIterationIn > intermediaryBufferCap) { - framesToReadThisIterationIn = intermediaryBufferCap; - } - - ma_data_converter_get_required_input_frame_count(&pDecoder->converter, framesToReadThisIterationOut, &requiredInputFrameCount); - if (framesToReadThisIterationIn > requiredInputFrameCount) { - framesToReadThisIterationIn = requiredInputFrameCount; - } - - if (requiredInputFrameCount > 0) { - result = ma_data_source_read_pcm_frames(pDecoder->pBackend, pIntermediaryBuffer, framesToReadThisIterationIn, &framesReadThisIterationIn); - - /* - Note here that even if we've reached the end, we don't want to abort because there might be more output frames needing to be - generated from cached input data, which might happen if resampling is being performed. - */ - if (result != MA_SUCCESS && result != MA_AT_END) { - break; - } - } else { - framesReadThisIterationIn = 0; - pIntermediaryBuffer[0] = 0; /* <-- This is just to silence a static analysis warning. */ - } - - /* - At this point we have our decoded data in input format and now we need to convert to output format. Note that even if we didn't read any - input frames, we still want to try processing frames because there may some output frames generated from cached input data. - */ - framesReadThisIterationOut = framesToReadThisIterationOut; - result = ma_data_converter_process_pcm_frames(&pDecoder->converter, pIntermediaryBuffer, &framesReadThisIterationIn, pRunningFramesOut, &framesReadThisIterationOut); - if (result != MA_SUCCESS) { - break; - } - - totalFramesReadOut += framesReadThisIterationOut; - - if (pRunningFramesOut != NULL) { - pRunningFramesOut = ma_offset_ptr(pRunningFramesOut, framesReadThisIterationOut * ma_get_bytes_per_frame(pDecoder->outputFormat, pDecoder->outputChannels)); - } - - if (framesReadThisIterationIn == 0 && framesReadThisIterationOut == 0) { - break; /* We're done. */ - } - } - } - } - } - - pDecoder->readPointerInPCMFrames += totalFramesReadOut; - - if (pFramesRead != NULL) { - *pFramesRead = totalFramesReadOut; - } - - if (result == MA_SUCCESS && totalFramesReadOut == 0) { - result = MA_AT_END; - } - - return result; -} - -MA_API ma_result ma_decoder_seek_to_pcm_frame(ma_decoder* pDecoder, ma_uint64 frameIndex) -{ - if (pDecoder == NULL) { - return MA_INVALID_ARGS; - } - - if (pDecoder->pBackend != NULL) { - ma_result result; - ma_uint64 internalFrameIndex; - ma_uint32 internalSampleRate; - ma_uint64 currentFrameIndex; - - result = ma_data_source_get_data_format(pDecoder->pBackend, NULL, NULL, &internalSampleRate, NULL, 0); - if (result != MA_SUCCESS) { - return result; /* Failed to retrieve the internal sample rate. */ - } - - if (internalSampleRate == pDecoder->outputSampleRate) { - internalFrameIndex = frameIndex; - } else { - internalFrameIndex = ma_calculate_frame_count_after_resampling(internalSampleRate, pDecoder->outputSampleRate, frameIndex); - } - - /* Only seek if we're requesting a different frame to what we're currently sitting on. */ - ma_data_source_get_cursor_in_pcm_frames(pDecoder->pBackend, ¤tFrameIndex); - if (currentFrameIndex != internalFrameIndex) { - result = ma_data_source_seek_to_pcm_frame(pDecoder->pBackend, internalFrameIndex); - if (result == MA_SUCCESS) { - pDecoder->readPointerInPCMFrames = frameIndex; - } - - /* Reset the data converter so that any cached data in the resampler is cleared. */ - ma_data_converter_reset(&pDecoder->converter); - } - - return result; - } - - /* Should never get here, but if we do it means onSeekToPCMFrame was not set by the backend. */ - return MA_INVALID_ARGS; -} - -MA_API ma_result ma_decoder_get_data_format(ma_decoder* pDecoder, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate, ma_channel* pChannelMap, size_t channelMapCap) -{ - if (pDecoder == NULL) { - return MA_INVALID_ARGS; - } - - if (pFormat != NULL) { - *pFormat = pDecoder->outputFormat; - } - - if (pChannels != NULL) { - *pChannels = pDecoder->outputChannels; - } - - if (pSampleRate != NULL) { - *pSampleRate = pDecoder->outputSampleRate; - } - - if (pChannelMap != NULL) { - ma_data_converter_get_output_channel_map(&pDecoder->converter, pChannelMap, channelMapCap); - } - - return MA_SUCCESS; -} - -MA_API ma_result ma_decoder_get_cursor_in_pcm_frames(ma_decoder* pDecoder, ma_uint64* pCursor) -{ - if (pCursor == NULL) { - return MA_INVALID_ARGS; - } - - *pCursor = 0; - - if (pDecoder == NULL) { - return MA_INVALID_ARGS; - } - - *pCursor = pDecoder->readPointerInPCMFrames; - - return MA_SUCCESS; -} - -MA_API ma_result ma_decoder_get_length_in_pcm_frames(ma_decoder* pDecoder, ma_uint64* pLength) -{ - if (pLength == NULL) { - return MA_INVALID_ARGS; - } - - *pLength = 0; - - if (pDecoder == NULL) { - return MA_INVALID_ARGS; - } - - if (pDecoder->pBackend != NULL) { - ma_result result; - ma_uint64 internalLengthInPCMFrames; - ma_uint32 internalSampleRate; - - result = ma_data_source_get_length_in_pcm_frames(pDecoder->pBackend, &internalLengthInPCMFrames); - if (result != MA_SUCCESS) { - return result; /* Failed to retrieve the internal length. */ - } - - result = ma_data_source_get_data_format(pDecoder->pBackend, NULL, NULL, &internalSampleRate, NULL, 0); - if (result != MA_SUCCESS) { - return result; /* Failed to retrieve the internal sample rate. */ - } - - if (internalSampleRate == pDecoder->outputSampleRate) { - *pLength = internalLengthInPCMFrames; - } else { - *pLength = ma_calculate_frame_count_after_resampling(pDecoder->outputSampleRate, internalSampleRate, internalLengthInPCMFrames); - } - - return MA_SUCCESS; - } else { - return MA_NO_BACKEND; - } -} - -MA_API ma_result ma_decoder_get_available_frames(ma_decoder* pDecoder, ma_uint64* pAvailableFrames) -{ - ma_result result; - ma_uint64 totalFrameCount; - - if (pAvailableFrames == NULL) { - return MA_INVALID_ARGS; - } - - *pAvailableFrames = 0; - - if (pDecoder == NULL) { - return MA_INVALID_ARGS; - } - - result = ma_decoder_get_length_in_pcm_frames(pDecoder, &totalFrameCount); - if (result != MA_SUCCESS) { - return result; - } - - if (totalFrameCount <= pDecoder->readPointerInPCMFrames) { - *pAvailableFrames = 0; - } else { - *pAvailableFrames = totalFrameCount - pDecoder->readPointerInPCMFrames; - } - - return MA_SUCCESS; -} - - -static ma_result ma_decoder__full_decode_and_uninit(ma_decoder* pDecoder, ma_decoder_config* pConfigOut, ma_uint64* pFrameCountOut, void** ppPCMFramesOut) -{ - ma_result result; - ma_uint64 totalFrameCount; - ma_uint64 bpf; - ma_uint64 dataCapInFrames; - void* pPCMFramesOut; - - MA_ASSERT(pDecoder != NULL); - - totalFrameCount = 0; - bpf = ma_get_bytes_per_frame(pDecoder->outputFormat, pDecoder->outputChannels); - - /* The frame count is unknown until we try reading. Thus, we just run in a loop. */ - dataCapInFrames = 0; - pPCMFramesOut = NULL; - for (;;) { - ma_uint64 frameCountToTryReading; - ma_uint64 framesJustRead; - - /* Make room if there's not enough. */ - if (totalFrameCount == dataCapInFrames) { - void* pNewPCMFramesOut; - ma_uint64 newDataCapInFrames = dataCapInFrames*2; - if (newDataCapInFrames == 0) { - newDataCapInFrames = 4096; - } - - if ((newDataCapInFrames * bpf) > MA_SIZE_MAX) { - ma_free(pPCMFramesOut, &pDecoder->allocationCallbacks); - return MA_TOO_BIG; - } - - pNewPCMFramesOut = (void*)ma_realloc(pPCMFramesOut, (size_t)(newDataCapInFrames * bpf), &pDecoder->allocationCallbacks); - if (pNewPCMFramesOut == NULL) { - ma_free(pPCMFramesOut, &pDecoder->allocationCallbacks); - return MA_OUT_OF_MEMORY; - } - - dataCapInFrames = newDataCapInFrames; - pPCMFramesOut = pNewPCMFramesOut; - } - - frameCountToTryReading = dataCapInFrames - totalFrameCount; - MA_ASSERT(frameCountToTryReading > 0); - - result = ma_decoder_read_pcm_frames(pDecoder, (ma_uint8*)pPCMFramesOut + (totalFrameCount * bpf), frameCountToTryReading, &framesJustRead); - totalFrameCount += framesJustRead; - - if (result != MA_SUCCESS) { - break; - } - - if (framesJustRead < frameCountToTryReading) { - break; - } - } - - - if (pConfigOut != NULL) { - pConfigOut->format = pDecoder->outputFormat; - pConfigOut->channels = pDecoder->outputChannels; - pConfigOut->sampleRate = pDecoder->outputSampleRate; - } - - if (ppPCMFramesOut != NULL) { - *ppPCMFramesOut = pPCMFramesOut; - } else { - ma_free(pPCMFramesOut, &pDecoder->allocationCallbacks); - } - - if (pFrameCountOut != NULL) { - *pFrameCountOut = totalFrameCount; - } - - ma_decoder_uninit(pDecoder); - return MA_SUCCESS; -} - -MA_API ma_result ma_decode_from_vfs(ma_vfs* pVFS, const char* pFilePath, ma_decoder_config* pConfig, ma_uint64* pFrameCountOut, void** ppPCMFramesOut) -{ - ma_result result; - ma_decoder_config config; - ma_decoder decoder; - - if (pFrameCountOut != NULL) { - *pFrameCountOut = 0; - } - if (ppPCMFramesOut != NULL) { - *ppPCMFramesOut = NULL; - } - - config = ma_decoder_config_init_copy(pConfig); - - result = ma_decoder_init_vfs(pVFS, pFilePath, &config, &decoder); - if (result != MA_SUCCESS) { - return result; - } - - result = ma_decoder__full_decode_and_uninit(&decoder, pConfig, pFrameCountOut, ppPCMFramesOut); - - return result; -} - -MA_API ma_result ma_decode_file(const char* pFilePath, ma_decoder_config* pConfig, ma_uint64* pFrameCountOut, void** ppPCMFramesOut) -{ - return ma_decode_from_vfs(NULL, pFilePath, pConfig, pFrameCountOut, ppPCMFramesOut); -} - -MA_API ma_result ma_decode_memory(const void* pData, size_t dataSize, ma_decoder_config* pConfig, ma_uint64* pFrameCountOut, void** ppPCMFramesOut) -{ - ma_decoder_config config; - ma_decoder decoder; - ma_result result; - - if (pFrameCountOut != NULL) { - *pFrameCountOut = 0; - } - if (ppPCMFramesOut != NULL) { - *ppPCMFramesOut = NULL; - } - - if (pData == NULL || dataSize == 0) { - return MA_INVALID_ARGS; - } - - config = ma_decoder_config_init_copy(pConfig); - - result = ma_decoder_init_memory(pData, dataSize, &config, &decoder); - if (result != MA_SUCCESS) { - return result; - } - - return ma_decoder__full_decode_and_uninit(&decoder, pConfig, pFrameCountOut, ppPCMFramesOut); -} -#endif /* MA_NO_DECODING */ - - -#ifndef MA_NO_ENCODING - -#if defined(MA_HAS_WAV) -static size_t ma_encoder__internal_on_write_wav(void* pUserData, const void* pData, size_t bytesToWrite) -{ - ma_encoder* pEncoder = (ma_encoder*)pUserData; - size_t bytesWritten = 0; - - MA_ASSERT(pEncoder != NULL); - - pEncoder->onWrite(pEncoder, pData, bytesToWrite, &bytesWritten); - return bytesWritten; -} - -static ma_bool32 ma_encoder__internal_on_seek_wav(void* pUserData, int offset, ma_dr_wav_seek_origin origin) -{ - ma_encoder* pEncoder = (ma_encoder*)pUserData; - ma_result result; - - MA_ASSERT(pEncoder != NULL); - - result = pEncoder->onSeek(pEncoder, offset, (origin == ma_dr_wav_seek_origin_start) ? ma_seek_origin_start : ma_seek_origin_current); - if (result != MA_SUCCESS) { - return MA_FALSE; - } else { - return MA_TRUE; - } -} - -static ma_result ma_encoder__on_init_wav(ma_encoder* pEncoder) -{ - ma_dr_wav_data_format wavFormat; - ma_allocation_callbacks allocationCallbacks; - ma_dr_wav* pWav; - - MA_ASSERT(pEncoder != NULL); - - pWav = (ma_dr_wav*)ma_malloc(sizeof(*pWav), &pEncoder->config.allocationCallbacks); - if (pWav == NULL) { - return MA_OUT_OF_MEMORY; - } - - wavFormat.container = ma_dr_wav_container_riff; - wavFormat.channels = pEncoder->config.channels; - wavFormat.sampleRate = pEncoder->config.sampleRate; - wavFormat.bitsPerSample = ma_get_bytes_per_sample(pEncoder->config.format) * 8; - if (pEncoder->config.format == ma_format_f32) { - wavFormat.format = MA_DR_WAVE_FORMAT_IEEE_FLOAT; - } else { - wavFormat.format = MA_DR_WAVE_FORMAT_PCM; - } - - allocationCallbacks.pUserData = pEncoder->config.allocationCallbacks.pUserData; - allocationCallbacks.onMalloc = pEncoder->config.allocationCallbacks.onMalloc; - allocationCallbacks.onRealloc = pEncoder->config.allocationCallbacks.onRealloc; - allocationCallbacks.onFree = pEncoder->config.allocationCallbacks.onFree; - - if (!ma_dr_wav_init_write(pWav, &wavFormat, ma_encoder__internal_on_write_wav, ma_encoder__internal_on_seek_wav, pEncoder, &allocationCallbacks)) { - return MA_ERROR; - } - - pEncoder->pInternalEncoder = pWav; - - return MA_SUCCESS; -} - -static void ma_encoder__on_uninit_wav(ma_encoder* pEncoder) -{ - ma_dr_wav* pWav; - - MA_ASSERT(pEncoder != NULL); - - pWav = (ma_dr_wav*)pEncoder->pInternalEncoder; - MA_ASSERT(pWav != NULL); - - ma_dr_wav_uninit(pWav); - ma_free(pWav, &pEncoder->config.allocationCallbacks); -} - -static ma_result ma_encoder__on_write_pcm_frames_wav(ma_encoder* pEncoder, const void* pFramesIn, ma_uint64 frameCount, ma_uint64* pFramesWritten) -{ - ma_dr_wav* pWav; - ma_uint64 framesWritten; - - MA_ASSERT(pEncoder != NULL); - - pWav = (ma_dr_wav*)pEncoder->pInternalEncoder; - MA_ASSERT(pWav != NULL); - - framesWritten = ma_dr_wav_write_pcm_frames(pWav, frameCount, pFramesIn); - - if (pFramesWritten != NULL) { - *pFramesWritten = framesWritten; - } - - return MA_SUCCESS; -} -#endif - -MA_API ma_encoder_config ma_encoder_config_init(ma_encoding_format encodingFormat, ma_format format, ma_uint32 channels, ma_uint32 sampleRate) -{ - ma_encoder_config config; - - MA_ZERO_OBJECT(&config); - config.encodingFormat = encodingFormat; - config.format = format; - config.channels = channels; - config.sampleRate = sampleRate; - - return config; -} - -MA_API ma_result ma_encoder_preinit(const ma_encoder_config* pConfig, ma_encoder* pEncoder) -{ - ma_result result; - - if (pEncoder == NULL) { - return MA_INVALID_ARGS; - } - - MA_ZERO_OBJECT(pEncoder); - - if (pConfig == NULL) { - return MA_INVALID_ARGS; - } - - if (pConfig->format == ma_format_unknown || pConfig->channels == 0 || pConfig->sampleRate == 0) { - return MA_INVALID_ARGS; - } - - pEncoder->config = *pConfig; - - result = ma_allocation_callbacks_init_copy(&pEncoder->config.allocationCallbacks, &pConfig->allocationCallbacks); - if (result != MA_SUCCESS) { - return result; - } - - return MA_SUCCESS; -} - -MA_API ma_result ma_encoder_init__internal(ma_encoder_write_proc onWrite, ma_encoder_seek_proc onSeek, void* pUserData, ma_encoder* pEncoder) -{ - ma_result result = MA_SUCCESS; - - /* This assumes ma_encoder_preinit() has been called prior. */ - MA_ASSERT(pEncoder != NULL); - - if (onWrite == NULL || onSeek == NULL) { - return MA_INVALID_ARGS; - } - - pEncoder->onWrite = onWrite; - pEncoder->onSeek = onSeek; - pEncoder->pUserData = pUserData; - - switch (pEncoder->config.encodingFormat) - { - case ma_encoding_format_wav: - { - #if defined(MA_HAS_WAV) - pEncoder->onInit = ma_encoder__on_init_wav; - pEncoder->onUninit = ma_encoder__on_uninit_wav; - pEncoder->onWritePCMFrames = ma_encoder__on_write_pcm_frames_wav; - #else - result = MA_NO_BACKEND; - #endif - } break; - - default: - { - result = MA_INVALID_ARGS; - } break; - } - - /* Getting here means we should have our backend callbacks set up. */ - if (result == MA_SUCCESS) { - result = pEncoder->onInit(pEncoder); - } - - return result; -} - -static ma_result ma_encoder__on_write_vfs(ma_encoder* pEncoder, const void* pBufferIn, size_t bytesToWrite, size_t* pBytesWritten) -{ - return ma_vfs_or_default_write(pEncoder->data.vfs.pVFS, pEncoder->data.vfs.file, pBufferIn, bytesToWrite, pBytesWritten); -} - -static ma_result ma_encoder__on_seek_vfs(ma_encoder* pEncoder, ma_int64 offset, ma_seek_origin origin) -{ - return ma_vfs_or_default_seek(pEncoder->data.vfs.pVFS, pEncoder->data.vfs.file, offset, origin); -} - -MA_API ma_result ma_encoder_init_vfs(ma_vfs* pVFS, const char* pFilePath, const ma_encoder_config* pConfig, ma_encoder* pEncoder) -{ - ma_result result; - ma_vfs_file file; - - result = ma_encoder_preinit(pConfig, pEncoder); - if (result != MA_SUCCESS) { - return result; - } - - /* Now open the file. If this fails we don't need to uninitialize the encoder. */ - result = ma_vfs_or_default_open(pVFS, pFilePath, MA_OPEN_MODE_WRITE, &file); - if (result != MA_SUCCESS) { - return result; - } - - pEncoder->data.vfs.pVFS = pVFS; - pEncoder->data.vfs.file = file; - - result = ma_encoder_init__internal(ma_encoder__on_write_vfs, ma_encoder__on_seek_vfs, NULL, pEncoder); - if (result != MA_SUCCESS) { - ma_vfs_or_default_close(pVFS, file); - return result; - } - - return MA_SUCCESS; -} - -MA_API ma_result ma_encoder_init_vfs_w(ma_vfs* pVFS, const wchar_t* pFilePath, const ma_encoder_config* pConfig, ma_encoder* pEncoder) -{ - ma_result result; - ma_vfs_file file; - - result = ma_encoder_preinit(pConfig, pEncoder); - if (result != MA_SUCCESS) { - return result; - } - - /* Now open the file. If this fails we don't need to uninitialize the encoder. */ - result = ma_vfs_or_default_open_w(pVFS, pFilePath, MA_OPEN_MODE_WRITE, &file); - if (result != MA_SUCCESS) { - return result; - } - - pEncoder->data.vfs.pVFS = pVFS; - pEncoder->data.vfs.file = file; - - result = ma_encoder_init__internal(ma_encoder__on_write_vfs, ma_encoder__on_seek_vfs, NULL, pEncoder); - if (result != MA_SUCCESS) { - ma_vfs_or_default_close(pVFS, file); - return result; - } - - return MA_SUCCESS; -} - -MA_API ma_result ma_encoder_init_file(const char* pFilePath, const ma_encoder_config* pConfig, ma_encoder* pEncoder) -{ - return ma_encoder_init_vfs(NULL, pFilePath, pConfig, pEncoder); -} - -MA_API ma_result ma_encoder_init_file_w(const wchar_t* pFilePath, const ma_encoder_config* pConfig, ma_encoder* pEncoder) -{ - return ma_encoder_init_vfs_w(NULL, pFilePath, pConfig, pEncoder); -} - -MA_API ma_result ma_encoder_init(ma_encoder_write_proc onWrite, ma_encoder_seek_proc onSeek, void* pUserData, const ma_encoder_config* pConfig, ma_encoder* pEncoder) -{ - ma_result result; - - result = ma_encoder_preinit(pConfig, pEncoder); - if (result != MA_SUCCESS) { - return result; - } - - return ma_encoder_init__internal(onWrite, onSeek, pUserData, pEncoder); -} - - -MA_API void ma_encoder_uninit(ma_encoder* pEncoder) -{ - if (pEncoder == NULL) { - return; - } - - if (pEncoder->onUninit) { - pEncoder->onUninit(pEncoder); - } - - /* If we have a file handle, close it. */ - if (pEncoder->onWrite == ma_encoder__on_write_vfs) { - ma_vfs_or_default_close(pEncoder->data.vfs.pVFS, pEncoder->data.vfs.file); - pEncoder->data.vfs.file = NULL; - } -} - - -MA_API ma_result ma_encoder_write_pcm_frames(ma_encoder* pEncoder, const void* pFramesIn, ma_uint64 frameCount, ma_uint64* pFramesWritten) -{ - if (pFramesWritten != NULL) { - *pFramesWritten = 0; - } - - if (pEncoder == NULL || pFramesIn == NULL) { - return MA_INVALID_ARGS; - } - - return pEncoder->onWritePCMFrames(pEncoder, pFramesIn, frameCount, pFramesWritten); -} -#endif /* MA_NO_ENCODING */ - - - -/************************************************************************************************************************************************************** - -Generation - -**************************************************************************************************************************************************************/ -#ifndef MA_NO_GENERATION -MA_API ma_waveform_config ma_waveform_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRate, ma_waveform_type type, double amplitude, double frequency) -{ - ma_waveform_config config; - - MA_ZERO_OBJECT(&config); - config.format = format; - config.channels = channels; - config.sampleRate = sampleRate; - config.type = type; - config.amplitude = amplitude; - config.frequency = frequency; - - return config; -} - -static ma_result ma_waveform__data_source_on_read(ma_data_source* pDataSource, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead) -{ - return ma_waveform_read_pcm_frames((ma_waveform*)pDataSource, pFramesOut, frameCount, pFramesRead); -} - -static ma_result ma_waveform__data_source_on_seek(ma_data_source* pDataSource, ma_uint64 frameIndex) -{ - return ma_waveform_seek_to_pcm_frame((ma_waveform*)pDataSource, frameIndex); -} - -static ma_result ma_waveform__data_source_on_get_data_format(ma_data_source* pDataSource, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate, ma_channel* pChannelMap, size_t channelMapCap) -{ - ma_waveform* pWaveform = (ma_waveform*)pDataSource; - - *pFormat = pWaveform->config.format; - *pChannels = pWaveform->config.channels; - *pSampleRate = pWaveform->config.sampleRate; - ma_channel_map_init_standard(ma_standard_channel_map_default, pChannelMap, channelMapCap, pWaveform->config.channels); - - return MA_SUCCESS; -} - -static ma_result ma_waveform__data_source_on_get_cursor(ma_data_source* pDataSource, ma_uint64* pCursor) -{ - ma_waveform* pWaveform = (ma_waveform*)pDataSource; - - *pCursor = (ma_uint64)(pWaveform->time / pWaveform->advance); - - return MA_SUCCESS; -} - -static double ma_waveform__calculate_advance(ma_uint32 sampleRate, double frequency) -{ - return (1.0 / (sampleRate / frequency)); -} - -static void ma_waveform__update_advance(ma_waveform* pWaveform) -{ - pWaveform->advance = ma_waveform__calculate_advance(pWaveform->config.sampleRate, pWaveform->config.frequency); -} - -static ma_data_source_vtable g_ma_waveform_data_source_vtable = -{ - ma_waveform__data_source_on_read, - ma_waveform__data_source_on_seek, - ma_waveform__data_source_on_get_data_format, - ma_waveform__data_source_on_get_cursor, - NULL, /* onGetLength. There's no notion of a length in waveforms. */ - NULL, /* onSetLooping */ - 0 -}; - -MA_API ma_result ma_waveform_init(const ma_waveform_config* pConfig, ma_waveform* pWaveform) -{ - ma_result result; - ma_data_source_config dataSourceConfig; - - if (pWaveform == NULL) { - return MA_INVALID_ARGS; - } - - MA_ZERO_OBJECT(pWaveform); - - dataSourceConfig = ma_data_source_config_init(); - dataSourceConfig.vtable = &g_ma_waveform_data_source_vtable; - - result = ma_data_source_init(&dataSourceConfig, &pWaveform->ds); - if (result != MA_SUCCESS) { - return result; - } - - pWaveform->config = *pConfig; - pWaveform->advance = ma_waveform__calculate_advance(pWaveform->config.sampleRate, pWaveform->config.frequency); - pWaveform->time = 0; - - return MA_SUCCESS; -} - -MA_API void ma_waveform_uninit(ma_waveform* pWaveform) -{ - if (pWaveform == NULL) { - return; - } - - ma_data_source_uninit(&pWaveform->ds); -} - -MA_API ma_result ma_waveform_set_amplitude(ma_waveform* pWaveform, double amplitude) -{ - if (pWaveform == NULL) { - return MA_INVALID_ARGS; - } - - pWaveform->config.amplitude = amplitude; - return MA_SUCCESS; -} - -MA_API ma_result ma_waveform_set_frequency(ma_waveform* pWaveform, double frequency) -{ - if (pWaveform == NULL) { - return MA_INVALID_ARGS; - } - - pWaveform->config.frequency = frequency; - ma_waveform__update_advance(pWaveform); - - return MA_SUCCESS; -} - -MA_API ma_result ma_waveform_set_type(ma_waveform* pWaveform, ma_waveform_type type) -{ - if (pWaveform == NULL) { - return MA_INVALID_ARGS; - } - - pWaveform->config.type = type; - return MA_SUCCESS; -} - -MA_API ma_result ma_waveform_set_sample_rate(ma_waveform* pWaveform, ma_uint32 sampleRate) -{ - if (pWaveform == NULL) { - return MA_INVALID_ARGS; - } - - pWaveform->config.sampleRate = sampleRate; - ma_waveform__update_advance(pWaveform); - - return MA_SUCCESS; -} - -static float ma_waveform_sine_f32(double time, double amplitude) -{ - return (float)(ma_sind(MA_TAU_D * time) * amplitude); -} - -static ma_int16 ma_waveform_sine_s16(double time, double amplitude) -{ - return ma_pcm_sample_f32_to_s16(ma_waveform_sine_f32(time, amplitude)); -} - -static float ma_waveform_square_f32(double time, double dutyCycle, double amplitude) -{ - double f = time - (ma_int64)time; - double r; - - if (f < dutyCycle) { - r = amplitude; - } else { - r = -amplitude; - } - - return (float)r; -} - -static ma_int16 ma_waveform_square_s16(double time, double dutyCycle, double amplitude) -{ - return ma_pcm_sample_f32_to_s16(ma_waveform_square_f32(time, dutyCycle, amplitude)); -} - -static float ma_waveform_triangle_f32(double time, double amplitude) -{ - double f = time - (ma_int64)time; - double r; - - r = 2 * ma_abs(2 * (f - 0.5)) - 1; - - return (float)(r * amplitude); -} - -static ma_int16 ma_waveform_triangle_s16(double time, double amplitude) -{ - return ma_pcm_sample_f32_to_s16(ma_waveform_triangle_f32(time, amplitude)); -} - -static float ma_waveform_sawtooth_f32(double time, double amplitude) -{ - double f = time - (ma_int64)time; - double r; - - r = 2 * (f - 0.5); - - return (float)(r * amplitude); -} - -static ma_int16 ma_waveform_sawtooth_s16(double time, double amplitude) -{ - return ma_pcm_sample_f32_to_s16(ma_waveform_sawtooth_f32(time, amplitude)); -} - -static void ma_waveform_read_pcm_frames__sine(ma_waveform* pWaveform, void* pFramesOut, ma_uint64 frameCount) -{ - ma_uint64 iFrame; - ma_uint64 iChannel; - ma_uint32 bps = ma_get_bytes_per_sample(pWaveform->config.format); - ma_uint32 bpf = bps * pWaveform->config.channels; - - MA_ASSERT(pWaveform != NULL); - MA_ASSERT(pFramesOut != NULL); - - if (pWaveform->config.format == ma_format_f32) { - float* pFramesOutF32 = (float*)pFramesOut; - for (iFrame = 0; iFrame < frameCount; iFrame += 1) { - float s = ma_waveform_sine_f32(pWaveform->time, pWaveform->config.amplitude); - pWaveform->time += pWaveform->advance; - - for (iChannel = 0; iChannel < pWaveform->config.channels; iChannel += 1) { - pFramesOutF32[iFrame*pWaveform->config.channels + iChannel] = s; - } - } - } else if (pWaveform->config.format == ma_format_s16) { - ma_int16* pFramesOutS16 = (ma_int16*)pFramesOut; - for (iFrame = 0; iFrame < frameCount; iFrame += 1) { - ma_int16 s = ma_waveform_sine_s16(pWaveform->time, pWaveform->config.amplitude); - pWaveform->time += pWaveform->advance; - - for (iChannel = 0; iChannel < pWaveform->config.channels; iChannel += 1) { - pFramesOutS16[iFrame*pWaveform->config.channels + iChannel] = s; - } - } - } else { - for (iFrame = 0; iFrame < frameCount; iFrame += 1) { - float s = ma_waveform_sine_f32(pWaveform->time, pWaveform->config.amplitude); - pWaveform->time += pWaveform->advance; - - for (iChannel = 0; iChannel < pWaveform->config.channels; iChannel += 1) { - ma_pcm_convert(ma_offset_ptr(pFramesOut, iFrame*bpf + iChannel*bps), pWaveform->config.format, &s, ma_format_f32, 1, ma_dither_mode_none); - } - } - } -} - -static void ma_waveform_read_pcm_frames__square(ma_waveform* pWaveform, double dutyCycle, void* pFramesOut, ma_uint64 frameCount) -{ - ma_uint64 iFrame; - ma_uint64 iChannel; - ma_uint32 bps = ma_get_bytes_per_sample(pWaveform->config.format); - ma_uint32 bpf = bps * pWaveform->config.channels; - - MA_ASSERT(pWaveform != NULL); - MA_ASSERT(pFramesOut != NULL); - - if (pWaveform->config.format == ma_format_f32) { - float* pFramesOutF32 = (float*)pFramesOut; - for (iFrame = 0; iFrame < frameCount; iFrame += 1) { - float s = ma_waveform_square_f32(pWaveform->time, dutyCycle, pWaveform->config.amplitude); - pWaveform->time += pWaveform->advance; - - for (iChannel = 0; iChannel < pWaveform->config.channels; iChannel += 1) { - pFramesOutF32[iFrame*pWaveform->config.channels + iChannel] = s; - } - } - } else if (pWaveform->config.format == ma_format_s16) { - ma_int16* pFramesOutS16 = (ma_int16*)pFramesOut; - for (iFrame = 0; iFrame < frameCount; iFrame += 1) { - ma_int16 s = ma_waveform_square_s16(pWaveform->time, dutyCycle, pWaveform->config.amplitude); - pWaveform->time += pWaveform->advance; - - for (iChannel = 0; iChannel < pWaveform->config.channels; iChannel += 1) { - pFramesOutS16[iFrame*pWaveform->config.channels + iChannel] = s; - } - } - } else { - for (iFrame = 0; iFrame < frameCount; iFrame += 1) { - float s = ma_waveform_square_f32(pWaveform->time, dutyCycle, pWaveform->config.amplitude); - pWaveform->time += pWaveform->advance; - - for (iChannel = 0; iChannel < pWaveform->config.channels; iChannel += 1) { - ma_pcm_convert(ma_offset_ptr(pFramesOut, iFrame*bpf + iChannel*bps), pWaveform->config.format, &s, ma_format_f32, 1, ma_dither_mode_none); - } - } - } -} - -static void ma_waveform_read_pcm_frames__triangle(ma_waveform* pWaveform, void* pFramesOut, ma_uint64 frameCount) -{ - ma_uint64 iFrame; - ma_uint64 iChannel; - ma_uint32 bps = ma_get_bytes_per_sample(pWaveform->config.format); - ma_uint32 bpf = bps * pWaveform->config.channels; - - MA_ASSERT(pWaveform != NULL); - MA_ASSERT(pFramesOut != NULL); - - if (pWaveform->config.format == ma_format_f32) { - float* pFramesOutF32 = (float*)pFramesOut; - for (iFrame = 0; iFrame < frameCount; iFrame += 1) { - float s = ma_waveform_triangle_f32(pWaveform->time, pWaveform->config.amplitude); - pWaveform->time += pWaveform->advance; - - for (iChannel = 0; iChannel < pWaveform->config.channels; iChannel += 1) { - pFramesOutF32[iFrame*pWaveform->config.channels + iChannel] = s; - } - } - } else if (pWaveform->config.format == ma_format_s16) { - ma_int16* pFramesOutS16 = (ma_int16*)pFramesOut; - for (iFrame = 0; iFrame < frameCount; iFrame += 1) { - ma_int16 s = ma_waveform_triangle_s16(pWaveform->time, pWaveform->config.amplitude); - pWaveform->time += pWaveform->advance; - - for (iChannel = 0; iChannel < pWaveform->config.channels; iChannel += 1) { - pFramesOutS16[iFrame*pWaveform->config.channels + iChannel] = s; - } - } - } else { - for (iFrame = 0; iFrame < frameCount; iFrame += 1) { - float s = ma_waveform_triangle_f32(pWaveform->time, pWaveform->config.amplitude); - pWaveform->time += pWaveform->advance; - - for (iChannel = 0; iChannel < pWaveform->config.channels; iChannel += 1) { - ma_pcm_convert(ma_offset_ptr(pFramesOut, iFrame*bpf + iChannel*bps), pWaveform->config.format, &s, ma_format_f32, 1, ma_dither_mode_none); - } - } - } -} - -static void ma_waveform_read_pcm_frames__sawtooth(ma_waveform* pWaveform, void* pFramesOut, ma_uint64 frameCount) -{ - ma_uint64 iFrame; - ma_uint64 iChannel; - ma_uint32 bps = ma_get_bytes_per_sample(pWaveform->config.format); - ma_uint32 bpf = bps * pWaveform->config.channels; - - MA_ASSERT(pWaveform != NULL); - MA_ASSERT(pFramesOut != NULL); - - if (pWaveform->config.format == ma_format_f32) { - float* pFramesOutF32 = (float*)pFramesOut; - for (iFrame = 0; iFrame < frameCount; iFrame += 1) { - float s = ma_waveform_sawtooth_f32(pWaveform->time, pWaveform->config.amplitude); - pWaveform->time += pWaveform->advance; - - for (iChannel = 0; iChannel < pWaveform->config.channels; iChannel += 1) { - pFramesOutF32[iFrame*pWaveform->config.channels + iChannel] = s; - } - } - } else if (pWaveform->config.format == ma_format_s16) { - ma_int16* pFramesOutS16 = (ma_int16*)pFramesOut; - for (iFrame = 0; iFrame < frameCount; iFrame += 1) { - ma_int16 s = ma_waveform_sawtooth_s16(pWaveform->time, pWaveform->config.amplitude); - pWaveform->time += pWaveform->advance; - - for (iChannel = 0; iChannel < pWaveform->config.channels; iChannel += 1) { - pFramesOutS16[iFrame*pWaveform->config.channels + iChannel] = s; - } - } - } else { - for (iFrame = 0; iFrame < frameCount; iFrame += 1) { - float s = ma_waveform_sawtooth_f32(pWaveform->time, pWaveform->config.amplitude); - pWaveform->time += pWaveform->advance; - - for (iChannel = 0; iChannel < pWaveform->config.channels; iChannel += 1) { - ma_pcm_convert(ma_offset_ptr(pFramesOut, iFrame*bpf + iChannel*bps), pWaveform->config.format, &s, ma_format_f32, 1, ma_dither_mode_none); - } - } - } -} - -MA_API ma_result ma_waveform_read_pcm_frames(ma_waveform* pWaveform, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead) -{ - if (pFramesRead != NULL) { - *pFramesRead = 0; - } - - if (frameCount == 0) { - return MA_INVALID_ARGS; - } - - if (pWaveform == NULL) { - return MA_INVALID_ARGS; - } - - if (pFramesOut != NULL) { - switch (pWaveform->config.type) - { - case ma_waveform_type_sine: - { - ma_waveform_read_pcm_frames__sine(pWaveform, pFramesOut, frameCount); - } break; - - case ma_waveform_type_square: - { - ma_waveform_read_pcm_frames__square(pWaveform, 0.5, pFramesOut, frameCount); - } break; - - case ma_waveform_type_triangle: - { - ma_waveform_read_pcm_frames__triangle(pWaveform, pFramesOut, frameCount); - } break; - - case ma_waveform_type_sawtooth: - { - ma_waveform_read_pcm_frames__sawtooth(pWaveform, pFramesOut, frameCount); - } break; - - default: return MA_INVALID_OPERATION; /* Unknown waveform type. */ - } - } else { - pWaveform->time += pWaveform->advance * (ma_int64)frameCount; /* Cast to int64 required for VC6. Won't affect anything in practice. */ - } - - if (pFramesRead != NULL) { - *pFramesRead = frameCount; - } - - return MA_SUCCESS; -} - -MA_API ma_result ma_waveform_seek_to_pcm_frame(ma_waveform* pWaveform, ma_uint64 frameIndex) -{ - if (pWaveform == NULL) { - return MA_INVALID_ARGS; - } - - pWaveform->time = pWaveform->advance * (ma_int64)frameIndex; /* Casting for VC6. Won't be an issue in practice. */ - - return MA_SUCCESS; -} - -MA_API ma_pulsewave_config ma_pulsewave_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRate, double dutyCycle, double amplitude, double frequency) -{ - ma_pulsewave_config config; - - MA_ZERO_OBJECT(&config); - config.format = format; - config.channels = channels; - config.sampleRate = sampleRate; - config.dutyCycle = dutyCycle; - config.amplitude = amplitude; - config.frequency = frequency; - - return config; -} - -MA_API ma_result ma_pulsewave_init(const ma_pulsewave_config* pConfig, ma_pulsewave* pWaveform) -{ - ma_result result; - ma_waveform_config config; - - if (pWaveform == NULL) { - return MA_INVALID_ARGS; - } - - MA_ZERO_OBJECT(pWaveform); - - config = ma_waveform_config_init( - pConfig->format, - pConfig->channels, - pConfig->sampleRate, - ma_waveform_type_square, - pConfig->amplitude, - pConfig->frequency - ); - - result = ma_waveform_init(&config, &pWaveform->waveform); - ma_pulsewave_set_duty_cycle(pWaveform, pConfig->dutyCycle); - - return result; -} - -MA_API void ma_pulsewave_uninit(ma_pulsewave* pWaveform) -{ - if (pWaveform == NULL) { - return; - } - - ma_waveform_uninit(&pWaveform->waveform); -} - -MA_API ma_result ma_pulsewave_read_pcm_frames(ma_pulsewave* pWaveform, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead) -{ - if (pFramesRead != NULL) { - *pFramesRead = 0; - } - - if (frameCount == 0) { - return MA_INVALID_ARGS; - } - - if (pWaveform == NULL) { - return MA_INVALID_ARGS; - } - - if (pFramesOut != NULL) { - ma_waveform_read_pcm_frames__square(&pWaveform->waveform, pWaveform->config.dutyCycle, pFramesOut, frameCount); - } else { - pWaveform->waveform.time += pWaveform->waveform.advance * (ma_int64)frameCount; /* Cast to int64 required for VC6. Won't affect anything in practice. */ - } - - if (pFramesRead != NULL) { - *pFramesRead = frameCount; - } - - return MA_SUCCESS; -} - -MA_API ma_result ma_pulsewave_seek_to_pcm_frame(ma_pulsewave* pWaveform, ma_uint64 frameIndex) -{ - if (pWaveform == NULL) { - return MA_INVALID_ARGS; - } - - ma_waveform_seek_to_pcm_frame(&pWaveform->waveform, frameIndex); - - return MA_SUCCESS; -} - -MA_API ma_result ma_pulsewave_set_amplitude(ma_pulsewave* pWaveform, double amplitude) -{ - if (pWaveform == NULL) { - return MA_INVALID_ARGS; - } - - pWaveform->config.amplitude = amplitude; - ma_waveform_set_amplitude(&pWaveform->waveform, amplitude); - - return MA_SUCCESS; -} - -MA_API ma_result ma_pulsewave_set_frequency(ma_pulsewave* pWaveform, double frequency) -{ - if (pWaveform == NULL) { - return MA_INVALID_ARGS; - } - - pWaveform->config.frequency = frequency; - ma_waveform_set_frequency(&pWaveform->waveform, frequency); - - return MA_SUCCESS; -} - -MA_API ma_result ma_pulsewave_set_sample_rate(ma_pulsewave* pWaveform, ma_uint32 sampleRate) -{ - if (pWaveform == NULL) { - return MA_INVALID_ARGS; - } - - pWaveform->config.sampleRate = sampleRate; - ma_waveform_set_sample_rate(&pWaveform->waveform, sampleRate); - - return MA_SUCCESS; -} - -MA_API ma_result ma_pulsewave_set_duty_cycle(ma_pulsewave* pWaveform, double dutyCycle) -{ - if (pWaveform == NULL) { - return MA_INVALID_ARGS; - } - - pWaveform->config.dutyCycle = dutyCycle; - - return MA_SUCCESS; -} - - - -MA_API ma_noise_config ma_noise_config_init(ma_format format, ma_uint32 channels, ma_noise_type type, ma_int32 seed, double amplitude) -{ - ma_noise_config config; - MA_ZERO_OBJECT(&config); - - config.format = format; - config.channels = channels; - config.type = type; - config.seed = seed; - config.amplitude = amplitude; - - if (config.seed == 0) { - config.seed = MA_DEFAULT_LCG_SEED; - } - - return config; -} - - -static ma_result ma_noise__data_source_on_read(ma_data_source* pDataSource, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead) -{ - return ma_noise_read_pcm_frames((ma_noise*)pDataSource, pFramesOut, frameCount, pFramesRead); -} - -static ma_result ma_noise__data_source_on_seek(ma_data_source* pDataSource, ma_uint64 frameIndex) -{ - /* No-op. Just pretend to be successful. */ - (void)pDataSource; - (void)frameIndex; - return MA_SUCCESS; -} - -static ma_result ma_noise__data_source_on_get_data_format(ma_data_source* pDataSource, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate, ma_channel* pChannelMap, size_t channelMapCap) -{ - ma_noise* pNoise = (ma_noise*)pDataSource; - - *pFormat = pNoise->config.format; - *pChannels = pNoise->config.channels; - *pSampleRate = 0; /* There is no notion of sample rate with noise generation. */ - ma_channel_map_init_standard(ma_standard_channel_map_default, pChannelMap, channelMapCap, pNoise->config.channels); - - return MA_SUCCESS; -} - -static ma_data_source_vtable g_ma_noise_data_source_vtable = -{ - ma_noise__data_source_on_read, - ma_noise__data_source_on_seek, /* No-op for noise. */ - ma_noise__data_source_on_get_data_format, - NULL, /* onGetCursor. No notion of a cursor for noise. */ - NULL, /* onGetLength. No notion of a length for noise. */ - NULL, /* onSetLooping */ - 0 -}; - - -#ifndef MA_PINK_NOISE_BIN_SIZE -#define MA_PINK_NOISE_BIN_SIZE 16 -#endif - -typedef struct -{ - size_t sizeInBytes; - struct - { - size_t binOffset; - size_t accumulationOffset; - size_t counterOffset; - } pink; - struct - { - size_t accumulationOffset; - } brownian; -} ma_noise_heap_layout; - -static ma_result ma_noise_get_heap_layout(const ma_noise_config* pConfig, ma_noise_heap_layout* pHeapLayout) -{ - MA_ASSERT(pHeapLayout != NULL); - - MA_ZERO_OBJECT(pHeapLayout); - - if (pConfig == NULL) { - return MA_INVALID_ARGS; - } - - if (pConfig->channels == 0) { - return MA_INVALID_ARGS; - } - - pHeapLayout->sizeInBytes = 0; - - /* Pink. */ - if (pConfig->type == ma_noise_type_pink) { - /* bin */ - pHeapLayout->pink.binOffset = pHeapLayout->sizeInBytes; - pHeapLayout->sizeInBytes += sizeof(double*) * pConfig->channels; - pHeapLayout->sizeInBytes += sizeof(double ) * pConfig->channels * MA_PINK_NOISE_BIN_SIZE; - - /* accumulation */ - pHeapLayout->pink.accumulationOffset = pHeapLayout->sizeInBytes; - pHeapLayout->sizeInBytes += sizeof(double) * pConfig->channels; - - /* counter */ - pHeapLayout->pink.counterOffset = pHeapLayout->sizeInBytes; - pHeapLayout->sizeInBytes += sizeof(ma_uint32) * pConfig->channels; - } - - /* Brownian. */ - if (pConfig->type == ma_noise_type_brownian) { - /* accumulation */ - pHeapLayout->brownian.accumulationOffset = pHeapLayout->sizeInBytes; - pHeapLayout->sizeInBytes += sizeof(double) * pConfig->channels; - } - - /* Make sure allocation size is aligned. */ - pHeapLayout->sizeInBytes = ma_align_64(pHeapLayout->sizeInBytes); - - return MA_SUCCESS; -} - -MA_API ma_result ma_noise_get_heap_size(const ma_noise_config* pConfig, size_t* pHeapSizeInBytes) -{ - ma_result result; - ma_noise_heap_layout heapLayout; - - if (pHeapSizeInBytes == NULL) { - return MA_INVALID_ARGS; - } - - *pHeapSizeInBytes = 0; - - result = ma_noise_get_heap_layout(pConfig, &heapLayout); - if (result != MA_SUCCESS) { - return result; - } - - *pHeapSizeInBytes = heapLayout.sizeInBytes; - - return MA_SUCCESS; -} - -MA_API ma_result ma_noise_init_preallocated(const ma_noise_config* pConfig, void* pHeap, ma_noise* pNoise) -{ - ma_result result; - ma_noise_heap_layout heapLayout; - ma_data_source_config dataSourceConfig; - ma_uint32 iChannel; - - if (pNoise == NULL) { - return MA_INVALID_ARGS; - } - - MA_ZERO_OBJECT(pNoise); - - result = ma_noise_get_heap_layout(pConfig, &heapLayout); - if (result != MA_SUCCESS) { - return result; - } - - pNoise->_pHeap = pHeap; - MA_ZERO_MEMORY(pNoise->_pHeap, heapLayout.sizeInBytes); - - dataSourceConfig = ma_data_source_config_init(); - dataSourceConfig.vtable = &g_ma_noise_data_source_vtable; - - result = ma_data_source_init(&dataSourceConfig, &pNoise->ds); - if (result != MA_SUCCESS) { - return result; - } - - pNoise->config = *pConfig; - ma_lcg_seed(&pNoise->lcg, pConfig->seed); - - if (pNoise->config.type == ma_noise_type_pink) { - pNoise->state.pink.bin = (double** )ma_offset_ptr(pHeap, heapLayout.pink.binOffset); - pNoise->state.pink.accumulation = (double* )ma_offset_ptr(pHeap, heapLayout.pink.accumulationOffset); - pNoise->state.pink.counter = (ma_uint32*)ma_offset_ptr(pHeap, heapLayout.pink.counterOffset); - - for (iChannel = 0; iChannel < pConfig->channels; iChannel += 1) { - pNoise->state.pink.bin[iChannel] = (double*)ma_offset_ptr(pHeap, heapLayout.pink.binOffset + (sizeof(double*) * pConfig->channels) + (sizeof(double) * MA_PINK_NOISE_BIN_SIZE * iChannel)); - pNoise->state.pink.accumulation[iChannel] = 0; - pNoise->state.pink.counter[iChannel] = 1; - } - } - - if (pNoise->config.type == ma_noise_type_brownian) { - pNoise->state.brownian.accumulation = (double*)ma_offset_ptr(pHeap, heapLayout.brownian.accumulationOffset); - - for (iChannel = 0; iChannel < pConfig->channels; iChannel += 1) { - pNoise->state.brownian.accumulation[iChannel] = 0; - } - } - - return MA_SUCCESS; -} - -MA_API ma_result ma_noise_init(const ma_noise_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_noise* pNoise) -{ - ma_result result; - size_t heapSizeInBytes; - void* pHeap; - - result = ma_noise_get_heap_size(pConfig, &heapSizeInBytes); - if (result != MA_SUCCESS) { - return result; - } - - if (heapSizeInBytes > 0) { - pHeap = ma_malloc(heapSizeInBytes, pAllocationCallbacks); - if (pHeap == NULL) { - return MA_OUT_OF_MEMORY; - } - } else { - pHeap = NULL; - } - - result = ma_noise_init_preallocated(pConfig, pHeap, pNoise); - if (result != MA_SUCCESS) { - ma_free(pHeap, pAllocationCallbacks); - return result; - } - - pNoise->_ownsHeap = MA_TRUE; - return MA_SUCCESS; -} - -MA_API void ma_noise_uninit(ma_noise* pNoise, const ma_allocation_callbacks* pAllocationCallbacks) -{ - if (pNoise == NULL) { - return; - } - - ma_data_source_uninit(&pNoise->ds); - - if (pNoise->_ownsHeap) { - ma_free(pNoise->_pHeap, pAllocationCallbacks); - } -} - -MA_API ma_result ma_noise_set_amplitude(ma_noise* pNoise, double amplitude) -{ - if (pNoise == NULL) { - return MA_INVALID_ARGS; - } - - pNoise->config.amplitude = amplitude; - return MA_SUCCESS; -} - -MA_API ma_result ma_noise_set_seed(ma_noise* pNoise, ma_int32 seed) -{ - if (pNoise == NULL) { - return MA_INVALID_ARGS; - } - - pNoise->lcg.state = seed; - return MA_SUCCESS; -} - - -MA_API ma_result ma_noise_set_type(ma_noise* pNoise, ma_noise_type type) -{ - if (pNoise == NULL) { - return MA_INVALID_ARGS; - } - - /* - This function should never have been implemented in the first place. Changing the type dynamically is not - supported. Instead you need to uninitialize and reinitialize a fresh `ma_noise` object. This function - will be removed in version 0.12. - */ - MA_ASSERT(MA_FALSE); - (void)type; - - return MA_INVALID_OPERATION; -} - -static MA_INLINE float ma_noise_f32_white(ma_noise* pNoise) -{ - return (float)(ma_lcg_rand_f64(&pNoise->lcg) * pNoise->config.amplitude); -} - -static MA_INLINE ma_int16 ma_noise_s16_white(ma_noise* pNoise) -{ - return ma_pcm_sample_f32_to_s16(ma_noise_f32_white(pNoise)); -} - -static MA_INLINE ma_uint64 ma_noise_read_pcm_frames__white(ma_noise* pNoise, void* pFramesOut, ma_uint64 frameCount) -{ - ma_uint64 iFrame; - ma_uint32 iChannel; - const ma_uint32 channels = pNoise->config.channels; - MA_ASSUME(channels > 0); - - if (pNoise->config.format == ma_format_f32) { - float* pFramesOutF32 = (float*)pFramesOut; - if (pNoise->config.duplicateChannels) { - for (iFrame = 0; iFrame < frameCount; iFrame += 1) { - float s = ma_noise_f32_white(pNoise); - for (iChannel = 0; iChannel < channels; iChannel += 1) { - pFramesOutF32[iFrame*channels + iChannel] = s; - } - } - } else { - for (iFrame = 0; iFrame < frameCount; iFrame += 1) { - for (iChannel = 0; iChannel < channels; iChannel += 1) { - pFramesOutF32[iFrame*channels + iChannel] = ma_noise_f32_white(pNoise); - } - } - } - } else if (pNoise->config.format == ma_format_s16) { - ma_int16* pFramesOutS16 = (ma_int16*)pFramesOut; - if (pNoise->config.duplicateChannels) { - for (iFrame = 0; iFrame < frameCount; iFrame += 1) { - ma_int16 s = ma_noise_s16_white(pNoise); - for (iChannel = 0; iChannel < channels; iChannel += 1) { - pFramesOutS16[iFrame*channels + iChannel] = s; - } - } - } else { - for (iFrame = 0; iFrame < frameCount; iFrame += 1) { - for (iChannel = 0; iChannel < channels; iChannel += 1) { - pFramesOutS16[iFrame*channels + iChannel] = ma_noise_s16_white(pNoise); - } - } - } - } else { - const ma_uint32 bps = ma_get_bytes_per_sample(pNoise->config.format); - const ma_uint32 bpf = bps * channels; - - if (pNoise->config.duplicateChannels) { - for (iFrame = 0; iFrame < frameCount; iFrame += 1) { - float s = ma_noise_f32_white(pNoise); - for (iChannel = 0; iChannel < channels; iChannel += 1) { - ma_pcm_convert(ma_offset_ptr(pFramesOut, iFrame*bpf + iChannel*bps), pNoise->config.format, &s, ma_format_f32, 1, ma_dither_mode_none); - } - } - } else { - for (iFrame = 0; iFrame < frameCount; iFrame += 1) { - for (iChannel = 0; iChannel < channels; iChannel += 1) { - float s = ma_noise_f32_white(pNoise); - ma_pcm_convert(ma_offset_ptr(pFramesOut, iFrame*bpf + iChannel*bps), pNoise->config.format, &s, ma_format_f32, 1, ma_dither_mode_none); - } - } - } - } - - return frameCount; -} - - -static MA_INLINE unsigned int ma_tzcnt32(unsigned int x) -{ - unsigned int n; - - /* Special case for odd numbers since they should happen about half the time. */ - if (x & 0x1) { - return 0; - } - - if (x == 0) { - return sizeof(x) << 3; - } - - n = 1; - if ((x & 0x0000FFFF) == 0) { x >>= 16; n += 16; } - if ((x & 0x000000FF) == 0) { x >>= 8; n += 8; } - if ((x & 0x0000000F) == 0) { x >>= 4; n += 4; } - if ((x & 0x00000003) == 0) { x >>= 2; n += 2; } - n -= x & 0x00000001; - - return n; -} - -/* -Pink noise generation based on Tonic (public domain) with modifications. https://github.com/TonicAudio/Tonic/blob/master/src/Tonic/Noise.h - -This is basically _the_ reference for pink noise from what I've found: http://www.firstpr.com.au/dsp/pink-noise/ -*/ -static MA_INLINE float ma_noise_f32_pink(ma_noise* pNoise, ma_uint32 iChannel) -{ - double result; - double binPrev; - double binNext; - unsigned int ibin; - - ibin = ma_tzcnt32(pNoise->state.pink.counter[iChannel]) & (MA_PINK_NOISE_BIN_SIZE - 1); - - binPrev = pNoise->state.pink.bin[iChannel][ibin]; - binNext = ma_lcg_rand_f64(&pNoise->lcg); - pNoise->state.pink.bin[iChannel][ibin] = binNext; - - pNoise->state.pink.accumulation[iChannel] += (binNext - binPrev); - pNoise->state.pink.counter[iChannel] += 1; - - result = (ma_lcg_rand_f64(&pNoise->lcg) + pNoise->state.pink.accumulation[iChannel]); - result /= 10; - - return (float)(result * pNoise->config.amplitude); -} - -static MA_INLINE ma_int16 ma_noise_s16_pink(ma_noise* pNoise, ma_uint32 iChannel) -{ - return ma_pcm_sample_f32_to_s16(ma_noise_f32_pink(pNoise, iChannel)); -} - -static MA_INLINE ma_uint64 ma_noise_read_pcm_frames__pink(ma_noise* pNoise, void* pFramesOut, ma_uint64 frameCount) -{ - ma_uint64 iFrame; - ma_uint32 iChannel; - const ma_uint32 channels = pNoise->config.channels; - MA_ASSUME(channels > 0); - - if (pNoise->config.format == ma_format_f32) { - float* pFramesOutF32 = (float*)pFramesOut; - if (pNoise->config.duplicateChannels) { - for (iFrame = 0; iFrame < frameCount; iFrame += 1) { - float s = ma_noise_f32_pink(pNoise, 0); - for (iChannel = 0; iChannel < channels; iChannel += 1) { - pFramesOutF32[iFrame*channels + iChannel] = s; - } - } - } else { - for (iFrame = 0; iFrame < frameCount; iFrame += 1) { - for (iChannel = 0; iChannel < channels; iChannel += 1) { - pFramesOutF32[iFrame*channels + iChannel] = ma_noise_f32_pink(pNoise, iChannel); - } - } - } - } else if (pNoise->config.format == ma_format_s16) { - ma_int16* pFramesOutS16 = (ma_int16*)pFramesOut; - if (pNoise->config.duplicateChannels) { - for (iFrame = 0; iFrame < frameCount; iFrame += 1) { - ma_int16 s = ma_noise_s16_pink(pNoise, 0); - for (iChannel = 0; iChannel < channels; iChannel += 1) { - pFramesOutS16[iFrame*channels + iChannel] = s; - } - } - } else { - for (iFrame = 0; iFrame < frameCount; iFrame += 1) { - for (iChannel = 0; iChannel < channels; iChannel += 1) { - pFramesOutS16[iFrame*channels + iChannel] = ma_noise_s16_pink(pNoise, iChannel); - } - } - } - } else { - const ma_uint32 bps = ma_get_bytes_per_sample(pNoise->config.format); - const ma_uint32 bpf = bps * channels; - - if (pNoise->config.duplicateChannels) { - for (iFrame = 0; iFrame < frameCount; iFrame += 1) { - float s = ma_noise_f32_pink(pNoise, 0); - for (iChannel = 0; iChannel < channels; iChannel += 1) { - ma_pcm_convert(ma_offset_ptr(pFramesOut, iFrame*bpf + iChannel*bps), pNoise->config.format, &s, ma_format_f32, 1, ma_dither_mode_none); - } - } - } else { - for (iFrame = 0; iFrame < frameCount; iFrame += 1) { - for (iChannel = 0; iChannel < channels; iChannel += 1) { - float s = ma_noise_f32_pink(pNoise, iChannel); - ma_pcm_convert(ma_offset_ptr(pFramesOut, iFrame*bpf + iChannel*bps), pNoise->config.format, &s, ma_format_f32, 1, ma_dither_mode_none); - } - } - } - } - - return frameCount; -} - - -static MA_INLINE float ma_noise_f32_brownian(ma_noise* pNoise, ma_uint32 iChannel) -{ - double result; - - result = (ma_lcg_rand_f64(&pNoise->lcg) + pNoise->state.brownian.accumulation[iChannel]); - result /= 1.005; /* Don't escape the -1..1 range on average. */ - - pNoise->state.brownian.accumulation[iChannel] = result; - result /= 20; - - return (float)(result * pNoise->config.amplitude); -} - -static MA_INLINE ma_int16 ma_noise_s16_brownian(ma_noise* pNoise, ma_uint32 iChannel) -{ - return ma_pcm_sample_f32_to_s16(ma_noise_f32_brownian(pNoise, iChannel)); -} - -static MA_INLINE ma_uint64 ma_noise_read_pcm_frames__brownian(ma_noise* pNoise, void* pFramesOut, ma_uint64 frameCount) -{ - ma_uint64 iFrame; - ma_uint32 iChannel; - const ma_uint32 channels = pNoise->config.channels; - MA_ASSUME(channels > 0); - - if (pNoise->config.format == ma_format_f32) { - float* pFramesOutF32 = (float*)pFramesOut; - if (pNoise->config.duplicateChannels) { - for (iFrame = 0; iFrame < frameCount; iFrame += 1) { - float s = ma_noise_f32_brownian(pNoise, 0); - for (iChannel = 0; iChannel < channels; iChannel += 1) { - pFramesOutF32[iFrame*channels + iChannel] = s; - } - } - } else { - for (iFrame = 0; iFrame < frameCount; iFrame += 1) { - for (iChannel = 0; iChannel < channels; iChannel += 1) { - pFramesOutF32[iFrame*channels + iChannel] = ma_noise_f32_brownian(pNoise, iChannel); - } - } - } - } else if (pNoise->config.format == ma_format_s16) { - ma_int16* pFramesOutS16 = (ma_int16*)pFramesOut; - if (pNoise->config.duplicateChannels) { - for (iFrame = 0; iFrame < frameCount; iFrame += 1) { - ma_int16 s = ma_noise_s16_brownian(pNoise, 0); - for (iChannel = 0; iChannel < channels; iChannel += 1) { - pFramesOutS16[iFrame*channels + iChannel] = s; - } - } - } else { - for (iFrame = 0; iFrame < frameCount; iFrame += 1) { - for (iChannel = 0; iChannel < channels; iChannel += 1) { - pFramesOutS16[iFrame*channels + iChannel] = ma_noise_s16_brownian(pNoise, iChannel); - } - } - } - } else { - const ma_uint32 bps = ma_get_bytes_per_sample(pNoise->config.format); - const ma_uint32 bpf = bps * channels; - - if (pNoise->config.duplicateChannels) { - for (iFrame = 0; iFrame < frameCount; iFrame += 1) { - float s = ma_noise_f32_brownian(pNoise, 0); - for (iChannel = 0; iChannel < channels; iChannel += 1) { - ma_pcm_convert(ma_offset_ptr(pFramesOut, iFrame*bpf + iChannel*bps), pNoise->config.format, &s, ma_format_f32, 1, ma_dither_mode_none); - } - } - } else { - for (iFrame = 0; iFrame < frameCount; iFrame += 1) { - for (iChannel = 0; iChannel < channels; iChannel += 1) { - float s = ma_noise_f32_brownian(pNoise, iChannel); - ma_pcm_convert(ma_offset_ptr(pFramesOut, iFrame*bpf + iChannel*bps), pNoise->config.format, &s, ma_format_f32, 1, ma_dither_mode_none); - } - } - } - } - - return frameCount; -} - -MA_API ma_result ma_noise_read_pcm_frames(ma_noise* pNoise, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead) -{ - ma_uint64 framesRead = 0; - - if (pFramesRead != NULL) { - *pFramesRead = 0; - } - - if (frameCount == 0) { - return MA_INVALID_ARGS; - } - - if (pNoise == NULL) { - return MA_INVALID_ARGS; - } - - /* The output buffer is allowed to be NULL. Since we aren't tracking cursors or anything we can just do nothing and pretend to be successful. */ - if (pFramesOut == NULL) { - framesRead = frameCount; - } else { - switch (pNoise->config.type) { - case ma_noise_type_white: framesRead = ma_noise_read_pcm_frames__white (pNoise, pFramesOut, frameCount); break; - case ma_noise_type_pink: framesRead = ma_noise_read_pcm_frames__pink (pNoise, pFramesOut, frameCount); break; - case ma_noise_type_brownian: framesRead = ma_noise_read_pcm_frames__brownian(pNoise, pFramesOut, frameCount); break; - default: return MA_INVALID_OPERATION; /* Unknown noise type. */ - } - } - - if (pFramesRead != NULL) { - *pFramesRead = framesRead; - } - - return MA_SUCCESS; -} -#endif /* MA_NO_GENERATION */ - - - -#ifndef MA_NO_RESOURCE_MANAGER -#ifndef MA_RESOURCE_MANAGER_PAGE_SIZE_IN_MILLISECONDS -#define MA_RESOURCE_MANAGER_PAGE_SIZE_IN_MILLISECONDS 1000 -#endif - -#ifndef MA_JOB_TYPE_RESOURCE_MANAGER_QUEUE_CAPACITY -#define MA_JOB_TYPE_RESOURCE_MANAGER_QUEUE_CAPACITY 1024 -#endif - -MA_API ma_resource_manager_pipeline_notifications ma_resource_manager_pipeline_notifications_init(void) -{ - ma_resource_manager_pipeline_notifications notifications; - - MA_ZERO_OBJECT(¬ifications); - - return notifications; -} - -static void ma_resource_manager_pipeline_notifications_signal_all_notifications(const ma_resource_manager_pipeline_notifications* pPipelineNotifications) -{ - if (pPipelineNotifications == NULL) { - return; - } - - if (pPipelineNotifications->init.pNotification) { ma_async_notification_signal(pPipelineNotifications->init.pNotification); } - if (pPipelineNotifications->done.pNotification) { ma_async_notification_signal(pPipelineNotifications->done.pNotification); } -} - -static void ma_resource_manager_pipeline_notifications_acquire_all_fences(const ma_resource_manager_pipeline_notifications* pPipelineNotifications) -{ - if (pPipelineNotifications == NULL) { - return; - } - - if (pPipelineNotifications->init.pFence != NULL) { ma_fence_acquire(pPipelineNotifications->init.pFence); } - if (pPipelineNotifications->done.pFence != NULL) { ma_fence_acquire(pPipelineNotifications->done.pFence); } -} - -static void ma_resource_manager_pipeline_notifications_release_all_fences(const ma_resource_manager_pipeline_notifications* pPipelineNotifications) -{ - if (pPipelineNotifications == NULL) { - return; - } - - if (pPipelineNotifications->init.pFence != NULL) { ma_fence_release(pPipelineNotifications->init.pFence); } - if (pPipelineNotifications->done.pFence != NULL) { ma_fence_release(pPipelineNotifications->done.pFence); } -} - - - -#ifndef MA_DEFAULT_HASH_SEED -#define MA_DEFAULT_HASH_SEED 42 -#endif - -/* MurmurHash3. Based on code from https://github.com/PeterScott/murmur3/blob/master/murmur3.c (public domain). */ -#if defined(__clang__) || (defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6))) - #pragma GCC diagnostic push - #if __GNUC__ >= 7 - #pragma GCC diagnostic ignored "-Wimplicit-fallthrough" - #endif -#endif - -static MA_INLINE ma_uint32 ma_rotl32(ma_uint32 x, ma_int8 r) -{ - return (x << r) | (x >> (32 - r)); -} - -static MA_INLINE ma_uint32 ma_hash_getblock(const ma_uint32* blocks, int i) -{ - ma_uint32 block; - - /* Try silencing a sanitization warning about unaligned access by doing a memcpy() instead of assignment. */ - MA_COPY_MEMORY(&block, ma_offset_ptr(blocks, i * sizeof(block)), sizeof(block)); - - if (ma_is_little_endian()) { - return block; - } else { - return ma_swap_endian_uint32(block); - } -} - -static MA_INLINE ma_uint32 ma_hash_fmix32(ma_uint32 h) -{ - h ^= h >> 16; - h *= 0x85ebca6b; - h ^= h >> 13; - h *= 0xc2b2ae35; - h ^= h >> 16; - - return h; -} - -static ma_uint32 ma_hash_32(const void* key, int len, ma_uint32 seed) -{ - const ma_uint8* data = (const ma_uint8*)key; - const ma_uint32* blocks; - const ma_uint8* tail; - const int nblocks = len / 4; - ma_uint32 h1 = seed; - ma_uint32 c1 = 0xcc9e2d51; - ma_uint32 c2 = 0x1b873593; - ma_uint32 k1; - int i; - - blocks = (const ma_uint32 *)(data + nblocks*4); - - for(i = -nblocks; i; i++) { - k1 = ma_hash_getblock(blocks,i); - - k1 *= c1; - k1 = ma_rotl32(k1, 15); - k1 *= c2; - - h1 ^= k1; - h1 = ma_rotl32(h1, 13); - h1 = h1*5 + 0xe6546b64; - } - - - tail = (const ma_uint8*)(data + nblocks*4); - - k1 = 0; - switch(len & 3) { - case 3: k1 ^= tail[2] << 16; - case 2: k1 ^= tail[1] << 8; - case 1: k1 ^= tail[0]; - k1 *= c1; k1 = ma_rotl32(k1, 15); k1 *= c2; h1 ^= k1; - }; - - - h1 ^= len; - h1 = ma_hash_fmix32(h1); - - return h1; -} - -#if defined(__clang__) || (defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6))) - #pragma GCC diagnostic push -#endif -/* End MurmurHash3 */ - -static ma_uint32 ma_hash_string_32(const char* str) -{ - return ma_hash_32(str, (int)strlen(str), MA_DEFAULT_HASH_SEED); -} - -static ma_uint32 ma_hash_string_w_32(const wchar_t* str) -{ - return ma_hash_32(str, (int)wcslen(str) * sizeof(*str), MA_DEFAULT_HASH_SEED); -} - - - - -/* -Basic BST Functions -*/ -static ma_result ma_resource_manager_data_buffer_node_search(ma_resource_manager* pResourceManager, ma_uint32 hashedName32, ma_resource_manager_data_buffer_node** ppDataBufferNode) -{ - ma_resource_manager_data_buffer_node* pCurrentNode; - - MA_ASSERT(pResourceManager != NULL); - MA_ASSERT(ppDataBufferNode != NULL); - - pCurrentNode = pResourceManager->pRootDataBufferNode; - while (pCurrentNode != NULL) { - if (hashedName32 == pCurrentNode->hashedName32) { - break; /* Found. */ - } else if (hashedName32 < pCurrentNode->hashedName32) { - pCurrentNode = pCurrentNode->pChildLo; - } else { - pCurrentNode = pCurrentNode->pChildHi; - } - } - - *ppDataBufferNode = pCurrentNode; - - if (pCurrentNode == NULL) { - return MA_DOES_NOT_EXIST; - } else { - return MA_SUCCESS; - } -} - -static ma_result ma_resource_manager_data_buffer_node_insert_point(ma_resource_manager* pResourceManager, ma_uint32 hashedName32, ma_resource_manager_data_buffer_node** ppInsertPoint) -{ - ma_result result = MA_SUCCESS; - ma_resource_manager_data_buffer_node* pCurrentNode; - - MA_ASSERT(pResourceManager != NULL); - MA_ASSERT(ppInsertPoint != NULL); - - *ppInsertPoint = NULL; - - if (pResourceManager->pRootDataBufferNode == NULL) { - return MA_SUCCESS; /* No items. */ - } - - /* We need to find the node that will become the parent of the new node. If a node is found that already has the same hashed name we need to return MA_ALREADY_EXISTS. */ - pCurrentNode = pResourceManager->pRootDataBufferNode; - while (pCurrentNode != NULL) { - if (hashedName32 == pCurrentNode->hashedName32) { - result = MA_ALREADY_EXISTS; - break; - } else { - if (hashedName32 < pCurrentNode->hashedName32) { - if (pCurrentNode->pChildLo == NULL) { - result = MA_SUCCESS; - break; - } else { - pCurrentNode = pCurrentNode->pChildLo; - } - } else { - if (pCurrentNode->pChildHi == NULL) { - result = MA_SUCCESS; - break; - } else { - pCurrentNode = pCurrentNode->pChildHi; - } - } - } - } - - *ppInsertPoint = pCurrentNode; - return result; -} - -static ma_result ma_resource_manager_data_buffer_node_insert_at(ma_resource_manager* pResourceManager, ma_resource_manager_data_buffer_node* pDataBufferNode, ma_resource_manager_data_buffer_node* pInsertPoint) -{ - MA_ASSERT(pResourceManager != NULL); - MA_ASSERT(pDataBufferNode != NULL); - - /* The key must have been set before calling this function. */ - MA_ASSERT(pDataBufferNode->hashedName32 != 0); - - if (pInsertPoint == NULL) { - /* It's the first node. */ - pResourceManager->pRootDataBufferNode = pDataBufferNode; - } else { - /* It's not the first node. It needs to be inserted. */ - if (pDataBufferNode->hashedName32 < pInsertPoint->hashedName32) { - MA_ASSERT(pInsertPoint->pChildLo == NULL); - pInsertPoint->pChildLo = pDataBufferNode; - } else { - MA_ASSERT(pInsertPoint->pChildHi == NULL); - pInsertPoint->pChildHi = pDataBufferNode; - } - } - - pDataBufferNode->pParent = pInsertPoint; - - return MA_SUCCESS; -} - -#if 0 /* Unused for now. */ -static ma_result ma_resource_manager_data_buffer_node_insert(ma_resource_manager* pResourceManager, ma_resource_manager_data_buffer_node* pDataBufferNode) -{ - ma_result result; - ma_resource_manager_data_buffer_node* pInsertPoint; - - MA_ASSERT(pResourceManager != NULL); - MA_ASSERT(pDataBufferNode != NULL); - - result = ma_resource_manager_data_buffer_node_insert_point(pResourceManager, pDataBufferNode->hashedName32, &pInsertPoint); - if (result != MA_SUCCESS) { - return MA_INVALID_ARGS; - } - - return ma_resource_manager_data_buffer_node_insert_at(pResourceManager, pDataBufferNode, pInsertPoint); -} -#endif - -static MA_INLINE ma_resource_manager_data_buffer_node* ma_resource_manager_data_buffer_node_find_min(ma_resource_manager_data_buffer_node* pDataBufferNode) -{ - ma_resource_manager_data_buffer_node* pCurrentNode; - - MA_ASSERT(pDataBufferNode != NULL); - - pCurrentNode = pDataBufferNode; - while (pCurrentNode->pChildLo != NULL) { - pCurrentNode = pCurrentNode->pChildLo; - } - - return pCurrentNode; -} - -static MA_INLINE ma_resource_manager_data_buffer_node* ma_resource_manager_data_buffer_node_find_max(ma_resource_manager_data_buffer_node* pDataBufferNode) -{ - ma_resource_manager_data_buffer_node* pCurrentNode; - - MA_ASSERT(pDataBufferNode != NULL); - - pCurrentNode = pDataBufferNode; - while (pCurrentNode->pChildHi != NULL) { - pCurrentNode = pCurrentNode->pChildHi; - } - - return pCurrentNode; -} - -static MA_INLINE ma_resource_manager_data_buffer_node* ma_resource_manager_data_buffer_node_find_inorder_successor(ma_resource_manager_data_buffer_node* pDataBufferNode) -{ - MA_ASSERT(pDataBufferNode != NULL); - MA_ASSERT(pDataBufferNode->pChildHi != NULL); - - return ma_resource_manager_data_buffer_node_find_min(pDataBufferNode->pChildHi); -} - -static MA_INLINE ma_resource_manager_data_buffer_node* ma_resource_manager_data_buffer_node_find_inorder_predecessor(ma_resource_manager_data_buffer_node* pDataBufferNode) -{ - MA_ASSERT(pDataBufferNode != NULL); - MA_ASSERT(pDataBufferNode->pChildLo != NULL); - - return ma_resource_manager_data_buffer_node_find_max(pDataBufferNode->pChildLo); -} - -static ma_result ma_resource_manager_data_buffer_node_remove(ma_resource_manager* pResourceManager, ma_resource_manager_data_buffer_node* pDataBufferNode) -{ - MA_ASSERT(pResourceManager != NULL); - MA_ASSERT(pDataBufferNode != NULL); - - if (pDataBufferNode->pChildLo == NULL) { - if (pDataBufferNode->pChildHi == NULL) { - /* Simple case - deleting a buffer with no children. */ - if (pDataBufferNode->pParent == NULL) { - MA_ASSERT(pResourceManager->pRootDataBufferNode == pDataBufferNode); /* There is only a single buffer in the tree which should be equal to the root node. */ - pResourceManager->pRootDataBufferNode = NULL; - } else { - if (pDataBufferNode->pParent->pChildLo == pDataBufferNode) { - pDataBufferNode->pParent->pChildLo = NULL; - } else { - pDataBufferNode->pParent->pChildHi = NULL; - } - } - } else { - /* Node has one child - pChildHi != NULL. */ - pDataBufferNode->pChildHi->pParent = pDataBufferNode->pParent; - - if (pDataBufferNode->pParent == NULL) { - MA_ASSERT(pResourceManager->pRootDataBufferNode == pDataBufferNode); - pResourceManager->pRootDataBufferNode = pDataBufferNode->pChildHi; - } else { - if (pDataBufferNode->pParent->pChildLo == pDataBufferNode) { - pDataBufferNode->pParent->pChildLo = pDataBufferNode->pChildHi; - } else { - pDataBufferNode->pParent->pChildHi = pDataBufferNode->pChildHi; - } - } - } - } else { - if (pDataBufferNode->pChildHi == NULL) { - /* Node has one child - pChildLo != NULL. */ - pDataBufferNode->pChildLo->pParent = pDataBufferNode->pParent; - - if (pDataBufferNode->pParent == NULL) { - MA_ASSERT(pResourceManager->pRootDataBufferNode == pDataBufferNode); - pResourceManager->pRootDataBufferNode = pDataBufferNode->pChildLo; - } else { - if (pDataBufferNode->pParent->pChildLo == pDataBufferNode) { - pDataBufferNode->pParent->pChildLo = pDataBufferNode->pChildLo; - } else { - pDataBufferNode->pParent->pChildHi = pDataBufferNode->pChildLo; - } - } - } else { - /* Complex case - deleting a node with two children. */ - ma_resource_manager_data_buffer_node* pReplacementDataBufferNode; - - /* For now we are just going to use the in-order successor as the replacement, but we may want to try to keep this balanced by switching between the two. */ - pReplacementDataBufferNode = ma_resource_manager_data_buffer_node_find_inorder_successor(pDataBufferNode); - MA_ASSERT(pReplacementDataBufferNode != NULL); - - /* - Now that we have our replacement node we can make the change. The simple way to do this would be to just exchange the values, and then remove the replacement - node, however we track specific nodes via pointers which means we can't just swap out the values. We need to instead just change the pointers around. The - replacement node should have at most 1 child. Therefore, we can detach it in terms of our simpler cases above. What we're essentially doing is detaching the - replacement node and reinserting it into the same position as the deleted node. - */ - MA_ASSERT(pReplacementDataBufferNode->pParent != NULL); /* The replacement node should never be the root which means it should always have a parent. */ - MA_ASSERT(pReplacementDataBufferNode->pChildLo == NULL); /* Because we used in-order successor. This would be pChildHi == NULL if we used in-order predecessor. */ - - if (pReplacementDataBufferNode->pChildHi == NULL) { - if (pReplacementDataBufferNode->pParent->pChildLo == pReplacementDataBufferNode) { - pReplacementDataBufferNode->pParent->pChildLo = NULL; - } else { - pReplacementDataBufferNode->pParent->pChildHi = NULL; - } - } else { - pReplacementDataBufferNode->pChildHi->pParent = pReplacementDataBufferNode->pParent; - if (pReplacementDataBufferNode->pParent->pChildLo == pReplacementDataBufferNode) { - pReplacementDataBufferNode->pParent->pChildLo = pReplacementDataBufferNode->pChildHi; - } else { - pReplacementDataBufferNode->pParent->pChildHi = pReplacementDataBufferNode->pChildHi; - } - } - - - /* The replacement node has essentially been detached from the binary tree, so now we need to replace the old data buffer with it. The first thing to update is the parent */ - if (pDataBufferNode->pParent != NULL) { - if (pDataBufferNode->pParent->pChildLo == pDataBufferNode) { - pDataBufferNode->pParent->pChildLo = pReplacementDataBufferNode; - } else { - pDataBufferNode->pParent->pChildHi = pReplacementDataBufferNode; - } - } - - /* Now need to update the replacement node's pointers. */ - pReplacementDataBufferNode->pParent = pDataBufferNode->pParent; - pReplacementDataBufferNode->pChildLo = pDataBufferNode->pChildLo; - pReplacementDataBufferNode->pChildHi = pDataBufferNode->pChildHi; - - /* Now the children of the replacement node need to have their parent pointers updated. */ - if (pReplacementDataBufferNode->pChildLo != NULL) { - pReplacementDataBufferNode->pChildLo->pParent = pReplacementDataBufferNode; - } - if (pReplacementDataBufferNode->pChildHi != NULL) { - pReplacementDataBufferNode->pChildHi->pParent = pReplacementDataBufferNode; - } - - /* Now the root node needs to be updated. */ - if (pResourceManager->pRootDataBufferNode == pDataBufferNode) { - pResourceManager->pRootDataBufferNode = pReplacementDataBufferNode; - } - } - } - - return MA_SUCCESS; -} - -#if 0 /* Unused for now. */ -static ma_result ma_resource_manager_data_buffer_node_remove_by_key(ma_resource_manager* pResourceManager, ma_uint32 hashedName32) -{ - ma_result result; - ma_resource_manager_data_buffer_node* pDataBufferNode; - - result = ma_resource_manager_data_buffer_search(pResourceManager, hashedName32, &pDataBufferNode); - if (result != MA_SUCCESS) { - return result; /* Could not find the data buffer. */ - } - - return ma_resource_manager_data_buffer_remove(pResourceManager, pDataBufferNode); -} -#endif - -static ma_resource_manager_data_supply_type ma_resource_manager_data_buffer_node_get_data_supply_type(ma_resource_manager_data_buffer_node* pDataBufferNode) -{ - return (ma_resource_manager_data_supply_type)ma_atomic_load_i32(&pDataBufferNode->data.type); -} - -static void ma_resource_manager_data_buffer_node_set_data_supply_type(ma_resource_manager_data_buffer_node* pDataBufferNode, ma_resource_manager_data_supply_type supplyType) -{ - ma_atomic_exchange_i32(&pDataBufferNode->data.type, supplyType); -} - -static ma_result ma_resource_manager_data_buffer_node_increment_ref(ma_resource_manager* pResourceManager, ma_resource_manager_data_buffer_node* pDataBufferNode, ma_uint32* pNewRefCount) -{ - ma_uint32 refCount; - - MA_ASSERT(pResourceManager != NULL); - MA_ASSERT(pDataBufferNode != NULL); - - (void)pResourceManager; - - refCount = ma_atomic_fetch_add_32(&pDataBufferNode->refCount, 1) + 1; - - if (pNewRefCount != NULL) { - *pNewRefCount = refCount; - } - - return MA_SUCCESS; -} - -static ma_result ma_resource_manager_data_buffer_node_decrement_ref(ma_resource_manager* pResourceManager, ma_resource_manager_data_buffer_node* pDataBufferNode, ma_uint32* pNewRefCount) -{ - ma_uint32 refCount; - - MA_ASSERT(pResourceManager != NULL); - MA_ASSERT(pDataBufferNode != NULL); - - (void)pResourceManager; - - refCount = ma_atomic_fetch_sub_32(&pDataBufferNode->refCount, 1) - 1; - - if (pNewRefCount != NULL) { - *pNewRefCount = refCount; - } - - return MA_SUCCESS; -} - -static void ma_resource_manager_data_buffer_node_free(ma_resource_manager* pResourceManager, ma_resource_manager_data_buffer_node* pDataBufferNode) -{ - MA_ASSERT(pResourceManager != NULL); - MA_ASSERT(pDataBufferNode != NULL); - - if (pDataBufferNode->isDataOwnedByResourceManager) { - if (ma_resource_manager_data_buffer_node_get_data_supply_type(pDataBufferNode) == ma_resource_manager_data_supply_type_encoded) { - ma_free((void*)pDataBufferNode->data.backend.encoded.pData, &pResourceManager->config.allocationCallbacks); - pDataBufferNode->data.backend.encoded.pData = NULL; - pDataBufferNode->data.backend.encoded.sizeInBytes = 0; - } else if (ma_resource_manager_data_buffer_node_get_data_supply_type(pDataBufferNode) == ma_resource_manager_data_supply_type_decoded) { - ma_free((void*)pDataBufferNode->data.backend.decoded.pData, &pResourceManager->config.allocationCallbacks); - pDataBufferNode->data.backend.decoded.pData = NULL; - pDataBufferNode->data.backend.decoded.totalFrameCount = 0; - } else if (ma_resource_manager_data_buffer_node_get_data_supply_type(pDataBufferNode) == ma_resource_manager_data_supply_type_decoded_paged) { - ma_paged_audio_buffer_data_uninit(&pDataBufferNode->data.backend.decodedPaged.data, &pResourceManager->config.allocationCallbacks); - } else { - /* Should never hit this if the node was successfully initialized. */ - MA_ASSERT(pDataBufferNode->result != MA_SUCCESS); - } - } - - /* The data buffer itself needs to be freed. */ - ma_free(pDataBufferNode, &pResourceManager->config.allocationCallbacks); -} - -static ma_result ma_resource_manager_data_buffer_node_result(const ma_resource_manager_data_buffer_node* pDataBufferNode) -{ - MA_ASSERT(pDataBufferNode != NULL); - - return (ma_result)ma_atomic_load_i32((ma_result*)&pDataBufferNode->result); /* Need a naughty const-cast here. */ -} - - -static ma_bool32 ma_resource_manager_is_threading_enabled(const ma_resource_manager* pResourceManager) -{ - MA_ASSERT(pResourceManager != NULL); - - return (pResourceManager->config.flags & MA_RESOURCE_MANAGER_FLAG_NO_THREADING) == 0; -} - - -typedef struct -{ - union - { - ma_async_notification_event e; - ma_async_notification_poll p; - } backend; /* Must be the first member. */ - ma_resource_manager* pResourceManager; -} ma_resource_manager_inline_notification; - -static ma_result ma_resource_manager_inline_notification_init(ma_resource_manager* pResourceManager, ma_resource_manager_inline_notification* pNotification) -{ - MA_ASSERT(pResourceManager != NULL); - MA_ASSERT(pNotification != NULL); - - pNotification->pResourceManager = pResourceManager; - - if (ma_resource_manager_is_threading_enabled(pResourceManager)) { - return ma_async_notification_event_init(&pNotification->backend.e); - } else { - return ma_async_notification_poll_init(&pNotification->backend.p); - } -} - -static void ma_resource_manager_inline_notification_uninit(ma_resource_manager_inline_notification* pNotification) -{ - MA_ASSERT(pNotification != NULL); - - if (ma_resource_manager_is_threading_enabled(pNotification->pResourceManager)) { - ma_async_notification_event_uninit(&pNotification->backend.e); - } else { - /* No need to uninitialize a polling notification. */ - } -} - -static void ma_resource_manager_inline_notification_wait(ma_resource_manager_inline_notification* pNotification) -{ - MA_ASSERT(pNotification != NULL); - - if (ma_resource_manager_is_threading_enabled(pNotification->pResourceManager)) { - ma_async_notification_event_wait(&pNotification->backend.e); - } else { - while (ma_async_notification_poll_is_signalled(&pNotification->backend.p) == MA_FALSE) { - ma_result result = ma_resource_manager_process_next_job(pNotification->pResourceManager); - if (result == MA_NO_DATA_AVAILABLE || result == MA_CANCELLED) { - break; - } - } - } -} - -static void ma_resource_manager_inline_notification_wait_and_uninit(ma_resource_manager_inline_notification* pNotification) -{ - ma_resource_manager_inline_notification_wait(pNotification); - ma_resource_manager_inline_notification_uninit(pNotification); -} - - -static void ma_resource_manager_data_buffer_bst_lock(ma_resource_manager* pResourceManager) -{ - MA_ASSERT(pResourceManager != NULL); - - if (ma_resource_manager_is_threading_enabled(pResourceManager)) { - #ifndef MA_NO_THREADING - { - ma_mutex_lock(&pResourceManager->dataBufferBSTLock); - } - #else - { - MA_ASSERT(MA_FALSE); /* Should never hit this. */ - } - #endif - } else { - /* Threading not enabled. Do nothing. */ - } -} - -static void ma_resource_manager_data_buffer_bst_unlock(ma_resource_manager* pResourceManager) -{ - MA_ASSERT(pResourceManager != NULL); - - if (ma_resource_manager_is_threading_enabled(pResourceManager)) { - #ifndef MA_NO_THREADING - { - ma_mutex_unlock(&pResourceManager->dataBufferBSTLock); - } - #else - { - MA_ASSERT(MA_FALSE); /* Should never hit this. */ - } - #endif - } else { - /* Threading not enabled. Do nothing. */ - } -} - -#ifndef MA_NO_THREADING -static ma_thread_result MA_THREADCALL ma_resource_manager_job_thread(void* pUserData) -{ - ma_resource_manager* pResourceManager = (ma_resource_manager*)pUserData; - MA_ASSERT(pResourceManager != NULL); - - for (;;) { - ma_result result; - ma_job job; - - result = ma_resource_manager_next_job(pResourceManager, &job); - if (result != MA_SUCCESS) { - break; - } - - /* Terminate if we got a quit message. */ - if (job.toc.breakup.code == MA_JOB_TYPE_QUIT) { - break; - } - - ma_job_process(&job); - } - - return (ma_thread_result)0; -} -#endif - -MA_API ma_resource_manager_config ma_resource_manager_config_init(void) -{ - ma_resource_manager_config config; - - MA_ZERO_OBJECT(&config); - config.decodedFormat = ma_format_unknown; - config.decodedChannels = 0; - config.decodedSampleRate = 0; - config.jobThreadCount = 1; /* A single miniaudio-managed job thread by default. */ - config.jobQueueCapacity = MA_JOB_TYPE_RESOURCE_MANAGER_QUEUE_CAPACITY; - - /* Flags. */ - config.flags = 0; - #ifdef MA_NO_THREADING - { - /* Threading is disabled at compile time so disable threading at runtime as well by default. */ - config.flags |= MA_RESOURCE_MANAGER_FLAG_NO_THREADING; - config.jobThreadCount = 0; - } - #endif - - return config; -} - - -MA_API ma_result ma_resource_manager_init(const ma_resource_manager_config* pConfig, ma_resource_manager* pResourceManager) -{ - ma_result result; - ma_job_queue_config jobQueueConfig; - - if (pResourceManager == NULL) { - return MA_INVALID_ARGS; - } - - MA_ZERO_OBJECT(pResourceManager); - - if (pConfig == NULL) { - return MA_INVALID_ARGS; - } - - #ifndef MA_NO_THREADING - { - if (pConfig->jobThreadCount > ma_countof(pResourceManager->jobThreads)) { - return MA_INVALID_ARGS; /* Requesting too many job threads. */ - } - } - #endif - - pResourceManager->config = *pConfig; - ma_allocation_callbacks_init_copy(&pResourceManager->config.allocationCallbacks, &pConfig->allocationCallbacks); - - /* Get the log set up early so we can start using it as soon as possible. */ - if (pResourceManager->config.pLog == NULL) { - result = ma_log_init(&pResourceManager->config.allocationCallbacks, &pResourceManager->log); - if (result == MA_SUCCESS) { - pResourceManager->config.pLog = &pResourceManager->log; - } else { - pResourceManager->config.pLog = NULL; /* Logging is unavailable. */ - } - } - - if (pResourceManager->config.pVFS == NULL) { - result = ma_default_vfs_init(&pResourceManager->defaultVFS, &pResourceManager->config.allocationCallbacks); - if (result != MA_SUCCESS) { - return result; /* Failed to initialize the default file system. */ - } - - pResourceManager->config.pVFS = &pResourceManager->defaultVFS; - } - - /* If threading has been disabled at compile time, enforce it at run time as well. */ - #ifdef MA_NO_THREADING - { - pResourceManager->config.flags |= MA_RESOURCE_MANAGER_FLAG_NO_THREADING; - } - #endif - - /* We need to force MA_RESOURCE_MANAGER_FLAG_NON_BLOCKING if MA_RESOURCE_MANAGER_FLAG_NO_THREADING is set. */ - if ((pResourceManager->config.flags & MA_RESOURCE_MANAGER_FLAG_NO_THREADING) != 0) { - pResourceManager->config.flags |= MA_RESOURCE_MANAGER_FLAG_NON_BLOCKING; - - /* We cannot allow job threads when MA_RESOURCE_MANAGER_FLAG_NO_THREADING has been set. This is an invalid use case. */ - if (pResourceManager->config.jobThreadCount > 0) { - return MA_INVALID_ARGS; - } - } - - /* Job queue. */ - jobQueueConfig.capacity = pResourceManager->config.jobQueueCapacity; - jobQueueConfig.flags = 0; - if ((pResourceManager->config.flags & MA_RESOURCE_MANAGER_FLAG_NON_BLOCKING) != 0) { - if (pResourceManager->config.jobThreadCount > 0) { - return MA_INVALID_ARGS; /* Non-blocking mode is only valid for self-managed job threads. */ - } - - jobQueueConfig.flags |= MA_JOB_QUEUE_FLAG_NON_BLOCKING; - } - - result = ma_job_queue_init(&jobQueueConfig, &pResourceManager->config.allocationCallbacks, &pResourceManager->jobQueue); - if (result != MA_SUCCESS) { - return result; - } - - - /* Custom decoding backends. */ - if (pConfig->ppCustomDecodingBackendVTables != NULL && pConfig->customDecodingBackendCount > 0) { - size_t sizeInBytes = sizeof(*pResourceManager->config.ppCustomDecodingBackendVTables) * pConfig->customDecodingBackendCount; - ma_decoding_backend_vtable** ppCustomDecodingBackendVTables; - - ppCustomDecodingBackendVTables = (ma_decoding_backend_vtable**)ma_malloc(sizeInBytes, &pResourceManager->config.allocationCallbacks); - if (pResourceManager->config.ppCustomDecodingBackendVTables == NULL) { - ma_job_queue_uninit(&pResourceManager->jobQueue, &pResourceManager->config.allocationCallbacks); - return MA_OUT_OF_MEMORY; - } - - MA_COPY_MEMORY(ppCustomDecodingBackendVTables, pConfig->ppCustomDecodingBackendVTables, sizeInBytes); - - pResourceManager->config.ppCustomDecodingBackendVTables = ppCustomDecodingBackendVTables; - pResourceManager->config.customDecodingBackendCount = pConfig->customDecodingBackendCount; - pResourceManager->config.pCustomDecodingBackendUserData = pConfig->pCustomDecodingBackendUserData; - } - - - - /* Here is where we initialize our threading stuff. We don't do this if we don't support threading. */ - if (ma_resource_manager_is_threading_enabled(pResourceManager)) { - #ifndef MA_NO_THREADING - { - ma_uint32 iJobThread; - - /* Data buffer lock. */ - result = ma_mutex_init(&pResourceManager->dataBufferBSTLock); - if (result != MA_SUCCESS) { - ma_job_queue_uninit(&pResourceManager->jobQueue, &pResourceManager->config.allocationCallbacks); - return result; - } - - /* Create the job threads last to ensure the threads has access to valid data. */ - for (iJobThread = 0; iJobThread < pResourceManager->config.jobThreadCount; iJobThread += 1) { - result = ma_thread_create(&pResourceManager->jobThreads[iJobThread], ma_thread_priority_normal, pResourceManager->config.jobThreadStackSize, ma_resource_manager_job_thread, pResourceManager, &pResourceManager->config.allocationCallbacks); - if (result != MA_SUCCESS) { - ma_mutex_uninit(&pResourceManager->dataBufferBSTLock); - ma_job_queue_uninit(&pResourceManager->jobQueue, &pResourceManager->config.allocationCallbacks); - return result; - } - } - } - #else - { - /* Threading is disabled at compile time. We should never get here because validation checks should have already been performed. */ - MA_ASSERT(MA_FALSE); - } - #endif - } - - return MA_SUCCESS; -} - - -static void ma_resource_manager_delete_all_data_buffer_nodes(ma_resource_manager* pResourceManager) -{ - MA_ASSERT(pResourceManager); - - /* If everything was done properly, there shouldn't be any active data buffers. */ - while (pResourceManager->pRootDataBufferNode != NULL) { - ma_resource_manager_data_buffer_node* pDataBufferNode = pResourceManager->pRootDataBufferNode; - ma_resource_manager_data_buffer_node_remove(pResourceManager, pDataBufferNode); - - /* The data buffer has been removed from the BST, so now we need to free its data. */ - ma_resource_manager_data_buffer_node_free(pResourceManager, pDataBufferNode); - } -} - -MA_API void ma_resource_manager_uninit(ma_resource_manager* pResourceManager) -{ - if (pResourceManager == NULL) { - return; - } - - /* - Job threads need to be killed first. To do this we need to post a quit message to the message queue and then wait for the thread. The quit message will never be removed from the - queue which means it will never not be returned after being encountered for the first time which means all threads will eventually receive it. - */ - ma_resource_manager_post_job_quit(pResourceManager); - - /* Wait for every job to finish before continuing to ensure nothing is sill trying to access any of our objects below. */ - if (ma_resource_manager_is_threading_enabled(pResourceManager)) { - #ifndef MA_NO_THREADING - { - ma_uint32 iJobThread; - - for (iJobThread = 0; iJobThread < pResourceManager->config.jobThreadCount; iJobThread += 1) { - ma_thread_wait(&pResourceManager->jobThreads[iJobThread]); - } - } - #else - { - MA_ASSERT(MA_FALSE); /* Should never hit this. */ - } - #endif - } - - /* At this point the thread should have returned and no other thread should be accessing our data. We can now delete all data buffers. */ - ma_resource_manager_delete_all_data_buffer_nodes(pResourceManager); - - /* The job queue is no longer needed. */ - ma_job_queue_uninit(&pResourceManager->jobQueue, &pResourceManager->config.allocationCallbacks); - - /* We're no longer doing anything with data buffers so the lock can now be uninitialized. */ - if (ma_resource_manager_is_threading_enabled(pResourceManager)) { - #ifndef MA_NO_THREADING - { - ma_mutex_uninit(&pResourceManager->dataBufferBSTLock); - } - #else - { - MA_ASSERT(MA_FALSE); /* Should never hit this. */ - } - #endif - } - - ma_free((ma_decoding_backend_vtable**)pResourceManager->config.ppCustomDecodingBackendVTables, &pResourceManager->config.allocationCallbacks); /* <-- Naughty const-cast, but this is safe. */ - - if (pResourceManager->config.pLog == &pResourceManager->log) { - ma_log_uninit(&pResourceManager->log); - } -} - -MA_API ma_log* ma_resource_manager_get_log(ma_resource_manager* pResourceManager) -{ - if (pResourceManager == NULL) { - return NULL; - } - - return pResourceManager->config.pLog; -} - - - -MA_API ma_resource_manager_data_source_config ma_resource_manager_data_source_config_init(void) -{ - ma_resource_manager_data_source_config config; - - MA_ZERO_OBJECT(&config); - config.rangeBegInPCMFrames = MA_DATA_SOURCE_DEFAULT_RANGE_BEG; - config.rangeEndInPCMFrames = MA_DATA_SOURCE_DEFAULT_RANGE_END; - config.loopPointBegInPCMFrames = MA_DATA_SOURCE_DEFAULT_LOOP_POINT_BEG; - config.loopPointEndInPCMFrames = MA_DATA_SOURCE_DEFAULT_LOOP_POINT_END; - config.isLooping = MA_FALSE; - - return config; -} - - -static ma_decoder_config ma_resource_manager__init_decoder_config(ma_resource_manager* pResourceManager) -{ - ma_decoder_config config; - - config = ma_decoder_config_init(pResourceManager->config.decodedFormat, pResourceManager->config.decodedChannels, pResourceManager->config.decodedSampleRate); - config.allocationCallbacks = pResourceManager->config.allocationCallbacks; - config.ppCustomBackendVTables = pResourceManager->config.ppCustomDecodingBackendVTables; - config.customBackendCount = pResourceManager->config.customDecodingBackendCount; - config.pCustomBackendUserData = pResourceManager->config.pCustomDecodingBackendUserData; - - return config; -} - -static ma_result ma_resource_manager__init_decoder(ma_resource_manager* pResourceManager, const char* pFilePath, const wchar_t* pFilePathW, ma_decoder* pDecoder) -{ - ma_result result; - ma_decoder_config config; - - MA_ASSERT(pResourceManager != NULL); - MA_ASSERT(pFilePath != NULL || pFilePathW != NULL); - MA_ASSERT(pDecoder != NULL); - - config = ma_resource_manager__init_decoder_config(pResourceManager); - - if (pFilePath != NULL) { - result = ma_decoder_init_vfs(pResourceManager->config.pVFS, pFilePath, &config, pDecoder); - if (result != MA_SUCCESS) { - ma_log_postf(ma_resource_manager_get_log(pResourceManager), MA_LOG_LEVEL_WARNING, "Failed to load file \"%s\". %s.\n", pFilePath, ma_result_description(result)); - return result; - } - } else { - result = ma_decoder_init_vfs_w(pResourceManager->config.pVFS, pFilePathW, &config, pDecoder); - if (result != MA_SUCCESS) { - #if (defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L) || defined(_MSC_VER) - ma_log_postf(ma_resource_manager_get_log(pResourceManager), MA_LOG_LEVEL_WARNING, "Failed to load file \"%ls\". %s.\n", pFilePathW, ma_result_description(result)); - #endif - return result; - } - } - - return MA_SUCCESS; -} - -static ma_bool32 ma_resource_manager_data_buffer_has_connector(ma_resource_manager_data_buffer* pDataBuffer) -{ - return ma_atomic_bool32_get(&pDataBuffer->isConnectorInitialized); -} - -static ma_data_source* ma_resource_manager_data_buffer_get_connector(ma_resource_manager_data_buffer* pDataBuffer) -{ - if (ma_resource_manager_data_buffer_has_connector(pDataBuffer) == MA_FALSE) { - return NULL; /* Connector not yet initialized. */ - } - - switch (pDataBuffer->pNode->data.type) - { - case ma_resource_manager_data_supply_type_encoded: return &pDataBuffer->connector.decoder; - case ma_resource_manager_data_supply_type_decoded: return &pDataBuffer->connector.buffer; - case ma_resource_manager_data_supply_type_decoded_paged: return &pDataBuffer->connector.pagedBuffer; - - case ma_resource_manager_data_supply_type_unknown: - default: - { - ma_log_postf(ma_resource_manager_get_log(pDataBuffer->pResourceManager), MA_LOG_LEVEL_ERROR, "Failed to retrieve data buffer connector. Unknown data supply type.\n"); - return NULL; - }; - }; -} - -static ma_result ma_resource_manager_data_buffer_init_connector(ma_resource_manager_data_buffer* pDataBuffer, const ma_resource_manager_data_source_config* pConfig, ma_async_notification* pInitNotification, ma_fence* pInitFence) -{ - ma_result result; - - MA_ASSERT(pDataBuffer != NULL); - MA_ASSERT(pConfig != NULL); - MA_ASSERT(ma_resource_manager_data_buffer_has_connector(pDataBuffer) == MA_FALSE); - - /* The underlying data buffer must be initialized before we'll be able to know how to initialize the backend. */ - result = ma_resource_manager_data_buffer_node_result(pDataBuffer->pNode); - if (result != MA_SUCCESS && result != MA_BUSY) { - return result; /* The data buffer is in an erroneous state. */ - } - - /* - We need to initialize either a ma_decoder or an ma_audio_buffer depending on whether or not the backing data is encoded or decoded. These act as the - "instance" to the data and are used to form the connection between underlying data buffer and the data source. If the data buffer is decoded, we can use - an ma_audio_buffer. This enables us to use memory mapping when mixing which saves us a bit of data movement overhead. - */ - switch (ma_resource_manager_data_buffer_node_get_data_supply_type(pDataBuffer->pNode)) - { - case ma_resource_manager_data_supply_type_encoded: /* Connector is a decoder. */ - { - ma_decoder_config config; - config = ma_resource_manager__init_decoder_config(pDataBuffer->pResourceManager); - result = ma_decoder_init_memory(pDataBuffer->pNode->data.backend.encoded.pData, pDataBuffer->pNode->data.backend.encoded.sizeInBytes, &config, &pDataBuffer->connector.decoder); - } break; - - case ma_resource_manager_data_supply_type_decoded: /* Connector is an audio buffer. */ - { - ma_audio_buffer_config config; - config = ma_audio_buffer_config_init(pDataBuffer->pNode->data.backend.decoded.format, pDataBuffer->pNode->data.backend.decoded.channels, pDataBuffer->pNode->data.backend.decoded.totalFrameCount, pDataBuffer->pNode->data.backend.decoded.pData, NULL); - result = ma_audio_buffer_init(&config, &pDataBuffer->connector.buffer); - } break; - - case ma_resource_manager_data_supply_type_decoded_paged: /* Connector is a paged audio buffer. */ - { - ma_paged_audio_buffer_config config; - config = ma_paged_audio_buffer_config_init(&pDataBuffer->pNode->data.backend.decodedPaged.data); - result = ma_paged_audio_buffer_init(&config, &pDataBuffer->connector.pagedBuffer); - } break; - - case ma_resource_manager_data_supply_type_unknown: - default: - { - /* Unknown data supply type. Should never happen. Need to post an error here. */ - return MA_INVALID_ARGS; - }; - } - - /* - Initialization of the connector is when we can fire the init notification. This will give the application access to - the format/channels/rate of the data source. - */ - if (result == MA_SUCCESS) { - /* - The resource manager supports the ability to set the range and loop settings via a config at - initialization time. This results in an case where the ranges could be set explicitly via - ma_data_source_set_*() before we get to this point here. If this happens, we'll end up - hitting a case where we just override those settings which results in what feels like a bug. - - To address this we only change the relevant properties if they're not equal to defaults. If - they're equal to defaults there's no need to change them anyway. If they're *not* set to the - default values, we can assume the user has set the range and loop settings via the config. If - they're doing their own calls to ma_data_source_set_*() in addition to setting them via the - config, that's entirely on the caller and any synchronization issue becomes their problem. - */ - if (pConfig->rangeBegInPCMFrames != MA_DATA_SOURCE_DEFAULT_RANGE_BEG || pConfig->rangeEndInPCMFrames != MA_DATA_SOURCE_DEFAULT_RANGE_END) { - ma_data_source_set_range_in_pcm_frames(pDataBuffer, pConfig->rangeBegInPCMFrames, pConfig->rangeEndInPCMFrames); - } - - if (pConfig->loopPointBegInPCMFrames != MA_DATA_SOURCE_DEFAULT_LOOP_POINT_BEG || pConfig->loopPointEndInPCMFrames != MA_DATA_SOURCE_DEFAULT_LOOP_POINT_END) { - ma_data_source_set_loop_point_in_pcm_frames(pDataBuffer, pConfig->loopPointBegInPCMFrames, pConfig->loopPointEndInPCMFrames); - } - - if (pConfig->isLooping != MA_FALSE) { - ma_data_source_set_looping(pDataBuffer, pConfig->isLooping); - } - - ma_atomic_bool32_set(&pDataBuffer->isConnectorInitialized, MA_TRUE); - - if (pInitNotification != NULL) { - ma_async_notification_signal(pInitNotification); - } - - if (pInitFence != NULL) { - ma_fence_release(pInitFence); - } - } - - /* At this point the backend should be initialized. We do *not* want to set pDataSource->result here - that needs to be done at a higher level to ensure it's done as the last step. */ - return result; -} - -static ma_result ma_resource_manager_data_buffer_uninit_connector(ma_resource_manager* pResourceManager, ma_resource_manager_data_buffer* pDataBuffer) -{ - MA_ASSERT(pResourceManager != NULL); - MA_ASSERT(pDataBuffer != NULL); - - (void)pResourceManager; - - switch (ma_resource_manager_data_buffer_node_get_data_supply_type(pDataBuffer->pNode)) - { - case ma_resource_manager_data_supply_type_encoded: /* Connector is a decoder. */ - { - ma_decoder_uninit(&pDataBuffer->connector.decoder); - } break; - - case ma_resource_manager_data_supply_type_decoded: /* Connector is an audio buffer. */ - { - ma_audio_buffer_uninit(&pDataBuffer->connector.buffer); - } break; - - case ma_resource_manager_data_supply_type_decoded_paged: /* Connector is a paged audio buffer. */ - { - ma_paged_audio_buffer_uninit(&pDataBuffer->connector.pagedBuffer); - } break; - - case ma_resource_manager_data_supply_type_unknown: - default: - { - /* Unknown data supply type. Should never happen. Need to post an error here. */ - return MA_INVALID_ARGS; - }; - } - - return MA_SUCCESS; -} - -static ma_uint32 ma_resource_manager_data_buffer_node_next_execution_order(ma_resource_manager_data_buffer_node* pDataBufferNode) -{ - MA_ASSERT(pDataBufferNode != NULL); - return ma_atomic_fetch_add_32(&pDataBufferNode->executionCounter, 1); -} - -static ma_result ma_resource_manager_data_buffer_node_init_supply_encoded(ma_resource_manager* pResourceManager, ma_resource_manager_data_buffer_node* pDataBufferNode, const char* pFilePath, const wchar_t* pFilePathW) -{ - ma_result result; - size_t dataSizeInBytes; - void* pData; - - MA_ASSERT(pResourceManager != NULL); - MA_ASSERT(pDataBufferNode != NULL); - MA_ASSERT(pFilePath != NULL || pFilePathW != NULL); - - result = ma_vfs_open_and_read_file_ex(pResourceManager->config.pVFS, pFilePath, pFilePathW, &pData, &dataSizeInBytes, &pResourceManager->config.allocationCallbacks); - if (result != MA_SUCCESS) { - if (pFilePath != NULL) { - ma_log_postf(ma_resource_manager_get_log(pResourceManager), MA_LOG_LEVEL_WARNING, "Failed to load file \"%s\". %s.\n", pFilePath, ma_result_description(result)); - } else { - #if (defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L) || defined(_MSC_VER) - ma_log_postf(ma_resource_manager_get_log(pResourceManager), MA_LOG_LEVEL_WARNING, "Failed to load file \"%ls\". %s.\n", pFilePathW, ma_result_description(result)); - #endif - } - - return result; - } - - pDataBufferNode->data.backend.encoded.pData = pData; - pDataBufferNode->data.backend.encoded.sizeInBytes = dataSizeInBytes; - ma_resource_manager_data_buffer_node_set_data_supply_type(pDataBufferNode, ma_resource_manager_data_supply_type_encoded); /* <-- Must be set last. */ - - return MA_SUCCESS; -} - -static ma_result ma_resource_manager_data_buffer_node_init_supply_decoded(ma_resource_manager* pResourceManager, ma_resource_manager_data_buffer_node* pDataBufferNode, const char* pFilePath, const wchar_t* pFilePathW, ma_uint32 flags, ma_decoder** ppDecoder) -{ - ma_result result = MA_SUCCESS; - ma_decoder* pDecoder; - ma_uint64 totalFrameCount; - - MA_ASSERT(pResourceManager != NULL); - MA_ASSERT(pDataBufferNode != NULL); - MA_ASSERT(ppDecoder != NULL); - MA_ASSERT(pFilePath != NULL || pFilePathW != NULL); - - *ppDecoder = NULL; /* For safety. */ - - pDecoder = (ma_decoder*)ma_malloc(sizeof(*pDecoder), &pResourceManager->config.allocationCallbacks); - if (pDecoder == NULL) { - return MA_OUT_OF_MEMORY; - } - - result = ma_resource_manager__init_decoder(pResourceManager, pFilePath, pFilePathW, pDecoder); - if (result != MA_SUCCESS) { - ma_free(pDecoder, &pResourceManager->config.allocationCallbacks); - return result; - } - - /* - At this point we have the decoder and we now need to initialize the data supply. This will - be either a decoded buffer, or a decoded paged buffer. A regular buffer is just one big heap - allocated buffer, whereas a paged buffer is a linked list of paged-sized buffers. The latter - is used when the length of a sound is unknown until a full decode has been performed. - */ - if ((flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_UNKNOWN_LENGTH) == 0) { - result = ma_decoder_get_length_in_pcm_frames(pDecoder, &totalFrameCount); - if (result != MA_SUCCESS) { - return result; - } - } else { - totalFrameCount = 0; - } - - if (totalFrameCount > 0) { - /* It's a known length. The data supply is a regular decoded buffer. */ - ma_uint64 dataSizeInBytes; - void* pData; - - dataSizeInBytes = totalFrameCount * ma_get_bytes_per_frame(pDecoder->outputFormat, pDecoder->outputChannels); - if (dataSizeInBytes > MA_SIZE_MAX) { - ma_decoder_uninit(pDecoder); - ma_free(pDecoder, &pResourceManager->config.allocationCallbacks); - return MA_TOO_BIG; - } - - pData = ma_malloc((size_t)dataSizeInBytes, &pResourceManager->config.allocationCallbacks); - if (pData == NULL) { - ma_decoder_uninit(pDecoder); - ma_free(pDecoder, &pResourceManager->config.allocationCallbacks); - return MA_OUT_OF_MEMORY; - } - - /* The buffer needs to be initialized to silence in case the caller reads from it. */ - ma_silence_pcm_frames(pData, totalFrameCount, pDecoder->outputFormat, pDecoder->outputChannels); - - /* Data has been allocated and the data supply can now be initialized. */ - pDataBufferNode->data.backend.decoded.pData = pData; - pDataBufferNode->data.backend.decoded.totalFrameCount = totalFrameCount; - pDataBufferNode->data.backend.decoded.format = pDecoder->outputFormat; - pDataBufferNode->data.backend.decoded.channels = pDecoder->outputChannels; - pDataBufferNode->data.backend.decoded.sampleRate = pDecoder->outputSampleRate; - pDataBufferNode->data.backend.decoded.decodedFrameCount = 0; - ma_resource_manager_data_buffer_node_set_data_supply_type(pDataBufferNode, ma_resource_manager_data_supply_type_decoded); /* <-- Must be set last. */ - } else { - /* - It's an unknown length. The data supply is a paged decoded buffer. Setting this up is - actually easier than the non-paged decoded buffer because we just need to initialize - a ma_paged_audio_buffer object. - */ - result = ma_paged_audio_buffer_data_init(pDecoder->outputFormat, pDecoder->outputChannels, &pDataBufferNode->data.backend.decodedPaged.data); - if (result != MA_SUCCESS) { - ma_decoder_uninit(pDecoder); - ma_free(pDecoder, &pResourceManager->config.allocationCallbacks); - return result; - } - - pDataBufferNode->data.backend.decodedPaged.sampleRate = pDecoder->outputSampleRate; - pDataBufferNode->data.backend.decodedPaged.decodedFrameCount = 0; - ma_resource_manager_data_buffer_node_set_data_supply_type(pDataBufferNode, ma_resource_manager_data_supply_type_decoded_paged); /* <-- Must be set last. */ - } - - *ppDecoder = pDecoder; - - return MA_SUCCESS; -} - -static ma_result ma_resource_manager_data_buffer_node_decode_next_page(ma_resource_manager* pResourceManager, ma_resource_manager_data_buffer_node* pDataBufferNode, ma_decoder* pDecoder) -{ - ma_result result = MA_SUCCESS; - ma_uint64 pageSizeInFrames; - ma_uint64 framesToTryReading; - ma_uint64 framesRead; - - MA_ASSERT(pResourceManager != NULL); - MA_ASSERT(pDataBufferNode != NULL); - MA_ASSERT(pDecoder != NULL); - - /* We need to know the size of a page in frames to know how many frames to decode. */ - pageSizeInFrames = MA_RESOURCE_MANAGER_PAGE_SIZE_IN_MILLISECONDS * (pDecoder->outputSampleRate/1000); - framesToTryReading = pageSizeInFrames; - - /* - Here is where we do the decoding of the next page. We'll run a slightly different path depending - on whether or not we're using a flat or paged buffer because the allocation of the page differs - between the two. For a flat buffer it's an offset to an already-allocated buffer. For a paged - buffer, we need to allocate a new page and attach it to the linked list. - */ - switch (ma_resource_manager_data_buffer_node_get_data_supply_type(pDataBufferNode)) - { - case ma_resource_manager_data_supply_type_decoded: - { - /* The destination buffer is an offset to the existing buffer. Don't read more than we originally retrieved when we first initialized the decoder. */ - void* pDst; - ma_uint64 framesRemaining = pDataBufferNode->data.backend.decoded.totalFrameCount - pDataBufferNode->data.backend.decoded.decodedFrameCount; - if (framesToTryReading > framesRemaining) { - framesToTryReading = framesRemaining; - } - - if (framesToTryReading > 0) { - pDst = ma_offset_ptr( - pDataBufferNode->data.backend.decoded.pData, - pDataBufferNode->data.backend.decoded.decodedFrameCount * ma_get_bytes_per_frame(pDataBufferNode->data.backend.decoded.format, pDataBufferNode->data.backend.decoded.channels) - ); - MA_ASSERT(pDst != NULL); - - result = ma_decoder_read_pcm_frames(pDecoder, pDst, framesToTryReading, &framesRead); - if (framesRead > 0) { - pDataBufferNode->data.backend.decoded.decodedFrameCount += framesRead; - } - } else { - framesRead = 0; - } - } break; - - case ma_resource_manager_data_supply_type_decoded_paged: - { - /* The destination buffer is a freshly allocated page. */ - ma_paged_audio_buffer_page* pPage; - - result = ma_paged_audio_buffer_data_allocate_page(&pDataBufferNode->data.backend.decodedPaged.data, framesToTryReading, NULL, &pResourceManager->config.allocationCallbacks, &pPage); - if (result != MA_SUCCESS) { - return result; - } - - result = ma_decoder_read_pcm_frames(pDecoder, pPage->pAudioData, framesToTryReading, &framesRead); - if (result == MA_SUCCESS && framesRead > 0) { - pPage->sizeInFrames = framesRead; - - result = ma_paged_audio_buffer_data_append_page(&pDataBufferNode->data.backend.decodedPaged.data, pPage); - if (result == MA_SUCCESS) { - pDataBufferNode->data.backend.decodedPaged.decodedFrameCount += framesRead; - } else { - /* Failed to append the page. Just abort and set the status to MA_AT_END. */ - ma_paged_audio_buffer_data_free_page(&pDataBufferNode->data.backend.decodedPaged.data, pPage, &pResourceManager->config.allocationCallbacks); - result = MA_AT_END; - } - } else { - /* No frames were read. Free the page and just set the status to MA_AT_END. */ - ma_paged_audio_buffer_data_free_page(&pDataBufferNode->data.backend.decodedPaged.data, pPage, &pResourceManager->config.allocationCallbacks); - result = MA_AT_END; - } - } break; - - case ma_resource_manager_data_supply_type_encoded: - case ma_resource_manager_data_supply_type_unknown: - default: - { - /* Unexpected data supply type. */ - ma_log_postf(ma_resource_manager_get_log(pResourceManager), MA_LOG_LEVEL_ERROR, "Unexpected data supply type (%d) when decoding page.", ma_resource_manager_data_buffer_node_get_data_supply_type(pDataBufferNode)); - return MA_ERROR; - }; - } - - if (result == MA_SUCCESS && framesRead == 0) { - result = MA_AT_END; - } - - return result; -} - -static ma_result ma_resource_manager_data_buffer_node_acquire_critical_section(ma_resource_manager* pResourceManager, const char* pFilePath, const wchar_t* pFilePathW, ma_uint32 hashedName32, ma_uint32 flags, const ma_resource_manager_data_supply* pExistingData, ma_fence* pInitFence, ma_fence* pDoneFence, ma_resource_manager_inline_notification* pInitNotification, ma_resource_manager_data_buffer_node** ppDataBufferNode) -{ - ma_result result = MA_SUCCESS; - ma_resource_manager_data_buffer_node* pDataBufferNode = NULL; - ma_resource_manager_data_buffer_node* pInsertPoint; - - if (ppDataBufferNode != NULL) { - *ppDataBufferNode = NULL; - } - - result = ma_resource_manager_data_buffer_node_insert_point(pResourceManager, hashedName32, &pInsertPoint); - if (result == MA_ALREADY_EXISTS) { - /* The node already exists. We just need to increment the reference count. */ - pDataBufferNode = pInsertPoint; - - result = ma_resource_manager_data_buffer_node_increment_ref(pResourceManager, pDataBufferNode, NULL); - if (result != MA_SUCCESS) { - return result; /* Should never happen. Failed to increment the reference count. */ - } - - result = MA_ALREADY_EXISTS; - goto done; - } else { - /* - The node does not already exist. We need to post a LOAD_DATA_BUFFER_NODE job here. This - needs to be done inside the critical section to ensure an uninitialization of the node - does not occur before initialization on another thread. - */ - pDataBufferNode = (ma_resource_manager_data_buffer_node*)ma_malloc(sizeof(*pDataBufferNode), &pResourceManager->config.allocationCallbacks); - if (pDataBufferNode == NULL) { - return MA_OUT_OF_MEMORY; - } - - MA_ZERO_OBJECT(pDataBufferNode); - pDataBufferNode->hashedName32 = hashedName32; - pDataBufferNode->refCount = 1; /* Always set to 1 by default (this is our first reference). */ - - if (pExistingData == NULL) { - pDataBufferNode->data.type = ma_resource_manager_data_supply_type_unknown; /* <-- We won't know this until we start decoding. */ - pDataBufferNode->result = MA_BUSY; /* Must be set to MA_BUSY before we leave the critical section, so might as well do it now. */ - pDataBufferNode->isDataOwnedByResourceManager = MA_TRUE; - } else { - pDataBufferNode->data = *pExistingData; - pDataBufferNode->result = MA_SUCCESS; /* Not loading asynchronously, so just set the status */ - pDataBufferNode->isDataOwnedByResourceManager = MA_FALSE; - } - - result = ma_resource_manager_data_buffer_node_insert_at(pResourceManager, pDataBufferNode, pInsertPoint); - if (result != MA_SUCCESS) { - ma_free(pDataBufferNode, &pResourceManager->config.allocationCallbacks); - return result; /* Should never happen. Failed to insert the data buffer into the BST. */ - } - - /* - Here is where we'll post the job, but only if we're loading asynchronously. If we're - loading synchronously we'll defer loading to a later stage, outside of the critical - section. - */ - if (pDataBufferNode->isDataOwnedByResourceManager && (flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_ASYNC) != 0) { - /* Loading asynchronously. Post the job. */ - ma_job job; - char* pFilePathCopy = NULL; - wchar_t* pFilePathWCopy = NULL; - - /* We need a copy of the file path. We should probably make this more efficient, but for now we'll do a transient memory allocation. */ - if (pFilePath != NULL) { - pFilePathCopy = ma_copy_string(pFilePath, &pResourceManager->config.allocationCallbacks); - } else { - pFilePathWCopy = ma_copy_string_w(pFilePathW, &pResourceManager->config.allocationCallbacks); - } - - if (pFilePathCopy == NULL && pFilePathWCopy == NULL) { - ma_resource_manager_data_buffer_node_remove(pResourceManager, pDataBufferNode); - ma_free(pDataBufferNode, &pResourceManager->config.allocationCallbacks); - return MA_OUT_OF_MEMORY; - } - - if ((flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_WAIT_INIT) != 0) { - ma_resource_manager_inline_notification_init(pResourceManager, pInitNotification); - } - - /* Acquire init and done fences before posting the job. These will be unacquired by the job thread. */ - if (pInitFence != NULL) { ma_fence_acquire(pInitFence); } - if (pDoneFence != NULL) { ma_fence_acquire(pDoneFence); } - - /* We now have everything we need to post the job to the job thread. */ - job = ma_job_init(MA_JOB_TYPE_RESOURCE_MANAGER_LOAD_DATA_BUFFER_NODE); - job.order = ma_resource_manager_data_buffer_node_next_execution_order(pDataBufferNode); - job.data.resourceManager.loadDataBufferNode.pResourceManager = pResourceManager; - job.data.resourceManager.loadDataBufferNode.pDataBufferNode = pDataBufferNode; - job.data.resourceManager.loadDataBufferNode.pFilePath = pFilePathCopy; - job.data.resourceManager.loadDataBufferNode.pFilePathW = pFilePathWCopy; - job.data.resourceManager.loadDataBufferNode.flags = flags; - job.data.resourceManager.loadDataBufferNode.pInitNotification = ((flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_WAIT_INIT) != 0) ? pInitNotification : NULL; - job.data.resourceManager.loadDataBufferNode.pDoneNotification = NULL; - job.data.resourceManager.loadDataBufferNode.pInitFence = pInitFence; - job.data.resourceManager.loadDataBufferNode.pDoneFence = pDoneFence; - - if ((flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_WAIT_INIT) != 0) { - result = ma_job_process(&job); - } else { - result = ma_resource_manager_post_job(pResourceManager, &job); - } - - if (result != MA_SUCCESS) { - /* Failed to post job. Probably ran out of memory. */ - ma_log_postf(ma_resource_manager_get_log(pResourceManager), MA_LOG_LEVEL_ERROR, "Failed to post MA_JOB_TYPE_RESOURCE_MANAGER_LOAD_DATA_BUFFER_NODE job. %s.\n", ma_result_description(result)); - - /* - Fences were acquired before posting the job, but since the job was not able to - be posted, we need to make sure we release them so nothing gets stuck waiting. - */ - if (pInitFence != NULL) { ma_fence_release(pInitFence); } - if (pDoneFence != NULL) { ma_fence_release(pDoneFence); } - - if ((flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_WAIT_INIT) != 0) { - ma_resource_manager_inline_notification_uninit(pInitNotification); - } else { - /* These will have been freed by the job thread, but with WAIT_INIT they will already have happened since the job has already been handled. */ - ma_free(pFilePathCopy, &pResourceManager->config.allocationCallbacks); - ma_free(pFilePathWCopy, &pResourceManager->config.allocationCallbacks); - } - - ma_resource_manager_data_buffer_node_remove(pResourceManager, pDataBufferNode); - ma_free(pDataBufferNode, &pResourceManager->config.allocationCallbacks); - - return result; - } - } - } - -done: - if (ppDataBufferNode != NULL) { - *ppDataBufferNode = pDataBufferNode; - } - - return result; -} - -static ma_result ma_resource_manager_data_buffer_node_acquire(ma_resource_manager* pResourceManager, const char* pFilePath, const wchar_t* pFilePathW, ma_uint32 hashedName32, ma_uint32 flags, const ma_resource_manager_data_supply* pExistingData, ma_fence* pInitFence, ma_fence* pDoneFence, ma_resource_manager_data_buffer_node** ppDataBufferNode) -{ - ma_result result = MA_SUCCESS; - ma_bool32 nodeAlreadyExists = MA_FALSE; - ma_resource_manager_data_buffer_node* pDataBufferNode = NULL; - ma_resource_manager_inline_notification initNotification; /* Used when the WAIT_INIT flag is set. */ - - if (ppDataBufferNode != NULL) { - *ppDataBufferNode = NULL; /* Safety. */ - } - - if (pResourceManager == NULL || (pFilePath == NULL && pFilePathW == NULL && hashedName32 == 0)) { - return MA_INVALID_ARGS; - } - - /* If we're specifying existing data, it must be valid. */ - if (pExistingData != NULL && pExistingData->type == ma_resource_manager_data_supply_type_unknown) { - return MA_INVALID_ARGS; - } - - /* If we don't support threading, remove the ASYNC flag to make the rest of this a bit simpler. */ - if (ma_resource_manager_is_threading_enabled(pResourceManager) == MA_FALSE) { - flags &= ~MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_ASYNC; - } - - if (hashedName32 == 0) { - if (pFilePath != NULL) { - hashedName32 = ma_hash_string_32(pFilePath); - } else { - hashedName32 = ma_hash_string_w_32(pFilePathW); - } - } - - /* - Here is where we either increment the node's reference count or allocate a new one and add it - to the BST. When allocating a new node, we need to make sure the LOAD_DATA_BUFFER_NODE job is - posted inside the critical section just in case the caller immediately uninitializes the node - as this will ensure the FREE_DATA_BUFFER_NODE job is given an execution order such that the - node is not uninitialized before initialization. - */ - ma_resource_manager_data_buffer_bst_lock(pResourceManager); - { - result = ma_resource_manager_data_buffer_node_acquire_critical_section(pResourceManager, pFilePath, pFilePathW, hashedName32, flags, pExistingData, pInitFence, pDoneFence, &initNotification, &pDataBufferNode); - } - ma_resource_manager_data_buffer_bst_unlock(pResourceManager); - - if (result == MA_ALREADY_EXISTS) { - nodeAlreadyExists = MA_TRUE; - result = MA_SUCCESS; - } else { - if (result != MA_SUCCESS) { - return result; - } - } - - /* - If we're loading synchronously, we'll need to load everything now. When loading asynchronously, - a job will have been posted inside the BST critical section so that an uninitialization can be - allocated an appropriate execution order thereby preventing it from being uninitialized before - the node is initialized by the decoding thread(s). - */ - if (nodeAlreadyExists == MA_FALSE) { /* Don't need to try loading anything if the node already exists. */ - if (pFilePath == NULL && pFilePathW == NULL) { - /* - If this path is hit, it means a buffer is being copied (i.e. initialized from only the - hashed name), but that node has been freed in the meantime, probably from some other - thread. This is an invalid operation. - */ - ma_log_postf(ma_resource_manager_get_log(pResourceManager), MA_LOG_LEVEL_WARNING, "Cloning data buffer node failed because the source node was released. The source node must remain valid until the cloning has completed.\n"); - result = MA_INVALID_OPERATION; - goto done; - } - - if (pDataBufferNode->isDataOwnedByResourceManager) { - if ((flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_ASYNC) == 0) { - /* Loading synchronously. Load the sound in it's entirety here. */ - if ((flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_DECODE) == 0) { - /* No decoding. This is the simple case - just store the file contents in memory. */ - result = ma_resource_manager_data_buffer_node_init_supply_encoded(pResourceManager, pDataBufferNode, pFilePath, pFilePathW); - if (result != MA_SUCCESS) { - goto done; - } - } else { - /* Decoding. We do this the same way as we do when loading asynchronously. */ - ma_decoder* pDecoder; - result = ma_resource_manager_data_buffer_node_init_supply_decoded(pResourceManager, pDataBufferNode, pFilePath, pFilePathW, flags, &pDecoder); - if (result != MA_SUCCESS) { - goto done; - } - - /* We have the decoder, now decode page by page just like we do when loading asynchronously. */ - for (;;) { - /* Decode next page. */ - result = ma_resource_manager_data_buffer_node_decode_next_page(pResourceManager, pDataBufferNode, pDecoder); - if (result != MA_SUCCESS) { - break; /* Will return MA_AT_END when the last page has been decoded. */ - } - } - - /* Reaching the end needs to be considered successful. */ - if (result == MA_AT_END) { - result = MA_SUCCESS; - } - - /* - At this point the data buffer is either fully decoded or some error occurred. Either - way, the decoder is no longer necessary. - */ - ma_decoder_uninit(pDecoder); - ma_free(pDecoder, &pResourceManager->config.allocationCallbacks); - } - - /* Getting here means we were successful. Make sure the status of the node is updated accordingly. */ - ma_atomic_exchange_i32(&pDataBufferNode->result, result); - } else { - /* Loading asynchronously. We may need to wait for initialization. */ - if ((flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_WAIT_INIT) != 0) { - ma_resource_manager_inline_notification_wait(&initNotification); - } - } - } else { - /* The data is not managed by the resource manager so there's nothing else to do. */ - MA_ASSERT(pExistingData != NULL); - } - } - -done: - /* If we failed to initialize the data buffer we need to free it. */ - if (result != MA_SUCCESS) { - if (nodeAlreadyExists == MA_FALSE) { - ma_resource_manager_data_buffer_node_remove(pResourceManager, pDataBufferNode); - ma_free(pDataBufferNode, &pResourceManager->config.allocationCallbacks); - } - } - - /* - The init notification needs to be uninitialized. This will be used if the node does not already - exist, and we've specified ASYNC | WAIT_INIT. - */ - if (nodeAlreadyExists == MA_FALSE && pDataBufferNode->isDataOwnedByResourceManager && (flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_ASYNC) != 0) { - if ((flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_WAIT_INIT) != 0) { - ma_resource_manager_inline_notification_uninit(&initNotification); - } - } - - if (ppDataBufferNode != NULL) { - *ppDataBufferNode = pDataBufferNode; - } - - return result; -} - -static ma_result ma_resource_manager_data_buffer_node_unacquire(ma_resource_manager* pResourceManager, ma_resource_manager_data_buffer_node* pDataBufferNode, const char* pName, const wchar_t* pNameW) -{ - ma_result result = MA_SUCCESS; - ma_uint32 refCount = 0xFFFFFFFF; /* The new reference count of the node after decrementing. Initialize to non-0 to be safe we don't fall into the freeing path. */ - ma_uint32 hashedName32 = 0; - - if (pResourceManager == NULL) { - return MA_INVALID_ARGS; - } - - if (pDataBufferNode == NULL) { - if (pName == NULL && pNameW == NULL) { - return MA_INVALID_ARGS; - } - - if (pName != NULL) { - hashedName32 = ma_hash_string_32(pName); - } else { - hashedName32 = ma_hash_string_w_32(pNameW); - } - } - - /* - The first thing to do is decrement the reference counter of the node. Then, if the reference - count is zero, we need to free the node. If the node is still in the process of loading, we'll - need to post a job to the job queue to free the node. Otherwise we'll just do it here. - */ - ma_resource_manager_data_buffer_bst_lock(pResourceManager); - { - /* Might need to find the node. Must be done inside the critical section. */ - if (pDataBufferNode == NULL) { - result = ma_resource_manager_data_buffer_node_search(pResourceManager, hashedName32, &pDataBufferNode); - if (result != MA_SUCCESS) { - goto stage2; /* Couldn't find the node. */ - } - } - - result = ma_resource_manager_data_buffer_node_decrement_ref(pResourceManager, pDataBufferNode, &refCount); - if (result != MA_SUCCESS) { - goto stage2; /* Should never happen. */ - } - - if (refCount == 0) { - result = ma_resource_manager_data_buffer_node_remove(pResourceManager, pDataBufferNode); - if (result != MA_SUCCESS) { - goto stage2; /* An error occurred when trying to remove the data buffer. This should never happen. */ - } - } - } - ma_resource_manager_data_buffer_bst_unlock(pResourceManager); - -stage2: - if (result != MA_SUCCESS) { - return result; - } - - /* - Here is where we need to free the node. We don't want to do this inside the critical section - above because we want to keep that as small as possible for multi-threaded efficiency. - */ - if (refCount == 0) { - if (ma_resource_manager_data_buffer_node_result(pDataBufferNode) == MA_BUSY) { - /* The sound is still loading. We need to delay the freeing of the node to a safe time. */ - ma_job job; - - /* We need to mark the node as unavailable for the sake of the resource manager worker threads. */ - ma_atomic_exchange_i32(&pDataBufferNode->result, MA_UNAVAILABLE); - - job = ma_job_init(MA_JOB_TYPE_RESOURCE_MANAGER_FREE_DATA_BUFFER_NODE); - job.order = ma_resource_manager_data_buffer_node_next_execution_order(pDataBufferNode); - job.data.resourceManager.freeDataBufferNode.pResourceManager = pResourceManager; - job.data.resourceManager.freeDataBufferNode.pDataBufferNode = pDataBufferNode; - - result = ma_resource_manager_post_job(pResourceManager, &job); - if (result != MA_SUCCESS) { - ma_log_postf(ma_resource_manager_get_log(pResourceManager), MA_LOG_LEVEL_ERROR, "Failed to post MA_JOB_TYPE_RESOURCE_MANAGER_FREE_DATA_BUFFER_NODE job. %s.\n", ma_result_description(result)); - return result; - } - - /* If we don't support threading, process the job queue here. */ - if (ma_resource_manager_is_threading_enabled(pResourceManager) == MA_FALSE) { - while (ma_resource_manager_data_buffer_node_result(pDataBufferNode) == MA_BUSY) { - result = ma_resource_manager_process_next_job(pResourceManager); - if (result == MA_NO_DATA_AVAILABLE || result == MA_CANCELLED) { - result = MA_SUCCESS; - break; - } - } - } else { - /* Threading is enabled. The job queue will deal with the rest of the cleanup from here. */ - } - } else { - /* The sound isn't loading so we can just free the node here. */ - ma_resource_manager_data_buffer_node_free(pResourceManager, pDataBufferNode); - } - } - - return result; -} - - - -static ma_uint32 ma_resource_manager_data_buffer_next_execution_order(ma_resource_manager_data_buffer* pDataBuffer) -{ - MA_ASSERT(pDataBuffer != NULL); - return ma_atomic_fetch_add_32(&pDataBuffer->executionCounter, 1); -} - -static ma_result ma_resource_manager_data_buffer_cb__read_pcm_frames(ma_data_source* pDataSource, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead) -{ - return ma_resource_manager_data_buffer_read_pcm_frames((ma_resource_manager_data_buffer*)pDataSource, pFramesOut, frameCount, pFramesRead); -} - -static ma_result ma_resource_manager_data_buffer_cb__seek_to_pcm_frame(ma_data_source* pDataSource, ma_uint64 frameIndex) -{ - return ma_resource_manager_data_buffer_seek_to_pcm_frame((ma_resource_manager_data_buffer*)pDataSource, frameIndex); -} - -static ma_result ma_resource_manager_data_buffer_cb__get_data_format(ma_data_source* pDataSource, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate, ma_channel* pChannelMap, size_t channelMapCap) -{ - return ma_resource_manager_data_buffer_get_data_format((ma_resource_manager_data_buffer*)pDataSource, pFormat, pChannels, pSampleRate, pChannelMap, channelMapCap); -} - -static ma_result ma_resource_manager_data_buffer_cb__get_cursor_in_pcm_frames(ma_data_source* pDataSource, ma_uint64* pCursor) -{ - return ma_resource_manager_data_buffer_get_cursor_in_pcm_frames((ma_resource_manager_data_buffer*)pDataSource, pCursor); -} - -static ma_result ma_resource_manager_data_buffer_cb__get_length_in_pcm_frames(ma_data_source* pDataSource, ma_uint64* pLength) -{ - return ma_resource_manager_data_buffer_get_length_in_pcm_frames((ma_resource_manager_data_buffer*)pDataSource, pLength); -} - -static ma_result ma_resource_manager_data_buffer_cb__set_looping(ma_data_source* pDataSource, ma_bool32 isLooping) -{ - ma_resource_manager_data_buffer* pDataBuffer = (ma_resource_manager_data_buffer*)pDataSource; - MA_ASSERT(pDataBuffer != NULL); - - ma_atomic_exchange_32(&pDataBuffer->isLooping, isLooping); - - /* The looping state needs to be set on the connector as well or else looping won't work when we read audio data. */ - ma_data_source_set_looping(ma_resource_manager_data_buffer_get_connector(pDataBuffer), isLooping); - - return MA_SUCCESS; -} - -static ma_data_source_vtable g_ma_resource_manager_data_buffer_vtable = -{ - ma_resource_manager_data_buffer_cb__read_pcm_frames, - ma_resource_manager_data_buffer_cb__seek_to_pcm_frame, - ma_resource_manager_data_buffer_cb__get_data_format, - ma_resource_manager_data_buffer_cb__get_cursor_in_pcm_frames, - ma_resource_manager_data_buffer_cb__get_length_in_pcm_frames, - ma_resource_manager_data_buffer_cb__set_looping, - 0 -}; - -static ma_result ma_resource_manager_data_buffer_init_ex_internal(ma_resource_manager* pResourceManager, const ma_resource_manager_data_source_config* pConfig, ma_uint32 hashedName32, ma_resource_manager_data_buffer* pDataBuffer) -{ - ma_result result = MA_SUCCESS; - ma_resource_manager_data_buffer_node* pDataBufferNode; - ma_data_source_config dataSourceConfig; - ma_bool32 async; - ma_uint32 flags; - ma_resource_manager_pipeline_notifications notifications; - - if (pDataBuffer == NULL) { - if (pConfig != NULL && pConfig->pNotifications != NULL) { - ma_resource_manager_pipeline_notifications_signal_all_notifications(pConfig->pNotifications); - } - - return MA_INVALID_ARGS; - } - - MA_ZERO_OBJECT(pDataBuffer); - - if (pConfig == NULL) { - return MA_INVALID_ARGS; - } - - if (pConfig->pNotifications != NULL) { - notifications = *pConfig->pNotifications; /* From here on out we should be referencing `notifications` instead of `pNotifications`. Set this to NULL to catch errors at testing time. */ - } else { - MA_ZERO_OBJECT(¬ifications); - } - - /* For safety, always remove the ASYNC flag if threading is disabled on the resource manager. */ - flags = pConfig->flags; - if (ma_resource_manager_is_threading_enabled(pResourceManager) == MA_FALSE) { - flags &= ~MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_ASYNC; - } - - if (pConfig->isLooping) { - flags |= MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_LOOPING; - } - - async = (flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_ASYNC) != 0; - - /* - Fences need to be acquired before doing anything. These must be acquired and released outside of - the node to ensure there's no holes where ma_fence_wait() could prematurely return before the - data buffer has completed initialization. - - When loading asynchronously, the node acquisition routine below will acquire the fences on this - thread and then release them on the async thread when the operation is complete. - - These fences are always released at the "done" tag at the end of this function. They'll be - acquired a second if loading asynchronously. This double acquisition system is just done to - simplify code maintenance. - */ - ma_resource_manager_pipeline_notifications_acquire_all_fences(¬ifications); - { - /* We first need to acquire a node. If ASYNC is not set, this will not return until the entire sound has been loaded. */ - result = ma_resource_manager_data_buffer_node_acquire(pResourceManager, pConfig->pFilePath, pConfig->pFilePathW, hashedName32, flags, NULL, notifications.init.pFence, notifications.done.pFence, &pDataBufferNode); - if (result != MA_SUCCESS) { - ma_resource_manager_pipeline_notifications_signal_all_notifications(¬ifications); - goto done; - } - - dataSourceConfig = ma_data_source_config_init(); - dataSourceConfig.vtable = &g_ma_resource_manager_data_buffer_vtable; - - result = ma_data_source_init(&dataSourceConfig, &pDataBuffer->ds); - if (result != MA_SUCCESS) { - ma_resource_manager_data_buffer_node_unacquire(pResourceManager, pDataBufferNode, NULL, NULL); - ma_resource_manager_pipeline_notifications_signal_all_notifications(¬ifications); - goto done; - } - - pDataBuffer->pResourceManager = pResourceManager; - pDataBuffer->pNode = pDataBufferNode; - pDataBuffer->flags = flags; - pDataBuffer->result = MA_BUSY; /* Always default to MA_BUSY for safety. It'll be overwritten when loading completes or an error occurs. */ - - /* If we're loading asynchronously we need to post a job to the job queue to initialize the connector. */ - if (async == MA_FALSE || ma_resource_manager_data_buffer_node_result(pDataBufferNode) == MA_SUCCESS) { - /* Loading synchronously or the data has already been fully loaded. We can just initialize the connector from here without a job. */ - result = ma_resource_manager_data_buffer_init_connector(pDataBuffer, pConfig, NULL, NULL); - ma_atomic_exchange_i32(&pDataBuffer->result, result); - - ma_resource_manager_pipeline_notifications_signal_all_notifications(¬ifications); - goto done; - } else { - /* The node's data supply isn't initialized yet. The caller has requested that we load asynchronously so we need to post a job to do this. */ - ma_job job; - ma_resource_manager_inline_notification initNotification; /* Used when the WAIT_INIT flag is set. */ - - if ((flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_WAIT_INIT) != 0) { - ma_resource_manager_inline_notification_init(pResourceManager, &initNotification); - } - - /* - The status of the data buffer needs to be set to MA_BUSY before posting the job so that the - worker thread is aware of its busy state. If the LOAD_DATA_BUFFER job sees a status other - than MA_BUSY, it'll assume an error and fall through to an early exit. - */ - ma_atomic_exchange_i32(&pDataBuffer->result, MA_BUSY); - - /* Acquire fences a second time. These will be released by the async thread. */ - ma_resource_manager_pipeline_notifications_acquire_all_fences(¬ifications); - - job = ma_job_init(MA_JOB_TYPE_RESOURCE_MANAGER_LOAD_DATA_BUFFER); - job.order = ma_resource_manager_data_buffer_next_execution_order(pDataBuffer); - job.data.resourceManager.loadDataBuffer.pDataBuffer = pDataBuffer; - job.data.resourceManager.loadDataBuffer.pInitNotification = ((flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_WAIT_INIT) != 0) ? &initNotification : notifications.init.pNotification; - job.data.resourceManager.loadDataBuffer.pDoneNotification = notifications.done.pNotification; - job.data.resourceManager.loadDataBuffer.pInitFence = notifications.init.pFence; - job.data.resourceManager.loadDataBuffer.pDoneFence = notifications.done.pFence; - job.data.resourceManager.loadDataBuffer.rangeBegInPCMFrames = pConfig->rangeBegInPCMFrames; - job.data.resourceManager.loadDataBuffer.rangeEndInPCMFrames = pConfig->rangeEndInPCMFrames; - job.data.resourceManager.loadDataBuffer.loopPointBegInPCMFrames = pConfig->loopPointBegInPCMFrames; - job.data.resourceManager.loadDataBuffer.loopPointEndInPCMFrames = pConfig->loopPointEndInPCMFrames; - job.data.resourceManager.loadDataBuffer.isLooping = (flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_LOOPING) != 0; - - /* If we need to wait for initialization to complete we can just process the job in place. */ - if ((flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_WAIT_INIT) != 0) { - result = ma_job_process(&job); - } else { - result = ma_resource_manager_post_job(pResourceManager, &job); - } - - if (result != MA_SUCCESS) { - /* We failed to post the job. Most likely there isn't enough room in the queue's buffer. */ - ma_log_postf(ma_resource_manager_get_log(pResourceManager), MA_LOG_LEVEL_ERROR, "Failed to post MA_JOB_TYPE_RESOURCE_MANAGER_LOAD_DATA_BUFFER job. %s.\n", ma_result_description(result)); - ma_atomic_exchange_i32(&pDataBuffer->result, result); - - /* Release the fences after the result has been set on the data buffer. */ - ma_resource_manager_pipeline_notifications_release_all_fences(¬ifications); - } else { - if ((flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_WAIT_INIT) != 0) { - ma_resource_manager_inline_notification_wait(&initNotification); - - if (notifications.init.pNotification != NULL) { - ma_async_notification_signal(notifications.init.pNotification); - } - - /* NOTE: Do not release the init fence here. It will have been done by the job. */ - - /* Make sure we return an error if initialization failed on the async thread. */ - result = ma_resource_manager_data_buffer_result(pDataBuffer); - if (result == MA_BUSY) { - result = MA_SUCCESS; - } - } - } - - if ((flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_WAIT_INIT) != 0) { - ma_resource_manager_inline_notification_uninit(&initNotification); - } - } - - if (result != MA_SUCCESS) { - ma_resource_manager_data_buffer_node_unacquire(pResourceManager, pDataBufferNode, NULL, NULL); - goto done; - } - } -done: - if (result == MA_SUCCESS) { - if (pConfig->initialSeekPointInPCMFrames > 0) { - ma_resource_manager_data_buffer_seek_to_pcm_frame(pDataBuffer, pConfig->initialSeekPointInPCMFrames); - } - } - - ma_resource_manager_pipeline_notifications_release_all_fences(¬ifications); - - return result; -} - -MA_API ma_result ma_resource_manager_data_buffer_init_ex(ma_resource_manager* pResourceManager, const ma_resource_manager_data_source_config* pConfig, ma_resource_manager_data_buffer* pDataBuffer) -{ - return ma_resource_manager_data_buffer_init_ex_internal(pResourceManager, pConfig, 0, pDataBuffer); -} - -MA_API ma_result ma_resource_manager_data_buffer_init(ma_resource_manager* pResourceManager, const char* pFilePath, ma_uint32 flags, const ma_resource_manager_pipeline_notifications* pNotifications, ma_resource_manager_data_buffer* pDataBuffer) -{ - ma_resource_manager_data_source_config config; - - config = ma_resource_manager_data_source_config_init(); - config.pFilePath = pFilePath; - config.flags = flags; - config.pNotifications = pNotifications; - - return ma_resource_manager_data_buffer_init_ex(pResourceManager, &config, pDataBuffer); -} - -MA_API ma_result ma_resource_manager_data_buffer_init_w(ma_resource_manager* pResourceManager, const wchar_t* pFilePath, ma_uint32 flags, const ma_resource_manager_pipeline_notifications* pNotifications, ma_resource_manager_data_buffer* pDataBuffer) -{ - ma_resource_manager_data_source_config config; - - config = ma_resource_manager_data_source_config_init(); - config.pFilePathW = pFilePath; - config.flags = flags; - config.pNotifications = pNotifications; - - return ma_resource_manager_data_buffer_init_ex(pResourceManager, &config, pDataBuffer); -} - -MA_API ma_result ma_resource_manager_data_buffer_init_copy(ma_resource_manager* pResourceManager, const ma_resource_manager_data_buffer* pExistingDataBuffer, ma_resource_manager_data_buffer* pDataBuffer) -{ - ma_resource_manager_data_source_config config; - - if (pExistingDataBuffer == NULL) { - return MA_INVALID_ARGS; - } - - MA_ASSERT(pExistingDataBuffer->pNode != NULL); /* <-- If you've triggered this, you've passed in an invalid existing data buffer. */ - - config = ma_resource_manager_data_source_config_init(); - config.flags = pExistingDataBuffer->flags; - - return ma_resource_manager_data_buffer_init_ex_internal(pResourceManager, &config, pExistingDataBuffer->pNode->hashedName32, pDataBuffer); -} - -static ma_result ma_resource_manager_data_buffer_uninit_internal(ma_resource_manager_data_buffer* pDataBuffer) -{ - MA_ASSERT(pDataBuffer != NULL); - - /* The connector should be uninitialized first. */ - ma_resource_manager_data_buffer_uninit_connector(pDataBuffer->pResourceManager, pDataBuffer); - - /* With the connector uninitialized we can unacquire the node. */ - ma_resource_manager_data_buffer_node_unacquire(pDataBuffer->pResourceManager, pDataBuffer->pNode, NULL, NULL); - - /* The base data source needs to be uninitialized as well. */ - ma_data_source_uninit(&pDataBuffer->ds); - - return MA_SUCCESS; -} - -MA_API ma_result ma_resource_manager_data_buffer_uninit(ma_resource_manager_data_buffer* pDataBuffer) -{ - ma_result result; - - if (pDataBuffer == NULL) { - return MA_INVALID_ARGS; - } - - if (ma_resource_manager_data_buffer_result(pDataBuffer) == MA_SUCCESS) { - /* The data buffer can be deleted synchronously. */ - return ma_resource_manager_data_buffer_uninit_internal(pDataBuffer); - } else { - /* - The data buffer needs to be deleted asynchronously because it's still loading. With the status set to MA_UNAVAILABLE, no more pages will - be loaded and the uninitialization should happen fairly quickly. Since the caller owns the data buffer, we need to wait for this event - to get processed before returning. - */ - ma_resource_manager_inline_notification notification; - ma_job job; - - /* - We need to mark the node as unavailable so we don't try reading from it anymore, but also to - let the loading thread know that it needs to abort it's loading procedure. - */ - ma_atomic_exchange_i32(&pDataBuffer->result, MA_UNAVAILABLE); - - result = ma_resource_manager_inline_notification_init(pDataBuffer->pResourceManager, ¬ification); - if (result != MA_SUCCESS) { - return result; /* Failed to create the notification. This should rarely, if ever, happen. */ - } - - job = ma_job_init(MA_JOB_TYPE_RESOURCE_MANAGER_FREE_DATA_BUFFER); - job.order = ma_resource_manager_data_buffer_next_execution_order(pDataBuffer); - job.data.resourceManager.freeDataBuffer.pDataBuffer = pDataBuffer; - job.data.resourceManager.freeDataBuffer.pDoneNotification = ¬ification; - job.data.resourceManager.freeDataBuffer.pDoneFence = NULL; - - result = ma_resource_manager_post_job(pDataBuffer->pResourceManager, &job); - if (result != MA_SUCCESS) { - ma_resource_manager_inline_notification_uninit(¬ification); - return result; - } - - ma_resource_manager_inline_notification_wait_and_uninit(¬ification); - } - - return result; -} - -MA_API ma_result ma_resource_manager_data_buffer_read_pcm_frames(ma_resource_manager_data_buffer* pDataBuffer, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead) -{ - ma_result result = MA_SUCCESS; - ma_uint64 framesRead = 0; - ma_bool32 isDecodedBufferBusy = MA_FALSE; - - /* Safety. */ - if (pFramesRead != NULL) { - *pFramesRead = 0; - } - - if (frameCount == 0) { - return MA_INVALID_ARGS; - } - - /* - We cannot be using the data buffer after it's been uninitialized. If you trigger this assert it means you're trying to read from the data buffer after - it's been uninitialized or is in the process of uninitializing. - */ - MA_ASSERT(ma_resource_manager_data_buffer_node_result(pDataBuffer->pNode) != MA_UNAVAILABLE); - - /* If the node is not initialized we need to abort with a busy code. */ - if (ma_resource_manager_data_buffer_has_connector(pDataBuffer) == MA_FALSE) { - return MA_BUSY; /* Still loading. */ - } - - /* - If we've got a seek scheduled we'll want to do that before reading. However, for paged buffers, there's - a chance that the sound hasn't yet been decoded up to the seek point will result in the seek failing. If - this happens, we need to keep the seek scheduled and return MA_BUSY. - */ - if (pDataBuffer->seekToCursorOnNextRead) { - pDataBuffer->seekToCursorOnNextRead = MA_FALSE; - - result = ma_data_source_seek_to_pcm_frame(ma_resource_manager_data_buffer_get_connector(pDataBuffer), pDataBuffer->seekTargetInPCMFrames); - if (result != MA_SUCCESS) { - if (result == MA_BAD_SEEK && ma_resource_manager_data_buffer_node_get_data_supply_type(pDataBuffer->pNode) == ma_resource_manager_data_supply_type_decoded_paged) { - pDataBuffer->seekToCursorOnNextRead = MA_TRUE; /* Keep the seek scheduled. We just haven't loaded enough data yet to do the seek properly. */ - return MA_BUSY; - } - - return result; - } - } - - /* - For decoded buffers (not paged) we need to check beforehand how many frames we have available. We cannot - exceed this amount. We'll read as much as we can, and then return MA_BUSY. - */ - if (ma_resource_manager_data_buffer_node_get_data_supply_type(pDataBuffer->pNode) == ma_resource_manager_data_supply_type_decoded) { - ma_uint64 availableFrames; - - isDecodedBufferBusy = (ma_resource_manager_data_buffer_node_result(pDataBuffer->pNode) == MA_BUSY); - - if (ma_resource_manager_data_buffer_get_available_frames(pDataBuffer, &availableFrames) == MA_SUCCESS) { - /* Don't try reading more than the available frame count if the data buffer node is still loading. */ - if (isDecodedBufferBusy) { - if (frameCount > availableFrames) { - frameCount = availableFrames; - - /* - If there's no frames available we want to set the status to MA_AT_END. The logic below - will check if the node is busy, and if so, change it to MA_BUSY. The reason we do this - is because we don't want to call `ma_data_source_read_pcm_frames()` if the frame count - is 0 because that'll result in a situation where it's possible MA_AT_END won't get - returned. - */ - if (frameCount == 0) { - result = MA_AT_END; - } - } else { - isDecodedBufferBusy = MA_FALSE; /* We have enough frames available in the buffer to avoid a MA_BUSY status. */ - } - } else { - /* - Getting here means the buffer has been fully loaded. We can just pass the frame count straight - into ma_data_source_read_pcm_frames() below and let ma_data_source handle it. - */ - } - } - } - - /* Don't attempt to read anything if we've got no frames available. */ - if (frameCount > 0) { - result = ma_data_source_read_pcm_frames(ma_resource_manager_data_buffer_get_connector(pDataBuffer), pFramesOut, frameCount, &framesRead); - } - - /* - If we returned MA_AT_END, but the node is still loading, we don't want to return that code or else the caller will interpret the sound - as at the end and terminate decoding. - */ - if (result == MA_AT_END) { - if (ma_resource_manager_data_buffer_node_result(pDataBuffer->pNode) == MA_BUSY) { - result = MA_BUSY; - } - } - - if (isDecodedBufferBusy) { - result = MA_BUSY; - } - - if (pFramesRead != NULL) { - *pFramesRead = framesRead; - } - - if (result == MA_SUCCESS && framesRead == 0) { - result = MA_AT_END; - } - - return result; -} - -MA_API ma_result ma_resource_manager_data_buffer_seek_to_pcm_frame(ma_resource_manager_data_buffer* pDataBuffer, ma_uint64 frameIndex) -{ - ma_result result; - - /* We cannot be using the data source after it's been uninitialized. */ - MA_ASSERT(ma_resource_manager_data_buffer_node_result(pDataBuffer->pNode) != MA_UNAVAILABLE); - - /* If we haven't yet got a connector we need to abort. */ - if (ma_resource_manager_data_buffer_has_connector(pDataBuffer) == MA_FALSE) { - pDataBuffer->seekTargetInPCMFrames = frameIndex; - pDataBuffer->seekToCursorOnNextRead = MA_TRUE; - return MA_BUSY; /* Still loading. */ - } - - result = ma_data_source_seek_to_pcm_frame(ma_resource_manager_data_buffer_get_connector(pDataBuffer), frameIndex); - if (result != MA_SUCCESS) { - return result; - } - - pDataBuffer->seekTargetInPCMFrames = ~(ma_uint64)0; /* <-- For identification purposes. */ - pDataBuffer->seekToCursorOnNextRead = MA_FALSE; - - return MA_SUCCESS; -} - -MA_API ma_result ma_resource_manager_data_buffer_get_data_format(ma_resource_manager_data_buffer* pDataBuffer, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate, ma_channel* pChannelMap, size_t channelMapCap) -{ - /* We cannot be using the data source after it's been uninitialized. */ - MA_ASSERT(ma_resource_manager_data_buffer_node_result(pDataBuffer->pNode) != MA_UNAVAILABLE); - - switch (ma_resource_manager_data_buffer_node_get_data_supply_type(pDataBuffer->pNode)) - { - case ma_resource_manager_data_supply_type_encoded: - { - return ma_data_source_get_data_format(&pDataBuffer->connector.decoder, pFormat, pChannels, pSampleRate, pChannelMap, channelMapCap); - }; - - case ma_resource_manager_data_supply_type_decoded: - { - *pFormat = pDataBuffer->pNode->data.backend.decoded.format; - *pChannels = pDataBuffer->pNode->data.backend.decoded.channels; - *pSampleRate = pDataBuffer->pNode->data.backend.decoded.sampleRate; - ma_channel_map_init_standard(ma_standard_channel_map_default, pChannelMap, channelMapCap, pDataBuffer->pNode->data.backend.decoded.channels); - return MA_SUCCESS; - }; - - case ma_resource_manager_data_supply_type_decoded_paged: - { - *pFormat = pDataBuffer->pNode->data.backend.decodedPaged.data.format; - *pChannels = pDataBuffer->pNode->data.backend.decodedPaged.data.channels; - *pSampleRate = pDataBuffer->pNode->data.backend.decodedPaged.sampleRate; - ma_channel_map_init_standard(ma_standard_channel_map_default, pChannelMap, channelMapCap, pDataBuffer->pNode->data.backend.decoded.channels); - return MA_SUCCESS; - }; - - case ma_resource_manager_data_supply_type_unknown: - { - return MA_BUSY; /* Still loading. */ - }; - - default: - { - /* Unknown supply type. Should never hit this. */ - return MA_INVALID_ARGS; - } - } -} - -MA_API ma_result ma_resource_manager_data_buffer_get_cursor_in_pcm_frames(ma_resource_manager_data_buffer* pDataBuffer, ma_uint64* pCursor) -{ - /* We cannot be using the data source after it's been uninitialized. */ - MA_ASSERT(ma_resource_manager_data_buffer_node_result(pDataBuffer->pNode) != MA_UNAVAILABLE); - - if (pDataBuffer == NULL || pCursor == NULL) { - return MA_INVALID_ARGS; - } - - *pCursor = 0; - - switch (ma_resource_manager_data_buffer_node_get_data_supply_type(pDataBuffer->pNode)) - { - case ma_resource_manager_data_supply_type_encoded: - { - return ma_decoder_get_cursor_in_pcm_frames(&pDataBuffer->connector.decoder, pCursor); - }; - - case ma_resource_manager_data_supply_type_decoded: - { - return ma_audio_buffer_get_cursor_in_pcm_frames(&pDataBuffer->connector.buffer, pCursor); - }; - - case ma_resource_manager_data_supply_type_decoded_paged: - { - return ma_paged_audio_buffer_get_cursor_in_pcm_frames(&pDataBuffer->connector.pagedBuffer, pCursor); - }; - - case ma_resource_manager_data_supply_type_unknown: - { - return MA_BUSY; - }; - - default: - { - return MA_INVALID_ARGS; - } - } -} - -MA_API ma_result ma_resource_manager_data_buffer_get_length_in_pcm_frames(ma_resource_manager_data_buffer* pDataBuffer, ma_uint64* pLength) -{ - /* We cannot be using the data source after it's been uninitialized. */ - MA_ASSERT(ma_resource_manager_data_buffer_node_result(pDataBuffer->pNode) != MA_UNAVAILABLE); - - if (pDataBuffer == NULL || pLength == NULL) { - return MA_INVALID_ARGS; - } - - if (ma_resource_manager_data_buffer_node_get_data_supply_type(pDataBuffer->pNode) == ma_resource_manager_data_supply_type_unknown) { - return MA_BUSY; /* Still loading. */ - } - - return ma_data_source_get_length_in_pcm_frames(ma_resource_manager_data_buffer_get_connector(pDataBuffer), pLength); -} - -MA_API ma_result ma_resource_manager_data_buffer_result(const ma_resource_manager_data_buffer* pDataBuffer) -{ - if (pDataBuffer == NULL) { - return MA_INVALID_ARGS; - } - - return (ma_result)ma_atomic_load_i32((ma_result*)&pDataBuffer->result); /* Need a naughty const-cast here. */ -} - -MA_API ma_result ma_resource_manager_data_buffer_set_looping(ma_resource_manager_data_buffer* pDataBuffer, ma_bool32 isLooping) -{ - return ma_data_source_set_looping(pDataBuffer, isLooping); -} - -MA_API ma_bool32 ma_resource_manager_data_buffer_is_looping(const ma_resource_manager_data_buffer* pDataBuffer) -{ - return ma_data_source_is_looping(pDataBuffer); -} - -MA_API ma_result ma_resource_manager_data_buffer_get_available_frames(ma_resource_manager_data_buffer* pDataBuffer, ma_uint64* pAvailableFrames) -{ - if (pAvailableFrames == NULL) { - return MA_INVALID_ARGS; - } - - *pAvailableFrames = 0; - - if (pDataBuffer == NULL) { - return MA_INVALID_ARGS; - } - - if (ma_resource_manager_data_buffer_node_get_data_supply_type(pDataBuffer->pNode) == ma_resource_manager_data_supply_type_unknown) { - if (ma_resource_manager_data_buffer_node_result(pDataBuffer->pNode) == MA_BUSY) { - return MA_BUSY; - } else { - return MA_INVALID_OPERATION; /* No connector. */ - } - } - - switch (ma_resource_manager_data_buffer_node_get_data_supply_type(pDataBuffer->pNode)) - { - case ma_resource_manager_data_supply_type_encoded: - { - return ma_decoder_get_available_frames(&pDataBuffer->connector.decoder, pAvailableFrames); - }; - - case ma_resource_manager_data_supply_type_decoded: - { - return ma_audio_buffer_get_available_frames(&pDataBuffer->connector.buffer, pAvailableFrames); - }; - - case ma_resource_manager_data_supply_type_decoded_paged: - { - ma_uint64 cursor; - ma_paged_audio_buffer_get_cursor_in_pcm_frames(&pDataBuffer->connector.pagedBuffer, &cursor); - - if (pDataBuffer->pNode->data.backend.decodedPaged.decodedFrameCount > cursor) { - *pAvailableFrames = pDataBuffer->pNode->data.backend.decodedPaged.decodedFrameCount - cursor; - } else { - *pAvailableFrames = 0; - } - - return MA_SUCCESS; - }; - - case ma_resource_manager_data_supply_type_unknown: - default: - { - /* Unknown supply type. Should never hit this. */ - return MA_INVALID_ARGS; - } - } -} - -MA_API ma_result ma_resource_manager_register_file(ma_resource_manager* pResourceManager, const char* pFilePath, ma_uint32 flags) -{ - return ma_resource_manager_data_buffer_node_acquire(pResourceManager, pFilePath, NULL, 0, flags, NULL, NULL, NULL, NULL); -} - -MA_API ma_result ma_resource_manager_register_file_w(ma_resource_manager* pResourceManager, const wchar_t* pFilePath, ma_uint32 flags) -{ - return ma_resource_manager_data_buffer_node_acquire(pResourceManager, NULL, pFilePath, 0, flags, NULL, NULL, NULL, NULL); -} - - -static ma_result ma_resource_manager_register_data(ma_resource_manager* pResourceManager, const char* pName, const wchar_t* pNameW, ma_resource_manager_data_supply* pExistingData) -{ - return ma_resource_manager_data_buffer_node_acquire(pResourceManager, pName, pNameW, 0, 0, pExistingData, NULL, NULL, NULL); -} - -static ma_result ma_resource_manager_register_decoded_data_internal(ma_resource_manager* pResourceManager, const char* pName, const wchar_t* pNameW, const void* pData, ma_uint64 frameCount, ma_format format, ma_uint32 channels, ma_uint32 sampleRate) -{ - ma_resource_manager_data_supply data; - data.type = ma_resource_manager_data_supply_type_decoded; - data.backend.decoded.pData = pData; - data.backend.decoded.totalFrameCount = frameCount; - data.backend.decoded.format = format; - data.backend.decoded.channels = channels; - data.backend.decoded.sampleRate = sampleRate; - - return ma_resource_manager_register_data(pResourceManager, pName, pNameW, &data); -} - -MA_API ma_result ma_resource_manager_register_decoded_data(ma_resource_manager* pResourceManager, const char* pName, const void* pData, ma_uint64 frameCount, ma_format format, ma_uint32 channels, ma_uint32 sampleRate) -{ - return ma_resource_manager_register_decoded_data_internal(pResourceManager, pName, NULL, pData, frameCount, format, channels, sampleRate); -} - -MA_API ma_result ma_resource_manager_register_decoded_data_w(ma_resource_manager* pResourceManager, const wchar_t* pName, const void* pData, ma_uint64 frameCount, ma_format format, ma_uint32 channels, ma_uint32 sampleRate) -{ - return ma_resource_manager_register_decoded_data_internal(pResourceManager, NULL, pName, pData, frameCount, format, channels, sampleRate); -} - - -static ma_result ma_resource_manager_register_encoded_data_internal(ma_resource_manager* pResourceManager, const char* pName, const wchar_t* pNameW, const void* pData, size_t sizeInBytes) -{ - ma_resource_manager_data_supply data; - data.type = ma_resource_manager_data_supply_type_encoded; - data.backend.encoded.pData = pData; - data.backend.encoded.sizeInBytes = sizeInBytes; - - return ma_resource_manager_register_data(pResourceManager, pName, pNameW, &data); -} - -MA_API ma_result ma_resource_manager_register_encoded_data(ma_resource_manager* pResourceManager, const char* pName, const void* pData, size_t sizeInBytes) -{ - return ma_resource_manager_register_encoded_data_internal(pResourceManager, pName, NULL, pData, sizeInBytes); -} - -MA_API ma_result ma_resource_manager_register_encoded_data_w(ma_resource_manager* pResourceManager, const wchar_t* pName, const void* pData, size_t sizeInBytes) -{ - return ma_resource_manager_register_encoded_data_internal(pResourceManager, NULL, pName, pData, sizeInBytes); -} - - -MA_API ma_result ma_resource_manager_unregister_file(ma_resource_manager* pResourceManager, const char* pFilePath) -{ - return ma_resource_manager_unregister_data(pResourceManager, pFilePath); -} - -MA_API ma_result ma_resource_manager_unregister_file_w(ma_resource_manager* pResourceManager, const wchar_t* pFilePath) -{ - return ma_resource_manager_unregister_data_w(pResourceManager, pFilePath); -} - -MA_API ma_result ma_resource_manager_unregister_data(ma_resource_manager* pResourceManager, const char* pName) -{ - return ma_resource_manager_data_buffer_node_unacquire(pResourceManager, NULL, pName, NULL); -} - -MA_API ma_result ma_resource_manager_unregister_data_w(ma_resource_manager* pResourceManager, const wchar_t* pName) -{ - return ma_resource_manager_data_buffer_node_unacquire(pResourceManager, NULL, NULL, pName); -} - - -static ma_uint32 ma_resource_manager_data_stream_next_execution_order(ma_resource_manager_data_stream* pDataStream) -{ - MA_ASSERT(pDataStream != NULL); - return ma_atomic_fetch_add_32(&pDataStream->executionCounter, 1); -} - -static ma_bool32 ma_resource_manager_data_stream_is_decoder_at_end(const ma_resource_manager_data_stream* pDataStream) -{ - MA_ASSERT(pDataStream != NULL); - return ma_atomic_load_32((ma_bool32*)&pDataStream->isDecoderAtEnd); -} - -static ma_uint32 ma_resource_manager_data_stream_seek_counter(const ma_resource_manager_data_stream* pDataStream) -{ - MA_ASSERT(pDataStream != NULL); - return ma_atomic_load_32((ma_uint32*)&pDataStream->seekCounter); -} - - -static ma_result ma_resource_manager_data_stream_cb__read_pcm_frames(ma_data_source* pDataSource, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead) -{ - return ma_resource_manager_data_stream_read_pcm_frames((ma_resource_manager_data_stream*)pDataSource, pFramesOut, frameCount, pFramesRead); -} - -static ma_result ma_resource_manager_data_stream_cb__seek_to_pcm_frame(ma_data_source* pDataSource, ma_uint64 frameIndex) -{ - return ma_resource_manager_data_stream_seek_to_pcm_frame((ma_resource_manager_data_stream*)pDataSource, frameIndex); -} - -static ma_result ma_resource_manager_data_stream_cb__get_data_format(ma_data_source* pDataSource, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate, ma_channel* pChannelMap, size_t channelMapCap) -{ - return ma_resource_manager_data_stream_get_data_format((ma_resource_manager_data_stream*)pDataSource, pFormat, pChannels, pSampleRate, pChannelMap, channelMapCap); -} - -static ma_result ma_resource_manager_data_stream_cb__get_cursor_in_pcm_frames(ma_data_source* pDataSource, ma_uint64* pCursor) -{ - return ma_resource_manager_data_stream_get_cursor_in_pcm_frames((ma_resource_manager_data_stream*)pDataSource, pCursor); -} - -static ma_result ma_resource_manager_data_stream_cb__get_length_in_pcm_frames(ma_data_source* pDataSource, ma_uint64* pLength) -{ - return ma_resource_manager_data_stream_get_length_in_pcm_frames((ma_resource_manager_data_stream*)pDataSource, pLength); -} - -static ma_result ma_resource_manager_data_stream_cb__set_looping(ma_data_source* pDataSource, ma_bool32 isLooping) -{ - ma_resource_manager_data_stream* pDataStream = (ma_resource_manager_data_stream*)pDataSource; - MA_ASSERT(pDataStream != NULL); - - ma_atomic_exchange_32(&pDataStream->isLooping, isLooping); - - return MA_SUCCESS; -} - -static ma_data_source_vtable g_ma_resource_manager_data_stream_vtable = -{ - ma_resource_manager_data_stream_cb__read_pcm_frames, - ma_resource_manager_data_stream_cb__seek_to_pcm_frame, - ma_resource_manager_data_stream_cb__get_data_format, - ma_resource_manager_data_stream_cb__get_cursor_in_pcm_frames, - ma_resource_manager_data_stream_cb__get_length_in_pcm_frames, - ma_resource_manager_data_stream_cb__set_looping, - 0 /*MA_DATA_SOURCE_SELF_MANAGED_RANGE_AND_LOOP_POINT*/ -}; - -static void ma_resource_manager_data_stream_set_absolute_cursor(ma_resource_manager_data_stream* pDataStream, ma_uint64 absoluteCursor) -{ - /* Loop if possible. */ - if (absoluteCursor > pDataStream->totalLengthInPCMFrames && pDataStream->totalLengthInPCMFrames > 0) { - absoluteCursor = absoluteCursor % pDataStream->totalLengthInPCMFrames; - } - - ma_atomic_exchange_64(&pDataStream->absoluteCursor, absoluteCursor); -} - -MA_API ma_result ma_resource_manager_data_stream_init_ex(ma_resource_manager* pResourceManager, const ma_resource_manager_data_source_config* pConfig, ma_resource_manager_data_stream* pDataStream) -{ - ma_result result; - ma_data_source_config dataSourceConfig; - char* pFilePathCopy = NULL; - wchar_t* pFilePathWCopy = NULL; - ma_job job; - ma_bool32 waitBeforeReturning = MA_FALSE; - ma_resource_manager_inline_notification waitNotification; - ma_resource_manager_pipeline_notifications notifications; - ma_uint32 flags; - - if (pDataStream == NULL) { - if (pConfig != NULL && pConfig->pNotifications != NULL) { - ma_resource_manager_pipeline_notifications_signal_all_notifications(pConfig->pNotifications); - } - - return MA_INVALID_ARGS; - } - - MA_ZERO_OBJECT(pDataStream); - - if (pConfig == NULL) { - return MA_INVALID_ARGS; - } - - if (pConfig->pNotifications != NULL) { - notifications = *pConfig->pNotifications; /* From here on out, `notifications` should be used instead of `pNotifications`. Setting this to NULL to catch any errors at testing time. */ - } else { - MA_ZERO_OBJECT(¬ifications); - } - - dataSourceConfig = ma_data_source_config_init(); - dataSourceConfig.vtable = &g_ma_resource_manager_data_stream_vtable; - - result = ma_data_source_init(&dataSourceConfig, &pDataStream->ds); - if (result != MA_SUCCESS) { - ma_resource_manager_pipeline_notifications_signal_all_notifications(¬ifications); - return result; - } - - flags = pConfig->flags; - if (pConfig->isLooping) { - flags |= MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_LOOPING; - } - - pDataStream->pResourceManager = pResourceManager; - pDataStream->flags = pConfig->flags; - pDataStream->result = MA_BUSY; - - ma_data_source_set_range_in_pcm_frames(pDataStream, pConfig->rangeBegInPCMFrames, pConfig->rangeEndInPCMFrames); - ma_data_source_set_loop_point_in_pcm_frames(pDataStream, pConfig->loopPointBegInPCMFrames, pConfig->loopPointEndInPCMFrames); - ma_data_source_set_looping(pDataStream, (flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_LOOPING) != 0); - - if (pResourceManager == NULL || (pConfig->pFilePath == NULL && pConfig->pFilePathW == NULL)) { - ma_resource_manager_pipeline_notifications_signal_all_notifications(¬ifications); - return MA_INVALID_ARGS; - } - - /* We want all access to the VFS and the internal decoder to happen on the job thread just to keep things easier to manage for the VFS. */ - - /* We need a copy of the file path. We should probably make this more efficient, but for now we'll do a transient memory allocation. */ - if (pConfig->pFilePath != NULL) { - pFilePathCopy = ma_copy_string(pConfig->pFilePath, &pResourceManager->config.allocationCallbacks); - } else { - pFilePathWCopy = ma_copy_string_w(pConfig->pFilePathW, &pResourceManager->config.allocationCallbacks); - } - - if (pFilePathCopy == NULL && pFilePathWCopy == NULL) { - ma_resource_manager_pipeline_notifications_signal_all_notifications(¬ifications); - return MA_OUT_OF_MEMORY; - } - - /* - We need to check for the presence of MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_ASYNC. If it's not set, we need to wait before returning. Otherwise we - can return immediately. Likewise, we'll also check for MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_WAIT_INIT and do the same. - */ - if ((pConfig->flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_ASYNC) == 0 || (pConfig->flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_WAIT_INIT) != 0) { - waitBeforeReturning = MA_TRUE; - ma_resource_manager_inline_notification_init(pResourceManager, &waitNotification); - } - - ma_resource_manager_pipeline_notifications_acquire_all_fences(¬ifications); - - /* Set the absolute cursor to our initial seek position so retrieval of the cursor returns a good value. */ - ma_resource_manager_data_stream_set_absolute_cursor(pDataStream, pConfig->initialSeekPointInPCMFrames); - - /* We now have everything we need to post the job. This is the last thing we need to do from here. The rest will be done by the job thread. */ - job = ma_job_init(MA_JOB_TYPE_RESOURCE_MANAGER_LOAD_DATA_STREAM); - job.order = ma_resource_manager_data_stream_next_execution_order(pDataStream); - job.data.resourceManager.loadDataStream.pDataStream = pDataStream; - job.data.resourceManager.loadDataStream.pFilePath = pFilePathCopy; - job.data.resourceManager.loadDataStream.pFilePathW = pFilePathWCopy; - job.data.resourceManager.loadDataStream.initialSeekPoint = pConfig->initialSeekPointInPCMFrames; - job.data.resourceManager.loadDataStream.pInitNotification = (waitBeforeReturning == MA_TRUE) ? &waitNotification : notifications.init.pNotification; - job.data.resourceManager.loadDataStream.pInitFence = notifications.init.pFence; - result = ma_resource_manager_post_job(pResourceManager, &job); - if (result != MA_SUCCESS) { - ma_resource_manager_pipeline_notifications_signal_all_notifications(¬ifications); - ma_resource_manager_pipeline_notifications_release_all_fences(¬ifications); - - if (waitBeforeReturning) { - ma_resource_manager_inline_notification_uninit(&waitNotification); - } - - ma_free(pFilePathCopy, &pResourceManager->config.allocationCallbacks); - ma_free(pFilePathWCopy, &pResourceManager->config.allocationCallbacks); - return result; - } - - /* Wait if needed. */ - if (waitBeforeReturning) { - ma_resource_manager_inline_notification_wait_and_uninit(&waitNotification); - - if (notifications.init.pNotification != NULL) { - ma_async_notification_signal(notifications.init.pNotification); - } - - /* - If there was an error during initialization make sure we return that result here. We don't want to do this - if we're not waiting because it will most likely be in a busy state. - */ - if (pDataStream->result != MA_SUCCESS) { - return pDataStream->result; - } - - /* NOTE: Do not release pInitFence here. That will be done by the job. */ - } - - return MA_SUCCESS; -} - -MA_API ma_result ma_resource_manager_data_stream_init(ma_resource_manager* pResourceManager, const char* pFilePath, ma_uint32 flags, const ma_resource_manager_pipeline_notifications* pNotifications, ma_resource_manager_data_stream* pDataStream) -{ - ma_resource_manager_data_source_config config; - - config = ma_resource_manager_data_source_config_init(); - config.pFilePath = pFilePath; - config.flags = flags; - config.pNotifications = pNotifications; - - return ma_resource_manager_data_stream_init_ex(pResourceManager, &config, pDataStream); -} - -MA_API ma_result ma_resource_manager_data_stream_init_w(ma_resource_manager* pResourceManager, const wchar_t* pFilePath, ma_uint32 flags, const ma_resource_manager_pipeline_notifications* pNotifications, ma_resource_manager_data_stream* pDataStream) -{ - ma_resource_manager_data_source_config config; - - config = ma_resource_manager_data_source_config_init(); - config.pFilePathW = pFilePath; - config.flags = flags; - config.pNotifications = pNotifications; - - return ma_resource_manager_data_stream_init_ex(pResourceManager, &config, pDataStream); -} - -MA_API ma_result ma_resource_manager_data_stream_uninit(ma_resource_manager_data_stream* pDataStream) -{ - ma_resource_manager_inline_notification freeEvent; - ma_job job; - - if (pDataStream == NULL) { - return MA_INVALID_ARGS; - } - - /* The first thing to do is set the result to unavailable. This will prevent future page decoding. */ - ma_atomic_exchange_i32(&pDataStream->result, MA_UNAVAILABLE); - - /* - We need to post a job to ensure we're not in the middle or decoding or anything. Because the object is owned by the caller, we'll need - to wait for it to complete before returning which means we need an event. - */ - ma_resource_manager_inline_notification_init(pDataStream->pResourceManager, &freeEvent); - - job = ma_job_init(MA_JOB_TYPE_RESOURCE_MANAGER_FREE_DATA_STREAM); - job.order = ma_resource_manager_data_stream_next_execution_order(pDataStream); - job.data.resourceManager.freeDataStream.pDataStream = pDataStream; - job.data.resourceManager.freeDataStream.pDoneNotification = &freeEvent; - job.data.resourceManager.freeDataStream.pDoneFence = NULL; - ma_resource_manager_post_job(pDataStream->pResourceManager, &job); - - /* We need to wait for the job to finish processing before we return. */ - ma_resource_manager_inline_notification_wait_and_uninit(&freeEvent); - - return MA_SUCCESS; -} - - -static ma_uint32 ma_resource_manager_data_stream_get_page_size_in_frames(ma_resource_manager_data_stream* pDataStream) -{ - MA_ASSERT(pDataStream != NULL); - MA_ASSERT(pDataStream->isDecoderInitialized == MA_TRUE); - - return MA_RESOURCE_MANAGER_PAGE_SIZE_IN_MILLISECONDS * (pDataStream->decoder.outputSampleRate/1000); -} - -static void* ma_resource_manager_data_stream_get_page_data_pointer(ma_resource_manager_data_stream* pDataStream, ma_uint32 pageIndex, ma_uint32 relativeCursor) -{ - MA_ASSERT(pDataStream != NULL); - MA_ASSERT(pDataStream->isDecoderInitialized == MA_TRUE); - MA_ASSERT(pageIndex == 0 || pageIndex == 1); - - return ma_offset_ptr(pDataStream->pPageData, ((ma_resource_manager_data_stream_get_page_size_in_frames(pDataStream) * pageIndex) + relativeCursor) * ma_get_bytes_per_frame(pDataStream->decoder.outputFormat, pDataStream->decoder.outputChannels)); -} - -static void ma_resource_manager_data_stream_fill_page(ma_resource_manager_data_stream* pDataStream, ma_uint32 pageIndex) -{ - ma_result result = MA_SUCCESS; - ma_uint64 pageSizeInFrames; - ma_uint64 totalFramesReadForThisPage = 0; - void* pPageData = ma_resource_manager_data_stream_get_page_data_pointer(pDataStream, pageIndex, 0); - - pageSizeInFrames = ma_resource_manager_data_stream_get_page_size_in_frames(pDataStream); - - /* The decoder needs to inherit the stream's looping and range state. */ - { - ma_uint64 rangeBeg; - ma_uint64 rangeEnd; - ma_uint64 loopPointBeg; - ma_uint64 loopPointEnd; - - ma_data_source_set_looping(&pDataStream->decoder, ma_resource_manager_data_stream_is_looping(pDataStream)); - - ma_data_source_get_range_in_pcm_frames(pDataStream, &rangeBeg, &rangeEnd); - ma_data_source_set_range_in_pcm_frames(&pDataStream->decoder, rangeBeg, rangeEnd); - - ma_data_source_get_loop_point_in_pcm_frames(pDataStream, &loopPointBeg, &loopPointEnd); - ma_data_source_set_loop_point_in_pcm_frames(&pDataStream->decoder, loopPointBeg, loopPointEnd); - } - - /* Just read straight from the decoder. It will deal with ranges and looping for us. */ - result = ma_data_source_read_pcm_frames(&pDataStream->decoder, pPageData, pageSizeInFrames, &totalFramesReadForThisPage); - if (result == MA_AT_END || totalFramesReadForThisPage < pageSizeInFrames) { - ma_atomic_exchange_32(&pDataStream->isDecoderAtEnd, MA_TRUE); - } - - ma_atomic_exchange_32(&pDataStream->pageFrameCount[pageIndex], (ma_uint32)totalFramesReadForThisPage); - ma_atomic_exchange_32(&pDataStream->isPageValid[pageIndex], MA_TRUE); -} - -static void ma_resource_manager_data_stream_fill_pages(ma_resource_manager_data_stream* pDataStream) -{ - ma_uint32 iPage; - - MA_ASSERT(pDataStream != NULL); - - for (iPage = 0; iPage < 2; iPage += 1) { - ma_resource_manager_data_stream_fill_page(pDataStream, iPage); - } -} - - -static ma_result ma_resource_manager_data_stream_map(ma_resource_manager_data_stream* pDataStream, void** ppFramesOut, ma_uint64* pFrameCount) -{ - ma_uint64 framesAvailable; - ma_uint64 frameCount = 0; - - /* We cannot be using the data source after it's been uninitialized. */ - MA_ASSERT(ma_resource_manager_data_stream_result(pDataStream) != MA_UNAVAILABLE); - - if (pFrameCount != NULL) { - frameCount = *pFrameCount; - *pFrameCount = 0; - } - if (ppFramesOut != NULL) { - *ppFramesOut = NULL; - } - - if (pDataStream == NULL || ppFramesOut == NULL || pFrameCount == NULL) { - return MA_INVALID_ARGS; - } - - if (ma_resource_manager_data_stream_result(pDataStream) != MA_SUCCESS) { - return MA_INVALID_OPERATION; - } - - /* Don't attempt to read while we're in the middle of seeking. Tell the caller that we're busy. */ - if (ma_resource_manager_data_stream_seek_counter(pDataStream) > 0) { - return MA_BUSY; - } - - /* If the page we're on is invalid it means we've caught up to the job thread. */ - if (ma_atomic_load_32(&pDataStream->isPageValid[pDataStream->currentPageIndex]) == MA_FALSE) { - framesAvailable = 0; - } else { - /* - The page we're on is valid so we must have some frames available. We need to make sure that we don't overflow into the next page, even if it's valid. The reason is - that the unmap process will only post an update for one page at a time. Keeping mapping tied to page boundaries makes this simpler. - */ - ma_uint32 currentPageFrameCount = ma_atomic_load_32(&pDataStream->pageFrameCount[pDataStream->currentPageIndex]); - MA_ASSERT(currentPageFrameCount >= pDataStream->relativeCursor); - - framesAvailable = currentPageFrameCount - pDataStream->relativeCursor; - } - - /* If there's no frames available and the result is set to MA_AT_END we need to return MA_AT_END. */ - if (framesAvailable == 0) { - if (ma_resource_manager_data_stream_is_decoder_at_end(pDataStream)) { - return MA_AT_END; - } else { - return MA_BUSY; /* There are no frames available, but we're not marked as EOF so we might have caught up to the job thread. Need to return MA_BUSY and wait for more data. */ - } - } - - MA_ASSERT(framesAvailable > 0); - - if (frameCount > framesAvailable) { - frameCount = framesAvailable; - } - - *ppFramesOut = ma_resource_manager_data_stream_get_page_data_pointer(pDataStream, pDataStream->currentPageIndex, pDataStream->relativeCursor); - *pFrameCount = frameCount; - - return MA_SUCCESS; -} - -static ma_result ma_resource_manager_data_stream_unmap(ma_resource_manager_data_stream* pDataStream, ma_uint64 frameCount) -{ - ma_uint32 newRelativeCursor; - ma_uint32 pageSizeInFrames; - ma_job job; - - /* We cannot be using the data source after it's been uninitialized. */ - MA_ASSERT(ma_resource_manager_data_stream_result(pDataStream) != MA_UNAVAILABLE); - - if (pDataStream == NULL) { - return MA_INVALID_ARGS; - } - - if (ma_resource_manager_data_stream_result(pDataStream) != MA_SUCCESS) { - return MA_INVALID_OPERATION; - } - - /* The frame count should always fit inside a 32-bit integer. */ - if (frameCount > 0xFFFFFFFF) { - return MA_INVALID_ARGS; - } - - pageSizeInFrames = ma_resource_manager_data_stream_get_page_size_in_frames(pDataStream); - - /* The absolute cursor needs to be updated for ma_resource_manager_data_stream_get_cursor_in_pcm_frames(). */ - ma_resource_manager_data_stream_set_absolute_cursor(pDataStream, ma_atomic_load_64(&pDataStream->absoluteCursor) + frameCount); - - /* Here is where we need to check if we need to load a new page, and if so, post a job to load it. */ - newRelativeCursor = pDataStream->relativeCursor + (ma_uint32)frameCount; - - /* If the new cursor has flowed over to the next page we need to mark the old one as invalid and post an event for it. */ - if (newRelativeCursor >= pageSizeInFrames) { - newRelativeCursor -= pageSizeInFrames; - - /* Here is where we post the job start decoding. */ - job = ma_job_init(MA_JOB_TYPE_RESOURCE_MANAGER_PAGE_DATA_STREAM); - job.order = ma_resource_manager_data_stream_next_execution_order(pDataStream); - job.data.resourceManager.pageDataStream.pDataStream = pDataStream; - job.data.resourceManager.pageDataStream.pageIndex = pDataStream->currentPageIndex; - - /* The page needs to be marked as invalid so that the public API doesn't try reading from it. */ - ma_atomic_exchange_32(&pDataStream->isPageValid[pDataStream->currentPageIndex], MA_FALSE); - - /* Before posting the job we need to make sure we set some state. */ - pDataStream->relativeCursor = newRelativeCursor; - pDataStream->currentPageIndex = (pDataStream->currentPageIndex + 1) & 0x01; - return ma_resource_manager_post_job(pDataStream->pResourceManager, &job); - } else { - /* We haven't moved into a new page so we can just move the cursor forward. */ - pDataStream->relativeCursor = newRelativeCursor; - return MA_SUCCESS; - } -} - - -MA_API ma_result ma_resource_manager_data_stream_read_pcm_frames(ma_resource_manager_data_stream* pDataStream, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead) -{ - ma_result result = MA_SUCCESS; - ma_uint64 totalFramesProcessed; - ma_format format; - ma_uint32 channels; - - /* Safety. */ - if (pFramesRead != NULL) { - *pFramesRead = 0; - } - - if (frameCount == 0) { - return MA_INVALID_ARGS; - } - - /* We cannot be using the data source after it's been uninitialized. */ - MA_ASSERT(ma_resource_manager_data_stream_result(pDataStream) != MA_UNAVAILABLE); - - if (pDataStream == NULL) { - return MA_INVALID_ARGS; - } - - if (ma_resource_manager_data_stream_result(pDataStream) != MA_SUCCESS) { - return MA_INVALID_OPERATION; - } - - /* Don't attempt to read while we're in the middle of seeking. Tell the caller that we're busy. */ - if (ma_resource_manager_data_stream_seek_counter(pDataStream) > 0) { - return MA_BUSY; - } - - ma_resource_manager_data_stream_get_data_format(pDataStream, &format, &channels, NULL, NULL, 0); - - /* Reading is implemented in terms of map/unmap. We need to run this in a loop because mapping is clamped against page boundaries. */ - totalFramesProcessed = 0; - while (totalFramesProcessed < frameCount) { - void* pMappedFrames; - ma_uint64 mappedFrameCount; - - mappedFrameCount = frameCount - totalFramesProcessed; - result = ma_resource_manager_data_stream_map(pDataStream, &pMappedFrames, &mappedFrameCount); - if (result != MA_SUCCESS) { - break; - } - - /* Copy the mapped data to the output buffer if we have one. It's allowed for pFramesOut to be NULL in which case a relative forward seek is performed. */ - if (pFramesOut != NULL) { - ma_copy_pcm_frames(ma_offset_pcm_frames_ptr(pFramesOut, totalFramesProcessed, format, channels), pMappedFrames, mappedFrameCount, format, channels); - } - - totalFramesProcessed += mappedFrameCount; - - result = ma_resource_manager_data_stream_unmap(pDataStream, mappedFrameCount); - if (result != MA_SUCCESS) { - break; /* This is really bad - will only get an error here if we failed to post a job to the queue for loading the next page. */ - } - } - - if (pFramesRead != NULL) { - *pFramesRead = totalFramesProcessed; - } - - if (result == MA_SUCCESS && totalFramesProcessed == 0) { - result = MA_AT_END; - } - - return result; -} - -MA_API ma_result ma_resource_manager_data_stream_seek_to_pcm_frame(ma_resource_manager_data_stream* pDataStream, ma_uint64 frameIndex) -{ - ma_job job; - ma_result streamResult; - - streamResult = ma_resource_manager_data_stream_result(pDataStream); - - /* We cannot be using the data source after it's been uninitialized. */ - MA_ASSERT(streamResult != MA_UNAVAILABLE); - - if (pDataStream == NULL) { - return MA_INVALID_ARGS; - } - - if (streamResult != MA_SUCCESS && streamResult != MA_BUSY) { - return MA_INVALID_OPERATION; - } - - /* If we're not already seeking and we're sitting on the same frame, just make this a no-op. */ - if (ma_atomic_load_32(&pDataStream->seekCounter) == 0) { - if (ma_atomic_load_64(&pDataStream->absoluteCursor) == frameIndex) { - return MA_SUCCESS; - } - } - - - /* Increment the seek counter first to indicate to read_paged_pcm_frames() and map_paged_pcm_frames() that we are in the middle of a seek and MA_BUSY should be returned. */ - ma_atomic_fetch_add_32(&pDataStream->seekCounter, 1); - - /* Update the absolute cursor so that ma_resource_manager_data_stream_get_cursor_in_pcm_frames() returns the new position. */ - ma_resource_manager_data_stream_set_absolute_cursor(pDataStream, frameIndex); - - /* - We need to clear our currently loaded pages so that the stream starts playback from the new seek point as soon as possible. These are for the purpose of the public - API and will be ignored by the seek job. The seek job will operate on the assumption that both pages have been marked as invalid and the cursor is at the start of - the first page. - */ - pDataStream->relativeCursor = 0; - pDataStream->currentPageIndex = 0; - ma_atomic_exchange_32(&pDataStream->isPageValid[0], MA_FALSE); - ma_atomic_exchange_32(&pDataStream->isPageValid[1], MA_FALSE); - - /* Make sure the data stream is not marked as at the end or else if we seek in response to hitting the end, we won't be able to read any more data. */ - ma_atomic_exchange_32(&pDataStream->isDecoderAtEnd, MA_FALSE); - - /* - The public API is not allowed to touch the internal decoder so we need to use a job to perform the seek. When seeking, the job thread will assume both pages - are invalid and any content contained within them will be discarded and replaced with newly decoded data. - */ - job = ma_job_init(MA_JOB_TYPE_RESOURCE_MANAGER_SEEK_DATA_STREAM); - job.order = ma_resource_manager_data_stream_next_execution_order(pDataStream); - job.data.resourceManager.seekDataStream.pDataStream = pDataStream; - job.data.resourceManager.seekDataStream.frameIndex = frameIndex; - return ma_resource_manager_post_job(pDataStream->pResourceManager, &job); -} - -MA_API ma_result ma_resource_manager_data_stream_get_data_format(ma_resource_manager_data_stream* pDataStream, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate, ma_channel* pChannelMap, size_t channelMapCap) -{ - /* We cannot be using the data source after it's been uninitialized. */ - MA_ASSERT(ma_resource_manager_data_stream_result(pDataStream) != MA_UNAVAILABLE); - - if (pFormat != NULL) { - *pFormat = ma_format_unknown; - } - - if (pChannels != NULL) { - *pChannels = 0; - } - - if (pSampleRate != NULL) { - *pSampleRate = 0; - } - - if (pChannelMap != NULL) { - MA_ZERO_MEMORY(pChannelMap, sizeof(*pChannelMap) * channelMapCap); - } - - if (pDataStream == NULL) { - return MA_INVALID_ARGS; - } - - if (ma_resource_manager_data_stream_result(pDataStream) != MA_SUCCESS) { - return MA_INVALID_OPERATION; - } - - /* - We're being a little bit naughty here and accessing the internal decoder from the public API. The output data format is constant, and we've defined this function - such that the application is responsible for ensuring it's not called while uninitializing so it should be safe. - */ - return ma_data_source_get_data_format(&pDataStream->decoder, pFormat, pChannels, pSampleRate, pChannelMap, channelMapCap); -} - -MA_API ma_result ma_resource_manager_data_stream_get_cursor_in_pcm_frames(ma_resource_manager_data_stream* pDataStream, ma_uint64* pCursor) -{ - ma_result result; - - if (pCursor == NULL) { - return MA_INVALID_ARGS; - } - - *pCursor = 0; - - /* We cannot be using the data source after it's been uninitialized. */ - MA_ASSERT(ma_resource_manager_data_stream_result(pDataStream) != MA_UNAVAILABLE); - - if (pDataStream == NULL) { - return MA_INVALID_ARGS; - } - - /* - If the stream is in an erroneous state we need to return an invalid operation. We can allow - this to be called when the data stream is in a busy state because the caller may have asked - for an initial seek position and it's convenient to return that as the cursor position. - */ - result = ma_resource_manager_data_stream_result(pDataStream); - if (result != MA_SUCCESS && result != MA_BUSY) { - return MA_INVALID_OPERATION; - } - - *pCursor = ma_atomic_load_64(&pDataStream->absoluteCursor); - - return MA_SUCCESS; -} - -MA_API ma_result ma_resource_manager_data_stream_get_length_in_pcm_frames(ma_resource_manager_data_stream* pDataStream, ma_uint64* pLength) -{ - ma_result streamResult; - - if (pLength == NULL) { - return MA_INVALID_ARGS; - } - - *pLength = 0; - - streamResult = ma_resource_manager_data_stream_result(pDataStream); - - /* We cannot be using the data source after it's been uninitialized. */ - MA_ASSERT(streamResult != MA_UNAVAILABLE); - - if (pDataStream == NULL) { - return MA_INVALID_ARGS; - } - - if (streamResult != MA_SUCCESS) { - return streamResult; - } - - /* - We most definitely do not want to be calling ma_decoder_get_length_in_pcm_frames() directly. Instead we want to use a cached value that we - calculated when we initialized it on the job thread. - */ - *pLength = pDataStream->totalLengthInPCMFrames; - if (*pLength == 0) { - return MA_NOT_IMPLEMENTED; /* Some decoders may not have a known length. */ - } - - return MA_SUCCESS; -} - -MA_API ma_result ma_resource_manager_data_stream_result(const ma_resource_manager_data_stream* pDataStream) -{ - if (pDataStream == NULL) { - return MA_INVALID_ARGS; - } - - return (ma_result)ma_atomic_load_i32(&pDataStream->result); -} - -MA_API ma_result ma_resource_manager_data_stream_set_looping(ma_resource_manager_data_stream* pDataStream, ma_bool32 isLooping) -{ - return ma_data_source_set_looping(pDataStream, isLooping); -} - -MA_API ma_bool32 ma_resource_manager_data_stream_is_looping(const ma_resource_manager_data_stream* pDataStream) -{ - if (pDataStream == NULL) { - return MA_FALSE; - } - - return ma_atomic_load_32((ma_bool32*)&pDataStream->isLooping); /* Naughty const-cast. Value won't change from here in practice (maybe from another thread). */ -} - -MA_API ma_result ma_resource_manager_data_stream_get_available_frames(ma_resource_manager_data_stream* pDataStream, ma_uint64* pAvailableFrames) -{ - ma_uint32 pageIndex0; - ma_uint32 pageIndex1; - ma_uint32 relativeCursor; - ma_uint64 availableFrames; - - if (pAvailableFrames == NULL) { - return MA_INVALID_ARGS; - } - - *pAvailableFrames = 0; - - if (pDataStream == NULL) { - return MA_INVALID_ARGS; - } - - pageIndex0 = pDataStream->currentPageIndex; - pageIndex1 = (pDataStream->currentPageIndex + 1) & 0x01; - relativeCursor = pDataStream->relativeCursor; - - availableFrames = 0; - if (ma_atomic_load_32(&pDataStream->isPageValid[pageIndex0])) { - availableFrames += ma_atomic_load_32(&pDataStream->pageFrameCount[pageIndex0]) - relativeCursor; - if (ma_atomic_load_32(&pDataStream->isPageValid[pageIndex1])) { - availableFrames += ma_atomic_load_32(&pDataStream->pageFrameCount[pageIndex1]); - } - } - - *pAvailableFrames = availableFrames; - return MA_SUCCESS; -} - - -static ma_result ma_resource_manager_data_source_preinit(ma_resource_manager* pResourceManager, const ma_resource_manager_data_source_config* pConfig, ma_resource_manager_data_source* pDataSource) -{ - if (pDataSource == NULL) { - return MA_INVALID_ARGS; - } - - MA_ZERO_OBJECT(pDataSource); - - if (pConfig == NULL) { - return MA_INVALID_ARGS; - } - - if (pResourceManager == NULL) { - return MA_INVALID_ARGS; - } - - pDataSource->flags = pConfig->flags; - if (pConfig->isLooping) { - pDataSource->flags |= MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_LOOPING; - } - - return MA_SUCCESS; -} - -MA_API ma_result ma_resource_manager_data_source_init_ex(ma_resource_manager* pResourceManager, const ma_resource_manager_data_source_config* pConfig, ma_resource_manager_data_source* pDataSource) -{ - ma_result result; - - result = ma_resource_manager_data_source_preinit(pResourceManager, pConfig, pDataSource); - if (result != MA_SUCCESS) { - return result; - } - - /* The data source itself is just a data stream or a data buffer. */ - if ((pConfig->flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_STREAM) != 0) { - return ma_resource_manager_data_stream_init_ex(pResourceManager, pConfig, &pDataSource->backend.stream); - } else { - return ma_resource_manager_data_buffer_init_ex(pResourceManager, pConfig, &pDataSource->backend.buffer); - } -} - -MA_API ma_result ma_resource_manager_data_source_init(ma_resource_manager* pResourceManager, const char* pName, ma_uint32 flags, const ma_resource_manager_pipeline_notifications* pNotifications, ma_resource_manager_data_source* pDataSource) -{ - ma_resource_manager_data_source_config config; - - config = ma_resource_manager_data_source_config_init(); - config.pFilePath = pName; - config.flags = flags; - config.pNotifications = pNotifications; - - return ma_resource_manager_data_source_init_ex(pResourceManager, &config, pDataSource); -} - -MA_API ma_result ma_resource_manager_data_source_init_w(ma_resource_manager* pResourceManager, const wchar_t* pName, ma_uint32 flags, const ma_resource_manager_pipeline_notifications* pNotifications, ma_resource_manager_data_source* pDataSource) -{ - ma_resource_manager_data_source_config config; - - config = ma_resource_manager_data_source_config_init(); - config.pFilePathW = pName; - config.flags = flags; - config.pNotifications = pNotifications; - - return ma_resource_manager_data_source_init_ex(pResourceManager, &config, pDataSource); -} - -MA_API ma_result ma_resource_manager_data_source_init_copy(ma_resource_manager* pResourceManager, const ma_resource_manager_data_source* pExistingDataSource, ma_resource_manager_data_source* pDataSource) -{ - ma_result result; - ma_resource_manager_data_source_config config; - - if (pExistingDataSource == NULL) { - return MA_INVALID_ARGS; - } - - config = ma_resource_manager_data_source_config_init(); - config.flags = pExistingDataSource->flags; - - result = ma_resource_manager_data_source_preinit(pResourceManager, &config, pDataSource); - if (result != MA_SUCCESS) { - return result; - } - - /* Copying can only be done from data buffers. Streams cannot be copied. */ - if ((pExistingDataSource->flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_STREAM) != 0) { - return MA_INVALID_OPERATION; - } - - return ma_resource_manager_data_buffer_init_copy(pResourceManager, &pExistingDataSource->backend.buffer, &pDataSource->backend.buffer); -} - -MA_API ma_result ma_resource_manager_data_source_uninit(ma_resource_manager_data_source* pDataSource) -{ - if (pDataSource == NULL) { - return MA_INVALID_ARGS; - } - - /* All we need to is uninitialize the underlying data buffer or data stream. */ - if ((pDataSource->flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_STREAM) != 0) { - return ma_resource_manager_data_stream_uninit(&pDataSource->backend.stream); - } else { - return ma_resource_manager_data_buffer_uninit(&pDataSource->backend.buffer); - } -} - -MA_API ma_result ma_resource_manager_data_source_read_pcm_frames(ma_resource_manager_data_source* pDataSource, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead) -{ - /* Safety. */ - if (pFramesRead != NULL) { - *pFramesRead = 0; - } - - if (pDataSource == NULL) { - return MA_INVALID_ARGS; - } - - if ((pDataSource->flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_STREAM) != 0) { - return ma_resource_manager_data_stream_read_pcm_frames(&pDataSource->backend.stream, pFramesOut, frameCount, pFramesRead); - } else { - return ma_resource_manager_data_buffer_read_pcm_frames(&pDataSource->backend.buffer, pFramesOut, frameCount, pFramesRead); - } -} - -MA_API ma_result ma_resource_manager_data_source_seek_to_pcm_frame(ma_resource_manager_data_source* pDataSource, ma_uint64 frameIndex) -{ - if (pDataSource == NULL) { - return MA_INVALID_ARGS; - } - - if ((pDataSource->flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_STREAM) != 0) { - return ma_resource_manager_data_stream_seek_to_pcm_frame(&pDataSource->backend.stream, frameIndex); - } else { - return ma_resource_manager_data_buffer_seek_to_pcm_frame(&pDataSource->backend.buffer, frameIndex); - } -} - -MA_API ma_result ma_resource_manager_data_source_map(ma_resource_manager_data_source* pDataSource, void** ppFramesOut, ma_uint64* pFrameCount) -{ - if (pDataSource == NULL) { - return MA_INVALID_ARGS; - } - - if ((pDataSource->flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_STREAM) != 0) { - return ma_resource_manager_data_stream_map(&pDataSource->backend.stream, ppFramesOut, pFrameCount); - } else { - return MA_NOT_IMPLEMENTED; /* Mapping not supported with data buffers. */ - } -} - -MA_API ma_result ma_resource_manager_data_source_unmap(ma_resource_manager_data_source* pDataSource, ma_uint64 frameCount) -{ - if (pDataSource == NULL) { - return MA_INVALID_ARGS; - } - - if ((pDataSource->flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_STREAM) != 0) { - return ma_resource_manager_data_stream_unmap(&pDataSource->backend.stream, frameCount); - } else { - return MA_NOT_IMPLEMENTED; /* Mapping not supported with data buffers. */ - } -} - -MA_API ma_result ma_resource_manager_data_source_get_data_format(ma_resource_manager_data_source* pDataSource, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate, ma_channel* pChannelMap, size_t channelMapCap) -{ - if (pDataSource == NULL) { - return MA_INVALID_ARGS; - } - - if ((pDataSource->flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_STREAM) != 0) { - return ma_resource_manager_data_stream_get_data_format(&pDataSource->backend.stream, pFormat, pChannels, pSampleRate, pChannelMap, channelMapCap); - } else { - return ma_resource_manager_data_buffer_get_data_format(&pDataSource->backend.buffer, pFormat, pChannels, pSampleRate, pChannelMap, channelMapCap); - } -} - -MA_API ma_result ma_resource_manager_data_source_get_cursor_in_pcm_frames(ma_resource_manager_data_source* pDataSource, ma_uint64* pCursor) -{ - if (pDataSource == NULL) { - return MA_INVALID_ARGS; - } - - if ((pDataSource->flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_STREAM) != 0) { - return ma_resource_manager_data_stream_get_cursor_in_pcm_frames(&pDataSource->backend.stream, pCursor); - } else { - return ma_resource_manager_data_buffer_get_cursor_in_pcm_frames(&pDataSource->backend.buffer, pCursor); - } -} - -MA_API ma_result ma_resource_manager_data_source_get_length_in_pcm_frames(ma_resource_manager_data_source* pDataSource, ma_uint64* pLength) -{ - if (pDataSource == NULL) { - return MA_INVALID_ARGS; - } - - if ((pDataSource->flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_STREAM) != 0) { - return ma_resource_manager_data_stream_get_length_in_pcm_frames(&pDataSource->backend.stream, pLength); - } else { - return ma_resource_manager_data_buffer_get_length_in_pcm_frames(&pDataSource->backend.buffer, pLength); - } -} - -MA_API ma_result ma_resource_manager_data_source_result(const ma_resource_manager_data_source* pDataSource) -{ - if (pDataSource == NULL) { - return MA_INVALID_ARGS; - } - - if ((pDataSource->flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_STREAM) != 0) { - return ma_resource_manager_data_stream_result(&pDataSource->backend.stream); - } else { - return ma_resource_manager_data_buffer_result(&pDataSource->backend.buffer); - } -} - -MA_API ma_result ma_resource_manager_data_source_set_looping(ma_resource_manager_data_source* pDataSource, ma_bool32 isLooping) -{ - if (pDataSource == NULL) { - return MA_INVALID_ARGS; - } - - if ((pDataSource->flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_STREAM) != 0) { - return ma_resource_manager_data_stream_set_looping(&pDataSource->backend.stream, isLooping); - } else { - return ma_resource_manager_data_buffer_set_looping(&pDataSource->backend.buffer, isLooping); - } -} - -MA_API ma_bool32 ma_resource_manager_data_source_is_looping(const ma_resource_manager_data_source* pDataSource) -{ - if (pDataSource == NULL) { - return MA_FALSE; - } - - if ((pDataSource->flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_STREAM) != 0) { - return ma_resource_manager_data_stream_is_looping(&pDataSource->backend.stream); - } else { - return ma_resource_manager_data_buffer_is_looping(&pDataSource->backend.buffer); - } -} - -MA_API ma_result ma_resource_manager_data_source_get_available_frames(ma_resource_manager_data_source* pDataSource, ma_uint64* pAvailableFrames) -{ - if (pAvailableFrames == NULL) { - return MA_INVALID_ARGS; - } - - *pAvailableFrames = 0; - - if (pDataSource == NULL) { - return MA_INVALID_ARGS; - } - - if ((pDataSource->flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_STREAM) != 0) { - return ma_resource_manager_data_stream_get_available_frames(&pDataSource->backend.stream, pAvailableFrames); - } else { - return ma_resource_manager_data_buffer_get_available_frames(&pDataSource->backend.buffer, pAvailableFrames); - } -} - - -MA_API ma_result ma_resource_manager_post_job(ma_resource_manager* pResourceManager, const ma_job* pJob) -{ - if (pResourceManager == NULL) { - return MA_INVALID_ARGS; - } - - return ma_job_queue_post(&pResourceManager->jobQueue, pJob); -} - -MA_API ma_result ma_resource_manager_post_job_quit(ma_resource_manager* pResourceManager) -{ - ma_job job = ma_job_init(MA_JOB_TYPE_QUIT); - return ma_resource_manager_post_job(pResourceManager, &job); -} - -MA_API ma_result ma_resource_manager_next_job(ma_resource_manager* pResourceManager, ma_job* pJob) -{ - if (pResourceManager == NULL) { - return MA_INVALID_ARGS; - } - - return ma_job_queue_next(&pResourceManager->jobQueue, pJob); -} - - -static ma_result ma_job_process__resource_manager__load_data_buffer_node(ma_job* pJob) -{ - ma_result result = MA_SUCCESS; - ma_resource_manager* pResourceManager; - ma_resource_manager_data_buffer_node* pDataBufferNode; - - MA_ASSERT(pJob != NULL); - - pResourceManager = (ma_resource_manager*)pJob->data.resourceManager.loadDataBufferNode.pResourceManager; - MA_ASSERT(pResourceManager != NULL); - - pDataBufferNode = (ma_resource_manager_data_buffer_node*)pJob->data.resourceManager.loadDataBufferNode.pDataBufferNode; - MA_ASSERT(pDataBufferNode != NULL); - MA_ASSERT(pDataBufferNode->isDataOwnedByResourceManager == MA_TRUE); /* The data should always be owned by the resource manager. */ - - /* The data buffer is not getting deleted, but we may be getting executed out of order. If so, we need to push the job back onto the queue and return. */ - if (pJob->order != ma_atomic_load_32(&pDataBufferNode->executionPointer)) { - return ma_resource_manager_post_job(pResourceManager, pJob); /* Attempting to execute out of order. Probably interleaved with a MA_JOB_TYPE_RESOURCE_MANAGER_FREE_DATA_BUFFER job. */ - } - - /* First thing we need to do is check whether or not the data buffer is getting deleted. If so we just abort. */ - if (ma_resource_manager_data_buffer_node_result(pDataBufferNode) != MA_BUSY) { - result = ma_resource_manager_data_buffer_node_result(pDataBufferNode); /* The data buffer may be getting deleted before it's even been loaded. */ - goto done; - } - - /* - We're ready to start loading. Essentially what we're doing here is initializing the data supply - of the node. Once this is complete, data buffers can have their connectors initialized which - will allow then to have audio data read from them. - - Note that when the data supply type has been moved away from "unknown", that is when other threads - will determine that the node is available for data delivery and the data buffer connectors can be - initialized. Therefore, it's important that it is set after the data supply has been initialized. - */ - if ((pJob->data.resourceManager.loadDataBufferNode.flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_DECODE) != 0) { - /* - Decoding. This is the complex case because we're not going to be doing the entire decoding - process here. Instead it's going to be split of multiple jobs and loaded in pages. The - reason for this is to evenly distribute decoding time across multiple sounds, rather than - having one huge sound hog all the available processing resources. - - The first thing we do is initialize a decoder. This is allocated on the heap and is passed - around to the paging jobs. When the last paging job has completed it's processing, it'll - free the decoder for us. - - This job does not do any actual decoding. It instead just posts a PAGE_DATA_BUFFER_NODE job - which is where the actual decoding work will be done. However, once this job is complete, - the node will be in a state where data buffer connectors can be initialized. - */ - ma_decoder* pDecoder; /* <-- Free'd on the last page decode. */ - ma_job pageDataBufferNodeJob; - - /* Allocate the decoder by initializing a decoded data supply. */ - result = ma_resource_manager_data_buffer_node_init_supply_decoded(pResourceManager, pDataBufferNode, pJob->data.resourceManager.loadDataBufferNode.pFilePath, pJob->data.resourceManager.loadDataBufferNode.pFilePathW, pJob->data.resourceManager.loadDataBufferNode.flags, &pDecoder); - - /* - Don't ever propagate an MA_BUSY result code or else the resource manager will think the - node is just busy decoding rather than in an error state. This should never happen, but - including this logic for safety just in case. - */ - if (result == MA_BUSY) { - result = MA_ERROR; - } - - if (result != MA_SUCCESS) { - if (pJob->data.resourceManager.loadDataBufferNode.pFilePath != NULL) { - ma_log_postf(ma_resource_manager_get_log(pResourceManager), MA_LOG_LEVEL_WARNING, "Failed to initialize data supply for \"%s\". %s.\n", pJob->data.resourceManager.loadDataBufferNode.pFilePath, ma_result_description(result)); - } else { - #if (defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L) || defined(_MSC_VER) - ma_log_postf(ma_resource_manager_get_log(pResourceManager), MA_LOG_LEVEL_WARNING, "Failed to initialize data supply for \"%ls\", %s.\n", pJob->data.resourceManager.loadDataBufferNode.pFilePathW, ma_result_description(result)); - #endif - } - - goto done; - } - - /* - At this point the node's data supply is initialized and other threads can start initializing - their data buffer connectors. However, no data will actually be available until we start to - actually decode it. To do this, we need to post a paging job which is where the decoding - work is done. - - Note that if an error occurred at an earlier point, this section will have been skipped. - */ - pageDataBufferNodeJob = ma_job_init(MA_JOB_TYPE_RESOURCE_MANAGER_PAGE_DATA_BUFFER_NODE); - pageDataBufferNodeJob.order = ma_resource_manager_data_buffer_node_next_execution_order(pDataBufferNode); - pageDataBufferNodeJob.data.resourceManager.pageDataBufferNode.pResourceManager = pResourceManager; - pageDataBufferNodeJob.data.resourceManager.pageDataBufferNode.pDataBufferNode = pDataBufferNode; - pageDataBufferNodeJob.data.resourceManager.pageDataBufferNode.pDecoder = pDecoder; - pageDataBufferNodeJob.data.resourceManager.pageDataBufferNode.pDoneNotification = pJob->data.resourceManager.loadDataBufferNode.pDoneNotification; - pageDataBufferNodeJob.data.resourceManager.pageDataBufferNode.pDoneFence = pJob->data.resourceManager.loadDataBufferNode.pDoneFence; - - /* The job has been set up so it can now be posted. */ - result = ma_resource_manager_post_job(pResourceManager, &pageDataBufferNodeJob); - - /* - When we get here, we want to make sure the result code is set to MA_BUSY. The reason for - this is that the result will be copied over to the node's internal result variable. In - this case, since the decoding is still in-progress, we need to make sure the result code - is set to MA_BUSY. - */ - if (result != MA_SUCCESS) { - ma_log_postf(ma_resource_manager_get_log(pResourceManager), MA_LOG_LEVEL_ERROR, "Failed to post MA_JOB_TYPE_RESOURCE_MANAGER_PAGE_DATA_BUFFER_NODE job. %s\n", ma_result_description(result)); - ma_decoder_uninit(pDecoder); - ma_free(pDecoder, &pResourceManager->config.allocationCallbacks); - } else { - result = MA_BUSY; - } - } else { - /* No decoding. This is the simple case. We need only read the file content into memory and we're done. */ - result = ma_resource_manager_data_buffer_node_init_supply_encoded(pResourceManager, pDataBufferNode, pJob->data.resourceManager.loadDataBufferNode.pFilePath, pJob->data.resourceManager.loadDataBufferNode.pFilePathW); - } - - -done: - /* File paths are no longer needed. */ - ma_free(pJob->data.resourceManager.loadDataBufferNode.pFilePath, &pResourceManager->config.allocationCallbacks); - ma_free(pJob->data.resourceManager.loadDataBufferNode.pFilePathW, &pResourceManager->config.allocationCallbacks); - - /* - We need to set the result to at the very end to ensure no other threads try reading the data before we've fully initialized the object. Other threads - are going to be inspecting this variable to determine whether or not they're ready to read data. We can only change the result if it's set to MA_BUSY - because otherwise we may be changing away from an error code which would be bad. An example is if the application creates a data buffer, but then - immediately deletes it before we've got to this point. In this case, pDataBuffer->result will be MA_UNAVAILABLE, and setting it to MA_SUCCESS or any - other error code would cause the buffer to look like it's in a state that it's not. - */ - ma_atomic_compare_and_swap_i32(&pDataBufferNode->result, MA_BUSY, result); - - /* At this point initialization is complete and we can signal the notification if any. */ - if (pJob->data.resourceManager.loadDataBufferNode.pInitNotification != NULL) { - ma_async_notification_signal(pJob->data.resourceManager.loadDataBufferNode.pInitNotification); - } - if (pJob->data.resourceManager.loadDataBufferNode.pInitFence != NULL) { - ma_fence_release(pJob->data.resourceManager.loadDataBufferNode.pInitFence); - } - - /* If we have a success result it means we've fully loaded the buffer. This will happen in the non-decoding case. */ - if (result != MA_BUSY) { - if (pJob->data.resourceManager.loadDataBufferNode.pDoneNotification != NULL) { - ma_async_notification_signal(pJob->data.resourceManager.loadDataBufferNode.pDoneNotification); - } - if (pJob->data.resourceManager.loadDataBufferNode.pDoneFence != NULL) { - ma_fence_release(pJob->data.resourceManager.loadDataBufferNode.pDoneFence); - } - } - - /* Increment the node's execution pointer so that the next jobs can be processed. This is how we keep decoding of pages in-order. */ - ma_atomic_fetch_add_32(&pDataBufferNode->executionPointer, 1); - - /* A busy result should be considered successful from the point of view of the job system. */ - if (result == MA_BUSY) { - result = MA_SUCCESS; - } - - return result; -} - -static ma_result ma_job_process__resource_manager__free_data_buffer_node(ma_job* pJob) -{ - ma_resource_manager* pResourceManager; - ma_resource_manager_data_buffer_node* pDataBufferNode; - - MA_ASSERT(pJob != NULL); - - pResourceManager = (ma_resource_manager*)pJob->data.resourceManager.freeDataBufferNode.pResourceManager; - MA_ASSERT(pResourceManager != NULL); - - pDataBufferNode = (ma_resource_manager_data_buffer_node*)pJob->data.resourceManager.freeDataBufferNode.pDataBufferNode; - MA_ASSERT(pDataBufferNode != NULL); - - if (pJob->order != ma_atomic_load_32(&pDataBufferNode->executionPointer)) { - return ma_resource_manager_post_job(pResourceManager, pJob); /* Out of order. */ - } - - ma_resource_manager_data_buffer_node_free(pResourceManager, pDataBufferNode); - - /* The event needs to be signalled last. */ - if (pJob->data.resourceManager.freeDataBufferNode.pDoneNotification != NULL) { - ma_async_notification_signal(pJob->data.resourceManager.freeDataBufferNode.pDoneNotification); - } - - if (pJob->data.resourceManager.freeDataBufferNode.pDoneFence != NULL) { - ma_fence_release(pJob->data.resourceManager.freeDataBufferNode.pDoneFence); - } - - ma_atomic_fetch_add_32(&pDataBufferNode->executionPointer, 1); - return MA_SUCCESS; -} - -static ma_result ma_job_process__resource_manager__page_data_buffer_node(ma_job* pJob) -{ - ma_result result = MA_SUCCESS; - ma_resource_manager* pResourceManager; - ma_resource_manager_data_buffer_node* pDataBufferNode; - - MA_ASSERT(pJob != NULL); - - pResourceManager = (ma_resource_manager*)pJob->data.resourceManager.pageDataBufferNode.pResourceManager; - MA_ASSERT(pResourceManager != NULL); - - pDataBufferNode = (ma_resource_manager_data_buffer_node*)pJob->data.resourceManager.pageDataBufferNode.pDataBufferNode; - MA_ASSERT(pDataBufferNode != NULL); - - if (pJob->order != ma_atomic_load_32(&pDataBufferNode->executionPointer)) { - return ma_resource_manager_post_job(pResourceManager, pJob); /* Out of order. */ - } - - /* Don't do any more decoding if the data buffer has started the uninitialization process. */ - result = ma_resource_manager_data_buffer_node_result(pDataBufferNode); - if (result != MA_BUSY) { - goto done; - } - - /* We're ready to decode the next page. */ - result = ma_resource_manager_data_buffer_node_decode_next_page(pResourceManager, pDataBufferNode, (ma_decoder*)pJob->data.resourceManager.pageDataBufferNode.pDecoder); - - /* - If we have a success code by this point, we want to post another job. We're going to set the - result back to MA_BUSY to make it clear that there's still more to load. - */ - if (result == MA_SUCCESS) { - ma_job newJob; - newJob = *pJob; /* Everything is the same as the input job, except the execution order. */ - newJob.order = ma_resource_manager_data_buffer_node_next_execution_order(pDataBufferNode); /* We need a fresh execution order. */ - - result = ma_resource_manager_post_job(pResourceManager, &newJob); - - /* Since the sound isn't yet fully decoded we want the status to be set to busy. */ - if (result == MA_SUCCESS) { - result = MA_BUSY; - } - } - -done: - /* If there's still more to decode the result will be set to MA_BUSY. Otherwise we can free the decoder. */ - if (result != MA_BUSY) { - ma_decoder_uninit((ma_decoder*)pJob->data.resourceManager.pageDataBufferNode.pDecoder); - ma_free(pJob->data.resourceManager.pageDataBufferNode.pDecoder, &pResourceManager->config.allocationCallbacks); - } - - /* If we reached the end we need to treat it as successful. */ - if (result == MA_AT_END) { - result = MA_SUCCESS; - } - - /* Make sure we set the result of node in case some error occurred. */ - ma_atomic_compare_and_swap_i32(&pDataBufferNode->result, MA_BUSY, result); - - /* Signal the notification after setting the result in case the notification callback wants to inspect the result code. */ - if (result != MA_BUSY) { - if (pJob->data.resourceManager.pageDataBufferNode.pDoneNotification != NULL) { - ma_async_notification_signal(pJob->data.resourceManager.pageDataBufferNode.pDoneNotification); - } - - if (pJob->data.resourceManager.pageDataBufferNode.pDoneFence != NULL) { - ma_fence_release(pJob->data.resourceManager.pageDataBufferNode.pDoneFence); - } - } - - ma_atomic_fetch_add_32(&pDataBufferNode->executionPointer, 1); - return result; -} - - -static ma_result ma_job_process__resource_manager__load_data_buffer(ma_job* pJob) -{ - ma_result result = MA_SUCCESS; - ma_resource_manager* pResourceManager; - ma_resource_manager_data_buffer* pDataBuffer; - ma_resource_manager_data_supply_type dataSupplyType = ma_resource_manager_data_supply_type_unknown; - ma_bool32 isConnectorInitialized = MA_FALSE; - - /* - All we're doing here is checking if the node has finished loading. If not, we just re-post the job - and keep waiting. Otherwise we increment the execution counter and set the buffer's result code. - */ - MA_ASSERT(pJob != NULL); - - pDataBuffer = (ma_resource_manager_data_buffer*)pJob->data.resourceManager.loadDataBuffer.pDataBuffer; - MA_ASSERT(pDataBuffer != NULL); - - pResourceManager = pDataBuffer->pResourceManager; - - if (pJob->order != ma_atomic_load_32(&pDataBuffer->executionPointer)) { - return ma_resource_manager_post_job(pResourceManager, pJob); /* Attempting to execute out of order. Probably interleaved with a MA_JOB_TYPE_RESOURCE_MANAGER_FREE_DATA_BUFFER job. */ - } - - /* - First thing we need to do is check whether or not the data buffer is getting deleted. If so we - just abort, but making sure we increment the execution pointer. - */ - result = ma_resource_manager_data_buffer_result(pDataBuffer); - if (result != MA_BUSY) { - goto done; /* <-- This will ensure the execution pointer is incremented. */ - } else { - result = MA_SUCCESS; /* <-- Make sure this is reset. */ - (void)result; /* <-- This is to suppress a static analysis diagnostic about "result" not being used. But for safety when I do future maintenance I don't want to delete that assignment. */ - } - - /* Try initializing the connector if we haven't already. */ - isConnectorInitialized = ma_resource_manager_data_buffer_has_connector(pDataBuffer); - if (isConnectorInitialized == MA_FALSE) { - dataSupplyType = ma_resource_manager_data_buffer_node_get_data_supply_type(pDataBuffer->pNode); - - if (dataSupplyType != ma_resource_manager_data_supply_type_unknown) { - /* We can now initialize the connector. If this fails, we need to abort. It's very rare for this to fail. */ - ma_resource_manager_data_source_config dataSourceConfig; /* For setting initial looping state and range. */ - dataSourceConfig = ma_resource_manager_data_source_config_init(); - dataSourceConfig.rangeBegInPCMFrames = pJob->data.resourceManager.loadDataBuffer.rangeBegInPCMFrames; - dataSourceConfig.rangeEndInPCMFrames = pJob->data.resourceManager.loadDataBuffer.rangeEndInPCMFrames; - dataSourceConfig.loopPointBegInPCMFrames = pJob->data.resourceManager.loadDataBuffer.loopPointBegInPCMFrames; - dataSourceConfig.loopPointEndInPCMFrames = pJob->data.resourceManager.loadDataBuffer.loopPointEndInPCMFrames; - dataSourceConfig.isLooping = pJob->data.resourceManager.loadDataBuffer.isLooping; - - result = ma_resource_manager_data_buffer_init_connector(pDataBuffer, &dataSourceConfig, pJob->data.resourceManager.loadDataBuffer.pInitNotification, pJob->data.resourceManager.loadDataBuffer.pInitFence); - if (result != MA_SUCCESS) { - ma_log_postf(ma_resource_manager_get_log(pResourceManager), MA_LOG_LEVEL_ERROR, "Failed to initialize connector for data buffer. %s.\n", ma_result_description(result)); - goto done; - } - } else { - /* Don't have a known data supply type. Most likely the data buffer node is still loading, but it could be that an error occurred. */ - } - } else { - /* The connector is already initialized. Nothing to do here. */ - } - - /* - If the data node is still loading, we need to repost the job and *not* increment the execution - pointer (i.e. we need to not fall through to the "done" label). - - There is a hole between here and the where the data connector is initialized where the data - buffer node may have finished initializing. We need to check for this by checking the result of - the data buffer node and whether or not we had an unknown data supply type at the time of - trying to initialize the data connector. - */ - result = ma_resource_manager_data_buffer_node_result(pDataBuffer->pNode); - if (result == MA_BUSY || (result == MA_SUCCESS && isConnectorInitialized == MA_FALSE && dataSupplyType == ma_resource_manager_data_supply_type_unknown)) { - return ma_resource_manager_post_job(pResourceManager, pJob); - } - -done: - /* Only move away from a busy code so that we don't trash any existing error codes. */ - ma_atomic_compare_and_swap_i32(&pDataBuffer->result, MA_BUSY, result); - - /* Only signal the other threads after the result has been set just for cleanliness sake. */ - if (pJob->data.resourceManager.loadDataBuffer.pDoneNotification != NULL) { - ma_async_notification_signal(pJob->data.resourceManager.loadDataBuffer.pDoneNotification); - } - if (pJob->data.resourceManager.loadDataBuffer.pDoneFence != NULL) { - ma_fence_release(pJob->data.resourceManager.loadDataBuffer.pDoneFence); - } - - /* - If at this point the data buffer has not had it's connector initialized, it means the - notification event was never signalled which means we need to signal it here. - */ - if (ma_resource_manager_data_buffer_has_connector(pDataBuffer) == MA_FALSE && result != MA_SUCCESS) { - if (pJob->data.resourceManager.loadDataBuffer.pInitNotification != NULL) { - ma_async_notification_signal(pJob->data.resourceManager.loadDataBuffer.pInitNotification); - } - if (pJob->data.resourceManager.loadDataBuffer.pInitFence != NULL) { - ma_fence_release(pJob->data.resourceManager.loadDataBuffer.pInitFence); - } - } - - ma_atomic_fetch_add_32(&pDataBuffer->executionPointer, 1); - return result; -} - -static ma_result ma_job_process__resource_manager__free_data_buffer(ma_job* pJob) -{ - ma_resource_manager* pResourceManager; - ma_resource_manager_data_buffer* pDataBuffer; - - MA_ASSERT(pJob != NULL); - - pDataBuffer = (ma_resource_manager_data_buffer*)pJob->data.resourceManager.freeDataBuffer.pDataBuffer; - MA_ASSERT(pDataBuffer != NULL); - - pResourceManager = pDataBuffer->pResourceManager; - - if (pJob->order != ma_atomic_load_32(&pDataBuffer->executionPointer)) { - return ma_resource_manager_post_job(pResourceManager, pJob); /* Out of order. */ - } - - ma_resource_manager_data_buffer_uninit_internal(pDataBuffer); - - /* The event needs to be signalled last. */ - if (pJob->data.resourceManager.freeDataBuffer.pDoneNotification != NULL) { - ma_async_notification_signal(pJob->data.resourceManager.freeDataBuffer.pDoneNotification); - } - - if (pJob->data.resourceManager.freeDataBuffer.pDoneFence != NULL) { - ma_fence_release(pJob->data.resourceManager.freeDataBuffer.pDoneFence); - } - - ma_atomic_fetch_add_32(&pDataBuffer->executionPointer, 1); - return MA_SUCCESS; -} - -static ma_result ma_job_process__resource_manager__load_data_stream(ma_job* pJob) -{ - ma_result result = MA_SUCCESS; - ma_decoder_config decoderConfig; - ma_uint32 pageBufferSizeInBytes; - ma_resource_manager* pResourceManager; - ma_resource_manager_data_stream* pDataStream; - - MA_ASSERT(pJob != NULL); - - pDataStream = (ma_resource_manager_data_stream*)pJob->data.resourceManager.loadDataStream.pDataStream; - MA_ASSERT(pDataStream != NULL); - - pResourceManager = pDataStream->pResourceManager; - - if (pJob->order != ma_atomic_load_32(&pDataStream->executionPointer)) { - return ma_resource_manager_post_job(pResourceManager, pJob); /* Out of order. */ - } - - if (ma_resource_manager_data_stream_result(pDataStream) != MA_BUSY) { - result = MA_INVALID_OPERATION; /* Most likely the data stream is being uninitialized. */ - goto done; - } - - /* We need to initialize the decoder first so we can determine the size of the pages. */ - decoderConfig = ma_resource_manager__init_decoder_config(pResourceManager); - - if (pJob->data.resourceManager.loadDataStream.pFilePath != NULL) { - result = ma_decoder_init_vfs(pResourceManager->config.pVFS, pJob->data.resourceManager.loadDataStream.pFilePath, &decoderConfig, &pDataStream->decoder); - } else { - result = ma_decoder_init_vfs_w(pResourceManager->config.pVFS, pJob->data.resourceManager.loadDataStream.pFilePathW, &decoderConfig, &pDataStream->decoder); - } - if (result != MA_SUCCESS) { - goto done; - } - - /* Retrieve the total length of the file before marking the decoder as loaded. */ - if ((pDataStream->flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_UNKNOWN_LENGTH) == 0) { - result = ma_decoder_get_length_in_pcm_frames(&pDataStream->decoder, &pDataStream->totalLengthInPCMFrames); - if (result != MA_SUCCESS) { - goto done; /* Failed to retrieve the length. */ - } - } else { - pDataStream->totalLengthInPCMFrames = 0; - } - - /* - Only mark the decoder as initialized when the length of the decoder has been retrieved because that can possibly require a scan over the whole file - and we don't want to have another thread trying to access the decoder while it's scanning. - */ - pDataStream->isDecoderInitialized = MA_TRUE; - - /* We have the decoder so we can now initialize our page buffer. */ - pageBufferSizeInBytes = ma_resource_manager_data_stream_get_page_size_in_frames(pDataStream) * 2 * ma_get_bytes_per_frame(pDataStream->decoder.outputFormat, pDataStream->decoder.outputChannels); - - pDataStream->pPageData = ma_malloc(pageBufferSizeInBytes, &pResourceManager->config.allocationCallbacks); - if (pDataStream->pPageData == NULL) { - ma_decoder_uninit(&pDataStream->decoder); - result = MA_OUT_OF_MEMORY; - goto done; - } - - /* Seek to our initial seek point before filling the initial pages. */ - ma_decoder_seek_to_pcm_frame(&pDataStream->decoder, pJob->data.resourceManager.loadDataStream.initialSeekPoint); - - /* We have our decoder and our page buffer, so now we need to fill our pages. */ - ma_resource_manager_data_stream_fill_pages(pDataStream); - - /* And now we're done. We want to make sure the result is MA_SUCCESS. */ - result = MA_SUCCESS; - -done: - ma_free(pJob->data.resourceManager.loadDataStream.pFilePath, &pResourceManager->config.allocationCallbacks); - ma_free(pJob->data.resourceManager.loadDataStream.pFilePathW, &pResourceManager->config.allocationCallbacks); - - /* We can only change the status away from MA_BUSY. If it's set to anything else it means an error has occurred somewhere or the uninitialization process has started (most likely). */ - ma_atomic_compare_and_swap_i32(&pDataStream->result, MA_BUSY, result); - - /* Only signal the other threads after the result has been set just for cleanliness sake. */ - if (pJob->data.resourceManager.loadDataStream.pInitNotification != NULL) { - ma_async_notification_signal(pJob->data.resourceManager.loadDataStream.pInitNotification); - } - if (pJob->data.resourceManager.loadDataStream.pInitFence != NULL) { - ma_fence_release(pJob->data.resourceManager.loadDataStream.pInitFence); - } - - ma_atomic_fetch_add_32(&pDataStream->executionPointer, 1); - return result; -} - -static ma_result ma_job_process__resource_manager__free_data_stream(ma_job* pJob) -{ - ma_resource_manager* pResourceManager; - ma_resource_manager_data_stream* pDataStream; - - MA_ASSERT(pJob != NULL); - - pDataStream = (ma_resource_manager_data_stream*)pJob->data.resourceManager.freeDataStream.pDataStream; - MA_ASSERT(pDataStream != NULL); - - pResourceManager = pDataStream->pResourceManager; - - if (pJob->order != ma_atomic_load_32(&pDataStream->executionPointer)) { - return ma_resource_manager_post_job(pResourceManager, pJob); /* Out of order. */ - } - - /* If our status is not MA_UNAVAILABLE we have a bug somewhere. */ - MA_ASSERT(ma_resource_manager_data_stream_result(pDataStream) == MA_UNAVAILABLE); - - if (pDataStream->isDecoderInitialized) { - ma_decoder_uninit(&pDataStream->decoder); - } - - if (pDataStream->pPageData != NULL) { - ma_free(pDataStream->pPageData, &pResourceManager->config.allocationCallbacks); - pDataStream->pPageData = NULL; /* Just in case... */ - } - - ma_data_source_uninit(&pDataStream->ds); - - /* The event needs to be signalled last. */ - if (pJob->data.resourceManager.freeDataStream.pDoneNotification != NULL) { - ma_async_notification_signal(pJob->data.resourceManager.freeDataStream.pDoneNotification); - } - if (pJob->data.resourceManager.freeDataStream.pDoneFence != NULL) { - ma_fence_release(pJob->data.resourceManager.freeDataStream.pDoneFence); - } - - /*ma_atomic_fetch_add_32(&pDataStream->executionPointer, 1);*/ - return MA_SUCCESS; -} - -static ma_result ma_job_process__resource_manager__page_data_stream(ma_job* pJob) -{ - ma_result result = MA_SUCCESS; - ma_resource_manager* pResourceManager; - ma_resource_manager_data_stream* pDataStream; - - MA_ASSERT(pJob != NULL); - - pDataStream = (ma_resource_manager_data_stream*)pJob->data.resourceManager.pageDataStream.pDataStream; - MA_ASSERT(pDataStream != NULL); - - pResourceManager = pDataStream->pResourceManager; - - if (pJob->order != ma_atomic_load_32(&pDataStream->executionPointer)) { - return ma_resource_manager_post_job(pResourceManager, pJob); /* Out of order. */ - } - - /* For streams, the status should be MA_SUCCESS. */ - if (ma_resource_manager_data_stream_result(pDataStream) != MA_SUCCESS) { - result = MA_INVALID_OPERATION; - goto done; - } - - ma_resource_manager_data_stream_fill_page(pDataStream, pJob->data.resourceManager.pageDataStream.pageIndex); - -done: - ma_atomic_fetch_add_32(&pDataStream->executionPointer, 1); - return result; -} - -static ma_result ma_job_process__resource_manager__seek_data_stream(ma_job* pJob) -{ - ma_result result = MA_SUCCESS; - ma_resource_manager* pResourceManager; - ma_resource_manager_data_stream* pDataStream; - - MA_ASSERT(pJob != NULL); - - pDataStream = (ma_resource_manager_data_stream*)pJob->data.resourceManager.seekDataStream.pDataStream; - MA_ASSERT(pDataStream != NULL); - - pResourceManager = pDataStream->pResourceManager; - - if (pJob->order != ma_atomic_load_32(&pDataStream->executionPointer)) { - return ma_resource_manager_post_job(pResourceManager, pJob); /* Out of order. */ - } - - /* For streams the status should be MA_SUCCESS for this to do anything. */ - if (ma_resource_manager_data_stream_result(pDataStream) != MA_SUCCESS || pDataStream->isDecoderInitialized == MA_FALSE) { - result = MA_INVALID_OPERATION; - goto done; - } - - /* - With seeking we just assume both pages are invalid and the relative frame cursor at position 0. This is basically exactly the same as loading, except - instead of initializing the decoder, we seek to a frame. - */ - ma_decoder_seek_to_pcm_frame(&pDataStream->decoder, pJob->data.resourceManager.seekDataStream.frameIndex); - - /* After seeking we'll need to reload the pages. */ - ma_resource_manager_data_stream_fill_pages(pDataStream); - - /* We need to let the public API know that we're done seeking. */ - ma_atomic_fetch_sub_32(&pDataStream->seekCounter, 1); - -done: - ma_atomic_fetch_add_32(&pDataStream->executionPointer, 1); - return result; -} - -MA_API ma_result ma_resource_manager_process_job(ma_resource_manager* pResourceManager, ma_job* pJob) -{ - if (pResourceManager == NULL || pJob == NULL) { - return MA_INVALID_ARGS; - } - - return ma_job_process(pJob); -} - -MA_API ma_result ma_resource_manager_process_next_job(ma_resource_manager* pResourceManager) -{ - ma_result result; - ma_job job; - - if (pResourceManager == NULL) { - return MA_INVALID_ARGS; - } - - /* This will return MA_CANCELLED if the next job is a quit job. */ - result = ma_resource_manager_next_job(pResourceManager, &job); - if (result != MA_SUCCESS) { - return result; - } - - return ma_job_process(&job); -} -#else -/* We'll get here if the resource manager is being excluded from the build. We need to define the job processing callbacks as no-ops. */ -static ma_result ma_job_process__resource_manager__load_data_buffer_node(ma_job* pJob) { return ma_job_process__noop(pJob); } -static ma_result ma_job_process__resource_manager__free_data_buffer_node(ma_job* pJob) { return ma_job_process__noop(pJob); } -static ma_result ma_job_process__resource_manager__page_data_buffer_node(ma_job* pJob) { return ma_job_process__noop(pJob); } -static ma_result ma_job_process__resource_manager__load_data_buffer(ma_job* pJob) { return ma_job_process__noop(pJob); } -static ma_result ma_job_process__resource_manager__free_data_buffer(ma_job* pJob) { return ma_job_process__noop(pJob); } -static ma_result ma_job_process__resource_manager__load_data_stream(ma_job* pJob) { return ma_job_process__noop(pJob); } -static ma_result ma_job_process__resource_manager__free_data_stream(ma_job* pJob) { return ma_job_process__noop(pJob); } -static ma_result ma_job_process__resource_manager__page_data_stream(ma_job* pJob) { return ma_job_process__noop(pJob); } -static ma_result ma_job_process__resource_manager__seek_data_stream(ma_job* pJob) { return ma_job_process__noop(pJob); } -#endif /* MA_NO_RESOURCE_MANAGER */ - - -#ifndef MA_NO_NODE_GRAPH - -static ma_stack* ma_stack_init(size_t sizeInBytes, const ma_allocation_callbacks* pAllocationCallbacks) -{ - ma_stack* pStack; - - if (sizeInBytes == 0) { - return NULL; - } - - pStack = (ma_stack*)ma_malloc(sizeof(*pStack) - sizeof(pStack->_data) + sizeInBytes, pAllocationCallbacks); - if (pStack == NULL) { - return NULL; - } - - pStack->offset = 0; - pStack->sizeInBytes = sizeInBytes; - - return pStack; -} - -static void ma_stack_uninit(ma_stack* pStack, const ma_allocation_callbacks* pAllocationCallbacks) -{ - if (pStack == NULL) { - return; - } - - ma_free(pStack, pAllocationCallbacks); -} - -static void* ma_stack_alloc(ma_stack* pStack, size_t sz) -{ - /* The size of the allocation is stored in the memory directly before the pointer. This needs to include padding to keep it aligned to ma_uintptr */ - void* p = (void*)((char*)pStack->_data + pStack->offset); - size_t* pSize = (size_t*)p; - - sz = (sz + (sizeof(ma_uintptr) - 1)) & ~(sizeof(ma_uintptr) - 1); /* Padding. */ - if (pStack->offset + sz + sizeof(size_t) > pStack->sizeInBytes) { - return NULL; /* Out of memory. */ - } - - pStack->offset += sz + sizeof(size_t); - - *pSize = sz; - return (void*)((char*)p + sizeof(size_t)); -} - -static void ma_stack_free(ma_stack* pStack, void* p) -{ - size_t* pSize; - - if (p == NULL) { - return; - } - - pSize = (size_t*)p - 1; - pStack->offset -= *pSize + sizeof(size_t); -} - - - -/* 10ms @ 48K = 480. Must never exceed 65535. */ -#ifndef MA_DEFAULT_NODE_CACHE_CAP_IN_FRAMES_PER_BUS -#define MA_DEFAULT_NODE_CACHE_CAP_IN_FRAMES_PER_BUS 480 -#endif - -#ifndef MA_DEFAULT_PREMIX_STACK_SIZE_PER_CHANNEL -#define MA_DEFAULT_PREMIX_STACK_SIZE_PER_CHANNEL 524288 -#endif - -static ma_result ma_node_read_pcm_frames(ma_node* pNode, ma_uint32 outputBusIndex, float* pFramesOut, ma_uint32 frameCount, ma_uint32* pFramesRead, ma_uint64 globalTime); - -MA_API void ma_debug_fill_pcm_frames_with_sine_wave(float* pFramesOut, ma_uint32 frameCount, ma_format format, ma_uint32 channels, ma_uint32 sampleRate) -{ - #ifndef MA_NO_GENERATION - { - ma_waveform_config waveformConfig; - ma_waveform waveform; - - waveformConfig = ma_waveform_config_init(format, channels, sampleRate, ma_waveform_type_sine, 1.0, 400); - ma_waveform_init(&waveformConfig, &waveform); - ma_waveform_read_pcm_frames(&waveform, pFramesOut, frameCount, NULL); - } - #else - { - (void)pFramesOut; - (void)frameCount; - (void)format; - (void)channels; - (void)sampleRate; - #if defined(MA_DEBUG_OUTPUT) - { - #if _MSC_VER - #pragma message ("ma_debug_fill_pcm_frames_with_sine_wave() will do nothing because MA_NO_GENERATION is enabled.") - #endif - } - #endif - } - #endif -} - - - -MA_API ma_node_graph_config ma_node_graph_config_init(ma_uint32 channels) -{ - ma_node_graph_config config; - - MA_ZERO_OBJECT(&config); - config.channels = channels; - config.processingSizeInFrames = 0; - - return config; -} - - -static void ma_node_graph_set_is_reading(ma_node_graph* pNodeGraph, ma_bool32 isReading) -{ - MA_ASSERT(pNodeGraph != NULL); - ma_atomic_exchange_32(&pNodeGraph->isReading, isReading); -} - -#if 0 -static ma_bool32 ma_node_graph_is_reading(ma_node_graph* pNodeGraph) -{ - MA_ASSERT(pNodeGraph != NULL); - return ma_atomic_load_32(&pNodeGraph->isReading); -} -#endif - - -static void ma_node_graph_node_process_pcm_frames(ma_node* pNode, const float** ppFramesIn, ma_uint32* pFrameCountIn, float** ppFramesOut, ma_uint32* pFrameCountOut) -{ - ma_node_graph* pNodeGraph = (ma_node_graph*)pNode; - ma_uint64 framesRead; - - ma_node_graph_read_pcm_frames(pNodeGraph, ppFramesOut[0], *pFrameCountOut, &framesRead); - - *pFrameCountOut = (ma_uint32)framesRead; /* Safe cast. */ - - (void)ppFramesIn; - (void)pFrameCountIn; -} - -static ma_node_vtable g_node_graph_node_vtable = -{ - ma_node_graph_node_process_pcm_frames, - NULL, /* onGetRequiredInputFrameCount */ - 0, /* 0 input buses. */ - 1, /* 1 output bus. */ - 0 /* Flags. */ -}; - -static void ma_node_graph_endpoint_process_pcm_frames(ma_node* pNode, const float** ppFramesIn, ma_uint32* pFrameCountIn, float** ppFramesOut, ma_uint32* pFrameCountOut) -{ - MA_ASSERT(pNode != NULL); - MA_ASSERT(ma_node_get_input_bus_count(pNode) == 1); - MA_ASSERT(ma_node_get_output_bus_count(pNode) == 1); - - /* Input channel count needs to be the same as the output channel count. */ - MA_ASSERT(ma_node_get_input_channels(pNode, 0) == ma_node_get_output_channels(pNode, 0)); - - /* We don't need to do anything here because it's a passthrough. */ - (void)pNode; - (void)ppFramesIn; - (void)pFrameCountIn; - (void)ppFramesOut; - (void)pFrameCountOut; - -#if 0 - /* The data has already been mixed. We just need to move it to the output buffer. */ - if (ppFramesIn != NULL) { - ma_copy_pcm_frames(ppFramesOut[0], ppFramesIn[0], *pFrameCountOut, ma_format_f32, ma_node_get_output_channels(pNode, 0)); - } -#endif -} - -static ma_node_vtable g_node_graph_endpoint_vtable = -{ - ma_node_graph_endpoint_process_pcm_frames, - NULL, /* onGetRequiredInputFrameCount */ - 1, /* 1 input bus. */ - 1, /* 1 output bus. */ - MA_NODE_FLAG_PASSTHROUGH /* Flags. The endpoint is a passthrough. */ -}; - -MA_API ma_result ma_node_graph_init(const ma_node_graph_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_node_graph* pNodeGraph) -{ - ma_result result; - ma_node_config baseConfig; - ma_node_config endpointConfig; - - if (pNodeGraph == NULL) { - return MA_INVALID_ARGS; - } - - MA_ZERO_OBJECT(pNodeGraph); - pNodeGraph->processingSizeInFrames = pConfig->processingSizeInFrames; - - /* Base node so we can use the node graph as a node into another graph. */ - baseConfig = ma_node_config_init(); - baseConfig.vtable = &g_node_graph_node_vtable; - baseConfig.pOutputChannels = &pConfig->channels; - - result = ma_node_init(pNodeGraph, &baseConfig, pAllocationCallbacks, &pNodeGraph->base); - if (result != MA_SUCCESS) { - return result; - } - - - /* Endpoint. */ - endpointConfig = ma_node_config_init(); - endpointConfig.vtable = &g_node_graph_endpoint_vtable; - endpointConfig.pInputChannels = &pConfig->channels; - endpointConfig.pOutputChannels = &pConfig->channels; - - result = ma_node_init(pNodeGraph, &endpointConfig, pAllocationCallbacks, &pNodeGraph->endpoint); - if (result != MA_SUCCESS) { - ma_node_uninit(&pNodeGraph->base, pAllocationCallbacks); - return result; - } - - - /* Processing cache. */ - if (pConfig->processingSizeInFrames > 0) { - pNodeGraph->pProcessingCache = (float*)ma_malloc(pConfig->processingSizeInFrames * pConfig->channels * sizeof(float), pAllocationCallbacks); - if (pNodeGraph->pProcessingCache == NULL) { - ma_node_uninit(&pNodeGraph->endpoint, pAllocationCallbacks); - ma_node_uninit(&pNodeGraph->base, pAllocationCallbacks); - return MA_OUT_OF_MEMORY; - } - } - - - /* - We need a pre-mix stack. The size of this stack is configurable via the config. The default value depends on the channel count. - */ - { - size_t preMixStackSizeInBytes = pConfig->preMixStackSizeInBytes; - if (preMixStackSizeInBytes == 0) { - preMixStackSizeInBytes = pConfig->channels * MA_DEFAULT_PREMIX_STACK_SIZE_PER_CHANNEL; - } - - pNodeGraph->pPreMixStack = ma_stack_init(preMixStackSizeInBytes, pAllocationCallbacks); - if (pNodeGraph->pPreMixStack == NULL) { - ma_node_uninit(&pNodeGraph->endpoint, pAllocationCallbacks); - ma_node_uninit(&pNodeGraph->base, pAllocationCallbacks); - if (pNodeGraph->pProcessingCache != NULL) { - ma_free(pNodeGraph->pProcessingCache, pAllocationCallbacks); - } - - return MA_OUT_OF_MEMORY; - } - } - - - return MA_SUCCESS; -} - -MA_API void ma_node_graph_uninit(ma_node_graph* pNodeGraph, const ma_allocation_callbacks* pAllocationCallbacks) -{ - if (pNodeGraph == NULL) { - return; - } - - ma_node_uninit(&pNodeGraph->endpoint, pAllocationCallbacks); - ma_node_uninit(&pNodeGraph->base, pAllocationCallbacks); - - if (pNodeGraph->pProcessingCache != NULL) { - ma_free(pNodeGraph->pProcessingCache, pAllocationCallbacks); - pNodeGraph->pProcessingCache = NULL; - } - - if (pNodeGraph->pPreMixStack != NULL) { - ma_stack_uninit(pNodeGraph->pPreMixStack, pAllocationCallbacks); - pNodeGraph->pPreMixStack = NULL; - } -} - -MA_API ma_node* ma_node_graph_get_endpoint(ma_node_graph* pNodeGraph) -{ - if (pNodeGraph == NULL) { - return NULL; - } - - return &pNodeGraph->endpoint; -} - -MA_API ma_result ma_node_graph_read_pcm_frames(ma_node_graph* pNodeGraph, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead) -{ - ma_result result = MA_SUCCESS; - ma_uint64 totalFramesRead; - ma_uint32 channels; - - if (pFramesRead != NULL) { - *pFramesRead = 0; /* Safety. */ - } - - if (pNodeGraph == NULL) { - return MA_INVALID_ARGS; - } - - channels = ma_node_get_output_channels(&pNodeGraph->endpoint, 0); - - - /* We'll be nice and try to do a full read of all frameCount frames. */ - totalFramesRead = 0; - while (totalFramesRead < frameCount) { - ma_uint32 framesJustRead; - ma_uint64 framesToRead; - float* pRunningFramesOut; - - framesToRead = frameCount - totalFramesRead; - if (framesToRead > 0xFFFFFFFF) { - framesToRead = 0xFFFFFFFF; - } - - pRunningFramesOut = (float*)ma_offset_pcm_frames_ptr(pFramesOut, totalFramesRead, ma_format_f32, channels); - - /* If there's anything in the cache, consume that first. */ - if (pNodeGraph->processingCacheFramesRemaining > 0) { - ma_uint32 framesToReadFromCache; - - framesToReadFromCache = (ma_uint32)framesToRead; - if (framesToReadFromCache > pNodeGraph->processingCacheFramesRemaining) { - framesToReadFromCache = pNodeGraph->processingCacheFramesRemaining; - } - - MA_COPY_MEMORY(pRunningFramesOut, pNodeGraph->pProcessingCache, framesToReadFromCache * channels * sizeof(float)); - MA_MOVE_MEMORY(pNodeGraph->pProcessingCache, pNodeGraph->pProcessingCache + (framesToReadFromCache * channels), (pNodeGraph->processingCacheFramesRemaining - framesToReadFromCache) * channels * sizeof(float)); - pNodeGraph->processingCacheFramesRemaining -= framesToReadFromCache; - - totalFramesRead += framesToReadFromCache; - continue; - } else { - /* - If processingSizeInFrames is non-zero, we need to make sure we always read in chunks of that size. If the frame count is less than - that, we need to read into the cache and then continue on. - */ - float* pReadDst = pRunningFramesOut; - - if (pNodeGraph->processingSizeInFrames > 0) { - if (framesToRead < pNodeGraph->processingSizeInFrames) { - pReadDst = pNodeGraph->pProcessingCache; /* We need to read into the cache because otherwise we'll overflow the output buffer. */ - } - - framesToRead = pNodeGraph->processingSizeInFrames; - } - - ma_node_graph_set_is_reading(pNodeGraph, MA_TRUE); - { - result = ma_node_read_pcm_frames(&pNodeGraph->endpoint, 0, pReadDst, (ma_uint32)framesToRead, &framesJustRead, ma_node_get_time(&pNodeGraph->endpoint)); - } - ma_node_graph_set_is_reading(pNodeGraph, MA_FALSE); - - /* - Do not increment the total frames read counter if we read into the cache. We use this to determine how many frames have - been written to the final output buffer. - */ - if (pReadDst == pNodeGraph->pProcessingCache) { - /* We read into the cache. */ - pNodeGraph->processingCacheFramesRemaining = framesJustRead; - } else { - /* We read straight into the output buffer. */ - totalFramesRead += framesJustRead; - } - - if (result != MA_SUCCESS) { - break; - } - - /* Abort if we weren't able to read any frames or else we risk getting stuck in a loop. */ - if (framesJustRead == 0) { - break; - } - } - } - - /* Let's go ahead and silence any leftover frames just for some added safety to ensure the caller doesn't try emitting garbage out of the speakers. */ - if (totalFramesRead < frameCount) { - ma_silence_pcm_frames(ma_offset_pcm_frames_ptr(pFramesOut, totalFramesRead, ma_format_f32, channels), (frameCount - totalFramesRead), ma_format_f32, channels); - } - - if (pFramesRead != NULL) { - *pFramesRead = totalFramesRead; - } - - return result; -} - -MA_API ma_uint32 ma_node_graph_get_channels(const ma_node_graph* pNodeGraph) -{ - if (pNodeGraph == NULL) { - return 0; - } - - return ma_node_get_output_channels(&pNodeGraph->endpoint, 0); -} - -MA_API ma_uint64 ma_node_graph_get_time(const ma_node_graph* pNodeGraph) -{ - if (pNodeGraph == NULL) { - return 0; - } - - return ma_node_get_time(&pNodeGraph->endpoint); /* Global time is just the local time of the endpoint. */ -} - -MA_API ma_result ma_node_graph_set_time(ma_node_graph* pNodeGraph, ma_uint64 globalTime) -{ - if (pNodeGraph == NULL) { - return MA_INVALID_ARGS; - } - - return ma_node_set_time(&pNodeGraph->endpoint, globalTime); /* Global time is just the local time of the endpoint. */ -} - - -#define MA_NODE_OUTPUT_BUS_FLAG_HAS_READ 0x01 /* Whether or not this bus ready to read more data. Only used on nodes with multiple output buses. */ - -static ma_result ma_node_output_bus_init(ma_node* pNode, ma_uint32 outputBusIndex, ma_uint32 channels, ma_node_output_bus* pOutputBus) -{ - MA_ASSERT(pOutputBus != NULL); - MA_ASSERT(outputBusIndex < MA_MAX_NODE_BUS_COUNT); - MA_ASSERT(outputBusIndex < ma_node_get_output_bus_count(pNode)); - MA_ASSERT(channels < 256); - - MA_ZERO_OBJECT(pOutputBus); - - if (channels == 0) { - return MA_INVALID_ARGS; - } - - pOutputBus->pNode = pNode; - pOutputBus->outputBusIndex = (ma_uint8)outputBusIndex; - pOutputBus->channels = (ma_uint8)channels; - pOutputBus->flags = MA_NODE_OUTPUT_BUS_FLAG_HAS_READ; /* <-- Important that this flag is set by default. */ - pOutputBus->volume = 1; - - return MA_SUCCESS; -} - -static void ma_node_output_bus_lock(ma_node_output_bus* pOutputBus) -{ - ma_spinlock_lock(&pOutputBus->lock); -} - -static void ma_node_output_bus_unlock(ma_node_output_bus* pOutputBus) -{ - ma_spinlock_unlock(&pOutputBus->lock); -} - - -static ma_uint32 ma_node_output_bus_get_channels(const ma_node_output_bus* pOutputBus) -{ - return pOutputBus->channels; -} - - -static void ma_node_output_bus_set_has_read(ma_node_output_bus* pOutputBus, ma_bool32 hasRead) -{ - if (hasRead) { - ma_atomic_fetch_or_32(&pOutputBus->flags, MA_NODE_OUTPUT_BUS_FLAG_HAS_READ); - } else { - ma_atomic_fetch_and_32(&pOutputBus->flags, (ma_uint32)~MA_NODE_OUTPUT_BUS_FLAG_HAS_READ); - } -} - -static ma_bool32 ma_node_output_bus_has_read(ma_node_output_bus* pOutputBus) -{ - return (ma_atomic_load_32(&pOutputBus->flags) & MA_NODE_OUTPUT_BUS_FLAG_HAS_READ) != 0; -} - - -static void ma_node_output_bus_set_is_attached(ma_node_output_bus* pOutputBus, ma_bool32 isAttached) -{ - ma_atomic_exchange_32(&pOutputBus->isAttached, isAttached); -} - -static ma_bool32 ma_node_output_bus_is_attached(ma_node_output_bus* pOutputBus) -{ - return ma_atomic_load_32(&pOutputBus->isAttached); -} - - -static ma_result ma_node_output_bus_set_volume(ma_node_output_bus* pOutputBus, float volume) -{ - MA_ASSERT(pOutputBus != NULL); - - if (volume < 0.0f) { - volume = 0.0f; - } - - ma_atomic_exchange_f32(&pOutputBus->volume, volume); - - return MA_SUCCESS; -} - -static float ma_node_output_bus_get_volume(const ma_node_output_bus* pOutputBus) -{ - return ma_atomic_load_f32((float*)&pOutputBus->volume); -} - - -static ma_result ma_node_input_bus_init(ma_uint32 channels, ma_node_input_bus* pInputBus) -{ - MA_ASSERT(pInputBus != NULL); - MA_ASSERT(channels < 256); - - MA_ZERO_OBJECT(pInputBus); - - if (channels == 0) { - return MA_INVALID_ARGS; - } - - pInputBus->channels = (ma_uint8)channels; - - return MA_SUCCESS; -} - -static void ma_node_input_bus_lock(ma_node_input_bus* pInputBus) -{ - MA_ASSERT(pInputBus != NULL); - - ma_spinlock_lock(&pInputBus->lock); -} - -static void ma_node_input_bus_unlock(ma_node_input_bus* pInputBus) -{ - MA_ASSERT(pInputBus != NULL); - - ma_spinlock_unlock(&pInputBus->lock); -} - - -static void ma_node_input_bus_next_begin(ma_node_input_bus* pInputBus) -{ - ma_atomic_fetch_add_32(&pInputBus->nextCounter, 1); -} - -static void ma_node_input_bus_next_end(ma_node_input_bus* pInputBus) -{ - ma_atomic_fetch_sub_32(&pInputBus->nextCounter, 1); -} - -static ma_uint32 ma_node_input_bus_get_next_counter(ma_node_input_bus* pInputBus) -{ - return ma_atomic_load_32(&pInputBus->nextCounter); -} - - -static ma_uint32 ma_node_input_bus_get_channels(const ma_node_input_bus* pInputBus) -{ - return pInputBus->channels; -} - - -static void ma_node_input_bus_detach__no_output_bus_lock(ma_node_input_bus* pInputBus, ma_node_output_bus* pOutputBus) -{ - MA_ASSERT(pInputBus != NULL); - MA_ASSERT(pOutputBus != NULL); - - /* - Mark the output bus as detached first. This will prevent future iterations on the audio thread - from iterating this output bus. - */ - ma_node_output_bus_set_is_attached(pOutputBus, MA_FALSE); - - /* - We cannot use the output bus lock here since it'll be getting used at a higher level, but we do - still need to use the input bus lock since we'll be updating pointers on two different output - buses. The same rules apply here as the attaching case. Although we're using a lock here, we're - *not* using a lock when iterating over the list in the audio thread. We therefore need to craft - this in a way such that the iteration on the audio thread doesn't break. - - The first thing to do is swap out the "next" pointer of the previous output bus with the - new "next" output bus. This is the operation that matters for iteration on the audio thread. - After that, the previous pointer on the new "next" pointer needs to be updated, after which - point the linked list will be in a good state. - */ - ma_node_input_bus_lock(pInputBus); - { - ma_node_output_bus* pOldPrev = (ma_node_output_bus*)ma_atomic_load_ptr(&pOutputBus->pPrev); - ma_node_output_bus* pOldNext = (ma_node_output_bus*)ma_atomic_load_ptr(&pOutputBus->pNext); - - if (pOldPrev != NULL) { - ma_atomic_exchange_ptr(&pOldPrev->pNext, pOldNext); /* <-- This is where the output bus is detached from the list. */ - } - if (pOldNext != NULL) { - ma_atomic_exchange_ptr(&pOldNext->pPrev, pOldPrev); /* <-- This is required for detachment. */ - } - } - ma_node_input_bus_unlock(pInputBus); - - /* At this point the output bus is detached and the linked list is completely unaware of it. Reset some data for safety. */ - ma_atomic_exchange_ptr(&pOutputBus->pNext, NULL); /* Using atomic exchanges here, mainly for the benefit of analysis tools which don't always recognize spinlocks. */ - ma_atomic_exchange_ptr(&pOutputBus->pPrev, NULL); /* As above. */ - pOutputBus->pInputNode = NULL; - pOutputBus->inputNodeInputBusIndex = 0; - - - /* - For thread-safety reasons, we don't want to be returning from this straight away. We need to - wait for the audio thread to finish with the output bus. There's two things we need to wait - for. The first is the part that selects the next output bus in the list, and the other is the - part that reads from the output bus. Basically all we're doing is waiting for the input bus - to stop referencing the output bus. - - We're doing this part last because we want the section above to run while the audio thread - is finishing up with the output bus, just for efficiency reasons. We marked the output bus as - detached right at the top of this function which is going to prevent the audio thread from - iterating the output bus again. - */ - - /* Part 1: Wait for the current iteration to complete. */ - while (ma_node_input_bus_get_next_counter(pInputBus) > 0) { - ma_yield(); - } - - /* Part 2: Wait for any reads to complete. */ - while (ma_atomic_load_32(&pOutputBus->refCount) > 0) { - ma_yield(); - } - - /* - At this point we're done detaching and we can be guaranteed that the audio thread is not going - to attempt to reference this output bus again (until attached again). - */ -} - -#if 0 /* Not used at the moment, but leaving here in case I need it later. */ -static void ma_node_input_bus_detach(ma_node_input_bus* pInputBus, ma_node_output_bus* pOutputBus) -{ - MA_ASSERT(pInputBus != NULL); - MA_ASSERT(pOutputBus != NULL); - - ma_node_output_bus_lock(pOutputBus); - { - ma_node_input_bus_detach__no_output_bus_lock(pInputBus, pOutputBus); - } - ma_node_output_bus_unlock(pOutputBus); -} -#endif - -static void ma_node_input_bus_attach(ma_node_input_bus* pInputBus, ma_node_output_bus* pOutputBus, ma_node* pNewInputNode, ma_uint32 inputNodeInputBusIndex) -{ - MA_ASSERT(pInputBus != NULL); - MA_ASSERT(pOutputBus != NULL); - - ma_node_output_bus_lock(pOutputBus); - { - ma_node_output_bus* pOldInputNode = (ma_node_output_bus*)ma_atomic_load_ptr(&pOutputBus->pInputNode); - - /* Detach from any existing attachment first if necessary. */ - if (pOldInputNode != NULL) { - ma_node_input_bus_detach__no_output_bus_lock(pInputBus, pOutputBus); - } - - /* - At this point we can be sure the output bus is not attached to anything. The linked list in the - old input bus has been updated so that pOutputBus will not get iterated again. - */ - pOutputBus->pInputNode = pNewInputNode; /* No need for an atomic assignment here because modification of this variable always happens within a lock. */ - pOutputBus->inputNodeInputBusIndex = (ma_uint8)inputNodeInputBusIndex; - - /* - Now we need to attach the output bus to the linked list. This involves updating two pointers on - two different output buses so I'm going to go ahead and keep this simple and just use a lock. - There are ways to do this without a lock, but it's just too hard to maintain for its value. - - Although we're locking here, it's important to remember that we're *not* locking when iterating - and reading audio data since that'll be running on the audio thread. As a result we need to be - careful how we craft this so that we don't break iteration. What we're going to do is always - attach the new item so that it becomes the first item in the list. That way, as we're iterating - we won't break any links in the list and iteration will continue safely. The detaching case will - also be crafted in a way as to not break list iteration. It's important to remember to use - atomic exchanges here since no locking is happening on the audio thread during iteration. - */ - ma_node_input_bus_lock(pInputBus); - { - ma_node_output_bus* pNewPrev = &pInputBus->head; - ma_node_output_bus* pNewNext = (ma_node_output_bus*)ma_atomic_load_ptr(&pInputBus->head.pNext); - - /* Update the local output bus. */ - ma_atomic_exchange_ptr(&pOutputBus->pPrev, pNewPrev); - ma_atomic_exchange_ptr(&pOutputBus->pNext, pNewNext); - - /* Update the other output buses to point back to the local output bus. */ - ma_atomic_exchange_ptr(&pInputBus->head.pNext, pOutputBus); /* <-- This is where the output bus is actually attached to the input bus. */ - - /* Do the previous pointer last. This is only used for detachment. */ - if (pNewNext != NULL) { - ma_atomic_exchange_ptr(&pNewNext->pPrev, pOutputBus); - } - } - ma_node_input_bus_unlock(pInputBus); - - /* - Mark the node as attached last. This is used to controlling whether or the output bus will be - iterated on the audio thread. Mainly required for detachment purposes. - */ - ma_node_output_bus_set_is_attached(pOutputBus, MA_TRUE); - } - ma_node_output_bus_unlock(pOutputBus); -} - -static ma_node_output_bus* ma_node_input_bus_next(ma_node_input_bus* pInputBus, ma_node_output_bus* pOutputBus) -{ - ma_node_output_bus* pNext; - - MA_ASSERT(pInputBus != NULL); - - if (pOutputBus == NULL) { - return NULL; - } - - ma_node_input_bus_next_begin(pInputBus); - { - pNext = pOutputBus; - for (;;) { - pNext = (ma_node_output_bus*)ma_atomic_load_ptr(&pNext->pNext); - if (pNext == NULL) { - break; /* Reached the end. */ - } - - if (ma_node_output_bus_is_attached(pNext) == MA_FALSE) { - continue; /* The node is not attached. Keep checking. */ - } - - /* The next node has been selected. */ - break; - } - - /* We need to increment the reference count of the selected node. */ - if (pNext != NULL) { - ma_atomic_fetch_add_32(&pNext->refCount, 1); - } - - /* The previous node is no longer being referenced. */ - ma_atomic_fetch_sub_32(&pOutputBus->refCount, 1); - } - ma_node_input_bus_next_end(pInputBus); - - return pNext; -} - -static ma_node_output_bus* ma_node_input_bus_first(ma_node_input_bus* pInputBus) -{ - return ma_node_input_bus_next(pInputBus, &pInputBus->head); -} - - - -static ma_result ma_node_input_bus_read_pcm_frames(ma_node* pInputNode, ma_node_input_bus* pInputBus, float* pFramesOut, ma_uint32 frameCount, ma_uint32* pFramesRead, ma_uint64 globalTime) -{ - ma_result result = MA_SUCCESS; - ma_node_output_bus* pOutputBus; - ma_node_output_bus* pFirst; - ma_uint32 inputChannels; - ma_bool32 doesOutputBufferHaveContent = MA_FALSE; - - /* - This will be called from the audio thread which means we can't be doing any locking. Basically, - this function will not perform any locking, whereas attaching and detaching will, but crafted in - such a way that we don't need to perform any locking here. The important thing to remember is - to always iterate in a forward direction. - - In order to process any data we need to first read from all input buses. That's where this - function comes in. This iterates over each of the attachments and accumulates/mixes them. We - also convert the channels to the nodes output channel count before mixing. We want to do this - channel conversion so that the caller of this function can invoke the processing callback - without having to do it themselves. - - When we iterate over each of the attachments on the input bus, we need to read as much data as - we can from each of them so that we don't end up with holes between each of the attachments. To - do this, we need to read from each attachment in a loop and read as many frames as we can, up - to `frameCount`. - */ - MA_ASSERT(pInputNode != NULL); - MA_ASSERT(pFramesRead != NULL); /* pFramesRead is critical and must always be specified. On input it's undefined and on output it'll be set to the number of frames actually read. */ - - *pFramesRead = 0; /* Safety. */ - - inputChannels = ma_node_input_bus_get_channels(pInputBus); - - /* - We need to be careful with how we call ma_node_input_bus_first() and ma_node_input_bus_next(). They - are both critical to our lock-free thread-safety system. We can only call ma_node_input_bus_first() - once per iteration, however we have an optimization to checks whether or not it's the first item in - the list. We therefore need to store a pointer to the first item rather than repeatedly calling - ma_node_input_bus_first(). It's safe to keep hold of this pointer, so long as we don't dereference it - after calling ma_node_input_bus_next(), which we won't be. - */ - pFirst = ma_node_input_bus_first(pInputBus); - if (pFirst == NULL) { - return MA_SUCCESS; /* No attachments. Read nothing. */ - } - - for (pOutputBus = pFirst; pOutputBus != NULL; pOutputBus = ma_node_input_bus_next(pInputBus, pOutputBus)) { - ma_uint32 framesProcessed = 0; - ma_bool32 isSilentOutput = MA_FALSE; - - MA_ASSERT(pOutputBus->pNode != NULL); - MA_ASSERT(((ma_node_base*)pOutputBus->pNode)->vtable != NULL); - - isSilentOutput = (((ma_node_base*)pOutputBus->pNode)->vtable->flags & MA_NODE_FLAG_SILENT_OUTPUT) != 0; - - if (pFramesOut != NULL) { - /* Read. */ - while (framesProcessed < frameCount) { - float* pRunningFramesOut; - ma_uint32 framesToRead; - ma_uint32 framesJustRead = 0; - - framesToRead = frameCount - framesProcessed; - pRunningFramesOut = ma_offset_pcm_frames_ptr_f32(pFramesOut, framesProcessed, inputChannels); - - if (doesOutputBufferHaveContent == MA_FALSE) { - /* Fast path. First attachment. We just read straight into the output buffer (no mixing required). */ - result = ma_node_read_pcm_frames(pOutputBus->pNode, pOutputBus->outputBusIndex, pRunningFramesOut, framesToRead, &framesJustRead, globalTime + framesProcessed); - } else { - /* Slow path. Not the first attachment. Mixing required. */ - ma_uint32 preMixBufferCapInFrames = ((ma_node_base*)pInputNode)->cachedDataCapInFramesPerBus; - float* pPreMixBuffer = (float*)ma_stack_alloc(((ma_node_base*)pInputNode)->pNodeGraph->pPreMixStack, preMixBufferCapInFrames * inputChannels * sizeof(float)); - - if (pPreMixBuffer == NULL) { - /* - If you're hitting this assert it means you've got an unusually deep chain of nodes, you've got an excessively large processing - size, or you have a combination of both, and as a result have run out of stack space. You can increase this using the - preMixStackSizeInBytes variable in ma_node_graph_config. If you're using ma_engine, you can do it via the preMixStackSizeInBytes - variable in ma_engine_config. It defaults to 512KB per output channel. - */ - MA_ASSERT(MA_FALSE); - } else { - if (framesToRead > preMixBufferCapInFrames) { - framesToRead = preMixBufferCapInFrames; - } - - result = ma_node_read_pcm_frames(pOutputBus->pNode, pOutputBus->outputBusIndex, pPreMixBuffer, framesToRead, &framesJustRead, globalTime + framesProcessed); - if (result == MA_SUCCESS || result == MA_AT_END) { - if (isSilentOutput == MA_FALSE) { /* Don't mix if the node outputs silence. */ - ma_mix_pcm_frames_f32(pRunningFramesOut, pPreMixBuffer, framesJustRead, inputChannels, /*volume*/1); - } - } - - /* The pre-mix buffer is no longer required. */ - ma_stack_free(((ma_node_base*)pInputNode)->pNodeGraph->pPreMixStack, pPreMixBuffer); - pPreMixBuffer = NULL; - } - } - - framesProcessed += framesJustRead; - - /* If we reached the end or otherwise failed to read any data we need to finish up with this output node. */ - if (result != MA_SUCCESS) { - break; - } - - /* If we didn't read anything, abort so we don't get stuck in a loop. */ - if (framesJustRead == 0) { - break; - } - } - - /* If it's the first attachment we didn't do any mixing. Any leftover samples need to be silenced. */ - if (pOutputBus == pFirst && framesProcessed < frameCount) { - ma_silence_pcm_frames(ma_offset_pcm_frames_ptr(pFramesOut, framesProcessed, ma_format_f32, inputChannels), (frameCount - framesProcessed), ma_format_f32, inputChannels); - } - - if (isSilentOutput == MA_FALSE) { - doesOutputBufferHaveContent = MA_TRUE; - } - } else { - /* Seek. */ - ma_node_read_pcm_frames(pOutputBus->pNode, pOutputBus->outputBusIndex, NULL, frameCount, &framesProcessed, globalTime); - } - } - - /* If we didn't output anything, output silence. */ - if (doesOutputBufferHaveContent == MA_FALSE && pFramesOut != NULL) { - ma_silence_pcm_frames(pFramesOut, frameCount, ma_format_f32, inputChannels); - } - - /* In this path we always "process" the entire amount. */ - *pFramesRead = frameCount; - - return result; -} - - -MA_API ma_node_config ma_node_config_init(void) -{ - ma_node_config config; - - MA_ZERO_OBJECT(&config); - config.initialState = ma_node_state_started; /* Nodes are started by default. */ - config.inputBusCount = MA_NODE_BUS_COUNT_UNKNOWN; - config.outputBusCount = MA_NODE_BUS_COUNT_UNKNOWN; - - return config; -} - -static ma_uint16 ma_node_config_get_cache_size_in_frames(const ma_node_config* pConfig, const ma_node_graph* pNodeGraph) -{ - ma_uint32 cacheSizeInFrames; - - (void)pConfig; - - if (pNodeGraph->processingSizeInFrames > 0) { - cacheSizeInFrames = pNodeGraph->processingSizeInFrames; - } else { - cacheSizeInFrames = MA_DEFAULT_NODE_CACHE_CAP_IN_FRAMES_PER_BUS; - } - - if (cacheSizeInFrames > 0xFFFF) { - cacheSizeInFrames = 0xFFFF; - } - - return (ma_uint16)cacheSizeInFrames; -} - - - -static ma_result ma_node_detach_full(ma_node* pNode); - -static float* ma_node_get_cached_input_ptr(ma_node* pNode, ma_uint32 inputBusIndex) -{ - ma_node_base* pNodeBase = (ma_node_base*)pNode; - ma_uint32 iInputBus; - float* pBasePtr; - - MA_ASSERT(pNodeBase != NULL); - - /* Input data is stored at the front of the buffer. */ - pBasePtr = pNodeBase->pCachedData; - for (iInputBus = 0; iInputBus < inputBusIndex; iInputBus += 1) { - pBasePtr += pNodeBase->cachedDataCapInFramesPerBus * ma_node_input_bus_get_channels(&pNodeBase->pInputBuses[iInputBus]); - } - - return pBasePtr; -} - -static float* ma_node_get_cached_output_ptr(ma_node* pNode, ma_uint32 outputBusIndex) -{ - ma_node_base* pNodeBase = (ma_node_base*)pNode; - ma_uint32 iInputBus; - ma_uint32 iOutputBus; - float* pBasePtr; - - MA_ASSERT(pNodeBase != NULL); - - /* Cached output data starts after the input data. */ - pBasePtr = pNodeBase->pCachedData; - for (iInputBus = 0; iInputBus < ma_node_get_input_bus_count(pNodeBase); iInputBus += 1) { - pBasePtr += pNodeBase->cachedDataCapInFramesPerBus * ma_node_input_bus_get_channels(&pNodeBase->pInputBuses[iInputBus]); - } - - for (iOutputBus = 0; iOutputBus < outputBusIndex; iOutputBus += 1) { - pBasePtr += pNodeBase->cachedDataCapInFramesPerBus * ma_node_output_bus_get_channels(&pNodeBase->pOutputBuses[iOutputBus]); - } - - return pBasePtr; -} - - -typedef struct -{ - size_t sizeInBytes; - size_t inputBusOffset; - size_t outputBusOffset; - size_t cachedDataOffset; - ma_uint32 inputBusCount; /* So it doesn't have to be calculated twice. */ - ma_uint32 outputBusCount; /* So it doesn't have to be calculated twice. */ -} ma_node_heap_layout; - -static ma_result ma_node_translate_bus_counts(const ma_node_config* pConfig, ma_uint32* pInputBusCount, ma_uint32* pOutputBusCount) -{ - ma_uint32 inputBusCount; - ma_uint32 outputBusCount; - - MA_ASSERT(pConfig != NULL); - MA_ASSERT(pInputBusCount != NULL); - MA_ASSERT(pOutputBusCount != NULL); - - /* Bus counts are determined by the vtable, unless they're set to `MA_NODE_BUS_COUNT_UNKNWON`, in which case they're taken from the config. */ - if (pConfig->vtable->inputBusCount == MA_NODE_BUS_COUNT_UNKNOWN) { - inputBusCount = pConfig->inputBusCount; - } else { - inputBusCount = pConfig->vtable->inputBusCount; - - if (pConfig->inputBusCount != MA_NODE_BUS_COUNT_UNKNOWN && pConfig->inputBusCount != pConfig->vtable->inputBusCount) { - return MA_INVALID_ARGS; /* Invalid configuration. You must not specify a conflicting bus count between the node's config and the vtable. */ - } - } - - if (pConfig->vtable->outputBusCount == MA_NODE_BUS_COUNT_UNKNOWN) { - outputBusCount = pConfig->outputBusCount; - } else { - outputBusCount = pConfig->vtable->outputBusCount; - - if (pConfig->outputBusCount != MA_NODE_BUS_COUNT_UNKNOWN && pConfig->outputBusCount != pConfig->vtable->outputBusCount) { - return MA_INVALID_ARGS; /* Invalid configuration. You must not specify a conflicting bus count between the node's config and the vtable. */ - } - } - - /* Bus counts must be within limits. */ - if (inputBusCount > MA_MAX_NODE_BUS_COUNT || outputBusCount > MA_MAX_NODE_BUS_COUNT) { - return MA_INVALID_ARGS; - } - - - /* We must have channel counts for each bus. */ - if ((inputBusCount > 0 && pConfig->pInputChannels == NULL) || (outputBusCount > 0 && pConfig->pOutputChannels == NULL)) { - return MA_INVALID_ARGS; /* You must specify channel counts for each input and output bus. */ - } - - - /* Some special rules for passthrough nodes. */ - if ((pConfig->vtable->flags & MA_NODE_FLAG_PASSTHROUGH) != 0) { - if ((pConfig->vtable->inputBusCount != 0 && pConfig->vtable->inputBusCount != 1) || pConfig->vtable->outputBusCount != 1) { - return MA_INVALID_ARGS; /* Passthrough nodes must have exactly 1 output bus and either 0 or 1 input bus. */ - } - - if (pConfig->pInputChannels[0] != pConfig->pOutputChannels[0]) { - return MA_INVALID_ARGS; /* Passthrough nodes must have the same number of channels between input and output nodes. */ - } - } - - - *pInputBusCount = inputBusCount; - *pOutputBusCount = outputBusCount; - - return MA_SUCCESS; -} - -static ma_result ma_node_get_heap_layout(ma_node_graph* pNodeGraph, const ma_node_config* pConfig, ma_node_heap_layout* pHeapLayout) -{ - ma_result result; - ma_uint32 inputBusCount; - ma_uint32 outputBusCount; - - MA_ASSERT(pHeapLayout != NULL); - - MA_ZERO_OBJECT(pHeapLayout); - - if (pConfig == NULL || pConfig->vtable == NULL || pConfig->vtable->onProcess == NULL) { - return MA_INVALID_ARGS; - } - - result = ma_node_translate_bus_counts(pConfig, &inputBusCount, &outputBusCount); - if (result != MA_SUCCESS) { - return result; - } - - pHeapLayout->sizeInBytes = 0; - - /* Input buses. */ - if (inputBusCount > MA_MAX_NODE_LOCAL_BUS_COUNT) { - pHeapLayout->inputBusOffset = pHeapLayout->sizeInBytes; - pHeapLayout->sizeInBytes += ma_align_64(sizeof(ma_node_input_bus) * inputBusCount); - } else { - pHeapLayout->inputBusOffset = MA_SIZE_MAX; /* MA_SIZE_MAX indicates that no heap allocation is required for the input bus. */ - } - - /* Output buses. */ - if (outputBusCount > MA_MAX_NODE_LOCAL_BUS_COUNT) { - pHeapLayout->outputBusOffset = pHeapLayout->sizeInBytes; - pHeapLayout->sizeInBytes += ma_align_64(sizeof(ma_node_output_bus) * outputBusCount); - } else { - pHeapLayout->outputBusOffset = MA_SIZE_MAX; - } - - /* - Cached audio data. - - We need to allocate memory for caching both input and output data. We have an optimization - where no caching is necessary for specific conditions: - - - The node has 0 inputs and 1 output. - - When a node meets the above conditions, no cache is allocated. - - The size choice for this buffer is a little bit finicky. We don't want to be too wasteful by - allocating too much, but at the same time we want it be large enough so that enough frames can - be processed for each call to ma_node_read_pcm_frames() so that it keeps things efficient. For - now I'm going with 10ms @ 48K which is 480 frames per bus. This is configurable at compile - time. It might also be worth investigating whether or not this can be configured at run time. - */ - if (inputBusCount == 0 && outputBusCount == 1) { - /* Fast path. No cache needed. */ - pHeapLayout->cachedDataOffset = MA_SIZE_MAX; - } else { - /* Slow path. Cache needed. */ - size_t cachedDataSizeInBytes = 0; - ma_uint32 cacheCapInFrames; - ma_uint32 iBus; - - /* The capacity of the cache is based on our callback processing size. */ - cacheCapInFrames = ma_node_config_get_cache_size_in_frames(pConfig, pNodeGraph); - - for (iBus = 0; iBus < inputBusCount; iBus += 1) { - cachedDataSizeInBytes += cacheCapInFrames * ma_get_bytes_per_frame(ma_format_f32, pConfig->pInputChannels[iBus]); - } - - for (iBus = 0; iBus < outputBusCount; iBus += 1) { - cachedDataSizeInBytes += cacheCapInFrames * ma_get_bytes_per_frame(ma_format_f32, pConfig->pOutputChannels[iBus]); - } - - pHeapLayout->cachedDataOffset = pHeapLayout->sizeInBytes; - pHeapLayout->sizeInBytes += ma_align_64(cachedDataSizeInBytes); - } - - - /* - Not technically part of the heap, but we can output the input and output bus counts so we can - avoid a redundant call to ma_node_translate_bus_counts(). - */ - pHeapLayout->inputBusCount = inputBusCount; - pHeapLayout->outputBusCount = outputBusCount; - - /* Make sure allocation size is aligned. */ - pHeapLayout->sizeInBytes = ma_align_64(pHeapLayout->sizeInBytes); - - return MA_SUCCESS; -} - -MA_API ma_result ma_node_get_heap_size(ma_node_graph* pNodeGraph, const ma_node_config* pConfig, size_t* pHeapSizeInBytes) -{ - ma_result result; - ma_node_heap_layout heapLayout; - - if (pHeapSizeInBytes == NULL) { - return MA_INVALID_ARGS; - } - - *pHeapSizeInBytes = 0; - - result = ma_node_get_heap_layout(pNodeGraph, pConfig, &heapLayout); - if (result != MA_SUCCESS) { - return result; - } - - *pHeapSizeInBytes = heapLayout.sizeInBytes; - - return MA_SUCCESS; -} - -MA_API ma_result ma_node_init_preallocated(ma_node_graph* pNodeGraph, const ma_node_config* pConfig, void* pHeap, ma_node* pNode) -{ - ma_node_base* pNodeBase = (ma_node_base*)pNode; - ma_result result; - ma_node_heap_layout heapLayout; - ma_uint32 iInputBus; - ma_uint32 iOutputBus; - - if (pNodeBase == NULL) { - return MA_INVALID_ARGS; - } - - MA_ZERO_OBJECT(pNodeBase); - - result = ma_node_get_heap_layout(pNodeGraph, pConfig, &heapLayout); - if (result != MA_SUCCESS) { - return result; - } - - pNodeBase->_pHeap = pHeap; - MA_ZERO_MEMORY(pHeap, heapLayout.sizeInBytes); - - pNodeBase->pNodeGraph = pNodeGraph; - pNodeBase->vtable = pConfig->vtable; - pNodeBase->state = pConfig->initialState; - pNodeBase->stateTimes[ma_node_state_started] = 0; - pNodeBase->stateTimes[ma_node_state_stopped] = (ma_uint64)(ma_int64)-1; /* Weird casting for VC6 compatibility. */ - pNodeBase->inputBusCount = heapLayout.inputBusCount; - pNodeBase->outputBusCount = heapLayout.outputBusCount; - - if (heapLayout.inputBusOffset != MA_SIZE_MAX) { - pNodeBase->pInputBuses = (ma_node_input_bus*)ma_offset_ptr(pHeap, heapLayout.inputBusOffset); - } else { - pNodeBase->pInputBuses = pNodeBase->_inputBuses; - } - - if (heapLayout.outputBusOffset != MA_SIZE_MAX) { - pNodeBase->pOutputBuses = (ma_node_output_bus*)ma_offset_ptr(pHeap, heapLayout.outputBusOffset); - } else { - pNodeBase->pOutputBuses = pNodeBase->_outputBuses; - } - - if (heapLayout.cachedDataOffset != MA_SIZE_MAX) { - pNodeBase->pCachedData = (float*)ma_offset_ptr(pHeap, heapLayout.cachedDataOffset); - pNodeBase->cachedDataCapInFramesPerBus = ma_node_config_get_cache_size_in_frames(pConfig, pNodeGraph); - } else { - pNodeBase->pCachedData = NULL; - } - - - /* We need to run an initialization step for each input and output bus. */ - for (iInputBus = 0; iInputBus < ma_node_get_input_bus_count(pNodeBase); iInputBus += 1) { - result = ma_node_input_bus_init(pConfig->pInputChannels[iInputBus], &pNodeBase->pInputBuses[iInputBus]); - if (result != MA_SUCCESS) { - return result; - } - } - - for (iOutputBus = 0; iOutputBus < ma_node_get_output_bus_count(pNodeBase); iOutputBus += 1) { - result = ma_node_output_bus_init(pNodeBase, iOutputBus, pConfig->pOutputChannels[iOutputBus], &pNodeBase->pOutputBuses[iOutputBus]); - if (result != MA_SUCCESS) { - return result; - } - } - - - /* The cached data needs to be initialized to silence (or a sine wave tone if we're debugging). */ - if (pNodeBase->pCachedData != NULL) { - ma_uint32 iBus; - - #if 1 /* Toggle this between 0 and 1 to turn debugging on or off. 1 = fill with a sine wave for debugging; 0 = fill with silence. */ - /* For safety we'll go ahead and default the buffer to silence. */ - for (iBus = 0; iBus < ma_node_get_input_bus_count(pNodeBase); iBus += 1) { - ma_silence_pcm_frames(ma_node_get_cached_input_ptr(pNode, iBus), pNodeBase->cachedDataCapInFramesPerBus, ma_format_f32, ma_node_input_bus_get_channels(&pNodeBase->pInputBuses[iBus])); - } - for (iBus = 0; iBus < ma_node_get_output_bus_count(pNodeBase); iBus += 1) { - ma_silence_pcm_frames(ma_node_get_cached_output_ptr(pNode, iBus), pNodeBase->cachedDataCapInFramesPerBus, ma_format_f32, ma_node_output_bus_get_channels(&pNodeBase->pOutputBuses[iBus])); - } - #else - /* For debugging. Default to a sine wave. */ - for (iBus = 0; iBus < ma_node_get_input_bus_count(pNodeBase); iBus += 1) { - ma_debug_fill_pcm_frames_with_sine_wave(ma_node_get_cached_input_ptr(pNode, iBus), pNodeBase->cachedDataCapInFramesPerBus, ma_format_f32, ma_node_input_bus_get_channels(&pNodeBase->pInputBuses[iBus]), 48000); - } - for (iBus = 0; iBus < ma_node_get_output_bus_count(pNodeBase); iBus += 1) { - ma_debug_fill_pcm_frames_with_sine_wave(ma_node_get_cached_output_ptr(pNode, iBus), pNodeBase->cachedDataCapInFramesPerBus, ma_format_f32, ma_node_output_bus_get_channels(&pNodeBase->pOutputBuses[iBus]), 48000); - } - #endif - } - - return MA_SUCCESS; -} - -MA_API ma_result ma_node_init(ma_node_graph* pNodeGraph, const ma_node_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_node* pNode) -{ - ma_result result; - size_t heapSizeInBytes; - void* pHeap; - - result = ma_node_get_heap_size(pNodeGraph, pConfig, &heapSizeInBytes); - if (result != MA_SUCCESS) { - return result; - } - - if (heapSizeInBytes > 0) { - pHeap = ma_malloc(heapSizeInBytes, pAllocationCallbacks); - if (pHeap == NULL) { - return MA_OUT_OF_MEMORY; - } - } else { - pHeap = NULL; - } - - result = ma_node_init_preallocated(pNodeGraph, pConfig, pHeap, pNode); - if (result != MA_SUCCESS) { - ma_free(pHeap, pAllocationCallbacks); - return result; - } - - ((ma_node_base*)pNode)->_ownsHeap = MA_TRUE; - return MA_SUCCESS; -} - -MA_API void ma_node_uninit(ma_node* pNode, const ma_allocation_callbacks* pAllocationCallbacks) -{ - ma_node_base* pNodeBase = (ma_node_base*)pNode; - - if (pNodeBase == NULL) { - return; - } - - /* - The first thing we need to do is fully detach the node. This will detach all inputs and - outputs. We need to do this first because it will sever the connection with the node graph and - allow us to complete uninitialization without needing to worry about thread-safety with the - audio thread. The detachment process will wait for any local processing of the node to finish. - */ - ma_node_detach_full(pNode); - - /* - At this point the node should be completely unreferenced by the node graph and we can finish up - the uninitialization process without needing to worry about thread-safety. - */ - if (pNodeBase->_ownsHeap) { - ma_free(pNodeBase->_pHeap, pAllocationCallbacks); - } -} - -MA_API ma_node_graph* ma_node_get_node_graph(const ma_node* pNode) -{ - if (pNode == NULL) { - return NULL; - } - - return ((const ma_node_base*)pNode)->pNodeGraph; -} - -MA_API ma_uint32 ma_node_get_input_bus_count(const ma_node* pNode) -{ - if (pNode == NULL) { - return 0; - } - - return ((ma_node_base*)pNode)->inputBusCount; -} - -MA_API ma_uint32 ma_node_get_output_bus_count(const ma_node* pNode) -{ - if (pNode == NULL) { - return 0; - } - - return ((ma_node_base*)pNode)->outputBusCount; -} - - -MA_API ma_uint32 ma_node_get_input_channels(const ma_node* pNode, ma_uint32 inputBusIndex) -{ - const ma_node_base* pNodeBase = (const ma_node_base*)pNode; - - if (pNode == NULL) { - return 0; - } - - if (inputBusIndex >= ma_node_get_input_bus_count(pNode)) { - return 0; /* Invalid bus index. */ - } - - return ma_node_input_bus_get_channels(&pNodeBase->pInputBuses[inputBusIndex]); -} - -MA_API ma_uint32 ma_node_get_output_channels(const ma_node* pNode, ma_uint32 outputBusIndex) -{ - const ma_node_base* pNodeBase = (const ma_node_base*)pNode; - - if (pNode == NULL) { - return 0; - } - - if (outputBusIndex >= ma_node_get_output_bus_count(pNode)) { - return 0; /* Invalid bus index. */ - } - - return ma_node_output_bus_get_channels(&pNodeBase->pOutputBuses[outputBusIndex]); -} - - -static ma_result ma_node_detach_full(ma_node* pNode) -{ - ma_node_base* pNodeBase = (ma_node_base*)pNode; - ma_uint32 iInputBus; - - if (pNodeBase == NULL) { - return MA_INVALID_ARGS; - } - - /* - Make sure the node is completely detached first. This will not return until the output bus is - guaranteed to no longer be referenced by the audio thread. - */ - ma_node_detach_all_output_buses(pNode); - - /* - At this point all output buses will have been detached from the graph and we can be guaranteed - that none of its input nodes will be getting processed by the graph. We can detach these - without needing to worry about the audio thread touching them. - */ - for (iInputBus = 0; iInputBus < ma_node_get_input_bus_count(pNode); iInputBus += 1) { - ma_node_input_bus* pInputBus; - ma_node_output_bus* pOutputBus; - - pInputBus = &pNodeBase->pInputBuses[iInputBus]; - - /* - This is important. We cannot be using ma_node_input_bus_first() or ma_node_input_bus_next(). Those - functions are specifically for the audio thread. We'll instead just manually iterate using standard - linked list logic. We don't need to worry about the audio thread referencing these because the step - above severed the connection to the graph. - */ - for (pOutputBus = (ma_node_output_bus*)ma_atomic_load_ptr(&pInputBus->head.pNext); pOutputBus != NULL; pOutputBus = (ma_node_output_bus*)ma_atomic_load_ptr(&pInputBus->head.pNext)) { - ma_node_detach_output_bus(pOutputBus->pNode, pOutputBus->outputBusIndex); /* This won't do any waiting in practice and should be efficient. */ - } - } - - return MA_SUCCESS; -} - -MA_API ma_result ma_node_detach_output_bus(ma_node* pNode, ma_uint32 outputBusIndex) -{ - ma_result result = MA_SUCCESS; - ma_node_base* pNodeBase = (ma_node_base*)pNode; - ma_node_base* pInputNodeBase; - - if (pNode == NULL) { - return MA_INVALID_ARGS; - } - - if (outputBusIndex >= ma_node_get_output_bus_count(pNode)) { - return MA_INVALID_ARGS; /* Invalid output bus index. */ - } - - /* We need to lock the output bus because we need to inspect the input node and grab its input bus. */ - ma_node_output_bus_lock(&pNodeBase->pOutputBuses[outputBusIndex]); - { - pInputNodeBase = (ma_node_base*)pNodeBase->pOutputBuses[outputBusIndex].pInputNode; - if (pInputNodeBase != NULL) { - ma_node_input_bus_detach__no_output_bus_lock(&pInputNodeBase->pInputBuses[pNodeBase->pOutputBuses[outputBusIndex].inputNodeInputBusIndex], &pNodeBase->pOutputBuses[outputBusIndex]); - } - } - ma_node_output_bus_unlock(&pNodeBase->pOutputBuses[outputBusIndex]); - - return result; -} - -MA_API ma_result ma_node_detach_all_output_buses(ma_node* pNode) -{ - ma_uint32 iOutputBus; - - if (pNode == NULL) { - return MA_INVALID_ARGS; - } - - for (iOutputBus = 0; iOutputBus < ma_node_get_output_bus_count(pNode); iOutputBus += 1) { - ma_node_detach_output_bus(pNode, iOutputBus); - } - - return MA_SUCCESS; -} - -MA_API ma_result ma_node_attach_output_bus(ma_node* pNode, ma_uint32 outputBusIndex, ma_node* pOtherNode, ma_uint32 otherNodeInputBusIndex) -{ - ma_node_base* pNodeBase = (ma_node_base*)pNode; - ma_node_base* pOtherNodeBase = (ma_node_base*)pOtherNode; - - if (pNodeBase == NULL || pOtherNodeBase == NULL) { - return MA_INVALID_ARGS; - } - - if (pNodeBase == pOtherNodeBase) { - return MA_INVALID_OPERATION; /* Cannot attach a node to itself. */ - } - - if (outputBusIndex >= ma_node_get_output_bus_count(pNode) || otherNodeInputBusIndex >= ma_node_get_input_bus_count(pOtherNode)) { - return MA_INVALID_OPERATION; /* Invalid bus index. */ - } - - /* The output channel count of the output node must be the same as the input channel count of the input node. */ - if (ma_node_get_output_channels(pNode, outputBusIndex) != ma_node_get_input_channels(pOtherNode, otherNodeInputBusIndex)) { - return MA_INVALID_OPERATION; /* Channel count is incompatible. */ - } - - /* This will deal with detaching if the output bus is already attached to something. */ - ma_node_input_bus_attach(&pOtherNodeBase->pInputBuses[otherNodeInputBusIndex], &pNodeBase->pOutputBuses[outputBusIndex], pOtherNode, otherNodeInputBusIndex); - - return MA_SUCCESS; -} - -MA_API ma_result ma_node_set_output_bus_volume(ma_node* pNode, ma_uint32 outputBusIndex, float volume) -{ - ma_node_base* pNodeBase = (ma_node_base*)pNode; - - if (pNodeBase == NULL) { - return MA_INVALID_ARGS; - } - - if (outputBusIndex >= ma_node_get_output_bus_count(pNode)) { - return MA_INVALID_ARGS; /* Invalid bus index. */ - } - - return ma_node_output_bus_set_volume(&pNodeBase->pOutputBuses[outputBusIndex], volume); -} - -MA_API float ma_node_get_output_bus_volume(const ma_node* pNode, ma_uint32 outputBusIndex) -{ - const ma_node_base* pNodeBase = (const ma_node_base*)pNode; - - if (pNodeBase == NULL) { - return 0; - } - - if (outputBusIndex >= ma_node_get_output_bus_count(pNode)) { - return 0; /* Invalid bus index. */ - } - - return ma_node_output_bus_get_volume(&pNodeBase->pOutputBuses[outputBusIndex]); -} - -MA_API ma_result ma_node_set_state(ma_node* pNode, ma_node_state state) -{ - ma_node_base* pNodeBase = (ma_node_base*)pNode; - - if (pNodeBase == NULL) { - return MA_INVALID_ARGS; - } - - ma_atomic_exchange_i32(&pNodeBase->state, state); - - return MA_SUCCESS; -} - -MA_API ma_node_state ma_node_get_state(const ma_node* pNode) -{ - const ma_node_base* pNodeBase = (const ma_node_base*)pNode; - - if (pNodeBase == NULL) { - return ma_node_state_stopped; - } - - return (ma_node_state)ma_atomic_load_i32(&pNodeBase->state); -} - -MA_API ma_result ma_node_set_state_time(ma_node* pNode, ma_node_state state, ma_uint64 globalTime) -{ - if (pNode == NULL) { - return MA_INVALID_ARGS; - } - - /* Validation check for safety since we'll be using this as an index into stateTimes[]. */ - if (state != ma_node_state_started && state != ma_node_state_stopped) { - return MA_INVALID_ARGS; - } - - ma_atomic_exchange_64(&((ma_node_base*)pNode)->stateTimes[state], globalTime); - - return MA_SUCCESS; -} - -MA_API ma_uint64 ma_node_get_state_time(const ma_node* pNode, ma_node_state state) -{ - if (pNode == NULL) { - return 0; - } - - /* Validation check for safety since we'll be using this as an index into stateTimes[]. */ - if (state != ma_node_state_started && state != ma_node_state_stopped) { - return 0; - } - - return ma_atomic_load_64(&((ma_node_base*)pNode)->stateTimes[state]); -} - -MA_API ma_node_state ma_node_get_state_by_time(const ma_node* pNode, ma_uint64 globalTime) -{ - if (pNode == NULL) { - return ma_node_state_stopped; - } - - return ma_node_get_state_by_time_range(pNode, globalTime, globalTime); -} - -MA_API ma_node_state ma_node_get_state_by_time_range(const ma_node* pNode, ma_uint64 globalTimeBeg, ma_uint64 globalTimeEnd) -{ - ma_node_state state; - - if (pNode == NULL) { - return ma_node_state_stopped; - } - - state = ma_node_get_state(pNode); - - /* An explicitly stopped node is always stopped. */ - if (state == ma_node_state_stopped) { - return ma_node_state_stopped; - } - - /* - Getting here means the node is marked as started, but it may still not be truly started due to - its start time not having been reached yet. Also, the stop time may have also been reached in - which case it'll be considered stopped. - */ - if (ma_node_get_state_time(pNode, ma_node_state_started) > globalTimeBeg) { - return ma_node_state_stopped; /* Start time has not yet been reached. */ - } - - if (ma_node_get_state_time(pNode, ma_node_state_stopped) <= globalTimeEnd) { - return ma_node_state_stopped; /* Stop time has been reached. */ - } - - /* Getting here means the node is marked as started and is within its start/stop times. */ - return ma_node_state_started; -} - -MA_API ma_uint64 ma_node_get_time(const ma_node* pNode) -{ - if (pNode == NULL) { - return 0; - } - - return ma_atomic_load_64(&((ma_node_base*)pNode)->localTime); -} - -MA_API ma_result ma_node_set_time(ma_node* pNode, ma_uint64 localTime) -{ - if (pNode == NULL) { - return MA_INVALID_ARGS; - } - - ma_atomic_exchange_64(&((ma_node_base*)pNode)->localTime, localTime); - - return MA_SUCCESS; -} - - - -static void ma_node_process_pcm_frames_internal(ma_node* pNode, const float** ppFramesIn, ma_uint32* pFrameCountIn, float** ppFramesOut, ma_uint32* pFrameCountOut) -{ - ma_node_base* pNodeBase = (ma_node_base*)pNode; - - MA_ASSERT(pNode != NULL); - - if (pNodeBase->vtable->onProcess) { - pNodeBase->vtable->onProcess(pNode, ppFramesIn, pFrameCountIn, ppFramesOut, pFrameCountOut); - } -} - -static ma_result ma_node_read_pcm_frames(ma_node* pNode, ma_uint32 outputBusIndex, float* pFramesOut, ma_uint32 frameCount, ma_uint32* pFramesRead, ma_uint64 globalTime) -{ - ma_node_base* pNodeBase = (ma_node_base*)pNode; - ma_result result = MA_SUCCESS; - ma_uint32 iInputBus; - ma_uint32 iOutputBus; - ma_uint32 inputBusCount; - ma_uint32 outputBusCount; - ma_uint32 totalFramesRead = 0; - float* ppFramesIn[MA_MAX_NODE_BUS_COUNT]; - float* ppFramesOut[MA_MAX_NODE_BUS_COUNT]; - ma_uint64 globalTimeBeg; - ma_uint64 globalTimeEnd; - ma_uint64 startTime; - ma_uint64 stopTime; - ma_uint32 timeOffsetBeg; - ma_uint32 timeOffsetEnd; - ma_uint32 frameCountIn; - ma_uint32 frameCountOut; - - /* - pFramesRead is mandatory. It must be used to determine how many frames were read. It's normal and - expected that the number of frames read may be different to that requested. Therefore, the caller - must look at this value to correctly determine how many frames were read. - */ - MA_ASSERT(pFramesRead != NULL); /* <-- If you've triggered this assert, you're using this function wrong. You *must* use this variable and inspect it after the call returns. */ - if (pFramesRead == NULL) { - return MA_INVALID_ARGS; - } - - *pFramesRead = 0; /* Safety. */ - - if (pNodeBase == NULL) { - return MA_INVALID_ARGS; - } - - if (outputBusIndex >= ma_node_get_output_bus_count(pNodeBase)) { - return MA_INVALID_ARGS; /* Invalid output bus index. */ - } - - /* Don't do anything if we're in a stopped state. */ - if (ma_node_get_state_by_time_range(pNode, globalTime, globalTime + frameCount) != ma_node_state_started) { - return MA_SUCCESS; /* We're in a stopped state. This is not an error - we just need to not read anything. */ - } - - - globalTimeBeg = globalTime; - globalTimeEnd = globalTime + frameCount; - startTime = ma_node_get_state_time(pNode, ma_node_state_started); - stopTime = ma_node_get_state_time(pNode, ma_node_state_stopped); - - /* - At this point we know that we are inside our start/stop times. However, we may need to adjust - our frame count and output pointer to accommodate since we could be straddling the time period - that this function is getting called for. - - It's possible (and likely) that the start time does not line up with the output buffer. We - therefore need to offset it by a number of frames to accommodate. The same thing applies for - the stop time. - */ - timeOffsetBeg = (globalTimeBeg < startTime) ? (ma_uint32)(globalTimeEnd - startTime) : 0; - timeOffsetEnd = (globalTimeEnd > stopTime) ? (ma_uint32)(globalTimeEnd - stopTime) : 0; - - /* Trim based on the start offset. We need to silence the start of the buffer. */ - if (timeOffsetBeg > 0) { - ma_silence_pcm_frames(pFramesOut, timeOffsetBeg, ma_format_f32, ma_node_get_output_channels(pNode, outputBusIndex)); - pFramesOut += timeOffsetBeg * ma_node_get_output_channels(pNode, outputBusIndex); - frameCount -= timeOffsetBeg; - } - - /* Trim based on the end offset. We don't need to silence the tail section because we'll just have a reduced value written to pFramesRead. */ - if (timeOffsetEnd > 0) { - frameCount -= timeOffsetEnd; - } - - - /* We run on different paths depending on the bus counts. */ - inputBusCount = ma_node_get_input_bus_count(pNode); - outputBusCount = ma_node_get_output_bus_count(pNode); - - /* - Run a simplified path when there are no inputs and one output. In this case there's nothing to - actually read and we can go straight to output. This is a very common scenario because the vast - majority of data source nodes will use this setup so this optimization I think is worthwhile. - */ - if (inputBusCount == 0 && outputBusCount == 1) { - /* Fast path. No need to read from input and no need for any caching. */ - frameCountIn = 0; - frameCountOut = frameCount; /* Just read as much as we can. The callback will return what was actually read. */ - - ppFramesOut[0] = pFramesOut; - - /* - If it's a passthrough we won't be expecting the callback to output anything, so we'll - need to pre-silence the output buffer. - */ - if ((pNodeBase->vtable->flags & MA_NODE_FLAG_PASSTHROUGH) != 0) { - ma_silence_pcm_frames(pFramesOut, frameCount, ma_format_f32, ma_node_get_output_channels(pNode, outputBusIndex)); - } - - ma_node_process_pcm_frames_internal(pNode, NULL, &frameCountIn, ppFramesOut, &frameCountOut); - totalFramesRead = frameCountOut; - } else { - /* Slow path. Need to read input data. */ - if ((pNodeBase->vtable->flags & MA_NODE_FLAG_PASSTHROUGH) != 0) { - /* - Fast path. We're running a passthrough. We need to read directly into the output buffer, but - still fire the callback so that event handling and trigger nodes can do their thing. Since - it's a passthrough there's no need for any kind of caching logic. - */ - MA_ASSERT(outputBusCount == inputBusCount); - MA_ASSERT(outputBusCount == 1); - MA_ASSERT(outputBusIndex == 0); - - /* We just read directly from input bus to output buffer, and then afterwards fire the callback. */ - ppFramesOut[0] = pFramesOut; - ppFramesIn[0] = ppFramesOut[0]; - - result = ma_node_input_bus_read_pcm_frames(pNodeBase, &pNodeBase->pInputBuses[0], ppFramesIn[0], frameCount, &totalFramesRead, globalTime); - if (result == MA_SUCCESS) { - /* Even though it's a passthrough, we still need to fire the callback. */ - frameCountIn = totalFramesRead; - frameCountOut = totalFramesRead; - - if (totalFramesRead > 0) { - ma_node_process_pcm_frames_internal(pNode, (const float**)ppFramesIn, &frameCountIn, ppFramesOut, &frameCountOut); /* From GCC: expected 'const float **' but argument is of type 'float **'. Shouldn't this be implicit? Explicit cast to silence the warning. */ - } - - /* - A passthrough should never have modified the input and output frame counts. If you're - triggering these asserts you need to fix your processing callback. - */ - MA_ASSERT(frameCountIn == totalFramesRead); - MA_ASSERT(frameCountOut == totalFramesRead); - } - } else { - /* Slow path. Need to do caching. */ - ma_uint32 framesToProcessIn; - ma_uint32 framesToProcessOut; - ma_bool32 consumeNullInput = MA_FALSE; - - /* - We use frameCount as a basis for the number of frames to read since that's what's being - requested, however we still need to clamp it to whatever can fit in the cache. - - This will also be used as the basis for determining how many input frames to read. This is - not ideal because it can result in too many input frames being read which introduces latency. - To solve this, nodes can implement an optional callback called onGetRequiredInputFrameCount - which is used as hint to miniaudio as to how many input frames it needs to read at a time. This - callback is completely optional, and if it's not set, miniaudio will assume `frameCount`. - - This function will be called multiple times for each period of time, once for each output node. - We cannot read from each input node each time this function is called. Instead we need to check - whether or not this is first output bus to be read from for this time period, and if so, read - from our input data. - - To determine whether or not we're ready to read data, we check a flag. There will be one flag - for each output. When the flag is set, it means data has been read previously and that we're - ready to advance time forward for our input nodes by reading fresh data. - */ - framesToProcessOut = frameCount; - if (framesToProcessOut > pNodeBase->cachedDataCapInFramesPerBus) { - framesToProcessOut = pNodeBase->cachedDataCapInFramesPerBus; - } - - framesToProcessIn = frameCount; - if (pNodeBase->vtable->onGetRequiredInputFrameCount) { - pNodeBase->vtable->onGetRequiredInputFrameCount(pNode, framesToProcessOut, &framesToProcessIn); /* <-- It does not matter if this fails. */ - } - if (framesToProcessIn > pNodeBase->cachedDataCapInFramesPerBus) { - framesToProcessIn = pNodeBase->cachedDataCapInFramesPerBus; - } - - - MA_ASSERT(framesToProcessIn <= 0xFFFF); - MA_ASSERT(framesToProcessOut <= 0xFFFF); - - if (ma_node_output_bus_has_read(&pNodeBase->pOutputBuses[outputBusIndex])) { - /* Getting here means we need to do another round of processing. */ - pNodeBase->cachedFrameCountOut = 0; - - for (;;) { - frameCountOut = 0; - - /* - We need to prepare our output frame pointers for processing. In the same iteration we need - to mark every output bus as unread so that future calls to this function for different buses - for the current time period don't pull in data when they should instead be reading from cache. - */ - for (iOutputBus = 0; iOutputBus < outputBusCount; iOutputBus += 1) { - ma_node_output_bus_set_has_read(&pNodeBase->pOutputBuses[iOutputBus], MA_FALSE); /* <-- This is what tells the next calls to this function for other output buses for this time period to read from cache instead of pulling in more data. */ - ppFramesOut[iOutputBus] = ma_node_get_cached_output_ptr(pNode, iOutputBus); - } - - /* We only need to read from input buses if there isn't already some data in the cache. */ - if (pNodeBase->cachedFrameCountIn == 0) { - ma_uint32 maxFramesReadIn = 0; - - /* Here is where we pull in data from the input buses. This is what will trigger an advance in time. */ - for (iInputBus = 0; iInputBus < inputBusCount; iInputBus += 1) { - ma_uint32 framesRead; - - /* The first thing to do is get the offset within our bulk allocation to store this input data. */ - ppFramesIn[iInputBus] = ma_node_get_cached_input_ptr(pNode, iInputBus); - - /* Once we've determined our destination pointer we can read. Note that we must inspect the number of frames read and fill any leftovers with silence for safety. */ - result = ma_node_input_bus_read_pcm_frames(pNodeBase, &pNodeBase->pInputBuses[iInputBus], ppFramesIn[iInputBus], framesToProcessIn, &framesRead, globalTime); - if (result != MA_SUCCESS) { - /* It doesn't really matter if we fail because we'll just fill with silence. */ - framesRead = 0; /* Just for safety, but I don't think it's really needed. */ - } - - /* TODO: Minor optimization opportunity here. If no frames were read and the buffer is already filled with silence, no need to re-silence it. */ - /* Any leftover frames need to silenced for safety. */ - if (framesRead < framesToProcessIn) { - ma_silence_pcm_frames(ppFramesIn[iInputBus] + (framesRead * ma_node_get_input_channels(pNodeBase, iInputBus)), (framesToProcessIn - framesRead), ma_format_f32, ma_node_get_input_channels(pNodeBase, iInputBus)); - } - - maxFramesReadIn = ma_max(maxFramesReadIn, framesRead); - } - - /* This was a fresh load of input data so reset our consumption counter. */ - pNodeBase->consumedFrameCountIn = 0; - - /* - We don't want to keep processing if there's nothing to process, so set the number of cached - input frames to the maximum number we read from each attachment (the lesser will be padded - with silence). If we didn't read anything, this will be set to 0 and the entire buffer will - have been assigned to silence. This being equal to 0 is an important property for us because - it allows us to detect when NULL can be passed into the processing callback for the input - buffer for the purpose of continuous processing. - */ - pNodeBase->cachedFrameCountIn = (ma_uint16)maxFramesReadIn; - } else { - /* We don't need to read anything, but we do need to prepare our input frame pointers. */ - for (iInputBus = 0; iInputBus < inputBusCount; iInputBus += 1) { - ppFramesIn[iInputBus] = ma_node_get_cached_input_ptr(pNode, iInputBus) + (pNodeBase->consumedFrameCountIn * ma_node_get_input_channels(pNodeBase, iInputBus)); - } - } - - /* - At this point we have our input data so now we need to do some processing. Sneaky little - optimization here - we can set the pointer to the output buffer for this output bus so - that the final copy into the output buffer is done directly by onProcess(). - */ - if (pFramesOut != NULL) { - ppFramesOut[outputBusIndex] = ma_offset_pcm_frames_ptr_f32(pFramesOut, pNodeBase->cachedFrameCountOut, ma_node_get_output_channels(pNode, outputBusIndex)); - } - - - /* Give the processing function the entire capacity of the output buffer. */ - frameCountOut = (framesToProcessOut - pNodeBase->cachedFrameCountOut); - - /* - We need to treat nodes with continuous processing a little differently. For these ones, - we always want to fire the callback with the requested number of frames, regardless of - pNodeBase->cachedFrameCountIn, which could be 0. Also, we want to check if we can pass - in NULL for the input buffer to the callback. - */ - if ((pNodeBase->vtable->flags & MA_NODE_FLAG_CONTINUOUS_PROCESSING) != 0) { - /* We're using continuous processing. Make sure we specify the whole frame count at all times. */ - frameCountIn = framesToProcessIn; /* Give the processing function as much input data as we've got in the buffer, including any silenced padding from short reads. */ - - if ((pNodeBase->vtable->flags & MA_NODE_FLAG_ALLOW_NULL_INPUT) != 0 && pNodeBase->consumedFrameCountIn == 0 && pNodeBase->cachedFrameCountIn == 0) { - consumeNullInput = MA_TRUE; - } else { - consumeNullInput = MA_FALSE; - } - - /* - Since we're using continuous processing we're always passing in a full frame count - regardless of how much input data was read. If this is greater than what we read as - input, we'll end up with an underflow. We instead need to make sure our cached frame - count is set to the number of frames we'll be passing to the data callback. Not - doing this will result in an underflow when we "consume" the cached data later on. - - Note that this check needs to be done after the "consumeNullInput" check above because - we use the property of cachedFrameCountIn being 0 to determine whether or not we - should be passing in a null pointer to the processing callback for when the node is - configured with MA_NODE_FLAG_ALLOW_NULL_INPUT. - */ - if (pNodeBase->cachedFrameCountIn < (ma_uint16)frameCountIn) { - pNodeBase->cachedFrameCountIn = (ma_uint16)frameCountIn; - } - } else { - frameCountIn = pNodeBase->cachedFrameCountIn; /* Give the processing function as much valid input data as we've got. */ - consumeNullInput = MA_FALSE; - } - - /* - Process data slightly differently depending on whether or not we're consuming NULL - input (checked just above). - */ - if (consumeNullInput) { - ma_node_process_pcm_frames_internal(pNode, NULL, &frameCountIn, ppFramesOut, &frameCountOut); - } else { - /* - We want to skip processing if there's no input data, but we can only do that safely if - we know that there is no chance of any output frames being produced. If continuous - processing is being used, this won't be a problem because the input frame count will - always be non-0. However, if continuous processing is *not* enabled and input and output - data is processed at different rates, we still need to process that last input frame - because there could be a few excess output frames needing to be produced from cached - data. The `MA_NODE_FLAG_DIFFERENT_PROCESSING_RATES` flag is used as the indicator for - determining whether or not we need to process the node even when there are no input - frames available right now. - */ - if (frameCountIn > 0 || (pNodeBase->vtable->flags & MA_NODE_FLAG_DIFFERENT_PROCESSING_RATES) != 0) { - ma_node_process_pcm_frames_internal(pNode, (const float**)ppFramesIn, &frameCountIn, ppFramesOut, &frameCountOut); /* From GCC: expected 'const float **' but argument is of type 'float **'. Shouldn't this be implicit? Explicit cast to silence the warning. */ - } else { - frameCountOut = 0; /* No data was processed. */ - } - } - - /* - Thanks to our sneaky optimization above we don't need to do any data copying directly into - the output buffer - the onProcess() callback just did that for us. We do, however, need to - apply the number of input and output frames that were processed. Note that due to continuous - processing above, we need to do explicit checks here. If we just consumed a NULL input - buffer it means that no actual input data was processed from the internal buffers and we - don't want to be modifying any counters. - */ - if (consumeNullInput == MA_FALSE) { - pNodeBase->consumedFrameCountIn += (ma_uint16)frameCountIn; - pNodeBase->cachedFrameCountIn -= (ma_uint16)frameCountIn; - } - - /* The cached output frame count is always equal to what we just read. */ - pNodeBase->cachedFrameCountOut += (ma_uint16)frameCountOut; - - /* If we couldn't process any data, we're done. The loop needs to be terminated here or else we'll get stuck in a loop. */ - if (pNodeBase->cachedFrameCountOut == framesToProcessOut || (frameCountOut == 0 && frameCountIn == 0)) { - break; - } - } - } else { - /* - We're not needing to read anything from the input buffer so just read directly from our - already-processed data. - */ - if (pFramesOut != NULL) { - ma_copy_pcm_frames(pFramesOut, ma_node_get_cached_output_ptr(pNodeBase, outputBusIndex), pNodeBase->cachedFrameCountOut, ma_format_f32, ma_node_get_output_channels(pNodeBase, outputBusIndex)); - } - } - - /* The number of frames read is always equal to the number of cached output frames. */ - totalFramesRead = pNodeBase->cachedFrameCountOut; - - /* Now that we've read the data, make sure our read flag is set. */ - ma_node_output_bus_set_has_read(&pNodeBase->pOutputBuses[outputBusIndex], MA_TRUE); - } - } - - /* Apply volume, if necessary. */ - ma_apply_volume_factor_f32(pFramesOut, totalFramesRead * ma_node_get_output_channels(pNodeBase, outputBusIndex), ma_node_output_bus_get_volume(&pNodeBase->pOutputBuses[outputBusIndex])); - - /* Advance our local time forward. */ - ma_atomic_fetch_add_64(&pNodeBase->localTime, (ma_uint64)totalFramesRead); - - *pFramesRead = totalFramesRead + timeOffsetBeg; /* Must include the silenced section at the start of the buffer. */ - return result; -} - - - - -/* Data source node. */ -MA_API ma_data_source_node_config ma_data_source_node_config_init(ma_data_source* pDataSource) -{ - ma_data_source_node_config config; - - MA_ZERO_OBJECT(&config); - config.nodeConfig = ma_node_config_init(); - config.pDataSource = pDataSource; - - return config; -} - - -static void ma_data_source_node_process_pcm_frames(ma_node* pNode, const float** ppFramesIn, ma_uint32* pFrameCountIn, float** ppFramesOut, ma_uint32* pFrameCountOut) -{ - ma_data_source_node* pDataSourceNode = (ma_data_source_node*)pNode; - ma_format format; - ma_uint32 channels; - ma_uint32 frameCount; - ma_uint64 framesRead = 0; - - MA_ASSERT(pDataSourceNode != NULL); - MA_ASSERT(pDataSourceNode->pDataSource != NULL); - MA_ASSERT(ma_node_get_input_bus_count(pDataSourceNode) == 0); - MA_ASSERT(ma_node_get_output_bus_count(pDataSourceNode) == 1); - - /* We don't want to read from ppFramesIn at all. Instead we read from the data source. */ - (void)ppFramesIn; - (void)pFrameCountIn; - - frameCount = *pFrameCountOut; - - /* miniaudio should never be calling this with a frame count of zero. */ - MA_ASSERT(frameCount > 0); - - if (ma_data_source_get_data_format(pDataSourceNode->pDataSource, &format, &channels, NULL, NULL, 0) == MA_SUCCESS) { /* <-- Don't care about sample rate here. */ - /* The node graph system requires samples be in floating point format. This is checked in ma_data_source_node_init(). */ - MA_ASSERT(format == ma_format_f32); - (void)format; /* Just to silence some static analysis tools. */ - - ma_data_source_read_pcm_frames(pDataSourceNode->pDataSource, ppFramesOut[0], frameCount, &framesRead); - } - - *pFrameCountOut = (ma_uint32)framesRead; -} - -static ma_node_vtable g_ma_data_source_node_vtable = -{ - ma_data_source_node_process_pcm_frames, - NULL, /* onGetRequiredInputFrameCount */ - 0, /* 0 input buses. */ - 1, /* 1 output bus. */ - 0 -}; - -MA_API ma_result ma_data_source_node_init(ma_node_graph* pNodeGraph, const ma_data_source_node_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_data_source_node* pDataSourceNode) -{ - ma_result result; - ma_format format; /* For validating the format, which must be ma_format_f32. */ - ma_uint32 channels; /* For specifying the channel count of the output bus. */ - ma_node_config baseConfig; - - if (pDataSourceNode == NULL) { - return MA_INVALID_ARGS; - } - - MA_ZERO_OBJECT(pDataSourceNode); - - if (pConfig == NULL) { - return MA_INVALID_ARGS; - } - - result = ma_data_source_get_data_format(pConfig->pDataSource, &format, &channels, NULL, NULL, 0); /* Don't care about sample rate. This will check pDataSource for NULL. */ - if (result != MA_SUCCESS) { - return result; - } - - MA_ASSERT(format == ma_format_f32); /* <-- If you've triggered this it means your data source is not outputting floating-point samples. You must configure your data source to use ma_format_f32. */ - if (format != ma_format_f32) { - return MA_INVALID_ARGS; /* Invalid format. */ - } - - /* The channel count is defined by the data source. If the caller has manually changed the channels we just ignore it. */ - baseConfig = pConfig->nodeConfig; - baseConfig.vtable = &g_ma_data_source_node_vtable; /* Explicitly set the vtable here to prevent callers from setting it incorrectly. */ - - /* - The channel count is defined by the data source. It is invalid for the caller to manually set - the channel counts in the config. `ma_data_source_node_config_init()` will have defaulted the - channel count pointer to NULL which is how it must remain. If you trigger any of these asserts - it means you're explicitly setting the channel count. Instead, configure the output channel - count of your data source to be the necessary channel count. - */ - if (baseConfig.pOutputChannels != NULL) { - return MA_INVALID_ARGS; - } - - baseConfig.pOutputChannels = &channels; - - result = ma_node_init(pNodeGraph, &baseConfig, pAllocationCallbacks, &pDataSourceNode->base); - if (result != MA_SUCCESS) { - return result; - } - - pDataSourceNode->pDataSource = pConfig->pDataSource; - - return MA_SUCCESS; -} - -MA_API void ma_data_source_node_uninit(ma_data_source_node* pDataSourceNode, const ma_allocation_callbacks* pAllocationCallbacks) -{ - ma_node_uninit(&pDataSourceNode->base, pAllocationCallbacks); -} - -MA_API ma_result ma_data_source_node_set_looping(ma_data_source_node* pDataSourceNode, ma_bool32 isLooping) -{ - if (pDataSourceNode == NULL) { - return MA_INVALID_ARGS; - } - - return ma_data_source_set_looping(pDataSourceNode->pDataSource, isLooping); -} - -MA_API ma_bool32 ma_data_source_node_is_looping(ma_data_source_node* pDataSourceNode) -{ - if (pDataSourceNode == NULL) { - return MA_FALSE; - } - - return ma_data_source_is_looping(pDataSourceNode->pDataSource); -} - - - -/* Splitter Node. */ -MA_API ma_splitter_node_config ma_splitter_node_config_init(ma_uint32 channels) -{ - ma_splitter_node_config config; - - MA_ZERO_OBJECT(&config); - config.nodeConfig = ma_node_config_init(); - config.channels = channels; - config.outputBusCount = 2; - - return config; -} - - -static void ma_splitter_node_process_pcm_frames(ma_node* pNode, const float** ppFramesIn, ma_uint32* pFrameCountIn, float** ppFramesOut, ma_uint32* pFrameCountOut) -{ - ma_node_base* pNodeBase = (ma_node_base*)pNode; - ma_uint32 iOutputBus; - ma_uint32 channels; - - MA_ASSERT(pNodeBase != NULL); - MA_ASSERT(ma_node_get_input_bus_count(pNodeBase) == 1); - - /* We don't need to consider the input frame count - it'll be the same as the output frame count and we process everything. */ - (void)pFrameCountIn; - - /* NOTE: This assumes the same number of channels for all inputs and outputs. This was checked in ma_splitter_node_init(). */ - channels = ma_node_get_input_channels(pNodeBase, 0); - - /* Splitting is just copying the first input bus and copying it over to each output bus. */ - for (iOutputBus = 0; iOutputBus < ma_node_get_output_bus_count(pNodeBase); iOutputBus += 1) { - ma_copy_pcm_frames(ppFramesOut[iOutputBus], ppFramesIn[0], *pFrameCountOut, ma_format_f32, channels); - } -} - -static ma_node_vtable g_ma_splitter_node_vtable = -{ - ma_splitter_node_process_pcm_frames, - NULL, /* onGetRequiredInputFrameCount */ - 1, /* 1 input bus. */ - MA_NODE_BUS_COUNT_UNKNOWN, /* The output bus count is specified on a per-node basis. */ - 0 -}; - -MA_API ma_result ma_splitter_node_init(ma_node_graph* pNodeGraph, const ma_splitter_node_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_splitter_node* pSplitterNode) -{ - ma_result result; - ma_node_config baseConfig; - ma_uint32 pInputChannels[1]; - ma_uint32 pOutputChannels[MA_MAX_NODE_BUS_COUNT]; - ma_uint32 iOutputBus; - - if (pSplitterNode == NULL) { - return MA_INVALID_ARGS; - } - - MA_ZERO_OBJECT(pSplitterNode); - - if (pConfig == NULL) { - return MA_INVALID_ARGS; - } - - if (pConfig->outputBusCount > MA_MAX_NODE_BUS_COUNT) { - return MA_INVALID_ARGS; /* Too many output buses. */ - } - - /* Splitters require the same number of channels between inputs and outputs. */ - pInputChannels[0] = pConfig->channels; - for (iOutputBus = 0; iOutputBus < pConfig->outputBusCount; iOutputBus += 1) { - pOutputChannels[iOutputBus] = pConfig->channels; - } - - baseConfig = pConfig->nodeConfig; - baseConfig.vtable = &g_ma_splitter_node_vtable; - baseConfig.pInputChannels = pInputChannels; - baseConfig.pOutputChannels = pOutputChannels; - baseConfig.outputBusCount = pConfig->outputBusCount; - - result = ma_node_init(pNodeGraph, &baseConfig, pAllocationCallbacks, &pSplitterNode->base); - if (result != MA_SUCCESS) { - return result; /* Failed to initialize the base node. */ - } - - return MA_SUCCESS; -} - -MA_API void ma_splitter_node_uninit(ma_splitter_node* pSplitterNode, const ma_allocation_callbacks* pAllocationCallbacks) -{ - ma_node_uninit(pSplitterNode, pAllocationCallbacks); -} - - -/* -Biquad Node -*/ -MA_API ma_biquad_node_config ma_biquad_node_config_init(ma_uint32 channels, float b0, float b1, float b2, float a0, float a1, float a2) -{ - ma_biquad_node_config config; - - config.nodeConfig = ma_node_config_init(); - config.biquad = ma_biquad_config_init(ma_format_f32, channels, b0, b1, b2, a0, a1, a2); - - return config; -} - -static void ma_biquad_node_process_pcm_frames(ma_node* pNode, const float** ppFramesIn, ma_uint32* pFrameCountIn, float** ppFramesOut, ma_uint32* pFrameCountOut) -{ - ma_biquad_node* pLPFNode = (ma_biquad_node*)pNode; - - MA_ASSERT(pNode != NULL); - (void)pFrameCountIn; - - ma_biquad_process_pcm_frames(&pLPFNode->biquad, ppFramesOut[0], ppFramesIn[0], *pFrameCountOut); -} - -static ma_node_vtable g_ma_biquad_node_vtable = -{ - ma_biquad_node_process_pcm_frames, - NULL, /* onGetRequiredInputFrameCount */ - 1, /* One input. */ - 1, /* One output. */ - 0 /* Default flags. */ -}; - -MA_API ma_result ma_biquad_node_init(ma_node_graph* pNodeGraph, const ma_biquad_node_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_biquad_node* pNode) -{ - ma_result result; - ma_node_config baseNodeConfig; - - if (pNode == NULL) { - return MA_INVALID_ARGS; - } - - MA_ZERO_OBJECT(pNode); - - if (pConfig == NULL) { - return MA_INVALID_ARGS; - } - - if (pConfig->biquad.format != ma_format_f32) { - return MA_INVALID_ARGS; /* The format must be f32. */ - } - - result = ma_biquad_init(&pConfig->biquad, pAllocationCallbacks, &pNode->biquad); - if (result != MA_SUCCESS) { - return result; - } - - baseNodeConfig = ma_node_config_init(); - baseNodeConfig.vtable = &g_ma_biquad_node_vtable; - baseNodeConfig.pInputChannels = &pConfig->biquad.channels; - baseNodeConfig.pOutputChannels = &pConfig->biquad.channels; - - result = ma_node_init(pNodeGraph, &baseNodeConfig, pAllocationCallbacks, pNode); - if (result != MA_SUCCESS) { - return result; - } - - return result; -} - -MA_API ma_result ma_biquad_node_reinit(const ma_biquad_config* pConfig, ma_biquad_node* pNode) -{ - ma_biquad_node* pLPFNode = (ma_biquad_node*)pNode; - - MA_ASSERT(pNode != NULL); - - return ma_biquad_reinit(pConfig, &pLPFNode->biquad); -} - -MA_API void ma_biquad_node_uninit(ma_biquad_node* pNode, const ma_allocation_callbacks* pAllocationCallbacks) -{ - ma_biquad_node* pLPFNode = (ma_biquad_node*)pNode; - - if (pNode == NULL) { - return; - } - - ma_node_uninit(pNode, pAllocationCallbacks); - ma_biquad_uninit(&pLPFNode->biquad, pAllocationCallbacks); -} - - - -/* -Low Pass Filter Node -*/ -MA_API ma_lpf_node_config ma_lpf_node_config_init(ma_uint32 channels, ma_uint32 sampleRate, double cutoffFrequency, ma_uint32 order) -{ - ma_lpf_node_config config; - - config.nodeConfig = ma_node_config_init(); - config.lpf = ma_lpf_config_init(ma_format_f32, channels, sampleRate, cutoffFrequency, order); - - return config; -} - -static void ma_lpf_node_process_pcm_frames(ma_node* pNode, const float** ppFramesIn, ma_uint32* pFrameCountIn, float** ppFramesOut, ma_uint32* pFrameCountOut) -{ - ma_lpf_node* pLPFNode = (ma_lpf_node*)pNode; - - MA_ASSERT(pNode != NULL); - (void)pFrameCountIn; - - ma_lpf_process_pcm_frames(&pLPFNode->lpf, ppFramesOut[0], ppFramesIn[0], *pFrameCountOut); -} - -static ma_node_vtable g_ma_lpf_node_vtable = -{ - ma_lpf_node_process_pcm_frames, - NULL, /* onGetRequiredInputFrameCount */ - 1, /* One input. */ - 1, /* One output. */ - 0 /* Default flags. */ -}; - -MA_API ma_result ma_lpf_node_init(ma_node_graph* pNodeGraph, const ma_lpf_node_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_lpf_node* pNode) -{ - ma_result result; - ma_node_config baseNodeConfig; - - if (pNode == NULL) { - return MA_INVALID_ARGS; - } - - MA_ZERO_OBJECT(pNode); - - if (pConfig == NULL) { - return MA_INVALID_ARGS; - } - - if (pConfig->lpf.format != ma_format_f32) { - return MA_INVALID_ARGS; /* The format must be f32. */ - } - - result = ma_lpf_init(&pConfig->lpf, pAllocationCallbacks, &pNode->lpf); - if (result != MA_SUCCESS) { - return result; - } - - baseNodeConfig = ma_node_config_init(); - baseNodeConfig.vtable = &g_ma_lpf_node_vtable; - baseNodeConfig.pInputChannels = &pConfig->lpf.channels; - baseNodeConfig.pOutputChannels = &pConfig->lpf.channels; - - result = ma_node_init(pNodeGraph, &baseNodeConfig, pAllocationCallbacks, pNode); - if (result != MA_SUCCESS) { - return result; - } - - return result; -} - -MA_API ma_result ma_lpf_node_reinit(const ma_lpf_config* pConfig, ma_lpf_node* pNode) -{ - ma_lpf_node* pLPFNode = (ma_lpf_node*)pNode; - - if (pNode == NULL) { - return MA_INVALID_ARGS; - } - - return ma_lpf_reinit(pConfig, &pLPFNode->lpf); -} - -MA_API void ma_lpf_node_uninit(ma_lpf_node* pNode, const ma_allocation_callbacks* pAllocationCallbacks) -{ - ma_lpf_node* pLPFNode = (ma_lpf_node*)pNode; - - if (pNode == NULL) { - return; - } - - ma_node_uninit(pNode, pAllocationCallbacks); - ma_lpf_uninit(&pLPFNode->lpf, pAllocationCallbacks); -} - - - -/* -High Pass Filter Node -*/ -MA_API ma_hpf_node_config ma_hpf_node_config_init(ma_uint32 channels, ma_uint32 sampleRate, double cutoffFrequency, ma_uint32 order) -{ - ma_hpf_node_config config; - - config.nodeConfig = ma_node_config_init(); - config.hpf = ma_hpf_config_init(ma_format_f32, channels, sampleRate, cutoffFrequency, order); - - return config; -} - -static void ma_hpf_node_process_pcm_frames(ma_node* pNode, const float** ppFramesIn, ma_uint32* pFrameCountIn, float** ppFramesOut, ma_uint32* pFrameCountOut) -{ - ma_hpf_node* pHPFNode = (ma_hpf_node*)pNode; - - MA_ASSERT(pNode != NULL); - (void)pFrameCountIn; - - ma_hpf_process_pcm_frames(&pHPFNode->hpf, ppFramesOut[0], ppFramesIn[0], *pFrameCountOut); -} - -static ma_node_vtable g_ma_hpf_node_vtable = -{ - ma_hpf_node_process_pcm_frames, - NULL, /* onGetRequiredInputFrameCount */ - 1, /* One input. */ - 1, /* One output. */ - 0 /* Default flags. */ -}; - -MA_API ma_result ma_hpf_node_init(ma_node_graph* pNodeGraph, const ma_hpf_node_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_hpf_node* pNode) -{ - ma_result result; - ma_node_config baseNodeConfig; - - if (pNode == NULL) { - return MA_INVALID_ARGS; - } - - MA_ZERO_OBJECT(pNode); - - if (pConfig == NULL) { - return MA_INVALID_ARGS; - } - - if (pConfig->hpf.format != ma_format_f32) { - return MA_INVALID_ARGS; /* The format must be f32. */ - } - - result = ma_hpf_init(&pConfig->hpf, pAllocationCallbacks, &pNode->hpf); - if (result != MA_SUCCESS) { - return result; - } - - baseNodeConfig = ma_node_config_init(); - baseNodeConfig.vtable = &g_ma_hpf_node_vtable; - baseNodeConfig.pInputChannels = &pConfig->hpf.channels; - baseNodeConfig.pOutputChannels = &pConfig->hpf.channels; - - result = ma_node_init(pNodeGraph, &baseNodeConfig, pAllocationCallbacks, pNode); - if (result != MA_SUCCESS) { - return result; - } - - return result; -} - -MA_API ma_result ma_hpf_node_reinit(const ma_hpf_config* pConfig, ma_hpf_node* pNode) -{ - ma_hpf_node* pHPFNode = (ma_hpf_node*)pNode; - - if (pNode == NULL) { - return MA_INVALID_ARGS; - } - - return ma_hpf_reinit(pConfig, &pHPFNode->hpf); -} - -MA_API void ma_hpf_node_uninit(ma_hpf_node* pNode, const ma_allocation_callbacks* pAllocationCallbacks) -{ - ma_hpf_node* pHPFNode = (ma_hpf_node*)pNode; - - if (pNode == NULL) { - return; - } - - ma_node_uninit(pNode, pAllocationCallbacks); - ma_hpf_uninit(&pHPFNode->hpf, pAllocationCallbacks); -} - - - - -/* -Band Pass Filter Node -*/ -MA_API ma_bpf_node_config ma_bpf_node_config_init(ma_uint32 channels, ma_uint32 sampleRate, double cutoffFrequency, ma_uint32 order) -{ - ma_bpf_node_config config; - - config.nodeConfig = ma_node_config_init(); - config.bpf = ma_bpf_config_init(ma_format_f32, channels, sampleRate, cutoffFrequency, order); - - return config; -} - -static void ma_bpf_node_process_pcm_frames(ma_node* pNode, const float** ppFramesIn, ma_uint32* pFrameCountIn, float** ppFramesOut, ma_uint32* pFrameCountOut) -{ - ma_bpf_node* pBPFNode = (ma_bpf_node*)pNode; - - MA_ASSERT(pNode != NULL); - (void)pFrameCountIn; - - ma_bpf_process_pcm_frames(&pBPFNode->bpf, ppFramesOut[0], ppFramesIn[0], *pFrameCountOut); -} - -static ma_node_vtable g_ma_bpf_node_vtable = -{ - ma_bpf_node_process_pcm_frames, - NULL, /* onGetRequiredInputFrameCount */ - 1, /* One input. */ - 1, /* One output. */ - 0 /* Default flags. */ -}; - -MA_API ma_result ma_bpf_node_init(ma_node_graph* pNodeGraph, const ma_bpf_node_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_bpf_node* pNode) -{ - ma_result result; - ma_node_config baseNodeConfig; - - if (pNode == NULL) { - return MA_INVALID_ARGS; - } - - MA_ZERO_OBJECT(pNode); - - if (pConfig == NULL) { - return MA_INVALID_ARGS; - } - - if (pConfig->bpf.format != ma_format_f32) { - return MA_INVALID_ARGS; /* The format must be f32. */ - } - - result = ma_bpf_init(&pConfig->bpf, pAllocationCallbacks, &pNode->bpf); - if (result != MA_SUCCESS) { - return result; - } - - baseNodeConfig = ma_node_config_init(); - baseNodeConfig.vtable = &g_ma_bpf_node_vtable; - baseNodeConfig.pInputChannels = &pConfig->bpf.channels; - baseNodeConfig.pOutputChannels = &pConfig->bpf.channels; - - result = ma_node_init(pNodeGraph, &baseNodeConfig, pAllocationCallbacks, pNode); - if (result != MA_SUCCESS) { - return result; - } - - return result; -} - -MA_API ma_result ma_bpf_node_reinit(const ma_bpf_config* pConfig, ma_bpf_node* pNode) -{ - ma_bpf_node* pBPFNode = (ma_bpf_node*)pNode; - - if (pNode == NULL) { - return MA_INVALID_ARGS; - } - - return ma_bpf_reinit(pConfig, &pBPFNode->bpf); -} - -MA_API void ma_bpf_node_uninit(ma_bpf_node* pNode, const ma_allocation_callbacks* pAllocationCallbacks) -{ - ma_bpf_node* pBPFNode = (ma_bpf_node*)pNode; - - if (pNode == NULL) { - return; - } - - ma_node_uninit(pNode, pAllocationCallbacks); - ma_bpf_uninit(&pBPFNode->bpf, pAllocationCallbacks); -} - - - -/* -Notching Filter Node -*/ -MA_API ma_notch_node_config ma_notch_node_config_init(ma_uint32 channels, ma_uint32 sampleRate, double q, double frequency) -{ - ma_notch_node_config config; - - config.nodeConfig = ma_node_config_init(); - config.notch = ma_notch2_config_init(ma_format_f32, channels, sampleRate, q, frequency); - - return config; -} - -static void ma_notch_node_process_pcm_frames(ma_node* pNode, const float** ppFramesIn, ma_uint32* pFrameCountIn, float** ppFramesOut, ma_uint32* pFrameCountOut) -{ - ma_notch_node* pBPFNode = (ma_notch_node*)pNode; - - MA_ASSERT(pNode != NULL); - (void)pFrameCountIn; - - ma_notch2_process_pcm_frames(&pBPFNode->notch, ppFramesOut[0], ppFramesIn[0], *pFrameCountOut); -} - -static ma_node_vtable g_ma_notch_node_vtable = -{ - ma_notch_node_process_pcm_frames, - NULL, /* onGetRequiredInputFrameCount */ - 1, /* One input. */ - 1, /* One output. */ - 0 /* Default flags. */ -}; - -MA_API ma_result ma_notch_node_init(ma_node_graph* pNodeGraph, const ma_notch_node_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_notch_node* pNode) -{ - ma_result result; - ma_node_config baseNodeConfig; - - if (pNode == NULL) { - return MA_INVALID_ARGS; - } - - MA_ZERO_OBJECT(pNode); - - if (pConfig == NULL) { - return MA_INVALID_ARGS; - } - - if (pConfig->notch.format != ma_format_f32) { - return MA_INVALID_ARGS; /* The format must be f32. */ - } - - result = ma_notch2_init(&pConfig->notch, pAllocationCallbacks, &pNode->notch); - if (result != MA_SUCCESS) { - return result; - } - - baseNodeConfig = ma_node_config_init(); - baseNodeConfig.vtable = &g_ma_notch_node_vtable; - baseNodeConfig.pInputChannels = &pConfig->notch.channels; - baseNodeConfig.pOutputChannels = &pConfig->notch.channels; - - result = ma_node_init(pNodeGraph, &baseNodeConfig, pAllocationCallbacks, pNode); - if (result != MA_SUCCESS) { - return result; - } - - return result; -} - -MA_API ma_result ma_notch_node_reinit(const ma_notch_config* pConfig, ma_notch_node* pNode) -{ - ma_notch_node* pNotchNode = (ma_notch_node*)pNode; - - if (pNode == NULL) { - return MA_INVALID_ARGS; - } - - return ma_notch2_reinit(pConfig, &pNotchNode->notch); -} - -MA_API void ma_notch_node_uninit(ma_notch_node* pNode, const ma_allocation_callbacks* pAllocationCallbacks) -{ - ma_notch_node* pNotchNode = (ma_notch_node*)pNode; - - if (pNode == NULL) { - return; - } - - ma_node_uninit(pNode, pAllocationCallbacks); - ma_notch2_uninit(&pNotchNode->notch, pAllocationCallbacks); -} - - - -/* -Peaking Filter Node -*/ -MA_API ma_peak_node_config ma_peak_node_config_init(ma_uint32 channels, ma_uint32 sampleRate, double gainDB, double q, double frequency) -{ - ma_peak_node_config config; - - config.nodeConfig = ma_node_config_init(); - config.peak = ma_peak2_config_init(ma_format_f32, channels, sampleRate, gainDB, q, frequency); - - return config; -} - -static void ma_peak_node_process_pcm_frames(ma_node* pNode, const float** ppFramesIn, ma_uint32* pFrameCountIn, float** ppFramesOut, ma_uint32* pFrameCountOut) -{ - ma_peak_node* pBPFNode = (ma_peak_node*)pNode; - - MA_ASSERT(pNode != NULL); - (void)pFrameCountIn; - - ma_peak2_process_pcm_frames(&pBPFNode->peak, ppFramesOut[0], ppFramesIn[0], *pFrameCountOut); -} - -static ma_node_vtable g_ma_peak_node_vtable = -{ - ma_peak_node_process_pcm_frames, - NULL, /* onGetRequiredInputFrameCount */ - 1, /* One input. */ - 1, /* One output. */ - 0 /* Default flags. */ -}; - -MA_API ma_result ma_peak_node_init(ma_node_graph* pNodeGraph, const ma_peak_node_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_peak_node* pNode) -{ - ma_result result; - ma_node_config baseNodeConfig; - - if (pNode == NULL) { - return MA_INVALID_ARGS; - } - - MA_ZERO_OBJECT(pNode); - - if (pConfig == NULL) { - return MA_INVALID_ARGS; - } - - if (pConfig->peak.format != ma_format_f32) { - return MA_INVALID_ARGS; /* The format must be f32. */ - } - - result = ma_peak2_init(&pConfig->peak, pAllocationCallbacks, &pNode->peak); - if (result != MA_SUCCESS) { - ma_node_uninit(pNode, pAllocationCallbacks); - return result; - } - - baseNodeConfig = ma_node_config_init(); - baseNodeConfig.vtable = &g_ma_peak_node_vtable; - baseNodeConfig.pInputChannels = &pConfig->peak.channels; - baseNodeConfig.pOutputChannels = &pConfig->peak.channels; - - result = ma_node_init(pNodeGraph, &baseNodeConfig, pAllocationCallbacks, pNode); - if (result != MA_SUCCESS) { - return result; - } - - return result; -} - -MA_API ma_result ma_peak_node_reinit(const ma_peak_config* pConfig, ma_peak_node* pNode) -{ - ma_peak_node* pPeakNode = (ma_peak_node*)pNode; - - if (pNode == NULL) { - return MA_INVALID_ARGS; - } - - return ma_peak2_reinit(pConfig, &pPeakNode->peak); -} - -MA_API void ma_peak_node_uninit(ma_peak_node* pNode, const ma_allocation_callbacks* pAllocationCallbacks) -{ - ma_peak_node* pPeakNode = (ma_peak_node*)pNode; - - if (pNode == NULL) { - return; - } - - ma_node_uninit(pNode, pAllocationCallbacks); - ma_peak2_uninit(&pPeakNode->peak, pAllocationCallbacks); -} - - - -/* -Low Shelf Filter Node -*/ -MA_API ma_loshelf_node_config ma_loshelf_node_config_init(ma_uint32 channels, ma_uint32 sampleRate, double gainDB, double q, double frequency) -{ - ma_loshelf_node_config config; - - config.nodeConfig = ma_node_config_init(); - config.loshelf = ma_loshelf2_config_init(ma_format_f32, channels, sampleRate, gainDB, q, frequency); - - return config; -} - -static void ma_loshelf_node_process_pcm_frames(ma_node* pNode, const float** ppFramesIn, ma_uint32* pFrameCountIn, float** ppFramesOut, ma_uint32* pFrameCountOut) -{ - ma_loshelf_node* pBPFNode = (ma_loshelf_node*)pNode; - - MA_ASSERT(pNode != NULL); - (void)pFrameCountIn; - - ma_loshelf2_process_pcm_frames(&pBPFNode->loshelf, ppFramesOut[0], ppFramesIn[0], *pFrameCountOut); -} - -static ma_node_vtable g_ma_loshelf_node_vtable = -{ - ma_loshelf_node_process_pcm_frames, - NULL, /* onGetRequiredInputFrameCount */ - 1, /* One input. */ - 1, /* One output. */ - 0 /* Default flags. */ -}; - -MA_API ma_result ma_loshelf_node_init(ma_node_graph* pNodeGraph, const ma_loshelf_node_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_loshelf_node* pNode) -{ - ma_result result; - ma_node_config baseNodeConfig; - - if (pNode == NULL) { - return MA_INVALID_ARGS; - } - - MA_ZERO_OBJECT(pNode); - - if (pConfig == NULL) { - return MA_INVALID_ARGS; - } - - if (pConfig->loshelf.format != ma_format_f32) { - return MA_INVALID_ARGS; /* The format must be f32. */ - } - - result = ma_loshelf2_init(&pConfig->loshelf, pAllocationCallbacks, &pNode->loshelf); - if (result != MA_SUCCESS) { - return result; - } - - baseNodeConfig = ma_node_config_init(); - baseNodeConfig.vtable = &g_ma_loshelf_node_vtable; - baseNodeConfig.pInputChannels = &pConfig->loshelf.channels; - baseNodeConfig.pOutputChannels = &pConfig->loshelf.channels; - - result = ma_node_init(pNodeGraph, &baseNodeConfig, pAllocationCallbacks, pNode); - if (result != MA_SUCCESS) { - return result; - } - - return result; -} - -MA_API ma_result ma_loshelf_node_reinit(const ma_loshelf_config* pConfig, ma_loshelf_node* pNode) -{ - ma_loshelf_node* pLoshelfNode = (ma_loshelf_node*)pNode; - - if (pNode == NULL) { - return MA_INVALID_ARGS; - } - - return ma_loshelf2_reinit(pConfig, &pLoshelfNode->loshelf); -} - -MA_API void ma_loshelf_node_uninit(ma_loshelf_node* pNode, const ma_allocation_callbacks* pAllocationCallbacks) -{ - ma_loshelf_node* pLoshelfNode = (ma_loshelf_node*)pNode; - - if (pNode == NULL) { - return; - } - - ma_node_uninit(pNode, pAllocationCallbacks); - ma_loshelf2_uninit(&pLoshelfNode->loshelf, pAllocationCallbacks); -} - - - -/* -High Shelf Filter Node -*/ -MA_API ma_hishelf_node_config ma_hishelf_node_config_init(ma_uint32 channels, ma_uint32 sampleRate, double gainDB, double q, double frequency) -{ - ma_hishelf_node_config config; - - config.nodeConfig = ma_node_config_init(); - config.hishelf = ma_hishelf2_config_init(ma_format_f32, channels, sampleRate, gainDB, q, frequency); - - return config; -} - -static void ma_hishelf_node_process_pcm_frames(ma_node* pNode, const float** ppFramesIn, ma_uint32* pFrameCountIn, float** ppFramesOut, ma_uint32* pFrameCountOut) -{ - ma_hishelf_node* pBPFNode = (ma_hishelf_node*)pNode; - - MA_ASSERT(pNode != NULL); - (void)pFrameCountIn; - - ma_hishelf2_process_pcm_frames(&pBPFNode->hishelf, ppFramesOut[0], ppFramesIn[0], *pFrameCountOut); -} - -static ma_node_vtable g_ma_hishelf_node_vtable = -{ - ma_hishelf_node_process_pcm_frames, - NULL, /* onGetRequiredInputFrameCount */ - 1, /* One input. */ - 1, /* One output. */ - 0 /* Default flags. */ -}; - -MA_API ma_result ma_hishelf_node_init(ma_node_graph* pNodeGraph, const ma_hishelf_node_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_hishelf_node* pNode) -{ - ma_result result; - ma_node_config baseNodeConfig; - - if (pNode == NULL) { - return MA_INVALID_ARGS; - } - - MA_ZERO_OBJECT(pNode); - - if (pConfig == NULL) { - return MA_INVALID_ARGS; - } - - if (pConfig->hishelf.format != ma_format_f32) { - return MA_INVALID_ARGS; /* The format must be f32. */ - } - - result = ma_hishelf2_init(&pConfig->hishelf, pAllocationCallbacks, &pNode->hishelf); - if (result != MA_SUCCESS) { - return result; - } - - baseNodeConfig = ma_node_config_init(); - baseNodeConfig.vtable = &g_ma_hishelf_node_vtable; - baseNodeConfig.pInputChannels = &pConfig->hishelf.channels; - baseNodeConfig.pOutputChannels = &pConfig->hishelf.channels; - - result = ma_node_init(pNodeGraph, &baseNodeConfig, pAllocationCallbacks, pNode); - if (result != MA_SUCCESS) { - return result; - } - - return result; -} - -MA_API ma_result ma_hishelf_node_reinit(const ma_hishelf_config* pConfig, ma_hishelf_node* pNode) -{ - ma_hishelf_node* pHishelfNode = (ma_hishelf_node*)pNode; - - if (pNode == NULL) { - return MA_INVALID_ARGS; - } - - return ma_hishelf2_reinit(pConfig, &pHishelfNode->hishelf); -} - -MA_API void ma_hishelf_node_uninit(ma_hishelf_node* pNode, const ma_allocation_callbacks* pAllocationCallbacks) -{ - ma_hishelf_node* pHishelfNode = (ma_hishelf_node*)pNode; - - if (pNode == NULL) { - return; - } - - ma_node_uninit(pNode, pAllocationCallbacks); - ma_hishelf2_uninit(&pHishelfNode->hishelf, pAllocationCallbacks); -} - - - - -MA_API ma_delay_node_config ma_delay_node_config_init(ma_uint32 channels, ma_uint32 sampleRate, ma_uint32 delayInFrames, float decay) -{ - ma_delay_node_config config; - - config.nodeConfig = ma_node_config_init(); - config.delay = ma_delay_config_init(channels, sampleRate, delayInFrames, decay); - - return config; -} - - -static void ma_delay_node_process_pcm_frames(ma_node* pNode, const float** ppFramesIn, ma_uint32* pFrameCountIn, float** ppFramesOut, ma_uint32* pFrameCountOut) -{ - ma_delay_node* pDelayNode = (ma_delay_node*)pNode; - - (void)pFrameCountIn; - - ma_delay_process_pcm_frames(&pDelayNode->delay, ppFramesOut[0], ppFramesIn[0], *pFrameCountOut); -} - -static ma_node_vtable g_ma_delay_node_vtable = -{ - ma_delay_node_process_pcm_frames, - NULL, - 1, /* 1 input channels. */ - 1, /* 1 output channel. */ - MA_NODE_FLAG_CONTINUOUS_PROCESSING /* Delay requires continuous processing to ensure the tail get's processed. */ -}; - -MA_API ma_result ma_delay_node_init(ma_node_graph* pNodeGraph, const ma_delay_node_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_delay_node* pDelayNode) -{ - ma_result result; - ma_node_config baseConfig; - - if (pDelayNode == NULL) { - return MA_INVALID_ARGS; - } - - MA_ZERO_OBJECT(pDelayNode); - - result = ma_delay_init(&pConfig->delay, pAllocationCallbacks, &pDelayNode->delay); - if (result != MA_SUCCESS) { - return result; - } - - baseConfig = pConfig->nodeConfig; - baseConfig.vtable = &g_ma_delay_node_vtable; - baseConfig.pInputChannels = &pConfig->delay.channels; - baseConfig.pOutputChannels = &pConfig->delay.channels; - - result = ma_node_init(pNodeGraph, &baseConfig, pAllocationCallbacks, &pDelayNode->baseNode); - if (result != MA_SUCCESS) { - ma_delay_uninit(&pDelayNode->delay, pAllocationCallbacks); - return result; - } - - return result; -} - -MA_API void ma_delay_node_uninit(ma_delay_node* pDelayNode, const ma_allocation_callbacks* pAllocationCallbacks) -{ - if (pDelayNode == NULL) { - return; - } - - /* The base node is always uninitialized first. */ - ma_node_uninit(pDelayNode, pAllocationCallbacks); - ma_delay_uninit(&pDelayNode->delay, pAllocationCallbacks); -} - -MA_API void ma_delay_node_set_wet(ma_delay_node* pDelayNode, float value) -{ - if (pDelayNode == NULL) { - return; - } - - ma_delay_set_wet(&pDelayNode->delay, value); -} - -MA_API float ma_delay_node_get_wet(const ma_delay_node* pDelayNode) -{ - if (pDelayNode == NULL) { - return 0; - } - - return ma_delay_get_wet(&pDelayNode->delay); -} - -MA_API void ma_delay_node_set_dry(ma_delay_node* pDelayNode, float value) -{ - if (pDelayNode == NULL) { - return; - } - - ma_delay_set_dry(&pDelayNode->delay, value); -} - -MA_API float ma_delay_node_get_dry(const ma_delay_node* pDelayNode) -{ - if (pDelayNode == NULL) { - return 0; - } - - return ma_delay_get_dry(&pDelayNode->delay); -} - -MA_API void ma_delay_node_set_decay(ma_delay_node* pDelayNode, float value) -{ - if (pDelayNode == NULL) { - return; - } - - ma_delay_set_decay(&pDelayNode->delay, value); -} - -MA_API float ma_delay_node_get_decay(const ma_delay_node* pDelayNode) -{ - if (pDelayNode == NULL) { - return 0; - } - - return ma_delay_get_decay(&pDelayNode->delay); -} -#endif /* MA_NO_NODE_GRAPH */ - - -/* SECTION: miniaudio_engine.c */ -#if !defined(MA_NO_ENGINE) && !defined(MA_NO_NODE_GRAPH) -/************************************************************************************************************************************************************** - -Engine - -**************************************************************************************************************************************************************/ -#define MA_SEEK_TARGET_NONE (~(ma_uint64)0) - - -static void ma_sound_set_at_end(ma_sound* pSound, ma_bool32 atEnd) -{ - MA_ASSERT(pSound != NULL); - ma_atomic_exchange_32(&pSound->atEnd, atEnd); - - /* Fire any callbacks or events. */ - if (atEnd) { - if (pSound->endCallback != NULL) { - pSound->endCallback(pSound->pEndCallbackUserData, pSound); - } - } -} - -static ma_bool32 ma_sound_get_at_end(const ma_sound* pSound) -{ - MA_ASSERT(pSound != NULL); - return ma_atomic_load_32(&pSound->atEnd); -} - - -MA_API ma_engine_node_config ma_engine_node_config_init(ma_engine* pEngine, ma_engine_node_type type, ma_uint32 flags) -{ - ma_engine_node_config config; - - MA_ZERO_OBJECT(&config); - config.pEngine = pEngine; - config.type = type; - config.isPitchDisabled = (flags & MA_SOUND_FLAG_NO_PITCH) != 0; - config.isSpatializationDisabled = (flags & MA_SOUND_FLAG_NO_SPATIALIZATION) != 0; - config.monoExpansionMode = pEngine->monoExpansionMode; - - return config; -} - - -static void ma_engine_node_update_pitch_if_required(ma_engine_node* pEngineNode) -{ - ma_bool32 isUpdateRequired = MA_FALSE; - float newPitch; - - MA_ASSERT(pEngineNode != NULL); - - newPitch = ma_atomic_load_explicit_f32(&pEngineNode->pitch, ma_atomic_memory_order_acquire); - - if (pEngineNode->oldPitch != newPitch) { - pEngineNode->oldPitch = newPitch; - isUpdateRequired = MA_TRUE; - } - - if (pEngineNode->oldDopplerPitch != pEngineNode->spatializer.dopplerPitch) { - pEngineNode->oldDopplerPitch = pEngineNode->spatializer.dopplerPitch; - isUpdateRequired = MA_TRUE; - } - - if (isUpdateRequired) { - float basePitch = (float)pEngineNode->sampleRate / ma_engine_get_sample_rate(pEngineNode->pEngine); - ma_linear_resampler_set_rate_ratio(&pEngineNode->resampler, basePitch * pEngineNode->oldPitch * pEngineNode->oldDopplerPitch); - } -} - -static ma_bool32 ma_engine_node_is_pitching_enabled(const ma_engine_node* pEngineNode) -{ - MA_ASSERT(pEngineNode != NULL); - - /* Don't try to be clever by skipping resampling in the pitch=1 case or else you'll glitch when moving away from 1. */ - return !ma_atomic_load_explicit_32(&pEngineNode->isPitchDisabled, ma_atomic_memory_order_acquire); -} - -static ma_bool32 ma_engine_node_is_spatialization_enabled(const ma_engine_node* pEngineNode) -{ - MA_ASSERT(pEngineNode != NULL); - - return !ma_atomic_load_explicit_32(&pEngineNode->isSpatializationDisabled, ma_atomic_memory_order_acquire); -} - -static ma_uint64 ma_engine_node_get_required_input_frame_count(const ma_engine_node* pEngineNode, ma_uint64 outputFrameCount) -{ - ma_uint64 inputFrameCount = 0; - - if (ma_engine_node_is_pitching_enabled(pEngineNode)) { - ma_result result = ma_linear_resampler_get_required_input_frame_count(&pEngineNode->resampler, outputFrameCount, &inputFrameCount); - if (result != MA_SUCCESS) { - inputFrameCount = 0; - } - } else { - inputFrameCount = outputFrameCount; /* No resampling, so 1:1. */ - } - - return inputFrameCount; -} - -static ma_result ma_engine_node_set_volume(ma_engine_node* pEngineNode, float volume) -{ - if (pEngineNode == NULL) { - return MA_INVALID_ARGS; - } - - ma_atomic_float_set(&pEngineNode->volume, volume); - - /* If we're not smoothing we should bypass the volume gainer entirely. */ - if (pEngineNode->volumeSmoothTimeInPCMFrames == 0) { - /* We should always have an active spatializer because it can be enabled and disabled dynamically. We can just use that for holding our volume. */ - ma_spatializer_set_master_volume(&pEngineNode->spatializer, volume); - } else { - /* We're using volume smoothing, so apply the master volume to the gainer. */ - ma_gainer_set_gain(&pEngineNode->volumeGainer, volume); - } - - return MA_SUCCESS; -} - -static ma_result ma_engine_node_get_volume(const ma_engine_node* pEngineNode, float* pVolume) -{ - if (pVolume == NULL) { - return MA_INVALID_ARGS; - } - - *pVolume = 0.0f; - - if (pEngineNode == NULL) { - return MA_INVALID_ARGS; - } - - *pVolume = ma_atomic_float_get((ma_atomic_float*)&pEngineNode->volume); - - return MA_SUCCESS; -} - - -static void ma_engine_node_process_pcm_frames__general(ma_engine_node* pEngineNode, const float** ppFramesIn, ma_uint32* pFrameCountIn, float** ppFramesOut, ma_uint32* pFrameCountOut) -{ - ma_uint32 frameCountIn; - ma_uint32 frameCountOut; - ma_uint32 totalFramesProcessedIn; - ma_uint32 totalFramesProcessedOut; - ma_uint32 channelsIn; - ma_uint32 channelsOut; - ma_bool32 isPitchingEnabled; - ma_bool32 isFadingEnabled; - ma_bool32 isSpatializationEnabled; - ma_bool32 isPanningEnabled; - ma_bool32 isVolumeSmoothingEnabled; - - frameCountIn = *pFrameCountIn; - frameCountOut = *pFrameCountOut; - - channelsIn = ma_spatializer_get_input_channels(&pEngineNode->spatializer); - channelsOut = ma_spatializer_get_output_channels(&pEngineNode->spatializer); - - totalFramesProcessedIn = 0; - totalFramesProcessedOut = 0; - - /* Update the fader if applicable. */ - { - ma_uint64 fadeLengthInFrames = ma_atomic_uint64_get(&pEngineNode->fadeSettings.fadeLengthInFrames); - if (fadeLengthInFrames != ~(ma_uint64)0) { - float fadeVolumeBeg = ma_atomic_float_get(&pEngineNode->fadeSettings.volumeBeg); - float fadeVolumeEnd = ma_atomic_float_get(&pEngineNode->fadeSettings.volumeEnd); - ma_int64 fadeStartOffsetInFrames = (ma_int64)ma_atomic_uint64_get(&pEngineNode->fadeSettings.absoluteGlobalTimeInFrames); - if (fadeStartOffsetInFrames == (ma_int64)(~(ma_uint64)0)) { - fadeStartOffsetInFrames = 0; - } else { - fadeStartOffsetInFrames -= ma_engine_get_time_in_pcm_frames(pEngineNode->pEngine); - } - - ma_fader_set_fade_ex(&pEngineNode->fader, fadeVolumeBeg, fadeVolumeEnd, fadeLengthInFrames, fadeStartOffsetInFrames); - - /* Reset the fade length so we don't erroneously apply it again. */ - ma_atomic_uint64_set(&pEngineNode->fadeSettings.fadeLengthInFrames, ~(ma_uint64)0); - } - } - - isPitchingEnabled = ma_engine_node_is_pitching_enabled(pEngineNode); - isFadingEnabled = pEngineNode->fader.volumeBeg != 1 || pEngineNode->fader.volumeEnd != 1; - isSpatializationEnabled = ma_engine_node_is_spatialization_enabled(pEngineNode); - isPanningEnabled = pEngineNode->panner.pan != 0 && channelsOut != 1; - isVolumeSmoothingEnabled = pEngineNode->volumeSmoothTimeInPCMFrames > 0; - - /* Keep going while we've still got data available for processing. */ - while (totalFramesProcessedOut < frameCountOut) { - /* - We need to process in a specific order. We always do resampling first because it's likely - we're going to be increasing the channel count after spatialization. Also, I want to do - fading based on the output sample rate. - - We'll first read into a buffer from the resampler. Then we'll do all processing that - operates on the on the input channel count. We'll then get the spatializer to output to - the output buffer and then do all effects from that point directly in the output buffer - in-place. - - Note that we're always running the resampler if pitching is enabled, even when the pitch - is 1. If we try to be clever and skip resampling when the pitch is 1, we'll get a glitch - when we move away from 1, back to 1, and then away from 1 again. We'll want to implement - any pitch=1 optimizations in the resampler itself. - - There's a small optimization here that we'll utilize since it might be a fairly common - case. When the input and output channel counts are the same, we'll read straight into the - output buffer from the resampler and do everything in-place. - */ - const float* pRunningFramesIn; - float* pRunningFramesOut; - float* pWorkingBuffer; /* This is the buffer that we'll be processing frames in. This is in input channels. */ - float temp[MA_DATA_CONVERTER_STACK_BUFFER_SIZE / sizeof(float)]; - ma_uint32 tempCapInFrames = ma_countof(temp) / channelsIn; - ma_uint32 framesAvailableIn; - ma_uint32 framesAvailableOut; - ma_uint32 framesJustProcessedIn; - ma_uint32 framesJustProcessedOut; - ma_bool32 isWorkingBufferValid = MA_FALSE; - - framesAvailableIn = frameCountIn - totalFramesProcessedIn; - framesAvailableOut = frameCountOut - totalFramesProcessedOut; - - pRunningFramesIn = ma_offset_pcm_frames_const_ptr_f32(ppFramesIn[0], totalFramesProcessedIn, channelsIn); - pRunningFramesOut = ma_offset_pcm_frames_ptr_f32(ppFramesOut[0], totalFramesProcessedOut, channelsOut); - - if (channelsIn == channelsOut) { - /* Fast path. Channel counts are the same. No need for an intermediary input buffer. */ - pWorkingBuffer = pRunningFramesOut; - } else { - /* Slow path. Channel counts are different. Need to use an intermediary input buffer. */ - pWorkingBuffer = temp; - if (framesAvailableOut > tempCapInFrames) { - framesAvailableOut = tempCapInFrames; - } - } - - /* First is resampler. */ - if (isPitchingEnabled) { - ma_uint64 resampleFrameCountIn = framesAvailableIn; - ma_uint64 resampleFrameCountOut = framesAvailableOut; - - ma_linear_resampler_process_pcm_frames(&pEngineNode->resampler, pRunningFramesIn, &resampleFrameCountIn, pWorkingBuffer, &resampleFrameCountOut); - isWorkingBufferValid = MA_TRUE; - - framesJustProcessedIn = (ma_uint32)resampleFrameCountIn; - framesJustProcessedOut = (ma_uint32)resampleFrameCountOut; - } else { - framesJustProcessedIn = ma_min(framesAvailableIn, framesAvailableOut); - framesJustProcessedOut = framesJustProcessedIn; /* When no resampling is being performed, the number of output frames is the same as input frames. */ - } - - /* Fading. */ - if (isFadingEnabled) { - if (isWorkingBufferValid) { - ma_fader_process_pcm_frames(&pEngineNode->fader, pWorkingBuffer, pWorkingBuffer, framesJustProcessedOut); /* In-place processing. */ - } else { - ma_fader_process_pcm_frames(&pEngineNode->fader, pWorkingBuffer, pRunningFramesIn, framesJustProcessedOut); - isWorkingBufferValid = MA_TRUE; - } - } - - /* - If we're using smoothing, we won't be applying volume via the spatializer, but instead from a ma_gainer. In this case - we'll want to apply our volume now. - */ - if (isVolumeSmoothingEnabled) { - if (isWorkingBufferValid) { - ma_gainer_process_pcm_frames(&pEngineNode->volumeGainer, pWorkingBuffer, pWorkingBuffer, framesJustProcessedOut); - } else { - ma_gainer_process_pcm_frames(&pEngineNode->volumeGainer, pWorkingBuffer, pRunningFramesIn, framesJustProcessedOut); - isWorkingBufferValid = MA_TRUE; - } - } - - /* - If at this point we still haven't actually done anything with the working buffer we need - to just read straight from the input buffer. - */ - if (isWorkingBufferValid == MA_FALSE) { - pWorkingBuffer = (float*)pRunningFramesIn; /* Naughty const cast, but it's safe at this point because we won't ever be writing to it from this point out. */ - } - - /* Spatialization. */ - if (isSpatializationEnabled) { - ma_uint32 iListener; - - /* - When determining the listener to use, we first check to see if the sound is pinned to a - specific listener. If so, we use that. Otherwise we just use the closest listener. - */ - if (pEngineNode->pinnedListenerIndex != MA_LISTENER_INDEX_CLOSEST && pEngineNode->pinnedListenerIndex < ma_engine_get_listener_count(pEngineNode->pEngine)) { - iListener = pEngineNode->pinnedListenerIndex; - } else { - ma_vec3f spatializerPosition = ma_spatializer_get_position(&pEngineNode->spatializer); - iListener = ma_engine_find_closest_listener(pEngineNode->pEngine, spatializerPosition.x, spatializerPosition.y, spatializerPosition.z); - } - - ma_spatializer_process_pcm_frames(&pEngineNode->spatializer, &pEngineNode->pEngine->listeners[iListener], pRunningFramesOut, pWorkingBuffer, framesJustProcessedOut); - } else { - /* No spatialization, but we still need to do channel conversion and master volume. */ - float volume; - ma_engine_node_get_volume(pEngineNode, &volume); /* Should never fail. */ - - if (channelsIn == channelsOut) { - /* No channel conversion required. Just copy straight to the output buffer. */ - if (isVolumeSmoothingEnabled) { - /* Volume has already been applied. Just copy straight to the output buffer. */ - ma_copy_pcm_frames(pRunningFramesOut, pWorkingBuffer, framesJustProcessedOut * channelsOut, ma_format_f32, channelsOut); - } else { - /* Volume has not been applied yet. Copy and apply volume in the same pass. */ - ma_copy_and_apply_volume_factor_f32(pRunningFramesOut, pWorkingBuffer, framesJustProcessedOut * channelsOut, volume); - } - } else { - /* Channel conversion required. TODO: Add support for channel maps here. */ - ma_channel_map_apply_f32(pRunningFramesOut, NULL, channelsOut, pWorkingBuffer, NULL, channelsIn, framesJustProcessedOut, ma_channel_mix_mode_simple, pEngineNode->monoExpansionMode); - - /* If we're using smoothing, the volume will have already been applied. */ - if (!isVolumeSmoothingEnabled) { - ma_apply_volume_factor_f32(pRunningFramesOut, framesJustProcessedOut * channelsOut, volume); - } - } - } - - /* At this point we can guarantee that the output buffer contains valid data. We can process everything in place now. */ - - /* Panning. */ - if (isPanningEnabled) { - ma_panner_process_pcm_frames(&pEngineNode->panner, pRunningFramesOut, pRunningFramesOut, framesJustProcessedOut); /* In-place processing. */ - } - - /* We're done for this chunk. */ - totalFramesProcessedIn += framesJustProcessedIn; - totalFramesProcessedOut += framesJustProcessedOut; - - /* If we didn't process any output frames this iteration it means we've either run out of input data, or run out of room in the output buffer. */ - if (framesJustProcessedOut == 0) { - break; - } - } - - /* At this point we're done processing. */ - *pFrameCountIn = totalFramesProcessedIn; - *pFrameCountOut = totalFramesProcessedOut; -} - -static void ma_engine_node_process_pcm_frames__sound(ma_node* pNode, const float** ppFramesIn, ma_uint32* pFrameCountIn, float** ppFramesOut, ma_uint32* pFrameCountOut) -{ - /* For sounds, we need to first read from the data source. Then we need to apply the engine effects (pan, pitch, fades, etc.). */ - ma_result result = MA_SUCCESS; - ma_sound* pSound = (ma_sound*)pNode; - ma_uint32 frameCount = *pFrameCountOut; - ma_uint32 totalFramesRead = 0; - ma_format dataSourceFormat; - ma_uint32 dataSourceChannels; - ma_uint8 temp[MA_DATA_CONVERTER_STACK_BUFFER_SIZE]; - ma_uint32 tempCapInFrames; - ma_uint64 seekTarget; - - /* This is a data source node which means no input buses. */ - (void)ppFramesIn; - (void)pFrameCountIn; - - /* If we're marked at the end we need to stop the sound and do nothing. */ - if (ma_sound_at_end(pSound)) { - ma_sound_stop(pSound); - *pFrameCountOut = 0; - return; - } - - /* If we're seeking, do so now before reading. */ - seekTarget = ma_atomic_load_64(&pSound->seekTarget); - if (seekTarget != MA_SEEK_TARGET_NONE) { - ma_data_source_seek_to_pcm_frame(pSound->pDataSource, seekTarget); - - /* Any time-dependant effects need to have their times updated. */ - ma_node_set_time(pSound, seekTarget); - - ma_atomic_exchange_64(&pSound->seekTarget, MA_SEEK_TARGET_NONE); - } - - /* - We want to update the pitch once. For sounds, this can be either at the start or at the end. If - we don't force this to only ever be updating once, we could end up in a situation where - retrieving the required input frame count ends up being different to what we actually retrieve. - What could happen is that the required input frame count is calculated, the pitch is update, - and then this processing function is called resulting in a different number of input frames - being processed. Do not call this in ma_engine_node_process_pcm_frames__general() or else - you'll hit the aforementioned bug. - */ - ma_engine_node_update_pitch_if_required(&pSound->engineNode); - - /* - For the convenience of the caller, we're doing to allow data sources to use non-floating-point formats and channel counts that differ - from the main engine. - */ - result = ma_data_source_get_data_format(pSound->pDataSource, &dataSourceFormat, &dataSourceChannels, NULL, NULL, 0); - if (result == MA_SUCCESS) { - tempCapInFrames = sizeof(temp) / ma_get_bytes_per_frame(dataSourceFormat, dataSourceChannels); - - /* Keep reading until we've read as much as was requested or we reach the end of the data source. */ - while (totalFramesRead < frameCount) { - ma_uint32 framesRemaining = frameCount - totalFramesRead; - ma_uint32 framesToRead; - ma_uint64 framesJustRead; - ma_uint32 frameCountIn; - ma_uint32 frameCountOut; - const float* pRunningFramesIn; - float* pRunningFramesOut; - - /* - The first thing we need to do is read into the temporary buffer. We can calculate exactly - how many input frames we'll need after resampling. - */ - framesToRead = (ma_uint32)ma_engine_node_get_required_input_frame_count(&pSound->engineNode, framesRemaining); - if (framesToRead > tempCapInFrames) { - framesToRead = tempCapInFrames; - } - - result = ma_data_source_read_pcm_frames(pSound->pDataSource, temp, framesToRead, &framesJustRead); - - /* If we reached the end of the sound we'll want to mark it as at the end and stop it. This should never be returned for looping sounds. */ - if (result == MA_AT_END) { - ma_sound_set_at_end(pSound, MA_TRUE); /* This will be set to false in ma_sound_start(). */ - } - - pRunningFramesOut = ma_offset_pcm_frames_ptr_f32(ppFramesOut[0], totalFramesRead, ma_node_get_output_channels(pNode, 0)); - - frameCountIn = (ma_uint32)framesJustRead; - frameCountOut = framesRemaining; - - /* Convert if necessary. */ - if (dataSourceFormat == ma_format_f32) { - /* Fast path. No data conversion necessary. */ - pRunningFramesIn = (float*)temp; - ma_engine_node_process_pcm_frames__general(&pSound->engineNode, &pRunningFramesIn, &frameCountIn, &pRunningFramesOut, &frameCountOut); - } else { - /* Slow path. Need to do sample format conversion to f32. If we give the f32 buffer the same count as the first temp buffer, we're guaranteed it'll be large enough. */ - float tempf32[MA_DATA_CONVERTER_STACK_BUFFER_SIZE]; /* Do not do `MA_DATA_CONVERTER_STACK_BUFFER_SIZE/sizeof(float)` here like we've done in other places. */ - ma_convert_pcm_frames_format(tempf32, ma_format_f32, temp, dataSourceFormat, framesJustRead, dataSourceChannels, ma_dither_mode_none); - - /* Now that we have our samples in f32 format we can process like normal. */ - pRunningFramesIn = tempf32; - ma_engine_node_process_pcm_frames__general(&pSound->engineNode, &pRunningFramesIn, &frameCountIn, &pRunningFramesOut, &frameCountOut); - } - - /* We should have processed all of our input frames since we calculated the required number of input frames at the top. */ - MA_ASSERT(frameCountIn == framesJustRead); - totalFramesRead += (ma_uint32)frameCountOut; /* Safe cast. */ - - if (result != MA_SUCCESS || ma_sound_at_end(pSound)) { - break; /* Might have reached the end. */ - } - } - } - - *pFrameCountOut = totalFramesRead; -} - -static void ma_engine_node_process_pcm_frames__group(ma_node* pNode, const float** ppFramesIn, ma_uint32* pFrameCountIn, float** ppFramesOut, ma_uint32* pFrameCountOut) -{ - /* - Make sure the pitch is updated before trying to read anything. It's important that this is done - only once and not in ma_engine_node_process_pcm_frames__general(). The reason for this is that - ma_engine_node_process_pcm_frames__general() will call ma_engine_node_get_required_input_frame_count(), - and if another thread modifies the pitch just after that call it can result in a glitch due to - the input rate changing. - */ - ma_engine_node_update_pitch_if_required((ma_engine_node*)pNode); - - /* For groups, the input data has already been read and we just need to apply the effect. */ - ma_engine_node_process_pcm_frames__general((ma_engine_node*)pNode, ppFramesIn, pFrameCountIn, ppFramesOut, pFrameCountOut); -} - -static ma_result ma_engine_node_get_required_input_frame_count__group(ma_node* pNode, ma_uint32 outputFrameCount, ma_uint32* pInputFrameCount) -{ - ma_uint64 inputFrameCount; - - MA_ASSERT(pInputFrameCount != NULL); - - /* Our pitch will affect this calculation. We need to update it. */ - ma_engine_node_update_pitch_if_required((ma_engine_node*)pNode); - - inputFrameCount = ma_engine_node_get_required_input_frame_count((ma_engine_node*)pNode, outputFrameCount); - if (inputFrameCount > 0xFFFFFFFF) { - inputFrameCount = 0xFFFFFFFF; /* Will never happen because miniaudio will only ever process in relatively small chunks. */ - } - - *pInputFrameCount = (ma_uint32)inputFrameCount; - - return MA_SUCCESS; -} - - -static ma_node_vtable g_ma_engine_node_vtable__sound = -{ - ma_engine_node_process_pcm_frames__sound, - NULL, /* onGetRequiredInputFrameCount */ - 0, /* Sounds are data source nodes which means they have zero inputs (their input is drawn from the data source itself). */ - 1, /* Sounds have one output bus. */ - 0 /* Default flags. */ -}; - -static ma_node_vtable g_ma_engine_node_vtable__group = -{ - ma_engine_node_process_pcm_frames__group, - ma_engine_node_get_required_input_frame_count__group, - 1, /* Groups have one input bus. */ - 1, /* Groups have one output bus. */ - MA_NODE_FLAG_DIFFERENT_PROCESSING_RATES /* The engine node does resampling so should let miniaudio know about it. */ -}; - - - -static ma_node_config ma_engine_node_base_node_config_init(const ma_engine_node_config* pConfig) -{ - ma_node_config baseNodeConfig; - - if (pConfig->type == ma_engine_node_type_sound) { - /* Sound. */ - baseNodeConfig = ma_node_config_init(); - baseNodeConfig.vtable = &g_ma_engine_node_vtable__sound; - baseNodeConfig.initialState = ma_node_state_stopped; /* Sounds are stopped by default. */ - } else { - /* Group. */ - baseNodeConfig = ma_node_config_init(); - baseNodeConfig.vtable = &g_ma_engine_node_vtable__group; - baseNodeConfig.initialState = ma_node_state_started; /* Groups are started by default. */ - } - - return baseNodeConfig; -} - -static ma_spatializer_config ma_engine_node_spatializer_config_init(const ma_node_config* pBaseNodeConfig) -{ - return ma_spatializer_config_init(pBaseNodeConfig->pInputChannels[0], pBaseNodeConfig->pOutputChannels[0]); -} - -typedef struct -{ - size_t sizeInBytes; - size_t baseNodeOffset; - size_t resamplerOffset; - size_t spatializerOffset; - size_t gainerOffset; -} ma_engine_node_heap_layout; - -static ma_result ma_engine_node_get_heap_layout(const ma_engine_node_config* pConfig, ma_engine_node_heap_layout* pHeapLayout) -{ - ma_result result; - size_t tempHeapSize; - ma_node_config baseNodeConfig; - ma_linear_resampler_config resamplerConfig; - ma_spatializer_config spatializerConfig; - ma_gainer_config gainerConfig; - ma_uint32 channelsIn; - ma_uint32 channelsOut; - ma_channel defaultStereoChannelMap[2] = {MA_CHANNEL_SIDE_LEFT, MA_CHANNEL_SIDE_RIGHT}; /* <-- Consistent with the default channel map of a stereo listener. Means channel conversion can run on a fast path. */ - - MA_ASSERT(pHeapLayout); - - MA_ZERO_OBJECT(pHeapLayout); - - if (pConfig == NULL) { - return MA_INVALID_ARGS; - } - - if (pConfig->pEngine == NULL) { - return MA_INVALID_ARGS; /* An engine must be specified. */ - } - - pHeapLayout->sizeInBytes = 0; - - channelsIn = (pConfig->channelsIn != 0) ? pConfig->channelsIn : ma_engine_get_channels(pConfig->pEngine); - channelsOut = (pConfig->channelsOut != 0) ? pConfig->channelsOut : ma_engine_get_channels(pConfig->pEngine); - - - /* Base node. */ - baseNodeConfig = ma_engine_node_base_node_config_init(pConfig); - baseNodeConfig.pInputChannels = &channelsIn; - baseNodeConfig.pOutputChannels = &channelsOut; - - result = ma_node_get_heap_size(ma_engine_get_node_graph(pConfig->pEngine), &baseNodeConfig, &tempHeapSize); - if (result != MA_SUCCESS) { - return result; /* Failed to retrieve the size of the heap for the base node. */ - } - - pHeapLayout->baseNodeOffset = pHeapLayout->sizeInBytes; - pHeapLayout->sizeInBytes += ma_align_64(tempHeapSize); - - - /* Resmapler. */ - resamplerConfig = ma_linear_resampler_config_init(ma_format_f32, channelsIn, 1, 1); /* Input and output sample rates don't affect the calculation of the heap size. */ - resamplerConfig.lpfOrder = 0; - - result = ma_linear_resampler_get_heap_size(&resamplerConfig, &tempHeapSize); - if (result != MA_SUCCESS) { - return result; /* Failed to retrieve the size of the heap for the resampler. */ - } - - pHeapLayout->resamplerOffset = pHeapLayout->sizeInBytes; - pHeapLayout->sizeInBytes += ma_align_64(tempHeapSize); - - - /* Spatializer. */ - spatializerConfig = ma_engine_node_spatializer_config_init(&baseNodeConfig); - - if (spatializerConfig.channelsIn == 2) { - spatializerConfig.pChannelMapIn = defaultStereoChannelMap; - } - - result = ma_spatializer_get_heap_size(&spatializerConfig, &tempHeapSize); - if (result != MA_SUCCESS) { - return result; /* Failed to retrieve the size of the heap for the spatializer. */ - } - - pHeapLayout->spatializerOffset = pHeapLayout->sizeInBytes; - pHeapLayout->sizeInBytes += ma_align_64(tempHeapSize); - - - /* Gainer. Will not be used if we are not using smoothing. */ - if (pConfig->volumeSmoothTimeInPCMFrames > 0) { - gainerConfig = ma_gainer_config_init(channelsIn, pConfig->volumeSmoothTimeInPCMFrames); - - result = ma_gainer_get_heap_size(&gainerConfig, &tempHeapSize); - if (result != MA_SUCCESS) { - return result; - } - - pHeapLayout->gainerOffset = pHeapLayout->sizeInBytes; - pHeapLayout->sizeInBytes += ma_align_64(tempHeapSize); - } - - - return MA_SUCCESS; -} - -MA_API ma_result ma_engine_node_get_heap_size(const ma_engine_node_config* pConfig, size_t* pHeapSizeInBytes) -{ - ma_result result; - ma_engine_node_heap_layout heapLayout; - - if (pHeapSizeInBytes == NULL) { - return MA_INVALID_ARGS; - } - - *pHeapSizeInBytes = 0; - - result = ma_engine_node_get_heap_layout(pConfig, &heapLayout); - if (result != MA_SUCCESS) { - return result; - } - - *pHeapSizeInBytes = heapLayout.sizeInBytes; - - return MA_SUCCESS; -} - -MA_API ma_result ma_engine_node_init_preallocated(const ma_engine_node_config* pConfig, void* pHeap, ma_engine_node* pEngineNode) -{ - ma_result result; - ma_engine_node_heap_layout heapLayout; - ma_node_config baseNodeConfig; - ma_linear_resampler_config resamplerConfig; - ma_fader_config faderConfig; - ma_spatializer_config spatializerConfig; - ma_panner_config pannerConfig; - ma_gainer_config gainerConfig; - ma_uint32 channelsIn; - ma_uint32 channelsOut; - ma_channel defaultStereoChannelMap[2] = {MA_CHANNEL_SIDE_LEFT, MA_CHANNEL_SIDE_RIGHT}; /* <-- Consistent with the default channel map of a stereo listener. Means channel conversion can run on a fast path. */ - - if (pEngineNode == NULL) { - return MA_INVALID_ARGS; - } - - MA_ZERO_OBJECT(pEngineNode); - - result = ma_engine_node_get_heap_layout(pConfig, &heapLayout); - if (result != MA_SUCCESS) { - return result; - } - - if (pConfig->pinnedListenerIndex != MA_LISTENER_INDEX_CLOSEST && pConfig->pinnedListenerIndex >= ma_engine_get_listener_count(pConfig->pEngine)) { - return MA_INVALID_ARGS; /* Invalid listener. */ - } - - pEngineNode->_pHeap = pHeap; - MA_ZERO_MEMORY(pHeap, heapLayout.sizeInBytes); - - pEngineNode->pEngine = pConfig->pEngine; - pEngineNode->sampleRate = (pConfig->sampleRate > 0) ? pConfig->sampleRate : ma_engine_get_sample_rate(pEngineNode->pEngine); - pEngineNode->volumeSmoothTimeInPCMFrames = pConfig->volumeSmoothTimeInPCMFrames; - pEngineNode->monoExpansionMode = pConfig->monoExpansionMode; - ma_atomic_float_set(&pEngineNode->volume, 1); - pEngineNode->pitch = 1; - pEngineNode->oldPitch = 1; - pEngineNode->oldDopplerPitch = 1; - pEngineNode->isPitchDisabled = pConfig->isPitchDisabled; - pEngineNode->isSpatializationDisabled = pConfig->isSpatializationDisabled; - pEngineNode->pinnedListenerIndex = pConfig->pinnedListenerIndex; - ma_atomic_float_set(&pEngineNode->fadeSettings.volumeBeg, 1); - ma_atomic_float_set(&pEngineNode->fadeSettings.volumeEnd, 1); - ma_atomic_uint64_set(&pEngineNode->fadeSettings.fadeLengthInFrames, (~(ma_uint64)0)); - ma_atomic_uint64_set(&pEngineNode->fadeSettings.absoluteGlobalTimeInFrames, (~(ma_uint64)0)); /* <-- Indicates that the fade should start immediately. */ - - channelsIn = (pConfig->channelsIn != 0) ? pConfig->channelsIn : ma_engine_get_channels(pConfig->pEngine); - channelsOut = (pConfig->channelsOut != 0) ? pConfig->channelsOut : ma_engine_get_channels(pConfig->pEngine); - - /* - If the sample rate of the sound is different to the engine, make sure pitching is enabled so that the resampler - is activated. Not doing this will result in the sound not being resampled if MA_SOUND_FLAG_NO_PITCH is used. - */ - if (pEngineNode->sampleRate != ma_engine_get_sample_rate(pEngineNode->pEngine)) { - pEngineNode->isPitchDisabled = MA_FALSE; - } - - - /* Base node. */ - baseNodeConfig = ma_engine_node_base_node_config_init(pConfig); - baseNodeConfig.pInputChannels = &channelsIn; - baseNodeConfig.pOutputChannels = &channelsOut; - - result = ma_node_init_preallocated(&pConfig->pEngine->nodeGraph, &baseNodeConfig, ma_offset_ptr(pHeap, heapLayout.baseNodeOffset), &pEngineNode->baseNode); - if (result != MA_SUCCESS) { - goto error0; - } - - - /* - We can now initialize the effects we need in order to implement the engine node. There's a - defined order of operations here, mainly centered around when we convert our channels from the - data source's native channel count to the engine's channel count. As a rule, we want to do as - much computation as possible before spatialization because there's a chance that will increase - the channel count, thereby increasing the amount of work needing to be done to process. - */ - - /* We'll always do resampling first. */ - resamplerConfig = ma_linear_resampler_config_init(ma_format_f32, baseNodeConfig.pInputChannels[0], pEngineNode->sampleRate, ma_engine_get_sample_rate(pEngineNode->pEngine)); - resamplerConfig.lpfOrder = 0; /* <-- Need to disable low-pass filtering for pitch shifting for now because there's cases where the biquads are becoming unstable. Need to figure out a better fix for this. */ - - result = ma_linear_resampler_init_preallocated(&resamplerConfig, ma_offset_ptr(pHeap, heapLayout.resamplerOffset), &pEngineNode->resampler); - if (result != MA_SUCCESS) { - goto error1; - } - - - /* After resampling will come the fader. */ - faderConfig = ma_fader_config_init(ma_format_f32, baseNodeConfig.pInputChannels[0], ma_engine_get_sample_rate(pEngineNode->pEngine)); - - result = ma_fader_init(&faderConfig, &pEngineNode->fader); - if (result != MA_SUCCESS) { - goto error2; - } - - - /* - Spatialization comes next. We spatialize based on the node's output channel count. It's up the caller to - ensure channels counts link up correctly in the node graph. - */ - spatializerConfig = ma_engine_node_spatializer_config_init(&baseNodeConfig); - spatializerConfig.gainSmoothTimeInFrames = pEngineNode->pEngine->gainSmoothTimeInFrames; - - if (spatializerConfig.channelsIn == 2) { - spatializerConfig.pChannelMapIn = defaultStereoChannelMap; - } - - result = ma_spatializer_init_preallocated(&spatializerConfig, ma_offset_ptr(pHeap, heapLayout.spatializerOffset), &pEngineNode->spatializer); - if (result != MA_SUCCESS) { - goto error2; - } - - - /* - After spatialization comes panning. We need to do this after spatialization because otherwise we wouldn't - be able to pan mono sounds. - */ - pannerConfig = ma_panner_config_init(ma_format_f32, baseNodeConfig.pOutputChannels[0]); - - result = ma_panner_init(&pannerConfig, &pEngineNode->panner); - if (result != MA_SUCCESS) { - goto error3; - } - - - /* We'll need a gainer for smoothing out volume changes if we have a non-zero smooth time. We apply this before converting to the output channel count. */ - if (pConfig->volumeSmoothTimeInPCMFrames > 0) { - gainerConfig = ma_gainer_config_init(channelsIn, pConfig->volumeSmoothTimeInPCMFrames); - - result = ma_gainer_init_preallocated(&gainerConfig, ma_offset_ptr(pHeap, heapLayout.gainerOffset), &pEngineNode->volumeGainer); - if (result != MA_SUCCESS) { - goto error3; - } - } - - - return MA_SUCCESS; - - /* No need for allocation callbacks here because we use a preallocated heap. */ -error3: ma_spatializer_uninit(&pEngineNode->spatializer, NULL); -error2: ma_linear_resampler_uninit(&pEngineNode->resampler, NULL); -error1: ma_node_uninit(&pEngineNode->baseNode, NULL); -error0: return result; -} - -MA_API ma_result ma_engine_node_init(const ma_engine_node_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_engine_node* pEngineNode) -{ - ma_result result; - size_t heapSizeInBytes; - void* pHeap; - - result = ma_engine_node_get_heap_size(pConfig, &heapSizeInBytes); - if (result != MA_SUCCESS) { - return result; - } - - if (heapSizeInBytes > 0) { - pHeap = ma_malloc(heapSizeInBytes, pAllocationCallbacks); - if (pHeap == NULL) { - return MA_OUT_OF_MEMORY; - } - } else { - pHeap = NULL; - } - - result = ma_engine_node_init_preallocated(pConfig, pHeap, pEngineNode); - if (result != MA_SUCCESS) { - ma_free(pHeap, pAllocationCallbacks); - return result; - } - - pEngineNode->_ownsHeap = MA_TRUE; - return MA_SUCCESS; -} - -MA_API void ma_engine_node_uninit(ma_engine_node* pEngineNode, const ma_allocation_callbacks* pAllocationCallbacks) -{ - /* - The base node always needs to be uninitialized first to ensure it's detached from the graph completely before we - destroy anything that might be in the middle of being used by the processing function. - */ - ma_node_uninit(&pEngineNode->baseNode, pAllocationCallbacks); - - /* Now that the node has been uninitialized we can safely uninitialize the rest. */ - if (pEngineNode->volumeSmoothTimeInPCMFrames > 0) { - ma_gainer_uninit(&pEngineNode->volumeGainer, pAllocationCallbacks); - } - - ma_spatializer_uninit(&pEngineNode->spatializer, pAllocationCallbacks); - ma_linear_resampler_uninit(&pEngineNode->resampler, pAllocationCallbacks); - - /* Free the heap last. */ - if (pEngineNode->_ownsHeap) { - ma_free(pEngineNode->_pHeap, pAllocationCallbacks); - } -} - - -MA_API ma_sound_config ma_sound_config_init(void) -{ - return ma_sound_config_init_2(NULL); -} - -MA_API ma_sound_config ma_sound_config_init_2(ma_engine* pEngine) -{ - ma_sound_config config; - - MA_ZERO_OBJECT(&config); - - if (pEngine != NULL) { - config.monoExpansionMode = pEngine->monoExpansionMode; - } else { - config.monoExpansionMode = ma_mono_expansion_mode_default; - } - - config.rangeEndInPCMFrames = ~((ma_uint64)0); - config.loopPointEndInPCMFrames = ~((ma_uint64)0); - - return config; -} - -MA_API ma_sound_group_config ma_sound_group_config_init(void) -{ - return ma_sound_group_config_init_2(NULL); -} - -MA_API ma_sound_group_config ma_sound_group_config_init_2(ma_engine* pEngine) -{ - ma_sound_group_config config; - - MA_ZERO_OBJECT(&config); - - if (pEngine != NULL) { - config.monoExpansionMode = pEngine->monoExpansionMode; - } else { - config.monoExpansionMode = ma_mono_expansion_mode_default; - } - - return config; -} - - -MA_API ma_engine_config ma_engine_config_init(void) -{ - ma_engine_config config; - - MA_ZERO_OBJECT(&config); - config.listenerCount = 1; /* Always want at least one listener. */ - config.monoExpansionMode = ma_mono_expansion_mode_default; - - return config; -} - - -#if !defined(MA_NO_DEVICE_IO) -static void ma_engine_data_callback_internal(ma_device* pDevice, void* pFramesOut, const void* pFramesIn, ma_uint32 frameCount) -{ - ma_engine* pEngine = (ma_engine*)pDevice->pUserData; - - (void)pFramesIn; - - /* - Experiment: Try processing a resource manager job if we're on the Emscripten build. - - This serves two purposes: - - 1) It ensures jobs are actually processed at some point since we cannot guarantee that the - caller is doing the right thing and calling ma_resource_manager_process_next_job(); and - - 2) It's an attempt at working around an issue where processing jobs on the Emscripten main - loop doesn't work as well as it should. When trying to load sounds without the `DECODE` - flag or with the `ASYNC` flag, the sound data is just not able to be loaded in time - before the callback is processed. I think it's got something to do with the single- - threaded nature of Web, but I'm not entirely sure. - */ - #if !defined(MA_NO_RESOURCE_MANAGER) && defined(MA_EMSCRIPTEN) - { - if (pEngine->pResourceManager != NULL) { - if ((pEngine->pResourceManager->config.flags & MA_RESOURCE_MANAGER_FLAG_NO_THREADING) != 0) { - ma_resource_manager_process_next_job(pEngine->pResourceManager); - } - } - } - #endif - - ma_engine_read_pcm_frames(pEngine, pFramesOut, frameCount, NULL); -} - -static ma_uint32 ma_device__get_processing_size_in_frames(ma_device* pDevice) -{ - /* - The processing size is the period size. The device can have a fixed sized processing size, or - it can be decided by the backend in which case it can be variable. - */ - if (pDevice->playback.intermediaryBufferCap > 0) { - /* Using a fixed sized processing callback. */ - return pDevice->playback.intermediaryBufferCap; - } else { - /* Not using a fixed sized processing callback. Need to estimate the processing size based on the backend. */ - return pDevice->playback.internalPeriodSizeInFrames; - } -} -#endif - -MA_API ma_result ma_engine_init(const ma_engine_config* pConfig, ma_engine* pEngine) -{ - ma_result result; - ma_node_graph_config nodeGraphConfig; - ma_engine_config engineConfig; - ma_spatializer_listener_config listenerConfig; - ma_uint32 iListener; - - if (pEngine == NULL) { - return MA_INVALID_ARGS; - } - - MA_ZERO_OBJECT(pEngine); - - /* The config is allowed to be NULL in which case we use defaults for everything. */ - if (pConfig != NULL) { - engineConfig = *pConfig; - } else { - engineConfig = ma_engine_config_init(); - } - - pEngine->monoExpansionMode = engineConfig.monoExpansionMode; - pEngine->defaultVolumeSmoothTimeInPCMFrames = engineConfig.defaultVolumeSmoothTimeInPCMFrames; - pEngine->onProcess = engineConfig.onProcess; - pEngine->pProcessUserData = engineConfig.pProcessUserData; - ma_allocation_callbacks_init_copy(&pEngine->allocationCallbacks, &engineConfig.allocationCallbacks); - - #if !defined(MA_NO_RESOURCE_MANAGER) - { - pEngine->pResourceManager = engineConfig.pResourceManager; - } - #endif - - #if !defined(MA_NO_DEVICE_IO) - { - pEngine->pDevice = engineConfig.pDevice; - - /* If we don't have a device, we need one. */ - if (pEngine->pDevice == NULL && engineConfig.noDevice == MA_FALSE) { - ma_device_config deviceConfig; - - pEngine->pDevice = (ma_device*)ma_malloc(sizeof(*pEngine->pDevice), &pEngine->allocationCallbacks); - if (pEngine->pDevice == NULL) { - return MA_OUT_OF_MEMORY; - } - - deviceConfig = ma_device_config_init(ma_device_type_playback); - deviceConfig.playback.pDeviceID = engineConfig.pPlaybackDeviceID; - deviceConfig.playback.format = ma_format_f32; - deviceConfig.playback.channels = engineConfig.channels; - deviceConfig.sampleRate = engineConfig.sampleRate; - deviceConfig.dataCallback = (engineConfig.dataCallback != NULL) ? engineConfig.dataCallback : ma_engine_data_callback_internal; - deviceConfig.pUserData = pEngine; - deviceConfig.notificationCallback = engineConfig.notificationCallback; - deviceConfig.periodSizeInFrames = engineConfig.periodSizeInFrames; - deviceConfig.periodSizeInMilliseconds = engineConfig.periodSizeInMilliseconds; - deviceConfig.noPreSilencedOutputBuffer = MA_TRUE; /* We'll always be outputting to every frame in the callback so there's no need for a pre-silenced buffer. */ - deviceConfig.noClip = MA_TRUE; /* The engine will do clipping itself. */ - - if (engineConfig.pContext == NULL) { - ma_context_config contextConfig = ma_context_config_init(); - contextConfig.allocationCallbacks = pEngine->allocationCallbacks; - contextConfig.pLog = engineConfig.pLog; - - /* If the engine config does not specify a log, use the resource manager's if we have one. */ - #ifndef MA_NO_RESOURCE_MANAGER - { - if (contextConfig.pLog == NULL && engineConfig.pResourceManager != NULL) { - contextConfig.pLog = ma_resource_manager_get_log(engineConfig.pResourceManager); - } - } - #endif - - result = ma_device_init_ex(NULL, 0, &contextConfig, &deviceConfig, pEngine->pDevice); - } else { - result = ma_device_init(engineConfig.pContext, &deviceConfig, pEngine->pDevice); - } - - if (result != MA_SUCCESS) { - ma_free(pEngine->pDevice, &pEngine->allocationCallbacks); - pEngine->pDevice = NULL; - return result; - } - - pEngine->ownsDevice = MA_TRUE; - } - - /* Update the channel count and sample rate of the engine config so we can reference it below. */ - if (pEngine->pDevice != NULL) { - engineConfig.channels = pEngine->pDevice->playback.channels; - engineConfig.sampleRate = pEngine->pDevice->sampleRate; - - /* - The processing size used by the engine is determined by engineConfig.periodSizeInFrames. We want - to make this equal to what the device is using for it's period size. If we don't do that, it's - possible that the node graph will split it's processing into multiple passes which can introduce - glitching. - */ - engineConfig.periodSizeInFrames = ma_device__get_processing_size_in_frames(pEngine->pDevice); - } - } - #endif - - if (engineConfig.channels == 0 || engineConfig.sampleRate == 0) { - return MA_INVALID_ARGS; - } - - pEngine->sampleRate = engineConfig.sampleRate; - - /* The engine always uses either the log that was passed into the config, or the context's log is available. */ - if (engineConfig.pLog != NULL) { - pEngine->pLog = engineConfig.pLog; - } else { - #if !defined(MA_NO_DEVICE_IO) - { - pEngine->pLog = ma_device_get_log(pEngine->pDevice); - } - #else - { - pEngine->pLog = NULL; - } - #endif - } - - - /* The engine is a node graph. This needs to be initialized after we have the device so we can determine the channel count. */ - nodeGraphConfig = ma_node_graph_config_init(engineConfig.channels); - nodeGraphConfig.processingSizeInFrames = engineConfig.periodSizeInFrames; - nodeGraphConfig.preMixStackSizeInBytes = engineConfig.preMixStackSizeInBytes; - - result = ma_node_graph_init(&nodeGraphConfig, &pEngine->allocationCallbacks, &pEngine->nodeGraph); - if (result != MA_SUCCESS) { - goto on_error_1; - } - - - /* We need at least one listener. */ - if (engineConfig.listenerCount == 0) { - engineConfig.listenerCount = 1; - } - - if (engineConfig.listenerCount > MA_ENGINE_MAX_LISTENERS) { - result = MA_INVALID_ARGS; /* Too many listeners. */ - goto on_error_1; - } - - for (iListener = 0; iListener < engineConfig.listenerCount; iListener += 1) { - listenerConfig = ma_spatializer_listener_config_init(ma_node_graph_get_channels(&pEngine->nodeGraph)); - - /* - If we're using a device, use the device's channel map for the listener. Otherwise just use - miniaudio's default channel map. - */ - #if !defined(MA_NO_DEVICE_IO) - { - if (pEngine->pDevice != NULL) { - /* - Temporarily disabled. There is a subtle bug here where front-left and front-right - will be used by the device's channel map, but this is not what we want to use for - spatialization. Instead we want to use side-left and side-right. I need to figure - out a better solution for this. For now, disabling the use of device channel maps. - */ - /*listenerConfig.pChannelMapOut = pEngine->pDevice->playback.channelMap;*/ - } - } - #endif - - result = ma_spatializer_listener_init(&listenerConfig, &pEngine->allocationCallbacks, &pEngine->listeners[iListener]); /* TODO: Change this to a pre-allocated heap. */ - if (result != MA_SUCCESS) { - goto on_error_2; - } - - pEngine->listenerCount += 1; - } - - - /* Gain smoothing for spatialized sounds. */ - pEngine->gainSmoothTimeInFrames = engineConfig.gainSmoothTimeInFrames; - if (pEngine->gainSmoothTimeInFrames == 0) { - ma_uint32 gainSmoothTimeInMilliseconds = engineConfig.gainSmoothTimeInMilliseconds; - if (gainSmoothTimeInMilliseconds == 0) { - gainSmoothTimeInMilliseconds = 8; - } - - pEngine->gainSmoothTimeInFrames = (gainSmoothTimeInMilliseconds * ma_engine_get_sample_rate(pEngine)) / 1000; /* 8ms by default. */ - } - - - /* We need a resource manager. */ - #ifndef MA_NO_RESOURCE_MANAGER - { - if (pEngine->pResourceManager == NULL) { - ma_resource_manager_config resourceManagerConfig; - - pEngine->pResourceManager = (ma_resource_manager*)ma_malloc(sizeof(*pEngine->pResourceManager), &pEngine->allocationCallbacks); - if (pEngine->pResourceManager == NULL) { - result = MA_OUT_OF_MEMORY; - goto on_error_2; - } - - resourceManagerConfig = ma_resource_manager_config_init(); - resourceManagerConfig.pLog = pEngine->pLog; /* Always use the engine's log for internally-managed resource managers. */ - resourceManagerConfig.decodedFormat = ma_format_f32; - resourceManagerConfig.decodedChannels = 0; /* Leave the decoded channel count as 0 so we can get good spatialization. */ - resourceManagerConfig.decodedSampleRate = ma_engine_get_sample_rate(pEngine); - ma_allocation_callbacks_init_copy(&resourceManagerConfig.allocationCallbacks, &pEngine->allocationCallbacks); - resourceManagerConfig.pVFS = engineConfig.pResourceManagerVFS; - - /* The Emscripten build cannot use threads unless it's targeting pthreads. */ - #if defined(MA_EMSCRIPTEN) && !defined(__EMSCRIPTEN_PTHREADS__) - { - resourceManagerConfig.jobThreadCount = 0; - resourceManagerConfig.flags |= MA_RESOURCE_MANAGER_FLAG_NO_THREADING; - } - #endif - - result = ma_resource_manager_init(&resourceManagerConfig, pEngine->pResourceManager); - if (result != MA_SUCCESS) { - goto on_error_3; - } - - pEngine->ownsResourceManager = MA_TRUE; - } - } - #endif - - /* Setup some stuff for inlined sounds. That is sounds played with ma_engine_play_sound(). */ - pEngine->inlinedSoundLock = 0; - pEngine->pInlinedSoundHead = NULL; - - /* Start the engine if required. This should always be the last step. */ - #if !defined(MA_NO_DEVICE_IO) - { - if (engineConfig.noAutoStart == MA_FALSE && pEngine->pDevice != NULL) { - result = ma_engine_start(pEngine); - if (result != MA_SUCCESS) { - goto on_error_4; /* Failed to start the engine. */ - } - } - } - #endif - - return MA_SUCCESS; - -#if !defined(MA_NO_DEVICE_IO) -on_error_4: -#endif -#if !defined(MA_NO_RESOURCE_MANAGER) -on_error_3: - if (pEngine->ownsResourceManager) { - ma_free(pEngine->pResourceManager, &pEngine->allocationCallbacks); - } -#endif /* MA_NO_RESOURCE_MANAGER */ -on_error_2: - for (iListener = 0; iListener < pEngine->listenerCount; iListener += 1) { - ma_spatializer_listener_uninit(&pEngine->listeners[iListener], &pEngine->allocationCallbacks); - } - - ma_node_graph_uninit(&pEngine->nodeGraph, &pEngine->allocationCallbacks); -on_error_1: - #if !defined(MA_NO_DEVICE_IO) - { - if (pEngine->ownsDevice) { - ma_device_uninit(pEngine->pDevice); - ma_free(pEngine->pDevice, &pEngine->allocationCallbacks); - } - } - #endif - - return result; -} - -MA_API void ma_engine_uninit(ma_engine* pEngine) -{ - ma_uint32 iListener; - - if (pEngine == NULL) { - return; - } - - /* The device must be uninitialized before the node graph to ensure the audio thread doesn't try accessing it. */ - #if !defined(MA_NO_DEVICE_IO) - { - if (pEngine->ownsDevice) { - ma_device_uninit(pEngine->pDevice); - ma_free(pEngine->pDevice, &pEngine->allocationCallbacks); - } else { - if (pEngine->pDevice != NULL) { - ma_device_stop(pEngine->pDevice); - } - } - } - #endif - - /* - All inlined sounds need to be deleted. I'm going to use a lock here just to future proof in case - I want to do some kind of garbage collection later on. - */ - ma_spinlock_lock(&pEngine->inlinedSoundLock); - { - for (;;) { - ma_sound_inlined* pSoundToDelete = pEngine->pInlinedSoundHead; - if (pSoundToDelete == NULL) { - break; /* Done. */ - } - - pEngine->pInlinedSoundHead = pSoundToDelete->pNext; - - ma_sound_uninit(&pSoundToDelete->sound); - ma_free(pSoundToDelete, &pEngine->allocationCallbacks); - } - } - ma_spinlock_unlock(&pEngine->inlinedSoundLock); - - for (iListener = 0; iListener < pEngine->listenerCount; iListener += 1) { - ma_spatializer_listener_uninit(&pEngine->listeners[iListener], &pEngine->allocationCallbacks); - } - - /* Make sure the node graph is uninitialized after the audio thread has been shutdown to prevent accessing of the node graph after being uninitialized. */ - ma_node_graph_uninit(&pEngine->nodeGraph, &pEngine->allocationCallbacks); - - /* Uninitialize the resource manager last to ensure we don't have a thread still trying to access it. */ -#ifndef MA_NO_RESOURCE_MANAGER - if (pEngine->ownsResourceManager) { - ma_resource_manager_uninit(pEngine->pResourceManager); - ma_free(pEngine->pResourceManager, &pEngine->allocationCallbacks); - } -#endif -} - -MA_API ma_result ma_engine_read_pcm_frames(ma_engine* pEngine, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead) -{ - ma_result result; - ma_uint64 framesRead = 0; - - if (pFramesRead != NULL) { - *pFramesRead = 0; - } - - result = ma_node_graph_read_pcm_frames(&pEngine->nodeGraph, pFramesOut, frameCount, &framesRead); - if (result != MA_SUCCESS) { - return result; - } - - if (pFramesRead != NULL) { - *pFramesRead = framesRead; - } - - if (pEngine->onProcess) { - pEngine->onProcess(pEngine->pProcessUserData, (float*)pFramesOut, framesRead); /* Safe cast to float* because the engine always works on floating point samples. */ - } - - return MA_SUCCESS; -} - -MA_API ma_node_graph* ma_engine_get_node_graph(ma_engine* pEngine) -{ - if (pEngine == NULL) { - return NULL; - } - - return &pEngine->nodeGraph; -} - -#if !defined(MA_NO_RESOURCE_MANAGER) -MA_API ma_resource_manager* ma_engine_get_resource_manager(ma_engine* pEngine) -{ - if (pEngine == NULL) { - return NULL; - } - - #if !defined(MA_NO_RESOURCE_MANAGER) - { - return pEngine->pResourceManager; - } - #else - { - return NULL; - } - #endif -} -#endif - -MA_API ma_device* ma_engine_get_device(ma_engine* pEngine) -{ - if (pEngine == NULL) { - return NULL; - } - - #if !defined(MA_NO_DEVICE_IO) - { - return pEngine->pDevice; - } - #else - { - return NULL; - } - #endif -} - -MA_API ma_log* ma_engine_get_log(ma_engine* pEngine) -{ - if (pEngine == NULL) { - return NULL; - } - - if (pEngine->pLog != NULL) { - return pEngine->pLog; - } else { - #if !defined(MA_NO_DEVICE_IO) - { - return ma_device_get_log(ma_engine_get_device(pEngine)); - } - #else - { - return NULL; - } - #endif - } -} - -MA_API ma_node* ma_engine_get_endpoint(ma_engine* pEngine) -{ - return ma_node_graph_get_endpoint(&pEngine->nodeGraph); -} - -MA_API ma_uint64 ma_engine_get_time_in_pcm_frames(const ma_engine* pEngine) -{ - return ma_node_graph_get_time(&pEngine->nodeGraph); -} - -MA_API ma_uint64 ma_engine_get_time_in_milliseconds(const ma_engine* pEngine) -{ - return ma_engine_get_time_in_pcm_frames(pEngine) * 1000 / ma_engine_get_sample_rate(pEngine); -} - -MA_API ma_result ma_engine_set_time_in_pcm_frames(ma_engine* pEngine, ma_uint64 globalTime) -{ - return ma_node_graph_set_time(&pEngine->nodeGraph, globalTime); -} - -MA_API ma_result ma_engine_set_time_in_milliseconds(ma_engine* pEngine, ma_uint64 globalTime) -{ - return ma_engine_set_time_in_pcm_frames(pEngine, globalTime * ma_engine_get_sample_rate(pEngine) / 1000); -} - -MA_API ma_uint64 ma_engine_get_time(const ma_engine* pEngine) -{ - return ma_engine_get_time_in_pcm_frames(pEngine); -} - -MA_API ma_result ma_engine_set_time(ma_engine* pEngine, ma_uint64 globalTime) -{ - return ma_engine_set_time_in_pcm_frames(pEngine, globalTime); -} - -MA_API ma_uint32 ma_engine_get_channels(const ma_engine* pEngine) -{ - return ma_node_graph_get_channels(&pEngine->nodeGraph); -} - -MA_API ma_uint32 ma_engine_get_sample_rate(const ma_engine* pEngine) -{ - if (pEngine == NULL) { - return 0; - } - - return pEngine->sampleRate; -} - - -MA_API ma_result ma_engine_start(ma_engine* pEngine) -{ - ma_result result; - - if (pEngine == NULL) { - return MA_INVALID_ARGS; - } - - #if !defined(MA_NO_DEVICE_IO) - { - if (pEngine->pDevice != NULL) { - result = ma_device_start(pEngine->pDevice); - } else { - result = MA_INVALID_OPERATION; /* The engine is running without a device which means there's no real notion of "starting" the engine. */ - } - } - #else - { - result = MA_INVALID_OPERATION; /* Device IO is disabled, so there's no real notion of "starting" the engine. */ - } - #endif - - if (result != MA_SUCCESS) { - return result; - } - - return MA_SUCCESS; -} - -MA_API ma_result ma_engine_stop(ma_engine* pEngine) -{ - ma_result result; - - if (pEngine == NULL) { - return MA_INVALID_ARGS; - } - - #if !defined(MA_NO_DEVICE_IO) - { - if (pEngine->pDevice != NULL) { - result = ma_device_stop(pEngine->pDevice); - } else { - result = MA_INVALID_OPERATION; /* The engine is running without a device which means there's no real notion of "stopping" the engine. */ - } - } - #else - { - result = MA_INVALID_OPERATION; /* Device IO is disabled, so there's no real notion of "stopping" the engine. */ - } - #endif - - if (result != MA_SUCCESS) { - return result; - } - - return MA_SUCCESS; -} - -MA_API ma_result ma_engine_set_volume(ma_engine* pEngine, float volume) -{ - if (pEngine == NULL) { - return MA_INVALID_ARGS; - } - - return ma_node_set_output_bus_volume(ma_node_graph_get_endpoint(&pEngine->nodeGraph), 0, volume); -} - -MA_API float ma_engine_get_volume(ma_engine* pEngine) -{ - if (pEngine == NULL) { - return 0; - } - - return ma_node_get_output_bus_volume(ma_node_graph_get_endpoint(&pEngine->nodeGraph), 0); -} - -MA_API ma_result ma_engine_set_gain_db(ma_engine* pEngine, float gainDB) -{ - return ma_engine_set_volume(pEngine, ma_volume_db_to_linear(gainDB)); -} - -MA_API float ma_engine_get_gain_db(ma_engine* pEngine) -{ - return ma_volume_linear_to_db(ma_engine_get_volume(pEngine)); -} - - -MA_API ma_uint32 ma_engine_get_listener_count(const ma_engine* pEngine) -{ - if (pEngine == NULL) { - return 0; - } - - return pEngine->listenerCount; -} - -MA_API ma_uint32 ma_engine_find_closest_listener(const ma_engine* pEngine, float absolutePosX, float absolutePosY, float absolutePosZ) -{ - ma_uint32 iListener; - ma_uint32 iListenerClosest; - float closestLen2 = MA_FLT_MAX; - - if (pEngine == NULL || pEngine->listenerCount == 1) { - return 0; - } - - iListenerClosest = 0; - for (iListener = 0; iListener < pEngine->listenerCount; iListener += 1) { - if (ma_engine_listener_is_enabled(pEngine, iListener)) { - float len2 = ma_vec3f_len2(ma_vec3f_sub(ma_spatializer_listener_get_position(&pEngine->listeners[iListener]), ma_vec3f_init_3f(absolutePosX, absolutePosY, absolutePosZ))); - if (closestLen2 > len2) { - closestLen2 = len2; - iListenerClosest = iListener; - } - } - } - - MA_ASSERT(iListenerClosest < 255); - return iListenerClosest; -} - -MA_API void ma_engine_listener_set_position(ma_engine* pEngine, ma_uint32 listenerIndex, float x, float y, float z) -{ - if (pEngine == NULL || listenerIndex >= pEngine->listenerCount) { - return; - } - - ma_spatializer_listener_set_position(&pEngine->listeners[listenerIndex], x, y, z); -} - -MA_API ma_vec3f ma_engine_listener_get_position(const ma_engine* pEngine, ma_uint32 listenerIndex) -{ - if (pEngine == NULL || listenerIndex >= pEngine->listenerCount) { - return ma_vec3f_init_3f(0, 0, 0); - } - - return ma_spatializer_listener_get_position(&pEngine->listeners[listenerIndex]); -} - -MA_API void ma_engine_listener_set_direction(ma_engine* pEngine, ma_uint32 listenerIndex, float x, float y, float z) -{ - if (pEngine == NULL || listenerIndex >= pEngine->listenerCount) { - return; - } - - ma_spatializer_listener_set_direction(&pEngine->listeners[listenerIndex], x, y, z); -} - -MA_API ma_vec3f ma_engine_listener_get_direction(const ma_engine* pEngine, ma_uint32 listenerIndex) -{ - if (pEngine == NULL || listenerIndex >= pEngine->listenerCount) { - return ma_vec3f_init_3f(0, 0, -1); - } - - return ma_spatializer_listener_get_direction(&pEngine->listeners[listenerIndex]); -} - -MA_API void ma_engine_listener_set_velocity(ma_engine* pEngine, ma_uint32 listenerIndex, float x, float y, float z) -{ - if (pEngine == NULL || listenerIndex >= pEngine->listenerCount) { - return; - } - - ma_spatializer_listener_set_velocity(&pEngine->listeners[listenerIndex], x, y, z); -} - -MA_API ma_vec3f ma_engine_listener_get_velocity(const ma_engine* pEngine, ma_uint32 listenerIndex) -{ - if (pEngine == NULL || listenerIndex >= pEngine->listenerCount) { - return ma_vec3f_init_3f(0, 0, 0); - } - - return ma_spatializer_listener_get_velocity(&pEngine->listeners[listenerIndex]); -} - -MA_API void ma_engine_listener_set_cone(ma_engine* pEngine, ma_uint32 listenerIndex, float innerAngleInRadians, float outerAngleInRadians, float outerGain) -{ - if (pEngine == NULL || listenerIndex >= pEngine->listenerCount) { - return; - } - - ma_spatializer_listener_set_cone(&pEngine->listeners[listenerIndex], innerAngleInRadians, outerAngleInRadians, outerGain); -} - -MA_API void ma_engine_listener_get_cone(const ma_engine* pEngine, ma_uint32 listenerIndex, float* pInnerAngleInRadians, float* pOuterAngleInRadians, float* pOuterGain) -{ - if (pInnerAngleInRadians != NULL) { - *pInnerAngleInRadians = 0; - } - - if (pOuterAngleInRadians != NULL) { - *pOuterAngleInRadians = 0; - } - - if (pOuterGain != NULL) { - *pOuterGain = 0; - } - - if (pEngine == NULL || listenerIndex >= pEngine->listenerCount) { - return; - } - - ma_spatializer_listener_get_cone(&pEngine->listeners[listenerIndex], pInnerAngleInRadians, pOuterAngleInRadians, pOuterGain); -} - -MA_API void ma_engine_listener_set_world_up(ma_engine* pEngine, ma_uint32 listenerIndex, float x, float y, float z) -{ - if (pEngine == NULL || listenerIndex >= pEngine->listenerCount) { - return; - } - - ma_spatializer_listener_set_world_up(&pEngine->listeners[listenerIndex], x, y, z); -} - -MA_API ma_vec3f ma_engine_listener_get_world_up(const ma_engine* pEngine, ma_uint32 listenerIndex) -{ - if (pEngine == NULL || listenerIndex >= pEngine->listenerCount) { - return ma_vec3f_init_3f(0, 1, 0); - } - - return ma_spatializer_listener_get_world_up(&pEngine->listeners[listenerIndex]); -} - -MA_API void ma_engine_listener_set_enabled(ma_engine* pEngine, ma_uint32 listenerIndex, ma_bool32 isEnabled) -{ - if (pEngine == NULL || listenerIndex >= pEngine->listenerCount) { - return; - } - - ma_spatializer_listener_set_enabled(&pEngine->listeners[listenerIndex], isEnabled); -} - -MA_API ma_bool32 ma_engine_listener_is_enabled(const ma_engine* pEngine, ma_uint32 listenerIndex) -{ - if (pEngine == NULL || listenerIndex >= pEngine->listenerCount) { - return MA_FALSE; - } - - return ma_spatializer_listener_is_enabled(&pEngine->listeners[listenerIndex]); -} - - -#ifndef MA_NO_RESOURCE_MANAGER -MA_API ma_result ma_engine_play_sound_ex(ma_engine* pEngine, const char* pFilePath, ma_node* pNode, ma_uint32 nodeInputBusIndex) -{ - ma_result result = MA_SUCCESS; - ma_sound_inlined* pSound = NULL; - ma_sound_inlined* pNextSound = NULL; - - if (pEngine == NULL || pFilePath == NULL) { - return MA_INVALID_ARGS; - } - - /* Attach to the endpoint node if nothing is specified. */ - if (pNode == NULL) { - pNode = ma_node_graph_get_endpoint(&pEngine->nodeGraph); - nodeInputBusIndex = 0; - } - - /* - We want to check if we can recycle an already-allocated inlined sound. Since this is just a - helper I'm not *too* concerned about performance here and I'm happy to use a lock to keep - the implementation simple. Maybe this can be optimized later if there's enough demand, but - if this function is being used it probably means the caller doesn't really care too much. - - What we do is check the atEnd flag. When this is true, we can recycle the sound. Otherwise - we just keep iterating. If we reach the end without finding a sound to recycle we just - allocate a new one. This doesn't scale well for a massive number of sounds being played - simultaneously as we don't ever actually free the sound objects. Some kind of garbage - collection routine might be valuable for this which I'll think about. - */ - ma_spinlock_lock(&pEngine->inlinedSoundLock); - { - ma_uint32 soundFlags = 0; - - for (pNextSound = pEngine->pInlinedSoundHead; pNextSound != NULL; pNextSound = pNextSound->pNext) { - if (ma_sound_at_end(&pNextSound->sound)) { - /* - The sound is at the end which means it's available for recycling. All we need to do - is uninitialize it and reinitialize it. All we're doing is recycling memory. - */ - pSound = pNextSound; - ma_atomic_fetch_sub_32(&pEngine->inlinedSoundCount, 1); - break; - } - } - - if (pSound != NULL) { - /* - We actually want to detach the sound from the list here. The reason is because we want the sound - to be in a consistent state at the non-recycled case to simplify the logic below. - */ - if (pEngine->pInlinedSoundHead == pSound) { - pEngine->pInlinedSoundHead = pSound->pNext; - } - - if (pSound->pPrev != NULL) { - pSound->pPrev->pNext = pSound->pNext; - } - if (pSound->pNext != NULL) { - pSound->pNext->pPrev = pSound->pPrev; - } - - /* Now the previous sound needs to be uninitialized. */ - ma_sound_uninit(&pNextSound->sound); - } else { - /* No sound available for recycling. Allocate one now. */ - pSound = (ma_sound_inlined*)ma_malloc(sizeof(*pSound), &pEngine->allocationCallbacks); - } - - if (pSound != NULL) { /* Safety check for the allocation above. */ - /* - At this point we should have memory allocated for the inlined sound. We just need - to initialize it like a normal sound now. - */ - soundFlags |= MA_SOUND_FLAG_ASYNC; /* For inlined sounds we don't want to be sitting around waiting for stuff to load so force an async load. */ - soundFlags |= MA_SOUND_FLAG_NO_DEFAULT_ATTACHMENT; /* We want specific control over where the sound is attached in the graph. We'll attach it manually just before playing the sound. */ - soundFlags |= MA_SOUND_FLAG_NO_PITCH; /* Pitching isn't usable with inlined sounds, so disable it to save on speed. */ - soundFlags |= MA_SOUND_FLAG_NO_SPATIALIZATION; /* Not currently doing spatialization with inlined sounds, but this might actually change later. For now disable spatialization. Will be removed if we ever add support for spatialization here. */ - - result = ma_sound_init_from_file(pEngine, pFilePath, soundFlags, NULL, NULL, &pSound->sound); - if (result == MA_SUCCESS) { - /* Now attach the sound to the graph. */ - result = ma_node_attach_output_bus(pSound, 0, pNode, nodeInputBusIndex); - if (result == MA_SUCCESS) { - /* At this point the sound should be loaded and we can go ahead and add it to the list. The new item becomes the new head. */ - pSound->pNext = pEngine->pInlinedSoundHead; - pSound->pPrev = NULL; - - pEngine->pInlinedSoundHead = pSound; /* <-- This is what attaches the sound to the list. */ - if (pSound->pNext != NULL) { - pSound->pNext->pPrev = pSound; - } - } else { - ma_free(pSound, &pEngine->allocationCallbacks); - } - } else { - ma_free(pSound, &pEngine->allocationCallbacks); - } - } else { - result = MA_OUT_OF_MEMORY; - } - } - ma_spinlock_unlock(&pEngine->inlinedSoundLock); - - if (result != MA_SUCCESS) { - return result; - } - - /* Finally we can start playing the sound. */ - result = ma_sound_start(&pSound->sound); - if (result != MA_SUCCESS) { - /* Failed to start the sound. We need to mark it for recycling and return an error. */ - ma_atomic_exchange_32(&pSound->sound.atEnd, MA_TRUE); - return result; - } - - ma_atomic_fetch_add_32(&pEngine->inlinedSoundCount, 1); - return result; -} - -MA_API ma_result ma_engine_play_sound(ma_engine* pEngine, const char* pFilePath, ma_sound_group* pGroup) -{ - return ma_engine_play_sound_ex(pEngine, pFilePath, pGroup, 0); -} -#endif - - -static ma_result ma_sound_preinit(ma_engine* pEngine, ma_sound* pSound) -{ - if (pSound == NULL) { - return MA_INVALID_ARGS; - } - - MA_ZERO_OBJECT(pSound); - pSound->seekTarget = MA_SEEK_TARGET_NONE; - - if (pEngine == NULL) { - return MA_INVALID_ARGS; - } - - return MA_SUCCESS; -} - -static ma_result ma_sound_init_from_data_source_internal(ma_engine* pEngine, const ma_sound_config* pConfig, ma_sound* pSound) -{ - ma_result result; - ma_engine_node_config engineNodeConfig; - ma_engine_node_type type; /* Will be set to ma_engine_node_type_group if no data source is specified. */ - - /* Do not clear pSound to zero here - that's done at a higher level with ma_sound_preinit(). */ - MA_ASSERT(pEngine != NULL); - MA_ASSERT(pSound != NULL); - - if (pConfig == NULL) { - return MA_INVALID_ARGS; - } - - pSound->pDataSource = pConfig->pDataSource; - - if (pConfig->pDataSource != NULL) { - type = ma_engine_node_type_sound; - } else { - type = ma_engine_node_type_group; - } - - /* - Sounds are engine nodes. Before we can initialize this we need to determine the channel count. - If we can't do this we need to abort. It's up to the caller to ensure they're using a data - source that provides this information upfront. - */ - engineNodeConfig = ma_engine_node_config_init(pEngine, type, pConfig->flags); - engineNodeConfig.channelsIn = pConfig->channelsIn; - engineNodeConfig.channelsOut = pConfig->channelsOut; - engineNodeConfig.volumeSmoothTimeInPCMFrames = pConfig->volumeSmoothTimeInPCMFrames; - engineNodeConfig.monoExpansionMode = pConfig->monoExpansionMode; - - if (engineNodeConfig.volumeSmoothTimeInPCMFrames == 0) { - engineNodeConfig.volumeSmoothTimeInPCMFrames = pEngine->defaultVolumeSmoothTimeInPCMFrames; - } - - /* If we're loading from a data source the input channel count needs to be the data source's native channel count. */ - if (pConfig->pDataSource != NULL) { - result = ma_data_source_get_data_format(pConfig->pDataSource, NULL, &engineNodeConfig.channelsIn, &engineNodeConfig.sampleRate, NULL, 0); - if (result != MA_SUCCESS) { - return result; /* Failed to retrieve the channel count. */ - } - - if (engineNodeConfig.channelsIn == 0) { - return MA_INVALID_OPERATION; /* Invalid channel count. */ - } - - if (engineNodeConfig.channelsOut == MA_SOUND_SOURCE_CHANNEL_COUNT) { - engineNodeConfig.channelsOut = engineNodeConfig.channelsIn; - } - } - - - /* Getting here means we should have a valid channel count and we can initialize the engine node. */ - result = ma_engine_node_init(&engineNodeConfig, &pEngine->allocationCallbacks, &pSound->engineNode); - if (result != MA_SUCCESS) { - return result; - } - - /* If no attachment is specified, attach the sound straight to the endpoint. */ - if (pConfig->pInitialAttachment == NULL) { - /* No group. Attach straight to the endpoint by default, unless the caller has requested that it not. */ - if ((pConfig->flags & MA_SOUND_FLAG_NO_DEFAULT_ATTACHMENT) == 0) { - result = ma_node_attach_output_bus(pSound, 0, ma_node_graph_get_endpoint(&pEngine->nodeGraph), 0); - } - } else { - /* An attachment is specified. Attach to it by default. The sound has only a single output bus, and the config will specify which input bus to attach to. */ - result = ma_node_attach_output_bus(pSound, 0, pConfig->pInitialAttachment, pConfig->initialAttachmentInputBusIndex); - } - - if (result != MA_SUCCESS) { - ma_engine_node_uninit(&pSound->engineNode, &pEngine->allocationCallbacks); - return result; - } - - - /* Apply initial range and looping state to the data source if applicable. */ - if (pConfig->rangeBegInPCMFrames != 0 || pConfig->rangeEndInPCMFrames != ~((ma_uint64)0)) { - ma_data_source_set_range_in_pcm_frames(ma_sound_get_data_source(pSound), pConfig->rangeBegInPCMFrames, pConfig->rangeEndInPCMFrames); - } - - if (pConfig->loopPointBegInPCMFrames != 0 || pConfig->loopPointEndInPCMFrames != ~((ma_uint64)0)) { - ma_data_source_set_range_in_pcm_frames(ma_sound_get_data_source(pSound), pConfig->loopPointBegInPCMFrames, pConfig->loopPointEndInPCMFrames); - } - - ma_sound_set_looping(pSound, pConfig->isLooping || ((pConfig->flags & MA_SOUND_FLAG_LOOPING) != 0)); - - return MA_SUCCESS; -} - -#ifndef MA_NO_RESOURCE_MANAGER -MA_API ma_result ma_sound_init_from_file_internal(ma_engine* pEngine, const ma_sound_config* pConfig, ma_sound* pSound) -{ - ma_result result = MA_SUCCESS; - ma_uint32 flags; - ma_sound_config config; - ma_resource_manager_pipeline_notifications notifications; - - /* - The engine requires knowledge of the channel count of the underlying data source before it can - initialize the sound. Therefore, we need to make the resource manager wait until initialization - of the underlying data source to be initialized so we can get access to the channel count. To - do this, the MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_WAIT_INIT is forced. - - Because we're initializing the data source before the sound, there's a chance the notification - will get triggered before this function returns. This is OK, so long as the caller is aware of - it and can avoid accessing the sound from within the notification. - */ - flags = pConfig->flags | MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_WAIT_INIT; - if (pConfig->isLooping) { - flags |= MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_LOOPING; - } - - pSound->pResourceManagerDataSource = (ma_resource_manager_data_source*)ma_malloc(sizeof(*pSound->pResourceManagerDataSource), &pEngine->allocationCallbacks); - if (pSound->pResourceManagerDataSource == NULL) { - return MA_OUT_OF_MEMORY; - } - - /* Removed in 0.12. Set pDoneFence on the notifications. */ - notifications = pConfig->initNotifications; - if (pConfig->pDoneFence != NULL && notifications.done.pFence == NULL) { - notifications.done.pFence = pConfig->pDoneFence; - } - - /* - We must wrap everything around the fence if one was specified. This ensures ma_fence_wait() does - not return prematurely before the sound has finished initializing. - */ - if (notifications.done.pFence) { ma_fence_acquire(notifications.done.pFence); } - { - ma_resource_manager_data_source_config resourceManagerDataSourceConfig = ma_resource_manager_data_source_config_init(); - resourceManagerDataSourceConfig.pFilePath = pConfig->pFilePath; - resourceManagerDataSourceConfig.pFilePathW = pConfig->pFilePathW; - resourceManagerDataSourceConfig.flags = flags; - resourceManagerDataSourceConfig.pNotifications = ¬ifications; - resourceManagerDataSourceConfig.initialSeekPointInPCMFrames = pConfig->initialSeekPointInPCMFrames; - resourceManagerDataSourceConfig.rangeBegInPCMFrames = pConfig->rangeBegInPCMFrames; - resourceManagerDataSourceConfig.rangeEndInPCMFrames = pConfig->rangeEndInPCMFrames; - resourceManagerDataSourceConfig.loopPointBegInPCMFrames = pConfig->loopPointBegInPCMFrames; - resourceManagerDataSourceConfig.loopPointEndInPCMFrames = pConfig->loopPointEndInPCMFrames; - resourceManagerDataSourceConfig.isLooping = (flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_LOOPING) != 0; - - result = ma_resource_manager_data_source_init_ex(pEngine->pResourceManager, &resourceManagerDataSourceConfig, pSound->pResourceManagerDataSource); - if (result != MA_SUCCESS) { - goto done; - } - - pSound->ownsDataSource = MA_TRUE; /* <-- Important. Not setting this will result in the resource manager data source never getting uninitialized. */ - - /* We need to use a slightly customized version of the config so we'll need to make a copy. */ - config = *pConfig; - config.pFilePath = NULL; - config.pFilePathW = NULL; - config.pDataSource = pSound->pResourceManagerDataSource; - - result = ma_sound_init_from_data_source_internal(pEngine, &config, pSound); - if (result != MA_SUCCESS) { - ma_resource_manager_data_source_uninit(pSound->pResourceManagerDataSource); - ma_free(pSound->pResourceManagerDataSource, &pEngine->allocationCallbacks); - MA_ZERO_OBJECT(pSound); - goto done; - } - } -done: - if (notifications.done.pFence) { ma_fence_release(notifications.done.pFence); } - return result; -} - -MA_API ma_result ma_sound_init_from_file(ma_engine* pEngine, const char* pFilePath, ma_uint32 flags, ma_sound_group* pGroup, ma_fence* pDoneFence, ma_sound* pSound) -{ - ma_sound_config config; - - if (pFilePath == NULL) { - return MA_INVALID_ARGS; - } - - config = ma_sound_config_init_2(pEngine); - config.pFilePath = pFilePath; - config.flags = flags; - config.pInitialAttachment = pGroup; - config.pDoneFence = pDoneFence; - - return ma_sound_init_ex(pEngine, &config, pSound); -} - -MA_API ma_result ma_sound_init_from_file_w(ma_engine* pEngine, const wchar_t* pFilePath, ma_uint32 flags, ma_sound_group* pGroup, ma_fence* pDoneFence, ma_sound* pSound) -{ - ma_sound_config config; - - if (pFilePath == NULL) { - return MA_INVALID_ARGS; - } - - config = ma_sound_config_init_2(pEngine); - config.pFilePathW = pFilePath; - config.flags = flags; - config.pInitialAttachment = pGroup; - config.pDoneFence = pDoneFence; - - return ma_sound_init_ex(pEngine, &config, pSound); -} - -MA_API ma_result ma_sound_init_copy(ma_engine* pEngine, const ma_sound* pExistingSound, ma_uint32 flags, ma_sound_group* pGroup, ma_sound* pSound) -{ - ma_result result; - ma_sound_config config; - - result = ma_sound_preinit(pEngine, pSound); - if (result != MA_SUCCESS) { - return result; - } - - if (pExistingSound == NULL) { - return MA_INVALID_ARGS; - } - - /* Cloning only works for data buffers (not streams) that are loaded from the resource manager. */ - if (pExistingSound->pResourceManagerDataSource == NULL) { - return MA_INVALID_OPERATION; - } - - /* - We need to make a clone of the data source. If the data source is not a data buffer (i.e. a stream) - this will fail. - */ - pSound->pResourceManagerDataSource = (ma_resource_manager_data_source*)ma_malloc(sizeof(*pSound->pResourceManagerDataSource), &pEngine->allocationCallbacks); - if (pSound->pResourceManagerDataSource == NULL) { - return MA_OUT_OF_MEMORY; - } - - result = ma_resource_manager_data_source_init_copy(pEngine->pResourceManager, pExistingSound->pResourceManagerDataSource, pSound->pResourceManagerDataSource); - if (result != MA_SUCCESS) { - ma_free(pSound->pResourceManagerDataSource, &pEngine->allocationCallbacks); - return result; - } - - config = ma_sound_config_init_2(pEngine); - config.pDataSource = pSound->pResourceManagerDataSource; - config.flags = flags; - config.pInitialAttachment = pGroup; - config.monoExpansionMode = pExistingSound->engineNode.monoExpansionMode; - config.volumeSmoothTimeInPCMFrames = pExistingSound->engineNode.volumeSmoothTimeInPCMFrames; - - result = ma_sound_init_from_data_source_internal(pEngine, &config, pSound); - if (result != MA_SUCCESS) { - ma_resource_manager_data_source_uninit(pSound->pResourceManagerDataSource); - ma_free(pSound->pResourceManagerDataSource, &pEngine->allocationCallbacks); - MA_ZERO_OBJECT(pSound); - return result; - } - - /* Make sure the sound is marked as the owner of the data source or else it will never get uninitialized. */ - pSound->ownsDataSource = MA_TRUE; - - return MA_SUCCESS; -} -#endif - -MA_API ma_result ma_sound_init_from_data_source(ma_engine* pEngine, ma_data_source* pDataSource, ma_uint32 flags, ma_sound_group* pGroup, ma_sound* pSound) -{ - ma_sound_config config = ma_sound_config_init_2(pEngine); - config.pDataSource = pDataSource; - config.flags = flags; - config.pInitialAttachment = pGroup; - return ma_sound_init_ex(pEngine, &config, pSound); -} - -MA_API ma_result ma_sound_init_ex(ma_engine* pEngine, const ma_sound_config* pConfig, ma_sound* pSound) -{ - ma_result result; - - result = ma_sound_preinit(pEngine, pSound); - if (result != MA_SUCCESS) { - return result; - } - - if (pConfig == NULL) { - return MA_INVALID_ARGS; - } - - pSound->endCallback = pConfig->endCallback; - pSound->pEndCallbackUserData = pConfig->pEndCallbackUserData; - - /* We need to load the sound differently depending on whether or not we're loading from a file. */ -#ifndef MA_NO_RESOURCE_MANAGER - if (pConfig->pFilePath != NULL || pConfig->pFilePathW != NULL) { - return ma_sound_init_from_file_internal(pEngine, pConfig, pSound); - } else -#endif - { - /* - Getting here means we're not loading from a file. We may be loading from an already-initialized - data source, or none at all. If we aren't specifying any data source, we'll be initializing - the equivalent to a group. ma_data_source_init_from_data_source_internal() will deal with this - for us, so no special treatment required here. - */ - return ma_sound_init_from_data_source_internal(pEngine, pConfig, pSound); - } -} - -MA_API void ma_sound_uninit(ma_sound* pSound) -{ - if (pSound == NULL) { - return; - } - - /* - Always uninitialize the node first. This ensures it's detached from the graph and does not return until it has done - so which makes thread safety beyond this point trivial. - */ - ma_engine_node_uninit(&pSound->engineNode, &pSound->engineNode.pEngine->allocationCallbacks); - - /* Once the sound is detached from the group we can guarantee that it won't be referenced by the mixer thread which means it's safe for us to destroy the data source. */ -#ifndef MA_NO_RESOURCE_MANAGER - if (pSound->ownsDataSource) { - ma_resource_manager_data_source_uninit(pSound->pResourceManagerDataSource); - ma_free(pSound->pResourceManagerDataSource, &pSound->engineNode.pEngine->allocationCallbacks); - pSound->pDataSource = NULL; - } -#else - MA_ASSERT(pSound->ownsDataSource == MA_FALSE); -#endif -} - -MA_API ma_engine* ma_sound_get_engine(const ma_sound* pSound) -{ - if (pSound == NULL) { - return NULL; - } - - return pSound->engineNode.pEngine; -} - -MA_API ma_data_source* ma_sound_get_data_source(const ma_sound* pSound) -{ - if (pSound == NULL) { - return NULL; - } - - return pSound->pDataSource; -} - -MA_API ma_result ma_sound_start(ma_sound* pSound) -{ - if (pSound == NULL) { - return MA_INVALID_ARGS; - } - - /* If the sound is already playing, do nothing. */ - if (ma_sound_is_playing(pSound)) { - return MA_SUCCESS; - } - - /* If the sound is at the end it means we want to start from the start again. */ - if (ma_sound_at_end(pSound)) { - ma_result result = ma_data_source_seek_to_pcm_frame(pSound->pDataSource, 0); - if (result != MA_SUCCESS && result != MA_NOT_IMPLEMENTED) { - return result; /* Failed to seek back to the start. */ - } - - /* Make sure we clear the end indicator. */ - ma_atomic_exchange_32(&pSound->atEnd, MA_FALSE); - } - - /* Make sure the sound is started. If there's a start delay, the sound won't actually start until the start time is reached. */ - ma_node_set_state(pSound, ma_node_state_started); - - return MA_SUCCESS; -} - -MA_API ma_result ma_sound_stop(ma_sound* pSound) -{ - if (pSound == NULL) { - return MA_INVALID_ARGS; - } - - /* This will stop the sound immediately. Use ma_sound_set_stop_time() to stop the sound at a specific time. */ - ma_node_set_state(pSound, ma_node_state_stopped); - - return MA_SUCCESS; -} - -MA_API ma_result ma_sound_stop_with_fade_in_pcm_frames(ma_sound* pSound, ma_uint64 fadeLengthInFrames) -{ - if (pSound == NULL) { - return MA_INVALID_ARGS; - } - - /* Stopping with a fade out requires us to schedule the stop into the future by the fade length. */ - ma_sound_set_stop_time_with_fade_in_pcm_frames(pSound, ma_engine_get_time_in_pcm_frames(ma_sound_get_engine(pSound)) + fadeLengthInFrames, fadeLengthInFrames); - - return MA_SUCCESS; -} - -MA_API ma_result ma_sound_stop_with_fade_in_milliseconds(ma_sound* pSound, ma_uint64 fadeLengthInMilliseconds) -{ - ma_uint64 sampleRate; - - if (pSound == NULL) { - return MA_INVALID_ARGS; - } - - sampleRate = ma_engine_get_sample_rate(ma_sound_get_engine(pSound)); - - return ma_sound_stop_with_fade_in_pcm_frames(pSound, (fadeLengthInMilliseconds * sampleRate) / 1000); -} - -MA_API void ma_sound_set_volume(ma_sound* pSound, float volume) -{ - if (pSound == NULL) { - return; - } - - ma_engine_node_set_volume(&pSound->engineNode, volume); -} - -MA_API float ma_sound_get_volume(const ma_sound* pSound) -{ - float volume = 0; - - if (pSound == NULL) { - return 0; - } - - ma_engine_node_get_volume(&pSound->engineNode, &volume); - - return volume; -} - -MA_API void ma_sound_set_pan(ma_sound* pSound, float pan) -{ - if (pSound == NULL) { - return; - } - - ma_panner_set_pan(&pSound->engineNode.panner, pan); -} - -MA_API float ma_sound_get_pan(const ma_sound* pSound) -{ - if (pSound == NULL) { - return 0; - } - - return ma_panner_get_pan(&pSound->engineNode.panner); -} - -MA_API void ma_sound_set_pan_mode(ma_sound* pSound, ma_pan_mode panMode) -{ - if (pSound == NULL) { - return; - } - - ma_panner_set_mode(&pSound->engineNode.panner, panMode); -} - -MA_API ma_pan_mode ma_sound_get_pan_mode(const ma_sound* pSound) -{ - if (pSound == NULL) { - return ma_pan_mode_balance; - } - - return ma_panner_get_mode(&pSound->engineNode.panner); -} - -MA_API void ma_sound_set_pitch(ma_sound* pSound, float pitch) -{ - if (pSound == NULL) { - return; - } - - if (pitch <= 0) { - return; - } - - ma_atomic_exchange_explicit_f32(&pSound->engineNode.pitch, pitch, ma_atomic_memory_order_release); -} - -MA_API float ma_sound_get_pitch(const ma_sound* pSound) -{ - if (pSound == NULL) { - return 0; - } - - return ma_atomic_load_f32(&pSound->engineNode.pitch); /* Naughty const-cast for this. */ -} - -MA_API void ma_sound_set_spatialization_enabled(ma_sound* pSound, ma_bool32 enabled) -{ - if (pSound == NULL) { - return; - } - - ma_atomic_exchange_explicit_32(&pSound->engineNode.isSpatializationDisabled, !enabled, ma_atomic_memory_order_release); -} - -MA_API ma_bool32 ma_sound_is_spatialization_enabled(const ma_sound* pSound) -{ - if (pSound == NULL) { - return MA_FALSE; - } - - return ma_engine_node_is_spatialization_enabled(&pSound->engineNode); -} - -MA_API void ma_sound_set_pinned_listener_index(ma_sound* pSound, ma_uint32 listenerIndex) -{ - if (pSound == NULL || listenerIndex >= ma_engine_get_listener_count(ma_sound_get_engine(pSound))) { - return; - } - - ma_atomic_exchange_explicit_32(&pSound->engineNode.pinnedListenerIndex, listenerIndex, ma_atomic_memory_order_release); -} - -MA_API ma_uint32 ma_sound_get_pinned_listener_index(const ma_sound* pSound) -{ - if (pSound == NULL) { - return MA_LISTENER_INDEX_CLOSEST; - } - - return ma_atomic_load_explicit_32(&pSound->engineNode.pinnedListenerIndex, ma_atomic_memory_order_acquire); -} - -MA_API ma_uint32 ma_sound_get_listener_index(const ma_sound* pSound) -{ - ma_uint32 listenerIndex; - - if (pSound == NULL) { - return 0; - } - - listenerIndex = ma_sound_get_pinned_listener_index(pSound); - if (listenerIndex == MA_LISTENER_INDEX_CLOSEST) { - ma_vec3f position = ma_sound_get_position(pSound); - return ma_engine_find_closest_listener(ma_sound_get_engine(pSound), position.x, position.y, position.z); - } - - return listenerIndex; -} - -MA_API ma_vec3f ma_sound_get_direction_to_listener(const ma_sound* pSound) -{ - ma_vec3f relativePos; - ma_engine* pEngine; - - if (pSound == NULL) { - return ma_vec3f_init_3f(0, 0, -1); - } - - pEngine = ma_sound_get_engine(pSound); - if (pEngine == NULL) { - return ma_vec3f_init_3f(0, 0, -1); - } - - ma_spatializer_get_relative_position_and_direction(&pSound->engineNode.spatializer, &pEngine->listeners[ma_sound_get_listener_index(pSound)], &relativePos, NULL); - - return ma_vec3f_normalize(ma_vec3f_neg(relativePos)); -} - -MA_API void ma_sound_set_position(ma_sound* pSound, float x, float y, float z) -{ - if (pSound == NULL) { - return; - } - - ma_spatializer_set_position(&pSound->engineNode.spatializer, x, y, z); -} - -MA_API ma_vec3f ma_sound_get_position(const ma_sound* pSound) -{ - if (pSound == NULL) { - return ma_vec3f_init_3f(0, 0, 0); - } - - return ma_spatializer_get_position(&pSound->engineNode.spatializer); -} - -MA_API void ma_sound_set_direction(ma_sound* pSound, float x, float y, float z) -{ - if (pSound == NULL) { - return; - } - - ma_spatializer_set_direction(&pSound->engineNode.spatializer, x, y, z); -} - -MA_API ma_vec3f ma_sound_get_direction(const ma_sound* pSound) -{ - if (pSound == NULL) { - return ma_vec3f_init_3f(0, 0, 0); - } - - return ma_spatializer_get_direction(&pSound->engineNode.spatializer); -} - -MA_API void ma_sound_set_velocity(ma_sound* pSound, float x, float y, float z) -{ - if (pSound == NULL) { - return; - } - - ma_spatializer_set_velocity(&pSound->engineNode.spatializer, x, y, z); -} - -MA_API ma_vec3f ma_sound_get_velocity(const ma_sound* pSound) -{ - if (pSound == NULL) { - return ma_vec3f_init_3f(0, 0, 0); - } - - return ma_spatializer_get_velocity(&pSound->engineNode.spatializer); -} - -MA_API void ma_sound_set_attenuation_model(ma_sound* pSound, ma_attenuation_model attenuationModel) -{ - if (pSound == NULL) { - return; - } - - ma_spatializer_set_attenuation_model(&pSound->engineNode.spatializer, attenuationModel); -} - -MA_API ma_attenuation_model ma_sound_get_attenuation_model(const ma_sound* pSound) -{ - if (pSound == NULL) { - return ma_attenuation_model_none; - } - - return ma_spatializer_get_attenuation_model(&pSound->engineNode.spatializer); -} - -MA_API void ma_sound_set_positioning(ma_sound* pSound, ma_positioning positioning) -{ - if (pSound == NULL) { - return; - } - - ma_spatializer_set_positioning(&pSound->engineNode.spatializer, positioning); -} - -MA_API ma_positioning ma_sound_get_positioning(const ma_sound* pSound) -{ - if (pSound == NULL) { - return ma_positioning_absolute; - } - - return ma_spatializer_get_positioning(&pSound->engineNode.spatializer); -} - -MA_API void ma_sound_set_rolloff(ma_sound* pSound, float rolloff) -{ - if (pSound == NULL) { - return; - } - - ma_spatializer_set_rolloff(&pSound->engineNode.spatializer, rolloff); -} - -MA_API float ma_sound_get_rolloff(const ma_sound* pSound) -{ - if (pSound == NULL) { - return 0; - } - - return ma_spatializer_get_rolloff(&pSound->engineNode.spatializer); -} - -MA_API void ma_sound_set_min_gain(ma_sound* pSound, float minGain) -{ - if (pSound == NULL) { - return; - } - - ma_spatializer_set_min_gain(&pSound->engineNode.spatializer, minGain); -} - -MA_API float ma_sound_get_min_gain(const ma_sound* pSound) -{ - if (pSound == NULL) { - return 0; - } - - return ma_spatializer_get_min_gain(&pSound->engineNode.spatializer); -} - -MA_API void ma_sound_set_max_gain(ma_sound* pSound, float maxGain) -{ - if (pSound == NULL) { - return; - } - - ma_spatializer_set_max_gain(&pSound->engineNode.spatializer, maxGain); -} - -MA_API float ma_sound_get_max_gain(const ma_sound* pSound) -{ - if (pSound == NULL) { - return 0; - } - - return ma_spatializer_get_max_gain(&pSound->engineNode.spatializer); -} - -MA_API void ma_sound_set_min_distance(ma_sound* pSound, float minDistance) -{ - if (pSound == NULL) { - return; - } - - ma_spatializer_set_min_distance(&pSound->engineNode.spatializer, minDistance); -} - -MA_API float ma_sound_get_min_distance(const ma_sound* pSound) -{ - if (pSound == NULL) { - return 0; - } - - return ma_spatializer_get_min_distance(&pSound->engineNode.spatializer); -} - -MA_API void ma_sound_set_max_distance(ma_sound* pSound, float maxDistance) -{ - if (pSound == NULL) { - return; - } - - ma_spatializer_set_max_distance(&pSound->engineNode.spatializer, maxDistance); -} - -MA_API float ma_sound_get_max_distance(const ma_sound* pSound) -{ - if (pSound == NULL) { - return 0; - } - - return ma_spatializer_get_max_distance(&pSound->engineNode.spatializer); -} - -MA_API void ma_sound_set_cone(ma_sound* pSound, float innerAngleInRadians, float outerAngleInRadians, float outerGain) -{ - if (pSound == NULL) { - return; - } - - ma_spatializer_set_cone(&pSound->engineNode.spatializer, innerAngleInRadians, outerAngleInRadians, outerGain); -} - -MA_API void ma_sound_get_cone(const ma_sound* pSound, float* pInnerAngleInRadians, float* pOuterAngleInRadians, float* pOuterGain) -{ - if (pInnerAngleInRadians != NULL) { - *pInnerAngleInRadians = 0; - } - - if (pOuterAngleInRadians != NULL) { - *pOuterAngleInRadians = 0; - } - - if (pOuterGain != NULL) { - *pOuterGain = 0; - } - - if (pSound == NULL) { - return; - } - - ma_spatializer_get_cone(&pSound->engineNode.spatializer, pInnerAngleInRadians, pOuterAngleInRadians, pOuterGain); -} - -MA_API void ma_sound_set_doppler_factor(ma_sound* pSound, float dopplerFactor) -{ - if (pSound == NULL) { - return; - } - - ma_spatializer_set_doppler_factor(&pSound->engineNode.spatializer, dopplerFactor); -} - -MA_API float ma_sound_get_doppler_factor(const ma_sound* pSound) -{ - if (pSound == NULL) { - return 0; - } - - return ma_spatializer_get_doppler_factor(&pSound->engineNode.spatializer); -} - -MA_API void ma_sound_set_directional_attenuation_factor(ma_sound* pSound, float directionalAttenuationFactor) -{ - if (pSound == NULL) { - return; - } - - ma_spatializer_set_directional_attenuation_factor(&pSound->engineNode.spatializer, directionalAttenuationFactor); -} - -MA_API float ma_sound_get_directional_attenuation_factor(const ma_sound* pSound) -{ - if (pSound == NULL) { - return 1; - } - - return ma_spatializer_get_directional_attenuation_factor(&pSound->engineNode.spatializer); -} - - -MA_API void ma_sound_set_fade_in_pcm_frames(ma_sound* pSound, float volumeBeg, float volumeEnd, ma_uint64 fadeLengthInFrames) -{ - if (pSound == NULL) { - return; - } - - ma_sound_set_fade_start_in_pcm_frames(pSound, volumeBeg, volumeEnd, fadeLengthInFrames, (~(ma_uint64)0)); -} - -MA_API void ma_sound_set_fade_in_milliseconds(ma_sound* pSound, float volumeBeg, float volumeEnd, ma_uint64 fadeLengthInMilliseconds) -{ - if (pSound == NULL) { - return; - } - - ma_sound_set_fade_in_pcm_frames(pSound, volumeBeg, volumeEnd, (fadeLengthInMilliseconds * pSound->engineNode.fader.config.sampleRate) / 1000); -} - -MA_API void ma_sound_set_fade_start_in_pcm_frames(ma_sound* pSound, float volumeBeg, float volumeEnd, ma_uint64 fadeLengthInFrames, ma_uint64 absoluteGlobalTimeInFrames) -{ - if (pSound == NULL) { - return; - } - - /* - We don't want to update the fader at this point because we need to use the engine's current time - to derive the fader's start offset. The timer is being updated on the audio thread so in order to - do this as accurately as possible we'll need to defer this to the audio thread. - */ - ma_atomic_float_set(&pSound->engineNode.fadeSettings.volumeBeg, volumeBeg); - ma_atomic_float_set(&pSound->engineNode.fadeSettings.volumeEnd, volumeEnd); - ma_atomic_uint64_set(&pSound->engineNode.fadeSettings.fadeLengthInFrames, fadeLengthInFrames); - ma_atomic_uint64_set(&pSound->engineNode.fadeSettings.absoluteGlobalTimeInFrames, absoluteGlobalTimeInFrames); -} - -MA_API void ma_sound_set_fade_start_in_milliseconds(ma_sound* pSound, float volumeBeg, float volumeEnd, ma_uint64 fadeLengthInMilliseconds, ma_uint64 absoluteGlobalTimeInMilliseconds) -{ - ma_uint32 sampleRate; - - if (pSound == NULL) { - return; - } - - sampleRate = ma_engine_get_sample_rate(ma_sound_get_engine(pSound)); - - ma_sound_set_fade_start_in_pcm_frames(pSound, volumeBeg, volumeEnd, (fadeLengthInMilliseconds * sampleRate) / 1000, (absoluteGlobalTimeInMilliseconds * sampleRate) / 1000); -} - -MA_API float ma_sound_get_current_fade_volume(const ma_sound* pSound) -{ - if (pSound == NULL) { - return MA_INVALID_ARGS; - } - - return ma_fader_get_current_volume(&pSound->engineNode.fader); -} - -MA_API void ma_sound_set_start_time_in_pcm_frames(ma_sound* pSound, ma_uint64 absoluteGlobalTimeInFrames) -{ - if (pSound == NULL) { - return; - } - - ma_node_set_state_time(pSound, ma_node_state_started, absoluteGlobalTimeInFrames); -} - -MA_API void ma_sound_set_start_time_in_milliseconds(ma_sound* pSound, ma_uint64 absoluteGlobalTimeInMilliseconds) -{ - if (pSound == NULL) { - return; - } - - ma_sound_set_start_time_in_pcm_frames(pSound, absoluteGlobalTimeInMilliseconds * ma_engine_get_sample_rate(ma_sound_get_engine(pSound)) / 1000); -} - -MA_API void ma_sound_set_stop_time_in_pcm_frames(ma_sound* pSound, ma_uint64 absoluteGlobalTimeInFrames) -{ - if (pSound == NULL) { - return; - } - - ma_sound_set_stop_time_with_fade_in_pcm_frames(pSound, absoluteGlobalTimeInFrames, 0); -} - -MA_API void ma_sound_set_stop_time_in_milliseconds(ma_sound* pSound, ma_uint64 absoluteGlobalTimeInMilliseconds) -{ - if (pSound == NULL) { - return; - } - - ma_sound_set_stop_time_in_pcm_frames(pSound, absoluteGlobalTimeInMilliseconds * ma_engine_get_sample_rate(ma_sound_get_engine(pSound)) / 1000); -} - -MA_API void ma_sound_set_stop_time_with_fade_in_pcm_frames(ma_sound* pSound, ma_uint64 stopAbsoluteGlobalTimeInFrames, ma_uint64 fadeLengthInFrames) -{ - if (pSound == NULL) { - return; - } - - if (fadeLengthInFrames > 0) { - if (fadeLengthInFrames > stopAbsoluteGlobalTimeInFrames) { - fadeLengthInFrames = stopAbsoluteGlobalTimeInFrames; - } - - ma_sound_set_fade_start_in_pcm_frames(pSound, -1, 0, fadeLengthInFrames, stopAbsoluteGlobalTimeInFrames - fadeLengthInFrames); - } - - ma_node_set_state_time(pSound, ma_node_state_stopped, stopAbsoluteGlobalTimeInFrames); -} - -MA_API void ma_sound_set_stop_time_with_fade_in_milliseconds(ma_sound* pSound, ma_uint64 stopAbsoluteGlobalTimeInMilliseconds, ma_uint64 fadeLengthInMilliseconds) -{ - ma_uint32 sampleRate; - - if (pSound == NULL) { - return; - } - - sampleRate = ma_engine_get_sample_rate(ma_sound_get_engine(pSound)); - - ma_sound_set_stop_time_with_fade_in_pcm_frames(pSound, (stopAbsoluteGlobalTimeInMilliseconds * sampleRate) / 1000, (fadeLengthInMilliseconds * sampleRate) / 1000); -} - -MA_API ma_bool32 ma_sound_is_playing(const ma_sound* pSound) -{ - if (pSound == NULL) { - return MA_FALSE; - } - - return ma_node_get_state_by_time(pSound, ma_engine_get_time_in_pcm_frames(ma_sound_get_engine(pSound))) == ma_node_state_started; -} - -MA_API ma_uint64 ma_sound_get_time_in_pcm_frames(const ma_sound* pSound) -{ - if (pSound == NULL) { - return 0; - } - - return ma_node_get_time(pSound); -} - -MA_API ma_uint64 ma_sound_get_time_in_milliseconds(const ma_sound* pSound) -{ - return ma_sound_get_time_in_pcm_frames(pSound) * 1000 / ma_engine_get_sample_rate(ma_sound_get_engine(pSound)); -} - -MA_API void ma_sound_set_looping(ma_sound* pSound, ma_bool32 isLooping) -{ - if (pSound == NULL) { - return; - } - - /* Looping is only a valid concept if the sound is backed by a data source. */ - if (pSound->pDataSource == NULL) { - return; - } - - /* The looping state needs to be applied to the data source in order for any looping to actually happen. */ - ma_data_source_set_looping(pSound->pDataSource, isLooping); -} - -MA_API ma_bool32 ma_sound_is_looping(const ma_sound* pSound) -{ - if (pSound == NULL) { - return MA_FALSE; - } - - /* There is no notion of looping for sounds that are not backed by a data source. */ - if (pSound->pDataSource == NULL) { - return MA_FALSE; - } - - return ma_data_source_is_looping(pSound->pDataSource); -} - -MA_API ma_bool32 ma_sound_at_end(const ma_sound* pSound) -{ - if (pSound == NULL) { - return MA_FALSE; - } - - /* There is no notion of an end of a sound if it's not backed by a data source. */ - if (pSound->pDataSource == NULL) { - return MA_FALSE; - } - - return ma_sound_get_at_end(pSound); -} - -MA_API ma_result ma_sound_seek_to_pcm_frame(ma_sound* pSound, ma_uint64 frameIndex) -{ - if (pSound == NULL) { - return MA_INVALID_ARGS; - } - - /* Seeking is only valid for sounds that are backed by a data source. */ - if (pSound->pDataSource == NULL) { - return MA_INVALID_OPERATION; - } - - /* We can't be seeking while reading at the same time. We just set the seek target and get the mixing thread to do the actual seek. */ - ma_atomic_exchange_64(&pSound->seekTarget, frameIndex); - - return MA_SUCCESS; -} - -MA_API ma_result ma_sound_seek_to_second(ma_sound* pSound, float seekPointInSeconds) -{ - ma_uint64 frameIndex; - ma_uint32 sampleRate; - ma_result result; - - if (pSound == NULL) { - return MA_INVALID_ARGS; - } - - result = ma_sound_get_data_format(pSound, NULL, NULL, &sampleRate, NULL, 0); - if (result != MA_SUCCESS) { - return result; - } - - /* We need PCM frames. We need to convert first */ - frameIndex = (ma_uint64)(seekPointInSeconds * sampleRate); - - return ma_sound_seek_to_pcm_frame(pSound, frameIndex); -} - -MA_API ma_result ma_sound_get_data_format(ma_sound* pSound, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate, ma_channel* pChannelMap, size_t channelMapCap) -{ - if (pSound == NULL) { - return MA_INVALID_ARGS; - } - - /* The data format is retrieved directly from the data source if the sound is backed by one. Otherwise we pull it from the node. */ - if (pSound->pDataSource == NULL) { - ma_uint32 channels; - - if (pFormat != NULL) { - *pFormat = ma_format_f32; - } - - channels = ma_node_get_input_channels(&pSound->engineNode, 0); - if (pChannels != NULL) { - *pChannels = channels; - } - - if (pSampleRate != NULL) { - *pSampleRate = pSound->engineNode.resampler.config.sampleRateIn; - } - - if (pChannelMap != NULL) { - ma_channel_map_init_standard(ma_standard_channel_map_default, pChannelMap, channelMapCap, channels); - } - - return MA_SUCCESS; - } else { - return ma_data_source_get_data_format(pSound->pDataSource, pFormat, pChannels, pSampleRate, pChannelMap, channelMapCap); - } -} - -MA_API ma_result ma_sound_get_cursor_in_pcm_frames(ma_sound* pSound, ma_uint64* pCursor) -{ - ma_uint64 seekTarget; - - if (pSound == NULL) { - return MA_INVALID_ARGS; - } - - /* The notion of a cursor is only valid for sounds that are backed by a data source. */ - if (pSound->pDataSource == NULL) { - return MA_INVALID_OPERATION; - } - - seekTarget = ma_atomic_load_64(&pSound->seekTarget); - if (seekTarget != MA_SEEK_TARGET_NONE) { - *pCursor = seekTarget; - return MA_SUCCESS; - } else { - return ma_data_source_get_cursor_in_pcm_frames(pSound->pDataSource, pCursor); - } -} - -MA_API ma_result ma_sound_get_length_in_pcm_frames(ma_sound* pSound, ma_uint64* pLength) -{ - if (pSound == NULL) { - return MA_INVALID_ARGS; - } - - /* The notion of a sound length is only valid for sounds that are backed by a data source. */ - if (pSound->pDataSource == NULL) { - return MA_INVALID_OPERATION; - } - - return ma_data_source_get_length_in_pcm_frames(pSound->pDataSource, pLength); -} - -MA_API ma_result ma_sound_get_cursor_in_seconds(ma_sound* pSound, float* pCursor) -{ - ma_result result; - ma_uint64 cursorInPCMFrames; - ma_uint32 sampleRate; - - if (pCursor != NULL) { - *pCursor = 0; - } - - result = ma_sound_get_cursor_in_pcm_frames(pSound, &cursorInPCMFrames); - if (result != MA_SUCCESS) { - return result; - } - - result = ma_sound_get_data_format(pSound, NULL, NULL, &sampleRate, NULL, 0); - if (result != MA_SUCCESS) { - return result; - } - - /* VC6 does not support division of unsigned 64-bit integers with floating point numbers. Need to use a signed number. This shouldn't effect anything in practice. */ - *pCursor = (ma_int64)cursorInPCMFrames / (float)sampleRate; - - return MA_SUCCESS; -} - -MA_API ma_result ma_sound_get_length_in_seconds(ma_sound* pSound, float* pLength) -{ - if (pSound == NULL) { - return MA_INVALID_ARGS; - } - - /* The notion of a sound length is only valid for sounds that are backed by a data source. */ - if (pSound->pDataSource == NULL) { - return MA_INVALID_OPERATION; - } - - return ma_data_source_get_length_in_seconds(pSound->pDataSource, pLength); -} - -MA_API ma_result ma_sound_set_end_callback(ma_sound* pSound, ma_sound_end_proc callback, void* pUserData) -{ - if (pSound == NULL) { - return MA_INVALID_ARGS; - } - - /* The notion of an end is only valid for sounds that are backed by a data source. */ - if (pSound->pDataSource == NULL) { - return MA_INVALID_OPERATION; - } - - pSound->endCallback = callback; - pSound->pEndCallbackUserData = pUserData; - - return MA_SUCCESS; -} - - -MA_API ma_result ma_sound_group_init(ma_engine* pEngine, ma_uint32 flags, ma_sound_group* pParentGroup, ma_sound_group* pGroup) -{ - ma_sound_group_config config = ma_sound_group_config_init_2(pEngine); - config.flags = flags; - config.pInitialAttachment = pParentGroup; - return ma_sound_group_init_ex(pEngine, &config, pGroup); -} - -MA_API ma_result ma_sound_group_init_ex(ma_engine* pEngine, const ma_sound_group_config* pConfig, ma_sound_group* pGroup) -{ - ma_sound_config soundConfig; - - if (pGroup == NULL) { - return MA_INVALID_ARGS; - } - - MA_ZERO_OBJECT(pGroup); - - if (pConfig == NULL) { - return MA_INVALID_ARGS; - } - - /* A sound group is just a sound without a data source. */ - soundConfig = *pConfig; - soundConfig.pFilePath = NULL; - soundConfig.pFilePathW = NULL; - soundConfig.pDataSource = NULL; - - /* - Groups need to have spatialization disabled by default because I think it'll be pretty rare - that programs will want to spatialize groups (but not unheard of). Certainly it feels like - disabling this by default feels like the right option. Spatialization can be enabled with a - call to ma_sound_group_set_spatialization_enabled(). - */ - soundConfig.flags |= MA_SOUND_FLAG_NO_SPATIALIZATION; - - return ma_sound_init_ex(pEngine, &soundConfig, pGroup); -} - -MA_API void ma_sound_group_uninit(ma_sound_group* pGroup) -{ - ma_sound_uninit(pGroup); -} - -MA_API ma_engine* ma_sound_group_get_engine(const ma_sound_group* pGroup) -{ - return ma_sound_get_engine(pGroup); -} - -MA_API ma_result ma_sound_group_start(ma_sound_group* pGroup) -{ - return ma_sound_start(pGroup); -} - -MA_API ma_result ma_sound_group_stop(ma_sound_group* pGroup) -{ - return ma_sound_stop(pGroup); -} - -MA_API void ma_sound_group_set_volume(ma_sound_group* pGroup, float volume) -{ - ma_sound_set_volume(pGroup, volume); -} - -MA_API float ma_sound_group_get_volume(const ma_sound_group* pGroup) -{ - return ma_sound_get_volume(pGroup); -} - -MA_API void ma_sound_group_set_pan(ma_sound_group* pGroup, float pan) -{ - ma_sound_set_pan(pGroup, pan); -} - -MA_API float ma_sound_group_get_pan(const ma_sound_group* pGroup) -{ - return ma_sound_get_pan(pGroup); -} - -MA_API void ma_sound_group_set_pan_mode(ma_sound_group* pGroup, ma_pan_mode panMode) -{ - ma_sound_set_pan_mode(pGroup, panMode); -} - -MA_API ma_pan_mode ma_sound_group_get_pan_mode(const ma_sound_group* pGroup) -{ - return ma_sound_get_pan_mode(pGroup); -} - -MA_API void ma_sound_group_set_pitch(ma_sound_group* pGroup, float pitch) -{ - ma_sound_set_pitch(pGroup, pitch); -} - -MA_API float ma_sound_group_get_pitch(const ma_sound_group* pGroup) -{ - return ma_sound_get_pitch(pGroup); -} - -MA_API void ma_sound_group_set_spatialization_enabled(ma_sound_group* pGroup, ma_bool32 enabled) -{ - ma_sound_set_spatialization_enabled(pGroup, enabled); -} - -MA_API ma_bool32 ma_sound_group_is_spatialization_enabled(const ma_sound_group* pGroup) -{ - return ma_sound_is_spatialization_enabled(pGroup); -} - -MA_API void ma_sound_group_set_pinned_listener_index(ma_sound_group* pGroup, ma_uint32 listenerIndex) -{ - ma_sound_set_pinned_listener_index(pGroup, listenerIndex); -} - -MA_API ma_uint32 ma_sound_group_get_pinned_listener_index(const ma_sound_group* pGroup) -{ - return ma_sound_get_pinned_listener_index(pGroup); -} - -MA_API ma_uint32 ma_sound_group_get_listener_index(const ma_sound_group* pGroup) -{ - return ma_sound_get_listener_index(pGroup); -} - -MA_API ma_vec3f ma_sound_group_get_direction_to_listener(const ma_sound_group* pGroup) -{ - return ma_sound_get_direction_to_listener(pGroup); -} - -MA_API void ma_sound_group_set_position(ma_sound_group* pGroup, float x, float y, float z) -{ - ma_sound_set_position(pGroup, x, y, z); -} - -MA_API ma_vec3f ma_sound_group_get_position(const ma_sound_group* pGroup) -{ - return ma_sound_get_position(pGroup); -} - -MA_API void ma_sound_group_set_direction(ma_sound_group* pGroup, float x, float y, float z) -{ - ma_sound_set_direction(pGroup, x, y, z); -} - -MA_API ma_vec3f ma_sound_group_get_direction(const ma_sound_group* pGroup) -{ - return ma_sound_get_direction(pGroup); -} - -MA_API void ma_sound_group_set_velocity(ma_sound_group* pGroup, float x, float y, float z) -{ - ma_sound_set_velocity(pGroup, x, y, z); -} - -MA_API ma_vec3f ma_sound_group_get_velocity(const ma_sound_group* pGroup) -{ - return ma_sound_get_velocity(pGroup); -} - -MA_API void ma_sound_group_set_attenuation_model(ma_sound_group* pGroup, ma_attenuation_model attenuationModel) -{ - ma_sound_set_attenuation_model(pGroup, attenuationModel); -} - -MA_API ma_attenuation_model ma_sound_group_get_attenuation_model(const ma_sound_group* pGroup) -{ - return ma_sound_get_attenuation_model(pGroup); -} - -MA_API void ma_sound_group_set_positioning(ma_sound_group* pGroup, ma_positioning positioning) -{ - ma_sound_set_positioning(pGroup, positioning); -} - -MA_API ma_positioning ma_sound_group_get_positioning(const ma_sound_group* pGroup) -{ - return ma_sound_get_positioning(pGroup); -} - -MA_API void ma_sound_group_set_rolloff(ma_sound_group* pGroup, float rolloff) -{ - ma_sound_set_rolloff(pGroup, rolloff); -} - -MA_API float ma_sound_group_get_rolloff(const ma_sound_group* pGroup) -{ - return ma_sound_get_rolloff(pGroup); -} - -MA_API void ma_sound_group_set_min_gain(ma_sound_group* pGroup, float minGain) -{ - ma_sound_set_min_gain(pGroup, minGain); -} - -MA_API float ma_sound_group_get_min_gain(const ma_sound_group* pGroup) -{ - return ma_sound_get_min_gain(pGroup); -} - -MA_API void ma_sound_group_set_max_gain(ma_sound_group* pGroup, float maxGain) -{ - ma_sound_set_max_gain(pGroup, maxGain); -} - -MA_API float ma_sound_group_get_max_gain(const ma_sound_group* pGroup) -{ - return ma_sound_get_max_gain(pGroup); -} - -MA_API void ma_sound_group_set_min_distance(ma_sound_group* pGroup, float minDistance) -{ - ma_sound_set_min_distance(pGroup, minDistance); -} - -MA_API float ma_sound_group_get_min_distance(const ma_sound_group* pGroup) -{ - return ma_sound_get_min_distance(pGroup); -} - -MA_API void ma_sound_group_set_max_distance(ma_sound_group* pGroup, float maxDistance) -{ - ma_sound_set_max_distance(pGroup, maxDistance); -} - -MA_API float ma_sound_group_get_max_distance(const ma_sound_group* pGroup) -{ - return ma_sound_get_max_distance(pGroup); -} - -MA_API void ma_sound_group_set_cone(ma_sound_group* pGroup, float innerAngleInRadians, float outerAngleInRadians, float outerGain) -{ - ma_sound_set_cone(pGroup, innerAngleInRadians, outerAngleInRadians, outerGain); -} - -MA_API void ma_sound_group_get_cone(const ma_sound_group* pGroup, float* pInnerAngleInRadians, float* pOuterAngleInRadians, float* pOuterGain) -{ - ma_sound_get_cone(pGroup, pInnerAngleInRadians, pOuterAngleInRadians, pOuterGain); -} - -MA_API void ma_sound_group_set_doppler_factor(ma_sound_group* pGroup, float dopplerFactor) -{ - ma_sound_set_doppler_factor(pGroup, dopplerFactor); -} - -MA_API float ma_sound_group_get_doppler_factor(const ma_sound_group* pGroup) -{ - return ma_sound_get_doppler_factor(pGroup); -} - -MA_API void ma_sound_group_set_directional_attenuation_factor(ma_sound_group* pGroup, float directionalAttenuationFactor) -{ - ma_sound_set_directional_attenuation_factor(pGroup, directionalAttenuationFactor); -} - -MA_API float ma_sound_group_get_directional_attenuation_factor(const ma_sound_group* pGroup) -{ - return ma_sound_get_directional_attenuation_factor(pGroup); -} - -MA_API void ma_sound_group_set_fade_in_pcm_frames(ma_sound_group* pGroup, float volumeBeg, float volumeEnd, ma_uint64 fadeLengthInFrames) -{ - ma_sound_set_fade_in_pcm_frames(pGroup, volumeBeg, volumeEnd, fadeLengthInFrames); -} - -MA_API void ma_sound_group_set_fade_in_milliseconds(ma_sound_group* pGroup, float volumeBeg, float volumeEnd, ma_uint64 fadeLengthInMilliseconds) -{ - ma_sound_set_fade_in_milliseconds(pGroup, volumeBeg, volumeEnd, fadeLengthInMilliseconds); -} - -MA_API float ma_sound_group_get_current_fade_volume(ma_sound_group* pGroup) -{ - return ma_sound_get_current_fade_volume(pGroup); -} - -MA_API void ma_sound_group_set_start_time_in_pcm_frames(ma_sound_group* pGroup, ma_uint64 absoluteGlobalTimeInFrames) -{ - ma_sound_set_start_time_in_pcm_frames(pGroup, absoluteGlobalTimeInFrames); -} - -MA_API void ma_sound_group_set_start_time_in_milliseconds(ma_sound_group* pGroup, ma_uint64 absoluteGlobalTimeInMilliseconds) -{ - ma_sound_set_start_time_in_milliseconds(pGroup, absoluteGlobalTimeInMilliseconds); -} - -MA_API void ma_sound_group_set_stop_time_in_pcm_frames(ma_sound_group* pGroup, ma_uint64 absoluteGlobalTimeInFrames) -{ - ma_sound_set_stop_time_in_pcm_frames(pGroup, absoluteGlobalTimeInFrames); -} - -MA_API void ma_sound_group_set_stop_time_in_milliseconds(ma_sound_group* pGroup, ma_uint64 absoluteGlobalTimeInMilliseconds) -{ - ma_sound_set_stop_time_in_milliseconds(pGroup, absoluteGlobalTimeInMilliseconds); -} - -MA_API ma_bool32 ma_sound_group_is_playing(const ma_sound_group* pGroup) -{ - return ma_sound_is_playing(pGroup); -} - -MA_API ma_uint64 ma_sound_group_get_time_in_pcm_frames(const ma_sound_group* pGroup) -{ - return ma_sound_get_time_in_pcm_frames(pGroup); -} -#endif /* MA_NO_ENGINE */ -/* END SECTION: miniaudio_engine.c */ - - - -/************************************************************************************************************************************************************** -*************************************************************************************************************************************************************** - -Auto Generated -============== -All code below is auto-generated from a tool. This mostly consists of decoding backend implementations such as ma_dr_wav, ma_dr_flac, etc. If you find a bug in the -code below please report the bug to the respective repository for the relevant project (probably dr_libs). - -*************************************************************************************************************************************************************** -**************************************************************************************************************************************************************/ -#if !defined(MA_NO_WAV) && (!defined(MA_NO_DECODING) || !defined(MA_NO_ENCODING)) -#if !defined(MA_DR_WAV_IMPLEMENTATION) -/* dr_wav_c begin */ -#ifndef ma_dr_wav_c -#define ma_dr_wav_c -#ifdef __MRC__ -#pragma options opt off -#endif -#include -#include -#include -#ifndef MA_DR_WAV_NO_STDIO -#include -#ifndef MA_DR_WAV_NO_WCHAR -#include -#endif -#endif -#ifndef MA_DR_WAV_ASSERT -#include -#define MA_DR_WAV_ASSERT(expression) assert(expression) -#endif -#ifndef MA_DR_WAV_MALLOC -#define MA_DR_WAV_MALLOC(sz) malloc((sz)) -#endif -#ifndef MA_DR_WAV_REALLOC -#define MA_DR_WAV_REALLOC(p, sz) realloc((p), (sz)) -#endif -#ifndef MA_DR_WAV_FREE -#define MA_DR_WAV_FREE(p) free((p)) -#endif -#ifndef MA_DR_WAV_COPY_MEMORY -#define MA_DR_WAV_COPY_MEMORY(dst, src, sz) memcpy((dst), (src), (sz)) -#endif -#ifndef MA_DR_WAV_ZERO_MEMORY -#define MA_DR_WAV_ZERO_MEMORY(p, sz) memset((p), 0, (sz)) -#endif -#ifndef MA_DR_WAV_ZERO_OBJECT -#define MA_DR_WAV_ZERO_OBJECT(p) MA_DR_WAV_ZERO_MEMORY((p), sizeof(*p)) -#endif -#define ma_dr_wav_countof(x) (sizeof(x) / sizeof(x[0])) -#define ma_dr_wav_align(x, a) ((((x) + (a) - 1) / (a)) * (a)) -#define ma_dr_wav_min(a, b) (((a) < (b)) ? (a) : (b)) -#define ma_dr_wav_max(a, b) (((a) > (b)) ? (a) : (b)) -#define ma_dr_wav_clamp(x, lo, hi) (ma_dr_wav_max((lo), ma_dr_wav_min((hi), (x)))) -#define ma_dr_wav_offset_ptr(p, offset) (((ma_uint8*)(p)) + (offset)) -#define MA_DR_WAV_MAX_SIMD_VECTOR_SIZE 32 -#define MA_DR_WAV_INT64_MIN ((ma_int64) ((ma_uint64)0x80000000 << 32)) -#define MA_DR_WAV_INT64_MAX ((ma_int64)(((ma_uint64)0x7FFFFFFF << 32) | 0xFFFFFFFF)) -#if defined(_MSC_VER) && _MSC_VER >= 1400 - #define MA_DR_WAV_HAS_BYTESWAP16_INTRINSIC - #define MA_DR_WAV_HAS_BYTESWAP32_INTRINSIC - #define MA_DR_WAV_HAS_BYTESWAP64_INTRINSIC -#elif defined(__clang__) - #if defined(__has_builtin) - #if __has_builtin(__builtin_bswap16) - #define MA_DR_WAV_HAS_BYTESWAP16_INTRINSIC - #endif - #if __has_builtin(__builtin_bswap32) - #define MA_DR_WAV_HAS_BYTESWAP32_INTRINSIC - #endif - #if __has_builtin(__builtin_bswap64) - #define MA_DR_WAV_HAS_BYTESWAP64_INTRINSIC - #endif - #endif -#elif defined(__GNUC__) - #if ((__GNUC__ > 4) || (__GNUC__ == 4 && __GNUC_MINOR__ >= 3)) - #define MA_DR_WAV_HAS_BYTESWAP32_INTRINSIC - #define MA_DR_WAV_HAS_BYTESWAP64_INTRINSIC - #endif - #if ((__GNUC__ > 4) || (__GNUC__ == 4 && __GNUC_MINOR__ >= 8)) - #define MA_DR_WAV_HAS_BYTESWAP16_INTRINSIC - #endif -#endif -MA_API void ma_dr_wav_version(ma_uint32* pMajor, ma_uint32* pMinor, ma_uint32* pRevision) -{ - if (pMajor) { - *pMajor = MA_DR_WAV_VERSION_MAJOR; - } - if (pMinor) { - *pMinor = MA_DR_WAV_VERSION_MINOR; - } - if (pRevision) { - *pRevision = MA_DR_WAV_VERSION_REVISION; - } -} -MA_API const char* ma_dr_wav_version_string(void) -{ - return MA_DR_WAV_VERSION_STRING; -} -#ifndef MA_DR_WAV_MAX_SAMPLE_RATE -#define MA_DR_WAV_MAX_SAMPLE_RATE 384000 -#endif -#ifndef MA_DR_WAV_MAX_CHANNELS -#define MA_DR_WAV_MAX_CHANNELS 256 -#endif -#ifndef MA_DR_WAV_MAX_BITS_PER_SAMPLE -#define MA_DR_WAV_MAX_BITS_PER_SAMPLE 64 -#endif -static const ma_uint8 ma_dr_wavGUID_W64_RIFF[16] = {0x72,0x69,0x66,0x66, 0x2E,0x91, 0xCF,0x11, 0xA5,0xD6, 0x28,0xDB,0x04,0xC1,0x00,0x00}; -static const ma_uint8 ma_dr_wavGUID_W64_WAVE[16] = {0x77,0x61,0x76,0x65, 0xF3,0xAC, 0xD3,0x11, 0x8C,0xD1, 0x00,0xC0,0x4F,0x8E,0xDB,0x8A}; -static const ma_uint8 ma_dr_wavGUID_W64_FMT [16] = {0x66,0x6D,0x74,0x20, 0xF3,0xAC, 0xD3,0x11, 0x8C,0xD1, 0x00,0xC0,0x4F,0x8E,0xDB,0x8A}; -static const ma_uint8 ma_dr_wavGUID_W64_FACT[16] = {0x66,0x61,0x63,0x74, 0xF3,0xAC, 0xD3,0x11, 0x8C,0xD1, 0x00,0xC0,0x4F,0x8E,0xDB,0x8A}; -static const ma_uint8 ma_dr_wavGUID_W64_DATA[16] = {0x64,0x61,0x74,0x61, 0xF3,0xAC, 0xD3,0x11, 0x8C,0xD1, 0x00,0xC0,0x4F,0x8E,0xDB,0x8A}; -static MA_INLINE int ma_dr_wav__is_little_endian(void) -{ -#if defined(MA_X86) || defined(MA_X64) - return MA_TRUE; -#elif defined(__BYTE_ORDER) && defined(__LITTLE_ENDIAN) && __BYTE_ORDER == __LITTLE_ENDIAN - return MA_TRUE; -#else - int n = 1; - return (*(char*)&n) == 1; -#endif -} -static MA_INLINE void ma_dr_wav_bytes_to_guid(const ma_uint8* data, ma_uint8* guid) -{ - int i; - for (i = 0; i < 16; ++i) { - guid[i] = data[i]; - } -} -static MA_INLINE ma_uint16 ma_dr_wav__bswap16(ma_uint16 n) -{ -#ifdef MA_DR_WAV_HAS_BYTESWAP16_INTRINSIC - #if defined(_MSC_VER) - return _byteswap_ushort(n); - #elif defined(__GNUC__) || defined(__clang__) - return __builtin_bswap16(n); - #else - #error "This compiler does not support the byte swap intrinsic." - #endif -#else - return ((n & 0xFF00) >> 8) | - ((n & 0x00FF) << 8); -#endif -} -static MA_INLINE ma_uint32 ma_dr_wav__bswap32(ma_uint32 n) -{ -#ifdef MA_DR_WAV_HAS_BYTESWAP32_INTRINSIC - #if defined(_MSC_VER) - return _byteswap_ulong(n); - #elif defined(__GNUC__) || defined(__clang__) - #if defined(MA_ARM) && (defined(__ARM_ARCH) && __ARM_ARCH >= 6) && !defined(MA_64BIT) - ma_uint32 r; - __asm__ __volatile__ ( - #if defined(MA_64BIT) - "rev %w[out], %w[in]" : [out]"=r"(r) : [in]"r"(n) - #else - "rev %[out], %[in]" : [out]"=r"(r) : [in]"r"(n) - #endif - ); - return r; - #else - return __builtin_bswap32(n); - #endif - #else - #error "This compiler does not support the byte swap intrinsic." - #endif -#else - return ((n & 0xFF000000) >> 24) | - ((n & 0x00FF0000) >> 8) | - ((n & 0x0000FF00) << 8) | - ((n & 0x000000FF) << 24); -#endif -} -static MA_INLINE ma_uint64 ma_dr_wav__bswap64(ma_uint64 n) -{ -#ifdef MA_DR_WAV_HAS_BYTESWAP64_INTRINSIC - #if defined(_MSC_VER) - return _byteswap_uint64(n); - #elif defined(__GNUC__) || defined(__clang__) - return __builtin_bswap64(n); - #else - #error "This compiler does not support the byte swap intrinsic." - #endif -#else - return ((n & ((ma_uint64)0xFF000000 << 32)) >> 56) | - ((n & ((ma_uint64)0x00FF0000 << 32)) >> 40) | - ((n & ((ma_uint64)0x0000FF00 << 32)) >> 24) | - ((n & ((ma_uint64)0x000000FF << 32)) >> 8) | - ((n & ((ma_uint64)0xFF000000 )) << 8) | - ((n & ((ma_uint64)0x00FF0000 )) << 24) | - ((n & ((ma_uint64)0x0000FF00 )) << 40) | - ((n & ((ma_uint64)0x000000FF )) << 56); -#endif -} -static MA_INLINE ma_int16 ma_dr_wav__bswap_s16(ma_int16 n) -{ - return (ma_int16)ma_dr_wav__bswap16((ma_uint16)n); -} -static MA_INLINE void ma_dr_wav__bswap_samples_s16(ma_int16* pSamples, ma_uint64 sampleCount) -{ - ma_uint64 iSample; - for (iSample = 0; iSample < sampleCount; iSample += 1) { - pSamples[iSample] = ma_dr_wav__bswap_s16(pSamples[iSample]); - } -} -static MA_INLINE void ma_dr_wav__bswap_s24(ma_uint8* p) -{ - ma_uint8 t; - t = p[0]; - p[0] = p[2]; - p[2] = t; -} -static MA_INLINE void ma_dr_wav__bswap_samples_s24(ma_uint8* pSamples, ma_uint64 sampleCount) -{ - ma_uint64 iSample; - for (iSample = 0; iSample < sampleCount; iSample += 1) { - ma_uint8* pSample = pSamples + (iSample*3); - ma_dr_wav__bswap_s24(pSample); - } -} -static MA_INLINE ma_int32 ma_dr_wav__bswap_s32(ma_int32 n) -{ - return (ma_int32)ma_dr_wav__bswap32((ma_uint32)n); -} -static MA_INLINE void ma_dr_wav__bswap_samples_s32(ma_int32* pSamples, ma_uint64 sampleCount) -{ - ma_uint64 iSample; - for (iSample = 0; iSample < sampleCount; iSample += 1) { - pSamples[iSample] = ma_dr_wav__bswap_s32(pSamples[iSample]); - } -} -static MA_INLINE ma_int64 ma_dr_wav__bswap_s64(ma_int64 n) -{ - return (ma_int64)ma_dr_wav__bswap64((ma_uint64)n); -} -static MA_INLINE void ma_dr_wav__bswap_samples_s64(ma_int64* pSamples, ma_uint64 sampleCount) -{ - ma_uint64 iSample; - for (iSample = 0; iSample < sampleCount; iSample += 1) { - pSamples[iSample] = ma_dr_wav__bswap_s64(pSamples[iSample]); - } -} -static MA_INLINE float ma_dr_wav__bswap_f32(float n) -{ - union { - ma_uint32 i; - float f; - } x; - x.f = n; - x.i = ma_dr_wav__bswap32(x.i); - return x.f; -} -static MA_INLINE void ma_dr_wav__bswap_samples_f32(float* pSamples, ma_uint64 sampleCount) -{ - ma_uint64 iSample; - for (iSample = 0; iSample < sampleCount; iSample += 1) { - pSamples[iSample] = ma_dr_wav__bswap_f32(pSamples[iSample]); - } -} -static MA_INLINE void ma_dr_wav__bswap_samples(void* pSamples, ma_uint64 sampleCount, ma_uint32 bytesPerSample) -{ - switch (bytesPerSample) - { - case 1: - { - } break; - case 2: - { - ma_dr_wav__bswap_samples_s16((ma_int16*)pSamples, sampleCount); - } break; - case 3: - { - ma_dr_wav__bswap_samples_s24((ma_uint8*)pSamples, sampleCount); - } break; - case 4: - { - ma_dr_wav__bswap_samples_s32((ma_int32*)pSamples, sampleCount); - } break; - case 8: - { - ma_dr_wav__bswap_samples_s64((ma_int64*)pSamples, sampleCount); - } break; - default: - { - MA_DR_WAV_ASSERT(MA_FALSE); - } break; - } -} -MA_PRIVATE MA_INLINE ma_bool32 ma_dr_wav_is_container_be(ma_dr_wav_container container) -{ - if (container == ma_dr_wav_container_rifx || container == ma_dr_wav_container_aiff) { - return MA_TRUE; - } else { - return MA_FALSE; - } -} -MA_PRIVATE MA_INLINE ma_uint16 ma_dr_wav_bytes_to_u16_le(const ma_uint8* data) -{ - return ((ma_uint16)data[0] << 0) | ((ma_uint16)data[1] << 8); -} -MA_PRIVATE MA_INLINE ma_uint16 ma_dr_wav_bytes_to_u16_be(const ma_uint8* data) -{ - return ((ma_uint16)data[1] << 0) | ((ma_uint16)data[0] << 8); -} -MA_PRIVATE MA_INLINE ma_uint16 ma_dr_wav_bytes_to_u16_ex(const ma_uint8* data, ma_dr_wav_container container) -{ - if (ma_dr_wav_is_container_be(container)) { - return ma_dr_wav_bytes_to_u16_be(data); - } else { - return ma_dr_wav_bytes_to_u16_le(data); - } -} -MA_PRIVATE MA_INLINE ma_uint32 ma_dr_wav_bytes_to_u32_le(const ma_uint8* data) -{ - return ((ma_uint32)data[0] << 0) | ((ma_uint32)data[1] << 8) | ((ma_uint32)data[2] << 16) | ((ma_uint32)data[3] << 24); -} -MA_PRIVATE MA_INLINE ma_uint32 ma_dr_wav_bytes_to_u32_be(const ma_uint8* data) -{ - return ((ma_uint32)data[3] << 0) | ((ma_uint32)data[2] << 8) | ((ma_uint32)data[1] << 16) | ((ma_uint32)data[0] << 24); -} -MA_PRIVATE MA_INLINE ma_uint32 ma_dr_wav_bytes_to_u32_ex(const ma_uint8* data, ma_dr_wav_container container) -{ - if (ma_dr_wav_is_container_be(container)) { - return ma_dr_wav_bytes_to_u32_be(data); - } else { - return ma_dr_wav_bytes_to_u32_le(data); - } -} -MA_PRIVATE ma_int64 ma_dr_wav_aiff_extented_to_s64(const ma_uint8* data) -{ - ma_uint32 exponent = ((ma_uint32)data[0] << 8) | data[1]; - ma_uint64 hi = ((ma_uint64)data[2] << 24) | ((ma_uint64)data[3] << 16) | ((ma_uint64)data[4] << 8) | ((ma_uint64)data[5] << 0); - ma_uint64 lo = ((ma_uint64)data[6] << 24) | ((ma_uint64)data[7] << 16) | ((ma_uint64)data[8] << 8) | ((ma_uint64)data[9] << 0); - ma_uint64 significand = (hi << 32) | lo; - int sign = exponent >> 15; - exponent &= 0x7FFF; - if (exponent == 0 && significand == 0) { - return 0; - } else if (exponent == 0x7FFF) { - return sign ? MA_DR_WAV_INT64_MIN : MA_DR_WAV_INT64_MAX; - } - exponent -= 16383; - if (exponent > 63) { - return sign ? MA_DR_WAV_INT64_MIN : MA_DR_WAV_INT64_MAX; - } else if (exponent < 1) { - return 0; - } - significand >>= (63 - exponent); - if (sign) { - return -(ma_int64)significand; - } else { - return (ma_int64)significand; - } -} -MA_PRIVATE void* ma_dr_wav__malloc_default(size_t sz, void* pUserData) -{ - (void)pUserData; - return MA_DR_WAV_MALLOC(sz); -} -MA_PRIVATE void* ma_dr_wav__realloc_default(void* p, size_t sz, void* pUserData) -{ - (void)pUserData; - return MA_DR_WAV_REALLOC(p, sz); -} -MA_PRIVATE void ma_dr_wav__free_default(void* p, void* pUserData) -{ - (void)pUserData; - MA_DR_WAV_FREE(p); -} -MA_PRIVATE void* ma_dr_wav__malloc_from_callbacks(size_t sz, const ma_allocation_callbacks* pAllocationCallbacks) -{ - if (pAllocationCallbacks == NULL) { - return NULL; - } - if (pAllocationCallbacks->onMalloc != NULL) { - return pAllocationCallbacks->onMalloc(sz, pAllocationCallbacks->pUserData); - } - if (pAllocationCallbacks->onRealloc != NULL) { - return pAllocationCallbacks->onRealloc(NULL, sz, pAllocationCallbacks->pUserData); - } - return NULL; -} -MA_PRIVATE void* ma_dr_wav__realloc_from_callbacks(void* p, size_t szNew, size_t szOld, const ma_allocation_callbacks* pAllocationCallbacks) -{ - if (pAllocationCallbacks == NULL) { - return NULL; - } - if (pAllocationCallbacks->onRealloc != NULL) { - return pAllocationCallbacks->onRealloc(p, szNew, pAllocationCallbacks->pUserData); - } - if (pAllocationCallbacks->onMalloc != NULL && pAllocationCallbacks->onFree != NULL) { - void* p2; - p2 = pAllocationCallbacks->onMalloc(szNew, pAllocationCallbacks->pUserData); - if (p2 == NULL) { - return NULL; - } - if (p != NULL) { - MA_DR_WAV_COPY_MEMORY(p2, p, szOld); - pAllocationCallbacks->onFree(p, pAllocationCallbacks->pUserData); - } - return p2; - } - return NULL; -} -MA_PRIVATE void ma_dr_wav__free_from_callbacks(void* p, const ma_allocation_callbacks* pAllocationCallbacks) -{ - if (p == NULL || pAllocationCallbacks == NULL) { - return; - } - if (pAllocationCallbacks->onFree != NULL) { - pAllocationCallbacks->onFree(p, pAllocationCallbacks->pUserData); - } -} -MA_PRIVATE ma_allocation_callbacks ma_dr_wav_copy_allocation_callbacks_or_defaults(const ma_allocation_callbacks* pAllocationCallbacks) -{ - if (pAllocationCallbacks != NULL) { - return *pAllocationCallbacks; - } else { - ma_allocation_callbacks allocationCallbacks; - allocationCallbacks.pUserData = NULL; - allocationCallbacks.onMalloc = ma_dr_wav__malloc_default; - allocationCallbacks.onRealloc = ma_dr_wav__realloc_default; - allocationCallbacks.onFree = ma_dr_wav__free_default; - return allocationCallbacks; - } -} -static MA_INLINE ma_bool32 ma_dr_wav__is_compressed_format_tag(ma_uint16 formatTag) -{ - return - formatTag == MA_DR_WAVE_FORMAT_ADPCM || - formatTag == MA_DR_WAVE_FORMAT_DVI_ADPCM; -} -MA_PRIVATE unsigned int ma_dr_wav__chunk_padding_size_riff(ma_uint64 chunkSize) -{ - return (unsigned int)(chunkSize % 2); -} -MA_PRIVATE unsigned int ma_dr_wav__chunk_padding_size_w64(ma_uint64 chunkSize) -{ - return (unsigned int)(chunkSize % 8); -} -MA_PRIVATE ma_uint64 ma_dr_wav_read_pcm_frames_s16__msadpcm(ma_dr_wav* pWav, ma_uint64 samplesToRead, ma_int16* pBufferOut); -MA_PRIVATE ma_uint64 ma_dr_wav_read_pcm_frames_s16__ima(ma_dr_wav* pWav, ma_uint64 samplesToRead, ma_int16* pBufferOut); -MA_PRIVATE ma_bool32 ma_dr_wav_init_write__internal(ma_dr_wav* pWav, const ma_dr_wav_data_format* pFormat, ma_uint64 totalSampleCount); -MA_PRIVATE ma_result ma_dr_wav__read_chunk_header(ma_dr_wav_read_proc onRead, void* pUserData, ma_dr_wav_container container, ma_uint64* pRunningBytesReadOut, ma_dr_wav_chunk_header* pHeaderOut) -{ - if (container == ma_dr_wav_container_riff || container == ma_dr_wav_container_rifx || container == ma_dr_wav_container_rf64 || container == ma_dr_wav_container_aiff) { - ma_uint8 sizeInBytes[4]; - if (onRead(pUserData, pHeaderOut->id.fourcc, 4) != 4) { - return MA_AT_END; - } - if (onRead(pUserData, sizeInBytes, 4) != 4) { - return MA_INVALID_FILE; - } - pHeaderOut->sizeInBytes = ma_dr_wav_bytes_to_u32_ex(sizeInBytes, container); - pHeaderOut->paddingSize = ma_dr_wav__chunk_padding_size_riff(pHeaderOut->sizeInBytes); - *pRunningBytesReadOut += 8; - } else if (container == ma_dr_wav_container_w64) { - ma_uint8 sizeInBytes[8]; - if (onRead(pUserData, pHeaderOut->id.guid, 16) != 16) { - return MA_AT_END; - } - if (onRead(pUserData, sizeInBytes, 8) != 8) { - return MA_INVALID_FILE; - } - pHeaderOut->sizeInBytes = ma_dr_wav_bytes_to_u64(sizeInBytes) - 24; - pHeaderOut->paddingSize = ma_dr_wav__chunk_padding_size_w64(pHeaderOut->sizeInBytes); - *pRunningBytesReadOut += 24; - } else { - return MA_INVALID_FILE; - } - return MA_SUCCESS; -} -MA_PRIVATE ma_bool32 ma_dr_wav__seek_forward(ma_dr_wav_seek_proc onSeek, ma_uint64 offset, void* pUserData) -{ - ma_uint64 bytesRemainingToSeek = offset; - while (bytesRemainingToSeek > 0) { - if (bytesRemainingToSeek > 0x7FFFFFFF) { - if (!onSeek(pUserData, 0x7FFFFFFF, ma_dr_wav_seek_origin_current)) { - return MA_FALSE; - } - bytesRemainingToSeek -= 0x7FFFFFFF; - } else { - if (!onSeek(pUserData, (int)bytesRemainingToSeek, ma_dr_wav_seek_origin_current)) { - return MA_FALSE; - } - bytesRemainingToSeek = 0; - } - } - return MA_TRUE; -} -MA_PRIVATE ma_bool32 ma_dr_wav__seek_from_start(ma_dr_wav_seek_proc onSeek, ma_uint64 offset, void* pUserData) -{ - if (offset <= 0x7FFFFFFF) { - return onSeek(pUserData, (int)offset, ma_dr_wav_seek_origin_start); - } - if (!onSeek(pUserData, 0x7FFFFFFF, ma_dr_wav_seek_origin_start)) { - return MA_FALSE; - } - offset -= 0x7FFFFFFF; - for (;;) { - if (offset <= 0x7FFFFFFF) { - return onSeek(pUserData, (int)offset, ma_dr_wav_seek_origin_current); - } - if (!onSeek(pUserData, 0x7FFFFFFF, ma_dr_wav_seek_origin_current)) { - return MA_FALSE; - } - offset -= 0x7FFFFFFF; - } -} -MA_PRIVATE size_t ma_dr_wav__on_read(ma_dr_wav_read_proc onRead, void* pUserData, void* pBufferOut, size_t bytesToRead, ma_uint64* pCursor) -{ - size_t bytesRead; - MA_DR_WAV_ASSERT(onRead != NULL); - MA_DR_WAV_ASSERT(pCursor != NULL); - bytesRead = onRead(pUserData, pBufferOut, bytesToRead); - *pCursor += bytesRead; - return bytesRead; -} -#if 0 -MA_PRIVATE ma_bool32 ma_dr_wav__on_seek(ma_dr_wav_seek_proc onSeek, void* pUserData, int offset, ma_dr_wav_seek_origin origin, ma_uint64* pCursor) -{ - MA_DR_WAV_ASSERT(onSeek != NULL); - MA_DR_WAV_ASSERT(pCursor != NULL); - if (!onSeek(pUserData, offset, origin)) { - return MA_FALSE; - } - if (origin == ma_dr_wav_seek_origin_start) { - *pCursor = offset; - } else { - *pCursor += offset; - } - return MA_TRUE; -} -#endif -#define MA_DR_WAV_SMPL_BYTES 36 -#define MA_DR_WAV_SMPL_LOOP_BYTES 24 -#define MA_DR_WAV_INST_BYTES 7 -#define MA_DR_WAV_ACID_BYTES 24 -#define MA_DR_WAV_CUE_BYTES 4 -#define MA_DR_WAV_BEXT_BYTES 602 -#define MA_DR_WAV_BEXT_DESCRIPTION_BYTES 256 -#define MA_DR_WAV_BEXT_ORIGINATOR_NAME_BYTES 32 -#define MA_DR_WAV_BEXT_ORIGINATOR_REF_BYTES 32 -#define MA_DR_WAV_BEXT_RESERVED_BYTES 180 -#define MA_DR_WAV_BEXT_UMID_BYTES 64 -#define MA_DR_WAV_CUE_POINT_BYTES 24 -#define MA_DR_WAV_LIST_LABEL_OR_NOTE_BYTES 4 -#define MA_DR_WAV_LIST_LABELLED_TEXT_BYTES 20 -#define MA_DR_WAV_METADATA_ALIGNMENT 8 -typedef enum -{ - ma_dr_wav__metadata_parser_stage_count, - ma_dr_wav__metadata_parser_stage_read -} ma_dr_wav__metadata_parser_stage; -typedef struct -{ - ma_dr_wav_read_proc onRead; - ma_dr_wav_seek_proc onSeek; - void *pReadSeekUserData; - ma_dr_wav__metadata_parser_stage stage; - ma_dr_wav_metadata *pMetadata; - ma_uint32 metadataCount; - ma_uint8 *pData; - ma_uint8 *pDataCursor; - ma_uint64 metadataCursor; - ma_uint64 extraCapacity; -} ma_dr_wav__metadata_parser; -MA_PRIVATE size_t ma_dr_wav__metadata_memory_capacity(ma_dr_wav__metadata_parser* pParser) -{ - ma_uint64 cap = sizeof(ma_dr_wav_metadata) * (ma_uint64)pParser->metadataCount + pParser->extraCapacity; - if (cap > MA_SIZE_MAX) { - return 0; - } - return (size_t)cap; -} -MA_PRIVATE ma_uint8* ma_dr_wav__metadata_get_memory(ma_dr_wav__metadata_parser* pParser, size_t size, size_t align) -{ - ma_uint8* pResult; - if (align) { - ma_uintptr modulo = (ma_uintptr)pParser->pDataCursor % align; - if (modulo != 0) { - pParser->pDataCursor += align - modulo; - } - } - pResult = pParser->pDataCursor; - MA_DR_WAV_ASSERT((pResult + size) <= (pParser->pData + ma_dr_wav__metadata_memory_capacity(pParser))); - pParser->pDataCursor += size; - return pResult; -} -MA_PRIVATE void ma_dr_wav__metadata_request_extra_memory_for_stage_2(ma_dr_wav__metadata_parser* pParser, size_t bytes, size_t align) -{ - size_t extra = bytes + (align ? (align - 1) : 0); - pParser->extraCapacity += extra; -} -MA_PRIVATE ma_result ma_dr_wav__metadata_alloc(ma_dr_wav__metadata_parser* pParser, ma_allocation_callbacks* pAllocationCallbacks) -{ - if (pParser->extraCapacity != 0 || pParser->metadataCount != 0) { - pAllocationCallbacks->onFree(pParser->pData, pAllocationCallbacks->pUserData); - pParser->pData = (ma_uint8*)pAllocationCallbacks->onMalloc(ma_dr_wav__metadata_memory_capacity(pParser), pAllocationCallbacks->pUserData); - pParser->pDataCursor = pParser->pData; - if (pParser->pData == NULL) { - return MA_OUT_OF_MEMORY; - } - pParser->pMetadata = (ma_dr_wav_metadata*)ma_dr_wav__metadata_get_memory(pParser, sizeof(ma_dr_wav_metadata) * pParser->metadataCount, 1); - pParser->metadataCursor = 0; - } - return MA_SUCCESS; -} -MA_PRIVATE size_t ma_dr_wav__metadata_parser_read(ma_dr_wav__metadata_parser* pParser, void* pBufferOut, size_t bytesToRead, ma_uint64* pCursor) -{ - if (pCursor != NULL) { - return ma_dr_wav__on_read(pParser->onRead, pParser->pReadSeekUserData, pBufferOut, bytesToRead, pCursor); - } else { - return pParser->onRead(pParser->pReadSeekUserData, pBufferOut, bytesToRead); - } -} -MA_PRIVATE ma_uint64 ma_dr_wav__read_smpl_to_metadata_obj(ma_dr_wav__metadata_parser* pParser, const ma_dr_wav_chunk_header* pChunkHeader, ma_dr_wav_metadata* pMetadata) -{ - ma_uint8 smplHeaderData[MA_DR_WAV_SMPL_BYTES]; - ma_uint64 totalBytesRead = 0; - size_t bytesJustRead; - if (pMetadata == NULL) { - return 0; - } - bytesJustRead = ma_dr_wav__metadata_parser_read(pParser, smplHeaderData, sizeof(smplHeaderData), &totalBytesRead); - MA_DR_WAV_ASSERT(pParser->stage == ma_dr_wav__metadata_parser_stage_read); - MA_DR_WAV_ASSERT(pChunkHeader != NULL); - if (pMetadata != NULL && bytesJustRead == sizeof(smplHeaderData)) { - ma_uint32 iSampleLoop; - pMetadata->type = ma_dr_wav_metadata_type_smpl; - pMetadata->data.smpl.manufacturerId = ma_dr_wav_bytes_to_u32(smplHeaderData + 0); - pMetadata->data.smpl.productId = ma_dr_wav_bytes_to_u32(smplHeaderData + 4); - pMetadata->data.smpl.samplePeriodNanoseconds = ma_dr_wav_bytes_to_u32(smplHeaderData + 8); - pMetadata->data.smpl.midiUnityNote = ma_dr_wav_bytes_to_u32(smplHeaderData + 12); - pMetadata->data.smpl.midiPitchFraction = ma_dr_wav_bytes_to_u32(smplHeaderData + 16); - pMetadata->data.smpl.smpteFormat = ma_dr_wav_bytes_to_u32(smplHeaderData + 20); - pMetadata->data.smpl.smpteOffset = ma_dr_wav_bytes_to_u32(smplHeaderData + 24); - pMetadata->data.smpl.sampleLoopCount = ma_dr_wav_bytes_to_u32(smplHeaderData + 28); - pMetadata->data.smpl.samplerSpecificDataSizeInBytes = ma_dr_wav_bytes_to_u32(smplHeaderData + 32); - if (pMetadata->data.smpl.sampleLoopCount == (pChunkHeader->sizeInBytes - MA_DR_WAV_SMPL_BYTES) / MA_DR_WAV_SMPL_LOOP_BYTES) { - pMetadata->data.smpl.pLoops = (ma_dr_wav_smpl_loop*)ma_dr_wav__metadata_get_memory(pParser, sizeof(ma_dr_wav_smpl_loop) * pMetadata->data.smpl.sampleLoopCount, MA_DR_WAV_METADATA_ALIGNMENT); - for (iSampleLoop = 0; iSampleLoop < pMetadata->data.smpl.sampleLoopCount; ++iSampleLoop) { - ma_uint8 smplLoopData[MA_DR_WAV_SMPL_LOOP_BYTES]; - bytesJustRead = ma_dr_wav__metadata_parser_read(pParser, smplLoopData, sizeof(smplLoopData), &totalBytesRead); - if (bytesJustRead == sizeof(smplLoopData)) { - pMetadata->data.smpl.pLoops[iSampleLoop].cuePointId = ma_dr_wav_bytes_to_u32(smplLoopData + 0); - pMetadata->data.smpl.pLoops[iSampleLoop].type = ma_dr_wav_bytes_to_u32(smplLoopData + 4); - pMetadata->data.smpl.pLoops[iSampleLoop].firstSampleByteOffset = ma_dr_wav_bytes_to_u32(smplLoopData + 8); - pMetadata->data.smpl.pLoops[iSampleLoop].lastSampleByteOffset = ma_dr_wav_bytes_to_u32(smplLoopData + 12); - pMetadata->data.smpl.pLoops[iSampleLoop].sampleFraction = ma_dr_wav_bytes_to_u32(smplLoopData + 16); - pMetadata->data.smpl.pLoops[iSampleLoop].playCount = ma_dr_wav_bytes_to_u32(smplLoopData + 20); - } else { - break; - } - } - if (pMetadata->data.smpl.samplerSpecificDataSizeInBytes > 0) { - pMetadata->data.smpl.pSamplerSpecificData = ma_dr_wav__metadata_get_memory(pParser, pMetadata->data.smpl.samplerSpecificDataSizeInBytes, 1); - MA_DR_WAV_ASSERT(pMetadata->data.smpl.pSamplerSpecificData != NULL); - ma_dr_wav__metadata_parser_read(pParser, pMetadata->data.smpl.pSamplerSpecificData, pMetadata->data.smpl.samplerSpecificDataSizeInBytes, &totalBytesRead); - } - } - } - return totalBytesRead; -} -MA_PRIVATE ma_uint64 ma_dr_wav__read_cue_to_metadata_obj(ma_dr_wav__metadata_parser* pParser, const ma_dr_wav_chunk_header* pChunkHeader, ma_dr_wav_metadata* pMetadata) -{ - ma_uint8 cueHeaderSectionData[MA_DR_WAV_CUE_BYTES]; - ma_uint64 totalBytesRead = 0; - size_t bytesJustRead; - if (pMetadata == NULL) { - return 0; - } - bytesJustRead = ma_dr_wav__metadata_parser_read(pParser, cueHeaderSectionData, sizeof(cueHeaderSectionData), &totalBytesRead); - MA_DR_WAV_ASSERT(pParser->stage == ma_dr_wav__metadata_parser_stage_read); - if (bytesJustRead == sizeof(cueHeaderSectionData)) { - pMetadata->type = ma_dr_wav_metadata_type_cue; - pMetadata->data.cue.cuePointCount = ma_dr_wav_bytes_to_u32(cueHeaderSectionData); - if (pMetadata->data.cue.cuePointCount == (pChunkHeader->sizeInBytes - MA_DR_WAV_CUE_BYTES) / MA_DR_WAV_CUE_POINT_BYTES) { - pMetadata->data.cue.pCuePoints = (ma_dr_wav_cue_point*)ma_dr_wav__metadata_get_memory(pParser, sizeof(ma_dr_wav_cue_point) * pMetadata->data.cue.cuePointCount, MA_DR_WAV_METADATA_ALIGNMENT); - MA_DR_WAV_ASSERT(pMetadata->data.cue.pCuePoints != NULL); - if (pMetadata->data.cue.cuePointCount > 0) { - ma_uint32 iCuePoint; - for (iCuePoint = 0; iCuePoint < pMetadata->data.cue.cuePointCount; ++iCuePoint) { - ma_uint8 cuePointData[MA_DR_WAV_CUE_POINT_BYTES]; - bytesJustRead = ma_dr_wav__metadata_parser_read(pParser, cuePointData, sizeof(cuePointData), &totalBytesRead); - if (bytesJustRead == sizeof(cuePointData)) { - pMetadata->data.cue.pCuePoints[iCuePoint].id = ma_dr_wav_bytes_to_u32(cuePointData + 0); - pMetadata->data.cue.pCuePoints[iCuePoint].playOrderPosition = ma_dr_wav_bytes_to_u32(cuePointData + 4); - pMetadata->data.cue.pCuePoints[iCuePoint].dataChunkId[0] = cuePointData[8]; - pMetadata->data.cue.pCuePoints[iCuePoint].dataChunkId[1] = cuePointData[9]; - pMetadata->data.cue.pCuePoints[iCuePoint].dataChunkId[2] = cuePointData[10]; - pMetadata->data.cue.pCuePoints[iCuePoint].dataChunkId[3] = cuePointData[11]; - pMetadata->data.cue.pCuePoints[iCuePoint].chunkStart = ma_dr_wav_bytes_to_u32(cuePointData + 12); - pMetadata->data.cue.pCuePoints[iCuePoint].blockStart = ma_dr_wav_bytes_to_u32(cuePointData + 16); - pMetadata->data.cue.pCuePoints[iCuePoint].sampleByteOffset = ma_dr_wav_bytes_to_u32(cuePointData + 20); - } else { - break; - } - } - } - } - } - return totalBytesRead; -} -MA_PRIVATE ma_uint64 ma_dr_wav__read_inst_to_metadata_obj(ma_dr_wav__metadata_parser* pParser, ma_dr_wav_metadata* pMetadata) -{ - ma_uint8 instData[MA_DR_WAV_INST_BYTES]; - ma_uint64 bytesRead; - if (pMetadata == NULL) { - return 0; - } - bytesRead = ma_dr_wav__metadata_parser_read(pParser, instData, sizeof(instData), NULL); - MA_DR_WAV_ASSERT(pParser->stage == ma_dr_wav__metadata_parser_stage_read); - if (bytesRead == sizeof(instData)) { - pMetadata->type = ma_dr_wav_metadata_type_inst; - pMetadata->data.inst.midiUnityNote = (ma_int8)instData[0]; - pMetadata->data.inst.fineTuneCents = (ma_int8)instData[1]; - pMetadata->data.inst.gainDecibels = (ma_int8)instData[2]; - pMetadata->data.inst.lowNote = (ma_int8)instData[3]; - pMetadata->data.inst.highNote = (ma_int8)instData[4]; - pMetadata->data.inst.lowVelocity = (ma_int8)instData[5]; - pMetadata->data.inst.highVelocity = (ma_int8)instData[6]; - } - return bytesRead; -} -MA_PRIVATE ma_uint64 ma_dr_wav__read_acid_to_metadata_obj(ma_dr_wav__metadata_parser* pParser, ma_dr_wav_metadata* pMetadata) -{ - ma_uint8 acidData[MA_DR_WAV_ACID_BYTES]; - ma_uint64 bytesRead; - if (pMetadata == NULL) { - return 0; - } - bytesRead = ma_dr_wav__metadata_parser_read(pParser, acidData, sizeof(acidData), NULL); - MA_DR_WAV_ASSERT(pParser->stage == ma_dr_wav__metadata_parser_stage_read); - if (bytesRead == sizeof(acidData)) { - pMetadata->type = ma_dr_wav_metadata_type_acid; - pMetadata->data.acid.flags = ma_dr_wav_bytes_to_u32(acidData + 0); - pMetadata->data.acid.midiUnityNote = ma_dr_wav_bytes_to_u16(acidData + 4); - pMetadata->data.acid.reserved1 = ma_dr_wav_bytes_to_u16(acidData + 6); - pMetadata->data.acid.reserved2 = ma_dr_wav_bytes_to_f32(acidData + 8); - pMetadata->data.acid.numBeats = ma_dr_wav_bytes_to_u32(acidData + 12); - pMetadata->data.acid.meterDenominator = ma_dr_wav_bytes_to_u16(acidData + 16); - pMetadata->data.acid.meterNumerator = ma_dr_wav_bytes_to_u16(acidData + 18); - pMetadata->data.acid.tempo = ma_dr_wav_bytes_to_f32(acidData + 20); - } - return bytesRead; -} -MA_PRIVATE size_t ma_dr_wav__strlen(const char* str) -{ - size_t result = 0; - while (*str++) { - result += 1; - } - return result; -} -MA_PRIVATE size_t ma_dr_wav__strlen_clamped(const char* str, size_t maxToRead) -{ - size_t result = 0; - while (*str++ && result < maxToRead) { - result += 1; - } - return result; -} -MA_PRIVATE char* ma_dr_wav__metadata_copy_string(ma_dr_wav__metadata_parser* pParser, const char* str, size_t maxToRead) -{ - size_t len = ma_dr_wav__strlen_clamped(str, maxToRead); - if (len) { - char* result = (char*)ma_dr_wav__metadata_get_memory(pParser, len + 1, 1); - MA_DR_WAV_ASSERT(result != NULL); - MA_DR_WAV_COPY_MEMORY(result, str, len); - result[len] = '\0'; - return result; - } else { - return NULL; - } -} -typedef struct -{ - const void* pBuffer; - size_t sizeInBytes; - size_t cursor; -} ma_dr_wav_buffer_reader; -MA_PRIVATE ma_result ma_dr_wav_buffer_reader_init(const void* pBuffer, size_t sizeInBytes, ma_dr_wav_buffer_reader* pReader) -{ - MA_DR_WAV_ASSERT(pBuffer != NULL); - MA_DR_WAV_ASSERT(pReader != NULL); - MA_DR_WAV_ZERO_OBJECT(pReader); - pReader->pBuffer = pBuffer; - pReader->sizeInBytes = sizeInBytes; - pReader->cursor = 0; - return MA_SUCCESS; -} -MA_PRIVATE const void* ma_dr_wav_buffer_reader_ptr(const ma_dr_wav_buffer_reader* pReader) -{ - MA_DR_WAV_ASSERT(pReader != NULL); - return ma_dr_wav_offset_ptr(pReader->pBuffer, pReader->cursor); -} -MA_PRIVATE ma_result ma_dr_wav_buffer_reader_seek(ma_dr_wav_buffer_reader* pReader, size_t bytesToSeek) -{ - MA_DR_WAV_ASSERT(pReader != NULL); - if (pReader->cursor + bytesToSeek > pReader->sizeInBytes) { - return MA_BAD_SEEK; - } - pReader->cursor += bytesToSeek; - return MA_SUCCESS; -} -MA_PRIVATE ma_result ma_dr_wav_buffer_reader_read(ma_dr_wav_buffer_reader* pReader, void* pDst, size_t bytesToRead, size_t* pBytesRead) -{ - ma_result result = MA_SUCCESS; - size_t bytesRemaining; - MA_DR_WAV_ASSERT(pReader != NULL); - if (pBytesRead != NULL) { - *pBytesRead = 0; - } - bytesRemaining = (pReader->sizeInBytes - pReader->cursor); - if (bytesToRead > bytesRemaining) { - bytesToRead = bytesRemaining; - } - if (pDst == NULL) { - result = ma_dr_wav_buffer_reader_seek(pReader, bytesToRead); - } else { - MA_DR_WAV_COPY_MEMORY(pDst, ma_dr_wav_buffer_reader_ptr(pReader), bytesToRead); - pReader->cursor += bytesToRead; - } - MA_DR_WAV_ASSERT(pReader->cursor <= pReader->sizeInBytes); - if (result == MA_SUCCESS) { - if (pBytesRead != NULL) { - *pBytesRead = bytesToRead; - } - } - return MA_SUCCESS; -} -MA_PRIVATE ma_result ma_dr_wav_buffer_reader_read_u16(ma_dr_wav_buffer_reader* pReader, ma_uint16* pDst) -{ - ma_result result; - size_t bytesRead; - ma_uint8 data[2]; - MA_DR_WAV_ASSERT(pReader != NULL); - MA_DR_WAV_ASSERT(pDst != NULL); - *pDst = 0; - result = ma_dr_wav_buffer_reader_read(pReader, data, sizeof(*pDst), &bytesRead); - if (result != MA_SUCCESS || bytesRead != sizeof(*pDst)) { - return result; - } - *pDst = ma_dr_wav_bytes_to_u16(data); - return MA_SUCCESS; -} -MA_PRIVATE ma_result ma_dr_wav_buffer_reader_read_u32(ma_dr_wav_buffer_reader* pReader, ma_uint32* pDst) -{ - ma_result result; - size_t bytesRead; - ma_uint8 data[4]; - MA_DR_WAV_ASSERT(pReader != NULL); - MA_DR_WAV_ASSERT(pDst != NULL); - *pDst = 0; - result = ma_dr_wav_buffer_reader_read(pReader, data, sizeof(*pDst), &bytesRead); - if (result != MA_SUCCESS || bytesRead != sizeof(*pDst)) { - return result; - } - *pDst = ma_dr_wav_bytes_to_u32(data); - return MA_SUCCESS; -} -MA_PRIVATE ma_uint64 ma_dr_wav__read_bext_to_metadata_obj(ma_dr_wav__metadata_parser* pParser, ma_dr_wav_metadata* pMetadata, ma_uint64 chunkSize) -{ - ma_uint8 bextData[MA_DR_WAV_BEXT_BYTES]; - size_t bytesRead = ma_dr_wav__metadata_parser_read(pParser, bextData, sizeof(bextData), NULL); - MA_DR_WAV_ASSERT(pParser->stage == ma_dr_wav__metadata_parser_stage_read); - if (bytesRead == sizeof(bextData)) { - ma_dr_wav_buffer_reader reader; - ma_uint32 timeReferenceLow; - ma_uint32 timeReferenceHigh; - size_t extraBytes; - pMetadata->type = ma_dr_wav_metadata_type_bext; - if (ma_dr_wav_buffer_reader_init(bextData, bytesRead, &reader) == MA_SUCCESS) { - pMetadata->data.bext.pDescription = ma_dr_wav__metadata_copy_string(pParser, (const char*)ma_dr_wav_buffer_reader_ptr(&reader), MA_DR_WAV_BEXT_DESCRIPTION_BYTES); - ma_dr_wav_buffer_reader_seek(&reader, MA_DR_WAV_BEXT_DESCRIPTION_BYTES); - pMetadata->data.bext.pOriginatorName = ma_dr_wav__metadata_copy_string(pParser, (const char*)ma_dr_wav_buffer_reader_ptr(&reader), MA_DR_WAV_BEXT_ORIGINATOR_NAME_BYTES); - ma_dr_wav_buffer_reader_seek(&reader, MA_DR_WAV_BEXT_ORIGINATOR_NAME_BYTES); - pMetadata->data.bext.pOriginatorReference = ma_dr_wav__metadata_copy_string(pParser, (const char*)ma_dr_wav_buffer_reader_ptr(&reader), MA_DR_WAV_BEXT_ORIGINATOR_REF_BYTES); - ma_dr_wav_buffer_reader_seek(&reader, MA_DR_WAV_BEXT_ORIGINATOR_REF_BYTES); - ma_dr_wav_buffer_reader_read(&reader, pMetadata->data.bext.pOriginationDate, sizeof(pMetadata->data.bext.pOriginationDate), NULL); - ma_dr_wav_buffer_reader_read(&reader, pMetadata->data.bext.pOriginationTime, sizeof(pMetadata->data.bext.pOriginationTime), NULL); - ma_dr_wav_buffer_reader_read_u32(&reader, &timeReferenceLow); - ma_dr_wav_buffer_reader_read_u32(&reader, &timeReferenceHigh); - pMetadata->data.bext.timeReference = ((ma_uint64)timeReferenceHigh << 32) + timeReferenceLow; - ma_dr_wav_buffer_reader_read_u16(&reader, &pMetadata->data.bext.version); - pMetadata->data.bext.pUMID = ma_dr_wav__metadata_get_memory(pParser, MA_DR_WAV_BEXT_UMID_BYTES, 1); - ma_dr_wav_buffer_reader_read(&reader, pMetadata->data.bext.pUMID, MA_DR_WAV_BEXT_UMID_BYTES, NULL); - ma_dr_wav_buffer_reader_read_u16(&reader, &pMetadata->data.bext.loudnessValue); - ma_dr_wav_buffer_reader_read_u16(&reader, &pMetadata->data.bext.loudnessRange); - ma_dr_wav_buffer_reader_read_u16(&reader, &pMetadata->data.bext.maxTruePeakLevel); - ma_dr_wav_buffer_reader_read_u16(&reader, &pMetadata->data.bext.maxMomentaryLoudness); - ma_dr_wav_buffer_reader_read_u16(&reader, &pMetadata->data.bext.maxShortTermLoudness); - MA_DR_WAV_ASSERT((ma_dr_wav_offset_ptr(ma_dr_wav_buffer_reader_ptr(&reader), MA_DR_WAV_BEXT_RESERVED_BYTES)) == (bextData + MA_DR_WAV_BEXT_BYTES)); - extraBytes = (size_t)(chunkSize - MA_DR_WAV_BEXT_BYTES); - if (extraBytes > 0) { - pMetadata->data.bext.pCodingHistory = (char*)ma_dr_wav__metadata_get_memory(pParser, extraBytes + 1, 1); - MA_DR_WAV_ASSERT(pMetadata->data.bext.pCodingHistory != NULL); - bytesRead += ma_dr_wav__metadata_parser_read(pParser, pMetadata->data.bext.pCodingHistory, extraBytes, NULL); - pMetadata->data.bext.codingHistorySize = (ma_uint32)ma_dr_wav__strlen(pMetadata->data.bext.pCodingHistory); - } else { - pMetadata->data.bext.pCodingHistory = NULL; - pMetadata->data.bext.codingHistorySize = 0; - } - } - } - return bytesRead; -} -MA_PRIVATE ma_uint64 ma_dr_wav__read_list_label_or_note_to_metadata_obj(ma_dr_wav__metadata_parser* pParser, ma_dr_wav_metadata* pMetadata, ma_uint64 chunkSize, ma_dr_wav_metadata_type type) -{ - ma_uint8 cueIDBuffer[MA_DR_WAV_LIST_LABEL_OR_NOTE_BYTES]; - ma_uint64 totalBytesRead = 0; - size_t bytesJustRead = ma_dr_wav__metadata_parser_read(pParser, cueIDBuffer, sizeof(cueIDBuffer), &totalBytesRead); - MA_DR_WAV_ASSERT(pParser->stage == ma_dr_wav__metadata_parser_stage_read); - if (bytesJustRead == sizeof(cueIDBuffer)) { - ma_uint32 sizeIncludingNullTerminator; - pMetadata->type = type; - pMetadata->data.labelOrNote.cuePointId = ma_dr_wav_bytes_to_u32(cueIDBuffer); - sizeIncludingNullTerminator = (ma_uint32)chunkSize - MA_DR_WAV_LIST_LABEL_OR_NOTE_BYTES; - if (sizeIncludingNullTerminator > 0) { - pMetadata->data.labelOrNote.stringLength = sizeIncludingNullTerminator - 1; - pMetadata->data.labelOrNote.pString = (char*)ma_dr_wav__metadata_get_memory(pParser, sizeIncludingNullTerminator, 1); - MA_DR_WAV_ASSERT(pMetadata->data.labelOrNote.pString != NULL); - ma_dr_wav__metadata_parser_read(pParser, pMetadata->data.labelOrNote.pString, sizeIncludingNullTerminator, &totalBytesRead); - } else { - pMetadata->data.labelOrNote.stringLength = 0; - pMetadata->data.labelOrNote.pString = NULL; - } - } - return totalBytesRead; -} -MA_PRIVATE ma_uint64 ma_dr_wav__read_list_labelled_cue_region_to_metadata_obj(ma_dr_wav__metadata_parser* pParser, ma_dr_wav_metadata* pMetadata, ma_uint64 chunkSize) -{ - ma_uint8 buffer[MA_DR_WAV_LIST_LABELLED_TEXT_BYTES]; - ma_uint64 totalBytesRead = 0; - size_t bytesJustRead = ma_dr_wav__metadata_parser_read(pParser, buffer, sizeof(buffer), &totalBytesRead); - MA_DR_WAV_ASSERT(pParser->stage == ma_dr_wav__metadata_parser_stage_read); - if (bytesJustRead == sizeof(buffer)) { - ma_uint32 sizeIncludingNullTerminator; - pMetadata->type = ma_dr_wav_metadata_type_list_labelled_cue_region; - pMetadata->data.labelledCueRegion.cuePointId = ma_dr_wav_bytes_to_u32(buffer + 0); - pMetadata->data.labelledCueRegion.sampleLength = ma_dr_wav_bytes_to_u32(buffer + 4); - pMetadata->data.labelledCueRegion.purposeId[0] = buffer[8]; - pMetadata->data.labelledCueRegion.purposeId[1] = buffer[9]; - pMetadata->data.labelledCueRegion.purposeId[2] = buffer[10]; - pMetadata->data.labelledCueRegion.purposeId[3] = buffer[11]; - pMetadata->data.labelledCueRegion.country = ma_dr_wav_bytes_to_u16(buffer + 12); - pMetadata->data.labelledCueRegion.language = ma_dr_wav_bytes_to_u16(buffer + 14); - pMetadata->data.labelledCueRegion.dialect = ma_dr_wav_bytes_to_u16(buffer + 16); - pMetadata->data.labelledCueRegion.codePage = ma_dr_wav_bytes_to_u16(buffer + 18); - sizeIncludingNullTerminator = (ma_uint32)chunkSize - MA_DR_WAV_LIST_LABELLED_TEXT_BYTES; - if (sizeIncludingNullTerminator > 0) { - pMetadata->data.labelledCueRegion.stringLength = sizeIncludingNullTerminator - 1; - pMetadata->data.labelledCueRegion.pString = (char*)ma_dr_wav__metadata_get_memory(pParser, sizeIncludingNullTerminator, 1); - MA_DR_WAV_ASSERT(pMetadata->data.labelledCueRegion.pString != NULL); - ma_dr_wav__metadata_parser_read(pParser, pMetadata->data.labelledCueRegion.pString, sizeIncludingNullTerminator, &totalBytesRead); - } else { - pMetadata->data.labelledCueRegion.stringLength = 0; - pMetadata->data.labelledCueRegion.pString = NULL; - } - } - return totalBytesRead; -} -MA_PRIVATE ma_uint64 ma_dr_wav__metadata_process_info_text_chunk(ma_dr_wav__metadata_parser* pParser, ma_uint64 chunkSize, ma_dr_wav_metadata_type type) -{ - ma_uint64 bytesRead = 0; - ma_uint32 stringSizeWithNullTerminator = (ma_uint32)chunkSize; - if (pParser->stage == ma_dr_wav__metadata_parser_stage_count) { - pParser->metadataCount += 1; - ma_dr_wav__metadata_request_extra_memory_for_stage_2(pParser, stringSizeWithNullTerminator, 1); - } else { - ma_dr_wav_metadata* pMetadata = &pParser->pMetadata[pParser->metadataCursor]; - pMetadata->type = type; - if (stringSizeWithNullTerminator > 0) { - pMetadata->data.infoText.stringLength = stringSizeWithNullTerminator - 1; - pMetadata->data.infoText.pString = (char*)ma_dr_wav__metadata_get_memory(pParser, stringSizeWithNullTerminator, 1); - MA_DR_WAV_ASSERT(pMetadata->data.infoText.pString != NULL); - bytesRead = ma_dr_wav__metadata_parser_read(pParser, pMetadata->data.infoText.pString, (size_t)stringSizeWithNullTerminator, NULL); - if (bytesRead == chunkSize) { - pParser->metadataCursor += 1; - } else { - } - } else { - pMetadata->data.infoText.stringLength = 0; - pMetadata->data.infoText.pString = NULL; - pParser->metadataCursor += 1; - } - } - return bytesRead; -} -MA_PRIVATE ma_uint64 ma_dr_wav__metadata_process_unknown_chunk(ma_dr_wav__metadata_parser* pParser, const ma_uint8* pChunkId, ma_uint64 chunkSize, ma_dr_wav_metadata_location location) -{ - ma_uint64 bytesRead = 0; - if (location == ma_dr_wav_metadata_location_invalid) { - return 0; - } - if (ma_dr_wav_fourcc_equal(pChunkId, "data") || ma_dr_wav_fourcc_equal(pChunkId, "fmt ") || ma_dr_wav_fourcc_equal(pChunkId, "fact")) { - return 0; - } - if (pParser->stage == ma_dr_wav__metadata_parser_stage_count) { - pParser->metadataCount += 1; - ma_dr_wav__metadata_request_extra_memory_for_stage_2(pParser, (size_t)chunkSize, 1); - } else { - ma_dr_wav_metadata* pMetadata = &pParser->pMetadata[pParser->metadataCursor]; - pMetadata->type = ma_dr_wav_metadata_type_unknown; - pMetadata->data.unknown.chunkLocation = location; - pMetadata->data.unknown.id[0] = pChunkId[0]; - pMetadata->data.unknown.id[1] = pChunkId[1]; - pMetadata->data.unknown.id[2] = pChunkId[2]; - pMetadata->data.unknown.id[3] = pChunkId[3]; - pMetadata->data.unknown.dataSizeInBytes = (ma_uint32)chunkSize; - pMetadata->data.unknown.pData = (ma_uint8 *)ma_dr_wav__metadata_get_memory(pParser, (size_t)chunkSize, 1); - MA_DR_WAV_ASSERT(pMetadata->data.unknown.pData != NULL); - bytesRead = ma_dr_wav__metadata_parser_read(pParser, pMetadata->data.unknown.pData, pMetadata->data.unknown.dataSizeInBytes, NULL); - if (bytesRead == pMetadata->data.unknown.dataSizeInBytes) { - pParser->metadataCursor += 1; - } else { - } - } - return bytesRead; -} -MA_PRIVATE ma_bool32 ma_dr_wav__chunk_matches(ma_dr_wav_metadata_type allowedMetadataTypes, const ma_uint8* pChunkID, ma_dr_wav_metadata_type type, const char* pID) -{ - return (allowedMetadataTypes & type) && ma_dr_wav_fourcc_equal(pChunkID, pID); -} -MA_PRIVATE ma_uint64 ma_dr_wav__metadata_process_chunk(ma_dr_wav__metadata_parser* pParser, const ma_dr_wav_chunk_header* pChunkHeader, ma_dr_wav_metadata_type allowedMetadataTypes) -{ - const ma_uint8 *pChunkID = pChunkHeader->id.fourcc; - ma_uint64 bytesRead = 0; - if (ma_dr_wav__chunk_matches(allowedMetadataTypes, pChunkID, ma_dr_wav_metadata_type_smpl, "smpl")) { - if (pChunkHeader->sizeInBytes >= MA_DR_WAV_SMPL_BYTES) { - if (pParser->stage == ma_dr_wav__metadata_parser_stage_count) { - ma_uint8 buffer[4]; - size_t bytesJustRead; - if (!pParser->onSeek(pParser->pReadSeekUserData, 28, ma_dr_wav_seek_origin_current)) { - return bytesRead; - } - bytesRead += 28; - bytesJustRead = ma_dr_wav__metadata_parser_read(pParser, buffer, sizeof(buffer), &bytesRead); - if (bytesJustRead == sizeof(buffer)) { - ma_uint32 loopCount = ma_dr_wav_bytes_to_u32(buffer); - ma_uint64 calculatedLoopCount; - calculatedLoopCount = (pChunkHeader->sizeInBytes - MA_DR_WAV_SMPL_BYTES) / MA_DR_WAV_SMPL_LOOP_BYTES; - if (calculatedLoopCount == loopCount) { - bytesJustRead = ma_dr_wav__metadata_parser_read(pParser, buffer, sizeof(buffer), &bytesRead); - if (bytesJustRead == sizeof(buffer)) { - ma_uint32 samplerSpecificDataSizeInBytes = ma_dr_wav_bytes_to_u32(buffer); - pParser->metadataCount += 1; - ma_dr_wav__metadata_request_extra_memory_for_stage_2(pParser, sizeof(ma_dr_wav_smpl_loop) * loopCount, MA_DR_WAV_METADATA_ALIGNMENT); - ma_dr_wav__metadata_request_extra_memory_for_stage_2(pParser, samplerSpecificDataSizeInBytes, 1); - } - } else { - } - } - } else { - bytesRead = ma_dr_wav__read_smpl_to_metadata_obj(pParser, pChunkHeader, &pParser->pMetadata[pParser->metadataCursor]); - if (bytesRead == pChunkHeader->sizeInBytes) { - pParser->metadataCursor += 1; - } else { - } - } - } else { - } - } else if (ma_dr_wav__chunk_matches(allowedMetadataTypes, pChunkID, ma_dr_wav_metadata_type_inst, "inst")) { - if (pChunkHeader->sizeInBytes == MA_DR_WAV_INST_BYTES) { - if (pParser->stage == ma_dr_wav__metadata_parser_stage_count) { - pParser->metadataCount += 1; - } else { - bytesRead = ma_dr_wav__read_inst_to_metadata_obj(pParser, &pParser->pMetadata[pParser->metadataCursor]); - if (bytesRead == pChunkHeader->sizeInBytes) { - pParser->metadataCursor += 1; - } else { - } - } - } else { - } - } else if (ma_dr_wav__chunk_matches(allowedMetadataTypes, pChunkID, ma_dr_wav_metadata_type_acid, "acid")) { - if (pChunkHeader->sizeInBytes == MA_DR_WAV_ACID_BYTES) { - if (pParser->stage == ma_dr_wav__metadata_parser_stage_count) { - pParser->metadataCount += 1; - } else { - bytesRead = ma_dr_wav__read_acid_to_metadata_obj(pParser, &pParser->pMetadata[pParser->metadataCursor]); - if (bytesRead == pChunkHeader->sizeInBytes) { - pParser->metadataCursor += 1; - } else { - } - } - } else { - } - } else if (ma_dr_wav__chunk_matches(allowedMetadataTypes, pChunkID, ma_dr_wav_metadata_type_cue, "cue ")) { - if (pChunkHeader->sizeInBytes >= MA_DR_WAV_CUE_BYTES) { - if (pParser->stage == ma_dr_wav__metadata_parser_stage_count) { - size_t cueCount; - pParser->metadataCount += 1; - cueCount = (size_t)(pChunkHeader->sizeInBytes - MA_DR_WAV_CUE_BYTES) / MA_DR_WAV_CUE_POINT_BYTES; - ma_dr_wav__metadata_request_extra_memory_for_stage_2(pParser, sizeof(ma_dr_wav_cue_point) * cueCount, MA_DR_WAV_METADATA_ALIGNMENT); - } else { - bytesRead = ma_dr_wav__read_cue_to_metadata_obj(pParser, pChunkHeader, &pParser->pMetadata[pParser->metadataCursor]); - if (bytesRead == pChunkHeader->sizeInBytes) { - pParser->metadataCursor += 1; - } else { - } - } - } else { - } - } else if (ma_dr_wav__chunk_matches(allowedMetadataTypes, pChunkID, ma_dr_wav_metadata_type_bext, "bext")) { - if (pChunkHeader->sizeInBytes >= MA_DR_WAV_BEXT_BYTES) { - if (pParser->stage == ma_dr_wav__metadata_parser_stage_count) { - char buffer[MA_DR_WAV_BEXT_DESCRIPTION_BYTES + 1]; - size_t allocSizeNeeded = MA_DR_WAV_BEXT_UMID_BYTES; - size_t bytesJustRead; - buffer[MA_DR_WAV_BEXT_DESCRIPTION_BYTES] = '\0'; - bytesJustRead = ma_dr_wav__metadata_parser_read(pParser, buffer, MA_DR_WAV_BEXT_DESCRIPTION_BYTES, &bytesRead); - if (bytesJustRead != MA_DR_WAV_BEXT_DESCRIPTION_BYTES) { - return bytesRead; - } - allocSizeNeeded += ma_dr_wav__strlen(buffer) + 1; - buffer[MA_DR_WAV_BEXT_ORIGINATOR_NAME_BYTES] = '\0'; - bytesJustRead = ma_dr_wav__metadata_parser_read(pParser, buffer, MA_DR_WAV_BEXT_ORIGINATOR_NAME_BYTES, &bytesRead); - if (bytesJustRead != MA_DR_WAV_BEXT_ORIGINATOR_NAME_BYTES) { - return bytesRead; - } - allocSizeNeeded += ma_dr_wav__strlen(buffer) + 1; - buffer[MA_DR_WAV_BEXT_ORIGINATOR_REF_BYTES] = '\0'; - bytesJustRead = ma_dr_wav__metadata_parser_read(pParser, buffer, MA_DR_WAV_BEXT_ORIGINATOR_REF_BYTES, &bytesRead); - if (bytesJustRead != MA_DR_WAV_BEXT_ORIGINATOR_REF_BYTES) { - return bytesRead; - } - allocSizeNeeded += ma_dr_wav__strlen(buffer) + 1; - allocSizeNeeded += (size_t)pChunkHeader->sizeInBytes - MA_DR_WAV_BEXT_BYTES; - ma_dr_wav__metadata_request_extra_memory_for_stage_2(pParser, allocSizeNeeded, 1); - pParser->metadataCount += 1; - } else { - bytesRead = ma_dr_wav__read_bext_to_metadata_obj(pParser, &pParser->pMetadata[pParser->metadataCursor], pChunkHeader->sizeInBytes); - if (bytesRead == pChunkHeader->sizeInBytes) { - pParser->metadataCursor += 1; - } else { - } - } - } else { - } - } else if (ma_dr_wav_fourcc_equal(pChunkID, "LIST") || ma_dr_wav_fourcc_equal(pChunkID, "list")) { - ma_dr_wav_metadata_location listType = ma_dr_wav_metadata_location_invalid; - while (bytesRead < pChunkHeader->sizeInBytes) { - ma_uint8 subchunkId[4]; - ma_uint8 subchunkSizeBuffer[4]; - ma_uint64 subchunkDataSize; - ma_uint64 subchunkBytesRead = 0; - ma_uint64 bytesJustRead = ma_dr_wav__metadata_parser_read(pParser, subchunkId, sizeof(subchunkId), &bytesRead); - if (bytesJustRead != sizeof(subchunkId)) { - break; - } - if (ma_dr_wav_fourcc_equal(subchunkId, "adtl")) { - listType = ma_dr_wav_metadata_location_inside_adtl_list; - continue; - } else if (ma_dr_wav_fourcc_equal(subchunkId, "INFO")) { - listType = ma_dr_wav_metadata_location_inside_info_list; - continue; - } - bytesJustRead = ma_dr_wav__metadata_parser_read(pParser, subchunkSizeBuffer, sizeof(subchunkSizeBuffer), &bytesRead); - if (bytesJustRead != sizeof(subchunkSizeBuffer)) { - break; - } - subchunkDataSize = ma_dr_wav_bytes_to_u32(subchunkSizeBuffer); - if (ma_dr_wav__chunk_matches(allowedMetadataTypes, subchunkId, ma_dr_wav_metadata_type_list_label, "labl") || ma_dr_wav__chunk_matches(allowedMetadataTypes, subchunkId, ma_dr_wav_metadata_type_list_note, "note")) { - if (subchunkDataSize >= MA_DR_WAV_LIST_LABEL_OR_NOTE_BYTES) { - ma_uint64 stringSizeWithNullTerm = subchunkDataSize - MA_DR_WAV_LIST_LABEL_OR_NOTE_BYTES; - if (pParser->stage == ma_dr_wav__metadata_parser_stage_count) { - pParser->metadataCount += 1; - ma_dr_wav__metadata_request_extra_memory_for_stage_2(pParser, (size_t)stringSizeWithNullTerm, 1); - } else { - subchunkBytesRead = ma_dr_wav__read_list_label_or_note_to_metadata_obj(pParser, &pParser->pMetadata[pParser->metadataCursor], subchunkDataSize, ma_dr_wav_fourcc_equal(subchunkId, "labl") ? ma_dr_wav_metadata_type_list_label : ma_dr_wav_metadata_type_list_note); - if (subchunkBytesRead == subchunkDataSize) { - pParser->metadataCursor += 1; - } else { - } - } - } else { - } - } else if (ma_dr_wav__chunk_matches(allowedMetadataTypes, subchunkId, ma_dr_wav_metadata_type_list_labelled_cue_region, "ltxt")) { - if (subchunkDataSize >= MA_DR_WAV_LIST_LABELLED_TEXT_BYTES) { - ma_uint64 stringSizeWithNullTerminator = subchunkDataSize - MA_DR_WAV_LIST_LABELLED_TEXT_BYTES; - if (pParser->stage == ma_dr_wav__metadata_parser_stage_count) { - pParser->metadataCount += 1; - ma_dr_wav__metadata_request_extra_memory_for_stage_2(pParser, (size_t)stringSizeWithNullTerminator, 1); - } else { - subchunkBytesRead = ma_dr_wav__read_list_labelled_cue_region_to_metadata_obj(pParser, &pParser->pMetadata[pParser->metadataCursor], subchunkDataSize); - if (subchunkBytesRead == subchunkDataSize) { - pParser->metadataCursor += 1; - } else { - } - } - } else { - } - } else if (ma_dr_wav__chunk_matches(allowedMetadataTypes, subchunkId, ma_dr_wav_metadata_type_list_info_software, "ISFT")) { - subchunkBytesRead = ma_dr_wav__metadata_process_info_text_chunk(pParser, subchunkDataSize, ma_dr_wav_metadata_type_list_info_software); - } else if (ma_dr_wav__chunk_matches(allowedMetadataTypes, subchunkId, ma_dr_wav_metadata_type_list_info_copyright, "ICOP")) { - subchunkBytesRead = ma_dr_wav__metadata_process_info_text_chunk(pParser, subchunkDataSize, ma_dr_wav_metadata_type_list_info_copyright); - } else if (ma_dr_wav__chunk_matches(allowedMetadataTypes, subchunkId, ma_dr_wav_metadata_type_list_info_title, "INAM")) { - subchunkBytesRead = ma_dr_wav__metadata_process_info_text_chunk(pParser, subchunkDataSize, ma_dr_wav_metadata_type_list_info_title); - } else if (ma_dr_wav__chunk_matches(allowedMetadataTypes, subchunkId, ma_dr_wav_metadata_type_list_info_artist, "IART")) { - subchunkBytesRead = ma_dr_wav__metadata_process_info_text_chunk(pParser, subchunkDataSize, ma_dr_wav_metadata_type_list_info_artist); - } else if (ma_dr_wav__chunk_matches(allowedMetadataTypes, subchunkId, ma_dr_wav_metadata_type_list_info_comment, "ICMT")) { - subchunkBytesRead = ma_dr_wav__metadata_process_info_text_chunk(pParser, subchunkDataSize, ma_dr_wav_metadata_type_list_info_comment); - } else if (ma_dr_wav__chunk_matches(allowedMetadataTypes, subchunkId, ma_dr_wav_metadata_type_list_info_date, "ICRD")) { - subchunkBytesRead = ma_dr_wav__metadata_process_info_text_chunk(pParser, subchunkDataSize, ma_dr_wav_metadata_type_list_info_date); - } else if (ma_dr_wav__chunk_matches(allowedMetadataTypes, subchunkId, ma_dr_wav_metadata_type_list_info_genre, "IGNR")) { - subchunkBytesRead = ma_dr_wav__metadata_process_info_text_chunk(pParser, subchunkDataSize, ma_dr_wav_metadata_type_list_info_genre); - } else if (ma_dr_wav__chunk_matches(allowedMetadataTypes, subchunkId, ma_dr_wav_metadata_type_list_info_album, "IPRD")) { - subchunkBytesRead = ma_dr_wav__metadata_process_info_text_chunk(pParser, subchunkDataSize, ma_dr_wav_metadata_type_list_info_album); - } else if (ma_dr_wav__chunk_matches(allowedMetadataTypes, subchunkId, ma_dr_wav_metadata_type_list_info_tracknumber, "ITRK")) { - subchunkBytesRead = ma_dr_wav__metadata_process_info_text_chunk(pParser, subchunkDataSize, ma_dr_wav_metadata_type_list_info_tracknumber); - } else if ((allowedMetadataTypes & ma_dr_wav_metadata_type_unknown) != 0) { - subchunkBytesRead = ma_dr_wav__metadata_process_unknown_chunk(pParser, subchunkId, subchunkDataSize, listType); - } - bytesRead += subchunkBytesRead; - MA_DR_WAV_ASSERT(subchunkBytesRead <= subchunkDataSize); - if (subchunkBytesRead < subchunkDataSize) { - ma_uint64 bytesToSeek = subchunkDataSize - subchunkBytesRead; - if (!pParser->onSeek(pParser->pReadSeekUserData, (int)bytesToSeek, ma_dr_wav_seek_origin_current)) { - break; - } - bytesRead += bytesToSeek; - } - if ((subchunkDataSize % 2) == 1) { - if (!pParser->onSeek(pParser->pReadSeekUserData, 1, ma_dr_wav_seek_origin_current)) { - break; - } - bytesRead += 1; - } - } - } else if ((allowedMetadataTypes & ma_dr_wav_metadata_type_unknown) != 0) { - bytesRead = ma_dr_wav__metadata_process_unknown_chunk(pParser, pChunkID, pChunkHeader->sizeInBytes, ma_dr_wav_metadata_location_top_level); - } - return bytesRead; -} -MA_PRIVATE ma_uint32 ma_dr_wav_get_bytes_per_pcm_frame(ma_dr_wav* pWav) -{ - ma_uint32 bytesPerFrame; - if ((pWav->bitsPerSample & 0x7) == 0) { - bytesPerFrame = (pWav->bitsPerSample * pWav->fmt.channels) >> 3; - } else { - bytesPerFrame = pWav->fmt.blockAlign; - } - if (pWav->translatedFormatTag == MA_DR_WAVE_FORMAT_ALAW || pWav->translatedFormatTag == MA_DR_WAVE_FORMAT_MULAW) { - if (bytesPerFrame != pWav->fmt.channels) { - return 0; - } - } - return bytesPerFrame; -} -MA_API ma_uint16 ma_dr_wav_fmt_get_format(const ma_dr_wav_fmt* pFMT) -{ - if (pFMT == NULL) { - return 0; - } - if (pFMT->formatTag != MA_DR_WAVE_FORMAT_EXTENSIBLE) { - return pFMT->formatTag; - } else { - return ma_dr_wav_bytes_to_u16(pFMT->subFormat); - } -} -MA_PRIVATE ma_bool32 ma_dr_wav_preinit(ma_dr_wav* pWav, ma_dr_wav_read_proc onRead, ma_dr_wav_seek_proc onSeek, void* pReadSeekUserData, const ma_allocation_callbacks* pAllocationCallbacks) -{ - if (pWav == NULL || onRead == NULL || onSeek == NULL) { - return MA_FALSE; - } - MA_DR_WAV_ZERO_MEMORY(pWav, sizeof(*pWav)); - pWav->onRead = onRead; - pWav->onSeek = onSeek; - pWav->pUserData = pReadSeekUserData; - pWav->allocationCallbacks = ma_dr_wav_copy_allocation_callbacks_or_defaults(pAllocationCallbacks); - if (pWav->allocationCallbacks.onFree == NULL || (pWav->allocationCallbacks.onMalloc == NULL && pWav->allocationCallbacks.onRealloc == NULL)) { - return MA_FALSE; - } - return MA_TRUE; -} -MA_PRIVATE ma_bool32 ma_dr_wav_init__internal(ma_dr_wav* pWav, ma_dr_wav_chunk_proc onChunk, void* pChunkUserData, ma_uint32 flags) -{ - ma_result result; - ma_uint64 cursor; - ma_bool32 sequential; - ma_uint8 riff[4]; - ma_dr_wav_fmt fmt; - unsigned short translatedFormatTag; - ma_uint64 dataChunkSize = 0; - ma_uint64 sampleCountFromFactChunk = 0; - ma_uint64 metadataStartPos; - ma_dr_wav__metadata_parser metadataParser; - ma_bool8 isProcessingMetadata = MA_FALSE; - ma_bool8 foundChunk_fmt = MA_FALSE; - ma_bool8 foundChunk_data = MA_FALSE; - ma_bool8 isAIFCFormType = MA_FALSE; - ma_uint64 aiffFrameCount = 0; - cursor = 0; - sequential = (flags & MA_DR_WAV_SEQUENTIAL) != 0; - MA_DR_WAV_ZERO_OBJECT(&fmt); - if (ma_dr_wav__on_read(pWav->onRead, pWav->pUserData, riff, sizeof(riff), &cursor) != sizeof(riff)) { - return MA_FALSE; - } - if (ma_dr_wav_fourcc_equal(riff, "RIFF")) { - pWav->container = ma_dr_wav_container_riff; - } else if (ma_dr_wav_fourcc_equal(riff, "RIFX")) { - pWav->container = ma_dr_wav_container_rifx; - } else if (ma_dr_wav_fourcc_equal(riff, "riff")) { - int i; - ma_uint8 riff2[12]; - pWav->container = ma_dr_wav_container_w64; - if (ma_dr_wav__on_read(pWav->onRead, pWav->pUserData, riff2, sizeof(riff2), &cursor) != sizeof(riff2)) { - return MA_FALSE; - } - for (i = 0; i < 12; ++i) { - if (riff2[i] != ma_dr_wavGUID_W64_RIFF[i+4]) { - return MA_FALSE; - } - } - } else if (ma_dr_wav_fourcc_equal(riff, "RF64")) { - pWav->container = ma_dr_wav_container_rf64; - } else if (ma_dr_wav_fourcc_equal(riff, "FORM")) { - pWav->container = ma_dr_wav_container_aiff; - } else { - return MA_FALSE; - } - if (pWav->container == ma_dr_wav_container_riff || pWav->container == ma_dr_wav_container_rifx || pWav->container == ma_dr_wav_container_rf64) { - ma_uint8 chunkSizeBytes[4]; - ma_uint8 wave[4]; - if (ma_dr_wav__on_read(pWav->onRead, pWav->pUserData, chunkSizeBytes, sizeof(chunkSizeBytes), &cursor) != sizeof(chunkSizeBytes)) { - return MA_FALSE; - } - if (pWav->container == ma_dr_wav_container_riff || pWav->container == ma_dr_wav_container_rifx) { - if (ma_dr_wav_bytes_to_u32_ex(chunkSizeBytes, pWav->container) < 36) { - } - } else if (pWav->container == ma_dr_wav_container_rf64) { - if (ma_dr_wav_bytes_to_u32_le(chunkSizeBytes) != 0xFFFFFFFF) { - return MA_FALSE; - } - } else { - return MA_FALSE; - } - if (ma_dr_wav__on_read(pWav->onRead, pWav->pUserData, wave, sizeof(wave), &cursor) != sizeof(wave)) { - return MA_FALSE; - } - if (!ma_dr_wav_fourcc_equal(wave, "WAVE")) { - return MA_FALSE; - } - } else if (pWav->container == ma_dr_wav_container_w64) { - ma_uint8 chunkSizeBytes[8]; - ma_uint8 wave[16]; - if (ma_dr_wav__on_read(pWav->onRead, pWav->pUserData, chunkSizeBytes, sizeof(chunkSizeBytes), &cursor) != sizeof(chunkSizeBytes)) { - return MA_FALSE; - } - if (ma_dr_wav_bytes_to_u64(chunkSizeBytes) < 80) { - return MA_FALSE; - } - if (ma_dr_wav__on_read(pWav->onRead, pWav->pUserData, wave, sizeof(wave), &cursor) != sizeof(wave)) { - return MA_FALSE; - } - if (!ma_dr_wav_guid_equal(wave, ma_dr_wavGUID_W64_WAVE)) { - return MA_FALSE; - } - } else if (pWav->container == ma_dr_wav_container_aiff) { - ma_uint8 chunkSizeBytes[4]; - ma_uint8 aiff[4]; - if (ma_dr_wav__on_read(pWav->onRead, pWav->pUserData, chunkSizeBytes, sizeof(chunkSizeBytes), &cursor) != sizeof(chunkSizeBytes)) { - return MA_FALSE; - } - if (ma_dr_wav_bytes_to_u32_be(chunkSizeBytes) < 18) { - return MA_FALSE; - } - if (ma_dr_wav__on_read(pWav->onRead, pWav->pUserData, aiff, sizeof(aiff), &cursor) != sizeof(aiff)) { - return MA_FALSE; - } - if (ma_dr_wav_fourcc_equal(aiff, "AIFF")) { - isAIFCFormType = MA_FALSE; - } else if (ma_dr_wav_fourcc_equal(aiff, "AIFC")) { - isAIFCFormType = MA_TRUE; - } else { - return MA_FALSE; - } - } else { - return MA_FALSE; - } - if (pWav->container == ma_dr_wav_container_rf64) { - ma_uint8 sizeBytes[8]; - ma_uint64 bytesRemainingInChunk; - ma_dr_wav_chunk_header header; - result = ma_dr_wav__read_chunk_header(pWav->onRead, pWav->pUserData, pWav->container, &cursor, &header); - if (result != MA_SUCCESS) { - return MA_FALSE; - } - if (!ma_dr_wav_fourcc_equal(header.id.fourcc, "ds64")) { - return MA_FALSE; - } - bytesRemainingInChunk = header.sizeInBytes + header.paddingSize; - if (!ma_dr_wav__seek_forward(pWav->onSeek, 8, pWav->pUserData)) { - return MA_FALSE; - } - bytesRemainingInChunk -= 8; - cursor += 8; - if (ma_dr_wav__on_read(pWav->onRead, pWav->pUserData, sizeBytes, sizeof(sizeBytes), &cursor) != sizeof(sizeBytes)) { - return MA_FALSE; - } - bytesRemainingInChunk -= 8; - dataChunkSize = ma_dr_wav_bytes_to_u64(sizeBytes); - if (ma_dr_wav__on_read(pWav->onRead, pWav->pUserData, sizeBytes, sizeof(sizeBytes), &cursor) != sizeof(sizeBytes)) { - return MA_FALSE; - } - bytesRemainingInChunk -= 8; - sampleCountFromFactChunk = ma_dr_wav_bytes_to_u64(sizeBytes); - if (!ma_dr_wav__seek_forward(pWav->onSeek, bytesRemainingInChunk, pWav->pUserData)) { - return MA_FALSE; - } - cursor += bytesRemainingInChunk; - } - metadataStartPos = cursor; - isProcessingMetadata = !sequential && ((flags & MA_DR_WAV_WITH_METADATA) != 0); - if (pWav->container != ma_dr_wav_container_riff && pWav->container != ma_dr_wav_container_rf64) { - isProcessingMetadata = MA_FALSE; - } - MA_DR_WAV_ZERO_MEMORY(&metadataParser, sizeof(metadataParser)); - if (isProcessingMetadata) { - metadataParser.onRead = pWav->onRead; - metadataParser.onSeek = pWav->onSeek; - metadataParser.pReadSeekUserData = pWav->pUserData; - metadataParser.stage = ma_dr_wav__metadata_parser_stage_count; - } - for (;;) { - ma_dr_wav_chunk_header header; - ma_uint64 chunkSize; - result = ma_dr_wav__read_chunk_header(pWav->onRead, pWav->pUserData, pWav->container, &cursor, &header); - if (result != MA_SUCCESS) { - break; - } - chunkSize = header.sizeInBytes; - if (!sequential && onChunk != NULL) { - ma_uint64 callbackBytesRead = onChunk(pChunkUserData, pWav->onRead, pWav->onSeek, pWav->pUserData, &header, pWav->container, &fmt); - if (callbackBytesRead > 0) { - if (ma_dr_wav__seek_from_start(pWav->onSeek, cursor, pWav->pUserData) == MA_FALSE) { - return MA_FALSE; - } - } - } - if (((pWav->container == ma_dr_wav_container_riff || pWav->container == ma_dr_wav_container_rifx || pWav->container == ma_dr_wav_container_rf64) && ma_dr_wav_fourcc_equal(header.id.fourcc, "fmt ")) || - ((pWav->container == ma_dr_wav_container_w64) && ma_dr_wav_guid_equal(header.id.guid, ma_dr_wavGUID_W64_FMT))) { - ma_uint8 fmtData[16]; - foundChunk_fmt = MA_TRUE; - if (pWav->onRead(pWav->pUserData, fmtData, sizeof(fmtData)) != sizeof(fmtData)) { - return MA_FALSE; - } - cursor += sizeof(fmtData); - fmt.formatTag = ma_dr_wav_bytes_to_u16_ex(fmtData + 0, pWav->container); - fmt.channels = ma_dr_wav_bytes_to_u16_ex(fmtData + 2, pWav->container); - fmt.sampleRate = ma_dr_wav_bytes_to_u32_ex(fmtData + 4, pWav->container); - fmt.avgBytesPerSec = ma_dr_wav_bytes_to_u32_ex(fmtData + 8, pWav->container); - fmt.blockAlign = ma_dr_wav_bytes_to_u16_ex(fmtData + 12, pWav->container); - fmt.bitsPerSample = ma_dr_wav_bytes_to_u16_ex(fmtData + 14, pWav->container); - fmt.extendedSize = 0; - fmt.validBitsPerSample = 0; - fmt.channelMask = 0; - MA_DR_WAV_ZERO_MEMORY(fmt.subFormat, sizeof(fmt.subFormat)); - if (header.sizeInBytes > 16) { - ma_uint8 fmt_cbSize[2]; - int bytesReadSoFar = 0; - if (pWav->onRead(pWav->pUserData, fmt_cbSize, sizeof(fmt_cbSize)) != sizeof(fmt_cbSize)) { - return MA_FALSE; - } - cursor += sizeof(fmt_cbSize); - bytesReadSoFar = 18; - fmt.extendedSize = ma_dr_wav_bytes_to_u16_ex(fmt_cbSize, pWav->container); - if (fmt.extendedSize > 0) { - if (fmt.formatTag == MA_DR_WAVE_FORMAT_EXTENSIBLE) { - if (fmt.extendedSize != 22) { - return MA_FALSE; - } - } - if (fmt.formatTag == MA_DR_WAVE_FORMAT_EXTENSIBLE) { - ma_uint8 fmtext[22]; - if (pWav->onRead(pWav->pUserData, fmtext, fmt.extendedSize) != fmt.extendedSize) { - return MA_FALSE; - } - fmt.validBitsPerSample = ma_dr_wav_bytes_to_u16_ex(fmtext + 0, pWav->container); - fmt.channelMask = ma_dr_wav_bytes_to_u32_ex(fmtext + 2, pWav->container); - ma_dr_wav_bytes_to_guid(fmtext + 6, fmt.subFormat); - } else { - if (pWav->onSeek(pWav->pUserData, fmt.extendedSize, ma_dr_wav_seek_origin_current) == MA_FALSE) { - return MA_FALSE; - } - } - cursor += fmt.extendedSize; - bytesReadSoFar += fmt.extendedSize; - } - if (pWav->onSeek(pWav->pUserData, (int)(header.sizeInBytes - bytesReadSoFar), ma_dr_wav_seek_origin_current) == MA_FALSE) { - return MA_FALSE; - } - cursor += (header.sizeInBytes - bytesReadSoFar); - } - if (header.paddingSize > 0) { - if (ma_dr_wav__seek_forward(pWav->onSeek, header.paddingSize, pWav->pUserData) == MA_FALSE) { - break; - } - cursor += header.paddingSize; - } - continue; - } - if (((pWav->container == ma_dr_wav_container_riff || pWav->container == ma_dr_wav_container_rifx || pWav->container == ma_dr_wav_container_rf64) && ma_dr_wav_fourcc_equal(header.id.fourcc, "data")) || - ((pWav->container == ma_dr_wav_container_w64) && ma_dr_wav_guid_equal(header.id.guid, ma_dr_wavGUID_W64_DATA))) { - foundChunk_data = MA_TRUE; - pWav->dataChunkDataPos = cursor; - if (pWav->container != ma_dr_wav_container_rf64) { - dataChunkSize = chunkSize; - } - if (sequential || !isProcessingMetadata) { - break; - } else { - chunkSize += header.paddingSize; - if (ma_dr_wav__seek_forward(pWav->onSeek, chunkSize, pWav->pUserData) == MA_FALSE) { - break; - } - cursor += chunkSize; - continue; - } - } - if (((pWav->container == ma_dr_wav_container_riff || pWav->container == ma_dr_wav_container_rifx || pWav->container == ma_dr_wav_container_rf64) && ma_dr_wav_fourcc_equal(header.id.fourcc, "fact")) || - ((pWav->container == ma_dr_wav_container_w64) && ma_dr_wav_guid_equal(header.id.guid, ma_dr_wavGUID_W64_FACT))) { - if (pWav->container == ma_dr_wav_container_riff || pWav->container == ma_dr_wav_container_rifx) { - ma_uint8 sampleCount[4]; - if (ma_dr_wav__on_read(pWav->onRead, pWav->pUserData, &sampleCount, 4, &cursor) != 4) { - return MA_FALSE; - } - chunkSize -= 4; - if (pWav->translatedFormatTag == MA_DR_WAVE_FORMAT_ADPCM) { - sampleCountFromFactChunk = ma_dr_wav_bytes_to_u32_ex(sampleCount, pWav->container); - } else { - sampleCountFromFactChunk = 0; - } - } else if (pWav->container == ma_dr_wav_container_w64) { - if (ma_dr_wav__on_read(pWav->onRead, pWav->pUserData, &sampleCountFromFactChunk, 8, &cursor) != 8) { - return MA_FALSE; - } - chunkSize -= 8; - } else if (pWav->container == ma_dr_wav_container_rf64) { - } - chunkSize += header.paddingSize; - if (ma_dr_wav__seek_forward(pWav->onSeek, chunkSize, pWav->pUserData) == MA_FALSE) { - break; - } - cursor += chunkSize; - continue; - } - if (pWav->container == ma_dr_wav_container_aiff && ma_dr_wav_fourcc_equal(header.id.fourcc, "COMM")) { - ma_uint8 commData[24]; - ma_uint32 commDataBytesToRead; - ma_uint16 channels; - ma_uint32 frameCount; - ma_uint16 sampleSizeInBits; - ma_int64 sampleRate; - ma_uint16 compressionFormat; - foundChunk_fmt = MA_TRUE; - if (isAIFCFormType) { - commDataBytesToRead = 24; - if (header.sizeInBytes < commDataBytesToRead) { - return MA_FALSE; - } - } else { - commDataBytesToRead = 18; - if (header.sizeInBytes != commDataBytesToRead) { - return MA_FALSE; - } - } - if (ma_dr_wav__on_read(pWav->onRead, pWav->pUserData, commData, commDataBytesToRead, &cursor) != commDataBytesToRead) { - return MA_FALSE; - } - channels = ma_dr_wav_bytes_to_u16_ex (commData + 0, pWav->container); - frameCount = ma_dr_wav_bytes_to_u32_ex (commData + 2, pWav->container); - sampleSizeInBits = ma_dr_wav_bytes_to_u16_ex (commData + 6, pWav->container); - sampleRate = ma_dr_wav_aiff_extented_to_s64(commData + 8); - if (sampleRate < 0 || sampleRate > 0xFFFFFFFF) { - return MA_FALSE; - } - if (isAIFCFormType) { - const ma_uint8* type = commData + 18; - if (ma_dr_wav_fourcc_equal(type, "NONE")) { - compressionFormat = MA_DR_WAVE_FORMAT_PCM; - } else if (ma_dr_wav_fourcc_equal(type, "raw ")) { - compressionFormat = MA_DR_WAVE_FORMAT_PCM; - if (sampleSizeInBits == 8) { - pWav->aiff.isUnsigned = MA_TRUE; - } - } else if (ma_dr_wav_fourcc_equal(type, "sowt")) { - compressionFormat = MA_DR_WAVE_FORMAT_PCM; - pWav->aiff.isLE = MA_TRUE; - } else if (ma_dr_wav_fourcc_equal(type, "fl32") || ma_dr_wav_fourcc_equal(type, "fl64") || ma_dr_wav_fourcc_equal(type, "FL32") || ma_dr_wav_fourcc_equal(type, "FL64")) { - compressionFormat = MA_DR_WAVE_FORMAT_IEEE_FLOAT; - } else if (ma_dr_wav_fourcc_equal(type, "alaw") || ma_dr_wav_fourcc_equal(type, "ALAW")) { - compressionFormat = MA_DR_WAVE_FORMAT_ALAW; - } else if (ma_dr_wav_fourcc_equal(type, "ulaw") || ma_dr_wav_fourcc_equal(type, "ULAW")) { - compressionFormat = MA_DR_WAVE_FORMAT_MULAW; - } else if (ma_dr_wav_fourcc_equal(type, "ima4")) { - compressionFormat = MA_DR_WAVE_FORMAT_DVI_ADPCM; - sampleSizeInBits = 4; - (void)compressionFormat; - (void)sampleSizeInBits; - return MA_FALSE; - } else { - return MA_FALSE; - } - } else { - compressionFormat = MA_DR_WAVE_FORMAT_PCM; - } - aiffFrameCount = frameCount; - fmt.formatTag = compressionFormat; - fmt.channels = channels; - fmt.sampleRate = (ma_uint32)sampleRate; - fmt.bitsPerSample = sampleSizeInBits; - fmt.blockAlign = (ma_uint16)(fmt.channels * fmt.bitsPerSample / 8); - fmt.avgBytesPerSec = fmt.blockAlign * fmt.sampleRate; - if (fmt.blockAlign == 0 && compressionFormat == MA_DR_WAVE_FORMAT_DVI_ADPCM) { - fmt.blockAlign = 34 * fmt.channels; - } - if (compressionFormat == MA_DR_WAVE_FORMAT_ALAW || compressionFormat == MA_DR_WAVE_FORMAT_MULAW) { - if (fmt.bitsPerSample > 8) { - fmt.bitsPerSample = 8; - fmt.blockAlign = fmt.channels; - } - } - fmt.bitsPerSample += (fmt.bitsPerSample & 7); - if (isAIFCFormType) { - if (ma_dr_wav__seek_forward(pWav->onSeek, (chunkSize - commDataBytesToRead), pWav->pUserData) == MA_FALSE) { - return MA_FALSE; - } - cursor += (chunkSize - commDataBytesToRead); - } - continue; - } - if (pWav->container == ma_dr_wav_container_aiff && ma_dr_wav_fourcc_equal(header.id.fourcc, "SSND")) { - ma_uint8 offsetAndBlockSizeData[8]; - ma_uint32 offset; - foundChunk_data = MA_TRUE; - if (ma_dr_wav__on_read(pWav->onRead, pWav->pUserData, offsetAndBlockSizeData, sizeof(offsetAndBlockSizeData), &cursor) != sizeof(offsetAndBlockSizeData)) { - return MA_FALSE; - } - offset = ma_dr_wav_bytes_to_u32_ex(offsetAndBlockSizeData + 0, pWav->container); - if (ma_dr_wav__seek_forward(pWav->onSeek, offset, pWav->pUserData) == MA_FALSE) { - return MA_FALSE; - } - cursor += offset; - pWav->dataChunkDataPos = cursor; - dataChunkSize = chunkSize; - if (sequential || !isProcessingMetadata) { - break; - } else { - if (ma_dr_wav__seek_forward(pWav->onSeek, chunkSize, pWav->pUserData) == MA_FALSE) { - break; - } - cursor += chunkSize; - continue; - } - } - if (isProcessingMetadata) { - ma_dr_wav__metadata_process_chunk(&metadataParser, &header, ma_dr_wav_metadata_type_all_including_unknown); - if (ma_dr_wav__seek_from_start(pWav->onSeek, cursor, pWav->pUserData) == MA_FALSE) { - break; - } - } - chunkSize += header.paddingSize; - if (ma_dr_wav__seek_forward(pWav->onSeek, chunkSize, pWav->pUserData) == MA_FALSE) { - break; - } - cursor += chunkSize; - } - if (!foundChunk_fmt || !foundChunk_data) { - return MA_FALSE; - } - if ((fmt.sampleRate == 0 || fmt.sampleRate > MA_DR_WAV_MAX_SAMPLE_RATE ) || - (fmt.channels == 0 || fmt.channels > MA_DR_WAV_MAX_CHANNELS ) || - (fmt.bitsPerSample == 0 || fmt.bitsPerSample > MA_DR_WAV_MAX_BITS_PER_SAMPLE) || - fmt.blockAlign == 0) { - return MA_FALSE; - } - translatedFormatTag = fmt.formatTag; - if (translatedFormatTag == MA_DR_WAVE_FORMAT_EXTENSIBLE) { - translatedFormatTag = ma_dr_wav_bytes_to_u16_ex(fmt.subFormat + 0, pWav->container); - } - if (!sequential) { - if (!ma_dr_wav__seek_from_start(pWav->onSeek, pWav->dataChunkDataPos, pWav->pUserData)) { - return MA_FALSE; - } - cursor = pWav->dataChunkDataPos; - } - if (isProcessingMetadata && metadataParser.metadataCount > 0) { - if (ma_dr_wav__seek_from_start(pWav->onSeek, metadataStartPos, pWav->pUserData) == MA_FALSE) { - return MA_FALSE; - } - result = ma_dr_wav__metadata_alloc(&metadataParser, &pWav->allocationCallbacks); - if (result != MA_SUCCESS) { - return MA_FALSE; - } - metadataParser.stage = ma_dr_wav__metadata_parser_stage_read; - for (;;) { - ma_dr_wav_chunk_header header; - ma_uint64 metadataBytesRead; - result = ma_dr_wav__read_chunk_header(pWav->onRead, pWav->pUserData, pWav->container, &cursor, &header); - if (result != MA_SUCCESS) { - break; - } - metadataBytesRead = ma_dr_wav__metadata_process_chunk(&metadataParser, &header, ma_dr_wav_metadata_type_all_including_unknown); - if (ma_dr_wav__seek_forward(pWav->onSeek, (header.sizeInBytes + header.paddingSize) - metadataBytesRead, pWav->pUserData) == MA_FALSE) { - ma_dr_wav_free(metadataParser.pMetadata, &pWav->allocationCallbacks); - return MA_FALSE; - } - } - pWav->pMetadata = metadataParser.pMetadata; - pWav->metadataCount = metadataParser.metadataCount; - } - if (dataChunkSize == 0xFFFFFFFF && (pWav->container == ma_dr_wav_container_riff || pWav->container == ma_dr_wav_container_rifx) && pWav->isSequentialWrite == MA_FALSE) { - dataChunkSize = 0; - for (;;) { - ma_uint8 temp[4096]; - size_t bytesRead = pWav->onRead(pWav->pUserData, temp, sizeof(temp)); - dataChunkSize += bytesRead; - if (bytesRead < sizeof(temp)) { - break; - } - } - } - if (ma_dr_wav__seek_from_start(pWav->onSeek, pWav->dataChunkDataPos, pWav->pUserData) == MA_FALSE) { - ma_dr_wav_free(pWav->pMetadata, &pWav->allocationCallbacks); - return MA_FALSE; - } - pWav->fmt = fmt; - pWav->sampleRate = fmt.sampleRate; - pWav->channels = fmt.channels; - pWav->bitsPerSample = fmt.bitsPerSample; - pWav->bytesRemaining = dataChunkSize; - pWav->translatedFormatTag = translatedFormatTag; - pWav->dataChunkDataSize = dataChunkSize; - if (sampleCountFromFactChunk != 0) { - pWav->totalPCMFrameCount = sampleCountFromFactChunk; - } else if (aiffFrameCount != 0) { - pWav->totalPCMFrameCount = aiffFrameCount; - } else { - ma_uint32 bytesPerFrame = ma_dr_wav_get_bytes_per_pcm_frame(pWav); - if (bytesPerFrame == 0) { - ma_dr_wav_free(pWav->pMetadata, &pWav->allocationCallbacks); - return MA_FALSE; - } - pWav->totalPCMFrameCount = dataChunkSize / bytesPerFrame; - if (pWav->translatedFormatTag == MA_DR_WAVE_FORMAT_ADPCM) { - ma_uint64 totalBlockHeaderSizeInBytes; - ma_uint64 blockCount = dataChunkSize / fmt.blockAlign; - if ((blockCount * fmt.blockAlign) < dataChunkSize) { - blockCount += 1; - } - totalBlockHeaderSizeInBytes = blockCount * (6*fmt.channels); - pWav->totalPCMFrameCount = ((dataChunkSize - totalBlockHeaderSizeInBytes) * 2) / fmt.channels; - } - if (pWav->translatedFormatTag == MA_DR_WAVE_FORMAT_DVI_ADPCM) { - ma_uint64 totalBlockHeaderSizeInBytes; - ma_uint64 blockCount = dataChunkSize / fmt.blockAlign; - if ((blockCount * fmt.blockAlign) < dataChunkSize) { - blockCount += 1; - } - totalBlockHeaderSizeInBytes = blockCount * (4*fmt.channels); - pWav->totalPCMFrameCount = ((dataChunkSize - totalBlockHeaderSizeInBytes) * 2) / fmt.channels; - pWav->totalPCMFrameCount += blockCount; - } - } - if (pWav->translatedFormatTag == MA_DR_WAVE_FORMAT_ADPCM || pWav->translatedFormatTag == MA_DR_WAVE_FORMAT_DVI_ADPCM) { - if (pWav->channels > 2) { - ma_dr_wav_free(pWav->pMetadata, &pWav->allocationCallbacks); - return MA_FALSE; - } - } - if (ma_dr_wav_get_bytes_per_pcm_frame(pWav) == 0) { - ma_dr_wav_free(pWav->pMetadata, &pWav->allocationCallbacks); - return MA_FALSE; - } -#ifdef MA_DR_WAV_LIBSNDFILE_COMPAT - if (pWav->translatedFormatTag == MA_DR_WAVE_FORMAT_ADPCM) { - ma_uint64 blockCount = dataChunkSize / fmt.blockAlign; - pWav->totalPCMFrameCount = (((blockCount * (fmt.blockAlign - (6*pWav->channels))) * 2)) / fmt.channels; - } - if (pWav->translatedFormatTag == MA_DR_WAVE_FORMAT_DVI_ADPCM) { - ma_uint64 blockCount = dataChunkSize / fmt.blockAlign; - pWav->totalPCMFrameCount = (((blockCount * (fmt.blockAlign - (4*pWav->channels))) * 2) + (blockCount * pWav->channels)) / fmt.channels; - } -#endif - return MA_TRUE; -} -MA_API ma_bool32 ma_dr_wav_init(ma_dr_wav* pWav, ma_dr_wav_read_proc onRead, ma_dr_wav_seek_proc onSeek, void* pUserData, const ma_allocation_callbacks* pAllocationCallbacks) -{ - return ma_dr_wav_init_ex(pWav, onRead, onSeek, NULL, pUserData, NULL, 0, pAllocationCallbacks); -} -MA_API ma_bool32 ma_dr_wav_init_ex(ma_dr_wav* pWav, ma_dr_wav_read_proc onRead, ma_dr_wav_seek_proc onSeek, ma_dr_wav_chunk_proc onChunk, void* pReadSeekUserData, void* pChunkUserData, ma_uint32 flags, const ma_allocation_callbacks* pAllocationCallbacks) -{ - if (!ma_dr_wav_preinit(pWav, onRead, onSeek, pReadSeekUserData, pAllocationCallbacks)) { - return MA_FALSE; - } - return ma_dr_wav_init__internal(pWav, onChunk, pChunkUserData, flags); -} -MA_API ma_bool32 ma_dr_wav_init_with_metadata(ma_dr_wav* pWav, ma_dr_wav_read_proc onRead, ma_dr_wav_seek_proc onSeek, void* pUserData, ma_uint32 flags, const ma_allocation_callbacks* pAllocationCallbacks) -{ - if (!ma_dr_wav_preinit(pWav, onRead, onSeek, pUserData, pAllocationCallbacks)) { - return MA_FALSE; - } - return ma_dr_wav_init__internal(pWav, NULL, NULL, flags | MA_DR_WAV_WITH_METADATA); -} -MA_API ma_dr_wav_metadata* ma_dr_wav_take_ownership_of_metadata(ma_dr_wav* pWav) -{ - ma_dr_wav_metadata *result = pWav->pMetadata; - pWav->pMetadata = NULL; - pWav->metadataCount = 0; - return result; -} -MA_PRIVATE size_t ma_dr_wav__write(ma_dr_wav* pWav, const void* pData, size_t dataSize) -{ - MA_DR_WAV_ASSERT(pWav != NULL); - MA_DR_WAV_ASSERT(pWav->onWrite != NULL); - return pWav->onWrite(pWav->pUserData, pData, dataSize); -} -MA_PRIVATE size_t ma_dr_wav__write_byte(ma_dr_wav* pWav, ma_uint8 byte) -{ - MA_DR_WAV_ASSERT(pWav != NULL); - MA_DR_WAV_ASSERT(pWav->onWrite != NULL); - return pWav->onWrite(pWav->pUserData, &byte, 1); -} -MA_PRIVATE size_t ma_dr_wav__write_u16ne_to_le(ma_dr_wav* pWav, ma_uint16 value) -{ - MA_DR_WAV_ASSERT(pWav != NULL); - MA_DR_WAV_ASSERT(pWav->onWrite != NULL); - if (!ma_dr_wav__is_little_endian()) { - value = ma_dr_wav__bswap16(value); - } - return ma_dr_wav__write(pWav, &value, 2); -} -MA_PRIVATE size_t ma_dr_wav__write_u32ne_to_le(ma_dr_wav* pWav, ma_uint32 value) -{ - MA_DR_WAV_ASSERT(pWav != NULL); - MA_DR_WAV_ASSERT(pWav->onWrite != NULL); - if (!ma_dr_wav__is_little_endian()) { - value = ma_dr_wav__bswap32(value); - } - return ma_dr_wav__write(pWav, &value, 4); -} -MA_PRIVATE size_t ma_dr_wav__write_u64ne_to_le(ma_dr_wav* pWav, ma_uint64 value) -{ - MA_DR_WAV_ASSERT(pWav != NULL); - MA_DR_WAV_ASSERT(pWav->onWrite != NULL); - if (!ma_dr_wav__is_little_endian()) { - value = ma_dr_wav__bswap64(value); - } - return ma_dr_wav__write(pWav, &value, 8); -} -MA_PRIVATE size_t ma_dr_wav__write_f32ne_to_le(ma_dr_wav* pWav, float value) -{ - union { - ma_uint32 u32; - float f32; - } u; - MA_DR_WAV_ASSERT(pWav != NULL); - MA_DR_WAV_ASSERT(pWav->onWrite != NULL); - u.f32 = value; - if (!ma_dr_wav__is_little_endian()) { - u.u32 = ma_dr_wav__bswap32(u.u32); - } - return ma_dr_wav__write(pWav, &u.u32, 4); -} -MA_PRIVATE size_t ma_dr_wav__write_or_count(ma_dr_wav* pWav, const void* pData, size_t dataSize) -{ - if (pWav == NULL) { - return dataSize; - } - return ma_dr_wav__write(pWav, pData, dataSize); -} -MA_PRIVATE size_t ma_dr_wav__write_or_count_byte(ma_dr_wav* pWav, ma_uint8 byte) -{ - if (pWav == NULL) { - return 1; - } - return ma_dr_wav__write_byte(pWav, byte); -} -MA_PRIVATE size_t ma_dr_wav__write_or_count_u16ne_to_le(ma_dr_wav* pWav, ma_uint16 value) -{ - if (pWav == NULL) { - return 2; - } - return ma_dr_wav__write_u16ne_to_le(pWav, value); -} -MA_PRIVATE size_t ma_dr_wav__write_or_count_u32ne_to_le(ma_dr_wav* pWav, ma_uint32 value) -{ - if (pWav == NULL) { - return 4; - } - return ma_dr_wav__write_u32ne_to_le(pWav, value); -} -#if 0 -MA_PRIVATE size_t ma_dr_wav__write_or_count_u64ne_to_le(ma_dr_wav* pWav, ma_uint64 value) -{ - if (pWav == NULL) { - return 8; - } - return ma_dr_wav__write_u64ne_to_le(pWav, value); -} -#endif -MA_PRIVATE size_t ma_dr_wav__write_or_count_f32ne_to_le(ma_dr_wav* pWav, float value) -{ - if (pWav == NULL) { - return 4; - } - return ma_dr_wav__write_f32ne_to_le(pWav, value); -} -MA_PRIVATE size_t ma_dr_wav__write_or_count_string_to_fixed_size_buf(ma_dr_wav* pWav, char* str, size_t bufFixedSize) -{ - size_t len; - if (pWav == NULL) { - return bufFixedSize; - } - len = ma_dr_wav__strlen_clamped(str, bufFixedSize); - ma_dr_wav__write_or_count(pWav, str, len); - if (len < bufFixedSize) { - size_t i; - for (i = 0; i < bufFixedSize - len; ++i) { - ma_dr_wav__write_byte(pWav, 0); - } - } - return bufFixedSize; -} -MA_PRIVATE size_t ma_dr_wav__write_or_count_metadata(ma_dr_wav* pWav, ma_dr_wav_metadata* pMetadatas, ma_uint32 metadataCount) -{ - size_t bytesWritten = 0; - ma_bool32 hasListAdtl = MA_FALSE; - ma_bool32 hasListInfo = MA_FALSE; - ma_uint32 iMetadata; - if (pMetadatas == NULL || metadataCount == 0) { - return 0; - } - for (iMetadata = 0; iMetadata < metadataCount; ++iMetadata) { - ma_dr_wav_metadata* pMetadata = &pMetadatas[iMetadata]; - ma_uint32 chunkSize = 0; - if ((pMetadata->type & ma_dr_wav_metadata_type_list_all_info_strings) || (pMetadata->type == ma_dr_wav_metadata_type_unknown && pMetadata->data.unknown.chunkLocation == ma_dr_wav_metadata_location_inside_info_list)) { - hasListInfo = MA_TRUE; - } - if ((pMetadata->type & ma_dr_wav_metadata_type_list_all_adtl) || (pMetadata->type == ma_dr_wav_metadata_type_unknown && pMetadata->data.unknown.chunkLocation == ma_dr_wav_metadata_location_inside_adtl_list)) { - hasListAdtl = MA_TRUE; - } - switch (pMetadata->type) { - case ma_dr_wav_metadata_type_smpl: - { - ma_uint32 iLoop; - chunkSize = MA_DR_WAV_SMPL_BYTES + MA_DR_WAV_SMPL_LOOP_BYTES * pMetadata->data.smpl.sampleLoopCount + pMetadata->data.smpl.samplerSpecificDataSizeInBytes; - bytesWritten += ma_dr_wav__write_or_count(pWav, "smpl", 4); - bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, chunkSize); - bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, pMetadata->data.smpl.manufacturerId); - bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, pMetadata->data.smpl.productId); - bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, pMetadata->data.smpl.samplePeriodNanoseconds); - bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, pMetadata->data.smpl.midiUnityNote); - bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, pMetadata->data.smpl.midiPitchFraction); - bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, pMetadata->data.smpl.smpteFormat); - bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, pMetadata->data.smpl.smpteOffset); - bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, pMetadata->data.smpl.sampleLoopCount); - bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, pMetadata->data.smpl.samplerSpecificDataSizeInBytes); - for (iLoop = 0; iLoop < pMetadata->data.smpl.sampleLoopCount; ++iLoop) { - bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, pMetadata->data.smpl.pLoops[iLoop].cuePointId); - bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, pMetadata->data.smpl.pLoops[iLoop].type); - bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, pMetadata->data.smpl.pLoops[iLoop].firstSampleByteOffset); - bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, pMetadata->data.smpl.pLoops[iLoop].lastSampleByteOffset); - bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, pMetadata->data.smpl.pLoops[iLoop].sampleFraction); - bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, pMetadata->data.smpl.pLoops[iLoop].playCount); - } - if (pMetadata->data.smpl.samplerSpecificDataSizeInBytes > 0) { - bytesWritten += ma_dr_wav__write_or_count(pWav, pMetadata->data.smpl.pSamplerSpecificData, pMetadata->data.smpl.samplerSpecificDataSizeInBytes); - } - } break; - case ma_dr_wav_metadata_type_inst: - { - chunkSize = MA_DR_WAV_INST_BYTES; - bytesWritten += ma_dr_wav__write_or_count(pWav, "inst", 4); - bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, chunkSize); - bytesWritten += ma_dr_wav__write_or_count(pWav, &pMetadata->data.inst.midiUnityNote, 1); - bytesWritten += ma_dr_wav__write_or_count(pWav, &pMetadata->data.inst.fineTuneCents, 1); - bytesWritten += ma_dr_wav__write_or_count(pWav, &pMetadata->data.inst.gainDecibels, 1); - bytesWritten += ma_dr_wav__write_or_count(pWav, &pMetadata->data.inst.lowNote, 1); - bytesWritten += ma_dr_wav__write_or_count(pWav, &pMetadata->data.inst.highNote, 1); - bytesWritten += ma_dr_wav__write_or_count(pWav, &pMetadata->data.inst.lowVelocity, 1); - bytesWritten += ma_dr_wav__write_or_count(pWav, &pMetadata->data.inst.highVelocity, 1); - } break; - case ma_dr_wav_metadata_type_cue: - { - ma_uint32 iCuePoint; - chunkSize = MA_DR_WAV_CUE_BYTES + MA_DR_WAV_CUE_POINT_BYTES * pMetadata->data.cue.cuePointCount; - bytesWritten += ma_dr_wav__write_or_count(pWav, "cue ", 4); - bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, chunkSize); - bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, pMetadata->data.cue.cuePointCount); - for (iCuePoint = 0; iCuePoint < pMetadata->data.cue.cuePointCount; ++iCuePoint) { - bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, pMetadata->data.cue.pCuePoints[iCuePoint].id); - bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, pMetadata->data.cue.pCuePoints[iCuePoint].playOrderPosition); - bytesWritten += ma_dr_wav__write_or_count(pWav, pMetadata->data.cue.pCuePoints[iCuePoint].dataChunkId, 4); - bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, pMetadata->data.cue.pCuePoints[iCuePoint].chunkStart); - bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, pMetadata->data.cue.pCuePoints[iCuePoint].blockStart); - bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, pMetadata->data.cue.pCuePoints[iCuePoint].sampleByteOffset); - } - } break; - case ma_dr_wav_metadata_type_acid: - { - chunkSize = MA_DR_WAV_ACID_BYTES; - bytesWritten += ma_dr_wav__write_or_count(pWav, "acid", 4); - bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, chunkSize); - bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, pMetadata->data.acid.flags); - bytesWritten += ma_dr_wav__write_or_count_u16ne_to_le(pWav, pMetadata->data.acid.midiUnityNote); - bytesWritten += ma_dr_wav__write_or_count_u16ne_to_le(pWav, pMetadata->data.acid.reserved1); - bytesWritten += ma_dr_wav__write_or_count_f32ne_to_le(pWav, pMetadata->data.acid.reserved2); - bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, pMetadata->data.acid.numBeats); - bytesWritten += ma_dr_wav__write_or_count_u16ne_to_le(pWav, pMetadata->data.acid.meterDenominator); - bytesWritten += ma_dr_wav__write_or_count_u16ne_to_le(pWav, pMetadata->data.acid.meterNumerator); - bytesWritten += ma_dr_wav__write_or_count_f32ne_to_le(pWav, pMetadata->data.acid.tempo); - } break; - case ma_dr_wav_metadata_type_bext: - { - char reservedBuf[MA_DR_WAV_BEXT_RESERVED_BYTES]; - ma_uint32 timeReferenceLow; - ma_uint32 timeReferenceHigh; - chunkSize = MA_DR_WAV_BEXT_BYTES + pMetadata->data.bext.codingHistorySize; - bytesWritten += ma_dr_wav__write_or_count(pWav, "bext", 4); - bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, chunkSize); - bytesWritten += ma_dr_wav__write_or_count_string_to_fixed_size_buf(pWav, pMetadata->data.bext.pDescription, MA_DR_WAV_BEXT_DESCRIPTION_BYTES); - bytesWritten += ma_dr_wav__write_or_count_string_to_fixed_size_buf(pWav, pMetadata->data.bext.pOriginatorName, MA_DR_WAV_BEXT_ORIGINATOR_NAME_BYTES); - bytesWritten += ma_dr_wav__write_or_count_string_to_fixed_size_buf(pWav, pMetadata->data.bext.pOriginatorReference, MA_DR_WAV_BEXT_ORIGINATOR_REF_BYTES); - bytesWritten += ma_dr_wav__write_or_count(pWav, pMetadata->data.bext.pOriginationDate, sizeof(pMetadata->data.bext.pOriginationDate)); - bytesWritten += ma_dr_wav__write_or_count(pWav, pMetadata->data.bext.pOriginationTime, sizeof(pMetadata->data.bext.pOriginationTime)); - timeReferenceLow = (ma_uint32)(pMetadata->data.bext.timeReference & 0xFFFFFFFF); - timeReferenceHigh = (ma_uint32)(pMetadata->data.bext.timeReference >> 32); - bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, timeReferenceLow); - bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, timeReferenceHigh); - bytesWritten += ma_dr_wav__write_or_count_u16ne_to_le(pWav, pMetadata->data.bext.version); - bytesWritten += ma_dr_wav__write_or_count(pWav, pMetadata->data.bext.pUMID, MA_DR_WAV_BEXT_UMID_BYTES); - bytesWritten += ma_dr_wav__write_or_count_u16ne_to_le(pWav, pMetadata->data.bext.loudnessValue); - bytesWritten += ma_dr_wav__write_or_count_u16ne_to_le(pWav, pMetadata->data.bext.loudnessRange); - bytesWritten += ma_dr_wav__write_or_count_u16ne_to_le(pWav, pMetadata->data.bext.maxTruePeakLevel); - bytesWritten += ma_dr_wav__write_or_count_u16ne_to_le(pWav, pMetadata->data.bext.maxMomentaryLoudness); - bytesWritten += ma_dr_wav__write_or_count_u16ne_to_le(pWav, pMetadata->data.bext.maxShortTermLoudness); - MA_DR_WAV_ZERO_MEMORY(reservedBuf, sizeof(reservedBuf)); - bytesWritten += ma_dr_wav__write_or_count(pWav, reservedBuf, sizeof(reservedBuf)); - if (pMetadata->data.bext.codingHistorySize > 0) { - bytesWritten += ma_dr_wav__write_or_count(pWav, pMetadata->data.bext.pCodingHistory, pMetadata->data.bext.codingHistorySize); - } - } break; - case ma_dr_wav_metadata_type_unknown: - { - if (pMetadata->data.unknown.chunkLocation == ma_dr_wav_metadata_location_top_level) { - chunkSize = pMetadata->data.unknown.dataSizeInBytes; - bytesWritten += ma_dr_wav__write_or_count(pWav, pMetadata->data.unknown.id, 4); - bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, chunkSize); - bytesWritten += ma_dr_wav__write_or_count(pWav, pMetadata->data.unknown.pData, pMetadata->data.unknown.dataSizeInBytes); - } - } break; - default: break; - } - if ((chunkSize % 2) != 0) { - bytesWritten += ma_dr_wav__write_or_count_byte(pWav, 0); - } - } - if (hasListInfo) { - ma_uint32 chunkSize = 4; - for (iMetadata = 0; iMetadata < metadataCount; ++iMetadata) { - ma_dr_wav_metadata* pMetadata = &pMetadatas[iMetadata]; - if ((pMetadata->type & ma_dr_wav_metadata_type_list_all_info_strings)) { - chunkSize += 8; - chunkSize += pMetadata->data.infoText.stringLength + 1; - } else if (pMetadata->type == ma_dr_wav_metadata_type_unknown && pMetadata->data.unknown.chunkLocation == ma_dr_wav_metadata_location_inside_info_list) { - chunkSize += 8; - chunkSize += pMetadata->data.unknown.dataSizeInBytes; - } - if ((chunkSize % 2) != 0) { - chunkSize += 1; - } - } - bytesWritten += ma_dr_wav__write_or_count(pWav, "LIST", 4); - bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, chunkSize); - bytesWritten += ma_dr_wav__write_or_count(pWav, "INFO", 4); - for (iMetadata = 0; iMetadata < metadataCount; ++iMetadata) { - ma_dr_wav_metadata* pMetadata = &pMetadatas[iMetadata]; - ma_uint32 subchunkSize = 0; - if (pMetadata->type & ma_dr_wav_metadata_type_list_all_info_strings) { - const char* pID = NULL; - switch (pMetadata->type) { - case ma_dr_wav_metadata_type_list_info_software: pID = "ISFT"; break; - case ma_dr_wav_metadata_type_list_info_copyright: pID = "ICOP"; break; - case ma_dr_wav_metadata_type_list_info_title: pID = "INAM"; break; - case ma_dr_wav_metadata_type_list_info_artist: pID = "IART"; break; - case ma_dr_wav_metadata_type_list_info_comment: pID = "ICMT"; break; - case ma_dr_wav_metadata_type_list_info_date: pID = "ICRD"; break; - case ma_dr_wav_metadata_type_list_info_genre: pID = "IGNR"; break; - case ma_dr_wav_metadata_type_list_info_album: pID = "IPRD"; break; - case ma_dr_wav_metadata_type_list_info_tracknumber: pID = "ITRK"; break; - default: break; - } - MA_DR_WAV_ASSERT(pID != NULL); - if (pMetadata->data.infoText.stringLength) { - subchunkSize = pMetadata->data.infoText.stringLength + 1; - bytesWritten += ma_dr_wav__write_or_count(pWav, pID, 4); - bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, subchunkSize); - bytesWritten += ma_dr_wav__write_or_count(pWav, pMetadata->data.infoText.pString, pMetadata->data.infoText.stringLength); - bytesWritten += ma_dr_wav__write_or_count_byte(pWav, '\0'); - } - } else if (pMetadata->type == ma_dr_wav_metadata_type_unknown && pMetadata->data.unknown.chunkLocation == ma_dr_wav_metadata_location_inside_info_list) { - if (pMetadata->data.unknown.dataSizeInBytes) { - subchunkSize = pMetadata->data.unknown.dataSizeInBytes; - bytesWritten += ma_dr_wav__write_or_count(pWav, pMetadata->data.unknown.id, 4); - bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, pMetadata->data.unknown.dataSizeInBytes); - bytesWritten += ma_dr_wav__write_or_count(pWav, pMetadata->data.unknown.pData, subchunkSize); - } - } - if ((subchunkSize % 2) != 0) { - bytesWritten += ma_dr_wav__write_or_count_byte(pWav, 0); - } - } - } - if (hasListAdtl) { - ma_uint32 chunkSize = 4; - for (iMetadata = 0; iMetadata < metadataCount; ++iMetadata) { - ma_dr_wav_metadata* pMetadata = &pMetadatas[iMetadata]; - switch (pMetadata->type) - { - case ma_dr_wav_metadata_type_list_label: - case ma_dr_wav_metadata_type_list_note: - { - chunkSize += 8; - chunkSize += MA_DR_WAV_LIST_LABEL_OR_NOTE_BYTES; - if (pMetadata->data.labelOrNote.stringLength > 0) { - chunkSize += pMetadata->data.labelOrNote.stringLength + 1; - } - } break; - case ma_dr_wav_metadata_type_list_labelled_cue_region: - { - chunkSize += 8; - chunkSize += MA_DR_WAV_LIST_LABELLED_TEXT_BYTES; - if (pMetadata->data.labelledCueRegion.stringLength > 0) { - chunkSize += pMetadata->data.labelledCueRegion.stringLength + 1; - } - } break; - case ma_dr_wav_metadata_type_unknown: - { - if (pMetadata->data.unknown.chunkLocation == ma_dr_wav_metadata_location_inside_adtl_list) { - chunkSize += 8; - chunkSize += pMetadata->data.unknown.dataSizeInBytes; - } - } break; - default: break; - } - if ((chunkSize % 2) != 0) { - chunkSize += 1; - } - } - bytesWritten += ma_dr_wav__write_or_count(pWav, "LIST", 4); - bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, chunkSize); - bytesWritten += ma_dr_wav__write_or_count(pWav, "adtl", 4); - for (iMetadata = 0; iMetadata < metadataCount; ++iMetadata) { - ma_dr_wav_metadata* pMetadata = &pMetadatas[iMetadata]; - ma_uint32 subchunkSize = 0; - switch (pMetadata->type) - { - case ma_dr_wav_metadata_type_list_label: - case ma_dr_wav_metadata_type_list_note: - { - if (pMetadata->data.labelOrNote.stringLength > 0) { - const char *pID = NULL; - if (pMetadata->type == ma_dr_wav_metadata_type_list_label) { - pID = "labl"; - } - else if (pMetadata->type == ma_dr_wav_metadata_type_list_note) { - pID = "note"; - } - MA_DR_WAV_ASSERT(pID != NULL); - MA_DR_WAV_ASSERT(pMetadata->data.labelOrNote.pString != NULL); - subchunkSize = MA_DR_WAV_LIST_LABEL_OR_NOTE_BYTES; - bytesWritten += ma_dr_wav__write_or_count(pWav, pID, 4); - subchunkSize += pMetadata->data.labelOrNote.stringLength + 1; - bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, subchunkSize); - bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, pMetadata->data.labelOrNote.cuePointId); - bytesWritten += ma_dr_wav__write_or_count(pWav, pMetadata->data.labelOrNote.pString, pMetadata->data.labelOrNote.stringLength); - bytesWritten += ma_dr_wav__write_or_count_byte(pWav, '\0'); - } - } break; - case ma_dr_wav_metadata_type_list_labelled_cue_region: - { - subchunkSize = MA_DR_WAV_LIST_LABELLED_TEXT_BYTES; - bytesWritten += ma_dr_wav__write_or_count(pWav, "ltxt", 4); - if (pMetadata->data.labelledCueRegion.stringLength > 0) { - subchunkSize += pMetadata->data.labelledCueRegion.stringLength + 1; - } - bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, subchunkSize); - bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, pMetadata->data.labelledCueRegion.cuePointId); - bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, pMetadata->data.labelledCueRegion.sampleLength); - bytesWritten += ma_dr_wav__write_or_count(pWav, pMetadata->data.labelledCueRegion.purposeId, 4); - bytesWritten += ma_dr_wav__write_or_count_u16ne_to_le(pWav, pMetadata->data.labelledCueRegion.country); - bytesWritten += ma_dr_wav__write_or_count_u16ne_to_le(pWav, pMetadata->data.labelledCueRegion.language); - bytesWritten += ma_dr_wav__write_or_count_u16ne_to_le(pWav, pMetadata->data.labelledCueRegion.dialect); - bytesWritten += ma_dr_wav__write_or_count_u16ne_to_le(pWav, pMetadata->data.labelledCueRegion.codePage); - if (pMetadata->data.labelledCueRegion.stringLength > 0) { - MA_DR_WAV_ASSERT(pMetadata->data.labelledCueRegion.pString != NULL); - bytesWritten += ma_dr_wav__write_or_count(pWav, pMetadata->data.labelledCueRegion.pString, pMetadata->data.labelledCueRegion.stringLength); - bytesWritten += ma_dr_wav__write_or_count_byte(pWav, '\0'); - } - } break; - case ma_dr_wav_metadata_type_unknown: - { - if (pMetadata->data.unknown.chunkLocation == ma_dr_wav_metadata_location_inside_adtl_list) { - subchunkSize = pMetadata->data.unknown.dataSizeInBytes; - MA_DR_WAV_ASSERT(pMetadata->data.unknown.pData != NULL); - bytesWritten += ma_dr_wav__write_or_count(pWav, pMetadata->data.unknown.id, 4); - bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, subchunkSize); - bytesWritten += ma_dr_wav__write_or_count(pWav, pMetadata->data.unknown.pData, subchunkSize); - } - } break; - default: break; - } - if ((subchunkSize % 2) != 0) { - bytesWritten += ma_dr_wav__write_or_count_byte(pWav, 0); - } - } - } - MA_DR_WAV_ASSERT((bytesWritten % 2) == 0); - return bytesWritten; -} -MA_PRIVATE ma_uint32 ma_dr_wav__riff_chunk_size_riff(ma_uint64 dataChunkSize, ma_dr_wav_metadata* pMetadata, ma_uint32 metadataCount) -{ - ma_uint64 chunkSize = 4 + 24 + (ma_uint64)ma_dr_wav__write_or_count_metadata(NULL, pMetadata, metadataCount) + 8 + dataChunkSize + ma_dr_wav__chunk_padding_size_riff(dataChunkSize); - if (chunkSize > 0xFFFFFFFFUL) { - chunkSize = 0xFFFFFFFFUL; - } - return (ma_uint32)chunkSize; -} -MA_PRIVATE ma_uint32 ma_dr_wav__data_chunk_size_riff(ma_uint64 dataChunkSize) -{ - if (dataChunkSize <= 0xFFFFFFFFUL) { - return (ma_uint32)dataChunkSize; - } else { - return 0xFFFFFFFFUL; - } -} -MA_PRIVATE ma_uint64 ma_dr_wav__riff_chunk_size_w64(ma_uint64 dataChunkSize) -{ - ma_uint64 dataSubchunkPaddingSize = ma_dr_wav__chunk_padding_size_w64(dataChunkSize); - return 80 + 24 + dataChunkSize + dataSubchunkPaddingSize; -} -MA_PRIVATE ma_uint64 ma_dr_wav__data_chunk_size_w64(ma_uint64 dataChunkSize) -{ - return 24 + dataChunkSize; -} -MA_PRIVATE ma_uint64 ma_dr_wav__riff_chunk_size_rf64(ma_uint64 dataChunkSize, ma_dr_wav_metadata *metadata, ma_uint32 numMetadata) -{ - ma_uint64 chunkSize = 4 + 36 + 24 + (ma_uint64)ma_dr_wav__write_or_count_metadata(NULL, metadata, numMetadata) + 8 + dataChunkSize + ma_dr_wav__chunk_padding_size_riff(dataChunkSize); - if (chunkSize > 0xFFFFFFFFUL) { - chunkSize = 0xFFFFFFFFUL; - } - return chunkSize; -} -MA_PRIVATE ma_uint64 ma_dr_wav__data_chunk_size_rf64(ma_uint64 dataChunkSize) -{ - return dataChunkSize; -} -MA_PRIVATE ma_bool32 ma_dr_wav_preinit_write(ma_dr_wav* pWav, const ma_dr_wav_data_format* pFormat, ma_bool32 isSequential, ma_dr_wav_write_proc onWrite, ma_dr_wav_seek_proc onSeek, void* pUserData, const ma_allocation_callbacks* pAllocationCallbacks) -{ - if (pWav == NULL || onWrite == NULL) { - return MA_FALSE; - } - if (!isSequential && onSeek == NULL) { - return MA_FALSE; - } - if (pFormat->format == MA_DR_WAVE_FORMAT_EXTENSIBLE) { - return MA_FALSE; - } - if (pFormat->format == MA_DR_WAVE_FORMAT_ADPCM || pFormat->format == MA_DR_WAVE_FORMAT_DVI_ADPCM) { - return MA_FALSE; - } - MA_DR_WAV_ZERO_MEMORY(pWav, sizeof(*pWav)); - pWav->onWrite = onWrite; - pWav->onSeek = onSeek; - pWav->pUserData = pUserData; - pWav->allocationCallbacks = ma_dr_wav_copy_allocation_callbacks_or_defaults(pAllocationCallbacks); - if (pWav->allocationCallbacks.onFree == NULL || (pWav->allocationCallbacks.onMalloc == NULL && pWav->allocationCallbacks.onRealloc == NULL)) { - return MA_FALSE; - } - pWav->fmt.formatTag = (ma_uint16)pFormat->format; - pWav->fmt.channels = (ma_uint16)pFormat->channels; - pWav->fmt.sampleRate = pFormat->sampleRate; - pWav->fmt.avgBytesPerSec = (ma_uint32)((pFormat->bitsPerSample * pFormat->sampleRate * pFormat->channels) / 8); - pWav->fmt.blockAlign = (ma_uint16)((pFormat->channels * pFormat->bitsPerSample) / 8); - pWav->fmt.bitsPerSample = (ma_uint16)pFormat->bitsPerSample; - pWav->fmt.extendedSize = 0; - pWav->isSequentialWrite = isSequential; - return MA_TRUE; -} -MA_PRIVATE ma_bool32 ma_dr_wav_init_write__internal(ma_dr_wav* pWav, const ma_dr_wav_data_format* pFormat, ma_uint64 totalSampleCount) -{ - size_t runningPos = 0; - ma_uint64 initialDataChunkSize = 0; - ma_uint64 chunkSizeFMT; - if (pWav->isSequentialWrite) { - initialDataChunkSize = (totalSampleCount * pWav->fmt.bitsPerSample) / 8; - if (pFormat->container == ma_dr_wav_container_riff) { - if (initialDataChunkSize > (0xFFFFFFFFUL - 36)) { - return MA_FALSE; - } - } - } - pWav->dataChunkDataSizeTargetWrite = initialDataChunkSize; - if (pFormat->container == ma_dr_wav_container_riff) { - ma_uint32 chunkSizeRIFF = 28 + (ma_uint32)initialDataChunkSize; - runningPos += ma_dr_wav__write(pWav, "RIFF", 4); - runningPos += ma_dr_wav__write_u32ne_to_le(pWav, chunkSizeRIFF); - runningPos += ma_dr_wav__write(pWav, "WAVE", 4); - } else if (pFormat->container == ma_dr_wav_container_w64) { - ma_uint64 chunkSizeRIFF = 80 + 24 + initialDataChunkSize; - runningPos += ma_dr_wav__write(pWav, ma_dr_wavGUID_W64_RIFF, 16); - runningPos += ma_dr_wav__write_u64ne_to_le(pWav, chunkSizeRIFF); - runningPos += ma_dr_wav__write(pWav, ma_dr_wavGUID_W64_WAVE, 16); - } else if (pFormat->container == ma_dr_wav_container_rf64) { - runningPos += ma_dr_wav__write(pWav, "RF64", 4); - runningPos += ma_dr_wav__write_u32ne_to_le(pWav, 0xFFFFFFFF); - runningPos += ma_dr_wav__write(pWav, "WAVE", 4); - } else { - return MA_FALSE; - } - if (pFormat->container == ma_dr_wav_container_rf64) { - ma_uint32 initialds64ChunkSize = 28; - ma_uint64 initialRiffChunkSize = 8 + initialds64ChunkSize + initialDataChunkSize; - runningPos += ma_dr_wav__write(pWav, "ds64", 4); - runningPos += ma_dr_wav__write_u32ne_to_le(pWav, initialds64ChunkSize); - runningPos += ma_dr_wav__write_u64ne_to_le(pWav, initialRiffChunkSize); - runningPos += ma_dr_wav__write_u64ne_to_le(pWav, initialDataChunkSize); - runningPos += ma_dr_wav__write_u64ne_to_le(pWav, totalSampleCount); - runningPos += ma_dr_wav__write_u32ne_to_le(pWav, 0); - } - if (pFormat->container == ma_dr_wav_container_riff || pFormat->container == ma_dr_wav_container_rf64) { - chunkSizeFMT = 16; - runningPos += ma_dr_wav__write(pWav, "fmt ", 4); - runningPos += ma_dr_wav__write_u32ne_to_le(pWav, (ma_uint32)chunkSizeFMT); - } else if (pFormat->container == ma_dr_wav_container_w64) { - chunkSizeFMT = 40; - runningPos += ma_dr_wav__write(pWav, ma_dr_wavGUID_W64_FMT, 16); - runningPos += ma_dr_wav__write_u64ne_to_le(pWav, chunkSizeFMT); - } - runningPos += ma_dr_wav__write_u16ne_to_le(pWav, pWav->fmt.formatTag); - runningPos += ma_dr_wav__write_u16ne_to_le(pWav, pWav->fmt.channels); - runningPos += ma_dr_wav__write_u32ne_to_le(pWav, pWav->fmt.sampleRate); - runningPos += ma_dr_wav__write_u32ne_to_le(pWav, pWav->fmt.avgBytesPerSec); - runningPos += ma_dr_wav__write_u16ne_to_le(pWav, pWav->fmt.blockAlign); - runningPos += ma_dr_wav__write_u16ne_to_le(pWav, pWav->fmt.bitsPerSample); - if (!pWav->isSequentialWrite && pWav->pMetadata != NULL && pWav->metadataCount > 0 && (pFormat->container == ma_dr_wav_container_riff || pFormat->container == ma_dr_wav_container_rf64)) { - runningPos += ma_dr_wav__write_or_count_metadata(pWav, pWav->pMetadata, pWav->metadataCount); - } - pWav->dataChunkDataPos = runningPos; - if (pFormat->container == ma_dr_wav_container_riff) { - ma_uint32 chunkSizeDATA = (ma_uint32)initialDataChunkSize; - runningPos += ma_dr_wav__write(pWav, "data", 4); - runningPos += ma_dr_wav__write_u32ne_to_le(pWav, chunkSizeDATA); - } else if (pFormat->container == ma_dr_wav_container_w64) { - ma_uint64 chunkSizeDATA = 24 + initialDataChunkSize; - runningPos += ma_dr_wav__write(pWav, ma_dr_wavGUID_W64_DATA, 16); - runningPos += ma_dr_wav__write_u64ne_to_le(pWav, chunkSizeDATA); - } else if (pFormat->container == ma_dr_wav_container_rf64) { - runningPos += ma_dr_wav__write(pWav, "data", 4); - runningPos += ma_dr_wav__write_u32ne_to_le(pWav, 0xFFFFFFFF); - } - pWav->container = pFormat->container; - pWav->channels = (ma_uint16)pFormat->channels; - pWav->sampleRate = pFormat->sampleRate; - pWav->bitsPerSample = (ma_uint16)pFormat->bitsPerSample; - pWav->translatedFormatTag = (ma_uint16)pFormat->format; - pWav->dataChunkDataPos = runningPos; - return MA_TRUE; -} -MA_API ma_bool32 ma_dr_wav_init_write(ma_dr_wav* pWav, const ma_dr_wav_data_format* pFormat, ma_dr_wav_write_proc onWrite, ma_dr_wav_seek_proc onSeek, void* pUserData, const ma_allocation_callbacks* pAllocationCallbacks) -{ - if (!ma_dr_wav_preinit_write(pWav, pFormat, MA_FALSE, onWrite, onSeek, pUserData, pAllocationCallbacks)) { - return MA_FALSE; - } - return ma_dr_wav_init_write__internal(pWav, pFormat, 0); -} -MA_API ma_bool32 ma_dr_wav_init_write_sequential(ma_dr_wav* pWav, const ma_dr_wav_data_format* pFormat, ma_uint64 totalSampleCount, ma_dr_wav_write_proc onWrite, void* pUserData, const ma_allocation_callbacks* pAllocationCallbacks) -{ - if (!ma_dr_wav_preinit_write(pWav, pFormat, MA_TRUE, onWrite, NULL, pUserData, pAllocationCallbacks)) { - return MA_FALSE; - } - return ma_dr_wav_init_write__internal(pWav, pFormat, totalSampleCount); -} -MA_API ma_bool32 ma_dr_wav_init_write_sequential_pcm_frames(ma_dr_wav* pWav, const ma_dr_wav_data_format* pFormat, ma_uint64 totalPCMFrameCount, ma_dr_wav_write_proc onWrite, void* pUserData, const ma_allocation_callbacks* pAllocationCallbacks) -{ - if (pFormat == NULL) { - return MA_FALSE; - } - return ma_dr_wav_init_write_sequential(pWav, pFormat, totalPCMFrameCount*pFormat->channels, onWrite, pUserData, pAllocationCallbacks); -} -MA_API ma_bool32 ma_dr_wav_init_write_with_metadata(ma_dr_wav* pWav, const ma_dr_wav_data_format* pFormat, ma_dr_wav_write_proc onWrite, ma_dr_wav_seek_proc onSeek, void* pUserData, const ma_allocation_callbacks* pAllocationCallbacks, ma_dr_wav_metadata* pMetadata, ma_uint32 metadataCount) -{ - if (!ma_dr_wav_preinit_write(pWav, pFormat, MA_FALSE, onWrite, onSeek, pUserData, pAllocationCallbacks)) { - return MA_FALSE; - } - pWav->pMetadata = pMetadata; - pWav->metadataCount = metadataCount; - return ma_dr_wav_init_write__internal(pWav, pFormat, 0); -} -MA_API ma_uint64 ma_dr_wav_target_write_size_bytes(const ma_dr_wav_data_format* pFormat, ma_uint64 totalFrameCount, ma_dr_wav_metadata* pMetadata, ma_uint32 metadataCount) -{ - ma_uint64 targetDataSizeBytes = (ma_uint64)((ma_int64)totalFrameCount * pFormat->channels * pFormat->bitsPerSample/8.0); - ma_uint64 riffChunkSizeBytes; - ma_uint64 fileSizeBytes = 0; - if (pFormat->container == ma_dr_wav_container_riff) { - riffChunkSizeBytes = ma_dr_wav__riff_chunk_size_riff(targetDataSizeBytes, pMetadata, metadataCount); - fileSizeBytes = (8 + riffChunkSizeBytes); - } else if (pFormat->container == ma_dr_wav_container_w64) { - riffChunkSizeBytes = ma_dr_wav__riff_chunk_size_w64(targetDataSizeBytes); - fileSizeBytes = riffChunkSizeBytes; - } else if (pFormat->container == ma_dr_wav_container_rf64) { - riffChunkSizeBytes = ma_dr_wav__riff_chunk_size_rf64(targetDataSizeBytes, pMetadata, metadataCount); - fileSizeBytes = (8 + riffChunkSizeBytes); - } - return fileSizeBytes; -} -#ifndef MA_DR_WAV_NO_STDIO -MA_PRIVATE size_t ma_dr_wav__on_read_stdio(void* pUserData, void* pBufferOut, size_t bytesToRead) -{ - return fread(pBufferOut, 1, bytesToRead, (FILE*)pUserData); -} -MA_PRIVATE size_t ma_dr_wav__on_write_stdio(void* pUserData, const void* pData, size_t bytesToWrite) -{ - return fwrite(pData, 1, bytesToWrite, (FILE*)pUserData); -} -MA_PRIVATE ma_bool32 ma_dr_wav__on_seek_stdio(void* pUserData, int offset, ma_dr_wav_seek_origin origin) -{ - return fseek((FILE*)pUserData, offset, (origin == ma_dr_wav_seek_origin_current) ? SEEK_CUR : SEEK_SET) == 0; -} -MA_API ma_bool32 ma_dr_wav_init_file(ma_dr_wav* pWav, const char* filename, const ma_allocation_callbacks* pAllocationCallbacks) -{ - return ma_dr_wav_init_file_ex(pWav, filename, NULL, NULL, 0, pAllocationCallbacks); -} -MA_PRIVATE ma_bool32 ma_dr_wav_init_file__internal_FILE(ma_dr_wav* pWav, FILE* pFile, ma_dr_wav_chunk_proc onChunk, void* pChunkUserData, ma_uint32 flags, const ma_allocation_callbacks* pAllocationCallbacks) -{ - ma_bool32 result; - result = ma_dr_wav_preinit(pWav, ma_dr_wav__on_read_stdio, ma_dr_wav__on_seek_stdio, (void*)pFile, pAllocationCallbacks); - if (result != MA_TRUE) { - fclose(pFile); - return result; - } - result = ma_dr_wav_init__internal(pWav, onChunk, pChunkUserData, flags); - if (result != MA_TRUE) { - fclose(pFile); - return result; - } - return MA_TRUE; -} -MA_API ma_bool32 ma_dr_wav_init_file_ex(ma_dr_wav* pWav, const char* filename, ma_dr_wav_chunk_proc onChunk, void* pChunkUserData, ma_uint32 flags, const ma_allocation_callbacks* pAllocationCallbacks) -{ - FILE* pFile; - if (ma_fopen(&pFile, filename, "rb") != MA_SUCCESS) { - return MA_FALSE; - } - return ma_dr_wav_init_file__internal_FILE(pWav, pFile, onChunk, pChunkUserData, flags, pAllocationCallbacks); -} -#ifndef MA_DR_WAV_NO_WCHAR -MA_API ma_bool32 ma_dr_wav_init_file_w(ma_dr_wav* pWav, const wchar_t* filename, const ma_allocation_callbacks* pAllocationCallbacks) -{ - return ma_dr_wav_init_file_ex_w(pWav, filename, NULL, NULL, 0, pAllocationCallbacks); -} -MA_API ma_bool32 ma_dr_wav_init_file_ex_w(ma_dr_wav* pWav, const wchar_t* filename, ma_dr_wav_chunk_proc onChunk, void* pChunkUserData, ma_uint32 flags, const ma_allocation_callbacks* pAllocationCallbacks) -{ - FILE* pFile; - if (ma_wfopen(&pFile, filename, L"rb", pAllocationCallbacks) != MA_SUCCESS) { - return MA_FALSE; - } - return ma_dr_wav_init_file__internal_FILE(pWav, pFile, onChunk, pChunkUserData, flags, pAllocationCallbacks); -} -#endif -MA_API ma_bool32 ma_dr_wav_init_file_with_metadata(ma_dr_wav* pWav, const char* filename, ma_uint32 flags, const ma_allocation_callbacks* pAllocationCallbacks) -{ - FILE* pFile; - if (ma_fopen(&pFile, filename, "rb") != MA_SUCCESS) { - return MA_FALSE; - } - return ma_dr_wav_init_file__internal_FILE(pWav, pFile, NULL, NULL, flags | MA_DR_WAV_WITH_METADATA, pAllocationCallbacks); -} -#ifndef MA_DR_WAV_NO_WCHAR -MA_API ma_bool32 ma_dr_wav_init_file_with_metadata_w(ma_dr_wav* pWav, const wchar_t* filename, ma_uint32 flags, const ma_allocation_callbacks* pAllocationCallbacks) -{ - FILE* pFile; - if (ma_wfopen(&pFile, filename, L"rb", pAllocationCallbacks) != MA_SUCCESS) { - return MA_FALSE; - } - return ma_dr_wav_init_file__internal_FILE(pWav, pFile, NULL, NULL, flags | MA_DR_WAV_WITH_METADATA, pAllocationCallbacks); -} -#endif -MA_PRIVATE ma_bool32 ma_dr_wav_init_file_write__internal_FILE(ma_dr_wav* pWav, FILE* pFile, const ma_dr_wav_data_format* pFormat, ma_uint64 totalSampleCount, ma_bool32 isSequential, const ma_allocation_callbacks* pAllocationCallbacks) -{ - ma_bool32 result; - result = ma_dr_wav_preinit_write(pWav, pFormat, isSequential, ma_dr_wav__on_write_stdio, ma_dr_wav__on_seek_stdio, (void*)pFile, pAllocationCallbacks); - if (result != MA_TRUE) { - fclose(pFile); - return result; - } - result = ma_dr_wav_init_write__internal(pWav, pFormat, totalSampleCount); - if (result != MA_TRUE) { - fclose(pFile); - return result; - } - return MA_TRUE; -} -MA_PRIVATE ma_bool32 ma_dr_wav_init_file_write__internal(ma_dr_wav* pWav, const char* filename, const ma_dr_wav_data_format* pFormat, ma_uint64 totalSampleCount, ma_bool32 isSequential, const ma_allocation_callbacks* pAllocationCallbacks) -{ - FILE* pFile; - if (ma_fopen(&pFile, filename, "wb") != MA_SUCCESS) { - return MA_FALSE; - } - return ma_dr_wav_init_file_write__internal_FILE(pWav, pFile, pFormat, totalSampleCount, isSequential, pAllocationCallbacks); -} -#ifndef MA_DR_WAV_NO_WCHAR -MA_PRIVATE ma_bool32 ma_dr_wav_init_file_write_w__internal(ma_dr_wav* pWav, const wchar_t* filename, const ma_dr_wav_data_format* pFormat, ma_uint64 totalSampleCount, ma_bool32 isSequential, const ma_allocation_callbacks* pAllocationCallbacks) -{ - FILE* pFile; - if (ma_wfopen(&pFile, filename, L"wb", pAllocationCallbacks) != MA_SUCCESS) { - return MA_FALSE; - } - return ma_dr_wav_init_file_write__internal_FILE(pWav, pFile, pFormat, totalSampleCount, isSequential, pAllocationCallbacks); -} -#endif -MA_API ma_bool32 ma_dr_wav_init_file_write(ma_dr_wav* pWav, const char* filename, const ma_dr_wav_data_format* pFormat, const ma_allocation_callbacks* pAllocationCallbacks) -{ - return ma_dr_wav_init_file_write__internal(pWav, filename, pFormat, 0, MA_FALSE, pAllocationCallbacks); -} -MA_API ma_bool32 ma_dr_wav_init_file_write_sequential(ma_dr_wav* pWav, const char* filename, const ma_dr_wav_data_format* pFormat, ma_uint64 totalSampleCount, const ma_allocation_callbacks* pAllocationCallbacks) -{ - return ma_dr_wav_init_file_write__internal(pWav, filename, pFormat, totalSampleCount, MA_TRUE, pAllocationCallbacks); -} -MA_API ma_bool32 ma_dr_wav_init_file_write_sequential_pcm_frames(ma_dr_wav* pWav, const char* filename, const ma_dr_wav_data_format* pFormat, ma_uint64 totalPCMFrameCount, const ma_allocation_callbacks* pAllocationCallbacks) -{ - if (pFormat == NULL) { - return MA_FALSE; - } - return ma_dr_wav_init_file_write_sequential(pWav, filename, pFormat, totalPCMFrameCount*pFormat->channels, pAllocationCallbacks); -} -#ifndef MA_DR_WAV_NO_WCHAR -MA_API ma_bool32 ma_dr_wav_init_file_write_w(ma_dr_wav* pWav, const wchar_t* filename, const ma_dr_wav_data_format* pFormat, const ma_allocation_callbacks* pAllocationCallbacks) -{ - return ma_dr_wav_init_file_write_w__internal(pWav, filename, pFormat, 0, MA_FALSE, pAllocationCallbacks); -} -MA_API ma_bool32 ma_dr_wav_init_file_write_sequential_w(ma_dr_wav* pWav, const wchar_t* filename, const ma_dr_wav_data_format* pFormat, ma_uint64 totalSampleCount, const ma_allocation_callbacks* pAllocationCallbacks) -{ - return ma_dr_wav_init_file_write_w__internal(pWav, filename, pFormat, totalSampleCount, MA_TRUE, pAllocationCallbacks); -} -MA_API ma_bool32 ma_dr_wav_init_file_write_sequential_pcm_frames_w(ma_dr_wav* pWav, const wchar_t* filename, const ma_dr_wav_data_format* pFormat, ma_uint64 totalPCMFrameCount, const ma_allocation_callbacks* pAllocationCallbacks) -{ - if (pFormat == NULL) { - return MA_FALSE; - } - return ma_dr_wav_init_file_write_sequential_w(pWav, filename, pFormat, totalPCMFrameCount*pFormat->channels, pAllocationCallbacks); -} -#endif -#endif -MA_PRIVATE size_t ma_dr_wav__on_read_memory(void* pUserData, void* pBufferOut, size_t bytesToRead) -{ - ma_dr_wav* pWav = (ma_dr_wav*)pUserData; - size_t bytesRemaining; - MA_DR_WAV_ASSERT(pWav != NULL); - MA_DR_WAV_ASSERT(pWav->memoryStream.dataSize >= pWav->memoryStream.currentReadPos); - bytesRemaining = pWav->memoryStream.dataSize - pWav->memoryStream.currentReadPos; - if (bytesToRead > bytesRemaining) { - bytesToRead = bytesRemaining; - } - if (bytesToRead > 0) { - MA_DR_WAV_COPY_MEMORY(pBufferOut, pWav->memoryStream.data + pWav->memoryStream.currentReadPos, bytesToRead); - pWav->memoryStream.currentReadPos += bytesToRead; - } - return bytesToRead; -} -MA_PRIVATE ma_bool32 ma_dr_wav__on_seek_memory(void* pUserData, int offset, ma_dr_wav_seek_origin origin) -{ - ma_dr_wav* pWav = (ma_dr_wav*)pUserData; - MA_DR_WAV_ASSERT(pWav != NULL); - if (origin == ma_dr_wav_seek_origin_current) { - if (offset > 0) { - if (pWav->memoryStream.currentReadPos + offset > pWav->memoryStream.dataSize) { - return MA_FALSE; - } - } else { - if (pWav->memoryStream.currentReadPos < (size_t)-offset) { - return MA_FALSE; - } - } - pWav->memoryStream.currentReadPos += offset; - } else { - if ((ma_uint32)offset <= pWav->memoryStream.dataSize) { - pWav->memoryStream.currentReadPos = offset; - } else { - return MA_FALSE; - } - } - return MA_TRUE; -} -MA_PRIVATE size_t ma_dr_wav__on_write_memory(void* pUserData, const void* pDataIn, size_t bytesToWrite) -{ - ma_dr_wav* pWav = (ma_dr_wav*)pUserData; - size_t bytesRemaining; - MA_DR_WAV_ASSERT(pWav != NULL); - MA_DR_WAV_ASSERT(pWav->memoryStreamWrite.dataCapacity >= pWav->memoryStreamWrite.currentWritePos); - bytesRemaining = pWav->memoryStreamWrite.dataCapacity - pWav->memoryStreamWrite.currentWritePos; - if (bytesRemaining < bytesToWrite) { - void* pNewData; - size_t newDataCapacity = (pWav->memoryStreamWrite.dataCapacity == 0) ? 256 : pWav->memoryStreamWrite.dataCapacity * 2; - if ((newDataCapacity - pWav->memoryStreamWrite.currentWritePos) < bytesToWrite) { - newDataCapacity = pWav->memoryStreamWrite.currentWritePos + bytesToWrite; - } - pNewData = ma_dr_wav__realloc_from_callbacks(*pWav->memoryStreamWrite.ppData, newDataCapacity, pWav->memoryStreamWrite.dataCapacity, &pWav->allocationCallbacks); - if (pNewData == NULL) { - return 0; - } - *pWav->memoryStreamWrite.ppData = pNewData; - pWav->memoryStreamWrite.dataCapacity = newDataCapacity; - } - MA_DR_WAV_COPY_MEMORY(((ma_uint8*)(*pWav->memoryStreamWrite.ppData)) + pWav->memoryStreamWrite.currentWritePos, pDataIn, bytesToWrite); - pWav->memoryStreamWrite.currentWritePos += bytesToWrite; - if (pWav->memoryStreamWrite.dataSize < pWav->memoryStreamWrite.currentWritePos) { - pWav->memoryStreamWrite.dataSize = pWav->memoryStreamWrite.currentWritePos; - } - *pWav->memoryStreamWrite.pDataSize = pWav->memoryStreamWrite.dataSize; - return bytesToWrite; -} -MA_PRIVATE ma_bool32 ma_dr_wav__on_seek_memory_write(void* pUserData, int offset, ma_dr_wav_seek_origin origin) -{ - ma_dr_wav* pWav = (ma_dr_wav*)pUserData; - MA_DR_WAV_ASSERT(pWav != NULL); - if (origin == ma_dr_wav_seek_origin_current) { - if (offset > 0) { - if (pWav->memoryStreamWrite.currentWritePos + offset > pWav->memoryStreamWrite.dataSize) { - offset = (int)(pWav->memoryStreamWrite.dataSize - pWav->memoryStreamWrite.currentWritePos); - } - } else { - if (pWav->memoryStreamWrite.currentWritePos < (size_t)-offset) { - offset = -(int)pWav->memoryStreamWrite.currentWritePos; - } - } - pWav->memoryStreamWrite.currentWritePos += offset; - } else { - if ((ma_uint32)offset <= pWav->memoryStreamWrite.dataSize) { - pWav->memoryStreamWrite.currentWritePos = offset; - } else { - pWav->memoryStreamWrite.currentWritePos = pWav->memoryStreamWrite.dataSize; - } - } - return MA_TRUE; -} -MA_API ma_bool32 ma_dr_wav_init_memory(ma_dr_wav* pWav, const void* data, size_t dataSize, const ma_allocation_callbacks* pAllocationCallbacks) -{ - return ma_dr_wav_init_memory_ex(pWav, data, dataSize, NULL, NULL, 0, pAllocationCallbacks); -} -MA_API ma_bool32 ma_dr_wav_init_memory_ex(ma_dr_wav* pWav, const void* data, size_t dataSize, ma_dr_wav_chunk_proc onChunk, void* pChunkUserData, ma_uint32 flags, const ma_allocation_callbacks* pAllocationCallbacks) -{ - if (data == NULL || dataSize == 0) { - return MA_FALSE; - } - if (!ma_dr_wav_preinit(pWav, ma_dr_wav__on_read_memory, ma_dr_wav__on_seek_memory, pWav, pAllocationCallbacks)) { - return MA_FALSE; - } - pWav->memoryStream.data = (const ma_uint8*)data; - pWav->memoryStream.dataSize = dataSize; - pWav->memoryStream.currentReadPos = 0; - return ma_dr_wav_init__internal(pWav, onChunk, pChunkUserData, flags); -} -MA_API ma_bool32 ma_dr_wav_init_memory_with_metadata(ma_dr_wav* pWav, const void* data, size_t dataSize, ma_uint32 flags, const ma_allocation_callbacks* pAllocationCallbacks) -{ - if (data == NULL || dataSize == 0) { - return MA_FALSE; - } - if (!ma_dr_wav_preinit(pWav, ma_dr_wav__on_read_memory, ma_dr_wav__on_seek_memory, pWav, pAllocationCallbacks)) { - return MA_FALSE; - } - pWav->memoryStream.data = (const ma_uint8*)data; - pWav->memoryStream.dataSize = dataSize; - pWav->memoryStream.currentReadPos = 0; - return ma_dr_wav_init__internal(pWav, NULL, NULL, flags | MA_DR_WAV_WITH_METADATA); -} -MA_PRIVATE ma_bool32 ma_dr_wav_init_memory_write__internal(ma_dr_wav* pWav, void** ppData, size_t* pDataSize, const ma_dr_wav_data_format* pFormat, ma_uint64 totalSampleCount, ma_bool32 isSequential, const ma_allocation_callbacks* pAllocationCallbacks) -{ - if (ppData == NULL || pDataSize == NULL) { - return MA_FALSE; - } - *ppData = NULL; - *pDataSize = 0; - if (!ma_dr_wav_preinit_write(pWav, pFormat, isSequential, ma_dr_wav__on_write_memory, ma_dr_wav__on_seek_memory_write, pWav, pAllocationCallbacks)) { - return MA_FALSE; - } - pWav->memoryStreamWrite.ppData = ppData; - pWav->memoryStreamWrite.pDataSize = pDataSize; - pWav->memoryStreamWrite.dataSize = 0; - pWav->memoryStreamWrite.dataCapacity = 0; - pWav->memoryStreamWrite.currentWritePos = 0; - return ma_dr_wav_init_write__internal(pWav, pFormat, totalSampleCount); -} -MA_API ma_bool32 ma_dr_wav_init_memory_write(ma_dr_wav* pWav, void** ppData, size_t* pDataSize, const ma_dr_wav_data_format* pFormat, const ma_allocation_callbacks* pAllocationCallbacks) -{ - return ma_dr_wav_init_memory_write__internal(pWav, ppData, pDataSize, pFormat, 0, MA_FALSE, pAllocationCallbacks); -} -MA_API ma_bool32 ma_dr_wav_init_memory_write_sequential(ma_dr_wav* pWav, void** ppData, size_t* pDataSize, const ma_dr_wav_data_format* pFormat, ma_uint64 totalSampleCount, const ma_allocation_callbacks* pAllocationCallbacks) -{ - return ma_dr_wav_init_memory_write__internal(pWav, ppData, pDataSize, pFormat, totalSampleCount, MA_TRUE, pAllocationCallbacks); -} -MA_API ma_bool32 ma_dr_wav_init_memory_write_sequential_pcm_frames(ma_dr_wav* pWav, void** ppData, size_t* pDataSize, const ma_dr_wav_data_format* pFormat, ma_uint64 totalPCMFrameCount, const ma_allocation_callbacks* pAllocationCallbacks) -{ - if (pFormat == NULL) { - return MA_FALSE; - } - return ma_dr_wav_init_memory_write_sequential(pWav, ppData, pDataSize, pFormat, totalPCMFrameCount*pFormat->channels, pAllocationCallbacks); -} -MA_API ma_result ma_dr_wav_uninit(ma_dr_wav* pWav) -{ - ma_result result = MA_SUCCESS; - if (pWav == NULL) { - return MA_INVALID_ARGS; - } - if (pWav->onWrite != NULL) { - ma_uint32 paddingSize = 0; - if (pWav->container == ma_dr_wav_container_riff || pWav->container == ma_dr_wav_container_rf64) { - paddingSize = ma_dr_wav__chunk_padding_size_riff(pWav->dataChunkDataSize); - } else { - paddingSize = ma_dr_wav__chunk_padding_size_w64(pWav->dataChunkDataSize); - } - if (paddingSize > 0) { - ma_uint64 paddingData = 0; - ma_dr_wav__write(pWav, &paddingData, paddingSize); - } - if (pWav->onSeek && !pWav->isSequentialWrite) { - if (pWav->container == ma_dr_wav_container_riff) { - if (pWav->onSeek(pWav->pUserData, 4, ma_dr_wav_seek_origin_start)) { - ma_uint32 riffChunkSize = ma_dr_wav__riff_chunk_size_riff(pWav->dataChunkDataSize, pWav->pMetadata, pWav->metadataCount); - ma_dr_wav__write_u32ne_to_le(pWav, riffChunkSize); - } - if (pWav->onSeek(pWav->pUserData, (int)pWav->dataChunkDataPos - 4, ma_dr_wav_seek_origin_start)) { - ma_uint32 dataChunkSize = ma_dr_wav__data_chunk_size_riff(pWav->dataChunkDataSize); - ma_dr_wav__write_u32ne_to_le(pWav, dataChunkSize); - } - } else if (pWav->container == ma_dr_wav_container_w64) { - if (pWav->onSeek(pWav->pUserData, 16, ma_dr_wav_seek_origin_start)) { - ma_uint64 riffChunkSize = ma_dr_wav__riff_chunk_size_w64(pWav->dataChunkDataSize); - ma_dr_wav__write_u64ne_to_le(pWav, riffChunkSize); - } - if (pWav->onSeek(pWav->pUserData, (int)pWav->dataChunkDataPos - 8, ma_dr_wav_seek_origin_start)) { - ma_uint64 dataChunkSize = ma_dr_wav__data_chunk_size_w64(pWav->dataChunkDataSize); - ma_dr_wav__write_u64ne_to_le(pWav, dataChunkSize); - } - } else if (pWav->container == ma_dr_wav_container_rf64) { - int ds64BodyPos = 12 + 8; - if (pWav->onSeek(pWav->pUserData, ds64BodyPos + 0, ma_dr_wav_seek_origin_start)) { - ma_uint64 riffChunkSize = ma_dr_wav__riff_chunk_size_rf64(pWav->dataChunkDataSize, pWav->pMetadata, pWav->metadataCount); - ma_dr_wav__write_u64ne_to_le(pWav, riffChunkSize); - } - if (pWav->onSeek(pWav->pUserData, ds64BodyPos + 8, ma_dr_wav_seek_origin_start)) { - ma_uint64 dataChunkSize = ma_dr_wav__data_chunk_size_rf64(pWav->dataChunkDataSize); - ma_dr_wav__write_u64ne_to_le(pWav, dataChunkSize); - } - } - } - if (pWav->isSequentialWrite) { - if (pWav->dataChunkDataSize != pWav->dataChunkDataSizeTargetWrite) { - result = MA_INVALID_FILE; - } - } - } else { - ma_dr_wav_free(pWav->pMetadata, &pWav->allocationCallbacks); - } -#ifndef MA_DR_WAV_NO_STDIO - if (pWav->onRead == ma_dr_wav__on_read_stdio || pWav->onWrite == ma_dr_wav__on_write_stdio) { - fclose((FILE*)pWav->pUserData); - } -#endif - return result; -} -MA_API size_t ma_dr_wav_read_raw(ma_dr_wav* pWav, size_t bytesToRead, void* pBufferOut) -{ - size_t bytesRead; - ma_uint32 bytesPerFrame; - if (pWav == NULL || bytesToRead == 0) { - return 0; - } - if (bytesToRead > pWav->bytesRemaining) { - bytesToRead = (size_t)pWav->bytesRemaining; - } - if (bytesToRead == 0) { - return 0; - } - bytesPerFrame = ma_dr_wav_get_bytes_per_pcm_frame(pWav); - if (bytesPerFrame == 0) { - return 0; - } - if (pBufferOut != NULL) { - bytesRead = pWav->onRead(pWav->pUserData, pBufferOut, bytesToRead); - } else { - bytesRead = 0; - while (bytesRead < bytesToRead) { - size_t bytesToSeek = (bytesToRead - bytesRead); - if (bytesToSeek > 0x7FFFFFFF) { - bytesToSeek = 0x7FFFFFFF; - } - if (pWav->onSeek(pWav->pUserData, (int)bytesToSeek, ma_dr_wav_seek_origin_current) == MA_FALSE) { - break; - } - bytesRead += bytesToSeek; - } - while (bytesRead < bytesToRead) { - ma_uint8 buffer[4096]; - size_t bytesSeeked; - size_t bytesToSeek = (bytesToRead - bytesRead); - if (bytesToSeek > sizeof(buffer)) { - bytesToSeek = sizeof(buffer); - } - bytesSeeked = pWav->onRead(pWav->pUserData, buffer, bytesToSeek); - bytesRead += bytesSeeked; - if (bytesSeeked < bytesToSeek) { - break; - } - } - } - pWav->readCursorInPCMFrames += bytesRead / bytesPerFrame; - pWav->bytesRemaining -= bytesRead; - return bytesRead; -} -MA_API ma_uint64 ma_dr_wav_read_pcm_frames_le(ma_dr_wav* pWav, ma_uint64 framesToRead, void* pBufferOut) -{ - ma_uint32 bytesPerFrame; - ma_uint64 bytesToRead; - ma_uint64 framesRemainingInFile; - if (pWav == NULL || framesToRead == 0) { - return 0; - } - if (ma_dr_wav__is_compressed_format_tag(pWav->translatedFormatTag)) { - return 0; - } - framesRemainingInFile = pWav->totalPCMFrameCount - pWav->readCursorInPCMFrames; - if (framesToRead > framesRemainingInFile) { - framesToRead = framesRemainingInFile; - } - bytesPerFrame = ma_dr_wav_get_bytes_per_pcm_frame(pWav); - if (bytesPerFrame == 0) { - return 0; - } - bytesToRead = framesToRead * bytesPerFrame; - if (bytesToRead > MA_SIZE_MAX) { - bytesToRead = (MA_SIZE_MAX / bytesPerFrame) * bytesPerFrame; - } - if (bytesToRead == 0) { - return 0; - } - return ma_dr_wav_read_raw(pWav, (size_t)bytesToRead, pBufferOut) / bytesPerFrame; -} -MA_API ma_uint64 ma_dr_wav_read_pcm_frames_be(ma_dr_wav* pWav, ma_uint64 framesToRead, void* pBufferOut) -{ - ma_uint64 framesRead = ma_dr_wav_read_pcm_frames_le(pWav, framesToRead, pBufferOut); - if (pBufferOut != NULL) { - ma_uint32 bytesPerFrame = ma_dr_wav_get_bytes_per_pcm_frame(pWav); - if (bytesPerFrame == 0) { - return 0; - } - ma_dr_wav__bswap_samples(pBufferOut, framesRead*pWav->channels, bytesPerFrame/pWav->channels); - } - return framesRead; -} -MA_API ma_uint64 ma_dr_wav_read_pcm_frames(ma_dr_wav* pWav, ma_uint64 framesToRead, void* pBufferOut) -{ - ma_uint64 framesRead = 0; - if (ma_dr_wav_is_container_be(pWav->container)) { - if (pWav->container != ma_dr_wav_container_aiff || pWav->aiff.isLE == MA_FALSE) { - if (ma_dr_wav__is_little_endian()) { - framesRead = ma_dr_wav_read_pcm_frames_be(pWav, framesToRead, pBufferOut); - } else { - framesRead = ma_dr_wav_read_pcm_frames_le(pWav, framesToRead, pBufferOut); - } - goto post_process; - } - } - if (ma_dr_wav__is_little_endian()) { - framesRead = ma_dr_wav_read_pcm_frames_le(pWav, framesToRead, pBufferOut); - } else { - framesRead = ma_dr_wav_read_pcm_frames_be(pWav, framesToRead, pBufferOut); - } - post_process: - { - if (pWav->container == ma_dr_wav_container_aiff && pWav->bitsPerSample == 8 && pWav->aiff.isUnsigned == MA_FALSE) { - if (pBufferOut != NULL) { - ma_uint64 iSample; - for (iSample = 0; iSample < framesRead * pWav->channels; iSample += 1) { - ((ma_uint8*)pBufferOut)[iSample] += 128; - } - } - } - } - return framesRead; -} -MA_PRIVATE ma_bool32 ma_dr_wav_seek_to_first_pcm_frame(ma_dr_wav* pWav) -{ - if (pWav->onWrite != NULL) { - return MA_FALSE; - } - if (!pWav->onSeek(pWav->pUserData, (int)pWav->dataChunkDataPos, ma_dr_wav_seek_origin_start)) { - return MA_FALSE; - } - if (ma_dr_wav__is_compressed_format_tag(pWav->translatedFormatTag)) { - if (pWav->translatedFormatTag == MA_DR_WAVE_FORMAT_ADPCM) { - MA_DR_WAV_ZERO_OBJECT(&pWav->msadpcm); - } else if (pWav->translatedFormatTag == MA_DR_WAVE_FORMAT_DVI_ADPCM) { - MA_DR_WAV_ZERO_OBJECT(&pWav->ima); - } else { - MA_DR_WAV_ASSERT(MA_FALSE); - } - } - pWav->readCursorInPCMFrames = 0; - pWav->bytesRemaining = pWav->dataChunkDataSize; - return MA_TRUE; -} -MA_API ma_bool32 ma_dr_wav_seek_to_pcm_frame(ma_dr_wav* pWav, ma_uint64 targetFrameIndex) -{ - if (pWav == NULL || pWav->onSeek == NULL) { - return MA_FALSE; - } - if (pWav->onWrite != NULL) { - return MA_FALSE; - } - if (pWav->totalPCMFrameCount == 0) { - return MA_TRUE; - } - if (targetFrameIndex > pWav->totalPCMFrameCount) { - targetFrameIndex = pWav->totalPCMFrameCount; - } - if (ma_dr_wav__is_compressed_format_tag(pWav->translatedFormatTag)) { - if (targetFrameIndex < pWav->readCursorInPCMFrames) { - if (!ma_dr_wav_seek_to_first_pcm_frame(pWav)) { - return MA_FALSE; - } - } - if (targetFrameIndex > pWav->readCursorInPCMFrames) { - ma_uint64 offsetInFrames = targetFrameIndex - pWav->readCursorInPCMFrames; - ma_int16 devnull[2048]; - while (offsetInFrames > 0) { - ma_uint64 framesRead = 0; - ma_uint64 framesToRead = offsetInFrames; - if (framesToRead > ma_dr_wav_countof(devnull)/pWav->channels) { - framesToRead = ma_dr_wav_countof(devnull)/pWav->channels; - } - if (pWav->translatedFormatTag == MA_DR_WAVE_FORMAT_ADPCM) { - framesRead = ma_dr_wav_read_pcm_frames_s16__msadpcm(pWav, framesToRead, devnull); - } else if (pWav->translatedFormatTag == MA_DR_WAVE_FORMAT_DVI_ADPCM) { - framesRead = ma_dr_wav_read_pcm_frames_s16__ima(pWav, framesToRead, devnull); - } else { - MA_DR_WAV_ASSERT(MA_FALSE); - } - if (framesRead != framesToRead) { - return MA_FALSE; - } - offsetInFrames -= framesRead; - } - } - } else { - ma_uint64 totalSizeInBytes; - ma_uint64 currentBytePos; - ma_uint64 targetBytePos; - ma_uint64 offset; - ma_uint32 bytesPerFrame; - bytesPerFrame = ma_dr_wav_get_bytes_per_pcm_frame(pWav); - if (bytesPerFrame == 0) { - return MA_FALSE; - } - totalSizeInBytes = pWav->totalPCMFrameCount * bytesPerFrame; - currentBytePos = totalSizeInBytes - pWav->bytesRemaining; - targetBytePos = targetFrameIndex * bytesPerFrame; - if (currentBytePos < targetBytePos) { - offset = (targetBytePos - currentBytePos); - } else { - if (!ma_dr_wav_seek_to_first_pcm_frame(pWav)) { - return MA_FALSE; - } - offset = targetBytePos; - } - while (offset > 0) { - int offset32 = ((offset > INT_MAX) ? INT_MAX : (int)offset); - if (!pWav->onSeek(pWav->pUserData, offset32, ma_dr_wav_seek_origin_current)) { - return MA_FALSE; - } - pWav->readCursorInPCMFrames += offset32 / bytesPerFrame; - pWav->bytesRemaining -= offset32; - offset -= offset32; - } - } - return MA_TRUE; -} -MA_API ma_result ma_dr_wav_get_cursor_in_pcm_frames(ma_dr_wav* pWav, ma_uint64* pCursor) -{ - if (pCursor == NULL) { - return MA_INVALID_ARGS; - } - *pCursor = 0; - if (pWav == NULL) { - return MA_INVALID_ARGS; - } - *pCursor = pWav->readCursorInPCMFrames; - return MA_SUCCESS; -} -MA_API ma_result ma_dr_wav_get_length_in_pcm_frames(ma_dr_wav* pWav, ma_uint64* pLength) -{ - if (pLength == NULL) { - return MA_INVALID_ARGS; - } - *pLength = 0; - if (pWav == NULL) { - return MA_INVALID_ARGS; - } - *pLength = pWav->totalPCMFrameCount; - return MA_SUCCESS; -} -MA_API size_t ma_dr_wav_write_raw(ma_dr_wav* pWav, size_t bytesToWrite, const void* pData) -{ - size_t bytesWritten; - if (pWav == NULL || bytesToWrite == 0 || pData == NULL) { - return 0; - } - bytesWritten = pWav->onWrite(pWav->pUserData, pData, bytesToWrite); - pWav->dataChunkDataSize += bytesWritten; - return bytesWritten; -} -MA_API ma_uint64 ma_dr_wav_write_pcm_frames_le(ma_dr_wav* pWav, ma_uint64 framesToWrite, const void* pData) -{ - ma_uint64 bytesToWrite; - ma_uint64 bytesWritten; - const ma_uint8* pRunningData; - if (pWav == NULL || framesToWrite == 0 || pData == NULL) { - return 0; - } - bytesToWrite = ((framesToWrite * pWav->channels * pWav->bitsPerSample) / 8); - if (bytesToWrite > MA_SIZE_MAX) { - return 0; - } - bytesWritten = 0; - pRunningData = (const ma_uint8*)pData; - while (bytesToWrite > 0) { - size_t bytesJustWritten; - ma_uint64 bytesToWriteThisIteration; - bytesToWriteThisIteration = bytesToWrite; - MA_DR_WAV_ASSERT(bytesToWriteThisIteration <= MA_SIZE_MAX); - bytesJustWritten = ma_dr_wav_write_raw(pWav, (size_t)bytesToWriteThisIteration, pRunningData); - if (bytesJustWritten == 0) { - break; - } - bytesToWrite -= bytesJustWritten; - bytesWritten += bytesJustWritten; - pRunningData += bytesJustWritten; - } - return (bytesWritten * 8) / pWav->bitsPerSample / pWav->channels; -} -MA_API ma_uint64 ma_dr_wav_write_pcm_frames_be(ma_dr_wav* pWav, ma_uint64 framesToWrite, const void* pData) -{ - ma_uint64 bytesToWrite; - ma_uint64 bytesWritten; - ma_uint32 bytesPerSample; - const ma_uint8* pRunningData; - if (pWav == NULL || framesToWrite == 0 || pData == NULL) { - return 0; - } - bytesToWrite = ((framesToWrite * pWav->channels * pWav->bitsPerSample) / 8); - if (bytesToWrite > MA_SIZE_MAX) { - return 0; - } - bytesWritten = 0; - pRunningData = (const ma_uint8*)pData; - bytesPerSample = ma_dr_wav_get_bytes_per_pcm_frame(pWav) / pWav->channels; - if (bytesPerSample == 0) { - return 0; - } - while (bytesToWrite > 0) { - ma_uint8 temp[4096]; - ma_uint32 sampleCount; - size_t bytesJustWritten; - ma_uint64 bytesToWriteThisIteration; - bytesToWriteThisIteration = bytesToWrite; - MA_DR_WAV_ASSERT(bytesToWriteThisIteration <= MA_SIZE_MAX); - sampleCount = sizeof(temp)/bytesPerSample; - if (bytesToWriteThisIteration > ((ma_uint64)sampleCount)*bytesPerSample) { - bytesToWriteThisIteration = ((ma_uint64)sampleCount)*bytesPerSample; - } - MA_DR_WAV_COPY_MEMORY(temp, pRunningData, (size_t)bytesToWriteThisIteration); - ma_dr_wav__bswap_samples(temp, sampleCount, bytesPerSample); - bytesJustWritten = ma_dr_wav_write_raw(pWav, (size_t)bytesToWriteThisIteration, temp); - if (bytesJustWritten == 0) { - break; - } - bytesToWrite -= bytesJustWritten; - bytesWritten += bytesJustWritten; - pRunningData += bytesJustWritten; - } - return (bytesWritten * 8) / pWav->bitsPerSample / pWav->channels; -} -MA_API ma_uint64 ma_dr_wav_write_pcm_frames(ma_dr_wav* pWav, ma_uint64 framesToWrite, const void* pData) -{ - if (ma_dr_wav__is_little_endian()) { - return ma_dr_wav_write_pcm_frames_le(pWav, framesToWrite, pData); - } else { - return ma_dr_wav_write_pcm_frames_be(pWav, framesToWrite, pData); - } -} -MA_PRIVATE ma_uint64 ma_dr_wav_read_pcm_frames_s16__msadpcm(ma_dr_wav* pWav, ma_uint64 framesToRead, ma_int16* pBufferOut) -{ - ma_uint64 totalFramesRead = 0; - static ma_int32 adaptationTable[] = { - 230, 230, 230, 230, 307, 409, 512, 614, - 768, 614, 512, 409, 307, 230, 230, 230 - }; - static ma_int32 coeff1Table[] = { 256, 512, 0, 192, 240, 460, 392 }; - static ma_int32 coeff2Table[] = { 0, -256, 0, 64, 0, -208, -232 }; - MA_DR_WAV_ASSERT(pWav != NULL); - MA_DR_WAV_ASSERT(framesToRead > 0); - while (pWav->readCursorInPCMFrames < pWav->totalPCMFrameCount) { - MA_DR_WAV_ASSERT(framesToRead > 0); - if (pWav->msadpcm.cachedFrameCount == 0 && pWav->msadpcm.bytesRemainingInBlock == 0) { - if (pWav->channels == 1) { - ma_uint8 header[7]; - if (pWav->onRead(pWav->pUserData, header, sizeof(header)) != sizeof(header)) { - return totalFramesRead; - } - pWav->msadpcm.bytesRemainingInBlock = pWav->fmt.blockAlign - sizeof(header); - pWav->msadpcm.predictor[0] = header[0]; - pWav->msadpcm.delta[0] = ma_dr_wav_bytes_to_s16(header + 1); - pWav->msadpcm.prevFrames[0][1] = (ma_int32)ma_dr_wav_bytes_to_s16(header + 3); - pWav->msadpcm.prevFrames[0][0] = (ma_int32)ma_dr_wav_bytes_to_s16(header + 5); - pWav->msadpcm.cachedFrames[2] = pWav->msadpcm.prevFrames[0][0]; - pWav->msadpcm.cachedFrames[3] = pWav->msadpcm.prevFrames[0][1]; - pWav->msadpcm.cachedFrameCount = 2; - if (pWav->msadpcm.predictor[0] >= ma_dr_wav_countof(coeff1Table)) { - return totalFramesRead; - } - } else { - ma_uint8 header[14]; - if (pWav->onRead(pWav->pUserData, header, sizeof(header)) != sizeof(header)) { - return totalFramesRead; - } - pWav->msadpcm.bytesRemainingInBlock = pWav->fmt.blockAlign - sizeof(header); - pWav->msadpcm.predictor[0] = header[0]; - pWav->msadpcm.predictor[1] = header[1]; - pWav->msadpcm.delta[0] = ma_dr_wav_bytes_to_s16(header + 2); - pWav->msadpcm.delta[1] = ma_dr_wav_bytes_to_s16(header + 4); - pWav->msadpcm.prevFrames[0][1] = (ma_int32)ma_dr_wav_bytes_to_s16(header + 6); - pWav->msadpcm.prevFrames[1][1] = (ma_int32)ma_dr_wav_bytes_to_s16(header + 8); - pWav->msadpcm.prevFrames[0][0] = (ma_int32)ma_dr_wav_bytes_to_s16(header + 10); - pWav->msadpcm.prevFrames[1][0] = (ma_int32)ma_dr_wav_bytes_to_s16(header + 12); - pWav->msadpcm.cachedFrames[0] = pWav->msadpcm.prevFrames[0][0]; - pWav->msadpcm.cachedFrames[1] = pWav->msadpcm.prevFrames[1][0]; - pWav->msadpcm.cachedFrames[2] = pWav->msadpcm.prevFrames[0][1]; - pWav->msadpcm.cachedFrames[3] = pWav->msadpcm.prevFrames[1][1]; - pWav->msadpcm.cachedFrameCount = 2; - if (pWav->msadpcm.predictor[0] >= ma_dr_wav_countof(coeff1Table) || pWav->msadpcm.predictor[1] >= ma_dr_wav_countof(coeff2Table)) { - return totalFramesRead; - } - } - } - while (framesToRead > 0 && pWav->msadpcm.cachedFrameCount > 0 && pWav->readCursorInPCMFrames < pWav->totalPCMFrameCount) { - if (pBufferOut != NULL) { - ma_uint32 iSample = 0; - for (iSample = 0; iSample < pWav->channels; iSample += 1) { - pBufferOut[iSample] = (ma_int16)pWav->msadpcm.cachedFrames[(ma_dr_wav_countof(pWav->msadpcm.cachedFrames) - (pWav->msadpcm.cachedFrameCount*pWav->channels)) + iSample]; - } - pBufferOut += pWav->channels; - } - framesToRead -= 1; - totalFramesRead += 1; - pWav->readCursorInPCMFrames += 1; - pWav->msadpcm.cachedFrameCount -= 1; - } - if (framesToRead == 0) { - break; - } - if (pWav->msadpcm.cachedFrameCount == 0) { - if (pWav->msadpcm.bytesRemainingInBlock == 0) { - continue; - } else { - ma_uint8 nibbles; - ma_int32 nibble0; - ma_int32 nibble1; - if (pWav->onRead(pWav->pUserData, &nibbles, 1) != 1) { - return totalFramesRead; - } - pWav->msadpcm.bytesRemainingInBlock -= 1; - nibble0 = ((nibbles & 0xF0) >> 4); if ((nibbles & 0x80)) { nibble0 |= 0xFFFFFFF0UL; } - nibble1 = ((nibbles & 0x0F) >> 0); if ((nibbles & 0x08)) { nibble1 |= 0xFFFFFFF0UL; } - if (pWav->channels == 1) { - ma_int32 newSample0; - ma_int32 newSample1; - newSample0 = ((pWav->msadpcm.prevFrames[0][1] * coeff1Table[pWav->msadpcm.predictor[0]]) + (pWav->msadpcm.prevFrames[0][0] * coeff2Table[pWav->msadpcm.predictor[0]])) >> 8; - newSample0 += nibble0 * pWav->msadpcm.delta[0]; - newSample0 = ma_dr_wav_clamp(newSample0, -32768, 32767); - pWav->msadpcm.delta[0] = (adaptationTable[((nibbles & 0xF0) >> 4)] * pWav->msadpcm.delta[0]) >> 8; - if (pWav->msadpcm.delta[0] < 16) { - pWav->msadpcm.delta[0] = 16; - } - pWav->msadpcm.prevFrames[0][0] = pWav->msadpcm.prevFrames[0][1]; - pWav->msadpcm.prevFrames[0][1] = newSample0; - newSample1 = ((pWav->msadpcm.prevFrames[0][1] * coeff1Table[pWav->msadpcm.predictor[0]]) + (pWav->msadpcm.prevFrames[0][0] * coeff2Table[pWav->msadpcm.predictor[0]])) >> 8; - newSample1 += nibble1 * pWav->msadpcm.delta[0]; - newSample1 = ma_dr_wav_clamp(newSample1, -32768, 32767); - pWav->msadpcm.delta[0] = (adaptationTable[((nibbles & 0x0F) >> 0)] * pWav->msadpcm.delta[0]) >> 8; - if (pWav->msadpcm.delta[0] < 16) { - pWav->msadpcm.delta[0] = 16; - } - pWav->msadpcm.prevFrames[0][0] = pWav->msadpcm.prevFrames[0][1]; - pWav->msadpcm.prevFrames[0][1] = newSample1; - pWav->msadpcm.cachedFrames[2] = newSample0; - pWav->msadpcm.cachedFrames[3] = newSample1; - pWav->msadpcm.cachedFrameCount = 2; - } else { - ma_int32 newSample0; - ma_int32 newSample1; - newSample0 = ((pWav->msadpcm.prevFrames[0][1] * coeff1Table[pWav->msadpcm.predictor[0]]) + (pWav->msadpcm.prevFrames[0][0] * coeff2Table[pWav->msadpcm.predictor[0]])) >> 8; - newSample0 += nibble0 * pWav->msadpcm.delta[0]; - newSample0 = ma_dr_wav_clamp(newSample0, -32768, 32767); - pWav->msadpcm.delta[0] = (adaptationTable[((nibbles & 0xF0) >> 4)] * pWav->msadpcm.delta[0]) >> 8; - if (pWav->msadpcm.delta[0] < 16) { - pWav->msadpcm.delta[0] = 16; - } - pWav->msadpcm.prevFrames[0][0] = pWav->msadpcm.prevFrames[0][1]; - pWav->msadpcm.prevFrames[0][1] = newSample0; - newSample1 = ((pWav->msadpcm.prevFrames[1][1] * coeff1Table[pWav->msadpcm.predictor[1]]) + (pWav->msadpcm.prevFrames[1][0] * coeff2Table[pWav->msadpcm.predictor[1]])) >> 8; - newSample1 += nibble1 * pWav->msadpcm.delta[1]; - newSample1 = ma_dr_wav_clamp(newSample1, -32768, 32767); - pWav->msadpcm.delta[1] = (adaptationTable[((nibbles & 0x0F) >> 0)] * pWav->msadpcm.delta[1]) >> 8; - if (pWav->msadpcm.delta[1] < 16) { - pWav->msadpcm.delta[1] = 16; - } - pWav->msadpcm.prevFrames[1][0] = pWav->msadpcm.prevFrames[1][1]; - pWav->msadpcm.prevFrames[1][1] = newSample1; - pWav->msadpcm.cachedFrames[2] = newSample0; - pWav->msadpcm.cachedFrames[3] = newSample1; - pWav->msadpcm.cachedFrameCount = 1; - } - } - } - } - return totalFramesRead; -} -MA_PRIVATE ma_uint64 ma_dr_wav_read_pcm_frames_s16__ima(ma_dr_wav* pWav, ma_uint64 framesToRead, ma_int16* pBufferOut) -{ - ma_uint64 totalFramesRead = 0; - ma_uint32 iChannel; - static ma_int32 indexTable[16] = { - -1, -1, -1, -1, 2, 4, 6, 8, - -1, -1, -1, -1, 2, 4, 6, 8 - }; - static ma_int32 stepTable[89] = { - 7, 8, 9, 10, 11, 12, 13, 14, 16, 17, - 19, 21, 23, 25, 28, 31, 34, 37, 41, 45, - 50, 55, 60, 66, 73, 80, 88, 97, 107, 118, - 130, 143, 157, 173, 190, 209, 230, 253, 279, 307, - 337, 371, 408, 449, 494, 544, 598, 658, 724, 796, - 876, 963, 1060, 1166, 1282, 1411, 1552, 1707, 1878, 2066, - 2272, 2499, 2749, 3024, 3327, 3660, 4026, 4428, 4871, 5358, - 5894, 6484, 7132, 7845, 8630, 9493, 10442, 11487, 12635, 13899, - 15289, 16818, 18500, 20350, 22385, 24623, 27086, 29794, 32767 - }; - MA_DR_WAV_ASSERT(pWav != NULL); - MA_DR_WAV_ASSERT(framesToRead > 0); - while (pWav->readCursorInPCMFrames < pWav->totalPCMFrameCount) { - MA_DR_WAV_ASSERT(framesToRead > 0); - if (pWav->ima.cachedFrameCount == 0 && pWav->ima.bytesRemainingInBlock == 0) { - if (pWav->channels == 1) { - ma_uint8 header[4]; - if (pWav->onRead(pWav->pUserData, header, sizeof(header)) != sizeof(header)) { - return totalFramesRead; - } - pWav->ima.bytesRemainingInBlock = pWav->fmt.blockAlign - sizeof(header); - if (header[2] >= ma_dr_wav_countof(stepTable)) { - pWav->onSeek(pWav->pUserData, pWav->ima.bytesRemainingInBlock, ma_dr_wav_seek_origin_current); - pWav->ima.bytesRemainingInBlock = 0; - return totalFramesRead; - } - pWav->ima.predictor[0] = (ma_int16)ma_dr_wav_bytes_to_u16(header + 0); - pWav->ima.stepIndex[0] = ma_dr_wav_clamp(header[2], 0, (ma_int32)ma_dr_wav_countof(stepTable)-1); - pWav->ima.cachedFrames[ma_dr_wav_countof(pWav->ima.cachedFrames) - 1] = pWav->ima.predictor[0]; - pWav->ima.cachedFrameCount = 1; - } else { - ma_uint8 header[8]; - if (pWav->onRead(pWav->pUserData, header, sizeof(header)) != sizeof(header)) { - return totalFramesRead; - } - pWav->ima.bytesRemainingInBlock = pWav->fmt.blockAlign - sizeof(header); - if (header[2] >= ma_dr_wav_countof(stepTable) || header[6] >= ma_dr_wav_countof(stepTable)) { - pWav->onSeek(pWav->pUserData, pWav->ima.bytesRemainingInBlock, ma_dr_wav_seek_origin_current); - pWav->ima.bytesRemainingInBlock = 0; - return totalFramesRead; - } - pWav->ima.predictor[0] = ma_dr_wav_bytes_to_s16(header + 0); - pWav->ima.stepIndex[0] = ma_dr_wav_clamp(header[2], 0, (ma_int32)ma_dr_wav_countof(stepTable)-1); - pWav->ima.predictor[1] = ma_dr_wav_bytes_to_s16(header + 4); - pWav->ima.stepIndex[1] = ma_dr_wav_clamp(header[6], 0, (ma_int32)ma_dr_wav_countof(stepTable)-1); - pWav->ima.cachedFrames[ma_dr_wav_countof(pWav->ima.cachedFrames) - 2] = pWav->ima.predictor[0]; - pWav->ima.cachedFrames[ma_dr_wav_countof(pWav->ima.cachedFrames) - 1] = pWav->ima.predictor[1]; - pWav->ima.cachedFrameCount = 1; - } - } - while (framesToRead > 0 && pWav->ima.cachedFrameCount > 0 && pWav->readCursorInPCMFrames < pWav->totalPCMFrameCount) { - if (pBufferOut != NULL) { - ma_uint32 iSample; - for (iSample = 0; iSample < pWav->channels; iSample += 1) { - pBufferOut[iSample] = (ma_int16)pWav->ima.cachedFrames[(ma_dr_wav_countof(pWav->ima.cachedFrames) - (pWav->ima.cachedFrameCount*pWav->channels)) + iSample]; - } - pBufferOut += pWav->channels; - } - framesToRead -= 1; - totalFramesRead += 1; - pWav->readCursorInPCMFrames += 1; - pWav->ima.cachedFrameCount -= 1; - } - if (framesToRead == 0) { - break; - } - if (pWav->ima.cachedFrameCount == 0) { - if (pWav->ima.bytesRemainingInBlock == 0) { - continue; - } else { - pWav->ima.cachedFrameCount = 8; - for (iChannel = 0; iChannel < pWav->channels; ++iChannel) { - ma_uint32 iByte; - ma_uint8 nibbles[4]; - if (pWav->onRead(pWav->pUserData, &nibbles, 4) != 4) { - pWav->ima.cachedFrameCount = 0; - return totalFramesRead; - } - pWav->ima.bytesRemainingInBlock -= 4; - for (iByte = 0; iByte < 4; ++iByte) { - ma_uint8 nibble0 = ((nibbles[iByte] & 0x0F) >> 0); - ma_uint8 nibble1 = ((nibbles[iByte] & 0xF0) >> 4); - ma_int32 step = stepTable[pWav->ima.stepIndex[iChannel]]; - ma_int32 predictor = pWav->ima.predictor[iChannel]; - ma_int32 diff = step >> 3; - if (nibble0 & 1) diff += step >> 2; - if (nibble0 & 2) diff += step >> 1; - if (nibble0 & 4) diff += step; - if (nibble0 & 8) diff = -diff; - predictor = ma_dr_wav_clamp(predictor + diff, -32768, 32767); - pWav->ima.predictor[iChannel] = predictor; - pWav->ima.stepIndex[iChannel] = ma_dr_wav_clamp(pWav->ima.stepIndex[iChannel] + indexTable[nibble0], 0, (ma_int32)ma_dr_wav_countof(stepTable)-1); - pWav->ima.cachedFrames[(ma_dr_wav_countof(pWav->ima.cachedFrames) - (pWav->ima.cachedFrameCount*pWav->channels)) + (iByte*2+0)*pWav->channels + iChannel] = predictor; - step = stepTable[pWav->ima.stepIndex[iChannel]]; - predictor = pWav->ima.predictor[iChannel]; - diff = step >> 3; - if (nibble1 & 1) diff += step >> 2; - if (nibble1 & 2) diff += step >> 1; - if (nibble1 & 4) diff += step; - if (nibble1 & 8) diff = -diff; - predictor = ma_dr_wav_clamp(predictor + diff, -32768, 32767); - pWav->ima.predictor[iChannel] = predictor; - pWav->ima.stepIndex[iChannel] = ma_dr_wav_clamp(pWav->ima.stepIndex[iChannel] + indexTable[nibble1], 0, (ma_int32)ma_dr_wav_countof(stepTable)-1); - pWav->ima.cachedFrames[(ma_dr_wav_countof(pWav->ima.cachedFrames) - (pWav->ima.cachedFrameCount*pWav->channels)) + (iByte*2+1)*pWav->channels + iChannel] = predictor; - } - } - } - } - } - return totalFramesRead; -} -#ifndef MA_DR_WAV_NO_CONVERSION_API -static unsigned short g_ma_dr_wavAlawTable[256] = { - 0xEA80, 0xEB80, 0xE880, 0xE980, 0xEE80, 0xEF80, 0xEC80, 0xED80, 0xE280, 0xE380, 0xE080, 0xE180, 0xE680, 0xE780, 0xE480, 0xE580, - 0xF540, 0xF5C0, 0xF440, 0xF4C0, 0xF740, 0xF7C0, 0xF640, 0xF6C0, 0xF140, 0xF1C0, 0xF040, 0xF0C0, 0xF340, 0xF3C0, 0xF240, 0xF2C0, - 0xAA00, 0xAE00, 0xA200, 0xA600, 0xBA00, 0xBE00, 0xB200, 0xB600, 0x8A00, 0x8E00, 0x8200, 0x8600, 0x9A00, 0x9E00, 0x9200, 0x9600, - 0xD500, 0xD700, 0xD100, 0xD300, 0xDD00, 0xDF00, 0xD900, 0xDB00, 0xC500, 0xC700, 0xC100, 0xC300, 0xCD00, 0xCF00, 0xC900, 0xCB00, - 0xFEA8, 0xFEB8, 0xFE88, 0xFE98, 0xFEE8, 0xFEF8, 0xFEC8, 0xFED8, 0xFE28, 0xFE38, 0xFE08, 0xFE18, 0xFE68, 0xFE78, 0xFE48, 0xFE58, - 0xFFA8, 0xFFB8, 0xFF88, 0xFF98, 0xFFE8, 0xFFF8, 0xFFC8, 0xFFD8, 0xFF28, 0xFF38, 0xFF08, 0xFF18, 0xFF68, 0xFF78, 0xFF48, 0xFF58, - 0xFAA0, 0xFAE0, 0xFA20, 0xFA60, 0xFBA0, 0xFBE0, 0xFB20, 0xFB60, 0xF8A0, 0xF8E0, 0xF820, 0xF860, 0xF9A0, 0xF9E0, 0xF920, 0xF960, - 0xFD50, 0xFD70, 0xFD10, 0xFD30, 0xFDD0, 0xFDF0, 0xFD90, 0xFDB0, 0xFC50, 0xFC70, 0xFC10, 0xFC30, 0xFCD0, 0xFCF0, 0xFC90, 0xFCB0, - 0x1580, 0x1480, 0x1780, 0x1680, 0x1180, 0x1080, 0x1380, 0x1280, 0x1D80, 0x1C80, 0x1F80, 0x1E80, 0x1980, 0x1880, 0x1B80, 0x1A80, - 0x0AC0, 0x0A40, 0x0BC0, 0x0B40, 0x08C0, 0x0840, 0x09C0, 0x0940, 0x0EC0, 0x0E40, 0x0FC0, 0x0F40, 0x0CC0, 0x0C40, 0x0DC0, 0x0D40, - 0x5600, 0x5200, 0x5E00, 0x5A00, 0x4600, 0x4200, 0x4E00, 0x4A00, 0x7600, 0x7200, 0x7E00, 0x7A00, 0x6600, 0x6200, 0x6E00, 0x6A00, - 0x2B00, 0x2900, 0x2F00, 0x2D00, 0x2300, 0x2100, 0x2700, 0x2500, 0x3B00, 0x3900, 0x3F00, 0x3D00, 0x3300, 0x3100, 0x3700, 0x3500, - 0x0158, 0x0148, 0x0178, 0x0168, 0x0118, 0x0108, 0x0138, 0x0128, 0x01D8, 0x01C8, 0x01F8, 0x01E8, 0x0198, 0x0188, 0x01B8, 0x01A8, - 0x0058, 0x0048, 0x0078, 0x0068, 0x0018, 0x0008, 0x0038, 0x0028, 0x00D8, 0x00C8, 0x00F8, 0x00E8, 0x0098, 0x0088, 0x00B8, 0x00A8, - 0x0560, 0x0520, 0x05E0, 0x05A0, 0x0460, 0x0420, 0x04E0, 0x04A0, 0x0760, 0x0720, 0x07E0, 0x07A0, 0x0660, 0x0620, 0x06E0, 0x06A0, - 0x02B0, 0x0290, 0x02F0, 0x02D0, 0x0230, 0x0210, 0x0270, 0x0250, 0x03B0, 0x0390, 0x03F0, 0x03D0, 0x0330, 0x0310, 0x0370, 0x0350 -}; -static unsigned short g_ma_dr_wavMulawTable[256] = { - 0x8284, 0x8684, 0x8A84, 0x8E84, 0x9284, 0x9684, 0x9A84, 0x9E84, 0xA284, 0xA684, 0xAA84, 0xAE84, 0xB284, 0xB684, 0xBA84, 0xBE84, - 0xC184, 0xC384, 0xC584, 0xC784, 0xC984, 0xCB84, 0xCD84, 0xCF84, 0xD184, 0xD384, 0xD584, 0xD784, 0xD984, 0xDB84, 0xDD84, 0xDF84, - 0xE104, 0xE204, 0xE304, 0xE404, 0xE504, 0xE604, 0xE704, 0xE804, 0xE904, 0xEA04, 0xEB04, 0xEC04, 0xED04, 0xEE04, 0xEF04, 0xF004, - 0xF0C4, 0xF144, 0xF1C4, 0xF244, 0xF2C4, 0xF344, 0xF3C4, 0xF444, 0xF4C4, 0xF544, 0xF5C4, 0xF644, 0xF6C4, 0xF744, 0xF7C4, 0xF844, - 0xF8A4, 0xF8E4, 0xF924, 0xF964, 0xF9A4, 0xF9E4, 0xFA24, 0xFA64, 0xFAA4, 0xFAE4, 0xFB24, 0xFB64, 0xFBA4, 0xFBE4, 0xFC24, 0xFC64, - 0xFC94, 0xFCB4, 0xFCD4, 0xFCF4, 0xFD14, 0xFD34, 0xFD54, 0xFD74, 0xFD94, 0xFDB4, 0xFDD4, 0xFDF4, 0xFE14, 0xFE34, 0xFE54, 0xFE74, - 0xFE8C, 0xFE9C, 0xFEAC, 0xFEBC, 0xFECC, 0xFEDC, 0xFEEC, 0xFEFC, 0xFF0C, 0xFF1C, 0xFF2C, 0xFF3C, 0xFF4C, 0xFF5C, 0xFF6C, 0xFF7C, - 0xFF88, 0xFF90, 0xFF98, 0xFFA0, 0xFFA8, 0xFFB0, 0xFFB8, 0xFFC0, 0xFFC8, 0xFFD0, 0xFFD8, 0xFFE0, 0xFFE8, 0xFFF0, 0xFFF8, 0x0000, - 0x7D7C, 0x797C, 0x757C, 0x717C, 0x6D7C, 0x697C, 0x657C, 0x617C, 0x5D7C, 0x597C, 0x557C, 0x517C, 0x4D7C, 0x497C, 0x457C, 0x417C, - 0x3E7C, 0x3C7C, 0x3A7C, 0x387C, 0x367C, 0x347C, 0x327C, 0x307C, 0x2E7C, 0x2C7C, 0x2A7C, 0x287C, 0x267C, 0x247C, 0x227C, 0x207C, - 0x1EFC, 0x1DFC, 0x1CFC, 0x1BFC, 0x1AFC, 0x19FC, 0x18FC, 0x17FC, 0x16FC, 0x15FC, 0x14FC, 0x13FC, 0x12FC, 0x11FC, 0x10FC, 0x0FFC, - 0x0F3C, 0x0EBC, 0x0E3C, 0x0DBC, 0x0D3C, 0x0CBC, 0x0C3C, 0x0BBC, 0x0B3C, 0x0ABC, 0x0A3C, 0x09BC, 0x093C, 0x08BC, 0x083C, 0x07BC, - 0x075C, 0x071C, 0x06DC, 0x069C, 0x065C, 0x061C, 0x05DC, 0x059C, 0x055C, 0x051C, 0x04DC, 0x049C, 0x045C, 0x041C, 0x03DC, 0x039C, - 0x036C, 0x034C, 0x032C, 0x030C, 0x02EC, 0x02CC, 0x02AC, 0x028C, 0x026C, 0x024C, 0x022C, 0x020C, 0x01EC, 0x01CC, 0x01AC, 0x018C, - 0x0174, 0x0164, 0x0154, 0x0144, 0x0134, 0x0124, 0x0114, 0x0104, 0x00F4, 0x00E4, 0x00D4, 0x00C4, 0x00B4, 0x00A4, 0x0094, 0x0084, - 0x0078, 0x0070, 0x0068, 0x0060, 0x0058, 0x0050, 0x0048, 0x0040, 0x0038, 0x0030, 0x0028, 0x0020, 0x0018, 0x0010, 0x0008, 0x0000 -}; -static MA_INLINE ma_int16 ma_dr_wav__alaw_to_s16(ma_uint8 sampleIn) -{ - return (short)g_ma_dr_wavAlawTable[sampleIn]; -} -static MA_INLINE ma_int16 ma_dr_wav__mulaw_to_s16(ma_uint8 sampleIn) -{ - return (short)g_ma_dr_wavMulawTable[sampleIn]; -} -MA_PRIVATE void ma_dr_wav__pcm_to_s16(ma_int16* pOut, const ma_uint8* pIn, size_t totalSampleCount, unsigned int bytesPerSample) -{ - size_t i; - if (bytesPerSample == 1) { - ma_dr_wav_u8_to_s16(pOut, pIn, totalSampleCount); - return; - } - if (bytesPerSample == 2) { - for (i = 0; i < totalSampleCount; ++i) { - *pOut++ = ((const ma_int16*)pIn)[i]; - } - return; - } - if (bytesPerSample == 3) { - ma_dr_wav_s24_to_s16(pOut, pIn, totalSampleCount); - return; - } - if (bytesPerSample == 4) { - ma_dr_wav_s32_to_s16(pOut, (const ma_int32*)pIn, totalSampleCount); - return; - } - if (bytesPerSample > 8) { - MA_DR_WAV_ZERO_MEMORY(pOut, totalSampleCount * sizeof(*pOut)); - return; - } - for (i = 0; i < totalSampleCount; ++i) { - ma_uint64 sample = 0; - unsigned int shift = (8 - bytesPerSample) * 8; - unsigned int j; - for (j = 0; j < bytesPerSample; j += 1) { - MA_DR_WAV_ASSERT(j < 8); - sample |= (ma_uint64)(pIn[j]) << shift; - shift += 8; - } - pIn += j; - *pOut++ = (ma_int16)((ma_int64)sample >> 48); - } -} -MA_PRIVATE void ma_dr_wav__ieee_to_s16(ma_int16* pOut, const ma_uint8* pIn, size_t totalSampleCount, unsigned int bytesPerSample) -{ - if (bytesPerSample == 4) { - ma_dr_wav_f32_to_s16(pOut, (const float*)pIn, totalSampleCount); - return; - } else if (bytesPerSample == 8) { - ma_dr_wav_f64_to_s16(pOut, (const double*)pIn, totalSampleCount); - return; - } else { - MA_DR_WAV_ZERO_MEMORY(pOut, totalSampleCount * sizeof(*pOut)); - return; - } -} -MA_PRIVATE ma_uint64 ma_dr_wav_read_pcm_frames_s16__pcm(ma_dr_wav* pWav, ma_uint64 framesToRead, ma_int16* pBufferOut) -{ - ma_uint64 totalFramesRead; - ma_uint8 sampleData[4096] = {0}; - ma_uint32 bytesPerFrame; - ma_uint32 bytesPerSample; - ma_uint64 samplesRead; - if ((pWav->translatedFormatTag == MA_DR_WAVE_FORMAT_PCM && pWav->bitsPerSample == 16) || pBufferOut == NULL) { - return ma_dr_wav_read_pcm_frames(pWav, framesToRead, pBufferOut); - } - bytesPerFrame = ma_dr_wav_get_bytes_per_pcm_frame(pWav); - if (bytesPerFrame == 0) { - return 0; - } - bytesPerSample = bytesPerFrame / pWav->channels; - if (bytesPerSample == 0 || (bytesPerFrame % pWav->channels) != 0) { - return 0; - } - totalFramesRead = 0; - while (framesToRead > 0) { - ma_uint64 framesToReadThisIteration = ma_dr_wav_min(framesToRead, sizeof(sampleData)/bytesPerFrame); - ma_uint64 framesRead = ma_dr_wav_read_pcm_frames(pWav, framesToReadThisIteration, sampleData); - if (framesRead == 0) { - break; - } - MA_DR_WAV_ASSERT(framesRead <= framesToReadThisIteration); - samplesRead = framesRead * pWav->channels; - if ((samplesRead * bytesPerSample) > sizeof(sampleData)) { - MA_DR_WAV_ASSERT(MA_FALSE); - break; - } - ma_dr_wav__pcm_to_s16(pBufferOut, sampleData, (size_t)samplesRead, bytesPerSample); - pBufferOut += samplesRead; - framesToRead -= framesRead; - totalFramesRead += framesRead; - } - return totalFramesRead; -} -MA_PRIVATE ma_uint64 ma_dr_wav_read_pcm_frames_s16__ieee(ma_dr_wav* pWav, ma_uint64 framesToRead, ma_int16* pBufferOut) -{ - ma_uint64 totalFramesRead; - ma_uint8 sampleData[4096] = {0}; - ma_uint32 bytesPerFrame; - ma_uint32 bytesPerSample; - ma_uint64 samplesRead; - if (pBufferOut == NULL) { - return ma_dr_wav_read_pcm_frames(pWav, framesToRead, NULL); - } - bytesPerFrame = ma_dr_wav_get_bytes_per_pcm_frame(pWav); - if (bytesPerFrame == 0) { - return 0; - } - bytesPerSample = bytesPerFrame / pWav->channels; - if (bytesPerSample == 0 || (bytesPerFrame % pWav->channels) != 0) { - return 0; - } - totalFramesRead = 0; - while (framesToRead > 0) { - ma_uint64 framesToReadThisIteration = ma_dr_wav_min(framesToRead, sizeof(sampleData)/bytesPerFrame); - ma_uint64 framesRead = ma_dr_wav_read_pcm_frames(pWav, framesToReadThisIteration, sampleData); - if (framesRead == 0) { - break; - } - MA_DR_WAV_ASSERT(framesRead <= framesToReadThisIteration); - samplesRead = framesRead * pWav->channels; - if ((samplesRead * bytesPerSample) > sizeof(sampleData)) { - MA_DR_WAV_ASSERT(MA_FALSE); - break; - } - ma_dr_wav__ieee_to_s16(pBufferOut, sampleData, (size_t)samplesRead, bytesPerSample); - pBufferOut += samplesRead; - framesToRead -= framesRead; - totalFramesRead += framesRead; - } - return totalFramesRead; -} -MA_PRIVATE ma_uint64 ma_dr_wav_read_pcm_frames_s16__alaw(ma_dr_wav* pWav, ma_uint64 framesToRead, ma_int16* pBufferOut) -{ - ma_uint64 totalFramesRead; - ma_uint8 sampleData[4096] = {0}; - ma_uint32 bytesPerFrame; - ma_uint32 bytesPerSample; - ma_uint64 samplesRead; - if (pBufferOut == NULL) { - return ma_dr_wav_read_pcm_frames(pWav, framesToRead, NULL); - } - bytesPerFrame = ma_dr_wav_get_bytes_per_pcm_frame(pWav); - if (bytesPerFrame == 0) { - return 0; - } - bytesPerSample = bytesPerFrame / pWav->channels; - if (bytesPerSample == 0 || (bytesPerFrame % pWav->channels) != 0) { - return 0; - } - totalFramesRead = 0; - while (framesToRead > 0) { - ma_uint64 framesToReadThisIteration = ma_dr_wav_min(framesToRead, sizeof(sampleData)/bytesPerFrame); - ma_uint64 framesRead = ma_dr_wav_read_pcm_frames(pWav, framesToReadThisIteration, sampleData); - if (framesRead == 0) { - break; - } - MA_DR_WAV_ASSERT(framesRead <= framesToReadThisIteration); - samplesRead = framesRead * pWav->channels; - if ((samplesRead * bytesPerSample) > sizeof(sampleData)) { - MA_DR_WAV_ASSERT(MA_FALSE); - break; - } - ma_dr_wav_alaw_to_s16(pBufferOut, sampleData, (size_t)samplesRead); - #ifdef MA_DR_WAV_LIBSNDFILE_COMPAT - { - if (pWav->container == ma_dr_wav_container_aiff) { - ma_uint64 iSample; - for (iSample = 0; iSample < samplesRead; iSample += 1) { - pBufferOut[iSample] = -pBufferOut[iSample]; - } - } - } - #endif - pBufferOut += samplesRead; - framesToRead -= framesRead; - totalFramesRead += framesRead; - } - return totalFramesRead; -} -MA_PRIVATE ma_uint64 ma_dr_wav_read_pcm_frames_s16__mulaw(ma_dr_wav* pWav, ma_uint64 framesToRead, ma_int16* pBufferOut) -{ - ma_uint64 totalFramesRead; - ma_uint8 sampleData[4096] = {0}; - ma_uint32 bytesPerFrame; - ma_uint32 bytesPerSample; - ma_uint64 samplesRead; - if (pBufferOut == NULL) { - return ma_dr_wav_read_pcm_frames(pWav, framesToRead, NULL); - } - bytesPerFrame = ma_dr_wav_get_bytes_per_pcm_frame(pWav); - if (bytesPerFrame == 0) { - return 0; - } - bytesPerSample = bytesPerFrame / pWav->channels; - if (bytesPerSample == 0 || (bytesPerFrame % pWav->channels) != 0) { - return 0; - } - totalFramesRead = 0; - while (framesToRead > 0) { - ma_uint64 framesToReadThisIteration = ma_dr_wav_min(framesToRead, sizeof(sampleData)/bytesPerFrame); - ma_uint64 framesRead = ma_dr_wav_read_pcm_frames(pWav, framesToReadThisIteration, sampleData); - if (framesRead == 0) { - break; - } - MA_DR_WAV_ASSERT(framesRead <= framesToReadThisIteration); - samplesRead = framesRead * pWav->channels; - if ((samplesRead * bytesPerSample) > sizeof(sampleData)) { - MA_DR_WAV_ASSERT(MA_FALSE); - break; - } - ma_dr_wav_mulaw_to_s16(pBufferOut, sampleData, (size_t)samplesRead); - #ifdef MA_DR_WAV_LIBSNDFILE_COMPAT - { - if (pWav->container == ma_dr_wav_container_aiff) { - ma_uint64 iSample; - for (iSample = 0; iSample < samplesRead; iSample += 1) { - pBufferOut[iSample] = -pBufferOut[iSample]; - } - } - } - #endif - pBufferOut += samplesRead; - framesToRead -= framesRead; - totalFramesRead += framesRead; - } - return totalFramesRead; -} -MA_API ma_uint64 ma_dr_wav_read_pcm_frames_s16(ma_dr_wav* pWav, ma_uint64 framesToRead, ma_int16* pBufferOut) -{ - if (pWav == NULL || framesToRead == 0) { - return 0; - } - if (pBufferOut == NULL) { - return ma_dr_wav_read_pcm_frames(pWav, framesToRead, NULL); - } - if (framesToRead * pWav->channels * sizeof(ma_int16) > MA_SIZE_MAX) { - framesToRead = MA_SIZE_MAX / sizeof(ma_int16) / pWav->channels; - } - if (pWav->translatedFormatTag == MA_DR_WAVE_FORMAT_PCM) { - return ma_dr_wav_read_pcm_frames_s16__pcm(pWav, framesToRead, pBufferOut); - } - if (pWav->translatedFormatTag == MA_DR_WAVE_FORMAT_IEEE_FLOAT) { - return ma_dr_wav_read_pcm_frames_s16__ieee(pWav, framesToRead, pBufferOut); - } - if (pWav->translatedFormatTag == MA_DR_WAVE_FORMAT_ALAW) { - return ma_dr_wav_read_pcm_frames_s16__alaw(pWav, framesToRead, pBufferOut); - } - if (pWav->translatedFormatTag == MA_DR_WAVE_FORMAT_MULAW) { - return ma_dr_wav_read_pcm_frames_s16__mulaw(pWav, framesToRead, pBufferOut); - } - if (pWav->translatedFormatTag == MA_DR_WAVE_FORMAT_ADPCM) { - return ma_dr_wav_read_pcm_frames_s16__msadpcm(pWav, framesToRead, pBufferOut); - } - if (pWav->translatedFormatTag == MA_DR_WAVE_FORMAT_DVI_ADPCM) { - return ma_dr_wav_read_pcm_frames_s16__ima(pWav, framesToRead, pBufferOut); - } - return 0; -} -MA_API ma_uint64 ma_dr_wav_read_pcm_frames_s16le(ma_dr_wav* pWav, ma_uint64 framesToRead, ma_int16* pBufferOut) -{ - ma_uint64 framesRead = ma_dr_wav_read_pcm_frames_s16(pWav, framesToRead, pBufferOut); - if (pBufferOut != NULL && ma_dr_wav__is_little_endian() == MA_FALSE) { - ma_dr_wav__bswap_samples_s16(pBufferOut, framesRead*pWav->channels); - } - return framesRead; -} -MA_API ma_uint64 ma_dr_wav_read_pcm_frames_s16be(ma_dr_wav* pWav, ma_uint64 framesToRead, ma_int16* pBufferOut) -{ - ma_uint64 framesRead = ma_dr_wav_read_pcm_frames_s16(pWav, framesToRead, pBufferOut); - if (pBufferOut != NULL && ma_dr_wav__is_little_endian() == MA_TRUE) { - ma_dr_wav__bswap_samples_s16(pBufferOut, framesRead*pWav->channels); - } - return framesRead; -} -MA_API void ma_dr_wav_u8_to_s16(ma_int16* pOut, const ma_uint8* pIn, size_t sampleCount) -{ - int r; - size_t i; - for (i = 0; i < sampleCount; ++i) { - int x = pIn[i]; - r = x << 8; - r = r - 32768; - pOut[i] = (short)r; - } -} -MA_API void ma_dr_wav_s24_to_s16(ma_int16* pOut, const ma_uint8* pIn, size_t sampleCount) -{ - int r; - size_t i; - for (i = 0; i < sampleCount; ++i) { - int x = ((int)(((unsigned int)(((const ma_uint8*)pIn)[i*3+0]) << 8) | ((unsigned int)(((const ma_uint8*)pIn)[i*3+1]) << 16) | ((unsigned int)(((const ma_uint8*)pIn)[i*3+2])) << 24)) >> 8; - r = x >> 8; - pOut[i] = (short)r; - } -} -MA_API void ma_dr_wav_s32_to_s16(ma_int16* pOut, const ma_int32* pIn, size_t sampleCount) -{ - int r; - size_t i; - for (i = 0; i < sampleCount; ++i) { - int x = pIn[i]; - r = x >> 16; - pOut[i] = (short)r; - } -} -MA_API void ma_dr_wav_f32_to_s16(ma_int16* pOut, const float* pIn, size_t sampleCount) -{ - int r; - size_t i; - for (i = 0; i < sampleCount; ++i) { - float x = pIn[i]; - float c; - c = ((x < -1) ? -1 : ((x > 1) ? 1 : x)); - c = c + 1; - r = (int)(c * 32767.5f); - r = r - 32768; - pOut[i] = (short)r; - } -} -MA_API void ma_dr_wav_f64_to_s16(ma_int16* pOut, const double* pIn, size_t sampleCount) -{ - int r; - size_t i; - for (i = 0; i < sampleCount; ++i) { - double x = pIn[i]; - double c; - c = ((x < -1) ? -1 : ((x > 1) ? 1 : x)); - c = c + 1; - r = (int)(c * 32767.5); - r = r - 32768; - pOut[i] = (short)r; - } -} -MA_API void ma_dr_wav_alaw_to_s16(ma_int16* pOut, const ma_uint8* pIn, size_t sampleCount) -{ - size_t i; - for (i = 0; i < sampleCount; ++i) { - pOut[i] = ma_dr_wav__alaw_to_s16(pIn[i]); - } -} -MA_API void ma_dr_wav_mulaw_to_s16(ma_int16* pOut, const ma_uint8* pIn, size_t sampleCount) -{ - size_t i; - for (i = 0; i < sampleCount; ++i) { - pOut[i] = ma_dr_wav__mulaw_to_s16(pIn[i]); - } -} -MA_PRIVATE void ma_dr_wav__pcm_to_f32(float* pOut, const ma_uint8* pIn, size_t sampleCount, unsigned int bytesPerSample) -{ - unsigned int i; - if (bytesPerSample == 1) { - ma_dr_wav_u8_to_f32(pOut, pIn, sampleCount); - return; - } - if (bytesPerSample == 2) { - ma_dr_wav_s16_to_f32(pOut, (const ma_int16*)pIn, sampleCount); - return; - } - if (bytesPerSample == 3) { - ma_dr_wav_s24_to_f32(pOut, pIn, sampleCount); - return; - } - if (bytesPerSample == 4) { - ma_dr_wav_s32_to_f32(pOut, (const ma_int32*)pIn, sampleCount); - return; - } - if (bytesPerSample > 8) { - MA_DR_WAV_ZERO_MEMORY(pOut, sampleCount * sizeof(*pOut)); - return; - } - for (i = 0; i < sampleCount; ++i) { - ma_uint64 sample = 0; - unsigned int shift = (8 - bytesPerSample) * 8; - unsigned int j; - for (j = 0; j < bytesPerSample; j += 1) { - MA_DR_WAV_ASSERT(j < 8); - sample |= (ma_uint64)(pIn[j]) << shift; - shift += 8; - } - pIn += j; - *pOut++ = (float)((ma_int64)sample / 9223372036854775807.0); - } -} -MA_PRIVATE void ma_dr_wav__ieee_to_f32(float* pOut, const ma_uint8* pIn, size_t sampleCount, unsigned int bytesPerSample) -{ - if (bytesPerSample == 4) { - unsigned int i; - for (i = 0; i < sampleCount; ++i) { - *pOut++ = ((const float*)pIn)[i]; - } - return; - } else if (bytesPerSample == 8) { - ma_dr_wav_f64_to_f32(pOut, (const double*)pIn, sampleCount); - return; - } else { - MA_DR_WAV_ZERO_MEMORY(pOut, sampleCount * sizeof(*pOut)); - return; - } -} -MA_PRIVATE ma_uint64 ma_dr_wav_read_pcm_frames_f32__pcm(ma_dr_wav* pWav, ma_uint64 framesToRead, float* pBufferOut) -{ - ma_uint64 totalFramesRead; - ma_uint8 sampleData[4096] = {0}; - ma_uint32 bytesPerFrame; - ma_uint32 bytesPerSample; - ma_uint64 samplesRead; - bytesPerFrame = ma_dr_wav_get_bytes_per_pcm_frame(pWav); - if (bytesPerFrame == 0) { - return 0; - } - bytesPerSample = bytesPerFrame / pWav->channels; - if (bytesPerSample == 0 || (bytesPerFrame % pWav->channels) != 0) { - return 0; - } - totalFramesRead = 0; - while (framesToRead > 0) { - ma_uint64 framesToReadThisIteration = ma_dr_wav_min(framesToRead, sizeof(sampleData)/bytesPerFrame); - ma_uint64 framesRead = ma_dr_wav_read_pcm_frames(pWav, framesToReadThisIteration, sampleData); - if (framesRead == 0) { - break; - } - MA_DR_WAV_ASSERT(framesRead <= framesToReadThisIteration); - samplesRead = framesRead * pWav->channels; - if ((samplesRead * bytesPerSample) > sizeof(sampleData)) { - MA_DR_WAV_ASSERT(MA_FALSE); - break; - } - ma_dr_wav__pcm_to_f32(pBufferOut, sampleData, (size_t)samplesRead, bytesPerSample); - pBufferOut += samplesRead; - framesToRead -= framesRead; - totalFramesRead += framesRead; - } - return totalFramesRead; -} -MA_PRIVATE ma_uint64 ma_dr_wav_read_pcm_frames_f32__msadpcm_ima(ma_dr_wav* pWav, ma_uint64 framesToRead, float* pBufferOut) -{ - ma_uint64 totalFramesRead; - ma_int16 samples16[2048]; - totalFramesRead = 0; - while (framesToRead > 0) { - ma_uint64 framesToReadThisIteration = ma_dr_wav_min(framesToRead, ma_dr_wav_countof(samples16)/pWav->channels); - ma_uint64 framesRead = ma_dr_wav_read_pcm_frames_s16(pWav, framesToReadThisIteration, samples16); - if (framesRead == 0) { - break; - } - MA_DR_WAV_ASSERT(framesRead <= framesToReadThisIteration); - ma_dr_wav_s16_to_f32(pBufferOut, samples16, (size_t)(framesRead*pWav->channels)); - pBufferOut += framesRead*pWav->channels; - framesToRead -= framesRead; - totalFramesRead += framesRead; - } - return totalFramesRead; -} -MA_PRIVATE ma_uint64 ma_dr_wav_read_pcm_frames_f32__ieee(ma_dr_wav* pWav, ma_uint64 framesToRead, float* pBufferOut) -{ - ma_uint64 totalFramesRead; - ma_uint8 sampleData[4096] = {0}; - ma_uint32 bytesPerFrame; - ma_uint32 bytesPerSample; - ma_uint64 samplesRead; - if (pWav->translatedFormatTag == MA_DR_WAVE_FORMAT_IEEE_FLOAT && pWav->bitsPerSample == 32) { - return ma_dr_wav_read_pcm_frames(pWav, framesToRead, pBufferOut); - } - bytesPerFrame = ma_dr_wav_get_bytes_per_pcm_frame(pWav); - if (bytesPerFrame == 0) { - return 0; - } - bytesPerSample = bytesPerFrame / pWav->channels; - if (bytesPerSample == 0 || (bytesPerFrame % pWav->channels) != 0) { - return 0; - } - totalFramesRead = 0; - while (framesToRead > 0) { - ma_uint64 framesToReadThisIteration = ma_dr_wav_min(framesToRead, sizeof(sampleData)/bytesPerFrame); - ma_uint64 framesRead = ma_dr_wav_read_pcm_frames(pWav, framesToReadThisIteration, sampleData); - if (framesRead == 0) { - break; - } - MA_DR_WAV_ASSERT(framesRead <= framesToReadThisIteration); - samplesRead = framesRead * pWav->channels; - if ((samplesRead * bytesPerSample) > sizeof(sampleData)) { - MA_DR_WAV_ASSERT(MA_FALSE); - break; - } - ma_dr_wav__ieee_to_f32(pBufferOut, sampleData, (size_t)samplesRead, bytesPerSample); - pBufferOut += samplesRead; - framesToRead -= framesRead; - totalFramesRead += framesRead; - } - return totalFramesRead; -} -MA_PRIVATE ma_uint64 ma_dr_wav_read_pcm_frames_f32__alaw(ma_dr_wav* pWav, ma_uint64 framesToRead, float* pBufferOut) -{ - ma_uint64 totalFramesRead; - ma_uint8 sampleData[4096] = {0}; - ma_uint32 bytesPerFrame; - ma_uint32 bytesPerSample; - ma_uint64 samplesRead; - bytesPerFrame = ma_dr_wav_get_bytes_per_pcm_frame(pWav); - if (bytesPerFrame == 0) { - return 0; - } - bytesPerSample = bytesPerFrame / pWav->channels; - if (bytesPerSample == 0 || (bytesPerFrame % pWav->channels) != 0) { - return 0; - } - totalFramesRead = 0; - while (framesToRead > 0) { - ma_uint64 framesToReadThisIteration = ma_dr_wav_min(framesToRead, sizeof(sampleData)/bytesPerFrame); - ma_uint64 framesRead = ma_dr_wav_read_pcm_frames(pWav, framesToReadThisIteration, sampleData); - if (framesRead == 0) { - break; - } - MA_DR_WAV_ASSERT(framesRead <= framesToReadThisIteration); - samplesRead = framesRead * pWav->channels; - if ((samplesRead * bytesPerSample) > sizeof(sampleData)) { - MA_DR_WAV_ASSERT(MA_FALSE); - break; - } - ma_dr_wav_alaw_to_f32(pBufferOut, sampleData, (size_t)samplesRead); - #ifdef MA_DR_WAV_LIBSNDFILE_COMPAT - { - if (pWav->container == ma_dr_wav_container_aiff) { - ma_uint64 iSample; - for (iSample = 0; iSample < samplesRead; iSample += 1) { - pBufferOut[iSample] = -pBufferOut[iSample]; - } - } - } - #endif - pBufferOut += samplesRead; - framesToRead -= framesRead; - totalFramesRead += framesRead; - } - return totalFramesRead; -} -MA_PRIVATE ma_uint64 ma_dr_wav_read_pcm_frames_f32__mulaw(ma_dr_wav* pWav, ma_uint64 framesToRead, float* pBufferOut) -{ - ma_uint64 totalFramesRead; - ma_uint8 sampleData[4096] = {0}; - ma_uint32 bytesPerFrame; - ma_uint32 bytesPerSample; - ma_uint64 samplesRead; - bytesPerFrame = ma_dr_wav_get_bytes_per_pcm_frame(pWav); - if (bytesPerFrame == 0) { - return 0; - } - bytesPerSample = bytesPerFrame / pWav->channels; - if (bytesPerSample == 0 || (bytesPerFrame % pWav->channels) != 0) { - return 0; - } - totalFramesRead = 0; - while (framesToRead > 0) { - ma_uint64 framesToReadThisIteration = ma_dr_wav_min(framesToRead, sizeof(sampleData)/bytesPerFrame); - ma_uint64 framesRead = ma_dr_wav_read_pcm_frames(pWav, framesToReadThisIteration, sampleData); - if (framesRead == 0) { - break; - } - MA_DR_WAV_ASSERT(framesRead <= framesToReadThisIteration); - samplesRead = framesRead * pWav->channels; - if ((samplesRead * bytesPerSample) > sizeof(sampleData)) { - MA_DR_WAV_ASSERT(MA_FALSE); - break; - } - ma_dr_wav_mulaw_to_f32(pBufferOut, sampleData, (size_t)samplesRead); - #ifdef MA_DR_WAV_LIBSNDFILE_COMPAT - { - if (pWav->container == ma_dr_wav_container_aiff) { - ma_uint64 iSample; - for (iSample = 0; iSample < samplesRead; iSample += 1) { - pBufferOut[iSample] = -pBufferOut[iSample]; - } - } - } - #endif - pBufferOut += samplesRead; - framesToRead -= framesRead; - totalFramesRead += framesRead; - } - return totalFramesRead; -} -MA_API ma_uint64 ma_dr_wav_read_pcm_frames_f32(ma_dr_wav* pWav, ma_uint64 framesToRead, float* pBufferOut) -{ - if (pWav == NULL || framesToRead == 0) { - return 0; - } - if (pBufferOut == NULL) { - return ma_dr_wav_read_pcm_frames(pWav, framesToRead, NULL); - } - if (framesToRead * pWav->channels * sizeof(float) > MA_SIZE_MAX) { - framesToRead = MA_SIZE_MAX / sizeof(float) / pWav->channels; - } - if (pWav->translatedFormatTag == MA_DR_WAVE_FORMAT_PCM) { - return ma_dr_wav_read_pcm_frames_f32__pcm(pWav, framesToRead, pBufferOut); - } - if (pWav->translatedFormatTag == MA_DR_WAVE_FORMAT_ADPCM || pWav->translatedFormatTag == MA_DR_WAVE_FORMAT_DVI_ADPCM) { - return ma_dr_wav_read_pcm_frames_f32__msadpcm_ima(pWav, framesToRead, pBufferOut); - } - if (pWav->translatedFormatTag == MA_DR_WAVE_FORMAT_IEEE_FLOAT) { - return ma_dr_wav_read_pcm_frames_f32__ieee(pWav, framesToRead, pBufferOut); - } - if (pWav->translatedFormatTag == MA_DR_WAVE_FORMAT_ALAW) { - return ma_dr_wav_read_pcm_frames_f32__alaw(pWav, framesToRead, pBufferOut); - } - if (pWav->translatedFormatTag == MA_DR_WAVE_FORMAT_MULAW) { - return ma_dr_wav_read_pcm_frames_f32__mulaw(pWav, framesToRead, pBufferOut); - } - return 0; -} -MA_API ma_uint64 ma_dr_wav_read_pcm_frames_f32le(ma_dr_wav* pWav, ma_uint64 framesToRead, float* pBufferOut) -{ - ma_uint64 framesRead = ma_dr_wav_read_pcm_frames_f32(pWav, framesToRead, pBufferOut); - if (pBufferOut != NULL && ma_dr_wav__is_little_endian() == MA_FALSE) { - ma_dr_wav__bswap_samples_f32(pBufferOut, framesRead*pWav->channels); - } - return framesRead; -} -MA_API ma_uint64 ma_dr_wav_read_pcm_frames_f32be(ma_dr_wav* pWav, ma_uint64 framesToRead, float* pBufferOut) -{ - ma_uint64 framesRead = ma_dr_wav_read_pcm_frames_f32(pWav, framesToRead, pBufferOut); - if (pBufferOut != NULL && ma_dr_wav__is_little_endian() == MA_TRUE) { - ma_dr_wav__bswap_samples_f32(pBufferOut, framesRead*pWav->channels); - } - return framesRead; -} -MA_API void ma_dr_wav_u8_to_f32(float* pOut, const ma_uint8* pIn, size_t sampleCount) -{ - size_t i; - if (pOut == NULL || pIn == NULL) { - return; - } -#ifdef MA_DR_WAV_LIBSNDFILE_COMPAT - for (i = 0; i < sampleCount; ++i) { - *pOut++ = (pIn[i] / 256.0f) * 2 - 1; - } -#else - for (i = 0; i < sampleCount; ++i) { - float x = pIn[i]; - x = x * 0.00784313725490196078f; - x = x - 1; - *pOut++ = x; - } -#endif -} -MA_API void ma_dr_wav_s16_to_f32(float* pOut, const ma_int16* pIn, size_t sampleCount) -{ - size_t i; - if (pOut == NULL || pIn == NULL) { - return; - } - for (i = 0; i < sampleCount; ++i) { - *pOut++ = pIn[i] * 0.000030517578125f; - } -} -MA_API void ma_dr_wav_s24_to_f32(float* pOut, const ma_uint8* pIn, size_t sampleCount) -{ - size_t i; - if (pOut == NULL || pIn == NULL) { - return; - } - for (i = 0; i < sampleCount; ++i) { - double x; - ma_uint32 a = ((ma_uint32)(pIn[i*3+0]) << 8); - ma_uint32 b = ((ma_uint32)(pIn[i*3+1]) << 16); - ma_uint32 c = ((ma_uint32)(pIn[i*3+2]) << 24); - x = (double)((ma_int32)(a | b | c) >> 8); - *pOut++ = (float)(x * 0.00000011920928955078125); - } -} -MA_API void ma_dr_wav_s32_to_f32(float* pOut, const ma_int32* pIn, size_t sampleCount) -{ - size_t i; - if (pOut == NULL || pIn == NULL) { - return; - } - for (i = 0; i < sampleCount; ++i) { - *pOut++ = (float)(pIn[i] / 2147483648.0); - } -} -MA_API void ma_dr_wav_f64_to_f32(float* pOut, const double* pIn, size_t sampleCount) -{ - size_t i; - if (pOut == NULL || pIn == NULL) { - return; - } - for (i = 0; i < sampleCount; ++i) { - *pOut++ = (float)pIn[i]; - } -} -MA_API void ma_dr_wav_alaw_to_f32(float* pOut, const ma_uint8* pIn, size_t sampleCount) -{ - size_t i; - if (pOut == NULL || pIn == NULL) { - return; - } - for (i = 0; i < sampleCount; ++i) { - *pOut++ = ma_dr_wav__alaw_to_s16(pIn[i]) / 32768.0f; - } -} -MA_API void ma_dr_wav_mulaw_to_f32(float* pOut, const ma_uint8* pIn, size_t sampleCount) -{ - size_t i; - if (pOut == NULL || pIn == NULL) { - return; - } - for (i = 0; i < sampleCount; ++i) { - *pOut++ = ma_dr_wav__mulaw_to_s16(pIn[i]) / 32768.0f; - } -} -MA_PRIVATE void ma_dr_wav__pcm_to_s32(ma_int32* pOut, const ma_uint8* pIn, size_t totalSampleCount, unsigned int bytesPerSample) -{ - unsigned int i; - if (bytesPerSample == 1) { - ma_dr_wav_u8_to_s32(pOut, pIn, totalSampleCount); - return; - } - if (bytesPerSample == 2) { - ma_dr_wav_s16_to_s32(pOut, (const ma_int16*)pIn, totalSampleCount); - return; - } - if (bytesPerSample == 3) { - ma_dr_wav_s24_to_s32(pOut, pIn, totalSampleCount); - return; - } - if (bytesPerSample == 4) { - for (i = 0; i < totalSampleCount; ++i) { - *pOut++ = ((const ma_int32*)pIn)[i]; - } - return; - } - if (bytesPerSample > 8) { - MA_DR_WAV_ZERO_MEMORY(pOut, totalSampleCount * sizeof(*pOut)); - return; - } - for (i = 0; i < totalSampleCount; ++i) { - ma_uint64 sample = 0; - unsigned int shift = (8 - bytesPerSample) * 8; - unsigned int j; - for (j = 0; j < bytesPerSample; j += 1) { - MA_DR_WAV_ASSERT(j < 8); - sample |= (ma_uint64)(pIn[j]) << shift; - shift += 8; - } - pIn += j; - *pOut++ = (ma_int32)((ma_int64)sample >> 32); - } -} -MA_PRIVATE void ma_dr_wav__ieee_to_s32(ma_int32* pOut, const ma_uint8* pIn, size_t totalSampleCount, unsigned int bytesPerSample) -{ - if (bytesPerSample == 4) { - ma_dr_wav_f32_to_s32(pOut, (const float*)pIn, totalSampleCount); - return; - } else if (bytesPerSample == 8) { - ma_dr_wav_f64_to_s32(pOut, (const double*)pIn, totalSampleCount); - return; - } else { - MA_DR_WAV_ZERO_MEMORY(pOut, totalSampleCount * sizeof(*pOut)); - return; - } -} -MA_PRIVATE ma_uint64 ma_dr_wav_read_pcm_frames_s32__pcm(ma_dr_wav* pWav, ma_uint64 framesToRead, ma_int32* pBufferOut) -{ - ma_uint64 totalFramesRead; - ma_uint8 sampleData[4096] = {0}; - ma_uint32 bytesPerFrame; - ma_uint32 bytesPerSample; - ma_uint64 samplesRead; - if (pWav->translatedFormatTag == MA_DR_WAVE_FORMAT_PCM && pWav->bitsPerSample == 32) { - return ma_dr_wav_read_pcm_frames(pWav, framesToRead, pBufferOut); - } - bytesPerFrame = ma_dr_wav_get_bytes_per_pcm_frame(pWav); - if (bytesPerFrame == 0) { - return 0; - } - bytesPerSample = bytesPerFrame / pWav->channels; - if (bytesPerSample == 0 || (bytesPerFrame % pWav->channels) != 0) { - return 0; - } - totalFramesRead = 0; - while (framesToRead > 0) { - ma_uint64 framesToReadThisIteration = ma_dr_wav_min(framesToRead, sizeof(sampleData)/bytesPerFrame); - ma_uint64 framesRead = ma_dr_wav_read_pcm_frames(pWav, framesToReadThisIteration, sampleData); - if (framesRead == 0) { - break; - } - MA_DR_WAV_ASSERT(framesRead <= framesToReadThisIteration); - samplesRead = framesRead * pWav->channels; - if ((samplesRead * bytesPerSample) > sizeof(sampleData)) { - MA_DR_WAV_ASSERT(MA_FALSE); - break; - } - ma_dr_wav__pcm_to_s32(pBufferOut, sampleData, (size_t)samplesRead, bytesPerSample); - pBufferOut += samplesRead; - framesToRead -= framesRead; - totalFramesRead += framesRead; - } - return totalFramesRead; -} -MA_PRIVATE ma_uint64 ma_dr_wav_read_pcm_frames_s32__msadpcm_ima(ma_dr_wav* pWav, ma_uint64 framesToRead, ma_int32* pBufferOut) -{ - ma_uint64 totalFramesRead = 0; - ma_int16 samples16[2048]; - while (framesToRead > 0) { - ma_uint64 framesToReadThisIteration = ma_dr_wav_min(framesToRead, ma_dr_wav_countof(samples16)/pWav->channels); - ma_uint64 framesRead = ma_dr_wav_read_pcm_frames_s16(pWav, framesToReadThisIteration, samples16); - if (framesRead == 0) { - break; - } - MA_DR_WAV_ASSERT(framesRead <= framesToReadThisIteration); - ma_dr_wav_s16_to_s32(pBufferOut, samples16, (size_t)(framesRead*pWav->channels)); - pBufferOut += framesRead*pWav->channels; - framesToRead -= framesRead; - totalFramesRead += framesRead; - } - return totalFramesRead; -} -MA_PRIVATE ma_uint64 ma_dr_wav_read_pcm_frames_s32__ieee(ma_dr_wav* pWav, ma_uint64 framesToRead, ma_int32* pBufferOut) -{ - ma_uint64 totalFramesRead; - ma_uint8 sampleData[4096] = {0}; - ma_uint32 bytesPerFrame; - ma_uint32 bytesPerSample; - ma_uint64 samplesRead; - bytesPerFrame = ma_dr_wav_get_bytes_per_pcm_frame(pWav); - if (bytesPerFrame == 0) { - return 0; - } - bytesPerSample = bytesPerFrame / pWav->channels; - if (bytesPerSample == 0 || (bytesPerFrame % pWav->channels) != 0) { - return 0; - } - totalFramesRead = 0; - while (framesToRead > 0) { - ma_uint64 framesToReadThisIteration = ma_dr_wav_min(framesToRead, sizeof(sampleData)/bytesPerFrame); - ma_uint64 framesRead = ma_dr_wav_read_pcm_frames(pWav, framesToReadThisIteration, sampleData); - if (framesRead == 0) { - break; - } - MA_DR_WAV_ASSERT(framesRead <= framesToReadThisIteration); - samplesRead = framesRead * pWav->channels; - if ((samplesRead * bytesPerSample) > sizeof(sampleData)) { - MA_DR_WAV_ASSERT(MA_FALSE); - break; - } - ma_dr_wav__ieee_to_s32(pBufferOut, sampleData, (size_t)samplesRead, bytesPerSample); - pBufferOut += samplesRead; - framesToRead -= framesRead; - totalFramesRead += framesRead; - } - return totalFramesRead; -} -MA_PRIVATE ma_uint64 ma_dr_wav_read_pcm_frames_s32__alaw(ma_dr_wav* pWav, ma_uint64 framesToRead, ma_int32* pBufferOut) -{ - ma_uint64 totalFramesRead; - ma_uint8 sampleData[4096] = {0}; - ma_uint32 bytesPerFrame; - ma_uint32 bytesPerSample; - ma_uint64 samplesRead; - bytesPerFrame = ma_dr_wav_get_bytes_per_pcm_frame(pWav); - if (bytesPerFrame == 0) { - return 0; - } - bytesPerSample = bytesPerFrame / pWav->channels; - if (bytesPerSample == 0 || (bytesPerFrame % pWav->channels) != 0) { - return 0; - } - totalFramesRead = 0; - while (framesToRead > 0) { - ma_uint64 framesToReadThisIteration = ma_dr_wav_min(framesToRead, sizeof(sampleData)/bytesPerFrame); - ma_uint64 framesRead = ma_dr_wav_read_pcm_frames(pWav, framesToReadThisIteration, sampleData); - if (framesRead == 0) { - break; - } - MA_DR_WAV_ASSERT(framesRead <= framesToReadThisIteration); - samplesRead = framesRead * pWav->channels; - if ((samplesRead * bytesPerSample) > sizeof(sampleData)) { - MA_DR_WAV_ASSERT(MA_FALSE); - break; - } - ma_dr_wav_alaw_to_s32(pBufferOut, sampleData, (size_t)samplesRead); - #ifdef MA_DR_WAV_LIBSNDFILE_COMPAT - { - if (pWav->container == ma_dr_wav_container_aiff) { - ma_uint64 iSample; - for (iSample = 0; iSample < samplesRead; iSample += 1) { - pBufferOut[iSample] = -pBufferOut[iSample]; - } - } - } - #endif - pBufferOut += samplesRead; - framesToRead -= framesRead; - totalFramesRead += framesRead; - } - return totalFramesRead; -} -MA_PRIVATE ma_uint64 ma_dr_wav_read_pcm_frames_s32__mulaw(ma_dr_wav* pWav, ma_uint64 framesToRead, ma_int32* pBufferOut) -{ - ma_uint64 totalFramesRead; - ma_uint8 sampleData[4096] = {0}; - ma_uint32 bytesPerFrame; - ma_uint32 bytesPerSample; - ma_uint64 samplesRead; - bytesPerFrame = ma_dr_wav_get_bytes_per_pcm_frame(pWav); - if (bytesPerFrame == 0) { - return 0; - } - bytesPerSample = bytesPerFrame / pWav->channels; - if (bytesPerSample == 0 || (bytesPerFrame % pWav->channels) != 0) { - return 0; - } - totalFramesRead = 0; - while (framesToRead > 0) { - ma_uint64 framesToReadThisIteration = ma_dr_wav_min(framesToRead, sizeof(sampleData)/bytesPerFrame); - ma_uint64 framesRead = ma_dr_wav_read_pcm_frames(pWav, framesToReadThisIteration, sampleData); - if (framesRead == 0) { - break; - } - MA_DR_WAV_ASSERT(framesRead <= framesToReadThisIteration); - samplesRead = framesRead * pWav->channels; - if ((samplesRead * bytesPerSample) > sizeof(sampleData)) { - MA_DR_WAV_ASSERT(MA_FALSE); - break; - } - ma_dr_wav_mulaw_to_s32(pBufferOut, sampleData, (size_t)samplesRead); - #ifdef MA_DR_WAV_LIBSNDFILE_COMPAT - { - if (pWav->container == ma_dr_wav_container_aiff) { - ma_uint64 iSample; - for (iSample = 0; iSample < samplesRead; iSample += 1) { - pBufferOut[iSample] = -pBufferOut[iSample]; - } - } - } - #endif - pBufferOut += samplesRead; - framesToRead -= framesRead; - totalFramesRead += framesRead; - } - return totalFramesRead; -} -MA_API ma_uint64 ma_dr_wav_read_pcm_frames_s32(ma_dr_wav* pWav, ma_uint64 framesToRead, ma_int32* pBufferOut) -{ - if (pWav == NULL || framesToRead == 0) { - return 0; - } - if (pBufferOut == NULL) { - return ma_dr_wav_read_pcm_frames(pWav, framesToRead, NULL); - } - if (framesToRead * pWav->channels * sizeof(ma_int32) > MA_SIZE_MAX) { - framesToRead = MA_SIZE_MAX / sizeof(ma_int32) / pWav->channels; - } - if (pWav->translatedFormatTag == MA_DR_WAVE_FORMAT_PCM) { - return ma_dr_wav_read_pcm_frames_s32__pcm(pWav, framesToRead, pBufferOut); - } - if (pWav->translatedFormatTag == MA_DR_WAVE_FORMAT_ADPCM || pWav->translatedFormatTag == MA_DR_WAVE_FORMAT_DVI_ADPCM) { - return ma_dr_wav_read_pcm_frames_s32__msadpcm_ima(pWav, framesToRead, pBufferOut); - } - if (pWav->translatedFormatTag == MA_DR_WAVE_FORMAT_IEEE_FLOAT) { - return ma_dr_wav_read_pcm_frames_s32__ieee(pWav, framesToRead, pBufferOut); - } - if (pWav->translatedFormatTag == MA_DR_WAVE_FORMAT_ALAW) { - return ma_dr_wav_read_pcm_frames_s32__alaw(pWav, framesToRead, pBufferOut); - } - if (pWav->translatedFormatTag == MA_DR_WAVE_FORMAT_MULAW) { - return ma_dr_wav_read_pcm_frames_s32__mulaw(pWav, framesToRead, pBufferOut); - } - return 0; -} -MA_API ma_uint64 ma_dr_wav_read_pcm_frames_s32le(ma_dr_wav* pWav, ma_uint64 framesToRead, ma_int32* pBufferOut) -{ - ma_uint64 framesRead = ma_dr_wav_read_pcm_frames_s32(pWav, framesToRead, pBufferOut); - if (pBufferOut != NULL && ma_dr_wav__is_little_endian() == MA_FALSE) { - ma_dr_wav__bswap_samples_s32(pBufferOut, framesRead*pWav->channels); - } - return framesRead; -} -MA_API ma_uint64 ma_dr_wav_read_pcm_frames_s32be(ma_dr_wav* pWav, ma_uint64 framesToRead, ma_int32* pBufferOut) -{ - ma_uint64 framesRead = ma_dr_wav_read_pcm_frames_s32(pWav, framesToRead, pBufferOut); - if (pBufferOut != NULL && ma_dr_wav__is_little_endian() == MA_TRUE) { - ma_dr_wav__bswap_samples_s32(pBufferOut, framesRead*pWav->channels); - } - return framesRead; -} -MA_API void ma_dr_wav_u8_to_s32(ma_int32* pOut, const ma_uint8* pIn, size_t sampleCount) -{ - size_t i; - if (pOut == NULL || pIn == NULL) { - return; - } - for (i = 0; i < sampleCount; ++i) { - *pOut++ = ((int)pIn[i] - 128) << 24; - } -} -MA_API void ma_dr_wav_s16_to_s32(ma_int32* pOut, const ma_int16* pIn, size_t sampleCount) -{ - size_t i; - if (pOut == NULL || pIn == NULL) { - return; - } - for (i = 0; i < sampleCount; ++i) { - *pOut++ = pIn[i] << 16; - } -} -MA_API void ma_dr_wav_s24_to_s32(ma_int32* pOut, const ma_uint8* pIn, size_t sampleCount) -{ - size_t i; - if (pOut == NULL || pIn == NULL) { - return; - } - for (i = 0; i < sampleCount; ++i) { - unsigned int s0 = pIn[i*3 + 0]; - unsigned int s1 = pIn[i*3 + 1]; - unsigned int s2 = pIn[i*3 + 2]; - ma_int32 sample32 = (ma_int32)((s0 << 8) | (s1 << 16) | (s2 << 24)); - *pOut++ = sample32; - } -} -MA_API void ma_dr_wav_f32_to_s32(ma_int32* pOut, const float* pIn, size_t sampleCount) -{ - size_t i; - if (pOut == NULL || pIn == NULL) { - return; - } - for (i = 0; i < sampleCount; ++i) { - *pOut++ = (ma_int32)(2147483648.0f * pIn[i]); - } -} -MA_API void ma_dr_wav_f64_to_s32(ma_int32* pOut, const double* pIn, size_t sampleCount) -{ - size_t i; - if (pOut == NULL || pIn == NULL) { - return; - } - for (i = 0; i < sampleCount; ++i) { - *pOut++ = (ma_int32)(2147483648.0 * pIn[i]); - } -} -MA_API void ma_dr_wav_alaw_to_s32(ma_int32* pOut, const ma_uint8* pIn, size_t sampleCount) -{ - size_t i; - if (pOut == NULL || pIn == NULL) { - return; - } - for (i = 0; i < sampleCount; ++i) { - *pOut++ = ((ma_int32)ma_dr_wav__alaw_to_s16(pIn[i])) << 16; - } -} -MA_API void ma_dr_wav_mulaw_to_s32(ma_int32* pOut, const ma_uint8* pIn, size_t sampleCount) -{ - size_t i; - if (pOut == NULL || pIn == NULL) { - return; - } - for (i= 0; i < sampleCount; ++i) { - *pOut++ = ((ma_int32)ma_dr_wav__mulaw_to_s16(pIn[i])) << 16; - } -} -MA_PRIVATE ma_int16* ma_dr_wav__read_pcm_frames_and_close_s16(ma_dr_wav* pWav, unsigned int* channels, unsigned int* sampleRate, ma_uint64* totalFrameCount) -{ - ma_uint64 sampleDataSize; - ma_int16* pSampleData; - ma_uint64 framesRead; - MA_DR_WAV_ASSERT(pWav != NULL); - sampleDataSize = pWav->totalPCMFrameCount * pWav->channels * sizeof(ma_int16); - if (sampleDataSize > MA_SIZE_MAX) { - ma_dr_wav_uninit(pWav); - return NULL; - } - pSampleData = (ma_int16*)ma_dr_wav__malloc_from_callbacks((size_t)sampleDataSize, &pWav->allocationCallbacks); - if (pSampleData == NULL) { - ma_dr_wav_uninit(pWav); - return NULL; - } - framesRead = ma_dr_wav_read_pcm_frames_s16(pWav, (size_t)pWav->totalPCMFrameCount, pSampleData); - if (framesRead != pWav->totalPCMFrameCount) { - ma_dr_wav__free_from_callbacks(pSampleData, &pWav->allocationCallbacks); - ma_dr_wav_uninit(pWav); - return NULL; - } - ma_dr_wav_uninit(pWav); - if (sampleRate) { - *sampleRate = pWav->sampleRate; - } - if (channels) { - *channels = pWav->channels; - } - if (totalFrameCount) { - *totalFrameCount = pWav->totalPCMFrameCount; - } - return pSampleData; -} -MA_PRIVATE float* ma_dr_wav__read_pcm_frames_and_close_f32(ma_dr_wav* pWav, unsigned int* channels, unsigned int* sampleRate, ma_uint64* totalFrameCount) -{ - ma_uint64 sampleDataSize; - float* pSampleData; - ma_uint64 framesRead; - MA_DR_WAV_ASSERT(pWav != NULL); - sampleDataSize = pWav->totalPCMFrameCount * pWav->channels * sizeof(float); - if (sampleDataSize > MA_SIZE_MAX) { - ma_dr_wav_uninit(pWav); - return NULL; - } - pSampleData = (float*)ma_dr_wav__malloc_from_callbacks((size_t)sampleDataSize, &pWav->allocationCallbacks); - if (pSampleData == NULL) { - ma_dr_wav_uninit(pWav); - return NULL; - } - framesRead = ma_dr_wav_read_pcm_frames_f32(pWav, (size_t)pWav->totalPCMFrameCount, pSampleData); - if (framesRead != pWav->totalPCMFrameCount) { - ma_dr_wav__free_from_callbacks(pSampleData, &pWav->allocationCallbacks); - ma_dr_wav_uninit(pWav); - return NULL; - } - ma_dr_wav_uninit(pWav); - if (sampleRate) { - *sampleRate = pWav->sampleRate; - } - if (channels) { - *channels = pWav->channels; - } - if (totalFrameCount) { - *totalFrameCount = pWav->totalPCMFrameCount; - } - return pSampleData; -} -MA_PRIVATE ma_int32* ma_dr_wav__read_pcm_frames_and_close_s32(ma_dr_wav* pWav, unsigned int* channels, unsigned int* sampleRate, ma_uint64* totalFrameCount) -{ - ma_uint64 sampleDataSize; - ma_int32* pSampleData; - ma_uint64 framesRead; - MA_DR_WAV_ASSERT(pWav != NULL); - sampleDataSize = pWav->totalPCMFrameCount * pWav->channels * sizeof(ma_int32); - if (sampleDataSize > MA_SIZE_MAX) { - ma_dr_wav_uninit(pWav); - return NULL; - } - pSampleData = (ma_int32*)ma_dr_wav__malloc_from_callbacks((size_t)sampleDataSize, &pWav->allocationCallbacks); - if (pSampleData == NULL) { - ma_dr_wav_uninit(pWav); - return NULL; - } - framesRead = ma_dr_wav_read_pcm_frames_s32(pWav, (size_t)pWav->totalPCMFrameCount, pSampleData); - if (framesRead != pWav->totalPCMFrameCount) { - ma_dr_wav__free_from_callbacks(pSampleData, &pWav->allocationCallbacks); - ma_dr_wav_uninit(pWav); - return NULL; - } - ma_dr_wav_uninit(pWav); - if (sampleRate) { - *sampleRate = pWav->sampleRate; - } - if (channels) { - *channels = pWav->channels; - } - if (totalFrameCount) { - *totalFrameCount = pWav->totalPCMFrameCount; - } - return pSampleData; -} -MA_API ma_int16* ma_dr_wav_open_and_read_pcm_frames_s16(ma_dr_wav_read_proc onRead, ma_dr_wav_seek_proc onSeek, void* pUserData, unsigned int* channelsOut, unsigned int* sampleRateOut, ma_uint64* totalFrameCountOut, const ma_allocation_callbacks* pAllocationCallbacks) -{ - ma_dr_wav wav; - if (channelsOut) { - *channelsOut = 0; - } - if (sampleRateOut) { - *sampleRateOut = 0; - } - if (totalFrameCountOut) { - *totalFrameCountOut = 0; - } - if (!ma_dr_wav_init(&wav, onRead, onSeek, pUserData, pAllocationCallbacks)) { - return NULL; - } - return ma_dr_wav__read_pcm_frames_and_close_s16(&wav, channelsOut, sampleRateOut, totalFrameCountOut); -} -MA_API float* ma_dr_wav_open_and_read_pcm_frames_f32(ma_dr_wav_read_proc onRead, ma_dr_wav_seek_proc onSeek, void* pUserData, unsigned int* channelsOut, unsigned int* sampleRateOut, ma_uint64* totalFrameCountOut, const ma_allocation_callbacks* pAllocationCallbacks) -{ - ma_dr_wav wav; - if (channelsOut) { - *channelsOut = 0; - } - if (sampleRateOut) { - *sampleRateOut = 0; - } - if (totalFrameCountOut) { - *totalFrameCountOut = 0; - } - if (!ma_dr_wav_init(&wav, onRead, onSeek, pUserData, pAllocationCallbacks)) { - return NULL; - } - return ma_dr_wav__read_pcm_frames_and_close_f32(&wav, channelsOut, sampleRateOut, totalFrameCountOut); -} -MA_API ma_int32* ma_dr_wav_open_and_read_pcm_frames_s32(ma_dr_wav_read_proc onRead, ma_dr_wav_seek_proc onSeek, void* pUserData, unsigned int* channelsOut, unsigned int* sampleRateOut, ma_uint64* totalFrameCountOut, const ma_allocation_callbacks* pAllocationCallbacks) -{ - ma_dr_wav wav; - if (channelsOut) { - *channelsOut = 0; - } - if (sampleRateOut) { - *sampleRateOut = 0; - } - if (totalFrameCountOut) { - *totalFrameCountOut = 0; - } - if (!ma_dr_wav_init(&wav, onRead, onSeek, pUserData, pAllocationCallbacks)) { - return NULL; - } - return ma_dr_wav__read_pcm_frames_and_close_s32(&wav, channelsOut, sampleRateOut, totalFrameCountOut); -} -#ifndef MA_DR_WAV_NO_STDIO -MA_API ma_int16* ma_dr_wav_open_file_and_read_pcm_frames_s16(const char* filename, unsigned int* channelsOut, unsigned int* sampleRateOut, ma_uint64* totalFrameCountOut, const ma_allocation_callbacks* pAllocationCallbacks) -{ - ma_dr_wav wav; - if (channelsOut) { - *channelsOut = 0; - } - if (sampleRateOut) { - *sampleRateOut = 0; - } - if (totalFrameCountOut) { - *totalFrameCountOut = 0; - } - if (!ma_dr_wav_init_file(&wav, filename, pAllocationCallbacks)) { - return NULL; - } - return ma_dr_wav__read_pcm_frames_and_close_s16(&wav, channelsOut, sampleRateOut, totalFrameCountOut); -} -MA_API float* ma_dr_wav_open_file_and_read_pcm_frames_f32(const char* filename, unsigned int* channelsOut, unsigned int* sampleRateOut, ma_uint64* totalFrameCountOut, const ma_allocation_callbacks* pAllocationCallbacks) -{ - ma_dr_wav wav; - if (channelsOut) { - *channelsOut = 0; - } - if (sampleRateOut) { - *sampleRateOut = 0; - } - if (totalFrameCountOut) { - *totalFrameCountOut = 0; - } - if (!ma_dr_wav_init_file(&wav, filename, pAllocationCallbacks)) { - return NULL; - } - return ma_dr_wav__read_pcm_frames_and_close_f32(&wav, channelsOut, sampleRateOut, totalFrameCountOut); -} -MA_API ma_int32* ma_dr_wav_open_file_and_read_pcm_frames_s32(const char* filename, unsigned int* channelsOut, unsigned int* sampleRateOut, ma_uint64* totalFrameCountOut, const ma_allocation_callbacks* pAllocationCallbacks) -{ - ma_dr_wav wav; - if (channelsOut) { - *channelsOut = 0; - } - if (sampleRateOut) { - *sampleRateOut = 0; - } - if (totalFrameCountOut) { - *totalFrameCountOut = 0; - } - if (!ma_dr_wav_init_file(&wav, filename, pAllocationCallbacks)) { - return NULL; - } - return ma_dr_wav__read_pcm_frames_and_close_s32(&wav, channelsOut, sampleRateOut, totalFrameCountOut); -} -#ifndef MA_DR_WAV_NO_WCHAR -MA_API ma_int16* ma_dr_wav_open_file_and_read_pcm_frames_s16_w(const wchar_t* filename, unsigned int* channelsOut, unsigned int* sampleRateOut, ma_uint64* totalFrameCountOut, const ma_allocation_callbacks* pAllocationCallbacks) -{ - ma_dr_wav wav; - if (sampleRateOut) { - *sampleRateOut = 0; - } - if (channelsOut) { - *channelsOut = 0; - } - if (totalFrameCountOut) { - *totalFrameCountOut = 0; - } - if (!ma_dr_wav_init_file_w(&wav, filename, pAllocationCallbacks)) { - return NULL; - } - return ma_dr_wav__read_pcm_frames_and_close_s16(&wav, channelsOut, sampleRateOut, totalFrameCountOut); -} -MA_API float* ma_dr_wav_open_file_and_read_pcm_frames_f32_w(const wchar_t* filename, unsigned int* channelsOut, unsigned int* sampleRateOut, ma_uint64* totalFrameCountOut, const ma_allocation_callbacks* pAllocationCallbacks) -{ - ma_dr_wav wav; - if (sampleRateOut) { - *sampleRateOut = 0; - } - if (channelsOut) { - *channelsOut = 0; - } - if (totalFrameCountOut) { - *totalFrameCountOut = 0; - } - if (!ma_dr_wav_init_file_w(&wav, filename, pAllocationCallbacks)) { - return NULL; - } - return ma_dr_wav__read_pcm_frames_and_close_f32(&wav, channelsOut, sampleRateOut, totalFrameCountOut); -} -MA_API ma_int32* ma_dr_wav_open_file_and_read_pcm_frames_s32_w(const wchar_t* filename, unsigned int* channelsOut, unsigned int* sampleRateOut, ma_uint64* totalFrameCountOut, const ma_allocation_callbacks* pAllocationCallbacks) -{ - ma_dr_wav wav; - if (sampleRateOut) { - *sampleRateOut = 0; - } - if (channelsOut) { - *channelsOut = 0; - } - if (totalFrameCountOut) { - *totalFrameCountOut = 0; - } - if (!ma_dr_wav_init_file_w(&wav, filename, pAllocationCallbacks)) { - return NULL; - } - return ma_dr_wav__read_pcm_frames_and_close_s32(&wav, channelsOut, sampleRateOut, totalFrameCountOut); -} -#endif -#endif -MA_API ma_int16* ma_dr_wav_open_memory_and_read_pcm_frames_s16(const void* data, size_t dataSize, unsigned int* channelsOut, unsigned int* sampleRateOut, ma_uint64* totalFrameCountOut, const ma_allocation_callbacks* pAllocationCallbacks) -{ - ma_dr_wav wav; - if (channelsOut) { - *channelsOut = 0; - } - if (sampleRateOut) { - *sampleRateOut = 0; - } - if (totalFrameCountOut) { - *totalFrameCountOut = 0; - } - if (!ma_dr_wav_init_memory(&wav, data, dataSize, pAllocationCallbacks)) { - return NULL; - } - return ma_dr_wav__read_pcm_frames_and_close_s16(&wav, channelsOut, sampleRateOut, totalFrameCountOut); -} -MA_API float* ma_dr_wav_open_memory_and_read_pcm_frames_f32(const void* data, size_t dataSize, unsigned int* channelsOut, unsigned int* sampleRateOut, ma_uint64* totalFrameCountOut, const ma_allocation_callbacks* pAllocationCallbacks) -{ - ma_dr_wav wav; - if (channelsOut) { - *channelsOut = 0; - } - if (sampleRateOut) { - *sampleRateOut = 0; - } - if (totalFrameCountOut) { - *totalFrameCountOut = 0; - } - if (!ma_dr_wav_init_memory(&wav, data, dataSize, pAllocationCallbacks)) { - return NULL; - } - return ma_dr_wav__read_pcm_frames_and_close_f32(&wav, channelsOut, sampleRateOut, totalFrameCountOut); -} -MA_API ma_int32* ma_dr_wav_open_memory_and_read_pcm_frames_s32(const void* data, size_t dataSize, unsigned int* channelsOut, unsigned int* sampleRateOut, ma_uint64* totalFrameCountOut, const ma_allocation_callbacks* pAllocationCallbacks) -{ - ma_dr_wav wav; - if (channelsOut) { - *channelsOut = 0; - } - if (sampleRateOut) { - *sampleRateOut = 0; - } - if (totalFrameCountOut) { - *totalFrameCountOut = 0; - } - if (!ma_dr_wav_init_memory(&wav, data, dataSize, pAllocationCallbacks)) { - return NULL; - } - return ma_dr_wav__read_pcm_frames_and_close_s32(&wav, channelsOut, sampleRateOut, totalFrameCountOut); -} -#endif -MA_API void ma_dr_wav_free(void* p, const ma_allocation_callbacks* pAllocationCallbacks) -{ - if (pAllocationCallbacks != NULL) { - ma_dr_wav__free_from_callbacks(p, pAllocationCallbacks); - } else { - ma_dr_wav__free_default(p, NULL); - } -} -MA_API ma_uint16 ma_dr_wav_bytes_to_u16(const ma_uint8* data) -{ - return ((ma_uint16)data[0] << 0) | ((ma_uint16)data[1] << 8); -} -MA_API ma_int16 ma_dr_wav_bytes_to_s16(const ma_uint8* data) -{ - return (ma_int16)ma_dr_wav_bytes_to_u16(data); -} -MA_API ma_uint32 ma_dr_wav_bytes_to_u32(const ma_uint8* data) -{ - return ma_dr_wav_bytes_to_u32_le(data); -} -MA_API float ma_dr_wav_bytes_to_f32(const ma_uint8* data) -{ - union { - ma_uint32 u32; - float f32; - } value; - value.u32 = ma_dr_wav_bytes_to_u32(data); - return value.f32; -} -MA_API ma_int32 ma_dr_wav_bytes_to_s32(const ma_uint8* data) -{ - return (ma_int32)ma_dr_wav_bytes_to_u32(data); -} -MA_API ma_uint64 ma_dr_wav_bytes_to_u64(const ma_uint8* data) -{ - return - ((ma_uint64)data[0] << 0) | ((ma_uint64)data[1] << 8) | ((ma_uint64)data[2] << 16) | ((ma_uint64)data[3] << 24) | - ((ma_uint64)data[4] << 32) | ((ma_uint64)data[5] << 40) | ((ma_uint64)data[6] << 48) | ((ma_uint64)data[7] << 56); -} -MA_API ma_int64 ma_dr_wav_bytes_to_s64(const ma_uint8* data) -{ - return (ma_int64)ma_dr_wav_bytes_to_u64(data); -} -MA_API ma_bool32 ma_dr_wav_guid_equal(const ma_uint8 a[16], const ma_uint8 b[16]) -{ - int i; - for (i = 0; i < 16; i += 1) { - if (a[i] != b[i]) { - return MA_FALSE; - } - } - return MA_TRUE; -} -MA_API ma_bool32 ma_dr_wav_fourcc_equal(const ma_uint8* a, const char* b) -{ - return - a[0] == b[0] && - a[1] == b[1] && - a[2] == b[2] && - a[3] == b[3]; -} -#ifdef __MRC__ -#pragma options opt reset -#endif -#endif -/* dr_wav_c end */ -#endif /* MA_DR_WAV_IMPLEMENTATION */ -#endif /* MA_NO_WAV */ - -#if !defined(MA_NO_FLAC) && !defined(MA_NO_DECODING) -#if !defined(MA_DR_FLAC_IMPLEMENTATION) -/* dr_flac_c begin */ -#ifndef ma_dr_flac_c -#define ma_dr_flac_c -#if defined(__clang__) || (defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6))) - #pragma GCC diagnostic push - #if __GNUC__ >= 7 - #pragma GCC diagnostic ignored "-Wimplicit-fallthrough" - #endif -#endif -#ifdef __linux__ - #ifndef _BSD_SOURCE - #define _BSD_SOURCE - #endif - #ifndef _DEFAULT_SOURCE - #define _DEFAULT_SOURCE - #endif - #ifndef __USE_BSD - #define __USE_BSD - #endif - #include -#endif -#include -#include -#if !defined(MA_DR_FLAC_NO_SIMD) - #if defined(MA_X64) || defined(MA_X86) - #if defined(_MSC_VER) && !defined(__clang__) - #if _MSC_VER >= 1400 && !defined(MA_DR_FLAC_NO_SSE2) - #define MA_DR_FLAC_SUPPORT_SSE2 - #endif - #if _MSC_VER >= 1600 && !defined(MA_DR_FLAC_NO_SSE41) - #define MA_DR_FLAC_SUPPORT_SSE41 - #endif - #elif defined(__clang__) || (defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 3))) - #if defined(__SSE2__) && !defined(MA_DR_FLAC_NO_SSE2) - #define MA_DR_FLAC_SUPPORT_SSE2 - #endif - #if defined(__SSE4_1__) && !defined(MA_DR_FLAC_NO_SSE41) - #define MA_DR_FLAC_SUPPORT_SSE41 - #endif - #endif - #if !defined(__GNUC__) && !defined(__clang__) && defined(__has_include) - #if !defined(MA_DR_FLAC_SUPPORT_SSE2) && !defined(MA_DR_FLAC_NO_SSE2) && __has_include() - #define MA_DR_FLAC_SUPPORT_SSE2 - #endif - #if !defined(MA_DR_FLAC_SUPPORT_SSE41) && !defined(MA_DR_FLAC_NO_SSE41) && __has_include() - #define MA_DR_FLAC_SUPPORT_SSE41 - #endif - #endif - #if defined(MA_DR_FLAC_SUPPORT_SSE41) - #include - #elif defined(MA_DR_FLAC_SUPPORT_SSE2) - #include - #endif - #endif - #if defined(MA_ARM) - #if !defined(MA_DR_FLAC_NO_NEON) && (defined(__ARM_NEON) || defined(__aarch64__) || defined(_M_ARM64)) - #define MA_DR_FLAC_SUPPORT_NEON - #include - #endif - #endif -#endif -#if !defined(MA_DR_FLAC_NO_SIMD) && (defined(MA_X86) || defined(MA_X64)) - #if defined(_MSC_VER) && !defined(__clang__) - #if _MSC_VER >= 1400 - #include - static void ma_dr_flac__cpuid(int info[4], int fid) - { - __cpuid(info, fid); - } - #else - #define MA_DR_FLAC_NO_CPUID - #endif - #else - #if defined(__GNUC__) || defined(__clang__) - static void ma_dr_flac__cpuid(int info[4], int fid) - { - #if defined(MA_X86) && defined(__PIC__) - __asm__ __volatile__ ( - "xchg{l} {%%}ebx, %k1;" - "cpuid;" - "xchg{l} {%%}ebx, %k1;" - : "=a"(info[0]), "=&r"(info[1]), "=c"(info[2]), "=d"(info[3]) : "a"(fid), "c"(0) - ); - #else - __asm__ __volatile__ ( - "cpuid" : "=a"(info[0]), "=b"(info[1]), "=c"(info[2]), "=d"(info[3]) : "a"(fid), "c"(0) - ); - #endif - } - #else - #define MA_DR_FLAC_NO_CPUID - #endif - #endif -#else - #define MA_DR_FLAC_NO_CPUID -#endif -static MA_INLINE ma_bool32 ma_dr_flac_has_sse2(void) -{ -#if defined(MA_DR_FLAC_SUPPORT_SSE2) - #if (defined(MA_X64) || defined(MA_X86)) && !defined(MA_DR_FLAC_NO_SSE2) - #if defined(MA_X64) - return MA_TRUE; - #elif (defined(_M_IX86_FP) && _M_IX86_FP == 2) || defined(__SSE2__) - return MA_TRUE; - #else - #if defined(MA_DR_FLAC_NO_CPUID) - return MA_FALSE; - #else - int info[4]; - ma_dr_flac__cpuid(info, 1); - return (info[3] & (1 << 26)) != 0; - #endif - #endif - #else - return MA_FALSE; - #endif -#else - return MA_FALSE; -#endif -} -static MA_INLINE ma_bool32 ma_dr_flac_has_sse41(void) -{ -#if defined(MA_DR_FLAC_SUPPORT_SSE41) - #if (defined(MA_X64) || defined(MA_X86)) && !defined(MA_DR_FLAC_NO_SSE41) - #if defined(__SSE4_1__) || defined(__AVX__) - return MA_TRUE; - #else - #if defined(MA_DR_FLAC_NO_CPUID) - return MA_FALSE; - #else - int info[4]; - ma_dr_flac__cpuid(info, 1); - return (info[2] & (1 << 19)) != 0; - #endif - #endif - #else - return MA_FALSE; - #endif -#else - return MA_FALSE; -#endif -} -#if defined(_MSC_VER) && _MSC_VER >= 1500 && (defined(MA_X86) || defined(MA_X64)) && !defined(__clang__) - #define MA_DR_FLAC_HAS_LZCNT_INTRINSIC -#elif (defined(__GNUC__) && ((__GNUC__ > 4) || (__GNUC__ == 4 && __GNUC_MINOR__ >= 7))) - #define MA_DR_FLAC_HAS_LZCNT_INTRINSIC -#elif defined(__clang__) - #if defined(__has_builtin) - #if __has_builtin(__builtin_clzll) || __has_builtin(__builtin_clzl) - #define MA_DR_FLAC_HAS_LZCNT_INTRINSIC - #endif - #endif -#endif -#if defined(_MSC_VER) && _MSC_VER >= 1400 && !defined(__clang__) - #define MA_DR_FLAC_HAS_BYTESWAP16_INTRINSIC - #define MA_DR_FLAC_HAS_BYTESWAP32_INTRINSIC - #define MA_DR_FLAC_HAS_BYTESWAP64_INTRINSIC -#elif defined(__clang__) - #if defined(__has_builtin) - #if __has_builtin(__builtin_bswap16) - #define MA_DR_FLAC_HAS_BYTESWAP16_INTRINSIC - #endif - #if __has_builtin(__builtin_bswap32) - #define MA_DR_FLAC_HAS_BYTESWAP32_INTRINSIC - #endif - #if __has_builtin(__builtin_bswap64) - #define MA_DR_FLAC_HAS_BYTESWAP64_INTRINSIC - #endif - #endif -#elif defined(__GNUC__) - #if ((__GNUC__ > 4) || (__GNUC__ == 4 && __GNUC_MINOR__ >= 3)) - #define MA_DR_FLAC_HAS_BYTESWAP32_INTRINSIC - #define MA_DR_FLAC_HAS_BYTESWAP64_INTRINSIC - #endif - #if ((__GNUC__ > 4) || (__GNUC__ == 4 && __GNUC_MINOR__ >= 8)) - #define MA_DR_FLAC_HAS_BYTESWAP16_INTRINSIC - #endif -#elif defined(__WATCOMC__) && defined(__386__) - #define MA_DR_FLAC_HAS_BYTESWAP16_INTRINSIC - #define MA_DR_FLAC_HAS_BYTESWAP32_INTRINSIC - #define MA_DR_FLAC_HAS_BYTESWAP64_INTRINSIC - extern __inline ma_uint16 _watcom_bswap16(ma_uint16); - extern __inline ma_uint32 _watcom_bswap32(ma_uint32); - extern __inline ma_uint64 _watcom_bswap64(ma_uint64); -#pragma aux _watcom_bswap16 = \ - "xchg al, ah" \ - parm [ax] \ - value [ax] \ - modify nomemory; -#pragma aux _watcom_bswap32 = \ - "bswap eax" \ - parm [eax] \ - value [eax] \ - modify nomemory; -#pragma aux _watcom_bswap64 = \ - "bswap eax" \ - "bswap edx" \ - "xchg eax,edx" \ - parm [eax edx] \ - value [eax edx] \ - modify nomemory; -#endif -#ifndef MA_DR_FLAC_ASSERT -#include -#define MA_DR_FLAC_ASSERT(expression) assert(expression) -#endif -#ifndef MA_DR_FLAC_MALLOC -#define MA_DR_FLAC_MALLOC(sz) malloc((sz)) -#endif -#ifndef MA_DR_FLAC_REALLOC -#define MA_DR_FLAC_REALLOC(p, sz) realloc((p), (sz)) -#endif -#ifndef MA_DR_FLAC_FREE -#define MA_DR_FLAC_FREE(p) free((p)) -#endif -#ifndef MA_DR_FLAC_COPY_MEMORY -#define MA_DR_FLAC_COPY_MEMORY(dst, src, sz) memcpy((dst), (src), (sz)) -#endif -#ifndef MA_DR_FLAC_ZERO_MEMORY -#define MA_DR_FLAC_ZERO_MEMORY(p, sz) memset((p), 0, (sz)) -#endif -#ifndef MA_DR_FLAC_ZERO_OBJECT -#define MA_DR_FLAC_ZERO_OBJECT(p) MA_DR_FLAC_ZERO_MEMORY((p), sizeof(*(p))) -#endif -#define MA_DR_FLAC_MAX_SIMD_VECTOR_SIZE 64 -#define MA_DR_FLAC_SUBFRAME_CONSTANT 0 -#define MA_DR_FLAC_SUBFRAME_VERBATIM 1 -#define MA_DR_FLAC_SUBFRAME_FIXED 8 -#define MA_DR_FLAC_SUBFRAME_LPC 32 -#define MA_DR_FLAC_SUBFRAME_RESERVED 255 -#define MA_DR_FLAC_RESIDUAL_CODING_METHOD_PARTITIONED_RICE 0 -#define MA_DR_FLAC_RESIDUAL_CODING_METHOD_PARTITIONED_RICE2 1 -#define MA_DR_FLAC_CHANNEL_ASSIGNMENT_INDEPENDENT 0 -#define MA_DR_FLAC_CHANNEL_ASSIGNMENT_LEFT_SIDE 8 -#define MA_DR_FLAC_CHANNEL_ASSIGNMENT_RIGHT_SIDE 9 -#define MA_DR_FLAC_CHANNEL_ASSIGNMENT_MID_SIDE 10 -#define MA_DR_FLAC_SEEKPOINT_SIZE_IN_BYTES 18 -#define MA_DR_FLAC_CUESHEET_TRACK_SIZE_IN_BYTES 36 -#define MA_DR_FLAC_CUESHEET_TRACK_INDEX_SIZE_IN_BYTES 12 -#define ma_dr_flac_align(x, a) ((((x) + (a) - 1) / (a)) * (a)) -MA_API void ma_dr_flac_version(ma_uint32* pMajor, ma_uint32* pMinor, ma_uint32* pRevision) -{ - if (pMajor) { - *pMajor = MA_DR_FLAC_VERSION_MAJOR; - } - if (pMinor) { - *pMinor = MA_DR_FLAC_VERSION_MINOR; - } - if (pRevision) { - *pRevision = MA_DR_FLAC_VERSION_REVISION; - } -} -MA_API const char* ma_dr_flac_version_string(void) -{ - return MA_DR_FLAC_VERSION_STRING; -} -#if defined(__has_feature) - #if __has_feature(thread_sanitizer) - #define MA_DR_FLAC_NO_THREAD_SANITIZE __attribute__((no_sanitize("thread"))) - #else - #define MA_DR_FLAC_NO_THREAD_SANITIZE - #endif -#else - #define MA_DR_FLAC_NO_THREAD_SANITIZE -#endif -#if defined(MA_DR_FLAC_HAS_LZCNT_INTRINSIC) -static ma_bool32 ma_dr_flac__gIsLZCNTSupported = MA_FALSE; -#endif -#ifndef MA_DR_FLAC_NO_CPUID -static ma_bool32 ma_dr_flac__gIsSSE2Supported = MA_FALSE; -static ma_bool32 ma_dr_flac__gIsSSE41Supported = MA_FALSE; -MA_DR_FLAC_NO_THREAD_SANITIZE static void ma_dr_flac__init_cpu_caps(void) -{ - static ma_bool32 isCPUCapsInitialized = MA_FALSE; - if (!isCPUCapsInitialized) { -#if defined(MA_DR_FLAC_HAS_LZCNT_INTRINSIC) - int info[4] = {0}; - ma_dr_flac__cpuid(info, 0x80000001); - ma_dr_flac__gIsLZCNTSupported = (info[2] & (1 << 5)) != 0; -#endif - ma_dr_flac__gIsSSE2Supported = ma_dr_flac_has_sse2(); - ma_dr_flac__gIsSSE41Supported = ma_dr_flac_has_sse41(); - isCPUCapsInitialized = MA_TRUE; - } -} -#else -static ma_bool32 ma_dr_flac__gIsNEONSupported = MA_FALSE; -static MA_INLINE ma_bool32 ma_dr_flac__has_neon(void) -{ -#if defined(MA_DR_FLAC_SUPPORT_NEON) - #if defined(MA_ARM) && !defined(MA_DR_FLAC_NO_NEON) - #if (defined(__ARM_NEON) || defined(__aarch64__) || defined(_M_ARM64)) - return MA_TRUE; - #else - return MA_FALSE; - #endif - #else - return MA_FALSE; - #endif -#else - return MA_FALSE; -#endif -} -MA_DR_FLAC_NO_THREAD_SANITIZE static void ma_dr_flac__init_cpu_caps(void) -{ - ma_dr_flac__gIsNEONSupported = ma_dr_flac__has_neon(); -#if defined(MA_DR_FLAC_HAS_LZCNT_INTRINSIC) && defined(MA_ARM) && (defined(__ARM_ARCH) && __ARM_ARCH >= 5) - ma_dr_flac__gIsLZCNTSupported = MA_TRUE; -#endif -} -#endif -static MA_INLINE ma_bool32 ma_dr_flac__is_little_endian(void) -{ -#if defined(MA_X86) || defined(MA_X64) - return MA_TRUE; -#elif defined(__BYTE_ORDER) && defined(__LITTLE_ENDIAN) && __BYTE_ORDER == __LITTLE_ENDIAN - return MA_TRUE; -#else - int n = 1; - return (*(char*)&n) == 1; -#endif -} -static MA_INLINE ma_uint16 ma_dr_flac__swap_endian_uint16(ma_uint16 n) -{ -#ifdef MA_DR_FLAC_HAS_BYTESWAP16_INTRINSIC - #if defined(_MSC_VER) && !defined(__clang__) - return _byteswap_ushort(n); - #elif defined(__GNUC__) || defined(__clang__) - return __builtin_bswap16(n); - #elif defined(__WATCOMC__) && defined(__386__) - return _watcom_bswap16(n); - #else - #error "This compiler does not support the byte swap intrinsic." - #endif -#else - return ((n & 0xFF00) >> 8) | - ((n & 0x00FF) << 8); -#endif -} -static MA_INLINE ma_uint32 ma_dr_flac__swap_endian_uint32(ma_uint32 n) -{ -#ifdef MA_DR_FLAC_HAS_BYTESWAP32_INTRINSIC - #if defined(_MSC_VER) && !defined(__clang__) - return _byteswap_ulong(n); - #elif defined(__GNUC__) || defined(__clang__) - #if defined(MA_ARM) && (defined(__ARM_ARCH) && __ARM_ARCH >= 6) && !defined(__ARM_ARCH_6M__) && !defined(MA_64BIT) - ma_uint32 r; - __asm__ __volatile__ ( - #if defined(MA_64BIT) - "rev %w[out], %w[in]" : [out]"=r"(r) : [in]"r"(n) - #else - "rev %[out], %[in]" : [out]"=r"(r) : [in]"r"(n) - #endif - ); - return r; - #else - return __builtin_bswap32(n); - #endif - #elif defined(__WATCOMC__) && defined(__386__) - return _watcom_bswap32(n); - #else - #error "This compiler does not support the byte swap intrinsic." - #endif -#else - return ((n & 0xFF000000) >> 24) | - ((n & 0x00FF0000) >> 8) | - ((n & 0x0000FF00) << 8) | - ((n & 0x000000FF) << 24); -#endif -} -static MA_INLINE ma_uint64 ma_dr_flac__swap_endian_uint64(ma_uint64 n) -{ -#ifdef MA_DR_FLAC_HAS_BYTESWAP64_INTRINSIC - #if defined(_MSC_VER) && !defined(__clang__) - return _byteswap_uint64(n); - #elif defined(__GNUC__) || defined(__clang__) - return __builtin_bswap64(n); - #elif defined(__WATCOMC__) && defined(__386__) - return _watcom_bswap64(n); - #else - #error "This compiler does not support the byte swap intrinsic." - #endif -#else - return ((n & ((ma_uint64)0xFF000000 << 32)) >> 56) | - ((n & ((ma_uint64)0x00FF0000 << 32)) >> 40) | - ((n & ((ma_uint64)0x0000FF00 << 32)) >> 24) | - ((n & ((ma_uint64)0x000000FF << 32)) >> 8) | - ((n & ((ma_uint64)0xFF000000 )) << 8) | - ((n & ((ma_uint64)0x00FF0000 )) << 24) | - ((n & ((ma_uint64)0x0000FF00 )) << 40) | - ((n & ((ma_uint64)0x000000FF )) << 56); -#endif -} -static MA_INLINE ma_uint16 ma_dr_flac__be2host_16(ma_uint16 n) -{ - if (ma_dr_flac__is_little_endian()) { - return ma_dr_flac__swap_endian_uint16(n); - } - return n; -} -static MA_INLINE ma_uint32 ma_dr_flac__be2host_32(ma_uint32 n) -{ - if (ma_dr_flac__is_little_endian()) { - return ma_dr_flac__swap_endian_uint32(n); - } - return n; -} -static MA_INLINE ma_uint32 ma_dr_flac__be2host_32_ptr_unaligned(const void* pData) -{ - const ma_uint8* pNum = (ma_uint8*)pData; - return *(pNum) << 24 | *(pNum+1) << 16 | *(pNum+2) << 8 | *(pNum+3); -} -static MA_INLINE ma_uint64 ma_dr_flac__be2host_64(ma_uint64 n) -{ - if (ma_dr_flac__is_little_endian()) { - return ma_dr_flac__swap_endian_uint64(n); - } - return n; -} -static MA_INLINE ma_uint32 ma_dr_flac__le2host_32(ma_uint32 n) -{ - if (!ma_dr_flac__is_little_endian()) { - return ma_dr_flac__swap_endian_uint32(n); - } - return n; -} -static MA_INLINE ma_uint32 ma_dr_flac__le2host_32_ptr_unaligned(const void* pData) -{ - const ma_uint8* pNum = (ma_uint8*)pData; - return *pNum | *(pNum+1) << 8 | *(pNum+2) << 16 | *(pNum+3) << 24; -} -static MA_INLINE ma_uint32 ma_dr_flac__unsynchsafe_32(ma_uint32 n) -{ - ma_uint32 result = 0; - result |= (n & 0x7F000000) >> 3; - result |= (n & 0x007F0000) >> 2; - result |= (n & 0x00007F00) >> 1; - result |= (n & 0x0000007F) >> 0; - return result; -} -static ma_uint8 ma_dr_flac__crc8_table[] = { - 0x00, 0x07, 0x0E, 0x09, 0x1C, 0x1B, 0x12, 0x15, 0x38, 0x3F, 0x36, 0x31, 0x24, 0x23, 0x2A, 0x2D, - 0x70, 0x77, 0x7E, 0x79, 0x6C, 0x6B, 0x62, 0x65, 0x48, 0x4F, 0x46, 0x41, 0x54, 0x53, 0x5A, 0x5D, - 0xE0, 0xE7, 0xEE, 0xE9, 0xFC, 0xFB, 0xF2, 0xF5, 0xD8, 0xDF, 0xD6, 0xD1, 0xC4, 0xC3, 0xCA, 0xCD, - 0x90, 0x97, 0x9E, 0x99, 0x8C, 0x8B, 0x82, 0x85, 0xA8, 0xAF, 0xA6, 0xA1, 0xB4, 0xB3, 0xBA, 0xBD, - 0xC7, 0xC0, 0xC9, 0xCE, 0xDB, 0xDC, 0xD5, 0xD2, 0xFF, 0xF8, 0xF1, 0xF6, 0xE3, 0xE4, 0xED, 0xEA, - 0xB7, 0xB0, 0xB9, 0xBE, 0xAB, 0xAC, 0xA5, 0xA2, 0x8F, 0x88, 0x81, 0x86, 0x93, 0x94, 0x9D, 0x9A, - 0x27, 0x20, 0x29, 0x2E, 0x3B, 0x3C, 0x35, 0x32, 0x1F, 0x18, 0x11, 0x16, 0x03, 0x04, 0x0D, 0x0A, - 0x57, 0x50, 0x59, 0x5E, 0x4B, 0x4C, 0x45, 0x42, 0x6F, 0x68, 0x61, 0x66, 0x73, 0x74, 0x7D, 0x7A, - 0x89, 0x8E, 0x87, 0x80, 0x95, 0x92, 0x9B, 0x9C, 0xB1, 0xB6, 0xBF, 0xB8, 0xAD, 0xAA, 0xA3, 0xA4, - 0xF9, 0xFE, 0xF7, 0xF0, 0xE5, 0xE2, 0xEB, 0xEC, 0xC1, 0xC6, 0xCF, 0xC8, 0xDD, 0xDA, 0xD3, 0xD4, - 0x69, 0x6E, 0x67, 0x60, 0x75, 0x72, 0x7B, 0x7C, 0x51, 0x56, 0x5F, 0x58, 0x4D, 0x4A, 0x43, 0x44, - 0x19, 0x1E, 0x17, 0x10, 0x05, 0x02, 0x0B, 0x0C, 0x21, 0x26, 0x2F, 0x28, 0x3D, 0x3A, 0x33, 0x34, - 0x4E, 0x49, 0x40, 0x47, 0x52, 0x55, 0x5C, 0x5B, 0x76, 0x71, 0x78, 0x7F, 0x6A, 0x6D, 0x64, 0x63, - 0x3E, 0x39, 0x30, 0x37, 0x22, 0x25, 0x2C, 0x2B, 0x06, 0x01, 0x08, 0x0F, 0x1A, 0x1D, 0x14, 0x13, - 0xAE, 0xA9, 0xA0, 0xA7, 0xB2, 0xB5, 0xBC, 0xBB, 0x96, 0x91, 0x98, 0x9F, 0x8A, 0x8D, 0x84, 0x83, - 0xDE, 0xD9, 0xD0, 0xD7, 0xC2, 0xC5, 0xCC, 0xCB, 0xE6, 0xE1, 0xE8, 0xEF, 0xFA, 0xFD, 0xF4, 0xF3 -}; -static ma_uint16 ma_dr_flac__crc16_table[] = { - 0x0000, 0x8005, 0x800F, 0x000A, 0x801B, 0x001E, 0x0014, 0x8011, - 0x8033, 0x0036, 0x003C, 0x8039, 0x0028, 0x802D, 0x8027, 0x0022, - 0x8063, 0x0066, 0x006C, 0x8069, 0x0078, 0x807D, 0x8077, 0x0072, - 0x0050, 0x8055, 0x805F, 0x005A, 0x804B, 0x004E, 0x0044, 0x8041, - 0x80C3, 0x00C6, 0x00CC, 0x80C9, 0x00D8, 0x80DD, 0x80D7, 0x00D2, - 0x00F0, 0x80F5, 0x80FF, 0x00FA, 0x80EB, 0x00EE, 0x00E4, 0x80E1, - 0x00A0, 0x80A5, 0x80AF, 0x00AA, 0x80BB, 0x00BE, 0x00B4, 0x80B1, - 0x8093, 0x0096, 0x009C, 0x8099, 0x0088, 0x808D, 0x8087, 0x0082, - 0x8183, 0x0186, 0x018C, 0x8189, 0x0198, 0x819D, 0x8197, 0x0192, - 0x01B0, 0x81B5, 0x81BF, 0x01BA, 0x81AB, 0x01AE, 0x01A4, 0x81A1, - 0x01E0, 0x81E5, 0x81EF, 0x01EA, 0x81FB, 0x01FE, 0x01F4, 0x81F1, - 0x81D3, 0x01D6, 0x01DC, 0x81D9, 0x01C8, 0x81CD, 0x81C7, 0x01C2, - 0x0140, 0x8145, 0x814F, 0x014A, 0x815B, 0x015E, 0x0154, 0x8151, - 0x8173, 0x0176, 0x017C, 0x8179, 0x0168, 0x816D, 0x8167, 0x0162, - 0x8123, 0x0126, 0x012C, 0x8129, 0x0138, 0x813D, 0x8137, 0x0132, - 0x0110, 0x8115, 0x811F, 0x011A, 0x810B, 0x010E, 0x0104, 0x8101, - 0x8303, 0x0306, 0x030C, 0x8309, 0x0318, 0x831D, 0x8317, 0x0312, - 0x0330, 0x8335, 0x833F, 0x033A, 0x832B, 0x032E, 0x0324, 0x8321, - 0x0360, 0x8365, 0x836F, 0x036A, 0x837B, 0x037E, 0x0374, 0x8371, - 0x8353, 0x0356, 0x035C, 0x8359, 0x0348, 0x834D, 0x8347, 0x0342, - 0x03C0, 0x83C5, 0x83CF, 0x03CA, 0x83DB, 0x03DE, 0x03D4, 0x83D1, - 0x83F3, 0x03F6, 0x03FC, 0x83F9, 0x03E8, 0x83ED, 0x83E7, 0x03E2, - 0x83A3, 0x03A6, 0x03AC, 0x83A9, 0x03B8, 0x83BD, 0x83B7, 0x03B2, - 0x0390, 0x8395, 0x839F, 0x039A, 0x838B, 0x038E, 0x0384, 0x8381, - 0x0280, 0x8285, 0x828F, 0x028A, 0x829B, 0x029E, 0x0294, 0x8291, - 0x82B3, 0x02B6, 0x02BC, 0x82B9, 0x02A8, 0x82AD, 0x82A7, 0x02A2, - 0x82E3, 0x02E6, 0x02EC, 0x82E9, 0x02F8, 0x82FD, 0x82F7, 0x02F2, - 0x02D0, 0x82D5, 0x82DF, 0x02DA, 0x82CB, 0x02CE, 0x02C4, 0x82C1, - 0x8243, 0x0246, 0x024C, 0x8249, 0x0258, 0x825D, 0x8257, 0x0252, - 0x0270, 0x8275, 0x827F, 0x027A, 0x826B, 0x026E, 0x0264, 0x8261, - 0x0220, 0x8225, 0x822F, 0x022A, 0x823B, 0x023E, 0x0234, 0x8231, - 0x8213, 0x0216, 0x021C, 0x8219, 0x0208, 0x820D, 0x8207, 0x0202 -}; -static MA_INLINE ma_uint8 ma_dr_flac_crc8_byte(ma_uint8 crc, ma_uint8 data) -{ - return ma_dr_flac__crc8_table[crc ^ data]; -} -static MA_INLINE ma_uint8 ma_dr_flac_crc8(ma_uint8 crc, ma_uint32 data, ma_uint32 count) -{ -#ifdef MA_DR_FLAC_NO_CRC - (void)crc; - (void)data; - (void)count; - return 0; -#else -#if 0 - ma_uint8 p = 0x07; - for (int i = count-1; i >= 0; --i) { - ma_uint8 bit = (data & (1 << i)) >> i; - if (crc & 0x80) { - crc = ((crc << 1) | bit) ^ p; - } else { - crc = ((crc << 1) | bit); - } - } - return crc; -#else - ma_uint32 wholeBytes; - ma_uint32 leftoverBits; - ma_uint64 leftoverDataMask; - static ma_uint64 leftoverDataMaskTable[8] = { - 0x00, 0x01, 0x03, 0x07, 0x0F, 0x1F, 0x3F, 0x7F - }; - MA_DR_FLAC_ASSERT(count <= 32); - wholeBytes = count >> 3; - leftoverBits = count - (wholeBytes*8); - leftoverDataMask = leftoverDataMaskTable[leftoverBits]; - switch (wholeBytes) { - case 4: crc = ma_dr_flac_crc8_byte(crc, (ma_uint8)((data & (0xFF000000UL << leftoverBits)) >> (24 + leftoverBits))); - case 3: crc = ma_dr_flac_crc8_byte(crc, (ma_uint8)((data & (0x00FF0000UL << leftoverBits)) >> (16 + leftoverBits))); - case 2: crc = ma_dr_flac_crc8_byte(crc, (ma_uint8)((data & (0x0000FF00UL << leftoverBits)) >> ( 8 + leftoverBits))); - case 1: crc = ma_dr_flac_crc8_byte(crc, (ma_uint8)((data & (0x000000FFUL << leftoverBits)) >> ( 0 + leftoverBits))); - case 0: if (leftoverBits > 0) crc = (ma_uint8)((crc << leftoverBits) ^ ma_dr_flac__crc8_table[(crc >> (8 - leftoverBits)) ^ (data & leftoverDataMask)]); - } - return crc; -#endif -#endif -} -static MA_INLINE ma_uint16 ma_dr_flac_crc16_byte(ma_uint16 crc, ma_uint8 data) -{ - return (crc << 8) ^ ma_dr_flac__crc16_table[(ma_uint8)(crc >> 8) ^ data]; -} -static MA_INLINE ma_uint16 ma_dr_flac_crc16_cache(ma_uint16 crc, ma_dr_flac_cache_t data) -{ -#ifdef MA_64BIT - crc = ma_dr_flac_crc16_byte(crc, (ma_uint8)((data >> 56) & 0xFF)); - crc = ma_dr_flac_crc16_byte(crc, (ma_uint8)((data >> 48) & 0xFF)); - crc = ma_dr_flac_crc16_byte(crc, (ma_uint8)((data >> 40) & 0xFF)); - crc = ma_dr_flac_crc16_byte(crc, (ma_uint8)((data >> 32) & 0xFF)); -#endif - crc = ma_dr_flac_crc16_byte(crc, (ma_uint8)((data >> 24) & 0xFF)); - crc = ma_dr_flac_crc16_byte(crc, (ma_uint8)((data >> 16) & 0xFF)); - crc = ma_dr_flac_crc16_byte(crc, (ma_uint8)((data >> 8) & 0xFF)); - crc = ma_dr_flac_crc16_byte(crc, (ma_uint8)((data >> 0) & 0xFF)); - return crc; -} -static MA_INLINE ma_uint16 ma_dr_flac_crc16_bytes(ma_uint16 crc, ma_dr_flac_cache_t data, ma_uint32 byteCount) -{ - switch (byteCount) - { -#ifdef MA_64BIT - case 8: crc = ma_dr_flac_crc16_byte(crc, (ma_uint8)((data >> 56) & 0xFF)); - case 7: crc = ma_dr_flac_crc16_byte(crc, (ma_uint8)((data >> 48) & 0xFF)); - case 6: crc = ma_dr_flac_crc16_byte(crc, (ma_uint8)((data >> 40) & 0xFF)); - case 5: crc = ma_dr_flac_crc16_byte(crc, (ma_uint8)((data >> 32) & 0xFF)); -#endif - case 4: crc = ma_dr_flac_crc16_byte(crc, (ma_uint8)((data >> 24) & 0xFF)); - case 3: crc = ma_dr_flac_crc16_byte(crc, (ma_uint8)((data >> 16) & 0xFF)); - case 2: crc = ma_dr_flac_crc16_byte(crc, (ma_uint8)((data >> 8) & 0xFF)); - case 1: crc = ma_dr_flac_crc16_byte(crc, (ma_uint8)((data >> 0) & 0xFF)); - } - return crc; -} -#if 0 -static MA_INLINE ma_uint16 ma_dr_flac_crc16__32bit(ma_uint16 crc, ma_uint32 data, ma_uint32 count) -{ -#ifdef MA_DR_FLAC_NO_CRC - (void)crc; - (void)data; - (void)count; - return 0; -#else -#if 0 - ma_uint16 p = 0x8005; - for (int i = count-1; i >= 0; --i) { - ma_uint16 bit = (data & (1ULL << i)) >> i; - if (r & 0x8000) { - r = ((r << 1) | bit) ^ p; - } else { - r = ((r << 1) | bit); - } - } - return crc; -#else - ma_uint32 wholeBytes; - ma_uint32 leftoverBits; - ma_uint64 leftoverDataMask; - static ma_uint64 leftoverDataMaskTable[8] = { - 0x00, 0x01, 0x03, 0x07, 0x0F, 0x1F, 0x3F, 0x7F - }; - MA_DR_FLAC_ASSERT(count <= 64); - wholeBytes = count >> 3; - leftoverBits = count & 7; - leftoverDataMask = leftoverDataMaskTable[leftoverBits]; - switch (wholeBytes) { - default: - case 4: crc = ma_dr_flac_crc16_byte(crc, (ma_uint8)((data & (0xFF000000UL << leftoverBits)) >> (24 + leftoverBits))); - case 3: crc = ma_dr_flac_crc16_byte(crc, (ma_uint8)((data & (0x00FF0000UL << leftoverBits)) >> (16 + leftoverBits))); - case 2: crc = ma_dr_flac_crc16_byte(crc, (ma_uint8)((data & (0x0000FF00UL << leftoverBits)) >> ( 8 + leftoverBits))); - case 1: crc = ma_dr_flac_crc16_byte(crc, (ma_uint8)((data & (0x000000FFUL << leftoverBits)) >> ( 0 + leftoverBits))); - case 0: if (leftoverBits > 0) crc = (crc << leftoverBits) ^ ma_dr_flac__crc16_table[(crc >> (16 - leftoverBits)) ^ (data & leftoverDataMask)]; - } - return crc; -#endif -#endif -} -static MA_INLINE ma_uint16 ma_dr_flac_crc16__64bit(ma_uint16 crc, ma_uint64 data, ma_uint32 count) -{ -#ifdef MA_DR_FLAC_NO_CRC - (void)crc; - (void)data; - (void)count; - return 0; -#else - ma_uint32 wholeBytes; - ma_uint32 leftoverBits; - ma_uint64 leftoverDataMask; - static ma_uint64 leftoverDataMaskTable[8] = { - 0x00, 0x01, 0x03, 0x07, 0x0F, 0x1F, 0x3F, 0x7F - }; - MA_DR_FLAC_ASSERT(count <= 64); - wholeBytes = count >> 3; - leftoverBits = count & 7; - leftoverDataMask = leftoverDataMaskTable[leftoverBits]; - switch (wholeBytes) { - default: - case 8: crc = ma_dr_flac_crc16_byte(crc, (ma_uint8)((data & (((ma_uint64)0xFF000000 << 32) << leftoverBits)) >> (56 + leftoverBits))); - case 7: crc = ma_dr_flac_crc16_byte(crc, (ma_uint8)((data & (((ma_uint64)0x00FF0000 << 32) << leftoverBits)) >> (48 + leftoverBits))); - case 6: crc = ma_dr_flac_crc16_byte(crc, (ma_uint8)((data & (((ma_uint64)0x0000FF00 << 32) << leftoverBits)) >> (40 + leftoverBits))); - case 5: crc = ma_dr_flac_crc16_byte(crc, (ma_uint8)((data & (((ma_uint64)0x000000FF << 32) << leftoverBits)) >> (32 + leftoverBits))); - case 4: crc = ma_dr_flac_crc16_byte(crc, (ma_uint8)((data & (((ma_uint64)0xFF000000 ) << leftoverBits)) >> (24 + leftoverBits))); - case 3: crc = ma_dr_flac_crc16_byte(crc, (ma_uint8)((data & (((ma_uint64)0x00FF0000 ) << leftoverBits)) >> (16 + leftoverBits))); - case 2: crc = ma_dr_flac_crc16_byte(crc, (ma_uint8)((data & (((ma_uint64)0x0000FF00 ) << leftoverBits)) >> ( 8 + leftoverBits))); - case 1: crc = ma_dr_flac_crc16_byte(crc, (ma_uint8)((data & (((ma_uint64)0x000000FF ) << leftoverBits)) >> ( 0 + leftoverBits))); - case 0: if (leftoverBits > 0) crc = (crc << leftoverBits) ^ ma_dr_flac__crc16_table[(crc >> (16 - leftoverBits)) ^ (data & leftoverDataMask)]; - } - return crc; -#endif -} -static MA_INLINE ma_uint16 ma_dr_flac_crc16(ma_uint16 crc, ma_dr_flac_cache_t data, ma_uint32 count) -{ -#ifdef MA_64BIT - return ma_dr_flac_crc16__64bit(crc, data, count); -#else - return ma_dr_flac_crc16__32bit(crc, data, count); -#endif -} -#endif -#ifdef MA_64BIT -#define ma_dr_flac__be2host__cache_line ma_dr_flac__be2host_64 -#else -#define ma_dr_flac__be2host__cache_line ma_dr_flac__be2host_32 -#endif -#define MA_DR_FLAC_CACHE_L1_SIZE_BYTES(bs) (sizeof((bs)->cache)) -#define MA_DR_FLAC_CACHE_L1_SIZE_BITS(bs) (sizeof((bs)->cache)*8) -#define MA_DR_FLAC_CACHE_L1_BITS_REMAINING(bs) (MA_DR_FLAC_CACHE_L1_SIZE_BITS(bs) - (bs)->consumedBits) -#define MA_DR_FLAC_CACHE_L1_SELECTION_MASK(_bitCount) (~((~(ma_dr_flac_cache_t)0) >> (_bitCount))) -#define MA_DR_FLAC_CACHE_L1_SELECTION_SHIFT(bs, _bitCount) (MA_DR_FLAC_CACHE_L1_SIZE_BITS(bs) - (_bitCount)) -#define MA_DR_FLAC_CACHE_L1_SELECT(bs, _bitCount) (((bs)->cache) & MA_DR_FLAC_CACHE_L1_SELECTION_MASK(_bitCount)) -#define MA_DR_FLAC_CACHE_L1_SELECT_AND_SHIFT(bs, _bitCount) (MA_DR_FLAC_CACHE_L1_SELECT((bs), (_bitCount)) >> MA_DR_FLAC_CACHE_L1_SELECTION_SHIFT((bs), (_bitCount))) -#define MA_DR_FLAC_CACHE_L1_SELECT_AND_SHIFT_SAFE(bs, _bitCount)(MA_DR_FLAC_CACHE_L1_SELECT((bs), (_bitCount)) >> (MA_DR_FLAC_CACHE_L1_SELECTION_SHIFT((bs), (_bitCount)) & (MA_DR_FLAC_CACHE_L1_SIZE_BITS(bs)-1))) -#define MA_DR_FLAC_CACHE_L2_SIZE_BYTES(bs) (sizeof((bs)->cacheL2)) -#define MA_DR_FLAC_CACHE_L2_LINE_COUNT(bs) (MA_DR_FLAC_CACHE_L2_SIZE_BYTES(bs) / sizeof((bs)->cacheL2[0])) -#define MA_DR_FLAC_CACHE_L2_LINES_REMAINING(bs) (MA_DR_FLAC_CACHE_L2_LINE_COUNT(bs) - (bs)->nextL2Line) -#ifndef MA_DR_FLAC_NO_CRC -static MA_INLINE void ma_dr_flac__reset_crc16(ma_dr_flac_bs* bs) -{ - bs->crc16 = 0; - bs->crc16CacheIgnoredBytes = bs->consumedBits >> 3; -} -static MA_INLINE void ma_dr_flac__update_crc16(ma_dr_flac_bs* bs) -{ - if (bs->crc16CacheIgnoredBytes == 0) { - bs->crc16 = ma_dr_flac_crc16_cache(bs->crc16, bs->crc16Cache); - } else { - bs->crc16 = ma_dr_flac_crc16_bytes(bs->crc16, bs->crc16Cache, MA_DR_FLAC_CACHE_L1_SIZE_BYTES(bs) - bs->crc16CacheIgnoredBytes); - bs->crc16CacheIgnoredBytes = 0; - } -} -static MA_INLINE ma_uint16 ma_dr_flac__flush_crc16(ma_dr_flac_bs* bs) -{ - MA_DR_FLAC_ASSERT((MA_DR_FLAC_CACHE_L1_BITS_REMAINING(bs) & 7) == 0); - if (MA_DR_FLAC_CACHE_L1_BITS_REMAINING(bs) == 0) { - ma_dr_flac__update_crc16(bs); - } else { - bs->crc16 = ma_dr_flac_crc16_bytes(bs->crc16, bs->crc16Cache >> MA_DR_FLAC_CACHE_L1_BITS_REMAINING(bs), (bs->consumedBits >> 3) - bs->crc16CacheIgnoredBytes); - bs->crc16CacheIgnoredBytes = bs->consumedBits >> 3; - } - return bs->crc16; -} -#endif -static MA_INLINE ma_bool32 ma_dr_flac__reload_l1_cache_from_l2(ma_dr_flac_bs* bs) -{ - size_t bytesRead; - size_t alignedL1LineCount; - if (bs->nextL2Line < MA_DR_FLAC_CACHE_L2_LINE_COUNT(bs)) { - bs->cache = bs->cacheL2[bs->nextL2Line++]; - return MA_TRUE; - } - if (bs->unalignedByteCount > 0) { - return MA_FALSE; - } - bytesRead = bs->onRead(bs->pUserData, bs->cacheL2, MA_DR_FLAC_CACHE_L2_SIZE_BYTES(bs)); - bs->nextL2Line = 0; - if (bytesRead == MA_DR_FLAC_CACHE_L2_SIZE_BYTES(bs)) { - bs->cache = bs->cacheL2[bs->nextL2Line++]; - return MA_TRUE; - } - alignedL1LineCount = bytesRead / MA_DR_FLAC_CACHE_L1_SIZE_BYTES(bs); - bs->unalignedByteCount = bytesRead - (alignedL1LineCount * MA_DR_FLAC_CACHE_L1_SIZE_BYTES(bs)); - if (bs->unalignedByteCount > 0) { - bs->unalignedCache = bs->cacheL2[alignedL1LineCount]; - } - if (alignedL1LineCount > 0) { - size_t offset = MA_DR_FLAC_CACHE_L2_LINE_COUNT(bs) - alignedL1LineCount; - size_t i; - for (i = alignedL1LineCount; i > 0; --i) { - bs->cacheL2[i-1 + offset] = bs->cacheL2[i-1]; - } - bs->nextL2Line = (ma_uint32)offset; - bs->cache = bs->cacheL2[bs->nextL2Line++]; - return MA_TRUE; - } else { - bs->nextL2Line = MA_DR_FLAC_CACHE_L2_LINE_COUNT(bs); - return MA_FALSE; - } -} -static ma_bool32 ma_dr_flac__reload_cache(ma_dr_flac_bs* bs) -{ - size_t bytesRead; -#ifndef MA_DR_FLAC_NO_CRC - ma_dr_flac__update_crc16(bs); -#endif - if (ma_dr_flac__reload_l1_cache_from_l2(bs)) { - bs->cache = ma_dr_flac__be2host__cache_line(bs->cache); - bs->consumedBits = 0; -#ifndef MA_DR_FLAC_NO_CRC - bs->crc16Cache = bs->cache; -#endif - return MA_TRUE; - } - bytesRead = bs->unalignedByteCount; - if (bytesRead == 0) { - bs->consumedBits = MA_DR_FLAC_CACHE_L1_SIZE_BITS(bs); - return MA_FALSE; - } - MA_DR_FLAC_ASSERT(bytesRead < MA_DR_FLAC_CACHE_L1_SIZE_BYTES(bs)); - bs->consumedBits = (ma_uint32)(MA_DR_FLAC_CACHE_L1_SIZE_BYTES(bs) - bytesRead) * 8; - bs->cache = ma_dr_flac__be2host__cache_line(bs->unalignedCache); - bs->cache &= MA_DR_FLAC_CACHE_L1_SELECTION_MASK(MA_DR_FLAC_CACHE_L1_BITS_REMAINING(bs)); - bs->unalignedByteCount = 0; -#ifndef MA_DR_FLAC_NO_CRC - bs->crc16Cache = bs->cache >> bs->consumedBits; - bs->crc16CacheIgnoredBytes = bs->consumedBits >> 3; -#endif - return MA_TRUE; -} -static void ma_dr_flac__reset_cache(ma_dr_flac_bs* bs) -{ - bs->nextL2Line = MA_DR_FLAC_CACHE_L2_LINE_COUNT(bs); - bs->consumedBits = MA_DR_FLAC_CACHE_L1_SIZE_BITS(bs); - bs->cache = 0; - bs->unalignedByteCount = 0; - bs->unalignedCache = 0; -#ifndef MA_DR_FLAC_NO_CRC - bs->crc16Cache = 0; - bs->crc16CacheIgnoredBytes = 0; -#endif -} -static MA_INLINE ma_bool32 ma_dr_flac__read_uint32(ma_dr_flac_bs* bs, unsigned int bitCount, ma_uint32* pResultOut) -{ - MA_DR_FLAC_ASSERT(bs != NULL); - MA_DR_FLAC_ASSERT(pResultOut != NULL); - MA_DR_FLAC_ASSERT(bitCount > 0); - MA_DR_FLAC_ASSERT(bitCount <= 32); - if (bs->consumedBits == MA_DR_FLAC_CACHE_L1_SIZE_BITS(bs)) { - if (!ma_dr_flac__reload_cache(bs)) { - return MA_FALSE; - } - } - if (bitCount <= MA_DR_FLAC_CACHE_L1_BITS_REMAINING(bs)) { -#ifdef MA_64BIT - *pResultOut = (ma_uint32)MA_DR_FLAC_CACHE_L1_SELECT_AND_SHIFT(bs, bitCount); - bs->consumedBits += bitCount; - bs->cache <<= bitCount; -#else - if (bitCount < MA_DR_FLAC_CACHE_L1_SIZE_BITS(bs)) { - *pResultOut = (ma_uint32)MA_DR_FLAC_CACHE_L1_SELECT_AND_SHIFT(bs, bitCount); - bs->consumedBits += bitCount; - bs->cache <<= bitCount; - } else { - *pResultOut = (ma_uint32)bs->cache; - bs->consumedBits = MA_DR_FLAC_CACHE_L1_SIZE_BITS(bs); - bs->cache = 0; - } -#endif - return MA_TRUE; - } else { - ma_uint32 bitCountHi = MA_DR_FLAC_CACHE_L1_BITS_REMAINING(bs); - ma_uint32 bitCountLo = bitCount - bitCountHi; - ma_uint32 resultHi; - MA_DR_FLAC_ASSERT(bitCountHi > 0); - MA_DR_FLAC_ASSERT(bitCountHi < 32); - resultHi = (ma_uint32)MA_DR_FLAC_CACHE_L1_SELECT_AND_SHIFT(bs, bitCountHi); - if (!ma_dr_flac__reload_cache(bs)) { - return MA_FALSE; - } - if (bitCountLo > MA_DR_FLAC_CACHE_L1_BITS_REMAINING(bs)) { - return MA_FALSE; - } - *pResultOut = (resultHi << bitCountLo) | (ma_uint32)MA_DR_FLAC_CACHE_L1_SELECT_AND_SHIFT(bs, bitCountLo); - bs->consumedBits += bitCountLo; - bs->cache <<= bitCountLo; - return MA_TRUE; - } -} -static ma_bool32 ma_dr_flac__read_int32(ma_dr_flac_bs* bs, unsigned int bitCount, ma_int32* pResult) -{ - ma_uint32 result; - MA_DR_FLAC_ASSERT(bs != NULL); - MA_DR_FLAC_ASSERT(pResult != NULL); - MA_DR_FLAC_ASSERT(bitCount > 0); - MA_DR_FLAC_ASSERT(bitCount <= 32); - if (!ma_dr_flac__read_uint32(bs, bitCount, &result)) { - return MA_FALSE; - } - if (bitCount < 32) { - ma_uint32 signbit; - signbit = ((result >> (bitCount-1)) & 0x01); - result |= (~signbit + 1) << bitCount; - } - *pResult = (ma_int32)result; - return MA_TRUE; -} -#ifdef MA_64BIT -static ma_bool32 ma_dr_flac__read_uint64(ma_dr_flac_bs* bs, unsigned int bitCount, ma_uint64* pResultOut) -{ - ma_uint32 resultHi; - ma_uint32 resultLo; - MA_DR_FLAC_ASSERT(bitCount <= 64); - MA_DR_FLAC_ASSERT(bitCount > 32); - if (!ma_dr_flac__read_uint32(bs, bitCount - 32, &resultHi)) { - return MA_FALSE; - } - if (!ma_dr_flac__read_uint32(bs, 32, &resultLo)) { - return MA_FALSE; - } - *pResultOut = (((ma_uint64)resultHi) << 32) | ((ma_uint64)resultLo); - return MA_TRUE; -} -#endif -#if 0 -static ma_bool32 ma_dr_flac__read_int64(ma_dr_flac_bs* bs, unsigned int bitCount, ma_int64* pResultOut) -{ - ma_uint64 result; - ma_uint64 signbit; - MA_DR_FLAC_ASSERT(bitCount <= 64); - if (!ma_dr_flac__read_uint64(bs, bitCount, &result)) { - return MA_FALSE; - } - signbit = ((result >> (bitCount-1)) & 0x01); - result |= (~signbit + 1) << bitCount; - *pResultOut = (ma_int64)result; - return MA_TRUE; -} -#endif -static ma_bool32 ma_dr_flac__read_uint16(ma_dr_flac_bs* bs, unsigned int bitCount, ma_uint16* pResult) -{ - ma_uint32 result; - MA_DR_FLAC_ASSERT(bs != NULL); - MA_DR_FLAC_ASSERT(pResult != NULL); - MA_DR_FLAC_ASSERT(bitCount > 0); - MA_DR_FLAC_ASSERT(bitCount <= 16); - if (!ma_dr_flac__read_uint32(bs, bitCount, &result)) { - return MA_FALSE; - } - *pResult = (ma_uint16)result; - return MA_TRUE; -} -#if 0 -static ma_bool32 ma_dr_flac__read_int16(ma_dr_flac_bs* bs, unsigned int bitCount, ma_int16* pResult) -{ - ma_int32 result; - MA_DR_FLAC_ASSERT(bs != NULL); - MA_DR_FLAC_ASSERT(pResult != NULL); - MA_DR_FLAC_ASSERT(bitCount > 0); - MA_DR_FLAC_ASSERT(bitCount <= 16); - if (!ma_dr_flac__read_int32(bs, bitCount, &result)) { - return MA_FALSE; - } - *pResult = (ma_int16)result; - return MA_TRUE; -} -#endif -static ma_bool32 ma_dr_flac__read_uint8(ma_dr_flac_bs* bs, unsigned int bitCount, ma_uint8* pResult) -{ - ma_uint32 result; - MA_DR_FLAC_ASSERT(bs != NULL); - MA_DR_FLAC_ASSERT(pResult != NULL); - MA_DR_FLAC_ASSERT(bitCount > 0); - MA_DR_FLAC_ASSERT(bitCount <= 8); - if (!ma_dr_flac__read_uint32(bs, bitCount, &result)) { - return MA_FALSE; - } - *pResult = (ma_uint8)result; - return MA_TRUE; -} -static ma_bool32 ma_dr_flac__read_int8(ma_dr_flac_bs* bs, unsigned int bitCount, ma_int8* pResult) -{ - ma_int32 result; - MA_DR_FLAC_ASSERT(bs != NULL); - MA_DR_FLAC_ASSERT(pResult != NULL); - MA_DR_FLAC_ASSERT(bitCount > 0); - MA_DR_FLAC_ASSERT(bitCount <= 8); - if (!ma_dr_flac__read_int32(bs, bitCount, &result)) { - return MA_FALSE; - } - *pResult = (ma_int8)result; - return MA_TRUE; -} -static ma_bool32 ma_dr_flac__seek_bits(ma_dr_flac_bs* bs, size_t bitsToSeek) -{ - if (bitsToSeek <= MA_DR_FLAC_CACHE_L1_BITS_REMAINING(bs)) { - bs->consumedBits += (ma_uint32)bitsToSeek; - bs->cache <<= bitsToSeek; - return MA_TRUE; - } else { - bitsToSeek -= MA_DR_FLAC_CACHE_L1_BITS_REMAINING(bs); - bs->consumedBits += MA_DR_FLAC_CACHE_L1_BITS_REMAINING(bs); - bs->cache = 0; -#ifdef MA_64BIT - while (bitsToSeek >= MA_DR_FLAC_CACHE_L1_SIZE_BITS(bs)) { - ma_uint64 bin; - if (!ma_dr_flac__read_uint64(bs, MA_DR_FLAC_CACHE_L1_SIZE_BITS(bs), &bin)) { - return MA_FALSE; - } - bitsToSeek -= MA_DR_FLAC_CACHE_L1_SIZE_BITS(bs); - } -#else - while (bitsToSeek >= MA_DR_FLAC_CACHE_L1_SIZE_BITS(bs)) { - ma_uint32 bin; - if (!ma_dr_flac__read_uint32(bs, MA_DR_FLAC_CACHE_L1_SIZE_BITS(bs), &bin)) { - return MA_FALSE; - } - bitsToSeek -= MA_DR_FLAC_CACHE_L1_SIZE_BITS(bs); - } -#endif - while (bitsToSeek >= 8) { - ma_uint8 bin; - if (!ma_dr_flac__read_uint8(bs, 8, &bin)) { - return MA_FALSE; - } - bitsToSeek -= 8; - } - if (bitsToSeek > 0) { - ma_uint8 bin; - if (!ma_dr_flac__read_uint8(bs, (ma_uint32)bitsToSeek, &bin)) { - return MA_FALSE; - } - bitsToSeek = 0; - } - MA_DR_FLAC_ASSERT(bitsToSeek == 0); - return MA_TRUE; - } -} -static ma_bool32 ma_dr_flac__find_and_seek_to_next_sync_code(ma_dr_flac_bs* bs) -{ - MA_DR_FLAC_ASSERT(bs != NULL); - if (!ma_dr_flac__seek_bits(bs, MA_DR_FLAC_CACHE_L1_BITS_REMAINING(bs) & 7)) { - return MA_FALSE; - } - for (;;) { - ma_uint8 hi; -#ifndef MA_DR_FLAC_NO_CRC - ma_dr_flac__reset_crc16(bs); -#endif - if (!ma_dr_flac__read_uint8(bs, 8, &hi)) { - return MA_FALSE; - } - if (hi == 0xFF) { - ma_uint8 lo; - if (!ma_dr_flac__read_uint8(bs, 6, &lo)) { - return MA_FALSE; - } - if (lo == 0x3E) { - return MA_TRUE; - } else { - if (!ma_dr_flac__seek_bits(bs, MA_DR_FLAC_CACHE_L1_BITS_REMAINING(bs) & 7)) { - return MA_FALSE; - } - } - } - } -} -#if defined(MA_DR_FLAC_HAS_LZCNT_INTRINSIC) -#define MA_DR_FLAC_IMPLEMENT_CLZ_LZCNT -#endif -#if defined(_MSC_VER) && _MSC_VER >= 1400 && (defined(MA_X64) || defined(MA_X86)) && !defined(__clang__) -#define MA_DR_FLAC_IMPLEMENT_CLZ_MSVC -#endif -#if defined(__WATCOMC__) && defined(__386__) -#define MA_DR_FLAC_IMPLEMENT_CLZ_WATCOM -#endif -#ifdef __MRC__ -#include -#define MA_DR_FLAC_IMPLEMENT_CLZ_MRC -#endif -static MA_INLINE ma_uint32 ma_dr_flac__clz_software(ma_dr_flac_cache_t x) -{ - ma_uint32 n; - static ma_uint32 clz_table_4[] = { - 0, - 4, - 3, 3, - 2, 2, 2, 2, - 1, 1, 1, 1, 1, 1, 1, 1 - }; - if (x == 0) { - return sizeof(x)*8; - } - n = clz_table_4[x >> (sizeof(x)*8 - 4)]; - if (n == 0) { -#ifdef MA_64BIT - if ((x & ((ma_uint64)0xFFFFFFFF << 32)) == 0) { n = 32; x <<= 32; } - if ((x & ((ma_uint64)0xFFFF0000 << 32)) == 0) { n += 16; x <<= 16; } - if ((x & ((ma_uint64)0xFF000000 << 32)) == 0) { n += 8; x <<= 8; } - if ((x & ((ma_uint64)0xF0000000 << 32)) == 0) { n += 4; x <<= 4; } -#else - if ((x & 0xFFFF0000) == 0) { n = 16; x <<= 16; } - if ((x & 0xFF000000) == 0) { n += 8; x <<= 8; } - if ((x & 0xF0000000) == 0) { n += 4; x <<= 4; } -#endif - n += clz_table_4[x >> (sizeof(x)*8 - 4)]; - } - return n - 1; -} -#ifdef MA_DR_FLAC_IMPLEMENT_CLZ_LZCNT -static MA_INLINE ma_bool32 ma_dr_flac__is_lzcnt_supported(void) -{ -#if defined(MA_DR_FLAC_HAS_LZCNT_INTRINSIC) && defined(MA_ARM) && (defined(__ARM_ARCH) && __ARM_ARCH >= 5) - return MA_TRUE; -#elif defined(__MRC__) - return MA_TRUE; -#else - #ifdef MA_DR_FLAC_HAS_LZCNT_INTRINSIC - return ma_dr_flac__gIsLZCNTSupported; - #else - return MA_FALSE; - #endif -#endif -} -static MA_INLINE ma_uint32 ma_dr_flac__clz_lzcnt(ma_dr_flac_cache_t x) -{ -#if defined(_MSC_VER) - #ifdef MA_64BIT - return (ma_uint32)__lzcnt64(x); - #else - return (ma_uint32)__lzcnt(x); - #endif -#else - #if defined(__GNUC__) || defined(__clang__) - #if defined(MA_X64) - { - ma_uint64 r; - __asm__ __volatile__ ( - "lzcnt{ %1, %0| %0, %1}" : "=r"(r) : "r"(x) : "cc" - ); - return (ma_uint32)r; - } - #elif defined(MA_X86) - { - ma_uint32 r; - __asm__ __volatile__ ( - "lzcnt{l %1, %0| %0, %1}" : "=r"(r) : "r"(x) : "cc" - ); - return r; - } - #elif defined(MA_ARM) && (defined(__ARM_ARCH) && __ARM_ARCH >= 5) && !defined(__ARM_ARCH_6M__) && !defined(MA_64BIT) - { - unsigned int r; - __asm__ __volatile__ ( - #if defined(MA_64BIT) - "clz %w[out], %w[in]" : [out]"=r"(r) : [in]"r"(x) - #else - "clz %[out], %[in]" : [out]"=r"(r) : [in]"r"(x) - #endif - ); - return r; - } - #else - if (x == 0) { - return sizeof(x)*8; - } - #ifdef MA_64BIT - return (ma_uint32)__builtin_clzll((ma_uint64)x); - #else - return (ma_uint32)__builtin_clzl((ma_uint32)x); - #endif - #endif - #else - #error "This compiler does not support the lzcnt intrinsic." - #endif -#endif -} -#endif -#ifdef MA_DR_FLAC_IMPLEMENT_CLZ_MSVC -#include -static MA_INLINE ma_uint32 ma_dr_flac__clz_msvc(ma_dr_flac_cache_t x) -{ - ma_uint32 n; - if (x == 0) { - return sizeof(x)*8; - } -#ifdef MA_64BIT - _BitScanReverse64((unsigned long*)&n, x); -#else - _BitScanReverse((unsigned long*)&n, x); -#endif - return sizeof(x)*8 - n - 1; -} -#endif -#ifdef MA_DR_FLAC_IMPLEMENT_CLZ_WATCOM -static __inline ma_uint32 ma_dr_flac__clz_watcom (ma_uint32); -#ifdef MA_DR_FLAC_IMPLEMENT_CLZ_WATCOM_LZCNT -#pragma aux ma_dr_flac__clz_watcom_lzcnt = \ - "db 0F3h, 0Fh, 0BDh, 0C0h" \ - parm [eax] \ - value [eax] \ - modify nomemory; -#else -#pragma aux ma_dr_flac__clz_watcom = \ - "bsr eax, eax" \ - "xor eax, 31" \ - parm [eax] nomemory \ - value [eax] \ - modify exact [eax] nomemory; -#endif -#endif -static MA_INLINE ma_uint32 ma_dr_flac__clz(ma_dr_flac_cache_t x) -{ -#ifdef MA_DR_FLAC_IMPLEMENT_CLZ_LZCNT - if (ma_dr_flac__is_lzcnt_supported()) { - return ma_dr_flac__clz_lzcnt(x); - } else -#endif - { -#ifdef MA_DR_FLAC_IMPLEMENT_CLZ_MSVC - return ma_dr_flac__clz_msvc(x); -#elif defined(MA_DR_FLAC_IMPLEMENT_CLZ_WATCOM_LZCNT) - return ma_dr_flac__clz_watcom_lzcnt(x); -#elif defined(MA_DR_FLAC_IMPLEMENT_CLZ_WATCOM) - return (x == 0) ? sizeof(x)*8 : ma_dr_flac__clz_watcom(x); -#elif defined(__MRC__) - return __cntlzw(x); -#else - return ma_dr_flac__clz_software(x); -#endif - } -} -static MA_INLINE ma_bool32 ma_dr_flac__seek_past_next_set_bit(ma_dr_flac_bs* bs, unsigned int* pOffsetOut) -{ - ma_uint32 zeroCounter = 0; - ma_uint32 setBitOffsetPlus1; - while (bs->cache == 0) { - zeroCounter += (ma_uint32)MA_DR_FLAC_CACHE_L1_BITS_REMAINING(bs); - if (!ma_dr_flac__reload_cache(bs)) { - return MA_FALSE; - } - } - if (bs->cache == 1) { - *pOffsetOut = zeroCounter + (ma_uint32)MA_DR_FLAC_CACHE_L1_BITS_REMAINING(bs) - 1; - if (!ma_dr_flac__reload_cache(bs)) { - return MA_FALSE; - } - return MA_TRUE; - } - setBitOffsetPlus1 = ma_dr_flac__clz(bs->cache); - setBitOffsetPlus1 += 1; - if (setBitOffsetPlus1 > MA_DR_FLAC_CACHE_L1_BITS_REMAINING(bs)) { - return MA_FALSE; - } - bs->consumedBits += setBitOffsetPlus1; - bs->cache <<= setBitOffsetPlus1; - *pOffsetOut = zeroCounter + setBitOffsetPlus1 - 1; - return MA_TRUE; -} -static ma_bool32 ma_dr_flac__seek_to_byte(ma_dr_flac_bs* bs, ma_uint64 offsetFromStart) -{ - MA_DR_FLAC_ASSERT(bs != NULL); - MA_DR_FLAC_ASSERT(offsetFromStart > 0); - if (offsetFromStart > 0x7FFFFFFF) { - ma_uint64 bytesRemaining = offsetFromStart; - if (!bs->onSeek(bs->pUserData, 0x7FFFFFFF, ma_dr_flac_seek_origin_start)) { - return MA_FALSE; - } - bytesRemaining -= 0x7FFFFFFF; - while (bytesRemaining > 0x7FFFFFFF) { - if (!bs->onSeek(bs->pUserData, 0x7FFFFFFF, ma_dr_flac_seek_origin_current)) { - return MA_FALSE; - } - bytesRemaining -= 0x7FFFFFFF; - } - if (bytesRemaining > 0) { - if (!bs->onSeek(bs->pUserData, (int)bytesRemaining, ma_dr_flac_seek_origin_current)) { - return MA_FALSE; - } - } - } else { - if (!bs->onSeek(bs->pUserData, (int)offsetFromStart, ma_dr_flac_seek_origin_start)) { - return MA_FALSE; - } - } - ma_dr_flac__reset_cache(bs); - return MA_TRUE; -} -static ma_result ma_dr_flac__read_utf8_coded_number(ma_dr_flac_bs* bs, ma_uint64* pNumberOut, ma_uint8* pCRCOut) -{ - ma_uint8 crc; - ma_uint64 result; - ma_uint8 utf8[7] = {0}; - int byteCount; - int i; - MA_DR_FLAC_ASSERT(bs != NULL); - MA_DR_FLAC_ASSERT(pNumberOut != NULL); - MA_DR_FLAC_ASSERT(pCRCOut != NULL); - crc = *pCRCOut; - if (!ma_dr_flac__read_uint8(bs, 8, utf8)) { - *pNumberOut = 0; - return MA_AT_END; - } - crc = ma_dr_flac_crc8(crc, utf8[0], 8); - if ((utf8[0] & 0x80) == 0) { - *pNumberOut = utf8[0]; - *pCRCOut = crc; - return MA_SUCCESS; - } - if ((utf8[0] & 0xE0) == 0xC0) { - byteCount = 2; - } else if ((utf8[0] & 0xF0) == 0xE0) { - byteCount = 3; - } else if ((utf8[0] & 0xF8) == 0xF0) { - byteCount = 4; - } else if ((utf8[0] & 0xFC) == 0xF8) { - byteCount = 5; - } else if ((utf8[0] & 0xFE) == 0xFC) { - byteCount = 6; - } else if ((utf8[0] & 0xFF) == 0xFE) { - byteCount = 7; - } else { - *pNumberOut = 0; - return MA_CRC_MISMATCH; - } - MA_DR_FLAC_ASSERT(byteCount > 1); - result = (ma_uint64)(utf8[0] & (0xFF >> (byteCount + 1))); - for (i = 1; i < byteCount; ++i) { - if (!ma_dr_flac__read_uint8(bs, 8, utf8 + i)) { - *pNumberOut = 0; - return MA_AT_END; - } - crc = ma_dr_flac_crc8(crc, utf8[i], 8); - result = (result << 6) | (utf8[i] & 0x3F); - } - *pNumberOut = result; - *pCRCOut = crc; - return MA_SUCCESS; -} -static MA_INLINE ma_uint32 ma_dr_flac__ilog2_u32(ma_uint32 x) -{ -#if 1 - ma_uint32 result = 0; - while (x > 0) { - result += 1; - x >>= 1; - } - return result; -#endif -} -static MA_INLINE ma_bool32 ma_dr_flac__use_64_bit_prediction(ma_uint32 bitsPerSample, ma_uint32 order, ma_uint32 precision) -{ - return bitsPerSample + precision + ma_dr_flac__ilog2_u32(order) > 32; -} -#if defined(__clang__) -__attribute__((no_sanitize("signed-integer-overflow"))) -#endif -static MA_INLINE ma_int32 ma_dr_flac__calculate_prediction_32(ma_uint32 order, ma_int32 shift, const ma_int32* coefficients, ma_int32* pDecodedSamples) -{ - ma_int32 prediction = 0; - MA_DR_FLAC_ASSERT(order <= 32); - switch (order) - { - case 32: prediction += coefficients[31] * pDecodedSamples[-32]; - case 31: prediction += coefficients[30] * pDecodedSamples[-31]; - case 30: prediction += coefficients[29] * pDecodedSamples[-30]; - case 29: prediction += coefficients[28] * pDecodedSamples[-29]; - case 28: prediction += coefficients[27] * pDecodedSamples[-28]; - case 27: prediction += coefficients[26] * pDecodedSamples[-27]; - case 26: prediction += coefficients[25] * pDecodedSamples[-26]; - case 25: prediction += coefficients[24] * pDecodedSamples[-25]; - case 24: prediction += coefficients[23] * pDecodedSamples[-24]; - case 23: prediction += coefficients[22] * pDecodedSamples[-23]; - case 22: prediction += coefficients[21] * pDecodedSamples[-22]; - case 21: prediction += coefficients[20] * pDecodedSamples[-21]; - case 20: prediction += coefficients[19] * pDecodedSamples[-20]; - case 19: prediction += coefficients[18] * pDecodedSamples[-19]; - case 18: prediction += coefficients[17] * pDecodedSamples[-18]; - case 17: prediction += coefficients[16] * pDecodedSamples[-17]; - case 16: prediction += coefficients[15] * pDecodedSamples[-16]; - case 15: prediction += coefficients[14] * pDecodedSamples[-15]; - case 14: prediction += coefficients[13] * pDecodedSamples[-14]; - case 13: prediction += coefficients[12] * pDecodedSamples[-13]; - case 12: prediction += coefficients[11] * pDecodedSamples[-12]; - case 11: prediction += coefficients[10] * pDecodedSamples[-11]; - case 10: prediction += coefficients[ 9] * pDecodedSamples[-10]; - case 9: prediction += coefficients[ 8] * pDecodedSamples[- 9]; - case 8: prediction += coefficients[ 7] * pDecodedSamples[- 8]; - case 7: prediction += coefficients[ 6] * pDecodedSamples[- 7]; - case 6: prediction += coefficients[ 5] * pDecodedSamples[- 6]; - case 5: prediction += coefficients[ 4] * pDecodedSamples[- 5]; - case 4: prediction += coefficients[ 3] * pDecodedSamples[- 4]; - case 3: prediction += coefficients[ 2] * pDecodedSamples[- 3]; - case 2: prediction += coefficients[ 1] * pDecodedSamples[- 2]; - case 1: prediction += coefficients[ 0] * pDecodedSamples[- 1]; - } - return (ma_int32)(prediction >> shift); -} -static MA_INLINE ma_int32 ma_dr_flac__calculate_prediction_64(ma_uint32 order, ma_int32 shift, const ma_int32* coefficients, ma_int32* pDecodedSamples) -{ - ma_int64 prediction; - MA_DR_FLAC_ASSERT(order <= 32); -#ifndef MA_64BIT - if (order == 8) - { - prediction = coefficients[0] * (ma_int64)pDecodedSamples[-1]; - prediction += coefficients[1] * (ma_int64)pDecodedSamples[-2]; - prediction += coefficients[2] * (ma_int64)pDecodedSamples[-3]; - prediction += coefficients[3] * (ma_int64)pDecodedSamples[-4]; - prediction += coefficients[4] * (ma_int64)pDecodedSamples[-5]; - prediction += coefficients[5] * (ma_int64)pDecodedSamples[-6]; - prediction += coefficients[6] * (ma_int64)pDecodedSamples[-7]; - prediction += coefficients[7] * (ma_int64)pDecodedSamples[-8]; - } - else if (order == 7) - { - prediction = coefficients[0] * (ma_int64)pDecodedSamples[-1]; - prediction += coefficients[1] * (ma_int64)pDecodedSamples[-2]; - prediction += coefficients[2] * (ma_int64)pDecodedSamples[-3]; - prediction += coefficients[3] * (ma_int64)pDecodedSamples[-4]; - prediction += coefficients[4] * (ma_int64)pDecodedSamples[-5]; - prediction += coefficients[5] * (ma_int64)pDecodedSamples[-6]; - prediction += coefficients[6] * (ma_int64)pDecodedSamples[-7]; - } - else if (order == 3) - { - prediction = coefficients[0] * (ma_int64)pDecodedSamples[-1]; - prediction += coefficients[1] * (ma_int64)pDecodedSamples[-2]; - prediction += coefficients[2] * (ma_int64)pDecodedSamples[-3]; - } - else if (order == 6) - { - prediction = coefficients[0] * (ma_int64)pDecodedSamples[-1]; - prediction += coefficients[1] * (ma_int64)pDecodedSamples[-2]; - prediction += coefficients[2] * (ma_int64)pDecodedSamples[-3]; - prediction += coefficients[3] * (ma_int64)pDecodedSamples[-4]; - prediction += coefficients[4] * (ma_int64)pDecodedSamples[-5]; - prediction += coefficients[5] * (ma_int64)pDecodedSamples[-6]; - } - else if (order == 5) - { - prediction = coefficients[0] * (ma_int64)pDecodedSamples[-1]; - prediction += coefficients[1] * (ma_int64)pDecodedSamples[-2]; - prediction += coefficients[2] * (ma_int64)pDecodedSamples[-3]; - prediction += coefficients[3] * (ma_int64)pDecodedSamples[-4]; - prediction += coefficients[4] * (ma_int64)pDecodedSamples[-5]; - } - else if (order == 4) - { - prediction = coefficients[0] * (ma_int64)pDecodedSamples[-1]; - prediction += coefficients[1] * (ma_int64)pDecodedSamples[-2]; - prediction += coefficients[2] * (ma_int64)pDecodedSamples[-3]; - prediction += coefficients[3] * (ma_int64)pDecodedSamples[-4]; - } - else if (order == 12) - { - prediction = coefficients[0] * (ma_int64)pDecodedSamples[-1]; - prediction += coefficients[1] * (ma_int64)pDecodedSamples[-2]; - prediction += coefficients[2] * (ma_int64)pDecodedSamples[-3]; - prediction += coefficients[3] * (ma_int64)pDecodedSamples[-4]; - prediction += coefficients[4] * (ma_int64)pDecodedSamples[-5]; - prediction += coefficients[5] * (ma_int64)pDecodedSamples[-6]; - prediction += coefficients[6] * (ma_int64)pDecodedSamples[-7]; - prediction += coefficients[7] * (ma_int64)pDecodedSamples[-8]; - prediction += coefficients[8] * (ma_int64)pDecodedSamples[-9]; - prediction += coefficients[9] * (ma_int64)pDecodedSamples[-10]; - prediction += coefficients[10] * (ma_int64)pDecodedSamples[-11]; - prediction += coefficients[11] * (ma_int64)pDecodedSamples[-12]; - } - else if (order == 2) - { - prediction = coefficients[0] * (ma_int64)pDecodedSamples[-1]; - prediction += coefficients[1] * (ma_int64)pDecodedSamples[-2]; - } - else if (order == 1) - { - prediction = coefficients[0] * (ma_int64)pDecodedSamples[-1]; - } - else if (order == 10) - { - prediction = coefficients[0] * (ma_int64)pDecodedSamples[-1]; - prediction += coefficients[1] * (ma_int64)pDecodedSamples[-2]; - prediction += coefficients[2] * (ma_int64)pDecodedSamples[-3]; - prediction += coefficients[3] * (ma_int64)pDecodedSamples[-4]; - prediction += coefficients[4] * (ma_int64)pDecodedSamples[-5]; - prediction += coefficients[5] * (ma_int64)pDecodedSamples[-6]; - prediction += coefficients[6] * (ma_int64)pDecodedSamples[-7]; - prediction += coefficients[7] * (ma_int64)pDecodedSamples[-8]; - prediction += coefficients[8] * (ma_int64)pDecodedSamples[-9]; - prediction += coefficients[9] * (ma_int64)pDecodedSamples[-10]; - } - else if (order == 9) - { - prediction = coefficients[0] * (ma_int64)pDecodedSamples[-1]; - prediction += coefficients[1] * (ma_int64)pDecodedSamples[-2]; - prediction += coefficients[2] * (ma_int64)pDecodedSamples[-3]; - prediction += coefficients[3] * (ma_int64)pDecodedSamples[-4]; - prediction += coefficients[4] * (ma_int64)pDecodedSamples[-5]; - prediction += coefficients[5] * (ma_int64)pDecodedSamples[-6]; - prediction += coefficients[6] * (ma_int64)pDecodedSamples[-7]; - prediction += coefficients[7] * (ma_int64)pDecodedSamples[-8]; - prediction += coefficients[8] * (ma_int64)pDecodedSamples[-9]; - } - else if (order == 11) - { - prediction = coefficients[0] * (ma_int64)pDecodedSamples[-1]; - prediction += coefficients[1] * (ma_int64)pDecodedSamples[-2]; - prediction += coefficients[2] * (ma_int64)pDecodedSamples[-3]; - prediction += coefficients[3] * (ma_int64)pDecodedSamples[-4]; - prediction += coefficients[4] * (ma_int64)pDecodedSamples[-5]; - prediction += coefficients[5] * (ma_int64)pDecodedSamples[-6]; - prediction += coefficients[6] * (ma_int64)pDecodedSamples[-7]; - prediction += coefficients[7] * (ma_int64)pDecodedSamples[-8]; - prediction += coefficients[8] * (ma_int64)pDecodedSamples[-9]; - prediction += coefficients[9] * (ma_int64)pDecodedSamples[-10]; - prediction += coefficients[10] * (ma_int64)pDecodedSamples[-11]; - } - else - { - int j; - prediction = 0; - for (j = 0; j < (int)order; ++j) { - prediction += coefficients[j] * (ma_int64)pDecodedSamples[-j-1]; - } - } -#endif -#ifdef MA_64BIT - prediction = 0; - switch (order) - { - case 32: prediction += coefficients[31] * (ma_int64)pDecodedSamples[-32]; - case 31: prediction += coefficients[30] * (ma_int64)pDecodedSamples[-31]; - case 30: prediction += coefficients[29] * (ma_int64)pDecodedSamples[-30]; - case 29: prediction += coefficients[28] * (ma_int64)pDecodedSamples[-29]; - case 28: prediction += coefficients[27] * (ma_int64)pDecodedSamples[-28]; - case 27: prediction += coefficients[26] * (ma_int64)pDecodedSamples[-27]; - case 26: prediction += coefficients[25] * (ma_int64)pDecodedSamples[-26]; - case 25: prediction += coefficients[24] * (ma_int64)pDecodedSamples[-25]; - case 24: prediction += coefficients[23] * (ma_int64)pDecodedSamples[-24]; - case 23: prediction += coefficients[22] * (ma_int64)pDecodedSamples[-23]; - case 22: prediction += coefficients[21] * (ma_int64)pDecodedSamples[-22]; - case 21: prediction += coefficients[20] * (ma_int64)pDecodedSamples[-21]; - case 20: prediction += coefficients[19] * (ma_int64)pDecodedSamples[-20]; - case 19: prediction += coefficients[18] * (ma_int64)pDecodedSamples[-19]; - case 18: prediction += coefficients[17] * (ma_int64)pDecodedSamples[-18]; - case 17: prediction += coefficients[16] * (ma_int64)pDecodedSamples[-17]; - case 16: prediction += coefficients[15] * (ma_int64)pDecodedSamples[-16]; - case 15: prediction += coefficients[14] * (ma_int64)pDecodedSamples[-15]; - case 14: prediction += coefficients[13] * (ma_int64)pDecodedSamples[-14]; - case 13: prediction += coefficients[12] * (ma_int64)pDecodedSamples[-13]; - case 12: prediction += coefficients[11] * (ma_int64)pDecodedSamples[-12]; - case 11: prediction += coefficients[10] * (ma_int64)pDecodedSamples[-11]; - case 10: prediction += coefficients[ 9] * (ma_int64)pDecodedSamples[-10]; - case 9: prediction += coefficients[ 8] * (ma_int64)pDecodedSamples[- 9]; - case 8: prediction += coefficients[ 7] * (ma_int64)pDecodedSamples[- 8]; - case 7: prediction += coefficients[ 6] * (ma_int64)pDecodedSamples[- 7]; - case 6: prediction += coefficients[ 5] * (ma_int64)pDecodedSamples[- 6]; - case 5: prediction += coefficients[ 4] * (ma_int64)pDecodedSamples[- 5]; - case 4: prediction += coefficients[ 3] * (ma_int64)pDecodedSamples[- 4]; - case 3: prediction += coefficients[ 2] * (ma_int64)pDecodedSamples[- 3]; - case 2: prediction += coefficients[ 1] * (ma_int64)pDecodedSamples[- 2]; - case 1: prediction += coefficients[ 0] * (ma_int64)pDecodedSamples[- 1]; - } -#endif - return (ma_int32)(prediction >> shift); -} -#if 0 -static ma_bool32 ma_dr_flac__decode_samples_with_residual__rice__reference(ma_dr_flac_bs* bs, ma_uint32 bitsPerSample, ma_uint32 count, ma_uint8 riceParam, ma_uint32 lpcOrder, ma_int32 lpcShift, ma_uint32 lpcPrecision, const ma_int32* coefficients, ma_int32* pSamplesOut) -{ - ma_uint32 i; - MA_DR_FLAC_ASSERT(bs != NULL); - MA_DR_FLAC_ASSERT(pSamplesOut != NULL); - for (i = 0; i < count; ++i) { - ma_uint32 zeroCounter = 0; - for (;;) { - ma_uint8 bit; - if (!ma_dr_flac__read_uint8(bs, 1, &bit)) { - return MA_FALSE; - } - if (bit == 0) { - zeroCounter += 1; - } else { - break; - } - } - ma_uint32 decodedRice; - if (riceParam > 0) { - if (!ma_dr_flac__read_uint32(bs, riceParam, &decodedRice)) { - return MA_FALSE; - } - } else { - decodedRice = 0; - } - decodedRice |= (zeroCounter << riceParam); - if ((decodedRice & 0x01)) { - decodedRice = ~(decodedRice >> 1); - } else { - decodedRice = (decodedRice >> 1); - } - if (ma_dr_flac__use_64_bit_prediction(bitsPerSample, lpcOrder, lpcPrecision)) { - pSamplesOut[i] = decodedRice + ma_dr_flac__calculate_prediction_64(lpcOrder, lpcShift, coefficients, pSamplesOut + i); - } else { - pSamplesOut[i] = decodedRice + ma_dr_flac__calculate_prediction_32(lpcOrder, lpcShift, coefficients, pSamplesOut + i); - } - } - return MA_TRUE; -} -#endif -#if 0 -static ma_bool32 ma_dr_flac__read_rice_parts__reference(ma_dr_flac_bs* bs, ma_uint8 riceParam, ma_uint32* pZeroCounterOut, ma_uint32* pRiceParamPartOut) -{ - ma_uint32 zeroCounter = 0; - ma_uint32 decodedRice; - for (;;) { - ma_uint8 bit; - if (!ma_dr_flac__read_uint8(bs, 1, &bit)) { - return MA_FALSE; - } - if (bit == 0) { - zeroCounter += 1; - } else { - break; - } - } - if (riceParam > 0) { - if (!ma_dr_flac__read_uint32(bs, riceParam, &decodedRice)) { - return MA_FALSE; - } - } else { - decodedRice = 0; - } - *pZeroCounterOut = zeroCounter; - *pRiceParamPartOut = decodedRice; - return MA_TRUE; -} -#endif -#if 0 -static MA_INLINE ma_bool32 ma_dr_flac__read_rice_parts(ma_dr_flac_bs* bs, ma_uint8 riceParam, ma_uint32* pZeroCounterOut, ma_uint32* pRiceParamPartOut) -{ - ma_dr_flac_cache_t riceParamMask; - ma_uint32 zeroCounter; - ma_uint32 setBitOffsetPlus1; - ma_uint32 riceParamPart; - ma_uint32 riceLength; - MA_DR_FLAC_ASSERT(riceParam > 0); - riceParamMask = MA_DR_FLAC_CACHE_L1_SELECTION_MASK(riceParam); - zeroCounter = 0; - while (bs->cache == 0) { - zeroCounter += (ma_uint32)MA_DR_FLAC_CACHE_L1_BITS_REMAINING(bs); - if (!ma_dr_flac__reload_cache(bs)) { - return MA_FALSE; - } - } - setBitOffsetPlus1 = ma_dr_flac__clz(bs->cache); - zeroCounter += setBitOffsetPlus1; - setBitOffsetPlus1 += 1; - riceLength = setBitOffsetPlus1 + riceParam; - if (riceLength < MA_DR_FLAC_CACHE_L1_BITS_REMAINING(bs)) { - riceParamPart = (ma_uint32)((bs->cache & (riceParamMask >> setBitOffsetPlus1)) >> MA_DR_FLAC_CACHE_L1_SELECTION_SHIFT(bs, riceLength)); - bs->consumedBits += riceLength; - bs->cache <<= riceLength; - } else { - ma_uint32 bitCountLo; - ma_dr_flac_cache_t resultHi; - bs->consumedBits += riceLength; - bs->cache <<= setBitOffsetPlus1 & (MA_DR_FLAC_CACHE_L1_SIZE_BITS(bs)-1); - bitCountLo = bs->consumedBits - MA_DR_FLAC_CACHE_L1_SIZE_BITS(bs); - resultHi = MA_DR_FLAC_CACHE_L1_SELECT_AND_SHIFT(bs, riceParam); - if (bs->nextL2Line < MA_DR_FLAC_CACHE_L2_LINE_COUNT(bs)) { -#ifndef MA_DR_FLAC_NO_CRC - ma_dr_flac__update_crc16(bs); -#endif - bs->cache = ma_dr_flac__be2host__cache_line(bs->cacheL2[bs->nextL2Line++]); - bs->consumedBits = 0; -#ifndef MA_DR_FLAC_NO_CRC - bs->crc16Cache = bs->cache; -#endif - } else { - if (!ma_dr_flac__reload_cache(bs)) { - return MA_FALSE; - } - if (bitCountLo > MA_DR_FLAC_CACHE_L1_BITS_REMAINING(bs)) { - return MA_FALSE; - } - } - riceParamPart = (ma_uint32)(resultHi | MA_DR_FLAC_CACHE_L1_SELECT_AND_SHIFT_SAFE(bs, bitCountLo)); - bs->consumedBits += bitCountLo; - bs->cache <<= bitCountLo; - } - pZeroCounterOut[0] = zeroCounter; - pRiceParamPartOut[0] = riceParamPart; - return MA_TRUE; -} -#endif -static MA_INLINE ma_bool32 ma_dr_flac__read_rice_parts_x1(ma_dr_flac_bs* bs, ma_uint8 riceParam, ma_uint32* pZeroCounterOut, ma_uint32* pRiceParamPartOut) -{ - ma_uint32 riceParamPlus1 = riceParam + 1; - ma_uint32 riceParamPlus1Shift = MA_DR_FLAC_CACHE_L1_SELECTION_SHIFT(bs, riceParamPlus1); - ma_uint32 riceParamPlus1MaxConsumedBits = MA_DR_FLAC_CACHE_L1_SIZE_BITS(bs) - riceParamPlus1; - ma_dr_flac_cache_t bs_cache = bs->cache; - ma_uint32 bs_consumedBits = bs->consumedBits; - ma_uint32 lzcount = ma_dr_flac__clz(bs_cache); - if (lzcount < sizeof(bs_cache)*8) { - pZeroCounterOut[0] = lzcount; - extract_rice_param_part: - bs_cache <<= lzcount; - bs_consumedBits += lzcount; - if (bs_consumedBits <= riceParamPlus1MaxConsumedBits) { - pRiceParamPartOut[0] = (ma_uint32)(bs_cache >> riceParamPlus1Shift); - bs_cache <<= riceParamPlus1; - bs_consumedBits += riceParamPlus1; - } else { - ma_uint32 riceParamPartHi; - ma_uint32 riceParamPartLo; - ma_uint32 riceParamPartLoBitCount; - riceParamPartHi = (ma_uint32)(bs_cache >> riceParamPlus1Shift); - riceParamPartLoBitCount = bs_consumedBits - riceParamPlus1MaxConsumedBits; - MA_DR_FLAC_ASSERT(riceParamPartLoBitCount > 0 && riceParamPartLoBitCount < 32); - if (bs->nextL2Line < MA_DR_FLAC_CACHE_L2_LINE_COUNT(bs)) { - #ifndef MA_DR_FLAC_NO_CRC - ma_dr_flac__update_crc16(bs); - #endif - bs_cache = ma_dr_flac__be2host__cache_line(bs->cacheL2[bs->nextL2Line++]); - bs_consumedBits = riceParamPartLoBitCount; - #ifndef MA_DR_FLAC_NO_CRC - bs->crc16Cache = bs_cache; - #endif - } else { - if (!ma_dr_flac__reload_cache(bs)) { - return MA_FALSE; - } - if (riceParamPartLoBitCount > MA_DR_FLAC_CACHE_L1_BITS_REMAINING(bs)) { - return MA_FALSE; - } - bs_cache = bs->cache; - bs_consumedBits = bs->consumedBits + riceParamPartLoBitCount; - } - riceParamPartLo = (ma_uint32)(bs_cache >> (MA_DR_FLAC_CACHE_L1_SELECTION_SHIFT(bs, riceParamPartLoBitCount))); - pRiceParamPartOut[0] = riceParamPartHi | riceParamPartLo; - bs_cache <<= riceParamPartLoBitCount; - } - } else { - ma_uint32 zeroCounter = (ma_uint32)(MA_DR_FLAC_CACHE_L1_SIZE_BITS(bs) - bs_consumedBits); - for (;;) { - if (bs->nextL2Line < MA_DR_FLAC_CACHE_L2_LINE_COUNT(bs)) { - #ifndef MA_DR_FLAC_NO_CRC - ma_dr_flac__update_crc16(bs); - #endif - bs_cache = ma_dr_flac__be2host__cache_line(bs->cacheL2[bs->nextL2Line++]); - bs_consumedBits = 0; - #ifndef MA_DR_FLAC_NO_CRC - bs->crc16Cache = bs_cache; - #endif - } else { - if (!ma_dr_flac__reload_cache(bs)) { - return MA_FALSE; - } - bs_cache = bs->cache; - bs_consumedBits = bs->consumedBits; - } - lzcount = ma_dr_flac__clz(bs_cache); - zeroCounter += lzcount; - if (lzcount < sizeof(bs_cache)*8) { - break; - } - } - pZeroCounterOut[0] = zeroCounter; - goto extract_rice_param_part; - } - bs->cache = bs_cache; - bs->consumedBits = bs_consumedBits; - return MA_TRUE; -} -static MA_INLINE ma_bool32 ma_dr_flac__seek_rice_parts(ma_dr_flac_bs* bs, ma_uint8 riceParam) -{ - ma_uint32 riceParamPlus1 = riceParam + 1; - ma_uint32 riceParamPlus1MaxConsumedBits = MA_DR_FLAC_CACHE_L1_SIZE_BITS(bs) - riceParamPlus1; - ma_dr_flac_cache_t bs_cache = bs->cache; - ma_uint32 bs_consumedBits = bs->consumedBits; - ma_uint32 lzcount = ma_dr_flac__clz(bs_cache); - if (lzcount < sizeof(bs_cache)*8) { - extract_rice_param_part: - bs_cache <<= lzcount; - bs_consumedBits += lzcount; - if (bs_consumedBits <= riceParamPlus1MaxConsumedBits) { - bs_cache <<= riceParamPlus1; - bs_consumedBits += riceParamPlus1; - } else { - ma_uint32 riceParamPartLoBitCount = bs_consumedBits - riceParamPlus1MaxConsumedBits; - MA_DR_FLAC_ASSERT(riceParamPartLoBitCount > 0 && riceParamPartLoBitCount < 32); - if (bs->nextL2Line < MA_DR_FLAC_CACHE_L2_LINE_COUNT(bs)) { - #ifndef MA_DR_FLAC_NO_CRC - ma_dr_flac__update_crc16(bs); - #endif - bs_cache = ma_dr_flac__be2host__cache_line(bs->cacheL2[bs->nextL2Line++]); - bs_consumedBits = riceParamPartLoBitCount; - #ifndef MA_DR_FLAC_NO_CRC - bs->crc16Cache = bs_cache; - #endif - } else { - if (!ma_dr_flac__reload_cache(bs)) { - return MA_FALSE; - } - if (riceParamPartLoBitCount > MA_DR_FLAC_CACHE_L1_BITS_REMAINING(bs)) { - return MA_FALSE; - } - bs_cache = bs->cache; - bs_consumedBits = bs->consumedBits + riceParamPartLoBitCount; - } - bs_cache <<= riceParamPartLoBitCount; - } - } else { - for (;;) { - if (bs->nextL2Line < MA_DR_FLAC_CACHE_L2_LINE_COUNT(bs)) { - #ifndef MA_DR_FLAC_NO_CRC - ma_dr_flac__update_crc16(bs); - #endif - bs_cache = ma_dr_flac__be2host__cache_line(bs->cacheL2[bs->nextL2Line++]); - bs_consumedBits = 0; - #ifndef MA_DR_FLAC_NO_CRC - bs->crc16Cache = bs_cache; - #endif - } else { - if (!ma_dr_flac__reload_cache(bs)) { - return MA_FALSE; - } - bs_cache = bs->cache; - bs_consumedBits = bs->consumedBits; - } - lzcount = ma_dr_flac__clz(bs_cache); - if (lzcount < sizeof(bs_cache)*8) { - break; - } - } - goto extract_rice_param_part; - } - bs->cache = bs_cache; - bs->consumedBits = bs_consumedBits; - return MA_TRUE; -} -static ma_bool32 ma_dr_flac__decode_samples_with_residual__rice__scalar_zeroorder(ma_dr_flac_bs* bs, ma_uint32 bitsPerSample, ma_uint32 count, ma_uint8 riceParam, ma_uint32 order, ma_int32 shift, const ma_int32* coefficients, ma_int32* pSamplesOut) -{ - ma_uint32 t[2] = {0x00000000, 0xFFFFFFFF}; - ma_uint32 zeroCountPart0; - ma_uint32 riceParamPart0; - ma_uint32 riceParamMask; - ma_uint32 i; - MA_DR_FLAC_ASSERT(bs != NULL); - MA_DR_FLAC_ASSERT(pSamplesOut != NULL); - (void)bitsPerSample; - (void)order; - (void)shift; - (void)coefficients; - riceParamMask = (ma_uint32)~((~0UL) << riceParam); - i = 0; - while (i < count) { - if (!ma_dr_flac__read_rice_parts_x1(bs, riceParam, &zeroCountPart0, &riceParamPart0)) { - return MA_FALSE; - } - riceParamPart0 &= riceParamMask; - riceParamPart0 |= (zeroCountPart0 << riceParam); - riceParamPart0 = (riceParamPart0 >> 1) ^ t[riceParamPart0 & 0x01]; - pSamplesOut[i] = riceParamPart0; - i += 1; - } - return MA_TRUE; -} -static ma_bool32 ma_dr_flac__decode_samples_with_residual__rice__scalar(ma_dr_flac_bs* bs, ma_uint32 bitsPerSample, ma_uint32 count, ma_uint8 riceParam, ma_uint32 lpcOrder, ma_int32 lpcShift, ma_uint32 lpcPrecision, const ma_int32* coefficients, ma_int32* pSamplesOut) -{ - ma_uint32 t[2] = {0x00000000, 0xFFFFFFFF}; - ma_uint32 zeroCountPart0 = 0; - ma_uint32 zeroCountPart1 = 0; - ma_uint32 zeroCountPart2 = 0; - ma_uint32 zeroCountPart3 = 0; - ma_uint32 riceParamPart0 = 0; - ma_uint32 riceParamPart1 = 0; - ma_uint32 riceParamPart2 = 0; - ma_uint32 riceParamPart3 = 0; - ma_uint32 riceParamMask; - const ma_int32* pSamplesOutEnd; - ma_uint32 i; - MA_DR_FLAC_ASSERT(bs != NULL); - MA_DR_FLAC_ASSERT(pSamplesOut != NULL); - if (lpcOrder == 0) { - return ma_dr_flac__decode_samples_with_residual__rice__scalar_zeroorder(bs, bitsPerSample, count, riceParam, lpcOrder, lpcShift, coefficients, pSamplesOut); - } - riceParamMask = (ma_uint32)~((~0UL) << riceParam); - pSamplesOutEnd = pSamplesOut + (count & ~3); - if (ma_dr_flac__use_64_bit_prediction(bitsPerSample, lpcOrder, lpcPrecision)) { - while (pSamplesOut < pSamplesOutEnd) { - if (!ma_dr_flac__read_rice_parts_x1(bs, riceParam, &zeroCountPart0, &riceParamPart0) || - !ma_dr_flac__read_rice_parts_x1(bs, riceParam, &zeroCountPart1, &riceParamPart1) || - !ma_dr_flac__read_rice_parts_x1(bs, riceParam, &zeroCountPart2, &riceParamPart2) || - !ma_dr_flac__read_rice_parts_x1(bs, riceParam, &zeroCountPart3, &riceParamPart3)) { - return MA_FALSE; - } - riceParamPart0 &= riceParamMask; - riceParamPart1 &= riceParamMask; - riceParamPart2 &= riceParamMask; - riceParamPart3 &= riceParamMask; - riceParamPart0 |= (zeroCountPart0 << riceParam); - riceParamPart1 |= (zeroCountPart1 << riceParam); - riceParamPart2 |= (zeroCountPart2 << riceParam); - riceParamPart3 |= (zeroCountPart3 << riceParam); - riceParamPart0 = (riceParamPart0 >> 1) ^ t[riceParamPart0 & 0x01]; - riceParamPart1 = (riceParamPart1 >> 1) ^ t[riceParamPart1 & 0x01]; - riceParamPart2 = (riceParamPart2 >> 1) ^ t[riceParamPart2 & 0x01]; - riceParamPart3 = (riceParamPart3 >> 1) ^ t[riceParamPart3 & 0x01]; - pSamplesOut[0] = riceParamPart0 + ma_dr_flac__calculate_prediction_64(lpcOrder, lpcShift, coefficients, pSamplesOut + 0); - pSamplesOut[1] = riceParamPart1 + ma_dr_flac__calculate_prediction_64(lpcOrder, lpcShift, coefficients, pSamplesOut + 1); - pSamplesOut[2] = riceParamPart2 + ma_dr_flac__calculate_prediction_64(lpcOrder, lpcShift, coefficients, pSamplesOut + 2); - pSamplesOut[3] = riceParamPart3 + ma_dr_flac__calculate_prediction_64(lpcOrder, lpcShift, coefficients, pSamplesOut + 3); - pSamplesOut += 4; - } - } else { - while (pSamplesOut < pSamplesOutEnd) { - if (!ma_dr_flac__read_rice_parts_x1(bs, riceParam, &zeroCountPart0, &riceParamPart0) || - !ma_dr_flac__read_rice_parts_x1(bs, riceParam, &zeroCountPart1, &riceParamPart1) || - !ma_dr_flac__read_rice_parts_x1(bs, riceParam, &zeroCountPart2, &riceParamPart2) || - !ma_dr_flac__read_rice_parts_x1(bs, riceParam, &zeroCountPart3, &riceParamPart3)) { - return MA_FALSE; - } - riceParamPart0 &= riceParamMask; - riceParamPart1 &= riceParamMask; - riceParamPart2 &= riceParamMask; - riceParamPart3 &= riceParamMask; - riceParamPart0 |= (zeroCountPart0 << riceParam); - riceParamPart1 |= (zeroCountPart1 << riceParam); - riceParamPart2 |= (zeroCountPart2 << riceParam); - riceParamPart3 |= (zeroCountPart3 << riceParam); - riceParamPart0 = (riceParamPart0 >> 1) ^ t[riceParamPart0 & 0x01]; - riceParamPart1 = (riceParamPart1 >> 1) ^ t[riceParamPart1 & 0x01]; - riceParamPart2 = (riceParamPart2 >> 1) ^ t[riceParamPart2 & 0x01]; - riceParamPart3 = (riceParamPart3 >> 1) ^ t[riceParamPart3 & 0x01]; - pSamplesOut[0] = riceParamPart0 + ma_dr_flac__calculate_prediction_32(lpcOrder, lpcShift, coefficients, pSamplesOut + 0); - pSamplesOut[1] = riceParamPart1 + ma_dr_flac__calculate_prediction_32(lpcOrder, lpcShift, coefficients, pSamplesOut + 1); - pSamplesOut[2] = riceParamPart2 + ma_dr_flac__calculate_prediction_32(lpcOrder, lpcShift, coefficients, pSamplesOut + 2); - pSamplesOut[3] = riceParamPart3 + ma_dr_flac__calculate_prediction_32(lpcOrder, lpcShift, coefficients, pSamplesOut + 3); - pSamplesOut += 4; - } - } - i = (count & ~3); - while (i < count) { - if (!ma_dr_flac__read_rice_parts_x1(bs, riceParam, &zeroCountPart0, &riceParamPart0)) { - return MA_FALSE; - } - riceParamPart0 &= riceParamMask; - riceParamPart0 |= (zeroCountPart0 << riceParam); - riceParamPart0 = (riceParamPart0 >> 1) ^ t[riceParamPart0 & 0x01]; - if (ma_dr_flac__use_64_bit_prediction(bitsPerSample, lpcOrder, lpcPrecision)) { - pSamplesOut[0] = riceParamPart0 + ma_dr_flac__calculate_prediction_64(lpcOrder, lpcShift, coefficients, pSamplesOut + 0); - } else { - pSamplesOut[0] = riceParamPart0 + ma_dr_flac__calculate_prediction_32(lpcOrder, lpcShift, coefficients, pSamplesOut + 0); - } - i += 1; - pSamplesOut += 1; - } - return MA_TRUE; -} -#if defined(MA_DR_FLAC_SUPPORT_SSE2) -static MA_INLINE __m128i ma_dr_flac__mm_packs_interleaved_epi32(__m128i a, __m128i b) -{ - __m128i r; - r = _mm_packs_epi32(a, b); - r = _mm_shuffle_epi32(r, _MM_SHUFFLE(3, 1, 2, 0)); - r = _mm_shufflehi_epi16(r, _MM_SHUFFLE(3, 1, 2, 0)); - r = _mm_shufflelo_epi16(r, _MM_SHUFFLE(3, 1, 2, 0)); - return r; -} -#endif -#if defined(MA_DR_FLAC_SUPPORT_SSE41) -static MA_INLINE __m128i ma_dr_flac__mm_not_si128(__m128i a) -{ - return _mm_xor_si128(a, _mm_cmpeq_epi32(_mm_setzero_si128(), _mm_setzero_si128())); -} -static MA_INLINE __m128i ma_dr_flac__mm_hadd_epi32(__m128i x) -{ - __m128i x64 = _mm_add_epi32(x, _mm_shuffle_epi32(x, _MM_SHUFFLE(1, 0, 3, 2))); - __m128i x32 = _mm_shufflelo_epi16(x64, _MM_SHUFFLE(1, 0, 3, 2)); - return _mm_add_epi32(x64, x32); -} -static MA_INLINE __m128i ma_dr_flac__mm_hadd_epi64(__m128i x) -{ - return _mm_add_epi64(x, _mm_shuffle_epi32(x, _MM_SHUFFLE(1, 0, 3, 2))); -} -static MA_INLINE __m128i ma_dr_flac__mm_srai_epi64(__m128i x, int count) -{ - __m128i lo = _mm_srli_epi64(x, count); - __m128i hi = _mm_srai_epi32(x, count); - hi = _mm_and_si128(hi, _mm_set_epi32(0xFFFFFFFF, 0, 0xFFFFFFFF, 0)); - return _mm_or_si128(lo, hi); -} -static ma_bool32 ma_dr_flac__decode_samples_with_residual__rice__sse41_32(ma_dr_flac_bs* bs, ma_uint32 count, ma_uint8 riceParam, ma_uint32 order, ma_int32 shift, const ma_int32* coefficients, ma_int32* pSamplesOut) -{ - int i; - ma_uint32 riceParamMask; - ma_int32* pDecodedSamples = pSamplesOut; - ma_int32* pDecodedSamplesEnd = pSamplesOut + (count & ~3); - ma_uint32 zeroCountParts0 = 0; - ma_uint32 zeroCountParts1 = 0; - ma_uint32 zeroCountParts2 = 0; - ma_uint32 zeroCountParts3 = 0; - ma_uint32 riceParamParts0 = 0; - ma_uint32 riceParamParts1 = 0; - ma_uint32 riceParamParts2 = 0; - ma_uint32 riceParamParts3 = 0; - __m128i coefficients128_0; - __m128i coefficients128_4; - __m128i coefficients128_8; - __m128i samples128_0; - __m128i samples128_4; - __m128i samples128_8; - __m128i riceParamMask128; - const ma_uint32 t[2] = {0x00000000, 0xFFFFFFFF}; - riceParamMask = (ma_uint32)~((~0UL) << riceParam); - riceParamMask128 = _mm_set1_epi32(riceParamMask); - coefficients128_0 = _mm_setzero_si128(); - coefficients128_4 = _mm_setzero_si128(); - coefficients128_8 = _mm_setzero_si128(); - samples128_0 = _mm_setzero_si128(); - samples128_4 = _mm_setzero_si128(); - samples128_8 = _mm_setzero_si128(); -#if 1 - { - int runningOrder = order; - if (runningOrder >= 4) { - coefficients128_0 = _mm_loadu_si128((const __m128i*)(coefficients + 0)); - samples128_0 = _mm_loadu_si128((const __m128i*)(pSamplesOut - 4)); - runningOrder -= 4; - } else { - switch (runningOrder) { - case 3: coefficients128_0 = _mm_set_epi32(0, coefficients[2], coefficients[1], coefficients[0]); samples128_0 = _mm_set_epi32(pSamplesOut[-1], pSamplesOut[-2], pSamplesOut[-3], 0); break; - case 2: coefficients128_0 = _mm_set_epi32(0, 0, coefficients[1], coefficients[0]); samples128_0 = _mm_set_epi32(pSamplesOut[-1], pSamplesOut[-2], 0, 0); break; - case 1: coefficients128_0 = _mm_set_epi32(0, 0, 0, coefficients[0]); samples128_0 = _mm_set_epi32(pSamplesOut[-1], 0, 0, 0); break; - } - runningOrder = 0; - } - if (runningOrder >= 4) { - coefficients128_4 = _mm_loadu_si128((const __m128i*)(coefficients + 4)); - samples128_4 = _mm_loadu_si128((const __m128i*)(pSamplesOut - 8)); - runningOrder -= 4; - } else { - switch (runningOrder) { - case 3: coefficients128_4 = _mm_set_epi32(0, coefficients[6], coefficients[5], coefficients[4]); samples128_4 = _mm_set_epi32(pSamplesOut[-5], pSamplesOut[-6], pSamplesOut[-7], 0); break; - case 2: coefficients128_4 = _mm_set_epi32(0, 0, coefficients[5], coefficients[4]); samples128_4 = _mm_set_epi32(pSamplesOut[-5], pSamplesOut[-6], 0, 0); break; - case 1: coefficients128_4 = _mm_set_epi32(0, 0, 0, coefficients[4]); samples128_4 = _mm_set_epi32(pSamplesOut[-5], 0, 0, 0); break; - } - runningOrder = 0; - } - if (runningOrder == 4) { - coefficients128_8 = _mm_loadu_si128((const __m128i*)(coefficients + 8)); - samples128_8 = _mm_loadu_si128((const __m128i*)(pSamplesOut - 12)); - runningOrder -= 4; - } else { - switch (runningOrder) { - case 3: coefficients128_8 = _mm_set_epi32(0, coefficients[10], coefficients[9], coefficients[8]); samples128_8 = _mm_set_epi32(pSamplesOut[-9], pSamplesOut[-10], pSamplesOut[-11], 0); break; - case 2: coefficients128_8 = _mm_set_epi32(0, 0, coefficients[9], coefficients[8]); samples128_8 = _mm_set_epi32(pSamplesOut[-9], pSamplesOut[-10], 0, 0); break; - case 1: coefficients128_8 = _mm_set_epi32(0, 0, 0, coefficients[8]); samples128_8 = _mm_set_epi32(pSamplesOut[-9], 0, 0, 0); break; - } - runningOrder = 0; - } - coefficients128_0 = _mm_shuffle_epi32(coefficients128_0, _MM_SHUFFLE(0, 1, 2, 3)); - coefficients128_4 = _mm_shuffle_epi32(coefficients128_4, _MM_SHUFFLE(0, 1, 2, 3)); - coefficients128_8 = _mm_shuffle_epi32(coefficients128_8, _MM_SHUFFLE(0, 1, 2, 3)); - } -#else - switch (order) - { - case 12: ((ma_int32*)&coefficients128_8)[0] = coefficients[11]; ((ma_int32*)&samples128_8)[0] = pDecodedSamples[-12]; - case 11: ((ma_int32*)&coefficients128_8)[1] = coefficients[10]; ((ma_int32*)&samples128_8)[1] = pDecodedSamples[-11]; - case 10: ((ma_int32*)&coefficients128_8)[2] = coefficients[ 9]; ((ma_int32*)&samples128_8)[2] = pDecodedSamples[-10]; - case 9: ((ma_int32*)&coefficients128_8)[3] = coefficients[ 8]; ((ma_int32*)&samples128_8)[3] = pDecodedSamples[- 9]; - case 8: ((ma_int32*)&coefficients128_4)[0] = coefficients[ 7]; ((ma_int32*)&samples128_4)[0] = pDecodedSamples[- 8]; - case 7: ((ma_int32*)&coefficients128_4)[1] = coefficients[ 6]; ((ma_int32*)&samples128_4)[1] = pDecodedSamples[- 7]; - case 6: ((ma_int32*)&coefficients128_4)[2] = coefficients[ 5]; ((ma_int32*)&samples128_4)[2] = pDecodedSamples[- 6]; - case 5: ((ma_int32*)&coefficients128_4)[3] = coefficients[ 4]; ((ma_int32*)&samples128_4)[3] = pDecodedSamples[- 5]; - case 4: ((ma_int32*)&coefficients128_0)[0] = coefficients[ 3]; ((ma_int32*)&samples128_0)[0] = pDecodedSamples[- 4]; - case 3: ((ma_int32*)&coefficients128_0)[1] = coefficients[ 2]; ((ma_int32*)&samples128_0)[1] = pDecodedSamples[- 3]; - case 2: ((ma_int32*)&coefficients128_0)[2] = coefficients[ 1]; ((ma_int32*)&samples128_0)[2] = pDecodedSamples[- 2]; - case 1: ((ma_int32*)&coefficients128_0)[3] = coefficients[ 0]; ((ma_int32*)&samples128_0)[3] = pDecodedSamples[- 1]; - } -#endif - while (pDecodedSamples < pDecodedSamplesEnd) { - __m128i prediction128; - __m128i zeroCountPart128; - __m128i riceParamPart128; - if (!ma_dr_flac__read_rice_parts_x1(bs, riceParam, &zeroCountParts0, &riceParamParts0) || - !ma_dr_flac__read_rice_parts_x1(bs, riceParam, &zeroCountParts1, &riceParamParts1) || - !ma_dr_flac__read_rice_parts_x1(bs, riceParam, &zeroCountParts2, &riceParamParts2) || - !ma_dr_flac__read_rice_parts_x1(bs, riceParam, &zeroCountParts3, &riceParamParts3)) { - return MA_FALSE; - } - zeroCountPart128 = _mm_set_epi32(zeroCountParts3, zeroCountParts2, zeroCountParts1, zeroCountParts0); - riceParamPart128 = _mm_set_epi32(riceParamParts3, riceParamParts2, riceParamParts1, riceParamParts0); - riceParamPart128 = _mm_and_si128(riceParamPart128, riceParamMask128); - riceParamPart128 = _mm_or_si128(riceParamPart128, _mm_slli_epi32(zeroCountPart128, riceParam)); - riceParamPart128 = _mm_xor_si128(_mm_srli_epi32(riceParamPart128, 1), _mm_add_epi32(ma_dr_flac__mm_not_si128(_mm_and_si128(riceParamPart128, _mm_set1_epi32(0x01))), _mm_set1_epi32(0x01))); - if (order <= 4) { - for (i = 0; i < 4; i += 1) { - prediction128 = _mm_mullo_epi32(coefficients128_0, samples128_0); - prediction128 = ma_dr_flac__mm_hadd_epi32(prediction128); - prediction128 = _mm_srai_epi32(prediction128, shift); - prediction128 = _mm_add_epi32(riceParamPart128, prediction128); - samples128_0 = _mm_alignr_epi8(prediction128, samples128_0, 4); - riceParamPart128 = _mm_alignr_epi8(_mm_setzero_si128(), riceParamPart128, 4); - } - } else if (order <= 8) { - for (i = 0; i < 4; i += 1) { - prediction128 = _mm_mullo_epi32(coefficients128_4, samples128_4); - prediction128 = _mm_add_epi32(prediction128, _mm_mullo_epi32(coefficients128_0, samples128_0)); - prediction128 = ma_dr_flac__mm_hadd_epi32(prediction128); - prediction128 = _mm_srai_epi32(prediction128, shift); - prediction128 = _mm_add_epi32(riceParamPart128, prediction128); - samples128_4 = _mm_alignr_epi8(samples128_0, samples128_4, 4); - samples128_0 = _mm_alignr_epi8(prediction128, samples128_0, 4); - riceParamPart128 = _mm_alignr_epi8(_mm_setzero_si128(), riceParamPart128, 4); - } - } else { - for (i = 0; i < 4; i += 1) { - prediction128 = _mm_mullo_epi32(coefficients128_8, samples128_8); - prediction128 = _mm_add_epi32(prediction128, _mm_mullo_epi32(coefficients128_4, samples128_4)); - prediction128 = _mm_add_epi32(prediction128, _mm_mullo_epi32(coefficients128_0, samples128_0)); - prediction128 = ma_dr_flac__mm_hadd_epi32(prediction128); - prediction128 = _mm_srai_epi32(prediction128, shift); - prediction128 = _mm_add_epi32(riceParamPart128, prediction128); - samples128_8 = _mm_alignr_epi8(samples128_4, samples128_8, 4); - samples128_4 = _mm_alignr_epi8(samples128_0, samples128_4, 4); - samples128_0 = _mm_alignr_epi8(prediction128, samples128_0, 4); - riceParamPart128 = _mm_alignr_epi8(_mm_setzero_si128(), riceParamPart128, 4); - } - } - _mm_storeu_si128((__m128i*)pDecodedSamples, samples128_0); - pDecodedSamples += 4; - } - i = (count & ~3); - while (i < (int)count) { - if (!ma_dr_flac__read_rice_parts_x1(bs, riceParam, &zeroCountParts0, &riceParamParts0)) { - return MA_FALSE; - } - riceParamParts0 &= riceParamMask; - riceParamParts0 |= (zeroCountParts0 << riceParam); - riceParamParts0 = (riceParamParts0 >> 1) ^ t[riceParamParts0 & 0x01]; - pDecodedSamples[0] = riceParamParts0 + ma_dr_flac__calculate_prediction_32(order, shift, coefficients, pDecodedSamples); - i += 1; - pDecodedSamples += 1; - } - return MA_TRUE; -} -static ma_bool32 ma_dr_flac__decode_samples_with_residual__rice__sse41_64(ma_dr_flac_bs* bs, ma_uint32 count, ma_uint8 riceParam, ma_uint32 order, ma_int32 shift, const ma_int32* coefficients, ma_int32* pSamplesOut) -{ - int i; - ma_uint32 riceParamMask; - ma_int32* pDecodedSamples = pSamplesOut; - ma_int32* pDecodedSamplesEnd = pSamplesOut + (count & ~3); - ma_uint32 zeroCountParts0 = 0; - ma_uint32 zeroCountParts1 = 0; - ma_uint32 zeroCountParts2 = 0; - ma_uint32 zeroCountParts3 = 0; - ma_uint32 riceParamParts0 = 0; - ma_uint32 riceParamParts1 = 0; - ma_uint32 riceParamParts2 = 0; - ma_uint32 riceParamParts3 = 0; - __m128i coefficients128_0; - __m128i coefficients128_4; - __m128i coefficients128_8; - __m128i samples128_0; - __m128i samples128_4; - __m128i samples128_8; - __m128i prediction128; - __m128i riceParamMask128; - const ma_uint32 t[2] = {0x00000000, 0xFFFFFFFF}; - MA_DR_FLAC_ASSERT(order <= 12); - riceParamMask = (ma_uint32)~((~0UL) << riceParam); - riceParamMask128 = _mm_set1_epi32(riceParamMask); - prediction128 = _mm_setzero_si128(); - coefficients128_0 = _mm_setzero_si128(); - coefficients128_4 = _mm_setzero_si128(); - coefficients128_8 = _mm_setzero_si128(); - samples128_0 = _mm_setzero_si128(); - samples128_4 = _mm_setzero_si128(); - samples128_8 = _mm_setzero_si128(); -#if 1 - { - int runningOrder = order; - if (runningOrder >= 4) { - coefficients128_0 = _mm_loadu_si128((const __m128i*)(coefficients + 0)); - samples128_0 = _mm_loadu_si128((const __m128i*)(pSamplesOut - 4)); - runningOrder -= 4; - } else { - switch (runningOrder) { - case 3: coefficients128_0 = _mm_set_epi32(0, coefficients[2], coefficients[1], coefficients[0]); samples128_0 = _mm_set_epi32(pSamplesOut[-1], pSamplesOut[-2], pSamplesOut[-3], 0); break; - case 2: coefficients128_0 = _mm_set_epi32(0, 0, coefficients[1], coefficients[0]); samples128_0 = _mm_set_epi32(pSamplesOut[-1], pSamplesOut[-2], 0, 0); break; - case 1: coefficients128_0 = _mm_set_epi32(0, 0, 0, coefficients[0]); samples128_0 = _mm_set_epi32(pSamplesOut[-1], 0, 0, 0); break; - } - runningOrder = 0; - } - if (runningOrder >= 4) { - coefficients128_4 = _mm_loadu_si128((const __m128i*)(coefficients + 4)); - samples128_4 = _mm_loadu_si128((const __m128i*)(pSamplesOut - 8)); - runningOrder -= 4; - } else { - switch (runningOrder) { - case 3: coefficients128_4 = _mm_set_epi32(0, coefficients[6], coefficients[5], coefficients[4]); samples128_4 = _mm_set_epi32(pSamplesOut[-5], pSamplesOut[-6], pSamplesOut[-7], 0); break; - case 2: coefficients128_4 = _mm_set_epi32(0, 0, coefficients[5], coefficients[4]); samples128_4 = _mm_set_epi32(pSamplesOut[-5], pSamplesOut[-6], 0, 0); break; - case 1: coefficients128_4 = _mm_set_epi32(0, 0, 0, coefficients[4]); samples128_4 = _mm_set_epi32(pSamplesOut[-5], 0, 0, 0); break; - } - runningOrder = 0; - } - if (runningOrder == 4) { - coefficients128_8 = _mm_loadu_si128((const __m128i*)(coefficients + 8)); - samples128_8 = _mm_loadu_si128((const __m128i*)(pSamplesOut - 12)); - runningOrder -= 4; - } else { - switch (runningOrder) { - case 3: coefficients128_8 = _mm_set_epi32(0, coefficients[10], coefficients[9], coefficients[8]); samples128_8 = _mm_set_epi32(pSamplesOut[-9], pSamplesOut[-10], pSamplesOut[-11], 0); break; - case 2: coefficients128_8 = _mm_set_epi32(0, 0, coefficients[9], coefficients[8]); samples128_8 = _mm_set_epi32(pSamplesOut[-9], pSamplesOut[-10], 0, 0); break; - case 1: coefficients128_8 = _mm_set_epi32(0, 0, 0, coefficients[8]); samples128_8 = _mm_set_epi32(pSamplesOut[-9], 0, 0, 0); break; - } - runningOrder = 0; - } - coefficients128_0 = _mm_shuffle_epi32(coefficients128_0, _MM_SHUFFLE(0, 1, 2, 3)); - coefficients128_4 = _mm_shuffle_epi32(coefficients128_4, _MM_SHUFFLE(0, 1, 2, 3)); - coefficients128_8 = _mm_shuffle_epi32(coefficients128_8, _MM_SHUFFLE(0, 1, 2, 3)); - } -#else - switch (order) - { - case 12: ((ma_int32*)&coefficients128_8)[0] = coefficients[11]; ((ma_int32*)&samples128_8)[0] = pDecodedSamples[-12]; - case 11: ((ma_int32*)&coefficients128_8)[1] = coefficients[10]; ((ma_int32*)&samples128_8)[1] = pDecodedSamples[-11]; - case 10: ((ma_int32*)&coefficients128_8)[2] = coefficients[ 9]; ((ma_int32*)&samples128_8)[2] = pDecodedSamples[-10]; - case 9: ((ma_int32*)&coefficients128_8)[3] = coefficients[ 8]; ((ma_int32*)&samples128_8)[3] = pDecodedSamples[- 9]; - case 8: ((ma_int32*)&coefficients128_4)[0] = coefficients[ 7]; ((ma_int32*)&samples128_4)[0] = pDecodedSamples[- 8]; - case 7: ((ma_int32*)&coefficients128_4)[1] = coefficients[ 6]; ((ma_int32*)&samples128_4)[1] = pDecodedSamples[- 7]; - case 6: ((ma_int32*)&coefficients128_4)[2] = coefficients[ 5]; ((ma_int32*)&samples128_4)[2] = pDecodedSamples[- 6]; - case 5: ((ma_int32*)&coefficients128_4)[3] = coefficients[ 4]; ((ma_int32*)&samples128_4)[3] = pDecodedSamples[- 5]; - case 4: ((ma_int32*)&coefficients128_0)[0] = coefficients[ 3]; ((ma_int32*)&samples128_0)[0] = pDecodedSamples[- 4]; - case 3: ((ma_int32*)&coefficients128_0)[1] = coefficients[ 2]; ((ma_int32*)&samples128_0)[1] = pDecodedSamples[- 3]; - case 2: ((ma_int32*)&coefficients128_0)[2] = coefficients[ 1]; ((ma_int32*)&samples128_0)[2] = pDecodedSamples[- 2]; - case 1: ((ma_int32*)&coefficients128_0)[3] = coefficients[ 0]; ((ma_int32*)&samples128_0)[3] = pDecodedSamples[- 1]; - } -#endif - while (pDecodedSamples < pDecodedSamplesEnd) { - __m128i zeroCountPart128; - __m128i riceParamPart128; - if (!ma_dr_flac__read_rice_parts_x1(bs, riceParam, &zeroCountParts0, &riceParamParts0) || - !ma_dr_flac__read_rice_parts_x1(bs, riceParam, &zeroCountParts1, &riceParamParts1) || - !ma_dr_flac__read_rice_parts_x1(bs, riceParam, &zeroCountParts2, &riceParamParts2) || - !ma_dr_flac__read_rice_parts_x1(bs, riceParam, &zeroCountParts3, &riceParamParts3)) { - return MA_FALSE; - } - zeroCountPart128 = _mm_set_epi32(zeroCountParts3, zeroCountParts2, zeroCountParts1, zeroCountParts0); - riceParamPart128 = _mm_set_epi32(riceParamParts3, riceParamParts2, riceParamParts1, riceParamParts0); - riceParamPart128 = _mm_and_si128(riceParamPart128, riceParamMask128); - riceParamPart128 = _mm_or_si128(riceParamPart128, _mm_slli_epi32(zeroCountPart128, riceParam)); - riceParamPart128 = _mm_xor_si128(_mm_srli_epi32(riceParamPart128, 1), _mm_add_epi32(ma_dr_flac__mm_not_si128(_mm_and_si128(riceParamPart128, _mm_set1_epi32(1))), _mm_set1_epi32(1))); - for (i = 0; i < 4; i += 1) { - prediction128 = _mm_xor_si128(prediction128, prediction128); - switch (order) - { - case 12: - case 11: prediction128 = _mm_add_epi64(prediction128, _mm_mul_epi32(_mm_shuffle_epi32(coefficients128_8, _MM_SHUFFLE(1, 1, 0, 0)), _mm_shuffle_epi32(samples128_8, _MM_SHUFFLE(1, 1, 0, 0)))); - case 10: - case 9: prediction128 = _mm_add_epi64(prediction128, _mm_mul_epi32(_mm_shuffle_epi32(coefficients128_8, _MM_SHUFFLE(3, 3, 2, 2)), _mm_shuffle_epi32(samples128_8, _MM_SHUFFLE(3, 3, 2, 2)))); - case 8: - case 7: prediction128 = _mm_add_epi64(prediction128, _mm_mul_epi32(_mm_shuffle_epi32(coefficients128_4, _MM_SHUFFLE(1, 1, 0, 0)), _mm_shuffle_epi32(samples128_4, _MM_SHUFFLE(1, 1, 0, 0)))); - case 6: - case 5: prediction128 = _mm_add_epi64(prediction128, _mm_mul_epi32(_mm_shuffle_epi32(coefficients128_4, _MM_SHUFFLE(3, 3, 2, 2)), _mm_shuffle_epi32(samples128_4, _MM_SHUFFLE(3, 3, 2, 2)))); - case 4: - case 3: prediction128 = _mm_add_epi64(prediction128, _mm_mul_epi32(_mm_shuffle_epi32(coefficients128_0, _MM_SHUFFLE(1, 1, 0, 0)), _mm_shuffle_epi32(samples128_0, _MM_SHUFFLE(1, 1, 0, 0)))); - case 2: - case 1: prediction128 = _mm_add_epi64(prediction128, _mm_mul_epi32(_mm_shuffle_epi32(coefficients128_0, _MM_SHUFFLE(3, 3, 2, 2)), _mm_shuffle_epi32(samples128_0, _MM_SHUFFLE(3, 3, 2, 2)))); - } - prediction128 = ma_dr_flac__mm_hadd_epi64(prediction128); - prediction128 = ma_dr_flac__mm_srai_epi64(prediction128, shift); - prediction128 = _mm_add_epi32(riceParamPart128, prediction128); - samples128_8 = _mm_alignr_epi8(samples128_4, samples128_8, 4); - samples128_4 = _mm_alignr_epi8(samples128_0, samples128_4, 4); - samples128_0 = _mm_alignr_epi8(prediction128, samples128_0, 4); - riceParamPart128 = _mm_alignr_epi8(_mm_setzero_si128(), riceParamPart128, 4); - } - _mm_storeu_si128((__m128i*)pDecodedSamples, samples128_0); - pDecodedSamples += 4; - } - i = (count & ~3); - while (i < (int)count) { - if (!ma_dr_flac__read_rice_parts_x1(bs, riceParam, &zeroCountParts0, &riceParamParts0)) { - return MA_FALSE; - } - riceParamParts0 &= riceParamMask; - riceParamParts0 |= (zeroCountParts0 << riceParam); - riceParamParts0 = (riceParamParts0 >> 1) ^ t[riceParamParts0 & 0x01]; - pDecodedSamples[0] = riceParamParts0 + ma_dr_flac__calculate_prediction_64(order, shift, coefficients, pDecodedSamples); - i += 1; - pDecodedSamples += 1; - } - return MA_TRUE; -} -static ma_bool32 ma_dr_flac__decode_samples_with_residual__rice__sse41(ma_dr_flac_bs* bs, ma_uint32 bitsPerSample, ma_uint32 count, ma_uint8 riceParam, ma_uint32 lpcOrder, ma_int32 lpcShift, ma_uint32 lpcPrecision, const ma_int32* coefficients, ma_int32* pSamplesOut) -{ - MA_DR_FLAC_ASSERT(bs != NULL); - MA_DR_FLAC_ASSERT(pSamplesOut != NULL); - if (lpcOrder > 0 && lpcOrder <= 12) { - if (ma_dr_flac__use_64_bit_prediction(bitsPerSample, lpcOrder, lpcPrecision)) { - return ma_dr_flac__decode_samples_with_residual__rice__sse41_64(bs, count, riceParam, lpcOrder, lpcShift, coefficients, pSamplesOut); - } else { - return ma_dr_flac__decode_samples_with_residual__rice__sse41_32(bs, count, riceParam, lpcOrder, lpcShift, coefficients, pSamplesOut); - } - } else { - return ma_dr_flac__decode_samples_with_residual__rice__scalar(bs, bitsPerSample, count, riceParam, lpcOrder, lpcShift, lpcPrecision, coefficients, pSamplesOut); - } -} -#endif -#if defined(MA_DR_FLAC_SUPPORT_NEON) -static MA_INLINE void ma_dr_flac__vst2q_s32(ma_int32* p, int32x4x2_t x) -{ - vst1q_s32(p+0, x.val[0]); - vst1q_s32(p+4, x.val[1]); -} -static MA_INLINE void ma_dr_flac__vst2q_u32(ma_uint32* p, uint32x4x2_t x) -{ - vst1q_u32(p+0, x.val[0]); - vst1q_u32(p+4, x.val[1]); -} -static MA_INLINE void ma_dr_flac__vst2q_f32(float* p, float32x4x2_t x) -{ - vst1q_f32(p+0, x.val[0]); - vst1q_f32(p+4, x.val[1]); -} -static MA_INLINE void ma_dr_flac__vst2q_s16(ma_int16* p, int16x4x2_t x) -{ - vst1q_s16(p, vcombine_s16(x.val[0], x.val[1])); -} -static MA_INLINE void ma_dr_flac__vst2q_u16(ma_uint16* p, uint16x4x2_t x) -{ - vst1q_u16(p, vcombine_u16(x.val[0], x.val[1])); -} -static MA_INLINE int32x4_t ma_dr_flac__vdupq_n_s32x4(ma_int32 x3, ma_int32 x2, ma_int32 x1, ma_int32 x0) -{ - ma_int32 x[4]; - x[3] = x3; - x[2] = x2; - x[1] = x1; - x[0] = x0; - return vld1q_s32(x); -} -static MA_INLINE int32x4_t ma_dr_flac__valignrq_s32_1(int32x4_t a, int32x4_t b) -{ - return vextq_s32(b, a, 1); -} -static MA_INLINE uint32x4_t ma_dr_flac__valignrq_u32_1(uint32x4_t a, uint32x4_t b) -{ - return vextq_u32(b, a, 1); -} -static MA_INLINE int32x2_t ma_dr_flac__vhaddq_s32(int32x4_t x) -{ - int32x2_t r = vadd_s32(vget_high_s32(x), vget_low_s32(x)); - return vpadd_s32(r, r); -} -static MA_INLINE int64x1_t ma_dr_flac__vhaddq_s64(int64x2_t x) -{ - return vadd_s64(vget_high_s64(x), vget_low_s64(x)); -} -static MA_INLINE int32x4_t ma_dr_flac__vrevq_s32(int32x4_t x) -{ - return vrev64q_s32(vcombine_s32(vget_high_s32(x), vget_low_s32(x))); -} -static MA_INLINE int32x4_t ma_dr_flac__vnotq_s32(int32x4_t x) -{ - return veorq_s32(x, vdupq_n_s32(0xFFFFFFFF)); -} -static MA_INLINE uint32x4_t ma_dr_flac__vnotq_u32(uint32x4_t x) -{ - return veorq_u32(x, vdupq_n_u32(0xFFFFFFFF)); -} -static ma_bool32 ma_dr_flac__decode_samples_with_residual__rice__neon_32(ma_dr_flac_bs* bs, ma_uint32 count, ma_uint8 riceParam, ma_uint32 order, ma_int32 shift, const ma_int32* coefficients, ma_int32* pSamplesOut) -{ - int i; - ma_uint32 riceParamMask; - ma_int32* pDecodedSamples = pSamplesOut; - ma_int32* pDecodedSamplesEnd = pSamplesOut + (count & ~3); - ma_uint32 zeroCountParts[4]; - ma_uint32 riceParamParts[4]; - int32x4_t coefficients128_0; - int32x4_t coefficients128_4; - int32x4_t coefficients128_8; - int32x4_t samples128_0; - int32x4_t samples128_4; - int32x4_t samples128_8; - uint32x4_t riceParamMask128; - int32x4_t riceParam128; - int32x2_t shift64; - uint32x4_t one128; - const ma_uint32 t[2] = {0x00000000, 0xFFFFFFFF}; - riceParamMask = (ma_uint32)~((~0UL) << riceParam); - riceParamMask128 = vdupq_n_u32(riceParamMask); - riceParam128 = vdupq_n_s32(riceParam); - shift64 = vdup_n_s32(-shift); - one128 = vdupq_n_u32(1); - { - int runningOrder = order; - ma_int32 tempC[4] = {0, 0, 0, 0}; - ma_int32 tempS[4] = {0, 0, 0, 0}; - if (runningOrder >= 4) { - coefficients128_0 = vld1q_s32(coefficients + 0); - samples128_0 = vld1q_s32(pSamplesOut - 4); - runningOrder -= 4; - } else { - switch (runningOrder) { - case 3: tempC[2] = coefficients[2]; tempS[1] = pSamplesOut[-3]; - case 2: tempC[1] = coefficients[1]; tempS[2] = pSamplesOut[-2]; - case 1: tempC[0] = coefficients[0]; tempS[3] = pSamplesOut[-1]; - } - coefficients128_0 = vld1q_s32(tempC); - samples128_0 = vld1q_s32(tempS); - runningOrder = 0; - } - if (runningOrder >= 4) { - coefficients128_4 = vld1q_s32(coefficients + 4); - samples128_4 = vld1q_s32(pSamplesOut - 8); - runningOrder -= 4; - } else { - switch (runningOrder) { - case 3: tempC[2] = coefficients[6]; tempS[1] = pSamplesOut[-7]; - case 2: tempC[1] = coefficients[5]; tempS[2] = pSamplesOut[-6]; - case 1: tempC[0] = coefficients[4]; tempS[3] = pSamplesOut[-5]; - } - coefficients128_4 = vld1q_s32(tempC); - samples128_4 = vld1q_s32(tempS); - runningOrder = 0; - } - if (runningOrder == 4) { - coefficients128_8 = vld1q_s32(coefficients + 8); - samples128_8 = vld1q_s32(pSamplesOut - 12); - runningOrder -= 4; - } else { - switch (runningOrder) { - case 3: tempC[2] = coefficients[10]; tempS[1] = pSamplesOut[-11]; - case 2: tempC[1] = coefficients[ 9]; tempS[2] = pSamplesOut[-10]; - case 1: tempC[0] = coefficients[ 8]; tempS[3] = pSamplesOut[- 9]; - } - coefficients128_8 = vld1q_s32(tempC); - samples128_8 = vld1q_s32(tempS); - runningOrder = 0; - } - coefficients128_0 = ma_dr_flac__vrevq_s32(coefficients128_0); - coefficients128_4 = ma_dr_flac__vrevq_s32(coefficients128_4); - coefficients128_8 = ma_dr_flac__vrevq_s32(coefficients128_8); - } - while (pDecodedSamples < pDecodedSamplesEnd) { - int32x4_t prediction128; - int32x2_t prediction64; - uint32x4_t zeroCountPart128; - uint32x4_t riceParamPart128; - if (!ma_dr_flac__read_rice_parts_x1(bs, riceParam, &zeroCountParts[0], &riceParamParts[0]) || - !ma_dr_flac__read_rice_parts_x1(bs, riceParam, &zeroCountParts[1], &riceParamParts[1]) || - !ma_dr_flac__read_rice_parts_x1(bs, riceParam, &zeroCountParts[2], &riceParamParts[2]) || - !ma_dr_flac__read_rice_parts_x1(bs, riceParam, &zeroCountParts[3], &riceParamParts[3])) { - return MA_FALSE; - } - zeroCountPart128 = vld1q_u32(zeroCountParts); - riceParamPart128 = vld1q_u32(riceParamParts); - riceParamPart128 = vandq_u32(riceParamPart128, riceParamMask128); - riceParamPart128 = vorrq_u32(riceParamPart128, vshlq_u32(zeroCountPart128, riceParam128)); - riceParamPart128 = veorq_u32(vshrq_n_u32(riceParamPart128, 1), vaddq_u32(ma_dr_flac__vnotq_u32(vandq_u32(riceParamPart128, one128)), one128)); - if (order <= 4) { - for (i = 0; i < 4; i += 1) { - prediction128 = vmulq_s32(coefficients128_0, samples128_0); - prediction64 = ma_dr_flac__vhaddq_s32(prediction128); - prediction64 = vshl_s32(prediction64, shift64); - prediction64 = vadd_s32(prediction64, vget_low_s32(vreinterpretq_s32_u32(riceParamPart128))); - samples128_0 = ma_dr_flac__valignrq_s32_1(vcombine_s32(prediction64, vdup_n_s32(0)), samples128_0); - riceParamPart128 = ma_dr_flac__valignrq_u32_1(vdupq_n_u32(0), riceParamPart128); - } - } else if (order <= 8) { - for (i = 0; i < 4; i += 1) { - prediction128 = vmulq_s32(coefficients128_4, samples128_4); - prediction128 = vmlaq_s32(prediction128, coefficients128_0, samples128_0); - prediction64 = ma_dr_flac__vhaddq_s32(prediction128); - prediction64 = vshl_s32(prediction64, shift64); - prediction64 = vadd_s32(prediction64, vget_low_s32(vreinterpretq_s32_u32(riceParamPart128))); - samples128_4 = ma_dr_flac__valignrq_s32_1(samples128_0, samples128_4); - samples128_0 = ma_dr_flac__valignrq_s32_1(vcombine_s32(prediction64, vdup_n_s32(0)), samples128_0); - riceParamPart128 = ma_dr_flac__valignrq_u32_1(vdupq_n_u32(0), riceParamPart128); - } - } else { - for (i = 0; i < 4; i += 1) { - prediction128 = vmulq_s32(coefficients128_8, samples128_8); - prediction128 = vmlaq_s32(prediction128, coefficients128_4, samples128_4); - prediction128 = vmlaq_s32(prediction128, coefficients128_0, samples128_0); - prediction64 = ma_dr_flac__vhaddq_s32(prediction128); - prediction64 = vshl_s32(prediction64, shift64); - prediction64 = vadd_s32(prediction64, vget_low_s32(vreinterpretq_s32_u32(riceParamPart128))); - samples128_8 = ma_dr_flac__valignrq_s32_1(samples128_4, samples128_8); - samples128_4 = ma_dr_flac__valignrq_s32_1(samples128_0, samples128_4); - samples128_0 = ma_dr_flac__valignrq_s32_1(vcombine_s32(prediction64, vdup_n_s32(0)), samples128_0); - riceParamPart128 = ma_dr_flac__valignrq_u32_1(vdupq_n_u32(0), riceParamPart128); - } - } - vst1q_s32(pDecodedSamples, samples128_0); - pDecodedSamples += 4; - } - i = (count & ~3); - while (i < (int)count) { - if (!ma_dr_flac__read_rice_parts_x1(bs, riceParam, &zeroCountParts[0], &riceParamParts[0])) { - return MA_FALSE; - } - riceParamParts[0] &= riceParamMask; - riceParamParts[0] |= (zeroCountParts[0] << riceParam); - riceParamParts[0] = (riceParamParts[0] >> 1) ^ t[riceParamParts[0] & 0x01]; - pDecodedSamples[0] = riceParamParts[0] + ma_dr_flac__calculate_prediction_32(order, shift, coefficients, pDecodedSamples); - i += 1; - pDecodedSamples += 1; - } - return MA_TRUE; -} -static ma_bool32 ma_dr_flac__decode_samples_with_residual__rice__neon_64(ma_dr_flac_bs* bs, ma_uint32 count, ma_uint8 riceParam, ma_uint32 order, ma_int32 shift, const ma_int32* coefficients, ma_int32* pSamplesOut) -{ - int i; - ma_uint32 riceParamMask; - ma_int32* pDecodedSamples = pSamplesOut; - ma_int32* pDecodedSamplesEnd = pSamplesOut + (count & ~3); - ma_uint32 zeroCountParts[4]; - ma_uint32 riceParamParts[4]; - int32x4_t coefficients128_0; - int32x4_t coefficients128_4; - int32x4_t coefficients128_8; - int32x4_t samples128_0; - int32x4_t samples128_4; - int32x4_t samples128_8; - uint32x4_t riceParamMask128; - int32x4_t riceParam128; - int64x1_t shift64; - uint32x4_t one128; - int64x2_t prediction128 = { 0 }; - uint32x4_t zeroCountPart128; - uint32x4_t riceParamPart128; - const ma_uint32 t[2] = {0x00000000, 0xFFFFFFFF}; - riceParamMask = (ma_uint32)~((~0UL) << riceParam); - riceParamMask128 = vdupq_n_u32(riceParamMask); - riceParam128 = vdupq_n_s32(riceParam); - shift64 = vdup_n_s64(-shift); - one128 = vdupq_n_u32(1); - { - int runningOrder = order; - ma_int32 tempC[4] = {0, 0, 0, 0}; - ma_int32 tempS[4] = {0, 0, 0, 0}; - if (runningOrder >= 4) { - coefficients128_0 = vld1q_s32(coefficients + 0); - samples128_0 = vld1q_s32(pSamplesOut - 4); - runningOrder -= 4; - } else { - switch (runningOrder) { - case 3: tempC[2] = coefficients[2]; tempS[1] = pSamplesOut[-3]; - case 2: tempC[1] = coefficients[1]; tempS[2] = pSamplesOut[-2]; - case 1: tempC[0] = coefficients[0]; tempS[3] = pSamplesOut[-1]; - } - coefficients128_0 = vld1q_s32(tempC); - samples128_0 = vld1q_s32(tempS); - runningOrder = 0; - } - if (runningOrder >= 4) { - coefficients128_4 = vld1q_s32(coefficients + 4); - samples128_4 = vld1q_s32(pSamplesOut - 8); - runningOrder -= 4; - } else { - switch (runningOrder) { - case 3: tempC[2] = coefficients[6]; tempS[1] = pSamplesOut[-7]; - case 2: tempC[1] = coefficients[5]; tempS[2] = pSamplesOut[-6]; - case 1: tempC[0] = coefficients[4]; tempS[3] = pSamplesOut[-5]; - } - coefficients128_4 = vld1q_s32(tempC); - samples128_4 = vld1q_s32(tempS); - runningOrder = 0; - } - if (runningOrder == 4) { - coefficients128_8 = vld1q_s32(coefficients + 8); - samples128_8 = vld1q_s32(pSamplesOut - 12); - runningOrder -= 4; - } else { - switch (runningOrder) { - case 3: tempC[2] = coefficients[10]; tempS[1] = pSamplesOut[-11]; - case 2: tempC[1] = coefficients[ 9]; tempS[2] = pSamplesOut[-10]; - case 1: tempC[0] = coefficients[ 8]; tempS[3] = pSamplesOut[- 9]; - } - coefficients128_8 = vld1q_s32(tempC); - samples128_8 = vld1q_s32(tempS); - runningOrder = 0; - } - coefficients128_0 = ma_dr_flac__vrevq_s32(coefficients128_0); - coefficients128_4 = ma_dr_flac__vrevq_s32(coefficients128_4); - coefficients128_8 = ma_dr_flac__vrevq_s32(coefficients128_8); - } - while (pDecodedSamples < pDecodedSamplesEnd) { - if (!ma_dr_flac__read_rice_parts_x1(bs, riceParam, &zeroCountParts[0], &riceParamParts[0]) || - !ma_dr_flac__read_rice_parts_x1(bs, riceParam, &zeroCountParts[1], &riceParamParts[1]) || - !ma_dr_flac__read_rice_parts_x1(bs, riceParam, &zeroCountParts[2], &riceParamParts[2]) || - !ma_dr_flac__read_rice_parts_x1(bs, riceParam, &zeroCountParts[3], &riceParamParts[3])) { - return MA_FALSE; - } - zeroCountPart128 = vld1q_u32(zeroCountParts); - riceParamPart128 = vld1q_u32(riceParamParts); - riceParamPart128 = vandq_u32(riceParamPart128, riceParamMask128); - riceParamPart128 = vorrq_u32(riceParamPart128, vshlq_u32(zeroCountPart128, riceParam128)); - riceParamPart128 = veorq_u32(vshrq_n_u32(riceParamPart128, 1), vaddq_u32(ma_dr_flac__vnotq_u32(vandq_u32(riceParamPart128, one128)), one128)); - for (i = 0; i < 4; i += 1) { - int64x1_t prediction64; - prediction128 = veorq_s64(prediction128, prediction128); - switch (order) - { - case 12: - case 11: prediction128 = vaddq_s64(prediction128, vmull_s32(vget_low_s32(coefficients128_8), vget_low_s32(samples128_8))); - case 10: - case 9: prediction128 = vaddq_s64(prediction128, vmull_s32(vget_high_s32(coefficients128_8), vget_high_s32(samples128_8))); - case 8: - case 7: prediction128 = vaddq_s64(prediction128, vmull_s32(vget_low_s32(coefficients128_4), vget_low_s32(samples128_4))); - case 6: - case 5: prediction128 = vaddq_s64(prediction128, vmull_s32(vget_high_s32(coefficients128_4), vget_high_s32(samples128_4))); - case 4: - case 3: prediction128 = vaddq_s64(prediction128, vmull_s32(vget_low_s32(coefficients128_0), vget_low_s32(samples128_0))); - case 2: - case 1: prediction128 = vaddq_s64(prediction128, vmull_s32(vget_high_s32(coefficients128_0), vget_high_s32(samples128_0))); - } - prediction64 = ma_dr_flac__vhaddq_s64(prediction128); - prediction64 = vshl_s64(prediction64, shift64); - prediction64 = vadd_s64(prediction64, vdup_n_s64(vgetq_lane_u32(riceParamPart128, 0))); - samples128_8 = ma_dr_flac__valignrq_s32_1(samples128_4, samples128_8); - samples128_4 = ma_dr_flac__valignrq_s32_1(samples128_0, samples128_4); - samples128_0 = ma_dr_flac__valignrq_s32_1(vcombine_s32(vreinterpret_s32_s64(prediction64), vdup_n_s32(0)), samples128_0); - riceParamPart128 = ma_dr_flac__valignrq_u32_1(vdupq_n_u32(0), riceParamPart128); - } - vst1q_s32(pDecodedSamples, samples128_0); - pDecodedSamples += 4; - } - i = (count & ~3); - while (i < (int)count) { - if (!ma_dr_flac__read_rice_parts_x1(bs, riceParam, &zeroCountParts[0], &riceParamParts[0])) { - return MA_FALSE; - } - riceParamParts[0] &= riceParamMask; - riceParamParts[0] |= (zeroCountParts[0] << riceParam); - riceParamParts[0] = (riceParamParts[0] >> 1) ^ t[riceParamParts[0] & 0x01]; - pDecodedSamples[0] = riceParamParts[0] + ma_dr_flac__calculate_prediction_64(order, shift, coefficients, pDecodedSamples); - i += 1; - pDecodedSamples += 1; - } - return MA_TRUE; -} -static ma_bool32 ma_dr_flac__decode_samples_with_residual__rice__neon(ma_dr_flac_bs* bs, ma_uint32 bitsPerSample, ma_uint32 count, ma_uint8 riceParam, ma_uint32 lpcOrder, ma_int32 lpcShift, ma_uint32 lpcPrecision, const ma_int32* coefficients, ma_int32* pSamplesOut) -{ - MA_DR_FLAC_ASSERT(bs != NULL); - MA_DR_FLAC_ASSERT(pSamplesOut != NULL); - if (lpcOrder > 0 && lpcOrder <= 12) { - if (ma_dr_flac__use_64_bit_prediction(bitsPerSample, lpcOrder, lpcPrecision)) { - return ma_dr_flac__decode_samples_with_residual__rice__neon_64(bs, count, riceParam, lpcOrder, lpcShift, coefficients, pSamplesOut); - } else { - return ma_dr_flac__decode_samples_with_residual__rice__neon_32(bs, count, riceParam, lpcOrder, lpcShift, coefficients, pSamplesOut); - } - } else { - return ma_dr_flac__decode_samples_with_residual__rice__scalar(bs, bitsPerSample, count, riceParam, lpcOrder, lpcShift, lpcPrecision, coefficients, pSamplesOut); - } -} -#endif -static ma_bool32 ma_dr_flac__decode_samples_with_residual__rice(ma_dr_flac_bs* bs, ma_uint32 bitsPerSample, ma_uint32 count, ma_uint8 riceParam, ma_uint32 lpcOrder, ma_int32 lpcShift, ma_uint32 lpcPrecision, const ma_int32* coefficients, ma_int32* pSamplesOut) -{ -#if defined(MA_DR_FLAC_SUPPORT_SSE41) - if (ma_dr_flac__gIsSSE41Supported) { - return ma_dr_flac__decode_samples_with_residual__rice__sse41(bs, bitsPerSample, count, riceParam, lpcOrder, lpcShift, lpcPrecision, coefficients, pSamplesOut); - } else -#elif defined(MA_DR_FLAC_SUPPORT_NEON) - if (ma_dr_flac__gIsNEONSupported) { - return ma_dr_flac__decode_samples_with_residual__rice__neon(bs, bitsPerSample, count, riceParam, lpcOrder, lpcShift, lpcPrecision, coefficients, pSamplesOut); - } else -#endif - { - #if 0 - return ma_dr_flac__decode_samples_with_residual__rice__reference(bs, bitsPerSample, count, riceParam, lpcOrder, lpcShift, lpcPrecision, coefficients, pSamplesOut); - #else - return ma_dr_flac__decode_samples_with_residual__rice__scalar(bs, bitsPerSample, count, riceParam, lpcOrder, lpcShift, lpcPrecision, coefficients, pSamplesOut); - #endif - } -} -static ma_bool32 ma_dr_flac__read_and_seek_residual__rice(ma_dr_flac_bs* bs, ma_uint32 count, ma_uint8 riceParam) -{ - ma_uint32 i; - MA_DR_FLAC_ASSERT(bs != NULL); - for (i = 0; i < count; ++i) { - if (!ma_dr_flac__seek_rice_parts(bs, riceParam)) { - return MA_FALSE; - } - } - return MA_TRUE; -} -#if defined(__clang__) -__attribute__((no_sanitize("signed-integer-overflow"))) -#endif -static ma_bool32 ma_dr_flac__decode_samples_with_residual__unencoded(ma_dr_flac_bs* bs, ma_uint32 bitsPerSample, ma_uint32 count, ma_uint8 unencodedBitsPerSample, ma_uint32 lpcOrder, ma_int32 lpcShift, ma_uint32 lpcPrecision, const ma_int32* coefficients, ma_int32* pSamplesOut) -{ - ma_uint32 i; - MA_DR_FLAC_ASSERT(bs != NULL); - MA_DR_FLAC_ASSERT(unencodedBitsPerSample <= 31); - MA_DR_FLAC_ASSERT(pSamplesOut != NULL); - for (i = 0; i < count; ++i) { - if (unencodedBitsPerSample > 0) { - if (!ma_dr_flac__read_int32(bs, unencodedBitsPerSample, pSamplesOut + i)) { - return MA_FALSE; - } - } else { - pSamplesOut[i] = 0; - } - if (ma_dr_flac__use_64_bit_prediction(bitsPerSample, lpcOrder, lpcPrecision)) { - pSamplesOut[i] += ma_dr_flac__calculate_prediction_64(lpcOrder, lpcShift, coefficients, pSamplesOut + i); - } else { - pSamplesOut[i] += ma_dr_flac__calculate_prediction_32(lpcOrder, lpcShift, coefficients, pSamplesOut + i); - } - } - return MA_TRUE; -} -static ma_bool32 ma_dr_flac__decode_samples_with_residual(ma_dr_flac_bs* bs, ma_uint32 bitsPerSample, ma_uint32 blockSize, ma_uint32 lpcOrder, ma_int32 lpcShift, ma_uint32 lpcPrecision, const ma_int32* coefficients, ma_int32* pDecodedSamples) -{ - ma_uint8 residualMethod; - ma_uint8 partitionOrder; - ma_uint32 samplesInPartition; - ma_uint32 partitionsRemaining; - MA_DR_FLAC_ASSERT(bs != NULL); - MA_DR_FLAC_ASSERT(blockSize != 0); - MA_DR_FLAC_ASSERT(pDecodedSamples != NULL); - if (!ma_dr_flac__read_uint8(bs, 2, &residualMethod)) { - return MA_FALSE; - } - if (residualMethod != MA_DR_FLAC_RESIDUAL_CODING_METHOD_PARTITIONED_RICE && residualMethod != MA_DR_FLAC_RESIDUAL_CODING_METHOD_PARTITIONED_RICE2) { - return MA_FALSE; - } - pDecodedSamples += lpcOrder; - if (!ma_dr_flac__read_uint8(bs, 4, &partitionOrder)) { - return MA_FALSE; - } - if (partitionOrder > 8) { - return MA_FALSE; - } - if ((blockSize / (1 << partitionOrder)) < lpcOrder) { - return MA_FALSE; - } - samplesInPartition = (blockSize / (1 << partitionOrder)) - lpcOrder; - partitionsRemaining = (1 << partitionOrder); - for (;;) { - ma_uint8 riceParam = 0; - if (residualMethod == MA_DR_FLAC_RESIDUAL_CODING_METHOD_PARTITIONED_RICE) { - if (!ma_dr_flac__read_uint8(bs, 4, &riceParam)) { - return MA_FALSE; - } - if (riceParam == 15) { - riceParam = 0xFF; - } - } else if (residualMethod == MA_DR_FLAC_RESIDUAL_CODING_METHOD_PARTITIONED_RICE2) { - if (!ma_dr_flac__read_uint8(bs, 5, &riceParam)) { - return MA_FALSE; - } - if (riceParam == 31) { - riceParam = 0xFF; - } - } - if (riceParam != 0xFF) { - if (!ma_dr_flac__decode_samples_with_residual__rice(bs, bitsPerSample, samplesInPartition, riceParam, lpcOrder, lpcShift, lpcPrecision, coefficients, pDecodedSamples)) { - return MA_FALSE; - } - } else { - ma_uint8 unencodedBitsPerSample = 0; - if (!ma_dr_flac__read_uint8(bs, 5, &unencodedBitsPerSample)) { - return MA_FALSE; - } - if (!ma_dr_flac__decode_samples_with_residual__unencoded(bs, bitsPerSample, samplesInPartition, unencodedBitsPerSample, lpcOrder, lpcShift, lpcPrecision, coefficients, pDecodedSamples)) { - return MA_FALSE; - } - } - pDecodedSamples += samplesInPartition; - if (partitionsRemaining == 1) { - break; - } - partitionsRemaining -= 1; - if (partitionOrder != 0) { - samplesInPartition = blockSize / (1 << partitionOrder); - } - } - return MA_TRUE; -} -static ma_bool32 ma_dr_flac__read_and_seek_residual(ma_dr_flac_bs* bs, ma_uint32 blockSize, ma_uint32 order) -{ - ma_uint8 residualMethod; - ma_uint8 partitionOrder; - ma_uint32 samplesInPartition; - ma_uint32 partitionsRemaining; - MA_DR_FLAC_ASSERT(bs != NULL); - MA_DR_FLAC_ASSERT(blockSize != 0); - if (!ma_dr_flac__read_uint8(bs, 2, &residualMethod)) { - return MA_FALSE; - } - if (residualMethod != MA_DR_FLAC_RESIDUAL_CODING_METHOD_PARTITIONED_RICE && residualMethod != MA_DR_FLAC_RESIDUAL_CODING_METHOD_PARTITIONED_RICE2) { - return MA_FALSE; - } - if (!ma_dr_flac__read_uint8(bs, 4, &partitionOrder)) { - return MA_FALSE; - } - if (partitionOrder > 8) { - return MA_FALSE; - } - if ((blockSize / (1 << partitionOrder)) <= order) { - return MA_FALSE; - } - samplesInPartition = (blockSize / (1 << partitionOrder)) - order; - partitionsRemaining = (1 << partitionOrder); - for (;;) - { - ma_uint8 riceParam = 0; - if (residualMethod == MA_DR_FLAC_RESIDUAL_CODING_METHOD_PARTITIONED_RICE) { - if (!ma_dr_flac__read_uint8(bs, 4, &riceParam)) { - return MA_FALSE; - } - if (riceParam == 15) { - riceParam = 0xFF; - } - } else if (residualMethod == MA_DR_FLAC_RESIDUAL_CODING_METHOD_PARTITIONED_RICE2) { - if (!ma_dr_flac__read_uint8(bs, 5, &riceParam)) { - return MA_FALSE; - } - if (riceParam == 31) { - riceParam = 0xFF; - } - } - if (riceParam != 0xFF) { - if (!ma_dr_flac__read_and_seek_residual__rice(bs, samplesInPartition, riceParam)) { - return MA_FALSE; - } - } else { - ma_uint8 unencodedBitsPerSample = 0; - if (!ma_dr_flac__read_uint8(bs, 5, &unencodedBitsPerSample)) { - return MA_FALSE; - } - if (!ma_dr_flac__seek_bits(bs, unencodedBitsPerSample * samplesInPartition)) { - return MA_FALSE; - } - } - if (partitionsRemaining == 1) { - break; - } - partitionsRemaining -= 1; - samplesInPartition = blockSize / (1 << partitionOrder); - } - return MA_TRUE; -} -static ma_bool32 ma_dr_flac__decode_samples__constant(ma_dr_flac_bs* bs, ma_uint32 blockSize, ma_uint32 subframeBitsPerSample, ma_int32* pDecodedSamples) -{ - ma_uint32 i; - ma_int32 sample; - if (!ma_dr_flac__read_int32(bs, subframeBitsPerSample, &sample)) { - return MA_FALSE; - } - for (i = 0; i < blockSize; ++i) { - pDecodedSamples[i] = sample; - } - return MA_TRUE; -} -static ma_bool32 ma_dr_flac__decode_samples__verbatim(ma_dr_flac_bs* bs, ma_uint32 blockSize, ma_uint32 subframeBitsPerSample, ma_int32* pDecodedSamples) -{ - ma_uint32 i; - for (i = 0; i < blockSize; ++i) { - ma_int32 sample; - if (!ma_dr_flac__read_int32(bs, subframeBitsPerSample, &sample)) { - return MA_FALSE; - } - pDecodedSamples[i] = sample; - } - return MA_TRUE; -} -static ma_bool32 ma_dr_flac__decode_samples__fixed(ma_dr_flac_bs* bs, ma_uint32 blockSize, ma_uint32 subframeBitsPerSample, ma_uint8 lpcOrder, ma_int32* pDecodedSamples) -{ - ma_uint32 i; - static ma_int32 lpcCoefficientsTable[5][4] = { - {0, 0, 0, 0}, - {1, 0, 0, 0}, - {2, -1, 0, 0}, - {3, -3, 1, 0}, - {4, -6, 4, -1} - }; - for (i = 0; i < lpcOrder; ++i) { - ma_int32 sample; - if (!ma_dr_flac__read_int32(bs, subframeBitsPerSample, &sample)) { - return MA_FALSE; - } - pDecodedSamples[i] = sample; - } - if (!ma_dr_flac__decode_samples_with_residual(bs, subframeBitsPerSample, blockSize, lpcOrder, 0, 4, lpcCoefficientsTable[lpcOrder], pDecodedSamples)) { - return MA_FALSE; - } - return MA_TRUE; -} -static ma_bool32 ma_dr_flac__decode_samples__lpc(ma_dr_flac_bs* bs, ma_uint32 blockSize, ma_uint32 bitsPerSample, ma_uint8 lpcOrder, ma_int32* pDecodedSamples) -{ - ma_uint8 i; - ma_uint8 lpcPrecision; - ma_int8 lpcShift; - ma_int32 coefficients[32]; - for (i = 0; i < lpcOrder; ++i) { - ma_int32 sample; - if (!ma_dr_flac__read_int32(bs, bitsPerSample, &sample)) { - return MA_FALSE; - } - pDecodedSamples[i] = sample; - } - if (!ma_dr_flac__read_uint8(bs, 4, &lpcPrecision)) { - return MA_FALSE; - } - if (lpcPrecision == 15) { - return MA_FALSE; - } - lpcPrecision += 1; - if (!ma_dr_flac__read_int8(bs, 5, &lpcShift)) { - return MA_FALSE; - } - if (lpcShift < 0) { - return MA_FALSE; - } - MA_DR_FLAC_ZERO_MEMORY(coefficients, sizeof(coefficients)); - for (i = 0; i < lpcOrder; ++i) { - if (!ma_dr_flac__read_int32(bs, lpcPrecision, coefficients + i)) { - return MA_FALSE; - } - } - if (!ma_dr_flac__decode_samples_with_residual(bs, bitsPerSample, blockSize, lpcOrder, lpcShift, lpcPrecision, coefficients, pDecodedSamples)) { - return MA_FALSE; - } - return MA_TRUE; -} -static ma_bool32 ma_dr_flac__read_next_flac_frame_header(ma_dr_flac_bs* bs, ma_uint8 streaminfoBitsPerSample, ma_dr_flac_frame_header* header) -{ - const ma_uint32 sampleRateTable[12] = {0, 88200, 176400, 192000, 8000, 16000, 22050, 24000, 32000, 44100, 48000, 96000}; - const ma_uint8 bitsPerSampleTable[8] = {0, 8, 12, (ma_uint8)-1, 16, 20, 24, (ma_uint8)-1}; - MA_DR_FLAC_ASSERT(bs != NULL); - MA_DR_FLAC_ASSERT(header != NULL); - for (;;) { - ma_uint8 crc8 = 0xCE; - ma_uint8 reserved = 0; - ma_uint8 blockingStrategy = 0; - ma_uint8 blockSize = 0; - ma_uint8 sampleRate = 0; - ma_uint8 channelAssignment = 0; - ma_uint8 bitsPerSample = 0; - ma_bool32 isVariableBlockSize; - if (!ma_dr_flac__find_and_seek_to_next_sync_code(bs)) { - return MA_FALSE; - } - if (!ma_dr_flac__read_uint8(bs, 1, &reserved)) { - return MA_FALSE; - } - if (reserved == 1) { - continue; - } - crc8 = ma_dr_flac_crc8(crc8, reserved, 1); - if (!ma_dr_flac__read_uint8(bs, 1, &blockingStrategy)) { - return MA_FALSE; - } - crc8 = ma_dr_flac_crc8(crc8, blockingStrategy, 1); - if (!ma_dr_flac__read_uint8(bs, 4, &blockSize)) { - return MA_FALSE; - } - if (blockSize == 0) { - continue; - } - crc8 = ma_dr_flac_crc8(crc8, blockSize, 4); - if (!ma_dr_flac__read_uint8(bs, 4, &sampleRate)) { - return MA_FALSE; - } - crc8 = ma_dr_flac_crc8(crc8, sampleRate, 4); - if (!ma_dr_flac__read_uint8(bs, 4, &channelAssignment)) { - return MA_FALSE; - } - if (channelAssignment > 10) { - continue; - } - crc8 = ma_dr_flac_crc8(crc8, channelAssignment, 4); - if (!ma_dr_flac__read_uint8(bs, 3, &bitsPerSample)) { - return MA_FALSE; - } - if (bitsPerSample == 3 || bitsPerSample == 7) { - continue; - } - crc8 = ma_dr_flac_crc8(crc8, bitsPerSample, 3); - if (!ma_dr_flac__read_uint8(bs, 1, &reserved)) { - return MA_FALSE; - } - if (reserved == 1) { - continue; - } - crc8 = ma_dr_flac_crc8(crc8, reserved, 1); - isVariableBlockSize = blockingStrategy == 1; - if (isVariableBlockSize) { - ma_uint64 pcmFrameNumber; - ma_result result = ma_dr_flac__read_utf8_coded_number(bs, &pcmFrameNumber, &crc8); - if (result != MA_SUCCESS) { - if (result == MA_AT_END) { - return MA_FALSE; - } else { - continue; - } - } - header->flacFrameNumber = 0; - header->pcmFrameNumber = pcmFrameNumber; - } else { - ma_uint64 flacFrameNumber = 0; - ma_result result = ma_dr_flac__read_utf8_coded_number(bs, &flacFrameNumber, &crc8); - if (result != MA_SUCCESS) { - if (result == MA_AT_END) { - return MA_FALSE; - } else { - continue; - } - } - header->flacFrameNumber = (ma_uint32)flacFrameNumber; - header->pcmFrameNumber = 0; - } - MA_DR_FLAC_ASSERT(blockSize > 0); - if (blockSize == 1) { - header->blockSizeInPCMFrames = 192; - } else if (blockSize <= 5) { - MA_DR_FLAC_ASSERT(blockSize >= 2); - header->blockSizeInPCMFrames = 576 * (1 << (blockSize - 2)); - } else if (blockSize == 6) { - if (!ma_dr_flac__read_uint16(bs, 8, &header->blockSizeInPCMFrames)) { - return MA_FALSE; - } - crc8 = ma_dr_flac_crc8(crc8, header->blockSizeInPCMFrames, 8); - header->blockSizeInPCMFrames += 1; - } else if (blockSize == 7) { - if (!ma_dr_flac__read_uint16(bs, 16, &header->blockSizeInPCMFrames)) { - return MA_FALSE; - } - crc8 = ma_dr_flac_crc8(crc8, header->blockSizeInPCMFrames, 16); - if (header->blockSizeInPCMFrames == 0xFFFF) { - return MA_FALSE; - } - header->blockSizeInPCMFrames += 1; - } else { - MA_DR_FLAC_ASSERT(blockSize >= 8); - header->blockSizeInPCMFrames = 256 * (1 << (blockSize - 8)); - } - if (sampleRate <= 11) { - header->sampleRate = sampleRateTable[sampleRate]; - } else if (sampleRate == 12) { - if (!ma_dr_flac__read_uint32(bs, 8, &header->sampleRate)) { - return MA_FALSE; - } - crc8 = ma_dr_flac_crc8(crc8, header->sampleRate, 8); - header->sampleRate *= 1000; - } else if (sampleRate == 13) { - if (!ma_dr_flac__read_uint32(bs, 16, &header->sampleRate)) { - return MA_FALSE; - } - crc8 = ma_dr_flac_crc8(crc8, header->sampleRate, 16); - } else if (sampleRate == 14) { - if (!ma_dr_flac__read_uint32(bs, 16, &header->sampleRate)) { - return MA_FALSE; - } - crc8 = ma_dr_flac_crc8(crc8, header->sampleRate, 16); - header->sampleRate *= 10; - } else { - continue; - } - header->channelAssignment = channelAssignment; - header->bitsPerSample = bitsPerSampleTable[bitsPerSample]; - if (header->bitsPerSample == 0) { - header->bitsPerSample = streaminfoBitsPerSample; - } - if (header->bitsPerSample != streaminfoBitsPerSample) { - return MA_FALSE; - } - if (!ma_dr_flac__read_uint8(bs, 8, &header->crc8)) { - return MA_FALSE; - } -#ifndef MA_DR_FLAC_NO_CRC - if (header->crc8 != crc8) { - continue; - } -#endif - return MA_TRUE; - } -} -static ma_bool32 ma_dr_flac__read_subframe_header(ma_dr_flac_bs* bs, ma_dr_flac_subframe* pSubframe) -{ - ma_uint8 header; - int type; - if (!ma_dr_flac__read_uint8(bs, 8, &header)) { - return MA_FALSE; - } - if ((header & 0x80) != 0) { - return MA_FALSE; - } - pSubframe->lpcOrder = 0; - type = (header & 0x7E) >> 1; - if (type == 0) { - pSubframe->subframeType = MA_DR_FLAC_SUBFRAME_CONSTANT; - } else if (type == 1) { - pSubframe->subframeType = MA_DR_FLAC_SUBFRAME_VERBATIM; - } else { - if ((type & 0x20) != 0) { - pSubframe->subframeType = MA_DR_FLAC_SUBFRAME_LPC; - pSubframe->lpcOrder = (ma_uint8)(type & 0x1F) + 1; - } else if ((type & 0x08) != 0) { - pSubframe->subframeType = MA_DR_FLAC_SUBFRAME_FIXED; - pSubframe->lpcOrder = (ma_uint8)(type & 0x07); - if (pSubframe->lpcOrder > 4) { - pSubframe->subframeType = MA_DR_FLAC_SUBFRAME_RESERVED; - pSubframe->lpcOrder = 0; - } - } else { - pSubframe->subframeType = MA_DR_FLAC_SUBFRAME_RESERVED; - } - } - if (pSubframe->subframeType == MA_DR_FLAC_SUBFRAME_RESERVED) { - return MA_FALSE; - } - pSubframe->wastedBitsPerSample = 0; - if ((header & 0x01) == 1) { - unsigned int wastedBitsPerSample; - if (!ma_dr_flac__seek_past_next_set_bit(bs, &wastedBitsPerSample)) { - return MA_FALSE; - } - pSubframe->wastedBitsPerSample = (ma_uint8)wastedBitsPerSample + 1; - } - return MA_TRUE; -} -static ma_bool32 ma_dr_flac__decode_subframe(ma_dr_flac_bs* bs, ma_dr_flac_frame* frame, int subframeIndex, ma_int32* pDecodedSamplesOut) -{ - ma_dr_flac_subframe* pSubframe; - ma_uint32 subframeBitsPerSample; - MA_DR_FLAC_ASSERT(bs != NULL); - MA_DR_FLAC_ASSERT(frame != NULL); - pSubframe = frame->subframes + subframeIndex; - if (!ma_dr_flac__read_subframe_header(bs, pSubframe)) { - return MA_FALSE; - } - subframeBitsPerSample = frame->header.bitsPerSample; - if ((frame->header.channelAssignment == MA_DR_FLAC_CHANNEL_ASSIGNMENT_LEFT_SIDE || frame->header.channelAssignment == MA_DR_FLAC_CHANNEL_ASSIGNMENT_MID_SIDE) && subframeIndex == 1) { - subframeBitsPerSample += 1; - } else if (frame->header.channelAssignment == MA_DR_FLAC_CHANNEL_ASSIGNMENT_RIGHT_SIDE && subframeIndex == 0) { - subframeBitsPerSample += 1; - } - if (subframeBitsPerSample > 32) { - return MA_FALSE; - } - if (pSubframe->wastedBitsPerSample >= subframeBitsPerSample) { - return MA_FALSE; - } - subframeBitsPerSample -= pSubframe->wastedBitsPerSample; - pSubframe->pSamplesS32 = pDecodedSamplesOut; - if (frame->header.blockSizeInPCMFrames < pSubframe->lpcOrder) { - return MA_FALSE; - } - switch (pSubframe->subframeType) - { - case MA_DR_FLAC_SUBFRAME_CONSTANT: - { - ma_dr_flac__decode_samples__constant(bs, frame->header.blockSizeInPCMFrames, subframeBitsPerSample, pSubframe->pSamplesS32); - } break; - case MA_DR_FLAC_SUBFRAME_VERBATIM: - { - ma_dr_flac__decode_samples__verbatim(bs, frame->header.blockSizeInPCMFrames, subframeBitsPerSample, pSubframe->pSamplesS32); - } break; - case MA_DR_FLAC_SUBFRAME_FIXED: - { - ma_dr_flac__decode_samples__fixed(bs, frame->header.blockSizeInPCMFrames, subframeBitsPerSample, pSubframe->lpcOrder, pSubframe->pSamplesS32); - } break; - case MA_DR_FLAC_SUBFRAME_LPC: - { - ma_dr_flac__decode_samples__lpc(bs, frame->header.blockSizeInPCMFrames, subframeBitsPerSample, pSubframe->lpcOrder, pSubframe->pSamplesS32); - } break; - default: return MA_FALSE; - } - return MA_TRUE; -} -static ma_bool32 ma_dr_flac__seek_subframe(ma_dr_flac_bs* bs, ma_dr_flac_frame* frame, int subframeIndex) -{ - ma_dr_flac_subframe* pSubframe; - ma_uint32 subframeBitsPerSample; - MA_DR_FLAC_ASSERT(bs != NULL); - MA_DR_FLAC_ASSERT(frame != NULL); - pSubframe = frame->subframes + subframeIndex; - if (!ma_dr_flac__read_subframe_header(bs, pSubframe)) { - return MA_FALSE; - } - subframeBitsPerSample = frame->header.bitsPerSample; - if ((frame->header.channelAssignment == MA_DR_FLAC_CHANNEL_ASSIGNMENT_LEFT_SIDE || frame->header.channelAssignment == MA_DR_FLAC_CHANNEL_ASSIGNMENT_MID_SIDE) && subframeIndex == 1) { - subframeBitsPerSample += 1; - } else if (frame->header.channelAssignment == MA_DR_FLAC_CHANNEL_ASSIGNMENT_RIGHT_SIDE && subframeIndex == 0) { - subframeBitsPerSample += 1; - } - if (pSubframe->wastedBitsPerSample >= subframeBitsPerSample) { - return MA_FALSE; - } - subframeBitsPerSample -= pSubframe->wastedBitsPerSample; - pSubframe->pSamplesS32 = NULL; - switch (pSubframe->subframeType) - { - case MA_DR_FLAC_SUBFRAME_CONSTANT: - { - if (!ma_dr_flac__seek_bits(bs, subframeBitsPerSample)) { - return MA_FALSE; - } - } break; - case MA_DR_FLAC_SUBFRAME_VERBATIM: - { - unsigned int bitsToSeek = frame->header.blockSizeInPCMFrames * subframeBitsPerSample; - if (!ma_dr_flac__seek_bits(bs, bitsToSeek)) { - return MA_FALSE; - } - } break; - case MA_DR_FLAC_SUBFRAME_FIXED: - { - unsigned int bitsToSeek = pSubframe->lpcOrder * subframeBitsPerSample; - if (!ma_dr_flac__seek_bits(bs, bitsToSeek)) { - return MA_FALSE; - } - if (!ma_dr_flac__read_and_seek_residual(bs, frame->header.blockSizeInPCMFrames, pSubframe->lpcOrder)) { - return MA_FALSE; - } - } break; - case MA_DR_FLAC_SUBFRAME_LPC: - { - ma_uint8 lpcPrecision; - unsigned int bitsToSeek = pSubframe->lpcOrder * subframeBitsPerSample; - if (!ma_dr_flac__seek_bits(bs, bitsToSeek)) { - return MA_FALSE; - } - if (!ma_dr_flac__read_uint8(bs, 4, &lpcPrecision)) { - return MA_FALSE; - } - if (lpcPrecision == 15) { - return MA_FALSE; - } - lpcPrecision += 1; - bitsToSeek = (pSubframe->lpcOrder * lpcPrecision) + 5; - if (!ma_dr_flac__seek_bits(bs, bitsToSeek)) { - return MA_FALSE; - } - if (!ma_dr_flac__read_and_seek_residual(bs, frame->header.blockSizeInPCMFrames, pSubframe->lpcOrder)) { - return MA_FALSE; - } - } break; - default: return MA_FALSE; - } - return MA_TRUE; -} -static MA_INLINE ma_uint8 ma_dr_flac__get_channel_count_from_channel_assignment(ma_int8 channelAssignment) -{ - ma_uint8 lookup[] = {1, 2, 3, 4, 5, 6, 7, 8, 2, 2, 2}; - MA_DR_FLAC_ASSERT(channelAssignment <= 10); - return lookup[channelAssignment]; -} -static ma_result ma_dr_flac__decode_flac_frame(ma_dr_flac* pFlac) -{ - int channelCount; - int i; - ma_uint8 paddingSizeInBits; - ma_uint16 desiredCRC16; -#ifndef MA_DR_FLAC_NO_CRC - ma_uint16 actualCRC16; -#endif - MA_DR_FLAC_ZERO_MEMORY(pFlac->currentFLACFrame.subframes, sizeof(pFlac->currentFLACFrame.subframes)); - if (pFlac->currentFLACFrame.header.blockSizeInPCMFrames > pFlac->maxBlockSizeInPCMFrames) { - return MA_ERROR; - } - channelCount = ma_dr_flac__get_channel_count_from_channel_assignment(pFlac->currentFLACFrame.header.channelAssignment); - if (channelCount != (int)pFlac->channels) { - return MA_ERROR; - } - for (i = 0; i < channelCount; ++i) { - if (!ma_dr_flac__decode_subframe(&pFlac->bs, &pFlac->currentFLACFrame, i, pFlac->pDecodedSamples + (pFlac->currentFLACFrame.header.blockSizeInPCMFrames * i))) { - return MA_ERROR; - } - } - paddingSizeInBits = (ma_uint8)(MA_DR_FLAC_CACHE_L1_BITS_REMAINING(&pFlac->bs) & 7); - if (paddingSizeInBits > 0) { - ma_uint8 padding = 0; - if (!ma_dr_flac__read_uint8(&pFlac->bs, paddingSizeInBits, &padding)) { - return MA_AT_END; - } - } -#ifndef MA_DR_FLAC_NO_CRC - actualCRC16 = ma_dr_flac__flush_crc16(&pFlac->bs); -#endif - if (!ma_dr_flac__read_uint16(&pFlac->bs, 16, &desiredCRC16)) { - return MA_AT_END; - } -#ifndef MA_DR_FLAC_NO_CRC - if (actualCRC16 != desiredCRC16) { - return MA_CRC_MISMATCH; - } -#endif - pFlac->currentFLACFrame.pcmFramesRemaining = pFlac->currentFLACFrame.header.blockSizeInPCMFrames; - return MA_SUCCESS; -} -static ma_result ma_dr_flac__seek_flac_frame(ma_dr_flac* pFlac) -{ - int channelCount; - int i; - ma_uint16 desiredCRC16; -#ifndef MA_DR_FLAC_NO_CRC - ma_uint16 actualCRC16; -#endif - channelCount = ma_dr_flac__get_channel_count_from_channel_assignment(pFlac->currentFLACFrame.header.channelAssignment); - for (i = 0; i < channelCount; ++i) { - if (!ma_dr_flac__seek_subframe(&pFlac->bs, &pFlac->currentFLACFrame, i)) { - return MA_ERROR; - } - } - if (!ma_dr_flac__seek_bits(&pFlac->bs, MA_DR_FLAC_CACHE_L1_BITS_REMAINING(&pFlac->bs) & 7)) { - return MA_ERROR; - } -#ifndef MA_DR_FLAC_NO_CRC - actualCRC16 = ma_dr_flac__flush_crc16(&pFlac->bs); -#endif - if (!ma_dr_flac__read_uint16(&pFlac->bs, 16, &desiredCRC16)) { - return MA_AT_END; - } -#ifndef MA_DR_FLAC_NO_CRC - if (actualCRC16 != desiredCRC16) { - return MA_CRC_MISMATCH; - } -#endif - return MA_SUCCESS; -} -static ma_bool32 ma_dr_flac__read_and_decode_next_flac_frame(ma_dr_flac* pFlac) -{ - MA_DR_FLAC_ASSERT(pFlac != NULL); - for (;;) { - ma_result result; - if (!ma_dr_flac__read_next_flac_frame_header(&pFlac->bs, pFlac->bitsPerSample, &pFlac->currentFLACFrame.header)) { - return MA_FALSE; - } - result = ma_dr_flac__decode_flac_frame(pFlac); - if (result != MA_SUCCESS) { - if (result == MA_CRC_MISMATCH) { - continue; - } else { - return MA_FALSE; - } - } - return MA_TRUE; - } -} -static void ma_dr_flac__get_pcm_frame_range_of_current_flac_frame(ma_dr_flac* pFlac, ma_uint64* pFirstPCMFrame, ma_uint64* pLastPCMFrame) -{ - ma_uint64 firstPCMFrame; - ma_uint64 lastPCMFrame; - MA_DR_FLAC_ASSERT(pFlac != NULL); - firstPCMFrame = pFlac->currentFLACFrame.header.pcmFrameNumber; - if (firstPCMFrame == 0) { - firstPCMFrame = ((ma_uint64)pFlac->currentFLACFrame.header.flacFrameNumber) * pFlac->maxBlockSizeInPCMFrames; - } - lastPCMFrame = firstPCMFrame + pFlac->currentFLACFrame.header.blockSizeInPCMFrames; - if (lastPCMFrame > 0) { - lastPCMFrame -= 1; - } - if (pFirstPCMFrame) { - *pFirstPCMFrame = firstPCMFrame; - } - if (pLastPCMFrame) { - *pLastPCMFrame = lastPCMFrame; - } -} -static ma_bool32 ma_dr_flac__seek_to_first_frame(ma_dr_flac* pFlac) -{ - ma_bool32 result; - MA_DR_FLAC_ASSERT(pFlac != NULL); - result = ma_dr_flac__seek_to_byte(&pFlac->bs, pFlac->firstFLACFramePosInBytes); - MA_DR_FLAC_ZERO_MEMORY(&pFlac->currentFLACFrame, sizeof(pFlac->currentFLACFrame)); - pFlac->currentPCMFrame = 0; - return result; -} -static MA_INLINE ma_result ma_dr_flac__seek_to_next_flac_frame(ma_dr_flac* pFlac) -{ - MA_DR_FLAC_ASSERT(pFlac != NULL); - return ma_dr_flac__seek_flac_frame(pFlac); -} -static ma_uint64 ma_dr_flac__seek_forward_by_pcm_frames(ma_dr_flac* pFlac, ma_uint64 pcmFramesToSeek) -{ - ma_uint64 pcmFramesRead = 0; - while (pcmFramesToSeek > 0) { - if (pFlac->currentFLACFrame.pcmFramesRemaining == 0) { - if (!ma_dr_flac__read_and_decode_next_flac_frame(pFlac)) { - break; - } - } else { - if (pFlac->currentFLACFrame.pcmFramesRemaining > pcmFramesToSeek) { - pcmFramesRead += pcmFramesToSeek; - pFlac->currentFLACFrame.pcmFramesRemaining -= (ma_uint32)pcmFramesToSeek; - pcmFramesToSeek = 0; - } else { - pcmFramesRead += pFlac->currentFLACFrame.pcmFramesRemaining; - pcmFramesToSeek -= pFlac->currentFLACFrame.pcmFramesRemaining; - pFlac->currentFLACFrame.pcmFramesRemaining = 0; - } - } - } - pFlac->currentPCMFrame += pcmFramesRead; - return pcmFramesRead; -} -static ma_bool32 ma_dr_flac__seek_to_pcm_frame__brute_force(ma_dr_flac* pFlac, ma_uint64 pcmFrameIndex) -{ - ma_bool32 isMidFrame = MA_FALSE; - ma_uint64 runningPCMFrameCount; - MA_DR_FLAC_ASSERT(pFlac != NULL); - if (pcmFrameIndex >= pFlac->currentPCMFrame) { - runningPCMFrameCount = pFlac->currentPCMFrame; - if (pFlac->currentPCMFrame == 0 && pFlac->currentFLACFrame.pcmFramesRemaining == 0) { - if (!ma_dr_flac__read_next_flac_frame_header(&pFlac->bs, pFlac->bitsPerSample, &pFlac->currentFLACFrame.header)) { - return MA_FALSE; - } - } else { - isMidFrame = MA_TRUE; - } - } else { - runningPCMFrameCount = 0; - if (!ma_dr_flac__seek_to_first_frame(pFlac)) { - return MA_FALSE; - } - if (!ma_dr_flac__read_next_flac_frame_header(&pFlac->bs, pFlac->bitsPerSample, &pFlac->currentFLACFrame.header)) { - return MA_FALSE; - } - } - for (;;) { - ma_uint64 pcmFrameCountInThisFLACFrame; - ma_uint64 firstPCMFrameInFLACFrame = 0; - ma_uint64 lastPCMFrameInFLACFrame = 0; - ma_dr_flac__get_pcm_frame_range_of_current_flac_frame(pFlac, &firstPCMFrameInFLACFrame, &lastPCMFrameInFLACFrame); - pcmFrameCountInThisFLACFrame = (lastPCMFrameInFLACFrame - firstPCMFrameInFLACFrame) + 1; - if (pcmFrameIndex < (runningPCMFrameCount + pcmFrameCountInThisFLACFrame)) { - ma_uint64 pcmFramesToDecode = pcmFrameIndex - runningPCMFrameCount; - if (!isMidFrame) { - ma_result result = ma_dr_flac__decode_flac_frame(pFlac); - if (result == MA_SUCCESS) { - return ma_dr_flac__seek_forward_by_pcm_frames(pFlac, pcmFramesToDecode) == pcmFramesToDecode; - } else { - if (result == MA_CRC_MISMATCH) { - goto next_iteration; - } else { - return MA_FALSE; - } - } - } else { - return ma_dr_flac__seek_forward_by_pcm_frames(pFlac, pcmFramesToDecode) == pcmFramesToDecode; - } - } else { - if (!isMidFrame) { - ma_result result = ma_dr_flac__seek_to_next_flac_frame(pFlac); - if (result == MA_SUCCESS) { - runningPCMFrameCount += pcmFrameCountInThisFLACFrame; - } else { - if (result == MA_CRC_MISMATCH) { - goto next_iteration; - } else { - return MA_FALSE; - } - } - } else { - runningPCMFrameCount += pFlac->currentFLACFrame.pcmFramesRemaining; - pFlac->currentFLACFrame.pcmFramesRemaining = 0; - isMidFrame = MA_FALSE; - } - if (pcmFrameIndex == pFlac->totalPCMFrameCount && runningPCMFrameCount == pFlac->totalPCMFrameCount) { - return MA_TRUE; - } - } - next_iteration: - if (!ma_dr_flac__read_next_flac_frame_header(&pFlac->bs, pFlac->bitsPerSample, &pFlac->currentFLACFrame.header)) { - return MA_FALSE; - } - } -} -#if !defined(MA_DR_FLAC_NO_CRC) -#define MA_DR_FLAC_BINARY_SEARCH_APPROX_COMPRESSION_RATIO 0.6f -static ma_bool32 ma_dr_flac__seek_to_approximate_flac_frame_to_byte(ma_dr_flac* pFlac, ma_uint64 targetByte, ma_uint64 rangeLo, ma_uint64 rangeHi, ma_uint64* pLastSuccessfulSeekOffset) -{ - MA_DR_FLAC_ASSERT(pFlac != NULL); - MA_DR_FLAC_ASSERT(pLastSuccessfulSeekOffset != NULL); - MA_DR_FLAC_ASSERT(targetByte >= rangeLo); - MA_DR_FLAC_ASSERT(targetByte <= rangeHi); - *pLastSuccessfulSeekOffset = pFlac->firstFLACFramePosInBytes; - for (;;) { - ma_uint64 lastTargetByte = targetByte; - if (!ma_dr_flac__seek_to_byte(&pFlac->bs, targetByte)) { - if (targetByte == 0) { - ma_dr_flac__seek_to_first_frame(pFlac); - return MA_FALSE; - } - targetByte = rangeLo + ((rangeHi - rangeLo)/2); - rangeHi = targetByte; - } else { - MA_DR_FLAC_ZERO_MEMORY(&pFlac->currentFLACFrame, sizeof(pFlac->currentFLACFrame)); -#if 1 - if (!ma_dr_flac__read_and_decode_next_flac_frame(pFlac)) { - targetByte = rangeLo + ((rangeHi - rangeLo)/2); - rangeHi = targetByte; - } else { - break; - } -#else - if (!ma_dr_flac__read_next_flac_frame_header(&pFlac->bs, pFlac->bitsPerSample, &pFlac->currentFLACFrame.header)) { - targetByte = rangeLo + ((rangeHi - rangeLo)/2); - rangeHi = targetByte; - } else { - break; - } -#endif - } - if(targetByte == lastTargetByte) { - return MA_FALSE; - } - } - ma_dr_flac__get_pcm_frame_range_of_current_flac_frame(pFlac, &pFlac->currentPCMFrame, NULL); - MA_DR_FLAC_ASSERT(targetByte <= rangeHi); - *pLastSuccessfulSeekOffset = targetByte; - return MA_TRUE; -} -static ma_bool32 ma_dr_flac__decode_flac_frame_and_seek_forward_by_pcm_frames(ma_dr_flac* pFlac, ma_uint64 offset) -{ -#if 0 - if (ma_dr_flac__decode_flac_frame(pFlac) != MA_SUCCESS) { - if (ma_dr_flac__read_and_decode_next_flac_frame(pFlac) == MA_FALSE) { - return MA_FALSE; - } - } -#endif - return ma_dr_flac__seek_forward_by_pcm_frames(pFlac, offset) == offset; -} -static ma_bool32 ma_dr_flac__seek_to_pcm_frame__binary_search_internal(ma_dr_flac* pFlac, ma_uint64 pcmFrameIndex, ma_uint64 byteRangeLo, ma_uint64 byteRangeHi) -{ - ma_uint64 targetByte; - ma_uint64 pcmRangeLo = pFlac->totalPCMFrameCount; - ma_uint64 pcmRangeHi = 0; - ma_uint64 lastSuccessfulSeekOffset = (ma_uint64)-1; - ma_uint64 closestSeekOffsetBeforeTargetPCMFrame = byteRangeLo; - ma_uint32 seekForwardThreshold = (pFlac->maxBlockSizeInPCMFrames != 0) ? pFlac->maxBlockSizeInPCMFrames*2 : 4096; - targetByte = byteRangeLo + (ma_uint64)(((ma_int64)((pcmFrameIndex - pFlac->currentPCMFrame) * pFlac->channels * pFlac->bitsPerSample)/8.0f) * MA_DR_FLAC_BINARY_SEARCH_APPROX_COMPRESSION_RATIO); - if (targetByte > byteRangeHi) { - targetByte = byteRangeHi; - } - for (;;) { - if (ma_dr_flac__seek_to_approximate_flac_frame_to_byte(pFlac, targetByte, byteRangeLo, byteRangeHi, &lastSuccessfulSeekOffset)) { - ma_uint64 newPCMRangeLo; - ma_uint64 newPCMRangeHi; - ma_dr_flac__get_pcm_frame_range_of_current_flac_frame(pFlac, &newPCMRangeLo, &newPCMRangeHi); - if (pcmRangeLo == newPCMRangeLo) { - if (!ma_dr_flac__seek_to_approximate_flac_frame_to_byte(pFlac, closestSeekOffsetBeforeTargetPCMFrame, closestSeekOffsetBeforeTargetPCMFrame, byteRangeHi, &lastSuccessfulSeekOffset)) { - break; - } - if (ma_dr_flac__decode_flac_frame_and_seek_forward_by_pcm_frames(pFlac, pcmFrameIndex - pFlac->currentPCMFrame)) { - return MA_TRUE; - } else { - break; - } - } - pcmRangeLo = newPCMRangeLo; - pcmRangeHi = newPCMRangeHi; - if (pcmRangeLo <= pcmFrameIndex && pcmRangeHi >= pcmFrameIndex) { - if (ma_dr_flac__decode_flac_frame_and_seek_forward_by_pcm_frames(pFlac, pcmFrameIndex - pFlac->currentPCMFrame) ) { - return MA_TRUE; - } else { - break; - } - } else { - const float approxCompressionRatio = (ma_int64)(lastSuccessfulSeekOffset - pFlac->firstFLACFramePosInBytes) / ((ma_int64)(pcmRangeLo * pFlac->channels * pFlac->bitsPerSample)/8.0f); - if (pcmRangeLo > pcmFrameIndex) { - byteRangeHi = lastSuccessfulSeekOffset; - if (byteRangeLo > byteRangeHi) { - byteRangeLo = byteRangeHi; - } - targetByte = byteRangeLo + ((byteRangeHi - byteRangeLo) / 2); - if (targetByte < byteRangeLo) { - targetByte = byteRangeLo; - } - } else { - if ((pcmFrameIndex - pcmRangeLo) < seekForwardThreshold) { - if (ma_dr_flac__decode_flac_frame_and_seek_forward_by_pcm_frames(pFlac, pcmFrameIndex - pFlac->currentPCMFrame)) { - return MA_TRUE; - } else { - break; - } - } else { - byteRangeLo = lastSuccessfulSeekOffset; - if (byteRangeHi < byteRangeLo) { - byteRangeHi = byteRangeLo; - } - targetByte = lastSuccessfulSeekOffset + (ma_uint64)(((ma_int64)((pcmFrameIndex-pcmRangeLo) * pFlac->channels * pFlac->bitsPerSample)/8.0f) * approxCompressionRatio); - if (targetByte > byteRangeHi) { - targetByte = byteRangeHi; - } - if (closestSeekOffsetBeforeTargetPCMFrame < lastSuccessfulSeekOffset) { - closestSeekOffsetBeforeTargetPCMFrame = lastSuccessfulSeekOffset; - } - } - } - } - } else { - break; - } - } - ma_dr_flac__seek_to_first_frame(pFlac); - return MA_FALSE; -} -static ma_bool32 ma_dr_flac__seek_to_pcm_frame__binary_search(ma_dr_flac* pFlac, ma_uint64 pcmFrameIndex) -{ - ma_uint64 byteRangeLo; - ma_uint64 byteRangeHi; - ma_uint32 seekForwardThreshold = (pFlac->maxBlockSizeInPCMFrames != 0) ? pFlac->maxBlockSizeInPCMFrames*2 : 4096; - if (ma_dr_flac__seek_to_first_frame(pFlac) == MA_FALSE) { - return MA_FALSE; - } - if (pcmFrameIndex < seekForwardThreshold) { - return ma_dr_flac__seek_forward_by_pcm_frames(pFlac, pcmFrameIndex) == pcmFrameIndex; - } - byteRangeLo = pFlac->firstFLACFramePosInBytes; - byteRangeHi = pFlac->firstFLACFramePosInBytes + (ma_uint64)((ma_int64)(pFlac->totalPCMFrameCount * pFlac->channels * pFlac->bitsPerSample)/8.0f); - return ma_dr_flac__seek_to_pcm_frame__binary_search_internal(pFlac, pcmFrameIndex, byteRangeLo, byteRangeHi); -} -#endif -static ma_bool32 ma_dr_flac__seek_to_pcm_frame__seek_table(ma_dr_flac* pFlac, ma_uint64 pcmFrameIndex) -{ - ma_uint32 iClosestSeekpoint = 0; - ma_bool32 isMidFrame = MA_FALSE; - ma_uint64 runningPCMFrameCount; - ma_uint32 iSeekpoint; - MA_DR_FLAC_ASSERT(pFlac != NULL); - if (pFlac->pSeekpoints == NULL || pFlac->seekpointCount == 0) { - return MA_FALSE; - } - if (pFlac->pSeekpoints[0].firstPCMFrame > pcmFrameIndex) { - return MA_FALSE; - } - for (iSeekpoint = 0; iSeekpoint < pFlac->seekpointCount; ++iSeekpoint) { - if (pFlac->pSeekpoints[iSeekpoint].firstPCMFrame >= pcmFrameIndex) { - break; - } - iClosestSeekpoint = iSeekpoint; - } - if (pFlac->pSeekpoints[iClosestSeekpoint].pcmFrameCount == 0 || pFlac->pSeekpoints[iClosestSeekpoint].pcmFrameCount > pFlac->maxBlockSizeInPCMFrames) { - return MA_FALSE; - } - if (pFlac->pSeekpoints[iClosestSeekpoint].firstPCMFrame > pFlac->totalPCMFrameCount && pFlac->totalPCMFrameCount > 0) { - return MA_FALSE; - } -#if !defined(MA_DR_FLAC_NO_CRC) - if (pFlac->totalPCMFrameCount > 0) { - ma_uint64 byteRangeLo; - ma_uint64 byteRangeHi; - byteRangeHi = pFlac->firstFLACFramePosInBytes + (ma_uint64)((ma_int64)(pFlac->totalPCMFrameCount * pFlac->channels * pFlac->bitsPerSample)/8.0f); - byteRangeLo = pFlac->firstFLACFramePosInBytes + pFlac->pSeekpoints[iClosestSeekpoint].flacFrameOffset; - if (iClosestSeekpoint < pFlac->seekpointCount-1) { - ma_uint32 iNextSeekpoint = iClosestSeekpoint + 1; - if (pFlac->pSeekpoints[iClosestSeekpoint].flacFrameOffset >= pFlac->pSeekpoints[iNextSeekpoint].flacFrameOffset || pFlac->pSeekpoints[iNextSeekpoint].pcmFrameCount == 0) { - return MA_FALSE; - } - if (pFlac->pSeekpoints[iNextSeekpoint].firstPCMFrame != (((ma_uint64)0xFFFFFFFF << 32) | 0xFFFFFFFF)) { - byteRangeHi = pFlac->firstFLACFramePosInBytes + pFlac->pSeekpoints[iNextSeekpoint].flacFrameOffset - 1; - } - } - if (ma_dr_flac__seek_to_byte(&pFlac->bs, pFlac->firstFLACFramePosInBytes + pFlac->pSeekpoints[iClosestSeekpoint].flacFrameOffset)) { - if (ma_dr_flac__read_next_flac_frame_header(&pFlac->bs, pFlac->bitsPerSample, &pFlac->currentFLACFrame.header)) { - ma_dr_flac__get_pcm_frame_range_of_current_flac_frame(pFlac, &pFlac->currentPCMFrame, NULL); - if (ma_dr_flac__seek_to_pcm_frame__binary_search_internal(pFlac, pcmFrameIndex, byteRangeLo, byteRangeHi)) { - return MA_TRUE; - } - } - } - } -#endif - if (pcmFrameIndex >= pFlac->currentPCMFrame && pFlac->pSeekpoints[iClosestSeekpoint].firstPCMFrame <= pFlac->currentPCMFrame) { - runningPCMFrameCount = pFlac->currentPCMFrame; - if (pFlac->currentPCMFrame == 0 && pFlac->currentFLACFrame.pcmFramesRemaining == 0) { - if (!ma_dr_flac__read_next_flac_frame_header(&pFlac->bs, pFlac->bitsPerSample, &pFlac->currentFLACFrame.header)) { - return MA_FALSE; - } - } else { - isMidFrame = MA_TRUE; - } - } else { - runningPCMFrameCount = pFlac->pSeekpoints[iClosestSeekpoint].firstPCMFrame; - if (!ma_dr_flac__seek_to_byte(&pFlac->bs, pFlac->firstFLACFramePosInBytes + pFlac->pSeekpoints[iClosestSeekpoint].flacFrameOffset)) { - return MA_FALSE; - } - if (!ma_dr_flac__read_next_flac_frame_header(&pFlac->bs, pFlac->bitsPerSample, &pFlac->currentFLACFrame.header)) { - return MA_FALSE; - } - } - for (;;) { - ma_uint64 pcmFrameCountInThisFLACFrame; - ma_uint64 firstPCMFrameInFLACFrame = 0; - ma_uint64 lastPCMFrameInFLACFrame = 0; - ma_dr_flac__get_pcm_frame_range_of_current_flac_frame(pFlac, &firstPCMFrameInFLACFrame, &lastPCMFrameInFLACFrame); - pcmFrameCountInThisFLACFrame = (lastPCMFrameInFLACFrame - firstPCMFrameInFLACFrame) + 1; - if (pcmFrameIndex < (runningPCMFrameCount + pcmFrameCountInThisFLACFrame)) { - ma_uint64 pcmFramesToDecode = pcmFrameIndex - runningPCMFrameCount; - if (!isMidFrame) { - ma_result result = ma_dr_flac__decode_flac_frame(pFlac); - if (result == MA_SUCCESS) { - return ma_dr_flac__seek_forward_by_pcm_frames(pFlac, pcmFramesToDecode) == pcmFramesToDecode; - } else { - if (result == MA_CRC_MISMATCH) { - goto next_iteration; - } else { - return MA_FALSE; - } - } - } else { - return ma_dr_flac__seek_forward_by_pcm_frames(pFlac, pcmFramesToDecode) == pcmFramesToDecode; - } - } else { - if (!isMidFrame) { - ma_result result = ma_dr_flac__seek_to_next_flac_frame(pFlac); - if (result == MA_SUCCESS) { - runningPCMFrameCount += pcmFrameCountInThisFLACFrame; - } else { - if (result == MA_CRC_MISMATCH) { - goto next_iteration; - } else { - return MA_FALSE; - } - } - } else { - runningPCMFrameCount += pFlac->currentFLACFrame.pcmFramesRemaining; - pFlac->currentFLACFrame.pcmFramesRemaining = 0; - isMidFrame = MA_FALSE; - } - if (pcmFrameIndex == pFlac->totalPCMFrameCount && runningPCMFrameCount == pFlac->totalPCMFrameCount) { - return MA_TRUE; - } - } - next_iteration: - if (!ma_dr_flac__read_next_flac_frame_header(&pFlac->bs, pFlac->bitsPerSample, &pFlac->currentFLACFrame.header)) { - return MA_FALSE; - } - } -} -#ifndef MA_DR_FLAC_NO_OGG -typedef struct -{ - ma_uint8 capturePattern[4]; - ma_uint8 structureVersion; - ma_uint8 headerType; - ma_uint64 granulePosition; - ma_uint32 serialNumber; - ma_uint32 sequenceNumber; - ma_uint32 checksum; - ma_uint8 segmentCount; - ma_uint8 segmentTable[255]; -} ma_dr_flac_ogg_page_header; -#endif -typedef struct -{ - ma_dr_flac_read_proc onRead; - ma_dr_flac_seek_proc onSeek; - ma_dr_flac_meta_proc onMeta; - ma_dr_flac_container container; - void* pUserData; - void* pUserDataMD; - ma_uint32 sampleRate; - ma_uint8 channels; - ma_uint8 bitsPerSample; - ma_uint64 totalPCMFrameCount; - ma_uint16 maxBlockSizeInPCMFrames; - ma_uint64 runningFilePos; - ma_bool32 hasStreamInfoBlock; - ma_bool32 hasMetadataBlocks; - ma_dr_flac_bs bs; - ma_dr_flac_frame_header firstFrameHeader; -#ifndef MA_DR_FLAC_NO_OGG - ma_uint32 oggSerial; - ma_uint64 oggFirstBytePos; - ma_dr_flac_ogg_page_header oggBosHeader; -#endif -} ma_dr_flac_init_info; -static MA_INLINE void ma_dr_flac__decode_block_header(ma_uint32 blockHeader, ma_uint8* isLastBlock, ma_uint8* blockType, ma_uint32* blockSize) -{ - blockHeader = ma_dr_flac__be2host_32(blockHeader); - *isLastBlock = (ma_uint8)((blockHeader & 0x80000000UL) >> 31); - *blockType = (ma_uint8)((blockHeader & 0x7F000000UL) >> 24); - *blockSize = (blockHeader & 0x00FFFFFFUL); -} -static MA_INLINE ma_bool32 ma_dr_flac__read_and_decode_block_header(ma_dr_flac_read_proc onRead, void* pUserData, ma_uint8* isLastBlock, ma_uint8* blockType, ma_uint32* blockSize) -{ - ma_uint32 blockHeader; - *blockSize = 0; - if (onRead(pUserData, &blockHeader, 4) != 4) { - return MA_FALSE; - } - ma_dr_flac__decode_block_header(blockHeader, isLastBlock, blockType, blockSize); - return MA_TRUE; -} -static ma_bool32 ma_dr_flac__read_streaminfo(ma_dr_flac_read_proc onRead, void* pUserData, ma_dr_flac_streaminfo* pStreamInfo) -{ - ma_uint32 blockSizes; - ma_uint64 frameSizes = 0; - ma_uint64 importantProps; - ma_uint8 md5[16]; - if (onRead(pUserData, &blockSizes, 4) != 4) { - return MA_FALSE; - } - if (onRead(pUserData, &frameSizes, 6) != 6) { - return MA_FALSE; - } - if (onRead(pUserData, &importantProps, 8) != 8) { - return MA_FALSE; - } - if (onRead(pUserData, md5, sizeof(md5)) != sizeof(md5)) { - return MA_FALSE; - } - blockSizes = ma_dr_flac__be2host_32(blockSizes); - frameSizes = ma_dr_flac__be2host_64(frameSizes); - importantProps = ma_dr_flac__be2host_64(importantProps); - pStreamInfo->minBlockSizeInPCMFrames = (ma_uint16)((blockSizes & 0xFFFF0000) >> 16); - pStreamInfo->maxBlockSizeInPCMFrames = (ma_uint16) (blockSizes & 0x0000FFFF); - pStreamInfo->minFrameSizeInPCMFrames = (ma_uint32)((frameSizes & (((ma_uint64)0x00FFFFFF << 16) << 24)) >> 40); - pStreamInfo->maxFrameSizeInPCMFrames = (ma_uint32)((frameSizes & (((ma_uint64)0x00FFFFFF << 16) << 0)) >> 16); - pStreamInfo->sampleRate = (ma_uint32)((importantProps & (((ma_uint64)0x000FFFFF << 16) << 28)) >> 44); - pStreamInfo->channels = (ma_uint8 )((importantProps & (((ma_uint64)0x0000000E << 16) << 24)) >> 41) + 1; - pStreamInfo->bitsPerSample = (ma_uint8 )((importantProps & (((ma_uint64)0x0000001F << 16) << 20)) >> 36) + 1; - pStreamInfo->totalPCMFrameCount = ((importantProps & ((((ma_uint64)0x0000000F << 16) << 16) | 0xFFFFFFFF))); - MA_DR_FLAC_COPY_MEMORY(pStreamInfo->md5, md5, sizeof(md5)); - return MA_TRUE; -} -static void* ma_dr_flac__malloc_default(size_t sz, void* pUserData) -{ - (void)pUserData; - return MA_DR_FLAC_MALLOC(sz); -} -static void* ma_dr_flac__realloc_default(void* p, size_t sz, void* pUserData) -{ - (void)pUserData; - return MA_DR_FLAC_REALLOC(p, sz); -} -static void ma_dr_flac__free_default(void* p, void* pUserData) -{ - (void)pUserData; - MA_DR_FLAC_FREE(p); -} -static void* ma_dr_flac__malloc_from_callbacks(size_t sz, const ma_allocation_callbacks* pAllocationCallbacks) -{ - if (pAllocationCallbacks == NULL) { - return NULL; - } - if (pAllocationCallbacks->onMalloc != NULL) { - return pAllocationCallbacks->onMalloc(sz, pAllocationCallbacks->pUserData); - } - if (pAllocationCallbacks->onRealloc != NULL) { - return pAllocationCallbacks->onRealloc(NULL, sz, pAllocationCallbacks->pUserData); - } - return NULL; -} -static void* ma_dr_flac__realloc_from_callbacks(void* p, size_t szNew, size_t szOld, const ma_allocation_callbacks* pAllocationCallbacks) -{ - if (pAllocationCallbacks == NULL) { - return NULL; - } - if (pAllocationCallbacks->onRealloc != NULL) { - return pAllocationCallbacks->onRealloc(p, szNew, pAllocationCallbacks->pUserData); - } - if (pAllocationCallbacks->onMalloc != NULL && pAllocationCallbacks->onFree != NULL) { - void* p2; - p2 = pAllocationCallbacks->onMalloc(szNew, pAllocationCallbacks->pUserData); - if (p2 == NULL) { - return NULL; - } - if (p != NULL) { - MA_DR_FLAC_COPY_MEMORY(p2, p, szOld); - pAllocationCallbacks->onFree(p, pAllocationCallbacks->pUserData); - } - return p2; - } - return NULL; -} -static void ma_dr_flac__free_from_callbacks(void* p, const ma_allocation_callbacks* pAllocationCallbacks) -{ - if (p == NULL || pAllocationCallbacks == NULL) { - return; - } - if (pAllocationCallbacks->onFree != NULL) { - pAllocationCallbacks->onFree(p, pAllocationCallbacks->pUserData); - } -} -static ma_bool32 ma_dr_flac__read_and_decode_metadata(ma_dr_flac_read_proc onRead, ma_dr_flac_seek_proc onSeek, ma_dr_flac_meta_proc onMeta, void* pUserData, void* pUserDataMD, ma_uint64* pFirstFramePos, ma_uint64* pSeektablePos, ma_uint32* pSeekpointCount, ma_allocation_callbacks* pAllocationCallbacks) -{ - ma_uint64 runningFilePos = 42; - ma_uint64 seektablePos = 0; - ma_uint32 seektableSize = 0; - for (;;) { - ma_dr_flac_metadata metadata; - ma_uint8 isLastBlock = 0; - ma_uint8 blockType = 0; - ma_uint32 blockSize; - if (ma_dr_flac__read_and_decode_block_header(onRead, pUserData, &isLastBlock, &blockType, &blockSize) == MA_FALSE) { - return MA_FALSE; - } - runningFilePos += 4; - metadata.type = blockType; - metadata.pRawData = NULL; - metadata.rawDataSize = 0; - switch (blockType) - { - case MA_DR_FLAC_METADATA_BLOCK_TYPE_APPLICATION: - { - if (blockSize < 4) { - return MA_FALSE; - } - if (onMeta) { - void* pRawData = ma_dr_flac__malloc_from_callbacks(blockSize, pAllocationCallbacks); - if (pRawData == NULL) { - return MA_FALSE; - } - if (onRead(pUserData, pRawData, blockSize) != blockSize) { - ma_dr_flac__free_from_callbacks(pRawData, pAllocationCallbacks); - return MA_FALSE; - } - metadata.pRawData = pRawData; - metadata.rawDataSize = blockSize; - metadata.data.application.id = ma_dr_flac__be2host_32(*(ma_uint32*)pRawData); - metadata.data.application.pData = (const void*)((ma_uint8*)pRawData + sizeof(ma_uint32)); - metadata.data.application.dataSize = blockSize - sizeof(ma_uint32); - onMeta(pUserDataMD, &metadata); - ma_dr_flac__free_from_callbacks(pRawData, pAllocationCallbacks); - } - } break; - case MA_DR_FLAC_METADATA_BLOCK_TYPE_SEEKTABLE: - { - seektablePos = runningFilePos; - seektableSize = blockSize; - if (onMeta) { - ma_uint32 seekpointCount; - ma_uint32 iSeekpoint; - void* pRawData; - seekpointCount = blockSize/MA_DR_FLAC_SEEKPOINT_SIZE_IN_BYTES; - pRawData = ma_dr_flac__malloc_from_callbacks(seekpointCount * sizeof(ma_dr_flac_seekpoint), pAllocationCallbacks); - if (pRawData == NULL) { - return MA_FALSE; - } - for (iSeekpoint = 0; iSeekpoint < seekpointCount; ++iSeekpoint) { - ma_dr_flac_seekpoint* pSeekpoint = (ma_dr_flac_seekpoint*)pRawData + iSeekpoint; - if (onRead(pUserData, pSeekpoint, MA_DR_FLAC_SEEKPOINT_SIZE_IN_BYTES) != MA_DR_FLAC_SEEKPOINT_SIZE_IN_BYTES) { - ma_dr_flac__free_from_callbacks(pRawData, pAllocationCallbacks); - return MA_FALSE; - } - pSeekpoint->firstPCMFrame = ma_dr_flac__be2host_64(pSeekpoint->firstPCMFrame); - pSeekpoint->flacFrameOffset = ma_dr_flac__be2host_64(pSeekpoint->flacFrameOffset); - pSeekpoint->pcmFrameCount = ma_dr_flac__be2host_16(pSeekpoint->pcmFrameCount); - } - metadata.pRawData = pRawData; - metadata.rawDataSize = blockSize; - metadata.data.seektable.seekpointCount = seekpointCount; - metadata.data.seektable.pSeekpoints = (const ma_dr_flac_seekpoint*)pRawData; - onMeta(pUserDataMD, &metadata); - ma_dr_flac__free_from_callbacks(pRawData, pAllocationCallbacks); - } - } break; - case MA_DR_FLAC_METADATA_BLOCK_TYPE_VORBIS_COMMENT: - { - if (blockSize < 8) { - return MA_FALSE; - } - if (onMeta) { - void* pRawData; - const char* pRunningData; - const char* pRunningDataEnd; - ma_uint32 i; - pRawData = ma_dr_flac__malloc_from_callbacks(blockSize, pAllocationCallbacks); - if (pRawData == NULL) { - return MA_FALSE; - } - if (onRead(pUserData, pRawData, blockSize) != blockSize) { - ma_dr_flac__free_from_callbacks(pRawData, pAllocationCallbacks); - return MA_FALSE; - } - metadata.pRawData = pRawData; - metadata.rawDataSize = blockSize; - pRunningData = (const char*)pRawData; - pRunningDataEnd = (const char*)pRawData + blockSize; - metadata.data.vorbis_comment.vendorLength = ma_dr_flac__le2host_32_ptr_unaligned(pRunningData); pRunningData += 4; - if ((pRunningDataEnd - pRunningData) - 4 < (ma_int64)metadata.data.vorbis_comment.vendorLength) { - ma_dr_flac__free_from_callbacks(pRawData, pAllocationCallbacks); - return MA_FALSE; - } - metadata.data.vorbis_comment.vendor = pRunningData; pRunningData += metadata.data.vorbis_comment.vendorLength; - metadata.data.vorbis_comment.commentCount = ma_dr_flac__le2host_32_ptr_unaligned(pRunningData); pRunningData += 4; - if ((pRunningDataEnd - pRunningData) / sizeof(ma_uint32) < metadata.data.vorbis_comment.commentCount) { - ma_dr_flac__free_from_callbacks(pRawData, pAllocationCallbacks); - return MA_FALSE; - } - metadata.data.vorbis_comment.pComments = pRunningData; - for (i = 0; i < metadata.data.vorbis_comment.commentCount; ++i) { - ma_uint32 commentLength; - if (pRunningDataEnd - pRunningData < 4) { - ma_dr_flac__free_from_callbacks(pRawData, pAllocationCallbacks); - return MA_FALSE; - } - commentLength = ma_dr_flac__le2host_32_ptr_unaligned(pRunningData); pRunningData += 4; - if (pRunningDataEnd - pRunningData < (ma_int64)commentLength) { - ma_dr_flac__free_from_callbacks(pRawData, pAllocationCallbacks); - return MA_FALSE; - } - pRunningData += commentLength; - } - onMeta(pUserDataMD, &metadata); - ma_dr_flac__free_from_callbacks(pRawData, pAllocationCallbacks); - } - } break; - case MA_DR_FLAC_METADATA_BLOCK_TYPE_CUESHEET: - { - if (blockSize < 396) { - return MA_FALSE; - } - if (onMeta) { - void* pRawData; - const char* pRunningData; - const char* pRunningDataEnd; - size_t bufferSize; - ma_uint8 iTrack; - ma_uint8 iIndex; - void* pTrackData; - pRawData = ma_dr_flac__malloc_from_callbacks(blockSize, pAllocationCallbacks); - if (pRawData == NULL) { - return MA_FALSE; - } - if (onRead(pUserData, pRawData, blockSize) != blockSize) { - ma_dr_flac__free_from_callbacks(pRawData, pAllocationCallbacks); - return MA_FALSE; - } - metadata.pRawData = pRawData; - metadata.rawDataSize = blockSize; - pRunningData = (const char*)pRawData; - pRunningDataEnd = (const char*)pRawData + blockSize; - MA_DR_FLAC_COPY_MEMORY(metadata.data.cuesheet.catalog, pRunningData, 128); pRunningData += 128; - metadata.data.cuesheet.leadInSampleCount = ma_dr_flac__be2host_64(*(const ma_uint64*)pRunningData); pRunningData += 8; - metadata.data.cuesheet.isCD = (pRunningData[0] & 0x80) != 0; pRunningData += 259; - metadata.data.cuesheet.trackCount = pRunningData[0]; pRunningData += 1; - metadata.data.cuesheet.pTrackData = NULL; - { - const char* pRunningDataSaved = pRunningData; - bufferSize = metadata.data.cuesheet.trackCount * MA_DR_FLAC_CUESHEET_TRACK_SIZE_IN_BYTES; - for (iTrack = 0; iTrack < metadata.data.cuesheet.trackCount; ++iTrack) { - ma_uint8 indexCount; - ma_uint32 indexPointSize; - if (pRunningDataEnd - pRunningData < MA_DR_FLAC_CUESHEET_TRACK_SIZE_IN_BYTES) { - ma_dr_flac__free_from_callbacks(pRawData, pAllocationCallbacks); - return MA_FALSE; - } - pRunningData += 35; - indexCount = pRunningData[0]; - pRunningData += 1; - bufferSize += indexCount * sizeof(ma_dr_flac_cuesheet_track_index); - indexPointSize = indexCount * MA_DR_FLAC_CUESHEET_TRACK_INDEX_SIZE_IN_BYTES; - if (pRunningDataEnd - pRunningData < (ma_int64)indexPointSize) { - ma_dr_flac__free_from_callbacks(pRawData, pAllocationCallbacks); - return MA_FALSE; - } - pRunningData += indexPointSize; - } - pRunningData = pRunningDataSaved; - } - { - char* pRunningTrackData; - pTrackData = ma_dr_flac__malloc_from_callbacks(bufferSize, pAllocationCallbacks); - if (pTrackData == NULL) { - ma_dr_flac__free_from_callbacks(pRawData, pAllocationCallbacks); - return MA_FALSE; - } - pRunningTrackData = (char*)pTrackData; - for (iTrack = 0; iTrack < metadata.data.cuesheet.trackCount; ++iTrack) { - ma_uint8 indexCount; - MA_DR_FLAC_COPY_MEMORY(pRunningTrackData, pRunningData, MA_DR_FLAC_CUESHEET_TRACK_SIZE_IN_BYTES); - pRunningData += MA_DR_FLAC_CUESHEET_TRACK_SIZE_IN_BYTES-1; - pRunningTrackData += MA_DR_FLAC_CUESHEET_TRACK_SIZE_IN_BYTES-1; - indexCount = pRunningData[0]; - pRunningData += 1; - pRunningTrackData += 1; - for (iIndex = 0; iIndex < indexCount; ++iIndex) { - ma_dr_flac_cuesheet_track_index* pTrackIndex = (ma_dr_flac_cuesheet_track_index*)pRunningTrackData; - MA_DR_FLAC_COPY_MEMORY(pRunningTrackData, pRunningData, MA_DR_FLAC_CUESHEET_TRACK_INDEX_SIZE_IN_BYTES); - pRunningData += MA_DR_FLAC_CUESHEET_TRACK_INDEX_SIZE_IN_BYTES; - pRunningTrackData += sizeof(ma_dr_flac_cuesheet_track_index); - pTrackIndex->offset = ma_dr_flac__be2host_64(pTrackIndex->offset); - } - } - metadata.data.cuesheet.pTrackData = pTrackData; - } - ma_dr_flac__free_from_callbacks(pRawData, pAllocationCallbacks); - pRawData = NULL; - onMeta(pUserDataMD, &metadata); - ma_dr_flac__free_from_callbacks(pTrackData, pAllocationCallbacks); - pTrackData = NULL; - } - } break; - case MA_DR_FLAC_METADATA_BLOCK_TYPE_PICTURE: - { - if (blockSize < 32) { - return MA_FALSE; - } - if (onMeta) { - void* pRawData; - const char* pRunningData; - const char* pRunningDataEnd; - pRawData = ma_dr_flac__malloc_from_callbacks(blockSize, pAllocationCallbacks); - if (pRawData == NULL) { - return MA_FALSE; - } - if (onRead(pUserData, pRawData, blockSize) != blockSize) { - ma_dr_flac__free_from_callbacks(pRawData, pAllocationCallbacks); - return MA_FALSE; - } - metadata.pRawData = pRawData; - metadata.rawDataSize = blockSize; - pRunningData = (const char*)pRawData; - pRunningDataEnd = (const char*)pRawData + blockSize; - metadata.data.picture.type = ma_dr_flac__be2host_32_ptr_unaligned(pRunningData); pRunningData += 4; - metadata.data.picture.mimeLength = ma_dr_flac__be2host_32_ptr_unaligned(pRunningData); pRunningData += 4; - if ((pRunningDataEnd - pRunningData) - 24 < (ma_int64)metadata.data.picture.mimeLength) { - ma_dr_flac__free_from_callbacks(pRawData, pAllocationCallbacks); - return MA_FALSE; - } - metadata.data.picture.mime = pRunningData; pRunningData += metadata.data.picture.mimeLength; - metadata.data.picture.descriptionLength = ma_dr_flac__be2host_32_ptr_unaligned(pRunningData); pRunningData += 4; - if ((pRunningDataEnd - pRunningData) - 20 < (ma_int64)metadata.data.picture.descriptionLength) { - ma_dr_flac__free_from_callbacks(pRawData, pAllocationCallbacks); - return MA_FALSE; - } - metadata.data.picture.description = pRunningData; pRunningData += metadata.data.picture.descriptionLength; - metadata.data.picture.width = ma_dr_flac__be2host_32_ptr_unaligned(pRunningData); pRunningData += 4; - metadata.data.picture.height = ma_dr_flac__be2host_32_ptr_unaligned(pRunningData); pRunningData += 4; - metadata.data.picture.colorDepth = ma_dr_flac__be2host_32_ptr_unaligned(pRunningData); pRunningData += 4; - metadata.data.picture.indexColorCount = ma_dr_flac__be2host_32_ptr_unaligned(pRunningData); pRunningData += 4; - metadata.data.picture.pictureDataSize = ma_dr_flac__be2host_32_ptr_unaligned(pRunningData); pRunningData += 4; - metadata.data.picture.pPictureData = (const ma_uint8*)pRunningData; - if (pRunningDataEnd - pRunningData < (ma_int64)metadata.data.picture.pictureDataSize) { - ma_dr_flac__free_from_callbacks(pRawData, pAllocationCallbacks); - return MA_FALSE; - } - onMeta(pUserDataMD, &metadata); - ma_dr_flac__free_from_callbacks(pRawData, pAllocationCallbacks); - } - } break; - case MA_DR_FLAC_METADATA_BLOCK_TYPE_PADDING: - { - if (onMeta) { - metadata.data.padding.unused = 0; - if (!onSeek(pUserData, blockSize, ma_dr_flac_seek_origin_current)) { - isLastBlock = MA_TRUE; - } else { - onMeta(pUserDataMD, &metadata); - } - } - } break; - case MA_DR_FLAC_METADATA_BLOCK_TYPE_INVALID: - { - if (onMeta) { - if (!onSeek(pUserData, blockSize, ma_dr_flac_seek_origin_current)) { - isLastBlock = MA_TRUE; - } - } - } break; - default: - { - if (onMeta) { - void* pRawData = ma_dr_flac__malloc_from_callbacks(blockSize, pAllocationCallbacks); - if (pRawData == NULL) { - return MA_FALSE; - } - if (onRead(pUserData, pRawData, blockSize) != blockSize) { - ma_dr_flac__free_from_callbacks(pRawData, pAllocationCallbacks); - return MA_FALSE; - } - metadata.pRawData = pRawData; - metadata.rawDataSize = blockSize; - onMeta(pUserDataMD, &metadata); - ma_dr_flac__free_from_callbacks(pRawData, pAllocationCallbacks); - } - } break; - } - if (onMeta == NULL && blockSize > 0) { - if (!onSeek(pUserData, blockSize, ma_dr_flac_seek_origin_current)) { - isLastBlock = MA_TRUE; - } - } - runningFilePos += blockSize; - if (isLastBlock) { - break; - } - } - *pSeektablePos = seektablePos; - *pSeekpointCount = seektableSize / MA_DR_FLAC_SEEKPOINT_SIZE_IN_BYTES; - *pFirstFramePos = runningFilePos; - return MA_TRUE; -} -static ma_bool32 ma_dr_flac__init_private__native(ma_dr_flac_init_info* pInit, ma_dr_flac_read_proc onRead, ma_dr_flac_seek_proc onSeek, ma_dr_flac_meta_proc onMeta, void* pUserData, void* pUserDataMD, ma_bool32 relaxed) -{ - ma_uint8 isLastBlock; - ma_uint8 blockType; - ma_uint32 blockSize; - (void)onSeek; - pInit->container = ma_dr_flac_container_native; - if (!ma_dr_flac__read_and_decode_block_header(onRead, pUserData, &isLastBlock, &blockType, &blockSize)) { - return MA_FALSE; - } - if (blockType != MA_DR_FLAC_METADATA_BLOCK_TYPE_STREAMINFO || blockSize != 34) { - if (!relaxed) { - return MA_FALSE; - } else { - pInit->hasStreamInfoBlock = MA_FALSE; - pInit->hasMetadataBlocks = MA_FALSE; - if (!ma_dr_flac__read_next_flac_frame_header(&pInit->bs, 0, &pInit->firstFrameHeader)) { - return MA_FALSE; - } - if (pInit->firstFrameHeader.bitsPerSample == 0) { - return MA_FALSE; - } - pInit->sampleRate = pInit->firstFrameHeader.sampleRate; - pInit->channels = ma_dr_flac__get_channel_count_from_channel_assignment(pInit->firstFrameHeader.channelAssignment); - pInit->bitsPerSample = pInit->firstFrameHeader.bitsPerSample; - pInit->maxBlockSizeInPCMFrames = 65535; - return MA_TRUE; - } - } else { - ma_dr_flac_streaminfo streaminfo; - if (!ma_dr_flac__read_streaminfo(onRead, pUserData, &streaminfo)) { - return MA_FALSE; - } - pInit->hasStreamInfoBlock = MA_TRUE; - pInit->sampleRate = streaminfo.sampleRate; - pInit->channels = streaminfo.channels; - pInit->bitsPerSample = streaminfo.bitsPerSample; - pInit->totalPCMFrameCount = streaminfo.totalPCMFrameCount; - pInit->maxBlockSizeInPCMFrames = streaminfo.maxBlockSizeInPCMFrames; - pInit->hasMetadataBlocks = !isLastBlock; - if (onMeta) { - ma_dr_flac_metadata metadata; - metadata.type = MA_DR_FLAC_METADATA_BLOCK_TYPE_STREAMINFO; - metadata.pRawData = NULL; - metadata.rawDataSize = 0; - metadata.data.streaminfo = streaminfo; - onMeta(pUserDataMD, &metadata); - } - return MA_TRUE; - } -} -#ifndef MA_DR_FLAC_NO_OGG -#define MA_DR_FLAC_OGG_MAX_PAGE_SIZE 65307 -#define MA_DR_FLAC_OGG_CAPTURE_PATTERN_CRC32 1605413199 -typedef enum -{ - ma_dr_flac_ogg_recover_on_crc_mismatch, - ma_dr_flac_ogg_fail_on_crc_mismatch -} ma_dr_flac_ogg_crc_mismatch_recovery; -#ifndef MA_DR_FLAC_NO_CRC -static ma_uint32 ma_dr_flac__crc32_table[] = { - 0x00000000L, 0x04C11DB7L, 0x09823B6EL, 0x0D4326D9L, - 0x130476DCL, 0x17C56B6BL, 0x1A864DB2L, 0x1E475005L, - 0x2608EDB8L, 0x22C9F00FL, 0x2F8AD6D6L, 0x2B4BCB61L, - 0x350C9B64L, 0x31CD86D3L, 0x3C8EA00AL, 0x384FBDBDL, - 0x4C11DB70L, 0x48D0C6C7L, 0x4593E01EL, 0x4152FDA9L, - 0x5F15ADACL, 0x5BD4B01BL, 0x569796C2L, 0x52568B75L, - 0x6A1936C8L, 0x6ED82B7FL, 0x639B0DA6L, 0x675A1011L, - 0x791D4014L, 0x7DDC5DA3L, 0x709F7B7AL, 0x745E66CDL, - 0x9823B6E0L, 0x9CE2AB57L, 0x91A18D8EL, 0x95609039L, - 0x8B27C03CL, 0x8FE6DD8BL, 0x82A5FB52L, 0x8664E6E5L, - 0xBE2B5B58L, 0xBAEA46EFL, 0xB7A96036L, 0xB3687D81L, - 0xAD2F2D84L, 0xA9EE3033L, 0xA4AD16EAL, 0xA06C0B5DL, - 0xD4326D90L, 0xD0F37027L, 0xDDB056FEL, 0xD9714B49L, - 0xC7361B4CL, 0xC3F706FBL, 0xCEB42022L, 0xCA753D95L, - 0xF23A8028L, 0xF6FB9D9FL, 0xFBB8BB46L, 0xFF79A6F1L, - 0xE13EF6F4L, 0xE5FFEB43L, 0xE8BCCD9AL, 0xEC7DD02DL, - 0x34867077L, 0x30476DC0L, 0x3D044B19L, 0x39C556AEL, - 0x278206ABL, 0x23431B1CL, 0x2E003DC5L, 0x2AC12072L, - 0x128E9DCFL, 0x164F8078L, 0x1B0CA6A1L, 0x1FCDBB16L, - 0x018AEB13L, 0x054BF6A4L, 0x0808D07DL, 0x0CC9CDCAL, - 0x7897AB07L, 0x7C56B6B0L, 0x71159069L, 0x75D48DDEL, - 0x6B93DDDBL, 0x6F52C06CL, 0x6211E6B5L, 0x66D0FB02L, - 0x5E9F46BFL, 0x5A5E5B08L, 0x571D7DD1L, 0x53DC6066L, - 0x4D9B3063L, 0x495A2DD4L, 0x44190B0DL, 0x40D816BAL, - 0xACA5C697L, 0xA864DB20L, 0xA527FDF9L, 0xA1E6E04EL, - 0xBFA1B04BL, 0xBB60ADFCL, 0xB6238B25L, 0xB2E29692L, - 0x8AAD2B2FL, 0x8E6C3698L, 0x832F1041L, 0x87EE0DF6L, - 0x99A95DF3L, 0x9D684044L, 0x902B669DL, 0x94EA7B2AL, - 0xE0B41DE7L, 0xE4750050L, 0xE9362689L, 0xEDF73B3EL, - 0xF3B06B3BL, 0xF771768CL, 0xFA325055L, 0xFEF34DE2L, - 0xC6BCF05FL, 0xC27DEDE8L, 0xCF3ECB31L, 0xCBFFD686L, - 0xD5B88683L, 0xD1799B34L, 0xDC3ABDEDL, 0xD8FBA05AL, - 0x690CE0EEL, 0x6DCDFD59L, 0x608EDB80L, 0x644FC637L, - 0x7A089632L, 0x7EC98B85L, 0x738AAD5CL, 0x774BB0EBL, - 0x4F040D56L, 0x4BC510E1L, 0x46863638L, 0x42472B8FL, - 0x5C007B8AL, 0x58C1663DL, 0x558240E4L, 0x51435D53L, - 0x251D3B9EL, 0x21DC2629L, 0x2C9F00F0L, 0x285E1D47L, - 0x36194D42L, 0x32D850F5L, 0x3F9B762CL, 0x3B5A6B9BL, - 0x0315D626L, 0x07D4CB91L, 0x0A97ED48L, 0x0E56F0FFL, - 0x1011A0FAL, 0x14D0BD4DL, 0x19939B94L, 0x1D528623L, - 0xF12F560EL, 0xF5EE4BB9L, 0xF8AD6D60L, 0xFC6C70D7L, - 0xE22B20D2L, 0xE6EA3D65L, 0xEBA91BBCL, 0xEF68060BL, - 0xD727BBB6L, 0xD3E6A601L, 0xDEA580D8L, 0xDA649D6FL, - 0xC423CD6AL, 0xC0E2D0DDL, 0xCDA1F604L, 0xC960EBB3L, - 0xBD3E8D7EL, 0xB9FF90C9L, 0xB4BCB610L, 0xB07DABA7L, - 0xAE3AFBA2L, 0xAAFBE615L, 0xA7B8C0CCL, 0xA379DD7BL, - 0x9B3660C6L, 0x9FF77D71L, 0x92B45BA8L, 0x9675461FL, - 0x8832161AL, 0x8CF30BADL, 0x81B02D74L, 0x857130C3L, - 0x5D8A9099L, 0x594B8D2EL, 0x5408ABF7L, 0x50C9B640L, - 0x4E8EE645L, 0x4A4FFBF2L, 0x470CDD2BL, 0x43CDC09CL, - 0x7B827D21L, 0x7F436096L, 0x7200464FL, 0x76C15BF8L, - 0x68860BFDL, 0x6C47164AL, 0x61043093L, 0x65C52D24L, - 0x119B4BE9L, 0x155A565EL, 0x18197087L, 0x1CD86D30L, - 0x029F3D35L, 0x065E2082L, 0x0B1D065BL, 0x0FDC1BECL, - 0x3793A651L, 0x3352BBE6L, 0x3E119D3FL, 0x3AD08088L, - 0x2497D08DL, 0x2056CD3AL, 0x2D15EBE3L, 0x29D4F654L, - 0xC5A92679L, 0xC1683BCEL, 0xCC2B1D17L, 0xC8EA00A0L, - 0xD6AD50A5L, 0xD26C4D12L, 0xDF2F6BCBL, 0xDBEE767CL, - 0xE3A1CBC1L, 0xE760D676L, 0xEA23F0AFL, 0xEEE2ED18L, - 0xF0A5BD1DL, 0xF464A0AAL, 0xF9278673L, 0xFDE69BC4L, - 0x89B8FD09L, 0x8D79E0BEL, 0x803AC667L, 0x84FBDBD0L, - 0x9ABC8BD5L, 0x9E7D9662L, 0x933EB0BBL, 0x97FFAD0CL, - 0xAFB010B1L, 0xAB710D06L, 0xA6322BDFL, 0xA2F33668L, - 0xBCB4666DL, 0xB8757BDAL, 0xB5365D03L, 0xB1F740B4L -}; -#endif -static MA_INLINE ma_uint32 ma_dr_flac_crc32_byte(ma_uint32 crc32, ma_uint8 data) -{ -#ifndef MA_DR_FLAC_NO_CRC - return (crc32 << 8) ^ ma_dr_flac__crc32_table[(ma_uint8)((crc32 >> 24) & 0xFF) ^ data]; -#else - (void)data; - return crc32; -#endif -} -#if 0 -static MA_INLINE ma_uint32 ma_dr_flac_crc32_uint32(ma_uint32 crc32, ma_uint32 data) -{ - crc32 = ma_dr_flac_crc32_byte(crc32, (ma_uint8)((data >> 24) & 0xFF)); - crc32 = ma_dr_flac_crc32_byte(crc32, (ma_uint8)((data >> 16) & 0xFF)); - crc32 = ma_dr_flac_crc32_byte(crc32, (ma_uint8)((data >> 8) & 0xFF)); - crc32 = ma_dr_flac_crc32_byte(crc32, (ma_uint8)((data >> 0) & 0xFF)); - return crc32; -} -static MA_INLINE ma_uint32 ma_dr_flac_crc32_uint64(ma_uint32 crc32, ma_uint64 data) -{ - crc32 = ma_dr_flac_crc32_uint32(crc32, (ma_uint32)((data >> 32) & 0xFFFFFFFF)); - crc32 = ma_dr_flac_crc32_uint32(crc32, (ma_uint32)((data >> 0) & 0xFFFFFFFF)); - return crc32; -} -#endif -static MA_INLINE ma_uint32 ma_dr_flac_crc32_buffer(ma_uint32 crc32, ma_uint8* pData, ma_uint32 dataSize) -{ - ma_uint32 i; - for (i = 0; i < dataSize; ++i) { - crc32 = ma_dr_flac_crc32_byte(crc32, pData[i]); - } - return crc32; -} -static MA_INLINE ma_bool32 ma_dr_flac_ogg__is_capture_pattern(ma_uint8 pattern[4]) -{ - return pattern[0] == 'O' && pattern[1] == 'g' && pattern[2] == 'g' && pattern[3] == 'S'; -} -static MA_INLINE ma_uint32 ma_dr_flac_ogg__get_page_header_size(ma_dr_flac_ogg_page_header* pHeader) -{ - return 27 + pHeader->segmentCount; -} -static MA_INLINE ma_uint32 ma_dr_flac_ogg__get_page_body_size(ma_dr_flac_ogg_page_header* pHeader) -{ - ma_uint32 pageBodySize = 0; - int i; - for (i = 0; i < pHeader->segmentCount; ++i) { - pageBodySize += pHeader->segmentTable[i]; - } - return pageBodySize; -} -static ma_result ma_dr_flac_ogg__read_page_header_after_capture_pattern(ma_dr_flac_read_proc onRead, void* pUserData, ma_dr_flac_ogg_page_header* pHeader, ma_uint32* pBytesRead, ma_uint32* pCRC32) -{ - ma_uint8 data[23]; - ma_uint32 i; - MA_DR_FLAC_ASSERT(*pCRC32 == MA_DR_FLAC_OGG_CAPTURE_PATTERN_CRC32); - if (onRead(pUserData, data, 23) != 23) { - return MA_AT_END; - } - *pBytesRead += 23; - pHeader->capturePattern[0] = 'O'; - pHeader->capturePattern[1] = 'g'; - pHeader->capturePattern[2] = 'g'; - pHeader->capturePattern[3] = 'S'; - pHeader->structureVersion = data[0]; - pHeader->headerType = data[1]; - MA_DR_FLAC_COPY_MEMORY(&pHeader->granulePosition, &data[ 2], 8); - MA_DR_FLAC_COPY_MEMORY(&pHeader->serialNumber, &data[10], 4); - MA_DR_FLAC_COPY_MEMORY(&pHeader->sequenceNumber, &data[14], 4); - MA_DR_FLAC_COPY_MEMORY(&pHeader->checksum, &data[18], 4); - pHeader->segmentCount = data[22]; - data[18] = 0; - data[19] = 0; - data[20] = 0; - data[21] = 0; - for (i = 0; i < 23; ++i) { - *pCRC32 = ma_dr_flac_crc32_byte(*pCRC32, data[i]); - } - if (onRead(pUserData, pHeader->segmentTable, pHeader->segmentCount) != pHeader->segmentCount) { - return MA_AT_END; - } - *pBytesRead += pHeader->segmentCount; - for (i = 0; i < pHeader->segmentCount; ++i) { - *pCRC32 = ma_dr_flac_crc32_byte(*pCRC32, pHeader->segmentTable[i]); - } - return MA_SUCCESS; -} -static ma_result ma_dr_flac_ogg__read_page_header(ma_dr_flac_read_proc onRead, void* pUserData, ma_dr_flac_ogg_page_header* pHeader, ma_uint32* pBytesRead, ma_uint32* pCRC32) -{ - ma_uint8 id[4]; - *pBytesRead = 0; - if (onRead(pUserData, id, 4) != 4) { - return MA_AT_END; - } - *pBytesRead += 4; - for (;;) { - if (ma_dr_flac_ogg__is_capture_pattern(id)) { - ma_result result; - *pCRC32 = MA_DR_FLAC_OGG_CAPTURE_PATTERN_CRC32; - result = ma_dr_flac_ogg__read_page_header_after_capture_pattern(onRead, pUserData, pHeader, pBytesRead, pCRC32); - if (result == MA_SUCCESS) { - return MA_SUCCESS; - } else { - if (result == MA_CRC_MISMATCH) { - continue; - } else { - return result; - } - } - } else { - id[0] = id[1]; - id[1] = id[2]; - id[2] = id[3]; - if (onRead(pUserData, &id[3], 1) != 1) { - return MA_AT_END; - } - *pBytesRead += 1; - } - } -} -typedef struct -{ - ma_dr_flac_read_proc onRead; - ma_dr_flac_seek_proc onSeek; - void* pUserData; - ma_uint64 currentBytePos; - ma_uint64 firstBytePos; - ma_uint32 serialNumber; - ma_dr_flac_ogg_page_header bosPageHeader; - ma_dr_flac_ogg_page_header currentPageHeader; - ma_uint32 bytesRemainingInPage; - ma_uint32 pageDataSize; - ma_uint8 pageData[MA_DR_FLAC_OGG_MAX_PAGE_SIZE]; -} ma_dr_flac_oggbs; -static size_t ma_dr_flac_oggbs__read_physical(ma_dr_flac_oggbs* oggbs, void* bufferOut, size_t bytesToRead) -{ - size_t bytesActuallyRead = oggbs->onRead(oggbs->pUserData, bufferOut, bytesToRead); - oggbs->currentBytePos += bytesActuallyRead; - return bytesActuallyRead; -} -static ma_bool32 ma_dr_flac_oggbs__seek_physical(ma_dr_flac_oggbs* oggbs, ma_uint64 offset, ma_dr_flac_seek_origin origin) -{ - if (origin == ma_dr_flac_seek_origin_start) { - if (offset <= 0x7FFFFFFF) { - if (!oggbs->onSeek(oggbs->pUserData, (int)offset, ma_dr_flac_seek_origin_start)) { - return MA_FALSE; - } - oggbs->currentBytePos = offset; - return MA_TRUE; - } else { - if (!oggbs->onSeek(oggbs->pUserData, 0x7FFFFFFF, ma_dr_flac_seek_origin_start)) { - return MA_FALSE; - } - oggbs->currentBytePos = offset; - return ma_dr_flac_oggbs__seek_physical(oggbs, offset - 0x7FFFFFFF, ma_dr_flac_seek_origin_current); - } - } else { - while (offset > 0x7FFFFFFF) { - if (!oggbs->onSeek(oggbs->pUserData, 0x7FFFFFFF, ma_dr_flac_seek_origin_current)) { - return MA_FALSE; - } - oggbs->currentBytePos += 0x7FFFFFFF; - offset -= 0x7FFFFFFF; - } - if (!oggbs->onSeek(oggbs->pUserData, (int)offset, ma_dr_flac_seek_origin_current)) { - return MA_FALSE; - } - oggbs->currentBytePos += offset; - return MA_TRUE; - } -} -static ma_bool32 ma_dr_flac_oggbs__goto_next_page(ma_dr_flac_oggbs* oggbs, ma_dr_flac_ogg_crc_mismatch_recovery recoveryMethod) -{ - ma_dr_flac_ogg_page_header header; - for (;;) { - ma_uint32 crc32 = 0; - ma_uint32 bytesRead; - ma_uint32 pageBodySize; -#ifndef MA_DR_FLAC_NO_CRC - ma_uint32 actualCRC32; -#endif - if (ma_dr_flac_ogg__read_page_header(oggbs->onRead, oggbs->pUserData, &header, &bytesRead, &crc32) != MA_SUCCESS) { - return MA_FALSE; - } - oggbs->currentBytePos += bytesRead; - pageBodySize = ma_dr_flac_ogg__get_page_body_size(&header); - if (pageBodySize > MA_DR_FLAC_OGG_MAX_PAGE_SIZE) { - continue; - } - if (header.serialNumber != oggbs->serialNumber) { - if (pageBodySize > 0 && !ma_dr_flac_oggbs__seek_physical(oggbs, pageBodySize, ma_dr_flac_seek_origin_current)) { - return MA_FALSE; - } - continue; - } - if (ma_dr_flac_oggbs__read_physical(oggbs, oggbs->pageData, pageBodySize) != pageBodySize) { - return MA_FALSE; - } - oggbs->pageDataSize = pageBodySize; -#ifndef MA_DR_FLAC_NO_CRC - actualCRC32 = ma_dr_flac_crc32_buffer(crc32, oggbs->pageData, oggbs->pageDataSize); - if (actualCRC32 != header.checksum) { - if (recoveryMethod == ma_dr_flac_ogg_recover_on_crc_mismatch) { - continue; - } else { - ma_dr_flac_oggbs__goto_next_page(oggbs, ma_dr_flac_ogg_recover_on_crc_mismatch); - return MA_FALSE; - } - } -#else - (void)recoveryMethod; -#endif - oggbs->currentPageHeader = header; - oggbs->bytesRemainingInPage = pageBodySize; - return MA_TRUE; - } -} -#if 0 -static ma_uint8 ma_dr_flac_oggbs__get_current_segment_index(ma_dr_flac_oggbs* oggbs, ma_uint8* pBytesRemainingInSeg) -{ - ma_uint32 bytesConsumedInPage = ma_dr_flac_ogg__get_page_body_size(&oggbs->currentPageHeader) - oggbs->bytesRemainingInPage; - ma_uint8 iSeg = 0; - ma_uint32 iByte = 0; - while (iByte < bytesConsumedInPage) { - ma_uint8 segmentSize = oggbs->currentPageHeader.segmentTable[iSeg]; - if (iByte + segmentSize > bytesConsumedInPage) { - break; - } else { - iSeg += 1; - iByte += segmentSize; - } - } - *pBytesRemainingInSeg = oggbs->currentPageHeader.segmentTable[iSeg] - (ma_uint8)(bytesConsumedInPage - iByte); - return iSeg; -} -static ma_bool32 ma_dr_flac_oggbs__seek_to_next_packet(ma_dr_flac_oggbs* oggbs) -{ - for (;;) { - ma_bool32 atEndOfPage = MA_FALSE; - ma_uint8 bytesRemainingInSeg; - ma_uint8 iFirstSeg = ma_dr_flac_oggbs__get_current_segment_index(oggbs, &bytesRemainingInSeg); - ma_uint32 bytesToEndOfPacketOrPage = bytesRemainingInSeg; - for (ma_uint8 iSeg = iFirstSeg; iSeg < oggbs->currentPageHeader.segmentCount; ++iSeg) { - ma_uint8 segmentSize = oggbs->currentPageHeader.segmentTable[iSeg]; - if (segmentSize < 255) { - if (iSeg == oggbs->currentPageHeader.segmentCount-1) { - atEndOfPage = MA_TRUE; - } - break; - } - bytesToEndOfPacketOrPage += segmentSize; - } - ma_dr_flac_oggbs__seek_physical(oggbs, bytesToEndOfPacketOrPage, ma_dr_flac_seek_origin_current); - oggbs->bytesRemainingInPage -= bytesToEndOfPacketOrPage; - if (atEndOfPage) { - if (!ma_dr_flac_oggbs__goto_next_page(oggbs)) { - return MA_FALSE; - } - if ((oggbs->currentPageHeader.headerType & 0x01) == 0) { - return MA_TRUE; - } - } else { - return MA_TRUE; - } - } -} -static ma_bool32 ma_dr_flac_oggbs__seek_to_next_frame(ma_dr_flac_oggbs* oggbs) -{ - return ma_dr_flac_oggbs__seek_to_next_packet(oggbs); -} -#endif -static size_t ma_dr_flac__on_read_ogg(void* pUserData, void* bufferOut, size_t bytesToRead) -{ - ma_dr_flac_oggbs* oggbs = (ma_dr_flac_oggbs*)pUserData; - ma_uint8* pRunningBufferOut = (ma_uint8*)bufferOut; - size_t bytesRead = 0; - MA_DR_FLAC_ASSERT(oggbs != NULL); - MA_DR_FLAC_ASSERT(pRunningBufferOut != NULL); - while (bytesRead < bytesToRead) { - size_t bytesRemainingToRead = bytesToRead - bytesRead; - if (oggbs->bytesRemainingInPage >= bytesRemainingToRead) { - MA_DR_FLAC_COPY_MEMORY(pRunningBufferOut, oggbs->pageData + (oggbs->pageDataSize - oggbs->bytesRemainingInPage), bytesRemainingToRead); - bytesRead += bytesRemainingToRead; - oggbs->bytesRemainingInPage -= (ma_uint32)bytesRemainingToRead; - break; - } - if (oggbs->bytesRemainingInPage > 0) { - MA_DR_FLAC_COPY_MEMORY(pRunningBufferOut, oggbs->pageData + (oggbs->pageDataSize - oggbs->bytesRemainingInPage), oggbs->bytesRemainingInPage); - bytesRead += oggbs->bytesRemainingInPage; - pRunningBufferOut += oggbs->bytesRemainingInPage; - oggbs->bytesRemainingInPage = 0; - } - MA_DR_FLAC_ASSERT(bytesRemainingToRead > 0); - if (!ma_dr_flac_oggbs__goto_next_page(oggbs, ma_dr_flac_ogg_recover_on_crc_mismatch)) { - break; - } - } - return bytesRead; -} -static ma_bool32 ma_dr_flac__on_seek_ogg(void* pUserData, int offset, ma_dr_flac_seek_origin origin) -{ - ma_dr_flac_oggbs* oggbs = (ma_dr_flac_oggbs*)pUserData; - int bytesSeeked = 0; - MA_DR_FLAC_ASSERT(oggbs != NULL); - MA_DR_FLAC_ASSERT(offset >= 0); - if (origin == ma_dr_flac_seek_origin_start) { - if (!ma_dr_flac_oggbs__seek_physical(oggbs, (int)oggbs->firstBytePos, ma_dr_flac_seek_origin_start)) { - return MA_FALSE; - } - if (!ma_dr_flac_oggbs__goto_next_page(oggbs, ma_dr_flac_ogg_fail_on_crc_mismatch)) { - return MA_FALSE; - } - return ma_dr_flac__on_seek_ogg(pUserData, offset, ma_dr_flac_seek_origin_current); - } - MA_DR_FLAC_ASSERT(origin == ma_dr_flac_seek_origin_current); - while (bytesSeeked < offset) { - int bytesRemainingToSeek = offset - bytesSeeked; - MA_DR_FLAC_ASSERT(bytesRemainingToSeek >= 0); - if (oggbs->bytesRemainingInPage >= (size_t)bytesRemainingToSeek) { - bytesSeeked += bytesRemainingToSeek; - (void)bytesSeeked; - oggbs->bytesRemainingInPage -= bytesRemainingToSeek; - break; - } - if (oggbs->bytesRemainingInPage > 0) { - bytesSeeked += (int)oggbs->bytesRemainingInPage; - oggbs->bytesRemainingInPage = 0; - } - MA_DR_FLAC_ASSERT(bytesRemainingToSeek > 0); - if (!ma_dr_flac_oggbs__goto_next_page(oggbs, ma_dr_flac_ogg_fail_on_crc_mismatch)) { - return MA_FALSE; - } - } - return MA_TRUE; -} -static ma_bool32 ma_dr_flac_ogg__seek_to_pcm_frame(ma_dr_flac* pFlac, ma_uint64 pcmFrameIndex) -{ - ma_dr_flac_oggbs* oggbs = (ma_dr_flac_oggbs*)pFlac->_oggbs; - ma_uint64 originalBytePos; - ma_uint64 runningGranulePosition; - ma_uint64 runningFrameBytePos; - ma_uint64 runningPCMFrameCount; - MA_DR_FLAC_ASSERT(oggbs != NULL); - originalBytePos = oggbs->currentBytePos; - if (!ma_dr_flac__seek_to_byte(&pFlac->bs, pFlac->firstFLACFramePosInBytes)) { - return MA_FALSE; - } - oggbs->bytesRemainingInPage = 0; - runningGranulePosition = 0; - for (;;) { - if (!ma_dr_flac_oggbs__goto_next_page(oggbs, ma_dr_flac_ogg_recover_on_crc_mismatch)) { - ma_dr_flac_oggbs__seek_physical(oggbs, originalBytePos, ma_dr_flac_seek_origin_start); - return MA_FALSE; - } - runningFrameBytePos = oggbs->currentBytePos - ma_dr_flac_ogg__get_page_header_size(&oggbs->currentPageHeader) - oggbs->pageDataSize; - if (oggbs->currentPageHeader.granulePosition >= pcmFrameIndex) { - break; - } - if ((oggbs->currentPageHeader.headerType & 0x01) == 0) { - if (oggbs->currentPageHeader.segmentTable[0] >= 2) { - ma_uint8 firstBytesInPage[2]; - firstBytesInPage[0] = oggbs->pageData[0]; - firstBytesInPage[1] = oggbs->pageData[1]; - if ((firstBytesInPage[0] == 0xFF) && (firstBytesInPage[1] & 0xFC) == 0xF8) { - runningGranulePosition = oggbs->currentPageHeader.granulePosition; - } - continue; - } - } - } - if (!ma_dr_flac_oggbs__seek_physical(oggbs, runningFrameBytePos, ma_dr_flac_seek_origin_start)) { - return MA_FALSE; - } - if (!ma_dr_flac_oggbs__goto_next_page(oggbs, ma_dr_flac_ogg_recover_on_crc_mismatch)) { - return MA_FALSE; - } - runningPCMFrameCount = runningGranulePosition; - for (;;) { - ma_uint64 firstPCMFrameInFLACFrame = 0; - ma_uint64 lastPCMFrameInFLACFrame = 0; - ma_uint64 pcmFrameCountInThisFrame; - if (!ma_dr_flac__read_next_flac_frame_header(&pFlac->bs, pFlac->bitsPerSample, &pFlac->currentFLACFrame.header)) { - return MA_FALSE; - } - ma_dr_flac__get_pcm_frame_range_of_current_flac_frame(pFlac, &firstPCMFrameInFLACFrame, &lastPCMFrameInFLACFrame); - pcmFrameCountInThisFrame = (lastPCMFrameInFLACFrame - firstPCMFrameInFLACFrame) + 1; - if (pcmFrameIndex == pFlac->totalPCMFrameCount && (runningPCMFrameCount + pcmFrameCountInThisFrame) == pFlac->totalPCMFrameCount) { - ma_result result = ma_dr_flac__decode_flac_frame(pFlac); - if (result == MA_SUCCESS) { - pFlac->currentPCMFrame = pcmFrameIndex; - pFlac->currentFLACFrame.pcmFramesRemaining = 0; - return MA_TRUE; - } else { - return MA_FALSE; - } - } - if (pcmFrameIndex < (runningPCMFrameCount + pcmFrameCountInThisFrame)) { - ma_result result = ma_dr_flac__decode_flac_frame(pFlac); - if (result == MA_SUCCESS) { - ma_uint64 pcmFramesToDecode = (size_t)(pcmFrameIndex - runningPCMFrameCount); - if (pcmFramesToDecode == 0) { - return MA_TRUE; - } - pFlac->currentPCMFrame = runningPCMFrameCount; - return ma_dr_flac__seek_forward_by_pcm_frames(pFlac, pcmFramesToDecode) == pcmFramesToDecode; - } else { - if (result == MA_CRC_MISMATCH) { - continue; - } else { - return MA_FALSE; - } - } - } else { - ma_result result = ma_dr_flac__seek_to_next_flac_frame(pFlac); - if (result == MA_SUCCESS) { - runningPCMFrameCount += pcmFrameCountInThisFrame; - } else { - if (result == MA_CRC_MISMATCH) { - continue; - } else { - return MA_FALSE; - } - } - } - } -} -static ma_bool32 ma_dr_flac__init_private__ogg(ma_dr_flac_init_info* pInit, ma_dr_flac_read_proc onRead, ma_dr_flac_seek_proc onSeek, ma_dr_flac_meta_proc onMeta, void* pUserData, void* pUserDataMD, ma_bool32 relaxed) -{ - ma_dr_flac_ogg_page_header header; - ma_uint32 crc32 = MA_DR_FLAC_OGG_CAPTURE_PATTERN_CRC32; - ma_uint32 bytesRead = 0; - (void)relaxed; - pInit->container = ma_dr_flac_container_ogg; - pInit->oggFirstBytePos = 0; - if (ma_dr_flac_ogg__read_page_header_after_capture_pattern(onRead, pUserData, &header, &bytesRead, &crc32) != MA_SUCCESS) { - return MA_FALSE; - } - pInit->runningFilePos += bytesRead; - for (;;) { - int pageBodySize; - if ((header.headerType & 0x02) == 0) { - return MA_FALSE; - } - pageBodySize = ma_dr_flac_ogg__get_page_body_size(&header); - if (pageBodySize == 51) { - ma_uint32 bytesRemainingInPage = pageBodySize; - ma_uint8 packetType; - if (onRead(pUserData, &packetType, 1) != 1) { - return MA_FALSE; - } - bytesRemainingInPage -= 1; - if (packetType == 0x7F) { - ma_uint8 sig[4]; - if (onRead(pUserData, sig, 4) != 4) { - return MA_FALSE; - } - bytesRemainingInPage -= 4; - if (sig[0] == 'F' && sig[1] == 'L' && sig[2] == 'A' && sig[3] == 'C') { - ma_uint8 mappingVersion[2]; - if (onRead(pUserData, mappingVersion, 2) != 2) { - return MA_FALSE; - } - if (mappingVersion[0] != 1) { - return MA_FALSE; - } - if (!onSeek(pUserData, 2, ma_dr_flac_seek_origin_current)) { - return MA_FALSE; - } - if (onRead(pUserData, sig, 4) != 4) { - return MA_FALSE; - } - if (sig[0] == 'f' && sig[1] == 'L' && sig[2] == 'a' && sig[3] == 'C') { - ma_dr_flac_streaminfo streaminfo; - ma_uint8 isLastBlock; - ma_uint8 blockType; - ma_uint32 blockSize; - if (!ma_dr_flac__read_and_decode_block_header(onRead, pUserData, &isLastBlock, &blockType, &blockSize)) { - return MA_FALSE; - } - if (blockType != MA_DR_FLAC_METADATA_BLOCK_TYPE_STREAMINFO || blockSize != 34) { - return MA_FALSE; - } - if (ma_dr_flac__read_streaminfo(onRead, pUserData, &streaminfo)) { - pInit->hasStreamInfoBlock = MA_TRUE; - pInit->sampleRate = streaminfo.sampleRate; - pInit->channels = streaminfo.channels; - pInit->bitsPerSample = streaminfo.bitsPerSample; - pInit->totalPCMFrameCount = streaminfo.totalPCMFrameCount; - pInit->maxBlockSizeInPCMFrames = streaminfo.maxBlockSizeInPCMFrames; - pInit->hasMetadataBlocks = !isLastBlock; - if (onMeta) { - ma_dr_flac_metadata metadata; - metadata.type = MA_DR_FLAC_METADATA_BLOCK_TYPE_STREAMINFO; - metadata.pRawData = NULL; - metadata.rawDataSize = 0; - metadata.data.streaminfo = streaminfo; - onMeta(pUserDataMD, &metadata); - } - pInit->runningFilePos += pageBodySize; - pInit->oggFirstBytePos = pInit->runningFilePos - 79; - pInit->oggSerial = header.serialNumber; - pInit->oggBosHeader = header; - break; - } else { - return MA_FALSE; - } - } else { - return MA_FALSE; - } - } else { - if (!onSeek(pUserData, bytesRemainingInPage, ma_dr_flac_seek_origin_current)) { - return MA_FALSE; - } - } - } else { - if (!onSeek(pUserData, bytesRemainingInPage, ma_dr_flac_seek_origin_current)) { - return MA_FALSE; - } - } - } else { - if (!onSeek(pUserData, pageBodySize, ma_dr_flac_seek_origin_current)) { - return MA_FALSE; - } - } - pInit->runningFilePos += pageBodySize; - if (ma_dr_flac_ogg__read_page_header(onRead, pUserData, &header, &bytesRead, &crc32) != MA_SUCCESS) { - return MA_FALSE; - } - pInit->runningFilePos += bytesRead; - } - pInit->hasMetadataBlocks = MA_TRUE; - return MA_TRUE; -} -#endif -static ma_bool32 ma_dr_flac__init_private(ma_dr_flac_init_info* pInit, ma_dr_flac_read_proc onRead, ma_dr_flac_seek_proc onSeek, ma_dr_flac_meta_proc onMeta, ma_dr_flac_container container, void* pUserData, void* pUserDataMD) -{ - ma_bool32 relaxed; - ma_uint8 id[4]; - if (pInit == NULL || onRead == NULL || onSeek == NULL) { - return MA_FALSE; - } - MA_DR_FLAC_ZERO_MEMORY(pInit, sizeof(*pInit)); - pInit->onRead = onRead; - pInit->onSeek = onSeek; - pInit->onMeta = onMeta; - pInit->container = container; - pInit->pUserData = pUserData; - pInit->pUserDataMD = pUserDataMD; - pInit->bs.onRead = onRead; - pInit->bs.onSeek = onSeek; - pInit->bs.pUserData = pUserData; - ma_dr_flac__reset_cache(&pInit->bs); - relaxed = container != ma_dr_flac_container_unknown; - for (;;) { - if (onRead(pUserData, id, 4) != 4) { - return MA_FALSE; - } - pInit->runningFilePos += 4; - if (id[0] == 'I' && id[1] == 'D' && id[2] == '3') { - ma_uint8 header[6]; - ma_uint8 flags; - ma_uint32 headerSize; - if (onRead(pUserData, header, 6) != 6) { - return MA_FALSE; - } - pInit->runningFilePos += 6; - flags = header[1]; - MA_DR_FLAC_COPY_MEMORY(&headerSize, header+2, 4); - headerSize = ma_dr_flac__unsynchsafe_32(ma_dr_flac__be2host_32(headerSize)); - if (flags & 0x10) { - headerSize += 10; - } - if (!onSeek(pUserData, headerSize, ma_dr_flac_seek_origin_current)) { - return MA_FALSE; - } - pInit->runningFilePos += headerSize; - } else { - break; - } - } - if (id[0] == 'f' && id[1] == 'L' && id[2] == 'a' && id[3] == 'C') { - return ma_dr_flac__init_private__native(pInit, onRead, onSeek, onMeta, pUserData, pUserDataMD, relaxed); - } -#ifndef MA_DR_FLAC_NO_OGG - if (id[0] == 'O' && id[1] == 'g' && id[2] == 'g' && id[3] == 'S') { - return ma_dr_flac__init_private__ogg(pInit, onRead, onSeek, onMeta, pUserData, pUserDataMD, relaxed); - } -#endif - if (relaxed) { - if (container == ma_dr_flac_container_native) { - return ma_dr_flac__init_private__native(pInit, onRead, onSeek, onMeta, pUserData, pUserDataMD, relaxed); - } -#ifndef MA_DR_FLAC_NO_OGG - if (container == ma_dr_flac_container_ogg) { - return ma_dr_flac__init_private__ogg(pInit, onRead, onSeek, onMeta, pUserData, pUserDataMD, relaxed); - } -#endif - } - return MA_FALSE; -} -static void ma_dr_flac__init_from_info(ma_dr_flac* pFlac, const ma_dr_flac_init_info* pInit) -{ - MA_DR_FLAC_ASSERT(pFlac != NULL); - MA_DR_FLAC_ASSERT(pInit != NULL); - MA_DR_FLAC_ZERO_MEMORY(pFlac, sizeof(*pFlac)); - pFlac->bs = pInit->bs; - pFlac->onMeta = pInit->onMeta; - pFlac->pUserDataMD = pInit->pUserDataMD; - pFlac->maxBlockSizeInPCMFrames = pInit->maxBlockSizeInPCMFrames; - pFlac->sampleRate = pInit->sampleRate; - pFlac->channels = (ma_uint8)pInit->channels; - pFlac->bitsPerSample = (ma_uint8)pInit->bitsPerSample; - pFlac->totalPCMFrameCount = pInit->totalPCMFrameCount; - pFlac->container = pInit->container; -} -static ma_dr_flac* ma_dr_flac_open_with_metadata_private(ma_dr_flac_read_proc onRead, ma_dr_flac_seek_proc onSeek, ma_dr_flac_meta_proc onMeta, ma_dr_flac_container container, void* pUserData, void* pUserDataMD, const ma_allocation_callbacks* pAllocationCallbacks) -{ - ma_dr_flac_init_info init; - ma_uint32 allocationSize; - ma_uint32 wholeSIMDVectorCountPerChannel; - ma_uint32 decodedSamplesAllocationSize; -#ifndef MA_DR_FLAC_NO_OGG - ma_dr_flac_oggbs* pOggbs = NULL; -#endif - ma_uint64 firstFramePos; - ma_uint64 seektablePos; - ma_uint32 seekpointCount; - ma_allocation_callbacks allocationCallbacks; - ma_dr_flac* pFlac; - ma_dr_flac__init_cpu_caps(); - if (!ma_dr_flac__init_private(&init, onRead, onSeek, onMeta, container, pUserData, pUserDataMD)) { - return NULL; - } - if (pAllocationCallbacks != NULL) { - allocationCallbacks = *pAllocationCallbacks; - if (allocationCallbacks.onFree == NULL || (allocationCallbacks.onMalloc == NULL && allocationCallbacks.onRealloc == NULL)) { - return NULL; - } - } else { - allocationCallbacks.pUserData = NULL; - allocationCallbacks.onMalloc = ma_dr_flac__malloc_default; - allocationCallbacks.onRealloc = ma_dr_flac__realloc_default; - allocationCallbacks.onFree = ma_dr_flac__free_default; - } - allocationSize = sizeof(ma_dr_flac); - if ((init.maxBlockSizeInPCMFrames % (MA_DR_FLAC_MAX_SIMD_VECTOR_SIZE / sizeof(ma_int32))) == 0) { - wholeSIMDVectorCountPerChannel = (init.maxBlockSizeInPCMFrames / (MA_DR_FLAC_MAX_SIMD_VECTOR_SIZE / sizeof(ma_int32))); - } else { - wholeSIMDVectorCountPerChannel = (init.maxBlockSizeInPCMFrames / (MA_DR_FLAC_MAX_SIMD_VECTOR_SIZE / sizeof(ma_int32))) + 1; - } - decodedSamplesAllocationSize = wholeSIMDVectorCountPerChannel * MA_DR_FLAC_MAX_SIMD_VECTOR_SIZE * init.channels; - allocationSize += decodedSamplesAllocationSize; - allocationSize += MA_DR_FLAC_MAX_SIMD_VECTOR_SIZE; -#ifndef MA_DR_FLAC_NO_OGG - if (init.container == ma_dr_flac_container_ogg) { - allocationSize += sizeof(ma_dr_flac_oggbs); - pOggbs = (ma_dr_flac_oggbs*)ma_dr_flac__malloc_from_callbacks(sizeof(*pOggbs), &allocationCallbacks); - if (pOggbs == NULL) { - return NULL; - } - MA_DR_FLAC_ZERO_MEMORY(pOggbs, sizeof(*pOggbs)); - pOggbs->onRead = onRead; - pOggbs->onSeek = onSeek; - pOggbs->pUserData = pUserData; - pOggbs->currentBytePos = init.oggFirstBytePos; - pOggbs->firstBytePos = init.oggFirstBytePos; - pOggbs->serialNumber = init.oggSerial; - pOggbs->bosPageHeader = init.oggBosHeader; - pOggbs->bytesRemainingInPage = 0; - } -#endif - firstFramePos = 42; - seektablePos = 0; - seekpointCount = 0; - if (init.hasMetadataBlocks) { - ma_dr_flac_read_proc onReadOverride = onRead; - ma_dr_flac_seek_proc onSeekOverride = onSeek; - void* pUserDataOverride = pUserData; -#ifndef MA_DR_FLAC_NO_OGG - if (init.container == ma_dr_flac_container_ogg) { - onReadOverride = ma_dr_flac__on_read_ogg; - onSeekOverride = ma_dr_flac__on_seek_ogg; - pUserDataOverride = (void*)pOggbs; - } -#endif - if (!ma_dr_flac__read_and_decode_metadata(onReadOverride, onSeekOverride, onMeta, pUserDataOverride, pUserDataMD, &firstFramePos, &seektablePos, &seekpointCount, &allocationCallbacks)) { - #ifndef MA_DR_FLAC_NO_OGG - ma_dr_flac__free_from_callbacks(pOggbs, &allocationCallbacks); - #endif - return NULL; - } - allocationSize += seekpointCount * sizeof(ma_dr_flac_seekpoint); - } - pFlac = (ma_dr_flac*)ma_dr_flac__malloc_from_callbacks(allocationSize, &allocationCallbacks); - if (pFlac == NULL) { - #ifndef MA_DR_FLAC_NO_OGG - ma_dr_flac__free_from_callbacks(pOggbs, &allocationCallbacks); - #endif - return NULL; - } - ma_dr_flac__init_from_info(pFlac, &init); - pFlac->allocationCallbacks = allocationCallbacks; - pFlac->pDecodedSamples = (ma_int32*)ma_dr_flac_align((size_t)pFlac->pExtraData, MA_DR_FLAC_MAX_SIMD_VECTOR_SIZE); -#ifndef MA_DR_FLAC_NO_OGG - if (init.container == ma_dr_flac_container_ogg) { - ma_dr_flac_oggbs* pInternalOggbs = (ma_dr_flac_oggbs*)((ma_uint8*)pFlac->pDecodedSamples + decodedSamplesAllocationSize + (seekpointCount * sizeof(ma_dr_flac_seekpoint))); - MA_DR_FLAC_COPY_MEMORY(pInternalOggbs, pOggbs, sizeof(*pOggbs)); - ma_dr_flac__free_from_callbacks(pOggbs, &allocationCallbacks); - pOggbs = NULL; - pFlac->bs.onRead = ma_dr_flac__on_read_ogg; - pFlac->bs.onSeek = ma_dr_flac__on_seek_ogg; - pFlac->bs.pUserData = (void*)pInternalOggbs; - pFlac->_oggbs = (void*)pInternalOggbs; - } -#endif - pFlac->firstFLACFramePosInBytes = firstFramePos; -#ifndef MA_DR_FLAC_NO_OGG - if (init.container == ma_dr_flac_container_ogg) - { - pFlac->pSeekpoints = NULL; - pFlac->seekpointCount = 0; - } - else -#endif - { - if (seektablePos != 0) { - pFlac->seekpointCount = seekpointCount; - pFlac->pSeekpoints = (ma_dr_flac_seekpoint*)((ma_uint8*)pFlac->pDecodedSamples + decodedSamplesAllocationSize); - MA_DR_FLAC_ASSERT(pFlac->bs.onSeek != NULL); - MA_DR_FLAC_ASSERT(pFlac->bs.onRead != NULL); - if (pFlac->bs.onSeek(pFlac->bs.pUserData, (int)seektablePos, ma_dr_flac_seek_origin_start)) { - ma_uint32 iSeekpoint; - for (iSeekpoint = 0; iSeekpoint < seekpointCount; iSeekpoint += 1) { - if (pFlac->bs.onRead(pFlac->bs.pUserData, pFlac->pSeekpoints + iSeekpoint, MA_DR_FLAC_SEEKPOINT_SIZE_IN_BYTES) == MA_DR_FLAC_SEEKPOINT_SIZE_IN_BYTES) { - pFlac->pSeekpoints[iSeekpoint].firstPCMFrame = ma_dr_flac__be2host_64(pFlac->pSeekpoints[iSeekpoint].firstPCMFrame); - pFlac->pSeekpoints[iSeekpoint].flacFrameOffset = ma_dr_flac__be2host_64(pFlac->pSeekpoints[iSeekpoint].flacFrameOffset); - pFlac->pSeekpoints[iSeekpoint].pcmFrameCount = ma_dr_flac__be2host_16(pFlac->pSeekpoints[iSeekpoint].pcmFrameCount); - } else { - pFlac->pSeekpoints = NULL; - pFlac->seekpointCount = 0; - break; - } - } - if (!pFlac->bs.onSeek(pFlac->bs.pUserData, (int)pFlac->firstFLACFramePosInBytes, ma_dr_flac_seek_origin_start)) { - ma_dr_flac__free_from_callbacks(pFlac, &allocationCallbacks); - return NULL; - } - } else { - pFlac->pSeekpoints = NULL; - pFlac->seekpointCount = 0; - } - } - } - if (!init.hasStreamInfoBlock) { - pFlac->currentFLACFrame.header = init.firstFrameHeader; - for (;;) { - ma_result result = ma_dr_flac__decode_flac_frame(pFlac); - if (result == MA_SUCCESS) { - break; - } else { - if (result == MA_CRC_MISMATCH) { - if (!ma_dr_flac__read_next_flac_frame_header(&pFlac->bs, pFlac->bitsPerSample, &pFlac->currentFLACFrame.header)) { - ma_dr_flac__free_from_callbacks(pFlac, &allocationCallbacks); - return NULL; - } - continue; - } else { - ma_dr_flac__free_from_callbacks(pFlac, &allocationCallbacks); - return NULL; - } - } - } - } - return pFlac; -} -#ifndef MA_DR_FLAC_NO_STDIO -#include -#ifndef MA_DR_FLAC_NO_WCHAR -#include -#endif -static size_t ma_dr_flac__on_read_stdio(void* pUserData, void* bufferOut, size_t bytesToRead) -{ - return fread(bufferOut, 1, bytesToRead, (FILE*)pUserData); -} -static ma_bool32 ma_dr_flac__on_seek_stdio(void* pUserData, int offset, ma_dr_flac_seek_origin origin) -{ - MA_DR_FLAC_ASSERT(offset >= 0); - return fseek((FILE*)pUserData, offset, (origin == ma_dr_flac_seek_origin_current) ? SEEK_CUR : SEEK_SET) == 0; -} -MA_API ma_dr_flac* ma_dr_flac_open_file(const char* pFileName, const ma_allocation_callbacks* pAllocationCallbacks) -{ - ma_dr_flac* pFlac; - FILE* pFile; - if (ma_fopen(&pFile, pFileName, "rb") != MA_SUCCESS) { - return NULL; - } - pFlac = ma_dr_flac_open(ma_dr_flac__on_read_stdio, ma_dr_flac__on_seek_stdio, (void*)pFile, pAllocationCallbacks); - if (pFlac == NULL) { - fclose(pFile); - return NULL; - } - return pFlac; -} -#ifndef MA_DR_FLAC_NO_WCHAR -MA_API ma_dr_flac* ma_dr_flac_open_file_w(const wchar_t* pFileName, const ma_allocation_callbacks* pAllocationCallbacks) -{ - ma_dr_flac* pFlac; - FILE* pFile; - if (ma_wfopen(&pFile, pFileName, L"rb", pAllocationCallbacks) != MA_SUCCESS) { - return NULL; - } - pFlac = ma_dr_flac_open(ma_dr_flac__on_read_stdio, ma_dr_flac__on_seek_stdio, (void*)pFile, pAllocationCallbacks); - if (pFlac == NULL) { - fclose(pFile); - return NULL; - } - return pFlac; -} -#endif -MA_API ma_dr_flac* ma_dr_flac_open_file_with_metadata(const char* pFileName, ma_dr_flac_meta_proc onMeta, void* pUserData, const ma_allocation_callbacks* pAllocationCallbacks) -{ - ma_dr_flac* pFlac; - FILE* pFile; - if (ma_fopen(&pFile, pFileName, "rb") != MA_SUCCESS) { - return NULL; - } - pFlac = ma_dr_flac_open_with_metadata_private(ma_dr_flac__on_read_stdio, ma_dr_flac__on_seek_stdio, onMeta, ma_dr_flac_container_unknown, (void*)pFile, pUserData, pAllocationCallbacks); - if (pFlac == NULL) { - fclose(pFile); - return pFlac; - } - return pFlac; -} -#ifndef MA_DR_FLAC_NO_WCHAR -MA_API ma_dr_flac* ma_dr_flac_open_file_with_metadata_w(const wchar_t* pFileName, ma_dr_flac_meta_proc onMeta, void* pUserData, const ma_allocation_callbacks* pAllocationCallbacks) -{ - ma_dr_flac* pFlac; - FILE* pFile; - if (ma_wfopen(&pFile, pFileName, L"rb", pAllocationCallbacks) != MA_SUCCESS) { - return NULL; - } - pFlac = ma_dr_flac_open_with_metadata_private(ma_dr_flac__on_read_stdio, ma_dr_flac__on_seek_stdio, onMeta, ma_dr_flac_container_unknown, (void*)pFile, pUserData, pAllocationCallbacks); - if (pFlac == NULL) { - fclose(pFile); - return pFlac; - } - return pFlac; -} -#endif -#endif -static size_t ma_dr_flac__on_read_memory(void* pUserData, void* bufferOut, size_t bytesToRead) -{ - ma_dr_flac__memory_stream* memoryStream = (ma_dr_flac__memory_stream*)pUserData; - size_t bytesRemaining; - MA_DR_FLAC_ASSERT(memoryStream != NULL); - MA_DR_FLAC_ASSERT(memoryStream->dataSize >= memoryStream->currentReadPos); - bytesRemaining = memoryStream->dataSize - memoryStream->currentReadPos; - if (bytesToRead > bytesRemaining) { - bytesToRead = bytesRemaining; - } - if (bytesToRead > 0) { - MA_DR_FLAC_COPY_MEMORY(bufferOut, memoryStream->data + memoryStream->currentReadPos, bytesToRead); - memoryStream->currentReadPos += bytesToRead; - } - return bytesToRead; -} -static ma_bool32 ma_dr_flac__on_seek_memory(void* pUserData, int offset, ma_dr_flac_seek_origin origin) -{ - ma_dr_flac__memory_stream* memoryStream = (ma_dr_flac__memory_stream*)pUserData; - MA_DR_FLAC_ASSERT(memoryStream != NULL); - MA_DR_FLAC_ASSERT(offset >= 0); - if (offset > (ma_int64)memoryStream->dataSize) { - return MA_FALSE; - } - if (origin == ma_dr_flac_seek_origin_current) { - if (memoryStream->currentReadPos + offset <= memoryStream->dataSize) { - memoryStream->currentReadPos += offset; - } else { - return MA_FALSE; - } - } else { - if ((ma_uint32)offset <= memoryStream->dataSize) { - memoryStream->currentReadPos = offset; - } else { - return MA_FALSE; - } - } - return MA_TRUE; -} -MA_API ma_dr_flac* ma_dr_flac_open_memory(const void* pData, size_t dataSize, const ma_allocation_callbacks* pAllocationCallbacks) -{ - ma_dr_flac__memory_stream memoryStream; - ma_dr_flac* pFlac; - memoryStream.data = (const ma_uint8*)pData; - memoryStream.dataSize = dataSize; - memoryStream.currentReadPos = 0; - pFlac = ma_dr_flac_open(ma_dr_flac__on_read_memory, ma_dr_flac__on_seek_memory, &memoryStream, pAllocationCallbacks); - if (pFlac == NULL) { - return NULL; - } - pFlac->memoryStream = memoryStream; -#ifndef MA_DR_FLAC_NO_OGG - if (pFlac->container == ma_dr_flac_container_ogg) - { - ma_dr_flac_oggbs* oggbs = (ma_dr_flac_oggbs*)pFlac->_oggbs; - oggbs->pUserData = &pFlac->memoryStream; - } - else -#endif - { - pFlac->bs.pUserData = &pFlac->memoryStream; - } - return pFlac; -} -MA_API ma_dr_flac* ma_dr_flac_open_memory_with_metadata(const void* pData, size_t dataSize, ma_dr_flac_meta_proc onMeta, void* pUserData, const ma_allocation_callbacks* pAllocationCallbacks) -{ - ma_dr_flac__memory_stream memoryStream; - ma_dr_flac* pFlac; - memoryStream.data = (const ma_uint8*)pData; - memoryStream.dataSize = dataSize; - memoryStream.currentReadPos = 0; - pFlac = ma_dr_flac_open_with_metadata_private(ma_dr_flac__on_read_memory, ma_dr_flac__on_seek_memory, onMeta, ma_dr_flac_container_unknown, &memoryStream, pUserData, pAllocationCallbacks); - if (pFlac == NULL) { - return NULL; - } - pFlac->memoryStream = memoryStream; -#ifndef MA_DR_FLAC_NO_OGG - if (pFlac->container == ma_dr_flac_container_ogg) - { - ma_dr_flac_oggbs* oggbs = (ma_dr_flac_oggbs*)pFlac->_oggbs; - oggbs->pUserData = &pFlac->memoryStream; - } - else -#endif - { - pFlac->bs.pUserData = &pFlac->memoryStream; - } - return pFlac; -} -MA_API ma_dr_flac* ma_dr_flac_open(ma_dr_flac_read_proc onRead, ma_dr_flac_seek_proc onSeek, void* pUserData, const ma_allocation_callbacks* pAllocationCallbacks) -{ - return ma_dr_flac_open_with_metadata_private(onRead, onSeek, NULL, ma_dr_flac_container_unknown, pUserData, pUserData, pAllocationCallbacks); -} -MA_API ma_dr_flac* ma_dr_flac_open_relaxed(ma_dr_flac_read_proc onRead, ma_dr_flac_seek_proc onSeek, ma_dr_flac_container container, void* pUserData, const ma_allocation_callbacks* pAllocationCallbacks) -{ - return ma_dr_flac_open_with_metadata_private(onRead, onSeek, NULL, container, pUserData, pUserData, pAllocationCallbacks); -} -MA_API ma_dr_flac* ma_dr_flac_open_with_metadata(ma_dr_flac_read_proc onRead, ma_dr_flac_seek_proc onSeek, ma_dr_flac_meta_proc onMeta, void* pUserData, const ma_allocation_callbacks* pAllocationCallbacks) -{ - return ma_dr_flac_open_with_metadata_private(onRead, onSeek, onMeta, ma_dr_flac_container_unknown, pUserData, pUserData, pAllocationCallbacks); -} -MA_API ma_dr_flac* ma_dr_flac_open_with_metadata_relaxed(ma_dr_flac_read_proc onRead, ma_dr_flac_seek_proc onSeek, ma_dr_flac_meta_proc onMeta, ma_dr_flac_container container, void* pUserData, const ma_allocation_callbacks* pAllocationCallbacks) -{ - return ma_dr_flac_open_with_metadata_private(onRead, onSeek, onMeta, container, pUserData, pUserData, pAllocationCallbacks); -} -MA_API void ma_dr_flac_close(ma_dr_flac* pFlac) -{ - if (pFlac == NULL) { - return; - } -#ifndef MA_DR_FLAC_NO_STDIO - if (pFlac->bs.onRead == ma_dr_flac__on_read_stdio) { - fclose((FILE*)pFlac->bs.pUserData); - } -#ifndef MA_DR_FLAC_NO_OGG - if (pFlac->container == ma_dr_flac_container_ogg) { - ma_dr_flac_oggbs* oggbs = (ma_dr_flac_oggbs*)pFlac->_oggbs; - MA_DR_FLAC_ASSERT(pFlac->bs.onRead == ma_dr_flac__on_read_ogg); - if (oggbs->onRead == ma_dr_flac__on_read_stdio) { - fclose((FILE*)oggbs->pUserData); - } - } -#endif -#endif - ma_dr_flac__free_from_callbacks(pFlac, &pFlac->allocationCallbacks); -} -#if 0 -static MA_INLINE void ma_dr_flac_read_pcm_frames_s32__decode_left_side__reference(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, ma_int32* pOutputSamples) -{ - ma_uint64 i; - for (i = 0; i < frameCount; ++i) { - ma_uint32 left = (ma_uint32)pInputSamples0[i] << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample); - ma_uint32 side = (ma_uint32)pInputSamples1[i] << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample); - ma_uint32 right = left - side; - pOutputSamples[i*2+0] = (ma_int32)left; - pOutputSamples[i*2+1] = (ma_int32)right; - } -} -#endif -static MA_INLINE void ma_dr_flac_read_pcm_frames_s32__decode_left_side__scalar(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, ma_int32* pOutputSamples) -{ - ma_uint64 i; - ma_uint64 frameCount4 = frameCount >> 2; - const ma_uint32* pInputSamples0U32 = (const ma_uint32*)pInputSamples0; - const ma_uint32* pInputSamples1U32 = (const ma_uint32*)pInputSamples1; - ma_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; - ma_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; - for (i = 0; i < frameCount4; ++i) { - ma_uint32 left0 = pInputSamples0U32[i*4+0] << shift0; - ma_uint32 left1 = pInputSamples0U32[i*4+1] << shift0; - ma_uint32 left2 = pInputSamples0U32[i*4+2] << shift0; - ma_uint32 left3 = pInputSamples0U32[i*4+3] << shift0; - ma_uint32 side0 = pInputSamples1U32[i*4+0] << shift1; - ma_uint32 side1 = pInputSamples1U32[i*4+1] << shift1; - ma_uint32 side2 = pInputSamples1U32[i*4+2] << shift1; - ma_uint32 side3 = pInputSamples1U32[i*4+3] << shift1; - ma_uint32 right0 = left0 - side0; - ma_uint32 right1 = left1 - side1; - ma_uint32 right2 = left2 - side2; - ma_uint32 right3 = left3 - side3; - pOutputSamples[i*8+0] = (ma_int32)left0; - pOutputSamples[i*8+1] = (ma_int32)right0; - pOutputSamples[i*8+2] = (ma_int32)left1; - pOutputSamples[i*8+3] = (ma_int32)right1; - pOutputSamples[i*8+4] = (ma_int32)left2; - pOutputSamples[i*8+5] = (ma_int32)right2; - pOutputSamples[i*8+6] = (ma_int32)left3; - pOutputSamples[i*8+7] = (ma_int32)right3; - } - for (i = (frameCount4 << 2); i < frameCount; ++i) { - ma_uint32 left = pInputSamples0U32[i] << shift0; - ma_uint32 side = pInputSamples1U32[i] << shift1; - ma_uint32 right = left - side; - pOutputSamples[i*2+0] = (ma_int32)left; - pOutputSamples[i*2+1] = (ma_int32)right; - } -} -#if defined(MA_DR_FLAC_SUPPORT_SSE2) -static MA_INLINE void ma_dr_flac_read_pcm_frames_s32__decode_left_side__sse2(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, ma_int32* pOutputSamples) -{ - ma_uint64 i; - ma_uint64 frameCount4 = frameCount >> 2; - const ma_uint32* pInputSamples0U32 = (const ma_uint32*)pInputSamples0; - const ma_uint32* pInputSamples1U32 = (const ma_uint32*)pInputSamples1; - ma_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; - ma_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; - MA_DR_FLAC_ASSERT(pFlac->bitsPerSample <= 24); - for (i = 0; i < frameCount4; ++i) { - __m128i left = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples0 + i), shift0); - __m128i side = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples1 + i), shift1); - __m128i right = _mm_sub_epi32(left, side); - _mm_storeu_si128((__m128i*)(pOutputSamples + i*8 + 0), _mm_unpacklo_epi32(left, right)); - _mm_storeu_si128((__m128i*)(pOutputSamples + i*8 + 4), _mm_unpackhi_epi32(left, right)); - } - for (i = (frameCount4 << 2); i < frameCount; ++i) { - ma_uint32 left = pInputSamples0U32[i] << shift0; - ma_uint32 side = pInputSamples1U32[i] << shift1; - ma_uint32 right = left - side; - pOutputSamples[i*2+0] = (ma_int32)left; - pOutputSamples[i*2+1] = (ma_int32)right; - } -} -#endif -#if defined(MA_DR_FLAC_SUPPORT_NEON) -static MA_INLINE void ma_dr_flac_read_pcm_frames_s32__decode_left_side__neon(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, ma_int32* pOutputSamples) -{ - ma_uint64 i; - ma_uint64 frameCount4 = frameCount >> 2; - const ma_uint32* pInputSamples0U32 = (const ma_uint32*)pInputSamples0; - const ma_uint32* pInputSamples1U32 = (const ma_uint32*)pInputSamples1; - ma_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; - ma_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; - int32x4_t shift0_4; - int32x4_t shift1_4; - MA_DR_FLAC_ASSERT(pFlac->bitsPerSample <= 24); - shift0_4 = vdupq_n_s32(shift0); - shift1_4 = vdupq_n_s32(shift1); - for (i = 0; i < frameCount4; ++i) { - uint32x4_t left; - uint32x4_t side; - uint32x4_t right; - left = vshlq_u32(vld1q_u32(pInputSamples0U32 + i*4), shift0_4); - side = vshlq_u32(vld1q_u32(pInputSamples1U32 + i*4), shift1_4); - right = vsubq_u32(left, side); - ma_dr_flac__vst2q_u32((ma_uint32*)pOutputSamples + i*8, vzipq_u32(left, right)); - } - for (i = (frameCount4 << 2); i < frameCount; ++i) { - ma_uint32 left = pInputSamples0U32[i] << shift0; - ma_uint32 side = pInputSamples1U32[i] << shift1; - ma_uint32 right = left - side; - pOutputSamples[i*2+0] = (ma_int32)left; - pOutputSamples[i*2+1] = (ma_int32)right; - } -} -#endif -static MA_INLINE void ma_dr_flac_read_pcm_frames_s32__decode_left_side(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, ma_int32* pOutputSamples) -{ -#if defined(MA_DR_FLAC_SUPPORT_SSE2) - if (ma_dr_flac__gIsSSE2Supported && pFlac->bitsPerSample <= 24) { - ma_dr_flac_read_pcm_frames_s32__decode_left_side__sse2(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); - } else -#elif defined(MA_DR_FLAC_SUPPORT_NEON) - if (ma_dr_flac__gIsNEONSupported && pFlac->bitsPerSample <= 24) { - ma_dr_flac_read_pcm_frames_s32__decode_left_side__neon(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); - } else -#endif - { -#if 0 - ma_dr_flac_read_pcm_frames_s32__decode_left_side__reference(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); -#else - ma_dr_flac_read_pcm_frames_s32__decode_left_side__scalar(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); -#endif - } -} -#if 0 -static MA_INLINE void ma_dr_flac_read_pcm_frames_s32__decode_right_side__reference(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, ma_int32* pOutputSamples) -{ - ma_uint64 i; - for (i = 0; i < frameCount; ++i) { - ma_uint32 side = (ma_uint32)pInputSamples0[i] << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample); - ma_uint32 right = (ma_uint32)pInputSamples1[i] << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample); - ma_uint32 left = right + side; - pOutputSamples[i*2+0] = (ma_int32)left; - pOutputSamples[i*2+1] = (ma_int32)right; - } -} -#endif -static MA_INLINE void ma_dr_flac_read_pcm_frames_s32__decode_right_side__scalar(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, ma_int32* pOutputSamples) -{ - ma_uint64 i; - ma_uint64 frameCount4 = frameCount >> 2; - const ma_uint32* pInputSamples0U32 = (const ma_uint32*)pInputSamples0; - const ma_uint32* pInputSamples1U32 = (const ma_uint32*)pInputSamples1; - ma_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; - ma_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; - for (i = 0; i < frameCount4; ++i) { - ma_uint32 side0 = pInputSamples0U32[i*4+0] << shift0; - ma_uint32 side1 = pInputSamples0U32[i*4+1] << shift0; - ma_uint32 side2 = pInputSamples0U32[i*4+2] << shift0; - ma_uint32 side3 = pInputSamples0U32[i*4+3] << shift0; - ma_uint32 right0 = pInputSamples1U32[i*4+0] << shift1; - ma_uint32 right1 = pInputSamples1U32[i*4+1] << shift1; - ma_uint32 right2 = pInputSamples1U32[i*4+2] << shift1; - ma_uint32 right3 = pInputSamples1U32[i*4+3] << shift1; - ma_uint32 left0 = right0 + side0; - ma_uint32 left1 = right1 + side1; - ma_uint32 left2 = right2 + side2; - ma_uint32 left3 = right3 + side3; - pOutputSamples[i*8+0] = (ma_int32)left0; - pOutputSamples[i*8+1] = (ma_int32)right0; - pOutputSamples[i*8+2] = (ma_int32)left1; - pOutputSamples[i*8+3] = (ma_int32)right1; - pOutputSamples[i*8+4] = (ma_int32)left2; - pOutputSamples[i*8+5] = (ma_int32)right2; - pOutputSamples[i*8+6] = (ma_int32)left3; - pOutputSamples[i*8+7] = (ma_int32)right3; - } - for (i = (frameCount4 << 2); i < frameCount; ++i) { - ma_uint32 side = pInputSamples0U32[i] << shift0; - ma_uint32 right = pInputSamples1U32[i] << shift1; - ma_uint32 left = right + side; - pOutputSamples[i*2+0] = (ma_int32)left; - pOutputSamples[i*2+1] = (ma_int32)right; - } -} -#if defined(MA_DR_FLAC_SUPPORT_SSE2) -static MA_INLINE void ma_dr_flac_read_pcm_frames_s32__decode_right_side__sse2(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, ma_int32* pOutputSamples) -{ - ma_uint64 i; - ma_uint64 frameCount4 = frameCount >> 2; - const ma_uint32* pInputSamples0U32 = (const ma_uint32*)pInputSamples0; - const ma_uint32* pInputSamples1U32 = (const ma_uint32*)pInputSamples1; - ma_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; - ma_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; - MA_DR_FLAC_ASSERT(pFlac->bitsPerSample <= 24); - for (i = 0; i < frameCount4; ++i) { - __m128i side = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples0 + i), shift0); - __m128i right = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples1 + i), shift1); - __m128i left = _mm_add_epi32(right, side); - _mm_storeu_si128((__m128i*)(pOutputSamples + i*8 + 0), _mm_unpacklo_epi32(left, right)); - _mm_storeu_si128((__m128i*)(pOutputSamples + i*8 + 4), _mm_unpackhi_epi32(left, right)); - } - for (i = (frameCount4 << 2); i < frameCount; ++i) { - ma_uint32 side = pInputSamples0U32[i] << shift0; - ma_uint32 right = pInputSamples1U32[i] << shift1; - ma_uint32 left = right + side; - pOutputSamples[i*2+0] = (ma_int32)left; - pOutputSamples[i*2+1] = (ma_int32)right; - } -} -#endif -#if defined(MA_DR_FLAC_SUPPORT_NEON) -static MA_INLINE void ma_dr_flac_read_pcm_frames_s32__decode_right_side__neon(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, ma_int32* pOutputSamples) -{ - ma_uint64 i; - ma_uint64 frameCount4 = frameCount >> 2; - const ma_uint32* pInputSamples0U32 = (const ma_uint32*)pInputSamples0; - const ma_uint32* pInputSamples1U32 = (const ma_uint32*)pInputSamples1; - ma_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; - ma_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; - int32x4_t shift0_4; - int32x4_t shift1_4; - MA_DR_FLAC_ASSERT(pFlac->bitsPerSample <= 24); - shift0_4 = vdupq_n_s32(shift0); - shift1_4 = vdupq_n_s32(shift1); - for (i = 0; i < frameCount4; ++i) { - uint32x4_t side; - uint32x4_t right; - uint32x4_t left; - side = vshlq_u32(vld1q_u32(pInputSamples0U32 + i*4), shift0_4); - right = vshlq_u32(vld1q_u32(pInputSamples1U32 + i*4), shift1_4); - left = vaddq_u32(right, side); - ma_dr_flac__vst2q_u32((ma_uint32*)pOutputSamples + i*8, vzipq_u32(left, right)); - } - for (i = (frameCount4 << 2); i < frameCount; ++i) { - ma_uint32 side = pInputSamples0U32[i] << shift0; - ma_uint32 right = pInputSamples1U32[i] << shift1; - ma_uint32 left = right + side; - pOutputSamples[i*2+0] = (ma_int32)left; - pOutputSamples[i*2+1] = (ma_int32)right; - } -} -#endif -static MA_INLINE void ma_dr_flac_read_pcm_frames_s32__decode_right_side(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, ma_int32* pOutputSamples) -{ -#if defined(MA_DR_FLAC_SUPPORT_SSE2) - if (ma_dr_flac__gIsSSE2Supported && pFlac->bitsPerSample <= 24) { - ma_dr_flac_read_pcm_frames_s32__decode_right_side__sse2(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); - } else -#elif defined(MA_DR_FLAC_SUPPORT_NEON) - if (ma_dr_flac__gIsNEONSupported && pFlac->bitsPerSample <= 24) { - ma_dr_flac_read_pcm_frames_s32__decode_right_side__neon(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); - } else -#endif - { -#if 0 - ma_dr_flac_read_pcm_frames_s32__decode_right_side__reference(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); -#else - ma_dr_flac_read_pcm_frames_s32__decode_right_side__scalar(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); -#endif - } -} -#if 0 -static MA_INLINE void ma_dr_flac_read_pcm_frames_s32__decode_mid_side__reference(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, ma_int32* pOutputSamples) -{ - for (ma_uint64 i = 0; i < frameCount; ++i) { - ma_uint32 mid = pInputSamples0U32[i] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; - ma_uint32 side = pInputSamples1U32[i] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; - mid = (mid << 1) | (side & 0x01); - pOutputSamples[i*2+0] = (ma_int32)((ma_uint32)((ma_int32)(mid + side) >> 1) << unusedBitsPerSample); - pOutputSamples[i*2+1] = (ma_int32)((ma_uint32)((ma_int32)(mid - side) >> 1) << unusedBitsPerSample); - } -} -#endif -static MA_INLINE void ma_dr_flac_read_pcm_frames_s32__decode_mid_side__scalar(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, ma_int32* pOutputSamples) -{ - ma_uint64 i; - ma_uint64 frameCount4 = frameCount >> 2; - const ma_uint32* pInputSamples0U32 = (const ma_uint32*)pInputSamples0; - const ma_uint32* pInputSamples1U32 = (const ma_uint32*)pInputSamples1; - ma_int32 shift = unusedBitsPerSample; - if (shift > 0) { - shift -= 1; - for (i = 0; i < frameCount4; ++i) { - ma_uint32 temp0L; - ma_uint32 temp1L; - ma_uint32 temp2L; - ma_uint32 temp3L; - ma_uint32 temp0R; - ma_uint32 temp1R; - ma_uint32 temp2R; - ma_uint32 temp3R; - ma_uint32 mid0 = pInputSamples0U32[i*4+0] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; - ma_uint32 mid1 = pInputSamples0U32[i*4+1] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; - ma_uint32 mid2 = pInputSamples0U32[i*4+2] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; - ma_uint32 mid3 = pInputSamples0U32[i*4+3] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; - ma_uint32 side0 = pInputSamples1U32[i*4+0] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; - ma_uint32 side1 = pInputSamples1U32[i*4+1] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; - ma_uint32 side2 = pInputSamples1U32[i*4+2] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; - ma_uint32 side3 = pInputSamples1U32[i*4+3] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; - mid0 = (mid0 << 1) | (side0 & 0x01); - mid1 = (mid1 << 1) | (side1 & 0x01); - mid2 = (mid2 << 1) | (side2 & 0x01); - mid3 = (mid3 << 1) | (side3 & 0x01); - temp0L = (mid0 + side0) << shift; - temp1L = (mid1 + side1) << shift; - temp2L = (mid2 + side2) << shift; - temp3L = (mid3 + side3) << shift; - temp0R = (mid0 - side0) << shift; - temp1R = (mid1 - side1) << shift; - temp2R = (mid2 - side2) << shift; - temp3R = (mid3 - side3) << shift; - pOutputSamples[i*8+0] = (ma_int32)temp0L; - pOutputSamples[i*8+1] = (ma_int32)temp0R; - pOutputSamples[i*8+2] = (ma_int32)temp1L; - pOutputSamples[i*8+3] = (ma_int32)temp1R; - pOutputSamples[i*8+4] = (ma_int32)temp2L; - pOutputSamples[i*8+5] = (ma_int32)temp2R; - pOutputSamples[i*8+6] = (ma_int32)temp3L; - pOutputSamples[i*8+7] = (ma_int32)temp3R; - } - } else { - for (i = 0; i < frameCount4; ++i) { - ma_uint32 temp0L; - ma_uint32 temp1L; - ma_uint32 temp2L; - ma_uint32 temp3L; - ma_uint32 temp0R; - ma_uint32 temp1R; - ma_uint32 temp2R; - ma_uint32 temp3R; - ma_uint32 mid0 = pInputSamples0U32[i*4+0] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; - ma_uint32 mid1 = pInputSamples0U32[i*4+1] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; - ma_uint32 mid2 = pInputSamples0U32[i*4+2] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; - ma_uint32 mid3 = pInputSamples0U32[i*4+3] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; - ma_uint32 side0 = pInputSamples1U32[i*4+0] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; - ma_uint32 side1 = pInputSamples1U32[i*4+1] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; - ma_uint32 side2 = pInputSamples1U32[i*4+2] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; - ma_uint32 side3 = pInputSamples1U32[i*4+3] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; - mid0 = (mid0 << 1) | (side0 & 0x01); - mid1 = (mid1 << 1) | (side1 & 0x01); - mid2 = (mid2 << 1) | (side2 & 0x01); - mid3 = (mid3 << 1) | (side3 & 0x01); - temp0L = (ma_uint32)((ma_int32)(mid0 + side0) >> 1); - temp1L = (ma_uint32)((ma_int32)(mid1 + side1) >> 1); - temp2L = (ma_uint32)((ma_int32)(mid2 + side2) >> 1); - temp3L = (ma_uint32)((ma_int32)(mid3 + side3) >> 1); - temp0R = (ma_uint32)((ma_int32)(mid0 - side0) >> 1); - temp1R = (ma_uint32)((ma_int32)(mid1 - side1) >> 1); - temp2R = (ma_uint32)((ma_int32)(mid2 - side2) >> 1); - temp3R = (ma_uint32)((ma_int32)(mid3 - side3) >> 1); - pOutputSamples[i*8+0] = (ma_int32)temp0L; - pOutputSamples[i*8+1] = (ma_int32)temp0R; - pOutputSamples[i*8+2] = (ma_int32)temp1L; - pOutputSamples[i*8+3] = (ma_int32)temp1R; - pOutputSamples[i*8+4] = (ma_int32)temp2L; - pOutputSamples[i*8+5] = (ma_int32)temp2R; - pOutputSamples[i*8+6] = (ma_int32)temp3L; - pOutputSamples[i*8+7] = (ma_int32)temp3R; - } - } - for (i = (frameCount4 << 2); i < frameCount; ++i) { - ma_uint32 mid = pInputSamples0U32[i] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; - ma_uint32 side = pInputSamples1U32[i] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; - mid = (mid << 1) | (side & 0x01); - pOutputSamples[i*2+0] = (ma_int32)((ma_uint32)((ma_int32)(mid + side) >> 1) << unusedBitsPerSample); - pOutputSamples[i*2+1] = (ma_int32)((ma_uint32)((ma_int32)(mid - side) >> 1) << unusedBitsPerSample); - } -} -#if defined(MA_DR_FLAC_SUPPORT_SSE2) -static MA_INLINE void ma_dr_flac_read_pcm_frames_s32__decode_mid_side__sse2(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, ma_int32* pOutputSamples) -{ - ma_uint64 i; - ma_uint64 frameCount4 = frameCount >> 2; - const ma_uint32* pInputSamples0U32 = (const ma_uint32*)pInputSamples0; - const ma_uint32* pInputSamples1U32 = (const ma_uint32*)pInputSamples1; - ma_int32 shift = unusedBitsPerSample; - MA_DR_FLAC_ASSERT(pFlac->bitsPerSample <= 24); - if (shift == 0) { - for (i = 0; i < frameCount4; ++i) { - __m128i mid; - __m128i side; - __m128i left; - __m128i right; - mid = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples0 + i), pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample); - side = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples1 + i), pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample); - mid = _mm_or_si128(_mm_slli_epi32(mid, 1), _mm_and_si128(side, _mm_set1_epi32(0x01))); - left = _mm_srai_epi32(_mm_add_epi32(mid, side), 1); - right = _mm_srai_epi32(_mm_sub_epi32(mid, side), 1); - _mm_storeu_si128((__m128i*)(pOutputSamples + i*8 + 0), _mm_unpacklo_epi32(left, right)); - _mm_storeu_si128((__m128i*)(pOutputSamples + i*8 + 4), _mm_unpackhi_epi32(left, right)); - } - for (i = (frameCount4 << 2); i < frameCount; ++i) { - ma_uint32 mid = pInputSamples0U32[i] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; - ma_uint32 side = pInputSamples1U32[i] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; - mid = (mid << 1) | (side & 0x01); - pOutputSamples[i*2+0] = (ma_int32)(mid + side) >> 1; - pOutputSamples[i*2+1] = (ma_int32)(mid - side) >> 1; - } - } else { - shift -= 1; - for (i = 0; i < frameCount4; ++i) { - __m128i mid; - __m128i side; - __m128i left; - __m128i right; - mid = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples0 + i), pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample); - side = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples1 + i), pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample); - mid = _mm_or_si128(_mm_slli_epi32(mid, 1), _mm_and_si128(side, _mm_set1_epi32(0x01))); - left = _mm_slli_epi32(_mm_add_epi32(mid, side), shift); - right = _mm_slli_epi32(_mm_sub_epi32(mid, side), shift); - _mm_storeu_si128((__m128i*)(pOutputSamples + i*8 + 0), _mm_unpacklo_epi32(left, right)); - _mm_storeu_si128((__m128i*)(pOutputSamples + i*8 + 4), _mm_unpackhi_epi32(left, right)); - } - for (i = (frameCount4 << 2); i < frameCount; ++i) { - ma_uint32 mid = pInputSamples0U32[i] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; - ma_uint32 side = pInputSamples1U32[i] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; - mid = (mid << 1) | (side & 0x01); - pOutputSamples[i*2+0] = (ma_int32)((mid + side) << shift); - pOutputSamples[i*2+1] = (ma_int32)((mid - side) << shift); - } - } -} -#endif -#if defined(MA_DR_FLAC_SUPPORT_NEON) -static MA_INLINE void ma_dr_flac_read_pcm_frames_s32__decode_mid_side__neon(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, ma_int32* pOutputSamples) -{ - ma_uint64 i; - ma_uint64 frameCount4 = frameCount >> 2; - const ma_uint32* pInputSamples0U32 = (const ma_uint32*)pInputSamples0; - const ma_uint32* pInputSamples1U32 = (const ma_uint32*)pInputSamples1; - ma_int32 shift = unusedBitsPerSample; - int32x4_t wbpsShift0_4; - int32x4_t wbpsShift1_4; - uint32x4_t one4; - MA_DR_FLAC_ASSERT(pFlac->bitsPerSample <= 24); - wbpsShift0_4 = vdupq_n_s32(pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample); - wbpsShift1_4 = vdupq_n_s32(pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample); - one4 = vdupq_n_u32(1); - if (shift == 0) { - for (i = 0; i < frameCount4; ++i) { - uint32x4_t mid; - uint32x4_t side; - int32x4_t left; - int32x4_t right; - mid = vshlq_u32(vld1q_u32(pInputSamples0U32 + i*4), wbpsShift0_4); - side = vshlq_u32(vld1q_u32(pInputSamples1U32 + i*4), wbpsShift1_4); - mid = vorrq_u32(vshlq_n_u32(mid, 1), vandq_u32(side, one4)); - left = vshrq_n_s32(vreinterpretq_s32_u32(vaddq_u32(mid, side)), 1); - right = vshrq_n_s32(vreinterpretq_s32_u32(vsubq_u32(mid, side)), 1); - ma_dr_flac__vst2q_s32(pOutputSamples + i*8, vzipq_s32(left, right)); - } - for (i = (frameCount4 << 2); i < frameCount; ++i) { - ma_uint32 mid = pInputSamples0U32[i] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; - ma_uint32 side = pInputSamples1U32[i] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; - mid = (mid << 1) | (side & 0x01); - pOutputSamples[i*2+0] = (ma_int32)(mid + side) >> 1; - pOutputSamples[i*2+1] = (ma_int32)(mid - side) >> 1; - } - } else { - int32x4_t shift4; - shift -= 1; - shift4 = vdupq_n_s32(shift); - for (i = 0; i < frameCount4; ++i) { - uint32x4_t mid; - uint32x4_t side; - int32x4_t left; - int32x4_t right; - mid = vshlq_u32(vld1q_u32(pInputSamples0U32 + i*4), wbpsShift0_4); - side = vshlq_u32(vld1q_u32(pInputSamples1U32 + i*4), wbpsShift1_4); - mid = vorrq_u32(vshlq_n_u32(mid, 1), vandq_u32(side, one4)); - left = vreinterpretq_s32_u32(vshlq_u32(vaddq_u32(mid, side), shift4)); - right = vreinterpretq_s32_u32(vshlq_u32(vsubq_u32(mid, side), shift4)); - ma_dr_flac__vst2q_s32(pOutputSamples + i*8, vzipq_s32(left, right)); - } - for (i = (frameCount4 << 2); i < frameCount; ++i) { - ma_uint32 mid = pInputSamples0U32[i] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; - ma_uint32 side = pInputSamples1U32[i] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; - mid = (mid << 1) | (side & 0x01); - pOutputSamples[i*2+0] = (ma_int32)((mid + side) << shift); - pOutputSamples[i*2+1] = (ma_int32)((mid - side) << shift); - } - } -} -#endif -static MA_INLINE void ma_dr_flac_read_pcm_frames_s32__decode_mid_side(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, ma_int32* pOutputSamples) -{ -#if defined(MA_DR_FLAC_SUPPORT_SSE2) - if (ma_dr_flac__gIsSSE2Supported && pFlac->bitsPerSample <= 24) { - ma_dr_flac_read_pcm_frames_s32__decode_mid_side__sse2(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); - } else -#elif defined(MA_DR_FLAC_SUPPORT_NEON) - if (ma_dr_flac__gIsNEONSupported && pFlac->bitsPerSample <= 24) { - ma_dr_flac_read_pcm_frames_s32__decode_mid_side__neon(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); - } else -#endif - { -#if 0 - ma_dr_flac_read_pcm_frames_s32__decode_mid_side__reference(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); -#else - ma_dr_flac_read_pcm_frames_s32__decode_mid_side__scalar(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); -#endif - } -} -#if 0 -static MA_INLINE void ma_dr_flac_read_pcm_frames_s32__decode_independent_stereo__reference(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, ma_int32* pOutputSamples) -{ - for (ma_uint64 i = 0; i < frameCount; ++i) { - pOutputSamples[i*2+0] = (ma_int32)((ma_uint32)pInputSamples0[i] << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample)); - pOutputSamples[i*2+1] = (ma_int32)((ma_uint32)pInputSamples1[i] << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample)); - } -} -#endif -static MA_INLINE void ma_dr_flac_read_pcm_frames_s32__decode_independent_stereo__scalar(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, ma_int32* pOutputSamples) -{ - ma_uint64 i; - ma_uint64 frameCount4 = frameCount >> 2; - const ma_uint32* pInputSamples0U32 = (const ma_uint32*)pInputSamples0; - const ma_uint32* pInputSamples1U32 = (const ma_uint32*)pInputSamples1; - ma_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; - ma_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; - for (i = 0; i < frameCount4; ++i) { - ma_uint32 tempL0 = pInputSamples0U32[i*4+0] << shift0; - ma_uint32 tempL1 = pInputSamples0U32[i*4+1] << shift0; - ma_uint32 tempL2 = pInputSamples0U32[i*4+2] << shift0; - ma_uint32 tempL3 = pInputSamples0U32[i*4+3] << shift0; - ma_uint32 tempR0 = pInputSamples1U32[i*4+0] << shift1; - ma_uint32 tempR1 = pInputSamples1U32[i*4+1] << shift1; - ma_uint32 tempR2 = pInputSamples1U32[i*4+2] << shift1; - ma_uint32 tempR3 = pInputSamples1U32[i*4+3] << shift1; - pOutputSamples[i*8+0] = (ma_int32)tempL0; - pOutputSamples[i*8+1] = (ma_int32)tempR0; - pOutputSamples[i*8+2] = (ma_int32)tempL1; - pOutputSamples[i*8+3] = (ma_int32)tempR1; - pOutputSamples[i*8+4] = (ma_int32)tempL2; - pOutputSamples[i*8+5] = (ma_int32)tempR2; - pOutputSamples[i*8+6] = (ma_int32)tempL3; - pOutputSamples[i*8+7] = (ma_int32)tempR3; - } - for (i = (frameCount4 << 2); i < frameCount; ++i) { - pOutputSamples[i*2+0] = (ma_int32)(pInputSamples0U32[i] << shift0); - pOutputSamples[i*2+1] = (ma_int32)(pInputSamples1U32[i] << shift1); - } -} -#if defined(MA_DR_FLAC_SUPPORT_SSE2) -static MA_INLINE void ma_dr_flac_read_pcm_frames_s32__decode_independent_stereo__sse2(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, ma_int32* pOutputSamples) -{ - ma_uint64 i; - ma_uint64 frameCount4 = frameCount >> 2; - const ma_uint32* pInputSamples0U32 = (const ma_uint32*)pInputSamples0; - const ma_uint32* pInputSamples1U32 = (const ma_uint32*)pInputSamples1; - ma_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; - ma_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; - for (i = 0; i < frameCount4; ++i) { - __m128i left = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples0 + i), shift0); - __m128i right = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples1 + i), shift1); - _mm_storeu_si128((__m128i*)(pOutputSamples + i*8 + 0), _mm_unpacklo_epi32(left, right)); - _mm_storeu_si128((__m128i*)(pOutputSamples + i*8 + 4), _mm_unpackhi_epi32(left, right)); - } - for (i = (frameCount4 << 2); i < frameCount; ++i) { - pOutputSamples[i*2+0] = (ma_int32)(pInputSamples0U32[i] << shift0); - pOutputSamples[i*2+1] = (ma_int32)(pInputSamples1U32[i] << shift1); - } -} -#endif -#if defined(MA_DR_FLAC_SUPPORT_NEON) -static MA_INLINE void ma_dr_flac_read_pcm_frames_s32__decode_independent_stereo__neon(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, ma_int32* pOutputSamples) -{ - ma_uint64 i; - ma_uint64 frameCount4 = frameCount >> 2; - const ma_uint32* pInputSamples0U32 = (const ma_uint32*)pInputSamples0; - const ma_uint32* pInputSamples1U32 = (const ma_uint32*)pInputSamples1; - ma_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; - ma_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; - int32x4_t shift4_0 = vdupq_n_s32(shift0); - int32x4_t shift4_1 = vdupq_n_s32(shift1); - for (i = 0; i < frameCount4; ++i) { - int32x4_t left; - int32x4_t right; - left = vreinterpretq_s32_u32(vshlq_u32(vld1q_u32(pInputSamples0U32 + i*4), shift4_0)); - right = vreinterpretq_s32_u32(vshlq_u32(vld1q_u32(pInputSamples1U32 + i*4), shift4_1)); - ma_dr_flac__vst2q_s32(pOutputSamples + i*8, vzipq_s32(left, right)); - } - for (i = (frameCount4 << 2); i < frameCount; ++i) { - pOutputSamples[i*2+0] = (ma_int32)(pInputSamples0U32[i] << shift0); - pOutputSamples[i*2+1] = (ma_int32)(pInputSamples1U32[i] << shift1); - } -} -#endif -static MA_INLINE void ma_dr_flac_read_pcm_frames_s32__decode_independent_stereo(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, ma_int32* pOutputSamples) -{ -#if defined(MA_DR_FLAC_SUPPORT_SSE2) - if (ma_dr_flac__gIsSSE2Supported && pFlac->bitsPerSample <= 24) { - ma_dr_flac_read_pcm_frames_s32__decode_independent_stereo__sse2(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); - } else -#elif defined(MA_DR_FLAC_SUPPORT_NEON) - if (ma_dr_flac__gIsNEONSupported && pFlac->bitsPerSample <= 24) { - ma_dr_flac_read_pcm_frames_s32__decode_independent_stereo__neon(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); - } else -#endif - { -#if 0 - ma_dr_flac_read_pcm_frames_s32__decode_independent_stereo__reference(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); -#else - ma_dr_flac_read_pcm_frames_s32__decode_independent_stereo__scalar(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); -#endif - } -} -MA_API ma_uint64 ma_dr_flac_read_pcm_frames_s32(ma_dr_flac* pFlac, ma_uint64 framesToRead, ma_int32* pBufferOut) -{ - ma_uint64 framesRead; - ma_uint32 unusedBitsPerSample; - if (pFlac == NULL || framesToRead == 0) { - return 0; - } - if (pBufferOut == NULL) { - return ma_dr_flac__seek_forward_by_pcm_frames(pFlac, framesToRead); - } - MA_DR_FLAC_ASSERT(pFlac->bitsPerSample <= 32); - unusedBitsPerSample = 32 - pFlac->bitsPerSample; - framesRead = 0; - while (framesToRead > 0) { - if (pFlac->currentFLACFrame.pcmFramesRemaining == 0) { - if (!ma_dr_flac__read_and_decode_next_flac_frame(pFlac)) { - break; - } - } else { - unsigned int channelCount = ma_dr_flac__get_channel_count_from_channel_assignment(pFlac->currentFLACFrame.header.channelAssignment); - ma_uint64 iFirstPCMFrame = pFlac->currentFLACFrame.header.blockSizeInPCMFrames - pFlac->currentFLACFrame.pcmFramesRemaining; - ma_uint64 frameCountThisIteration = framesToRead; - if (frameCountThisIteration > pFlac->currentFLACFrame.pcmFramesRemaining) { - frameCountThisIteration = pFlac->currentFLACFrame.pcmFramesRemaining; - } - if (channelCount == 2) { - const ma_int32* pDecodedSamples0 = pFlac->currentFLACFrame.subframes[0].pSamplesS32 + iFirstPCMFrame; - const ma_int32* pDecodedSamples1 = pFlac->currentFLACFrame.subframes[1].pSamplesS32 + iFirstPCMFrame; - switch (pFlac->currentFLACFrame.header.channelAssignment) - { - case MA_DR_FLAC_CHANNEL_ASSIGNMENT_LEFT_SIDE: - { - ma_dr_flac_read_pcm_frames_s32__decode_left_side(pFlac, frameCountThisIteration, unusedBitsPerSample, pDecodedSamples0, pDecodedSamples1, pBufferOut); - } break; - case MA_DR_FLAC_CHANNEL_ASSIGNMENT_RIGHT_SIDE: - { - ma_dr_flac_read_pcm_frames_s32__decode_right_side(pFlac, frameCountThisIteration, unusedBitsPerSample, pDecodedSamples0, pDecodedSamples1, pBufferOut); - } break; - case MA_DR_FLAC_CHANNEL_ASSIGNMENT_MID_SIDE: - { - ma_dr_flac_read_pcm_frames_s32__decode_mid_side(pFlac, frameCountThisIteration, unusedBitsPerSample, pDecodedSamples0, pDecodedSamples1, pBufferOut); - } break; - case MA_DR_FLAC_CHANNEL_ASSIGNMENT_INDEPENDENT: - default: - { - ma_dr_flac_read_pcm_frames_s32__decode_independent_stereo(pFlac, frameCountThisIteration, unusedBitsPerSample, pDecodedSamples0, pDecodedSamples1, pBufferOut); - } break; - } - } else { - ma_uint64 i; - for (i = 0; i < frameCountThisIteration; ++i) { - unsigned int j; - for (j = 0; j < channelCount; ++j) { - pBufferOut[(i*channelCount)+j] = (ma_int32)((ma_uint32)(pFlac->currentFLACFrame.subframes[j].pSamplesS32[iFirstPCMFrame + i]) << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[j].wastedBitsPerSample)); - } - } - } - framesRead += frameCountThisIteration; - pBufferOut += frameCountThisIteration * channelCount; - framesToRead -= frameCountThisIteration; - pFlac->currentPCMFrame += frameCountThisIteration; - pFlac->currentFLACFrame.pcmFramesRemaining -= (ma_uint32)frameCountThisIteration; - } - } - return framesRead; -} -#if 0 -static MA_INLINE void ma_dr_flac_read_pcm_frames_s16__decode_left_side__reference(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, ma_int16* pOutputSamples) -{ - ma_uint64 i; - for (i = 0; i < frameCount; ++i) { - ma_uint32 left = (ma_uint32)pInputSamples0[i] << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample); - ma_uint32 side = (ma_uint32)pInputSamples1[i] << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample); - ma_uint32 right = left - side; - left >>= 16; - right >>= 16; - pOutputSamples[i*2+0] = (ma_int16)left; - pOutputSamples[i*2+1] = (ma_int16)right; - } -} -#endif -static MA_INLINE void ma_dr_flac_read_pcm_frames_s16__decode_left_side__scalar(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, ma_int16* pOutputSamples) -{ - ma_uint64 i; - ma_uint64 frameCount4 = frameCount >> 2; - const ma_uint32* pInputSamples0U32 = (const ma_uint32*)pInputSamples0; - const ma_uint32* pInputSamples1U32 = (const ma_uint32*)pInputSamples1; - ma_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; - ma_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; - for (i = 0; i < frameCount4; ++i) { - ma_uint32 left0 = pInputSamples0U32[i*4+0] << shift0; - ma_uint32 left1 = pInputSamples0U32[i*4+1] << shift0; - ma_uint32 left2 = pInputSamples0U32[i*4+2] << shift0; - ma_uint32 left3 = pInputSamples0U32[i*4+3] << shift0; - ma_uint32 side0 = pInputSamples1U32[i*4+0] << shift1; - ma_uint32 side1 = pInputSamples1U32[i*4+1] << shift1; - ma_uint32 side2 = pInputSamples1U32[i*4+2] << shift1; - ma_uint32 side3 = pInputSamples1U32[i*4+3] << shift1; - ma_uint32 right0 = left0 - side0; - ma_uint32 right1 = left1 - side1; - ma_uint32 right2 = left2 - side2; - ma_uint32 right3 = left3 - side3; - left0 >>= 16; - left1 >>= 16; - left2 >>= 16; - left3 >>= 16; - right0 >>= 16; - right1 >>= 16; - right2 >>= 16; - right3 >>= 16; - pOutputSamples[i*8+0] = (ma_int16)left0; - pOutputSamples[i*8+1] = (ma_int16)right0; - pOutputSamples[i*8+2] = (ma_int16)left1; - pOutputSamples[i*8+3] = (ma_int16)right1; - pOutputSamples[i*8+4] = (ma_int16)left2; - pOutputSamples[i*8+5] = (ma_int16)right2; - pOutputSamples[i*8+6] = (ma_int16)left3; - pOutputSamples[i*8+7] = (ma_int16)right3; - } - for (i = (frameCount4 << 2); i < frameCount; ++i) { - ma_uint32 left = pInputSamples0U32[i] << shift0; - ma_uint32 side = pInputSamples1U32[i] << shift1; - ma_uint32 right = left - side; - left >>= 16; - right >>= 16; - pOutputSamples[i*2+0] = (ma_int16)left; - pOutputSamples[i*2+1] = (ma_int16)right; - } -} -#if defined(MA_DR_FLAC_SUPPORT_SSE2) -static MA_INLINE void ma_dr_flac_read_pcm_frames_s16__decode_left_side__sse2(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, ma_int16* pOutputSamples) -{ - ma_uint64 i; - ma_uint64 frameCount4 = frameCount >> 2; - const ma_uint32* pInputSamples0U32 = (const ma_uint32*)pInputSamples0; - const ma_uint32* pInputSamples1U32 = (const ma_uint32*)pInputSamples1; - ma_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; - ma_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; - MA_DR_FLAC_ASSERT(pFlac->bitsPerSample <= 24); - for (i = 0; i < frameCount4; ++i) { - __m128i left = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples0 + i), shift0); - __m128i side = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples1 + i), shift1); - __m128i right = _mm_sub_epi32(left, side); - left = _mm_srai_epi32(left, 16); - right = _mm_srai_epi32(right, 16); - _mm_storeu_si128((__m128i*)(pOutputSamples + i*8), ma_dr_flac__mm_packs_interleaved_epi32(left, right)); - } - for (i = (frameCount4 << 2); i < frameCount; ++i) { - ma_uint32 left = pInputSamples0U32[i] << shift0; - ma_uint32 side = pInputSamples1U32[i] << shift1; - ma_uint32 right = left - side; - left >>= 16; - right >>= 16; - pOutputSamples[i*2+0] = (ma_int16)left; - pOutputSamples[i*2+1] = (ma_int16)right; - } -} -#endif -#if defined(MA_DR_FLAC_SUPPORT_NEON) -static MA_INLINE void ma_dr_flac_read_pcm_frames_s16__decode_left_side__neon(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, ma_int16* pOutputSamples) -{ - ma_uint64 i; - ma_uint64 frameCount4 = frameCount >> 2; - const ma_uint32* pInputSamples0U32 = (const ma_uint32*)pInputSamples0; - const ma_uint32* pInputSamples1U32 = (const ma_uint32*)pInputSamples1; - ma_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; - ma_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; - int32x4_t shift0_4; - int32x4_t shift1_4; - MA_DR_FLAC_ASSERT(pFlac->bitsPerSample <= 24); - shift0_4 = vdupq_n_s32(shift0); - shift1_4 = vdupq_n_s32(shift1); - for (i = 0; i < frameCount4; ++i) { - uint32x4_t left; - uint32x4_t side; - uint32x4_t right; - left = vshlq_u32(vld1q_u32(pInputSamples0U32 + i*4), shift0_4); - side = vshlq_u32(vld1q_u32(pInputSamples1U32 + i*4), shift1_4); - right = vsubq_u32(left, side); - left = vshrq_n_u32(left, 16); - right = vshrq_n_u32(right, 16); - ma_dr_flac__vst2q_u16((ma_uint16*)pOutputSamples + i*8, vzip_u16(vmovn_u32(left), vmovn_u32(right))); - } - for (i = (frameCount4 << 2); i < frameCount; ++i) { - ma_uint32 left = pInputSamples0U32[i] << shift0; - ma_uint32 side = pInputSamples1U32[i] << shift1; - ma_uint32 right = left - side; - left >>= 16; - right >>= 16; - pOutputSamples[i*2+0] = (ma_int16)left; - pOutputSamples[i*2+1] = (ma_int16)right; - } -} -#endif -static MA_INLINE void ma_dr_flac_read_pcm_frames_s16__decode_left_side(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, ma_int16* pOutputSamples) -{ -#if defined(MA_DR_FLAC_SUPPORT_SSE2) - if (ma_dr_flac__gIsSSE2Supported && pFlac->bitsPerSample <= 24) { - ma_dr_flac_read_pcm_frames_s16__decode_left_side__sse2(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); - } else -#elif defined(MA_DR_FLAC_SUPPORT_NEON) - if (ma_dr_flac__gIsNEONSupported && pFlac->bitsPerSample <= 24) { - ma_dr_flac_read_pcm_frames_s16__decode_left_side__neon(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); - } else -#endif - { -#if 0 - ma_dr_flac_read_pcm_frames_s16__decode_left_side__reference(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); -#else - ma_dr_flac_read_pcm_frames_s16__decode_left_side__scalar(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); -#endif - } -} -#if 0 -static MA_INLINE void ma_dr_flac_read_pcm_frames_s16__decode_right_side__reference(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, ma_int16* pOutputSamples) -{ - ma_uint64 i; - for (i = 0; i < frameCount; ++i) { - ma_uint32 side = (ma_uint32)pInputSamples0[i] << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample); - ma_uint32 right = (ma_uint32)pInputSamples1[i] << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample); - ma_uint32 left = right + side; - left >>= 16; - right >>= 16; - pOutputSamples[i*2+0] = (ma_int16)left; - pOutputSamples[i*2+1] = (ma_int16)right; - } -} -#endif -static MA_INLINE void ma_dr_flac_read_pcm_frames_s16__decode_right_side__scalar(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, ma_int16* pOutputSamples) -{ - ma_uint64 i; - ma_uint64 frameCount4 = frameCount >> 2; - const ma_uint32* pInputSamples0U32 = (const ma_uint32*)pInputSamples0; - const ma_uint32* pInputSamples1U32 = (const ma_uint32*)pInputSamples1; - ma_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; - ma_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; - for (i = 0; i < frameCount4; ++i) { - ma_uint32 side0 = pInputSamples0U32[i*4+0] << shift0; - ma_uint32 side1 = pInputSamples0U32[i*4+1] << shift0; - ma_uint32 side2 = pInputSamples0U32[i*4+2] << shift0; - ma_uint32 side3 = pInputSamples0U32[i*4+3] << shift0; - ma_uint32 right0 = pInputSamples1U32[i*4+0] << shift1; - ma_uint32 right1 = pInputSamples1U32[i*4+1] << shift1; - ma_uint32 right2 = pInputSamples1U32[i*4+2] << shift1; - ma_uint32 right3 = pInputSamples1U32[i*4+3] << shift1; - ma_uint32 left0 = right0 + side0; - ma_uint32 left1 = right1 + side1; - ma_uint32 left2 = right2 + side2; - ma_uint32 left3 = right3 + side3; - left0 >>= 16; - left1 >>= 16; - left2 >>= 16; - left3 >>= 16; - right0 >>= 16; - right1 >>= 16; - right2 >>= 16; - right3 >>= 16; - pOutputSamples[i*8+0] = (ma_int16)left0; - pOutputSamples[i*8+1] = (ma_int16)right0; - pOutputSamples[i*8+2] = (ma_int16)left1; - pOutputSamples[i*8+3] = (ma_int16)right1; - pOutputSamples[i*8+4] = (ma_int16)left2; - pOutputSamples[i*8+5] = (ma_int16)right2; - pOutputSamples[i*8+6] = (ma_int16)left3; - pOutputSamples[i*8+7] = (ma_int16)right3; - } - for (i = (frameCount4 << 2); i < frameCount; ++i) { - ma_uint32 side = pInputSamples0U32[i] << shift0; - ma_uint32 right = pInputSamples1U32[i] << shift1; - ma_uint32 left = right + side; - left >>= 16; - right >>= 16; - pOutputSamples[i*2+0] = (ma_int16)left; - pOutputSamples[i*2+1] = (ma_int16)right; - } -} -#if defined(MA_DR_FLAC_SUPPORT_SSE2) -static MA_INLINE void ma_dr_flac_read_pcm_frames_s16__decode_right_side__sse2(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, ma_int16* pOutputSamples) -{ - ma_uint64 i; - ma_uint64 frameCount4 = frameCount >> 2; - const ma_uint32* pInputSamples0U32 = (const ma_uint32*)pInputSamples0; - const ma_uint32* pInputSamples1U32 = (const ma_uint32*)pInputSamples1; - ma_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; - ma_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; - MA_DR_FLAC_ASSERT(pFlac->bitsPerSample <= 24); - for (i = 0; i < frameCount4; ++i) { - __m128i side = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples0 + i), shift0); - __m128i right = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples1 + i), shift1); - __m128i left = _mm_add_epi32(right, side); - left = _mm_srai_epi32(left, 16); - right = _mm_srai_epi32(right, 16); - _mm_storeu_si128((__m128i*)(pOutputSamples + i*8), ma_dr_flac__mm_packs_interleaved_epi32(left, right)); - } - for (i = (frameCount4 << 2); i < frameCount; ++i) { - ma_uint32 side = pInputSamples0U32[i] << shift0; - ma_uint32 right = pInputSamples1U32[i] << shift1; - ma_uint32 left = right + side; - left >>= 16; - right >>= 16; - pOutputSamples[i*2+0] = (ma_int16)left; - pOutputSamples[i*2+1] = (ma_int16)right; - } -} -#endif -#if defined(MA_DR_FLAC_SUPPORT_NEON) -static MA_INLINE void ma_dr_flac_read_pcm_frames_s16__decode_right_side__neon(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, ma_int16* pOutputSamples) -{ - ma_uint64 i; - ma_uint64 frameCount4 = frameCount >> 2; - const ma_uint32* pInputSamples0U32 = (const ma_uint32*)pInputSamples0; - const ma_uint32* pInputSamples1U32 = (const ma_uint32*)pInputSamples1; - ma_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; - ma_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; - int32x4_t shift0_4; - int32x4_t shift1_4; - MA_DR_FLAC_ASSERT(pFlac->bitsPerSample <= 24); - shift0_4 = vdupq_n_s32(shift0); - shift1_4 = vdupq_n_s32(shift1); - for (i = 0; i < frameCount4; ++i) { - uint32x4_t side; - uint32x4_t right; - uint32x4_t left; - side = vshlq_u32(vld1q_u32(pInputSamples0U32 + i*4), shift0_4); - right = vshlq_u32(vld1q_u32(pInputSamples1U32 + i*4), shift1_4); - left = vaddq_u32(right, side); - left = vshrq_n_u32(left, 16); - right = vshrq_n_u32(right, 16); - ma_dr_flac__vst2q_u16((ma_uint16*)pOutputSamples + i*8, vzip_u16(vmovn_u32(left), vmovn_u32(right))); - } - for (i = (frameCount4 << 2); i < frameCount; ++i) { - ma_uint32 side = pInputSamples0U32[i] << shift0; - ma_uint32 right = pInputSamples1U32[i] << shift1; - ma_uint32 left = right + side; - left >>= 16; - right >>= 16; - pOutputSamples[i*2+0] = (ma_int16)left; - pOutputSamples[i*2+1] = (ma_int16)right; - } -} -#endif -static MA_INLINE void ma_dr_flac_read_pcm_frames_s16__decode_right_side(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, ma_int16* pOutputSamples) -{ -#if defined(MA_DR_FLAC_SUPPORT_SSE2) - if (ma_dr_flac__gIsSSE2Supported && pFlac->bitsPerSample <= 24) { - ma_dr_flac_read_pcm_frames_s16__decode_right_side__sse2(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); - } else -#elif defined(MA_DR_FLAC_SUPPORT_NEON) - if (ma_dr_flac__gIsNEONSupported && pFlac->bitsPerSample <= 24) { - ma_dr_flac_read_pcm_frames_s16__decode_right_side__neon(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); - } else -#endif - { -#if 0 - ma_dr_flac_read_pcm_frames_s16__decode_right_side__reference(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); -#else - ma_dr_flac_read_pcm_frames_s16__decode_right_side__scalar(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); -#endif - } -} -#if 0 -static MA_INLINE void ma_dr_flac_read_pcm_frames_s16__decode_mid_side__reference(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, ma_int16* pOutputSamples) -{ - for (ma_uint64 i = 0; i < frameCount; ++i) { - ma_uint32 mid = (ma_uint32)pInputSamples0[i] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; - ma_uint32 side = (ma_uint32)pInputSamples1[i] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; - mid = (mid << 1) | (side & 0x01); - pOutputSamples[i*2+0] = (ma_int16)(((ma_uint32)((ma_int32)(mid + side) >> 1) << unusedBitsPerSample) >> 16); - pOutputSamples[i*2+1] = (ma_int16)(((ma_uint32)((ma_int32)(mid - side) >> 1) << unusedBitsPerSample) >> 16); - } -} -#endif -static MA_INLINE void ma_dr_flac_read_pcm_frames_s16__decode_mid_side__scalar(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, ma_int16* pOutputSamples) -{ - ma_uint64 i; - ma_uint64 frameCount4 = frameCount >> 2; - const ma_uint32* pInputSamples0U32 = (const ma_uint32*)pInputSamples0; - const ma_uint32* pInputSamples1U32 = (const ma_uint32*)pInputSamples1; - ma_uint32 shift = unusedBitsPerSample; - if (shift > 0) { - shift -= 1; - for (i = 0; i < frameCount4; ++i) { - ma_uint32 temp0L; - ma_uint32 temp1L; - ma_uint32 temp2L; - ma_uint32 temp3L; - ma_uint32 temp0R; - ma_uint32 temp1R; - ma_uint32 temp2R; - ma_uint32 temp3R; - ma_uint32 mid0 = pInputSamples0U32[i*4+0] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; - ma_uint32 mid1 = pInputSamples0U32[i*4+1] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; - ma_uint32 mid2 = pInputSamples0U32[i*4+2] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; - ma_uint32 mid3 = pInputSamples0U32[i*4+3] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; - ma_uint32 side0 = pInputSamples1U32[i*4+0] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; - ma_uint32 side1 = pInputSamples1U32[i*4+1] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; - ma_uint32 side2 = pInputSamples1U32[i*4+2] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; - ma_uint32 side3 = pInputSamples1U32[i*4+3] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; - mid0 = (mid0 << 1) | (side0 & 0x01); - mid1 = (mid1 << 1) | (side1 & 0x01); - mid2 = (mid2 << 1) | (side2 & 0x01); - mid3 = (mid3 << 1) | (side3 & 0x01); - temp0L = (mid0 + side0) << shift; - temp1L = (mid1 + side1) << shift; - temp2L = (mid2 + side2) << shift; - temp3L = (mid3 + side3) << shift; - temp0R = (mid0 - side0) << shift; - temp1R = (mid1 - side1) << shift; - temp2R = (mid2 - side2) << shift; - temp3R = (mid3 - side3) << shift; - temp0L >>= 16; - temp1L >>= 16; - temp2L >>= 16; - temp3L >>= 16; - temp0R >>= 16; - temp1R >>= 16; - temp2R >>= 16; - temp3R >>= 16; - pOutputSamples[i*8+0] = (ma_int16)temp0L; - pOutputSamples[i*8+1] = (ma_int16)temp0R; - pOutputSamples[i*8+2] = (ma_int16)temp1L; - pOutputSamples[i*8+3] = (ma_int16)temp1R; - pOutputSamples[i*8+4] = (ma_int16)temp2L; - pOutputSamples[i*8+5] = (ma_int16)temp2R; - pOutputSamples[i*8+6] = (ma_int16)temp3L; - pOutputSamples[i*8+7] = (ma_int16)temp3R; - } - } else { - for (i = 0; i < frameCount4; ++i) { - ma_uint32 temp0L; - ma_uint32 temp1L; - ma_uint32 temp2L; - ma_uint32 temp3L; - ma_uint32 temp0R; - ma_uint32 temp1R; - ma_uint32 temp2R; - ma_uint32 temp3R; - ma_uint32 mid0 = pInputSamples0U32[i*4+0] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; - ma_uint32 mid1 = pInputSamples0U32[i*4+1] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; - ma_uint32 mid2 = pInputSamples0U32[i*4+2] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; - ma_uint32 mid3 = pInputSamples0U32[i*4+3] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; - ma_uint32 side0 = pInputSamples1U32[i*4+0] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; - ma_uint32 side1 = pInputSamples1U32[i*4+1] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; - ma_uint32 side2 = pInputSamples1U32[i*4+2] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; - ma_uint32 side3 = pInputSamples1U32[i*4+3] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; - mid0 = (mid0 << 1) | (side0 & 0x01); - mid1 = (mid1 << 1) | (side1 & 0x01); - mid2 = (mid2 << 1) | (side2 & 0x01); - mid3 = (mid3 << 1) | (side3 & 0x01); - temp0L = ((ma_int32)(mid0 + side0) >> 1); - temp1L = ((ma_int32)(mid1 + side1) >> 1); - temp2L = ((ma_int32)(mid2 + side2) >> 1); - temp3L = ((ma_int32)(mid3 + side3) >> 1); - temp0R = ((ma_int32)(mid0 - side0) >> 1); - temp1R = ((ma_int32)(mid1 - side1) >> 1); - temp2R = ((ma_int32)(mid2 - side2) >> 1); - temp3R = ((ma_int32)(mid3 - side3) >> 1); - temp0L >>= 16; - temp1L >>= 16; - temp2L >>= 16; - temp3L >>= 16; - temp0R >>= 16; - temp1R >>= 16; - temp2R >>= 16; - temp3R >>= 16; - pOutputSamples[i*8+0] = (ma_int16)temp0L; - pOutputSamples[i*8+1] = (ma_int16)temp0R; - pOutputSamples[i*8+2] = (ma_int16)temp1L; - pOutputSamples[i*8+3] = (ma_int16)temp1R; - pOutputSamples[i*8+4] = (ma_int16)temp2L; - pOutputSamples[i*8+5] = (ma_int16)temp2R; - pOutputSamples[i*8+6] = (ma_int16)temp3L; - pOutputSamples[i*8+7] = (ma_int16)temp3R; - } - } - for (i = (frameCount4 << 2); i < frameCount; ++i) { - ma_uint32 mid = pInputSamples0U32[i] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; - ma_uint32 side = pInputSamples1U32[i] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; - mid = (mid << 1) | (side & 0x01); - pOutputSamples[i*2+0] = (ma_int16)(((ma_uint32)((ma_int32)(mid + side) >> 1) << unusedBitsPerSample) >> 16); - pOutputSamples[i*2+1] = (ma_int16)(((ma_uint32)((ma_int32)(mid - side) >> 1) << unusedBitsPerSample) >> 16); - } -} -#if defined(MA_DR_FLAC_SUPPORT_SSE2) -static MA_INLINE void ma_dr_flac_read_pcm_frames_s16__decode_mid_side__sse2(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, ma_int16* pOutputSamples) -{ - ma_uint64 i; - ma_uint64 frameCount4 = frameCount >> 2; - const ma_uint32* pInputSamples0U32 = (const ma_uint32*)pInputSamples0; - const ma_uint32* pInputSamples1U32 = (const ma_uint32*)pInputSamples1; - ma_uint32 shift = unusedBitsPerSample; - MA_DR_FLAC_ASSERT(pFlac->bitsPerSample <= 24); - if (shift == 0) { - for (i = 0; i < frameCount4; ++i) { - __m128i mid; - __m128i side; - __m128i left; - __m128i right; - mid = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples0 + i), pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample); - side = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples1 + i), pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample); - mid = _mm_or_si128(_mm_slli_epi32(mid, 1), _mm_and_si128(side, _mm_set1_epi32(0x01))); - left = _mm_srai_epi32(_mm_add_epi32(mid, side), 1); - right = _mm_srai_epi32(_mm_sub_epi32(mid, side), 1); - left = _mm_srai_epi32(left, 16); - right = _mm_srai_epi32(right, 16); - _mm_storeu_si128((__m128i*)(pOutputSamples + i*8), ma_dr_flac__mm_packs_interleaved_epi32(left, right)); - } - for (i = (frameCount4 << 2); i < frameCount; ++i) { - ma_uint32 mid = pInputSamples0U32[i] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; - ma_uint32 side = pInputSamples1U32[i] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; - mid = (mid << 1) | (side & 0x01); - pOutputSamples[i*2+0] = (ma_int16)(((ma_int32)(mid + side) >> 1) >> 16); - pOutputSamples[i*2+1] = (ma_int16)(((ma_int32)(mid - side) >> 1) >> 16); - } - } else { - shift -= 1; - for (i = 0; i < frameCount4; ++i) { - __m128i mid; - __m128i side; - __m128i left; - __m128i right; - mid = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples0 + i), pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample); - side = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples1 + i), pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample); - mid = _mm_or_si128(_mm_slli_epi32(mid, 1), _mm_and_si128(side, _mm_set1_epi32(0x01))); - left = _mm_slli_epi32(_mm_add_epi32(mid, side), shift); - right = _mm_slli_epi32(_mm_sub_epi32(mid, side), shift); - left = _mm_srai_epi32(left, 16); - right = _mm_srai_epi32(right, 16); - _mm_storeu_si128((__m128i*)(pOutputSamples + i*8), ma_dr_flac__mm_packs_interleaved_epi32(left, right)); - } - for (i = (frameCount4 << 2); i < frameCount; ++i) { - ma_uint32 mid = pInputSamples0U32[i] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; - ma_uint32 side = pInputSamples1U32[i] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; - mid = (mid << 1) | (side & 0x01); - pOutputSamples[i*2+0] = (ma_int16)(((mid + side) << shift) >> 16); - pOutputSamples[i*2+1] = (ma_int16)(((mid - side) << shift) >> 16); - } - } -} -#endif -#if defined(MA_DR_FLAC_SUPPORT_NEON) -static MA_INLINE void ma_dr_flac_read_pcm_frames_s16__decode_mid_side__neon(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, ma_int16* pOutputSamples) -{ - ma_uint64 i; - ma_uint64 frameCount4 = frameCount >> 2; - const ma_uint32* pInputSamples0U32 = (const ma_uint32*)pInputSamples0; - const ma_uint32* pInputSamples1U32 = (const ma_uint32*)pInputSamples1; - ma_uint32 shift = unusedBitsPerSample; - int32x4_t wbpsShift0_4; - int32x4_t wbpsShift1_4; - MA_DR_FLAC_ASSERT(pFlac->bitsPerSample <= 24); - wbpsShift0_4 = vdupq_n_s32(pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample); - wbpsShift1_4 = vdupq_n_s32(pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample); - if (shift == 0) { - for (i = 0; i < frameCount4; ++i) { - uint32x4_t mid; - uint32x4_t side; - int32x4_t left; - int32x4_t right; - mid = vshlq_u32(vld1q_u32(pInputSamples0U32 + i*4), wbpsShift0_4); - side = vshlq_u32(vld1q_u32(pInputSamples1U32 + i*4), wbpsShift1_4); - mid = vorrq_u32(vshlq_n_u32(mid, 1), vandq_u32(side, vdupq_n_u32(1))); - left = vshrq_n_s32(vreinterpretq_s32_u32(vaddq_u32(mid, side)), 1); - right = vshrq_n_s32(vreinterpretq_s32_u32(vsubq_u32(mid, side)), 1); - left = vshrq_n_s32(left, 16); - right = vshrq_n_s32(right, 16); - ma_dr_flac__vst2q_s16(pOutputSamples + i*8, vzip_s16(vmovn_s32(left), vmovn_s32(right))); - } - for (i = (frameCount4 << 2); i < frameCount; ++i) { - ma_uint32 mid = pInputSamples0U32[i] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; - ma_uint32 side = pInputSamples1U32[i] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; - mid = (mid << 1) | (side & 0x01); - pOutputSamples[i*2+0] = (ma_int16)(((ma_int32)(mid + side) >> 1) >> 16); - pOutputSamples[i*2+1] = (ma_int16)(((ma_int32)(mid - side) >> 1) >> 16); - } - } else { - int32x4_t shift4; - shift -= 1; - shift4 = vdupq_n_s32(shift); - for (i = 0; i < frameCount4; ++i) { - uint32x4_t mid; - uint32x4_t side; - int32x4_t left; - int32x4_t right; - mid = vshlq_u32(vld1q_u32(pInputSamples0U32 + i*4), wbpsShift0_4); - side = vshlq_u32(vld1q_u32(pInputSamples1U32 + i*4), wbpsShift1_4); - mid = vorrq_u32(vshlq_n_u32(mid, 1), vandq_u32(side, vdupq_n_u32(1))); - left = vreinterpretq_s32_u32(vshlq_u32(vaddq_u32(mid, side), shift4)); - right = vreinterpretq_s32_u32(vshlq_u32(vsubq_u32(mid, side), shift4)); - left = vshrq_n_s32(left, 16); - right = vshrq_n_s32(right, 16); - ma_dr_flac__vst2q_s16(pOutputSamples + i*8, vzip_s16(vmovn_s32(left), vmovn_s32(right))); - } - for (i = (frameCount4 << 2); i < frameCount; ++i) { - ma_uint32 mid = pInputSamples0U32[i] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; - ma_uint32 side = pInputSamples1U32[i] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; - mid = (mid << 1) | (side & 0x01); - pOutputSamples[i*2+0] = (ma_int16)(((mid + side) << shift) >> 16); - pOutputSamples[i*2+1] = (ma_int16)(((mid - side) << shift) >> 16); - } - } -} -#endif -static MA_INLINE void ma_dr_flac_read_pcm_frames_s16__decode_mid_side(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, ma_int16* pOutputSamples) -{ -#if defined(MA_DR_FLAC_SUPPORT_SSE2) - if (ma_dr_flac__gIsSSE2Supported && pFlac->bitsPerSample <= 24) { - ma_dr_flac_read_pcm_frames_s16__decode_mid_side__sse2(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); - } else -#elif defined(MA_DR_FLAC_SUPPORT_NEON) - if (ma_dr_flac__gIsNEONSupported && pFlac->bitsPerSample <= 24) { - ma_dr_flac_read_pcm_frames_s16__decode_mid_side__neon(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); - } else -#endif - { -#if 0 - ma_dr_flac_read_pcm_frames_s16__decode_mid_side__reference(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); -#else - ma_dr_flac_read_pcm_frames_s16__decode_mid_side__scalar(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); -#endif - } -} -#if 0 -static MA_INLINE void ma_dr_flac_read_pcm_frames_s16__decode_independent_stereo__reference(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, ma_int16* pOutputSamples) -{ - for (ma_uint64 i = 0; i < frameCount; ++i) { - pOutputSamples[i*2+0] = (ma_int16)((ma_int32)((ma_uint32)pInputSamples0[i] << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample)) >> 16); - pOutputSamples[i*2+1] = (ma_int16)((ma_int32)((ma_uint32)pInputSamples1[i] << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample)) >> 16); - } -} -#endif -static MA_INLINE void ma_dr_flac_read_pcm_frames_s16__decode_independent_stereo__scalar(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, ma_int16* pOutputSamples) -{ - ma_uint64 i; - ma_uint64 frameCount4 = frameCount >> 2; - const ma_uint32* pInputSamples0U32 = (const ma_uint32*)pInputSamples0; - const ma_uint32* pInputSamples1U32 = (const ma_uint32*)pInputSamples1; - ma_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; - ma_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; - for (i = 0; i < frameCount4; ++i) { - ma_uint32 tempL0 = pInputSamples0U32[i*4+0] << shift0; - ma_uint32 tempL1 = pInputSamples0U32[i*4+1] << shift0; - ma_uint32 tempL2 = pInputSamples0U32[i*4+2] << shift0; - ma_uint32 tempL3 = pInputSamples0U32[i*4+3] << shift0; - ma_uint32 tempR0 = pInputSamples1U32[i*4+0] << shift1; - ma_uint32 tempR1 = pInputSamples1U32[i*4+1] << shift1; - ma_uint32 tempR2 = pInputSamples1U32[i*4+2] << shift1; - ma_uint32 tempR3 = pInputSamples1U32[i*4+3] << shift1; - tempL0 >>= 16; - tempL1 >>= 16; - tempL2 >>= 16; - tempL3 >>= 16; - tempR0 >>= 16; - tempR1 >>= 16; - tempR2 >>= 16; - tempR3 >>= 16; - pOutputSamples[i*8+0] = (ma_int16)tempL0; - pOutputSamples[i*8+1] = (ma_int16)tempR0; - pOutputSamples[i*8+2] = (ma_int16)tempL1; - pOutputSamples[i*8+3] = (ma_int16)tempR1; - pOutputSamples[i*8+4] = (ma_int16)tempL2; - pOutputSamples[i*8+5] = (ma_int16)tempR2; - pOutputSamples[i*8+6] = (ma_int16)tempL3; - pOutputSamples[i*8+7] = (ma_int16)tempR3; - } - for (i = (frameCount4 << 2); i < frameCount; ++i) { - pOutputSamples[i*2+0] = (ma_int16)((pInputSamples0U32[i] << shift0) >> 16); - pOutputSamples[i*2+1] = (ma_int16)((pInputSamples1U32[i] << shift1) >> 16); - } -} -#if defined(MA_DR_FLAC_SUPPORT_SSE2) -static MA_INLINE void ma_dr_flac_read_pcm_frames_s16__decode_independent_stereo__sse2(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, ma_int16* pOutputSamples) -{ - ma_uint64 i; - ma_uint64 frameCount4 = frameCount >> 2; - const ma_uint32* pInputSamples0U32 = (const ma_uint32*)pInputSamples0; - const ma_uint32* pInputSamples1U32 = (const ma_uint32*)pInputSamples1; - ma_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; - ma_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; - for (i = 0; i < frameCount4; ++i) { - __m128i left = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples0 + i), shift0); - __m128i right = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples1 + i), shift1); - left = _mm_srai_epi32(left, 16); - right = _mm_srai_epi32(right, 16); - _mm_storeu_si128((__m128i*)(pOutputSamples + i*8), ma_dr_flac__mm_packs_interleaved_epi32(left, right)); - } - for (i = (frameCount4 << 2); i < frameCount; ++i) { - pOutputSamples[i*2+0] = (ma_int16)((pInputSamples0U32[i] << shift0) >> 16); - pOutputSamples[i*2+1] = (ma_int16)((pInputSamples1U32[i] << shift1) >> 16); - } -} -#endif -#if defined(MA_DR_FLAC_SUPPORT_NEON) -static MA_INLINE void ma_dr_flac_read_pcm_frames_s16__decode_independent_stereo__neon(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, ma_int16* pOutputSamples) -{ - ma_uint64 i; - ma_uint64 frameCount4 = frameCount >> 2; - const ma_uint32* pInputSamples0U32 = (const ma_uint32*)pInputSamples0; - const ma_uint32* pInputSamples1U32 = (const ma_uint32*)pInputSamples1; - ma_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; - ma_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; - int32x4_t shift0_4 = vdupq_n_s32(shift0); - int32x4_t shift1_4 = vdupq_n_s32(shift1); - for (i = 0; i < frameCount4; ++i) { - int32x4_t left; - int32x4_t right; - left = vreinterpretq_s32_u32(vshlq_u32(vld1q_u32(pInputSamples0U32 + i*4), shift0_4)); - right = vreinterpretq_s32_u32(vshlq_u32(vld1q_u32(pInputSamples1U32 + i*4), shift1_4)); - left = vshrq_n_s32(left, 16); - right = vshrq_n_s32(right, 16); - ma_dr_flac__vst2q_s16(pOutputSamples + i*8, vzip_s16(vmovn_s32(left), vmovn_s32(right))); - } - for (i = (frameCount4 << 2); i < frameCount; ++i) { - pOutputSamples[i*2+0] = (ma_int16)((pInputSamples0U32[i] << shift0) >> 16); - pOutputSamples[i*2+1] = (ma_int16)((pInputSamples1U32[i] << shift1) >> 16); - } -} -#endif -static MA_INLINE void ma_dr_flac_read_pcm_frames_s16__decode_independent_stereo(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, ma_int16* pOutputSamples) -{ -#if defined(MA_DR_FLAC_SUPPORT_SSE2) - if (ma_dr_flac__gIsSSE2Supported && pFlac->bitsPerSample <= 24) { - ma_dr_flac_read_pcm_frames_s16__decode_independent_stereo__sse2(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); - } else -#elif defined(MA_DR_FLAC_SUPPORT_NEON) - if (ma_dr_flac__gIsNEONSupported && pFlac->bitsPerSample <= 24) { - ma_dr_flac_read_pcm_frames_s16__decode_independent_stereo__neon(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); - } else -#endif - { -#if 0 - ma_dr_flac_read_pcm_frames_s16__decode_independent_stereo__reference(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); -#else - ma_dr_flac_read_pcm_frames_s16__decode_independent_stereo__scalar(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); -#endif - } -} -MA_API ma_uint64 ma_dr_flac_read_pcm_frames_s16(ma_dr_flac* pFlac, ma_uint64 framesToRead, ma_int16* pBufferOut) -{ - ma_uint64 framesRead; - ma_uint32 unusedBitsPerSample; - if (pFlac == NULL || framesToRead == 0) { - return 0; - } - if (pBufferOut == NULL) { - return ma_dr_flac__seek_forward_by_pcm_frames(pFlac, framesToRead); - } - MA_DR_FLAC_ASSERT(pFlac->bitsPerSample <= 32); - unusedBitsPerSample = 32 - pFlac->bitsPerSample; - framesRead = 0; - while (framesToRead > 0) { - if (pFlac->currentFLACFrame.pcmFramesRemaining == 0) { - if (!ma_dr_flac__read_and_decode_next_flac_frame(pFlac)) { - break; - } - } else { - unsigned int channelCount = ma_dr_flac__get_channel_count_from_channel_assignment(pFlac->currentFLACFrame.header.channelAssignment); - ma_uint64 iFirstPCMFrame = pFlac->currentFLACFrame.header.blockSizeInPCMFrames - pFlac->currentFLACFrame.pcmFramesRemaining; - ma_uint64 frameCountThisIteration = framesToRead; - if (frameCountThisIteration > pFlac->currentFLACFrame.pcmFramesRemaining) { - frameCountThisIteration = pFlac->currentFLACFrame.pcmFramesRemaining; - } - if (channelCount == 2) { - const ma_int32* pDecodedSamples0 = pFlac->currentFLACFrame.subframes[0].pSamplesS32 + iFirstPCMFrame; - const ma_int32* pDecodedSamples1 = pFlac->currentFLACFrame.subframes[1].pSamplesS32 + iFirstPCMFrame; - switch (pFlac->currentFLACFrame.header.channelAssignment) - { - case MA_DR_FLAC_CHANNEL_ASSIGNMENT_LEFT_SIDE: - { - ma_dr_flac_read_pcm_frames_s16__decode_left_side(pFlac, frameCountThisIteration, unusedBitsPerSample, pDecodedSamples0, pDecodedSamples1, pBufferOut); - } break; - case MA_DR_FLAC_CHANNEL_ASSIGNMENT_RIGHT_SIDE: - { - ma_dr_flac_read_pcm_frames_s16__decode_right_side(pFlac, frameCountThisIteration, unusedBitsPerSample, pDecodedSamples0, pDecodedSamples1, pBufferOut); - } break; - case MA_DR_FLAC_CHANNEL_ASSIGNMENT_MID_SIDE: - { - ma_dr_flac_read_pcm_frames_s16__decode_mid_side(pFlac, frameCountThisIteration, unusedBitsPerSample, pDecodedSamples0, pDecodedSamples1, pBufferOut); - } break; - case MA_DR_FLAC_CHANNEL_ASSIGNMENT_INDEPENDENT: - default: - { - ma_dr_flac_read_pcm_frames_s16__decode_independent_stereo(pFlac, frameCountThisIteration, unusedBitsPerSample, pDecodedSamples0, pDecodedSamples1, pBufferOut); - } break; - } - } else { - ma_uint64 i; - for (i = 0; i < frameCountThisIteration; ++i) { - unsigned int j; - for (j = 0; j < channelCount; ++j) { - ma_int32 sampleS32 = (ma_int32)((ma_uint32)(pFlac->currentFLACFrame.subframes[j].pSamplesS32[iFirstPCMFrame + i]) << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[j].wastedBitsPerSample)); - pBufferOut[(i*channelCount)+j] = (ma_int16)(sampleS32 >> 16); - } - } - } - framesRead += frameCountThisIteration; - pBufferOut += frameCountThisIteration * channelCount; - framesToRead -= frameCountThisIteration; - pFlac->currentPCMFrame += frameCountThisIteration; - pFlac->currentFLACFrame.pcmFramesRemaining -= (ma_uint32)frameCountThisIteration; - } - } - return framesRead; -} -#if 0 -static MA_INLINE void ma_dr_flac_read_pcm_frames_f32__decode_left_side__reference(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, float* pOutputSamples) -{ - ma_uint64 i; - for (i = 0; i < frameCount; ++i) { - ma_uint32 left = (ma_uint32)pInputSamples0[i] << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample); - ma_uint32 side = (ma_uint32)pInputSamples1[i] << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample); - ma_uint32 right = left - side; - pOutputSamples[i*2+0] = (float)((ma_int32)left / 2147483648.0); - pOutputSamples[i*2+1] = (float)((ma_int32)right / 2147483648.0); - } -} -#endif -static MA_INLINE void ma_dr_flac_read_pcm_frames_f32__decode_left_side__scalar(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, float* pOutputSamples) -{ - ma_uint64 i; - ma_uint64 frameCount4 = frameCount >> 2; - const ma_uint32* pInputSamples0U32 = (const ma_uint32*)pInputSamples0; - const ma_uint32* pInputSamples1U32 = (const ma_uint32*)pInputSamples1; - ma_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; - ma_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; - float factor = 1 / 2147483648.0; - for (i = 0; i < frameCount4; ++i) { - ma_uint32 left0 = pInputSamples0U32[i*4+0] << shift0; - ma_uint32 left1 = pInputSamples0U32[i*4+1] << shift0; - ma_uint32 left2 = pInputSamples0U32[i*4+2] << shift0; - ma_uint32 left3 = pInputSamples0U32[i*4+3] << shift0; - ma_uint32 side0 = pInputSamples1U32[i*4+0] << shift1; - ma_uint32 side1 = pInputSamples1U32[i*4+1] << shift1; - ma_uint32 side2 = pInputSamples1U32[i*4+2] << shift1; - ma_uint32 side3 = pInputSamples1U32[i*4+3] << shift1; - ma_uint32 right0 = left0 - side0; - ma_uint32 right1 = left1 - side1; - ma_uint32 right2 = left2 - side2; - ma_uint32 right3 = left3 - side3; - pOutputSamples[i*8+0] = (ma_int32)left0 * factor; - pOutputSamples[i*8+1] = (ma_int32)right0 * factor; - pOutputSamples[i*8+2] = (ma_int32)left1 * factor; - pOutputSamples[i*8+3] = (ma_int32)right1 * factor; - pOutputSamples[i*8+4] = (ma_int32)left2 * factor; - pOutputSamples[i*8+5] = (ma_int32)right2 * factor; - pOutputSamples[i*8+6] = (ma_int32)left3 * factor; - pOutputSamples[i*8+7] = (ma_int32)right3 * factor; - } - for (i = (frameCount4 << 2); i < frameCount; ++i) { - ma_uint32 left = pInputSamples0U32[i] << shift0; - ma_uint32 side = pInputSamples1U32[i] << shift1; - ma_uint32 right = left - side; - pOutputSamples[i*2+0] = (ma_int32)left * factor; - pOutputSamples[i*2+1] = (ma_int32)right * factor; - } -} -#if defined(MA_DR_FLAC_SUPPORT_SSE2) -static MA_INLINE void ma_dr_flac_read_pcm_frames_f32__decode_left_side__sse2(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, float* pOutputSamples) -{ - ma_uint64 i; - ma_uint64 frameCount4 = frameCount >> 2; - const ma_uint32* pInputSamples0U32 = (const ma_uint32*)pInputSamples0; - const ma_uint32* pInputSamples1U32 = (const ma_uint32*)pInputSamples1; - ma_uint32 shift0 = (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample) - 8; - ma_uint32 shift1 = (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample) - 8; - __m128 factor; - MA_DR_FLAC_ASSERT(pFlac->bitsPerSample <= 24); - factor = _mm_set1_ps(1.0f / 8388608.0f); - for (i = 0; i < frameCount4; ++i) { - __m128i left = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples0 + i), shift0); - __m128i side = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples1 + i), shift1); - __m128i right = _mm_sub_epi32(left, side); - __m128 leftf = _mm_mul_ps(_mm_cvtepi32_ps(left), factor); - __m128 rightf = _mm_mul_ps(_mm_cvtepi32_ps(right), factor); - _mm_storeu_ps(pOutputSamples + i*8 + 0, _mm_unpacklo_ps(leftf, rightf)); - _mm_storeu_ps(pOutputSamples + i*8 + 4, _mm_unpackhi_ps(leftf, rightf)); - } - for (i = (frameCount4 << 2); i < frameCount; ++i) { - ma_uint32 left = pInputSamples0U32[i] << shift0; - ma_uint32 side = pInputSamples1U32[i] << shift1; - ma_uint32 right = left - side; - pOutputSamples[i*2+0] = (ma_int32)left / 8388608.0f; - pOutputSamples[i*2+1] = (ma_int32)right / 8388608.0f; - } -} -#endif -#if defined(MA_DR_FLAC_SUPPORT_NEON) -static MA_INLINE void ma_dr_flac_read_pcm_frames_f32__decode_left_side__neon(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, float* pOutputSamples) -{ - ma_uint64 i; - ma_uint64 frameCount4 = frameCount >> 2; - const ma_uint32* pInputSamples0U32 = (const ma_uint32*)pInputSamples0; - const ma_uint32* pInputSamples1U32 = (const ma_uint32*)pInputSamples1; - ma_uint32 shift0 = (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample) - 8; - ma_uint32 shift1 = (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample) - 8; - float32x4_t factor4; - int32x4_t shift0_4; - int32x4_t shift1_4; - MA_DR_FLAC_ASSERT(pFlac->bitsPerSample <= 24); - factor4 = vdupq_n_f32(1.0f / 8388608.0f); - shift0_4 = vdupq_n_s32(shift0); - shift1_4 = vdupq_n_s32(shift1); - for (i = 0; i < frameCount4; ++i) { - uint32x4_t left; - uint32x4_t side; - uint32x4_t right; - float32x4_t leftf; - float32x4_t rightf; - left = vshlq_u32(vld1q_u32(pInputSamples0U32 + i*4), shift0_4); - side = vshlq_u32(vld1q_u32(pInputSamples1U32 + i*4), shift1_4); - right = vsubq_u32(left, side); - leftf = vmulq_f32(vcvtq_f32_s32(vreinterpretq_s32_u32(left)), factor4); - rightf = vmulq_f32(vcvtq_f32_s32(vreinterpretq_s32_u32(right)), factor4); - ma_dr_flac__vst2q_f32(pOutputSamples + i*8, vzipq_f32(leftf, rightf)); - } - for (i = (frameCount4 << 2); i < frameCount; ++i) { - ma_uint32 left = pInputSamples0U32[i] << shift0; - ma_uint32 side = pInputSamples1U32[i] << shift1; - ma_uint32 right = left - side; - pOutputSamples[i*2+0] = (ma_int32)left / 8388608.0f; - pOutputSamples[i*2+1] = (ma_int32)right / 8388608.0f; - } -} -#endif -static MA_INLINE void ma_dr_flac_read_pcm_frames_f32__decode_left_side(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, float* pOutputSamples) -{ -#if defined(MA_DR_FLAC_SUPPORT_SSE2) - if (ma_dr_flac__gIsSSE2Supported && pFlac->bitsPerSample <= 24) { - ma_dr_flac_read_pcm_frames_f32__decode_left_side__sse2(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); - } else -#elif defined(MA_DR_FLAC_SUPPORT_NEON) - if (ma_dr_flac__gIsNEONSupported && pFlac->bitsPerSample <= 24) { - ma_dr_flac_read_pcm_frames_f32__decode_left_side__neon(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); - } else -#endif - { -#if 0 - ma_dr_flac_read_pcm_frames_f32__decode_left_side__reference(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); -#else - ma_dr_flac_read_pcm_frames_f32__decode_left_side__scalar(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); -#endif - } -} -#if 0 -static MA_INLINE void ma_dr_flac_read_pcm_frames_f32__decode_right_side__reference(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, float* pOutputSamples) -{ - ma_uint64 i; - for (i = 0; i < frameCount; ++i) { - ma_uint32 side = (ma_uint32)pInputSamples0[i] << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample); - ma_uint32 right = (ma_uint32)pInputSamples1[i] << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample); - ma_uint32 left = right + side; - pOutputSamples[i*2+0] = (float)((ma_int32)left / 2147483648.0); - pOutputSamples[i*2+1] = (float)((ma_int32)right / 2147483648.0); - } -} -#endif -static MA_INLINE void ma_dr_flac_read_pcm_frames_f32__decode_right_side__scalar(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, float* pOutputSamples) -{ - ma_uint64 i; - ma_uint64 frameCount4 = frameCount >> 2; - const ma_uint32* pInputSamples0U32 = (const ma_uint32*)pInputSamples0; - const ma_uint32* pInputSamples1U32 = (const ma_uint32*)pInputSamples1; - ma_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; - ma_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; - float factor = 1 / 2147483648.0; - for (i = 0; i < frameCount4; ++i) { - ma_uint32 side0 = pInputSamples0U32[i*4+0] << shift0; - ma_uint32 side1 = pInputSamples0U32[i*4+1] << shift0; - ma_uint32 side2 = pInputSamples0U32[i*4+2] << shift0; - ma_uint32 side3 = pInputSamples0U32[i*4+3] << shift0; - ma_uint32 right0 = pInputSamples1U32[i*4+0] << shift1; - ma_uint32 right1 = pInputSamples1U32[i*4+1] << shift1; - ma_uint32 right2 = pInputSamples1U32[i*4+2] << shift1; - ma_uint32 right3 = pInputSamples1U32[i*4+3] << shift1; - ma_uint32 left0 = right0 + side0; - ma_uint32 left1 = right1 + side1; - ma_uint32 left2 = right2 + side2; - ma_uint32 left3 = right3 + side3; - pOutputSamples[i*8+0] = (ma_int32)left0 * factor; - pOutputSamples[i*8+1] = (ma_int32)right0 * factor; - pOutputSamples[i*8+2] = (ma_int32)left1 * factor; - pOutputSamples[i*8+3] = (ma_int32)right1 * factor; - pOutputSamples[i*8+4] = (ma_int32)left2 * factor; - pOutputSamples[i*8+5] = (ma_int32)right2 * factor; - pOutputSamples[i*8+6] = (ma_int32)left3 * factor; - pOutputSamples[i*8+7] = (ma_int32)right3 * factor; - } - for (i = (frameCount4 << 2); i < frameCount; ++i) { - ma_uint32 side = pInputSamples0U32[i] << shift0; - ma_uint32 right = pInputSamples1U32[i] << shift1; - ma_uint32 left = right + side; - pOutputSamples[i*2+0] = (ma_int32)left * factor; - pOutputSamples[i*2+1] = (ma_int32)right * factor; - } -} -#if defined(MA_DR_FLAC_SUPPORT_SSE2) -static MA_INLINE void ma_dr_flac_read_pcm_frames_f32__decode_right_side__sse2(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, float* pOutputSamples) -{ - ma_uint64 i; - ma_uint64 frameCount4 = frameCount >> 2; - const ma_uint32* pInputSamples0U32 = (const ma_uint32*)pInputSamples0; - const ma_uint32* pInputSamples1U32 = (const ma_uint32*)pInputSamples1; - ma_uint32 shift0 = (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample) - 8; - ma_uint32 shift1 = (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample) - 8; - __m128 factor; - MA_DR_FLAC_ASSERT(pFlac->bitsPerSample <= 24); - factor = _mm_set1_ps(1.0f / 8388608.0f); - for (i = 0; i < frameCount4; ++i) { - __m128i side = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples0 + i), shift0); - __m128i right = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples1 + i), shift1); - __m128i left = _mm_add_epi32(right, side); - __m128 leftf = _mm_mul_ps(_mm_cvtepi32_ps(left), factor); - __m128 rightf = _mm_mul_ps(_mm_cvtepi32_ps(right), factor); - _mm_storeu_ps(pOutputSamples + i*8 + 0, _mm_unpacklo_ps(leftf, rightf)); - _mm_storeu_ps(pOutputSamples + i*8 + 4, _mm_unpackhi_ps(leftf, rightf)); - } - for (i = (frameCount4 << 2); i < frameCount; ++i) { - ma_uint32 side = pInputSamples0U32[i] << shift0; - ma_uint32 right = pInputSamples1U32[i] << shift1; - ma_uint32 left = right + side; - pOutputSamples[i*2+0] = (ma_int32)left / 8388608.0f; - pOutputSamples[i*2+1] = (ma_int32)right / 8388608.0f; - } -} -#endif -#if defined(MA_DR_FLAC_SUPPORT_NEON) -static MA_INLINE void ma_dr_flac_read_pcm_frames_f32__decode_right_side__neon(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, float* pOutputSamples) -{ - ma_uint64 i; - ma_uint64 frameCount4 = frameCount >> 2; - const ma_uint32* pInputSamples0U32 = (const ma_uint32*)pInputSamples0; - const ma_uint32* pInputSamples1U32 = (const ma_uint32*)pInputSamples1; - ma_uint32 shift0 = (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample) - 8; - ma_uint32 shift1 = (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample) - 8; - float32x4_t factor4; - int32x4_t shift0_4; - int32x4_t shift1_4; - MA_DR_FLAC_ASSERT(pFlac->bitsPerSample <= 24); - factor4 = vdupq_n_f32(1.0f / 8388608.0f); - shift0_4 = vdupq_n_s32(shift0); - shift1_4 = vdupq_n_s32(shift1); - for (i = 0; i < frameCount4; ++i) { - uint32x4_t side; - uint32x4_t right; - uint32x4_t left; - float32x4_t leftf; - float32x4_t rightf; - side = vshlq_u32(vld1q_u32(pInputSamples0U32 + i*4), shift0_4); - right = vshlq_u32(vld1q_u32(pInputSamples1U32 + i*4), shift1_4); - left = vaddq_u32(right, side); - leftf = vmulq_f32(vcvtq_f32_s32(vreinterpretq_s32_u32(left)), factor4); - rightf = vmulq_f32(vcvtq_f32_s32(vreinterpretq_s32_u32(right)), factor4); - ma_dr_flac__vst2q_f32(pOutputSamples + i*8, vzipq_f32(leftf, rightf)); - } - for (i = (frameCount4 << 2); i < frameCount; ++i) { - ma_uint32 side = pInputSamples0U32[i] << shift0; - ma_uint32 right = pInputSamples1U32[i] << shift1; - ma_uint32 left = right + side; - pOutputSamples[i*2+0] = (ma_int32)left / 8388608.0f; - pOutputSamples[i*2+1] = (ma_int32)right / 8388608.0f; - } -} -#endif -static MA_INLINE void ma_dr_flac_read_pcm_frames_f32__decode_right_side(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, float* pOutputSamples) -{ -#if defined(MA_DR_FLAC_SUPPORT_SSE2) - if (ma_dr_flac__gIsSSE2Supported && pFlac->bitsPerSample <= 24) { - ma_dr_flac_read_pcm_frames_f32__decode_right_side__sse2(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); - } else -#elif defined(MA_DR_FLAC_SUPPORT_NEON) - if (ma_dr_flac__gIsNEONSupported && pFlac->bitsPerSample <= 24) { - ma_dr_flac_read_pcm_frames_f32__decode_right_side__neon(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); - } else -#endif - { -#if 0 - ma_dr_flac_read_pcm_frames_f32__decode_right_side__reference(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); -#else - ma_dr_flac_read_pcm_frames_f32__decode_right_side__scalar(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); -#endif - } -} -#if 0 -static MA_INLINE void ma_dr_flac_read_pcm_frames_f32__decode_mid_side__reference(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, float* pOutputSamples) -{ - for (ma_uint64 i = 0; i < frameCount; ++i) { - ma_uint32 mid = (ma_uint32)pInputSamples0[i] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; - ma_uint32 side = (ma_uint32)pInputSamples1[i] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; - mid = (mid << 1) | (side & 0x01); - pOutputSamples[i*2+0] = (float)((((ma_int32)(mid + side) >> 1) << (unusedBitsPerSample)) / 2147483648.0); - pOutputSamples[i*2+1] = (float)((((ma_int32)(mid - side) >> 1) << (unusedBitsPerSample)) / 2147483648.0); - } -} -#endif -static MA_INLINE void ma_dr_flac_read_pcm_frames_f32__decode_mid_side__scalar(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, float* pOutputSamples) -{ - ma_uint64 i; - ma_uint64 frameCount4 = frameCount >> 2; - const ma_uint32* pInputSamples0U32 = (const ma_uint32*)pInputSamples0; - const ma_uint32* pInputSamples1U32 = (const ma_uint32*)pInputSamples1; - ma_uint32 shift = unusedBitsPerSample; - float factor = 1 / 2147483648.0; - if (shift > 0) { - shift -= 1; - for (i = 0; i < frameCount4; ++i) { - ma_uint32 temp0L; - ma_uint32 temp1L; - ma_uint32 temp2L; - ma_uint32 temp3L; - ma_uint32 temp0R; - ma_uint32 temp1R; - ma_uint32 temp2R; - ma_uint32 temp3R; - ma_uint32 mid0 = pInputSamples0U32[i*4+0] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; - ma_uint32 mid1 = pInputSamples0U32[i*4+1] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; - ma_uint32 mid2 = pInputSamples0U32[i*4+2] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; - ma_uint32 mid3 = pInputSamples0U32[i*4+3] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; - ma_uint32 side0 = pInputSamples1U32[i*4+0] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; - ma_uint32 side1 = pInputSamples1U32[i*4+1] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; - ma_uint32 side2 = pInputSamples1U32[i*4+2] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; - ma_uint32 side3 = pInputSamples1U32[i*4+3] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; - mid0 = (mid0 << 1) | (side0 & 0x01); - mid1 = (mid1 << 1) | (side1 & 0x01); - mid2 = (mid2 << 1) | (side2 & 0x01); - mid3 = (mid3 << 1) | (side3 & 0x01); - temp0L = (mid0 + side0) << shift; - temp1L = (mid1 + side1) << shift; - temp2L = (mid2 + side2) << shift; - temp3L = (mid3 + side3) << shift; - temp0R = (mid0 - side0) << shift; - temp1R = (mid1 - side1) << shift; - temp2R = (mid2 - side2) << shift; - temp3R = (mid3 - side3) << shift; - pOutputSamples[i*8+0] = (ma_int32)temp0L * factor; - pOutputSamples[i*8+1] = (ma_int32)temp0R * factor; - pOutputSamples[i*8+2] = (ma_int32)temp1L * factor; - pOutputSamples[i*8+3] = (ma_int32)temp1R * factor; - pOutputSamples[i*8+4] = (ma_int32)temp2L * factor; - pOutputSamples[i*8+5] = (ma_int32)temp2R * factor; - pOutputSamples[i*8+6] = (ma_int32)temp3L * factor; - pOutputSamples[i*8+7] = (ma_int32)temp3R * factor; - } - } else { - for (i = 0; i < frameCount4; ++i) { - ma_uint32 temp0L; - ma_uint32 temp1L; - ma_uint32 temp2L; - ma_uint32 temp3L; - ma_uint32 temp0R; - ma_uint32 temp1R; - ma_uint32 temp2R; - ma_uint32 temp3R; - ma_uint32 mid0 = pInputSamples0U32[i*4+0] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; - ma_uint32 mid1 = pInputSamples0U32[i*4+1] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; - ma_uint32 mid2 = pInputSamples0U32[i*4+2] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; - ma_uint32 mid3 = pInputSamples0U32[i*4+3] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; - ma_uint32 side0 = pInputSamples1U32[i*4+0] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; - ma_uint32 side1 = pInputSamples1U32[i*4+1] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; - ma_uint32 side2 = pInputSamples1U32[i*4+2] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; - ma_uint32 side3 = pInputSamples1U32[i*4+3] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; - mid0 = (mid0 << 1) | (side0 & 0x01); - mid1 = (mid1 << 1) | (side1 & 0x01); - mid2 = (mid2 << 1) | (side2 & 0x01); - mid3 = (mid3 << 1) | (side3 & 0x01); - temp0L = (ma_uint32)((ma_int32)(mid0 + side0) >> 1); - temp1L = (ma_uint32)((ma_int32)(mid1 + side1) >> 1); - temp2L = (ma_uint32)((ma_int32)(mid2 + side2) >> 1); - temp3L = (ma_uint32)((ma_int32)(mid3 + side3) >> 1); - temp0R = (ma_uint32)((ma_int32)(mid0 - side0) >> 1); - temp1R = (ma_uint32)((ma_int32)(mid1 - side1) >> 1); - temp2R = (ma_uint32)((ma_int32)(mid2 - side2) >> 1); - temp3R = (ma_uint32)((ma_int32)(mid3 - side3) >> 1); - pOutputSamples[i*8+0] = (ma_int32)temp0L * factor; - pOutputSamples[i*8+1] = (ma_int32)temp0R * factor; - pOutputSamples[i*8+2] = (ma_int32)temp1L * factor; - pOutputSamples[i*8+3] = (ma_int32)temp1R * factor; - pOutputSamples[i*8+4] = (ma_int32)temp2L * factor; - pOutputSamples[i*8+5] = (ma_int32)temp2R * factor; - pOutputSamples[i*8+6] = (ma_int32)temp3L * factor; - pOutputSamples[i*8+7] = (ma_int32)temp3R * factor; - } - } - for (i = (frameCount4 << 2); i < frameCount; ++i) { - ma_uint32 mid = pInputSamples0U32[i] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; - ma_uint32 side = pInputSamples1U32[i] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; - mid = (mid << 1) | (side & 0x01); - pOutputSamples[i*2+0] = (ma_int32)((ma_uint32)((ma_int32)(mid + side) >> 1) << unusedBitsPerSample) * factor; - pOutputSamples[i*2+1] = (ma_int32)((ma_uint32)((ma_int32)(mid - side) >> 1) << unusedBitsPerSample) * factor; - } -} -#if defined(MA_DR_FLAC_SUPPORT_SSE2) -static MA_INLINE void ma_dr_flac_read_pcm_frames_f32__decode_mid_side__sse2(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, float* pOutputSamples) -{ - ma_uint64 i; - ma_uint64 frameCount4 = frameCount >> 2; - const ma_uint32* pInputSamples0U32 = (const ma_uint32*)pInputSamples0; - const ma_uint32* pInputSamples1U32 = (const ma_uint32*)pInputSamples1; - ma_uint32 shift = unusedBitsPerSample - 8; - float factor; - __m128 factor128; - MA_DR_FLAC_ASSERT(pFlac->bitsPerSample <= 24); - factor = 1.0f / 8388608.0f; - factor128 = _mm_set1_ps(factor); - if (shift == 0) { - for (i = 0; i < frameCount4; ++i) { - __m128i mid; - __m128i side; - __m128i tempL; - __m128i tempR; - __m128 leftf; - __m128 rightf; - mid = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples0 + i), pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample); - side = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples1 + i), pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample); - mid = _mm_or_si128(_mm_slli_epi32(mid, 1), _mm_and_si128(side, _mm_set1_epi32(0x01))); - tempL = _mm_srai_epi32(_mm_add_epi32(mid, side), 1); - tempR = _mm_srai_epi32(_mm_sub_epi32(mid, side), 1); - leftf = _mm_mul_ps(_mm_cvtepi32_ps(tempL), factor128); - rightf = _mm_mul_ps(_mm_cvtepi32_ps(tempR), factor128); - _mm_storeu_ps(pOutputSamples + i*8 + 0, _mm_unpacklo_ps(leftf, rightf)); - _mm_storeu_ps(pOutputSamples + i*8 + 4, _mm_unpackhi_ps(leftf, rightf)); - } - for (i = (frameCount4 << 2); i < frameCount; ++i) { - ma_uint32 mid = pInputSamples0U32[i] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; - ma_uint32 side = pInputSamples1U32[i] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; - mid = (mid << 1) | (side & 0x01); - pOutputSamples[i*2+0] = ((ma_int32)(mid + side) >> 1) * factor; - pOutputSamples[i*2+1] = ((ma_int32)(mid - side) >> 1) * factor; - } - } else { - shift -= 1; - for (i = 0; i < frameCount4; ++i) { - __m128i mid; - __m128i side; - __m128i tempL; - __m128i tempR; - __m128 leftf; - __m128 rightf; - mid = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples0 + i), pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample); - side = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples1 + i), pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample); - mid = _mm_or_si128(_mm_slli_epi32(mid, 1), _mm_and_si128(side, _mm_set1_epi32(0x01))); - tempL = _mm_slli_epi32(_mm_add_epi32(mid, side), shift); - tempR = _mm_slli_epi32(_mm_sub_epi32(mid, side), shift); - leftf = _mm_mul_ps(_mm_cvtepi32_ps(tempL), factor128); - rightf = _mm_mul_ps(_mm_cvtepi32_ps(tempR), factor128); - _mm_storeu_ps(pOutputSamples + i*8 + 0, _mm_unpacklo_ps(leftf, rightf)); - _mm_storeu_ps(pOutputSamples + i*8 + 4, _mm_unpackhi_ps(leftf, rightf)); - } - for (i = (frameCount4 << 2); i < frameCount; ++i) { - ma_uint32 mid = pInputSamples0U32[i] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; - ma_uint32 side = pInputSamples1U32[i] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; - mid = (mid << 1) | (side & 0x01); - pOutputSamples[i*2+0] = (ma_int32)((mid + side) << shift) * factor; - pOutputSamples[i*2+1] = (ma_int32)((mid - side) << shift) * factor; - } - } -} -#endif -#if defined(MA_DR_FLAC_SUPPORT_NEON) -static MA_INLINE void ma_dr_flac_read_pcm_frames_f32__decode_mid_side__neon(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, float* pOutputSamples) -{ - ma_uint64 i; - ma_uint64 frameCount4 = frameCount >> 2; - const ma_uint32* pInputSamples0U32 = (const ma_uint32*)pInputSamples0; - const ma_uint32* pInputSamples1U32 = (const ma_uint32*)pInputSamples1; - ma_uint32 shift = unusedBitsPerSample - 8; - float factor; - float32x4_t factor4; - int32x4_t shift4; - int32x4_t wbps0_4; - int32x4_t wbps1_4; - MA_DR_FLAC_ASSERT(pFlac->bitsPerSample <= 24); - factor = 1.0f / 8388608.0f; - factor4 = vdupq_n_f32(factor); - wbps0_4 = vdupq_n_s32(pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample); - wbps1_4 = vdupq_n_s32(pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample); - if (shift == 0) { - for (i = 0; i < frameCount4; ++i) { - int32x4_t lefti; - int32x4_t righti; - float32x4_t leftf; - float32x4_t rightf; - uint32x4_t mid = vshlq_u32(vld1q_u32(pInputSamples0U32 + i*4), wbps0_4); - uint32x4_t side = vshlq_u32(vld1q_u32(pInputSamples1U32 + i*4), wbps1_4); - mid = vorrq_u32(vshlq_n_u32(mid, 1), vandq_u32(side, vdupq_n_u32(1))); - lefti = vshrq_n_s32(vreinterpretq_s32_u32(vaddq_u32(mid, side)), 1); - righti = vshrq_n_s32(vreinterpretq_s32_u32(vsubq_u32(mid, side)), 1); - leftf = vmulq_f32(vcvtq_f32_s32(lefti), factor4); - rightf = vmulq_f32(vcvtq_f32_s32(righti), factor4); - ma_dr_flac__vst2q_f32(pOutputSamples + i*8, vzipq_f32(leftf, rightf)); - } - for (i = (frameCount4 << 2); i < frameCount; ++i) { - ma_uint32 mid = pInputSamples0U32[i] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; - ma_uint32 side = pInputSamples1U32[i] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; - mid = (mid << 1) | (side & 0x01); - pOutputSamples[i*2+0] = ((ma_int32)(mid + side) >> 1) * factor; - pOutputSamples[i*2+1] = ((ma_int32)(mid - side) >> 1) * factor; - } - } else { - shift -= 1; - shift4 = vdupq_n_s32(shift); - for (i = 0; i < frameCount4; ++i) { - uint32x4_t mid; - uint32x4_t side; - int32x4_t lefti; - int32x4_t righti; - float32x4_t leftf; - float32x4_t rightf; - mid = vshlq_u32(vld1q_u32(pInputSamples0U32 + i*4), wbps0_4); - side = vshlq_u32(vld1q_u32(pInputSamples1U32 + i*4), wbps1_4); - mid = vorrq_u32(vshlq_n_u32(mid, 1), vandq_u32(side, vdupq_n_u32(1))); - lefti = vreinterpretq_s32_u32(vshlq_u32(vaddq_u32(mid, side), shift4)); - righti = vreinterpretq_s32_u32(vshlq_u32(vsubq_u32(mid, side), shift4)); - leftf = vmulq_f32(vcvtq_f32_s32(lefti), factor4); - rightf = vmulq_f32(vcvtq_f32_s32(righti), factor4); - ma_dr_flac__vst2q_f32(pOutputSamples + i*8, vzipq_f32(leftf, rightf)); - } - for (i = (frameCount4 << 2); i < frameCount; ++i) { - ma_uint32 mid = pInputSamples0U32[i] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; - ma_uint32 side = pInputSamples1U32[i] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; - mid = (mid << 1) | (side & 0x01); - pOutputSamples[i*2+0] = (ma_int32)((mid + side) << shift) * factor; - pOutputSamples[i*2+1] = (ma_int32)((mid - side) << shift) * factor; - } - } -} -#endif -static MA_INLINE void ma_dr_flac_read_pcm_frames_f32__decode_mid_side(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, float* pOutputSamples) -{ -#if defined(MA_DR_FLAC_SUPPORT_SSE2) - if (ma_dr_flac__gIsSSE2Supported && pFlac->bitsPerSample <= 24) { - ma_dr_flac_read_pcm_frames_f32__decode_mid_side__sse2(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); - } else -#elif defined(MA_DR_FLAC_SUPPORT_NEON) - if (ma_dr_flac__gIsNEONSupported && pFlac->bitsPerSample <= 24) { - ma_dr_flac_read_pcm_frames_f32__decode_mid_side__neon(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); - } else -#endif - { -#if 0 - ma_dr_flac_read_pcm_frames_f32__decode_mid_side__reference(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); -#else - ma_dr_flac_read_pcm_frames_f32__decode_mid_side__scalar(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); -#endif - } -} -#if 0 -static MA_INLINE void ma_dr_flac_read_pcm_frames_f32__decode_independent_stereo__reference(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, float* pOutputSamples) -{ - for (ma_uint64 i = 0; i < frameCount; ++i) { - pOutputSamples[i*2+0] = (float)((ma_int32)((ma_uint32)pInputSamples0[i] << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample)) / 2147483648.0); - pOutputSamples[i*2+1] = (float)((ma_int32)((ma_uint32)pInputSamples1[i] << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample)) / 2147483648.0); - } -} -#endif -static MA_INLINE void ma_dr_flac_read_pcm_frames_f32__decode_independent_stereo__scalar(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, float* pOutputSamples) -{ - ma_uint64 i; - ma_uint64 frameCount4 = frameCount >> 2; - const ma_uint32* pInputSamples0U32 = (const ma_uint32*)pInputSamples0; - const ma_uint32* pInputSamples1U32 = (const ma_uint32*)pInputSamples1; - ma_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; - ma_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; - float factor = 1 / 2147483648.0; - for (i = 0; i < frameCount4; ++i) { - ma_uint32 tempL0 = pInputSamples0U32[i*4+0] << shift0; - ma_uint32 tempL1 = pInputSamples0U32[i*4+1] << shift0; - ma_uint32 tempL2 = pInputSamples0U32[i*4+2] << shift0; - ma_uint32 tempL3 = pInputSamples0U32[i*4+3] << shift0; - ma_uint32 tempR0 = pInputSamples1U32[i*4+0] << shift1; - ma_uint32 tempR1 = pInputSamples1U32[i*4+1] << shift1; - ma_uint32 tempR2 = pInputSamples1U32[i*4+2] << shift1; - ma_uint32 tempR3 = pInputSamples1U32[i*4+3] << shift1; - pOutputSamples[i*8+0] = (ma_int32)tempL0 * factor; - pOutputSamples[i*8+1] = (ma_int32)tempR0 * factor; - pOutputSamples[i*8+2] = (ma_int32)tempL1 * factor; - pOutputSamples[i*8+3] = (ma_int32)tempR1 * factor; - pOutputSamples[i*8+4] = (ma_int32)tempL2 * factor; - pOutputSamples[i*8+5] = (ma_int32)tempR2 * factor; - pOutputSamples[i*8+6] = (ma_int32)tempL3 * factor; - pOutputSamples[i*8+7] = (ma_int32)tempR3 * factor; - } - for (i = (frameCount4 << 2); i < frameCount; ++i) { - pOutputSamples[i*2+0] = (ma_int32)(pInputSamples0U32[i] << shift0) * factor; - pOutputSamples[i*2+1] = (ma_int32)(pInputSamples1U32[i] << shift1) * factor; - } -} -#if defined(MA_DR_FLAC_SUPPORT_SSE2) -static MA_INLINE void ma_dr_flac_read_pcm_frames_f32__decode_independent_stereo__sse2(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, float* pOutputSamples) -{ - ma_uint64 i; - ma_uint64 frameCount4 = frameCount >> 2; - const ma_uint32* pInputSamples0U32 = (const ma_uint32*)pInputSamples0; - const ma_uint32* pInputSamples1U32 = (const ma_uint32*)pInputSamples1; - ma_uint32 shift0 = (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample) - 8; - ma_uint32 shift1 = (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample) - 8; - float factor = 1.0f / 8388608.0f; - __m128 factor128 = _mm_set1_ps(factor); - for (i = 0; i < frameCount4; ++i) { - __m128i lefti; - __m128i righti; - __m128 leftf; - __m128 rightf; - lefti = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples0 + i), shift0); - righti = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples1 + i), shift1); - leftf = _mm_mul_ps(_mm_cvtepi32_ps(lefti), factor128); - rightf = _mm_mul_ps(_mm_cvtepi32_ps(righti), factor128); - _mm_storeu_ps(pOutputSamples + i*8 + 0, _mm_unpacklo_ps(leftf, rightf)); - _mm_storeu_ps(pOutputSamples + i*8 + 4, _mm_unpackhi_ps(leftf, rightf)); - } - for (i = (frameCount4 << 2); i < frameCount; ++i) { - pOutputSamples[i*2+0] = (ma_int32)(pInputSamples0U32[i] << shift0) * factor; - pOutputSamples[i*2+1] = (ma_int32)(pInputSamples1U32[i] << shift1) * factor; - } -} -#endif -#if defined(MA_DR_FLAC_SUPPORT_NEON) -static MA_INLINE void ma_dr_flac_read_pcm_frames_f32__decode_independent_stereo__neon(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, float* pOutputSamples) -{ - ma_uint64 i; - ma_uint64 frameCount4 = frameCount >> 2; - const ma_uint32* pInputSamples0U32 = (const ma_uint32*)pInputSamples0; - const ma_uint32* pInputSamples1U32 = (const ma_uint32*)pInputSamples1; - ma_uint32 shift0 = (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample) - 8; - ma_uint32 shift1 = (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample) - 8; - float factor = 1.0f / 8388608.0f; - float32x4_t factor4 = vdupq_n_f32(factor); - int32x4_t shift0_4 = vdupq_n_s32(shift0); - int32x4_t shift1_4 = vdupq_n_s32(shift1); - for (i = 0; i < frameCount4; ++i) { - int32x4_t lefti; - int32x4_t righti; - float32x4_t leftf; - float32x4_t rightf; - lefti = vreinterpretq_s32_u32(vshlq_u32(vld1q_u32(pInputSamples0U32 + i*4), shift0_4)); - righti = vreinterpretq_s32_u32(vshlq_u32(vld1q_u32(pInputSamples1U32 + i*4), shift1_4)); - leftf = vmulq_f32(vcvtq_f32_s32(lefti), factor4); - rightf = vmulq_f32(vcvtq_f32_s32(righti), factor4); - ma_dr_flac__vst2q_f32(pOutputSamples + i*8, vzipq_f32(leftf, rightf)); - } - for (i = (frameCount4 << 2); i < frameCount; ++i) { - pOutputSamples[i*2+0] = (ma_int32)(pInputSamples0U32[i] << shift0) * factor; - pOutputSamples[i*2+1] = (ma_int32)(pInputSamples1U32[i] << shift1) * factor; - } -} -#endif -static MA_INLINE void ma_dr_flac_read_pcm_frames_f32__decode_independent_stereo(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, float* pOutputSamples) -{ -#if defined(MA_DR_FLAC_SUPPORT_SSE2) - if (ma_dr_flac__gIsSSE2Supported && pFlac->bitsPerSample <= 24) { - ma_dr_flac_read_pcm_frames_f32__decode_independent_stereo__sse2(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); - } else -#elif defined(MA_DR_FLAC_SUPPORT_NEON) - if (ma_dr_flac__gIsNEONSupported && pFlac->bitsPerSample <= 24) { - ma_dr_flac_read_pcm_frames_f32__decode_independent_stereo__neon(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); - } else -#endif - { -#if 0 - ma_dr_flac_read_pcm_frames_f32__decode_independent_stereo__reference(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); -#else - ma_dr_flac_read_pcm_frames_f32__decode_independent_stereo__scalar(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); -#endif - } -} -MA_API ma_uint64 ma_dr_flac_read_pcm_frames_f32(ma_dr_flac* pFlac, ma_uint64 framesToRead, float* pBufferOut) -{ - ma_uint64 framesRead; - ma_uint32 unusedBitsPerSample; - if (pFlac == NULL || framesToRead == 0) { - return 0; - } - if (pBufferOut == NULL) { - return ma_dr_flac__seek_forward_by_pcm_frames(pFlac, framesToRead); - } - MA_DR_FLAC_ASSERT(pFlac->bitsPerSample <= 32); - unusedBitsPerSample = 32 - pFlac->bitsPerSample; - framesRead = 0; - while (framesToRead > 0) { - if (pFlac->currentFLACFrame.pcmFramesRemaining == 0) { - if (!ma_dr_flac__read_and_decode_next_flac_frame(pFlac)) { - break; - } - } else { - unsigned int channelCount = ma_dr_flac__get_channel_count_from_channel_assignment(pFlac->currentFLACFrame.header.channelAssignment); - ma_uint64 iFirstPCMFrame = pFlac->currentFLACFrame.header.blockSizeInPCMFrames - pFlac->currentFLACFrame.pcmFramesRemaining; - ma_uint64 frameCountThisIteration = framesToRead; - if (frameCountThisIteration > pFlac->currentFLACFrame.pcmFramesRemaining) { - frameCountThisIteration = pFlac->currentFLACFrame.pcmFramesRemaining; - } - if (channelCount == 2) { - const ma_int32* pDecodedSamples0 = pFlac->currentFLACFrame.subframes[0].pSamplesS32 + iFirstPCMFrame; - const ma_int32* pDecodedSamples1 = pFlac->currentFLACFrame.subframes[1].pSamplesS32 + iFirstPCMFrame; - switch (pFlac->currentFLACFrame.header.channelAssignment) - { - case MA_DR_FLAC_CHANNEL_ASSIGNMENT_LEFT_SIDE: - { - ma_dr_flac_read_pcm_frames_f32__decode_left_side(pFlac, frameCountThisIteration, unusedBitsPerSample, pDecodedSamples0, pDecodedSamples1, pBufferOut); - } break; - case MA_DR_FLAC_CHANNEL_ASSIGNMENT_RIGHT_SIDE: - { - ma_dr_flac_read_pcm_frames_f32__decode_right_side(pFlac, frameCountThisIteration, unusedBitsPerSample, pDecodedSamples0, pDecodedSamples1, pBufferOut); - } break; - case MA_DR_FLAC_CHANNEL_ASSIGNMENT_MID_SIDE: - { - ma_dr_flac_read_pcm_frames_f32__decode_mid_side(pFlac, frameCountThisIteration, unusedBitsPerSample, pDecodedSamples0, pDecodedSamples1, pBufferOut); - } break; - case MA_DR_FLAC_CHANNEL_ASSIGNMENT_INDEPENDENT: - default: - { - ma_dr_flac_read_pcm_frames_f32__decode_independent_stereo(pFlac, frameCountThisIteration, unusedBitsPerSample, pDecodedSamples0, pDecodedSamples1, pBufferOut); - } break; - } - } else { - ma_uint64 i; - for (i = 0; i < frameCountThisIteration; ++i) { - unsigned int j; - for (j = 0; j < channelCount; ++j) { - ma_int32 sampleS32 = (ma_int32)((ma_uint32)(pFlac->currentFLACFrame.subframes[j].pSamplesS32[iFirstPCMFrame + i]) << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[j].wastedBitsPerSample)); - pBufferOut[(i*channelCount)+j] = (float)(sampleS32 / 2147483648.0); - } - } - } - framesRead += frameCountThisIteration; - pBufferOut += frameCountThisIteration * channelCount; - framesToRead -= frameCountThisIteration; - pFlac->currentPCMFrame += frameCountThisIteration; - pFlac->currentFLACFrame.pcmFramesRemaining -= (unsigned int)frameCountThisIteration; - } - } - return framesRead; -} -MA_API ma_bool32 ma_dr_flac_seek_to_pcm_frame(ma_dr_flac* pFlac, ma_uint64 pcmFrameIndex) -{ - if (pFlac == NULL) { - return MA_FALSE; - } - if (pFlac->currentPCMFrame == pcmFrameIndex) { - return MA_TRUE; - } - if (pFlac->firstFLACFramePosInBytes == 0) { - return MA_FALSE; - } - if (pcmFrameIndex == 0) { - pFlac->currentPCMFrame = 0; - return ma_dr_flac__seek_to_first_frame(pFlac); - } else { - ma_bool32 wasSuccessful = MA_FALSE; - ma_uint64 originalPCMFrame = pFlac->currentPCMFrame; - if (pcmFrameIndex > pFlac->totalPCMFrameCount) { - pcmFrameIndex = pFlac->totalPCMFrameCount; - } - if (pcmFrameIndex > pFlac->currentPCMFrame) { - ma_uint32 offset = (ma_uint32)(pcmFrameIndex - pFlac->currentPCMFrame); - if (pFlac->currentFLACFrame.pcmFramesRemaining > offset) { - pFlac->currentFLACFrame.pcmFramesRemaining -= offset; - pFlac->currentPCMFrame = pcmFrameIndex; - return MA_TRUE; - } - } else { - ma_uint32 offsetAbs = (ma_uint32)(pFlac->currentPCMFrame - pcmFrameIndex); - ma_uint32 currentFLACFramePCMFrameCount = pFlac->currentFLACFrame.header.blockSizeInPCMFrames; - ma_uint32 currentFLACFramePCMFramesConsumed = currentFLACFramePCMFrameCount - pFlac->currentFLACFrame.pcmFramesRemaining; - if (currentFLACFramePCMFramesConsumed > offsetAbs) { - pFlac->currentFLACFrame.pcmFramesRemaining += offsetAbs; - pFlac->currentPCMFrame = pcmFrameIndex; - return MA_TRUE; - } - } -#ifndef MA_DR_FLAC_NO_OGG - if (pFlac->container == ma_dr_flac_container_ogg) - { - wasSuccessful = ma_dr_flac_ogg__seek_to_pcm_frame(pFlac, pcmFrameIndex); - } - else -#endif - { - if (!pFlac->_noSeekTableSeek) { - wasSuccessful = ma_dr_flac__seek_to_pcm_frame__seek_table(pFlac, pcmFrameIndex); - } -#if !defined(MA_DR_FLAC_NO_CRC) - if (!wasSuccessful && !pFlac->_noBinarySearchSeek && pFlac->totalPCMFrameCount > 0) { - wasSuccessful = ma_dr_flac__seek_to_pcm_frame__binary_search(pFlac, pcmFrameIndex); - } -#endif - if (!wasSuccessful && !pFlac->_noBruteForceSeek) { - wasSuccessful = ma_dr_flac__seek_to_pcm_frame__brute_force(pFlac, pcmFrameIndex); - } - } - if (wasSuccessful) { - pFlac->currentPCMFrame = pcmFrameIndex; - } else { - if (ma_dr_flac_seek_to_pcm_frame(pFlac, originalPCMFrame) == MA_FALSE) { - ma_dr_flac_seek_to_pcm_frame(pFlac, 0); - } - } - return wasSuccessful; - } -} -#define MA_DR_FLAC_DEFINE_FULL_READ_AND_CLOSE(extension, type) \ -static type* ma_dr_flac__full_read_and_close_ ## extension (ma_dr_flac* pFlac, unsigned int* channelsOut, unsigned int* sampleRateOut, ma_uint64* totalPCMFrameCountOut)\ -{ \ - type* pSampleData = NULL; \ - ma_uint64 totalPCMFrameCount; \ - \ - MA_DR_FLAC_ASSERT(pFlac != NULL); \ - \ - totalPCMFrameCount = pFlac->totalPCMFrameCount; \ - \ - if (totalPCMFrameCount == 0) { \ - type buffer[4096]; \ - ma_uint64 pcmFramesRead; \ - size_t sampleDataBufferSize = sizeof(buffer); \ - \ - pSampleData = (type*)ma_dr_flac__malloc_from_callbacks(sampleDataBufferSize, &pFlac->allocationCallbacks); \ - if (pSampleData == NULL) { \ - goto on_error; \ - } \ - \ - while ((pcmFramesRead = (ma_uint64)ma_dr_flac_read_pcm_frames_##extension(pFlac, sizeof(buffer)/sizeof(buffer[0])/pFlac->channels, buffer)) > 0) { \ - if (((totalPCMFrameCount + pcmFramesRead) * pFlac->channels * sizeof(type)) > sampleDataBufferSize) { \ - type* pNewSampleData; \ - size_t newSampleDataBufferSize; \ - \ - newSampleDataBufferSize = sampleDataBufferSize * 2; \ - pNewSampleData = (type*)ma_dr_flac__realloc_from_callbacks(pSampleData, newSampleDataBufferSize, sampleDataBufferSize, &pFlac->allocationCallbacks); \ - if (pNewSampleData == NULL) { \ - ma_dr_flac__free_from_callbacks(pSampleData, &pFlac->allocationCallbacks); \ - goto on_error; \ - } \ - \ - sampleDataBufferSize = newSampleDataBufferSize; \ - pSampleData = pNewSampleData; \ - } \ - \ - MA_DR_FLAC_COPY_MEMORY(pSampleData + (totalPCMFrameCount*pFlac->channels), buffer, (size_t)(pcmFramesRead*pFlac->channels*sizeof(type))); \ - totalPCMFrameCount += pcmFramesRead; \ - } \ - \ - \ - MA_DR_FLAC_ZERO_MEMORY(pSampleData + (totalPCMFrameCount*pFlac->channels), (size_t)(sampleDataBufferSize - totalPCMFrameCount*pFlac->channels*sizeof(type))); \ - } else { \ - ma_uint64 dataSize = totalPCMFrameCount*pFlac->channels*sizeof(type); \ - if (dataSize > (ma_uint64)MA_SIZE_MAX) { \ - goto on_error; \ - } \ - \ - pSampleData = (type*)ma_dr_flac__malloc_from_callbacks((size_t)dataSize, &pFlac->allocationCallbacks); \ - if (pSampleData == NULL) { \ - goto on_error; \ - } \ - \ - totalPCMFrameCount = ma_dr_flac_read_pcm_frames_##extension(pFlac, pFlac->totalPCMFrameCount, pSampleData); \ - } \ - \ - if (sampleRateOut) *sampleRateOut = pFlac->sampleRate; \ - if (channelsOut) *channelsOut = pFlac->channels; \ - if (totalPCMFrameCountOut) *totalPCMFrameCountOut = totalPCMFrameCount; \ - \ - ma_dr_flac_close(pFlac); \ - return pSampleData; \ - \ -on_error: \ - ma_dr_flac_close(pFlac); \ - return NULL; \ -} -MA_DR_FLAC_DEFINE_FULL_READ_AND_CLOSE(s32, ma_int32) -MA_DR_FLAC_DEFINE_FULL_READ_AND_CLOSE(s16, ma_int16) -MA_DR_FLAC_DEFINE_FULL_READ_AND_CLOSE(f32, float) -MA_API ma_int32* ma_dr_flac_open_and_read_pcm_frames_s32(ma_dr_flac_read_proc onRead, ma_dr_flac_seek_proc onSeek, void* pUserData, unsigned int* channelsOut, unsigned int* sampleRateOut, ma_uint64* totalPCMFrameCountOut, const ma_allocation_callbacks* pAllocationCallbacks) -{ - ma_dr_flac* pFlac; - if (channelsOut) { - *channelsOut = 0; - } - if (sampleRateOut) { - *sampleRateOut = 0; - } - if (totalPCMFrameCountOut) { - *totalPCMFrameCountOut = 0; - } - pFlac = ma_dr_flac_open(onRead, onSeek, pUserData, pAllocationCallbacks); - if (pFlac == NULL) { - return NULL; - } - return ma_dr_flac__full_read_and_close_s32(pFlac, channelsOut, sampleRateOut, totalPCMFrameCountOut); -} -MA_API ma_int16* ma_dr_flac_open_and_read_pcm_frames_s16(ma_dr_flac_read_proc onRead, ma_dr_flac_seek_proc onSeek, void* pUserData, unsigned int* channelsOut, unsigned int* sampleRateOut, ma_uint64* totalPCMFrameCountOut, const ma_allocation_callbacks* pAllocationCallbacks) -{ - ma_dr_flac* pFlac; - if (channelsOut) { - *channelsOut = 0; - } - if (sampleRateOut) { - *sampleRateOut = 0; - } - if (totalPCMFrameCountOut) { - *totalPCMFrameCountOut = 0; - } - pFlac = ma_dr_flac_open(onRead, onSeek, pUserData, pAllocationCallbacks); - if (pFlac == NULL) { - return NULL; - } - return ma_dr_flac__full_read_and_close_s16(pFlac, channelsOut, sampleRateOut, totalPCMFrameCountOut); -} -MA_API float* ma_dr_flac_open_and_read_pcm_frames_f32(ma_dr_flac_read_proc onRead, ma_dr_flac_seek_proc onSeek, void* pUserData, unsigned int* channelsOut, unsigned int* sampleRateOut, ma_uint64* totalPCMFrameCountOut, const ma_allocation_callbacks* pAllocationCallbacks) -{ - ma_dr_flac* pFlac; - if (channelsOut) { - *channelsOut = 0; - } - if (sampleRateOut) { - *sampleRateOut = 0; - } - if (totalPCMFrameCountOut) { - *totalPCMFrameCountOut = 0; - } - pFlac = ma_dr_flac_open(onRead, onSeek, pUserData, pAllocationCallbacks); - if (pFlac == NULL) { - return NULL; - } - return ma_dr_flac__full_read_and_close_f32(pFlac, channelsOut, sampleRateOut, totalPCMFrameCountOut); -} -#ifndef MA_DR_FLAC_NO_STDIO -MA_API ma_int32* ma_dr_flac_open_file_and_read_pcm_frames_s32(const char* filename, unsigned int* channels, unsigned int* sampleRate, ma_uint64* totalPCMFrameCount, const ma_allocation_callbacks* pAllocationCallbacks) -{ - ma_dr_flac* pFlac; - if (sampleRate) { - *sampleRate = 0; - } - if (channels) { - *channels = 0; - } - if (totalPCMFrameCount) { - *totalPCMFrameCount = 0; - } - pFlac = ma_dr_flac_open_file(filename, pAllocationCallbacks); - if (pFlac == NULL) { - return NULL; - } - return ma_dr_flac__full_read_and_close_s32(pFlac, channels, sampleRate, totalPCMFrameCount); -} -MA_API ma_int16* ma_dr_flac_open_file_and_read_pcm_frames_s16(const char* filename, unsigned int* channels, unsigned int* sampleRate, ma_uint64* totalPCMFrameCount, const ma_allocation_callbacks* pAllocationCallbacks) -{ - ma_dr_flac* pFlac; - if (sampleRate) { - *sampleRate = 0; - } - if (channels) { - *channels = 0; - } - if (totalPCMFrameCount) { - *totalPCMFrameCount = 0; - } - pFlac = ma_dr_flac_open_file(filename, pAllocationCallbacks); - if (pFlac == NULL) { - return NULL; - } - return ma_dr_flac__full_read_and_close_s16(pFlac, channels, sampleRate, totalPCMFrameCount); -} -MA_API float* ma_dr_flac_open_file_and_read_pcm_frames_f32(const char* filename, unsigned int* channels, unsigned int* sampleRate, ma_uint64* totalPCMFrameCount, const ma_allocation_callbacks* pAllocationCallbacks) -{ - ma_dr_flac* pFlac; - if (sampleRate) { - *sampleRate = 0; - } - if (channels) { - *channels = 0; - } - if (totalPCMFrameCount) { - *totalPCMFrameCount = 0; - } - pFlac = ma_dr_flac_open_file(filename, pAllocationCallbacks); - if (pFlac == NULL) { - return NULL; - } - return ma_dr_flac__full_read_and_close_f32(pFlac, channels, sampleRate, totalPCMFrameCount); -} -#endif -MA_API ma_int32* ma_dr_flac_open_memory_and_read_pcm_frames_s32(const void* data, size_t dataSize, unsigned int* channels, unsigned int* sampleRate, ma_uint64* totalPCMFrameCount, const ma_allocation_callbacks* pAllocationCallbacks) -{ - ma_dr_flac* pFlac; - if (sampleRate) { - *sampleRate = 0; - } - if (channels) { - *channels = 0; - } - if (totalPCMFrameCount) { - *totalPCMFrameCount = 0; - } - pFlac = ma_dr_flac_open_memory(data, dataSize, pAllocationCallbacks); - if (pFlac == NULL) { - return NULL; - } - return ma_dr_flac__full_read_and_close_s32(pFlac, channels, sampleRate, totalPCMFrameCount); -} -MA_API ma_int16* ma_dr_flac_open_memory_and_read_pcm_frames_s16(const void* data, size_t dataSize, unsigned int* channels, unsigned int* sampleRate, ma_uint64* totalPCMFrameCount, const ma_allocation_callbacks* pAllocationCallbacks) -{ - ma_dr_flac* pFlac; - if (sampleRate) { - *sampleRate = 0; - } - if (channels) { - *channels = 0; - } - if (totalPCMFrameCount) { - *totalPCMFrameCount = 0; - } - pFlac = ma_dr_flac_open_memory(data, dataSize, pAllocationCallbacks); - if (pFlac == NULL) { - return NULL; - } - return ma_dr_flac__full_read_and_close_s16(pFlac, channels, sampleRate, totalPCMFrameCount); -} -MA_API float* ma_dr_flac_open_memory_and_read_pcm_frames_f32(const void* data, size_t dataSize, unsigned int* channels, unsigned int* sampleRate, ma_uint64* totalPCMFrameCount, const ma_allocation_callbacks* pAllocationCallbacks) -{ - ma_dr_flac* pFlac; - if (sampleRate) { - *sampleRate = 0; - } - if (channels) { - *channels = 0; - } - if (totalPCMFrameCount) { - *totalPCMFrameCount = 0; - } - pFlac = ma_dr_flac_open_memory(data, dataSize, pAllocationCallbacks); - if (pFlac == NULL) { - return NULL; - } - return ma_dr_flac__full_read_and_close_f32(pFlac, channels, sampleRate, totalPCMFrameCount); -} -MA_API void ma_dr_flac_free(void* p, const ma_allocation_callbacks* pAllocationCallbacks) -{ - if (pAllocationCallbacks != NULL) { - ma_dr_flac__free_from_callbacks(p, pAllocationCallbacks); - } else { - ma_dr_flac__free_default(p, NULL); - } -} -MA_API void ma_dr_flac_init_vorbis_comment_iterator(ma_dr_flac_vorbis_comment_iterator* pIter, ma_uint32 commentCount, const void* pComments) -{ - if (pIter == NULL) { - return; - } - pIter->countRemaining = commentCount; - pIter->pRunningData = (const char*)pComments; -} -MA_API const char* ma_dr_flac_next_vorbis_comment(ma_dr_flac_vorbis_comment_iterator* pIter, ma_uint32* pCommentLengthOut) -{ - ma_int32 length; - const char* pComment; - if (pCommentLengthOut) { - *pCommentLengthOut = 0; - } - if (pIter == NULL || pIter->countRemaining == 0 || pIter->pRunningData == NULL) { - return NULL; - } - length = ma_dr_flac__le2host_32_ptr_unaligned(pIter->pRunningData); - pIter->pRunningData += 4; - pComment = pIter->pRunningData; - pIter->pRunningData += length; - pIter->countRemaining -= 1; - if (pCommentLengthOut) { - *pCommentLengthOut = length; - } - return pComment; -} -MA_API void ma_dr_flac_init_cuesheet_track_iterator(ma_dr_flac_cuesheet_track_iterator* pIter, ma_uint32 trackCount, const void* pTrackData) -{ - if (pIter == NULL) { - return; - } - pIter->countRemaining = trackCount; - pIter->pRunningData = (const char*)pTrackData; -} -MA_API ma_bool32 ma_dr_flac_next_cuesheet_track(ma_dr_flac_cuesheet_track_iterator* pIter, ma_dr_flac_cuesheet_track* pCuesheetTrack) -{ - ma_dr_flac_cuesheet_track cuesheetTrack; - const char* pRunningData; - ma_uint64 offsetHi; - ma_uint64 offsetLo; - if (pIter == NULL || pIter->countRemaining == 0 || pIter->pRunningData == NULL) { - return MA_FALSE; - } - pRunningData = pIter->pRunningData; - offsetHi = ma_dr_flac__be2host_32(*(const ma_uint32*)pRunningData); pRunningData += 4; - offsetLo = ma_dr_flac__be2host_32(*(const ma_uint32*)pRunningData); pRunningData += 4; - cuesheetTrack.offset = offsetLo | (offsetHi << 32); - cuesheetTrack.trackNumber = pRunningData[0]; pRunningData += 1; - MA_DR_FLAC_COPY_MEMORY(cuesheetTrack.ISRC, pRunningData, sizeof(cuesheetTrack.ISRC)); pRunningData += 12; - cuesheetTrack.isAudio = (pRunningData[0] & 0x80) != 0; - cuesheetTrack.preEmphasis = (pRunningData[0] & 0x40) != 0; pRunningData += 14; - cuesheetTrack.indexCount = pRunningData[0]; pRunningData += 1; - cuesheetTrack.pIndexPoints = (const ma_dr_flac_cuesheet_track_index*)pRunningData; pRunningData += cuesheetTrack.indexCount * sizeof(ma_dr_flac_cuesheet_track_index); - pIter->pRunningData = pRunningData; - pIter->countRemaining -= 1; - if (pCuesheetTrack) { - *pCuesheetTrack = cuesheetTrack; - } - return MA_TRUE; -} -#if defined(__clang__) || (defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6))) - #pragma GCC diagnostic pop -#endif -#endif -/* dr_flac_c end */ -#endif /* MA_DR_FLAC_IMPLEMENTATION */ -#endif /* MA_NO_FLAC */ - -#if !defined(MA_NO_MP3) && !defined(MA_NO_DECODING) -#if !defined(MA_DR_MP3_IMPLEMENTATION) -/* dr_mp3_c begin */ -#ifndef ma_dr_mp3_c -#define ma_dr_mp3_c -#include -#include -#include -MA_API void ma_dr_mp3_version(ma_uint32* pMajor, ma_uint32* pMinor, ma_uint32* pRevision) -{ - if (pMajor) { - *pMajor = MA_DR_MP3_VERSION_MAJOR; - } - if (pMinor) { - *pMinor = MA_DR_MP3_VERSION_MINOR; - } - if (pRevision) { - *pRevision = MA_DR_MP3_VERSION_REVISION; - } -} -MA_API const char* ma_dr_mp3_version_string(void) -{ - return MA_DR_MP3_VERSION_STRING; -} -#if defined(__TINYC__) -#define MA_DR_MP3_NO_SIMD -#endif -#define MA_DR_MP3_OFFSET_PTR(p, offset) ((void*)((ma_uint8*)(p) + (offset))) -#define MA_DR_MP3_MAX_FREE_FORMAT_FRAME_SIZE 2304 -#ifndef MA_DR_MP3_MAX_FRAME_SYNC_MATCHES -#define MA_DR_MP3_MAX_FRAME_SYNC_MATCHES 10 -#endif -#define MA_DR_MP3_MAX_L3_FRAME_PAYLOAD_BYTES MA_DR_MP3_MAX_FREE_FORMAT_FRAME_SIZE -#define MA_DR_MP3_MAX_BITRESERVOIR_BYTES 511 -#define MA_DR_MP3_SHORT_BLOCK_TYPE 2 -#define MA_DR_MP3_STOP_BLOCK_TYPE 3 -#define MA_DR_MP3_MODE_MONO 3 -#define MA_DR_MP3_MODE_JOINT_STEREO 1 -#define MA_DR_MP3_HDR_SIZE 4 -#define MA_DR_MP3_HDR_IS_MONO(h) (((h[3]) & 0xC0) == 0xC0) -#define MA_DR_MP3_HDR_IS_MS_STEREO(h) (((h[3]) & 0xE0) == 0x60) -#define MA_DR_MP3_HDR_IS_FREE_FORMAT(h) (((h[2]) & 0xF0) == 0) -#define MA_DR_MP3_HDR_IS_CRC(h) (!((h[1]) & 1)) -#define MA_DR_MP3_HDR_TEST_PADDING(h) ((h[2]) & 0x2) -#define MA_DR_MP3_HDR_TEST_MPEG1(h) ((h[1]) & 0x8) -#define MA_DR_MP3_HDR_TEST_NOT_MPEG25(h) ((h[1]) & 0x10) -#define MA_DR_MP3_HDR_TEST_I_STEREO(h) ((h[3]) & 0x10) -#define MA_DR_MP3_HDR_TEST_MS_STEREO(h) ((h[3]) & 0x20) -#define MA_DR_MP3_HDR_GET_STEREO_MODE(h) (((h[3]) >> 6) & 3) -#define MA_DR_MP3_HDR_GET_STEREO_MODE_EXT(h) (((h[3]) >> 4) & 3) -#define MA_DR_MP3_HDR_GET_LAYER(h) (((h[1]) >> 1) & 3) -#define MA_DR_MP3_HDR_GET_BITRATE(h) ((h[2]) >> 4) -#define MA_DR_MP3_HDR_GET_SAMPLE_RATE(h) (((h[2]) >> 2) & 3) -#define MA_DR_MP3_HDR_GET_MY_SAMPLE_RATE(h) (MA_DR_MP3_HDR_GET_SAMPLE_RATE(h) + (((h[1] >> 3) & 1) + ((h[1] >> 4) & 1))*3) -#define MA_DR_MP3_HDR_IS_FRAME_576(h) ((h[1] & 14) == 2) -#define MA_DR_MP3_HDR_IS_LAYER_1(h) ((h[1] & 6) == 6) -#define MA_DR_MP3_BITS_DEQUANTIZER_OUT -1 -#define MA_DR_MP3_MAX_SCF (255 + MA_DR_MP3_BITS_DEQUANTIZER_OUT*4 - 210) -#define MA_DR_MP3_MAX_SCFI ((MA_DR_MP3_MAX_SCF + 3) & ~3) -#define MA_DR_MP3_MIN(a, b) ((a) > (b) ? (b) : (a)) -#define MA_DR_MP3_MAX(a, b) ((a) < (b) ? (b) : (a)) -#if !defined(MA_DR_MP3_NO_SIMD) -#if !defined(MA_DR_MP3_ONLY_SIMD) && (defined(_M_X64) || defined(__x86_64__) || defined(__aarch64__) || defined(_M_ARM64) || defined(_M_ARM64EC)) -#define MA_DR_MP3_ONLY_SIMD -#endif -#if ((defined(_MSC_VER) && _MSC_VER >= 1400) && defined(_M_X64)) || ((defined(__i386) || defined(_M_IX86) || defined(__i386__) || defined(__x86_64__)) && ((defined(_M_IX86_FP) && _M_IX86_FP == 2) || defined(__SSE2__))) -#if defined(_MSC_VER) -#include -#endif -#include -#define MA_DR_MP3_HAVE_SSE 1 -#define MA_DR_MP3_HAVE_SIMD 1 -#define MA_DR_MP3_VSTORE _mm_storeu_ps -#define MA_DR_MP3_VLD _mm_loadu_ps -#define MA_DR_MP3_VSET _mm_set1_ps -#define MA_DR_MP3_VADD _mm_add_ps -#define MA_DR_MP3_VSUB _mm_sub_ps -#define MA_DR_MP3_VMUL _mm_mul_ps -#define MA_DR_MP3_VMAC(a, x, y) _mm_add_ps(a, _mm_mul_ps(x, y)) -#define MA_DR_MP3_VMSB(a, x, y) _mm_sub_ps(a, _mm_mul_ps(x, y)) -#define MA_DR_MP3_VMUL_S(x, s) _mm_mul_ps(x, _mm_set1_ps(s)) -#define MA_DR_MP3_VREV(x) _mm_shuffle_ps(x, x, _MM_SHUFFLE(0, 1, 2, 3)) -typedef __m128 ma_dr_mp3_f4; -#if defined(_MSC_VER) || defined(MA_DR_MP3_ONLY_SIMD) -#define ma_dr_mp3_cpuid __cpuid -#else -static __inline__ __attribute__((always_inline)) void ma_dr_mp3_cpuid(int CPUInfo[], const int InfoType) -{ -#if defined(__PIC__) - __asm__ __volatile__( -#if defined(__x86_64__) - "push %%rbx\n" - "cpuid\n" - "xchgl %%ebx, %1\n" - "pop %%rbx\n" -#else - "xchgl %%ebx, %1\n" - "cpuid\n" - "xchgl %%ebx, %1\n" -#endif - : "=a" (CPUInfo[0]), "=r" (CPUInfo[1]), "=c" (CPUInfo[2]), "=d" (CPUInfo[3]) - : "a" (InfoType)); -#else - __asm__ __volatile__( - "cpuid" - : "=a" (CPUInfo[0]), "=b" (CPUInfo[1]), "=c" (CPUInfo[2]), "=d" (CPUInfo[3]) - : "a" (InfoType)); -#endif -} -#endif -static int ma_dr_mp3_have_simd(void) -{ -#ifdef MA_DR_MP3_ONLY_SIMD - return 1; -#else - static int g_have_simd; - int CPUInfo[4]; -#ifdef MINIMP3_TEST - static int g_counter; - if (g_counter++ > 100) - return 0; -#endif - if (g_have_simd) - goto end; - ma_dr_mp3_cpuid(CPUInfo, 0); - if (CPUInfo[0] > 0) - { - ma_dr_mp3_cpuid(CPUInfo, 1); - g_have_simd = (CPUInfo[3] & (1 << 26)) + 1; - return g_have_simd - 1; - } -end: - return g_have_simd - 1; -#endif -} -#elif defined(__ARM_NEON) || defined(__aarch64__) || defined(_M_ARM64) || defined(_M_ARM64EC) -#include -#define MA_DR_MP3_HAVE_SSE 0 -#define MA_DR_MP3_HAVE_SIMD 1 -#define MA_DR_MP3_VSTORE vst1q_f32 -#define MA_DR_MP3_VLD vld1q_f32 -#define MA_DR_MP3_VSET vmovq_n_f32 -#define MA_DR_MP3_VADD vaddq_f32 -#define MA_DR_MP3_VSUB vsubq_f32 -#define MA_DR_MP3_VMUL vmulq_f32 -#define MA_DR_MP3_VMAC(a, x, y) vmlaq_f32(a, x, y) -#define MA_DR_MP3_VMSB(a, x, y) vmlsq_f32(a, x, y) -#define MA_DR_MP3_VMUL_S(x, s) vmulq_f32(x, vmovq_n_f32(s)) -#define MA_DR_MP3_VREV(x) vcombine_f32(vget_high_f32(vrev64q_f32(x)), vget_low_f32(vrev64q_f32(x))) -typedef float32x4_t ma_dr_mp3_f4; -static int ma_dr_mp3_have_simd(void) -{ - return 1; -} -#else -#define MA_DR_MP3_HAVE_SSE 0 -#define MA_DR_MP3_HAVE_SIMD 0 -#ifdef MA_DR_MP3_ONLY_SIMD -#error MA_DR_MP3_ONLY_SIMD used, but SSE/NEON not enabled -#endif -#endif -#else -#define MA_DR_MP3_HAVE_SIMD 0 -#endif -#if defined(__ARM_ARCH) && (__ARM_ARCH >= 6) && !defined(__aarch64__) && !defined(_M_ARM64) && !defined(_M_ARM64EC) && !defined(__ARM_ARCH_6M__) -#define MA_DR_MP3_HAVE_ARMV6 1 -static __inline__ __attribute__((always_inline)) ma_int32 ma_dr_mp3_clip_int16_arm(ma_int32 a) -{ - ma_int32 x = 0; - __asm__ ("ssat %0, #16, %1" : "=r"(x) : "r"(a)); - return x; -} -#else -#define MA_DR_MP3_HAVE_ARMV6 0 -#endif -#ifndef MA_DR_MP3_ASSERT -#include -#define MA_DR_MP3_ASSERT(expression) assert(expression) -#endif -#ifndef MA_DR_MP3_COPY_MEMORY -#define MA_DR_MP3_COPY_MEMORY(dst, src, sz) memcpy((dst), (src), (sz)) -#endif -#ifndef MA_DR_MP3_MOVE_MEMORY -#define MA_DR_MP3_MOVE_MEMORY(dst, src, sz) memmove((dst), (src), (sz)) -#endif -#ifndef MA_DR_MP3_ZERO_MEMORY -#define MA_DR_MP3_ZERO_MEMORY(p, sz) memset((p), 0, (sz)) -#endif -#define MA_DR_MP3_ZERO_OBJECT(p) MA_DR_MP3_ZERO_MEMORY((p), sizeof(*(p))) -#ifndef MA_DR_MP3_MALLOC -#define MA_DR_MP3_MALLOC(sz) malloc((sz)) -#endif -#ifndef MA_DR_MP3_REALLOC -#define MA_DR_MP3_REALLOC(p, sz) realloc((p), (sz)) -#endif -#ifndef MA_DR_MP3_FREE -#define MA_DR_MP3_FREE(p) free((p)) -#endif -typedef struct -{ - const ma_uint8 *buf; - int pos, limit; -} ma_dr_mp3_bs; -typedef struct -{ - float scf[3*64]; - ma_uint8 total_bands, stereo_bands, bitalloc[64], scfcod[64]; -} ma_dr_mp3_L12_scale_info; -typedef struct -{ - ma_uint8 tab_offset, code_tab_width, band_count; -} ma_dr_mp3_L12_subband_alloc; -typedef struct -{ - const ma_uint8 *sfbtab; - ma_uint16 part_23_length, big_values, scalefac_compress; - ma_uint8 global_gain, block_type, mixed_block_flag, n_long_sfb, n_short_sfb; - ma_uint8 table_select[3], region_count[3], subblock_gain[3]; - ma_uint8 preflag, scalefac_scale, count1_table, scfsi; -} ma_dr_mp3_L3_gr_info; -typedef struct -{ - ma_dr_mp3_bs bs; - ma_uint8 maindata[MA_DR_MP3_MAX_BITRESERVOIR_BYTES + MA_DR_MP3_MAX_L3_FRAME_PAYLOAD_BYTES]; - ma_dr_mp3_L3_gr_info gr_info[4]; - float grbuf[2][576], scf[40], syn[18 + 15][2*32]; - ma_uint8 ist_pos[2][39]; -} ma_dr_mp3dec_scratch; -static void ma_dr_mp3_bs_init(ma_dr_mp3_bs *bs, const ma_uint8 *data, int bytes) -{ - bs->buf = data; - bs->pos = 0; - bs->limit = bytes*8; -} -static ma_uint32 ma_dr_mp3_bs_get_bits(ma_dr_mp3_bs *bs, int n) -{ - ma_uint32 next, cache = 0, s = bs->pos & 7; - int shl = n + s; - const ma_uint8 *p = bs->buf + (bs->pos >> 3); - if ((bs->pos += n) > bs->limit) - return 0; - next = *p++ & (255 >> s); - while ((shl -= 8) > 0) - { - cache |= next << shl; - next = *p++; - } - return cache | (next >> -shl); -} -static int ma_dr_mp3_hdr_valid(const ma_uint8 *h) -{ - return h[0] == 0xff && - ((h[1] & 0xF0) == 0xf0 || (h[1] & 0xFE) == 0xe2) && - (MA_DR_MP3_HDR_GET_LAYER(h) != 0) && - (MA_DR_MP3_HDR_GET_BITRATE(h) != 15) && - (MA_DR_MP3_HDR_GET_SAMPLE_RATE(h) != 3); -} -static int ma_dr_mp3_hdr_compare(const ma_uint8 *h1, const ma_uint8 *h2) -{ - return ma_dr_mp3_hdr_valid(h2) && - ((h1[1] ^ h2[1]) & 0xFE) == 0 && - ((h1[2] ^ h2[2]) & 0x0C) == 0 && - !(MA_DR_MP3_HDR_IS_FREE_FORMAT(h1) ^ MA_DR_MP3_HDR_IS_FREE_FORMAT(h2)); -} -static unsigned ma_dr_mp3_hdr_bitrate_kbps(const ma_uint8 *h) -{ - static const ma_uint8 halfrate[2][3][15] = { - { { 0,4,8,12,16,20,24,28,32,40,48,56,64,72,80 }, { 0,4,8,12,16,20,24,28,32,40,48,56,64,72,80 }, { 0,16,24,28,32,40,48,56,64,72,80,88,96,112,128 } }, - { { 0,16,20,24,28,32,40,48,56,64,80,96,112,128,160 }, { 0,16,24,28,32,40,48,56,64,80,96,112,128,160,192 }, { 0,16,32,48,64,80,96,112,128,144,160,176,192,208,224 } }, - }; - return 2*halfrate[!!MA_DR_MP3_HDR_TEST_MPEG1(h)][MA_DR_MP3_HDR_GET_LAYER(h) - 1][MA_DR_MP3_HDR_GET_BITRATE(h)]; -} -static unsigned ma_dr_mp3_hdr_sample_rate_hz(const ma_uint8 *h) -{ - static const unsigned g_hz[3] = { 44100, 48000, 32000 }; - return g_hz[MA_DR_MP3_HDR_GET_SAMPLE_RATE(h)] >> (int)!MA_DR_MP3_HDR_TEST_MPEG1(h) >> (int)!MA_DR_MP3_HDR_TEST_NOT_MPEG25(h); -} -static unsigned ma_dr_mp3_hdr_frame_samples(const ma_uint8 *h) -{ - return MA_DR_MP3_HDR_IS_LAYER_1(h) ? 384 : (1152 >> (int)MA_DR_MP3_HDR_IS_FRAME_576(h)); -} -static int ma_dr_mp3_hdr_frame_bytes(const ma_uint8 *h, int free_format_size) -{ - int frame_bytes = ma_dr_mp3_hdr_frame_samples(h)*ma_dr_mp3_hdr_bitrate_kbps(h)*125/ma_dr_mp3_hdr_sample_rate_hz(h); - if (MA_DR_MP3_HDR_IS_LAYER_1(h)) - { - frame_bytes &= ~3; - } - return frame_bytes ? frame_bytes : free_format_size; -} -static int ma_dr_mp3_hdr_padding(const ma_uint8 *h) -{ - return MA_DR_MP3_HDR_TEST_PADDING(h) ? (MA_DR_MP3_HDR_IS_LAYER_1(h) ? 4 : 1) : 0; -} -#ifndef MA_DR_MP3_ONLY_MP3 -static const ma_dr_mp3_L12_subband_alloc *ma_dr_mp3_L12_subband_alloc_table(const ma_uint8 *hdr, ma_dr_mp3_L12_scale_info *sci) -{ - const ma_dr_mp3_L12_subband_alloc *alloc; - int mode = MA_DR_MP3_HDR_GET_STEREO_MODE(hdr); - int nbands, stereo_bands = (mode == MA_DR_MP3_MODE_MONO) ? 0 : (mode == MA_DR_MP3_MODE_JOINT_STEREO) ? (MA_DR_MP3_HDR_GET_STEREO_MODE_EXT(hdr) << 2) + 4 : 32; - if (MA_DR_MP3_HDR_IS_LAYER_1(hdr)) - { - static const ma_dr_mp3_L12_subband_alloc g_alloc_L1[] = { { 76, 4, 32 } }; - alloc = g_alloc_L1; - nbands = 32; - } else if (!MA_DR_MP3_HDR_TEST_MPEG1(hdr)) - { - static const ma_dr_mp3_L12_subband_alloc g_alloc_L2M2[] = { { 60, 4, 4 }, { 44, 3, 7 }, { 44, 2, 19 } }; - alloc = g_alloc_L2M2; - nbands = 30; - } else - { - static const ma_dr_mp3_L12_subband_alloc g_alloc_L2M1[] = { { 0, 4, 3 }, { 16, 4, 8 }, { 32, 3, 12 }, { 40, 2, 7 } }; - int sample_rate_idx = MA_DR_MP3_HDR_GET_SAMPLE_RATE(hdr); - unsigned kbps = ma_dr_mp3_hdr_bitrate_kbps(hdr) >> (int)(mode != MA_DR_MP3_MODE_MONO); - if (!kbps) - { - kbps = 192; - } - alloc = g_alloc_L2M1; - nbands = 27; - if (kbps < 56) - { - static const ma_dr_mp3_L12_subband_alloc g_alloc_L2M1_lowrate[] = { { 44, 4, 2 }, { 44, 3, 10 } }; - alloc = g_alloc_L2M1_lowrate; - nbands = sample_rate_idx == 2 ? 12 : 8; - } else if (kbps >= 96 && sample_rate_idx != 1) - { - nbands = 30; - } - } - sci->total_bands = (ma_uint8)nbands; - sci->stereo_bands = (ma_uint8)MA_DR_MP3_MIN(stereo_bands, nbands); - return alloc; -} -static void ma_dr_mp3_L12_read_scalefactors(ma_dr_mp3_bs *bs, ma_uint8 *pba, ma_uint8 *scfcod, int bands, float *scf) -{ - static const float g_deq_L12[18*3] = { -#define MA_DR_MP3_DQ(x) 9.53674316e-07f/x, 7.56931807e-07f/x, 6.00777173e-07f/x - MA_DR_MP3_DQ(3),MA_DR_MP3_DQ(7),MA_DR_MP3_DQ(15),MA_DR_MP3_DQ(31),MA_DR_MP3_DQ(63),MA_DR_MP3_DQ(127),MA_DR_MP3_DQ(255),MA_DR_MP3_DQ(511),MA_DR_MP3_DQ(1023),MA_DR_MP3_DQ(2047),MA_DR_MP3_DQ(4095),MA_DR_MP3_DQ(8191),MA_DR_MP3_DQ(16383),MA_DR_MP3_DQ(32767),MA_DR_MP3_DQ(65535),MA_DR_MP3_DQ(3),MA_DR_MP3_DQ(5),MA_DR_MP3_DQ(9) - }; - int i, m; - for (i = 0; i < bands; i++) - { - float s = 0; - int ba = *pba++; - int mask = ba ? 4 + ((19 >> scfcod[i]) & 3) : 0; - for (m = 4; m; m >>= 1) - { - if (mask & m) - { - int b = ma_dr_mp3_bs_get_bits(bs, 6); - s = g_deq_L12[ba*3 - 6 + b % 3]*(int)(1 << 21 >> b/3); - } - *scf++ = s; - } - } -} -static void ma_dr_mp3_L12_read_scale_info(const ma_uint8 *hdr, ma_dr_mp3_bs *bs, ma_dr_mp3_L12_scale_info *sci) -{ - static const ma_uint8 g_bitalloc_code_tab[] = { - 0,17, 3, 4, 5,6,7, 8,9,10,11,12,13,14,15,16, - 0,17,18, 3,19,4,5, 6,7, 8, 9,10,11,12,13,16, - 0,17,18, 3,19,4,5,16, - 0,17,18,16, - 0,17,18,19, 4,5,6, 7,8, 9,10,11,12,13,14,15, - 0,17,18, 3,19,4,5, 6,7, 8, 9,10,11,12,13,14, - 0, 2, 3, 4, 5,6,7, 8,9,10,11,12,13,14,15,16 - }; - const ma_dr_mp3_L12_subband_alloc *subband_alloc = ma_dr_mp3_L12_subband_alloc_table(hdr, sci); - int i, k = 0, ba_bits = 0; - const ma_uint8 *ba_code_tab = g_bitalloc_code_tab; - for (i = 0; i < sci->total_bands; i++) - { - ma_uint8 ba; - if (i == k) - { - k += subband_alloc->band_count; - ba_bits = subband_alloc->code_tab_width; - ba_code_tab = g_bitalloc_code_tab + subband_alloc->tab_offset; - subband_alloc++; - } - ba = ba_code_tab[ma_dr_mp3_bs_get_bits(bs, ba_bits)]; - sci->bitalloc[2*i] = ba; - if (i < sci->stereo_bands) - { - ba = ba_code_tab[ma_dr_mp3_bs_get_bits(bs, ba_bits)]; - } - sci->bitalloc[2*i + 1] = sci->stereo_bands ? ba : 0; - } - for (i = 0; i < 2*sci->total_bands; i++) - { - sci->scfcod[i] = (ma_uint8)(sci->bitalloc[i] ? MA_DR_MP3_HDR_IS_LAYER_1(hdr) ? 2 : ma_dr_mp3_bs_get_bits(bs, 2) : 6); - } - ma_dr_mp3_L12_read_scalefactors(bs, sci->bitalloc, sci->scfcod, sci->total_bands*2, sci->scf); - for (i = sci->stereo_bands; i < sci->total_bands; i++) - { - sci->bitalloc[2*i + 1] = 0; - } -} -static int ma_dr_mp3_L12_dequantize_granule(float *grbuf, ma_dr_mp3_bs *bs, ma_dr_mp3_L12_scale_info *sci, int group_size) -{ - int i, j, k, choff = 576; - for (j = 0; j < 4; j++) - { - float *dst = grbuf + group_size*j; - for (i = 0; i < 2*sci->total_bands; i++) - { - int ba = sci->bitalloc[i]; - if (ba != 0) - { - if (ba < 17) - { - int half = (1 << (ba - 1)) - 1; - for (k = 0; k < group_size; k++) - { - dst[k] = (float)((int)ma_dr_mp3_bs_get_bits(bs, ba) - half); - } - } else - { - unsigned mod = (2 << (ba - 17)) + 1; - unsigned code = ma_dr_mp3_bs_get_bits(bs, mod + 2 - (mod >> 3)); - for (k = 0; k < group_size; k++, code /= mod) - { - dst[k] = (float)((int)(code % mod - mod/2)); - } - } - } - dst += choff; - choff = 18 - choff; - } - } - return group_size*4; -} -static void ma_dr_mp3_L12_apply_scf_384(ma_dr_mp3_L12_scale_info *sci, const float *scf, float *dst) -{ - int i, k; - MA_DR_MP3_COPY_MEMORY(dst + 576 + sci->stereo_bands*18, dst + sci->stereo_bands*18, (sci->total_bands - sci->stereo_bands)*18*sizeof(float)); - for (i = 0; i < sci->total_bands; i++, dst += 18, scf += 6) - { - for (k = 0; k < 12; k++) - { - dst[k + 0] *= scf[0]; - dst[k + 576] *= scf[3]; - } - } -} -#endif -static int ma_dr_mp3_L3_read_side_info(ma_dr_mp3_bs *bs, ma_dr_mp3_L3_gr_info *gr, const ma_uint8 *hdr) -{ - static const ma_uint8 g_scf_long[8][23] = { - { 6,6,6,6,6,6,8,10,12,14,16,20,24,28,32,38,46,52,60,68,58,54,0 }, - { 12,12,12,12,12,12,16,20,24,28,32,40,48,56,64,76,90,2,2,2,2,2,0 }, - { 6,6,6,6,6,6,8,10,12,14,16,20,24,28,32,38,46,52,60,68,58,54,0 }, - { 6,6,6,6,6,6,8,10,12,14,16,18,22,26,32,38,46,54,62,70,76,36,0 }, - { 6,6,6,6,6,6,8,10,12,14,16,20,24,28,32,38,46,52,60,68,58,54,0 }, - { 4,4,4,4,4,4,6,6,8,8,10,12,16,20,24,28,34,42,50,54,76,158,0 }, - { 4,4,4,4,4,4,6,6,6,8,10,12,16,18,22,28,34,40,46,54,54,192,0 }, - { 4,4,4,4,4,4,6,6,8,10,12,16,20,24,30,38,46,56,68,84,102,26,0 } - }; - static const ma_uint8 g_scf_short[8][40] = { - { 4,4,4,4,4,4,4,4,4,6,6,6,8,8,8,10,10,10,12,12,12,14,14,14,18,18,18,24,24,24,30,30,30,40,40,40,18,18,18,0 }, - { 8,8,8,8,8,8,8,8,8,12,12,12,16,16,16,20,20,20,24,24,24,28,28,28,36,36,36,2,2,2,2,2,2,2,2,2,26,26,26,0 }, - { 4,4,4,4,4,4,4,4,4,6,6,6,6,6,6,8,8,8,10,10,10,14,14,14,18,18,18,26,26,26,32,32,32,42,42,42,18,18,18,0 }, - { 4,4,4,4,4,4,4,4,4,6,6,6,8,8,8,10,10,10,12,12,12,14,14,14,18,18,18,24,24,24,32,32,32,44,44,44,12,12,12,0 }, - { 4,4,4,4,4,4,4,4,4,6,6,6,8,8,8,10,10,10,12,12,12,14,14,14,18,18,18,24,24,24,30,30,30,40,40,40,18,18,18,0 }, - { 4,4,4,4,4,4,4,4,4,4,4,4,6,6,6,8,8,8,10,10,10,12,12,12,14,14,14,18,18,18,22,22,22,30,30,30,56,56,56,0 }, - { 4,4,4,4,4,4,4,4,4,4,4,4,6,6,6,6,6,6,10,10,10,12,12,12,14,14,14,16,16,16,20,20,20,26,26,26,66,66,66,0 }, - { 4,4,4,4,4,4,4,4,4,4,4,4,6,6,6,8,8,8,12,12,12,16,16,16,20,20,20,26,26,26,34,34,34,42,42,42,12,12,12,0 } - }; - static const ma_uint8 g_scf_mixed[8][40] = { - { 6,6,6,6,6,6,6,6,6,8,8,8,10,10,10,12,12,12,14,14,14,18,18,18,24,24,24,30,30,30,40,40,40,18,18,18,0 }, - { 12,12,12,4,4,4,8,8,8,12,12,12,16,16,16,20,20,20,24,24,24,28,28,28,36,36,36,2,2,2,2,2,2,2,2,2,26,26,26,0 }, - { 6,6,6,6,6,6,6,6,6,6,6,6,8,8,8,10,10,10,14,14,14,18,18,18,26,26,26,32,32,32,42,42,42,18,18,18,0 }, - { 6,6,6,6,6,6,6,6,6,8,8,8,10,10,10,12,12,12,14,14,14,18,18,18,24,24,24,32,32,32,44,44,44,12,12,12,0 }, - { 6,6,6,6,6,6,6,6,6,8,8,8,10,10,10,12,12,12,14,14,14,18,18,18,24,24,24,30,30,30,40,40,40,18,18,18,0 }, - { 4,4,4,4,4,4,6,6,4,4,4,6,6,6,8,8,8,10,10,10,12,12,12,14,14,14,18,18,18,22,22,22,30,30,30,56,56,56,0 }, - { 4,4,4,4,4,4,6,6,4,4,4,6,6,6,6,6,6,10,10,10,12,12,12,14,14,14,16,16,16,20,20,20,26,26,26,66,66,66,0 }, - { 4,4,4,4,4,4,6,6,4,4,4,6,6,6,8,8,8,12,12,12,16,16,16,20,20,20,26,26,26,34,34,34,42,42,42,12,12,12,0 } - }; - unsigned tables, scfsi = 0; - int main_data_begin, part_23_sum = 0; - int gr_count = MA_DR_MP3_HDR_IS_MONO(hdr) ? 1 : 2; - int sr_idx = MA_DR_MP3_HDR_GET_MY_SAMPLE_RATE(hdr); sr_idx -= (sr_idx != 0); - if (MA_DR_MP3_HDR_TEST_MPEG1(hdr)) - { - gr_count *= 2; - main_data_begin = ma_dr_mp3_bs_get_bits(bs, 9); - scfsi = ma_dr_mp3_bs_get_bits(bs, 7 + gr_count); - } else - { - main_data_begin = ma_dr_mp3_bs_get_bits(bs, 8 + gr_count) >> gr_count; - } - do - { - if (MA_DR_MP3_HDR_IS_MONO(hdr)) - { - scfsi <<= 4; - } - gr->part_23_length = (ma_uint16)ma_dr_mp3_bs_get_bits(bs, 12); - part_23_sum += gr->part_23_length; - gr->big_values = (ma_uint16)ma_dr_mp3_bs_get_bits(bs, 9); - if (gr->big_values > 288) - { - return -1; - } - gr->global_gain = (ma_uint8)ma_dr_mp3_bs_get_bits(bs, 8); - gr->scalefac_compress = (ma_uint16)ma_dr_mp3_bs_get_bits(bs, MA_DR_MP3_HDR_TEST_MPEG1(hdr) ? 4 : 9); - gr->sfbtab = g_scf_long[sr_idx]; - gr->n_long_sfb = 22; - gr->n_short_sfb = 0; - if (ma_dr_mp3_bs_get_bits(bs, 1)) - { - gr->block_type = (ma_uint8)ma_dr_mp3_bs_get_bits(bs, 2); - if (!gr->block_type) - { - return -1; - } - gr->mixed_block_flag = (ma_uint8)ma_dr_mp3_bs_get_bits(bs, 1); - gr->region_count[0] = 7; - gr->region_count[1] = 255; - if (gr->block_type == MA_DR_MP3_SHORT_BLOCK_TYPE) - { - scfsi &= 0x0F0F; - if (!gr->mixed_block_flag) - { - gr->region_count[0] = 8; - gr->sfbtab = g_scf_short[sr_idx]; - gr->n_long_sfb = 0; - gr->n_short_sfb = 39; - } else - { - gr->sfbtab = g_scf_mixed[sr_idx]; - gr->n_long_sfb = MA_DR_MP3_HDR_TEST_MPEG1(hdr) ? 8 : 6; - gr->n_short_sfb = 30; - } - } - tables = ma_dr_mp3_bs_get_bits(bs, 10); - tables <<= 5; - gr->subblock_gain[0] = (ma_uint8)ma_dr_mp3_bs_get_bits(bs, 3); - gr->subblock_gain[1] = (ma_uint8)ma_dr_mp3_bs_get_bits(bs, 3); - gr->subblock_gain[2] = (ma_uint8)ma_dr_mp3_bs_get_bits(bs, 3); - } else - { - gr->block_type = 0; - gr->mixed_block_flag = 0; - tables = ma_dr_mp3_bs_get_bits(bs, 15); - gr->region_count[0] = (ma_uint8)ma_dr_mp3_bs_get_bits(bs, 4); - gr->region_count[1] = (ma_uint8)ma_dr_mp3_bs_get_bits(bs, 3); - gr->region_count[2] = 255; - } - gr->table_select[0] = (ma_uint8)(tables >> 10); - gr->table_select[1] = (ma_uint8)((tables >> 5) & 31); - gr->table_select[2] = (ma_uint8)((tables) & 31); - gr->preflag = (ma_uint8)(MA_DR_MP3_HDR_TEST_MPEG1(hdr) ? ma_dr_mp3_bs_get_bits(bs, 1) : (gr->scalefac_compress >= 500)); - gr->scalefac_scale = (ma_uint8)ma_dr_mp3_bs_get_bits(bs, 1); - gr->count1_table = (ma_uint8)ma_dr_mp3_bs_get_bits(bs, 1); - gr->scfsi = (ma_uint8)((scfsi >> 12) & 15); - scfsi <<= 4; - gr++; - } while(--gr_count); - if (part_23_sum + bs->pos > bs->limit + main_data_begin*8) - { - return -1; - } - return main_data_begin; -} -static void ma_dr_mp3_L3_read_scalefactors(ma_uint8 *scf, ma_uint8 *ist_pos, const ma_uint8 *scf_size, const ma_uint8 *scf_count, ma_dr_mp3_bs *bitbuf, int scfsi) -{ - int i, k; - for (i = 0; i < 4 && scf_count[i]; i++, scfsi *= 2) - { - int cnt = scf_count[i]; - if (scfsi & 8) - { - MA_DR_MP3_COPY_MEMORY(scf, ist_pos, cnt); - } else - { - int bits = scf_size[i]; - if (!bits) - { - MA_DR_MP3_ZERO_MEMORY(scf, cnt); - MA_DR_MP3_ZERO_MEMORY(ist_pos, cnt); - } else - { - int max_scf = (scfsi < 0) ? (1 << bits) - 1 : -1; - for (k = 0; k < cnt; k++) - { - int s = ma_dr_mp3_bs_get_bits(bitbuf, bits); - ist_pos[k] = (ma_uint8)(s == max_scf ? -1 : s); - scf[k] = (ma_uint8)s; - } - } - } - ist_pos += cnt; - scf += cnt; - } - scf[0] = scf[1] = scf[2] = 0; -} -static float ma_dr_mp3_L3_ldexp_q2(float y, int exp_q2) -{ - static const float g_expfrac[4] = { 9.31322575e-10f,7.83145814e-10f,6.58544508e-10f,5.53767716e-10f }; - int e; - do - { - e = MA_DR_MP3_MIN(30*4, exp_q2); - y *= g_expfrac[e & 3]*(1 << 30 >> (e >> 2)); - } while ((exp_q2 -= e) > 0); - return y; -} -static void ma_dr_mp3_L3_decode_scalefactors(const ma_uint8 *hdr, ma_uint8 *ist_pos, ma_dr_mp3_bs *bs, const ma_dr_mp3_L3_gr_info *gr, float *scf, int ch) -{ - static const ma_uint8 g_scf_partitions[3][28] = { - { 6,5,5, 5,6,5,5,5,6,5, 7,3,11,10,0,0, 7, 7, 7,0, 6, 6,6,3, 8, 8,5,0 }, - { 8,9,6,12,6,9,9,9,6,9,12,6,15,18,0,0, 6,15,12,0, 6,12,9,6, 6,18,9,0 }, - { 9,9,6,12,9,9,9,9,9,9,12,6,18,18,0,0,12,12,12,0,12, 9,9,6,15,12,9,0 } - }; - const ma_uint8 *scf_partition = g_scf_partitions[!!gr->n_short_sfb + !gr->n_long_sfb]; - ma_uint8 scf_size[4], iscf[40]; - int i, scf_shift = gr->scalefac_scale + 1, gain_exp, scfsi = gr->scfsi; - float gain; - if (MA_DR_MP3_HDR_TEST_MPEG1(hdr)) - { - static const ma_uint8 g_scfc_decode[16] = { 0,1,2,3, 12,5,6,7, 9,10,11,13, 14,15,18,19 }; - int part = g_scfc_decode[gr->scalefac_compress]; - scf_size[1] = scf_size[0] = (ma_uint8)(part >> 2); - scf_size[3] = scf_size[2] = (ma_uint8)(part & 3); - } else - { - static const ma_uint8 g_mod[6*4] = { 5,5,4,4,5,5,4,1,4,3,1,1,5,6,6,1,4,4,4,1,4,3,1,1 }; - int k, modprod, sfc, ist = MA_DR_MP3_HDR_TEST_I_STEREO(hdr) && ch; - sfc = gr->scalefac_compress >> ist; - for (k = ist*3*4; sfc >= 0; sfc -= modprod, k += 4) - { - for (modprod = 1, i = 3; i >= 0; i--) - { - scf_size[i] = (ma_uint8)(sfc / modprod % g_mod[k + i]); - modprod *= g_mod[k + i]; - } - } - scf_partition += k; - scfsi = -16; - } - ma_dr_mp3_L3_read_scalefactors(iscf, ist_pos, scf_size, scf_partition, bs, scfsi); - if (gr->n_short_sfb) - { - int sh = 3 - scf_shift; - for (i = 0; i < gr->n_short_sfb; i += 3) - { - iscf[gr->n_long_sfb + i + 0] = (ma_uint8)(iscf[gr->n_long_sfb + i + 0] + (gr->subblock_gain[0] << sh)); - iscf[gr->n_long_sfb + i + 1] = (ma_uint8)(iscf[gr->n_long_sfb + i + 1] + (gr->subblock_gain[1] << sh)); - iscf[gr->n_long_sfb + i + 2] = (ma_uint8)(iscf[gr->n_long_sfb + i + 2] + (gr->subblock_gain[2] << sh)); - } - } else if (gr->preflag) - { - static const ma_uint8 g_preamp[10] = { 1,1,1,1,2,2,3,3,3,2 }; - for (i = 0; i < 10; i++) - { - iscf[11 + i] = (ma_uint8)(iscf[11 + i] + g_preamp[i]); - } - } - gain_exp = gr->global_gain + MA_DR_MP3_BITS_DEQUANTIZER_OUT*4 - 210 - (MA_DR_MP3_HDR_IS_MS_STEREO(hdr) ? 2 : 0); - gain = ma_dr_mp3_L3_ldexp_q2(1 << (MA_DR_MP3_MAX_SCFI/4), MA_DR_MP3_MAX_SCFI - gain_exp); - for (i = 0; i < (int)(gr->n_long_sfb + gr->n_short_sfb); i++) - { - scf[i] = ma_dr_mp3_L3_ldexp_q2(gain, iscf[i] << scf_shift); - } -} -static const float g_ma_dr_mp3_pow43[129 + 16] = { - 0,-1,-2.519842f,-4.326749f,-6.349604f,-8.549880f,-10.902724f,-13.390518f,-16.000000f,-18.720754f,-21.544347f,-24.463781f,-27.473142f,-30.567351f,-33.741992f,-36.993181f, - 0,1,2.519842f,4.326749f,6.349604f,8.549880f,10.902724f,13.390518f,16.000000f,18.720754f,21.544347f,24.463781f,27.473142f,30.567351f,33.741992f,36.993181f,40.317474f,43.711787f,47.173345f,50.699631f,54.288352f,57.937408f,61.644865f,65.408941f,69.227979f,73.100443f,77.024898f,81.000000f,85.024491f,89.097188f,93.216975f,97.382800f,101.593667f,105.848633f,110.146801f,114.487321f,118.869381f,123.292209f,127.755065f,132.257246f,136.798076f,141.376907f,145.993119f,150.646117f,155.335327f,160.060199f,164.820202f,169.614826f,174.443577f,179.305980f,184.201575f,189.129918f,194.090580f,199.083145f,204.107210f,209.162385f,214.248292f,219.364564f,224.510845f,229.686789f,234.892058f,240.126328f,245.389280f,250.680604f,256.000000f,261.347174f,266.721841f,272.123723f,277.552547f,283.008049f,288.489971f,293.998060f,299.532071f,305.091761f,310.676898f,316.287249f,321.922592f,327.582707f,333.267377f,338.976394f,344.709550f,350.466646f,356.247482f,362.051866f,367.879608f,373.730522f,379.604427f,385.501143f,391.420496f,397.362314f,403.326427f,409.312672f,415.320884f,421.350905f,427.402579f,433.475750f,439.570269f,445.685987f,451.822757f,457.980436f,464.158883f,470.357960f,476.577530f,482.817459f,489.077615f,495.357868f,501.658090f,507.978156f,514.317941f,520.677324f,527.056184f,533.454404f,539.871867f,546.308458f,552.764065f,559.238575f,565.731879f,572.243870f,578.774440f,585.323483f,591.890898f,598.476581f,605.080431f,611.702349f,618.342238f,625.000000f,631.675540f,638.368763f,645.079578f -}; -static float ma_dr_mp3_L3_pow_43(int x) -{ - float frac; - int sign, mult = 256; - if (x < 129) - { - return g_ma_dr_mp3_pow43[16 + x]; - } - if (x < 1024) - { - mult = 16; - x <<= 3; - } - sign = 2*x & 64; - frac = (float)((x & 63) - sign) / ((x & ~63) + sign); - return g_ma_dr_mp3_pow43[16 + ((x + sign) >> 6)]*(1.f + frac*((4.f/3) + frac*(2.f/9)))*mult; -} -static void ma_dr_mp3_L3_huffman(float *dst, ma_dr_mp3_bs *bs, const ma_dr_mp3_L3_gr_info *gr_info, const float *scf, int layer3gr_limit) -{ - static const ma_int16 tabs[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 785,785,785,785,784,784,784,784,513,513,513,513,513,513,513,513,256,256,256,256,256,256,256,256,256,256,256,256,256,256,256,256, - -255,1313,1298,1282,785,785,785,785,784,784,784,784,769,769,769,769,256,256,256,256,256,256,256,256,256,256,256,256,256,256,256,256,290,288, - -255,1313,1298,1282,769,769,769,769,529,529,529,529,529,529,529,529,528,528,528,528,528,528,528,528,512,512,512,512,512,512,512,512,290,288, - -253,-318,-351,-367,785,785,785,785,784,784,784,784,769,769,769,769,256,256,256,256,256,256,256,256,256,256,256,256,256,256,256,256,819,818,547,547,275,275,275,275,561,560,515,546,289,274,288,258, - -254,-287,1329,1299,1314,1312,1057,1057,1042,1042,1026,1026,784,784,784,784,529,529,529,529,529,529,529,529,769,769,769,769,768,768,768,768,563,560,306,306,291,259, - -252,-413,-477,-542,1298,-575,1041,1041,784,784,784,784,769,769,769,769,256,256,256,256,256,256,256,256,256,256,256,256,256,256,256,256,-383,-399,1107,1092,1106,1061,849,849,789,789,1104,1091,773,773,1076,1075,341,340,325,309,834,804,577,577,532,532,516,516,832,818,803,816,561,561,531,531,515,546,289,289,288,258, - -252,-429,-493,-559,1057,1057,1042,1042,529,529,529,529,529,529,529,529,784,784,784,784,769,769,769,769,512,512,512,512,512,512,512,512,-382,1077,-415,1106,1061,1104,849,849,789,789,1091,1076,1029,1075,834,834,597,581,340,340,339,324,804,833,532,532,832,772,818,803,817,787,816,771,290,290,290,290,288,258, - -253,-349,-414,-447,-463,1329,1299,-479,1314,1312,1057,1057,1042,1042,1026,1026,785,785,785,785,784,784,784,784,769,769,769,769,768,768,768,768,-319,851,821,-335,836,850,805,849,341,340,325,336,533,533,579,579,564,564,773,832,578,548,563,516,321,276,306,291,304,259, - -251,-572,-733,-830,-863,-879,1041,1041,784,784,784,784,769,769,769,769,256,256,256,256,256,256,256,256,256,256,256,256,256,256,256,256,-511,-527,-543,1396,1351,1381,1366,1395,1335,1380,-559,1334,1138,1138,1063,1063,1350,1392,1031,1031,1062,1062,1364,1363,1120,1120,1333,1348,881,881,881,881,375,374,359,373,343,358,341,325,791,791,1123,1122,-703,1105,1045,-719,865,865,790,790,774,774,1104,1029,338,293,323,308,-799,-815,833,788,772,818,803,816,322,292,307,320,561,531,515,546,289,274,288,258, - -251,-525,-605,-685,-765,-831,-846,1298,1057,1057,1312,1282,785,785,785,785,784,784,784,784,769,769,769,769,512,512,512,512,512,512,512,512,1399,1398,1383,1367,1382,1396,1351,-511,1381,1366,1139,1139,1079,1079,1124,1124,1364,1349,1363,1333,882,882,882,882,807,807,807,807,1094,1094,1136,1136,373,341,535,535,881,775,867,822,774,-591,324,338,-671,849,550,550,866,864,609,609,293,336,534,534,789,835,773,-751,834,804,308,307,833,788,832,772,562,562,547,547,305,275,560,515,290,290, - -252,-397,-477,-557,-622,-653,-719,-735,-750,1329,1299,1314,1057,1057,1042,1042,1312,1282,1024,1024,785,785,785,785,784,784,784,784,769,769,769,769,-383,1127,1141,1111,1126,1140,1095,1110,869,869,883,883,1079,1109,882,882,375,374,807,868,838,881,791,-463,867,822,368,263,852,837,836,-543,610,610,550,550,352,336,534,534,865,774,851,821,850,805,593,533,579,564,773,832,578,578,548,548,577,577,307,276,306,291,516,560,259,259, - -250,-2107,-2507,-2764,-2909,-2974,-3007,-3023,1041,1041,1040,1040,769,769,769,769,256,256,256,256,256,256,256,256,256,256,256,256,256,256,256,256,-767,-1052,-1213,-1277,-1358,-1405,-1469,-1535,-1550,-1582,-1614,-1647,-1662,-1694,-1726,-1759,-1774,-1807,-1822,-1854,-1886,1565,-1919,-1935,-1951,-1967,1731,1730,1580,1717,-1983,1729,1564,-1999,1548,-2015,-2031,1715,1595,-2047,1714,-2063,1610,-2079,1609,-2095,1323,1323,1457,1457,1307,1307,1712,1547,1641,1700,1699,1594,1685,1625,1442,1442,1322,1322,-780,-973,-910,1279,1278,1277,1262,1276,1261,1275,1215,1260,1229,-959,974,974,989,989,-943,735,478,478,495,463,506,414,-1039,1003,958,1017,927,942,987,957,431,476,1272,1167,1228,-1183,1256,-1199,895,895,941,941,1242,1227,1212,1135,1014,1014,490,489,503,487,910,1013,985,925,863,894,970,955,1012,847,-1343,831,755,755,984,909,428,366,754,559,-1391,752,486,457,924,997,698,698,983,893,740,740,908,877,739,739,667,667,953,938,497,287,271,271,683,606,590,712,726,574,302,302,738,736,481,286,526,725,605,711,636,724,696,651,589,681,666,710,364,467,573,695,466,466,301,465,379,379,709,604,665,679,316,316,634,633,436,436,464,269,424,394,452,332,438,363,347,408,393,448,331,422,362,407,392,421,346,406,391,376,375,359,1441,1306,-2367,1290,-2383,1337,-2399,-2415,1426,1321,-2431,1411,1336,-2447,-2463,-2479,1169,1169,1049,1049,1424,1289,1412,1352,1319,-2495,1154,1154,1064,1064,1153,1153,416,390,360,404,403,389,344,374,373,343,358,372,327,357,342,311,356,326,1395,1394,1137,1137,1047,1047,1365,1392,1287,1379,1334,1364,1349,1378,1318,1363,792,792,792,792,1152,1152,1032,1032,1121,1121,1046,1046,1120,1120,1030,1030,-2895,1106,1061,1104,849,849,789,789,1091,1076,1029,1090,1060,1075,833,833,309,324,532,532,832,772,818,803,561,561,531,560,515,546,289,274,288,258, - -250,-1179,-1579,-1836,-1996,-2124,-2253,-2333,-2413,-2477,-2542,-2574,-2607,-2622,-2655,1314,1313,1298,1312,1282,785,785,785,785,1040,1040,1025,1025,768,768,768,768,-766,-798,-830,-862,-895,-911,-927,-943,-959,-975,-991,-1007,-1023,-1039,-1055,-1070,1724,1647,-1103,-1119,1631,1767,1662,1738,1708,1723,-1135,1780,1615,1779,1599,1677,1646,1778,1583,-1151,1777,1567,1737,1692,1765,1722,1707,1630,1751,1661,1764,1614,1736,1676,1763,1750,1645,1598,1721,1691,1762,1706,1582,1761,1566,-1167,1749,1629,767,766,751,765,494,494,735,764,719,749,734,763,447,447,748,718,477,506,431,491,446,476,461,505,415,430,475,445,504,399,460,489,414,503,383,474,429,459,502,502,746,752,488,398,501,473,413,472,486,271,480,270,-1439,-1455,1357,-1471,-1487,-1503,1341,1325,-1519,1489,1463,1403,1309,-1535,1372,1448,1418,1476,1356,1462,1387,-1551,1475,1340,1447,1402,1386,-1567,1068,1068,1474,1461,455,380,468,440,395,425,410,454,364,467,466,464,453,269,409,448,268,432,1371,1473,1432,1417,1308,1460,1355,1446,1459,1431,1083,1083,1401,1416,1458,1445,1067,1067,1370,1457,1051,1051,1291,1430,1385,1444,1354,1415,1400,1443,1082,1082,1173,1113,1186,1066,1185,1050,-1967,1158,1128,1172,1097,1171,1081,-1983,1157,1112,416,266,375,400,1170,1142,1127,1065,793,793,1169,1033,1156,1096,1141,1111,1155,1080,1126,1140,898,898,808,808,897,897,792,792,1095,1152,1032,1125,1110,1139,1079,1124,882,807,838,881,853,791,-2319,867,368,263,822,852,837,866,806,865,-2399,851,352,262,534,534,821,836,594,594,549,549,593,593,533,533,848,773,579,579,564,578,548,563,276,276,577,576,306,291,516,560,305,305,275,259, - -251,-892,-2058,-2620,-2828,-2957,-3023,-3039,1041,1041,1040,1040,769,769,769,769,256,256,256,256,256,256,256,256,256,256,256,256,256,256,256,256,-511,-527,-543,-559,1530,-575,-591,1528,1527,1407,1526,1391,1023,1023,1023,1023,1525,1375,1268,1268,1103,1103,1087,1087,1039,1039,1523,-604,815,815,815,815,510,495,509,479,508,463,507,447,431,505,415,399,-734,-782,1262,-815,1259,1244,-831,1258,1228,-847,-863,1196,-879,1253,987,987,748,-767,493,493,462,477,414,414,686,669,478,446,461,445,474,429,487,458,412,471,1266,1264,1009,1009,799,799,-1019,-1276,-1452,-1581,-1677,-1757,-1821,-1886,-1933,-1997,1257,1257,1483,1468,1512,1422,1497,1406,1467,1496,1421,1510,1134,1134,1225,1225,1466,1451,1374,1405,1252,1252,1358,1480,1164,1164,1251,1251,1238,1238,1389,1465,-1407,1054,1101,-1423,1207,-1439,830,830,1248,1038,1237,1117,1223,1148,1236,1208,411,426,395,410,379,269,1193,1222,1132,1235,1221,1116,976,976,1192,1162,1177,1220,1131,1191,963,963,-1647,961,780,-1663,558,558,994,993,437,408,393,407,829,978,813,797,947,-1743,721,721,377,392,844,950,828,890,706,706,812,859,796,960,948,843,934,874,571,571,-1919,690,555,689,421,346,539,539,944,779,918,873,932,842,903,888,570,570,931,917,674,674,-2575,1562,-2591,1609,-2607,1654,1322,1322,1441,1441,1696,1546,1683,1593,1669,1624,1426,1426,1321,1321,1639,1680,1425,1425,1305,1305,1545,1668,1608,1623,1667,1592,1638,1666,1320,1320,1652,1607,1409,1409,1304,1304,1288,1288,1664,1637,1395,1395,1335,1335,1622,1636,1394,1394,1319,1319,1606,1621,1392,1392,1137,1137,1137,1137,345,390,360,375,404,373,1047,-2751,-2767,-2783,1062,1121,1046,-2799,1077,-2815,1106,1061,789,789,1105,1104,263,355,310,340,325,354,352,262,339,324,1091,1076,1029,1090,1060,1075,833,833,788,788,1088,1028,818,818,803,803,561,561,531,531,816,771,546,546,289,274,288,258, - -253,-317,-381,-446,-478,-509,1279,1279,-811,-1179,-1451,-1756,-1900,-2028,-2189,-2253,-2333,-2414,-2445,-2511,-2526,1313,1298,-2559,1041,1041,1040,1040,1025,1025,1024,1024,1022,1007,1021,991,1020,975,1019,959,687,687,1018,1017,671,671,655,655,1016,1015,639,639,758,758,623,623,757,607,756,591,755,575,754,559,543,543,1009,783,-575,-621,-685,-749,496,-590,750,749,734,748,974,989,1003,958,988,973,1002,942,987,957,972,1001,926,986,941,971,956,1000,910,985,925,999,894,970,-1071,-1087,-1102,1390,-1135,1436,1509,1451,1374,-1151,1405,1358,1480,1420,-1167,1507,1494,1389,1342,1465,1435,1450,1326,1505,1310,1493,1373,1479,1404,1492,1464,1419,428,443,472,397,736,526,464,464,486,457,442,471,484,482,1357,1449,1434,1478,1388,1491,1341,1490,1325,1489,1463,1403,1309,1477,1372,1448,1418,1433,1476,1356,1462,1387,-1439,1475,1340,1447,1402,1474,1324,1461,1371,1473,269,448,1432,1417,1308,1460,-1711,1459,-1727,1441,1099,1099,1446,1386,1431,1401,-1743,1289,1083,1083,1160,1160,1458,1445,1067,1067,1370,1457,1307,1430,1129,1129,1098,1098,268,432,267,416,266,400,-1887,1144,1187,1082,1173,1113,1186,1066,1050,1158,1128,1143,1172,1097,1171,1081,420,391,1157,1112,1170,1142,1127,1065,1169,1049,1156,1096,1141,1111,1155,1080,1126,1154,1064,1153,1140,1095,1048,-2159,1125,1110,1137,-2175,823,823,1139,1138,807,807,384,264,368,263,868,838,853,791,867,822,852,837,866,806,865,790,-2319,851,821,836,352,262,850,805,849,-2399,533,533,835,820,336,261,578,548,563,577,532,532,832,772,562,562,547,547,305,275,560,515,290,290,288,258 }; - static const ma_uint8 tab32[] = { 130,162,193,209,44,28,76,140,9,9,9,9,9,9,9,9,190,254,222,238,126,94,157,157,109,61,173,205}; - static const ma_uint8 tab33[] = { 252,236,220,204,188,172,156,140,124,108,92,76,60,44,28,12 }; - static const ma_int16 tabindex[2*16] = { 0,32,64,98,0,132,180,218,292,364,426,538,648,746,0,1126,1460,1460,1460,1460,1460,1460,1460,1460,1842,1842,1842,1842,1842,1842,1842,1842 }; - static const ma_uint8 g_linbits[] = { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,2,3,4,6,8,10,13,4,5,6,7,8,9,11,13 }; -#define MA_DR_MP3_PEEK_BITS(n) (bs_cache >> (32 - (n))) -#define MA_DR_MP3_FLUSH_BITS(n) { bs_cache <<= (n); bs_sh += (n); } -#define MA_DR_MP3_CHECK_BITS while (bs_sh >= 0) { bs_cache |= (ma_uint32)*bs_next_ptr++ << bs_sh; bs_sh -= 8; } -#define MA_DR_MP3_BSPOS ((bs_next_ptr - bs->buf)*8 - 24 + bs_sh) - float one = 0.0f; - int ireg = 0, big_val_cnt = gr_info->big_values; - const ma_uint8 *sfb = gr_info->sfbtab; - const ma_uint8 *bs_next_ptr = bs->buf + bs->pos/8; - ma_uint32 bs_cache = (((bs_next_ptr[0]*256u + bs_next_ptr[1])*256u + bs_next_ptr[2])*256u + bs_next_ptr[3]) << (bs->pos & 7); - int pairs_to_decode, np, bs_sh = (bs->pos & 7) - 8; - bs_next_ptr += 4; - while (big_val_cnt > 0) - { - int tab_num = gr_info->table_select[ireg]; - int sfb_cnt = gr_info->region_count[ireg++]; - const ma_int16 *codebook = tabs + tabindex[tab_num]; - int linbits = g_linbits[tab_num]; - if (linbits) - { - do - { - np = *sfb++ / 2; - pairs_to_decode = MA_DR_MP3_MIN(big_val_cnt, np); - one = *scf++; - do - { - int j, w = 5; - int leaf = codebook[MA_DR_MP3_PEEK_BITS(w)]; - while (leaf < 0) - { - MA_DR_MP3_FLUSH_BITS(w); - w = leaf & 7; - leaf = codebook[MA_DR_MP3_PEEK_BITS(w) - (leaf >> 3)]; - } - MA_DR_MP3_FLUSH_BITS(leaf >> 8); - for (j = 0; j < 2; j++, dst++, leaf >>= 4) - { - int lsb = leaf & 0x0F; - if (lsb == 15) - { - lsb += MA_DR_MP3_PEEK_BITS(linbits); - MA_DR_MP3_FLUSH_BITS(linbits); - MA_DR_MP3_CHECK_BITS; - *dst = one*ma_dr_mp3_L3_pow_43(lsb)*((ma_int32)bs_cache < 0 ? -1: 1); - } else - { - *dst = g_ma_dr_mp3_pow43[16 + lsb - 16*(bs_cache >> 31)]*one; - } - MA_DR_MP3_FLUSH_BITS(lsb ? 1 : 0); - } - MA_DR_MP3_CHECK_BITS; - } while (--pairs_to_decode); - } while ((big_val_cnt -= np) > 0 && --sfb_cnt >= 0); - } else - { - do - { - np = *sfb++ / 2; - pairs_to_decode = MA_DR_MP3_MIN(big_val_cnt, np); - one = *scf++; - do - { - int j, w = 5; - int leaf = codebook[MA_DR_MP3_PEEK_BITS(w)]; - while (leaf < 0) - { - MA_DR_MP3_FLUSH_BITS(w); - w = leaf & 7; - leaf = codebook[MA_DR_MP3_PEEK_BITS(w) - (leaf >> 3)]; - } - MA_DR_MP3_FLUSH_BITS(leaf >> 8); - for (j = 0; j < 2; j++, dst++, leaf >>= 4) - { - int lsb = leaf & 0x0F; - *dst = g_ma_dr_mp3_pow43[16 + lsb - 16*(bs_cache >> 31)]*one; - MA_DR_MP3_FLUSH_BITS(lsb ? 1 : 0); - } - MA_DR_MP3_CHECK_BITS; - } while (--pairs_to_decode); - } while ((big_val_cnt -= np) > 0 && --sfb_cnt >= 0); - } - } - for (np = 1 - big_val_cnt;; dst += 4) - { - const ma_uint8 *codebook_count1 = (gr_info->count1_table) ? tab33 : tab32; - int leaf = codebook_count1[MA_DR_MP3_PEEK_BITS(4)]; - if (!(leaf & 8)) - { - leaf = codebook_count1[(leaf >> 3) + (bs_cache << 4 >> (32 - (leaf & 3)))]; - } - MA_DR_MP3_FLUSH_BITS(leaf & 7); - if (MA_DR_MP3_BSPOS > layer3gr_limit) - { - break; - } -#define MA_DR_MP3_RELOAD_SCALEFACTOR if (!--np) { np = *sfb++/2; if (!np) break; one = *scf++; } -#define MA_DR_MP3_DEQ_COUNT1(s) if (leaf & (128 >> s)) { dst[s] = ((ma_int32)bs_cache < 0) ? -one : one; MA_DR_MP3_FLUSH_BITS(1) } - MA_DR_MP3_RELOAD_SCALEFACTOR; - MA_DR_MP3_DEQ_COUNT1(0); - MA_DR_MP3_DEQ_COUNT1(1); - MA_DR_MP3_RELOAD_SCALEFACTOR; - MA_DR_MP3_DEQ_COUNT1(2); - MA_DR_MP3_DEQ_COUNT1(3); - MA_DR_MP3_CHECK_BITS; - } - bs->pos = layer3gr_limit; -} -static void ma_dr_mp3_L3_midside_stereo(float *left, int n) -{ - int i = 0; - float *right = left + 576; -#if MA_DR_MP3_HAVE_SIMD - if (ma_dr_mp3_have_simd()) - { - for (; i < n - 3; i += 4) - { - ma_dr_mp3_f4 vl = MA_DR_MP3_VLD(left + i); - ma_dr_mp3_f4 vr = MA_DR_MP3_VLD(right + i); - MA_DR_MP3_VSTORE(left + i, MA_DR_MP3_VADD(vl, vr)); - MA_DR_MP3_VSTORE(right + i, MA_DR_MP3_VSUB(vl, vr)); - } -#ifdef __GNUC__ - if (__builtin_constant_p(n % 4 == 0) && n % 4 == 0) - return; -#endif - } -#endif - for (; i < n; i++) - { - float a = left[i]; - float b = right[i]; - left[i] = a + b; - right[i] = a - b; - } -} -static void ma_dr_mp3_L3_intensity_stereo_band(float *left, int n, float kl, float kr) -{ - int i; - for (i = 0; i < n; i++) - { - left[i + 576] = left[i]*kr; - left[i] = left[i]*kl; - } -} -static void ma_dr_mp3_L3_stereo_top_band(const float *right, const ma_uint8 *sfb, int nbands, int max_band[3]) -{ - int i, k; - max_band[0] = max_band[1] = max_band[2] = -1; - for (i = 0; i < nbands; i++) - { - for (k = 0; k < sfb[i]; k += 2) - { - if (right[k] != 0 || right[k + 1] != 0) - { - max_band[i % 3] = i; - break; - } - } - right += sfb[i]; - } -} -static void ma_dr_mp3_L3_stereo_process(float *left, const ma_uint8 *ist_pos, const ma_uint8 *sfb, const ma_uint8 *hdr, int max_band[3], int mpeg2_sh) -{ - static const float g_pan[7*2] = { 0,1,0.21132487f,0.78867513f,0.36602540f,0.63397460f,0.5f,0.5f,0.63397460f,0.36602540f,0.78867513f,0.21132487f,1,0 }; - unsigned i, max_pos = MA_DR_MP3_HDR_TEST_MPEG1(hdr) ? 7 : 64; - for (i = 0; sfb[i]; i++) - { - unsigned ipos = ist_pos[i]; - if ((int)i > max_band[i % 3] && ipos < max_pos) - { - float kl, kr, s = MA_DR_MP3_HDR_TEST_MS_STEREO(hdr) ? 1.41421356f : 1; - if (MA_DR_MP3_HDR_TEST_MPEG1(hdr)) - { - kl = g_pan[2*ipos]; - kr = g_pan[2*ipos + 1]; - } else - { - kl = 1; - kr = ma_dr_mp3_L3_ldexp_q2(1, (ipos + 1) >> 1 << mpeg2_sh); - if (ipos & 1) - { - kl = kr; - kr = 1; - } - } - ma_dr_mp3_L3_intensity_stereo_band(left, sfb[i], kl*s, kr*s); - } else if (MA_DR_MP3_HDR_TEST_MS_STEREO(hdr)) - { - ma_dr_mp3_L3_midside_stereo(left, sfb[i]); - } - left += sfb[i]; - } -} -static void ma_dr_mp3_L3_intensity_stereo(float *left, ma_uint8 *ist_pos, const ma_dr_mp3_L3_gr_info *gr, const ma_uint8 *hdr) -{ - int max_band[3], n_sfb = gr->n_long_sfb + gr->n_short_sfb; - int i, max_blocks = gr->n_short_sfb ? 3 : 1; - ma_dr_mp3_L3_stereo_top_band(left + 576, gr->sfbtab, n_sfb, max_band); - if (gr->n_long_sfb) - { - max_band[0] = max_band[1] = max_band[2] = MA_DR_MP3_MAX(MA_DR_MP3_MAX(max_band[0], max_band[1]), max_band[2]); - } - for (i = 0; i < max_blocks; i++) - { - int default_pos = MA_DR_MP3_HDR_TEST_MPEG1(hdr) ? 3 : 0; - int itop = n_sfb - max_blocks + i; - int prev = itop - max_blocks; - ist_pos[itop] = (ma_uint8)(max_band[i] >= prev ? default_pos : ist_pos[prev]); - } - ma_dr_mp3_L3_stereo_process(left, ist_pos, gr->sfbtab, hdr, max_band, gr[1].scalefac_compress & 1); -} -static void ma_dr_mp3_L3_reorder(float *grbuf, float *scratch, const ma_uint8 *sfb) -{ - int i, len; - float *src = grbuf, *dst = scratch; - for (;0 != (len = *sfb); sfb += 3, src += 2*len) - { - for (i = 0; i < len; i++, src++) - { - *dst++ = src[0*len]; - *dst++ = src[1*len]; - *dst++ = src[2*len]; - } - } - MA_DR_MP3_COPY_MEMORY(grbuf, scratch, (dst - scratch)*sizeof(float)); -} -static void ma_dr_mp3_L3_antialias(float *grbuf, int nbands) -{ - static const float g_aa[2][8] = { - {0.85749293f,0.88174200f,0.94962865f,0.98331459f,0.99551782f,0.99916056f,0.99989920f,0.99999316f}, - {0.51449576f,0.47173197f,0.31337745f,0.18191320f,0.09457419f,0.04096558f,0.01419856f,0.00369997f} - }; - for (; nbands > 0; nbands--, grbuf += 18) - { - int i = 0; -#if MA_DR_MP3_HAVE_SIMD - if (ma_dr_mp3_have_simd()) for (; i < 8; i += 4) - { - ma_dr_mp3_f4 vu = MA_DR_MP3_VLD(grbuf + 18 + i); - ma_dr_mp3_f4 vd = MA_DR_MP3_VLD(grbuf + 14 - i); - ma_dr_mp3_f4 vc0 = MA_DR_MP3_VLD(g_aa[0] + i); - ma_dr_mp3_f4 vc1 = MA_DR_MP3_VLD(g_aa[1] + i); - vd = MA_DR_MP3_VREV(vd); - MA_DR_MP3_VSTORE(grbuf + 18 + i, MA_DR_MP3_VSUB(MA_DR_MP3_VMUL(vu, vc0), MA_DR_MP3_VMUL(vd, vc1))); - vd = MA_DR_MP3_VADD(MA_DR_MP3_VMUL(vu, vc1), MA_DR_MP3_VMUL(vd, vc0)); - MA_DR_MP3_VSTORE(grbuf + 14 - i, MA_DR_MP3_VREV(vd)); - } -#endif -#ifndef MA_DR_MP3_ONLY_SIMD - for(; i < 8; i++) - { - float u = grbuf[18 + i]; - float d = grbuf[17 - i]; - grbuf[18 + i] = u*g_aa[0][i] - d*g_aa[1][i]; - grbuf[17 - i] = u*g_aa[1][i] + d*g_aa[0][i]; - } -#endif - } -} -static void ma_dr_mp3_L3_dct3_9(float *y) -{ - float s0, s1, s2, s3, s4, s5, s6, s7, s8, t0, t2, t4; - s0 = y[0]; s2 = y[2]; s4 = y[4]; s6 = y[6]; s8 = y[8]; - t0 = s0 + s6*0.5f; - s0 -= s6; - t4 = (s4 + s2)*0.93969262f; - t2 = (s8 + s2)*0.76604444f; - s6 = (s4 - s8)*0.17364818f; - s4 += s8 - s2; - s2 = s0 - s4*0.5f; - y[4] = s4 + s0; - s8 = t0 - t2 + s6; - s0 = t0 - t4 + t2; - s4 = t0 + t4 - s6; - s1 = y[1]; s3 = y[3]; s5 = y[5]; s7 = y[7]; - s3 *= 0.86602540f; - t0 = (s5 + s1)*0.98480775f; - t4 = (s5 - s7)*0.34202014f; - t2 = (s1 + s7)*0.64278761f; - s1 = (s1 - s5 - s7)*0.86602540f; - s5 = t0 - s3 - t2; - s7 = t4 - s3 - t0; - s3 = t4 + s3 - t2; - y[0] = s4 - s7; - y[1] = s2 + s1; - y[2] = s0 - s3; - y[3] = s8 + s5; - y[5] = s8 - s5; - y[6] = s0 + s3; - y[7] = s2 - s1; - y[8] = s4 + s7; -} -static void ma_dr_mp3_L3_imdct36(float *grbuf, float *overlap, const float *window, int nbands) -{ - int i, j; - static const float g_twid9[18] = { - 0.73727734f,0.79335334f,0.84339145f,0.88701083f,0.92387953f,0.95371695f,0.97629601f,0.99144486f,0.99904822f,0.67559021f,0.60876143f,0.53729961f,0.46174861f,0.38268343f,0.30070580f,0.21643961f,0.13052619f,0.04361938f - }; - for (j = 0; j < nbands; j++, grbuf += 18, overlap += 9) - { - float co[9], si[9]; - co[0] = -grbuf[0]; - si[0] = grbuf[17]; - for (i = 0; i < 4; i++) - { - si[8 - 2*i] = grbuf[4*i + 1] - grbuf[4*i + 2]; - co[1 + 2*i] = grbuf[4*i + 1] + grbuf[4*i + 2]; - si[7 - 2*i] = grbuf[4*i + 4] - grbuf[4*i + 3]; - co[2 + 2*i] = -(grbuf[4*i + 3] + grbuf[4*i + 4]); - } - ma_dr_mp3_L3_dct3_9(co); - ma_dr_mp3_L3_dct3_9(si); - si[1] = -si[1]; - si[3] = -si[3]; - si[5] = -si[5]; - si[7] = -si[7]; - i = 0; -#if MA_DR_MP3_HAVE_SIMD - if (ma_dr_mp3_have_simd()) for (; i < 8; i += 4) - { - ma_dr_mp3_f4 vovl = MA_DR_MP3_VLD(overlap + i); - ma_dr_mp3_f4 vc = MA_DR_MP3_VLD(co + i); - ma_dr_mp3_f4 vs = MA_DR_MP3_VLD(si + i); - ma_dr_mp3_f4 vr0 = MA_DR_MP3_VLD(g_twid9 + i); - ma_dr_mp3_f4 vr1 = MA_DR_MP3_VLD(g_twid9 + 9 + i); - ma_dr_mp3_f4 vw0 = MA_DR_MP3_VLD(window + i); - ma_dr_mp3_f4 vw1 = MA_DR_MP3_VLD(window + 9 + i); - ma_dr_mp3_f4 vsum = MA_DR_MP3_VADD(MA_DR_MP3_VMUL(vc, vr1), MA_DR_MP3_VMUL(vs, vr0)); - MA_DR_MP3_VSTORE(overlap + i, MA_DR_MP3_VSUB(MA_DR_MP3_VMUL(vc, vr0), MA_DR_MP3_VMUL(vs, vr1))); - MA_DR_MP3_VSTORE(grbuf + i, MA_DR_MP3_VSUB(MA_DR_MP3_VMUL(vovl, vw0), MA_DR_MP3_VMUL(vsum, vw1))); - vsum = MA_DR_MP3_VADD(MA_DR_MP3_VMUL(vovl, vw1), MA_DR_MP3_VMUL(vsum, vw0)); - MA_DR_MP3_VSTORE(grbuf + 14 - i, MA_DR_MP3_VREV(vsum)); - } -#endif - for (; i < 9; i++) - { - float ovl = overlap[i]; - float sum = co[i]*g_twid9[9 + i] + si[i]*g_twid9[0 + i]; - overlap[i] = co[i]*g_twid9[0 + i] - si[i]*g_twid9[9 + i]; - grbuf[i] = ovl*window[0 + i] - sum*window[9 + i]; - grbuf[17 - i] = ovl*window[9 + i] + sum*window[0 + i]; - } - } -} -static void ma_dr_mp3_L3_idct3(float x0, float x1, float x2, float *dst) -{ - float m1 = x1*0.86602540f; - float a1 = x0 - x2*0.5f; - dst[1] = x0 + x2; - dst[0] = a1 + m1; - dst[2] = a1 - m1; -} -static void ma_dr_mp3_L3_imdct12(float *x, float *dst, float *overlap) -{ - static const float g_twid3[6] = { 0.79335334f,0.92387953f,0.99144486f, 0.60876143f,0.38268343f,0.13052619f }; - float co[3], si[3]; - int i; - ma_dr_mp3_L3_idct3(-x[0], x[6] + x[3], x[12] + x[9], co); - ma_dr_mp3_L3_idct3(x[15], x[12] - x[9], x[6] - x[3], si); - si[1] = -si[1]; - for (i = 0; i < 3; i++) - { - float ovl = overlap[i]; - float sum = co[i]*g_twid3[3 + i] + si[i]*g_twid3[0 + i]; - overlap[i] = co[i]*g_twid3[0 + i] - si[i]*g_twid3[3 + i]; - dst[i] = ovl*g_twid3[2 - i] - sum*g_twid3[5 - i]; - dst[5 - i] = ovl*g_twid3[5 - i] + sum*g_twid3[2 - i]; - } -} -static void ma_dr_mp3_L3_imdct_short(float *grbuf, float *overlap, int nbands) -{ - for (;nbands > 0; nbands--, overlap += 9, grbuf += 18) - { - float tmp[18]; - MA_DR_MP3_COPY_MEMORY(tmp, grbuf, sizeof(tmp)); - MA_DR_MP3_COPY_MEMORY(grbuf, overlap, 6*sizeof(float)); - ma_dr_mp3_L3_imdct12(tmp, grbuf + 6, overlap + 6); - ma_dr_mp3_L3_imdct12(tmp + 1, grbuf + 12, overlap + 6); - ma_dr_mp3_L3_imdct12(tmp + 2, overlap, overlap + 6); - } -} -static void ma_dr_mp3_L3_change_sign(float *grbuf) -{ - int b, i; - for (b = 0, grbuf += 18; b < 32; b += 2, grbuf += 36) - for (i = 1; i < 18; i += 2) - grbuf[i] = -grbuf[i]; -} -static void ma_dr_mp3_L3_imdct_gr(float *grbuf, float *overlap, unsigned block_type, unsigned n_long_bands) -{ - static const float g_mdct_window[2][18] = { - { 0.99904822f,0.99144486f,0.97629601f,0.95371695f,0.92387953f,0.88701083f,0.84339145f,0.79335334f,0.73727734f,0.04361938f,0.13052619f,0.21643961f,0.30070580f,0.38268343f,0.46174861f,0.53729961f,0.60876143f,0.67559021f }, - { 1,1,1,1,1,1,0.99144486f,0.92387953f,0.79335334f,0,0,0,0,0,0,0.13052619f,0.38268343f,0.60876143f } - }; - if (n_long_bands) - { - ma_dr_mp3_L3_imdct36(grbuf, overlap, g_mdct_window[0], n_long_bands); - grbuf += 18*n_long_bands; - overlap += 9*n_long_bands; - } - if (block_type == MA_DR_MP3_SHORT_BLOCK_TYPE) - ma_dr_mp3_L3_imdct_short(grbuf, overlap, 32 - n_long_bands); - else - ma_dr_mp3_L3_imdct36(grbuf, overlap, g_mdct_window[block_type == MA_DR_MP3_STOP_BLOCK_TYPE], 32 - n_long_bands); -} -static void ma_dr_mp3_L3_save_reservoir(ma_dr_mp3dec *h, ma_dr_mp3dec_scratch *s) -{ - int pos = (s->bs.pos + 7)/8u; - int remains = s->bs.limit/8u - pos; - if (remains > MA_DR_MP3_MAX_BITRESERVOIR_BYTES) - { - pos += remains - MA_DR_MP3_MAX_BITRESERVOIR_BYTES; - remains = MA_DR_MP3_MAX_BITRESERVOIR_BYTES; - } - if (remains > 0) - { - MA_DR_MP3_MOVE_MEMORY(h->reserv_buf, s->maindata + pos, remains); - } - h->reserv = remains; -} -static int ma_dr_mp3_L3_restore_reservoir(ma_dr_mp3dec *h, ma_dr_mp3_bs *bs, ma_dr_mp3dec_scratch *s, int main_data_begin) -{ - int frame_bytes = (bs->limit - bs->pos)/8; - int bytes_have = MA_DR_MP3_MIN(h->reserv, main_data_begin); - MA_DR_MP3_COPY_MEMORY(s->maindata, h->reserv_buf + MA_DR_MP3_MAX(0, h->reserv - main_data_begin), MA_DR_MP3_MIN(h->reserv, main_data_begin)); - MA_DR_MP3_COPY_MEMORY(s->maindata + bytes_have, bs->buf + bs->pos/8, frame_bytes); - ma_dr_mp3_bs_init(&s->bs, s->maindata, bytes_have + frame_bytes); - return h->reserv >= main_data_begin; -} -static void ma_dr_mp3_L3_decode(ma_dr_mp3dec *h, ma_dr_mp3dec_scratch *s, ma_dr_mp3_L3_gr_info *gr_info, int nch) -{ - int ch; - for (ch = 0; ch < nch; ch++) - { - int layer3gr_limit = s->bs.pos + gr_info[ch].part_23_length; - ma_dr_mp3_L3_decode_scalefactors(h->header, s->ist_pos[ch], &s->bs, gr_info + ch, s->scf, ch); - ma_dr_mp3_L3_huffman(s->grbuf[ch], &s->bs, gr_info + ch, s->scf, layer3gr_limit); - } - if (MA_DR_MP3_HDR_TEST_I_STEREO(h->header)) - { - ma_dr_mp3_L3_intensity_stereo(s->grbuf[0], s->ist_pos[1], gr_info, h->header); - } else if (MA_DR_MP3_HDR_IS_MS_STEREO(h->header)) - { - ma_dr_mp3_L3_midside_stereo(s->grbuf[0], 576); - } - for (ch = 0; ch < nch; ch++, gr_info++) - { - int aa_bands = 31; - int n_long_bands = (gr_info->mixed_block_flag ? 2 : 0) << (int)(MA_DR_MP3_HDR_GET_MY_SAMPLE_RATE(h->header) == 2); - if (gr_info->n_short_sfb) - { - aa_bands = n_long_bands - 1; - ma_dr_mp3_L3_reorder(s->grbuf[ch] + n_long_bands*18, s->syn[0], gr_info->sfbtab + gr_info->n_long_sfb); - } - ma_dr_mp3_L3_antialias(s->grbuf[ch], aa_bands); - ma_dr_mp3_L3_imdct_gr(s->grbuf[ch], h->mdct_overlap[ch], gr_info->block_type, n_long_bands); - ma_dr_mp3_L3_change_sign(s->grbuf[ch]); - } -} -static void ma_dr_mp3d_DCT_II(float *grbuf, int n) -{ - static const float g_sec[24] = { - 10.19000816f,0.50060302f,0.50241929f,3.40760851f,0.50547093f,0.52249861f,2.05778098f,0.51544732f,0.56694406f,1.48416460f,0.53104258f,0.64682180f,1.16943991f,0.55310392f,0.78815460f,0.97256821f,0.58293498f,1.06067765f,0.83934963f,0.62250412f,1.72244716f,0.74453628f,0.67480832f,5.10114861f - }; - int i, k = 0; -#if MA_DR_MP3_HAVE_SIMD - if (ma_dr_mp3_have_simd()) for (; k < n; k += 4) - { - ma_dr_mp3_f4 t[4][8], *x; - float *y = grbuf + k; - for (x = t[0], i = 0; i < 8; i++, x++) - { - ma_dr_mp3_f4 x0 = MA_DR_MP3_VLD(&y[i*18]); - ma_dr_mp3_f4 x1 = MA_DR_MP3_VLD(&y[(15 - i)*18]); - ma_dr_mp3_f4 x2 = MA_DR_MP3_VLD(&y[(16 + i)*18]); - ma_dr_mp3_f4 x3 = MA_DR_MP3_VLD(&y[(31 - i)*18]); - ma_dr_mp3_f4 t0 = MA_DR_MP3_VADD(x0, x3); - ma_dr_mp3_f4 t1 = MA_DR_MP3_VADD(x1, x2); - ma_dr_mp3_f4 t2 = MA_DR_MP3_VMUL_S(MA_DR_MP3_VSUB(x1, x2), g_sec[3*i + 0]); - ma_dr_mp3_f4 t3 = MA_DR_MP3_VMUL_S(MA_DR_MP3_VSUB(x0, x3), g_sec[3*i + 1]); - x[0] = MA_DR_MP3_VADD(t0, t1); - x[8] = MA_DR_MP3_VMUL_S(MA_DR_MP3_VSUB(t0, t1), g_sec[3*i + 2]); - x[16] = MA_DR_MP3_VADD(t3, t2); - x[24] = MA_DR_MP3_VMUL_S(MA_DR_MP3_VSUB(t3, t2), g_sec[3*i + 2]); - } - for (x = t[0], i = 0; i < 4; i++, x += 8) - { - ma_dr_mp3_f4 x0 = x[0], x1 = x[1], x2 = x[2], x3 = x[3], x4 = x[4], x5 = x[5], x6 = x[6], x7 = x[7], xt; - xt = MA_DR_MP3_VSUB(x0, x7); x0 = MA_DR_MP3_VADD(x0, x7); - x7 = MA_DR_MP3_VSUB(x1, x6); x1 = MA_DR_MP3_VADD(x1, x6); - x6 = MA_DR_MP3_VSUB(x2, x5); x2 = MA_DR_MP3_VADD(x2, x5); - x5 = MA_DR_MP3_VSUB(x3, x4); x3 = MA_DR_MP3_VADD(x3, x4); - x4 = MA_DR_MP3_VSUB(x0, x3); x0 = MA_DR_MP3_VADD(x0, x3); - x3 = MA_DR_MP3_VSUB(x1, x2); x1 = MA_DR_MP3_VADD(x1, x2); - x[0] = MA_DR_MP3_VADD(x0, x1); - x[4] = MA_DR_MP3_VMUL_S(MA_DR_MP3_VSUB(x0, x1), 0.70710677f); - x5 = MA_DR_MP3_VADD(x5, x6); - x6 = MA_DR_MP3_VMUL_S(MA_DR_MP3_VADD(x6, x7), 0.70710677f); - x7 = MA_DR_MP3_VADD(x7, xt); - x3 = MA_DR_MP3_VMUL_S(MA_DR_MP3_VADD(x3, x4), 0.70710677f); - x5 = MA_DR_MP3_VSUB(x5, MA_DR_MP3_VMUL_S(x7, 0.198912367f)); - x7 = MA_DR_MP3_VADD(x7, MA_DR_MP3_VMUL_S(x5, 0.382683432f)); - x5 = MA_DR_MP3_VSUB(x5, MA_DR_MP3_VMUL_S(x7, 0.198912367f)); - x0 = MA_DR_MP3_VSUB(xt, x6); xt = MA_DR_MP3_VADD(xt, x6); - x[1] = MA_DR_MP3_VMUL_S(MA_DR_MP3_VADD(xt, x7), 0.50979561f); - x[2] = MA_DR_MP3_VMUL_S(MA_DR_MP3_VADD(x4, x3), 0.54119611f); - x[3] = MA_DR_MP3_VMUL_S(MA_DR_MP3_VSUB(x0, x5), 0.60134488f); - x[5] = MA_DR_MP3_VMUL_S(MA_DR_MP3_VADD(x0, x5), 0.89997619f); - x[6] = MA_DR_MP3_VMUL_S(MA_DR_MP3_VSUB(x4, x3), 1.30656302f); - x[7] = MA_DR_MP3_VMUL_S(MA_DR_MP3_VSUB(xt, x7), 2.56291556f); - } - if (k > n - 3) - { -#if MA_DR_MP3_HAVE_SSE -#define MA_DR_MP3_VSAVE2(i, v) _mm_storel_pi((__m64 *)(void*)&y[i*18], v) -#else -#define MA_DR_MP3_VSAVE2(i, v) vst1_f32((float32_t *)&y[(i)*18], vget_low_f32(v)) -#endif - for (i = 0; i < 7; i++, y += 4*18) - { - ma_dr_mp3_f4 s = MA_DR_MP3_VADD(t[3][i], t[3][i + 1]); - MA_DR_MP3_VSAVE2(0, t[0][i]); - MA_DR_MP3_VSAVE2(1, MA_DR_MP3_VADD(t[2][i], s)); - MA_DR_MP3_VSAVE2(2, MA_DR_MP3_VADD(t[1][i], t[1][i + 1])); - MA_DR_MP3_VSAVE2(3, MA_DR_MP3_VADD(t[2][1 + i], s)); - } - MA_DR_MP3_VSAVE2(0, t[0][7]); - MA_DR_MP3_VSAVE2(1, MA_DR_MP3_VADD(t[2][7], t[3][7])); - MA_DR_MP3_VSAVE2(2, t[1][7]); - MA_DR_MP3_VSAVE2(3, t[3][7]); - } else - { -#define MA_DR_MP3_VSAVE4(i, v) MA_DR_MP3_VSTORE(&y[(i)*18], v) - for (i = 0; i < 7; i++, y += 4*18) - { - ma_dr_mp3_f4 s = MA_DR_MP3_VADD(t[3][i], t[3][i + 1]); - MA_DR_MP3_VSAVE4(0, t[0][i]); - MA_DR_MP3_VSAVE4(1, MA_DR_MP3_VADD(t[2][i], s)); - MA_DR_MP3_VSAVE4(2, MA_DR_MP3_VADD(t[1][i], t[1][i + 1])); - MA_DR_MP3_VSAVE4(3, MA_DR_MP3_VADD(t[2][1 + i], s)); - } - MA_DR_MP3_VSAVE4(0, t[0][7]); - MA_DR_MP3_VSAVE4(1, MA_DR_MP3_VADD(t[2][7], t[3][7])); - MA_DR_MP3_VSAVE4(2, t[1][7]); - MA_DR_MP3_VSAVE4(3, t[3][7]); - } - } else -#endif -#ifdef MA_DR_MP3_ONLY_SIMD - {} -#else - for (; k < n; k++) - { - float t[4][8], *x, *y = grbuf + k; - for (x = t[0], i = 0; i < 8; i++, x++) - { - float x0 = y[i*18]; - float x1 = y[(15 - i)*18]; - float x2 = y[(16 + i)*18]; - float x3 = y[(31 - i)*18]; - float t0 = x0 + x3; - float t1 = x1 + x2; - float t2 = (x1 - x2)*g_sec[3*i + 0]; - float t3 = (x0 - x3)*g_sec[3*i + 1]; - x[0] = t0 + t1; - x[8] = (t0 - t1)*g_sec[3*i + 2]; - x[16] = t3 + t2; - x[24] = (t3 - t2)*g_sec[3*i + 2]; - } - for (x = t[0], i = 0; i < 4; i++, x += 8) - { - float x0 = x[0], x1 = x[1], x2 = x[2], x3 = x[3], x4 = x[4], x5 = x[5], x6 = x[6], x7 = x[7], xt; - xt = x0 - x7; x0 += x7; - x7 = x1 - x6; x1 += x6; - x6 = x2 - x5; x2 += x5; - x5 = x3 - x4; x3 += x4; - x4 = x0 - x3; x0 += x3; - x3 = x1 - x2; x1 += x2; - x[0] = x0 + x1; - x[4] = (x0 - x1)*0.70710677f; - x5 = x5 + x6; - x6 = (x6 + x7)*0.70710677f; - x7 = x7 + xt; - x3 = (x3 + x4)*0.70710677f; - x5 -= x7*0.198912367f; - x7 += x5*0.382683432f; - x5 -= x7*0.198912367f; - x0 = xt - x6; xt += x6; - x[1] = (xt + x7)*0.50979561f; - x[2] = (x4 + x3)*0.54119611f; - x[3] = (x0 - x5)*0.60134488f; - x[5] = (x0 + x5)*0.89997619f; - x[6] = (x4 - x3)*1.30656302f; - x[7] = (xt - x7)*2.56291556f; - } - for (i = 0; i < 7; i++, y += 4*18) - { - y[0*18] = t[0][i]; - y[1*18] = t[2][i] + t[3][i] + t[3][i + 1]; - y[2*18] = t[1][i] + t[1][i + 1]; - y[3*18] = t[2][i + 1] + t[3][i] + t[3][i + 1]; - } - y[0*18] = t[0][7]; - y[1*18] = t[2][7] + t[3][7]; - y[2*18] = t[1][7]; - y[3*18] = t[3][7]; - } -#endif -} -#ifndef MA_DR_MP3_FLOAT_OUTPUT -typedef ma_int16 ma_dr_mp3d_sample_t; -static ma_int16 ma_dr_mp3d_scale_pcm(float sample) -{ - ma_int16 s; -#if MA_DR_MP3_HAVE_ARMV6 - ma_int32 s32 = (ma_int32)(sample + .5f); - s32 -= (s32 < 0); - s = (ma_int16)ma_dr_mp3_clip_int16_arm(s32); -#else - if (sample >= 32766.5f) return (ma_int16) 32767; - if (sample <= -32767.5f) return (ma_int16)-32768; - s = (ma_int16)(sample + .5f); - s -= (s < 0); -#endif - return s; -} -#else -typedef float ma_dr_mp3d_sample_t; -static float ma_dr_mp3d_scale_pcm(float sample) -{ - return sample*(1.f/32768.f); -} -#endif -static void ma_dr_mp3d_synth_pair(ma_dr_mp3d_sample_t *pcm, int nch, const float *z) -{ - float a; - a = (z[14*64] - z[ 0]) * 29; - a += (z[ 1*64] + z[13*64]) * 213; - a += (z[12*64] - z[ 2*64]) * 459; - a += (z[ 3*64] + z[11*64]) * 2037; - a += (z[10*64] - z[ 4*64]) * 5153; - a += (z[ 5*64] + z[ 9*64]) * 6574; - a += (z[ 8*64] - z[ 6*64]) * 37489; - a += z[ 7*64] * 75038; - pcm[0] = ma_dr_mp3d_scale_pcm(a); - z += 2; - a = z[14*64] * 104; - a += z[12*64] * 1567; - a += z[10*64] * 9727; - a += z[ 8*64] * 64019; - a += z[ 6*64] * -9975; - a += z[ 4*64] * -45; - a += z[ 2*64] * 146; - a += z[ 0*64] * -5; - pcm[16*nch] = ma_dr_mp3d_scale_pcm(a); -} -static void ma_dr_mp3d_synth(float *xl, ma_dr_mp3d_sample_t *dstl, int nch, float *lins) -{ - int i; - float *xr = xl + 576*(nch - 1); - ma_dr_mp3d_sample_t *dstr = dstl + (nch - 1); - static const float g_win[] = { - -1,26,-31,208,218,401,-519,2063,2000,4788,-5517,7134,5959,35640,-39336,74992, - -1,24,-35,202,222,347,-581,2080,1952,4425,-5879,7640,5288,33791,-41176,74856, - -1,21,-38,196,225,294,-645,2087,1893,4063,-6237,8092,4561,31947,-43006,74630, - -1,19,-41,190,227,244,-711,2085,1822,3705,-6589,8492,3776,30112,-44821,74313, - -1,17,-45,183,228,197,-779,2075,1739,3351,-6935,8840,2935,28289,-46617,73908, - -1,16,-49,176,228,153,-848,2057,1644,3004,-7271,9139,2037,26482,-48390,73415, - -2,14,-53,169,227,111,-919,2032,1535,2663,-7597,9389,1082,24694,-50137,72835, - -2,13,-58,161,224,72,-991,2001,1414,2330,-7910,9592,70,22929,-51853,72169, - -2,11,-63,154,221,36,-1064,1962,1280,2006,-8209,9750,-998,21189,-53534,71420, - -2,10,-68,147,215,2,-1137,1919,1131,1692,-8491,9863,-2122,19478,-55178,70590, - -3,9,-73,139,208,-29,-1210,1870,970,1388,-8755,9935,-3300,17799,-56778,69679, - -3,8,-79,132,200,-57,-1283,1817,794,1095,-8998,9966,-4533,16155,-58333,68692, - -4,7,-85,125,189,-83,-1356,1759,605,814,-9219,9959,-5818,14548,-59838,67629, - -4,7,-91,117,177,-106,-1428,1698,402,545,-9416,9916,-7154,12980,-61289,66494, - -5,6,-97,111,163,-127,-1498,1634,185,288,-9585,9838,-8540,11455,-62684,65290 - }; - float *zlin = lins + 15*64; - const float *w = g_win; - zlin[4*15] = xl[18*16]; - zlin[4*15 + 1] = xr[18*16]; - zlin[4*15 + 2] = xl[0]; - zlin[4*15 + 3] = xr[0]; - zlin[4*31] = xl[1 + 18*16]; - zlin[4*31 + 1] = xr[1 + 18*16]; - zlin[4*31 + 2] = xl[1]; - zlin[4*31 + 3] = xr[1]; - ma_dr_mp3d_synth_pair(dstr, nch, lins + 4*15 + 1); - ma_dr_mp3d_synth_pair(dstr + 32*nch, nch, lins + 4*15 + 64 + 1); - ma_dr_mp3d_synth_pair(dstl, nch, lins + 4*15); - ma_dr_mp3d_synth_pair(dstl + 32*nch, nch, lins + 4*15 + 64); -#if MA_DR_MP3_HAVE_SIMD - if (ma_dr_mp3_have_simd()) for (i = 14; i >= 0; i--) - { -#define MA_DR_MP3_VLOAD(k) ma_dr_mp3_f4 w0 = MA_DR_MP3_VSET(*w++); ma_dr_mp3_f4 w1 = MA_DR_MP3_VSET(*w++); ma_dr_mp3_f4 vz = MA_DR_MP3_VLD(&zlin[4*i - 64*k]); ma_dr_mp3_f4 vy = MA_DR_MP3_VLD(&zlin[4*i - 64*(15 - k)]); -#define MA_DR_MP3_V0(k) { MA_DR_MP3_VLOAD(k) b = MA_DR_MP3_VADD(MA_DR_MP3_VMUL(vz, w1), MA_DR_MP3_VMUL(vy, w0)) ; a = MA_DR_MP3_VSUB(MA_DR_MP3_VMUL(vz, w0), MA_DR_MP3_VMUL(vy, w1)); } -#define MA_DR_MP3_V1(k) { MA_DR_MP3_VLOAD(k) b = MA_DR_MP3_VADD(b, MA_DR_MP3_VADD(MA_DR_MP3_VMUL(vz, w1), MA_DR_MP3_VMUL(vy, w0))); a = MA_DR_MP3_VADD(a, MA_DR_MP3_VSUB(MA_DR_MP3_VMUL(vz, w0), MA_DR_MP3_VMUL(vy, w1))); } -#define MA_DR_MP3_V2(k) { MA_DR_MP3_VLOAD(k) b = MA_DR_MP3_VADD(b, MA_DR_MP3_VADD(MA_DR_MP3_VMUL(vz, w1), MA_DR_MP3_VMUL(vy, w0))); a = MA_DR_MP3_VADD(a, MA_DR_MP3_VSUB(MA_DR_MP3_VMUL(vy, w1), MA_DR_MP3_VMUL(vz, w0))); } - ma_dr_mp3_f4 a, b; - zlin[4*i] = xl[18*(31 - i)]; - zlin[4*i + 1] = xr[18*(31 - i)]; - zlin[4*i + 2] = xl[1 + 18*(31 - i)]; - zlin[4*i + 3] = xr[1 + 18*(31 - i)]; - zlin[4*i + 64] = xl[1 + 18*(1 + i)]; - zlin[4*i + 64 + 1] = xr[1 + 18*(1 + i)]; - zlin[4*i - 64 + 2] = xl[18*(1 + i)]; - zlin[4*i - 64 + 3] = xr[18*(1 + i)]; - MA_DR_MP3_V0(0) MA_DR_MP3_V2(1) MA_DR_MP3_V1(2) MA_DR_MP3_V2(3) MA_DR_MP3_V1(4) MA_DR_MP3_V2(5) MA_DR_MP3_V1(6) MA_DR_MP3_V2(7) - { -#ifndef MA_DR_MP3_FLOAT_OUTPUT -#if MA_DR_MP3_HAVE_SSE - static const ma_dr_mp3_f4 g_max = { 32767.0f, 32767.0f, 32767.0f, 32767.0f }; - static const ma_dr_mp3_f4 g_min = { -32768.0f, -32768.0f, -32768.0f, -32768.0f }; - __m128i pcm8 = _mm_packs_epi32(_mm_cvtps_epi32(_mm_max_ps(_mm_min_ps(a, g_max), g_min)), - _mm_cvtps_epi32(_mm_max_ps(_mm_min_ps(b, g_max), g_min))); - dstr[(15 - i)*nch] = (ma_int16)_mm_extract_epi16(pcm8, 1); - dstr[(17 + i)*nch] = (ma_int16)_mm_extract_epi16(pcm8, 5); - dstl[(15 - i)*nch] = (ma_int16)_mm_extract_epi16(pcm8, 0); - dstl[(17 + i)*nch] = (ma_int16)_mm_extract_epi16(pcm8, 4); - dstr[(47 - i)*nch] = (ma_int16)_mm_extract_epi16(pcm8, 3); - dstr[(49 + i)*nch] = (ma_int16)_mm_extract_epi16(pcm8, 7); - dstl[(47 - i)*nch] = (ma_int16)_mm_extract_epi16(pcm8, 2); - dstl[(49 + i)*nch] = (ma_int16)_mm_extract_epi16(pcm8, 6); -#else - int16x4_t pcma, pcmb; - a = MA_DR_MP3_VADD(a, MA_DR_MP3_VSET(0.5f)); - b = MA_DR_MP3_VADD(b, MA_DR_MP3_VSET(0.5f)); - pcma = vqmovn_s32(vqaddq_s32(vcvtq_s32_f32(a), vreinterpretq_s32_u32(vcltq_f32(a, MA_DR_MP3_VSET(0))))); - pcmb = vqmovn_s32(vqaddq_s32(vcvtq_s32_f32(b), vreinterpretq_s32_u32(vcltq_f32(b, MA_DR_MP3_VSET(0))))); - vst1_lane_s16(dstr + (15 - i)*nch, pcma, 1); - vst1_lane_s16(dstr + (17 + i)*nch, pcmb, 1); - vst1_lane_s16(dstl + (15 - i)*nch, pcma, 0); - vst1_lane_s16(dstl + (17 + i)*nch, pcmb, 0); - vst1_lane_s16(dstr + (47 - i)*nch, pcma, 3); - vst1_lane_s16(dstr + (49 + i)*nch, pcmb, 3); - vst1_lane_s16(dstl + (47 - i)*nch, pcma, 2); - vst1_lane_s16(dstl + (49 + i)*nch, pcmb, 2); -#endif -#else - #if MA_DR_MP3_HAVE_SSE - static const ma_dr_mp3_f4 g_scale = { 1.0f/32768.0f, 1.0f/32768.0f, 1.0f/32768.0f, 1.0f/32768.0f }; - #else - const ma_dr_mp3_f4 g_scale = vdupq_n_f32(1.0f/32768.0f); - #endif - a = MA_DR_MP3_VMUL(a, g_scale); - b = MA_DR_MP3_VMUL(b, g_scale); -#if MA_DR_MP3_HAVE_SSE - _mm_store_ss(dstr + (15 - i)*nch, _mm_shuffle_ps(a, a, _MM_SHUFFLE(1, 1, 1, 1))); - _mm_store_ss(dstr + (17 + i)*nch, _mm_shuffle_ps(b, b, _MM_SHUFFLE(1, 1, 1, 1))); - _mm_store_ss(dstl + (15 - i)*nch, _mm_shuffle_ps(a, a, _MM_SHUFFLE(0, 0, 0, 0))); - _mm_store_ss(dstl + (17 + i)*nch, _mm_shuffle_ps(b, b, _MM_SHUFFLE(0, 0, 0, 0))); - _mm_store_ss(dstr + (47 - i)*nch, _mm_shuffle_ps(a, a, _MM_SHUFFLE(3, 3, 3, 3))); - _mm_store_ss(dstr + (49 + i)*nch, _mm_shuffle_ps(b, b, _MM_SHUFFLE(3, 3, 3, 3))); - _mm_store_ss(dstl + (47 - i)*nch, _mm_shuffle_ps(a, a, _MM_SHUFFLE(2, 2, 2, 2))); - _mm_store_ss(dstl + (49 + i)*nch, _mm_shuffle_ps(b, b, _MM_SHUFFLE(2, 2, 2, 2))); -#else - vst1q_lane_f32(dstr + (15 - i)*nch, a, 1); - vst1q_lane_f32(dstr + (17 + i)*nch, b, 1); - vst1q_lane_f32(dstl + (15 - i)*nch, a, 0); - vst1q_lane_f32(dstl + (17 + i)*nch, b, 0); - vst1q_lane_f32(dstr + (47 - i)*nch, a, 3); - vst1q_lane_f32(dstr + (49 + i)*nch, b, 3); - vst1q_lane_f32(dstl + (47 - i)*nch, a, 2); - vst1q_lane_f32(dstl + (49 + i)*nch, b, 2); -#endif -#endif - } - } else -#endif -#ifdef MA_DR_MP3_ONLY_SIMD - {} -#else - for (i = 14; i >= 0; i--) - { -#define MA_DR_MP3_LOAD(k) float w0 = *w++; float w1 = *w++; float *vz = &zlin[4*i - k*64]; float *vy = &zlin[4*i - (15 - k)*64]; -#define MA_DR_MP3_S0(k) { int j; MA_DR_MP3_LOAD(k); for (j = 0; j < 4; j++) b[j] = vz[j]*w1 + vy[j]*w0, a[j] = vz[j]*w0 - vy[j]*w1; } -#define MA_DR_MP3_S1(k) { int j; MA_DR_MP3_LOAD(k); for (j = 0; j < 4; j++) b[j] += vz[j]*w1 + vy[j]*w0, a[j] += vz[j]*w0 - vy[j]*w1; } -#define MA_DR_MP3_S2(k) { int j; MA_DR_MP3_LOAD(k); for (j = 0; j < 4; j++) b[j] += vz[j]*w1 + vy[j]*w0, a[j] += vy[j]*w1 - vz[j]*w0; } - float a[4], b[4]; - zlin[4*i] = xl[18*(31 - i)]; - zlin[4*i + 1] = xr[18*(31 - i)]; - zlin[4*i + 2] = xl[1 + 18*(31 - i)]; - zlin[4*i + 3] = xr[1 + 18*(31 - i)]; - zlin[4*(i + 16)] = xl[1 + 18*(1 + i)]; - zlin[4*(i + 16) + 1] = xr[1 + 18*(1 + i)]; - zlin[4*(i - 16) + 2] = xl[18*(1 + i)]; - zlin[4*(i - 16) + 3] = xr[18*(1 + i)]; - MA_DR_MP3_S0(0) MA_DR_MP3_S2(1) MA_DR_MP3_S1(2) MA_DR_MP3_S2(3) MA_DR_MP3_S1(4) MA_DR_MP3_S2(5) MA_DR_MP3_S1(6) MA_DR_MP3_S2(7) - dstr[(15 - i)*nch] = ma_dr_mp3d_scale_pcm(a[1]); - dstr[(17 + i)*nch] = ma_dr_mp3d_scale_pcm(b[1]); - dstl[(15 - i)*nch] = ma_dr_mp3d_scale_pcm(a[0]); - dstl[(17 + i)*nch] = ma_dr_mp3d_scale_pcm(b[0]); - dstr[(47 - i)*nch] = ma_dr_mp3d_scale_pcm(a[3]); - dstr[(49 + i)*nch] = ma_dr_mp3d_scale_pcm(b[3]); - dstl[(47 - i)*nch] = ma_dr_mp3d_scale_pcm(a[2]); - dstl[(49 + i)*nch] = ma_dr_mp3d_scale_pcm(b[2]); - } -#endif -} -static void ma_dr_mp3d_synth_granule(float *qmf_state, float *grbuf, int nbands, int nch, ma_dr_mp3d_sample_t *pcm, float *lins) -{ - int i; - for (i = 0; i < nch; i++) - { - ma_dr_mp3d_DCT_II(grbuf + 576*i, nbands); - } - MA_DR_MP3_COPY_MEMORY(lins, qmf_state, sizeof(float)*15*64); - for (i = 0; i < nbands; i += 2) - { - ma_dr_mp3d_synth(grbuf + i, pcm + 32*nch*i, nch, lins + i*64); - } -#ifndef MA_DR_MP3_NONSTANDARD_BUT_LOGICAL - if (nch == 1) - { - for (i = 0; i < 15*64; i += 2) - { - qmf_state[i] = lins[nbands*64 + i]; - } - } else -#endif - { - MA_DR_MP3_COPY_MEMORY(qmf_state, lins + nbands*64, sizeof(float)*15*64); - } -} -static int ma_dr_mp3d_match_frame(const ma_uint8 *hdr, int mp3_bytes, int frame_bytes) -{ - int i, nmatch; - for (i = 0, nmatch = 0; nmatch < MA_DR_MP3_MAX_FRAME_SYNC_MATCHES; nmatch++) - { - i += ma_dr_mp3_hdr_frame_bytes(hdr + i, frame_bytes) + ma_dr_mp3_hdr_padding(hdr + i); - if (i + MA_DR_MP3_HDR_SIZE > mp3_bytes) - return nmatch > 0; - if (!ma_dr_mp3_hdr_compare(hdr, hdr + i)) - return 0; - } - return 1; -} -static int ma_dr_mp3d_find_frame(const ma_uint8 *mp3, int mp3_bytes, int *free_format_bytes, int *ptr_frame_bytes) -{ - int i, k; - for (i = 0; i < mp3_bytes - MA_DR_MP3_HDR_SIZE; i++, mp3++) - { - if (ma_dr_mp3_hdr_valid(mp3)) - { - int frame_bytes = ma_dr_mp3_hdr_frame_bytes(mp3, *free_format_bytes); - int frame_and_padding = frame_bytes + ma_dr_mp3_hdr_padding(mp3); - for (k = MA_DR_MP3_HDR_SIZE; !frame_bytes && k < MA_DR_MP3_MAX_FREE_FORMAT_FRAME_SIZE && i + 2*k < mp3_bytes - MA_DR_MP3_HDR_SIZE; k++) - { - if (ma_dr_mp3_hdr_compare(mp3, mp3 + k)) - { - int fb = k - ma_dr_mp3_hdr_padding(mp3); - int nextfb = fb + ma_dr_mp3_hdr_padding(mp3 + k); - if (i + k + nextfb + MA_DR_MP3_HDR_SIZE > mp3_bytes || !ma_dr_mp3_hdr_compare(mp3, mp3 + k + nextfb)) - continue; - frame_and_padding = k; - frame_bytes = fb; - *free_format_bytes = fb; - } - } - if ((frame_bytes && i + frame_and_padding <= mp3_bytes && - ma_dr_mp3d_match_frame(mp3, mp3_bytes - i, frame_bytes)) || - (!i && frame_and_padding == mp3_bytes)) - { - *ptr_frame_bytes = frame_and_padding; - return i; - } - *free_format_bytes = 0; - } - } - *ptr_frame_bytes = 0; - return mp3_bytes; -} -MA_API void ma_dr_mp3dec_init(ma_dr_mp3dec *dec) -{ - dec->header[0] = 0; -} -MA_API int ma_dr_mp3dec_decode_frame(ma_dr_mp3dec *dec, const ma_uint8 *mp3, int mp3_bytes, void *pcm, ma_dr_mp3dec_frame_info *info) -{ - int i = 0, igr, frame_size = 0, success = 1; - const ma_uint8 *hdr; - ma_dr_mp3_bs bs_frame[1]; - ma_dr_mp3dec_scratch scratch; - if (mp3_bytes > 4 && dec->header[0] == 0xff && ma_dr_mp3_hdr_compare(dec->header, mp3)) - { - frame_size = ma_dr_mp3_hdr_frame_bytes(mp3, dec->free_format_bytes) + ma_dr_mp3_hdr_padding(mp3); - if (frame_size != mp3_bytes && (frame_size + MA_DR_MP3_HDR_SIZE > mp3_bytes || !ma_dr_mp3_hdr_compare(mp3, mp3 + frame_size))) - { - frame_size = 0; - } - } - if (!frame_size) - { - MA_DR_MP3_ZERO_MEMORY(dec, sizeof(ma_dr_mp3dec)); - i = ma_dr_mp3d_find_frame(mp3, mp3_bytes, &dec->free_format_bytes, &frame_size); - if (!frame_size || i + frame_size > mp3_bytes) - { - info->frame_bytes = i; - return 0; - } - } - hdr = mp3 + i; - MA_DR_MP3_COPY_MEMORY(dec->header, hdr, MA_DR_MP3_HDR_SIZE); - info->frame_bytes = i + frame_size; - info->channels = MA_DR_MP3_HDR_IS_MONO(hdr) ? 1 : 2; - info->hz = ma_dr_mp3_hdr_sample_rate_hz(hdr); - info->layer = 4 - MA_DR_MP3_HDR_GET_LAYER(hdr); - info->bitrate_kbps = ma_dr_mp3_hdr_bitrate_kbps(hdr); - ma_dr_mp3_bs_init(bs_frame, hdr + MA_DR_MP3_HDR_SIZE, frame_size - MA_DR_MP3_HDR_SIZE); - if (MA_DR_MP3_HDR_IS_CRC(hdr)) - { - ma_dr_mp3_bs_get_bits(bs_frame, 16); - } - if (info->layer == 3) - { - int main_data_begin = ma_dr_mp3_L3_read_side_info(bs_frame, scratch.gr_info, hdr); - if (main_data_begin < 0 || bs_frame->pos > bs_frame->limit) - { - ma_dr_mp3dec_init(dec); - return 0; - } - success = ma_dr_mp3_L3_restore_reservoir(dec, bs_frame, &scratch, main_data_begin); - if (success && pcm != NULL) - { - for (igr = 0; igr < (MA_DR_MP3_HDR_TEST_MPEG1(hdr) ? 2 : 1); igr++, pcm = MA_DR_MP3_OFFSET_PTR(pcm, sizeof(ma_dr_mp3d_sample_t)*576*info->channels)) - { - MA_DR_MP3_ZERO_MEMORY(scratch.grbuf[0], 576*2*sizeof(float)); - ma_dr_mp3_L3_decode(dec, &scratch, scratch.gr_info + igr*info->channels, info->channels); - ma_dr_mp3d_synth_granule(dec->qmf_state, scratch.grbuf[0], 18, info->channels, (ma_dr_mp3d_sample_t*)pcm, scratch.syn[0]); - } - } - ma_dr_mp3_L3_save_reservoir(dec, &scratch); - } else - { -#ifdef MA_DR_MP3_ONLY_MP3 - return 0; -#else - ma_dr_mp3_L12_scale_info sci[1]; - if (pcm == NULL) { - return ma_dr_mp3_hdr_frame_samples(hdr); - } - ma_dr_mp3_L12_read_scale_info(hdr, bs_frame, sci); - MA_DR_MP3_ZERO_MEMORY(scratch.grbuf[0], 576*2*sizeof(float)); - for (i = 0, igr = 0; igr < 3; igr++) - { - if (12 == (i += ma_dr_mp3_L12_dequantize_granule(scratch.grbuf[0] + i, bs_frame, sci, info->layer | 1))) - { - i = 0; - ma_dr_mp3_L12_apply_scf_384(sci, sci->scf + igr, scratch.grbuf[0]); - ma_dr_mp3d_synth_granule(dec->qmf_state, scratch.grbuf[0], 12, info->channels, (ma_dr_mp3d_sample_t*)pcm, scratch.syn[0]); - MA_DR_MP3_ZERO_MEMORY(scratch.grbuf[0], 576*2*sizeof(float)); - pcm = MA_DR_MP3_OFFSET_PTR(pcm, sizeof(ma_dr_mp3d_sample_t)*384*info->channels); - } - if (bs_frame->pos > bs_frame->limit) - { - ma_dr_mp3dec_init(dec); - return 0; - } - } -#endif - } - return success*ma_dr_mp3_hdr_frame_samples(dec->header); -} -MA_API void ma_dr_mp3dec_f32_to_s16(const float *in, ma_int16 *out, size_t num_samples) -{ - size_t i = 0; -#if MA_DR_MP3_HAVE_SIMD - size_t aligned_count = num_samples & ~7; - for(; i < aligned_count; i+=8) - { - ma_dr_mp3_f4 scale = MA_DR_MP3_VSET(32768.0f); - ma_dr_mp3_f4 a = MA_DR_MP3_VMUL(MA_DR_MP3_VLD(&in[i ]), scale); - ma_dr_mp3_f4 b = MA_DR_MP3_VMUL(MA_DR_MP3_VLD(&in[i+4]), scale); -#if MA_DR_MP3_HAVE_SSE - ma_dr_mp3_f4 s16max = MA_DR_MP3_VSET( 32767.0f); - ma_dr_mp3_f4 s16min = MA_DR_MP3_VSET(-32768.0f); - __m128i pcm8 = _mm_packs_epi32(_mm_cvtps_epi32(_mm_max_ps(_mm_min_ps(a, s16max), s16min)), - _mm_cvtps_epi32(_mm_max_ps(_mm_min_ps(b, s16max), s16min))); - out[i ] = (ma_int16)_mm_extract_epi16(pcm8, 0); - out[i+1] = (ma_int16)_mm_extract_epi16(pcm8, 1); - out[i+2] = (ma_int16)_mm_extract_epi16(pcm8, 2); - out[i+3] = (ma_int16)_mm_extract_epi16(pcm8, 3); - out[i+4] = (ma_int16)_mm_extract_epi16(pcm8, 4); - out[i+5] = (ma_int16)_mm_extract_epi16(pcm8, 5); - out[i+6] = (ma_int16)_mm_extract_epi16(pcm8, 6); - out[i+7] = (ma_int16)_mm_extract_epi16(pcm8, 7); -#else - int16x4_t pcma, pcmb; - a = MA_DR_MP3_VADD(a, MA_DR_MP3_VSET(0.5f)); - b = MA_DR_MP3_VADD(b, MA_DR_MP3_VSET(0.5f)); - pcma = vqmovn_s32(vqaddq_s32(vcvtq_s32_f32(a), vreinterpretq_s32_u32(vcltq_f32(a, MA_DR_MP3_VSET(0))))); - pcmb = vqmovn_s32(vqaddq_s32(vcvtq_s32_f32(b), vreinterpretq_s32_u32(vcltq_f32(b, MA_DR_MP3_VSET(0))))); - vst1_lane_s16(out+i , pcma, 0); - vst1_lane_s16(out+i+1, pcma, 1); - vst1_lane_s16(out+i+2, pcma, 2); - vst1_lane_s16(out+i+3, pcma, 3); - vst1_lane_s16(out+i+4, pcmb, 0); - vst1_lane_s16(out+i+5, pcmb, 1); - vst1_lane_s16(out+i+6, pcmb, 2); - vst1_lane_s16(out+i+7, pcmb, 3); -#endif - } -#endif - for(; i < num_samples; i++) - { - float sample = in[i] * 32768.0f; - if (sample >= 32766.5f) - out[i] = (ma_int16) 32767; - else if (sample <= -32767.5f) - out[i] = (ma_int16)-32768; - else - { - short s = (ma_int16)(sample + .5f); - s -= (s < 0); - out[i] = s; - } - } -} -#ifndef MA_DR_MP3_SEEK_LEADING_MP3_FRAMES -#define MA_DR_MP3_SEEK_LEADING_MP3_FRAMES 2 -#endif -#define MA_DR_MP3_MIN_DATA_CHUNK_SIZE 16384 -#ifndef MA_DR_MP3_DATA_CHUNK_SIZE -#define MA_DR_MP3_DATA_CHUNK_SIZE (MA_DR_MP3_MIN_DATA_CHUNK_SIZE*4) -#endif -#define MA_DR_MP3_COUNTOF(x) (sizeof(x) / sizeof(x[0])) -#define MA_DR_MP3_CLAMP(x, lo, hi) (MA_DR_MP3_MAX(lo, MA_DR_MP3_MIN(x, hi))) -#ifndef MA_DR_MP3_PI_D -#define MA_DR_MP3_PI_D 3.14159265358979323846264 -#endif -#define MA_DR_MP3_DEFAULT_RESAMPLER_LPF_ORDER 2 -static MA_INLINE float ma_dr_mp3_mix_f32(float x, float y, float a) -{ - return x*(1-a) + y*a; -} -static MA_INLINE float ma_dr_mp3_mix_f32_fast(float x, float y, float a) -{ - float r0 = (y - x); - float r1 = r0*a; - return x + r1; -} -static MA_INLINE ma_uint32 ma_dr_mp3_gcf_u32(ma_uint32 a, ma_uint32 b) -{ - for (;;) { - if (b == 0) { - break; - } else { - ma_uint32 t = a; - a = b; - b = t % a; - } - } - return a; -} -static void* ma_dr_mp3__malloc_default(size_t sz, void* pUserData) -{ - (void)pUserData; - return MA_DR_MP3_MALLOC(sz); -} -static void* ma_dr_mp3__realloc_default(void* p, size_t sz, void* pUserData) -{ - (void)pUserData; - return MA_DR_MP3_REALLOC(p, sz); -} -static void ma_dr_mp3__free_default(void* p, void* pUserData) -{ - (void)pUserData; - MA_DR_MP3_FREE(p); -} -static void* ma_dr_mp3__malloc_from_callbacks(size_t sz, const ma_allocation_callbacks* pAllocationCallbacks) -{ - if (pAllocationCallbacks == NULL) { - return NULL; - } - if (pAllocationCallbacks->onMalloc != NULL) { - return pAllocationCallbacks->onMalloc(sz, pAllocationCallbacks->pUserData); - } - if (pAllocationCallbacks->onRealloc != NULL) { - return pAllocationCallbacks->onRealloc(NULL, sz, pAllocationCallbacks->pUserData); - } - return NULL; -} -static void* ma_dr_mp3__realloc_from_callbacks(void* p, size_t szNew, size_t szOld, const ma_allocation_callbacks* pAllocationCallbacks) -{ - if (pAllocationCallbacks == NULL) { - return NULL; - } - if (pAllocationCallbacks->onRealloc != NULL) { - return pAllocationCallbacks->onRealloc(p, szNew, pAllocationCallbacks->pUserData); - } - if (pAllocationCallbacks->onMalloc != NULL && pAllocationCallbacks->onFree != NULL) { - void* p2; - p2 = pAllocationCallbacks->onMalloc(szNew, pAllocationCallbacks->pUserData); - if (p2 == NULL) { - return NULL; - } - if (p != NULL) { - MA_DR_MP3_COPY_MEMORY(p2, p, szOld); - pAllocationCallbacks->onFree(p, pAllocationCallbacks->pUserData); - } - return p2; - } - return NULL; -} -static void ma_dr_mp3__free_from_callbacks(void* p, const ma_allocation_callbacks* pAllocationCallbacks) -{ - if (p == NULL || pAllocationCallbacks == NULL) { - return; - } - if (pAllocationCallbacks->onFree != NULL) { - pAllocationCallbacks->onFree(p, pAllocationCallbacks->pUserData); - } -} -static ma_allocation_callbacks ma_dr_mp3_copy_allocation_callbacks_or_defaults(const ma_allocation_callbacks* pAllocationCallbacks) -{ - if (pAllocationCallbacks != NULL) { - return *pAllocationCallbacks; - } else { - ma_allocation_callbacks allocationCallbacks; - allocationCallbacks.pUserData = NULL; - allocationCallbacks.onMalloc = ma_dr_mp3__malloc_default; - allocationCallbacks.onRealloc = ma_dr_mp3__realloc_default; - allocationCallbacks.onFree = ma_dr_mp3__free_default; - return allocationCallbacks; - } -} -static size_t ma_dr_mp3__on_read(ma_dr_mp3* pMP3, void* pBufferOut, size_t bytesToRead) -{ - size_t bytesRead = pMP3->onRead(pMP3->pUserData, pBufferOut, bytesToRead); - pMP3->streamCursor += bytesRead; - return bytesRead; -} -static ma_bool32 ma_dr_mp3__on_seek(ma_dr_mp3* pMP3, int offset, ma_dr_mp3_seek_origin origin) -{ - MA_DR_MP3_ASSERT(offset >= 0); - if (!pMP3->onSeek(pMP3->pUserData, offset, origin)) { - return MA_FALSE; - } - if (origin == ma_dr_mp3_seek_origin_start) { - pMP3->streamCursor = (ma_uint64)offset; - } else { - pMP3->streamCursor += offset; - } - return MA_TRUE; -} -static ma_bool32 ma_dr_mp3__on_seek_64(ma_dr_mp3* pMP3, ma_uint64 offset, ma_dr_mp3_seek_origin origin) -{ - if (offset <= 0x7FFFFFFF) { - return ma_dr_mp3__on_seek(pMP3, (int)offset, origin); - } - if (!ma_dr_mp3__on_seek(pMP3, 0x7FFFFFFF, ma_dr_mp3_seek_origin_start)) { - return MA_FALSE; - } - offset -= 0x7FFFFFFF; - while (offset > 0) { - if (offset <= 0x7FFFFFFF) { - if (!ma_dr_mp3__on_seek(pMP3, (int)offset, ma_dr_mp3_seek_origin_current)) { - return MA_FALSE; - } - offset = 0; - } else { - if (!ma_dr_mp3__on_seek(pMP3, 0x7FFFFFFF, ma_dr_mp3_seek_origin_current)) { - return MA_FALSE; - } - offset -= 0x7FFFFFFF; - } - } - return MA_TRUE; -} -static ma_uint32 ma_dr_mp3_decode_next_frame_ex__callbacks(ma_dr_mp3* pMP3, ma_dr_mp3d_sample_t* pPCMFrames) -{ - ma_uint32 pcmFramesRead = 0; - MA_DR_MP3_ASSERT(pMP3 != NULL); - MA_DR_MP3_ASSERT(pMP3->onRead != NULL); - if (pMP3->atEnd) { - return 0; - } - for (;;) { - ma_dr_mp3dec_frame_info info; - if (pMP3->dataSize < MA_DR_MP3_MIN_DATA_CHUNK_SIZE) { - size_t bytesRead; - if (pMP3->pData != NULL) { - MA_DR_MP3_MOVE_MEMORY(pMP3->pData, pMP3->pData + pMP3->dataConsumed, pMP3->dataSize); - } - pMP3->dataConsumed = 0; - if (pMP3->dataCapacity < MA_DR_MP3_DATA_CHUNK_SIZE) { - ma_uint8* pNewData; - size_t newDataCap; - newDataCap = MA_DR_MP3_DATA_CHUNK_SIZE; - pNewData = (ma_uint8*)ma_dr_mp3__realloc_from_callbacks(pMP3->pData, newDataCap, pMP3->dataCapacity, &pMP3->allocationCallbacks); - if (pNewData == NULL) { - return 0; - } - pMP3->pData = pNewData; - pMP3->dataCapacity = newDataCap; - } - bytesRead = ma_dr_mp3__on_read(pMP3, pMP3->pData + pMP3->dataSize, (pMP3->dataCapacity - pMP3->dataSize)); - if (bytesRead == 0) { - if (pMP3->dataSize == 0) { - pMP3->atEnd = MA_TRUE; - return 0; - } - } - pMP3->dataSize += bytesRead; - } - if (pMP3->dataSize > INT_MAX) { - pMP3->atEnd = MA_TRUE; - return 0; - } - MA_DR_MP3_ASSERT(pMP3->pData != NULL); - MA_DR_MP3_ASSERT(pMP3->dataCapacity > 0); - if (pMP3->pData == NULL) { - return 0; - } - pcmFramesRead = ma_dr_mp3dec_decode_frame(&pMP3->decoder, pMP3->pData + pMP3->dataConsumed, (int)pMP3->dataSize, pPCMFrames, &info); - if (info.frame_bytes > 0) { - pMP3->dataConsumed += (size_t)info.frame_bytes; - pMP3->dataSize -= (size_t)info.frame_bytes; - } - if (pcmFramesRead > 0) { - pcmFramesRead = ma_dr_mp3_hdr_frame_samples(pMP3->decoder.header); - pMP3->pcmFramesConsumedInMP3Frame = 0; - pMP3->pcmFramesRemainingInMP3Frame = pcmFramesRead; - pMP3->mp3FrameChannels = info.channels; - pMP3->mp3FrameSampleRate = info.hz; - break; - } else if (info.frame_bytes == 0) { - size_t bytesRead; - MA_DR_MP3_MOVE_MEMORY(pMP3->pData, pMP3->pData + pMP3->dataConsumed, pMP3->dataSize); - pMP3->dataConsumed = 0; - if (pMP3->dataCapacity == pMP3->dataSize) { - ma_uint8* pNewData; - size_t newDataCap; - newDataCap = pMP3->dataCapacity + MA_DR_MP3_DATA_CHUNK_SIZE; - pNewData = (ma_uint8*)ma_dr_mp3__realloc_from_callbacks(pMP3->pData, newDataCap, pMP3->dataCapacity, &pMP3->allocationCallbacks); - if (pNewData == NULL) { - return 0; - } - pMP3->pData = pNewData; - pMP3->dataCapacity = newDataCap; - } - bytesRead = ma_dr_mp3__on_read(pMP3, pMP3->pData + pMP3->dataSize, (pMP3->dataCapacity - pMP3->dataSize)); - if (bytesRead == 0) { - pMP3->atEnd = MA_TRUE; - return 0; - } - pMP3->dataSize += bytesRead; - } - }; - return pcmFramesRead; -} -static ma_uint32 ma_dr_mp3_decode_next_frame_ex__memory(ma_dr_mp3* pMP3, ma_dr_mp3d_sample_t* pPCMFrames) -{ - ma_uint32 pcmFramesRead = 0; - ma_dr_mp3dec_frame_info info; - MA_DR_MP3_ASSERT(pMP3 != NULL); - MA_DR_MP3_ASSERT(pMP3->memory.pData != NULL); - if (pMP3->atEnd) { - return 0; - } - for (;;) { - pcmFramesRead = ma_dr_mp3dec_decode_frame(&pMP3->decoder, pMP3->memory.pData + pMP3->memory.currentReadPos, (int)(pMP3->memory.dataSize - pMP3->memory.currentReadPos), pPCMFrames, &info); - if (pcmFramesRead > 0) { - pcmFramesRead = ma_dr_mp3_hdr_frame_samples(pMP3->decoder.header); - pMP3->pcmFramesConsumedInMP3Frame = 0; - pMP3->pcmFramesRemainingInMP3Frame = pcmFramesRead; - pMP3->mp3FrameChannels = info.channels; - pMP3->mp3FrameSampleRate = info.hz; - break; - } else if (info.frame_bytes > 0) { - pMP3->memory.currentReadPos += (size_t)info.frame_bytes; - } else { - break; - } - } - pMP3->memory.currentReadPos += (size_t)info.frame_bytes; - return pcmFramesRead; -} -static ma_uint32 ma_dr_mp3_decode_next_frame_ex(ma_dr_mp3* pMP3, ma_dr_mp3d_sample_t* pPCMFrames) -{ - if (pMP3->memory.pData != NULL && pMP3->memory.dataSize > 0) { - return ma_dr_mp3_decode_next_frame_ex__memory(pMP3, pPCMFrames); - } else { - return ma_dr_mp3_decode_next_frame_ex__callbacks(pMP3, pPCMFrames); - } -} -static ma_uint32 ma_dr_mp3_decode_next_frame(ma_dr_mp3* pMP3) -{ - MA_DR_MP3_ASSERT(pMP3 != NULL); - return ma_dr_mp3_decode_next_frame_ex(pMP3, (ma_dr_mp3d_sample_t*)pMP3->pcmFrames); -} -#if 0 -static ma_uint32 ma_dr_mp3_seek_next_frame(ma_dr_mp3* pMP3) -{ - ma_uint32 pcmFrameCount; - MA_DR_MP3_ASSERT(pMP3 != NULL); - pcmFrameCount = ma_dr_mp3_decode_next_frame_ex(pMP3, NULL); - if (pcmFrameCount == 0) { - return 0; - } - pMP3->currentPCMFrame += pcmFrameCount; - pMP3->pcmFramesConsumedInMP3Frame = pcmFrameCount; - pMP3->pcmFramesRemainingInMP3Frame = 0; - return pcmFrameCount; -} -#endif -static ma_bool32 ma_dr_mp3_init_internal(ma_dr_mp3* pMP3, ma_dr_mp3_read_proc onRead, ma_dr_mp3_seek_proc onSeek, void* pUserData, const ma_allocation_callbacks* pAllocationCallbacks) -{ - MA_DR_MP3_ASSERT(pMP3 != NULL); - MA_DR_MP3_ASSERT(onRead != NULL); - ma_dr_mp3dec_init(&pMP3->decoder); - pMP3->onRead = onRead; - pMP3->onSeek = onSeek; - pMP3->pUserData = pUserData; - pMP3->allocationCallbacks = ma_dr_mp3_copy_allocation_callbacks_or_defaults(pAllocationCallbacks); - if (pMP3->allocationCallbacks.onFree == NULL || (pMP3->allocationCallbacks.onMalloc == NULL && pMP3->allocationCallbacks.onRealloc == NULL)) { - return MA_FALSE; - } - if (ma_dr_mp3_decode_next_frame(pMP3) == 0) { - ma_dr_mp3__free_from_callbacks(pMP3->pData, &pMP3->allocationCallbacks); - return MA_FALSE; - } - pMP3->channels = pMP3->mp3FrameChannels; - pMP3->sampleRate = pMP3->mp3FrameSampleRate; - return MA_TRUE; -} -MA_API ma_bool32 ma_dr_mp3_init(ma_dr_mp3* pMP3, ma_dr_mp3_read_proc onRead, ma_dr_mp3_seek_proc onSeek, void* pUserData, const ma_allocation_callbacks* pAllocationCallbacks) -{ - if (pMP3 == NULL || onRead == NULL) { - return MA_FALSE; - } - MA_DR_MP3_ZERO_OBJECT(pMP3); - return ma_dr_mp3_init_internal(pMP3, onRead, onSeek, pUserData, pAllocationCallbacks); -} -static size_t ma_dr_mp3__on_read_memory(void* pUserData, void* pBufferOut, size_t bytesToRead) -{ - ma_dr_mp3* pMP3 = (ma_dr_mp3*)pUserData; - size_t bytesRemaining; - MA_DR_MP3_ASSERT(pMP3 != NULL); - MA_DR_MP3_ASSERT(pMP3->memory.dataSize >= pMP3->memory.currentReadPos); - bytesRemaining = pMP3->memory.dataSize - pMP3->memory.currentReadPos; - if (bytesToRead > bytesRemaining) { - bytesToRead = bytesRemaining; - } - if (bytesToRead > 0) { - MA_DR_MP3_COPY_MEMORY(pBufferOut, pMP3->memory.pData + pMP3->memory.currentReadPos, bytesToRead); - pMP3->memory.currentReadPos += bytesToRead; - } - return bytesToRead; -} -static ma_bool32 ma_dr_mp3__on_seek_memory(void* pUserData, int byteOffset, ma_dr_mp3_seek_origin origin) -{ - ma_dr_mp3* pMP3 = (ma_dr_mp3*)pUserData; - MA_DR_MP3_ASSERT(pMP3 != NULL); - if (origin == ma_dr_mp3_seek_origin_current) { - if (byteOffset > 0) { - if (pMP3->memory.currentReadPos + byteOffset > pMP3->memory.dataSize) { - byteOffset = (int)(pMP3->memory.dataSize - pMP3->memory.currentReadPos); - } - } else { - if (pMP3->memory.currentReadPos < (size_t)-byteOffset) { - byteOffset = -(int)pMP3->memory.currentReadPos; - } - } - pMP3->memory.currentReadPos += byteOffset; - } else { - if ((ma_uint32)byteOffset <= pMP3->memory.dataSize) { - pMP3->memory.currentReadPos = byteOffset; - } else { - pMP3->memory.currentReadPos = pMP3->memory.dataSize; - } - } - return MA_TRUE; -} -MA_API ma_bool32 ma_dr_mp3_init_memory(ma_dr_mp3* pMP3, const void* pData, size_t dataSize, const ma_allocation_callbacks* pAllocationCallbacks) -{ - if (pMP3 == NULL) { - return MA_FALSE; - } - MA_DR_MP3_ZERO_OBJECT(pMP3); - if (pData == NULL || dataSize == 0) { - return MA_FALSE; - } - pMP3->memory.pData = (const ma_uint8*)pData; - pMP3->memory.dataSize = dataSize; - pMP3->memory.currentReadPos = 0; - return ma_dr_mp3_init_internal(pMP3, ma_dr_mp3__on_read_memory, ma_dr_mp3__on_seek_memory, pMP3, pAllocationCallbacks); -} -#ifndef MA_DR_MP3_NO_STDIO -#include -#include -static size_t ma_dr_mp3__on_read_stdio(void* pUserData, void* pBufferOut, size_t bytesToRead) -{ - return fread(pBufferOut, 1, bytesToRead, (FILE*)pUserData); -} -static ma_bool32 ma_dr_mp3__on_seek_stdio(void* pUserData, int offset, ma_dr_mp3_seek_origin origin) -{ - return fseek((FILE*)pUserData, offset, (origin == ma_dr_mp3_seek_origin_current) ? SEEK_CUR : SEEK_SET) == 0; -} -MA_API ma_bool32 ma_dr_mp3_init_file(ma_dr_mp3* pMP3, const char* pFilePath, const ma_allocation_callbacks* pAllocationCallbacks) -{ - ma_bool32 result; - FILE* pFile; - if (ma_fopen(&pFile, pFilePath, "rb") != MA_SUCCESS) { - return MA_FALSE; - } - result = ma_dr_mp3_init(pMP3, ma_dr_mp3__on_read_stdio, ma_dr_mp3__on_seek_stdio, (void*)pFile, pAllocationCallbacks); - if (result != MA_TRUE) { - fclose(pFile); - return result; - } - return MA_TRUE; -} -MA_API ma_bool32 ma_dr_mp3_init_file_w(ma_dr_mp3* pMP3, const wchar_t* pFilePath, const ma_allocation_callbacks* pAllocationCallbacks) -{ - ma_bool32 result; - FILE* pFile; - if (ma_wfopen(&pFile, pFilePath, L"rb", pAllocationCallbacks) != MA_SUCCESS) { - return MA_FALSE; - } - result = ma_dr_mp3_init(pMP3, ma_dr_mp3__on_read_stdio, ma_dr_mp3__on_seek_stdio, (void*)pFile, pAllocationCallbacks); - if (result != MA_TRUE) { - fclose(pFile); - return result; - } - return MA_TRUE; -} -#endif -MA_API void ma_dr_mp3_uninit(ma_dr_mp3* pMP3) -{ - if (pMP3 == NULL) { - return; - } -#ifndef MA_DR_MP3_NO_STDIO - if (pMP3->onRead == ma_dr_mp3__on_read_stdio) { - FILE* pFile = (FILE*)pMP3->pUserData; - if (pFile != NULL) { - fclose(pFile); - pMP3->pUserData = NULL; - } - } -#endif - ma_dr_mp3__free_from_callbacks(pMP3->pData, &pMP3->allocationCallbacks); -} -#if defined(MA_DR_MP3_FLOAT_OUTPUT) -static void ma_dr_mp3_f32_to_s16(ma_int16* dst, const float* src, ma_uint64 sampleCount) -{ - ma_uint64 i; - ma_uint64 i4; - ma_uint64 sampleCount4; - i = 0; - sampleCount4 = sampleCount >> 2; - for (i4 = 0; i4 < sampleCount4; i4 += 1) { - float x0 = src[i+0]; - float x1 = src[i+1]; - float x2 = src[i+2]; - float x3 = src[i+3]; - x0 = ((x0 < -1) ? -1 : ((x0 > 1) ? 1 : x0)); - x1 = ((x1 < -1) ? -1 : ((x1 > 1) ? 1 : x1)); - x2 = ((x2 < -1) ? -1 : ((x2 > 1) ? 1 : x2)); - x3 = ((x3 < -1) ? -1 : ((x3 > 1) ? 1 : x3)); - x0 = x0 * 32767.0f; - x1 = x1 * 32767.0f; - x2 = x2 * 32767.0f; - x3 = x3 * 32767.0f; - dst[i+0] = (ma_int16)x0; - dst[i+1] = (ma_int16)x1; - dst[i+2] = (ma_int16)x2; - dst[i+3] = (ma_int16)x3; - i += 4; - } - for (; i < sampleCount; i += 1) { - float x = src[i]; - x = ((x < -1) ? -1 : ((x > 1) ? 1 : x)); - x = x * 32767.0f; - dst[i] = (ma_int16)x; - } -} -#endif -#if !defined(MA_DR_MP3_FLOAT_OUTPUT) -static void ma_dr_mp3_s16_to_f32(float* dst, const ma_int16* src, ma_uint64 sampleCount) -{ - ma_uint64 i; - for (i = 0; i < sampleCount; i += 1) { - float x = (float)src[i]; - x = x * 0.000030517578125f; - dst[i] = x; - } -} -#endif -static ma_uint64 ma_dr_mp3_read_pcm_frames_raw(ma_dr_mp3* pMP3, ma_uint64 framesToRead, void* pBufferOut) -{ - ma_uint64 totalFramesRead = 0; - MA_DR_MP3_ASSERT(pMP3 != NULL); - MA_DR_MP3_ASSERT(pMP3->onRead != NULL); - while (framesToRead > 0) { - ma_uint32 framesToConsume = (ma_uint32)MA_DR_MP3_MIN(pMP3->pcmFramesRemainingInMP3Frame, framesToRead); - if (pBufferOut != NULL) { - #if defined(MA_DR_MP3_FLOAT_OUTPUT) - float* pFramesOutF32 = (float*)MA_DR_MP3_OFFSET_PTR(pBufferOut, sizeof(float) * totalFramesRead * pMP3->channels); - float* pFramesInF32 = (float*)MA_DR_MP3_OFFSET_PTR(&pMP3->pcmFrames[0], sizeof(float) * pMP3->pcmFramesConsumedInMP3Frame * pMP3->mp3FrameChannels); - MA_DR_MP3_COPY_MEMORY(pFramesOutF32, pFramesInF32, sizeof(float) * framesToConsume * pMP3->channels); - #else - ma_int16* pFramesOutS16 = (ma_int16*)MA_DR_MP3_OFFSET_PTR(pBufferOut, sizeof(ma_int16) * totalFramesRead * pMP3->channels); - ma_int16* pFramesInS16 = (ma_int16*)MA_DR_MP3_OFFSET_PTR(&pMP3->pcmFrames[0], sizeof(ma_int16) * pMP3->pcmFramesConsumedInMP3Frame * pMP3->mp3FrameChannels); - MA_DR_MP3_COPY_MEMORY(pFramesOutS16, pFramesInS16, sizeof(ma_int16) * framesToConsume * pMP3->channels); - #endif - } - pMP3->currentPCMFrame += framesToConsume; - pMP3->pcmFramesConsumedInMP3Frame += framesToConsume; - pMP3->pcmFramesRemainingInMP3Frame -= framesToConsume; - totalFramesRead += framesToConsume; - framesToRead -= framesToConsume; - if (framesToRead == 0) { - break; - } - MA_DR_MP3_ASSERT(pMP3->pcmFramesRemainingInMP3Frame == 0); - if (ma_dr_mp3_decode_next_frame(pMP3) == 0) { - break; - } - } - return totalFramesRead; -} -MA_API ma_uint64 ma_dr_mp3_read_pcm_frames_f32(ma_dr_mp3* pMP3, ma_uint64 framesToRead, float* pBufferOut) -{ - if (pMP3 == NULL || pMP3->onRead == NULL) { - return 0; - } -#if defined(MA_DR_MP3_FLOAT_OUTPUT) - return ma_dr_mp3_read_pcm_frames_raw(pMP3, framesToRead, pBufferOut); -#else - { - ma_int16 pTempS16[8192]; - ma_uint64 totalPCMFramesRead = 0; - while (totalPCMFramesRead < framesToRead) { - ma_uint64 framesJustRead; - ma_uint64 framesRemaining = framesToRead - totalPCMFramesRead; - ma_uint64 framesToReadNow = MA_DR_MP3_COUNTOF(pTempS16) / pMP3->channels; - if (framesToReadNow > framesRemaining) { - framesToReadNow = framesRemaining; - } - framesJustRead = ma_dr_mp3_read_pcm_frames_raw(pMP3, framesToReadNow, pTempS16); - if (framesJustRead == 0) { - break; - } - ma_dr_mp3_s16_to_f32((float*)MA_DR_MP3_OFFSET_PTR(pBufferOut, sizeof(float) * totalPCMFramesRead * pMP3->channels), pTempS16, framesJustRead * pMP3->channels); - totalPCMFramesRead += framesJustRead; - } - return totalPCMFramesRead; - } -#endif -} -MA_API ma_uint64 ma_dr_mp3_read_pcm_frames_s16(ma_dr_mp3* pMP3, ma_uint64 framesToRead, ma_int16* pBufferOut) -{ - if (pMP3 == NULL || pMP3->onRead == NULL) { - return 0; - } -#if !defined(MA_DR_MP3_FLOAT_OUTPUT) - return ma_dr_mp3_read_pcm_frames_raw(pMP3, framesToRead, pBufferOut); -#else - { - float pTempF32[4096]; - ma_uint64 totalPCMFramesRead = 0; - while (totalPCMFramesRead < framesToRead) { - ma_uint64 framesJustRead; - ma_uint64 framesRemaining = framesToRead - totalPCMFramesRead; - ma_uint64 framesToReadNow = MA_DR_MP3_COUNTOF(pTempF32) / pMP3->channels; - if (framesToReadNow > framesRemaining) { - framesToReadNow = framesRemaining; - } - framesJustRead = ma_dr_mp3_read_pcm_frames_raw(pMP3, framesToReadNow, pTempF32); - if (framesJustRead == 0) { - break; - } - ma_dr_mp3_f32_to_s16((ma_int16*)MA_DR_MP3_OFFSET_PTR(pBufferOut, sizeof(ma_int16) * totalPCMFramesRead * pMP3->channels), pTempF32, framesJustRead * pMP3->channels); - totalPCMFramesRead += framesJustRead; - } - return totalPCMFramesRead; - } -#endif -} -static void ma_dr_mp3_reset(ma_dr_mp3* pMP3) -{ - MA_DR_MP3_ASSERT(pMP3 != NULL); - pMP3->pcmFramesConsumedInMP3Frame = 0; - pMP3->pcmFramesRemainingInMP3Frame = 0; - pMP3->currentPCMFrame = 0; - pMP3->dataSize = 0; - pMP3->atEnd = MA_FALSE; - ma_dr_mp3dec_init(&pMP3->decoder); -} -static ma_bool32 ma_dr_mp3_seek_to_start_of_stream(ma_dr_mp3* pMP3) -{ - MA_DR_MP3_ASSERT(pMP3 != NULL); - MA_DR_MP3_ASSERT(pMP3->onSeek != NULL); - if (!ma_dr_mp3__on_seek(pMP3, 0, ma_dr_mp3_seek_origin_start)) { - return MA_FALSE; - } - ma_dr_mp3_reset(pMP3); - return MA_TRUE; -} -static ma_bool32 ma_dr_mp3_seek_forward_by_pcm_frames__brute_force(ma_dr_mp3* pMP3, ma_uint64 frameOffset) -{ - ma_uint64 framesRead; -#if defined(MA_DR_MP3_FLOAT_OUTPUT) - framesRead = ma_dr_mp3_read_pcm_frames_f32(pMP3, frameOffset, NULL); -#else - framesRead = ma_dr_mp3_read_pcm_frames_s16(pMP3, frameOffset, NULL); -#endif - if (framesRead != frameOffset) { - return MA_FALSE; - } - return MA_TRUE; -} -static ma_bool32 ma_dr_mp3_seek_to_pcm_frame__brute_force(ma_dr_mp3* pMP3, ma_uint64 frameIndex) -{ - MA_DR_MP3_ASSERT(pMP3 != NULL); - if (frameIndex == pMP3->currentPCMFrame) { - return MA_TRUE; - } - if (frameIndex < pMP3->currentPCMFrame) { - if (!ma_dr_mp3_seek_to_start_of_stream(pMP3)) { - return MA_FALSE; - } - } - MA_DR_MP3_ASSERT(frameIndex >= pMP3->currentPCMFrame); - return ma_dr_mp3_seek_forward_by_pcm_frames__brute_force(pMP3, (frameIndex - pMP3->currentPCMFrame)); -} -static ma_bool32 ma_dr_mp3_find_closest_seek_point(ma_dr_mp3* pMP3, ma_uint64 frameIndex, ma_uint32* pSeekPointIndex) -{ - ma_uint32 iSeekPoint; - MA_DR_MP3_ASSERT(pSeekPointIndex != NULL); - *pSeekPointIndex = 0; - if (frameIndex < pMP3->pSeekPoints[0].pcmFrameIndex) { - return MA_FALSE; - } - for (iSeekPoint = 0; iSeekPoint < pMP3->seekPointCount; ++iSeekPoint) { - if (pMP3->pSeekPoints[iSeekPoint].pcmFrameIndex > frameIndex) { - break; - } - *pSeekPointIndex = iSeekPoint; - } - return MA_TRUE; -} -static ma_bool32 ma_dr_mp3_seek_to_pcm_frame__seek_table(ma_dr_mp3* pMP3, ma_uint64 frameIndex) -{ - ma_dr_mp3_seek_point seekPoint; - ma_uint32 priorSeekPointIndex; - ma_uint16 iMP3Frame; - ma_uint64 leftoverFrames; - MA_DR_MP3_ASSERT(pMP3 != NULL); - MA_DR_MP3_ASSERT(pMP3->pSeekPoints != NULL); - MA_DR_MP3_ASSERT(pMP3->seekPointCount > 0); - if (ma_dr_mp3_find_closest_seek_point(pMP3, frameIndex, &priorSeekPointIndex)) { - seekPoint = pMP3->pSeekPoints[priorSeekPointIndex]; - } else { - seekPoint.seekPosInBytes = 0; - seekPoint.pcmFrameIndex = 0; - seekPoint.mp3FramesToDiscard = 0; - seekPoint.pcmFramesToDiscard = 0; - } - if (!ma_dr_mp3__on_seek_64(pMP3, seekPoint.seekPosInBytes, ma_dr_mp3_seek_origin_start)) { - return MA_FALSE; - } - ma_dr_mp3_reset(pMP3); - for (iMP3Frame = 0; iMP3Frame < seekPoint.mp3FramesToDiscard; ++iMP3Frame) { - ma_uint32 pcmFramesRead; - ma_dr_mp3d_sample_t* pPCMFrames; - pPCMFrames = NULL; - if (iMP3Frame == seekPoint.mp3FramesToDiscard-1) { - pPCMFrames = (ma_dr_mp3d_sample_t*)pMP3->pcmFrames; - } - pcmFramesRead = ma_dr_mp3_decode_next_frame_ex(pMP3, pPCMFrames); - if (pcmFramesRead == 0) { - return MA_FALSE; - } - } - pMP3->currentPCMFrame = seekPoint.pcmFrameIndex - seekPoint.pcmFramesToDiscard; - leftoverFrames = frameIndex - pMP3->currentPCMFrame; - return ma_dr_mp3_seek_forward_by_pcm_frames__brute_force(pMP3, leftoverFrames); -} -MA_API ma_bool32 ma_dr_mp3_seek_to_pcm_frame(ma_dr_mp3* pMP3, ma_uint64 frameIndex) -{ - if (pMP3 == NULL || pMP3->onSeek == NULL) { - return MA_FALSE; - } - if (frameIndex == 0) { - return ma_dr_mp3_seek_to_start_of_stream(pMP3); - } - if (pMP3->pSeekPoints != NULL && pMP3->seekPointCount > 0) { - return ma_dr_mp3_seek_to_pcm_frame__seek_table(pMP3, frameIndex); - } else { - return ma_dr_mp3_seek_to_pcm_frame__brute_force(pMP3, frameIndex); - } -} -MA_API ma_bool32 ma_dr_mp3_get_mp3_and_pcm_frame_count(ma_dr_mp3* pMP3, ma_uint64* pMP3FrameCount, ma_uint64* pPCMFrameCount) -{ - ma_uint64 currentPCMFrame; - ma_uint64 totalPCMFrameCount; - ma_uint64 totalMP3FrameCount; - if (pMP3 == NULL) { - return MA_FALSE; - } - if (pMP3->onSeek == NULL) { - return MA_FALSE; - } - currentPCMFrame = pMP3->currentPCMFrame; - if (!ma_dr_mp3_seek_to_start_of_stream(pMP3)) { - return MA_FALSE; - } - totalPCMFrameCount = 0; - totalMP3FrameCount = 0; - for (;;) { - ma_uint32 pcmFramesInCurrentMP3Frame; - pcmFramesInCurrentMP3Frame = ma_dr_mp3_decode_next_frame_ex(pMP3, NULL); - if (pcmFramesInCurrentMP3Frame == 0) { - break; - } - totalPCMFrameCount += pcmFramesInCurrentMP3Frame; - totalMP3FrameCount += 1; - } - if (!ma_dr_mp3_seek_to_start_of_stream(pMP3)) { - return MA_FALSE; - } - if (!ma_dr_mp3_seek_to_pcm_frame(pMP3, currentPCMFrame)) { - return MA_FALSE; - } - if (pMP3FrameCount != NULL) { - *pMP3FrameCount = totalMP3FrameCount; - } - if (pPCMFrameCount != NULL) { - *pPCMFrameCount = totalPCMFrameCount; - } - return MA_TRUE; -} -MA_API ma_uint64 ma_dr_mp3_get_pcm_frame_count(ma_dr_mp3* pMP3) -{ - ma_uint64 totalPCMFrameCount; - if (!ma_dr_mp3_get_mp3_and_pcm_frame_count(pMP3, NULL, &totalPCMFrameCount)) { - return 0; - } - return totalPCMFrameCount; -} -MA_API ma_uint64 ma_dr_mp3_get_mp3_frame_count(ma_dr_mp3* pMP3) -{ - ma_uint64 totalMP3FrameCount; - if (!ma_dr_mp3_get_mp3_and_pcm_frame_count(pMP3, &totalMP3FrameCount, NULL)) { - return 0; - } - return totalMP3FrameCount; -} -static void ma_dr_mp3__accumulate_running_pcm_frame_count(ma_dr_mp3* pMP3, ma_uint32 pcmFrameCountIn, ma_uint64* pRunningPCMFrameCount, float* pRunningPCMFrameCountFractionalPart) -{ - float srcRatio; - float pcmFrameCountOutF; - ma_uint32 pcmFrameCountOut; - srcRatio = (float)pMP3->mp3FrameSampleRate / (float)pMP3->sampleRate; - MA_DR_MP3_ASSERT(srcRatio > 0); - pcmFrameCountOutF = *pRunningPCMFrameCountFractionalPart + (pcmFrameCountIn / srcRatio); - pcmFrameCountOut = (ma_uint32)pcmFrameCountOutF; - *pRunningPCMFrameCountFractionalPart = pcmFrameCountOutF - pcmFrameCountOut; - *pRunningPCMFrameCount += pcmFrameCountOut; -} -typedef struct -{ - ma_uint64 bytePos; - ma_uint64 pcmFrameIndex; -} ma_dr_mp3__seeking_mp3_frame_info; -MA_API ma_bool32 ma_dr_mp3_calculate_seek_points(ma_dr_mp3* pMP3, ma_uint32* pSeekPointCount, ma_dr_mp3_seek_point* pSeekPoints) -{ - ma_uint32 seekPointCount; - ma_uint64 currentPCMFrame; - ma_uint64 totalMP3FrameCount; - ma_uint64 totalPCMFrameCount; - if (pMP3 == NULL || pSeekPointCount == NULL || pSeekPoints == NULL) { - return MA_FALSE; - } - seekPointCount = *pSeekPointCount; - if (seekPointCount == 0) { - return MA_FALSE; - } - currentPCMFrame = pMP3->currentPCMFrame; - if (!ma_dr_mp3_get_mp3_and_pcm_frame_count(pMP3, &totalMP3FrameCount, &totalPCMFrameCount)) { - return MA_FALSE; - } - if (totalMP3FrameCount < MA_DR_MP3_SEEK_LEADING_MP3_FRAMES+1) { - seekPointCount = 1; - pSeekPoints[0].seekPosInBytes = 0; - pSeekPoints[0].pcmFrameIndex = 0; - pSeekPoints[0].mp3FramesToDiscard = 0; - pSeekPoints[0].pcmFramesToDiscard = 0; - } else { - ma_uint64 pcmFramesBetweenSeekPoints; - ma_dr_mp3__seeking_mp3_frame_info mp3FrameInfo[MA_DR_MP3_SEEK_LEADING_MP3_FRAMES+1]; - ma_uint64 runningPCMFrameCount = 0; - float runningPCMFrameCountFractionalPart = 0; - ma_uint64 nextTargetPCMFrame; - ma_uint32 iMP3Frame; - ma_uint32 iSeekPoint; - if (seekPointCount > totalMP3FrameCount-1) { - seekPointCount = (ma_uint32)totalMP3FrameCount-1; - } - pcmFramesBetweenSeekPoints = totalPCMFrameCount / (seekPointCount+1); - if (!ma_dr_mp3_seek_to_start_of_stream(pMP3)) { - return MA_FALSE; - } - for (iMP3Frame = 0; iMP3Frame < MA_DR_MP3_SEEK_LEADING_MP3_FRAMES+1; ++iMP3Frame) { - ma_uint32 pcmFramesInCurrentMP3FrameIn; - MA_DR_MP3_ASSERT(pMP3->streamCursor >= pMP3->dataSize); - mp3FrameInfo[iMP3Frame].bytePos = pMP3->streamCursor - pMP3->dataSize; - mp3FrameInfo[iMP3Frame].pcmFrameIndex = runningPCMFrameCount; - pcmFramesInCurrentMP3FrameIn = ma_dr_mp3_decode_next_frame_ex(pMP3, NULL); - if (pcmFramesInCurrentMP3FrameIn == 0) { - return MA_FALSE; - } - ma_dr_mp3__accumulate_running_pcm_frame_count(pMP3, pcmFramesInCurrentMP3FrameIn, &runningPCMFrameCount, &runningPCMFrameCountFractionalPart); - } - nextTargetPCMFrame = 0; - for (iSeekPoint = 0; iSeekPoint < seekPointCount; ++iSeekPoint) { - nextTargetPCMFrame += pcmFramesBetweenSeekPoints; - for (;;) { - if (nextTargetPCMFrame < runningPCMFrameCount) { - pSeekPoints[iSeekPoint].seekPosInBytes = mp3FrameInfo[0].bytePos; - pSeekPoints[iSeekPoint].pcmFrameIndex = nextTargetPCMFrame; - pSeekPoints[iSeekPoint].mp3FramesToDiscard = MA_DR_MP3_SEEK_LEADING_MP3_FRAMES; - pSeekPoints[iSeekPoint].pcmFramesToDiscard = (ma_uint16)(nextTargetPCMFrame - mp3FrameInfo[MA_DR_MP3_SEEK_LEADING_MP3_FRAMES-1].pcmFrameIndex); - break; - } else { - size_t i; - ma_uint32 pcmFramesInCurrentMP3FrameIn; - for (i = 0; i < MA_DR_MP3_COUNTOF(mp3FrameInfo)-1; ++i) { - mp3FrameInfo[i] = mp3FrameInfo[i+1]; - } - mp3FrameInfo[MA_DR_MP3_COUNTOF(mp3FrameInfo)-1].bytePos = pMP3->streamCursor - pMP3->dataSize; - mp3FrameInfo[MA_DR_MP3_COUNTOF(mp3FrameInfo)-1].pcmFrameIndex = runningPCMFrameCount; - pcmFramesInCurrentMP3FrameIn = ma_dr_mp3_decode_next_frame_ex(pMP3, NULL); - if (pcmFramesInCurrentMP3FrameIn == 0) { - pSeekPoints[iSeekPoint].seekPosInBytes = mp3FrameInfo[0].bytePos; - pSeekPoints[iSeekPoint].pcmFrameIndex = nextTargetPCMFrame; - pSeekPoints[iSeekPoint].mp3FramesToDiscard = MA_DR_MP3_SEEK_LEADING_MP3_FRAMES; - pSeekPoints[iSeekPoint].pcmFramesToDiscard = (ma_uint16)(nextTargetPCMFrame - mp3FrameInfo[MA_DR_MP3_SEEK_LEADING_MP3_FRAMES-1].pcmFrameIndex); - break; - } - ma_dr_mp3__accumulate_running_pcm_frame_count(pMP3, pcmFramesInCurrentMP3FrameIn, &runningPCMFrameCount, &runningPCMFrameCountFractionalPart); - } - } - } - if (!ma_dr_mp3_seek_to_start_of_stream(pMP3)) { - return MA_FALSE; - } - if (!ma_dr_mp3_seek_to_pcm_frame(pMP3, currentPCMFrame)) { - return MA_FALSE; - } - } - *pSeekPointCount = seekPointCount; - return MA_TRUE; -} -MA_API ma_bool32 ma_dr_mp3_bind_seek_table(ma_dr_mp3* pMP3, ma_uint32 seekPointCount, ma_dr_mp3_seek_point* pSeekPoints) -{ - if (pMP3 == NULL) { - return MA_FALSE; - } - if (seekPointCount == 0 || pSeekPoints == NULL) { - pMP3->seekPointCount = 0; - pMP3->pSeekPoints = NULL; - } else { - pMP3->seekPointCount = seekPointCount; - pMP3->pSeekPoints = pSeekPoints; - } - return MA_TRUE; -} -static float* ma_dr_mp3__full_read_and_close_f32(ma_dr_mp3* pMP3, ma_dr_mp3_config* pConfig, ma_uint64* pTotalFrameCount) -{ - ma_uint64 totalFramesRead = 0; - ma_uint64 framesCapacity = 0; - float* pFrames = NULL; - float temp[4096]; - MA_DR_MP3_ASSERT(pMP3 != NULL); - for (;;) { - ma_uint64 framesToReadRightNow = MA_DR_MP3_COUNTOF(temp) / pMP3->channels; - ma_uint64 framesJustRead = ma_dr_mp3_read_pcm_frames_f32(pMP3, framesToReadRightNow, temp); - if (framesJustRead == 0) { - break; - } - if (framesCapacity < totalFramesRead + framesJustRead) { - ma_uint64 oldFramesBufferSize; - ma_uint64 newFramesBufferSize; - ma_uint64 newFramesCap; - float* pNewFrames; - newFramesCap = framesCapacity * 2; - if (newFramesCap < totalFramesRead + framesJustRead) { - newFramesCap = totalFramesRead + framesJustRead; - } - oldFramesBufferSize = framesCapacity * pMP3->channels * sizeof(float); - newFramesBufferSize = newFramesCap * pMP3->channels * sizeof(float); - if (newFramesBufferSize > (ma_uint64)MA_SIZE_MAX) { - break; - } - pNewFrames = (float*)ma_dr_mp3__realloc_from_callbacks(pFrames, (size_t)newFramesBufferSize, (size_t)oldFramesBufferSize, &pMP3->allocationCallbacks); - if (pNewFrames == NULL) { - ma_dr_mp3__free_from_callbacks(pFrames, &pMP3->allocationCallbacks); - break; - } - pFrames = pNewFrames; - framesCapacity = newFramesCap; - } - MA_DR_MP3_COPY_MEMORY(pFrames + totalFramesRead*pMP3->channels, temp, (size_t)(framesJustRead*pMP3->channels*sizeof(float))); - totalFramesRead += framesJustRead; - if (framesJustRead != framesToReadRightNow) { - break; - } - } - if (pConfig != NULL) { - pConfig->channels = pMP3->channels; - pConfig->sampleRate = pMP3->sampleRate; - } - ma_dr_mp3_uninit(pMP3); - if (pTotalFrameCount) { - *pTotalFrameCount = totalFramesRead; - } - return pFrames; -} -static ma_int16* ma_dr_mp3__full_read_and_close_s16(ma_dr_mp3* pMP3, ma_dr_mp3_config* pConfig, ma_uint64* pTotalFrameCount) -{ - ma_uint64 totalFramesRead = 0; - ma_uint64 framesCapacity = 0; - ma_int16* pFrames = NULL; - ma_int16 temp[4096]; - MA_DR_MP3_ASSERT(pMP3 != NULL); - for (;;) { - ma_uint64 framesToReadRightNow = MA_DR_MP3_COUNTOF(temp) / pMP3->channels; - ma_uint64 framesJustRead = ma_dr_mp3_read_pcm_frames_s16(pMP3, framesToReadRightNow, temp); - if (framesJustRead == 0) { - break; - } - if (framesCapacity < totalFramesRead + framesJustRead) { - ma_uint64 newFramesBufferSize; - ma_uint64 oldFramesBufferSize; - ma_uint64 newFramesCap; - ma_int16* pNewFrames; - newFramesCap = framesCapacity * 2; - if (newFramesCap < totalFramesRead + framesJustRead) { - newFramesCap = totalFramesRead + framesJustRead; - } - oldFramesBufferSize = framesCapacity * pMP3->channels * sizeof(ma_int16); - newFramesBufferSize = newFramesCap * pMP3->channels * sizeof(ma_int16); - if (newFramesBufferSize > (ma_uint64)MA_SIZE_MAX) { - break; - } - pNewFrames = (ma_int16*)ma_dr_mp3__realloc_from_callbacks(pFrames, (size_t)newFramesBufferSize, (size_t)oldFramesBufferSize, &pMP3->allocationCallbacks); - if (pNewFrames == NULL) { - ma_dr_mp3__free_from_callbacks(pFrames, &pMP3->allocationCallbacks); - break; - } - pFrames = pNewFrames; - framesCapacity = newFramesCap; - } - MA_DR_MP3_COPY_MEMORY(pFrames + totalFramesRead*pMP3->channels, temp, (size_t)(framesJustRead*pMP3->channels*sizeof(ma_int16))); - totalFramesRead += framesJustRead; - if (framesJustRead != framesToReadRightNow) { - break; - } - } - if (pConfig != NULL) { - pConfig->channels = pMP3->channels; - pConfig->sampleRate = pMP3->sampleRate; - } - ma_dr_mp3_uninit(pMP3); - if (pTotalFrameCount) { - *pTotalFrameCount = totalFramesRead; - } - return pFrames; -} -MA_API float* ma_dr_mp3_open_and_read_pcm_frames_f32(ma_dr_mp3_read_proc onRead, ma_dr_mp3_seek_proc onSeek, void* pUserData, ma_dr_mp3_config* pConfig, ma_uint64* pTotalFrameCount, const ma_allocation_callbacks* pAllocationCallbacks) -{ - ma_dr_mp3 mp3; - if (!ma_dr_mp3_init(&mp3, onRead, onSeek, pUserData, pAllocationCallbacks)) { - return NULL; - } - return ma_dr_mp3__full_read_and_close_f32(&mp3, pConfig, pTotalFrameCount); -} -MA_API ma_int16* ma_dr_mp3_open_and_read_pcm_frames_s16(ma_dr_mp3_read_proc onRead, ma_dr_mp3_seek_proc onSeek, void* pUserData, ma_dr_mp3_config* pConfig, ma_uint64* pTotalFrameCount, const ma_allocation_callbacks* pAllocationCallbacks) -{ - ma_dr_mp3 mp3; - if (!ma_dr_mp3_init(&mp3, onRead, onSeek, pUserData, pAllocationCallbacks)) { - return NULL; - } - return ma_dr_mp3__full_read_and_close_s16(&mp3, pConfig, pTotalFrameCount); -} -MA_API float* ma_dr_mp3_open_memory_and_read_pcm_frames_f32(const void* pData, size_t dataSize, ma_dr_mp3_config* pConfig, ma_uint64* pTotalFrameCount, const ma_allocation_callbacks* pAllocationCallbacks) -{ - ma_dr_mp3 mp3; - if (!ma_dr_mp3_init_memory(&mp3, pData, dataSize, pAllocationCallbacks)) { - return NULL; - } - return ma_dr_mp3__full_read_and_close_f32(&mp3, pConfig, pTotalFrameCount); -} -MA_API ma_int16* ma_dr_mp3_open_memory_and_read_pcm_frames_s16(const void* pData, size_t dataSize, ma_dr_mp3_config* pConfig, ma_uint64* pTotalFrameCount, const ma_allocation_callbacks* pAllocationCallbacks) -{ - ma_dr_mp3 mp3; - if (!ma_dr_mp3_init_memory(&mp3, pData, dataSize, pAllocationCallbacks)) { - return NULL; - } - return ma_dr_mp3__full_read_and_close_s16(&mp3, pConfig, pTotalFrameCount); -} -#ifndef MA_DR_MP3_NO_STDIO -MA_API float* ma_dr_mp3_open_file_and_read_pcm_frames_f32(const char* filePath, ma_dr_mp3_config* pConfig, ma_uint64* pTotalFrameCount, const ma_allocation_callbacks* pAllocationCallbacks) -{ - ma_dr_mp3 mp3; - if (!ma_dr_mp3_init_file(&mp3, filePath, pAllocationCallbacks)) { - return NULL; - } - return ma_dr_mp3__full_read_and_close_f32(&mp3, pConfig, pTotalFrameCount); -} -MA_API ma_int16* ma_dr_mp3_open_file_and_read_pcm_frames_s16(const char* filePath, ma_dr_mp3_config* pConfig, ma_uint64* pTotalFrameCount, const ma_allocation_callbacks* pAllocationCallbacks) -{ - ma_dr_mp3 mp3; - if (!ma_dr_mp3_init_file(&mp3, filePath, pAllocationCallbacks)) { - return NULL; - } - return ma_dr_mp3__full_read_and_close_s16(&mp3, pConfig, pTotalFrameCount); -} -#endif -MA_API void* ma_dr_mp3_malloc(size_t sz, const ma_allocation_callbacks* pAllocationCallbacks) -{ - if (pAllocationCallbacks != NULL) { - return ma_dr_mp3__malloc_from_callbacks(sz, pAllocationCallbacks); - } else { - return ma_dr_mp3__malloc_default(sz, NULL); - } -} -MA_API void ma_dr_mp3_free(void* p, const ma_allocation_callbacks* pAllocationCallbacks) -{ - if (pAllocationCallbacks != NULL) { - ma_dr_mp3__free_from_callbacks(p, pAllocationCallbacks); - } else { - ma_dr_mp3__free_default(p, NULL); - } -} -#endif -/* dr_mp3_c end */ -#endif /* MA_DR_MP3_IMPLEMENTATION */ -#endif /* MA_NO_MP3 */ - - -/* End globally disabled warnings. */ -#if defined(_MSC_VER) - #pragma warning(pop) -#endif - -#endif /* miniaudio_c */ -#endif /* MINIAUDIO_IMPLEMENTATION */ - - -/* -This software is available as a choice of the following licenses. Choose -whichever you prefer. - -=============================================================================== -ALTERNATIVE 1 - Public Domain (www.unlicense.org) -=============================================================================== -This is free and unencumbered software released into the public domain. - -Anyone is free to copy, modify, publish, use, compile, sell, or distribute this -software, either in source code form or as a compiled binary, for any purpose, -commercial or non-commercial, and by any means. - -In jurisdictions that recognize copyright laws, the author or authors of this -software dedicate any and all copyright interest in the software to the public -domain. We make this dedication for the benefit of the public at large and to -the detriment of our heirs and successors. We intend this dedication to be an -overt act of relinquishment in perpetuity of all present and future rights to -this software under copyright law. - -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 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. - -For more information, please refer to - -=============================================================================== -ALTERNATIVE 2 - MIT No Attribution -=============================================================================== -Copyright 2025 David Reid - -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. - -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. -*/ diff --git a/tests-cmake/main.cpp b/tests-cmake/main.cpp new file mode 100644 index 0000000000..ba8440093d --- /dev/null +++ b/tests-cmake/main.cpp @@ -0,0 +1,9 @@ +#ifdef IS_DESKTOP + +#include "Arduino.h" +int main(){ + setup(); + while(true) loop(); +} + +#endif \ No newline at end of file diff --git a/tests-cmake/miniaudio/CMakeLists.txt b/tests-cmake/miniaudio/CMakeLists.txt index 0a83cbbdf8..036b780c77 100644 --- a/tests-cmake/miniaudio/CMakeLists.txt +++ b/tests-cmake/miniaudio/CMakeLists.txt @@ -10,7 +10,6 @@ set (CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} -O0 -ldl -lpthread -lm") # Emulator is not necessary for -DIS_MIN_DESKTOP set(ADD_ARDUINO_EMULATOR OFF CACHE BOOL "Add Arduino Emulator Library") -set(ADD_PORTAUDIO OFF CACHE BOOL "No Portaudio") # Build with arduino-audio-tools if(CMAKE_SOURCE_DIR STREQUAL CMAKE_CURRENT_SOURCE_DIR) @@ -26,7 +25,7 @@ file(DOWNLOAD https://raw.githubusercontent.com/mackron/miniaudio/master/miniaud add_executable (generator generator.cpp) # set preprocessor defines -target_compile_definitions(generator PUBLIC -DIS_MIN_DESKTOP) +target_compile_definitions(generator PUBLIC -DEXIT_ON_STOP -DIS_MIN_DESKTOP) # specify libraries target_link_libraries(generator arduino-audio-tools) diff --git a/tests-cmake/miniaudio/generator.cpp b/tests-cmake/miniaudio/generator.cpp index 833d1a51bb..b1e44abe07 100644 --- a/tests-cmake/miniaudio/generator.cpp +++ b/tests-cmake/miniaudio/generator.cpp @@ -1,7 +1,7 @@ // Simple wrapper for Arduino sketch to compilable with cpp in cmake #include "AudioTools.h" -#include "AudioTools/AudioLibs/MiniAudioStream.h" -#include "AudioTools/Disk/SDtdioStream.h" +#include "AudioLibs/MiniAudioStream.h" +#include "AudioLibs/StdioStream.h" //LinuxStdio out; // Output to stdio on Desktop MiniAudioStream out; // Output to MiniAudioStream @@ -10,7 +10,7 @@ GeneratedSoundStream in_stream(sine_wave); // Stream generated from sin StreamCopy copier(out, in_stream); // copies sound to out void setup(){ - AudioToolsLogger.begin(Serial, AudioToolsLogLevel::Info); + AudioLogger::instance().begin(Serial, AudioLogger::Warning); auto cfg = out.defaultConfig(TX_MODE); //cfg.sample_rate = 22000; diff --git a/tests-cmake/codec/hls/miniaudio.h b/tests-cmake/miniaudio/miniaudio.h similarity index 82% rename from tests-cmake/codec/hls/miniaudio.h rename to tests-cmake/miniaudio/miniaudio.h index c74bebeb3c..dbe8e4d1e0 100644 --- a/tests-cmake/codec/hls/miniaudio.h +++ b/tests-cmake/miniaudio/miniaudio.h @@ -1,6 +1,6 @@ /* Audio playback and capture library. Choice of public domain or MIT-0. See license statements at the end of this file. -miniaudio - v0.11.22 - 2025-02-24 +miniaudio - v0.11.15 - 2023-04-30 David Reid - mackron@gmail.com @@ -12,18 +12,15 @@ GitHub: https://github.com/mackron/miniaudio /* 1. Introduction =============== -To use miniaudio, include "miniaudio.h": +miniaudio is a single file library for audio playback and capture. To use it, do the following in +one .c file: ```c + #define MINIAUDIO_IMPLEMENTATION #include "miniaudio.h" ``` -The implementation is contained in "miniaudio.c". Just compile this like any other source file. You -can include miniaudio.c if you want to compile your project as a single translation unit: - - ```c - #include "miniaudio.c" - ``` +You can do `#include "miniaudio.h"` in other parts of the program just like any other header. miniaudio includes both low level and high level APIs. The low level API is good for those who want to do all of their mixing themselves and only require a light weight interface to the underlying @@ -90,7 +87,7 @@ device on the stack, but you could allocate it on the heap if that suits your si // Do something here. Probably your program's main loop. - ma_device_uninit(&device); + ma_device_uninit(&device); // This will stop the device so no need to do that manually. return 0; } ``` @@ -296,7 +293,7 @@ avoids the same sound being loaded multiple times. The node graph is used for mixing and effect processing. The idea is that you connect a number of nodes into the graph by connecting each node's outputs to another node's inputs. Each node can -implement its own effect. By chaining nodes together, advanced mixing and effect processing can +implement it's own effect. By chaining nodes together, advanced mixing and effect processing can be achieved. The engine encapsulates both the resource manager and the node graph to create a simple, easy to @@ -401,7 +398,7 @@ the be started and/or stopped at a specific time. This can be done with the foll ``` The start/stop time needs to be specified based on the absolute timer which is controlled by the -engine. The current global time in PCM frames can be retrieved with +engine. The current global time time in PCM frames can be retrieved with `ma_engine_get_time_in_pcm_frames()`. The engine's global time can be changed with `ma_engine_set_time_in_pcm_frames()` for synchronization purposes if required. Note that scheduling a start time still requires an explicit call to `ma_sound_start()` before anything will play: @@ -433,11 +430,11 @@ Sounds and sound groups are nodes in the engine's node graph and can be plugged API. This makes it possible to connect sounds and sound groups to effect nodes to produce complex effect chains. -A sound can have its volume changed with `ma_sound_set_volume()`. If you prefer decibel volume +A sound can have it's volume changed with `ma_sound_set_volume()`. If you prefer decibel volume control you can use `ma_volume_db_to_linear()` to convert from decibel representation to linear. Panning and pitching is supported with `ma_sound_set_pan()` and `ma_sound_set_pitch()`. If you know -a sound will never have its pitch changed with `ma_sound_set_pitch()` or via the doppler effect, +a sound will never have it's pitch changed with `ma_sound_set_pitch()` or via the doppler effect, you can specify the `MA_SOUND_FLAG_NO_PITCH` flag when initializing the sound for an optimization. By default, sounds and sound groups have spatialization enabled. If you don't ever want to @@ -486,12 +483,21 @@ link the relevant frameworks but should compile cleanly out of the box with Xcod through the command line requires linking to `-lpthread` and `-lm`. Due to the way miniaudio links to frameworks at runtime, your application may not pass Apple's -notarization process. To fix this there are two options. The first is to compile with -`-DMA_NO_RUNTIME_LINKING` which in turn will require linking with -`-framework CoreFoundation -framework CoreAudio -framework AudioToolbox`. If you get errors about -AudioToolbox, try with `-framework AudioUnit` instead. You may get this when using older versions -of iOS. Alternatively, if you would rather keep using runtime linking you can add the following to -your entitlements.xcent file: +notarization process. To fix this there are two options. The first is to use the +`MA_NO_RUNTIME_LINKING` option, like so: + + ```c + #ifdef __APPLE__ + #define MA_NO_RUNTIME_LINKING + #endif + #define MINIAUDIO_IMPLEMENTATION + #include "miniaudio.h" + ``` + +This will require linking with `-framework CoreFoundation -framework CoreAudio -framework AudioToolbox`. +If you get errors about AudioToolbox, try with `-framework AudioUnit` instead. You may get this when +using older versions of iOS. Alternatively, if you would rather keep using runtime linking you can +add the following to your entitlements.xcent file: ``` com.apple.security.cs.allow-dyld-environment-variables @@ -532,24 +538,10 @@ you'll need to disable run-time linking with `MA_NO_RUNTIME_LINKING` and link wi The Emscripten build emits Web Audio JavaScript directly and should compile cleanly out of the box. You cannot use `-std=c*` compiler flags, nor `-ansi`. -You can enable the use of AudioWorkets by defining `MA_ENABLE_AUDIO_WORKLETS` and then compiling -with the following options: - - -sAUDIO_WORKLET=1 -sWASM_WORKERS=1 -sASYNCIFY - -An example for compiling with AudioWorklet support might look like this: - - emcc program.c -o bin/program.html -DMA_ENABLE_AUDIO_WORKLETS -sAUDIO_WORKLET=1 -sWASM_WORKERS=1 -sASYNCIFY - -To run locally, you'll need to use emrun: - - emrun bin/program.html - - 2.7. Build Options ------------------ -`#define` these options before including miniaudio.c, or pass them as compiler flags: +`#define` these options before including miniaudio.h. +----------------------------------+--------------------------------------------------------------------+ | Option | Description | @@ -580,8 +572,6 @@ To run locally, you'll need to use emrun: +----------------------------------+--------------------------------------------------------------------+ | MA_NO_WEBAUDIO | Disables the Web Audio backend. | +----------------------------------+--------------------------------------------------------------------+ - | MA_NO_CUSTOM | Disables support for custom backends. | - +----------------------------------+--------------------------------------------------------------------+ | MA_NO_NULL | Disables the null backend. | +----------------------------------+--------------------------------------------------------------------+ | MA_ENABLE_ONLY_SPECIFIC_BACKENDS | Disables all backends by default and requires `MA_ENABLE_*` to | @@ -626,9 +616,6 @@ To run locally, you'll need to use emrun: | MA_ENABLE_WEBAUDIO | Used in conjunction with MA_ENABLE_ONLY_SPECIFIC_BACKENDS to | | | enable the Web Audio backend. | +----------------------------------+--------------------------------------------------------------------+ - | MA_ENABLE_CUSTOM | Used in conjunction with MA_ENABLE_ONLY_SPECIFIC_BACKENDS to | - | | enable custom backends. | - +----------------------------------+--------------------------------------------------------------------+ | MA_ENABLE_NULL | Used in conjunction with MA_ENABLE_ONLY_SPECIFIC_BACKENDS to | | | enable the null backend. | +----------------------------------+--------------------------------------------------------------------+ @@ -692,30 +679,11 @@ To run locally, you'll need to use emrun: | | You may need to enable this if your target platform does not allow | | | runtime linking via `dlopen()`. | +----------------------------------+--------------------------------------------------------------------+ - | MA_USE_STDINT | (Pass this in as a compiler flag. Do not `#define` this before | - | | miniaudio.c) Forces the use of stdint.h for sized types. | - +----------------------------------+--------------------------------------------------------------------+ | MA_DEBUG_OUTPUT | Enable `printf()` output of debug logs (`MA_LOG_LEVEL_DEBUG`). | +----------------------------------+--------------------------------------------------------------------+ | MA_COINIT_VALUE | Windows only. The value to pass to internal calls to | | | `CoInitializeEx()`. Defaults to `COINIT_MULTITHREADED`. | +----------------------------------+--------------------------------------------------------------------+ - | MA_FORCE_UWP | Windows only. Affects only the WASAPI backend. Will force the | - | | WASAPI backend to use the UWP code path instead of the regular | - | | desktop path. This is normally auto-detected and should rarely be | - | | needed to be used explicitly, but can be useful for debugging. | - +----------------------------------+--------------------------------------------------------------------+ - | MA_ON_THREAD_ENTRY | Defines some code that will be executed as soon as an internal | - | | miniaudio-managed thread is created. This will be the first thing | - | | to be executed by the thread entry point. | - +----------------------------------+--------------------------------------------------------------------+ - | MA_ON_THREAD_EXIT | Defines some code that will be executed from the entry point of an | - | | internal miniaudio-managed thread upon exit. This will be the last | - | | thing to be executed before the thread's entry point exits. | - +----------------------------------+--------------------------------------------------------------------+ - | MA_THREAD_DEFAULT_STACK_SIZE | If set, specifies the default stack size used by miniaudio-managed | - | | threads. | - +----------------------------------+--------------------------------------------------------------------+ | MA_API | Controls how public APIs should be decorated. Default is `extern`. | +----------------------------------+--------------------------------------------------------------------+ @@ -1327,7 +1295,7 @@ only works for sounds that were initialized with `ma_sound_init_from_file()` and When you initialize a sound, if you specify a sound group the sound will be attached to that group automatically. If you set it to NULL, it will be automatically attached to the engine's endpoint. -If you would instead rather leave the sound unattached by default, you can specify the +If you would instead rather leave the sound unattached by default, you can can specify the `MA_SOUND_FLAG_NO_DEFAULT_ATTACHMENT` flag. This is useful if you want to set up a complex node graph. @@ -1693,7 +1661,7 @@ an example for initializing a data source: // ... - ma_resource_manager_data_source_uninit(&dataSource); + ma_resource_manager_data_source_uninit(pResourceManager, &dataSource); ``` The `flags` parameter specifies how you want to perform loading of the sound file. It can be a @@ -1704,7 +1672,6 @@ combination of the following flags: MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_DECODE MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_ASYNC MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_WAIT_INIT - MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_LOOPING ``` When no flags are specified (set to 0), the sound will be fully loaded into memory, but not @@ -1725,14 +1692,6 @@ can instead stream audio data which you can do by specifying the second pages. When a new page needs to be decoded, a job will be posted to the job queue and then subsequently processed in a job thread. -The `MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_LOOPING` flag can be used so that the sound will loop -when it reaches the end by default. It's recommended you use this flag when you want to have a -looping streaming sound. If you try loading a very short sound as a stream, you will get a glitch. -This is because the resource manager needs to pre-fill the initial buffer at initialization time, -and if you don't specify the `MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_LOOPING` flag, the resource -manager will assume the sound is not looping and will stop filling the buffer when it reaches the -end, therefore resulting in a discontinuous buffer. - For in-memory sounds, reference counting is used to ensure the data is loaded only once. This means multiple calls to `ma_resource_manager_data_source_init()` with the same file path will result in the file data only being loaded once. Each call to `ma_resource_manager_data_source_init()` must be @@ -1747,7 +1706,7 @@ actual file paths. When `ma_resource_manager_data_source_init()` is called (with `MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_STREAM` flag), the resource manager will look for these explicitly registered data buffers and, if found, will use it as the backing data for the data source. Note that the resource manager does *not* make a copy of this data so it is up to the -caller to ensure the pointer stays valid for its lifetime. Use +caller to ensure the pointer stays valid for it's lifetime. Use `ma_resource_manager_unregister_data()` to unregister the self-managed data. You can also use `ma_resource_manager_register_file()` and `ma_resource_manager_unregister_file()` to register and unregister a file. It does not make sense to use the `MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_STREAM` @@ -1939,10 +1898,10 @@ once after the other: ```c ma_resource_manager_data_source_init(pResourceManager, "my_file", ..., &myDataBuffer0); // Refcount = 1. Initial load. - ma_resource_manager_data_source_uninit(&myDataBuffer0); // Refcount = 0. Unloaded. + ma_resource_manager_data_source_uninit(pResourceManager, &myDataBuffer0); // Refcount = 0. Unloaded. ma_resource_manager_data_source_init(pResourceManager, "my_file", ..., &myDataBuffer1); // Refcount = 1. Reloaded because previous uninit() unloaded it. - ma_resource_manager_data_source_uninit(&myDataBuffer1); // Refcount = 0. Unloaded. + ma_resource_manager_data_source_uninit(pResourceManager, &myDataBuffer1); // Refcount = 0. Unloaded. ``` A binary search tree (BST) is used for storing data buffers as it has good balance between @@ -2058,7 +2017,7 @@ In the above graph, it starts with two data sources whose outputs are attached t splitter node. It's at this point that the two data sources are mixed. After mixing, the splitter performs it's processing routine and produces two outputs which is simply a duplication of the input stream. One output is attached to a low pass filter, whereas the other output is attached to -a echo/delay. The outputs of the low pass filter and the echo are attached to the endpoint, and +a echo/delay. The outputs of the the low pass filter and the echo are attached to the endpoint, and since they're both connected to the same input bus, they'll be mixed. Each input bus must be configured to accept the same number of channels, but the number of channels @@ -2099,7 +2058,7 @@ data from the graph: ``` When you read audio data, miniaudio starts at the node graph's endpoint node which then pulls in -data from its input attachments, which in turn recursively pull in data from their inputs, and so +data from it's input attachments, which in turn recursively pull in data from their inputs, and so on. At the start of the graph there will be some kind of data source node which will have zero inputs and will instead read directly from a data source. The base nodes don't literally need to read from a `ma_data_source` object, but they will always have some kind of underlying object that @@ -2345,7 +2304,7 @@ You can start and stop a node with the following: By default the node is in a started state, but since it won't be connected to anything won't actually be invoked by the node graph until it's connected. When you stop a node, data will not be -read from any of its input connections. You can use this property to stop a group of sounds +read from any of it's input connections. You can use this property to stop a group of sounds atomically. You can configure the initial state of a node in it's config: @@ -2438,29 +2397,29 @@ audio thread is finished so that control is not handed back to the caller thereb chance to free the node's memory. When the audio thread is processing a node, it does so by reading from each of the output buses of -the node. In order for a node to process data for one of its output buses, it needs to read from -each of its input buses, and so on an so forth. It follows that once all output buses of a node +the node. In order for a node to process data for one of it's output buses, it needs to read from +each of it's input buses, and so on an so forth. It follows that once all output buses of a node are detached, the node as a whole will be disconnected and no further processing will occur unless it's output buses are reattached, which won't be happening when the node is being uninitialized. By having `ma_node_detach_output_bus()` wait until the audio thread is finished with it, we can simplify a few things, at the expense of making `ma_node_detach_output_bus()` a bit slower. By doing this, the implementation of `ma_node_uninit()` becomes trivial - just detach all output -nodes, followed by each of the attachments to each of its input nodes, and then do any final clean +nodes, followed by each of the attachments to each of it's input nodes, and then do any final clean up. With the above design, the worst-case scenario is `ma_node_detach_output_bus()` taking as long as it takes to process the output bus being detached. This will happen if it's called at just the wrong moment where the audio thread has just iterated it and has just started processing. The caller of `ma_node_detach_output_bus()` will stall until the audio thread is finished, which -includes the cost of recursively processing its inputs. This is the biggest compromise made with -the approach taken by miniaudio for its lock-free processing system. The cost of detaching nodes +includes the cost of recursively processing it's inputs. This is the biggest compromise made with +the approach taken by miniaudio for it's lock-free processing system. The cost of detaching nodes earlier in the pipeline (data sources, for example) will be cheaper than the cost of detaching higher level nodes, such as some kind of final post-processing endpoint. If you need to do mass detachments, detach starting from the lowest level nodes and work your way towards the final endpoint node (but don't try detaching the node graph's endpoint). If the audio thread is not running, detachment will be fast and detachment in any order will be the same. The reason nodes need to wait for their input attachments to complete is due to the potential for desyncs between -data sources. If the node was to terminate processing mid way through processing its inputs, +data sources. If the node was to terminate processing mid way through processing it's inputs, there's a chance that some of the underlying data sources will have been read, but then others not. That will then result in a potential desynchronization when detaching and reattaching higher-level nodes. A possible solution to this is to have an option when detaching to terminate processing @@ -2501,18 +2460,37 @@ used. The same general process applies to detachment. See `ma_node_attach_output 8. Decoding =========== The `ma_decoder` API is used for reading audio files. Decoders are completely decoupled from -devices and can be used independently. Built-in support is included for the following formats: +devices and can be used independently. The following formats are supported: - +---------+ - | Format | - +---------+ - | WAV | - | MP3 | - | FLAC | - +---------+ + +---------+------------------+----------+ + | Format | Decoding Backend | Built-In | + +---------+------------------+----------+ + | WAV | dr_wav | Yes | + | MP3 | dr_mp3 | Yes | + | FLAC | dr_flac | Yes | + | Vorbis | stb_vorbis | No | + +---------+------------------+----------+ -You can disable the built-in decoders by specifying one or more of the following options before the -miniaudio implementation: +Vorbis is supported via stb_vorbis which can be enabled by including the header section before the +implementation of miniaudio, like the following: + + ```c + #define STB_VORBIS_HEADER_ONLY + #include "extras/stb_vorbis.c" // Enables Vorbis decoding. + + #define MINIAUDIO_IMPLEMENTATION + #include "miniaudio.h" + + // The stb_vorbis implementation must come after the implementation of miniaudio. + #undef STB_VORBIS_HEADER_ONLY + #include "extras/stb_vorbis.c" + ``` + +A copy of stb_vorbis is included in the "extras" folder in the miniaudio repository (https://github.com/mackron/miniaudio). + +Built-in decoders are amalgamated into the implementation section of miniaudio. You can disable the +built-in decoders by specifying one or more of the following options before the miniaudio +implementation: ```c #define MA_NO_WAV @@ -2520,8 +2498,8 @@ miniaudio implementation: #define MA_NO_FLAC ``` -miniaudio supports the ability to plug in custom decoders. See the section below for details on how -to use custom decoders. +Disabling built-in decoding libraries is useful if you use these libraries independently of the +`ma_decoder` API. A decoder can be initialized from a file with `ma_decoder_init_file()`, a block of memory with `ma_decoder_init_memory()`, or from data delivered via callbacks with `ma_decoder_init()`. Here is @@ -2662,7 +2640,8 @@ opportunity to clean up and internal data. 9. Encoding =========== -The `ma_encoding` API is used for writing audio files. The only supported output format is WAV. +The `ma_encoding` API is used for writing audio files. The only supported output format is WAV +which is achieved via dr_wav which is amalgamated into the implementation section of miniaudio. This can be disabled by specifying the following option before the implementation of miniaudio: ```c @@ -2702,16 +2681,9 @@ outputting any audio data. To output audio data, use `ma_encoder_write_pcm_frame example below: ```c - ma_uint64 framesWritten; - result = ma_encoder_write_pcm_frames(&encoder, pPCMFramesToWrite, framesToWrite, &framesWritten); - if (result != MA_SUCCESS) { - ... handle error ... - } + framesWritten = ma_encoder_write_pcm_frames(&encoder, pPCMFramesToWrite, framesToWrite); ``` -The `framesWritten` variable will contain the number of PCM frames that were actually written. This -is optionally and you can pass in `NULL` if you need this. - Encoders must be uninitialized with `ma_encoder_uninit()`. @@ -2831,7 +2803,7 @@ weights. Custom weights can be passed in as the last parameter of `ma_channel_converter_config_init()`. Predefined channel maps can be retrieved with `ma_channel_map_init_standard()`. This takes a -`ma_standard_channel_map` enum as its first parameter, which can be one of the following: +`ma_standard_channel_map` enum as it's first parameter, which can be one of the following: +-----------------------------------+-----------------------------------------------------------+ | Name | Description | @@ -2917,7 +2889,7 @@ like the following: ma_resample_algorithm_linear); ma_resampler resampler; - ma_result result = ma_resampler_init(&config, NULL, &resampler); + ma_result result = ma_resampler_init(&config, &resampler); if (result != MA_SUCCESS) { // An error occurred... } @@ -3159,7 +3131,7 @@ Biquad filtering is achieved with the `ma_biquad` API. Example: ```c ma_biquad_config config = ma_biquad_config_init(ma_format_f32, channels, b0, b1, b2, a0, a1, a2); - ma_result result = ma_biquad_init(&config, NULL, &biquad); + ma_result result = ma_biquad_init(&config, &biquad); if (result != MA_SUCCESS) { // Error. } @@ -3443,7 +3415,7 @@ miniaudio supports reading from a buffer of raw audio data via the `ma_audio_buf read from memory that's managed by the application, but can also handle the memory management for you internally. Memory management is flexible and should support most use cases. -Audio buffers are initialized using the standard configuration system used everywhere in miniaudio: +Audio buffers are initialised using the standard configuration system used everywhere in miniaudio: ```c ma_audio_buffer_config config = ma_audio_buffer_config_init( @@ -3750,7 +3722,7 @@ extern "C" { #define MA_VERSION_MAJOR 0 #define MA_VERSION_MINOR 11 -#define MA_VERSION_REVISION 22 +#define MA_VERSION_REVISION 15 #define MA_VERSION_STRING MA_XSTRINGIFY(MA_VERSION_MAJOR) "." MA_XSTRINGIFY(MA_VERSION_MINOR) "." MA_XSTRINGIFY(MA_VERSION_REVISION) #if defined(_MSC_VER) && !defined(__clang__) @@ -3767,7 +3739,8 @@ extern "C" { #endif -#if defined(__LP64__) || defined(_WIN64) || (defined(__x86_64__) && !defined(__ILP32__)) || defined(_M_X64) || defined(__ia64) || defined(_M_IA64) || defined(__aarch64__) || defined(_M_ARM64) || defined(__powerpc64__) || defined(__ppc64__) + +#if defined(__LP64__) || defined(_WIN64) || (defined(__x86_64__) && !defined(__ILP32__)) || defined(_M_X64) || defined(__ia64) || defined(_M_IA64) || defined(__aarch64__) || defined(_M_ARM64) || defined(__powerpc64__) #define MA_SIZEOF_PTR 8 #else #define MA_SIZEOF_PTR 4 @@ -3831,7 +3804,7 @@ typedef void* ma_handle; typedef void* ma_ptr; /* -ma_proc is annoying because when compiling with GCC we get pedantic warnings about converting +ma_proc is annoying because when compiling with GCC we get pendantic warnings about converting between `void*` and `void (*)()`. We can't use `void (*)()` with MSVC however, because we'll get warning C4191 about "type cast between incompatible function types". To work around this I'm going to use a different data type depending on the compiler. @@ -3941,13 +3914,6 @@ typedef ma_uint16 wchar_t; #ifdef _MSC_VER #define MA_INLINE __forceinline - - /* noinline was introduced in Visual Studio 2005. */ - #if _MSC_VER >= 1400 - #define MA_NO_INLINE __declspec(noinline) - #else - #define MA_NO_INLINE - #endif #elif defined(__GNUC__) /* I've had a bug report where GCC is emitting warnings about functions possibly not being inlineable. This warning happens when @@ -3964,59 +3930,45 @@ typedef ma_uint16 wchar_t; #if (__GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 2)) || defined(__clang__) #define MA_INLINE MA_GNUC_INLINE_HINT __attribute__((always_inline)) - #define MA_NO_INLINE __attribute__((noinline)) #else #define MA_INLINE MA_GNUC_INLINE_HINT - #define MA_NO_INLINE __attribute__((noinline)) #endif #elif defined(__WATCOMC__) #define MA_INLINE __inline - #define MA_NO_INLINE #else #define MA_INLINE - #define MA_NO_INLINE #endif -/* MA_DLL is not officially supported. You're on your own if you want to use this. */ -#if defined(MA_DLL) - #if defined(_WIN32) - #define MA_DLL_IMPORT __declspec(dllimport) - #define MA_DLL_EXPORT __declspec(dllexport) - #define MA_DLL_PRIVATE static - #else - #if defined(__GNUC__) && __GNUC__ >= 4 - #define MA_DLL_IMPORT __attribute__((visibility("default"))) - #define MA_DLL_EXPORT __attribute__((visibility("default"))) - #define MA_DLL_PRIVATE __attribute__((visibility("hidden"))) - #else - #define MA_DLL_IMPORT - #define MA_DLL_EXPORT +#if !defined(MA_API) + #if defined(MA_DLL) + #if defined(_WIN32) + #define MA_DLL_IMPORT __declspec(dllimport) + #define MA_DLL_EXPORT __declspec(dllexport) #define MA_DLL_PRIVATE static + #else + #if defined(__GNUC__) && __GNUC__ >= 4 + #define MA_DLL_IMPORT __attribute__((visibility("default"))) + #define MA_DLL_EXPORT __attribute__((visibility("default"))) + #define MA_DLL_PRIVATE __attribute__((visibility("hidden"))) + #else + #define MA_DLL_IMPORT + #define MA_DLL_EXPORT + #define MA_DLL_PRIVATE static + #endif #endif - #endif -#endif -#if !defined(MA_API) - #if defined(MA_DLL) #if defined(MINIAUDIO_IMPLEMENTATION) || defined(MA_IMPLEMENTATION) #define MA_API MA_DLL_EXPORT #else #define MA_API MA_DLL_IMPORT #endif - #else - #define MA_API extern - #endif -#endif - -#if !defined(MA_STATIC) - #if defined(MA_DLL) #define MA_PRIVATE MA_DLL_PRIVATE #else + #define MA_API extern #define MA_PRIVATE static #endif #endif - /* SIMD alignment in bytes. Currently set to 32 bytes in preparation for future AVX optimizations. */ #define MA_SIMD_ALIGNMENT 32 @@ -4025,7 +3977,7 @@ Special wchar_t type to ensure any structures in the public sections that refere consistent size across all platforms. On Windows, wchar_t is 2 bytes, whereas everywhere else it's 4 bytes. Since Windows likes to use -wchar_t for its IDs, we need a special explicitly sized wchar type that is always 2 bytes on all +wchar_t for it's IDs, we need a special explicitly sized wchar type that is always 2 bytes on all platforms. */ #if !defined(MA_POSIX) && defined(MA_WIN32) @@ -4051,7 +4003,7 @@ MA_LOG_LEVEL_INFO callback. MA_LOG_LEVEL_WARNING - Warnings. You should enable this in you development builds and action them when encountered. These + Warnings. You should enable this in you development builds and action them when encounted. These logs usually indicate a potential problem or misconfiguration, but still allow you to keep running. This will never be called from within the data callback. @@ -4214,31 +4166,28 @@ typedef enum MA_CANCELLED = -51, MA_MEMORY_ALREADY_MAPPED = -52, - /* General non-standard errors. */ - MA_CRC_MISMATCH = -100, - /* General miniaudio-specific errors. */ - MA_FORMAT_NOT_SUPPORTED = -200, - MA_DEVICE_TYPE_NOT_SUPPORTED = -201, - MA_SHARE_MODE_NOT_SUPPORTED = -202, - MA_NO_BACKEND = -203, - MA_NO_DEVICE = -204, - MA_API_NOT_FOUND = -205, - MA_INVALID_DEVICE_CONFIG = -206, - MA_LOOP = -207, - MA_BACKEND_NOT_ENABLED = -208, + MA_FORMAT_NOT_SUPPORTED = -100, + MA_DEVICE_TYPE_NOT_SUPPORTED = -101, + MA_SHARE_MODE_NOT_SUPPORTED = -102, + MA_NO_BACKEND = -103, + MA_NO_DEVICE = -104, + MA_API_NOT_FOUND = -105, + MA_INVALID_DEVICE_CONFIG = -106, + MA_LOOP = -107, + MA_BACKEND_NOT_ENABLED = -108, /* State errors. */ - MA_DEVICE_NOT_INITIALIZED = -300, - MA_DEVICE_ALREADY_INITIALIZED = -301, - MA_DEVICE_NOT_STARTED = -302, - MA_DEVICE_NOT_STOPPED = -303, + MA_DEVICE_NOT_INITIALIZED = -200, + MA_DEVICE_ALREADY_INITIALIZED = -201, + MA_DEVICE_NOT_STARTED = -202, + MA_DEVICE_NOT_STOPPED = -203, /* Operation errors. */ - MA_FAILED_TO_INIT_BACKEND = -400, - MA_FAILED_TO_OPEN_BACKEND_DEVICE = -401, - MA_FAILED_TO_START_BACKEND_DEVICE = -402, - MA_FAILED_TO_STOP_BACKEND_DEVICE = -403 + MA_FAILED_TO_INIT_BACKEND = -300, + MA_FAILED_TO_OPEN_BACKEND_DEVICE = -301, + MA_FAILED_TO_START_BACKEND_DEVICE = -302, + MA_FAILED_TO_STOP_BACKEND_DEVICE = -303 } ma_result; @@ -4300,7 +4249,7 @@ typedef enum ma_standard_sample_rate_192000 = 192000, ma_standard_sample_rate_16000 = 16000, /* Extreme lows */ - ma_standard_sample_rate_11025 = 11025, + ma_standard_sample_rate_11025 = 11250, ma_standard_sample_rate_8000 = 8000, ma_standard_sample_rate_352800 = 352800, /* Extreme highs */ @@ -5090,14 +5039,13 @@ typedef struct float volumeBeg; /* If volumeBeg and volumeEnd is equal to 1, no fading happens (ma_fader_process_pcm_frames() will run as a passthrough). */ float volumeEnd; ma_uint64 lengthInFrames; /* The total length of the fade. */ - ma_int64 cursorInFrames; /* The current time in frames. Incremented by ma_fader_process_pcm_frames(). Signed because it'll be offset by startOffsetInFrames in set_fade_ex(). */ + ma_uint64 cursorInFrames; /* The current time in frames. Incremented by ma_fader_process_pcm_frames(). */ } ma_fader; MA_API ma_result ma_fader_init(const ma_fader_config* pConfig, ma_fader* pFader); MA_API ma_result ma_fader_process_pcm_frames(ma_fader* pFader, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount); MA_API void ma_fader_get_data_format(const ma_fader* pFader, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate); MA_API void ma_fader_set_fade(ma_fader* pFader, float volumeBeg, float volumeEnd, ma_uint64 lengthInFrames); -MA_API void ma_fader_set_fade_ex(ma_fader* pFader, float volumeBeg, float volumeEnd, ma_uint64 lengthInFrames, ma_int64 startOffsetInFrames); MA_API float ma_fader_get_current_volume(const ma_fader* pFader); @@ -5423,7 +5371,7 @@ MA_API void ma_resampler_uninit(ma_resampler* pResampler, const ma_allocation_ca /* Converts the given input data. -Both the input and output frames must be in the format specified in the config when the resampler was initialized. +Both the input and output frames must be in the format specified in the config when the resampler was initilized. On input, [pFrameCountOut] contains the number of output frames to process. On output it contains the number of output frames that were actually processed, which may be less than the requested amount which will happen if there's not enough input data. You can use @@ -5483,7 +5431,7 @@ input frames. MA_API ma_result ma_resampler_get_expected_output_frame_count(const ma_resampler* pResampler, ma_uint64 inputFrameCount, ma_uint64* pOutputFrameCount); /* -Resets the resampler's timer and clears its internal cache. +Resets the resampler's timer and clears it's internal cache. */ MA_API ma_result ma_resampler_reset(ma_resampler* pResampler); @@ -5704,7 +5652,7 @@ MA_API void ma_channel_map_init_standard(ma_standard_channel_map standardChannel /* Copies a channel map. -Both input and output channel map buffers must have a capacity of at least `channels`. +Both input and output channel map buffers must have a capacity of at at least `channels`. */ MA_API void ma_channel_map_copy(ma_channel* pOut, const ma_channel* pIn, ma_uint32 channels); @@ -5843,8 +5791,6 @@ MA_API void ma_data_source_uninit(ma_data_source* pDataSource); MA_API ma_result ma_data_source_read_pcm_frames(ma_data_source* pDataSource, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead); /* Must support pFramesOut = NULL in which case a forward seek should be performed. */ MA_API ma_result ma_data_source_seek_pcm_frames(ma_data_source* pDataSource, ma_uint64 frameCount, ma_uint64* pFramesSeeked); /* Can only seek forward. Equivalent to ma_data_source_read_pcm_frames(pDataSource, NULL, frameCount, &framesRead); */ MA_API ma_result ma_data_source_seek_to_pcm_frame(ma_data_source* pDataSource, ma_uint64 frameIndex); -MA_API ma_result ma_data_source_seek_seconds(ma_data_source* pDataSource, float secondCount, float* pSecondsSeeked); /* Can only seek forward. Abstraction to ma_data_source_seek_pcm_frames() */ -MA_API ma_result ma_data_source_seek_to_second(ma_data_source* pDataSource, float seekPointInSeconds); /* Abstraction to ma_data_source_seek_to_pcm_frame() */ MA_API ma_result ma_data_source_get_data_format(ma_data_source* pDataSource, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate, ma_channel* pChannelMap, size_t channelMapCap); MA_API ma_result ma_data_source_get_cursor_in_pcm_frames(ma_data_source* pDataSource, ma_uint64* pCursor); MA_API ma_result ma_data_source_get_length_in_pcm_frames(ma_data_source* pDataSource, ma_uint64* pLength); /* Returns MA_NOT_IMPLEMENTED if the length is unknown or cannot be determined. Decoders can return this. */ @@ -6210,12 +6156,6 @@ MA_API ma_result ma_event_wait(ma_event* pEvent); Signals the specified auto-reset event. */ MA_API ma_result ma_event_signal(ma_event* pEvent); - - -MA_API ma_result ma_semaphore_init(int initialValue, ma_semaphore* pSemaphore); -MA_API void ma_semaphore_uninit(ma_semaphore* pSemaphore); -MA_API ma_result ma_semaphore_wait(ma_semaphore* pSemaphore); -MA_API ma_result ma_semaphore_release(ma_semaphore* pSemaphore); #endif /* MA_NO_THREADING */ @@ -6307,7 +6247,7 @@ Job Queue /* Slot Allocator -------------- -The idea of the slot allocator is for it to be used in conjunction with a fixed sized buffer. You use the slot allocator to allocate an index that can be used +The idea of the slot allocator is for it to be used in conjunction with a fixed sized buffer. You use the slot allocator to allocator an index that can be used as the insertion point for an object. Slots are reference counted to help mitigate the ABA problem in the lock-free queue we use for tracking jobs. @@ -6750,8 +6690,7 @@ typedef enum ma_device_notification_type_stopped, ma_device_notification_type_rerouted, ma_device_notification_type_interruption_began, - ma_device_notification_type_interruption_ended, - ma_device_notification_type_unlocked + ma_device_notification_type_interruption_ended } ma_device_notification_type; typedef struct @@ -7040,8 +6979,6 @@ typedef union int nullbackend; /* The null backend uses an integer for device IDs. */ } ma_device_id; -MA_API ma_bool32 ma_device_id_equal(const ma_device_id* pA, const ma_device_id* pB); - typedef struct ma_context_config ma_context_config; typedef struct ma_device_config ma_device_config; @@ -7079,7 +7016,7 @@ struct ma_device_config ma_uint32 periods; ma_performance_profile performanceProfile; ma_bool8 noPreSilencedOutputBuffer; /* When set to true, the contents of the output buffer passed into the data callback will be left undefined rather than initialized to silence. */ - ma_bool8 noClip; /* When set to true, the contents of the output buffer passed into the data callback will not be clipped after returning. Only applies when the playback sample format is f32. */ + ma_bool8 noClip; /* When set to true, the contents of the output buffer passed into the data callback will be clipped after returning. Only applies when the playback sample format is f32. */ ma_bool8 noDisableDenormals; /* Do not disable denormals when firing the data callback. */ ma_bool8 noFixedSizedCallback; /* Disables strict fixed-sized data callbacks. Setting this to true will result in the period size being treated only as a hint to the backend. This is an optimization for those who don't need fixed sized callbacks. */ ma_device_data_proc dataCallback; @@ -7129,7 +7066,6 @@ struct ma_device_config { const char* pStreamNamePlayback; const char* pStreamNameCapture; - int channelMap; } pulse; struct { @@ -7149,13 +7085,12 @@ struct ma_device_config ma_aaudio_allowed_capture_policy allowedCapturePolicy; ma_bool32 noAutoStartAfterReroute; ma_bool32 enableCompatibilityWorkarounds; - ma_bool32 allowSetBufferCapacity; } aaudio; }; /* -The callback for handling device enumeration. This is fired from `ma_context_enumerate_devices()`. +The callback for handling device enumeration. This is fired from `ma_context_enumerated_devices()`. Parameters @@ -7222,7 +7157,7 @@ and on output returns detailed information about the device in `ma_device_info`. case when the device ID is NULL, in which case information about the default device needs to be retrieved. Once the context has been created and the device ID retrieved (if using anything other than the default device), the device can be created. -This is a little bit more complicated than initialization of the context due to its more complicated configuration. When initializing a +This is a little bit more complicated than initialization of the context due to it's more complicated configuration. When initializing a device, a duplex device may be requested. This means a separate data format needs to be specified for both playback and capture. On input, the data format is set to what the application wants. On output it's set to the native format which should match as closely as possible to the requested format. The conversion between the format requested by the application and the device's native format will be handled @@ -7243,10 +7178,10 @@ asynchronous reading and writing, `onDeviceStart()` and `onDeviceStop()` should The handling of data delivery between the application and the device is the most complicated part of the process. To make this a bit easier, some helper callbacks are available. If the backend uses a blocking read/write style of API, the `onDeviceRead()` and `onDeviceWrite()` callbacks can optionally be implemented. These are blocking and work just like reading and writing from a file. If the -backend uses a callback for data delivery, that callback must call `ma_device_handle_backend_data_callback()` from within its callback. +backend uses a callback for data delivery, that callback must call `ma_device_handle_backend_data_callback()` from within it's callback. This allows miniaudio to then process any necessary data conversion and then pass it to the miniaudio data callback. -If the backend requires absolute flexibility with its data delivery, it can optionally implement the `onDeviceDataLoop()` callback +If the backend requires absolute flexibility with it's data delivery, it can optionally implement the `onDeviceDataLoop()` callback which will allow it to implement the logic that will run on the audio thread. This is much more advanced and is completely optional. The audio thread should run data delivery logic in a loop while `ma_device_get_state() == ma_device_state_started` and no errors have been @@ -7286,10 +7221,6 @@ struct ma_context_config void* pUserData; ma_allocation_callbacks allocationCallbacks; struct - { - ma_handle hWnd; /* HWND. Optional window handle to pass into SetCooperativeLevel(). Will default to the foreground window, and if that fails, the desktop window. */ - } dsound; - struct { ma_bool32 useVerboseDeviceEnumeration; } alsa; @@ -7378,7 +7309,6 @@ struct ma_context #ifdef MA_SUPPORT_DSOUND struct { - ma_handle hWnd; /* Can be null. */ ma_handle hDSoundDLL; ma_proc DirectSoundCreate; ma_proc DirectSoundEnumerateA; @@ -7734,8 +7664,6 @@ struct ma_context ma_proc RegOpenKeyExA; ma_proc RegCloseKey; ma_proc RegQueryValueExA; - - /*HRESULT*/ long CoInitializeResult; } win32; #endif #ifdef MA_POSIX @@ -7985,7 +7913,6 @@ struct ma_device { /*AAudioStream**/ ma_ptr pStreamPlayback; /*AAudioStream**/ ma_ptr pStreamCapture; - ma_mutex rerouteLock; ma_aaudio_usage usage; ma_aaudio_content_type contentType; ma_aaudio_input_preset inputPreset; @@ -8016,12 +7943,21 @@ struct ma_device struct { /* AudioWorklets path. */ - /* EMSCRIPTEN_WEBAUDIO_T */ int audioContext; - /* EMSCRIPTEN_WEBAUDIO_T */ int audioWorklet; - float* pIntermediaryBuffer; - void* pStackBuffer; - ma_result initResult; /* Set to MA_BUSY while initialization is in progress. */ - int deviceIndex; /* We store the device in a list on the JavaScript side. This is used to map our C object to the JS object. */ + /* EMSCRIPTEN_WEBAUDIO_T */ int audioContextPlayback; + /* EMSCRIPTEN_WEBAUDIO_T */ int audioContextCapture; + /* EMSCRIPTEN_AUDIO_WORKLET_NODE_T */ int workletNodePlayback; + /* EMSCRIPTEN_AUDIO_WORKLET_NODE_T */ int workletNodeCapture; + size_t intermediaryBufferSizeInFramesPlayback; + size_t intermediaryBufferSizeInFramesCapture; + float* pIntermediaryBufferPlayback; + float* pIntermediaryBufferCapture; + void* pStackBufferPlayback; + void* pStackBufferCapture; + ma_bool32 isInitialized; + + /* ScriptProcessorNode path. */ + int indexPlayback; /* We use a factory on the JavaScript side to manage devices and use an index for JS/C interop. */ + int indexCapture; } webaudio; #endif #ifdef MA_SUPPORT_NULL @@ -8409,10 +8345,6 @@ Retrieves basic information about every active playback and/or capture device. This function will allocate memory internally for the device lists and return a pointer to them through the `ppPlaybackDeviceInfos` and `ppCaptureDeviceInfos` parameters. If you do not want to incur the overhead of these allocations consider using `ma_context_enumerate_devices()` which will instead use a callback. -Note that this only retrieves the ID and name/description of the device. The reason for only retrieving basic information is that it would otherwise require -opening the backend device in order to probe it for more detailed information which can be inefficient. Consider using `ma_context_get_device_info()` for this, -but don't call it from within the enumeration callback. - Parameters ---------- @@ -8454,7 +8386,7 @@ The returned pointers will become invalid upon the next call this this function, See Also -------- -ma_context_enumerate_devices() +ma_context_get_devices() */ MA_API ma_result ma_context_get_devices(ma_context* pContext, ma_device_info** ppPlaybackDeviceInfos, ma_uint32* pPlaybackDeviceCount, ma_device_info** ppCaptureDeviceInfos, ma_uint32* pCaptureDeviceCount); @@ -8593,7 +8525,7 @@ from a microphone. Whether or not you should send or receive data from the devic playback, capture, full-duplex or loopback. (Note that loopback mode is only supported on select backends.) Sending and receiving audio data to and from the device is done via a callback which is fired by miniaudio at periodic time intervals. -The frequency at which data is delivered to and from a device depends on the size of its period. The size of the period can be defined in terms of PCM frames +The frequency at which data is delivered to and from a device depends on the size of it's period. The size of the period can be defined in terms of PCM frames or milliseconds, whichever is more convenient. Generally speaking, the smaller the period, the lower the latency at the expense of higher CPU usage and increased risk of glitching due to the more frequent and granular data deliver intervals. The size of a period will depend on your requirements, but miniaudio's defaults should work fine for most scenarios. If you're building a game you should leave this fairly small, whereas if you're building a simple @@ -8667,7 +8599,7 @@ then be set directly on the structure. Below are the members of the `ma_device_c performanceProfile A hint to miniaudio as to the performance requirements of your program. Can be either `ma_performance_profile_low_latency` (default) or - `ma_performance_profile_conservative`. This mainly affects the size of default buffers and can usually be left at its default value. + `ma_performance_profile_conservative`. This mainly affects the size of default buffers and can usually be left at it's default value. noPreSilencedOutputBuffer When set to true, the contents of the output buffer passed into the data callback will be left undefined. When set to false (default), the contents of @@ -8675,8 +8607,8 @@ then be set directly on the structure. Below are the members of the `ma_device_c callback will write to every sample in the output buffer, or if you are doing your own clearing. noClip - When set to true, the contents of the output buffer are left alone after returning and it will be left up to the backend itself to decide whether or - not to clip. When set to false (default), the contents of the output buffer passed into the data callback will be clipped after returning. This only + When set to true, the contents of the output buffer passed into the data callback will be clipped after returning. When set to false (default), the + contents of the output buffer are left alone after returning and it will be left up to the backend itself to decide whether or not the clip. This only applies when the playback sample format is f32. noDisableDenormals @@ -8707,7 +8639,7 @@ then be set directly on the structure. Below are the members of the `ma_device_c A pointer that will passed to callbacks in pBackendVTable. resampling.linear.lpfOrder - The linear resampler applies a low-pass filter as part of its processing for anti-aliasing. This setting controls the order of the filter. The higher + The linear resampler applies a low-pass filter as part of it's processing for anti-aliasing. This setting controls the order of the filter. The higher the value, the better the quality, in general. Setting this to 0 will disable low-pass filtering altogether. The maximum value is `MA_MAX_FILTER_ORDER`. The default value is `min(4, MA_MAX_FILTER_ORDER)`. @@ -8784,9 +8716,6 @@ then be set directly on the structure. Below are the members of the `ma_device_c pulse.pStreamNameCapture PulseAudio only. Sets the stream name for capture. - pulse.channelMap - PulseAudio only. Sets the channel map that is requested from PulseAudio. See MA_PA_CHANNEL_MAP_* constants. Defaults to MA_PA_CHANNEL_MAP_AIFF. - coreaudio.allowNominalSampleRateChange Core Audio only. Desktop only. When enabled, allows the sample rate of the device to be changed at the operating system level. This is disabled by default in order to prevent intrusive changes to the user's system. This is useful if you want to use a sample rate @@ -8960,7 +8889,7 @@ Unsafe. It is not safe to call this inside any callback. Remarks ------- -You only need to use this function if you want to configure the context differently to its defaults. You should never use this function if you want to manage +You only need to use this function if you want to configure the context differently to it's defaults. You should never use this function if you want to manage your own context. See the documentation for `ma_context_init()` for information on the different context configuration options. @@ -9192,6 +9121,8 @@ speakers or received from the microphone which can in turn result in de-syncs. Do not call this in any callback. +This will be called implicitly by `ma_device_uninit()`. + See Also -------- @@ -9725,7 +9656,7 @@ Utilities ************************************************************************************************************************************************************/ /* -Calculates a buffer size in milliseconds (rounded up) from the specified number of frames and sample rate. +Calculates a buffer size in milliseconds from the specified number of frames and sample rate. */ MA_API ma_uint32 ma_calculate_buffer_size_in_milliseconds_from_frames(ma_uint32 bufferSizeInFrames, ma_uint32 sampleRate); @@ -9982,7 +9913,7 @@ struct ma_decoder void* pInputCache; /* In input format. Can be null if it's not needed. */ ma_uint64 inputCacheCap; /* The capacity of the input cache. */ ma_uint64 inputCacheConsumed; /* The number of frames that have been consumed in the cache. Used for determining the next valid frame. */ - ma_uint64 inputCacheRemaining; /* The number of valid frames remaining in the cache. */ + ma_uint64 inputCacheRemaining; /* The number of valid frames remaining in the cahce. */ ma_allocation_callbacks allocationCallbacks; union { @@ -10023,7 +9954,7 @@ This is not thread safe without your own synchronization. MA_API ma_result ma_decoder_read_pcm_frames(ma_decoder* pDecoder, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead); /* -Seeks to a PCM frame based on its absolute index. +Seeks to a PCM frame based on it's absolute index. This is not thread safe without your own synchronization. */ @@ -10114,7 +10045,7 @@ struct ma_encoder ma_encoder_uninit_proc onUninit; ma_encoder_write_pcm_frames_proc onWritePCMFrames; void* pUserData; - void* pInternalEncoder; + void* pInternalEncoder; /* <-- The drwav/drflac/stb_vorbis/etc. objects. */ union { struct @@ -10179,33 +10110,6 @@ MA_API ma_result ma_waveform_set_frequency(ma_waveform* pWaveform, double freque MA_API ma_result ma_waveform_set_type(ma_waveform* pWaveform, ma_waveform_type type); MA_API ma_result ma_waveform_set_sample_rate(ma_waveform* pWaveform, ma_uint32 sampleRate); -typedef struct -{ - ma_format format; - ma_uint32 channels; - ma_uint32 sampleRate; - double dutyCycle; - double amplitude; - double frequency; -} ma_pulsewave_config; - -MA_API ma_pulsewave_config ma_pulsewave_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRate, double dutyCycle, double amplitude, double frequency); - -typedef struct -{ - ma_waveform waveform; - ma_pulsewave_config config; -} ma_pulsewave; - -MA_API ma_result ma_pulsewave_init(const ma_pulsewave_config* pConfig, ma_pulsewave* pWaveform); -MA_API void ma_pulsewave_uninit(ma_pulsewave* pWaveform); -MA_API ma_result ma_pulsewave_read_pcm_frames(ma_pulsewave* pWaveform, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead); -MA_API ma_result ma_pulsewave_seek_to_pcm_frame(ma_pulsewave* pWaveform, ma_uint64 frameIndex); -MA_API ma_result ma_pulsewave_set_amplitude(ma_pulsewave* pWaveform, double amplitude); -MA_API ma_result ma_pulsewave_set_frequency(ma_pulsewave* pWaveform, double frequency); -MA_API ma_result ma_pulsewave_set_sample_rate(ma_pulsewave* pWaveform, ma_uint32 sampleRate); -MA_API ma_result ma_pulsewave_set_duty_cycle(ma_pulsewave* pWaveform, double dutyCycle); - typedef enum { ma_noise_type_white, @@ -10228,7 +10132,7 @@ MA_API ma_noise_config ma_noise_config_init(ma_format format, ma_uint32 channels typedef struct { - ma_data_source_base ds; + ma_data_source_vtable ds; ma_noise_config config; ma_lcg lcg; union @@ -10286,8 +10190,7 @@ typedef enum MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_DECODE = 0x00000002, /* Decode data before storing in memory. When set, decoding is done at the resource manager level rather than the mixing thread. Results in faster mixing, but higher memory usage. */ MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_ASYNC = 0x00000004, /* When set, the resource manager will load the data source asynchronously. */ MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_WAIT_INIT = 0x00000008, /* When set, waits for initialization of the underlying data source before returning from ma_resource_manager_data_source_init(). */ - MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_UNKNOWN_LENGTH = 0x00000010, /* Gives the resource manager a hint that the length of the data source is unknown and calling `ma_data_source_get_length_in_pcm_frames()` should be avoided. */ - MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_LOOPING = 0x00000020 /* When set, configures the data source to loop by default. */ + MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_UNKNOWN_LENGTH = 0x00000010 /* Gives the resource manager a hint that the length of the data source is unknown and calling `ma_data_source_get_length_in_pcm_frames()` should be avoided. */ } ma_resource_manager_data_source_flags; @@ -10355,8 +10258,8 @@ typedef struct ma_uint64 rangeEndInPCMFrames; ma_uint64 loopPointBegInPCMFrames; ma_uint64 loopPointEndInPCMFrames; + ma_bool32 isLooping; ma_uint32 flags; - ma_bool32 isLooping; /* Deprecated. Use the MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_LOOPING flag in `flags` instead. */ } ma_resource_manager_data_source_config; MA_API ma_resource_manager_data_source_config ma_resource_manager_data_source_config_init(void); @@ -10599,16 +10502,6 @@ Node Graph /* Use this when the bus count is determined by the node instance rather than the vtable. */ #define MA_NODE_BUS_COUNT_UNKNOWN 255 - -/* For some internal memory management of ma_node_graph. */ -typedef struct -{ - size_t offset; - size_t sizeInBytes; - unsigned char _data[1]; -} ma_stack; - - typedef struct ma_node_graph ma_node_graph; typedef void ma_node; @@ -10637,7 +10530,7 @@ typedef struct /* Extended processing callback. This callback is used for effects that process input and output at different rates (i.e. they perform resampling). This is similar to the simple version, only - they take two separate frame counts: one for input, and one for output. + they take two seperate frame counts: one for input, and one for output. On input, `pFrameCountOut` is equal to the capacity of the output buffer for each bus, whereas `pFrameCountIn` will be equal to the number of PCM frames in each of the buffers in `ppFramesIn`. @@ -10648,7 +10541,7 @@ typedef struct void (* onProcess)(ma_node* pNode, const float** ppFramesIn, ma_uint32* pFrameCountIn, float** ppFramesOut, ma_uint32* pFrameCountOut); /* - A callback for retrieving the number of input frames that are required to output the + A callback for retrieving the number of a input frames that are required to output the specified number of output frames. You would only want to implement this when the node performs resampling. This is optional, even for nodes that perform resampling, but it does offer a small reduction in latency as it allows miniaudio to calculate the exact number of input frames @@ -10733,14 +10626,10 @@ typedef struct ma_node_base ma_node_base; struct ma_node_base { /* These variables are set once at startup. */ - ma_node_graph* pNodeGraph; /* The graph this node belongs to. */ + ma_node_graph* pNodeGraph; /* The graph this node belongs to. */ const ma_node_vtable* vtable; - ma_uint32 inputBusCount; - ma_uint32 outputBusCount; - ma_node_input_bus* pInputBuses; - ma_node_output_bus* pOutputBuses; - float* pCachedData; /* Allocated on the heap. Fixed size. Needs to be stored on the heap because reading from output buses is done in separate function calls. */ - ma_uint16 cachedDataCapInFramesPerBus; /* The capacity of the input data cache in frames, per bus. */ + float* pCachedData; /* Allocated on the heap. Fixed size. Needs to be stored on the heap because reading from output buses is done in separate function calls. */ + ma_uint16 cachedDataCapInFramesPerBus; /* The capacity of the input data cache in frames, per bus. */ /* These variables are read and written only from the audio thread. */ ma_uint16 cachedFrameCountOut; @@ -10748,9 +10637,13 @@ struct ma_node_base ma_uint16 consumedFrameCountIn; /* These variables are read and written between different threads. */ - MA_ATOMIC(4, ma_node_state) state; /* When set to stopped, nothing will be read, regardless of the times in stateTimes. */ - MA_ATOMIC(8, ma_uint64) stateTimes[2]; /* Indexed by ma_node_state. Specifies the time based on the global clock that a node should be considered to be in the relevant state. */ - MA_ATOMIC(8, ma_uint64) localTime; /* The node's local clock. This is just a running sum of the number of output frames that have been processed. Can be modified by any thread with `ma_node_set_time()`. */ + MA_ATOMIC(4, ma_node_state) state; /* When set to stopped, nothing will be read, regardless of the times in stateTimes. */ + MA_ATOMIC(8, ma_uint64) stateTimes[2]; /* Indexed by ma_node_state. Specifies the time based on the global clock that a node should be considered to be in the relevant state. */ + MA_ATOMIC(8, ma_uint64) localTime; /* The node's local clock. This is just a running sum of the number of output frames that have been processed. Can be modified by any thread with `ma_node_set_time()`. */ + ma_uint32 inputBusCount; + ma_uint32 outputBusCount; + ma_node_input_bus* pInputBuses; + ma_node_output_bus* pOutputBuses; /* Memory management. */ ma_node_input_bus _inputBuses[MA_MAX_NODE_LOCAL_BUS_COUNT]; @@ -10786,8 +10679,7 @@ MA_API ma_result ma_node_set_time(ma_node* pNode, ma_uint64 localTime); typedef struct { ma_uint32 channels; - ma_uint32 processingSizeInFrames; /* This is the preferred processing size for node processing callbacks unless overridden by a node itself. Can be 0 in which case it will be based on the frame count passed into ma_node_graph_read_pcm_frames(), but will not be well defined. */ - size_t preMixStackSizeInBytes; /* Defaults to 512KB per channel. Reducing this will save memory, but the depth of your node graph will be more restricted. */ + ma_uint16 nodeCacheCapInFrames; } ma_node_graph_config; MA_API ma_node_graph_config ma_node_graph_config_init(ma_uint32 channels); @@ -10798,15 +10690,10 @@ struct ma_node_graph /* Immutable. */ ma_node_base base; /* The node graph itself is a node so it can be connected as an input to different node graph. This has zero inputs and calls ma_node_graph_read_pcm_frames() to generate it's output. */ ma_node_base endpoint; /* Special node that all nodes eventually connect to. Data is read from this node in ma_node_graph_read_pcm_frames(). */ - float* pProcessingCache; /* This will be allocated when processingSizeInFrames is non-zero. This is needed because ma_node_graph_read_pcm_frames() can be called with a variable number of frames, and we may need to do some buffering in situations where the caller requests a frame count that's not a multiple of processingSizeInFrames. */ - ma_uint32 processingCacheFramesRemaining; - ma_uint32 processingSizeInFrames; + ma_uint16 nodeCacheCapInFrames; /* Read and written by multiple threads. */ MA_ATOMIC(4, ma_bool32) isReading; - - /* Modified only by the audio thread. */ - ma_stack* pPreMixStack; }; MA_API ma_result ma_node_graph_init(const ma_node_graph_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_node_graph* pNodeGraph); @@ -11091,7 +10978,6 @@ typedef enum MA_SOUND_FLAG_ASYNC = 0x00000004, /* MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_ASYNC */ MA_SOUND_FLAG_WAIT_INIT = 0x00000008, /* MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_WAIT_INIT */ MA_SOUND_FLAG_UNKNOWN_LENGTH = 0x00000010, /* MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_UNKNOWN_LENGTH */ - MA_SOUND_FLAG_LOOPING = 0x00000020, /* MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_LOOPING */ /* ma_sound specific flags. */ MA_SOUND_FLAG_NO_DEFAULT_ATTACHMENT = 0x00001000, /* Do not attach to the endpoint by default. Useful for when setting up nodes in a complex graph system. */ @@ -11131,7 +11017,7 @@ MA_API ma_engine_node_config ma_engine_node_config_init(ma_engine* pEngine, ma_e /* Base node object for both ma_sound and ma_sound_group. */ typedef struct { - ma_node_base baseNode; /* Must be the first member for compatibility with the ma_node API. */ + ma_node_base baseNode; /* Must be the first member for compatiblity with the ma_node API. */ ma_engine* pEngine; /* A pointer to the engine. Set based on the value from the config. */ ma_uint32 sampleRate; /* The sample rate of the input data. For sounds backed by a data source, this will be the data source's sample rate. Otherwise it'll be the engine's sample rate. */ ma_uint32 volumeSmoothTimeInPCMFrames; @@ -11149,15 +11035,6 @@ typedef struct MA_ATOMIC(4, ma_bool32) isSpatializationDisabled; /* Set to false by default. When set to false, will not have spatialisation applied. */ MA_ATOMIC(4, ma_uint32) pinnedListenerIndex; /* The index of the listener this node should always use for spatialization. If set to MA_LISTENER_INDEX_CLOSEST the engine will use the closest listener. */ - /* When setting a fade, it's not done immediately in ma_sound_set_fade(). It's deferred to the audio thread which means we need to store the settings here. */ - struct - { - ma_atomic_float volumeBeg; - ma_atomic_float volumeEnd; - ma_atomic_uint64 fadeLengthInFrames; /* <-- Defaults to (~(ma_uint64)0) which is used to indicate that no fade should be applied. */ - ma_atomic_uint64 absoluteGlobalTimeInFrames; /* <-- The time to start the fade. */ - } fadeSettings; - /* Memory management. */ ma_bool8 _ownsHeap; void* _pHeap; @@ -11191,13 +11068,13 @@ typedef struct ma_uint64 rangeEndInPCMFrames; ma_uint64 loopPointBegInPCMFrames; ma_uint64 loopPointEndInPCMFrames; + ma_bool32 isLooping; ma_sound_end_proc endCallback; /* Fired when the sound reaches the end. Will be fired from the audio thread. Do not restart, uninitialize or otherwise change the state of the sound from here. Instead fire an event or set a variable to indicate to a different thread to change the start of the sound. Will not be fired in response to a scheduled stop with ma_sound_set_stop_time_*(). */ void* pEndCallbackUserData; #ifndef MA_NO_RESOURCE_MANAGER ma_resource_manager_pipeline_notifications initNotifications; #endif ma_fence* pDoneFence; /* Deprecated. Use initNotifications instead. Released when the resource manager has finished decoding the entire sound. Not used with streams. */ - ma_bool32 isLooping; /* Deprecated. Use the MA_SOUND_FLAG_LOOPING flag in `flags` instead. */ } ma_sound_config; MA_API ma_sound_config ma_sound_config_init(void); /* Deprecated. Will be removed in version 0.12. Use ma_sound_config_2() instead. */ @@ -11238,8 +11115,6 @@ typedef ma_sound ma_sound_group; MA_API ma_sound_group_config ma_sound_group_config_init(void); /* Deprecated. Will be removed in version 0.12. Use ma_sound_config_2() instead. */ MA_API ma_sound_group_config ma_sound_group_config_init_2(ma_engine* pEngine); /* Will be renamed to ma_sound_config_init() in version 0.12. */ -typedef void (* ma_engine_process_proc)(void* pUserData, float* pFramesOut, ma_uint64 frameCount); - typedef struct { #if !defined(MA_NO_RESOURCE_MANAGER) @@ -11249,7 +11124,6 @@ typedef struct ma_context* pContext; ma_device* pDevice; /* If set, the caller is responsible for calling ma_engine_data_callback() in the device's data callback. */ ma_device_id* pPlaybackDeviceID; /* The ID of the playback device to use with the default listener. */ - ma_device_data_proc dataCallback; /* Can be null. Can be used to provide a custom device data callback. */ ma_device_notification_proc notificationCallback; #endif ma_log* pLog; /* When set to NULL, will use the context's log. */ @@ -11261,14 +11135,11 @@ typedef struct ma_uint32 gainSmoothTimeInFrames; /* The number of frames to interpolate the gain of spatialized sounds across. If set to 0, will use gainSmoothTimeInMilliseconds. */ ma_uint32 gainSmoothTimeInMilliseconds; /* When set to 0, gainSmoothTimeInFrames will be used. If both are set to 0, a default value will be used. */ ma_uint32 defaultVolumeSmoothTimeInPCMFrames; /* Defaults to 0. Controls the default amount of smoothing to apply to volume changes to sounds. High values means more smoothing at the expense of high latency (will take longer to reach the new volume). */ - ma_uint32 preMixStackSizeInBytes; /* A stack is used for internal processing in the node graph. This allows you to configure the size of this stack. Smaller values will reduce the maximum depth of your node graph. You should rarely need to modify this. */ ma_allocation_callbacks allocationCallbacks; ma_bool32 noAutoStart; /* When set to true, requires an explicit call to ma_engine_start(). This is false by default, meaning the engine will be started automatically in ma_engine_init(). */ ma_bool32 noDevice; /* When set to true, don't create a default device. ma_engine_read_pcm_frames() can be called manually to read data. */ ma_mono_expansion_mode monoExpansionMode; /* Controls how the mono channel should be expanded to other channels when spatialization is disabled on a sound. */ ma_vfs* pResourceManagerVFS; /* A pointer to a pre-allocated VFS object to use with the resource manager. This is ignored if pResourceManager is not NULL. */ - ma_engine_process_proc onProcess; /* Fired at the end of each call to ma_engine_read_pcm_frames(). For engine's that manage their own internal device (the default configuration), this will be fired from the audio thread, and you do not need to call ma_engine_read_pcm_frames() manually in order to trigger this. */ - void* pProcessUserData; /* User data that's passed into onProcess. */ } ma_engine_config; MA_API ma_engine_config ma_engine_config_init(void); @@ -11276,12 +11147,12 @@ MA_API ma_engine_config ma_engine_config_init(void); struct ma_engine { - ma_node_graph nodeGraph; /* An engine is a node graph. It should be able to be plugged into any ma_node_graph API (with a cast) which means this must be the first member of this struct. */ + ma_node_graph nodeGraph; /* An engine is a node graph. It should be able to be plugged into any ma_node_graph API (with a cast) which means this must be the first member of this struct. */ #if !defined(MA_NO_RESOURCE_MANAGER) ma_resource_manager* pResourceManager; #endif #if !defined(MA_NO_DEVICE_IO) - ma_device* pDevice; /* Optionally set via the config, otherwise allocated by the engine in ma_engine_init(). */ + ma_device* pDevice; /* Optionally set via the config, otherwise allocated by the engine in ma_engine_init(). */ #endif ma_log* pLog; ma_uint32 sampleRate; @@ -11290,14 +11161,12 @@ struct ma_engine ma_allocation_callbacks allocationCallbacks; ma_bool8 ownsResourceManager; ma_bool8 ownsDevice; - ma_spinlock inlinedSoundLock; /* For synchronizing access to the inlined sound list. */ - ma_sound_inlined* pInlinedSoundHead; /* The first inlined sound. Inlined sounds are tracked in a linked list. */ - MA_ATOMIC(4, ma_uint32) inlinedSoundCount; /* The total number of allocated inlined sound objects. Used for debugging. */ - ma_uint32 gainSmoothTimeInFrames; /* The number of frames to interpolate the gain of spatialized sounds across. */ + ma_spinlock inlinedSoundLock; /* For synchronizing access so the inlined sound list. */ + ma_sound_inlined* pInlinedSoundHead; /* The first inlined sound. Inlined sounds are tracked in a linked list. */ + MA_ATOMIC(4, ma_uint32) inlinedSoundCount; /* The total number of allocated inlined sound objects. Used for debugging. */ + ma_uint32 gainSmoothTimeInFrames; /* The number of frames to interpolate the gain of spatialized sounds across. */ ma_uint32 defaultVolumeSmoothTimeInPCMFrames; ma_mono_expansion_mode monoExpansionMode; - ma_engine_process_proc onProcess; - void* pProcessUserData; }; MA_API ma_result ma_engine_init(const ma_engine_config* pConfig, ma_engine* pEngine); @@ -11322,9 +11191,7 @@ MA_API ma_uint32 ma_engine_get_sample_rate(const ma_engine* pEngine); MA_API ma_result ma_engine_start(ma_engine* pEngine); MA_API ma_result ma_engine_stop(ma_engine* pEngine); MA_API ma_result ma_engine_set_volume(ma_engine* pEngine, float volume); -MA_API float ma_engine_get_volume(ma_engine* pEngine); MA_API ma_result ma_engine_set_gain_db(ma_engine* pEngine, float gainDB); -MA_API float ma_engine_get_gain_db(ma_engine* pEngine); MA_API ma_uint32 ma_engine_get_listener_count(const ma_engine* pEngine); MA_API ma_uint32 ma_engine_find_closest_listener(const ma_engine* pEngine, float absolutePosX, float absolutePosY, float absolutePosZ); @@ -11358,8 +11225,6 @@ MA_API ma_engine* ma_sound_get_engine(const ma_sound* pSound); MA_API ma_data_source* ma_sound_get_data_source(const ma_sound* pSound); MA_API ma_result ma_sound_start(ma_sound* pSound); MA_API ma_result ma_sound_stop(ma_sound* pSound); -MA_API ma_result ma_sound_stop_with_fade_in_pcm_frames(ma_sound* pSound, ma_uint64 fadeLengthInFrames); /* Will overwrite any scheduled stop and fade. */ -MA_API ma_result ma_sound_stop_with_fade_in_milliseconds(ma_sound* pSound, ma_uint64 fadeLengthInFrames); /* Will overwrite any scheduled stop and fade. */ MA_API void ma_sound_set_volume(ma_sound* pSound, float volume); MA_API float ma_sound_get_volume(const ma_sound* pSound); MA_API void ma_sound_set_pan(ma_sound* pSound, float pan); @@ -11402,23 +11267,17 @@ MA_API void ma_sound_set_directional_attenuation_factor(ma_sound* pSound, float MA_API float ma_sound_get_directional_attenuation_factor(const ma_sound* pSound); MA_API void ma_sound_set_fade_in_pcm_frames(ma_sound* pSound, float volumeBeg, float volumeEnd, ma_uint64 fadeLengthInFrames); MA_API void ma_sound_set_fade_in_milliseconds(ma_sound* pSound, float volumeBeg, float volumeEnd, ma_uint64 fadeLengthInMilliseconds); -MA_API void ma_sound_set_fade_start_in_pcm_frames(ma_sound* pSound, float volumeBeg, float volumeEnd, ma_uint64 fadeLengthInFrames, ma_uint64 absoluteGlobalTimeInFrames); -MA_API void ma_sound_set_fade_start_in_milliseconds(ma_sound* pSound, float volumeBeg, float volumeEnd, ma_uint64 fadeLengthInMilliseconds, ma_uint64 absoluteGlobalTimeInMilliseconds); MA_API float ma_sound_get_current_fade_volume(const ma_sound* pSound); MA_API void ma_sound_set_start_time_in_pcm_frames(ma_sound* pSound, ma_uint64 absoluteGlobalTimeInFrames); MA_API void ma_sound_set_start_time_in_milliseconds(ma_sound* pSound, ma_uint64 absoluteGlobalTimeInMilliseconds); MA_API void ma_sound_set_stop_time_in_pcm_frames(ma_sound* pSound, ma_uint64 absoluteGlobalTimeInFrames); MA_API void ma_sound_set_stop_time_in_milliseconds(ma_sound* pSound, ma_uint64 absoluteGlobalTimeInMilliseconds); -MA_API void ma_sound_set_stop_time_with_fade_in_pcm_frames(ma_sound* pSound, ma_uint64 stopAbsoluteGlobalTimeInFrames, ma_uint64 fadeLengthInFrames); -MA_API void ma_sound_set_stop_time_with_fade_in_milliseconds(ma_sound* pSound, ma_uint64 stopAbsoluteGlobalTimeInMilliseconds, ma_uint64 fadeLengthInMilliseconds); MA_API ma_bool32 ma_sound_is_playing(const ma_sound* pSound); MA_API ma_uint64 ma_sound_get_time_in_pcm_frames(const ma_sound* pSound); -MA_API ma_uint64 ma_sound_get_time_in_milliseconds(const ma_sound* pSound); MA_API void ma_sound_set_looping(ma_sound* pSound, ma_bool32 isLooping); MA_API ma_bool32 ma_sound_is_looping(const ma_sound* pSound); MA_API ma_bool32 ma_sound_at_end(const ma_sound* pSound); MA_API ma_result ma_sound_seek_to_pcm_frame(ma_sound* pSound, ma_uint64 frameIndex); /* Just a wrapper around ma_data_source_seek_to_pcm_frame(). */ -MA_API ma_result ma_sound_seek_to_second(ma_sound* pSound, float seekPointInSeconds); /* Abstraction to ma_sound_seek_to_pcm_frame() */ MA_API ma_result ma_sound_get_data_format(ma_sound* pSound, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate, ma_channel* pChannelMap, size_t channelMapCap); MA_API ma_result ma_sound_get_cursor_in_pcm_frames(ma_sound* pSound, ma_uint64* pCursor); MA_API ma_result ma_sound_get_length_in_pcm_frames(ma_sound* pSound, ma_uint64* pLength); @@ -11560,7 +11419,6 @@ IMPLEMENTATION #endif -/* Architecture Detection */ #if !defined(MA_64BIT) && !defined(MA_32BIT) #ifdef _WIN32 #ifdef _WIN64 @@ -11590,18 +11448,12 @@ IMPLEMENTATION #endif #endif -#if defined(__arm__) || defined(_M_ARM) -#define MA_ARM32 -#endif -#if defined(__arm64) || defined(__arm64__) || defined(__aarch64__) || defined(_M_ARM64) -#define MA_ARM64 -#endif - +/* Architecture Detection */ #if defined(__x86_64__) || defined(_M_X64) #define MA_X64 #elif defined(__i386) || defined(_M_IX86) #define MA_X86 -#elif defined(MA_ARM32) || defined(MA_ARM64) +#elif defined(__arm__) || defined(_M_ARM) || defined(__arm64) || defined(__arm64__) || defined(__aarch64__) || defined(_M_ARM64) #define MA_ARM #endif @@ -11695,7 +11547,7 @@ IMPLEMENTATION What's basically happening is that we're saving and restoring the ebx register manually. */ - #if defined(MA_X86) && defined(__PIC__) + #if defined(DRFLAC_X86) && defined(__PIC__) __asm__ __volatile__ ( "xchg{l} {%%}ebx, %k1;" "cpuid;" @@ -12315,7 +12167,7 @@ static MA_INLINE void ma_zero_memory_default(void* p, size_t sz) #define ma_abs(x) (((x) > 0) ? (x) : -(x)) #define ma_clamp(x, lo, hi) (ma_max(lo, ma_min(x, hi))) #define ma_offset_ptr(p, offset) (((ma_uint8*)(p)) + (offset)) -#define ma_align(x, a) (((x) + ((a)-1)) & ~((a)-1)) +#define ma_align(x, a) ((x + (a-1)) & ~(a-1)) #define ma_align_64(x) ma_align(x, 8) #define ma_buffer_frame_capacity(buffer, channels, format) (sizeof(buffer) / ma_get_bytes_per_sample(format) / (channels)) @@ -12444,11 +12296,8 @@ Return Values: 34: ERANGE Not using symbolic constants for errors because I want to avoid #including errno.h - -These are marked as no-inline because of some bad code generation by Clang. None of these functions -are used in any performance-critical code within miniaudio. */ -MA_API MA_NO_INLINE int ma_strcpy_s(char* dst, size_t dstSizeInBytes, const char* src) +MA_API int ma_strcpy_s(char* dst, size_t dstSizeInBytes, const char* src) { size_t i; @@ -12476,7 +12325,7 @@ MA_API MA_NO_INLINE int ma_strcpy_s(char* dst, size_t dstSizeInBytes, const char return 34; } -MA_API MA_NO_INLINE int ma_wcscpy_s(wchar_t* dst, size_t dstCap, const wchar_t* src) +MA_API int ma_wcscpy_s(wchar_t* dst, size_t dstCap, const wchar_t* src) { size_t i; @@ -12505,7 +12354,7 @@ MA_API MA_NO_INLINE int ma_wcscpy_s(wchar_t* dst, size_t dstCap, const wchar_t* } -MA_API MA_NO_INLINE int ma_strncpy_s(char* dst, size_t dstSizeInBytes, const char* src, size_t count) +MA_API int ma_strncpy_s(char* dst, size_t dstSizeInBytes, const char* src, size_t count) { size_t maxcount; size_t i; @@ -12539,7 +12388,7 @@ MA_API MA_NO_INLINE int ma_strncpy_s(char* dst, size_t dstSizeInBytes, const cha return 34; } -MA_API MA_NO_INLINE int ma_strcat_s(char* dst, size_t dstSizeInBytes, const char* src) +MA_API int ma_strcat_s(char* dst, size_t dstSizeInBytes, const char* src) { char* dstorig; @@ -12581,7 +12430,7 @@ MA_API MA_NO_INLINE int ma_strcat_s(char* dst, size_t dstSizeInBytes, const char return 0; } -MA_API MA_NO_INLINE int ma_strncat_s(char* dst, size_t dstSizeInBytes, const char* src, size_t count) +MA_API int ma_strncat_s(char* dst, size_t dstSizeInBytes, const char* src, size_t count) { char* dstorig; @@ -12627,7 +12476,7 @@ MA_API MA_NO_INLINE int ma_strncat_s(char* dst, size_t dstSizeInBytes, const cha return 0; } -MA_API MA_NO_INLINE int ma_itoa_s(int value, char* dst, size_t dstSizeInBytes, int radix) +MA_API int ma_itoa_s(int value, char* dst, size_t dstSizeInBytes, int radix) { int sign; unsigned int valueU; @@ -12696,7 +12545,7 @@ MA_API MA_NO_INLINE int ma_itoa_s(int value, char* dst, size_t dstSizeInBytes, i return 0; } -MA_API MA_NO_INLINE int ma_strcmp(const char* str1, const char* str2) +MA_API int ma_strcmp(const char* str1, const char* str2) { if (str1 == str2) return 0; @@ -12719,7 +12568,7 @@ MA_API MA_NO_INLINE int ma_strcmp(const char* str1, const char* str2) return ((unsigned char*)str1)[0] - ((unsigned char*)str2)[0]; } -MA_API MA_NO_INLINE int ma_strappend(char* dst, size_t dstSize, const char* srcA, const char* srcB) +MA_API int ma_strappend(char* dst, size_t dstSize, const char* srcA, const char* srcB) { int result; @@ -12736,7 +12585,7 @@ MA_API MA_NO_INLINE int ma_strappend(char* dst, size_t dstSize, const char* srcA return result; } -MA_API MA_NO_INLINE char* ma_copy_string(const char* src, const ma_allocation_callbacks* pAllocationCallbacks) +MA_API char* ma_copy_string(const char* src, const ma_allocation_callbacks* pAllocationCallbacks) { size_t sz; char* dst; @@ -12756,7 +12605,7 @@ MA_API MA_NO_INLINE char* ma_copy_string(const char* src, const ma_allocation_ca return dst; } -MA_API MA_NO_INLINE wchar_t* ma_copy_string_w(const wchar_t* src, const ma_allocation_callbacks* pAllocationCallbacks) +MA_API wchar_t* ma_copy_string_w(const wchar_t* src, const ma_allocation_callbacks* pAllocationCallbacks) { size_t sz = wcslen(src)+1; wchar_t* dst = (wchar_t*)ma_malloc(sz * sizeof(*dst), pAllocationCallbacks); @@ -13716,7 +13565,7 @@ MA_API ma_result ma_log_postv(ma_log* pLog, ma_uint32 level, const char* pFormat /* First try formatting into our fixed sized stack allocated buffer. If this is too small we'll fallback to a heap allocation. */ length = vsnprintf(pFormattedMessageStack, sizeof(pFormattedMessageStack), pFormat, args); if (length < 0) { - return MA_INVALID_OPERATION; /* An error occurred when trying to convert the buffer. */ + return MA_INVALID_OPERATION; /* An error occured when trying to convert the buffer. */ } if ((size_t)length < sizeof(pFormattedMessageStack)) { @@ -13932,7 +13781,7 @@ static ma_uint32 ma_ffs_32(ma_uint32 x) /* Just a naive implementation just to get things working for now. Will optimize this later. */ for (i = 0; i < 32; i += 1) { - if ((x & (1U << i)) != 0) { + if ((x & (1 << i)) != 0) { return i; } } @@ -14096,7 +13945,8 @@ Atomics **************************************************************************************************************************************************************/ /* c89atomic.h begin */ -#ifndef ma_atomic_h +#ifndef c89atomic_h +#define c89atomic_h #if defined(__cplusplus) extern "C" { #endif @@ -14107,83 +13957,149 @@ extern "C" { #pragma GCC diagnostic ignored "-Wc++11-long-long" #endif #endif -typedef int ma_atomic_memory_order; -#define MA_ATOMIC_HAS_8 -#define MA_ATOMIC_HAS_16 -#define MA_ATOMIC_HAS_32 -#define MA_ATOMIC_HAS_64 +typedef signed char c89atomic_int8; +typedef unsigned char c89atomic_uint8; +typedef signed short c89atomic_int16; +typedef unsigned short c89atomic_uint16; +typedef signed int c89atomic_int32; +typedef unsigned int c89atomic_uint32; +#if defined(_MSC_VER) && !defined(__clang__) + typedef signed __int64 c89atomic_int64; + typedef unsigned __int64 c89atomic_uint64; +#else + typedef signed long long c89atomic_int64; + typedef unsigned long long c89atomic_uint64; +#endif +typedef int c89atomic_memory_order; +typedef unsigned char c89atomic_bool; +#if !defined(C89ATOMIC_64BIT) && !defined(C89ATOMIC_32BIT) +#ifdef _WIN32 +#ifdef _WIN64 +#define C89ATOMIC_64BIT +#else +#define C89ATOMIC_32BIT +#endif +#endif +#endif +#if !defined(C89ATOMIC_64BIT) && !defined(C89ATOMIC_32BIT) +#ifdef __GNUC__ +#ifdef __LP64__ +#define C89ATOMIC_64BIT +#else +#define C89ATOMIC_32BIT +#endif +#endif +#endif +#if !defined(C89ATOMIC_64BIT) && !defined(C89ATOMIC_32BIT) +#include +#if INTPTR_MAX == INT64_MAX +#define C89ATOMIC_64BIT +#else +#define C89ATOMIC_32BIT +#endif +#endif +#if defined(__arm__) || defined(_M_ARM) +#define C89ATOMIC_ARM32 +#endif +#if defined(__arm64) || defined(__arm64__) || defined(__aarch64__) || defined(_M_ARM64) +#define C89ATOMIC_ARM64 +#endif +#if defined(__x86_64__) || defined(_M_X64) +#define C89ATOMIC_X64 +#elif defined(__i386) || defined(_M_IX86) +#define C89ATOMIC_X86 +#elif defined(C89ATOMIC_ARM32) || defined(C89ATOMIC_ARM64) +#define C89ATOMIC_ARM +#endif +#if defined(_MSC_VER) + #define C89ATOMIC_INLINE __forceinline +#elif defined(__GNUC__) + #if defined(__STRICT_ANSI__) + #define C89ATOMIC_INLINE __inline__ __attribute__((always_inline)) + #else + #define C89ATOMIC_INLINE inline __attribute__((always_inline)) + #endif +#elif defined(__WATCOMC__) || defined(__DMC__) + #define C89ATOMIC_INLINE __inline +#else + #define C89ATOMIC_INLINE +#endif +#define C89ATOMIC_HAS_8 +#define C89ATOMIC_HAS_16 +#define C89ATOMIC_HAS_32 +#define C89ATOMIC_HAS_64 #if (defined(_MSC_VER) ) || defined(__WATCOMC__) || defined(__DMC__) - #define MA_ATOMIC_MSVC_ARM_INTRINSIC(dst, src, order, intrin, ma_atomicType, msvcType) \ - ma_atomicType result; \ + #define C89ATOMIC_MSVC_ARM_INTRINSIC(dst, src, order, intrin, c89atomicType, msvcType) \ + c89atomicType result; \ switch (order) \ { \ - case ma_atomic_memory_order_relaxed: \ + case c89atomic_memory_order_relaxed: \ { \ - result = (ma_atomicType)intrin##_nf((volatile msvcType*)dst, (msvcType)src); \ + result = (c89atomicType)intrin##_nf((volatile msvcType*)dst, (msvcType)src); \ } break; \ - case ma_atomic_memory_order_consume: \ - case ma_atomic_memory_order_acquire: \ + case c89atomic_memory_order_consume: \ + case c89atomic_memory_order_acquire: \ { \ - result = (ma_atomicType)intrin##_acq((volatile msvcType*)dst, (msvcType)src); \ + result = (c89atomicType)intrin##_acq((volatile msvcType*)dst, (msvcType)src); \ } break; \ - case ma_atomic_memory_order_release: \ + case c89atomic_memory_order_release: \ { \ - result = (ma_atomicType)intrin##_rel((volatile msvcType*)dst, (msvcType)src); \ + result = (c89atomicType)intrin##_rel((volatile msvcType*)dst, (msvcType)src); \ } break; \ - case ma_atomic_memory_order_acq_rel: \ - case ma_atomic_memory_order_seq_cst: \ + case c89atomic_memory_order_acq_rel: \ + case c89atomic_memory_order_seq_cst: \ default: \ { \ - result = (ma_atomicType)intrin((volatile msvcType*)dst, (msvcType)src); \ + result = (c89atomicType)intrin((volatile msvcType*)dst, (msvcType)src); \ } break; \ } \ return result; - #define MA_ATOMIC_MSVC_ARM_INTRINSIC_COMPARE_EXCHANGE(ptr, expected, desired, order, intrin, ma_atomicType, msvcType) \ - ma_atomicType result; \ + #define C89ATOMIC_MSVC_ARM_INTRINSIC_COMPARE_EXCHANGE(ptr, expected, desired, order, intrin, c89atomicType, msvcType) \ + c89atomicType result; \ switch (order) \ { \ - case ma_atomic_memory_order_relaxed: \ + case c89atomic_memory_order_relaxed: \ { \ - result = (ma_atomicType)intrin##_nf((volatile msvcType*)ptr, (msvcType)expected, (msvcType)desired); \ + result = (c89atomicType)intrin##_nf((volatile msvcType*)ptr, (msvcType)expected, (msvcType)desired); \ } break; \ - case ma_atomic_memory_order_consume: \ - case ma_atomic_memory_order_acquire: \ + case c89atomic_memory_order_consume: \ + case c89atomic_memory_order_acquire: \ { \ - result = (ma_atomicType)intrin##_acq((volatile msvcType*)ptr, (msvcType)expected, (msvcType)desired); \ + result = (c89atomicType)intrin##_acq((volatile msvcType*)ptr, (msvcType)expected, (msvcType)desired); \ } break; \ - case ma_atomic_memory_order_release: \ + case c89atomic_memory_order_release: \ { \ - result = (ma_atomicType)intrin##_rel((volatile msvcType*)ptr, (msvcType)expected, (msvcType)desired); \ + result = (c89atomicType)intrin##_rel((volatile msvcType*)ptr, (msvcType)expected, (msvcType)desired); \ } break; \ - case ma_atomic_memory_order_acq_rel: \ - case ma_atomic_memory_order_seq_cst: \ + case c89atomic_memory_order_acq_rel: \ + case c89atomic_memory_order_seq_cst: \ default: \ { \ - result = (ma_atomicType)intrin((volatile msvcType*)ptr, (msvcType)expected, (msvcType)desired); \ + result = (c89atomicType)intrin((volatile msvcType*)ptr, (msvcType)expected, (msvcType)desired); \ } break; \ } \ return result; - #define ma_atomic_memory_order_relaxed 0 - #define ma_atomic_memory_order_consume 1 - #define ma_atomic_memory_order_acquire 2 - #define ma_atomic_memory_order_release 3 - #define ma_atomic_memory_order_acq_rel 4 - #define ma_atomic_memory_order_seq_cst 5 - #if _MSC_VER < 1600 && defined(MA_X86) - #define MA_ATOMIC_MSVC_USE_INLINED_ASSEMBLY + #define c89atomic_memory_order_relaxed 0 + #define c89atomic_memory_order_consume 1 + #define c89atomic_memory_order_acquire 2 + #define c89atomic_memory_order_release 3 + #define c89atomic_memory_order_acq_rel 4 + #define c89atomic_memory_order_seq_cst 5 + #if _MSC_VER < 1600 && defined(C89ATOMIC_X86) + #define C89ATOMIC_MSVC_USE_INLINED_ASSEMBLY #endif #if _MSC_VER < 1600 - #undef MA_ATOMIC_HAS_8 - #undef MA_ATOMIC_HAS_16 + #undef C89ATOMIC_HAS_8 + #undef C89ATOMIC_HAS_16 #endif - #if !defined(MA_ATOMIC_MSVC_USE_INLINED_ASSEMBLY) + #if !defined(C89ATOMIC_MSVC_USE_INLINED_ASSEMBLY) #include #endif - #if defined(MA_ATOMIC_MSVC_USE_INLINED_ASSEMBLY) - #if defined(MA_ATOMIC_HAS_8) - static MA_INLINE ma_uint8 __stdcall ma_atomic_compare_and_swap_8(volatile ma_uint8* dst, ma_uint8 expected, ma_uint8 desired) + #if defined(C89ATOMIC_MSVC_USE_INLINED_ASSEMBLY) + #if defined(C89ATOMIC_HAS_8) + static C89ATOMIC_INLINE c89atomic_uint8 __stdcall c89atomic_compare_and_swap_8(volatile c89atomic_uint8* dst, c89atomic_uint8 expected, c89atomic_uint8 desired) { - ma_uint8 result = 0; + c89atomic_uint8 result = 0; __asm { mov ecx, dst mov al, expected @@ -14194,10 +14110,10 @@ typedef int ma_atomic_memory_order; return result; } #endif - #if defined(MA_ATOMIC_HAS_16) - static MA_INLINE ma_uint16 __stdcall ma_atomic_compare_and_swap_16(volatile ma_uint16* dst, ma_uint16 expected, ma_uint16 desired) + #if defined(C89ATOMIC_HAS_16) + static C89ATOMIC_INLINE c89atomic_uint16 __stdcall c89atomic_compare_and_swap_16(volatile c89atomic_uint16* dst, c89atomic_uint16 expected, c89atomic_uint16 desired) { - ma_uint16 result = 0; + c89atomic_uint16 result = 0; __asm { mov ecx, dst mov ax, expected @@ -14208,10 +14124,10 @@ typedef int ma_atomic_memory_order; return result; } #endif - #if defined(MA_ATOMIC_HAS_32) - static MA_INLINE ma_uint32 __stdcall ma_atomic_compare_and_swap_32(volatile ma_uint32* dst, ma_uint32 expected, ma_uint32 desired) + #if defined(C89ATOMIC_HAS_32) + static C89ATOMIC_INLINE c89atomic_uint32 __stdcall c89atomic_compare_and_swap_32(volatile c89atomic_uint32* dst, c89atomic_uint32 expected, c89atomic_uint32 desired) { - ma_uint32 result = 0; + c89atomic_uint32 result = 0; __asm { mov ecx, dst mov eax, expected @@ -14222,11 +14138,11 @@ typedef int ma_atomic_memory_order; return result; } #endif - #if defined(MA_ATOMIC_HAS_64) - static MA_INLINE ma_uint64 __stdcall ma_atomic_compare_and_swap_64(volatile ma_uint64* dst, ma_uint64 expected, ma_uint64 desired) + #if defined(C89ATOMIC_HAS_64) + static C89ATOMIC_INLINE c89atomic_uint64 __stdcall c89atomic_compare_and_swap_64(volatile c89atomic_uint64* dst, c89atomic_uint64 expected, c89atomic_uint64 desired) { - ma_uint32 resultEAX = 0; - ma_uint32 resultEDX = 0; + c89atomic_uint32 resultEAX = 0; + c89atomic_uint32 resultEDX = 0; __asm { mov esi, dst mov eax, dword ptr expected @@ -14237,28 +14153,28 @@ typedef int ma_atomic_memory_order; mov resultEAX, eax mov resultEDX, edx } - return ((ma_uint64)resultEDX << 32) | resultEAX; + return ((c89atomic_uint64)resultEDX << 32) | resultEAX; } #endif #else - #if defined(MA_ATOMIC_HAS_8) - #define ma_atomic_compare_and_swap_8( dst, expected, desired) (ma_uint8 )_InterlockedCompareExchange8((volatile char*)dst, (char)desired, (char)expected) + #if defined(C89ATOMIC_HAS_8) + #define c89atomic_compare_and_swap_8( dst, expected, desired) (c89atomic_uint8 )_InterlockedCompareExchange8((volatile char*)dst, (char)desired, (char)expected) #endif - #if defined(MA_ATOMIC_HAS_16) - #define ma_atomic_compare_and_swap_16(dst, expected, desired) (ma_uint16)_InterlockedCompareExchange16((volatile short*)dst, (short)desired, (short)expected) + #if defined(C89ATOMIC_HAS_16) + #define c89atomic_compare_and_swap_16(dst, expected, desired) (c89atomic_uint16)_InterlockedCompareExchange16((volatile short*)dst, (short)desired, (short)expected) #endif - #if defined(MA_ATOMIC_HAS_32) - #define ma_atomic_compare_and_swap_32(dst, expected, desired) (ma_uint32)_InterlockedCompareExchange((volatile long*)dst, (long)desired, (long)expected) + #if defined(C89ATOMIC_HAS_32) + #define c89atomic_compare_and_swap_32(dst, expected, desired) (c89atomic_uint32)_InterlockedCompareExchange((volatile long*)dst, (long)desired, (long)expected) #endif - #if defined(MA_ATOMIC_HAS_64) - #define ma_atomic_compare_and_swap_64(dst, expected, desired) (ma_uint64)_InterlockedCompareExchange64((volatile ma_int64*)dst, (ma_int64)desired, (ma_int64)expected) + #if defined(C89ATOMIC_HAS_64) + #define c89atomic_compare_and_swap_64(dst, expected, desired) (c89atomic_uint64)_InterlockedCompareExchange64((volatile c89atomic_int64*)dst, (c89atomic_int64)desired, (c89atomic_int64)expected) #endif #endif - #if defined(MA_ATOMIC_MSVC_USE_INLINED_ASSEMBLY) - #if defined(MA_ATOMIC_HAS_8) - static MA_INLINE ma_uint8 __stdcall ma_atomic_exchange_explicit_8(volatile ma_uint8* dst, ma_uint8 src, ma_atomic_memory_order order) + #if defined(C89ATOMIC_MSVC_USE_INLINED_ASSEMBLY) + #if defined(C89ATOMIC_HAS_8) + static C89ATOMIC_INLINE c89atomic_uint8 __stdcall c89atomic_exchange_explicit_8(volatile c89atomic_uint8* dst, c89atomic_uint8 src, c89atomic_memory_order order) { - ma_uint8 result = 0; + c89atomic_uint8 result = 0; (void)order; __asm { mov ecx, dst @@ -14269,10 +14185,10 @@ typedef int ma_atomic_memory_order; return result; } #endif - #if defined(MA_ATOMIC_HAS_16) - static MA_INLINE ma_uint16 __stdcall ma_atomic_exchange_explicit_16(volatile ma_uint16* dst, ma_uint16 src, ma_atomic_memory_order order) + #if defined(C89ATOMIC_HAS_16) + static C89ATOMIC_INLINE c89atomic_uint16 __stdcall c89atomic_exchange_explicit_16(volatile c89atomic_uint16* dst, c89atomic_uint16 src, c89atomic_memory_order order) { - ma_uint16 result = 0; + c89atomic_uint16 result = 0; (void)order; __asm { mov ecx, dst @@ -14283,10 +14199,10 @@ typedef int ma_atomic_memory_order; return result; } #endif - #if defined(MA_ATOMIC_HAS_32) - static MA_INLINE ma_uint32 __stdcall ma_atomic_exchange_explicit_32(volatile ma_uint32* dst, ma_uint32 src, ma_atomic_memory_order order) + #if defined(C89ATOMIC_HAS_32) + static C89ATOMIC_INLINE c89atomic_uint32 __stdcall c89atomic_exchange_explicit_32(volatile c89atomic_uint32* dst, c89atomic_uint32 src, c89atomic_memory_order order) { - ma_uint32 result = 0; + c89atomic_uint32 result = 0; (void)order; __asm { mov ecx, dst @@ -14298,68 +14214,68 @@ typedef int ma_atomic_memory_order; } #endif #else - #if defined(MA_ATOMIC_HAS_8) - static MA_INLINE ma_uint8 __stdcall ma_atomic_exchange_explicit_8(volatile ma_uint8* dst, ma_uint8 src, ma_atomic_memory_order order) + #if defined(C89ATOMIC_HAS_8) + static C89ATOMIC_INLINE c89atomic_uint8 __stdcall c89atomic_exchange_explicit_8(volatile c89atomic_uint8* dst, c89atomic_uint8 src, c89atomic_memory_order order) { - #if defined(MA_ARM) - MA_ATOMIC_MSVC_ARM_INTRINSIC(dst, src, order, _InterlockedExchange8, ma_uint8, char); + #if defined(C89ATOMIC_ARM) + C89ATOMIC_MSVC_ARM_INTRINSIC(dst, src, order, _InterlockedExchange8, c89atomic_uint8, char); #else (void)order; - return (ma_uint8)_InterlockedExchange8((volatile char*)dst, (char)src); + return (c89atomic_uint8)_InterlockedExchange8((volatile char*)dst, (char)src); #endif } #endif - #if defined(MA_ATOMIC_HAS_16) - static MA_INLINE ma_uint16 __stdcall ma_atomic_exchange_explicit_16(volatile ma_uint16* dst, ma_uint16 src, ma_atomic_memory_order order) + #if defined(C89ATOMIC_HAS_16) + static C89ATOMIC_INLINE c89atomic_uint16 __stdcall c89atomic_exchange_explicit_16(volatile c89atomic_uint16* dst, c89atomic_uint16 src, c89atomic_memory_order order) { - #if defined(MA_ARM) - MA_ATOMIC_MSVC_ARM_INTRINSIC(dst, src, order, _InterlockedExchange16, ma_uint16, short); + #if defined(C89ATOMIC_ARM) + C89ATOMIC_MSVC_ARM_INTRINSIC(dst, src, order, _InterlockedExchange16, c89atomic_uint16, short); #else (void)order; - return (ma_uint16)_InterlockedExchange16((volatile short*)dst, (short)src); + return (c89atomic_uint16)_InterlockedExchange16((volatile short*)dst, (short)src); #endif } #endif - #if defined(MA_ATOMIC_HAS_32) - static MA_INLINE ma_uint32 __stdcall ma_atomic_exchange_explicit_32(volatile ma_uint32* dst, ma_uint32 src, ma_atomic_memory_order order) + #if defined(C89ATOMIC_HAS_32) + static C89ATOMIC_INLINE c89atomic_uint32 __stdcall c89atomic_exchange_explicit_32(volatile c89atomic_uint32* dst, c89atomic_uint32 src, c89atomic_memory_order order) { - #if defined(MA_ARM) - MA_ATOMIC_MSVC_ARM_INTRINSIC(dst, src, order, _InterlockedExchange, ma_uint32, long); + #if defined(C89ATOMIC_ARM) + C89ATOMIC_MSVC_ARM_INTRINSIC(dst, src, order, _InterlockedExchange, c89atomic_uint32, long); #else (void)order; - return (ma_uint32)_InterlockedExchange((volatile long*)dst, (long)src); + return (c89atomic_uint32)_InterlockedExchange((volatile long*)dst, (long)src); #endif } #endif - #if defined(MA_ATOMIC_HAS_64) && defined(MA_64BIT) - static MA_INLINE ma_uint64 __stdcall ma_atomic_exchange_explicit_64(volatile ma_uint64* dst, ma_uint64 src, ma_atomic_memory_order order) + #if defined(C89ATOMIC_HAS_64) && defined(C89ATOMIC_64BIT) + static C89ATOMIC_INLINE c89atomic_uint64 __stdcall c89atomic_exchange_explicit_64(volatile c89atomic_uint64* dst, c89atomic_uint64 src, c89atomic_memory_order order) { - #if defined(MA_ARM) - MA_ATOMIC_MSVC_ARM_INTRINSIC(dst, src, order, _InterlockedExchange64, ma_uint64, long long); + #if defined(C89ATOMIC_ARM) + C89ATOMIC_MSVC_ARM_INTRINSIC(dst, src, order, _InterlockedExchange64, c89atomic_uint64, long long); #else (void)order; - return (ma_uint64)_InterlockedExchange64((volatile long long*)dst, (long long)src); + return (c89atomic_uint64)_InterlockedExchange64((volatile long long*)dst, (long long)src); #endif } #else #endif #endif - #if defined(MA_ATOMIC_HAS_64) && !defined(MA_64BIT) - static MA_INLINE ma_uint64 __stdcall ma_atomic_exchange_explicit_64(volatile ma_uint64* dst, ma_uint64 src, ma_atomic_memory_order order) + #if defined(C89ATOMIC_HAS_64) && !defined(C89ATOMIC_64BIT) + static C89ATOMIC_INLINE c89atomic_uint64 __stdcall c89atomic_exchange_explicit_64(volatile c89atomic_uint64* dst, c89atomic_uint64 src, c89atomic_memory_order order) { - ma_uint64 oldValue; + c89atomic_uint64 oldValue; do { oldValue = *dst; - } while (ma_atomic_compare_and_swap_64(dst, oldValue, src) != oldValue); + } while (c89atomic_compare_and_swap_64(dst, oldValue, src) != oldValue); (void)order; return oldValue; } #endif - #if defined(MA_ATOMIC_MSVC_USE_INLINED_ASSEMBLY) - #if defined(MA_ATOMIC_HAS_8) - static MA_INLINE ma_uint8 __stdcall ma_atomic_fetch_add_explicit_8(volatile ma_uint8* dst, ma_uint8 src, ma_atomic_memory_order order) + #if defined(C89ATOMIC_MSVC_USE_INLINED_ASSEMBLY) + #if defined(C89ATOMIC_HAS_8) + static C89ATOMIC_INLINE c89atomic_uint8 __stdcall c89atomic_fetch_add_explicit_8(volatile c89atomic_uint8* dst, c89atomic_uint8 src, c89atomic_memory_order order) { - ma_uint8 result = 0; + c89atomic_uint8 result = 0; (void)order; __asm { mov ecx, dst @@ -14370,10 +14286,10 @@ typedef int ma_atomic_memory_order; return result; } #endif - #if defined(MA_ATOMIC_HAS_16) - static MA_INLINE ma_uint16 __stdcall ma_atomic_fetch_add_explicit_16(volatile ma_uint16* dst, ma_uint16 src, ma_atomic_memory_order order) + #if defined(C89ATOMIC_HAS_16) + static C89ATOMIC_INLINE c89atomic_uint16 __stdcall c89atomic_fetch_add_explicit_16(volatile c89atomic_uint16* dst, c89atomic_uint16 src, c89atomic_memory_order order) { - ma_uint16 result = 0; + c89atomic_uint16 result = 0; (void)order; __asm { mov ecx, dst @@ -14384,10 +14300,10 @@ typedef int ma_atomic_memory_order; return result; } #endif - #if defined(MA_ATOMIC_HAS_32) - static MA_INLINE ma_uint32 __stdcall ma_atomic_fetch_add_explicit_32(volatile ma_uint32* dst, ma_uint32 src, ma_atomic_memory_order order) + #if defined(C89ATOMIC_HAS_32) + static C89ATOMIC_INLINE c89atomic_uint32 __stdcall c89atomic_fetch_add_explicit_32(volatile c89atomic_uint32* dst, c89atomic_uint32 src, c89atomic_memory_order order) { - ma_uint32 result = 0; + c89atomic_uint32 result = 0; (void)order; __asm { mov ecx, dst @@ -14399,67 +14315,67 @@ typedef int ma_atomic_memory_order; } #endif #else - #if defined(MA_ATOMIC_HAS_8) - static MA_INLINE ma_uint8 __stdcall ma_atomic_fetch_add_explicit_8(volatile ma_uint8* dst, ma_uint8 src, ma_atomic_memory_order order) + #if defined(C89ATOMIC_HAS_8) + static C89ATOMIC_INLINE c89atomic_uint8 __stdcall c89atomic_fetch_add_explicit_8(volatile c89atomic_uint8* dst, c89atomic_uint8 src, c89atomic_memory_order order) { - #if defined(MA_ARM) - MA_ATOMIC_MSVC_ARM_INTRINSIC(dst, src, order, _InterlockedExchangeAdd8, ma_uint8, char); + #if defined(C89ATOMIC_ARM) + C89ATOMIC_MSVC_ARM_INTRINSIC(dst, src, order, _InterlockedExchangeAdd8, c89atomic_uint8, char); #else (void)order; - return (ma_uint8)_InterlockedExchangeAdd8((volatile char*)dst, (char)src); + return (c89atomic_uint8)_InterlockedExchangeAdd8((volatile char*)dst, (char)src); #endif } #endif - #if defined(MA_ATOMIC_HAS_16) - static MA_INLINE ma_uint16 __stdcall ma_atomic_fetch_add_explicit_16(volatile ma_uint16* dst, ma_uint16 src, ma_atomic_memory_order order) + #if defined(C89ATOMIC_HAS_16) + static C89ATOMIC_INLINE c89atomic_uint16 __stdcall c89atomic_fetch_add_explicit_16(volatile c89atomic_uint16* dst, c89atomic_uint16 src, c89atomic_memory_order order) { - #if defined(MA_ARM) - MA_ATOMIC_MSVC_ARM_INTRINSIC(dst, src, order, _InterlockedExchangeAdd16, ma_uint16, short); + #if defined(C89ATOMIC_ARM) + C89ATOMIC_MSVC_ARM_INTRINSIC(dst, src, order, _InterlockedExchangeAdd16, c89atomic_uint16, short); #else (void)order; - return (ma_uint16)_InterlockedExchangeAdd16((volatile short*)dst, (short)src); + return (c89atomic_uint16)_InterlockedExchangeAdd16((volatile short*)dst, (short)src); #endif } #endif - #if defined(MA_ATOMIC_HAS_32) - static MA_INLINE ma_uint32 __stdcall ma_atomic_fetch_add_explicit_32(volatile ma_uint32* dst, ma_uint32 src, ma_atomic_memory_order order) + #if defined(C89ATOMIC_HAS_32) + static C89ATOMIC_INLINE c89atomic_uint32 __stdcall c89atomic_fetch_add_explicit_32(volatile c89atomic_uint32* dst, c89atomic_uint32 src, c89atomic_memory_order order) { - #if defined(MA_ARM) - MA_ATOMIC_MSVC_ARM_INTRINSIC(dst, src, order, _InterlockedExchangeAdd, ma_uint32, long); + #if defined(C89ATOMIC_ARM) + C89ATOMIC_MSVC_ARM_INTRINSIC(dst, src, order, _InterlockedExchangeAdd, c89atomic_uint32, long); #else (void)order; - return (ma_uint32)_InterlockedExchangeAdd((volatile long*)dst, (long)src); + return (c89atomic_uint32)_InterlockedExchangeAdd((volatile long*)dst, (long)src); #endif } #endif - #if defined(MA_ATOMIC_HAS_64) && defined(MA_64BIT) - static MA_INLINE ma_uint64 __stdcall ma_atomic_fetch_add_explicit_64(volatile ma_uint64* dst, ma_uint64 src, ma_atomic_memory_order order) + #if defined(C89ATOMIC_HAS_64) && defined(C89ATOMIC_64BIT) + static C89ATOMIC_INLINE c89atomic_uint64 __stdcall c89atomic_fetch_add_explicit_64(volatile c89atomic_uint64* dst, c89atomic_uint64 src, c89atomic_memory_order order) { - #if defined(MA_ARM) - MA_ATOMIC_MSVC_ARM_INTRINSIC(dst, src, order, _InterlockedExchangeAdd64, ma_uint64, long long); + #if defined(C89ATOMIC_ARM) + C89ATOMIC_MSVC_ARM_INTRINSIC(dst, src, order, _InterlockedExchangeAdd64, c89atomic_uint64, long long); #else (void)order; - return (ma_uint64)_InterlockedExchangeAdd64((volatile long long*)dst, (long long)src); + return (c89atomic_uint64)_InterlockedExchangeAdd64((volatile long long*)dst, (long long)src); #endif } #else #endif #endif - #if defined(MA_ATOMIC_HAS_64) && !defined(MA_64BIT) - static MA_INLINE ma_uint64 __stdcall ma_atomic_fetch_add_explicit_64(volatile ma_uint64* dst, ma_uint64 src, ma_atomic_memory_order order) + #if defined(C89ATOMIC_HAS_64) && !defined(C89ATOMIC_64BIT) + static C89ATOMIC_INLINE c89atomic_uint64 __stdcall c89atomic_fetch_add_explicit_64(volatile c89atomic_uint64* dst, c89atomic_uint64 src, c89atomic_memory_order order) { - ma_uint64 oldValue; - ma_uint64 newValue; + c89atomic_uint64 oldValue; + c89atomic_uint64 newValue; do { oldValue = *dst; newValue = oldValue + src; - } while (ma_atomic_compare_and_swap_64(dst, oldValue, newValue) != oldValue); + } while (c89atomic_compare_and_swap_64(dst, oldValue, newValue) != oldValue); (void)order; return oldValue; } #endif - #if defined(MA_ATOMIC_MSVC_USE_INLINED_ASSEMBLY) - static MA_INLINE void __stdcall ma_atomic_thread_fence(ma_atomic_memory_order order) + #if defined(C89ATOMIC_MSVC_USE_INLINED_ASSEMBLY) + static C89ATOMIC_INLINE void __stdcall c89atomic_thread_fence(c89atomic_memory_order order) { (void)order; __asm { @@ -14467,1076 +14383,1067 @@ typedef int ma_atomic_memory_order; } } #else - #if defined(MA_X64) - #define ma_atomic_thread_fence(order) __faststorefence(), (void)order - #elif defined(MA_ARM64) - #define ma_atomic_thread_fence(order) __dmb(_ARM64_BARRIER_ISH), (void)order + #if defined(C89ATOMIC_X64) + #define c89atomic_thread_fence(order) __faststorefence(), (void)order + #elif defined(C89ATOMIC_ARM64) + #define c89atomic_thread_fence(order) __dmb(_ARM64_BARRIER_ISH), (void)order #else - static MA_INLINE void ma_atomic_thread_fence(ma_atomic_memory_order order) + static C89ATOMIC_INLINE void c89atomic_thread_fence(c89atomic_memory_order order) { - volatile ma_uint32 barrier = 0; - ma_atomic_fetch_add_explicit_32(&barrier, 0, order); + volatile c89atomic_uint32 barrier = 0; + c89atomic_fetch_add_explicit_32(&barrier, 0, order); } #endif #endif - #define ma_atomic_compiler_fence() ma_atomic_thread_fence(ma_atomic_memory_order_seq_cst) - #define ma_atomic_signal_fence(order) ma_atomic_thread_fence(order) - #if defined(MA_ATOMIC_HAS_8) - static MA_INLINE ma_uint8 ma_atomic_load_explicit_8(volatile const ma_uint8* ptr, ma_atomic_memory_order order) + #define c89atomic_compiler_fence() c89atomic_thread_fence(c89atomic_memory_order_seq_cst) + #define c89atomic_signal_fence(order) c89atomic_thread_fence(order) + #if defined(C89ATOMIC_HAS_8) + static C89ATOMIC_INLINE c89atomic_uint8 c89atomic_load_explicit_8(volatile const c89atomic_uint8* ptr, c89atomic_memory_order order) { - #if defined(MA_ARM) - MA_ATOMIC_MSVC_ARM_INTRINSIC_COMPARE_EXCHANGE(ptr, 0, 0, order, _InterlockedCompareExchange8, ma_uint8, char); + #if defined(C89ATOMIC_ARM) + C89ATOMIC_MSVC_ARM_INTRINSIC_COMPARE_EXCHANGE(ptr, 0, 0, order, _InterlockedCompareExchange8, c89atomic_uint8, char); #else (void)order; - return ma_atomic_compare_and_swap_8((volatile ma_uint8*)ptr, 0, 0); + return c89atomic_compare_and_swap_8((volatile c89atomic_uint8*)ptr, 0, 0); #endif } #endif - #if defined(MA_ATOMIC_HAS_16) - static MA_INLINE ma_uint16 ma_atomic_load_explicit_16(volatile const ma_uint16* ptr, ma_atomic_memory_order order) + #if defined(C89ATOMIC_HAS_16) + static C89ATOMIC_INLINE c89atomic_uint16 c89atomic_load_explicit_16(volatile const c89atomic_uint16* ptr, c89atomic_memory_order order) { - #if defined(MA_ARM) - MA_ATOMIC_MSVC_ARM_INTRINSIC_COMPARE_EXCHANGE(ptr, 0, 0, order, _InterlockedCompareExchange16, ma_uint16, short); + #if defined(C89ATOMIC_ARM) + C89ATOMIC_MSVC_ARM_INTRINSIC_COMPARE_EXCHANGE(ptr, 0, 0, order, _InterlockedCompareExchange16, c89atomic_uint16, short); #else (void)order; - return ma_atomic_compare_and_swap_16((volatile ma_uint16*)ptr, 0, 0); + return c89atomic_compare_and_swap_16((volatile c89atomic_uint16*)ptr, 0, 0); #endif } #endif - #if defined(MA_ATOMIC_HAS_32) - static MA_INLINE ma_uint32 ma_atomic_load_explicit_32(volatile const ma_uint32* ptr, ma_atomic_memory_order order) + #if defined(C89ATOMIC_HAS_32) + static C89ATOMIC_INLINE c89atomic_uint32 c89atomic_load_explicit_32(volatile const c89atomic_uint32* ptr, c89atomic_memory_order order) { - #if defined(MA_ARM) - MA_ATOMIC_MSVC_ARM_INTRINSIC_COMPARE_EXCHANGE(ptr, 0, 0, order, _InterlockedCompareExchange, ma_uint32, long); + #if defined(C89ATOMIC_ARM) + C89ATOMIC_MSVC_ARM_INTRINSIC_COMPARE_EXCHANGE(ptr, 0, 0, order, _InterlockedCompareExchange, c89atomic_uint32, long); #else (void)order; - return ma_atomic_compare_and_swap_32((volatile ma_uint32*)ptr, 0, 0); + return c89atomic_compare_and_swap_32((volatile c89atomic_uint32*)ptr, 0, 0); #endif } #endif - #if defined(MA_ATOMIC_HAS_64) - static MA_INLINE ma_uint64 ma_atomic_load_explicit_64(volatile const ma_uint64* ptr, ma_atomic_memory_order order) + #if defined(C89ATOMIC_HAS_64) + static C89ATOMIC_INLINE c89atomic_uint64 c89atomic_load_explicit_64(volatile const c89atomic_uint64* ptr, c89atomic_memory_order order) { - #if defined(MA_ARM) - MA_ATOMIC_MSVC_ARM_INTRINSIC_COMPARE_EXCHANGE(ptr, 0, 0, order, _InterlockedCompareExchange64, ma_uint64, long long); + #if defined(C89ATOMIC_ARM) + C89ATOMIC_MSVC_ARM_INTRINSIC_COMPARE_EXCHANGE(ptr, 0, 0, order, _InterlockedCompareExchange64, c89atomic_uint64, long long); #else (void)order; - return ma_atomic_compare_and_swap_64((volatile ma_uint64*)ptr, 0, 0); + return c89atomic_compare_and_swap_64((volatile c89atomic_uint64*)ptr, 0, 0); #endif } #endif - #if defined(MA_ATOMIC_HAS_8) - #define ma_atomic_store_explicit_8( dst, src, order) (void)ma_atomic_exchange_explicit_8 (dst, src, order) + #if defined(C89ATOMIC_HAS_8) + #define c89atomic_store_explicit_8( dst, src, order) (void)c89atomic_exchange_explicit_8 (dst, src, order) #endif - #if defined(MA_ATOMIC_HAS_16) - #define ma_atomic_store_explicit_16(dst, src, order) (void)ma_atomic_exchange_explicit_16(dst, src, order) + #if defined(C89ATOMIC_HAS_16) + #define c89atomic_store_explicit_16(dst, src, order) (void)c89atomic_exchange_explicit_16(dst, src, order) #endif - #if defined(MA_ATOMIC_HAS_32) - #define ma_atomic_store_explicit_32(dst, src, order) (void)ma_atomic_exchange_explicit_32(dst, src, order) + #if defined(C89ATOMIC_HAS_32) + #define c89atomic_store_explicit_32(dst, src, order) (void)c89atomic_exchange_explicit_32(dst, src, order) #endif - #if defined(MA_ATOMIC_HAS_64) - #define ma_atomic_store_explicit_64(dst, src, order) (void)ma_atomic_exchange_explicit_64(dst, src, order) + #if defined(C89ATOMIC_HAS_64) + #define c89atomic_store_explicit_64(dst, src, order) (void)c89atomic_exchange_explicit_64(dst, src, order) #endif - #if defined(MA_ATOMIC_HAS_8) - static MA_INLINE ma_uint8 __stdcall ma_atomic_fetch_sub_explicit_8(volatile ma_uint8* dst, ma_uint8 src, ma_atomic_memory_order order) + #if defined(C89ATOMIC_HAS_8) + static C89ATOMIC_INLINE c89atomic_uint8 __stdcall c89atomic_fetch_sub_explicit_8(volatile c89atomic_uint8* dst, c89atomic_uint8 src, c89atomic_memory_order order) { - ma_uint8 oldValue; - ma_uint8 newValue; + c89atomic_uint8 oldValue; + c89atomic_uint8 newValue; do { oldValue = *dst; - newValue = (ma_uint8)(oldValue - src); - } while (ma_atomic_compare_and_swap_8(dst, oldValue, newValue) != oldValue); + newValue = (c89atomic_uint8)(oldValue - src); + } while (c89atomic_compare_and_swap_8(dst, oldValue, newValue) != oldValue); (void)order; return oldValue; } #endif - #if defined(MA_ATOMIC_HAS_16) - static MA_INLINE ma_uint16 __stdcall ma_atomic_fetch_sub_explicit_16(volatile ma_uint16* dst, ma_uint16 src, ma_atomic_memory_order order) + #if defined(C89ATOMIC_HAS_16) + static C89ATOMIC_INLINE c89atomic_uint16 __stdcall c89atomic_fetch_sub_explicit_16(volatile c89atomic_uint16* dst, c89atomic_uint16 src, c89atomic_memory_order order) { - ma_uint16 oldValue; - ma_uint16 newValue; + c89atomic_uint16 oldValue; + c89atomic_uint16 newValue; do { oldValue = *dst; - newValue = (ma_uint16)(oldValue - src); - } while (ma_atomic_compare_and_swap_16(dst, oldValue, newValue) != oldValue); + newValue = (c89atomic_uint16)(oldValue - src); + } while (c89atomic_compare_and_swap_16(dst, oldValue, newValue) != oldValue); (void)order; return oldValue; } #endif - #if defined(MA_ATOMIC_HAS_32) - static MA_INLINE ma_uint32 __stdcall ma_atomic_fetch_sub_explicit_32(volatile ma_uint32* dst, ma_uint32 src, ma_atomic_memory_order order) + #if defined(C89ATOMIC_HAS_32) + static C89ATOMIC_INLINE c89atomic_uint32 __stdcall c89atomic_fetch_sub_explicit_32(volatile c89atomic_uint32* dst, c89atomic_uint32 src, c89atomic_memory_order order) { - ma_uint32 oldValue; - ma_uint32 newValue; + c89atomic_uint32 oldValue; + c89atomic_uint32 newValue; do { oldValue = *dst; newValue = oldValue - src; - } while (ma_atomic_compare_and_swap_32(dst, oldValue, newValue) != oldValue); + } while (c89atomic_compare_and_swap_32(dst, oldValue, newValue) != oldValue); (void)order; return oldValue; } #endif - #if defined(MA_ATOMIC_HAS_64) - static MA_INLINE ma_uint64 __stdcall ma_atomic_fetch_sub_explicit_64(volatile ma_uint64* dst, ma_uint64 src, ma_atomic_memory_order order) + #if defined(C89ATOMIC_HAS_64) + static C89ATOMIC_INLINE c89atomic_uint64 __stdcall c89atomic_fetch_sub_explicit_64(volatile c89atomic_uint64* dst, c89atomic_uint64 src, c89atomic_memory_order order) { - ma_uint64 oldValue; - ma_uint64 newValue; + c89atomic_uint64 oldValue; + c89atomic_uint64 newValue; do { oldValue = *dst; newValue = oldValue - src; - } while (ma_atomic_compare_and_swap_64(dst, oldValue, newValue) != oldValue); + } while (c89atomic_compare_and_swap_64(dst, oldValue, newValue) != oldValue); (void)order; return oldValue; } #endif - #if defined(MA_ATOMIC_HAS_8) - static MA_INLINE ma_uint8 __stdcall ma_atomic_fetch_and_explicit_8(volatile ma_uint8* dst, ma_uint8 src, ma_atomic_memory_order order) + #if defined(C89ATOMIC_HAS_8) + static C89ATOMIC_INLINE c89atomic_uint8 __stdcall c89atomic_fetch_and_explicit_8(volatile c89atomic_uint8* dst, c89atomic_uint8 src, c89atomic_memory_order order) { - #if defined(MA_ARM) - MA_ATOMIC_MSVC_ARM_INTRINSIC(dst, src, order, _InterlockedAnd8, ma_uint8, char); + #if defined(C89ATOMIC_ARM) + C89ATOMIC_MSVC_ARM_INTRINSIC(dst, src, order, _InterlockedAnd8, c89atomic_uint8, char); #else - ma_uint8 oldValue; - ma_uint8 newValue; + c89atomic_uint8 oldValue; + c89atomic_uint8 newValue; do { oldValue = *dst; - newValue = (ma_uint8)(oldValue & src); - } while (ma_atomic_compare_and_swap_8(dst, oldValue, newValue) != oldValue); + newValue = (c89atomic_uint8)(oldValue & src); + } while (c89atomic_compare_and_swap_8(dst, oldValue, newValue) != oldValue); (void)order; return oldValue; #endif } #endif - #if defined(MA_ATOMIC_HAS_16) - static MA_INLINE ma_uint16 __stdcall ma_atomic_fetch_and_explicit_16(volatile ma_uint16* dst, ma_uint16 src, ma_atomic_memory_order order) + #if defined(C89ATOMIC_HAS_16) + static C89ATOMIC_INLINE c89atomic_uint16 __stdcall c89atomic_fetch_and_explicit_16(volatile c89atomic_uint16* dst, c89atomic_uint16 src, c89atomic_memory_order order) { - #if defined(MA_ARM) - MA_ATOMIC_MSVC_ARM_INTRINSIC(dst, src, order, _InterlockedAnd16, ma_uint16, short); + #if defined(C89ATOMIC_ARM) + C89ATOMIC_MSVC_ARM_INTRINSIC(dst, src, order, _InterlockedAnd16, c89atomic_uint16, short); #else - ma_uint16 oldValue; - ma_uint16 newValue; + c89atomic_uint16 oldValue; + c89atomic_uint16 newValue; do { oldValue = *dst; - newValue = (ma_uint16)(oldValue & src); - } while (ma_atomic_compare_and_swap_16(dst, oldValue, newValue) != oldValue); + newValue = (c89atomic_uint16)(oldValue & src); + } while (c89atomic_compare_and_swap_16(dst, oldValue, newValue) != oldValue); (void)order; return oldValue; #endif } #endif - #if defined(MA_ATOMIC_HAS_32) - static MA_INLINE ma_uint32 __stdcall ma_atomic_fetch_and_explicit_32(volatile ma_uint32* dst, ma_uint32 src, ma_atomic_memory_order order) + #if defined(C89ATOMIC_HAS_32) + static C89ATOMIC_INLINE c89atomic_uint32 __stdcall c89atomic_fetch_and_explicit_32(volatile c89atomic_uint32* dst, c89atomic_uint32 src, c89atomic_memory_order order) { - #if defined(MA_ARM) - MA_ATOMIC_MSVC_ARM_INTRINSIC(dst, src, order, _InterlockedAnd, ma_uint32, long); + #if defined(C89ATOMIC_ARM) + C89ATOMIC_MSVC_ARM_INTRINSIC(dst, src, order, _InterlockedAnd, c89atomic_uint32, long); #else - ma_uint32 oldValue; - ma_uint32 newValue; + c89atomic_uint32 oldValue; + c89atomic_uint32 newValue; do { oldValue = *dst; newValue = oldValue & src; - } while (ma_atomic_compare_and_swap_32(dst, oldValue, newValue) != oldValue); + } while (c89atomic_compare_and_swap_32(dst, oldValue, newValue) != oldValue); (void)order; return oldValue; #endif } #endif - #if defined(MA_ATOMIC_HAS_64) - static MA_INLINE ma_uint64 __stdcall ma_atomic_fetch_and_explicit_64(volatile ma_uint64* dst, ma_uint64 src, ma_atomic_memory_order order) + #if defined(C89ATOMIC_HAS_64) + static C89ATOMIC_INLINE c89atomic_uint64 __stdcall c89atomic_fetch_and_explicit_64(volatile c89atomic_uint64* dst, c89atomic_uint64 src, c89atomic_memory_order order) { - #if defined(MA_ARM) - MA_ATOMIC_MSVC_ARM_INTRINSIC(dst, src, order, _InterlockedAnd64, ma_uint64, long long); + #if defined(C89ATOMIC_ARM) + C89ATOMIC_MSVC_ARM_INTRINSIC(dst, src, order, _InterlockedAnd64, c89atomic_uint64, long long); #else - ma_uint64 oldValue; - ma_uint64 newValue; + c89atomic_uint64 oldValue; + c89atomic_uint64 newValue; do { oldValue = *dst; newValue = oldValue & src; - } while (ma_atomic_compare_and_swap_64(dst, oldValue, newValue) != oldValue); + } while (c89atomic_compare_and_swap_64(dst, oldValue, newValue) != oldValue); (void)order; return oldValue; #endif } #endif - #if defined(MA_ATOMIC_HAS_8) - static MA_INLINE ma_uint8 __stdcall ma_atomic_fetch_xor_explicit_8(volatile ma_uint8* dst, ma_uint8 src, ma_atomic_memory_order order) + #if defined(C89ATOMIC_HAS_8) + static C89ATOMIC_INLINE c89atomic_uint8 __stdcall c89atomic_fetch_xor_explicit_8(volatile c89atomic_uint8* dst, c89atomic_uint8 src, c89atomic_memory_order order) { - #if defined(MA_ARM) - MA_ATOMIC_MSVC_ARM_INTRINSIC(dst, src, order, _InterlockedXor8, ma_uint8, char); + #if defined(C89ATOMIC_ARM) + C89ATOMIC_MSVC_ARM_INTRINSIC(dst, src, order, _InterlockedXor8, c89atomic_uint8, char); #else - ma_uint8 oldValue; - ma_uint8 newValue; + c89atomic_uint8 oldValue; + c89atomic_uint8 newValue; do { oldValue = *dst; - newValue = (ma_uint8)(oldValue ^ src); - } while (ma_atomic_compare_and_swap_8(dst, oldValue, newValue) != oldValue); + newValue = (c89atomic_uint8)(oldValue ^ src); + } while (c89atomic_compare_and_swap_8(dst, oldValue, newValue) != oldValue); (void)order; return oldValue; #endif } #endif - #if defined(MA_ATOMIC_HAS_16) - static MA_INLINE ma_uint16 __stdcall ma_atomic_fetch_xor_explicit_16(volatile ma_uint16* dst, ma_uint16 src, ma_atomic_memory_order order) + #if defined(C89ATOMIC_HAS_16) + static C89ATOMIC_INLINE c89atomic_uint16 __stdcall c89atomic_fetch_xor_explicit_16(volatile c89atomic_uint16* dst, c89atomic_uint16 src, c89atomic_memory_order order) { - #if defined(MA_ARM) - MA_ATOMIC_MSVC_ARM_INTRINSIC(dst, src, order, _InterlockedXor16, ma_uint16, short); + #if defined(C89ATOMIC_ARM) + C89ATOMIC_MSVC_ARM_INTRINSIC(dst, src, order, _InterlockedXor16, c89atomic_uint16, short); #else - ma_uint16 oldValue; - ma_uint16 newValue; + c89atomic_uint16 oldValue; + c89atomic_uint16 newValue; do { oldValue = *dst; - newValue = (ma_uint16)(oldValue ^ src); - } while (ma_atomic_compare_and_swap_16(dst, oldValue, newValue) != oldValue); + newValue = (c89atomic_uint16)(oldValue ^ src); + } while (c89atomic_compare_and_swap_16(dst, oldValue, newValue) != oldValue); (void)order; return oldValue; #endif } #endif - #if defined(MA_ATOMIC_HAS_32) - static MA_INLINE ma_uint32 __stdcall ma_atomic_fetch_xor_explicit_32(volatile ma_uint32* dst, ma_uint32 src, ma_atomic_memory_order order) + #if defined(C89ATOMIC_HAS_32) + static C89ATOMIC_INLINE c89atomic_uint32 __stdcall c89atomic_fetch_xor_explicit_32(volatile c89atomic_uint32* dst, c89atomic_uint32 src, c89atomic_memory_order order) { - #if defined(MA_ARM) - MA_ATOMIC_MSVC_ARM_INTRINSIC(dst, src, order, _InterlockedXor, ma_uint32, long); + #if defined(C89ATOMIC_ARM) + C89ATOMIC_MSVC_ARM_INTRINSIC(dst, src, order, _InterlockedXor, c89atomic_uint32, long); #else - ma_uint32 oldValue; - ma_uint32 newValue; + c89atomic_uint32 oldValue; + c89atomic_uint32 newValue; do { oldValue = *dst; newValue = oldValue ^ src; - } while (ma_atomic_compare_and_swap_32(dst, oldValue, newValue) != oldValue); + } while (c89atomic_compare_and_swap_32(dst, oldValue, newValue) != oldValue); (void)order; return oldValue; #endif } #endif - #if defined(MA_ATOMIC_HAS_64) - static MA_INLINE ma_uint64 __stdcall ma_atomic_fetch_xor_explicit_64(volatile ma_uint64* dst, ma_uint64 src, ma_atomic_memory_order order) + #if defined(C89ATOMIC_HAS_64) + static C89ATOMIC_INLINE c89atomic_uint64 __stdcall c89atomic_fetch_xor_explicit_64(volatile c89atomic_uint64* dst, c89atomic_uint64 src, c89atomic_memory_order order) { - #if defined(MA_ARM) - MA_ATOMIC_MSVC_ARM_INTRINSIC(dst, src, order, _InterlockedXor64, ma_uint64, long long); + #if defined(C89ATOMIC_ARM) + C89ATOMIC_MSVC_ARM_INTRINSIC(dst, src, order, _InterlockedXor64, c89atomic_uint64, long long); #else - ma_uint64 oldValue; - ma_uint64 newValue; + c89atomic_uint64 oldValue; + c89atomic_uint64 newValue; do { oldValue = *dst; newValue = oldValue ^ src; - } while (ma_atomic_compare_and_swap_64(dst, oldValue, newValue) != oldValue); + } while (c89atomic_compare_and_swap_64(dst, oldValue, newValue) != oldValue); (void)order; return oldValue; #endif } #endif - #if defined(MA_ATOMIC_HAS_8) - static MA_INLINE ma_uint8 __stdcall ma_atomic_fetch_or_explicit_8(volatile ma_uint8* dst, ma_uint8 src, ma_atomic_memory_order order) + #if defined(C89ATOMIC_HAS_8) + static C89ATOMIC_INLINE c89atomic_uint8 __stdcall c89atomic_fetch_or_explicit_8(volatile c89atomic_uint8* dst, c89atomic_uint8 src, c89atomic_memory_order order) { - #if defined(MA_ARM) - MA_ATOMIC_MSVC_ARM_INTRINSIC(dst, src, order, _InterlockedOr8, ma_uint8, char); + #if defined(C89ATOMIC_ARM) + C89ATOMIC_MSVC_ARM_INTRINSIC(dst, src, order, _InterlockedOr8, c89atomic_uint8, char); #else - ma_uint8 oldValue; - ma_uint8 newValue; + c89atomic_uint8 oldValue; + c89atomic_uint8 newValue; do { oldValue = *dst; - newValue = (ma_uint8)(oldValue | src); - } while (ma_atomic_compare_and_swap_8(dst, oldValue, newValue) != oldValue); + newValue = (c89atomic_uint8)(oldValue | src); + } while (c89atomic_compare_and_swap_8(dst, oldValue, newValue) != oldValue); (void)order; return oldValue; #endif } #endif - #if defined(MA_ATOMIC_HAS_16) - static MA_INLINE ma_uint16 __stdcall ma_atomic_fetch_or_explicit_16(volatile ma_uint16* dst, ma_uint16 src, ma_atomic_memory_order order) + #if defined(C89ATOMIC_HAS_16) + static C89ATOMIC_INLINE c89atomic_uint16 __stdcall c89atomic_fetch_or_explicit_16(volatile c89atomic_uint16* dst, c89atomic_uint16 src, c89atomic_memory_order order) { - #if defined(MA_ARM) - MA_ATOMIC_MSVC_ARM_INTRINSIC(dst, src, order, _InterlockedOr16, ma_uint16, short); + #if defined(C89ATOMIC_ARM) + C89ATOMIC_MSVC_ARM_INTRINSIC(dst, src, order, _InterlockedOr16, c89atomic_uint16, short); #else - ma_uint16 oldValue; - ma_uint16 newValue; + c89atomic_uint16 oldValue; + c89atomic_uint16 newValue; do { oldValue = *dst; - newValue = (ma_uint16)(oldValue | src); - } while (ma_atomic_compare_and_swap_16(dst, oldValue, newValue) != oldValue); + newValue = (c89atomic_uint16)(oldValue | src); + } while (c89atomic_compare_and_swap_16(dst, oldValue, newValue) != oldValue); (void)order; return oldValue; #endif } #endif - #if defined(MA_ATOMIC_HAS_32) - static MA_INLINE ma_uint32 __stdcall ma_atomic_fetch_or_explicit_32(volatile ma_uint32* dst, ma_uint32 src, ma_atomic_memory_order order) + #if defined(C89ATOMIC_HAS_32) + static C89ATOMIC_INLINE c89atomic_uint32 __stdcall c89atomic_fetch_or_explicit_32(volatile c89atomic_uint32* dst, c89atomic_uint32 src, c89atomic_memory_order order) { - #if defined(MA_ARM) - MA_ATOMIC_MSVC_ARM_INTRINSIC(dst, src, order, _InterlockedOr, ma_uint32, long); + #if defined(C89ATOMIC_ARM) + C89ATOMIC_MSVC_ARM_INTRINSIC(dst, src, order, _InterlockedOr, c89atomic_uint32, long); #else - ma_uint32 oldValue; - ma_uint32 newValue; + c89atomic_uint32 oldValue; + c89atomic_uint32 newValue; do { oldValue = *dst; newValue = oldValue | src; - } while (ma_atomic_compare_and_swap_32(dst, oldValue, newValue) != oldValue); + } while (c89atomic_compare_and_swap_32(dst, oldValue, newValue) != oldValue); (void)order; return oldValue; #endif } #endif - #if defined(MA_ATOMIC_HAS_64) - static MA_INLINE ma_uint64 __stdcall ma_atomic_fetch_or_explicit_64(volatile ma_uint64* dst, ma_uint64 src, ma_atomic_memory_order order) + #if defined(C89ATOMIC_HAS_64) + static C89ATOMIC_INLINE c89atomic_uint64 __stdcall c89atomic_fetch_or_explicit_64(volatile c89atomic_uint64* dst, c89atomic_uint64 src, c89atomic_memory_order order) { - #if defined(MA_ARM) - MA_ATOMIC_MSVC_ARM_INTRINSIC(dst, src, order, _InterlockedOr64, ma_uint64, long long); + #if defined(C89ATOMIC_ARM) + C89ATOMIC_MSVC_ARM_INTRINSIC(dst, src, order, _InterlockedOr64, c89atomic_uint64, long long); #else - ma_uint64 oldValue; - ma_uint64 newValue; + c89atomic_uint64 oldValue; + c89atomic_uint64 newValue; do { oldValue = *dst; newValue = oldValue | src; - } while (ma_atomic_compare_and_swap_64(dst, oldValue, newValue) != oldValue); + } while (c89atomic_compare_and_swap_64(dst, oldValue, newValue) != oldValue); (void)order; return oldValue; #endif } #endif - #if defined(MA_ATOMIC_HAS_8) - #define ma_atomic_test_and_set_explicit_8( dst, order) ma_atomic_exchange_explicit_8 (dst, 1, order) + #if defined(C89ATOMIC_HAS_8) + #define c89atomic_test_and_set_explicit_8( dst, order) c89atomic_exchange_explicit_8 (dst, 1, order) #endif - #if defined(MA_ATOMIC_HAS_16) - #define ma_atomic_test_and_set_explicit_16(dst, order) ma_atomic_exchange_explicit_16(dst, 1, order) + #if defined(C89ATOMIC_HAS_16) + #define c89atomic_test_and_set_explicit_16(dst, order) c89atomic_exchange_explicit_16(dst, 1, order) #endif - #if defined(MA_ATOMIC_HAS_32) - #define ma_atomic_test_and_set_explicit_32(dst, order) ma_atomic_exchange_explicit_32(dst, 1, order) + #if defined(C89ATOMIC_HAS_32) + #define c89atomic_test_and_set_explicit_32(dst, order) c89atomic_exchange_explicit_32(dst, 1, order) #endif - #if defined(MA_ATOMIC_HAS_64) - #define ma_atomic_test_and_set_explicit_64(dst, order) ma_atomic_exchange_explicit_64(dst, 1, order) + #if defined(C89ATOMIC_HAS_64) + #define c89atomic_test_and_set_explicit_64(dst, order) c89atomic_exchange_explicit_64(dst, 1, order) #endif - #if defined(MA_ATOMIC_HAS_8) - #define ma_atomic_clear_explicit_8( dst, order) ma_atomic_store_explicit_8 (dst, 0, order) + #if defined(C89ATOMIC_HAS_8) + #define c89atomic_clear_explicit_8( dst, order) c89atomic_store_explicit_8 (dst, 0, order) #endif - #if defined(MA_ATOMIC_HAS_16) - #define ma_atomic_clear_explicit_16(dst, order) ma_atomic_store_explicit_16(dst, 0, order) + #if defined(C89ATOMIC_HAS_16) + #define c89atomic_clear_explicit_16(dst, order) c89atomic_store_explicit_16(dst, 0, order) #endif - #if defined(MA_ATOMIC_HAS_32) - #define ma_atomic_clear_explicit_32(dst, order) ma_atomic_store_explicit_32(dst, 0, order) + #if defined(C89ATOMIC_HAS_32) + #define c89atomic_clear_explicit_32(dst, order) c89atomic_store_explicit_32(dst, 0, order) #endif - #if defined(MA_ATOMIC_HAS_64) - #define ma_atomic_clear_explicit_64(dst, order) ma_atomic_store_explicit_64(dst, 0, order) + #if defined(C89ATOMIC_HAS_64) + #define c89atomic_clear_explicit_64(dst, order) c89atomic_store_explicit_64(dst, 0, order) #endif - #if defined(MA_ATOMIC_HAS_8) - typedef ma_uint8 ma_atomic_flag; - #define ma_atomic_flag_test_and_set_explicit(ptr, order) (ma_bool32)ma_atomic_test_and_set_explicit_8(ptr, order) - #define ma_atomic_flag_clear_explicit(ptr, order) ma_atomic_clear_explicit_8(ptr, order) - #define ma_atomic_flag_load_explicit(ptr, order) ma_atomic_load_explicit_8(ptr, order) + #if defined(C89ATOMIC_HAS_8) + typedef c89atomic_uint8 c89atomic_flag; + #define c89atomic_flag_test_and_set_explicit(ptr, order) (c89atomic_bool)c89atomic_test_and_set_explicit_8(ptr, order) + #define c89atomic_flag_clear_explicit(ptr, order) c89atomic_clear_explicit_8(ptr, order) + #define c89atoimc_flag_load_explicit(ptr, order) c89atomic_load_explicit_8(ptr, order) #else - typedef ma_uint32 ma_atomic_flag; - #define ma_atomic_flag_test_and_set_explicit(ptr, order) (ma_bool32)ma_atomic_test_and_set_explicit_32(ptr, order) - #define ma_atomic_flag_clear_explicit(ptr, order) ma_atomic_clear_explicit_32(ptr, order) - #define ma_atomic_flag_load_explicit(ptr, order) ma_atomic_load_explicit_32(ptr, order) + typedef c89atomic_uint32 c89atomic_flag; + #define c89atomic_flag_test_and_set_explicit(ptr, order) (c89atomic_bool)c89atomic_test_and_set_explicit_32(ptr, order) + #define c89atomic_flag_clear_explicit(ptr, order) c89atomic_clear_explicit_32(ptr, order) + #define c89atoimc_flag_load_explicit(ptr, order) c89atomic_load_explicit_32(ptr, order) #endif #elif defined(__clang__) || (defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 7))) - #define MA_ATOMIC_HAS_NATIVE_COMPARE_EXCHANGE - #define MA_ATOMIC_HAS_NATIVE_IS_LOCK_FREE - #define ma_atomic_memory_order_relaxed __ATOMIC_RELAXED - #define ma_atomic_memory_order_consume __ATOMIC_CONSUME - #define ma_atomic_memory_order_acquire __ATOMIC_ACQUIRE - #define ma_atomic_memory_order_release __ATOMIC_RELEASE - #define ma_atomic_memory_order_acq_rel __ATOMIC_ACQ_REL - #define ma_atomic_memory_order_seq_cst __ATOMIC_SEQ_CST - #define ma_atomic_compiler_fence() __asm__ __volatile__("":::"memory") - #define ma_atomic_thread_fence(order) __atomic_thread_fence(order) - #define ma_atomic_signal_fence(order) __atomic_signal_fence(order) - #define ma_atomic_is_lock_free_8(ptr) __atomic_is_lock_free(1, ptr) - #define ma_atomic_is_lock_free_16(ptr) __atomic_is_lock_free(2, ptr) - #define ma_atomic_is_lock_free_32(ptr) __atomic_is_lock_free(4, ptr) - #define ma_atomic_is_lock_free_64(ptr) __atomic_is_lock_free(8, ptr) - #define ma_atomic_test_and_set_explicit_8( dst, order) __atomic_exchange_n(dst, 1, order) - #define ma_atomic_test_and_set_explicit_16(dst, order) __atomic_exchange_n(dst, 1, order) - #define ma_atomic_test_and_set_explicit_32(dst, order) __atomic_exchange_n(dst, 1, order) - #define ma_atomic_test_and_set_explicit_64(dst, order) __atomic_exchange_n(dst, 1, order) - #define ma_atomic_clear_explicit_8( dst, order) __atomic_store_n(dst, 0, order) - #define ma_atomic_clear_explicit_16(dst, order) __atomic_store_n(dst, 0, order) - #define ma_atomic_clear_explicit_32(dst, order) __atomic_store_n(dst, 0, order) - #define ma_atomic_clear_explicit_64(dst, order) __atomic_store_n(dst, 0, order) - #define ma_atomic_store_explicit_8( dst, src, order) __atomic_store_n(dst, src, order) - #define ma_atomic_store_explicit_16(dst, src, order) __atomic_store_n(dst, src, order) - #define ma_atomic_store_explicit_32(dst, src, order) __atomic_store_n(dst, src, order) - #define ma_atomic_store_explicit_64(dst, src, order) __atomic_store_n(dst, src, order) - #define ma_atomic_load_explicit_8( dst, order) __atomic_load_n(dst, order) - #define ma_atomic_load_explicit_16(dst, order) __atomic_load_n(dst, order) - #define ma_atomic_load_explicit_32(dst, order) __atomic_load_n(dst, order) - #define ma_atomic_load_explicit_64(dst, order) __atomic_load_n(dst, order) - #define ma_atomic_exchange_explicit_8( dst, src, order) __atomic_exchange_n(dst, src, order) - #define ma_atomic_exchange_explicit_16(dst, src, order) __atomic_exchange_n(dst, src, order) - #define ma_atomic_exchange_explicit_32(dst, src, order) __atomic_exchange_n(dst, src, order) - #define ma_atomic_exchange_explicit_64(dst, src, order) __atomic_exchange_n(dst, src, order) - #define ma_atomic_compare_exchange_strong_explicit_8( dst, expected, desired, successOrder, failureOrder) __atomic_compare_exchange_n(dst, expected, desired, 0, successOrder, failureOrder) - #define ma_atomic_compare_exchange_strong_explicit_16(dst, expected, desired, successOrder, failureOrder) __atomic_compare_exchange_n(dst, expected, desired, 0, successOrder, failureOrder) - #define ma_atomic_compare_exchange_strong_explicit_32(dst, expected, desired, successOrder, failureOrder) __atomic_compare_exchange_n(dst, expected, desired, 0, successOrder, failureOrder) - #define ma_atomic_compare_exchange_strong_explicit_64(dst, expected, desired, successOrder, failureOrder) __atomic_compare_exchange_n(dst, expected, desired, 0, successOrder, failureOrder) - #define ma_atomic_compare_exchange_weak_explicit_8( dst, expected, desired, successOrder, failureOrder) __atomic_compare_exchange_n(dst, expected, desired, 1, successOrder, failureOrder) - #define ma_atomic_compare_exchange_weak_explicit_16(dst, expected, desired, successOrder, failureOrder) __atomic_compare_exchange_n(dst, expected, desired, 1, successOrder, failureOrder) - #define ma_atomic_compare_exchange_weak_explicit_32(dst, expected, desired, successOrder, failureOrder) __atomic_compare_exchange_n(dst, expected, desired, 1, successOrder, failureOrder) - #define ma_atomic_compare_exchange_weak_explicit_64(dst, expected, desired, successOrder, failureOrder) __atomic_compare_exchange_n(dst, expected, desired, 1, successOrder, failureOrder) - #define ma_atomic_fetch_add_explicit_8( dst, src, order) __atomic_fetch_add(dst, src, order) - #define ma_atomic_fetch_add_explicit_16(dst, src, order) __atomic_fetch_add(dst, src, order) - #define ma_atomic_fetch_add_explicit_32(dst, src, order) __atomic_fetch_add(dst, src, order) - #define ma_atomic_fetch_add_explicit_64(dst, src, order) __atomic_fetch_add(dst, src, order) - #define ma_atomic_fetch_sub_explicit_8( dst, src, order) __atomic_fetch_sub(dst, src, order) - #define ma_atomic_fetch_sub_explicit_16(dst, src, order) __atomic_fetch_sub(dst, src, order) - #define ma_atomic_fetch_sub_explicit_32(dst, src, order) __atomic_fetch_sub(dst, src, order) - #define ma_atomic_fetch_sub_explicit_64(dst, src, order) __atomic_fetch_sub(dst, src, order) - #define ma_atomic_fetch_or_explicit_8( dst, src, order) __atomic_fetch_or(dst, src, order) - #define ma_atomic_fetch_or_explicit_16(dst, src, order) __atomic_fetch_or(dst, src, order) - #define ma_atomic_fetch_or_explicit_32(dst, src, order) __atomic_fetch_or(dst, src, order) - #define ma_atomic_fetch_or_explicit_64(dst, src, order) __atomic_fetch_or(dst, src, order) - #define ma_atomic_fetch_xor_explicit_8( dst, src, order) __atomic_fetch_xor(dst, src, order) - #define ma_atomic_fetch_xor_explicit_16(dst, src, order) __atomic_fetch_xor(dst, src, order) - #define ma_atomic_fetch_xor_explicit_32(dst, src, order) __atomic_fetch_xor(dst, src, order) - #define ma_atomic_fetch_xor_explicit_64(dst, src, order) __atomic_fetch_xor(dst, src, order) - #define ma_atomic_fetch_and_explicit_8( dst, src, order) __atomic_fetch_and(dst, src, order) - #define ma_atomic_fetch_and_explicit_16(dst, src, order) __atomic_fetch_and(dst, src, order) - #define ma_atomic_fetch_and_explicit_32(dst, src, order) __atomic_fetch_and(dst, src, order) - #define ma_atomic_fetch_and_explicit_64(dst, src, order) __atomic_fetch_and(dst, src, order) - static MA_INLINE ma_uint8 ma_atomic_compare_and_swap_8(volatile ma_uint8* dst, ma_uint8 expected, ma_uint8 desired) + #define C89ATOMIC_HAS_NATIVE_COMPARE_EXCHANGE + #define C89ATOMIC_HAS_NATIVE_IS_LOCK_FREE + #define c89atomic_memory_order_relaxed __ATOMIC_RELAXED + #define c89atomic_memory_order_consume __ATOMIC_CONSUME + #define c89atomic_memory_order_acquire __ATOMIC_ACQUIRE + #define c89atomic_memory_order_release __ATOMIC_RELEASE + #define c89atomic_memory_order_acq_rel __ATOMIC_ACQ_REL + #define c89atomic_memory_order_seq_cst __ATOMIC_SEQ_CST + #define c89atomic_compiler_fence() __asm__ __volatile__("":::"memory") + #define c89atomic_thread_fence(order) __atomic_thread_fence(order) + #define c89atomic_signal_fence(order) __atomic_signal_fence(order) + #define c89atomic_is_lock_free_8(ptr) __atomic_is_lock_free(1, ptr) + #define c89atomic_is_lock_free_16(ptr) __atomic_is_lock_free(2, ptr) + #define c89atomic_is_lock_free_32(ptr) __atomic_is_lock_free(4, ptr) + #define c89atomic_is_lock_free_64(ptr) __atomic_is_lock_free(8, ptr) + #define c89atomic_test_and_set_explicit_8( dst, order) __atomic_exchange_n(dst, 1, order) + #define c89atomic_test_and_set_explicit_16(dst, order) __atomic_exchange_n(dst, 1, order) + #define c89atomic_test_and_set_explicit_32(dst, order) __atomic_exchange_n(dst, 1, order) + #define c89atomic_test_and_set_explicit_64(dst, order) __atomic_exchange_n(dst, 1, order) + #define c89atomic_clear_explicit_8( dst, order) __atomic_store_n(dst, 0, order) + #define c89atomic_clear_explicit_16(dst, order) __atomic_store_n(dst, 0, order) + #define c89atomic_clear_explicit_32(dst, order) __atomic_store_n(dst, 0, order) + #define c89atomic_clear_explicit_64(dst, order) __atomic_store_n(dst, 0, order) + #define c89atomic_store_explicit_8( dst, src, order) __atomic_store_n(dst, src, order) + #define c89atomic_store_explicit_16(dst, src, order) __atomic_store_n(dst, src, order) + #define c89atomic_store_explicit_32(dst, src, order) __atomic_store_n(dst, src, order) + #define c89atomic_store_explicit_64(dst, src, order) __atomic_store_n(dst, src, order) + #define c89atomic_load_explicit_8( dst, order) __atomic_load_n(dst, order) + #define c89atomic_load_explicit_16(dst, order) __atomic_load_n(dst, order) + #define c89atomic_load_explicit_32(dst, order) __atomic_load_n(dst, order) + #define c89atomic_load_explicit_64(dst, order) __atomic_load_n(dst, order) + #define c89atomic_exchange_explicit_8( dst, src, order) __atomic_exchange_n(dst, src, order) + #define c89atomic_exchange_explicit_16(dst, src, order) __atomic_exchange_n(dst, src, order) + #define c89atomic_exchange_explicit_32(dst, src, order) __atomic_exchange_n(dst, src, order) + #define c89atomic_exchange_explicit_64(dst, src, order) __atomic_exchange_n(dst, src, order) + #define c89atomic_compare_exchange_strong_explicit_8( dst, expected, desired, successOrder, failureOrder) __atomic_compare_exchange_n(dst, expected, desired, 0, successOrder, failureOrder) + #define c89atomic_compare_exchange_strong_explicit_16(dst, expected, desired, successOrder, failureOrder) __atomic_compare_exchange_n(dst, expected, desired, 0, successOrder, failureOrder) + #define c89atomic_compare_exchange_strong_explicit_32(dst, expected, desired, successOrder, failureOrder) __atomic_compare_exchange_n(dst, expected, desired, 0, successOrder, failureOrder) + #define c89atomic_compare_exchange_strong_explicit_64(dst, expected, desired, successOrder, failureOrder) __atomic_compare_exchange_n(dst, expected, desired, 0, successOrder, failureOrder) + #define c89atomic_compare_exchange_weak_explicit_8( dst, expected, desired, successOrder, failureOrder) __atomic_compare_exchange_n(dst, expected, desired, 1, successOrder, failureOrder) + #define c89atomic_compare_exchange_weak_explicit_16(dst, expected, desired, successOrder, failureOrder) __atomic_compare_exchange_n(dst, expected, desired, 1, successOrder, failureOrder) + #define c89atomic_compare_exchange_weak_explicit_32(dst, expected, desired, successOrder, failureOrder) __atomic_compare_exchange_n(dst, expected, desired, 1, successOrder, failureOrder) + #define c89atomic_compare_exchange_weak_explicit_64(dst, expected, desired, successOrder, failureOrder) __atomic_compare_exchange_n(dst, expected, desired, 1, successOrder, failureOrder) + #define c89atomic_fetch_add_explicit_8( dst, src, order) __atomic_fetch_add(dst, src, order) + #define c89atomic_fetch_add_explicit_16(dst, src, order) __atomic_fetch_add(dst, src, order) + #define c89atomic_fetch_add_explicit_32(dst, src, order) __atomic_fetch_add(dst, src, order) + #define c89atomic_fetch_add_explicit_64(dst, src, order) __atomic_fetch_add(dst, src, order) + #define c89atomic_fetch_sub_explicit_8( dst, src, order) __atomic_fetch_sub(dst, src, order) + #define c89atomic_fetch_sub_explicit_16(dst, src, order) __atomic_fetch_sub(dst, src, order) + #define c89atomic_fetch_sub_explicit_32(dst, src, order) __atomic_fetch_sub(dst, src, order) + #define c89atomic_fetch_sub_explicit_64(dst, src, order) __atomic_fetch_sub(dst, src, order) + #define c89atomic_fetch_or_explicit_8( dst, src, order) __atomic_fetch_or(dst, src, order) + #define c89atomic_fetch_or_explicit_16(dst, src, order) __atomic_fetch_or(dst, src, order) + #define c89atomic_fetch_or_explicit_32(dst, src, order) __atomic_fetch_or(dst, src, order) + #define c89atomic_fetch_or_explicit_64(dst, src, order) __atomic_fetch_or(dst, src, order) + #define c89atomic_fetch_xor_explicit_8( dst, src, order) __atomic_fetch_xor(dst, src, order) + #define c89atomic_fetch_xor_explicit_16(dst, src, order) __atomic_fetch_xor(dst, src, order) + #define c89atomic_fetch_xor_explicit_32(dst, src, order) __atomic_fetch_xor(dst, src, order) + #define c89atomic_fetch_xor_explicit_64(dst, src, order) __atomic_fetch_xor(dst, src, order) + #define c89atomic_fetch_and_explicit_8( dst, src, order) __atomic_fetch_and(dst, src, order) + #define c89atomic_fetch_and_explicit_16(dst, src, order) __atomic_fetch_and(dst, src, order) + #define c89atomic_fetch_and_explicit_32(dst, src, order) __atomic_fetch_and(dst, src, order) + #define c89atomic_fetch_and_explicit_64(dst, src, order) __atomic_fetch_and(dst, src, order) + static C89ATOMIC_INLINE c89atomic_uint8 c89atomic_compare_and_swap_8(volatile c89atomic_uint8* dst, c89atomic_uint8 expected, c89atomic_uint8 desired) { __atomic_compare_exchange_n(dst, &expected, desired, 0, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST); return expected; } - static MA_INLINE ma_uint16 ma_atomic_compare_and_swap_16(volatile ma_uint16* dst, ma_uint16 expected, ma_uint16 desired) + static C89ATOMIC_INLINE c89atomic_uint16 c89atomic_compare_and_swap_16(volatile c89atomic_uint16* dst, c89atomic_uint16 expected, c89atomic_uint16 desired) { __atomic_compare_exchange_n(dst, &expected, desired, 0, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST); return expected; } - static MA_INLINE ma_uint32 ma_atomic_compare_and_swap_32(volatile ma_uint32* dst, ma_uint32 expected, ma_uint32 desired) + static C89ATOMIC_INLINE c89atomic_uint32 c89atomic_compare_and_swap_32(volatile c89atomic_uint32* dst, c89atomic_uint32 expected, c89atomic_uint32 desired) { __atomic_compare_exchange_n(dst, &expected, desired, 0, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST); return expected; } - #if defined(__clang__) - #pragma clang diagnostic push - #if __clang_major__ >= 8 - #pragma clang diagnostic ignored "-Watomic-alignment" - #endif - #endif - static MA_INLINE ma_uint64 ma_atomic_compare_and_swap_64(volatile ma_uint64* dst, ma_uint64 expected, ma_uint64 desired) + static C89ATOMIC_INLINE c89atomic_uint64 c89atomic_compare_and_swap_64(volatile c89atomic_uint64* dst, c89atomic_uint64 expected, c89atomic_uint64 desired) { __atomic_compare_exchange_n(dst, &expected, desired, 0, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST); return expected; } - #if defined(__clang__) - #pragma clang diagnostic pop - #endif - typedef ma_uint8 ma_atomic_flag; - #define ma_atomic_flag_test_and_set_explicit(dst, order) (ma_bool32)__atomic_test_and_set(dst, order) - #define ma_atomic_flag_clear_explicit(dst, order) __atomic_clear(dst, order) - #define ma_atomic_flag_load_explicit(ptr, order) ma_atomic_load_explicit_8(ptr, order) + typedef c89atomic_uint8 c89atomic_flag; + #define c89atomic_flag_test_and_set_explicit(dst, order) (c89atomic_bool)__atomic_test_and_set(dst, order) + #define c89atomic_flag_clear_explicit(dst, order) __atomic_clear(dst, order) + #define c89atoimc_flag_load_explicit(ptr, order) c89atomic_load_explicit_8(ptr, order) #else - #define ma_atomic_memory_order_relaxed 1 - #define ma_atomic_memory_order_consume 2 - #define ma_atomic_memory_order_acquire 3 - #define ma_atomic_memory_order_release 4 - #define ma_atomic_memory_order_acq_rel 5 - #define ma_atomic_memory_order_seq_cst 6 - #define ma_atomic_compiler_fence() __asm__ __volatile__("":::"memory") + #define c89atomic_memory_order_relaxed 1 + #define c89atomic_memory_order_consume 2 + #define c89atomic_memory_order_acquire 3 + #define c89atomic_memory_order_release 4 + #define c89atomic_memory_order_acq_rel 5 + #define c89atomic_memory_order_seq_cst 6 + #define c89atomic_compiler_fence() __asm__ __volatile__("":::"memory") #if defined(__GNUC__) - #define ma_atomic_thread_fence(order) __sync_synchronize(), (void)order - static MA_INLINE ma_uint8 ma_atomic_exchange_explicit_8(volatile ma_uint8* dst, ma_uint8 src, ma_atomic_memory_order order) + #define c89atomic_thread_fence(order) __sync_synchronize(), (void)order + static C89ATOMIC_INLINE c89atomic_uint8 c89atomic_exchange_explicit_8(volatile c89atomic_uint8* dst, c89atomic_uint8 src, c89atomic_memory_order order) { - if (order > ma_atomic_memory_order_acquire) { + if (order > c89atomic_memory_order_acquire) { __sync_synchronize(); } return __sync_lock_test_and_set(dst, src); } - static MA_INLINE ma_uint16 ma_atomic_exchange_explicit_16(volatile ma_uint16* dst, ma_uint16 src, ma_atomic_memory_order order) + static C89ATOMIC_INLINE c89atomic_uint16 c89atomic_exchange_explicit_16(volatile c89atomic_uint16* dst, c89atomic_uint16 src, c89atomic_memory_order order) { - ma_uint16 oldValue; + c89atomic_uint16 oldValue; do { oldValue = *dst; } while (__sync_val_compare_and_swap(dst, oldValue, src) != oldValue); (void)order; return oldValue; } - static MA_INLINE ma_uint32 ma_atomic_exchange_explicit_32(volatile ma_uint32* dst, ma_uint32 src, ma_atomic_memory_order order) + static C89ATOMIC_INLINE c89atomic_uint32 c89atomic_exchange_explicit_32(volatile c89atomic_uint32* dst, c89atomic_uint32 src, c89atomic_memory_order order) { - ma_uint32 oldValue; + c89atomic_uint32 oldValue; do { oldValue = *dst; } while (__sync_val_compare_and_swap(dst, oldValue, src) != oldValue); (void)order; return oldValue; } - static MA_INLINE ma_uint64 ma_atomic_exchange_explicit_64(volatile ma_uint64* dst, ma_uint64 src, ma_atomic_memory_order order) + static C89ATOMIC_INLINE c89atomic_uint64 c89atomic_exchange_explicit_64(volatile c89atomic_uint64* dst, c89atomic_uint64 src, c89atomic_memory_order order) { - ma_uint64 oldValue; + c89atomic_uint64 oldValue; do { oldValue = *dst; } while (__sync_val_compare_and_swap(dst, oldValue, src) != oldValue); (void)order; return oldValue; } - static MA_INLINE ma_uint8 ma_atomic_fetch_add_explicit_8(volatile ma_uint8* dst, ma_uint8 src, ma_atomic_memory_order order) + static C89ATOMIC_INLINE c89atomic_uint8 c89atomic_fetch_add_explicit_8(volatile c89atomic_uint8* dst, c89atomic_uint8 src, c89atomic_memory_order order) { (void)order; return __sync_fetch_and_add(dst, src); } - static MA_INLINE ma_uint16 ma_atomic_fetch_add_explicit_16(volatile ma_uint16* dst, ma_uint16 src, ma_atomic_memory_order order) + static C89ATOMIC_INLINE c89atomic_uint16 c89atomic_fetch_add_explicit_16(volatile c89atomic_uint16* dst, c89atomic_uint16 src, c89atomic_memory_order order) { (void)order; return __sync_fetch_and_add(dst, src); } - static MA_INLINE ma_uint32 ma_atomic_fetch_add_explicit_32(volatile ma_uint32* dst, ma_uint32 src, ma_atomic_memory_order order) + static C89ATOMIC_INLINE c89atomic_uint32 c89atomic_fetch_add_explicit_32(volatile c89atomic_uint32* dst, c89atomic_uint32 src, c89atomic_memory_order order) { (void)order; return __sync_fetch_and_add(dst, src); } - static MA_INLINE ma_uint64 ma_atomic_fetch_add_explicit_64(volatile ma_uint64* dst, ma_uint64 src, ma_atomic_memory_order order) + static C89ATOMIC_INLINE c89atomic_uint64 c89atomic_fetch_add_explicit_64(volatile c89atomic_uint64* dst, c89atomic_uint64 src, c89atomic_memory_order order) { (void)order; return __sync_fetch_and_add(dst, src); } - static MA_INLINE ma_uint8 ma_atomic_fetch_sub_explicit_8(volatile ma_uint8* dst, ma_uint8 src, ma_atomic_memory_order order) + static C89ATOMIC_INLINE c89atomic_uint8 c89atomic_fetch_sub_explicit_8(volatile c89atomic_uint8* dst, c89atomic_uint8 src, c89atomic_memory_order order) { (void)order; return __sync_fetch_and_sub(dst, src); } - static MA_INLINE ma_uint16 ma_atomic_fetch_sub_explicit_16(volatile ma_uint16* dst, ma_uint16 src, ma_atomic_memory_order order) + static C89ATOMIC_INLINE c89atomic_uint16 c89atomic_fetch_sub_explicit_16(volatile c89atomic_uint16* dst, c89atomic_uint16 src, c89atomic_memory_order order) { (void)order; return __sync_fetch_and_sub(dst, src); } - static MA_INLINE ma_uint32 ma_atomic_fetch_sub_explicit_32(volatile ma_uint32* dst, ma_uint32 src, ma_atomic_memory_order order) + static C89ATOMIC_INLINE c89atomic_uint32 c89atomic_fetch_sub_explicit_32(volatile c89atomic_uint32* dst, c89atomic_uint32 src, c89atomic_memory_order order) { (void)order; return __sync_fetch_and_sub(dst, src); } - static MA_INLINE ma_uint64 ma_atomic_fetch_sub_explicit_64(volatile ma_uint64* dst, ma_uint64 src, ma_atomic_memory_order order) + static C89ATOMIC_INLINE c89atomic_uint64 c89atomic_fetch_sub_explicit_64(volatile c89atomic_uint64* dst, c89atomic_uint64 src, c89atomic_memory_order order) { (void)order; return __sync_fetch_and_sub(dst, src); } - static MA_INLINE ma_uint8 ma_atomic_fetch_or_explicit_8(volatile ma_uint8* dst, ma_uint8 src, ma_atomic_memory_order order) + static C89ATOMIC_INLINE c89atomic_uint8 c89atomic_fetch_or_explicit_8(volatile c89atomic_uint8* dst, c89atomic_uint8 src, c89atomic_memory_order order) { (void)order; return __sync_fetch_and_or(dst, src); } - static MA_INLINE ma_uint16 ma_atomic_fetch_or_explicit_16(volatile ma_uint16* dst, ma_uint16 src, ma_atomic_memory_order order) + static C89ATOMIC_INLINE c89atomic_uint16 c89atomic_fetch_or_explicit_16(volatile c89atomic_uint16* dst, c89atomic_uint16 src, c89atomic_memory_order order) { (void)order; return __sync_fetch_and_or(dst, src); } - static MA_INLINE ma_uint32 ma_atomic_fetch_or_explicit_32(volatile ma_uint32* dst, ma_uint32 src, ma_atomic_memory_order order) + static C89ATOMIC_INLINE c89atomic_uint32 c89atomic_fetch_or_explicit_32(volatile c89atomic_uint32* dst, c89atomic_uint32 src, c89atomic_memory_order order) { (void)order; return __sync_fetch_and_or(dst, src); } - static MA_INLINE ma_uint64 ma_atomic_fetch_or_explicit_64(volatile ma_uint64* dst, ma_uint64 src, ma_atomic_memory_order order) + static C89ATOMIC_INLINE c89atomic_uint64 c89atomic_fetch_or_explicit_64(volatile c89atomic_uint64* dst, c89atomic_uint64 src, c89atomic_memory_order order) { (void)order; return __sync_fetch_and_or(dst, src); } - static MA_INLINE ma_uint8 ma_atomic_fetch_xor_explicit_8(volatile ma_uint8* dst, ma_uint8 src, ma_atomic_memory_order order) + static C89ATOMIC_INLINE c89atomic_uint8 c89atomic_fetch_xor_explicit_8(volatile c89atomic_uint8* dst, c89atomic_uint8 src, c89atomic_memory_order order) { (void)order; return __sync_fetch_and_xor(dst, src); } - static MA_INLINE ma_uint16 ma_atomic_fetch_xor_explicit_16(volatile ma_uint16* dst, ma_uint16 src, ma_atomic_memory_order order) + static C89ATOMIC_INLINE c89atomic_uint16 c89atomic_fetch_xor_explicit_16(volatile c89atomic_uint16* dst, c89atomic_uint16 src, c89atomic_memory_order order) { (void)order; return __sync_fetch_and_xor(dst, src); } - static MA_INLINE ma_uint32 ma_atomic_fetch_xor_explicit_32(volatile ma_uint32* dst, ma_uint32 src, ma_atomic_memory_order order) + static C89ATOMIC_INLINE c89atomic_uint32 c89atomic_fetch_xor_explicit_32(volatile c89atomic_uint32* dst, c89atomic_uint32 src, c89atomic_memory_order order) { (void)order; return __sync_fetch_and_xor(dst, src); } - static MA_INLINE ma_uint64 ma_atomic_fetch_xor_explicit_64(volatile ma_uint64* dst, ma_uint64 src, ma_atomic_memory_order order) + static C89ATOMIC_INLINE c89atomic_uint64 c89atomic_fetch_xor_explicit_64(volatile c89atomic_uint64* dst, c89atomic_uint64 src, c89atomic_memory_order order) { (void)order; return __sync_fetch_and_xor(dst, src); } - static MA_INLINE ma_uint8 ma_atomic_fetch_and_explicit_8(volatile ma_uint8* dst, ma_uint8 src, ma_atomic_memory_order order) + static C89ATOMIC_INLINE c89atomic_uint8 c89atomic_fetch_and_explicit_8(volatile c89atomic_uint8* dst, c89atomic_uint8 src, c89atomic_memory_order order) { (void)order; return __sync_fetch_and_and(dst, src); } - static MA_INLINE ma_uint16 ma_atomic_fetch_and_explicit_16(volatile ma_uint16* dst, ma_uint16 src, ma_atomic_memory_order order) + static C89ATOMIC_INLINE c89atomic_uint16 c89atomic_fetch_and_explicit_16(volatile c89atomic_uint16* dst, c89atomic_uint16 src, c89atomic_memory_order order) { (void)order; return __sync_fetch_and_and(dst, src); } - static MA_INLINE ma_uint32 ma_atomic_fetch_and_explicit_32(volatile ma_uint32* dst, ma_uint32 src, ma_atomic_memory_order order) + static C89ATOMIC_INLINE c89atomic_uint32 c89atomic_fetch_and_explicit_32(volatile c89atomic_uint32* dst, c89atomic_uint32 src, c89atomic_memory_order order) { (void)order; return __sync_fetch_and_and(dst, src); } - static MA_INLINE ma_uint64 ma_atomic_fetch_and_explicit_64(volatile ma_uint64* dst, ma_uint64 src, ma_atomic_memory_order order) + static C89ATOMIC_INLINE c89atomic_uint64 c89atomic_fetch_and_explicit_64(volatile c89atomic_uint64* dst, c89atomic_uint64 src, c89atomic_memory_order order) { (void)order; return __sync_fetch_and_and(dst, src); } - #define ma_atomic_compare_and_swap_8( dst, expected, desired) __sync_val_compare_and_swap(dst, expected, desired) - #define ma_atomic_compare_and_swap_16(dst, expected, desired) __sync_val_compare_and_swap(dst, expected, desired) - #define ma_atomic_compare_and_swap_32(dst, expected, desired) __sync_val_compare_and_swap(dst, expected, desired) - #define ma_atomic_compare_and_swap_64(dst, expected, desired) __sync_val_compare_and_swap(dst, expected, desired) + #define c89atomic_compare_and_swap_8( dst, expected, desired) __sync_val_compare_and_swap(dst, expected, desired) + #define c89atomic_compare_and_swap_16(dst, expected, desired) __sync_val_compare_and_swap(dst, expected, desired) + #define c89atomic_compare_and_swap_32(dst, expected, desired) __sync_val_compare_and_swap(dst, expected, desired) + #define c89atomic_compare_and_swap_64(dst, expected, desired) __sync_val_compare_and_swap(dst, expected, desired) #else - #if defined(MA_X86) - #define ma_atomic_thread_fence(order) __asm__ __volatile__("lock; addl $0, (%%esp)" ::: "memory", "cc") - #elif defined(MA_X64) - #define ma_atomic_thread_fence(order) __asm__ __volatile__("lock; addq $0, (%%rsp)" ::: "memory", "cc") + #if defined(C89ATOMIC_X86) + #define c89atomic_thread_fence(order) __asm__ __volatile__("lock; addl $0, (%%esp)" ::: "memory", "cc") + #elif defined(C89ATOMIC_X64) + #define c89atomic_thread_fence(order) __asm__ __volatile__("lock; addq $0, (%%rsp)" ::: "memory", "cc") #else #error Unsupported architecture. Please submit a feature request. #endif - static MA_INLINE ma_uint8 ma_atomic_compare_and_swap_8(volatile ma_uint8* dst, ma_uint8 expected, ma_uint8 desired) + static C89ATOMIC_INLINE c89atomic_uint8 c89atomic_compare_and_swap_8(volatile c89atomic_uint8* dst, c89atomic_uint8 expected, c89atomic_uint8 desired) { - ma_uint8 result; - #if defined(MA_X86) || defined(MA_X64) + c89atomic_uint8 result; + #if defined(C89ATOMIC_X86) || defined(C89ATOMIC_X64) __asm__ __volatile__("lock; cmpxchg %3, %0" : "+m"(*dst), "=a"(result) : "a"(expected), "d"(desired) : "cc"); #else #error Unsupported architecture. Please submit a feature request. #endif return result; } - static MA_INLINE ma_uint16 ma_atomic_compare_and_swap_16(volatile ma_uint16* dst, ma_uint16 expected, ma_uint16 desired) + static C89ATOMIC_INLINE c89atomic_uint16 c89atomic_compare_and_swap_16(volatile c89atomic_uint16* dst, c89atomic_uint16 expected, c89atomic_uint16 desired) { - ma_uint16 result; - #if defined(MA_X86) || defined(MA_X64) + c89atomic_uint16 result; + #if defined(C89ATOMIC_X86) || defined(C89ATOMIC_X64) __asm__ __volatile__("lock; cmpxchg %3, %0" : "+m"(*dst), "=a"(result) : "a"(expected), "d"(desired) : "cc"); #else #error Unsupported architecture. Please submit a feature request. #endif return result; } - static MA_INLINE ma_uint32 ma_atomic_compare_and_swap_32(volatile ma_uint32* dst, ma_uint32 expected, ma_uint32 desired) + static C89ATOMIC_INLINE c89atomic_uint32 c89atomic_compare_and_swap_32(volatile c89atomic_uint32* dst, c89atomic_uint32 expected, c89atomic_uint32 desired) { - ma_uint32 result; - #if defined(MA_X86) || defined(MA_X64) + c89atomic_uint32 result; + #if defined(C89ATOMIC_X86) || defined(C89ATOMIC_X64) __asm__ __volatile__("lock; cmpxchg %3, %0" : "+m"(*dst), "=a"(result) : "a"(expected), "d"(desired) : "cc"); #else #error Unsupported architecture. Please submit a feature request. #endif return result; } - static MA_INLINE ma_uint64 ma_atomic_compare_and_swap_64(volatile ma_uint64* dst, ma_uint64 expected, ma_uint64 desired) + static C89ATOMIC_INLINE c89atomic_uint64 c89atomic_compare_and_swap_64(volatile c89atomic_uint64* dst, c89atomic_uint64 expected, c89atomic_uint64 desired) { - volatile ma_uint64 result; - #if defined(MA_X86) - ma_uint32 resultEAX; - ma_uint32 resultEDX; + volatile c89atomic_uint64 result; + #if defined(C89ATOMIC_X86) + c89atomic_uint32 resultEAX; + c89atomic_uint32 resultEDX; __asm__ __volatile__("push %%ebx; xchg %5, %%ebx; lock; cmpxchg8b %0; pop %%ebx" : "+m"(*dst), "=a"(resultEAX), "=d"(resultEDX) : "a"(expected & 0xFFFFFFFF), "d"(expected >> 32), "r"(desired & 0xFFFFFFFF), "c"(desired >> 32) : "cc"); - result = ((ma_uint64)resultEDX << 32) | resultEAX; - #elif defined(MA_X64) + result = ((c89atomic_uint64)resultEDX << 32) | resultEAX; + #elif defined(C89ATOMIC_X64) __asm__ __volatile__("lock; cmpxchg %3, %0" : "+m"(*dst), "=a"(result) : "a"(expected), "d"(desired) : "cc"); #else #error Unsupported architecture. Please submit a feature request. #endif return result; } - static MA_INLINE ma_uint8 ma_atomic_exchange_explicit_8(volatile ma_uint8* dst, ma_uint8 src, ma_atomic_memory_order order) + static C89ATOMIC_INLINE c89atomic_uint8 c89atomic_exchange_explicit_8(volatile c89atomic_uint8* dst, c89atomic_uint8 src, c89atomic_memory_order order) { - ma_uint8 result = 0; + c89atomic_uint8 result = 0; (void)order; - #if defined(MA_X86) || defined(MA_X64) + #if defined(C89ATOMIC_X86) || defined(C89ATOMIC_X64) __asm__ __volatile__("lock; xchg %1, %0" : "+m"(*dst), "=a"(result) : "a"(src)); #else #error Unsupported architecture. Please submit a feature request. #endif return result; } - static MA_INLINE ma_uint16 ma_atomic_exchange_explicit_16(volatile ma_uint16* dst, ma_uint16 src, ma_atomic_memory_order order) + static C89ATOMIC_INLINE c89atomic_uint16 c89atomic_exchange_explicit_16(volatile c89atomic_uint16* dst, c89atomic_uint16 src, c89atomic_memory_order order) { - ma_uint16 result = 0; + c89atomic_uint16 result = 0; (void)order; - #if defined(MA_X86) || defined(MA_X64) + #if defined(C89ATOMIC_X86) || defined(C89ATOMIC_X64) __asm__ __volatile__("lock; xchg %1, %0" : "+m"(*dst), "=a"(result) : "a"(src)); #else #error Unsupported architecture. Please submit a feature request. #endif return result; } - static MA_INLINE ma_uint32 ma_atomic_exchange_explicit_32(volatile ma_uint32* dst, ma_uint32 src, ma_atomic_memory_order order) + static C89ATOMIC_INLINE c89atomic_uint32 c89atomic_exchange_explicit_32(volatile c89atomic_uint32* dst, c89atomic_uint32 src, c89atomic_memory_order order) { - ma_uint32 result; + c89atomic_uint32 result; (void)order; - #if defined(MA_X86) || defined(MA_X64) + #if defined(C89ATOMIC_X86) || defined(C89ATOMIC_X64) __asm__ __volatile__("lock; xchg %1, %0" : "+m"(*dst), "=a"(result) : "a"(src)); #else #error Unsupported architecture. Please submit a feature request. #endif return result; } - static MA_INLINE ma_uint64 ma_atomic_exchange_explicit_64(volatile ma_uint64* dst, ma_uint64 src, ma_atomic_memory_order order) + static C89ATOMIC_INLINE c89atomic_uint64 c89atomic_exchange_explicit_64(volatile c89atomic_uint64* dst, c89atomic_uint64 src, c89atomic_memory_order order) { - ma_uint64 result; + c89atomic_uint64 result; (void)order; - #if defined(MA_X86) + #if defined(C89ATOMIC_X86) do { result = *dst; - } while (ma_atomic_compare_and_swap_64(dst, result, src) != result); - #elif defined(MA_X64) + } while (c89atomic_compare_and_swap_64(dst, result, src) != result); + #elif defined(C89ATOMIC_X64) __asm__ __volatile__("lock; xchg %1, %0" : "+m"(*dst), "=a"(result) : "a"(src)); #else #error Unsupported architecture. Please submit a feature request. #endif return result; } - static MA_INLINE ma_uint8 ma_atomic_fetch_add_explicit_8(volatile ma_uint8* dst, ma_uint8 src, ma_atomic_memory_order order) + static C89ATOMIC_INLINE c89atomic_uint8 c89atomic_fetch_add_explicit_8(volatile c89atomic_uint8* dst, c89atomic_uint8 src, c89atomic_memory_order order) { - ma_uint8 result; + c89atomic_uint8 result; (void)order; - #if defined(MA_X86) || defined(MA_X64) + #if defined(C89ATOMIC_X86) || defined(C89ATOMIC_X64) __asm__ __volatile__("lock; xadd %1, %0" : "+m"(*dst), "=a"(result) : "a"(src) : "cc"); #else #error Unsupported architecture. Please submit a feature request. #endif return result; } - static MA_INLINE ma_uint16 ma_atomic_fetch_add_explicit_16(volatile ma_uint16* dst, ma_uint16 src, ma_atomic_memory_order order) + static C89ATOMIC_INLINE c89atomic_uint16 c89atomic_fetch_add_explicit_16(volatile c89atomic_uint16* dst, c89atomic_uint16 src, c89atomic_memory_order order) { - ma_uint16 result; + c89atomic_uint16 result; (void)order; - #if defined(MA_X86) || defined(MA_X64) + #if defined(C89ATOMIC_X86) || defined(C89ATOMIC_X64) __asm__ __volatile__("lock; xadd %1, %0" : "+m"(*dst), "=a"(result) : "a"(src) : "cc"); #else #error Unsupported architecture. Please submit a feature request. #endif return result; } - static MA_INLINE ma_uint32 ma_atomic_fetch_add_explicit_32(volatile ma_uint32* dst, ma_uint32 src, ma_atomic_memory_order order) + static C89ATOMIC_INLINE c89atomic_uint32 c89atomic_fetch_add_explicit_32(volatile c89atomic_uint32* dst, c89atomic_uint32 src, c89atomic_memory_order order) { - ma_uint32 result; + c89atomic_uint32 result; (void)order; - #if defined(MA_X86) || defined(MA_X64) + #if defined(C89ATOMIC_X86) || defined(C89ATOMIC_X64) __asm__ __volatile__("lock; xadd %1, %0" : "+m"(*dst), "=a"(result) : "a"(src) : "cc"); #else #error Unsupported architecture. Please submit a feature request. #endif return result; } - static MA_INLINE ma_uint64 ma_atomic_fetch_add_explicit_64(volatile ma_uint64* dst, ma_uint64 src, ma_atomic_memory_order order) + static C89ATOMIC_INLINE c89atomic_uint64 c89atomic_fetch_add_explicit_64(volatile c89atomic_uint64* dst, c89atomic_uint64 src, c89atomic_memory_order order) { - #if defined(MA_X86) - ma_uint64 oldValue; - ma_uint64 newValue; + #if defined(C89ATOMIC_X86) + c89atomic_uint64 oldValue; + c89atomic_uint64 newValue; (void)order; do { oldValue = *dst; newValue = oldValue + src; - } while (ma_atomic_compare_and_swap_64(dst, oldValue, newValue) != oldValue); + } while (c89atomic_compare_and_swap_64(dst, oldValue, newValue) != oldValue); return oldValue; - #elif defined(MA_X64) - ma_uint64 result; + #elif defined(C89ATOMIC_X64) + c89atomic_uint64 result; (void)order; __asm__ __volatile__("lock; xadd %1, %0" : "+m"(*dst), "=a"(result) : "a"(src) : "cc"); return result; #endif } - static MA_INLINE ma_uint8 ma_atomic_fetch_sub_explicit_8(volatile ma_uint8* dst, ma_uint8 src, ma_atomic_memory_order order) + static C89ATOMIC_INLINE c89atomic_uint8 c89atomic_fetch_sub_explicit_8(volatile c89atomic_uint8* dst, c89atomic_uint8 src, c89atomic_memory_order order) { - ma_uint8 oldValue; - ma_uint8 newValue; + c89atomic_uint8 oldValue; + c89atomic_uint8 newValue; do { oldValue = *dst; - newValue = (ma_uint8)(oldValue - src); - } while (ma_atomic_compare_and_swap_8(dst, oldValue, newValue) != oldValue); + newValue = (c89atomic_uint8)(oldValue - src); + } while (c89atomic_compare_and_swap_8(dst, oldValue, newValue) != oldValue); (void)order; return oldValue; } - static MA_INLINE ma_uint16 ma_atomic_fetch_sub_explicit_16(volatile ma_uint16* dst, ma_uint16 src, ma_atomic_memory_order order) + static C89ATOMIC_INLINE c89atomic_uint16 c89atomic_fetch_sub_explicit_16(volatile c89atomic_uint16* dst, c89atomic_uint16 src, c89atomic_memory_order order) { - ma_uint16 oldValue; - ma_uint16 newValue; + c89atomic_uint16 oldValue; + c89atomic_uint16 newValue; do { oldValue = *dst; - newValue = (ma_uint16)(oldValue - src); - } while (ma_atomic_compare_and_swap_16(dst, oldValue, newValue) != oldValue); + newValue = (c89atomic_uint16)(oldValue - src); + } while (c89atomic_compare_and_swap_16(dst, oldValue, newValue) != oldValue); (void)order; return oldValue; } - static MA_INLINE ma_uint32 ma_atomic_fetch_sub_explicit_32(volatile ma_uint32* dst, ma_uint32 src, ma_atomic_memory_order order) + static C89ATOMIC_INLINE c89atomic_uint32 c89atomic_fetch_sub_explicit_32(volatile c89atomic_uint32* dst, c89atomic_uint32 src, c89atomic_memory_order order) { - ma_uint32 oldValue; - ma_uint32 newValue; + c89atomic_uint32 oldValue; + c89atomic_uint32 newValue; do { oldValue = *dst; newValue = oldValue - src; - } while (ma_atomic_compare_and_swap_32(dst, oldValue, newValue) != oldValue); + } while (c89atomic_compare_and_swap_32(dst, oldValue, newValue) != oldValue); (void)order; return oldValue; } - static MA_INLINE ma_uint64 ma_atomic_fetch_sub_explicit_64(volatile ma_uint64* dst, ma_uint64 src, ma_atomic_memory_order order) + static C89ATOMIC_INLINE c89atomic_uint64 c89atomic_fetch_sub_explicit_64(volatile c89atomic_uint64* dst, c89atomic_uint64 src, c89atomic_memory_order order) { - ma_uint64 oldValue; - ma_uint64 newValue; + c89atomic_uint64 oldValue; + c89atomic_uint64 newValue; do { oldValue = *dst; newValue = oldValue - src; - } while (ma_atomic_compare_and_swap_64(dst, oldValue, newValue) != oldValue); + } while (c89atomic_compare_and_swap_64(dst, oldValue, newValue) != oldValue); (void)order; return oldValue; } - static MA_INLINE ma_uint8 ma_atomic_fetch_and_explicit_8(volatile ma_uint8* dst, ma_uint8 src, ma_atomic_memory_order order) + static C89ATOMIC_INLINE c89atomic_uint8 c89atomic_fetch_and_explicit_8(volatile c89atomic_uint8* dst, c89atomic_uint8 src, c89atomic_memory_order order) { - ma_uint8 oldValue; - ma_uint8 newValue; + c89atomic_uint8 oldValue; + c89atomic_uint8 newValue; do { oldValue = *dst; - newValue = (ma_uint8)(oldValue & src); - } while (ma_atomic_compare_and_swap_8(dst, oldValue, newValue) != oldValue); + newValue = (c89atomic_uint8)(oldValue & src); + } while (c89atomic_compare_and_swap_8(dst, oldValue, newValue) != oldValue); (void)order; return oldValue; } - static MA_INLINE ma_uint16 ma_atomic_fetch_and_explicit_16(volatile ma_uint16* dst, ma_uint16 src, ma_atomic_memory_order order) + static C89ATOMIC_INLINE c89atomic_uint16 c89atomic_fetch_and_explicit_16(volatile c89atomic_uint16* dst, c89atomic_uint16 src, c89atomic_memory_order order) { - ma_uint16 oldValue; - ma_uint16 newValue; + c89atomic_uint16 oldValue; + c89atomic_uint16 newValue; do { oldValue = *dst; - newValue = (ma_uint16)(oldValue & src); - } while (ma_atomic_compare_and_swap_16(dst, oldValue, newValue) != oldValue); + newValue = (c89atomic_uint16)(oldValue & src); + } while (c89atomic_compare_and_swap_16(dst, oldValue, newValue) != oldValue); (void)order; return oldValue; } - static MA_INLINE ma_uint32 ma_atomic_fetch_and_explicit_32(volatile ma_uint32* dst, ma_uint32 src, ma_atomic_memory_order order) + static C89ATOMIC_INLINE c89atomic_uint32 c89atomic_fetch_and_explicit_32(volatile c89atomic_uint32* dst, c89atomic_uint32 src, c89atomic_memory_order order) { - ma_uint32 oldValue; - ma_uint32 newValue; + c89atomic_uint32 oldValue; + c89atomic_uint32 newValue; do { oldValue = *dst; newValue = oldValue & src; - } while (ma_atomic_compare_and_swap_32(dst, oldValue, newValue) != oldValue); + } while (c89atomic_compare_and_swap_32(dst, oldValue, newValue) != oldValue); (void)order; return oldValue; } - static MA_INLINE ma_uint64 ma_atomic_fetch_and_explicit_64(volatile ma_uint64* dst, ma_uint64 src, ma_atomic_memory_order order) + static C89ATOMIC_INLINE c89atomic_uint64 c89atomic_fetch_and_explicit_64(volatile c89atomic_uint64* dst, c89atomic_uint64 src, c89atomic_memory_order order) { - ma_uint64 oldValue; - ma_uint64 newValue; + c89atomic_uint64 oldValue; + c89atomic_uint64 newValue; do { oldValue = *dst; newValue = oldValue & src; - } while (ma_atomic_compare_and_swap_64(dst, oldValue, newValue) != oldValue); + } while (c89atomic_compare_and_swap_64(dst, oldValue, newValue) != oldValue); (void)order; return oldValue; } - static MA_INLINE ma_uint8 ma_atomic_fetch_xor_explicit_8(volatile ma_uint8* dst, ma_uint8 src, ma_atomic_memory_order order) + static C89ATOMIC_INLINE c89atomic_uint8 c89atomic_fetch_xor_explicit_8(volatile c89atomic_uint8* dst, c89atomic_uint8 src, c89atomic_memory_order order) { - ma_uint8 oldValue; - ma_uint8 newValue; + c89atomic_uint8 oldValue; + c89atomic_uint8 newValue; do { oldValue = *dst; - newValue = (ma_uint8)(oldValue ^ src); - } while (ma_atomic_compare_and_swap_8(dst, oldValue, newValue) != oldValue); + newValue = (c89atomic_uint8)(oldValue ^ src); + } while (c89atomic_compare_and_swap_8(dst, oldValue, newValue) != oldValue); (void)order; return oldValue; } - static MA_INLINE ma_uint16 ma_atomic_fetch_xor_explicit_16(volatile ma_uint16* dst, ma_uint16 src, ma_atomic_memory_order order) + static C89ATOMIC_INLINE c89atomic_uint16 c89atomic_fetch_xor_explicit_16(volatile c89atomic_uint16* dst, c89atomic_uint16 src, c89atomic_memory_order order) { - ma_uint16 oldValue; - ma_uint16 newValue; + c89atomic_uint16 oldValue; + c89atomic_uint16 newValue; do { oldValue = *dst; - newValue = (ma_uint16)(oldValue ^ src); - } while (ma_atomic_compare_and_swap_16(dst, oldValue, newValue) != oldValue); + newValue = (c89atomic_uint16)(oldValue ^ src); + } while (c89atomic_compare_and_swap_16(dst, oldValue, newValue) != oldValue); (void)order; return oldValue; } - static MA_INLINE ma_uint32 ma_atomic_fetch_xor_explicit_32(volatile ma_uint32* dst, ma_uint32 src, ma_atomic_memory_order order) + static C89ATOMIC_INLINE c89atomic_uint32 c89atomic_fetch_xor_explicit_32(volatile c89atomic_uint32* dst, c89atomic_uint32 src, c89atomic_memory_order order) { - ma_uint32 oldValue; - ma_uint32 newValue; + c89atomic_uint32 oldValue; + c89atomic_uint32 newValue; do { oldValue = *dst; newValue = oldValue ^ src; - } while (ma_atomic_compare_and_swap_32(dst, oldValue, newValue) != oldValue); + } while (c89atomic_compare_and_swap_32(dst, oldValue, newValue) != oldValue); (void)order; return oldValue; } - static MA_INLINE ma_uint64 ma_atomic_fetch_xor_explicit_64(volatile ma_uint64* dst, ma_uint64 src, ma_atomic_memory_order order) + static C89ATOMIC_INLINE c89atomic_uint64 c89atomic_fetch_xor_explicit_64(volatile c89atomic_uint64* dst, c89atomic_uint64 src, c89atomic_memory_order order) { - ma_uint64 oldValue; - ma_uint64 newValue; + c89atomic_uint64 oldValue; + c89atomic_uint64 newValue; do { oldValue = *dst; newValue = oldValue ^ src; - } while (ma_atomic_compare_and_swap_64(dst, oldValue, newValue) != oldValue); + } while (c89atomic_compare_and_swap_64(dst, oldValue, newValue) != oldValue); (void)order; return oldValue; } - static MA_INLINE ma_uint8 ma_atomic_fetch_or_explicit_8(volatile ma_uint8* dst, ma_uint8 src, ma_atomic_memory_order order) + static C89ATOMIC_INLINE c89atomic_uint8 c89atomic_fetch_or_explicit_8(volatile c89atomic_uint8* dst, c89atomic_uint8 src, c89atomic_memory_order order) { - ma_uint8 oldValue; - ma_uint8 newValue; + c89atomic_uint8 oldValue; + c89atomic_uint8 newValue; do { oldValue = *dst; - newValue = (ma_uint8)(oldValue | src); - } while (ma_atomic_compare_and_swap_8(dst, oldValue, newValue) != oldValue); + newValue = (c89atomic_uint8)(oldValue | src); + } while (c89atomic_compare_and_swap_8(dst, oldValue, newValue) != oldValue); (void)order; return oldValue; } - static MA_INLINE ma_uint16 ma_atomic_fetch_or_explicit_16(volatile ma_uint16* dst, ma_uint16 src, ma_atomic_memory_order order) + static C89ATOMIC_INLINE c89atomic_uint16 c89atomic_fetch_or_explicit_16(volatile c89atomic_uint16* dst, c89atomic_uint16 src, c89atomic_memory_order order) { - ma_uint16 oldValue; - ma_uint16 newValue; + c89atomic_uint16 oldValue; + c89atomic_uint16 newValue; do { oldValue = *dst; - newValue = (ma_uint16)(oldValue | src); - } while (ma_atomic_compare_and_swap_16(dst, oldValue, newValue) != oldValue); + newValue = (c89atomic_uint16)(oldValue | src); + } while (c89atomic_compare_and_swap_16(dst, oldValue, newValue) != oldValue); (void)order; return oldValue; } - static MA_INLINE ma_uint32 ma_atomic_fetch_or_explicit_32(volatile ma_uint32* dst, ma_uint32 src, ma_atomic_memory_order order) + static C89ATOMIC_INLINE c89atomic_uint32 c89atomic_fetch_or_explicit_32(volatile c89atomic_uint32* dst, c89atomic_uint32 src, c89atomic_memory_order order) { - ma_uint32 oldValue; - ma_uint32 newValue; + c89atomic_uint32 oldValue; + c89atomic_uint32 newValue; do { oldValue = *dst; newValue = oldValue | src; - } while (ma_atomic_compare_and_swap_32(dst, oldValue, newValue) != oldValue); + } while (c89atomic_compare_and_swap_32(dst, oldValue, newValue) != oldValue); (void)order; return oldValue; } - static MA_INLINE ma_uint64 ma_atomic_fetch_or_explicit_64(volatile ma_uint64* dst, ma_uint64 src, ma_atomic_memory_order order) + static C89ATOMIC_INLINE c89atomic_uint64 c89atomic_fetch_or_explicit_64(volatile c89atomic_uint64* dst, c89atomic_uint64 src, c89atomic_memory_order order) { - ma_uint64 oldValue; - ma_uint64 newValue; + c89atomic_uint64 oldValue; + c89atomic_uint64 newValue; do { oldValue = *dst; newValue = oldValue | src; - } while (ma_atomic_compare_and_swap_64(dst, oldValue, newValue) != oldValue); + } while (c89atomic_compare_and_swap_64(dst, oldValue, newValue) != oldValue); (void)order; return oldValue; } #endif - #define ma_atomic_signal_fence(order) ma_atomic_thread_fence(order) - static MA_INLINE ma_uint8 ma_atomic_load_explicit_8(volatile const ma_uint8* ptr, ma_atomic_memory_order order) + #define c89atomic_signal_fence(order) c89atomic_thread_fence(order) + static C89ATOMIC_INLINE c89atomic_uint8 c89atomic_load_explicit_8(volatile const c89atomic_uint8* ptr, c89atomic_memory_order order) { (void)order; - return ma_atomic_compare_and_swap_8((ma_uint8*)ptr, 0, 0); + return c89atomic_compare_and_swap_8((c89atomic_uint8*)ptr, 0, 0); } - static MA_INLINE ma_uint16 ma_atomic_load_explicit_16(volatile const ma_uint16* ptr, ma_atomic_memory_order order) + static C89ATOMIC_INLINE c89atomic_uint16 c89atomic_load_explicit_16(volatile const c89atomic_uint16* ptr, c89atomic_memory_order order) { (void)order; - return ma_atomic_compare_and_swap_16((ma_uint16*)ptr, 0, 0); + return c89atomic_compare_and_swap_16((c89atomic_uint16*)ptr, 0, 0); } - static MA_INLINE ma_uint32 ma_atomic_load_explicit_32(volatile const ma_uint32* ptr, ma_atomic_memory_order order) + static C89ATOMIC_INLINE c89atomic_uint32 c89atomic_load_explicit_32(volatile const c89atomic_uint32* ptr, c89atomic_memory_order order) { (void)order; - return ma_atomic_compare_and_swap_32((ma_uint32*)ptr, 0, 0); + return c89atomic_compare_and_swap_32((c89atomic_uint32*)ptr, 0, 0); } - static MA_INLINE ma_uint64 ma_atomic_load_explicit_64(volatile const ma_uint64* ptr, ma_atomic_memory_order order) + static C89ATOMIC_INLINE c89atomic_uint64 c89atomic_load_explicit_64(volatile const c89atomic_uint64* ptr, c89atomic_memory_order order) { (void)order; - return ma_atomic_compare_and_swap_64((ma_uint64*)ptr, 0, 0); - } - #define ma_atomic_store_explicit_8( dst, src, order) (void)ma_atomic_exchange_explicit_8 (dst, src, order) - #define ma_atomic_store_explicit_16(dst, src, order) (void)ma_atomic_exchange_explicit_16(dst, src, order) - #define ma_atomic_store_explicit_32(dst, src, order) (void)ma_atomic_exchange_explicit_32(dst, src, order) - #define ma_atomic_store_explicit_64(dst, src, order) (void)ma_atomic_exchange_explicit_64(dst, src, order) - #define ma_atomic_test_and_set_explicit_8( dst, order) ma_atomic_exchange_explicit_8 (dst, 1, order) - #define ma_atomic_test_and_set_explicit_16(dst, order) ma_atomic_exchange_explicit_16(dst, 1, order) - #define ma_atomic_test_and_set_explicit_32(dst, order) ma_atomic_exchange_explicit_32(dst, 1, order) - #define ma_atomic_test_and_set_explicit_64(dst, order) ma_atomic_exchange_explicit_64(dst, 1, order) - #define ma_atomic_clear_explicit_8( dst, order) ma_atomic_store_explicit_8 (dst, 0, order) - #define ma_atomic_clear_explicit_16(dst, order) ma_atomic_store_explicit_16(dst, 0, order) - #define ma_atomic_clear_explicit_32(dst, order) ma_atomic_store_explicit_32(dst, 0, order) - #define ma_atomic_clear_explicit_64(dst, order) ma_atomic_store_explicit_64(dst, 0, order) - typedef ma_uint8 ma_atomic_flag; - #define ma_atomic_flag_test_and_set_explicit(ptr, order) (ma_bool32)ma_atomic_test_and_set_explicit_8(ptr, order) - #define ma_atomic_flag_clear_explicit(ptr, order) ma_atomic_clear_explicit_8(ptr, order) - #define ma_atomic_flag_load_explicit(ptr, order) ma_atomic_load_explicit_8(ptr, order) -#endif -#if !defined(MA_ATOMIC_HAS_NATIVE_COMPARE_EXCHANGE) - #if defined(MA_ATOMIC_HAS_8) - static MA_INLINE ma_bool32 ma_atomic_compare_exchange_strong_explicit_8(volatile ma_uint8* dst, ma_uint8* expected, ma_uint8 desired, ma_atomic_memory_order successOrder, ma_atomic_memory_order failureOrder) + return c89atomic_compare_and_swap_64((c89atomic_uint64*)ptr, 0, 0); + } + #define c89atomic_store_explicit_8( dst, src, order) (void)c89atomic_exchange_explicit_8 (dst, src, order) + #define c89atomic_store_explicit_16(dst, src, order) (void)c89atomic_exchange_explicit_16(dst, src, order) + #define c89atomic_store_explicit_32(dst, src, order) (void)c89atomic_exchange_explicit_32(dst, src, order) + #define c89atomic_store_explicit_64(dst, src, order) (void)c89atomic_exchange_explicit_64(dst, src, order) + #define c89atomic_test_and_set_explicit_8( dst, order) c89atomic_exchange_explicit_8 (dst, 1, order) + #define c89atomic_test_and_set_explicit_16(dst, order) c89atomic_exchange_explicit_16(dst, 1, order) + #define c89atomic_test_and_set_explicit_32(dst, order) c89atomic_exchange_explicit_32(dst, 1, order) + #define c89atomic_test_and_set_explicit_64(dst, order) c89atomic_exchange_explicit_64(dst, 1, order) + #define c89atomic_clear_explicit_8( dst, order) c89atomic_store_explicit_8 (dst, 0, order) + #define c89atomic_clear_explicit_16(dst, order) c89atomic_store_explicit_16(dst, 0, order) + #define c89atomic_clear_explicit_32(dst, order) c89atomic_store_explicit_32(dst, 0, order) + #define c89atomic_clear_explicit_64(dst, order) c89atomic_store_explicit_64(dst, 0, order) + typedef c89atomic_uint8 c89atomic_flag; + #define c89atomic_flag_test_and_set_explicit(ptr, order) (c89atomic_bool)c89atomic_test_and_set_explicit_8(ptr, order) + #define c89atomic_flag_clear_explicit(ptr, order) c89atomic_clear_explicit_8(ptr, order) + #define c89atoimc_flag_load_explicit(ptr, order) c89atomic_load_explicit_8(ptr, order) +#endif +#if !defined(C89ATOMIC_HAS_NATIVE_COMPARE_EXCHANGE) + #if defined(C89ATOMIC_HAS_8) + static C89ATOMIC_INLINE c89atomic_bool c89atomic_compare_exchange_strong_explicit_8(volatile c89atomic_uint8* dst, c89atomic_uint8* expected, c89atomic_uint8 desired, c89atomic_memory_order successOrder, c89atomic_memory_order failureOrder) { - ma_uint8 expectedValue; - ma_uint8 result; + c89atomic_uint8 expectedValue; + c89atomic_uint8 result; (void)successOrder; (void)failureOrder; - expectedValue = ma_atomic_load_explicit_8(expected, ma_atomic_memory_order_seq_cst); - result = ma_atomic_compare_and_swap_8(dst, expectedValue, desired); + expectedValue = c89atomic_load_explicit_8(expected, c89atomic_memory_order_seq_cst); + result = c89atomic_compare_and_swap_8(dst, expectedValue, desired); if (result == expectedValue) { return 1; } else { - ma_atomic_store_explicit_8(expected, result, failureOrder); + c89atomic_store_explicit_8(expected, result, failureOrder); return 0; } } #endif - #if defined(MA_ATOMIC_HAS_16) - static MA_INLINE ma_bool32 ma_atomic_compare_exchange_strong_explicit_16(volatile ma_uint16* dst, ma_uint16* expected, ma_uint16 desired, ma_atomic_memory_order successOrder, ma_atomic_memory_order failureOrder) + #if defined(C89ATOMIC_HAS_16) + static C89ATOMIC_INLINE c89atomic_bool c89atomic_compare_exchange_strong_explicit_16(volatile c89atomic_uint16* dst, c89atomic_uint16* expected, c89atomic_uint16 desired, c89atomic_memory_order successOrder, c89atomic_memory_order failureOrder) { - ma_uint16 expectedValue; - ma_uint16 result; + c89atomic_uint16 expectedValue; + c89atomic_uint16 result; (void)successOrder; (void)failureOrder; - expectedValue = ma_atomic_load_explicit_16(expected, ma_atomic_memory_order_seq_cst); - result = ma_atomic_compare_and_swap_16(dst, expectedValue, desired); + expectedValue = c89atomic_load_explicit_16(expected, c89atomic_memory_order_seq_cst); + result = c89atomic_compare_and_swap_16(dst, expectedValue, desired); if (result == expectedValue) { return 1; } else { - ma_atomic_store_explicit_16(expected, result, failureOrder); + c89atomic_store_explicit_16(expected, result, failureOrder); return 0; } } #endif - #if defined(MA_ATOMIC_HAS_32) - static MA_INLINE ma_bool32 ma_atomic_compare_exchange_strong_explicit_32(volatile ma_uint32* dst, ma_uint32* expected, ma_uint32 desired, ma_atomic_memory_order successOrder, ma_atomic_memory_order failureOrder) + #if defined(C89ATOMIC_HAS_32) + static C89ATOMIC_INLINE c89atomic_bool c89atomic_compare_exchange_strong_explicit_32(volatile c89atomic_uint32* dst, c89atomic_uint32* expected, c89atomic_uint32 desired, c89atomic_memory_order successOrder, c89atomic_memory_order failureOrder) { - ma_uint32 expectedValue; - ma_uint32 result; + c89atomic_uint32 expectedValue; + c89atomic_uint32 result; (void)successOrder; (void)failureOrder; - expectedValue = ma_atomic_load_explicit_32(expected, ma_atomic_memory_order_seq_cst); - result = ma_atomic_compare_and_swap_32(dst, expectedValue, desired); + expectedValue = c89atomic_load_explicit_32(expected, c89atomic_memory_order_seq_cst); + result = c89atomic_compare_and_swap_32(dst, expectedValue, desired); if (result == expectedValue) { return 1; } else { - ma_atomic_store_explicit_32(expected, result, failureOrder); + c89atomic_store_explicit_32(expected, result, failureOrder); return 0; } } #endif - #if defined(MA_ATOMIC_HAS_64) - static MA_INLINE ma_bool32 ma_atomic_compare_exchange_strong_explicit_64(volatile ma_uint64* dst, volatile ma_uint64* expected, ma_uint64 desired, ma_atomic_memory_order successOrder, ma_atomic_memory_order failureOrder) + #if defined(C89ATOMIC_HAS_64) + static C89ATOMIC_INLINE c89atomic_bool c89atomic_compare_exchange_strong_explicit_64(volatile c89atomic_uint64* dst, volatile c89atomic_uint64* expected, c89atomic_uint64 desired, c89atomic_memory_order successOrder, c89atomic_memory_order failureOrder) { - ma_uint64 expectedValue; - ma_uint64 result; + c89atomic_uint64 expectedValue; + c89atomic_uint64 result; (void)successOrder; (void)failureOrder; - expectedValue = ma_atomic_load_explicit_64(expected, ma_atomic_memory_order_seq_cst); - result = ma_atomic_compare_and_swap_64(dst, expectedValue, desired); + expectedValue = c89atomic_load_explicit_64(expected, c89atomic_memory_order_seq_cst); + result = c89atomic_compare_and_swap_64(dst, expectedValue, desired); if (result == expectedValue) { return 1; } else { - ma_atomic_store_explicit_64(expected, result, failureOrder); + c89atomic_store_explicit_64(expected, result, failureOrder); return 0; } } #endif - #define ma_atomic_compare_exchange_weak_explicit_8( dst, expected, desired, successOrder, failureOrder) ma_atomic_compare_exchange_strong_explicit_8 (dst, expected, desired, successOrder, failureOrder) - #define ma_atomic_compare_exchange_weak_explicit_16(dst, expected, desired, successOrder, failureOrder) ma_atomic_compare_exchange_strong_explicit_16(dst, expected, desired, successOrder, failureOrder) - #define ma_atomic_compare_exchange_weak_explicit_32(dst, expected, desired, successOrder, failureOrder) ma_atomic_compare_exchange_strong_explicit_32(dst, expected, desired, successOrder, failureOrder) - #define ma_atomic_compare_exchange_weak_explicit_64(dst, expected, desired, successOrder, failureOrder) ma_atomic_compare_exchange_strong_explicit_64(dst, expected, desired, successOrder, failureOrder) + #define c89atomic_compare_exchange_weak_explicit_8( dst, expected, desired, successOrder, failureOrder) c89atomic_compare_exchange_strong_explicit_8 (dst, expected, desired, successOrder, failureOrder) + #define c89atomic_compare_exchange_weak_explicit_16(dst, expected, desired, successOrder, failureOrder) c89atomic_compare_exchange_strong_explicit_16(dst, expected, desired, successOrder, failureOrder) + #define c89atomic_compare_exchange_weak_explicit_32(dst, expected, desired, successOrder, failureOrder) c89atomic_compare_exchange_strong_explicit_32(dst, expected, desired, successOrder, failureOrder) + #define c89atomic_compare_exchange_weak_explicit_64(dst, expected, desired, successOrder, failureOrder) c89atomic_compare_exchange_strong_explicit_64(dst, expected, desired, successOrder, failureOrder) #endif -#if !defined(MA_ATOMIC_HAS_NATIVE_IS_LOCK_FREE) - static MA_INLINE ma_bool32 ma_atomic_is_lock_free_8(volatile void* ptr) +#if !defined(C89ATOMIC_HAS_NATIVE_IS_LOCK_FREE) + static C89ATOMIC_INLINE c89atomic_bool c89atomic_is_lock_free_8(volatile void* ptr) { (void)ptr; return 1; } - static MA_INLINE ma_bool32 ma_atomic_is_lock_free_16(volatile void* ptr) + static C89ATOMIC_INLINE c89atomic_bool c89atomic_is_lock_free_16(volatile void* ptr) { (void)ptr; return 1; } - static MA_INLINE ma_bool32 ma_atomic_is_lock_free_32(volatile void* ptr) + static C89ATOMIC_INLINE c89atomic_bool c89atomic_is_lock_free_32(volatile void* ptr) { (void)ptr; return 1; } - static MA_INLINE ma_bool32 ma_atomic_is_lock_free_64(volatile void* ptr) + static C89ATOMIC_INLINE c89atomic_bool c89atomic_is_lock_free_64(volatile void* ptr) { (void)ptr; - #if defined(MA_64BIT) + #if defined(C89ATOMIC_64BIT) return 1; #else - #if defined(MA_X86) || defined(MA_X64) + #if defined(C89ATOMIC_X86) || defined(C89ATOMIC_X64) return 1; #else return 0; @@ -15544,432 +15451,432 @@ typedef int ma_atomic_memory_order; #endif } #endif -#if defined(MA_64BIT) - static MA_INLINE ma_bool32 ma_atomic_is_lock_free_ptr(volatile void** ptr) +#if defined(C89ATOMIC_64BIT) + static C89ATOMIC_INLINE c89atomic_bool c89atomic_is_lock_free_ptr(volatile void** ptr) { - return ma_atomic_is_lock_free_64((volatile ma_uint64*)ptr); + return c89atomic_is_lock_free_64((volatile c89atomic_uint64*)ptr); } - static MA_INLINE void* ma_atomic_load_explicit_ptr(volatile void** ptr, ma_atomic_memory_order order) + static C89ATOMIC_INLINE void* c89atomic_load_explicit_ptr(volatile void** ptr, c89atomic_memory_order order) { - return (void*)ma_atomic_load_explicit_64((volatile ma_uint64*)ptr, order); + return (void*)c89atomic_load_explicit_64((volatile c89atomic_uint64*)ptr, order); } - static MA_INLINE void ma_atomic_store_explicit_ptr(volatile void** dst, void* src, ma_atomic_memory_order order) + static C89ATOMIC_INLINE void c89atomic_store_explicit_ptr(volatile void** dst, void* src, c89atomic_memory_order order) { - ma_atomic_store_explicit_64((volatile ma_uint64*)dst, (ma_uint64)src, order); + c89atomic_store_explicit_64((volatile c89atomic_uint64*)dst, (c89atomic_uint64)src, order); } - static MA_INLINE void* ma_atomic_exchange_explicit_ptr(volatile void** dst, void* src, ma_atomic_memory_order order) + static C89ATOMIC_INLINE void* c89atomic_exchange_explicit_ptr(volatile void** dst, void* src, c89atomic_memory_order order) { - return (void*)ma_atomic_exchange_explicit_64((volatile ma_uint64*)dst, (ma_uint64)src, order); + return (void*)c89atomic_exchange_explicit_64((volatile c89atomic_uint64*)dst, (c89atomic_uint64)src, order); } - static MA_INLINE ma_bool32 ma_atomic_compare_exchange_strong_explicit_ptr(volatile void** dst, void** expected, void* desired, ma_atomic_memory_order successOrder, ma_atomic_memory_order failureOrder) + static C89ATOMIC_INLINE c89atomic_bool c89atomic_compare_exchange_strong_explicit_ptr(volatile void** dst, void** expected, void* desired, c89atomic_memory_order successOrder, c89atomic_memory_order failureOrder) { - return ma_atomic_compare_exchange_strong_explicit_64((volatile ma_uint64*)dst, (ma_uint64*)expected, (ma_uint64)desired, successOrder, failureOrder); + return c89atomic_compare_exchange_strong_explicit_64((volatile c89atomic_uint64*)dst, (c89atomic_uint64*)expected, (c89atomic_uint64)desired, successOrder, failureOrder); } - static MA_INLINE ma_bool32 ma_atomic_compare_exchange_weak_explicit_ptr(volatile void** dst, void** expected, void* desired, ma_atomic_memory_order successOrder, ma_atomic_memory_order failureOrder) + static C89ATOMIC_INLINE c89atomic_bool c89atomic_compare_exchange_weak_explicit_ptr(volatile void** dst, void** expected, void* desired, c89atomic_memory_order successOrder, c89atomic_memory_order failureOrder) { - return ma_atomic_compare_exchange_weak_explicit_64((volatile ma_uint64*)dst, (ma_uint64*)expected, (ma_uint64)desired, successOrder, failureOrder); + return c89atomic_compare_exchange_weak_explicit_64((volatile c89atomic_uint64*)dst, (c89atomic_uint64*)expected, (c89atomic_uint64)desired, successOrder, failureOrder); } - static MA_INLINE void* ma_atomic_compare_and_swap_ptr(volatile void** dst, void* expected, void* desired) + static C89ATOMIC_INLINE void* c89atomic_compare_and_swap_ptr(volatile void** dst, void* expected, void* desired) { - return (void*)ma_atomic_compare_and_swap_64((volatile ma_uint64*)dst, (ma_uint64)expected, (ma_uint64)desired); + return (void*)c89atomic_compare_and_swap_64((volatile c89atomic_uint64*)dst, (c89atomic_uint64)expected, (c89atomic_uint64)desired); } -#elif defined(MA_32BIT) - static MA_INLINE ma_bool32 ma_atomic_is_lock_free_ptr(volatile void** ptr) +#elif defined(C89ATOMIC_32BIT) + static C89ATOMIC_INLINE c89atomic_bool c89atomic_is_lock_free_ptr(volatile void** ptr) { - return ma_atomic_is_lock_free_32((volatile ma_uint32*)ptr); + return c89atomic_is_lock_free_32((volatile c89atomic_uint32*)ptr); } - static MA_INLINE void* ma_atomic_load_explicit_ptr(volatile void** ptr, ma_atomic_memory_order order) + static C89ATOMIC_INLINE void* c89atomic_load_explicit_ptr(volatile void** ptr, c89atomic_memory_order order) { - return (void*)ma_atomic_load_explicit_32((volatile ma_uint32*)ptr, order); + return (void*)c89atomic_load_explicit_32((volatile c89atomic_uint32*)ptr, order); } - static MA_INLINE void ma_atomic_store_explicit_ptr(volatile void** dst, void* src, ma_atomic_memory_order order) + static C89ATOMIC_INLINE void c89atomic_store_explicit_ptr(volatile void** dst, void* src, c89atomic_memory_order order) { - ma_atomic_store_explicit_32((volatile ma_uint32*)dst, (ma_uint32)src, order); + c89atomic_store_explicit_32((volatile c89atomic_uint32*)dst, (c89atomic_uint32)src, order); } - static MA_INLINE void* ma_atomic_exchange_explicit_ptr(volatile void** dst, void* src, ma_atomic_memory_order order) + static C89ATOMIC_INLINE void* c89atomic_exchange_explicit_ptr(volatile void** dst, void* src, c89atomic_memory_order order) { - return (void*)ma_atomic_exchange_explicit_32((volatile ma_uint32*)dst, (ma_uint32)src, order); + return (void*)c89atomic_exchange_explicit_32((volatile c89atomic_uint32*)dst, (c89atomic_uint32)src, order); } - static MA_INLINE ma_bool32 ma_atomic_compare_exchange_strong_explicit_ptr(volatile void** dst, void** expected, void* desired, ma_atomic_memory_order successOrder, ma_atomic_memory_order failureOrder) + static C89ATOMIC_INLINE c89atomic_bool c89atomic_compare_exchange_strong_explicit_ptr(volatile void** dst, void** expected, void* desired, c89atomic_memory_order successOrder, c89atomic_memory_order failureOrder) { - return ma_atomic_compare_exchange_strong_explicit_32((volatile ma_uint32*)dst, (ma_uint32*)expected, (ma_uint32)desired, successOrder, failureOrder); + return c89atomic_compare_exchange_strong_explicit_32((volatile c89atomic_uint32*)dst, (c89atomic_uint32*)expected, (c89atomic_uint32)desired, successOrder, failureOrder); } - static MA_INLINE ma_bool32 ma_atomic_compare_exchange_weak_explicit_ptr(volatile void** dst, void** expected, void* desired, ma_atomic_memory_order successOrder, ma_atomic_memory_order failureOrder) + static C89ATOMIC_INLINE c89atomic_bool c89atomic_compare_exchange_weak_explicit_ptr(volatile void** dst, void** expected, void* desired, c89atomic_memory_order successOrder, c89atomic_memory_order failureOrder) { - return ma_atomic_compare_exchange_weak_explicit_32((volatile ma_uint32*)dst, (ma_uint32*)expected, (ma_uint32)desired, successOrder, failureOrder); + return c89atomic_compare_exchange_weak_explicit_32((volatile c89atomic_uint32*)dst, (c89atomic_uint32*)expected, (c89atomic_uint32)desired, successOrder, failureOrder); } - static MA_INLINE void* ma_atomic_compare_and_swap_ptr(volatile void** dst, void* expected, void* desired) + static C89ATOMIC_INLINE void* c89atomic_compare_and_swap_ptr(volatile void** dst, void* expected, void* desired) { - return (void*)ma_atomic_compare_and_swap_32((volatile ma_uint32*)dst, (ma_uint32)expected, (ma_uint32)desired); + return (void*)c89atomic_compare_and_swap_32((volatile c89atomic_uint32*)dst, (c89atomic_uint32)expected, (c89atomic_uint32)desired); } #else #error Unsupported architecture. #endif -#define ma_atomic_flag_test_and_set(ptr) ma_atomic_flag_test_and_set_explicit(ptr, ma_atomic_memory_order_seq_cst) -#define ma_atomic_flag_clear(ptr) ma_atomic_flag_clear_explicit(ptr, ma_atomic_memory_order_seq_cst) -#define ma_atomic_store_ptr(dst, src) ma_atomic_store_explicit_ptr((volatile void**)dst, (void*)src, ma_atomic_memory_order_seq_cst) -#define ma_atomic_load_ptr(ptr) ma_atomic_load_explicit_ptr((volatile void**)ptr, ma_atomic_memory_order_seq_cst) -#define ma_atomic_exchange_ptr(dst, src) ma_atomic_exchange_explicit_ptr((volatile void**)dst, (void*)src, ma_atomic_memory_order_seq_cst) -#define ma_atomic_compare_exchange_strong_ptr(dst, expected, desired) ma_atomic_compare_exchange_strong_explicit_ptr((volatile void**)dst, (void**)expected, (void*)desired, ma_atomic_memory_order_seq_cst, ma_atomic_memory_order_seq_cst) -#define ma_atomic_compare_exchange_weak_ptr(dst, expected, desired) ma_atomic_compare_exchange_weak_explicit_ptr((volatile void**)dst, (void**)expected, (void*)desired, ma_atomic_memory_order_seq_cst, ma_atomic_memory_order_seq_cst) -#define ma_atomic_test_and_set_8( ptr) ma_atomic_test_and_set_explicit_8( ptr, ma_atomic_memory_order_seq_cst) -#define ma_atomic_test_and_set_16(ptr) ma_atomic_test_and_set_explicit_16(ptr, ma_atomic_memory_order_seq_cst) -#define ma_atomic_test_and_set_32(ptr) ma_atomic_test_and_set_explicit_32(ptr, ma_atomic_memory_order_seq_cst) -#define ma_atomic_test_and_set_64(ptr) ma_atomic_test_and_set_explicit_64(ptr, ma_atomic_memory_order_seq_cst) -#define ma_atomic_clear_8( ptr) ma_atomic_clear_explicit_8( ptr, ma_atomic_memory_order_seq_cst) -#define ma_atomic_clear_16(ptr) ma_atomic_clear_explicit_16(ptr, ma_atomic_memory_order_seq_cst) -#define ma_atomic_clear_32(ptr) ma_atomic_clear_explicit_32(ptr, ma_atomic_memory_order_seq_cst) -#define ma_atomic_clear_64(ptr) ma_atomic_clear_explicit_64(ptr, ma_atomic_memory_order_seq_cst) -#define ma_atomic_store_8( dst, src) ma_atomic_store_explicit_8( dst, src, ma_atomic_memory_order_seq_cst) -#define ma_atomic_store_16(dst, src) ma_atomic_store_explicit_16(dst, src, ma_atomic_memory_order_seq_cst) -#define ma_atomic_store_32(dst, src) ma_atomic_store_explicit_32(dst, src, ma_atomic_memory_order_seq_cst) -#define ma_atomic_store_64(dst, src) ma_atomic_store_explicit_64(dst, src, ma_atomic_memory_order_seq_cst) -#define ma_atomic_load_8( ptr) ma_atomic_load_explicit_8( ptr, ma_atomic_memory_order_seq_cst) -#define ma_atomic_load_16(ptr) ma_atomic_load_explicit_16(ptr, ma_atomic_memory_order_seq_cst) -#define ma_atomic_load_32(ptr) ma_atomic_load_explicit_32(ptr, ma_atomic_memory_order_seq_cst) -#define ma_atomic_load_64(ptr) ma_atomic_load_explicit_64(ptr, ma_atomic_memory_order_seq_cst) -#define ma_atomic_exchange_8( dst, src) ma_atomic_exchange_explicit_8( dst, src, ma_atomic_memory_order_seq_cst) -#define ma_atomic_exchange_16(dst, src) ma_atomic_exchange_explicit_16(dst, src, ma_atomic_memory_order_seq_cst) -#define ma_atomic_exchange_32(dst, src) ma_atomic_exchange_explicit_32(dst, src, ma_atomic_memory_order_seq_cst) -#define ma_atomic_exchange_64(dst, src) ma_atomic_exchange_explicit_64(dst, src, ma_atomic_memory_order_seq_cst) -#define ma_atomic_compare_exchange_strong_8( dst, expected, desired) ma_atomic_compare_exchange_strong_explicit_8( dst, expected, desired, ma_atomic_memory_order_seq_cst, ma_atomic_memory_order_seq_cst) -#define ma_atomic_compare_exchange_strong_16(dst, expected, desired) ma_atomic_compare_exchange_strong_explicit_16(dst, expected, desired, ma_atomic_memory_order_seq_cst, ma_atomic_memory_order_seq_cst) -#define ma_atomic_compare_exchange_strong_32(dst, expected, desired) ma_atomic_compare_exchange_strong_explicit_32(dst, expected, desired, ma_atomic_memory_order_seq_cst, ma_atomic_memory_order_seq_cst) -#define ma_atomic_compare_exchange_strong_64(dst, expected, desired) ma_atomic_compare_exchange_strong_explicit_64(dst, expected, desired, ma_atomic_memory_order_seq_cst, ma_atomic_memory_order_seq_cst) -#define ma_atomic_compare_exchange_weak_8( dst, expected, desired) ma_atomic_compare_exchange_weak_explicit_8( dst, expected, desired, ma_atomic_memory_order_seq_cst, ma_atomic_memory_order_seq_cst) -#define ma_atomic_compare_exchange_weak_16( dst, expected, desired) ma_atomic_compare_exchange_weak_explicit_16(dst, expected, desired, ma_atomic_memory_order_seq_cst, ma_atomic_memory_order_seq_cst) -#define ma_atomic_compare_exchange_weak_32( dst, expected, desired) ma_atomic_compare_exchange_weak_explicit_32(dst, expected, desired, ma_atomic_memory_order_seq_cst, ma_atomic_memory_order_seq_cst) -#define ma_atomic_compare_exchange_weak_64( dst, expected, desired) ma_atomic_compare_exchange_weak_explicit_64(dst, expected, desired, ma_atomic_memory_order_seq_cst, ma_atomic_memory_order_seq_cst) -#define ma_atomic_fetch_add_8( dst, src) ma_atomic_fetch_add_explicit_8( dst, src, ma_atomic_memory_order_seq_cst) -#define ma_atomic_fetch_add_16(dst, src) ma_atomic_fetch_add_explicit_16(dst, src, ma_atomic_memory_order_seq_cst) -#define ma_atomic_fetch_add_32(dst, src) ma_atomic_fetch_add_explicit_32(dst, src, ma_atomic_memory_order_seq_cst) -#define ma_atomic_fetch_add_64(dst, src) ma_atomic_fetch_add_explicit_64(dst, src, ma_atomic_memory_order_seq_cst) -#define ma_atomic_fetch_sub_8( dst, src) ma_atomic_fetch_sub_explicit_8( dst, src, ma_atomic_memory_order_seq_cst) -#define ma_atomic_fetch_sub_16(dst, src) ma_atomic_fetch_sub_explicit_16(dst, src, ma_atomic_memory_order_seq_cst) -#define ma_atomic_fetch_sub_32(dst, src) ma_atomic_fetch_sub_explicit_32(dst, src, ma_atomic_memory_order_seq_cst) -#define ma_atomic_fetch_sub_64(dst, src) ma_atomic_fetch_sub_explicit_64(dst, src, ma_atomic_memory_order_seq_cst) -#define ma_atomic_fetch_or_8( dst, src) ma_atomic_fetch_or_explicit_8( dst, src, ma_atomic_memory_order_seq_cst) -#define ma_atomic_fetch_or_16(dst, src) ma_atomic_fetch_or_explicit_16(dst, src, ma_atomic_memory_order_seq_cst) -#define ma_atomic_fetch_or_32(dst, src) ma_atomic_fetch_or_explicit_32(dst, src, ma_atomic_memory_order_seq_cst) -#define ma_atomic_fetch_or_64(dst, src) ma_atomic_fetch_or_explicit_64(dst, src, ma_atomic_memory_order_seq_cst) -#define ma_atomic_fetch_xor_8( dst, src) ma_atomic_fetch_xor_explicit_8( dst, src, ma_atomic_memory_order_seq_cst) -#define ma_atomic_fetch_xor_16(dst, src) ma_atomic_fetch_xor_explicit_16(dst, src, ma_atomic_memory_order_seq_cst) -#define ma_atomic_fetch_xor_32(dst, src) ma_atomic_fetch_xor_explicit_32(dst, src, ma_atomic_memory_order_seq_cst) -#define ma_atomic_fetch_xor_64(dst, src) ma_atomic_fetch_xor_explicit_64(dst, src, ma_atomic_memory_order_seq_cst) -#define ma_atomic_fetch_and_8( dst, src) ma_atomic_fetch_and_explicit_8 (dst, src, ma_atomic_memory_order_seq_cst) -#define ma_atomic_fetch_and_16(dst, src) ma_atomic_fetch_and_explicit_16(dst, src, ma_atomic_memory_order_seq_cst) -#define ma_atomic_fetch_and_32(dst, src) ma_atomic_fetch_and_explicit_32(dst, src, ma_atomic_memory_order_seq_cst) -#define ma_atomic_fetch_and_64(dst, src) ma_atomic_fetch_and_explicit_64(dst, src, ma_atomic_memory_order_seq_cst) -#define ma_atomic_test_and_set_explicit_i8( ptr, order) (ma_int8 )ma_atomic_test_and_set_explicit_8( (ma_uint8* )ptr, order) -#define ma_atomic_test_and_set_explicit_i16(ptr, order) (ma_int16)ma_atomic_test_and_set_explicit_16((ma_uint16*)ptr, order) -#define ma_atomic_test_and_set_explicit_i32(ptr, order) (ma_int32)ma_atomic_test_and_set_explicit_32((ma_uint32*)ptr, order) -#define ma_atomic_test_and_set_explicit_i64(ptr, order) (ma_int64)ma_atomic_test_and_set_explicit_64((ma_uint64*)ptr, order) -#define ma_atomic_clear_explicit_i8( ptr, order) ma_atomic_clear_explicit_8( (ma_uint8* )ptr, order) -#define ma_atomic_clear_explicit_i16(ptr, order) ma_atomic_clear_explicit_16((ma_uint16*)ptr, order) -#define ma_atomic_clear_explicit_i32(ptr, order) ma_atomic_clear_explicit_32((ma_uint32*)ptr, order) -#define ma_atomic_clear_explicit_i64(ptr, order) ma_atomic_clear_explicit_64((ma_uint64*)ptr, order) -#define ma_atomic_store_explicit_i8( dst, src, order) ma_atomic_store_explicit_8( (ma_uint8* )dst, (ma_uint8 )src, order) -#define ma_atomic_store_explicit_i16(dst, src, order) ma_atomic_store_explicit_16((ma_uint16*)dst, (ma_uint16)src, order) -#define ma_atomic_store_explicit_i32(dst, src, order) ma_atomic_store_explicit_32((ma_uint32*)dst, (ma_uint32)src, order) -#define ma_atomic_store_explicit_i64(dst, src, order) ma_atomic_store_explicit_64((ma_uint64*)dst, (ma_uint64)src, order) -#define ma_atomic_load_explicit_i8( ptr, order) (ma_int8 )ma_atomic_load_explicit_8( (ma_uint8* )ptr, order) -#define ma_atomic_load_explicit_i16(ptr, order) (ma_int16)ma_atomic_load_explicit_16((ma_uint16*)ptr, order) -#define ma_atomic_load_explicit_i32(ptr, order) (ma_int32)ma_atomic_load_explicit_32((ma_uint32*)ptr, order) -#define ma_atomic_load_explicit_i64(ptr, order) (ma_int64)ma_atomic_load_explicit_64((ma_uint64*)ptr, order) -#define ma_atomic_exchange_explicit_i8( dst, src, order) (ma_int8 )ma_atomic_exchange_explicit_8 ((ma_uint8* )dst, (ma_uint8 )src, order) -#define ma_atomic_exchange_explicit_i16(dst, src, order) (ma_int16)ma_atomic_exchange_explicit_16((ma_uint16*)dst, (ma_uint16)src, order) -#define ma_atomic_exchange_explicit_i32(dst, src, order) (ma_int32)ma_atomic_exchange_explicit_32((ma_uint32*)dst, (ma_uint32)src, order) -#define ma_atomic_exchange_explicit_i64(dst, src, order) (ma_int64)ma_atomic_exchange_explicit_64((ma_uint64*)dst, (ma_uint64)src, order) -#define ma_atomic_compare_exchange_strong_explicit_i8( dst, expected, desired, successOrder, failureOrder) ma_atomic_compare_exchange_strong_explicit_8( (ma_uint8* )dst, (ma_uint8* )expected, (ma_uint8 )desired, successOrder, failureOrder) -#define ma_atomic_compare_exchange_strong_explicit_i16(dst, expected, desired, successOrder, failureOrder) ma_atomic_compare_exchange_strong_explicit_16((ma_uint16*)dst, (ma_uint16*)expected, (ma_uint16)desired, successOrder, failureOrder) -#define ma_atomic_compare_exchange_strong_explicit_i32(dst, expected, desired, successOrder, failureOrder) ma_atomic_compare_exchange_strong_explicit_32((ma_uint32*)dst, (ma_uint32*)expected, (ma_uint32)desired, successOrder, failureOrder) -#define ma_atomic_compare_exchange_strong_explicit_i64(dst, expected, desired, successOrder, failureOrder) ma_atomic_compare_exchange_strong_explicit_64((ma_uint64*)dst, (ma_uint64*)expected, (ma_uint64)desired, successOrder, failureOrder) -#define ma_atomic_compare_exchange_weak_explicit_i8( dst, expected, desired, successOrder, failureOrder) ma_atomic_compare_exchange_weak_explicit_8( (ma_uint8* )dst, (ma_uint8* )expected, (ma_uint8 )desired, successOrder, failureOrder) -#define ma_atomic_compare_exchange_weak_explicit_i16(dst, expected, desired, successOrder, failureOrder) ma_atomic_compare_exchange_weak_explicit_16((ma_uint16*)dst, (ma_uint16*)expected, (ma_uint16)desired, successOrder, failureOrder) -#define ma_atomic_compare_exchange_weak_explicit_i32(dst, expected, desired, successOrder, failureOrder) ma_atomic_compare_exchange_weak_explicit_32((ma_uint32*)dst, (ma_uint32*)expected, (ma_uint32)desired, successOrder, failureOrder) -#define ma_atomic_compare_exchange_weak_explicit_i64(dst, expected, desired, successOrder, failureOrder) ma_atomic_compare_exchange_weak_explicit_64((ma_uint64*)dst, (ma_uint64*)expected, (ma_uint64)desired, successOrder, failureOrder) -#define ma_atomic_fetch_add_explicit_i8( dst, src, order) (ma_int8 )ma_atomic_fetch_add_explicit_8( (ma_uint8* )dst, (ma_uint8 )src, order) -#define ma_atomic_fetch_add_explicit_i16(dst, src, order) (ma_int16)ma_atomic_fetch_add_explicit_16((ma_uint16*)dst, (ma_uint16)src, order) -#define ma_atomic_fetch_add_explicit_i32(dst, src, order) (ma_int32)ma_atomic_fetch_add_explicit_32((ma_uint32*)dst, (ma_uint32)src, order) -#define ma_atomic_fetch_add_explicit_i64(dst, src, order) (ma_int64)ma_atomic_fetch_add_explicit_64((ma_uint64*)dst, (ma_uint64)src, order) -#define ma_atomic_fetch_sub_explicit_i8( dst, src, order) (ma_int8 )ma_atomic_fetch_sub_explicit_8( (ma_uint8* )dst, (ma_uint8 )src, order) -#define ma_atomic_fetch_sub_explicit_i16(dst, src, order) (ma_int16)ma_atomic_fetch_sub_explicit_16((ma_uint16*)dst, (ma_uint16)src, order) -#define ma_atomic_fetch_sub_explicit_i32(dst, src, order) (ma_int32)ma_atomic_fetch_sub_explicit_32((ma_uint32*)dst, (ma_uint32)src, order) -#define ma_atomic_fetch_sub_explicit_i64(dst, src, order) (ma_int64)ma_atomic_fetch_sub_explicit_64((ma_uint64*)dst, (ma_uint64)src, order) -#define ma_atomic_fetch_or_explicit_i8( dst, src, order) (ma_int8 )ma_atomic_fetch_or_explicit_8( (ma_uint8* )dst, (ma_uint8 )src, order) -#define ma_atomic_fetch_or_explicit_i16(dst, src, order) (ma_int16)ma_atomic_fetch_or_explicit_16((ma_uint16*)dst, (ma_uint16)src, order) -#define ma_atomic_fetch_or_explicit_i32(dst, src, order) (ma_int32)ma_atomic_fetch_or_explicit_32((ma_uint32*)dst, (ma_uint32)src, order) -#define ma_atomic_fetch_or_explicit_i64(dst, src, order) (ma_int64)ma_atomic_fetch_or_explicit_64((ma_uint64*)dst, (ma_uint64)src, order) -#define ma_atomic_fetch_xor_explicit_i8( dst, src, order) (ma_int8 )ma_atomic_fetch_xor_explicit_8( (ma_uint8* )dst, (ma_uint8 )src, order) -#define ma_atomic_fetch_xor_explicit_i16(dst, src, order) (ma_int16)ma_atomic_fetch_xor_explicit_16((ma_uint16*)dst, (ma_uint16)src, order) -#define ma_atomic_fetch_xor_explicit_i32(dst, src, order) (ma_int32)ma_atomic_fetch_xor_explicit_32((ma_uint32*)dst, (ma_uint32)src, order) -#define ma_atomic_fetch_xor_explicit_i64(dst, src, order) (ma_int64)ma_atomic_fetch_xor_explicit_64((ma_uint64*)dst, (ma_uint64)src, order) -#define ma_atomic_fetch_and_explicit_i8( dst, src, order) (ma_int8 )ma_atomic_fetch_and_explicit_8( (ma_uint8* )dst, (ma_uint8 )src, order) -#define ma_atomic_fetch_and_explicit_i16(dst, src, order) (ma_int16)ma_atomic_fetch_and_explicit_16((ma_uint16*)dst, (ma_uint16)src, order) -#define ma_atomic_fetch_and_explicit_i32(dst, src, order) (ma_int32)ma_atomic_fetch_and_explicit_32((ma_uint32*)dst, (ma_uint32)src, order) -#define ma_atomic_fetch_and_explicit_i64(dst, src, order) (ma_int64)ma_atomic_fetch_and_explicit_64((ma_uint64*)dst, (ma_uint64)src, order) -#define ma_atomic_test_and_set_i8( ptr) ma_atomic_test_and_set_explicit_i8( ptr, ma_atomic_memory_order_seq_cst) -#define ma_atomic_test_and_set_i16(ptr) ma_atomic_test_and_set_explicit_i16(ptr, ma_atomic_memory_order_seq_cst) -#define ma_atomic_test_and_set_i32(ptr) ma_atomic_test_and_set_explicit_i32(ptr, ma_atomic_memory_order_seq_cst) -#define ma_atomic_test_and_set_i64(ptr) ma_atomic_test_and_set_explicit_i64(ptr, ma_atomic_memory_order_seq_cst) -#define ma_atomic_clear_i8( ptr) ma_atomic_clear_explicit_i8( ptr, ma_atomic_memory_order_seq_cst) -#define ma_atomic_clear_i16(ptr) ma_atomic_clear_explicit_i16(ptr, ma_atomic_memory_order_seq_cst) -#define ma_atomic_clear_i32(ptr) ma_atomic_clear_explicit_i32(ptr, ma_atomic_memory_order_seq_cst) -#define ma_atomic_clear_i64(ptr) ma_atomic_clear_explicit_i64(ptr, ma_atomic_memory_order_seq_cst) -#define ma_atomic_store_i8( dst, src) ma_atomic_store_explicit_i8( dst, src, ma_atomic_memory_order_seq_cst) -#define ma_atomic_store_i16(dst, src) ma_atomic_store_explicit_i16(dst, src, ma_atomic_memory_order_seq_cst) -#define ma_atomic_store_i32(dst, src) ma_atomic_store_explicit_i32(dst, src, ma_atomic_memory_order_seq_cst) -#define ma_atomic_store_i64(dst, src) ma_atomic_store_explicit_i64(dst, src, ma_atomic_memory_order_seq_cst) -#define ma_atomic_load_i8( ptr) ma_atomic_load_explicit_i8( ptr, ma_atomic_memory_order_seq_cst) -#define ma_atomic_load_i16(ptr) ma_atomic_load_explicit_i16(ptr, ma_atomic_memory_order_seq_cst) -#define ma_atomic_load_i32(ptr) ma_atomic_load_explicit_i32(ptr, ma_atomic_memory_order_seq_cst) -#define ma_atomic_load_i64(ptr) ma_atomic_load_explicit_i64(ptr, ma_atomic_memory_order_seq_cst) -#define ma_atomic_exchange_i8( dst, src) ma_atomic_exchange_explicit_i8( dst, src, ma_atomic_memory_order_seq_cst) -#define ma_atomic_exchange_i16(dst, src) ma_atomic_exchange_explicit_i16(dst, src, ma_atomic_memory_order_seq_cst) -#define ma_atomic_exchange_i32(dst, src) ma_atomic_exchange_explicit_i32(dst, src, ma_atomic_memory_order_seq_cst) -#define ma_atomic_exchange_i64(dst, src) ma_atomic_exchange_explicit_i64(dst, src, ma_atomic_memory_order_seq_cst) -#define ma_atomic_compare_exchange_strong_i8( dst, expected, desired) ma_atomic_compare_exchange_strong_explicit_i8( dst, expected, desired, ma_atomic_memory_order_seq_cst, ma_atomic_memory_order_seq_cst) -#define ma_atomic_compare_exchange_strong_i16(dst, expected, desired) ma_atomic_compare_exchange_strong_explicit_i16(dst, expected, desired, ma_atomic_memory_order_seq_cst, ma_atomic_memory_order_seq_cst) -#define ma_atomic_compare_exchange_strong_i32(dst, expected, desired) ma_atomic_compare_exchange_strong_explicit_i32(dst, expected, desired, ma_atomic_memory_order_seq_cst, ma_atomic_memory_order_seq_cst) -#define ma_atomic_compare_exchange_strong_i64(dst, expected, desired) ma_atomic_compare_exchange_strong_explicit_i64(dst, expected, desired, ma_atomic_memory_order_seq_cst, ma_atomic_memory_order_seq_cst) -#define ma_atomic_compare_exchange_weak_i8( dst, expected, desired) ma_atomic_compare_exchange_weak_explicit_i8( dst, expected, desired, ma_atomic_memory_order_seq_cst, ma_atomic_memory_order_seq_cst) -#define ma_atomic_compare_exchange_weak_i16(dst, expected, desired) ma_atomic_compare_exchange_weak_explicit_i16(dst, expected, desired, ma_atomic_memory_order_seq_cst, ma_atomic_memory_order_seq_cst) -#define ma_atomic_compare_exchange_weak_i32(dst, expected, desired) ma_atomic_compare_exchange_weak_explicit_i32(dst, expected, desired, ma_atomic_memory_order_seq_cst, ma_atomic_memory_order_seq_cst) -#define ma_atomic_compare_exchange_weak_i64(dst, expected, desired) ma_atomic_compare_exchange_weak_explicit_i64(dst, expected, desired, ma_atomic_memory_order_seq_cst, ma_atomic_memory_order_seq_cst) -#define ma_atomic_fetch_add_i8( dst, src) ma_atomic_fetch_add_explicit_i8( dst, src, ma_atomic_memory_order_seq_cst) -#define ma_atomic_fetch_add_i16(dst, src) ma_atomic_fetch_add_explicit_i16(dst, src, ma_atomic_memory_order_seq_cst) -#define ma_atomic_fetch_add_i32(dst, src) ma_atomic_fetch_add_explicit_i32(dst, src, ma_atomic_memory_order_seq_cst) -#define ma_atomic_fetch_add_i64(dst, src) ma_atomic_fetch_add_explicit_i64(dst, src, ma_atomic_memory_order_seq_cst) -#define ma_atomic_fetch_sub_i8( dst, src) ma_atomic_fetch_sub_explicit_i8( dst, src, ma_atomic_memory_order_seq_cst) -#define ma_atomic_fetch_sub_i16(dst, src) ma_atomic_fetch_sub_explicit_i16(dst, src, ma_atomic_memory_order_seq_cst) -#define ma_atomic_fetch_sub_i32(dst, src) ma_atomic_fetch_sub_explicit_i32(dst, src, ma_atomic_memory_order_seq_cst) -#define ma_atomic_fetch_sub_i64(dst, src) ma_atomic_fetch_sub_explicit_i64(dst, src, ma_atomic_memory_order_seq_cst) -#define ma_atomic_fetch_or_i8( dst, src) ma_atomic_fetch_or_explicit_i8( dst, src, ma_atomic_memory_order_seq_cst) -#define ma_atomic_fetch_or_i16(dst, src) ma_atomic_fetch_or_explicit_i16(dst, src, ma_atomic_memory_order_seq_cst) -#define ma_atomic_fetch_or_i32(dst, src) ma_atomic_fetch_or_explicit_i32(dst, src, ma_atomic_memory_order_seq_cst) -#define ma_atomic_fetch_or_i64(dst, src) ma_atomic_fetch_or_explicit_i64(dst, src, ma_atomic_memory_order_seq_cst) -#define ma_atomic_fetch_xor_i8( dst, src) ma_atomic_fetch_xor_explicit_i8( dst, src, ma_atomic_memory_order_seq_cst) -#define ma_atomic_fetch_xor_i16(dst, src) ma_atomic_fetch_xor_explicit_i16(dst, src, ma_atomic_memory_order_seq_cst) -#define ma_atomic_fetch_xor_i32(dst, src) ma_atomic_fetch_xor_explicit_i32(dst, src, ma_atomic_memory_order_seq_cst) -#define ma_atomic_fetch_xor_i64(dst, src) ma_atomic_fetch_xor_explicit_i64(dst, src, ma_atomic_memory_order_seq_cst) -#define ma_atomic_fetch_and_i8( dst, src) ma_atomic_fetch_and_explicit_i8( dst, src, ma_atomic_memory_order_seq_cst) -#define ma_atomic_fetch_and_i16(dst, src) ma_atomic_fetch_and_explicit_i16(dst, src, ma_atomic_memory_order_seq_cst) -#define ma_atomic_fetch_and_i32(dst, src) ma_atomic_fetch_and_explicit_i32(dst, src, ma_atomic_memory_order_seq_cst) -#define ma_atomic_fetch_and_i64(dst, src) ma_atomic_fetch_and_explicit_i64(dst, src, ma_atomic_memory_order_seq_cst) -#define ma_atomic_compare_and_swap_i8( dst, expected, dedsired) (ma_int8 )ma_atomic_compare_and_swap_8( (ma_uint8* )dst, (ma_uint8 )expected, (ma_uint8 )dedsired) -#define ma_atomic_compare_and_swap_i16(dst, expected, dedsired) (ma_int16)ma_atomic_compare_and_swap_16((ma_uint16*)dst, (ma_uint16)expected, (ma_uint16)dedsired) -#define ma_atomic_compare_and_swap_i32(dst, expected, dedsired) (ma_int32)ma_atomic_compare_and_swap_32((ma_uint32*)dst, (ma_uint32)expected, (ma_uint32)dedsired) -#define ma_atomic_compare_and_swap_i64(dst, expected, dedsired) (ma_int64)ma_atomic_compare_and_swap_64((ma_uint64*)dst, (ma_uint64)expected, (ma_uint64)dedsired) +#define c89atomic_flag_test_and_set(ptr) c89atomic_flag_test_and_set_explicit(ptr, c89atomic_memory_order_seq_cst) +#define c89atomic_flag_clear(ptr) c89atomic_flag_clear_explicit(ptr, c89atomic_memory_order_seq_cst) +#define c89atomic_store_ptr(dst, src) c89atomic_store_explicit_ptr((volatile void**)dst, (void*)src, c89atomic_memory_order_seq_cst) +#define c89atomic_load_ptr(ptr) c89atomic_load_explicit_ptr((volatile void**)ptr, c89atomic_memory_order_seq_cst) +#define c89atomic_exchange_ptr(dst, src) c89atomic_exchange_explicit_ptr((volatile void**)dst, (void*)src, c89atomic_memory_order_seq_cst) +#define c89atomic_compare_exchange_strong_ptr(dst, expected, desired) c89atomic_compare_exchange_strong_explicit_ptr((volatile void**)dst, (void**)expected, (void*)desired, c89atomic_memory_order_seq_cst, c89atomic_memory_order_seq_cst) +#define c89atomic_compare_exchange_weak_ptr(dst, expected, desired) c89atomic_compare_exchange_weak_explicit_ptr((volatile void**)dst, (void**)expected, (void*)desired, c89atomic_memory_order_seq_cst, c89atomic_memory_order_seq_cst) +#define c89atomic_test_and_set_8( ptr) c89atomic_test_and_set_explicit_8( ptr, c89atomic_memory_order_seq_cst) +#define c89atomic_test_and_set_16(ptr) c89atomic_test_and_set_explicit_16(ptr, c89atomic_memory_order_seq_cst) +#define c89atomic_test_and_set_32(ptr) c89atomic_test_and_set_explicit_32(ptr, c89atomic_memory_order_seq_cst) +#define c89atomic_test_and_set_64(ptr) c89atomic_test_and_set_explicit_64(ptr, c89atomic_memory_order_seq_cst) +#define c89atomic_clear_8( ptr) c89atomic_clear_explicit_8( ptr, c89atomic_memory_order_seq_cst) +#define c89atomic_clear_16(ptr) c89atomic_clear_explicit_16(ptr, c89atomic_memory_order_seq_cst) +#define c89atomic_clear_32(ptr) c89atomic_clear_explicit_32(ptr, c89atomic_memory_order_seq_cst) +#define c89atomic_clear_64(ptr) c89atomic_clear_explicit_64(ptr, c89atomic_memory_order_seq_cst) +#define c89atomic_store_8( dst, src) c89atomic_store_explicit_8( dst, src, c89atomic_memory_order_seq_cst) +#define c89atomic_store_16(dst, src) c89atomic_store_explicit_16(dst, src, c89atomic_memory_order_seq_cst) +#define c89atomic_store_32(dst, src) c89atomic_store_explicit_32(dst, src, c89atomic_memory_order_seq_cst) +#define c89atomic_store_64(dst, src) c89atomic_store_explicit_64(dst, src, c89atomic_memory_order_seq_cst) +#define c89atomic_load_8( ptr) c89atomic_load_explicit_8( ptr, c89atomic_memory_order_seq_cst) +#define c89atomic_load_16(ptr) c89atomic_load_explicit_16(ptr, c89atomic_memory_order_seq_cst) +#define c89atomic_load_32(ptr) c89atomic_load_explicit_32(ptr, c89atomic_memory_order_seq_cst) +#define c89atomic_load_64(ptr) c89atomic_load_explicit_64(ptr, c89atomic_memory_order_seq_cst) +#define c89atomic_exchange_8( dst, src) c89atomic_exchange_explicit_8( dst, src, c89atomic_memory_order_seq_cst) +#define c89atomic_exchange_16(dst, src) c89atomic_exchange_explicit_16(dst, src, c89atomic_memory_order_seq_cst) +#define c89atomic_exchange_32(dst, src) c89atomic_exchange_explicit_32(dst, src, c89atomic_memory_order_seq_cst) +#define c89atomic_exchange_64(dst, src) c89atomic_exchange_explicit_64(dst, src, c89atomic_memory_order_seq_cst) +#define c89atomic_compare_exchange_strong_8( dst, expected, desired) c89atomic_compare_exchange_strong_explicit_8( dst, expected, desired, c89atomic_memory_order_seq_cst, c89atomic_memory_order_seq_cst) +#define c89atomic_compare_exchange_strong_16(dst, expected, desired) c89atomic_compare_exchange_strong_explicit_16(dst, expected, desired, c89atomic_memory_order_seq_cst, c89atomic_memory_order_seq_cst) +#define c89atomic_compare_exchange_strong_32(dst, expected, desired) c89atomic_compare_exchange_strong_explicit_32(dst, expected, desired, c89atomic_memory_order_seq_cst, c89atomic_memory_order_seq_cst) +#define c89atomic_compare_exchange_strong_64(dst, expected, desired) c89atomic_compare_exchange_strong_explicit_64(dst, expected, desired, c89atomic_memory_order_seq_cst, c89atomic_memory_order_seq_cst) +#define c89atomic_compare_exchange_weak_8( dst, expected, desired) c89atomic_compare_exchange_weak_explicit_8( dst, expected, desired, c89atomic_memory_order_seq_cst, c89atomic_memory_order_seq_cst) +#define c89atomic_compare_exchange_weak_16( dst, expected, desired) c89atomic_compare_exchange_weak_explicit_16(dst, expected, desired, c89atomic_memory_order_seq_cst, c89atomic_memory_order_seq_cst) +#define c89atomic_compare_exchange_weak_32( dst, expected, desired) c89atomic_compare_exchange_weak_explicit_32(dst, expected, desired, c89atomic_memory_order_seq_cst, c89atomic_memory_order_seq_cst) +#define c89atomic_compare_exchange_weak_64( dst, expected, desired) c89atomic_compare_exchange_weak_explicit_64(dst, expected, desired, c89atomic_memory_order_seq_cst, c89atomic_memory_order_seq_cst) +#define c89atomic_fetch_add_8( dst, src) c89atomic_fetch_add_explicit_8( dst, src, c89atomic_memory_order_seq_cst) +#define c89atomic_fetch_add_16(dst, src) c89atomic_fetch_add_explicit_16(dst, src, c89atomic_memory_order_seq_cst) +#define c89atomic_fetch_add_32(dst, src) c89atomic_fetch_add_explicit_32(dst, src, c89atomic_memory_order_seq_cst) +#define c89atomic_fetch_add_64(dst, src) c89atomic_fetch_add_explicit_64(dst, src, c89atomic_memory_order_seq_cst) +#define c89atomic_fetch_sub_8( dst, src) c89atomic_fetch_sub_explicit_8( dst, src, c89atomic_memory_order_seq_cst) +#define c89atomic_fetch_sub_16(dst, src) c89atomic_fetch_sub_explicit_16(dst, src, c89atomic_memory_order_seq_cst) +#define c89atomic_fetch_sub_32(dst, src) c89atomic_fetch_sub_explicit_32(dst, src, c89atomic_memory_order_seq_cst) +#define c89atomic_fetch_sub_64(dst, src) c89atomic_fetch_sub_explicit_64(dst, src, c89atomic_memory_order_seq_cst) +#define c89atomic_fetch_or_8( dst, src) c89atomic_fetch_or_explicit_8( dst, src, c89atomic_memory_order_seq_cst) +#define c89atomic_fetch_or_16(dst, src) c89atomic_fetch_or_explicit_16(dst, src, c89atomic_memory_order_seq_cst) +#define c89atomic_fetch_or_32(dst, src) c89atomic_fetch_or_explicit_32(dst, src, c89atomic_memory_order_seq_cst) +#define c89atomic_fetch_or_64(dst, src) c89atomic_fetch_or_explicit_64(dst, src, c89atomic_memory_order_seq_cst) +#define c89atomic_fetch_xor_8( dst, src) c89atomic_fetch_xor_explicit_8( dst, src, c89atomic_memory_order_seq_cst) +#define c89atomic_fetch_xor_16(dst, src) c89atomic_fetch_xor_explicit_16(dst, src, c89atomic_memory_order_seq_cst) +#define c89atomic_fetch_xor_32(dst, src) c89atomic_fetch_xor_explicit_32(dst, src, c89atomic_memory_order_seq_cst) +#define c89atomic_fetch_xor_64(dst, src) c89atomic_fetch_xor_explicit_64(dst, src, c89atomic_memory_order_seq_cst) +#define c89atomic_fetch_and_8( dst, src) c89atomic_fetch_and_explicit_8 (dst, src, c89atomic_memory_order_seq_cst) +#define c89atomic_fetch_and_16(dst, src) c89atomic_fetch_and_explicit_16(dst, src, c89atomic_memory_order_seq_cst) +#define c89atomic_fetch_and_32(dst, src) c89atomic_fetch_and_explicit_32(dst, src, c89atomic_memory_order_seq_cst) +#define c89atomic_fetch_and_64(dst, src) c89atomic_fetch_and_explicit_64(dst, src, c89atomic_memory_order_seq_cst) +#define c89atomic_test_and_set_explicit_i8( ptr, order) (c89atomic_int8 )c89atomic_test_and_set_explicit_8( (c89atomic_uint8* )ptr, order) +#define c89atomic_test_and_set_explicit_i16(ptr, order) (c89atomic_int16)c89atomic_test_and_set_explicit_16((c89atomic_uint16*)ptr, order) +#define c89atomic_test_and_set_explicit_i32(ptr, order) (c89atomic_int32)c89atomic_test_and_set_explicit_32((c89atomic_uint32*)ptr, order) +#define c89atomic_test_and_set_explicit_i64(ptr, order) (c89atomic_int64)c89atomic_test_and_set_explicit_64((c89atomic_uint64*)ptr, order) +#define c89atomic_clear_explicit_i8( ptr, order) c89atomic_clear_explicit_8( (c89atomic_uint8* )ptr, order) +#define c89atomic_clear_explicit_i16(ptr, order) c89atomic_clear_explicit_16((c89atomic_uint16*)ptr, order) +#define c89atomic_clear_explicit_i32(ptr, order) c89atomic_clear_explicit_32((c89atomic_uint32*)ptr, order) +#define c89atomic_clear_explicit_i64(ptr, order) c89atomic_clear_explicit_64((c89atomic_uint64*)ptr, order) +#define c89atomic_store_explicit_i8( dst, src, order) c89atomic_store_explicit_8( (c89atomic_uint8* )dst, (c89atomic_uint8 )src, order) +#define c89atomic_store_explicit_i16(dst, src, order) c89atomic_store_explicit_16((c89atomic_uint16*)dst, (c89atomic_uint16)src, order) +#define c89atomic_store_explicit_i32(dst, src, order) c89atomic_store_explicit_32((c89atomic_uint32*)dst, (c89atomic_uint32)src, order) +#define c89atomic_store_explicit_i64(dst, src, order) c89atomic_store_explicit_64((c89atomic_uint64*)dst, (c89atomic_uint64)src, order) +#define c89atomic_load_explicit_i8( ptr, order) (c89atomic_int8 )c89atomic_load_explicit_8( (c89atomic_uint8* )ptr, order) +#define c89atomic_load_explicit_i16(ptr, order) (c89atomic_int16)c89atomic_load_explicit_16((c89atomic_uint16*)ptr, order) +#define c89atomic_load_explicit_i32(ptr, order) (c89atomic_int32)c89atomic_load_explicit_32((c89atomic_uint32*)ptr, order) +#define c89atomic_load_explicit_i64(ptr, order) (c89atomic_int64)c89atomic_load_explicit_64((c89atomic_uint64*)ptr, order) +#define c89atomic_exchange_explicit_i8( dst, src, order) (c89atomic_int8 )c89atomic_exchange_explicit_8 ((c89atomic_uint8* )dst, (c89atomic_uint8 )src, order) +#define c89atomic_exchange_explicit_i16(dst, src, order) (c89atomic_int16)c89atomic_exchange_explicit_16((c89atomic_uint16*)dst, (c89atomic_uint16)src, order) +#define c89atomic_exchange_explicit_i32(dst, src, order) (c89atomic_int32)c89atomic_exchange_explicit_32((c89atomic_uint32*)dst, (c89atomic_uint32)src, order) +#define c89atomic_exchange_explicit_i64(dst, src, order) (c89atomic_int64)c89atomic_exchange_explicit_64((c89atomic_uint64*)dst, (c89atomic_uint64)src, order) +#define c89atomic_compare_exchange_strong_explicit_i8( dst, expected, desired, successOrder, failureOrder) c89atomic_compare_exchange_strong_explicit_8( (c89atomic_uint8* )dst, (c89atomic_uint8* )expected, (c89atomic_uint8 )desired, successOrder, failureOrder) +#define c89atomic_compare_exchange_strong_explicit_i16(dst, expected, desired, successOrder, failureOrder) c89atomic_compare_exchange_strong_explicit_16((c89atomic_uint16*)dst, (c89atomic_uint16*)expected, (c89atomic_uint16)desired, successOrder, failureOrder) +#define c89atomic_compare_exchange_strong_explicit_i32(dst, expected, desired, successOrder, failureOrder) c89atomic_compare_exchange_strong_explicit_32((c89atomic_uint32*)dst, (c89atomic_uint32*)expected, (c89atomic_uint32)desired, successOrder, failureOrder) +#define c89atomic_compare_exchange_strong_explicit_i64(dst, expected, desired, successOrder, failureOrder) c89atomic_compare_exchange_strong_explicit_64((c89atomic_uint64*)dst, (c89atomic_uint64*)expected, (c89atomic_uint64)desired, successOrder, failureOrder) +#define c89atomic_compare_exchange_weak_explicit_i8( dst, expected, desired, successOrder, failureOrder) c89atomic_compare_exchange_weak_explicit_8( (c89atomic_uint8* )dst, (c89atomic_uint8* )expected, (c89atomic_uint8 )desired, successOrder, failureOrder) +#define c89atomic_compare_exchange_weak_explicit_i16(dst, expected, desired, successOrder, failureOrder) c89atomic_compare_exchange_weak_explicit_16((c89atomic_uint16*)dst, (c89atomic_uint16*)expected, (c89atomic_uint16)desired, successOrder, failureOrder) +#define c89atomic_compare_exchange_weak_explicit_i32(dst, expected, desired, successOrder, failureOrder) c89atomic_compare_exchange_weak_explicit_32((c89atomic_uint32*)dst, (c89atomic_uint32*)expected, (c89atomic_uint32)desired, successOrder, failureOrder) +#define c89atomic_compare_exchange_weak_explicit_i64(dst, expected, desired, successOrder, failureOrder) c89atomic_compare_exchange_weak_explicit_64((c89atomic_uint64*)dst, (c89atomic_uint64*)expected, (c89atomic_uint64)desired, successOrder, failureOrder) +#define c89atomic_fetch_add_explicit_i8( dst, src, order) (c89atomic_int8 )c89atomic_fetch_add_explicit_8( (c89atomic_uint8* )dst, (c89atomic_uint8 )src, order) +#define c89atomic_fetch_add_explicit_i16(dst, src, order) (c89atomic_int16)c89atomic_fetch_add_explicit_16((c89atomic_uint16*)dst, (c89atomic_uint16)src, order) +#define c89atomic_fetch_add_explicit_i32(dst, src, order) (c89atomic_int32)c89atomic_fetch_add_explicit_32((c89atomic_uint32*)dst, (c89atomic_uint32)src, order) +#define c89atomic_fetch_add_explicit_i64(dst, src, order) (c89atomic_int64)c89atomic_fetch_add_explicit_64((c89atomic_uint64*)dst, (c89atomic_uint64)src, order) +#define c89atomic_fetch_sub_explicit_i8( dst, src, order) (c89atomic_int8 )c89atomic_fetch_sub_explicit_8( (c89atomic_uint8* )dst, (c89atomic_uint8 )src, order) +#define c89atomic_fetch_sub_explicit_i16(dst, src, order) (c89atomic_int16)c89atomic_fetch_sub_explicit_16((c89atomic_uint16*)dst, (c89atomic_uint16)src, order) +#define c89atomic_fetch_sub_explicit_i32(dst, src, order) (c89atomic_int32)c89atomic_fetch_sub_explicit_32((c89atomic_uint32*)dst, (c89atomic_uint32)src, order) +#define c89atomic_fetch_sub_explicit_i64(dst, src, order) (c89atomic_int64)c89atomic_fetch_sub_explicit_64((c89atomic_uint64*)dst, (c89atomic_uint64)src, order) +#define c89atomic_fetch_or_explicit_i8( dst, src, order) (c89atomic_int8 )c89atomic_fetch_or_explicit_8( (c89atomic_uint8* )dst, (c89atomic_uint8 )src, order) +#define c89atomic_fetch_or_explicit_i16(dst, src, order) (c89atomic_int16)c89atomic_fetch_or_explicit_16((c89atomic_uint16*)dst, (c89atomic_uint16)src, order) +#define c89atomic_fetch_or_explicit_i32(dst, src, order) (c89atomic_int32)c89atomic_fetch_or_explicit_32((c89atomic_uint32*)dst, (c89atomic_uint32)src, order) +#define c89atomic_fetch_or_explicit_i64(dst, src, order) (c89atomic_int64)c89atomic_fetch_or_explicit_64((c89atomic_uint64*)dst, (c89atomic_uint64)src, order) +#define c89atomic_fetch_xor_explicit_i8( dst, src, order) (c89atomic_int8 )c89atomic_fetch_xor_explicit_8( (c89atomic_uint8* )dst, (c89atomic_uint8 )src, order) +#define c89atomic_fetch_xor_explicit_i16(dst, src, order) (c89atomic_int16)c89atomic_fetch_xor_explicit_16((c89atomic_uint16*)dst, (c89atomic_uint16)src, order) +#define c89atomic_fetch_xor_explicit_i32(dst, src, order) (c89atomic_int32)c89atomic_fetch_xor_explicit_32((c89atomic_uint32*)dst, (c89atomic_uint32)src, order) +#define c89atomic_fetch_xor_explicit_i64(dst, src, order) (c89atomic_int64)c89atomic_fetch_xor_explicit_64((c89atomic_uint64*)dst, (c89atomic_uint64)src, order) +#define c89atomic_fetch_and_explicit_i8( dst, src, order) (c89atomic_int8 )c89atomic_fetch_and_explicit_8( (c89atomic_uint8* )dst, (c89atomic_uint8 )src, order) +#define c89atomic_fetch_and_explicit_i16(dst, src, order) (c89atomic_int16)c89atomic_fetch_and_explicit_16((c89atomic_uint16*)dst, (c89atomic_uint16)src, order) +#define c89atomic_fetch_and_explicit_i32(dst, src, order) (c89atomic_int32)c89atomic_fetch_and_explicit_32((c89atomic_uint32*)dst, (c89atomic_uint32)src, order) +#define c89atomic_fetch_and_explicit_i64(dst, src, order) (c89atomic_int64)c89atomic_fetch_and_explicit_64((c89atomic_uint64*)dst, (c89atomic_uint64)src, order) +#define c89atomic_test_and_set_i8( ptr) c89atomic_test_and_set_explicit_i8( ptr, c89atomic_memory_order_seq_cst) +#define c89atomic_test_and_set_i16(ptr) c89atomic_test_and_set_explicit_i16(ptr, c89atomic_memory_order_seq_cst) +#define c89atomic_test_and_set_i32(ptr) c89atomic_test_and_set_explicit_i32(ptr, c89atomic_memory_order_seq_cst) +#define c89atomic_test_and_set_i64(ptr) c89atomic_test_and_set_explicit_i64(ptr, c89atomic_memory_order_seq_cst) +#define c89atomic_clear_i8( ptr) c89atomic_clear_explicit_i8( ptr, c89atomic_memory_order_seq_cst) +#define c89atomic_clear_i16(ptr) c89atomic_clear_explicit_i16(ptr, c89atomic_memory_order_seq_cst) +#define c89atomic_clear_i32(ptr) c89atomic_clear_explicit_i32(ptr, c89atomic_memory_order_seq_cst) +#define c89atomic_clear_i64(ptr) c89atomic_clear_explicit_i64(ptr, c89atomic_memory_order_seq_cst) +#define c89atomic_store_i8( dst, src) c89atomic_store_explicit_i8( dst, src, c89atomic_memory_order_seq_cst) +#define c89atomic_store_i16(dst, src) c89atomic_store_explicit_i16(dst, src, c89atomic_memory_order_seq_cst) +#define c89atomic_store_i32(dst, src) c89atomic_store_explicit_i32(dst, src, c89atomic_memory_order_seq_cst) +#define c89atomic_store_i64(dst, src) c89atomic_store_explicit_i64(dst, src, c89atomic_memory_order_seq_cst) +#define c89atomic_load_i8( ptr) c89atomic_load_explicit_i8( ptr, c89atomic_memory_order_seq_cst) +#define c89atomic_load_i16(ptr) c89atomic_load_explicit_i16(ptr, c89atomic_memory_order_seq_cst) +#define c89atomic_load_i32(ptr) c89atomic_load_explicit_i32(ptr, c89atomic_memory_order_seq_cst) +#define c89atomic_load_i64(ptr) c89atomic_load_explicit_i64(ptr, c89atomic_memory_order_seq_cst) +#define c89atomic_exchange_i8( dst, src) c89atomic_exchange_explicit_i8( dst, src, c89atomic_memory_order_seq_cst) +#define c89atomic_exchange_i16(dst, src) c89atomic_exchange_explicit_i16(dst, src, c89atomic_memory_order_seq_cst) +#define c89atomic_exchange_i32(dst, src) c89atomic_exchange_explicit_i32(dst, src, c89atomic_memory_order_seq_cst) +#define c89atomic_exchange_i64(dst, src) c89atomic_exchange_explicit_i64(dst, src, c89atomic_memory_order_seq_cst) +#define c89atomic_compare_exchange_strong_i8( dst, expected, desired) c89atomic_compare_exchange_strong_explicit_i8( dst, expected, desired, c89atomic_memory_order_seq_cst, c89atomic_memory_order_seq_cst) +#define c89atomic_compare_exchange_strong_i16(dst, expected, desired) c89atomic_compare_exchange_strong_explicit_i16(dst, expected, desired, c89atomic_memory_order_seq_cst, c89atomic_memory_order_seq_cst) +#define c89atomic_compare_exchange_strong_i32(dst, expected, desired) c89atomic_compare_exchange_strong_explicit_i32(dst, expected, desired, c89atomic_memory_order_seq_cst, c89atomic_memory_order_seq_cst) +#define c89atomic_compare_exchange_strong_i64(dst, expected, desired) c89atomic_compare_exchange_strong_explicit_i64(dst, expected, desired, c89atomic_memory_order_seq_cst, c89atomic_memory_order_seq_cst) +#define c89atomic_compare_exchange_weak_i8( dst, expected, desired) c89atomic_compare_exchange_weak_explicit_i8( dst, expected, desired, c89atomic_memory_order_seq_cst, c89atomic_memory_order_seq_cst) +#define c89atomic_compare_exchange_weak_i16(dst, expected, desired) c89atomic_compare_exchange_weak_explicit_i16(dst, expected, desired, c89atomic_memory_order_seq_cst, c89atomic_memory_order_seq_cst) +#define c89atomic_compare_exchange_weak_i32(dst, expected, desired) c89atomic_compare_exchange_weak_explicit_i32(dst, expected, desired, c89atomic_memory_order_seq_cst, c89atomic_memory_order_seq_cst) +#define c89atomic_compare_exchange_weak_i64(dst, expected, desired) c89atomic_compare_exchange_weak_explicit_i64(dst, expected, desired, c89atomic_memory_order_seq_cst, c89atomic_memory_order_seq_cst) +#define c89atomic_fetch_add_i8( dst, src) c89atomic_fetch_add_explicit_i8( dst, src, c89atomic_memory_order_seq_cst) +#define c89atomic_fetch_add_i16(dst, src) c89atomic_fetch_add_explicit_i16(dst, src, c89atomic_memory_order_seq_cst) +#define c89atomic_fetch_add_i32(dst, src) c89atomic_fetch_add_explicit_i32(dst, src, c89atomic_memory_order_seq_cst) +#define c89atomic_fetch_add_i64(dst, src) c89atomic_fetch_add_explicit_i64(dst, src, c89atomic_memory_order_seq_cst) +#define c89atomic_fetch_sub_i8( dst, src) c89atomic_fetch_sub_explicit_i8( dst, src, c89atomic_memory_order_seq_cst) +#define c89atomic_fetch_sub_i16(dst, src) c89atomic_fetch_sub_explicit_i16(dst, src, c89atomic_memory_order_seq_cst) +#define c89atomic_fetch_sub_i32(dst, src) c89atomic_fetch_sub_explicit_i32(dst, src, c89atomic_memory_order_seq_cst) +#define c89atomic_fetch_sub_i64(dst, src) c89atomic_fetch_sub_explicit_i64(dst, src, c89atomic_memory_order_seq_cst) +#define c89atomic_fetch_or_i8( dst, src) c89atomic_fetch_or_explicit_i8( dst, src, c89atomic_memory_order_seq_cst) +#define c89atomic_fetch_or_i16(dst, src) c89atomic_fetch_or_explicit_i16(dst, src, c89atomic_memory_order_seq_cst) +#define c89atomic_fetch_or_i32(dst, src) c89atomic_fetch_or_explicit_i32(dst, src, c89atomic_memory_order_seq_cst) +#define c89atomic_fetch_or_i64(dst, src) c89atomic_fetch_or_explicit_i64(dst, src, c89atomic_memory_order_seq_cst) +#define c89atomic_fetch_xor_i8( dst, src) c89atomic_fetch_xor_explicit_i8( dst, src, c89atomic_memory_order_seq_cst) +#define c89atomic_fetch_xor_i16(dst, src) c89atomic_fetch_xor_explicit_i16(dst, src, c89atomic_memory_order_seq_cst) +#define c89atomic_fetch_xor_i32(dst, src) c89atomic_fetch_xor_explicit_i32(dst, src, c89atomic_memory_order_seq_cst) +#define c89atomic_fetch_xor_i64(dst, src) c89atomic_fetch_xor_explicit_i64(dst, src, c89atomic_memory_order_seq_cst) +#define c89atomic_fetch_and_i8( dst, src) c89atomic_fetch_and_explicit_i8( dst, src, c89atomic_memory_order_seq_cst) +#define c89atomic_fetch_and_i16(dst, src) c89atomic_fetch_and_explicit_i16(dst, src, c89atomic_memory_order_seq_cst) +#define c89atomic_fetch_and_i32(dst, src) c89atomic_fetch_and_explicit_i32(dst, src, c89atomic_memory_order_seq_cst) +#define c89atomic_fetch_and_i64(dst, src) c89atomic_fetch_and_explicit_i64(dst, src, c89atomic_memory_order_seq_cst) +#define c89atomic_compare_and_swap_i8( dst, expected, dedsired) (c89atomic_int8 )c89atomic_compare_and_swap_8( (c89atomic_uint8* )dst, (c89atomic_uint8 )expected, (c89atomic_uint8 )dedsired) +#define c89atomic_compare_and_swap_i16(dst, expected, dedsired) (c89atomic_int16)c89atomic_compare_and_swap_16((c89atomic_uint16*)dst, (c89atomic_uint16)expected, (c89atomic_uint16)dedsired) +#define c89atomic_compare_and_swap_i32(dst, expected, dedsired) (c89atomic_int32)c89atomic_compare_and_swap_32((c89atomic_uint32*)dst, (c89atomic_uint32)expected, (c89atomic_uint32)dedsired) +#define c89atomic_compare_and_swap_i64(dst, expected, dedsired) (c89atomic_int64)c89atomic_compare_and_swap_64((c89atomic_uint64*)dst, (c89atomic_uint64)expected, (c89atomic_uint64)dedsired) typedef union { - ma_uint32 i; + c89atomic_uint32 i; float f; -} ma_atomic_if32; +} c89atomic_if32; typedef union { - ma_uint64 i; + c89atomic_uint64 i; double f; -} ma_atomic_if64; -#define ma_atomic_clear_explicit_f32(ptr, order) ma_atomic_clear_explicit_32((ma_uint32*)ptr, order) -#define ma_atomic_clear_explicit_f64(ptr, order) ma_atomic_clear_explicit_64((ma_uint64*)ptr, order) -static MA_INLINE void ma_atomic_store_explicit_f32(volatile float* dst, float src, ma_atomic_memory_order order) +} c89atomic_if64; +#define c89atomic_clear_explicit_f32(ptr, order) c89atomic_clear_explicit_32((c89atomic_uint32*)ptr, order) +#define c89atomic_clear_explicit_f64(ptr, order) c89atomic_clear_explicit_64((c89atomic_uint64*)ptr, order) +static C89ATOMIC_INLINE void c89atomic_store_explicit_f32(volatile float* dst, float src, c89atomic_memory_order order) { - ma_atomic_if32 x; + c89atomic_if32 x; x.f = src; - ma_atomic_store_explicit_32((volatile ma_uint32*)dst, x.i, order); + c89atomic_store_explicit_32((volatile c89atomic_uint32*)dst, x.i, order); } -static MA_INLINE void ma_atomic_store_explicit_f64(volatile double* dst, double src, ma_atomic_memory_order order) +static C89ATOMIC_INLINE void c89atomic_store_explicit_f64(volatile double* dst, double src, c89atomic_memory_order order) { - ma_atomic_if64 x; + c89atomic_if64 x; x.f = src; - ma_atomic_store_explicit_64((volatile ma_uint64*)dst, x.i, order); + c89atomic_store_explicit_64((volatile c89atomic_uint64*)dst, x.i, order); } -static MA_INLINE float ma_atomic_load_explicit_f32(volatile const float* ptr, ma_atomic_memory_order order) +static C89ATOMIC_INLINE float c89atomic_load_explicit_f32(volatile const float* ptr, c89atomic_memory_order order) { - ma_atomic_if32 r; - r.i = ma_atomic_load_explicit_32((volatile const ma_uint32*)ptr, order); + c89atomic_if32 r; + r.i = c89atomic_load_explicit_32((volatile const c89atomic_uint32*)ptr, order); return r.f; } -static MA_INLINE double ma_atomic_load_explicit_f64(volatile const double* ptr, ma_atomic_memory_order order) +static C89ATOMIC_INLINE double c89atomic_load_explicit_f64(volatile const double* ptr, c89atomic_memory_order order) { - ma_atomic_if64 r; - r.i = ma_atomic_load_explicit_64((volatile const ma_uint64*)ptr, order); + c89atomic_if64 r; + r.i = c89atomic_load_explicit_64((volatile const c89atomic_uint64*)ptr, order); return r.f; } -static MA_INLINE float ma_atomic_exchange_explicit_f32(volatile float* dst, float src, ma_atomic_memory_order order) +static C89ATOMIC_INLINE float c89atomic_exchange_explicit_f32(volatile float* dst, float src, c89atomic_memory_order order) { - ma_atomic_if32 r; - ma_atomic_if32 x; + c89atomic_if32 r; + c89atomic_if32 x; x.f = src; - r.i = ma_atomic_exchange_explicit_32((volatile ma_uint32*)dst, x.i, order); + r.i = c89atomic_exchange_explicit_32((volatile c89atomic_uint32*)dst, x.i, order); return r.f; } -static MA_INLINE double ma_atomic_exchange_explicit_f64(volatile double* dst, double src, ma_atomic_memory_order order) +static C89ATOMIC_INLINE double c89atomic_exchange_explicit_f64(volatile double* dst, double src, c89atomic_memory_order order) { - ma_atomic_if64 r; - ma_atomic_if64 x; + c89atomic_if64 r; + c89atomic_if64 x; x.f = src; - r.i = ma_atomic_exchange_explicit_64((volatile ma_uint64*)dst, x.i, order); + r.i = c89atomic_exchange_explicit_64((volatile c89atomic_uint64*)dst, x.i, order); return r.f; } -static MA_INLINE ma_bool32 ma_atomic_compare_exchange_strong_explicit_f32(volatile float* dst, float* expected, float desired, ma_atomic_memory_order successOrder, ma_atomic_memory_order failureOrder) +static C89ATOMIC_INLINE c89atomic_bool c89atomic_compare_exchange_strong_explicit_f32(volatile float* dst, float* expected, float desired, c89atomic_memory_order successOrder, c89atomic_memory_order failureOrder) { - ma_atomic_if32 d; + c89atomic_if32 d; d.f = desired; - return ma_atomic_compare_exchange_strong_explicit_32((volatile ma_uint32*)dst, (ma_uint32*)expected, d.i, successOrder, failureOrder); + return c89atomic_compare_exchange_strong_explicit_32((volatile c89atomic_uint32*)dst, (c89atomic_uint32*)expected, d.i, successOrder, failureOrder); } -static MA_INLINE ma_bool32 ma_atomic_compare_exchange_strong_explicit_f64(volatile double* dst, double* expected, double desired, ma_atomic_memory_order successOrder, ma_atomic_memory_order failureOrder) +static C89ATOMIC_INLINE c89atomic_bool c89atomic_compare_exchange_strong_explicit_f64(volatile double* dst, double* expected, double desired, c89atomic_memory_order successOrder, c89atomic_memory_order failureOrder) { - ma_atomic_if64 d; + c89atomic_if64 d; d.f = desired; - return ma_atomic_compare_exchange_strong_explicit_64((volatile ma_uint64*)dst, (ma_uint64*)expected, d.i, successOrder, failureOrder); + return c89atomic_compare_exchange_strong_explicit_64((volatile c89atomic_uint64*)dst, (c89atomic_uint64*)expected, d.i, successOrder, failureOrder); } -static MA_INLINE ma_bool32 ma_atomic_compare_exchange_weak_explicit_f32(volatile float* dst, float* expected, float desired, ma_atomic_memory_order successOrder, ma_atomic_memory_order failureOrder) +static C89ATOMIC_INLINE c89atomic_bool c89atomic_compare_exchange_weak_explicit_f32(volatile float* dst, float* expected, float desired, c89atomic_memory_order successOrder, c89atomic_memory_order failureOrder) { - ma_atomic_if32 d; + c89atomic_if32 d; d.f = desired; - return ma_atomic_compare_exchange_weak_explicit_32((volatile ma_uint32*)dst, (ma_uint32*)expected, d.i, successOrder, failureOrder); + return c89atomic_compare_exchange_weak_explicit_32((volatile c89atomic_uint32*)dst, (c89atomic_uint32*)expected, d.i, successOrder, failureOrder); } -static MA_INLINE ma_bool32 ma_atomic_compare_exchange_weak_explicit_f64(volatile double* dst, double* expected, double desired, ma_atomic_memory_order successOrder, ma_atomic_memory_order failureOrder) +static C89ATOMIC_INLINE c89atomic_bool c89atomic_compare_exchange_weak_explicit_f64(volatile double* dst, double* expected, double desired, c89atomic_memory_order successOrder, c89atomic_memory_order failureOrder) { - ma_atomic_if64 d; + c89atomic_if64 d; d.f = desired; - return ma_atomic_compare_exchange_weak_explicit_64((volatile ma_uint64*)dst, (ma_uint64*)expected, d.i, successOrder, failureOrder); + return c89atomic_compare_exchange_weak_explicit_64((volatile c89atomic_uint64*)dst, (c89atomic_uint64*)expected, d.i, successOrder, failureOrder); } -static MA_INLINE float ma_atomic_fetch_add_explicit_f32(volatile float* dst, float src, ma_atomic_memory_order order) +static C89ATOMIC_INLINE float c89atomic_fetch_add_explicit_f32(volatile float* dst, float src, c89atomic_memory_order order) { - ma_atomic_if32 r; - ma_atomic_if32 x; + c89atomic_if32 r; + c89atomic_if32 x; x.f = src; - r.i = ma_atomic_fetch_add_explicit_32((volatile ma_uint32*)dst, x.i, order); + r.i = c89atomic_fetch_add_explicit_32((volatile c89atomic_uint32*)dst, x.i, order); return r.f; } -static MA_INLINE double ma_atomic_fetch_add_explicit_f64(volatile double* dst, double src, ma_atomic_memory_order order) +static C89ATOMIC_INLINE double c89atomic_fetch_add_explicit_f64(volatile double* dst, double src, c89atomic_memory_order order) { - ma_atomic_if64 r; - ma_atomic_if64 x; + c89atomic_if64 r; + c89atomic_if64 x; x.f = src; - r.i = ma_atomic_fetch_add_explicit_64((volatile ma_uint64*)dst, x.i, order); + r.i = c89atomic_fetch_add_explicit_64((volatile c89atomic_uint64*)dst, x.i, order); return r.f; } -static MA_INLINE float ma_atomic_fetch_sub_explicit_f32(volatile float* dst, float src, ma_atomic_memory_order order) +static C89ATOMIC_INLINE float c89atomic_fetch_sub_explicit_f32(volatile float* dst, float src, c89atomic_memory_order order) { - ma_atomic_if32 r; - ma_atomic_if32 x; + c89atomic_if32 r; + c89atomic_if32 x; x.f = src; - r.i = ma_atomic_fetch_sub_explicit_32((volatile ma_uint32*)dst, x.i, order); + r.i = c89atomic_fetch_sub_explicit_32((volatile c89atomic_uint32*)dst, x.i, order); return r.f; } -static MA_INLINE double ma_atomic_fetch_sub_explicit_f64(volatile double* dst, double src, ma_atomic_memory_order order) +static C89ATOMIC_INLINE double c89atomic_fetch_sub_explicit_f64(volatile double* dst, double src, c89atomic_memory_order order) { - ma_atomic_if64 r; - ma_atomic_if64 x; + c89atomic_if64 r; + c89atomic_if64 x; x.f = src; - r.i = ma_atomic_fetch_sub_explicit_64((volatile ma_uint64*)dst, x.i, order); + r.i = c89atomic_fetch_sub_explicit_64((volatile c89atomic_uint64*)dst, x.i, order); return r.f; } -static MA_INLINE float ma_atomic_fetch_or_explicit_f32(volatile float* dst, float src, ma_atomic_memory_order order) +static C89ATOMIC_INLINE float c89atomic_fetch_or_explicit_f32(volatile float* dst, float src, c89atomic_memory_order order) { - ma_atomic_if32 r; - ma_atomic_if32 x; + c89atomic_if32 r; + c89atomic_if32 x; x.f = src; - r.i = ma_atomic_fetch_or_explicit_32((volatile ma_uint32*)dst, x.i, order); + r.i = c89atomic_fetch_or_explicit_32((volatile c89atomic_uint32*)dst, x.i, order); return r.f; } -static MA_INLINE double ma_atomic_fetch_or_explicit_f64(volatile double* dst, double src, ma_atomic_memory_order order) +static C89ATOMIC_INLINE double c89atomic_fetch_or_explicit_f64(volatile double* dst, double src, c89atomic_memory_order order) { - ma_atomic_if64 r; - ma_atomic_if64 x; + c89atomic_if64 r; + c89atomic_if64 x; x.f = src; - r.i = ma_atomic_fetch_or_explicit_64((volatile ma_uint64*)dst, x.i, order); + r.i = c89atomic_fetch_or_explicit_64((volatile c89atomic_uint64*)dst, x.i, order); return r.f; } -static MA_INLINE float ma_atomic_fetch_xor_explicit_f32(volatile float* dst, float src, ma_atomic_memory_order order) +static C89ATOMIC_INLINE float c89atomic_fetch_xor_explicit_f32(volatile float* dst, float src, c89atomic_memory_order order) { - ma_atomic_if32 r; - ma_atomic_if32 x; + c89atomic_if32 r; + c89atomic_if32 x; x.f = src; - r.i = ma_atomic_fetch_xor_explicit_32((volatile ma_uint32*)dst, x.i, order); + r.i = c89atomic_fetch_xor_explicit_32((volatile c89atomic_uint32*)dst, x.i, order); return r.f; } -static MA_INLINE double ma_atomic_fetch_xor_explicit_f64(volatile double* dst, double src, ma_atomic_memory_order order) +static C89ATOMIC_INLINE double c89atomic_fetch_xor_explicit_f64(volatile double* dst, double src, c89atomic_memory_order order) { - ma_atomic_if64 r; - ma_atomic_if64 x; + c89atomic_if64 r; + c89atomic_if64 x; x.f = src; - r.i = ma_atomic_fetch_xor_explicit_64((volatile ma_uint64*)dst, x.i, order); + r.i = c89atomic_fetch_xor_explicit_64((volatile c89atomic_uint64*)dst, x.i, order); return r.f; } -static MA_INLINE float ma_atomic_fetch_and_explicit_f32(volatile float* dst, float src, ma_atomic_memory_order order) +static C89ATOMIC_INLINE float c89atomic_fetch_and_explicit_f32(volatile float* dst, float src, c89atomic_memory_order order) { - ma_atomic_if32 r; - ma_atomic_if32 x; + c89atomic_if32 r; + c89atomic_if32 x; x.f = src; - r.i = ma_atomic_fetch_and_explicit_32((volatile ma_uint32*)dst, x.i, order); + r.i = c89atomic_fetch_and_explicit_32((volatile c89atomic_uint32*)dst, x.i, order); return r.f; } -static MA_INLINE double ma_atomic_fetch_and_explicit_f64(volatile double* dst, double src, ma_atomic_memory_order order) +static C89ATOMIC_INLINE double c89atomic_fetch_and_explicit_f64(volatile double* dst, double src, c89atomic_memory_order order) { - ma_atomic_if64 r; - ma_atomic_if64 x; + c89atomic_if64 r; + c89atomic_if64 x; x.f = src; - r.i = ma_atomic_fetch_and_explicit_64((volatile ma_uint64*)dst, x.i, order); + r.i = c89atomic_fetch_and_explicit_64((volatile c89atomic_uint64*)dst, x.i, order); return r.f; } -#define ma_atomic_clear_f32(ptr) (float )ma_atomic_clear_explicit_f32(ptr, ma_atomic_memory_order_seq_cst) -#define ma_atomic_clear_f64(ptr) (double)ma_atomic_clear_explicit_f64(ptr, ma_atomic_memory_order_seq_cst) -#define ma_atomic_store_f32(dst, src) ma_atomic_store_explicit_f32(dst, src, ma_atomic_memory_order_seq_cst) -#define ma_atomic_store_f64(dst, src) ma_atomic_store_explicit_f64(dst, src, ma_atomic_memory_order_seq_cst) -#define ma_atomic_load_f32(ptr) (float )ma_atomic_load_explicit_f32(ptr, ma_atomic_memory_order_seq_cst) -#define ma_atomic_load_f64(ptr) (double)ma_atomic_load_explicit_f64(ptr, ma_atomic_memory_order_seq_cst) -#define ma_atomic_exchange_f32(dst, src) (float )ma_atomic_exchange_explicit_f32(dst, src, ma_atomic_memory_order_seq_cst) -#define ma_atomic_exchange_f64(dst, src) (double)ma_atomic_exchange_explicit_f64(dst, src, ma_atomic_memory_order_seq_cst) -#define ma_atomic_compare_exchange_strong_f32(dst, expected, desired) ma_atomic_compare_exchange_strong_explicit_f32(dst, expected, desired, ma_atomic_memory_order_seq_cst, ma_atomic_memory_order_seq_cst) -#define ma_atomic_compare_exchange_strong_f64(dst, expected, desired) ma_atomic_compare_exchange_strong_explicit_f64(dst, expected, desired, ma_atomic_memory_order_seq_cst, ma_atomic_memory_order_seq_cst) -#define ma_atomic_compare_exchange_weak_f32(dst, expected, desired) ma_atomic_compare_exchange_weak_explicit_f32(dst, expected, desired, ma_atomic_memory_order_seq_cst, ma_atomic_memory_order_seq_cst) -#define ma_atomic_compare_exchange_weak_f64(dst, expected, desired) ma_atomic_compare_exchange_weak_explicit_f64(dst, expected, desired, ma_atomic_memory_order_seq_cst, ma_atomic_memory_order_seq_cst) -#define ma_atomic_fetch_add_f32(dst, src) ma_atomic_fetch_add_explicit_f32(dst, src, ma_atomic_memory_order_seq_cst) -#define ma_atomic_fetch_add_f64(dst, src) ma_atomic_fetch_add_explicit_f64(dst, src, ma_atomic_memory_order_seq_cst) -#define ma_atomic_fetch_sub_f32(dst, src) ma_atomic_fetch_sub_explicit_f32(dst, src, ma_atomic_memory_order_seq_cst) -#define ma_atomic_fetch_sub_f64(dst, src) ma_atomic_fetch_sub_explicit_f64(dst, src, ma_atomic_memory_order_seq_cst) -#define ma_atomic_fetch_or_f32(dst, src) ma_atomic_fetch_or_explicit_f32(dst, src, ma_atomic_memory_order_seq_cst) -#define ma_atomic_fetch_or_f64(dst, src) ma_atomic_fetch_or_explicit_f64(dst, src, ma_atomic_memory_order_seq_cst) -#define ma_atomic_fetch_xor_f32(dst, src) ma_atomic_fetch_xor_explicit_f32(dst, src, ma_atomic_memory_order_seq_cst) -#define ma_atomic_fetch_xor_f64(dst, src) ma_atomic_fetch_xor_explicit_f64(dst, src, ma_atomic_memory_order_seq_cst) -#define ma_atomic_fetch_and_f32(dst, src) ma_atomic_fetch_and_explicit_f32(dst, src, ma_atomic_memory_order_seq_cst) -#define ma_atomic_fetch_and_f64(dst, src) ma_atomic_fetch_and_explicit_f64(dst, src, ma_atomic_memory_order_seq_cst) -static MA_INLINE float ma_atomic_compare_and_swap_f32(volatile float* dst, float expected, float desired) -{ - ma_atomic_if32 r; - ma_atomic_if32 e, d; +#define c89atomic_clear_f32(ptr) (float )c89atomic_clear_explicit_f32(ptr, c89atomic_memory_order_seq_cst) +#define c89atomic_clear_f64(ptr) (double)c89atomic_clear_explicit_f64(ptr, c89atomic_memory_order_seq_cst) +#define c89atomic_store_f32(dst, src) c89atomic_store_explicit_f32(dst, src, c89atomic_memory_order_seq_cst) +#define c89atomic_store_f64(dst, src) c89atomic_store_explicit_f64(dst, src, c89atomic_memory_order_seq_cst) +#define c89atomic_load_f32(ptr) (float )c89atomic_load_explicit_f32(ptr, c89atomic_memory_order_seq_cst) +#define c89atomic_load_f64(ptr) (double)c89atomic_load_explicit_f64(ptr, c89atomic_memory_order_seq_cst) +#define c89atomic_exchange_f32(dst, src) (float )c89atomic_exchange_explicit_f32(dst, src, c89atomic_memory_order_seq_cst) +#define c89atomic_exchange_f64(dst, src) (double)c89atomic_exchange_explicit_f64(dst, src, c89atomic_memory_order_seq_cst) +#define c89atomic_compare_exchange_strong_f32(dst, expected, desired) c89atomic_compare_exchange_strong_explicit_f32(dst, expected, desired, c89atomic_memory_order_seq_cst, c89atomic_memory_order_seq_cst) +#define c89atomic_compare_exchange_strong_f64(dst, expected, desired) c89atomic_compare_exchange_strong_explicit_f64(dst, expected, desired, c89atomic_memory_order_seq_cst, c89atomic_memory_order_seq_cst) +#define c89atomic_compare_exchange_weak_f32(dst, expected, desired) c89atomic_compare_exchange_weak_explicit_f32(dst, expected, desired, c89atomic_memory_order_seq_cst, c89atomic_memory_order_seq_cst) +#define c89atomic_compare_exchange_weak_f64(dst, expected, desired) c89atomic_compare_exchange_weak_explicit_f64(dst, expected, desired, c89atomic_memory_order_seq_cst, c89atomic_memory_order_seq_cst) +#define c89atomic_fetch_add_f32(dst, src) c89atomic_fetch_add_explicit_f32(dst, src, c89atomic_memory_order_seq_cst) +#define c89atomic_fetch_add_f64(dst, src) c89atomic_fetch_add_explicit_f64(dst, src, c89atomic_memory_order_seq_cst) +#define c89atomic_fetch_sub_f32(dst, src) c89atomic_fetch_sub_explicit_f32(dst, src, c89atomic_memory_order_seq_cst) +#define c89atomic_fetch_sub_f64(dst, src) c89atomic_fetch_sub_explicit_f64(dst, src, c89atomic_memory_order_seq_cst) +#define c89atomic_fetch_or_f32(dst, src) c89atomic_fetch_or_explicit_f32(dst, src, c89atomic_memory_order_seq_cst) +#define c89atomic_fetch_or_f64(dst, src) c89atomic_fetch_or_explicit_f64(dst, src, c89atomic_memory_order_seq_cst) +#define c89atomic_fetch_xor_f32(dst, src) c89atomic_fetch_xor_explicit_f32(dst, src, c89atomic_memory_order_seq_cst) +#define c89atomic_fetch_xor_f64(dst, src) c89atomic_fetch_xor_explicit_f64(dst, src, c89atomic_memory_order_seq_cst) +#define c89atomic_fetch_and_f32(dst, src) c89atomic_fetch_and_explicit_f32(dst, src, c89atomic_memory_order_seq_cst) +#define c89atomic_fetch_and_f64(dst, src) c89atomic_fetch_and_explicit_f64(dst, src, c89atomic_memory_order_seq_cst) +static C89ATOMIC_INLINE float c89atomic_compare_and_swap_f32(volatile float* dst, float expected, float desired) +{ + c89atomic_if32 r; + c89atomic_if32 e, d; e.f = expected; d.f = desired; - r.i = ma_atomic_compare_and_swap_32((volatile ma_uint32*)dst, e.i, d.i); + r.i = c89atomic_compare_and_swap_32((volatile c89atomic_uint32*)dst, e.i, d.i); return r.f; } -static MA_INLINE double ma_atomic_compare_and_swap_f64(volatile double* dst, double expected, double desired) +static C89ATOMIC_INLINE double c89atomic_compare_and_swap_f64(volatile double* dst, double expected, double desired) { - ma_atomic_if64 r; - ma_atomic_if64 e, d; + c89atomic_if64 r; + c89atomic_if64 e, d; e.f = expected; d.f = desired; - r.i = ma_atomic_compare_and_swap_64((volatile ma_uint64*)dst, e.i, d.i); + r.i = c89atomic_compare_and_swap_64((volatile c89atomic_uint64*)dst, e.i, d.i); return r.f; } -typedef ma_atomic_flag ma_atomic_spinlock; -static MA_INLINE void ma_atomic_spinlock_lock(volatile ma_atomic_spinlock* pSpinlock) +typedef c89atomic_flag c89atomic_spinlock; +static C89ATOMIC_INLINE void c89atomic_spinlock_lock(volatile c89atomic_spinlock* pSpinlock) { for (;;) { - if (ma_atomic_flag_test_and_set_explicit(pSpinlock, ma_atomic_memory_order_acquire) == 0) { + if (c89atomic_flag_test_and_set_explicit(pSpinlock, c89atomic_memory_order_acquire) == 0) { break; } - while (ma_atomic_flag_load_explicit(pSpinlock, ma_atomic_memory_order_relaxed) == 1) { + while (c89atoimc_flag_load_explicit(pSpinlock, c89atomic_memory_order_relaxed) == 1) { } } } -static MA_INLINE void ma_atomic_spinlock_unlock(volatile ma_atomic_spinlock* pSpinlock) +static C89ATOMIC_INLINE void c89atomic_spinlock_unlock(volatile c89atomic_spinlock* pSpinlock) { - ma_atomic_flag_clear_explicit(pSpinlock, ma_atomic_memory_order_release); + c89atomic_flag_clear_explicit(pSpinlock, c89atomic_memory_order_release); } #if defined(__clang__) || (defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6))) #pragma GCC diagnostic pop @@ -15983,65 +15890,65 @@ static MA_INLINE void ma_atomic_spinlock_unlock(volatile ma_atomic_spinlock* pSp #define MA_ATOMIC_SAFE_TYPE_IMPL(c89TypeExtension, type) \ static MA_INLINE ma_##type ma_atomic_##type##_get(ma_atomic_##type* x) \ { \ - return (ma_##type)ma_atomic_load_##c89TypeExtension(&x->value); \ + return (ma_##type)c89atomic_load_##c89TypeExtension(&x->value); \ } \ static MA_INLINE void ma_atomic_##type##_set(ma_atomic_##type* x, ma_##type value) \ { \ - ma_atomic_store_##c89TypeExtension(&x->value, value); \ + c89atomic_store_##c89TypeExtension(&x->value, value); \ } \ static MA_INLINE ma_##type ma_atomic_##type##_exchange(ma_atomic_##type* x, ma_##type value) \ { \ - return (ma_##type)ma_atomic_exchange_##c89TypeExtension(&x->value, value); \ + return (ma_##type)c89atomic_exchange_##c89TypeExtension(&x->value, value); \ } \ static MA_INLINE ma_bool32 ma_atomic_##type##_compare_exchange(ma_atomic_##type* x, ma_##type* expected, ma_##type desired) \ { \ - return ma_atomic_compare_exchange_weak_##c89TypeExtension(&x->value, expected, desired); \ + return c89atomic_compare_exchange_weak_##c89TypeExtension(&x->value, expected, desired); \ } \ static MA_INLINE ma_##type ma_atomic_##type##_fetch_add(ma_atomic_##type* x, ma_##type y) \ { \ - return (ma_##type)ma_atomic_fetch_add_##c89TypeExtension(&x->value, y); \ + return (ma_##type)c89atomic_fetch_add_##c89TypeExtension(&x->value, y); \ } \ static MA_INLINE ma_##type ma_atomic_##type##_fetch_sub(ma_atomic_##type* x, ma_##type y) \ { \ - return (ma_##type)ma_atomic_fetch_sub_##c89TypeExtension(&x->value, y); \ + return (ma_##type)c89atomic_fetch_sub_##c89TypeExtension(&x->value, y); \ } \ static MA_INLINE ma_##type ma_atomic_##type##_fetch_or(ma_atomic_##type* x, ma_##type y) \ { \ - return (ma_##type)ma_atomic_fetch_or_##c89TypeExtension(&x->value, y); \ + return (ma_##type)c89atomic_fetch_or_##c89TypeExtension(&x->value, y); \ } \ static MA_INLINE ma_##type ma_atomic_##type##_fetch_xor(ma_atomic_##type* x, ma_##type y) \ { \ - return (ma_##type)ma_atomic_fetch_xor_##c89TypeExtension(&x->value, y); \ + return (ma_##type)c89atomic_fetch_xor_##c89TypeExtension(&x->value, y); \ } \ static MA_INLINE ma_##type ma_atomic_##type##_fetch_and(ma_atomic_##type* x, ma_##type y) \ { \ - return (ma_##type)ma_atomic_fetch_and_##c89TypeExtension(&x->value, y); \ + return (ma_##type)c89atomic_fetch_and_##c89TypeExtension(&x->value, y); \ } \ static MA_INLINE ma_##type ma_atomic_##type##_compare_and_swap(ma_atomic_##type* x, ma_##type expected, ma_##type desired) \ { \ - return (ma_##type)ma_atomic_compare_and_swap_##c89TypeExtension(&x->value, expected, desired); \ + return (ma_##type)c89atomic_compare_and_swap_##c89TypeExtension(&x->value, expected, desired); \ } \ #define MA_ATOMIC_SAFE_TYPE_IMPL_PTR(type) \ static MA_INLINE ma_##type* ma_atomic_ptr_##type##_get(ma_atomic_ptr_##type* x) \ { \ - return ma_atomic_load_ptr((void**)&x->value); \ + return c89atomic_load_ptr((void**)&x->value); \ } \ static MA_INLINE void ma_atomic_ptr_##type##_set(ma_atomic_ptr_##type* x, ma_##type* value) \ { \ - ma_atomic_store_ptr((void**)&x->value, (void*)value); \ + c89atomic_store_ptr((void**)&x->value, (void*)value); \ } \ static MA_INLINE ma_##type* ma_atomic_ptr_##type##_exchange(ma_atomic_ptr_##type* x, ma_##type* value) \ { \ - return ma_atomic_exchange_ptr((void**)&x->value, (void*)value); \ + return c89atomic_exchange_ptr((void**)&x->value, (void*)value); \ } \ static MA_INLINE ma_bool32 ma_atomic_ptr_##type##_compare_exchange(ma_atomic_ptr_##type* x, ma_##type** expected, ma_##type* desired) \ { \ - return ma_atomic_compare_exchange_weak_ptr((void**)&x->value, (void*)expected, (void*)desired); \ + return c89atomic_compare_exchange_weak_ptr((void**)&x->value, (void*)expected, (void*)desired); \ } \ static MA_INLINE ma_##type* ma_atomic_ptr_##type##_compare_and_swap(ma_atomic_ptr_##type* x, ma_##type* expected, ma_##type* desired) \ { \ - return (ma_##type*)ma_atomic_compare_and_swap_ptr((void**)&x->value, (void*)expected, (void*)desired); \ + return (ma_##type*)c89atomic_compare_and_swap_ptr((void**)&x->value, (void*)expected, (void*)desired); \ } \ MA_ATOMIC_SAFE_TYPE_IMPL(32, uint32) @@ -16124,11 +16031,11 @@ static MA_INLINE ma_result ma_spinlock_lock_ex(volatile ma_spinlock* pSpinlock, } for (;;) { - if (ma_atomic_exchange_explicit_32(pSpinlock, 1, ma_atomic_memory_order_acquire) == 0) { + if (c89atomic_exchange_explicit_32(pSpinlock, 1, c89atomic_memory_order_acquire) == 0) { break; } - while (ma_atomic_load_explicit_32(pSpinlock, ma_atomic_memory_order_relaxed) == 1) { + while (c89atomic_load_explicit_32(pSpinlock, c89atomic_memory_order_relaxed) == 1) { if (yield) { ma_yield(); } @@ -16154,7 +16061,7 @@ MA_API ma_result ma_spinlock_unlock(volatile ma_spinlock* pSpinlock) return MA_INVALID_ARGS; } - ma_atomic_store_explicit_32(pSpinlock, 0, ma_atomic_memory_order_release); + c89atomic_store_explicit_32(pSpinlock, 0, c89atomic_memory_order_release); return MA_SUCCESS; } @@ -16176,7 +16083,7 @@ static ma_result ma_thread_create__posix(ma_thread* pThread, ma_thread_priority int result; pthread_attr_t* pAttr = NULL; -#if !defined(__EMSCRIPTEN__) && !defined(__3DS__) +#if !defined(__EMSCRIPTEN__) /* Try setting the thread priority. It's not critical if anything fails here. */ pthread_attr_t attr; if (pthread_attr_init(&attr) == 0) { @@ -16222,34 +16129,19 @@ static ma_result ma_thread_create__posix(ma_thread* pThread, ma_thread_priority if (priority == ma_thread_priority_idle) { sched.sched_priority = priorityMin; } else if (priority == ma_thread_priority_realtime) { - #if defined(MA_PTHREAD_REALTIME_THREAD_PRIORITY) - { - sched.sched_priority = MA_PTHREAD_REALTIME_THREAD_PRIORITY; + sched.sched_priority = priorityMax; + } else { + sched.sched_priority += ((int)priority + 5) * priorityStep; /* +5 because the lowest priority is -5. */ + if (sched.sched_priority < priorityMin) { + sched.sched_priority = priorityMin; } - #else - { + if (sched.sched_priority > priorityMax) { sched.sched_priority = priorityMax; } - #endif - } else { - sched.sched_priority += ((int)priority + 5) * priorityStep; /* +5 because the lowest priority is -5. */ - } - - if (sched.sched_priority < priorityMin) { - sched.sched_priority = priorityMin; - } - if (sched.sched_priority > priorityMax) { - sched.sched_priority = priorityMax; } - /* I'm not treating a failure of setting the priority as a critical error so not aborting on failure here. */ - if (pthread_attr_setschedparam(&attr, &sched) == 0) { - #if !defined(MA_ANDROID) || (defined(__ANDROID_API__) && __ANDROID_API__ >= 28) - { - pthread_attr_setinheritsched(&attr, PTHREAD_EXPLICIT_SCHED); - } - #endif - } + /* I'm not treating a failure of setting the priority as a critical error so not checking the return value here. */ + pthread_attr_setschedparam(&attr, &sched); } } } @@ -16281,15 +16173,7 @@ static void ma_thread_wait__posix(ma_thread* pThread) static ma_result ma_mutex_init__posix(ma_mutex* pMutex) { - int result; - - if (pMutex == NULL) { - return MA_INVALID_ARGS; - } - - MA_ZERO_OBJECT(pMutex); - - result = pthread_mutex_init((pthread_mutex_t*)pMutex, NULL); + int result = pthread_mutex_init((pthread_mutex_t*)pMutex, NULL); if (result != 0) { return ma_result_from_errno(result); } @@ -16924,7 +16808,7 @@ MA_API ma_result ma_fence_acquire(ma_fence* pFence) } for (;;) { - ma_uint32 oldCounter = ma_atomic_load_32(&pFence->counter); + ma_uint32 oldCounter = c89atomic_load_32(&pFence->counter); ma_uint32 newCounter = oldCounter + 1; /* Make sure we're not about to exceed our maximum value. */ @@ -16933,7 +16817,7 @@ MA_API ma_result ma_fence_acquire(ma_fence* pFence) return MA_OUT_OF_RANGE; } - if (ma_atomic_compare_exchange_weak_32(&pFence->counter, &oldCounter, newCounter)) { + if (c89atomic_compare_exchange_weak_32(&pFence->counter, &oldCounter, newCounter)) { return MA_SUCCESS; } else { if (oldCounter == MA_FENCE_COUNTER_MAX) { @@ -16954,7 +16838,7 @@ MA_API ma_result ma_fence_release(ma_fence* pFence) } for (;;) { - ma_uint32 oldCounter = ma_atomic_load_32(&pFence->counter); + ma_uint32 oldCounter = c89atomic_load_32(&pFence->counter); ma_uint32 newCounter = oldCounter - 1; if (oldCounter == 0) { @@ -16962,7 +16846,7 @@ MA_API ma_result ma_fence_release(ma_fence* pFence) return MA_INVALID_OPERATION; /* Acquire/release mismatch. */ } - if (ma_atomic_compare_exchange_weak_32(&pFence->counter, &oldCounter, newCounter)) { + if (c89atomic_compare_exchange_weak_32(&pFence->counter, &oldCounter, newCounter)) { #ifndef MA_NO_THREADING { if (newCounter == 0) { @@ -16993,7 +16877,7 @@ MA_API ma_result ma_fence_wait(ma_fence* pFence) for (;;) { ma_uint32 counter; - counter = ma_atomic_load_32(&pFence->counter); + counter = c89atomic_load_32(&pFence->counter); if (counter == 0) { /* Counter has hit zero. By the time we get here some other thread may have acquired the @@ -17326,7 +17210,7 @@ MA_API ma_result ma_slot_allocator_alloc(ma_slot_allocator* pAllocator, ma_uint6 ma_uint32 newBitfield; ma_uint32 bitOffset; - oldBitfield = ma_atomic_load_32(&pAllocator->pGroups[iGroup].bitfield); /* <-- This copy must happen. The compiler must not optimize this away. */ + oldBitfield = c89atomic_load_32(&pAllocator->pGroups[iGroup].bitfield); /* <-- This copy must happen. The compiler must not optimize this away. */ /* Fast check to see if anything is available. */ if (oldBitfield == 0xFFFFFFFF) { @@ -17338,11 +17222,11 @@ MA_API ma_result ma_slot_allocator_alloc(ma_slot_allocator* pAllocator, ma_uint6 newBitfield = oldBitfield | (1 << bitOffset); - if (ma_atomic_compare_and_swap_32(&pAllocator->pGroups[iGroup].bitfield, oldBitfield, newBitfield) == oldBitfield) { + if (c89atomic_compare_and_swap_32(&pAllocator->pGroups[iGroup].bitfield, oldBitfield, newBitfield) == oldBitfield) { ma_uint32 slotIndex; /* Increment the counter as soon as possible to have other threads report out-of-memory sooner than later. */ - ma_atomic_fetch_add_32(&pAllocator->count, 1); + c89atomic_fetch_add_32(&pAllocator->count, 1); /* The slot index is required for constructing the output value. */ slotIndex = (iGroup << 5) + bitOffset; /* iGroup << 5 = iGroup * 32 */ @@ -17391,12 +17275,12 @@ MA_API ma_result ma_slot_allocator_free(ma_slot_allocator* pAllocator, ma_uint64 MA_ASSERT(iBit < 32); /* This must be true due to the logic we used to actually calculate it. */ - while (ma_atomic_load_32(&pAllocator->count) > 0) { + while (c89atomic_load_32(&pAllocator->count) > 0) { /* CAS */ ma_uint32 oldBitfield; ma_uint32 newBitfield; - oldBitfield = ma_atomic_load_32(&pAllocator->pGroups[iGroup].bitfield); /* <-- This copy must happen. The compiler must not optimize this away. */ + oldBitfield = c89atomic_load_32(&pAllocator->pGroups[iGroup].bitfield); /* <-- This copy must happen. The compiler must not optimize this away. */ newBitfield = oldBitfield & ~(1 << iBit); /* Debugging for checking for double-frees. */ @@ -17408,8 +17292,8 @@ MA_API ma_result ma_slot_allocator_free(ma_slot_allocator* pAllocator, ma_uint64 } #endif - if (ma_atomic_compare_and_swap_32(&pAllocator->pGroups[iGroup].bitfield, oldBitfield, newBitfield) == oldBitfield) { - ma_atomic_fetch_sub_32(&pAllocator->count, 1); + if (c89atomic_compare_and_swap_32(&pAllocator->pGroups[iGroup].bitfield, oldBitfield, newBitfield) == oldBitfield) { + c89atomic_fetch_sub_32(&pAllocator->count, 1); return MA_SUCCESS; } } @@ -17501,7 +17385,7 @@ static ma_job_proc g_jobVTable[MA_JOB_TYPE_COUNT] = /* Device. */ #if !defined(MA_NO_DEVICE_IO) - ma_job_process__device__aaudio_reroute /* MA_JOB_TYPE_DEVICE_AAUDIO_REROUTE */ + ma_job_process__device__aaudio_reroute /*MA_JOB_TYPE_DEVICE_AAUDIO_REROUTE*/ #endif }; @@ -17740,7 +17624,7 @@ MA_API void ma_job_queue_uninit(ma_job_queue* pQueue, const ma_allocation_callba static ma_bool32 ma_job_queue_cas(volatile ma_uint64* dst, ma_uint64 expected, ma_uint64 desired) { /* The new counter is taken from the expected value. */ - return ma_atomic_compare_and_swap_64(dst, expected, ma_job_set_refcount(desired, ma_job_extract_refcount(expected) + 1)) == expected; + return c89atomic_compare_and_swap_64(dst, expected, ma_job_set_refcount(desired, ma_job_extract_refcount(expected) + 1)) == expected; } MA_API ma_result ma_job_queue_post(ma_job_queue* pQueue, const ma_job* pJob) @@ -17778,10 +17662,10 @@ MA_API ma_result ma_job_queue_post(ma_job_queue* pQueue, const ma_job* pJob) { /* The job is stored in memory so now we need to add it to our linked list. We only ever add items to the end of the list. */ for (;;) { - tail = ma_atomic_load_64(&pQueue->tail); - next = ma_atomic_load_64(&pQueue->pJobs[ma_job_extract_slot(tail)].next); + tail = c89atomic_load_64(&pQueue->tail); + next = c89atomic_load_64(&pQueue->pJobs[ma_job_extract_slot(tail)].next); - if (ma_job_toc_to_allocation(tail) == ma_job_toc_to_allocation(ma_atomic_load_64(&pQueue->tail))) { + if (ma_job_toc_to_allocation(tail) == ma_job_toc_to_allocation(c89atomic_load_64(&pQueue->tail))) { if (ma_job_extract_slot(next) == 0xFFFF) { if (ma_job_queue_cas(&pQueue->pJobs[ma_job_extract_slot(tail)].next, next, slot)) { break; @@ -17846,17 +17730,17 @@ MA_API ma_result ma_job_queue_next(ma_job_queue* pQueue, ma_job* pJob) is stored. One thread can fall through to the freeing of this item while another is still using "head" for the retrieval of the "next" variable. - The slot allocator might need to make use of some reference counting to ensure it's only truly freed when + The slot allocator might need to make use of some reference counting to ensure it's only truely freed when there are no more references to the item. This must be fixed before removing these locks. */ /* Now we need to remove the root item from the list. */ for (;;) { - head = ma_atomic_load_64(&pQueue->head); - tail = ma_atomic_load_64(&pQueue->tail); - next = ma_atomic_load_64(&pQueue->pJobs[ma_job_extract_slot(head)].next); + head = c89atomic_load_64(&pQueue->head); + tail = c89atomic_load_64(&pQueue->tail); + next = c89atomic_load_64(&pQueue->pJobs[ma_job_extract_slot(head)].next); - if (ma_job_toc_to_allocation(head) == ma_job_toc_to_allocation(ma_atomic_load_64(&pQueue->head))) { + if (ma_job_toc_to_allocation(head) == ma_job_toc_to_allocation(c89atomic_load_64(&pQueue->head))) { if (ma_job_extract_slot(head) == ma_job_extract_slot(tail)) { if (ma_job_extract_slot(next) == 0xFFFF) { #ifndef MA_USE_EXPERIMENTAL_LOCK_FREE_JOB_QUEUE @@ -17895,121 +17779,6 @@ MA_API ma_result ma_job_queue_next(ma_job_queue* pQueue, ma_job* pJob) -/******************************************************************************* - -Dynamic Linking - -*******************************************************************************/ -#ifdef MA_POSIX - /* No need for dlfcn.h if we're not using runtime linking. */ - #ifndef MA_NO_RUNTIME_LINKING - #include - #endif -#endif - -MA_API ma_handle ma_dlopen(ma_log* pLog, const char* filename) -{ -#ifndef MA_NO_RUNTIME_LINKING - ma_handle handle; - - ma_log_postf(pLog, MA_LOG_LEVEL_DEBUG, "Loading library: %s\n", filename); - - #ifdef MA_WIN32 - /* From MSDN: Desktop applications cannot use LoadPackagedLibrary; if a desktop application calls this function it fails with APPMODEL_ERROR_NO_PACKAGE.*/ - #if !defined(MA_WIN32_UWP) || !(defined(WINAPI_FAMILY) && ((defined(WINAPI_FAMILY_PHONE_APP) && WINAPI_FAMILY == WINAPI_FAMILY_PHONE_APP))) - handle = (ma_handle)LoadLibraryA(filename); - #else - /* *sigh* It appears there is no ANSI version of LoadPackagedLibrary()... */ - WCHAR filenameW[4096]; - if (MultiByteToWideChar(CP_UTF8, 0, filename, -1, filenameW, sizeof(filenameW)) == 0) { - handle = NULL; - } else { - handle = (ma_handle)LoadPackagedLibrary(filenameW, 0); - } - #endif - #else - handle = (ma_handle)dlopen(filename, RTLD_NOW); - #endif - - /* - I'm not considering failure to load a library an error nor a warning because seamlessly falling through to a lower-priority - backend is a deliberate design choice. Instead I'm logging it as an informational message. - */ - if (handle == NULL) { - ma_log_postf(pLog, MA_LOG_LEVEL_INFO, "Failed to load library: %s\n", filename); - } - - return handle; -#else - /* Runtime linking is disabled. */ - (void)pLog; - (void)filename; - return NULL; -#endif -} - -MA_API void ma_dlclose(ma_log* pLog, ma_handle handle) -{ -#ifndef MA_NO_RUNTIME_LINKING - #ifdef MA_WIN32 - FreeLibrary((HMODULE)handle); - #else - /* Hack for Android bug (see https://github.com/android/ndk/issues/360). Calling dlclose() pre-API 28 may segfault. */ - #if !defined(MA_ANDROID) || (defined(__ANDROID_API__) && __ANDROID_API__ >= 28) - { - dlclose((void*)handle); - } - #else - { - (void)handle; - } - #endif - #endif - - (void)pLog; -#else - /* Runtime linking is disabled. */ - (void)pLog; - (void)handle; -#endif -} - -MA_API ma_proc ma_dlsym(ma_log* pLog, ma_handle handle, const char* symbol) -{ -#ifndef MA_NO_RUNTIME_LINKING - ma_proc proc; - - ma_log_postf(pLog, MA_LOG_LEVEL_DEBUG, "Loading symbol: %s\n", symbol); - -#ifdef _WIN32 - proc = (ma_proc)GetProcAddress((HMODULE)handle, symbol); -#else -#if (defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 8))) || defined(__clang__) - #pragma GCC diagnostic push - #pragma GCC diagnostic ignored "-Wpedantic" -#endif - proc = (ma_proc)dlsym((void*)handle, symbol); -#if (defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 8))) || defined(__clang__) - #pragma GCC diagnostic pop -#endif -#endif - - if (proc == NULL) { - ma_log_postf(pLog, MA_LOG_LEVEL_WARNING, "Failed to load symbol: %s\n", symbol); - } - - (void)pLog; /* It's possible for pContext to be unused. */ - return proc; -#else - /* Runtime linking is disabled. */ - (void)pLog; - (void)handle; - (void)symbol; - return NULL; -#endif -} - - /************************************************************************************************************************************************************ ************************************************************************************************************************************************************* @@ -18027,13 +17796,9 @@ DEVICE I/O #endif #endif -#ifdef MA_APPLE - #include -#endif - #ifndef MA_NO_DEVICE_IO -#if defined(MA_APPLE) && (MAC_OS_X_VERSION_MIN_REQUIRED < 101200) +#if defined(MA_APPLE) && (__MAC_OS_X_VERSION_MIN_REQUIRED < 101200) #include /* For mach_absolute_time() */ #endif @@ -18047,10 +17812,6 @@ DEVICE I/O #endif #endif -/* This must be set to at least 26. */ -#ifndef MA_AAUDIO_MIN_ANDROID_SDK_VERSION -#define MA_AAUDIO_MIN_ANDROID_SDK_VERSION 27 -#endif MA_API void ma_device_info_add_native_data_format(ma_device_info* pDeviceInfo, ma_format format, ma_uint32 channels, ma_uint32 sampleRate, ma_uint32 flags) @@ -18197,7 +17958,7 @@ MA_API ma_bool32 ma_is_backend_enabled(ma_backend backend) #if defined(MA_HAS_AAUDIO) #if defined(MA_ANDROID) { - return ma_android_sdk_version() >= MA_AAUDIO_MIN_ANDROID_SDK_VERSION; + return ma_android_sdk_version() >= 26; } #else return MA_FALSE; @@ -18514,6 +18275,7 @@ typedef LONG (WINAPI * MA_PFN_RegCloseKey)(HKEY hKey); typedef LONG (WINAPI * MA_PFN_RegQueryValueExA)(HKEY hKey, const char* lpValueName, DWORD* lpReserved, DWORD* lpType, BYTE* lpData, DWORD* lpcbData); #endif /* MA_WIN32_DESKTOP */ + MA_API size_t ma_strlen_WCHAR(const WCHAR* str) { size_t len = 0; @@ -18577,7 +18339,7 @@ Timing *******************************************************************************/ #if defined(MA_WIN32) && !defined(MA_POSIX) static LARGE_INTEGER g_ma_TimerFrequency; /* <-- Initialized to zero since it's static. */ - static void ma_timer_init(ma_timer* pTimer) + void ma_timer_init(ma_timer* pTimer) { LARGE_INTEGER counter; @@ -18589,7 +18351,7 @@ Timing pTimer->counter = counter.QuadPart; } - static double ma_timer_get_time_in_seconds(ma_timer* pTimer) + double ma_timer_get_time_in_seconds(ma_timer* pTimer) { LARGE_INTEGER counter; if (!QueryPerformanceCounter(&counter)) { @@ -18598,7 +18360,7 @@ Timing return (double)(counter.QuadPart - pTimer->counter) / g_ma_TimerFrequency.QuadPart; } -#elif defined(MA_APPLE) && (MAC_OS_X_VERSION_MIN_REQUIRED < 101200) +#elif defined(MA_APPLE) && (__MAC_OS_X_VERSION_MIN_REQUIRED < 101200) static ma_uint64 g_ma_TimerFrequency = 0; static void ma_timer_init(ma_timer* pTimer) { @@ -18681,6 +18443,105 @@ Timing #endif +/******************************************************************************* + +Dynamic Linking + +*******************************************************************************/ +MA_API ma_handle ma_dlopen(ma_context* pContext, const char* filename) +{ +#ifndef MA_NO_RUNTIME_LINKING + ma_handle handle; + + ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_DEBUG, "Loading library: %s\n", filename); + +#ifdef _WIN32 + /* From MSDN: Desktop applications cannot use LoadPackagedLibrary; if a desktop application calls this function it fails with APPMODEL_ERROR_NO_PACKAGE.*/ + #if !defined(WINAPI_FAMILY) || (defined(WINAPI_FAMILY) && (defined(WINAPI_FAMILY_DESKTOP_APP) && WINAPI_FAMILY == WINAPI_FAMILY_DESKTOP_APP)) + handle = (ma_handle)LoadLibraryA(filename); + #else + /* *sigh* It appears there is no ANSI version of LoadPackagedLibrary()... */ + WCHAR filenameW[4096]; + if (MultiByteToWideChar(CP_UTF8, 0, filename, -1, filenameW, sizeof(filenameW)) == 0) { + handle = NULL; + } else { + handle = (ma_handle)LoadPackagedLibrary(filenameW, 0); + } + #endif +#else + handle = (ma_handle)dlopen(filename, RTLD_NOW); +#endif + + /* + I'm not considering failure to load a library an error nor a warning because seamlessly falling through to a lower-priority + backend is a deliberate design choice. Instead I'm logging it as an informational message. + */ + if (handle == NULL) { + ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_INFO, "Failed to load library: %s\n", filename); + } + + (void)pContext; /* It's possible for pContext to be unused. */ + return handle; +#else + /* Runtime linking is disabled. */ + (void)pContext; + (void)filename; + return NULL; +#endif +} + +MA_API void ma_dlclose(ma_context* pContext, ma_handle handle) +{ +#ifndef MA_NO_RUNTIME_LINKING +#ifdef _WIN32 + FreeLibrary((HMODULE)handle); +#else + dlclose((void*)handle); +#endif + + (void)pContext; +#else + /* Runtime linking is disabled. */ + (void)pContext; + (void)handle; +#endif +} + +MA_API ma_proc ma_dlsym(ma_context* pContext, ma_handle handle, const char* symbol) +{ +#ifndef MA_NO_RUNTIME_LINKING + ma_proc proc; + + ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_DEBUG, "Loading symbol: %s\n", symbol); + +#ifdef _WIN32 + proc = (ma_proc)GetProcAddress((HMODULE)handle, symbol); +#else +#if defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 8)) + #pragma GCC diagnostic push + #pragma GCC diagnostic ignored "-Wpedantic" +#endif + proc = (ma_proc)dlsym((void*)handle, symbol); +#if defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 8)) + #pragma GCC diagnostic pop +#endif +#endif + + if (proc == NULL) { + ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_WARNING, "Failed to load symbol: %s\n", symbol); + } + + (void)pContext; /* It's possible for pContext to be unused. */ + return proc; +#else + /* Runtime linking is disabled. */ + (void)pContext; + (void)handle; + (void)symbol; + return NULL; +#endif +} + #if 0 static ma_uint32 ma_get_closest_standard_sample_rate(ma_uint32 sampleRateIn) @@ -18762,36 +18623,30 @@ static void ma_device__on_notification(ma_device_notification notification) } } -static void ma_device__on_notification_started(ma_device* pDevice) +void ma_device__on_notification_started(ma_device* pDevice) { ma_device__on_notification(ma_device_notification_init(pDevice, ma_device_notification_type_started)); } -static void ma_device__on_notification_stopped(ma_device* pDevice) +void ma_device__on_notification_stopped(ma_device* pDevice) { ma_device__on_notification(ma_device_notification_init(pDevice, ma_device_notification_type_stopped)); } -/* Not all platforms support reroute notifications. */ -#if !defined(MA_EMSCRIPTEN) -static void ma_device__on_notification_rerouted(ma_device* pDevice) +void ma_device__on_notification_rerouted(ma_device* pDevice) { ma_device__on_notification(ma_device_notification_init(pDevice, ma_device_notification_type_rerouted)); } -#endif -#if defined(MA_EMSCRIPTEN) -#ifdef __cplusplus -extern "C" { -#endif -void EMSCRIPTEN_KEEPALIVE ma_device__on_notification_unlocked(ma_device* pDevice) +void ma_device__on_notification_interruption_began(ma_device* pDevice) { - ma_device__on_notification(ma_device_notification_init(pDevice, ma_device_notification_type_unlocked)); + ma_device__on_notification(ma_device_notification_init(pDevice, ma_device_notification_type_interruption_began)); } -#ifdef __cplusplus + +void ma_device__on_notification_interruption_ended(ma_device* pDevice) +{ + ma_device__on_notification(ma_device_notification_init(pDevice, ma_device_notification_type_interruption_ended)); } -#endif -#endif static void ma_device__on_data_inner(ma_device* pDevice, void* pFramesOut, const void* pFramesIn, ma_uint32 frameCount) @@ -18918,7 +18773,7 @@ static void ma_device__handle_data_callback(ma_device* pDevice, void* pFramesOut unsigned int prevDenormalState = ma_device_disable_denormals(pDevice); { /* Volume control of input makes things a bit awkward because the input buffer is read-only. We'll need to use a temp buffer and loop in this case. */ - if (pFramesIn != NULL && masterVolumeFactor != 1) { + if (pFramesIn != NULL && masterVolumeFactor < 1) { ma_uint8 tempFramesIn[MA_DATA_CONVERTER_STACK_BUFFER_SIZE]; ma_uint32 bpfCapture = ma_get_bytes_per_frame(pDevice->capture.format, pDevice->capture.channels); ma_uint32 bpfPlayback = ma_get_bytes_per_frame(pDevice->playback.format, pDevice->playback.channels); @@ -18941,7 +18796,7 @@ static void ma_device__handle_data_callback(ma_device* pDevice, void* pFramesOut /* Volume control and clipping for playback devices. */ if (pFramesOut != NULL) { - if (masterVolumeFactor != 1) { + if (masterVolumeFactor < 1) { if (pFramesIn == NULL) { /* <-- In full-duplex situations, the volume will have been applied to the input samples before the data callback. Applying it again post-callback will incorrectly compound it. */ ma_apply_volume_factor_pcm_frames(pFramesOut, frameCount, pDevice->playback.format, pDevice->playback.channels, masterVolumeFactor); } @@ -18953,11 +18808,6 @@ static void ma_device__handle_data_callback(ma_device* pDevice, void* pFramesOut } } ma_device_restore_denormals(pDevice, prevDenormalState); - } else { - /* No data callback. Just silence the output. */ - if (pFramesOut != NULL) { - ma_silence_pcm_frames(pFramesOut, frameCount, pDevice->playback.format, pDevice->playback.channels); - } } } @@ -19043,7 +18893,9 @@ static void ma_device__read_frames_from_client(ma_device* pDevice, ma_uint32 fra framesToReadThisIterationIn = requiredInputFrameCount; } - ma_device__handle_data_callback(pDevice, pIntermediaryBuffer, NULL, (ma_uint32)framesToReadThisIterationIn); + if (framesToReadThisIterationIn > 0) { + ma_device__handle_data_callback(pDevice, pIntermediaryBuffer, NULL, (ma_uint32)framesToReadThisIterationIn); + } /* At this point we have our decoded data in input format and now we need to convert to output format. Note that even if we didn't read any @@ -19084,7 +18936,7 @@ static void ma_device__send_frames_to_client(ma_device* pDevice, ma_uint32 frame ma_uint64 totalClientFramesProcessed = 0; const void* pRunningFramesInDeviceFormat = pFramesInDeviceFormat; - /* We just keep going until we've exhausted all of our input frames and cannot generate any more output frames. */ + /* We just keep going until we've exhaused all of our input frames and cannot generate any more output frames. */ for (;;) { ma_uint64 deviceFramesProcessedThisIteration; ma_uint64 clientFramesProcessedThisIteration; @@ -19249,10 +19101,10 @@ static MA_INLINE void ma_device__set_state(ma_device* pDevice, ma_device_state n #if defined(MA_WIN32) - static GUID MA_GUID_KSDATAFORMAT_SUBTYPE_PCM = {0x00000001, 0x0000, 0x0010, {0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71}}; - static GUID MA_GUID_KSDATAFORMAT_SUBTYPE_IEEE_FLOAT = {0x00000003, 0x0000, 0x0010, {0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71}}; - /*static GUID MA_GUID_KSDATAFORMAT_SUBTYPE_ALAW = {0x00000006, 0x0000, 0x0010, {0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71}};*/ - /*static GUID MA_GUID_KSDATAFORMAT_SUBTYPE_MULAW = {0x00000007, 0x0000, 0x0010, {0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71}};*/ + GUID MA_GUID_KSDATAFORMAT_SUBTYPE_PCM = {0x00000001, 0x0000, 0x0010, {0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71}}; + GUID MA_GUID_KSDATAFORMAT_SUBTYPE_IEEE_FLOAT = {0x00000003, 0x0000, 0x0010, {0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71}}; + /*GUID MA_GUID_KSDATAFORMAT_SUBTYPE_ALAW = {0x00000006, 0x0000, 0x0010, {0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71}};*/ + /*GUID MA_GUID_KSDATAFORMAT_SUBTYPE_MULAW = {0x00000007, 0x0000, 0x0010, {0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71}};*/ #endif @@ -19367,7 +19219,7 @@ static ma_result ma_device_audio_thread__default_read_write(ma_device* pDevice) } /* - If we weren't able to generate any output frames it must mean we've exhausted all of our input. The only time this would not be the case is if capturedClientData was too small + If we weren't able to generate any output frames it must mean we've exhaused all of our input. The only time this would not be the case is if capturedClientData was too small which should never be the case when it's of the size MA_DATA_CONVERTER_STACK_BUFFER_SIZE. */ if (capturedClientFramesToProcessThisIteration == 0) { @@ -19570,7 +19422,7 @@ static ma_result ma_device_do_operation__null(ma_device* pDevice, ma_uint32 oper /* The first thing to do is wait for an operation slot to become available. We only have a single slot for this, but we could extend this later - to support queuing of operations. + to support queing of operations. */ result = ma_semaphore_wait(&pDevice->null_device.operationSemaphore); if (result != MA_SUCCESS) { @@ -19968,7 +19820,7 @@ WIN32 COMMON *******************************************************************************/ #if defined(MA_WIN32) -#if defined(MA_WIN32_DESKTOP) || defined(MA_WIN32_GDK) +#if defined(MA_WIN32_DESKTOP) #define ma_CoInitializeEx(pContext, pvReserved, dwCoInit) ((pContext->win32.CoInitializeEx) ? ((MA_PFN_CoInitializeEx)pContext->win32.CoInitializeEx)(pvReserved, dwCoInit) : ((MA_PFN_CoInitialize)pContext->win32.CoInitialize)(pvReserved)) #define ma_CoUninitialize(pContext) ((MA_PFN_CoUninitialize)pContext->win32.CoUninitialize)() #define ma_CoCreateInstance(pContext, rclsid, pUnkOuter, dwClsContext, riid, ppv) ((MA_PFN_CoCreateInstance)pContext->win32.CoCreateInstance)(rclsid, pUnkOuter, dwClsContext, riid, ppv) @@ -20824,12 +20676,12 @@ static HRESULT STDMETHODCALLTYPE ma_completion_handler_uwp_QueryInterface(ma_com static ULONG STDMETHODCALLTYPE ma_completion_handler_uwp_AddRef(ma_completion_handler_uwp* pThis) { - return (ULONG)ma_atomic_fetch_add_32(&pThis->counter, 1) + 1; + return (ULONG)c89atomic_fetch_add_32(&pThis->counter, 1) + 1; } static ULONG STDMETHODCALLTYPE ma_completion_handler_uwp_Release(ma_completion_handler_uwp* pThis) { - ma_uint32 newRefCount = ma_atomic_fetch_sub_32(&pThis->counter, 1) - 1; + ma_uint32 newRefCount = c89atomic_fetch_sub_32(&pThis->counter, 1) - 1; if (newRefCount == 0) { return 0; /* We don't free anything here because we never allocate the object on the heap. */ } @@ -20901,12 +20753,12 @@ static HRESULT STDMETHODCALLTYPE ma_IMMNotificationClient_QueryInterface(ma_IMMN static ULONG STDMETHODCALLTYPE ma_IMMNotificationClient_AddRef(ma_IMMNotificationClient* pThis) { - return (ULONG)ma_atomic_fetch_add_32(&pThis->counter, 1) + 1; + return (ULONG)c89atomic_fetch_add_32(&pThis->counter, 1) + 1; } static ULONG STDMETHODCALLTYPE ma_IMMNotificationClient_Release(ma_IMMNotificationClient* pThis) { - ma_uint32 newRefCount = ma_atomic_fetch_sub_32(&pThis->counter, 1) - 1; + ma_uint32 newRefCount = c89atomic_fetch_sub_32(&pThis->counter, 1) - 1; if (newRefCount == 0) { return 0; /* We don't free anything here because we never allocate the object on the heap. */ } @@ -21028,9 +20880,13 @@ static HRESULT STDMETHODCALLTYPE ma_IMMNotificationClient_OnDefaultDeviceChanged /*ma_log_postf(ma_device_get_log(pThis->pDevice), MA_LOG_LEVEL_DEBUG, "IMMNotificationClient_OnDefaultDeviceChanged(dataFlow=%d, role=%d, pDefaultDeviceID=%S)\n", dataFlow, role, (pDefaultDeviceID != NULL) ? pDefaultDeviceID : L"(NULL)");*/ #endif - (void)role; + /* We only ever use the eConsole role in miniaudio. */ + if (role != ma_eConsole) { + ma_log_postf(ma_device_get_log(pThis->pDevice), MA_LOG_LEVEL_DEBUG, "[WASAPI] Stream rerouting: role != eConsole\n"); + return S_OK; + } - /* We only care about devices with the same data flow as the current device. */ + /* We only care about devices with the same data flow and role as the current device. */ if ((pThis->pDevice->type == ma_device_type_playback && dataFlow != ma_eRender) || (pThis->pDevice->type == ma_device_type_capture && dataFlow != ma_eCapture) || (pThis->pDevice->type == ma_device_type_loopback && dataFlow != ma_eRender)) { @@ -21387,7 +21243,7 @@ static ma_result ma_context_get_device_info_from_IAudioClient__wasapi(ma_context } /* - Exclusive Mode. We repeatedly call IsFormatSupported() here. This is not currently supported on + Exlcusive Mode. We repeatedly call IsFormatSupported() here. This is not currently supported on UWP. Failure to retrieve the exclusive mode format is not considered an error, so from here on out, MA_SUCCESS is guaranteed to be returned. */ @@ -21592,23 +21448,8 @@ static ma_result ma_context_get_MMDevice__wasapi(ma_context* pContext, ma_device MA_ASSERT(pContext != NULL); MA_ASSERT(ppMMDevice != NULL); - /* - This weird COM init/uninit here is a hack to work around a crash when changing devices. What is happening is - WASAPI fires a callback from another thread when the device is changed. It's from that thread where this - function is getting called. What I'm suspecting is that the other thread is not initializing COM which in turn - results in CoCreateInstance() failing. - - The community has reported that this seems to fix the crash. There are future plans to move all WASAPI operation - over to a single thread to make everything safer, but in the meantime while we wait for that to come online I'm - happy enough to use this hack instead. - */ - ma_CoInitializeEx(pContext, NULL, MA_COINIT_VALUE); - { - hr = ma_CoCreateInstance(pContext, &MA_CLSID_MMDeviceEnumerator, NULL, CLSCTX_ALL, &MA_IID_IMMDeviceEnumerator, (void**)&pDeviceEnumerator); - } - ma_CoUninitialize(pContext); - - if (FAILED(hr)) { /* <-- This is checking the call above to ma_CoCreateInstance(). */ + hr = ma_CoCreateInstance(pContext, &MA_CLSID_MMDeviceEnumerator, NULL, CLSCTX_ALL, &MA_IID_IMMDeviceEnumerator, (void**)&pDeviceEnumerator); + if (FAILED(hr)) { ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to create IMMDeviceEnumerator.\n"); return ma_result_from_HRESULT(hr); } @@ -21640,7 +21481,7 @@ static ma_result ma_context_get_device_id_from_MMDevice__wasapi(ma_context* pCon size_t idlen = ma_strlen_WCHAR(pDeviceIDString); if (idlen+1 > ma_countof(pDeviceID->wasapi)) { ma_CoTaskMemFree(pContext, pDeviceIDString); - MA_ASSERT(MA_FALSE); /* NOTE: If this is triggered, please report it. It means the format of the ID must have changed and is too long to fit in our fixed sized buffer. */ + MA_ASSERT(MA_FALSE); /* NOTE: If this is triggered, please report it. It means the format of the ID must haved change and is too long to fit in our fixed sized buffer. */ return MA_ERROR; } @@ -22084,16 +21925,12 @@ static ma_result ma_device_uninit__wasapi(ma_device* pDevice) { MA_ASSERT(pDevice != NULL); - #if defined(MA_WIN32_DESKTOP) || defined(MA_WIN32_GDK) - { - if (pDevice->wasapi.pDeviceEnumerator) { - ((ma_IMMDeviceEnumerator*)pDevice->wasapi.pDeviceEnumerator)->lpVtbl->UnregisterEndpointNotificationCallback((ma_IMMDeviceEnumerator*)pDevice->wasapi.pDeviceEnumerator, &pDevice->wasapi.notificationClient); - ma_IMMDeviceEnumerator_Release((ma_IMMDeviceEnumerator*)pDevice->wasapi.pDeviceEnumerator); - } - - ma_mutex_uninit(&pDevice->wasapi.rerouteLock); +#if defined(MA_WIN32_DESKTOP) || defined(MA_WIN32_GDK) + if (pDevice->wasapi.pDeviceEnumerator) { + ((ma_IMMDeviceEnumerator*)pDevice->wasapi.pDeviceEnumerator)->lpVtbl->UnregisterEndpointNotificationCallback((ma_IMMDeviceEnumerator*)pDevice->wasapi.pDeviceEnumerator, &pDevice->wasapi.notificationClient); + ma_IMMDeviceEnumerator_Release((ma_IMMDeviceEnumerator*)pDevice->wasapi.pDeviceEnumerator); } - #endif +#endif if (pDevice->wasapi.pRenderClient) { if (pDevice->wasapi.pMappedBufferPlayback != NULL) { @@ -22315,7 +22152,7 @@ static ma_result ma_device_init_internal__wasapi(ma_context* pContext, ma_device MA_COPY_MEMORY(&wf, pNativeFormat, cbSize); } - + result = MA_SUCCESS; } @@ -22394,7 +22231,7 @@ static ma_result ma_device_init_internal__wasapi(ma_context* pContext, ma_device MA_REFERENCE_TIME bufferDuration = periodDurationInMicroseconds * pData->periodsOut * 10; /* - If the periodicity is too small, Initialize() will fail with AUDCLNT_E_INVALID_DEVICE_PERIOD. In this case we should just keep increasing + If the periodicy is too small, Initialize() will fail with AUDCLNT_E_INVALID_DEVICE_PERIOD. In this case we should just keep increasing it and trying it again. */ hr = E_FAIL; @@ -22404,7 +22241,7 @@ static ma_result ma_device_init_internal__wasapi(ma_context* pContext, ma_device if (bufferDuration > 500*10000) { break; } else { - if (bufferDuration == 0) { /* <-- Just a sanity check to prevent an infinite loop. Should never happen, but it makes me feel better. */ + if (bufferDuration == 0) { /* <-- Just a sanity check to prevent an infinit loop. Should never happen, but it makes me feel better. */ break; } @@ -23094,7 +22931,7 @@ static ma_result ma_device_start__wasapi_nolock(ma_device* pDevice) if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex || pDevice->type == ma_device_type_loopback) { hr = ma_IAudioClient_Start((ma_IAudioClient*)pDevice->wasapi.pAudioClientCapture); if (FAILED(hr)) { - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to start internal capture device. HRESULT = %d.", (int)hr); + ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to start internal capture device."); return ma_result_from_HRESULT(hr); } @@ -23104,7 +22941,7 @@ static ma_result ma_device_start__wasapi_nolock(ma_device* pDevice) if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) { hr = ma_IAudioClient_Start((ma_IAudioClient*)pDevice->wasapi.pAudioClientPlayback); if (FAILED(hr)) { - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to start internal playback device. HRESULT = %d.", (int)hr); + ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to start internal playback device."); return ma_result_from_HRESULT(hr); } @@ -23143,14 +22980,6 @@ static ma_result ma_device_stop__wasapi_nolock(ma_device* pDevice) } if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex || pDevice->type == ma_device_type_loopback) { - /* If we have a mapped buffer we need to release it. */ - if (pDevice->wasapi.pMappedBufferCapture != NULL) { - ma_IAudioCaptureClient_ReleaseBuffer((ma_IAudioCaptureClient*)pDevice->wasapi.pCaptureClient, pDevice->wasapi.mappedBufferCaptureCap); - pDevice->wasapi.pMappedBufferCapture = NULL; - pDevice->wasapi.mappedBufferCaptureCap = 0; - pDevice->wasapi.mappedBufferCaptureLen = 0; - } - hr = ma_IAudioClient_Stop((ma_IAudioClient*)pDevice->wasapi.pAudioClientCapture); if (FAILED(hr)) { ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to stop internal capture device."); @@ -23164,34 +22993,31 @@ static ma_result ma_device_stop__wasapi_nolock(ma_device* pDevice) return ma_result_from_HRESULT(hr); } + /* If we have a mapped buffer we need to release it. */ + if (pDevice->wasapi.pMappedBufferCapture != NULL) { + ma_IAudioCaptureClient_ReleaseBuffer((ma_IAudioCaptureClient*)pDevice->wasapi.pCaptureClient, pDevice->wasapi.mappedBufferCaptureCap); + pDevice->wasapi.pMappedBufferCapture = NULL; + pDevice->wasapi.mappedBufferCaptureCap = 0; + pDevice->wasapi.mappedBufferCaptureLen = 0; + } + ma_atomic_bool32_set(&pDevice->wasapi.isStartedCapture, MA_FALSE); } if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) { - if (pDevice->wasapi.pMappedBufferPlayback != NULL) { - ma_silence_pcm_frames( - ma_offset_pcm_frames_ptr(pDevice->wasapi.pMappedBufferPlayback, pDevice->wasapi.mappedBufferPlaybackLen, pDevice->playback.internalFormat, pDevice->playback.internalChannels), - pDevice->wasapi.mappedBufferPlaybackCap - pDevice->wasapi.mappedBufferPlaybackLen, - pDevice->playback.internalFormat, pDevice->playback.internalChannels - ); - ma_IAudioRenderClient_ReleaseBuffer((ma_IAudioRenderClient*)pDevice->wasapi.pRenderClient, pDevice->wasapi.mappedBufferPlaybackCap, 0); - pDevice->wasapi.pMappedBufferPlayback = NULL; - pDevice->wasapi.mappedBufferPlaybackCap = 0; - pDevice->wasapi.mappedBufferPlaybackLen = 0; - } - /* The buffer needs to be drained before stopping the device. Not doing this will result in the last few frames not getting output to the speakers. This is a problem for very short sounds because it'll result in a significant portion of it not getting played. */ if (ma_atomic_bool32_get(&pDevice->wasapi.isStartedPlayback)) { /* We need to make sure we put a timeout here or else we'll risk getting stuck in a deadlock in some cases. */ - DWORD waitTime = (pDevice->wasapi.actualBufferSizeInFramesPlayback * 1000) / pDevice->playback.internalSampleRate; + DWORD waitTime = pDevice->wasapi.actualBufferSizeInFramesPlayback / pDevice->playback.internalSampleRate; if (pDevice->playback.shareMode == ma_share_mode_exclusive) { WaitForSingleObject((HANDLE)pDevice->wasapi.hEventPlayback, waitTime); - } else { - ma_uint32 prevFramesAvailablePlayback = (ma_uint32)-1; + } + else { + ma_uint32 prevFramesAvaialablePlayback = (ma_uint32)-1; ma_uint32 framesAvailablePlayback; for (;;) { result = ma_device__get_available_frames__wasapi(pDevice, (ma_IAudioClient*)pDevice->wasapi.pAudioClientPlayback, &framesAvailablePlayback); @@ -23207,13 +23033,13 @@ static ma_result ma_device_stop__wasapi_nolock(ma_device* pDevice) Just a safety check to avoid an infinite loop. If this iteration results in a situation where the number of available frames has not changed, get out of the loop. I don't think this should ever happen, but I think it's nice to have just in case. */ - if (framesAvailablePlayback == prevFramesAvailablePlayback) { + if (framesAvailablePlayback == prevFramesAvaialablePlayback) { break; } - prevFramesAvailablePlayback = framesAvailablePlayback; + prevFramesAvaialablePlayback = framesAvailablePlayback; + WaitForSingleObject((HANDLE)pDevice->wasapi.hEventPlayback, waitTime * 1000); ResetEvent((HANDLE)pDevice->wasapi.hEventPlayback); /* Manual reset. */ - WaitForSingleObject((HANDLE)pDevice->wasapi.hEventPlayback, waitTime); } } } @@ -23225,20 +23051,19 @@ static ma_result ma_device_stop__wasapi_nolock(ma_device* pDevice) } /* The audio client needs to be reset otherwise restarting will fail. */ - { - ma_int32 retries = 5; - - while ((hr = ma_IAudioClient_Reset((ma_IAudioClient*)pDevice->wasapi.pAudioClientPlayback)) == MA_AUDCLNT_E_BUFFER_OPERATION_PENDING && retries > 0) { - ma_sleep(10); - retries -= 1; - } - } - + hr = ma_IAudioClient_Reset((ma_IAudioClient*)pDevice->wasapi.pAudioClientPlayback); if (FAILED(hr)) { ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to reset internal playback device."); return ma_result_from_HRESULT(hr); } + if (pDevice->wasapi.pMappedBufferPlayback != NULL) { + ma_IAudioRenderClient_ReleaseBuffer((ma_IAudioRenderClient*)pDevice->wasapi.pRenderClient, pDevice->wasapi.mappedBufferPlaybackCap, 0); + pDevice->wasapi.pMappedBufferPlayback = NULL; + pDevice->wasapi.mappedBufferPlaybackCap = 0; + pDevice->wasapi.mappedBufferPlaybackLen = 0; + } + ma_atomic_bool32_set(&pDevice->wasapi.isStartedPlayback, MA_FALSE); } @@ -23435,7 +23260,7 @@ static ma_result ma_device_read__wasapi(ma_device* pDevice, void* pFrames, ma_ui /* At this point we should be able to loop back to the start of the loop and try retrieving a data buffer again. */ } else { - /* An error occurred and we need to abort. */ + /* An error occured and we need to abort. */ ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to retrieve internal buffer from capture device in preparation for reading from the device. HRESULT = %d. Stopping device.\n", (int)hr); result = ma_result_from_HRESULT(hr); break; @@ -23583,14 +23408,14 @@ static ma_result ma_context_uninit__wasapi(ma_context* pContext) ma_thread_wait(&pContext->wasapi.commandThread); if (pContext->wasapi.hAvrt) { - ma_dlclose(ma_context_get_log(pContext), pContext->wasapi.hAvrt); + ma_dlclose(pContext, pContext->wasapi.hAvrt); pContext->wasapi.hAvrt = NULL; } #if defined(MA_WIN32_UWP) { if (pContext->wasapi.hMMDevapi) { - ma_dlclose(ma_context_get_log(pContext), pContext->wasapi.hMMDevapi); + ma_dlclose(pContext, pContext->wasapi.hMMDevapi); pContext->wasapi.hMMDevapi = NULL; } } @@ -23624,15 +23449,15 @@ static ma_result ma_context_init__wasapi(ma_context* pContext, const ma_context_ ma_PFNVerifyVersionInfoW _VerifyVersionInfoW; ma_PFNVerSetConditionMask _VerSetConditionMask; - kernel32DLL = ma_dlopen(ma_context_get_log(pContext), "kernel32.dll"); + kernel32DLL = ma_dlopen(pContext, "kernel32.dll"); if (kernel32DLL == NULL) { return MA_NO_BACKEND; } - _VerifyVersionInfoW = (ma_PFNVerifyVersionInfoW )ma_dlsym(ma_context_get_log(pContext), kernel32DLL, "VerifyVersionInfoW"); - _VerSetConditionMask = (ma_PFNVerSetConditionMask)ma_dlsym(ma_context_get_log(pContext), kernel32DLL, "VerSetConditionMask"); + _VerifyVersionInfoW = (ma_PFNVerifyVersionInfoW )ma_dlsym(pContext, kernel32DLL, "VerifyVersionInfoW"); + _VerSetConditionMask = (ma_PFNVerSetConditionMask)ma_dlsym(pContext, kernel32DLL, "VerSetConditionMask"); if (_VerifyVersionInfoW == NULL || _VerSetConditionMask == NULL) { - ma_dlclose(ma_context_get_log(pContext), kernel32DLL); + ma_dlclose(pContext, kernel32DLL); return MA_NO_BACKEND; } @@ -23647,7 +23472,7 @@ static ma_result ma_context_init__wasapi(ma_context* pContext, const ma_context_ result = MA_NO_BACKEND; } - ma_dlclose(ma_context_get_log(pContext), kernel32DLL); + ma_dlclose(pContext, kernel32DLL); } #endif @@ -23657,39 +23482,6 @@ static ma_result ma_context_init__wasapi(ma_context* pContext, const ma_context_ MA_ZERO_OBJECT(&pContext->wasapi); - - #if defined(MA_WIN32_UWP) - { - /* Link to mmdevapi so we can get access to ActivateAudioInterfaceAsync(). */ - pContext->wasapi.hMMDevapi = ma_dlopen(ma_context_get_log(pContext), "mmdevapi.dll"); - if (pContext->wasapi.hMMDevapi) { - pContext->wasapi.ActivateAudioInterfaceAsync = ma_dlsym(ma_context_get_log(pContext), pContext->wasapi.hMMDevapi, "ActivateAudioInterfaceAsync"); - if (pContext->wasapi.ActivateAudioInterfaceAsync == NULL) { - ma_dlclose(ma_context_get_log(pContext), pContext->wasapi.hMMDevapi); - return MA_NO_BACKEND; /* ActivateAudioInterfaceAsync() could not be loaded. */ - } - } else { - return MA_NO_BACKEND; /* Failed to load mmdevapi.dll which is required for ActivateAudioInterfaceAsync() */ - } - } - #endif - - /* Optionally use the Avrt API to specify the audio thread's latency sensitivity requirements */ - pContext->wasapi.hAvrt = ma_dlopen(ma_context_get_log(pContext), "avrt.dll"); - if (pContext->wasapi.hAvrt) { - pContext->wasapi.AvSetMmThreadCharacteristicsA = ma_dlsym(ma_context_get_log(pContext), pContext->wasapi.hAvrt, "AvSetMmThreadCharacteristicsA"); - pContext->wasapi.AvRevertMmThreadcharacteristics = ma_dlsym(ma_context_get_log(pContext), pContext->wasapi.hAvrt, "AvRevertMmThreadCharacteristics"); - - /* If either function could not be found, disable use of avrt entirely. */ - if (!pContext->wasapi.AvSetMmThreadCharacteristicsA || !pContext->wasapi.AvRevertMmThreadcharacteristics) { - pContext->wasapi.AvSetMmThreadCharacteristicsA = NULL; - pContext->wasapi.AvRevertMmThreadcharacteristics = NULL; - ma_dlclose(ma_context_get_log(pContext), pContext->wasapi.hAvrt); - pContext->wasapi.hAvrt = NULL; - } - } - - /* Annoyingly, WASAPI does not allow you to release an IAudioClient object from a different thread than the one that retrieved it with GetService(). This can result in a deadlock in two @@ -23733,6 +23525,41 @@ static ma_result ma_context_init__wasapi(ma_context* pContext, const ma_context_ ma_mutex_uninit(&pContext->wasapi.commandLock); return result; } + + #if defined(MA_WIN32_UWP) + { + /* Link to mmdevapi so we can get access to ActivateAudioInterfaceAsync(). */ + pContext->wasapi.hMMDevapi = ma_dlopen(pContext, "mmdevapi.dll"); + if (pContext->wasapi.hMMDevapi) { + pContext->wasapi.ActivateAudioInterfaceAsync = ma_dlsym(pContext, pContext->wasapi.hMMDevapi, "ActivateAudioInterfaceAsync"); + if (pContext->wasapi.ActivateAudioInterfaceAsync == NULL) { + ma_semaphore_uninit(&pContext->wasapi.commandSem); + ma_mutex_uninit(&pContext->wasapi.commandLock); + ma_dlclose(pContext, pContext->wasapi.hMMDevapi); + return MA_NO_BACKEND; /* ActivateAudioInterfaceAsync() could not be loaded. */ + } + } else { + ma_semaphore_uninit(&pContext->wasapi.commandSem); + ma_mutex_uninit(&pContext->wasapi.commandLock); + return MA_NO_BACKEND; /* Failed to load mmdevapi.dll which is required for ActivateAudioInterfaceAsync() */ + } + } + #endif + + /* Optionally use the Avrt API to specify the audio thread's latency sensitivity requirements */ + pContext->wasapi.hAvrt = ma_dlopen(pContext, "avrt.dll"); + if (pContext->wasapi.hAvrt) { + pContext->wasapi.AvSetMmThreadCharacteristicsA = ma_dlsym(pContext, pContext->wasapi.hAvrt, "AvSetMmThreadCharacteristicsA"); + pContext->wasapi.AvRevertMmThreadcharacteristics = ma_dlsym(pContext, pContext->wasapi.hAvrt, "AvRevertMmThreadCharacteristics"); + + /* If either function could not be found, disable use of avrt entirely. */ + if (!pContext->wasapi.AvSetMmThreadCharacteristicsA || !pContext->wasapi.AvRevertMmThreadcharacteristics) { + pContext->wasapi.AvSetMmThreadCharacteristicsA = NULL; + pContext->wasapi.AvRevertMmThreadcharacteristics = NULL; + ma_dlclose(pContext, pContext->wasapi.hAvrt); + pContext->wasapi.hAvrt = NULL; + } + } } @@ -23805,13 +23632,6 @@ DirectSound Backend #define MA_DSBPLAY_TERMINATEBY_DISTANCE 0x00000010 #define MA_DSBPLAY_TERMINATEBY_PRIORITY 0x00000020 -#define MA_DSBSTATUS_PLAYING 0x00000001 -#define MA_DSBSTATUS_BUFFERLOST 0x00000002 -#define MA_DSBSTATUS_LOOPING 0x00000004 -#define MA_DSBSTATUS_LOCHARDWARE 0x00000008 -#define MA_DSBSTATUS_LOCSOFTWARE 0x00000010 -#define MA_DSBSTATUS_TERMINATED 0x00000020 - #define MA_DSCBSTART_LOOPING 0x00000001 typedef struct @@ -24181,12 +24001,9 @@ static ma_result ma_context_create_IDirectSound__dsound(ma_context* pContext, ma } /* The cooperative level must be set before doing anything else. */ - hWnd = (HWND)pContext->dsound.hWnd; + hWnd = ((MA_PFN_GetForegroundWindow)pContext->win32.GetForegroundWindow)(); if (hWnd == 0) { - hWnd = ((MA_PFN_GetForegroundWindow)pContext->win32.GetForegroundWindow)(); - if (hWnd == 0) { - hWnd = ((MA_PFN_GetDesktopWindow)pContext->win32.GetDesktopWindow)(); - } + hWnd = ((MA_PFN_GetDesktopWindow)pContext->win32.GetDesktopWindow)(); } hr = ma_IDirectSound_SetCooperativeLevel(pDirectSound, hWnd, (shareMode == ma_share_mode_exclusive) ? MA_DSSCL_EXCLUSIVE : MA_DSSCL_PRIORITY); @@ -24360,7 +24177,7 @@ static BOOL CALLBACK ma_context_enumerate_devices_callback__dsound(GUID* lpGuid, /* Call the callback function, but make sure we stop enumerating if the callee requested so. */ MA_ASSERT(pData != NULL); - pData->terminated = (pData->callback(pData->pContext, pData->deviceType, &deviceInfo, pData->pUserData) == MA_FALSE); + pData->terminated = !pData->callback(pData->pContext, pData->deviceType, &deviceInfo, pData->pUserData); if (pData->terminated) { return FALSE; /* Stop enumeration. */ } else { @@ -24690,8 +24507,8 @@ static ma_result ma_device_init__dsound(ma_device* pDevice, const ma_device_conf } /* - Unfortunately DirectSound uses different APIs and data structures for playback and capture devices. We need to initialize - the capture device first because we'll want to match its buffer size and period count on the playback side if we're using + Unfortunately DirectSound uses different APIs and data structures for playback and catpure devices. We need to initialize + the capture device first because we'll want to match it's buffer size and period count on the playback side if we're using full-duplex mode. */ if (pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) { @@ -24974,7 +24791,6 @@ static ma_result ma_device_data_loop__dsound(ma_device* pDevice) ma_bool32 isPlaybackDeviceStarted = MA_FALSE; ma_uint32 framesWrittenToPlaybackDevice = 0; /* For knowing whether or not the playback device needs to be started. */ ma_uint32 waitTimeInMilliseconds = 1; - DWORD playbackBufferStatus = 0; MA_ASSERT(pDevice != NULL); @@ -25303,20 +25119,6 @@ static ma_result ma_device_data_loop__dsound(ma_device* pDevice) break; } - hr = ma_IDirectSoundBuffer_GetStatus((ma_IDirectSoundBuffer*)pDevice->dsound.pPlaybackBuffer, &playbackBufferStatus); - if (SUCCEEDED(hr) && (playbackBufferStatus & MA_DSBSTATUS_PLAYING) == 0 && isPlaybackDeviceStarted) { - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, "[DirectSound] Attempting to resume audio due to state: %d.", (int)playbackBufferStatus); - hr = ma_IDirectSoundBuffer_Play((ma_IDirectSoundBuffer*)pDevice->dsound.pPlaybackBuffer, 0, 0, MA_DSBPLAY_LOOPING); - if (FAILED(hr)) { - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[DirectSound] IDirectSoundBuffer_Play() failed after attempting to resume from state %d.", (int)playbackBufferStatus); - return ma_result_from_HRESULT(hr); - } - - isPlaybackDeviceStarted = MA_TRUE; - ma_sleep(waitTimeInMilliseconds); - continue; - } - if (physicalPlayCursorInBytes < prevPlayCursorInBytesPlayback) { physicalPlayCursorLoopFlagPlayback = !physicalPlayCursorLoopFlagPlayback; } @@ -25485,7 +25287,7 @@ static ma_result ma_context_uninit__dsound(ma_context* pContext) MA_ASSERT(pContext != NULL); MA_ASSERT(pContext->backend == ma_backend_dsound); - ma_dlclose(ma_context_get_log(pContext), pContext->dsound.hDSoundDLL); + ma_dlclose(pContext, pContext->dsound.hDSoundDLL); return MA_SUCCESS; } @@ -25496,15 +25298,15 @@ static ma_result ma_context_init__dsound(ma_context* pContext, const ma_context_ (void)pConfig; - pContext->dsound.hDSoundDLL = ma_dlopen(ma_context_get_log(pContext), "dsound.dll"); + pContext->dsound.hDSoundDLL = ma_dlopen(pContext, "dsound.dll"); if (pContext->dsound.hDSoundDLL == NULL) { return MA_API_NOT_FOUND; } - pContext->dsound.DirectSoundCreate = ma_dlsym(ma_context_get_log(pContext), pContext->dsound.hDSoundDLL, "DirectSoundCreate"); - pContext->dsound.DirectSoundEnumerateA = ma_dlsym(ma_context_get_log(pContext), pContext->dsound.hDSoundDLL, "DirectSoundEnumerateA"); - pContext->dsound.DirectSoundCaptureCreate = ma_dlsym(ma_context_get_log(pContext), pContext->dsound.hDSoundDLL, "DirectSoundCaptureCreate"); - pContext->dsound.DirectSoundCaptureEnumerateA = ma_dlsym(ma_context_get_log(pContext), pContext->dsound.hDSoundDLL, "DirectSoundCaptureEnumerateA"); + pContext->dsound.DirectSoundCreate = ma_dlsym(pContext, pContext->dsound.hDSoundDLL, "DirectSoundCreate"); + pContext->dsound.DirectSoundEnumerateA = ma_dlsym(pContext, pContext->dsound.hDSoundDLL, "DirectSoundEnumerateA"); + pContext->dsound.DirectSoundCaptureCreate = ma_dlsym(pContext, pContext->dsound.hDSoundDLL, "DirectSoundCaptureCreate"); + pContext->dsound.DirectSoundCaptureEnumerateA = ma_dlsym(pContext, pContext->dsound.hDSoundDLL, "DirectSoundCaptureEnumerateA"); /* We need to support all functions or nothing. DirectSound with Windows 95 seems to not work too @@ -25518,8 +25320,6 @@ static ma_result ma_context_init__dsound(ma_context* pContext, const ma_context_ return MA_API_NOT_FOUND; } - pContext->dsound.hWnd = pConfig->dsound.hWnd; - pCallbacks->onContextInit = ma_context_init__dsound; pCallbacks->onContextUninit = ma_context_uninit__dsound; pCallbacks->onContextEnumerateDevices = ma_context_enumerate_devices__dsound; @@ -25842,7 +25642,7 @@ static ma_result ma_context_get_device_info_from_WAVECAPS(ma_context* pContext, - If the name GUID is not present in the registry we'll also need to stick to the original 31 characters. - I like consistency, so I want the returned device names to be consistent with those returned by WASAPI and DirectSound. The problem, however is that WASAPI and DirectSound use " ()" format (such as "Speakers (High Definition Audio)"), - but WinMM does not specify the component name. From my admittedly limited testing, I've notice the component name seems to + but WinMM does not specificy the component name. From my admittedly limited testing, I've notice the component name seems to usually fit within the 31 characters of the fixed sized buffer, so what I'm going to do is parse that string for the component name, and then concatenate the name from the registry. */ @@ -26110,7 +25910,7 @@ static ma_result ma_device_init__winmm(ma_device* pDevice, const ma_device_confi return MA_DEVICE_TYPE_NOT_SUPPORTED; } - /* No exclusive mode with WinMM. */ + /* No exlusive mode with WinMM. */ if (((pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) && pDescriptorPlayback->shareMode == ma_share_mode_exclusive) || ((pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) && pDescriptorCapture->shareMode == ma_share_mode_exclusive)) { return MA_SHARE_MODE_NOT_SUPPORTED; @@ -26132,7 +25932,7 @@ static ma_result ma_device_init__winmm(ma_device* pDevice, const ma_device_confi /* We use an event to know when a new fragment needs to be enqueued. */ pDevice->winmm.hEventCapture = (ma_handle)CreateEventA(NULL, TRUE, TRUE, NULL); if (pDevice->winmm.hEventCapture == NULL) { - errorMsg = "[WinMM] Failed to create event for fragment enqueuing for the capture device.", errorCode = ma_result_from_GetLastError(GetLastError()); + errorMsg = "[WinMM] Failed to create event for fragment enqueing for the capture device.", errorCode = ma_result_from_GetLastError(GetLastError()); goto on_error; } @@ -26170,7 +25970,7 @@ static ma_result ma_device_init__winmm(ma_device* pDevice, const ma_device_confi /* We use an event to know when a new fragment needs to be enqueued. */ pDevice->winmm.hEventPlayback = (ma_handle)CreateEventA(NULL, TRUE, TRUE, NULL); if (pDevice->winmm.hEventPlayback == NULL) { - errorMsg = "[WinMM] Failed to create event for fragment enqueuing for the playback device.", errorCode = ma_result_from_GetLastError(GetLastError()); + errorMsg = "[WinMM] Failed to create event for fragment enqueing for the playback device.", errorCode = ma_result_from_GetLastError(GetLastError()); goto on_error; } @@ -26586,7 +26386,7 @@ static ma_result ma_context_uninit__winmm(ma_context* pContext) MA_ASSERT(pContext != NULL); MA_ASSERT(pContext->backend == ma_backend_winmm); - ma_dlclose(ma_context_get_log(pContext), pContext->winmm.hWinMM); + ma_dlclose(pContext, pContext->winmm.hWinMM); return MA_SUCCESS; } @@ -26596,28 +26396,28 @@ static ma_result ma_context_init__winmm(ma_context* pContext, const ma_context_c (void)pConfig; - pContext->winmm.hWinMM = ma_dlopen(ma_context_get_log(pContext), "winmm.dll"); + pContext->winmm.hWinMM = ma_dlopen(pContext, "winmm.dll"); if (pContext->winmm.hWinMM == NULL) { return MA_NO_BACKEND; } - pContext->winmm.waveOutGetNumDevs = ma_dlsym(ma_context_get_log(pContext), pContext->winmm.hWinMM, "waveOutGetNumDevs"); - pContext->winmm.waveOutGetDevCapsA = ma_dlsym(ma_context_get_log(pContext), pContext->winmm.hWinMM, "waveOutGetDevCapsA"); - pContext->winmm.waveOutOpen = ma_dlsym(ma_context_get_log(pContext), pContext->winmm.hWinMM, "waveOutOpen"); - pContext->winmm.waveOutClose = ma_dlsym(ma_context_get_log(pContext), pContext->winmm.hWinMM, "waveOutClose"); - pContext->winmm.waveOutPrepareHeader = ma_dlsym(ma_context_get_log(pContext), pContext->winmm.hWinMM, "waveOutPrepareHeader"); - pContext->winmm.waveOutUnprepareHeader = ma_dlsym(ma_context_get_log(pContext), pContext->winmm.hWinMM, "waveOutUnprepareHeader"); - pContext->winmm.waveOutWrite = ma_dlsym(ma_context_get_log(pContext), pContext->winmm.hWinMM, "waveOutWrite"); - pContext->winmm.waveOutReset = ma_dlsym(ma_context_get_log(pContext), pContext->winmm.hWinMM, "waveOutReset"); - pContext->winmm.waveInGetNumDevs = ma_dlsym(ma_context_get_log(pContext), pContext->winmm.hWinMM, "waveInGetNumDevs"); - pContext->winmm.waveInGetDevCapsA = ma_dlsym(ma_context_get_log(pContext), pContext->winmm.hWinMM, "waveInGetDevCapsA"); - pContext->winmm.waveInOpen = ma_dlsym(ma_context_get_log(pContext), pContext->winmm.hWinMM, "waveInOpen"); - pContext->winmm.waveInClose = ma_dlsym(ma_context_get_log(pContext), pContext->winmm.hWinMM, "waveInClose"); - pContext->winmm.waveInPrepareHeader = ma_dlsym(ma_context_get_log(pContext), pContext->winmm.hWinMM, "waveInPrepareHeader"); - pContext->winmm.waveInUnprepareHeader = ma_dlsym(ma_context_get_log(pContext), pContext->winmm.hWinMM, "waveInUnprepareHeader"); - pContext->winmm.waveInAddBuffer = ma_dlsym(ma_context_get_log(pContext), pContext->winmm.hWinMM, "waveInAddBuffer"); - pContext->winmm.waveInStart = ma_dlsym(ma_context_get_log(pContext), pContext->winmm.hWinMM, "waveInStart"); - pContext->winmm.waveInReset = ma_dlsym(ma_context_get_log(pContext), pContext->winmm.hWinMM, "waveInReset"); + pContext->winmm.waveOutGetNumDevs = ma_dlsym(pContext, pContext->winmm.hWinMM, "waveOutGetNumDevs"); + pContext->winmm.waveOutGetDevCapsA = ma_dlsym(pContext, pContext->winmm.hWinMM, "waveOutGetDevCapsA"); + pContext->winmm.waveOutOpen = ma_dlsym(pContext, pContext->winmm.hWinMM, "waveOutOpen"); + pContext->winmm.waveOutClose = ma_dlsym(pContext, pContext->winmm.hWinMM, "waveOutClose"); + pContext->winmm.waveOutPrepareHeader = ma_dlsym(pContext, pContext->winmm.hWinMM, "waveOutPrepareHeader"); + pContext->winmm.waveOutUnprepareHeader = ma_dlsym(pContext, pContext->winmm.hWinMM, "waveOutUnprepareHeader"); + pContext->winmm.waveOutWrite = ma_dlsym(pContext, pContext->winmm.hWinMM, "waveOutWrite"); + pContext->winmm.waveOutReset = ma_dlsym(pContext, pContext->winmm.hWinMM, "waveOutReset"); + pContext->winmm.waveInGetNumDevs = ma_dlsym(pContext, pContext->winmm.hWinMM, "waveInGetNumDevs"); + pContext->winmm.waveInGetDevCapsA = ma_dlsym(pContext, pContext->winmm.hWinMM, "waveInGetDevCapsA"); + pContext->winmm.waveInOpen = ma_dlsym(pContext, pContext->winmm.hWinMM, "waveInOpen"); + pContext->winmm.waveInClose = ma_dlsym(pContext, pContext->winmm.hWinMM, "waveInClose"); + pContext->winmm.waveInPrepareHeader = ma_dlsym(pContext, pContext->winmm.hWinMM, "waveInPrepareHeader"); + pContext->winmm.waveInUnprepareHeader = ma_dlsym(pContext, pContext->winmm.hWinMM, "waveInUnprepareHeader"); + pContext->winmm.waveInAddBuffer = ma_dlsym(pContext, pContext->winmm.hWinMM, "waveInAddBuffer"); + pContext->winmm.waveInStart = ma_dlsym(pContext, pContext->winmm.hWinMM, "waveInStart"); + pContext->winmm.waveInReset = ma_dlsym(pContext, pContext->winmm.hWinMM, "waveInReset"); pCallbacks->onContextInit = ma_context_init__winmm; pCallbacks->onContextUninit = ma_context_uninit__winmm; @@ -27292,7 +27092,7 @@ static ma_result ma_context_open_pcm__alsa(ma_context* pContext, ma_share_mode s /* We're trying to open a specific device. There's a few things to consider here: - miniaudio recognizes a special format of device id that excludes the "hw", "dmix", etc. prefix. It looks like this: ":0,0", ":0,1", etc. When + miniaudio recongnizes a special format of device id that excludes the "hw", "dmix", etc. prefix. It looks like this: ":0,0", ":0,1", etc. When an ID of this format is specified, it indicates to miniaudio that it can try different combinations of plugins ("hw", "dmix", etc.) until it finds an appropriate one that works. This comes in very handy when trying to open a device in shared mode ("dmix"), vs exclusive mode ("hw"). */ @@ -27391,7 +27191,7 @@ static ma_result ma_context_enumerate_devices__alsa(ma_context* pContext, ma_enu /* At this point, hwid looks like "hw:0,0". In simplified enumeration mode, we actually want to strip off the plugin name so it looks like ":0,0". The reason for this is that this special format is detected at device - initialization time and is used as an indicator to try to use the most appropriate plugin depending on the + initialization time and is used as an indicator to try and use the most appropriate plugin depending on the device type and sharing mode. */ char* dst = hwid; @@ -27570,7 +27370,7 @@ static void ma_context_iterate_rates_and_add_native_data_format__alsa(ma_context ((ma_snd_pcm_hw_params_get_rate_min_proc)pContext->alsa.snd_pcm_hw_params_get_rate_min)(pHWParams, &minSampleRate, &sampleRateDir); ((ma_snd_pcm_hw_params_get_rate_max_proc)pContext->alsa.snd_pcm_hw_params_get_rate_max)(pHWParams, &maxSampleRate, &sampleRateDir); - /* Make sure our sample rates are clamped to sane values. Stupid devices like "pulse" will reports rates like "1" which is ridiculous. */ + /* Make sure our sample rates are clamped to sane values. Stupid devices like "pulse" will reports rates like "1" which is ridiculus. */ minSampleRate = ma_clamp(minSampleRate, (unsigned int)ma_standard_sample_rate_min, (unsigned int)ma_standard_sample_rate_max); maxSampleRate = ma_clamp(maxSampleRate, (unsigned int)ma_standard_sample_rate_min, (unsigned int)ma_standard_sample_rate_max); @@ -27646,10 +27446,10 @@ static ma_result ma_context_get_device_info__alsa(ma_context* pContext, ma_devic /* Some ALSA devices can support many permutations of formats, channels and rates. We only support a fixed number of permutations which means we need to employ some strategies to ensure the best - combinations are returned. An example is the "pulse" device which can do its own data conversion + combinations are returned. An example is the "pulse" device which can do it's own data conversion in software and as a result can support any combination of format, channels and rate. - We want to ensure that the first data formats are the best. We have a list of favored sample + We want to ensure the the first data formats are the best. We have a list of favored sample formats and sample rates, so these will be the basis of our iteration. */ @@ -28227,21 +28027,7 @@ static ma_result ma_device_start__alsa(ma_device* pDevice) } if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) { - /* - When data is written to the device we wait for the device to get ready to receive data with poll(). In my testing - I have observed that poll() can sometimes block forever unless the device is started explicitly with snd_pcm_start() - or some data is written with snd_pcm_writei(). - - To resolve this I've decided to do an explicit start with snd_pcm_start(). The problem with this is that the device - is started without any data in the internal buffer which will result in an immediate underrun. If instead we were - to call into snd_pcm_writei() in an attempt to prevent the underrun, we would run the risk of a weird deadlock - issue as documented inside ma_device_write__alsa(). - */ - resultALSA = ((ma_snd_pcm_start_proc)pDevice->pContext->alsa.snd_pcm_start)((ma_snd_pcm_t*)pDevice->alsa.pPCMPlayback); - if (resultALSA < 0) { - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[ALSA] Failed to start playback device."); - return ma_result_from_errno(-resultALSA); - } + /* Don't need to do anything for playback because it'll be started automatically when enough data has been written. */ } return MA_SUCCESS; @@ -28249,13 +28035,6 @@ static ma_result ma_device_start__alsa(ma_device* pDevice) static ma_result ma_device_stop__alsa(ma_device* pDevice) { - /* - The stop callback will get called on the worker thread after read/write__alsa() has returned. At this point there is - a small chance that our wakeupfd has not been cleared. We'll clear that out now if applicable. - */ - int resultPoll; - int resultRead; - if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) { ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "[ALSA] Dropping capture device...\n"); ((ma_snd_pcm_drop_proc)pDevice->pContext->alsa.snd_pcm_drop)((ma_snd_pcm_t*)pDevice->alsa.pPCMCapture); @@ -28268,16 +28047,6 @@ static ma_result ma_device_stop__alsa(ma_device* pDevice) } else { ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "[ALSA] Preparing capture device successful.\n"); } - - /* Clear the wakeupfd. */ - resultPoll = poll((struct pollfd*)pDevice->alsa.pPollDescriptorsCapture, 1, 0); - if (resultPoll > 0) { - ma_uint64 t; - resultRead = read(((struct pollfd*)pDevice->alsa.pPollDescriptorsCapture)[0].fd, &t, sizeof(t)); - if (resultRead != sizeof(t)) { - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "[ALSA] Failed to read from capture wakeupfd. read() = %d\n", resultRead); - } - } } if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) { @@ -28292,16 +28061,6 @@ static ma_result ma_device_stop__alsa(ma_device* pDevice) } else { ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "[ALSA] Preparing playback device successful.\n"); } - - /* Clear the wakeupfd. */ - resultPoll = poll((struct pollfd*)pDevice->alsa.pPollDescriptorsPlayback, 1, 0); - if (resultPoll > 0) { - ma_uint64 t; - resultRead = read(((struct pollfd*)pDevice->alsa.pPollDescriptorsPlayback)[0].fd, &t, sizeof(t)); - if (resultRead != sizeof(t)) { - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "[ALSA] Failed to read from playback wakeupfd. read() = %d\n", resultRead); - } - } } return MA_SUCCESS; @@ -28314,27 +28073,20 @@ static ma_result ma_device_wait__alsa(ma_device* pDevice, ma_snd_pcm_t* pPCM, st int resultALSA; int resultPoll = poll(pPollDescriptors, pollDescriptorCount, -1); if (resultPoll < 0) { - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_WARNING, "[ALSA] poll() failed.\n"); - - /* - There have been reports that poll() is returning an error randomly and that instead of - returning an error, simply trying again will work. I'm experimenting with adopting this - advice. - */ - continue; - /*return ma_result_from_errno(errno);*/ + ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[ALSA] poll() failed."); + return ma_result_from_errno(errno); } /* Before checking the ALSA poll descriptor flag we need to check if the wakeup descriptor - has had it's POLLIN flag set. If so, we need to actually read the data and then exit the + has had it's POLLIN flag set. If so, we need to actually read the data and then exit function. The wakeup descriptor will be the first item in the descriptors buffer. */ if ((pPollDescriptors[0].revents & POLLIN) != 0) { ma_uint64 t; int resultRead = read(pPollDescriptors[0].fd, &t, sizeof(t)); /* <-- Important that we read here so that the next write() does not block. */ if (resultRead < 0) { - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[ALSA] read() failed.\n"); + ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[ALSA] read() failed."); return ma_result_from_errno(errno); } @@ -28348,17 +28100,13 @@ static ma_result ma_device_wait__alsa(ma_device* pDevice, ma_snd_pcm_t* pPCM, st */ resultALSA = ((ma_snd_pcm_poll_descriptors_revents_proc)pDevice->pContext->alsa.snd_pcm_poll_descriptors_revents)(pPCM, pPollDescriptors + 1, pollDescriptorCount - 1, &revents); /* +1, -1 to ignore the wakeup descriptor. */ if (resultALSA < 0) { - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[ALSA] snd_pcm_poll_descriptors_revents() failed.\n"); + ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[ALSA] snd_pcm_poll_descriptors_revents() failed."); return ma_result_from_errno(-resultALSA); } if ((revents & POLLERR) != 0) { - ma_snd_pcm_state_t state = ((ma_snd_pcm_state_proc)pDevice->pContext->alsa.snd_pcm_state)(pPCM); - if (state == MA_SND_PCM_STATE_XRUN) { - /* The PCM is in a xrun state. This will be recovered from at a higher level. We can disregard this. */ - } else { - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_WARNING, "[ALSA] POLLERR detected. status = %d\n", ((ma_snd_pcm_state_proc)pDevice->pContext->alsa.snd_pcm_state)(pPCM)); - } + ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[ALSA] POLLERR detected."); + return ma_result_from_errno(errno); } if ((revents & requiredEvent) == requiredEvent) { @@ -28533,7 +28281,7 @@ static ma_result ma_context_uninit__alsa(ma_context* pContext) ((ma_snd_config_update_free_global_proc)pContext->alsa.snd_config_update_free_global)(); #ifndef MA_NO_RUNTIME_LINKING - ma_dlclose(ma_context_get_log(pContext), pContext->alsa.asoundSO); + ma_dlclose(pContext, pContext->alsa.asoundSO); #endif ma_mutex_uninit(&pContext->alsa.internalDeviceEnumLock); @@ -28552,7 +28300,7 @@ static ma_result ma_context_init__alsa(ma_context* pContext, const ma_context_co size_t i; for (i = 0; i < ma_countof(libasoundNames); ++i) { - pContext->alsa.asoundSO = ma_dlopen(ma_context_get_log(pContext), libasoundNames[i]); + pContext->alsa.asoundSO = ma_dlopen(pContext, libasoundNames[i]); if (pContext->alsa.asoundSO != NULL) { break; } @@ -28563,72 +28311,72 @@ static ma_result ma_context_init__alsa(ma_context* pContext, const ma_context_co return MA_NO_BACKEND; } - pContext->alsa.snd_pcm_open = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_open"); - pContext->alsa.snd_pcm_close = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_close"); - pContext->alsa.snd_pcm_hw_params_sizeof = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_hw_params_sizeof"); - pContext->alsa.snd_pcm_hw_params_any = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_hw_params_any"); - pContext->alsa.snd_pcm_hw_params_set_format = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_hw_params_set_format"); - pContext->alsa.snd_pcm_hw_params_set_format_first = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_hw_params_set_format_first"); - pContext->alsa.snd_pcm_hw_params_get_format_mask = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_hw_params_get_format_mask"); - pContext->alsa.snd_pcm_hw_params_set_channels = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_hw_params_set_channels"); - pContext->alsa.snd_pcm_hw_params_set_channels_near = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_hw_params_set_channels_near"); - pContext->alsa.snd_pcm_hw_params_set_channels_minmax = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_hw_params_set_channels_minmax"); - pContext->alsa.snd_pcm_hw_params_set_rate_resample = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_hw_params_set_rate_resample"); - pContext->alsa.snd_pcm_hw_params_set_rate = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_hw_params_set_rate"); - pContext->alsa.snd_pcm_hw_params_set_rate_near = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_hw_params_set_rate_near"); - pContext->alsa.snd_pcm_hw_params_set_buffer_size_near = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_hw_params_set_buffer_size_near"); - pContext->alsa.snd_pcm_hw_params_set_periods_near = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_hw_params_set_periods_near"); - pContext->alsa.snd_pcm_hw_params_set_access = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_hw_params_set_access"); - pContext->alsa.snd_pcm_hw_params_get_format = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_hw_params_get_format"); - pContext->alsa.snd_pcm_hw_params_get_channels = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_hw_params_get_channels"); - pContext->alsa.snd_pcm_hw_params_get_channels_min = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_hw_params_get_channels_min"); - pContext->alsa.snd_pcm_hw_params_get_channels_max = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_hw_params_get_channels_max"); - pContext->alsa.snd_pcm_hw_params_get_rate = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_hw_params_get_rate"); - pContext->alsa.snd_pcm_hw_params_get_rate_min = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_hw_params_get_rate_min"); - pContext->alsa.snd_pcm_hw_params_get_rate_max = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_hw_params_get_rate_max"); - pContext->alsa.snd_pcm_hw_params_get_buffer_size = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_hw_params_get_buffer_size"); - pContext->alsa.snd_pcm_hw_params_get_periods = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_hw_params_get_periods"); - pContext->alsa.snd_pcm_hw_params_get_access = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_hw_params_get_access"); - pContext->alsa.snd_pcm_hw_params_test_format = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_hw_params_test_format"); - pContext->alsa.snd_pcm_hw_params_test_channels = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_hw_params_test_channels"); - pContext->alsa.snd_pcm_hw_params_test_rate = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_hw_params_test_rate"); - pContext->alsa.snd_pcm_hw_params = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_hw_params"); - pContext->alsa.snd_pcm_sw_params_sizeof = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_sw_params_sizeof"); - pContext->alsa.snd_pcm_sw_params_current = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_sw_params_current"); - pContext->alsa.snd_pcm_sw_params_get_boundary = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_sw_params_get_boundary"); - pContext->alsa.snd_pcm_sw_params_set_avail_min = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_sw_params_set_avail_min"); - pContext->alsa.snd_pcm_sw_params_set_start_threshold = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_sw_params_set_start_threshold"); - pContext->alsa.snd_pcm_sw_params_set_stop_threshold = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_sw_params_set_stop_threshold"); - pContext->alsa.snd_pcm_sw_params = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_sw_params"); - pContext->alsa.snd_pcm_format_mask_sizeof = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_format_mask_sizeof"); - pContext->alsa.snd_pcm_format_mask_test = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_format_mask_test"); - pContext->alsa.snd_pcm_get_chmap = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_get_chmap"); - pContext->alsa.snd_pcm_state = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_state"); - pContext->alsa.snd_pcm_prepare = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_prepare"); - pContext->alsa.snd_pcm_start = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_start"); - pContext->alsa.snd_pcm_drop = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_drop"); - pContext->alsa.snd_pcm_drain = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_drain"); - pContext->alsa.snd_pcm_reset = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_reset"); - pContext->alsa.snd_device_name_hint = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_device_name_hint"); - pContext->alsa.snd_device_name_get_hint = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_device_name_get_hint"); - pContext->alsa.snd_card_get_index = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_card_get_index"); - pContext->alsa.snd_device_name_free_hint = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_device_name_free_hint"); - pContext->alsa.snd_pcm_mmap_begin = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_mmap_begin"); - pContext->alsa.snd_pcm_mmap_commit = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_mmap_commit"); - pContext->alsa.snd_pcm_recover = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_recover"); - pContext->alsa.snd_pcm_readi = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_readi"); - pContext->alsa.snd_pcm_writei = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_writei"); - pContext->alsa.snd_pcm_avail = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_avail"); - pContext->alsa.snd_pcm_avail_update = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_avail_update"); - pContext->alsa.snd_pcm_wait = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_wait"); - pContext->alsa.snd_pcm_nonblock = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_nonblock"); - pContext->alsa.snd_pcm_info = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_info"); - pContext->alsa.snd_pcm_info_sizeof = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_info_sizeof"); - pContext->alsa.snd_pcm_info_get_name = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_info_get_name"); - pContext->alsa.snd_pcm_poll_descriptors = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_poll_descriptors"); - pContext->alsa.snd_pcm_poll_descriptors_count = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_poll_descriptors_count"); - pContext->alsa.snd_pcm_poll_descriptors_revents = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_poll_descriptors_revents"); - pContext->alsa.snd_config_update_free_global = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_config_update_free_global"); + pContext->alsa.snd_pcm_open = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_open"); + pContext->alsa.snd_pcm_close = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_close"); + pContext->alsa.snd_pcm_hw_params_sizeof = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_hw_params_sizeof"); + pContext->alsa.snd_pcm_hw_params_any = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_hw_params_any"); + pContext->alsa.snd_pcm_hw_params_set_format = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_hw_params_set_format"); + pContext->alsa.snd_pcm_hw_params_set_format_first = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_hw_params_set_format_first"); + pContext->alsa.snd_pcm_hw_params_get_format_mask = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_hw_params_get_format_mask"); + pContext->alsa.snd_pcm_hw_params_set_channels = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_hw_params_set_channels"); + pContext->alsa.snd_pcm_hw_params_set_channels_near = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_hw_params_set_channels_near"); + pContext->alsa.snd_pcm_hw_params_set_channels_minmax = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_hw_params_set_channels_minmax"); + pContext->alsa.snd_pcm_hw_params_set_rate_resample = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_hw_params_set_rate_resample"); + pContext->alsa.snd_pcm_hw_params_set_rate = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_hw_params_set_rate"); + pContext->alsa.snd_pcm_hw_params_set_rate_near = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_hw_params_set_rate_near"); + pContext->alsa.snd_pcm_hw_params_set_buffer_size_near = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_hw_params_set_buffer_size_near"); + pContext->alsa.snd_pcm_hw_params_set_periods_near = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_hw_params_set_periods_near"); + pContext->alsa.snd_pcm_hw_params_set_access = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_hw_params_set_access"); + pContext->alsa.snd_pcm_hw_params_get_format = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_hw_params_get_format"); + pContext->alsa.snd_pcm_hw_params_get_channels = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_hw_params_get_channels"); + pContext->alsa.snd_pcm_hw_params_get_channels_min = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_hw_params_get_channels_min"); + pContext->alsa.snd_pcm_hw_params_get_channels_max = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_hw_params_get_channels_max"); + pContext->alsa.snd_pcm_hw_params_get_rate = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_hw_params_get_rate"); + pContext->alsa.snd_pcm_hw_params_get_rate_min = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_hw_params_get_rate_min"); + pContext->alsa.snd_pcm_hw_params_get_rate_max = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_hw_params_get_rate_max"); + pContext->alsa.snd_pcm_hw_params_get_buffer_size = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_hw_params_get_buffer_size"); + pContext->alsa.snd_pcm_hw_params_get_periods = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_hw_params_get_periods"); + pContext->alsa.snd_pcm_hw_params_get_access = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_hw_params_get_access"); + pContext->alsa.snd_pcm_hw_params_test_format = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_hw_params_test_format"); + pContext->alsa.snd_pcm_hw_params_test_channels = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_hw_params_test_channels"); + pContext->alsa.snd_pcm_hw_params_test_rate = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_hw_params_test_rate"); + pContext->alsa.snd_pcm_hw_params = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_hw_params"); + pContext->alsa.snd_pcm_sw_params_sizeof = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_sw_params_sizeof"); + pContext->alsa.snd_pcm_sw_params_current = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_sw_params_current"); + pContext->alsa.snd_pcm_sw_params_get_boundary = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_sw_params_get_boundary"); + pContext->alsa.snd_pcm_sw_params_set_avail_min = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_sw_params_set_avail_min"); + pContext->alsa.snd_pcm_sw_params_set_start_threshold = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_sw_params_set_start_threshold"); + pContext->alsa.snd_pcm_sw_params_set_stop_threshold = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_sw_params_set_stop_threshold"); + pContext->alsa.snd_pcm_sw_params = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_sw_params"); + pContext->alsa.snd_pcm_format_mask_sizeof = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_format_mask_sizeof"); + pContext->alsa.snd_pcm_format_mask_test = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_format_mask_test"); + pContext->alsa.snd_pcm_get_chmap = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_get_chmap"); + pContext->alsa.snd_pcm_state = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_state"); + pContext->alsa.snd_pcm_prepare = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_prepare"); + pContext->alsa.snd_pcm_start = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_start"); + pContext->alsa.snd_pcm_drop = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_drop"); + pContext->alsa.snd_pcm_drain = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_drain"); + pContext->alsa.snd_pcm_reset = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_reset"); + pContext->alsa.snd_device_name_hint = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_device_name_hint"); + pContext->alsa.snd_device_name_get_hint = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_device_name_get_hint"); + pContext->alsa.snd_card_get_index = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_card_get_index"); + pContext->alsa.snd_device_name_free_hint = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_device_name_free_hint"); + pContext->alsa.snd_pcm_mmap_begin = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_mmap_begin"); + pContext->alsa.snd_pcm_mmap_commit = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_mmap_commit"); + pContext->alsa.snd_pcm_recover = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_recover"); + pContext->alsa.snd_pcm_readi = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_readi"); + pContext->alsa.snd_pcm_writei = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_writei"); + pContext->alsa.snd_pcm_avail = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_avail"); + pContext->alsa.snd_pcm_avail_update = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_avail_update"); + pContext->alsa.snd_pcm_wait = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_wait"); + pContext->alsa.snd_pcm_nonblock = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_nonblock"); + pContext->alsa.snd_pcm_info = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_info"); + pContext->alsa.snd_pcm_info_sizeof = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_info_sizeof"); + pContext->alsa.snd_pcm_info_get_name = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_info_get_name"); + pContext->alsa.snd_pcm_poll_descriptors = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_poll_descriptors"); + pContext->alsa.snd_pcm_poll_descriptors_count = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_poll_descriptors_count"); + pContext->alsa.snd_pcm_poll_descriptors_revents = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_poll_descriptors_revents"); + pContext->alsa.snd_config_update_free_global = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_config_update_free_global"); #else /* The system below is just for type safety. */ ma_snd_pcm_open_proc _snd_pcm_open = snd_pcm_open; @@ -28789,7 +28537,7 @@ static ma_result ma_context_init__alsa(ma_context* pContext, const ma_context_co return MA_SUCCESS; } -#endif /* MA_HAS_ALSA */ +#endif /* ALSA */ @@ -28800,7 +28548,7 @@ PulseAudio Backend ******************************************************************************/ #ifdef MA_HAS_PULSEAUDIO /* -The PulseAudio API, along with Apple's Core Audio, is the worst of the mainstream audio APIs. This is a brief description of what's going on +The PulseAudio API, along with Apple's Core Audio, is the worst of the maintream audio APIs. This is a brief description of what's going on in the PulseAudio backend. I apologize if this gets a bit ranty for your liking - you might want to skip this discussion. PulseAudio has something they call the "Simple API", which unfortunately isn't suitable for miniaudio. I've not seen anywhere where it @@ -28815,7 +28563,7 @@ get fun, and I don't mean that in a good way... The problems start with the very name of the API - "asynchronous". Yes, this is an asynchronous oriented API which means your commands don't immediately take effect. You instead need to issue your commands, and then wait for them to complete. The waiting mechanism is -enabled through the use of a "main loop". In the asynchronous API you cannot get away from the main loop, and the main loop is where almost +enabled through the use of a "main loop". In the asychronous API you cannot get away from the main loop, and the main loop is where almost all of PulseAudio's problems stem from. When you first initialize PulseAudio you need an object referred to as "main loop". You can implement this yourself by defining your own @@ -28865,7 +28613,7 @@ because PulseAudio takes it literally, specifically the "can be". You would thin writing and reading data to and from the stream, and that would be right, except when it's not. When you initialize the stream, you can set a flag that tells PulseAudio to not start the stream automatically. This is required because miniaudio does not auto-start devices straight after initialization - you need to call `ma_device_start()` manually. The problem is that even when this flag is specified, -PulseAudio will immediately fire its write or read callback. This is *technically* correct (based on the wording in the documentation) +PulseAudio will immediately fire it's write or read callback. This is *technically* correct (based on the wording in the documentation) because indeed, data *can* be written at this point. The problem is that it's not *practical*. It makes sense that the write/read callback would be where a program will want to write or read data to or from the stream, but when it's called before the application has even requested that the stream be started, it's just not practical because the program probably isn't ready for any kind of data delivery at @@ -30243,18 +29991,16 @@ static ma_pa_buffer_attr ma_device__pa_buffer_attr_new(ma_uint32 periodSizeInFra static ma_pa_stream* ma_device__pa_stream_new__pulse(ma_device* pDevice, const char* pStreamName, const ma_pa_sample_spec* ss, const ma_pa_channel_map* cmap) { - static ma_atomic_uint32 g_StreamCounter = { 0 }; + static int g_StreamCounter = 0; char actualStreamName[256]; if (pStreamName != NULL) { ma_strncpy_s(actualStreamName, sizeof(actualStreamName), pStreamName, (size_t)-1); } else { - const char* pBaseName = "miniaudio:"; - size_t baseNameLen = strlen(pBaseName); - ma_strcpy_s(actualStreamName, sizeof(actualStreamName), pBaseName); - ma_itoa_s((int)ma_atomic_uint32_get(&g_StreamCounter), actualStreamName + baseNameLen, sizeof(actualStreamName)-baseNameLen, 10); + ma_strcpy_s(actualStreamName, sizeof(actualStreamName), "miniaudio:"); + ma_itoa_s(g_StreamCounter, actualStreamName + 8, sizeof(actualStreamName)-8, 10); /* 8 = strlen("miniaudio:") */ } - ma_atomic_uint32_fetch_add(&g_StreamCounter, 1); + g_StreamCounter += 1; return ((ma_pa_stream_new_proc)pDevice->pContext->pulse.pa_stream_new)((ma_pa_context*)pDevice->pulse.pPulseContext, actualStreamName, ss, cmap); } @@ -30486,6 +30232,11 @@ static ma_result ma_device_init__pulse(ma_device* pDevice, const ma_device_confi /* Notes for PulseAudio: + - We're always using native format/channels/rate regardless of whether or not PulseAudio + supports the format directly through their own data conversion system. I'm doing this to + reduce as much variability from the PulseAudio side as possible because it's seems to be + extremely unreliable at everything it does. + - When both the period size in frames and milliseconds are 0, we default to miniaudio's default buffer sizes rather than leaving it up to PulseAudio because I don't trust PulseAudio to give us any kind of reasonable latency by default. @@ -30508,7 +30259,6 @@ static ma_result ma_device_init__pulse(ma_device* pDevice, const ma_device_confi ma_pa_buffer_attr attr; const ma_pa_sample_spec* pActualSS = NULL; const ma_pa_buffer_attr* pActualAttr = NULL; - const ma_pa_channel_map* pActualChannelMap = NULL; ma_uint32 iChannel; ma_pa_stream_flags_t streamFlags; @@ -30569,13 +30319,12 @@ static ma_result ma_device_init__pulse(ma_device* pDevice, const ma_device_confi } /* Use a default channel map. */ - ((ma_pa_channel_map_init_extend_proc)pDevice->pContext->pulse.pa_channel_map_init_extend)(&cmap, ss.channels, pConfig->pulse.channelMap); + ((ma_pa_channel_map_init_extend_proc)pDevice->pContext->pulse.pa_channel_map_init_extend)(&cmap, ss.channels, MA_PA_CHANNEL_MAP_DEFAULT); /* Use the requested sample rate if one was specified. */ if (pDescriptorCapture->sampleRate != 0) { ss.rate = pDescriptorCapture->sampleRate; } - streamFlags = MA_PA_STREAM_START_CORKED | MA_PA_STREAM_ADJUST_LATENCY; if (ma_format_from_pulse(ss.format) == ma_format_unknown) { if (ma_is_little_endian()) { @@ -30583,17 +30332,14 @@ static ma_result ma_device_init__pulse(ma_device* pDevice, const ma_device_confi } else { ss.format = MA_PA_SAMPLE_FLOAT32BE; } - streamFlags |= MA_PA_STREAM_FIX_FORMAT; ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, "[PulseAudio] sample_spec.format not supported by miniaudio. Defaulting to PA_SAMPLE_FLOAT32.\n"); } if (ss.rate == 0) { ss.rate = MA_DEFAULT_SAMPLE_RATE; - streamFlags |= MA_PA_STREAM_FIX_RATE; ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, "[PulseAudio] sample_spec.rate = 0. Defaulting to %d.\n", ss.rate); } if (ss.channels == 0) { ss.channels = MA_DEFAULT_CHANNELS; - streamFlags |= MA_PA_STREAM_FIX_CHANNELS; ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, "[PulseAudio] sample_spec.channels = 0. Defaulting to %d.\n", ss.channels); } @@ -30622,6 +30368,7 @@ static ma_result ma_device_init__pulse(ma_device* pDevice, const ma_device_confi /* Connect after we've got all of our internal state set up. */ + streamFlags = MA_PA_STREAM_START_CORKED | MA_PA_STREAM_ADJUST_LATENCY | MA_PA_STREAM_FIX_FORMAT | MA_PA_STREAM_FIX_RATE | MA_PA_STREAM_FIX_CHANNELS; if (devCapture != NULL) { streamFlags |= MA_PA_STREAM_DONT_MOVE; } @@ -30658,12 +30405,7 @@ static ma_result ma_device_init__pulse(ma_device* pDevice, const ma_device_confi goto on_error4; } - /* Internal channel map. */ - pActualChannelMap = ((ma_pa_stream_get_channel_map_proc)pDevice->pContext->pulse.pa_stream_get_channel_map)((ma_pa_stream*)pDevice->pulse.pStreamCapture); - if (pActualChannelMap == NULL) { - pActualChannelMap = &cmap; /* Fallback just in case. */ - } /* Bug in PipeWire. There have been reports that PipeWire is returning AUX channels when reporting @@ -30673,8 +30415,8 @@ static ma_result ma_device_init__pulse(ma_device* pDevice, const ma_device_confi fixed sooner than later. I might remove this hack later. */ if (pDescriptorCapture->channels > 2) { - for (iChannel = 0; iChannel < pDescriptorCapture->channels; iChannel += 1) { - pDescriptorCapture->channelMap[iChannel] = ma_channel_position_from_pulse(pActualChannelMap->map[iChannel]); + for (iChannel = 0; iChannel < pDescriptorCapture->channels; ++iChannel) { + pDescriptorCapture->channelMap[iChannel] = ma_channel_position_from_pulse(cmap.map[iChannel]); } } else { /* Hack for mono and stereo. */ @@ -30721,7 +30463,7 @@ static ma_result ma_device_init__pulse(ma_device* pDevice, const ma_device_confi } /* Use a default channel map. */ - ((ma_pa_channel_map_init_extend_proc)pDevice->pContext->pulse.pa_channel_map_init_extend)(&cmap, ss.channels, pConfig->pulse.channelMap); + ((ma_pa_channel_map_init_extend_proc)pDevice->pContext->pulse.pa_channel_map_init_extend)(&cmap, ss.channels, MA_PA_CHANNEL_MAP_DEFAULT); /* Use the requested sample rate if one was specified. */ @@ -30729,24 +30471,20 @@ static ma_result ma_device_init__pulse(ma_device* pDevice, const ma_device_confi ss.rate = pDescriptorPlayback->sampleRate; } - streamFlags = MA_PA_STREAM_START_CORKED | MA_PA_STREAM_ADJUST_LATENCY; if (ma_format_from_pulse(ss.format) == ma_format_unknown) { if (ma_is_little_endian()) { ss.format = MA_PA_SAMPLE_FLOAT32LE; } else { ss.format = MA_PA_SAMPLE_FLOAT32BE; } - streamFlags |= MA_PA_STREAM_FIX_FORMAT; ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, "[PulseAudio] sample_spec.format not supported by miniaudio. Defaulting to PA_SAMPLE_FLOAT32.\n"); } if (ss.rate == 0) { ss.rate = MA_DEFAULT_SAMPLE_RATE; - streamFlags |= MA_PA_STREAM_FIX_RATE; ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, "[PulseAudio] sample_spec.rate = 0. Defaulting to %d.\n", ss.rate); } if (ss.channels == 0) { ss.channels = MA_DEFAULT_CHANNELS; - streamFlags |= MA_PA_STREAM_FIX_CHANNELS; ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, "[PulseAudio] sample_spec.channels = 0. Defaulting to %d.\n", ss.channels); } @@ -30779,6 +30517,7 @@ static ma_result ma_device_init__pulse(ma_device* pDevice, const ma_device_confi /* Connect after we've got all of our internal state set up. */ + streamFlags = MA_PA_STREAM_START_CORKED | MA_PA_STREAM_ADJUST_LATENCY | MA_PA_STREAM_FIX_FORMAT | MA_PA_STREAM_FIX_RATE | MA_PA_STREAM_FIX_CHANNELS; if (devPlayback != NULL) { streamFlags |= MA_PA_STREAM_DONT_MOVE; } @@ -30815,12 +30554,7 @@ static ma_result ma_device_init__pulse(ma_device* pDevice, const ma_device_confi goto on_error4; } - /* Internal channel map. */ - pActualChannelMap = ((ma_pa_stream_get_channel_map_proc)pDevice->pContext->pulse.pa_stream_get_channel_map)((ma_pa_stream*)pDevice->pulse.pStreamPlayback); - if (pActualChannelMap == NULL) { - pActualChannelMap = &cmap; /* Fallback just in case. */ - } /* Bug in PipeWire. There have been reports that PipeWire is returning AUX channels when reporting @@ -30830,8 +30564,8 @@ static ma_result ma_device_init__pulse(ma_device* pDevice, const ma_device_confi fixed sooner than later. I might remove this hack later. */ if (pDescriptorPlayback->channels > 2) { - for (iChannel = 0; iChannel < pDescriptorPlayback->channels; iChannel += 1) { - pDescriptorPlayback->channelMap[iChannel] = ma_channel_position_from_pulse(pActualChannelMap->map[iChannel]); + for (iChannel = 0; iChannel < pDescriptorPlayback->channels; ++iChannel) { + pDescriptorPlayback->channelMap[iChannel] = ma_channel_position_from_pulse(cmap.map[iChannel]); } } else { /* Hack for mono and stereo. */ @@ -31062,7 +30796,7 @@ static ma_result ma_context_uninit__pulse(ma_context* pContext) ma_free(pContext->pulse.pApplicationName, &pContext->allocationCallbacks); #ifndef MA_NO_RUNTIME_LINKING - ma_dlclose(ma_context_get_log(pContext), pContext->pulse.pulseSO); + ma_dlclose(pContext, pContext->pulse.pulseSO); #endif return MA_SUCCESS; @@ -31079,7 +30813,7 @@ static ma_result ma_context_init__pulse(ma_context* pContext, const ma_context_c size_t i; for (i = 0; i < ma_countof(libpulseNames); ++i) { - pContext->pulse.pulseSO = ma_dlopen(ma_context_get_log(pContext), libpulseNames[i]); + pContext->pulse.pulseSO = ma_dlopen(pContext, libpulseNames[i]); if (pContext->pulse.pulseSO != NULL) { break; } @@ -31089,67 +30823,67 @@ static ma_result ma_context_init__pulse(ma_context* pContext, const ma_context_c return MA_NO_BACKEND; } - pContext->pulse.pa_mainloop_new = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_mainloop_new"); - pContext->pulse.pa_mainloop_free = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_mainloop_free"); - pContext->pulse.pa_mainloop_quit = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_mainloop_quit"); - pContext->pulse.pa_mainloop_get_api = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_mainloop_get_api"); - pContext->pulse.pa_mainloop_iterate = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_mainloop_iterate"); - pContext->pulse.pa_mainloop_wakeup = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_mainloop_wakeup"); - pContext->pulse.pa_threaded_mainloop_new = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_threaded_mainloop_new"); - pContext->pulse.pa_threaded_mainloop_free = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_threaded_mainloop_free"); - pContext->pulse.pa_threaded_mainloop_start = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_threaded_mainloop_start"); - pContext->pulse.pa_threaded_mainloop_stop = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_threaded_mainloop_stop"); - pContext->pulse.pa_threaded_mainloop_lock = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_threaded_mainloop_lock"); - pContext->pulse.pa_threaded_mainloop_unlock = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_threaded_mainloop_unlock"); - pContext->pulse.pa_threaded_mainloop_wait = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_threaded_mainloop_wait"); - pContext->pulse.pa_threaded_mainloop_signal = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_threaded_mainloop_signal"); - pContext->pulse.pa_threaded_mainloop_accept = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_threaded_mainloop_accept"); - pContext->pulse.pa_threaded_mainloop_get_retval = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_threaded_mainloop_get_retval"); - pContext->pulse.pa_threaded_mainloop_get_api = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_threaded_mainloop_get_api"); - pContext->pulse.pa_threaded_mainloop_in_thread = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_threaded_mainloop_in_thread"); - pContext->pulse.pa_threaded_mainloop_set_name = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_threaded_mainloop_set_name"); - pContext->pulse.pa_context_new = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_context_new"); - pContext->pulse.pa_context_unref = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_context_unref"); - pContext->pulse.pa_context_connect = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_context_connect"); - pContext->pulse.pa_context_disconnect = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_context_disconnect"); - pContext->pulse.pa_context_set_state_callback = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_context_set_state_callback"); - pContext->pulse.pa_context_get_state = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_context_get_state"); - pContext->pulse.pa_context_get_sink_info_list = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_context_get_sink_info_list"); - pContext->pulse.pa_context_get_source_info_list = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_context_get_source_info_list"); - pContext->pulse.pa_context_get_sink_info_by_name = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_context_get_sink_info_by_name"); - pContext->pulse.pa_context_get_source_info_by_name = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_context_get_source_info_by_name"); - pContext->pulse.pa_operation_unref = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_operation_unref"); - pContext->pulse.pa_operation_get_state = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_operation_get_state"); - pContext->pulse.pa_channel_map_init_extend = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_channel_map_init_extend"); - pContext->pulse.pa_channel_map_valid = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_channel_map_valid"); - pContext->pulse.pa_channel_map_compatible = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_channel_map_compatible"); - pContext->pulse.pa_stream_new = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_stream_new"); - pContext->pulse.pa_stream_unref = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_stream_unref"); - pContext->pulse.pa_stream_connect_playback = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_stream_connect_playback"); - pContext->pulse.pa_stream_connect_record = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_stream_connect_record"); - pContext->pulse.pa_stream_disconnect = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_stream_disconnect"); - pContext->pulse.pa_stream_get_state = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_stream_get_state"); - pContext->pulse.pa_stream_get_sample_spec = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_stream_get_sample_spec"); - pContext->pulse.pa_stream_get_channel_map = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_stream_get_channel_map"); - pContext->pulse.pa_stream_get_buffer_attr = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_stream_get_buffer_attr"); - pContext->pulse.pa_stream_set_buffer_attr = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_stream_set_buffer_attr"); - pContext->pulse.pa_stream_get_device_name = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_stream_get_device_name"); - pContext->pulse.pa_stream_set_write_callback = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_stream_set_write_callback"); - pContext->pulse.pa_stream_set_read_callback = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_stream_set_read_callback"); - pContext->pulse.pa_stream_set_suspended_callback = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_stream_set_suspended_callback"); - pContext->pulse.pa_stream_set_moved_callback = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_stream_set_moved_callback"); - pContext->pulse.pa_stream_is_suspended = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_stream_is_suspended"); - pContext->pulse.pa_stream_flush = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_stream_flush"); - pContext->pulse.pa_stream_drain = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_stream_drain"); - pContext->pulse.pa_stream_is_corked = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_stream_is_corked"); - pContext->pulse.pa_stream_cork = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_stream_cork"); - pContext->pulse.pa_stream_trigger = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_stream_trigger"); - pContext->pulse.pa_stream_begin_write = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_stream_begin_write"); - pContext->pulse.pa_stream_write = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_stream_write"); - pContext->pulse.pa_stream_peek = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_stream_peek"); - pContext->pulse.pa_stream_drop = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_stream_drop"); - pContext->pulse.pa_stream_writable_size = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_stream_writable_size"); - pContext->pulse.pa_stream_readable_size = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_stream_readable_size"); + pContext->pulse.pa_mainloop_new = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_mainloop_new"); + pContext->pulse.pa_mainloop_free = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_mainloop_free"); + pContext->pulse.pa_mainloop_quit = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_mainloop_quit"); + pContext->pulse.pa_mainloop_get_api = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_mainloop_get_api"); + pContext->pulse.pa_mainloop_iterate = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_mainloop_iterate"); + pContext->pulse.pa_mainloop_wakeup = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_mainloop_wakeup"); + pContext->pulse.pa_threaded_mainloop_new = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_threaded_mainloop_new"); + pContext->pulse.pa_threaded_mainloop_free = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_threaded_mainloop_free"); + pContext->pulse.pa_threaded_mainloop_start = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_threaded_mainloop_start"); + pContext->pulse.pa_threaded_mainloop_stop = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_threaded_mainloop_stop"); + pContext->pulse.pa_threaded_mainloop_lock = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_threaded_mainloop_lock"); + pContext->pulse.pa_threaded_mainloop_unlock = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_threaded_mainloop_unlock"); + pContext->pulse.pa_threaded_mainloop_wait = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_threaded_mainloop_wait"); + pContext->pulse.pa_threaded_mainloop_signal = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_threaded_mainloop_signal"); + pContext->pulse.pa_threaded_mainloop_accept = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_threaded_mainloop_accept"); + pContext->pulse.pa_threaded_mainloop_get_retval = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_threaded_mainloop_get_retval"); + pContext->pulse.pa_threaded_mainloop_get_api = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_threaded_mainloop_get_api"); + pContext->pulse.pa_threaded_mainloop_in_thread = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_threaded_mainloop_in_thread"); + pContext->pulse.pa_threaded_mainloop_set_name = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_threaded_mainloop_set_name"); + pContext->pulse.pa_context_new = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_context_new"); + pContext->pulse.pa_context_unref = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_context_unref"); + pContext->pulse.pa_context_connect = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_context_connect"); + pContext->pulse.pa_context_disconnect = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_context_disconnect"); + pContext->pulse.pa_context_set_state_callback = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_context_set_state_callback"); + pContext->pulse.pa_context_get_state = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_context_get_state"); + pContext->pulse.pa_context_get_sink_info_list = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_context_get_sink_info_list"); + pContext->pulse.pa_context_get_source_info_list = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_context_get_source_info_list"); + pContext->pulse.pa_context_get_sink_info_by_name = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_context_get_sink_info_by_name"); + pContext->pulse.pa_context_get_source_info_by_name = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_context_get_source_info_by_name"); + pContext->pulse.pa_operation_unref = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_operation_unref"); + pContext->pulse.pa_operation_get_state = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_operation_get_state"); + pContext->pulse.pa_channel_map_init_extend = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_channel_map_init_extend"); + pContext->pulse.pa_channel_map_valid = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_channel_map_valid"); + pContext->pulse.pa_channel_map_compatible = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_channel_map_compatible"); + pContext->pulse.pa_stream_new = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_stream_new"); + pContext->pulse.pa_stream_unref = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_stream_unref"); + pContext->pulse.pa_stream_connect_playback = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_stream_connect_playback"); + pContext->pulse.pa_stream_connect_record = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_stream_connect_record"); + pContext->pulse.pa_stream_disconnect = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_stream_disconnect"); + pContext->pulse.pa_stream_get_state = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_stream_get_state"); + pContext->pulse.pa_stream_get_sample_spec = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_stream_get_sample_spec"); + pContext->pulse.pa_stream_get_channel_map = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_stream_get_channel_map"); + pContext->pulse.pa_stream_get_buffer_attr = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_stream_get_buffer_attr"); + pContext->pulse.pa_stream_set_buffer_attr = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_stream_set_buffer_attr"); + pContext->pulse.pa_stream_get_device_name = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_stream_get_device_name"); + pContext->pulse.pa_stream_set_write_callback = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_stream_set_write_callback"); + pContext->pulse.pa_stream_set_read_callback = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_stream_set_read_callback"); + pContext->pulse.pa_stream_set_suspended_callback = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_stream_set_suspended_callback"); + pContext->pulse.pa_stream_set_moved_callback = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_stream_set_moved_callback"); + pContext->pulse.pa_stream_is_suspended = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_stream_is_suspended"); + pContext->pulse.pa_stream_flush = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_stream_flush"); + pContext->pulse.pa_stream_drain = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_stream_drain"); + pContext->pulse.pa_stream_is_corked = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_stream_is_corked"); + pContext->pulse.pa_stream_cork = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_stream_cork"); + pContext->pulse.pa_stream_trigger = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_stream_trigger"); + pContext->pulse.pa_stream_begin_write = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_stream_begin_write"); + pContext->pulse.pa_stream_write = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_stream_write"); + pContext->pulse.pa_stream_peek = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_stream_peek"); + pContext->pulse.pa_stream_drop = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_stream_drop"); + pContext->pulse.pa_stream_writable_size = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_stream_writable_size"); + pContext->pulse.pa_stream_readable_size = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_stream_readable_size"); #else /* This strange assignment system is just for type safety. */ ma_pa_mainloop_new_proc _pa_mainloop_new = pa_mainloop_new; @@ -31294,7 +31028,7 @@ static ma_result ma_context_init__pulse(ma_context* pContext, const ma_context_c ma_free(pContext->pulse.pServerName, &pContext->allocationCallbacks); ma_free(pContext->pulse.pApplicationName, &pContext->allocationCallbacks); #ifndef MA_NO_RUNTIME_LINKING - ma_dlclose(ma_context_get_log(pContext), pContext->pulse.pulseSO); + ma_dlclose(pContext, pContext->pulse.pulseSO); #endif return result; } @@ -31858,7 +31592,7 @@ static ma_result ma_context_uninit__jack(ma_context* pContext) pContext->jack.pClientName = NULL; #ifndef MA_NO_RUNTIME_LINKING - ma_dlclose(ma_context_get_log(pContext), pContext->jack.jackSO); + ma_dlclose(pContext, pContext->jack.jackSO); #endif return MA_SUCCESS; @@ -31880,7 +31614,7 @@ static ma_result ma_context_init__jack(ma_context* pContext, const ma_context_co size_t i; for (i = 0; i < ma_countof(libjackNames); ++i) { - pContext->jack.jackSO = ma_dlopen(ma_context_get_log(pContext), libjackNames[i]); + pContext->jack.jackSO = ma_dlopen(pContext, libjackNames[i]); if (pContext->jack.jackSO != NULL) { break; } @@ -31890,22 +31624,22 @@ static ma_result ma_context_init__jack(ma_context* pContext, const ma_context_co return MA_NO_BACKEND; } - pContext->jack.jack_client_open = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->jack.jackSO, "jack_client_open"); - pContext->jack.jack_client_close = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->jack.jackSO, "jack_client_close"); - pContext->jack.jack_client_name_size = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->jack.jackSO, "jack_client_name_size"); - pContext->jack.jack_set_process_callback = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->jack.jackSO, "jack_set_process_callback"); - pContext->jack.jack_set_buffer_size_callback = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->jack.jackSO, "jack_set_buffer_size_callback"); - pContext->jack.jack_on_shutdown = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->jack.jackSO, "jack_on_shutdown"); - pContext->jack.jack_get_sample_rate = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->jack.jackSO, "jack_get_sample_rate"); - pContext->jack.jack_get_buffer_size = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->jack.jackSO, "jack_get_buffer_size"); - pContext->jack.jack_get_ports = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->jack.jackSO, "jack_get_ports"); - pContext->jack.jack_activate = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->jack.jackSO, "jack_activate"); - pContext->jack.jack_deactivate = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->jack.jackSO, "jack_deactivate"); - pContext->jack.jack_connect = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->jack.jackSO, "jack_connect"); - pContext->jack.jack_port_register = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->jack.jackSO, "jack_port_register"); - pContext->jack.jack_port_name = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->jack.jackSO, "jack_port_name"); - pContext->jack.jack_port_get_buffer = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->jack.jackSO, "jack_port_get_buffer"); - pContext->jack.jack_free = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->jack.jackSO, "jack_free"); + pContext->jack.jack_client_open = (ma_proc)ma_dlsym(pContext, pContext->jack.jackSO, "jack_client_open"); + pContext->jack.jack_client_close = (ma_proc)ma_dlsym(pContext, pContext->jack.jackSO, "jack_client_close"); + pContext->jack.jack_client_name_size = (ma_proc)ma_dlsym(pContext, pContext->jack.jackSO, "jack_client_name_size"); + pContext->jack.jack_set_process_callback = (ma_proc)ma_dlsym(pContext, pContext->jack.jackSO, "jack_set_process_callback"); + pContext->jack.jack_set_buffer_size_callback = (ma_proc)ma_dlsym(pContext, pContext->jack.jackSO, "jack_set_buffer_size_callback"); + pContext->jack.jack_on_shutdown = (ma_proc)ma_dlsym(pContext, pContext->jack.jackSO, "jack_on_shutdown"); + pContext->jack.jack_get_sample_rate = (ma_proc)ma_dlsym(pContext, pContext->jack.jackSO, "jack_get_sample_rate"); + pContext->jack.jack_get_buffer_size = (ma_proc)ma_dlsym(pContext, pContext->jack.jackSO, "jack_get_buffer_size"); + pContext->jack.jack_get_ports = (ma_proc)ma_dlsym(pContext, pContext->jack.jackSO, "jack_get_ports"); + pContext->jack.jack_activate = (ma_proc)ma_dlsym(pContext, pContext->jack.jackSO, "jack_activate"); + pContext->jack.jack_deactivate = (ma_proc)ma_dlsym(pContext, pContext->jack.jackSO, "jack_deactivate"); + pContext->jack.jack_connect = (ma_proc)ma_dlsym(pContext, pContext->jack.jackSO, "jack_connect"); + pContext->jack.jack_port_register = (ma_proc)ma_dlsym(pContext, pContext->jack.jackSO, "jack_port_register"); + pContext->jack.jack_port_name = (ma_proc)ma_dlsym(pContext, pContext->jack.jackSO, "jack_port_name"); + pContext->jack.jack_port_get_buffer = (ma_proc)ma_dlsym(pContext, pContext->jack.jackSO, "jack_port_get_buffer"); + pContext->jack.jack_free = (ma_proc)ma_dlsym(pContext, pContext->jack.jackSO, "jack_free"); #else /* This strange assignment system is here just to ensure type safety of miniaudio's function pointer @@ -31961,7 +31695,7 @@ static ma_result ma_context_init__jack(ma_context* pContext, const ma_context_co if (result != MA_SUCCESS) { ma_free(pContext->jack.pClientName, &pContext->allocationCallbacks); #ifndef MA_NO_RUNTIME_LINKING - ma_dlclose(ma_context_get_log(pContext), pContext->jack.jackSO); + ma_dlclose(pContext, pContext->jack.jackSO); #endif return MA_NO_BACKEND; } @@ -31984,7 +31718,7 @@ static ma_result ma_context_init__jack(ma_context* pContext, const ma_context_co return MA_SUCCESS; } -#endif /* MA_HAS_JACK */ +#endif /* JACK */ @@ -32075,7 +31809,7 @@ that supports this level of detail. There was some public domain sample code I s and AudioUnit APIs, but I couldn't see anything that gave low-level control over device selection and capabilities (the distinction between playback and capture in particular). Therefore, miniaudio is using the AudioObject API. -Most (all?) functions in the AudioObject API take a AudioObjectID as its input. This is the device identifier. When +Most (all?) functions in the AudioObject API take a AudioObjectID as it's input. This is the device identifier. When retrieving global information, such as the device list, you use kAudioObjectSystemObject. When retrieving device-specific data, you pass in the ID for that device. In order to retrieve device-specific IDs you need to enumerate over each of the devices. This is done using the AudioObjectGetPropertyDataSize() and AudioObjectGetPropertyData() APIs which seem to be @@ -32095,18 +31829,6 @@ size, allocate a block of memory of that size and then call AudioObjectGetProper AudioDeviceID's so just do "dataSize/sizeof(AudioDeviceID)" to know the device count. */ -#if defined(MA_APPLE_MOBILE) -static void ma_device__on_notification_interruption_began(ma_device* pDevice) -{ - ma_device__on_notification(ma_device_notification_init(pDevice, ma_device_notification_type_interruption_began)); -} - -static void ma_device__on_notification_interruption_ended(ma_device* pDevice) -{ - ma_device__on_notification(ma_device_notification_init(pDevice, ma_device_notification_type_interruption_ended)); -} -#endif - static ma_result ma_result_from_OSStatus(OSStatus status) { switch (status) @@ -32410,12 +32132,6 @@ static ma_result ma_get_channel_map_from_AudioChannelLayout(AudioChannelLayout* #define AUDIO_OBJECT_PROPERTY_ELEMENT kAudioObjectPropertyElementMaster #endif -/* kAudioDevicePropertyScope* were renamed to kAudioObjectPropertyScope* in 10.8. */ -#if !defined(MAC_OS_X_VERSION_10_8) || (MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_8) -#define kAudioObjectPropertyScopeInput kAudioDevicePropertyScopeInput -#define kAudioObjectPropertyScopeOutput kAudioDevicePropertyScopeOutput -#endif - static ma_result ma_get_device_object_ids__coreaudio(ma_context* pContext, UInt32* pDeviceCount, AudioObjectID** ppDeviceObjectIDs) /* NOTE: Free the returned buffer with ma_free(). */ { AudioObjectPropertyAddress propAddressDevices; @@ -33005,7 +32721,7 @@ static ma_result ma_find_best_format__coreaudio(ma_context* pContext, AudioObjec desiredSampleRate = sampleRate; if (desiredSampleRate == 0) { - desiredSampleRate = (ma_uint32)pOrigFormat->mSampleRate; + desiredSampleRate = pOrigFormat->mSampleRate; } desiredChannelCount = channels; @@ -33029,9 +32745,9 @@ static ma_result ma_find_best_format__coreaudio(ma_context* pContext, AudioObjec hasSupportedFormat = MA_FALSE; for (iFormat = 0; iFormat < deviceFormatDescriptionCount; ++iFormat) { - ma_format formatFromDescription; - ma_result formatResult = ma_format_from_AudioStreamBasicDescription(&pDeviceFormatDescriptions[iFormat].mFormat, &formatFromDescription); - if (formatResult == MA_SUCCESS && formatFromDescription != ma_format_unknown) { + ma_format format; + ma_result formatResult = ma_format_from_AudioStreamBasicDescription(&pDeviceFormatDescriptions[iFormat].mFormat, &format); + if (formatResult == MA_SUCCESS && format != ma_format_unknown) { hasSupportedFormat = MA_TRUE; bestDeviceFormatSoFar = pDeviceFormatDescriptions[iFormat].mFormat; break; @@ -33648,7 +33364,7 @@ static OSStatus ma_on_output__coreaudio(void* pUserData, AudioUnitRenderActionFl } } else { /* This is the deinterleaved case. We need to update each buffer in groups of internalChannels. This assumes each buffer is the same size. */ - MA_ASSERT(pDevice->playback.internalChannels <= MA_MAX_CHANNELS); /* This should have been validated at initialization time. */ + MA_ASSERT(pDevice->playback.internalChannels <= MA_MAX_CHANNELS); /* This should heve been validated at initialization time. */ /* For safety we'll check that the internal channels is a multiple of the buffer count. If it's not it means something @@ -33739,12 +33455,11 @@ static OSStatus ma_on_input__coreaudio(void* pUserData, AudioUnitRenderActionFla */ for (iBuffer = 0; iBuffer < pRenderedBufferList->mNumberBuffers; ++iBuffer) { pRenderedBufferList->mBuffers[iBuffer].mDataByteSize = pDevice->coreaudio.audioBufferCapInFrames * ma_get_bytes_per_sample(pDevice->capture.internalFormat) * pRenderedBufferList->mBuffers[iBuffer].mNumberChannels; - /*printf("DEBUG: nDataByteSize = %d\n", (int)pRenderedBufferList->mBuffers[iBuffer].mDataByteSize);*/ } status = ((ma_AudioUnitRender_proc)pDevice->pContext->coreaudio.AudioUnitRender)((AudioUnit)pDevice->coreaudio.audioUnitCapture, pActionFlags, pTimeStamp, busNumber, frameCount, pRenderedBufferList); if (status != noErr) { - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "ERROR: AudioUnitRender() failed with %d.\n", (int)status); + ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, " ERROR: AudioUnitRender() failed with %d.\n", (int)status); return status; } @@ -33980,7 +33695,7 @@ static ma_result ma_context__init_device_tracking__coreaudio(ma_context* pContex ma_spinlock_lock(&g_DeviceTrackingInitLock_CoreAudio); { - /* Don't do anything if we've already initialized device tracking. */ + /* Don't do anything if we've already initializd device tracking. */ if (g_DeviceTrackingInitCounter_CoreAudio == 0) { AudioObjectPropertyAddress propAddress; propAddress.mScope = kAudioObjectPropertyScopeGlobal; @@ -34292,11 +34007,11 @@ typedef struct static ma_result ma_device_init_internal__coreaudio(ma_context* pContext, ma_device_type deviceType, const ma_device_id* pDeviceID, ma_device_init_internal_data__coreaudio* pData, void* pDevice_DoNotReference) /* <-- pDevice is typed as void* intentionally so as to avoid accidentally referencing it. */ { - ma_result result = MA_SUCCESS; + ma_result result; OSStatus status; UInt32 enableIOFlag; AudioStreamBasicDescription bestFormat; - ma_uint32 actualPeriodSizeInFrames; + UInt32 actualPeriodSizeInFrames; AURenderCallbackStruct callbackInfo; #if defined(MA_APPLE_DESKTOP) AudioObjectID deviceObjectID; @@ -34448,7 +34163,7 @@ static ma_result ma_device_init_internal__coreaudio(ma_context* pContext, ma_dev returning a result code of -10863. I have also tried changing the format directly on the input scope on the input bus, but this just results in `ca_require: IsStreamFormatWritable(inScope, inElement) NotWritable` when trying to set the format. - Something that does seem to work, however, has been setting the nominal sample rate on the device object. The problem with + Something that does seem to work, however, has been setting the nominal sample rate on the deivce object. The problem with this, however, is that it actually changes the sample rate at the operating system level and not just the application. This could be intrusive to the user, however, so I don't think it's wise to make this the default. Instead I'm making this a configuration option. When the `coreaudio.allowNominalSampleRateChange` config option is set to true, changing the sample @@ -34499,28 +34214,15 @@ static ma_result ma_device_init_internal__coreaudio(ma_context* pContext, ma_dev /* I've had a report that the channel count returned by AudioUnitGetProperty above is inconsistent with AVAudioSession outputNumberOfChannels. I'm going to try using the AVAudioSession values instead. - - UPDATE 20/02/2025: - When testing on the simulator with an iPhone 15 and iOS 17 I get an error when initializing the audio - unit if set the input channels to pAudioSession.inputNumberOfChannels. What is happening is the channel - count returned from AudioUnitGetProperty() above is set to 2, but pAudioSession is reporting a channel - count of 1. When this happens, the call to AudioUnitSetProprty() below just down below will succeed, but - AudioUnitInitialize() further down will fail. The only solution I have come up with is to not set the - channel count to pAudioSession.inputNumberOfChannels. */ if (deviceType == ma_device_type_playback) { bestFormat.mChannelsPerFrame = (UInt32)pAudioSession.outputNumberOfChannels; } - - #if 0 if (deviceType == ma_device_type_capture) { - /*printf("DEBUG: bestFormat.mChannelsPerFrame = %d; pAudioSession.inputNumberOfChannels = %d\n", (int)bestFormat.mChannelsPerFrame, (int)pAudioSession.inputNumberOfChannels);*/ bestFormat.mChannelsPerFrame = (UInt32)pAudioSession.inputNumberOfChannels; } - #endif } - status = ((ma_AudioUnitSetProperty_proc)pContext->coreaudio.AudioUnitSetProperty)(pData->audioUnit, kAudioUnitProperty_StreamFormat, formatScope, formatElement, &bestFormat, sizeof(bestFormat)); if (status != noErr) { ((ma_AudioComponentInstanceDispose_proc)pContext->coreaudio.AudioComponentInstanceDispose)(pData->audioUnit); @@ -34540,7 +34242,7 @@ static ma_result ma_device_init_internal__coreaudio(ma_context* pContext, ma_dev } pData->channelsOut = bestFormat.mChannelsPerFrame; - pData->sampleRateOut = (ma_uint32)bestFormat.mSampleRate; + pData->sampleRateOut = bestFormat.mSampleRate; } /* Clamp the channel count for safety. */ @@ -34847,7 +34549,7 @@ static ma_result ma_device_init__coreaudio(ma_device* pDevice, const ma_device_c ma_get_AudioObject_uid(pDevice->pContext, pDevice->coreaudio.deviceObjectIDCapture, sizeof(pDevice->capture.id.coreaudio), pDevice->capture.id.coreaudio); /* - If we are using the default device we'll need to listen for changes to the system's default device so we can seamlessly + If we are using the default device we'll need to listen for changes to the system's default device so we can seemlessly switch the device in the background. */ if (pConfig->capture.pDeviceID == NULL) { @@ -34911,7 +34613,7 @@ static ma_result ma_device_init__coreaudio(ma_device* pDevice, const ma_device_c ma_get_AudioObject_uid(pDevice->pContext, pDevice->coreaudio.deviceObjectIDPlayback, sizeof(pDevice->playback.id.coreaudio), pDevice->playback.id.coreaudio); /* - If we are using the default device we'll need to listen for changes to the system's default device so we can seamlessly + If we are using the default device we'll need to listen for changes to the system's default device so we can seemlessly switch the device in the background. */ if (pDescriptorPlayback->pDeviceID == NULL && (pConfig->deviceType != ma_device_type_duplex || pDescriptorCapture->pDeviceID != NULL)) { @@ -35005,9 +34707,9 @@ static ma_result ma_context_uninit__coreaudio(ma_context* pContext) #endif #if !defined(MA_NO_RUNTIME_LINKING) && !defined(MA_APPLE_MOBILE) - ma_dlclose(ma_context_get_log(pContext), pContext->coreaudio.hAudioUnit); - ma_dlclose(ma_context_get_log(pContext), pContext->coreaudio.hCoreAudio); - ma_dlclose(ma_context_get_log(pContext), pContext->coreaudio.hCoreFoundation); + ma_dlclose(pContext, pContext->coreaudio.hAudioUnit); + ma_dlclose(pContext, pContext->coreaudio.hCoreAudio); + ma_dlclose(pContext, pContext->coreaudio.hCoreFoundation); #endif #if !defined(MA_APPLE_MOBILE) @@ -35096,26 +34798,26 @@ static ma_result ma_context_init__coreaudio(ma_context* pContext, const ma_conte #endif #if !defined(MA_NO_RUNTIME_LINKING) && !defined(MA_APPLE_MOBILE) - pContext->coreaudio.hCoreFoundation = ma_dlopen(ma_context_get_log(pContext), "/System/Library/Frameworks/CoreFoundation.framework/CoreFoundation"); + pContext->coreaudio.hCoreFoundation = ma_dlopen(pContext, "CoreFoundation.framework/CoreFoundation"); if (pContext->coreaudio.hCoreFoundation == NULL) { return MA_API_NOT_FOUND; } - pContext->coreaudio.CFStringGetCString = ma_dlsym(ma_context_get_log(pContext), pContext->coreaudio.hCoreFoundation, "CFStringGetCString"); - pContext->coreaudio.CFRelease = ma_dlsym(ma_context_get_log(pContext), pContext->coreaudio.hCoreFoundation, "CFRelease"); + pContext->coreaudio.CFStringGetCString = ma_dlsym(pContext, pContext->coreaudio.hCoreFoundation, "CFStringGetCString"); + pContext->coreaudio.CFRelease = ma_dlsym(pContext, pContext->coreaudio.hCoreFoundation, "CFRelease"); - pContext->coreaudio.hCoreAudio = ma_dlopen(ma_context_get_log(pContext), "/System/Library/Frameworks/CoreAudio.framework/CoreAudio"); + pContext->coreaudio.hCoreAudio = ma_dlopen(pContext, "CoreAudio.framework/CoreAudio"); if (pContext->coreaudio.hCoreAudio == NULL) { - ma_dlclose(ma_context_get_log(pContext), pContext->coreaudio.hCoreFoundation); + ma_dlclose(pContext, pContext->coreaudio.hCoreFoundation); return MA_API_NOT_FOUND; } - pContext->coreaudio.AudioObjectGetPropertyData = ma_dlsym(ma_context_get_log(pContext), pContext->coreaudio.hCoreAudio, "AudioObjectGetPropertyData"); - pContext->coreaudio.AudioObjectGetPropertyDataSize = ma_dlsym(ma_context_get_log(pContext), pContext->coreaudio.hCoreAudio, "AudioObjectGetPropertyDataSize"); - pContext->coreaudio.AudioObjectSetPropertyData = ma_dlsym(ma_context_get_log(pContext), pContext->coreaudio.hCoreAudio, "AudioObjectSetPropertyData"); - pContext->coreaudio.AudioObjectAddPropertyListener = ma_dlsym(ma_context_get_log(pContext), pContext->coreaudio.hCoreAudio, "AudioObjectAddPropertyListener"); - pContext->coreaudio.AudioObjectRemovePropertyListener = ma_dlsym(ma_context_get_log(pContext), pContext->coreaudio.hCoreAudio, "AudioObjectRemovePropertyListener"); + pContext->coreaudio.AudioObjectGetPropertyData = ma_dlsym(pContext, pContext->coreaudio.hCoreAudio, "AudioObjectGetPropertyData"); + pContext->coreaudio.AudioObjectGetPropertyDataSize = ma_dlsym(pContext, pContext->coreaudio.hCoreAudio, "AudioObjectGetPropertyDataSize"); + pContext->coreaudio.AudioObjectSetPropertyData = ma_dlsym(pContext, pContext->coreaudio.hCoreAudio, "AudioObjectSetPropertyData"); + pContext->coreaudio.AudioObjectAddPropertyListener = ma_dlsym(pContext, pContext->coreaudio.hCoreAudio, "AudioObjectAddPropertyListener"); + pContext->coreaudio.AudioObjectRemovePropertyListener = ma_dlsym(pContext, pContext->coreaudio.hCoreAudio, "AudioObjectRemovePropertyListener"); /* It looks like Apple has moved some APIs from AudioUnit into AudioToolbox on more recent versions of macOS. They are still @@ -35123,35 +34825,35 @@ static ma_result ma_context_init__coreaudio(ma_context* pContext, const ma_conte The way it'll work is that it'll first try AudioUnit, and if the required symbols are not present there we'll fall back to AudioToolbox. */ - pContext->coreaudio.hAudioUnit = ma_dlopen(ma_context_get_log(pContext), "/System/Library/Frameworks/AudioUnit.framework/AudioUnit"); + pContext->coreaudio.hAudioUnit = ma_dlopen(pContext, "AudioUnit.framework/AudioUnit"); if (pContext->coreaudio.hAudioUnit == NULL) { - ma_dlclose(ma_context_get_log(pContext), pContext->coreaudio.hCoreAudio); - ma_dlclose(ma_context_get_log(pContext), pContext->coreaudio.hCoreFoundation); + ma_dlclose(pContext, pContext->coreaudio.hCoreAudio); + ma_dlclose(pContext, pContext->coreaudio.hCoreFoundation); return MA_API_NOT_FOUND; } - if (ma_dlsym(ma_context_get_log(pContext), pContext->coreaudio.hAudioUnit, "AudioComponentFindNext") == NULL) { + if (ma_dlsym(pContext, pContext->coreaudio.hAudioUnit, "AudioComponentFindNext") == NULL) { /* Couldn't find the required symbols in AudioUnit, so fall back to AudioToolbox. */ - ma_dlclose(ma_context_get_log(pContext), pContext->coreaudio.hAudioUnit); - pContext->coreaudio.hAudioUnit = ma_dlopen(ma_context_get_log(pContext), "/System/Library/Frameworks/AudioToolbox.framework/AudioToolbox"); + ma_dlclose(pContext, pContext->coreaudio.hAudioUnit); + pContext->coreaudio.hAudioUnit = ma_dlopen(pContext, "AudioToolbox.framework/AudioToolbox"); if (pContext->coreaudio.hAudioUnit == NULL) { - ma_dlclose(ma_context_get_log(pContext), pContext->coreaudio.hCoreAudio); - ma_dlclose(ma_context_get_log(pContext), pContext->coreaudio.hCoreFoundation); + ma_dlclose(pContext, pContext->coreaudio.hCoreAudio); + ma_dlclose(pContext, pContext->coreaudio.hCoreFoundation); return MA_API_NOT_FOUND; } } - pContext->coreaudio.AudioComponentFindNext = ma_dlsym(ma_context_get_log(pContext), pContext->coreaudio.hAudioUnit, "AudioComponentFindNext"); - pContext->coreaudio.AudioComponentInstanceDispose = ma_dlsym(ma_context_get_log(pContext), pContext->coreaudio.hAudioUnit, "AudioComponentInstanceDispose"); - pContext->coreaudio.AudioComponentInstanceNew = ma_dlsym(ma_context_get_log(pContext), pContext->coreaudio.hAudioUnit, "AudioComponentInstanceNew"); - pContext->coreaudio.AudioOutputUnitStart = ma_dlsym(ma_context_get_log(pContext), pContext->coreaudio.hAudioUnit, "AudioOutputUnitStart"); - pContext->coreaudio.AudioOutputUnitStop = ma_dlsym(ma_context_get_log(pContext), pContext->coreaudio.hAudioUnit, "AudioOutputUnitStop"); - pContext->coreaudio.AudioUnitAddPropertyListener = ma_dlsym(ma_context_get_log(pContext), pContext->coreaudio.hAudioUnit, "AudioUnitAddPropertyListener"); - pContext->coreaudio.AudioUnitGetPropertyInfo = ma_dlsym(ma_context_get_log(pContext), pContext->coreaudio.hAudioUnit, "AudioUnitGetPropertyInfo"); - pContext->coreaudio.AudioUnitGetProperty = ma_dlsym(ma_context_get_log(pContext), pContext->coreaudio.hAudioUnit, "AudioUnitGetProperty"); - pContext->coreaudio.AudioUnitSetProperty = ma_dlsym(ma_context_get_log(pContext), pContext->coreaudio.hAudioUnit, "AudioUnitSetProperty"); - pContext->coreaudio.AudioUnitInitialize = ma_dlsym(ma_context_get_log(pContext), pContext->coreaudio.hAudioUnit, "AudioUnitInitialize"); - pContext->coreaudio.AudioUnitRender = ma_dlsym(ma_context_get_log(pContext), pContext->coreaudio.hAudioUnit, "AudioUnitRender"); + pContext->coreaudio.AudioComponentFindNext = ma_dlsym(pContext, pContext->coreaudio.hAudioUnit, "AudioComponentFindNext"); + pContext->coreaudio.AudioComponentInstanceDispose = ma_dlsym(pContext, pContext->coreaudio.hAudioUnit, "AudioComponentInstanceDispose"); + pContext->coreaudio.AudioComponentInstanceNew = ma_dlsym(pContext, pContext->coreaudio.hAudioUnit, "AudioComponentInstanceNew"); + pContext->coreaudio.AudioOutputUnitStart = ma_dlsym(pContext, pContext->coreaudio.hAudioUnit, "AudioOutputUnitStart"); + pContext->coreaudio.AudioOutputUnitStop = ma_dlsym(pContext, pContext->coreaudio.hAudioUnit, "AudioOutputUnitStop"); + pContext->coreaudio.AudioUnitAddPropertyListener = ma_dlsym(pContext, pContext->coreaudio.hAudioUnit, "AudioUnitAddPropertyListener"); + pContext->coreaudio.AudioUnitGetPropertyInfo = ma_dlsym(pContext, pContext->coreaudio.hAudioUnit, "AudioUnitGetPropertyInfo"); + pContext->coreaudio.AudioUnitGetProperty = ma_dlsym(pContext, pContext->coreaudio.hAudioUnit, "AudioUnitGetProperty"); + pContext->coreaudio.AudioUnitSetProperty = ma_dlsym(pContext, pContext->coreaudio.hAudioUnit, "AudioUnitSetProperty"); + pContext->coreaudio.AudioUnitInitialize = ma_dlsym(pContext, pContext->coreaudio.hAudioUnit, "AudioUnitInitialize"); + pContext->coreaudio.AudioUnitRender = ma_dlsym(pContext, pContext->coreaudio.hAudioUnit, "AudioUnitRender"); #else pContext->coreaudio.CFStringGetCString = (ma_proc)CFStringGetCString; pContext->coreaudio.CFRelease = (ma_proc)CFRelease; @@ -35193,9 +34895,9 @@ static ma_result ma_context_init__coreaudio(ma_context* pContext, const ma_conte pContext->coreaudio.component = ((ma_AudioComponentFindNext_proc)pContext->coreaudio.AudioComponentFindNext)(NULL, &desc); if (pContext->coreaudio.component == NULL) { #if !defined(MA_NO_RUNTIME_LINKING) && !defined(MA_APPLE_MOBILE) - ma_dlclose(ma_context_get_log(pContext), pContext->coreaudio.hAudioUnit); - ma_dlclose(ma_context_get_log(pContext), pContext->coreaudio.hCoreAudio); - ma_dlclose(ma_context_get_log(pContext), pContext->coreaudio.hCoreFoundation); + ma_dlclose(pContext, pContext->coreaudio.hAudioUnit); + ma_dlclose(pContext, pContext->coreaudio.hCoreAudio); + ma_dlclose(pContext, pContext->coreaudio.hCoreFoundation); #endif return MA_FAILED_TO_INIT_BACKEND; } @@ -35205,9 +34907,9 @@ static ma_result ma_context_init__coreaudio(ma_context* pContext, const ma_conte result = ma_context__init_device_tracking__coreaudio(pContext); if (result != MA_SUCCESS) { #if !defined(MA_NO_RUNTIME_LINKING) && !defined(MA_APPLE_MOBILE) - ma_dlclose(ma_context_get_log(pContext), pContext->coreaudio.hAudioUnit); - ma_dlclose(ma_context_get_log(pContext), pContext->coreaudio.hCoreAudio); - ma_dlclose(ma_context_get_log(pContext), pContext->coreaudio.hCoreFoundation); + ma_dlclose(pContext, pContext->coreaudio.hAudioUnit); + ma_dlclose(pContext, pContext->coreaudio.hCoreAudio); + ma_dlclose(pContext, pContext->coreaudio.hCoreFoundation); #endif return result; } @@ -35229,7 +34931,7 @@ static ma_result ma_context_init__coreaudio(ma_context* pContext, const ma_conte return MA_SUCCESS; } -#endif /* MA_HAS_COREAUDIO */ +#endif /* Core Audio */ @@ -35721,7 +35423,7 @@ static ma_result ma_device_uninit__sndio(ma_device* pDevice) ((ma_sio_close_proc)pDevice->pContext->sndio.sio_close)((struct ma_sio_hdl*)pDevice->sndio.handleCapture); } - if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) { + if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) { ((ma_sio_close_proc)pDevice->pContext->sndio.sio_close)((struct ma_sio_hdl*)pDevice->sndio.handlePlayback); } @@ -36028,7 +35730,7 @@ static ma_result ma_context_init__sndio(ma_context* pContext, const ma_context_c size_t i; for (i = 0; i < ma_countof(libsndioNames); ++i) { - pContext->sndio.sndioSO = ma_dlopen(ma_context_get_log(pContext), libsndioNames[i]); + pContext->sndio.sndioSO = ma_dlopen(pContext, libsndioNames[i]); if (pContext->sndio.sndioSO != NULL) { break; } @@ -36038,16 +35740,16 @@ static ma_result ma_context_init__sndio(ma_context* pContext, const ma_context_c return MA_NO_BACKEND; } - pContext->sndio.sio_open = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->sndio.sndioSO, "sio_open"); - pContext->sndio.sio_close = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->sndio.sndioSO, "sio_close"); - pContext->sndio.sio_setpar = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->sndio.sndioSO, "sio_setpar"); - pContext->sndio.sio_getpar = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->sndio.sndioSO, "sio_getpar"); - pContext->sndio.sio_getcap = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->sndio.sndioSO, "sio_getcap"); - pContext->sndio.sio_write = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->sndio.sndioSO, "sio_write"); - pContext->sndio.sio_read = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->sndio.sndioSO, "sio_read"); - pContext->sndio.sio_start = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->sndio.sndioSO, "sio_start"); - pContext->sndio.sio_stop = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->sndio.sndioSO, "sio_stop"); - pContext->sndio.sio_initpar = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->sndio.sndioSO, "sio_initpar"); + pContext->sndio.sio_open = (ma_proc)ma_dlsym(pContext, pContext->sndio.sndioSO, "sio_open"); + pContext->sndio.sio_close = (ma_proc)ma_dlsym(pContext, pContext->sndio.sndioSO, "sio_close"); + pContext->sndio.sio_setpar = (ma_proc)ma_dlsym(pContext, pContext->sndio.sndioSO, "sio_setpar"); + pContext->sndio.sio_getpar = (ma_proc)ma_dlsym(pContext, pContext->sndio.sndioSO, "sio_getpar"); + pContext->sndio.sio_getcap = (ma_proc)ma_dlsym(pContext, pContext->sndio.sndioSO, "sio_getcap"); + pContext->sndio.sio_write = (ma_proc)ma_dlsym(pContext, pContext->sndio.sndioSO, "sio_write"); + pContext->sndio.sio_read = (ma_proc)ma_dlsym(pContext, pContext->sndio.sndioSO, "sio_read"); + pContext->sndio.sio_start = (ma_proc)ma_dlsym(pContext, pContext->sndio.sndioSO, "sio_start"); + pContext->sndio.sio_stop = (ma_proc)ma_dlsym(pContext, pContext->sndio.sndioSO, "sio_stop"); + pContext->sndio.sio_initpar = (ma_proc)ma_dlsym(pContext, pContext->sndio.sndioSO, "sio_initpar"); #else pContext->sndio.sio_open = sio_open; pContext->sndio.sio_close = sio_close; @@ -36076,7 +35778,7 @@ static ma_result ma_context_init__sndio(ma_context* pContext, const ma_context_c (void)pConfig; return MA_SUCCESS; } -#endif /* MA_HAS_SNDIO */ +#endif /* sndio */ @@ -36094,10 +35796,6 @@ audio(4) Backend #include #include -#ifdef __NetBSD__ -#include -#endif - #if defined(__OpenBSD__) #include #if defined(OpenBSD) && OpenBSD >= 201709 @@ -36317,15 +36015,9 @@ static ma_result ma_context_get_device_info_from_fd__audio4(ma_context* pContext ma_uint32 channels; ma_uint32 sampleRate; -#if defined(__NetBSD__) && (__NetBSD_Version__ >= 900000000) - if (ioctl(fd, AUDIO_GETFORMAT, &fdInfo) < 0) { - return MA_ERROR; - } -#else if (ioctl(fd, AUDIO_GETINFO, &fdInfo) < 0) { return MA_ERROR; } -#endif if (deviceType == ma_device_type_playback) { channels = fdInfo.play.channels; @@ -36603,11 +36295,7 @@ static ma_result ma_device_init_fd__audio4(ma_device* pDevice, const ma_device_c /* We're using a default device. Get the info from the /dev/audioctl file instead of /dev/audio. */ int fdctl = open(pDefaultDeviceCtlNames[iDefaultDevice], fdFlags, 0); if (fdctl != -1) { -#if defined(__NetBSD__) && (__NetBSD_Version__ >= 900000000) - fdInfoResult = ioctl(fdctl, AUDIO_GETFORMAT, &fdInfo); -#else fdInfoResult = ioctl(fdctl, AUDIO_GETINFO, &fdInfo); -#endif close(fdctl); } } @@ -36974,7 +36662,7 @@ static ma_result ma_context_init__audio4(ma_context* pContext, const ma_context_ return MA_SUCCESS; } -#endif /* MA_HAS_AUDIO4 */ +#endif /* audio4 */ /****************************************************************************** @@ -37337,7 +37025,7 @@ static ma_result ma_device_init_fd__oss(ma_device* pDevice, const ma_device_conf } /* - The OSS documentation is very clear about the order we should be initializing the device's properties: + The OSS documantation is very clear about the order we should be initializing the device's properties: 1) Format 2) Channels 3) Sample rate. @@ -37605,7 +37293,7 @@ static ma_result ma_context_init__oss(ma_context* pContext, const ma_context_con return MA_SUCCESS; } -#endif /* MA_HAS_OSS */ +#endif /* OSS */ @@ -37618,9 +37306,7 @@ AAudio Backend ******************************************************************************/ #ifdef MA_HAS_AAUDIO -#ifdef MA_NO_RUNTIME_LINKING - #include -#endif +/*#include */ typedef int32_t ma_aaudio_result_t; typedef int32_t ma_aaudio_direction_t; @@ -37833,7 +37519,9 @@ static void ma_stream_error_callback__aaudio(ma_AAudioStream* pStream, void* pUs MA_ASSERT(pDevice != NULL); (void)error; + ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, "[AAudio] ERROR CALLBACK: error=%d, AAudioStream_getState()=%d\n", error, ((MA_PFN_AAudioStream_getState)pDevice->pContext->aaudio.AAudioStream_getState)(pStream)); + /* When we get an error, we'll assume that the stream is in an erroneous state and needs to be restarted. From the documentation, we cannot do this from the error callback. Therefore we are going to use an event thread for the AAudio backend to do this @@ -37861,9 +37549,7 @@ static ma_aaudio_data_callback_result_t ma_stream_data_callback_capture__aaudio( ma_device* pDevice = (ma_device*)pUserData; MA_ASSERT(pDevice != NULL); - if (frameCount > 0) { - ma_device_handle_backend_data_callback(pDevice, NULL, pAudioData, (ma_uint32)frameCount); - } + ma_device_handle_backend_data_callback(pDevice, NULL, pAudioData, frameCount); (void)pStream; return MA_AAUDIO_CALLBACK_RESULT_CONTINUE; @@ -37874,14 +37560,7 @@ static ma_aaudio_data_callback_result_t ma_stream_data_callback_playback__aaudio ma_device* pDevice = (ma_device*)pUserData; MA_ASSERT(pDevice != NULL); - /* - I've had a report that AAudio can sometimes post a frame count of 0. We need to check for that here - so we don't get any errors at a deeper level. I'm doing the same with the capture side for safety, - though I've not yet had any reports about that one. - */ - if (frameCount > 0) { - ma_device_handle_backend_data_callback(pDevice, pAudioData, NULL, (ma_uint32)frameCount); - } + ma_device_handle_backend_data_callback(pDevice, pAudioData, NULL, frameCount); (void)pStream; return MA_AAUDIO_CALLBACK_RESULT_CONTINUE; @@ -37916,25 +37595,32 @@ static ma_result ma_create_and_configure_AAudioStreamBuilder__aaudio(ma_context* ((MA_PFN_AAudioStreamBuilder_setSampleRate)pContext->aaudio.AAudioStreamBuilder_setSampleRate)(pBuilder, pDescriptor->sampleRate); } - if (pDescriptor->channels != 0) { - ((MA_PFN_AAudioStreamBuilder_setChannelCount)pContext->aaudio.AAudioStreamBuilder_setChannelCount)(pBuilder, pDescriptor->channels); - } - - if (pDescriptor->format != ma_format_unknown) { - ((MA_PFN_AAudioStreamBuilder_setFormat)pContext->aaudio.AAudioStreamBuilder_setFormat)(pBuilder, (pDescriptor->format == ma_format_s16) ? MA_AAUDIO_FORMAT_PCM_I16 : MA_AAUDIO_FORMAT_PCM_FLOAT); + if (deviceType == ma_device_type_capture) { + if (pDescriptor->channels != 0) { + ((MA_PFN_AAudioStreamBuilder_setChannelCount)pContext->aaudio.AAudioStreamBuilder_setChannelCount)(pBuilder, pDescriptor->channels); + } + if (pDescriptor->format != ma_format_unknown) { + ((MA_PFN_AAudioStreamBuilder_setFormat)pContext->aaudio.AAudioStreamBuilder_setFormat)(pBuilder, (pDescriptor->format == ma_format_s16) ? MA_AAUDIO_FORMAT_PCM_I16 : MA_AAUDIO_FORMAT_PCM_FLOAT); + } + } else { + if (pDescriptor->channels != 0) { + ((MA_PFN_AAudioStreamBuilder_setChannelCount)pContext->aaudio.AAudioStreamBuilder_setChannelCount)(pBuilder, pDescriptor->channels); + } + if (pDescriptor->format != ma_format_unknown) { + ((MA_PFN_AAudioStreamBuilder_setFormat)pContext->aaudio.AAudioStreamBuilder_setFormat)(pBuilder, (pDescriptor->format == ma_format_s16) ? MA_AAUDIO_FORMAT_PCM_I16 : MA_AAUDIO_FORMAT_PCM_FLOAT); + } } /* - There have been reports where setting the frames per data callback results in an error. - In particular, re-routing may inadvertently switch from low-latency mode, resulting in a less stable - stream from the legacy path (AudioStreamLegacy). To address this, we simply don't set the value. It - can still be set if it's explicitly requested via the aaudio.allowSetBufferCapacity variable in the - device config. + There have been reports where setting the frames per data callback results in an error + later on from Android. To address this, I'm experimenting with simply not setting it on + anything from Android 11 and earlier. Suggestions welcome on how we might be able to make + this more targetted. */ - if ((!pConfig->aaudio.enableCompatibilityWorkarounds || ma_android_sdk_version() > 30) && pConfig->aaudio.allowSetBufferCapacity) { + if (pConfig->aaudio.enableCompatibilityWorkarounds && ma_android_sdk_version() > 30) { /* - AAudio is annoying when it comes to its buffer calculation stuff because it doesn't let you + AAudio is annoying when it comes to it's buffer calculation stuff because it doesn't let you retrieve the actual sample rate until after you've opened the stream. But you need to configure the buffer capacity before you open the stream... :/ @@ -37968,11 +37654,7 @@ static ma_result ma_create_and_configure_AAudioStreamBuilder__aaudio(ma_context* ((MA_PFN_AAudioStreamBuilder_setDataCallback)pContext->aaudio.AAudioStreamBuilder_setDataCallback)(pBuilder, ma_stream_data_callback_playback__aaudio, (void*)pDevice); } - /* - If we set AAUDIO_PERFORMANCE_MODE_LOW_LATENCY, we allow for MMAP (non-legacy path). - Since there's a mapping between miniaudio's performance profiles and AAudio's performance modes, let's use it. - Beware though, with a conservative performance profile, AAudio will indeed take the legacy path. - */ + /* Not sure how this affects things, but since there's a mapping between miniaudio's performance profiles and AAudio's performance modes, let go ahead and set it. */ ((MA_PFN_AAudioStreamBuilder_setPerformanceMode)pContext->aaudio.AAudioStreamBuilder_setPerformanceMode)(pBuilder, (pConfig->performanceProfile == ma_performance_profile_low_latency) ? MA_AAUDIO_PERFORMANCE_MODE_LOW_LATENCY : MA_AAUDIO_PERFORMANCE_MODE_NONE); /* We need to set an error callback to detect device changes. */ @@ -38008,9 +37690,6 @@ static ma_result ma_open_stream_basic__aaudio(ma_context* pContext, const ma_dev return result; } - /* Let's give AAudio a hint to avoid the legacy path (AudioStreamLegacy). */ - ((MA_PFN_AAudioStreamBuilder_setPerformanceMode)pContext->aaudio.AAudioStreamBuilder_setPerformanceMode)(pBuilder, MA_AAUDIO_PERFORMANCE_MODE_LOW_LATENCY); - return ma_open_stream_and_close_builder__aaudio(pContext, pBuilder, ppStream); } @@ -38035,10 +37714,6 @@ static ma_result ma_open_stream__aaudio(ma_device* pDevice, const ma_device_conf static ma_result ma_close_stream__aaudio(ma_context* pContext, ma_AAudioStream* pStream) { - if (pStream == NULL) { - return MA_INVALID_ARGS; - } - return ma_result_from_aaudio(((MA_PFN_AAudioStream_close)pContext->aaudio.AAudioStream_close)(pStream)); } @@ -38165,15 +37840,16 @@ static ma_result ma_context_get_device_info__aaudio(ma_context* pContext, ma_dev return MA_SUCCESS; } -static ma_result ma_close_streams__aaudio(ma_device* pDevice) + +static ma_result ma_device_uninit__aaudio(ma_device* pDevice) { MA_ASSERT(pDevice != NULL); - /* When re-routing, streams may have been closed and never re-opened. Hence the extra checks below. */ if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) { ma_close_stream__aaudio(pDevice->pContext, (ma_AAudioStream*)pDevice->aaudio.pStreamCapture); pDevice->aaudio.pStreamCapture = NULL; } + if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) { ma_close_stream__aaudio(pDevice->pContext, (ma_AAudioStream*)pDevice->aaudio.pStreamPlayback); pDevice->aaudio.pStreamPlayback = NULL; @@ -38182,23 +37858,6 @@ static ma_result ma_close_streams__aaudio(ma_device* pDevice) return MA_SUCCESS; } -static ma_result ma_device_uninit__aaudio(ma_device* pDevice) -{ - MA_ASSERT(pDevice != NULL); - - /* Wait for any rerouting to finish before attempting to close the streams. */ - ma_mutex_lock(&pDevice->aaudio.rerouteLock); - { - ma_close_streams__aaudio(pDevice); - } - ma_mutex_unlock(&pDevice->aaudio.rerouteLock); - - /* Destroy re-routing lock. */ - ma_mutex_uninit(&pDevice->aaudio.rerouteLock); - - return MA_SUCCESS; -} - static ma_result ma_device_init_by_type__aaudio(ma_device* pDevice, const ma_device_config* pConfig, ma_device_type deviceType, ma_device_descriptor* pDescriptor, ma_AAudioStream** ppStream) { ma_result result; @@ -38246,7 +37905,7 @@ static ma_result ma_device_init_by_type__aaudio(ma_device* pDevice, const ma_dev return MA_SUCCESS; } -static ma_result ma_device_init_streams__aaudio(ma_device* pDevice, const ma_device_config* pConfig, ma_device_descriptor* pDescriptorPlayback, ma_device_descriptor* pDescriptorCapture) +static ma_result ma_device_init__aaudio(ma_device* pDevice, const ma_device_config* pConfig, ma_device_descriptor* pDescriptorPlayback, ma_device_descriptor* pDescriptorCapture) { ma_result result; @@ -38279,25 +37938,6 @@ static ma_result ma_device_init_streams__aaudio(ma_device* pDevice, const ma_dev return MA_SUCCESS; } -static ma_result ma_device_init__aaudio(ma_device* pDevice, const ma_device_config* pConfig, ma_device_descriptor* pDescriptorPlayback, ma_device_descriptor* pDescriptorCapture) -{ - ma_result result; - - MA_ASSERT(pDevice != NULL); - - result = ma_device_init_streams__aaudio(pDevice, pConfig, pDescriptorPlayback, pDescriptorCapture); - if (result != MA_SUCCESS) { - return result; - } - - result = ma_mutex_init(&pDevice->aaudio.rerouteLock); - if (result != MA_SUCCESS) { - return result; - } - - return MA_SUCCESS; -} - static ma_result ma_device_start_stream__aaudio(ma_device* pDevice, ma_AAudioStream* pStream) { ma_aaudio_result_t resultAA; @@ -38305,16 +37945,12 @@ static ma_result ma_device_start_stream__aaudio(ma_device* pDevice, ma_AAudioStr MA_ASSERT(pDevice != NULL); - if (pStream == NULL) { - return MA_INVALID_ARGS; - } - resultAA = ((MA_PFN_AAudioStream_requestStart)pDevice->pContext->aaudio.AAudioStream_requestStart)(pStream); if (resultAA != MA_AAUDIO_OK) { return ma_result_from_aaudio(resultAA); } - /* Do we actually need to wait for the device to transition into its started state? */ + /* Do we actually need to wait for the device to transition into it's started state? */ /* The device should be in either a starting or started state. If it's not set to started we need to wait for it to transition. It should go from starting to started. */ currentState = ((MA_PFN_AAudioStream_getState)pDevice->pContext->aaudio.AAudioStream_getState)(pStream); @@ -38341,10 +37977,6 @@ static ma_result ma_device_stop_stream__aaudio(ma_device* pDevice, ma_AAudioStre MA_ASSERT(pDevice != NULL); - if (pStream == NULL) { - return MA_INVALID_ARGS; - } - /* From the AAudio documentation: @@ -38430,20 +38062,22 @@ static ma_result ma_device_stop__aaudio(ma_device* pDevice) static ma_result ma_device_reinit__aaudio(ma_device* pDevice, ma_device_type deviceType) { ma_result result; - int32_t retries = 0; MA_ASSERT(pDevice != NULL); - /* - TODO: Stop retrying if main thread is about to uninit device. - */ - ma_mutex_lock(&pDevice->aaudio.rerouteLock); - { -error_disconnected: - /* The first thing to do is close the streams. */ - ma_close_streams__aaudio(pDevice); + /* The first thing to do is close the streams. */ + if (deviceType == ma_device_type_capture || deviceType == ma_device_type_duplex) { + ma_close_stream__aaudio(pDevice->pContext, (ma_AAudioStream*)pDevice->aaudio.pStreamCapture); + pDevice->aaudio.pStreamCapture = NULL; + } - /* Now we need to reinitialize each streams. The hardest part with this is just filling output the config and descriptors. */ + if (deviceType == ma_device_type_playback || deviceType == ma_device_type_duplex) { + ma_close_stream__aaudio(pDevice->pContext, (ma_AAudioStream*)pDevice->aaudio.pStreamPlayback); + pDevice->aaudio.pStreamPlayback = NULL; + } + + /* Now we need to reinitialize each streams. The hardest part with this is just filling output the config and descriptors. */ + { ma_device_config deviceConfig; ma_device_descriptor descriptorPlayback; ma_device_descriptor descriptorCapture; @@ -38492,17 +38126,15 @@ static ma_result ma_device_reinit__aaudio(ma_device* pDevice, ma_device_type dev descriptorPlayback.periodCount = deviceConfig.periods; } - result = ma_device_init_streams__aaudio(pDevice, &deviceConfig, &descriptorPlayback, &descriptorCapture); + result = ma_device_init__aaudio(pDevice, &deviceConfig, &descriptorPlayback, &descriptorCapture); if (result != MA_SUCCESS) { - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_WARNING, "[AAudio] Failed to create stream after route change."); - goto done; + return result; } result = ma_device_post_init(pDevice, deviceType, &descriptorPlayback, &descriptorCapture); if (result != MA_SUCCESS) { - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_WARNING, "[AAudio] Failed to initialize device after route change."); - ma_close_streams__aaudio(pDevice); - goto done; + ma_device_uninit__aaudio(pDevice); + return result; } /* We'll only ever do this in response to a reroute. */ @@ -38511,29 +38143,14 @@ static ma_result ma_device_reinit__aaudio(ma_device* pDevice, ma_device_type dev /* If the device is started, start the streams. Maybe make this configurable? */ if (ma_device_get_state(pDevice) == ma_device_state_started) { if (pDevice->aaudio.noAutoStartAfterReroute == MA_FALSE) { - result = ma_device_start__aaudio(pDevice); - if (result != MA_SUCCESS) { - /* We got disconnected! Retry a few times, until we find a connected device! */ - retries += 1; - if (retries <= 3) { - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, "[AAudio] Failed to start stream after route change, retrying(%d)", retries); - goto error_disconnected; - } - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, "[AAudio] Failed to start stream after route change."); - goto done; - } + ma_device_start__aaudio(pDevice); } else { ma_device_stop(pDevice); /* Do a full device stop so we set internal state correctly. */ } } - - result = MA_SUCCESS; - } -done: - /* Re-routing done */ - ma_mutex_unlock(&pDevice->aaudio.rerouteLock); - return result; + return MA_SUCCESS; + } } static ma_result ma_device_get_info__aaudio(ma_device* pDevice, ma_device_type type, ma_device_info* pDeviceInfo) @@ -38544,12 +38161,12 @@ static ma_result ma_device_get_info__aaudio(ma_device* pDevice, ma_device_type t MA_ASSERT(type != ma_device_type_duplex); MA_ASSERT(pDeviceInfo != NULL); - if (type == ma_device_type_capture) { + if (type == ma_device_type_playback) { pStream = (ma_AAudioStream*)pDevice->aaudio.pStreamCapture; pDeviceInfo->id.aaudio = pDevice->capture.id.aaudio; ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), MA_DEFAULT_CAPTURE_DEVICE_NAME, (size_t)-1); /* Only supporting default devices. */ } - if (type == ma_device_type_playback) { + if (type == ma_device_type_capture) { pStream = (ma_AAudioStream*)pDevice->aaudio.pStreamPlayback; pDeviceInfo->id.aaudio = pDevice->playback.id.aaudio; ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), MA_DEFAULT_PLAYBACK_DEVICE_NAME, (size_t)-1); /* Only supporting default devices. */ @@ -38574,7 +38191,7 @@ static ma_result ma_context_uninit__aaudio(ma_context* pContext) ma_device_job_thread_uninit(&pContext->aaudio.jobThread, &pContext->allocationCallbacks); - ma_dlclose(ma_context_get_log(pContext), pContext->aaudio.hAAudio); + ma_dlclose(pContext, pContext->aaudio.hAAudio); pContext->aaudio.hAAudio = NULL; return MA_SUCCESS; @@ -38582,14 +38199,13 @@ static ma_result ma_context_uninit__aaudio(ma_context* pContext) static ma_result ma_context_init__aaudio(ma_context* pContext, const ma_context_config* pConfig, ma_backend_callbacks* pCallbacks) { -#if !defined(MA_NO_RUNTIME_LINKING) size_t i; const char* libNames[] = { "libaaudio.so" }; for (i = 0; i < ma_countof(libNames); ++i) { - pContext->aaudio.hAAudio = ma_dlopen(ma_context_get_log(pContext), libNames[i]); + pContext->aaudio.hAAudio = ma_dlopen(pContext, libNames[i]); if (pContext->aaudio.hAAudio != NULL) { break; } @@ -38599,68 +38215,36 @@ static ma_result ma_context_init__aaudio(ma_context* pContext, const ma_context_ return MA_FAILED_TO_INIT_BACKEND; } - pContext->aaudio.AAudio_createStreamBuilder = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->aaudio.hAAudio, "AAudio_createStreamBuilder"); - pContext->aaudio.AAudioStreamBuilder_delete = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->aaudio.hAAudio, "AAudioStreamBuilder_delete"); - pContext->aaudio.AAudioStreamBuilder_setDeviceId = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->aaudio.hAAudio, "AAudioStreamBuilder_setDeviceId"); - pContext->aaudio.AAudioStreamBuilder_setDirection = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->aaudio.hAAudio, "AAudioStreamBuilder_setDirection"); - pContext->aaudio.AAudioStreamBuilder_setSharingMode = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->aaudio.hAAudio, "AAudioStreamBuilder_setSharingMode"); - pContext->aaudio.AAudioStreamBuilder_setFormat = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->aaudio.hAAudio, "AAudioStreamBuilder_setFormat"); - pContext->aaudio.AAudioStreamBuilder_setChannelCount = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->aaudio.hAAudio, "AAudioStreamBuilder_setChannelCount"); - pContext->aaudio.AAudioStreamBuilder_setSampleRate = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->aaudio.hAAudio, "AAudioStreamBuilder_setSampleRate"); - pContext->aaudio.AAudioStreamBuilder_setBufferCapacityInFrames = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->aaudio.hAAudio, "AAudioStreamBuilder_setBufferCapacityInFrames"); - pContext->aaudio.AAudioStreamBuilder_setFramesPerDataCallback = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->aaudio.hAAudio, "AAudioStreamBuilder_setFramesPerDataCallback"); - pContext->aaudio.AAudioStreamBuilder_setDataCallback = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->aaudio.hAAudio, "AAudioStreamBuilder_setDataCallback"); - pContext->aaudio.AAudioStreamBuilder_setErrorCallback = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->aaudio.hAAudio, "AAudioStreamBuilder_setErrorCallback"); - pContext->aaudio.AAudioStreamBuilder_setPerformanceMode = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->aaudio.hAAudio, "AAudioStreamBuilder_setPerformanceMode"); - pContext->aaudio.AAudioStreamBuilder_setUsage = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->aaudio.hAAudio, "AAudioStreamBuilder_setUsage"); - pContext->aaudio.AAudioStreamBuilder_setContentType = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->aaudio.hAAudio, "AAudioStreamBuilder_setContentType"); - pContext->aaudio.AAudioStreamBuilder_setInputPreset = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->aaudio.hAAudio, "AAudioStreamBuilder_setInputPreset"); - pContext->aaudio.AAudioStreamBuilder_setAllowedCapturePolicy = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->aaudio.hAAudio, "AAudioStreamBuilder_setAllowedCapturePolicy"); - pContext->aaudio.AAudioStreamBuilder_openStream = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->aaudio.hAAudio, "AAudioStreamBuilder_openStream"); - pContext->aaudio.AAudioStream_close = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->aaudio.hAAudio, "AAudioStream_close"); - pContext->aaudio.AAudioStream_getState = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->aaudio.hAAudio, "AAudioStream_getState"); - pContext->aaudio.AAudioStream_waitForStateChange = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->aaudio.hAAudio, "AAudioStream_waitForStateChange"); - pContext->aaudio.AAudioStream_getFormat = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->aaudio.hAAudio, "AAudioStream_getFormat"); - pContext->aaudio.AAudioStream_getChannelCount = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->aaudio.hAAudio, "AAudioStream_getChannelCount"); - pContext->aaudio.AAudioStream_getSampleRate = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->aaudio.hAAudio, "AAudioStream_getSampleRate"); - pContext->aaudio.AAudioStream_getBufferCapacityInFrames = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->aaudio.hAAudio, "AAudioStream_getBufferCapacityInFrames"); - pContext->aaudio.AAudioStream_getFramesPerDataCallback = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->aaudio.hAAudio, "AAudioStream_getFramesPerDataCallback"); - pContext->aaudio.AAudioStream_getFramesPerBurst = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->aaudio.hAAudio, "AAudioStream_getFramesPerBurst"); - pContext->aaudio.AAudioStream_requestStart = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->aaudio.hAAudio, "AAudioStream_requestStart"); - pContext->aaudio.AAudioStream_requestStop = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->aaudio.hAAudio, "AAudioStream_requestStop"); -#else - pContext->aaudio.AAudio_createStreamBuilder = (ma_proc)AAudio_createStreamBuilder; - pContext->aaudio.AAudioStreamBuilder_delete = (ma_proc)AAudioStreamBuilder_delete; - pContext->aaudio.AAudioStreamBuilder_setDeviceId = (ma_proc)AAudioStreamBuilder_setDeviceId; - pContext->aaudio.AAudioStreamBuilder_setDirection = (ma_proc)AAudioStreamBuilder_setDirection; - pContext->aaudio.AAudioStreamBuilder_setSharingMode = (ma_proc)AAudioStreamBuilder_setSharingMode; - pContext->aaudio.AAudioStreamBuilder_setFormat = (ma_proc)AAudioStreamBuilder_setFormat; - pContext->aaudio.AAudioStreamBuilder_setChannelCount = (ma_proc)AAudioStreamBuilder_setChannelCount; - pContext->aaudio.AAudioStreamBuilder_setSampleRate = (ma_proc)AAudioStreamBuilder_setSampleRate; - pContext->aaudio.AAudioStreamBuilder_setBufferCapacityInFrames = (ma_proc)AAudioStreamBuilder_setBufferCapacityInFrames; - pContext->aaudio.AAudioStreamBuilder_setFramesPerDataCallback = (ma_proc)AAudioStreamBuilder_setFramesPerDataCallback; - pContext->aaudio.AAudioStreamBuilder_setDataCallback = (ma_proc)AAudioStreamBuilder_setDataCallback; - pContext->aaudio.AAudioStreamBuilder_setErrorCallback = (ma_proc)AAudioStreamBuilder_setErrorCallback; - pContext->aaudio.AAudioStreamBuilder_setPerformanceMode = (ma_proc)AAudioStreamBuilder_setPerformanceMode; - pContext->aaudio.AAudioStreamBuilder_setUsage = (ma_proc)AAudioStreamBuilder_setUsage; - pContext->aaudio.AAudioStreamBuilder_setContentType = (ma_proc)AAudioStreamBuilder_setContentType; - pContext->aaudio.AAudioStreamBuilder_setInputPreset = (ma_proc)AAudioStreamBuilder_setInputPreset; - #if defined(__ANDROID_API__) && __ANDROID_API__ >= 29 - pContext->aaudio.AAudioStreamBuilder_setAllowedCapturePolicy = (ma_proc)AAudioStreamBuilder_setAllowedCapturePolicy; - #endif - pContext->aaudio.AAudioStreamBuilder_openStream = (ma_proc)AAudioStreamBuilder_openStream; - pContext->aaudio.AAudioStream_close = (ma_proc)AAudioStream_close; - pContext->aaudio.AAudioStream_getState = (ma_proc)AAudioStream_getState; - pContext->aaudio.AAudioStream_waitForStateChange = (ma_proc)AAudioStream_waitForStateChange; - pContext->aaudio.AAudioStream_getFormat = (ma_proc)AAudioStream_getFormat; - pContext->aaudio.AAudioStream_getChannelCount = (ma_proc)AAudioStream_getChannelCount; - pContext->aaudio.AAudioStream_getSampleRate = (ma_proc)AAudioStream_getSampleRate; - pContext->aaudio.AAudioStream_getBufferCapacityInFrames = (ma_proc)AAudioStream_getBufferCapacityInFrames; - pContext->aaudio.AAudioStream_getFramesPerDataCallback = (ma_proc)AAudioStream_getFramesPerDataCallback; - pContext->aaudio.AAudioStream_getFramesPerBurst = (ma_proc)AAudioStream_getFramesPerBurst; - pContext->aaudio.AAudioStream_requestStart = (ma_proc)AAudioStream_requestStart; - pContext->aaudio.AAudioStream_requestStop = (ma_proc)AAudioStream_requestStop; -#endif + pContext->aaudio.AAudio_createStreamBuilder = (ma_proc)ma_dlsym(pContext, pContext->aaudio.hAAudio, "AAudio_createStreamBuilder"); + pContext->aaudio.AAudioStreamBuilder_delete = (ma_proc)ma_dlsym(pContext, pContext->aaudio.hAAudio, "AAudioStreamBuilder_delete"); + pContext->aaudio.AAudioStreamBuilder_setDeviceId = (ma_proc)ma_dlsym(pContext, pContext->aaudio.hAAudio, "AAudioStreamBuilder_setDeviceId"); + pContext->aaudio.AAudioStreamBuilder_setDirection = (ma_proc)ma_dlsym(pContext, pContext->aaudio.hAAudio, "AAudioStreamBuilder_setDirection"); + pContext->aaudio.AAudioStreamBuilder_setSharingMode = (ma_proc)ma_dlsym(pContext, pContext->aaudio.hAAudio, "AAudioStreamBuilder_setSharingMode"); + pContext->aaudio.AAudioStreamBuilder_setFormat = (ma_proc)ma_dlsym(pContext, pContext->aaudio.hAAudio, "AAudioStreamBuilder_setFormat"); + pContext->aaudio.AAudioStreamBuilder_setChannelCount = (ma_proc)ma_dlsym(pContext, pContext->aaudio.hAAudio, "AAudioStreamBuilder_setChannelCount"); + pContext->aaudio.AAudioStreamBuilder_setSampleRate = (ma_proc)ma_dlsym(pContext, pContext->aaudio.hAAudio, "AAudioStreamBuilder_setSampleRate"); + pContext->aaudio.AAudioStreamBuilder_setBufferCapacityInFrames = (ma_proc)ma_dlsym(pContext, pContext->aaudio.hAAudio, "AAudioStreamBuilder_setBufferCapacityInFrames"); + pContext->aaudio.AAudioStreamBuilder_setFramesPerDataCallback = (ma_proc)ma_dlsym(pContext, pContext->aaudio.hAAudio, "AAudioStreamBuilder_setFramesPerDataCallback"); + pContext->aaudio.AAudioStreamBuilder_setDataCallback = (ma_proc)ma_dlsym(pContext, pContext->aaudio.hAAudio, "AAudioStreamBuilder_setDataCallback"); + pContext->aaudio.AAudioStreamBuilder_setErrorCallback = (ma_proc)ma_dlsym(pContext, pContext->aaudio.hAAudio, "AAudioStreamBuilder_setErrorCallback"); + pContext->aaudio.AAudioStreamBuilder_setPerformanceMode = (ma_proc)ma_dlsym(pContext, pContext->aaudio.hAAudio, "AAudioStreamBuilder_setPerformanceMode"); + pContext->aaudio.AAudioStreamBuilder_setUsage = (ma_proc)ma_dlsym(pContext, pContext->aaudio.hAAudio, "AAudioStreamBuilder_setUsage"); + pContext->aaudio.AAudioStreamBuilder_setContentType = (ma_proc)ma_dlsym(pContext, pContext->aaudio.hAAudio, "AAudioStreamBuilder_setContentType"); + pContext->aaudio.AAudioStreamBuilder_setInputPreset = (ma_proc)ma_dlsym(pContext, pContext->aaudio.hAAudio, "AAudioStreamBuilder_setInputPreset"); + pContext->aaudio.AAudioStreamBuilder_setAllowedCapturePolicy = (ma_proc)ma_dlsym(pContext, pContext->aaudio.hAAudio, "AAudioStreamBuilder_setAllowedCapturePolicy"); + pContext->aaudio.AAudioStreamBuilder_openStream = (ma_proc)ma_dlsym(pContext, pContext->aaudio.hAAudio, "AAudioStreamBuilder_openStream"); + pContext->aaudio.AAudioStream_close = (ma_proc)ma_dlsym(pContext, pContext->aaudio.hAAudio, "AAudioStream_close"); + pContext->aaudio.AAudioStream_getState = (ma_proc)ma_dlsym(pContext, pContext->aaudio.hAAudio, "AAudioStream_getState"); + pContext->aaudio.AAudioStream_waitForStateChange = (ma_proc)ma_dlsym(pContext, pContext->aaudio.hAAudio, "AAudioStream_waitForStateChange"); + pContext->aaudio.AAudioStream_getFormat = (ma_proc)ma_dlsym(pContext, pContext->aaudio.hAAudio, "AAudioStream_getFormat"); + pContext->aaudio.AAudioStream_getChannelCount = (ma_proc)ma_dlsym(pContext, pContext->aaudio.hAAudio, "AAudioStream_getChannelCount"); + pContext->aaudio.AAudioStream_getSampleRate = (ma_proc)ma_dlsym(pContext, pContext->aaudio.hAAudio, "AAudioStream_getSampleRate"); + pContext->aaudio.AAudioStream_getBufferCapacityInFrames = (ma_proc)ma_dlsym(pContext, pContext->aaudio.hAAudio, "AAudioStream_getBufferCapacityInFrames"); + pContext->aaudio.AAudioStream_getFramesPerDataCallback = (ma_proc)ma_dlsym(pContext, pContext->aaudio.hAAudio, "AAudioStream_getFramesPerDataCallback"); + pContext->aaudio.AAudioStream_getFramesPerBurst = (ma_proc)ma_dlsym(pContext, pContext->aaudio.hAAudio, "AAudioStream_getFramesPerBurst"); + pContext->aaudio.AAudioStream_requestStart = (ma_proc)ma_dlsym(pContext, pContext->aaudio.hAAudio, "AAudioStream_requestStart"); + pContext->aaudio.AAudioStream_requestStop = (ma_proc)ma_dlsym(pContext, pContext->aaudio.hAAudio, "AAudioStream_requestStop"); + pCallbacks->onContextInit = ma_context_init__aaudio; pCallbacks->onContextUninit = ma_context_uninit__aaudio; @@ -38685,7 +38269,7 @@ static ma_result ma_context_init__aaudio(ma_context* pContext, const ma_context_ result = ma_device_job_thread_init(&jobThreadConfig, &pContext->allocationCallbacks, &pContext->aaudio.jobThread); if (result != MA_SUCCESS) { - ma_dlclose(ma_context_get_log(pContext), pContext->aaudio.hAAudio); + ma_dlclose(pContext, pContext->aaudio.hAAudio); pContext->aaudio.hAAudio = NULL; return result; } @@ -38698,7 +38282,6 @@ static ma_result ma_context_init__aaudio(ma_context* pContext, const ma_context_ static ma_result ma_job_process__device__aaudio_reroute(ma_job* pJob) { - ma_result result; ma_device* pDevice; MA_ASSERT(pJob != NULL); @@ -38707,18 +38290,7 @@ static ma_result ma_job_process__device__aaudio_reroute(ma_job* pJob) MA_ASSERT(pDevice != NULL); /* Here is where we need to reroute the device. To do this we need to uninitialize the stream and reinitialize it. */ - result = ma_device_reinit__aaudio(pDevice, (ma_device_type)pJob->data.device.aaudio.reroute.deviceType); - if (result != MA_SUCCESS) { - /* - Getting here means we failed to reroute the device. The best thing I can think of here is to - just stop the device. - */ - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[AAudio] Stopping device due to reroute failure."); - ma_device_stop(pDevice); - return result; - } - - return MA_SUCCESS; + return ma_device_reinit__aaudio(pDevice, (ma_device_type)pJob->data.device.aaudio.reroute.deviceType); } #else /* Getting here means there is no AAudio backend so we need a no-op job implementation. */ @@ -39713,7 +39285,7 @@ static ma_result ma_device_start__opensl(ma_device* pDevice) return ma_result_from_OpenSL(resultSL); } - /* In playback mode (no duplex) we need to load some initial buffers. In duplex mode we need to enqueue silent buffers. */ + /* In playback mode (no duplex) we need to load some initial buffers. In duplex mode we need to enqueu silent buffers. */ if (pDevice->type == ma_device_type_duplex) { MA_ZERO_MEMORY(pDevice->opensl.pBufferPlayback, pDevice->playback.internalPeriodSizeInFrames * pDevice->playback.internalPeriods * ma_get_bytes_per_frame(pDevice->playback.internalFormat, pDevice->playback.internalChannels)); } else { @@ -39834,7 +39406,7 @@ static ma_result ma_context_uninit__opensl(ma_context* pContext) static ma_result ma_dlsym_SLInterfaceID__opensl(ma_context* pContext, const char* pName, ma_handle* pHandle) { /* We need to return an error if the symbol cannot be found. This is important because there have been reports that some symbols do not exist. */ - ma_handle* p = (ma_handle*)ma_dlsym(ma_context_get_log(pContext), pContext->opensl.libOpenSLES, pName); + ma_handle* p = (ma_handle*)ma_dlsym(pContext, pContext->opensl.libOpenSLES, pName); if (p == NULL) { ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_INFO, "[OpenSL] Cannot find symbol %s", pName); return MA_NO_BACKEND; @@ -39892,7 +39464,7 @@ static ma_result ma_context_init__opensl(ma_context* pContext, const ma_context_ references to the symbols and will hopefully skip the checks. */ for (i = 0; i < ma_countof(libOpenSLESNames); i += 1) { - pContext->opensl.libOpenSLES = ma_dlopen(ma_context_get_log(pContext), libOpenSLESNames[i]); + pContext->opensl.libOpenSLES = ma_dlopen(pContext, libOpenSLESNames[i]); if (pContext->opensl.libOpenSLES != NULL) { break; } @@ -39905,49 +39477,49 @@ static ma_result ma_context_init__opensl(ma_context* pContext, const ma_context_ result = ma_dlsym_SLInterfaceID__opensl(pContext, "SL_IID_ENGINE", &pContext->opensl.SL_IID_ENGINE); if (result != MA_SUCCESS) { - ma_dlclose(ma_context_get_log(pContext), pContext->opensl.libOpenSLES); + ma_dlclose(pContext, pContext->opensl.libOpenSLES); return result; } result = ma_dlsym_SLInterfaceID__opensl(pContext, "SL_IID_AUDIOIODEVICECAPABILITIES", &pContext->opensl.SL_IID_AUDIOIODEVICECAPABILITIES); if (result != MA_SUCCESS) { - ma_dlclose(ma_context_get_log(pContext), pContext->opensl.libOpenSLES); + ma_dlclose(pContext, pContext->opensl.libOpenSLES); return result; } result = ma_dlsym_SLInterfaceID__opensl(pContext, "SL_IID_ANDROIDSIMPLEBUFFERQUEUE", &pContext->opensl.SL_IID_ANDROIDSIMPLEBUFFERQUEUE); if (result != MA_SUCCESS) { - ma_dlclose(ma_context_get_log(pContext), pContext->opensl.libOpenSLES); + ma_dlclose(pContext, pContext->opensl.libOpenSLES); return result; } result = ma_dlsym_SLInterfaceID__opensl(pContext, "SL_IID_RECORD", &pContext->opensl.SL_IID_RECORD); if (result != MA_SUCCESS) { - ma_dlclose(ma_context_get_log(pContext), pContext->opensl.libOpenSLES); + ma_dlclose(pContext, pContext->opensl.libOpenSLES); return result; } result = ma_dlsym_SLInterfaceID__opensl(pContext, "SL_IID_PLAY", &pContext->opensl.SL_IID_PLAY); if (result != MA_SUCCESS) { - ma_dlclose(ma_context_get_log(pContext), pContext->opensl.libOpenSLES); + ma_dlclose(pContext, pContext->opensl.libOpenSLES); return result; } result = ma_dlsym_SLInterfaceID__opensl(pContext, "SL_IID_OUTPUTMIX", &pContext->opensl.SL_IID_OUTPUTMIX); if (result != MA_SUCCESS) { - ma_dlclose(ma_context_get_log(pContext), pContext->opensl.libOpenSLES); + ma_dlclose(pContext, pContext->opensl.libOpenSLES); return result; } result = ma_dlsym_SLInterfaceID__opensl(pContext, "SL_IID_ANDROIDCONFIGURATION", &pContext->opensl.SL_IID_ANDROIDCONFIGURATION); if (result != MA_SUCCESS) { - ma_dlclose(ma_context_get_log(pContext), pContext->opensl.libOpenSLES); + ma_dlclose(pContext, pContext->opensl.libOpenSLES); return result; } - pContext->opensl.slCreateEngine = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->opensl.libOpenSLES, "slCreateEngine"); + pContext->opensl.slCreateEngine = (ma_proc)ma_dlsym(pContext, pContext->opensl.libOpenSLES, "slCreateEngine"); if (pContext->opensl.slCreateEngine == NULL) { - ma_dlclose(ma_context_get_log(pContext), pContext->opensl.libOpenSLES); + ma_dlclose(pContext, pContext->opensl.libOpenSLES); ma_log_post(ma_context_get_log(pContext), MA_LOG_LEVEL_INFO, "[OpenSL] Cannot find symbol slCreateEngine."); return MA_NO_BACKEND; } @@ -39971,7 +39543,7 @@ static ma_result ma_context_init__opensl(ma_context* pContext, const ma_context_ ma_spinlock_unlock(&g_maOpenSLSpinlock); if (result != MA_SUCCESS) { - ma_dlclose(ma_context_get_log(pContext), pContext->opensl.libOpenSLES); + ma_dlclose(pContext, pContext->opensl.libOpenSLES); ma_log_post(ma_context_get_log(pContext), MA_LOG_LEVEL_INFO, "[OpenSL] Failed to initialize OpenSL engine."); return result; } @@ -40004,10 +39576,6 @@ Web Audio Backend #if (__EMSCRIPTEN_major__ > 3) || (__EMSCRIPTEN_major__ == 3 && (__EMSCRIPTEN_minor__ > 1 || (__EMSCRIPTEN_minor__ == 1 && __EMSCRIPTEN_tiny__ >= 32))) #include #define MA_SUPPORT_AUDIO_WORKLETS - - #if (__EMSCRIPTEN_major__ > 3) || (__EMSCRIPTEN_major__ == 3 && (__EMSCRIPTEN_minor__ > 1 || (__EMSCRIPTEN_minor__ == 1 && __EMSCRIPTEN_tiny__ >= 70))) - #define MA_SUPPORT_AUDIO_WORKLETS_VARIABLE_BUFFER_SIZE - #endif #endif /* @@ -40019,7 +39587,7 @@ TODO: Version 0.12: Swap this logic around so that AudioWorklets are used by def /* The thread stack size must be a multiple of 16. */ #ifndef MA_AUDIO_WORKLETS_THREAD_STACK_SIZE -#define MA_AUDIO_WORKLETS_THREAD_STACK_SIZE 131072 +#define MA_AUDIO_WORKLETS_THREAD_STACK_SIZE 16384 #endif #if defined(MA_USE_AUDIO_WORKLETS) @@ -40138,78 +39706,101 @@ static ma_result ma_context_get_device_info__webaudio(ma_context* pContext, ma_d return MA_SUCCESS; } -static ma_result ma_device_uninit__webaudio(ma_device* pDevice) +#if !defined(MA_USE_AUDIO_WORKLETS) +static void ma_device_uninit_by_index__webaudio(ma_device* pDevice, ma_device_type deviceType, int deviceIndex) { MA_ASSERT(pDevice != NULL); - #if defined(MA_USE_AUDIO_WORKLETS) - { - EM_ASM({ - var device = window.miniaudio.get_device_by_index($0); + EM_ASM({ + var device = miniaudio.get_device_by_index($0); + var pAllocationCallbacks = $3; - if (device.streamNode !== undefined) { - device.streamNode.disconnect(); - device.streamNode = undefined; - } + /* Make sure all nodes are disconnected and marked for collection. */ + if (device.scriptNode !== undefined) { + device.scriptNode.onaudioprocess = function(e) {}; /* We want to reset the callback to ensure it doesn't get called after AudioContext.close() has returned. Shouldn't happen since we're disconnecting, but just to be safe... */ + device.scriptNode.disconnect(); + device.scriptNode = undefined; + } + if (device.streamNode !== undefined) { + device.streamNode.disconnect(); + device.streamNode = undefined; + } - device.pDevice = undefined; - }, pDevice->webaudio.deviceIndex); + /* + Stop the device. I think there is a chance the callback could get fired after calling this, hence why we want + to clear the callback before closing. + */ + device.webaudio.close(); + device.webaudio = undefined; - emscripten_destroy_web_audio_node(pDevice->webaudio.audioWorklet); - emscripten_destroy_audio_context(pDevice->webaudio.audioContext); - ma_free(pDevice->webaudio.pStackBuffer, &pDevice->pContext->allocationCallbacks); - } - #else - { - EM_ASM({ - var device = window.miniaudio.get_device_by_index($0); + /* Can't forget to free the intermediary buffer. This is the buffer that's shared between JavaScript and C. */ + if (device.intermediaryBuffer !== undefined) { + _ma_free_emscripten(device.intermediaryBuffer, pAllocationCallbacks); + device.intermediaryBuffer = undefined; + device.intermediaryBufferView = undefined; + device.intermediaryBufferSizeInBytes = undefined; + } - /* Make sure all nodes are disconnected and marked for collection. */ - if (device.scriptNode !== undefined) { - device.scriptNode.onaudioprocess = function(e) {}; /* We want to reset the callback to ensure it doesn't get called after AudioContext.close() has returned. Shouldn't happen since we're disconnecting, but just to be safe... */ - device.scriptNode.disconnect(); - device.scriptNode = undefined; - } + /* Make sure the device is untracked so the slot can be reused later. */ + miniaudio.untrack_device_by_index($0); + }, deviceIndex, deviceType, &pDevice->pContext->allocationCallbacks); +} +#endif - if (device.streamNode !== undefined) { - device.streamNode.disconnect(); - device.streamNode = undefined; - } +static void ma_device_uninit_by_type__webaudio(ma_device* pDevice, ma_device_type deviceType) +{ + MA_ASSERT(pDevice != NULL); + MA_ASSERT(deviceType == ma_device_type_capture || deviceType == ma_device_type_playback); - /* - Stop the device. I think there is a chance the callback could get fired after calling this, hence why we want - to clear the callback before closing. - */ - device.webaudio.close(); - device.webaudio = undefined; - device.pDevice = undefined; - }, pDevice->webaudio.deviceIndex); +#if defined(MA_USE_AUDIO_WORKLETS) + if (deviceType == ma_device_type_capture) { + ma_free(pDevice->webaudio.pIntermediaryBufferCapture, &pDevice->pContext->allocationCallbacks); + ma_free(pDevice->webaudio.pStackBufferCapture, &pDevice->pContext->allocationCallbacks); + emscripten_destroy_audio_context(pDevice->webaudio.audioContextCapture); + } else { + ma_free(pDevice->webaudio.pIntermediaryBufferPlayback, &pDevice->pContext->allocationCallbacks); + ma_free(pDevice->webaudio.pStackBufferPlayback, &pDevice->pContext->allocationCallbacks); + emscripten_destroy_audio_context(pDevice->webaudio.audioContextPlayback); } - #endif +#else + if (deviceType == ma_device_type_capture) { + ma_device_uninit_by_index__webaudio(pDevice, ma_device_type_capture, pDevice->webaudio.indexCapture); + } else { + ma_device_uninit_by_index__webaudio(pDevice, ma_device_type_playback, pDevice->webaudio.indexPlayback); + } +#endif +} - /* Clean up the device on the JS side. */ - EM_ASM({ - window.miniaudio.untrack_device_by_index($0); - }, pDevice->webaudio.deviceIndex); +static ma_result ma_device_uninit__webaudio(ma_device* pDevice) +{ + MA_ASSERT(pDevice != NULL); - ma_free(pDevice->webaudio.pIntermediaryBuffer, &pDevice->pContext->allocationCallbacks); + if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) { + ma_device_uninit_by_type__webaudio(pDevice, ma_device_type_capture); + } + + if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) { + ma_device_uninit_by_type__webaudio(pDevice, ma_device_type_playback); + } return MA_SUCCESS; } -#if !defined(MA_USE_AUDIO_WORKLETS) static ma_uint32 ma_calculate_period_size_in_frames_from_descriptor__webaudio(const ma_device_descriptor* pDescriptor, ma_uint32 nativeSampleRate, ma_performance_profile performanceProfile) { +#if defined(MA_USE_AUDIO_WORKLETS) + (void)pDescriptor; + (void)nativeSampleRate; + (void)performanceProfile; + + return 256; +#else /* There have been reports of the default buffer size being too small on some browsers. If we're using the default buffer size, we'll make sure the period size is bigger than our standard defaults. */ ma_uint32 periodSizeInFrames; - if (nativeSampleRate == 0) { - nativeSampleRate = MA_DEFAULT_SAMPLE_RATE; - } - if (pDescriptor->periodSizeInFrames == 0) { if (pDescriptor->periodSizeInMilliseconds == 0) { if (performanceProfile == ma_performance_profile_low_latency) { @@ -40234,8 +39825,8 @@ static ma_uint32 ma_calculate_period_size_in_frames_from_descriptor__webaudio(co } return periodSizeInFrames; -} #endif +} #if defined(MA_USE_AUDIO_WORKLETS) @@ -40243,14 +39834,16 @@ typedef struct { ma_device* pDevice; const ma_device_config* pConfig; - ma_device_descriptor* pDescriptorPlayback; - ma_device_descriptor* pDescriptorCapture; + ma_device_descriptor* pDescriptor; + ma_device_type deviceType; + ma_uint32 channels; } ma_audio_worklet_thread_initialized_data; static EM_BOOL ma_audio_worklet_process_callback__webaudio(int inputCount, const AudioSampleFrame* pInputs, int outputCount, AudioSampleFrame* pOutputs, int paramCount, const AudioParamFrame* pParams, void* pUserData) { ma_device* pDevice = (ma_device*)pUserData; ma_uint32 frameCount; + ma_uint32 framesProcessed; (void)paramCount; (void)pParams; @@ -40263,46 +39856,40 @@ static EM_BOOL ma_audio_worklet_process_callback__webaudio(int inputCount, const Unfortunately the audio data is not interleaved so we'll need to convert it before we give the data to miniaudio for further processing. */ - if (pDevice->type == ma_device_type_playback) { - frameCount = pDevice->playback.internalPeriodSizeInFrames; - } else { - frameCount = pDevice->capture.internalPeriodSizeInFrames; - } + frameCount = 128; - if (ma_device_get_state(pDevice) != ma_device_state_started) { - /* Fill the output buffer with zero to avoid a noise sound */ - for (int i = 0; i < outputCount; i += 1) { - MA_ZERO_MEMORY(pOutputs[i].data, pOutputs[i].numberOfChannels * frameCount * sizeof(float)); - } + /* Run the conversion logic in a loop for robustness. */ + framesProcessed = 0; + while (framesProcessed < frameCount) { + ma_uint32 framesToProcessThisIteration = frameCount - framesProcessed; - return EM_TRUE; - } + if (inputCount > 0) { + if (framesToProcessThisIteration > pDevice->webaudio.intermediaryBufferSizeInFramesPlayback) { + framesToProcessThisIteration = pDevice->webaudio.intermediaryBufferSizeInFramesPlayback; + } - if (inputCount > 0) { - /* Input data needs to be interleaved before we hand it to the client. */ - for (ma_uint32 iChannel = 0; iChannel < pDevice->capture.internalChannels; iChannel += 1) { - for (ma_uint32 iFrame = 0; iFrame < frameCount; iFrame += 1) { - pDevice->webaudio.pIntermediaryBuffer[iFrame*pDevice->capture.internalChannels + iChannel] = pInputs[0].data[frameCount*iChannel + iFrame]; + /* Input data needs to be interleaved before we hand it to the client. */ + for (ma_uint32 iFrame = 0; iFrame < framesToProcessThisIteration; iFrame += 1) { + for (ma_uint32 iChannel = 0; iChannel < pDevice->capture.internalChannels; iChannel += 1) { + pDevice->webaudio.pIntermediaryBufferCapture[iFrame*pDevice->capture.internalChannels + iChannel] = pInputs[0].data[frameCount*iChannel + framesProcessed + iFrame]; + } } + + ma_device_process_pcm_frames_capture__webaudio(pDevice, framesToProcessThisIteration, pDevice->webaudio.pIntermediaryBufferCapture); } - ma_device_process_pcm_frames_capture__webaudio(pDevice, frameCount, pDevice->webaudio.pIntermediaryBuffer); - } - - if (outputCount > 0) { - /* If it's a capture-only device, we'll need to output silence. */ - if (pDevice->type == ma_device_type_capture) { - MA_ZERO_MEMORY(pOutputs[0].data, frameCount * pDevice->playback.internalChannels * sizeof(float)); - } else { - ma_device_process_pcm_frames_playback__webaudio(pDevice, frameCount, pDevice->webaudio.pIntermediaryBuffer); + if (outputCount > 0) { + ma_device_process_pcm_frames_playback__webaudio(pDevice, framesToProcessThisIteration, pDevice->webaudio.pIntermediaryBufferPlayback); /* We've read the data from the client. Now we need to deinterleave the buffer and output to the output buffer. */ - for (ma_uint32 iChannel = 0; iChannel < pDevice->playback.internalChannels; iChannel += 1) { - for (ma_uint32 iFrame = 0; iFrame < frameCount; iFrame += 1) { - pOutputs[0].data[frameCount*iChannel + iFrame] = pDevice->webaudio.pIntermediaryBuffer[iFrame*pDevice->playback.internalChannels + iChannel]; + for (ma_uint32 iFrame = 0; iFrame < framesToProcessThisIteration; iFrame += 1) { + for (ma_uint32 iChannel = 0; iChannel < pDevice->playback.internalChannels; iChannel += 1) { + pOutputs[0].data[frameCount*iChannel + framesProcessed + iFrame] = pDevice->webaudio.pIntermediaryBufferPlayback[iFrame*pDevice->playback.internalChannels + iChannel]; } } } + + framesProcessed += framesToProcessThisIteration; } return EM_TRUE; @@ -40312,142 +39899,76 @@ static EM_BOOL ma_audio_worklet_process_callback__webaudio(int inputCount, const static void ma_audio_worklet_processor_created__webaudio(EMSCRIPTEN_WEBAUDIO_T audioContext, EM_BOOL success, void* pUserData) { ma_audio_worklet_thread_initialized_data* pParameters = (ma_audio_worklet_thread_initialized_data*)pUserData; - EmscriptenAudioWorkletNodeCreateOptions audioWorkletOptions; - int channels = 0; - size_t intermediaryBufferSizeInFrames; - int sampleRate; + EmscriptenAudioWorkletNodeCreateOptions workletNodeOptions; + EMSCRIPTEN_AUDIO_WORKLET_NODE_T workletNode; + int outputChannelCount = 0; if (success == EM_FALSE) { - pParameters->pDevice->webaudio.initResult = MA_ERROR; - ma_free(pParameters, &pParameters->pDevice->pContext->allocationCallbacks); + pParameters->pDevice->webaudio.isInitialized = MA_TRUE; return; } - /* The next step is to initialize the audio worklet node. */ - MA_ZERO_OBJECT(&audioWorkletOptions); + MA_ZERO_OBJECT(&workletNodeOptions); - /* - The way channel counts work with Web Audio is confusing. As far as I can tell, there's no way to know the channel - count from MediaStreamAudioSourceNode (what we use for capture)? The only way to have control is to configure an - output channel count on the capture side. This is slightly confusing for capture mode because intuitively you - wouldn't actually connect an output to an input-only node, but this is what we'll have to do in order to have - proper control over the channel count. In the capture case, we'll have to output silence to its output node. - */ - if (pParameters->pConfig->deviceType == ma_device_type_capture) { - channels = (int)((pParameters->pDescriptorCapture->channels > 0) ? pParameters->pDescriptorCapture->channels : MA_DEFAULT_CHANNELS); - audioWorkletOptions.numberOfInputs = 1; + if (pParameters->deviceType == ma_device_type_capture) { + workletNodeOptions.numberOfInputs = 1; } else { - channels = (int)((pParameters->pDescriptorPlayback->channels > 0) ? pParameters->pDescriptorPlayback->channels : MA_DEFAULT_CHANNELS); + outputChannelCount = (int)pParameters->channels; /* Safe cast. */ - if (pParameters->pConfig->deviceType == ma_device_type_duplex) { - audioWorkletOptions.numberOfInputs = 1; - } else { - audioWorkletOptions.numberOfInputs = 0; - } + workletNodeOptions.numberOfOutputs = 1; + workletNodeOptions.outputChannelCounts = &outputChannelCount; } - audioWorkletOptions.numberOfOutputs = 1; - audioWorkletOptions.outputChannelCounts = &channels; + /* Here is where we create the node that will do our processing. */ + workletNode = emscripten_create_wasm_audio_worklet_node(audioContext, "miniaudio", &workletNodeOptions, &ma_audio_worklet_process_callback__webaudio, pParameters->pDevice); + if (pParameters->deviceType == ma_device_type_capture) { + pParameters->pDevice->webaudio.workletNodeCapture = workletNode; + } else { + pParameters->pDevice->webaudio.workletNodePlayback = workletNode; + } /* - Now that we know the channel count to use we can allocate the intermediary buffer. The - intermediary buffer is used for interleaving and deinterleaving. + With the worklet node created we can now attach it to the graph. This is done differently depending on whether or not + it's capture or playback mode. */ - #if defined(MA_SUPPORT_AUDIO_WORKLETS_VARIABLE_BUFFER_SIZE) - { - intermediaryBufferSizeInFrames = (size_t)emscripten_audio_context_quantum_size(audioContext); - } - #else - { - intermediaryBufferSizeInFrames = 128; - } - #endif - - pParameters->pDevice->webaudio.pIntermediaryBuffer = (float*)ma_malloc(intermediaryBufferSizeInFrames * (ma_uint32)channels * sizeof(float), &pParameters->pDevice->pContext->allocationCallbacks); - if (pParameters->pDevice->webaudio.pIntermediaryBuffer == NULL) { - pParameters->pDevice->webaudio.initResult = MA_OUT_OF_MEMORY; - ma_free(pParameters, &pParameters->pDevice->pContext->allocationCallbacks); - return; - } - - pParameters->pDevice->webaudio.audioWorklet = emscripten_create_wasm_audio_worklet_node(audioContext, "miniaudio", &audioWorkletOptions, &ma_audio_worklet_process_callback__webaudio, pParameters->pDevice); - - /* With the audio worklet initialized we can now attach it to the graph. */ - if (pParameters->pConfig->deviceType == ma_device_type_capture || pParameters->pConfig->deviceType == ma_device_type_duplex) { - ma_result attachmentResult = (ma_result)EM_ASM_INT({ - var getUserMediaResult = 0; - var audioWorklet = emscriptenGetAudioObject($0); + if (pParameters->deviceType == ma_device_type_capture) { + EM_ASM({ + var workletNode = emscriptenGetAudioObject($0); var audioContext = emscriptenGetAudioObject($1); navigator.mediaDevices.getUserMedia({audio:true, video:false}) .then(function(stream) { audioContext.streamNode = audioContext.createMediaStreamSource(stream); - audioContext.streamNode.connect(audioWorklet); - audioWorklet.connect(audioContext.destination); - getUserMediaResult = 0; /* 0 = MA_SUCCESS */ + audioContext.streamNode.connect(workletNode); + + /* + Now that the worklet node has been connected, do we need to inspect workletNode.channelCount + to check the actual channel count, or is it safe to assume it's always 2? + */ }) .catch(function(error) { - console.log("navigator.mediaDevices.getUserMedia Failed: " + error); - getUserMediaResult = -1; /* -1 = MA_ERROR */ - }); - return getUserMediaResult; - }, pParameters->pDevice->webaudio.audioWorklet, audioContext); - - if (attachmentResult != MA_SUCCESS) { - ma_log_postf(ma_device_get_log(pParameters->pDevice), MA_LOG_LEVEL_ERROR, "Web Audio: Failed to connect capture node."); - emscripten_destroy_web_audio_node(pParameters->pDevice->webaudio.audioWorklet); - pParameters->pDevice->webaudio.initResult = attachmentResult; - ma_free(pParameters, &pParameters->pDevice->pContext->allocationCallbacks); - return; - } - } - - /* If it's playback only we can now attach the worklet node to the graph. This has already been done for the duplex case. */ - if (pParameters->pConfig->deviceType == ma_device_type_playback) { - ma_result attachmentResult = (ma_result)EM_ASM_INT({ - var audioWorklet = emscriptenGetAudioObject($0); + }); + }, workletNode, audioContext); + } else { + EM_ASM({ + var workletNode = emscriptenGetAudioObject($0); var audioContext = emscriptenGetAudioObject($1); - audioWorklet.connect(audioContext.destination); - return 0; /* 0 = MA_SUCCESS */ - }, pParameters->pDevice->webaudio.audioWorklet, audioContext); - - if (attachmentResult != MA_SUCCESS) { - ma_log_postf(ma_device_get_log(pParameters->pDevice), MA_LOG_LEVEL_ERROR, "Web Audio: Failed to connect playback node."); - pParameters->pDevice->webaudio.initResult = attachmentResult; - ma_free(pParameters, &pParameters->pDevice->pContext->allocationCallbacks); - return; - } + workletNode.connect(audioContext.destination); + }, workletNode, audioContext); } - /* We need to update the descriptors so that they reflect the internal data format. Both capture and playback should be the same. */ - sampleRate = EM_ASM_INT({ return emscriptenGetAudioObject($0).sampleRate; }, audioContext); + pParameters->pDevice->webaudio.isInitialized = MA_TRUE; - if (pParameters->pDescriptorCapture != NULL) { - pParameters->pDescriptorCapture->format = ma_format_f32; - pParameters->pDescriptorCapture->channels = (ma_uint32)channels; - pParameters->pDescriptorCapture->sampleRate = (ma_uint32)sampleRate; - ma_channel_map_init_standard(ma_standard_channel_map_webaudio, pParameters->pDescriptorCapture->channelMap, ma_countof(pParameters->pDescriptorCapture->channelMap), pParameters->pDescriptorCapture->channels); - pParameters->pDescriptorCapture->periodSizeInFrames = intermediaryBufferSizeInFrames; - pParameters->pDescriptorCapture->periodCount = 1; - } - - if (pParameters->pDescriptorPlayback != NULL) { - pParameters->pDescriptorPlayback->format = ma_format_f32; - pParameters->pDescriptorPlayback->channels = (ma_uint32)channels; - pParameters->pDescriptorPlayback->sampleRate = (ma_uint32)sampleRate; - ma_channel_map_init_standard(ma_standard_channel_map_webaudio, pParameters->pDescriptorPlayback->channelMap, ma_countof(pParameters->pDescriptorPlayback->channelMap), pParameters->pDescriptorPlayback->channels); - pParameters->pDescriptorPlayback->periodSizeInFrames = intermediaryBufferSizeInFrames; - pParameters->pDescriptorPlayback->periodCount = 1; - } + ma_log_postf(ma_device_get_log(pParameters->pDevice), MA_LOG_LEVEL_DEBUG, "AudioWorklets: Created worklet node: %d\n", workletNode); - /* At this point we're done and we can return. */ - ma_log_postf(ma_device_get_log(pParameters->pDevice), MA_LOG_LEVEL_DEBUG, "AudioWorklets: Created worklet node: %d\n", pParameters->pDevice->webaudio.audioWorklet); - pParameters->pDevice->webaudio.initResult = MA_SUCCESS; + /* Our parameter data is no longer needed. */ ma_free(pParameters, &pParameters->pDevice->pContext->allocationCallbacks); } + + static void ma_audio_worklet_thread_initialized__webaudio(EMSCRIPTEN_WEBAUDIO_T audioContext, EM_BOOL success, void* pUserData) { ma_audio_worklet_thread_initialized_data* pParameters = (ma_audio_worklet_thread_initialized_data*)pUserData; @@ -40456,7 +39977,7 @@ static void ma_audio_worklet_thread_initialized__webaudio(EMSCRIPTEN_WEBAUDIO_T MA_ASSERT(pParameters != NULL); if (success == EM_FALSE) { - pParameters->pDevice->webaudio.initResult = MA_ERROR; + pParameters->pDevice->webaudio.isInitialized = MA_TRUE; return; } @@ -40467,53 +39988,45 @@ static void ma_audio_worklet_thread_initialized__webaudio(EMSCRIPTEN_WEBAUDIO_T } #endif -static ma_result ma_device_init__webaudio(ma_device* pDevice, const ma_device_config* pConfig, ma_device_descriptor* pDescriptorPlayback, ma_device_descriptor* pDescriptorCapture) +static ma_result ma_device_init_by_type__webaudio(ma_device* pDevice, const ma_device_config* pConfig, ma_device_descriptor* pDescriptor, ma_device_type deviceType) { - if (pConfig->deviceType == ma_device_type_loopback) { - return MA_DEVICE_TYPE_NOT_SUPPORTED; - } +#if defined(MA_USE_AUDIO_WORKLETS) + EMSCRIPTEN_WEBAUDIO_T audioContext; + void* pStackBuffer; + size_t intermediaryBufferSizeInFrames; + float* pIntermediaryBuffer; +#endif + ma_uint32 channels; + ma_uint32 sampleRate; + ma_uint32 periodSizeInFrames; - /* No exclusive mode with Web Audio. */ - if (((pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) && pDescriptorPlayback->shareMode == ma_share_mode_exclusive) || - ((pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) && pDescriptorCapture->shareMode == ma_share_mode_exclusive)) { - return MA_SHARE_MODE_NOT_SUPPORTED; + MA_ASSERT(pDevice != NULL); + MA_ASSERT(pConfig != NULL); + MA_ASSERT(deviceType != ma_device_type_duplex); + + if (deviceType == ma_device_type_capture && !ma_is_capture_supported__webaudio()) { + return MA_NO_DEVICE; } - /* - With AudioWorklets we'll have just a single AudioContext. I'm not sure why I'm not doing this for ScriptProcessorNode so - it might be worthwhile to look into that as well. - */ - #if defined(MA_USE_AUDIO_WORKLETS) + /* We're going to calculate some stuff in C just to simplify the JS code. */ + channels = (pDescriptor->channels > 0) ? pDescriptor->channels : MA_DEFAULT_CHANNELS; + sampleRate = (pDescriptor->sampleRate > 0) ? pDescriptor->sampleRate : MA_DEFAULT_SAMPLE_RATE; + periodSizeInFrames = ma_calculate_period_size_in_frames_from_descriptor__webaudio(pDescriptor, sampleRate, pConfig->performanceProfile); + + ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "periodSizeInFrames = %d\n", (int)periodSizeInFrames); + +#if defined(MA_USE_AUDIO_WORKLETS) { - EmscriptenWebAudioCreateAttributes audioContextAttributes; ma_audio_worklet_thread_initialized_data* pInitParameters; - void* pStackBuffer; - - if (pConfig->performanceProfile == ma_performance_profile_conservative) { - audioContextAttributes.latencyHint = MA_WEBAUDIO_LATENCY_HINT_PLAYBACK; - } else { - audioContextAttributes.latencyHint = MA_WEBAUDIO_LATENCY_HINT_INTERACTIVE; - } + EmscriptenWebAudioCreateAttributes audioContextAttributes; - /* - In my testing, Firefox does not seem to capture audio data properly if the sample rate is set - to anything other than 48K. This does not seem to be the case for other browsers. For this reason, - if the device type is anything other than playback, we'll leave the sample rate as-is and let the - browser pick the appropriate rate for us. - */ - if (pConfig->deviceType == ma_device_type_playback) { - audioContextAttributes.sampleRate = pDescriptorPlayback->sampleRate; - } else { - audioContextAttributes.sampleRate = 0; - } + audioContextAttributes.latencyHint = MA_WEBAUDIO_LATENCY_HINT_INTERACTIVE; + audioContextAttributes.sampleRate = sampleRate; /* It's not clear if this can return an error. None of the tests in the Emscripten repository check for this, so neither am I for now. */ - pDevice->webaudio.audioContext = emscripten_create_audio_context(&audioContextAttributes); + audioContext = emscripten_create_audio_context(&audioContextAttributes); - /* - With the context created we can now create the worklet. We can only have a single worklet per audio - context which means we'll need to craft this appropriately to handle duplex devices correctly. - */ + ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "TRACE: AUDIO CONTEXT CREATED\n"); /* We now need to create a worker thread. This is a bit weird because we need to allocate our @@ -40522,228 +40035,339 @@ static ma_result ma_device_init__webaudio(ma_device* pDevice, const ma_device_co */ pStackBuffer = ma_aligned_malloc(MA_AUDIO_WORKLETS_THREAD_STACK_SIZE, 16, &pDevice->pContext->allocationCallbacks); if (pStackBuffer == NULL) { - emscripten_destroy_audio_context(pDevice->webaudio.audioContext); + emscripten_destroy_audio_context(audioContext); + return MA_OUT_OF_MEMORY; + } + + /* + We need an intermediary buffer for data conversion. WebAudio reports data in uninterleaved + format whereas we require it to be interleaved. We'll do this in chunks of 128 frames. + */ + intermediaryBufferSizeInFrames = 128; + pIntermediaryBuffer = ma_malloc(intermediaryBufferSizeInFrames * channels * sizeof(float), &pDevice->pContext->allocationCallbacks); + if (pIntermediaryBuffer == NULL) { + ma_free(pStackBuffer, &pDevice->pContext->allocationCallbacks); + emscripten_destroy_audio_context(audioContext); return MA_OUT_OF_MEMORY; } - /* Our thread initialization parameters need to be allocated on the heap so they don't go out of scope. */ - pInitParameters = (ma_audio_worklet_thread_initialized_data*)ma_malloc(sizeof(*pInitParameters), &pDevice->pContext->allocationCallbacks); + pInitParameters = ma_malloc(sizeof(*pInitParameters), &pDevice->pContext->allocationCallbacks); if (pInitParameters == NULL) { + ma_free(pIntermediaryBuffer, &pDevice->pContext->allocationCallbacks); ma_free(pStackBuffer, &pDevice->pContext->allocationCallbacks); - emscripten_destroy_audio_context(pDevice->webaudio.audioContext); + emscripten_destroy_audio_context(audioContext); return MA_OUT_OF_MEMORY; } - pInitParameters->pDevice = pDevice; - pInitParameters->pConfig = pConfig; - pInitParameters->pDescriptorPlayback = pDescriptorPlayback; - pInitParameters->pDescriptorCapture = pDescriptorCapture; + pInitParameters->pDevice = pDevice; + pInitParameters->pConfig = pConfig; + pInitParameters->pDescriptor = pDescriptor; + pInitParameters->deviceType = deviceType; + pInitParameters->channels = channels; /* We need to flag the device as not yet initialized so we can wait on it later. Unfortunately all of the Emscripten WebAudio stuff is asynchronous. */ - pDevice->webaudio.initResult = MA_BUSY; - { - emscripten_start_wasm_audio_worklet_thread_async(pDevice->webaudio.audioContext, pStackBuffer, MA_AUDIO_WORKLETS_THREAD_STACK_SIZE, ma_audio_worklet_thread_initialized__webaudio, pInitParameters); - } - while (pDevice->webaudio.initResult == MA_BUSY) { emscripten_sleep(1); } /* We must wait for initialization to complete. We're just spinning here. The emscripten_sleep() call is why we need to build with `-sASYNCIFY`. */ + pDevice->webaudio.isInitialized = MA_FALSE; - /* Initialization is now complete. Descriptors were updated when the worklet was initialized. */ - if (pDevice->webaudio.initResult != MA_SUCCESS) { - ma_free(pStackBuffer, &pDevice->pContext->allocationCallbacks); - emscripten_destroy_audio_context(pDevice->webaudio.audioContext); - return pDevice->webaudio.initResult; - } + ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "TRACE: CREATING WORKLET\n"); - /* We need to add an entry to the miniaudio.devices list on the JS side so we can do some JS/C interop. */ - pDevice->webaudio.deviceIndex = EM_ASM_INT({ - return window.miniaudio.track_device({ - webaudio: emscriptenGetAudioObject($0), - state: 1, /* 1 = ma_device_state_stopped */ - pDevice: $1 - }); - }, pDevice->webaudio.audioContext, pDevice); - - return MA_SUCCESS; - } - #else - { - /* ScriptProcessorNode. This path requires us to do almost everything in JS, but we'll do as much as we can in C. */ - ma_uint32 deviceIndex; - ma_uint32 channels; - ma_uint32 sampleRate; - ma_uint32 periodSizeInFrames; + emscripten_start_wasm_audio_worklet_thread_async(audioContext, pStackBuffer, MA_AUDIO_WORKLETS_THREAD_STACK_SIZE, ma_audio_worklet_thread_initialized__webaudio, pInitParameters); - /* The channel count will depend on the device type. If it's a capture, use its, otherwise use the playback side. */ - if (pConfig->deviceType == ma_device_type_capture) { - channels = (pDescriptorCapture->channels > 0) ? pDescriptorCapture->channels : MA_DEFAULT_CHANNELS; - } else { - channels = (pDescriptorPlayback->channels > 0) ? pDescriptorPlayback->channels : MA_DEFAULT_CHANNELS; + /* We must wait for initialization to complete. We're just spinning here. The emscripten_sleep() call is why we need to build with `-sASYNCIFY`. */ + while (pDevice->webaudio.isInitialized == MA_FALSE) { + emscripten_sleep(1); } /* - When testing in Firefox, I've seen it where capture mode fails if the sample rate is changed to anything other than it's - native rate. For this reason we're leaving the sample rate untouched for capture devices. + Now that initialization is finished we can go ahead and extract our channel count so that + miniaudio can set up a data converter at a higher level. */ - if (pConfig->deviceType == ma_device_type_playback) { - sampleRate = pDescriptorPlayback->sampleRate; + if (deviceType == ma_device_type_capture) { + /* + For capture we won't actually know what the channel count is. Everything I've seen seems + to indicate that the default channel count is 2, so I'm sticking with that. + */ + channels = 2; } else { - sampleRate = 0; /* Let the browser decide when capturing. */ + /* Get the channel count from the audio context. */ + channels = (ma_uint32)EM_ASM_INT({ + return emscriptenGetAudioObject($0).destination.channelCount; + }, audioContext); } - /* The period size needs to be a power of 2. */ - if (pConfig->deviceType == ma_device_type_capture) { - periodSizeInFrames = ma_calculate_period_size_in_frames_from_descriptor__webaudio(pDescriptorCapture, sampleRate, pConfig->performanceProfile); - } else { - periodSizeInFrames = ma_calculate_period_size_in_frames_from_descriptor__webaudio(pDescriptorPlayback, sampleRate, pConfig->performanceProfile); - } + ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "TRACE: INITIALIZED. channels = %u\n", channels); + } +#else + /* We create the device on the JavaScript side and reference it using an index. We use this to make it possible to reference the device between JavaScript and C. */ + int deviceIndex = EM_ASM_INT({ + var channels = $0; + var sampleRate = $1; + var bufferSize = $2; /* In PCM frames. */ + var isCapture = $3; + var pDevice = $4; + var pAllocationCallbacks = $5; - /* We need an intermediary buffer for doing interleaving and deinterleaving. */ - pDevice->webaudio.pIntermediaryBuffer = (float*)ma_malloc(periodSizeInFrames * channels * sizeof(float), &pDevice->pContext->allocationCallbacks); - if (pDevice->webaudio.pIntermediaryBuffer == NULL) { - return MA_OUT_OF_MEMORY; + if (typeof(window.miniaudio) === 'undefined') { + return -1; /* Context not initialized. */ } - deviceIndex = EM_ASM_INT({ - var deviceType = $0; - var channels = $1; - var sampleRate = $2; - var bufferSize = $3; - var pIntermediaryBuffer = $4; - var pDevice = $5; + var device = {}; - if (typeof(window.miniaudio) === 'undefined') { - return -1; /* Context not initialized. */ - } + /* The AudioContext must be created in a suspended state. */ + device.webaudio = new (window.AudioContext || window.webkitAudioContext)({sampleRate:sampleRate}); + device.webaudio.suspend(); + device.state = 1; /* ma_device_state_stopped */ - var device = {}; + /* We need an intermediary buffer which we use for JavaScript and C interop. This buffer stores interleaved f32 PCM data. */ + device.intermediaryBufferSizeInBytes = channels * bufferSize * 4; + device.intermediaryBuffer = _ma_malloc_emscripten(device.intermediaryBufferSizeInBytes, pAllocationCallbacks); + device.intermediaryBufferView = new Float32Array(Module.HEAPF32.buffer, device.intermediaryBuffer, device.intermediaryBufferSizeInBytes); - /* First thing we need is an AudioContext. */ - var audioContextOptions = {}; - if (deviceType == window.miniaudio.device_type.playback && sampleRate != 0) { - audioContextOptions.sampleRate = sampleRate; - } + /* + Both playback and capture devices use a ScriptProcessorNode for performing per-sample operations. + + ScriptProcessorNode is actually deprecated so this is likely to be temporary. The way this works for playback is very simple. You just set a callback + that's periodically fired, just like a normal audio callback function. But apparently this design is "flawed" and is now deprecated in favour of + something called AudioWorklets which _forces_ you to load a _separate_ .js file at run time... nice... Hopefully ScriptProcessorNode will continue to + work for years to come, but this may need to change to use AudioSourceBufferNode instead, which I think is what Emscripten uses for it's built-in SDL + implementation. I'll be avoiding that insane AudioWorklet API like the plague... + + For capture it is a bit unintuitive. We use the ScriptProccessorNode _only_ to get the raw PCM data. It is connected to an AudioContext just like the + playback case, however we just output silence to the AudioContext instead of passing any real data. It would make more sense to me to use the + MediaRecorder API, but unfortunately you need to specify a MIME time (Opus, Vorbis, etc.) for the binary blob that's returned to the client, but I've + been unable to figure out how to get this as raw PCM. The closest I can think is to use the MIME type for WAV files and just parse it, but I don't know + how well this would work. Although ScriptProccessorNode is deprecated, in practice it seems to have pretty good browser support so I'm leaving it like + this for now. If anyone knows how I could get raw PCM data using the MediaRecorder API please let me know! + */ + device.scriptNode = device.webaudio.createScriptProcessor(bufferSize, (isCapture) ? channels : 0, (isCapture) ? 0 : channels); - device.webaudio = new (window.AudioContext || window.webkitAudioContext)(audioContextOptions); - device.webaudio.suspend(); /* The AudioContext must be created in a suspended state. */ - device.state = window.miniaudio.device_state.stopped; + if (isCapture) { + device.scriptNode.onaudioprocess = function(e) { + if (device.intermediaryBuffer === undefined) { + return; /* This means the device has been uninitialized. */ + } - /* - We need to create a ScriptProcessorNode. The channel situation is the same as the AudioWorklet path in that we - need to specify an output and configure the channel count there. - */ - var channelCountIn = 0; - var channelCountOut = channels; - if (deviceType != window.miniaudio.device_type.playback) { - channelCountIn = channels; - } + if (device.intermediaryBufferView.length == 0) { + /* Recreate intermediaryBufferView when losing reference to the underlying buffer, probably due to emscripten resizing heap. */ + device.intermediaryBufferView = new Float32Array(Module.HEAPF32.buffer, device.intermediaryBuffer, device.intermediaryBufferSizeInBytes); + } + + /* Make sure silence it output to the AudioContext destination. Not doing this will cause sound to come out of the speakers! */ + for (var iChannel = 0; iChannel < e.outputBuffer.numberOfChannels; ++iChannel) { + e.outputBuffer.getChannelData(iChannel).fill(0.0); + } - device.scriptNode = device.webaudio.createScriptProcessor(bufferSize, channelCountIn, channelCountOut); + /* There are some situations where we may want to send silence to the client. */ + var sendSilence = false; + if (device.streamNode === undefined) { + sendSilence = true; + } - /* The node processing callback. */ - device.scriptNode.onaudioprocess = function(e) { - if (device.intermediaryBufferView == null || device.intermediaryBufferView.length == 0) { - device.intermediaryBufferView = new Float32Array(HEAPF32.buffer, pIntermediaryBuffer, bufferSize * channels); + /* Sanity check. This will never happen, right? */ + if (e.inputBuffer.numberOfChannels != channels) { + console.log("Capture: Channel count mismatch. " + e.inputBufer.numberOfChannels + " != " + channels + ". Sending silence."); + sendSilence = true; } - /* Do the capture side first. */ - if (deviceType == window.miniaudio.device_type.capture || deviceType == window.miniaudio.device_type.duplex) { - /* The data must be interleaved before being processed miniaudio. */ - for (var iChannel = 0; iChannel < channels; iChannel += 1) { - var inputBuffer = e.inputBuffer.getChannelData(iChannel); - var intermediaryBuffer = device.intermediaryBufferView; + /* This looped design guards against the situation where e.inputBuffer is a different size to the original buffer size. Should never happen in practice. */ + var totalFramesProcessed = 0; + while (totalFramesProcessed < e.inputBuffer.length) { + var framesRemaining = e.inputBuffer.length - totalFramesProcessed; + var framesToProcess = framesRemaining; + if (framesToProcess > (device.intermediaryBufferSizeInBytes/channels/4)) { + framesToProcess = (device.intermediaryBufferSizeInBytes/channels/4); + } - for (var iFrame = 0; iFrame < bufferSize; iFrame += 1) { - intermediaryBuffer[iFrame*channels + iChannel] = inputBuffer[iFrame]; + /* We need to do the reverse of the playback case. We need to interleave the input data and copy it into the intermediary buffer. Then we send it to the client. */ + if (sendSilence) { + device.intermediaryBufferView.fill(0.0); + } else { + for (var iFrame = 0; iFrame < framesToProcess; ++iFrame) { + for (var iChannel = 0; iChannel < e.inputBuffer.numberOfChannels; ++iChannel) { + device.intermediaryBufferView[iFrame*channels + iChannel] = e.inputBuffer.getChannelData(iChannel)[totalFramesProcessed + iFrame]; + } } } - _ma_device_process_pcm_frames_capture__webaudio(pDevice, bufferSize, pIntermediaryBuffer); + /* Send data to the client from our intermediary buffer. */ + _ma_device_process_pcm_frames_capture__webaudio(pDevice, framesToProcess, device.intermediaryBuffer); + + totalFramesProcessed += framesToProcess; + } + }; + + navigator.mediaDevices.getUserMedia({audio:true, video:false}) + .then(function(stream) { + device.streamNode = device.webaudio.createMediaStreamSource(stream); + device.streamNode.connect(device.scriptNode); + device.scriptNode.connect(device.webaudio.destination); + }) + .catch(function(error) { + /* I think this should output silence... */ + device.scriptNode.connect(device.webaudio.destination); + }); + } else { + device.scriptNode.onaudioprocess = function(e) { + if (device.intermediaryBuffer === undefined) { + return; /* This means the device has been uninitialized. */ + } + + if(device.intermediaryBufferView.length == 0) { + /* Recreate intermediaryBufferView when losing reference to the underlying buffer, probably due to emscripten resizing heap. */ + device.intermediaryBufferView = new Float32Array(Module.HEAPF32.buffer, device.intermediaryBuffer, device.intermediaryBufferSizeInBytes); + } + + var outputSilence = false; + + /* Sanity check. This will never happen, right? */ + if (e.outputBuffer.numberOfChannels != channels) { + console.log("Playback: Channel count mismatch. " + e.outputBufer.numberOfChannels + " != " + channels + ". Outputting silence."); + outputSilence = true; + return; } - if (deviceType == window.miniaudio.device_type.playback || deviceType == window.miniaudio.device_type.duplex) { - _ma_device_process_pcm_frames_playback__webaudio(pDevice, bufferSize, pIntermediaryBuffer); + /* This looped design guards against the situation where e.outputBuffer is a different size to the original buffer size. Should never happen in practice. */ + var totalFramesProcessed = 0; + while (totalFramesProcessed < e.outputBuffer.length) { + var framesRemaining = e.outputBuffer.length - totalFramesProcessed; + var framesToProcess = framesRemaining; + if (framesToProcess > (device.intermediaryBufferSizeInBytes/channels/4)) { + framesToProcess = (device.intermediaryBufferSizeInBytes/channels/4); + } - for (var iChannel = 0; iChannel < e.outputBuffer.numberOfChannels; ++iChannel) { - var outputBuffer = e.outputBuffer.getChannelData(iChannel); - var intermediaryBuffer = device.intermediaryBufferView; + /* Read data from the client into our intermediary buffer. */ + _ma_device_process_pcm_frames_playback__webaudio(pDevice, framesToProcess, device.intermediaryBuffer); - for (var iFrame = 0; iFrame < bufferSize; iFrame += 1) { - outputBuffer[iFrame] = intermediaryBuffer[iFrame*channels + iChannel]; + /* At this point we'll have data in our intermediary buffer which we now need to deinterleave and copy over to the output buffers. */ + if (outputSilence) { + for (var iChannel = 0; iChannel < e.outputBuffer.numberOfChannels; ++iChannel) { + e.outputBuffer.getChannelData(iChannel).fill(0.0); + } + } else { + for (var iChannel = 0; iChannel < e.outputBuffer.numberOfChannels; ++iChannel) { + var outputBuffer = e.outputBuffer.getChannelData(iChannel); + var intermediaryBuffer = device.intermediaryBufferView; + for (var iFrame = 0; iFrame < framesToProcess; ++iFrame) { + outputBuffer[totalFramesProcessed + iFrame] = intermediaryBuffer[iFrame*channels + iChannel]; + } } } - } else { - /* It's a capture-only device. Make sure the output is silenced. */ - for (var iChannel = 0; iChannel < e.outputBuffer.numberOfChannels; ++iChannel) { - e.outputBuffer.getChannelData(iChannel).fill(0.0); - } + + totalFramesProcessed += framesToProcess; } }; - /* Now we need to connect our node to the graph. */ - if (deviceType == window.miniaudio.device_type.capture || deviceType == window.miniaudio.device_type.duplex) { - navigator.mediaDevices.getUserMedia({audio:true, video:false}) - .then(function(stream) { - device.streamNode = device.webaudio.createMediaStreamSource(stream); - device.streamNode.connect(device.scriptNode); - device.scriptNode.connect(device.webaudio.destination); - }) - .catch(function(error) { - console.log("Failed to get user media: " + error); - }); - } + device.scriptNode.connect(device.webaudio.destination); + } - if (deviceType == window.miniaudio.device_type.playback) { - device.scriptNode.connect(device.webaudio.destination); - } + return miniaudio.track_device(device); + }, channels, sampleRate, periodSizeInFrames, deviceType == ma_device_type_capture, pDevice, &pDevice->pContext->allocationCallbacks); - device.pDevice = pDevice; + if (deviceIndex < 0) { + return MA_FAILED_TO_OPEN_BACKEND_DEVICE; + } +#endif - return window.miniaudio.track_device(device); - }, pConfig->deviceType, channels, sampleRate, periodSizeInFrames, pDevice->webaudio.pIntermediaryBuffer, pDevice); +#if defined(MA_USE_AUDIO_WORKLETS) + if (deviceType == ma_device_type_capture) { + pDevice->webaudio.audioContextCapture = audioContext; + pDevice->webaudio.pStackBufferCapture = pStackBuffer; + pDevice->webaudio.intermediaryBufferSizeInFramesCapture = intermediaryBufferSizeInFrames; + pDevice->webaudio.pIntermediaryBufferCapture = pIntermediaryBuffer; + } else { + pDevice->webaudio.audioContextPlayback = audioContext; + pDevice->webaudio.pStackBufferPlayback = pStackBuffer; + pDevice->webaudio.intermediaryBufferSizeInFramesPlayback = intermediaryBufferSizeInFrames; + pDevice->webaudio.pIntermediaryBufferPlayback = pIntermediaryBuffer; + } +#else + if (deviceType == ma_device_type_capture) { + pDevice->webaudio.indexCapture = deviceIndex; + } else { + pDevice->webaudio.indexPlayback = deviceIndex; + } +#endif - if (deviceIndex < 0) { - return MA_FAILED_TO_OPEN_BACKEND_DEVICE; - } + pDescriptor->format = ma_format_f32; + pDescriptor->channels = channels; + ma_channel_map_init_standard(ma_standard_channel_map_webaudio, pDescriptor->channelMap, ma_countof(pDescriptor->channelMap), pDescriptor->channels); + pDescriptor->periodSizeInFrames = periodSizeInFrames; + pDescriptor->periodCount = 1; - pDevice->webaudio.deviceIndex = deviceIndex; +#if defined(MA_USE_AUDIO_WORKLETS) + pDescriptor->sampleRate = sampleRate; /* Is this good enough to be used in the general case? */ +#else + pDescriptor->sampleRate = EM_ASM_INT({ return miniaudio.get_device_by_index($0).webaudio.sampleRate; }, deviceIndex); +#endif - /* Grab the sample rate from the audio context directly. */ - sampleRate = (ma_uint32)EM_ASM_INT({ return window.miniaudio.get_device_by_index($0).webaudio.sampleRate; }, deviceIndex); + return MA_SUCCESS; +} - if (pDescriptorCapture != NULL) { - pDescriptorCapture->format = ma_format_f32; - pDescriptorCapture->channels = channels; - pDescriptorCapture->sampleRate = sampleRate; - ma_channel_map_init_standard(ma_standard_channel_map_webaudio, pDescriptorCapture->channelMap, ma_countof(pDescriptorCapture->channelMap), pDescriptorCapture->channels); - pDescriptorCapture->periodSizeInFrames = periodSizeInFrames; - pDescriptorCapture->periodCount = 1; - } +static ma_result ma_device_init__webaudio(ma_device* pDevice, const ma_device_config* pConfig, ma_device_descriptor* pDescriptorPlayback, ma_device_descriptor* pDescriptorCapture) +{ + ma_result result; - if (pDescriptorPlayback != NULL) { - pDescriptorPlayback->format = ma_format_f32; - pDescriptorPlayback->channels = channels; - pDescriptorPlayback->sampleRate = sampleRate; - ma_channel_map_init_standard(ma_standard_channel_map_webaudio, pDescriptorPlayback->channelMap, ma_countof(pDescriptorPlayback->channelMap), pDescriptorPlayback->channels); - pDescriptorPlayback->periodSizeInFrames = periodSizeInFrames; - pDescriptorPlayback->periodCount = 1; + if (pConfig->deviceType == ma_device_type_loopback) { + return MA_DEVICE_TYPE_NOT_SUPPORTED; + } + + /* No exclusive mode with Web Audio. */ + if (((pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) && pDescriptorPlayback->shareMode == ma_share_mode_exclusive) || + ((pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) && pDescriptorCapture->shareMode == ma_share_mode_exclusive)) { + return MA_SHARE_MODE_NOT_SUPPORTED; + } + + if (pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) { + result = ma_device_init_by_type__webaudio(pDevice, pConfig, pDescriptorCapture, ma_device_type_capture); + if (result != MA_SUCCESS) { + return result; } + } - return MA_SUCCESS; + if (pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) { + result = ma_device_init_by_type__webaudio(pDevice, pConfig, pDescriptorPlayback, ma_device_type_playback); + if (result != MA_SUCCESS) { + if (pConfig->deviceType == ma_device_type_duplex) { + ma_device_uninit_by_type__webaudio(pDevice, ma_device_type_capture); + } + return result; + } } - #endif + + return MA_SUCCESS; } static ma_result ma_device_start__webaudio(ma_device* pDevice) { MA_ASSERT(pDevice != NULL); - EM_ASM({ - var device = window.miniaudio.get_device_by_index($0); - device.webaudio.resume(); - device.state = window.miniaudio.device_state.started; - }, pDevice->webaudio.deviceIndex); +#if defined(MA_USE_AUDIO_WORKLETS) + if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) { + emscripten_resume_audio_context_sync(pDevice->webaudio.audioContextCapture); + } + + if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) { + emscripten_resume_audio_context_sync(pDevice->webaudio.audioContextPlayback); + } +#else + if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) { + EM_ASM({ + var device = miniaudio.get_device_by_index($0); + device.webaudio.resume(); + device.state = 2; /* ma_device_state_started */ + }, pDevice->webaudio.indexCapture); + } + + if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) { + EM_ASM({ + var device = miniaudio.get_device_by_index($0); + device.webaudio.resume(); + device.state = 2; /* ma_device_state_started */ + }, pDevice->webaudio.indexPlayback); + } +#endif return MA_SUCCESS; } @@ -40761,11 +40385,37 @@ static ma_result ma_device_stop__webaudio(ma_device* pDevice) I read this to mean that "any current context processing blocks" are processed by suspend() - i.e. They they are drained. We therefore shouldn't need to do any kind of explicit draining. */ - EM_ASM({ - var device = window.miniaudio.get_device_by_index($0); - device.webaudio.suspend(); - device.state = window.miniaudio.device_state.stopped; - }, pDevice->webaudio.deviceIndex); + +#if defined(MA_USE_AUDIO_WORKLETS) + /* I can't seem to find a way to suspend an AudioContext via the C Emscripten API. Is this an oversight? */ + if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) { + EM_ASM({ + emscriptenGetAudioObject($0).suspend(); + }, pDevice->webaudio.audioContextCapture); + } + + if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) { + EM_ASM({ + emscriptenGetAudioObject($0).suspend(); + }, pDevice->webaudio.audioContextPlayback); + } +#else + if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) { + EM_ASM({ + var device = miniaudio.get_device_by_index($0); + device.webaudio.suspend(); + device.state = 1; /* ma_device_state_stopped */ + }, pDevice->webaudio.indexCapture); + } + + if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) { + EM_ASM({ + var device = miniaudio.get_device_by_index($0); + device.webaudio.suspend(); + device.state = 1; /* ma_device_state_stopped */ + }, pDevice->webaudio.indexPlayback); + } +#endif ma_device__on_notification_stopped(pDevice); @@ -40782,11 +40432,7 @@ static ma_result ma_context_uninit__webaudio(ma_context* pContext) /* Remove the global miniaudio object from window if there are no more references to it. */ EM_ASM({ if (typeof(window.miniaudio) !== 'undefined') { - miniaudio.unlock_event_types.map(function(event_type) { - document.removeEventListener(event_type, miniaudio.unlock, true); - }); - - window.miniaudio.referenceCount -= 1; + window.miniaudio.referenceCount--; if (window.miniaudio.referenceCount === 0) { delete window.miniaudio; } @@ -40814,21 +40460,7 @@ static ma_result ma_context_init__webaudio(ma_context* pContext, const ma_contex window.miniaudio = { referenceCount: 0 }; - - /* Device types. */ - window.miniaudio.device_type = {}; - window.miniaudio.device_type.playback = $0; - window.miniaudio.device_type.capture = $1; - window.miniaudio.device_type.duplex = $2; - - /* Device states. */ - window.miniaudio.device_state = {}; - window.miniaudio.device_state.stopped = $3; - window.miniaudio.device_state.started = $4; - - /* Device cache for mapping devices to indexes for JavaScript/C interop. */ - let miniaudio = window.miniaudio; - miniaudio.devices = []; + miniaudio.devices = []; /* Device cache for mapping devices to indexes for JavaScript/C interop. */ miniaudio.track_device = function(device) { /* Try inserting into a free slot first. */ @@ -40871,21 +40503,14 @@ static ma_result ma_context_init__webaudio(ma_context* pContext, const ma_contex }; miniaudio.unlock_event_types = (function(){ - return ['touchend', 'click']; + return ['touchstart', 'touchend', 'click']; })(); miniaudio.unlock = function() { for(var i = 0; i < miniaudio.devices.length; ++i) { var device = miniaudio.devices[i]; - if (device != null && - device.webaudio != null && - device.state === miniaudio.device_state.started) { - - device.webaudio.resume().then(() => { - _ma_device__on_notification_unlocked(device.pDevice); - }, - (error) => {console.error("Failed to resume audiocontext", error); - }); + if (device != null && device.webaudio != null && device.state === 2 /* ma_device_state_started */) { + device.webaudio.resume(); } } miniaudio.unlock_event_types.map(function(event_type) { @@ -40898,10 +40523,10 @@ static ma_result ma_context_init__webaudio(ma_context* pContext, const ma_contex }); } - window.miniaudio.referenceCount += 1; + window.miniaudio.referenceCount++; return 1; - }, ma_device_type_playback, ma_device_type_capture, ma_device_type_duplex, ma_device_state_stopped, ma_device_state_started); + }, 0); /* Must pass in a dummy argument for C99 compatibility. */ if (resultFromJS != 1) { return MA_FAILED_TO_INIT_BACKEND; @@ -40921,7 +40546,7 @@ static ma_result ma_context_init__webaudio(ma_context* pContext, const ma_contex return MA_SUCCESS; } -#endif /* MA_HAS_WEBAUDIO */ +#endif /* Web Audio */ @@ -41200,7 +40825,7 @@ MA_API ma_result ma_device_post_init(ma_device* pDevice, ma_device_type deviceTy ma_device_info deviceInfo; if (deviceType == ma_device_type_capture || deviceType == ma_device_type_duplex || deviceType == ma_device_type_loopback) { - result = ma_device_get_info(pDevice, ma_device_type_capture, &deviceInfo); + result = ma_device_get_info(pDevice, (deviceType == ma_device_type_loopback) ? ma_device_type_playback : ma_device_type_capture, &deviceInfo); if (result == MA_SUCCESS) { ma_strncpy_s(pDevice->capture.name, sizeof(pDevice->capture.name), deviceInfo.name, (size_t)-1); } else { @@ -41236,18 +40861,14 @@ MA_API ma_result ma_device_post_init(ma_device* pDevice, ma_device_type deviceTy static ma_thread_result MA_THREADCALL ma_worker_thread(void* pData) { ma_device* pDevice = (ma_device*)pData; -#ifdef MA_WIN32 - HRESULT CoInitializeResult; -#endif - MA_ASSERT(pDevice != NULL); #ifdef MA_WIN32 - CoInitializeResult = ma_CoInitializeEx(pDevice->pContext, NULL, MA_COINIT_VALUE); + ma_CoInitializeEx(pDevice->pContext, NULL, MA_COINIT_VALUE); #endif /* - When the device is being initialized its initial state is set to ma_device_state_uninitialized. Before returning from + When the device is being initialized it's initial state is set to ma_device_state_uninitialized. Before returning from ma_device_init(), the state needs to be set to something valid. In miniaudio the device's default state immediately after initialization is stopped, so therefore we need to mark the device as such. miniaudio will wait on the worker thread to signal an event to know when the worker thread is ready for action. @@ -41323,20 +40944,13 @@ static ma_thread_result MA_THREADCALL ma_worker_thread(void* pData) ma_device__on_notification_stopped(pDevice); } - /* If we stopped because the device has been uninitialized, abort now. */ - if (ma_device_get_state(pDevice) == ma_device_state_uninitialized) { - break; - } - /* A function somewhere is waiting for the device to have stopped for real so we need to signal an event to allow it to continue. */ ma_device__set_state(pDevice, ma_device_state_stopped); ma_event_signal(&pDevice->stopEvent); } #ifdef MA_WIN32 - if (CoInitializeResult == S_OK) { - ma_CoUninitialize(pDevice->pContext); - } + ma_CoUninitialize(pDevice->pContext); #endif return (ma_thread_result)0; @@ -41358,17 +40972,11 @@ static ma_bool32 ma_device__is_initialized(ma_device* pDevice) static ma_result ma_context_uninit_backend_apis__win32(ma_context* pContext) { /* For some reason UWP complains when CoUninitialize() is called. I'm just not going to call it on UWP. */ -#if defined(MA_WIN32_DESKTOP) || defined(MA_WIN32_GDK) - if (pContext->win32.CoInitializeResult == S_OK) { - ma_CoUninitialize(pContext); - } - - #if defined(MA_WIN32_DESKTOP) - ma_dlclose(ma_context_get_log(pContext), pContext->win32.hUser32DLL); - ma_dlclose(ma_context_get_log(pContext), pContext->win32.hAdvapi32DLL); - #endif - - ma_dlclose(ma_context_get_log(pContext), pContext->win32.hOle32DLL); +#ifdef MA_WIN32_DESKTOP + ma_CoUninitialize(pContext); + ma_dlclose(pContext, pContext->win32.hUser32DLL); + ma_dlclose(pContext, pContext->win32.hOle32DLL); + ma_dlclose(pContext, pContext->win32.hAdvapi32DLL); #else (void)pContext; #endif @@ -41378,47 +40986,46 @@ static ma_result ma_context_uninit_backend_apis__win32(ma_context* pContext) static ma_result ma_context_init_backend_apis__win32(ma_context* pContext) { -#if defined(MA_WIN32_DESKTOP) || defined(MA_WIN32_GDK) - #if defined(MA_WIN32_DESKTOP) - /* User32.dll */ - pContext->win32.hUser32DLL = ma_dlopen(ma_context_get_log(pContext), "user32.dll"); - if (pContext->win32.hUser32DLL == NULL) { - return MA_FAILED_TO_INIT_BACKEND; - } +#ifdef MA_WIN32_DESKTOP + /* Ole32.dll */ + pContext->win32.hOle32DLL = ma_dlopen(pContext, "ole32.dll"); + if (pContext->win32.hOle32DLL == NULL) { + return MA_FAILED_TO_INIT_BACKEND; + } - pContext->win32.GetForegroundWindow = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->win32.hUser32DLL, "GetForegroundWindow"); - pContext->win32.GetDesktopWindow = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->win32.hUser32DLL, "GetDesktopWindow"); + pContext->win32.CoInitialize = (ma_proc)ma_dlsym(pContext, pContext->win32.hOle32DLL, "CoInitialize"); + pContext->win32.CoInitializeEx = (ma_proc)ma_dlsym(pContext, pContext->win32.hOle32DLL, "CoInitializeEx"); + pContext->win32.CoUninitialize = (ma_proc)ma_dlsym(pContext, pContext->win32.hOle32DLL, "CoUninitialize"); + pContext->win32.CoCreateInstance = (ma_proc)ma_dlsym(pContext, pContext->win32.hOle32DLL, "CoCreateInstance"); + pContext->win32.CoTaskMemFree = (ma_proc)ma_dlsym(pContext, pContext->win32.hOle32DLL, "CoTaskMemFree"); + pContext->win32.PropVariantClear = (ma_proc)ma_dlsym(pContext, pContext->win32.hOle32DLL, "PropVariantClear"); + pContext->win32.StringFromGUID2 = (ma_proc)ma_dlsym(pContext, pContext->win32.hOle32DLL, "StringFromGUID2"); - /* Advapi32.dll */ - pContext->win32.hAdvapi32DLL = ma_dlopen(ma_context_get_log(pContext), "advapi32.dll"); - if (pContext->win32.hAdvapi32DLL == NULL) { - return MA_FAILED_TO_INIT_BACKEND; - } + /* User32.dll */ + pContext->win32.hUser32DLL = ma_dlopen(pContext, "user32.dll"); + if (pContext->win32.hUser32DLL == NULL) { + return MA_FAILED_TO_INIT_BACKEND; + } - pContext->win32.RegOpenKeyExA = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->win32.hAdvapi32DLL, "RegOpenKeyExA"); - pContext->win32.RegCloseKey = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->win32.hAdvapi32DLL, "RegCloseKey"); - pContext->win32.RegQueryValueExA = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->win32.hAdvapi32DLL, "RegQueryValueExA"); - #endif + pContext->win32.GetForegroundWindow = (ma_proc)ma_dlsym(pContext, pContext->win32.hUser32DLL, "GetForegroundWindow"); + pContext->win32.GetDesktopWindow = (ma_proc)ma_dlsym(pContext, pContext->win32.hUser32DLL, "GetDesktopWindow"); - /* Ole32.dll */ - pContext->win32.hOle32DLL = ma_dlopen(ma_context_get_log(pContext), "ole32.dll"); - if (pContext->win32.hOle32DLL == NULL) { + + /* Advapi32.dll */ + pContext->win32.hAdvapi32DLL = ma_dlopen(pContext, "advapi32.dll"); + if (pContext->win32.hAdvapi32DLL == NULL) { return MA_FAILED_TO_INIT_BACKEND; } - pContext->win32.CoInitialize = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->win32.hOle32DLL, "CoInitialize"); - pContext->win32.CoInitializeEx = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->win32.hOle32DLL, "CoInitializeEx"); - pContext->win32.CoUninitialize = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->win32.hOle32DLL, "CoUninitialize"); - pContext->win32.CoCreateInstance = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->win32.hOle32DLL, "CoCreateInstance"); - pContext->win32.CoTaskMemFree = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->win32.hOle32DLL, "CoTaskMemFree"); - pContext->win32.PropVariantClear = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->win32.hOle32DLL, "PropVariantClear"); - pContext->win32.StringFromGUID2 = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->win32.hOle32DLL, "StringFromGUID2"); + pContext->win32.RegOpenKeyExA = (ma_proc)ma_dlsym(pContext, pContext->win32.hAdvapi32DLL, "RegOpenKeyExA"); + pContext->win32.RegCloseKey = (ma_proc)ma_dlsym(pContext, pContext->win32.hAdvapi32DLL, "RegCloseKey"); + pContext->win32.RegQueryValueExA = (ma_proc)ma_dlsym(pContext, pContext->win32.hAdvapi32DLL, "RegQueryValueExA"); #else (void)pContext; /* Unused. */ #endif - pContext->win32.CoInitializeResult = ma_CoInitializeEx(pContext, NULL, MA_COINIT_VALUE); + ma_CoInitializeEx(pContext, NULL, MA_COINIT_VALUE); return MA_SUCCESS; } #else @@ -41592,24 +41199,6 @@ MA_API ma_result ma_device_job_thread_next(ma_device_job_thread* pJobThread, ma_ } -MA_API ma_bool32 ma_device_id_equal(const ma_device_id* pA, const ma_device_id* pB) -{ - size_t i; - - if (pA == NULL || pB == NULL) { - return MA_FALSE; - } - - for (i = 0; i < sizeof(ma_device_id); i += 1) { - if (((const char*)pA)[i] != ((const char*)pB)[i]) { - return MA_FALSE; - } - } - - return MA_TRUE; -} - - MA_API ma_context_config ma_context_config_init(void) { @@ -42311,6 +41900,7 @@ MA_API ma_result ma_device_init(ma_context* pContext, const ma_device_config* pC } + /* If we're using fixed sized callbacks we'll need to make use of an intermediary buffer. Needs to be done after post_init_setup() because we'll need access to the sample rate. @@ -42383,7 +41973,7 @@ MA_API ma_result ma_device_init(ma_context* pContext, const ma_device_config* pC return result; } - /* Wait for the worker thread to put the device into its stopped state for real. */ + /* Wait for the worker thread to put the device into it's stopped state for real. */ ma_event_wait(&pDevice->stopEvent); MA_ASSERT(ma_device_get_state(pDevice) == ma_device_state_stopped); } else { @@ -42409,7 +41999,7 @@ MA_API ma_result ma_device_init(ma_context* pContext, const ma_device_config* pC ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, "[%s]\n", ma_get_backend_name(pDevice->pContext->backend)); if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex || pDevice->type == ma_device_type_loopback) { char name[MA_MAX_DEVICE_NAME_LENGTH + 1]; - ma_device_get_name(pDevice, ma_device_type_capture, name, sizeof(name), NULL); + ma_device_get_name(pDevice, (pDevice->type == ma_device_type_loopback) ? ma_device_type_playback : ma_device_type_capture, name, sizeof(name), NULL); ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, " %s (%s)\n", name, "Capture"); ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, " Format: %s -> %s\n", ma_get_format_name(pDevice->capture.internalFormat), ma_get_format_name(pDevice->capture.format)); @@ -42556,23 +42146,10 @@ MA_API void ma_device_uninit(ma_device* pDevice) return; } - /* - It's possible for the miniaudio side of the device and the backend to not be in sync due to - system-level situations such as the computer being put into sleep mode and the backend not - notifying miniaudio of the fact the device has stopped. It's possible for this to result in a - deadlock due to miniaudio thinking the device is in a running state, when in fact it's not - running at all. For this reason I am no longer explicitly stopping the device. I don't think - this should affect anyone in practice since uninitializing the backend will naturally stop the - device anyway. - */ - #if 0 - { - /* Make sure the device is stopped first. The backends will probably handle this naturally, but I like to do it explicitly for my own sanity. */ - if (ma_device_is_started(pDevice)) { - ma_device_stop(pDevice); - } + /* Make sure the device is stopped first. The backends will probably handle this naturally, but I like to do it explicitly for my own sanity. */ + if (ma_device_is_started(pDevice)) { + ma_device_stop(pDevice); } - #endif /* Putting the device into an uninitialized state will make the worker thread return. */ ma_device__set_state(pDevice, ma_device_state_uninitialized); @@ -42662,17 +42239,6 @@ MA_API ma_result ma_device_get_info(ma_device* pDevice, ma_device_type type, ma_ if (type == ma_device_type_playback) { return ma_context_get_device_info(pDevice->pContext, type, pDevice->playback.pID, pDeviceInfo); } else { - /* - Here we're getting the capture side, which is the branch we'll be entering for a loopback - device, since loopback is capturing. However, if the device is using the default device ID, - it won't get the correct information because it'll think we're asking for the default - capture device, where in fact for loopback we want the default *playback* device. We'll do - a bit of a hack here to make sure we get the correct info. - */ - if (pDevice->type == ma_device_type_loopback && pDevice->capture.pID == NULL) { - type = ma_device_type_playback; - } - return ma_context_get_device_info(pDevice->pContext, type, pDevice->capture.pID, pDeviceInfo); } } @@ -42734,15 +42300,6 @@ MA_API ma_result ma_device_start(ma_device* pDevice) ma_mutex_lock(&pDevice->startStopLock); { - /* - We need to check again if the device is in a started state because it's possible for one thread to have started the device - while another was waiting on the mutex. - */ - if (ma_device_get_state(pDevice) == ma_device_state_started) { - ma_mutex_unlock(&pDevice->startStopLock); - return MA_SUCCESS; /* Already started. */ - } - /* Starting and stopping are wrapped in a mutex which means we can assert that the device is in a stopped or paused state. */ MA_ASSERT(ma_device_get_state(pDevice) == ma_device_state_stopped); @@ -42803,15 +42360,6 @@ MA_API ma_result ma_device_stop(ma_device* pDevice) ma_mutex_lock(&pDevice->startStopLock); { - /* - We need to check again if the device is in a stopped state because it's possible for one thread to have stopped the device - while another was waiting on the mutex. - */ - if (ma_device_get_state(pDevice) == ma_device_state_stopped) { - ma_mutex_unlock(&pDevice->startStopLock); - return MA_SUCCESS; /* Already stopped. */ - } - /* Starting and stopping are wrapped in a mutex which means we can assert that the device is in a started or paused state. */ MA_ASSERT(ma_device_get_state(pDevice) == ma_device_state_started); @@ -42830,7 +42378,7 @@ MA_API ma_result ma_device_stop(ma_device* pDevice) } else { /* Synchronous backends. The stop callback is always called from the worker thread. Do not call the stop callback here. If - the backend is implementing its own audio thread loop we'll need to wake it up if required. Note that we need to make + the backend is implementing it's own audio thread loop we'll need to wake it up if required. Note that we need to make sure the state of the device is *not* playing right now, which it shouldn't be since we set it above. This is super important though, so I'm asserting it here as well for extra safety in case we accidentally change something later. */ @@ -42947,15 +42495,6 @@ MA_API ma_result ma_device_handle_backend_data_callback(ma_device* pDevice, void return MA_INVALID_ARGS; } - /* - There is an assert deeper in the code that checks that frameCount > 0. Since this is a public facing - API we'll need to check for that here. I've had reports that AAudio can sometimes post a frame count - of 0. - */ - if (frameCount == 0) { - return MA_INVALID_ARGS; - } - if (pDevice->type == ma_device_type_duplex) { if (pInput != NULL) { ma_device__handle_duplex_callback_capture(pDevice, frameCount, pInput, &pDevice->duplexRB.rb); @@ -43030,7 +42569,7 @@ MA_API ma_uint32 ma_calculate_buffer_size_in_milliseconds_from_frames(ma_uint32 return 0; } - return (bufferSizeInFrames*1000 + (sampleRate - 1)) / sampleRate; + return bufferSizeInFrames*1000 / sampleRate; } MA_API ma_uint32 ma_calculate_buffer_size_in_frames_from_milliseconds(ma_uint32 bufferSizeInMilliseconds, ma_uint32 sampleRate) @@ -45142,14 +44681,13 @@ static MA_INLINE void ma_pcm_f32_to_s16__neon(void* dst, const void* src, ma_uin d1 = vmovq_n_f32(0); } else if (ditherMode == ma_dither_mode_rectangle) { float d0v[4]; - float d1v[4]; - d0v[0] = ma_dither_f32_rectangle(ditherMin, ditherMax); d0v[1] = ma_dither_f32_rectangle(ditherMin, ditherMax); d0v[2] = ma_dither_f32_rectangle(ditherMin, ditherMax); d0v[3] = ma_dither_f32_rectangle(ditherMin, ditherMax); d0 = vld1q_f32(d0v); + float d1v[4]; d1v[0] = ma_dither_f32_rectangle(ditherMin, ditherMax); d1v[1] = ma_dither_f32_rectangle(ditherMin, ditherMax); d1v[2] = ma_dither_f32_rectangle(ditherMin, ditherMax); @@ -45157,14 +44695,13 @@ static MA_INLINE void ma_pcm_f32_to_s16__neon(void* dst, const void* src, ma_uin d1 = vld1q_f32(d1v); } else { float d0v[4]; - float d1v[4]; - d0v[0] = ma_dither_f32_triangle(ditherMin, ditherMax); d0v[1] = ma_dither_f32_triangle(ditherMin, ditherMax); d0v[2] = ma_dither_f32_triangle(ditherMin, ditherMax); d0v[3] = ma_dither_f32_triangle(ditherMin, ditherMax); d0 = vld1q_f32(d0v); + float d1v[4]; d1v[0] = ma_dither_f32_triangle(ditherMin, ditherMax); d1v[1] = ma_dither_f32_triangle(ditherMin, ditherMax); d1v[2] = ma_dither_f32_triangle(ditherMin, ditherMax); @@ -47858,7 +47395,7 @@ static ma_result ma_bpf_get_heap_layout(const ma_bpf_config* pConfig, ma_bpf_hea return MA_INVALID_ARGS; } - bpf2Count = pConfig->order / 2; + bpf2Count = pConfig->channels / 2; pHeapLayout->sizeInBytes = 0; @@ -49777,65 +49314,48 @@ MA_API ma_result ma_fader_process_pcm_frames(ma_fader* pFader, void* pFramesOut, return MA_INVALID_ARGS; } - /* If the cursor is still negative we need to just copy the absolute number of those frames, but no more than frameCount. */ - if (pFader->cursorInFrames < 0) { - ma_uint64 absCursorInFrames = (ma_uint64)0 - pFader->cursorInFrames; - if (absCursorInFrames > frameCount) { - absCursorInFrames = frameCount; - } - - ma_copy_pcm_frames(pFramesOut, pFramesIn, absCursorInFrames, pFader->config.format, pFader->config.channels); - - pFader->cursorInFrames += absCursorInFrames; - frameCount -= absCursorInFrames; - pFramesOut = ma_offset_ptr(pFramesOut, ma_get_bytes_per_frame(pFader->config.format, pFader->config.channels)*absCursorInFrames); - pFramesIn = ma_offset_ptr(pFramesIn, ma_get_bytes_per_frame(pFader->config.format, pFader->config.channels)*absCursorInFrames); + /* + For now we need to clamp frameCount so that the cursor never overflows 32-bits. This is required for + the conversion to a float which we use for the linear interpolation. This might be changed later. + */ + if (frameCount + pFader->cursorInFrames > UINT_MAX) { + frameCount = UINT_MAX - pFader->cursorInFrames; } - if (pFader->cursorInFrames >= 0) { - /* - For now we need to clamp frameCount so that the cursor never overflows 32-bits. This is required for - the conversion to a float which we use for the linear interpolation. This might be changed later. - */ - if (frameCount + pFader->cursorInFrames > UINT_MAX) { - frameCount = UINT_MAX - pFader->cursorInFrames; + /* Optimized path if volumeBeg and volumeEnd are equal. */ + if (pFader->volumeBeg == pFader->volumeEnd) { + if (pFader->volumeBeg == 1) { + /* Straight copy. */ + ma_copy_pcm_frames(pFramesOut, pFramesIn, frameCount, pFader->config.format, pFader->config.channels); + } else { + /* Copy with volume. */ + ma_copy_and_apply_volume_and_clip_pcm_frames(pFramesOut, pFramesIn, frameCount, pFader->config.format, pFader->config.channels, pFader->volumeEnd); } - - /* Optimized path if volumeBeg and volumeEnd are equal. */ - if (pFader->volumeBeg == pFader->volumeEnd) { - if (pFader->volumeBeg == 1) { - /* Straight copy. */ - ma_copy_pcm_frames(pFramesOut, pFramesIn, frameCount, pFader->config.format, pFader->config.channels); - } else { - /* Copy with volume. */ - ma_copy_and_apply_volume_and_clip_pcm_frames(pFramesOut, pFramesIn, frameCount, pFader->config.format, pFader->config.channels, pFader->volumeBeg); - } + } else { + /* Slower path. Volumes are different, so may need to do an interpolation. */ + if (pFader->cursorInFrames >= pFader->lengthInFrames) { + /* Fast path. We've gone past the end of the fade period so just apply the end volume to all samples. */ + ma_copy_and_apply_volume_and_clip_pcm_frames(pFramesOut, pFramesIn, frameCount, pFader->config.format, pFader->config.channels, pFader->volumeEnd); } else { - /* Slower path. Volumes are different, so may need to do an interpolation. */ - if ((ma_uint64)pFader->cursorInFrames >= pFader->lengthInFrames) { - /* Fast path. We've gone past the end of the fade period so just apply the end volume to all samples. */ - ma_copy_and_apply_volume_and_clip_pcm_frames(pFramesOut, pFramesIn, frameCount, pFader->config.format, pFader->config.channels, pFader->volumeEnd); - } else { - /* Slow path. This is where we do the actual fading. */ - ma_uint64 iFrame; - ma_uint32 iChannel; + /* Slow path. This is where we do the actual fading. */ + ma_uint64 iFrame; + ma_uint32 iChannel; - /* For now we only support f32. Support for other formats might be added later. */ - if (pFader->config.format == ma_format_f32) { - const float* pFramesInF32 = (const float*)pFramesIn; - /* */ float* pFramesOutF32 = ( float*)pFramesOut; + /* For now we only support f32. Support for other formats will be added later. */ + if (pFader->config.format == ma_format_f32) { + const float* pFramesInF32 = (const float*)pFramesIn; + /* */ float* pFramesOutF32 = ( float*)pFramesOut; - for (iFrame = 0; iFrame < frameCount; iFrame += 1) { - float a = (ma_uint32)ma_min(pFader->cursorInFrames + iFrame, pFader->lengthInFrames) / (float)((ma_uint32)pFader->lengthInFrames); /* Safe cast due to the frameCount clamp at the top of this function. */ - float volume = ma_mix_f32_fast(pFader->volumeBeg, pFader->volumeEnd, a); + for (iFrame = 0; iFrame < frameCount; iFrame += 1) { + float a = (ma_uint32)ma_min(pFader->cursorInFrames + iFrame, pFader->lengthInFrames) / (float)((ma_uint32)pFader->lengthInFrames); /* Safe cast due to the frameCount clamp at the top of this function. */ + float volume = ma_mix_f32_fast(pFader->volumeBeg, pFader->volumeEnd, a); - for (iChannel = 0; iChannel < pFader->config.channels; iChannel += 1) { - pFramesOutF32[iFrame*pFader->config.channels + iChannel] = pFramesInF32[iFrame*pFader->config.channels + iChannel] * volume; - } + for (iChannel = 0; iChannel < pFader->config.channels; iChannel += 1) { + pFramesOutF32[iFrame*pFader->config.channels + iChannel] = pFramesInF32[iFrame*pFader->config.channels + iChannel] * volume; } - } else { - return MA_NOT_IMPLEMENTED; } + } else { + return MA_NOT_IMPLEMENTED; } } } @@ -49865,11 +49385,6 @@ MA_API void ma_fader_get_data_format(const ma_fader* pFader, ma_format* pFormat, } MA_API void ma_fader_set_fade(ma_fader* pFader, float volumeBeg, float volumeEnd, ma_uint64 lengthInFrames) -{ - ma_fader_set_fade_ex(pFader, volumeBeg, volumeEnd, lengthInFrames, 0); -} - -MA_API void ma_fader_set_fade_ex(ma_fader* pFader, float volumeBeg, float volumeEnd, ma_uint64 lengthInFrames, ma_int64 startOffsetInFrames) { if (pFader == NULL) { return; @@ -49888,15 +49403,10 @@ MA_API void ma_fader_set_fade_ex(ma_fader* pFader, float volumeBeg, float volume lengthInFrames = UINT_MAX; } - /* The start offset needs to be clamped to ensure it doesn't overflow a signed number. */ - if (startOffsetInFrames > INT_MAX) { - startOffsetInFrames = INT_MAX; - } - pFader->volumeBeg = volumeBeg; pFader->volumeEnd = volumeEnd; pFader->lengthInFrames = lengthInFrames; - pFader->cursorInFrames = -startOffsetInFrames; + pFader->cursorInFrames = 0; /* Reset cursor. */ } MA_API float ma_fader_get_current_volume(const ma_fader* pFader) @@ -49905,18 +49415,13 @@ MA_API float ma_fader_get_current_volume(const ma_fader* pFader) return 0.0f; } - /* Any frames prior to the start of the fade period will be at unfaded volume. */ - if (pFader->cursorInFrames < 0) { - return 1.0f; - } - /* The current volume depends on the position of the cursor. */ if (pFader->cursorInFrames == 0) { return pFader->volumeBeg; - } else if ((ma_uint64)pFader->cursorInFrames >= pFader->lengthInFrames) { /* Safe case because the < 0 case was checked above. */ + } else if (pFader->cursorInFrames >= pFader->lengthInFrames) { return pFader->volumeEnd; } else { - /* The cursor is somewhere inside the fading period. We can figure this out with a simple linear interpolation between volumeBeg and volumeEnd based on our cursor position. */ + /* The cursor is somewhere inside the fading period. We can figure this out with a simple linear interpoluation between volumeBeg and volumeEnd based on our cursor position. */ return ma_mix_f32_fast(pFader->volumeBeg, pFader->volumeEnd, (ma_uint32)pFader->cursorInFrames / (float)((ma_uint32)pFader->lengthInFrames)); /* Safe cast to uint32 because we clamp it in ma_fader_process_pcm_frames(). */ } } @@ -50139,9 +49644,9 @@ static float ma_attenuation_exponential(float distance, float minDistance, float /* -Doppler Effect calculation taken from the OpenAL spec, with two main differences: +Dopper Effect calculation taken from the OpenAL spec, with two main differences: - 1) The source to listener vector will have already been calculated at an earlier step so we can + 1) The source to listener vector will have already been calcualted at an earlier step so we can just use that directly. We need only the position of the source relative to the origin. 2) We don't scale by a frequency because we actually just want the ratio which we'll plug straight @@ -50180,7 +49685,7 @@ static void ma_get_default_channel_map_for_spatializer(ma_channel* pChannelMap, Special case for stereo. Want to default the left and right speakers to side left and side right so that they're facing directly down the X axis rather than slightly forward. Not doing this will result in sounds being quieter when behind the listener. This might - actually be good for some scenarios, but I don't think it's an appropriate default because + actually be good for some scenerios, but I don't think it's an appropriate default because it can be a bit unexpected. */ if (channelCount == 2) { @@ -50514,7 +50019,7 @@ MA_API ma_spatializer_config ma_spatializer_config_init(ma_uint32 channelsIn, ma config.maxDistance = MA_FLT_MAX; config.rolloff = 1; config.coneInnerAngleInRadians = 6.283185f; /* 360 degrees. */ - config.coneOuterAngleInRadians = 6.283185f; /* 360 degrees. */ + config.coneOuterAngleInRadians = 6.283185f; /* 360 degress. */ config.coneOuterGain = 0.0f; config.dopplerFactor = 1; config.directionalAttenuationFactor = 1; @@ -50748,7 +50253,7 @@ static float ma_calculate_angular_gain(ma_vec3f dirA, ma_vec3f dirB, float coneI To do cone attenuation, I'm just using the same math that we'd use to implement a basic spotlight in OpenGL. We just need to get the direction from the source to the listener and then do a dot product against that and the direction of the spotlight. Then we just compare that dot product against the cosine of the inner and outer - angles. If the dot product is greater than the outer angle, we just use coneOuterGain. If it's less than + angles. If the dot product is greater than the the outer angle, we just use coneOuterGain. If it's less than the inner angle, we just use a gain of 1. Otherwise we linearly interpolate between 1 and coneOuterGain. */ if (coneInnerAngleInRadians < 6.283185f) { @@ -50791,7 +50296,7 @@ MA_API ma_result ma_spatializer_process_pcm_frames(ma_spatializer* pSpatializer, } /* If we're not spatializing we need to run an optimized path. */ - if (ma_atomic_load_i32(&pSpatializer->attenuationModel) == ma_attenuation_model_none) { + if (c89atomic_load_i32(&pSpatializer->attenuationModel) == ma_attenuation_model_none) { if (ma_spatializer_listener_is_enabled(pListener)) { /* No attenuation is required, but we'll need to do some channel conversion. */ if (pSpatializer->channelsIn == pSpatializer->channelsOut) { @@ -50818,7 +50323,7 @@ MA_API ma_result ma_spatializer_process_pcm_frames(ma_spatializer* pSpatializer, ma_vec3f relativePosNormalized; ma_vec3f relativePos; /* The position relative to the listener. */ ma_vec3f relativeDir; /* The direction of the sound, relative to the listener. */ - ma_vec3f listenerVel; /* The velocity of the listener. For doppler pitch calculation. */ + ma_vec3f listenerVel; /* The volocity of the listener. For doppler pitch calculation. */ float speedOfSound; float distance = 0; float gain = 1; @@ -50899,11 +50404,11 @@ MA_API ma_result ma_spatializer_process_pcm_frames(ma_spatializer* pSpatializer, To do cone attenuation, I'm just using the same math that we'd use to implement a basic spotlight in OpenGL. We just need to get the direction from the source to the listener and then do a dot product against that and the direction of the spotlight. Then we just compare that dot product against the cosine of the inner and outer - angles. If the dot product is greater than the outer angle, we just use coneOuterGain. If it's less than + angles. If the dot product is greater than the the outer angle, we just use coneOuterGain. If it's less than the inner angle, we just use a gain of 1. Otherwise we linearly interpolate between 1 and coneOuterGain. */ if (distance > 0) { - /* Source angular gain. */ + /* Source anglular gain. */ float spatializerConeInnerAngle; float spatializerConeOuterAngle; float spatializerConeOuterGain; @@ -51132,7 +50637,7 @@ MA_API void ma_spatializer_set_attenuation_model(ma_spatializer* pSpatializer, m return; } - ma_atomic_exchange_i32(&pSpatializer->attenuationModel, attenuationModel); + c89atomic_exchange_i32(&pSpatializer->attenuationModel, attenuationModel); } MA_API ma_attenuation_model ma_spatializer_get_attenuation_model(const ma_spatializer* pSpatializer) @@ -51141,7 +50646,7 @@ MA_API ma_attenuation_model ma_spatializer_get_attenuation_model(const ma_spatia return ma_attenuation_model_none; } - return (ma_attenuation_model)ma_atomic_load_i32(&pSpatializer->attenuationModel); + return (ma_attenuation_model)c89atomic_load_i32(&pSpatializer->attenuationModel); } MA_API void ma_spatializer_set_positioning(ma_spatializer* pSpatializer, ma_positioning positioning) @@ -51150,7 +50655,7 @@ MA_API void ma_spatializer_set_positioning(ma_spatializer* pSpatializer, ma_posi return; } - ma_atomic_exchange_i32(&pSpatializer->positioning, positioning); + c89atomic_exchange_i32(&pSpatializer->positioning, positioning); } MA_API ma_positioning ma_spatializer_get_positioning(const ma_spatializer* pSpatializer) @@ -51159,7 +50664,7 @@ MA_API ma_positioning ma_spatializer_get_positioning(const ma_spatializer* pSpat return ma_positioning_absolute; } - return (ma_positioning)ma_atomic_load_i32(&pSpatializer->positioning); + return (ma_positioning)c89atomic_load_i32(&pSpatializer->positioning); } MA_API void ma_spatializer_set_rolloff(ma_spatializer* pSpatializer, float rolloff) @@ -51168,7 +50673,7 @@ MA_API void ma_spatializer_set_rolloff(ma_spatializer* pSpatializer, float rollo return; } - ma_atomic_exchange_f32(&pSpatializer->rolloff, rolloff); + c89atomic_exchange_f32(&pSpatializer->rolloff, rolloff); } MA_API float ma_spatializer_get_rolloff(const ma_spatializer* pSpatializer) @@ -51177,7 +50682,7 @@ MA_API float ma_spatializer_get_rolloff(const ma_spatializer* pSpatializer) return 0; } - return ma_atomic_load_f32(&pSpatializer->rolloff); + return c89atomic_load_f32(&pSpatializer->rolloff); } MA_API void ma_spatializer_set_min_gain(ma_spatializer* pSpatializer, float minGain) @@ -51186,7 +50691,7 @@ MA_API void ma_spatializer_set_min_gain(ma_spatializer* pSpatializer, float minG return; } - ma_atomic_exchange_f32(&pSpatializer->minGain, minGain); + c89atomic_exchange_f32(&pSpatializer->minGain, minGain); } MA_API float ma_spatializer_get_min_gain(const ma_spatializer* pSpatializer) @@ -51195,7 +50700,7 @@ MA_API float ma_spatializer_get_min_gain(const ma_spatializer* pSpatializer) return 0; } - return ma_atomic_load_f32(&pSpatializer->minGain); + return c89atomic_load_f32(&pSpatializer->minGain); } MA_API void ma_spatializer_set_max_gain(ma_spatializer* pSpatializer, float maxGain) @@ -51204,7 +50709,7 @@ MA_API void ma_spatializer_set_max_gain(ma_spatializer* pSpatializer, float maxG return; } - ma_atomic_exchange_f32(&pSpatializer->maxGain, maxGain); + c89atomic_exchange_f32(&pSpatializer->maxGain, maxGain); } MA_API float ma_spatializer_get_max_gain(const ma_spatializer* pSpatializer) @@ -51213,7 +50718,7 @@ MA_API float ma_spatializer_get_max_gain(const ma_spatializer* pSpatializer) return 0; } - return ma_atomic_load_f32(&pSpatializer->maxGain); + return c89atomic_load_f32(&pSpatializer->maxGain); } MA_API void ma_spatializer_set_min_distance(ma_spatializer* pSpatializer, float minDistance) @@ -51222,7 +50727,7 @@ MA_API void ma_spatializer_set_min_distance(ma_spatializer* pSpatializer, float return; } - ma_atomic_exchange_f32(&pSpatializer->minDistance, minDistance); + c89atomic_exchange_f32(&pSpatializer->minDistance, minDistance); } MA_API float ma_spatializer_get_min_distance(const ma_spatializer* pSpatializer) @@ -51231,7 +50736,7 @@ MA_API float ma_spatializer_get_min_distance(const ma_spatializer* pSpatializer) return 0; } - return ma_atomic_load_f32(&pSpatializer->minDistance); + return c89atomic_load_f32(&pSpatializer->minDistance); } MA_API void ma_spatializer_set_max_distance(ma_spatializer* pSpatializer, float maxDistance) @@ -51240,7 +50745,7 @@ MA_API void ma_spatializer_set_max_distance(ma_spatializer* pSpatializer, float return; } - ma_atomic_exchange_f32(&pSpatializer->maxDistance, maxDistance); + c89atomic_exchange_f32(&pSpatializer->maxDistance, maxDistance); } MA_API float ma_spatializer_get_max_distance(const ma_spatializer* pSpatializer) @@ -51249,7 +50754,7 @@ MA_API float ma_spatializer_get_max_distance(const ma_spatializer* pSpatializer) return 0; } - return ma_atomic_load_f32(&pSpatializer->maxDistance); + return c89atomic_load_f32(&pSpatializer->maxDistance); } MA_API void ma_spatializer_set_cone(ma_spatializer* pSpatializer, float innerAngleInRadians, float outerAngleInRadians, float outerGain) @@ -51258,9 +50763,9 @@ MA_API void ma_spatializer_set_cone(ma_spatializer* pSpatializer, float innerAng return; } - ma_atomic_exchange_f32(&pSpatializer->coneInnerAngleInRadians, innerAngleInRadians); - ma_atomic_exchange_f32(&pSpatializer->coneOuterAngleInRadians, outerAngleInRadians); - ma_atomic_exchange_f32(&pSpatializer->coneOuterGain, outerGain); + c89atomic_exchange_f32(&pSpatializer->coneInnerAngleInRadians, innerAngleInRadians); + c89atomic_exchange_f32(&pSpatializer->coneOuterAngleInRadians, outerAngleInRadians); + c89atomic_exchange_f32(&pSpatializer->coneOuterGain, outerGain); } MA_API void ma_spatializer_get_cone(const ma_spatializer* pSpatializer, float* pInnerAngleInRadians, float* pOuterAngleInRadians, float* pOuterGain) @@ -51270,15 +50775,15 @@ MA_API void ma_spatializer_get_cone(const ma_spatializer* pSpatializer, float* p } if (pInnerAngleInRadians != NULL) { - *pInnerAngleInRadians = ma_atomic_load_f32(&pSpatializer->coneInnerAngleInRadians); + *pInnerAngleInRadians = c89atomic_load_f32(&pSpatializer->coneInnerAngleInRadians); } if (pOuterAngleInRadians != NULL) { - *pOuterAngleInRadians = ma_atomic_load_f32(&pSpatializer->coneOuterAngleInRadians); + *pOuterAngleInRadians = c89atomic_load_f32(&pSpatializer->coneOuterAngleInRadians); } if (pOuterGain != NULL) { - *pOuterGain = ma_atomic_load_f32(&pSpatializer->coneOuterGain); + *pOuterGain = c89atomic_load_f32(&pSpatializer->coneOuterGain); } } @@ -51288,7 +50793,7 @@ MA_API void ma_spatializer_set_doppler_factor(ma_spatializer* pSpatializer, floa return; } - ma_atomic_exchange_f32(&pSpatializer->dopplerFactor, dopplerFactor); + c89atomic_exchange_f32(&pSpatializer->dopplerFactor, dopplerFactor); } MA_API float ma_spatializer_get_doppler_factor(const ma_spatializer* pSpatializer) @@ -51297,7 +50802,7 @@ MA_API float ma_spatializer_get_doppler_factor(const ma_spatializer* pSpatialize return 1; } - return ma_atomic_load_f32(&pSpatializer->dopplerFactor); + return c89atomic_load_f32(&pSpatializer->dopplerFactor); } MA_API void ma_spatializer_set_directional_attenuation_factor(ma_spatializer* pSpatializer, float directionalAttenuationFactor) @@ -51306,7 +50811,7 @@ MA_API void ma_spatializer_set_directional_attenuation_factor(ma_spatializer* pS return; } - ma_atomic_exchange_f32(&pSpatializer->directionalAttenuationFactor, directionalAttenuationFactor); + c89atomic_exchange_f32(&pSpatializer->directionalAttenuationFactor, directionalAttenuationFactor); } MA_API float ma_spatializer_get_directional_attenuation_factor(const ma_spatializer* pSpatializer) @@ -51315,7 +50820,7 @@ MA_API float ma_spatializer_get_directional_attenuation_factor(const ma_spatiali return 1; } - return ma_atomic_load_f32(&pSpatializer->directionalAttenuationFactor); + return c89atomic_load_f32(&pSpatializer->directionalAttenuationFactor); } MA_API void ma_spatializer_set_position(ma_spatializer* pSpatializer, float x, float y, float z) @@ -51415,7 +50920,7 @@ MA_API void ma_spatializer_get_relative_position_and_direction(const ma_spatiali listenerDirection = ma_spatializer_listener_get_direction(pListener); /* - We need to calculate the right vector from our forward and up vectors. This is done with + We need to calcualte the right vector from our forward and up vectors. This is done with a cross product. */ axisZ = ma_vec3f_normalize(listenerDirection); /* Normalization required here because we can't trust the caller. */ @@ -51561,7 +51066,7 @@ static ma_result ma_linear_resampler_set_rate_internal(ma_linear_resampler* pRes lpfConfig = ma_lpf_config_init(pResampler->config.format, pResampler->config.channels, lpfSampleRate, lpfCutoffFrequency, pResampler->config.lpfOrder); /* - If the resampler is already initialized we don't want to do a fresh initialization of the low-pass filter because it will result in the cached frames + If the resampler is alreay initialized we don't want to do a fresh initialization of the low-pass filter because it will result in the cached frames getting cleared. Instead we re-initialize the filter which will maintain any cached frames. */ if (isResamplerAlreadyInitialized) { @@ -51837,10 +51342,8 @@ static ma_result ma_linear_resampler_process_pcm_frames_s16_downsample(ma_linear } } - /* Filter. Do not apply filtering if sample rates are the same or else you'll get dangerous glitching. */ - if (pResampler->config.sampleRateIn != pResampler->config.sampleRateOut) { - ma_lpf_process_pcm_frame_s16(&pResampler->lpf, pResampler->x1.s16, pResampler->x1.s16); - } + /* Filter. */ + ma_lpf_process_pcm_frame_s16(&pResampler->lpf, pResampler->x1.s16, pResampler->x1.s16); framesProcessedIn += 1; pResampler->inTimeInt -= 1; @@ -51926,10 +51429,8 @@ static ma_result ma_linear_resampler_process_pcm_frames_s16_upsample(ma_linear_r MA_ASSERT(pResampler->inTimeInt == 0); ma_linear_resampler_interpolate_frame_s16(pResampler, pFramesOutS16); - /* Filter. Do not apply filtering if sample rates are the same or else you'll get dangerous glitching. */ - if (pResampler->config.sampleRateIn != pResampler->config.sampleRateOut) { - ma_lpf_process_pcm_frame_s16(&pResampler->lpf, pFramesOutS16, pFramesOutS16); - } + /* Filter. */ + ma_lpf_process_pcm_frame_s16(&pResampler->lpf, pFramesOutS16, pFramesOutS16); pFramesOutS16 += pResampler->config.channels; } @@ -52001,10 +51502,8 @@ static ma_result ma_linear_resampler_process_pcm_frames_f32_downsample(ma_linear } } - /* Filter. Do not apply filtering if sample rates are the same or else you'll get dangerous glitching. */ - if (pResampler->config.sampleRateIn != pResampler->config.sampleRateOut) { - ma_lpf_process_pcm_frame_f32(&pResampler->lpf, pResampler->x1.f32, pResampler->x1.f32); - } + /* Filter. */ + ma_lpf_process_pcm_frame_f32(&pResampler->lpf, pResampler->x1.f32, pResampler->x1.f32); framesProcessedIn += 1; pResampler->inTimeInt -= 1; @@ -52090,10 +51589,8 @@ static ma_result ma_linear_resampler_process_pcm_frames_f32_upsample(ma_linear_r MA_ASSERT(pResampler->inTimeInt == 0); ma_linear_resampler_interpolate_frame_f32(pResampler, pFramesOutF32); - /* Filter. Do not apply filtering if sample rates are the same or else you'll get dangerous glitching. */ - if (pResampler->config.sampleRateIn != pResampler->config.sampleRateOut) { - ma_lpf_process_pcm_frame_f32(&pResampler->lpf, pFramesOutF32, pFramesOutF32); - } + /* Filter. */ + ma_lpf_process_pcm_frame_f32(&pResampler->lpf, pFramesOutF32, pFramesOutF32); pFramesOutF32 += pResampler->config.channels; } @@ -52163,7 +51660,7 @@ MA_API ma_result ma_linear_resampler_set_rate_ratio(ma_linear_resampler* pResamp return MA_INVALID_ARGS; } - d = 1000000; + d = 1000; n = (ma_uint32)(ratioInOut * d); if (n == 0) { @@ -52256,7 +51753,7 @@ MA_API ma_result ma_linear_resampler_get_expected_output_frame_count(const ma_li preliminaryInputFrameCount = (pResampler->inTimeInt + outputFrameCount*pResampler->inAdvanceInt ) + preliminaryInputFrameCountFromFrac; /* - If the total number of *whole* input frames that would be required to generate our preliminary output frame count is greater than + If the total number of *whole* input frames that would be required to generate our preliminary output frame count is greather than the amount of whole input frames we have available as input we need to *not* add an extra output frame as there won't be enough data to actually process. Otherwise we need to add the extra output frame. */ @@ -52294,7 +51791,7 @@ MA_API ma_result ma_linear_resampler_reset(ma_linear_resampler* pResampler) } } - /* The low pass filter needs to have its cache reset. */ + /* The low pass filter needs to have it's cache reset. */ ma_lpf_clear_cache(&pResampler->lpf); return MA_SUCCESS; @@ -52811,19 +52308,19 @@ static float ma_calculate_channel_position_rectangular_weight(ma_channel channel of contribution to apply to the side/left and back/left speakers, however, is a bit more complicated. Imagine the front/left speaker as emitting audio from two planes - the front plane and the left plane. You can think of the front/left - speaker emitting half of its total volume from the front, and the other half from the left. Since part of its volume is being emitted + speaker emitting half of it's total volume from the front, and the other half from the left. Since part of it's volume is being emitted from the left side, and the side/left and back/left channels also emit audio from the left plane, one would expect that they would receive some amount of contribution from front/left speaker. The amount of contribution depends on how many planes are shared between the two speakers. Note that in the examples below I've added a top/front/left speaker as an example just to show how the math works across 3 spatial dimensions. The first thing to do is figure out how each speaker's volume is spread over each of plane: - - front/left: 2 planes (front and left) = 1/2 = half its total volume on each plane + - front/left: 2 planes (front and left) = 1/2 = half it's total volume on each plane - side/left: 1 plane (left only) = 1/1 = entire volume from left plane - - back/left: 2 planes (back and left) = 1/2 = half its total volume on each plane - - top/front/left: 3 planes (top, front and left) = 1/3 = one third its total volume on each plane + - back/left: 2 planes (back and left) = 1/2 = half it's total volume on each plane + - top/front/left: 3 planes (top, front and left) = 1/3 = one third it's total volume on each plane - The amount of volume each channel contributes to each of its planes is what controls how much it is willing to given and take to other + The amount of volume each channel contributes to each of it's planes is what controls how much it is willing to given and take to other channels on the same plane. The volume that is willing to the given by one channel is multiplied by the volume that is willing to be taken by the other to produce the final contribution. */ @@ -52934,7 +52431,12 @@ static ma_channel_conversion_path ma_channel_map_get_conversion_path(const ma_ch ma_uint32 iChannelIn; ma_bool32 areAllChannelPositionsPresent = MA_TRUE; for (iChannelIn = 0; iChannelIn < channelsIn; ++iChannelIn) { - ma_bool32 isInputChannelPositionInOutput = ma_channel_map_contains_channel_position(channelsOut, pChannelMapOut, ma_channel_map_get_channel(pChannelMapIn, channelsIn, iChannelIn)); + ma_bool32 isInputChannelPositionInOutput = MA_FALSE; + if (ma_channel_map_contains_channel_position(channelsOut, pChannelMapOut, ma_channel_map_get_channel(pChannelMapIn, channelsIn, iChannelIn))) { + isInputChannelPositionInOutput = MA_TRUE; + break; + } + if (!isInputChannelPositionInOutput) { areAllChannelPositionsPresent = MA_FALSE; break; @@ -52961,8 +52463,8 @@ static ma_result ma_channel_map_build_shuffle_table(const ma_channel* pChannelMa } /* - When building the shuffle table we just do a 1:1 mapping based on the first occurrence of a channel. If the - input channel has more than one occurrence of a channel position, the second one will be ignored. + When building the shuffle table we just do a 1:1 mapping based on the first occurance of a channel. If the + input channel has more than one occurance of a channel position, the second one will be ignored. */ for (iChannelOut = 0; iChannelOut < channelCountOut; iChannelOut += 1) { ma_channel channelOut; @@ -53337,7 +52839,7 @@ static ma_result ma_channel_map_apply_mono_in_f32(float* MA_RESTRICT pFramesOut, for (iFrame = 0; iFrame < unrolledFrameCount; iFrame += 1) { __m128 in0 = _mm_set1_ps(pFramesIn[iFrame*2 + 0]); __m128 in1 = _mm_set1_ps(pFramesIn[iFrame*2 + 1]); - _mm_storeu_ps(&pFramesOut[iFrame*4 + 0], _mm_shuffle_ps(in0, in1, _MM_SHUFFLE(0, 0, 0, 0))); + _mm_storeu_ps(&pFramesOut[iFrame*4 + 0], _mm_shuffle_ps(in1, in0, _MM_SHUFFLE(0, 0, 0, 0))); } /* Tail. */ @@ -53363,7 +52865,7 @@ static ma_result ma_channel_map_apply_mono_in_f32(float* MA_RESTRICT pFramesOut, __m128 in1 = _mm_set1_ps(pFramesIn[iFrame*2 + 1]); _mm_storeu_ps(&pFramesOut[iFrame*12 + 0], in0); - _mm_storeu_ps(&pFramesOut[iFrame*12 + 4], _mm_shuffle_ps(in0, in1, _MM_SHUFFLE(0, 0, 0, 0))); + _mm_storeu_ps(&pFramesOut[iFrame*12 + 4], _mm_shuffle_ps(in1, in0, _MM_SHUFFLE(0, 0, 0, 0))); _mm_storeu_ps(&pFramesOut[iFrame*12 + 8], in1); } @@ -55257,7 +54759,7 @@ static ma_result ma_data_converter_process_pcm_frames__channels_first(ma_data_co Before doing any processing we need to determine how many frames we should try processing this iteration, for both input and output. The resampler requires us to perform format and channel conversion before passing any data into it. If we get our input count wrong, we'll - end up performing redundant pre-processing. This isn't the end of the world, but it does + end up peforming redundant pre-processing. This isn't the end of the world, but it does result in some inefficiencies proportionate to how far our estimates are off. If the resampler has a means to calculate exactly how much we'll need, we'll use that. @@ -56427,7 +55929,7 @@ MA_API const char* ma_channel_position_to_string(ma_channel channel) case MA_CHANNEL_LFE : return "CHANNEL_LFE"; case MA_CHANNEL_BACK_LEFT : return "CHANNEL_BACK_LEFT"; case MA_CHANNEL_BACK_RIGHT : return "CHANNEL_BACK_RIGHT"; - case MA_CHANNEL_FRONT_LEFT_CENTER : return "CHANNEL_FRONT_LEFT_CENTER"; + case MA_CHANNEL_FRONT_LEFT_CENTER : return "CHANNEL_FRONT_LEFT_CENTER "; case MA_CHANNEL_FRONT_RIGHT_CENTER: return "CHANNEL_FRONT_RIGHT_CENTER"; case MA_CHANNEL_BACK_CENTER : return "CHANNEL_BACK_CENTER"; case MA_CHANNEL_SIDE_LEFT : return "CHANNEL_SIDE_LEFT"; @@ -56558,13 +56060,13 @@ static MA_INLINE ma_uint32 ma_rb__extract_offset_loop_flag(ma_uint32 encodedOffs static MA_INLINE void* ma_rb__get_read_ptr(ma_rb* pRB) { MA_ASSERT(pRB != NULL); - return ma_offset_ptr(pRB->pBuffer, ma_rb__extract_offset_in_bytes(ma_atomic_load_32(&pRB->encodedReadOffset))); + return ma_offset_ptr(pRB->pBuffer, ma_rb__extract_offset_in_bytes(c89atomic_load_32(&pRB->encodedReadOffset))); } static MA_INLINE void* ma_rb__get_write_ptr(ma_rb* pRB) { MA_ASSERT(pRB != NULL); - return ma_offset_ptr(pRB->pBuffer, ma_rb__extract_offset_in_bytes(ma_atomic_load_32(&pRB->encodedWriteOffset))); + return ma_offset_ptr(pRB->pBuffer, ma_rb__extract_offset_in_bytes(c89atomic_load_32(&pRB->encodedWriteOffset))); } static MA_INLINE ma_uint32 ma_rb__construct_offset(ma_uint32 offsetInBytes, ma_uint32 offsetLoopFlag) @@ -56657,8 +56159,8 @@ MA_API void ma_rb_reset(ma_rb* pRB) return; } - ma_atomic_exchange_32(&pRB->encodedReadOffset, 0); - ma_atomic_exchange_32(&pRB->encodedWriteOffset, 0); + c89atomic_exchange_32(&pRB->encodedReadOffset, 0); + c89atomic_exchange_32(&pRB->encodedWriteOffset, 0); } MA_API ma_result ma_rb_acquire_read(ma_rb* pRB, size_t* pSizeInBytes, void** ppBufferOut) @@ -56677,10 +56179,10 @@ MA_API ma_result ma_rb_acquire_read(ma_rb* pRB, size_t* pSizeInBytes, void** ppB } /* The returned buffer should never move ahead of the write pointer. */ - writeOffset = ma_atomic_load_32(&pRB->encodedWriteOffset); + writeOffset = c89atomic_load_32(&pRB->encodedWriteOffset); ma_rb__deconstruct_offset(writeOffset, &writeOffsetInBytes, &writeOffsetLoopFlag); - readOffset = ma_atomic_load_32(&pRB->encodedReadOffset); + readOffset = c89atomic_load_32(&pRB->encodedReadOffset); ma_rb__deconstruct_offset(readOffset, &readOffsetInBytes, &readOffsetLoopFlag); /* @@ -56716,7 +56218,7 @@ MA_API ma_result ma_rb_commit_read(ma_rb* pRB, size_t sizeInBytes) return MA_INVALID_ARGS; } - readOffset = ma_atomic_load_32(&pRB->encodedReadOffset); + readOffset = c89atomic_load_32(&pRB->encodedReadOffset); ma_rb__deconstruct_offset(readOffset, &readOffsetInBytes, &readOffsetLoopFlag); /* Check that sizeInBytes is correct. It should never go beyond the end of the buffer. */ @@ -56732,9 +56234,13 @@ MA_API ma_result ma_rb_commit_read(ma_rb* pRB, size_t sizeInBytes) newReadOffsetLoopFlag ^= 0x80000000; } - ma_atomic_exchange_32(&pRB->encodedReadOffset, ma_rb__construct_offset(newReadOffsetInBytes, newReadOffsetLoopFlag)); + c89atomic_exchange_32(&pRB->encodedReadOffset, ma_rb__construct_offset(newReadOffsetLoopFlag, newReadOffsetInBytes)); - return MA_SUCCESS; + if (ma_rb_pointer_distance(pRB) == 0) { + return MA_AT_END; + } else { + return MA_SUCCESS; + } } MA_API ma_result ma_rb_acquire_write(ma_rb* pRB, size_t* pSizeInBytes, void** ppBufferOut) @@ -56753,10 +56259,10 @@ MA_API ma_result ma_rb_acquire_write(ma_rb* pRB, size_t* pSizeInBytes, void** pp } /* The returned buffer should never overtake the read buffer. */ - readOffset = ma_atomic_load_32(&pRB->encodedReadOffset); + readOffset = c89atomic_load_32(&pRB->encodedReadOffset); ma_rb__deconstruct_offset(readOffset, &readOffsetInBytes, &readOffsetLoopFlag); - writeOffset = ma_atomic_load_32(&pRB->encodedWriteOffset); + writeOffset = c89atomic_load_32(&pRB->encodedWriteOffset); ma_rb__deconstruct_offset(writeOffset, &writeOffsetInBytes, &writeOffsetLoopFlag); /* @@ -56798,7 +56304,7 @@ MA_API ma_result ma_rb_commit_write(ma_rb* pRB, size_t sizeInBytes) return MA_INVALID_ARGS; } - writeOffset = ma_atomic_load_32(&pRB->encodedWriteOffset); + writeOffset = c89atomic_load_32(&pRB->encodedWriteOffset); ma_rb__deconstruct_offset(writeOffset, &writeOffsetInBytes, &writeOffsetLoopFlag); /* Check that sizeInBytes is correct. It should never go beyond the end of the buffer. */ @@ -56814,9 +56320,13 @@ MA_API ma_result ma_rb_commit_write(ma_rb* pRB, size_t sizeInBytes) newWriteOffsetLoopFlag ^= 0x80000000; } - ma_atomic_exchange_32(&pRB->encodedWriteOffset, ma_rb__construct_offset(newWriteOffsetInBytes, newWriteOffsetLoopFlag)); + c89atomic_exchange_32(&pRB->encodedWriteOffset, ma_rb__construct_offset(newWriteOffsetLoopFlag, newWriteOffsetInBytes)); - return MA_SUCCESS; + if (ma_rb_pointer_distance(pRB) == 0) { + return MA_AT_END; + } else { + return MA_SUCCESS; + } } MA_API ma_result ma_rb_seek_read(ma_rb* pRB, size_t offsetInBytes) @@ -56834,10 +56344,10 @@ MA_API ma_result ma_rb_seek_read(ma_rb* pRB, size_t offsetInBytes) return MA_INVALID_ARGS; } - readOffset = ma_atomic_load_32(&pRB->encodedReadOffset); + readOffset = c89atomic_load_32(&pRB->encodedReadOffset); ma_rb__deconstruct_offset(readOffset, &readOffsetInBytes, &readOffsetLoopFlag); - writeOffset = ma_atomic_load_32(&pRB->encodedWriteOffset); + writeOffset = c89atomic_load_32(&pRB->encodedWriteOffset); ma_rb__deconstruct_offset(writeOffset, &writeOffsetInBytes, &writeOffsetLoopFlag); newReadOffsetLoopFlag = readOffsetLoopFlag; @@ -56859,7 +56369,7 @@ MA_API ma_result ma_rb_seek_read(ma_rb* pRB, size_t offsetInBytes) } } - ma_atomic_exchange_32(&pRB->encodedReadOffset, ma_rb__construct_offset(newReadOffsetInBytes, newReadOffsetLoopFlag)); + c89atomic_exchange_32(&pRB->encodedReadOffset, ma_rb__construct_offset(newReadOffsetInBytes, newReadOffsetLoopFlag)); return MA_SUCCESS; } @@ -56878,10 +56388,10 @@ MA_API ma_result ma_rb_seek_write(ma_rb* pRB, size_t offsetInBytes) return MA_INVALID_ARGS; } - readOffset = ma_atomic_load_32(&pRB->encodedReadOffset); + readOffset = c89atomic_load_32(&pRB->encodedReadOffset); ma_rb__deconstruct_offset(readOffset, &readOffsetInBytes, &readOffsetLoopFlag); - writeOffset = ma_atomic_load_32(&pRB->encodedWriteOffset); + writeOffset = c89atomic_load_32(&pRB->encodedWriteOffset); ma_rb__deconstruct_offset(writeOffset, &writeOffsetInBytes, &writeOffsetLoopFlag); newWriteOffsetLoopFlag = writeOffsetLoopFlag; @@ -56903,7 +56413,7 @@ MA_API ma_result ma_rb_seek_write(ma_rb* pRB, size_t offsetInBytes) } } - ma_atomic_exchange_32(&pRB->encodedWriteOffset, ma_rb__construct_offset(newWriteOffsetInBytes, newWriteOffsetLoopFlag)); + c89atomic_exchange_32(&pRB->encodedWriteOffset, ma_rb__construct_offset(newWriteOffsetInBytes, newWriteOffsetLoopFlag)); return MA_SUCCESS; } @@ -56920,10 +56430,10 @@ MA_API ma_int32 ma_rb_pointer_distance(ma_rb* pRB) return 0; } - readOffset = ma_atomic_load_32(&pRB->encodedReadOffset); + readOffset = c89atomic_load_32(&pRB->encodedReadOffset); ma_rb__deconstruct_offset(readOffset, &readOffsetInBytes, &readOffsetLoopFlag); - writeOffset = ma_atomic_load_32(&pRB->encodedWriteOffset); + writeOffset = c89atomic_load_32(&pRB->encodedWriteOffset); ma_rb__deconstruct_offset(writeOffset, &writeOffsetInBytes, &writeOffsetLoopFlag); if (readOffsetLoopFlag == writeOffsetLoopFlag) { @@ -57018,7 +56528,7 @@ static ma_result ma_pcm_rb_data_source__on_read(ma_data_source* pDataSource, voi if (framesToRead > 0xFFFFFFFF) { framesToRead = 0xFFFFFFFF; } - + mappedFrameCount = (ma_uint32)framesToRead; result = ma_pcm_rb_acquire_read(pRB, &mappedFrameCount, &pMappedBuffer); if (result != MA_SUCCESS) { @@ -57039,16 +56549,6 @@ static ma_result ma_pcm_rb_data_source__on_read(ma_data_source* pDataSource, voi totalFramesRead += mappedFrameCount; } - /* - There is no notion of an "end" in a ring buffer. If we didn't have enough data to fill the requested frame - count we'll need to pad with silence. If we don't do this, totalFramesRead might equal 0 which will result - in the data source layer at a higher level translating this to MA_AT_END which is incorrect for a ring buffer. - */ - if (totalFramesRead < frameCount) { - ma_silence_pcm_frames(ma_offset_pcm_frames_ptr(pFramesOut, totalFramesRead, pRB->format, pRB->channels), (frameCount - totalFramesRead), pRB->format, pRB->channels); - totalFramesRead = frameCount; - } - *pFramesRead = totalFramesRead; return MA_SUCCESS; } @@ -57078,7 +56578,7 @@ static ma_result ma_pcm_rb_data_source__on_get_data_format(ma_data_source* pData return MA_SUCCESS; } -static ma_data_source_vtable ma_gRBDataSourceVTable = +static ma_data_source_vtable ma_gRBDataSourceVTable = { ma_pcm_rb_data_source__on_read, NULL, /* onSeek */ @@ -57597,10 +57097,6 @@ MA_API ma_result ma_data_source_init(const ma_data_source_config* pConfig, ma_da return MA_INVALID_ARGS; } - if (pConfig->vtable == NULL) { - return MA_INVALID_ARGS; - } - pDataSourceBase->vtable = pConfig->vtable; pDataSourceBase->rangeBegInFrames = MA_DATA_SOURCE_DEFAULT_RANGE_BEG; pDataSourceBase->rangeEndInFrames = MA_DATA_SOURCE_DEFAULT_RANGE_END; @@ -57651,58 +57147,6 @@ static ma_result ma_data_source_resolve_current(ma_data_source* pDataSource, ma_ return MA_SUCCESS; } -static ma_result ma_data_source_read_pcm_frames_from_backend(ma_data_source* pDataSource, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead) -{ - ma_data_source_base* pDataSourceBase = (ma_data_source_base*)pDataSource; - - MA_ASSERT(pDataSourceBase != NULL); - MA_ASSERT(pDataSourceBase->vtable != NULL); - MA_ASSERT(pDataSourceBase->vtable->onRead != NULL); - MA_ASSERT(pFramesRead != NULL); - - if (pFramesOut != NULL) { - return pDataSourceBase->vtable->onRead(pDataSourceBase, pFramesOut, frameCount, pFramesRead); - } else { - /* - No output buffer. Probably seeking forward. Read and discard. Can probably optimize this in terms of - onSeek and onGetCursor, but need to keep in mind that the data source may not implement these functions. - */ - ma_result result; - ma_uint64 framesRead; - ma_format format; - ma_uint32 channels; - ma_uint64 discardBufferCapInFrames; - ma_uint8 pDiscardBuffer[4096]; - - result = ma_data_source_get_data_format(pDataSource, &format, &channels, NULL, NULL, 0); - if (result != MA_SUCCESS) { - return result; - } - - discardBufferCapInFrames = sizeof(pDiscardBuffer) / ma_get_bytes_per_frame(format, channels); - - framesRead = 0; - while (framesRead < frameCount) { - ma_uint64 framesReadThisIteration = 0; - ma_uint64 framesToRead = frameCount - framesRead; - if (framesToRead > discardBufferCapInFrames) { - framesToRead = discardBufferCapInFrames; - } - - result = pDataSourceBase->vtable->onRead(pDataSourceBase, pDiscardBuffer, framesToRead, &framesReadThisIteration); - if (result != MA_SUCCESS) { - return result; - } - - framesRead += framesReadThisIteration; - } - - *pFramesRead = framesRead; - - return MA_SUCCESS; - } -} - static ma_result ma_data_source_read_pcm_frames_within_range(ma_data_source* pDataSource, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead) { ma_data_source_base* pDataSourceBase = (ma_data_source_base*)pDataSource; @@ -57718,11 +57162,9 @@ static ma_result ma_data_source_read_pcm_frames_within_range(ma_data_source* pDa return MA_INVALID_ARGS; } - MA_ASSERT(pDataSourceBase->vtable != NULL); - if ((pDataSourceBase->vtable->flags & MA_DATA_SOURCE_SELF_MANAGED_RANGE_AND_LOOP_POINT) != 0 || (pDataSourceBase->rangeEndInFrames == ~((ma_uint64)0) && (pDataSourceBase->loopEndInFrames == ~((ma_uint64)0) || loop == MA_FALSE))) { /* Either the data source is self-managing the range, or no range is set - just read like normal. The data source itself will tell us when the end is reached. */ - result = ma_data_source_read_pcm_frames_from_backend(pDataSource, pFramesOut, frameCount, &framesRead); + result = pDataSourceBase->vtable->onRead(pDataSourceBase, pFramesOut, frameCount, &framesRead); } else { /* Need to clamp to within the range. */ ma_uint64 relativeCursor; @@ -57731,7 +57173,7 @@ static ma_result ma_data_source_read_pcm_frames_within_range(ma_data_source* pDa result = ma_data_source_get_cursor_in_pcm_frames(pDataSourceBase, &relativeCursor); if (result != MA_SUCCESS) { /* Failed to retrieve the cursor. Cannot read within a range or loop points. Just read like normal - this may happen for things like noise data sources where it doesn't really matter. */ - result = ma_data_source_read_pcm_frames_from_backend(pDataSource, pFramesOut, frameCount, &framesRead); + result = pDataSourceBase->vtable->onRead(pDataSourceBase, pFramesOut, frameCount, &framesRead); } else { ma_uint64 rangeBeg; ma_uint64 rangeEnd; @@ -57759,7 +57201,7 @@ static ma_result ma_data_source_read_pcm_frames_within_range(ma_data_source* pDa MA_AT_END so the higher level function can know about it. */ if (frameCount > 0) { - result = ma_data_source_read_pcm_frames_from_backend(pDataSource, pFramesOut, frameCount, &framesRead); + result = pDataSourceBase->vtable->onRead(pDataSourceBase, pFramesOut, frameCount, &framesRead); } else { result = MA_AT_END; /* The cursor is sitting on the end of the range which means we're at the end. */ } @@ -57841,7 +57283,7 @@ MA_API ma_result ma_data_source_read_pcm_frames(ma_data_source* pDataSource, voi totalFramesProcessed += framesProcessed; /* - If we encountered an error from the read callback, make sure it's propagated to the caller. The caller may need to know whether or not MA_BUSY is returned which is + If we encounted an error from the read callback, make sure it's propagated to the caller. The caller may need to know whether or not MA_BUSY is returned which is not necessarily considered an error. */ if (result != MA_SUCCESS && result != MA_AT_END) { @@ -57932,7 +57374,7 @@ MA_API ma_result ma_data_source_seek_to_pcm_frame(ma_data_source* pDataSource, m ma_data_source_base* pDataSourceBase = (ma_data_source_base*)pDataSource; if (pDataSourceBase == NULL) { - return MA_INVALID_ARGS; + return MA_SUCCESS; } if (pDataSourceBase->vtable->onSeek == NULL) { @@ -57940,61 +57382,12 @@ MA_API ma_result ma_data_source_seek_to_pcm_frame(ma_data_source* pDataSource, m } if (frameIndex > pDataSourceBase->rangeEndInFrames) { - return MA_INVALID_OPERATION; /* Trying to seek too far forward. */ + return MA_INVALID_OPERATION; /* Trying to seek to far forward. */ } - MA_ASSERT(pDataSourceBase->vtable != NULL); - return pDataSourceBase->vtable->onSeek(pDataSource, pDataSourceBase->rangeBegInFrames + frameIndex); } -MA_API ma_result ma_data_source_seek_seconds(ma_data_source* pDataSource, float secondCount, float* pSecondsSeeked) -{ - ma_uint64 frameCount; - ma_uint64 framesSeeked = 0; - ma_uint32 sampleRate; - ma_result result; - - if (pDataSource == NULL) { - return MA_INVALID_ARGS; - } - - result = ma_data_source_get_data_format(pDataSource, NULL, NULL, &sampleRate, NULL, 0); - if (result != MA_SUCCESS) { - return result; - } - - /* We need PCM frames instead of seconds */ - frameCount = (ma_uint64)(secondCount * sampleRate); - - result = ma_data_source_seek_pcm_frames(pDataSource, frameCount, &framesSeeked); - - /* VC6 doesn't support division between unsigned 64-bit integer and floating point number. Signed integer needed. This shouldn't affect anything in practice */ - *pSecondsSeeked = (ma_int64)framesSeeked / (float)sampleRate; - return result; -} - -MA_API ma_result ma_data_source_seek_to_second(ma_data_source* pDataSource, float seekPointInSeconds) -{ - ma_uint64 frameIndex; - ma_uint32 sampleRate; - ma_result result; - - if (pDataSource == NULL) { - return MA_INVALID_ARGS; - } - - result = ma_data_source_get_data_format(pDataSource, NULL, NULL, &sampleRate, NULL, 0); - if (result != MA_SUCCESS) { - return result; - } - - /* We need PCM frames instead of seconds */ - frameIndex = (ma_uint64)(seekPointInSeconds * sampleRate); - - return ma_data_source_seek_to_pcm_frame(pDataSource, frameIndex); -} - MA_API ma_result ma_data_source_get_data_format(ma_data_source* pDataSource, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate, ma_channel* pChannelMap, size_t channelMapCap) { ma_data_source_base* pDataSourceBase = (ma_data_source_base*)pDataSource; @@ -58021,8 +57414,6 @@ MA_API ma_result ma_data_source_get_data_format(ma_data_source* pDataSource, ma_ return MA_INVALID_ARGS; } - MA_ASSERT(pDataSourceBase->vtable != NULL); - if (pDataSourceBase->vtable->onGetDataFormat == NULL) { return MA_NOT_IMPLEMENTED; } @@ -58063,8 +57454,6 @@ MA_API ma_result ma_data_source_get_cursor_in_pcm_frames(ma_data_source* pDataSo return MA_SUCCESS; } - MA_ASSERT(pDataSourceBase->vtable != NULL); - if (pDataSourceBase->vtable->onGetCursor == NULL) { return MA_NOT_IMPLEMENTED; } @@ -58098,8 +57487,6 @@ MA_API ma_result ma_data_source_get_length_in_pcm_frames(ma_data_source* pDataSo return MA_INVALID_ARGS; } - MA_ASSERT(pDataSourceBase->vtable != NULL); - /* If we have a range defined we'll use that to determine the length. This is one of rare times where we'll actually trust the caller. If they've set the range, I think it's mostly safe to @@ -58185,9 +57572,7 @@ MA_API ma_result ma_data_source_set_looping(ma_data_source* pDataSource, ma_bool return MA_INVALID_ARGS; } - ma_atomic_exchange_32(&pDataSourceBase->isLooping, isLooping); - - MA_ASSERT(pDataSourceBase->vtable != NULL); + c89atomic_exchange_32(&pDataSourceBase->isLooping, isLooping); /* If there's no callback for this just treat it as a successful no-op. */ if (pDataSourceBase->vtable->onSetLooping == NULL) { @@ -58205,7 +57590,7 @@ MA_API ma_bool32 ma_data_source_is_looping(const ma_data_source* pDataSource) return MA_FALSE; } - return ma_atomic_load_32(&pDataSourceBase->isLooping); + return c89atomic_load_32(&pDataSourceBase->isLooping); } MA_API ma_result ma_data_source_set_range_in_pcm_frames(ma_data_source* pDataSource, ma_uint64 rangeBegInFrames, ma_uint64 rangeEndInFrames) @@ -58226,7 +57611,7 @@ MA_API ma_result ma_data_source_set_range_in_pcm_frames(ma_data_source* pDataSou /* We may need to adjust the position of the cursor to ensure it's clamped to the range. Grab it now - so we can calculate its absolute position before we change the range. + so we can calculate it's absolute position before we change the range. */ result = ma_data_source_get_cursor_in_pcm_frames(pDataSource, &relativeCursor); if (result == MA_SUCCESS) { @@ -58257,10 +57642,10 @@ MA_API ma_result ma_data_source_set_range_in_pcm_frames(ma_data_source* pDataSou pDataSourceBase->loopBegInFrames = 0; pDataSourceBase->loopEndInFrames = ~((ma_uint64)0); - + /* Seek to within range. Note that our seek positions here are relative to the new range. We don't want - to do this if we failed to retrieve the cursor earlier on because it probably means the data source + do do this if we failed to retrieve the cursor earlier on because it probably means the data source has no notion of a cursor. In practice the seek would probably fail (which we silently ignore), but I'm just not even going to attempt it. */ @@ -58279,13 +57664,6 @@ MA_API void ma_data_source_get_range_in_pcm_frames(const ma_data_source* pDataSo { const ma_data_source_base* pDataSourceBase = (const ma_data_source_base*)pDataSource; - if (pRangeBegInFrames != NULL) { - *pRangeBegInFrames = 0; - } - if (pRangeEndInFrames != NULL) { - *pRangeEndInFrames = 0; - } - if (pDataSource == NULL) { return; } @@ -58330,13 +57708,6 @@ MA_API void ma_data_source_get_loop_point_in_pcm_frames(const ma_data_source* pD { const ma_data_source_base* pDataSourceBase = (const ma_data_source_base*)pDataSource; - if (pLoopBegInFrames != NULL) { - *pLoopBegInFrames = 0; - } - if (pLoopEndInFrames != NULL) { - *pLoopEndInFrames = 0; - } - if (pDataSource == NULL) { return; } @@ -58985,9 +58356,9 @@ MA_API void ma_paged_audio_buffer_data_uninit(ma_paged_audio_buffer_data* pData, } /* All pages need to be freed. */ - pPage = (ma_paged_audio_buffer_page*)ma_atomic_load_ptr(&pData->head.pNext); + pPage = (ma_paged_audio_buffer_page*)c89atomic_load_ptr(&pData->head.pNext); while (pPage != NULL) { - ma_paged_audio_buffer_page* pNext = (ma_paged_audio_buffer_page*)ma_atomic_load_ptr(&pPage->pNext); + ma_paged_audio_buffer_page* pNext = (ma_paged_audio_buffer_page*)c89atomic_load_ptr(&pPage->pNext); ma_free(pPage, pAllocationCallbacks); pPage = pNext; @@ -59027,7 +58398,7 @@ MA_API ma_result ma_paged_audio_buffer_data_get_length_in_pcm_frames(ma_paged_au } /* Calculate the length from the linked list. */ - for (pPage = (ma_paged_audio_buffer_page*)ma_atomic_load_ptr(&pData->head.pNext); pPage != NULL; pPage = (ma_paged_audio_buffer_page*)ma_atomic_load_ptr(&pPage->pNext)) { + for (pPage = (ma_paged_audio_buffer_page*)c89atomic_load_ptr(&pData->head.pNext); pPage != NULL; pPage = (ma_paged_audio_buffer_page*)c89atomic_load_ptr(&pPage->pNext)) { *pLength += pPage->sizeInFrames; } @@ -59093,12 +58464,12 @@ MA_API ma_result ma_paged_audio_buffer_data_append_page(ma_paged_audio_buffer_da /* First thing to do is update the tail. */ for (;;) { - ma_paged_audio_buffer_page* pOldTail = (ma_paged_audio_buffer_page*)ma_atomic_load_ptr(&pData->pTail); + ma_paged_audio_buffer_page* pOldTail = (ma_paged_audio_buffer_page*)c89atomic_load_ptr(&pData->pTail); ma_paged_audio_buffer_page* pNewTail = pPage; - if (ma_atomic_compare_exchange_weak_ptr((volatile void**)&pData->pTail, (void**)&pOldTail, pNewTail)) { + if (c89atomic_compare_exchange_weak_ptr((volatile void**)&pData->pTail, (void**)&pOldTail, pNewTail)) { /* Here is where we append the page to the list. After this, the page is attached to the list and ready to be read from. */ - ma_atomic_exchange_ptr(&pOldTail->pNext, pPage); + c89atomic_exchange_ptr(&pOldTail->pNext, pPage); break; /* Done. */ } } @@ -59255,7 +58626,7 @@ MA_API ma_result ma_paged_audio_buffer_read_pcm_frames(ma_paged_audio_buffer* pP if (pPagedAudioBuffer->relativeCursor == pPagedAudioBuffer->pCurrent->sizeInFrames) { /* We reached the end of the page. Need to move to the next. If there's no more pages, we're done. */ - ma_paged_audio_buffer_page* pNext = (ma_paged_audio_buffer_page*)ma_atomic_load_ptr(&pPagedAudioBuffer->pCurrent->pNext); + ma_paged_audio_buffer_page* pNext = (ma_paged_audio_buffer_page*)c89atomic_load_ptr(&pPagedAudioBuffer->pCurrent->pNext); if (pNext == NULL) { result = MA_AT_END; break; /* We've reached the end. */ @@ -59297,12 +58668,12 @@ MA_API ma_result ma_paged_audio_buffer_seek_to_pcm_frame(ma_paged_audio_buffer* ma_paged_audio_buffer_page* pPage; ma_uint64 runningCursor = 0; - for (pPage = (ma_paged_audio_buffer_page*)ma_atomic_load_ptr(&ma_paged_audio_buffer_data_get_head(pPagedAudioBuffer->pData)->pNext); pPage != NULL; pPage = (ma_paged_audio_buffer_page*)ma_atomic_load_ptr(&pPage->pNext)) { + for (pPage = (ma_paged_audio_buffer_page*)c89atomic_load_ptr(&ma_paged_audio_buffer_data_get_head(pPagedAudioBuffer->pData)->pNext); pPage != NULL; pPage = (ma_paged_audio_buffer_page*)c89atomic_load_ptr(&pPage->pNext)) { ma_uint64 pageRangeBeg = runningCursor; ma_uint64 pageRangeEnd = pageRangeBeg + pPage->sizeInFrames; if (frameIndex >= pageRangeBeg) { - if (frameIndex < pageRangeEnd || (frameIndex == pageRangeEnd && pPage == (ma_paged_audio_buffer_page*)ma_atomic_load_ptr(ma_paged_audio_buffer_data_get_tail(pPagedAudioBuffer->pData)))) { /* A small edge case - allow seeking to the very end of the buffer. */ + if (frameIndex < pageRangeEnd || (frameIndex == pageRangeEnd && pPage == (ma_paged_audio_buffer_page*)c89atomic_load_ptr(ma_paged_audio_buffer_data_get_tail(pPagedAudioBuffer->pData)))) { /* A small edge case - allow seeking to the very end of the buffer. */ /* We found the page. */ pPagedAudioBuffer->pCurrent = pPage; pPagedAudioBuffer->absoluteCursor = frameIndex; @@ -59515,36 +58886,85 @@ MA_API ma_result ma_vfs_info(ma_vfs* pVFS, ma_vfs_file file, ma_file_info* pInfo } -#if !defined(MA_USE_WIN32_FILEIO) && (defined(MA_WIN32) && defined(MA_WIN32_DESKTOP) && !defined(MA_NO_WIN32_FILEIO) && !defined(MA_POSIX)) - #define MA_USE_WIN32_FILEIO -#endif +static ma_result ma_vfs_open_and_read_file_ex(ma_vfs* pVFS, const char* pFilePath, const wchar_t* pFilePathW, void** ppData, size_t* pSize, const ma_allocation_callbacks* pAllocationCallbacks) +{ + ma_result result; + ma_vfs_file file; + ma_file_info info; + void* pData; + size_t bytesRead; -#if defined(MA_USE_WIN32_FILEIO) -/* -We need to dynamically load SetFilePointer or SetFilePointerEx because older versions of Windows do -not have the Ex version. We therefore need to do some dynamic branching depending on what's available. + if (ppData != NULL) { + *ppData = NULL; + } + if (pSize != NULL) { + *pSize = 0; + } -We load these when we load our first file from the default VFS. It's left open for the life of the -program and is left to the OS to uninitialize when the program terminates. -*/ -typedef DWORD (__stdcall * ma_SetFilePointer_proc)(HANDLE hFile, LONG lDistanceToMove, LONG* lpDistanceToMoveHigh, DWORD dwMoveMethod); -typedef BOOL (__stdcall * ma_SetFilePointerEx_proc)(HANDLE hFile, LARGE_INTEGER liDistanceToMove, LARGE_INTEGER* lpNewFilePointer, DWORD dwMoveMethod); + if (ppData == NULL) { + return MA_INVALID_ARGS; + } + + if (pFilePath != NULL) { + result = ma_vfs_open(pVFS, pFilePath, MA_OPEN_MODE_READ, &file); + } else { + result = ma_vfs_open_w(pVFS, pFilePathW, MA_OPEN_MODE_READ, &file); + } + if (result != MA_SUCCESS) { + return result; + } -static ma_handle hKernel32DLL = NULL; -static ma_SetFilePointer_proc ma_SetFilePointer = NULL; -static ma_SetFilePointerEx_proc ma_SetFilePointerEx = NULL; + result = ma_vfs_info(pVFS, file, &info); + if (result != MA_SUCCESS) { + ma_vfs_close(pVFS, file); + return result; + } -static void ma_win32_fileio_init(void) -{ - if (hKernel32DLL == NULL) { - hKernel32DLL = ma_dlopen(NULL, "kernel32.dll"); - if (hKernel32DLL != NULL) { - ma_SetFilePointer = (ma_SetFilePointer_proc) ma_dlsym(NULL, hKernel32DLL, "SetFilePointer"); - ma_SetFilePointerEx = (ma_SetFilePointerEx_proc)ma_dlsym(NULL, hKernel32DLL, "SetFilePointerEx"); - } + if (info.sizeInBytes > MA_SIZE_MAX) { + ma_vfs_close(pVFS, file); + return MA_TOO_BIG; + } + + pData = ma_malloc((size_t)info.sizeInBytes, pAllocationCallbacks); /* Safe cast. */ + if (pData == NULL) { + ma_vfs_close(pVFS, file); + return result; } + + result = ma_vfs_read(pVFS, file, pData, (size_t)info.sizeInBytes, &bytesRead); /* Safe cast. */ + ma_vfs_close(pVFS, file); + + if (result != MA_SUCCESS) { + ma_free(pData, pAllocationCallbacks); + return result; + } + + if (pSize != NULL) { + *pSize = bytesRead; + } + + MA_ASSERT(ppData != NULL); + *ppData = pData; + + return MA_SUCCESS; +} + +MA_API ma_result ma_vfs_open_and_read_file(ma_vfs* pVFS, const char* pFilePath, void** ppData, size_t* pSize, const ma_allocation_callbacks* pAllocationCallbacks) +{ + return ma_vfs_open_and_read_file_ex(pVFS, pFilePath, NULL, ppData, pSize, pAllocationCallbacks); +} + +MA_API ma_result ma_vfs_open_and_read_file_w(ma_vfs* pVFS, const wchar_t* pFilePath, void** ppData, size_t* pSize, const ma_allocation_callbacks* pAllocationCallbacks) +{ + return ma_vfs_open_and_read_file_ex(pVFS, NULL, pFilePath, ppData, pSize, pAllocationCallbacks); } + +#if !defined(MA_USE_WIN32_FILEIO) && (defined(MA_WIN32) && defined(MA_WIN32_DESKTOP) && !defined(MA_NO_WIN32_FILEIO) && !defined(MA_POSIX)) + #define MA_USE_WIN32_FILEIO +#endif + +#if defined(MA_USE_WIN32_FILEIO) static void ma_default_vfs__get_open_settings_win32(ma_uint32 openMode, DWORD* pDesiredAccess, DWORD* pShareMode, DWORD* pCreationDisposition) { *pDesiredAccess = 0; @@ -59576,9 +58996,6 @@ static ma_result ma_default_vfs_open__win32(ma_vfs* pVFS, const char* pFilePath, (void)pVFS; - /* Load some Win32 symbols dynamically so we can dynamically check for the existence of SetFilePointerEx. */ - ma_win32_fileio_init(); - ma_default_vfs__get_open_settings_win32(openMode, &dwDesiredAccess, &dwShareMode, &dwCreationDisposition); hFile = CreateFileA(pFilePath, dwDesiredAccess, dwShareMode, NULL, dwCreationDisposition, FILE_ATTRIBUTE_NORMAL, NULL); @@ -59599,9 +59016,6 @@ static ma_result ma_default_vfs_open_w__win32(ma_vfs* pVFS, const wchar_t* pFile (void)pVFS; - /* Load some Win32 symbols dynamically so we can dynamically check for the existence of SetFilePointerEx. */ - ma_win32_fileio_init(); - ma_default_vfs__get_open_settings_win32(openMode, &dwDesiredAccess, &dwShareMode, &dwCreationDisposition); hFile = CreateFileW(pFilePath, dwDesiredAccess, dwShareMode, NULL, dwCreationDisposition, FILE_ATTRIBUTE_NORMAL, NULL); @@ -59727,19 +59141,16 @@ static ma_result ma_default_vfs_seek__win32(ma_vfs* pVFS, ma_vfs_file file, ma_i dwMoveMethod = FILE_BEGIN; } - if (ma_SetFilePointerEx != NULL) { - result = ma_SetFilePointerEx((HANDLE)file, liDistanceToMove, NULL, dwMoveMethod); - } else if (ma_SetFilePointer != NULL) { - /* No SetFilePointerEx() so restrict to 31 bits. */ - if (offset > 0x7FFFFFFF) { - return MA_OUT_OF_RANGE; - } - - result = ma_SetFilePointer((HANDLE)file, (LONG)liDistanceToMove.QuadPart, NULL, dwMoveMethod); - } else { - return MA_NOT_IMPLEMENTED; +#if (defined(_MSC_VER) && _MSC_VER <= 1200) || defined(__DMC__) + /* No SetFilePointerEx() so restrict to 31 bits. */ + if (origin > 0x7FFFFFFF) { + return MA_OUT_OF_RANGE; } + result = SetFilePointer((HANDLE)file, (LONG)liDistanceToMove.QuadPart, NULL, dwMoveMethod); +#else + result = SetFilePointerEx((HANDLE)file, liDistanceToMove, NULL, dwMoveMethod); +#endif if (result == 0) { return ma_result_from_GetLastError(GetLastError()); } @@ -59752,22 +59163,20 @@ static ma_result ma_default_vfs_tell__win32(ma_vfs* pVFS, ma_vfs_file file, ma_i LARGE_INTEGER liZero; LARGE_INTEGER liTell; BOOL result; +#if (defined(_MSC_VER) && _MSC_VER <= 1200) || defined(__DMC__) + LONG tell; +#endif (void)pVFS; liZero.QuadPart = 0; - if (ma_SetFilePointerEx != NULL) { - result = ma_SetFilePointerEx((HANDLE)file, liZero, &liTell, FILE_CURRENT); - } else if (ma_SetFilePointer != NULL) { - LONG tell; - - result = ma_SetFilePointer((HANDLE)file, (LONG)liZero.QuadPart, &tell, FILE_CURRENT); - liTell.QuadPart = tell; - } else { - return MA_NOT_IMPLEMENTED; - } - +#if (defined(_MSC_VER) && _MSC_VER <= 1200) || defined(__DMC__) + result = SetFilePointer((HANDLE)file, (LONG)liZero.QuadPart, &tell, FILE_CURRENT); + liTell.QuadPart = tell; +#else + result = SetFilePointerEx((HANDLE)file, liZero, &liTell, FILE_CURRENT); +#endif if (result == 0) { return ma_result_from_GetLastError(GetLastError()); } @@ -59941,7 +59350,7 @@ static ma_result ma_default_vfs_seek__stdio(ma_vfs* pVFS, ma_vfs_file file, ma_i result = _fseeki64((FILE*)file, offset, whence); #else /* No _fseeki64() so restrict to 31 bits. */ - if (offset > 0x7FFFFFFF) { + if (origin > 0x7FFFFFFF) { return MA_OUT_OF_RANGE; } @@ -60243,81 +59652,6 @@ MA_API ma_result ma_vfs_or_default_info(ma_vfs* pVFS, ma_vfs_file file, ma_file_ -static ma_result ma_vfs_open_and_read_file_ex(ma_vfs* pVFS, const char* pFilePath, const wchar_t* pFilePathW, void** ppData, size_t* pSize, const ma_allocation_callbacks* pAllocationCallbacks) -{ - ma_result result; - ma_vfs_file file; - ma_file_info info; - void* pData; - size_t bytesRead; - - if (ppData != NULL) { - *ppData = NULL; - } - if (pSize != NULL) { - *pSize = 0; - } - - if (ppData == NULL) { - return MA_INVALID_ARGS; - } - - if (pFilePath != NULL) { - result = ma_vfs_or_default_open(pVFS, pFilePath, MA_OPEN_MODE_READ, &file); - } else { - result = ma_vfs_or_default_open_w(pVFS, pFilePathW, MA_OPEN_MODE_READ, &file); - } - if (result != MA_SUCCESS) { - return result; - } - - result = ma_vfs_or_default_info(pVFS, file, &info); - if (result != MA_SUCCESS) { - ma_vfs_or_default_close(pVFS, file); - return result; - } - - if (info.sizeInBytes > MA_SIZE_MAX) { - ma_vfs_or_default_close(pVFS, file); - return MA_TOO_BIG; - } - - pData = ma_malloc((size_t)info.sizeInBytes, pAllocationCallbacks); /* Safe cast. */ - if (pData == NULL) { - ma_vfs_or_default_close(pVFS, file); - return result; - } - - result = ma_vfs_or_default_read(pVFS, file, pData, (size_t)info.sizeInBytes, &bytesRead); /* Safe cast. */ - ma_vfs_or_default_close(pVFS, file); - - if (result != MA_SUCCESS) { - ma_free(pData, pAllocationCallbacks); - return result; - } - - if (pSize != NULL) { - *pSize = bytesRead; - } - - MA_ASSERT(ppData != NULL); - *ppData = pData; - - return MA_SUCCESS; -} - -MA_API ma_result ma_vfs_open_and_read_file(ma_vfs* pVFS, const char* pFilePath, void** ppData, size_t* pSize, const ma_allocation_callbacks* pAllocationCallbacks) -{ - return ma_vfs_open_and_read_file_ex(pVFS, pFilePath, NULL, ppData, pSize, pAllocationCallbacks); -} - -MA_API ma_result ma_vfs_open_and_read_file_w(ma_vfs* pVFS, const wchar_t* pFilePath, void** ppData, size_t* pSize, const ma_allocation_callbacks* pAllocationCallbacks) -{ - return ma_vfs_open_and_read_file_ex(pVFS, NULL, pFilePath, ppData, pSize, pAllocationCallbacks); -} - - - /************************************************************************************************************************************************************** Decoding and Encoding Headers. These are auto-generated from a tool. @@ -60325,76 +59659,195 @@ Decoding and Encoding Headers. These are auto-generated from a tool. **************************************************************************************************************************************************************/ #if !defined(MA_NO_WAV) && (!defined(MA_NO_DECODING) || !defined(MA_NO_ENCODING)) /* dr_wav_h begin */ -#ifndef ma_dr_wav_h -#define ma_dr_wav_h +#ifndef dr_wav_h +#define dr_wav_h #ifdef __cplusplus extern "C" { #endif -#define MA_DR_WAV_STRINGIFY(x) #x -#define MA_DR_WAV_XSTRINGIFY(x) MA_DR_WAV_STRINGIFY(x) -#define MA_DR_WAV_VERSION_MAJOR 0 -#define MA_DR_WAV_VERSION_MINOR 13 -#define MA_DR_WAV_VERSION_REVISION 18 -#define MA_DR_WAV_VERSION_STRING MA_DR_WAV_XSTRINGIFY(MA_DR_WAV_VERSION_MAJOR) "." MA_DR_WAV_XSTRINGIFY(MA_DR_WAV_VERSION_MINOR) "." MA_DR_WAV_XSTRINGIFY(MA_DR_WAV_VERSION_REVISION) +#define DRWAV_STRINGIFY(x) #x +#define DRWAV_XSTRINGIFY(x) DRWAV_STRINGIFY(x) +#define DRWAV_VERSION_MAJOR 0 +#define DRWAV_VERSION_MINOR 13 +#define DRWAV_VERSION_REVISION 8 +#define DRWAV_VERSION_STRING DRWAV_XSTRINGIFY(DRWAV_VERSION_MAJOR) "." DRWAV_XSTRINGIFY(DRWAV_VERSION_MINOR) "." DRWAV_XSTRINGIFY(DRWAV_VERSION_REVISION) #include -#define MA_DR_WAVE_FORMAT_PCM 0x1 -#define MA_DR_WAVE_FORMAT_ADPCM 0x2 -#define MA_DR_WAVE_FORMAT_IEEE_FLOAT 0x3 -#define MA_DR_WAVE_FORMAT_ALAW 0x6 -#define MA_DR_WAVE_FORMAT_MULAW 0x7 -#define MA_DR_WAVE_FORMAT_DVI_ADPCM 0x11 -#define MA_DR_WAVE_FORMAT_EXTENSIBLE 0xFFFE -#define MA_DR_WAV_SEQUENTIAL 0x00000001 -#define MA_DR_WAV_WITH_METADATA 0x00000002 -MA_API void ma_dr_wav_version(ma_uint32* pMajor, ma_uint32* pMinor, ma_uint32* pRevision); -MA_API const char* ma_dr_wav_version_string(void); +typedef signed char drwav_int8; +typedef unsigned char drwav_uint8; +typedef signed short drwav_int16; +typedef unsigned short drwav_uint16; +typedef signed int drwav_int32; +typedef unsigned int drwav_uint32; +#if defined(_MSC_VER) && !defined(__clang__) + typedef signed __int64 drwav_int64; + typedef unsigned __int64 drwav_uint64; +#else + #if defined(__clang__) || (defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6))) + #pragma GCC diagnostic push + #pragma GCC diagnostic ignored "-Wlong-long" + #if defined(__clang__) + #pragma GCC diagnostic ignored "-Wc++11-long-long" + #endif + #endif + typedef signed long long drwav_int64; + typedef unsigned long long drwav_uint64; + #if defined(__clang__) || (defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6))) + #pragma GCC diagnostic pop + #endif +#endif +#if defined(__LP64__) || defined(_WIN64) || (defined(__x86_64__) && !defined(__ILP32__)) || defined(_M_X64) || defined(__ia64) || defined (_M_IA64) || defined(__aarch64__) || defined(_M_ARM64) || defined(__powerpc64__) + typedef drwav_uint64 drwav_uintptr; +#else + typedef drwav_uint32 drwav_uintptr; +#endif +typedef drwav_uint8 drwav_bool8; +typedef drwav_uint32 drwav_bool32; +#define DRWAV_TRUE 1 +#define DRWAV_FALSE 0 +#if !defined(DRWAV_API) + #if defined(DRWAV_DLL) + #if defined(_WIN32) + #define DRWAV_DLL_IMPORT __declspec(dllimport) + #define DRWAV_DLL_EXPORT __declspec(dllexport) + #define DRWAV_DLL_PRIVATE static + #else + #if defined(__GNUC__) && __GNUC__ >= 4 + #define DRWAV_DLL_IMPORT __attribute__((visibility("default"))) + #define DRWAV_DLL_EXPORT __attribute__((visibility("default"))) + #define DRWAV_DLL_PRIVATE __attribute__((visibility("hidden"))) + #else + #define DRWAV_DLL_IMPORT + #define DRWAV_DLL_EXPORT + #define DRWAV_DLL_PRIVATE static + #endif + #endif + #if defined(DR_WAV_IMPLEMENTATION) || defined(DRWAV_IMPLEMENTATION) + #define DRWAV_API DRWAV_DLL_EXPORT + #else + #define DRWAV_API DRWAV_DLL_IMPORT + #endif + #define DRWAV_PRIVATE DRWAV_DLL_PRIVATE + #else + #define DRWAV_API extern + #define DRWAV_PRIVATE static + #endif +#endif +typedef drwav_int32 drwav_result; +#define DRWAV_SUCCESS 0 +#define DRWAV_ERROR -1 +#define DRWAV_INVALID_ARGS -2 +#define DRWAV_INVALID_OPERATION -3 +#define DRWAV_OUT_OF_MEMORY -4 +#define DRWAV_OUT_OF_RANGE -5 +#define DRWAV_ACCESS_DENIED -6 +#define DRWAV_DOES_NOT_EXIST -7 +#define DRWAV_ALREADY_EXISTS -8 +#define DRWAV_TOO_MANY_OPEN_FILES -9 +#define DRWAV_INVALID_FILE -10 +#define DRWAV_TOO_BIG -11 +#define DRWAV_PATH_TOO_LONG -12 +#define DRWAV_NAME_TOO_LONG -13 +#define DRWAV_NOT_DIRECTORY -14 +#define DRWAV_IS_DIRECTORY -15 +#define DRWAV_DIRECTORY_NOT_EMPTY -16 +#define DRWAV_END_OF_FILE -17 +#define DRWAV_NO_SPACE -18 +#define DRWAV_BUSY -19 +#define DRWAV_IO_ERROR -20 +#define DRWAV_INTERRUPT -21 +#define DRWAV_UNAVAILABLE -22 +#define DRWAV_ALREADY_IN_USE -23 +#define DRWAV_BAD_ADDRESS -24 +#define DRWAV_BAD_SEEK -25 +#define DRWAV_BAD_PIPE -26 +#define DRWAV_DEADLOCK -27 +#define DRWAV_TOO_MANY_LINKS -28 +#define DRWAV_NOT_IMPLEMENTED -29 +#define DRWAV_NO_MESSAGE -30 +#define DRWAV_BAD_MESSAGE -31 +#define DRWAV_NO_DATA_AVAILABLE -32 +#define DRWAV_INVALID_DATA -33 +#define DRWAV_TIMEOUT -34 +#define DRWAV_NO_NETWORK -35 +#define DRWAV_NOT_UNIQUE -36 +#define DRWAV_NOT_SOCKET -37 +#define DRWAV_NO_ADDRESS -38 +#define DRWAV_BAD_PROTOCOL -39 +#define DRWAV_PROTOCOL_UNAVAILABLE -40 +#define DRWAV_PROTOCOL_NOT_SUPPORTED -41 +#define DRWAV_PROTOCOL_FAMILY_NOT_SUPPORTED -42 +#define DRWAV_ADDRESS_FAMILY_NOT_SUPPORTED -43 +#define DRWAV_SOCKET_NOT_SUPPORTED -44 +#define DRWAV_CONNECTION_RESET -45 +#define DRWAV_ALREADY_CONNECTED -46 +#define DRWAV_NOT_CONNECTED -47 +#define DRWAV_CONNECTION_REFUSED -48 +#define DRWAV_NO_HOST -49 +#define DRWAV_IN_PROGRESS -50 +#define DRWAV_CANCELLED -51 +#define DRWAV_MEMORY_ALREADY_MAPPED -52 +#define DRWAV_AT_END -53 +#define DR_WAVE_FORMAT_PCM 0x1 +#define DR_WAVE_FORMAT_ADPCM 0x2 +#define DR_WAVE_FORMAT_IEEE_FLOAT 0x3 +#define DR_WAVE_FORMAT_ALAW 0x6 +#define DR_WAVE_FORMAT_MULAW 0x7 +#define DR_WAVE_FORMAT_DVI_ADPCM 0x11 +#define DR_WAVE_FORMAT_EXTENSIBLE 0xFFFE +#define DRWAV_SEQUENTIAL 0x00000001 +DRWAV_API void drwav_version(drwav_uint32* pMajor, drwav_uint32* pMinor, drwav_uint32* pRevision); +DRWAV_API const char* drwav_version_string(void); typedef enum { - ma_dr_wav_seek_origin_start, - ma_dr_wav_seek_origin_current -} ma_dr_wav_seek_origin; + drwav_seek_origin_start, + drwav_seek_origin_current +} drwav_seek_origin; typedef enum { - ma_dr_wav_container_riff, - ma_dr_wav_container_rifx, - ma_dr_wav_container_w64, - ma_dr_wav_container_rf64, - ma_dr_wav_container_aiff -} ma_dr_wav_container; + drwav_container_riff, + drwav_container_w64, + drwav_container_rf64 +} drwav_container; typedef struct { union { - ma_uint8 fourcc[4]; - ma_uint8 guid[16]; + drwav_uint8 fourcc[4]; + drwav_uint8 guid[16]; } id; - ma_uint64 sizeInBytes; + drwav_uint64 sizeInBytes; unsigned int paddingSize; -} ma_dr_wav_chunk_header; +} drwav_chunk_header; typedef struct { - ma_uint16 formatTag; - ma_uint16 channels; - ma_uint32 sampleRate; - ma_uint32 avgBytesPerSec; - ma_uint16 blockAlign; - ma_uint16 bitsPerSample; - ma_uint16 extendedSize; - ma_uint16 validBitsPerSample; - ma_uint32 channelMask; - ma_uint8 subFormat[16]; -} ma_dr_wav_fmt; -MA_API ma_uint16 ma_dr_wav_fmt_get_format(const ma_dr_wav_fmt* pFMT); -typedef size_t (* ma_dr_wav_read_proc)(void* pUserData, void* pBufferOut, size_t bytesToRead); -typedef size_t (* ma_dr_wav_write_proc)(void* pUserData, const void* pData, size_t bytesToWrite); -typedef ma_bool32 (* ma_dr_wav_seek_proc)(void* pUserData, int offset, ma_dr_wav_seek_origin origin); -typedef ma_uint64 (* ma_dr_wav_chunk_proc)(void* pChunkUserData, ma_dr_wav_read_proc onRead, ma_dr_wav_seek_proc onSeek, void* pReadSeekUserData, const ma_dr_wav_chunk_header* pChunkHeader, ma_dr_wav_container container, const ma_dr_wav_fmt* pFMT); + drwav_uint16 formatTag; + drwav_uint16 channels; + drwav_uint32 sampleRate; + drwav_uint32 avgBytesPerSec; + drwav_uint16 blockAlign; + drwav_uint16 bitsPerSample; + drwav_uint16 extendedSize; + drwav_uint16 validBitsPerSample; + drwav_uint32 channelMask; + drwav_uint8 subFormat[16]; +} drwav_fmt; +DRWAV_API drwav_uint16 drwav_fmt_get_format(const drwav_fmt* pFMT); +typedef size_t (* drwav_read_proc)(void* pUserData, void* pBufferOut, size_t bytesToRead); +typedef size_t (* drwav_write_proc)(void* pUserData, const void* pData, size_t bytesToWrite); +typedef drwav_bool32 (* drwav_seek_proc)(void* pUserData, int offset, drwav_seek_origin origin); +typedef drwav_uint64 (* drwav_chunk_proc)(void* pChunkUserData, drwav_read_proc onRead, drwav_seek_proc onSeek, void* pReadSeekUserData, const drwav_chunk_header* pChunkHeader, drwav_container container, const drwav_fmt* pFMT); +typedef struct +{ + void* pUserData; + void* (* onMalloc)(size_t sz, void* pUserData); + void* (* onRealloc)(void* p, size_t sz, void* pUserData); + void (* onFree)(void* p, void* pUserData); +} drwav_allocation_callbacks; typedef struct { - const ma_uint8* data; + const drwav_uint8* data; size_t dataSize; size_t currentReadPos; -} ma_dr_wav__memory_stream; +} drwav__memory_stream; typedef struct { void** ppData; @@ -60402,129 +59855,129 @@ typedef struct size_t dataSize; size_t dataCapacity; size_t currentWritePos; -} ma_dr_wav__memory_stream_write; +} drwav__memory_stream_write; typedef struct { - ma_dr_wav_container container; - ma_uint32 format; - ma_uint32 channels; - ma_uint32 sampleRate; - ma_uint32 bitsPerSample; -} ma_dr_wav_data_format; + drwav_container container; + drwav_uint32 format; + drwav_uint32 channels; + drwav_uint32 sampleRate; + drwav_uint32 bitsPerSample; +} drwav_data_format; typedef enum { - ma_dr_wav_metadata_type_none = 0, - ma_dr_wav_metadata_type_unknown = 1 << 0, - ma_dr_wav_metadata_type_smpl = 1 << 1, - ma_dr_wav_metadata_type_inst = 1 << 2, - ma_dr_wav_metadata_type_cue = 1 << 3, - ma_dr_wav_metadata_type_acid = 1 << 4, - ma_dr_wav_metadata_type_bext = 1 << 5, - ma_dr_wav_metadata_type_list_label = 1 << 6, - ma_dr_wav_metadata_type_list_note = 1 << 7, - ma_dr_wav_metadata_type_list_labelled_cue_region = 1 << 8, - ma_dr_wav_metadata_type_list_info_software = 1 << 9, - ma_dr_wav_metadata_type_list_info_copyright = 1 << 10, - ma_dr_wav_metadata_type_list_info_title = 1 << 11, - ma_dr_wav_metadata_type_list_info_artist = 1 << 12, - ma_dr_wav_metadata_type_list_info_comment = 1 << 13, - ma_dr_wav_metadata_type_list_info_date = 1 << 14, - ma_dr_wav_metadata_type_list_info_genre = 1 << 15, - ma_dr_wav_metadata_type_list_info_album = 1 << 16, - ma_dr_wav_metadata_type_list_info_tracknumber = 1 << 17, - ma_dr_wav_metadata_type_list_all_info_strings = ma_dr_wav_metadata_type_list_info_software - | ma_dr_wav_metadata_type_list_info_copyright - | ma_dr_wav_metadata_type_list_info_title - | ma_dr_wav_metadata_type_list_info_artist - | ma_dr_wav_metadata_type_list_info_comment - | ma_dr_wav_metadata_type_list_info_date - | ma_dr_wav_metadata_type_list_info_genre - | ma_dr_wav_metadata_type_list_info_album - | ma_dr_wav_metadata_type_list_info_tracknumber, - ma_dr_wav_metadata_type_list_all_adtl = ma_dr_wav_metadata_type_list_label - | ma_dr_wav_metadata_type_list_note - | ma_dr_wav_metadata_type_list_labelled_cue_region, - ma_dr_wav_metadata_type_all = -2, - ma_dr_wav_metadata_type_all_including_unknown = -1 -} ma_dr_wav_metadata_type; + drwav_metadata_type_none = 0, + drwav_metadata_type_unknown = 1 << 0, + drwav_metadata_type_smpl = 1 << 1, + drwav_metadata_type_inst = 1 << 2, + drwav_metadata_type_cue = 1 << 3, + drwav_metadata_type_acid = 1 << 4, + drwav_metadata_type_bext = 1 << 5, + drwav_metadata_type_list_label = 1 << 6, + drwav_metadata_type_list_note = 1 << 7, + drwav_metadata_type_list_labelled_cue_region = 1 << 8, + drwav_metadata_type_list_info_software = 1 << 9, + drwav_metadata_type_list_info_copyright = 1 << 10, + drwav_metadata_type_list_info_title = 1 << 11, + drwav_metadata_type_list_info_artist = 1 << 12, + drwav_metadata_type_list_info_comment = 1 << 13, + drwav_metadata_type_list_info_date = 1 << 14, + drwav_metadata_type_list_info_genre = 1 << 15, + drwav_metadata_type_list_info_album = 1 << 16, + drwav_metadata_type_list_info_tracknumber = 1 << 17, + drwav_metadata_type_list_all_info_strings = drwav_metadata_type_list_info_software + | drwav_metadata_type_list_info_copyright + | drwav_metadata_type_list_info_title + | drwav_metadata_type_list_info_artist + | drwav_metadata_type_list_info_comment + | drwav_metadata_type_list_info_date + | drwav_metadata_type_list_info_genre + | drwav_metadata_type_list_info_album + | drwav_metadata_type_list_info_tracknumber, + drwav_metadata_type_list_all_adtl = drwav_metadata_type_list_label + | drwav_metadata_type_list_note + | drwav_metadata_type_list_labelled_cue_region, + drwav_metadata_type_all = -2, + drwav_metadata_type_all_including_unknown = -1 +} drwav_metadata_type; typedef enum { - ma_dr_wav_smpl_loop_type_forward = 0, - ma_dr_wav_smpl_loop_type_pingpong = 1, - ma_dr_wav_smpl_loop_type_backward = 2 -} ma_dr_wav_smpl_loop_type; + drwav_smpl_loop_type_forward = 0, + drwav_smpl_loop_type_pingpong = 1, + drwav_smpl_loop_type_backward = 2 +} drwav_smpl_loop_type; typedef struct { - ma_uint32 cuePointId; - ma_uint32 type; - ma_uint32 firstSampleByteOffset; - ma_uint32 lastSampleByteOffset; - ma_uint32 sampleFraction; - ma_uint32 playCount; -} ma_dr_wav_smpl_loop; + drwav_uint32 cuePointId; + drwav_uint32 type; + drwav_uint32 firstSampleByteOffset; + drwav_uint32 lastSampleByteOffset; + drwav_uint32 sampleFraction; + drwav_uint32 playCount; +} drwav_smpl_loop; typedef struct { - ma_uint32 manufacturerId; - ma_uint32 productId; - ma_uint32 samplePeriodNanoseconds; - ma_uint32 midiUnityNote; - ma_uint32 midiPitchFraction; - ma_uint32 smpteFormat; - ma_uint32 smpteOffset; - ma_uint32 sampleLoopCount; - ma_uint32 samplerSpecificDataSizeInBytes; - ma_dr_wav_smpl_loop* pLoops; - ma_uint8* pSamplerSpecificData; -} ma_dr_wav_smpl; + drwav_uint32 manufacturerId; + drwav_uint32 productId; + drwav_uint32 samplePeriodNanoseconds; + drwav_uint32 midiUnityNote; + drwav_uint32 midiPitchFraction; + drwav_uint32 smpteFormat; + drwav_uint32 smpteOffset; + drwav_uint32 sampleLoopCount; + drwav_uint32 samplerSpecificDataSizeInBytes; + drwav_smpl_loop* pLoops; + drwav_uint8* pSamplerSpecificData; +} drwav_smpl; typedef struct { - ma_int8 midiUnityNote; - ma_int8 fineTuneCents; - ma_int8 gainDecibels; - ma_int8 lowNote; - ma_int8 highNote; - ma_int8 lowVelocity; - ma_int8 highVelocity; -} ma_dr_wav_inst; + drwav_int8 midiUnityNote; + drwav_int8 fineTuneCents; + drwav_int8 gainDecibels; + drwav_int8 lowNote; + drwav_int8 highNote; + drwav_int8 lowVelocity; + drwav_int8 highVelocity; +} drwav_inst; typedef struct { - ma_uint32 id; - ma_uint32 playOrderPosition; - ma_uint8 dataChunkId[4]; - ma_uint32 chunkStart; - ma_uint32 blockStart; - ma_uint32 sampleByteOffset; -} ma_dr_wav_cue_point; + drwav_uint32 id; + drwav_uint32 playOrderPosition; + drwav_uint8 dataChunkId[4]; + drwav_uint32 chunkStart; + drwav_uint32 blockStart; + drwav_uint32 sampleByteOffset; +} drwav_cue_point; typedef struct { - ma_uint32 cuePointCount; - ma_dr_wav_cue_point *pCuePoints; -} ma_dr_wav_cue; + drwav_uint32 cuePointCount; + drwav_cue_point *pCuePoints; +} drwav_cue; typedef enum { - ma_dr_wav_acid_flag_one_shot = 1, - ma_dr_wav_acid_flag_root_note_set = 2, - ma_dr_wav_acid_flag_stretch = 4, - ma_dr_wav_acid_flag_disk_based = 8, - ma_dr_wav_acid_flag_acidizer = 16 -} ma_dr_wav_acid_flag; + drwav_acid_flag_one_shot = 1, + drwav_acid_flag_root_note_set = 2, + drwav_acid_flag_stretch = 4, + drwav_acid_flag_disk_based = 8, + drwav_acid_flag_acidizer = 16 +} drwav_acid_flag; typedef struct { - ma_uint32 flags; - ma_uint16 midiUnityNote; - ma_uint16 reserved1; + drwav_uint32 flags; + drwav_uint16 midiUnityNote; + drwav_uint16 reserved1; float reserved2; - ma_uint32 numBeats; - ma_uint16 meterDenominator; - ma_uint16 meterNumerator; + drwav_uint32 numBeats; + drwav_uint16 meterDenominator; + drwav_uint16 meterNumerator; float tempo; -} ma_dr_wav_acid; +} drwav_acid; typedef struct { - ma_uint32 cuePointId; - ma_uint32 stringLength; + drwav_uint32 cuePointId; + drwav_uint32 stringLength; char* pString; -} ma_dr_wav_list_label_or_note; +} drwav_list_label_or_note; typedef struct { char* pDescription; @@ -60532,210 +59985,206 @@ typedef struct char* pOriginatorReference; char pOriginationDate[10]; char pOriginationTime[8]; - ma_uint64 timeReference; - ma_uint16 version; + drwav_uint64 timeReference; + drwav_uint16 version; char* pCodingHistory; - ma_uint32 codingHistorySize; - ma_uint8* pUMID; - ma_uint16 loudnessValue; - ma_uint16 loudnessRange; - ma_uint16 maxTruePeakLevel; - ma_uint16 maxMomentaryLoudness; - ma_uint16 maxShortTermLoudness; -} ma_dr_wav_bext; + drwav_uint32 codingHistorySize; + drwav_uint8* pUMID; + drwav_uint16 loudnessValue; + drwav_uint16 loudnessRange; + drwav_uint16 maxTruePeakLevel; + drwav_uint16 maxMomentaryLoudness; + drwav_uint16 maxShortTermLoudness; +} drwav_bext; typedef struct { - ma_uint32 stringLength; + drwav_uint32 stringLength; char* pString; -} ma_dr_wav_list_info_text; +} drwav_list_info_text; typedef struct { - ma_uint32 cuePointId; - ma_uint32 sampleLength; - ma_uint8 purposeId[4]; - ma_uint16 country; - ma_uint16 language; - ma_uint16 dialect; - ma_uint16 codePage; - ma_uint32 stringLength; + drwav_uint32 cuePointId; + drwav_uint32 sampleLength; + drwav_uint8 purposeId[4]; + drwav_uint16 country; + drwav_uint16 language; + drwav_uint16 dialect; + drwav_uint16 codePage; + drwav_uint32 stringLength; char* pString; -} ma_dr_wav_list_labelled_cue_region; +} drwav_list_labelled_cue_region; typedef enum { - ma_dr_wav_metadata_location_invalid, - ma_dr_wav_metadata_location_top_level, - ma_dr_wav_metadata_location_inside_info_list, - ma_dr_wav_metadata_location_inside_adtl_list -} ma_dr_wav_metadata_location; + drwav_metadata_location_invalid, + drwav_metadata_location_top_level, + drwav_metadata_location_inside_info_list, + drwav_metadata_location_inside_adtl_list +} drwav_metadata_location; typedef struct { - ma_uint8 id[4]; - ma_dr_wav_metadata_location chunkLocation; - ma_uint32 dataSizeInBytes; - ma_uint8* pData; -} ma_dr_wav_unknown_metadata; + drwav_uint8 id[4]; + drwav_metadata_location chunkLocation; + drwav_uint32 dataSizeInBytes; + drwav_uint8* pData; +} drwav_unknown_metadata; typedef struct { - ma_dr_wav_metadata_type type; + drwav_metadata_type type; union { - ma_dr_wav_cue cue; - ma_dr_wav_smpl smpl; - ma_dr_wav_acid acid; - ma_dr_wav_inst inst; - ma_dr_wav_bext bext; - ma_dr_wav_list_label_or_note labelOrNote; - ma_dr_wav_list_labelled_cue_region labelledCueRegion; - ma_dr_wav_list_info_text infoText; - ma_dr_wav_unknown_metadata unknown; + drwav_cue cue; + drwav_smpl smpl; + drwav_acid acid; + drwav_inst inst; + drwav_bext bext; + drwav_list_label_or_note labelOrNote; + drwav_list_labelled_cue_region labelledCueRegion; + drwav_list_info_text infoText; + drwav_unknown_metadata unknown; } data; -} ma_dr_wav_metadata; +} drwav_metadata; typedef struct { - ma_dr_wav_read_proc onRead; - ma_dr_wav_write_proc onWrite; - ma_dr_wav_seek_proc onSeek; + drwav_read_proc onRead; + drwav_write_proc onWrite; + drwav_seek_proc onSeek; void* pUserData; - ma_allocation_callbacks allocationCallbacks; - ma_dr_wav_container container; - ma_dr_wav_fmt fmt; - ma_uint32 sampleRate; - ma_uint16 channels; - ma_uint16 bitsPerSample; - ma_uint16 translatedFormatTag; - ma_uint64 totalPCMFrameCount; - ma_uint64 dataChunkDataSize; - ma_uint64 dataChunkDataPos; - ma_uint64 bytesRemaining; - ma_uint64 readCursorInPCMFrames; - ma_uint64 dataChunkDataSizeTargetWrite; - ma_bool32 isSequentialWrite; - ma_dr_wav_metadata* pMetadata; - ma_uint32 metadataCount; - ma_dr_wav__memory_stream memoryStream; - ma_dr_wav__memory_stream_write memoryStreamWrite; + drwav_allocation_callbacks allocationCallbacks; + drwav_container container; + drwav_fmt fmt; + drwav_uint32 sampleRate; + drwav_uint16 channels; + drwav_uint16 bitsPerSample; + drwav_uint16 translatedFormatTag; + drwav_uint64 totalPCMFrameCount; + drwav_uint64 dataChunkDataSize; + drwav_uint64 dataChunkDataPos; + drwav_uint64 bytesRemaining; + drwav_uint64 readCursorInPCMFrames; + drwav_uint64 dataChunkDataSizeTargetWrite; + drwav_bool32 isSequentialWrite; + drwav_metadata_type allowedMetadataTypes; + drwav_metadata* pMetadata; + drwav_uint32 metadataCount; + drwav__memory_stream memoryStream; + drwav__memory_stream_write memoryStreamWrite; struct { - ma_uint32 bytesRemainingInBlock; - ma_uint16 predictor[2]; - ma_int32 delta[2]; - ma_int32 cachedFrames[4]; - ma_uint32 cachedFrameCount; - ma_int32 prevFrames[2][2]; + drwav_uint32 bytesRemainingInBlock; + drwav_uint16 predictor[2]; + drwav_int32 delta[2]; + drwav_int32 cachedFrames[4]; + drwav_uint32 cachedFrameCount; + drwav_int32 prevFrames[2][2]; } msadpcm; struct { - ma_uint32 bytesRemainingInBlock; - ma_int32 predictor[2]; - ma_int32 stepIndex[2]; - ma_int32 cachedFrames[16]; - ma_uint32 cachedFrameCount; + drwav_uint32 bytesRemainingInBlock; + drwav_int32 predictor[2]; + drwav_int32 stepIndex[2]; + drwav_int32 cachedFrames[16]; + drwav_uint32 cachedFrameCount; } ima; - struct - { - ma_bool8 isLE; - ma_bool8 isUnsigned; - } aiff; -} ma_dr_wav; -MA_API ma_bool32 ma_dr_wav_init(ma_dr_wav* pWav, ma_dr_wav_read_proc onRead, ma_dr_wav_seek_proc onSeek, void* pUserData, const ma_allocation_callbacks* pAllocationCallbacks); -MA_API ma_bool32 ma_dr_wav_init_ex(ma_dr_wav* pWav, ma_dr_wav_read_proc onRead, ma_dr_wav_seek_proc onSeek, ma_dr_wav_chunk_proc onChunk, void* pReadSeekUserData, void* pChunkUserData, ma_uint32 flags, const ma_allocation_callbacks* pAllocationCallbacks); -MA_API ma_bool32 ma_dr_wav_init_with_metadata(ma_dr_wav* pWav, ma_dr_wav_read_proc onRead, ma_dr_wav_seek_proc onSeek, void* pUserData, ma_uint32 flags, const ma_allocation_callbacks* pAllocationCallbacks); -MA_API ma_bool32 ma_dr_wav_init_write(ma_dr_wav* pWav, const ma_dr_wav_data_format* pFormat, ma_dr_wav_write_proc onWrite, ma_dr_wav_seek_proc onSeek, void* pUserData, const ma_allocation_callbacks* pAllocationCallbacks); -MA_API ma_bool32 ma_dr_wav_init_write_sequential(ma_dr_wav* pWav, const ma_dr_wav_data_format* pFormat, ma_uint64 totalSampleCount, ma_dr_wav_write_proc onWrite, void* pUserData, const ma_allocation_callbacks* pAllocationCallbacks); -MA_API ma_bool32 ma_dr_wav_init_write_sequential_pcm_frames(ma_dr_wav* pWav, const ma_dr_wav_data_format* pFormat, ma_uint64 totalPCMFrameCount, ma_dr_wav_write_proc onWrite, void* pUserData, const ma_allocation_callbacks* pAllocationCallbacks); -MA_API ma_bool32 ma_dr_wav_init_write_with_metadata(ma_dr_wav* pWav, const ma_dr_wav_data_format* pFormat, ma_dr_wav_write_proc onWrite, ma_dr_wav_seek_proc onSeek, void* pUserData, const ma_allocation_callbacks* pAllocationCallbacks, ma_dr_wav_metadata* pMetadata, ma_uint32 metadataCount); -MA_API ma_uint64 ma_dr_wav_target_write_size_bytes(const ma_dr_wav_data_format* pFormat, ma_uint64 totalFrameCount, ma_dr_wav_metadata* pMetadata, ma_uint32 metadataCount); -MA_API ma_dr_wav_metadata* ma_dr_wav_take_ownership_of_metadata(ma_dr_wav* pWav); -MA_API ma_result ma_dr_wav_uninit(ma_dr_wav* pWav); -MA_API size_t ma_dr_wav_read_raw(ma_dr_wav* pWav, size_t bytesToRead, void* pBufferOut); -MA_API ma_uint64 ma_dr_wav_read_pcm_frames(ma_dr_wav* pWav, ma_uint64 framesToRead, void* pBufferOut); -MA_API ma_uint64 ma_dr_wav_read_pcm_frames_le(ma_dr_wav* pWav, ma_uint64 framesToRead, void* pBufferOut); -MA_API ma_uint64 ma_dr_wav_read_pcm_frames_be(ma_dr_wav* pWav, ma_uint64 framesToRead, void* pBufferOut); -MA_API ma_bool32 ma_dr_wav_seek_to_pcm_frame(ma_dr_wav* pWav, ma_uint64 targetFrameIndex); -MA_API ma_result ma_dr_wav_get_cursor_in_pcm_frames(ma_dr_wav* pWav, ma_uint64* pCursor); -MA_API ma_result ma_dr_wav_get_length_in_pcm_frames(ma_dr_wav* pWav, ma_uint64* pLength); -MA_API size_t ma_dr_wav_write_raw(ma_dr_wav* pWav, size_t bytesToWrite, const void* pData); -MA_API ma_uint64 ma_dr_wav_write_pcm_frames(ma_dr_wav* pWav, ma_uint64 framesToWrite, const void* pData); -MA_API ma_uint64 ma_dr_wav_write_pcm_frames_le(ma_dr_wav* pWav, ma_uint64 framesToWrite, const void* pData); -MA_API ma_uint64 ma_dr_wav_write_pcm_frames_be(ma_dr_wav* pWav, ma_uint64 framesToWrite, const void* pData); -#ifndef MA_DR_WAV_NO_CONVERSION_API -MA_API ma_uint64 ma_dr_wav_read_pcm_frames_s16(ma_dr_wav* pWav, ma_uint64 framesToRead, ma_int16* pBufferOut); -MA_API ma_uint64 ma_dr_wav_read_pcm_frames_s16le(ma_dr_wav* pWav, ma_uint64 framesToRead, ma_int16* pBufferOut); -MA_API ma_uint64 ma_dr_wav_read_pcm_frames_s16be(ma_dr_wav* pWav, ma_uint64 framesToRead, ma_int16* pBufferOut); -MA_API void ma_dr_wav_u8_to_s16(ma_int16* pOut, const ma_uint8* pIn, size_t sampleCount); -MA_API void ma_dr_wav_s24_to_s16(ma_int16* pOut, const ma_uint8* pIn, size_t sampleCount); -MA_API void ma_dr_wav_s32_to_s16(ma_int16* pOut, const ma_int32* pIn, size_t sampleCount); -MA_API void ma_dr_wav_f32_to_s16(ma_int16* pOut, const float* pIn, size_t sampleCount); -MA_API void ma_dr_wav_f64_to_s16(ma_int16* pOut, const double* pIn, size_t sampleCount); -MA_API void ma_dr_wav_alaw_to_s16(ma_int16* pOut, const ma_uint8* pIn, size_t sampleCount); -MA_API void ma_dr_wav_mulaw_to_s16(ma_int16* pOut, const ma_uint8* pIn, size_t sampleCount); -MA_API ma_uint64 ma_dr_wav_read_pcm_frames_f32(ma_dr_wav* pWav, ma_uint64 framesToRead, float* pBufferOut); -MA_API ma_uint64 ma_dr_wav_read_pcm_frames_f32le(ma_dr_wav* pWav, ma_uint64 framesToRead, float* pBufferOut); -MA_API ma_uint64 ma_dr_wav_read_pcm_frames_f32be(ma_dr_wav* pWav, ma_uint64 framesToRead, float* pBufferOut); -MA_API void ma_dr_wav_u8_to_f32(float* pOut, const ma_uint8* pIn, size_t sampleCount); -MA_API void ma_dr_wav_s16_to_f32(float* pOut, const ma_int16* pIn, size_t sampleCount); -MA_API void ma_dr_wav_s24_to_f32(float* pOut, const ma_uint8* pIn, size_t sampleCount); -MA_API void ma_dr_wav_s32_to_f32(float* pOut, const ma_int32* pIn, size_t sampleCount); -MA_API void ma_dr_wav_f64_to_f32(float* pOut, const double* pIn, size_t sampleCount); -MA_API void ma_dr_wav_alaw_to_f32(float* pOut, const ma_uint8* pIn, size_t sampleCount); -MA_API void ma_dr_wav_mulaw_to_f32(float* pOut, const ma_uint8* pIn, size_t sampleCount); -MA_API ma_uint64 ma_dr_wav_read_pcm_frames_s32(ma_dr_wav* pWav, ma_uint64 framesToRead, ma_int32* pBufferOut); -MA_API ma_uint64 ma_dr_wav_read_pcm_frames_s32le(ma_dr_wav* pWav, ma_uint64 framesToRead, ma_int32* pBufferOut); -MA_API ma_uint64 ma_dr_wav_read_pcm_frames_s32be(ma_dr_wav* pWav, ma_uint64 framesToRead, ma_int32* pBufferOut); -MA_API void ma_dr_wav_u8_to_s32(ma_int32* pOut, const ma_uint8* pIn, size_t sampleCount); -MA_API void ma_dr_wav_s16_to_s32(ma_int32* pOut, const ma_int16* pIn, size_t sampleCount); -MA_API void ma_dr_wav_s24_to_s32(ma_int32* pOut, const ma_uint8* pIn, size_t sampleCount); -MA_API void ma_dr_wav_f32_to_s32(ma_int32* pOut, const float* pIn, size_t sampleCount); -MA_API void ma_dr_wav_f64_to_s32(ma_int32* pOut, const double* pIn, size_t sampleCount); -MA_API void ma_dr_wav_alaw_to_s32(ma_int32* pOut, const ma_uint8* pIn, size_t sampleCount); -MA_API void ma_dr_wav_mulaw_to_s32(ma_int32* pOut, const ma_uint8* pIn, size_t sampleCount); -#endif -#ifndef MA_DR_WAV_NO_STDIO -MA_API ma_bool32 ma_dr_wav_init_file(ma_dr_wav* pWav, const char* filename, const ma_allocation_callbacks* pAllocationCallbacks); -MA_API ma_bool32 ma_dr_wav_init_file_ex(ma_dr_wav* pWav, const char* filename, ma_dr_wav_chunk_proc onChunk, void* pChunkUserData, ma_uint32 flags, const ma_allocation_callbacks* pAllocationCallbacks); -MA_API ma_bool32 ma_dr_wav_init_file_w(ma_dr_wav* pWav, const wchar_t* filename, const ma_allocation_callbacks* pAllocationCallbacks); -MA_API ma_bool32 ma_dr_wav_init_file_ex_w(ma_dr_wav* pWav, const wchar_t* filename, ma_dr_wav_chunk_proc onChunk, void* pChunkUserData, ma_uint32 flags, const ma_allocation_callbacks* pAllocationCallbacks); -MA_API ma_bool32 ma_dr_wav_init_file_with_metadata(ma_dr_wav* pWav, const char* filename, ma_uint32 flags, const ma_allocation_callbacks* pAllocationCallbacks); -MA_API ma_bool32 ma_dr_wav_init_file_with_metadata_w(ma_dr_wav* pWav, const wchar_t* filename, ma_uint32 flags, const ma_allocation_callbacks* pAllocationCallbacks); -MA_API ma_bool32 ma_dr_wav_init_file_write(ma_dr_wav* pWav, const char* filename, const ma_dr_wav_data_format* pFormat, const ma_allocation_callbacks* pAllocationCallbacks); -MA_API ma_bool32 ma_dr_wav_init_file_write_sequential(ma_dr_wav* pWav, const char* filename, const ma_dr_wav_data_format* pFormat, ma_uint64 totalSampleCount, const ma_allocation_callbacks* pAllocationCallbacks); -MA_API ma_bool32 ma_dr_wav_init_file_write_sequential_pcm_frames(ma_dr_wav* pWav, const char* filename, const ma_dr_wav_data_format* pFormat, ma_uint64 totalPCMFrameCount, const ma_allocation_callbacks* pAllocationCallbacks); -MA_API ma_bool32 ma_dr_wav_init_file_write_w(ma_dr_wav* pWav, const wchar_t* filename, const ma_dr_wav_data_format* pFormat, const ma_allocation_callbacks* pAllocationCallbacks); -MA_API ma_bool32 ma_dr_wav_init_file_write_sequential_w(ma_dr_wav* pWav, const wchar_t* filename, const ma_dr_wav_data_format* pFormat, ma_uint64 totalSampleCount, const ma_allocation_callbacks* pAllocationCallbacks); -MA_API ma_bool32 ma_dr_wav_init_file_write_sequential_pcm_frames_w(ma_dr_wav* pWav, const wchar_t* filename, const ma_dr_wav_data_format* pFormat, ma_uint64 totalPCMFrameCount, const ma_allocation_callbacks* pAllocationCallbacks); -#endif -MA_API ma_bool32 ma_dr_wav_init_memory(ma_dr_wav* pWav, const void* data, size_t dataSize, const ma_allocation_callbacks* pAllocationCallbacks); -MA_API ma_bool32 ma_dr_wav_init_memory_ex(ma_dr_wav* pWav, const void* data, size_t dataSize, ma_dr_wav_chunk_proc onChunk, void* pChunkUserData, ma_uint32 flags, const ma_allocation_callbacks* pAllocationCallbacks); -MA_API ma_bool32 ma_dr_wav_init_memory_with_metadata(ma_dr_wav* pWav, const void* data, size_t dataSize, ma_uint32 flags, const ma_allocation_callbacks* pAllocationCallbacks); -MA_API ma_bool32 ma_dr_wav_init_memory_write(ma_dr_wav* pWav, void** ppData, size_t* pDataSize, const ma_dr_wav_data_format* pFormat, const ma_allocation_callbacks* pAllocationCallbacks); -MA_API ma_bool32 ma_dr_wav_init_memory_write_sequential(ma_dr_wav* pWav, void** ppData, size_t* pDataSize, const ma_dr_wav_data_format* pFormat, ma_uint64 totalSampleCount, const ma_allocation_callbacks* pAllocationCallbacks); -MA_API ma_bool32 ma_dr_wav_init_memory_write_sequential_pcm_frames(ma_dr_wav* pWav, void** ppData, size_t* pDataSize, const ma_dr_wav_data_format* pFormat, ma_uint64 totalPCMFrameCount, const ma_allocation_callbacks* pAllocationCallbacks); -#ifndef MA_DR_WAV_NO_CONVERSION_API -MA_API ma_int16* ma_dr_wav_open_and_read_pcm_frames_s16(ma_dr_wav_read_proc onRead, ma_dr_wav_seek_proc onSeek, void* pUserData, unsigned int* channelsOut, unsigned int* sampleRateOut, ma_uint64* totalFrameCountOut, const ma_allocation_callbacks* pAllocationCallbacks); -MA_API float* ma_dr_wav_open_and_read_pcm_frames_f32(ma_dr_wav_read_proc onRead, ma_dr_wav_seek_proc onSeek, void* pUserData, unsigned int* channelsOut, unsigned int* sampleRateOut, ma_uint64* totalFrameCountOut, const ma_allocation_callbacks* pAllocationCallbacks); -MA_API ma_int32* ma_dr_wav_open_and_read_pcm_frames_s32(ma_dr_wav_read_proc onRead, ma_dr_wav_seek_proc onSeek, void* pUserData, unsigned int* channelsOut, unsigned int* sampleRateOut, ma_uint64* totalFrameCountOut, const ma_allocation_callbacks* pAllocationCallbacks); -#ifndef MA_DR_WAV_NO_STDIO -MA_API ma_int16* ma_dr_wav_open_file_and_read_pcm_frames_s16(const char* filename, unsigned int* channelsOut, unsigned int* sampleRateOut, ma_uint64* totalFrameCountOut, const ma_allocation_callbacks* pAllocationCallbacks); -MA_API float* ma_dr_wav_open_file_and_read_pcm_frames_f32(const char* filename, unsigned int* channelsOut, unsigned int* sampleRateOut, ma_uint64* totalFrameCountOut, const ma_allocation_callbacks* pAllocationCallbacks); -MA_API ma_int32* ma_dr_wav_open_file_and_read_pcm_frames_s32(const char* filename, unsigned int* channelsOut, unsigned int* sampleRateOut, ma_uint64* totalFrameCountOut, const ma_allocation_callbacks* pAllocationCallbacks); -MA_API ma_int16* ma_dr_wav_open_file_and_read_pcm_frames_s16_w(const wchar_t* filename, unsigned int* channelsOut, unsigned int* sampleRateOut, ma_uint64* totalFrameCountOut, const ma_allocation_callbacks* pAllocationCallbacks); -MA_API float* ma_dr_wav_open_file_and_read_pcm_frames_f32_w(const wchar_t* filename, unsigned int* channelsOut, unsigned int* sampleRateOut, ma_uint64* totalFrameCountOut, const ma_allocation_callbacks* pAllocationCallbacks); -MA_API ma_int32* ma_dr_wav_open_file_and_read_pcm_frames_s32_w(const wchar_t* filename, unsigned int* channelsOut, unsigned int* sampleRateOut, ma_uint64* totalFrameCountOut, const ma_allocation_callbacks* pAllocationCallbacks); -#endif -MA_API ma_int16* ma_dr_wav_open_memory_and_read_pcm_frames_s16(const void* data, size_t dataSize, unsigned int* channelsOut, unsigned int* sampleRateOut, ma_uint64* totalFrameCountOut, const ma_allocation_callbacks* pAllocationCallbacks); -MA_API float* ma_dr_wav_open_memory_and_read_pcm_frames_f32(const void* data, size_t dataSize, unsigned int* channelsOut, unsigned int* sampleRateOut, ma_uint64* totalFrameCountOut, const ma_allocation_callbacks* pAllocationCallbacks); -MA_API ma_int32* ma_dr_wav_open_memory_and_read_pcm_frames_s32(const void* data, size_t dataSize, unsigned int* channelsOut, unsigned int* sampleRateOut, ma_uint64* totalFrameCountOut, const ma_allocation_callbacks* pAllocationCallbacks); -#endif -MA_API void ma_dr_wav_free(void* p, const ma_allocation_callbacks* pAllocationCallbacks); -MA_API ma_uint16 ma_dr_wav_bytes_to_u16(const ma_uint8* data); -MA_API ma_int16 ma_dr_wav_bytes_to_s16(const ma_uint8* data); -MA_API ma_uint32 ma_dr_wav_bytes_to_u32(const ma_uint8* data); -MA_API ma_int32 ma_dr_wav_bytes_to_s32(const ma_uint8* data); -MA_API ma_uint64 ma_dr_wav_bytes_to_u64(const ma_uint8* data); -MA_API ma_int64 ma_dr_wav_bytes_to_s64(const ma_uint8* data); -MA_API float ma_dr_wav_bytes_to_f32(const ma_uint8* data); -MA_API ma_bool32 ma_dr_wav_guid_equal(const ma_uint8 a[16], const ma_uint8 b[16]); -MA_API ma_bool32 ma_dr_wav_fourcc_equal(const ma_uint8* a, const char* b); +} drwav; +DRWAV_API drwav_bool32 drwav_init(drwav* pWav, drwav_read_proc onRead, drwav_seek_proc onSeek, void* pUserData, const drwav_allocation_callbacks* pAllocationCallbacks); +DRWAV_API drwav_bool32 drwav_init_ex(drwav* pWav, drwav_read_proc onRead, drwav_seek_proc onSeek, drwav_chunk_proc onChunk, void* pReadSeekUserData, void* pChunkUserData, drwav_uint32 flags, const drwav_allocation_callbacks* pAllocationCallbacks); +DRWAV_API drwav_bool32 drwav_init_with_metadata(drwav* pWav, drwav_read_proc onRead, drwav_seek_proc onSeek, void* pUserData, drwav_uint32 flags, const drwav_allocation_callbacks* pAllocationCallbacks); +DRWAV_API drwav_bool32 drwav_init_write(drwav* pWav, const drwav_data_format* pFormat, drwav_write_proc onWrite, drwav_seek_proc onSeek, void* pUserData, const drwav_allocation_callbacks* pAllocationCallbacks); +DRWAV_API drwav_bool32 drwav_init_write_sequential(drwav* pWav, const drwav_data_format* pFormat, drwav_uint64 totalSampleCount, drwav_write_proc onWrite, void* pUserData, const drwav_allocation_callbacks* pAllocationCallbacks); +DRWAV_API drwav_bool32 drwav_init_write_sequential_pcm_frames(drwav* pWav, const drwav_data_format* pFormat, drwav_uint64 totalPCMFrameCount, drwav_write_proc onWrite, void* pUserData, const drwav_allocation_callbacks* pAllocationCallbacks); +DRWAV_API drwav_bool32 drwav_init_write_with_metadata(drwav* pWav, const drwav_data_format* pFormat, drwav_write_proc onWrite, drwav_seek_proc onSeek, void* pUserData, const drwav_allocation_callbacks* pAllocationCallbacks, drwav_metadata* pMetadata, drwav_uint32 metadataCount); +DRWAV_API drwav_uint64 drwav_target_write_size_bytes(const drwav_data_format* pFormat, drwav_uint64 totalFrameCount, drwav_metadata* pMetadata, drwav_uint32 metadataCount); +DRWAV_API drwav_metadata* drwav_take_ownership_of_metadata(drwav* pWav); +DRWAV_API drwav_result drwav_uninit(drwav* pWav); +DRWAV_API size_t drwav_read_raw(drwav* pWav, size_t bytesToRead, void* pBufferOut); +DRWAV_API drwav_uint64 drwav_read_pcm_frames(drwav* pWav, drwav_uint64 framesToRead, void* pBufferOut); +DRWAV_API drwav_uint64 drwav_read_pcm_frames_le(drwav* pWav, drwav_uint64 framesToRead, void* pBufferOut); +DRWAV_API drwav_uint64 drwav_read_pcm_frames_be(drwav* pWav, drwav_uint64 framesToRead, void* pBufferOut); +DRWAV_API drwav_bool32 drwav_seek_to_pcm_frame(drwav* pWav, drwav_uint64 targetFrameIndex); +DRWAV_API drwav_result drwav_get_cursor_in_pcm_frames(drwav* pWav, drwav_uint64* pCursor); +DRWAV_API drwav_result drwav_get_length_in_pcm_frames(drwav* pWav, drwav_uint64* pLength); +DRWAV_API size_t drwav_write_raw(drwav* pWav, size_t bytesToWrite, const void* pData); +DRWAV_API drwav_uint64 drwav_write_pcm_frames(drwav* pWav, drwav_uint64 framesToWrite, const void* pData); +DRWAV_API drwav_uint64 drwav_write_pcm_frames_le(drwav* pWav, drwav_uint64 framesToWrite, const void* pData); +DRWAV_API drwav_uint64 drwav_write_pcm_frames_be(drwav* pWav, drwav_uint64 framesToWrite, const void* pData); +#ifndef DR_WAV_NO_CONVERSION_API +DRWAV_API drwav_uint64 drwav_read_pcm_frames_s16(drwav* pWav, drwav_uint64 framesToRead, drwav_int16* pBufferOut); +DRWAV_API drwav_uint64 drwav_read_pcm_frames_s16le(drwav* pWav, drwav_uint64 framesToRead, drwav_int16* pBufferOut); +DRWAV_API drwav_uint64 drwav_read_pcm_frames_s16be(drwav* pWav, drwav_uint64 framesToRead, drwav_int16* pBufferOut); +DRWAV_API void drwav_u8_to_s16(drwav_int16* pOut, const drwav_uint8* pIn, size_t sampleCount); +DRWAV_API void drwav_s24_to_s16(drwav_int16* pOut, const drwav_uint8* pIn, size_t sampleCount); +DRWAV_API void drwav_s32_to_s16(drwav_int16* pOut, const drwav_int32* pIn, size_t sampleCount); +DRWAV_API void drwav_f32_to_s16(drwav_int16* pOut, const float* pIn, size_t sampleCount); +DRWAV_API void drwav_f64_to_s16(drwav_int16* pOut, const double* pIn, size_t sampleCount); +DRWAV_API void drwav_alaw_to_s16(drwav_int16* pOut, const drwav_uint8* pIn, size_t sampleCount); +DRWAV_API void drwav_mulaw_to_s16(drwav_int16* pOut, const drwav_uint8* pIn, size_t sampleCount); +DRWAV_API drwav_uint64 drwav_read_pcm_frames_f32(drwav* pWav, drwav_uint64 framesToRead, float* pBufferOut); +DRWAV_API drwav_uint64 drwav_read_pcm_frames_f32le(drwav* pWav, drwav_uint64 framesToRead, float* pBufferOut); +DRWAV_API drwav_uint64 drwav_read_pcm_frames_f32be(drwav* pWav, drwav_uint64 framesToRead, float* pBufferOut); +DRWAV_API void drwav_u8_to_f32(float* pOut, const drwav_uint8* pIn, size_t sampleCount); +DRWAV_API void drwav_s16_to_f32(float* pOut, const drwav_int16* pIn, size_t sampleCount); +DRWAV_API void drwav_s24_to_f32(float* pOut, const drwav_uint8* pIn, size_t sampleCount); +DRWAV_API void drwav_s32_to_f32(float* pOut, const drwav_int32* pIn, size_t sampleCount); +DRWAV_API void drwav_f64_to_f32(float* pOut, const double* pIn, size_t sampleCount); +DRWAV_API void drwav_alaw_to_f32(float* pOut, const drwav_uint8* pIn, size_t sampleCount); +DRWAV_API void drwav_mulaw_to_f32(float* pOut, const drwav_uint8* pIn, size_t sampleCount); +DRWAV_API drwav_uint64 drwav_read_pcm_frames_s32(drwav* pWav, drwav_uint64 framesToRead, drwav_int32* pBufferOut); +DRWAV_API drwav_uint64 drwav_read_pcm_frames_s32le(drwav* pWav, drwav_uint64 framesToRead, drwav_int32* pBufferOut); +DRWAV_API drwav_uint64 drwav_read_pcm_frames_s32be(drwav* pWav, drwav_uint64 framesToRead, drwav_int32* pBufferOut); +DRWAV_API void drwav_u8_to_s32(drwav_int32* pOut, const drwav_uint8* pIn, size_t sampleCount); +DRWAV_API void drwav_s16_to_s32(drwav_int32* pOut, const drwav_int16* pIn, size_t sampleCount); +DRWAV_API void drwav_s24_to_s32(drwav_int32* pOut, const drwav_uint8* pIn, size_t sampleCount); +DRWAV_API void drwav_f32_to_s32(drwav_int32* pOut, const float* pIn, size_t sampleCount); +DRWAV_API void drwav_f64_to_s32(drwav_int32* pOut, const double* pIn, size_t sampleCount); +DRWAV_API void drwav_alaw_to_s32(drwav_int32* pOut, const drwav_uint8* pIn, size_t sampleCount); +DRWAV_API void drwav_mulaw_to_s32(drwav_int32* pOut, const drwav_uint8* pIn, size_t sampleCount); +#endif +#ifndef DR_WAV_NO_STDIO +DRWAV_API drwav_bool32 drwav_init_file(drwav* pWav, const char* filename, const drwav_allocation_callbacks* pAllocationCallbacks); +DRWAV_API drwav_bool32 drwav_init_file_ex(drwav* pWav, const char* filename, drwav_chunk_proc onChunk, void* pChunkUserData, drwav_uint32 flags, const drwav_allocation_callbacks* pAllocationCallbacks); +DRWAV_API drwav_bool32 drwav_init_file_w(drwav* pWav, const wchar_t* filename, const drwav_allocation_callbacks* pAllocationCallbacks); +DRWAV_API drwav_bool32 drwav_init_file_ex_w(drwav* pWav, const wchar_t* filename, drwav_chunk_proc onChunk, void* pChunkUserData, drwav_uint32 flags, const drwav_allocation_callbacks* pAllocationCallbacks); +DRWAV_API drwav_bool32 drwav_init_file_with_metadata(drwav* pWav, const char* filename, drwav_uint32 flags, const drwav_allocation_callbacks* pAllocationCallbacks); +DRWAV_API drwav_bool32 drwav_init_file_with_metadata_w(drwav* pWav, const wchar_t* filename, drwav_uint32 flags, const drwav_allocation_callbacks* pAllocationCallbacks); +DRWAV_API drwav_bool32 drwav_init_file_write(drwav* pWav, const char* filename, const drwav_data_format* pFormat, const drwav_allocation_callbacks* pAllocationCallbacks); +DRWAV_API drwav_bool32 drwav_init_file_write_sequential(drwav* pWav, const char* filename, const drwav_data_format* pFormat, drwav_uint64 totalSampleCount, const drwav_allocation_callbacks* pAllocationCallbacks); +DRWAV_API drwav_bool32 drwav_init_file_write_sequential_pcm_frames(drwav* pWav, const char* filename, const drwav_data_format* pFormat, drwav_uint64 totalPCMFrameCount, const drwav_allocation_callbacks* pAllocationCallbacks); +DRWAV_API drwav_bool32 drwav_init_file_write_w(drwav* pWav, const wchar_t* filename, const drwav_data_format* pFormat, const drwav_allocation_callbacks* pAllocationCallbacks); +DRWAV_API drwav_bool32 drwav_init_file_write_sequential_w(drwav* pWav, const wchar_t* filename, const drwav_data_format* pFormat, drwav_uint64 totalSampleCount, const drwav_allocation_callbacks* pAllocationCallbacks); +DRWAV_API drwav_bool32 drwav_init_file_write_sequential_pcm_frames_w(drwav* pWav, const wchar_t* filename, const drwav_data_format* pFormat, drwav_uint64 totalPCMFrameCount, const drwav_allocation_callbacks* pAllocationCallbacks); +#endif +DRWAV_API drwav_bool32 drwav_init_memory(drwav* pWav, const void* data, size_t dataSize, const drwav_allocation_callbacks* pAllocationCallbacks); +DRWAV_API drwav_bool32 drwav_init_memory_ex(drwav* pWav, const void* data, size_t dataSize, drwav_chunk_proc onChunk, void* pChunkUserData, drwav_uint32 flags, const drwav_allocation_callbacks* pAllocationCallbacks); +DRWAV_API drwav_bool32 drwav_init_memory_with_metadata(drwav* pWav, const void* data, size_t dataSize, drwav_uint32 flags, const drwav_allocation_callbacks* pAllocationCallbacks); +DRWAV_API drwav_bool32 drwav_init_memory_write(drwav* pWav, void** ppData, size_t* pDataSize, const drwav_data_format* pFormat, const drwav_allocation_callbacks* pAllocationCallbacks); +DRWAV_API drwav_bool32 drwav_init_memory_write_sequential(drwav* pWav, void** ppData, size_t* pDataSize, const drwav_data_format* pFormat, drwav_uint64 totalSampleCount, const drwav_allocation_callbacks* pAllocationCallbacks); +DRWAV_API drwav_bool32 drwav_init_memory_write_sequential_pcm_frames(drwav* pWav, void** ppData, size_t* pDataSize, const drwav_data_format* pFormat, drwav_uint64 totalPCMFrameCount, const drwav_allocation_callbacks* pAllocationCallbacks); +#ifndef DR_WAV_NO_CONVERSION_API +DRWAV_API drwav_int16* drwav_open_and_read_pcm_frames_s16(drwav_read_proc onRead, drwav_seek_proc onSeek, void* pUserData, unsigned int* channelsOut, unsigned int* sampleRateOut, drwav_uint64* totalFrameCountOut, const drwav_allocation_callbacks* pAllocationCallbacks); +DRWAV_API float* drwav_open_and_read_pcm_frames_f32(drwav_read_proc onRead, drwav_seek_proc onSeek, void* pUserData, unsigned int* channelsOut, unsigned int* sampleRateOut, drwav_uint64* totalFrameCountOut, const drwav_allocation_callbacks* pAllocationCallbacks); +DRWAV_API drwav_int32* drwav_open_and_read_pcm_frames_s32(drwav_read_proc onRead, drwav_seek_proc onSeek, void* pUserData, unsigned int* channelsOut, unsigned int* sampleRateOut, drwav_uint64* totalFrameCountOut, const drwav_allocation_callbacks* pAllocationCallbacks); +#ifndef DR_WAV_NO_STDIO +DRWAV_API drwav_int16* drwav_open_file_and_read_pcm_frames_s16(const char* filename, unsigned int* channelsOut, unsigned int* sampleRateOut, drwav_uint64* totalFrameCountOut, const drwav_allocation_callbacks* pAllocationCallbacks); +DRWAV_API float* drwav_open_file_and_read_pcm_frames_f32(const char* filename, unsigned int* channelsOut, unsigned int* sampleRateOut, drwav_uint64* totalFrameCountOut, const drwav_allocation_callbacks* pAllocationCallbacks); +DRWAV_API drwav_int32* drwav_open_file_and_read_pcm_frames_s32(const char* filename, unsigned int* channelsOut, unsigned int* sampleRateOut, drwav_uint64* totalFrameCountOut, const drwav_allocation_callbacks* pAllocationCallbacks); +DRWAV_API drwav_int16* drwav_open_file_and_read_pcm_frames_s16_w(const wchar_t* filename, unsigned int* channelsOut, unsigned int* sampleRateOut, drwav_uint64* totalFrameCountOut, const drwav_allocation_callbacks* pAllocationCallbacks); +DRWAV_API float* drwav_open_file_and_read_pcm_frames_f32_w(const wchar_t* filename, unsigned int* channelsOut, unsigned int* sampleRateOut, drwav_uint64* totalFrameCountOut, const drwav_allocation_callbacks* pAllocationCallbacks); +DRWAV_API drwav_int32* drwav_open_file_and_read_pcm_frames_s32_w(const wchar_t* filename, unsigned int* channelsOut, unsigned int* sampleRateOut, drwav_uint64* totalFrameCountOut, const drwav_allocation_callbacks* pAllocationCallbacks); +#endif +DRWAV_API drwav_int16* drwav_open_memory_and_read_pcm_frames_s16(const void* data, size_t dataSize, unsigned int* channelsOut, unsigned int* sampleRateOut, drwav_uint64* totalFrameCountOut, const drwav_allocation_callbacks* pAllocationCallbacks); +DRWAV_API float* drwav_open_memory_and_read_pcm_frames_f32(const void* data, size_t dataSize, unsigned int* channelsOut, unsigned int* sampleRateOut, drwav_uint64* totalFrameCountOut, const drwav_allocation_callbacks* pAllocationCallbacks); +DRWAV_API drwav_int32* drwav_open_memory_and_read_pcm_frames_s32(const void* data, size_t dataSize, unsigned int* channelsOut, unsigned int* sampleRateOut, drwav_uint64* totalFrameCountOut, const drwav_allocation_callbacks* pAllocationCallbacks); +#endif +DRWAV_API void drwav_free(void* p, const drwav_allocation_callbacks* pAllocationCallbacks); +DRWAV_API drwav_uint16 drwav_bytes_to_u16(const drwav_uint8* data); +DRWAV_API drwav_int16 drwav_bytes_to_s16(const drwav_uint8* data); +DRWAV_API drwav_uint32 drwav_bytes_to_u32(const drwav_uint8* data); +DRWAV_API drwav_int32 drwav_bytes_to_s32(const drwav_uint8* data); +DRWAV_API drwav_uint64 drwav_bytes_to_u64(const drwav_uint8* data); +DRWAV_API drwav_int64 drwav_bytes_to_s64(const drwav_uint8* data); +DRWAV_API float drwav_bytes_to_f32(const drwav_uint8* data); +DRWAV_API drwav_bool32 drwav_guid_equal(const drwav_uint8 a[16], const drwav_uint8 b[16]); +DRWAV_API drwav_bool32 drwav_fourcc_equal(const drwav_uint8* a, const char* b); #ifdef __cplusplus } #endif @@ -60745,284 +60194,354 @@ MA_API ma_bool32 ma_dr_wav_fourcc_equal(const ma_uint8* a, const char* b); #if !defined(MA_NO_FLAC) && !defined(MA_NO_DECODING) /* dr_flac_h begin */ -#ifndef ma_dr_flac_h -#define ma_dr_flac_h +#ifndef dr_flac_h +#define dr_flac_h #ifdef __cplusplus extern "C" { #endif -#define MA_DR_FLAC_STRINGIFY(x) #x -#define MA_DR_FLAC_XSTRINGIFY(x) MA_DR_FLAC_STRINGIFY(x) -#define MA_DR_FLAC_VERSION_MAJOR 0 -#define MA_DR_FLAC_VERSION_MINOR 12 -#define MA_DR_FLAC_VERSION_REVISION 43 -#define MA_DR_FLAC_VERSION_STRING MA_DR_FLAC_XSTRINGIFY(MA_DR_FLAC_VERSION_MAJOR) "." MA_DR_FLAC_XSTRINGIFY(MA_DR_FLAC_VERSION_MINOR) "." MA_DR_FLAC_XSTRINGIFY(MA_DR_FLAC_VERSION_REVISION) +#define DRFLAC_STRINGIFY(x) #x +#define DRFLAC_XSTRINGIFY(x) DRFLAC_STRINGIFY(x) +#define DRFLAC_VERSION_MAJOR 0 +#define DRFLAC_VERSION_MINOR 12 +#define DRFLAC_VERSION_REVISION 39 +#define DRFLAC_VERSION_STRING DRFLAC_XSTRINGIFY(DRFLAC_VERSION_MAJOR) "." DRFLAC_XSTRINGIFY(DRFLAC_VERSION_MINOR) "." DRFLAC_XSTRINGIFY(DRFLAC_VERSION_REVISION) #include +typedef signed char drflac_int8; +typedef unsigned char drflac_uint8; +typedef signed short drflac_int16; +typedef unsigned short drflac_uint16; +typedef signed int drflac_int32; +typedef unsigned int drflac_uint32; +#if defined(_MSC_VER) && !defined(__clang__) + typedef signed __int64 drflac_int64; + typedef unsigned __int64 drflac_uint64; +#else + #if defined(__clang__) || (defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6))) + #pragma GCC diagnostic push + #pragma GCC diagnostic ignored "-Wlong-long" + #if defined(__clang__) + #pragma GCC diagnostic ignored "-Wc++11-long-long" + #endif + #endif + typedef signed long long drflac_int64; + typedef unsigned long long drflac_uint64; + #if defined(__clang__) || (defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6))) + #pragma GCC diagnostic pop + #endif +#endif +#if defined(__LP64__) || defined(_WIN64) || (defined(__x86_64__) && !defined(__ILP32__)) || defined(_M_X64) || defined(__ia64) || defined(_M_IA64) || defined(__aarch64__) || defined(_M_ARM64) || defined(__powerpc64__) + typedef drflac_uint64 drflac_uintptr; +#else + typedef drflac_uint32 drflac_uintptr; +#endif +typedef drflac_uint8 drflac_bool8; +typedef drflac_uint32 drflac_bool32; +#define DRFLAC_TRUE 1 +#define DRFLAC_FALSE 0 +#if !defined(DRFLAC_API) + #if defined(DRFLAC_DLL) + #if defined(_WIN32) + #define DRFLAC_DLL_IMPORT __declspec(dllimport) + #define DRFLAC_DLL_EXPORT __declspec(dllexport) + #define DRFLAC_DLL_PRIVATE static + #else + #if defined(__GNUC__) && __GNUC__ >= 4 + #define DRFLAC_DLL_IMPORT __attribute__((visibility("default"))) + #define DRFLAC_DLL_EXPORT __attribute__((visibility("default"))) + #define DRFLAC_DLL_PRIVATE __attribute__((visibility("hidden"))) + #else + #define DRFLAC_DLL_IMPORT + #define DRFLAC_DLL_EXPORT + #define DRFLAC_DLL_PRIVATE static + #endif + #endif + #if defined(DR_FLAC_IMPLEMENTATION) || defined(DRFLAC_IMPLEMENTATION) + #define DRFLAC_API DRFLAC_DLL_EXPORT + #else + #define DRFLAC_API DRFLAC_DLL_IMPORT + #endif + #define DRFLAC_PRIVATE DRFLAC_DLL_PRIVATE + #else + #define DRFLAC_API extern + #define DRFLAC_PRIVATE static + #endif +#endif #if defined(_MSC_VER) && _MSC_VER >= 1700 - #define MA_DR_FLAC_DEPRECATED __declspec(deprecated) + #define DRFLAC_DEPRECATED __declspec(deprecated) #elif (defined(__GNUC__) && __GNUC__ >= 4) - #define MA_DR_FLAC_DEPRECATED __attribute__((deprecated)) + #define DRFLAC_DEPRECATED __attribute__((deprecated)) #elif defined(__has_feature) #if __has_feature(attribute_deprecated) - #define MA_DR_FLAC_DEPRECATED __attribute__((deprecated)) + #define DRFLAC_DEPRECATED __attribute__((deprecated)) #else - #define MA_DR_FLAC_DEPRECATED + #define DRFLAC_DEPRECATED #endif #else - #define MA_DR_FLAC_DEPRECATED + #define DRFLAC_DEPRECATED #endif -MA_API void ma_dr_flac_version(ma_uint32* pMajor, ma_uint32* pMinor, ma_uint32* pRevision); -MA_API const char* ma_dr_flac_version_string(void); -#ifndef MA_DR_FLAC_BUFFER_SIZE -#define MA_DR_FLAC_BUFFER_SIZE 4096 +DRFLAC_API void drflac_version(drflac_uint32* pMajor, drflac_uint32* pMinor, drflac_uint32* pRevision); +DRFLAC_API const char* drflac_version_string(void); +#ifndef DR_FLAC_BUFFER_SIZE +#define DR_FLAC_BUFFER_SIZE 4096 #endif -#ifdef MA_64BIT -typedef ma_uint64 ma_dr_flac_cache_t; +#if defined(_WIN64) || defined(_LP64) || defined(__LP64__) +#define DRFLAC_64BIT +#endif +#ifdef DRFLAC_64BIT +typedef drflac_uint64 drflac_cache_t; #else -typedef ma_uint32 ma_dr_flac_cache_t; -#endif -#define MA_DR_FLAC_METADATA_BLOCK_TYPE_STREAMINFO 0 -#define MA_DR_FLAC_METADATA_BLOCK_TYPE_PADDING 1 -#define MA_DR_FLAC_METADATA_BLOCK_TYPE_APPLICATION 2 -#define MA_DR_FLAC_METADATA_BLOCK_TYPE_SEEKTABLE 3 -#define MA_DR_FLAC_METADATA_BLOCK_TYPE_VORBIS_COMMENT 4 -#define MA_DR_FLAC_METADATA_BLOCK_TYPE_CUESHEET 5 -#define MA_DR_FLAC_METADATA_BLOCK_TYPE_PICTURE 6 -#define MA_DR_FLAC_METADATA_BLOCK_TYPE_INVALID 127 -#define MA_DR_FLAC_PICTURE_TYPE_OTHER 0 -#define MA_DR_FLAC_PICTURE_TYPE_FILE_ICON 1 -#define MA_DR_FLAC_PICTURE_TYPE_OTHER_FILE_ICON 2 -#define MA_DR_FLAC_PICTURE_TYPE_COVER_FRONT 3 -#define MA_DR_FLAC_PICTURE_TYPE_COVER_BACK 4 -#define MA_DR_FLAC_PICTURE_TYPE_LEAFLET_PAGE 5 -#define MA_DR_FLAC_PICTURE_TYPE_MEDIA 6 -#define MA_DR_FLAC_PICTURE_TYPE_LEAD_ARTIST 7 -#define MA_DR_FLAC_PICTURE_TYPE_ARTIST 8 -#define MA_DR_FLAC_PICTURE_TYPE_CONDUCTOR 9 -#define MA_DR_FLAC_PICTURE_TYPE_BAND 10 -#define MA_DR_FLAC_PICTURE_TYPE_COMPOSER 11 -#define MA_DR_FLAC_PICTURE_TYPE_LYRICIST 12 -#define MA_DR_FLAC_PICTURE_TYPE_RECORDING_LOCATION 13 -#define MA_DR_FLAC_PICTURE_TYPE_DURING_RECORDING 14 -#define MA_DR_FLAC_PICTURE_TYPE_DURING_PERFORMANCE 15 -#define MA_DR_FLAC_PICTURE_TYPE_SCREEN_CAPTURE 16 -#define MA_DR_FLAC_PICTURE_TYPE_BRIGHT_COLORED_FISH 17 -#define MA_DR_FLAC_PICTURE_TYPE_ILLUSTRATION 18 -#define MA_DR_FLAC_PICTURE_TYPE_BAND_LOGOTYPE 19 -#define MA_DR_FLAC_PICTURE_TYPE_PUBLISHER_LOGOTYPE 20 +typedef drflac_uint32 drflac_cache_t; +#endif +#define DRFLAC_METADATA_BLOCK_TYPE_STREAMINFO 0 +#define DRFLAC_METADATA_BLOCK_TYPE_PADDING 1 +#define DRFLAC_METADATA_BLOCK_TYPE_APPLICATION 2 +#define DRFLAC_METADATA_BLOCK_TYPE_SEEKTABLE 3 +#define DRFLAC_METADATA_BLOCK_TYPE_VORBIS_COMMENT 4 +#define DRFLAC_METADATA_BLOCK_TYPE_CUESHEET 5 +#define DRFLAC_METADATA_BLOCK_TYPE_PICTURE 6 +#define DRFLAC_METADATA_BLOCK_TYPE_INVALID 127 +#define DRFLAC_PICTURE_TYPE_OTHER 0 +#define DRFLAC_PICTURE_TYPE_FILE_ICON 1 +#define DRFLAC_PICTURE_TYPE_OTHER_FILE_ICON 2 +#define DRFLAC_PICTURE_TYPE_COVER_FRONT 3 +#define DRFLAC_PICTURE_TYPE_COVER_BACK 4 +#define DRFLAC_PICTURE_TYPE_LEAFLET_PAGE 5 +#define DRFLAC_PICTURE_TYPE_MEDIA 6 +#define DRFLAC_PICTURE_TYPE_LEAD_ARTIST 7 +#define DRFLAC_PICTURE_TYPE_ARTIST 8 +#define DRFLAC_PICTURE_TYPE_CONDUCTOR 9 +#define DRFLAC_PICTURE_TYPE_BAND 10 +#define DRFLAC_PICTURE_TYPE_COMPOSER 11 +#define DRFLAC_PICTURE_TYPE_LYRICIST 12 +#define DRFLAC_PICTURE_TYPE_RECORDING_LOCATION 13 +#define DRFLAC_PICTURE_TYPE_DURING_RECORDING 14 +#define DRFLAC_PICTURE_TYPE_DURING_PERFORMANCE 15 +#define DRFLAC_PICTURE_TYPE_SCREEN_CAPTURE 16 +#define DRFLAC_PICTURE_TYPE_BRIGHT_COLORED_FISH 17 +#define DRFLAC_PICTURE_TYPE_ILLUSTRATION 18 +#define DRFLAC_PICTURE_TYPE_BAND_LOGOTYPE 19 +#define DRFLAC_PICTURE_TYPE_PUBLISHER_LOGOTYPE 20 typedef enum { - ma_dr_flac_container_native, - ma_dr_flac_container_ogg, - ma_dr_flac_container_unknown -} ma_dr_flac_container; + drflac_container_native, + drflac_container_ogg, + drflac_container_unknown +} drflac_container; typedef enum { - ma_dr_flac_seek_origin_start, - ma_dr_flac_seek_origin_current -} ma_dr_flac_seek_origin; + drflac_seek_origin_start, + drflac_seek_origin_current +} drflac_seek_origin; typedef struct { - ma_uint64 firstPCMFrame; - ma_uint64 flacFrameOffset; - ma_uint16 pcmFrameCount; -} ma_dr_flac_seekpoint; + drflac_uint64 firstPCMFrame; + drflac_uint64 flacFrameOffset; + drflac_uint16 pcmFrameCount; +} drflac_seekpoint; typedef struct { - ma_uint16 minBlockSizeInPCMFrames; - ma_uint16 maxBlockSizeInPCMFrames; - ma_uint32 minFrameSizeInPCMFrames; - ma_uint32 maxFrameSizeInPCMFrames; - ma_uint32 sampleRate; - ma_uint8 channels; - ma_uint8 bitsPerSample; - ma_uint64 totalPCMFrameCount; - ma_uint8 md5[16]; -} ma_dr_flac_streaminfo; + drflac_uint16 minBlockSizeInPCMFrames; + drflac_uint16 maxBlockSizeInPCMFrames; + drflac_uint32 minFrameSizeInPCMFrames; + drflac_uint32 maxFrameSizeInPCMFrames; + drflac_uint32 sampleRate; + drflac_uint8 channels; + drflac_uint8 bitsPerSample; + drflac_uint64 totalPCMFrameCount; + drflac_uint8 md5[16]; +} drflac_streaminfo; typedef struct { - ma_uint32 type; + drflac_uint32 type; const void* pRawData; - ma_uint32 rawDataSize; + drflac_uint32 rawDataSize; union { - ma_dr_flac_streaminfo streaminfo; + drflac_streaminfo streaminfo; struct { int unused; } padding; struct { - ma_uint32 id; + drflac_uint32 id; const void* pData; - ma_uint32 dataSize; + drflac_uint32 dataSize; } application; struct { - ma_uint32 seekpointCount; - const ma_dr_flac_seekpoint* pSeekpoints; + drflac_uint32 seekpointCount; + const drflac_seekpoint* pSeekpoints; } seektable; struct { - ma_uint32 vendorLength; + drflac_uint32 vendorLength; const char* vendor; - ma_uint32 commentCount; + drflac_uint32 commentCount; const void* pComments; } vorbis_comment; struct { char catalog[128]; - ma_uint64 leadInSampleCount; - ma_bool32 isCD; - ma_uint8 trackCount; + drflac_uint64 leadInSampleCount; + drflac_bool32 isCD; + drflac_uint8 trackCount; const void* pTrackData; } cuesheet; struct { - ma_uint32 type; - ma_uint32 mimeLength; + drflac_uint32 type; + drflac_uint32 mimeLength; const char* mime; - ma_uint32 descriptionLength; + drflac_uint32 descriptionLength; const char* description; - ma_uint32 width; - ma_uint32 height; - ma_uint32 colorDepth; - ma_uint32 indexColorCount; - ma_uint32 pictureDataSize; - const ma_uint8* pPictureData; + drflac_uint32 width; + drflac_uint32 height; + drflac_uint32 colorDepth; + drflac_uint32 indexColorCount; + drflac_uint32 pictureDataSize; + const drflac_uint8* pPictureData; } picture; } data; -} ma_dr_flac_metadata; -typedef size_t (* ma_dr_flac_read_proc)(void* pUserData, void* pBufferOut, size_t bytesToRead); -typedef ma_bool32 (* ma_dr_flac_seek_proc)(void* pUserData, int offset, ma_dr_flac_seek_origin origin); -typedef void (* ma_dr_flac_meta_proc)(void* pUserData, ma_dr_flac_metadata* pMetadata); +} drflac_metadata; +typedef size_t (* drflac_read_proc)(void* pUserData, void* pBufferOut, size_t bytesToRead); +typedef drflac_bool32 (* drflac_seek_proc)(void* pUserData, int offset, drflac_seek_origin origin); +typedef void (* drflac_meta_proc)(void* pUserData, drflac_metadata* pMetadata); typedef struct { - const ma_uint8* data; + void* pUserData; + void* (* onMalloc)(size_t sz, void* pUserData); + void* (* onRealloc)(void* p, size_t sz, void* pUserData); + void (* onFree)(void* p, void* pUserData); +} drflac_allocation_callbacks; +typedef struct +{ + const drflac_uint8* data; size_t dataSize; size_t currentReadPos; -} ma_dr_flac__memory_stream; +} drflac__memory_stream; typedef struct { - ma_dr_flac_read_proc onRead; - ma_dr_flac_seek_proc onSeek; + drflac_read_proc onRead; + drflac_seek_proc onSeek; void* pUserData; size_t unalignedByteCount; - ma_dr_flac_cache_t unalignedCache; - ma_uint32 nextL2Line; - ma_uint32 consumedBits; - ma_dr_flac_cache_t cacheL2[MA_DR_FLAC_BUFFER_SIZE/sizeof(ma_dr_flac_cache_t)]; - ma_dr_flac_cache_t cache; - ma_uint16 crc16; - ma_dr_flac_cache_t crc16Cache; - ma_uint32 crc16CacheIgnoredBytes; -} ma_dr_flac_bs; + drflac_cache_t unalignedCache; + drflac_uint32 nextL2Line; + drflac_uint32 consumedBits; + drflac_cache_t cacheL2[DR_FLAC_BUFFER_SIZE/sizeof(drflac_cache_t)]; + drflac_cache_t cache; + drflac_uint16 crc16; + drflac_cache_t crc16Cache; + drflac_uint32 crc16CacheIgnoredBytes; +} drflac_bs; typedef struct { - ma_uint8 subframeType; - ma_uint8 wastedBitsPerSample; - ma_uint8 lpcOrder; - ma_int32* pSamplesS32; -} ma_dr_flac_subframe; + drflac_uint8 subframeType; + drflac_uint8 wastedBitsPerSample; + drflac_uint8 lpcOrder; + drflac_int32* pSamplesS32; +} drflac_subframe; typedef struct { - ma_uint64 pcmFrameNumber; - ma_uint32 flacFrameNumber; - ma_uint32 sampleRate; - ma_uint16 blockSizeInPCMFrames; - ma_uint8 channelAssignment; - ma_uint8 bitsPerSample; - ma_uint8 crc8; -} ma_dr_flac_frame_header; + drflac_uint64 pcmFrameNumber; + drflac_uint32 flacFrameNumber; + drflac_uint32 sampleRate; + drflac_uint16 blockSizeInPCMFrames; + drflac_uint8 channelAssignment; + drflac_uint8 bitsPerSample; + drflac_uint8 crc8; +} drflac_frame_header; typedef struct { - ma_dr_flac_frame_header header; - ma_uint32 pcmFramesRemaining; - ma_dr_flac_subframe subframes[8]; -} ma_dr_flac_frame; + drflac_frame_header header; + drflac_uint32 pcmFramesRemaining; + drflac_subframe subframes[8]; +} drflac_frame; typedef struct { - ma_dr_flac_meta_proc onMeta; + drflac_meta_proc onMeta; void* pUserDataMD; - ma_allocation_callbacks allocationCallbacks; - ma_uint32 sampleRate; - ma_uint8 channels; - ma_uint8 bitsPerSample; - ma_uint16 maxBlockSizeInPCMFrames; - ma_uint64 totalPCMFrameCount; - ma_dr_flac_container container; - ma_uint32 seekpointCount; - ma_dr_flac_frame currentFLACFrame; - ma_uint64 currentPCMFrame; - ma_uint64 firstFLACFramePosInBytes; - ma_dr_flac__memory_stream memoryStream; - ma_int32* pDecodedSamples; - ma_dr_flac_seekpoint* pSeekpoints; + drflac_allocation_callbacks allocationCallbacks; + drflac_uint32 sampleRate; + drflac_uint8 channels; + drflac_uint8 bitsPerSample; + drflac_uint16 maxBlockSizeInPCMFrames; + drflac_uint64 totalPCMFrameCount; + drflac_container container; + drflac_uint32 seekpointCount; + drflac_frame currentFLACFrame; + drflac_uint64 currentPCMFrame; + drflac_uint64 firstFLACFramePosInBytes; + drflac__memory_stream memoryStream; + drflac_int32* pDecodedSamples; + drflac_seekpoint* pSeekpoints; void* _oggbs; - ma_bool32 _noSeekTableSeek : 1; - ma_bool32 _noBinarySearchSeek : 1; - ma_bool32 _noBruteForceSeek : 1; - ma_dr_flac_bs bs; - ma_uint8 pExtraData[1]; -} ma_dr_flac; -MA_API ma_dr_flac* ma_dr_flac_open(ma_dr_flac_read_proc onRead, ma_dr_flac_seek_proc onSeek, void* pUserData, const ma_allocation_callbacks* pAllocationCallbacks); -MA_API ma_dr_flac* ma_dr_flac_open_relaxed(ma_dr_flac_read_proc onRead, ma_dr_flac_seek_proc onSeek, ma_dr_flac_container container, void* pUserData, const ma_allocation_callbacks* pAllocationCallbacks); -MA_API ma_dr_flac* ma_dr_flac_open_with_metadata(ma_dr_flac_read_proc onRead, ma_dr_flac_seek_proc onSeek, ma_dr_flac_meta_proc onMeta, void* pUserData, const ma_allocation_callbacks* pAllocationCallbacks); -MA_API ma_dr_flac* ma_dr_flac_open_with_metadata_relaxed(ma_dr_flac_read_proc onRead, ma_dr_flac_seek_proc onSeek, ma_dr_flac_meta_proc onMeta, ma_dr_flac_container container, void* pUserData, const ma_allocation_callbacks* pAllocationCallbacks); -MA_API void ma_dr_flac_close(ma_dr_flac* pFlac); -MA_API ma_uint64 ma_dr_flac_read_pcm_frames_s32(ma_dr_flac* pFlac, ma_uint64 framesToRead, ma_int32* pBufferOut); -MA_API ma_uint64 ma_dr_flac_read_pcm_frames_s16(ma_dr_flac* pFlac, ma_uint64 framesToRead, ma_int16* pBufferOut); -MA_API ma_uint64 ma_dr_flac_read_pcm_frames_f32(ma_dr_flac* pFlac, ma_uint64 framesToRead, float* pBufferOut); -MA_API ma_bool32 ma_dr_flac_seek_to_pcm_frame(ma_dr_flac* pFlac, ma_uint64 pcmFrameIndex); -#ifndef MA_DR_FLAC_NO_STDIO -MA_API ma_dr_flac* ma_dr_flac_open_file(const char* pFileName, const ma_allocation_callbacks* pAllocationCallbacks); -MA_API ma_dr_flac* ma_dr_flac_open_file_w(const wchar_t* pFileName, const ma_allocation_callbacks* pAllocationCallbacks); -MA_API ma_dr_flac* ma_dr_flac_open_file_with_metadata(const char* pFileName, ma_dr_flac_meta_proc onMeta, void* pUserData, const ma_allocation_callbacks* pAllocationCallbacks); -MA_API ma_dr_flac* ma_dr_flac_open_file_with_metadata_w(const wchar_t* pFileName, ma_dr_flac_meta_proc onMeta, void* pUserData, const ma_allocation_callbacks* pAllocationCallbacks); -#endif -MA_API ma_dr_flac* ma_dr_flac_open_memory(const void* pData, size_t dataSize, const ma_allocation_callbacks* pAllocationCallbacks); -MA_API ma_dr_flac* ma_dr_flac_open_memory_with_metadata(const void* pData, size_t dataSize, ma_dr_flac_meta_proc onMeta, void* pUserData, const ma_allocation_callbacks* pAllocationCallbacks); -MA_API ma_int32* ma_dr_flac_open_and_read_pcm_frames_s32(ma_dr_flac_read_proc onRead, ma_dr_flac_seek_proc onSeek, void* pUserData, unsigned int* channels, unsigned int* sampleRate, ma_uint64* totalPCMFrameCount, const ma_allocation_callbacks* pAllocationCallbacks); -MA_API ma_int16* ma_dr_flac_open_and_read_pcm_frames_s16(ma_dr_flac_read_proc onRead, ma_dr_flac_seek_proc onSeek, void* pUserData, unsigned int* channels, unsigned int* sampleRate, ma_uint64* totalPCMFrameCount, const ma_allocation_callbacks* pAllocationCallbacks); -MA_API float* ma_dr_flac_open_and_read_pcm_frames_f32(ma_dr_flac_read_proc onRead, ma_dr_flac_seek_proc onSeek, void* pUserData, unsigned int* channels, unsigned int* sampleRate, ma_uint64* totalPCMFrameCount, const ma_allocation_callbacks* pAllocationCallbacks); -#ifndef MA_DR_FLAC_NO_STDIO -MA_API ma_int32* ma_dr_flac_open_file_and_read_pcm_frames_s32(const char* filename, unsigned int* channels, unsigned int* sampleRate, ma_uint64* totalPCMFrameCount, const ma_allocation_callbacks* pAllocationCallbacks); -MA_API ma_int16* ma_dr_flac_open_file_and_read_pcm_frames_s16(const char* filename, unsigned int* channels, unsigned int* sampleRate, ma_uint64* totalPCMFrameCount, const ma_allocation_callbacks* pAllocationCallbacks); -MA_API float* ma_dr_flac_open_file_and_read_pcm_frames_f32(const char* filename, unsigned int* channels, unsigned int* sampleRate, ma_uint64* totalPCMFrameCount, const ma_allocation_callbacks* pAllocationCallbacks); -#endif -MA_API ma_int32* ma_dr_flac_open_memory_and_read_pcm_frames_s32(const void* data, size_t dataSize, unsigned int* channels, unsigned int* sampleRate, ma_uint64* totalPCMFrameCount, const ma_allocation_callbacks* pAllocationCallbacks); -MA_API ma_int16* ma_dr_flac_open_memory_and_read_pcm_frames_s16(const void* data, size_t dataSize, unsigned int* channels, unsigned int* sampleRate, ma_uint64* totalPCMFrameCount, const ma_allocation_callbacks* pAllocationCallbacks); -MA_API float* ma_dr_flac_open_memory_and_read_pcm_frames_f32(const void* data, size_t dataSize, unsigned int* channels, unsigned int* sampleRate, ma_uint64* totalPCMFrameCount, const ma_allocation_callbacks* pAllocationCallbacks); -MA_API void ma_dr_flac_free(void* p, const ma_allocation_callbacks* pAllocationCallbacks); + drflac_bool32 _noSeekTableSeek : 1; + drflac_bool32 _noBinarySearchSeek : 1; + drflac_bool32 _noBruteForceSeek : 1; + drflac_bs bs; + drflac_uint8 pExtraData[1]; +} drflac; +DRFLAC_API drflac* drflac_open(drflac_read_proc onRead, drflac_seek_proc onSeek, void* pUserData, const drflac_allocation_callbacks* pAllocationCallbacks); +DRFLAC_API drflac* drflac_open_relaxed(drflac_read_proc onRead, drflac_seek_proc onSeek, drflac_container container, void* pUserData, const drflac_allocation_callbacks* pAllocationCallbacks); +DRFLAC_API drflac* drflac_open_with_metadata(drflac_read_proc onRead, drflac_seek_proc onSeek, drflac_meta_proc onMeta, void* pUserData, const drflac_allocation_callbacks* pAllocationCallbacks); +DRFLAC_API drflac* drflac_open_with_metadata_relaxed(drflac_read_proc onRead, drflac_seek_proc onSeek, drflac_meta_proc onMeta, drflac_container container, void* pUserData, const drflac_allocation_callbacks* pAllocationCallbacks); +DRFLAC_API void drflac_close(drflac* pFlac); +DRFLAC_API drflac_uint64 drflac_read_pcm_frames_s32(drflac* pFlac, drflac_uint64 framesToRead, drflac_int32* pBufferOut); +DRFLAC_API drflac_uint64 drflac_read_pcm_frames_s16(drflac* pFlac, drflac_uint64 framesToRead, drflac_int16* pBufferOut); +DRFLAC_API drflac_uint64 drflac_read_pcm_frames_f32(drflac* pFlac, drflac_uint64 framesToRead, float* pBufferOut); +DRFLAC_API drflac_bool32 drflac_seek_to_pcm_frame(drflac* pFlac, drflac_uint64 pcmFrameIndex); +#ifndef DR_FLAC_NO_STDIO +DRFLAC_API drflac* drflac_open_file(const char* pFileName, const drflac_allocation_callbacks* pAllocationCallbacks); +DRFLAC_API drflac* drflac_open_file_w(const wchar_t* pFileName, const drflac_allocation_callbacks* pAllocationCallbacks); +DRFLAC_API drflac* drflac_open_file_with_metadata(const char* pFileName, drflac_meta_proc onMeta, void* pUserData, const drflac_allocation_callbacks* pAllocationCallbacks); +DRFLAC_API drflac* drflac_open_file_with_metadata_w(const wchar_t* pFileName, drflac_meta_proc onMeta, void* pUserData, const drflac_allocation_callbacks* pAllocationCallbacks); +#endif +DRFLAC_API drflac* drflac_open_memory(const void* pData, size_t dataSize, const drflac_allocation_callbacks* pAllocationCallbacks); +DRFLAC_API drflac* drflac_open_memory_with_metadata(const void* pData, size_t dataSize, drflac_meta_proc onMeta, void* pUserData, const drflac_allocation_callbacks* pAllocationCallbacks); +DRFLAC_API drflac_int32* drflac_open_and_read_pcm_frames_s32(drflac_read_proc onRead, drflac_seek_proc onSeek, void* pUserData, unsigned int* channels, unsigned int* sampleRate, drflac_uint64* totalPCMFrameCount, const drflac_allocation_callbacks* pAllocationCallbacks); +DRFLAC_API drflac_int16* drflac_open_and_read_pcm_frames_s16(drflac_read_proc onRead, drflac_seek_proc onSeek, void* pUserData, unsigned int* channels, unsigned int* sampleRate, drflac_uint64* totalPCMFrameCount, const drflac_allocation_callbacks* pAllocationCallbacks); +DRFLAC_API float* drflac_open_and_read_pcm_frames_f32(drflac_read_proc onRead, drflac_seek_proc onSeek, void* pUserData, unsigned int* channels, unsigned int* sampleRate, drflac_uint64* totalPCMFrameCount, const drflac_allocation_callbacks* pAllocationCallbacks); +#ifndef DR_FLAC_NO_STDIO +DRFLAC_API drflac_int32* drflac_open_file_and_read_pcm_frames_s32(const char* filename, unsigned int* channels, unsigned int* sampleRate, drflac_uint64* totalPCMFrameCount, const drflac_allocation_callbacks* pAllocationCallbacks); +DRFLAC_API drflac_int16* drflac_open_file_and_read_pcm_frames_s16(const char* filename, unsigned int* channels, unsigned int* sampleRate, drflac_uint64* totalPCMFrameCount, const drflac_allocation_callbacks* pAllocationCallbacks); +DRFLAC_API float* drflac_open_file_and_read_pcm_frames_f32(const char* filename, unsigned int* channels, unsigned int* sampleRate, drflac_uint64* totalPCMFrameCount, const drflac_allocation_callbacks* pAllocationCallbacks); +#endif +DRFLAC_API drflac_int32* drflac_open_memory_and_read_pcm_frames_s32(const void* data, size_t dataSize, unsigned int* channels, unsigned int* sampleRate, drflac_uint64* totalPCMFrameCount, const drflac_allocation_callbacks* pAllocationCallbacks); +DRFLAC_API drflac_int16* drflac_open_memory_and_read_pcm_frames_s16(const void* data, size_t dataSize, unsigned int* channels, unsigned int* sampleRate, drflac_uint64* totalPCMFrameCount, const drflac_allocation_callbacks* pAllocationCallbacks); +DRFLAC_API float* drflac_open_memory_and_read_pcm_frames_f32(const void* data, size_t dataSize, unsigned int* channels, unsigned int* sampleRate, drflac_uint64* totalPCMFrameCount, const drflac_allocation_callbacks* pAllocationCallbacks); +DRFLAC_API void drflac_free(void* p, const drflac_allocation_callbacks* pAllocationCallbacks); typedef struct { - ma_uint32 countRemaining; + drflac_uint32 countRemaining; const char* pRunningData; -} ma_dr_flac_vorbis_comment_iterator; -MA_API void ma_dr_flac_init_vorbis_comment_iterator(ma_dr_flac_vorbis_comment_iterator* pIter, ma_uint32 commentCount, const void* pComments); -MA_API const char* ma_dr_flac_next_vorbis_comment(ma_dr_flac_vorbis_comment_iterator* pIter, ma_uint32* pCommentLengthOut); +} drflac_vorbis_comment_iterator; +DRFLAC_API void drflac_init_vorbis_comment_iterator(drflac_vorbis_comment_iterator* pIter, drflac_uint32 commentCount, const void* pComments); +DRFLAC_API const char* drflac_next_vorbis_comment(drflac_vorbis_comment_iterator* pIter, drflac_uint32* pCommentLengthOut); typedef struct { - ma_uint32 countRemaining; + drflac_uint32 countRemaining; const char* pRunningData; -} ma_dr_flac_cuesheet_track_iterator; +} drflac_cuesheet_track_iterator; typedef struct { - ma_uint64 offset; - ma_uint8 index; - ma_uint8 reserved[3]; -} ma_dr_flac_cuesheet_track_index; + drflac_uint64 offset; + drflac_uint8 index; + drflac_uint8 reserved[3]; +} drflac_cuesheet_track_index; typedef struct { - ma_uint64 offset; - ma_uint8 trackNumber; + drflac_uint64 offset; + drflac_uint8 trackNumber; char ISRC[12]; - ma_bool8 isAudio; - ma_bool8 preEmphasis; - ma_uint8 indexCount; - const ma_dr_flac_cuesheet_track_index* pIndexPoints; -} ma_dr_flac_cuesheet_track; -MA_API void ma_dr_flac_init_cuesheet_track_iterator(ma_dr_flac_cuesheet_track_iterator* pIter, ma_uint32 trackCount, const void* pTrackData); -MA_API ma_bool32 ma_dr_flac_next_cuesheet_track(ma_dr_flac_cuesheet_track_iterator* pIter, ma_dr_flac_cuesheet_track* pCuesheetTrack); + drflac_bool8 isAudio; + drflac_bool8 preEmphasis; + drflac_uint8 indexCount; + const drflac_cuesheet_track_index* pIndexPoints; +} drflac_cuesheet_track; +DRFLAC_API void drflac_init_cuesheet_track_iterator(drflac_cuesheet_track_iterator* pIter, drflac_uint32 trackCount, const void* pTrackData); +DRFLAC_API drflac_bool32 drflac_next_cuesheet_track(drflac_cuesheet_track_iterator* pIter, drflac_cuesheet_track* pCuesheetTrack); #ifdef __cplusplus } #endif @@ -61032,109 +60551,249 @@ MA_API ma_bool32 ma_dr_flac_next_cuesheet_track(ma_dr_flac_cuesheet_track_iterat #if !defined(MA_NO_MP3) && !defined(MA_NO_DECODING) /* dr_mp3_h begin */ -#ifndef ma_dr_mp3_h -#define ma_dr_mp3_h +#ifndef dr_mp3_h +#define dr_mp3_h #ifdef __cplusplus extern "C" { #endif -#define MA_DR_MP3_STRINGIFY(x) #x -#define MA_DR_MP3_XSTRINGIFY(x) MA_DR_MP3_STRINGIFY(x) -#define MA_DR_MP3_VERSION_MAJOR 0 -#define MA_DR_MP3_VERSION_MINOR 6 -#define MA_DR_MP3_VERSION_REVISION 40 -#define MA_DR_MP3_VERSION_STRING MA_DR_MP3_XSTRINGIFY(MA_DR_MP3_VERSION_MAJOR) "." MA_DR_MP3_XSTRINGIFY(MA_DR_MP3_VERSION_MINOR) "." MA_DR_MP3_XSTRINGIFY(MA_DR_MP3_VERSION_REVISION) +#define DRMP3_STRINGIFY(x) #x +#define DRMP3_XSTRINGIFY(x) DRMP3_STRINGIFY(x) +#define DRMP3_VERSION_MAJOR 0 +#define DRMP3_VERSION_MINOR 6 +#define DRMP3_VERSION_REVISION 34 +#define DRMP3_VERSION_STRING DRMP3_XSTRINGIFY(DRMP3_VERSION_MAJOR) "." DRMP3_XSTRINGIFY(DRMP3_VERSION_MINOR) "." DRMP3_XSTRINGIFY(DRMP3_VERSION_REVISION) #include -#define MA_DR_MP3_MAX_PCM_FRAMES_PER_MP3_FRAME 1152 -#define MA_DR_MP3_MAX_SAMPLES_PER_FRAME (MA_DR_MP3_MAX_PCM_FRAMES_PER_MP3_FRAME*2) -MA_API void ma_dr_mp3_version(ma_uint32* pMajor, ma_uint32* pMinor, ma_uint32* pRevision); -MA_API const char* ma_dr_mp3_version_string(void); +typedef signed char drmp3_int8; +typedef unsigned char drmp3_uint8; +typedef signed short drmp3_int16; +typedef unsigned short drmp3_uint16; +typedef signed int drmp3_int32; +typedef unsigned int drmp3_uint32; +#if defined(_MSC_VER) && !defined(__clang__) + typedef signed __int64 drmp3_int64; + typedef unsigned __int64 drmp3_uint64; +#else + #if defined(__clang__) || (defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6))) + #pragma GCC diagnostic push + #pragma GCC diagnostic ignored "-Wlong-long" + #if defined(__clang__) + #pragma GCC diagnostic ignored "-Wc++11-long-long" + #endif + #endif + typedef signed long long drmp3_int64; + typedef unsigned long long drmp3_uint64; + #if defined(__clang__) || (defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6))) + #pragma GCC diagnostic pop + #endif +#endif +#if defined(__LP64__) || defined(_WIN64) || (defined(__x86_64__) && !defined(__ILP32__)) || defined(_M_X64) || defined(__ia64) || defined (_M_IA64) || defined(__aarch64__) || defined(_M_ARM64) || defined(__powerpc64__) + typedef drmp3_uint64 drmp3_uintptr; +#else + typedef drmp3_uint32 drmp3_uintptr; +#endif +typedef drmp3_uint8 drmp3_bool8; +typedef drmp3_uint32 drmp3_bool32; +#define DRMP3_TRUE 1 +#define DRMP3_FALSE 0 +#if !defined(DRMP3_API) + #if defined(DRMP3_DLL) + #if defined(_WIN32) + #define DRMP3_DLL_IMPORT __declspec(dllimport) + #define DRMP3_DLL_EXPORT __declspec(dllexport) + #define DRMP3_DLL_PRIVATE static + #else + #if defined(__GNUC__) && __GNUC__ >= 4 + #define DRMP3_DLL_IMPORT __attribute__((visibility("default"))) + #define DRMP3_DLL_EXPORT __attribute__((visibility("default"))) + #define DRMP3_DLL_PRIVATE __attribute__((visibility("hidden"))) + #else + #define DRMP3_DLL_IMPORT + #define DRMP3_DLL_EXPORT + #define DRMP3_DLL_PRIVATE static + #endif + #endif + #if defined(DR_MP3_IMPLEMENTATION) || defined(DRMP3_IMPLEMENTATION) + #define DRMP3_API DRMP3_DLL_EXPORT + #else + #define DRMP3_API DRMP3_DLL_IMPORT + #endif + #define DRMP3_PRIVATE DRMP3_DLL_PRIVATE + #else + #define DRMP3_API extern + #define DRMP3_PRIVATE static + #endif +#endif +typedef drmp3_int32 drmp3_result; +#define DRMP3_SUCCESS 0 +#define DRMP3_ERROR -1 +#define DRMP3_INVALID_ARGS -2 +#define DRMP3_INVALID_OPERATION -3 +#define DRMP3_OUT_OF_MEMORY -4 +#define DRMP3_OUT_OF_RANGE -5 +#define DRMP3_ACCESS_DENIED -6 +#define DRMP3_DOES_NOT_EXIST -7 +#define DRMP3_ALREADY_EXISTS -8 +#define DRMP3_TOO_MANY_OPEN_FILES -9 +#define DRMP3_INVALID_FILE -10 +#define DRMP3_TOO_BIG -11 +#define DRMP3_PATH_TOO_LONG -12 +#define DRMP3_NAME_TOO_LONG -13 +#define DRMP3_NOT_DIRECTORY -14 +#define DRMP3_IS_DIRECTORY -15 +#define DRMP3_DIRECTORY_NOT_EMPTY -16 +#define DRMP3_END_OF_FILE -17 +#define DRMP3_NO_SPACE -18 +#define DRMP3_BUSY -19 +#define DRMP3_IO_ERROR -20 +#define DRMP3_INTERRUPT -21 +#define DRMP3_UNAVAILABLE -22 +#define DRMP3_ALREADY_IN_USE -23 +#define DRMP3_BAD_ADDRESS -24 +#define DRMP3_BAD_SEEK -25 +#define DRMP3_BAD_PIPE -26 +#define DRMP3_DEADLOCK -27 +#define DRMP3_TOO_MANY_LINKS -28 +#define DRMP3_NOT_IMPLEMENTED -29 +#define DRMP3_NO_MESSAGE -30 +#define DRMP3_BAD_MESSAGE -31 +#define DRMP3_NO_DATA_AVAILABLE -32 +#define DRMP3_INVALID_DATA -33 +#define DRMP3_TIMEOUT -34 +#define DRMP3_NO_NETWORK -35 +#define DRMP3_NOT_UNIQUE -36 +#define DRMP3_NOT_SOCKET -37 +#define DRMP3_NO_ADDRESS -38 +#define DRMP3_BAD_PROTOCOL -39 +#define DRMP3_PROTOCOL_UNAVAILABLE -40 +#define DRMP3_PROTOCOL_NOT_SUPPORTED -41 +#define DRMP3_PROTOCOL_FAMILY_NOT_SUPPORTED -42 +#define DRMP3_ADDRESS_FAMILY_NOT_SUPPORTED -43 +#define DRMP3_SOCKET_NOT_SUPPORTED -44 +#define DRMP3_CONNECTION_RESET -45 +#define DRMP3_ALREADY_CONNECTED -46 +#define DRMP3_NOT_CONNECTED -47 +#define DRMP3_CONNECTION_REFUSED -48 +#define DRMP3_NO_HOST -49 +#define DRMP3_IN_PROGRESS -50 +#define DRMP3_CANCELLED -51 +#define DRMP3_MEMORY_ALREADY_MAPPED -52 +#define DRMP3_AT_END -53 +#define DRMP3_MAX_PCM_FRAMES_PER_MP3_FRAME 1152 +#define DRMP3_MAX_SAMPLES_PER_FRAME (DRMP3_MAX_PCM_FRAMES_PER_MP3_FRAME*2) +#ifdef _MSC_VER + #define DRMP3_INLINE __forceinline +#elif defined(__GNUC__) + #if defined(__STRICT_ANSI__) + #define DRMP3_GNUC_INLINE_HINT __inline__ + #else + #define DRMP3_GNUC_INLINE_HINT inline + #endif + #if (__GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 2)) || defined(__clang__) + #define DRMP3_INLINE DRMP3_GNUC_INLINE_HINT __attribute__((always_inline)) + #else + #define DRMP3_INLINE DRMP3_GNUC_INLINE_HINT + #endif +#elif defined(__WATCOMC__) + #define DRMP3_INLINE __inline +#else + #define DRMP3_INLINE +#endif +DRMP3_API void drmp3_version(drmp3_uint32* pMajor, drmp3_uint32* pMinor, drmp3_uint32* pRevision); +DRMP3_API const char* drmp3_version_string(void); typedef struct { int frame_bytes, channels, hz, layer, bitrate_kbps; -} ma_dr_mp3dec_frame_info; +} drmp3dec_frame_info; typedef struct { float mdct_overlap[2][9*32], qmf_state[15*2*32]; int reserv, free_format_bytes; - ma_uint8 header[4], reserv_buf[511]; -} ma_dr_mp3dec; -MA_API void ma_dr_mp3dec_init(ma_dr_mp3dec *dec); -MA_API int ma_dr_mp3dec_decode_frame(ma_dr_mp3dec *dec, const ma_uint8 *mp3, int mp3_bytes, void *pcm, ma_dr_mp3dec_frame_info *info); -MA_API void ma_dr_mp3dec_f32_to_s16(const float *in, ma_int16 *out, size_t num_samples); + drmp3_uint8 header[4], reserv_buf[511]; +} drmp3dec; +DRMP3_API void drmp3dec_init(drmp3dec *dec); +DRMP3_API int drmp3dec_decode_frame(drmp3dec *dec, const drmp3_uint8 *mp3, int mp3_bytes, void *pcm, drmp3dec_frame_info *info); +DRMP3_API void drmp3dec_f32_to_s16(const float *in, drmp3_int16 *out, size_t num_samples); typedef enum { - ma_dr_mp3_seek_origin_start, - ma_dr_mp3_seek_origin_current -} ma_dr_mp3_seek_origin; + drmp3_seek_origin_start, + drmp3_seek_origin_current +} drmp3_seek_origin; typedef struct { - ma_uint64 seekPosInBytes; - ma_uint64 pcmFrameIndex; - ma_uint16 mp3FramesToDiscard; - ma_uint16 pcmFramesToDiscard; -} ma_dr_mp3_seek_point; -typedef size_t (* ma_dr_mp3_read_proc)(void* pUserData, void* pBufferOut, size_t bytesToRead); -typedef ma_bool32 (* ma_dr_mp3_seek_proc)(void* pUserData, int offset, ma_dr_mp3_seek_origin origin); + drmp3_uint64 seekPosInBytes; + drmp3_uint64 pcmFrameIndex; + drmp3_uint16 mp3FramesToDiscard; + drmp3_uint16 pcmFramesToDiscard; +} drmp3_seek_point; +typedef size_t (* drmp3_read_proc)(void* pUserData, void* pBufferOut, size_t bytesToRead); +typedef drmp3_bool32 (* drmp3_seek_proc)(void* pUserData, int offset, drmp3_seek_origin origin); typedef struct { - ma_uint32 channels; - ma_uint32 sampleRate; -} ma_dr_mp3_config; + void* pUserData; + void* (* onMalloc)(size_t sz, void* pUserData); + void* (* onRealloc)(void* p, size_t sz, void* pUserData); + void (* onFree)(void* p, void* pUserData); +} drmp3_allocation_callbacks; typedef struct { - ma_dr_mp3dec decoder; - ma_uint32 channels; - ma_uint32 sampleRate; - ma_dr_mp3_read_proc onRead; - ma_dr_mp3_seek_proc onSeek; + drmp3_uint32 channels; + drmp3_uint32 sampleRate; +} drmp3_config; +typedef struct +{ + drmp3dec decoder; + drmp3_uint32 channels; + drmp3_uint32 sampleRate; + drmp3_read_proc onRead; + drmp3_seek_proc onSeek; void* pUserData; - ma_allocation_callbacks allocationCallbacks; - ma_uint32 mp3FrameChannels; - ma_uint32 mp3FrameSampleRate; - ma_uint32 pcmFramesConsumedInMP3Frame; - ma_uint32 pcmFramesRemainingInMP3Frame; - ma_uint8 pcmFrames[sizeof(float)*MA_DR_MP3_MAX_SAMPLES_PER_FRAME]; - ma_uint64 currentPCMFrame; - ma_uint64 streamCursor; - ma_dr_mp3_seek_point* pSeekPoints; - ma_uint32 seekPointCount; + drmp3_allocation_callbacks allocationCallbacks; + drmp3_uint32 mp3FrameChannels; + drmp3_uint32 mp3FrameSampleRate; + drmp3_uint32 pcmFramesConsumedInMP3Frame; + drmp3_uint32 pcmFramesRemainingInMP3Frame; + drmp3_uint8 pcmFrames[sizeof(float)*DRMP3_MAX_SAMPLES_PER_FRAME]; + drmp3_uint64 currentPCMFrame; + drmp3_uint64 streamCursor; + drmp3_seek_point* pSeekPoints; + drmp3_uint32 seekPointCount; size_t dataSize; size_t dataCapacity; size_t dataConsumed; - ma_uint8* pData; - ma_bool32 atEnd : 1; + drmp3_uint8* pData; + drmp3_bool32 atEnd : 1; struct { - const ma_uint8* pData; + const drmp3_uint8* pData; size_t dataSize; size_t currentReadPos; } memory; -} ma_dr_mp3; -MA_API ma_bool32 ma_dr_mp3_init(ma_dr_mp3* pMP3, ma_dr_mp3_read_proc onRead, ma_dr_mp3_seek_proc onSeek, void* pUserData, const ma_allocation_callbacks* pAllocationCallbacks); -MA_API ma_bool32 ma_dr_mp3_init_memory(ma_dr_mp3* pMP3, const void* pData, size_t dataSize, const ma_allocation_callbacks* pAllocationCallbacks); -#ifndef MA_DR_MP3_NO_STDIO -MA_API ma_bool32 ma_dr_mp3_init_file(ma_dr_mp3* pMP3, const char* pFilePath, const ma_allocation_callbacks* pAllocationCallbacks); -MA_API ma_bool32 ma_dr_mp3_init_file_w(ma_dr_mp3* pMP3, const wchar_t* pFilePath, const ma_allocation_callbacks* pAllocationCallbacks); -#endif -MA_API void ma_dr_mp3_uninit(ma_dr_mp3* pMP3); -MA_API ma_uint64 ma_dr_mp3_read_pcm_frames_f32(ma_dr_mp3* pMP3, ma_uint64 framesToRead, float* pBufferOut); -MA_API ma_uint64 ma_dr_mp3_read_pcm_frames_s16(ma_dr_mp3* pMP3, ma_uint64 framesToRead, ma_int16* pBufferOut); -MA_API ma_bool32 ma_dr_mp3_seek_to_pcm_frame(ma_dr_mp3* pMP3, ma_uint64 frameIndex); -MA_API ma_uint64 ma_dr_mp3_get_pcm_frame_count(ma_dr_mp3* pMP3); -MA_API ma_uint64 ma_dr_mp3_get_mp3_frame_count(ma_dr_mp3* pMP3); -MA_API ma_bool32 ma_dr_mp3_get_mp3_and_pcm_frame_count(ma_dr_mp3* pMP3, ma_uint64* pMP3FrameCount, ma_uint64* pPCMFrameCount); -MA_API ma_bool32 ma_dr_mp3_calculate_seek_points(ma_dr_mp3* pMP3, ma_uint32* pSeekPointCount, ma_dr_mp3_seek_point* pSeekPoints); -MA_API ma_bool32 ma_dr_mp3_bind_seek_table(ma_dr_mp3* pMP3, ma_uint32 seekPointCount, ma_dr_mp3_seek_point* pSeekPoints); -MA_API float* ma_dr_mp3_open_and_read_pcm_frames_f32(ma_dr_mp3_read_proc onRead, ma_dr_mp3_seek_proc onSeek, void* pUserData, ma_dr_mp3_config* pConfig, ma_uint64* pTotalFrameCount, const ma_allocation_callbacks* pAllocationCallbacks); -MA_API ma_int16* ma_dr_mp3_open_and_read_pcm_frames_s16(ma_dr_mp3_read_proc onRead, ma_dr_mp3_seek_proc onSeek, void* pUserData, ma_dr_mp3_config* pConfig, ma_uint64* pTotalFrameCount, const ma_allocation_callbacks* pAllocationCallbacks); -MA_API float* ma_dr_mp3_open_memory_and_read_pcm_frames_f32(const void* pData, size_t dataSize, ma_dr_mp3_config* pConfig, ma_uint64* pTotalFrameCount, const ma_allocation_callbacks* pAllocationCallbacks); -MA_API ma_int16* ma_dr_mp3_open_memory_and_read_pcm_frames_s16(const void* pData, size_t dataSize, ma_dr_mp3_config* pConfig, ma_uint64* pTotalFrameCount, const ma_allocation_callbacks* pAllocationCallbacks); -#ifndef MA_DR_MP3_NO_STDIO -MA_API float* ma_dr_mp3_open_file_and_read_pcm_frames_f32(const char* filePath, ma_dr_mp3_config* pConfig, ma_uint64* pTotalFrameCount, const ma_allocation_callbacks* pAllocationCallbacks); -MA_API ma_int16* ma_dr_mp3_open_file_and_read_pcm_frames_s16(const char* filePath, ma_dr_mp3_config* pConfig, ma_uint64* pTotalFrameCount, const ma_allocation_callbacks* pAllocationCallbacks); -#endif -MA_API void* ma_dr_mp3_malloc(size_t sz, const ma_allocation_callbacks* pAllocationCallbacks); -MA_API void ma_dr_mp3_free(void* p, const ma_allocation_callbacks* pAllocationCallbacks); +} drmp3; +DRMP3_API drmp3_bool32 drmp3_init(drmp3* pMP3, drmp3_read_proc onRead, drmp3_seek_proc onSeek, void* pUserData, const drmp3_allocation_callbacks* pAllocationCallbacks); +DRMP3_API drmp3_bool32 drmp3_init_memory(drmp3* pMP3, const void* pData, size_t dataSize, const drmp3_allocation_callbacks* pAllocationCallbacks); +#ifndef DR_MP3_NO_STDIO +DRMP3_API drmp3_bool32 drmp3_init_file(drmp3* pMP3, const char* pFilePath, const drmp3_allocation_callbacks* pAllocationCallbacks); +DRMP3_API drmp3_bool32 drmp3_init_file_w(drmp3* pMP3, const wchar_t* pFilePath, const drmp3_allocation_callbacks* pAllocationCallbacks); +#endif +DRMP3_API void drmp3_uninit(drmp3* pMP3); +DRMP3_API drmp3_uint64 drmp3_read_pcm_frames_f32(drmp3* pMP3, drmp3_uint64 framesToRead, float* pBufferOut); +DRMP3_API drmp3_uint64 drmp3_read_pcm_frames_s16(drmp3* pMP3, drmp3_uint64 framesToRead, drmp3_int16* pBufferOut); +DRMP3_API drmp3_bool32 drmp3_seek_to_pcm_frame(drmp3* pMP3, drmp3_uint64 frameIndex); +DRMP3_API drmp3_uint64 drmp3_get_pcm_frame_count(drmp3* pMP3); +DRMP3_API drmp3_uint64 drmp3_get_mp3_frame_count(drmp3* pMP3); +DRMP3_API drmp3_bool32 drmp3_get_mp3_and_pcm_frame_count(drmp3* pMP3, drmp3_uint64* pMP3FrameCount, drmp3_uint64* pPCMFrameCount); +DRMP3_API drmp3_bool32 drmp3_calculate_seek_points(drmp3* pMP3, drmp3_uint32* pSeekPointCount, drmp3_seek_point* pSeekPoints); +DRMP3_API drmp3_bool32 drmp3_bind_seek_table(drmp3* pMP3, drmp3_uint32 seekPointCount, drmp3_seek_point* pSeekPoints); +DRMP3_API float* drmp3_open_and_read_pcm_frames_f32(drmp3_read_proc onRead, drmp3_seek_proc onSeek, void* pUserData, drmp3_config* pConfig, drmp3_uint64* pTotalFrameCount, const drmp3_allocation_callbacks* pAllocationCallbacks); +DRMP3_API drmp3_int16* drmp3_open_and_read_pcm_frames_s16(drmp3_read_proc onRead, drmp3_seek_proc onSeek, void* pUserData, drmp3_config* pConfig, drmp3_uint64* pTotalFrameCount, const drmp3_allocation_callbacks* pAllocationCallbacks); +DRMP3_API float* drmp3_open_memory_and_read_pcm_frames_f32(const void* pData, size_t dataSize, drmp3_config* pConfig, drmp3_uint64* pTotalFrameCount, const drmp3_allocation_callbacks* pAllocationCallbacks); +DRMP3_API drmp3_int16* drmp3_open_memory_and_read_pcm_frames_s16(const void* pData, size_t dataSize, drmp3_config* pConfig, drmp3_uint64* pTotalFrameCount, const drmp3_allocation_callbacks* pAllocationCallbacks); +#ifndef DR_MP3_NO_STDIO +DRMP3_API float* drmp3_open_file_and_read_pcm_frames_f32(const char* filePath, drmp3_config* pConfig, drmp3_uint64* pTotalFrameCount, const drmp3_allocation_callbacks* pAllocationCallbacks); +DRMP3_API drmp3_int16* drmp3_open_file_and_read_pcm_frames_s16(const char* filePath, drmp3_config* pConfig, drmp3_uint64* pTotalFrameCount, const drmp3_allocation_callbacks* pAllocationCallbacks); +#endif +DRMP3_API void* drmp3_malloc(size_t sz, const drmp3_allocation_callbacks* pAllocationCallbacks); +DRMP3_API void drmp3_free(void* p, const drmp3_allocation_callbacks* pAllocationCallbacks); #ifdef __cplusplus } #endif @@ -61203,7 +60862,7 @@ MA_API ma_decoder_config ma_decoder_config_init(ma_format outputFormat, ma_uint3 return config; } -MA_API ma_decoder_config ma_decoder_config_init_default(void) +MA_API ma_decoder_config ma_decoder_config_init_default() { return ma_decoder_config_init(ma_format_unknown, 0, 0); } @@ -61348,7 +61007,7 @@ static ma_result ma_decoder_internal_on_tell__custom(void* pUserData, ma_int64* } -static ma_result ma_decoder_init_from_vtable__internal(const ma_decoding_backend_vtable* pVTable, void* pVTableUserData, const ma_decoder_config* pConfig, ma_decoder* pDecoder) +static ma_result ma_decoder_init_from_vtable(const ma_decoding_backend_vtable* pVTable, void* pVTableUserData, const ma_decoder_config* pConfig, ma_decoder* pDecoder) { ma_result result; ma_decoding_backend_config backendConfig; @@ -61377,93 +61036,6 @@ static ma_result ma_decoder_init_from_vtable__internal(const ma_decoding_backend return MA_SUCCESS; } -static ma_result ma_decoder_init_from_file__internal(const ma_decoding_backend_vtable* pVTable, void* pVTableUserData, const char* pFilePath, const ma_decoder_config* pConfig, ma_decoder* pDecoder) -{ - ma_result result; - ma_decoding_backend_config backendConfig; - ma_data_source* pBackend; - - MA_ASSERT(pVTable != NULL); - MA_ASSERT(pConfig != NULL); - MA_ASSERT(pDecoder != NULL); - - if (pVTable->onInitFile == NULL) { - return MA_NOT_IMPLEMENTED; - } - - backendConfig = ma_decoding_backend_config_init(pConfig->format, pConfig->seekPointCount); - - result = pVTable->onInitFile(pVTableUserData, pFilePath, &backendConfig, &pDecoder->allocationCallbacks, &pBackend); - if (result != MA_SUCCESS) { - return result; /* Failed to initialize the backend from this vtable. */ - } - - /* Getting here means we were able to initialize the backend so we can now initialize the decoder. */ - pDecoder->pBackend = pBackend; - pDecoder->pBackendVTable = pVTable; - pDecoder->pBackendUserData = pConfig->pCustomBackendUserData; - - return MA_SUCCESS; -} - -static ma_result ma_decoder_init_from_file_w__internal(const ma_decoding_backend_vtable* pVTable, void* pVTableUserData, const wchar_t* pFilePath, const ma_decoder_config* pConfig, ma_decoder* pDecoder) -{ - ma_result result; - ma_decoding_backend_config backendConfig; - ma_data_source* pBackend; - - MA_ASSERT(pVTable != NULL); - MA_ASSERT(pConfig != NULL); - MA_ASSERT(pDecoder != NULL); - - if (pVTable->onInitFileW == NULL) { - return MA_NOT_IMPLEMENTED; - } - - backendConfig = ma_decoding_backend_config_init(pConfig->format, pConfig->seekPointCount); - - result = pVTable->onInitFileW(pVTableUserData, pFilePath, &backendConfig, &pDecoder->allocationCallbacks, &pBackend); - if (result != MA_SUCCESS) { - return result; /* Failed to initialize the backend from this vtable. */ - } - - /* Getting here means we were able to initialize the backend so we can now initialize the decoder. */ - pDecoder->pBackend = pBackend; - pDecoder->pBackendVTable = pVTable; - pDecoder->pBackendUserData = pConfig->pCustomBackendUserData; - - return MA_SUCCESS; -} - -static ma_result ma_decoder_init_from_memory__internal(const ma_decoding_backend_vtable* pVTable, void* pVTableUserData, const void* pData, size_t dataSize, const ma_decoder_config* pConfig, ma_decoder* pDecoder) -{ - ma_result result; - ma_decoding_backend_config backendConfig; - ma_data_source* pBackend; - - MA_ASSERT(pVTable != NULL); - MA_ASSERT(pConfig != NULL); - MA_ASSERT(pDecoder != NULL); - - if (pVTable->onInitMemory == NULL) { - return MA_NOT_IMPLEMENTED; - } - - backendConfig = ma_decoding_backend_config_init(pConfig->format, pConfig->seekPointCount); - - result = pVTable->onInitMemory(pVTableUserData, pData, dataSize, &backendConfig, &pDecoder->allocationCallbacks, &pBackend); - if (result != MA_SUCCESS) { - return result; /* Failed to initialize the backend from this vtable. */ - } - - /* Getting here means we were able to initialize the backend so we can now initialize the decoder. */ - pDecoder->pBackend = pBackend; - pDecoder->pBackendVTable = pVTable; - pDecoder->pBackendUserData = pConfig->pCustomBackendUserData; - - return MA_SUCCESS; -} - static ma_result ma_decoder_init_custom__internal(const ma_decoder_config* pConfig, ma_decoder* pDecoder) @@ -61481,8 +61053,8 @@ static ma_result ma_decoder_init_custom__internal(const ma_decoder_config* pConf /* The order each backend is listed is what defines the priority. */ for (ivtable = 0; ivtable < pConfig->customBackendCount; ivtable += 1) { const ma_decoding_backend_vtable* pVTable = pConfig->ppCustomBackendVTables[ivtable]; - if (pVTable != NULL) { - result = ma_decoder_init_from_vtable__internal(pVTable, pConfig->pCustomBackendUserData, pConfig, pDecoder); + if (pVTable != NULL && pVTable->onInit != NULL) { + result = ma_decoder_init_from_vtable(pVTable, pConfig->pCustomBackendUserData, pConfig, pDecoder); if (result == MA_SUCCESS) { return MA_SUCCESS; } else { @@ -61501,96 +61073,9 @@ static ma_result ma_decoder_init_custom__internal(const ma_decoder_config* pConf return MA_NO_BACKEND; } -static ma_result ma_decoder_init_custom_from_file__internal(const char* pFilePath, const ma_decoder_config* pConfig, ma_decoder* pDecoder) -{ - ma_result result = MA_NO_BACKEND; - size_t ivtable; - - MA_ASSERT(pConfig != NULL); - MA_ASSERT(pDecoder != NULL); - - if (pConfig->ppCustomBackendVTables == NULL) { - return MA_NO_BACKEND; - } - - /* The order each backend is listed is what defines the priority. */ - for (ivtable = 0; ivtable < pConfig->customBackendCount; ivtable += 1) { - const ma_decoding_backend_vtable* pVTable = pConfig->ppCustomBackendVTables[ivtable]; - if (pVTable != NULL) { - result = ma_decoder_init_from_file__internal(pVTable, pConfig->pCustomBackendUserData, pFilePath, pConfig, pDecoder); - if (result == MA_SUCCESS) { - return MA_SUCCESS; - } - } else { - /* No vtable. */ - } - } - - /* Getting here means we couldn't find a backend. */ - return MA_NO_BACKEND; -} - -static ma_result ma_decoder_init_custom_from_file_w__internal(const wchar_t* pFilePath, const ma_decoder_config* pConfig, ma_decoder* pDecoder) -{ - ma_result result = MA_NO_BACKEND; - size_t ivtable; - - MA_ASSERT(pConfig != NULL); - MA_ASSERT(pDecoder != NULL); - - if (pConfig->ppCustomBackendVTables == NULL) { - return MA_NO_BACKEND; - } - - /* The order each backend is listed is what defines the priority. */ - for (ivtable = 0; ivtable < pConfig->customBackendCount; ivtable += 1) { - const ma_decoding_backend_vtable* pVTable = pConfig->ppCustomBackendVTables[ivtable]; - if (pVTable != NULL) { - result = ma_decoder_init_from_file_w__internal(pVTable, pConfig->pCustomBackendUserData, pFilePath, pConfig, pDecoder); - if (result == MA_SUCCESS) { - return MA_SUCCESS; - } - } else { - /* No vtable. */ - } - } - - /* Getting here means we couldn't find a backend. */ - return MA_NO_BACKEND; -} - -static ma_result ma_decoder_init_custom_from_memory__internal(const void* pData, size_t dataSize, const ma_decoder_config* pConfig, ma_decoder* pDecoder) -{ - ma_result result = MA_NO_BACKEND; - size_t ivtable; - - MA_ASSERT(pConfig != NULL); - MA_ASSERT(pDecoder != NULL); - - if (pConfig->ppCustomBackendVTables == NULL) { - return MA_NO_BACKEND; - } - - /* The order each backend is listed is what defines the priority. */ - for (ivtable = 0; ivtable < pConfig->customBackendCount; ivtable += 1) { - const ma_decoding_backend_vtable* pVTable = pConfig->ppCustomBackendVTables[ivtable]; - if (pVTable != NULL) { - result = ma_decoder_init_from_memory__internal(pVTable, pConfig->pCustomBackendUserData, pData, dataSize, pConfig, pDecoder); - if (result == MA_SUCCESS) { - return MA_SUCCESS; - } - } else { - /* No vtable. */ - } - } - - /* Getting here means we couldn't find a backend. */ - return MA_NO_BACKEND; -} - /* WAV */ -#ifdef ma_dr_wav_h +#ifdef dr_wav_h #define MA_HAS_WAV typedef struct @@ -61602,7 +61087,7 @@ typedef struct void* pReadSeekTellUserData; ma_format format; /* Can be f32, s16 or s32. */ #if !defined(MA_NO_WAV) - ma_dr_wav dr; + drwav dr; #endif } ma_wav; @@ -61656,6 +61141,25 @@ static ma_data_source_vtable g_ma_wav_ds_vtable = #if !defined(MA_NO_WAV) +static drwav_allocation_callbacks drwav_allocation_callbacks_from_miniaudio(const ma_allocation_callbacks* pAllocationCallbacks) +{ + drwav_allocation_callbacks callbacks; + + if (pAllocationCallbacks != NULL) { + callbacks.onMalloc = pAllocationCallbacks->onMalloc; + callbacks.onRealloc = pAllocationCallbacks->onRealloc; + callbacks.onFree = pAllocationCallbacks->onFree; + callbacks.pUserData = pAllocationCallbacks->pUserData; + } else { + callbacks.onMalloc = ma__malloc_default; + callbacks.onRealloc = ma__realloc_default; + callbacks.onFree = ma__free_default; + callbacks.pUserData = NULL; + } + + return callbacks; +} + static size_t ma_wav_dr_callback__read(void* pUserData, void* pBufferOut, size_t bytesToRead) { ma_wav* pWav = (ma_wav*)pUserData; @@ -61670,7 +61174,7 @@ static size_t ma_wav_dr_callback__read(void* pUserData, void* pBufferOut, size_t return bytesRead; } -static ma_bool32 ma_wav_dr_callback__seek(void* pUserData, int offset, ma_dr_wav_seek_origin origin) +static drwav_bool32 ma_wav_dr_callback__seek(void* pUserData, int offset, drwav_seek_origin origin) { ma_wav* pWav = (ma_wav*)pUserData; ma_result result; @@ -61679,7 +61183,7 @@ static ma_bool32 ma_wav_dr_callback__seek(void* pUserData, int offset, ma_dr_wav MA_ASSERT(pWav != NULL); maSeekOrigin = ma_seek_origin_start; - if (origin == ma_dr_wav_seek_origin_current) { + if (origin == drwav_seek_origin_current) { maSeekOrigin = ma_seek_origin_current; } @@ -61721,47 +61225,6 @@ static ma_result ma_wav_init_internal(const ma_decoding_backend_config* pConfig, return MA_SUCCESS; } -static ma_result ma_wav_post_init(ma_wav* pWav) -{ - /* - If an explicit format was not specified, try picking the closest match based on the internal - format. The format needs to be supported by miniaudio. - */ - if (pWav->format == ma_format_unknown) { - switch (pWav->dr.translatedFormatTag) - { - case MA_DR_WAVE_FORMAT_PCM: - { - if (pWav->dr.bitsPerSample == 8) { - pWav->format = ma_format_u8; - } else if (pWav->dr.bitsPerSample == 16) { - pWav->format = ma_format_s16; - } else if (pWav->dr.bitsPerSample == 24) { - pWav->format = ma_format_s24; - } else if (pWav->dr.bitsPerSample == 32) { - pWav->format = ma_format_s32; - } - } break; - - case MA_DR_WAVE_FORMAT_IEEE_FLOAT: - { - if (pWav->dr.bitsPerSample == 32) { - pWav->format = ma_format_f32; - } - } break; - - default: break; - } - - /* Fall back to f32 if we couldn't find anything. */ - if (pWav->format == ma_format_unknown) { - pWav->format = ma_format_f32; - } - } - - return MA_SUCCESS; -} - MA_API ma_result ma_wav_init(ma_read_proc onRead, ma_seek_proc onSeek, ma_tell_proc onTell, void* pReadSeekTellUserData, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_wav* pWav) { ma_result result; @@ -61782,14 +61245,49 @@ MA_API ma_result ma_wav_init(ma_read_proc onRead, ma_seek_proc onSeek, ma_tell_p #if !defined(MA_NO_WAV) { - ma_bool32 wavResult; + drwav_allocation_callbacks wavAllocationCallbacks = drwav_allocation_callbacks_from_miniaudio(pAllocationCallbacks); + drwav_bool32 wavResult; - wavResult = ma_dr_wav_init(&pWav->dr, ma_wav_dr_callback__read, ma_wav_dr_callback__seek, pWav, pAllocationCallbacks); + wavResult = drwav_init(&pWav->dr, ma_wav_dr_callback__read, ma_wav_dr_callback__seek, pWav, &wavAllocationCallbacks); if (wavResult != MA_TRUE) { return MA_INVALID_FILE; } - ma_wav_post_init(pWav); + /* + If an explicit format was not specified, try picking the closest match based on the internal + format. The format needs to be supported by miniaudio. + */ + if (pWav->format == ma_format_unknown) { + switch (pWav->dr.translatedFormatTag) + { + case DR_WAVE_FORMAT_PCM: + { + if (pWav->dr.bitsPerSample == 8) { + pWav->format = ma_format_u8; + } else if (pWav->dr.bitsPerSample == 16) { + pWav->format = ma_format_s16; + } else if (pWav->dr.bitsPerSample == 24) { + pWav->format = ma_format_s24; + } else if (pWav->dr.bitsPerSample == 32) { + pWav->format = ma_format_s32; + } + } break; + + case DR_WAVE_FORMAT_IEEE_FLOAT: + { + if (pWav->dr.bitsPerSample == 32) { + pWav->format = ma_format_f32; + } + } break; + + default: break; + } + + /* Fall back to f32 if we couldn't find anything. */ + if (pWav->format == ma_format_unknown) { + pWav->format = ma_format_f32; + } + } return MA_SUCCESS; } @@ -61813,15 +61311,14 @@ MA_API ma_result ma_wav_init_file(const char* pFilePath, const ma_decoding_backe #if !defined(MA_NO_WAV) { - ma_bool32 wavResult; + drwav_allocation_callbacks wavAllocationCallbacks = drwav_allocation_callbacks_from_miniaudio(pAllocationCallbacks); + drwav_bool32 wavResult; - wavResult = ma_dr_wav_init_file(&pWav->dr, pFilePath, pAllocationCallbacks); + wavResult = drwav_init_file(&pWav->dr, pFilePath, &wavAllocationCallbacks); if (wavResult != MA_TRUE) { return MA_INVALID_FILE; } - ma_wav_post_init(pWav); - return MA_SUCCESS; } #else @@ -61845,15 +61342,14 @@ MA_API ma_result ma_wav_init_file_w(const wchar_t* pFilePath, const ma_decoding_ #if !defined(MA_NO_WAV) { - ma_bool32 wavResult; + drwav_allocation_callbacks wavAllocationCallbacks = drwav_allocation_callbacks_from_miniaudio(pAllocationCallbacks); + drwav_bool32 wavResult; - wavResult = ma_dr_wav_init_file_w(&pWav->dr, pFilePath, pAllocationCallbacks); + wavResult = drwav_init_file_w(&pWav->dr, pFilePath, &wavAllocationCallbacks); if (wavResult != MA_TRUE) { return MA_INVALID_FILE; } - ma_wav_post_init(pWav); - return MA_SUCCESS; } #else @@ -61877,15 +61373,14 @@ MA_API ma_result ma_wav_init_memory(const void* pData, size_t dataSize, const ma #if !defined(MA_NO_WAV) { - ma_bool32 wavResult; + drwav_allocation_callbacks wavAllocationCallbacks = drwav_allocation_callbacks_from_miniaudio(pAllocationCallbacks); + drwav_bool32 wavResult; - wavResult = ma_dr_wav_init_memory(&pWav->dr, pData, dataSize, pAllocationCallbacks); + wavResult = drwav_init_memory(&pWav->dr, pData, dataSize, &wavAllocationCallbacks); if (wavResult != MA_TRUE) { return MA_INVALID_FILE; } - ma_wav_post_init(pWav); - return MA_SUCCESS; } #else @@ -61909,7 +61404,7 @@ MA_API void ma_wav_uninit(ma_wav* pWav, const ma_allocation_callbacks* pAllocati #if !defined(MA_NO_WAV) { - ma_dr_wav_uninit(&pWav->dr); + drwav_uninit(&pWav->dr); } #else { @@ -61948,28 +61443,28 @@ MA_API ma_result ma_wav_read_pcm_frames(ma_wav* pWav, void* pFramesOut, ma_uint6 { case ma_format_f32: { - totalFramesRead = ma_dr_wav_read_pcm_frames_f32(&pWav->dr, frameCount, (float*)pFramesOut); + totalFramesRead = drwav_read_pcm_frames_f32(&pWav->dr, frameCount, (float*)pFramesOut); } break; case ma_format_s16: { - totalFramesRead = ma_dr_wav_read_pcm_frames_s16(&pWav->dr, frameCount, (ma_int16*)pFramesOut); + totalFramesRead = drwav_read_pcm_frames_s16(&pWav->dr, frameCount, (drwav_int16*)pFramesOut); } break; case ma_format_s32: { - totalFramesRead = ma_dr_wav_read_pcm_frames_s32(&pWav->dr, frameCount, (ma_int32*)pFramesOut); + totalFramesRead = drwav_read_pcm_frames_s32(&pWav->dr, frameCount, (drwav_int32*)pFramesOut); } break; /* Fallback to a raw read. */ case ma_format_unknown: return MA_INVALID_OPERATION; /* <-- this should never be hit because initialization would just fall back to a supported format. */ default: { - totalFramesRead = ma_dr_wav_read_pcm_frames(&pWav->dr, frameCount, pFramesOut); + totalFramesRead = drwav_read_pcm_frames(&pWav->dr, frameCount, pFramesOut); } break; } - /* In the future we'll update ma_dr_wav to return MA_AT_END for us. */ + /* In the future we'll update dr_wav to return MA_AT_END for us. */ if (totalFramesRead == 0) { result = MA_AT_END; } @@ -62006,10 +61501,10 @@ MA_API ma_result ma_wav_seek_to_pcm_frame(ma_wav* pWav, ma_uint64 frameIndex) #if !defined(MA_NO_WAV) { - ma_bool32 wavResult; + drwav_bool32 wavResult; - wavResult = ma_dr_wav_seek_to_pcm_frame(&pWav->dr, frameIndex); - if (wavResult != MA_TRUE) { + wavResult = drwav_seek_to_pcm_frame(&pWav->dr, frameIndex); + if (wavResult != DRWAV_TRUE) { return MA_ERROR; } @@ -62090,9 +61585,9 @@ MA_API ma_result ma_wav_get_cursor_in_pcm_frames(ma_wav* pWav, ma_uint64* pCurso #if !defined(MA_NO_WAV) { - ma_result wavResult = ma_dr_wav_get_cursor_in_pcm_frames(&pWav->dr, pCursor); - if (wavResult != MA_SUCCESS) { - return (ma_result)wavResult; /* ma_dr_wav result codes map to miniaudio's. */ + drwav_result wavResult = drwav_get_cursor_in_pcm_frames(&pWav->dr, pCursor); + if (wavResult != DRWAV_SUCCESS) { + return (ma_result)wavResult; /* dr_wav result codes map to miniaudio's. */ } return MA_SUCCESS; @@ -62120,9 +61615,9 @@ MA_API ma_result ma_wav_get_length_in_pcm_frames(ma_wav* pWav, ma_uint64* pLengt #if !defined(MA_NO_WAV) { - ma_result wavResult = ma_dr_wav_get_length_in_pcm_frames(&pWav->dr, pLength); - if (wavResult != MA_SUCCESS) { - return (ma_result)wavResult; /* ma_dr_wav result codes map to miniaudio's. */ + drwav_result wavResult = drwav_get_length_in_pcm_frames(&pWav->dr, pLength); + if (wavResult != DRWAV_SUCCESS) { + return (ma_result)wavResult; /* dr_wav result codes map to miniaudio's. */ } return MA_SUCCESS; @@ -62254,27 +61749,12 @@ static ma_decoding_backend_vtable g_ma_decoding_backend_vtable_wav = static ma_result ma_decoder_init_wav__internal(const ma_decoder_config* pConfig, ma_decoder* pDecoder) { - return ma_decoder_init_from_vtable__internal(&g_ma_decoding_backend_vtable_wav, NULL, pConfig, pDecoder); + return ma_decoder_init_from_vtable(&g_ma_decoding_backend_vtable_wav, NULL, pConfig, pDecoder); } - -static ma_result ma_decoder_init_wav_from_file__internal(const char* pFilePath, const ma_decoder_config* pConfig, ma_decoder* pDecoder) -{ - return ma_decoder_init_from_file__internal(&g_ma_decoding_backend_vtable_wav, NULL, pFilePath, pConfig, pDecoder); -} - -static ma_result ma_decoder_init_wav_from_file_w__internal(const wchar_t* pFilePath, const ma_decoder_config* pConfig, ma_decoder* pDecoder) -{ - return ma_decoder_init_from_file_w__internal(&g_ma_decoding_backend_vtable_wav, NULL, pFilePath, pConfig, pDecoder); -} - -static ma_result ma_decoder_init_wav_from_memory__internal(const void* pData, size_t dataSize, const ma_decoder_config* pConfig, ma_decoder* pDecoder) -{ - return ma_decoder_init_from_memory__internal(&g_ma_decoding_backend_vtable_wav, NULL, pData, dataSize, pConfig, pDecoder); -} -#endif /* ma_dr_wav_h */ +#endif /* dr_wav_h */ /* FLAC */ -#ifdef ma_dr_flac_h +#ifdef dr_flac_h #define MA_HAS_FLAC typedef struct @@ -62286,7 +61766,7 @@ typedef struct void* pReadSeekTellUserData; ma_format format; /* Can be f32, s16 or s32. */ #if !defined(MA_NO_FLAC) - ma_dr_flac* dr; + drflac* dr; #endif } ma_flac; @@ -62340,6 +61820,25 @@ static ma_data_source_vtable g_ma_flac_ds_vtable = #if !defined(MA_NO_FLAC) +static drflac_allocation_callbacks drflac_allocation_callbacks_from_miniaudio(const ma_allocation_callbacks* pAllocationCallbacks) +{ + drflac_allocation_callbacks callbacks; + + if (pAllocationCallbacks != NULL) { + callbacks.onMalloc = pAllocationCallbacks->onMalloc; + callbacks.onRealloc = pAllocationCallbacks->onRealloc; + callbacks.onFree = pAllocationCallbacks->onFree; + callbacks.pUserData = pAllocationCallbacks->pUserData; + } else { + callbacks.onMalloc = ma__malloc_default; + callbacks.onRealloc = ma__realloc_default; + callbacks.onFree = ma__free_default; + callbacks.pUserData = NULL; + } + + return callbacks; +} + static size_t ma_flac_dr_callback__read(void* pUserData, void* pBufferOut, size_t bytesToRead) { ma_flac* pFlac = (ma_flac*)pUserData; @@ -62354,7 +61853,7 @@ static size_t ma_flac_dr_callback__read(void* pUserData, void* pBufferOut, size_ return bytesRead; } -static ma_bool32 ma_flac_dr_callback__seek(void* pUserData, int offset, ma_dr_flac_seek_origin origin) +static drflac_bool32 ma_flac_dr_callback__seek(void* pUserData, int offset, drflac_seek_origin origin) { ma_flac* pFlac = (ma_flac*)pUserData; ma_result result; @@ -62363,7 +61862,7 @@ static ma_bool32 ma_flac_dr_callback__seek(void* pUserData, int offset, ma_dr_fl MA_ASSERT(pFlac != NULL); maSeekOrigin = ma_seek_origin_start; - if (origin == ma_dr_flac_seek_origin_current) { + if (origin == drflac_seek_origin_current) { maSeekOrigin = ma_seek_origin_current; } @@ -62425,7 +61924,9 @@ MA_API ma_result ma_flac_init(ma_read_proc onRead, ma_seek_proc onSeek, ma_tell_ #if !defined(MA_NO_FLAC) { - pFlac->dr = ma_dr_flac_open(ma_flac_dr_callback__read, ma_flac_dr_callback__seek, pFlac, pAllocationCallbacks); + drflac_allocation_callbacks flacAllocationCallbacks = drflac_allocation_callbacks_from_miniaudio(pAllocationCallbacks); + + pFlac->dr = drflac_open(ma_flac_dr_callback__read, ma_flac_dr_callback__seek, pFlac, &flacAllocationCallbacks); if (pFlac->dr == NULL) { return MA_INVALID_FILE; } @@ -62452,7 +61953,9 @@ MA_API ma_result ma_flac_init_file(const char* pFilePath, const ma_decoding_back #if !defined(MA_NO_FLAC) { - pFlac->dr = ma_dr_flac_open_file(pFilePath, pAllocationCallbacks); + drflac_allocation_callbacks flacAllocationCallbacks = drflac_allocation_callbacks_from_miniaudio(pAllocationCallbacks); + + pFlac->dr = drflac_open_file(pFilePath, &flacAllocationCallbacks); if (pFlac->dr == NULL) { return MA_INVALID_FILE; } @@ -62480,7 +61983,9 @@ MA_API ma_result ma_flac_init_file_w(const wchar_t* pFilePath, const ma_decoding #if !defined(MA_NO_FLAC) { - pFlac->dr = ma_dr_flac_open_file_w(pFilePath, pAllocationCallbacks); + drflac_allocation_callbacks flacAllocationCallbacks = drflac_allocation_callbacks_from_miniaudio(pAllocationCallbacks); + + pFlac->dr = drflac_open_file_w(pFilePath, &flacAllocationCallbacks); if (pFlac->dr == NULL) { return MA_INVALID_FILE; } @@ -62508,7 +62013,9 @@ MA_API ma_result ma_flac_init_memory(const void* pData, size_t dataSize, const m #if !defined(MA_NO_FLAC) { - pFlac->dr = ma_dr_flac_open_memory(pData, dataSize, pAllocationCallbacks); + drflac_allocation_callbacks flacAllocationCallbacks = drflac_allocation_callbacks_from_miniaudio(pAllocationCallbacks); + + pFlac->dr = drflac_open_memory(pData, dataSize, &flacAllocationCallbacks); if (pFlac->dr == NULL) { return MA_INVALID_FILE; } @@ -62536,7 +62043,7 @@ MA_API void ma_flac_uninit(ma_flac* pFlac, const ma_allocation_callbacks* pAlloc #if !defined(MA_NO_FLAC) { - ma_dr_flac_close(pFlac->dr); + drflac_close(pFlac->dr); } #else { @@ -62575,17 +62082,17 @@ MA_API ma_result ma_flac_read_pcm_frames(ma_flac* pFlac, void* pFramesOut, ma_ui { case ma_format_f32: { - totalFramesRead = ma_dr_flac_read_pcm_frames_f32(pFlac->dr, frameCount, (float*)pFramesOut); + totalFramesRead = drflac_read_pcm_frames_f32(pFlac->dr, frameCount, (float*)pFramesOut); } break; case ma_format_s16: { - totalFramesRead = ma_dr_flac_read_pcm_frames_s16(pFlac->dr, frameCount, (ma_int16*)pFramesOut); + totalFramesRead = drflac_read_pcm_frames_s16(pFlac->dr, frameCount, (drflac_int16*)pFramesOut); } break; case ma_format_s32: { - totalFramesRead = ma_dr_flac_read_pcm_frames_s32(pFlac->dr, frameCount, (ma_int32*)pFramesOut); + totalFramesRead = drflac_read_pcm_frames_s32(pFlac->dr, frameCount, (drflac_int32*)pFramesOut); } break; case ma_format_u8: @@ -62597,7 +62104,7 @@ MA_API ma_result ma_flac_read_pcm_frames(ma_flac* pFlac, void* pFramesOut, ma_ui }; } - /* In the future we'll update ma_dr_flac to return MA_AT_END for us. */ + /* In the future we'll update dr_flac to return MA_AT_END for us. */ if (totalFramesRead == 0) { result = MA_AT_END; } @@ -62634,10 +62141,10 @@ MA_API ma_result ma_flac_seek_to_pcm_frame(ma_flac* pFlac, ma_uint64 frameIndex) #if !defined(MA_NO_FLAC) { - ma_bool32 flacResult; + drflac_bool32 flacResult; - flacResult = ma_dr_flac_seek_to_pcm_frame(pFlac->dr, frameIndex); - if (flacResult != MA_TRUE) { + flacResult = drflac_seek_to_pcm_frame(pFlac->dr, frameIndex); + if (flacResult != DRFLAC_TRUE) { return MA_ERROR; } @@ -62876,27 +62383,12 @@ static ma_decoding_backend_vtable g_ma_decoding_backend_vtable_flac = static ma_result ma_decoder_init_flac__internal(const ma_decoder_config* pConfig, ma_decoder* pDecoder) { - return ma_decoder_init_from_vtable__internal(&g_ma_decoding_backend_vtable_flac, NULL, pConfig, pDecoder); -} - -static ma_result ma_decoder_init_flac_from_file__internal(const char* pFilePath, const ma_decoder_config* pConfig, ma_decoder* pDecoder) -{ - return ma_decoder_init_from_file__internal(&g_ma_decoding_backend_vtable_flac, NULL, pFilePath, pConfig, pDecoder); -} - -static ma_result ma_decoder_init_flac_from_file_w__internal(const wchar_t* pFilePath, const ma_decoder_config* pConfig, ma_decoder* pDecoder) -{ - return ma_decoder_init_from_file_w__internal(&g_ma_decoding_backend_vtable_flac, NULL, pFilePath, pConfig, pDecoder); -} - -static ma_result ma_decoder_init_flac_from_memory__internal(const void* pData, size_t dataSize, const ma_decoder_config* pConfig, ma_decoder* pDecoder) -{ - return ma_decoder_init_from_memory__internal(&g_ma_decoding_backend_vtable_flac, NULL, pData, dataSize, pConfig, pDecoder); + return ma_decoder_init_from_vtable(&g_ma_decoding_backend_vtable_flac, NULL, pConfig, pDecoder); } -#endif /* ma_dr_flac_h */ +#endif /* dr_flac_h */ /* MP3 */ -#ifdef ma_dr_mp3_h +#ifdef dr_mp3_h #define MA_HAS_MP3 typedef struct @@ -62908,9 +62400,9 @@ typedef struct void* pReadSeekTellUserData; ma_format format; /* Can be f32 or s16. */ #if !defined(MA_NO_MP3) - ma_dr_mp3 dr; - ma_uint32 seekPointCount; - ma_dr_mp3_seek_point* pSeekPoints; /* Only used if seek table generation is used. */ + drmp3 dr; + drmp3_uint32 seekPointCount; + drmp3_seek_point* pSeekPoints; /* Only used if seek table generation is used. */ #endif } ma_mp3; @@ -62964,6 +62456,25 @@ static ma_data_source_vtable g_ma_mp3_ds_vtable = #if !defined(MA_NO_MP3) +static drmp3_allocation_callbacks drmp3_allocation_callbacks_from_miniaudio(const ma_allocation_callbacks* pAllocationCallbacks) +{ + drmp3_allocation_callbacks callbacks; + + if (pAllocationCallbacks != NULL) { + callbacks.onMalloc = pAllocationCallbacks->onMalloc; + callbacks.onRealloc = pAllocationCallbacks->onRealloc; + callbacks.onFree = pAllocationCallbacks->onFree; + callbacks.pUserData = pAllocationCallbacks->pUserData; + } else { + callbacks.onMalloc = ma__malloc_default; + callbacks.onRealloc = ma__realloc_default; + callbacks.onFree = ma__free_default; + callbacks.pUserData = NULL; + } + + return callbacks; +} + static size_t ma_mp3_dr_callback__read(void* pUserData, void* pBufferOut, size_t bytesToRead) { ma_mp3* pMP3 = (ma_mp3*)pUserData; @@ -62978,7 +62489,7 @@ static size_t ma_mp3_dr_callback__read(void* pUserData, void* pBufferOut, size_t return bytesRead; } -static ma_bool32 ma_mp3_dr_callback__seek(void* pUserData, int offset, ma_dr_mp3_seek_origin origin) +static drmp3_bool32 ma_mp3_dr_callback__seek(void* pUserData, int offset, drmp3_seek_origin origin) { ma_mp3* pMP3 = (ma_mp3*)pUserData; ma_result result; @@ -62987,7 +62498,7 @@ static ma_bool32 ma_mp3_dr_callback__seek(void* pUserData, int offset, ma_dr_mp3 MA_ASSERT(pMP3 != NULL); maSeekOrigin = ma_seek_origin_start; - if (origin == ma_dr_mp3_seek_origin_current) { + if (origin == drmp3_seek_origin_current) { maSeekOrigin = ma_seek_origin_current; } @@ -63031,28 +62542,28 @@ static ma_result ma_mp3_init_internal(const ma_decoding_backend_config* pConfig, static ma_result ma_mp3_generate_seek_table(ma_mp3* pMP3, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks) { - ma_bool32 mp3Result; - ma_uint32 seekPointCount = 0; - ma_dr_mp3_seek_point* pSeekPoints = NULL; + drmp3_bool32 mp3Result; + drmp3_uint32 seekPointCount = 0; + drmp3_seek_point* pSeekPoints = NULL; MA_ASSERT(pMP3 != NULL); MA_ASSERT(pConfig != NULL); seekPointCount = pConfig->seekPointCount; if (seekPointCount > 0) { - pSeekPoints = (ma_dr_mp3_seek_point*)ma_malloc(sizeof(*pMP3->pSeekPoints) * seekPointCount, pAllocationCallbacks); + pSeekPoints = (drmp3_seek_point*)ma_malloc(sizeof(*pMP3->pSeekPoints) * seekPointCount, pAllocationCallbacks); if (pSeekPoints == NULL) { return MA_OUT_OF_MEMORY; } } - mp3Result = ma_dr_mp3_calculate_seek_points(&pMP3->dr, &seekPointCount, pSeekPoints); + mp3Result = drmp3_calculate_seek_points(&pMP3->dr, &seekPointCount, pSeekPoints); if (mp3Result != MA_TRUE) { ma_free(pSeekPoints, pAllocationCallbacks); return MA_ERROR; } - mp3Result = ma_dr_mp3_bind_seek_table(&pMP3->dr, seekPointCount, pSeekPoints); + mp3Result = drmp3_bind_seek_table(&pMP3->dr, seekPointCount, pSeekPoints); if (mp3Result != MA_TRUE) { ma_free(pSeekPoints, pAllocationCallbacks); return MA_ERROR; @@ -63064,18 +62575,6 @@ static ma_result ma_mp3_generate_seek_table(ma_mp3* pMP3, const ma_decoding_back return MA_SUCCESS; } -static ma_result ma_mp3_post_init(ma_mp3* pMP3, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks) -{ - ma_result result; - - result = ma_mp3_generate_seek_table(pMP3, pConfig, pAllocationCallbacks); - if (result != MA_SUCCESS) { - return result; - } - - return MA_SUCCESS; -} - MA_API ma_result ma_mp3_init(ma_read_proc onRead, ma_seek_proc onSeek, ma_tell_proc onTell, void* pReadSeekTellUserData, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_mp3* pMP3) { ma_result result; @@ -63096,14 +62595,15 @@ MA_API ma_result ma_mp3_init(ma_read_proc onRead, ma_seek_proc onSeek, ma_tell_p #if !defined(MA_NO_MP3) { - ma_bool32 mp3Result; + drmp3_allocation_callbacks mp3AllocationCallbacks = drmp3_allocation_callbacks_from_miniaudio(pAllocationCallbacks); + drmp3_bool32 mp3Result; - mp3Result = ma_dr_mp3_init(&pMP3->dr, ma_mp3_dr_callback__read, ma_mp3_dr_callback__seek, pMP3, pAllocationCallbacks); + mp3Result = drmp3_init(&pMP3->dr, ma_mp3_dr_callback__read, ma_mp3_dr_callback__seek, pMP3, &mp3AllocationCallbacks); if (mp3Result != MA_TRUE) { return MA_INVALID_FILE; } - ma_mp3_post_init(pMP3, pConfig, pAllocationCallbacks); + ma_mp3_generate_seek_table(pMP3, pConfig, pAllocationCallbacks); return MA_SUCCESS; } @@ -63127,14 +62627,15 @@ MA_API ma_result ma_mp3_init_file(const char* pFilePath, const ma_decoding_backe #if !defined(MA_NO_MP3) { - ma_bool32 mp3Result; + drmp3_allocation_callbacks mp3AllocationCallbacks = drmp3_allocation_callbacks_from_miniaudio(pAllocationCallbacks); + drmp3_bool32 mp3Result; - mp3Result = ma_dr_mp3_init_file(&pMP3->dr, pFilePath, pAllocationCallbacks); + mp3Result = drmp3_init_file(&pMP3->dr, pFilePath, &mp3AllocationCallbacks); if (mp3Result != MA_TRUE) { return MA_INVALID_FILE; } - ma_mp3_post_init(pMP3, pConfig, pAllocationCallbacks); + ma_mp3_generate_seek_table(pMP3, pConfig, pAllocationCallbacks); return MA_SUCCESS; } @@ -63159,14 +62660,15 @@ MA_API ma_result ma_mp3_init_file_w(const wchar_t* pFilePath, const ma_decoding_ #if !defined(MA_NO_MP3) { - ma_bool32 mp3Result; + drmp3_allocation_callbacks mp3AllocationCallbacks = drmp3_allocation_callbacks_from_miniaudio(pAllocationCallbacks); + drmp3_bool32 mp3Result; - mp3Result = ma_dr_mp3_init_file_w(&pMP3->dr, pFilePath, pAllocationCallbacks); + mp3Result = drmp3_init_file_w(&pMP3->dr, pFilePath, &mp3AllocationCallbacks); if (mp3Result != MA_TRUE) { return MA_INVALID_FILE; } - ma_mp3_post_init(pMP3, pConfig, pAllocationCallbacks); + ma_mp3_generate_seek_table(pMP3, pConfig, pAllocationCallbacks); return MA_SUCCESS; } @@ -63191,14 +62693,15 @@ MA_API ma_result ma_mp3_init_memory(const void* pData, size_t dataSize, const ma #if !defined(MA_NO_MP3) { - ma_bool32 mp3Result; + drmp3_allocation_callbacks mp3AllocationCallbacks = drmp3_allocation_callbacks_from_miniaudio(pAllocationCallbacks); + drmp3_bool32 mp3Result; - mp3Result = ma_dr_mp3_init_memory(&pMP3->dr, pData, dataSize, pAllocationCallbacks); + mp3Result = drmp3_init_memory(&pMP3->dr, pData, dataSize, &mp3AllocationCallbacks); if (mp3Result != MA_TRUE) { return MA_INVALID_FILE; } - ma_mp3_post_init(pMP3, pConfig, pAllocationCallbacks); + ma_mp3_generate_seek_table(pMP3, pConfig, pAllocationCallbacks); return MA_SUCCESS; } @@ -63221,7 +62724,7 @@ MA_API void ma_mp3_uninit(ma_mp3* pMP3, const ma_allocation_callbacks* pAllocati #if !defined(MA_NO_MP3) { - ma_dr_mp3_uninit(&pMP3->dr); + drmp3_uninit(&pMP3->dr); } #else { @@ -63263,12 +62766,12 @@ MA_API ma_result ma_mp3_read_pcm_frames(ma_mp3* pMP3, void* pFramesOut, ma_uint6 { case ma_format_f32: { - totalFramesRead = ma_dr_mp3_read_pcm_frames_f32(&pMP3->dr, frameCount, (float*)pFramesOut); + totalFramesRead = drmp3_read_pcm_frames_f32(&pMP3->dr, frameCount, (float*)pFramesOut); } break; case ma_format_s16: { - totalFramesRead = ma_dr_mp3_read_pcm_frames_s16(&pMP3->dr, frameCount, (ma_int16*)pFramesOut); + totalFramesRead = drmp3_read_pcm_frames_s16(&pMP3->dr, frameCount, (drmp3_int16*)pFramesOut); } break; case ma_format_u8: @@ -63281,7 +62784,7 @@ MA_API ma_result ma_mp3_read_pcm_frames(ma_mp3* pMP3, void* pFramesOut, ma_uint6 }; } - /* In the future we'll update ma_dr_mp3 to return MA_AT_END for us. */ + /* In the future we'll update dr_mp3 to return MA_AT_END for us. */ if (totalFramesRead == 0) { result = MA_AT_END; } @@ -63314,10 +62817,10 @@ MA_API ma_result ma_mp3_seek_to_pcm_frame(ma_mp3* pMP3, ma_uint64 frameIndex) #if !defined(MA_NO_MP3) { - ma_bool32 mp3Result; + drmp3_bool32 mp3Result; - mp3Result = ma_dr_mp3_seek_to_pcm_frame(&pMP3->dr, frameIndex); - if (mp3Result != MA_TRUE) { + mp3Result = drmp3_seek_to_pcm_frame(&pMP3->dr, frameIndex); + if (mp3Result != DRMP3_TRUE) { return MA_ERROR; } @@ -63425,7 +62928,7 @@ MA_API ma_result ma_mp3_get_length_in_pcm_frames(ma_mp3* pMP3, ma_uint64* pLengt #if !defined(MA_NO_MP3) { - *pLength = ma_dr_mp3_get_pcm_frame_count(&pMP3->dr); + *pLength = drmp3_get_pcm_frame_count(&pMP3->dr); return MA_SUCCESS; } @@ -63556,24 +63059,9 @@ static ma_decoding_backend_vtable g_ma_decoding_backend_vtable_mp3 = static ma_result ma_decoder_init_mp3__internal(const ma_decoder_config* pConfig, ma_decoder* pDecoder) { - return ma_decoder_init_from_vtable__internal(&g_ma_decoding_backend_vtable_mp3, NULL, pConfig, pDecoder); -} - -static ma_result ma_decoder_init_mp3_from_file__internal(const char* pFilePath, const ma_decoder_config* pConfig, ma_decoder* pDecoder) -{ - return ma_decoder_init_from_file__internal(&g_ma_decoding_backend_vtable_mp3, NULL, pFilePath, pConfig, pDecoder); -} - -static ma_result ma_decoder_init_mp3_from_file_w__internal(const wchar_t* pFilePath, const ma_decoder_config* pConfig, ma_decoder* pDecoder) -{ - return ma_decoder_init_from_file_w__internal(&g_ma_decoding_backend_vtable_mp3, NULL, pFilePath, pConfig, pDecoder); -} - -static ma_result ma_decoder_init_mp3_from_memory__internal(const void* pData, size_t dataSize, const ma_decoder_config* pConfig, ma_decoder* pDecoder) -{ - return ma_decoder_init_from_memory__internal(&g_ma_decoding_backend_vtable_mp3, NULL, pData, dataSize, pConfig, pDecoder); + return ma_decoder_init_from_vtable(&g_ma_decoding_backend_vtable_mp3, NULL, pConfig, pDecoder); } -#endif /* ma_dr_mp3_h */ +#endif /* dr_mp3_h */ /* Vorbis */ #ifdef STB_VORBIS_INCLUDE_STB_VORBIS_H @@ -63796,7 +63284,7 @@ MA_API ma_result ma_stbvorbis_init(ma_read_proc onRead, ma_seek_proc onSeek, ma_ #if !defined(MA_NO_VORBIS) { /* - stb_vorbis lacks a callback based API for its pulling API which means we're stuck with the + stb_vorbis lacks a callback based API for it's pulling API which means we're stuck with the pushing API. In order for us to be able to successfully initialize the decoder we need to supply it with enough data. We need to keep loading data until we have enough. */ @@ -63877,7 +63365,7 @@ MA_API ma_result ma_stbvorbis_init_memory(const void* pData, size_t dataSize, co { (void)pAllocationCallbacks; - /* stb_vorbis uses an int as its size specifier, restricting it to 32-bit even on 64-bit systems. *sigh*. */ + /* stb_vorbis uses an int as it's size specifier, restricting it to 32-bit even on 64-bit systems. *sigh*. */ if (dataSize > INT_MAX) { return MA_TOO_BIG; } @@ -63967,7 +63455,7 @@ MA_API ma_result ma_stbvorbis_read_pcm_frames(ma_stbvorbis* pVorbis, void* pFram /* The first thing to do is read from any already-cached frames. */ ma_uint32 framesToReadFromCache = (ma_uint32)ma_min(pVorbis->push.framesRemaining, (frameCount - totalFramesRead)); /* Safe cast because pVorbis->framesRemaining is 32-bit. */ - /* The output pointer can be null in which case we just treat it as a seek. */ + /* The output pointer can be null in which case we just treate it as a seek. */ if (pFramesOut != NULL) { ma_uint64 iFrame; for (iFrame = 0; iFrame < framesToReadFromCache; iFrame += 1) { @@ -64041,7 +63529,7 @@ MA_API ma_result ma_stbvorbis_read_pcm_frames(ma_stbvorbis* pVorbis, void* pFram } } - /* If we don't have a success code at this point it means we've encountered an error or the end of the file has been reached (probably the latter). */ + /* If we don't have a success code at this point it means we've encounted an error or the end of the file has been reached (probably the latter). */ if (result != MA_SUCCESS) { break; } @@ -64153,6 +63641,8 @@ MA_API ma_result ma_stbvorbis_seek_to_pcm_frame(ma_stbvorbis* pVorbis, ma_uint64 } result = ma_stbvorbis_read_pcm_frames(pVorbis, buffer, framesToRead, &framesRead); + pVorbis->cursor += framesRead; + if (result != MA_SUCCESS) { return result; } @@ -64388,22 +63878,7 @@ static ma_decoding_backend_vtable g_ma_decoding_backend_vtable_stbvorbis = static ma_result ma_decoder_init_vorbis__internal(const ma_decoder_config* pConfig, ma_decoder* pDecoder) { - return ma_decoder_init_from_vtable__internal(&g_ma_decoding_backend_vtable_stbvorbis, NULL, pConfig, pDecoder); -} - -static ma_result ma_decoder_init_vorbis_from_file__internal(const char* pFilePath, const ma_decoder_config* pConfig, ma_decoder* pDecoder) -{ - return ma_decoder_init_from_file__internal(&g_ma_decoding_backend_vtable_stbvorbis, NULL, pFilePath, pConfig, pDecoder); -} - -static ma_result ma_decoder_init_vorbis_from_file_w__internal(const wchar_t* pFilePath, const ma_decoder_config* pConfig, ma_decoder* pDecoder) -{ - return ma_decoder_init_from_file_w__internal(&g_ma_decoding_backend_vtable_stbvorbis, NULL, pFilePath, pConfig, pDecoder); -} - -static ma_result ma_decoder_init_vorbis_from_memory__internal(const void* pData, size_t dataSize, const ma_decoder_config* pConfig, ma_decoder* pDecoder) -{ - return ma_decoder_init_from_memory__internal(&g_ma_decoding_backend_vtable_stbvorbis, NULL, pData, dataSize, pConfig, pDecoder); + return ma_decoder_init_from_vtable(&g_ma_decoding_backend_vtable_stbvorbis, NULL, pConfig, pDecoder); } #endif /* STB_VORBIS_INCLUDE_STB_VORBIS_H */ @@ -64470,6 +63945,10 @@ static ma_result ma_decoder__preinit(ma_decoder_read_proc onRead, ma_decoder_see MA_ZERO_OBJECT(pDecoder); + if (onRead == NULL || onSeek == NULL) { + return MA_INVALID_ARGS; + } + dataSourceConfig = ma_data_source_config_init(); dataSourceConfig.vtable = &g_ma_decoder_data_source_vtable; @@ -64713,7 +64192,7 @@ static ma_result ma_decoder__on_tell_memory(ma_decoder* pDecoder, ma_int64* pCur return MA_SUCCESS; } -static ma_result ma_decoder__preinit_memory_wrapper(const void* pData, size_t dataSize, const ma_decoder_config* pConfig, ma_decoder* pDecoder) +static ma_result ma_decoder__preinit_memory(const void* pData, size_t dataSize, const ma_decoder_config* pConfig, ma_decoder* pDecoder) { ma_result result = ma_decoder__preinit(ma_decoder__on_read_memory, ma_decoder__on_seek_memory, ma_decoder__on_tell_memory, NULL, pConfig, pDecoder); if (result != MA_SUCCESS) { @@ -64734,128 +64213,25 @@ static ma_result ma_decoder__preinit_memory_wrapper(const void* pData, size_t da MA_API ma_result ma_decoder_init_memory(const void* pData, size_t dataSize, const ma_decoder_config* pConfig, ma_decoder* pDecoder) { - ma_result result; ma_decoder_config config; + ma_result result; - config = ma_decoder_config_init_copy(pConfig); + config = ma_decoder_config_init_copy(pConfig); /* Make sure the config is not NULL. */ - result = ma_decoder__preinit(NULL, NULL, NULL, NULL, &config, pDecoder); + result = ma_decoder__preinit_memory(pData, dataSize, &config, pDecoder); if (result != MA_SUCCESS) { return result; } - if (pData == NULL || dataSize == 0) { - return MA_INVALID_ARGS; - } - - /* If the backend has support for loading from a file path we'll want to use that. If that all fails we'll fall back to the VFS path. */ - result = MA_NO_BACKEND; - - if (config.encodingFormat != ma_encoding_format_unknown) { - #ifdef MA_HAS_WAV - if (config.encodingFormat == ma_encoding_format_wav) { - result = ma_decoder_init_wav_from_memory__internal(pData, dataSize, &config, pDecoder); - } - #endif - #ifdef MA_HAS_FLAC - if (config.encodingFormat == ma_encoding_format_flac) { - result = ma_decoder_init_flac_from_memory__internal(pData, dataSize, &config, pDecoder); - } - #endif - #ifdef MA_HAS_MP3 - if (config.encodingFormat == ma_encoding_format_mp3) { - result = ma_decoder_init_mp3_from_memory__internal(pData, dataSize, &config, pDecoder); - } - #endif - #ifdef MA_HAS_VORBIS - if (config.encodingFormat == ma_encoding_format_vorbis) { - result = ma_decoder_init_vorbis_from_memory__internal(pData, dataSize, &config, pDecoder); - } - #endif - } - - if (result != MA_SUCCESS) { - /* Getting here means we weren't able to initialize a decoder of a specific encoding format. */ - - /* - We use trial and error to open a decoder. We prioritize custom decoders so that if they - implement the same encoding format they take priority over the built-in decoders. - */ - result = ma_decoder_init_custom_from_memory__internal(pData, dataSize, &config, pDecoder); - - /* - If we get to this point and we still haven't found a decoder, and the caller has requested a - specific encoding format, there's no hope for it. Abort. - */ - if (result != MA_SUCCESS && config.encodingFormat != ma_encoding_format_unknown) { - return MA_NO_BACKEND; - } - - /* Use trial and error for stock decoders. */ - if (result != MA_SUCCESS) { - #ifdef MA_HAS_WAV - if (result != MA_SUCCESS) { - result = ma_decoder_init_wav_from_memory__internal(pData, dataSize, &config, pDecoder); - } - #endif - #ifdef MA_HAS_FLAC - if (result != MA_SUCCESS) { - result = ma_decoder_init_flac_from_memory__internal(pData, dataSize, &config, pDecoder); - } - #endif - #ifdef MA_HAS_MP3 - if (result != MA_SUCCESS) { - result = ma_decoder_init_mp3_from_memory__internal(pData, dataSize, &config, pDecoder); - } - #endif - #ifdef MA_HAS_VORBIS - if (result != MA_SUCCESS) { - result = ma_decoder_init_vorbis_from_memory__internal(pData, dataSize, &config, pDecoder); - } - #endif - } - } - - /* - If at this point we still haven't successfully initialized the decoder it most likely means - the backend doesn't have an implementation for loading from a file path. We'll try using - miniaudio's built-in file IO for loading file. - */ - if (result == MA_SUCCESS) { - /* Initialization was successful. Finish up. */ - result = ma_decoder__postinit(&config, pDecoder); - if (result != MA_SUCCESS) { - /* - The backend was initialized successfully, but for some reason post-initialization failed. This is most likely - due to an out of memory error. We're going to abort with an error here and not try to recover. - */ - if (pDecoder->pBackendVTable != NULL && pDecoder->pBackendVTable->onUninit != NULL) { - pDecoder->pBackendVTable->onUninit(pDecoder->pBackendUserData, &pDecoder->pBackend, &pDecoder->allocationCallbacks); - } - - return result; - } - } else { - /* Probably no implementation for loading from a block of memory. Use miniaudio's abstraction instead. */ - result = ma_decoder__preinit_memory_wrapper(pData, dataSize, &config, pDecoder); - if (result != MA_SUCCESS) { - return result; - } - - result = ma_decoder_init__internal(ma_decoder__on_read_memory, ma_decoder__on_seek_memory, NULL, &config, pDecoder); - if (result != MA_SUCCESS) { - return result; - } - } - - return MA_SUCCESS; + return ma_decoder_init__internal(ma_decoder__on_read_memory, ma_decoder__on_seek_memory, NULL, &config, pDecoder); } #if defined(MA_HAS_WAV) || \ defined(MA_HAS_MP3) || \ defined(MA_HAS_FLAC) || \ - defined(MA_HAS_VORBIS) + defined(MA_HAS_VORBIS) || \ + defined(MA_HAS_OPUS) #define MA_HAS_PATH_API #endif @@ -65314,305 +64690,14 @@ MA_API ma_result ma_decoder_init_vfs_w(ma_vfs* pVFS, const wchar_t* pFilePath, c return MA_SUCCESS; } - -static ma_result ma_decoder__preinit_file(const char* pFilePath, const ma_decoder_config* pConfig, ma_decoder* pDecoder) -{ - ma_result result; - - result = ma_decoder__preinit(NULL, NULL, NULL, NULL, pConfig, pDecoder); - if (result != MA_SUCCESS) { - return result; - } - - if (pFilePath == NULL || pFilePath[0] == '\0') { - return MA_INVALID_ARGS; - } - - return MA_SUCCESS; -} - MA_API ma_result ma_decoder_init_file(const char* pFilePath, const ma_decoder_config* pConfig, ma_decoder* pDecoder) { - ma_result result; - ma_decoder_config config; - - config = ma_decoder_config_init_copy(pConfig); - result = ma_decoder__preinit_file(pFilePath, &config, pDecoder); - if (result != MA_SUCCESS) { - return result; - } - - /* If the backend has support for loading from a file path we'll want to use that. If that all fails we'll fall back to the VFS path. */ - result = MA_NO_BACKEND; - - if (config.encodingFormat != ma_encoding_format_unknown) { - #ifdef MA_HAS_WAV - if (config.encodingFormat == ma_encoding_format_wav) { - result = ma_decoder_init_wav_from_file__internal(pFilePath, &config, pDecoder); - } - #endif - #ifdef MA_HAS_FLAC - if (config.encodingFormat == ma_encoding_format_flac) { - result = ma_decoder_init_flac_from_file__internal(pFilePath, &config, pDecoder); - } - #endif - #ifdef MA_HAS_MP3 - if (config.encodingFormat == ma_encoding_format_mp3) { - result = ma_decoder_init_mp3_from_file__internal(pFilePath, &config, pDecoder); - } - #endif - #ifdef MA_HAS_VORBIS - if (config.encodingFormat == ma_encoding_format_vorbis) { - result = ma_decoder_init_vorbis_from_file__internal(pFilePath, &config, pDecoder); - } - #endif - } - - if (result != MA_SUCCESS) { - /* Getting here means we weren't able to initialize a decoder of a specific encoding format. */ - - /* - We use trial and error to open a decoder. We prioritize custom decoders so that if they - implement the same encoding format they take priority over the built-in decoders. - */ - result = ma_decoder_init_custom_from_file__internal(pFilePath, &config, pDecoder); - - /* - If we get to this point and we still haven't found a decoder, and the caller has requested a - specific encoding format, there's no hope for it. Abort. - */ - if (result != MA_SUCCESS && config.encodingFormat != ma_encoding_format_unknown) { - return MA_NO_BACKEND; - } - - /* First try loading based on the file extension so we don't waste time opening and closing files. */ - #ifdef MA_HAS_WAV - if (result != MA_SUCCESS && ma_path_extension_equal(pFilePath, "wav")) { - result = ma_decoder_init_wav_from_file__internal(pFilePath, &config, pDecoder); - } - #endif - #ifdef MA_HAS_FLAC - if (result != MA_SUCCESS && ma_path_extension_equal(pFilePath, "flac")) { - result = ma_decoder_init_flac_from_file__internal(pFilePath, &config, pDecoder); - } - #endif - #ifdef MA_HAS_MP3 - if (result != MA_SUCCESS && ma_path_extension_equal(pFilePath, "mp3")) { - result = ma_decoder_init_mp3_from_file__internal(pFilePath, &config, pDecoder); - } - #endif - #ifdef MA_HAS_VORBIS - if (result != MA_SUCCESS && ma_path_extension_equal(pFilePath, "ogg")) { - result = ma_decoder_init_vorbis_from_file__internal(pFilePath, &config, pDecoder); - } - #endif - - /* - If we still haven't got a result just use trial and error. Custom decoders have already been attempted, so here we - need only iterate over our stock decoders. - */ - if (result != MA_SUCCESS) { - #ifdef MA_HAS_WAV - if (result != MA_SUCCESS) { - result = ma_decoder_init_wav_from_file__internal(pFilePath, &config, pDecoder); - } - #endif - #ifdef MA_HAS_FLAC - if (result != MA_SUCCESS) { - result = ma_decoder_init_flac_from_file__internal(pFilePath, &config, pDecoder); - } - #endif - #ifdef MA_HAS_MP3 - if (result != MA_SUCCESS) { - result = ma_decoder_init_mp3_from_file__internal(pFilePath, &config, pDecoder); - } - #endif - #ifdef MA_HAS_VORBIS - if (result != MA_SUCCESS) { - result = ma_decoder_init_vorbis_from_file__internal(pFilePath, &config, pDecoder); - } - #endif - } - } - - /* - If at this point we still haven't successfully initialized the decoder it most likely means - the backend doesn't have an implementation for loading from a file path. We'll try using - miniaudio's built-in file IO for loading file. - */ - if (result == MA_SUCCESS) { - /* Initialization was successful. Finish up. */ - result = ma_decoder__postinit(&config, pDecoder); - if (result != MA_SUCCESS) { - /* - The backend was initialized successfully, but for some reason post-initialization failed. This is most likely - due to an out of memory error. We're going to abort with an error here and not try to recover. - */ - if (pDecoder->pBackendVTable != NULL && pDecoder->pBackendVTable->onUninit != NULL) { - pDecoder->pBackendVTable->onUninit(pDecoder->pBackendUserData, &pDecoder->pBackend, &pDecoder->allocationCallbacks); - } - - return result; - } - } else { - /* Probably no implementation for loading from a file path. Use miniaudio's file IO instead. */ - result = ma_decoder_init_vfs(NULL, pFilePath, pConfig, pDecoder); - if (result != MA_SUCCESS) { - return result; - } - } - - return MA_SUCCESS; -} - -static ma_result ma_decoder__preinit_file_w(const wchar_t* pFilePath, const ma_decoder_config* pConfig, ma_decoder* pDecoder) -{ - ma_result result; - - result = ma_decoder__preinit(NULL, NULL, NULL, NULL, pConfig, pDecoder); - if (result != MA_SUCCESS) { - return result; - } - - if (pFilePath == NULL || pFilePath[0] == '\0') { - return MA_INVALID_ARGS; - } - - return MA_SUCCESS; + return ma_decoder_init_vfs(NULL, pFilePath, pConfig, pDecoder); } MA_API ma_result ma_decoder_init_file_w(const wchar_t* pFilePath, const ma_decoder_config* pConfig, ma_decoder* pDecoder) { - ma_result result; - ma_decoder_config config; - - config = ma_decoder_config_init_copy(pConfig); - result = ma_decoder__preinit_file_w(pFilePath, &config, pDecoder); - if (result != MA_SUCCESS) { - return result; - } - - /* If the backend has support for loading from a file path we'll want to use that. If that all fails we'll fall back to the VFS path. */ - result = MA_NO_BACKEND; - - if (config.encodingFormat != ma_encoding_format_unknown) { - #ifdef MA_HAS_WAV - if (config.encodingFormat == ma_encoding_format_wav) { - result = ma_decoder_init_wav_from_file_w__internal(pFilePath, &config, pDecoder); - } - #endif - #ifdef MA_HAS_FLAC - if (config.encodingFormat == ma_encoding_format_flac) { - result = ma_decoder_init_flac_from_file_w__internal(pFilePath, &config, pDecoder); - } - #endif - #ifdef MA_HAS_MP3 - if (config.encodingFormat == ma_encoding_format_mp3) { - result = ma_decoder_init_mp3_from_file_w__internal(pFilePath, &config, pDecoder); - } - #endif - #ifdef MA_HAS_VORBIS - if (config.encodingFormat == ma_encoding_format_vorbis) { - result = ma_decoder_init_vorbis_from_file_w__internal(pFilePath, &config, pDecoder); - } - #endif - } - - if (result != MA_SUCCESS) { - /* Getting here means we weren't able to initialize a decoder of a specific encoding format. */ - - /* - We use trial and error to open a decoder. We prioritize custom decoders so that if they - implement the same encoding format they take priority over the built-in decoders. - */ - result = ma_decoder_init_custom_from_file_w__internal(pFilePath, &config, pDecoder); - - /* - If we get to this point and we still haven't found a decoder, and the caller has requested a - specific encoding format, there's no hope for it. Abort. - */ - if (result != MA_SUCCESS && config.encodingFormat != ma_encoding_format_unknown) { - return MA_NO_BACKEND; - } - - /* First try loading based on the file extension so we don't waste time opening and closing files. */ - #ifdef MA_HAS_WAV - if (result != MA_SUCCESS && ma_path_extension_equal_w(pFilePath, L"wav")) { - result = ma_decoder_init_wav_from_file_w__internal(pFilePath, &config, pDecoder); - } - #endif - #ifdef MA_HAS_FLAC - if (result != MA_SUCCESS && ma_path_extension_equal_w(pFilePath, L"flac")) { - result = ma_decoder_init_flac_from_file_w__internal(pFilePath, &config, pDecoder); - } - #endif - #ifdef MA_HAS_MP3 - if (result != MA_SUCCESS && ma_path_extension_equal_w(pFilePath, L"mp3")) { - result = ma_decoder_init_mp3_from_file_w__internal(pFilePath, &config, pDecoder); - } - #endif - #ifdef MA_HAS_VORBIS - if (result != MA_SUCCESS && ma_path_extension_equal_w(pFilePath, L"ogg")) { - result = ma_decoder_init_vorbis_from_file_w__internal(pFilePath, &config, pDecoder); - } - #endif - - /* - If we still haven't got a result just use trial and error. Custom decoders have already been attempted, so here we - need only iterate over our stock decoders. - */ - if (result != MA_SUCCESS) { - #ifdef MA_HAS_WAV - if (result != MA_SUCCESS) { - result = ma_decoder_init_wav_from_file_w__internal(pFilePath, &config, pDecoder); - } - #endif - #ifdef MA_HAS_FLAC - if (result != MA_SUCCESS) { - result = ma_decoder_init_flac_from_file_w__internal(pFilePath, &config, pDecoder); - } - #endif - #ifdef MA_HAS_MP3 - if (result != MA_SUCCESS) { - result = ma_decoder_init_mp3_from_file_w__internal(pFilePath, &config, pDecoder); - } - #endif - #ifdef MA_HAS_VORBIS - if (result != MA_SUCCESS) { - result = ma_decoder_init_vorbis_from_file_w__internal(pFilePath, &config, pDecoder); - } - #endif - } - } - - /* - If at this point we still haven't successfully initialized the decoder it most likely means - the backend doesn't have an implementation for loading from a file path. We'll try using - miniaudio's built-in file IO for loading file. - */ - if (result == MA_SUCCESS) { - /* Initialization was successful. Finish up. */ - result = ma_decoder__postinit(&config, pDecoder); - if (result != MA_SUCCESS) { - /* - The backend was initialized successfully, but for some reason post-initialization failed. This is most likely - due to an out of memory error. We're going to abort with an error here and not try to recover. - */ - if (pDecoder->pBackendVTable != NULL && pDecoder->pBackendVTable->onUninit != NULL) { - pDecoder->pBackendVTable->onUninit(pDecoder->pBackendUserData, &pDecoder->pBackend, &pDecoder->allocationCallbacks); - } - - return result; - } - } else { - /* Probably no implementation for loading from a file path. Use miniaudio's file IO instead. */ - result = ma_decoder_init_vfs_w(NULL, pFilePath, pConfig, pDecoder); - if (result != MA_SUCCESS) { - return result; - } - } - - return MA_SUCCESS; + return ma_decoder_init_vfs_w(NULL, pFilePath, pConfig, pDecoder); } MA_API ma_result ma_decoder_uninit(ma_decoder* pDecoder) @@ -65670,7 +64755,7 @@ MA_API ma_result ma_decoder_read_pcm_frames(ma_decoder* pDecoder, void* pFramesO } else { /* Getting here means we need to do data conversion. If we're seeking forward and are _not_ doing resampling we can run this in a fast path. If we're doing resampling we - need to run through each sample because we need to ensure its internal cache is updated. + need to run through each sample because we need to ensure it's internal cache is updated. */ if (pFramesOut == NULL && pDecoder->converter.hasResampler == MA_FALSE) { result = ma_data_source_read_pcm_frames(pDecoder->pBackend, NULL, frameCount, &totalFramesReadOut); @@ -65760,17 +64845,8 @@ MA_API ma_result ma_decoder_read_pcm_frames(ma_decoder* pDecoder, void* pFramesO if (requiredInputFrameCount > 0) { result = ma_data_source_read_pcm_frames(pDecoder->pBackend, pIntermediaryBuffer, framesToReadThisIterationIn, &framesReadThisIterationIn); - - /* - Note here that even if we've reached the end, we don't want to abort because there might be more output frames needing to be - generated from cached input data, which might happen if resampling is being performed. - */ - if (result != MA_SUCCESS && result != MA_AT_END) { - break; - } } else { framesReadThisIterationIn = 0; - pIntermediaryBuffer[0] = 0; /* <-- This is just to silence a static analysis warning. */ } /* @@ -66115,42 +65191,42 @@ static size_t ma_encoder__internal_on_write_wav(void* pUserData, const void* pDa return bytesWritten; } -static ma_bool32 ma_encoder__internal_on_seek_wav(void* pUserData, int offset, ma_dr_wav_seek_origin origin) +static drwav_bool32 ma_encoder__internal_on_seek_wav(void* pUserData, int offset, drwav_seek_origin origin) { ma_encoder* pEncoder = (ma_encoder*)pUserData; ma_result result; MA_ASSERT(pEncoder != NULL); - result = pEncoder->onSeek(pEncoder, offset, (origin == ma_dr_wav_seek_origin_start) ? ma_seek_origin_start : ma_seek_origin_current); + result = pEncoder->onSeek(pEncoder, offset, (origin == drwav_seek_origin_start) ? ma_seek_origin_start : ma_seek_origin_current); if (result != MA_SUCCESS) { - return MA_FALSE; + return DRWAV_FALSE; } else { - return MA_TRUE; + return DRWAV_TRUE; } } static ma_result ma_encoder__on_init_wav(ma_encoder* pEncoder) { - ma_dr_wav_data_format wavFormat; - ma_allocation_callbacks allocationCallbacks; - ma_dr_wav* pWav; + drwav_data_format wavFormat; + drwav_allocation_callbacks allocationCallbacks; + drwav* pWav; MA_ASSERT(pEncoder != NULL); - pWav = (ma_dr_wav*)ma_malloc(sizeof(*pWav), &pEncoder->config.allocationCallbacks); + pWav = (drwav*)ma_malloc(sizeof(*pWav), &pEncoder->config.allocationCallbacks); if (pWav == NULL) { return MA_OUT_OF_MEMORY; } - wavFormat.container = ma_dr_wav_container_riff; + wavFormat.container = drwav_container_riff; wavFormat.channels = pEncoder->config.channels; wavFormat.sampleRate = pEncoder->config.sampleRate; wavFormat.bitsPerSample = ma_get_bytes_per_sample(pEncoder->config.format) * 8; if (pEncoder->config.format == ma_format_f32) { - wavFormat.format = MA_DR_WAVE_FORMAT_IEEE_FLOAT; + wavFormat.format = DR_WAVE_FORMAT_IEEE_FLOAT; } else { - wavFormat.format = MA_DR_WAVE_FORMAT_PCM; + wavFormat.format = DR_WAVE_FORMAT_PCM; } allocationCallbacks.pUserData = pEncoder->config.allocationCallbacks.pUserData; @@ -66158,7 +65234,7 @@ static ma_result ma_encoder__on_init_wav(ma_encoder* pEncoder) allocationCallbacks.onRealloc = pEncoder->config.allocationCallbacks.onRealloc; allocationCallbacks.onFree = pEncoder->config.allocationCallbacks.onFree; - if (!ma_dr_wav_init_write(pWav, &wavFormat, ma_encoder__internal_on_write_wav, ma_encoder__internal_on_seek_wav, pEncoder, &allocationCallbacks)) { + if (!drwav_init_write(pWav, &wavFormat, ma_encoder__internal_on_write_wav, ma_encoder__internal_on_seek_wav, pEncoder, &allocationCallbacks)) { return MA_ERROR; } @@ -66169,28 +65245,28 @@ static ma_result ma_encoder__on_init_wav(ma_encoder* pEncoder) static void ma_encoder__on_uninit_wav(ma_encoder* pEncoder) { - ma_dr_wav* pWav; + drwav* pWav; MA_ASSERT(pEncoder != NULL); - pWav = (ma_dr_wav*)pEncoder->pInternalEncoder; + pWav = (drwav*)pEncoder->pInternalEncoder; MA_ASSERT(pWav != NULL); - ma_dr_wav_uninit(pWav); + drwav_uninit(pWav); ma_free(pWav, &pEncoder->config.allocationCallbacks); } static ma_result ma_encoder__on_write_pcm_frames_wav(ma_encoder* pEncoder, const void* pFramesIn, ma_uint64 frameCount, ma_uint64* pFramesWritten) { - ma_dr_wav* pWav; + drwav* pWav; ma_uint64 framesWritten; MA_ASSERT(pEncoder != NULL); - pWav = (ma_dr_wav*)pEncoder->pInternalEncoder; + pWav = (drwav*)pEncoder->pInternalEncoder; MA_ASSERT(pWav != NULL); - framesWritten = ma_dr_wav_write_pcm_frames(pWav, frameCount, pFramesIn); + framesWritten = drwav_write_pcm_frames(pWav, frameCount, pFramesIn); if (pFramesWritten != NULL) { *pFramesWritten = framesWritten; @@ -66568,12 +65644,12 @@ static ma_int16 ma_waveform_sine_s16(double time, double amplitude) return ma_pcm_sample_f32_to_s16(ma_waveform_sine_f32(time, amplitude)); } -static float ma_waveform_square_f32(double time, double dutyCycle, double amplitude) +static float ma_waveform_square_f32(double time, double amplitude) { double f = time - (ma_int64)time; double r; - if (f < dutyCycle) { + if (f < 0.5) { r = amplitude; } else { r = -amplitude; @@ -66582,9 +65658,9 @@ static float ma_waveform_square_f32(double time, double dutyCycle, double amplit return (float)r; } -static ma_int16 ma_waveform_square_s16(double time, double dutyCycle, double amplitude) +static ma_int16 ma_waveform_square_s16(double time, double amplitude) { - return ma_pcm_sample_f32_to_s16(ma_waveform_square_f32(time, dutyCycle, amplitude)); + return ma_pcm_sample_f32_to_s16(ma_waveform_square_f32(time, amplitude)); } static float ma_waveform_triangle_f32(double time, double amplitude) @@ -66659,7 +65735,7 @@ static void ma_waveform_read_pcm_frames__sine(ma_waveform* pWaveform, void* pFra } } -static void ma_waveform_read_pcm_frames__square(ma_waveform* pWaveform, double dutyCycle, void* pFramesOut, ma_uint64 frameCount) +static void ma_waveform_read_pcm_frames__square(ma_waveform* pWaveform, void* pFramesOut, ma_uint64 frameCount) { ma_uint64 iFrame; ma_uint64 iChannel; @@ -66672,7 +65748,7 @@ static void ma_waveform_read_pcm_frames__square(ma_waveform* pWaveform, double d if (pWaveform->config.format == ma_format_f32) { float* pFramesOutF32 = (float*)pFramesOut; for (iFrame = 0; iFrame < frameCount; iFrame += 1) { - float s = ma_waveform_square_f32(pWaveform->time, dutyCycle, pWaveform->config.amplitude); + float s = ma_waveform_square_f32(pWaveform->time, pWaveform->config.amplitude); pWaveform->time += pWaveform->advance; for (iChannel = 0; iChannel < pWaveform->config.channels; iChannel += 1) { @@ -66682,7 +65758,7 @@ static void ma_waveform_read_pcm_frames__square(ma_waveform* pWaveform, double d } else if (pWaveform->config.format == ma_format_s16) { ma_int16* pFramesOutS16 = (ma_int16*)pFramesOut; for (iFrame = 0; iFrame < frameCount; iFrame += 1) { - ma_int16 s = ma_waveform_square_s16(pWaveform->time, dutyCycle, pWaveform->config.amplitude); + ma_int16 s = ma_waveform_square_s16(pWaveform->time, pWaveform->config.amplitude); pWaveform->time += pWaveform->advance; for (iChannel = 0; iChannel < pWaveform->config.channels; iChannel += 1) { @@ -66691,7 +65767,7 @@ static void ma_waveform_read_pcm_frames__square(ma_waveform* pWaveform, double d } } else { for (iFrame = 0; iFrame < frameCount; iFrame += 1) { - float s = ma_waveform_square_f32(pWaveform->time, dutyCycle, pWaveform->config.amplitude); + float s = ma_waveform_square_f32(pWaveform->time, pWaveform->config.amplitude); pWaveform->time += pWaveform->advance; for (iChannel = 0; iChannel < pWaveform->config.channels; iChannel += 1) { @@ -66809,7 +65885,7 @@ MA_API ma_result ma_waveform_read_pcm_frames(ma_waveform* pWaveform, void* pFram case ma_waveform_type_square: { - ma_waveform_read_pcm_frames__square(pWaveform, 0.5, pFramesOut, frameCount); + ma_waveform_read_pcm_frames__square(pWaveform, pFramesOut, frameCount); } break; case ma_waveform_type_triangle: @@ -66846,142 +65922,6 @@ MA_API ma_result ma_waveform_seek_to_pcm_frame(ma_waveform* pWaveform, ma_uint64 return MA_SUCCESS; } -MA_API ma_pulsewave_config ma_pulsewave_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRate, double dutyCycle, double amplitude, double frequency) -{ - ma_pulsewave_config config; - - MA_ZERO_OBJECT(&config); - config.format = format; - config.channels = channels; - config.sampleRate = sampleRate; - config.dutyCycle = dutyCycle; - config.amplitude = amplitude; - config.frequency = frequency; - - return config; -} - -MA_API ma_result ma_pulsewave_init(const ma_pulsewave_config* pConfig, ma_pulsewave* pWaveform) -{ - ma_result result; - ma_waveform_config config; - - if (pWaveform == NULL) { - return MA_INVALID_ARGS; - } - - MA_ZERO_OBJECT(pWaveform); - - config = ma_waveform_config_init( - pConfig->format, - pConfig->channels, - pConfig->sampleRate, - ma_waveform_type_square, - pConfig->amplitude, - pConfig->frequency - ); - - result = ma_waveform_init(&config, &pWaveform->waveform); - ma_pulsewave_set_duty_cycle(pWaveform, pConfig->dutyCycle); - - return result; -} - -MA_API void ma_pulsewave_uninit(ma_pulsewave* pWaveform) -{ - if (pWaveform == NULL) { - return; - } - - ma_waveform_uninit(&pWaveform->waveform); -} - -MA_API ma_result ma_pulsewave_read_pcm_frames(ma_pulsewave* pWaveform, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead) -{ - if (pFramesRead != NULL) { - *pFramesRead = 0; - } - - if (frameCount == 0) { - return MA_INVALID_ARGS; - } - - if (pWaveform == NULL) { - return MA_INVALID_ARGS; - } - - if (pFramesOut != NULL) { - ma_waveform_read_pcm_frames__square(&pWaveform->waveform, pWaveform->config.dutyCycle, pFramesOut, frameCount); - } else { - pWaveform->waveform.time += pWaveform->waveform.advance * (ma_int64)frameCount; /* Cast to int64 required for VC6. Won't affect anything in practice. */ - } - - if (pFramesRead != NULL) { - *pFramesRead = frameCount; - } - - return MA_SUCCESS; -} - -MA_API ma_result ma_pulsewave_seek_to_pcm_frame(ma_pulsewave* pWaveform, ma_uint64 frameIndex) -{ - if (pWaveform == NULL) { - return MA_INVALID_ARGS; - } - - ma_waveform_seek_to_pcm_frame(&pWaveform->waveform, frameIndex); - - return MA_SUCCESS; -} - -MA_API ma_result ma_pulsewave_set_amplitude(ma_pulsewave* pWaveform, double amplitude) -{ - if (pWaveform == NULL) { - return MA_INVALID_ARGS; - } - - pWaveform->config.amplitude = amplitude; - ma_waveform_set_amplitude(&pWaveform->waveform, amplitude); - - return MA_SUCCESS; -} - -MA_API ma_result ma_pulsewave_set_frequency(ma_pulsewave* pWaveform, double frequency) -{ - if (pWaveform == NULL) { - return MA_INVALID_ARGS; - } - - pWaveform->config.frequency = frequency; - ma_waveform_set_frequency(&pWaveform->waveform, frequency); - - return MA_SUCCESS; -} - -MA_API ma_result ma_pulsewave_set_sample_rate(ma_pulsewave* pWaveform, ma_uint32 sampleRate) -{ - if (pWaveform == NULL) { - return MA_INVALID_ARGS; - } - - pWaveform->config.sampleRate = sampleRate; - ma_waveform_set_sample_rate(&pWaveform->waveform, sampleRate); - - return MA_SUCCESS; -} - -MA_API ma_result ma_pulsewave_set_duty_cycle(ma_pulsewave* pWaveform, double dutyCycle) -{ - if (pWaveform == NULL) { - return MA_INVALID_ARGS; - } - - pWaveform->config.dutyCycle = dutyCycle; - - return MA_SUCCESS; -} - - MA_API ma_noise_config ma_noise_config_init(ma_format format, ma_uint32 channels, ma_noise_type type, ma_int32 seed, double amplitude) { @@ -67251,7 +66191,7 @@ MA_API ma_result ma_noise_set_type(ma_noise* pNoise, ma_noise_type type) /* This function should never have been implemented in the first place. Changing the type dynamically is not - supported. Instead you need to uninitialize and reinitialize a fresh `ma_noise` object. This function + supported. Instead you need to uninitialize and reinitiailize a fresh `ma_noise` object. This function will be removed in version 0.12. */ MA_ASSERT(MA_FALSE); @@ -68018,12 +66958,12 @@ static ma_result ma_resource_manager_data_buffer_node_remove_by_key(ma_resource_ static ma_resource_manager_data_supply_type ma_resource_manager_data_buffer_node_get_data_supply_type(ma_resource_manager_data_buffer_node* pDataBufferNode) { - return (ma_resource_manager_data_supply_type)ma_atomic_load_i32(&pDataBufferNode->data.type); + return (ma_resource_manager_data_supply_type)c89atomic_load_i32(&pDataBufferNode->data.type); } static void ma_resource_manager_data_buffer_node_set_data_supply_type(ma_resource_manager_data_buffer_node* pDataBufferNode, ma_resource_manager_data_supply_type supplyType) { - ma_atomic_exchange_i32(&pDataBufferNode->data.type, supplyType); + c89atomic_exchange_i32(&pDataBufferNode->data.type, supplyType); } static ma_result ma_resource_manager_data_buffer_node_increment_ref(ma_resource_manager* pResourceManager, ma_resource_manager_data_buffer_node* pDataBufferNode, ma_uint32* pNewRefCount) @@ -68035,7 +66975,7 @@ static ma_result ma_resource_manager_data_buffer_node_increment_ref(ma_resource_ (void)pResourceManager; - refCount = ma_atomic_fetch_add_32(&pDataBufferNode->refCount, 1) + 1; + refCount = c89atomic_fetch_add_32(&pDataBufferNode->refCount, 1) + 1; if (pNewRefCount != NULL) { *pNewRefCount = refCount; @@ -68053,7 +66993,7 @@ static ma_result ma_resource_manager_data_buffer_node_decrement_ref(ma_resource_ (void)pResourceManager; - refCount = ma_atomic_fetch_sub_32(&pDataBufferNode->refCount, 1) - 1; + refCount = c89atomic_fetch_sub_32(&pDataBufferNode->refCount, 1) - 1; if (pNewRefCount != NULL) { *pNewRefCount = refCount; @@ -68092,7 +67032,7 @@ static ma_result ma_resource_manager_data_buffer_node_result(const ma_resource_m { MA_ASSERT(pDataBufferNode != NULL); - return (ma_result)ma_atomic_load_i32((ma_result*)&pDataBufferNode->result); /* Need a naughty const-cast here. */ + return (ma_result)c89atomic_load_i32((ma_result*)&pDataBufferNode->result); /* Need a naughty const-cast here. */ } @@ -68297,7 +67237,7 @@ MA_API ma_result ma_resource_manager_init(const ma_resource_manager_config* pCon pResourceManager->config.pVFS = &pResourceManager->defaultVFS; } - /* If threading has been disabled at compile time, enforce it at run time as well. */ + /* If threading has been disabled at compile time, enfore it at run time as well. */ #ifdef MA_NO_THREADING { pResourceManager->config.flags |= MA_RESOURCE_MANAGER_FLAG_NO_THREADING; @@ -68334,17 +67274,15 @@ MA_API ma_result ma_resource_manager_init(const ma_resource_manager_config* pCon /* Custom decoding backends. */ if (pConfig->ppCustomDecodingBackendVTables != NULL && pConfig->customDecodingBackendCount > 0) { size_t sizeInBytes = sizeof(*pResourceManager->config.ppCustomDecodingBackendVTables) * pConfig->customDecodingBackendCount; - ma_decoding_backend_vtable** ppCustomDecodingBackendVTables; - ppCustomDecodingBackendVTables = (ma_decoding_backend_vtable**)ma_malloc(sizeInBytes, &pResourceManager->config.allocationCallbacks); + pResourceManager->config.ppCustomDecodingBackendVTables = (ma_decoding_backend_vtable**)ma_malloc(sizeInBytes, &pResourceManager->config.allocationCallbacks); if (pResourceManager->config.ppCustomDecodingBackendVTables == NULL) { ma_job_queue_uninit(&pResourceManager->jobQueue, &pResourceManager->config.allocationCallbacks); return MA_OUT_OF_MEMORY; } - MA_COPY_MEMORY(ppCustomDecodingBackendVTables, pConfig->ppCustomDecodingBackendVTables, sizeInBytes); + MA_COPY_MEMORY(pResourceManager->config.ppCustomDecodingBackendVTables, pConfig->ppCustomDecodingBackendVTables, sizeInBytes); - pResourceManager->config.ppCustomDecodingBackendVTables = ppCustomDecodingBackendVTables; pResourceManager->config.customDecodingBackendCount = pConfig->customDecodingBackendCount; pResourceManager->config.pCustomDecodingBackendUserData = pConfig->pCustomDecodingBackendUserData; } @@ -68395,7 +67333,7 @@ static void ma_resource_manager_delete_all_data_buffer_nodes(ma_resource_manager ma_resource_manager_data_buffer_node* pDataBufferNode = pResourceManager->pRootDataBufferNode; ma_resource_manager_data_buffer_node_remove(pResourceManager, pDataBufferNode); - /* The data buffer has been removed from the BST, so now we need to free its data. */ + /* The data buffer has been removed from the BST, so now we need to free it's data. */ ma_resource_manager_data_buffer_node_free(pResourceManager, pDataBufferNode); } } @@ -68408,7 +67346,7 @@ MA_API void ma_resource_manager_uninit(ma_resource_manager* pResourceManager) /* Job threads need to be killed first. To do this we need to post a quit message to the message queue and then wait for the thread. The quit message will never be removed from the - queue which means it will never not be returned after being encountered for the first time which means all threads will eventually receive it. + queue which means it will never not be returned after being encounted for the first time which means all threads will eventually receive it. */ ma_resource_manager_post_job_quit(pResourceManager); @@ -68448,7 +67386,7 @@ MA_API void ma_resource_manager_uninit(ma_resource_manager* pResourceManager) #endif } - ma_free((ma_decoding_backend_vtable**)pResourceManager->config.ppCustomDecodingBackendVTables, &pResourceManager->config.allocationCallbacks); /* <-- Naughty const-cast, but this is safe. */ + ma_free(pResourceManager->config.ppCustomDecodingBackendVTables, &pResourceManager->config.allocationCallbacks); if (pResourceManager->config.pLog == &pResourceManager->log) { ma_log_uninit(&pResourceManager->log); @@ -68682,7 +67620,7 @@ static ma_result ma_resource_manager_data_buffer_uninit_connector(ma_resource_ma static ma_uint32 ma_resource_manager_data_buffer_node_next_execution_order(ma_resource_manager_data_buffer_node* pDataBufferNode) { MA_ASSERT(pDataBufferNode != NULL); - return ma_atomic_fetch_add_32(&pDataBufferNode->executionCounter, 1); + return c89atomic_fetch_add_32(&pDataBufferNode->executionCounter, 1); } static ma_result ma_resource_manager_data_buffer_node_init_supply_encoded(ma_resource_manager* pResourceManager, ma_resource_manager_data_buffer_node* pDataBufferNode, const char* pFilePath, const wchar_t* pFilePathW) @@ -68866,7 +67804,7 @@ static ma_result ma_resource_manager_data_buffer_node_decode_next_page(ma_resour } result = ma_decoder_read_pcm_frames(pDecoder, pPage->pAudioData, framesToTryReading, &framesRead); - if (result == MA_SUCCESS && framesRead > 0) { + if (framesRead > 0) { pPage->sizeInFrames = framesRead; result = ma_paged_audio_buffer_data_append_page(&pDataBufferNode->data.backend.decodedPaged.data, pPage); @@ -68999,12 +67937,7 @@ static ma_result ma_resource_manager_data_buffer_node_acquire_critical_section(m job.data.resourceManager.loadDataBufferNode.pInitFence = pInitFence; job.data.resourceManager.loadDataBufferNode.pDoneFence = pDoneFence; - if ((flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_WAIT_INIT) != 0) { - result = ma_job_process(&job); - } else { - result = ma_resource_manager_post_job(pResourceManager, &job); - } - + result = ma_resource_manager_post_job(pResourceManager, &job); if (result != MA_SUCCESS) { /* Failed to post job. Probably ran out of memory. */ ma_log_postf(ma_resource_manager_get_log(pResourceManager), MA_LOG_LEVEL_ERROR, "Failed to post MA_JOB_TYPE_RESOURCE_MANAGER_LOAD_DATA_BUFFER_NODE job. %s.\n", ma_result_description(result)); @@ -69017,13 +67950,12 @@ static ma_result ma_resource_manager_data_buffer_node_acquire_critical_section(m if (pDoneFence != NULL) { ma_fence_release(pDoneFence); } if ((flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_WAIT_INIT) != 0) { - ma_resource_manager_inline_notification_uninit(pInitNotification); - } else { - /* These will have been freed by the job thread, but with WAIT_INIT they will already have happened since the job has already been handled. */ - ma_free(pFilePathCopy, &pResourceManager->config.allocationCallbacks); - ma_free(pFilePathWCopy, &pResourceManager->config.allocationCallbacks); + ma_resource_manager_inline_notification_init(pResourceManager, pInitNotification); } + ma_free(pFilePathCopy, &pResourceManager->config.allocationCallbacks); + ma_free(pFilePathWCopy, &pResourceManager->config.allocationCallbacks); + ma_resource_manager_data_buffer_node_remove(pResourceManager, pDataBufferNode); ma_free(pDataBufferNode, &pResourceManager->config.allocationCallbacks); @@ -69153,7 +68085,7 @@ static ma_result ma_resource_manager_data_buffer_node_acquire(ma_resource_manage } /* Getting here means we were successful. Make sure the status of the node is updated accordingly. */ - ma_atomic_exchange_i32(&pDataBufferNode->result, result); + c89atomic_exchange_i32(&pDataBufferNode->result, result); } else { /* Loading asynchronously. We may need to wait for initialization. */ if ((flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_WAIT_INIT) != 0) { @@ -69258,7 +68190,7 @@ static ma_result ma_resource_manager_data_buffer_node_unacquire(ma_resource_mana ma_job job; /* We need to mark the node as unavailable for the sake of the resource manager worker threads. */ - ma_atomic_exchange_i32(&pDataBufferNode->result, MA_UNAVAILABLE); + c89atomic_exchange_i32(&pDataBufferNode->result, MA_UNAVAILABLE); job = ma_job_init(MA_JOB_TYPE_RESOURCE_MANAGER_FREE_DATA_BUFFER_NODE); job.order = ma_resource_manager_data_buffer_node_next_execution_order(pDataBufferNode); @@ -69297,7 +68229,7 @@ static ma_result ma_resource_manager_data_buffer_node_unacquire(ma_resource_mana static ma_uint32 ma_resource_manager_data_buffer_next_execution_order(ma_resource_manager_data_buffer* pDataBuffer) { MA_ASSERT(pDataBuffer != NULL); - return ma_atomic_fetch_add_32(&pDataBuffer->executionCounter, 1); + return c89atomic_fetch_add_32(&pDataBuffer->executionCounter, 1); } static ma_result ma_resource_manager_data_buffer_cb__read_pcm_frames(ma_data_source* pDataSource, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead) @@ -69330,7 +68262,7 @@ static ma_result ma_resource_manager_data_buffer_cb__set_looping(ma_data_source* ma_resource_manager_data_buffer* pDataBuffer = (ma_resource_manager_data_buffer*)pDataSource; MA_ASSERT(pDataBuffer != NULL); - ma_atomic_exchange_32(&pDataBuffer->isLooping, isLooping); + c89atomic_exchange_32(&pDataBuffer->isLooping, isLooping); /* The looping state needs to be set on the connector as well or else looping won't work when we read audio data. */ ma_data_source_set_looping(ma_resource_manager_data_buffer_get_connector(pDataBuffer), isLooping); @@ -69384,14 +68316,10 @@ static ma_result ma_resource_manager_data_buffer_init_ex_internal(ma_resource_ma flags &= ~MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_ASYNC; } - if (pConfig->isLooping) { - flags |= MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_LOOPING; - } - async = (flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_ASYNC) != 0; /* - Fences need to be acquired before doing anything. These must be acquired and released outside of + Fences need to be acquired before doing anything. These must be aquired and released outside of the node to ensure there's no holes where ma_fence_wait() could prematurely return before the data buffer has completed initialization. @@ -69400,7 +68328,7 @@ static ma_result ma_resource_manager_data_buffer_init_ex_internal(ma_resource_ma These fences are always released at the "done" tag at the end of this function. They'll be acquired a second if loading asynchronously. This double acquisition system is just done to - simplify code maintenance. + simplify code maintanence. */ ma_resource_manager_pipeline_notifications_acquire_all_fences(¬ifications); { @@ -69430,7 +68358,7 @@ static ma_result ma_resource_manager_data_buffer_init_ex_internal(ma_resource_ma if (async == MA_FALSE || ma_resource_manager_data_buffer_node_result(pDataBufferNode) == MA_SUCCESS) { /* Loading synchronously or the data has already been fully loaded. We can just initialize the connector from here without a job. */ result = ma_resource_manager_data_buffer_init_connector(pDataBuffer, pConfig, NULL, NULL); - ma_atomic_exchange_i32(&pDataBuffer->result, result); + c89atomic_exchange_i32(&pDataBuffer->result, result); ma_resource_manager_pipeline_notifications_signal_all_notifications(¬ifications); goto done; @@ -69445,10 +68373,10 @@ static ma_result ma_resource_manager_data_buffer_init_ex_internal(ma_resource_ma /* The status of the data buffer needs to be set to MA_BUSY before posting the job so that the - worker thread is aware of its busy state. If the LOAD_DATA_BUFFER job sees a status other + worker thread is aware of it's busy state. If the LOAD_DATA_BUFFER job sees a status other than MA_BUSY, it'll assume an error and fall through to an early exit. */ - ma_atomic_exchange_i32(&pDataBuffer->result, MA_BUSY); + c89atomic_exchange_i32(&pDataBuffer->result, MA_BUSY); /* Acquire fences a second time. These will be released by the async thread. */ ma_resource_manager_pipeline_notifications_acquire_all_fences(¬ifications); @@ -69464,19 +68392,13 @@ static ma_result ma_resource_manager_data_buffer_init_ex_internal(ma_resource_ma job.data.resourceManager.loadDataBuffer.rangeEndInPCMFrames = pConfig->rangeEndInPCMFrames; job.data.resourceManager.loadDataBuffer.loopPointBegInPCMFrames = pConfig->loopPointBegInPCMFrames; job.data.resourceManager.loadDataBuffer.loopPointEndInPCMFrames = pConfig->loopPointEndInPCMFrames; - job.data.resourceManager.loadDataBuffer.isLooping = (flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_LOOPING) != 0; - - /* If we need to wait for initialization to complete we can just process the job in place. */ - if ((flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_WAIT_INIT) != 0) { - result = ma_job_process(&job); - } else { - result = ma_resource_manager_post_job(pResourceManager, &job); - } + job.data.resourceManager.loadDataBuffer.isLooping = pConfig->isLooping; + result = ma_resource_manager_post_job(pResourceManager, &job); if (result != MA_SUCCESS) { /* We failed to post the job. Most likely there isn't enough room in the queue's buffer. */ ma_log_postf(ma_resource_manager_get_log(pResourceManager), MA_LOG_LEVEL_ERROR, "Failed to post MA_JOB_TYPE_RESOURCE_MANAGER_LOAD_DATA_BUFFER job. %s.\n", ma_result_description(result)); - ma_atomic_exchange_i32(&pDataBuffer->result, result); + c89atomic_exchange_i32(&pDataBuffer->result, result); /* Release the fences after the result has been set on the data buffer. */ ma_resource_manager_pipeline_notifications_release_all_fences(¬ifications); @@ -69605,7 +68527,7 @@ MA_API ma_result ma_resource_manager_data_buffer_uninit(ma_resource_manager_data We need to mark the node as unavailable so we don't try reading from it anymore, but also to let the loading thread know that it needs to abort it's loading procedure. */ - ma_atomic_exchange_i32(&pDataBuffer->result, MA_UNAVAILABLE); + c89atomic_exchange_i32(&pDataBuffer->result, MA_UNAVAILABLE); result = ma_resource_manager_inline_notification_init(pDataBuffer->pResourceManager, ¬ification); if (result != MA_SUCCESS) { @@ -69685,29 +68607,22 @@ MA_API ma_result ma_resource_manager_data_buffer_read_pcm_frames(ma_resource_man isDecodedBufferBusy = (ma_resource_manager_data_buffer_node_result(pDataBuffer->pNode) == MA_BUSY); if (ma_resource_manager_data_buffer_get_available_frames(pDataBuffer, &availableFrames) == MA_SUCCESS) { - /* Don't try reading more than the available frame count if the data buffer node is still loading. */ - if (isDecodedBufferBusy) { - if (frameCount > availableFrames) { - frameCount = availableFrames; + /* Don't try reading more than the available frame count. */ + if (frameCount > availableFrames) { + frameCount = availableFrames; - /* - If there's no frames available we want to set the status to MA_AT_END. The logic below - will check if the node is busy, and if so, change it to MA_BUSY. The reason we do this - is because we don't want to call `ma_data_source_read_pcm_frames()` if the frame count - is 0 because that'll result in a situation where it's possible MA_AT_END won't get - returned. - */ - if (frameCount == 0) { - result = MA_AT_END; - } - } else { - isDecodedBufferBusy = MA_FALSE; /* We have enough frames available in the buffer to avoid a MA_BUSY status. */ - } - } else { /* - Getting here means the buffer has been fully loaded. We can just pass the frame count straight - into ma_data_source_read_pcm_frames() below and let ma_data_source handle it. + If there's no frames available we want to set the status to MA_AT_END. The logic below + will check if the node is busy, and if so, change it to MA_BUSY. The reason we do this + is because we don't want to call `ma_data_source_read_pcm_frames()` if the frame count + is 0 because that'll result in a situation where it's possible MA_AT_END won't get + returned. */ + if (frameCount == 0) { + result = MA_AT_END; + } + } else { + isDecodedBufferBusy = MA_FALSE; /* We have enough frames available in the buffer to avoid a MA_BUSY status. */ } } } @@ -69872,7 +68787,7 @@ MA_API ma_result ma_resource_manager_data_buffer_result(const ma_resource_manage return MA_INVALID_ARGS; } - return (ma_result)ma_atomic_load_i32((ma_result*)&pDataBuffer->result); /* Need a naughty const-cast here. */ + return (ma_result)c89atomic_load_i32((ma_result*)&pDataBuffer->result); /* Need a naughty const-cast here. */ } MA_API ma_result ma_resource_manager_data_buffer_set_looping(ma_resource_manager_data_buffer* pDataBuffer, ma_bool32 isLooping) @@ -70025,19 +68940,19 @@ MA_API ma_result ma_resource_manager_unregister_data_w(ma_resource_manager* pRes static ma_uint32 ma_resource_manager_data_stream_next_execution_order(ma_resource_manager_data_stream* pDataStream) { MA_ASSERT(pDataStream != NULL); - return ma_atomic_fetch_add_32(&pDataStream->executionCounter, 1); + return c89atomic_fetch_add_32(&pDataStream->executionCounter, 1); } static ma_bool32 ma_resource_manager_data_stream_is_decoder_at_end(const ma_resource_manager_data_stream* pDataStream) { MA_ASSERT(pDataStream != NULL); - return ma_atomic_load_32((ma_bool32*)&pDataStream->isDecoderAtEnd); + return c89atomic_load_32((ma_bool32*)&pDataStream->isDecoderAtEnd); } static ma_uint32 ma_resource_manager_data_stream_seek_counter(const ma_resource_manager_data_stream* pDataStream) { MA_ASSERT(pDataStream != NULL); - return ma_atomic_load_32((ma_uint32*)&pDataStream->seekCounter); + return c89atomic_load_32((ma_uint32*)&pDataStream->seekCounter); } @@ -70071,7 +68986,7 @@ static ma_result ma_resource_manager_data_stream_cb__set_looping(ma_data_source* ma_resource_manager_data_stream* pDataStream = (ma_resource_manager_data_stream*)pDataSource; MA_ASSERT(pDataStream != NULL); - ma_atomic_exchange_32(&pDataStream->isLooping, isLooping); + c89atomic_exchange_32(&pDataStream->isLooping, isLooping); return MA_SUCCESS; } @@ -70094,7 +69009,7 @@ static void ma_resource_manager_data_stream_set_absolute_cursor(ma_resource_mana absoluteCursor = absoluteCursor % pDataStream->totalLengthInPCMFrames; } - ma_atomic_exchange_64(&pDataStream->absoluteCursor, absoluteCursor); + c89atomic_exchange_64(&pDataStream->absoluteCursor, absoluteCursor); } MA_API ma_result ma_resource_manager_data_stream_init_ex(ma_resource_manager* pResourceManager, const ma_resource_manager_data_source_config* pConfig, ma_resource_manager_data_stream* pDataStream) @@ -70107,7 +69022,6 @@ MA_API ma_result ma_resource_manager_data_stream_init_ex(ma_resource_manager* pR ma_bool32 waitBeforeReturning = MA_FALSE; ma_resource_manager_inline_notification waitNotification; ma_resource_manager_pipeline_notifications notifications; - ma_uint32 flags; if (pDataStream == NULL) { if (pConfig != NULL && pConfig->pNotifications != NULL) { @@ -70138,18 +69052,13 @@ MA_API ma_result ma_resource_manager_data_stream_init_ex(ma_resource_manager* pR return result; } - flags = pConfig->flags; - if (pConfig->isLooping) { - flags |= MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_LOOPING; - } - pDataStream->pResourceManager = pResourceManager; pDataStream->flags = pConfig->flags; pDataStream->result = MA_BUSY; ma_data_source_set_range_in_pcm_frames(pDataStream, pConfig->rangeBegInPCMFrames, pConfig->rangeEndInPCMFrames); ma_data_source_set_loop_point_in_pcm_frames(pDataStream, pConfig->loopPointBegInPCMFrames, pConfig->loopPointEndInPCMFrames); - ma_data_source_set_looping(pDataStream, (flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_LOOPING) != 0); + ma_data_source_set_looping(pDataStream, pConfig->isLooping); if (pResourceManager == NULL || (pConfig->pFilePath == NULL && pConfig->pFilePathW == NULL)) { ma_resource_manager_pipeline_notifications_signal_all_notifications(¬ifications); @@ -70263,7 +69172,7 @@ MA_API ma_result ma_resource_manager_data_stream_uninit(ma_resource_manager_data } /* The first thing to do is set the result to unavailable. This will prevent future page decoding. */ - ma_atomic_exchange_i32(&pDataStream->result, MA_UNAVAILABLE); + c89atomic_exchange_i32(&pDataStream->result, MA_UNAVAILABLE); /* We need to post a job to ensure we're not in the middle or decoding or anything. Because the object is owned by the caller, we'll need @@ -70330,11 +69239,11 @@ static void ma_resource_manager_data_stream_fill_page(ma_resource_manager_data_s /* Just read straight from the decoder. It will deal with ranges and looping for us. */ result = ma_data_source_read_pcm_frames(&pDataStream->decoder, pPageData, pageSizeInFrames, &totalFramesReadForThisPage); if (result == MA_AT_END || totalFramesReadForThisPage < pageSizeInFrames) { - ma_atomic_exchange_32(&pDataStream->isDecoderAtEnd, MA_TRUE); + c89atomic_exchange_32(&pDataStream->isDecoderAtEnd, MA_TRUE); } - ma_atomic_exchange_32(&pDataStream->pageFrameCount[pageIndex], (ma_uint32)totalFramesReadForThisPage); - ma_atomic_exchange_32(&pDataStream->isPageValid[pageIndex], MA_TRUE); + c89atomic_exchange_32(&pDataStream->pageFrameCount[pageIndex], (ma_uint32)totalFramesReadForThisPage); + c89atomic_exchange_32(&pDataStream->isPageValid[pageIndex], MA_TRUE); } static void ma_resource_manager_data_stream_fill_pages(ma_resource_manager_data_stream* pDataStream) @@ -70379,14 +69288,14 @@ static ma_result ma_resource_manager_data_stream_map(ma_resource_manager_data_st } /* If the page we're on is invalid it means we've caught up to the job thread. */ - if (ma_atomic_load_32(&pDataStream->isPageValid[pDataStream->currentPageIndex]) == MA_FALSE) { + if (c89atomic_load_32(&pDataStream->isPageValid[pDataStream->currentPageIndex]) == MA_FALSE) { framesAvailable = 0; } else { /* The page we're on is valid so we must have some frames available. We need to make sure that we don't overflow into the next page, even if it's valid. The reason is that the unmap process will only post an update for one page at a time. Keeping mapping tied to page boundaries makes this simpler. */ - ma_uint32 currentPageFrameCount = ma_atomic_load_32(&pDataStream->pageFrameCount[pDataStream->currentPageIndex]); + ma_uint32 currentPageFrameCount = c89atomic_load_32(&pDataStream->pageFrameCount[pDataStream->currentPageIndex]); MA_ASSERT(currentPageFrameCount >= pDataStream->relativeCursor); framesAvailable = currentPageFrameCount - pDataStream->relativeCursor; @@ -70438,7 +69347,7 @@ static ma_result ma_resource_manager_data_stream_unmap(ma_resource_manager_data_ pageSizeInFrames = ma_resource_manager_data_stream_get_page_size_in_frames(pDataStream); /* The absolute cursor needs to be updated for ma_resource_manager_data_stream_get_cursor_in_pcm_frames(). */ - ma_resource_manager_data_stream_set_absolute_cursor(pDataStream, ma_atomic_load_64(&pDataStream->absoluteCursor) + frameCount); + ma_resource_manager_data_stream_set_absolute_cursor(pDataStream, c89atomic_load_64(&pDataStream->absoluteCursor) + frameCount); /* Here is where we need to check if we need to load a new page, and if so, post a job to load it. */ newRelativeCursor = pDataStream->relativeCursor + (ma_uint32)frameCount; @@ -70454,7 +69363,7 @@ static ma_result ma_resource_manager_data_stream_unmap(ma_resource_manager_data_ job.data.resourceManager.pageDataStream.pageIndex = pDataStream->currentPageIndex; /* The page needs to be marked as invalid so that the public API doesn't try reading from it. */ - ma_atomic_exchange_32(&pDataStream->isPageValid[pDataStream->currentPageIndex], MA_FALSE); + c89atomic_exchange_32(&pDataStream->isPageValid[pDataStream->currentPageIndex], MA_FALSE); /* Before posting the job we need to make sure we set some state. */ pDataStream->relativeCursor = newRelativeCursor; @@ -70557,15 +69466,15 @@ MA_API ma_result ma_resource_manager_data_stream_seek_to_pcm_frame(ma_resource_m } /* If we're not already seeking and we're sitting on the same frame, just make this a no-op. */ - if (ma_atomic_load_32(&pDataStream->seekCounter) == 0) { - if (ma_atomic_load_64(&pDataStream->absoluteCursor) == frameIndex) { + if (c89atomic_load_32(&pDataStream->seekCounter) == 0) { + if (c89atomic_load_64(&pDataStream->absoluteCursor) == frameIndex) { return MA_SUCCESS; } } /* Increment the seek counter first to indicate to read_paged_pcm_frames() and map_paged_pcm_frames() that we are in the middle of a seek and MA_BUSY should be returned. */ - ma_atomic_fetch_add_32(&pDataStream->seekCounter, 1); + c89atomic_fetch_add_32(&pDataStream->seekCounter, 1); /* Update the absolute cursor so that ma_resource_manager_data_stream_get_cursor_in_pcm_frames() returns the new position. */ ma_resource_manager_data_stream_set_absolute_cursor(pDataStream, frameIndex); @@ -70577,11 +69486,11 @@ MA_API ma_result ma_resource_manager_data_stream_seek_to_pcm_frame(ma_resource_m */ pDataStream->relativeCursor = 0; pDataStream->currentPageIndex = 0; - ma_atomic_exchange_32(&pDataStream->isPageValid[0], MA_FALSE); - ma_atomic_exchange_32(&pDataStream->isPageValid[1], MA_FALSE); + c89atomic_exchange_32(&pDataStream->isPageValid[0], MA_FALSE); + c89atomic_exchange_32(&pDataStream->isPageValid[1], MA_FALSE); /* Make sure the data stream is not marked as at the end or else if we seek in response to hitting the end, we won't be able to read any more data. */ - ma_atomic_exchange_32(&pDataStream->isDecoderAtEnd, MA_FALSE); + c89atomic_exchange_32(&pDataStream->isDecoderAtEnd, MA_FALSE); /* The public API is not allowed to touch the internal decoder so we need to use a job to perform the seek. When seeking, the job thread will assume both pages @@ -70657,7 +69566,7 @@ MA_API ma_result ma_resource_manager_data_stream_get_cursor_in_pcm_frames(ma_res return MA_INVALID_OPERATION; } - *pCursor = ma_atomic_load_64(&pDataStream->absoluteCursor); + *pCursor = c89atomic_load_64(&pDataStream->absoluteCursor); return MA_SUCCESS; } @@ -70703,7 +69612,7 @@ MA_API ma_result ma_resource_manager_data_stream_result(const ma_resource_manage return MA_INVALID_ARGS; } - return (ma_result)ma_atomic_load_i32(&pDataStream->result); + return (ma_result)c89atomic_load_i32(&pDataStream->result); } MA_API ma_result ma_resource_manager_data_stream_set_looping(ma_resource_manager_data_stream* pDataStream, ma_bool32 isLooping) @@ -70717,7 +69626,7 @@ MA_API ma_bool32 ma_resource_manager_data_stream_is_looping(const ma_resource_ma return MA_FALSE; } - return ma_atomic_load_32((ma_bool32*)&pDataStream->isLooping); /* Naughty const-cast. Value won't change from here in practice (maybe from another thread). */ + return c89atomic_load_32((ma_bool32*)&pDataStream->isLooping); /* Naughty const-cast. Value won't change from here in practice (maybe from another thread). */ } MA_API ma_result ma_resource_manager_data_stream_get_available_frames(ma_resource_manager_data_stream* pDataStream, ma_uint64* pAvailableFrames) @@ -70742,10 +69651,10 @@ MA_API ma_result ma_resource_manager_data_stream_get_available_frames(ma_resourc relativeCursor = pDataStream->relativeCursor; availableFrames = 0; - if (ma_atomic_load_32(&pDataStream->isPageValid[pageIndex0])) { - availableFrames += ma_atomic_load_32(&pDataStream->pageFrameCount[pageIndex0]) - relativeCursor; - if (ma_atomic_load_32(&pDataStream->isPageValid[pageIndex1])) { - availableFrames += ma_atomic_load_32(&pDataStream->pageFrameCount[pageIndex1]); + if (c89atomic_load_32(&pDataStream->isPageValid[pageIndex0])) { + availableFrames += c89atomic_load_32(&pDataStream->pageFrameCount[pageIndex0]) - relativeCursor; + if (c89atomic_load_32(&pDataStream->isPageValid[pageIndex1])) { + availableFrames += c89atomic_load_32(&pDataStream->pageFrameCount[pageIndex1]); } } @@ -70771,9 +69680,6 @@ static ma_result ma_resource_manager_data_source_preinit(ma_resource_manager* pR } pDataSource->flags = pConfig->flags; - if (pConfig->isLooping) { - pDataSource->flags |= MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_LOOPING; - } return MA_SUCCESS; } @@ -71054,7 +69960,7 @@ static ma_result ma_job_process__resource_manager__load_data_buffer_node(ma_job* MA_ASSERT(pDataBufferNode->isDataOwnedByResourceManager == MA_TRUE); /* The data should always be owned by the resource manager. */ /* The data buffer is not getting deleted, but we may be getting executed out of order. If so, we need to push the job back onto the queue and return. */ - if (pJob->order != ma_atomic_load_32(&pDataBufferNode->executionPointer)) { + if (pJob->order != c89atomic_load_32(&pDataBufferNode->executionPointer)) { return ma_resource_manager_post_job(pResourceManager, pJob); /* Attempting to execute out of order. Probably interleaved with a MA_JOB_TYPE_RESOURCE_MANAGER_FREE_DATA_BUFFER job. */ } @@ -71165,7 +70071,7 @@ static ma_result ma_job_process__resource_manager__load_data_buffer_node(ma_job* immediately deletes it before we've got to this point. In this case, pDataBuffer->result will be MA_UNAVAILABLE, and setting it to MA_SUCCESS or any other error code would cause the buffer to look like it's in a state that it's not. */ - ma_atomic_compare_and_swap_i32(&pDataBufferNode->result, MA_BUSY, result); + c89atomic_compare_and_swap_i32(&pDataBufferNode->result, MA_BUSY, result); /* At this point initialization is complete and we can signal the notification if any. */ if (pJob->data.resourceManager.loadDataBufferNode.pInitNotification != NULL) { @@ -71186,13 +70092,7 @@ static ma_result ma_job_process__resource_manager__load_data_buffer_node(ma_job* } /* Increment the node's execution pointer so that the next jobs can be processed. This is how we keep decoding of pages in-order. */ - ma_atomic_fetch_add_32(&pDataBufferNode->executionPointer, 1); - - /* A busy result should be considered successful from the point of view of the job system. */ - if (result == MA_BUSY) { - result = MA_SUCCESS; - } - + c89atomic_fetch_add_32(&pDataBufferNode->executionPointer, 1); return result; } @@ -71209,7 +70109,7 @@ static ma_result ma_job_process__resource_manager__free_data_buffer_node(ma_job* pDataBufferNode = (ma_resource_manager_data_buffer_node*)pJob->data.resourceManager.freeDataBufferNode.pDataBufferNode; MA_ASSERT(pDataBufferNode != NULL); - if (pJob->order != ma_atomic_load_32(&pDataBufferNode->executionPointer)) { + if (pJob->order != c89atomic_load_32(&pDataBufferNode->executionPointer)) { return ma_resource_manager_post_job(pResourceManager, pJob); /* Out of order. */ } @@ -71224,7 +70124,7 @@ static ma_result ma_job_process__resource_manager__free_data_buffer_node(ma_job* ma_fence_release(pJob->data.resourceManager.freeDataBufferNode.pDoneFence); } - ma_atomic_fetch_add_32(&pDataBufferNode->executionPointer, 1); + c89atomic_fetch_add_32(&pDataBufferNode->executionPointer, 1); return MA_SUCCESS; } @@ -71242,7 +70142,7 @@ static ma_result ma_job_process__resource_manager__page_data_buffer_node(ma_job* pDataBufferNode = (ma_resource_manager_data_buffer_node*)pJob->data.resourceManager.pageDataBufferNode.pDataBufferNode; MA_ASSERT(pDataBufferNode != NULL); - if (pJob->order != ma_atomic_load_32(&pDataBufferNode->executionPointer)) { + if (pJob->order != c89atomic_load_32(&pDataBufferNode->executionPointer)) { return ma_resource_manager_post_job(pResourceManager, pJob); /* Out of order. */ } @@ -71285,7 +70185,7 @@ static ma_result ma_job_process__resource_manager__page_data_buffer_node(ma_job* } /* Make sure we set the result of node in case some error occurred. */ - ma_atomic_compare_and_swap_i32(&pDataBufferNode->result, MA_BUSY, result); + c89atomic_compare_and_swap_i32(&pDataBufferNode->result, MA_BUSY, result); /* Signal the notification after setting the result in case the notification callback wants to inspect the result code. */ if (result != MA_BUSY) { @@ -71298,7 +70198,7 @@ static ma_result ma_job_process__resource_manager__page_data_buffer_node(ma_job* } } - ma_atomic_fetch_add_32(&pDataBufferNode->executionPointer, 1); + c89atomic_fetch_add_32(&pDataBufferNode->executionPointer, 1); return result; } @@ -71322,7 +70222,7 @@ static ma_result ma_job_process__resource_manager__load_data_buffer(ma_job* pJob pResourceManager = pDataBuffer->pResourceManager; - if (pJob->order != ma_atomic_load_32(&pDataBuffer->executionPointer)) { + if (pJob->order != c89atomic_load_32(&pDataBuffer->executionPointer)) { return ma_resource_manager_post_job(pResourceManager, pJob); /* Attempting to execute out of order. Probably interleaved with a MA_JOB_TYPE_RESOURCE_MANAGER_FREE_DATA_BUFFER job. */ } @@ -71332,10 +70232,9 @@ static ma_result ma_job_process__resource_manager__load_data_buffer(ma_job* pJob */ result = ma_resource_manager_data_buffer_result(pDataBuffer); if (result != MA_BUSY) { - goto done; /* <-- This will ensure the execution pointer is incremented. */ + goto done; /* <-- This will ensure the exucution pointer is incremented. */ } else { result = MA_SUCCESS; /* <-- Make sure this is reset. */ - (void)result; /* <-- This is to suppress a static analysis diagnostic about "result" not being used. But for safety when I do future maintenance I don't want to delete that assignment. */ } /* Try initializing the connector if we haven't already. */ @@ -71381,7 +70280,7 @@ static ma_result ma_job_process__resource_manager__load_data_buffer(ma_job* pJob done: /* Only move away from a busy code so that we don't trash any existing error codes. */ - ma_atomic_compare_and_swap_i32(&pDataBuffer->result, MA_BUSY, result); + c89atomic_compare_and_swap_i32(&pDataBuffer->result, MA_BUSY, result); /* Only signal the other threads after the result has been set just for cleanliness sake. */ if (pJob->data.resourceManager.loadDataBuffer.pDoneNotification != NULL) { @@ -71404,7 +70303,7 @@ static ma_result ma_job_process__resource_manager__load_data_buffer(ma_job* pJob } } - ma_atomic_fetch_add_32(&pDataBuffer->executionPointer, 1); + c89atomic_fetch_add_32(&pDataBuffer->executionPointer, 1); return result; } @@ -71420,7 +70319,7 @@ static ma_result ma_job_process__resource_manager__free_data_buffer(ma_job* pJob pResourceManager = pDataBuffer->pResourceManager; - if (pJob->order != ma_atomic_load_32(&pDataBuffer->executionPointer)) { + if (pJob->order != c89atomic_load_32(&pDataBuffer->executionPointer)) { return ma_resource_manager_post_job(pResourceManager, pJob); /* Out of order. */ } @@ -71435,7 +70334,7 @@ static ma_result ma_job_process__resource_manager__free_data_buffer(ma_job* pJob ma_fence_release(pJob->data.resourceManager.freeDataBuffer.pDoneFence); } - ma_atomic_fetch_add_32(&pDataBuffer->executionPointer, 1); + c89atomic_fetch_add_32(&pDataBuffer->executionPointer, 1); return MA_SUCCESS; } @@ -71454,7 +70353,7 @@ static ma_result ma_job_process__resource_manager__load_data_stream(ma_job* pJob pResourceManager = pDataStream->pResourceManager; - if (pJob->order != ma_atomic_load_32(&pDataStream->executionPointer)) { + if (pJob->order != c89atomic_load_32(&pDataStream->executionPointer)) { return ma_resource_manager_post_job(pResourceManager, pJob); /* Out of order. */ } @@ -71515,7 +70414,7 @@ static ma_result ma_job_process__resource_manager__load_data_stream(ma_job* pJob ma_free(pJob->data.resourceManager.loadDataStream.pFilePathW, &pResourceManager->config.allocationCallbacks); /* We can only change the status away from MA_BUSY. If it's set to anything else it means an error has occurred somewhere or the uninitialization process has started (most likely). */ - ma_atomic_compare_and_swap_i32(&pDataStream->result, MA_BUSY, result); + c89atomic_compare_and_swap_i32(&pDataStream->result, MA_BUSY, result); /* Only signal the other threads after the result has been set just for cleanliness sake. */ if (pJob->data.resourceManager.loadDataStream.pInitNotification != NULL) { @@ -71525,7 +70424,7 @@ static ma_result ma_job_process__resource_manager__load_data_stream(ma_job* pJob ma_fence_release(pJob->data.resourceManager.loadDataStream.pInitFence); } - ma_atomic_fetch_add_32(&pDataStream->executionPointer, 1); + c89atomic_fetch_add_32(&pDataStream->executionPointer, 1); return result; } @@ -71541,7 +70440,7 @@ static ma_result ma_job_process__resource_manager__free_data_stream(ma_job* pJob pResourceManager = pDataStream->pResourceManager; - if (pJob->order != ma_atomic_load_32(&pDataStream->executionPointer)) { + if (pJob->order != c89atomic_load_32(&pDataStream->executionPointer)) { return ma_resource_manager_post_job(pResourceManager, pJob); /* Out of order. */ } @@ -71567,7 +70466,7 @@ static ma_result ma_job_process__resource_manager__free_data_stream(ma_job* pJob ma_fence_release(pJob->data.resourceManager.freeDataStream.pDoneFence); } - /*ma_atomic_fetch_add_32(&pDataStream->executionPointer, 1);*/ + /*c89atomic_fetch_add_32(&pDataStream->executionPointer, 1);*/ return MA_SUCCESS; } @@ -71584,7 +70483,7 @@ static ma_result ma_job_process__resource_manager__page_data_stream(ma_job* pJob pResourceManager = pDataStream->pResourceManager; - if (pJob->order != ma_atomic_load_32(&pDataStream->executionPointer)) { + if (pJob->order != c89atomic_load_32(&pDataStream->executionPointer)) { return ma_resource_manager_post_job(pResourceManager, pJob); /* Out of order. */ } @@ -71597,7 +70496,7 @@ static ma_result ma_job_process__resource_manager__page_data_stream(ma_job* pJob ma_resource_manager_data_stream_fill_page(pDataStream, pJob->data.resourceManager.pageDataStream.pageIndex); done: - ma_atomic_fetch_add_32(&pDataStream->executionPointer, 1); + c89atomic_fetch_add_32(&pDataStream->executionPointer, 1); return result; } @@ -71614,7 +70513,7 @@ static ma_result ma_job_process__resource_manager__seek_data_stream(ma_job* pJob pResourceManager = pDataStream->pResourceManager; - if (pJob->order != ma_atomic_load_32(&pDataStream->executionPointer)) { + if (pJob->order != c89atomic_load_32(&pDataStream->executionPointer)) { return ma_resource_manager_post_job(pResourceManager, pJob); /* Out of order. */ } @@ -71634,10 +70533,10 @@ static ma_result ma_job_process__resource_manager__seek_data_stream(ma_job* pJob ma_resource_manager_data_stream_fill_pages(pDataStream); /* We need to let the public API know that we're done seeking. */ - ma_atomic_fetch_sub_32(&pDataStream->seekCounter, 1); + c89atomic_fetch_sub_32(&pDataStream->seekCounter, 1); done: - ma_atomic_fetch_add_32(&pDataStream->executionPointer, 1); + c89atomic_fetch_add_32(&pDataStream->executionPointer, 1); return result; } @@ -71682,74 +70581,11 @@ static ma_result ma_job_process__resource_manager__seek_data_stream(ma_job* pJob #ifndef MA_NO_NODE_GRAPH - -static ma_stack* ma_stack_init(size_t sizeInBytes, const ma_allocation_callbacks* pAllocationCallbacks) -{ - ma_stack* pStack; - - if (sizeInBytes == 0) { - return NULL; - } - - pStack = (ma_stack*)ma_malloc(sizeof(*pStack) - sizeof(pStack->_data) + sizeInBytes, pAllocationCallbacks); - if (pStack == NULL) { - return NULL; - } - - pStack->offset = 0; - pStack->sizeInBytes = sizeInBytes; - - return pStack; -} - -static void ma_stack_uninit(ma_stack* pStack, const ma_allocation_callbacks* pAllocationCallbacks) -{ - if (pStack == NULL) { - return; - } - - ma_free(pStack, pAllocationCallbacks); -} - -static void* ma_stack_alloc(ma_stack* pStack, size_t sz) -{ - /* The size of the allocation is stored in the memory directly before the pointer. This needs to include padding to keep it aligned to ma_uintptr */ - void* p = (void*)((char*)pStack->_data + pStack->offset); - size_t* pSize = (size_t*)p; - - sz = (sz + (sizeof(ma_uintptr) - 1)) & ~(sizeof(ma_uintptr) - 1); /* Padding. */ - if (pStack->offset + sz + sizeof(size_t) > pStack->sizeInBytes) { - return NULL; /* Out of memory. */ - } - - pStack->offset += sz + sizeof(size_t); - - *pSize = sz; - return (void*)((char*)p + sizeof(size_t)); -} - -static void ma_stack_free(ma_stack* pStack, void* p) -{ - size_t* pSize; - - if (p == NULL) { - return; - } - - pSize = (size_t*)p - 1; - pStack->offset -= *pSize + sizeof(size_t); -} - - - /* 10ms @ 48K = 480. Must never exceed 65535. */ #ifndef MA_DEFAULT_NODE_CACHE_CAP_IN_FRAMES_PER_BUS #define MA_DEFAULT_NODE_CACHE_CAP_IN_FRAMES_PER_BUS 480 #endif -#ifndef MA_DEFAULT_PREMIX_STACK_SIZE_PER_CHANNEL -#define MA_DEFAULT_PREMIX_STACK_SIZE_PER_CHANNEL 524288 -#endif static ma_result ma_node_read_pcm_frames(ma_node* pNode, ma_uint32 outputBusIndex, float* pFramesOut, ma_uint32 frameCount, ma_uint32* pFramesRead, ma_uint64 globalTime); @@ -71789,8 +70625,8 @@ MA_API ma_node_graph_config ma_node_graph_config_init(ma_uint32 channels) ma_node_graph_config config; MA_ZERO_OBJECT(&config); - config.channels = channels; - config.processingSizeInFrames = 0; + config.channels = channels; + config.nodeCacheCapInFrames = MA_DEFAULT_NODE_CACHE_CAP_IN_FRAMES_PER_BUS; return config; } @@ -71799,14 +70635,14 @@ MA_API ma_node_graph_config ma_node_graph_config_init(ma_uint32 channels) static void ma_node_graph_set_is_reading(ma_node_graph* pNodeGraph, ma_bool32 isReading) { MA_ASSERT(pNodeGraph != NULL); - ma_atomic_exchange_32(&pNodeGraph->isReading, isReading); + c89atomic_exchange_32(&pNodeGraph->isReading, isReading); } #if 0 static ma_bool32 ma_node_graph_is_reading(ma_node_graph* pNodeGraph) { MA_ASSERT(pNodeGraph != NULL); - return ma_atomic_load_32(&pNodeGraph->isReading); + return c89atomic_load_32(&pNodeGraph->isReading); } #endif @@ -71877,7 +70713,11 @@ MA_API ma_result ma_node_graph_init(const ma_node_graph_config* pConfig, const m } MA_ZERO_OBJECT(pNodeGraph); - pNodeGraph->processingSizeInFrames = pConfig->processingSizeInFrames; + pNodeGraph->nodeCacheCapInFrames = pConfig->nodeCacheCapInFrames; + if (pNodeGraph->nodeCacheCapInFrames == 0) { + pNodeGraph->nodeCacheCapInFrames = MA_DEFAULT_NODE_CACHE_CAP_IN_FRAMES_PER_BUS; + } + /* Base node so we can use the node graph as a node into another graph. */ baseConfig = ma_node_config_init(); @@ -71902,40 +70742,6 @@ MA_API ma_result ma_node_graph_init(const ma_node_graph_config* pConfig, const m return result; } - - /* Processing cache. */ - if (pConfig->processingSizeInFrames > 0) { - pNodeGraph->pProcessingCache = (float*)ma_malloc(pConfig->processingSizeInFrames * pConfig->channels * sizeof(float), pAllocationCallbacks); - if (pNodeGraph->pProcessingCache == NULL) { - ma_node_uninit(&pNodeGraph->endpoint, pAllocationCallbacks); - ma_node_uninit(&pNodeGraph->base, pAllocationCallbacks); - return MA_OUT_OF_MEMORY; - } - } - - - /* - We need a pre-mix stack. The size of this stack is configurable via the config. The default value depends on the channel count. - */ - { - size_t preMixStackSizeInBytes = pConfig->preMixStackSizeInBytes; - if (preMixStackSizeInBytes == 0) { - preMixStackSizeInBytes = pConfig->channels * MA_DEFAULT_PREMIX_STACK_SIZE_PER_CHANNEL; - } - - pNodeGraph->pPreMixStack = ma_stack_init(preMixStackSizeInBytes, pAllocationCallbacks); - if (pNodeGraph->pPreMixStack == NULL) { - ma_node_uninit(&pNodeGraph->endpoint, pAllocationCallbacks); - ma_node_uninit(&pNodeGraph->base, pAllocationCallbacks); - if (pNodeGraph->pProcessingCache != NULL) { - ma_free(pNodeGraph->pProcessingCache, pAllocationCallbacks); - } - - return MA_OUT_OF_MEMORY; - } - } - - return MA_SUCCESS; } @@ -71946,17 +70752,6 @@ MA_API void ma_node_graph_uninit(ma_node_graph* pNodeGraph, const ma_allocation_ } ma_node_uninit(&pNodeGraph->endpoint, pAllocationCallbacks); - ma_node_uninit(&pNodeGraph->base, pAllocationCallbacks); - - if (pNodeGraph->pProcessingCache != NULL) { - ma_free(pNodeGraph->pProcessingCache, pAllocationCallbacks); - pNodeGraph->pProcessingCache = NULL; - } - - if (pNodeGraph->pPreMixStack != NULL) { - ma_stack_uninit(pNodeGraph->pPreMixStack, pAllocationCallbacks); - pNodeGraph->pPreMixStack = NULL; - } } MA_API ma_node* ma_node_graph_get_endpoint(ma_node_graph* pNodeGraph) @@ -71989,72 +70784,27 @@ MA_API ma_result ma_node_graph_read_pcm_frames(ma_node_graph* pNodeGraph, void* totalFramesRead = 0; while (totalFramesRead < frameCount) { ma_uint32 framesJustRead; - ma_uint64 framesToRead; - float* pRunningFramesOut; + ma_uint64 framesToRead = frameCount - totalFramesRead; - framesToRead = frameCount - totalFramesRead; if (framesToRead > 0xFFFFFFFF) { framesToRead = 0xFFFFFFFF; } - pRunningFramesOut = (float*)ma_offset_pcm_frames_ptr(pFramesOut, totalFramesRead, ma_format_f32, channels); - - /* If there's anything in the cache, consume that first. */ - if (pNodeGraph->processingCacheFramesRemaining > 0) { - ma_uint32 framesToReadFromCache; - - framesToReadFromCache = (ma_uint32)framesToRead; - if (framesToReadFromCache > pNodeGraph->processingCacheFramesRemaining) { - framesToReadFromCache = pNodeGraph->processingCacheFramesRemaining; - } - - MA_COPY_MEMORY(pRunningFramesOut, pNodeGraph->pProcessingCache, framesToReadFromCache * channels * sizeof(float)); - MA_MOVE_MEMORY(pNodeGraph->pProcessingCache, pNodeGraph->pProcessingCache + (framesToReadFromCache * channels), (pNodeGraph->processingCacheFramesRemaining - framesToReadFromCache) * channels * sizeof(float)); - pNodeGraph->processingCacheFramesRemaining -= framesToReadFromCache; - - totalFramesRead += framesToReadFromCache; - continue; - } else { - /* - If processingSizeInFrames is non-zero, we need to make sure we always read in chunks of that size. If the frame count is less than - that, we need to read into the cache and then continue on. - */ - float* pReadDst = pRunningFramesOut; - - if (pNodeGraph->processingSizeInFrames > 0) { - if (framesToRead < pNodeGraph->processingSizeInFrames) { - pReadDst = pNodeGraph->pProcessingCache; /* We need to read into the cache because otherwise we'll overflow the output buffer. */ - } - - framesToRead = pNodeGraph->processingSizeInFrames; - } - - ma_node_graph_set_is_reading(pNodeGraph, MA_TRUE); - { - result = ma_node_read_pcm_frames(&pNodeGraph->endpoint, 0, pReadDst, (ma_uint32)framesToRead, &framesJustRead, ma_node_get_time(&pNodeGraph->endpoint)); - } - ma_node_graph_set_is_reading(pNodeGraph, MA_FALSE); + ma_node_graph_set_is_reading(pNodeGraph, MA_TRUE); + { + result = ma_node_read_pcm_frames(&pNodeGraph->endpoint, 0, (float*)ma_offset_pcm_frames_ptr(pFramesOut, totalFramesRead, ma_format_f32, channels), (ma_uint32)framesToRead, &framesJustRead, ma_node_get_time(&pNodeGraph->endpoint)); + } + ma_node_graph_set_is_reading(pNodeGraph, MA_FALSE); - /* - Do not increment the total frames read counter if we read into the cache. We use this to determine how many frames have - been written to the final output buffer. - */ - if (pReadDst == pNodeGraph->pProcessingCache) { - /* We read into the cache. */ - pNodeGraph->processingCacheFramesRemaining = framesJustRead; - } else { - /* We read straight into the output buffer. */ - totalFramesRead += framesJustRead; - } + totalFramesRead += framesJustRead; - if (result != MA_SUCCESS) { - break; - } + if (result != MA_SUCCESS) { + break; + } - /* Abort if we weren't able to read any frames or else we risk getting stuck in a loop. */ - if (framesJustRead == 0) { - break; - } + /* Abort if we weren't able to read any frames or else we risk getting stuck in a loop. */ + if (framesJustRead == 0) { + break; } } @@ -72142,26 +70892,26 @@ static ma_uint32 ma_node_output_bus_get_channels(const ma_node_output_bus* pOutp static void ma_node_output_bus_set_has_read(ma_node_output_bus* pOutputBus, ma_bool32 hasRead) { if (hasRead) { - ma_atomic_fetch_or_32(&pOutputBus->flags, MA_NODE_OUTPUT_BUS_FLAG_HAS_READ); + c89atomic_fetch_or_32(&pOutputBus->flags, MA_NODE_OUTPUT_BUS_FLAG_HAS_READ); } else { - ma_atomic_fetch_and_32(&pOutputBus->flags, (ma_uint32)~MA_NODE_OUTPUT_BUS_FLAG_HAS_READ); + c89atomic_fetch_and_32(&pOutputBus->flags, (ma_uint32)~MA_NODE_OUTPUT_BUS_FLAG_HAS_READ); } } static ma_bool32 ma_node_output_bus_has_read(ma_node_output_bus* pOutputBus) { - return (ma_atomic_load_32(&pOutputBus->flags) & MA_NODE_OUTPUT_BUS_FLAG_HAS_READ) != 0; + return (c89atomic_load_32(&pOutputBus->flags) & MA_NODE_OUTPUT_BUS_FLAG_HAS_READ) != 0; } static void ma_node_output_bus_set_is_attached(ma_node_output_bus* pOutputBus, ma_bool32 isAttached) { - ma_atomic_exchange_32(&pOutputBus->isAttached, isAttached); + c89atomic_exchange_32(&pOutputBus->isAttached, isAttached); } static ma_bool32 ma_node_output_bus_is_attached(ma_node_output_bus* pOutputBus) { - return ma_atomic_load_32(&pOutputBus->isAttached); + return c89atomic_load_32(&pOutputBus->isAttached); } @@ -72173,14 +70923,14 @@ static ma_result ma_node_output_bus_set_volume(ma_node_output_bus* pOutputBus, f volume = 0.0f; } - ma_atomic_exchange_f32(&pOutputBus->volume, volume); + c89atomic_exchange_f32(&pOutputBus->volume, volume); return MA_SUCCESS; } static float ma_node_output_bus_get_volume(const ma_node_output_bus* pOutputBus) { - return ma_atomic_load_f32((float*)&pOutputBus->volume); + return c89atomic_load_f32((float*)&pOutputBus->volume); } @@ -72217,17 +70967,17 @@ static void ma_node_input_bus_unlock(ma_node_input_bus* pInputBus) static void ma_node_input_bus_next_begin(ma_node_input_bus* pInputBus) { - ma_atomic_fetch_add_32(&pInputBus->nextCounter, 1); + c89atomic_fetch_add_32(&pInputBus->nextCounter, 1); } static void ma_node_input_bus_next_end(ma_node_input_bus* pInputBus) { - ma_atomic_fetch_sub_32(&pInputBus->nextCounter, 1); + c89atomic_fetch_sub_32(&pInputBus->nextCounter, 1); } static ma_uint32 ma_node_input_bus_get_next_counter(ma_node_input_bus* pInputBus) { - return ma_atomic_load_32(&pInputBus->nextCounter); + return c89atomic_load_32(&pInputBus->nextCounter); } @@ -72255,28 +71005,28 @@ static void ma_node_input_bus_detach__no_output_bus_lock(ma_node_input_bus* pInp *not* using a lock when iterating over the list in the audio thread. We therefore need to craft this in a way such that the iteration on the audio thread doesn't break. - The first thing to do is swap out the "next" pointer of the previous output bus with the + The the first thing to do is swap out the "next" pointer of the previous output bus with the new "next" output bus. This is the operation that matters for iteration on the audio thread. After that, the previous pointer on the new "next" pointer needs to be updated, after which point the linked list will be in a good state. */ ma_node_input_bus_lock(pInputBus); { - ma_node_output_bus* pOldPrev = (ma_node_output_bus*)ma_atomic_load_ptr(&pOutputBus->pPrev); - ma_node_output_bus* pOldNext = (ma_node_output_bus*)ma_atomic_load_ptr(&pOutputBus->pNext); + ma_node_output_bus* pOldPrev = (ma_node_output_bus*)c89atomic_load_ptr(&pOutputBus->pPrev); + ma_node_output_bus* pOldNext = (ma_node_output_bus*)c89atomic_load_ptr(&pOutputBus->pNext); if (pOldPrev != NULL) { - ma_atomic_exchange_ptr(&pOldPrev->pNext, pOldNext); /* <-- This is where the output bus is detached from the list. */ + c89atomic_exchange_ptr(&pOldPrev->pNext, pOldNext); /* <-- This is where the output bus is detached from the list. */ } if (pOldNext != NULL) { - ma_atomic_exchange_ptr(&pOldNext->pPrev, pOldPrev); /* <-- This is required for detachment. */ + c89atomic_exchange_ptr(&pOldNext->pPrev, pOldPrev); /* <-- This is required for detachment. */ } } ma_node_input_bus_unlock(pInputBus); /* At this point the output bus is detached and the linked list is completely unaware of it. Reset some data for safety. */ - ma_atomic_exchange_ptr(&pOutputBus->pNext, NULL); /* Using atomic exchanges here, mainly for the benefit of analysis tools which don't always recognize spinlocks. */ - ma_atomic_exchange_ptr(&pOutputBus->pPrev, NULL); /* As above. */ + c89atomic_exchange_ptr(&pOutputBus->pNext, NULL); /* Using atomic exchanges here, mainly for the benefit of analysis tools which don't always recognize spinlocks. */ + c89atomic_exchange_ptr(&pOutputBus->pPrev, NULL); /* As above. */ pOutputBus->pInputNode = NULL; pOutputBus->inputNodeInputBusIndex = 0; @@ -72300,7 +71050,7 @@ static void ma_node_input_bus_detach__no_output_bus_lock(ma_node_input_bus* pInp } /* Part 2: Wait for any reads to complete. */ - while (ma_atomic_load_32(&pOutputBus->refCount) > 0) { + while (c89atomic_load_32(&pOutputBus->refCount) > 0) { ma_yield(); } @@ -72331,7 +71081,7 @@ static void ma_node_input_bus_attach(ma_node_input_bus* pInputBus, ma_node_outpu ma_node_output_bus_lock(pOutputBus); { - ma_node_output_bus* pOldInputNode = (ma_node_output_bus*)ma_atomic_load_ptr(&pOutputBus->pInputNode); + ma_node_output_bus* pOldInputNode = (ma_node_output_bus*)c89atomic_load_ptr(&pOutputBus->pInputNode); /* Detach from any existing attachment first if necessary. */ if (pOldInputNode != NULL) { @@ -72348,7 +71098,7 @@ static void ma_node_input_bus_attach(ma_node_input_bus* pInputBus, ma_node_outpu /* Now we need to attach the output bus to the linked list. This involves updating two pointers on two different output buses so I'm going to go ahead and keep this simple and just use a lock. - There are ways to do this without a lock, but it's just too hard to maintain for its value. + There are ways to do this without a lock, but it's just too hard to maintain for it's value. Although we're locking here, it's important to remember that we're *not* locking when iterating and reading audio data since that'll be running on the audio thread. As a result we need to be @@ -72361,18 +71111,18 @@ static void ma_node_input_bus_attach(ma_node_input_bus* pInputBus, ma_node_outpu ma_node_input_bus_lock(pInputBus); { ma_node_output_bus* pNewPrev = &pInputBus->head; - ma_node_output_bus* pNewNext = (ma_node_output_bus*)ma_atomic_load_ptr(&pInputBus->head.pNext); + ma_node_output_bus* pNewNext = (ma_node_output_bus*)c89atomic_load_ptr(&pInputBus->head.pNext); /* Update the local output bus. */ - ma_atomic_exchange_ptr(&pOutputBus->pPrev, pNewPrev); - ma_atomic_exchange_ptr(&pOutputBus->pNext, pNewNext); + c89atomic_exchange_ptr(&pOutputBus->pPrev, pNewPrev); + c89atomic_exchange_ptr(&pOutputBus->pNext, pNewNext); /* Update the other output buses to point back to the local output bus. */ - ma_atomic_exchange_ptr(&pInputBus->head.pNext, pOutputBus); /* <-- This is where the output bus is actually attached to the input bus. */ + c89atomic_exchange_ptr(&pInputBus->head.pNext, pOutputBus); /* <-- This is where the output bus is actually attached to the input bus. */ /* Do the previous pointer last. This is only used for detachment. */ if (pNewNext != NULL) { - ma_atomic_exchange_ptr(&pNewNext->pPrev, pOutputBus); + c89atomic_exchange_ptr(&pNewNext->pPrev, pOutputBus); } } ma_node_input_bus_unlock(pInputBus); @@ -72400,7 +71150,7 @@ static ma_node_output_bus* ma_node_input_bus_next(ma_node_input_bus* pInputBus, { pNext = pOutputBus; for (;;) { - pNext = (ma_node_output_bus*)ma_atomic_load_ptr(&pNext->pNext); + pNext = (ma_node_output_bus*)c89atomic_load_ptr(&pNext->pNext); if (pNext == NULL) { break; /* Reached the end. */ } @@ -72415,11 +71165,11 @@ static ma_node_output_bus* ma_node_input_bus_next(ma_node_input_bus* pInputBus, /* We need to increment the reference count of the selected node. */ if (pNext != NULL) { - ma_atomic_fetch_add_32(&pNext->refCount, 1); + c89atomic_fetch_add_32(&pNext->refCount, 1); } /* The previous node is no longer being referenced. */ - ma_atomic_fetch_sub_32(&pOutputBus->refCount, 1); + c89atomic_fetch_sub_32(&pOutputBus->refCount, 1); } ma_node_input_bus_next_end(pInputBus); @@ -72441,9 +71191,11 @@ static ma_result ma_node_input_bus_read_pcm_frames(ma_node* pInputNode, ma_node_ ma_uint32 inputChannels; ma_bool32 doesOutputBufferHaveContent = MA_FALSE; + (void)pInputNode; /* Not currently used. */ + /* This will be called from the audio thread which means we can't be doing any locking. Basically, - this function will not perform any locking, whereas attaching and detaching will, but crafted in + this function will not perfom any locking, whereas attaching and detaching will, but crafted in such a way that we don't need to perform any locking here. The important thing to remember is to always iterate in a forward direction. @@ -72489,12 +71241,19 @@ static ma_result ma_node_input_bus_read_pcm_frames(ma_node* pInputNode, ma_node_ if (pFramesOut != NULL) { /* Read. */ + float temp[MA_DATA_CONVERTER_STACK_BUFFER_SIZE / sizeof(float)]; + ma_uint32 tempCapInFrames = ma_countof(temp) / inputChannels; + while (framesProcessed < frameCount) { float* pRunningFramesOut; ma_uint32 framesToRead; - ma_uint32 framesJustRead = 0; + ma_uint32 framesJustRead; framesToRead = frameCount - framesProcessed; + if (framesToRead > tempCapInFrames) { + framesToRead = tempCapInFrames; + } + pRunningFramesOut = ma_offset_pcm_frames_ptr_f32(pFramesOut, framesProcessed, inputChannels); if (doesOutputBufferHaveContent == MA_FALSE) { @@ -72502,32 +71261,11 @@ static ma_result ma_node_input_bus_read_pcm_frames(ma_node* pInputNode, ma_node_ result = ma_node_read_pcm_frames(pOutputBus->pNode, pOutputBus->outputBusIndex, pRunningFramesOut, framesToRead, &framesJustRead, globalTime + framesProcessed); } else { /* Slow path. Not the first attachment. Mixing required. */ - ma_uint32 preMixBufferCapInFrames = ((ma_node_base*)pInputNode)->cachedDataCapInFramesPerBus; - float* pPreMixBuffer = (float*)ma_stack_alloc(((ma_node_base*)pInputNode)->pNodeGraph->pPreMixStack, preMixBufferCapInFrames * inputChannels * sizeof(float)); - - if (pPreMixBuffer == NULL) { - /* - If you're hitting this assert it means you've got an unusually deep chain of nodes, you've got an excessively large processing - size, or you have a combination of both, and as a result have run out of stack space. You can increase this using the - preMixStackSizeInBytes variable in ma_node_graph_config. If you're using ma_engine, you can do it via the preMixStackSizeInBytes - variable in ma_engine_config. It defaults to 512KB per output channel. - */ - MA_ASSERT(MA_FALSE); - } else { - if (framesToRead > preMixBufferCapInFrames) { - framesToRead = preMixBufferCapInFrames; + result = ma_node_read_pcm_frames(pOutputBus->pNode, pOutputBus->outputBusIndex, temp, framesToRead, &framesJustRead, globalTime + framesProcessed); + if (result == MA_SUCCESS || result == MA_AT_END) { + if (isSilentOutput == MA_FALSE) { /* Don't mix if the node outputs silence. */ + ma_mix_pcm_frames_f32(pRunningFramesOut, temp, framesJustRead, inputChannels, /*volume*/1); } - - result = ma_node_read_pcm_frames(pOutputBus->pNode, pOutputBus->outputBusIndex, pPreMixBuffer, framesToRead, &framesJustRead, globalTime + framesProcessed); - if (result == MA_SUCCESS || result == MA_AT_END) { - if (isSilentOutput == MA_FALSE) { /* Don't mix if the node outputs silence. */ - ma_mix_pcm_frames_f32(pRunningFramesOut, pPreMixBuffer, framesJustRead, inputChannels, /*volume*/1); - } - } - - /* The pre-mix buffer is no longer required. */ - ma_stack_free(((ma_node_base*)pInputNode)->pNodeGraph->pPreMixStack, pPreMixBuffer); - pPreMixBuffer = NULL; } } @@ -72582,25 +71320,6 @@ MA_API ma_node_config ma_node_config_init(void) return config; } -static ma_uint16 ma_node_config_get_cache_size_in_frames(const ma_node_config* pConfig, const ma_node_graph* pNodeGraph) -{ - ma_uint32 cacheSizeInFrames; - - (void)pConfig; - - if (pNodeGraph->processingSizeInFrames > 0) { - cacheSizeInFrames = pNodeGraph->processingSizeInFrames; - } else { - cacheSizeInFrames = MA_DEFAULT_NODE_CACHE_CAP_IN_FRAMES_PER_BUS; - } - - if (cacheSizeInFrames > 0xFFFF) { - cacheSizeInFrames = 0xFFFF; - } - - return (ma_uint16)cacheSizeInFrames; -} - static ma_result ma_node_detach_full(ma_node* pNode); @@ -72755,7 +71474,7 @@ static ma_result ma_node_get_heap_layout(ma_node_graph* pNodeGraph, const ma_nod /* Cached audio data. - We need to allocate memory for caching both input and output data. We have an optimization + We need to allocate memory for a caching both input and output data. We have an optimization where no caching is necessary for specific conditions: - The node has 0 inputs and 1 output. @@ -72774,18 +71493,14 @@ static ma_result ma_node_get_heap_layout(ma_node_graph* pNodeGraph, const ma_nod } else { /* Slow path. Cache needed. */ size_t cachedDataSizeInBytes = 0; - ma_uint32 cacheCapInFrames; ma_uint32 iBus; - /* The capacity of the cache is based on our callback processing size. */ - cacheCapInFrames = ma_node_config_get_cache_size_in_frames(pConfig, pNodeGraph); - for (iBus = 0; iBus < inputBusCount; iBus += 1) { - cachedDataSizeInBytes += cacheCapInFrames * ma_get_bytes_per_frame(ma_format_f32, pConfig->pInputChannels[iBus]); + cachedDataSizeInBytes += pNodeGraph->nodeCacheCapInFrames * ma_get_bytes_per_frame(ma_format_f32, pConfig->pInputChannels[iBus]); } for (iBus = 0; iBus < outputBusCount; iBus += 1) { - cachedDataSizeInBytes += cacheCapInFrames * ma_get_bytes_per_frame(ma_format_f32, pConfig->pOutputChannels[iBus]); + cachedDataSizeInBytes += pNodeGraph->nodeCacheCapInFrames * ma_get_bytes_per_frame(ma_format_f32, pConfig->pOutputChannels[iBus]); } pHeapLayout->cachedDataOffset = pHeapLayout->sizeInBytes; @@ -72864,19 +71579,20 @@ MA_API ma_result ma_node_init_preallocated(ma_node_graph* pNodeGraph, const ma_n } if (heapLayout.outputBusOffset != MA_SIZE_MAX) { - pNodeBase->pOutputBuses = (ma_node_output_bus*)ma_offset_ptr(pHeap, heapLayout.outputBusOffset); + pNodeBase->pOutputBuses = (ma_node_output_bus*)ma_offset_ptr(pHeap, heapLayout.inputBusOffset); } else { pNodeBase->pOutputBuses = pNodeBase->_outputBuses; } if (heapLayout.cachedDataOffset != MA_SIZE_MAX) { pNodeBase->pCachedData = (float*)ma_offset_ptr(pHeap, heapLayout.cachedDataOffset); - pNodeBase->cachedDataCapInFramesPerBus = ma_node_config_get_cache_size_in_frames(pConfig, pNodeGraph); + pNodeBase->cachedDataCapInFramesPerBus = pNodeGraph->nodeCacheCapInFrames; } else { pNodeBase->pCachedData = NULL; } + /* We need to run an initialization step for each input and output bus. */ for (iInputBus = 0; iInputBus < ma_node_get_input_bus_count(pNodeBase); iInputBus += 1) { result = ma_node_input_bus_init(pConfig->pInputChannels[iInputBus], &pNodeBase->pInputBuses[iInputBus]); @@ -73050,7 +71766,7 @@ static ma_result ma_node_detach_full(ma_node* pNode) /* At this point all output buses will have been detached from the graph and we can be guaranteed - that none of its input nodes will be getting processed by the graph. We can detach these + that none of it's input nodes will be getting processed by the graph. We can detach these without needing to worry about the audio thread touching them. */ for (iInputBus = 0; iInputBus < ma_node_get_input_bus_count(pNode); iInputBus += 1) { @@ -73065,7 +71781,7 @@ static ma_result ma_node_detach_full(ma_node* pNode) linked list logic. We don't need to worry about the audio thread referencing these because the step above severed the connection to the graph. */ - for (pOutputBus = (ma_node_output_bus*)ma_atomic_load_ptr(&pInputBus->head.pNext); pOutputBus != NULL; pOutputBus = (ma_node_output_bus*)ma_atomic_load_ptr(&pInputBus->head.pNext)) { + for (pOutputBus = (ma_node_output_bus*)c89atomic_load_ptr(&pInputBus->head.pNext); pOutputBus != NULL; pOutputBus = (ma_node_output_bus*)c89atomic_load_ptr(&pOutputBus->pNext)) { ma_node_detach_output_bus(pOutputBus->pNode, pOutputBus->outputBusIndex); /* This won't do any waiting in practice and should be efficient. */ } } @@ -73087,7 +71803,7 @@ MA_API ma_result ma_node_detach_output_bus(ma_node* pNode, ma_uint32 outputBusIn return MA_INVALID_ARGS; /* Invalid output bus index. */ } - /* We need to lock the output bus because we need to inspect the input node and grab its input bus. */ + /* We need to lock the output bus because we need to inspect the input node and grab it's input bus. */ ma_node_output_bus_lock(&pNodeBase->pOutputBuses[outputBusIndex]); { pInputNodeBase = (ma_node_base*)pNodeBase->pOutputBuses[outputBusIndex].pInputNode; @@ -73181,7 +71897,7 @@ MA_API ma_result ma_node_set_state(ma_node* pNode, ma_node_state state) return MA_INVALID_ARGS; } - ma_atomic_exchange_i32(&pNodeBase->state, state); + c89atomic_exchange_i32(&pNodeBase->state, state); return MA_SUCCESS; } @@ -73194,7 +71910,7 @@ MA_API ma_node_state ma_node_get_state(const ma_node* pNode) return ma_node_state_stopped; } - return (ma_node_state)ma_atomic_load_i32(&pNodeBase->state); + return (ma_node_state)c89atomic_load_i32(&pNodeBase->state); } MA_API ma_result ma_node_set_state_time(ma_node* pNode, ma_node_state state, ma_uint64 globalTime) @@ -73208,7 +71924,7 @@ MA_API ma_result ma_node_set_state_time(ma_node* pNode, ma_node_state state, ma_ return MA_INVALID_ARGS; } - ma_atomic_exchange_64(&((ma_node_base*)pNode)->stateTimes[state], globalTime); + c89atomic_exchange_64(&((ma_node_base*)pNode)->stateTimes[state], globalTime); return MA_SUCCESS; } @@ -73224,7 +71940,7 @@ MA_API ma_uint64 ma_node_get_state_time(const ma_node* pNode, ma_node_state stat return 0; } - return ma_atomic_load_64(&((ma_node_base*)pNode)->stateTimes[state]); + return c89atomic_load_64(&((ma_node_base*)pNode)->stateTimes[state]); } MA_API ma_node_state ma_node_get_state_by_time(const ma_node* pNode, ma_uint64 globalTime) @@ -73253,7 +71969,7 @@ MA_API ma_node_state ma_node_get_state_by_time_range(const ma_node* pNode, ma_ui /* Getting here means the node is marked as started, but it may still not be truly started due to - its start time not having been reached yet. Also, the stop time may have also been reached in + it's start time not having been reached yet. Also, the stop time may have also been reached in which case it'll be considered stopped. */ if (ma_node_get_state_time(pNode, ma_node_state_started) > globalTimeBeg) { @@ -73264,7 +71980,7 @@ MA_API ma_node_state ma_node_get_state_by_time_range(const ma_node* pNode, ma_ui return ma_node_state_stopped; /* Stop time has been reached. */ } - /* Getting here means the node is marked as started and is within its start/stop times. */ + /* Getting here means the node is marked as started and is within it's start/stop times. */ return ma_node_state_started; } @@ -73274,7 +71990,7 @@ MA_API ma_uint64 ma_node_get_time(const ma_node* pNode) return 0; } - return ma_atomic_load_64(&((ma_node_base*)pNode)->localTime); + return c89atomic_load_64(&((ma_node_base*)pNode)->localTime); } MA_API ma_result ma_node_set_time(ma_node* pNode, ma_uint64 localTime) @@ -73283,7 +71999,7 @@ MA_API ma_result ma_node_set_time(ma_node* pNode, ma_uint64 localTime) return MA_INVALID_ARGS; } - ma_atomic_exchange_64(&((ma_node_base*)pNode)->localTime, localTime); + c89atomic_exchange_64(&((ma_node_base*)pNode)->localTime, localTime); return MA_SUCCESS; } @@ -73354,11 +72070,11 @@ static ma_result ma_node_read_pcm_frames(ma_node* pNode, ma_uint32 outputBusInde /* At this point we know that we are inside our start/stop times. However, we may need to adjust - our frame count and output pointer to accommodate since we could be straddling the time period + our frame count and output pointer to accomodate since we could be straddling the time period that this function is getting called for. It's possible (and likely) that the start time does not line up with the output buffer. We - therefore need to offset it by a number of frames to accommodate. The same thing applies for + therefore need to offset it by a number of frames to accomodate. The same thing applies for the stop time. */ timeOffsetBeg = (globalTimeBeg < startTime) ? (ma_uint32)(globalTimeEnd - startTime) : 0; @@ -73426,12 +72142,12 @@ static ma_result ma_node_read_pcm_frames(ma_node* pNode, ma_uint32 outputBusInde frameCountOut = totalFramesRead; if (totalFramesRead > 0) { - ma_node_process_pcm_frames_internal(pNode, (const float**)ppFramesIn, &frameCountIn, ppFramesOut, &frameCountOut); /* From GCC: expected 'const float **' but argument is of type 'float **'. Shouldn't this be implicit? Explicit cast to silence the warning. */ + ma_node_process_pcm_frames_internal(pNode, (const float**)ppFramesIn, &frameCountIn, ppFramesOut, &frameCountOut); /* From GCC: expected 'const float **' but argument is of type 'float **'. Shouldn't this be implicit? Excplicit cast to silence the warning. */ } /* A passthrough should never have modified the input and output frame counts. If you're - triggering these asserts you need to fix your processing callback. + triggering these assers you need to fix your processing callback. */ MA_ASSERT(frameCountIn == totalFramesRead); MA_ASSERT(frameCountOut == totalFramesRead); @@ -73609,7 +72325,7 @@ static ma_result ma_node_read_pcm_frames(ma_node* pNode, ma_uint32 outputBusInde frames available right now. */ if (frameCountIn > 0 || (pNodeBase->vtable->flags & MA_NODE_FLAG_DIFFERENT_PROCESSING_RATES) != 0) { - ma_node_process_pcm_frames_internal(pNode, (const float**)ppFramesIn, &frameCountIn, ppFramesOut, &frameCountOut); /* From GCC: expected 'const float **' but argument is of type 'float **'. Shouldn't this be implicit? Explicit cast to silence the warning. */ + ma_node_process_pcm_frames_internal(pNode, (const float**)ppFramesIn, &frameCountIn, ppFramesOut, &frameCountOut); /* From GCC: expected 'const float **' but argument is of type 'float **'. Shouldn't this be implicit? Excplicit cast to silence the warning. */ } else { frameCountOut = 0; /* No data was processed. */ } @@ -73658,7 +72374,7 @@ static ma_result ma_node_read_pcm_frames(ma_node* pNode, ma_uint32 outputBusInde ma_apply_volume_factor_f32(pFramesOut, totalFramesRead * ma_node_get_output_channels(pNodeBase, outputBusIndex), ma_node_output_bus_get_volume(&pNodeBase->pOutputBuses[outputBusIndex])); /* Advance our local time forward. */ - ma_atomic_fetch_add_64(&pNodeBase->localTime, (ma_uint64)totalFramesRead); + c89atomic_fetch_add_64(&pNodeBase->localTime, (ma_uint64)totalFramesRead); *pFramesRead = totalFramesRead + timeOffsetBeg; /* Must include the silenced section at the start of the buffer. */ return result; @@ -74785,7 +73501,7 @@ Engine static void ma_sound_set_at_end(ma_sound* pSound, ma_bool32 atEnd) { MA_ASSERT(pSound != NULL); - ma_atomic_exchange_32(&pSound->atEnd, atEnd); + c89atomic_exchange_32(&pSound->atEnd, atEnd); /* Fire any callbacks or events. */ if (atEnd) { @@ -74798,7 +73514,7 @@ static void ma_sound_set_at_end(ma_sound* pSound, ma_bool32 atEnd) static ma_bool32 ma_sound_get_at_end(const ma_sound* pSound) { MA_ASSERT(pSound != NULL); - return ma_atomic_load_32(&pSound->atEnd); + return c89atomic_load_32(&pSound->atEnd); } @@ -74824,7 +73540,7 @@ static void ma_engine_node_update_pitch_if_required(ma_engine_node* pEngineNode) MA_ASSERT(pEngineNode != NULL); - newPitch = ma_atomic_load_explicit_f32(&pEngineNode->pitch, ma_atomic_memory_order_acquire); + newPitch = c89atomic_load_explicit_f32(&pEngineNode->pitch, c89atomic_memory_order_acquire); if (pEngineNode->oldPitch != newPitch) { pEngineNode->oldPitch = newPitch; @@ -74846,15 +73562,15 @@ static ma_bool32 ma_engine_node_is_pitching_enabled(const ma_engine_node* pEngin { MA_ASSERT(pEngineNode != NULL); - /* Don't try to be clever by skipping resampling in the pitch=1 case or else you'll glitch when moving away from 1. */ - return !ma_atomic_load_explicit_32(&pEngineNode->isPitchDisabled, ma_atomic_memory_order_acquire); + /* Don't try to be clever by skiping resampling in the pitch=1 case or else you'll glitch when moving away from 1. */ + return !c89atomic_load_explicit_32(&pEngineNode->isPitchDisabled, c89atomic_memory_order_acquire); } static ma_bool32 ma_engine_node_is_spatialization_enabled(const ma_engine_node* pEngineNode) { MA_ASSERT(pEngineNode != NULL); - return !ma_atomic_load_explicit_32(&pEngineNode->isSpatializationDisabled, ma_atomic_memory_order_acquire); + return !c89atomic_load_explicit_32(&pEngineNode->isSpatializationDisabled, c89atomic_memory_order_acquire); } static ma_uint64 ma_engine_node_get_required_input_frame_count(const ma_engine_node* pEngineNode, ma_uint64 outputFrameCount) @@ -74883,7 +73599,7 @@ static ma_result ma_engine_node_set_volume(ma_engine_node* pEngineNode, float vo /* If we're not smoothing we should bypass the volume gainer entirely. */ if (pEngineNode->volumeSmoothTimeInPCMFrames == 0) { - /* We should always have an active spatializer because it can be enabled and disabled dynamically. We can just use that for holding our volume. */ + /* We should always have an active spatializer because it can be enabled and disabled dynamically. We can just use that for hodling our volume. */ ma_spatializer_set_master_volume(&pEngineNode->spatializer, volume); } else { /* We're using volume smoothing, so apply the master volume to the gainer. */ @@ -74934,26 +73650,6 @@ static void ma_engine_node_process_pcm_frames__general(ma_engine_node* pEngineNo totalFramesProcessedIn = 0; totalFramesProcessedOut = 0; - /* Update the fader if applicable. */ - { - ma_uint64 fadeLengthInFrames = ma_atomic_uint64_get(&pEngineNode->fadeSettings.fadeLengthInFrames); - if (fadeLengthInFrames != ~(ma_uint64)0) { - float fadeVolumeBeg = ma_atomic_float_get(&pEngineNode->fadeSettings.volumeBeg); - float fadeVolumeEnd = ma_atomic_float_get(&pEngineNode->fadeSettings.volumeEnd); - ma_int64 fadeStartOffsetInFrames = (ma_int64)ma_atomic_uint64_get(&pEngineNode->fadeSettings.absoluteGlobalTimeInFrames); - if (fadeStartOffsetInFrames == (ma_int64)(~(ma_uint64)0)) { - fadeStartOffsetInFrames = 0; - } else { - fadeStartOffsetInFrames -= ma_engine_get_time_in_pcm_frames(pEngineNode->pEngine); - } - - ma_fader_set_fade_ex(&pEngineNode->fader, fadeVolumeBeg, fadeVolumeEnd, fadeLengthInFrames, fadeStartOffsetInFrames); - - /* Reset the fade length so we don't erroneously apply it again. */ - ma_atomic_uint64_set(&pEngineNode->fadeSettings.fadeLengthInFrames, ~(ma_uint64)0); - } - } - isPitchingEnabled = ma_engine_node_is_pitching_enabled(pEngineNode); isFadingEnabled = pEngineNode->fader.volumeBeg != 1 || pEngineNode->fader.volumeEnd != 1; isSpatializationEnabled = ma_engine_node_is_spatialization_enabled(pEngineNode); @@ -74972,10 +73668,10 @@ static void ma_engine_node_process_pcm_frames__general(ma_engine_node* pEngineNo the output buffer and then do all effects from that point directly in the output buffer in-place. - Note that we're always running the resampler if pitching is enabled, even when the pitch - is 1. If we try to be clever and skip resampling when the pitch is 1, we'll get a glitch - when we move away from 1, back to 1, and then away from 1 again. We'll want to implement - any pitch=1 optimizations in the resampler itself. + Note that we're always running the resampler. If we try to be clever and skip resampling + when the pitch is 1, we'll get a glitch when we move away from 1, back to 1, and then + away from 1 again. We'll want to implement any pitch=1 optimizations in the resampler + itself. There's a small optimization here that we'll utilize since it might be a fairly common case. When the input and output channel counts are the same, we'll read straight into the @@ -75143,14 +73839,14 @@ static void ma_engine_node_process_pcm_frames__sound(ma_node* pNode, const float } /* If we're seeking, do so now before reading. */ - seekTarget = ma_atomic_load_64(&pSound->seekTarget); + seekTarget = c89atomic_load_64(&pSound->seekTarget); if (seekTarget != MA_SEEK_TARGET_NONE) { ma_data_source_seek_to_pcm_frame(pSound->pDataSource, seekTarget); /* Any time-dependant effects need to have their times updated. */ ma_node_set_time(pSound, seekTarget); - ma_atomic_exchange_64(&pSound->seekTarget, MA_SEEK_TARGET_NONE); + c89atomic_exchange_64(&pSound->seekTarget, MA_SEEK_TARGET_NONE); } /* @@ -75198,7 +73894,7 @@ static void ma_engine_node_process_pcm_frames__sound(ma_node* pNode, const float ma_sound_set_at_end(pSound, MA_TRUE); /* This will be set to false in ma_sound_start(). */ } - pRunningFramesOut = ma_offset_pcm_frames_ptr_f32(ppFramesOut[0], totalFramesRead, ma_node_get_output_channels(pNode, 0)); + pRunningFramesOut = ma_offset_pcm_frames_ptr_f32(ppFramesOut[0], totalFramesRead, ma_engine_get_channels(ma_sound_get_engine(pSound))); frameCountIn = (ma_uint32)framesJustRead; frameCountOut = framesRemaining; @@ -75473,10 +74169,6 @@ MA_API ma_result ma_engine_node_init_preallocated(const ma_engine_node_config* p pEngineNode->isPitchDisabled = pConfig->isPitchDisabled; pEngineNode->isSpatializationDisabled = pConfig->isSpatializationDisabled; pEngineNode->pinnedListenerIndex = pConfig->pinnedListenerIndex; - ma_atomic_float_set(&pEngineNode->fadeSettings.volumeBeg, 1); - ma_atomic_float_set(&pEngineNode->fadeSettings.volumeEnd, 1); - ma_atomic_uint64_set(&pEngineNode->fadeSettings.fadeLengthInFrames, (~(ma_uint64)0)); - ma_atomic_uint64_set(&pEngineNode->fadeSettings.absoluteGlobalTimeInFrames, (~(ma_uint64)0)); /* <-- Indicates that the fade should start immediately. */ channelsIn = (pConfig->channelsIn != 0) ? pConfig->channelsIn : ma_engine_get_channels(pConfig->pEngine); channelsOut = (pConfig->channelsOut != 0) ? pConfig->channelsOut : ma_engine_get_channels(pConfig->pEngine); @@ -75529,7 +74221,7 @@ MA_API ma_result ma_engine_node_init_preallocated(const ma_engine_node_config* p /* - Spatialization comes next. We spatialize based on the node's output channel count. It's up the caller to + Spatialization comes next. We spatialize based ont he node's output channel count. It's up the caller to ensure channels counts link up correctly in the node graph. */ spatializerConfig = ma_engine_node_spatializer_config_init(&baseNodeConfig); @@ -75719,21 +74411,6 @@ static void ma_engine_data_callback_internal(ma_device* pDevice, void* pFramesOu ma_engine_read_pcm_frames(pEngine, pFramesOut, frameCount, NULL); } - -static ma_uint32 ma_device__get_processing_size_in_frames(ma_device* pDevice) -{ - /* - The processing size is the period size. The device can have a fixed sized processing size, or - it can be decided by the backend in which case it can be variable. - */ - if (pDevice->playback.intermediaryBufferCap > 0) { - /* Using a fixed sized processing callback. */ - return pDevice->playback.intermediaryBufferCap; - } else { - /* Not using a fixed sized processing callback. Need to estimate the processing size based on the backend. */ - return pDevice->playback.internalPeriodSizeInFrames; - } -} #endif MA_API ma_result ma_engine_init(const ma_engine_config* pConfig, ma_engine* pEngine) @@ -75759,8 +74436,6 @@ MA_API ma_result ma_engine_init(const ma_engine_config* pConfig, ma_engine* pEng pEngine->monoExpansionMode = engineConfig.monoExpansionMode; pEngine->defaultVolumeSmoothTimeInPCMFrames = engineConfig.defaultVolumeSmoothTimeInPCMFrames; - pEngine->onProcess = engineConfig.onProcess; - pEngine->pProcessUserData = engineConfig.pProcessUserData; ma_allocation_callbacks_init_copy(&pEngine->allocationCallbacks, &engineConfig.allocationCallbacks); #if !defined(MA_NO_RESOURCE_MANAGER) @@ -75787,7 +74462,7 @@ MA_API ma_result ma_engine_init(const ma_engine_config* pConfig, ma_engine* pEng deviceConfig.playback.format = ma_format_f32; deviceConfig.playback.channels = engineConfig.channels; deviceConfig.sampleRate = engineConfig.sampleRate; - deviceConfig.dataCallback = (engineConfig.dataCallback != NULL) ? engineConfig.dataCallback : ma_engine_data_callback_internal; + deviceConfig.dataCallback = ma_engine_data_callback_internal; deviceConfig.pUserData = pEngine; deviceConfig.notificationCallback = engineConfig.notificationCallback; deviceConfig.periodSizeInFrames = engineConfig.periodSizeInFrames; @@ -75827,14 +74502,6 @@ MA_API ma_result ma_engine_init(const ma_engine_config* pConfig, ma_engine* pEng if (pEngine->pDevice != NULL) { engineConfig.channels = pEngine->pDevice->playback.channels; engineConfig.sampleRate = pEngine->pDevice->sampleRate; - - /* - The processing size used by the engine is determined by engineConfig.periodSizeInFrames. We want - to make this equal to what the device is using for it's period size. If we don't do that, it's - possible that the node graph will split it's processing into multiple passes which can introduce - glitching. - */ - engineConfig.periodSizeInFrames = ma_device__get_processing_size_in_frames(pEngine->pDevice); } } #endif @@ -75861,10 +74528,9 @@ MA_API ma_result ma_engine_init(const ma_engine_config* pConfig, ma_engine* pEng } - /* The engine is a node graph. This needs to be initialized after we have the device so we can determine the channel count. */ + /* The engine is a node graph. This needs to be initialized after we have the device so we can can determine the channel count. */ nodeGraphConfig = ma_node_graph_config_init(engineConfig.channels); - nodeGraphConfig.processingSizeInFrames = engineConfig.periodSizeInFrames; - nodeGraphConfig.preMixStackSizeInBytes = engineConfig.preMixStackSizeInBytes; + nodeGraphConfig.nodeCacheCapInFrames = (engineConfig.periodSizeInFrames > 0xFFFF) ? 0xFFFF : (ma_uint16)engineConfig.periodSizeInFrames; result = ma_node_graph_init(&nodeGraphConfig, &pEngine->allocationCallbacks, &pEngine->nodeGraph); if (result != MA_SUCCESS) { @@ -75944,8 +74610,8 @@ MA_API ma_result ma_engine_init(const ma_engine_config* pConfig, ma_engine* pEng ma_allocation_callbacks_init_copy(&resourceManagerConfig.allocationCallbacks, &pEngine->allocationCallbacks); resourceManagerConfig.pVFS = engineConfig.pResourceManagerVFS; - /* The Emscripten build cannot use threads unless it's targeting pthreads. */ - #if defined(MA_EMSCRIPTEN) && !defined(__EMSCRIPTEN_PTHREADS__) + /* The Emscripten build cannot use threads. */ + #if defined(MA_EMSCRIPTEN) { resourceManagerConfig.jobThreadCount = 0; resourceManagerConfig.flags |= MA_RESOURCE_MANAGER_FLAG_NO_THREADING; @@ -76068,27 +74734,7 @@ MA_API void ma_engine_uninit(ma_engine* pEngine) MA_API ma_result ma_engine_read_pcm_frames(ma_engine* pEngine, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead) { - ma_result result; - ma_uint64 framesRead = 0; - - if (pFramesRead != NULL) { - *pFramesRead = 0; - } - - result = ma_node_graph_read_pcm_frames(&pEngine->nodeGraph, pFramesOut, frameCount, &framesRead); - if (result != MA_SUCCESS) { - return result; - } - - if (pFramesRead != NULL) { - *pFramesRead = framesRead; - } - - if (pEngine->onProcess) { - pEngine->onProcess(pEngine->pProcessUserData, (float*)pFramesOut, framesRead); /* Safe cast to float* because the engine always works on floating point samples. */ - } - - return MA_SUCCESS; + return ma_node_graph_read_pcm_frames(&pEngine->nodeGraph, pFramesOut, frameCount, pFramesRead); } MA_API ma_node_graph* ma_engine_get_node_graph(ma_engine* pEngine) @@ -76274,23 +74920,13 @@ MA_API ma_result ma_engine_set_volume(ma_engine* pEngine, float volume) return ma_node_set_output_bus_volume(ma_node_graph_get_endpoint(&pEngine->nodeGraph), 0, volume); } -MA_API float ma_engine_get_volume(ma_engine* pEngine) +MA_API ma_result ma_engine_set_gain_db(ma_engine* pEngine, float gainDB) { if (pEngine == NULL) { - return 0; + return MA_INVALID_ARGS; } - return ma_node_get_output_bus_volume(ma_node_graph_get_endpoint(&pEngine->nodeGraph), 0); -} - -MA_API ma_result ma_engine_set_gain_db(ma_engine* pEngine, float gainDB) -{ - return ma_engine_set_volume(pEngine, ma_volume_db_to_linear(gainDB)); -} - -MA_API float ma_engine_get_gain_db(ma_engine* pEngine) -{ - return ma_volume_linear_to_db(ma_engine_get_volume(pEngine)); + return ma_node_set_output_bus_volume(ma_node_graph_get_endpoint(&pEngine->nodeGraph), 0, ma_volume_db_to_linear(gainDB)); } @@ -76405,10 +75041,6 @@ MA_API void ma_engine_listener_get_cone(const ma_engine* pEngine, ma_uint32 list *pOuterGain = 0; } - if (pEngine == NULL || listenerIndex >= pEngine->listenerCount) { - return; - } - ma_spatializer_listener_get_cone(&pEngine->listeners[listenerIndex], pInnerAngleInRadians, pOuterAngleInRadians, pOuterGain); } @@ -76460,7 +75092,7 @@ MA_API ma_result ma_engine_play_sound_ex(ma_engine* pEngine, const char* pFilePa return MA_INVALID_ARGS; } - /* Attach to the endpoint node if nothing is specified. */ + /* Attach to the endpoint node if nothing is specicied. */ if (pNode == NULL) { pNode = ma_node_graph_get_endpoint(&pEngine->nodeGraph); nodeInputBusIndex = 0; @@ -76489,7 +75121,7 @@ MA_API ma_result ma_engine_play_sound_ex(ma_engine* pEngine, const char* pFilePa is uninitialize it and reinitialize it. All we're doing is recycling memory. */ pSound = pNextSound; - ma_atomic_fetch_sub_32(&pEngine->inlinedSoundCount, 1); + c89atomic_fetch_sub_32(&pEngine->inlinedSoundCount, 1); break; } } @@ -76560,11 +75192,11 @@ MA_API ma_result ma_engine_play_sound_ex(ma_engine* pEngine, const char* pFilePa result = ma_sound_start(&pSound->sound); if (result != MA_SUCCESS) { /* Failed to start the sound. We need to mark it for recycling and return an error. */ - ma_atomic_exchange_32(&pSound->sound.atEnd, MA_TRUE); + c89atomic_exchange_32(&pSound->sound.atEnd, MA_TRUE); return result; } - ma_atomic_fetch_add_32(&pEngine->inlinedSoundCount, 1); + c89atomic_fetch_add_32(&pEngine->inlinedSoundCount, 1); return result; } @@ -76677,7 +75309,7 @@ static ma_result ma_sound_init_from_data_source_internal(ma_engine* pEngine, con ma_data_source_set_range_in_pcm_frames(ma_sound_get_data_source(pSound), pConfig->loopPointBegInPCMFrames, pConfig->loopPointEndInPCMFrames); } - ma_sound_set_looping(pSound, pConfig->isLooping || ((pConfig->flags & MA_SOUND_FLAG_LOOPING) != 0)); + ma_sound_set_looping(pSound, pConfig->isLooping); return MA_SUCCESS; } @@ -76701,9 +75333,6 @@ MA_API ma_result ma_sound_init_from_file_internal(ma_engine* pEngine, const ma_s it and can avoid accessing the sound from within the notification. */ flags = pConfig->flags | MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_WAIT_INIT; - if (pConfig->isLooping) { - flags |= MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_LOOPING; - } pSound->pResourceManagerDataSource = (ma_resource_manager_data_source*)ma_malloc(sizeof(*pSound->pResourceManagerDataSource), &pEngine->allocationCallbacks); if (pSound->pResourceManagerDataSource == NULL) { @@ -76732,7 +75361,7 @@ MA_API ma_result ma_sound_init_from_file_internal(ma_engine* pEngine, const ma_s resourceManagerDataSourceConfig.rangeEndInPCMFrames = pConfig->rangeEndInPCMFrames; resourceManagerDataSourceConfig.loopPointBegInPCMFrames = pConfig->loopPointBegInPCMFrames; resourceManagerDataSourceConfig.loopPointEndInPCMFrames = pConfig->loopPointEndInPCMFrames; - resourceManagerDataSourceConfig.isLooping = (flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_LOOPING) != 0; + resourceManagerDataSourceConfig.isLooping = pConfig->isLooping; result = ma_resource_manager_data_source_init_ex(pEngine->pResourceManager, &resourceManagerDataSourceConfig, pSound->pResourceManagerDataSource); if (result != MA_SUCCESS) { @@ -76815,7 +75444,7 @@ MA_API ma_result ma_sound_init_copy(ma_engine* pEngine, const ma_sound* pExistin /* We need to make a clone of the data source. If the data source is not a data buffer (i.e. a stream) - this will fail. + the this will fail. */ pSound->pResourceManagerDataSource = (ma_resource_manager_data_source*)ma_malloc(sizeof(*pSound->pResourceManagerDataSource), &pEngine->allocationCallbacks); if (pSound->pResourceManagerDataSource == NULL) { @@ -76843,9 +75472,6 @@ MA_API ma_result ma_sound_init_copy(ma_engine* pEngine, const ma_sound* pExistin return result; } - /* Make sure the sound is marked as the owner of the data source or else it will never get uninitialized. */ - pSound->ownsDataSource = MA_TRUE; - return MA_SUCCESS; } #endif @@ -76884,7 +75510,7 @@ MA_API ma_result ma_sound_init_ex(ma_engine* pEngine, const ma_sound_config* pCo { /* Getting here means we're not loading from a file. We may be loading from an already-initialized - data source, or none at all. If we aren't specifying any data source, we'll be initializing + data source, or none at all. If we aren't specifying any data source, we'll be initializing the the equivalent to a group. ma_data_source_init_from_data_source_internal() will deal with this for us, so no special treatment required here. */ @@ -76953,7 +75579,7 @@ MA_API ma_result ma_sound_start(ma_sound* pSound) } /* Make sure we clear the end indicator. */ - ma_atomic_exchange_32(&pSound->atEnd, MA_FALSE); + c89atomic_exchange_32(&pSound->atEnd, MA_FALSE); } /* Make sure the sound is started. If there's a start delay, the sound won't actually start until the start time is reached. */ @@ -76974,31 +75600,6 @@ MA_API ma_result ma_sound_stop(ma_sound* pSound) return MA_SUCCESS; } -MA_API ma_result ma_sound_stop_with_fade_in_pcm_frames(ma_sound* pSound, ma_uint64 fadeLengthInFrames) -{ - if (pSound == NULL) { - return MA_INVALID_ARGS; - } - - /* Stopping with a fade out requires us to schedule the stop into the future by the fade length. */ - ma_sound_set_stop_time_with_fade_in_pcm_frames(pSound, ma_engine_get_time_in_pcm_frames(ma_sound_get_engine(pSound)) + fadeLengthInFrames, fadeLengthInFrames); - - return MA_SUCCESS; -} - -MA_API ma_result ma_sound_stop_with_fade_in_milliseconds(ma_sound* pSound, ma_uint64 fadeLengthInMilliseconds) -{ - ma_uint64 sampleRate; - - if (pSound == NULL) { - return MA_INVALID_ARGS; - } - - sampleRate = ma_engine_get_sample_rate(ma_sound_get_engine(pSound)); - - return ma_sound_stop_with_fade_in_pcm_frames(pSound, (fadeLengthInMilliseconds * sampleRate) / 1000); -} - MA_API void ma_sound_set_volume(ma_sound* pSound, float volume) { if (pSound == NULL) { @@ -77067,7 +75668,7 @@ MA_API void ma_sound_set_pitch(ma_sound* pSound, float pitch) return; } - ma_atomic_exchange_explicit_f32(&pSound->engineNode.pitch, pitch, ma_atomic_memory_order_release); + c89atomic_exchange_explicit_f32(&pSound->engineNode.pitch, pitch, c89atomic_memory_order_release); } MA_API float ma_sound_get_pitch(const ma_sound* pSound) @@ -77076,7 +75677,7 @@ MA_API float ma_sound_get_pitch(const ma_sound* pSound) return 0; } - return ma_atomic_load_f32(&pSound->engineNode.pitch); /* Naughty const-cast for this. */ + return c89atomic_load_f32(&pSound->engineNode.pitch); /* Naughty const-cast for this. */ } MA_API void ma_sound_set_spatialization_enabled(ma_sound* pSound, ma_bool32 enabled) @@ -77085,7 +75686,7 @@ MA_API void ma_sound_set_spatialization_enabled(ma_sound* pSound, ma_bool32 enab return; } - ma_atomic_exchange_explicit_32(&pSound->engineNode.isSpatializationDisabled, !enabled, ma_atomic_memory_order_release); + c89atomic_exchange_explicit_32(&pSound->engineNode.isSpatializationDisabled, !enabled, c89atomic_memory_order_release); } MA_API ma_bool32 ma_sound_is_spatialization_enabled(const ma_sound* pSound) @@ -77103,7 +75704,7 @@ MA_API void ma_sound_set_pinned_listener_index(ma_sound* pSound, ma_uint32 liste return; } - ma_atomic_exchange_explicit_32(&pSound->engineNode.pinnedListenerIndex, listenerIndex, ma_atomic_memory_order_release); + c89atomic_exchange_explicit_32(&pSound->engineNode.pinnedListenerIndex, listenerIndex, c89atomic_memory_order_release); } MA_API ma_uint32 ma_sound_get_pinned_listener_index(const ma_sound* pSound) @@ -77112,7 +75713,7 @@ MA_API ma_uint32 ma_sound_get_pinned_listener_index(const ma_sound* pSound) return MA_LISTENER_INDEX_CLOSEST; } - return ma_atomic_load_explicit_32(&pSound->engineNode.pinnedListenerIndex, ma_atomic_memory_order_acquire); + return c89atomic_load_explicit_32(&pSound->engineNode.pinnedListenerIndex, c89atomic_memory_order_acquire); } MA_API ma_uint32 ma_sound_get_listener_index(const ma_sound* pSound) @@ -77354,10 +75955,6 @@ MA_API void ma_sound_get_cone(const ma_sound* pSound, float* pInnerAngleInRadian *pOuterGain = 0; } - if (pSound == NULL) { - return; - } - ma_spatializer_get_cone(&pSound->engineNode.spatializer, pInnerAngleInRadians, pOuterAngleInRadians, pOuterGain); } @@ -77404,7 +76001,7 @@ MA_API void ma_sound_set_fade_in_pcm_frames(ma_sound* pSound, float volumeBeg, f return; } - ma_sound_set_fade_start_in_pcm_frames(pSound, volumeBeg, volumeEnd, fadeLengthInFrames, (~(ma_uint64)0)); + ma_fader_set_fade(&pSound->engineNode.fader, volumeBeg, volumeEnd, fadeLengthInFrames); } MA_API void ma_sound_set_fade_in_milliseconds(ma_sound* pSound, float volumeBeg, float volumeEnd, ma_uint64 fadeLengthInMilliseconds) @@ -77416,36 +76013,6 @@ MA_API void ma_sound_set_fade_in_milliseconds(ma_sound* pSound, float volumeBeg, ma_sound_set_fade_in_pcm_frames(pSound, volumeBeg, volumeEnd, (fadeLengthInMilliseconds * pSound->engineNode.fader.config.sampleRate) / 1000); } -MA_API void ma_sound_set_fade_start_in_pcm_frames(ma_sound* pSound, float volumeBeg, float volumeEnd, ma_uint64 fadeLengthInFrames, ma_uint64 absoluteGlobalTimeInFrames) -{ - if (pSound == NULL) { - return; - } - - /* - We don't want to update the fader at this point because we need to use the engine's current time - to derive the fader's start offset. The timer is being updated on the audio thread so in order to - do this as accurately as possible we'll need to defer this to the audio thread. - */ - ma_atomic_float_set(&pSound->engineNode.fadeSettings.volumeBeg, volumeBeg); - ma_atomic_float_set(&pSound->engineNode.fadeSettings.volumeEnd, volumeEnd); - ma_atomic_uint64_set(&pSound->engineNode.fadeSettings.fadeLengthInFrames, fadeLengthInFrames); - ma_atomic_uint64_set(&pSound->engineNode.fadeSettings.absoluteGlobalTimeInFrames, absoluteGlobalTimeInFrames); -} - -MA_API void ma_sound_set_fade_start_in_milliseconds(ma_sound* pSound, float volumeBeg, float volumeEnd, ma_uint64 fadeLengthInMilliseconds, ma_uint64 absoluteGlobalTimeInMilliseconds) -{ - ma_uint32 sampleRate; - - if (pSound == NULL) { - return; - } - - sampleRate = ma_engine_get_sample_rate(ma_sound_get_engine(pSound)); - - ma_sound_set_fade_start_in_pcm_frames(pSound, volumeBeg, volumeEnd, (fadeLengthInMilliseconds * sampleRate) / 1000, (absoluteGlobalTimeInMilliseconds * sampleRate) / 1000); -} - MA_API float ma_sound_get_current_fade_volume(const ma_sound* pSound) { if (pSound == NULL) { @@ -77479,7 +76046,7 @@ MA_API void ma_sound_set_stop_time_in_pcm_frames(ma_sound* pSound, ma_uint64 abs return; } - ma_sound_set_stop_time_with_fade_in_pcm_frames(pSound, absoluteGlobalTimeInFrames, 0); + ma_node_set_state_time(pSound, ma_node_state_stopped, absoluteGlobalTimeInFrames); } MA_API void ma_sound_set_stop_time_in_milliseconds(ma_sound* pSound, ma_uint64 absoluteGlobalTimeInMilliseconds) @@ -77491,36 +76058,6 @@ MA_API void ma_sound_set_stop_time_in_milliseconds(ma_sound* pSound, ma_uint64 a ma_sound_set_stop_time_in_pcm_frames(pSound, absoluteGlobalTimeInMilliseconds * ma_engine_get_sample_rate(ma_sound_get_engine(pSound)) / 1000); } -MA_API void ma_sound_set_stop_time_with_fade_in_pcm_frames(ma_sound* pSound, ma_uint64 stopAbsoluteGlobalTimeInFrames, ma_uint64 fadeLengthInFrames) -{ - if (pSound == NULL) { - return; - } - - if (fadeLengthInFrames > 0) { - if (fadeLengthInFrames > stopAbsoluteGlobalTimeInFrames) { - fadeLengthInFrames = stopAbsoluteGlobalTimeInFrames; - } - - ma_sound_set_fade_start_in_pcm_frames(pSound, -1, 0, fadeLengthInFrames, stopAbsoluteGlobalTimeInFrames - fadeLengthInFrames); - } - - ma_node_set_state_time(pSound, ma_node_state_stopped, stopAbsoluteGlobalTimeInFrames); -} - -MA_API void ma_sound_set_stop_time_with_fade_in_milliseconds(ma_sound* pSound, ma_uint64 stopAbsoluteGlobalTimeInMilliseconds, ma_uint64 fadeLengthInMilliseconds) -{ - ma_uint32 sampleRate; - - if (pSound == NULL) { - return; - } - - sampleRate = ma_engine_get_sample_rate(ma_sound_get_engine(pSound)); - - ma_sound_set_stop_time_with_fade_in_pcm_frames(pSound, (stopAbsoluteGlobalTimeInMilliseconds * sampleRate) / 1000, (fadeLengthInMilliseconds * sampleRate) / 1000); -} - MA_API ma_bool32 ma_sound_is_playing(const ma_sound* pSound) { if (pSound == NULL) { @@ -77539,11 +76076,6 @@ MA_API ma_uint64 ma_sound_get_time_in_pcm_frames(const ma_sound* pSound) return ma_node_get_time(pSound); } -MA_API ma_uint64 ma_sound_get_time_in_milliseconds(const ma_sound* pSound) -{ - return ma_sound_get_time_in_pcm_frames(pSound) * 1000 / ma_engine_get_sample_rate(ma_sound_get_engine(pSound)); -} - MA_API void ma_sound_set_looping(ma_sound* pSound, ma_bool32 isLooping) { if (pSound == NULL) { @@ -77599,32 +76131,11 @@ MA_API ma_result ma_sound_seek_to_pcm_frame(ma_sound* pSound, ma_uint64 frameInd } /* We can't be seeking while reading at the same time. We just set the seek target and get the mixing thread to do the actual seek. */ - ma_atomic_exchange_64(&pSound->seekTarget, frameIndex); + c89atomic_exchange_64(&pSound->seekTarget, frameIndex); return MA_SUCCESS; } -MA_API ma_result ma_sound_seek_to_second(ma_sound* pSound, float seekPointInSeconds) -{ - ma_uint64 frameIndex; - ma_uint32 sampleRate; - ma_result result; - - if (pSound == NULL) { - return MA_INVALID_ARGS; - } - - result = ma_sound_get_data_format(pSound, NULL, NULL, &sampleRate, NULL, 0); - if (result != MA_SUCCESS) { - return result; - } - - /* We need PCM frames. We need to convert first */ - frameIndex = (ma_uint64)(seekPointInSeconds * sampleRate); - - return ma_sound_seek_to_pcm_frame(pSound, frameIndex); -} - MA_API ma_result ma_sound_get_data_format(ma_sound* pSound, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate, ma_channel* pChannelMap, size_t channelMapCap) { if (pSound == NULL) { @@ -77660,8 +76171,6 @@ MA_API ma_result ma_sound_get_data_format(ma_sound* pSound, ma_format* pFormat, MA_API ma_result ma_sound_get_cursor_in_pcm_frames(ma_sound* pSound, ma_uint64* pCursor) { - ma_uint64 seekTarget; - if (pSound == NULL) { return MA_INVALID_ARGS; } @@ -77671,13 +76180,7 @@ MA_API ma_result ma_sound_get_cursor_in_pcm_frames(ma_sound* pSound, ma_uint64* return MA_INVALID_OPERATION; } - seekTarget = ma_atomic_load_64(&pSound->seekTarget); - if (seekTarget != MA_SEEK_TARGET_NONE) { - *pCursor = seekTarget; - return MA_SUCCESS; - } else { - return ma_data_source_get_cursor_in_pcm_frames(pSound->pDataSource, pCursor); - } + return ma_data_source_get_cursor_in_pcm_frames(pSound->pDataSource, pCursor); } MA_API ma_result ma_sound_get_length_in_pcm_frames(ma_sound* pSound, ma_uint64* pLength) @@ -77696,28 +76199,16 @@ MA_API ma_result ma_sound_get_length_in_pcm_frames(ma_sound* pSound, ma_uint64* MA_API ma_result ma_sound_get_cursor_in_seconds(ma_sound* pSound, float* pCursor) { - ma_result result; - ma_uint64 cursorInPCMFrames; - ma_uint32 sampleRate; - - if (pCursor != NULL) { - *pCursor = 0; - } - - result = ma_sound_get_cursor_in_pcm_frames(pSound, &cursorInPCMFrames); - if (result != MA_SUCCESS) { - return result; + if (pSound == NULL) { + return MA_INVALID_ARGS; } - result = ma_sound_get_data_format(pSound, NULL, NULL, &sampleRate, NULL, 0); - if (result != MA_SUCCESS) { - return result; + /* The notion of a cursor is only valid for sounds that are backed by a data source. */ + if (pSound->pDataSource == NULL) { + return MA_INVALID_OPERATION; } - /* VC6 does not support division of unsigned 64-bit integers with floating point numbers. Need to use a signed number. This shouldn't effect anything in practice. */ - *pCursor = (ma_int64)cursorInPCMFrames / (float)sampleRate; - - return MA_SUCCESS; + return ma_data_source_get_cursor_in_seconds(pSound->pDataSource, pCursor); } MA_API ma_result ma_sound_get_length_in_seconds(ma_sound* pSound, float* pLength) @@ -78065,135 +76556,167 @@ MA_API ma_uint64 ma_sound_group_get_time_in_pcm_frames(const ma_sound_group* pGr Auto Generated ============== -All code below is auto-generated from a tool. This mostly consists of decoding backend implementations such as ma_dr_wav, ma_dr_flac, etc. If you find a bug in the +All code below is auto-generated from a tool. This mostly consists of decoding backend implementations such as dr_wav, dr_flac, etc. If you find a bug in the code below please report the bug to the respective repository for the relevant project (probably dr_libs). *************************************************************************************************************************************************************** **************************************************************************************************************************************************************/ #if !defined(MA_NO_WAV) && (!defined(MA_NO_DECODING) || !defined(MA_NO_ENCODING)) -#if !defined(MA_DR_WAV_IMPLEMENTATION) +#if !defined(DR_WAV_IMPLEMENTATION) && !defined(DRWAV_IMPLEMENTATION) /* For backwards compatibility. Will be removed in version 0.11 for cleanliness. */ /* dr_wav_c begin */ -#ifndef ma_dr_wav_c -#define ma_dr_wav_c +#ifndef dr_wav_c +#define dr_wav_c #ifdef __MRC__ #pragma options opt off #endif #include #include #include -#ifndef MA_DR_WAV_NO_STDIO +#ifndef DR_WAV_NO_STDIO #include -#ifndef MA_DR_WAV_NO_WCHAR +#ifndef DR_WAV_NO_WCHAR #include #endif #endif -#ifndef MA_DR_WAV_ASSERT +#ifndef DRWAV_ASSERT #include -#define MA_DR_WAV_ASSERT(expression) assert(expression) +#define DRWAV_ASSERT(expression) assert(expression) +#endif +#ifndef DRWAV_MALLOC +#define DRWAV_MALLOC(sz) malloc((sz)) #endif -#ifndef MA_DR_WAV_MALLOC -#define MA_DR_WAV_MALLOC(sz) malloc((sz)) +#ifndef DRWAV_REALLOC +#define DRWAV_REALLOC(p, sz) realloc((p), (sz)) #endif -#ifndef MA_DR_WAV_REALLOC -#define MA_DR_WAV_REALLOC(p, sz) realloc((p), (sz)) +#ifndef DRWAV_FREE +#define DRWAV_FREE(p) free((p)) #endif -#ifndef MA_DR_WAV_FREE -#define MA_DR_WAV_FREE(p) free((p)) +#ifndef DRWAV_COPY_MEMORY +#define DRWAV_COPY_MEMORY(dst, src, sz) memcpy((dst), (src), (sz)) #endif -#ifndef MA_DR_WAV_COPY_MEMORY -#define MA_DR_WAV_COPY_MEMORY(dst, src, sz) memcpy((dst), (src), (sz)) +#ifndef DRWAV_ZERO_MEMORY +#define DRWAV_ZERO_MEMORY(p, sz) memset((p), 0, (sz)) #endif -#ifndef MA_DR_WAV_ZERO_MEMORY -#define MA_DR_WAV_ZERO_MEMORY(p, sz) memset((p), 0, (sz)) +#ifndef DRWAV_ZERO_OBJECT +#define DRWAV_ZERO_OBJECT(p) DRWAV_ZERO_MEMORY((p), sizeof(*p)) +#endif +#define drwav_countof(x) (sizeof(x) / sizeof(x[0])) +#define drwav_align(x, a) ((((x) + (a) - 1) / (a)) * (a)) +#define drwav_min(a, b) (((a) < (b)) ? (a) : (b)) +#define drwav_max(a, b) (((a) > (b)) ? (a) : (b)) +#define drwav_clamp(x, lo, hi) (drwav_max((lo), drwav_min((hi), (x)))) +#define drwav_offset_ptr(p, offset) (((drwav_uint8*)(p)) + (offset)) +#define DRWAV_MAX_SIMD_VECTOR_SIZE 64 +#if defined(__x86_64__) || defined(_M_X64) + #define DRWAV_X64 +#elif defined(__i386) || defined(_M_IX86) + #define DRWAV_X86 +#elif defined(__arm__) || defined(_M_ARM) + #define DRWAV_ARM +#endif +#ifdef _MSC_VER + #define DRWAV_INLINE __forceinline +#elif defined(__GNUC__) + #if defined(__STRICT_ANSI__) + #define DRWAV_GNUC_INLINE_HINT __inline__ + #else + #define DRWAV_GNUC_INLINE_HINT inline + #endif + #if (__GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 2)) || defined(__clang__) + #define DRWAV_INLINE DRWAV_GNUC_INLINE_HINT __attribute__((always_inline)) + #else + #define DRWAV_INLINE DRWAV_GNUC_INLINE_HINT + #endif +#elif defined(__WATCOMC__) + #define DRWAV_INLINE __inline +#else + #define DRWAV_INLINE #endif -#ifndef MA_DR_WAV_ZERO_OBJECT -#define MA_DR_WAV_ZERO_OBJECT(p) MA_DR_WAV_ZERO_MEMORY((p), sizeof(*p)) +#if defined(SIZE_MAX) + #define DRWAV_SIZE_MAX SIZE_MAX +#else + #if defined(_WIN64) || defined(_LP64) || defined(__LP64__) + #define DRWAV_SIZE_MAX ((drwav_uint64)0xFFFFFFFFFFFFFFFF) + #else + #define DRWAV_SIZE_MAX 0xFFFFFFFF + #endif #endif -#define ma_dr_wav_countof(x) (sizeof(x) / sizeof(x[0])) -#define ma_dr_wav_align(x, a) ((((x) + (a) - 1) / (a)) * (a)) -#define ma_dr_wav_min(a, b) (((a) < (b)) ? (a) : (b)) -#define ma_dr_wav_max(a, b) (((a) > (b)) ? (a) : (b)) -#define ma_dr_wav_clamp(x, lo, hi) (ma_dr_wav_max((lo), ma_dr_wav_min((hi), (x)))) -#define ma_dr_wav_offset_ptr(p, offset) (((ma_uint8*)(p)) + (offset)) -#define MA_DR_WAV_MAX_SIMD_VECTOR_SIZE 32 -#define MA_DR_WAV_INT64_MIN ((ma_int64) ((ma_uint64)0x80000000 << 32)) -#define MA_DR_WAV_INT64_MAX ((ma_int64)(((ma_uint64)0x7FFFFFFF << 32) | 0xFFFFFFFF)) #if defined(_MSC_VER) && _MSC_VER >= 1400 - #define MA_DR_WAV_HAS_BYTESWAP16_INTRINSIC - #define MA_DR_WAV_HAS_BYTESWAP32_INTRINSIC - #define MA_DR_WAV_HAS_BYTESWAP64_INTRINSIC + #define DRWAV_HAS_BYTESWAP16_INTRINSIC + #define DRWAV_HAS_BYTESWAP32_INTRINSIC + #define DRWAV_HAS_BYTESWAP64_INTRINSIC #elif defined(__clang__) #if defined(__has_builtin) #if __has_builtin(__builtin_bswap16) - #define MA_DR_WAV_HAS_BYTESWAP16_INTRINSIC + #define DRWAV_HAS_BYTESWAP16_INTRINSIC #endif #if __has_builtin(__builtin_bswap32) - #define MA_DR_WAV_HAS_BYTESWAP32_INTRINSIC + #define DRWAV_HAS_BYTESWAP32_INTRINSIC #endif #if __has_builtin(__builtin_bswap64) - #define MA_DR_WAV_HAS_BYTESWAP64_INTRINSIC + #define DRWAV_HAS_BYTESWAP64_INTRINSIC #endif #endif #elif defined(__GNUC__) #if ((__GNUC__ > 4) || (__GNUC__ == 4 && __GNUC_MINOR__ >= 3)) - #define MA_DR_WAV_HAS_BYTESWAP32_INTRINSIC - #define MA_DR_WAV_HAS_BYTESWAP64_INTRINSIC + #define DRWAV_HAS_BYTESWAP32_INTRINSIC + #define DRWAV_HAS_BYTESWAP64_INTRINSIC #endif #if ((__GNUC__ > 4) || (__GNUC__ == 4 && __GNUC_MINOR__ >= 8)) - #define MA_DR_WAV_HAS_BYTESWAP16_INTRINSIC + #define DRWAV_HAS_BYTESWAP16_INTRINSIC #endif #endif -MA_API void ma_dr_wav_version(ma_uint32* pMajor, ma_uint32* pMinor, ma_uint32* pRevision) +DRWAV_API void drwav_version(drwav_uint32* pMajor, drwav_uint32* pMinor, drwav_uint32* pRevision) { if (pMajor) { - *pMajor = MA_DR_WAV_VERSION_MAJOR; + *pMajor = DRWAV_VERSION_MAJOR; } if (pMinor) { - *pMinor = MA_DR_WAV_VERSION_MINOR; + *pMinor = DRWAV_VERSION_MINOR; } if (pRevision) { - *pRevision = MA_DR_WAV_VERSION_REVISION; + *pRevision = DRWAV_VERSION_REVISION; } } -MA_API const char* ma_dr_wav_version_string(void) +DRWAV_API const char* drwav_version_string(void) { - return MA_DR_WAV_VERSION_STRING; + return DRWAV_VERSION_STRING; } -#ifndef MA_DR_WAV_MAX_SAMPLE_RATE -#define MA_DR_WAV_MAX_SAMPLE_RATE 384000 +#ifndef DRWAV_MAX_SAMPLE_RATE +#define DRWAV_MAX_SAMPLE_RATE 384000 #endif -#ifndef MA_DR_WAV_MAX_CHANNELS -#define MA_DR_WAV_MAX_CHANNELS 256 +#ifndef DRWAV_MAX_CHANNELS +#define DRWAV_MAX_CHANNELS 256 #endif -#ifndef MA_DR_WAV_MAX_BITS_PER_SAMPLE -#define MA_DR_WAV_MAX_BITS_PER_SAMPLE 64 +#ifndef DRWAV_MAX_BITS_PER_SAMPLE +#define DRWAV_MAX_BITS_PER_SAMPLE 64 #endif -static const ma_uint8 ma_dr_wavGUID_W64_RIFF[16] = {0x72,0x69,0x66,0x66, 0x2E,0x91, 0xCF,0x11, 0xA5,0xD6, 0x28,0xDB,0x04,0xC1,0x00,0x00}; -static const ma_uint8 ma_dr_wavGUID_W64_WAVE[16] = {0x77,0x61,0x76,0x65, 0xF3,0xAC, 0xD3,0x11, 0x8C,0xD1, 0x00,0xC0,0x4F,0x8E,0xDB,0x8A}; -static const ma_uint8 ma_dr_wavGUID_W64_FMT [16] = {0x66,0x6D,0x74,0x20, 0xF3,0xAC, 0xD3,0x11, 0x8C,0xD1, 0x00,0xC0,0x4F,0x8E,0xDB,0x8A}; -static const ma_uint8 ma_dr_wavGUID_W64_FACT[16] = {0x66,0x61,0x63,0x74, 0xF3,0xAC, 0xD3,0x11, 0x8C,0xD1, 0x00,0xC0,0x4F,0x8E,0xDB,0x8A}; -static const ma_uint8 ma_dr_wavGUID_W64_DATA[16] = {0x64,0x61,0x74,0x61, 0xF3,0xAC, 0xD3,0x11, 0x8C,0xD1, 0x00,0xC0,0x4F,0x8E,0xDB,0x8A}; -static MA_INLINE int ma_dr_wav__is_little_endian(void) +static const drwav_uint8 drwavGUID_W64_RIFF[16] = {0x72,0x69,0x66,0x66, 0x2E,0x91, 0xCF,0x11, 0xA5,0xD6, 0x28,0xDB,0x04,0xC1,0x00,0x00}; +static const drwav_uint8 drwavGUID_W64_WAVE[16] = {0x77,0x61,0x76,0x65, 0xF3,0xAC, 0xD3,0x11, 0x8C,0xD1, 0x00,0xC0,0x4F,0x8E,0xDB,0x8A}; +static const drwav_uint8 drwavGUID_W64_FMT [16] = {0x66,0x6D,0x74,0x20, 0xF3,0xAC, 0xD3,0x11, 0x8C,0xD1, 0x00,0xC0,0x4F,0x8E,0xDB,0x8A}; +static const drwav_uint8 drwavGUID_W64_FACT[16] = {0x66,0x61,0x63,0x74, 0xF3,0xAC, 0xD3,0x11, 0x8C,0xD1, 0x00,0xC0,0x4F,0x8E,0xDB,0x8A}; +static const drwav_uint8 drwavGUID_W64_DATA[16] = {0x64,0x61,0x74,0x61, 0xF3,0xAC, 0xD3,0x11, 0x8C,0xD1, 0x00,0xC0,0x4F,0x8E,0xDB,0x8A}; +static DRWAV_INLINE int drwav__is_little_endian(void) { -#if defined(MA_X86) || defined(MA_X64) - return MA_TRUE; +#if defined(DRWAV_X86) || defined(DRWAV_X64) + return DRWAV_TRUE; #elif defined(__BYTE_ORDER) && defined(__LITTLE_ENDIAN) && __BYTE_ORDER == __LITTLE_ENDIAN - return MA_TRUE; + return DRWAV_TRUE; #else int n = 1; return (*(char*)&n) == 1; #endif } -static MA_INLINE void ma_dr_wav_bytes_to_guid(const ma_uint8* data, ma_uint8* guid) +static DRWAV_INLINE void drwav_bytes_to_guid(const drwav_uint8* data, drwav_uint8* guid) { int i; for (i = 0; i < 16; ++i) { guid[i] = data[i]; } } -static MA_INLINE ma_uint16 ma_dr_wav__bswap16(ma_uint16 n) +static DRWAV_INLINE drwav_uint16 drwav__bswap16(drwav_uint16 n) { -#ifdef MA_DR_WAV_HAS_BYTESWAP16_INTRINSIC +#ifdef DRWAV_HAS_BYTESWAP16_INTRINSIC #if defined(_MSC_VER) return _byteswap_ushort(n); #elif defined(__GNUC__) || defined(__clang__) @@ -78206,16 +76729,16 @@ static MA_INLINE ma_uint16 ma_dr_wav__bswap16(ma_uint16 n) ((n & 0x00FF) << 8); #endif } -static MA_INLINE ma_uint32 ma_dr_wav__bswap32(ma_uint32 n) +static DRWAV_INLINE drwav_uint32 drwav__bswap32(drwav_uint32 n) { -#ifdef MA_DR_WAV_HAS_BYTESWAP32_INTRINSIC +#ifdef DRWAV_HAS_BYTESWAP32_INTRINSIC #if defined(_MSC_VER) return _byteswap_ulong(n); #elif defined(__GNUC__) || defined(__clang__) - #if defined(MA_ARM) && (defined(__ARM_ARCH) && __ARM_ARCH >= 6) && !defined(MA_64BIT) - ma_uint32 r; + #if defined(DRWAV_ARM) && (defined(__ARM_ARCH) && __ARM_ARCH >= 6) && !defined(DRWAV_64BIT) + drwav_uint32 r; __asm__ __volatile__ ( - #if defined(MA_64BIT) + #if defined(DRWAV_64BIT) "rev %w[out], %w[in]" : [out]"=r"(r) : [in]"r"(n) #else "rev %[out], %[in]" : [out]"=r"(r) : [in]"r"(n) @@ -78235,9 +76758,9 @@ static MA_INLINE ma_uint32 ma_dr_wav__bswap32(ma_uint32 n) ((n & 0x000000FF) << 24); #endif } -static MA_INLINE ma_uint64 ma_dr_wav__bswap64(ma_uint64 n) +static DRWAV_INLINE drwav_uint64 drwav__bswap64(drwav_uint64 n) { -#ifdef MA_DR_WAV_HAS_BYTESWAP64_INTRINSIC +#ifdef DRWAV_HAS_BYTESWAP64_INTRINSIC #if defined(_MSC_VER) return _byteswap_uint64(n); #elif defined(__GNUC__) || defined(__clang__) @@ -78246,82 +76769,88 @@ static MA_INLINE ma_uint64 ma_dr_wav__bswap64(ma_uint64 n) #error "This compiler does not support the byte swap intrinsic." #endif #else - return ((n & ((ma_uint64)0xFF000000 << 32)) >> 56) | - ((n & ((ma_uint64)0x00FF0000 << 32)) >> 40) | - ((n & ((ma_uint64)0x0000FF00 << 32)) >> 24) | - ((n & ((ma_uint64)0x000000FF << 32)) >> 8) | - ((n & ((ma_uint64)0xFF000000 )) << 8) | - ((n & ((ma_uint64)0x00FF0000 )) << 24) | - ((n & ((ma_uint64)0x0000FF00 )) << 40) | - ((n & ((ma_uint64)0x000000FF )) << 56); + return ((n & ((drwav_uint64)0xFF000000 << 32)) >> 56) | + ((n & ((drwav_uint64)0x00FF0000 << 32)) >> 40) | + ((n & ((drwav_uint64)0x0000FF00 << 32)) >> 24) | + ((n & ((drwav_uint64)0x000000FF << 32)) >> 8) | + ((n & ((drwav_uint64)0xFF000000 )) << 8) | + ((n & ((drwav_uint64)0x00FF0000 )) << 24) | + ((n & ((drwav_uint64)0x0000FF00 )) << 40) | + ((n & ((drwav_uint64)0x000000FF )) << 56); #endif } -static MA_INLINE ma_int16 ma_dr_wav__bswap_s16(ma_int16 n) +static DRWAV_INLINE drwav_int16 drwav__bswap_s16(drwav_int16 n) { - return (ma_int16)ma_dr_wav__bswap16((ma_uint16)n); + return (drwav_int16)drwav__bswap16((drwav_uint16)n); } -static MA_INLINE void ma_dr_wav__bswap_samples_s16(ma_int16* pSamples, ma_uint64 sampleCount) +static DRWAV_INLINE void drwav__bswap_samples_s16(drwav_int16* pSamples, drwav_uint64 sampleCount) { - ma_uint64 iSample; + drwav_uint64 iSample; for (iSample = 0; iSample < sampleCount; iSample += 1) { - pSamples[iSample] = ma_dr_wav__bswap_s16(pSamples[iSample]); + pSamples[iSample] = drwav__bswap_s16(pSamples[iSample]); } } -static MA_INLINE void ma_dr_wav__bswap_s24(ma_uint8* p) +static DRWAV_INLINE void drwav__bswap_s24(drwav_uint8* p) { - ma_uint8 t; + drwav_uint8 t; t = p[0]; p[0] = p[2]; p[2] = t; } -static MA_INLINE void ma_dr_wav__bswap_samples_s24(ma_uint8* pSamples, ma_uint64 sampleCount) +static DRWAV_INLINE void drwav__bswap_samples_s24(drwav_uint8* pSamples, drwav_uint64 sampleCount) { - ma_uint64 iSample; + drwav_uint64 iSample; for (iSample = 0; iSample < sampleCount; iSample += 1) { - ma_uint8* pSample = pSamples + (iSample*3); - ma_dr_wav__bswap_s24(pSample); + drwav_uint8* pSample = pSamples + (iSample*3); + drwav__bswap_s24(pSample); } } -static MA_INLINE ma_int32 ma_dr_wav__bswap_s32(ma_int32 n) +static DRWAV_INLINE drwav_int32 drwav__bswap_s32(drwav_int32 n) { - return (ma_int32)ma_dr_wav__bswap32((ma_uint32)n); + return (drwav_int32)drwav__bswap32((drwav_uint32)n); } -static MA_INLINE void ma_dr_wav__bswap_samples_s32(ma_int32* pSamples, ma_uint64 sampleCount) +static DRWAV_INLINE void drwav__bswap_samples_s32(drwav_int32* pSamples, drwav_uint64 sampleCount) { - ma_uint64 iSample; + drwav_uint64 iSample; for (iSample = 0; iSample < sampleCount; iSample += 1) { - pSamples[iSample] = ma_dr_wav__bswap_s32(pSamples[iSample]); + pSamples[iSample] = drwav__bswap_s32(pSamples[iSample]); } } -static MA_INLINE ma_int64 ma_dr_wav__bswap_s64(ma_int64 n) +static DRWAV_INLINE float drwav__bswap_f32(float n) { - return (ma_int64)ma_dr_wav__bswap64((ma_uint64)n); + union { + drwav_uint32 i; + float f; + } x; + x.f = n; + x.i = drwav__bswap32(x.i); + return x.f; } -static MA_INLINE void ma_dr_wav__bswap_samples_s64(ma_int64* pSamples, ma_uint64 sampleCount) +static DRWAV_INLINE void drwav__bswap_samples_f32(float* pSamples, drwav_uint64 sampleCount) { - ma_uint64 iSample; + drwav_uint64 iSample; for (iSample = 0; iSample < sampleCount; iSample += 1) { - pSamples[iSample] = ma_dr_wav__bswap_s64(pSamples[iSample]); + pSamples[iSample] = drwav__bswap_f32(pSamples[iSample]); } } -static MA_INLINE float ma_dr_wav__bswap_f32(float n) +static DRWAV_INLINE double drwav__bswap_f64(double n) { union { - ma_uint32 i; - float f; + drwav_uint64 i; + double f; } x; x.f = n; - x.i = ma_dr_wav__bswap32(x.i); + x.i = drwav__bswap64(x.i); return x.f; } -static MA_INLINE void ma_dr_wav__bswap_samples_f32(float* pSamples, ma_uint64 sampleCount) +static DRWAV_INLINE void drwav__bswap_samples_f64(double* pSamples, drwav_uint64 sampleCount) { - ma_uint64 iSample; + drwav_uint64 iSample; for (iSample = 0; iSample < sampleCount; iSample += 1) { - pSamples[iSample] = ma_dr_wav__bswap_f32(pSamples[iSample]); + pSamples[iSample] = drwav__bswap_f64(pSamples[iSample]); } } -static MA_INLINE void ma_dr_wav__bswap_samples(void* pSamples, ma_uint64 sampleCount, ma_uint32 bytesPerSample) +static DRWAV_INLINE void drwav__bswap_samples_pcm(void* pSamples, drwav_uint64 sampleCount, drwav_uint32 bytesPerSample) { switch (bytesPerSample) { @@ -78330,108 +76859,87 @@ static MA_INLINE void ma_dr_wav__bswap_samples(void* pSamples, ma_uint64 sampleC } break; case 2: { - ma_dr_wav__bswap_samples_s16((ma_int16*)pSamples, sampleCount); + drwav__bswap_samples_s16((drwav_int16*)pSamples, sampleCount); } break; case 3: { - ma_dr_wav__bswap_samples_s24((ma_uint8*)pSamples, sampleCount); + drwav__bswap_samples_s24((drwav_uint8*)pSamples, sampleCount); } break; case 4: { - ma_dr_wav__bswap_samples_s32((ma_int32*)pSamples, sampleCount); - } break; - case 8: - { - ma_dr_wav__bswap_samples_s64((ma_int64*)pSamples, sampleCount); + drwav__bswap_samples_s32((drwav_int32*)pSamples, sampleCount); } break; default: { - MA_DR_WAV_ASSERT(MA_FALSE); + DRWAV_ASSERT(DRWAV_FALSE); } break; } } -MA_PRIVATE MA_INLINE ma_bool32 ma_dr_wav_is_container_be(ma_dr_wav_container container) -{ - if (container == ma_dr_wav_container_rifx || container == ma_dr_wav_container_aiff) { - return MA_TRUE; - } else { - return MA_FALSE; - } -} -MA_PRIVATE MA_INLINE ma_uint16 ma_dr_wav_bytes_to_u16_le(const ma_uint8* data) -{ - return ((ma_uint16)data[0] << 0) | ((ma_uint16)data[1] << 8); -} -MA_PRIVATE MA_INLINE ma_uint16 ma_dr_wav_bytes_to_u16_be(const ma_uint8* data) -{ - return ((ma_uint16)data[1] << 0) | ((ma_uint16)data[0] << 8); -} -MA_PRIVATE MA_INLINE ma_uint16 ma_dr_wav_bytes_to_u16_ex(const ma_uint8* data, ma_dr_wav_container container) -{ - if (ma_dr_wav_is_container_be(container)) { - return ma_dr_wav_bytes_to_u16_be(data); - } else { - return ma_dr_wav_bytes_to_u16_le(data); - } -} -MA_PRIVATE MA_INLINE ma_uint32 ma_dr_wav_bytes_to_u32_le(const ma_uint8* data) -{ - return ((ma_uint32)data[0] << 0) | ((ma_uint32)data[1] << 8) | ((ma_uint32)data[2] << 16) | ((ma_uint32)data[3] << 24); -} -MA_PRIVATE MA_INLINE ma_uint32 ma_dr_wav_bytes_to_u32_be(const ma_uint8* data) -{ - return ((ma_uint32)data[3] << 0) | ((ma_uint32)data[2] << 8) | ((ma_uint32)data[1] << 16) | ((ma_uint32)data[0] << 24); -} -MA_PRIVATE MA_INLINE ma_uint32 ma_dr_wav_bytes_to_u32_ex(const ma_uint8* data, ma_dr_wav_container container) +static DRWAV_INLINE void drwav__bswap_samples_ieee(void* pSamples, drwav_uint64 sampleCount, drwav_uint32 bytesPerSample) { - if (ma_dr_wav_is_container_be(container)) { - return ma_dr_wav_bytes_to_u32_be(data); - } else { - return ma_dr_wav_bytes_to_u32_le(data); + switch (bytesPerSample) + { + #if 0 + case 2: + { + drwav__bswap_samples_f16((drwav_float16*)pSamples, sampleCount); + } break; + #endif + case 4: + { + drwav__bswap_samples_f32((float*)pSamples, sampleCount); + } break; + case 8: + { + drwav__bswap_samples_f64((double*)pSamples, sampleCount); + } break; + default: + { + DRWAV_ASSERT(DRWAV_FALSE); + } break; } } -MA_PRIVATE ma_int64 ma_dr_wav_aiff_extented_to_s64(const ma_uint8* data) +static DRWAV_INLINE void drwav__bswap_samples(void* pSamples, drwav_uint64 sampleCount, drwav_uint32 bytesPerSample, drwav_uint16 format) { - ma_uint32 exponent = ((ma_uint32)data[0] << 8) | data[1]; - ma_uint64 hi = ((ma_uint64)data[2] << 24) | ((ma_uint64)data[3] << 16) | ((ma_uint64)data[4] << 8) | ((ma_uint64)data[5] << 0); - ma_uint64 lo = ((ma_uint64)data[6] << 24) | ((ma_uint64)data[7] << 16) | ((ma_uint64)data[8] << 8) | ((ma_uint64)data[9] << 0); - ma_uint64 significand = (hi << 32) | lo; - int sign = exponent >> 15; - exponent &= 0x7FFF; - if (exponent == 0 && significand == 0) { - return 0; - } else if (exponent == 0x7FFF) { - return sign ? MA_DR_WAV_INT64_MIN : MA_DR_WAV_INT64_MAX; - } - exponent -= 16383; - if (exponent > 63) { - return sign ? MA_DR_WAV_INT64_MIN : MA_DR_WAV_INT64_MAX; - } else if (exponent < 1) { - return 0; - } - significand >>= (63 - exponent); - if (sign) { - return -(ma_int64)significand; - } else { - return (ma_int64)significand; + switch (format) + { + case DR_WAVE_FORMAT_PCM: + { + drwav__bswap_samples_pcm(pSamples, sampleCount, bytesPerSample); + } break; + case DR_WAVE_FORMAT_IEEE_FLOAT: + { + drwav__bswap_samples_ieee(pSamples, sampleCount, bytesPerSample); + } break; + case DR_WAVE_FORMAT_ALAW: + case DR_WAVE_FORMAT_MULAW: + { + drwav__bswap_samples_s16((drwav_int16*)pSamples, sampleCount); + } break; + case DR_WAVE_FORMAT_ADPCM: + case DR_WAVE_FORMAT_DVI_ADPCM: + default: + { + DRWAV_ASSERT(DRWAV_FALSE); + } break; } } -MA_PRIVATE void* ma_dr_wav__malloc_default(size_t sz, void* pUserData) +DRWAV_PRIVATE void* drwav__malloc_default(size_t sz, void* pUserData) { (void)pUserData; - return MA_DR_WAV_MALLOC(sz); + return DRWAV_MALLOC(sz); } -MA_PRIVATE void* ma_dr_wav__realloc_default(void* p, size_t sz, void* pUserData) +DRWAV_PRIVATE void* drwav__realloc_default(void* p, size_t sz, void* pUserData) { (void)pUserData; - return MA_DR_WAV_REALLOC(p, sz); + return DRWAV_REALLOC(p, sz); } -MA_PRIVATE void ma_dr_wav__free_default(void* p, void* pUserData) +DRWAV_PRIVATE void drwav__free_default(void* p, void* pUserData) { (void)pUserData; - MA_DR_WAV_FREE(p); + DRWAV_FREE(p); } -MA_PRIVATE void* ma_dr_wav__malloc_from_callbacks(size_t sz, const ma_allocation_callbacks* pAllocationCallbacks) +DRWAV_PRIVATE void* drwav__malloc_from_callbacks(size_t sz, const drwav_allocation_callbacks* pAllocationCallbacks) { if (pAllocationCallbacks == NULL) { return NULL; @@ -78444,7 +76952,7 @@ MA_PRIVATE void* ma_dr_wav__malloc_from_callbacks(size_t sz, const ma_allocation } return NULL; } -MA_PRIVATE void* ma_dr_wav__realloc_from_callbacks(void* p, size_t szNew, size_t szOld, const ma_allocation_callbacks* pAllocationCallbacks) +DRWAV_PRIVATE void* drwav__realloc_from_callbacks(void* p, size_t szNew, size_t szOld, const drwav_allocation_callbacks* pAllocationCallbacks) { if (pAllocationCallbacks == NULL) { return NULL; @@ -78459,14 +76967,14 @@ MA_PRIVATE void* ma_dr_wav__realloc_from_callbacks(void* p, size_t szNew, size_t return NULL; } if (p != NULL) { - MA_DR_WAV_COPY_MEMORY(p2, p, szOld); + DRWAV_COPY_MEMORY(p2, p, szOld); pAllocationCallbacks->onFree(p, pAllocationCallbacks->pUserData); } return p2; } return NULL; } -MA_PRIVATE void ma_dr_wav__free_from_callbacks(void* p, const ma_allocation_callbacks* pAllocationCallbacks) +DRWAV_PRIVATE void drwav__free_from_callbacks(void* p, const drwav_allocation_callbacks* pAllocationCallbacks) { if (p == NULL || pAllocationCallbacks == NULL) { return; @@ -78475,288 +76983,369 @@ MA_PRIVATE void ma_dr_wav__free_from_callbacks(void* p, const ma_allocation_call pAllocationCallbacks->onFree(p, pAllocationCallbacks->pUserData); } } -MA_PRIVATE ma_allocation_callbacks ma_dr_wav_copy_allocation_callbacks_or_defaults(const ma_allocation_callbacks* pAllocationCallbacks) +DRWAV_PRIVATE drwav_allocation_callbacks drwav_copy_allocation_callbacks_or_defaults(const drwav_allocation_callbacks* pAllocationCallbacks) { if (pAllocationCallbacks != NULL) { return *pAllocationCallbacks; } else { - ma_allocation_callbacks allocationCallbacks; + drwav_allocation_callbacks allocationCallbacks; allocationCallbacks.pUserData = NULL; - allocationCallbacks.onMalloc = ma_dr_wav__malloc_default; - allocationCallbacks.onRealloc = ma_dr_wav__realloc_default; - allocationCallbacks.onFree = ma_dr_wav__free_default; + allocationCallbacks.onMalloc = drwav__malloc_default; + allocationCallbacks.onRealloc = drwav__realloc_default; + allocationCallbacks.onFree = drwav__free_default; return allocationCallbacks; } } -static MA_INLINE ma_bool32 ma_dr_wav__is_compressed_format_tag(ma_uint16 formatTag) +static DRWAV_INLINE drwav_bool32 drwav__is_compressed_format_tag(drwav_uint16 formatTag) { return - formatTag == MA_DR_WAVE_FORMAT_ADPCM || - formatTag == MA_DR_WAVE_FORMAT_DVI_ADPCM; + formatTag == DR_WAVE_FORMAT_ADPCM || + formatTag == DR_WAVE_FORMAT_DVI_ADPCM; } -MA_PRIVATE unsigned int ma_dr_wav__chunk_padding_size_riff(ma_uint64 chunkSize) +DRWAV_PRIVATE unsigned int drwav__chunk_padding_size_riff(drwav_uint64 chunkSize) { return (unsigned int)(chunkSize % 2); } -MA_PRIVATE unsigned int ma_dr_wav__chunk_padding_size_w64(ma_uint64 chunkSize) +DRWAV_PRIVATE unsigned int drwav__chunk_padding_size_w64(drwav_uint64 chunkSize) { return (unsigned int)(chunkSize % 8); } -MA_PRIVATE ma_uint64 ma_dr_wav_read_pcm_frames_s16__msadpcm(ma_dr_wav* pWav, ma_uint64 samplesToRead, ma_int16* pBufferOut); -MA_PRIVATE ma_uint64 ma_dr_wav_read_pcm_frames_s16__ima(ma_dr_wav* pWav, ma_uint64 samplesToRead, ma_int16* pBufferOut); -MA_PRIVATE ma_bool32 ma_dr_wav_init_write__internal(ma_dr_wav* pWav, const ma_dr_wav_data_format* pFormat, ma_uint64 totalSampleCount); -MA_PRIVATE ma_result ma_dr_wav__read_chunk_header(ma_dr_wav_read_proc onRead, void* pUserData, ma_dr_wav_container container, ma_uint64* pRunningBytesReadOut, ma_dr_wav_chunk_header* pHeaderOut) +DRWAV_PRIVATE drwav_uint64 drwav_read_pcm_frames_s16__msadpcm(drwav* pWav, drwav_uint64 samplesToRead, drwav_int16* pBufferOut); +DRWAV_PRIVATE drwav_uint64 drwav_read_pcm_frames_s16__ima(drwav* pWav, drwav_uint64 samplesToRead, drwav_int16* pBufferOut); +DRWAV_PRIVATE drwav_bool32 drwav_init_write__internal(drwav* pWav, const drwav_data_format* pFormat, drwav_uint64 totalSampleCount); +DRWAV_PRIVATE drwav_result drwav__read_chunk_header(drwav_read_proc onRead, void* pUserData, drwav_container container, drwav_uint64* pRunningBytesReadOut, drwav_chunk_header* pHeaderOut) { - if (container == ma_dr_wav_container_riff || container == ma_dr_wav_container_rifx || container == ma_dr_wav_container_rf64 || container == ma_dr_wav_container_aiff) { - ma_uint8 sizeInBytes[4]; + if (container == drwav_container_riff || container == drwav_container_rf64) { + drwav_uint8 sizeInBytes[4]; if (onRead(pUserData, pHeaderOut->id.fourcc, 4) != 4) { - return MA_AT_END; + return DRWAV_AT_END; } if (onRead(pUserData, sizeInBytes, 4) != 4) { - return MA_INVALID_FILE; + return DRWAV_INVALID_FILE; } - pHeaderOut->sizeInBytes = ma_dr_wav_bytes_to_u32_ex(sizeInBytes, container); - pHeaderOut->paddingSize = ma_dr_wav__chunk_padding_size_riff(pHeaderOut->sizeInBytes); + pHeaderOut->sizeInBytes = drwav_bytes_to_u32(sizeInBytes); + pHeaderOut->paddingSize = drwav__chunk_padding_size_riff(pHeaderOut->sizeInBytes); *pRunningBytesReadOut += 8; - } else if (container == ma_dr_wav_container_w64) { - ma_uint8 sizeInBytes[8]; + } else { + drwav_uint8 sizeInBytes[8]; if (onRead(pUserData, pHeaderOut->id.guid, 16) != 16) { - return MA_AT_END; + return DRWAV_AT_END; } if (onRead(pUserData, sizeInBytes, 8) != 8) { - return MA_INVALID_FILE; + return DRWAV_INVALID_FILE; } - pHeaderOut->sizeInBytes = ma_dr_wav_bytes_to_u64(sizeInBytes) - 24; - pHeaderOut->paddingSize = ma_dr_wav__chunk_padding_size_w64(pHeaderOut->sizeInBytes); + pHeaderOut->sizeInBytes = drwav_bytes_to_u64(sizeInBytes) - 24; + pHeaderOut->paddingSize = drwav__chunk_padding_size_w64(pHeaderOut->sizeInBytes); *pRunningBytesReadOut += 24; - } else { - return MA_INVALID_FILE; } - return MA_SUCCESS; + return DRWAV_SUCCESS; } -MA_PRIVATE ma_bool32 ma_dr_wav__seek_forward(ma_dr_wav_seek_proc onSeek, ma_uint64 offset, void* pUserData) +DRWAV_PRIVATE drwav_bool32 drwav__seek_forward(drwav_seek_proc onSeek, drwav_uint64 offset, void* pUserData) { - ma_uint64 bytesRemainingToSeek = offset; + drwav_uint64 bytesRemainingToSeek = offset; while (bytesRemainingToSeek > 0) { if (bytesRemainingToSeek > 0x7FFFFFFF) { - if (!onSeek(pUserData, 0x7FFFFFFF, ma_dr_wav_seek_origin_current)) { - return MA_FALSE; + if (!onSeek(pUserData, 0x7FFFFFFF, drwav_seek_origin_current)) { + return DRWAV_FALSE; } bytesRemainingToSeek -= 0x7FFFFFFF; } else { - if (!onSeek(pUserData, (int)bytesRemainingToSeek, ma_dr_wav_seek_origin_current)) { - return MA_FALSE; + if (!onSeek(pUserData, (int)bytesRemainingToSeek, drwav_seek_origin_current)) { + return DRWAV_FALSE; } bytesRemainingToSeek = 0; } } - return MA_TRUE; + return DRWAV_TRUE; } -MA_PRIVATE ma_bool32 ma_dr_wav__seek_from_start(ma_dr_wav_seek_proc onSeek, ma_uint64 offset, void* pUserData) +DRWAV_PRIVATE drwav_bool32 drwav__seek_from_start(drwav_seek_proc onSeek, drwav_uint64 offset, void* pUserData) { if (offset <= 0x7FFFFFFF) { - return onSeek(pUserData, (int)offset, ma_dr_wav_seek_origin_start); + return onSeek(pUserData, (int)offset, drwav_seek_origin_start); } - if (!onSeek(pUserData, 0x7FFFFFFF, ma_dr_wav_seek_origin_start)) { - return MA_FALSE; + if (!onSeek(pUserData, 0x7FFFFFFF, drwav_seek_origin_start)) { + return DRWAV_FALSE; } offset -= 0x7FFFFFFF; for (;;) { if (offset <= 0x7FFFFFFF) { - return onSeek(pUserData, (int)offset, ma_dr_wav_seek_origin_current); + return onSeek(pUserData, (int)offset, drwav_seek_origin_current); } - if (!onSeek(pUserData, 0x7FFFFFFF, ma_dr_wav_seek_origin_current)) { - return MA_FALSE; + if (!onSeek(pUserData, 0x7FFFFFFF, drwav_seek_origin_current)) { + return DRWAV_FALSE; } offset -= 0x7FFFFFFF; } } -MA_PRIVATE size_t ma_dr_wav__on_read(ma_dr_wav_read_proc onRead, void* pUserData, void* pBufferOut, size_t bytesToRead, ma_uint64* pCursor) +DRWAV_PRIVATE drwav_bool32 drwav__read_fmt(drwav_read_proc onRead, drwav_seek_proc onSeek, void* pUserData, drwav_container container, drwav_uint64* pRunningBytesReadOut, drwav_fmt* fmtOut) +{ + drwav_chunk_header header; + drwav_uint8 fmt[16]; + if (drwav__read_chunk_header(onRead, pUserData, container, pRunningBytesReadOut, &header) != DRWAV_SUCCESS) { + return DRWAV_FALSE; + } + while (((container == drwav_container_riff || container == drwav_container_rf64) && !drwav_fourcc_equal(header.id.fourcc, "fmt ")) || (container == drwav_container_w64 && !drwav_guid_equal(header.id.guid, drwavGUID_W64_FMT))) { + if (!drwav__seek_forward(onSeek, header.sizeInBytes + header.paddingSize, pUserData)) { + return DRWAV_FALSE; + } + *pRunningBytesReadOut += header.sizeInBytes + header.paddingSize; + if (drwav__read_chunk_header(onRead, pUserData, container, pRunningBytesReadOut, &header) != DRWAV_SUCCESS) { + return DRWAV_FALSE; + } + } + if (container == drwav_container_riff || container == drwav_container_rf64) { + if (!drwav_fourcc_equal(header.id.fourcc, "fmt ")) { + return DRWAV_FALSE; + } + } else { + if (!drwav_guid_equal(header.id.guid, drwavGUID_W64_FMT)) { + return DRWAV_FALSE; + } + } + if (onRead(pUserData, fmt, sizeof(fmt)) != sizeof(fmt)) { + return DRWAV_FALSE; + } + *pRunningBytesReadOut += sizeof(fmt); + fmtOut->formatTag = drwav_bytes_to_u16(fmt + 0); + fmtOut->channels = drwav_bytes_to_u16(fmt + 2); + fmtOut->sampleRate = drwav_bytes_to_u32(fmt + 4); + fmtOut->avgBytesPerSec = drwav_bytes_to_u32(fmt + 8); + fmtOut->blockAlign = drwav_bytes_to_u16(fmt + 12); + fmtOut->bitsPerSample = drwav_bytes_to_u16(fmt + 14); + fmtOut->extendedSize = 0; + fmtOut->validBitsPerSample = 0; + fmtOut->channelMask = 0; + DRWAV_ZERO_MEMORY(fmtOut->subFormat, sizeof(fmtOut->subFormat)); + if (header.sizeInBytes > 16) { + drwav_uint8 fmt_cbSize[2]; + int bytesReadSoFar = 0; + if (onRead(pUserData, fmt_cbSize, sizeof(fmt_cbSize)) != sizeof(fmt_cbSize)) { + return DRWAV_FALSE; + } + *pRunningBytesReadOut += sizeof(fmt_cbSize); + bytesReadSoFar = 18; + fmtOut->extendedSize = drwav_bytes_to_u16(fmt_cbSize); + if (fmtOut->extendedSize > 0) { + if (fmtOut->formatTag == DR_WAVE_FORMAT_EXTENSIBLE) { + if (fmtOut->extendedSize != 22) { + return DRWAV_FALSE; + } + } + if (fmtOut->formatTag == DR_WAVE_FORMAT_EXTENSIBLE) { + drwav_uint8 fmtext[22]; + if (onRead(pUserData, fmtext, fmtOut->extendedSize) != fmtOut->extendedSize) { + return DRWAV_FALSE; + } + fmtOut->validBitsPerSample = drwav_bytes_to_u16(fmtext + 0); + fmtOut->channelMask = drwav_bytes_to_u32(fmtext + 2); + drwav_bytes_to_guid(fmtext + 6, fmtOut->subFormat); + } else { + if (!onSeek(pUserData, fmtOut->extendedSize, drwav_seek_origin_current)) { + return DRWAV_FALSE; + } + } + *pRunningBytesReadOut += fmtOut->extendedSize; + bytesReadSoFar += fmtOut->extendedSize; + } + if (!onSeek(pUserData, (int)(header.sizeInBytes - bytesReadSoFar), drwav_seek_origin_current)) { + return DRWAV_FALSE; + } + *pRunningBytesReadOut += (header.sizeInBytes - bytesReadSoFar); + } + if (header.paddingSize > 0) { + if (!onSeek(pUserData, header.paddingSize, drwav_seek_origin_current)) { + return DRWAV_FALSE; + } + *pRunningBytesReadOut += header.paddingSize; + } + return DRWAV_TRUE; +} +DRWAV_PRIVATE size_t drwav__on_read(drwav_read_proc onRead, void* pUserData, void* pBufferOut, size_t bytesToRead, drwav_uint64* pCursor) { size_t bytesRead; - MA_DR_WAV_ASSERT(onRead != NULL); - MA_DR_WAV_ASSERT(pCursor != NULL); + DRWAV_ASSERT(onRead != NULL); + DRWAV_ASSERT(pCursor != NULL); bytesRead = onRead(pUserData, pBufferOut, bytesToRead); *pCursor += bytesRead; return bytesRead; } #if 0 -MA_PRIVATE ma_bool32 ma_dr_wav__on_seek(ma_dr_wav_seek_proc onSeek, void* pUserData, int offset, ma_dr_wav_seek_origin origin, ma_uint64* pCursor) +DRWAV_PRIVATE drwav_bool32 drwav__on_seek(drwav_seek_proc onSeek, void* pUserData, int offset, drwav_seek_origin origin, drwav_uint64* pCursor) { - MA_DR_WAV_ASSERT(onSeek != NULL); - MA_DR_WAV_ASSERT(pCursor != NULL); + DRWAV_ASSERT(onSeek != NULL); + DRWAV_ASSERT(pCursor != NULL); if (!onSeek(pUserData, offset, origin)) { - return MA_FALSE; + return DRWAV_FALSE; } - if (origin == ma_dr_wav_seek_origin_start) { + if (origin == drwav_seek_origin_start) { *pCursor = offset; } else { *pCursor += offset; } - return MA_TRUE; -} -#endif -#define MA_DR_WAV_SMPL_BYTES 36 -#define MA_DR_WAV_SMPL_LOOP_BYTES 24 -#define MA_DR_WAV_INST_BYTES 7 -#define MA_DR_WAV_ACID_BYTES 24 -#define MA_DR_WAV_CUE_BYTES 4 -#define MA_DR_WAV_BEXT_BYTES 602 -#define MA_DR_WAV_BEXT_DESCRIPTION_BYTES 256 -#define MA_DR_WAV_BEXT_ORIGINATOR_NAME_BYTES 32 -#define MA_DR_WAV_BEXT_ORIGINATOR_REF_BYTES 32 -#define MA_DR_WAV_BEXT_RESERVED_BYTES 180 -#define MA_DR_WAV_BEXT_UMID_BYTES 64 -#define MA_DR_WAV_CUE_POINT_BYTES 24 -#define MA_DR_WAV_LIST_LABEL_OR_NOTE_BYTES 4 -#define MA_DR_WAV_LIST_LABELLED_TEXT_BYTES 20 -#define MA_DR_WAV_METADATA_ALIGNMENT 8 + return DRWAV_TRUE; +} +#endif +#define DRWAV_SMPL_BYTES 36 +#define DRWAV_SMPL_LOOP_BYTES 24 +#define DRWAV_INST_BYTES 7 +#define DRWAV_ACID_BYTES 24 +#define DRWAV_CUE_BYTES 4 +#define DRWAV_BEXT_BYTES 602 +#define DRWAV_BEXT_DESCRIPTION_BYTES 256 +#define DRWAV_BEXT_ORIGINATOR_NAME_BYTES 32 +#define DRWAV_BEXT_ORIGINATOR_REF_BYTES 32 +#define DRWAV_BEXT_RESERVED_BYTES 180 +#define DRWAV_BEXT_UMID_BYTES 64 +#define DRWAV_CUE_POINT_BYTES 24 +#define DRWAV_LIST_LABEL_OR_NOTE_BYTES 4 +#define DRWAV_LIST_LABELLED_TEXT_BYTES 20 +#define DRWAV_METADATA_ALIGNMENT 8 typedef enum { - ma_dr_wav__metadata_parser_stage_count, - ma_dr_wav__metadata_parser_stage_read -} ma_dr_wav__metadata_parser_stage; + drwav__metadata_parser_stage_count, + drwav__metadata_parser_stage_read +} drwav__metadata_parser_stage; typedef struct { - ma_dr_wav_read_proc onRead; - ma_dr_wav_seek_proc onSeek; + drwav_read_proc onRead; + drwav_seek_proc onSeek; void *pReadSeekUserData; - ma_dr_wav__metadata_parser_stage stage; - ma_dr_wav_metadata *pMetadata; - ma_uint32 metadataCount; - ma_uint8 *pData; - ma_uint8 *pDataCursor; - ma_uint64 metadataCursor; - ma_uint64 extraCapacity; -} ma_dr_wav__metadata_parser; -MA_PRIVATE size_t ma_dr_wav__metadata_memory_capacity(ma_dr_wav__metadata_parser* pParser) -{ - ma_uint64 cap = sizeof(ma_dr_wav_metadata) * (ma_uint64)pParser->metadataCount + pParser->extraCapacity; - if (cap > MA_SIZE_MAX) { + drwav__metadata_parser_stage stage; + drwav_metadata *pMetadata; + drwav_uint32 metadataCount; + drwav_uint8 *pData; + drwav_uint8 *pDataCursor; + drwav_uint64 metadataCursor; + drwav_uint64 extraCapacity; +} drwav__metadata_parser; +DRWAV_PRIVATE size_t drwav__metadata_memory_capacity(drwav__metadata_parser* pParser) +{ + drwav_uint64 cap = sizeof(drwav_metadata) * (drwav_uint64)pParser->metadataCount + pParser->extraCapacity; + if (cap > DRWAV_SIZE_MAX) { return 0; } return (size_t)cap; } -MA_PRIVATE ma_uint8* ma_dr_wav__metadata_get_memory(ma_dr_wav__metadata_parser* pParser, size_t size, size_t align) +DRWAV_PRIVATE drwav_uint8* drwav__metadata_get_memory(drwav__metadata_parser* pParser, size_t size, size_t align) { - ma_uint8* pResult; + drwav_uint8* pResult; if (align) { - ma_uintptr modulo = (ma_uintptr)pParser->pDataCursor % align; + drwav_uintptr modulo = (drwav_uintptr)pParser->pDataCursor % align; if (modulo != 0) { pParser->pDataCursor += align - modulo; } } pResult = pParser->pDataCursor; - MA_DR_WAV_ASSERT((pResult + size) <= (pParser->pData + ma_dr_wav__metadata_memory_capacity(pParser))); + DRWAV_ASSERT((pResult + size) <= (pParser->pData + drwav__metadata_memory_capacity(pParser))); pParser->pDataCursor += size; return pResult; } -MA_PRIVATE void ma_dr_wav__metadata_request_extra_memory_for_stage_2(ma_dr_wav__metadata_parser* pParser, size_t bytes, size_t align) +DRWAV_PRIVATE void drwav__metadata_request_extra_memory_for_stage_2(drwav__metadata_parser* pParser, size_t bytes, size_t align) { size_t extra = bytes + (align ? (align - 1) : 0); pParser->extraCapacity += extra; } -MA_PRIVATE ma_result ma_dr_wav__metadata_alloc(ma_dr_wav__metadata_parser* pParser, ma_allocation_callbacks* pAllocationCallbacks) +DRWAV_PRIVATE drwav_result drwav__metadata_alloc(drwav__metadata_parser* pParser, drwav_allocation_callbacks* pAllocationCallbacks) { if (pParser->extraCapacity != 0 || pParser->metadataCount != 0) { pAllocationCallbacks->onFree(pParser->pData, pAllocationCallbacks->pUserData); - pParser->pData = (ma_uint8*)pAllocationCallbacks->onMalloc(ma_dr_wav__metadata_memory_capacity(pParser), pAllocationCallbacks->pUserData); + pParser->pData = (drwav_uint8*)pAllocationCallbacks->onMalloc(drwav__metadata_memory_capacity(pParser), pAllocationCallbacks->pUserData); pParser->pDataCursor = pParser->pData; if (pParser->pData == NULL) { - return MA_OUT_OF_MEMORY; + return DRWAV_OUT_OF_MEMORY; } - pParser->pMetadata = (ma_dr_wav_metadata*)ma_dr_wav__metadata_get_memory(pParser, sizeof(ma_dr_wav_metadata) * pParser->metadataCount, 1); + pParser->pMetadata = (drwav_metadata*)drwav__metadata_get_memory(pParser, sizeof(drwav_metadata) * pParser->metadataCount, 1); pParser->metadataCursor = 0; } - return MA_SUCCESS; + return DRWAV_SUCCESS; } -MA_PRIVATE size_t ma_dr_wav__metadata_parser_read(ma_dr_wav__metadata_parser* pParser, void* pBufferOut, size_t bytesToRead, ma_uint64* pCursor) +DRWAV_PRIVATE size_t drwav__metadata_parser_read(drwav__metadata_parser* pParser, void* pBufferOut, size_t bytesToRead, drwav_uint64* pCursor) { if (pCursor != NULL) { - return ma_dr_wav__on_read(pParser->onRead, pParser->pReadSeekUserData, pBufferOut, bytesToRead, pCursor); + return drwav__on_read(pParser->onRead, pParser->pReadSeekUserData, pBufferOut, bytesToRead, pCursor); } else { return pParser->onRead(pParser->pReadSeekUserData, pBufferOut, bytesToRead); } } -MA_PRIVATE ma_uint64 ma_dr_wav__read_smpl_to_metadata_obj(ma_dr_wav__metadata_parser* pParser, const ma_dr_wav_chunk_header* pChunkHeader, ma_dr_wav_metadata* pMetadata) +DRWAV_PRIVATE drwav_uint64 drwav__read_smpl_to_metadata_obj(drwav__metadata_parser* pParser, const drwav_chunk_header* pChunkHeader, drwav_metadata* pMetadata) { - ma_uint8 smplHeaderData[MA_DR_WAV_SMPL_BYTES]; - ma_uint64 totalBytesRead = 0; + drwav_uint8 smplHeaderData[DRWAV_SMPL_BYTES]; + drwav_uint64 totalBytesRead = 0; size_t bytesJustRead; if (pMetadata == NULL) { return 0; } - bytesJustRead = ma_dr_wav__metadata_parser_read(pParser, smplHeaderData, sizeof(smplHeaderData), &totalBytesRead); - MA_DR_WAV_ASSERT(pParser->stage == ma_dr_wav__metadata_parser_stage_read); - MA_DR_WAV_ASSERT(pChunkHeader != NULL); + bytesJustRead = drwav__metadata_parser_read(pParser, smplHeaderData, sizeof(smplHeaderData), &totalBytesRead); + DRWAV_ASSERT(pParser->stage == drwav__metadata_parser_stage_read); + DRWAV_ASSERT(pChunkHeader != NULL); if (pMetadata != NULL && bytesJustRead == sizeof(smplHeaderData)) { - ma_uint32 iSampleLoop; - pMetadata->type = ma_dr_wav_metadata_type_smpl; - pMetadata->data.smpl.manufacturerId = ma_dr_wav_bytes_to_u32(smplHeaderData + 0); - pMetadata->data.smpl.productId = ma_dr_wav_bytes_to_u32(smplHeaderData + 4); - pMetadata->data.smpl.samplePeriodNanoseconds = ma_dr_wav_bytes_to_u32(smplHeaderData + 8); - pMetadata->data.smpl.midiUnityNote = ma_dr_wav_bytes_to_u32(smplHeaderData + 12); - pMetadata->data.smpl.midiPitchFraction = ma_dr_wav_bytes_to_u32(smplHeaderData + 16); - pMetadata->data.smpl.smpteFormat = ma_dr_wav_bytes_to_u32(smplHeaderData + 20); - pMetadata->data.smpl.smpteOffset = ma_dr_wav_bytes_to_u32(smplHeaderData + 24); - pMetadata->data.smpl.sampleLoopCount = ma_dr_wav_bytes_to_u32(smplHeaderData + 28); - pMetadata->data.smpl.samplerSpecificDataSizeInBytes = ma_dr_wav_bytes_to_u32(smplHeaderData + 32); - if (pMetadata->data.smpl.sampleLoopCount == (pChunkHeader->sizeInBytes - MA_DR_WAV_SMPL_BYTES) / MA_DR_WAV_SMPL_LOOP_BYTES) { - pMetadata->data.smpl.pLoops = (ma_dr_wav_smpl_loop*)ma_dr_wav__metadata_get_memory(pParser, sizeof(ma_dr_wav_smpl_loop) * pMetadata->data.smpl.sampleLoopCount, MA_DR_WAV_METADATA_ALIGNMENT); + drwav_uint32 iSampleLoop; + pMetadata->type = drwav_metadata_type_smpl; + pMetadata->data.smpl.manufacturerId = drwav_bytes_to_u32(smplHeaderData + 0); + pMetadata->data.smpl.productId = drwav_bytes_to_u32(smplHeaderData + 4); + pMetadata->data.smpl.samplePeriodNanoseconds = drwav_bytes_to_u32(smplHeaderData + 8); + pMetadata->data.smpl.midiUnityNote = drwav_bytes_to_u32(smplHeaderData + 12); + pMetadata->data.smpl.midiPitchFraction = drwav_bytes_to_u32(smplHeaderData + 16); + pMetadata->data.smpl.smpteFormat = drwav_bytes_to_u32(smplHeaderData + 20); + pMetadata->data.smpl.smpteOffset = drwav_bytes_to_u32(smplHeaderData + 24); + pMetadata->data.smpl.sampleLoopCount = drwav_bytes_to_u32(smplHeaderData + 28); + pMetadata->data.smpl.samplerSpecificDataSizeInBytes = drwav_bytes_to_u32(smplHeaderData + 32); + if (pMetadata->data.smpl.sampleLoopCount == (pChunkHeader->sizeInBytes - DRWAV_SMPL_BYTES) / DRWAV_SMPL_LOOP_BYTES) { + pMetadata->data.smpl.pLoops = (drwav_smpl_loop*)drwav__metadata_get_memory(pParser, sizeof(drwav_smpl_loop) * pMetadata->data.smpl.sampleLoopCount, DRWAV_METADATA_ALIGNMENT); for (iSampleLoop = 0; iSampleLoop < pMetadata->data.smpl.sampleLoopCount; ++iSampleLoop) { - ma_uint8 smplLoopData[MA_DR_WAV_SMPL_LOOP_BYTES]; - bytesJustRead = ma_dr_wav__metadata_parser_read(pParser, smplLoopData, sizeof(smplLoopData), &totalBytesRead); + drwav_uint8 smplLoopData[DRWAV_SMPL_LOOP_BYTES]; + bytesJustRead = drwav__metadata_parser_read(pParser, smplLoopData, sizeof(smplLoopData), &totalBytesRead); if (bytesJustRead == sizeof(smplLoopData)) { - pMetadata->data.smpl.pLoops[iSampleLoop].cuePointId = ma_dr_wav_bytes_to_u32(smplLoopData + 0); - pMetadata->data.smpl.pLoops[iSampleLoop].type = ma_dr_wav_bytes_to_u32(smplLoopData + 4); - pMetadata->data.smpl.pLoops[iSampleLoop].firstSampleByteOffset = ma_dr_wav_bytes_to_u32(smplLoopData + 8); - pMetadata->data.smpl.pLoops[iSampleLoop].lastSampleByteOffset = ma_dr_wav_bytes_to_u32(smplLoopData + 12); - pMetadata->data.smpl.pLoops[iSampleLoop].sampleFraction = ma_dr_wav_bytes_to_u32(smplLoopData + 16); - pMetadata->data.smpl.pLoops[iSampleLoop].playCount = ma_dr_wav_bytes_to_u32(smplLoopData + 20); + pMetadata->data.smpl.pLoops[iSampleLoop].cuePointId = drwav_bytes_to_u32(smplLoopData + 0); + pMetadata->data.smpl.pLoops[iSampleLoop].type = drwav_bytes_to_u32(smplLoopData + 4); + pMetadata->data.smpl.pLoops[iSampleLoop].firstSampleByteOffset = drwav_bytes_to_u32(smplLoopData + 8); + pMetadata->data.smpl.pLoops[iSampleLoop].lastSampleByteOffset = drwav_bytes_to_u32(smplLoopData + 12); + pMetadata->data.smpl.pLoops[iSampleLoop].sampleFraction = drwav_bytes_to_u32(smplLoopData + 16); + pMetadata->data.smpl.pLoops[iSampleLoop].playCount = drwav_bytes_to_u32(smplLoopData + 20); } else { break; } } if (pMetadata->data.smpl.samplerSpecificDataSizeInBytes > 0) { - pMetadata->data.smpl.pSamplerSpecificData = ma_dr_wav__metadata_get_memory(pParser, pMetadata->data.smpl.samplerSpecificDataSizeInBytes, 1); - MA_DR_WAV_ASSERT(pMetadata->data.smpl.pSamplerSpecificData != NULL); - ma_dr_wav__metadata_parser_read(pParser, pMetadata->data.smpl.pSamplerSpecificData, pMetadata->data.smpl.samplerSpecificDataSizeInBytes, &totalBytesRead); + pMetadata->data.smpl.pSamplerSpecificData = drwav__metadata_get_memory(pParser, pMetadata->data.smpl.samplerSpecificDataSizeInBytes, 1); + DRWAV_ASSERT(pMetadata->data.smpl.pSamplerSpecificData != NULL); + drwav__metadata_parser_read(pParser, pMetadata->data.smpl.pSamplerSpecificData, pMetadata->data.smpl.samplerSpecificDataSizeInBytes, &totalBytesRead); } } } return totalBytesRead; } -MA_PRIVATE ma_uint64 ma_dr_wav__read_cue_to_metadata_obj(ma_dr_wav__metadata_parser* pParser, const ma_dr_wav_chunk_header* pChunkHeader, ma_dr_wav_metadata* pMetadata) +DRWAV_PRIVATE drwav_uint64 drwav__read_cue_to_metadata_obj(drwav__metadata_parser* pParser, const drwav_chunk_header* pChunkHeader, drwav_metadata* pMetadata) { - ma_uint8 cueHeaderSectionData[MA_DR_WAV_CUE_BYTES]; - ma_uint64 totalBytesRead = 0; + drwav_uint8 cueHeaderSectionData[DRWAV_CUE_BYTES]; + drwav_uint64 totalBytesRead = 0; size_t bytesJustRead; if (pMetadata == NULL) { return 0; } - bytesJustRead = ma_dr_wav__metadata_parser_read(pParser, cueHeaderSectionData, sizeof(cueHeaderSectionData), &totalBytesRead); - MA_DR_WAV_ASSERT(pParser->stage == ma_dr_wav__metadata_parser_stage_read); + bytesJustRead = drwav__metadata_parser_read(pParser, cueHeaderSectionData, sizeof(cueHeaderSectionData), &totalBytesRead); + DRWAV_ASSERT(pParser->stage == drwav__metadata_parser_stage_read); if (bytesJustRead == sizeof(cueHeaderSectionData)) { - pMetadata->type = ma_dr_wav_metadata_type_cue; - pMetadata->data.cue.cuePointCount = ma_dr_wav_bytes_to_u32(cueHeaderSectionData); - if (pMetadata->data.cue.cuePointCount == (pChunkHeader->sizeInBytes - MA_DR_WAV_CUE_BYTES) / MA_DR_WAV_CUE_POINT_BYTES) { - pMetadata->data.cue.pCuePoints = (ma_dr_wav_cue_point*)ma_dr_wav__metadata_get_memory(pParser, sizeof(ma_dr_wav_cue_point) * pMetadata->data.cue.cuePointCount, MA_DR_WAV_METADATA_ALIGNMENT); - MA_DR_WAV_ASSERT(pMetadata->data.cue.pCuePoints != NULL); + pMetadata->type = drwav_metadata_type_cue; + pMetadata->data.cue.cuePointCount = drwav_bytes_to_u32(cueHeaderSectionData); + if (pMetadata->data.cue.cuePointCount == (pChunkHeader->sizeInBytes - DRWAV_CUE_BYTES) / DRWAV_CUE_POINT_BYTES) { + pMetadata->data.cue.pCuePoints = (drwav_cue_point*)drwav__metadata_get_memory(pParser, sizeof(drwav_cue_point) * pMetadata->data.cue.cuePointCount, DRWAV_METADATA_ALIGNMENT); + DRWAV_ASSERT(pMetadata->data.cue.pCuePoints != NULL); if (pMetadata->data.cue.cuePointCount > 0) { - ma_uint32 iCuePoint; + drwav_uint32 iCuePoint; for (iCuePoint = 0; iCuePoint < pMetadata->data.cue.cuePointCount; ++iCuePoint) { - ma_uint8 cuePointData[MA_DR_WAV_CUE_POINT_BYTES]; - bytesJustRead = ma_dr_wav__metadata_parser_read(pParser, cuePointData, sizeof(cuePointData), &totalBytesRead); + drwav_uint8 cuePointData[DRWAV_CUE_POINT_BYTES]; + bytesJustRead = drwav__metadata_parser_read(pParser, cuePointData, sizeof(cuePointData), &totalBytesRead); if (bytesJustRead == sizeof(cuePointData)) { - pMetadata->data.cue.pCuePoints[iCuePoint].id = ma_dr_wav_bytes_to_u32(cuePointData + 0); - pMetadata->data.cue.pCuePoints[iCuePoint].playOrderPosition = ma_dr_wav_bytes_to_u32(cuePointData + 4); + pMetadata->data.cue.pCuePoints[iCuePoint].id = drwav_bytes_to_u32(cuePointData + 0); + pMetadata->data.cue.pCuePoints[iCuePoint].playOrderPosition = drwav_bytes_to_u32(cuePointData + 4); pMetadata->data.cue.pCuePoints[iCuePoint].dataChunkId[0] = cuePointData[8]; pMetadata->data.cue.pCuePoints[iCuePoint].dataChunkId[1] = cuePointData[9]; pMetadata->data.cue.pCuePoints[iCuePoint].dataChunkId[2] = cuePointData[10]; pMetadata->data.cue.pCuePoints[iCuePoint].dataChunkId[3] = cuePointData[11]; - pMetadata->data.cue.pCuePoints[iCuePoint].chunkStart = ma_dr_wav_bytes_to_u32(cuePointData + 12); - pMetadata->data.cue.pCuePoints[iCuePoint].blockStart = ma_dr_wav_bytes_to_u32(cuePointData + 16); - pMetadata->data.cue.pCuePoints[iCuePoint].sampleByteOffset = ma_dr_wav_bytes_to_u32(cuePointData + 20); + pMetadata->data.cue.pCuePoints[iCuePoint].chunkStart = drwav_bytes_to_u32(cuePointData + 12); + pMetadata->data.cue.pCuePoints[iCuePoint].blockStart = drwav_bytes_to_u32(cuePointData + 16); + pMetadata->data.cue.pCuePoints[iCuePoint].sampleByteOffset = drwav_bytes_to_u32(cuePointData + 20); } else { break; } @@ -78766,50 +77355,50 @@ MA_PRIVATE ma_uint64 ma_dr_wav__read_cue_to_metadata_obj(ma_dr_wav__metadata_par } return totalBytesRead; } -MA_PRIVATE ma_uint64 ma_dr_wav__read_inst_to_metadata_obj(ma_dr_wav__metadata_parser* pParser, ma_dr_wav_metadata* pMetadata) +DRWAV_PRIVATE drwav_uint64 drwav__read_inst_to_metadata_obj(drwav__metadata_parser* pParser, drwav_metadata* pMetadata) { - ma_uint8 instData[MA_DR_WAV_INST_BYTES]; - ma_uint64 bytesRead; + drwav_uint8 instData[DRWAV_INST_BYTES]; + drwav_uint64 bytesRead; if (pMetadata == NULL) { return 0; } - bytesRead = ma_dr_wav__metadata_parser_read(pParser, instData, sizeof(instData), NULL); - MA_DR_WAV_ASSERT(pParser->stage == ma_dr_wav__metadata_parser_stage_read); + bytesRead = drwav__metadata_parser_read(pParser, instData, sizeof(instData), NULL); + DRWAV_ASSERT(pParser->stage == drwav__metadata_parser_stage_read); if (bytesRead == sizeof(instData)) { - pMetadata->type = ma_dr_wav_metadata_type_inst; - pMetadata->data.inst.midiUnityNote = (ma_int8)instData[0]; - pMetadata->data.inst.fineTuneCents = (ma_int8)instData[1]; - pMetadata->data.inst.gainDecibels = (ma_int8)instData[2]; - pMetadata->data.inst.lowNote = (ma_int8)instData[3]; - pMetadata->data.inst.highNote = (ma_int8)instData[4]; - pMetadata->data.inst.lowVelocity = (ma_int8)instData[5]; - pMetadata->data.inst.highVelocity = (ma_int8)instData[6]; + pMetadata->type = drwav_metadata_type_inst; + pMetadata->data.inst.midiUnityNote = (drwav_int8)instData[0]; + pMetadata->data.inst.fineTuneCents = (drwav_int8)instData[1]; + pMetadata->data.inst.gainDecibels = (drwav_int8)instData[2]; + pMetadata->data.inst.lowNote = (drwav_int8)instData[3]; + pMetadata->data.inst.highNote = (drwav_int8)instData[4]; + pMetadata->data.inst.lowVelocity = (drwav_int8)instData[5]; + pMetadata->data.inst.highVelocity = (drwav_int8)instData[6]; } return bytesRead; } -MA_PRIVATE ma_uint64 ma_dr_wav__read_acid_to_metadata_obj(ma_dr_wav__metadata_parser* pParser, ma_dr_wav_metadata* pMetadata) +DRWAV_PRIVATE drwav_uint64 drwav__read_acid_to_metadata_obj(drwav__metadata_parser* pParser, drwav_metadata* pMetadata) { - ma_uint8 acidData[MA_DR_WAV_ACID_BYTES]; - ma_uint64 bytesRead; + drwav_uint8 acidData[DRWAV_ACID_BYTES]; + drwav_uint64 bytesRead; if (pMetadata == NULL) { return 0; } - bytesRead = ma_dr_wav__metadata_parser_read(pParser, acidData, sizeof(acidData), NULL); - MA_DR_WAV_ASSERT(pParser->stage == ma_dr_wav__metadata_parser_stage_read); + bytesRead = drwav__metadata_parser_read(pParser, acidData, sizeof(acidData), NULL); + DRWAV_ASSERT(pParser->stage == drwav__metadata_parser_stage_read); if (bytesRead == sizeof(acidData)) { - pMetadata->type = ma_dr_wav_metadata_type_acid; - pMetadata->data.acid.flags = ma_dr_wav_bytes_to_u32(acidData + 0); - pMetadata->data.acid.midiUnityNote = ma_dr_wav_bytes_to_u16(acidData + 4); - pMetadata->data.acid.reserved1 = ma_dr_wav_bytes_to_u16(acidData + 6); - pMetadata->data.acid.reserved2 = ma_dr_wav_bytes_to_f32(acidData + 8); - pMetadata->data.acid.numBeats = ma_dr_wav_bytes_to_u32(acidData + 12); - pMetadata->data.acid.meterDenominator = ma_dr_wav_bytes_to_u16(acidData + 16); - pMetadata->data.acid.meterNumerator = ma_dr_wav_bytes_to_u16(acidData + 18); - pMetadata->data.acid.tempo = ma_dr_wav_bytes_to_f32(acidData + 20); + pMetadata->type = drwav_metadata_type_acid; + pMetadata->data.acid.flags = drwav_bytes_to_u32(acidData + 0); + pMetadata->data.acid.midiUnityNote = drwav_bytes_to_u16(acidData + 4); + pMetadata->data.acid.reserved1 = drwav_bytes_to_u16(acidData + 6); + pMetadata->data.acid.reserved2 = drwav_bytes_to_f32(acidData + 8); + pMetadata->data.acid.numBeats = drwav_bytes_to_u32(acidData + 12); + pMetadata->data.acid.meterDenominator = drwav_bytes_to_u16(acidData + 16); + pMetadata->data.acid.meterNumerator = drwav_bytes_to_u16(acidData + 18); + pMetadata->data.acid.tempo = drwav_bytes_to_f32(acidData + 20); } return bytesRead; } -MA_PRIVATE size_t ma_dr_wav__strlen(const char* str) +DRWAV_PRIVATE size_t drwav__strlen(const char* str) { size_t result = 0; while (*str++) { @@ -78817,7 +77406,7 @@ MA_PRIVATE size_t ma_dr_wav__strlen(const char* str) } return result; } -MA_PRIVATE size_t ma_dr_wav__strlen_clamped(const char* str, size_t maxToRead) +DRWAV_PRIVATE size_t drwav__strlen_clamped(const char* str, size_t maxToRead) { size_t result = 0; while (*str++ && result < maxToRead) { @@ -78825,13 +77414,13 @@ MA_PRIVATE size_t ma_dr_wav__strlen_clamped(const char* str, size_t maxToRead) } return result; } -MA_PRIVATE char* ma_dr_wav__metadata_copy_string(ma_dr_wav__metadata_parser* pParser, const char* str, size_t maxToRead) +DRWAV_PRIVATE char* drwav__metadata_copy_string(drwav__metadata_parser* pParser, const char* str, size_t maxToRead) { - size_t len = ma_dr_wav__strlen_clamped(str, maxToRead); + size_t len = drwav__strlen_clamped(str, maxToRead); if (len) { - char* result = (char*)ma_dr_wav__metadata_get_memory(pParser, len + 1, 1); - MA_DR_WAV_ASSERT(result != NULL); - MA_DR_WAV_COPY_MEMORY(result, str, len); + char* result = (char*)drwav__metadata_get_memory(pParser, len + 1, 1); + DRWAV_ASSERT(result != NULL); + DRWAV_COPY_MEMORY(result, str, len); result[len] = '\0'; return result; } else { @@ -78843,36 +77432,36 @@ typedef struct const void* pBuffer; size_t sizeInBytes; size_t cursor; -} ma_dr_wav_buffer_reader; -MA_PRIVATE ma_result ma_dr_wav_buffer_reader_init(const void* pBuffer, size_t sizeInBytes, ma_dr_wav_buffer_reader* pReader) +} drwav_buffer_reader; +DRWAV_PRIVATE drwav_result drwav_buffer_reader_init(const void* pBuffer, size_t sizeInBytes, drwav_buffer_reader* pReader) { - MA_DR_WAV_ASSERT(pBuffer != NULL); - MA_DR_WAV_ASSERT(pReader != NULL); - MA_DR_WAV_ZERO_OBJECT(pReader); + DRWAV_ASSERT(pBuffer != NULL); + DRWAV_ASSERT(pReader != NULL); + DRWAV_ZERO_OBJECT(pReader); pReader->pBuffer = pBuffer; pReader->sizeInBytes = sizeInBytes; pReader->cursor = 0; - return MA_SUCCESS; + return DRWAV_SUCCESS; } -MA_PRIVATE const void* ma_dr_wav_buffer_reader_ptr(const ma_dr_wav_buffer_reader* pReader) +DRWAV_PRIVATE const void* drwav_buffer_reader_ptr(const drwav_buffer_reader* pReader) { - MA_DR_WAV_ASSERT(pReader != NULL); - return ma_dr_wav_offset_ptr(pReader->pBuffer, pReader->cursor); + DRWAV_ASSERT(pReader != NULL); + return drwav_offset_ptr(pReader->pBuffer, pReader->cursor); } -MA_PRIVATE ma_result ma_dr_wav_buffer_reader_seek(ma_dr_wav_buffer_reader* pReader, size_t bytesToSeek) +DRWAV_PRIVATE drwav_result drwav_buffer_reader_seek(drwav_buffer_reader* pReader, size_t bytesToSeek) { - MA_DR_WAV_ASSERT(pReader != NULL); + DRWAV_ASSERT(pReader != NULL); if (pReader->cursor + bytesToSeek > pReader->sizeInBytes) { - return MA_BAD_SEEK; + return DRWAV_BAD_SEEK; } pReader->cursor += bytesToSeek; - return MA_SUCCESS; + return DRWAV_SUCCESS; } -MA_PRIVATE ma_result ma_dr_wav_buffer_reader_read(ma_dr_wav_buffer_reader* pReader, void* pDst, size_t bytesToRead, size_t* pBytesRead) +DRWAV_PRIVATE drwav_result drwav_buffer_reader_read(drwav_buffer_reader* pReader, void* pDst, size_t bytesToRead, size_t* pBytesRead) { - ma_result result = MA_SUCCESS; + drwav_result result = DRWAV_SUCCESS; size_t bytesRemaining; - MA_DR_WAV_ASSERT(pReader != NULL); + DRWAV_ASSERT(pReader != NULL); if (pBytesRead != NULL) { *pBytesRead = 0; } @@ -78881,87 +77470,87 @@ MA_PRIVATE ma_result ma_dr_wav_buffer_reader_read(ma_dr_wav_buffer_reader* pRead bytesToRead = bytesRemaining; } if (pDst == NULL) { - result = ma_dr_wav_buffer_reader_seek(pReader, bytesToRead); + result = drwav_buffer_reader_seek(pReader, bytesToRead); } else { - MA_DR_WAV_COPY_MEMORY(pDst, ma_dr_wav_buffer_reader_ptr(pReader), bytesToRead); + DRWAV_COPY_MEMORY(pDst, drwav_buffer_reader_ptr(pReader), bytesToRead); pReader->cursor += bytesToRead; } - MA_DR_WAV_ASSERT(pReader->cursor <= pReader->sizeInBytes); - if (result == MA_SUCCESS) { + DRWAV_ASSERT(pReader->cursor <= pReader->sizeInBytes); + if (result == DRWAV_SUCCESS) { if (pBytesRead != NULL) { *pBytesRead = bytesToRead; } } - return MA_SUCCESS; + return DRWAV_SUCCESS; } -MA_PRIVATE ma_result ma_dr_wav_buffer_reader_read_u16(ma_dr_wav_buffer_reader* pReader, ma_uint16* pDst) +DRWAV_PRIVATE drwav_result drwav_buffer_reader_read_u16(drwav_buffer_reader* pReader, drwav_uint16* pDst) { - ma_result result; + drwav_result result; size_t bytesRead; - ma_uint8 data[2]; - MA_DR_WAV_ASSERT(pReader != NULL); - MA_DR_WAV_ASSERT(pDst != NULL); + drwav_uint8 data[2]; + DRWAV_ASSERT(pReader != NULL); + DRWAV_ASSERT(pDst != NULL); *pDst = 0; - result = ma_dr_wav_buffer_reader_read(pReader, data, sizeof(*pDst), &bytesRead); - if (result != MA_SUCCESS || bytesRead != sizeof(*pDst)) { + result = drwav_buffer_reader_read(pReader, data, sizeof(*pDst), &bytesRead); + if (result != DRWAV_SUCCESS || bytesRead != sizeof(*pDst)) { return result; } - *pDst = ma_dr_wav_bytes_to_u16(data); - return MA_SUCCESS; + *pDst = drwav_bytes_to_u16(data); + return DRWAV_SUCCESS; } -MA_PRIVATE ma_result ma_dr_wav_buffer_reader_read_u32(ma_dr_wav_buffer_reader* pReader, ma_uint32* pDst) +DRWAV_PRIVATE drwav_result drwav_buffer_reader_read_u32(drwav_buffer_reader* pReader, drwav_uint32* pDst) { - ma_result result; + drwav_result result; size_t bytesRead; - ma_uint8 data[4]; - MA_DR_WAV_ASSERT(pReader != NULL); - MA_DR_WAV_ASSERT(pDst != NULL); + drwav_uint8 data[4]; + DRWAV_ASSERT(pReader != NULL); + DRWAV_ASSERT(pDst != NULL); *pDst = 0; - result = ma_dr_wav_buffer_reader_read(pReader, data, sizeof(*pDst), &bytesRead); - if (result != MA_SUCCESS || bytesRead != sizeof(*pDst)) { + result = drwav_buffer_reader_read(pReader, data, sizeof(*pDst), &bytesRead); + if (result != DRWAV_SUCCESS || bytesRead != sizeof(*pDst)) { return result; } - *pDst = ma_dr_wav_bytes_to_u32(data); - return MA_SUCCESS; + *pDst = drwav_bytes_to_u32(data); + return DRWAV_SUCCESS; } -MA_PRIVATE ma_uint64 ma_dr_wav__read_bext_to_metadata_obj(ma_dr_wav__metadata_parser* pParser, ma_dr_wav_metadata* pMetadata, ma_uint64 chunkSize) +DRWAV_PRIVATE drwav_uint64 drwav__read_bext_to_metadata_obj(drwav__metadata_parser* pParser, drwav_metadata* pMetadata, drwav_uint64 chunkSize) { - ma_uint8 bextData[MA_DR_WAV_BEXT_BYTES]; - size_t bytesRead = ma_dr_wav__metadata_parser_read(pParser, bextData, sizeof(bextData), NULL); - MA_DR_WAV_ASSERT(pParser->stage == ma_dr_wav__metadata_parser_stage_read); + drwav_uint8 bextData[DRWAV_BEXT_BYTES]; + size_t bytesRead = drwav__metadata_parser_read(pParser, bextData, sizeof(bextData), NULL); + DRWAV_ASSERT(pParser->stage == drwav__metadata_parser_stage_read); if (bytesRead == sizeof(bextData)) { - ma_dr_wav_buffer_reader reader; - ma_uint32 timeReferenceLow; - ma_uint32 timeReferenceHigh; + drwav_buffer_reader reader; + drwav_uint32 timeReferenceLow; + drwav_uint32 timeReferenceHigh; size_t extraBytes; - pMetadata->type = ma_dr_wav_metadata_type_bext; - if (ma_dr_wav_buffer_reader_init(bextData, bytesRead, &reader) == MA_SUCCESS) { - pMetadata->data.bext.pDescription = ma_dr_wav__metadata_copy_string(pParser, (const char*)ma_dr_wav_buffer_reader_ptr(&reader), MA_DR_WAV_BEXT_DESCRIPTION_BYTES); - ma_dr_wav_buffer_reader_seek(&reader, MA_DR_WAV_BEXT_DESCRIPTION_BYTES); - pMetadata->data.bext.pOriginatorName = ma_dr_wav__metadata_copy_string(pParser, (const char*)ma_dr_wav_buffer_reader_ptr(&reader), MA_DR_WAV_BEXT_ORIGINATOR_NAME_BYTES); - ma_dr_wav_buffer_reader_seek(&reader, MA_DR_WAV_BEXT_ORIGINATOR_NAME_BYTES); - pMetadata->data.bext.pOriginatorReference = ma_dr_wav__metadata_copy_string(pParser, (const char*)ma_dr_wav_buffer_reader_ptr(&reader), MA_DR_WAV_BEXT_ORIGINATOR_REF_BYTES); - ma_dr_wav_buffer_reader_seek(&reader, MA_DR_WAV_BEXT_ORIGINATOR_REF_BYTES); - ma_dr_wav_buffer_reader_read(&reader, pMetadata->data.bext.pOriginationDate, sizeof(pMetadata->data.bext.pOriginationDate), NULL); - ma_dr_wav_buffer_reader_read(&reader, pMetadata->data.bext.pOriginationTime, sizeof(pMetadata->data.bext.pOriginationTime), NULL); - ma_dr_wav_buffer_reader_read_u32(&reader, &timeReferenceLow); - ma_dr_wav_buffer_reader_read_u32(&reader, &timeReferenceHigh); - pMetadata->data.bext.timeReference = ((ma_uint64)timeReferenceHigh << 32) + timeReferenceLow; - ma_dr_wav_buffer_reader_read_u16(&reader, &pMetadata->data.bext.version); - pMetadata->data.bext.pUMID = ma_dr_wav__metadata_get_memory(pParser, MA_DR_WAV_BEXT_UMID_BYTES, 1); - ma_dr_wav_buffer_reader_read(&reader, pMetadata->data.bext.pUMID, MA_DR_WAV_BEXT_UMID_BYTES, NULL); - ma_dr_wav_buffer_reader_read_u16(&reader, &pMetadata->data.bext.loudnessValue); - ma_dr_wav_buffer_reader_read_u16(&reader, &pMetadata->data.bext.loudnessRange); - ma_dr_wav_buffer_reader_read_u16(&reader, &pMetadata->data.bext.maxTruePeakLevel); - ma_dr_wav_buffer_reader_read_u16(&reader, &pMetadata->data.bext.maxMomentaryLoudness); - ma_dr_wav_buffer_reader_read_u16(&reader, &pMetadata->data.bext.maxShortTermLoudness); - MA_DR_WAV_ASSERT((ma_dr_wav_offset_ptr(ma_dr_wav_buffer_reader_ptr(&reader), MA_DR_WAV_BEXT_RESERVED_BYTES)) == (bextData + MA_DR_WAV_BEXT_BYTES)); - extraBytes = (size_t)(chunkSize - MA_DR_WAV_BEXT_BYTES); + pMetadata->type = drwav_metadata_type_bext; + if (drwav_buffer_reader_init(bextData, bytesRead, &reader) == DRWAV_SUCCESS) { + pMetadata->data.bext.pDescription = drwav__metadata_copy_string(pParser, (const char*)drwav_buffer_reader_ptr(&reader), DRWAV_BEXT_DESCRIPTION_BYTES); + drwav_buffer_reader_seek(&reader, DRWAV_BEXT_DESCRIPTION_BYTES); + pMetadata->data.bext.pOriginatorName = drwav__metadata_copy_string(pParser, (const char*)drwav_buffer_reader_ptr(&reader), DRWAV_BEXT_ORIGINATOR_NAME_BYTES); + drwav_buffer_reader_seek(&reader, DRWAV_BEXT_ORIGINATOR_NAME_BYTES); + pMetadata->data.bext.pOriginatorReference = drwav__metadata_copy_string(pParser, (const char*)drwav_buffer_reader_ptr(&reader), DRWAV_BEXT_ORIGINATOR_REF_BYTES); + drwav_buffer_reader_seek(&reader, DRWAV_BEXT_ORIGINATOR_REF_BYTES); + drwav_buffer_reader_read(&reader, pMetadata->data.bext.pOriginationDate, sizeof(pMetadata->data.bext.pOriginationDate), NULL); + drwav_buffer_reader_read(&reader, pMetadata->data.bext.pOriginationTime, sizeof(pMetadata->data.bext.pOriginationTime), NULL); + drwav_buffer_reader_read_u32(&reader, &timeReferenceLow); + drwav_buffer_reader_read_u32(&reader, &timeReferenceHigh); + pMetadata->data.bext.timeReference = ((drwav_uint64)timeReferenceHigh << 32) + timeReferenceLow; + drwav_buffer_reader_read_u16(&reader, &pMetadata->data.bext.version); + pMetadata->data.bext.pUMID = drwav__metadata_get_memory(pParser, DRWAV_BEXT_UMID_BYTES, 1); + drwav_buffer_reader_read(&reader, pMetadata->data.bext.pUMID, DRWAV_BEXT_UMID_BYTES, NULL); + drwav_buffer_reader_read_u16(&reader, &pMetadata->data.bext.loudnessValue); + drwav_buffer_reader_read_u16(&reader, &pMetadata->data.bext.loudnessRange); + drwav_buffer_reader_read_u16(&reader, &pMetadata->data.bext.maxTruePeakLevel); + drwav_buffer_reader_read_u16(&reader, &pMetadata->data.bext.maxMomentaryLoudness); + drwav_buffer_reader_read_u16(&reader, &pMetadata->data.bext.maxShortTermLoudness); + DRWAV_ASSERT((drwav_offset_ptr(drwav_buffer_reader_ptr(&reader), DRWAV_BEXT_RESERVED_BYTES)) == (bextData + DRWAV_BEXT_BYTES)); + extraBytes = (size_t)(chunkSize - DRWAV_BEXT_BYTES); if (extraBytes > 0) { - pMetadata->data.bext.pCodingHistory = (char*)ma_dr_wav__metadata_get_memory(pParser, extraBytes + 1, 1); - MA_DR_WAV_ASSERT(pMetadata->data.bext.pCodingHistory != NULL); - bytesRead += ma_dr_wav__metadata_parser_read(pParser, pMetadata->data.bext.pCodingHistory, extraBytes, NULL); - pMetadata->data.bext.codingHistorySize = (ma_uint32)ma_dr_wav__strlen(pMetadata->data.bext.pCodingHistory); + pMetadata->data.bext.pCodingHistory = (char*)drwav__metadata_get_memory(pParser, extraBytes + 1, 1); + DRWAV_ASSERT(pMetadata->data.bext.pCodingHistory != NULL); + bytesRead += drwav__metadata_parser_read(pParser, pMetadata->data.bext.pCodingHistory, extraBytes, NULL); + pMetadata->data.bext.codingHistorySize = (drwav_uint32)drwav__strlen(pMetadata->data.bext.pCodingHistory); } else { pMetadata->data.bext.pCodingHistory = NULL; pMetadata->data.bext.codingHistorySize = 0; @@ -78970,22 +77559,22 @@ MA_PRIVATE ma_uint64 ma_dr_wav__read_bext_to_metadata_obj(ma_dr_wav__metadata_pa } return bytesRead; } -MA_PRIVATE ma_uint64 ma_dr_wav__read_list_label_or_note_to_metadata_obj(ma_dr_wav__metadata_parser* pParser, ma_dr_wav_metadata* pMetadata, ma_uint64 chunkSize, ma_dr_wav_metadata_type type) +DRWAV_PRIVATE drwav_uint64 drwav__read_list_label_or_note_to_metadata_obj(drwav__metadata_parser* pParser, drwav_metadata* pMetadata, drwav_uint64 chunkSize, drwav_metadata_type type) { - ma_uint8 cueIDBuffer[MA_DR_WAV_LIST_LABEL_OR_NOTE_BYTES]; - ma_uint64 totalBytesRead = 0; - size_t bytesJustRead = ma_dr_wav__metadata_parser_read(pParser, cueIDBuffer, sizeof(cueIDBuffer), &totalBytesRead); - MA_DR_WAV_ASSERT(pParser->stage == ma_dr_wav__metadata_parser_stage_read); + drwav_uint8 cueIDBuffer[DRWAV_LIST_LABEL_OR_NOTE_BYTES]; + drwav_uint64 totalBytesRead = 0; + size_t bytesJustRead = drwav__metadata_parser_read(pParser, cueIDBuffer, sizeof(cueIDBuffer), &totalBytesRead); + DRWAV_ASSERT(pParser->stage == drwav__metadata_parser_stage_read); if (bytesJustRead == sizeof(cueIDBuffer)) { - ma_uint32 sizeIncludingNullTerminator; + drwav_uint32 sizeIncludingNullTerminator; pMetadata->type = type; - pMetadata->data.labelOrNote.cuePointId = ma_dr_wav_bytes_to_u32(cueIDBuffer); - sizeIncludingNullTerminator = (ma_uint32)chunkSize - MA_DR_WAV_LIST_LABEL_OR_NOTE_BYTES; + pMetadata->data.labelOrNote.cuePointId = drwav_bytes_to_u32(cueIDBuffer); + sizeIncludingNullTerminator = (drwav_uint32)chunkSize - DRWAV_LIST_LABEL_OR_NOTE_BYTES; if (sizeIncludingNullTerminator > 0) { pMetadata->data.labelOrNote.stringLength = sizeIncludingNullTerminator - 1; - pMetadata->data.labelOrNote.pString = (char*)ma_dr_wav__metadata_get_memory(pParser, sizeIncludingNullTerminator, 1); - MA_DR_WAV_ASSERT(pMetadata->data.labelOrNote.pString != NULL); - ma_dr_wav__metadata_parser_read(pParser, pMetadata->data.labelOrNote.pString, sizeIncludingNullTerminator, &totalBytesRead); + pMetadata->data.labelOrNote.pString = (char*)drwav__metadata_get_memory(pParser, sizeIncludingNullTerminator, 1); + DRWAV_ASSERT(pMetadata->data.labelOrNote.pString != NULL); + drwav__metadata_parser_read(pParser, pMetadata->data.labelOrNote.pString, sizeIncludingNullTerminator, &totalBytesRead); } else { pMetadata->data.labelOrNote.stringLength = 0; pMetadata->data.labelOrNote.pString = NULL; @@ -78993,31 +77582,31 @@ MA_PRIVATE ma_uint64 ma_dr_wav__read_list_label_or_note_to_metadata_obj(ma_dr_wa } return totalBytesRead; } -MA_PRIVATE ma_uint64 ma_dr_wav__read_list_labelled_cue_region_to_metadata_obj(ma_dr_wav__metadata_parser* pParser, ma_dr_wav_metadata* pMetadata, ma_uint64 chunkSize) +DRWAV_PRIVATE drwav_uint64 drwav__read_list_labelled_cue_region_to_metadata_obj(drwav__metadata_parser* pParser, drwav_metadata* pMetadata, drwav_uint64 chunkSize) { - ma_uint8 buffer[MA_DR_WAV_LIST_LABELLED_TEXT_BYTES]; - ma_uint64 totalBytesRead = 0; - size_t bytesJustRead = ma_dr_wav__metadata_parser_read(pParser, buffer, sizeof(buffer), &totalBytesRead); - MA_DR_WAV_ASSERT(pParser->stage == ma_dr_wav__metadata_parser_stage_read); + drwav_uint8 buffer[DRWAV_LIST_LABELLED_TEXT_BYTES]; + drwav_uint64 totalBytesRead = 0; + size_t bytesJustRead = drwav__metadata_parser_read(pParser, buffer, sizeof(buffer), &totalBytesRead); + DRWAV_ASSERT(pParser->stage == drwav__metadata_parser_stage_read); if (bytesJustRead == sizeof(buffer)) { - ma_uint32 sizeIncludingNullTerminator; - pMetadata->type = ma_dr_wav_metadata_type_list_labelled_cue_region; - pMetadata->data.labelledCueRegion.cuePointId = ma_dr_wav_bytes_to_u32(buffer + 0); - pMetadata->data.labelledCueRegion.sampleLength = ma_dr_wav_bytes_to_u32(buffer + 4); + drwav_uint32 sizeIncludingNullTerminator; + pMetadata->type = drwav_metadata_type_list_labelled_cue_region; + pMetadata->data.labelledCueRegion.cuePointId = drwav_bytes_to_u32(buffer + 0); + pMetadata->data.labelledCueRegion.sampleLength = drwav_bytes_to_u32(buffer + 4); pMetadata->data.labelledCueRegion.purposeId[0] = buffer[8]; pMetadata->data.labelledCueRegion.purposeId[1] = buffer[9]; pMetadata->data.labelledCueRegion.purposeId[2] = buffer[10]; pMetadata->data.labelledCueRegion.purposeId[3] = buffer[11]; - pMetadata->data.labelledCueRegion.country = ma_dr_wav_bytes_to_u16(buffer + 12); - pMetadata->data.labelledCueRegion.language = ma_dr_wav_bytes_to_u16(buffer + 14); - pMetadata->data.labelledCueRegion.dialect = ma_dr_wav_bytes_to_u16(buffer + 16); - pMetadata->data.labelledCueRegion.codePage = ma_dr_wav_bytes_to_u16(buffer + 18); - sizeIncludingNullTerminator = (ma_uint32)chunkSize - MA_DR_WAV_LIST_LABELLED_TEXT_BYTES; + pMetadata->data.labelledCueRegion.country = drwav_bytes_to_u16(buffer + 12); + pMetadata->data.labelledCueRegion.language = drwav_bytes_to_u16(buffer + 14); + pMetadata->data.labelledCueRegion.dialect = drwav_bytes_to_u16(buffer + 16); + pMetadata->data.labelledCueRegion.codePage = drwav_bytes_to_u16(buffer + 18); + sizeIncludingNullTerminator = (drwav_uint32)chunkSize - DRWAV_LIST_LABELLED_TEXT_BYTES; if (sizeIncludingNullTerminator > 0) { pMetadata->data.labelledCueRegion.stringLength = sizeIncludingNullTerminator - 1; - pMetadata->data.labelledCueRegion.pString = (char*)ma_dr_wav__metadata_get_memory(pParser, sizeIncludingNullTerminator, 1); - MA_DR_WAV_ASSERT(pMetadata->data.labelledCueRegion.pString != NULL); - ma_dr_wav__metadata_parser_read(pParser, pMetadata->data.labelledCueRegion.pString, sizeIncludingNullTerminator, &totalBytesRead); + pMetadata->data.labelledCueRegion.pString = (char*)drwav__metadata_get_memory(pParser, sizeIncludingNullTerminator, 1); + DRWAV_ASSERT(pMetadata->data.labelledCueRegion.pString != NULL); + drwav__metadata_parser_read(pParser, pMetadata->data.labelledCueRegion.pString, sizeIncludingNullTerminator, &totalBytesRead); } else { pMetadata->data.labelledCueRegion.stringLength = 0; pMetadata->data.labelledCueRegion.pString = NULL; @@ -79025,21 +77614,21 @@ MA_PRIVATE ma_uint64 ma_dr_wav__read_list_labelled_cue_region_to_metadata_obj(ma } return totalBytesRead; } -MA_PRIVATE ma_uint64 ma_dr_wav__metadata_process_info_text_chunk(ma_dr_wav__metadata_parser* pParser, ma_uint64 chunkSize, ma_dr_wav_metadata_type type) +DRWAV_PRIVATE drwav_uint64 drwav__metadata_process_info_text_chunk(drwav__metadata_parser* pParser, drwav_uint64 chunkSize, drwav_metadata_type type) { - ma_uint64 bytesRead = 0; - ma_uint32 stringSizeWithNullTerminator = (ma_uint32)chunkSize; - if (pParser->stage == ma_dr_wav__metadata_parser_stage_count) { + drwav_uint64 bytesRead = 0; + drwav_uint32 stringSizeWithNullTerminator = (drwav_uint32)chunkSize; + if (pParser->stage == drwav__metadata_parser_stage_count) { pParser->metadataCount += 1; - ma_dr_wav__metadata_request_extra_memory_for_stage_2(pParser, stringSizeWithNullTerminator, 1); + drwav__metadata_request_extra_memory_for_stage_2(pParser, stringSizeWithNullTerminator, 1); } else { - ma_dr_wav_metadata* pMetadata = &pParser->pMetadata[pParser->metadataCursor]; + drwav_metadata* pMetadata = &pParser->pMetadata[pParser->metadataCursor]; pMetadata->type = type; if (stringSizeWithNullTerminator > 0) { pMetadata->data.infoText.stringLength = stringSizeWithNullTerminator - 1; - pMetadata->data.infoText.pString = (char*)ma_dr_wav__metadata_get_memory(pParser, stringSizeWithNullTerminator, 1); - MA_DR_WAV_ASSERT(pMetadata->data.infoText.pString != NULL); - bytesRead = ma_dr_wav__metadata_parser_read(pParser, pMetadata->data.infoText.pString, (size_t)stringSizeWithNullTerminator, NULL); + pMetadata->data.infoText.pString = (char*)drwav__metadata_get_memory(pParser, stringSizeWithNullTerminator, 1); + DRWAV_ASSERT(pMetadata->data.infoText.pString != NULL); + bytesRead = drwav__metadata_parser_read(pParser, pMetadata->data.infoText.pString, (size_t)stringSizeWithNullTerminator, NULL); if (bytesRead == chunkSize) { pParser->metadataCursor += 1; } else { @@ -79052,30 +77641,30 @@ MA_PRIVATE ma_uint64 ma_dr_wav__metadata_process_info_text_chunk(ma_dr_wav__meta } return bytesRead; } -MA_PRIVATE ma_uint64 ma_dr_wav__metadata_process_unknown_chunk(ma_dr_wav__metadata_parser* pParser, const ma_uint8* pChunkId, ma_uint64 chunkSize, ma_dr_wav_metadata_location location) +DRWAV_PRIVATE drwav_uint64 drwav__metadata_process_unknown_chunk(drwav__metadata_parser* pParser, const drwav_uint8* pChunkId, drwav_uint64 chunkSize, drwav_metadata_location location) { - ma_uint64 bytesRead = 0; - if (location == ma_dr_wav_metadata_location_invalid) { + drwav_uint64 bytesRead = 0; + if (location == drwav_metadata_location_invalid) { return 0; } - if (ma_dr_wav_fourcc_equal(pChunkId, "data") || ma_dr_wav_fourcc_equal(pChunkId, "fmt ") || ma_dr_wav_fourcc_equal(pChunkId, "fact")) { + if (drwav_fourcc_equal(pChunkId, "data") || drwav_fourcc_equal(pChunkId, "fmt") || drwav_fourcc_equal(pChunkId, "fact")) { return 0; } - if (pParser->stage == ma_dr_wav__metadata_parser_stage_count) { + if (pParser->stage == drwav__metadata_parser_stage_count) { pParser->metadataCount += 1; - ma_dr_wav__metadata_request_extra_memory_for_stage_2(pParser, (size_t)chunkSize, 1); + drwav__metadata_request_extra_memory_for_stage_2(pParser, (size_t)chunkSize, 1); } else { - ma_dr_wav_metadata* pMetadata = &pParser->pMetadata[pParser->metadataCursor]; - pMetadata->type = ma_dr_wav_metadata_type_unknown; + drwav_metadata* pMetadata = &pParser->pMetadata[pParser->metadataCursor]; + pMetadata->type = drwav_metadata_type_unknown; pMetadata->data.unknown.chunkLocation = location; pMetadata->data.unknown.id[0] = pChunkId[0]; pMetadata->data.unknown.id[1] = pChunkId[1]; pMetadata->data.unknown.id[2] = pChunkId[2]; pMetadata->data.unknown.id[3] = pChunkId[3]; - pMetadata->data.unknown.dataSizeInBytes = (ma_uint32)chunkSize; - pMetadata->data.unknown.pData = (ma_uint8 *)ma_dr_wav__metadata_get_memory(pParser, (size_t)chunkSize, 1); - MA_DR_WAV_ASSERT(pMetadata->data.unknown.pData != NULL); - bytesRead = ma_dr_wav__metadata_parser_read(pParser, pMetadata->data.unknown.pData, pMetadata->data.unknown.dataSizeInBytes, NULL); + pMetadata->data.unknown.dataSizeInBytes = (drwav_uint32)chunkSize; + pMetadata->data.unknown.pData = (drwav_uint8 *)drwav__metadata_get_memory(pParser, (size_t)chunkSize, 1); + DRWAV_ASSERT(pMetadata->data.unknown.pData != NULL); + bytesRead = drwav__metadata_parser_read(pParser, pMetadata->data.unknown.pData, pMetadata->data.unknown.dataSizeInBytes, NULL); if (bytesRead == pMetadata->data.unknown.dataSizeInBytes) { pParser->metadataCursor += 1; } else { @@ -79083,41 +77672,41 @@ MA_PRIVATE ma_uint64 ma_dr_wav__metadata_process_unknown_chunk(ma_dr_wav__metada } return bytesRead; } -MA_PRIVATE ma_bool32 ma_dr_wav__chunk_matches(ma_dr_wav_metadata_type allowedMetadataTypes, const ma_uint8* pChunkID, ma_dr_wav_metadata_type type, const char* pID) +DRWAV_PRIVATE drwav_bool32 drwav__chunk_matches(drwav_metadata_type allowedMetadataTypes, const drwav_uint8* pChunkID, drwav_metadata_type type, const char* pID) { - return (allowedMetadataTypes & type) && ma_dr_wav_fourcc_equal(pChunkID, pID); + return (allowedMetadataTypes & type) && drwav_fourcc_equal(pChunkID, pID); } -MA_PRIVATE ma_uint64 ma_dr_wav__metadata_process_chunk(ma_dr_wav__metadata_parser* pParser, const ma_dr_wav_chunk_header* pChunkHeader, ma_dr_wav_metadata_type allowedMetadataTypes) +DRWAV_PRIVATE drwav_uint64 drwav__metadata_process_chunk(drwav__metadata_parser* pParser, const drwav_chunk_header* pChunkHeader, drwav_metadata_type allowedMetadataTypes) { - const ma_uint8 *pChunkID = pChunkHeader->id.fourcc; - ma_uint64 bytesRead = 0; - if (ma_dr_wav__chunk_matches(allowedMetadataTypes, pChunkID, ma_dr_wav_metadata_type_smpl, "smpl")) { - if (pChunkHeader->sizeInBytes >= MA_DR_WAV_SMPL_BYTES) { - if (pParser->stage == ma_dr_wav__metadata_parser_stage_count) { - ma_uint8 buffer[4]; + const drwav_uint8 *pChunkID = pChunkHeader->id.fourcc; + drwav_uint64 bytesRead = 0; + if (drwav__chunk_matches(allowedMetadataTypes, pChunkID, drwav_metadata_type_smpl, "smpl")) { + if (pChunkHeader->sizeInBytes >= DRWAV_SMPL_BYTES) { + if (pParser->stage == drwav__metadata_parser_stage_count) { + drwav_uint8 buffer[4]; size_t bytesJustRead; - if (!pParser->onSeek(pParser->pReadSeekUserData, 28, ma_dr_wav_seek_origin_current)) { + if (!pParser->onSeek(pParser->pReadSeekUserData, 28, drwav_seek_origin_current)) { return bytesRead; } bytesRead += 28; - bytesJustRead = ma_dr_wav__metadata_parser_read(pParser, buffer, sizeof(buffer), &bytesRead); + bytesJustRead = drwav__metadata_parser_read(pParser, buffer, sizeof(buffer), &bytesRead); if (bytesJustRead == sizeof(buffer)) { - ma_uint32 loopCount = ma_dr_wav_bytes_to_u32(buffer); - ma_uint64 calculatedLoopCount; - calculatedLoopCount = (pChunkHeader->sizeInBytes - MA_DR_WAV_SMPL_BYTES) / MA_DR_WAV_SMPL_LOOP_BYTES; + drwav_uint32 loopCount = drwav_bytes_to_u32(buffer); + drwav_uint64 calculatedLoopCount; + calculatedLoopCount = (pChunkHeader->sizeInBytes - DRWAV_SMPL_BYTES) / DRWAV_SMPL_LOOP_BYTES; if (calculatedLoopCount == loopCount) { - bytesJustRead = ma_dr_wav__metadata_parser_read(pParser, buffer, sizeof(buffer), &bytesRead); + bytesJustRead = drwav__metadata_parser_read(pParser, buffer, sizeof(buffer), &bytesRead); if (bytesJustRead == sizeof(buffer)) { - ma_uint32 samplerSpecificDataSizeInBytes = ma_dr_wav_bytes_to_u32(buffer); + drwav_uint32 samplerSpecificDataSizeInBytes = drwav_bytes_to_u32(buffer); pParser->metadataCount += 1; - ma_dr_wav__metadata_request_extra_memory_for_stage_2(pParser, sizeof(ma_dr_wav_smpl_loop) * loopCount, MA_DR_WAV_METADATA_ALIGNMENT); - ma_dr_wav__metadata_request_extra_memory_for_stage_2(pParser, samplerSpecificDataSizeInBytes, 1); + drwav__metadata_request_extra_memory_for_stage_2(pParser, sizeof(drwav_smpl_loop) * loopCount, DRWAV_METADATA_ALIGNMENT); + drwav__metadata_request_extra_memory_for_stage_2(pParser, samplerSpecificDataSizeInBytes, 1); } } else { } } } else { - bytesRead = ma_dr_wav__read_smpl_to_metadata_obj(pParser, pChunkHeader, &pParser->pMetadata[pParser->metadataCursor]); + bytesRead = drwav__read_smpl_to_metadata_obj(pParser, pChunkHeader, &pParser->pMetadata[pParser->metadataCursor]); if (bytesRead == pChunkHeader->sizeInBytes) { pParser->metadataCursor += 1; } else { @@ -79125,12 +77714,12 @@ MA_PRIVATE ma_uint64 ma_dr_wav__metadata_process_chunk(ma_dr_wav__metadata_parse } } else { } - } else if (ma_dr_wav__chunk_matches(allowedMetadataTypes, pChunkID, ma_dr_wav_metadata_type_inst, "inst")) { - if (pChunkHeader->sizeInBytes == MA_DR_WAV_INST_BYTES) { - if (pParser->stage == ma_dr_wav__metadata_parser_stage_count) { + } else if (drwav__chunk_matches(allowedMetadataTypes, pChunkID, drwav_metadata_type_inst, "inst")) { + if (pChunkHeader->sizeInBytes == DRWAV_INST_BYTES) { + if (pParser->stage == drwav__metadata_parser_stage_count) { pParser->metadataCount += 1; } else { - bytesRead = ma_dr_wav__read_inst_to_metadata_obj(pParser, &pParser->pMetadata[pParser->metadataCursor]); + bytesRead = drwav__read_inst_to_metadata_obj(pParser, &pParser->pMetadata[pParser->metadataCursor]); if (bytesRead == pChunkHeader->sizeInBytes) { pParser->metadataCursor += 1; } else { @@ -79138,12 +77727,12 @@ MA_PRIVATE ma_uint64 ma_dr_wav__metadata_process_chunk(ma_dr_wav__metadata_parse } } else { } - } else if (ma_dr_wav__chunk_matches(allowedMetadataTypes, pChunkID, ma_dr_wav_metadata_type_acid, "acid")) { - if (pChunkHeader->sizeInBytes == MA_DR_WAV_ACID_BYTES) { - if (pParser->stage == ma_dr_wav__metadata_parser_stage_count) { + } else if (drwav__chunk_matches(allowedMetadataTypes, pChunkID, drwav_metadata_type_acid, "acid")) { + if (pChunkHeader->sizeInBytes == DRWAV_ACID_BYTES) { + if (pParser->stage == drwav__metadata_parser_stage_count) { pParser->metadataCount += 1; } else { - bytesRead = ma_dr_wav__read_acid_to_metadata_obj(pParser, &pParser->pMetadata[pParser->metadataCursor]); + bytesRead = drwav__read_acid_to_metadata_obj(pParser, &pParser->pMetadata[pParser->metadataCursor]); if (bytesRead == pChunkHeader->sizeInBytes) { pParser->metadataCursor += 1; } else { @@ -79151,15 +77740,15 @@ MA_PRIVATE ma_uint64 ma_dr_wav__metadata_process_chunk(ma_dr_wav__metadata_parse } } else { } - } else if (ma_dr_wav__chunk_matches(allowedMetadataTypes, pChunkID, ma_dr_wav_metadata_type_cue, "cue ")) { - if (pChunkHeader->sizeInBytes >= MA_DR_WAV_CUE_BYTES) { - if (pParser->stage == ma_dr_wav__metadata_parser_stage_count) { + } else if (drwav__chunk_matches(allowedMetadataTypes, pChunkID, drwav_metadata_type_cue, "cue ")) { + if (pChunkHeader->sizeInBytes >= DRWAV_CUE_BYTES) { + if (pParser->stage == drwav__metadata_parser_stage_count) { size_t cueCount; pParser->metadataCount += 1; - cueCount = (size_t)(pChunkHeader->sizeInBytes - MA_DR_WAV_CUE_BYTES) / MA_DR_WAV_CUE_POINT_BYTES; - ma_dr_wav__metadata_request_extra_memory_for_stage_2(pParser, sizeof(ma_dr_wav_cue_point) * cueCount, MA_DR_WAV_METADATA_ALIGNMENT); + cueCount = (size_t)(pChunkHeader->sizeInBytes - DRWAV_CUE_BYTES) / DRWAV_CUE_POINT_BYTES; + drwav__metadata_request_extra_memory_for_stage_2(pParser, sizeof(drwav_cue_point) * cueCount, DRWAV_METADATA_ALIGNMENT); } else { - bytesRead = ma_dr_wav__read_cue_to_metadata_obj(pParser, pChunkHeader, &pParser->pMetadata[pParser->metadataCursor]); + bytesRead = drwav__read_cue_to_metadata_obj(pParser, pChunkHeader, &pParser->pMetadata[pParser->metadataCursor]); if (bytesRead == pChunkHeader->sizeInBytes) { pParser->metadataCursor += 1; } else { @@ -79167,35 +77756,35 @@ MA_PRIVATE ma_uint64 ma_dr_wav__metadata_process_chunk(ma_dr_wav__metadata_parse } } else { } - } else if (ma_dr_wav__chunk_matches(allowedMetadataTypes, pChunkID, ma_dr_wav_metadata_type_bext, "bext")) { - if (pChunkHeader->sizeInBytes >= MA_DR_WAV_BEXT_BYTES) { - if (pParser->stage == ma_dr_wav__metadata_parser_stage_count) { - char buffer[MA_DR_WAV_BEXT_DESCRIPTION_BYTES + 1]; - size_t allocSizeNeeded = MA_DR_WAV_BEXT_UMID_BYTES; + } else if (drwav__chunk_matches(allowedMetadataTypes, pChunkID, drwav_metadata_type_bext, "bext")) { + if (pChunkHeader->sizeInBytes >= DRWAV_BEXT_BYTES) { + if (pParser->stage == drwav__metadata_parser_stage_count) { + char buffer[DRWAV_BEXT_DESCRIPTION_BYTES + 1]; + size_t allocSizeNeeded = DRWAV_BEXT_UMID_BYTES; size_t bytesJustRead; - buffer[MA_DR_WAV_BEXT_DESCRIPTION_BYTES] = '\0'; - bytesJustRead = ma_dr_wav__metadata_parser_read(pParser, buffer, MA_DR_WAV_BEXT_DESCRIPTION_BYTES, &bytesRead); - if (bytesJustRead != MA_DR_WAV_BEXT_DESCRIPTION_BYTES) { + buffer[DRWAV_BEXT_DESCRIPTION_BYTES] = '\0'; + bytesJustRead = drwav__metadata_parser_read(pParser, buffer, DRWAV_BEXT_DESCRIPTION_BYTES, &bytesRead); + if (bytesJustRead != DRWAV_BEXT_DESCRIPTION_BYTES) { return bytesRead; } - allocSizeNeeded += ma_dr_wav__strlen(buffer) + 1; - buffer[MA_DR_WAV_BEXT_ORIGINATOR_NAME_BYTES] = '\0'; - bytesJustRead = ma_dr_wav__metadata_parser_read(pParser, buffer, MA_DR_WAV_BEXT_ORIGINATOR_NAME_BYTES, &bytesRead); - if (bytesJustRead != MA_DR_WAV_BEXT_ORIGINATOR_NAME_BYTES) { + allocSizeNeeded += drwav__strlen(buffer) + 1; + buffer[DRWAV_BEXT_ORIGINATOR_NAME_BYTES] = '\0'; + bytesJustRead = drwav__metadata_parser_read(pParser, buffer, DRWAV_BEXT_ORIGINATOR_NAME_BYTES, &bytesRead); + if (bytesJustRead != DRWAV_BEXT_ORIGINATOR_NAME_BYTES) { return bytesRead; } - allocSizeNeeded += ma_dr_wav__strlen(buffer) + 1; - buffer[MA_DR_WAV_BEXT_ORIGINATOR_REF_BYTES] = '\0'; - bytesJustRead = ma_dr_wav__metadata_parser_read(pParser, buffer, MA_DR_WAV_BEXT_ORIGINATOR_REF_BYTES, &bytesRead); - if (bytesJustRead != MA_DR_WAV_BEXT_ORIGINATOR_REF_BYTES) { + allocSizeNeeded += drwav__strlen(buffer) + 1; + buffer[DRWAV_BEXT_ORIGINATOR_REF_BYTES] = '\0'; + bytesJustRead = drwav__metadata_parser_read(pParser, buffer, DRWAV_BEXT_ORIGINATOR_REF_BYTES, &bytesRead); + if (bytesJustRead != DRWAV_BEXT_ORIGINATOR_REF_BYTES) { return bytesRead; } - allocSizeNeeded += ma_dr_wav__strlen(buffer) + 1; - allocSizeNeeded += (size_t)pChunkHeader->sizeInBytes - MA_DR_WAV_BEXT_BYTES; - ma_dr_wav__metadata_request_extra_memory_for_stage_2(pParser, allocSizeNeeded, 1); + allocSizeNeeded += drwav__strlen(buffer) + 1; + allocSizeNeeded += (size_t)pChunkHeader->sizeInBytes - DRWAV_BEXT_BYTES; + drwav__metadata_request_extra_memory_for_stage_2(pParser, allocSizeNeeded, 1); pParser->metadataCount += 1; } else { - bytesRead = ma_dr_wav__read_bext_to_metadata_obj(pParser, &pParser->pMetadata[pParser->metadataCursor], pChunkHeader->sizeInBytes); + bytesRead = drwav__read_bext_to_metadata_obj(pParser, &pParser->pMetadata[pParser->metadataCursor], pChunkHeader->sizeInBytes); if (bytesRead == pChunkHeader->sizeInBytes) { pParser->metadataCursor += 1; } else { @@ -79203,37 +77792,37 @@ MA_PRIVATE ma_uint64 ma_dr_wav__metadata_process_chunk(ma_dr_wav__metadata_parse } } else { } - } else if (ma_dr_wav_fourcc_equal(pChunkID, "LIST") || ma_dr_wav_fourcc_equal(pChunkID, "list")) { - ma_dr_wav_metadata_location listType = ma_dr_wav_metadata_location_invalid; + } else if (drwav_fourcc_equal(pChunkID, "LIST") || drwav_fourcc_equal(pChunkID, "list")) { + drwav_metadata_location listType = drwav_metadata_location_invalid; while (bytesRead < pChunkHeader->sizeInBytes) { - ma_uint8 subchunkId[4]; - ma_uint8 subchunkSizeBuffer[4]; - ma_uint64 subchunkDataSize; - ma_uint64 subchunkBytesRead = 0; - ma_uint64 bytesJustRead = ma_dr_wav__metadata_parser_read(pParser, subchunkId, sizeof(subchunkId), &bytesRead); + drwav_uint8 subchunkId[4]; + drwav_uint8 subchunkSizeBuffer[4]; + drwav_uint64 subchunkDataSize; + drwav_uint64 subchunkBytesRead = 0; + drwav_uint64 bytesJustRead = drwav__metadata_parser_read(pParser, subchunkId, sizeof(subchunkId), &bytesRead); if (bytesJustRead != sizeof(subchunkId)) { break; } - if (ma_dr_wav_fourcc_equal(subchunkId, "adtl")) { - listType = ma_dr_wav_metadata_location_inside_adtl_list; + if (drwav_fourcc_equal(subchunkId, "adtl")) { + listType = drwav_metadata_location_inside_adtl_list; continue; - } else if (ma_dr_wav_fourcc_equal(subchunkId, "INFO")) { - listType = ma_dr_wav_metadata_location_inside_info_list; + } else if (drwav_fourcc_equal(subchunkId, "INFO")) { + listType = drwav_metadata_location_inside_info_list; continue; } - bytesJustRead = ma_dr_wav__metadata_parser_read(pParser, subchunkSizeBuffer, sizeof(subchunkSizeBuffer), &bytesRead); + bytesJustRead = drwav__metadata_parser_read(pParser, subchunkSizeBuffer, sizeof(subchunkSizeBuffer), &bytesRead); if (bytesJustRead != sizeof(subchunkSizeBuffer)) { break; } - subchunkDataSize = ma_dr_wav_bytes_to_u32(subchunkSizeBuffer); - if (ma_dr_wav__chunk_matches(allowedMetadataTypes, subchunkId, ma_dr_wav_metadata_type_list_label, "labl") || ma_dr_wav__chunk_matches(allowedMetadataTypes, subchunkId, ma_dr_wav_metadata_type_list_note, "note")) { - if (subchunkDataSize >= MA_DR_WAV_LIST_LABEL_OR_NOTE_BYTES) { - ma_uint64 stringSizeWithNullTerm = subchunkDataSize - MA_DR_WAV_LIST_LABEL_OR_NOTE_BYTES; - if (pParser->stage == ma_dr_wav__metadata_parser_stage_count) { + subchunkDataSize = drwav_bytes_to_u32(subchunkSizeBuffer); + if (drwav__chunk_matches(allowedMetadataTypes, subchunkId, drwav_metadata_type_list_label, "labl") || drwav__chunk_matches(allowedMetadataTypes, subchunkId, drwav_metadata_type_list_note, "note")) { + if (subchunkDataSize >= DRWAV_LIST_LABEL_OR_NOTE_BYTES) { + drwav_uint64 stringSizeWithNullTerm = subchunkDataSize - DRWAV_LIST_LABEL_OR_NOTE_BYTES; + if (pParser->stage == drwav__metadata_parser_stage_count) { pParser->metadataCount += 1; - ma_dr_wav__metadata_request_extra_memory_for_stage_2(pParser, (size_t)stringSizeWithNullTerm, 1); + drwav__metadata_request_extra_memory_for_stage_2(pParser, (size_t)stringSizeWithNullTerm, 1); } else { - subchunkBytesRead = ma_dr_wav__read_list_label_or_note_to_metadata_obj(pParser, &pParser->pMetadata[pParser->metadataCursor], subchunkDataSize, ma_dr_wav_fourcc_equal(subchunkId, "labl") ? ma_dr_wav_metadata_type_list_label : ma_dr_wav_metadata_type_list_note); + subchunkBytesRead = drwav__read_list_label_or_note_to_metadata_obj(pParser, &pParser->pMetadata[pParser->metadataCursor], subchunkDataSize, drwav_fourcc_equal(subchunkId, "labl") ? drwav_metadata_type_list_label : drwav_metadata_type_list_note); if (subchunkBytesRead == subchunkDataSize) { pParser->metadataCursor += 1; } else { @@ -79241,14 +77830,14 @@ MA_PRIVATE ma_uint64 ma_dr_wav__metadata_process_chunk(ma_dr_wav__metadata_parse } } else { } - } else if (ma_dr_wav__chunk_matches(allowedMetadataTypes, subchunkId, ma_dr_wav_metadata_type_list_labelled_cue_region, "ltxt")) { - if (subchunkDataSize >= MA_DR_WAV_LIST_LABELLED_TEXT_BYTES) { - ma_uint64 stringSizeWithNullTerminator = subchunkDataSize - MA_DR_WAV_LIST_LABELLED_TEXT_BYTES; - if (pParser->stage == ma_dr_wav__metadata_parser_stage_count) { + } else if (drwav__chunk_matches(allowedMetadataTypes, subchunkId, drwav_metadata_type_list_labelled_cue_region, "ltxt")) { + if (subchunkDataSize >= DRWAV_LIST_LABELLED_TEXT_BYTES) { + drwav_uint64 stringSizeWithNullTerminator = subchunkDataSize - DRWAV_LIST_LABELLED_TEXT_BYTES; + if (pParser->stage == drwav__metadata_parser_stage_count) { pParser->metadataCount += 1; - ma_dr_wav__metadata_request_extra_memory_for_stage_2(pParser, (size_t)stringSizeWithNullTerminator, 1); + drwav__metadata_request_extra_memory_for_stage_2(pParser, (size_t)stringSizeWithNullTerminator, 1); } else { - subchunkBytesRead = ma_dr_wav__read_list_labelled_cue_region_to_metadata_obj(pParser, &pParser->pMetadata[pParser->metadataCursor], subchunkDataSize); + subchunkBytesRead = drwav__read_list_labelled_cue_region_to_metadata_obj(pParser, &pParser->pMetadata[pParser->metadataCursor], subchunkDataSize); if (subchunkBytesRead == subchunkDataSize) { pParser->metadataCursor += 1; } else { @@ -79256,541 +77845,332 @@ MA_PRIVATE ma_uint64 ma_dr_wav__metadata_process_chunk(ma_dr_wav__metadata_parse } } else { } - } else if (ma_dr_wav__chunk_matches(allowedMetadataTypes, subchunkId, ma_dr_wav_metadata_type_list_info_software, "ISFT")) { - subchunkBytesRead = ma_dr_wav__metadata_process_info_text_chunk(pParser, subchunkDataSize, ma_dr_wav_metadata_type_list_info_software); - } else if (ma_dr_wav__chunk_matches(allowedMetadataTypes, subchunkId, ma_dr_wav_metadata_type_list_info_copyright, "ICOP")) { - subchunkBytesRead = ma_dr_wav__metadata_process_info_text_chunk(pParser, subchunkDataSize, ma_dr_wav_metadata_type_list_info_copyright); - } else if (ma_dr_wav__chunk_matches(allowedMetadataTypes, subchunkId, ma_dr_wav_metadata_type_list_info_title, "INAM")) { - subchunkBytesRead = ma_dr_wav__metadata_process_info_text_chunk(pParser, subchunkDataSize, ma_dr_wav_metadata_type_list_info_title); - } else if (ma_dr_wav__chunk_matches(allowedMetadataTypes, subchunkId, ma_dr_wav_metadata_type_list_info_artist, "IART")) { - subchunkBytesRead = ma_dr_wav__metadata_process_info_text_chunk(pParser, subchunkDataSize, ma_dr_wav_metadata_type_list_info_artist); - } else if (ma_dr_wav__chunk_matches(allowedMetadataTypes, subchunkId, ma_dr_wav_metadata_type_list_info_comment, "ICMT")) { - subchunkBytesRead = ma_dr_wav__metadata_process_info_text_chunk(pParser, subchunkDataSize, ma_dr_wav_metadata_type_list_info_comment); - } else if (ma_dr_wav__chunk_matches(allowedMetadataTypes, subchunkId, ma_dr_wav_metadata_type_list_info_date, "ICRD")) { - subchunkBytesRead = ma_dr_wav__metadata_process_info_text_chunk(pParser, subchunkDataSize, ma_dr_wav_metadata_type_list_info_date); - } else if (ma_dr_wav__chunk_matches(allowedMetadataTypes, subchunkId, ma_dr_wav_metadata_type_list_info_genre, "IGNR")) { - subchunkBytesRead = ma_dr_wav__metadata_process_info_text_chunk(pParser, subchunkDataSize, ma_dr_wav_metadata_type_list_info_genre); - } else if (ma_dr_wav__chunk_matches(allowedMetadataTypes, subchunkId, ma_dr_wav_metadata_type_list_info_album, "IPRD")) { - subchunkBytesRead = ma_dr_wav__metadata_process_info_text_chunk(pParser, subchunkDataSize, ma_dr_wav_metadata_type_list_info_album); - } else if (ma_dr_wav__chunk_matches(allowedMetadataTypes, subchunkId, ma_dr_wav_metadata_type_list_info_tracknumber, "ITRK")) { - subchunkBytesRead = ma_dr_wav__metadata_process_info_text_chunk(pParser, subchunkDataSize, ma_dr_wav_metadata_type_list_info_tracknumber); - } else if ((allowedMetadataTypes & ma_dr_wav_metadata_type_unknown) != 0) { - subchunkBytesRead = ma_dr_wav__metadata_process_unknown_chunk(pParser, subchunkId, subchunkDataSize, listType); + } else if (drwav__chunk_matches(allowedMetadataTypes, subchunkId, drwav_metadata_type_list_info_software, "ISFT")) { + subchunkBytesRead = drwav__metadata_process_info_text_chunk(pParser, subchunkDataSize, drwav_metadata_type_list_info_software); + } else if (drwav__chunk_matches(allowedMetadataTypes, subchunkId, drwav_metadata_type_list_info_copyright, "ICOP")) { + subchunkBytesRead = drwav__metadata_process_info_text_chunk(pParser, subchunkDataSize, drwav_metadata_type_list_info_copyright); + } else if (drwav__chunk_matches(allowedMetadataTypes, subchunkId, drwav_metadata_type_list_info_title, "INAM")) { + subchunkBytesRead = drwav__metadata_process_info_text_chunk(pParser, subchunkDataSize, drwav_metadata_type_list_info_title); + } else if (drwav__chunk_matches(allowedMetadataTypes, subchunkId, drwav_metadata_type_list_info_artist, "IART")) { + subchunkBytesRead = drwav__metadata_process_info_text_chunk(pParser, subchunkDataSize, drwav_metadata_type_list_info_artist); + } else if (drwav__chunk_matches(allowedMetadataTypes, subchunkId, drwav_metadata_type_list_info_comment, "ICMT")) { + subchunkBytesRead = drwav__metadata_process_info_text_chunk(pParser, subchunkDataSize, drwav_metadata_type_list_info_comment); + } else if (drwav__chunk_matches(allowedMetadataTypes, subchunkId, drwav_metadata_type_list_info_date, "ICRD")) { + subchunkBytesRead = drwav__metadata_process_info_text_chunk(pParser, subchunkDataSize, drwav_metadata_type_list_info_date); + } else if (drwav__chunk_matches(allowedMetadataTypes, subchunkId, drwav_metadata_type_list_info_genre, "IGNR")) { + subchunkBytesRead = drwav__metadata_process_info_text_chunk(pParser, subchunkDataSize, drwav_metadata_type_list_info_genre); + } else if (drwav__chunk_matches(allowedMetadataTypes, subchunkId, drwav_metadata_type_list_info_album, "IPRD")) { + subchunkBytesRead = drwav__metadata_process_info_text_chunk(pParser, subchunkDataSize, drwav_metadata_type_list_info_album); + } else if (drwav__chunk_matches(allowedMetadataTypes, subchunkId, drwav_metadata_type_list_info_tracknumber, "ITRK")) { + subchunkBytesRead = drwav__metadata_process_info_text_chunk(pParser, subchunkDataSize, drwav_metadata_type_list_info_tracknumber); + } else if ((allowedMetadataTypes & drwav_metadata_type_unknown) != 0) { + subchunkBytesRead = drwav__metadata_process_unknown_chunk(pParser, subchunkId, subchunkDataSize, listType); } bytesRead += subchunkBytesRead; - MA_DR_WAV_ASSERT(subchunkBytesRead <= subchunkDataSize); + DRWAV_ASSERT(subchunkBytesRead <= subchunkDataSize); if (subchunkBytesRead < subchunkDataSize) { - ma_uint64 bytesToSeek = subchunkDataSize - subchunkBytesRead; - if (!pParser->onSeek(pParser->pReadSeekUserData, (int)bytesToSeek, ma_dr_wav_seek_origin_current)) { + drwav_uint64 bytesToSeek = subchunkDataSize - subchunkBytesRead; + if (!pParser->onSeek(pParser->pReadSeekUserData, (int)bytesToSeek, drwav_seek_origin_current)) { break; } bytesRead += bytesToSeek; } if ((subchunkDataSize % 2) == 1) { - if (!pParser->onSeek(pParser->pReadSeekUserData, 1, ma_dr_wav_seek_origin_current)) { + if (!pParser->onSeek(pParser->pReadSeekUserData, 1, drwav_seek_origin_current)) { break; } bytesRead += 1; } } - } else if ((allowedMetadataTypes & ma_dr_wav_metadata_type_unknown) != 0) { - bytesRead = ma_dr_wav__metadata_process_unknown_chunk(pParser, pChunkID, pChunkHeader->sizeInBytes, ma_dr_wav_metadata_location_top_level); + } else if ((allowedMetadataTypes & drwav_metadata_type_unknown) != 0) { + bytesRead = drwav__metadata_process_unknown_chunk(pParser, pChunkID, pChunkHeader->sizeInBytes, drwav_metadata_location_top_level); } return bytesRead; } -MA_PRIVATE ma_uint32 ma_dr_wav_get_bytes_per_pcm_frame(ma_dr_wav* pWav) +DRWAV_PRIVATE drwav_uint32 drwav_get_bytes_per_pcm_frame(drwav* pWav) { - ma_uint32 bytesPerFrame; + drwav_uint32 bytesPerFrame; if ((pWav->bitsPerSample & 0x7) == 0) { bytesPerFrame = (pWav->bitsPerSample * pWav->fmt.channels) >> 3; } else { bytesPerFrame = pWav->fmt.blockAlign; } - if (pWav->translatedFormatTag == MA_DR_WAVE_FORMAT_ALAW || pWav->translatedFormatTag == MA_DR_WAVE_FORMAT_MULAW) { + if (pWav->translatedFormatTag == DR_WAVE_FORMAT_ALAW || pWav->translatedFormatTag == DR_WAVE_FORMAT_MULAW) { if (bytesPerFrame != pWav->fmt.channels) { return 0; } } return bytesPerFrame; } -MA_API ma_uint16 ma_dr_wav_fmt_get_format(const ma_dr_wav_fmt* pFMT) +DRWAV_API drwav_uint16 drwav_fmt_get_format(const drwav_fmt* pFMT) { if (pFMT == NULL) { return 0; } - if (pFMT->formatTag != MA_DR_WAVE_FORMAT_EXTENSIBLE) { + if (pFMT->formatTag != DR_WAVE_FORMAT_EXTENSIBLE) { return pFMT->formatTag; } else { - return ma_dr_wav_bytes_to_u16(pFMT->subFormat); + return drwav_bytes_to_u16(pFMT->subFormat); } } -MA_PRIVATE ma_bool32 ma_dr_wav_preinit(ma_dr_wav* pWav, ma_dr_wav_read_proc onRead, ma_dr_wav_seek_proc onSeek, void* pReadSeekUserData, const ma_allocation_callbacks* pAllocationCallbacks) +DRWAV_PRIVATE drwav_bool32 drwav_preinit(drwav* pWav, drwav_read_proc onRead, drwav_seek_proc onSeek, void* pReadSeekUserData, const drwav_allocation_callbacks* pAllocationCallbacks) { if (pWav == NULL || onRead == NULL || onSeek == NULL) { - return MA_FALSE; + return DRWAV_FALSE; } - MA_DR_WAV_ZERO_MEMORY(pWav, sizeof(*pWav)); + DRWAV_ZERO_MEMORY(pWav, sizeof(*pWav)); pWav->onRead = onRead; pWav->onSeek = onSeek; pWav->pUserData = pReadSeekUserData; - pWav->allocationCallbacks = ma_dr_wav_copy_allocation_callbacks_or_defaults(pAllocationCallbacks); + pWav->allocationCallbacks = drwav_copy_allocation_callbacks_or_defaults(pAllocationCallbacks); if (pWav->allocationCallbacks.onFree == NULL || (pWav->allocationCallbacks.onMalloc == NULL && pWav->allocationCallbacks.onRealloc == NULL)) { - return MA_FALSE; + return DRWAV_FALSE; } - return MA_TRUE; + return DRWAV_TRUE; } -MA_PRIVATE ma_bool32 ma_dr_wav_init__internal(ma_dr_wav* pWav, ma_dr_wav_chunk_proc onChunk, void* pChunkUserData, ma_uint32 flags) +DRWAV_PRIVATE drwav_bool32 drwav_init__internal(drwav* pWav, drwav_chunk_proc onChunk, void* pChunkUserData, drwav_uint32 flags) { - ma_result result; - ma_uint64 cursor; - ma_bool32 sequential; - ma_uint8 riff[4]; - ma_dr_wav_fmt fmt; + drwav_uint64 cursor; + drwav_bool32 sequential; + drwav_uint8 riff[4]; + drwav_fmt fmt; unsigned short translatedFormatTag; - ma_uint64 dataChunkSize = 0; - ma_uint64 sampleCountFromFactChunk = 0; - ma_uint64 metadataStartPos; - ma_dr_wav__metadata_parser metadataParser; - ma_bool8 isProcessingMetadata = MA_FALSE; - ma_bool8 foundChunk_fmt = MA_FALSE; - ma_bool8 foundChunk_data = MA_FALSE; - ma_bool8 isAIFCFormType = MA_FALSE; - ma_uint64 aiffFrameCount = 0; + drwav_bool32 foundDataChunk; + drwav_uint64 dataChunkSize = 0; + drwav_uint64 sampleCountFromFactChunk = 0; + drwav_uint64 chunkSize; + drwav__metadata_parser metadataParser; cursor = 0; - sequential = (flags & MA_DR_WAV_SEQUENTIAL) != 0; - MA_DR_WAV_ZERO_OBJECT(&fmt); - if (ma_dr_wav__on_read(pWav->onRead, pWav->pUserData, riff, sizeof(riff), &cursor) != sizeof(riff)) { - return MA_FALSE; + sequential = (flags & DRWAV_SEQUENTIAL) != 0; + if (drwav__on_read(pWav->onRead, pWav->pUserData, riff, sizeof(riff), &cursor) != sizeof(riff)) { + return DRWAV_FALSE; } - if (ma_dr_wav_fourcc_equal(riff, "RIFF")) { - pWav->container = ma_dr_wav_container_riff; - } else if (ma_dr_wav_fourcc_equal(riff, "RIFX")) { - pWav->container = ma_dr_wav_container_rifx; - } else if (ma_dr_wav_fourcc_equal(riff, "riff")) { + if (drwav_fourcc_equal(riff, "RIFF")) { + pWav->container = drwav_container_riff; + } else if (drwav_fourcc_equal(riff, "riff")) { int i; - ma_uint8 riff2[12]; - pWav->container = ma_dr_wav_container_w64; - if (ma_dr_wav__on_read(pWav->onRead, pWav->pUserData, riff2, sizeof(riff2), &cursor) != sizeof(riff2)) { - return MA_FALSE; + drwav_uint8 riff2[12]; + pWav->container = drwav_container_w64; + if (drwav__on_read(pWav->onRead, pWav->pUserData, riff2, sizeof(riff2), &cursor) != sizeof(riff2)) { + return DRWAV_FALSE; } for (i = 0; i < 12; ++i) { - if (riff2[i] != ma_dr_wavGUID_W64_RIFF[i+4]) { - return MA_FALSE; + if (riff2[i] != drwavGUID_W64_RIFF[i+4]) { + return DRWAV_FALSE; } } - } else if (ma_dr_wav_fourcc_equal(riff, "RF64")) { - pWav->container = ma_dr_wav_container_rf64; - } else if (ma_dr_wav_fourcc_equal(riff, "FORM")) { - pWav->container = ma_dr_wav_container_aiff; + } else if (drwav_fourcc_equal(riff, "RF64")) { + pWav->container = drwav_container_rf64; } else { - return MA_FALSE; + return DRWAV_FALSE; } - if (pWav->container == ma_dr_wav_container_riff || pWav->container == ma_dr_wav_container_rifx || pWav->container == ma_dr_wav_container_rf64) { - ma_uint8 chunkSizeBytes[4]; - ma_uint8 wave[4]; - if (ma_dr_wav__on_read(pWav->onRead, pWav->pUserData, chunkSizeBytes, sizeof(chunkSizeBytes), &cursor) != sizeof(chunkSizeBytes)) { - return MA_FALSE; + if (pWav->container == drwav_container_riff || pWav->container == drwav_container_rf64) { + drwav_uint8 chunkSizeBytes[4]; + drwav_uint8 wave[4]; + if (drwav__on_read(pWav->onRead, pWav->pUserData, chunkSizeBytes, sizeof(chunkSizeBytes), &cursor) != sizeof(chunkSizeBytes)) { + return DRWAV_FALSE; } - if (pWav->container == ma_dr_wav_container_riff || pWav->container == ma_dr_wav_container_rifx) { - if (ma_dr_wav_bytes_to_u32_ex(chunkSizeBytes, pWav->container) < 36) { - } - } else if (pWav->container == ma_dr_wav_container_rf64) { - if (ma_dr_wav_bytes_to_u32_le(chunkSizeBytes) != 0xFFFFFFFF) { - return MA_FALSE; + if (pWav->container == drwav_container_riff) { + if (drwav_bytes_to_u32(chunkSizeBytes) < 36) { + return DRWAV_FALSE; } } else { - return MA_FALSE; - } - if (ma_dr_wav__on_read(pWav->onRead, pWav->pUserData, wave, sizeof(wave), &cursor) != sizeof(wave)) { - return MA_FALSE; - } - if (!ma_dr_wav_fourcc_equal(wave, "WAVE")) { - return MA_FALSE; - } - } else if (pWav->container == ma_dr_wav_container_w64) { - ma_uint8 chunkSizeBytes[8]; - ma_uint8 wave[16]; - if (ma_dr_wav__on_read(pWav->onRead, pWav->pUserData, chunkSizeBytes, sizeof(chunkSizeBytes), &cursor) != sizeof(chunkSizeBytes)) { - return MA_FALSE; - } - if (ma_dr_wav_bytes_to_u64(chunkSizeBytes) < 80) { - return MA_FALSE; + if (drwav_bytes_to_u32(chunkSizeBytes) != 0xFFFFFFFF) { + return DRWAV_FALSE; + } } - if (ma_dr_wav__on_read(pWav->onRead, pWav->pUserData, wave, sizeof(wave), &cursor) != sizeof(wave)) { - return MA_FALSE; + if (drwav__on_read(pWav->onRead, pWav->pUserData, wave, sizeof(wave), &cursor) != sizeof(wave)) { + return DRWAV_FALSE; } - if (!ma_dr_wav_guid_equal(wave, ma_dr_wavGUID_W64_WAVE)) { - return MA_FALSE; + if (!drwav_fourcc_equal(wave, "WAVE")) { + return DRWAV_FALSE; } - } else if (pWav->container == ma_dr_wav_container_aiff) { - ma_uint8 chunkSizeBytes[4]; - ma_uint8 aiff[4]; - if (ma_dr_wav__on_read(pWav->onRead, pWav->pUserData, chunkSizeBytes, sizeof(chunkSizeBytes), &cursor) != sizeof(chunkSizeBytes)) { - return MA_FALSE; + } else { + drwav_uint8 chunkSizeBytes[8]; + drwav_uint8 wave[16]; + if (drwav__on_read(pWav->onRead, pWav->pUserData, chunkSizeBytes, sizeof(chunkSizeBytes), &cursor) != sizeof(chunkSizeBytes)) { + return DRWAV_FALSE; } - if (ma_dr_wav_bytes_to_u32_be(chunkSizeBytes) < 18) { - return MA_FALSE; + if (drwav_bytes_to_u64(chunkSizeBytes) < 80) { + return DRWAV_FALSE; } - if (ma_dr_wav__on_read(pWav->onRead, pWav->pUserData, aiff, sizeof(aiff), &cursor) != sizeof(aiff)) { - return MA_FALSE; + if (drwav__on_read(pWav->onRead, pWav->pUserData, wave, sizeof(wave), &cursor) != sizeof(wave)) { + return DRWAV_FALSE; } - if (ma_dr_wav_fourcc_equal(aiff, "AIFF")) { - isAIFCFormType = MA_FALSE; - } else if (ma_dr_wav_fourcc_equal(aiff, "AIFC")) { - isAIFCFormType = MA_TRUE; - } else { - return MA_FALSE; + if (!drwav_guid_equal(wave, drwavGUID_W64_WAVE)) { + return DRWAV_FALSE; } - } else { - return MA_FALSE; } - if (pWav->container == ma_dr_wav_container_rf64) { - ma_uint8 sizeBytes[8]; - ma_uint64 bytesRemainingInChunk; - ma_dr_wav_chunk_header header; - result = ma_dr_wav__read_chunk_header(pWav->onRead, pWav->pUserData, pWav->container, &cursor, &header); - if (result != MA_SUCCESS) { - return MA_FALSE; + if (pWav->container == drwav_container_rf64) { + drwav_uint8 sizeBytes[8]; + drwav_uint64 bytesRemainingInChunk; + drwav_chunk_header header; + drwav_result result = drwav__read_chunk_header(pWav->onRead, pWav->pUserData, pWav->container, &cursor, &header); + if (result != DRWAV_SUCCESS) { + return DRWAV_FALSE; } - if (!ma_dr_wav_fourcc_equal(header.id.fourcc, "ds64")) { - return MA_FALSE; + if (!drwav_fourcc_equal(header.id.fourcc, "ds64")) { + return DRWAV_FALSE; } bytesRemainingInChunk = header.sizeInBytes + header.paddingSize; - if (!ma_dr_wav__seek_forward(pWav->onSeek, 8, pWav->pUserData)) { - return MA_FALSE; + if (!drwav__seek_forward(pWav->onSeek, 8, pWav->pUserData)) { + return DRWAV_FALSE; } bytesRemainingInChunk -= 8; cursor += 8; - if (ma_dr_wav__on_read(pWav->onRead, pWav->pUserData, sizeBytes, sizeof(sizeBytes), &cursor) != sizeof(sizeBytes)) { - return MA_FALSE; + if (drwav__on_read(pWav->onRead, pWav->pUserData, sizeBytes, sizeof(sizeBytes), &cursor) != sizeof(sizeBytes)) { + return DRWAV_FALSE; } bytesRemainingInChunk -= 8; - dataChunkSize = ma_dr_wav_bytes_to_u64(sizeBytes); - if (ma_dr_wav__on_read(pWav->onRead, pWav->pUserData, sizeBytes, sizeof(sizeBytes), &cursor) != sizeof(sizeBytes)) { - return MA_FALSE; + dataChunkSize = drwav_bytes_to_u64(sizeBytes); + if (drwav__on_read(pWav->onRead, pWav->pUserData, sizeBytes, sizeof(sizeBytes), &cursor) != sizeof(sizeBytes)) { + return DRWAV_FALSE; } bytesRemainingInChunk -= 8; - sampleCountFromFactChunk = ma_dr_wav_bytes_to_u64(sizeBytes); - if (!ma_dr_wav__seek_forward(pWav->onSeek, bytesRemainingInChunk, pWav->pUserData)) { - return MA_FALSE; + sampleCountFromFactChunk = drwav_bytes_to_u64(sizeBytes); + if (!drwav__seek_forward(pWav->onSeek, bytesRemainingInChunk, pWav->pUserData)) { + return DRWAV_FALSE; } cursor += bytesRemainingInChunk; } - metadataStartPos = cursor; - isProcessingMetadata = !sequential && ((flags & MA_DR_WAV_WITH_METADATA) != 0); - if (pWav->container != ma_dr_wav_container_riff && pWav->container != ma_dr_wav_container_rf64) { - isProcessingMetadata = MA_FALSE; + if (!drwav__read_fmt(pWav->onRead, pWav->onSeek, pWav->pUserData, pWav->container, &cursor, &fmt)) { + return DRWAV_FALSE; } - MA_DR_WAV_ZERO_MEMORY(&metadataParser, sizeof(metadataParser)); - if (isProcessingMetadata) { + if ((fmt.sampleRate == 0 || fmt.sampleRate > DRWAV_MAX_SAMPLE_RATE) || + (fmt.channels == 0 || fmt.channels > DRWAV_MAX_CHANNELS) || + (fmt.bitsPerSample == 0 || fmt.bitsPerSample > DRWAV_MAX_BITS_PER_SAMPLE) || + fmt.blockAlign == 0) { + return DRWAV_FALSE; + } + translatedFormatTag = fmt.formatTag; + if (translatedFormatTag == DR_WAVE_FORMAT_EXTENSIBLE) { + translatedFormatTag = drwav_bytes_to_u16(fmt.subFormat + 0); + } + DRWAV_ZERO_MEMORY(&metadataParser, sizeof(metadataParser)); + if (!sequential && pWav->allowedMetadataTypes != drwav_metadata_type_none && (pWav->container == drwav_container_riff || pWav->container == drwav_container_rf64)) { + drwav_uint64 cursorForMetadata = cursor; metadataParser.onRead = pWav->onRead; metadataParser.onSeek = pWav->onSeek; metadataParser.pReadSeekUserData = pWav->pUserData; - metadataParser.stage = ma_dr_wav__metadata_parser_stage_count; + metadataParser.stage = drwav__metadata_parser_stage_count; + for (;;) { + drwav_result result; + drwav_uint64 bytesRead; + drwav_uint64 remainingBytes; + drwav_chunk_header header; + result = drwav__read_chunk_header(pWav->onRead, pWav->pUserData, pWav->container, &cursorForMetadata, &header); + if (result != DRWAV_SUCCESS) { + break; + } + bytesRead = drwav__metadata_process_chunk(&metadataParser, &header, pWav->allowedMetadataTypes); + DRWAV_ASSERT(bytesRead <= header.sizeInBytes); + remainingBytes = header.sizeInBytes - bytesRead + header.paddingSize; + if (!drwav__seek_forward(pWav->onSeek, remainingBytes, pWav->pUserData)) { + break; + } + cursorForMetadata += remainingBytes; + } + if (!drwav__seek_from_start(pWav->onSeek, cursor, pWav->pUserData)) { + return DRWAV_FALSE; + } + drwav__metadata_alloc(&metadataParser, &pWav->allocationCallbacks); + metadataParser.stage = drwav__metadata_parser_stage_read; } + foundDataChunk = DRWAV_FALSE; for (;;) { - ma_dr_wav_chunk_header header; - ma_uint64 chunkSize; - result = ma_dr_wav__read_chunk_header(pWav->onRead, pWav->pUserData, pWav->container, &cursor, &header); - if (result != MA_SUCCESS) { - break; + drwav_chunk_header header; + drwav_result result = drwav__read_chunk_header(pWav->onRead, pWav->pUserData, pWav->container, &cursor, &header); + if (result != DRWAV_SUCCESS) { + if (!foundDataChunk) { + return DRWAV_FALSE; + } else { + break; + } } - chunkSize = header.sizeInBytes; if (!sequential && onChunk != NULL) { - ma_uint64 callbackBytesRead = onChunk(pChunkUserData, pWav->onRead, pWav->onSeek, pWav->pUserData, &header, pWav->container, &fmt); + drwav_uint64 callbackBytesRead = onChunk(pChunkUserData, pWav->onRead, pWav->onSeek, pWav->pUserData, &header, pWav->container, &fmt); if (callbackBytesRead > 0) { - if (ma_dr_wav__seek_from_start(pWav->onSeek, cursor, pWav->pUserData) == MA_FALSE) { - return MA_FALSE; + if (!drwav__seek_from_start(pWav->onSeek, cursor, pWav->pUserData)) { + return DRWAV_FALSE; } } } - if (((pWav->container == ma_dr_wav_container_riff || pWav->container == ma_dr_wav_container_rifx || pWav->container == ma_dr_wav_container_rf64) && ma_dr_wav_fourcc_equal(header.id.fourcc, "fmt ")) || - ((pWav->container == ma_dr_wav_container_w64) && ma_dr_wav_guid_equal(header.id.guid, ma_dr_wavGUID_W64_FMT))) { - ma_uint8 fmtData[16]; - foundChunk_fmt = MA_TRUE; - if (pWav->onRead(pWav->pUserData, fmtData, sizeof(fmtData)) != sizeof(fmtData)) { - return MA_FALSE; - } - cursor += sizeof(fmtData); - fmt.formatTag = ma_dr_wav_bytes_to_u16_ex(fmtData + 0, pWav->container); - fmt.channels = ma_dr_wav_bytes_to_u16_ex(fmtData + 2, pWav->container); - fmt.sampleRate = ma_dr_wav_bytes_to_u32_ex(fmtData + 4, pWav->container); - fmt.avgBytesPerSec = ma_dr_wav_bytes_to_u32_ex(fmtData + 8, pWav->container); - fmt.blockAlign = ma_dr_wav_bytes_to_u16_ex(fmtData + 12, pWav->container); - fmt.bitsPerSample = ma_dr_wav_bytes_to_u16_ex(fmtData + 14, pWav->container); - fmt.extendedSize = 0; - fmt.validBitsPerSample = 0; - fmt.channelMask = 0; - MA_DR_WAV_ZERO_MEMORY(fmt.subFormat, sizeof(fmt.subFormat)); - if (header.sizeInBytes > 16) { - ma_uint8 fmt_cbSize[2]; - int bytesReadSoFar = 0; - if (pWav->onRead(pWav->pUserData, fmt_cbSize, sizeof(fmt_cbSize)) != sizeof(fmt_cbSize)) { - return MA_FALSE; - } - cursor += sizeof(fmt_cbSize); - bytesReadSoFar = 18; - fmt.extendedSize = ma_dr_wav_bytes_to_u16_ex(fmt_cbSize, pWav->container); - if (fmt.extendedSize > 0) { - if (fmt.formatTag == MA_DR_WAVE_FORMAT_EXTENSIBLE) { - if (fmt.extendedSize != 22) { - return MA_FALSE; - } - } - if (fmt.formatTag == MA_DR_WAVE_FORMAT_EXTENSIBLE) { - ma_uint8 fmtext[22]; - if (pWav->onRead(pWav->pUserData, fmtext, fmt.extendedSize) != fmt.extendedSize) { - return MA_FALSE; - } - fmt.validBitsPerSample = ma_dr_wav_bytes_to_u16_ex(fmtext + 0, pWav->container); - fmt.channelMask = ma_dr_wav_bytes_to_u32_ex(fmtext + 2, pWav->container); - ma_dr_wav_bytes_to_guid(fmtext + 6, fmt.subFormat); - } else { - if (pWav->onSeek(pWav->pUserData, fmt.extendedSize, ma_dr_wav_seek_origin_current) == MA_FALSE) { - return MA_FALSE; - } - } - cursor += fmt.extendedSize; - bytesReadSoFar += fmt.extendedSize; - } - if (pWav->onSeek(pWav->pUserData, (int)(header.sizeInBytes - bytesReadSoFar), ma_dr_wav_seek_origin_current) == MA_FALSE) { - return MA_FALSE; + if (!sequential && pWav->allowedMetadataTypes != drwav_metadata_type_none && (pWav->container == drwav_container_riff || pWav->container == drwav_container_rf64)) { + drwav_uint64 bytesRead = drwav__metadata_process_chunk(&metadataParser, &header, pWav->allowedMetadataTypes); + if (bytesRead > 0) { + if (!drwav__seek_from_start(pWav->onSeek, cursor, pWav->pUserData)) { + return DRWAV_FALSE; } - cursor += (header.sizeInBytes - bytesReadSoFar); } - if (header.paddingSize > 0) { - if (ma_dr_wav__seek_forward(pWav->onSeek, header.paddingSize, pWav->pUserData) == MA_FALSE) { - break; - } - cursor += header.paddingSize; - } - continue; } - if (((pWav->container == ma_dr_wav_container_riff || pWav->container == ma_dr_wav_container_rifx || pWav->container == ma_dr_wav_container_rf64) && ma_dr_wav_fourcc_equal(header.id.fourcc, "data")) || - ((pWav->container == ma_dr_wav_container_w64) && ma_dr_wav_guid_equal(header.id.guid, ma_dr_wavGUID_W64_DATA))) { - foundChunk_data = MA_TRUE; - pWav->dataChunkDataPos = cursor; - if (pWav->container != ma_dr_wav_container_rf64) { - dataChunkSize = chunkSize; - } - if (sequential || !isProcessingMetadata) { - break; - } else { - chunkSize += header.paddingSize; - if (ma_dr_wav__seek_forward(pWav->onSeek, chunkSize, pWav->pUserData) == MA_FALSE) { - break; - } - cursor += chunkSize; - continue; - } + if (!foundDataChunk) { + pWav->dataChunkDataPos = cursor; } - if (((pWav->container == ma_dr_wav_container_riff || pWav->container == ma_dr_wav_container_rifx || pWav->container == ma_dr_wav_container_rf64) && ma_dr_wav_fourcc_equal(header.id.fourcc, "fact")) || - ((pWav->container == ma_dr_wav_container_w64) && ma_dr_wav_guid_equal(header.id.guid, ma_dr_wavGUID_W64_FACT))) { - if (pWav->container == ma_dr_wav_container_riff || pWav->container == ma_dr_wav_container_rifx) { - ma_uint8 sampleCount[4]; - if (ma_dr_wav__on_read(pWav->onRead, pWav->pUserData, &sampleCount, 4, &cursor) != 4) { - return MA_FALSE; - } - chunkSize -= 4; - if (pWav->translatedFormatTag == MA_DR_WAVE_FORMAT_ADPCM) { - sampleCountFromFactChunk = ma_dr_wav_bytes_to_u32_ex(sampleCount, pWav->container); - } else { - sampleCountFromFactChunk = 0; - } - } else if (pWav->container == ma_dr_wav_container_w64) { - if (ma_dr_wav__on_read(pWav->onRead, pWav->pUserData, &sampleCountFromFactChunk, 8, &cursor) != 8) { - return MA_FALSE; + chunkSize = header.sizeInBytes; + if (pWav->container == drwav_container_riff || pWav->container == drwav_container_rf64) { + if (drwav_fourcc_equal(header.id.fourcc, "data")) { + foundDataChunk = DRWAV_TRUE; + if (pWav->container != drwav_container_rf64) { + dataChunkSize = chunkSize; } - chunkSize -= 8; - } else if (pWav->container == ma_dr_wav_container_rf64) { } - chunkSize += header.paddingSize; - if (ma_dr_wav__seek_forward(pWav->onSeek, chunkSize, pWav->pUserData) == MA_FALSE) { - break; + } else { + if (drwav_guid_equal(header.id.guid, drwavGUID_W64_DATA)) { + foundDataChunk = DRWAV_TRUE; + dataChunkSize = chunkSize; } - cursor += chunkSize; - continue; } - if (pWav->container == ma_dr_wav_container_aiff && ma_dr_wav_fourcc_equal(header.id.fourcc, "COMM")) { - ma_uint8 commData[24]; - ma_uint32 commDataBytesToRead; - ma_uint16 channels; - ma_uint32 frameCount; - ma_uint16 sampleSizeInBits; - ma_int64 sampleRate; - ma_uint16 compressionFormat; - foundChunk_fmt = MA_TRUE; - if (isAIFCFormType) { - commDataBytesToRead = 24; - if (header.sizeInBytes < commDataBytesToRead) { - return MA_FALSE; + if (foundDataChunk && sequential) { + break; + } + if (pWav->container == drwav_container_riff) { + if (drwav_fourcc_equal(header.id.fourcc, "fact")) { + drwav_uint32 sampleCount; + if (drwav__on_read(pWav->onRead, pWav->pUserData, &sampleCount, 4, &cursor) != 4) { + return DRWAV_FALSE; } - } else { - commDataBytesToRead = 18; - if (header.sizeInBytes != commDataBytesToRead) { - return MA_FALSE; + chunkSize -= 4; + if (!foundDataChunk) { + pWav->dataChunkDataPos = cursor; } - } - if (ma_dr_wav__on_read(pWav->onRead, pWav->pUserData, commData, commDataBytesToRead, &cursor) != commDataBytesToRead) { - return MA_FALSE; - } - channels = ma_dr_wav_bytes_to_u16_ex (commData + 0, pWav->container); - frameCount = ma_dr_wav_bytes_to_u32_ex (commData + 2, pWav->container); - sampleSizeInBits = ma_dr_wav_bytes_to_u16_ex (commData + 6, pWav->container); - sampleRate = ma_dr_wav_aiff_extented_to_s64(commData + 8); - if (sampleRate < 0 || sampleRate > 0xFFFFFFFF) { - return MA_FALSE; - } - if (isAIFCFormType) { - const ma_uint8* type = commData + 18; - if (ma_dr_wav_fourcc_equal(type, "NONE")) { - compressionFormat = MA_DR_WAVE_FORMAT_PCM; - } else if (ma_dr_wav_fourcc_equal(type, "raw ")) { - compressionFormat = MA_DR_WAVE_FORMAT_PCM; - if (sampleSizeInBits == 8) { - pWav->aiff.isUnsigned = MA_TRUE; - } - } else if (ma_dr_wav_fourcc_equal(type, "sowt")) { - compressionFormat = MA_DR_WAVE_FORMAT_PCM; - pWav->aiff.isLE = MA_TRUE; - } else if (ma_dr_wav_fourcc_equal(type, "fl32") || ma_dr_wav_fourcc_equal(type, "fl64") || ma_dr_wav_fourcc_equal(type, "FL32") || ma_dr_wav_fourcc_equal(type, "FL64")) { - compressionFormat = MA_DR_WAVE_FORMAT_IEEE_FLOAT; - } else if (ma_dr_wav_fourcc_equal(type, "alaw") || ma_dr_wav_fourcc_equal(type, "ALAW")) { - compressionFormat = MA_DR_WAVE_FORMAT_ALAW; - } else if (ma_dr_wav_fourcc_equal(type, "ulaw") || ma_dr_wav_fourcc_equal(type, "ULAW")) { - compressionFormat = MA_DR_WAVE_FORMAT_MULAW; - } else if (ma_dr_wav_fourcc_equal(type, "ima4")) { - compressionFormat = MA_DR_WAVE_FORMAT_DVI_ADPCM; - sampleSizeInBits = 4; - (void)compressionFormat; - (void)sampleSizeInBits; - return MA_FALSE; + if (pWav->translatedFormatTag == DR_WAVE_FORMAT_ADPCM) { + sampleCountFromFactChunk = sampleCount; } else { - return MA_FALSE; - } - } else { - compressionFormat = MA_DR_WAVE_FORMAT_PCM; - } - aiffFrameCount = frameCount; - fmt.formatTag = compressionFormat; - fmt.channels = channels; - fmt.sampleRate = (ma_uint32)sampleRate; - fmt.bitsPerSample = sampleSizeInBits; - fmt.blockAlign = (ma_uint16)(fmt.channels * fmt.bitsPerSample / 8); - fmt.avgBytesPerSec = fmt.blockAlign * fmt.sampleRate; - if (fmt.blockAlign == 0 && compressionFormat == MA_DR_WAVE_FORMAT_DVI_ADPCM) { - fmt.blockAlign = 34 * fmt.channels; - } - if (compressionFormat == MA_DR_WAVE_FORMAT_ALAW || compressionFormat == MA_DR_WAVE_FORMAT_MULAW) { - if (fmt.bitsPerSample > 8) { - fmt.bitsPerSample = 8; - fmt.blockAlign = fmt.channels; + sampleCountFromFactChunk = 0; } } - fmt.bitsPerSample += (fmt.bitsPerSample & 7); - if (isAIFCFormType) { - if (ma_dr_wav__seek_forward(pWav->onSeek, (chunkSize - commDataBytesToRead), pWav->pUserData) == MA_FALSE) { - return MA_FALSE; + } else if (pWav->container == drwav_container_w64) { + if (drwav_guid_equal(header.id.guid, drwavGUID_W64_FACT)) { + if (drwav__on_read(pWav->onRead, pWav->pUserData, &sampleCountFromFactChunk, 8, &cursor) != 8) { + return DRWAV_FALSE; } - cursor += (chunkSize - commDataBytesToRead); - } - continue; - } - if (pWav->container == ma_dr_wav_container_aiff && ma_dr_wav_fourcc_equal(header.id.fourcc, "SSND")) { - ma_uint8 offsetAndBlockSizeData[8]; - ma_uint32 offset; - foundChunk_data = MA_TRUE; - if (ma_dr_wav__on_read(pWav->onRead, pWav->pUserData, offsetAndBlockSizeData, sizeof(offsetAndBlockSizeData), &cursor) != sizeof(offsetAndBlockSizeData)) { - return MA_FALSE; - } - offset = ma_dr_wav_bytes_to_u32_ex(offsetAndBlockSizeData + 0, pWav->container); - if (ma_dr_wav__seek_forward(pWav->onSeek, offset, pWav->pUserData) == MA_FALSE) { - return MA_FALSE; - } - cursor += offset; - pWav->dataChunkDataPos = cursor; - dataChunkSize = chunkSize; - if (sequential || !isProcessingMetadata) { - break; - } else { - if (ma_dr_wav__seek_forward(pWav->onSeek, chunkSize, pWav->pUserData) == MA_FALSE) { - break; + chunkSize -= 8; + if (!foundDataChunk) { + pWav->dataChunkDataPos = cursor; } - cursor += chunkSize; - continue; - } - } - if (isProcessingMetadata) { - ma_dr_wav__metadata_process_chunk(&metadataParser, &header, ma_dr_wav_metadata_type_all_including_unknown); - if (ma_dr_wav__seek_from_start(pWav->onSeek, cursor, pWav->pUserData) == MA_FALSE) { - break; } + } else if (pWav->container == drwav_container_rf64) { } chunkSize += header.paddingSize; - if (ma_dr_wav__seek_forward(pWav->onSeek, chunkSize, pWav->pUserData) == MA_FALSE) { + if (!drwav__seek_forward(pWav->onSeek, chunkSize, pWav->pUserData)) { break; } cursor += chunkSize; + if (!foundDataChunk) { + pWav->dataChunkDataPos = cursor; + } } - if (!foundChunk_fmt || !foundChunk_data) { - return MA_FALSE; - } - if ((fmt.sampleRate == 0 || fmt.sampleRate > MA_DR_WAV_MAX_SAMPLE_RATE ) || - (fmt.channels == 0 || fmt.channels > MA_DR_WAV_MAX_CHANNELS ) || - (fmt.bitsPerSample == 0 || fmt.bitsPerSample > MA_DR_WAV_MAX_BITS_PER_SAMPLE) || - fmt.blockAlign == 0) { - return MA_FALSE; - } - translatedFormatTag = fmt.formatTag; - if (translatedFormatTag == MA_DR_WAVE_FORMAT_EXTENSIBLE) { - translatedFormatTag = ma_dr_wav_bytes_to_u16_ex(fmt.subFormat + 0, pWav->container); + pWav->pMetadata = metadataParser.pMetadata; + pWav->metadataCount = metadataParser.metadataCount; + if (!foundDataChunk) { + return DRWAV_FALSE; } if (!sequential) { - if (!ma_dr_wav__seek_from_start(pWav->onSeek, pWav->dataChunkDataPos, pWav->pUserData)) { - return MA_FALSE; + if (!drwav__seek_from_start(pWav->onSeek, pWav->dataChunkDataPos, pWav->pUserData)) { + return DRWAV_FALSE; } cursor = pWav->dataChunkDataPos; } - if (isProcessingMetadata && metadataParser.metadataCount > 0) { - if (ma_dr_wav__seek_from_start(pWav->onSeek, metadataStartPos, pWav->pUserData) == MA_FALSE) { - return MA_FALSE; - } - result = ma_dr_wav__metadata_alloc(&metadataParser, &pWav->allocationCallbacks); - if (result != MA_SUCCESS) { - return MA_FALSE; - } - metadataParser.stage = ma_dr_wav__metadata_parser_stage_read; - for (;;) { - ma_dr_wav_chunk_header header; - ma_uint64 metadataBytesRead; - result = ma_dr_wav__read_chunk_header(pWav->onRead, pWav->pUserData, pWav->container, &cursor, &header); - if (result != MA_SUCCESS) { - break; - } - metadataBytesRead = ma_dr_wav__metadata_process_chunk(&metadataParser, &header, ma_dr_wav_metadata_type_all_including_unknown); - if (ma_dr_wav__seek_forward(pWav->onSeek, (header.sizeInBytes + header.paddingSize) - metadataBytesRead, pWav->pUserData) == MA_FALSE) { - ma_dr_wav_free(metadataParser.pMetadata, &pWav->allocationCallbacks); - return MA_FALSE; - } - } - pWav->pMetadata = metadataParser.pMetadata; - pWav->metadataCount = metadataParser.metadataCount; - } - if (dataChunkSize == 0xFFFFFFFF && (pWav->container == ma_dr_wav_container_riff || pWav->container == ma_dr_wav_container_rifx) && pWav->isSequentialWrite == MA_FALSE) { - dataChunkSize = 0; - for (;;) { - ma_uint8 temp[4096]; - size_t bytesRead = pWav->onRead(pWav->pUserData, temp, sizeof(temp)); - dataChunkSize += bytesRead; - if (bytesRead < sizeof(temp)) { - break; - } - } - } - if (ma_dr_wav__seek_from_start(pWav->onSeek, pWav->dataChunkDataPos, pWav->pUserData) == MA_FALSE) { - ma_dr_wav_free(pWav->pMetadata, &pWav->allocationCallbacks); - return MA_FALSE; - } pWav->fmt = fmt; pWav->sampleRate = fmt.sampleRate; pWav->channels = fmt.channels; @@ -79800,27 +78180,24 @@ MA_PRIVATE ma_bool32 ma_dr_wav_init__internal(ma_dr_wav* pWav, ma_dr_wav_chunk_p pWav->dataChunkDataSize = dataChunkSize; if (sampleCountFromFactChunk != 0) { pWav->totalPCMFrameCount = sampleCountFromFactChunk; - } else if (aiffFrameCount != 0) { - pWav->totalPCMFrameCount = aiffFrameCount; } else { - ma_uint32 bytesPerFrame = ma_dr_wav_get_bytes_per_pcm_frame(pWav); + drwav_uint32 bytesPerFrame = drwav_get_bytes_per_pcm_frame(pWav); if (bytesPerFrame == 0) { - ma_dr_wav_free(pWav->pMetadata, &pWav->allocationCallbacks); - return MA_FALSE; + return DRWAV_FALSE; } pWav->totalPCMFrameCount = dataChunkSize / bytesPerFrame; - if (pWav->translatedFormatTag == MA_DR_WAVE_FORMAT_ADPCM) { - ma_uint64 totalBlockHeaderSizeInBytes; - ma_uint64 blockCount = dataChunkSize / fmt.blockAlign; + if (pWav->translatedFormatTag == DR_WAVE_FORMAT_ADPCM) { + drwav_uint64 totalBlockHeaderSizeInBytes; + drwav_uint64 blockCount = dataChunkSize / fmt.blockAlign; if ((blockCount * fmt.blockAlign) < dataChunkSize) { blockCount += 1; } totalBlockHeaderSizeInBytes = blockCount * (6*fmt.channels); pWav->totalPCMFrameCount = ((dataChunkSize - totalBlockHeaderSizeInBytes) * 2) / fmt.channels; } - if (pWav->translatedFormatTag == MA_DR_WAVE_FORMAT_DVI_ADPCM) { - ma_uint64 totalBlockHeaderSizeInBytes; - ma_uint64 blockCount = dataChunkSize / fmt.blockAlign; + if (pWav->translatedFormatTag == DR_WAVE_FORMAT_DVI_ADPCM) { + drwav_uint64 totalBlockHeaderSizeInBytes; + drwav_uint64 blockCount = dataChunkSize / fmt.blockAlign; if ((blockCount * fmt.blockAlign) < dataChunkSize) { blockCount += 1; } @@ -79829,308 +78206,307 @@ MA_PRIVATE ma_bool32 ma_dr_wav_init__internal(ma_dr_wav* pWav, ma_dr_wav_chunk_p pWav->totalPCMFrameCount += blockCount; } } - if (pWav->translatedFormatTag == MA_DR_WAVE_FORMAT_ADPCM || pWav->translatedFormatTag == MA_DR_WAVE_FORMAT_DVI_ADPCM) { + if (pWav->translatedFormatTag == DR_WAVE_FORMAT_ADPCM || pWav->translatedFormatTag == DR_WAVE_FORMAT_DVI_ADPCM) { if (pWav->channels > 2) { - ma_dr_wav_free(pWav->pMetadata, &pWav->allocationCallbacks); - return MA_FALSE; + return DRWAV_FALSE; } } - if (ma_dr_wav_get_bytes_per_pcm_frame(pWav) == 0) { - ma_dr_wav_free(pWav->pMetadata, &pWav->allocationCallbacks); - return MA_FALSE; + if (drwav_get_bytes_per_pcm_frame(pWav) == 0) { + return DRWAV_FALSE; } -#ifdef MA_DR_WAV_LIBSNDFILE_COMPAT - if (pWav->translatedFormatTag == MA_DR_WAVE_FORMAT_ADPCM) { - ma_uint64 blockCount = dataChunkSize / fmt.blockAlign; +#ifdef DR_WAV_LIBSNDFILE_COMPAT + if (pWav->translatedFormatTag == DR_WAVE_FORMAT_ADPCM) { + drwav_uint64 blockCount = dataChunkSize / fmt.blockAlign; pWav->totalPCMFrameCount = (((blockCount * (fmt.blockAlign - (6*pWav->channels))) * 2)) / fmt.channels; } - if (pWav->translatedFormatTag == MA_DR_WAVE_FORMAT_DVI_ADPCM) { - ma_uint64 blockCount = dataChunkSize / fmt.blockAlign; + if (pWav->translatedFormatTag == DR_WAVE_FORMAT_DVI_ADPCM) { + drwav_uint64 blockCount = dataChunkSize / fmt.blockAlign; pWav->totalPCMFrameCount = (((blockCount * (fmt.blockAlign - (4*pWav->channels))) * 2) + (blockCount * pWav->channels)) / fmt.channels; } #endif - return MA_TRUE; + return DRWAV_TRUE; } -MA_API ma_bool32 ma_dr_wav_init(ma_dr_wav* pWav, ma_dr_wav_read_proc onRead, ma_dr_wav_seek_proc onSeek, void* pUserData, const ma_allocation_callbacks* pAllocationCallbacks) +DRWAV_API drwav_bool32 drwav_init(drwav* pWav, drwav_read_proc onRead, drwav_seek_proc onSeek, void* pUserData, const drwav_allocation_callbacks* pAllocationCallbacks) { - return ma_dr_wav_init_ex(pWav, onRead, onSeek, NULL, pUserData, NULL, 0, pAllocationCallbacks); + return drwav_init_ex(pWav, onRead, onSeek, NULL, pUserData, NULL, 0, pAllocationCallbacks); } -MA_API ma_bool32 ma_dr_wav_init_ex(ma_dr_wav* pWav, ma_dr_wav_read_proc onRead, ma_dr_wav_seek_proc onSeek, ma_dr_wav_chunk_proc onChunk, void* pReadSeekUserData, void* pChunkUserData, ma_uint32 flags, const ma_allocation_callbacks* pAllocationCallbacks) +DRWAV_API drwav_bool32 drwav_init_ex(drwav* pWav, drwav_read_proc onRead, drwav_seek_proc onSeek, drwav_chunk_proc onChunk, void* pReadSeekUserData, void* pChunkUserData, drwav_uint32 flags, const drwav_allocation_callbacks* pAllocationCallbacks) { - if (!ma_dr_wav_preinit(pWav, onRead, onSeek, pReadSeekUserData, pAllocationCallbacks)) { - return MA_FALSE; + if (!drwav_preinit(pWav, onRead, onSeek, pReadSeekUserData, pAllocationCallbacks)) { + return DRWAV_FALSE; } - return ma_dr_wav_init__internal(pWav, onChunk, pChunkUserData, flags); + return drwav_init__internal(pWav, onChunk, pChunkUserData, flags); } -MA_API ma_bool32 ma_dr_wav_init_with_metadata(ma_dr_wav* pWav, ma_dr_wav_read_proc onRead, ma_dr_wav_seek_proc onSeek, void* pUserData, ma_uint32 flags, const ma_allocation_callbacks* pAllocationCallbacks) +DRWAV_API drwav_bool32 drwav_init_with_metadata(drwav* pWav, drwav_read_proc onRead, drwav_seek_proc onSeek, void* pUserData, drwav_uint32 flags, const drwav_allocation_callbacks* pAllocationCallbacks) { - if (!ma_dr_wav_preinit(pWav, onRead, onSeek, pUserData, pAllocationCallbacks)) { - return MA_FALSE; + if (!drwav_preinit(pWav, onRead, onSeek, pUserData, pAllocationCallbacks)) { + return DRWAV_FALSE; } - return ma_dr_wav_init__internal(pWav, NULL, NULL, flags | MA_DR_WAV_WITH_METADATA); + pWav->allowedMetadataTypes = drwav_metadata_type_all_including_unknown; + return drwav_init__internal(pWav, NULL, NULL, flags); } -MA_API ma_dr_wav_metadata* ma_dr_wav_take_ownership_of_metadata(ma_dr_wav* pWav) +DRWAV_API drwav_metadata* drwav_take_ownership_of_metadata(drwav* pWav) { - ma_dr_wav_metadata *result = pWav->pMetadata; + drwav_metadata *result = pWav->pMetadata; pWav->pMetadata = NULL; pWav->metadataCount = 0; return result; } -MA_PRIVATE size_t ma_dr_wav__write(ma_dr_wav* pWav, const void* pData, size_t dataSize) +DRWAV_PRIVATE size_t drwav__write(drwav* pWav, const void* pData, size_t dataSize) { - MA_DR_WAV_ASSERT(pWav != NULL); - MA_DR_WAV_ASSERT(pWav->onWrite != NULL); + DRWAV_ASSERT(pWav != NULL); + DRWAV_ASSERT(pWav->onWrite != NULL); return pWav->onWrite(pWav->pUserData, pData, dataSize); } -MA_PRIVATE size_t ma_dr_wav__write_byte(ma_dr_wav* pWav, ma_uint8 byte) +DRWAV_PRIVATE size_t drwav__write_byte(drwav* pWav, drwav_uint8 byte) { - MA_DR_WAV_ASSERT(pWav != NULL); - MA_DR_WAV_ASSERT(pWav->onWrite != NULL); + DRWAV_ASSERT(pWav != NULL); + DRWAV_ASSERT(pWav->onWrite != NULL); return pWav->onWrite(pWav->pUserData, &byte, 1); } -MA_PRIVATE size_t ma_dr_wav__write_u16ne_to_le(ma_dr_wav* pWav, ma_uint16 value) +DRWAV_PRIVATE size_t drwav__write_u16ne_to_le(drwav* pWav, drwav_uint16 value) { - MA_DR_WAV_ASSERT(pWav != NULL); - MA_DR_WAV_ASSERT(pWav->onWrite != NULL); - if (!ma_dr_wav__is_little_endian()) { - value = ma_dr_wav__bswap16(value); + DRWAV_ASSERT(pWav != NULL); + DRWAV_ASSERT(pWav->onWrite != NULL); + if (!drwav__is_little_endian()) { + value = drwav__bswap16(value); } - return ma_dr_wav__write(pWav, &value, 2); + return drwav__write(pWav, &value, 2); } -MA_PRIVATE size_t ma_dr_wav__write_u32ne_to_le(ma_dr_wav* pWav, ma_uint32 value) +DRWAV_PRIVATE size_t drwav__write_u32ne_to_le(drwav* pWav, drwav_uint32 value) { - MA_DR_WAV_ASSERT(pWav != NULL); - MA_DR_WAV_ASSERT(pWav->onWrite != NULL); - if (!ma_dr_wav__is_little_endian()) { - value = ma_dr_wav__bswap32(value); + DRWAV_ASSERT(pWav != NULL); + DRWAV_ASSERT(pWav->onWrite != NULL); + if (!drwav__is_little_endian()) { + value = drwav__bswap32(value); } - return ma_dr_wav__write(pWav, &value, 4); + return drwav__write(pWav, &value, 4); } -MA_PRIVATE size_t ma_dr_wav__write_u64ne_to_le(ma_dr_wav* pWav, ma_uint64 value) +DRWAV_PRIVATE size_t drwav__write_u64ne_to_le(drwav* pWav, drwav_uint64 value) { - MA_DR_WAV_ASSERT(pWav != NULL); - MA_DR_WAV_ASSERT(pWav->onWrite != NULL); - if (!ma_dr_wav__is_little_endian()) { - value = ma_dr_wav__bswap64(value); + DRWAV_ASSERT(pWav != NULL); + DRWAV_ASSERT(pWav->onWrite != NULL); + if (!drwav__is_little_endian()) { + value = drwav__bswap64(value); } - return ma_dr_wav__write(pWav, &value, 8); + return drwav__write(pWav, &value, 8); } -MA_PRIVATE size_t ma_dr_wav__write_f32ne_to_le(ma_dr_wav* pWav, float value) +DRWAV_PRIVATE size_t drwav__write_f32ne_to_le(drwav* pWav, float value) { union { - ma_uint32 u32; + drwav_uint32 u32; float f32; } u; - MA_DR_WAV_ASSERT(pWav != NULL); - MA_DR_WAV_ASSERT(pWav->onWrite != NULL); + DRWAV_ASSERT(pWav != NULL); + DRWAV_ASSERT(pWav->onWrite != NULL); u.f32 = value; - if (!ma_dr_wav__is_little_endian()) { - u.u32 = ma_dr_wav__bswap32(u.u32); + if (!drwav__is_little_endian()) { + u.u32 = drwav__bswap32(u.u32); } - return ma_dr_wav__write(pWav, &u.u32, 4); + return drwav__write(pWav, &u.u32, 4); } -MA_PRIVATE size_t ma_dr_wav__write_or_count(ma_dr_wav* pWav, const void* pData, size_t dataSize) +DRWAV_PRIVATE size_t drwav__write_or_count(drwav* pWav, const void* pData, size_t dataSize) { if (pWav == NULL) { return dataSize; } - return ma_dr_wav__write(pWav, pData, dataSize); + return drwav__write(pWav, pData, dataSize); } -MA_PRIVATE size_t ma_dr_wav__write_or_count_byte(ma_dr_wav* pWav, ma_uint8 byte) +DRWAV_PRIVATE size_t drwav__write_or_count_byte(drwav* pWav, drwav_uint8 byte) { if (pWav == NULL) { return 1; } - return ma_dr_wav__write_byte(pWav, byte); + return drwav__write_byte(pWav, byte); } -MA_PRIVATE size_t ma_dr_wav__write_or_count_u16ne_to_le(ma_dr_wav* pWav, ma_uint16 value) +DRWAV_PRIVATE size_t drwav__write_or_count_u16ne_to_le(drwav* pWav, drwav_uint16 value) { if (pWav == NULL) { return 2; } - return ma_dr_wav__write_u16ne_to_le(pWav, value); + return drwav__write_u16ne_to_le(pWav, value); } -MA_PRIVATE size_t ma_dr_wav__write_or_count_u32ne_to_le(ma_dr_wav* pWav, ma_uint32 value) +DRWAV_PRIVATE size_t drwav__write_or_count_u32ne_to_le(drwav* pWav, drwav_uint32 value) { if (pWav == NULL) { return 4; } - return ma_dr_wav__write_u32ne_to_le(pWav, value); + return drwav__write_u32ne_to_le(pWav, value); } #if 0 -MA_PRIVATE size_t ma_dr_wav__write_or_count_u64ne_to_le(ma_dr_wav* pWav, ma_uint64 value) +DRWAV_PRIVATE size_t drwav__write_or_count_u64ne_to_le(drwav* pWav, drwav_uint64 value) { if (pWav == NULL) { return 8; } - return ma_dr_wav__write_u64ne_to_le(pWav, value); + return drwav__write_u64ne_to_le(pWav, value); } #endif -MA_PRIVATE size_t ma_dr_wav__write_or_count_f32ne_to_le(ma_dr_wav* pWav, float value) +DRWAV_PRIVATE size_t drwav__write_or_count_f32ne_to_le(drwav* pWav, float value) { if (pWav == NULL) { return 4; } - return ma_dr_wav__write_f32ne_to_le(pWav, value); + return drwav__write_f32ne_to_le(pWav, value); } -MA_PRIVATE size_t ma_dr_wav__write_or_count_string_to_fixed_size_buf(ma_dr_wav* pWav, char* str, size_t bufFixedSize) +DRWAV_PRIVATE size_t drwav__write_or_count_string_to_fixed_size_buf(drwav* pWav, char* str, size_t bufFixedSize) { size_t len; if (pWav == NULL) { return bufFixedSize; } - len = ma_dr_wav__strlen_clamped(str, bufFixedSize); - ma_dr_wav__write_or_count(pWav, str, len); + len = drwav__strlen_clamped(str, bufFixedSize); + drwav__write_or_count(pWav, str, len); if (len < bufFixedSize) { size_t i; for (i = 0; i < bufFixedSize - len; ++i) { - ma_dr_wav__write_byte(pWav, 0); + drwav__write_byte(pWav, 0); } } return bufFixedSize; } -MA_PRIVATE size_t ma_dr_wav__write_or_count_metadata(ma_dr_wav* pWav, ma_dr_wav_metadata* pMetadatas, ma_uint32 metadataCount) +DRWAV_PRIVATE size_t drwav__write_or_count_metadata(drwav* pWav, drwav_metadata* pMetadatas, drwav_uint32 metadataCount) { size_t bytesWritten = 0; - ma_bool32 hasListAdtl = MA_FALSE; - ma_bool32 hasListInfo = MA_FALSE; - ma_uint32 iMetadata; + drwav_bool32 hasListAdtl = DRWAV_FALSE; + drwav_bool32 hasListInfo = DRWAV_FALSE; + drwav_uint32 iMetadata; if (pMetadatas == NULL || metadataCount == 0) { return 0; } for (iMetadata = 0; iMetadata < metadataCount; ++iMetadata) { - ma_dr_wav_metadata* pMetadata = &pMetadatas[iMetadata]; - ma_uint32 chunkSize = 0; - if ((pMetadata->type & ma_dr_wav_metadata_type_list_all_info_strings) || (pMetadata->type == ma_dr_wav_metadata_type_unknown && pMetadata->data.unknown.chunkLocation == ma_dr_wav_metadata_location_inside_info_list)) { - hasListInfo = MA_TRUE; + drwav_metadata* pMetadata = &pMetadatas[iMetadata]; + drwav_uint32 chunkSize = 0; + if ((pMetadata->type & drwav_metadata_type_list_all_info_strings) || (pMetadata->type == drwav_metadata_type_unknown && pMetadata->data.unknown.chunkLocation == drwav_metadata_location_inside_info_list)) { + hasListInfo = DRWAV_TRUE; } - if ((pMetadata->type & ma_dr_wav_metadata_type_list_all_adtl) || (pMetadata->type == ma_dr_wav_metadata_type_unknown && pMetadata->data.unknown.chunkLocation == ma_dr_wav_metadata_location_inside_adtl_list)) { - hasListAdtl = MA_TRUE; + if ((pMetadata->type & drwav_metadata_type_list_all_adtl) || (pMetadata->type == drwav_metadata_type_unknown && pMetadata->data.unknown.chunkLocation == drwav_metadata_location_inside_adtl_list)) { + hasListAdtl = DRWAV_TRUE; } switch (pMetadata->type) { - case ma_dr_wav_metadata_type_smpl: + case drwav_metadata_type_smpl: { - ma_uint32 iLoop; - chunkSize = MA_DR_WAV_SMPL_BYTES + MA_DR_WAV_SMPL_LOOP_BYTES * pMetadata->data.smpl.sampleLoopCount + pMetadata->data.smpl.samplerSpecificDataSizeInBytes; - bytesWritten += ma_dr_wav__write_or_count(pWav, "smpl", 4); - bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, chunkSize); - bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, pMetadata->data.smpl.manufacturerId); - bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, pMetadata->data.smpl.productId); - bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, pMetadata->data.smpl.samplePeriodNanoseconds); - bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, pMetadata->data.smpl.midiUnityNote); - bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, pMetadata->data.smpl.midiPitchFraction); - bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, pMetadata->data.smpl.smpteFormat); - bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, pMetadata->data.smpl.smpteOffset); - bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, pMetadata->data.smpl.sampleLoopCount); - bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, pMetadata->data.smpl.samplerSpecificDataSizeInBytes); + drwav_uint32 iLoop; + chunkSize = DRWAV_SMPL_BYTES + DRWAV_SMPL_LOOP_BYTES * pMetadata->data.smpl.sampleLoopCount + pMetadata->data.smpl.samplerSpecificDataSizeInBytes; + bytesWritten += drwav__write_or_count(pWav, "smpl", 4); + bytesWritten += drwav__write_or_count_u32ne_to_le(pWav, chunkSize); + bytesWritten += drwav__write_or_count_u32ne_to_le(pWav, pMetadata->data.smpl.manufacturerId); + bytesWritten += drwav__write_or_count_u32ne_to_le(pWav, pMetadata->data.smpl.productId); + bytesWritten += drwav__write_or_count_u32ne_to_le(pWav, pMetadata->data.smpl.samplePeriodNanoseconds); + bytesWritten += drwav__write_or_count_u32ne_to_le(pWav, pMetadata->data.smpl.midiUnityNote); + bytesWritten += drwav__write_or_count_u32ne_to_le(pWav, pMetadata->data.smpl.midiPitchFraction); + bytesWritten += drwav__write_or_count_u32ne_to_le(pWav, pMetadata->data.smpl.smpteFormat); + bytesWritten += drwav__write_or_count_u32ne_to_le(pWav, pMetadata->data.smpl.smpteOffset); + bytesWritten += drwav__write_or_count_u32ne_to_le(pWav, pMetadata->data.smpl.sampleLoopCount); + bytesWritten += drwav__write_or_count_u32ne_to_le(pWav, pMetadata->data.smpl.samplerSpecificDataSizeInBytes); for (iLoop = 0; iLoop < pMetadata->data.smpl.sampleLoopCount; ++iLoop) { - bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, pMetadata->data.smpl.pLoops[iLoop].cuePointId); - bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, pMetadata->data.smpl.pLoops[iLoop].type); - bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, pMetadata->data.smpl.pLoops[iLoop].firstSampleByteOffset); - bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, pMetadata->data.smpl.pLoops[iLoop].lastSampleByteOffset); - bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, pMetadata->data.smpl.pLoops[iLoop].sampleFraction); - bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, pMetadata->data.smpl.pLoops[iLoop].playCount); + bytesWritten += drwav__write_or_count_u32ne_to_le(pWav, pMetadata->data.smpl.pLoops[iLoop].cuePointId); + bytesWritten += drwav__write_or_count_u32ne_to_le(pWav, pMetadata->data.smpl.pLoops[iLoop].type); + bytesWritten += drwav__write_or_count_u32ne_to_le(pWav, pMetadata->data.smpl.pLoops[iLoop].firstSampleByteOffset); + bytesWritten += drwav__write_or_count_u32ne_to_le(pWav, pMetadata->data.smpl.pLoops[iLoop].lastSampleByteOffset); + bytesWritten += drwav__write_or_count_u32ne_to_le(pWav, pMetadata->data.smpl.pLoops[iLoop].sampleFraction); + bytesWritten += drwav__write_or_count_u32ne_to_le(pWav, pMetadata->data.smpl.pLoops[iLoop].playCount); } if (pMetadata->data.smpl.samplerSpecificDataSizeInBytes > 0) { - bytesWritten += ma_dr_wav__write_or_count(pWav, pMetadata->data.smpl.pSamplerSpecificData, pMetadata->data.smpl.samplerSpecificDataSizeInBytes); + bytesWritten += drwav__write_or_count(pWav, pMetadata->data.smpl.pSamplerSpecificData, pMetadata->data.smpl.samplerSpecificDataSizeInBytes); } } break; - case ma_dr_wav_metadata_type_inst: + case drwav_metadata_type_inst: { - chunkSize = MA_DR_WAV_INST_BYTES; - bytesWritten += ma_dr_wav__write_or_count(pWav, "inst", 4); - bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, chunkSize); - bytesWritten += ma_dr_wav__write_or_count(pWav, &pMetadata->data.inst.midiUnityNote, 1); - bytesWritten += ma_dr_wav__write_or_count(pWav, &pMetadata->data.inst.fineTuneCents, 1); - bytesWritten += ma_dr_wav__write_or_count(pWav, &pMetadata->data.inst.gainDecibels, 1); - bytesWritten += ma_dr_wav__write_or_count(pWav, &pMetadata->data.inst.lowNote, 1); - bytesWritten += ma_dr_wav__write_or_count(pWav, &pMetadata->data.inst.highNote, 1); - bytesWritten += ma_dr_wav__write_or_count(pWav, &pMetadata->data.inst.lowVelocity, 1); - bytesWritten += ma_dr_wav__write_or_count(pWav, &pMetadata->data.inst.highVelocity, 1); + chunkSize = DRWAV_INST_BYTES; + bytesWritten += drwav__write_or_count(pWav, "inst", 4); + bytesWritten += drwav__write_or_count_u32ne_to_le(pWav, chunkSize); + bytesWritten += drwav__write_or_count(pWav, &pMetadata->data.inst.midiUnityNote, 1); + bytesWritten += drwav__write_or_count(pWav, &pMetadata->data.inst.fineTuneCents, 1); + bytesWritten += drwav__write_or_count(pWav, &pMetadata->data.inst.gainDecibels, 1); + bytesWritten += drwav__write_or_count(pWav, &pMetadata->data.inst.lowNote, 1); + bytesWritten += drwav__write_or_count(pWav, &pMetadata->data.inst.highNote, 1); + bytesWritten += drwav__write_or_count(pWav, &pMetadata->data.inst.lowVelocity, 1); + bytesWritten += drwav__write_or_count(pWav, &pMetadata->data.inst.highVelocity, 1); } break; - case ma_dr_wav_metadata_type_cue: + case drwav_metadata_type_cue: { - ma_uint32 iCuePoint; - chunkSize = MA_DR_WAV_CUE_BYTES + MA_DR_WAV_CUE_POINT_BYTES * pMetadata->data.cue.cuePointCount; - bytesWritten += ma_dr_wav__write_or_count(pWav, "cue ", 4); - bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, chunkSize); - bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, pMetadata->data.cue.cuePointCount); + drwav_uint32 iCuePoint; + chunkSize = DRWAV_CUE_BYTES + DRWAV_CUE_POINT_BYTES * pMetadata->data.cue.cuePointCount; + bytesWritten += drwav__write_or_count(pWav, "cue ", 4); + bytesWritten += drwav__write_or_count_u32ne_to_le(pWav, chunkSize); + bytesWritten += drwav__write_or_count_u32ne_to_le(pWav, pMetadata->data.cue.cuePointCount); for (iCuePoint = 0; iCuePoint < pMetadata->data.cue.cuePointCount; ++iCuePoint) { - bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, pMetadata->data.cue.pCuePoints[iCuePoint].id); - bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, pMetadata->data.cue.pCuePoints[iCuePoint].playOrderPosition); - bytesWritten += ma_dr_wav__write_or_count(pWav, pMetadata->data.cue.pCuePoints[iCuePoint].dataChunkId, 4); - bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, pMetadata->data.cue.pCuePoints[iCuePoint].chunkStart); - bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, pMetadata->data.cue.pCuePoints[iCuePoint].blockStart); - bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, pMetadata->data.cue.pCuePoints[iCuePoint].sampleByteOffset); + bytesWritten += drwav__write_or_count_u32ne_to_le(pWav, pMetadata->data.cue.pCuePoints[iCuePoint].id); + bytesWritten += drwav__write_or_count_u32ne_to_le(pWav, pMetadata->data.cue.pCuePoints[iCuePoint].playOrderPosition); + bytesWritten += drwav__write_or_count(pWav, pMetadata->data.cue.pCuePoints[iCuePoint].dataChunkId, 4); + bytesWritten += drwav__write_or_count_u32ne_to_le(pWav, pMetadata->data.cue.pCuePoints[iCuePoint].chunkStart); + bytesWritten += drwav__write_or_count_u32ne_to_le(pWav, pMetadata->data.cue.pCuePoints[iCuePoint].blockStart); + bytesWritten += drwav__write_or_count_u32ne_to_le(pWav, pMetadata->data.cue.pCuePoints[iCuePoint].sampleByteOffset); } } break; - case ma_dr_wav_metadata_type_acid: + case drwav_metadata_type_acid: { - chunkSize = MA_DR_WAV_ACID_BYTES; - bytesWritten += ma_dr_wav__write_or_count(pWav, "acid", 4); - bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, chunkSize); - bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, pMetadata->data.acid.flags); - bytesWritten += ma_dr_wav__write_or_count_u16ne_to_le(pWav, pMetadata->data.acid.midiUnityNote); - bytesWritten += ma_dr_wav__write_or_count_u16ne_to_le(pWav, pMetadata->data.acid.reserved1); - bytesWritten += ma_dr_wav__write_or_count_f32ne_to_le(pWav, pMetadata->data.acid.reserved2); - bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, pMetadata->data.acid.numBeats); - bytesWritten += ma_dr_wav__write_or_count_u16ne_to_le(pWav, pMetadata->data.acid.meterDenominator); - bytesWritten += ma_dr_wav__write_or_count_u16ne_to_le(pWav, pMetadata->data.acid.meterNumerator); - bytesWritten += ma_dr_wav__write_or_count_f32ne_to_le(pWav, pMetadata->data.acid.tempo); + chunkSize = DRWAV_ACID_BYTES; + bytesWritten += drwav__write_or_count(pWav, "acid", 4); + bytesWritten += drwav__write_or_count_u32ne_to_le(pWav, chunkSize); + bytesWritten += drwav__write_or_count_u32ne_to_le(pWav, pMetadata->data.acid.flags); + bytesWritten += drwav__write_or_count_u16ne_to_le(pWav, pMetadata->data.acid.midiUnityNote); + bytesWritten += drwav__write_or_count_u16ne_to_le(pWav, pMetadata->data.acid.reserved1); + bytesWritten += drwav__write_or_count_f32ne_to_le(pWav, pMetadata->data.acid.reserved2); + bytesWritten += drwav__write_or_count_u32ne_to_le(pWav, pMetadata->data.acid.numBeats); + bytesWritten += drwav__write_or_count_u16ne_to_le(pWav, pMetadata->data.acid.meterDenominator); + bytesWritten += drwav__write_or_count_u16ne_to_le(pWav, pMetadata->data.acid.meterNumerator); + bytesWritten += drwav__write_or_count_f32ne_to_le(pWav, pMetadata->data.acid.tempo); } break; - case ma_dr_wav_metadata_type_bext: + case drwav_metadata_type_bext: { - char reservedBuf[MA_DR_WAV_BEXT_RESERVED_BYTES]; - ma_uint32 timeReferenceLow; - ma_uint32 timeReferenceHigh; - chunkSize = MA_DR_WAV_BEXT_BYTES + pMetadata->data.bext.codingHistorySize; - bytesWritten += ma_dr_wav__write_or_count(pWav, "bext", 4); - bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, chunkSize); - bytesWritten += ma_dr_wav__write_or_count_string_to_fixed_size_buf(pWav, pMetadata->data.bext.pDescription, MA_DR_WAV_BEXT_DESCRIPTION_BYTES); - bytesWritten += ma_dr_wav__write_or_count_string_to_fixed_size_buf(pWav, pMetadata->data.bext.pOriginatorName, MA_DR_WAV_BEXT_ORIGINATOR_NAME_BYTES); - bytesWritten += ma_dr_wav__write_or_count_string_to_fixed_size_buf(pWav, pMetadata->data.bext.pOriginatorReference, MA_DR_WAV_BEXT_ORIGINATOR_REF_BYTES); - bytesWritten += ma_dr_wav__write_or_count(pWav, pMetadata->data.bext.pOriginationDate, sizeof(pMetadata->data.bext.pOriginationDate)); - bytesWritten += ma_dr_wav__write_or_count(pWav, pMetadata->data.bext.pOriginationTime, sizeof(pMetadata->data.bext.pOriginationTime)); - timeReferenceLow = (ma_uint32)(pMetadata->data.bext.timeReference & 0xFFFFFFFF); - timeReferenceHigh = (ma_uint32)(pMetadata->data.bext.timeReference >> 32); - bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, timeReferenceLow); - bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, timeReferenceHigh); - bytesWritten += ma_dr_wav__write_or_count_u16ne_to_le(pWav, pMetadata->data.bext.version); - bytesWritten += ma_dr_wav__write_or_count(pWav, pMetadata->data.bext.pUMID, MA_DR_WAV_BEXT_UMID_BYTES); - bytesWritten += ma_dr_wav__write_or_count_u16ne_to_le(pWav, pMetadata->data.bext.loudnessValue); - bytesWritten += ma_dr_wav__write_or_count_u16ne_to_le(pWav, pMetadata->data.bext.loudnessRange); - bytesWritten += ma_dr_wav__write_or_count_u16ne_to_le(pWav, pMetadata->data.bext.maxTruePeakLevel); - bytesWritten += ma_dr_wav__write_or_count_u16ne_to_le(pWav, pMetadata->data.bext.maxMomentaryLoudness); - bytesWritten += ma_dr_wav__write_or_count_u16ne_to_le(pWav, pMetadata->data.bext.maxShortTermLoudness); - MA_DR_WAV_ZERO_MEMORY(reservedBuf, sizeof(reservedBuf)); - bytesWritten += ma_dr_wav__write_or_count(pWav, reservedBuf, sizeof(reservedBuf)); + char reservedBuf[DRWAV_BEXT_RESERVED_BYTES]; + drwav_uint32 timeReferenceLow; + drwav_uint32 timeReferenceHigh; + chunkSize = DRWAV_BEXT_BYTES + pMetadata->data.bext.codingHistorySize; + bytesWritten += drwav__write_or_count(pWav, "bext", 4); + bytesWritten += drwav__write_or_count_u32ne_to_le(pWav, chunkSize); + bytesWritten += drwav__write_or_count_string_to_fixed_size_buf(pWav, pMetadata->data.bext.pDescription, DRWAV_BEXT_DESCRIPTION_BYTES); + bytesWritten += drwav__write_or_count_string_to_fixed_size_buf(pWav, pMetadata->data.bext.pOriginatorName, DRWAV_BEXT_ORIGINATOR_NAME_BYTES); + bytesWritten += drwav__write_or_count_string_to_fixed_size_buf(pWav, pMetadata->data.bext.pOriginatorReference, DRWAV_BEXT_ORIGINATOR_REF_BYTES); + bytesWritten += drwav__write_or_count(pWav, pMetadata->data.bext.pOriginationDate, sizeof(pMetadata->data.bext.pOriginationDate)); + bytesWritten += drwav__write_or_count(pWav, pMetadata->data.bext.pOriginationTime, sizeof(pMetadata->data.bext.pOriginationTime)); + timeReferenceLow = (drwav_uint32)(pMetadata->data.bext.timeReference & 0xFFFFFFFF); + timeReferenceHigh = (drwav_uint32)(pMetadata->data.bext.timeReference >> 32); + bytesWritten += drwav__write_or_count_u32ne_to_le(pWav, timeReferenceLow); + bytesWritten += drwav__write_or_count_u32ne_to_le(pWav, timeReferenceHigh); + bytesWritten += drwav__write_or_count_u16ne_to_le(pWav, pMetadata->data.bext.version); + bytesWritten += drwav__write_or_count(pWav, pMetadata->data.bext.pUMID, DRWAV_BEXT_UMID_BYTES); + bytesWritten += drwav__write_or_count_u16ne_to_le(pWav, pMetadata->data.bext.loudnessValue); + bytesWritten += drwav__write_or_count_u16ne_to_le(pWav, pMetadata->data.bext.loudnessRange); + bytesWritten += drwav__write_or_count_u16ne_to_le(pWav, pMetadata->data.bext.maxTruePeakLevel); + bytesWritten += drwav__write_or_count_u16ne_to_le(pWav, pMetadata->data.bext.maxMomentaryLoudness); + bytesWritten += drwav__write_or_count_u16ne_to_le(pWav, pMetadata->data.bext.maxShortTermLoudness); + DRWAV_ZERO_MEMORY(reservedBuf, sizeof(reservedBuf)); + bytesWritten += drwav__write_or_count(pWav, reservedBuf, sizeof(reservedBuf)); if (pMetadata->data.bext.codingHistorySize > 0) { - bytesWritten += ma_dr_wav__write_or_count(pWav, pMetadata->data.bext.pCodingHistory, pMetadata->data.bext.codingHistorySize); + bytesWritten += drwav__write_or_count(pWav, pMetadata->data.bext.pCodingHistory, pMetadata->data.bext.codingHistorySize); } } break; - case ma_dr_wav_metadata_type_unknown: + case drwav_metadata_type_unknown: { - if (pMetadata->data.unknown.chunkLocation == ma_dr_wav_metadata_location_top_level) { + if (pMetadata->data.unknown.chunkLocation == drwav_metadata_location_top_level) { chunkSize = pMetadata->data.unknown.dataSizeInBytes; - bytesWritten += ma_dr_wav__write_or_count(pWav, pMetadata->data.unknown.id, 4); - bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, chunkSize); - bytesWritten += ma_dr_wav__write_or_count(pWav, pMetadata->data.unknown.pData, pMetadata->data.unknown.dataSizeInBytes); + bytesWritten += drwav__write_or_count(pWav, pMetadata->data.unknown.id, 4); + bytesWritten += drwav__write_or_count_u32ne_to_le(pWav, chunkSize); + bytesWritten += drwav__write_or_count(pWav, pMetadata->data.unknown.pData, pMetadata->data.unknown.dataSizeInBytes); } } break; default: break; } if ((chunkSize % 2) != 0) { - bytesWritten += ma_dr_wav__write_or_count_byte(pWav, 0); + bytesWritten += drwav__write_or_count_byte(pWav, 0); } } if (hasListInfo) { - ma_uint32 chunkSize = 4; + drwav_uint32 chunkSize = 4; for (iMetadata = 0; iMetadata < metadataCount; ++iMetadata) { - ma_dr_wav_metadata* pMetadata = &pMetadatas[iMetadata]; - if ((pMetadata->type & ma_dr_wav_metadata_type_list_all_info_strings)) { + drwav_metadata* pMetadata = &pMetadatas[iMetadata]; + if ((pMetadata->type & drwav_metadata_type_list_all_info_strings)) { chunkSize += 8; chunkSize += pMetadata->data.infoText.stringLength + 1; - } else if (pMetadata->type == ma_dr_wav_metadata_type_unknown && pMetadata->data.unknown.chunkLocation == ma_dr_wav_metadata_location_inside_info_list) { + } else if (pMetadata->type == drwav_metadata_type_unknown && pMetadata->data.unknown.chunkLocation == drwav_metadata_location_inside_info_list) { chunkSize += 8; chunkSize += pMetadata->data.unknown.dataSizeInBytes; } @@ -80138,73 +78514,73 @@ MA_PRIVATE size_t ma_dr_wav__write_or_count_metadata(ma_dr_wav* pWav, ma_dr_wav_ chunkSize += 1; } } - bytesWritten += ma_dr_wav__write_or_count(pWav, "LIST", 4); - bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, chunkSize); - bytesWritten += ma_dr_wav__write_or_count(pWav, "INFO", 4); + bytesWritten += drwav__write_or_count(pWav, "LIST", 4); + bytesWritten += drwav__write_or_count_u32ne_to_le(pWav, chunkSize); + bytesWritten += drwav__write_or_count(pWav, "INFO", 4); for (iMetadata = 0; iMetadata < metadataCount; ++iMetadata) { - ma_dr_wav_metadata* pMetadata = &pMetadatas[iMetadata]; - ma_uint32 subchunkSize = 0; - if (pMetadata->type & ma_dr_wav_metadata_type_list_all_info_strings) { + drwav_metadata* pMetadata = &pMetadatas[iMetadata]; + drwav_uint32 subchunkSize = 0; + if (pMetadata->type & drwav_metadata_type_list_all_info_strings) { const char* pID = NULL; switch (pMetadata->type) { - case ma_dr_wav_metadata_type_list_info_software: pID = "ISFT"; break; - case ma_dr_wav_metadata_type_list_info_copyright: pID = "ICOP"; break; - case ma_dr_wav_metadata_type_list_info_title: pID = "INAM"; break; - case ma_dr_wav_metadata_type_list_info_artist: pID = "IART"; break; - case ma_dr_wav_metadata_type_list_info_comment: pID = "ICMT"; break; - case ma_dr_wav_metadata_type_list_info_date: pID = "ICRD"; break; - case ma_dr_wav_metadata_type_list_info_genre: pID = "IGNR"; break; - case ma_dr_wav_metadata_type_list_info_album: pID = "IPRD"; break; - case ma_dr_wav_metadata_type_list_info_tracknumber: pID = "ITRK"; break; + case drwav_metadata_type_list_info_software: pID = "ISFT"; break; + case drwav_metadata_type_list_info_copyright: pID = "ICOP"; break; + case drwav_metadata_type_list_info_title: pID = "INAM"; break; + case drwav_metadata_type_list_info_artist: pID = "IART"; break; + case drwav_metadata_type_list_info_comment: pID = "ICMT"; break; + case drwav_metadata_type_list_info_date: pID = "ICRD"; break; + case drwav_metadata_type_list_info_genre: pID = "IGNR"; break; + case drwav_metadata_type_list_info_album: pID = "IPRD"; break; + case drwav_metadata_type_list_info_tracknumber: pID = "ITRK"; break; default: break; } - MA_DR_WAV_ASSERT(pID != NULL); + DRWAV_ASSERT(pID != NULL); if (pMetadata->data.infoText.stringLength) { subchunkSize = pMetadata->data.infoText.stringLength + 1; - bytesWritten += ma_dr_wav__write_or_count(pWav, pID, 4); - bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, subchunkSize); - bytesWritten += ma_dr_wav__write_or_count(pWav, pMetadata->data.infoText.pString, pMetadata->data.infoText.stringLength); - bytesWritten += ma_dr_wav__write_or_count_byte(pWav, '\0'); + bytesWritten += drwav__write_or_count(pWav, pID, 4); + bytesWritten += drwav__write_or_count_u32ne_to_le(pWav, subchunkSize); + bytesWritten += drwav__write_or_count(pWav, pMetadata->data.infoText.pString, pMetadata->data.infoText.stringLength); + bytesWritten += drwav__write_or_count_byte(pWav, '\0'); } - } else if (pMetadata->type == ma_dr_wav_metadata_type_unknown && pMetadata->data.unknown.chunkLocation == ma_dr_wav_metadata_location_inside_info_list) { + } else if (pMetadata->type == drwav_metadata_type_unknown && pMetadata->data.unknown.chunkLocation == drwav_metadata_location_inside_info_list) { if (pMetadata->data.unknown.dataSizeInBytes) { subchunkSize = pMetadata->data.unknown.dataSizeInBytes; - bytesWritten += ma_dr_wav__write_or_count(pWav, pMetadata->data.unknown.id, 4); - bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, pMetadata->data.unknown.dataSizeInBytes); - bytesWritten += ma_dr_wav__write_or_count(pWav, pMetadata->data.unknown.pData, subchunkSize); + bytesWritten += drwav__write_or_count(pWav, pMetadata->data.unknown.id, 4); + bytesWritten += drwav__write_or_count_u32ne_to_le(pWav, pMetadata->data.unknown.dataSizeInBytes); + bytesWritten += drwav__write_or_count(pWav, pMetadata->data.unknown.pData, subchunkSize); } } if ((subchunkSize % 2) != 0) { - bytesWritten += ma_dr_wav__write_or_count_byte(pWav, 0); + bytesWritten += drwav__write_or_count_byte(pWav, 0); } } } if (hasListAdtl) { - ma_uint32 chunkSize = 4; + drwav_uint32 chunkSize = 4; for (iMetadata = 0; iMetadata < metadataCount; ++iMetadata) { - ma_dr_wav_metadata* pMetadata = &pMetadatas[iMetadata]; + drwav_metadata* pMetadata = &pMetadatas[iMetadata]; switch (pMetadata->type) { - case ma_dr_wav_metadata_type_list_label: - case ma_dr_wav_metadata_type_list_note: + case drwav_metadata_type_list_label: + case drwav_metadata_type_list_note: { chunkSize += 8; - chunkSize += MA_DR_WAV_LIST_LABEL_OR_NOTE_BYTES; + chunkSize += DRWAV_LIST_LABEL_OR_NOTE_BYTES; if (pMetadata->data.labelOrNote.stringLength > 0) { chunkSize += pMetadata->data.labelOrNote.stringLength + 1; } } break; - case ma_dr_wav_metadata_type_list_labelled_cue_region: + case drwav_metadata_type_list_labelled_cue_region: { chunkSize += 8; - chunkSize += MA_DR_WAV_LIST_LABELLED_TEXT_BYTES; + chunkSize += DRWAV_LIST_LABELLED_TEXT_BYTES; if (pMetadata->data.labelledCueRegion.stringLength > 0) { chunkSize += pMetadata->data.labelledCueRegion.stringLength + 1; } } break; - case ma_dr_wav_metadata_type_unknown: + case drwav_metadata_type_unknown: { - if (pMetadata->data.unknown.chunkLocation == ma_dr_wav_metadata_location_inside_adtl_list) { + if (pMetadata->data.unknown.chunkLocation == drwav_metadata_location_inside_adtl_list) { chunkSize += 8; chunkSize += pMetadata->data.unknown.dataSizeInBytes; } @@ -80215,457 +78591,968 @@ MA_PRIVATE size_t ma_dr_wav__write_or_count_metadata(ma_dr_wav* pWav, ma_dr_wav_ chunkSize += 1; } } - bytesWritten += ma_dr_wav__write_or_count(pWav, "LIST", 4); - bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, chunkSize); - bytesWritten += ma_dr_wav__write_or_count(pWav, "adtl", 4); + bytesWritten += drwav__write_or_count(pWav, "LIST", 4); + bytesWritten += drwav__write_or_count_u32ne_to_le(pWav, chunkSize); + bytesWritten += drwav__write_or_count(pWav, "adtl", 4); for (iMetadata = 0; iMetadata < metadataCount; ++iMetadata) { - ma_dr_wav_metadata* pMetadata = &pMetadatas[iMetadata]; - ma_uint32 subchunkSize = 0; + drwav_metadata* pMetadata = &pMetadatas[iMetadata]; + drwav_uint32 subchunkSize = 0; switch (pMetadata->type) { - case ma_dr_wav_metadata_type_list_label: - case ma_dr_wav_metadata_type_list_note: + case drwav_metadata_type_list_label: + case drwav_metadata_type_list_note: { if (pMetadata->data.labelOrNote.stringLength > 0) { const char *pID = NULL; - if (pMetadata->type == ma_dr_wav_metadata_type_list_label) { + if (pMetadata->type == drwav_metadata_type_list_label) { pID = "labl"; } - else if (pMetadata->type == ma_dr_wav_metadata_type_list_note) { + else if (pMetadata->type == drwav_metadata_type_list_note) { pID = "note"; } - MA_DR_WAV_ASSERT(pID != NULL); - MA_DR_WAV_ASSERT(pMetadata->data.labelOrNote.pString != NULL); - subchunkSize = MA_DR_WAV_LIST_LABEL_OR_NOTE_BYTES; - bytesWritten += ma_dr_wav__write_or_count(pWav, pID, 4); + DRWAV_ASSERT(pID != NULL); + DRWAV_ASSERT(pMetadata->data.labelOrNote.pString != NULL); + subchunkSize = DRWAV_LIST_LABEL_OR_NOTE_BYTES; + bytesWritten += drwav__write_or_count(pWav, pID, 4); subchunkSize += pMetadata->data.labelOrNote.stringLength + 1; - bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, subchunkSize); - bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, pMetadata->data.labelOrNote.cuePointId); - bytesWritten += ma_dr_wav__write_or_count(pWav, pMetadata->data.labelOrNote.pString, pMetadata->data.labelOrNote.stringLength); - bytesWritten += ma_dr_wav__write_or_count_byte(pWav, '\0'); + bytesWritten += drwav__write_or_count_u32ne_to_le(pWav, subchunkSize); + bytesWritten += drwav__write_or_count_u32ne_to_le(pWav, pMetadata->data.labelOrNote.cuePointId); + bytesWritten += drwav__write_or_count(pWav, pMetadata->data.labelOrNote.pString, pMetadata->data.labelOrNote.stringLength); + bytesWritten += drwav__write_or_count_byte(pWav, '\0'); } } break; - case ma_dr_wav_metadata_type_list_labelled_cue_region: + case drwav_metadata_type_list_labelled_cue_region: { - subchunkSize = MA_DR_WAV_LIST_LABELLED_TEXT_BYTES; - bytesWritten += ma_dr_wav__write_or_count(pWav, "ltxt", 4); + subchunkSize = DRWAV_LIST_LABELLED_TEXT_BYTES; + bytesWritten += drwav__write_or_count(pWav, "ltxt", 4); if (pMetadata->data.labelledCueRegion.stringLength > 0) { subchunkSize += pMetadata->data.labelledCueRegion.stringLength + 1; } - bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, subchunkSize); - bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, pMetadata->data.labelledCueRegion.cuePointId); - bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, pMetadata->data.labelledCueRegion.sampleLength); - bytesWritten += ma_dr_wav__write_or_count(pWav, pMetadata->data.labelledCueRegion.purposeId, 4); - bytesWritten += ma_dr_wav__write_or_count_u16ne_to_le(pWav, pMetadata->data.labelledCueRegion.country); - bytesWritten += ma_dr_wav__write_or_count_u16ne_to_le(pWav, pMetadata->data.labelledCueRegion.language); - bytesWritten += ma_dr_wav__write_or_count_u16ne_to_le(pWav, pMetadata->data.labelledCueRegion.dialect); - bytesWritten += ma_dr_wav__write_or_count_u16ne_to_le(pWav, pMetadata->data.labelledCueRegion.codePage); + bytesWritten += drwav__write_or_count_u32ne_to_le(pWav, subchunkSize); + bytesWritten += drwav__write_or_count_u32ne_to_le(pWav, pMetadata->data.labelledCueRegion.cuePointId); + bytesWritten += drwav__write_or_count_u32ne_to_le(pWav, pMetadata->data.labelledCueRegion.sampleLength); + bytesWritten += drwav__write_or_count(pWav, pMetadata->data.labelledCueRegion.purposeId, 4); + bytesWritten += drwav__write_or_count_u16ne_to_le(pWav, pMetadata->data.labelledCueRegion.country); + bytesWritten += drwav__write_or_count_u16ne_to_le(pWav, pMetadata->data.labelledCueRegion.language); + bytesWritten += drwav__write_or_count_u16ne_to_le(pWav, pMetadata->data.labelledCueRegion.dialect); + bytesWritten += drwav__write_or_count_u16ne_to_le(pWav, pMetadata->data.labelledCueRegion.codePage); if (pMetadata->data.labelledCueRegion.stringLength > 0) { - MA_DR_WAV_ASSERT(pMetadata->data.labelledCueRegion.pString != NULL); - bytesWritten += ma_dr_wav__write_or_count(pWav, pMetadata->data.labelledCueRegion.pString, pMetadata->data.labelledCueRegion.stringLength); - bytesWritten += ma_dr_wav__write_or_count_byte(pWav, '\0'); + DRWAV_ASSERT(pMetadata->data.labelledCueRegion.pString != NULL); + bytesWritten += drwav__write_or_count(pWav, pMetadata->data.labelledCueRegion.pString, pMetadata->data.labelledCueRegion.stringLength); + bytesWritten += drwav__write_or_count_byte(pWav, '\0'); } } break; - case ma_dr_wav_metadata_type_unknown: + case drwav_metadata_type_unknown: { - if (pMetadata->data.unknown.chunkLocation == ma_dr_wav_metadata_location_inside_adtl_list) { + if (pMetadata->data.unknown.chunkLocation == drwav_metadata_location_inside_adtl_list) { subchunkSize = pMetadata->data.unknown.dataSizeInBytes; - MA_DR_WAV_ASSERT(pMetadata->data.unknown.pData != NULL); - bytesWritten += ma_dr_wav__write_or_count(pWav, pMetadata->data.unknown.id, 4); - bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, subchunkSize); - bytesWritten += ma_dr_wav__write_or_count(pWav, pMetadata->data.unknown.pData, subchunkSize); + DRWAV_ASSERT(pMetadata->data.unknown.pData != NULL); + bytesWritten += drwav__write_or_count(pWav, pMetadata->data.unknown.id, 4); + bytesWritten += drwav__write_or_count_u32ne_to_le(pWav, subchunkSize); + bytesWritten += drwav__write_or_count(pWav, pMetadata->data.unknown.pData, subchunkSize); } } break; default: break; } if ((subchunkSize % 2) != 0) { - bytesWritten += ma_dr_wav__write_or_count_byte(pWav, 0); + bytesWritten += drwav__write_or_count_byte(pWav, 0); } } } - MA_DR_WAV_ASSERT((bytesWritten % 2) == 0); + DRWAV_ASSERT((bytesWritten % 2) == 0); return bytesWritten; } -MA_PRIVATE ma_uint32 ma_dr_wav__riff_chunk_size_riff(ma_uint64 dataChunkSize, ma_dr_wav_metadata* pMetadata, ma_uint32 metadataCount) +DRWAV_PRIVATE drwav_uint32 drwav__riff_chunk_size_riff(drwav_uint64 dataChunkSize, drwav_metadata* pMetadata, drwav_uint32 metadataCount) { - ma_uint64 chunkSize = 4 + 24 + (ma_uint64)ma_dr_wav__write_or_count_metadata(NULL, pMetadata, metadataCount) + 8 + dataChunkSize + ma_dr_wav__chunk_padding_size_riff(dataChunkSize); + drwav_uint64 chunkSize = 4 + 24 + (drwav_uint64)drwav__write_or_count_metadata(NULL, pMetadata, metadataCount) + 8 + dataChunkSize + drwav__chunk_padding_size_riff(dataChunkSize); if (chunkSize > 0xFFFFFFFFUL) { chunkSize = 0xFFFFFFFFUL; } - return (ma_uint32)chunkSize; + return (drwav_uint32)chunkSize; } -MA_PRIVATE ma_uint32 ma_dr_wav__data_chunk_size_riff(ma_uint64 dataChunkSize) +DRWAV_PRIVATE drwav_uint32 drwav__data_chunk_size_riff(drwav_uint64 dataChunkSize) { if (dataChunkSize <= 0xFFFFFFFFUL) { - return (ma_uint32)dataChunkSize; + return (drwav_uint32)dataChunkSize; } else { return 0xFFFFFFFFUL; } } -MA_PRIVATE ma_uint64 ma_dr_wav__riff_chunk_size_w64(ma_uint64 dataChunkSize) +DRWAV_PRIVATE drwav_uint64 drwav__riff_chunk_size_w64(drwav_uint64 dataChunkSize) { - ma_uint64 dataSubchunkPaddingSize = ma_dr_wav__chunk_padding_size_w64(dataChunkSize); + drwav_uint64 dataSubchunkPaddingSize = drwav__chunk_padding_size_w64(dataChunkSize); return 80 + 24 + dataChunkSize + dataSubchunkPaddingSize; } -MA_PRIVATE ma_uint64 ma_dr_wav__data_chunk_size_w64(ma_uint64 dataChunkSize) +DRWAV_PRIVATE drwav_uint64 drwav__data_chunk_size_w64(drwav_uint64 dataChunkSize) { return 24 + dataChunkSize; } -MA_PRIVATE ma_uint64 ma_dr_wav__riff_chunk_size_rf64(ma_uint64 dataChunkSize, ma_dr_wav_metadata *metadata, ma_uint32 numMetadata) +DRWAV_PRIVATE drwav_uint64 drwav__riff_chunk_size_rf64(drwav_uint64 dataChunkSize, drwav_metadata *metadata, drwav_uint32 numMetadata) { - ma_uint64 chunkSize = 4 + 36 + 24 + (ma_uint64)ma_dr_wav__write_or_count_metadata(NULL, metadata, numMetadata) + 8 + dataChunkSize + ma_dr_wav__chunk_padding_size_riff(dataChunkSize); + drwav_uint64 chunkSize = 4 + 36 + 24 + (drwav_uint64)drwav__write_or_count_metadata(NULL, metadata, numMetadata) + 8 + dataChunkSize + drwav__chunk_padding_size_riff(dataChunkSize); if (chunkSize > 0xFFFFFFFFUL) { chunkSize = 0xFFFFFFFFUL; } return chunkSize; } -MA_PRIVATE ma_uint64 ma_dr_wav__data_chunk_size_rf64(ma_uint64 dataChunkSize) +DRWAV_PRIVATE drwav_uint64 drwav__data_chunk_size_rf64(drwav_uint64 dataChunkSize) { return dataChunkSize; } -MA_PRIVATE ma_bool32 ma_dr_wav_preinit_write(ma_dr_wav* pWav, const ma_dr_wav_data_format* pFormat, ma_bool32 isSequential, ma_dr_wav_write_proc onWrite, ma_dr_wav_seek_proc onSeek, void* pUserData, const ma_allocation_callbacks* pAllocationCallbacks) +DRWAV_PRIVATE drwav_bool32 drwav_preinit_write(drwav* pWav, const drwav_data_format* pFormat, drwav_bool32 isSequential, drwav_write_proc onWrite, drwav_seek_proc onSeek, void* pUserData, const drwav_allocation_callbacks* pAllocationCallbacks) { if (pWav == NULL || onWrite == NULL) { - return MA_FALSE; + return DRWAV_FALSE; } if (!isSequential && onSeek == NULL) { - return MA_FALSE; + return DRWAV_FALSE; } - if (pFormat->format == MA_DR_WAVE_FORMAT_EXTENSIBLE) { - return MA_FALSE; + if (pFormat->format == DR_WAVE_FORMAT_EXTENSIBLE) { + return DRWAV_FALSE; } - if (pFormat->format == MA_DR_WAVE_FORMAT_ADPCM || pFormat->format == MA_DR_WAVE_FORMAT_DVI_ADPCM) { - return MA_FALSE; + if (pFormat->format == DR_WAVE_FORMAT_ADPCM || pFormat->format == DR_WAVE_FORMAT_DVI_ADPCM) { + return DRWAV_FALSE; } - MA_DR_WAV_ZERO_MEMORY(pWav, sizeof(*pWav)); + DRWAV_ZERO_MEMORY(pWav, sizeof(*pWav)); pWav->onWrite = onWrite; pWav->onSeek = onSeek; pWav->pUserData = pUserData; - pWav->allocationCallbacks = ma_dr_wav_copy_allocation_callbacks_or_defaults(pAllocationCallbacks); + pWav->allocationCallbacks = drwav_copy_allocation_callbacks_or_defaults(pAllocationCallbacks); if (pWav->allocationCallbacks.onFree == NULL || (pWav->allocationCallbacks.onMalloc == NULL && pWav->allocationCallbacks.onRealloc == NULL)) { - return MA_FALSE; + return DRWAV_FALSE; } - pWav->fmt.formatTag = (ma_uint16)pFormat->format; - pWav->fmt.channels = (ma_uint16)pFormat->channels; + pWav->fmt.formatTag = (drwav_uint16)pFormat->format; + pWav->fmt.channels = (drwav_uint16)pFormat->channels; pWav->fmt.sampleRate = pFormat->sampleRate; - pWav->fmt.avgBytesPerSec = (ma_uint32)((pFormat->bitsPerSample * pFormat->sampleRate * pFormat->channels) / 8); - pWav->fmt.blockAlign = (ma_uint16)((pFormat->channels * pFormat->bitsPerSample) / 8); - pWav->fmt.bitsPerSample = (ma_uint16)pFormat->bitsPerSample; + pWav->fmt.avgBytesPerSec = (drwav_uint32)((pFormat->bitsPerSample * pFormat->sampleRate * pFormat->channels) / 8); + pWav->fmt.blockAlign = (drwav_uint16)((pFormat->channels * pFormat->bitsPerSample) / 8); + pWav->fmt.bitsPerSample = (drwav_uint16)pFormat->bitsPerSample; pWav->fmt.extendedSize = 0; pWav->isSequentialWrite = isSequential; - return MA_TRUE; + return DRWAV_TRUE; } -MA_PRIVATE ma_bool32 ma_dr_wav_init_write__internal(ma_dr_wav* pWav, const ma_dr_wav_data_format* pFormat, ma_uint64 totalSampleCount) +DRWAV_PRIVATE drwav_bool32 drwav_init_write__internal(drwav* pWav, const drwav_data_format* pFormat, drwav_uint64 totalSampleCount) { size_t runningPos = 0; - ma_uint64 initialDataChunkSize = 0; - ma_uint64 chunkSizeFMT; + drwav_uint64 initialDataChunkSize = 0; + drwav_uint64 chunkSizeFMT; if (pWav->isSequentialWrite) { initialDataChunkSize = (totalSampleCount * pWav->fmt.bitsPerSample) / 8; - if (pFormat->container == ma_dr_wav_container_riff) { + if (pFormat->container == drwav_container_riff) { if (initialDataChunkSize > (0xFFFFFFFFUL - 36)) { - return MA_FALSE; + return DRWAV_FALSE; } } } pWav->dataChunkDataSizeTargetWrite = initialDataChunkSize; - if (pFormat->container == ma_dr_wav_container_riff) { - ma_uint32 chunkSizeRIFF = 28 + (ma_uint32)initialDataChunkSize; - runningPos += ma_dr_wav__write(pWav, "RIFF", 4); - runningPos += ma_dr_wav__write_u32ne_to_le(pWav, chunkSizeRIFF); - runningPos += ma_dr_wav__write(pWav, "WAVE", 4); - } else if (pFormat->container == ma_dr_wav_container_w64) { - ma_uint64 chunkSizeRIFF = 80 + 24 + initialDataChunkSize; - runningPos += ma_dr_wav__write(pWav, ma_dr_wavGUID_W64_RIFF, 16); - runningPos += ma_dr_wav__write_u64ne_to_le(pWav, chunkSizeRIFF); - runningPos += ma_dr_wav__write(pWav, ma_dr_wavGUID_W64_WAVE, 16); - } else if (pFormat->container == ma_dr_wav_container_rf64) { - runningPos += ma_dr_wav__write(pWav, "RF64", 4); - runningPos += ma_dr_wav__write_u32ne_to_le(pWav, 0xFFFFFFFF); - runningPos += ma_dr_wav__write(pWav, "WAVE", 4); - } else { - return MA_FALSE; - } - if (pFormat->container == ma_dr_wav_container_rf64) { - ma_uint32 initialds64ChunkSize = 28; - ma_uint64 initialRiffChunkSize = 8 + initialds64ChunkSize + initialDataChunkSize; - runningPos += ma_dr_wav__write(pWav, "ds64", 4); - runningPos += ma_dr_wav__write_u32ne_to_le(pWav, initialds64ChunkSize); - runningPos += ma_dr_wav__write_u64ne_to_le(pWav, initialRiffChunkSize); - runningPos += ma_dr_wav__write_u64ne_to_le(pWav, initialDataChunkSize); - runningPos += ma_dr_wav__write_u64ne_to_le(pWav, totalSampleCount); - runningPos += ma_dr_wav__write_u32ne_to_le(pWav, 0); - } - if (pFormat->container == ma_dr_wav_container_riff || pFormat->container == ma_dr_wav_container_rf64) { + if (pFormat->container == drwav_container_riff) { + drwav_uint32 chunkSizeRIFF = 28 + (drwav_uint32)initialDataChunkSize; + runningPos += drwav__write(pWav, "RIFF", 4); + runningPos += drwav__write_u32ne_to_le(pWav, chunkSizeRIFF); + runningPos += drwav__write(pWav, "WAVE", 4); + } else if (pFormat->container == drwav_container_w64) { + drwav_uint64 chunkSizeRIFF = 80 + 24 + initialDataChunkSize; + runningPos += drwav__write(pWav, drwavGUID_W64_RIFF, 16); + runningPos += drwav__write_u64ne_to_le(pWav, chunkSizeRIFF); + runningPos += drwav__write(pWav, drwavGUID_W64_WAVE, 16); + } else if (pFormat->container == drwav_container_rf64) { + runningPos += drwav__write(pWav, "RF64", 4); + runningPos += drwav__write_u32ne_to_le(pWav, 0xFFFFFFFF); + runningPos += drwav__write(pWav, "WAVE", 4); + } + if (pFormat->container == drwav_container_rf64) { + drwav_uint32 initialds64ChunkSize = 28; + drwav_uint64 initialRiffChunkSize = 8 + initialds64ChunkSize + initialDataChunkSize; + runningPos += drwav__write(pWav, "ds64", 4); + runningPos += drwav__write_u32ne_to_le(pWav, initialds64ChunkSize); + runningPos += drwav__write_u64ne_to_le(pWav, initialRiffChunkSize); + runningPos += drwav__write_u64ne_to_le(pWav, initialDataChunkSize); + runningPos += drwav__write_u64ne_to_le(pWav, totalSampleCount); + runningPos += drwav__write_u32ne_to_le(pWav, 0); + } + if (pFormat->container == drwav_container_riff || pFormat->container == drwav_container_rf64) { chunkSizeFMT = 16; - runningPos += ma_dr_wav__write(pWav, "fmt ", 4); - runningPos += ma_dr_wav__write_u32ne_to_le(pWav, (ma_uint32)chunkSizeFMT); - } else if (pFormat->container == ma_dr_wav_container_w64) { + runningPos += drwav__write(pWav, "fmt ", 4); + runningPos += drwav__write_u32ne_to_le(pWav, (drwav_uint32)chunkSizeFMT); + } else if (pFormat->container == drwav_container_w64) { chunkSizeFMT = 40; - runningPos += ma_dr_wav__write(pWav, ma_dr_wavGUID_W64_FMT, 16); - runningPos += ma_dr_wav__write_u64ne_to_le(pWav, chunkSizeFMT); - } - runningPos += ma_dr_wav__write_u16ne_to_le(pWav, pWav->fmt.formatTag); - runningPos += ma_dr_wav__write_u16ne_to_le(pWav, pWav->fmt.channels); - runningPos += ma_dr_wav__write_u32ne_to_le(pWav, pWav->fmt.sampleRate); - runningPos += ma_dr_wav__write_u32ne_to_le(pWav, pWav->fmt.avgBytesPerSec); - runningPos += ma_dr_wav__write_u16ne_to_le(pWav, pWav->fmt.blockAlign); - runningPos += ma_dr_wav__write_u16ne_to_le(pWav, pWav->fmt.bitsPerSample); - if (!pWav->isSequentialWrite && pWav->pMetadata != NULL && pWav->metadataCount > 0 && (pFormat->container == ma_dr_wav_container_riff || pFormat->container == ma_dr_wav_container_rf64)) { - runningPos += ma_dr_wav__write_or_count_metadata(pWav, pWav->pMetadata, pWav->metadataCount); + runningPos += drwav__write(pWav, drwavGUID_W64_FMT, 16); + runningPos += drwav__write_u64ne_to_le(pWav, chunkSizeFMT); + } + runningPos += drwav__write_u16ne_to_le(pWav, pWav->fmt.formatTag); + runningPos += drwav__write_u16ne_to_le(pWav, pWav->fmt.channels); + runningPos += drwav__write_u32ne_to_le(pWav, pWav->fmt.sampleRate); + runningPos += drwav__write_u32ne_to_le(pWav, pWav->fmt.avgBytesPerSec); + runningPos += drwav__write_u16ne_to_le(pWav, pWav->fmt.blockAlign); + runningPos += drwav__write_u16ne_to_le(pWav, pWav->fmt.bitsPerSample); + if (!pWav->isSequentialWrite && pWav->pMetadata != NULL && pWav->metadataCount > 0 && (pFormat->container == drwav_container_riff || pFormat->container == drwav_container_rf64)) { + runningPos += drwav__write_or_count_metadata(pWav, pWav->pMetadata, pWav->metadataCount); } pWav->dataChunkDataPos = runningPos; - if (pFormat->container == ma_dr_wav_container_riff) { - ma_uint32 chunkSizeDATA = (ma_uint32)initialDataChunkSize; - runningPos += ma_dr_wav__write(pWav, "data", 4); - runningPos += ma_dr_wav__write_u32ne_to_le(pWav, chunkSizeDATA); - } else if (pFormat->container == ma_dr_wav_container_w64) { - ma_uint64 chunkSizeDATA = 24 + initialDataChunkSize; - runningPos += ma_dr_wav__write(pWav, ma_dr_wavGUID_W64_DATA, 16); - runningPos += ma_dr_wav__write_u64ne_to_le(pWav, chunkSizeDATA); - } else if (pFormat->container == ma_dr_wav_container_rf64) { - runningPos += ma_dr_wav__write(pWav, "data", 4); - runningPos += ma_dr_wav__write_u32ne_to_le(pWav, 0xFFFFFFFF); + if (pFormat->container == drwav_container_riff) { + drwav_uint32 chunkSizeDATA = (drwav_uint32)initialDataChunkSize; + runningPos += drwav__write(pWav, "data", 4); + runningPos += drwav__write_u32ne_to_le(pWav, chunkSizeDATA); + } else if (pFormat->container == drwav_container_w64) { + drwav_uint64 chunkSizeDATA = 24 + initialDataChunkSize; + runningPos += drwav__write(pWav, drwavGUID_W64_DATA, 16); + runningPos += drwav__write_u64ne_to_le(pWav, chunkSizeDATA); + } else if (pFormat->container == drwav_container_rf64) { + runningPos += drwav__write(pWav, "data", 4); + runningPos += drwav__write_u32ne_to_le(pWav, 0xFFFFFFFF); } pWav->container = pFormat->container; - pWav->channels = (ma_uint16)pFormat->channels; + pWav->channels = (drwav_uint16)pFormat->channels; pWav->sampleRate = pFormat->sampleRate; - pWav->bitsPerSample = (ma_uint16)pFormat->bitsPerSample; - pWav->translatedFormatTag = (ma_uint16)pFormat->format; + pWav->bitsPerSample = (drwav_uint16)pFormat->bitsPerSample; + pWav->translatedFormatTag = (drwav_uint16)pFormat->format; pWav->dataChunkDataPos = runningPos; - return MA_TRUE; + return DRWAV_TRUE; } -MA_API ma_bool32 ma_dr_wav_init_write(ma_dr_wav* pWav, const ma_dr_wav_data_format* pFormat, ma_dr_wav_write_proc onWrite, ma_dr_wav_seek_proc onSeek, void* pUserData, const ma_allocation_callbacks* pAllocationCallbacks) +DRWAV_API drwav_bool32 drwav_init_write(drwav* pWav, const drwav_data_format* pFormat, drwav_write_proc onWrite, drwav_seek_proc onSeek, void* pUserData, const drwav_allocation_callbacks* pAllocationCallbacks) { - if (!ma_dr_wav_preinit_write(pWav, pFormat, MA_FALSE, onWrite, onSeek, pUserData, pAllocationCallbacks)) { - return MA_FALSE; + if (!drwav_preinit_write(pWav, pFormat, DRWAV_FALSE, onWrite, onSeek, pUserData, pAllocationCallbacks)) { + return DRWAV_FALSE; } - return ma_dr_wav_init_write__internal(pWav, pFormat, 0); + return drwav_init_write__internal(pWav, pFormat, 0); } -MA_API ma_bool32 ma_dr_wav_init_write_sequential(ma_dr_wav* pWav, const ma_dr_wav_data_format* pFormat, ma_uint64 totalSampleCount, ma_dr_wav_write_proc onWrite, void* pUserData, const ma_allocation_callbacks* pAllocationCallbacks) +DRWAV_API drwav_bool32 drwav_init_write_sequential(drwav* pWav, const drwav_data_format* pFormat, drwav_uint64 totalSampleCount, drwav_write_proc onWrite, void* pUserData, const drwav_allocation_callbacks* pAllocationCallbacks) { - if (!ma_dr_wav_preinit_write(pWav, pFormat, MA_TRUE, onWrite, NULL, pUserData, pAllocationCallbacks)) { - return MA_FALSE; + if (!drwav_preinit_write(pWav, pFormat, DRWAV_TRUE, onWrite, NULL, pUserData, pAllocationCallbacks)) { + return DRWAV_FALSE; } - return ma_dr_wav_init_write__internal(pWav, pFormat, totalSampleCount); + return drwav_init_write__internal(pWav, pFormat, totalSampleCount); } -MA_API ma_bool32 ma_dr_wav_init_write_sequential_pcm_frames(ma_dr_wav* pWav, const ma_dr_wav_data_format* pFormat, ma_uint64 totalPCMFrameCount, ma_dr_wav_write_proc onWrite, void* pUserData, const ma_allocation_callbacks* pAllocationCallbacks) +DRWAV_API drwav_bool32 drwav_init_write_sequential_pcm_frames(drwav* pWav, const drwav_data_format* pFormat, drwav_uint64 totalPCMFrameCount, drwav_write_proc onWrite, void* pUserData, const drwav_allocation_callbacks* pAllocationCallbacks) { if (pFormat == NULL) { - return MA_FALSE; + return DRWAV_FALSE; } - return ma_dr_wav_init_write_sequential(pWav, pFormat, totalPCMFrameCount*pFormat->channels, onWrite, pUserData, pAllocationCallbacks); + return drwav_init_write_sequential(pWav, pFormat, totalPCMFrameCount*pFormat->channels, onWrite, pUserData, pAllocationCallbacks); } -MA_API ma_bool32 ma_dr_wav_init_write_with_metadata(ma_dr_wav* pWav, const ma_dr_wav_data_format* pFormat, ma_dr_wav_write_proc onWrite, ma_dr_wav_seek_proc onSeek, void* pUserData, const ma_allocation_callbacks* pAllocationCallbacks, ma_dr_wav_metadata* pMetadata, ma_uint32 metadataCount) +DRWAV_API drwav_bool32 drwav_init_write_with_metadata(drwav* pWav, const drwav_data_format* pFormat, drwav_write_proc onWrite, drwav_seek_proc onSeek, void* pUserData, const drwav_allocation_callbacks* pAllocationCallbacks, drwav_metadata* pMetadata, drwav_uint32 metadataCount) { - if (!ma_dr_wav_preinit_write(pWav, pFormat, MA_FALSE, onWrite, onSeek, pUserData, pAllocationCallbacks)) { - return MA_FALSE; + if (!drwav_preinit_write(pWav, pFormat, DRWAV_FALSE, onWrite, onSeek, pUserData, pAllocationCallbacks)) { + return DRWAV_FALSE; } pWav->pMetadata = pMetadata; pWav->metadataCount = metadataCount; - return ma_dr_wav_init_write__internal(pWav, pFormat, 0); + return drwav_init_write__internal(pWav, pFormat, 0); } -MA_API ma_uint64 ma_dr_wav_target_write_size_bytes(const ma_dr_wav_data_format* pFormat, ma_uint64 totalFrameCount, ma_dr_wav_metadata* pMetadata, ma_uint32 metadataCount) +DRWAV_API drwav_uint64 drwav_target_write_size_bytes(const drwav_data_format* pFormat, drwav_uint64 totalFrameCount, drwav_metadata* pMetadata, drwav_uint32 metadataCount) { - ma_uint64 targetDataSizeBytes = (ma_uint64)((ma_int64)totalFrameCount * pFormat->channels * pFormat->bitsPerSample/8.0); - ma_uint64 riffChunkSizeBytes; - ma_uint64 fileSizeBytes = 0; - if (pFormat->container == ma_dr_wav_container_riff) { - riffChunkSizeBytes = ma_dr_wav__riff_chunk_size_riff(targetDataSizeBytes, pMetadata, metadataCount); + drwav_uint64 targetDataSizeBytes = (drwav_uint64)((drwav_int64)totalFrameCount * pFormat->channels * pFormat->bitsPerSample/8.0); + drwav_uint64 riffChunkSizeBytes; + drwav_uint64 fileSizeBytes = 0; + if (pFormat->container == drwav_container_riff) { + riffChunkSizeBytes = drwav__riff_chunk_size_riff(targetDataSizeBytes, pMetadata, metadataCount); fileSizeBytes = (8 + riffChunkSizeBytes); - } else if (pFormat->container == ma_dr_wav_container_w64) { - riffChunkSizeBytes = ma_dr_wav__riff_chunk_size_w64(targetDataSizeBytes); + } else if (pFormat->container == drwav_container_w64) { + riffChunkSizeBytes = drwav__riff_chunk_size_w64(targetDataSizeBytes); fileSizeBytes = riffChunkSizeBytes; - } else if (pFormat->container == ma_dr_wav_container_rf64) { - riffChunkSizeBytes = ma_dr_wav__riff_chunk_size_rf64(targetDataSizeBytes, pMetadata, metadataCount); + } else if (pFormat->container == drwav_container_rf64) { + riffChunkSizeBytes = drwav__riff_chunk_size_rf64(targetDataSizeBytes, pMetadata, metadataCount); fileSizeBytes = (8 + riffChunkSizeBytes); } return fileSizeBytes; } -#ifndef MA_DR_WAV_NO_STDIO -MA_PRIVATE size_t ma_dr_wav__on_read_stdio(void* pUserData, void* pBufferOut, size_t bytesToRead) +#ifndef DR_WAV_NO_STDIO +#include +DRWAV_PRIVATE drwav_result drwav_result_from_errno(int e) +{ + switch (e) + { + case 0: return DRWAV_SUCCESS; + #ifdef EPERM + case EPERM: return DRWAV_INVALID_OPERATION; + #endif + #ifdef ENOENT + case ENOENT: return DRWAV_DOES_NOT_EXIST; + #endif + #ifdef ESRCH + case ESRCH: return DRWAV_DOES_NOT_EXIST; + #endif + #ifdef EINTR + case EINTR: return DRWAV_INTERRUPT; + #endif + #ifdef EIO + case EIO: return DRWAV_IO_ERROR; + #endif + #ifdef ENXIO + case ENXIO: return DRWAV_DOES_NOT_EXIST; + #endif + #ifdef E2BIG + case E2BIG: return DRWAV_INVALID_ARGS; + #endif + #ifdef ENOEXEC + case ENOEXEC: return DRWAV_INVALID_FILE; + #endif + #ifdef EBADF + case EBADF: return DRWAV_INVALID_FILE; + #endif + #ifdef ECHILD + case ECHILD: return DRWAV_ERROR; + #endif + #ifdef EAGAIN + case EAGAIN: return DRWAV_UNAVAILABLE; + #endif + #ifdef ENOMEM + case ENOMEM: return DRWAV_OUT_OF_MEMORY; + #endif + #ifdef EACCES + case EACCES: return DRWAV_ACCESS_DENIED; + #endif + #ifdef EFAULT + case EFAULT: return DRWAV_BAD_ADDRESS; + #endif + #ifdef ENOTBLK + case ENOTBLK: return DRWAV_ERROR; + #endif + #ifdef EBUSY + case EBUSY: return DRWAV_BUSY; + #endif + #ifdef EEXIST + case EEXIST: return DRWAV_ALREADY_EXISTS; + #endif + #ifdef EXDEV + case EXDEV: return DRWAV_ERROR; + #endif + #ifdef ENODEV + case ENODEV: return DRWAV_DOES_NOT_EXIST; + #endif + #ifdef ENOTDIR + case ENOTDIR: return DRWAV_NOT_DIRECTORY; + #endif + #ifdef EISDIR + case EISDIR: return DRWAV_IS_DIRECTORY; + #endif + #ifdef EINVAL + case EINVAL: return DRWAV_INVALID_ARGS; + #endif + #ifdef ENFILE + case ENFILE: return DRWAV_TOO_MANY_OPEN_FILES; + #endif + #ifdef EMFILE + case EMFILE: return DRWAV_TOO_MANY_OPEN_FILES; + #endif + #ifdef ENOTTY + case ENOTTY: return DRWAV_INVALID_OPERATION; + #endif + #ifdef ETXTBSY + case ETXTBSY: return DRWAV_BUSY; + #endif + #ifdef EFBIG + case EFBIG: return DRWAV_TOO_BIG; + #endif + #ifdef ENOSPC + case ENOSPC: return DRWAV_NO_SPACE; + #endif + #ifdef ESPIPE + case ESPIPE: return DRWAV_BAD_SEEK; + #endif + #ifdef EROFS + case EROFS: return DRWAV_ACCESS_DENIED; + #endif + #ifdef EMLINK + case EMLINK: return DRWAV_TOO_MANY_LINKS; + #endif + #ifdef EPIPE + case EPIPE: return DRWAV_BAD_PIPE; + #endif + #ifdef EDOM + case EDOM: return DRWAV_OUT_OF_RANGE; + #endif + #ifdef ERANGE + case ERANGE: return DRWAV_OUT_OF_RANGE; + #endif + #ifdef EDEADLK + case EDEADLK: return DRWAV_DEADLOCK; + #endif + #ifdef ENAMETOOLONG + case ENAMETOOLONG: return DRWAV_PATH_TOO_LONG; + #endif + #ifdef ENOLCK + case ENOLCK: return DRWAV_ERROR; + #endif + #ifdef ENOSYS + case ENOSYS: return DRWAV_NOT_IMPLEMENTED; + #endif + #ifdef ENOTEMPTY + case ENOTEMPTY: return DRWAV_DIRECTORY_NOT_EMPTY; + #endif + #ifdef ELOOP + case ELOOP: return DRWAV_TOO_MANY_LINKS; + #endif + #ifdef ENOMSG + case ENOMSG: return DRWAV_NO_MESSAGE; + #endif + #ifdef EIDRM + case EIDRM: return DRWAV_ERROR; + #endif + #ifdef ECHRNG + case ECHRNG: return DRWAV_ERROR; + #endif + #ifdef EL2NSYNC + case EL2NSYNC: return DRWAV_ERROR; + #endif + #ifdef EL3HLT + case EL3HLT: return DRWAV_ERROR; + #endif + #ifdef EL3RST + case EL3RST: return DRWAV_ERROR; + #endif + #ifdef ELNRNG + case ELNRNG: return DRWAV_OUT_OF_RANGE; + #endif + #ifdef EUNATCH + case EUNATCH: return DRWAV_ERROR; + #endif + #ifdef ENOCSI + case ENOCSI: return DRWAV_ERROR; + #endif + #ifdef EL2HLT + case EL2HLT: return DRWAV_ERROR; + #endif + #ifdef EBADE + case EBADE: return DRWAV_ERROR; + #endif + #ifdef EBADR + case EBADR: return DRWAV_ERROR; + #endif + #ifdef EXFULL + case EXFULL: return DRWAV_ERROR; + #endif + #ifdef ENOANO + case ENOANO: return DRWAV_ERROR; + #endif + #ifdef EBADRQC + case EBADRQC: return DRWAV_ERROR; + #endif + #ifdef EBADSLT + case EBADSLT: return DRWAV_ERROR; + #endif + #ifdef EBFONT + case EBFONT: return DRWAV_INVALID_FILE; + #endif + #ifdef ENOSTR + case ENOSTR: return DRWAV_ERROR; + #endif + #ifdef ENODATA + case ENODATA: return DRWAV_NO_DATA_AVAILABLE; + #endif + #ifdef ETIME + case ETIME: return DRWAV_TIMEOUT; + #endif + #ifdef ENOSR + case ENOSR: return DRWAV_NO_DATA_AVAILABLE; + #endif + #ifdef ENONET + case ENONET: return DRWAV_NO_NETWORK; + #endif + #ifdef ENOPKG + case ENOPKG: return DRWAV_ERROR; + #endif + #ifdef EREMOTE + case EREMOTE: return DRWAV_ERROR; + #endif + #ifdef ENOLINK + case ENOLINK: return DRWAV_ERROR; + #endif + #ifdef EADV + case EADV: return DRWAV_ERROR; + #endif + #ifdef ESRMNT + case ESRMNT: return DRWAV_ERROR; + #endif + #ifdef ECOMM + case ECOMM: return DRWAV_ERROR; + #endif + #ifdef EPROTO + case EPROTO: return DRWAV_ERROR; + #endif + #ifdef EMULTIHOP + case EMULTIHOP: return DRWAV_ERROR; + #endif + #ifdef EDOTDOT + case EDOTDOT: return DRWAV_ERROR; + #endif + #ifdef EBADMSG + case EBADMSG: return DRWAV_BAD_MESSAGE; + #endif + #ifdef EOVERFLOW + case EOVERFLOW: return DRWAV_TOO_BIG; + #endif + #ifdef ENOTUNIQ + case ENOTUNIQ: return DRWAV_NOT_UNIQUE; + #endif + #ifdef EBADFD + case EBADFD: return DRWAV_ERROR; + #endif + #ifdef EREMCHG + case EREMCHG: return DRWAV_ERROR; + #endif + #ifdef ELIBACC + case ELIBACC: return DRWAV_ACCESS_DENIED; + #endif + #ifdef ELIBBAD + case ELIBBAD: return DRWAV_INVALID_FILE; + #endif + #ifdef ELIBSCN + case ELIBSCN: return DRWAV_INVALID_FILE; + #endif + #ifdef ELIBMAX + case ELIBMAX: return DRWAV_ERROR; + #endif + #ifdef ELIBEXEC + case ELIBEXEC: return DRWAV_ERROR; + #endif + #ifdef EILSEQ + case EILSEQ: return DRWAV_INVALID_DATA; + #endif + #ifdef ERESTART + case ERESTART: return DRWAV_ERROR; + #endif + #ifdef ESTRPIPE + case ESTRPIPE: return DRWAV_ERROR; + #endif + #ifdef EUSERS + case EUSERS: return DRWAV_ERROR; + #endif + #ifdef ENOTSOCK + case ENOTSOCK: return DRWAV_NOT_SOCKET; + #endif + #ifdef EDESTADDRREQ + case EDESTADDRREQ: return DRWAV_NO_ADDRESS; + #endif + #ifdef EMSGSIZE + case EMSGSIZE: return DRWAV_TOO_BIG; + #endif + #ifdef EPROTOTYPE + case EPROTOTYPE: return DRWAV_BAD_PROTOCOL; + #endif + #ifdef ENOPROTOOPT + case ENOPROTOOPT: return DRWAV_PROTOCOL_UNAVAILABLE; + #endif + #ifdef EPROTONOSUPPORT + case EPROTONOSUPPORT: return DRWAV_PROTOCOL_NOT_SUPPORTED; + #endif + #ifdef ESOCKTNOSUPPORT + case ESOCKTNOSUPPORT: return DRWAV_SOCKET_NOT_SUPPORTED; + #endif + #ifdef EOPNOTSUPP + case EOPNOTSUPP: return DRWAV_INVALID_OPERATION; + #endif + #ifdef EPFNOSUPPORT + case EPFNOSUPPORT: return DRWAV_PROTOCOL_FAMILY_NOT_SUPPORTED; + #endif + #ifdef EAFNOSUPPORT + case EAFNOSUPPORT: return DRWAV_ADDRESS_FAMILY_NOT_SUPPORTED; + #endif + #ifdef EADDRINUSE + case EADDRINUSE: return DRWAV_ALREADY_IN_USE; + #endif + #ifdef EADDRNOTAVAIL + case EADDRNOTAVAIL: return DRWAV_ERROR; + #endif + #ifdef ENETDOWN + case ENETDOWN: return DRWAV_NO_NETWORK; + #endif + #ifdef ENETUNREACH + case ENETUNREACH: return DRWAV_NO_NETWORK; + #endif + #ifdef ENETRESET + case ENETRESET: return DRWAV_NO_NETWORK; + #endif + #ifdef ECONNABORTED + case ECONNABORTED: return DRWAV_NO_NETWORK; + #endif + #ifdef ECONNRESET + case ECONNRESET: return DRWAV_CONNECTION_RESET; + #endif + #ifdef ENOBUFS + case ENOBUFS: return DRWAV_NO_SPACE; + #endif + #ifdef EISCONN + case EISCONN: return DRWAV_ALREADY_CONNECTED; + #endif + #ifdef ENOTCONN + case ENOTCONN: return DRWAV_NOT_CONNECTED; + #endif + #ifdef ESHUTDOWN + case ESHUTDOWN: return DRWAV_ERROR; + #endif + #ifdef ETOOMANYREFS + case ETOOMANYREFS: return DRWAV_ERROR; + #endif + #ifdef ETIMEDOUT + case ETIMEDOUT: return DRWAV_TIMEOUT; + #endif + #ifdef ECONNREFUSED + case ECONNREFUSED: return DRWAV_CONNECTION_REFUSED; + #endif + #ifdef EHOSTDOWN + case EHOSTDOWN: return DRWAV_NO_HOST; + #endif + #ifdef EHOSTUNREACH + case EHOSTUNREACH: return DRWAV_NO_HOST; + #endif + #ifdef EALREADY + case EALREADY: return DRWAV_IN_PROGRESS; + #endif + #ifdef EINPROGRESS + case EINPROGRESS: return DRWAV_IN_PROGRESS; + #endif + #ifdef ESTALE + case ESTALE: return DRWAV_INVALID_FILE; + #endif + #ifdef EUCLEAN + case EUCLEAN: return DRWAV_ERROR; + #endif + #ifdef ENOTNAM + case ENOTNAM: return DRWAV_ERROR; + #endif + #ifdef ENAVAIL + case ENAVAIL: return DRWAV_ERROR; + #endif + #ifdef EISNAM + case EISNAM: return DRWAV_ERROR; + #endif + #ifdef EREMOTEIO + case EREMOTEIO: return DRWAV_IO_ERROR; + #endif + #ifdef EDQUOT + case EDQUOT: return DRWAV_NO_SPACE; + #endif + #ifdef ENOMEDIUM + case ENOMEDIUM: return DRWAV_DOES_NOT_EXIST; + #endif + #ifdef EMEDIUMTYPE + case EMEDIUMTYPE: return DRWAV_ERROR; + #endif + #ifdef ECANCELED + case ECANCELED: return DRWAV_CANCELLED; + #endif + #ifdef ENOKEY + case ENOKEY: return DRWAV_ERROR; + #endif + #ifdef EKEYEXPIRED + case EKEYEXPIRED: return DRWAV_ERROR; + #endif + #ifdef EKEYREVOKED + case EKEYREVOKED: return DRWAV_ERROR; + #endif + #ifdef EKEYREJECTED + case EKEYREJECTED: return DRWAV_ERROR; + #endif + #ifdef EOWNERDEAD + case EOWNERDEAD: return DRWAV_ERROR; + #endif + #ifdef ENOTRECOVERABLE + case ENOTRECOVERABLE: return DRWAV_ERROR; + #endif + #ifdef ERFKILL + case ERFKILL: return DRWAV_ERROR; + #endif + #ifdef EHWPOISON + case EHWPOISON: return DRWAV_ERROR; + #endif + default: return DRWAV_ERROR; + } +} +DRWAV_PRIVATE drwav_result drwav_fopen(FILE** ppFile, const char* pFilePath, const char* pOpenMode) +{ +#if defined(_MSC_VER) && _MSC_VER >= 1400 + errno_t err; +#endif + if (ppFile != NULL) { + *ppFile = NULL; + } + if (pFilePath == NULL || pOpenMode == NULL || ppFile == NULL) { + return DRWAV_INVALID_ARGS; + } +#if defined(_MSC_VER) && _MSC_VER >= 1400 + err = fopen_s(ppFile, pFilePath, pOpenMode); + if (err != 0) { + return drwav_result_from_errno(err); + } +#else +#if defined(_WIN32) || defined(__APPLE__) + *ppFile = fopen(pFilePath, pOpenMode); +#else + #if defined(_FILE_OFFSET_BITS) && _FILE_OFFSET_BITS == 64 && defined(_LARGEFILE64_SOURCE) + *ppFile = fopen64(pFilePath, pOpenMode); + #else + *ppFile = fopen(pFilePath, pOpenMode); + #endif +#endif + if (*ppFile == NULL) { + drwav_result result = drwav_result_from_errno(errno); + if (result == DRWAV_SUCCESS) { + result = DRWAV_ERROR; + } + return result; + } +#endif + return DRWAV_SUCCESS; +} +#if defined(_WIN32) + #if defined(_MSC_VER) || defined(__MINGW64__) || (!defined(__STRICT_ANSI__) && !defined(_NO_EXT_KEYS)) + #define DRWAV_HAS_WFOPEN + #endif +#endif +#ifndef DR_WAV_NO_WCHAR +DRWAV_PRIVATE drwav_result drwav_wfopen(FILE** ppFile, const wchar_t* pFilePath, const wchar_t* pOpenMode, const drwav_allocation_callbacks* pAllocationCallbacks) +{ + if (ppFile != NULL) { + *ppFile = NULL; + } + if (pFilePath == NULL || pOpenMode == NULL || ppFile == NULL) { + return DRWAV_INVALID_ARGS; + } +#if defined(DRWAV_HAS_WFOPEN) + { + #if defined(_MSC_VER) && _MSC_VER >= 1400 + errno_t err = _wfopen_s(ppFile, pFilePath, pOpenMode); + if (err != 0) { + return drwav_result_from_errno(err); + } + #else + *ppFile = _wfopen(pFilePath, pOpenMode); + if (*ppFile == NULL) { + return drwav_result_from_errno(errno); + } + #endif + (void)pAllocationCallbacks; + } +#else + #if defined(__DJGPP__) + { + } + #else + { + mbstate_t mbs; + size_t lenMB; + const wchar_t* pFilePathTemp = pFilePath; + char* pFilePathMB = NULL; + char pOpenModeMB[32] = {0}; + DRWAV_ZERO_OBJECT(&mbs); + lenMB = wcsrtombs(NULL, &pFilePathTemp, 0, &mbs); + if (lenMB == (size_t)-1) { + return drwav_result_from_errno(errno); + } + pFilePathMB = (char*)drwav__malloc_from_callbacks(lenMB + 1, pAllocationCallbacks); + if (pFilePathMB == NULL) { + return DRWAV_OUT_OF_MEMORY; + } + pFilePathTemp = pFilePath; + DRWAV_ZERO_OBJECT(&mbs); + wcsrtombs(pFilePathMB, &pFilePathTemp, lenMB + 1, &mbs); + { + size_t i = 0; + for (;;) { + if (pOpenMode[i] == 0) { + pOpenModeMB[i] = '\0'; + break; + } + pOpenModeMB[i] = (char)pOpenMode[i]; + i += 1; + } + } + *ppFile = fopen(pFilePathMB, pOpenModeMB); + drwav__free_from_callbacks(pFilePathMB, pAllocationCallbacks); + } + #endif + if (*ppFile == NULL) { + return DRWAV_ERROR; + } +#endif + return DRWAV_SUCCESS; +} +#endif +DRWAV_PRIVATE size_t drwav__on_read_stdio(void* pUserData, void* pBufferOut, size_t bytesToRead) { return fread(pBufferOut, 1, bytesToRead, (FILE*)pUserData); } -MA_PRIVATE size_t ma_dr_wav__on_write_stdio(void* pUserData, const void* pData, size_t bytesToWrite) +DRWAV_PRIVATE size_t drwav__on_write_stdio(void* pUserData, const void* pData, size_t bytesToWrite) { return fwrite(pData, 1, bytesToWrite, (FILE*)pUserData); } -MA_PRIVATE ma_bool32 ma_dr_wav__on_seek_stdio(void* pUserData, int offset, ma_dr_wav_seek_origin origin) +DRWAV_PRIVATE drwav_bool32 drwav__on_seek_stdio(void* pUserData, int offset, drwav_seek_origin origin) { - return fseek((FILE*)pUserData, offset, (origin == ma_dr_wav_seek_origin_current) ? SEEK_CUR : SEEK_SET) == 0; + return fseek((FILE*)pUserData, offset, (origin == drwav_seek_origin_current) ? SEEK_CUR : SEEK_SET) == 0; } -MA_API ma_bool32 ma_dr_wav_init_file(ma_dr_wav* pWav, const char* filename, const ma_allocation_callbacks* pAllocationCallbacks) +DRWAV_API drwav_bool32 drwav_init_file(drwav* pWav, const char* filename, const drwav_allocation_callbacks* pAllocationCallbacks) { - return ma_dr_wav_init_file_ex(pWav, filename, NULL, NULL, 0, pAllocationCallbacks); + return drwav_init_file_ex(pWav, filename, NULL, NULL, 0, pAllocationCallbacks); } -MA_PRIVATE ma_bool32 ma_dr_wav_init_file__internal_FILE(ma_dr_wav* pWav, FILE* pFile, ma_dr_wav_chunk_proc onChunk, void* pChunkUserData, ma_uint32 flags, const ma_allocation_callbacks* pAllocationCallbacks) +DRWAV_PRIVATE drwav_bool32 drwav_init_file__internal_FILE(drwav* pWav, FILE* pFile, drwav_chunk_proc onChunk, void* pChunkUserData, drwav_uint32 flags, drwav_metadata_type allowedMetadataTypes, const drwav_allocation_callbacks* pAllocationCallbacks) { - ma_bool32 result; - result = ma_dr_wav_preinit(pWav, ma_dr_wav__on_read_stdio, ma_dr_wav__on_seek_stdio, (void*)pFile, pAllocationCallbacks); - if (result != MA_TRUE) { + drwav_bool32 result; + result = drwav_preinit(pWav, drwav__on_read_stdio, drwav__on_seek_stdio, (void*)pFile, pAllocationCallbacks); + if (result != DRWAV_TRUE) { fclose(pFile); return result; } - result = ma_dr_wav_init__internal(pWav, onChunk, pChunkUserData, flags); - if (result != MA_TRUE) { + pWav->allowedMetadataTypes = allowedMetadataTypes; + result = drwav_init__internal(pWav, onChunk, pChunkUserData, flags); + if (result != DRWAV_TRUE) { fclose(pFile); return result; } - return MA_TRUE; + return DRWAV_TRUE; } -MA_API ma_bool32 ma_dr_wav_init_file_ex(ma_dr_wav* pWav, const char* filename, ma_dr_wav_chunk_proc onChunk, void* pChunkUserData, ma_uint32 flags, const ma_allocation_callbacks* pAllocationCallbacks) +DRWAV_API drwav_bool32 drwav_init_file_ex(drwav* pWav, const char* filename, drwav_chunk_proc onChunk, void* pChunkUserData, drwav_uint32 flags, const drwav_allocation_callbacks* pAllocationCallbacks) { FILE* pFile; - if (ma_fopen(&pFile, filename, "rb") != MA_SUCCESS) { - return MA_FALSE; + if (drwav_fopen(&pFile, filename, "rb") != DRWAV_SUCCESS) { + return DRWAV_FALSE; } - return ma_dr_wav_init_file__internal_FILE(pWav, pFile, onChunk, pChunkUserData, flags, pAllocationCallbacks); + return drwav_init_file__internal_FILE(pWav, pFile, onChunk, pChunkUserData, flags, drwav_metadata_type_none, pAllocationCallbacks); } -#ifndef MA_DR_WAV_NO_WCHAR -MA_API ma_bool32 ma_dr_wav_init_file_w(ma_dr_wav* pWav, const wchar_t* filename, const ma_allocation_callbacks* pAllocationCallbacks) +#ifndef DR_WAV_NO_WCHAR +DRWAV_API drwav_bool32 drwav_init_file_w(drwav* pWav, const wchar_t* filename, const drwav_allocation_callbacks* pAllocationCallbacks) { - return ma_dr_wav_init_file_ex_w(pWav, filename, NULL, NULL, 0, pAllocationCallbacks); + return drwav_init_file_ex_w(pWav, filename, NULL, NULL, 0, pAllocationCallbacks); } -MA_API ma_bool32 ma_dr_wav_init_file_ex_w(ma_dr_wav* pWav, const wchar_t* filename, ma_dr_wav_chunk_proc onChunk, void* pChunkUserData, ma_uint32 flags, const ma_allocation_callbacks* pAllocationCallbacks) +DRWAV_API drwav_bool32 drwav_init_file_ex_w(drwav* pWav, const wchar_t* filename, drwav_chunk_proc onChunk, void* pChunkUserData, drwav_uint32 flags, const drwav_allocation_callbacks* pAllocationCallbacks) { FILE* pFile; - if (ma_wfopen(&pFile, filename, L"rb", pAllocationCallbacks) != MA_SUCCESS) { - return MA_FALSE; + if (drwav_wfopen(&pFile, filename, L"rb", pAllocationCallbacks) != DRWAV_SUCCESS) { + return DRWAV_FALSE; } - return ma_dr_wav_init_file__internal_FILE(pWav, pFile, onChunk, pChunkUserData, flags, pAllocationCallbacks); + return drwav_init_file__internal_FILE(pWav, pFile, onChunk, pChunkUserData, flags, drwav_metadata_type_none, pAllocationCallbacks); } #endif -MA_API ma_bool32 ma_dr_wav_init_file_with_metadata(ma_dr_wav* pWav, const char* filename, ma_uint32 flags, const ma_allocation_callbacks* pAllocationCallbacks) +DRWAV_API drwav_bool32 drwav_init_file_with_metadata(drwav* pWav, const char* filename, drwav_uint32 flags, const drwav_allocation_callbacks* pAllocationCallbacks) { FILE* pFile; - if (ma_fopen(&pFile, filename, "rb") != MA_SUCCESS) { - return MA_FALSE; + if (drwav_fopen(&pFile, filename, "rb") != DRWAV_SUCCESS) { + return DRWAV_FALSE; } - return ma_dr_wav_init_file__internal_FILE(pWav, pFile, NULL, NULL, flags | MA_DR_WAV_WITH_METADATA, pAllocationCallbacks); + return drwav_init_file__internal_FILE(pWav, pFile, NULL, NULL, flags, drwav_metadata_type_all_including_unknown, pAllocationCallbacks); } -#ifndef MA_DR_WAV_NO_WCHAR -MA_API ma_bool32 ma_dr_wav_init_file_with_metadata_w(ma_dr_wav* pWav, const wchar_t* filename, ma_uint32 flags, const ma_allocation_callbacks* pAllocationCallbacks) +#ifndef DR_WAV_NO_WCHAR +DRWAV_API drwav_bool32 drwav_init_file_with_metadata_w(drwav* pWav, const wchar_t* filename, drwav_uint32 flags, const drwav_allocation_callbacks* pAllocationCallbacks) { FILE* pFile; - if (ma_wfopen(&pFile, filename, L"rb", pAllocationCallbacks) != MA_SUCCESS) { - return MA_FALSE; + if (drwav_wfopen(&pFile, filename, L"rb", pAllocationCallbacks) != DRWAV_SUCCESS) { + return DRWAV_FALSE; } - return ma_dr_wav_init_file__internal_FILE(pWav, pFile, NULL, NULL, flags | MA_DR_WAV_WITH_METADATA, pAllocationCallbacks); + return drwav_init_file__internal_FILE(pWav, pFile, NULL, NULL, flags, drwav_metadata_type_all_including_unknown, pAllocationCallbacks); } #endif -MA_PRIVATE ma_bool32 ma_dr_wav_init_file_write__internal_FILE(ma_dr_wav* pWav, FILE* pFile, const ma_dr_wav_data_format* pFormat, ma_uint64 totalSampleCount, ma_bool32 isSequential, const ma_allocation_callbacks* pAllocationCallbacks) +DRWAV_PRIVATE drwav_bool32 drwav_init_file_write__internal_FILE(drwav* pWav, FILE* pFile, const drwav_data_format* pFormat, drwav_uint64 totalSampleCount, drwav_bool32 isSequential, const drwav_allocation_callbacks* pAllocationCallbacks) { - ma_bool32 result; - result = ma_dr_wav_preinit_write(pWav, pFormat, isSequential, ma_dr_wav__on_write_stdio, ma_dr_wav__on_seek_stdio, (void*)pFile, pAllocationCallbacks); - if (result != MA_TRUE) { + drwav_bool32 result; + result = drwav_preinit_write(pWav, pFormat, isSequential, drwav__on_write_stdio, drwav__on_seek_stdio, (void*)pFile, pAllocationCallbacks); + if (result != DRWAV_TRUE) { fclose(pFile); return result; } - result = ma_dr_wav_init_write__internal(pWav, pFormat, totalSampleCount); - if (result != MA_TRUE) { + result = drwav_init_write__internal(pWav, pFormat, totalSampleCount); + if (result != DRWAV_TRUE) { fclose(pFile); return result; } - return MA_TRUE; + return DRWAV_TRUE; } -MA_PRIVATE ma_bool32 ma_dr_wav_init_file_write__internal(ma_dr_wav* pWav, const char* filename, const ma_dr_wav_data_format* pFormat, ma_uint64 totalSampleCount, ma_bool32 isSequential, const ma_allocation_callbacks* pAllocationCallbacks) +DRWAV_PRIVATE drwav_bool32 drwav_init_file_write__internal(drwav* pWav, const char* filename, const drwav_data_format* pFormat, drwav_uint64 totalSampleCount, drwav_bool32 isSequential, const drwav_allocation_callbacks* pAllocationCallbacks) { FILE* pFile; - if (ma_fopen(&pFile, filename, "wb") != MA_SUCCESS) { - return MA_FALSE; + if (drwav_fopen(&pFile, filename, "wb") != DRWAV_SUCCESS) { + return DRWAV_FALSE; } - return ma_dr_wav_init_file_write__internal_FILE(pWav, pFile, pFormat, totalSampleCount, isSequential, pAllocationCallbacks); + return drwav_init_file_write__internal_FILE(pWav, pFile, pFormat, totalSampleCount, isSequential, pAllocationCallbacks); } -#ifndef MA_DR_WAV_NO_WCHAR -MA_PRIVATE ma_bool32 ma_dr_wav_init_file_write_w__internal(ma_dr_wav* pWav, const wchar_t* filename, const ma_dr_wav_data_format* pFormat, ma_uint64 totalSampleCount, ma_bool32 isSequential, const ma_allocation_callbacks* pAllocationCallbacks) +#ifndef DR_WAV_NO_WCHAR +DRWAV_PRIVATE drwav_bool32 drwav_init_file_write_w__internal(drwav* pWav, const wchar_t* filename, const drwav_data_format* pFormat, drwav_uint64 totalSampleCount, drwav_bool32 isSequential, const drwav_allocation_callbacks* pAllocationCallbacks) { FILE* pFile; - if (ma_wfopen(&pFile, filename, L"wb", pAllocationCallbacks) != MA_SUCCESS) { - return MA_FALSE; + if (drwav_wfopen(&pFile, filename, L"wb", pAllocationCallbacks) != DRWAV_SUCCESS) { + return DRWAV_FALSE; } - return ma_dr_wav_init_file_write__internal_FILE(pWav, pFile, pFormat, totalSampleCount, isSequential, pAllocationCallbacks); + return drwav_init_file_write__internal_FILE(pWav, pFile, pFormat, totalSampleCount, isSequential, pAllocationCallbacks); } #endif -MA_API ma_bool32 ma_dr_wav_init_file_write(ma_dr_wav* pWav, const char* filename, const ma_dr_wav_data_format* pFormat, const ma_allocation_callbacks* pAllocationCallbacks) +DRWAV_API drwav_bool32 drwav_init_file_write(drwav* pWav, const char* filename, const drwav_data_format* pFormat, const drwav_allocation_callbacks* pAllocationCallbacks) { - return ma_dr_wav_init_file_write__internal(pWav, filename, pFormat, 0, MA_FALSE, pAllocationCallbacks); + return drwav_init_file_write__internal(pWav, filename, pFormat, 0, DRWAV_FALSE, pAllocationCallbacks); } -MA_API ma_bool32 ma_dr_wav_init_file_write_sequential(ma_dr_wav* pWav, const char* filename, const ma_dr_wav_data_format* pFormat, ma_uint64 totalSampleCount, const ma_allocation_callbacks* pAllocationCallbacks) +DRWAV_API drwav_bool32 drwav_init_file_write_sequential(drwav* pWav, const char* filename, const drwav_data_format* pFormat, drwav_uint64 totalSampleCount, const drwav_allocation_callbacks* pAllocationCallbacks) { - return ma_dr_wav_init_file_write__internal(pWav, filename, pFormat, totalSampleCount, MA_TRUE, pAllocationCallbacks); + return drwav_init_file_write__internal(pWav, filename, pFormat, totalSampleCount, DRWAV_TRUE, pAllocationCallbacks); } -MA_API ma_bool32 ma_dr_wav_init_file_write_sequential_pcm_frames(ma_dr_wav* pWav, const char* filename, const ma_dr_wav_data_format* pFormat, ma_uint64 totalPCMFrameCount, const ma_allocation_callbacks* pAllocationCallbacks) +DRWAV_API drwav_bool32 drwav_init_file_write_sequential_pcm_frames(drwav* pWav, const char* filename, const drwav_data_format* pFormat, drwav_uint64 totalPCMFrameCount, const drwav_allocation_callbacks* pAllocationCallbacks) { if (pFormat == NULL) { - return MA_FALSE; + return DRWAV_FALSE; } - return ma_dr_wav_init_file_write_sequential(pWav, filename, pFormat, totalPCMFrameCount*pFormat->channels, pAllocationCallbacks); + return drwav_init_file_write_sequential(pWav, filename, pFormat, totalPCMFrameCount*pFormat->channels, pAllocationCallbacks); } -#ifndef MA_DR_WAV_NO_WCHAR -MA_API ma_bool32 ma_dr_wav_init_file_write_w(ma_dr_wav* pWav, const wchar_t* filename, const ma_dr_wav_data_format* pFormat, const ma_allocation_callbacks* pAllocationCallbacks) +#ifndef DR_WAV_NO_WCHAR +DRWAV_API drwav_bool32 drwav_init_file_write_w(drwav* pWav, const wchar_t* filename, const drwav_data_format* pFormat, const drwav_allocation_callbacks* pAllocationCallbacks) { - return ma_dr_wav_init_file_write_w__internal(pWav, filename, pFormat, 0, MA_FALSE, pAllocationCallbacks); + return drwav_init_file_write_w__internal(pWav, filename, pFormat, 0, DRWAV_FALSE, pAllocationCallbacks); } -MA_API ma_bool32 ma_dr_wav_init_file_write_sequential_w(ma_dr_wav* pWav, const wchar_t* filename, const ma_dr_wav_data_format* pFormat, ma_uint64 totalSampleCount, const ma_allocation_callbacks* pAllocationCallbacks) +DRWAV_API drwav_bool32 drwav_init_file_write_sequential_w(drwav* pWav, const wchar_t* filename, const drwav_data_format* pFormat, drwav_uint64 totalSampleCount, const drwav_allocation_callbacks* pAllocationCallbacks) { - return ma_dr_wav_init_file_write_w__internal(pWav, filename, pFormat, totalSampleCount, MA_TRUE, pAllocationCallbacks); + return drwav_init_file_write_w__internal(pWav, filename, pFormat, totalSampleCount, DRWAV_TRUE, pAllocationCallbacks); } -MA_API ma_bool32 ma_dr_wav_init_file_write_sequential_pcm_frames_w(ma_dr_wav* pWav, const wchar_t* filename, const ma_dr_wav_data_format* pFormat, ma_uint64 totalPCMFrameCount, const ma_allocation_callbacks* pAllocationCallbacks) +DRWAV_API drwav_bool32 drwav_init_file_write_sequential_pcm_frames_w(drwav* pWav, const wchar_t* filename, const drwav_data_format* pFormat, drwav_uint64 totalPCMFrameCount, const drwav_allocation_callbacks* pAllocationCallbacks) { if (pFormat == NULL) { - return MA_FALSE; + return DRWAV_FALSE; } - return ma_dr_wav_init_file_write_sequential_w(pWav, filename, pFormat, totalPCMFrameCount*pFormat->channels, pAllocationCallbacks); + return drwav_init_file_write_sequential_w(pWav, filename, pFormat, totalPCMFrameCount*pFormat->channels, pAllocationCallbacks); } #endif #endif -MA_PRIVATE size_t ma_dr_wav__on_read_memory(void* pUserData, void* pBufferOut, size_t bytesToRead) +DRWAV_PRIVATE size_t drwav__on_read_memory(void* pUserData, void* pBufferOut, size_t bytesToRead) { - ma_dr_wav* pWav = (ma_dr_wav*)pUserData; + drwav* pWav = (drwav*)pUserData; size_t bytesRemaining; - MA_DR_WAV_ASSERT(pWav != NULL); - MA_DR_WAV_ASSERT(pWav->memoryStream.dataSize >= pWav->memoryStream.currentReadPos); + DRWAV_ASSERT(pWav != NULL); + DRWAV_ASSERT(pWav->memoryStream.dataSize >= pWav->memoryStream.currentReadPos); bytesRemaining = pWav->memoryStream.dataSize - pWav->memoryStream.currentReadPos; if (bytesToRead > bytesRemaining) { bytesToRead = bytesRemaining; } if (bytesToRead > 0) { - MA_DR_WAV_COPY_MEMORY(pBufferOut, pWav->memoryStream.data + pWav->memoryStream.currentReadPos, bytesToRead); + DRWAV_COPY_MEMORY(pBufferOut, pWav->memoryStream.data + pWav->memoryStream.currentReadPos, bytesToRead); pWav->memoryStream.currentReadPos += bytesToRead; } return bytesToRead; } -MA_PRIVATE ma_bool32 ma_dr_wav__on_seek_memory(void* pUserData, int offset, ma_dr_wav_seek_origin origin) +DRWAV_PRIVATE drwav_bool32 drwav__on_seek_memory(void* pUserData, int offset, drwav_seek_origin origin) { - ma_dr_wav* pWav = (ma_dr_wav*)pUserData; - MA_DR_WAV_ASSERT(pWav != NULL); - if (origin == ma_dr_wav_seek_origin_current) { + drwav* pWav = (drwav*)pUserData; + DRWAV_ASSERT(pWav != NULL); + if (origin == drwav_seek_origin_current) { if (offset > 0) { if (pWav->memoryStream.currentReadPos + offset > pWav->memoryStream.dataSize) { - return MA_FALSE; + return DRWAV_FALSE; } } else { if (pWav->memoryStream.currentReadPos < (size_t)-offset) { - return MA_FALSE; + return DRWAV_FALSE; } } pWav->memoryStream.currentReadPos += offset; } else { - if ((ma_uint32)offset <= pWav->memoryStream.dataSize) { + if ((drwav_uint32)offset <= pWav->memoryStream.dataSize) { pWav->memoryStream.currentReadPos = offset; } else { - return MA_FALSE; + return DRWAV_FALSE; } } - return MA_TRUE; + return DRWAV_TRUE; } -MA_PRIVATE size_t ma_dr_wav__on_write_memory(void* pUserData, const void* pDataIn, size_t bytesToWrite) +DRWAV_PRIVATE size_t drwav__on_write_memory(void* pUserData, const void* pDataIn, size_t bytesToWrite) { - ma_dr_wav* pWav = (ma_dr_wav*)pUserData; + drwav* pWav = (drwav*)pUserData; size_t bytesRemaining; - MA_DR_WAV_ASSERT(pWav != NULL); - MA_DR_WAV_ASSERT(pWav->memoryStreamWrite.dataCapacity >= pWav->memoryStreamWrite.currentWritePos); + DRWAV_ASSERT(pWav != NULL); + DRWAV_ASSERT(pWav->memoryStreamWrite.dataCapacity >= pWav->memoryStreamWrite.currentWritePos); bytesRemaining = pWav->memoryStreamWrite.dataCapacity - pWav->memoryStreamWrite.currentWritePos; if (bytesRemaining < bytesToWrite) { void* pNewData; @@ -80673,14 +79560,14 @@ MA_PRIVATE size_t ma_dr_wav__on_write_memory(void* pUserData, const void* pDataI if ((newDataCapacity - pWav->memoryStreamWrite.currentWritePos) < bytesToWrite) { newDataCapacity = pWav->memoryStreamWrite.currentWritePos + bytesToWrite; } - pNewData = ma_dr_wav__realloc_from_callbacks(*pWav->memoryStreamWrite.ppData, newDataCapacity, pWav->memoryStreamWrite.dataCapacity, &pWav->allocationCallbacks); + pNewData = drwav__realloc_from_callbacks(*pWav->memoryStreamWrite.ppData, newDataCapacity, pWav->memoryStreamWrite.dataCapacity, &pWav->allocationCallbacks); if (pNewData == NULL) { return 0; } *pWav->memoryStreamWrite.ppData = pNewData; pWav->memoryStreamWrite.dataCapacity = newDataCapacity; } - MA_DR_WAV_COPY_MEMORY(((ma_uint8*)(*pWav->memoryStreamWrite.ppData)) + pWav->memoryStreamWrite.currentWritePos, pDataIn, bytesToWrite); + DRWAV_COPY_MEMORY(((drwav_uint8*)(*pWav->memoryStreamWrite.ppData)) + pWav->memoryStreamWrite.currentWritePos, pDataIn, bytesToWrite); pWav->memoryStreamWrite.currentWritePos += bytesToWrite; if (pWav->memoryStreamWrite.dataSize < pWav->memoryStreamWrite.currentWritePos) { pWav->memoryStreamWrite.dataSize = pWav->memoryStreamWrite.currentWritePos; @@ -80688,11 +79575,11 @@ MA_PRIVATE size_t ma_dr_wav__on_write_memory(void* pUserData, const void* pDataI *pWav->memoryStreamWrite.pDataSize = pWav->memoryStreamWrite.dataSize; return bytesToWrite; } -MA_PRIVATE ma_bool32 ma_dr_wav__on_seek_memory_write(void* pUserData, int offset, ma_dr_wav_seek_origin origin) +DRWAV_PRIVATE drwav_bool32 drwav__on_seek_memory_write(void* pUserData, int offset, drwav_seek_origin origin) { - ma_dr_wav* pWav = (ma_dr_wav*)pUserData; - MA_DR_WAV_ASSERT(pWav != NULL); - if (origin == ma_dr_wav_seek_origin_current) { + drwav* pWav = (drwav*)pUserData; + DRWAV_ASSERT(pWav != NULL); + if (origin == drwav_seek_origin_current) { if (offset > 0) { if (pWav->memoryStreamWrite.currentWritePos + offset > pWav->memoryStreamWrite.dataSize) { offset = (int)(pWav->memoryStreamWrite.dataSize - pWav->memoryStreamWrite.currentWritePos); @@ -80704,143 +79591,146 @@ MA_PRIVATE ma_bool32 ma_dr_wav__on_seek_memory_write(void* pUserData, int offset } pWav->memoryStreamWrite.currentWritePos += offset; } else { - if ((ma_uint32)offset <= pWav->memoryStreamWrite.dataSize) { + if ((drwav_uint32)offset <= pWav->memoryStreamWrite.dataSize) { pWav->memoryStreamWrite.currentWritePos = offset; } else { pWav->memoryStreamWrite.currentWritePos = pWav->memoryStreamWrite.dataSize; } } - return MA_TRUE; + return DRWAV_TRUE; } -MA_API ma_bool32 ma_dr_wav_init_memory(ma_dr_wav* pWav, const void* data, size_t dataSize, const ma_allocation_callbacks* pAllocationCallbacks) +DRWAV_API drwav_bool32 drwav_init_memory(drwav* pWav, const void* data, size_t dataSize, const drwav_allocation_callbacks* pAllocationCallbacks) { - return ma_dr_wav_init_memory_ex(pWav, data, dataSize, NULL, NULL, 0, pAllocationCallbacks); + return drwav_init_memory_ex(pWav, data, dataSize, NULL, NULL, 0, pAllocationCallbacks); } -MA_API ma_bool32 ma_dr_wav_init_memory_ex(ma_dr_wav* pWav, const void* data, size_t dataSize, ma_dr_wav_chunk_proc onChunk, void* pChunkUserData, ma_uint32 flags, const ma_allocation_callbacks* pAllocationCallbacks) +DRWAV_API drwav_bool32 drwav_init_memory_ex(drwav* pWav, const void* data, size_t dataSize, drwav_chunk_proc onChunk, void* pChunkUserData, drwav_uint32 flags, const drwav_allocation_callbacks* pAllocationCallbacks) { if (data == NULL || dataSize == 0) { - return MA_FALSE; + return DRWAV_FALSE; } - if (!ma_dr_wav_preinit(pWav, ma_dr_wav__on_read_memory, ma_dr_wav__on_seek_memory, pWav, pAllocationCallbacks)) { - return MA_FALSE; + if (!drwav_preinit(pWav, drwav__on_read_memory, drwav__on_seek_memory, pWav, pAllocationCallbacks)) { + return DRWAV_FALSE; } - pWav->memoryStream.data = (const ma_uint8*)data; + pWav->memoryStream.data = (const drwav_uint8*)data; pWav->memoryStream.dataSize = dataSize; pWav->memoryStream.currentReadPos = 0; - return ma_dr_wav_init__internal(pWav, onChunk, pChunkUserData, flags); + return drwav_init__internal(pWav, onChunk, pChunkUserData, flags); } -MA_API ma_bool32 ma_dr_wav_init_memory_with_metadata(ma_dr_wav* pWav, const void* data, size_t dataSize, ma_uint32 flags, const ma_allocation_callbacks* pAllocationCallbacks) +DRWAV_API drwav_bool32 drwav_init_memory_with_metadata(drwav* pWav, const void* data, size_t dataSize, drwav_uint32 flags, const drwav_allocation_callbacks* pAllocationCallbacks) { if (data == NULL || dataSize == 0) { - return MA_FALSE; + return DRWAV_FALSE; } - if (!ma_dr_wav_preinit(pWav, ma_dr_wav__on_read_memory, ma_dr_wav__on_seek_memory, pWav, pAllocationCallbacks)) { - return MA_FALSE; + if (!drwav_preinit(pWav, drwav__on_read_memory, drwav__on_seek_memory, pWav, pAllocationCallbacks)) { + return DRWAV_FALSE; } - pWav->memoryStream.data = (const ma_uint8*)data; + pWav->memoryStream.data = (const drwav_uint8*)data; pWav->memoryStream.dataSize = dataSize; pWav->memoryStream.currentReadPos = 0; - return ma_dr_wav_init__internal(pWav, NULL, NULL, flags | MA_DR_WAV_WITH_METADATA); + pWav->allowedMetadataTypes = drwav_metadata_type_all_including_unknown; + return drwav_init__internal(pWav, NULL, NULL, flags); } -MA_PRIVATE ma_bool32 ma_dr_wav_init_memory_write__internal(ma_dr_wav* pWav, void** ppData, size_t* pDataSize, const ma_dr_wav_data_format* pFormat, ma_uint64 totalSampleCount, ma_bool32 isSequential, const ma_allocation_callbacks* pAllocationCallbacks) +DRWAV_PRIVATE drwav_bool32 drwav_init_memory_write__internal(drwav* pWav, void** ppData, size_t* pDataSize, const drwav_data_format* pFormat, drwav_uint64 totalSampleCount, drwav_bool32 isSequential, const drwav_allocation_callbacks* pAllocationCallbacks) { if (ppData == NULL || pDataSize == NULL) { - return MA_FALSE; + return DRWAV_FALSE; } *ppData = NULL; *pDataSize = 0; - if (!ma_dr_wav_preinit_write(pWav, pFormat, isSequential, ma_dr_wav__on_write_memory, ma_dr_wav__on_seek_memory_write, pWav, pAllocationCallbacks)) { - return MA_FALSE; + if (!drwav_preinit_write(pWav, pFormat, isSequential, drwav__on_write_memory, drwav__on_seek_memory_write, pWav, pAllocationCallbacks)) { + return DRWAV_FALSE; } pWav->memoryStreamWrite.ppData = ppData; pWav->memoryStreamWrite.pDataSize = pDataSize; pWav->memoryStreamWrite.dataSize = 0; pWav->memoryStreamWrite.dataCapacity = 0; pWav->memoryStreamWrite.currentWritePos = 0; - return ma_dr_wav_init_write__internal(pWav, pFormat, totalSampleCount); + return drwav_init_write__internal(pWav, pFormat, totalSampleCount); } -MA_API ma_bool32 ma_dr_wav_init_memory_write(ma_dr_wav* pWav, void** ppData, size_t* pDataSize, const ma_dr_wav_data_format* pFormat, const ma_allocation_callbacks* pAllocationCallbacks) +DRWAV_API drwav_bool32 drwav_init_memory_write(drwav* pWav, void** ppData, size_t* pDataSize, const drwav_data_format* pFormat, const drwav_allocation_callbacks* pAllocationCallbacks) { - return ma_dr_wav_init_memory_write__internal(pWav, ppData, pDataSize, pFormat, 0, MA_FALSE, pAllocationCallbacks); + return drwav_init_memory_write__internal(pWav, ppData, pDataSize, pFormat, 0, DRWAV_FALSE, pAllocationCallbacks); } -MA_API ma_bool32 ma_dr_wav_init_memory_write_sequential(ma_dr_wav* pWav, void** ppData, size_t* pDataSize, const ma_dr_wav_data_format* pFormat, ma_uint64 totalSampleCount, const ma_allocation_callbacks* pAllocationCallbacks) +DRWAV_API drwav_bool32 drwav_init_memory_write_sequential(drwav* pWav, void** ppData, size_t* pDataSize, const drwav_data_format* pFormat, drwav_uint64 totalSampleCount, const drwav_allocation_callbacks* pAllocationCallbacks) { - return ma_dr_wav_init_memory_write__internal(pWav, ppData, pDataSize, pFormat, totalSampleCount, MA_TRUE, pAllocationCallbacks); + return drwav_init_memory_write__internal(pWav, ppData, pDataSize, pFormat, totalSampleCount, DRWAV_TRUE, pAllocationCallbacks); } -MA_API ma_bool32 ma_dr_wav_init_memory_write_sequential_pcm_frames(ma_dr_wav* pWav, void** ppData, size_t* pDataSize, const ma_dr_wav_data_format* pFormat, ma_uint64 totalPCMFrameCount, const ma_allocation_callbacks* pAllocationCallbacks) +DRWAV_API drwav_bool32 drwav_init_memory_write_sequential_pcm_frames(drwav* pWav, void** ppData, size_t* pDataSize, const drwav_data_format* pFormat, drwav_uint64 totalPCMFrameCount, const drwav_allocation_callbacks* pAllocationCallbacks) { if (pFormat == NULL) { - return MA_FALSE; + return DRWAV_FALSE; } - return ma_dr_wav_init_memory_write_sequential(pWav, ppData, pDataSize, pFormat, totalPCMFrameCount*pFormat->channels, pAllocationCallbacks); + return drwav_init_memory_write_sequential(pWav, ppData, pDataSize, pFormat, totalPCMFrameCount*pFormat->channels, pAllocationCallbacks); } -MA_API ma_result ma_dr_wav_uninit(ma_dr_wav* pWav) +DRWAV_API drwav_result drwav_uninit(drwav* pWav) { - ma_result result = MA_SUCCESS; + drwav_result result = DRWAV_SUCCESS; if (pWav == NULL) { - return MA_INVALID_ARGS; + return DRWAV_INVALID_ARGS; } if (pWav->onWrite != NULL) { - ma_uint32 paddingSize = 0; - if (pWav->container == ma_dr_wav_container_riff || pWav->container == ma_dr_wav_container_rf64) { - paddingSize = ma_dr_wav__chunk_padding_size_riff(pWav->dataChunkDataSize); + drwav_uint32 paddingSize = 0; + if (pWav->container == drwav_container_riff || pWav->container == drwav_container_rf64) { + paddingSize = drwav__chunk_padding_size_riff(pWav->dataChunkDataSize); } else { - paddingSize = ma_dr_wav__chunk_padding_size_w64(pWav->dataChunkDataSize); + paddingSize = drwav__chunk_padding_size_w64(pWav->dataChunkDataSize); } if (paddingSize > 0) { - ma_uint64 paddingData = 0; - ma_dr_wav__write(pWav, &paddingData, paddingSize); + drwav_uint64 paddingData = 0; + drwav__write(pWav, &paddingData, paddingSize); } if (pWav->onSeek && !pWav->isSequentialWrite) { - if (pWav->container == ma_dr_wav_container_riff) { - if (pWav->onSeek(pWav->pUserData, 4, ma_dr_wav_seek_origin_start)) { - ma_uint32 riffChunkSize = ma_dr_wav__riff_chunk_size_riff(pWav->dataChunkDataSize, pWav->pMetadata, pWav->metadataCount); - ma_dr_wav__write_u32ne_to_le(pWav, riffChunkSize); + if (pWav->container == drwav_container_riff) { + if (pWav->onSeek(pWav->pUserData, 4, drwav_seek_origin_start)) { + drwav_uint32 riffChunkSize = drwav__riff_chunk_size_riff(pWav->dataChunkDataSize, pWav->pMetadata, pWav->metadataCount); + drwav__write_u32ne_to_le(pWav, riffChunkSize); } - if (pWav->onSeek(pWav->pUserData, (int)pWav->dataChunkDataPos - 4, ma_dr_wav_seek_origin_start)) { - ma_uint32 dataChunkSize = ma_dr_wav__data_chunk_size_riff(pWav->dataChunkDataSize); - ma_dr_wav__write_u32ne_to_le(pWav, dataChunkSize); + if (pWav->onSeek(pWav->pUserData, (int)pWav->dataChunkDataPos - 4, drwav_seek_origin_start)) { + drwav_uint32 dataChunkSize = drwav__data_chunk_size_riff(pWav->dataChunkDataSize); + drwav__write_u32ne_to_le(pWav, dataChunkSize); } - } else if (pWav->container == ma_dr_wav_container_w64) { - if (pWav->onSeek(pWav->pUserData, 16, ma_dr_wav_seek_origin_start)) { - ma_uint64 riffChunkSize = ma_dr_wav__riff_chunk_size_w64(pWav->dataChunkDataSize); - ma_dr_wav__write_u64ne_to_le(pWav, riffChunkSize); + } else if (pWav->container == drwav_container_w64) { + if (pWav->onSeek(pWav->pUserData, 16, drwav_seek_origin_start)) { + drwav_uint64 riffChunkSize = drwav__riff_chunk_size_w64(pWav->dataChunkDataSize); + drwav__write_u64ne_to_le(pWav, riffChunkSize); } - if (pWav->onSeek(pWav->pUserData, (int)pWav->dataChunkDataPos - 8, ma_dr_wav_seek_origin_start)) { - ma_uint64 dataChunkSize = ma_dr_wav__data_chunk_size_w64(pWav->dataChunkDataSize); - ma_dr_wav__write_u64ne_to_le(pWav, dataChunkSize); + if (pWav->onSeek(pWav->pUserData, (int)pWav->dataChunkDataPos - 8, drwav_seek_origin_start)) { + drwav_uint64 dataChunkSize = drwav__data_chunk_size_w64(pWav->dataChunkDataSize); + drwav__write_u64ne_to_le(pWav, dataChunkSize); } - } else if (pWav->container == ma_dr_wav_container_rf64) { + } else if (pWav->container == drwav_container_rf64) { int ds64BodyPos = 12 + 8; - if (pWav->onSeek(pWav->pUserData, ds64BodyPos + 0, ma_dr_wav_seek_origin_start)) { - ma_uint64 riffChunkSize = ma_dr_wav__riff_chunk_size_rf64(pWav->dataChunkDataSize, pWav->pMetadata, pWav->metadataCount); - ma_dr_wav__write_u64ne_to_le(pWav, riffChunkSize); + if (pWav->onSeek(pWav->pUserData, ds64BodyPos + 0, drwav_seek_origin_start)) { + drwav_uint64 riffChunkSize = drwav__riff_chunk_size_rf64(pWav->dataChunkDataSize, pWav->pMetadata, pWav->metadataCount); + drwav__write_u64ne_to_le(pWav, riffChunkSize); } - if (pWav->onSeek(pWav->pUserData, ds64BodyPos + 8, ma_dr_wav_seek_origin_start)) { - ma_uint64 dataChunkSize = ma_dr_wav__data_chunk_size_rf64(pWav->dataChunkDataSize); - ma_dr_wav__write_u64ne_to_le(pWav, dataChunkSize); + if (pWav->onSeek(pWav->pUserData, ds64BodyPos + 8, drwav_seek_origin_start)) { + drwav_uint64 dataChunkSize = drwav__data_chunk_size_rf64(pWav->dataChunkDataSize); + drwav__write_u64ne_to_le(pWav, dataChunkSize); } } } if (pWav->isSequentialWrite) { if (pWav->dataChunkDataSize != pWav->dataChunkDataSizeTargetWrite) { - result = MA_INVALID_FILE; + result = DRWAV_INVALID_FILE; } } } else { - ma_dr_wav_free(pWav->pMetadata, &pWav->allocationCallbacks); + if (pWav->pMetadata != NULL) { + pWav->allocationCallbacks.onFree(pWav->pMetadata, pWav->allocationCallbacks.pUserData); + } } -#ifndef MA_DR_WAV_NO_STDIO - if (pWav->onRead == ma_dr_wav__on_read_stdio || pWav->onWrite == ma_dr_wav__on_write_stdio) { +#ifndef DR_WAV_NO_STDIO + if (pWav->onRead == drwav__on_read_stdio || pWav->onWrite == drwav__on_write_stdio) { fclose((FILE*)pWav->pUserData); } #endif return result; } -MA_API size_t ma_dr_wav_read_raw(ma_dr_wav* pWav, size_t bytesToRead, void* pBufferOut) +DRWAV_API size_t drwav_read_raw(drwav* pWav, size_t bytesToRead, void* pBufferOut) { size_t bytesRead; - ma_uint32 bytesPerFrame; + drwav_uint32 bytesPerFrame; if (pWav == NULL || bytesToRead == 0) { return 0; } @@ -80850,7 +79740,7 @@ MA_API size_t ma_dr_wav_read_raw(ma_dr_wav* pWav, size_t bytesToRead, void* pBuf if (bytesToRead == 0) { return 0; } - bytesPerFrame = ma_dr_wav_get_bytes_per_pcm_frame(pWav); + bytesPerFrame = drwav_get_bytes_per_pcm_frame(pWav); if (bytesPerFrame == 0) { return 0; } @@ -80863,13 +79753,13 @@ MA_API size_t ma_dr_wav_read_raw(ma_dr_wav* pWav, size_t bytesToRead, void* pBuf if (bytesToSeek > 0x7FFFFFFF) { bytesToSeek = 0x7FFFFFFF; } - if (pWav->onSeek(pWav->pUserData, (int)bytesToSeek, ma_dr_wav_seek_origin_current) == MA_FALSE) { + if (pWav->onSeek(pWav->pUserData, (int)bytesToSeek, drwav_seek_origin_current) == DRWAV_FALSE) { break; } bytesRead += bytesToSeek; } while (bytesRead < bytesToRead) { - ma_uint8 buffer[4096]; + drwav_uint8 buffer[4096]; size_t bytesSeeked; size_t bytesToSeek = (bytesToRead - bytesRead); if (bytesToSeek > sizeof(buffer)) { @@ -80886,198 +79776,171 @@ MA_API size_t ma_dr_wav_read_raw(ma_dr_wav* pWav, size_t bytesToRead, void* pBuf pWav->bytesRemaining -= bytesRead; return bytesRead; } -MA_API ma_uint64 ma_dr_wav_read_pcm_frames_le(ma_dr_wav* pWav, ma_uint64 framesToRead, void* pBufferOut) +DRWAV_API drwav_uint64 drwav_read_pcm_frames_le(drwav* pWav, drwav_uint64 framesToRead, void* pBufferOut) { - ma_uint32 bytesPerFrame; - ma_uint64 bytesToRead; - ma_uint64 framesRemainingInFile; + drwav_uint32 bytesPerFrame; + drwav_uint64 bytesToRead; if (pWav == NULL || framesToRead == 0) { return 0; } - if (ma_dr_wav__is_compressed_format_tag(pWav->translatedFormatTag)) { + if (drwav__is_compressed_format_tag(pWav->translatedFormatTag)) { return 0; } - framesRemainingInFile = pWav->totalPCMFrameCount - pWav->readCursorInPCMFrames; - if (framesToRead > framesRemainingInFile) { - framesToRead = framesRemainingInFile; - } - bytesPerFrame = ma_dr_wav_get_bytes_per_pcm_frame(pWav); + bytesPerFrame = drwav_get_bytes_per_pcm_frame(pWav); if (bytesPerFrame == 0) { return 0; } bytesToRead = framesToRead * bytesPerFrame; - if (bytesToRead > MA_SIZE_MAX) { - bytesToRead = (MA_SIZE_MAX / bytesPerFrame) * bytesPerFrame; + if (bytesToRead > DRWAV_SIZE_MAX) { + bytesToRead = (DRWAV_SIZE_MAX / bytesPerFrame) * bytesPerFrame; } if (bytesToRead == 0) { return 0; } - return ma_dr_wav_read_raw(pWav, (size_t)bytesToRead, pBufferOut) / bytesPerFrame; + return drwav_read_raw(pWav, (size_t)bytesToRead, pBufferOut) / bytesPerFrame; } -MA_API ma_uint64 ma_dr_wav_read_pcm_frames_be(ma_dr_wav* pWav, ma_uint64 framesToRead, void* pBufferOut) +DRWAV_API drwav_uint64 drwav_read_pcm_frames_be(drwav* pWav, drwav_uint64 framesToRead, void* pBufferOut) { - ma_uint64 framesRead = ma_dr_wav_read_pcm_frames_le(pWav, framesToRead, pBufferOut); + drwav_uint64 framesRead = drwav_read_pcm_frames_le(pWav, framesToRead, pBufferOut); if (pBufferOut != NULL) { - ma_uint32 bytesPerFrame = ma_dr_wav_get_bytes_per_pcm_frame(pWav); + drwav_uint32 bytesPerFrame = drwav_get_bytes_per_pcm_frame(pWav); if (bytesPerFrame == 0) { return 0; } - ma_dr_wav__bswap_samples(pBufferOut, framesRead*pWav->channels, bytesPerFrame/pWav->channels); + drwav__bswap_samples(pBufferOut, framesRead*pWav->channels, bytesPerFrame/pWav->channels, pWav->translatedFormatTag); } return framesRead; } -MA_API ma_uint64 ma_dr_wav_read_pcm_frames(ma_dr_wav* pWav, ma_uint64 framesToRead, void* pBufferOut) +DRWAV_API drwav_uint64 drwav_read_pcm_frames(drwav* pWav, drwav_uint64 framesToRead, void* pBufferOut) { - ma_uint64 framesRead = 0; - if (ma_dr_wav_is_container_be(pWav->container)) { - if (pWav->container != ma_dr_wav_container_aiff || pWav->aiff.isLE == MA_FALSE) { - if (ma_dr_wav__is_little_endian()) { - framesRead = ma_dr_wav_read_pcm_frames_be(pWav, framesToRead, pBufferOut); - } else { - framesRead = ma_dr_wav_read_pcm_frames_le(pWav, framesToRead, pBufferOut); - } - goto post_process; - } - } - if (ma_dr_wav__is_little_endian()) { - framesRead = ma_dr_wav_read_pcm_frames_le(pWav, framesToRead, pBufferOut); + if (drwav__is_little_endian()) { + return drwav_read_pcm_frames_le(pWav, framesToRead, pBufferOut); } else { - framesRead = ma_dr_wav_read_pcm_frames_be(pWav, framesToRead, pBufferOut); + return drwav_read_pcm_frames_be(pWav, framesToRead, pBufferOut); } - post_process: - { - if (pWav->container == ma_dr_wav_container_aiff && pWav->bitsPerSample == 8 && pWav->aiff.isUnsigned == MA_FALSE) { - if (pBufferOut != NULL) { - ma_uint64 iSample; - for (iSample = 0; iSample < framesRead * pWav->channels; iSample += 1) { - ((ma_uint8*)pBufferOut)[iSample] += 128; - } - } - } - } - return framesRead; } -MA_PRIVATE ma_bool32 ma_dr_wav_seek_to_first_pcm_frame(ma_dr_wav* pWav) +DRWAV_PRIVATE drwav_bool32 drwav_seek_to_first_pcm_frame(drwav* pWav) { if (pWav->onWrite != NULL) { - return MA_FALSE; + return DRWAV_FALSE; } - if (!pWav->onSeek(pWav->pUserData, (int)pWav->dataChunkDataPos, ma_dr_wav_seek_origin_start)) { - return MA_FALSE; + if (!pWav->onSeek(pWav->pUserData, (int)pWav->dataChunkDataPos, drwav_seek_origin_start)) { + return DRWAV_FALSE; } - if (ma_dr_wav__is_compressed_format_tag(pWav->translatedFormatTag)) { - if (pWav->translatedFormatTag == MA_DR_WAVE_FORMAT_ADPCM) { - MA_DR_WAV_ZERO_OBJECT(&pWav->msadpcm); - } else if (pWav->translatedFormatTag == MA_DR_WAVE_FORMAT_DVI_ADPCM) { - MA_DR_WAV_ZERO_OBJECT(&pWav->ima); + if (drwav__is_compressed_format_tag(pWav->translatedFormatTag)) { + if (pWav->translatedFormatTag == DR_WAVE_FORMAT_ADPCM) { + DRWAV_ZERO_OBJECT(&pWav->msadpcm); + } else if (pWav->translatedFormatTag == DR_WAVE_FORMAT_DVI_ADPCM) { + DRWAV_ZERO_OBJECT(&pWav->ima); } else { - MA_DR_WAV_ASSERT(MA_FALSE); + DRWAV_ASSERT(DRWAV_FALSE); } } pWav->readCursorInPCMFrames = 0; pWav->bytesRemaining = pWav->dataChunkDataSize; - return MA_TRUE; + return DRWAV_TRUE; } -MA_API ma_bool32 ma_dr_wav_seek_to_pcm_frame(ma_dr_wav* pWav, ma_uint64 targetFrameIndex) +DRWAV_API drwav_bool32 drwav_seek_to_pcm_frame(drwav* pWav, drwav_uint64 targetFrameIndex) { if (pWav == NULL || pWav->onSeek == NULL) { - return MA_FALSE; + return DRWAV_FALSE; } if (pWav->onWrite != NULL) { - return MA_FALSE; + return DRWAV_FALSE; } if (pWav->totalPCMFrameCount == 0) { - return MA_TRUE; + return DRWAV_TRUE; } if (targetFrameIndex > pWav->totalPCMFrameCount) { targetFrameIndex = pWav->totalPCMFrameCount; } - if (ma_dr_wav__is_compressed_format_tag(pWav->translatedFormatTag)) { + if (drwav__is_compressed_format_tag(pWav->translatedFormatTag)) { if (targetFrameIndex < pWav->readCursorInPCMFrames) { - if (!ma_dr_wav_seek_to_first_pcm_frame(pWav)) { - return MA_FALSE; + if (!drwav_seek_to_first_pcm_frame(pWav)) { + return DRWAV_FALSE; } } if (targetFrameIndex > pWav->readCursorInPCMFrames) { - ma_uint64 offsetInFrames = targetFrameIndex - pWav->readCursorInPCMFrames; - ma_int16 devnull[2048]; + drwav_uint64 offsetInFrames = targetFrameIndex - pWav->readCursorInPCMFrames; + drwav_int16 devnull[2048]; while (offsetInFrames > 0) { - ma_uint64 framesRead = 0; - ma_uint64 framesToRead = offsetInFrames; - if (framesToRead > ma_dr_wav_countof(devnull)/pWav->channels) { - framesToRead = ma_dr_wav_countof(devnull)/pWav->channels; + drwav_uint64 framesRead = 0; + drwav_uint64 framesToRead = offsetInFrames; + if (framesToRead > drwav_countof(devnull)/pWav->channels) { + framesToRead = drwav_countof(devnull)/pWav->channels; } - if (pWav->translatedFormatTag == MA_DR_WAVE_FORMAT_ADPCM) { - framesRead = ma_dr_wav_read_pcm_frames_s16__msadpcm(pWav, framesToRead, devnull); - } else if (pWav->translatedFormatTag == MA_DR_WAVE_FORMAT_DVI_ADPCM) { - framesRead = ma_dr_wav_read_pcm_frames_s16__ima(pWav, framesToRead, devnull); + if (pWav->translatedFormatTag == DR_WAVE_FORMAT_ADPCM) { + framesRead = drwav_read_pcm_frames_s16__msadpcm(pWav, framesToRead, devnull); + } else if (pWav->translatedFormatTag == DR_WAVE_FORMAT_DVI_ADPCM) { + framesRead = drwav_read_pcm_frames_s16__ima(pWav, framesToRead, devnull); } else { - MA_DR_WAV_ASSERT(MA_FALSE); + DRWAV_ASSERT(DRWAV_FALSE); } if (framesRead != framesToRead) { - return MA_FALSE; + return DRWAV_FALSE; } offsetInFrames -= framesRead; } } } else { - ma_uint64 totalSizeInBytes; - ma_uint64 currentBytePos; - ma_uint64 targetBytePos; - ma_uint64 offset; - ma_uint32 bytesPerFrame; - bytesPerFrame = ma_dr_wav_get_bytes_per_pcm_frame(pWav); + drwav_uint64 totalSizeInBytes; + drwav_uint64 currentBytePos; + drwav_uint64 targetBytePos; + drwav_uint64 offset; + drwav_uint32 bytesPerFrame; + bytesPerFrame = drwav_get_bytes_per_pcm_frame(pWav); if (bytesPerFrame == 0) { - return MA_FALSE; + return DRWAV_FALSE; } totalSizeInBytes = pWav->totalPCMFrameCount * bytesPerFrame; + DRWAV_ASSERT(totalSizeInBytes >= pWav->bytesRemaining); currentBytePos = totalSizeInBytes - pWav->bytesRemaining; targetBytePos = targetFrameIndex * bytesPerFrame; if (currentBytePos < targetBytePos) { offset = (targetBytePos - currentBytePos); } else { - if (!ma_dr_wav_seek_to_first_pcm_frame(pWav)) { - return MA_FALSE; + if (!drwav_seek_to_first_pcm_frame(pWav)) { + return DRWAV_FALSE; } offset = targetBytePos; } while (offset > 0) { int offset32 = ((offset > INT_MAX) ? INT_MAX : (int)offset); - if (!pWav->onSeek(pWav->pUserData, offset32, ma_dr_wav_seek_origin_current)) { - return MA_FALSE; + if (!pWav->onSeek(pWav->pUserData, offset32, drwav_seek_origin_current)) { + return DRWAV_FALSE; } pWav->readCursorInPCMFrames += offset32 / bytesPerFrame; pWav->bytesRemaining -= offset32; offset -= offset32; } } - return MA_TRUE; + return DRWAV_TRUE; } -MA_API ma_result ma_dr_wav_get_cursor_in_pcm_frames(ma_dr_wav* pWav, ma_uint64* pCursor) +DRWAV_API drwav_result drwav_get_cursor_in_pcm_frames(drwav* pWav, drwav_uint64* pCursor) { if (pCursor == NULL) { - return MA_INVALID_ARGS; + return DRWAV_INVALID_ARGS; } *pCursor = 0; if (pWav == NULL) { - return MA_INVALID_ARGS; + return DRWAV_INVALID_ARGS; } *pCursor = pWav->readCursorInPCMFrames; - return MA_SUCCESS; + return DRWAV_SUCCESS; } -MA_API ma_result ma_dr_wav_get_length_in_pcm_frames(ma_dr_wav* pWav, ma_uint64* pLength) +DRWAV_API drwav_result drwav_get_length_in_pcm_frames(drwav* pWav, drwav_uint64* pLength) { if (pLength == NULL) { - return MA_INVALID_ARGS; + return DRWAV_INVALID_ARGS; } *pLength = 0; if (pWav == NULL) { - return MA_INVALID_ARGS; + return DRWAV_INVALID_ARGS; } *pLength = pWav->totalPCMFrameCount; - return MA_SUCCESS; + return DRWAV_SUCCESS; } -MA_API size_t ma_dr_wav_write_raw(ma_dr_wav* pWav, size_t bytesToWrite, const void* pData) +DRWAV_API size_t drwav_write_raw(drwav* pWav, size_t bytesToWrite, const void* pData) { size_t bytesWritten; if (pWav == NULL || bytesToWrite == 0 || pData == NULL) { @@ -81087,26 +79950,26 @@ MA_API size_t ma_dr_wav_write_raw(ma_dr_wav* pWav, size_t bytesToWrite, const vo pWav->dataChunkDataSize += bytesWritten; return bytesWritten; } -MA_API ma_uint64 ma_dr_wav_write_pcm_frames_le(ma_dr_wav* pWav, ma_uint64 framesToWrite, const void* pData) +DRWAV_API drwav_uint64 drwav_write_pcm_frames_le(drwav* pWav, drwav_uint64 framesToWrite, const void* pData) { - ma_uint64 bytesToWrite; - ma_uint64 bytesWritten; - const ma_uint8* pRunningData; + drwav_uint64 bytesToWrite; + drwav_uint64 bytesWritten; + const drwav_uint8* pRunningData; if (pWav == NULL || framesToWrite == 0 || pData == NULL) { return 0; } bytesToWrite = ((framesToWrite * pWav->channels * pWav->bitsPerSample) / 8); - if (bytesToWrite > MA_SIZE_MAX) { + if (bytesToWrite > DRWAV_SIZE_MAX) { return 0; } bytesWritten = 0; - pRunningData = (const ma_uint8*)pData; + pRunningData = (const drwav_uint8*)pData; while (bytesToWrite > 0) { size_t bytesJustWritten; - ma_uint64 bytesToWriteThisIteration; + drwav_uint64 bytesToWriteThisIteration; bytesToWriteThisIteration = bytesToWrite; - MA_DR_WAV_ASSERT(bytesToWriteThisIteration <= MA_SIZE_MAX); - bytesJustWritten = ma_dr_wav_write_raw(pWav, (size_t)bytesToWriteThisIteration, pRunningData); + DRWAV_ASSERT(bytesToWriteThisIteration <= DRWAV_SIZE_MAX); + bytesJustWritten = drwav_write_raw(pWav, (size_t)bytesToWriteThisIteration, pRunningData); if (bytesJustWritten == 0) { break; } @@ -81116,39 +79979,39 @@ MA_API ma_uint64 ma_dr_wav_write_pcm_frames_le(ma_dr_wav* pWav, ma_uint64 frames } return (bytesWritten * 8) / pWav->bitsPerSample / pWav->channels; } -MA_API ma_uint64 ma_dr_wav_write_pcm_frames_be(ma_dr_wav* pWav, ma_uint64 framesToWrite, const void* pData) +DRWAV_API drwav_uint64 drwav_write_pcm_frames_be(drwav* pWav, drwav_uint64 framesToWrite, const void* pData) { - ma_uint64 bytesToWrite; - ma_uint64 bytesWritten; - ma_uint32 bytesPerSample; - const ma_uint8* pRunningData; + drwav_uint64 bytesToWrite; + drwav_uint64 bytesWritten; + drwav_uint32 bytesPerSample; + const drwav_uint8* pRunningData; if (pWav == NULL || framesToWrite == 0 || pData == NULL) { return 0; } bytesToWrite = ((framesToWrite * pWav->channels * pWav->bitsPerSample) / 8); - if (bytesToWrite > MA_SIZE_MAX) { + if (bytesToWrite > DRWAV_SIZE_MAX) { return 0; } bytesWritten = 0; - pRunningData = (const ma_uint8*)pData; - bytesPerSample = ma_dr_wav_get_bytes_per_pcm_frame(pWav) / pWav->channels; + pRunningData = (const drwav_uint8*)pData; + bytesPerSample = drwav_get_bytes_per_pcm_frame(pWav) / pWav->channels; if (bytesPerSample == 0) { return 0; } while (bytesToWrite > 0) { - ma_uint8 temp[4096]; - ma_uint32 sampleCount; + drwav_uint8 temp[4096]; + drwav_uint32 sampleCount; size_t bytesJustWritten; - ma_uint64 bytesToWriteThisIteration; + drwav_uint64 bytesToWriteThisIteration; bytesToWriteThisIteration = bytesToWrite; - MA_DR_WAV_ASSERT(bytesToWriteThisIteration <= MA_SIZE_MAX); + DRWAV_ASSERT(bytesToWriteThisIteration <= DRWAV_SIZE_MAX); sampleCount = sizeof(temp)/bytesPerSample; - if (bytesToWriteThisIteration > ((ma_uint64)sampleCount)*bytesPerSample) { - bytesToWriteThisIteration = ((ma_uint64)sampleCount)*bytesPerSample; + if (bytesToWriteThisIteration > ((drwav_uint64)sampleCount)*bytesPerSample) { + bytesToWriteThisIteration = ((drwav_uint64)sampleCount)*bytesPerSample; } - MA_DR_WAV_COPY_MEMORY(temp, pRunningData, (size_t)bytesToWriteThisIteration); - ma_dr_wav__bswap_samples(temp, sampleCount, bytesPerSample); - bytesJustWritten = ma_dr_wav_write_raw(pWav, (size_t)bytesToWriteThisIteration, temp); + DRWAV_COPY_MEMORY(temp, pRunningData, (size_t)bytesToWriteThisIteration); + drwav__bswap_samples(temp, sampleCount, bytesPerSample, pWav->translatedFormatTag); + bytesJustWritten = drwav_write_raw(pWav, (size_t)bytesToWriteThisIteration, temp); if (bytesJustWritten == 0) { break; } @@ -81158,73 +80021,61 @@ MA_API ma_uint64 ma_dr_wav_write_pcm_frames_be(ma_dr_wav* pWav, ma_uint64 frames } return (bytesWritten * 8) / pWav->bitsPerSample / pWav->channels; } -MA_API ma_uint64 ma_dr_wav_write_pcm_frames(ma_dr_wav* pWav, ma_uint64 framesToWrite, const void* pData) +DRWAV_API drwav_uint64 drwav_write_pcm_frames(drwav* pWav, drwav_uint64 framesToWrite, const void* pData) { - if (ma_dr_wav__is_little_endian()) { - return ma_dr_wav_write_pcm_frames_le(pWav, framesToWrite, pData); + if (drwav__is_little_endian()) { + return drwav_write_pcm_frames_le(pWav, framesToWrite, pData); } else { - return ma_dr_wav_write_pcm_frames_be(pWav, framesToWrite, pData); + return drwav_write_pcm_frames_be(pWav, framesToWrite, pData); } } -MA_PRIVATE ma_uint64 ma_dr_wav_read_pcm_frames_s16__msadpcm(ma_dr_wav* pWav, ma_uint64 framesToRead, ma_int16* pBufferOut) +DRWAV_PRIVATE drwav_uint64 drwav_read_pcm_frames_s16__msadpcm(drwav* pWav, drwav_uint64 framesToRead, drwav_int16* pBufferOut) { - ma_uint64 totalFramesRead = 0; - static ma_int32 adaptationTable[] = { - 230, 230, 230, 230, 307, 409, 512, 614, - 768, 614, 512, 409, 307, 230, 230, 230 - }; - static ma_int32 coeff1Table[] = { 256, 512, 0, 192, 240, 460, 392 }; - static ma_int32 coeff2Table[] = { 0, -256, 0, 64, 0, -208, -232 }; - MA_DR_WAV_ASSERT(pWav != NULL); - MA_DR_WAV_ASSERT(framesToRead > 0); + drwav_uint64 totalFramesRead = 0; + DRWAV_ASSERT(pWav != NULL); + DRWAV_ASSERT(framesToRead > 0); while (pWav->readCursorInPCMFrames < pWav->totalPCMFrameCount) { - MA_DR_WAV_ASSERT(framesToRead > 0); + DRWAV_ASSERT(framesToRead > 0); if (pWav->msadpcm.cachedFrameCount == 0 && pWav->msadpcm.bytesRemainingInBlock == 0) { if (pWav->channels == 1) { - ma_uint8 header[7]; + drwav_uint8 header[7]; if (pWav->onRead(pWav->pUserData, header, sizeof(header)) != sizeof(header)) { return totalFramesRead; } pWav->msadpcm.bytesRemainingInBlock = pWav->fmt.blockAlign - sizeof(header); pWav->msadpcm.predictor[0] = header[0]; - pWav->msadpcm.delta[0] = ma_dr_wav_bytes_to_s16(header + 1); - pWav->msadpcm.prevFrames[0][1] = (ma_int32)ma_dr_wav_bytes_to_s16(header + 3); - pWav->msadpcm.prevFrames[0][0] = (ma_int32)ma_dr_wav_bytes_to_s16(header + 5); + pWav->msadpcm.delta[0] = drwav_bytes_to_s16(header + 1); + pWav->msadpcm.prevFrames[0][1] = (drwav_int32)drwav_bytes_to_s16(header + 3); + pWav->msadpcm.prevFrames[0][0] = (drwav_int32)drwav_bytes_to_s16(header + 5); pWav->msadpcm.cachedFrames[2] = pWav->msadpcm.prevFrames[0][0]; pWav->msadpcm.cachedFrames[3] = pWav->msadpcm.prevFrames[0][1]; pWav->msadpcm.cachedFrameCount = 2; - if (pWav->msadpcm.predictor[0] >= ma_dr_wav_countof(coeff1Table)) { - return totalFramesRead; - } } else { - ma_uint8 header[14]; + drwav_uint8 header[14]; if (pWav->onRead(pWav->pUserData, header, sizeof(header)) != sizeof(header)) { return totalFramesRead; } pWav->msadpcm.bytesRemainingInBlock = pWav->fmt.blockAlign - sizeof(header); pWav->msadpcm.predictor[0] = header[0]; pWav->msadpcm.predictor[1] = header[1]; - pWav->msadpcm.delta[0] = ma_dr_wav_bytes_to_s16(header + 2); - pWav->msadpcm.delta[1] = ma_dr_wav_bytes_to_s16(header + 4); - pWav->msadpcm.prevFrames[0][1] = (ma_int32)ma_dr_wav_bytes_to_s16(header + 6); - pWav->msadpcm.prevFrames[1][1] = (ma_int32)ma_dr_wav_bytes_to_s16(header + 8); - pWav->msadpcm.prevFrames[0][0] = (ma_int32)ma_dr_wav_bytes_to_s16(header + 10); - pWav->msadpcm.prevFrames[1][0] = (ma_int32)ma_dr_wav_bytes_to_s16(header + 12); + pWav->msadpcm.delta[0] = drwav_bytes_to_s16(header + 2); + pWav->msadpcm.delta[1] = drwav_bytes_to_s16(header + 4); + pWav->msadpcm.prevFrames[0][1] = (drwav_int32)drwav_bytes_to_s16(header + 6); + pWav->msadpcm.prevFrames[1][1] = (drwav_int32)drwav_bytes_to_s16(header + 8); + pWav->msadpcm.prevFrames[0][0] = (drwav_int32)drwav_bytes_to_s16(header + 10); + pWav->msadpcm.prevFrames[1][0] = (drwav_int32)drwav_bytes_to_s16(header + 12); pWav->msadpcm.cachedFrames[0] = pWav->msadpcm.prevFrames[0][0]; pWav->msadpcm.cachedFrames[1] = pWav->msadpcm.prevFrames[1][0]; pWav->msadpcm.cachedFrames[2] = pWav->msadpcm.prevFrames[0][1]; pWav->msadpcm.cachedFrames[3] = pWav->msadpcm.prevFrames[1][1]; pWav->msadpcm.cachedFrameCount = 2; - if (pWav->msadpcm.predictor[0] >= ma_dr_wav_countof(coeff1Table) || pWav->msadpcm.predictor[1] >= ma_dr_wav_countof(coeff2Table)) { - return totalFramesRead; - } } } while (framesToRead > 0 && pWav->msadpcm.cachedFrameCount > 0 && pWav->readCursorInPCMFrames < pWav->totalPCMFrameCount) { if (pBufferOut != NULL) { - ma_uint32 iSample = 0; + drwav_uint32 iSample = 0; for (iSample = 0; iSample < pWav->channels; iSample += 1) { - pBufferOut[iSample] = (ma_int16)pWav->msadpcm.cachedFrames[(ma_dr_wav_countof(pWav->msadpcm.cachedFrames) - (pWav->msadpcm.cachedFrameCount*pWav->channels)) + iSample]; + pBufferOut[iSample] = (drwav_int16)pWav->msadpcm.cachedFrames[(drwav_countof(pWav->msadpcm.cachedFrames) - (pWav->msadpcm.cachedFrameCount*pWav->channels)) + iSample]; } pBufferOut += pWav->channels; } @@ -81240,9 +80091,15 @@ MA_PRIVATE ma_uint64 ma_dr_wav_read_pcm_frames_s16__msadpcm(ma_dr_wav* pWav, ma_ if (pWav->msadpcm.bytesRemainingInBlock == 0) { continue; } else { - ma_uint8 nibbles; - ma_int32 nibble0; - ma_int32 nibble1; + static drwav_int32 adaptationTable[] = { + 230, 230, 230, 230, 307, 409, 512, 614, + 768, 614, 512, 409, 307, 230, 230, 230 + }; + static drwav_int32 coeff1Table[] = { 256, 512, 0, 192, 240, 460, 392 }; + static drwav_int32 coeff2Table[] = { 0, -256, 0, 64, 0, -208, -232 }; + drwav_uint8 nibbles; + drwav_int32 nibble0; + drwav_int32 nibble1; if (pWav->onRead(pWav->pUserData, &nibbles, 1) != 1) { return totalFramesRead; } @@ -81250,11 +80107,11 @@ MA_PRIVATE ma_uint64 ma_dr_wav_read_pcm_frames_s16__msadpcm(ma_dr_wav* pWav, ma_ nibble0 = ((nibbles & 0xF0) >> 4); if ((nibbles & 0x80)) { nibble0 |= 0xFFFFFFF0UL; } nibble1 = ((nibbles & 0x0F) >> 0); if ((nibbles & 0x08)) { nibble1 |= 0xFFFFFFF0UL; } if (pWav->channels == 1) { - ma_int32 newSample0; - ma_int32 newSample1; + drwav_int32 newSample0; + drwav_int32 newSample1; newSample0 = ((pWav->msadpcm.prevFrames[0][1] * coeff1Table[pWav->msadpcm.predictor[0]]) + (pWav->msadpcm.prevFrames[0][0] * coeff2Table[pWav->msadpcm.predictor[0]])) >> 8; newSample0 += nibble0 * pWav->msadpcm.delta[0]; - newSample0 = ma_dr_wav_clamp(newSample0, -32768, 32767); + newSample0 = drwav_clamp(newSample0, -32768, 32767); pWav->msadpcm.delta[0] = (adaptationTable[((nibbles & 0xF0) >> 4)] * pWav->msadpcm.delta[0]) >> 8; if (pWav->msadpcm.delta[0] < 16) { pWav->msadpcm.delta[0] = 16; @@ -81263,7 +80120,7 @@ MA_PRIVATE ma_uint64 ma_dr_wav_read_pcm_frames_s16__msadpcm(ma_dr_wav* pWav, ma_ pWav->msadpcm.prevFrames[0][1] = newSample0; newSample1 = ((pWav->msadpcm.prevFrames[0][1] * coeff1Table[pWav->msadpcm.predictor[0]]) + (pWav->msadpcm.prevFrames[0][0] * coeff2Table[pWav->msadpcm.predictor[0]])) >> 8; newSample1 += nibble1 * pWav->msadpcm.delta[0]; - newSample1 = ma_dr_wav_clamp(newSample1, -32768, 32767); + newSample1 = drwav_clamp(newSample1, -32768, 32767); pWav->msadpcm.delta[0] = (adaptationTable[((nibbles & 0x0F) >> 0)] * pWav->msadpcm.delta[0]) >> 8; if (pWav->msadpcm.delta[0] < 16) { pWav->msadpcm.delta[0] = 16; @@ -81274,11 +80131,11 @@ MA_PRIVATE ma_uint64 ma_dr_wav_read_pcm_frames_s16__msadpcm(ma_dr_wav* pWav, ma_ pWav->msadpcm.cachedFrames[3] = newSample1; pWav->msadpcm.cachedFrameCount = 2; } else { - ma_int32 newSample0; - ma_int32 newSample1; + drwav_int32 newSample0; + drwav_int32 newSample1; newSample0 = ((pWav->msadpcm.prevFrames[0][1] * coeff1Table[pWav->msadpcm.predictor[0]]) + (pWav->msadpcm.prevFrames[0][0] * coeff2Table[pWav->msadpcm.predictor[0]])) >> 8; newSample0 += nibble0 * pWav->msadpcm.delta[0]; - newSample0 = ma_dr_wav_clamp(newSample0, -32768, 32767); + newSample0 = drwav_clamp(newSample0, -32768, 32767); pWav->msadpcm.delta[0] = (adaptationTable[((nibbles & 0xF0) >> 4)] * pWav->msadpcm.delta[0]) >> 8; if (pWav->msadpcm.delta[0] < 16) { pWav->msadpcm.delta[0] = 16; @@ -81287,7 +80144,7 @@ MA_PRIVATE ma_uint64 ma_dr_wav_read_pcm_frames_s16__msadpcm(ma_dr_wav* pWav, ma_ pWav->msadpcm.prevFrames[0][1] = newSample0; newSample1 = ((pWav->msadpcm.prevFrames[1][1] * coeff1Table[pWav->msadpcm.predictor[1]]) + (pWav->msadpcm.prevFrames[1][0] * coeff2Table[pWav->msadpcm.predictor[1]])) >> 8; newSample1 += nibble1 * pWav->msadpcm.delta[1]; - newSample1 = ma_dr_wav_clamp(newSample1, -32768, 32767); + newSample1 = drwav_clamp(newSample1, -32768, 32767); pWav->msadpcm.delta[1] = (adaptationTable[((nibbles & 0x0F) >> 0)] * pWav->msadpcm.delta[1]) >> 8; if (pWav->msadpcm.delta[1] < 16) { pWav->msadpcm.delta[1] = 16; @@ -81303,15 +80160,15 @@ MA_PRIVATE ma_uint64 ma_dr_wav_read_pcm_frames_s16__msadpcm(ma_dr_wav* pWav, ma_ } return totalFramesRead; } -MA_PRIVATE ma_uint64 ma_dr_wav_read_pcm_frames_s16__ima(ma_dr_wav* pWav, ma_uint64 framesToRead, ma_int16* pBufferOut) +DRWAV_PRIVATE drwav_uint64 drwav_read_pcm_frames_s16__ima(drwav* pWav, drwav_uint64 framesToRead, drwav_int16* pBufferOut) { - ma_uint64 totalFramesRead = 0; - ma_uint32 iChannel; - static ma_int32 indexTable[16] = { + drwav_uint64 totalFramesRead = 0; + drwav_uint32 iChannel; + static drwav_int32 indexTable[16] = { -1, -1, -1, -1, 2, 4, 6, 8, -1, -1, -1, -1, 2, 4, 6, 8 }; - static ma_int32 stepTable[89] = { + static drwav_int32 stepTable[89] = { 7, 8, 9, 10, 11, 12, 13, 14, 16, 17, 19, 21, 23, 25, 28, 31, 34, 37, 41, 45, 50, 55, 60, 66, 73, 80, 88, 97, 107, 118, @@ -81322,51 +80179,51 @@ MA_PRIVATE ma_uint64 ma_dr_wav_read_pcm_frames_s16__ima(ma_dr_wav* pWav, ma_uint 5894, 6484, 7132, 7845, 8630, 9493, 10442, 11487, 12635, 13899, 15289, 16818, 18500, 20350, 22385, 24623, 27086, 29794, 32767 }; - MA_DR_WAV_ASSERT(pWav != NULL); - MA_DR_WAV_ASSERT(framesToRead > 0); + DRWAV_ASSERT(pWav != NULL); + DRWAV_ASSERT(framesToRead > 0); while (pWav->readCursorInPCMFrames < pWav->totalPCMFrameCount) { - MA_DR_WAV_ASSERT(framesToRead > 0); + DRWAV_ASSERT(framesToRead > 0); if (pWav->ima.cachedFrameCount == 0 && pWav->ima.bytesRemainingInBlock == 0) { if (pWav->channels == 1) { - ma_uint8 header[4]; + drwav_uint8 header[4]; if (pWav->onRead(pWav->pUserData, header, sizeof(header)) != sizeof(header)) { return totalFramesRead; } pWav->ima.bytesRemainingInBlock = pWav->fmt.blockAlign - sizeof(header); - if (header[2] >= ma_dr_wav_countof(stepTable)) { - pWav->onSeek(pWav->pUserData, pWav->ima.bytesRemainingInBlock, ma_dr_wav_seek_origin_current); + if (header[2] >= drwav_countof(stepTable)) { + pWav->onSeek(pWav->pUserData, pWav->ima.bytesRemainingInBlock, drwav_seek_origin_current); pWav->ima.bytesRemainingInBlock = 0; return totalFramesRead; } - pWav->ima.predictor[0] = (ma_int16)ma_dr_wav_bytes_to_u16(header + 0); - pWav->ima.stepIndex[0] = ma_dr_wav_clamp(header[2], 0, (ma_int32)ma_dr_wav_countof(stepTable)-1); - pWav->ima.cachedFrames[ma_dr_wav_countof(pWav->ima.cachedFrames) - 1] = pWav->ima.predictor[0]; + pWav->ima.predictor[0] = drwav_bytes_to_s16(header + 0); + pWav->ima.stepIndex[0] = drwav_clamp(header[2], 0, (drwav_int32)drwav_countof(stepTable)-1); + pWav->ima.cachedFrames[drwav_countof(pWav->ima.cachedFrames) - 1] = pWav->ima.predictor[0]; pWav->ima.cachedFrameCount = 1; } else { - ma_uint8 header[8]; + drwav_uint8 header[8]; if (pWav->onRead(pWav->pUserData, header, sizeof(header)) != sizeof(header)) { return totalFramesRead; } pWav->ima.bytesRemainingInBlock = pWav->fmt.blockAlign - sizeof(header); - if (header[2] >= ma_dr_wav_countof(stepTable) || header[6] >= ma_dr_wav_countof(stepTable)) { - pWav->onSeek(pWav->pUserData, pWav->ima.bytesRemainingInBlock, ma_dr_wav_seek_origin_current); + if (header[2] >= drwav_countof(stepTable) || header[6] >= drwav_countof(stepTable)) { + pWav->onSeek(pWav->pUserData, pWav->ima.bytesRemainingInBlock, drwav_seek_origin_current); pWav->ima.bytesRemainingInBlock = 0; return totalFramesRead; } - pWav->ima.predictor[0] = ma_dr_wav_bytes_to_s16(header + 0); - pWav->ima.stepIndex[0] = ma_dr_wav_clamp(header[2], 0, (ma_int32)ma_dr_wav_countof(stepTable)-1); - pWav->ima.predictor[1] = ma_dr_wav_bytes_to_s16(header + 4); - pWav->ima.stepIndex[1] = ma_dr_wav_clamp(header[6], 0, (ma_int32)ma_dr_wav_countof(stepTable)-1); - pWav->ima.cachedFrames[ma_dr_wav_countof(pWav->ima.cachedFrames) - 2] = pWav->ima.predictor[0]; - pWav->ima.cachedFrames[ma_dr_wav_countof(pWav->ima.cachedFrames) - 1] = pWav->ima.predictor[1]; + pWav->ima.predictor[0] = drwav_bytes_to_s16(header + 0); + pWav->ima.stepIndex[0] = drwav_clamp(header[2], 0, (drwav_int32)drwav_countof(stepTable)-1); + pWav->ima.predictor[1] = drwav_bytes_to_s16(header + 4); + pWav->ima.stepIndex[1] = drwav_clamp(header[6], 0, (drwav_int32)drwav_countof(stepTable)-1); + pWav->ima.cachedFrames[drwav_countof(pWav->ima.cachedFrames) - 2] = pWav->ima.predictor[0]; + pWav->ima.cachedFrames[drwav_countof(pWav->ima.cachedFrames) - 1] = pWav->ima.predictor[1]; pWav->ima.cachedFrameCount = 1; } } while (framesToRead > 0 && pWav->ima.cachedFrameCount > 0 && pWav->readCursorInPCMFrames < pWav->totalPCMFrameCount) { if (pBufferOut != NULL) { - ma_uint32 iSample; + drwav_uint32 iSample; for (iSample = 0; iSample < pWav->channels; iSample += 1) { - pBufferOut[iSample] = (ma_int16)pWav->ima.cachedFrames[(ma_dr_wav_countof(pWav->ima.cachedFrames) - (pWav->ima.cachedFrameCount*pWav->channels)) + iSample]; + pBufferOut[iSample] = (drwav_int16)pWav->ima.cachedFrames[(drwav_countof(pWav->ima.cachedFrames) - (pWav->ima.cachedFrameCount*pWav->channels)) + iSample]; } pBufferOut += pWav->channels; } @@ -81384,27 +80241,27 @@ MA_PRIVATE ma_uint64 ma_dr_wav_read_pcm_frames_s16__ima(ma_dr_wav* pWav, ma_uint } else { pWav->ima.cachedFrameCount = 8; for (iChannel = 0; iChannel < pWav->channels; ++iChannel) { - ma_uint32 iByte; - ma_uint8 nibbles[4]; + drwav_uint32 iByte; + drwav_uint8 nibbles[4]; if (pWav->onRead(pWav->pUserData, &nibbles, 4) != 4) { pWav->ima.cachedFrameCount = 0; return totalFramesRead; } pWav->ima.bytesRemainingInBlock -= 4; for (iByte = 0; iByte < 4; ++iByte) { - ma_uint8 nibble0 = ((nibbles[iByte] & 0x0F) >> 0); - ma_uint8 nibble1 = ((nibbles[iByte] & 0xF0) >> 4); - ma_int32 step = stepTable[pWav->ima.stepIndex[iChannel]]; - ma_int32 predictor = pWav->ima.predictor[iChannel]; - ma_int32 diff = step >> 3; + drwav_uint8 nibble0 = ((nibbles[iByte] & 0x0F) >> 0); + drwav_uint8 nibble1 = ((nibbles[iByte] & 0xF0) >> 4); + drwav_int32 step = stepTable[pWav->ima.stepIndex[iChannel]]; + drwav_int32 predictor = pWav->ima.predictor[iChannel]; + drwav_int32 diff = step >> 3; if (nibble0 & 1) diff += step >> 2; if (nibble0 & 2) diff += step >> 1; if (nibble0 & 4) diff += step; if (nibble0 & 8) diff = -diff; - predictor = ma_dr_wav_clamp(predictor + diff, -32768, 32767); + predictor = drwav_clamp(predictor + diff, -32768, 32767); pWav->ima.predictor[iChannel] = predictor; - pWav->ima.stepIndex[iChannel] = ma_dr_wav_clamp(pWav->ima.stepIndex[iChannel] + indexTable[nibble0], 0, (ma_int32)ma_dr_wav_countof(stepTable)-1); - pWav->ima.cachedFrames[(ma_dr_wav_countof(pWav->ima.cachedFrames) - (pWav->ima.cachedFrameCount*pWav->channels)) + (iByte*2+0)*pWav->channels + iChannel] = predictor; + pWav->ima.stepIndex[iChannel] = drwav_clamp(pWav->ima.stepIndex[iChannel] + indexTable[nibble0], 0, (drwav_int32)drwav_countof(stepTable)-1); + pWav->ima.cachedFrames[(drwav_countof(pWav->ima.cachedFrames) - (pWav->ima.cachedFrameCount*pWav->channels)) + (iByte*2+0)*pWav->channels + iChannel] = predictor; step = stepTable[pWav->ima.stepIndex[iChannel]]; predictor = pWav->ima.predictor[iChannel]; diff = step >> 3; @@ -81412,10 +80269,10 @@ MA_PRIVATE ma_uint64 ma_dr_wav_read_pcm_frames_s16__ima(ma_dr_wav* pWav, ma_uint if (nibble1 & 2) diff += step >> 1; if (nibble1 & 4) diff += step; if (nibble1 & 8) diff = -diff; - predictor = ma_dr_wav_clamp(predictor + diff, -32768, 32767); + predictor = drwav_clamp(predictor + diff, -32768, 32767); pWav->ima.predictor[iChannel] = predictor; - pWav->ima.stepIndex[iChannel] = ma_dr_wav_clamp(pWav->ima.stepIndex[iChannel] + indexTable[nibble1], 0, (ma_int32)ma_dr_wav_countof(stepTable)-1); - pWav->ima.cachedFrames[(ma_dr_wav_countof(pWav->ima.cachedFrames) - (pWav->ima.cachedFrameCount*pWav->channels)) + (iByte*2+1)*pWav->channels + iChannel] = predictor; + pWav->ima.stepIndex[iChannel] = drwav_clamp(pWav->ima.stepIndex[iChannel] + indexTable[nibble1], 0, (drwav_int32)drwav_countof(stepTable)-1); + pWav->ima.cachedFrames[(drwav_countof(pWav->ima.cachedFrames) - (pWav->ima.cachedFrameCount*pWav->channels)) + (iByte*2+1)*pWav->channels + iChannel] = predictor; } } } @@ -81423,8 +80280,8 @@ MA_PRIVATE ma_uint64 ma_dr_wav_read_pcm_frames_s16__ima(ma_dr_wav* pWav, ma_uint } return totalFramesRead; } -#ifndef MA_DR_WAV_NO_CONVERSION_API -static unsigned short g_ma_dr_wavAlawTable[256] = { +#ifndef DR_WAV_NO_CONVERSION_API +static unsigned short g_drwavAlawTable[256] = { 0xEA80, 0xEB80, 0xE880, 0xE980, 0xEE80, 0xEF80, 0xEC80, 0xED80, 0xE280, 0xE380, 0xE080, 0xE180, 0xE680, 0xE780, 0xE480, 0xE580, 0xF540, 0xF5C0, 0xF440, 0xF4C0, 0xF740, 0xF7C0, 0xF640, 0xF6C0, 0xF140, 0xF1C0, 0xF040, 0xF0C0, 0xF340, 0xF3C0, 0xF240, 0xF2C0, 0xAA00, 0xAE00, 0xA200, 0xA600, 0xBA00, 0xBE00, 0xB200, 0xB600, 0x8A00, 0x8E00, 0x8200, 0x8600, 0x9A00, 0x9E00, 0x9200, 0x9600, @@ -81442,7 +80299,7 @@ static unsigned short g_ma_dr_wavAlawTable[256] = { 0x0560, 0x0520, 0x05E0, 0x05A0, 0x0460, 0x0420, 0x04E0, 0x04A0, 0x0760, 0x0720, 0x07E0, 0x07A0, 0x0660, 0x0620, 0x06E0, 0x06A0, 0x02B0, 0x0290, 0x02F0, 0x02D0, 0x0230, 0x0210, 0x0270, 0x0250, 0x03B0, 0x0390, 0x03F0, 0x03D0, 0x0330, 0x0310, 0x0370, 0x0350 }; -static unsigned short g_ma_dr_wavMulawTable[256] = { +static unsigned short g_drwavMulawTable[256] = { 0x8284, 0x8684, 0x8A84, 0x8E84, 0x9284, 0x9684, 0x9A84, 0x9E84, 0xA284, 0xA684, 0xAA84, 0xAE84, 0xB284, 0xB684, 0xBA84, 0xBE84, 0xC184, 0xC384, 0xC584, 0xC784, 0xC984, 0xCB84, 0xCD84, 0xCF84, 0xD184, 0xD384, 0xD584, 0xD784, 0xD984, 0xDB84, 0xDD84, 0xDF84, 0xE104, 0xE204, 0xE304, 0xE404, 0xE504, 0xE604, 0xE704, 0xE804, 0xE904, 0xEA04, 0xEB04, 0xEC04, 0xED04, 0xEE04, 0xEF04, 0xF004, @@ -81460,76 +80317,76 @@ static unsigned short g_ma_dr_wavMulawTable[256] = { 0x0174, 0x0164, 0x0154, 0x0144, 0x0134, 0x0124, 0x0114, 0x0104, 0x00F4, 0x00E4, 0x00D4, 0x00C4, 0x00B4, 0x00A4, 0x0094, 0x0084, 0x0078, 0x0070, 0x0068, 0x0060, 0x0058, 0x0050, 0x0048, 0x0040, 0x0038, 0x0030, 0x0028, 0x0020, 0x0018, 0x0010, 0x0008, 0x0000 }; -static MA_INLINE ma_int16 ma_dr_wav__alaw_to_s16(ma_uint8 sampleIn) +static DRWAV_INLINE drwav_int16 drwav__alaw_to_s16(drwav_uint8 sampleIn) { - return (short)g_ma_dr_wavAlawTable[sampleIn]; + return (short)g_drwavAlawTable[sampleIn]; } -static MA_INLINE ma_int16 ma_dr_wav__mulaw_to_s16(ma_uint8 sampleIn) +static DRWAV_INLINE drwav_int16 drwav__mulaw_to_s16(drwav_uint8 sampleIn) { - return (short)g_ma_dr_wavMulawTable[sampleIn]; + return (short)g_drwavMulawTable[sampleIn]; } -MA_PRIVATE void ma_dr_wav__pcm_to_s16(ma_int16* pOut, const ma_uint8* pIn, size_t totalSampleCount, unsigned int bytesPerSample) +DRWAV_PRIVATE void drwav__pcm_to_s16(drwav_int16* pOut, const drwav_uint8* pIn, size_t totalSampleCount, unsigned int bytesPerSample) { size_t i; if (bytesPerSample == 1) { - ma_dr_wav_u8_to_s16(pOut, pIn, totalSampleCount); + drwav_u8_to_s16(pOut, pIn, totalSampleCount); return; } if (bytesPerSample == 2) { for (i = 0; i < totalSampleCount; ++i) { - *pOut++ = ((const ma_int16*)pIn)[i]; + *pOut++ = ((const drwav_int16*)pIn)[i]; } return; } if (bytesPerSample == 3) { - ma_dr_wav_s24_to_s16(pOut, pIn, totalSampleCount); + drwav_s24_to_s16(pOut, pIn, totalSampleCount); return; } if (bytesPerSample == 4) { - ma_dr_wav_s32_to_s16(pOut, (const ma_int32*)pIn, totalSampleCount); + drwav_s32_to_s16(pOut, (const drwav_int32*)pIn, totalSampleCount); return; } if (bytesPerSample > 8) { - MA_DR_WAV_ZERO_MEMORY(pOut, totalSampleCount * sizeof(*pOut)); + DRWAV_ZERO_MEMORY(pOut, totalSampleCount * sizeof(*pOut)); return; } for (i = 0; i < totalSampleCount; ++i) { - ma_uint64 sample = 0; + drwav_uint64 sample = 0; unsigned int shift = (8 - bytesPerSample) * 8; unsigned int j; for (j = 0; j < bytesPerSample; j += 1) { - MA_DR_WAV_ASSERT(j < 8); - sample |= (ma_uint64)(pIn[j]) << shift; + DRWAV_ASSERT(j < 8); + sample |= (drwav_uint64)(pIn[j]) << shift; shift += 8; } pIn += j; - *pOut++ = (ma_int16)((ma_int64)sample >> 48); + *pOut++ = (drwav_int16)((drwav_int64)sample >> 48); } } -MA_PRIVATE void ma_dr_wav__ieee_to_s16(ma_int16* pOut, const ma_uint8* pIn, size_t totalSampleCount, unsigned int bytesPerSample) +DRWAV_PRIVATE void drwav__ieee_to_s16(drwav_int16* pOut, const drwav_uint8* pIn, size_t totalSampleCount, unsigned int bytesPerSample) { if (bytesPerSample == 4) { - ma_dr_wav_f32_to_s16(pOut, (const float*)pIn, totalSampleCount); + drwav_f32_to_s16(pOut, (const float*)pIn, totalSampleCount); return; } else if (bytesPerSample == 8) { - ma_dr_wav_f64_to_s16(pOut, (const double*)pIn, totalSampleCount); + drwav_f64_to_s16(pOut, (const double*)pIn, totalSampleCount); return; } else { - MA_DR_WAV_ZERO_MEMORY(pOut, totalSampleCount * sizeof(*pOut)); + DRWAV_ZERO_MEMORY(pOut, totalSampleCount * sizeof(*pOut)); return; } } -MA_PRIVATE ma_uint64 ma_dr_wav_read_pcm_frames_s16__pcm(ma_dr_wav* pWav, ma_uint64 framesToRead, ma_int16* pBufferOut) +DRWAV_PRIVATE drwav_uint64 drwav_read_pcm_frames_s16__pcm(drwav* pWav, drwav_uint64 framesToRead, drwav_int16* pBufferOut) { - ma_uint64 totalFramesRead; - ma_uint8 sampleData[4096] = {0}; - ma_uint32 bytesPerFrame; - ma_uint32 bytesPerSample; - ma_uint64 samplesRead; - if ((pWav->translatedFormatTag == MA_DR_WAVE_FORMAT_PCM && pWav->bitsPerSample == 16) || pBufferOut == NULL) { - return ma_dr_wav_read_pcm_frames(pWav, framesToRead, pBufferOut); - } - bytesPerFrame = ma_dr_wav_get_bytes_per_pcm_frame(pWav); + drwav_uint64 totalFramesRead; + drwav_uint8 sampleData[4096] = {0}; + drwav_uint32 bytesPerFrame; + drwav_uint32 bytesPerSample; + drwav_uint64 samplesRead; + if ((pWav->translatedFormatTag == DR_WAVE_FORMAT_PCM && pWav->bitsPerSample == 16) || pBufferOut == NULL) { + return drwav_read_pcm_frames(pWav, framesToRead, pBufferOut); + } + bytesPerFrame = drwav_get_bytes_per_pcm_frame(pWav); if (bytesPerFrame == 0) { return 0; } @@ -81539,35 +80396,35 @@ MA_PRIVATE ma_uint64 ma_dr_wav_read_pcm_frames_s16__pcm(ma_dr_wav* pWav, ma_uint } totalFramesRead = 0; while (framesToRead > 0) { - ma_uint64 framesToReadThisIteration = ma_dr_wav_min(framesToRead, sizeof(sampleData)/bytesPerFrame); - ma_uint64 framesRead = ma_dr_wav_read_pcm_frames(pWav, framesToReadThisIteration, sampleData); + drwav_uint64 framesToReadThisIteration = drwav_min(framesToRead, sizeof(sampleData)/bytesPerFrame); + drwav_uint64 framesRead = drwav_read_pcm_frames(pWav, framesToReadThisIteration, sampleData); if (framesRead == 0) { break; } - MA_DR_WAV_ASSERT(framesRead <= framesToReadThisIteration); + DRWAV_ASSERT(framesRead <= framesToReadThisIteration); samplesRead = framesRead * pWav->channels; if ((samplesRead * bytesPerSample) > sizeof(sampleData)) { - MA_DR_WAV_ASSERT(MA_FALSE); + DRWAV_ASSERT(DRWAV_FALSE); break; } - ma_dr_wav__pcm_to_s16(pBufferOut, sampleData, (size_t)samplesRead, bytesPerSample); + drwav__pcm_to_s16(pBufferOut, sampleData, (size_t)samplesRead, bytesPerSample); pBufferOut += samplesRead; framesToRead -= framesRead; totalFramesRead += framesRead; } return totalFramesRead; } -MA_PRIVATE ma_uint64 ma_dr_wav_read_pcm_frames_s16__ieee(ma_dr_wav* pWav, ma_uint64 framesToRead, ma_int16* pBufferOut) +DRWAV_PRIVATE drwav_uint64 drwav_read_pcm_frames_s16__ieee(drwav* pWav, drwav_uint64 framesToRead, drwav_int16* pBufferOut) { - ma_uint64 totalFramesRead; - ma_uint8 sampleData[4096] = {0}; - ma_uint32 bytesPerFrame; - ma_uint32 bytesPerSample; - ma_uint64 samplesRead; + drwav_uint64 totalFramesRead; + drwav_uint8 sampleData[4096] = {0}; + drwav_uint32 bytesPerFrame; + drwav_uint32 bytesPerSample; + drwav_uint64 samplesRead; if (pBufferOut == NULL) { - return ma_dr_wav_read_pcm_frames(pWav, framesToRead, NULL); + return drwav_read_pcm_frames(pWav, framesToRead, NULL); } - bytesPerFrame = ma_dr_wav_get_bytes_per_pcm_frame(pWav); + bytesPerFrame = drwav_get_bytes_per_pcm_frame(pWav); if (bytesPerFrame == 0) { return 0; } @@ -81577,35 +80434,35 @@ MA_PRIVATE ma_uint64 ma_dr_wav_read_pcm_frames_s16__ieee(ma_dr_wav* pWav, ma_uin } totalFramesRead = 0; while (framesToRead > 0) { - ma_uint64 framesToReadThisIteration = ma_dr_wav_min(framesToRead, sizeof(sampleData)/bytesPerFrame); - ma_uint64 framesRead = ma_dr_wav_read_pcm_frames(pWav, framesToReadThisIteration, sampleData); + drwav_uint64 framesToReadThisIteration = drwav_min(framesToRead, sizeof(sampleData)/bytesPerFrame); + drwav_uint64 framesRead = drwav_read_pcm_frames(pWav, framesToReadThisIteration, sampleData); if (framesRead == 0) { break; } - MA_DR_WAV_ASSERT(framesRead <= framesToReadThisIteration); + DRWAV_ASSERT(framesRead <= framesToReadThisIteration); samplesRead = framesRead * pWav->channels; if ((samplesRead * bytesPerSample) > sizeof(sampleData)) { - MA_DR_WAV_ASSERT(MA_FALSE); + DRWAV_ASSERT(DRWAV_FALSE); break; } - ma_dr_wav__ieee_to_s16(pBufferOut, sampleData, (size_t)samplesRead, bytesPerSample); + drwav__ieee_to_s16(pBufferOut, sampleData, (size_t)samplesRead, bytesPerSample); pBufferOut += samplesRead; framesToRead -= framesRead; totalFramesRead += framesRead; } return totalFramesRead; } -MA_PRIVATE ma_uint64 ma_dr_wav_read_pcm_frames_s16__alaw(ma_dr_wav* pWav, ma_uint64 framesToRead, ma_int16* pBufferOut) +DRWAV_PRIVATE drwav_uint64 drwav_read_pcm_frames_s16__alaw(drwav* pWav, drwav_uint64 framesToRead, drwav_int16* pBufferOut) { - ma_uint64 totalFramesRead; - ma_uint8 sampleData[4096] = {0}; - ma_uint32 bytesPerFrame; - ma_uint32 bytesPerSample; - ma_uint64 samplesRead; + drwav_uint64 totalFramesRead; + drwav_uint8 sampleData[4096] = {0}; + drwav_uint32 bytesPerFrame; + drwav_uint32 bytesPerSample; + drwav_uint64 samplesRead; if (pBufferOut == NULL) { - return ma_dr_wav_read_pcm_frames(pWav, framesToRead, NULL); + return drwav_read_pcm_frames(pWav, framesToRead, NULL); } - bytesPerFrame = ma_dr_wav_get_bytes_per_pcm_frame(pWav); + bytesPerFrame = drwav_get_bytes_per_pcm_frame(pWav); if (bytesPerFrame == 0) { return 0; } @@ -81615,45 +80472,35 @@ MA_PRIVATE ma_uint64 ma_dr_wav_read_pcm_frames_s16__alaw(ma_dr_wav* pWav, ma_uin } totalFramesRead = 0; while (framesToRead > 0) { - ma_uint64 framesToReadThisIteration = ma_dr_wav_min(framesToRead, sizeof(sampleData)/bytesPerFrame); - ma_uint64 framesRead = ma_dr_wav_read_pcm_frames(pWav, framesToReadThisIteration, sampleData); + drwav_uint64 framesToReadThisIteration = drwav_min(framesToRead, sizeof(sampleData)/bytesPerFrame); + drwav_uint64 framesRead = drwav_read_pcm_frames(pWav, framesToReadThisIteration, sampleData); if (framesRead == 0) { break; } - MA_DR_WAV_ASSERT(framesRead <= framesToReadThisIteration); + DRWAV_ASSERT(framesRead <= framesToReadThisIteration); samplesRead = framesRead * pWav->channels; if ((samplesRead * bytesPerSample) > sizeof(sampleData)) { - MA_DR_WAV_ASSERT(MA_FALSE); + DRWAV_ASSERT(DRWAV_FALSE); break; } - ma_dr_wav_alaw_to_s16(pBufferOut, sampleData, (size_t)samplesRead); - #ifdef MA_DR_WAV_LIBSNDFILE_COMPAT - { - if (pWav->container == ma_dr_wav_container_aiff) { - ma_uint64 iSample; - for (iSample = 0; iSample < samplesRead; iSample += 1) { - pBufferOut[iSample] = -pBufferOut[iSample]; - } - } - } - #endif + drwav_alaw_to_s16(pBufferOut, sampleData, (size_t)samplesRead); pBufferOut += samplesRead; framesToRead -= framesRead; totalFramesRead += framesRead; } return totalFramesRead; } -MA_PRIVATE ma_uint64 ma_dr_wav_read_pcm_frames_s16__mulaw(ma_dr_wav* pWav, ma_uint64 framesToRead, ma_int16* pBufferOut) +DRWAV_PRIVATE drwav_uint64 drwav_read_pcm_frames_s16__mulaw(drwav* pWav, drwav_uint64 framesToRead, drwav_int16* pBufferOut) { - ma_uint64 totalFramesRead; - ma_uint8 sampleData[4096] = {0}; - ma_uint32 bytesPerFrame; - ma_uint32 bytesPerSample; - ma_uint64 samplesRead; + drwav_uint64 totalFramesRead; + drwav_uint8 sampleData[4096] = {0}; + drwav_uint32 bytesPerFrame; + drwav_uint32 bytesPerSample; + drwav_uint64 samplesRead; if (pBufferOut == NULL) { - return ma_dr_wav_read_pcm_frames(pWav, framesToRead, NULL); + return drwav_read_pcm_frames(pWav, framesToRead, NULL); } - bytesPerFrame = ma_dr_wav_get_bytes_per_pcm_frame(pWav); + bytesPerFrame = drwav_get_bytes_per_pcm_frame(pWav); if (bytesPerFrame == 0) { return 0; } @@ -81663,82 +80510,72 @@ MA_PRIVATE ma_uint64 ma_dr_wav_read_pcm_frames_s16__mulaw(ma_dr_wav* pWav, ma_ui } totalFramesRead = 0; while (framesToRead > 0) { - ma_uint64 framesToReadThisIteration = ma_dr_wav_min(framesToRead, sizeof(sampleData)/bytesPerFrame); - ma_uint64 framesRead = ma_dr_wav_read_pcm_frames(pWav, framesToReadThisIteration, sampleData); + drwav_uint64 framesToReadThisIteration = drwav_min(framesToRead, sizeof(sampleData)/bytesPerFrame); + drwav_uint64 framesRead = drwav_read_pcm_frames(pWav, framesToReadThisIteration, sampleData); if (framesRead == 0) { break; } - MA_DR_WAV_ASSERT(framesRead <= framesToReadThisIteration); + DRWAV_ASSERT(framesRead <= framesToReadThisIteration); samplesRead = framesRead * pWav->channels; if ((samplesRead * bytesPerSample) > sizeof(sampleData)) { - MA_DR_WAV_ASSERT(MA_FALSE); + DRWAV_ASSERT(DRWAV_FALSE); break; } - ma_dr_wav_mulaw_to_s16(pBufferOut, sampleData, (size_t)samplesRead); - #ifdef MA_DR_WAV_LIBSNDFILE_COMPAT - { - if (pWav->container == ma_dr_wav_container_aiff) { - ma_uint64 iSample; - for (iSample = 0; iSample < samplesRead; iSample += 1) { - pBufferOut[iSample] = -pBufferOut[iSample]; - } - } - } - #endif + drwav_mulaw_to_s16(pBufferOut, sampleData, (size_t)samplesRead); pBufferOut += samplesRead; framesToRead -= framesRead; totalFramesRead += framesRead; } return totalFramesRead; } -MA_API ma_uint64 ma_dr_wav_read_pcm_frames_s16(ma_dr_wav* pWav, ma_uint64 framesToRead, ma_int16* pBufferOut) +DRWAV_API drwav_uint64 drwav_read_pcm_frames_s16(drwav* pWav, drwav_uint64 framesToRead, drwav_int16* pBufferOut) { if (pWav == NULL || framesToRead == 0) { return 0; } if (pBufferOut == NULL) { - return ma_dr_wav_read_pcm_frames(pWav, framesToRead, NULL); + return drwav_read_pcm_frames(pWav, framesToRead, NULL); } - if (framesToRead * pWav->channels * sizeof(ma_int16) > MA_SIZE_MAX) { - framesToRead = MA_SIZE_MAX / sizeof(ma_int16) / pWav->channels; + if (framesToRead * pWav->channels * sizeof(drwav_int16) > DRWAV_SIZE_MAX) { + framesToRead = DRWAV_SIZE_MAX / sizeof(drwav_int16) / pWav->channels; } - if (pWav->translatedFormatTag == MA_DR_WAVE_FORMAT_PCM) { - return ma_dr_wav_read_pcm_frames_s16__pcm(pWav, framesToRead, pBufferOut); + if (pWav->translatedFormatTag == DR_WAVE_FORMAT_PCM) { + return drwav_read_pcm_frames_s16__pcm(pWav, framesToRead, pBufferOut); } - if (pWav->translatedFormatTag == MA_DR_WAVE_FORMAT_IEEE_FLOAT) { - return ma_dr_wav_read_pcm_frames_s16__ieee(pWav, framesToRead, pBufferOut); + if (pWav->translatedFormatTag == DR_WAVE_FORMAT_IEEE_FLOAT) { + return drwav_read_pcm_frames_s16__ieee(pWav, framesToRead, pBufferOut); } - if (pWav->translatedFormatTag == MA_DR_WAVE_FORMAT_ALAW) { - return ma_dr_wav_read_pcm_frames_s16__alaw(pWav, framesToRead, pBufferOut); + if (pWav->translatedFormatTag == DR_WAVE_FORMAT_ALAW) { + return drwav_read_pcm_frames_s16__alaw(pWav, framesToRead, pBufferOut); } - if (pWav->translatedFormatTag == MA_DR_WAVE_FORMAT_MULAW) { - return ma_dr_wav_read_pcm_frames_s16__mulaw(pWav, framesToRead, pBufferOut); + if (pWav->translatedFormatTag == DR_WAVE_FORMAT_MULAW) { + return drwav_read_pcm_frames_s16__mulaw(pWav, framesToRead, pBufferOut); } - if (pWav->translatedFormatTag == MA_DR_WAVE_FORMAT_ADPCM) { - return ma_dr_wav_read_pcm_frames_s16__msadpcm(pWav, framesToRead, pBufferOut); + if (pWav->translatedFormatTag == DR_WAVE_FORMAT_ADPCM) { + return drwav_read_pcm_frames_s16__msadpcm(pWav, framesToRead, pBufferOut); } - if (pWav->translatedFormatTag == MA_DR_WAVE_FORMAT_DVI_ADPCM) { - return ma_dr_wav_read_pcm_frames_s16__ima(pWav, framesToRead, pBufferOut); + if (pWav->translatedFormatTag == DR_WAVE_FORMAT_DVI_ADPCM) { + return drwav_read_pcm_frames_s16__ima(pWav, framesToRead, pBufferOut); } return 0; } -MA_API ma_uint64 ma_dr_wav_read_pcm_frames_s16le(ma_dr_wav* pWav, ma_uint64 framesToRead, ma_int16* pBufferOut) +DRWAV_API drwav_uint64 drwav_read_pcm_frames_s16le(drwav* pWav, drwav_uint64 framesToRead, drwav_int16* pBufferOut) { - ma_uint64 framesRead = ma_dr_wav_read_pcm_frames_s16(pWav, framesToRead, pBufferOut); - if (pBufferOut != NULL && ma_dr_wav__is_little_endian() == MA_FALSE) { - ma_dr_wav__bswap_samples_s16(pBufferOut, framesRead*pWav->channels); + drwav_uint64 framesRead = drwav_read_pcm_frames_s16(pWav, framesToRead, pBufferOut); + if (pBufferOut != NULL && drwav__is_little_endian() == DRWAV_FALSE) { + drwav__bswap_samples_s16(pBufferOut, framesRead*pWav->channels); } return framesRead; } -MA_API ma_uint64 ma_dr_wav_read_pcm_frames_s16be(ma_dr_wav* pWav, ma_uint64 framesToRead, ma_int16* pBufferOut) +DRWAV_API drwav_uint64 drwav_read_pcm_frames_s16be(drwav* pWav, drwav_uint64 framesToRead, drwav_int16* pBufferOut) { - ma_uint64 framesRead = ma_dr_wav_read_pcm_frames_s16(pWav, framesToRead, pBufferOut); - if (pBufferOut != NULL && ma_dr_wav__is_little_endian() == MA_TRUE) { - ma_dr_wav__bswap_samples_s16(pBufferOut, framesRead*pWav->channels); + drwav_uint64 framesRead = drwav_read_pcm_frames_s16(pWav, framesToRead, pBufferOut); + if (pBufferOut != NULL && drwav__is_little_endian() == DRWAV_TRUE) { + drwav__bswap_samples_s16(pBufferOut, framesRead*pWav->channels); } return framesRead; } -MA_API void ma_dr_wav_u8_to_s16(ma_int16* pOut, const ma_uint8* pIn, size_t sampleCount) +DRWAV_API void drwav_u8_to_s16(drwav_int16* pOut, const drwav_uint8* pIn, size_t sampleCount) { int r; size_t i; @@ -81749,17 +80586,17 @@ MA_API void ma_dr_wav_u8_to_s16(ma_int16* pOut, const ma_uint8* pIn, size_t samp pOut[i] = (short)r; } } -MA_API void ma_dr_wav_s24_to_s16(ma_int16* pOut, const ma_uint8* pIn, size_t sampleCount) +DRWAV_API void drwav_s24_to_s16(drwav_int16* pOut, const drwav_uint8* pIn, size_t sampleCount) { int r; size_t i; for (i = 0; i < sampleCount; ++i) { - int x = ((int)(((unsigned int)(((const ma_uint8*)pIn)[i*3+0]) << 8) | ((unsigned int)(((const ma_uint8*)pIn)[i*3+1]) << 16) | ((unsigned int)(((const ma_uint8*)pIn)[i*3+2])) << 24)) >> 8; + int x = ((int)(((unsigned int)(((const drwav_uint8*)pIn)[i*3+0]) << 8) | ((unsigned int)(((const drwav_uint8*)pIn)[i*3+1]) << 16) | ((unsigned int)(((const drwav_uint8*)pIn)[i*3+2])) << 24)) >> 8; r = x >> 8; pOut[i] = (short)r; } } -MA_API void ma_dr_wav_s32_to_s16(ma_int16* pOut, const ma_int32* pIn, size_t sampleCount) +DRWAV_API void drwav_s32_to_s16(drwav_int16* pOut, const drwav_int32* pIn, size_t sampleCount) { int r; size_t i; @@ -81769,7 +80606,7 @@ MA_API void ma_dr_wav_s32_to_s16(ma_int16* pOut, const ma_int32* pIn, size_t sam pOut[i] = (short)r; } } -MA_API void ma_dr_wav_f32_to_s16(ma_int16* pOut, const float* pIn, size_t sampleCount) +DRWAV_API void drwav_f32_to_s16(drwav_int16* pOut, const float* pIn, size_t sampleCount) { int r; size_t i; @@ -81783,7 +80620,7 @@ MA_API void ma_dr_wav_f32_to_s16(ma_int16* pOut, const float* pIn, size_t sample pOut[i] = (short)r; } } -MA_API void ma_dr_wav_f64_to_s16(ma_int16* pOut, const double* pIn, size_t sampleCount) +DRWAV_API void drwav_f64_to_s16(drwav_int16* pOut, const double* pIn, size_t sampleCount) { int r; size_t i; @@ -81797,57 +80634,57 @@ MA_API void ma_dr_wav_f64_to_s16(ma_int16* pOut, const double* pIn, size_t sampl pOut[i] = (short)r; } } -MA_API void ma_dr_wav_alaw_to_s16(ma_int16* pOut, const ma_uint8* pIn, size_t sampleCount) +DRWAV_API void drwav_alaw_to_s16(drwav_int16* pOut, const drwav_uint8* pIn, size_t sampleCount) { size_t i; for (i = 0; i < sampleCount; ++i) { - pOut[i] = ma_dr_wav__alaw_to_s16(pIn[i]); + pOut[i] = drwav__alaw_to_s16(pIn[i]); } } -MA_API void ma_dr_wav_mulaw_to_s16(ma_int16* pOut, const ma_uint8* pIn, size_t sampleCount) +DRWAV_API void drwav_mulaw_to_s16(drwav_int16* pOut, const drwav_uint8* pIn, size_t sampleCount) { size_t i; for (i = 0; i < sampleCount; ++i) { - pOut[i] = ma_dr_wav__mulaw_to_s16(pIn[i]); + pOut[i] = drwav__mulaw_to_s16(pIn[i]); } } -MA_PRIVATE void ma_dr_wav__pcm_to_f32(float* pOut, const ma_uint8* pIn, size_t sampleCount, unsigned int bytesPerSample) +DRWAV_PRIVATE void drwav__pcm_to_f32(float* pOut, const drwav_uint8* pIn, size_t sampleCount, unsigned int bytesPerSample) { unsigned int i; if (bytesPerSample == 1) { - ma_dr_wav_u8_to_f32(pOut, pIn, sampleCount); + drwav_u8_to_f32(pOut, pIn, sampleCount); return; } if (bytesPerSample == 2) { - ma_dr_wav_s16_to_f32(pOut, (const ma_int16*)pIn, sampleCount); + drwav_s16_to_f32(pOut, (const drwav_int16*)pIn, sampleCount); return; } if (bytesPerSample == 3) { - ma_dr_wav_s24_to_f32(pOut, pIn, sampleCount); + drwav_s24_to_f32(pOut, pIn, sampleCount); return; } if (bytesPerSample == 4) { - ma_dr_wav_s32_to_f32(pOut, (const ma_int32*)pIn, sampleCount); + drwav_s32_to_f32(pOut, (const drwav_int32*)pIn, sampleCount); return; } if (bytesPerSample > 8) { - MA_DR_WAV_ZERO_MEMORY(pOut, sampleCount * sizeof(*pOut)); + DRWAV_ZERO_MEMORY(pOut, sampleCount * sizeof(*pOut)); return; } for (i = 0; i < sampleCount; ++i) { - ma_uint64 sample = 0; + drwav_uint64 sample = 0; unsigned int shift = (8 - bytesPerSample) * 8; unsigned int j; for (j = 0; j < bytesPerSample; j += 1) { - MA_DR_WAV_ASSERT(j < 8); - sample |= (ma_uint64)(pIn[j]) << shift; + DRWAV_ASSERT(j < 8); + sample |= (drwav_uint64)(pIn[j]) << shift; shift += 8; } pIn += j; - *pOut++ = (float)((ma_int64)sample / 9223372036854775807.0); + *pOut++ = (float)((drwav_int64)sample / 9223372036854775807.0); } } -MA_PRIVATE void ma_dr_wav__ieee_to_f32(float* pOut, const ma_uint8* pIn, size_t sampleCount, unsigned int bytesPerSample) +DRWAV_PRIVATE void drwav__ieee_to_f32(float* pOut, const drwav_uint8* pIn, size_t sampleCount, unsigned int bytesPerSample) { if (bytesPerSample == 4) { unsigned int i; @@ -81856,21 +80693,21 @@ MA_PRIVATE void ma_dr_wav__ieee_to_f32(float* pOut, const ma_uint8* pIn, size_t } return; } else if (bytesPerSample == 8) { - ma_dr_wav_f64_to_f32(pOut, (const double*)pIn, sampleCount); + drwav_f64_to_f32(pOut, (const double*)pIn, sampleCount); return; } else { - MA_DR_WAV_ZERO_MEMORY(pOut, sampleCount * sizeof(*pOut)); + DRWAV_ZERO_MEMORY(pOut, sampleCount * sizeof(*pOut)); return; } } -MA_PRIVATE ma_uint64 ma_dr_wav_read_pcm_frames_f32__pcm(ma_dr_wav* pWav, ma_uint64 framesToRead, float* pBufferOut) +DRWAV_PRIVATE drwav_uint64 drwav_read_pcm_frames_f32__pcm(drwav* pWav, drwav_uint64 framesToRead, float* pBufferOut) { - ma_uint64 totalFramesRead; - ma_uint8 sampleData[4096] = {0}; - ma_uint32 bytesPerFrame; - ma_uint32 bytesPerSample; - ma_uint64 samplesRead; - bytesPerFrame = ma_dr_wav_get_bytes_per_pcm_frame(pWav); + drwav_uint64 totalFramesRead; + drwav_uint8 sampleData[4096] = {0}; + drwav_uint32 bytesPerFrame; + drwav_uint32 bytesPerSample; + drwav_uint64 samplesRead; + bytesPerFrame = drwav_get_bytes_per_pcm_frame(pWav); if (bytesPerFrame == 0) { return 0; } @@ -81880,89 +80717,54 @@ MA_PRIVATE ma_uint64 ma_dr_wav_read_pcm_frames_f32__pcm(ma_dr_wav* pWav, ma_uint } totalFramesRead = 0; while (framesToRead > 0) { - ma_uint64 framesToReadThisIteration = ma_dr_wav_min(framesToRead, sizeof(sampleData)/bytesPerFrame); - ma_uint64 framesRead = ma_dr_wav_read_pcm_frames(pWav, framesToReadThisIteration, sampleData); + drwav_uint64 framesToReadThisIteration = drwav_min(framesToRead, sizeof(sampleData)/bytesPerFrame); + drwav_uint64 framesRead = drwav_read_pcm_frames(pWav, framesToReadThisIteration, sampleData); if (framesRead == 0) { break; } - MA_DR_WAV_ASSERT(framesRead <= framesToReadThisIteration); + DRWAV_ASSERT(framesRead <= framesToReadThisIteration); samplesRead = framesRead * pWav->channels; if ((samplesRead * bytesPerSample) > sizeof(sampleData)) { - MA_DR_WAV_ASSERT(MA_FALSE); + DRWAV_ASSERT(DRWAV_FALSE); break; } - ma_dr_wav__pcm_to_f32(pBufferOut, sampleData, (size_t)samplesRead, bytesPerSample); + drwav__pcm_to_f32(pBufferOut, sampleData, (size_t)samplesRead, bytesPerSample); pBufferOut += samplesRead; framesToRead -= framesRead; totalFramesRead += framesRead; } return totalFramesRead; } -MA_PRIVATE ma_uint64 ma_dr_wav_read_pcm_frames_f32__msadpcm_ima(ma_dr_wav* pWav, ma_uint64 framesToRead, float* pBufferOut) +DRWAV_PRIVATE drwav_uint64 drwav_read_pcm_frames_f32__msadpcm_ima(drwav* pWav, drwav_uint64 framesToRead, float* pBufferOut) { - ma_uint64 totalFramesRead; - ma_int16 samples16[2048]; + drwav_uint64 totalFramesRead; + drwav_int16 samples16[2048]; totalFramesRead = 0; while (framesToRead > 0) { - ma_uint64 framesToReadThisIteration = ma_dr_wav_min(framesToRead, ma_dr_wav_countof(samples16)/pWav->channels); - ma_uint64 framesRead = ma_dr_wav_read_pcm_frames_s16(pWav, framesToReadThisIteration, samples16); + drwav_uint64 framesToReadThisIteration = drwav_min(framesToRead, drwav_countof(samples16)/pWav->channels); + drwav_uint64 framesRead = drwav_read_pcm_frames_s16(pWav, framesToReadThisIteration, samples16); if (framesRead == 0) { break; } - MA_DR_WAV_ASSERT(framesRead <= framesToReadThisIteration); - ma_dr_wav_s16_to_f32(pBufferOut, samples16, (size_t)(framesRead*pWav->channels)); + DRWAV_ASSERT(framesRead <= framesToReadThisIteration); + drwav_s16_to_f32(pBufferOut, samples16, (size_t)(framesRead*pWav->channels)); pBufferOut += framesRead*pWav->channels; framesToRead -= framesRead; totalFramesRead += framesRead; } return totalFramesRead; } -MA_PRIVATE ma_uint64 ma_dr_wav_read_pcm_frames_f32__ieee(ma_dr_wav* pWav, ma_uint64 framesToRead, float* pBufferOut) +DRWAV_PRIVATE drwav_uint64 drwav_read_pcm_frames_f32__ieee(drwav* pWav, drwav_uint64 framesToRead, float* pBufferOut) { - ma_uint64 totalFramesRead; - ma_uint8 sampleData[4096] = {0}; - ma_uint32 bytesPerFrame; - ma_uint32 bytesPerSample; - ma_uint64 samplesRead; - if (pWav->translatedFormatTag == MA_DR_WAVE_FORMAT_IEEE_FLOAT && pWav->bitsPerSample == 32) { - return ma_dr_wav_read_pcm_frames(pWav, framesToRead, pBufferOut); - } - bytesPerFrame = ma_dr_wav_get_bytes_per_pcm_frame(pWav); - if (bytesPerFrame == 0) { - return 0; - } - bytesPerSample = bytesPerFrame / pWav->channels; - if (bytesPerSample == 0 || (bytesPerFrame % pWav->channels) != 0) { - return 0; + drwav_uint64 totalFramesRead; + drwav_uint8 sampleData[4096] = {0}; + drwav_uint32 bytesPerFrame; + drwav_uint32 bytesPerSample; + drwav_uint64 samplesRead; + if (pWav->translatedFormatTag == DR_WAVE_FORMAT_IEEE_FLOAT && pWav->bitsPerSample == 32) { + return drwav_read_pcm_frames(pWav, framesToRead, pBufferOut); } - totalFramesRead = 0; - while (framesToRead > 0) { - ma_uint64 framesToReadThisIteration = ma_dr_wav_min(framesToRead, sizeof(sampleData)/bytesPerFrame); - ma_uint64 framesRead = ma_dr_wav_read_pcm_frames(pWav, framesToReadThisIteration, sampleData); - if (framesRead == 0) { - break; - } - MA_DR_WAV_ASSERT(framesRead <= framesToReadThisIteration); - samplesRead = framesRead * pWav->channels; - if ((samplesRead * bytesPerSample) > sizeof(sampleData)) { - MA_DR_WAV_ASSERT(MA_FALSE); - break; - } - ma_dr_wav__ieee_to_f32(pBufferOut, sampleData, (size_t)samplesRead, bytesPerSample); - pBufferOut += samplesRead; - framesToRead -= framesRead; - totalFramesRead += framesRead; - } - return totalFramesRead; -} -MA_PRIVATE ma_uint64 ma_dr_wav_read_pcm_frames_f32__alaw(ma_dr_wav* pWav, ma_uint64 framesToRead, float* pBufferOut) -{ - ma_uint64 totalFramesRead; - ma_uint8 sampleData[4096] = {0}; - ma_uint32 bytesPerFrame; - ma_uint32 bytesPerSample; - ma_uint64 samplesRead; - bytesPerFrame = ma_dr_wav_get_bytes_per_pcm_frame(pWav); + bytesPerFrame = drwav_get_bytes_per_pcm_frame(pWav); if (bytesPerFrame == 0) { return 0; } @@ -81972,42 +80774,32 @@ MA_PRIVATE ma_uint64 ma_dr_wav_read_pcm_frames_f32__alaw(ma_dr_wav* pWav, ma_uin } totalFramesRead = 0; while (framesToRead > 0) { - ma_uint64 framesToReadThisIteration = ma_dr_wav_min(framesToRead, sizeof(sampleData)/bytesPerFrame); - ma_uint64 framesRead = ma_dr_wav_read_pcm_frames(pWav, framesToReadThisIteration, sampleData); + drwav_uint64 framesToReadThisIteration = drwav_min(framesToRead, sizeof(sampleData)/bytesPerFrame); + drwav_uint64 framesRead = drwav_read_pcm_frames(pWav, framesToReadThisIteration, sampleData); if (framesRead == 0) { break; } - MA_DR_WAV_ASSERT(framesRead <= framesToReadThisIteration); + DRWAV_ASSERT(framesRead <= framesToReadThisIteration); samplesRead = framesRead * pWav->channels; if ((samplesRead * bytesPerSample) > sizeof(sampleData)) { - MA_DR_WAV_ASSERT(MA_FALSE); + DRWAV_ASSERT(DRWAV_FALSE); break; } - ma_dr_wav_alaw_to_f32(pBufferOut, sampleData, (size_t)samplesRead); - #ifdef MA_DR_WAV_LIBSNDFILE_COMPAT - { - if (pWav->container == ma_dr_wav_container_aiff) { - ma_uint64 iSample; - for (iSample = 0; iSample < samplesRead; iSample += 1) { - pBufferOut[iSample] = -pBufferOut[iSample]; - } - } - } - #endif + drwav__ieee_to_f32(pBufferOut, sampleData, (size_t)samplesRead, bytesPerSample); pBufferOut += samplesRead; framesToRead -= framesRead; totalFramesRead += framesRead; } return totalFramesRead; } -MA_PRIVATE ma_uint64 ma_dr_wav_read_pcm_frames_f32__mulaw(ma_dr_wav* pWav, ma_uint64 framesToRead, float* pBufferOut) +DRWAV_PRIVATE drwav_uint64 drwav_read_pcm_frames_f32__alaw(drwav* pWav, drwav_uint64 framesToRead, float* pBufferOut) { - ma_uint64 totalFramesRead; - ma_uint8 sampleData[4096] = {0}; - ma_uint32 bytesPerFrame; - ma_uint32 bytesPerSample; - ma_uint64 samplesRead; - bytesPerFrame = ma_dr_wav_get_bytes_per_pcm_frame(pWav); + drwav_uint64 totalFramesRead; + drwav_uint8 sampleData[4096] = {0}; + drwav_uint32 bytesPerFrame; + drwav_uint32 bytesPerSample; + drwav_uint64 samplesRead; + bytesPerFrame = drwav_get_bytes_per_pcm_frame(pWav); if (bytesPerFrame == 0) { return 0; } @@ -82017,277 +80809,32 @@ MA_PRIVATE ma_uint64 ma_dr_wav_read_pcm_frames_f32__mulaw(ma_dr_wav* pWav, ma_ui } totalFramesRead = 0; while (framesToRead > 0) { - ma_uint64 framesToReadThisIteration = ma_dr_wav_min(framesToRead, sizeof(sampleData)/bytesPerFrame); - ma_uint64 framesRead = ma_dr_wav_read_pcm_frames(pWav, framesToReadThisIteration, sampleData); + drwav_uint64 framesToReadThisIteration = drwav_min(framesToRead, sizeof(sampleData)/bytesPerFrame); + drwav_uint64 framesRead = drwav_read_pcm_frames(pWav, framesToReadThisIteration, sampleData); if (framesRead == 0) { break; } - MA_DR_WAV_ASSERT(framesRead <= framesToReadThisIteration); + DRWAV_ASSERT(framesRead <= framesToReadThisIteration); samplesRead = framesRead * pWav->channels; if ((samplesRead * bytesPerSample) > sizeof(sampleData)) { - MA_DR_WAV_ASSERT(MA_FALSE); + DRWAV_ASSERT(DRWAV_FALSE); break; } - ma_dr_wav_mulaw_to_f32(pBufferOut, sampleData, (size_t)samplesRead); - #ifdef MA_DR_WAV_LIBSNDFILE_COMPAT - { - if (pWav->container == ma_dr_wav_container_aiff) { - ma_uint64 iSample; - for (iSample = 0; iSample < samplesRead; iSample += 1) { - pBufferOut[iSample] = -pBufferOut[iSample]; - } - } - } - #endif + drwav_alaw_to_f32(pBufferOut, sampleData, (size_t)samplesRead); pBufferOut += samplesRead; framesToRead -= framesRead; totalFramesRead += framesRead; } return totalFramesRead; } -MA_API ma_uint64 ma_dr_wav_read_pcm_frames_f32(ma_dr_wav* pWav, ma_uint64 framesToRead, float* pBufferOut) -{ - if (pWav == NULL || framesToRead == 0) { - return 0; - } - if (pBufferOut == NULL) { - return ma_dr_wav_read_pcm_frames(pWav, framesToRead, NULL); - } - if (framesToRead * pWav->channels * sizeof(float) > MA_SIZE_MAX) { - framesToRead = MA_SIZE_MAX / sizeof(float) / pWav->channels; - } - if (pWav->translatedFormatTag == MA_DR_WAVE_FORMAT_PCM) { - return ma_dr_wav_read_pcm_frames_f32__pcm(pWav, framesToRead, pBufferOut); - } - if (pWav->translatedFormatTag == MA_DR_WAVE_FORMAT_ADPCM || pWav->translatedFormatTag == MA_DR_WAVE_FORMAT_DVI_ADPCM) { - return ma_dr_wav_read_pcm_frames_f32__msadpcm_ima(pWav, framesToRead, pBufferOut); - } - if (pWav->translatedFormatTag == MA_DR_WAVE_FORMAT_IEEE_FLOAT) { - return ma_dr_wav_read_pcm_frames_f32__ieee(pWav, framesToRead, pBufferOut); - } - if (pWav->translatedFormatTag == MA_DR_WAVE_FORMAT_ALAW) { - return ma_dr_wav_read_pcm_frames_f32__alaw(pWav, framesToRead, pBufferOut); - } - if (pWav->translatedFormatTag == MA_DR_WAVE_FORMAT_MULAW) { - return ma_dr_wav_read_pcm_frames_f32__mulaw(pWav, framesToRead, pBufferOut); - } - return 0; -} -MA_API ma_uint64 ma_dr_wav_read_pcm_frames_f32le(ma_dr_wav* pWav, ma_uint64 framesToRead, float* pBufferOut) -{ - ma_uint64 framesRead = ma_dr_wav_read_pcm_frames_f32(pWav, framesToRead, pBufferOut); - if (pBufferOut != NULL && ma_dr_wav__is_little_endian() == MA_FALSE) { - ma_dr_wav__bswap_samples_f32(pBufferOut, framesRead*pWav->channels); - } - return framesRead; -} -MA_API ma_uint64 ma_dr_wav_read_pcm_frames_f32be(ma_dr_wav* pWav, ma_uint64 framesToRead, float* pBufferOut) -{ - ma_uint64 framesRead = ma_dr_wav_read_pcm_frames_f32(pWav, framesToRead, pBufferOut); - if (pBufferOut != NULL && ma_dr_wav__is_little_endian() == MA_TRUE) { - ma_dr_wav__bswap_samples_f32(pBufferOut, framesRead*pWav->channels); - } - return framesRead; -} -MA_API void ma_dr_wav_u8_to_f32(float* pOut, const ma_uint8* pIn, size_t sampleCount) +DRWAV_PRIVATE drwav_uint64 drwav_read_pcm_frames_f32__mulaw(drwav* pWav, drwav_uint64 framesToRead, float* pBufferOut) { - size_t i; - if (pOut == NULL || pIn == NULL) { - return; - } -#ifdef MA_DR_WAV_LIBSNDFILE_COMPAT - for (i = 0; i < sampleCount; ++i) { - *pOut++ = (pIn[i] / 256.0f) * 2 - 1; - } -#else - for (i = 0; i < sampleCount; ++i) { - float x = pIn[i]; - x = x * 0.00784313725490196078f; - x = x - 1; - *pOut++ = x; - } -#endif -} -MA_API void ma_dr_wav_s16_to_f32(float* pOut, const ma_int16* pIn, size_t sampleCount) -{ - size_t i; - if (pOut == NULL || pIn == NULL) { - return; - } - for (i = 0; i < sampleCount; ++i) { - *pOut++ = pIn[i] * 0.000030517578125f; - } -} -MA_API void ma_dr_wav_s24_to_f32(float* pOut, const ma_uint8* pIn, size_t sampleCount) -{ - size_t i; - if (pOut == NULL || pIn == NULL) { - return; - } - for (i = 0; i < sampleCount; ++i) { - double x; - ma_uint32 a = ((ma_uint32)(pIn[i*3+0]) << 8); - ma_uint32 b = ((ma_uint32)(pIn[i*3+1]) << 16); - ma_uint32 c = ((ma_uint32)(pIn[i*3+2]) << 24); - x = (double)((ma_int32)(a | b | c) >> 8); - *pOut++ = (float)(x * 0.00000011920928955078125); - } -} -MA_API void ma_dr_wav_s32_to_f32(float* pOut, const ma_int32* pIn, size_t sampleCount) -{ - size_t i; - if (pOut == NULL || pIn == NULL) { - return; - } - for (i = 0; i < sampleCount; ++i) { - *pOut++ = (float)(pIn[i] / 2147483648.0); - } -} -MA_API void ma_dr_wav_f64_to_f32(float* pOut, const double* pIn, size_t sampleCount) -{ - size_t i; - if (pOut == NULL || pIn == NULL) { - return; - } - for (i = 0; i < sampleCount; ++i) { - *pOut++ = (float)pIn[i]; - } -} -MA_API void ma_dr_wav_alaw_to_f32(float* pOut, const ma_uint8* pIn, size_t sampleCount) -{ - size_t i; - if (pOut == NULL || pIn == NULL) { - return; - } - for (i = 0; i < sampleCount; ++i) { - *pOut++ = ma_dr_wav__alaw_to_s16(pIn[i]) / 32768.0f; - } -} -MA_API void ma_dr_wav_mulaw_to_f32(float* pOut, const ma_uint8* pIn, size_t sampleCount) -{ - size_t i; - if (pOut == NULL || pIn == NULL) { - return; - } - for (i = 0; i < sampleCount; ++i) { - *pOut++ = ma_dr_wav__mulaw_to_s16(pIn[i]) / 32768.0f; - } -} -MA_PRIVATE void ma_dr_wav__pcm_to_s32(ma_int32* pOut, const ma_uint8* pIn, size_t totalSampleCount, unsigned int bytesPerSample) -{ - unsigned int i; - if (bytesPerSample == 1) { - ma_dr_wav_u8_to_s32(pOut, pIn, totalSampleCount); - return; - } - if (bytesPerSample == 2) { - ma_dr_wav_s16_to_s32(pOut, (const ma_int16*)pIn, totalSampleCount); - return; - } - if (bytesPerSample == 3) { - ma_dr_wav_s24_to_s32(pOut, pIn, totalSampleCount); - return; - } - if (bytesPerSample == 4) { - for (i = 0; i < totalSampleCount; ++i) { - *pOut++ = ((const ma_int32*)pIn)[i]; - } - return; - } - if (bytesPerSample > 8) { - MA_DR_WAV_ZERO_MEMORY(pOut, totalSampleCount * sizeof(*pOut)); - return; - } - for (i = 0; i < totalSampleCount; ++i) { - ma_uint64 sample = 0; - unsigned int shift = (8 - bytesPerSample) * 8; - unsigned int j; - for (j = 0; j < bytesPerSample; j += 1) { - MA_DR_WAV_ASSERT(j < 8); - sample |= (ma_uint64)(pIn[j]) << shift; - shift += 8; - } - pIn += j; - *pOut++ = (ma_int32)((ma_int64)sample >> 32); - } -} -MA_PRIVATE void ma_dr_wav__ieee_to_s32(ma_int32* pOut, const ma_uint8* pIn, size_t totalSampleCount, unsigned int bytesPerSample) -{ - if (bytesPerSample == 4) { - ma_dr_wav_f32_to_s32(pOut, (const float*)pIn, totalSampleCount); - return; - } else if (bytesPerSample == 8) { - ma_dr_wav_f64_to_s32(pOut, (const double*)pIn, totalSampleCount); - return; - } else { - MA_DR_WAV_ZERO_MEMORY(pOut, totalSampleCount * sizeof(*pOut)); - return; - } -} -MA_PRIVATE ma_uint64 ma_dr_wav_read_pcm_frames_s32__pcm(ma_dr_wav* pWav, ma_uint64 framesToRead, ma_int32* pBufferOut) -{ - ma_uint64 totalFramesRead; - ma_uint8 sampleData[4096] = {0}; - ma_uint32 bytesPerFrame; - ma_uint32 bytesPerSample; - ma_uint64 samplesRead; - if (pWav->translatedFormatTag == MA_DR_WAVE_FORMAT_PCM && pWav->bitsPerSample == 32) { - return ma_dr_wav_read_pcm_frames(pWav, framesToRead, pBufferOut); - } - bytesPerFrame = ma_dr_wav_get_bytes_per_pcm_frame(pWav); - if (bytesPerFrame == 0) { - return 0; - } - bytesPerSample = bytesPerFrame / pWav->channels; - if (bytesPerSample == 0 || (bytesPerFrame % pWav->channels) != 0) { - return 0; - } - totalFramesRead = 0; - while (framesToRead > 0) { - ma_uint64 framesToReadThisIteration = ma_dr_wav_min(framesToRead, sizeof(sampleData)/bytesPerFrame); - ma_uint64 framesRead = ma_dr_wav_read_pcm_frames(pWav, framesToReadThisIteration, sampleData); - if (framesRead == 0) { - break; - } - MA_DR_WAV_ASSERT(framesRead <= framesToReadThisIteration); - samplesRead = framesRead * pWav->channels; - if ((samplesRead * bytesPerSample) > sizeof(sampleData)) { - MA_DR_WAV_ASSERT(MA_FALSE); - break; - } - ma_dr_wav__pcm_to_s32(pBufferOut, sampleData, (size_t)samplesRead, bytesPerSample); - pBufferOut += samplesRead; - framesToRead -= framesRead; - totalFramesRead += framesRead; - } - return totalFramesRead; -} -MA_PRIVATE ma_uint64 ma_dr_wav_read_pcm_frames_s32__msadpcm_ima(ma_dr_wav* pWav, ma_uint64 framesToRead, ma_int32* pBufferOut) -{ - ma_uint64 totalFramesRead = 0; - ma_int16 samples16[2048]; - while (framesToRead > 0) { - ma_uint64 framesToReadThisIteration = ma_dr_wav_min(framesToRead, ma_dr_wav_countof(samples16)/pWav->channels); - ma_uint64 framesRead = ma_dr_wav_read_pcm_frames_s16(pWav, framesToReadThisIteration, samples16); - if (framesRead == 0) { - break; - } - MA_DR_WAV_ASSERT(framesRead <= framesToReadThisIteration); - ma_dr_wav_s16_to_s32(pBufferOut, samples16, (size_t)(framesRead*pWav->channels)); - pBufferOut += framesRead*pWav->channels; - framesToRead -= framesRead; - totalFramesRead += framesRead; - } - return totalFramesRead; -} -MA_PRIVATE ma_uint64 ma_dr_wav_read_pcm_frames_s32__ieee(ma_dr_wav* pWav, ma_uint64 framesToRead, ma_int32* pBufferOut) -{ - ma_uint64 totalFramesRead; - ma_uint8 sampleData[4096] = {0}; - ma_uint32 bytesPerFrame; - ma_uint32 bytesPerSample; - ma_uint64 samplesRead; - bytesPerFrame = ma_dr_wav_get_bytes_per_pcm_frame(pWav); + drwav_uint64 totalFramesRead; + drwav_uint8 sampleData[4096] = {0}; + drwav_uint32 bytesPerFrame; + drwav_uint32 bytesPerSample; + drwav_uint64 samplesRead; + bytesPerFrame = drwav_get_bytes_per_pcm_frame(pWav); if (bytesPerFrame == 0) { return 0; } @@ -82297,32 +80844,214 @@ MA_PRIVATE ma_uint64 ma_dr_wav_read_pcm_frames_s32__ieee(ma_dr_wav* pWav, ma_uin } totalFramesRead = 0; while (framesToRead > 0) { - ma_uint64 framesToReadThisIteration = ma_dr_wav_min(framesToRead, sizeof(sampleData)/bytesPerFrame); - ma_uint64 framesRead = ma_dr_wav_read_pcm_frames(pWav, framesToReadThisIteration, sampleData); + drwav_uint64 framesToReadThisIteration = drwav_min(framesToRead, sizeof(sampleData)/bytesPerFrame); + drwav_uint64 framesRead = drwav_read_pcm_frames(pWav, framesToReadThisIteration, sampleData); if (framesRead == 0) { break; } - MA_DR_WAV_ASSERT(framesRead <= framesToReadThisIteration); + DRWAV_ASSERT(framesRead <= framesToReadThisIteration); samplesRead = framesRead * pWav->channels; if ((samplesRead * bytesPerSample) > sizeof(sampleData)) { - MA_DR_WAV_ASSERT(MA_FALSE); + DRWAV_ASSERT(DRWAV_FALSE); break; } - ma_dr_wav__ieee_to_s32(pBufferOut, sampleData, (size_t)samplesRead, bytesPerSample); + drwav_mulaw_to_f32(pBufferOut, sampleData, (size_t)samplesRead); pBufferOut += samplesRead; framesToRead -= framesRead; totalFramesRead += framesRead; } return totalFramesRead; } -MA_PRIVATE ma_uint64 ma_dr_wav_read_pcm_frames_s32__alaw(ma_dr_wav* pWav, ma_uint64 framesToRead, ma_int32* pBufferOut) +DRWAV_API drwav_uint64 drwav_read_pcm_frames_f32(drwav* pWav, drwav_uint64 framesToRead, float* pBufferOut) { - ma_uint64 totalFramesRead; - ma_uint8 sampleData[4096] = {0}; - ma_uint32 bytesPerFrame; - ma_uint32 bytesPerSample; - ma_uint64 samplesRead; - bytesPerFrame = ma_dr_wav_get_bytes_per_pcm_frame(pWav); + if (pWav == NULL || framesToRead == 0) { + return 0; + } + if (pBufferOut == NULL) { + return drwav_read_pcm_frames(pWav, framesToRead, NULL); + } + if (framesToRead * pWav->channels * sizeof(float) > DRWAV_SIZE_MAX) { + framesToRead = DRWAV_SIZE_MAX / sizeof(float) / pWav->channels; + } + if (pWav->translatedFormatTag == DR_WAVE_FORMAT_PCM) { + return drwav_read_pcm_frames_f32__pcm(pWav, framesToRead, pBufferOut); + } + if (pWav->translatedFormatTag == DR_WAVE_FORMAT_ADPCM || pWav->translatedFormatTag == DR_WAVE_FORMAT_DVI_ADPCM) { + return drwav_read_pcm_frames_f32__msadpcm_ima(pWav, framesToRead, pBufferOut); + } + if (pWav->translatedFormatTag == DR_WAVE_FORMAT_IEEE_FLOAT) { + return drwav_read_pcm_frames_f32__ieee(pWav, framesToRead, pBufferOut); + } + if (pWav->translatedFormatTag == DR_WAVE_FORMAT_ALAW) { + return drwav_read_pcm_frames_f32__alaw(pWav, framesToRead, pBufferOut); + } + if (pWav->translatedFormatTag == DR_WAVE_FORMAT_MULAW) { + return drwav_read_pcm_frames_f32__mulaw(pWav, framesToRead, pBufferOut); + } + return 0; +} +DRWAV_API drwav_uint64 drwav_read_pcm_frames_f32le(drwav* pWav, drwav_uint64 framesToRead, float* pBufferOut) +{ + drwav_uint64 framesRead = drwav_read_pcm_frames_f32(pWav, framesToRead, pBufferOut); + if (pBufferOut != NULL && drwav__is_little_endian() == DRWAV_FALSE) { + drwav__bswap_samples_f32(pBufferOut, framesRead*pWav->channels); + } + return framesRead; +} +DRWAV_API drwav_uint64 drwav_read_pcm_frames_f32be(drwav* pWav, drwav_uint64 framesToRead, float* pBufferOut) +{ + drwav_uint64 framesRead = drwav_read_pcm_frames_f32(pWav, framesToRead, pBufferOut); + if (pBufferOut != NULL && drwav__is_little_endian() == DRWAV_TRUE) { + drwav__bswap_samples_f32(pBufferOut, framesRead*pWav->channels); + } + return framesRead; +} +DRWAV_API void drwav_u8_to_f32(float* pOut, const drwav_uint8* pIn, size_t sampleCount) +{ + size_t i; + if (pOut == NULL || pIn == NULL) { + return; + } +#ifdef DR_WAV_LIBSNDFILE_COMPAT + for (i = 0; i < sampleCount; ++i) { + *pOut++ = (pIn[i] / 256.0f) * 2 - 1; + } +#else + for (i = 0; i < sampleCount; ++i) { + float x = pIn[i]; + x = x * 0.00784313725490196078f; + x = x - 1; + *pOut++ = x; + } +#endif +} +DRWAV_API void drwav_s16_to_f32(float* pOut, const drwav_int16* pIn, size_t sampleCount) +{ + size_t i; + if (pOut == NULL || pIn == NULL) { + return; + } + for (i = 0; i < sampleCount; ++i) { + *pOut++ = pIn[i] * 0.000030517578125f; + } +} +DRWAV_API void drwav_s24_to_f32(float* pOut, const drwav_uint8* pIn, size_t sampleCount) +{ + size_t i; + if (pOut == NULL || pIn == NULL) { + return; + } + for (i = 0; i < sampleCount; ++i) { + double x; + drwav_uint32 a = ((drwav_uint32)(pIn[i*3+0]) << 8); + drwav_uint32 b = ((drwav_uint32)(pIn[i*3+1]) << 16); + drwav_uint32 c = ((drwav_uint32)(pIn[i*3+2]) << 24); + x = (double)((drwav_int32)(a | b | c) >> 8); + *pOut++ = (float)(x * 0.00000011920928955078125); + } +} +DRWAV_API void drwav_s32_to_f32(float* pOut, const drwav_int32* pIn, size_t sampleCount) +{ + size_t i; + if (pOut == NULL || pIn == NULL) { + return; + } + for (i = 0; i < sampleCount; ++i) { + *pOut++ = (float)(pIn[i] / 2147483648.0); + } +} +DRWAV_API void drwav_f64_to_f32(float* pOut, const double* pIn, size_t sampleCount) +{ + size_t i; + if (pOut == NULL || pIn == NULL) { + return; + } + for (i = 0; i < sampleCount; ++i) { + *pOut++ = (float)pIn[i]; + } +} +DRWAV_API void drwav_alaw_to_f32(float* pOut, const drwav_uint8* pIn, size_t sampleCount) +{ + size_t i; + if (pOut == NULL || pIn == NULL) { + return; + } + for (i = 0; i < sampleCount; ++i) { + *pOut++ = drwav__alaw_to_s16(pIn[i]) / 32768.0f; + } +} +DRWAV_API void drwav_mulaw_to_f32(float* pOut, const drwav_uint8* pIn, size_t sampleCount) +{ + size_t i; + if (pOut == NULL || pIn == NULL) { + return; + } + for (i = 0; i < sampleCount; ++i) { + *pOut++ = drwav__mulaw_to_s16(pIn[i]) / 32768.0f; + } +} +DRWAV_PRIVATE void drwav__pcm_to_s32(drwav_int32* pOut, const drwav_uint8* pIn, size_t totalSampleCount, unsigned int bytesPerSample) +{ + unsigned int i; + if (bytesPerSample == 1) { + drwav_u8_to_s32(pOut, pIn, totalSampleCount); + return; + } + if (bytesPerSample == 2) { + drwav_s16_to_s32(pOut, (const drwav_int16*)pIn, totalSampleCount); + return; + } + if (bytesPerSample == 3) { + drwav_s24_to_s32(pOut, pIn, totalSampleCount); + return; + } + if (bytesPerSample == 4) { + for (i = 0; i < totalSampleCount; ++i) { + *pOut++ = ((const drwav_int32*)pIn)[i]; + } + return; + } + if (bytesPerSample > 8) { + DRWAV_ZERO_MEMORY(pOut, totalSampleCount * sizeof(*pOut)); + return; + } + for (i = 0; i < totalSampleCount; ++i) { + drwav_uint64 sample = 0; + unsigned int shift = (8 - bytesPerSample) * 8; + unsigned int j; + for (j = 0; j < bytesPerSample; j += 1) { + DRWAV_ASSERT(j < 8); + sample |= (drwav_uint64)(pIn[j]) << shift; + shift += 8; + } + pIn += j; + *pOut++ = (drwav_int32)((drwav_int64)sample >> 32); + } +} +DRWAV_PRIVATE void drwav__ieee_to_s32(drwav_int32* pOut, const drwav_uint8* pIn, size_t totalSampleCount, unsigned int bytesPerSample) +{ + if (bytesPerSample == 4) { + drwav_f32_to_s32(pOut, (const float*)pIn, totalSampleCount); + return; + } else if (bytesPerSample == 8) { + drwav_f64_to_s32(pOut, (const double*)pIn, totalSampleCount); + return; + } else { + DRWAV_ZERO_MEMORY(pOut, totalSampleCount * sizeof(*pOut)); + return; + } +} +DRWAV_PRIVATE drwav_uint64 drwav_read_pcm_frames_s32__pcm(drwav* pWav, drwav_uint64 framesToRead, drwav_int32* pBufferOut) +{ + drwav_uint64 totalFramesRead; + drwav_uint8 sampleData[4096] = {0}; + drwav_uint32 bytesPerFrame; + drwav_uint32 bytesPerSample; + drwav_uint64 samplesRead; + if (pWav->translatedFormatTag == DR_WAVE_FORMAT_PCM && pWav->bitsPerSample == 32) { + return drwav_read_pcm_frames(pWav, framesToRead, pBufferOut); + } + bytesPerFrame = drwav_get_bytes_per_pcm_frame(pWav); if (bytesPerFrame == 0) { return 0; } @@ -82332,42 +81061,85 @@ MA_PRIVATE ma_uint64 ma_dr_wav_read_pcm_frames_s32__alaw(ma_dr_wav* pWav, ma_uin } totalFramesRead = 0; while (framesToRead > 0) { - ma_uint64 framesToReadThisIteration = ma_dr_wav_min(framesToRead, sizeof(sampleData)/bytesPerFrame); - ma_uint64 framesRead = ma_dr_wav_read_pcm_frames(pWav, framesToReadThisIteration, sampleData); + drwav_uint64 framesToReadThisIteration = drwav_min(framesToRead, sizeof(sampleData)/bytesPerFrame); + drwav_uint64 framesRead = drwav_read_pcm_frames(pWav, framesToReadThisIteration, sampleData); if (framesRead == 0) { break; } - MA_DR_WAV_ASSERT(framesRead <= framesToReadThisIteration); + DRWAV_ASSERT(framesRead <= framesToReadThisIteration); samplesRead = framesRead * pWav->channels; if ((samplesRead * bytesPerSample) > sizeof(sampleData)) { - MA_DR_WAV_ASSERT(MA_FALSE); + DRWAV_ASSERT(DRWAV_FALSE); break; } - ma_dr_wav_alaw_to_s32(pBufferOut, sampleData, (size_t)samplesRead); - #ifdef MA_DR_WAV_LIBSNDFILE_COMPAT - { - if (pWav->container == ma_dr_wav_container_aiff) { - ma_uint64 iSample; - for (iSample = 0; iSample < samplesRead; iSample += 1) { - pBufferOut[iSample] = -pBufferOut[iSample]; - } - } + drwav__pcm_to_s32(pBufferOut, sampleData, (size_t)samplesRead, bytesPerSample); + pBufferOut += samplesRead; + framesToRead -= framesRead; + totalFramesRead += framesRead; + } + return totalFramesRead; +} +DRWAV_PRIVATE drwav_uint64 drwav_read_pcm_frames_s32__msadpcm_ima(drwav* pWav, drwav_uint64 framesToRead, drwav_int32* pBufferOut) +{ + drwav_uint64 totalFramesRead = 0; + drwav_int16 samples16[2048]; + while (framesToRead > 0) { + drwav_uint64 framesToReadThisIteration = drwav_min(framesToRead, drwav_countof(samples16)/pWav->channels); + drwav_uint64 framesRead = drwav_read_pcm_frames_s16(pWav, framesToReadThisIteration, samples16); + if (framesRead == 0) { + break; } - #endif + DRWAV_ASSERT(framesRead <= framesToReadThisIteration); + drwav_s16_to_s32(pBufferOut, samples16, (size_t)(framesRead*pWav->channels)); + pBufferOut += framesRead*pWav->channels; + framesToRead -= framesRead; + totalFramesRead += framesRead; + } + return totalFramesRead; +} +DRWAV_PRIVATE drwav_uint64 drwav_read_pcm_frames_s32__ieee(drwav* pWav, drwav_uint64 framesToRead, drwav_int32* pBufferOut) +{ + drwav_uint64 totalFramesRead; + drwav_uint8 sampleData[4096] = {0}; + drwav_uint32 bytesPerFrame; + drwav_uint32 bytesPerSample; + drwav_uint64 samplesRead; + bytesPerFrame = drwav_get_bytes_per_pcm_frame(pWav); + if (bytesPerFrame == 0) { + return 0; + } + bytesPerSample = bytesPerFrame / pWav->channels; + if (bytesPerSample == 0 || (bytesPerFrame % pWav->channels) != 0) { + return 0; + } + totalFramesRead = 0; + while (framesToRead > 0) { + drwav_uint64 framesToReadThisIteration = drwav_min(framesToRead, sizeof(sampleData)/bytesPerFrame); + drwav_uint64 framesRead = drwav_read_pcm_frames(pWav, framesToReadThisIteration, sampleData); + if (framesRead == 0) { + break; + } + DRWAV_ASSERT(framesRead <= framesToReadThisIteration); + samplesRead = framesRead * pWav->channels; + if ((samplesRead * bytesPerSample) > sizeof(sampleData)) { + DRWAV_ASSERT(DRWAV_FALSE); + break; + } + drwav__ieee_to_s32(pBufferOut, sampleData, (size_t)samplesRead, bytesPerSample); pBufferOut += samplesRead; framesToRead -= framesRead; totalFramesRead += framesRead; } return totalFramesRead; } -MA_PRIVATE ma_uint64 ma_dr_wav_read_pcm_frames_s32__mulaw(ma_dr_wav* pWav, ma_uint64 framesToRead, ma_int32* pBufferOut) +DRWAV_PRIVATE drwav_uint64 drwav_read_pcm_frames_s32__alaw(drwav* pWav, drwav_uint64 framesToRead, drwav_int32* pBufferOut) { - ma_uint64 totalFramesRead; - ma_uint8 sampleData[4096] = {0}; - ma_uint32 bytesPerFrame; - ma_uint32 bytesPerSample; - ma_uint64 samplesRead; - bytesPerFrame = ma_dr_wav_get_bytes_per_pcm_frame(pWav); + drwav_uint64 totalFramesRead; + drwav_uint8 sampleData[4096] = {0}; + drwav_uint32 bytesPerFrame; + drwav_uint32 bytesPerSample; + drwav_uint64 samplesRead; + bytesPerFrame = drwav_get_bytes_per_pcm_frame(pWav); if (bytesPerFrame == 0) { return 0; } @@ -82377,79 +81149,104 @@ MA_PRIVATE ma_uint64 ma_dr_wav_read_pcm_frames_s32__mulaw(ma_dr_wav* pWav, ma_ui } totalFramesRead = 0; while (framesToRead > 0) { - ma_uint64 framesToReadThisIteration = ma_dr_wav_min(framesToRead, sizeof(sampleData)/bytesPerFrame); - ma_uint64 framesRead = ma_dr_wav_read_pcm_frames(pWav, framesToReadThisIteration, sampleData); + drwav_uint64 framesToReadThisIteration = drwav_min(framesToRead, sizeof(sampleData)/bytesPerFrame); + drwav_uint64 framesRead = drwav_read_pcm_frames(pWav, framesToReadThisIteration, sampleData); if (framesRead == 0) { break; } - MA_DR_WAV_ASSERT(framesRead <= framesToReadThisIteration); + DRWAV_ASSERT(framesRead <= framesToReadThisIteration); samplesRead = framesRead * pWav->channels; if ((samplesRead * bytesPerSample) > sizeof(sampleData)) { - MA_DR_WAV_ASSERT(MA_FALSE); + DRWAV_ASSERT(DRWAV_FALSE); break; } - ma_dr_wav_mulaw_to_s32(pBufferOut, sampleData, (size_t)samplesRead); - #ifdef MA_DR_WAV_LIBSNDFILE_COMPAT - { - if (pWav->container == ma_dr_wav_container_aiff) { - ma_uint64 iSample; - for (iSample = 0; iSample < samplesRead; iSample += 1) { - pBufferOut[iSample] = -pBufferOut[iSample]; - } - } + drwav_alaw_to_s32(pBufferOut, sampleData, (size_t)samplesRead); + pBufferOut += samplesRead; + framesToRead -= framesRead; + totalFramesRead += framesRead; + } + return totalFramesRead; +} +DRWAV_PRIVATE drwav_uint64 drwav_read_pcm_frames_s32__mulaw(drwav* pWav, drwav_uint64 framesToRead, drwav_int32* pBufferOut) +{ + drwav_uint64 totalFramesRead; + drwav_uint8 sampleData[4096] = {0}; + drwav_uint32 bytesPerFrame; + drwav_uint32 bytesPerSample; + drwav_uint64 samplesRead; + bytesPerFrame = drwav_get_bytes_per_pcm_frame(pWav); + if (bytesPerFrame == 0) { + return 0; + } + bytesPerSample = bytesPerFrame / pWav->channels; + if (bytesPerSample == 0 || (bytesPerFrame % pWav->channels) != 0) { + return 0; + } + totalFramesRead = 0; + while (framesToRead > 0) { + drwav_uint64 framesToReadThisIteration = drwav_min(framesToRead, sizeof(sampleData)/bytesPerFrame); + drwav_uint64 framesRead = drwav_read_pcm_frames(pWav, framesToReadThisIteration, sampleData); + if (framesRead == 0) { + break; } - #endif + DRWAV_ASSERT(framesRead <= framesToReadThisIteration); + samplesRead = framesRead * pWav->channels; + if ((samplesRead * bytesPerSample) > sizeof(sampleData)) { + DRWAV_ASSERT(DRWAV_FALSE); + break; + } + drwav_mulaw_to_s32(pBufferOut, sampleData, (size_t)samplesRead); pBufferOut += samplesRead; framesToRead -= framesRead; totalFramesRead += framesRead; } return totalFramesRead; } -MA_API ma_uint64 ma_dr_wav_read_pcm_frames_s32(ma_dr_wav* pWav, ma_uint64 framesToRead, ma_int32* pBufferOut) +DRWAV_API drwav_uint64 drwav_read_pcm_frames_s32(drwav* pWav, drwav_uint64 framesToRead, drwav_int32* pBufferOut) { if (pWav == NULL || framesToRead == 0) { return 0; } if (pBufferOut == NULL) { - return ma_dr_wav_read_pcm_frames(pWav, framesToRead, NULL); + return drwav_read_pcm_frames(pWav, framesToRead, NULL); } - if (framesToRead * pWav->channels * sizeof(ma_int32) > MA_SIZE_MAX) { - framesToRead = MA_SIZE_MAX / sizeof(ma_int32) / pWav->channels; + if (framesToRead * pWav->channels * sizeof(drwav_int32) > DRWAV_SIZE_MAX) { + framesToRead = DRWAV_SIZE_MAX / sizeof(drwav_int32) / pWav->channels; } - if (pWav->translatedFormatTag == MA_DR_WAVE_FORMAT_PCM) { - return ma_dr_wav_read_pcm_frames_s32__pcm(pWav, framesToRead, pBufferOut); + if (pWav->translatedFormatTag == DR_WAVE_FORMAT_PCM) { + return drwav_read_pcm_frames_s32__pcm(pWav, framesToRead, pBufferOut); } - if (pWav->translatedFormatTag == MA_DR_WAVE_FORMAT_ADPCM || pWav->translatedFormatTag == MA_DR_WAVE_FORMAT_DVI_ADPCM) { - return ma_dr_wav_read_pcm_frames_s32__msadpcm_ima(pWav, framesToRead, pBufferOut); + if (pWav->translatedFormatTag == DR_WAVE_FORMAT_ADPCM || pWav->translatedFormatTag == DR_WAVE_FORMAT_DVI_ADPCM) { + return drwav_read_pcm_frames_s32__msadpcm_ima(pWav, framesToRead, pBufferOut); } - if (pWav->translatedFormatTag == MA_DR_WAVE_FORMAT_IEEE_FLOAT) { - return ma_dr_wav_read_pcm_frames_s32__ieee(pWav, framesToRead, pBufferOut); + if (pWav->translatedFormatTag == DR_WAVE_FORMAT_IEEE_FLOAT) { + return drwav_read_pcm_frames_s32__ieee(pWav, framesToRead, pBufferOut); } - if (pWav->translatedFormatTag == MA_DR_WAVE_FORMAT_ALAW) { - return ma_dr_wav_read_pcm_frames_s32__alaw(pWav, framesToRead, pBufferOut); + if (pWav->translatedFormatTag == DR_WAVE_FORMAT_ALAW) { + return drwav_read_pcm_frames_s32__alaw(pWav, framesToRead, pBufferOut); } - if (pWav->translatedFormatTag == MA_DR_WAVE_FORMAT_MULAW) { - return ma_dr_wav_read_pcm_frames_s32__mulaw(pWav, framesToRead, pBufferOut); + if (pWav->translatedFormatTag == DR_WAVE_FORMAT_MULAW) { + return drwav_read_pcm_frames_s32__mulaw(pWav, framesToRead, pBufferOut); } return 0; } -MA_API ma_uint64 ma_dr_wav_read_pcm_frames_s32le(ma_dr_wav* pWav, ma_uint64 framesToRead, ma_int32* pBufferOut) +DRWAV_API drwav_uint64 drwav_read_pcm_frames_s32le(drwav* pWav, drwav_uint64 framesToRead, drwav_int32* pBufferOut) { - ma_uint64 framesRead = ma_dr_wav_read_pcm_frames_s32(pWav, framesToRead, pBufferOut); - if (pBufferOut != NULL && ma_dr_wav__is_little_endian() == MA_FALSE) { - ma_dr_wav__bswap_samples_s32(pBufferOut, framesRead*pWav->channels); + drwav_uint64 framesRead = drwav_read_pcm_frames_s32(pWav, framesToRead, pBufferOut); + if (pBufferOut != NULL && drwav__is_little_endian() == DRWAV_FALSE) { + drwav__bswap_samples_s32(pBufferOut, framesRead*pWav->channels); } return framesRead; } -MA_API ma_uint64 ma_dr_wav_read_pcm_frames_s32be(ma_dr_wav* pWav, ma_uint64 framesToRead, ma_int32* pBufferOut) +DRWAV_API drwav_uint64 drwav_read_pcm_frames_s32be(drwav* pWav, drwav_uint64 framesToRead, drwav_int32* pBufferOut) { - ma_uint64 framesRead = ma_dr_wav_read_pcm_frames_s32(pWav, framesToRead, pBufferOut); - if (pBufferOut != NULL && ma_dr_wav__is_little_endian() == MA_TRUE) { - ma_dr_wav__bswap_samples_s32(pBufferOut, framesRead*pWav->channels); + drwav_uint64 framesRead = drwav_read_pcm_frames_s32(pWav, framesToRead, pBufferOut); + if (pBufferOut != NULL && drwav__is_little_endian() == DRWAV_TRUE) { + drwav__bswap_samples_s32(pBufferOut, framesRead*pWav->channels); } return framesRead; } -MA_API void ma_dr_wav_u8_to_s32(ma_int32* pOut, const ma_uint8* pIn, size_t sampleCount) +DRWAV_API void drwav_u8_to_s32(drwav_int32* pOut, const drwav_uint8* pIn, size_t sampleCount) { size_t i; if (pOut == NULL || pIn == NULL) { @@ -82459,7 +81256,7 @@ MA_API void ma_dr_wav_u8_to_s32(ma_int32* pOut, const ma_uint8* pIn, size_t samp *pOut++ = ((int)pIn[i] - 128) << 24; } } -MA_API void ma_dr_wav_s16_to_s32(ma_int32* pOut, const ma_int16* pIn, size_t sampleCount) +DRWAV_API void drwav_s16_to_s32(drwav_int32* pOut, const drwav_int16* pIn, size_t sampleCount) { size_t i; if (pOut == NULL || pIn == NULL) { @@ -82469,7 +81266,7 @@ MA_API void ma_dr_wav_s16_to_s32(ma_int32* pOut, const ma_int16* pIn, size_t sam *pOut++ = pIn[i] << 16; } } -MA_API void ma_dr_wav_s24_to_s32(ma_int32* pOut, const ma_uint8* pIn, size_t sampleCount) +DRWAV_API void drwav_s24_to_s32(drwav_int32* pOut, const drwav_uint8* pIn, size_t sampleCount) { size_t i; if (pOut == NULL || pIn == NULL) { @@ -82479,73 +81276,73 @@ MA_API void ma_dr_wav_s24_to_s32(ma_int32* pOut, const ma_uint8* pIn, size_t sam unsigned int s0 = pIn[i*3 + 0]; unsigned int s1 = pIn[i*3 + 1]; unsigned int s2 = pIn[i*3 + 2]; - ma_int32 sample32 = (ma_int32)((s0 << 8) | (s1 << 16) | (s2 << 24)); + drwav_int32 sample32 = (drwav_int32)((s0 << 8) | (s1 << 16) | (s2 << 24)); *pOut++ = sample32; } } -MA_API void ma_dr_wav_f32_to_s32(ma_int32* pOut, const float* pIn, size_t sampleCount) +DRWAV_API void drwav_f32_to_s32(drwav_int32* pOut, const float* pIn, size_t sampleCount) { size_t i; if (pOut == NULL || pIn == NULL) { return; } for (i = 0; i < sampleCount; ++i) { - *pOut++ = (ma_int32)(2147483648.0f * pIn[i]); + *pOut++ = (drwav_int32)(2147483648.0 * pIn[i]); } } -MA_API void ma_dr_wav_f64_to_s32(ma_int32* pOut, const double* pIn, size_t sampleCount) +DRWAV_API void drwav_f64_to_s32(drwav_int32* pOut, const double* pIn, size_t sampleCount) { size_t i; if (pOut == NULL || pIn == NULL) { return; } for (i = 0; i < sampleCount; ++i) { - *pOut++ = (ma_int32)(2147483648.0 * pIn[i]); + *pOut++ = (drwav_int32)(2147483648.0 * pIn[i]); } } -MA_API void ma_dr_wav_alaw_to_s32(ma_int32* pOut, const ma_uint8* pIn, size_t sampleCount) +DRWAV_API void drwav_alaw_to_s32(drwav_int32* pOut, const drwav_uint8* pIn, size_t sampleCount) { size_t i; if (pOut == NULL || pIn == NULL) { return; } for (i = 0; i < sampleCount; ++i) { - *pOut++ = ((ma_int32)ma_dr_wav__alaw_to_s16(pIn[i])) << 16; + *pOut++ = ((drwav_int32)drwav__alaw_to_s16(pIn[i])) << 16; } } -MA_API void ma_dr_wav_mulaw_to_s32(ma_int32* pOut, const ma_uint8* pIn, size_t sampleCount) +DRWAV_API void drwav_mulaw_to_s32(drwav_int32* pOut, const drwav_uint8* pIn, size_t sampleCount) { size_t i; if (pOut == NULL || pIn == NULL) { return; } for (i= 0; i < sampleCount; ++i) { - *pOut++ = ((ma_int32)ma_dr_wav__mulaw_to_s16(pIn[i])) << 16; + *pOut++ = ((drwav_int32)drwav__mulaw_to_s16(pIn[i])) << 16; } } -MA_PRIVATE ma_int16* ma_dr_wav__read_pcm_frames_and_close_s16(ma_dr_wav* pWav, unsigned int* channels, unsigned int* sampleRate, ma_uint64* totalFrameCount) +DRWAV_PRIVATE drwav_int16* drwav__read_pcm_frames_and_close_s16(drwav* pWav, unsigned int* channels, unsigned int* sampleRate, drwav_uint64* totalFrameCount) { - ma_uint64 sampleDataSize; - ma_int16* pSampleData; - ma_uint64 framesRead; - MA_DR_WAV_ASSERT(pWav != NULL); - sampleDataSize = pWav->totalPCMFrameCount * pWav->channels * sizeof(ma_int16); - if (sampleDataSize > MA_SIZE_MAX) { - ma_dr_wav_uninit(pWav); + drwav_uint64 sampleDataSize; + drwav_int16* pSampleData; + drwav_uint64 framesRead; + DRWAV_ASSERT(pWav != NULL); + sampleDataSize = pWav->totalPCMFrameCount * pWav->channels * sizeof(drwav_int16); + if (sampleDataSize > DRWAV_SIZE_MAX) { + drwav_uninit(pWav); return NULL; } - pSampleData = (ma_int16*)ma_dr_wav__malloc_from_callbacks((size_t)sampleDataSize, &pWav->allocationCallbacks); + pSampleData = (drwav_int16*)drwav__malloc_from_callbacks((size_t)sampleDataSize, &pWav->allocationCallbacks); if (pSampleData == NULL) { - ma_dr_wav_uninit(pWav); + drwav_uninit(pWav); return NULL; } - framesRead = ma_dr_wav_read_pcm_frames_s16(pWav, (size_t)pWav->totalPCMFrameCount, pSampleData); + framesRead = drwav_read_pcm_frames_s16(pWav, (size_t)pWav->totalPCMFrameCount, pSampleData); if (framesRead != pWav->totalPCMFrameCount) { - ma_dr_wav__free_from_callbacks(pSampleData, &pWav->allocationCallbacks); - ma_dr_wav_uninit(pWav); + drwav__free_from_callbacks(pSampleData, &pWav->allocationCallbacks); + drwav_uninit(pWav); return NULL; } - ma_dr_wav_uninit(pWav); + drwav_uninit(pWav); if (sampleRate) { *sampleRate = pWav->sampleRate; } @@ -82557,29 +81354,29 @@ MA_PRIVATE ma_int16* ma_dr_wav__read_pcm_frames_and_close_s16(ma_dr_wav* pWav, u } return pSampleData; } -MA_PRIVATE float* ma_dr_wav__read_pcm_frames_and_close_f32(ma_dr_wav* pWav, unsigned int* channels, unsigned int* sampleRate, ma_uint64* totalFrameCount) +DRWAV_PRIVATE float* drwav__read_pcm_frames_and_close_f32(drwav* pWav, unsigned int* channels, unsigned int* sampleRate, drwav_uint64* totalFrameCount) { - ma_uint64 sampleDataSize; + drwav_uint64 sampleDataSize; float* pSampleData; - ma_uint64 framesRead; - MA_DR_WAV_ASSERT(pWav != NULL); + drwav_uint64 framesRead; + DRWAV_ASSERT(pWav != NULL); sampleDataSize = pWav->totalPCMFrameCount * pWav->channels * sizeof(float); - if (sampleDataSize > MA_SIZE_MAX) { - ma_dr_wav_uninit(pWav); + if (sampleDataSize > DRWAV_SIZE_MAX) { + drwav_uninit(pWav); return NULL; } - pSampleData = (float*)ma_dr_wav__malloc_from_callbacks((size_t)sampleDataSize, &pWav->allocationCallbacks); + pSampleData = (float*)drwav__malloc_from_callbacks((size_t)sampleDataSize, &pWav->allocationCallbacks); if (pSampleData == NULL) { - ma_dr_wav_uninit(pWav); + drwav_uninit(pWav); return NULL; } - framesRead = ma_dr_wav_read_pcm_frames_f32(pWav, (size_t)pWav->totalPCMFrameCount, pSampleData); + framesRead = drwav_read_pcm_frames_f32(pWav, (size_t)pWav->totalPCMFrameCount, pSampleData); if (framesRead != pWav->totalPCMFrameCount) { - ma_dr_wav__free_from_callbacks(pSampleData, &pWav->allocationCallbacks); - ma_dr_wav_uninit(pWav); + drwav__free_from_callbacks(pSampleData, &pWav->allocationCallbacks); + drwav_uninit(pWav); return NULL; } - ma_dr_wav_uninit(pWav); + drwav_uninit(pWav); if (sampleRate) { *sampleRate = pWav->sampleRate; } @@ -82591,29 +81388,29 @@ MA_PRIVATE float* ma_dr_wav__read_pcm_frames_and_close_f32(ma_dr_wav* pWav, unsi } return pSampleData; } -MA_PRIVATE ma_int32* ma_dr_wav__read_pcm_frames_and_close_s32(ma_dr_wav* pWav, unsigned int* channels, unsigned int* sampleRate, ma_uint64* totalFrameCount) +DRWAV_PRIVATE drwav_int32* drwav__read_pcm_frames_and_close_s32(drwav* pWav, unsigned int* channels, unsigned int* sampleRate, drwav_uint64* totalFrameCount) { - ma_uint64 sampleDataSize; - ma_int32* pSampleData; - ma_uint64 framesRead; - MA_DR_WAV_ASSERT(pWav != NULL); - sampleDataSize = pWav->totalPCMFrameCount * pWav->channels * sizeof(ma_int32); - if (sampleDataSize > MA_SIZE_MAX) { - ma_dr_wav_uninit(pWav); + drwav_uint64 sampleDataSize; + drwav_int32* pSampleData; + drwav_uint64 framesRead; + DRWAV_ASSERT(pWav != NULL); + sampleDataSize = pWav->totalPCMFrameCount * pWav->channels * sizeof(drwav_int32); + if (sampleDataSize > DRWAV_SIZE_MAX) { + drwav_uninit(pWav); return NULL; } - pSampleData = (ma_int32*)ma_dr_wav__malloc_from_callbacks((size_t)sampleDataSize, &pWav->allocationCallbacks); + pSampleData = (drwav_int32*)drwav__malloc_from_callbacks((size_t)sampleDataSize, &pWav->allocationCallbacks); if (pSampleData == NULL) { - ma_dr_wav_uninit(pWav); + drwav_uninit(pWav); return NULL; } - framesRead = ma_dr_wav_read_pcm_frames_s32(pWav, (size_t)pWav->totalPCMFrameCount, pSampleData); + framesRead = drwav_read_pcm_frames_s32(pWav, (size_t)pWav->totalPCMFrameCount, pSampleData); if (framesRead != pWav->totalPCMFrameCount) { - ma_dr_wav__free_from_callbacks(pSampleData, &pWav->allocationCallbacks); - ma_dr_wav_uninit(pWav); + drwav__free_from_callbacks(pSampleData, &pWav->allocationCallbacks); + drwav_uninit(pWav); return NULL; } - ma_dr_wav_uninit(pWav); + drwav_uninit(pWav); if (sampleRate) { *sampleRate = pWav->sampleRate; } @@ -82625,9 +81422,9 @@ MA_PRIVATE ma_int32* ma_dr_wav__read_pcm_frames_and_close_s32(ma_dr_wav* pWav, u } return pSampleData; } -MA_API ma_int16* ma_dr_wav_open_and_read_pcm_frames_s16(ma_dr_wav_read_proc onRead, ma_dr_wav_seek_proc onSeek, void* pUserData, unsigned int* channelsOut, unsigned int* sampleRateOut, ma_uint64* totalFrameCountOut, const ma_allocation_callbacks* pAllocationCallbacks) +DRWAV_API drwav_int16* drwav_open_and_read_pcm_frames_s16(drwav_read_proc onRead, drwav_seek_proc onSeek, void* pUserData, unsigned int* channelsOut, unsigned int* sampleRateOut, drwav_uint64* totalFrameCountOut, const drwav_allocation_callbacks* pAllocationCallbacks) { - ma_dr_wav wav; + drwav wav; if (channelsOut) { *channelsOut = 0; } @@ -82637,14 +81434,14 @@ MA_API ma_int16* ma_dr_wav_open_and_read_pcm_frames_s16(ma_dr_wav_read_proc onRe if (totalFrameCountOut) { *totalFrameCountOut = 0; } - if (!ma_dr_wav_init(&wav, onRead, onSeek, pUserData, pAllocationCallbacks)) { + if (!drwav_init(&wav, onRead, onSeek, pUserData, pAllocationCallbacks)) { return NULL; } - return ma_dr_wav__read_pcm_frames_and_close_s16(&wav, channelsOut, sampleRateOut, totalFrameCountOut); + return drwav__read_pcm_frames_and_close_s16(&wav, channelsOut, sampleRateOut, totalFrameCountOut); } -MA_API float* ma_dr_wav_open_and_read_pcm_frames_f32(ma_dr_wav_read_proc onRead, ma_dr_wav_seek_proc onSeek, void* pUserData, unsigned int* channelsOut, unsigned int* sampleRateOut, ma_uint64* totalFrameCountOut, const ma_allocation_callbacks* pAllocationCallbacks) +DRWAV_API float* drwav_open_and_read_pcm_frames_f32(drwav_read_proc onRead, drwav_seek_proc onSeek, void* pUserData, unsigned int* channelsOut, unsigned int* sampleRateOut, drwav_uint64* totalFrameCountOut, const drwav_allocation_callbacks* pAllocationCallbacks) { - ma_dr_wav wav; + drwav wav; if (channelsOut) { *channelsOut = 0; } @@ -82654,14 +81451,14 @@ MA_API float* ma_dr_wav_open_and_read_pcm_frames_f32(ma_dr_wav_read_proc onRead, if (totalFrameCountOut) { *totalFrameCountOut = 0; } - if (!ma_dr_wav_init(&wav, onRead, onSeek, pUserData, pAllocationCallbacks)) { + if (!drwav_init(&wav, onRead, onSeek, pUserData, pAllocationCallbacks)) { return NULL; } - return ma_dr_wav__read_pcm_frames_and_close_f32(&wav, channelsOut, sampleRateOut, totalFrameCountOut); + return drwav__read_pcm_frames_and_close_f32(&wav, channelsOut, sampleRateOut, totalFrameCountOut); } -MA_API ma_int32* ma_dr_wav_open_and_read_pcm_frames_s32(ma_dr_wav_read_proc onRead, ma_dr_wav_seek_proc onSeek, void* pUserData, unsigned int* channelsOut, unsigned int* sampleRateOut, ma_uint64* totalFrameCountOut, const ma_allocation_callbacks* pAllocationCallbacks) +DRWAV_API drwav_int32* drwav_open_and_read_pcm_frames_s32(drwav_read_proc onRead, drwav_seek_proc onSeek, void* pUserData, unsigned int* channelsOut, unsigned int* sampleRateOut, drwav_uint64* totalFrameCountOut, const drwav_allocation_callbacks* pAllocationCallbacks) { - ma_dr_wav wav; + drwav wav; if (channelsOut) { *channelsOut = 0; } @@ -82671,15 +81468,15 @@ MA_API ma_int32* ma_dr_wav_open_and_read_pcm_frames_s32(ma_dr_wav_read_proc onRe if (totalFrameCountOut) { *totalFrameCountOut = 0; } - if (!ma_dr_wav_init(&wav, onRead, onSeek, pUserData, pAllocationCallbacks)) { + if (!drwav_init(&wav, onRead, onSeek, pUserData, pAllocationCallbacks)) { return NULL; } - return ma_dr_wav__read_pcm_frames_and_close_s32(&wav, channelsOut, sampleRateOut, totalFrameCountOut); + return drwav__read_pcm_frames_and_close_s32(&wav, channelsOut, sampleRateOut, totalFrameCountOut); } -#ifndef MA_DR_WAV_NO_STDIO -MA_API ma_int16* ma_dr_wav_open_file_and_read_pcm_frames_s16(const char* filename, unsigned int* channelsOut, unsigned int* sampleRateOut, ma_uint64* totalFrameCountOut, const ma_allocation_callbacks* pAllocationCallbacks) +#ifndef DR_WAV_NO_STDIO +DRWAV_API drwav_int16* drwav_open_file_and_read_pcm_frames_s16(const char* filename, unsigned int* channelsOut, unsigned int* sampleRateOut, drwav_uint64* totalFrameCountOut, const drwav_allocation_callbacks* pAllocationCallbacks) { - ma_dr_wav wav; + drwav wav; if (channelsOut) { *channelsOut = 0; } @@ -82689,14 +81486,14 @@ MA_API ma_int16* ma_dr_wav_open_file_and_read_pcm_frames_s16(const char* filenam if (totalFrameCountOut) { *totalFrameCountOut = 0; } - if (!ma_dr_wav_init_file(&wav, filename, pAllocationCallbacks)) { + if (!drwav_init_file(&wav, filename, pAllocationCallbacks)) { return NULL; } - return ma_dr_wav__read_pcm_frames_and_close_s16(&wav, channelsOut, sampleRateOut, totalFrameCountOut); + return drwav__read_pcm_frames_and_close_s16(&wav, channelsOut, sampleRateOut, totalFrameCountOut); } -MA_API float* ma_dr_wav_open_file_and_read_pcm_frames_f32(const char* filename, unsigned int* channelsOut, unsigned int* sampleRateOut, ma_uint64* totalFrameCountOut, const ma_allocation_callbacks* pAllocationCallbacks) +DRWAV_API float* drwav_open_file_and_read_pcm_frames_f32(const char* filename, unsigned int* channelsOut, unsigned int* sampleRateOut, drwav_uint64* totalFrameCountOut, const drwav_allocation_callbacks* pAllocationCallbacks) { - ma_dr_wav wav; + drwav wav; if (channelsOut) { *channelsOut = 0; } @@ -82706,14 +81503,14 @@ MA_API float* ma_dr_wav_open_file_and_read_pcm_frames_f32(const char* filename, if (totalFrameCountOut) { *totalFrameCountOut = 0; } - if (!ma_dr_wav_init_file(&wav, filename, pAllocationCallbacks)) { + if (!drwav_init_file(&wav, filename, pAllocationCallbacks)) { return NULL; } - return ma_dr_wav__read_pcm_frames_and_close_f32(&wav, channelsOut, sampleRateOut, totalFrameCountOut); + return drwav__read_pcm_frames_and_close_f32(&wav, channelsOut, sampleRateOut, totalFrameCountOut); } -MA_API ma_int32* ma_dr_wav_open_file_and_read_pcm_frames_s32(const char* filename, unsigned int* channelsOut, unsigned int* sampleRateOut, ma_uint64* totalFrameCountOut, const ma_allocation_callbacks* pAllocationCallbacks) +DRWAV_API drwav_int32* drwav_open_file_and_read_pcm_frames_s32(const char* filename, unsigned int* channelsOut, unsigned int* sampleRateOut, drwav_uint64* totalFrameCountOut, const drwav_allocation_callbacks* pAllocationCallbacks) { - ma_dr_wav wav; + drwav wav; if (channelsOut) { *channelsOut = 0; } @@ -82723,15 +81520,15 @@ MA_API ma_int32* ma_dr_wav_open_file_and_read_pcm_frames_s32(const char* filenam if (totalFrameCountOut) { *totalFrameCountOut = 0; } - if (!ma_dr_wav_init_file(&wav, filename, pAllocationCallbacks)) { + if (!drwav_init_file(&wav, filename, pAllocationCallbacks)) { return NULL; } - return ma_dr_wav__read_pcm_frames_and_close_s32(&wav, channelsOut, sampleRateOut, totalFrameCountOut); + return drwav__read_pcm_frames_and_close_s32(&wav, channelsOut, sampleRateOut, totalFrameCountOut); } -#ifndef MA_DR_WAV_NO_WCHAR -MA_API ma_int16* ma_dr_wav_open_file_and_read_pcm_frames_s16_w(const wchar_t* filename, unsigned int* channelsOut, unsigned int* sampleRateOut, ma_uint64* totalFrameCountOut, const ma_allocation_callbacks* pAllocationCallbacks) +#ifndef DR_WAV_NO_WCHAR +DRWAV_API drwav_int16* drwav_open_file_and_read_pcm_frames_s16_w(const wchar_t* filename, unsigned int* channelsOut, unsigned int* sampleRateOut, drwav_uint64* totalFrameCountOut, const drwav_allocation_callbacks* pAllocationCallbacks) { - ma_dr_wav wav; + drwav wav; if (sampleRateOut) { *sampleRateOut = 0; } @@ -82741,14 +81538,14 @@ MA_API ma_int16* ma_dr_wav_open_file_and_read_pcm_frames_s16_w(const wchar_t* fi if (totalFrameCountOut) { *totalFrameCountOut = 0; } - if (!ma_dr_wav_init_file_w(&wav, filename, pAllocationCallbacks)) { + if (!drwav_init_file_w(&wav, filename, pAllocationCallbacks)) { return NULL; } - return ma_dr_wav__read_pcm_frames_and_close_s16(&wav, channelsOut, sampleRateOut, totalFrameCountOut); + return drwav__read_pcm_frames_and_close_s16(&wav, channelsOut, sampleRateOut, totalFrameCountOut); } -MA_API float* ma_dr_wav_open_file_and_read_pcm_frames_f32_w(const wchar_t* filename, unsigned int* channelsOut, unsigned int* sampleRateOut, ma_uint64* totalFrameCountOut, const ma_allocation_callbacks* pAllocationCallbacks) +DRWAV_API float* drwav_open_file_and_read_pcm_frames_f32_w(const wchar_t* filename, unsigned int* channelsOut, unsigned int* sampleRateOut, drwav_uint64* totalFrameCountOut, const drwav_allocation_callbacks* pAllocationCallbacks) { - ma_dr_wav wav; + drwav wav; if (sampleRateOut) { *sampleRateOut = 0; } @@ -82758,14 +81555,14 @@ MA_API float* ma_dr_wav_open_file_and_read_pcm_frames_f32_w(const wchar_t* filen if (totalFrameCountOut) { *totalFrameCountOut = 0; } - if (!ma_dr_wav_init_file_w(&wav, filename, pAllocationCallbacks)) { + if (!drwav_init_file_w(&wav, filename, pAllocationCallbacks)) { return NULL; } - return ma_dr_wav__read_pcm_frames_and_close_f32(&wav, channelsOut, sampleRateOut, totalFrameCountOut); + return drwav__read_pcm_frames_and_close_f32(&wav, channelsOut, sampleRateOut, totalFrameCountOut); } -MA_API ma_int32* ma_dr_wav_open_file_and_read_pcm_frames_s32_w(const wchar_t* filename, unsigned int* channelsOut, unsigned int* sampleRateOut, ma_uint64* totalFrameCountOut, const ma_allocation_callbacks* pAllocationCallbacks) +DRWAV_API drwav_int32* drwav_open_file_and_read_pcm_frames_s32_w(const wchar_t* filename, unsigned int* channelsOut, unsigned int* sampleRateOut, drwav_uint64* totalFrameCountOut, const drwav_allocation_callbacks* pAllocationCallbacks) { - ma_dr_wav wav; + drwav wav; if (sampleRateOut) { *sampleRateOut = 0; } @@ -82775,16 +81572,16 @@ MA_API ma_int32* ma_dr_wav_open_file_and_read_pcm_frames_s32_w(const wchar_t* fi if (totalFrameCountOut) { *totalFrameCountOut = 0; } - if (!ma_dr_wav_init_file_w(&wav, filename, pAllocationCallbacks)) { + if (!drwav_init_file_w(&wav, filename, pAllocationCallbacks)) { return NULL; } - return ma_dr_wav__read_pcm_frames_and_close_s32(&wav, channelsOut, sampleRateOut, totalFrameCountOut); + return drwav__read_pcm_frames_and_close_s32(&wav, channelsOut, sampleRateOut, totalFrameCountOut); } #endif #endif -MA_API ma_int16* ma_dr_wav_open_memory_and_read_pcm_frames_s16(const void* data, size_t dataSize, unsigned int* channelsOut, unsigned int* sampleRateOut, ma_uint64* totalFrameCountOut, const ma_allocation_callbacks* pAllocationCallbacks) +DRWAV_API drwav_int16* drwav_open_memory_and_read_pcm_frames_s16(const void* data, size_t dataSize, unsigned int* channelsOut, unsigned int* sampleRateOut, drwav_uint64* totalFrameCountOut, const drwav_allocation_callbacks* pAllocationCallbacks) { - ma_dr_wav wav; + drwav wav; if (channelsOut) { *channelsOut = 0; } @@ -82794,14 +81591,14 @@ MA_API ma_int16* ma_dr_wav_open_memory_and_read_pcm_frames_s16(const void* data, if (totalFrameCountOut) { *totalFrameCountOut = 0; } - if (!ma_dr_wav_init_memory(&wav, data, dataSize, pAllocationCallbacks)) { + if (!drwav_init_memory(&wav, data, dataSize, pAllocationCallbacks)) { return NULL; } - return ma_dr_wav__read_pcm_frames_and_close_s16(&wav, channelsOut, sampleRateOut, totalFrameCountOut); + return drwav__read_pcm_frames_and_close_s16(&wav, channelsOut, sampleRateOut, totalFrameCountOut); } -MA_API float* ma_dr_wav_open_memory_and_read_pcm_frames_f32(const void* data, size_t dataSize, unsigned int* channelsOut, unsigned int* sampleRateOut, ma_uint64* totalFrameCountOut, const ma_allocation_callbacks* pAllocationCallbacks) +DRWAV_API float* drwav_open_memory_and_read_pcm_frames_f32(const void* data, size_t dataSize, unsigned int* channelsOut, unsigned int* sampleRateOut, drwav_uint64* totalFrameCountOut, const drwav_allocation_callbacks* pAllocationCallbacks) { - ma_dr_wav wav; + drwav wav; if (channelsOut) { *channelsOut = 0; } @@ -82811,14 +81608,14 @@ MA_API float* ma_dr_wav_open_memory_and_read_pcm_frames_f32(const void* data, si if (totalFrameCountOut) { *totalFrameCountOut = 0; } - if (!ma_dr_wav_init_memory(&wav, data, dataSize, pAllocationCallbacks)) { + if (!drwav_init_memory(&wav, data, dataSize, pAllocationCallbacks)) { return NULL; } - return ma_dr_wav__read_pcm_frames_and_close_f32(&wav, channelsOut, sampleRateOut, totalFrameCountOut); + return drwav__read_pcm_frames_and_close_f32(&wav, channelsOut, sampleRateOut, totalFrameCountOut); } -MA_API ma_int32* ma_dr_wav_open_memory_and_read_pcm_frames_s32(const void* data, size_t dataSize, unsigned int* channelsOut, unsigned int* sampleRateOut, ma_uint64* totalFrameCountOut, const ma_allocation_callbacks* pAllocationCallbacks) +DRWAV_API drwav_int32* drwav_open_memory_and_read_pcm_frames_s32(const void* data, size_t dataSize, unsigned int* channelsOut, unsigned int* sampleRateOut, drwav_uint64* totalFrameCountOut, const drwav_allocation_callbacks* pAllocationCallbacks) { - ma_dr_wav wav; + drwav wav; if (channelsOut) { *channelsOut = 0; } @@ -82828,66 +81625,66 @@ MA_API ma_int32* ma_dr_wav_open_memory_and_read_pcm_frames_s32(const void* data, if (totalFrameCountOut) { *totalFrameCountOut = 0; } - if (!ma_dr_wav_init_memory(&wav, data, dataSize, pAllocationCallbacks)) { + if (!drwav_init_memory(&wav, data, dataSize, pAllocationCallbacks)) { return NULL; } - return ma_dr_wav__read_pcm_frames_and_close_s32(&wav, channelsOut, sampleRateOut, totalFrameCountOut); + return drwav__read_pcm_frames_and_close_s32(&wav, channelsOut, sampleRateOut, totalFrameCountOut); } #endif -MA_API void ma_dr_wav_free(void* p, const ma_allocation_callbacks* pAllocationCallbacks) +DRWAV_API void drwav_free(void* p, const drwav_allocation_callbacks* pAllocationCallbacks) { if (pAllocationCallbacks != NULL) { - ma_dr_wav__free_from_callbacks(p, pAllocationCallbacks); + drwav__free_from_callbacks(p, pAllocationCallbacks); } else { - ma_dr_wav__free_default(p, NULL); + drwav__free_default(p, NULL); } } -MA_API ma_uint16 ma_dr_wav_bytes_to_u16(const ma_uint8* data) +DRWAV_API drwav_uint16 drwav_bytes_to_u16(const drwav_uint8* data) { - return ((ma_uint16)data[0] << 0) | ((ma_uint16)data[1] << 8); + return ((drwav_uint16)data[0] << 0) | ((drwav_uint16)data[1] << 8); } -MA_API ma_int16 ma_dr_wav_bytes_to_s16(const ma_uint8* data) +DRWAV_API drwav_int16 drwav_bytes_to_s16(const drwav_uint8* data) { - return (ma_int16)ma_dr_wav_bytes_to_u16(data); + return (drwav_int16)drwav_bytes_to_u16(data); } -MA_API ma_uint32 ma_dr_wav_bytes_to_u32(const ma_uint8* data) +DRWAV_API drwav_uint32 drwav_bytes_to_u32(const drwav_uint8* data) { - return ma_dr_wav_bytes_to_u32_le(data); + return ((drwav_uint32)data[0] << 0) | ((drwav_uint32)data[1] << 8) | ((drwav_uint32)data[2] << 16) | ((drwav_uint32)data[3] << 24); } -MA_API float ma_dr_wav_bytes_to_f32(const ma_uint8* data) +DRWAV_API float drwav_bytes_to_f32(const drwav_uint8* data) { union { - ma_uint32 u32; + drwav_uint32 u32; float f32; } value; - value.u32 = ma_dr_wav_bytes_to_u32(data); + value.u32 = drwav_bytes_to_u32(data); return value.f32; } -MA_API ma_int32 ma_dr_wav_bytes_to_s32(const ma_uint8* data) +DRWAV_API drwav_int32 drwav_bytes_to_s32(const drwav_uint8* data) { - return (ma_int32)ma_dr_wav_bytes_to_u32(data); + return (drwav_int32)drwav_bytes_to_u32(data); } -MA_API ma_uint64 ma_dr_wav_bytes_to_u64(const ma_uint8* data) +DRWAV_API drwav_uint64 drwav_bytes_to_u64(const drwav_uint8* data) { return - ((ma_uint64)data[0] << 0) | ((ma_uint64)data[1] << 8) | ((ma_uint64)data[2] << 16) | ((ma_uint64)data[3] << 24) | - ((ma_uint64)data[4] << 32) | ((ma_uint64)data[5] << 40) | ((ma_uint64)data[6] << 48) | ((ma_uint64)data[7] << 56); + ((drwav_uint64)data[0] << 0) | ((drwav_uint64)data[1] << 8) | ((drwav_uint64)data[2] << 16) | ((drwav_uint64)data[3] << 24) | + ((drwav_uint64)data[4] << 32) | ((drwav_uint64)data[5] << 40) | ((drwav_uint64)data[6] << 48) | ((drwav_uint64)data[7] << 56); } -MA_API ma_int64 ma_dr_wav_bytes_to_s64(const ma_uint8* data) +DRWAV_API drwav_int64 drwav_bytes_to_s64(const drwav_uint8* data) { - return (ma_int64)ma_dr_wav_bytes_to_u64(data); + return (drwav_int64)drwav_bytes_to_u64(data); } -MA_API ma_bool32 ma_dr_wav_guid_equal(const ma_uint8 a[16], const ma_uint8 b[16]) +DRWAV_API drwav_bool32 drwav_guid_equal(const drwav_uint8 a[16], const drwav_uint8 b[16]) { int i; for (i = 0; i < 16; i += 1) { if (a[i] != b[i]) { - return MA_FALSE; + return DRWAV_FALSE; } } - return MA_TRUE; + return DRWAV_TRUE; } -MA_API ma_bool32 ma_dr_wav_fourcc_equal(const ma_uint8* a, const char* b) +DRWAV_API drwav_bool32 drwav_fourcc_equal(const drwav_uint8* a, const char* b) { return a[0] == b[0] && @@ -82900,14 +81697,14 @@ MA_API ma_bool32 ma_dr_wav_fourcc_equal(const ma_uint8* a, const char* b) #endif #endif /* dr_wav_c end */ -#endif /* MA_DR_WAV_IMPLEMENTATION */ +#endif /* DRWAV_IMPLEMENTATION */ #endif /* MA_NO_WAV */ #if !defined(MA_NO_FLAC) && !defined(MA_NO_DECODING) -#if !defined(MA_DR_FLAC_IMPLEMENTATION) +#if !defined(DR_FLAC_IMPLEMENTATION) && !defined(DRFLAC_IMPLEMENTATION) /* For backwards compatibility. Will be removed in version 0.11 for cleanliness. */ /* dr_flac_c begin */ -#ifndef ma_dr_flac_c -#define ma_dr_flac_c +#ifndef dr_flac_c +#define dr_flac_c #if defined(__clang__) || (defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6))) #pragma GCC diagnostic push #if __GNUC__ >= 7 @@ -82928,60 +81725,85 @@ MA_API ma_bool32 ma_dr_wav_fourcc_equal(const ma_uint8* a, const char* b) #endif #include #include -#if !defined(MA_DR_FLAC_NO_SIMD) - #if defined(MA_X64) || defined(MA_X86) +#ifdef _MSC_VER + #define DRFLAC_INLINE __forceinline +#elif defined(__GNUC__) + #if defined(__STRICT_ANSI__) + #define DRFLAC_GNUC_INLINE_HINT __inline__ + #else + #define DRFLAC_GNUC_INLINE_HINT inline + #endif + #if (__GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 2)) || defined(__clang__) + #define DRFLAC_INLINE DRFLAC_GNUC_INLINE_HINT __attribute__((always_inline)) + #else + #define DRFLAC_INLINE DRFLAC_GNUC_INLINE_HINT + #endif +#elif defined(__WATCOMC__) + #define DRFLAC_INLINE __inline +#else + #define DRFLAC_INLINE +#endif +#if defined(__x86_64__) || defined(_M_X64) + #define DRFLAC_X64 +#elif defined(__i386) || defined(_M_IX86) + #define DRFLAC_X86 +#elif defined(__arm__) || defined(_M_ARM) || defined(__arm64) || defined(__arm64__) || defined(__aarch64__) || defined(_M_ARM64) + #define DRFLAC_ARM +#endif +#if !defined(DR_FLAC_NO_SIMD) + #if defined(DRFLAC_X64) || defined(DRFLAC_X86) #if defined(_MSC_VER) && !defined(__clang__) - #if _MSC_VER >= 1400 && !defined(MA_DR_FLAC_NO_SSE2) - #define MA_DR_FLAC_SUPPORT_SSE2 + #if _MSC_VER >= 1400 && !defined(DRFLAC_NO_SSE2) + #define DRFLAC_SUPPORT_SSE2 #endif - #if _MSC_VER >= 1600 && !defined(MA_DR_FLAC_NO_SSE41) - #define MA_DR_FLAC_SUPPORT_SSE41 + #if _MSC_VER >= 1600 && !defined(DRFLAC_NO_SSE41) + #define DRFLAC_SUPPORT_SSE41 #endif #elif defined(__clang__) || (defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 3))) - #if defined(__SSE2__) && !defined(MA_DR_FLAC_NO_SSE2) - #define MA_DR_FLAC_SUPPORT_SSE2 + #if defined(__SSE2__) && !defined(DRFLAC_NO_SSE2) + #define DRFLAC_SUPPORT_SSE2 #endif - #if defined(__SSE4_1__) && !defined(MA_DR_FLAC_NO_SSE41) - #define MA_DR_FLAC_SUPPORT_SSE41 + #if defined(__SSE4_1__) && !defined(DRFLAC_NO_SSE41) + #define DRFLAC_SUPPORT_SSE41 #endif #endif #if !defined(__GNUC__) && !defined(__clang__) && defined(__has_include) - #if !defined(MA_DR_FLAC_SUPPORT_SSE2) && !defined(MA_DR_FLAC_NO_SSE2) && __has_include() - #define MA_DR_FLAC_SUPPORT_SSE2 + #if !defined(DRFLAC_SUPPORT_SSE2) && !defined(DRFLAC_NO_SSE2) && __has_include() + #define DRFLAC_SUPPORT_SSE2 #endif - #if !defined(MA_DR_FLAC_SUPPORT_SSE41) && !defined(MA_DR_FLAC_NO_SSE41) && __has_include() - #define MA_DR_FLAC_SUPPORT_SSE41 + #if !defined(DRFLAC_SUPPORT_SSE41) && !defined(DRFLAC_NO_SSE41) && __has_include() + #define DRFLAC_SUPPORT_SSE41 #endif #endif - #if defined(MA_DR_FLAC_SUPPORT_SSE41) + #if defined(DRFLAC_SUPPORT_SSE41) #include - #elif defined(MA_DR_FLAC_SUPPORT_SSE2) + #elif defined(DRFLAC_SUPPORT_SSE2) #include #endif #endif - #if defined(MA_ARM) - #if !defined(MA_DR_FLAC_NO_NEON) && (defined(__ARM_NEON) || defined(__aarch64__) || defined(_M_ARM64)) - #define MA_DR_FLAC_SUPPORT_NEON + #if defined(DRFLAC_ARM) + #if !defined(DRFLAC_NO_NEON) && (defined(__ARM_NEON) || defined(__aarch64__) || defined(_M_ARM64)) + #define DRFLAC_SUPPORT_NEON #include #endif #endif #endif -#if !defined(MA_DR_FLAC_NO_SIMD) && (defined(MA_X86) || defined(MA_X64)) +#if !defined(DR_FLAC_NO_SIMD) && (defined(DRFLAC_X86) || defined(DRFLAC_X64)) #if defined(_MSC_VER) && !defined(__clang__) #if _MSC_VER >= 1400 #include - static void ma_dr_flac__cpuid(int info[4], int fid) + static void drflac__cpuid(int info[4], int fid) { __cpuid(info, fid); } #else - #define MA_DR_FLAC_NO_CPUID + #define DRFLAC_NO_CPUID #endif #else #if defined(__GNUC__) || defined(__clang__) - static void ma_dr_flac__cpuid(int info[4], int fid) + static void drflac__cpuid(int info[4], int fid) { - #if defined(MA_X86) && defined(__PIC__) + #if defined(DRFLAC_X86) && defined(__PIC__) __asm__ __volatile__ ( "xchg{l} {%%}ebx, %k1;" "cpuid;" @@ -82995,100 +81817,100 @@ MA_API ma_bool32 ma_dr_wav_fourcc_equal(const ma_uint8* a, const char* b) #endif } #else - #define MA_DR_FLAC_NO_CPUID + #define DRFLAC_NO_CPUID #endif #endif #else - #define MA_DR_FLAC_NO_CPUID + #define DRFLAC_NO_CPUID #endif -static MA_INLINE ma_bool32 ma_dr_flac_has_sse2(void) +static DRFLAC_INLINE drflac_bool32 drflac_has_sse2(void) { -#if defined(MA_DR_FLAC_SUPPORT_SSE2) - #if (defined(MA_X64) || defined(MA_X86)) && !defined(MA_DR_FLAC_NO_SSE2) - #if defined(MA_X64) - return MA_TRUE; +#if defined(DRFLAC_SUPPORT_SSE2) + #if (defined(DRFLAC_X64) || defined(DRFLAC_X86)) && !defined(DRFLAC_NO_SSE2) + #if defined(DRFLAC_X64) + return DRFLAC_TRUE; #elif (defined(_M_IX86_FP) && _M_IX86_FP == 2) || defined(__SSE2__) - return MA_TRUE; + return DRFLAC_TRUE; #else - #if defined(MA_DR_FLAC_NO_CPUID) - return MA_FALSE; + #if defined(DRFLAC_NO_CPUID) + return DRFLAC_FALSE; #else int info[4]; - ma_dr_flac__cpuid(info, 1); + drflac__cpuid(info, 1); return (info[3] & (1 << 26)) != 0; #endif #endif #else - return MA_FALSE; + return DRFLAC_FALSE; #endif #else - return MA_FALSE; + return DRFLAC_FALSE; #endif } -static MA_INLINE ma_bool32 ma_dr_flac_has_sse41(void) +static DRFLAC_INLINE drflac_bool32 drflac_has_sse41(void) { -#if defined(MA_DR_FLAC_SUPPORT_SSE41) - #if (defined(MA_X64) || defined(MA_X86)) && !defined(MA_DR_FLAC_NO_SSE41) +#if defined(DRFLAC_SUPPORT_SSE41) + #if (defined(DRFLAC_X64) || defined(DRFLAC_X86)) && !defined(DRFLAC_NO_SSE41) #if defined(__SSE4_1__) || defined(__AVX__) - return MA_TRUE; + return DRFLAC_TRUE; #else - #if defined(MA_DR_FLAC_NO_CPUID) - return MA_FALSE; + #if defined(DRFLAC_NO_CPUID) + return DRFLAC_FALSE; #else int info[4]; - ma_dr_flac__cpuid(info, 1); + drflac__cpuid(info, 1); return (info[2] & (1 << 19)) != 0; #endif #endif #else - return MA_FALSE; + return DRFLAC_FALSE; #endif #else - return MA_FALSE; + return DRFLAC_FALSE; #endif } -#if defined(_MSC_VER) && _MSC_VER >= 1500 && (defined(MA_X86) || defined(MA_X64)) && !defined(__clang__) - #define MA_DR_FLAC_HAS_LZCNT_INTRINSIC +#if defined(_MSC_VER) && _MSC_VER >= 1500 && (defined(DRFLAC_X86) || defined(DRFLAC_X64)) && !defined(__clang__) + #define DRFLAC_HAS_LZCNT_INTRINSIC #elif (defined(__GNUC__) && ((__GNUC__ > 4) || (__GNUC__ == 4 && __GNUC_MINOR__ >= 7))) - #define MA_DR_FLAC_HAS_LZCNT_INTRINSIC + #define DRFLAC_HAS_LZCNT_INTRINSIC #elif defined(__clang__) #if defined(__has_builtin) #if __has_builtin(__builtin_clzll) || __has_builtin(__builtin_clzl) - #define MA_DR_FLAC_HAS_LZCNT_INTRINSIC + #define DRFLAC_HAS_LZCNT_INTRINSIC #endif #endif #endif #if defined(_MSC_VER) && _MSC_VER >= 1400 && !defined(__clang__) - #define MA_DR_FLAC_HAS_BYTESWAP16_INTRINSIC - #define MA_DR_FLAC_HAS_BYTESWAP32_INTRINSIC - #define MA_DR_FLAC_HAS_BYTESWAP64_INTRINSIC + #define DRFLAC_HAS_BYTESWAP16_INTRINSIC + #define DRFLAC_HAS_BYTESWAP32_INTRINSIC + #define DRFLAC_HAS_BYTESWAP64_INTRINSIC #elif defined(__clang__) #if defined(__has_builtin) #if __has_builtin(__builtin_bswap16) - #define MA_DR_FLAC_HAS_BYTESWAP16_INTRINSIC + #define DRFLAC_HAS_BYTESWAP16_INTRINSIC #endif #if __has_builtin(__builtin_bswap32) - #define MA_DR_FLAC_HAS_BYTESWAP32_INTRINSIC + #define DRFLAC_HAS_BYTESWAP32_INTRINSIC #endif #if __has_builtin(__builtin_bswap64) - #define MA_DR_FLAC_HAS_BYTESWAP64_INTRINSIC + #define DRFLAC_HAS_BYTESWAP64_INTRINSIC #endif #endif #elif defined(__GNUC__) #if ((__GNUC__ > 4) || (__GNUC__ == 4 && __GNUC_MINOR__ >= 3)) - #define MA_DR_FLAC_HAS_BYTESWAP32_INTRINSIC - #define MA_DR_FLAC_HAS_BYTESWAP64_INTRINSIC + #define DRFLAC_HAS_BYTESWAP32_INTRINSIC + #define DRFLAC_HAS_BYTESWAP64_INTRINSIC #endif #if ((__GNUC__ > 4) || (__GNUC__ == 4 && __GNUC_MINOR__ >= 8)) - #define MA_DR_FLAC_HAS_BYTESWAP16_INTRINSIC + #define DRFLAC_HAS_BYTESWAP16_INTRINSIC #endif #elif defined(__WATCOMC__) && defined(__386__) - #define MA_DR_FLAC_HAS_BYTESWAP16_INTRINSIC - #define MA_DR_FLAC_HAS_BYTESWAP32_INTRINSIC - #define MA_DR_FLAC_HAS_BYTESWAP64_INTRINSIC - extern __inline ma_uint16 _watcom_bswap16(ma_uint16); - extern __inline ma_uint32 _watcom_bswap32(ma_uint32); - extern __inline ma_uint64 _watcom_bswap64(ma_uint64); + #define DRFLAC_HAS_BYTESWAP16_INTRINSIC + #define DRFLAC_HAS_BYTESWAP32_INTRINSIC + #define DRFLAC_HAS_BYTESWAP64_INTRINSIC + extern __inline drflac_uint16 _watcom_bswap16(drflac_uint16); + extern __inline drflac_uint32 _watcom_bswap32(drflac_uint32); + extern __inline drflac_uint64 _watcom_bswap64(drflac_uint64); #pragma aux _watcom_bswap16 = \ "xchg al, ah" \ parm [ax] \ @@ -83107,129 +81929,185 @@ static MA_INLINE ma_bool32 ma_dr_flac_has_sse41(void) value [eax edx] \ modify nomemory; #endif -#ifndef MA_DR_FLAC_ASSERT +#ifndef DRFLAC_ASSERT #include -#define MA_DR_FLAC_ASSERT(expression) assert(expression) -#endif -#ifndef MA_DR_FLAC_MALLOC -#define MA_DR_FLAC_MALLOC(sz) malloc((sz)) -#endif -#ifndef MA_DR_FLAC_REALLOC -#define MA_DR_FLAC_REALLOC(p, sz) realloc((p), (sz)) -#endif -#ifndef MA_DR_FLAC_FREE -#define MA_DR_FLAC_FREE(p) free((p)) -#endif -#ifndef MA_DR_FLAC_COPY_MEMORY -#define MA_DR_FLAC_COPY_MEMORY(dst, src, sz) memcpy((dst), (src), (sz)) -#endif -#ifndef MA_DR_FLAC_ZERO_MEMORY -#define MA_DR_FLAC_ZERO_MEMORY(p, sz) memset((p), 0, (sz)) -#endif -#ifndef MA_DR_FLAC_ZERO_OBJECT -#define MA_DR_FLAC_ZERO_OBJECT(p) MA_DR_FLAC_ZERO_MEMORY((p), sizeof(*(p))) -#endif -#define MA_DR_FLAC_MAX_SIMD_VECTOR_SIZE 64 -#define MA_DR_FLAC_SUBFRAME_CONSTANT 0 -#define MA_DR_FLAC_SUBFRAME_VERBATIM 1 -#define MA_DR_FLAC_SUBFRAME_FIXED 8 -#define MA_DR_FLAC_SUBFRAME_LPC 32 -#define MA_DR_FLAC_SUBFRAME_RESERVED 255 -#define MA_DR_FLAC_RESIDUAL_CODING_METHOD_PARTITIONED_RICE 0 -#define MA_DR_FLAC_RESIDUAL_CODING_METHOD_PARTITIONED_RICE2 1 -#define MA_DR_FLAC_CHANNEL_ASSIGNMENT_INDEPENDENT 0 -#define MA_DR_FLAC_CHANNEL_ASSIGNMENT_LEFT_SIDE 8 -#define MA_DR_FLAC_CHANNEL_ASSIGNMENT_RIGHT_SIDE 9 -#define MA_DR_FLAC_CHANNEL_ASSIGNMENT_MID_SIDE 10 -#define MA_DR_FLAC_SEEKPOINT_SIZE_IN_BYTES 18 -#define MA_DR_FLAC_CUESHEET_TRACK_SIZE_IN_BYTES 36 -#define MA_DR_FLAC_CUESHEET_TRACK_INDEX_SIZE_IN_BYTES 12 -#define ma_dr_flac_align(x, a) ((((x) + (a) - 1) / (a)) * (a)) -MA_API void ma_dr_flac_version(ma_uint32* pMajor, ma_uint32* pMinor, ma_uint32* pRevision) +#define DRFLAC_ASSERT(expression) assert(expression) +#endif +#ifndef DRFLAC_MALLOC +#define DRFLAC_MALLOC(sz) malloc((sz)) +#endif +#ifndef DRFLAC_REALLOC +#define DRFLAC_REALLOC(p, sz) realloc((p), (sz)) +#endif +#ifndef DRFLAC_FREE +#define DRFLAC_FREE(p) free((p)) +#endif +#ifndef DRFLAC_COPY_MEMORY +#define DRFLAC_COPY_MEMORY(dst, src, sz) memcpy((dst), (src), (sz)) +#endif +#ifndef DRFLAC_ZERO_MEMORY +#define DRFLAC_ZERO_MEMORY(p, sz) memset((p), 0, (sz)) +#endif +#ifndef DRFLAC_ZERO_OBJECT +#define DRFLAC_ZERO_OBJECT(p) DRFLAC_ZERO_MEMORY((p), sizeof(*(p))) +#endif +#define DRFLAC_MAX_SIMD_VECTOR_SIZE 64 +typedef drflac_int32 drflac_result; +#define DRFLAC_SUCCESS 0 +#define DRFLAC_ERROR -1 +#define DRFLAC_INVALID_ARGS -2 +#define DRFLAC_INVALID_OPERATION -3 +#define DRFLAC_OUT_OF_MEMORY -4 +#define DRFLAC_OUT_OF_RANGE -5 +#define DRFLAC_ACCESS_DENIED -6 +#define DRFLAC_DOES_NOT_EXIST -7 +#define DRFLAC_ALREADY_EXISTS -8 +#define DRFLAC_TOO_MANY_OPEN_FILES -9 +#define DRFLAC_INVALID_FILE -10 +#define DRFLAC_TOO_BIG -11 +#define DRFLAC_PATH_TOO_LONG -12 +#define DRFLAC_NAME_TOO_LONG -13 +#define DRFLAC_NOT_DIRECTORY -14 +#define DRFLAC_IS_DIRECTORY -15 +#define DRFLAC_DIRECTORY_NOT_EMPTY -16 +#define DRFLAC_END_OF_FILE -17 +#define DRFLAC_NO_SPACE -18 +#define DRFLAC_BUSY -19 +#define DRFLAC_IO_ERROR -20 +#define DRFLAC_INTERRUPT -21 +#define DRFLAC_UNAVAILABLE -22 +#define DRFLAC_ALREADY_IN_USE -23 +#define DRFLAC_BAD_ADDRESS -24 +#define DRFLAC_BAD_SEEK -25 +#define DRFLAC_BAD_PIPE -26 +#define DRFLAC_DEADLOCK -27 +#define DRFLAC_TOO_MANY_LINKS -28 +#define DRFLAC_NOT_IMPLEMENTED -29 +#define DRFLAC_NO_MESSAGE -30 +#define DRFLAC_BAD_MESSAGE -31 +#define DRFLAC_NO_DATA_AVAILABLE -32 +#define DRFLAC_INVALID_DATA -33 +#define DRFLAC_TIMEOUT -34 +#define DRFLAC_NO_NETWORK -35 +#define DRFLAC_NOT_UNIQUE -36 +#define DRFLAC_NOT_SOCKET -37 +#define DRFLAC_NO_ADDRESS -38 +#define DRFLAC_BAD_PROTOCOL -39 +#define DRFLAC_PROTOCOL_UNAVAILABLE -40 +#define DRFLAC_PROTOCOL_NOT_SUPPORTED -41 +#define DRFLAC_PROTOCOL_FAMILY_NOT_SUPPORTED -42 +#define DRFLAC_ADDRESS_FAMILY_NOT_SUPPORTED -43 +#define DRFLAC_SOCKET_NOT_SUPPORTED -44 +#define DRFLAC_CONNECTION_RESET -45 +#define DRFLAC_ALREADY_CONNECTED -46 +#define DRFLAC_NOT_CONNECTED -47 +#define DRFLAC_CONNECTION_REFUSED -48 +#define DRFLAC_NO_HOST -49 +#define DRFLAC_IN_PROGRESS -50 +#define DRFLAC_CANCELLED -51 +#define DRFLAC_MEMORY_ALREADY_MAPPED -52 +#define DRFLAC_AT_END -53 +#define DRFLAC_CRC_MISMATCH -128 +#define DRFLAC_SUBFRAME_CONSTANT 0 +#define DRFLAC_SUBFRAME_VERBATIM 1 +#define DRFLAC_SUBFRAME_FIXED 8 +#define DRFLAC_SUBFRAME_LPC 32 +#define DRFLAC_SUBFRAME_RESERVED 255 +#define DRFLAC_RESIDUAL_CODING_METHOD_PARTITIONED_RICE 0 +#define DRFLAC_RESIDUAL_CODING_METHOD_PARTITIONED_RICE2 1 +#define DRFLAC_CHANNEL_ASSIGNMENT_INDEPENDENT 0 +#define DRFLAC_CHANNEL_ASSIGNMENT_LEFT_SIDE 8 +#define DRFLAC_CHANNEL_ASSIGNMENT_RIGHT_SIDE 9 +#define DRFLAC_CHANNEL_ASSIGNMENT_MID_SIDE 10 +#define DRFLAC_SEEKPOINT_SIZE_IN_BYTES 18 +#define DRFLAC_CUESHEET_TRACK_SIZE_IN_BYTES 36 +#define DRFLAC_CUESHEET_TRACK_INDEX_SIZE_IN_BYTES 12 +#define drflac_align(x, a) ((((x) + (a) - 1) / (a)) * (a)) +DRFLAC_API void drflac_version(drflac_uint32* pMajor, drflac_uint32* pMinor, drflac_uint32* pRevision) { if (pMajor) { - *pMajor = MA_DR_FLAC_VERSION_MAJOR; + *pMajor = DRFLAC_VERSION_MAJOR; } if (pMinor) { - *pMinor = MA_DR_FLAC_VERSION_MINOR; + *pMinor = DRFLAC_VERSION_MINOR; } if (pRevision) { - *pRevision = MA_DR_FLAC_VERSION_REVISION; + *pRevision = DRFLAC_VERSION_REVISION; } } -MA_API const char* ma_dr_flac_version_string(void) +DRFLAC_API const char* drflac_version_string(void) { - return MA_DR_FLAC_VERSION_STRING; + return DRFLAC_VERSION_STRING; } #if defined(__has_feature) #if __has_feature(thread_sanitizer) - #define MA_DR_FLAC_NO_THREAD_SANITIZE __attribute__((no_sanitize("thread"))) + #define DRFLAC_NO_THREAD_SANITIZE __attribute__((no_sanitize("thread"))) #else - #define MA_DR_FLAC_NO_THREAD_SANITIZE + #define DRFLAC_NO_THREAD_SANITIZE #endif #else - #define MA_DR_FLAC_NO_THREAD_SANITIZE + #define DRFLAC_NO_THREAD_SANITIZE #endif -#if defined(MA_DR_FLAC_HAS_LZCNT_INTRINSIC) -static ma_bool32 ma_dr_flac__gIsLZCNTSupported = MA_FALSE; +#if defined(DRFLAC_HAS_LZCNT_INTRINSIC) +static drflac_bool32 drflac__gIsLZCNTSupported = DRFLAC_FALSE; #endif -#ifndef MA_DR_FLAC_NO_CPUID -static ma_bool32 ma_dr_flac__gIsSSE2Supported = MA_FALSE; -static ma_bool32 ma_dr_flac__gIsSSE41Supported = MA_FALSE; -MA_DR_FLAC_NO_THREAD_SANITIZE static void ma_dr_flac__init_cpu_caps(void) +#ifndef DRFLAC_NO_CPUID +static drflac_bool32 drflac__gIsSSE2Supported = DRFLAC_FALSE; +static drflac_bool32 drflac__gIsSSE41Supported = DRFLAC_FALSE; +DRFLAC_NO_THREAD_SANITIZE static void drflac__init_cpu_caps(void) { - static ma_bool32 isCPUCapsInitialized = MA_FALSE; + static drflac_bool32 isCPUCapsInitialized = DRFLAC_FALSE; if (!isCPUCapsInitialized) { -#if defined(MA_DR_FLAC_HAS_LZCNT_INTRINSIC) +#if defined(DRFLAC_HAS_LZCNT_INTRINSIC) int info[4] = {0}; - ma_dr_flac__cpuid(info, 0x80000001); - ma_dr_flac__gIsLZCNTSupported = (info[2] & (1 << 5)) != 0; + drflac__cpuid(info, 0x80000001); + drflac__gIsLZCNTSupported = (info[2] & (1 << 5)) != 0; #endif - ma_dr_flac__gIsSSE2Supported = ma_dr_flac_has_sse2(); - ma_dr_flac__gIsSSE41Supported = ma_dr_flac_has_sse41(); - isCPUCapsInitialized = MA_TRUE; + drflac__gIsSSE2Supported = drflac_has_sse2(); + drflac__gIsSSE41Supported = drflac_has_sse41(); + isCPUCapsInitialized = DRFLAC_TRUE; } } #else -static ma_bool32 ma_dr_flac__gIsNEONSupported = MA_FALSE; -static MA_INLINE ma_bool32 ma_dr_flac__has_neon(void) +static drflac_bool32 drflac__gIsNEONSupported = DRFLAC_FALSE; +static DRFLAC_INLINE drflac_bool32 drflac__has_neon(void) { -#if defined(MA_DR_FLAC_SUPPORT_NEON) - #if defined(MA_ARM) && !defined(MA_DR_FLAC_NO_NEON) +#if defined(DRFLAC_SUPPORT_NEON) + #if defined(DRFLAC_ARM) && !defined(DRFLAC_NO_NEON) #if (defined(__ARM_NEON) || defined(__aarch64__) || defined(_M_ARM64)) - return MA_TRUE; + return DRFLAC_TRUE; #else - return MA_FALSE; + return DRFLAC_FALSE; #endif #else - return MA_FALSE; + return DRFLAC_FALSE; #endif #else - return MA_FALSE; + return DRFLAC_FALSE; #endif } -MA_DR_FLAC_NO_THREAD_SANITIZE static void ma_dr_flac__init_cpu_caps(void) +DRFLAC_NO_THREAD_SANITIZE static void drflac__init_cpu_caps(void) { - ma_dr_flac__gIsNEONSupported = ma_dr_flac__has_neon(); -#if defined(MA_DR_FLAC_HAS_LZCNT_INTRINSIC) && defined(MA_ARM) && (defined(__ARM_ARCH) && __ARM_ARCH >= 5) - ma_dr_flac__gIsLZCNTSupported = MA_TRUE; + drflac__gIsNEONSupported = drflac__has_neon(); +#if defined(DRFLAC_HAS_LZCNT_INTRINSIC) && defined(DRFLAC_ARM) && (defined(__ARM_ARCH) && __ARM_ARCH >= 5) + drflac__gIsLZCNTSupported = DRFLAC_TRUE; #endif } #endif -static MA_INLINE ma_bool32 ma_dr_flac__is_little_endian(void) +static DRFLAC_INLINE drflac_bool32 drflac__is_little_endian(void) { -#if defined(MA_X86) || defined(MA_X64) - return MA_TRUE; +#if defined(DRFLAC_X86) || defined(DRFLAC_X64) + return DRFLAC_TRUE; #elif defined(__BYTE_ORDER) && defined(__LITTLE_ENDIAN) && __BYTE_ORDER == __LITTLE_ENDIAN - return MA_TRUE; + return DRFLAC_TRUE; #else int n = 1; return (*(char*)&n) == 1; #endif } -static MA_INLINE ma_uint16 ma_dr_flac__swap_endian_uint16(ma_uint16 n) +static DRFLAC_INLINE drflac_uint16 drflac__swap_endian_uint16(drflac_uint16 n) { -#ifdef MA_DR_FLAC_HAS_BYTESWAP16_INTRINSIC +#ifdef DRFLAC_HAS_BYTESWAP16_INTRINSIC #if defined(_MSC_VER) && !defined(__clang__) return _byteswap_ushort(n); #elif defined(__GNUC__) || defined(__clang__) @@ -83244,16 +82122,16 @@ static MA_INLINE ma_uint16 ma_dr_flac__swap_endian_uint16(ma_uint16 n) ((n & 0x00FF) << 8); #endif } -static MA_INLINE ma_uint32 ma_dr_flac__swap_endian_uint32(ma_uint32 n) +static DRFLAC_INLINE drflac_uint32 drflac__swap_endian_uint32(drflac_uint32 n) { -#ifdef MA_DR_FLAC_HAS_BYTESWAP32_INTRINSIC +#ifdef DRFLAC_HAS_BYTESWAP32_INTRINSIC #if defined(_MSC_VER) && !defined(__clang__) return _byteswap_ulong(n); #elif defined(__GNUC__) || defined(__clang__) - #if defined(MA_ARM) && (defined(__ARM_ARCH) && __ARM_ARCH >= 6) && !defined(__ARM_ARCH_6M__) && !defined(MA_64BIT) - ma_uint32 r; + #if defined(DRFLAC_ARM) && (defined(__ARM_ARCH) && __ARM_ARCH >= 6) && !defined(DRFLAC_64BIT) + drflac_uint32 r; __asm__ __volatile__ ( - #if defined(MA_64BIT) + #if defined(DRFLAC_64BIT) "rev %w[out], %w[in]" : [out]"=r"(r) : [in]"r"(n) #else "rev %[out], %[in]" : [out]"=r"(r) : [in]"r"(n) @@ -83275,9 +82153,9 @@ static MA_INLINE ma_uint32 ma_dr_flac__swap_endian_uint32(ma_uint32 n) ((n & 0x000000FF) << 24); #endif } -static MA_INLINE ma_uint64 ma_dr_flac__swap_endian_uint64(ma_uint64 n) +static DRFLAC_INLINE drflac_uint64 drflac__swap_endian_uint64(drflac_uint64 n) { -#ifdef MA_DR_FLAC_HAS_BYTESWAP64_INTRINSIC +#ifdef DRFLAC_HAS_BYTESWAP64_INTRINSIC #if defined(_MSC_VER) && !defined(__clang__) return _byteswap_uint64(n); #elif defined(__GNUC__) || defined(__clang__) @@ -83288,64 +82166,64 @@ static MA_INLINE ma_uint64 ma_dr_flac__swap_endian_uint64(ma_uint64 n) #error "This compiler does not support the byte swap intrinsic." #endif #else - return ((n & ((ma_uint64)0xFF000000 << 32)) >> 56) | - ((n & ((ma_uint64)0x00FF0000 << 32)) >> 40) | - ((n & ((ma_uint64)0x0000FF00 << 32)) >> 24) | - ((n & ((ma_uint64)0x000000FF << 32)) >> 8) | - ((n & ((ma_uint64)0xFF000000 )) << 8) | - ((n & ((ma_uint64)0x00FF0000 )) << 24) | - ((n & ((ma_uint64)0x0000FF00 )) << 40) | - ((n & ((ma_uint64)0x000000FF )) << 56); + return ((n & ((drflac_uint64)0xFF000000 << 32)) >> 56) | + ((n & ((drflac_uint64)0x00FF0000 << 32)) >> 40) | + ((n & ((drflac_uint64)0x0000FF00 << 32)) >> 24) | + ((n & ((drflac_uint64)0x000000FF << 32)) >> 8) | + ((n & ((drflac_uint64)0xFF000000 )) << 8) | + ((n & ((drflac_uint64)0x00FF0000 )) << 24) | + ((n & ((drflac_uint64)0x0000FF00 )) << 40) | + ((n & ((drflac_uint64)0x000000FF )) << 56); #endif } -static MA_INLINE ma_uint16 ma_dr_flac__be2host_16(ma_uint16 n) +static DRFLAC_INLINE drflac_uint16 drflac__be2host_16(drflac_uint16 n) { - if (ma_dr_flac__is_little_endian()) { - return ma_dr_flac__swap_endian_uint16(n); + if (drflac__is_little_endian()) { + return drflac__swap_endian_uint16(n); } return n; } -static MA_INLINE ma_uint32 ma_dr_flac__be2host_32(ma_uint32 n) +static DRFLAC_INLINE drflac_uint32 drflac__be2host_32(drflac_uint32 n) { - if (ma_dr_flac__is_little_endian()) { - return ma_dr_flac__swap_endian_uint32(n); + if (drflac__is_little_endian()) { + return drflac__swap_endian_uint32(n); } return n; } -static MA_INLINE ma_uint32 ma_dr_flac__be2host_32_ptr_unaligned(const void* pData) +static DRFLAC_INLINE drflac_uint32 drflac__be2host_32_ptr_unaligned(const void* pData) { - const ma_uint8* pNum = (ma_uint8*)pData; + const drflac_uint8* pNum = (drflac_uint8*)pData; return *(pNum) << 24 | *(pNum+1) << 16 | *(pNum+2) << 8 | *(pNum+3); } -static MA_INLINE ma_uint64 ma_dr_flac__be2host_64(ma_uint64 n) +static DRFLAC_INLINE drflac_uint64 drflac__be2host_64(drflac_uint64 n) { - if (ma_dr_flac__is_little_endian()) { - return ma_dr_flac__swap_endian_uint64(n); + if (drflac__is_little_endian()) { + return drflac__swap_endian_uint64(n); } return n; } -static MA_INLINE ma_uint32 ma_dr_flac__le2host_32(ma_uint32 n) +static DRFLAC_INLINE drflac_uint32 drflac__le2host_32(drflac_uint32 n) { - if (!ma_dr_flac__is_little_endian()) { - return ma_dr_flac__swap_endian_uint32(n); + if (!drflac__is_little_endian()) { + return drflac__swap_endian_uint32(n); } return n; } -static MA_INLINE ma_uint32 ma_dr_flac__le2host_32_ptr_unaligned(const void* pData) +static DRFLAC_INLINE drflac_uint32 drflac__le2host_32_ptr_unaligned(const void* pData) { - const ma_uint8* pNum = (ma_uint8*)pData; + const drflac_uint8* pNum = (drflac_uint8*)pData; return *pNum | *(pNum+1) << 8 | *(pNum+2) << 16 | *(pNum+3) << 24; } -static MA_INLINE ma_uint32 ma_dr_flac__unsynchsafe_32(ma_uint32 n) +static DRFLAC_INLINE drflac_uint32 drflac__unsynchsafe_32(drflac_uint32 n) { - ma_uint32 result = 0; + drflac_uint32 result = 0; result |= (n & 0x7F000000) >> 3; result |= (n & 0x007F0000) >> 2; result |= (n & 0x00007F00) >> 1; result |= (n & 0x0000007F) >> 0; return result; } -static ma_uint8 ma_dr_flac__crc8_table[] = { +static drflac_uint8 drflac__crc8_table[] = { 0x00, 0x07, 0x0E, 0x09, 0x1C, 0x1B, 0x12, 0x15, 0x38, 0x3F, 0x36, 0x31, 0x24, 0x23, 0x2A, 0x2D, 0x70, 0x77, 0x7E, 0x79, 0x6C, 0x6B, 0x62, 0x65, 0x48, 0x4F, 0x46, 0x41, 0x54, 0x53, 0x5A, 0x5D, 0xE0, 0xE7, 0xEE, 0xE9, 0xFC, 0xFB, 0xF2, 0xF5, 0xD8, 0xDF, 0xD6, 0xD1, 0xC4, 0xC3, 0xCA, 0xCD, @@ -83363,7 +82241,7 @@ static ma_uint8 ma_dr_flac__crc8_table[] = { 0xAE, 0xA9, 0xA0, 0xA7, 0xB2, 0xB5, 0xBC, 0xBB, 0x96, 0x91, 0x98, 0x9F, 0x8A, 0x8D, 0x84, 0x83, 0xDE, 0xD9, 0xD0, 0xD7, 0xC2, 0xC5, 0xCC, 0xCB, 0xE6, 0xE1, 0xE8, 0xEF, 0xFA, 0xFD, 0xF4, 0xF3 }; -static ma_uint16 ma_dr_flac__crc16_table[] = { +static drflac_uint16 drflac__crc16_table[] = { 0x0000, 0x8005, 0x800F, 0x000A, 0x801B, 0x001E, 0x0014, 0x8011, 0x8033, 0x0036, 0x003C, 0x8039, 0x0028, 0x802D, 0x8027, 0x0022, 0x8063, 0x0066, 0x006C, 0x8069, 0x0078, 0x807D, 0x8077, 0x0072, @@ -83397,22 +82275,22 @@ static ma_uint16 ma_dr_flac__crc16_table[] = { 0x0220, 0x8225, 0x822F, 0x022A, 0x823B, 0x023E, 0x0234, 0x8231, 0x8213, 0x0216, 0x021C, 0x8219, 0x0208, 0x820D, 0x8207, 0x0202 }; -static MA_INLINE ma_uint8 ma_dr_flac_crc8_byte(ma_uint8 crc, ma_uint8 data) +static DRFLAC_INLINE drflac_uint8 drflac_crc8_byte(drflac_uint8 crc, drflac_uint8 data) { - return ma_dr_flac__crc8_table[crc ^ data]; + return drflac__crc8_table[crc ^ data]; } -static MA_INLINE ma_uint8 ma_dr_flac_crc8(ma_uint8 crc, ma_uint32 data, ma_uint32 count) +static DRFLAC_INLINE drflac_uint8 drflac_crc8(drflac_uint8 crc, drflac_uint32 data, drflac_uint32 count) { -#ifdef MA_DR_FLAC_NO_CRC +#ifdef DR_FLAC_NO_CRC (void)crc; (void)data; (void)count; return 0; #else #if 0 - ma_uint8 p = 0x07; + drflac_uint8 p = 0x07; for (int i = count-1; i >= 0; --i) { - ma_uint8 bit = (data & (1 << i)) >> i; + drflac_uint8 bit = (data & (1 << i)) >> i; if (crc & 0x80) { crc = ((crc << 1) | bit) ^ p; } else { @@ -83421,75 +82299,75 @@ static MA_INLINE ma_uint8 ma_dr_flac_crc8(ma_uint8 crc, ma_uint32 data, ma_uint3 } return crc; #else - ma_uint32 wholeBytes; - ma_uint32 leftoverBits; - ma_uint64 leftoverDataMask; - static ma_uint64 leftoverDataMaskTable[8] = { + drflac_uint32 wholeBytes; + drflac_uint32 leftoverBits; + drflac_uint64 leftoverDataMask; + static drflac_uint64 leftoverDataMaskTable[8] = { 0x00, 0x01, 0x03, 0x07, 0x0F, 0x1F, 0x3F, 0x7F }; - MA_DR_FLAC_ASSERT(count <= 32); + DRFLAC_ASSERT(count <= 32); wholeBytes = count >> 3; leftoverBits = count - (wholeBytes*8); leftoverDataMask = leftoverDataMaskTable[leftoverBits]; switch (wholeBytes) { - case 4: crc = ma_dr_flac_crc8_byte(crc, (ma_uint8)((data & (0xFF000000UL << leftoverBits)) >> (24 + leftoverBits))); - case 3: crc = ma_dr_flac_crc8_byte(crc, (ma_uint8)((data & (0x00FF0000UL << leftoverBits)) >> (16 + leftoverBits))); - case 2: crc = ma_dr_flac_crc8_byte(crc, (ma_uint8)((data & (0x0000FF00UL << leftoverBits)) >> ( 8 + leftoverBits))); - case 1: crc = ma_dr_flac_crc8_byte(crc, (ma_uint8)((data & (0x000000FFUL << leftoverBits)) >> ( 0 + leftoverBits))); - case 0: if (leftoverBits > 0) crc = (ma_uint8)((crc << leftoverBits) ^ ma_dr_flac__crc8_table[(crc >> (8 - leftoverBits)) ^ (data & leftoverDataMask)]); + case 4: crc = drflac_crc8_byte(crc, (drflac_uint8)((data & (0xFF000000UL << leftoverBits)) >> (24 + leftoverBits))); + case 3: crc = drflac_crc8_byte(crc, (drflac_uint8)((data & (0x00FF0000UL << leftoverBits)) >> (16 + leftoverBits))); + case 2: crc = drflac_crc8_byte(crc, (drflac_uint8)((data & (0x0000FF00UL << leftoverBits)) >> ( 8 + leftoverBits))); + case 1: crc = drflac_crc8_byte(crc, (drflac_uint8)((data & (0x000000FFUL << leftoverBits)) >> ( 0 + leftoverBits))); + case 0: if (leftoverBits > 0) crc = (drflac_uint8)((crc << leftoverBits) ^ drflac__crc8_table[(crc >> (8 - leftoverBits)) ^ (data & leftoverDataMask)]); } return crc; #endif #endif } -static MA_INLINE ma_uint16 ma_dr_flac_crc16_byte(ma_uint16 crc, ma_uint8 data) +static DRFLAC_INLINE drflac_uint16 drflac_crc16_byte(drflac_uint16 crc, drflac_uint8 data) { - return (crc << 8) ^ ma_dr_flac__crc16_table[(ma_uint8)(crc >> 8) ^ data]; + return (crc << 8) ^ drflac__crc16_table[(drflac_uint8)(crc >> 8) ^ data]; } -static MA_INLINE ma_uint16 ma_dr_flac_crc16_cache(ma_uint16 crc, ma_dr_flac_cache_t data) +static DRFLAC_INLINE drflac_uint16 drflac_crc16_cache(drflac_uint16 crc, drflac_cache_t data) { -#ifdef MA_64BIT - crc = ma_dr_flac_crc16_byte(crc, (ma_uint8)((data >> 56) & 0xFF)); - crc = ma_dr_flac_crc16_byte(crc, (ma_uint8)((data >> 48) & 0xFF)); - crc = ma_dr_flac_crc16_byte(crc, (ma_uint8)((data >> 40) & 0xFF)); - crc = ma_dr_flac_crc16_byte(crc, (ma_uint8)((data >> 32) & 0xFF)); +#ifdef DRFLAC_64BIT + crc = drflac_crc16_byte(crc, (drflac_uint8)((data >> 56) & 0xFF)); + crc = drflac_crc16_byte(crc, (drflac_uint8)((data >> 48) & 0xFF)); + crc = drflac_crc16_byte(crc, (drflac_uint8)((data >> 40) & 0xFF)); + crc = drflac_crc16_byte(crc, (drflac_uint8)((data >> 32) & 0xFF)); #endif - crc = ma_dr_flac_crc16_byte(crc, (ma_uint8)((data >> 24) & 0xFF)); - crc = ma_dr_flac_crc16_byte(crc, (ma_uint8)((data >> 16) & 0xFF)); - crc = ma_dr_flac_crc16_byte(crc, (ma_uint8)((data >> 8) & 0xFF)); - crc = ma_dr_flac_crc16_byte(crc, (ma_uint8)((data >> 0) & 0xFF)); + crc = drflac_crc16_byte(crc, (drflac_uint8)((data >> 24) & 0xFF)); + crc = drflac_crc16_byte(crc, (drflac_uint8)((data >> 16) & 0xFF)); + crc = drflac_crc16_byte(crc, (drflac_uint8)((data >> 8) & 0xFF)); + crc = drflac_crc16_byte(crc, (drflac_uint8)((data >> 0) & 0xFF)); return crc; } -static MA_INLINE ma_uint16 ma_dr_flac_crc16_bytes(ma_uint16 crc, ma_dr_flac_cache_t data, ma_uint32 byteCount) +static DRFLAC_INLINE drflac_uint16 drflac_crc16_bytes(drflac_uint16 crc, drflac_cache_t data, drflac_uint32 byteCount) { switch (byteCount) { -#ifdef MA_64BIT - case 8: crc = ma_dr_flac_crc16_byte(crc, (ma_uint8)((data >> 56) & 0xFF)); - case 7: crc = ma_dr_flac_crc16_byte(crc, (ma_uint8)((data >> 48) & 0xFF)); - case 6: crc = ma_dr_flac_crc16_byte(crc, (ma_uint8)((data >> 40) & 0xFF)); - case 5: crc = ma_dr_flac_crc16_byte(crc, (ma_uint8)((data >> 32) & 0xFF)); +#ifdef DRFLAC_64BIT + case 8: crc = drflac_crc16_byte(crc, (drflac_uint8)((data >> 56) & 0xFF)); + case 7: crc = drflac_crc16_byte(crc, (drflac_uint8)((data >> 48) & 0xFF)); + case 6: crc = drflac_crc16_byte(crc, (drflac_uint8)((data >> 40) & 0xFF)); + case 5: crc = drflac_crc16_byte(crc, (drflac_uint8)((data >> 32) & 0xFF)); #endif - case 4: crc = ma_dr_flac_crc16_byte(crc, (ma_uint8)((data >> 24) & 0xFF)); - case 3: crc = ma_dr_flac_crc16_byte(crc, (ma_uint8)((data >> 16) & 0xFF)); - case 2: crc = ma_dr_flac_crc16_byte(crc, (ma_uint8)((data >> 8) & 0xFF)); - case 1: crc = ma_dr_flac_crc16_byte(crc, (ma_uint8)((data >> 0) & 0xFF)); + case 4: crc = drflac_crc16_byte(crc, (drflac_uint8)((data >> 24) & 0xFF)); + case 3: crc = drflac_crc16_byte(crc, (drflac_uint8)((data >> 16) & 0xFF)); + case 2: crc = drflac_crc16_byte(crc, (drflac_uint8)((data >> 8) & 0xFF)); + case 1: crc = drflac_crc16_byte(crc, (drflac_uint8)((data >> 0) & 0xFF)); } return crc; } #if 0 -static MA_INLINE ma_uint16 ma_dr_flac_crc16__32bit(ma_uint16 crc, ma_uint32 data, ma_uint32 count) +static DRFLAC_INLINE drflac_uint16 drflac_crc16__32bit(drflac_uint16 crc, drflac_uint32 data, drflac_uint32 count) { -#ifdef MA_DR_FLAC_NO_CRC +#ifdef DR_FLAC_NO_CRC (void)crc; (void)data; (void)count; return 0; #else #if 0 - ma_uint16 p = 0x8005; + drflac_uint16 p = 0x8005; for (int i = count-1; i >= 0; --i) { - ma_uint16 bit = (data & (1ULL << i)) >> i; + drflac_uint16 bit = (data & (1ULL << i)) >> i; if (r & 0x8000) { r = ((r << 1) | bit) ^ p; } else { @@ -83498,433 +82376,433 @@ static MA_INLINE ma_uint16 ma_dr_flac_crc16__32bit(ma_uint16 crc, ma_uint32 data } return crc; #else - ma_uint32 wholeBytes; - ma_uint32 leftoverBits; - ma_uint64 leftoverDataMask; - static ma_uint64 leftoverDataMaskTable[8] = { + drflac_uint32 wholeBytes; + drflac_uint32 leftoverBits; + drflac_uint64 leftoverDataMask; + static drflac_uint64 leftoverDataMaskTable[8] = { 0x00, 0x01, 0x03, 0x07, 0x0F, 0x1F, 0x3F, 0x7F }; - MA_DR_FLAC_ASSERT(count <= 64); + DRFLAC_ASSERT(count <= 64); wholeBytes = count >> 3; leftoverBits = count & 7; leftoverDataMask = leftoverDataMaskTable[leftoverBits]; switch (wholeBytes) { default: - case 4: crc = ma_dr_flac_crc16_byte(crc, (ma_uint8)((data & (0xFF000000UL << leftoverBits)) >> (24 + leftoverBits))); - case 3: crc = ma_dr_flac_crc16_byte(crc, (ma_uint8)((data & (0x00FF0000UL << leftoverBits)) >> (16 + leftoverBits))); - case 2: crc = ma_dr_flac_crc16_byte(crc, (ma_uint8)((data & (0x0000FF00UL << leftoverBits)) >> ( 8 + leftoverBits))); - case 1: crc = ma_dr_flac_crc16_byte(crc, (ma_uint8)((data & (0x000000FFUL << leftoverBits)) >> ( 0 + leftoverBits))); - case 0: if (leftoverBits > 0) crc = (crc << leftoverBits) ^ ma_dr_flac__crc16_table[(crc >> (16 - leftoverBits)) ^ (data & leftoverDataMask)]; + case 4: crc = drflac_crc16_byte(crc, (drflac_uint8)((data & (0xFF000000UL << leftoverBits)) >> (24 + leftoverBits))); + case 3: crc = drflac_crc16_byte(crc, (drflac_uint8)((data & (0x00FF0000UL << leftoverBits)) >> (16 + leftoverBits))); + case 2: crc = drflac_crc16_byte(crc, (drflac_uint8)((data & (0x0000FF00UL << leftoverBits)) >> ( 8 + leftoverBits))); + case 1: crc = drflac_crc16_byte(crc, (drflac_uint8)((data & (0x000000FFUL << leftoverBits)) >> ( 0 + leftoverBits))); + case 0: if (leftoverBits > 0) crc = (crc << leftoverBits) ^ drflac__crc16_table[(crc >> (16 - leftoverBits)) ^ (data & leftoverDataMask)]; } return crc; #endif #endif } -static MA_INLINE ma_uint16 ma_dr_flac_crc16__64bit(ma_uint16 crc, ma_uint64 data, ma_uint32 count) +static DRFLAC_INLINE drflac_uint16 drflac_crc16__64bit(drflac_uint16 crc, drflac_uint64 data, drflac_uint32 count) { -#ifdef MA_DR_FLAC_NO_CRC +#ifdef DR_FLAC_NO_CRC (void)crc; (void)data; (void)count; return 0; #else - ma_uint32 wholeBytes; - ma_uint32 leftoverBits; - ma_uint64 leftoverDataMask; - static ma_uint64 leftoverDataMaskTable[8] = { + drflac_uint32 wholeBytes; + drflac_uint32 leftoverBits; + drflac_uint64 leftoverDataMask; + static drflac_uint64 leftoverDataMaskTable[8] = { 0x00, 0x01, 0x03, 0x07, 0x0F, 0x1F, 0x3F, 0x7F }; - MA_DR_FLAC_ASSERT(count <= 64); + DRFLAC_ASSERT(count <= 64); wholeBytes = count >> 3; leftoverBits = count & 7; leftoverDataMask = leftoverDataMaskTable[leftoverBits]; switch (wholeBytes) { default: - case 8: crc = ma_dr_flac_crc16_byte(crc, (ma_uint8)((data & (((ma_uint64)0xFF000000 << 32) << leftoverBits)) >> (56 + leftoverBits))); - case 7: crc = ma_dr_flac_crc16_byte(crc, (ma_uint8)((data & (((ma_uint64)0x00FF0000 << 32) << leftoverBits)) >> (48 + leftoverBits))); - case 6: crc = ma_dr_flac_crc16_byte(crc, (ma_uint8)((data & (((ma_uint64)0x0000FF00 << 32) << leftoverBits)) >> (40 + leftoverBits))); - case 5: crc = ma_dr_flac_crc16_byte(crc, (ma_uint8)((data & (((ma_uint64)0x000000FF << 32) << leftoverBits)) >> (32 + leftoverBits))); - case 4: crc = ma_dr_flac_crc16_byte(crc, (ma_uint8)((data & (((ma_uint64)0xFF000000 ) << leftoverBits)) >> (24 + leftoverBits))); - case 3: crc = ma_dr_flac_crc16_byte(crc, (ma_uint8)((data & (((ma_uint64)0x00FF0000 ) << leftoverBits)) >> (16 + leftoverBits))); - case 2: crc = ma_dr_flac_crc16_byte(crc, (ma_uint8)((data & (((ma_uint64)0x0000FF00 ) << leftoverBits)) >> ( 8 + leftoverBits))); - case 1: crc = ma_dr_flac_crc16_byte(crc, (ma_uint8)((data & (((ma_uint64)0x000000FF ) << leftoverBits)) >> ( 0 + leftoverBits))); - case 0: if (leftoverBits > 0) crc = (crc << leftoverBits) ^ ma_dr_flac__crc16_table[(crc >> (16 - leftoverBits)) ^ (data & leftoverDataMask)]; + case 8: crc = drflac_crc16_byte(crc, (drflac_uint8)((data & (((drflac_uint64)0xFF000000 << 32) << leftoverBits)) >> (56 + leftoverBits))); + case 7: crc = drflac_crc16_byte(crc, (drflac_uint8)((data & (((drflac_uint64)0x00FF0000 << 32) << leftoverBits)) >> (48 + leftoverBits))); + case 6: crc = drflac_crc16_byte(crc, (drflac_uint8)((data & (((drflac_uint64)0x0000FF00 << 32) << leftoverBits)) >> (40 + leftoverBits))); + case 5: crc = drflac_crc16_byte(crc, (drflac_uint8)((data & (((drflac_uint64)0x000000FF << 32) << leftoverBits)) >> (32 + leftoverBits))); + case 4: crc = drflac_crc16_byte(crc, (drflac_uint8)((data & (((drflac_uint64)0xFF000000 ) << leftoverBits)) >> (24 + leftoverBits))); + case 3: crc = drflac_crc16_byte(crc, (drflac_uint8)((data & (((drflac_uint64)0x00FF0000 ) << leftoverBits)) >> (16 + leftoverBits))); + case 2: crc = drflac_crc16_byte(crc, (drflac_uint8)((data & (((drflac_uint64)0x0000FF00 ) << leftoverBits)) >> ( 8 + leftoverBits))); + case 1: crc = drflac_crc16_byte(crc, (drflac_uint8)((data & (((drflac_uint64)0x000000FF ) << leftoverBits)) >> ( 0 + leftoverBits))); + case 0: if (leftoverBits > 0) crc = (crc << leftoverBits) ^ drflac__crc16_table[(crc >> (16 - leftoverBits)) ^ (data & leftoverDataMask)]; } return crc; #endif } -static MA_INLINE ma_uint16 ma_dr_flac_crc16(ma_uint16 crc, ma_dr_flac_cache_t data, ma_uint32 count) +static DRFLAC_INLINE drflac_uint16 drflac_crc16(drflac_uint16 crc, drflac_cache_t data, drflac_uint32 count) { -#ifdef MA_64BIT - return ma_dr_flac_crc16__64bit(crc, data, count); +#ifdef DRFLAC_64BIT + return drflac_crc16__64bit(crc, data, count); #else - return ma_dr_flac_crc16__32bit(crc, data, count); + return drflac_crc16__32bit(crc, data, count); #endif } #endif -#ifdef MA_64BIT -#define ma_dr_flac__be2host__cache_line ma_dr_flac__be2host_64 +#ifdef DRFLAC_64BIT +#define drflac__be2host__cache_line drflac__be2host_64 #else -#define ma_dr_flac__be2host__cache_line ma_dr_flac__be2host_32 -#endif -#define MA_DR_FLAC_CACHE_L1_SIZE_BYTES(bs) (sizeof((bs)->cache)) -#define MA_DR_FLAC_CACHE_L1_SIZE_BITS(bs) (sizeof((bs)->cache)*8) -#define MA_DR_FLAC_CACHE_L1_BITS_REMAINING(bs) (MA_DR_FLAC_CACHE_L1_SIZE_BITS(bs) - (bs)->consumedBits) -#define MA_DR_FLAC_CACHE_L1_SELECTION_MASK(_bitCount) (~((~(ma_dr_flac_cache_t)0) >> (_bitCount))) -#define MA_DR_FLAC_CACHE_L1_SELECTION_SHIFT(bs, _bitCount) (MA_DR_FLAC_CACHE_L1_SIZE_BITS(bs) - (_bitCount)) -#define MA_DR_FLAC_CACHE_L1_SELECT(bs, _bitCount) (((bs)->cache) & MA_DR_FLAC_CACHE_L1_SELECTION_MASK(_bitCount)) -#define MA_DR_FLAC_CACHE_L1_SELECT_AND_SHIFT(bs, _bitCount) (MA_DR_FLAC_CACHE_L1_SELECT((bs), (_bitCount)) >> MA_DR_FLAC_CACHE_L1_SELECTION_SHIFT((bs), (_bitCount))) -#define MA_DR_FLAC_CACHE_L1_SELECT_AND_SHIFT_SAFE(bs, _bitCount)(MA_DR_FLAC_CACHE_L1_SELECT((bs), (_bitCount)) >> (MA_DR_FLAC_CACHE_L1_SELECTION_SHIFT((bs), (_bitCount)) & (MA_DR_FLAC_CACHE_L1_SIZE_BITS(bs)-1))) -#define MA_DR_FLAC_CACHE_L2_SIZE_BYTES(bs) (sizeof((bs)->cacheL2)) -#define MA_DR_FLAC_CACHE_L2_LINE_COUNT(bs) (MA_DR_FLAC_CACHE_L2_SIZE_BYTES(bs) / sizeof((bs)->cacheL2[0])) -#define MA_DR_FLAC_CACHE_L2_LINES_REMAINING(bs) (MA_DR_FLAC_CACHE_L2_LINE_COUNT(bs) - (bs)->nextL2Line) -#ifndef MA_DR_FLAC_NO_CRC -static MA_INLINE void ma_dr_flac__reset_crc16(ma_dr_flac_bs* bs) +#define drflac__be2host__cache_line drflac__be2host_32 +#endif +#define DRFLAC_CACHE_L1_SIZE_BYTES(bs) (sizeof((bs)->cache)) +#define DRFLAC_CACHE_L1_SIZE_BITS(bs) (sizeof((bs)->cache)*8) +#define DRFLAC_CACHE_L1_BITS_REMAINING(bs) (DRFLAC_CACHE_L1_SIZE_BITS(bs) - (bs)->consumedBits) +#define DRFLAC_CACHE_L1_SELECTION_MASK(_bitCount) (~((~(drflac_cache_t)0) >> (_bitCount))) +#define DRFLAC_CACHE_L1_SELECTION_SHIFT(bs, _bitCount) (DRFLAC_CACHE_L1_SIZE_BITS(bs) - (_bitCount)) +#define DRFLAC_CACHE_L1_SELECT(bs, _bitCount) (((bs)->cache) & DRFLAC_CACHE_L1_SELECTION_MASK(_bitCount)) +#define DRFLAC_CACHE_L1_SELECT_AND_SHIFT(bs, _bitCount) (DRFLAC_CACHE_L1_SELECT((bs), (_bitCount)) >> DRFLAC_CACHE_L1_SELECTION_SHIFT((bs), (_bitCount))) +#define DRFLAC_CACHE_L1_SELECT_AND_SHIFT_SAFE(bs, _bitCount)(DRFLAC_CACHE_L1_SELECT((bs), (_bitCount)) >> (DRFLAC_CACHE_L1_SELECTION_SHIFT((bs), (_bitCount)) & (DRFLAC_CACHE_L1_SIZE_BITS(bs)-1))) +#define DRFLAC_CACHE_L2_SIZE_BYTES(bs) (sizeof((bs)->cacheL2)) +#define DRFLAC_CACHE_L2_LINE_COUNT(bs) (DRFLAC_CACHE_L2_SIZE_BYTES(bs) / sizeof((bs)->cacheL2[0])) +#define DRFLAC_CACHE_L2_LINES_REMAINING(bs) (DRFLAC_CACHE_L2_LINE_COUNT(bs) - (bs)->nextL2Line) +#ifndef DR_FLAC_NO_CRC +static DRFLAC_INLINE void drflac__reset_crc16(drflac_bs* bs) { bs->crc16 = 0; bs->crc16CacheIgnoredBytes = bs->consumedBits >> 3; } -static MA_INLINE void ma_dr_flac__update_crc16(ma_dr_flac_bs* bs) +static DRFLAC_INLINE void drflac__update_crc16(drflac_bs* bs) { if (bs->crc16CacheIgnoredBytes == 0) { - bs->crc16 = ma_dr_flac_crc16_cache(bs->crc16, bs->crc16Cache); + bs->crc16 = drflac_crc16_cache(bs->crc16, bs->crc16Cache); } else { - bs->crc16 = ma_dr_flac_crc16_bytes(bs->crc16, bs->crc16Cache, MA_DR_FLAC_CACHE_L1_SIZE_BYTES(bs) - bs->crc16CacheIgnoredBytes); + bs->crc16 = drflac_crc16_bytes(bs->crc16, bs->crc16Cache, DRFLAC_CACHE_L1_SIZE_BYTES(bs) - bs->crc16CacheIgnoredBytes); bs->crc16CacheIgnoredBytes = 0; } } -static MA_INLINE ma_uint16 ma_dr_flac__flush_crc16(ma_dr_flac_bs* bs) +static DRFLAC_INLINE drflac_uint16 drflac__flush_crc16(drflac_bs* bs) { - MA_DR_FLAC_ASSERT((MA_DR_FLAC_CACHE_L1_BITS_REMAINING(bs) & 7) == 0); - if (MA_DR_FLAC_CACHE_L1_BITS_REMAINING(bs) == 0) { - ma_dr_flac__update_crc16(bs); + DRFLAC_ASSERT((DRFLAC_CACHE_L1_BITS_REMAINING(bs) & 7) == 0); + if (DRFLAC_CACHE_L1_BITS_REMAINING(bs) == 0) { + drflac__update_crc16(bs); } else { - bs->crc16 = ma_dr_flac_crc16_bytes(bs->crc16, bs->crc16Cache >> MA_DR_FLAC_CACHE_L1_BITS_REMAINING(bs), (bs->consumedBits >> 3) - bs->crc16CacheIgnoredBytes); + bs->crc16 = drflac_crc16_bytes(bs->crc16, bs->crc16Cache >> DRFLAC_CACHE_L1_BITS_REMAINING(bs), (bs->consumedBits >> 3) - bs->crc16CacheIgnoredBytes); bs->crc16CacheIgnoredBytes = bs->consumedBits >> 3; } return bs->crc16; } #endif -static MA_INLINE ma_bool32 ma_dr_flac__reload_l1_cache_from_l2(ma_dr_flac_bs* bs) +static DRFLAC_INLINE drflac_bool32 drflac__reload_l1_cache_from_l2(drflac_bs* bs) { size_t bytesRead; size_t alignedL1LineCount; - if (bs->nextL2Line < MA_DR_FLAC_CACHE_L2_LINE_COUNT(bs)) { + if (bs->nextL2Line < DRFLAC_CACHE_L2_LINE_COUNT(bs)) { bs->cache = bs->cacheL2[bs->nextL2Line++]; - return MA_TRUE; + return DRFLAC_TRUE; } if (bs->unalignedByteCount > 0) { - return MA_FALSE; + return DRFLAC_FALSE; } - bytesRead = bs->onRead(bs->pUserData, bs->cacheL2, MA_DR_FLAC_CACHE_L2_SIZE_BYTES(bs)); + bytesRead = bs->onRead(bs->pUserData, bs->cacheL2, DRFLAC_CACHE_L2_SIZE_BYTES(bs)); bs->nextL2Line = 0; - if (bytesRead == MA_DR_FLAC_CACHE_L2_SIZE_BYTES(bs)) { + if (bytesRead == DRFLAC_CACHE_L2_SIZE_BYTES(bs)) { bs->cache = bs->cacheL2[bs->nextL2Line++]; - return MA_TRUE; + return DRFLAC_TRUE; } - alignedL1LineCount = bytesRead / MA_DR_FLAC_CACHE_L1_SIZE_BYTES(bs); - bs->unalignedByteCount = bytesRead - (alignedL1LineCount * MA_DR_FLAC_CACHE_L1_SIZE_BYTES(bs)); + alignedL1LineCount = bytesRead / DRFLAC_CACHE_L1_SIZE_BYTES(bs); + bs->unalignedByteCount = bytesRead - (alignedL1LineCount * DRFLAC_CACHE_L1_SIZE_BYTES(bs)); if (bs->unalignedByteCount > 0) { bs->unalignedCache = bs->cacheL2[alignedL1LineCount]; } if (alignedL1LineCount > 0) { - size_t offset = MA_DR_FLAC_CACHE_L2_LINE_COUNT(bs) - alignedL1LineCount; + size_t offset = DRFLAC_CACHE_L2_LINE_COUNT(bs) - alignedL1LineCount; size_t i; for (i = alignedL1LineCount; i > 0; --i) { bs->cacheL2[i-1 + offset] = bs->cacheL2[i-1]; } - bs->nextL2Line = (ma_uint32)offset; + bs->nextL2Line = (drflac_uint32)offset; bs->cache = bs->cacheL2[bs->nextL2Line++]; - return MA_TRUE; + return DRFLAC_TRUE; } else { - bs->nextL2Line = MA_DR_FLAC_CACHE_L2_LINE_COUNT(bs); - return MA_FALSE; + bs->nextL2Line = DRFLAC_CACHE_L2_LINE_COUNT(bs); + return DRFLAC_FALSE; } } -static ma_bool32 ma_dr_flac__reload_cache(ma_dr_flac_bs* bs) +static drflac_bool32 drflac__reload_cache(drflac_bs* bs) { size_t bytesRead; -#ifndef MA_DR_FLAC_NO_CRC - ma_dr_flac__update_crc16(bs); +#ifndef DR_FLAC_NO_CRC + drflac__update_crc16(bs); #endif - if (ma_dr_flac__reload_l1_cache_from_l2(bs)) { - bs->cache = ma_dr_flac__be2host__cache_line(bs->cache); + if (drflac__reload_l1_cache_from_l2(bs)) { + bs->cache = drflac__be2host__cache_line(bs->cache); bs->consumedBits = 0; -#ifndef MA_DR_FLAC_NO_CRC +#ifndef DR_FLAC_NO_CRC bs->crc16Cache = bs->cache; #endif - return MA_TRUE; + return DRFLAC_TRUE; } bytesRead = bs->unalignedByteCount; if (bytesRead == 0) { - bs->consumedBits = MA_DR_FLAC_CACHE_L1_SIZE_BITS(bs); - return MA_FALSE; + bs->consumedBits = DRFLAC_CACHE_L1_SIZE_BITS(bs); + return DRFLAC_FALSE; } - MA_DR_FLAC_ASSERT(bytesRead < MA_DR_FLAC_CACHE_L1_SIZE_BYTES(bs)); - bs->consumedBits = (ma_uint32)(MA_DR_FLAC_CACHE_L1_SIZE_BYTES(bs) - bytesRead) * 8; - bs->cache = ma_dr_flac__be2host__cache_line(bs->unalignedCache); - bs->cache &= MA_DR_FLAC_CACHE_L1_SELECTION_MASK(MA_DR_FLAC_CACHE_L1_BITS_REMAINING(bs)); + DRFLAC_ASSERT(bytesRead < DRFLAC_CACHE_L1_SIZE_BYTES(bs)); + bs->consumedBits = (drflac_uint32)(DRFLAC_CACHE_L1_SIZE_BYTES(bs) - bytesRead) * 8; + bs->cache = drflac__be2host__cache_line(bs->unalignedCache); + bs->cache &= DRFLAC_CACHE_L1_SELECTION_MASK(DRFLAC_CACHE_L1_BITS_REMAINING(bs)); bs->unalignedByteCount = 0; -#ifndef MA_DR_FLAC_NO_CRC +#ifndef DR_FLAC_NO_CRC bs->crc16Cache = bs->cache >> bs->consumedBits; bs->crc16CacheIgnoredBytes = bs->consumedBits >> 3; #endif - return MA_TRUE; + return DRFLAC_TRUE; } -static void ma_dr_flac__reset_cache(ma_dr_flac_bs* bs) +static void drflac__reset_cache(drflac_bs* bs) { - bs->nextL2Line = MA_DR_FLAC_CACHE_L2_LINE_COUNT(bs); - bs->consumedBits = MA_DR_FLAC_CACHE_L1_SIZE_BITS(bs); + bs->nextL2Line = DRFLAC_CACHE_L2_LINE_COUNT(bs); + bs->consumedBits = DRFLAC_CACHE_L1_SIZE_BITS(bs); bs->cache = 0; bs->unalignedByteCount = 0; bs->unalignedCache = 0; -#ifndef MA_DR_FLAC_NO_CRC +#ifndef DR_FLAC_NO_CRC bs->crc16Cache = 0; bs->crc16CacheIgnoredBytes = 0; #endif } -static MA_INLINE ma_bool32 ma_dr_flac__read_uint32(ma_dr_flac_bs* bs, unsigned int bitCount, ma_uint32* pResultOut) +static DRFLAC_INLINE drflac_bool32 drflac__read_uint32(drflac_bs* bs, unsigned int bitCount, drflac_uint32* pResultOut) { - MA_DR_FLAC_ASSERT(bs != NULL); - MA_DR_FLAC_ASSERT(pResultOut != NULL); - MA_DR_FLAC_ASSERT(bitCount > 0); - MA_DR_FLAC_ASSERT(bitCount <= 32); - if (bs->consumedBits == MA_DR_FLAC_CACHE_L1_SIZE_BITS(bs)) { - if (!ma_dr_flac__reload_cache(bs)) { - return MA_FALSE; + DRFLAC_ASSERT(bs != NULL); + DRFLAC_ASSERT(pResultOut != NULL); + DRFLAC_ASSERT(bitCount > 0); + DRFLAC_ASSERT(bitCount <= 32); + if (bs->consumedBits == DRFLAC_CACHE_L1_SIZE_BITS(bs)) { + if (!drflac__reload_cache(bs)) { + return DRFLAC_FALSE; } } - if (bitCount <= MA_DR_FLAC_CACHE_L1_BITS_REMAINING(bs)) { -#ifdef MA_64BIT - *pResultOut = (ma_uint32)MA_DR_FLAC_CACHE_L1_SELECT_AND_SHIFT(bs, bitCount); + if (bitCount <= DRFLAC_CACHE_L1_BITS_REMAINING(bs)) { +#ifdef DRFLAC_64BIT + *pResultOut = (drflac_uint32)DRFLAC_CACHE_L1_SELECT_AND_SHIFT(bs, bitCount); bs->consumedBits += bitCount; bs->cache <<= bitCount; #else - if (bitCount < MA_DR_FLAC_CACHE_L1_SIZE_BITS(bs)) { - *pResultOut = (ma_uint32)MA_DR_FLAC_CACHE_L1_SELECT_AND_SHIFT(bs, bitCount); + if (bitCount < DRFLAC_CACHE_L1_SIZE_BITS(bs)) { + *pResultOut = (drflac_uint32)DRFLAC_CACHE_L1_SELECT_AND_SHIFT(bs, bitCount); bs->consumedBits += bitCount; bs->cache <<= bitCount; } else { - *pResultOut = (ma_uint32)bs->cache; - bs->consumedBits = MA_DR_FLAC_CACHE_L1_SIZE_BITS(bs); + *pResultOut = (drflac_uint32)bs->cache; + bs->consumedBits = DRFLAC_CACHE_L1_SIZE_BITS(bs); bs->cache = 0; } #endif - return MA_TRUE; + return DRFLAC_TRUE; } else { - ma_uint32 bitCountHi = MA_DR_FLAC_CACHE_L1_BITS_REMAINING(bs); - ma_uint32 bitCountLo = bitCount - bitCountHi; - ma_uint32 resultHi; - MA_DR_FLAC_ASSERT(bitCountHi > 0); - MA_DR_FLAC_ASSERT(bitCountHi < 32); - resultHi = (ma_uint32)MA_DR_FLAC_CACHE_L1_SELECT_AND_SHIFT(bs, bitCountHi); - if (!ma_dr_flac__reload_cache(bs)) { - return MA_FALSE; - } - if (bitCountLo > MA_DR_FLAC_CACHE_L1_BITS_REMAINING(bs)) { - return MA_FALSE; - } - *pResultOut = (resultHi << bitCountLo) | (ma_uint32)MA_DR_FLAC_CACHE_L1_SELECT_AND_SHIFT(bs, bitCountLo); + drflac_uint32 bitCountHi = DRFLAC_CACHE_L1_BITS_REMAINING(bs); + drflac_uint32 bitCountLo = bitCount - bitCountHi; + drflac_uint32 resultHi; + DRFLAC_ASSERT(bitCountHi > 0); + DRFLAC_ASSERT(bitCountHi < 32); + resultHi = (drflac_uint32)DRFLAC_CACHE_L1_SELECT_AND_SHIFT(bs, bitCountHi); + if (!drflac__reload_cache(bs)) { + return DRFLAC_FALSE; + } + if (bitCountLo > DRFLAC_CACHE_L1_BITS_REMAINING(bs)) { + return DRFLAC_FALSE; + } + *pResultOut = (resultHi << bitCountLo) | (drflac_uint32)DRFLAC_CACHE_L1_SELECT_AND_SHIFT(bs, bitCountLo); bs->consumedBits += bitCountLo; bs->cache <<= bitCountLo; - return MA_TRUE; + return DRFLAC_TRUE; } } -static ma_bool32 ma_dr_flac__read_int32(ma_dr_flac_bs* bs, unsigned int bitCount, ma_int32* pResult) +static drflac_bool32 drflac__read_int32(drflac_bs* bs, unsigned int bitCount, drflac_int32* pResult) { - ma_uint32 result; - MA_DR_FLAC_ASSERT(bs != NULL); - MA_DR_FLAC_ASSERT(pResult != NULL); - MA_DR_FLAC_ASSERT(bitCount > 0); - MA_DR_FLAC_ASSERT(bitCount <= 32); - if (!ma_dr_flac__read_uint32(bs, bitCount, &result)) { - return MA_FALSE; + drflac_uint32 result; + DRFLAC_ASSERT(bs != NULL); + DRFLAC_ASSERT(pResult != NULL); + DRFLAC_ASSERT(bitCount > 0); + DRFLAC_ASSERT(bitCount <= 32); + if (!drflac__read_uint32(bs, bitCount, &result)) { + return DRFLAC_FALSE; } if (bitCount < 32) { - ma_uint32 signbit; + drflac_uint32 signbit; signbit = ((result >> (bitCount-1)) & 0x01); result |= (~signbit + 1) << bitCount; } - *pResult = (ma_int32)result; - return MA_TRUE; + *pResult = (drflac_int32)result; + return DRFLAC_TRUE; } -#ifdef MA_64BIT -static ma_bool32 ma_dr_flac__read_uint64(ma_dr_flac_bs* bs, unsigned int bitCount, ma_uint64* pResultOut) +#ifdef DRFLAC_64BIT +static drflac_bool32 drflac__read_uint64(drflac_bs* bs, unsigned int bitCount, drflac_uint64* pResultOut) { - ma_uint32 resultHi; - ma_uint32 resultLo; - MA_DR_FLAC_ASSERT(bitCount <= 64); - MA_DR_FLAC_ASSERT(bitCount > 32); - if (!ma_dr_flac__read_uint32(bs, bitCount - 32, &resultHi)) { - return MA_FALSE; + drflac_uint32 resultHi; + drflac_uint32 resultLo; + DRFLAC_ASSERT(bitCount <= 64); + DRFLAC_ASSERT(bitCount > 32); + if (!drflac__read_uint32(bs, bitCount - 32, &resultHi)) { + return DRFLAC_FALSE; } - if (!ma_dr_flac__read_uint32(bs, 32, &resultLo)) { - return MA_FALSE; + if (!drflac__read_uint32(bs, 32, &resultLo)) { + return DRFLAC_FALSE; } - *pResultOut = (((ma_uint64)resultHi) << 32) | ((ma_uint64)resultLo); - return MA_TRUE; + *pResultOut = (((drflac_uint64)resultHi) << 32) | ((drflac_uint64)resultLo); + return DRFLAC_TRUE; } #endif #if 0 -static ma_bool32 ma_dr_flac__read_int64(ma_dr_flac_bs* bs, unsigned int bitCount, ma_int64* pResultOut) +static drflac_bool32 drflac__read_int64(drflac_bs* bs, unsigned int bitCount, drflac_int64* pResultOut) { - ma_uint64 result; - ma_uint64 signbit; - MA_DR_FLAC_ASSERT(bitCount <= 64); - if (!ma_dr_flac__read_uint64(bs, bitCount, &result)) { - return MA_FALSE; + drflac_uint64 result; + drflac_uint64 signbit; + DRFLAC_ASSERT(bitCount <= 64); + if (!drflac__read_uint64(bs, bitCount, &result)) { + return DRFLAC_FALSE; } signbit = ((result >> (bitCount-1)) & 0x01); result |= (~signbit + 1) << bitCount; - *pResultOut = (ma_int64)result; - return MA_TRUE; + *pResultOut = (drflac_int64)result; + return DRFLAC_TRUE; } #endif -static ma_bool32 ma_dr_flac__read_uint16(ma_dr_flac_bs* bs, unsigned int bitCount, ma_uint16* pResult) +static drflac_bool32 drflac__read_uint16(drflac_bs* bs, unsigned int bitCount, drflac_uint16* pResult) { - ma_uint32 result; - MA_DR_FLAC_ASSERT(bs != NULL); - MA_DR_FLAC_ASSERT(pResult != NULL); - MA_DR_FLAC_ASSERT(bitCount > 0); - MA_DR_FLAC_ASSERT(bitCount <= 16); - if (!ma_dr_flac__read_uint32(bs, bitCount, &result)) { - return MA_FALSE; + drflac_uint32 result; + DRFLAC_ASSERT(bs != NULL); + DRFLAC_ASSERT(pResult != NULL); + DRFLAC_ASSERT(bitCount > 0); + DRFLAC_ASSERT(bitCount <= 16); + if (!drflac__read_uint32(bs, bitCount, &result)) { + return DRFLAC_FALSE; } - *pResult = (ma_uint16)result; - return MA_TRUE; + *pResult = (drflac_uint16)result; + return DRFLAC_TRUE; } #if 0 -static ma_bool32 ma_dr_flac__read_int16(ma_dr_flac_bs* bs, unsigned int bitCount, ma_int16* pResult) -{ - ma_int32 result; - MA_DR_FLAC_ASSERT(bs != NULL); - MA_DR_FLAC_ASSERT(pResult != NULL); - MA_DR_FLAC_ASSERT(bitCount > 0); - MA_DR_FLAC_ASSERT(bitCount <= 16); - if (!ma_dr_flac__read_int32(bs, bitCount, &result)) { - return MA_FALSE; +static drflac_bool32 drflac__read_int16(drflac_bs* bs, unsigned int bitCount, drflac_int16* pResult) +{ + drflac_int32 result; + DRFLAC_ASSERT(bs != NULL); + DRFLAC_ASSERT(pResult != NULL); + DRFLAC_ASSERT(bitCount > 0); + DRFLAC_ASSERT(bitCount <= 16); + if (!drflac__read_int32(bs, bitCount, &result)) { + return DRFLAC_FALSE; } - *pResult = (ma_int16)result; - return MA_TRUE; + *pResult = (drflac_int16)result; + return DRFLAC_TRUE; } #endif -static ma_bool32 ma_dr_flac__read_uint8(ma_dr_flac_bs* bs, unsigned int bitCount, ma_uint8* pResult) +static drflac_bool32 drflac__read_uint8(drflac_bs* bs, unsigned int bitCount, drflac_uint8* pResult) { - ma_uint32 result; - MA_DR_FLAC_ASSERT(bs != NULL); - MA_DR_FLAC_ASSERT(pResult != NULL); - MA_DR_FLAC_ASSERT(bitCount > 0); - MA_DR_FLAC_ASSERT(bitCount <= 8); - if (!ma_dr_flac__read_uint32(bs, bitCount, &result)) { - return MA_FALSE; + drflac_uint32 result; + DRFLAC_ASSERT(bs != NULL); + DRFLAC_ASSERT(pResult != NULL); + DRFLAC_ASSERT(bitCount > 0); + DRFLAC_ASSERT(bitCount <= 8); + if (!drflac__read_uint32(bs, bitCount, &result)) { + return DRFLAC_FALSE; } - *pResult = (ma_uint8)result; - return MA_TRUE; + *pResult = (drflac_uint8)result; + return DRFLAC_TRUE; } -static ma_bool32 ma_dr_flac__read_int8(ma_dr_flac_bs* bs, unsigned int bitCount, ma_int8* pResult) +static drflac_bool32 drflac__read_int8(drflac_bs* bs, unsigned int bitCount, drflac_int8* pResult) { - ma_int32 result; - MA_DR_FLAC_ASSERT(bs != NULL); - MA_DR_FLAC_ASSERT(pResult != NULL); - MA_DR_FLAC_ASSERT(bitCount > 0); - MA_DR_FLAC_ASSERT(bitCount <= 8); - if (!ma_dr_flac__read_int32(bs, bitCount, &result)) { - return MA_FALSE; + drflac_int32 result; + DRFLAC_ASSERT(bs != NULL); + DRFLAC_ASSERT(pResult != NULL); + DRFLAC_ASSERT(bitCount > 0); + DRFLAC_ASSERT(bitCount <= 8); + if (!drflac__read_int32(bs, bitCount, &result)) { + return DRFLAC_FALSE; } - *pResult = (ma_int8)result; - return MA_TRUE; + *pResult = (drflac_int8)result; + return DRFLAC_TRUE; } -static ma_bool32 ma_dr_flac__seek_bits(ma_dr_flac_bs* bs, size_t bitsToSeek) +static drflac_bool32 drflac__seek_bits(drflac_bs* bs, size_t bitsToSeek) { - if (bitsToSeek <= MA_DR_FLAC_CACHE_L1_BITS_REMAINING(bs)) { - bs->consumedBits += (ma_uint32)bitsToSeek; + if (bitsToSeek <= DRFLAC_CACHE_L1_BITS_REMAINING(bs)) { + bs->consumedBits += (drflac_uint32)bitsToSeek; bs->cache <<= bitsToSeek; - return MA_TRUE; + return DRFLAC_TRUE; } else { - bitsToSeek -= MA_DR_FLAC_CACHE_L1_BITS_REMAINING(bs); - bs->consumedBits += MA_DR_FLAC_CACHE_L1_BITS_REMAINING(bs); + bitsToSeek -= DRFLAC_CACHE_L1_BITS_REMAINING(bs); + bs->consumedBits += DRFLAC_CACHE_L1_BITS_REMAINING(bs); bs->cache = 0; -#ifdef MA_64BIT - while (bitsToSeek >= MA_DR_FLAC_CACHE_L1_SIZE_BITS(bs)) { - ma_uint64 bin; - if (!ma_dr_flac__read_uint64(bs, MA_DR_FLAC_CACHE_L1_SIZE_BITS(bs), &bin)) { - return MA_FALSE; +#ifdef DRFLAC_64BIT + while (bitsToSeek >= DRFLAC_CACHE_L1_SIZE_BITS(bs)) { + drflac_uint64 bin; + if (!drflac__read_uint64(bs, DRFLAC_CACHE_L1_SIZE_BITS(bs), &bin)) { + return DRFLAC_FALSE; } - bitsToSeek -= MA_DR_FLAC_CACHE_L1_SIZE_BITS(bs); + bitsToSeek -= DRFLAC_CACHE_L1_SIZE_BITS(bs); } #else - while (bitsToSeek >= MA_DR_FLAC_CACHE_L1_SIZE_BITS(bs)) { - ma_uint32 bin; - if (!ma_dr_flac__read_uint32(bs, MA_DR_FLAC_CACHE_L1_SIZE_BITS(bs), &bin)) { - return MA_FALSE; + while (bitsToSeek >= DRFLAC_CACHE_L1_SIZE_BITS(bs)) { + drflac_uint32 bin; + if (!drflac__read_uint32(bs, DRFLAC_CACHE_L1_SIZE_BITS(bs), &bin)) { + return DRFLAC_FALSE; } - bitsToSeek -= MA_DR_FLAC_CACHE_L1_SIZE_BITS(bs); + bitsToSeek -= DRFLAC_CACHE_L1_SIZE_BITS(bs); } #endif while (bitsToSeek >= 8) { - ma_uint8 bin; - if (!ma_dr_flac__read_uint8(bs, 8, &bin)) { - return MA_FALSE; + drflac_uint8 bin; + if (!drflac__read_uint8(bs, 8, &bin)) { + return DRFLAC_FALSE; } bitsToSeek -= 8; } if (bitsToSeek > 0) { - ma_uint8 bin; - if (!ma_dr_flac__read_uint8(bs, (ma_uint32)bitsToSeek, &bin)) { - return MA_FALSE; + drflac_uint8 bin; + if (!drflac__read_uint8(bs, (drflac_uint32)bitsToSeek, &bin)) { + return DRFLAC_FALSE; } bitsToSeek = 0; } - MA_DR_FLAC_ASSERT(bitsToSeek == 0); - return MA_TRUE; + DRFLAC_ASSERT(bitsToSeek == 0); + return DRFLAC_TRUE; } } -static ma_bool32 ma_dr_flac__find_and_seek_to_next_sync_code(ma_dr_flac_bs* bs) +static drflac_bool32 drflac__find_and_seek_to_next_sync_code(drflac_bs* bs) { - MA_DR_FLAC_ASSERT(bs != NULL); - if (!ma_dr_flac__seek_bits(bs, MA_DR_FLAC_CACHE_L1_BITS_REMAINING(bs) & 7)) { - return MA_FALSE; + DRFLAC_ASSERT(bs != NULL); + if (!drflac__seek_bits(bs, DRFLAC_CACHE_L1_BITS_REMAINING(bs) & 7)) { + return DRFLAC_FALSE; } for (;;) { - ma_uint8 hi; -#ifndef MA_DR_FLAC_NO_CRC - ma_dr_flac__reset_crc16(bs); + drflac_uint8 hi; +#ifndef DR_FLAC_NO_CRC + drflac__reset_crc16(bs); #endif - if (!ma_dr_flac__read_uint8(bs, 8, &hi)) { - return MA_FALSE; + if (!drflac__read_uint8(bs, 8, &hi)) { + return DRFLAC_FALSE; } if (hi == 0xFF) { - ma_uint8 lo; - if (!ma_dr_flac__read_uint8(bs, 6, &lo)) { - return MA_FALSE; + drflac_uint8 lo; + if (!drflac__read_uint8(bs, 6, &lo)) { + return DRFLAC_FALSE; } if (lo == 0x3E) { - return MA_TRUE; + return DRFLAC_TRUE; } else { - if (!ma_dr_flac__seek_bits(bs, MA_DR_FLAC_CACHE_L1_BITS_REMAINING(bs) & 7)) { - return MA_FALSE; + if (!drflac__seek_bits(bs, DRFLAC_CACHE_L1_BITS_REMAINING(bs) & 7)) { + return DRFLAC_FALSE; } } } } } -#if defined(MA_DR_FLAC_HAS_LZCNT_INTRINSIC) -#define MA_DR_FLAC_IMPLEMENT_CLZ_LZCNT +#if defined(DRFLAC_HAS_LZCNT_INTRINSIC) +#define DRFLAC_IMPLEMENT_CLZ_LZCNT #endif -#if defined(_MSC_VER) && _MSC_VER >= 1400 && (defined(MA_X64) || defined(MA_X86)) && !defined(__clang__) -#define MA_DR_FLAC_IMPLEMENT_CLZ_MSVC +#if defined(_MSC_VER) && _MSC_VER >= 1400 && (defined(DRFLAC_X64) || defined(DRFLAC_X86)) && !defined(__clang__) +#define DRFLAC_IMPLEMENT_CLZ_MSVC #endif #if defined(__WATCOMC__) && defined(__386__) -#define MA_DR_FLAC_IMPLEMENT_CLZ_WATCOM +#define DRFLAC_IMPLEMENT_CLZ_WATCOM #endif #ifdef __MRC__ #include -#define MA_DR_FLAC_IMPLEMENT_CLZ_MRC +#define DRFLAC_IMPLEMENT_CLZ_MRC #endif -static MA_INLINE ma_uint32 ma_dr_flac__clz_software(ma_dr_flac_cache_t x) +static DRFLAC_INLINE drflac_uint32 drflac__clz_software(drflac_cache_t x) { - ma_uint32 n; - static ma_uint32 clz_table_4[] = { + drflac_uint32 n; + static drflac_uint32 clz_table_4[] = { 0, 4, 3, 3, @@ -83936,11 +82814,11 @@ static MA_INLINE ma_uint32 ma_dr_flac__clz_software(ma_dr_flac_cache_t x) } n = clz_table_4[x >> (sizeof(x)*8 - 4)]; if (n == 0) { -#ifdef MA_64BIT - if ((x & ((ma_uint64)0xFFFFFFFF << 32)) == 0) { n = 32; x <<= 32; } - if ((x & ((ma_uint64)0xFFFF0000 << 32)) == 0) { n += 16; x <<= 16; } - if ((x & ((ma_uint64)0xFF000000 << 32)) == 0) { n += 8; x <<= 8; } - if ((x & ((ma_uint64)0xF0000000 << 32)) == 0) { n += 4; x <<= 4; } +#ifdef DRFLAC_64BIT + if ((x & ((drflac_uint64)0xFFFFFFFF << 32)) == 0) { n = 32; x <<= 32; } + if ((x & ((drflac_uint64)0xFFFF0000 << 32)) == 0) { n += 16; x <<= 16; } + if ((x & ((drflac_uint64)0xFF000000 << 32)) == 0) { n += 8; x <<= 8; } + if ((x & ((drflac_uint64)0xF0000000 << 32)) == 0) { n += 4; x <<= 4; } #else if ((x & 0xFFFF0000) == 0) { n = 16; x <<= 16; } if ((x & 0xFF000000) == 0) { n += 8; x <<= 8; } @@ -83950,52 +82828,52 @@ static MA_INLINE ma_uint32 ma_dr_flac__clz_software(ma_dr_flac_cache_t x) } return n - 1; } -#ifdef MA_DR_FLAC_IMPLEMENT_CLZ_LZCNT -static MA_INLINE ma_bool32 ma_dr_flac__is_lzcnt_supported(void) +#ifdef DRFLAC_IMPLEMENT_CLZ_LZCNT +static DRFLAC_INLINE drflac_bool32 drflac__is_lzcnt_supported(void) { -#if defined(MA_DR_FLAC_HAS_LZCNT_INTRINSIC) && defined(MA_ARM) && (defined(__ARM_ARCH) && __ARM_ARCH >= 5) - return MA_TRUE; +#if defined(DRFLAC_HAS_LZCNT_INTRINSIC) && defined(DRFLAC_ARM) && (defined(__ARM_ARCH) && __ARM_ARCH >= 5) + return DRFLAC_TRUE; #elif defined(__MRC__) - return MA_TRUE; + return DRFLAC_TRUE; #else - #ifdef MA_DR_FLAC_HAS_LZCNT_INTRINSIC - return ma_dr_flac__gIsLZCNTSupported; + #ifdef DRFLAC_HAS_LZCNT_INTRINSIC + return drflac__gIsLZCNTSupported; #else - return MA_FALSE; + return DRFLAC_FALSE; #endif #endif } -static MA_INLINE ma_uint32 ma_dr_flac__clz_lzcnt(ma_dr_flac_cache_t x) +static DRFLAC_INLINE drflac_uint32 drflac__clz_lzcnt(drflac_cache_t x) { #if defined(_MSC_VER) - #ifdef MA_64BIT - return (ma_uint32)__lzcnt64(x); + #ifdef DRFLAC_64BIT + return (drflac_uint32)__lzcnt64(x); #else - return (ma_uint32)__lzcnt(x); + return (drflac_uint32)__lzcnt(x); #endif #else #if defined(__GNUC__) || defined(__clang__) - #if defined(MA_X64) + #if defined(DRFLAC_X64) { - ma_uint64 r; + drflac_uint64 r; __asm__ __volatile__ ( "lzcnt{ %1, %0| %0, %1}" : "=r"(r) : "r"(x) : "cc" ); - return (ma_uint32)r; + return (drflac_uint32)r; } - #elif defined(MA_X86) + #elif defined(DRFLAC_X86) { - ma_uint32 r; + drflac_uint32 r; __asm__ __volatile__ ( "lzcnt{l %1, %0| %0, %1}" : "=r"(r) : "r"(x) : "cc" ); return r; } - #elif defined(MA_ARM) && (defined(__ARM_ARCH) && __ARM_ARCH >= 5) && !defined(__ARM_ARCH_6M__) && !defined(MA_64BIT) + #elif defined(DRFLAC_ARM) && (defined(__ARM_ARCH) && __ARM_ARCH >= 5) && !defined(DRFLAC_64BIT) { unsigned int r; __asm__ __volatile__ ( - #if defined(MA_64BIT) + #if defined(DRFLAC_64BIT) "clz %w[out], %w[in]" : [out]"=r"(r) : [in]"r"(x) #else "clz %[out], %[in]" : [out]"=r"(r) : [in]"r"(x) @@ -84007,10 +82885,10 @@ static MA_INLINE ma_uint32 ma_dr_flac__clz_lzcnt(ma_dr_flac_cache_t x) if (x == 0) { return sizeof(x)*8; } - #ifdef MA_64BIT - return (ma_uint32)__builtin_clzll((ma_uint64)x); + #ifdef DRFLAC_64BIT + return (drflac_uint32)__builtin_clzll((drflac_uint64)x); #else - return (ma_uint32)__builtin_clzl((ma_uint32)x); + return (drflac_uint32)__builtin_clzl((drflac_uint32)x); #endif #endif #else @@ -84019,15 +82897,15 @@ static MA_INLINE ma_uint32 ma_dr_flac__clz_lzcnt(ma_dr_flac_cache_t x) #endif } #endif -#ifdef MA_DR_FLAC_IMPLEMENT_CLZ_MSVC +#ifdef DRFLAC_IMPLEMENT_CLZ_MSVC #include -static MA_INLINE ma_uint32 ma_dr_flac__clz_msvc(ma_dr_flac_cache_t x) +static DRFLAC_INLINE drflac_uint32 drflac__clz_msvc(drflac_cache_t x) { - ma_uint32 n; + drflac_uint32 n; if (x == 0) { return sizeof(x)*8; } -#ifdef MA_64BIT +#ifdef DRFLAC_64BIT _BitScanReverse64((unsigned long*)&n, x); #else _BitScanReverse((unsigned long*)&n, x); @@ -84035,16 +82913,16 @@ static MA_INLINE ma_uint32 ma_dr_flac__clz_msvc(ma_dr_flac_cache_t x) return sizeof(x)*8 - n - 1; } #endif -#ifdef MA_DR_FLAC_IMPLEMENT_CLZ_WATCOM -static __inline ma_uint32 ma_dr_flac__clz_watcom (ma_uint32); -#ifdef MA_DR_FLAC_IMPLEMENT_CLZ_WATCOM_LZCNT -#pragma aux ma_dr_flac__clz_watcom_lzcnt = \ +#ifdef DRFLAC_IMPLEMENT_CLZ_WATCOM +static __inline drflac_uint32 drflac__clz_watcom (drflac_uint32); +#ifdef DRFLAC_IMPLEMENT_CLZ_WATCOM_LZCNT +#pragma aux drflac__clz_watcom_lzcnt = \ "db 0F3h, 0Fh, 0BDh, 0C0h" \ parm [eax] \ value [eax] \ modify nomemory; #else -#pragma aux ma_dr_flac__clz_watcom = \ +#pragma aux drflac__clz_watcom = \ "bsr eax, eax" \ "xor eax, 31" \ parm [eax] nomemory \ @@ -84052,103 +82930,103 @@ static __inline ma_uint32 ma_dr_flac__clz_watcom (ma_uint32); modify exact [eax] nomemory; #endif #endif -static MA_INLINE ma_uint32 ma_dr_flac__clz(ma_dr_flac_cache_t x) +static DRFLAC_INLINE drflac_uint32 drflac__clz(drflac_cache_t x) { -#ifdef MA_DR_FLAC_IMPLEMENT_CLZ_LZCNT - if (ma_dr_flac__is_lzcnt_supported()) { - return ma_dr_flac__clz_lzcnt(x); +#ifdef DRFLAC_IMPLEMENT_CLZ_LZCNT + if (drflac__is_lzcnt_supported()) { + return drflac__clz_lzcnt(x); } else #endif { -#ifdef MA_DR_FLAC_IMPLEMENT_CLZ_MSVC - return ma_dr_flac__clz_msvc(x); -#elif defined(MA_DR_FLAC_IMPLEMENT_CLZ_WATCOM_LZCNT) - return ma_dr_flac__clz_watcom_lzcnt(x); -#elif defined(MA_DR_FLAC_IMPLEMENT_CLZ_WATCOM) - return (x == 0) ? sizeof(x)*8 : ma_dr_flac__clz_watcom(x); +#ifdef DRFLAC_IMPLEMENT_CLZ_MSVC + return drflac__clz_msvc(x); +#elif defined(DRFLAC_IMPLEMENT_CLZ_WATCOM_LZCNT) + return drflac__clz_watcom_lzcnt(x); +#elif defined(DRFLAC_IMPLEMENT_CLZ_WATCOM) + return (x == 0) ? sizeof(x)*8 : drflac__clz_watcom(x); #elif defined(__MRC__) return __cntlzw(x); #else - return ma_dr_flac__clz_software(x); + return drflac__clz_software(x); #endif } } -static MA_INLINE ma_bool32 ma_dr_flac__seek_past_next_set_bit(ma_dr_flac_bs* bs, unsigned int* pOffsetOut) +static DRFLAC_INLINE drflac_bool32 drflac__seek_past_next_set_bit(drflac_bs* bs, unsigned int* pOffsetOut) { - ma_uint32 zeroCounter = 0; - ma_uint32 setBitOffsetPlus1; + drflac_uint32 zeroCounter = 0; + drflac_uint32 setBitOffsetPlus1; while (bs->cache == 0) { - zeroCounter += (ma_uint32)MA_DR_FLAC_CACHE_L1_BITS_REMAINING(bs); - if (!ma_dr_flac__reload_cache(bs)) { - return MA_FALSE; + zeroCounter += (drflac_uint32)DRFLAC_CACHE_L1_BITS_REMAINING(bs); + if (!drflac__reload_cache(bs)) { + return DRFLAC_FALSE; } } if (bs->cache == 1) { - *pOffsetOut = zeroCounter + (ma_uint32)MA_DR_FLAC_CACHE_L1_BITS_REMAINING(bs) - 1; - if (!ma_dr_flac__reload_cache(bs)) { - return MA_FALSE; + *pOffsetOut = zeroCounter + (drflac_uint32)DRFLAC_CACHE_L1_BITS_REMAINING(bs) - 1; + if (!drflac__reload_cache(bs)) { + return DRFLAC_FALSE; } - return MA_TRUE; + return DRFLAC_TRUE; } - setBitOffsetPlus1 = ma_dr_flac__clz(bs->cache); + setBitOffsetPlus1 = drflac__clz(bs->cache); setBitOffsetPlus1 += 1; - if (setBitOffsetPlus1 > MA_DR_FLAC_CACHE_L1_BITS_REMAINING(bs)) { - return MA_FALSE; + if (setBitOffsetPlus1 > DRFLAC_CACHE_L1_BITS_REMAINING(bs)) { + return DRFLAC_FALSE; } bs->consumedBits += setBitOffsetPlus1; bs->cache <<= setBitOffsetPlus1; *pOffsetOut = zeroCounter + setBitOffsetPlus1 - 1; - return MA_TRUE; + return DRFLAC_TRUE; } -static ma_bool32 ma_dr_flac__seek_to_byte(ma_dr_flac_bs* bs, ma_uint64 offsetFromStart) +static drflac_bool32 drflac__seek_to_byte(drflac_bs* bs, drflac_uint64 offsetFromStart) { - MA_DR_FLAC_ASSERT(bs != NULL); - MA_DR_FLAC_ASSERT(offsetFromStart > 0); + DRFLAC_ASSERT(bs != NULL); + DRFLAC_ASSERT(offsetFromStart > 0); if (offsetFromStart > 0x7FFFFFFF) { - ma_uint64 bytesRemaining = offsetFromStart; - if (!bs->onSeek(bs->pUserData, 0x7FFFFFFF, ma_dr_flac_seek_origin_start)) { - return MA_FALSE; + drflac_uint64 bytesRemaining = offsetFromStart; + if (!bs->onSeek(bs->pUserData, 0x7FFFFFFF, drflac_seek_origin_start)) { + return DRFLAC_FALSE; } bytesRemaining -= 0x7FFFFFFF; while (bytesRemaining > 0x7FFFFFFF) { - if (!bs->onSeek(bs->pUserData, 0x7FFFFFFF, ma_dr_flac_seek_origin_current)) { - return MA_FALSE; + if (!bs->onSeek(bs->pUserData, 0x7FFFFFFF, drflac_seek_origin_current)) { + return DRFLAC_FALSE; } bytesRemaining -= 0x7FFFFFFF; } if (bytesRemaining > 0) { - if (!bs->onSeek(bs->pUserData, (int)bytesRemaining, ma_dr_flac_seek_origin_current)) { - return MA_FALSE; + if (!bs->onSeek(bs->pUserData, (int)bytesRemaining, drflac_seek_origin_current)) { + return DRFLAC_FALSE; } } } else { - if (!bs->onSeek(bs->pUserData, (int)offsetFromStart, ma_dr_flac_seek_origin_start)) { - return MA_FALSE; + if (!bs->onSeek(bs->pUserData, (int)offsetFromStart, drflac_seek_origin_start)) { + return DRFLAC_FALSE; } } - ma_dr_flac__reset_cache(bs); - return MA_TRUE; + drflac__reset_cache(bs); + return DRFLAC_TRUE; } -static ma_result ma_dr_flac__read_utf8_coded_number(ma_dr_flac_bs* bs, ma_uint64* pNumberOut, ma_uint8* pCRCOut) +static drflac_result drflac__read_utf8_coded_number(drflac_bs* bs, drflac_uint64* pNumberOut, drflac_uint8* pCRCOut) { - ma_uint8 crc; - ma_uint64 result; - ma_uint8 utf8[7] = {0}; + drflac_uint8 crc; + drflac_uint64 result; + drflac_uint8 utf8[7] = {0}; int byteCount; int i; - MA_DR_FLAC_ASSERT(bs != NULL); - MA_DR_FLAC_ASSERT(pNumberOut != NULL); - MA_DR_FLAC_ASSERT(pCRCOut != NULL); + DRFLAC_ASSERT(bs != NULL); + DRFLAC_ASSERT(pNumberOut != NULL); + DRFLAC_ASSERT(pCRCOut != NULL); crc = *pCRCOut; - if (!ma_dr_flac__read_uint8(bs, 8, utf8)) { + if (!drflac__read_uint8(bs, 8, utf8)) { *pNumberOut = 0; - return MA_AT_END; + return DRFLAC_AT_END; } - crc = ma_dr_flac_crc8(crc, utf8[0], 8); + crc = drflac_crc8(crc, utf8[0], 8); if ((utf8[0] & 0x80) == 0) { *pNumberOut = utf8[0]; *pCRCOut = crc; - return MA_SUCCESS; + return DRFLAC_SUCCESS; } if ((utf8[0] & 0xE0) == 0xC0) { byteCount = 2; @@ -84164,26 +83042,26 @@ static ma_result ma_dr_flac__read_utf8_coded_number(ma_dr_flac_bs* bs, ma_uint64 byteCount = 7; } else { *pNumberOut = 0; - return MA_CRC_MISMATCH; + return DRFLAC_CRC_MISMATCH; } - MA_DR_FLAC_ASSERT(byteCount > 1); - result = (ma_uint64)(utf8[0] & (0xFF >> (byteCount + 1))); + DRFLAC_ASSERT(byteCount > 1); + result = (drflac_uint64)(utf8[0] & (0xFF >> (byteCount + 1))); for (i = 1; i < byteCount; ++i) { - if (!ma_dr_flac__read_uint8(bs, 8, utf8 + i)) { + if (!drflac__read_uint8(bs, 8, utf8 + i)) { *pNumberOut = 0; - return MA_AT_END; + return DRFLAC_AT_END; } - crc = ma_dr_flac_crc8(crc, utf8[i], 8); + crc = drflac_crc8(crc, utf8[i], 8); result = (result << 6) | (utf8[i] & 0x3F); } *pNumberOut = result; *pCRCOut = crc; - return MA_SUCCESS; + return DRFLAC_SUCCESS; } -static MA_INLINE ma_uint32 ma_dr_flac__ilog2_u32(ma_uint32 x) +static DRFLAC_INLINE drflac_uint32 drflac__ilog2_u32(drflac_uint32 x) { #if 1 - ma_uint32 result = 0; + drflac_uint32 result = 0; while (x > 0) { result += 1; x >>= 1; @@ -84191,17 +83069,17 @@ static MA_INLINE ma_uint32 ma_dr_flac__ilog2_u32(ma_uint32 x) return result; #endif } -static MA_INLINE ma_bool32 ma_dr_flac__use_64_bit_prediction(ma_uint32 bitsPerSample, ma_uint32 order, ma_uint32 precision) +static DRFLAC_INLINE drflac_bool32 drflac__use_64_bit_prediction(drflac_uint32 bitsPerSample, drflac_uint32 order, drflac_uint32 precision) { - return bitsPerSample + precision + ma_dr_flac__ilog2_u32(order) > 32; + return bitsPerSample + precision + drflac__ilog2_u32(order) > 32; } #if defined(__clang__) __attribute__((no_sanitize("signed-integer-overflow"))) #endif -static MA_INLINE ma_int32 ma_dr_flac__calculate_prediction_32(ma_uint32 order, ma_int32 shift, const ma_int32* coefficients, ma_int32* pDecodedSamples) +static DRFLAC_INLINE drflac_int32 drflac__calculate_prediction_32(drflac_uint32 order, drflac_int32 shift, const drflac_int32* coefficients, drflac_int32* pDecodedSamples) { - ma_int32 prediction = 0; - MA_DR_FLAC_ASSERT(order <= 32); + drflac_int32 prediction = 0; + DRFLAC_ASSERT(order <= 32); switch (order) { case 32: prediction += coefficients[31] * pDecodedSamples[-32]; @@ -84237,188 +83115,188 @@ static MA_INLINE ma_int32 ma_dr_flac__calculate_prediction_32(ma_uint32 order, m case 2: prediction += coefficients[ 1] * pDecodedSamples[- 2]; case 1: prediction += coefficients[ 0] * pDecodedSamples[- 1]; } - return (ma_int32)(prediction >> shift); + return (drflac_int32)(prediction >> shift); } -static MA_INLINE ma_int32 ma_dr_flac__calculate_prediction_64(ma_uint32 order, ma_int32 shift, const ma_int32* coefficients, ma_int32* pDecodedSamples) +static DRFLAC_INLINE drflac_int32 drflac__calculate_prediction_64(drflac_uint32 order, drflac_int32 shift, const drflac_int32* coefficients, drflac_int32* pDecodedSamples) { - ma_int64 prediction; - MA_DR_FLAC_ASSERT(order <= 32); -#ifndef MA_64BIT + drflac_int64 prediction; + DRFLAC_ASSERT(order <= 32); +#ifndef DRFLAC_64BIT if (order == 8) { - prediction = coefficients[0] * (ma_int64)pDecodedSamples[-1]; - prediction += coefficients[1] * (ma_int64)pDecodedSamples[-2]; - prediction += coefficients[2] * (ma_int64)pDecodedSamples[-3]; - prediction += coefficients[3] * (ma_int64)pDecodedSamples[-4]; - prediction += coefficients[4] * (ma_int64)pDecodedSamples[-5]; - prediction += coefficients[5] * (ma_int64)pDecodedSamples[-6]; - prediction += coefficients[6] * (ma_int64)pDecodedSamples[-7]; - prediction += coefficients[7] * (ma_int64)pDecodedSamples[-8]; + prediction = coefficients[0] * (drflac_int64)pDecodedSamples[-1]; + prediction += coefficients[1] * (drflac_int64)pDecodedSamples[-2]; + prediction += coefficients[2] * (drflac_int64)pDecodedSamples[-3]; + prediction += coefficients[3] * (drflac_int64)pDecodedSamples[-4]; + prediction += coefficients[4] * (drflac_int64)pDecodedSamples[-5]; + prediction += coefficients[5] * (drflac_int64)pDecodedSamples[-6]; + prediction += coefficients[6] * (drflac_int64)pDecodedSamples[-7]; + prediction += coefficients[7] * (drflac_int64)pDecodedSamples[-8]; } else if (order == 7) { - prediction = coefficients[0] * (ma_int64)pDecodedSamples[-1]; - prediction += coefficients[1] * (ma_int64)pDecodedSamples[-2]; - prediction += coefficients[2] * (ma_int64)pDecodedSamples[-3]; - prediction += coefficients[3] * (ma_int64)pDecodedSamples[-4]; - prediction += coefficients[4] * (ma_int64)pDecodedSamples[-5]; - prediction += coefficients[5] * (ma_int64)pDecodedSamples[-6]; - prediction += coefficients[6] * (ma_int64)pDecodedSamples[-7]; + prediction = coefficients[0] * (drflac_int64)pDecodedSamples[-1]; + prediction += coefficients[1] * (drflac_int64)pDecodedSamples[-2]; + prediction += coefficients[2] * (drflac_int64)pDecodedSamples[-3]; + prediction += coefficients[3] * (drflac_int64)pDecodedSamples[-4]; + prediction += coefficients[4] * (drflac_int64)pDecodedSamples[-5]; + prediction += coefficients[5] * (drflac_int64)pDecodedSamples[-6]; + prediction += coefficients[6] * (drflac_int64)pDecodedSamples[-7]; } else if (order == 3) { - prediction = coefficients[0] * (ma_int64)pDecodedSamples[-1]; - prediction += coefficients[1] * (ma_int64)pDecodedSamples[-2]; - prediction += coefficients[2] * (ma_int64)pDecodedSamples[-3]; + prediction = coefficients[0] * (drflac_int64)pDecodedSamples[-1]; + prediction += coefficients[1] * (drflac_int64)pDecodedSamples[-2]; + prediction += coefficients[2] * (drflac_int64)pDecodedSamples[-3]; } else if (order == 6) { - prediction = coefficients[0] * (ma_int64)pDecodedSamples[-1]; - prediction += coefficients[1] * (ma_int64)pDecodedSamples[-2]; - prediction += coefficients[2] * (ma_int64)pDecodedSamples[-3]; - prediction += coefficients[3] * (ma_int64)pDecodedSamples[-4]; - prediction += coefficients[4] * (ma_int64)pDecodedSamples[-5]; - prediction += coefficients[5] * (ma_int64)pDecodedSamples[-6]; + prediction = coefficients[0] * (drflac_int64)pDecodedSamples[-1]; + prediction += coefficients[1] * (drflac_int64)pDecodedSamples[-2]; + prediction += coefficients[2] * (drflac_int64)pDecodedSamples[-3]; + prediction += coefficients[3] * (drflac_int64)pDecodedSamples[-4]; + prediction += coefficients[4] * (drflac_int64)pDecodedSamples[-5]; + prediction += coefficients[5] * (drflac_int64)pDecodedSamples[-6]; } else if (order == 5) { - prediction = coefficients[0] * (ma_int64)pDecodedSamples[-1]; - prediction += coefficients[1] * (ma_int64)pDecodedSamples[-2]; - prediction += coefficients[2] * (ma_int64)pDecodedSamples[-3]; - prediction += coefficients[3] * (ma_int64)pDecodedSamples[-4]; - prediction += coefficients[4] * (ma_int64)pDecodedSamples[-5]; + prediction = coefficients[0] * (drflac_int64)pDecodedSamples[-1]; + prediction += coefficients[1] * (drflac_int64)pDecodedSamples[-2]; + prediction += coefficients[2] * (drflac_int64)pDecodedSamples[-3]; + prediction += coefficients[3] * (drflac_int64)pDecodedSamples[-4]; + prediction += coefficients[4] * (drflac_int64)pDecodedSamples[-5]; } else if (order == 4) { - prediction = coefficients[0] * (ma_int64)pDecodedSamples[-1]; - prediction += coefficients[1] * (ma_int64)pDecodedSamples[-2]; - prediction += coefficients[2] * (ma_int64)pDecodedSamples[-3]; - prediction += coefficients[3] * (ma_int64)pDecodedSamples[-4]; + prediction = coefficients[0] * (drflac_int64)pDecodedSamples[-1]; + prediction += coefficients[1] * (drflac_int64)pDecodedSamples[-2]; + prediction += coefficients[2] * (drflac_int64)pDecodedSamples[-3]; + prediction += coefficients[3] * (drflac_int64)pDecodedSamples[-4]; } else if (order == 12) { - prediction = coefficients[0] * (ma_int64)pDecodedSamples[-1]; - prediction += coefficients[1] * (ma_int64)pDecodedSamples[-2]; - prediction += coefficients[2] * (ma_int64)pDecodedSamples[-3]; - prediction += coefficients[3] * (ma_int64)pDecodedSamples[-4]; - prediction += coefficients[4] * (ma_int64)pDecodedSamples[-5]; - prediction += coefficients[5] * (ma_int64)pDecodedSamples[-6]; - prediction += coefficients[6] * (ma_int64)pDecodedSamples[-7]; - prediction += coefficients[7] * (ma_int64)pDecodedSamples[-8]; - prediction += coefficients[8] * (ma_int64)pDecodedSamples[-9]; - prediction += coefficients[9] * (ma_int64)pDecodedSamples[-10]; - prediction += coefficients[10] * (ma_int64)pDecodedSamples[-11]; - prediction += coefficients[11] * (ma_int64)pDecodedSamples[-12]; + prediction = coefficients[0] * (drflac_int64)pDecodedSamples[-1]; + prediction += coefficients[1] * (drflac_int64)pDecodedSamples[-2]; + prediction += coefficients[2] * (drflac_int64)pDecodedSamples[-3]; + prediction += coefficients[3] * (drflac_int64)pDecodedSamples[-4]; + prediction += coefficients[4] * (drflac_int64)pDecodedSamples[-5]; + prediction += coefficients[5] * (drflac_int64)pDecodedSamples[-6]; + prediction += coefficients[6] * (drflac_int64)pDecodedSamples[-7]; + prediction += coefficients[7] * (drflac_int64)pDecodedSamples[-8]; + prediction += coefficients[8] * (drflac_int64)pDecodedSamples[-9]; + prediction += coefficients[9] * (drflac_int64)pDecodedSamples[-10]; + prediction += coefficients[10] * (drflac_int64)pDecodedSamples[-11]; + prediction += coefficients[11] * (drflac_int64)pDecodedSamples[-12]; } else if (order == 2) { - prediction = coefficients[0] * (ma_int64)pDecodedSamples[-1]; - prediction += coefficients[1] * (ma_int64)pDecodedSamples[-2]; + prediction = coefficients[0] * (drflac_int64)pDecodedSamples[-1]; + prediction += coefficients[1] * (drflac_int64)pDecodedSamples[-2]; } else if (order == 1) { - prediction = coefficients[0] * (ma_int64)pDecodedSamples[-1]; + prediction = coefficients[0] * (drflac_int64)pDecodedSamples[-1]; } else if (order == 10) { - prediction = coefficients[0] * (ma_int64)pDecodedSamples[-1]; - prediction += coefficients[1] * (ma_int64)pDecodedSamples[-2]; - prediction += coefficients[2] * (ma_int64)pDecodedSamples[-3]; - prediction += coefficients[3] * (ma_int64)pDecodedSamples[-4]; - prediction += coefficients[4] * (ma_int64)pDecodedSamples[-5]; - prediction += coefficients[5] * (ma_int64)pDecodedSamples[-6]; - prediction += coefficients[6] * (ma_int64)pDecodedSamples[-7]; - prediction += coefficients[7] * (ma_int64)pDecodedSamples[-8]; - prediction += coefficients[8] * (ma_int64)pDecodedSamples[-9]; - prediction += coefficients[9] * (ma_int64)pDecodedSamples[-10]; + prediction = coefficients[0] * (drflac_int64)pDecodedSamples[-1]; + prediction += coefficients[1] * (drflac_int64)pDecodedSamples[-2]; + prediction += coefficients[2] * (drflac_int64)pDecodedSamples[-3]; + prediction += coefficients[3] * (drflac_int64)pDecodedSamples[-4]; + prediction += coefficients[4] * (drflac_int64)pDecodedSamples[-5]; + prediction += coefficients[5] * (drflac_int64)pDecodedSamples[-6]; + prediction += coefficients[6] * (drflac_int64)pDecodedSamples[-7]; + prediction += coefficients[7] * (drflac_int64)pDecodedSamples[-8]; + prediction += coefficients[8] * (drflac_int64)pDecodedSamples[-9]; + prediction += coefficients[9] * (drflac_int64)pDecodedSamples[-10]; } else if (order == 9) { - prediction = coefficients[0] * (ma_int64)pDecodedSamples[-1]; - prediction += coefficients[1] * (ma_int64)pDecodedSamples[-2]; - prediction += coefficients[2] * (ma_int64)pDecodedSamples[-3]; - prediction += coefficients[3] * (ma_int64)pDecodedSamples[-4]; - prediction += coefficients[4] * (ma_int64)pDecodedSamples[-5]; - prediction += coefficients[5] * (ma_int64)pDecodedSamples[-6]; - prediction += coefficients[6] * (ma_int64)pDecodedSamples[-7]; - prediction += coefficients[7] * (ma_int64)pDecodedSamples[-8]; - prediction += coefficients[8] * (ma_int64)pDecodedSamples[-9]; + prediction = coefficients[0] * (drflac_int64)pDecodedSamples[-1]; + prediction += coefficients[1] * (drflac_int64)pDecodedSamples[-2]; + prediction += coefficients[2] * (drflac_int64)pDecodedSamples[-3]; + prediction += coefficients[3] * (drflac_int64)pDecodedSamples[-4]; + prediction += coefficients[4] * (drflac_int64)pDecodedSamples[-5]; + prediction += coefficients[5] * (drflac_int64)pDecodedSamples[-6]; + prediction += coefficients[6] * (drflac_int64)pDecodedSamples[-7]; + prediction += coefficients[7] * (drflac_int64)pDecodedSamples[-8]; + prediction += coefficients[8] * (drflac_int64)pDecodedSamples[-9]; } else if (order == 11) { - prediction = coefficients[0] * (ma_int64)pDecodedSamples[-1]; - prediction += coefficients[1] * (ma_int64)pDecodedSamples[-2]; - prediction += coefficients[2] * (ma_int64)pDecodedSamples[-3]; - prediction += coefficients[3] * (ma_int64)pDecodedSamples[-4]; - prediction += coefficients[4] * (ma_int64)pDecodedSamples[-5]; - prediction += coefficients[5] * (ma_int64)pDecodedSamples[-6]; - prediction += coefficients[6] * (ma_int64)pDecodedSamples[-7]; - prediction += coefficients[7] * (ma_int64)pDecodedSamples[-8]; - prediction += coefficients[8] * (ma_int64)pDecodedSamples[-9]; - prediction += coefficients[9] * (ma_int64)pDecodedSamples[-10]; - prediction += coefficients[10] * (ma_int64)pDecodedSamples[-11]; + prediction = coefficients[0] * (drflac_int64)pDecodedSamples[-1]; + prediction += coefficients[1] * (drflac_int64)pDecodedSamples[-2]; + prediction += coefficients[2] * (drflac_int64)pDecodedSamples[-3]; + prediction += coefficients[3] * (drflac_int64)pDecodedSamples[-4]; + prediction += coefficients[4] * (drflac_int64)pDecodedSamples[-5]; + prediction += coefficients[5] * (drflac_int64)pDecodedSamples[-6]; + prediction += coefficients[6] * (drflac_int64)pDecodedSamples[-7]; + prediction += coefficients[7] * (drflac_int64)pDecodedSamples[-8]; + prediction += coefficients[8] * (drflac_int64)pDecodedSamples[-9]; + prediction += coefficients[9] * (drflac_int64)pDecodedSamples[-10]; + prediction += coefficients[10] * (drflac_int64)pDecodedSamples[-11]; } else { int j; prediction = 0; for (j = 0; j < (int)order; ++j) { - prediction += coefficients[j] * (ma_int64)pDecodedSamples[-j-1]; + prediction += coefficients[j] * (drflac_int64)pDecodedSamples[-j-1]; } } #endif -#ifdef MA_64BIT +#ifdef DRFLAC_64BIT prediction = 0; switch (order) { - case 32: prediction += coefficients[31] * (ma_int64)pDecodedSamples[-32]; - case 31: prediction += coefficients[30] * (ma_int64)pDecodedSamples[-31]; - case 30: prediction += coefficients[29] * (ma_int64)pDecodedSamples[-30]; - case 29: prediction += coefficients[28] * (ma_int64)pDecodedSamples[-29]; - case 28: prediction += coefficients[27] * (ma_int64)pDecodedSamples[-28]; - case 27: prediction += coefficients[26] * (ma_int64)pDecodedSamples[-27]; - case 26: prediction += coefficients[25] * (ma_int64)pDecodedSamples[-26]; - case 25: prediction += coefficients[24] * (ma_int64)pDecodedSamples[-25]; - case 24: prediction += coefficients[23] * (ma_int64)pDecodedSamples[-24]; - case 23: prediction += coefficients[22] * (ma_int64)pDecodedSamples[-23]; - case 22: prediction += coefficients[21] * (ma_int64)pDecodedSamples[-22]; - case 21: prediction += coefficients[20] * (ma_int64)pDecodedSamples[-21]; - case 20: prediction += coefficients[19] * (ma_int64)pDecodedSamples[-20]; - case 19: prediction += coefficients[18] * (ma_int64)pDecodedSamples[-19]; - case 18: prediction += coefficients[17] * (ma_int64)pDecodedSamples[-18]; - case 17: prediction += coefficients[16] * (ma_int64)pDecodedSamples[-17]; - case 16: prediction += coefficients[15] * (ma_int64)pDecodedSamples[-16]; - case 15: prediction += coefficients[14] * (ma_int64)pDecodedSamples[-15]; - case 14: prediction += coefficients[13] * (ma_int64)pDecodedSamples[-14]; - case 13: prediction += coefficients[12] * (ma_int64)pDecodedSamples[-13]; - case 12: prediction += coefficients[11] * (ma_int64)pDecodedSamples[-12]; - case 11: prediction += coefficients[10] * (ma_int64)pDecodedSamples[-11]; - case 10: prediction += coefficients[ 9] * (ma_int64)pDecodedSamples[-10]; - case 9: prediction += coefficients[ 8] * (ma_int64)pDecodedSamples[- 9]; - case 8: prediction += coefficients[ 7] * (ma_int64)pDecodedSamples[- 8]; - case 7: prediction += coefficients[ 6] * (ma_int64)pDecodedSamples[- 7]; - case 6: prediction += coefficients[ 5] * (ma_int64)pDecodedSamples[- 6]; - case 5: prediction += coefficients[ 4] * (ma_int64)pDecodedSamples[- 5]; - case 4: prediction += coefficients[ 3] * (ma_int64)pDecodedSamples[- 4]; - case 3: prediction += coefficients[ 2] * (ma_int64)pDecodedSamples[- 3]; - case 2: prediction += coefficients[ 1] * (ma_int64)pDecodedSamples[- 2]; - case 1: prediction += coefficients[ 0] * (ma_int64)pDecodedSamples[- 1]; - } -#endif - return (ma_int32)(prediction >> shift); + case 32: prediction += coefficients[31] * (drflac_int64)pDecodedSamples[-32]; + case 31: prediction += coefficients[30] * (drflac_int64)pDecodedSamples[-31]; + case 30: prediction += coefficients[29] * (drflac_int64)pDecodedSamples[-30]; + case 29: prediction += coefficients[28] * (drflac_int64)pDecodedSamples[-29]; + case 28: prediction += coefficients[27] * (drflac_int64)pDecodedSamples[-28]; + case 27: prediction += coefficients[26] * (drflac_int64)pDecodedSamples[-27]; + case 26: prediction += coefficients[25] * (drflac_int64)pDecodedSamples[-26]; + case 25: prediction += coefficients[24] * (drflac_int64)pDecodedSamples[-25]; + case 24: prediction += coefficients[23] * (drflac_int64)pDecodedSamples[-24]; + case 23: prediction += coefficients[22] * (drflac_int64)pDecodedSamples[-23]; + case 22: prediction += coefficients[21] * (drflac_int64)pDecodedSamples[-22]; + case 21: prediction += coefficients[20] * (drflac_int64)pDecodedSamples[-21]; + case 20: prediction += coefficients[19] * (drflac_int64)pDecodedSamples[-20]; + case 19: prediction += coefficients[18] * (drflac_int64)pDecodedSamples[-19]; + case 18: prediction += coefficients[17] * (drflac_int64)pDecodedSamples[-18]; + case 17: prediction += coefficients[16] * (drflac_int64)pDecodedSamples[-17]; + case 16: prediction += coefficients[15] * (drflac_int64)pDecodedSamples[-16]; + case 15: prediction += coefficients[14] * (drflac_int64)pDecodedSamples[-15]; + case 14: prediction += coefficients[13] * (drflac_int64)pDecodedSamples[-14]; + case 13: prediction += coefficients[12] * (drflac_int64)pDecodedSamples[-13]; + case 12: prediction += coefficients[11] * (drflac_int64)pDecodedSamples[-12]; + case 11: prediction += coefficients[10] * (drflac_int64)pDecodedSamples[-11]; + case 10: prediction += coefficients[ 9] * (drflac_int64)pDecodedSamples[-10]; + case 9: prediction += coefficients[ 8] * (drflac_int64)pDecodedSamples[- 9]; + case 8: prediction += coefficients[ 7] * (drflac_int64)pDecodedSamples[- 8]; + case 7: prediction += coefficients[ 6] * (drflac_int64)pDecodedSamples[- 7]; + case 6: prediction += coefficients[ 5] * (drflac_int64)pDecodedSamples[- 6]; + case 5: prediction += coefficients[ 4] * (drflac_int64)pDecodedSamples[- 5]; + case 4: prediction += coefficients[ 3] * (drflac_int64)pDecodedSamples[- 4]; + case 3: prediction += coefficients[ 2] * (drflac_int64)pDecodedSamples[- 3]; + case 2: prediction += coefficients[ 1] * (drflac_int64)pDecodedSamples[- 2]; + case 1: prediction += coefficients[ 0] * (drflac_int64)pDecodedSamples[- 1]; + } +#endif + return (drflac_int32)(prediction >> shift); } #if 0 -static ma_bool32 ma_dr_flac__decode_samples_with_residual__rice__reference(ma_dr_flac_bs* bs, ma_uint32 bitsPerSample, ma_uint32 count, ma_uint8 riceParam, ma_uint32 lpcOrder, ma_int32 lpcShift, ma_uint32 lpcPrecision, const ma_int32* coefficients, ma_int32* pSamplesOut) +static drflac_bool32 drflac__decode_samples_with_residual__rice__reference(drflac_bs* bs, drflac_uint32 bitsPerSample, drflac_uint32 count, drflac_uint8 riceParam, drflac_uint32 lpcOrder, drflac_int32 lpcShift, drflac_uint32 lpcPrecision, const drflac_int32* coefficients, drflac_int32* pSamplesOut) { - ma_uint32 i; - MA_DR_FLAC_ASSERT(bs != NULL); - MA_DR_FLAC_ASSERT(pSamplesOut != NULL); + drflac_uint32 i; + DRFLAC_ASSERT(bs != NULL); + DRFLAC_ASSERT(pSamplesOut != NULL); for (i = 0; i < count; ++i) { - ma_uint32 zeroCounter = 0; + drflac_uint32 zeroCounter = 0; for (;;) { - ma_uint8 bit; - if (!ma_dr_flac__read_uint8(bs, 1, &bit)) { - return MA_FALSE; + drflac_uint8 bit; + if (!drflac__read_uint8(bs, 1, &bit)) { + return DRFLAC_FALSE; } if (bit == 0) { zeroCounter += 1; @@ -84426,10 +83304,10 @@ static ma_bool32 ma_dr_flac__decode_samples_with_residual__rice__reference(ma_dr break; } } - ma_uint32 decodedRice; + drflac_uint32 decodedRice; if (riceParam > 0) { - if (!ma_dr_flac__read_uint32(bs, riceParam, &decodedRice)) { - return MA_FALSE; + if (!drflac__read_uint32(bs, riceParam, &decodedRice)) { + return DRFLAC_FALSE; } } else { decodedRice = 0; @@ -84440,24 +83318,24 @@ static ma_bool32 ma_dr_flac__decode_samples_with_residual__rice__reference(ma_dr } else { decodedRice = (decodedRice >> 1); } - if (ma_dr_flac__use_64_bit_prediction(bitsPerSample, lpcOrder, lpcPrecision)) { - pSamplesOut[i] = decodedRice + ma_dr_flac__calculate_prediction_64(lpcOrder, lpcShift, coefficients, pSamplesOut + i); + if (drflac__use_64_bit_prediction(bitsPerSample, lpcOrder, lpcPrecision)) { + pSamplesOut[i] = decodedRice + drflac__calculate_prediction_64(lpcOrder, lpcShift, coefficients, pSamplesOut + i); } else { - pSamplesOut[i] = decodedRice + ma_dr_flac__calculate_prediction_32(lpcOrder, lpcShift, coefficients, pSamplesOut + i); + pSamplesOut[i] = decodedRice + drflac__calculate_prediction_32(lpcOrder, lpcShift, coefficients, pSamplesOut + i); } } - return MA_TRUE; + return DRFLAC_TRUE; } #endif #if 0 -static ma_bool32 ma_dr_flac__read_rice_parts__reference(ma_dr_flac_bs* bs, ma_uint8 riceParam, ma_uint32* pZeroCounterOut, ma_uint32* pRiceParamPartOut) +static drflac_bool32 drflac__read_rice_parts__reference(drflac_bs* bs, drflac_uint8 riceParam, drflac_uint32* pZeroCounterOut, drflac_uint32* pRiceParamPartOut) { - ma_uint32 zeroCounter = 0; - ma_uint32 decodedRice; + drflac_uint32 zeroCounter = 0; + drflac_uint32 decodedRice; for (;;) { - ma_uint8 bit; - if (!ma_dr_flac__read_uint8(bs, 1, &bit)) { - return MA_FALSE; + drflac_uint8 bit; + if (!drflac__read_uint8(bs, 1, &bit)) { + return DRFLAC_FALSE; } if (bit == 0) { zeroCounter += 1; @@ -84466,142 +83344,142 @@ static ma_bool32 ma_dr_flac__read_rice_parts__reference(ma_dr_flac_bs* bs, ma_ui } } if (riceParam > 0) { - if (!ma_dr_flac__read_uint32(bs, riceParam, &decodedRice)) { - return MA_FALSE; + if (!drflac__read_uint32(bs, riceParam, &decodedRice)) { + return DRFLAC_FALSE; } } else { decodedRice = 0; } *pZeroCounterOut = zeroCounter; *pRiceParamPartOut = decodedRice; - return MA_TRUE; + return DRFLAC_TRUE; } #endif #if 0 -static MA_INLINE ma_bool32 ma_dr_flac__read_rice_parts(ma_dr_flac_bs* bs, ma_uint8 riceParam, ma_uint32* pZeroCounterOut, ma_uint32* pRiceParamPartOut) -{ - ma_dr_flac_cache_t riceParamMask; - ma_uint32 zeroCounter; - ma_uint32 setBitOffsetPlus1; - ma_uint32 riceParamPart; - ma_uint32 riceLength; - MA_DR_FLAC_ASSERT(riceParam > 0); - riceParamMask = MA_DR_FLAC_CACHE_L1_SELECTION_MASK(riceParam); +static DRFLAC_INLINE drflac_bool32 drflac__read_rice_parts(drflac_bs* bs, drflac_uint8 riceParam, drflac_uint32* pZeroCounterOut, drflac_uint32* pRiceParamPartOut) +{ + drflac_cache_t riceParamMask; + drflac_uint32 zeroCounter; + drflac_uint32 setBitOffsetPlus1; + drflac_uint32 riceParamPart; + drflac_uint32 riceLength; + DRFLAC_ASSERT(riceParam > 0); + riceParamMask = DRFLAC_CACHE_L1_SELECTION_MASK(riceParam); zeroCounter = 0; while (bs->cache == 0) { - zeroCounter += (ma_uint32)MA_DR_FLAC_CACHE_L1_BITS_REMAINING(bs); - if (!ma_dr_flac__reload_cache(bs)) { - return MA_FALSE; + zeroCounter += (drflac_uint32)DRFLAC_CACHE_L1_BITS_REMAINING(bs); + if (!drflac__reload_cache(bs)) { + return DRFLAC_FALSE; } } - setBitOffsetPlus1 = ma_dr_flac__clz(bs->cache); + setBitOffsetPlus1 = drflac__clz(bs->cache); zeroCounter += setBitOffsetPlus1; setBitOffsetPlus1 += 1; riceLength = setBitOffsetPlus1 + riceParam; - if (riceLength < MA_DR_FLAC_CACHE_L1_BITS_REMAINING(bs)) { - riceParamPart = (ma_uint32)((bs->cache & (riceParamMask >> setBitOffsetPlus1)) >> MA_DR_FLAC_CACHE_L1_SELECTION_SHIFT(bs, riceLength)); + if (riceLength < DRFLAC_CACHE_L1_BITS_REMAINING(bs)) { + riceParamPart = (drflac_uint32)((bs->cache & (riceParamMask >> setBitOffsetPlus1)) >> DRFLAC_CACHE_L1_SELECTION_SHIFT(bs, riceLength)); bs->consumedBits += riceLength; bs->cache <<= riceLength; } else { - ma_uint32 bitCountLo; - ma_dr_flac_cache_t resultHi; + drflac_uint32 bitCountLo; + drflac_cache_t resultHi; bs->consumedBits += riceLength; - bs->cache <<= setBitOffsetPlus1 & (MA_DR_FLAC_CACHE_L1_SIZE_BITS(bs)-1); - bitCountLo = bs->consumedBits - MA_DR_FLAC_CACHE_L1_SIZE_BITS(bs); - resultHi = MA_DR_FLAC_CACHE_L1_SELECT_AND_SHIFT(bs, riceParam); - if (bs->nextL2Line < MA_DR_FLAC_CACHE_L2_LINE_COUNT(bs)) { -#ifndef MA_DR_FLAC_NO_CRC - ma_dr_flac__update_crc16(bs); -#endif - bs->cache = ma_dr_flac__be2host__cache_line(bs->cacheL2[bs->nextL2Line++]); + bs->cache <<= setBitOffsetPlus1 & (DRFLAC_CACHE_L1_SIZE_BITS(bs)-1); + bitCountLo = bs->consumedBits - DRFLAC_CACHE_L1_SIZE_BITS(bs); + resultHi = DRFLAC_CACHE_L1_SELECT_AND_SHIFT(bs, riceParam); + if (bs->nextL2Line < DRFLAC_CACHE_L2_LINE_COUNT(bs)) { +#ifndef DR_FLAC_NO_CRC + drflac__update_crc16(bs); +#endif + bs->cache = drflac__be2host__cache_line(bs->cacheL2[bs->nextL2Line++]); bs->consumedBits = 0; -#ifndef MA_DR_FLAC_NO_CRC +#ifndef DR_FLAC_NO_CRC bs->crc16Cache = bs->cache; #endif } else { - if (!ma_dr_flac__reload_cache(bs)) { - return MA_FALSE; + if (!drflac__reload_cache(bs)) { + return DRFLAC_FALSE; } - if (bitCountLo > MA_DR_FLAC_CACHE_L1_BITS_REMAINING(bs)) { - return MA_FALSE; + if (bitCountLo > DRFLAC_CACHE_L1_BITS_REMAINING(bs)) { + return DRFLAC_FALSE; } } - riceParamPart = (ma_uint32)(resultHi | MA_DR_FLAC_CACHE_L1_SELECT_AND_SHIFT_SAFE(bs, bitCountLo)); + riceParamPart = (drflac_uint32)(resultHi | DRFLAC_CACHE_L1_SELECT_AND_SHIFT_SAFE(bs, bitCountLo)); bs->consumedBits += bitCountLo; bs->cache <<= bitCountLo; } pZeroCounterOut[0] = zeroCounter; pRiceParamPartOut[0] = riceParamPart; - return MA_TRUE; + return DRFLAC_TRUE; } #endif -static MA_INLINE ma_bool32 ma_dr_flac__read_rice_parts_x1(ma_dr_flac_bs* bs, ma_uint8 riceParam, ma_uint32* pZeroCounterOut, ma_uint32* pRiceParamPartOut) +static DRFLAC_INLINE drflac_bool32 drflac__read_rice_parts_x1(drflac_bs* bs, drflac_uint8 riceParam, drflac_uint32* pZeroCounterOut, drflac_uint32* pRiceParamPartOut) { - ma_uint32 riceParamPlus1 = riceParam + 1; - ma_uint32 riceParamPlus1Shift = MA_DR_FLAC_CACHE_L1_SELECTION_SHIFT(bs, riceParamPlus1); - ma_uint32 riceParamPlus1MaxConsumedBits = MA_DR_FLAC_CACHE_L1_SIZE_BITS(bs) - riceParamPlus1; - ma_dr_flac_cache_t bs_cache = bs->cache; - ma_uint32 bs_consumedBits = bs->consumedBits; - ma_uint32 lzcount = ma_dr_flac__clz(bs_cache); + drflac_uint32 riceParamPlus1 = riceParam + 1; + drflac_uint32 riceParamPlus1Shift = DRFLAC_CACHE_L1_SELECTION_SHIFT(bs, riceParamPlus1); + drflac_uint32 riceParamPlus1MaxConsumedBits = DRFLAC_CACHE_L1_SIZE_BITS(bs) - riceParamPlus1; + drflac_cache_t bs_cache = bs->cache; + drflac_uint32 bs_consumedBits = bs->consumedBits; + drflac_uint32 lzcount = drflac__clz(bs_cache); if (lzcount < sizeof(bs_cache)*8) { pZeroCounterOut[0] = lzcount; extract_rice_param_part: bs_cache <<= lzcount; bs_consumedBits += lzcount; if (bs_consumedBits <= riceParamPlus1MaxConsumedBits) { - pRiceParamPartOut[0] = (ma_uint32)(bs_cache >> riceParamPlus1Shift); + pRiceParamPartOut[0] = (drflac_uint32)(bs_cache >> riceParamPlus1Shift); bs_cache <<= riceParamPlus1; bs_consumedBits += riceParamPlus1; } else { - ma_uint32 riceParamPartHi; - ma_uint32 riceParamPartLo; - ma_uint32 riceParamPartLoBitCount; - riceParamPartHi = (ma_uint32)(bs_cache >> riceParamPlus1Shift); + drflac_uint32 riceParamPartHi; + drflac_uint32 riceParamPartLo; + drflac_uint32 riceParamPartLoBitCount; + riceParamPartHi = (drflac_uint32)(bs_cache >> riceParamPlus1Shift); riceParamPartLoBitCount = bs_consumedBits - riceParamPlus1MaxConsumedBits; - MA_DR_FLAC_ASSERT(riceParamPartLoBitCount > 0 && riceParamPartLoBitCount < 32); - if (bs->nextL2Line < MA_DR_FLAC_CACHE_L2_LINE_COUNT(bs)) { - #ifndef MA_DR_FLAC_NO_CRC - ma_dr_flac__update_crc16(bs); + DRFLAC_ASSERT(riceParamPartLoBitCount > 0 && riceParamPartLoBitCount < 32); + if (bs->nextL2Line < DRFLAC_CACHE_L2_LINE_COUNT(bs)) { + #ifndef DR_FLAC_NO_CRC + drflac__update_crc16(bs); #endif - bs_cache = ma_dr_flac__be2host__cache_line(bs->cacheL2[bs->nextL2Line++]); + bs_cache = drflac__be2host__cache_line(bs->cacheL2[bs->nextL2Line++]); bs_consumedBits = riceParamPartLoBitCount; - #ifndef MA_DR_FLAC_NO_CRC + #ifndef DR_FLAC_NO_CRC bs->crc16Cache = bs_cache; #endif } else { - if (!ma_dr_flac__reload_cache(bs)) { - return MA_FALSE; + if (!drflac__reload_cache(bs)) { + return DRFLAC_FALSE; } - if (riceParamPartLoBitCount > MA_DR_FLAC_CACHE_L1_BITS_REMAINING(bs)) { - return MA_FALSE; + if (riceParamPartLoBitCount > DRFLAC_CACHE_L1_BITS_REMAINING(bs)) { + return DRFLAC_FALSE; } bs_cache = bs->cache; bs_consumedBits = bs->consumedBits + riceParamPartLoBitCount; } - riceParamPartLo = (ma_uint32)(bs_cache >> (MA_DR_FLAC_CACHE_L1_SELECTION_SHIFT(bs, riceParamPartLoBitCount))); + riceParamPartLo = (drflac_uint32)(bs_cache >> (DRFLAC_CACHE_L1_SELECTION_SHIFT(bs, riceParamPartLoBitCount))); pRiceParamPartOut[0] = riceParamPartHi | riceParamPartLo; bs_cache <<= riceParamPartLoBitCount; } } else { - ma_uint32 zeroCounter = (ma_uint32)(MA_DR_FLAC_CACHE_L1_SIZE_BITS(bs) - bs_consumedBits); + drflac_uint32 zeroCounter = (drflac_uint32)(DRFLAC_CACHE_L1_SIZE_BITS(bs) - bs_consumedBits); for (;;) { - if (bs->nextL2Line < MA_DR_FLAC_CACHE_L2_LINE_COUNT(bs)) { - #ifndef MA_DR_FLAC_NO_CRC - ma_dr_flac__update_crc16(bs); + if (bs->nextL2Line < DRFLAC_CACHE_L2_LINE_COUNT(bs)) { + #ifndef DR_FLAC_NO_CRC + drflac__update_crc16(bs); #endif - bs_cache = ma_dr_flac__be2host__cache_line(bs->cacheL2[bs->nextL2Line++]); + bs_cache = drflac__be2host__cache_line(bs->cacheL2[bs->nextL2Line++]); bs_consumedBits = 0; - #ifndef MA_DR_FLAC_NO_CRC + #ifndef DR_FLAC_NO_CRC bs->crc16Cache = bs_cache; #endif } else { - if (!ma_dr_flac__reload_cache(bs)) { - return MA_FALSE; + if (!drflac__reload_cache(bs)) { + return DRFLAC_FALSE; } bs_cache = bs->cache; bs_consumedBits = bs->consumedBits; } - lzcount = ma_dr_flac__clz(bs_cache); + lzcount = drflac__clz(bs_cache); zeroCounter += lzcount; if (lzcount < sizeof(bs_cache)*8) { break; @@ -84612,15 +83490,15 @@ static MA_INLINE ma_bool32 ma_dr_flac__read_rice_parts_x1(ma_dr_flac_bs* bs, ma_ } bs->cache = bs_cache; bs->consumedBits = bs_consumedBits; - return MA_TRUE; + return DRFLAC_TRUE; } -static MA_INLINE ma_bool32 ma_dr_flac__seek_rice_parts(ma_dr_flac_bs* bs, ma_uint8 riceParam) +static DRFLAC_INLINE drflac_bool32 drflac__seek_rice_parts(drflac_bs* bs, drflac_uint8 riceParam) { - ma_uint32 riceParamPlus1 = riceParam + 1; - ma_uint32 riceParamPlus1MaxConsumedBits = MA_DR_FLAC_CACHE_L1_SIZE_BITS(bs) - riceParamPlus1; - ma_dr_flac_cache_t bs_cache = bs->cache; - ma_uint32 bs_consumedBits = bs->consumedBits; - ma_uint32 lzcount = ma_dr_flac__clz(bs_cache); + drflac_uint32 riceParamPlus1 = riceParam + 1; + drflac_uint32 riceParamPlus1MaxConsumedBits = DRFLAC_CACHE_L1_SIZE_BITS(bs) - riceParamPlus1; + drflac_cache_t bs_cache = bs->cache; + drflac_uint32 bs_consumedBits = bs->consumedBits; + drflac_uint32 lzcount = drflac__clz(bs_cache); if (lzcount < sizeof(bs_cache)*8) { extract_rice_param_part: bs_cache <<= lzcount; @@ -84629,23 +83507,23 @@ static MA_INLINE ma_bool32 ma_dr_flac__seek_rice_parts(ma_dr_flac_bs* bs, ma_uin bs_cache <<= riceParamPlus1; bs_consumedBits += riceParamPlus1; } else { - ma_uint32 riceParamPartLoBitCount = bs_consumedBits - riceParamPlus1MaxConsumedBits; - MA_DR_FLAC_ASSERT(riceParamPartLoBitCount > 0 && riceParamPartLoBitCount < 32); - if (bs->nextL2Line < MA_DR_FLAC_CACHE_L2_LINE_COUNT(bs)) { - #ifndef MA_DR_FLAC_NO_CRC - ma_dr_flac__update_crc16(bs); + drflac_uint32 riceParamPartLoBitCount = bs_consumedBits - riceParamPlus1MaxConsumedBits; + DRFLAC_ASSERT(riceParamPartLoBitCount > 0 && riceParamPartLoBitCount < 32); + if (bs->nextL2Line < DRFLAC_CACHE_L2_LINE_COUNT(bs)) { + #ifndef DR_FLAC_NO_CRC + drflac__update_crc16(bs); #endif - bs_cache = ma_dr_flac__be2host__cache_line(bs->cacheL2[bs->nextL2Line++]); + bs_cache = drflac__be2host__cache_line(bs->cacheL2[bs->nextL2Line++]); bs_consumedBits = riceParamPartLoBitCount; - #ifndef MA_DR_FLAC_NO_CRC + #ifndef DR_FLAC_NO_CRC bs->crc16Cache = bs_cache; #endif } else { - if (!ma_dr_flac__reload_cache(bs)) { - return MA_FALSE; + if (!drflac__reload_cache(bs)) { + return DRFLAC_FALSE; } - if (riceParamPartLoBitCount > MA_DR_FLAC_CACHE_L1_BITS_REMAINING(bs)) { - return MA_FALSE; + if (riceParamPartLoBitCount > DRFLAC_CACHE_L1_BITS_REMAINING(bs)) { + return DRFLAC_FALSE; } bs_cache = bs->cache; bs_consumedBits = bs->consumedBits + riceParamPartLoBitCount; @@ -84654,23 +83532,23 @@ static MA_INLINE ma_bool32 ma_dr_flac__seek_rice_parts(ma_dr_flac_bs* bs, ma_uin } } else { for (;;) { - if (bs->nextL2Line < MA_DR_FLAC_CACHE_L2_LINE_COUNT(bs)) { - #ifndef MA_DR_FLAC_NO_CRC - ma_dr_flac__update_crc16(bs); + if (bs->nextL2Line < DRFLAC_CACHE_L2_LINE_COUNT(bs)) { + #ifndef DR_FLAC_NO_CRC + drflac__update_crc16(bs); #endif - bs_cache = ma_dr_flac__be2host__cache_line(bs->cacheL2[bs->nextL2Line++]); + bs_cache = drflac__be2host__cache_line(bs->cacheL2[bs->nextL2Line++]); bs_consumedBits = 0; - #ifndef MA_DR_FLAC_NO_CRC + #ifndef DR_FLAC_NO_CRC bs->crc16Cache = bs_cache; #endif } else { - if (!ma_dr_flac__reload_cache(bs)) { - return MA_FALSE; + if (!drflac__reload_cache(bs)) { + return DRFLAC_FALSE; } bs_cache = bs->cache; bs_consumedBits = bs->consumedBits; } - lzcount = ma_dr_flac__clz(bs_cache); + lzcount = drflac__clz(bs_cache); if (lzcount < sizeof(bs_cache)*8) { break; } @@ -84679,26 +83557,26 @@ static MA_INLINE ma_bool32 ma_dr_flac__seek_rice_parts(ma_dr_flac_bs* bs, ma_uin } bs->cache = bs_cache; bs->consumedBits = bs_consumedBits; - return MA_TRUE; + return DRFLAC_TRUE; } -static ma_bool32 ma_dr_flac__decode_samples_with_residual__rice__scalar_zeroorder(ma_dr_flac_bs* bs, ma_uint32 bitsPerSample, ma_uint32 count, ma_uint8 riceParam, ma_uint32 order, ma_int32 shift, const ma_int32* coefficients, ma_int32* pSamplesOut) +static drflac_bool32 drflac__decode_samples_with_residual__rice__scalar_zeroorder(drflac_bs* bs, drflac_uint32 bitsPerSample, drflac_uint32 count, drflac_uint8 riceParam, drflac_uint32 order, drflac_int32 shift, const drflac_int32* coefficients, drflac_int32* pSamplesOut) { - ma_uint32 t[2] = {0x00000000, 0xFFFFFFFF}; - ma_uint32 zeroCountPart0; - ma_uint32 riceParamPart0; - ma_uint32 riceParamMask; - ma_uint32 i; - MA_DR_FLAC_ASSERT(bs != NULL); - MA_DR_FLAC_ASSERT(pSamplesOut != NULL); + drflac_uint32 t[2] = {0x00000000, 0xFFFFFFFF}; + drflac_uint32 zeroCountPart0; + drflac_uint32 riceParamPart0; + drflac_uint32 riceParamMask; + drflac_uint32 i; + DRFLAC_ASSERT(bs != NULL); + DRFLAC_ASSERT(pSamplesOut != NULL); (void)bitsPerSample; (void)order; (void)shift; (void)coefficients; - riceParamMask = (ma_uint32)~((~0UL) << riceParam); + riceParamMask = (drflac_uint32)~((~0UL) << riceParam); i = 0; while (i < count) { - if (!ma_dr_flac__read_rice_parts_x1(bs, riceParam, &zeroCountPart0, &riceParamPart0)) { - return MA_FALSE; + if (!drflac__read_rice_parts_x1(bs, riceParam, &zeroCountPart0, &riceParamPart0)) { + return DRFLAC_FALSE; } riceParamPart0 &= riceParamMask; riceParamPart0 |= (zeroCountPart0 << riceParam); @@ -84706,36 +83584,36 @@ static ma_bool32 ma_dr_flac__decode_samples_with_residual__rice__scalar_zeroorde pSamplesOut[i] = riceParamPart0; i += 1; } - return MA_TRUE; -} -static ma_bool32 ma_dr_flac__decode_samples_with_residual__rice__scalar(ma_dr_flac_bs* bs, ma_uint32 bitsPerSample, ma_uint32 count, ma_uint8 riceParam, ma_uint32 lpcOrder, ma_int32 lpcShift, ma_uint32 lpcPrecision, const ma_int32* coefficients, ma_int32* pSamplesOut) -{ - ma_uint32 t[2] = {0x00000000, 0xFFFFFFFF}; - ma_uint32 zeroCountPart0 = 0; - ma_uint32 zeroCountPart1 = 0; - ma_uint32 zeroCountPart2 = 0; - ma_uint32 zeroCountPart3 = 0; - ma_uint32 riceParamPart0 = 0; - ma_uint32 riceParamPart1 = 0; - ma_uint32 riceParamPart2 = 0; - ma_uint32 riceParamPart3 = 0; - ma_uint32 riceParamMask; - const ma_int32* pSamplesOutEnd; - ma_uint32 i; - MA_DR_FLAC_ASSERT(bs != NULL); - MA_DR_FLAC_ASSERT(pSamplesOut != NULL); + return DRFLAC_TRUE; +} +static drflac_bool32 drflac__decode_samples_with_residual__rice__scalar(drflac_bs* bs, drflac_uint32 bitsPerSample, drflac_uint32 count, drflac_uint8 riceParam, drflac_uint32 lpcOrder, drflac_int32 lpcShift, drflac_uint32 lpcPrecision, const drflac_int32* coefficients, drflac_int32* pSamplesOut) +{ + drflac_uint32 t[2] = {0x00000000, 0xFFFFFFFF}; + drflac_uint32 zeroCountPart0 = 0; + drflac_uint32 zeroCountPart1 = 0; + drflac_uint32 zeroCountPart2 = 0; + drflac_uint32 zeroCountPart3 = 0; + drflac_uint32 riceParamPart0 = 0; + drflac_uint32 riceParamPart1 = 0; + drflac_uint32 riceParamPart2 = 0; + drflac_uint32 riceParamPart3 = 0; + drflac_uint32 riceParamMask; + const drflac_int32* pSamplesOutEnd; + drflac_uint32 i; + DRFLAC_ASSERT(bs != NULL); + DRFLAC_ASSERT(pSamplesOut != NULL); if (lpcOrder == 0) { - return ma_dr_flac__decode_samples_with_residual__rice__scalar_zeroorder(bs, bitsPerSample, count, riceParam, lpcOrder, lpcShift, coefficients, pSamplesOut); + return drflac__decode_samples_with_residual__rice__scalar_zeroorder(bs, bitsPerSample, count, riceParam, lpcOrder, lpcShift, coefficients, pSamplesOut); } - riceParamMask = (ma_uint32)~((~0UL) << riceParam); + riceParamMask = (drflac_uint32)~((~0UL) << riceParam); pSamplesOutEnd = pSamplesOut + (count & ~3); - if (ma_dr_flac__use_64_bit_prediction(bitsPerSample, lpcOrder, lpcPrecision)) { + if (drflac__use_64_bit_prediction(bitsPerSample, lpcOrder, lpcPrecision)) { while (pSamplesOut < pSamplesOutEnd) { - if (!ma_dr_flac__read_rice_parts_x1(bs, riceParam, &zeroCountPart0, &riceParamPart0) || - !ma_dr_flac__read_rice_parts_x1(bs, riceParam, &zeroCountPart1, &riceParamPart1) || - !ma_dr_flac__read_rice_parts_x1(bs, riceParam, &zeroCountPart2, &riceParamPart2) || - !ma_dr_flac__read_rice_parts_x1(bs, riceParam, &zeroCountPart3, &riceParamPart3)) { - return MA_FALSE; + if (!drflac__read_rice_parts_x1(bs, riceParam, &zeroCountPart0, &riceParamPart0) || + !drflac__read_rice_parts_x1(bs, riceParam, &zeroCountPart1, &riceParamPart1) || + !drflac__read_rice_parts_x1(bs, riceParam, &zeroCountPart2, &riceParamPart2) || + !drflac__read_rice_parts_x1(bs, riceParam, &zeroCountPart3, &riceParamPart3)) { + return DRFLAC_FALSE; } riceParamPart0 &= riceParamMask; riceParamPart1 &= riceParamMask; @@ -84749,19 +83627,19 @@ static ma_bool32 ma_dr_flac__decode_samples_with_residual__rice__scalar(ma_dr_fl riceParamPart1 = (riceParamPart1 >> 1) ^ t[riceParamPart1 & 0x01]; riceParamPart2 = (riceParamPart2 >> 1) ^ t[riceParamPart2 & 0x01]; riceParamPart3 = (riceParamPart3 >> 1) ^ t[riceParamPart3 & 0x01]; - pSamplesOut[0] = riceParamPart0 + ma_dr_flac__calculate_prediction_64(lpcOrder, lpcShift, coefficients, pSamplesOut + 0); - pSamplesOut[1] = riceParamPart1 + ma_dr_flac__calculate_prediction_64(lpcOrder, lpcShift, coefficients, pSamplesOut + 1); - pSamplesOut[2] = riceParamPart2 + ma_dr_flac__calculate_prediction_64(lpcOrder, lpcShift, coefficients, pSamplesOut + 2); - pSamplesOut[3] = riceParamPart3 + ma_dr_flac__calculate_prediction_64(lpcOrder, lpcShift, coefficients, pSamplesOut + 3); + pSamplesOut[0] = riceParamPart0 + drflac__calculate_prediction_64(lpcOrder, lpcShift, coefficients, pSamplesOut + 0); + pSamplesOut[1] = riceParamPart1 + drflac__calculate_prediction_64(lpcOrder, lpcShift, coefficients, pSamplesOut + 1); + pSamplesOut[2] = riceParamPart2 + drflac__calculate_prediction_64(lpcOrder, lpcShift, coefficients, pSamplesOut + 2); + pSamplesOut[3] = riceParamPart3 + drflac__calculate_prediction_64(lpcOrder, lpcShift, coefficients, pSamplesOut + 3); pSamplesOut += 4; } } else { while (pSamplesOut < pSamplesOutEnd) { - if (!ma_dr_flac__read_rice_parts_x1(bs, riceParam, &zeroCountPart0, &riceParamPart0) || - !ma_dr_flac__read_rice_parts_x1(bs, riceParam, &zeroCountPart1, &riceParamPart1) || - !ma_dr_flac__read_rice_parts_x1(bs, riceParam, &zeroCountPart2, &riceParamPart2) || - !ma_dr_flac__read_rice_parts_x1(bs, riceParam, &zeroCountPart3, &riceParamPart3)) { - return MA_FALSE; + if (!drflac__read_rice_parts_x1(bs, riceParam, &zeroCountPart0, &riceParamPart0) || + !drflac__read_rice_parts_x1(bs, riceParam, &zeroCountPart1, &riceParamPart1) || + !drflac__read_rice_parts_x1(bs, riceParam, &zeroCountPart2, &riceParamPart2) || + !drflac__read_rice_parts_x1(bs, riceParam, &zeroCountPart3, &riceParamPart3)) { + return DRFLAC_FALSE; } riceParamPart0 &= riceParamMask; riceParamPart1 &= riceParamMask; @@ -84775,33 +83653,33 @@ static ma_bool32 ma_dr_flac__decode_samples_with_residual__rice__scalar(ma_dr_fl riceParamPart1 = (riceParamPart1 >> 1) ^ t[riceParamPart1 & 0x01]; riceParamPart2 = (riceParamPart2 >> 1) ^ t[riceParamPart2 & 0x01]; riceParamPart3 = (riceParamPart3 >> 1) ^ t[riceParamPart3 & 0x01]; - pSamplesOut[0] = riceParamPart0 + ma_dr_flac__calculate_prediction_32(lpcOrder, lpcShift, coefficients, pSamplesOut + 0); - pSamplesOut[1] = riceParamPart1 + ma_dr_flac__calculate_prediction_32(lpcOrder, lpcShift, coefficients, pSamplesOut + 1); - pSamplesOut[2] = riceParamPart2 + ma_dr_flac__calculate_prediction_32(lpcOrder, lpcShift, coefficients, pSamplesOut + 2); - pSamplesOut[3] = riceParamPart3 + ma_dr_flac__calculate_prediction_32(lpcOrder, lpcShift, coefficients, pSamplesOut + 3); + pSamplesOut[0] = riceParamPart0 + drflac__calculate_prediction_32(lpcOrder, lpcShift, coefficients, pSamplesOut + 0); + pSamplesOut[1] = riceParamPart1 + drflac__calculate_prediction_32(lpcOrder, lpcShift, coefficients, pSamplesOut + 1); + pSamplesOut[2] = riceParamPart2 + drflac__calculate_prediction_32(lpcOrder, lpcShift, coefficients, pSamplesOut + 2); + pSamplesOut[3] = riceParamPart3 + drflac__calculate_prediction_32(lpcOrder, lpcShift, coefficients, pSamplesOut + 3); pSamplesOut += 4; } } i = (count & ~3); while (i < count) { - if (!ma_dr_flac__read_rice_parts_x1(bs, riceParam, &zeroCountPart0, &riceParamPart0)) { - return MA_FALSE; + if (!drflac__read_rice_parts_x1(bs, riceParam, &zeroCountPart0, &riceParamPart0)) { + return DRFLAC_FALSE; } riceParamPart0 &= riceParamMask; riceParamPart0 |= (zeroCountPart0 << riceParam); riceParamPart0 = (riceParamPart0 >> 1) ^ t[riceParamPart0 & 0x01]; - if (ma_dr_flac__use_64_bit_prediction(bitsPerSample, lpcOrder, lpcPrecision)) { - pSamplesOut[0] = riceParamPart0 + ma_dr_flac__calculate_prediction_64(lpcOrder, lpcShift, coefficients, pSamplesOut + 0); + if (drflac__use_64_bit_prediction(bitsPerSample, lpcOrder, lpcPrecision)) { + pSamplesOut[0] = riceParamPart0 + drflac__calculate_prediction_64(lpcOrder, lpcShift, coefficients, pSamplesOut + 0); } else { - pSamplesOut[0] = riceParamPart0 + ma_dr_flac__calculate_prediction_32(lpcOrder, lpcShift, coefficients, pSamplesOut + 0); + pSamplesOut[0] = riceParamPart0 + drflac__calculate_prediction_32(lpcOrder, lpcShift, coefficients, pSamplesOut + 0); } i += 1; pSamplesOut += 1; } - return MA_TRUE; + return DRFLAC_TRUE; } -#if defined(MA_DR_FLAC_SUPPORT_SSE2) -static MA_INLINE __m128i ma_dr_flac__mm_packs_interleaved_epi32(__m128i a, __m128i b) +#if defined(DRFLAC_SUPPORT_SSE2) +static DRFLAC_INLINE __m128i drflac__mm_packs_interleaved_epi32(__m128i a, __m128i b) { __m128i r; r = _mm_packs_epi32(a, b); @@ -84811,42 +83689,42 @@ static MA_INLINE __m128i ma_dr_flac__mm_packs_interleaved_epi32(__m128i a, __m12 return r; } #endif -#if defined(MA_DR_FLAC_SUPPORT_SSE41) -static MA_INLINE __m128i ma_dr_flac__mm_not_si128(__m128i a) +#if defined(DRFLAC_SUPPORT_SSE41) +static DRFLAC_INLINE __m128i drflac__mm_not_si128(__m128i a) { return _mm_xor_si128(a, _mm_cmpeq_epi32(_mm_setzero_si128(), _mm_setzero_si128())); } -static MA_INLINE __m128i ma_dr_flac__mm_hadd_epi32(__m128i x) +static DRFLAC_INLINE __m128i drflac__mm_hadd_epi32(__m128i x) { __m128i x64 = _mm_add_epi32(x, _mm_shuffle_epi32(x, _MM_SHUFFLE(1, 0, 3, 2))); __m128i x32 = _mm_shufflelo_epi16(x64, _MM_SHUFFLE(1, 0, 3, 2)); return _mm_add_epi32(x64, x32); } -static MA_INLINE __m128i ma_dr_flac__mm_hadd_epi64(__m128i x) +static DRFLAC_INLINE __m128i drflac__mm_hadd_epi64(__m128i x) { return _mm_add_epi64(x, _mm_shuffle_epi32(x, _MM_SHUFFLE(1, 0, 3, 2))); } -static MA_INLINE __m128i ma_dr_flac__mm_srai_epi64(__m128i x, int count) +static DRFLAC_INLINE __m128i drflac__mm_srai_epi64(__m128i x, int count) { __m128i lo = _mm_srli_epi64(x, count); __m128i hi = _mm_srai_epi32(x, count); hi = _mm_and_si128(hi, _mm_set_epi32(0xFFFFFFFF, 0, 0xFFFFFFFF, 0)); return _mm_or_si128(lo, hi); } -static ma_bool32 ma_dr_flac__decode_samples_with_residual__rice__sse41_32(ma_dr_flac_bs* bs, ma_uint32 count, ma_uint8 riceParam, ma_uint32 order, ma_int32 shift, const ma_int32* coefficients, ma_int32* pSamplesOut) +static drflac_bool32 drflac__decode_samples_with_residual__rice__sse41_32(drflac_bs* bs, drflac_uint32 count, drflac_uint8 riceParam, drflac_uint32 order, drflac_int32 shift, const drflac_int32* coefficients, drflac_int32* pSamplesOut) { int i; - ma_uint32 riceParamMask; - ma_int32* pDecodedSamples = pSamplesOut; - ma_int32* pDecodedSamplesEnd = pSamplesOut + (count & ~3); - ma_uint32 zeroCountParts0 = 0; - ma_uint32 zeroCountParts1 = 0; - ma_uint32 zeroCountParts2 = 0; - ma_uint32 zeroCountParts3 = 0; - ma_uint32 riceParamParts0 = 0; - ma_uint32 riceParamParts1 = 0; - ma_uint32 riceParamParts2 = 0; - ma_uint32 riceParamParts3 = 0; + drflac_uint32 riceParamMask; + drflac_int32* pDecodedSamples = pSamplesOut; + drflac_int32* pDecodedSamplesEnd = pSamplesOut + (count & ~3); + drflac_uint32 zeroCountParts0 = 0; + drflac_uint32 zeroCountParts1 = 0; + drflac_uint32 zeroCountParts2 = 0; + drflac_uint32 zeroCountParts3 = 0; + drflac_uint32 riceParamParts0 = 0; + drflac_uint32 riceParamParts1 = 0; + drflac_uint32 riceParamParts2 = 0; + drflac_uint32 riceParamParts3 = 0; __m128i coefficients128_0; __m128i coefficients128_4; __m128i coefficients128_8; @@ -84854,8 +83732,8 @@ static ma_bool32 ma_dr_flac__decode_samples_with_residual__rice__sse41_32(ma_dr_ __m128i samples128_4; __m128i samples128_8; __m128i riceParamMask128; - const ma_uint32 t[2] = {0x00000000, 0xFFFFFFFF}; - riceParamMask = (ma_uint32)~((~0UL) << riceParam); + const drflac_uint32 t[2] = {0x00000000, 0xFFFFFFFF}; + riceParamMask = (drflac_uint32)~((~0UL) << riceParam); riceParamMask128 = _mm_set1_epi32(riceParamMask); coefficients128_0 = _mm_setzero_si128(); coefficients128_4 = _mm_setzero_si128(); @@ -84909,39 +83787,39 @@ static ma_bool32 ma_dr_flac__decode_samples_with_residual__rice__sse41_32(ma_dr_ #else switch (order) { - case 12: ((ma_int32*)&coefficients128_8)[0] = coefficients[11]; ((ma_int32*)&samples128_8)[0] = pDecodedSamples[-12]; - case 11: ((ma_int32*)&coefficients128_8)[1] = coefficients[10]; ((ma_int32*)&samples128_8)[1] = pDecodedSamples[-11]; - case 10: ((ma_int32*)&coefficients128_8)[2] = coefficients[ 9]; ((ma_int32*)&samples128_8)[2] = pDecodedSamples[-10]; - case 9: ((ma_int32*)&coefficients128_8)[3] = coefficients[ 8]; ((ma_int32*)&samples128_8)[3] = pDecodedSamples[- 9]; - case 8: ((ma_int32*)&coefficients128_4)[0] = coefficients[ 7]; ((ma_int32*)&samples128_4)[0] = pDecodedSamples[- 8]; - case 7: ((ma_int32*)&coefficients128_4)[1] = coefficients[ 6]; ((ma_int32*)&samples128_4)[1] = pDecodedSamples[- 7]; - case 6: ((ma_int32*)&coefficients128_4)[2] = coefficients[ 5]; ((ma_int32*)&samples128_4)[2] = pDecodedSamples[- 6]; - case 5: ((ma_int32*)&coefficients128_4)[3] = coefficients[ 4]; ((ma_int32*)&samples128_4)[3] = pDecodedSamples[- 5]; - case 4: ((ma_int32*)&coefficients128_0)[0] = coefficients[ 3]; ((ma_int32*)&samples128_0)[0] = pDecodedSamples[- 4]; - case 3: ((ma_int32*)&coefficients128_0)[1] = coefficients[ 2]; ((ma_int32*)&samples128_0)[1] = pDecodedSamples[- 3]; - case 2: ((ma_int32*)&coefficients128_0)[2] = coefficients[ 1]; ((ma_int32*)&samples128_0)[2] = pDecodedSamples[- 2]; - case 1: ((ma_int32*)&coefficients128_0)[3] = coefficients[ 0]; ((ma_int32*)&samples128_0)[3] = pDecodedSamples[- 1]; + case 12: ((drflac_int32*)&coefficients128_8)[0] = coefficients[11]; ((drflac_int32*)&samples128_8)[0] = pDecodedSamples[-12]; + case 11: ((drflac_int32*)&coefficients128_8)[1] = coefficients[10]; ((drflac_int32*)&samples128_8)[1] = pDecodedSamples[-11]; + case 10: ((drflac_int32*)&coefficients128_8)[2] = coefficients[ 9]; ((drflac_int32*)&samples128_8)[2] = pDecodedSamples[-10]; + case 9: ((drflac_int32*)&coefficients128_8)[3] = coefficients[ 8]; ((drflac_int32*)&samples128_8)[3] = pDecodedSamples[- 9]; + case 8: ((drflac_int32*)&coefficients128_4)[0] = coefficients[ 7]; ((drflac_int32*)&samples128_4)[0] = pDecodedSamples[- 8]; + case 7: ((drflac_int32*)&coefficients128_4)[1] = coefficients[ 6]; ((drflac_int32*)&samples128_4)[1] = pDecodedSamples[- 7]; + case 6: ((drflac_int32*)&coefficients128_4)[2] = coefficients[ 5]; ((drflac_int32*)&samples128_4)[2] = pDecodedSamples[- 6]; + case 5: ((drflac_int32*)&coefficients128_4)[3] = coefficients[ 4]; ((drflac_int32*)&samples128_4)[3] = pDecodedSamples[- 5]; + case 4: ((drflac_int32*)&coefficients128_0)[0] = coefficients[ 3]; ((drflac_int32*)&samples128_0)[0] = pDecodedSamples[- 4]; + case 3: ((drflac_int32*)&coefficients128_0)[1] = coefficients[ 2]; ((drflac_int32*)&samples128_0)[1] = pDecodedSamples[- 3]; + case 2: ((drflac_int32*)&coefficients128_0)[2] = coefficients[ 1]; ((drflac_int32*)&samples128_0)[2] = pDecodedSamples[- 2]; + case 1: ((drflac_int32*)&coefficients128_0)[3] = coefficients[ 0]; ((drflac_int32*)&samples128_0)[3] = pDecodedSamples[- 1]; } #endif while (pDecodedSamples < pDecodedSamplesEnd) { __m128i prediction128; __m128i zeroCountPart128; __m128i riceParamPart128; - if (!ma_dr_flac__read_rice_parts_x1(bs, riceParam, &zeroCountParts0, &riceParamParts0) || - !ma_dr_flac__read_rice_parts_x1(bs, riceParam, &zeroCountParts1, &riceParamParts1) || - !ma_dr_flac__read_rice_parts_x1(bs, riceParam, &zeroCountParts2, &riceParamParts2) || - !ma_dr_flac__read_rice_parts_x1(bs, riceParam, &zeroCountParts3, &riceParamParts3)) { - return MA_FALSE; + if (!drflac__read_rice_parts_x1(bs, riceParam, &zeroCountParts0, &riceParamParts0) || + !drflac__read_rice_parts_x1(bs, riceParam, &zeroCountParts1, &riceParamParts1) || + !drflac__read_rice_parts_x1(bs, riceParam, &zeroCountParts2, &riceParamParts2) || + !drflac__read_rice_parts_x1(bs, riceParam, &zeroCountParts3, &riceParamParts3)) { + return DRFLAC_FALSE; } zeroCountPart128 = _mm_set_epi32(zeroCountParts3, zeroCountParts2, zeroCountParts1, zeroCountParts0); riceParamPart128 = _mm_set_epi32(riceParamParts3, riceParamParts2, riceParamParts1, riceParamParts0); riceParamPart128 = _mm_and_si128(riceParamPart128, riceParamMask128); riceParamPart128 = _mm_or_si128(riceParamPart128, _mm_slli_epi32(zeroCountPart128, riceParam)); - riceParamPart128 = _mm_xor_si128(_mm_srli_epi32(riceParamPart128, 1), _mm_add_epi32(ma_dr_flac__mm_not_si128(_mm_and_si128(riceParamPart128, _mm_set1_epi32(0x01))), _mm_set1_epi32(0x01))); + riceParamPart128 = _mm_xor_si128(_mm_srli_epi32(riceParamPart128, 1), _mm_add_epi32(drflac__mm_not_si128(_mm_and_si128(riceParamPart128, _mm_set1_epi32(0x01))), _mm_set1_epi32(0x01))); if (order <= 4) { for (i = 0; i < 4; i += 1) { prediction128 = _mm_mullo_epi32(coefficients128_0, samples128_0); - prediction128 = ma_dr_flac__mm_hadd_epi32(prediction128); + prediction128 = drflac__mm_hadd_epi32(prediction128); prediction128 = _mm_srai_epi32(prediction128, shift); prediction128 = _mm_add_epi32(riceParamPart128, prediction128); samples128_0 = _mm_alignr_epi8(prediction128, samples128_0, 4); @@ -84951,7 +83829,7 @@ static ma_bool32 ma_dr_flac__decode_samples_with_residual__rice__sse41_32(ma_dr_ for (i = 0; i < 4; i += 1) { prediction128 = _mm_mullo_epi32(coefficients128_4, samples128_4); prediction128 = _mm_add_epi32(prediction128, _mm_mullo_epi32(coefficients128_0, samples128_0)); - prediction128 = ma_dr_flac__mm_hadd_epi32(prediction128); + prediction128 = drflac__mm_hadd_epi32(prediction128); prediction128 = _mm_srai_epi32(prediction128, shift); prediction128 = _mm_add_epi32(riceParamPart128, prediction128); samples128_4 = _mm_alignr_epi8(samples128_0, samples128_4, 4); @@ -84963,7 +83841,7 @@ static ma_bool32 ma_dr_flac__decode_samples_with_residual__rice__sse41_32(ma_dr_ prediction128 = _mm_mullo_epi32(coefficients128_8, samples128_8); prediction128 = _mm_add_epi32(prediction128, _mm_mullo_epi32(coefficients128_4, samples128_4)); prediction128 = _mm_add_epi32(prediction128, _mm_mullo_epi32(coefficients128_0, samples128_0)); - prediction128 = ma_dr_flac__mm_hadd_epi32(prediction128); + prediction128 = drflac__mm_hadd_epi32(prediction128); prediction128 = _mm_srai_epi32(prediction128, shift); prediction128 = _mm_add_epi32(riceParamPart128, prediction128); samples128_8 = _mm_alignr_epi8(samples128_4, samples128_8, 4); @@ -84977,32 +83855,32 @@ static ma_bool32 ma_dr_flac__decode_samples_with_residual__rice__sse41_32(ma_dr_ } i = (count & ~3); while (i < (int)count) { - if (!ma_dr_flac__read_rice_parts_x1(bs, riceParam, &zeroCountParts0, &riceParamParts0)) { - return MA_FALSE; + if (!drflac__read_rice_parts_x1(bs, riceParam, &zeroCountParts0, &riceParamParts0)) { + return DRFLAC_FALSE; } riceParamParts0 &= riceParamMask; riceParamParts0 |= (zeroCountParts0 << riceParam); riceParamParts0 = (riceParamParts0 >> 1) ^ t[riceParamParts0 & 0x01]; - pDecodedSamples[0] = riceParamParts0 + ma_dr_flac__calculate_prediction_32(order, shift, coefficients, pDecodedSamples); + pDecodedSamples[0] = riceParamParts0 + drflac__calculate_prediction_32(order, shift, coefficients, pDecodedSamples); i += 1; pDecodedSamples += 1; } - return MA_TRUE; + return DRFLAC_TRUE; } -static ma_bool32 ma_dr_flac__decode_samples_with_residual__rice__sse41_64(ma_dr_flac_bs* bs, ma_uint32 count, ma_uint8 riceParam, ma_uint32 order, ma_int32 shift, const ma_int32* coefficients, ma_int32* pSamplesOut) +static drflac_bool32 drflac__decode_samples_with_residual__rice__sse41_64(drflac_bs* bs, drflac_uint32 count, drflac_uint8 riceParam, drflac_uint32 order, drflac_int32 shift, const drflac_int32* coefficients, drflac_int32* pSamplesOut) { int i; - ma_uint32 riceParamMask; - ma_int32* pDecodedSamples = pSamplesOut; - ma_int32* pDecodedSamplesEnd = pSamplesOut + (count & ~3); - ma_uint32 zeroCountParts0 = 0; - ma_uint32 zeroCountParts1 = 0; - ma_uint32 zeroCountParts2 = 0; - ma_uint32 zeroCountParts3 = 0; - ma_uint32 riceParamParts0 = 0; - ma_uint32 riceParamParts1 = 0; - ma_uint32 riceParamParts2 = 0; - ma_uint32 riceParamParts3 = 0; + drflac_uint32 riceParamMask; + drflac_int32* pDecodedSamples = pSamplesOut; + drflac_int32* pDecodedSamplesEnd = pSamplesOut + (count & ~3); + drflac_uint32 zeroCountParts0 = 0; + drflac_uint32 zeroCountParts1 = 0; + drflac_uint32 zeroCountParts2 = 0; + drflac_uint32 zeroCountParts3 = 0; + drflac_uint32 riceParamParts0 = 0; + drflac_uint32 riceParamParts1 = 0; + drflac_uint32 riceParamParts2 = 0; + drflac_uint32 riceParamParts3 = 0; __m128i coefficients128_0; __m128i coefficients128_4; __m128i coefficients128_8; @@ -85011,9 +83889,9 @@ static ma_bool32 ma_dr_flac__decode_samples_with_residual__rice__sse41_64(ma_dr_ __m128i samples128_8; __m128i prediction128; __m128i riceParamMask128; - const ma_uint32 t[2] = {0x00000000, 0xFFFFFFFF}; - MA_DR_FLAC_ASSERT(order <= 12); - riceParamMask = (ma_uint32)~((~0UL) << riceParam); + const drflac_uint32 t[2] = {0x00000000, 0xFFFFFFFF}; + DRFLAC_ASSERT(order <= 12); + riceParamMask = (drflac_uint32)~((~0UL) << riceParam); riceParamMask128 = _mm_set1_epi32(riceParamMask); prediction128 = _mm_setzero_si128(); coefficients128_0 = _mm_setzero_si128(); @@ -85068,34 +83946,34 @@ static ma_bool32 ma_dr_flac__decode_samples_with_residual__rice__sse41_64(ma_dr_ #else switch (order) { - case 12: ((ma_int32*)&coefficients128_8)[0] = coefficients[11]; ((ma_int32*)&samples128_8)[0] = pDecodedSamples[-12]; - case 11: ((ma_int32*)&coefficients128_8)[1] = coefficients[10]; ((ma_int32*)&samples128_8)[1] = pDecodedSamples[-11]; - case 10: ((ma_int32*)&coefficients128_8)[2] = coefficients[ 9]; ((ma_int32*)&samples128_8)[2] = pDecodedSamples[-10]; - case 9: ((ma_int32*)&coefficients128_8)[3] = coefficients[ 8]; ((ma_int32*)&samples128_8)[3] = pDecodedSamples[- 9]; - case 8: ((ma_int32*)&coefficients128_4)[0] = coefficients[ 7]; ((ma_int32*)&samples128_4)[0] = pDecodedSamples[- 8]; - case 7: ((ma_int32*)&coefficients128_4)[1] = coefficients[ 6]; ((ma_int32*)&samples128_4)[1] = pDecodedSamples[- 7]; - case 6: ((ma_int32*)&coefficients128_4)[2] = coefficients[ 5]; ((ma_int32*)&samples128_4)[2] = pDecodedSamples[- 6]; - case 5: ((ma_int32*)&coefficients128_4)[3] = coefficients[ 4]; ((ma_int32*)&samples128_4)[3] = pDecodedSamples[- 5]; - case 4: ((ma_int32*)&coefficients128_0)[0] = coefficients[ 3]; ((ma_int32*)&samples128_0)[0] = pDecodedSamples[- 4]; - case 3: ((ma_int32*)&coefficients128_0)[1] = coefficients[ 2]; ((ma_int32*)&samples128_0)[1] = pDecodedSamples[- 3]; - case 2: ((ma_int32*)&coefficients128_0)[2] = coefficients[ 1]; ((ma_int32*)&samples128_0)[2] = pDecodedSamples[- 2]; - case 1: ((ma_int32*)&coefficients128_0)[3] = coefficients[ 0]; ((ma_int32*)&samples128_0)[3] = pDecodedSamples[- 1]; + case 12: ((drflac_int32*)&coefficients128_8)[0] = coefficients[11]; ((drflac_int32*)&samples128_8)[0] = pDecodedSamples[-12]; + case 11: ((drflac_int32*)&coefficients128_8)[1] = coefficients[10]; ((drflac_int32*)&samples128_8)[1] = pDecodedSamples[-11]; + case 10: ((drflac_int32*)&coefficients128_8)[2] = coefficients[ 9]; ((drflac_int32*)&samples128_8)[2] = pDecodedSamples[-10]; + case 9: ((drflac_int32*)&coefficients128_8)[3] = coefficients[ 8]; ((drflac_int32*)&samples128_8)[3] = pDecodedSamples[- 9]; + case 8: ((drflac_int32*)&coefficients128_4)[0] = coefficients[ 7]; ((drflac_int32*)&samples128_4)[0] = pDecodedSamples[- 8]; + case 7: ((drflac_int32*)&coefficients128_4)[1] = coefficients[ 6]; ((drflac_int32*)&samples128_4)[1] = pDecodedSamples[- 7]; + case 6: ((drflac_int32*)&coefficients128_4)[2] = coefficients[ 5]; ((drflac_int32*)&samples128_4)[2] = pDecodedSamples[- 6]; + case 5: ((drflac_int32*)&coefficients128_4)[3] = coefficients[ 4]; ((drflac_int32*)&samples128_4)[3] = pDecodedSamples[- 5]; + case 4: ((drflac_int32*)&coefficients128_0)[0] = coefficients[ 3]; ((drflac_int32*)&samples128_0)[0] = pDecodedSamples[- 4]; + case 3: ((drflac_int32*)&coefficients128_0)[1] = coefficients[ 2]; ((drflac_int32*)&samples128_0)[1] = pDecodedSamples[- 3]; + case 2: ((drflac_int32*)&coefficients128_0)[2] = coefficients[ 1]; ((drflac_int32*)&samples128_0)[2] = pDecodedSamples[- 2]; + case 1: ((drflac_int32*)&coefficients128_0)[3] = coefficients[ 0]; ((drflac_int32*)&samples128_0)[3] = pDecodedSamples[- 1]; } #endif while (pDecodedSamples < pDecodedSamplesEnd) { __m128i zeroCountPart128; __m128i riceParamPart128; - if (!ma_dr_flac__read_rice_parts_x1(bs, riceParam, &zeroCountParts0, &riceParamParts0) || - !ma_dr_flac__read_rice_parts_x1(bs, riceParam, &zeroCountParts1, &riceParamParts1) || - !ma_dr_flac__read_rice_parts_x1(bs, riceParam, &zeroCountParts2, &riceParamParts2) || - !ma_dr_flac__read_rice_parts_x1(bs, riceParam, &zeroCountParts3, &riceParamParts3)) { - return MA_FALSE; + if (!drflac__read_rice_parts_x1(bs, riceParam, &zeroCountParts0, &riceParamParts0) || + !drflac__read_rice_parts_x1(bs, riceParam, &zeroCountParts1, &riceParamParts1) || + !drflac__read_rice_parts_x1(bs, riceParam, &zeroCountParts2, &riceParamParts2) || + !drflac__read_rice_parts_x1(bs, riceParam, &zeroCountParts3, &riceParamParts3)) { + return DRFLAC_FALSE; } zeroCountPart128 = _mm_set_epi32(zeroCountParts3, zeroCountParts2, zeroCountParts1, zeroCountParts0); riceParamPart128 = _mm_set_epi32(riceParamParts3, riceParamParts2, riceParamParts1, riceParamParts0); riceParamPart128 = _mm_and_si128(riceParamPart128, riceParamMask128); riceParamPart128 = _mm_or_si128(riceParamPart128, _mm_slli_epi32(zeroCountPart128, riceParam)); - riceParamPart128 = _mm_xor_si128(_mm_srli_epi32(riceParamPart128, 1), _mm_add_epi32(ma_dr_flac__mm_not_si128(_mm_and_si128(riceParamPart128, _mm_set1_epi32(1))), _mm_set1_epi32(1))); + riceParamPart128 = _mm_xor_si128(_mm_srli_epi32(riceParamPart128, 1), _mm_add_epi32(drflac__mm_not_si128(_mm_and_si128(riceParamPart128, _mm_set1_epi32(1))), _mm_set1_epi32(1))); for (i = 0; i < 4; i += 1) { prediction128 = _mm_xor_si128(prediction128, prediction128); switch (order) @@ -85113,8 +83991,8 @@ static ma_bool32 ma_dr_flac__decode_samples_with_residual__rice__sse41_64(ma_dr_ case 2: case 1: prediction128 = _mm_add_epi64(prediction128, _mm_mul_epi32(_mm_shuffle_epi32(coefficients128_0, _MM_SHUFFLE(3, 3, 2, 2)), _mm_shuffle_epi32(samples128_0, _MM_SHUFFLE(3, 3, 2, 2)))); } - prediction128 = ma_dr_flac__mm_hadd_epi64(prediction128); - prediction128 = ma_dr_flac__mm_srai_epi64(prediction128, shift); + prediction128 = drflac__mm_hadd_epi64(prediction128); + prediction128 = drflac__mm_srai_epi64(prediction128, shift); prediction128 = _mm_add_epi32(riceParamPart128, prediction128); samples128_8 = _mm_alignr_epi8(samples128_4, samples128_8, 4); samples128_4 = _mm_alignr_epi8(samples128_0, samples128_4, 4); @@ -85126,103 +84004,103 @@ static ma_bool32 ma_dr_flac__decode_samples_with_residual__rice__sse41_64(ma_dr_ } i = (count & ~3); while (i < (int)count) { - if (!ma_dr_flac__read_rice_parts_x1(bs, riceParam, &zeroCountParts0, &riceParamParts0)) { - return MA_FALSE; + if (!drflac__read_rice_parts_x1(bs, riceParam, &zeroCountParts0, &riceParamParts0)) { + return DRFLAC_FALSE; } riceParamParts0 &= riceParamMask; riceParamParts0 |= (zeroCountParts0 << riceParam); riceParamParts0 = (riceParamParts0 >> 1) ^ t[riceParamParts0 & 0x01]; - pDecodedSamples[0] = riceParamParts0 + ma_dr_flac__calculate_prediction_64(order, shift, coefficients, pDecodedSamples); + pDecodedSamples[0] = riceParamParts0 + drflac__calculate_prediction_64(order, shift, coefficients, pDecodedSamples); i += 1; pDecodedSamples += 1; } - return MA_TRUE; + return DRFLAC_TRUE; } -static ma_bool32 ma_dr_flac__decode_samples_with_residual__rice__sse41(ma_dr_flac_bs* bs, ma_uint32 bitsPerSample, ma_uint32 count, ma_uint8 riceParam, ma_uint32 lpcOrder, ma_int32 lpcShift, ma_uint32 lpcPrecision, const ma_int32* coefficients, ma_int32* pSamplesOut) +static drflac_bool32 drflac__decode_samples_with_residual__rice__sse41(drflac_bs* bs, drflac_uint32 bitsPerSample, drflac_uint32 count, drflac_uint8 riceParam, drflac_uint32 lpcOrder, drflac_int32 lpcShift, drflac_uint32 lpcPrecision, const drflac_int32* coefficients, drflac_int32* pSamplesOut) { - MA_DR_FLAC_ASSERT(bs != NULL); - MA_DR_FLAC_ASSERT(pSamplesOut != NULL); + DRFLAC_ASSERT(bs != NULL); + DRFLAC_ASSERT(pSamplesOut != NULL); if (lpcOrder > 0 && lpcOrder <= 12) { - if (ma_dr_flac__use_64_bit_prediction(bitsPerSample, lpcOrder, lpcPrecision)) { - return ma_dr_flac__decode_samples_with_residual__rice__sse41_64(bs, count, riceParam, lpcOrder, lpcShift, coefficients, pSamplesOut); + if (drflac__use_64_bit_prediction(bitsPerSample, lpcOrder, lpcPrecision)) { + return drflac__decode_samples_with_residual__rice__sse41_64(bs, count, riceParam, lpcOrder, lpcShift, coefficients, pSamplesOut); } else { - return ma_dr_flac__decode_samples_with_residual__rice__sse41_32(bs, count, riceParam, lpcOrder, lpcShift, coefficients, pSamplesOut); + return drflac__decode_samples_with_residual__rice__sse41_32(bs, count, riceParam, lpcOrder, lpcShift, coefficients, pSamplesOut); } } else { - return ma_dr_flac__decode_samples_with_residual__rice__scalar(bs, bitsPerSample, count, riceParam, lpcOrder, lpcShift, lpcPrecision, coefficients, pSamplesOut); + return drflac__decode_samples_with_residual__rice__scalar(bs, bitsPerSample, count, riceParam, lpcOrder, lpcShift, lpcPrecision, coefficients, pSamplesOut); } } #endif -#if defined(MA_DR_FLAC_SUPPORT_NEON) -static MA_INLINE void ma_dr_flac__vst2q_s32(ma_int32* p, int32x4x2_t x) +#if defined(DRFLAC_SUPPORT_NEON) +static DRFLAC_INLINE void drflac__vst2q_s32(drflac_int32* p, int32x4x2_t x) { vst1q_s32(p+0, x.val[0]); vst1q_s32(p+4, x.val[1]); } -static MA_INLINE void ma_dr_flac__vst2q_u32(ma_uint32* p, uint32x4x2_t x) +static DRFLAC_INLINE void drflac__vst2q_u32(drflac_uint32* p, uint32x4x2_t x) { vst1q_u32(p+0, x.val[0]); vst1q_u32(p+4, x.val[1]); } -static MA_INLINE void ma_dr_flac__vst2q_f32(float* p, float32x4x2_t x) +static DRFLAC_INLINE void drflac__vst2q_f32(float* p, float32x4x2_t x) { vst1q_f32(p+0, x.val[0]); vst1q_f32(p+4, x.val[1]); } -static MA_INLINE void ma_dr_flac__vst2q_s16(ma_int16* p, int16x4x2_t x) +static DRFLAC_INLINE void drflac__vst2q_s16(drflac_int16* p, int16x4x2_t x) { vst1q_s16(p, vcombine_s16(x.val[0], x.val[1])); } -static MA_INLINE void ma_dr_flac__vst2q_u16(ma_uint16* p, uint16x4x2_t x) +static DRFLAC_INLINE void drflac__vst2q_u16(drflac_uint16* p, uint16x4x2_t x) { vst1q_u16(p, vcombine_u16(x.val[0], x.val[1])); } -static MA_INLINE int32x4_t ma_dr_flac__vdupq_n_s32x4(ma_int32 x3, ma_int32 x2, ma_int32 x1, ma_int32 x0) +static DRFLAC_INLINE int32x4_t drflac__vdupq_n_s32x4(drflac_int32 x3, drflac_int32 x2, drflac_int32 x1, drflac_int32 x0) { - ma_int32 x[4]; + drflac_int32 x[4]; x[3] = x3; x[2] = x2; x[1] = x1; x[0] = x0; return vld1q_s32(x); } -static MA_INLINE int32x4_t ma_dr_flac__valignrq_s32_1(int32x4_t a, int32x4_t b) +static DRFLAC_INLINE int32x4_t drflac__valignrq_s32_1(int32x4_t a, int32x4_t b) { return vextq_s32(b, a, 1); } -static MA_INLINE uint32x4_t ma_dr_flac__valignrq_u32_1(uint32x4_t a, uint32x4_t b) +static DRFLAC_INLINE uint32x4_t drflac__valignrq_u32_1(uint32x4_t a, uint32x4_t b) { return vextq_u32(b, a, 1); } -static MA_INLINE int32x2_t ma_dr_flac__vhaddq_s32(int32x4_t x) +static DRFLAC_INLINE int32x2_t drflac__vhaddq_s32(int32x4_t x) { int32x2_t r = vadd_s32(vget_high_s32(x), vget_low_s32(x)); return vpadd_s32(r, r); } -static MA_INLINE int64x1_t ma_dr_flac__vhaddq_s64(int64x2_t x) +static DRFLAC_INLINE int64x1_t drflac__vhaddq_s64(int64x2_t x) { return vadd_s64(vget_high_s64(x), vget_low_s64(x)); } -static MA_INLINE int32x4_t ma_dr_flac__vrevq_s32(int32x4_t x) +static DRFLAC_INLINE int32x4_t drflac__vrevq_s32(int32x4_t x) { return vrev64q_s32(vcombine_s32(vget_high_s32(x), vget_low_s32(x))); } -static MA_INLINE int32x4_t ma_dr_flac__vnotq_s32(int32x4_t x) +static DRFLAC_INLINE int32x4_t drflac__vnotq_s32(int32x4_t x) { return veorq_s32(x, vdupq_n_s32(0xFFFFFFFF)); } -static MA_INLINE uint32x4_t ma_dr_flac__vnotq_u32(uint32x4_t x) +static DRFLAC_INLINE uint32x4_t drflac__vnotq_u32(uint32x4_t x) { return veorq_u32(x, vdupq_n_u32(0xFFFFFFFF)); } -static ma_bool32 ma_dr_flac__decode_samples_with_residual__rice__neon_32(ma_dr_flac_bs* bs, ma_uint32 count, ma_uint8 riceParam, ma_uint32 order, ma_int32 shift, const ma_int32* coefficients, ma_int32* pSamplesOut) +static drflac_bool32 drflac__decode_samples_with_residual__rice__neon_32(drflac_bs* bs, drflac_uint32 count, drflac_uint8 riceParam, drflac_uint32 order, drflac_int32 shift, const drflac_int32* coefficients, drflac_int32* pSamplesOut) { int i; - ma_uint32 riceParamMask; - ma_int32* pDecodedSamples = pSamplesOut; - ma_int32* pDecodedSamplesEnd = pSamplesOut + (count & ~3); - ma_uint32 zeroCountParts[4]; - ma_uint32 riceParamParts[4]; + drflac_uint32 riceParamMask; + drflac_int32* pDecodedSamples = pSamplesOut; + drflac_int32* pDecodedSamplesEnd = pSamplesOut + (count & ~3); + drflac_uint32 zeroCountParts[4]; + drflac_uint32 riceParamParts[4]; int32x4_t coefficients128_0; int32x4_t coefficients128_4; int32x4_t coefficients128_8; @@ -85233,16 +84111,16 @@ static ma_bool32 ma_dr_flac__decode_samples_with_residual__rice__neon_32(ma_dr_f int32x4_t riceParam128; int32x2_t shift64; uint32x4_t one128; - const ma_uint32 t[2] = {0x00000000, 0xFFFFFFFF}; - riceParamMask = (ma_uint32)~((~0UL) << riceParam); + const drflac_uint32 t[2] = {0x00000000, 0xFFFFFFFF}; + riceParamMask = (drflac_uint32)~((~0UL) << riceParam); riceParamMask128 = vdupq_n_u32(riceParamMask); riceParam128 = vdupq_n_s32(riceParam); shift64 = vdup_n_s32(-shift); one128 = vdupq_n_u32(1); { int runningOrder = order; - ma_int32 tempC[4] = {0, 0, 0, 0}; - ma_int32 tempS[4] = {0, 0, 0, 0}; + drflac_int32 tempC[4] = {0, 0, 0, 0}; + drflac_int32 tempS[4] = {0, 0, 0, 0}; if (runningOrder >= 4) { coefficients128_0 = vld1q_s32(coefficients + 0); samples128_0 = vld1q_s32(pSamplesOut - 4); @@ -85285,58 +84163,58 @@ static ma_bool32 ma_dr_flac__decode_samples_with_residual__rice__neon_32(ma_dr_f samples128_8 = vld1q_s32(tempS); runningOrder = 0; } - coefficients128_0 = ma_dr_flac__vrevq_s32(coefficients128_0); - coefficients128_4 = ma_dr_flac__vrevq_s32(coefficients128_4); - coefficients128_8 = ma_dr_flac__vrevq_s32(coefficients128_8); + coefficients128_0 = drflac__vrevq_s32(coefficients128_0); + coefficients128_4 = drflac__vrevq_s32(coefficients128_4); + coefficients128_8 = drflac__vrevq_s32(coefficients128_8); } while (pDecodedSamples < pDecodedSamplesEnd) { int32x4_t prediction128; int32x2_t prediction64; uint32x4_t zeroCountPart128; uint32x4_t riceParamPart128; - if (!ma_dr_flac__read_rice_parts_x1(bs, riceParam, &zeroCountParts[0], &riceParamParts[0]) || - !ma_dr_flac__read_rice_parts_x1(bs, riceParam, &zeroCountParts[1], &riceParamParts[1]) || - !ma_dr_flac__read_rice_parts_x1(bs, riceParam, &zeroCountParts[2], &riceParamParts[2]) || - !ma_dr_flac__read_rice_parts_x1(bs, riceParam, &zeroCountParts[3], &riceParamParts[3])) { - return MA_FALSE; + if (!drflac__read_rice_parts_x1(bs, riceParam, &zeroCountParts[0], &riceParamParts[0]) || + !drflac__read_rice_parts_x1(bs, riceParam, &zeroCountParts[1], &riceParamParts[1]) || + !drflac__read_rice_parts_x1(bs, riceParam, &zeroCountParts[2], &riceParamParts[2]) || + !drflac__read_rice_parts_x1(bs, riceParam, &zeroCountParts[3], &riceParamParts[3])) { + return DRFLAC_FALSE; } zeroCountPart128 = vld1q_u32(zeroCountParts); riceParamPart128 = vld1q_u32(riceParamParts); riceParamPart128 = vandq_u32(riceParamPart128, riceParamMask128); riceParamPart128 = vorrq_u32(riceParamPart128, vshlq_u32(zeroCountPart128, riceParam128)); - riceParamPart128 = veorq_u32(vshrq_n_u32(riceParamPart128, 1), vaddq_u32(ma_dr_flac__vnotq_u32(vandq_u32(riceParamPart128, one128)), one128)); + riceParamPart128 = veorq_u32(vshrq_n_u32(riceParamPart128, 1), vaddq_u32(drflac__vnotq_u32(vandq_u32(riceParamPart128, one128)), one128)); if (order <= 4) { for (i = 0; i < 4; i += 1) { prediction128 = vmulq_s32(coefficients128_0, samples128_0); - prediction64 = ma_dr_flac__vhaddq_s32(prediction128); + prediction64 = drflac__vhaddq_s32(prediction128); prediction64 = vshl_s32(prediction64, shift64); prediction64 = vadd_s32(prediction64, vget_low_s32(vreinterpretq_s32_u32(riceParamPart128))); - samples128_0 = ma_dr_flac__valignrq_s32_1(vcombine_s32(prediction64, vdup_n_s32(0)), samples128_0); - riceParamPart128 = ma_dr_flac__valignrq_u32_1(vdupq_n_u32(0), riceParamPart128); + samples128_0 = drflac__valignrq_s32_1(vcombine_s32(prediction64, vdup_n_s32(0)), samples128_0); + riceParamPart128 = drflac__valignrq_u32_1(vdupq_n_u32(0), riceParamPart128); } } else if (order <= 8) { for (i = 0; i < 4; i += 1) { prediction128 = vmulq_s32(coefficients128_4, samples128_4); prediction128 = vmlaq_s32(prediction128, coefficients128_0, samples128_0); - prediction64 = ma_dr_flac__vhaddq_s32(prediction128); + prediction64 = drflac__vhaddq_s32(prediction128); prediction64 = vshl_s32(prediction64, shift64); prediction64 = vadd_s32(prediction64, vget_low_s32(vreinterpretq_s32_u32(riceParamPart128))); - samples128_4 = ma_dr_flac__valignrq_s32_1(samples128_0, samples128_4); - samples128_0 = ma_dr_flac__valignrq_s32_1(vcombine_s32(prediction64, vdup_n_s32(0)), samples128_0); - riceParamPart128 = ma_dr_flac__valignrq_u32_1(vdupq_n_u32(0), riceParamPart128); + samples128_4 = drflac__valignrq_s32_1(samples128_0, samples128_4); + samples128_0 = drflac__valignrq_s32_1(vcombine_s32(prediction64, vdup_n_s32(0)), samples128_0); + riceParamPart128 = drflac__valignrq_u32_1(vdupq_n_u32(0), riceParamPart128); } } else { for (i = 0; i < 4; i += 1) { prediction128 = vmulq_s32(coefficients128_8, samples128_8); prediction128 = vmlaq_s32(prediction128, coefficients128_4, samples128_4); prediction128 = vmlaq_s32(prediction128, coefficients128_0, samples128_0); - prediction64 = ma_dr_flac__vhaddq_s32(prediction128); + prediction64 = drflac__vhaddq_s32(prediction128); prediction64 = vshl_s32(prediction64, shift64); prediction64 = vadd_s32(prediction64, vget_low_s32(vreinterpretq_s32_u32(riceParamPart128))); - samples128_8 = ma_dr_flac__valignrq_s32_1(samples128_4, samples128_8); - samples128_4 = ma_dr_flac__valignrq_s32_1(samples128_0, samples128_4); - samples128_0 = ma_dr_flac__valignrq_s32_1(vcombine_s32(prediction64, vdup_n_s32(0)), samples128_0); - riceParamPart128 = ma_dr_flac__valignrq_u32_1(vdupq_n_u32(0), riceParamPart128); + samples128_8 = drflac__valignrq_s32_1(samples128_4, samples128_8); + samples128_4 = drflac__valignrq_s32_1(samples128_0, samples128_4); + samples128_0 = drflac__valignrq_s32_1(vcombine_s32(prediction64, vdup_n_s32(0)), samples128_0); + riceParamPart128 = drflac__valignrq_u32_1(vdupq_n_u32(0), riceParamPart128); } } vst1q_s32(pDecodedSamples, samples128_0); @@ -85344,26 +84222,26 @@ static ma_bool32 ma_dr_flac__decode_samples_with_residual__rice__neon_32(ma_dr_f } i = (count & ~3); while (i < (int)count) { - if (!ma_dr_flac__read_rice_parts_x1(bs, riceParam, &zeroCountParts[0], &riceParamParts[0])) { - return MA_FALSE; + if (!drflac__read_rice_parts_x1(bs, riceParam, &zeroCountParts[0], &riceParamParts[0])) { + return DRFLAC_FALSE; } riceParamParts[0] &= riceParamMask; riceParamParts[0] |= (zeroCountParts[0] << riceParam); riceParamParts[0] = (riceParamParts[0] >> 1) ^ t[riceParamParts[0] & 0x01]; - pDecodedSamples[0] = riceParamParts[0] + ma_dr_flac__calculate_prediction_32(order, shift, coefficients, pDecodedSamples); + pDecodedSamples[0] = riceParamParts[0] + drflac__calculate_prediction_32(order, shift, coefficients, pDecodedSamples); i += 1; pDecodedSamples += 1; } - return MA_TRUE; + return DRFLAC_TRUE; } -static ma_bool32 ma_dr_flac__decode_samples_with_residual__rice__neon_64(ma_dr_flac_bs* bs, ma_uint32 count, ma_uint8 riceParam, ma_uint32 order, ma_int32 shift, const ma_int32* coefficients, ma_int32* pSamplesOut) +static drflac_bool32 drflac__decode_samples_with_residual__rice__neon_64(drflac_bs* bs, drflac_uint32 count, drflac_uint8 riceParam, drflac_uint32 order, drflac_int32 shift, const drflac_int32* coefficients, drflac_int32* pSamplesOut) { int i; - ma_uint32 riceParamMask; - ma_int32* pDecodedSamples = pSamplesOut; - ma_int32* pDecodedSamplesEnd = pSamplesOut + (count & ~3); - ma_uint32 zeroCountParts[4]; - ma_uint32 riceParamParts[4]; + drflac_uint32 riceParamMask; + drflac_int32* pDecodedSamples = pSamplesOut; + drflac_int32* pDecodedSamplesEnd = pSamplesOut + (count & ~3); + drflac_uint32 zeroCountParts[4]; + drflac_uint32 riceParamParts[4]; int32x4_t coefficients128_0; int32x4_t coefficients128_4; int32x4_t coefficients128_8; @@ -85377,16 +84255,16 @@ static ma_bool32 ma_dr_flac__decode_samples_with_residual__rice__neon_64(ma_dr_f int64x2_t prediction128 = { 0 }; uint32x4_t zeroCountPart128; uint32x4_t riceParamPart128; - const ma_uint32 t[2] = {0x00000000, 0xFFFFFFFF}; - riceParamMask = (ma_uint32)~((~0UL) << riceParam); + const drflac_uint32 t[2] = {0x00000000, 0xFFFFFFFF}; + riceParamMask = (drflac_uint32)~((~0UL) << riceParam); riceParamMask128 = vdupq_n_u32(riceParamMask); riceParam128 = vdupq_n_s32(riceParam); shift64 = vdup_n_s64(-shift); one128 = vdupq_n_u32(1); { int runningOrder = order; - ma_int32 tempC[4] = {0, 0, 0, 0}; - ma_int32 tempS[4] = {0, 0, 0, 0}; + drflac_int32 tempC[4] = {0, 0, 0, 0}; + drflac_int32 tempS[4] = {0, 0, 0, 0}; if (runningOrder >= 4) { coefficients128_0 = vld1q_s32(coefficients + 0); samples128_0 = vld1q_s32(pSamplesOut - 4); @@ -85429,22 +84307,22 @@ static ma_bool32 ma_dr_flac__decode_samples_with_residual__rice__neon_64(ma_dr_f samples128_8 = vld1q_s32(tempS); runningOrder = 0; } - coefficients128_0 = ma_dr_flac__vrevq_s32(coefficients128_0); - coefficients128_4 = ma_dr_flac__vrevq_s32(coefficients128_4); - coefficients128_8 = ma_dr_flac__vrevq_s32(coefficients128_8); + coefficients128_0 = drflac__vrevq_s32(coefficients128_0); + coefficients128_4 = drflac__vrevq_s32(coefficients128_4); + coefficients128_8 = drflac__vrevq_s32(coefficients128_8); } while (pDecodedSamples < pDecodedSamplesEnd) { - if (!ma_dr_flac__read_rice_parts_x1(bs, riceParam, &zeroCountParts[0], &riceParamParts[0]) || - !ma_dr_flac__read_rice_parts_x1(bs, riceParam, &zeroCountParts[1], &riceParamParts[1]) || - !ma_dr_flac__read_rice_parts_x1(bs, riceParam, &zeroCountParts[2], &riceParamParts[2]) || - !ma_dr_flac__read_rice_parts_x1(bs, riceParam, &zeroCountParts[3], &riceParamParts[3])) { - return MA_FALSE; + if (!drflac__read_rice_parts_x1(bs, riceParam, &zeroCountParts[0], &riceParamParts[0]) || + !drflac__read_rice_parts_x1(bs, riceParam, &zeroCountParts[1], &riceParamParts[1]) || + !drflac__read_rice_parts_x1(bs, riceParam, &zeroCountParts[2], &riceParamParts[2]) || + !drflac__read_rice_parts_x1(bs, riceParam, &zeroCountParts[3], &riceParamParts[3])) { + return DRFLAC_FALSE; } zeroCountPart128 = vld1q_u32(zeroCountParts); riceParamPart128 = vld1q_u32(riceParamParts); riceParamPart128 = vandq_u32(riceParamPart128, riceParamMask128); riceParamPart128 = vorrq_u32(riceParamPart128, vshlq_u32(zeroCountPart128, riceParam128)); - riceParamPart128 = veorq_u32(vshrq_n_u32(riceParamPart128, 1), vaddq_u32(ma_dr_flac__vnotq_u32(vandq_u32(riceParamPart128, one128)), one128)); + riceParamPart128 = veorq_u32(vshrq_n_u32(riceParamPart128, 1), vaddq_u32(drflac__vnotq_u32(vandq_u32(riceParamPart128, one128)), one128)); for (i = 0; i < 4; i += 1) { int64x1_t prediction64; prediction128 = veorq_s64(prediction128, prediction128); @@ -85463,156 +84341,156 @@ static ma_bool32 ma_dr_flac__decode_samples_with_residual__rice__neon_64(ma_dr_f case 2: case 1: prediction128 = vaddq_s64(prediction128, vmull_s32(vget_high_s32(coefficients128_0), vget_high_s32(samples128_0))); } - prediction64 = ma_dr_flac__vhaddq_s64(prediction128); + prediction64 = drflac__vhaddq_s64(prediction128); prediction64 = vshl_s64(prediction64, shift64); prediction64 = vadd_s64(prediction64, vdup_n_s64(vgetq_lane_u32(riceParamPart128, 0))); - samples128_8 = ma_dr_flac__valignrq_s32_1(samples128_4, samples128_8); - samples128_4 = ma_dr_flac__valignrq_s32_1(samples128_0, samples128_4); - samples128_0 = ma_dr_flac__valignrq_s32_1(vcombine_s32(vreinterpret_s32_s64(prediction64), vdup_n_s32(0)), samples128_0); - riceParamPart128 = ma_dr_flac__valignrq_u32_1(vdupq_n_u32(0), riceParamPart128); + samples128_8 = drflac__valignrq_s32_1(samples128_4, samples128_8); + samples128_4 = drflac__valignrq_s32_1(samples128_0, samples128_4); + samples128_0 = drflac__valignrq_s32_1(vcombine_s32(vreinterpret_s32_s64(prediction64), vdup_n_s32(0)), samples128_0); + riceParamPart128 = drflac__valignrq_u32_1(vdupq_n_u32(0), riceParamPart128); } vst1q_s32(pDecodedSamples, samples128_0); pDecodedSamples += 4; } i = (count & ~3); while (i < (int)count) { - if (!ma_dr_flac__read_rice_parts_x1(bs, riceParam, &zeroCountParts[0], &riceParamParts[0])) { - return MA_FALSE; + if (!drflac__read_rice_parts_x1(bs, riceParam, &zeroCountParts[0], &riceParamParts[0])) { + return DRFLAC_FALSE; } riceParamParts[0] &= riceParamMask; riceParamParts[0] |= (zeroCountParts[0] << riceParam); riceParamParts[0] = (riceParamParts[0] >> 1) ^ t[riceParamParts[0] & 0x01]; - pDecodedSamples[0] = riceParamParts[0] + ma_dr_flac__calculate_prediction_64(order, shift, coefficients, pDecodedSamples); + pDecodedSamples[0] = riceParamParts[0] + drflac__calculate_prediction_64(order, shift, coefficients, pDecodedSamples); i += 1; pDecodedSamples += 1; } - return MA_TRUE; + return DRFLAC_TRUE; } -static ma_bool32 ma_dr_flac__decode_samples_with_residual__rice__neon(ma_dr_flac_bs* bs, ma_uint32 bitsPerSample, ma_uint32 count, ma_uint8 riceParam, ma_uint32 lpcOrder, ma_int32 lpcShift, ma_uint32 lpcPrecision, const ma_int32* coefficients, ma_int32* pSamplesOut) +static drflac_bool32 drflac__decode_samples_with_residual__rice__neon(drflac_bs* bs, drflac_uint32 bitsPerSample, drflac_uint32 count, drflac_uint8 riceParam, drflac_uint32 lpcOrder, drflac_int32 lpcShift, drflac_uint32 lpcPrecision, const drflac_int32* coefficients, drflac_int32* pSamplesOut) { - MA_DR_FLAC_ASSERT(bs != NULL); - MA_DR_FLAC_ASSERT(pSamplesOut != NULL); + DRFLAC_ASSERT(bs != NULL); + DRFLAC_ASSERT(pSamplesOut != NULL); if (lpcOrder > 0 && lpcOrder <= 12) { - if (ma_dr_flac__use_64_bit_prediction(bitsPerSample, lpcOrder, lpcPrecision)) { - return ma_dr_flac__decode_samples_with_residual__rice__neon_64(bs, count, riceParam, lpcOrder, lpcShift, coefficients, pSamplesOut); + if (drflac__use_64_bit_prediction(bitsPerSample, lpcOrder, lpcPrecision)) { + return drflac__decode_samples_with_residual__rice__neon_64(bs, count, riceParam, lpcOrder, lpcShift, coefficients, pSamplesOut); } else { - return ma_dr_flac__decode_samples_with_residual__rice__neon_32(bs, count, riceParam, lpcOrder, lpcShift, coefficients, pSamplesOut); + return drflac__decode_samples_with_residual__rice__neon_32(bs, count, riceParam, lpcOrder, lpcShift, coefficients, pSamplesOut); } } else { - return ma_dr_flac__decode_samples_with_residual__rice__scalar(bs, bitsPerSample, count, riceParam, lpcOrder, lpcShift, lpcPrecision, coefficients, pSamplesOut); + return drflac__decode_samples_with_residual__rice__scalar(bs, bitsPerSample, count, riceParam, lpcOrder, lpcShift, lpcPrecision, coefficients, pSamplesOut); } } #endif -static ma_bool32 ma_dr_flac__decode_samples_with_residual__rice(ma_dr_flac_bs* bs, ma_uint32 bitsPerSample, ma_uint32 count, ma_uint8 riceParam, ma_uint32 lpcOrder, ma_int32 lpcShift, ma_uint32 lpcPrecision, const ma_int32* coefficients, ma_int32* pSamplesOut) +static drflac_bool32 drflac__decode_samples_with_residual__rice(drflac_bs* bs, drflac_uint32 bitsPerSample, drflac_uint32 count, drflac_uint8 riceParam, drflac_uint32 lpcOrder, drflac_int32 lpcShift, drflac_uint32 lpcPrecision, const drflac_int32* coefficients, drflac_int32* pSamplesOut) { -#if defined(MA_DR_FLAC_SUPPORT_SSE41) - if (ma_dr_flac__gIsSSE41Supported) { - return ma_dr_flac__decode_samples_with_residual__rice__sse41(bs, bitsPerSample, count, riceParam, lpcOrder, lpcShift, lpcPrecision, coefficients, pSamplesOut); +#if defined(DRFLAC_SUPPORT_SSE41) + if (drflac__gIsSSE41Supported) { + return drflac__decode_samples_with_residual__rice__sse41(bs, bitsPerSample, count, riceParam, lpcOrder, lpcShift, lpcPrecision, coefficients, pSamplesOut); } else -#elif defined(MA_DR_FLAC_SUPPORT_NEON) - if (ma_dr_flac__gIsNEONSupported) { - return ma_dr_flac__decode_samples_with_residual__rice__neon(bs, bitsPerSample, count, riceParam, lpcOrder, lpcShift, lpcPrecision, coefficients, pSamplesOut); +#elif defined(DRFLAC_SUPPORT_NEON) + if (drflac__gIsNEONSupported) { + return drflac__decode_samples_with_residual__rice__neon(bs, bitsPerSample, count, riceParam, lpcOrder, lpcShift, lpcPrecision, coefficients, pSamplesOut); } else #endif { #if 0 - return ma_dr_flac__decode_samples_with_residual__rice__reference(bs, bitsPerSample, count, riceParam, lpcOrder, lpcShift, lpcPrecision, coefficients, pSamplesOut); + return drflac__decode_samples_with_residual__rice__reference(bs, bitsPerSample, count, riceParam, lpcOrder, lpcShift, lpcPrecision, coefficients, pSamplesOut); #else - return ma_dr_flac__decode_samples_with_residual__rice__scalar(bs, bitsPerSample, count, riceParam, lpcOrder, lpcShift, lpcPrecision, coefficients, pSamplesOut); + return drflac__decode_samples_with_residual__rice__scalar(bs, bitsPerSample, count, riceParam, lpcOrder, lpcShift, lpcPrecision, coefficients, pSamplesOut); #endif } } -static ma_bool32 ma_dr_flac__read_and_seek_residual__rice(ma_dr_flac_bs* bs, ma_uint32 count, ma_uint8 riceParam) +static drflac_bool32 drflac__read_and_seek_residual__rice(drflac_bs* bs, drflac_uint32 count, drflac_uint8 riceParam) { - ma_uint32 i; - MA_DR_FLAC_ASSERT(bs != NULL); + drflac_uint32 i; + DRFLAC_ASSERT(bs != NULL); for (i = 0; i < count; ++i) { - if (!ma_dr_flac__seek_rice_parts(bs, riceParam)) { - return MA_FALSE; + if (!drflac__seek_rice_parts(bs, riceParam)) { + return DRFLAC_FALSE; } } - return MA_TRUE; + return DRFLAC_TRUE; } #if defined(__clang__) __attribute__((no_sanitize("signed-integer-overflow"))) #endif -static ma_bool32 ma_dr_flac__decode_samples_with_residual__unencoded(ma_dr_flac_bs* bs, ma_uint32 bitsPerSample, ma_uint32 count, ma_uint8 unencodedBitsPerSample, ma_uint32 lpcOrder, ma_int32 lpcShift, ma_uint32 lpcPrecision, const ma_int32* coefficients, ma_int32* pSamplesOut) +static drflac_bool32 drflac__decode_samples_with_residual__unencoded(drflac_bs* bs, drflac_uint32 bitsPerSample, drflac_uint32 count, drflac_uint8 unencodedBitsPerSample, drflac_uint32 lpcOrder, drflac_int32 lpcShift, drflac_uint32 lpcPrecision, const drflac_int32* coefficients, drflac_int32* pSamplesOut) { - ma_uint32 i; - MA_DR_FLAC_ASSERT(bs != NULL); - MA_DR_FLAC_ASSERT(unencodedBitsPerSample <= 31); - MA_DR_FLAC_ASSERT(pSamplesOut != NULL); + drflac_uint32 i; + DRFLAC_ASSERT(bs != NULL); + DRFLAC_ASSERT(unencodedBitsPerSample <= 31); + DRFLAC_ASSERT(pSamplesOut != NULL); for (i = 0; i < count; ++i) { if (unencodedBitsPerSample > 0) { - if (!ma_dr_flac__read_int32(bs, unencodedBitsPerSample, pSamplesOut + i)) { - return MA_FALSE; + if (!drflac__read_int32(bs, unencodedBitsPerSample, pSamplesOut + i)) { + return DRFLAC_FALSE; } } else { pSamplesOut[i] = 0; } - if (ma_dr_flac__use_64_bit_prediction(bitsPerSample, lpcOrder, lpcPrecision)) { - pSamplesOut[i] += ma_dr_flac__calculate_prediction_64(lpcOrder, lpcShift, coefficients, pSamplesOut + i); + if (drflac__use_64_bit_prediction(bitsPerSample, lpcOrder, lpcPrecision)) { + pSamplesOut[i] += drflac__calculate_prediction_64(lpcOrder, lpcShift, coefficients, pSamplesOut + i); } else { - pSamplesOut[i] += ma_dr_flac__calculate_prediction_32(lpcOrder, lpcShift, coefficients, pSamplesOut + i); + pSamplesOut[i] += drflac__calculate_prediction_32(lpcOrder, lpcShift, coefficients, pSamplesOut + i); } } - return MA_TRUE; + return DRFLAC_TRUE; } -static ma_bool32 ma_dr_flac__decode_samples_with_residual(ma_dr_flac_bs* bs, ma_uint32 bitsPerSample, ma_uint32 blockSize, ma_uint32 lpcOrder, ma_int32 lpcShift, ma_uint32 lpcPrecision, const ma_int32* coefficients, ma_int32* pDecodedSamples) +static drflac_bool32 drflac__decode_samples_with_residual(drflac_bs* bs, drflac_uint32 bitsPerSample, drflac_uint32 blockSize, drflac_uint32 lpcOrder, drflac_int32 lpcShift, drflac_uint32 lpcPrecision, const drflac_int32* coefficients, drflac_int32* pDecodedSamples) { - ma_uint8 residualMethod; - ma_uint8 partitionOrder; - ma_uint32 samplesInPartition; - ma_uint32 partitionsRemaining; - MA_DR_FLAC_ASSERT(bs != NULL); - MA_DR_FLAC_ASSERT(blockSize != 0); - MA_DR_FLAC_ASSERT(pDecodedSamples != NULL); - if (!ma_dr_flac__read_uint8(bs, 2, &residualMethod)) { - return MA_FALSE; + drflac_uint8 residualMethod; + drflac_uint8 partitionOrder; + drflac_uint32 samplesInPartition; + drflac_uint32 partitionsRemaining; + DRFLAC_ASSERT(bs != NULL); + DRFLAC_ASSERT(blockSize != 0); + DRFLAC_ASSERT(pDecodedSamples != NULL); + if (!drflac__read_uint8(bs, 2, &residualMethod)) { + return DRFLAC_FALSE; } - if (residualMethod != MA_DR_FLAC_RESIDUAL_CODING_METHOD_PARTITIONED_RICE && residualMethod != MA_DR_FLAC_RESIDUAL_CODING_METHOD_PARTITIONED_RICE2) { - return MA_FALSE; + if (residualMethod != DRFLAC_RESIDUAL_CODING_METHOD_PARTITIONED_RICE && residualMethod != DRFLAC_RESIDUAL_CODING_METHOD_PARTITIONED_RICE2) { + return DRFLAC_FALSE; } pDecodedSamples += lpcOrder; - if (!ma_dr_flac__read_uint8(bs, 4, &partitionOrder)) { - return MA_FALSE; + if (!drflac__read_uint8(bs, 4, &partitionOrder)) { + return DRFLAC_FALSE; } if (partitionOrder > 8) { - return MA_FALSE; + return DRFLAC_FALSE; } if ((blockSize / (1 << partitionOrder)) < lpcOrder) { - return MA_FALSE; + return DRFLAC_FALSE; } samplesInPartition = (blockSize / (1 << partitionOrder)) - lpcOrder; partitionsRemaining = (1 << partitionOrder); for (;;) { - ma_uint8 riceParam = 0; - if (residualMethod == MA_DR_FLAC_RESIDUAL_CODING_METHOD_PARTITIONED_RICE) { - if (!ma_dr_flac__read_uint8(bs, 4, &riceParam)) { - return MA_FALSE; + drflac_uint8 riceParam = 0; + if (residualMethod == DRFLAC_RESIDUAL_CODING_METHOD_PARTITIONED_RICE) { + if (!drflac__read_uint8(bs, 4, &riceParam)) { + return DRFLAC_FALSE; } if (riceParam == 15) { riceParam = 0xFF; } - } else if (residualMethod == MA_DR_FLAC_RESIDUAL_CODING_METHOD_PARTITIONED_RICE2) { - if (!ma_dr_flac__read_uint8(bs, 5, &riceParam)) { - return MA_FALSE; + } else if (residualMethod == DRFLAC_RESIDUAL_CODING_METHOD_PARTITIONED_RICE2) { + if (!drflac__read_uint8(bs, 5, &riceParam)) { + return DRFLAC_FALSE; } if (riceParam == 31) { riceParam = 0xFF; } } if (riceParam != 0xFF) { - if (!ma_dr_flac__decode_samples_with_residual__rice(bs, bitsPerSample, samplesInPartition, riceParam, lpcOrder, lpcShift, lpcPrecision, coefficients, pDecodedSamples)) { - return MA_FALSE; + if (!drflac__decode_samples_with_residual__rice(bs, bitsPerSample, samplesInPartition, riceParam, lpcOrder, lpcShift, lpcPrecision, coefficients, pDecodedSamples)) { + return DRFLAC_FALSE; } } else { - ma_uint8 unencodedBitsPerSample = 0; - if (!ma_dr_flac__read_uint8(bs, 5, &unencodedBitsPerSample)) { - return MA_FALSE; + drflac_uint8 unencodedBitsPerSample = 0; + if (!drflac__read_uint8(bs, 5, &unencodedBitsPerSample)) { + return DRFLAC_FALSE; } - if (!ma_dr_flac__decode_samples_with_residual__unencoded(bs, bitsPerSample, samplesInPartition, unencodedBitsPerSample, lpcOrder, lpcShift, lpcPrecision, coefficients, pDecodedSamples)) { - return MA_FALSE; + if (!drflac__decode_samples_with_residual__unencoded(bs, bitsPerSample, samplesInPartition, unencodedBitsPerSample, lpcOrder, lpcShift, lpcPrecision, coefficients, pDecodedSamples)) { + return DRFLAC_FALSE; } } pDecodedSamples += samplesInPartition; @@ -85624,62 +84502,62 @@ static ma_bool32 ma_dr_flac__decode_samples_with_residual(ma_dr_flac_bs* bs, ma_ samplesInPartition = blockSize / (1 << partitionOrder); } } - return MA_TRUE; + return DRFLAC_TRUE; } -static ma_bool32 ma_dr_flac__read_and_seek_residual(ma_dr_flac_bs* bs, ma_uint32 blockSize, ma_uint32 order) +static drflac_bool32 drflac__read_and_seek_residual(drflac_bs* bs, drflac_uint32 blockSize, drflac_uint32 order) { - ma_uint8 residualMethod; - ma_uint8 partitionOrder; - ma_uint32 samplesInPartition; - ma_uint32 partitionsRemaining; - MA_DR_FLAC_ASSERT(bs != NULL); - MA_DR_FLAC_ASSERT(blockSize != 0); - if (!ma_dr_flac__read_uint8(bs, 2, &residualMethod)) { - return MA_FALSE; + drflac_uint8 residualMethod; + drflac_uint8 partitionOrder; + drflac_uint32 samplesInPartition; + drflac_uint32 partitionsRemaining; + DRFLAC_ASSERT(bs != NULL); + DRFLAC_ASSERT(blockSize != 0); + if (!drflac__read_uint8(bs, 2, &residualMethod)) { + return DRFLAC_FALSE; } - if (residualMethod != MA_DR_FLAC_RESIDUAL_CODING_METHOD_PARTITIONED_RICE && residualMethod != MA_DR_FLAC_RESIDUAL_CODING_METHOD_PARTITIONED_RICE2) { - return MA_FALSE; + if (residualMethod != DRFLAC_RESIDUAL_CODING_METHOD_PARTITIONED_RICE && residualMethod != DRFLAC_RESIDUAL_CODING_METHOD_PARTITIONED_RICE2) { + return DRFLAC_FALSE; } - if (!ma_dr_flac__read_uint8(bs, 4, &partitionOrder)) { - return MA_FALSE; + if (!drflac__read_uint8(bs, 4, &partitionOrder)) { + return DRFLAC_FALSE; } if (partitionOrder > 8) { - return MA_FALSE; + return DRFLAC_FALSE; } if ((blockSize / (1 << partitionOrder)) <= order) { - return MA_FALSE; + return DRFLAC_FALSE; } samplesInPartition = (blockSize / (1 << partitionOrder)) - order; partitionsRemaining = (1 << partitionOrder); for (;;) { - ma_uint8 riceParam = 0; - if (residualMethod == MA_DR_FLAC_RESIDUAL_CODING_METHOD_PARTITIONED_RICE) { - if (!ma_dr_flac__read_uint8(bs, 4, &riceParam)) { - return MA_FALSE; + drflac_uint8 riceParam = 0; + if (residualMethod == DRFLAC_RESIDUAL_CODING_METHOD_PARTITIONED_RICE) { + if (!drflac__read_uint8(bs, 4, &riceParam)) { + return DRFLAC_FALSE; } if (riceParam == 15) { riceParam = 0xFF; } - } else if (residualMethod == MA_DR_FLAC_RESIDUAL_CODING_METHOD_PARTITIONED_RICE2) { - if (!ma_dr_flac__read_uint8(bs, 5, &riceParam)) { - return MA_FALSE; + } else if (residualMethod == DRFLAC_RESIDUAL_CODING_METHOD_PARTITIONED_RICE2) { + if (!drflac__read_uint8(bs, 5, &riceParam)) { + return DRFLAC_FALSE; } if (riceParam == 31) { riceParam = 0xFF; } } if (riceParam != 0xFF) { - if (!ma_dr_flac__read_and_seek_residual__rice(bs, samplesInPartition, riceParam)) { - return MA_FALSE; + if (!drflac__read_and_seek_residual__rice(bs, samplesInPartition, riceParam)) { + return DRFLAC_FALSE; } } else { - ma_uint8 unencodedBitsPerSample = 0; - if (!ma_dr_flac__read_uint8(bs, 5, &unencodedBitsPerSample)) { - return MA_FALSE; + drflac_uint8 unencodedBitsPerSample = 0; + if (!drflac__read_uint8(bs, 5, &unencodedBitsPerSample)) { + return DRFLAC_FALSE; } - if (!ma_dr_flac__seek_bits(bs, unencodedBitsPerSample * samplesInPartition)) { - return MA_FALSE; + if (!drflac__seek_bits(bs, unencodedBitsPerSample * samplesInPartition)) { + return DRFLAC_FALSE; } } if (partitionsRemaining == 1) { @@ -85688,36 +84566,36 @@ static ma_bool32 ma_dr_flac__read_and_seek_residual(ma_dr_flac_bs* bs, ma_uint32 partitionsRemaining -= 1; samplesInPartition = blockSize / (1 << partitionOrder); } - return MA_TRUE; + return DRFLAC_TRUE; } -static ma_bool32 ma_dr_flac__decode_samples__constant(ma_dr_flac_bs* bs, ma_uint32 blockSize, ma_uint32 subframeBitsPerSample, ma_int32* pDecodedSamples) +static drflac_bool32 drflac__decode_samples__constant(drflac_bs* bs, drflac_uint32 blockSize, drflac_uint32 subframeBitsPerSample, drflac_int32* pDecodedSamples) { - ma_uint32 i; - ma_int32 sample; - if (!ma_dr_flac__read_int32(bs, subframeBitsPerSample, &sample)) { - return MA_FALSE; + drflac_uint32 i; + drflac_int32 sample; + if (!drflac__read_int32(bs, subframeBitsPerSample, &sample)) { + return DRFLAC_FALSE; } for (i = 0; i < blockSize; ++i) { pDecodedSamples[i] = sample; } - return MA_TRUE; + return DRFLAC_TRUE; } -static ma_bool32 ma_dr_flac__decode_samples__verbatim(ma_dr_flac_bs* bs, ma_uint32 blockSize, ma_uint32 subframeBitsPerSample, ma_int32* pDecodedSamples) +static drflac_bool32 drflac__decode_samples__verbatim(drflac_bs* bs, drflac_uint32 blockSize, drflac_uint32 subframeBitsPerSample, drflac_int32* pDecodedSamples) { - ma_uint32 i; + drflac_uint32 i; for (i = 0; i < blockSize; ++i) { - ma_int32 sample; - if (!ma_dr_flac__read_int32(bs, subframeBitsPerSample, &sample)) { - return MA_FALSE; + drflac_int32 sample; + if (!drflac__read_int32(bs, subframeBitsPerSample, &sample)) { + return DRFLAC_FALSE; } pDecodedSamples[i] = sample; } - return MA_TRUE; + return DRFLAC_TRUE; } -static ma_bool32 ma_dr_flac__decode_samples__fixed(ma_dr_flac_bs* bs, ma_uint32 blockSize, ma_uint32 subframeBitsPerSample, ma_uint8 lpcOrder, ma_int32* pDecodedSamples) +static drflac_bool32 drflac__decode_samples__fixed(drflac_bs* bs, drflac_uint32 blockSize, drflac_uint32 subframeBitsPerSample, drflac_uint8 lpcOrder, drflac_int32* pDecodedSamples) { - ma_uint32 i; - static ma_int32 lpcCoefficientsTable[5][4] = { + drflac_uint32 i; + static drflac_int32 lpcCoefficientsTable[5][4] = { {0, 0, 0, 0}, {1, 0, 0, 0}, {2, -1, 0, 0}, @@ -85725,122 +84603,122 @@ static ma_bool32 ma_dr_flac__decode_samples__fixed(ma_dr_flac_bs* bs, ma_uint32 {4, -6, 4, -1} }; for (i = 0; i < lpcOrder; ++i) { - ma_int32 sample; - if (!ma_dr_flac__read_int32(bs, subframeBitsPerSample, &sample)) { - return MA_FALSE; + drflac_int32 sample; + if (!drflac__read_int32(bs, subframeBitsPerSample, &sample)) { + return DRFLAC_FALSE; } pDecodedSamples[i] = sample; } - if (!ma_dr_flac__decode_samples_with_residual(bs, subframeBitsPerSample, blockSize, lpcOrder, 0, 4, lpcCoefficientsTable[lpcOrder], pDecodedSamples)) { - return MA_FALSE; + if (!drflac__decode_samples_with_residual(bs, subframeBitsPerSample, blockSize, lpcOrder, 0, 4, lpcCoefficientsTable[lpcOrder], pDecodedSamples)) { + return DRFLAC_FALSE; } - return MA_TRUE; + return DRFLAC_TRUE; } -static ma_bool32 ma_dr_flac__decode_samples__lpc(ma_dr_flac_bs* bs, ma_uint32 blockSize, ma_uint32 bitsPerSample, ma_uint8 lpcOrder, ma_int32* pDecodedSamples) +static drflac_bool32 drflac__decode_samples__lpc(drflac_bs* bs, drflac_uint32 blockSize, drflac_uint32 bitsPerSample, drflac_uint8 lpcOrder, drflac_int32* pDecodedSamples) { - ma_uint8 i; - ma_uint8 lpcPrecision; - ma_int8 lpcShift; - ma_int32 coefficients[32]; + drflac_uint8 i; + drflac_uint8 lpcPrecision; + drflac_int8 lpcShift; + drflac_int32 coefficients[32]; for (i = 0; i < lpcOrder; ++i) { - ma_int32 sample; - if (!ma_dr_flac__read_int32(bs, bitsPerSample, &sample)) { - return MA_FALSE; + drflac_int32 sample; + if (!drflac__read_int32(bs, bitsPerSample, &sample)) { + return DRFLAC_FALSE; } pDecodedSamples[i] = sample; } - if (!ma_dr_flac__read_uint8(bs, 4, &lpcPrecision)) { - return MA_FALSE; + if (!drflac__read_uint8(bs, 4, &lpcPrecision)) { + return DRFLAC_FALSE; } if (lpcPrecision == 15) { - return MA_FALSE; + return DRFLAC_FALSE; } lpcPrecision += 1; - if (!ma_dr_flac__read_int8(bs, 5, &lpcShift)) { - return MA_FALSE; + if (!drflac__read_int8(bs, 5, &lpcShift)) { + return DRFLAC_FALSE; } if (lpcShift < 0) { - return MA_FALSE; + return DRFLAC_FALSE; } - MA_DR_FLAC_ZERO_MEMORY(coefficients, sizeof(coefficients)); + DRFLAC_ZERO_MEMORY(coefficients, sizeof(coefficients)); for (i = 0; i < lpcOrder; ++i) { - if (!ma_dr_flac__read_int32(bs, lpcPrecision, coefficients + i)) { - return MA_FALSE; + if (!drflac__read_int32(bs, lpcPrecision, coefficients + i)) { + return DRFLAC_FALSE; } } - if (!ma_dr_flac__decode_samples_with_residual(bs, bitsPerSample, blockSize, lpcOrder, lpcShift, lpcPrecision, coefficients, pDecodedSamples)) { - return MA_FALSE; + if (!drflac__decode_samples_with_residual(bs, bitsPerSample, blockSize, lpcOrder, lpcShift, lpcPrecision, coefficients, pDecodedSamples)) { + return DRFLAC_FALSE; } - return MA_TRUE; + return DRFLAC_TRUE; } -static ma_bool32 ma_dr_flac__read_next_flac_frame_header(ma_dr_flac_bs* bs, ma_uint8 streaminfoBitsPerSample, ma_dr_flac_frame_header* header) +static drflac_bool32 drflac__read_next_flac_frame_header(drflac_bs* bs, drflac_uint8 streaminfoBitsPerSample, drflac_frame_header* header) { - const ma_uint32 sampleRateTable[12] = {0, 88200, 176400, 192000, 8000, 16000, 22050, 24000, 32000, 44100, 48000, 96000}; - const ma_uint8 bitsPerSampleTable[8] = {0, 8, 12, (ma_uint8)-1, 16, 20, 24, (ma_uint8)-1}; - MA_DR_FLAC_ASSERT(bs != NULL); - MA_DR_FLAC_ASSERT(header != NULL); + const drflac_uint32 sampleRateTable[12] = {0, 88200, 176400, 192000, 8000, 16000, 22050, 24000, 32000, 44100, 48000, 96000}; + const drflac_uint8 bitsPerSampleTable[8] = {0, 8, 12, (drflac_uint8)-1, 16, 20, 24, (drflac_uint8)-1}; + DRFLAC_ASSERT(bs != NULL); + DRFLAC_ASSERT(header != NULL); for (;;) { - ma_uint8 crc8 = 0xCE; - ma_uint8 reserved = 0; - ma_uint8 blockingStrategy = 0; - ma_uint8 blockSize = 0; - ma_uint8 sampleRate = 0; - ma_uint8 channelAssignment = 0; - ma_uint8 bitsPerSample = 0; - ma_bool32 isVariableBlockSize; - if (!ma_dr_flac__find_and_seek_to_next_sync_code(bs)) { - return MA_FALSE; - } - if (!ma_dr_flac__read_uint8(bs, 1, &reserved)) { - return MA_FALSE; + drflac_uint8 crc8 = 0xCE; + drflac_uint8 reserved = 0; + drflac_uint8 blockingStrategy = 0; + drflac_uint8 blockSize = 0; + drflac_uint8 sampleRate = 0; + drflac_uint8 channelAssignment = 0; + drflac_uint8 bitsPerSample = 0; + drflac_bool32 isVariableBlockSize; + if (!drflac__find_and_seek_to_next_sync_code(bs)) { + return DRFLAC_FALSE; + } + if (!drflac__read_uint8(bs, 1, &reserved)) { + return DRFLAC_FALSE; } if (reserved == 1) { continue; } - crc8 = ma_dr_flac_crc8(crc8, reserved, 1); - if (!ma_dr_flac__read_uint8(bs, 1, &blockingStrategy)) { - return MA_FALSE; + crc8 = drflac_crc8(crc8, reserved, 1); + if (!drflac__read_uint8(bs, 1, &blockingStrategy)) { + return DRFLAC_FALSE; } - crc8 = ma_dr_flac_crc8(crc8, blockingStrategy, 1); - if (!ma_dr_flac__read_uint8(bs, 4, &blockSize)) { - return MA_FALSE; + crc8 = drflac_crc8(crc8, blockingStrategy, 1); + if (!drflac__read_uint8(bs, 4, &blockSize)) { + return DRFLAC_FALSE; } if (blockSize == 0) { continue; } - crc8 = ma_dr_flac_crc8(crc8, blockSize, 4); - if (!ma_dr_flac__read_uint8(bs, 4, &sampleRate)) { - return MA_FALSE; + crc8 = drflac_crc8(crc8, blockSize, 4); + if (!drflac__read_uint8(bs, 4, &sampleRate)) { + return DRFLAC_FALSE; } - crc8 = ma_dr_flac_crc8(crc8, sampleRate, 4); - if (!ma_dr_flac__read_uint8(bs, 4, &channelAssignment)) { - return MA_FALSE; + crc8 = drflac_crc8(crc8, sampleRate, 4); + if (!drflac__read_uint8(bs, 4, &channelAssignment)) { + return DRFLAC_FALSE; } if (channelAssignment > 10) { continue; } - crc8 = ma_dr_flac_crc8(crc8, channelAssignment, 4); - if (!ma_dr_flac__read_uint8(bs, 3, &bitsPerSample)) { - return MA_FALSE; + crc8 = drflac_crc8(crc8, channelAssignment, 4); + if (!drflac__read_uint8(bs, 3, &bitsPerSample)) { + return DRFLAC_FALSE; } if (bitsPerSample == 3 || bitsPerSample == 7) { continue; } - crc8 = ma_dr_flac_crc8(crc8, bitsPerSample, 3); - if (!ma_dr_flac__read_uint8(bs, 1, &reserved)) { - return MA_FALSE; + crc8 = drflac_crc8(crc8, bitsPerSample, 3); + if (!drflac__read_uint8(bs, 1, &reserved)) { + return DRFLAC_FALSE; } if (reserved == 1) { continue; } - crc8 = ma_dr_flac_crc8(crc8, reserved, 1); + crc8 = drflac_crc8(crc8, reserved, 1); isVariableBlockSize = blockingStrategy == 1; if (isVariableBlockSize) { - ma_uint64 pcmFrameNumber; - ma_result result = ma_dr_flac__read_utf8_coded_number(bs, &pcmFrameNumber, &crc8); - if (result != MA_SUCCESS) { - if (result == MA_AT_END) { - return MA_FALSE; + drflac_uint64 pcmFrameNumber; + drflac_result result = drflac__read_utf8_coded_number(bs, &pcmFrameNumber, &crc8); + if (result != DRFLAC_SUCCESS) { + if (result == DRFLAC_AT_END) { + return DRFLAC_FALSE; } else { continue; } @@ -85848,61 +84726,61 @@ static ma_bool32 ma_dr_flac__read_next_flac_frame_header(ma_dr_flac_bs* bs, ma_u header->flacFrameNumber = 0; header->pcmFrameNumber = pcmFrameNumber; } else { - ma_uint64 flacFrameNumber = 0; - ma_result result = ma_dr_flac__read_utf8_coded_number(bs, &flacFrameNumber, &crc8); - if (result != MA_SUCCESS) { - if (result == MA_AT_END) { - return MA_FALSE; + drflac_uint64 flacFrameNumber = 0; + drflac_result result = drflac__read_utf8_coded_number(bs, &flacFrameNumber, &crc8); + if (result != DRFLAC_SUCCESS) { + if (result == DRFLAC_AT_END) { + return DRFLAC_FALSE; } else { continue; } } - header->flacFrameNumber = (ma_uint32)flacFrameNumber; + header->flacFrameNumber = (drflac_uint32)flacFrameNumber; header->pcmFrameNumber = 0; } - MA_DR_FLAC_ASSERT(blockSize > 0); + DRFLAC_ASSERT(blockSize > 0); if (blockSize == 1) { header->blockSizeInPCMFrames = 192; } else if (blockSize <= 5) { - MA_DR_FLAC_ASSERT(blockSize >= 2); + DRFLAC_ASSERT(blockSize >= 2); header->blockSizeInPCMFrames = 576 * (1 << (blockSize - 2)); } else if (blockSize == 6) { - if (!ma_dr_flac__read_uint16(bs, 8, &header->blockSizeInPCMFrames)) { - return MA_FALSE; + if (!drflac__read_uint16(bs, 8, &header->blockSizeInPCMFrames)) { + return DRFLAC_FALSE; } - crc8 = ma_dr_flac_crc8(crc8, header->blockSizeInPCMFrames, 8); + crc8 = drflac_crc8(crc8, header->blockSizeInPCMFrames, 8); header->blockSizeInPCMFrames += 1; } else if (blockSize == 7) { - if (!ma_dr_flac__read_uint16(bs, 16, &header->blockSizeInPCMFrames)) { - return MA_FALSE; + if (!drflac__read_uint16(bs, 16, &header->blockSizeInPCMFrames)) { + return DRFLAC_FALSE; } - crc8 = ma_dr_flac_crc8(crc8, header->blockSizeInPCMFrames, 16); + crc8 = drflac_crc8(crc8, header->blockSizeInPCMFrames, 16); if (header->blockSizeInPCMFrames == 0xFFFF) { - return MA_FALSE; + return DRFLAC_FALSE; } header->blockSizeInPCMFrames += 1; } else { - MA_DR_FLAC_ASSERT(blockSize >= 8); + DRFLAC_ASSERT(blockSize >= 8); header->blockSizeInPCMFrames = 256 * (1 << (blockSize - 8)); } if (sampleRate <= 11) { header->sampleRate = sampleRateTable[sampleRate]; } else if (sampleRate == 12) { - if (!ma_dr_flac__read_uint32(bs, 8, &header->sampleRate)) { - return MA_FALSE; + if (!drflac__read_uint32(bs, 8, &header->sampleRate)) { + return DRFLAC_FALSE; } - crc8 = ma_dr_flac_crc8(crc8, header->sampleRate, 8); + crc8 = drflac_crc8(crc8, header->sampleRate, 8); header->sampleRate *= 1000; } else if (sampleRate == 13) { - if (!ma_dr_flac__read_uint32(bs, 16, &header->sampleRate)) { - return MA_FALSE; + if (!drflac__read_uint32(bs, 16, &header->sampleRate)) { + return DRFLAC_FALSE; } - crc8 = ma_dr_flac_crc8(crc8, header->sampleRate, 16); + crc8 = drflac_crc8(crc8, header->sampleRate, 16); } else if (sampleRate == 14) { - if (!ma_dr_flac__read_uint32(bs, 16, &header->sampleRate)) { - return MA_FALSE; + if (!drflac__read_uint32(bs, 16, &header->sampleRate)) { + return DRFLAC_FALSE; } - crc8 = ma_dr_flac_crc8(crc8, header->sampleRate, 16); + crc8 = drflac_crc8(crc8, header->sampleRate, 16); header->sampleRate *= 10; } else { continue; @@ -85913,290 +84791,286 @@ static ma_bool32 ma_dr_flac__read_next_flac_frame_header(ma_dr_flac_bs* bs, ma_u header->bitsPerSample = streaminfoBitsPerSample; } if (header->bitsPerSample != streaminfoBitsPerSample) { - return MA_FALSE; + return DRFLAC_FALSE; } - if (!ma_dr_flac__read_uint8(bs, 8, &header->crc8)) { - return MA_FALSE; + if (!drflac__read_uint8(bs, 8, &header->crc8)) { + return DRFLAC_FALSE; } -#ifndef MA_DR_FLAC_NO_CRC +#ifndef DR_FLAC_NO_CRC if (header->crc8 != crc8) { continue; } #endif - return MA_TRUE; + return DRFLAC_TRUE; } } -static ma_bool32 ma_dr_flac__read_subframe_header(ma_dr_flac_bs* bs, ma_dr_flac_subframe* pSubframe) +static drflac_bool32 drflac__read_subframe_header(drflac_bs* bs, drflac_subframe* pSubframe) { - ma_uint8 header; + drflac_uint8 header; int type; - if (!ma_dr_flac__read_uint8(bs, 8, &header)) { - return MA_FALSE; + if (!drflac__read_uint8(bs, 8, &header)) { + return DRFLAC_FALSE; } if ((header & 0x80) != 0) { - return MA_FALSE; + return DRFLAC_FALSE; } - pSubframe->lpcOrder = 0; type = (header & 0x7E) >> 1; if (type == 0) { - pSubframe->subframeType = MA_DR_FLAC_SUBFRAME_CONSTANT; + pSubframe->subframeType = DRFLAC_SUBFRAME_CONSTANT; } else if (type == 1) { - pSubframe->subframeType = MA_DR_FLAC_SUBFRAME_VERBATIM; + pSubframe->subframeType = DRFLAC_SUBFRAME_VERBATIM; } else { if ((type & 0x20) != 0) { - pSubframe->subframeType = MA_DR_FLAC_SUBFRAME_LPC; - pSubframe->lpcOrder = (ma_uint8)(type & 0x1F) + 1; + pSubframe->subframeType = DRFLAC_SUBFRAME_LPC; + pSubframe->lpcOrder = (drflac_uint8)(type & 0x1F) + 1; } else if ((type & 0x08) != 0) { - pSubframe->subframeType = MA_DR_FLAC_SUBFRAME_FIXED; - pSubframe->lpcOrder = (ma_uint8)(type & 0x07); + pSubframe->subframeType = DRFLAC_SUBFRAME_FIXED; + pSubframe->lpcOrder = (drflac_uint8)(type & 0x07); if (pSubframe->lpcOrder > 4) { - pSubframe->subframeType = MA_DR_FLAC_SUBFRAME_RESERVED; + pSubframe->subframeType = DRFLAC_SUBFRAME_RESERVED; pSubframe->lpcOrder = 0; } } else { - pSubframe->subframeType = MA_DR_FLAC_SUBFRAME_RESERVED; + pSubframe->subframeType = DRFLAC_SUBFRAME_RESERVED; } } - if (pSubframe->subframeType == MA_DR_FLAC_SUBFRAME_RESERVED) { - return MA_FALSE; + if (pSubframe->subframeType == DRFLAC_SUBFRAME_RESERVED) { + return DRFLAC_FALSE; } pSubframe->wastedBitsPerSample = 0; if ((header & 0x01) == 1) { unsigned int wastedBitsPerSample; - if (!ma_dr_flac__seek_past_next_set_bit(bs, &wastedBitsPerSample)) { - return MA_FALSE; + if (!drflac__seek_past_next_set_bit(bs, &wastedBitsPerSample)) { + return DRFLAC_FALSE; } - pSubframe->wastedBitsPerSample = (ma_uint8)wastedBitsPerSample + 1; + pSubframe->wastedBitsPerSample = (drflac_uint8)wastedBitsPerSample + 1; } - return MA_TRUE; + return DRFLAC_TRUE; } -static ma_bool32 ma_dr_flac__decode_subframe(ma_dr_flac_bs* bs, ma_dr_flac_frame* frame, int subframeIndex, ma_int32* pDecodedSamplesOut) +static drflac_bool32 drflac__decode_subframe(drflac_bs* bs, drflac_frame* frame, int subframeIndex, drflac_int32* pDecodedSamplesOut) { - ma_dr_flac_subframe* pSubframe; - ma_uint32 subframeBitsPerSample; - MA_DR_FLAC_ASSERT(bs != NULL); - MA_DR_FLAC_ASSERT(frame != NULL); + drflac_subframe* pSubframe; + drflac_uint32 subframeBitsPerSample; + DRFLAC_ASSERT(bs != NULL); + DRFLAC_ASSERT(frame != NULL); pSubframe = frame->subframes + subframeIndex; - if (!ma_dr_flac__read_subframe_header(bs, pSubframe)) { - return MA_FALSE; + if (!drflac__read_subframe_header(bs, pSubframe)) { + return DRFLAC_FALSE; } subframeBitsPerSample = frame->header.bitsPerSample; - if ((frame->header.channelAssignment == MA_DR_FLAC_CHANNEL_ASSIGNMENT_LEFT_SIDE || frame->header.channelAssignment == MA_DR_FLAC_CHANNEL_ASSIGNMENT_MID_SIDE) && subframeIndex == 1) { + if ((frame->header.channelAssignment == DRFLAC_CHANNEL_ASSIGNMENT_LEFT_SIDE || frame->header.channelAssignment == DRFLAC_CHANNEL_ASSIGNMENT_MID_SIDE) && subframeIndex == 1) { subframeBitsPerSample += 1; - } else if (frame->header.channelAssignment == MA_DR_FLAC_CHANNEL_ASSIGNMENT_RIGHT_SIDE && subframeIndex == 0) { + } else if (frame->header.channelAssignment == DRFLAC_CHANNEL_ASSIGNMENT_RIGHT_SIDE && subframeIndex == 0) { subframeBitsPerSample += 1; } if (subframeBitsPerSample > 32) { - return MA_FALSE; + return DRFLAC_FALSE; } if (pSubframe->wastedBitsPerSample >= subframeBitsPerSample) { - return MA_FALSE; + return DRFLAC_FALSE; } subframeBitsPerSample -= pSubframe->wastedBitsPerSample; pSubframe->pSamplesS32 = pDecodedSamplesOut; - if (frame->header.blockSizeInPCMFrames < pSubframe->lpcOrder) { - return MA_FALSE; - } switch (pSubframe->subframeType) { - case MA_DR_FLAC_SUBFRAME_CONSTANT: + case DRFLAC_SUBFRAME_CONSTANT: { - ma_dr_flac__decode_samples__constant(bs, frame->header.blockSizeInPCMFrames, subframeBitsPerSample, pSubframe->pSamplesS32); + drflac__decode_samples__constant(bs, frame->header.blockSizeInPCMFrames, subframeBitsPerSample, pSubframe->pSamplesS32); } break; - case MA_DR_FLAC_SUBFRAME_VERBATIM: + case DRFLAC_SUBFRAME_VERBATIM: { - ma_dr_flac__decode_samples__verbatim(bs, frame->header.blockSizeInPCMFrames, subframeBitsPerSample, pSubframe->pSamplesS32); + drflac__decode_samples__verbatim(bs, frame->header.blockSizeInPCMFrames, subframeBitsPerSample, pSubframe->pSamplesS32); } break; - case MA_DR_FLAC_SUBFRAME_FIXED: + case DRFLAC_SUBFRAME_FIXED: { - ma_dr_flac__decode_samples__fixed(bs, frame->header.blockSizeInPCMFrames, subframeBitsPerSample, pSubframe->lpcOrder, pSubframe->pSamplesS32); + drflac__decode_samples__fixed(bs, frame->header.blockSizeInPCMFrames, subframeBitsPerSample, pSubframe->lpcOrder, pSubframe->pSamplesS32); } break; - case MA_DR_FLAC_SUBFRAME_LPC: + case DRFLAC_SUBFRAME_LPC: { - ma_dr_flac__decode_samples__lpc(bs, frame->header.blockSizeInPCMFrames, subframeBitsPerSample, pSubframe->lpcOrder, pSubframe->pSamplesS32); + drflac__decode_samples__lpc(bs, frame->header.blockSizeInPCMFrames, subframeBitsPerSample, pSubframe->lpcOrder, pSubframe->pSamplesS32); } break; - default: return MA_FALSE; + default: return DRFLAC_FALSE; } - return MA_TRUE; + return DRFLAC_TRUE; } -static ma_bool32 ma_dr_flac__seek_subframe(ma_dr_flac_bs* bs, ma_dr_flac_frame* frame, int subframeIndex) +static drflac_bool32 drflac__seek_subframe(drflac_bs* bs, drflac_frame* frame, int subframeIndex) { - ma_dr_flac_subframe* pSubframe; - ma_uint32 subframeBitsPerSample; - MA_DR_FLAC_ASSERT(bs != NULL); - MA_DR_FLAC_ASSERT(frame != NULL); + drflac_subframe* pSubframe; + drflac_uint32 subframeBitsPerSample; + DRFLAC_ASSERT(bs != NULL); + DRFLAC_ASSERT(frame != NULL); pSubframe = frame->subframes + subframeIndex; - if (!ma_dr_flac__read_subframe_header(bs, pSubframe)) { - return MA_FALSE; + if (!drflac__read_subframe_header(bs, pSubframe)) { + return DRFLAC_FALSE; } subframeBitsPerSample = frame->header.bitsPerSample; - if ((frame->header.channelAssignment == MA_DR_FLAC_CHANNEL_ASSIGNMENT_LEFT_SIDE || frame->header.channelAssignment == MA_DR_FLAC_CHANNEL_ASSIGNMENT_MID_SIDE) && subframeIndex == 1) { + if ((frame->header.channelAssignment == DRFLAC_CHANNEL_ASSIGNMENT_LEFT_SIDE || frame->header.channelAssignment == DRFLAC_CHANNEL_ASSIGNMENT_MID_SIDE) && subframeIndex == 1) { subframeBitsPerSample += 1; - } else if (frame->header.channelAssignment == MA_DR_FLAC_CHANNEL_ASSIGNMENT_RIGHT_SIDE && subframeIndex == 0) { + } else if (frame->header.channelAssignment == DRFLAC_CHANNEL_ASSIGNMENT_RIGHT_SIDE && subframeIndex == 0) { subframeBitsPerSample += 1; } if (pSubframe->wastedBitsPerSample >= subframeBitsPerSample) { - return MA_FALSE; + return DRFLAC_FALSE; } subframeBitsPerSample -= pSubframe->wastedBitsPerSample; pSubframe->pSamplesS32 = NULL; switch (pSubframe->subframeType) { - case MA_DR_FLAC_SUBFRAME_CONSTANT: + case DRFLAC_SUBFRAME_CONSTANT: { - if (!ma_dr_flac__seek_bits(bs, subframeBitsPerSample)) { - return MA_FALSE; + if (!drflac__seek_bits(bs, subframeBitsPerSample)) { + return DRFLAC_FALSE; } } break; - case MA_DR_FLAC_SUBFRAME_VERBATIM: + case DRFLAC_SUBFRAME_VERBATIM: { unsigned int bitsToSeek = frame->header.blockSizeInPCMFrames * subframeBitsPerSample; - if (!ma_dr_flac__seek_bits(bs, bitsToSeek)) { - return MA_FALSE; + if (!drflac__seek_bits(bs, bitsToSeek)) { + return DRFLAC_FALSE; } } break; - case MA_DR_FLAC_SUBFRAME_FIXED: + case DRFLAC_SUBFRAME_FIXED: { unsigned int bitsToSeek = pSubframe->lpcOrder * subframeBitsPerSample; - if (!ma_dr_flac__seek_bits(bs, bitsToSeek)) { - return MA_FALSE; + if (!drflac__seek_bits(bs, bitsToSeek)) { + return DRFLAC_FALSE; } - if (!ma_dr_flac__read_and_seek_residual(bs, frame->header.blockSizeInPCMFrames, pSubframe->lpcOrder)) { - return MA_FALSE; + if (!drflac__read_and_seek_residual(bs, frame->header.blockSizeInPCMFrames, pSubframe->lpcOrder)) { + return DRFLAC_FALSE; } } break; - case MA_DR_FLAC_SUBFRAME_LPC: + case DRFLAC_SUBFRAME_LPC: { - ma_uint8 lpcPrecision; + drflac_uint8 lpcPrecision; unsigned int bitsToSeek = pSubframe->lpcOrder * subframeBitsPerSample; - if (!ma_dr_flac__seek_bits(bs, bitsToSeek)) { - return MA_FALSE; + if (!drflac__seek_bits(bs, bitsToSeek)) { + return DRFLAC_FALSE; } - if (!ma_dr_flac__read_uint8(bs, 4, &lpcPrecision)) { - return MA_FALSE; + if (!drflac__read_uint8(bs, 4, &lpcPrecision)) { + return DRFLAC_FALSE; } if (lpcPrecision == 15) { - return MA_FALSE; + return DRFLAC_FALSE; } lpcPrecision += 1; bitsToSeek = (pSubframe->lpcOrder * lpcPrecision) + 5; - if (!ma_dr_flac__seek_bits(bs, bitsToSeek)) { - return MA_FALSE; + if (!drflac__seek_bits(bs, bitsToSeek)) { + return DRFLAC_FALSE; } - if (!ma_dr_flac__read_and_seek_residual(bs, frame->header.blockSizeInPCMFrames, pSubframe->lpcOrder)) { - return MA_FALSE; + if (!drflac__read_and_seek_residual(bs, frame->header.blockSizeInPCMFrames, pSubframe->lpcOrder)) { + return DRFLAC_FALSE; } } break; - default: return MA_FALSE; + default: return DRFLAC_FALSE; } - return MA_TRUE; + return DRFLAC_TRUE; } -static MA_INLINE ma_uint8 ma_dr_flac__get_channel_count_from_channel_assignment(ma_int8 channelAssignment) +static DRFLAC_INLINE drflac_uint8 drflac__get_channel_count_from_channel_assignment(drflac_int8 channelAssignment) { - ma_uint8 lookup[] = {1, 2, 3, 4, 5, 6, 7, 8, 2, 2, 2}; - MA_DR_FLAC_ASSERT(channelAssignment <= 10); + drflac_uint8 lookup[] = {1, 2, 3, 4, 5, 6, 7, 8, 2, 2, 2}; + DRFLAC_ASSERT(channelAssignment <= 10); return lookup[channelAssignment]; } -static ma_result ma_dr_flac__decode_flac_frame(ma_dr_flac* pFlac) +static drflac_result drflac__decode_flac_frame(drflac* pFlac) { int channelCount; int i; - ma_uint8 paddingSizeInBits; - ma_uint16 desiredCRC16; -#ifndef MA_DR_FLAC_NO_CRC - ma_uint16 actualCRC16; + drflac_uint8 paddingSizeInBits; + drflac_uint16 desiredCRC16; +#ifndef DR_FLAC_NO_CRC + drflac_uint16 actualCRC16; #endif - MA_DR_FLAC_ZERO_MEMORY(pFlac->currentFLACFrame.subframes, sizeof(pFlac->currentFLACFrame.subframes)); + DRFLAC_ZERO_MEMORY(pFlac->currentFLACFrame.subframes, sizeof(pFlac->currentFLACFrame.subframes)); if (pFlac->currentFLACFrame.header.blockSizeInPCMFrames > pFlac->maxBlockSizeInPCMFrames) { - return MA_ERROR; + return DRFLAC_ERROR; } - channelCount = ma_dr_flac__get_channel_count_from_channel_assignment(pFlac->currentFLACFrame.header.channelAssignment); + channelCount = drflac__get_channel_count_from_channel_assignment(pFlac->currentFLACFrame.header.channelAssignment); if (channelCount != (int)pFlac->channels) { - return MA_ERROR; + return DRFLAC_ERROR; } for (i = 0; i < channelCount; ++i) { - if (!ma_dr_flac__decode_subframe(&pFlac->bs, &pFlac->currentFLACFrame, i, pFlac->pDecodedSamples + (pFlac->currentFLACFrame.header.blockSizeInPCMFrames * i))) { - return MA_ERROR; + if (!drflac__decode_subframe(&pFlac->bs, &pFlac->currentFLACFrame, i, pFlac->pDecodedSamples + (pFlac->currentFLACFrame.header.blockSizeInPCMFrames * i))) { + return DRFLAC_ERROR; } } - paddingSizeInBits = (ma_uint8)(MA_DR_FLAC_CACHE_L1_BITS_REMAINING(&pFlac->bs) & 7); + paddingSizeInBits = (drflac_uint8)(DRFLAC_CACHE_L1_BITS_REMAINING(&pFlac->bs) & 7); if (paddingSizeInBits > 0) { - ma_uint8 padding = 0; - if (!ma_dr_flac__read_uint8(&pFlac->bs, paddingSizeInBits, &padding)) { - return MA_AT_END; + drflac_uint8 padding = 0; + if (!drflac__read_uint8(&pFlac->bs, paddingSizeInBits, &padding)) { + return DRFLAC_AT_END; } } -#ifndef MA_DR_FLAC_NO_CRC - actualCRC16 = ma_dr_flac__flush_crc16(&pFlac->bs); +#ifndef DR_FLAC_NO_CRC + actualCRC16 = drflac__flush_crc16(&pFlac->bs); #endif - if (!ma_dr_flac__read_uint16(&pFlac->bs, 16, &desiredCRC16)) { - return MA_AT_END; + if (!drflac__read_uint16(&pFlac->bs, 16, &desiredCRC16)) { + return DRFLAC_AT_END; } -#ifndef MA_DR_FLAC_NO_CRC +#ifndef DR_FLAC_NO_CRC if (actualCRC16 != desiredCRC16) { - return MA_CRC_MISMATCH; + return DRFLAC_CRC_MISMATCH; } #endif pFlac->currentFLACFrame.pcmFramesRemaining = pFlac->currentFLACFrame.header.blockSizeInPCMFrames; - return MA_SUCCESS; + return DRFLAC_SUCCESS; } -static ma_result ma_dr_flac__seek_flac_frame(ma_dr_flac* pFlac) +static drflac_result drflac__seek_flac_frame(drflac* pFlac) { int channelCount; int i; - ma_uint16 desiredCRC16; -#ifndef MA_DR_FLAC_NO_CRC - ma_uint16 actualCRC16; + drflac_uint16 desiredCRC16; +#ifndef DR_FLAC_NO_CRC + drflac_uint16 actualCRC16; #endif - channelCount = ma_dr_flac__get_channel_count_from_channel_assignment(pFlac->currentFLACFrame.header.channelAssignment); + channelCount = drflac__get_channel_count_from_channel_assignment(pFlac->currentFLACFrame.header.channelAssignment); for (i = 0; i < channelCount; ++i) { - if (!ma_dr_flac__seek_subframe(&pFlac->bs, &pFlac->currentFLACFrame, i)) { - return MA_ERROR; + if (!drflac__seek_subframe(&pFlac->bs, &pFlac->currentFLACFrame, i)) { + return DRFLAC_ERROR; } } - if (!ma_dr_flac__seek_bits(&pFlac->bs, MA_DR_FLAC_CACHE_L1_BITS_REMAINING(&pFlac->bs) & 7)) { - return MA_ERROR; + if (!drflac__seek_bits(&pFlac->bs, DRFLAC_CACHE_L1_BITS_REMAINING(&pFlac->bs) & 7)) { + return DRFLAC_ERROR; } -#ifndef MA_DR_FLAC_NO_CRC - actualCRC16 = ma_dr_flac__flush_crc16(&pFlac->bs); +#ifndef DR_FLAC_NO_CRC + actualCRC16 = drflac__flush_crc16(&pFlac->bs); #endif - if (!ma_dr_flac__read_uint16(&pFlac->bs, 16, &desiredCRC16)) { - return MA_AT_END; + if (!drflac__read_uint16(&pFlac->bs, 16, &desiredCRC16)) { + return DRFLAC_AT_END; } -#ifndef MA_DR_FLAC_NO_CRC +#ifndef DR_FLAC_NO_CRC if (actualCRC16 != desiredCRC16) { - return MA_CRC_MISMATCH; + return DRFLAC_CRC_MISMATCH; } #endif - return MA_SUCCESS; + return DRFLAC_SUCCESS; } -static ma_bool32 ma_dr_flac__read_and_decode_next_flac_frame(ma_dr_flac* pFlac) +static drflac_bool32 drflac__read_and_decode_next_flac_frame(drflac* pFlac) { - MA_DR_FLAC_ASSERT(pFlac != NULL); + DRFLAC_ASSERT(pFlac != NULL); for (;;) { - ma_result result; - if (!ma_dr_flac__read_next_flac_frame_header(&pFlac->bs, pFlac->bitsPerSample, &pFlac->currentFLACFrame.header)) { - return MA_FALSE; + drflac_result result; + if (!drflac__read_next_flac_frame_header(&pFlac->bs, pFlac->bitsPerSample, &pFlac->currentFLACFrame.header)) { + return DRFLAC_FALSE; } - result = ma_dr_flac__decode_flac_frame(pFlac); - if (result != MA_SUCCESS) { - if (result == MA_CRC_MISMATCH) { + result = drflac__decode_flac_frame(pFlac); + if (result != DRFLAC_SUCCESS) { + if (result == DRFLAC_CRC_MISMATCH) { continue; } else { - return MA_FALSE; + return DRFLAC_FALSE; } } - return MA_TRUE; + return DRFLAC_TRUE; } } -static void ma_dr_flac__get_pcm_frame_range_of_current_flac_frame(ma_dr_flac* pFlac, ma_uint64* pFirstPCMFrame, ma_uint64* pLastPCMFrame) +static void drflac__get_pcm_frame_range_of_current_flac_frame(drflac* pFlac, drflac_uint64* pFirstPCMFrame, drflac_uint64* pLastPCMFrame) { - ma_uint64 firstPCMFrame; - ma_uint64 lastPCMFrame; - MA_DR_FLAC_ASSERT(pFlac != NULL); + drflac_uint64 firstPCMFrame; + drflac_uint64 lastPCMFrame; + DRFLAC_ASSERT(pFlac != NULL); firstPCMFrame = pFlac->currentFLACFrame.header.pcmFrameNumber; if (firstPCMFrame == 0) { - firstPCMFrame = ((ma_uint64)pFlac->currentFLACFrame.header.flacFrameNumber) * pFlac->maxBlockSizeInPCMFrames; + firstPCMFrame = ((drflac_uint64)pFlac->currentFLACFrame.header.flacFrameNumber) * pFlac->maxBlockSizeInPCMFrames; } lastPCMFrame = firstPCMFrame + pFlac->currentFLACFrame.header.blockSizeInPCMFrames; if (lastPCMFrame > 0) { @@ -86209,32 +85083,32 @@ static void ma_dr_flac__get_pcm_frame_range_of_current_flac_frame(ma_dr_flac* pF *pLastPCMFrame = lastPCMFrame; } } -static ma_bool32 ma_dr_flac__seek_to_first_frame(ma_dr_flac* pFlac) +static drflac_bool32 drflac__seek_to_first_frame(drflac* pFlac) { - ma_bool32 result; - MA_DR_FLAC_ASSERT(pFlac != NULL); - result = ma_dr_flac__seek_to_byte(&pFlac->bs, pFlac->firstFLACFramePosInBytes); - MA_DR_FLAC_ZERO_MEMORY(&pFlac->currentFLACFrame, sizeof(pFlac->currentFLACFrame)); + drflac_bool32 result; + DRFLAC_ASSERT(pFlac != NULL); + result = drflac__seek_to_byte(&pFlac->bs, pFlac->firstFLACFramePosInBytes); + DRFLAC_ZERO_MEMORY(&pFlac->currentFLACFrame, sizeof(pFlac->currentFLACFrame)); pFlac->currentPCMFrame = 0; return result; } -static MA_INLINE ma_result ma_dr_flac__seek_to_next_flac_frame(ma_dr_flac* pFlac) +static DRFLAC_INLINE drflac_result drflac__seek_to_next_flac_frame(drflac* pFlac) { - MA_DR_FLAC_ASSERT(pFlac != NULL); - return ma_dr_flac__seek_flac_frame(pFlac); + DRFLAC_ASSERT(pFlac != NULL); + return drflac__seek_flac_frame(pFlac); } -static ma_uint64 ma_dr_flac__seek_forward_by_pcm_frames(ma_dr_flac* pFlac, ma_uint64 pcmFramesToSeek) +static drflac_uint64 drflac__seek_forward_by_pcm_frames(drflac* pFlac, drflac_uint64 pcmFramesToSeek) { - ma_uint64 pcmFramesRead = 0; + drflac_uint64 pcmFramesRead = 0; while (pcmFramesToSeek > 0) { if (pFlac->currentFLACFrame.pcmFramesRemaining == 0) { - if (!ma_dr_flac__read_and_decode_next_flac_frame(pFlac)) { + if (!drflac__read_and_decode_next_flac_frame(pFlac)) { break; } } else { if (pFlac->currentFLACFrame.pcmFramesRemaining > pcmFramesToSeek) { pcmFramesRead += pcmFramesToSeek; - pFlac->currentFLACFrame.pcmFramesRemaining -= (ma_uint32)pcmFramesToSeek; + pFlac->currentFLACFrame.pcmFramesRemaining -= (drflac_uint32)pcmFramesToSeek; pcmFramesToSeek = 0; } else { pcmFramesRead += pFlac->currentFLACFrame.pcmFramesRemaining; @@ -86246,107 +85120,107 @@ static ma_uint64 ma_dr_flac__seek_forward_by_pcm_frames(ma_dr_flac* pFlac, ma_ui pFlac->currentPCMFrame += pcmFramesRead; return pcmFramesRead; } -static ma_bool32 ma_dr_flac__seek_to_pcm_frame__brute_force(ma_dr_flac* pFlac, ma_uint64 pcmFrameIndex) +static drflac_bool32 drflac__seek_to_pcm_frame__brute_force(drflac* pFlac, drflac_uint64 pcmFrameIndex) { - ma_bool32 isMidFrame = MA_FALSE; - ma_uint64 runningPCMFrameCount; - MA_DR_FLAC_ASSERT(pFlac != NULL); + drflac_bool32 isMidFrame = DRFLAC_FALSE; + drflac_uint64 runningPCMFrameCount; + DRFLAC_ASSERT(pFlac != NULL); if (pcmFrameIndex >= pFlac->currentPCMFrame) { runningPCMFrameCount = pFlac->currentPCMFrame; if (pFlac->currentPCMFrame == 0 && pFlac->currentFLACFrame.pcmFramesRemaining == 0) { - if (!ma_dr_flac__read_next_flac_frame_header(&pFlac->bs, pFlac->bitsPerSample, &pFlac->currentFLACFrame.header)) { - return MA_FALSE; + if (!drflac__read_next_flac_frame_header(&pFlac->bs, pFlac->bitsPerSample, &pFlac->currentFLACFrame.header)) { + return DRFLAC_FALSE; } } else { - isMidFrame = MA_TRUE; + isMidFrame = DRFLAC_TRUE; } } else { runningPCMFrameCount = 0; - if (!ma_dr_flac__seek_to_first_frame(pFlac)) { - return MA_FALSE; + if (!drflac__seek_to_first_frame(pFlac)) { + return DRFLAC_FALSE; } - if (!ma_dr_flac__read_next_flac_frame_header(&pFlac->bs, pFlac->bitsPerSample, &pFlac->currentFLACFrame.header)) { - return MA_FALSE; + if (!drflac__read_next_flac_frame_header(&pFlac->bs, pFlac->bitsPerSample, &pFlac->currentFLACFrame.header)) { + return DRFLAC_FALSE; } } for (;;) { - ma_uint64 pcmFrameCountInThisFLACFrame; - ma_uint64 firstPCMFrameInFLACFrame = 0; - ma_uint64 lastPCMFrameInFLACFrame = 0; - ma_dr_flac__get_pcm_frame_range_of_current_flac_frame(pFlac, &firstPCMFrameInFLACFrame, &lastPCMFrameInFLACFrame); + drflac_uint64 pcmFrameCountInThisFLACFrame; + drflac_uint64 firstPCMFrameInFLACFrame = 0; + drflac_uint64 lastPCMFrameInFLACFrame = 0; + drflac__get_pcm_frame_range_of_current_flac_frame(pFlac, &firstPCMFrameInFLACFrame, &lastPCMFrameInFLACFrame); pcmFrameCountInThisFLACFrame = (lastPCMFrameInFLACFrame - firstPCMFrameInFLACFrame) + 1; if (pcmFrameIndex < (runningPCMFrameCount + pcmFrameCountInThisFLACFrame)) { - ma_uint64 pcmFramesToDecode = pcmFrameIndex - runningPCMFrameCount; + drflac_uint64 pcmFramesToDecode = pcmFrameIndex - runningPCMFrameCount; if (!isMidFrame) { - ma_result result = ma_dr_flac__decode_flac_frame(pFlac); - if (result == MA_SUCCESS) { - return ma_dr_flac__seek_forward_by_pcm_frames(pFlac, pcmFramesToDecode) == pcmFramesToDecode; + drflac_result result = drflac__decode_flac_frame(pFlac); + if (result == DRFLAC_SUCCESS) { + return drflac__seek_forward_by_pcm_frames(pFlac, pcmFramesToDecode) == pcmFramesToDecode; } else { - if (result == MA_CRC_MISMATCH) { + if (result == DRFLAC_CRC_MISMATCH) { goto next_iteration; } else { - return MA_FALSE; + return DRFLAC_FALSE; } } } else { - return ma_dr_flac__seek_forward_by_pcm_frames(pFlac, pcmFramesToDecode) == pcmFramesToDecode; + return drflac__seek_forward_by_pcm_frames(pFlac, pcmFramesToDecode) == pcmFramesToDecode; } } else { if (!isMidFrame) { - ma_result result = ma_dr_flac__seek_to_next_flac_frame(pFlac); - if (result == MA_SUCCESS) { + drflac_result result = drflac__seek_to_next_flac_frame(pFlac); + if (result == DRFLAC_SUCCESS) { runningPCMFrameCount += pcmFrameCountInThisFLACFrame; } else { - if (result == MA_CRC_MISMATCH) { + if (result == DRFLAC_CRC_MISMATCH) { goto next_iteration; } else { - return MA_FALSE; + return DRFLAC_FALSE; } } } else { runningPCMFrameCount += pFlac->currentFLACFrame.pcmFramesRemaining; pFlac->currentFLACFrame.pcmFramesRemaining = 0; - isMidFrame = MA_FALSE; + isMidFrame = DRFLAC_FALSE; } if (pcmFrameIndex == pFlac->totalPCMFrameCount && runningPCMFrameCount == pFlac->totalPCMFrameCount) { - return MA_TRUE; + return DRFLAC_TRUE; } } next_iteration: - if (!ma_dr_flac__read_next_flac_frame_header(&pFlac->bs, pFlac->bitsPerSample, &pFlac->currentFLACFrame.header)) { - return MA_FALSE; + if (!drflac__read_next_flac_frame_header(&pFlac->bs, pFlac->bitsPerSample, &pFlac->currentFLACFrame.header)) { + return DRFLAC_FALSE; } } } -#if !defined(MA_DR_FLAC_NO_CRC) -#define MA_DR_FLAC_BINARY_SEARCH_APPROX_COMPRESSION_RATIO 0.6f -static ma_bool32 ma_dr_flac__seek_to_approximate_flac_frame_to_byte(ma_dr_flac* pFlac, ma_uint64 targetByte, ma_uint64 rangeLo, ma_uint64 rangeHi, ma_uint64* pLastSuccessfulSeekOffset) +#if !defined(DR_FLAC_NO_CRC) +#define DRFLAC_BINARY_SEARCH_APPROX_COMPRESSION_RATIO 0.6f +static drflac_bool32 drflac__seek_to_approximate_flac_frame_to_byte(drflac* pFlac, drflac_uint64 targetByte, drflac_uint64 rangeLo, drflac_uint64 rangeHi, drflac_uint64* pLastSuccessfulSeekOffset) { - MA_DR_FLAC_ASSERT(pFlac != NULL); - MA_DR_FLAC_ASSERT(pLastSuccessfulSeekOffset != NULL); - MA_DR_FLAC_ASSERT(targetByte >= rangeLo); - MA_DR_FLAC_ASSERT(targetByte <= rangeHi); + DRFLAC_ASSERT(pFlac != NULL); + DRFLAC_ASSERT(pLastSuccessfulSeekOffset != NULL); + DRFLAC_ASSERT(targetByte >= rangeLo); + DRFLAC_ASSERT(targetByte <= rangeHi); *pLastSuccessfulSeekOffset = pFlac->firstFLACFramePosInBytes; for (;;) { - ma_uint64 lastTargetByte = targetByte; - if (!ma_dr_flac__seek_to_byte(&pFlac->bs, targetByte)) { + drflac_uint64 lastTargetByte = targetByte; + if (!drflac__seek_to_byte(&pFlac->bs, targetByte)) { if (targetByte == 0) { - ma_dr_flac__seek_to_first_frame(pFlac); - return MA_FALSE; + drflac__seek_to_first_frame(pFlac); + return DRFLAC_FALSE; } targetByte = rangeLo + ((rangeHi - rangeLo)/2); rangeHi = targetByte; } else { - MA_DR_FLAC_ZERO_MEMORY(&pFlac->currentFLACFrame, sizeof(pFlac->currentFLACFrame)); + DRFLAC_ZERO_MEMORY(&pFlac->currentFLACFrame, sizeof(pFlac->currentFLACFrame)); #if 1 - if (!ma_dr_flac__read_and_decode_next_flac_frame(pFlac)) { + if (!drflac__read_and_decode_next_flac_frame(pFlac)) { targetByte = rangeLo + ((rangeHi - rangeLo)/2); rangeHi = targetByte; } else { break; } #else - if (!ma_dr_flac__read_next_flac_frame_header(&pFlac->bs, pFlac->bitsPerSample, &pFlac->currentFLACFrame.header)) { + if (!drflac__read_next_flac_frame_header(&pFlac->bs, pFlac->bitsPerSample, &pFlac->currentFLACFrame.header)) { targetByte = rangeLo + ((rangeHi - rangeLo)/2); rangeHi = targetByte; } else { @@ -86355,48 +85229,48 @@ static ma_bool32 ma_dr_flac__seek_to_approximate_flac_frame_to_byte(ma_dr_flac* #endif } if(targetByte == lastTargetByte) { - return MA_FALSE; + return DRFLAC_FALSE; } } - ma_dr_flac__get_pcm_frame_range_of_current_flac_frame(pFlac, &pFlac->currentPCMFrame, NULL); - MA_DR_FLAC_ASSERT(targetByte <= rangeHi); + drflac__get_pcm_frame_range_of_current_flac_frame(pFlac, &pFlac->currentPCMFrame, NULL); + DRFLAC_ASSERT(targetByte <= rangeHi); *pLastSuccessfulSeekOffset = targetByte; - return MA_TRUE; + return DRFLAC_TRUE; } -static ma_bool32 ma_dr_flac__decode_flac_frame_and_seek_forward_by_pcm_frames(ma_dr_flac* pFlac, ma_uint64 offset) +static drflac_bool32 drflac__decode_flac_frame_and_seek_forward_by_pcm_frames(drflac* pFlac, drflac_uint64 offset) { #if 0 - if (ma_dr_flac__decode_flac_frame(pFlac) != MA_SUCCESS) { - if (ma_dr_flac__read_and_decode_next_flac_frame(pFlac) == MA_FALSE) { - return MA_FALSE; + if (drflac__decode_flac_frame(pFlac) != DRFLAC_SUCCESS) { + if (drflac__read_and_decode_next_flac_frame(pFlac) == DRFLAC_FALSE) { + return DRFLAC_FALSE; } } #endif - return ma_dr_flac__seek_forward_by_pcm_frames(pFlac, offset) == offset; + return drflac__seek_forward_by_pcm_frames(pFlac, offset) == offset; } -static ma_bool32 ma_dr_flac__seek_to_pcm_frame__binary_search_internal(ma_dr_flac* pFlac, ma_uint64 pcmFrameIndex, ma_uint64 byteRangeLo, ma_uint64 byteRangeHi) +static drflac_bool32 drflac__seek_to_pcm_frame__binary_search_internal(drflac* pFlac, drflac_uint64 pcmFrameIndex, drflac_uint64 byteRangeLo, drflac_uint64 byteRangeHi) { - ma_uint64 targetByte; - ma_uint64 pcmRangeLo = pFlac->totalPCMFrameCount; - ma_uint64 pcmRangeHi = 0; - ma_uint64 lastSuccessfulSeekOffset = (ma_uint64)-1; - ma_uint64 closestSeekOffsetBeforeTargetPCMFrame = byteRangeLo; - ma_uint32 seekForwardThreshold = (pFlac->maxBlockSizeInPCMFrames != 0) ? pFlac->maxBlockSizeInPCMFrames*2 : 4096; - targetByte = byteRangeLo + (ma_uint64)(((ma_int64)((pcmFrameIndex - pFlac->currentPCMFrame) * pFlac->channels * pFlac->bitsPerSample)/8.0f) * MA_DR_FLAC_BINARY_SEARCH_APPROX_COMPRESSION_RATIO); + drflac_uint64 targetByte; + drflac_uint64 pcmRangeLo = pFlac->totalPCMFrameCount; + drflac_uint64 pcmRangeHi = 0; + drflac_uint64 lastSuccessfulSeekOffset = (drflac_uint64)-1; + drflac_uint64 closestSeekOffsetBeforeTargetPCMFrame = byteRangeLo; + drflac_uint32 seekForwardThreshold = (pFlac->maxBlockSizeInPCMFrames != 0) ? pFlac->maxBlockSizeInPCMFrames*2 : 4096; + targetByte = byteRangeLo + (drflac_uint64)(((drflac_int64)((pcmFrameIndex - pFlac->currentPCMFrame) * pFlac->channels * pFlac->bitsPerSample)/8.0f) * DRFLAC_BINARY_SEARCH_APPROX_COMPRESSION_RATIO); if (targetByte > byteRangeHi) { targetByte = byteRangeHi; } for (;;) { - if (ma_dr_flac__seek_to_approximate_flac_frame_to_byte(pFlac, targetByte, byteRangeLo, byteRangeHi, &lastSuccessfulSeekOffset)) { - ma_uint64 newPCMRangeLo; - ma_uint64 newPCMRangeHi; - ma_dr_flac__get_pcm_frame_range_of_current_flac_frame(pFlac, &newPCMRangeLo, &newPCMRangeHi); + if (drflac__seek_to_approximate_flac_frame_to_byte(pFlac, targetByte, byteRangeLo, byteRangeHi, &lastSuccessfulSeekOffset)) { + drflac_uint64 newPCMRangeLo; + drflac_uint64 newPCMRangeHi; + drflac__get_pcm_frame_range_of_current_flac_frame(pFlac, &newPCMRangeLo, &newPCMRangeHi); if (pcmRangeLo == newPCMRangeLo) { - if (!ma_dr_flac__seek_to_approximate_flac_frame_to_byte(pFlac, closestSeekOffsetBeforeTargetPCMFrame, closestSeekOffsetBeforeTargetPCMFrame, byteRangeHi, &lastSuccessfulSeekOffset)) { + if (!drflac__seek_to_approximate_flac_frame_to_byte(pFlac, closestSeekOffsetBeforeTargetPCMFrame, closestSeekOffsetBeforeTargetPCMFrame, byteRangeHi, &lastSuccessfulSeekOffset)) { break; } - if (ma_dr_flac__decode_flac_frame_and_seek_forward_by_pcm_frames(pFlac, pcmFrameIndex - pFlac->currentPCMFrame)) { - return MA_TRUE; + if (drflac__decode_flac_frame_and_seek_forward_by_pcm_frames(pFlac, pcmFrameIndex - pFlac->currentPCMFrame)) { + return DRFLAC_TRUE; } else { break; } @@ -86404,13 +85278,13 @@ static ma_bool32 ma_dr_flac__seek_to_pcm_frame__binary_search_internal(ma_dr_fla pcmRangeLo = newPCMRangeLo; pcmRangeHi = newPCMRangeHi; if (pcmRangeLo <= pcmFrameIndex && pcmRangeHi >= pcmFrameIndex) { - if (ma_dr_flac__decode_flac_frame_and_seek_forward_by_pcm_frames(pFlac, pcmFrameIndex - pFlac->currentPCMFrame) ) { - return MA_TRUE; + if (drflac__decode_flac_frame_and_seek_forward_by_pcm_frames(pFlac, pcmFrameIndex - pFlac->currentPCMFrame) ) { + return DRFLAC_TRUE; } else { break; } } else { - const float approxCompressionRatio = (ma_int64)(lastSuccessfulSeekOffset - pFlac->firstFLACFramePosInBytes) / ((ma_int64)(pcmRangeLo * pFlac->channels * pFlac->bitsPerSample)/8.0f); + const float approxCompressionRatio = (drflac_int64)(lastSuccessfulSeekOffset - pFlac->firstFLACFramePosInBytes) / ((drflac_int64)(pcmRangeLo * pFlac->channels * pFlac->bitsPerSample)/8.0f); if (pcmRangeLo > pcmFrameIndex) { byteRangeHi = lastSuccessfulSeekOffset; if (byteRangeLo > byteRangeHi) { @@ -86422,8 +85296,8 @@ static ma_bool32 ma_dr_flac__seek_to_pcm_frame__binary_search_internal(ma_dr_fla } } else { if ((pcmFrameIndex - pcmRangeLo) < seekForwardThreshold) { - if (ma_dr_flac__decode_flac_frame_and_seek_forward_by_pcm_frames(pFlac, pcmFrameIndex - pFlac->currentPCMFrame)) { - return MA_TRUE; + if (drflac__decode_flac_frame_and_seek_forward_by_pcm_frames(pFlac, pcmFrameIndex - pFlac->currentPCMFrame)) { + return DRFLAC_TRUE; } else { break; } @@ -86432,7 +85306,7 @@ static ma_bool32 ma_dr_flac__seek_to_pcm_frame__binary_search_internal(ma_dr_fla if (byteRangeHi < byteRangeLo) { byteRangeHi = byteRangeLo; } - targetByte = lastSuccessfulSeekOffset + (ma_uint64)(((ma_int64)((pcmFrameIndex-pcmRangeLo) * pFlac->channels * pFlac->bitsPerSample)/8.0f) * approxCompressionRatio); + targetByte = lastSuccessfulSeekOffset + (drflac_uint64)(((drflac_int64)((pcmFrameIndex-pcmRangeLo) * pFlac->channels * pFlac->bitsPerSample)/8.0f) * approxCompressionRatio); if (targetByte > byteRangeHi) { targetByte = byteRangeHi; } @@ -86446,37 +85320,37 @@ static ma_bool32 ma_dr_flac__seek_to_pcm_frame__binary_search_internal(ma_dr_fla break; } } - ma_dr_flac__seek_to_first_frame(pFlac); - return MA_FALSE; + drflac__seek_to_first_frame(pFlac); + return DRFLAC_FALSE; } -static ma_bool32 ma_dr_flac__seek_to_pcm_frame__binary_search(ma_dr_flac* pFlac, ma_uint64 pcmFrameIndex) +static drflac_bool32 drflac__seek_to_pcm_frame__binary_search(drflac* pFlac, drflac_uint64 pcmFrameIndex) { - ma_uint64 byteRangeLo; - ma_uint64 byteRangeHi; - ma_uint32 seekForwardThreshold = (pFlac->maxBlockSizeInPCMFrames != 0) ? pFlac->maxBlockSizeInPCMFrames*2 : 4096; - if (ma_dr_flac__seek_to_first_frame(pFlac) == MA_FALSE) { - return MA_FALSE; + drflac_uint64 byteRangeLo; + drflac_uint64 byteRangeHi; + drflac_uint32 seekForwardThreshold = (pFlac->maxBlockSizeInPCMFrames != 0) ? pFlac->maxBlockSizeInPCMFrames*2 : 4096; + if (drflac__seek_to_first_frame(pFlac) == DRFLAC_FALSE) { + return DRFLAC_FALSE; } if (pcmFrameIndex < seekForwardThreshold) { - return ma_dr_flac__seek_forward_by_pcm_frames(pFlac, pcmFrameIndex) == pcmFrameIndex; + return drflac__seek_forward_by_pcm_frames(pFlac, pcmFrameIndex) == pcmFrameIndex; } byteRangeLo = pFlac->firstFLACFramePosInBytes; - byteRangeHi = pFlac->firstFLACFramePosInBytes + (ma_uint64)((ma_int64)(pFlac->totalPCMFrameCount * pFlac->channels * pFlac->bitsPerSample)/8.0f); - return ma_dr_flac__seek_to_pcm_frame__binary_search_internal(pFlac, pcmFrameIndex, byteRangeLo, byteRangeHi); + byteRangeHi = pFlac->firstFLACFramePosInBytes + (drflac_uint64)((drflac_int64)(pFlac->totalPCMFrameCount * pFlac->channels * pFlac->bitsPerSample)/8.0f); + return drflac__seek_to_pcm_frame__binary_search_internal(pFlac, pcmFrameIndex, byteRangeLo, byteRangeHi); } #endif -static ma_bool32 ma_dr_flac__seek_to_pcm_frame__seek_table(ma_dr_flac* pFlac, ma_uint64 pcmFrameIndex) +static drflac_bool32 drflac__seek_to_pcm_frame__seek_table(drflac* pFlac, drflac_uint64 pcmFrameIndex) { - ma_uint32 iClosestSeekpoint = 0; - ma_bool32 isMidFrame = MA_FALSE; - ma_uint64 runningPCMFrameCount; - ma_uint32 iSeekpoint; - MA_DR_FLAC_ASSERT(pFlac != NULL); + drflac_uint32 iClosestSeekpoint = 0; + drflac_bool32 isMidFrame = DRFLAC_FALSE; + drflac_uint64 runningPCMFrameCount; + drflac_uint32 iSeekpoint; + DRFLAC_ASSERT(pFlac != NULL); if (pFlac->pSeekpoints == NULL || pFlac->seekpointCount == 0) { - return MA_FALSE; + return DRFLAC_FALSE; } if (pFlac->pSeekpoints[0].firstPCMFrame > pcmFrameIndex) { - return MA_FALSE; + return DRFLAC_FALSE; } for (iSeekpoint = 0; iSeekpoint < pFlac->seekpointCount; ++iSeekpoint) { if (pFlac->pSeekpoints[iSeekpoint].firstPCMFrame >= pcmFrameIndex) { @@ -86485,31 +85359,31 @@ static ma_bool32 ma_dr_flac__seek_to_pcm_frame__seek_table(ma_dr_flac* pFlac, ma iClosestSeekpoint = iSeekpoint; } if (pFlac->pSeekpoints[iClosestSeekpoint].pcmFrameCount == 0 || pFlac->pSeekpoints[iClosestSeekpoint].pcmFrameCount > pFlac->maxBlockSizeInPCMFrames) { - return MA_FALSE; + return DRFLAC_FALSE; } if (pFlac->pSeekpoints[iClosestSeekpoint].firstPCMFrame > pFlac->totalPCMFrameCount && pFlac->totalPCMFrameCount > 0) { - return MA_FALSE; + return DRFLAC_FALSE; } -#if !defined(MA_DR_FLAC_NO_CRC) +#if !defined(DR_FLAC_NO_CRC) if (pFlac->totalPCMFrameCount > 0) { - ma_uint64 byteRangeLo; - ma_uint64 byteRangeHi; - byteRangeHi = pFlac->firstFLACFramePosInBytes + (ma_uint64)((ma_int64)(pFlac->totalPCMFrameCount * pFlac->channels * pFlac->bitsPerSample)/8.0f); + drflac_uint64 byteRangeLo; + drflac_uint64 byteRangeHi; + byteRangeHi = pFlac->firstFLACFramePosInBytes + (drflac_uint64)((drflac_int64)(pFlac->totalPCMFrameCount * pFlac->channels * pFlac->bitsPerSample)/8.0f); byteRangeLo = pFlac->firstFLACFramePosInBytes + pFlac->pSeekpoints[iClosestSeekpoint].flacFrameOffset; if (iClosestSeekpoint < pFlac->seekpointCount-1) { - ma_uint32 iNextSeekpoint = iClosestSeekpoint + 1; + drflac_uint32 iNextSeekpoint = iClosestSeekpoint + 1; if (pFlac->pSeekpoints[iClosestSeekpoint].flacFrameOffset >= pFlac->pSeekpoints[iNextSeekpoint].flacFrameOffset || pFlac->pSeekpoints[iNextSeekpoint].pcmFrameCount == 0) { - return MA_FALSE; + return DRFLAC_FALSE; } - if (pFlac->pSeekpoints[iNextSeekpoint].firstPCMFrame != (((ma_uint64)0xFFFFFFFF << 32) | 0xFFFFFFFF)) { + if (pFlac->pSeekpoints[iNextSeekpoint].firstPCMFrame != (((drflac_uint64)0xFFFFFFFF << 32) | 0xFFFFFFFF)) { byteRangeHi = pFlac->firstFLACFramePosInBytes + pFlac->pSeekpoints[iNextSeekpoint].flacFrameOffset - 1; } } - if (ma_dr_flac__seek_to_byte(&pFlac->bs, pFlac->firstFLACFramePosInBytes + pFlac->pSeekpoints[iClosestSeekpoint].flacFrameOffset)) { - if (ma_dr_flac__read_next_flac_frame_header(&pFlac->bs, pFlac->bitsPerSample, &pFlac->currentFLACFrame.header)) { - ma_dr_flac__get_pcm_frame_range_of_current_flac_frame(pFlac, &pFlac->currentPCMFrame, NULL); - if (ma_dr_flac__seek_to_pcm_frame__binary_search_internal(pFlac, pcmFrameIndex, byteRangeLo, byteRangeHi)) { - return MA_TRUE; + if (drflac__seek_to_byte(&pFlac->bs, pFlac->firstFLACFramePosInBytes + pFlac->pSeekpoints[iClosestSeekpoint].flacFrameOffset)) { + if (drflac__read_next_flac_frame_header(&pFlac->bs, pFlac->bitsPerSample, &pFlac->currentFLACFrame.header)) { + drflac__get_pcm_frame_range_of_current_flac_frame(pFlac, &pFlac->currentPCMFrame, NULL); + if (drflac__seek_to_pcm_frame__binary_search_internal(pFlac, pcmFrameIndex, byteRangeLo, byteRangeHi)) { + return DRFLAC_TRUE; } } } @@ -86518,173 +85392,173 @@ static ma_bool32 ma_dr_flac__seek_to_pcm_frame__seek_table(ma_dr_flac* pFlac, ma if (pcmFrameIndex >= pFlac->currentPCMFrame && pFlac->pSeekpoints[iClosestSeekpoint].firstPCMFrame <= pFlac->currentPCMFrame) { runningPCMFrameCount = pFlac->currentPCMFrame; if (pFlac->currentPCMFrame == 0 && pFlac->currentFLACFrame.pcmFramesRemaining == 0) { - if (!ma_dr_flac__read_next_flac_frame_header(&pFlac->bs, pFlac->bitsPerSample, &pFlac->currentFLACFrame.header)) { - return MA_FALSE; + if (!drflac__read_next_flac_frame_header(&pFlac->bs, pFlac->bitsPerSample, &pFlac->currentFLACFrame.header)) { + return DRFLAC_FALSE; } } else { - isMidFrame = MA_TRUE; + isMidFrame = DRFLAC_TRUE; } } else { runningPCMFrameCount = pFlac->pSeekpoints[iClosestSeekpoint].firstPCMFrame; - if (!ma_dr_flac__seek_to_byte(&pFlac->bs, pFlac->firstFLACFramePosInBytes + pFlac->pSeekpoints[iClosestSeekpoint].flacFrameOffset)) { - return MA_FALSE; + if (!drflac__seek_to_byte(&pFlac->bs, pFlac->firstFLACFramePosInBytes + pFlac->pSeekpoints[iClosestSeekpoint].flacFrameOffset)) { + return DRFLAC_FALSE; } - if (!ma_dr_flac__read_next_flac_frame_header(&pFlac->bs, pFlac->bitsPerSample, &pFlac->currentFLACFrame.header)) { - return MA_FALSE; + if (!drflac__read_next_flac_frame_header(&pFlac->bs, pFlac->bitsPerSample, &pFlac->currentFLACFrame.header)) { + return DRFLAC_FALSE; } } for (;;) { - ma_uint64 pcmFrameCountInThisFLACFrame; - ma_uint64 firstPCMFrameInFLACFrame = 0; - ma_uint64 lastPCMFrameInFLACFrame = 0; - ma_dr_flac__get_pcm_frame_range_of_current_flac_frame(pFlac, &firstPCMFrameInFLACFrame, &lastPCMFrameInFLACFrame); + drflac_uint64 pcmFrameCountInThisFLACFrame; + drflac_uint64 firstPCMFrameInFLACFrame = 0; + drflac_uint64 lastPCMFrameInFLACFrame = 0; + drflac__get_pcm_frame_range_of_current_flac_frame(pFlac, &firstPCMFrameInFLACFrame, &lastPCMFrameInFLACFrame); pcmFrameCountInThisFLACFrame = (lastPCMFrameInFLACFrame - firstPCMFrameInFLACFrame) + 1; if (pcmFrameIndex < (runningPCMFrameCount + pcmFrameCountInThisFLACFrame)) { - ma_uint64 pcmFramesToDecode = pcmFrameIndex - runningPCMFrameCount; + drflac_uint64 pcmFramesToDecode = pcmFrameIndex - runningPCMFrameCount; if (!isMidFrame) { - ma_result result = ma_dr_flac__decode_flac_frame(pFlac); - if (result == MA_SUCCESS) { - return ma_dr_flac__seek_forward_by_pcm_frames(pFlac, pcmFramesToDecode) == pcmFramesToDecode; + drflac_result result = drflac__decode_flac_frame(pFlac); + if (result == DRFLAC_SUCCESS) { + return drflac__seek_forward_by_pcm_frames(pFlac, pcmFramesToDecode) == pcmFramesToDecode; } else { - if (result == MA_CRC_MISMATCH) { + if (result == DRFLAC_CRC_MISMATCH) { goto next_iteration; } else { - return MA_FALSE; + return DRFLAC_FALSE; } } } else { - return ma_dr_flac__seek_forward_by_pcm_frames(pFlac, pcmFramesToDecode) == pcmFramesToDecode; + return drflac__seek_forward_by_pcm_frames(pFlac, pcmFramesToDecode) == pcmFramesToDecode; } } else { if (!isMidFrame) { - ma_result result = ma_dr_flac__seek_to_next_flac_frame(pFlac); - if (result == MA_SUCCESS) { + drflac_result result = drflac__seek_to_next_flac_frame(pFlac); + if (result == DRFLAC_SUCCESS) { runningPCMFrameCount += pcmFrameCountInThisFLACFrame; } else { - if (result == MA_CRC_MISMATCH) { + if (result == DRFLAC_CRC_MISMATCH) { goto next_iteration; } else { - return MA_FALSE; + return DRFLAC_FALSE; } } } else { runningPCMFrameCount += pFlac->currentFLACFrame.pcmFramesRemaining; pFlac->currentFLACFrame.pcmFramesRemaining = 0; - isMidFrame = MA_FALSE; + isMidFrame = DRFLAC_FALSE; } if (pcmFrameIndex == pFlac->totalPCMFrameCount && runningPCMFrameCount == pFlac->totalPCMFrameCount) { - return MA_TRUE; + return DRFLAC_TRUE; } } next_iteration: - if (!ma_dr_flac__read_next_flac_frame_header(&pFlac->bs, pFlac->bitsPerSample, &pFlac->currentFLACFrame.header)) { - return MA_FALSE; + if (!drflac__read_next_flac_frame_header(&pFlac->bs, pFlac->bitsPerSample, &pFlac->currentFLACFrame.header)) { + return DRFLAC_FALSE; } } } -#ifndef MA_DR_FLAC_NO_OGG +#ifndef DR_FLAC_NO_OGG typedef struct { - ma_uint8 capturePattern[4]; - ma_uint8 structureVersion; - ma_uint8 headerType; - ma_uint64 granulePosition; - ma_uint32 serialNumber; - ma_uint32 sequenceNumber; - ma_uint32 checksum; - ma_uint8 segmentCount; - ma_uint8 segmentTable[255]; -} ma_dr_flac_ogg_page_header; + drflac_uint8 capturePattern[4]; + drflac_uint8 structureVersion; + drflac_uint8 headerType; + drflac_uint64 granulePosition; + drflac_uint32 serialNumber; + drflac_uint32 sequenceNumber; + drflac_uint32 checksum; + drflac_uint8 segmentCount; + drflac_uint8 segmentTable[255]; +} drflac_ogg_page_header; #endif typedef struct { - ma_dr_flac_read_proc onRead; - ma_dr_flac_seek_proc onSeek; - ma_dr_flac_meta_proc onMeta; - ma_dr_flac_container container; + drflac_read_proc onRead; + drflac_seek_proc onSeek; + drflac_meta_proc onMeta; + drflac_container container; void* pUserData; void* pUserDataMD; - ma_uint32 sampleRate; - ma_uint8 channels; - ma_uint8 bitsPerSample; - ma_uint64 totalPCMFrameCount; - ma_uint16 maxBlockSizeInPCMFrames; - ma_uint64 runningFilePos; - ma_bool32 hasStreamInfoBlock; - ma_bool32 hasMetadataBlocks; - ma_dr_flac_bs bs; - ma_dr_flac_frame_header firstFrameHeader; -#ifndef MA_DR_FLAC_NO_OGG - ma_uint32 oggSerial; - ma_uint64 oggFirstBytePos; - ma_dr_flac_ogg_page_header oggBosHeader; -#endif -} ma_dr_flac_init_info; -static MA_INLINE void ma_dr_flac__decode_block_header(ma_uint32 blockHeader, ma_uint8* isLastBlock, ma_uint8* blockType, ma_uint32* blockSize) -{ - blockHeader = ma_dr_flac__be2host_32(blockHeader); - *isLastBlock = (ma_uint8)((blockHeader & 0x80000000UL) >> 31); - *blockType = (ma_uint8)((blockHeader & 0x7F000000UL) >> 24); + drflac_uint32 sampleRate; + drflac_uint8 channels; + drflac_uint8 bitsPerSample; + drflac_uint64 totalPCMFrameCount; + drflac_uint16 maxBlockSizeInPCMFrames; + drflac_uint64 runningFilePos; + drflac_bool32 hasStreamInfoBlock; + drflac_bool32 hasMetadataBlocks; + drflac_bs bs; + drflac_frame_header firstFrameHeader; +#ifndef DR_FLAC_NO_OGG + drflac_uint32 oggSerial; + drflac_uint64 oggFirstBytePos; + drflac_ogg_page_header oggBosHeader; +#endif +} drflac_init_info; +static DRFLAC_INLINE void drflac__decode_block_header(drflac_uint32 blockHeader, drflac_uint8* isLastBlock, drflac_uint8* blockType, drflac_uint32* blockSize) +{ + blockHeader = drflac__be2host_32(blockHeader); + *isLastBlock = (drflac_uint8)((blockHeader & 0x80000000UL) >> 31); + *blockType = (drflac_uint8)((blockHeader & 0x7F000000UL) >> 24); *blockSize = (blockHeader & 0x00FFFFFFUL); } -static MA_INLINE ma_bool32 ma_dr_flac__read_and_decode_block_header(ma_dr_flac_read_proc onRead, void* pUserData, ma_uint8* isLastBlock, ma_uint8* blockType, ma_uint32* blockSize) +static DRFLAC_INLINE drflac_bool32 drflac__read_and_decode_block_header(drflac_read_proc onRead, void* pUserData, drflac_uint8* isLastBlock, drflac_uint8* blockType, drflac_uint32* blockSize) { - ma_uint32 blockHeader; + drflac_uint32 blockHeader; *blockSize = 0; if (onRead(pUserData, &blockHeader, 4) != 4) { - return MA_FALSE; + return DRFLAC_FALSE; } - ma_dr_flac__decode_block_header(blockHeader, isLastBlock, blockType, blockSize); - return MA_TRUE; + drflac__decode_block_header(blockHeader, isLastBlock, blockType, blockSize); + return DRFLAC_TRUE; } -static ma_bool32 ma_dr_flac__read_streaminfo(ma_dr_flac_read_proc onRead, void* pUserData, ma_dr_flac_streaminfo* pStreamInfo) +static drflac_bool32 drflac__read_streaminfo(drflac_read_proc onRead, void* pUserData, drflac_streaminfo* pStreamInfo) { - ma_uint32 blockSizes; - ma_uint64 frameSizes = 0; - ma_uint64 importantProps; - ma_uint8 md5[16]; + drflac_uint32 blockSizes; + drflac_uint64 frameSizes = 0; + drflac_uint64 importantProps; + drflac_uint8 md5[16]; if (onRead(pUserData, &blockSizes, 4) != 4) { - return MA_FALSE; + return DRFLAC_FALSE; } if (onRead(pUserData, &frameSizes, 6) != 6) { - return MA_FALSE; + return DRFLAC_FALSE; } if (onRead(pUserData, &importantProps, 8) != 8) { - return MA_FALSE; + return DRFLAC_FALSE; } if (onRead(pUserData, md5, sizeof(md5)) != sizeof(md5)) { - return MA_FALSE; - } - blockSizes = ma_dr_flac__be2host_32(blockSizes); - frameSizes = ma_dr_flac__be2host_64(frameSizes); - importantProps = ma_dr_flac__be2host_64(importantProps); - pStreamInfo->minBlockSizeInPCMFrames = (ma_uint16)((blockSizes & 0xFFFF0000) >> 16); - pStreamInfo->maxBlockSizeInPCMFrames = (ma_uint16) (blockSizes & 0x0000FFFF); - pStreamInfo->minFrameSizeInPCMFrames = (ma_uint32)((frameSizes & (((ma_uint64)0x00FFFFFF << 16) << 24)) >> 40); - pStreamInfo->maxFrameSizeInPCMFrames = (ma_uint32)((frameSizes & (((ma_uint64)0x00FFFFFF << 16) << 0)) >> 16); - pStreamInfo->sampleRate = (ma_uint32)((importantProps & (((ma_uint64)0x000FFFFF << 16) << 28)) >> 44); - pStreamInfo->channels = (ma_uint8 )((importantProps & (((ma_uint64)0x0000000E << 16) << 24)) >> 41) + 1; - pStreamInfo->bitsPerSample = (ma_uint8 )((importantProps & (((ma_uint64)0x0000001F << 16) << 20)) >> 36) + 1; - pStreamInfo->totalPCMFrameCount = ((importantProps & ((((ma_uint64)0x0000000F << 16) << 16) | 0xFFFFFFFF))); - MA_DR_FLAC_COPY_MEMORY(pStreamInfo->md5, md5, sizeof(md5)); - return MA_TRUE; -} -static void* ma_dr_flac__malloc_default(size_t sz, void* pUserData) + return DRFLAC_FALSE; + } + blockSizes = drflac__be2host_32(blockSizes); + frameSizes = drflac__be2host_64(frameSizes); + importantProps = drflac__be2host_64(importantProps); + pStreamInfo->minBlockSizeInPCMFrames = (drflac_uint16)((blockSizes & 0xFFFF0000) >> 16); + pStreamInfo->maxBlockSizeInPCMFrames = (drflac_uint16) (blockSizes & 0x0000FFFF); + pStreamInfo->minFrameSizeInPCMFrames = (drflac_uint32)((frameSizes & (((drflac_uint64)0x00FFFFFF << 16) << 24)) >> 40); + pStreamInfo->maxFrameSizeInPCMFrames = (drflac_uint32)((frameSizes & (((drflac_uint64)0x00FFFFFF << 16) << 0)) >> 16); + pStreamInfo->sampleRate = (drflac_uint32)((importantProps & (((drflac_uint64)0x000FFFFF << 16) << 28)) >> 44); + pStreamInfo->channels = (drflac_uint8 )((importantProps & (((drflac_uint64)0x0000000E << 16) << 24)) >> 41) + 1; + pStreamInfo->bitsPerSample = (drflac_uint8 )((importantProps & (((drflac_uint64)0x0000001F << 16) << 20)) >> 36) + 1; + pStreamInfo->totalPCMFrameCount = ((importantProps & ((((drflac_uint64)0x0000000F << 16) << 16) | 0xFFFFFFFF))); + DRFLAC_COPY_MEMORY(pStreamInfo->md5, md5, sizeof(md5)); + return DRFLAC_TRUE; +} +static void* drflac__malloc_default(size_t sz, void* pUserData) { (void)pUserData; - return MA_DR_FLAC_MALLOC(sz); + return DRFLAC_MALLOC(sz); } -static void* ma_dr_flac__realloc_default(void* p, size_t sz, void* pUserData) +static void* drflac__realloc_default(void* p, size_t sz, void* pUserData) { (void)pUserData; - return MA_DR_FLAC_REALLOC(p, sz); + return DRFLAC_REALLOC(p, sz); } -static void ma_dr_flac__free_default(void* p, void* pUserData) +static void drflac__free_default(void* p, void* pUserData) { (void)pUserData; - MA_DR_FLAC_FREE(p); + DRFLAC_FREE(p); } -static void* ma_dr_flac__malloc_from_callbacks(size_t sz, const ma_allocation_callbacks* pAllocationCallbacks) +static void* drflac__malloc_from_callbacks(size_t sz, const drflac_allocation_callbacks* pAllocationCallbacks) { if (pAllocationCallbacks == NULL) { return NULL; @@ -86697,7 +85571,7 @@ static void* ma_dr_flac__malloc_from_callbacks(size_t sz, const ma_allocation_ca } return NULL; } -static void* ma_dr_flac__realloc_from_callbacks(void* p, size_t szNew, size_t szOld, const ma_allocation_callbacks* pAllocationCallbacks) +static void* drflac__realloc_from_callbacks(void* p, size_t szNew, size_t szOld, const drflac_allocation_callbacks* pAllocationCallbacks) { if (pAllocationCallbacks == NULL) { return NULL; @@ -86712,14 +85586,14 @@ static void* ma_dr_flac__realloc_from_callbacks(void* p, size_t szNew, size_t sz return NULL; } if (p != NULL) { - MA_DR_FLAC_COPY_MEMORY(p2, p, szOld); + DRFLAC_COPY_MEMORY(p2, p, szOld); pAllocationCallbacks->onFree(p, pAllocationCallbacks->pUserData); } return p2; } return NULL; } -static void ma_dr_flac__free_from_callbacks(void* p, const ma_allocation_callbacks* pAllocationCallbacks) +static void drflac__free_from_callbacks(void* p, const drflac_allocation_callbacks* pAllocationCallbacks) { if (p == NULL || pAllocationCallbacks == NULL) { return; @@ -86728,18 +85602,18 @@ static void ma_dr_flac__free_from_callbacks(void* p, const ma_allocation_callbac pAllocationCallbacks->onFree(p, pAllocationCallbacks->pUserData); } } -static ma_bool32 ma_dr_flac__read_and_decode_metadata(ma_dr_flac_read_proc onRead, ma_dr_flac_seek_proc onSeek, ma_dr_flac_meta_proc onMeta, void* pUserData, void* pUserDataMD, ma_uint64* pFirstFramePos, ma_uint64* pSeektablePos, ma_uint32* pSeekpointCount, ma_allocation_callbacks* pAllocationCallbacks) +static drflac_bool32 drflac__read_and_decode_metadata(drflac_read_proc onRead, drflac_seek_proc onSeek, drflac_meta_proc onMeta, void* pUserData, void* pUserDataMD, drflac_uint64* pFirstFramePos, drflac_uint64* pSeektablePos, drflac_uint32* pSeekpointCount, drflac_allocation_callbacks* pAllocationCallbacks) { - ma_uint64 runningFilePos = 42; - ma_uint64 seektablePos = 0; - ma_uint32 seektableSize = 0; + drflac_uint64 runningFilePos = 42; + drflac_uint64 seektablePos = 0; + drflac_uint32 seektableSize = 0; for (;;) { - ma_dr_flac_metadata metadata; - ma_uint8 isLastBlock = 0; - ma_uint8 blockType = 0; - ma_uint32 blockSize; - if (ma_dr_flac__read_and_decode_block_header(onRead, pUserData, &isLastBlock, &blockType, &blockSize) == MA_FALSE) { - return MA_FALSE; + drflac_metadata metadata; + drflac_uint8 isLastBlock = 0; + drflac_uint8 blockType; + drflac_uint32 blockSize; + if (drflac__read_and_decode_block_header(onRead, pUserData, &isLastBlock, &blockType, &blockSize) == DRFLAC_FALSE) { + return DRFLAC_FALSE; } runningFilePos += 4; metadata.type = blockType; @@ -86747,159 +85621,159 @@ static ma_bool32 ma_dr_flac__read_and_decode_metadata(ma_dr_flac_read_proc onRea metadata.rawDataSize = 0; switch (blockType) { - case MA_DR_FLAC_METADATA_BLOCK_TYPE_APPLICATION: + case DRFLAC_METADATA_BLOCK_TYPE_APPLICATION: { if (blockSize < 4) { - return MA_FALSE; + return DRFLAC_FALSE; } if (onMeta) { - void* pRawData = ma_dr_flac__malloc_from_callbacks(blockSize, pAllocationCallbacks); + void* pRawData = drflac__malloc_from_callbacks(blockSize, pAllocationCallbacks); if (pRawData == NULL) { - return MA_FALSE; + return DRFLAC_FALSE; } if (onRead(pUserData, pRawData, blockSize) != blockSize) { - ma_dr_flac__free_from_callbacks(pRawData, pAllocationCallbacks); - return MA_FALSE; + drflac__free_from_callbacks(pRawData, pAllocationCallbacks); + return DRFLAC_FALSE; } metadata.pRawData = pRawData; metadata.rawDataSize = blockSize; - metadata.data.application.id = ma_dr_flac__be2host_32(*(ma_uint32*)pRawData); - metadata.data.application.pData = (const void*)((ma_uint8*)pRawData + sizeof(ma_uint32)); - metadata.data.application.dataSize = blockSize - sizeof(ma_uint32); + metadata.data.application.id = drflac__be2host_32(*(drflac_uint32*)pRawData); + metadata.data.application.pData = (const void*)((drflac_uint8*)pRawData + sizeof(drflac_uint32)); + metadata.data.application.dataSize = blockSize - sizeof(drflac_uint32); onMeta(pUserDataMD, &metadata); - ma_dr_flac__free_from_callbacks(pRawData, pAllocationCallbacks); + drflac__free_from_callbacks(pRawData, pAllocationCallbacks); } } break; - case MA_DR_FLAC_METADATA_BLOCK_TYPE_SEEKTABLE: + case DRFLAC_METADATA_BLOCK_TYPE_SEEKTABLE: { seektablePos = runningFilePos; seektableSize = blockSize; if (onMeta) { - ma_uint32 seekpointCount; - ma_uint32 iSeekpoint; + drflac_uint32 seekpointCount; + drflac_uint32 iSeekpoint; void* pRawData; - seekpointCount = blockSize/MA_DR_FLAC_SEEKPOINT_SIZE_IN_BYTES; - pRawData = ma_dr_flac__malloc_from_callbacks(seekpointCount * sizeof(ma_dr_flac_seekpoint), pAllocationCallbacks); + seekpointCount = blockSize/DRFLAC_SEEKPOINT_SIZE_IN_BYTES; + pRawData = drflac__malloc_from_callbacks(seekpointCount * sizeof(drflac_seekpoint), pAllocationCallbacks); if (pRawData == NULL) { - return MA_FALSE; + return DRFLAC_FALSE; } for (iSeekpoint = 0; iSeekpoint < seekpointCount; ++iSeekpoint) { - ma_dr_flac_seekpoint* pSeekpoint = (ma_dr_flac_seekpoint*)pRawData + iSeekpoint; - if (onRead(pUserData, pSeekpoint, MA_DR_FLAC_SEEKPOINT_SIZE_IN_BYTES) != MA_DR_FLAC_SEEKPOINT_SIZE_IN_BYTES) { - ma_dr_flac__free_from_callbacks(pRawData, pAllocationCallbacks); - return MA_FALSE; + drflac_seekpoint* pSeekpoint = (drflac_seekpoint*)pRawData + iSeekpoint; + if (onRead(pUserData, pSeekpoint, DRFLAC_SEEKPOINT_SIZE_IN_BYTES) != DRFLAC_SEEKPOINT_SIZE_IN_BYTES) { + drflac__free_from_callbacks(pRawData, pAllocationCallbacks); + return DRFLAC_FALSE; } - pSeekpoint->firstPCMFrame = ma_dr_flac__be2host_64(pSeekpoint->firstPCMFrame); - pSeekpoint->flacFrameOffset = ma_dr_flac__be2host_64(pSeekpoint->flacFrameOffset); - pSeekpoint->pcmFrameCount = ma_dr_flac__be2host_16(pSeekpoint->pcmFrameCount); + pSeekpoint->firstPCMFrame = drflac__be2host_64(pSeekpoint->firstPCMFrame); + pSeekpoint->flacFrameOffset = drflac__be2host_64(pSeekpoint->flacFrameOffset); + pSeekpoint->pcmFrameCount = drflac__be2host_16(pSeekpoint->pcmFrameCount); } metadata.pRawData = pRawData; metadata.rawDataSize = blockSize; metadata.data.seektable.seekpointCount = seekpointCount; - metadata.data.seektable.pSeekpoints = (const ma_dr_flac_seekpoint*)pRawData; + metadata.data.seektable.pSeekpoints = (const drflac_seekpoint*)pRawData; onMeta(pUserDataMD, &metadata); - ma_dr_flac__free_from_callbacks(pRawData, pAllocationCallbacks); + drflac__free_from_callbacks(pRawData, pAllocationCallbacks); } } break; - case MA_DR_FLAC_METADATA_BLOCK_TYPE_VORBIS_COMMENT: + case DRFLAC_METADATA_BLOCK_TYPE_VORBIS_COMMENT: { if (blockSize < 8) { - return MA_FALSE; + return DRFLAC_FALSE; } if (onMeta) { void* pRawData; const char* pRunningData; const char* pRunningDataEnd; - ma_uint32 i; - pRawData = ma_dr_flac__malloc_from_callbacks(blockSize, pAllocationCallbacks); + drflac_uint32 i; + pRawData = drflac__malloc_from_callbacks(blockSize, pAllocationCallbacks); if (pRawData == NULL) { - return MA_FALSE; + return DRFLAC_FALSE; } if (onRead(pUserData, pRawData, blockSize) != blockSize) { - ma_dr_flac__free_from_callbacks(pRawData, pAllocationCallbacks); - return MA_FALSE; + drflac__free_from_callbacks(pRawData, pAllocationCallbacks); + return DRFLAC_FALSE; } metadata.pRawData = pRawData; metadata.rawDataSize = blockSize; pRunningData = (const char*)pRawData; pRunningDataEnd = (const char*)pRawData + blockSize; - metadata.data.vorbis_comment.vendorLength = ma_dr_flac__le2host_32_ptr_unaligned(pRunningData); pRunningData += 4; - if ((pRunningDataEnd - pRunningData) - 4 < (ma_int64)metadata.data.vorbis_comment.vendorLength) { - ma_dr_flac__free_from_callbacks(pRawData, pAllocationCallbacks); - return MA_FALSE; + metadata.data.vorbis_comment.vendorLength = drflac__le2host_32_ptr_unaligned(pRunningData); pRunningData += 4; + if ((pRunningDataEnd - pRunningData) - 4 < (drflac_int64)metadata.data.vorbis_comment.vendorLength) { + drflac__free_from_callbacks(pRawData, pAllocationCallbacks); + return DRFLAC_FALSE; } metadata.data.vorbis_comment.vendor = pRunningData; pRunningData += metadata.data.vorbis_comment.vendorLength; - metadata.data.vorbis_comment.commentCount = ma_dr_flac__le2host_32_ptr_unaligned(pRunningData); pRunningData += 4; - if ((pRunningDataEnd - pRunningData) / sizeof(ma_uint32) < metadata.data.vorbis_comment.commentCount) { - ma_dr_flac__free_from_callbacks(pRawData, pAllocationCallbacks); - return MA_FALSE; + metadata.data.vorbis_comment.commentCount = drflac__le2host_32_ptr_unaligned(pRunningData); pRunningData += 4; + if ((pRunningDataEnd - pRunningData) / sizeof(drflac_uint32) < metadata.data.vorbis_comment.commentCount) { + drflac__free_from_callbacks(pRawData, pAllocationCallbacks); + return DRFLAC_FALSE; } metadata.data.vorbis_comment.pComments = pRunningData; for (i = 0; i < metadata.data.vorbis_comment.commentCount; ++i) { - ma_uint32 commentLength; + drflac_uint32 commentLength; if (pRunningDataEnd - pRunningData < 4) { - ma_dr_flac__free_from_callbacks(pRawData, pAllocationCallbacks); - return MA_FALSE; + drflac__free_from_callbacks(pRawData, pAllocationCallbacks); + return DRFLAC_FALSE; } - commentLength = ma_dr_flac__le2host_32_ptr_unaligned(pRunningData); pRunningData += 4; - if (pRunningDataEnd - pRunningData < (ma_int64)commentLength) { - ma_dr_flac__free_from_callbacks(pRawData, pAllocationCallbacks); - return MA_FALSE; + commentLength = drflac__le2host_32_ptr_unaligned(pRunningData); pRunningData += 4; + if (pRunningDataEnd - pRunningData < (drflac_int64)commentLength) { + drflac__free_from_callbacks(pRawData, pAllocationCallbacks); + return DRFLAC_FALSE; } pRunningData += commentLength; } onMeta(pUserDataMD, &metadata); - ma_dr_flac__free_from_callbacks(pRawData, pAllocationCallbacks); + drflac__free_from_callbacks(pRawData, pAllocationCallbacks); } } break; - case MA_DR_FLAC_METADATA_BLOCK_TYPE_CUESHEET: + case DRFLAC_METADATA_BLOCK_TYPE_CUESHEET: { if (blockSize < 396) { - return MA_FALSE; + return DRFLAC_FALSE; } if (onMeta) { void* pRawData; const char* pRunningData; const char* pRunningDataEnd; size_t bufferSize; - ma_uint8 iTrack; - ma_uint8 iIndex; + drflac_uint8 iTrack; + drflac_uint8 iIndex; void* pTrackData; - pRawData = ma_dr_flac__malloc_from_callbacks(blockSize, pAllocationCallbacks); + pRawData = drflac__malloc_from_callbacks(blockSize, pAllocationCallbacks); if (pRawData == NULL) { - return MA_FALSE; + return DRFLAC_FALSE; } if (onRead(pUserData, pRawData, blockSize) != blockSize) { - ma_dr_flac__free_from_callbacks(pRawData, pAllocationCallbacks); - return MA_FALSE; + drflac__free_from_callbacks(pRawData, pAllocationCallbacks); + return DRFLAC_FALSE; } metadata.pRawData = pRawData; metadata.rawDataSize = blockSize; pRunningData = (const char*)pRawData; pRunningDataEnd = (const char*)pRawData + blockSize; - MA_DR_FLAC_COPY_MEMORY(metadata.data.cuesheet.catalog, pRunningData, 128); pRunningData += 128; - metadata.data.cuesheet.leadInSampleCount = ma_dr_flac__be2host_64(*(const ma_uint64*)pRunningData); pRunningData += 8; + DRFLAC_COPY_MEMORY(metadata.data.cuesheet.catalog, pRunningData, 128); pRunningData += 128; + metadata.data.cuesheet.leadInSampleCount = drflac__be2host_64(*(const drflac_uint64*)pRunningData); pRunningData += 8; metadata.data.cuesheet.isCD = (pRunningData[0] & 0x80) != 0; pRunningData += 259; metadata.data.cuesheet.trackCount = pRunningData[0]; pRunningData += 1; metadata.data.cuesheet.pTrackData = NULL; { const char* pRunningDataSaved = pRunningData; - bufferSize = metadata.data.cuesheet.trackCount * MA_DR_FLAC_CUESHEET_TRACK_SIZE_IN_BYTES; + bufferSize = metadata.data.cuesheet.trackCount * DRFLAC_CUESHEET_TRACK_SIZE_IN_BYTES; for (iTrack = 0; iTrack < metadata.data.cuesheet.trackCount; ++iTrack) { - ma_uint8 indexCount; - ma_uint32 indexPointSize; - if (pRunningDataEnd - pRunningData < MA_DR_FLAC_CUESHEET_TRACK_SIZE_IN_BYTES) { - ma_dr_flac__free_from_callbacks(pRawData, pAllocationCallbacks); - return MA_FALSE; + drflac_uint8 indexCount; + drflac_uint32 indexPointSize; + if (pRunningDataEnd - pRunningData < DRFLAC_CUESHEET_TRACK_SIZE_IN_BYTES) { + drflac__free_from_callbacks(pRawData, pAllocationCallbacks); + return DRFLAC_FALSE; } pRunningData += 35; indexCount = pRunningData[0]; pRunningData += 1; - bufferSize += indexCount * sizeof(ma_dr_flac_cuesheet_track_index); - indexPointSize = indexCount * MA_DR_FLAC_CUESHEET_TRACK_INDEX_SIZE_IN_BYTES; - if (pRunningDataEnd - pRunningData < (ma_int64)indexPointSize) { - ma_dr_flac__free_from_callbacks(pRawData, pAllocationCallbacks); - return MA_FALSE; + bufferSize += indexCount * sizeof(drflac_cuesheet_track_index); + indexPointSize = indexCount * DRFLAC_CUESHEET_TRACK_INDEX_SIZE_IN_BYTES; + if (pRunningDataEnd - pRunningData < (drflac_int64)indexPointSize) { + drflac__free_from_callbacks(pRawData, pAllocationCallbacks); + return DRFLAC_FALSE; } pRunningData += indexPointSize; } @@ -86907,125 +85781,125 @@ static ma_bool32 ma_dr_flac__read_and_decode_metadata(ma_dr_flac_read_proc onRea } { char* pRunningTrackData; - pTrackData = ma_dr_flac__malloc_from_callbacks(bufferSize, pAllocationCallbacks); + pTrackData = drflac__malloc_from_callbacks(bufferSize, pAllocationCallbacks); if (pTrackData == NULL) { - ma_dr_flac__free_from_callbacks(pRawData, pAllocationCallbacks); - return MA_FALSE; + drflac__free_from_callbacks(pRawData, pAllocationCallbacks); + return DRFLAC_FALSE; } pRunningTrackData = (char*)pTrackData; for (iTrack = 0; iTrack < metadata.data.cuesheet.trackCount; ++iTrack) { - ma_uint8 indexCount; - MA_DR_FLAC_COPY_MEMORY(pRunningTrackData, pRunningData, MA_DR_FLAC_CUESHEET_TRACK_SIZE_IN_BYTES); - pRunningData += MA_DR_FLAC_CUESHEET_TRACK_SIZE_IN_BYTES-1; - pRunningTrackData += MA_DR_FLAC_CUESHEET_TRACK_SIZE_IN_BYTES-1; + drflac_uint8 indexCount; + DRFLAC_COPY_MEMORY(pRunningTrackData, pRunningData, DRFLAC_CUESHEET_TRACK_SIZE_IN_BYTES); + pRunningData += DRFLAC_CUESHEET_TRACK_SIZE_IN_BYTES-1; + pRunningTrackData += DRFLAC_CUESHEET_TRACK_SIZE_IN_BYTES-1; indexCount = pRunningData[0]; pRunningData += 1; pRunningTrackData += 1; for (iIndex = 0; iIndex < indexCount; ++iIndex) { - ma_dr_flac_cuesheet_track_index* pTrackIndex = (ma_dr_flac_cuesheet_track_index*)pRunningTrackData; - MA_DR_FLAC_COPY_MEMORY(pRunningTrackData, pRunningData, MA_DR_FLAC_CUESHEET_TRACK_INDEX_SIZE_IN_BYTES); - pRunningData += MA_DR_FLAC_CUESHEET_TRACK_INDEX_SIZE_IN_BYTES; - pRunningTrackData += sizeof(ma_dr_flac_cuesheet_track_index); - pTrackIndex->offset = ma_dr_flac__be2host_64(pTrackIndex->offset); + drflac_cuesheet_track_index* pTrackIndex = (drflac_cuesheet_track_index*)pRunningTrackData; + DRFLAC_COPY_MEMORY(pRunningTrackData, pRunningData, DRFLAC_CUESHEET_TRACK_INDEX_SIZE_IN_BYTES); + pRunningData += DRFLAC_CUESHEET_TRACK_INDEX_SIZE_IN_BYTES; + pRunningTrackData += sizeof(drflac_cuesheet_track_index); + pTrackIndex->offset = drflac__be2host_64(pTrackIndex->offset); } } metadata.data.cuesheet.pTrackData = pTrackData; } - ma_dr_flac__free_from_callbacks(pRawData, pAllocationCallbacks); + drflac__free_from_callbacks(pRawData, pAllocationCallbacks); pRawData = NULL; onMeta(pUserDataMD, &metadata); - ma_dr_flac__free_from_callbacks(pTrackData, pAllocationCallbacks); + drflac__free_from_callbacks(pTrackData, pAllocationCallbacks); pTrackData = NULL; } } break; - case MA_DR_FLAC_METADATA_BLOCK_TYPE_PICTURE: + case DRFLAC_METADATA_BLOCK_TYPE_PICTURE: { if (blockSize < 32) { - return MA_FALSE; + return DRFLAC_FALSE; } if (onMeta) { void* pRawData; const char* pRunningData; const char* pRunningDataEnd; - pRawData = ma_dr_flac__malloc_from_callbacks(blockSize, pAllocationCallbacks); + pRawData = drflac__malloc_from_callbacks(blockSize, pAllocationCallbacks); if (pRawData == NULL) { - return MA_FALSE; + return DRFLAC_FALSE; } if (onRead(pUserData, pRawData, blockSize) != blockSize) { - ma_dr_flac__free_from_callbacks(pRawData, pAllocationCallbacks); - return MA_FALSE; + drflac__free_from_callbacks(pRawData, pAllocationCallbacks); + return DRFLAC_FALSE; } metadata.pRawData = pRawData; metadata.rawDataSize = blockSize; pRunningData = (const char*)pRawData; pRunningDataEnd = (const char*)pRawData + blockSize; - metadata.data.picture.type = ma_dr_flac__be2host_32_ptr_unaligned(pRunningData); pRunningData += 4; - metadata.data.picture.mimeLength = ma_dr_flac__be2host_32_ptr_unaligned(pRunningData); pRunningData += 4; - if ((pRunningDataEnd - pRunningData) - 24 < (ma_int64)metadata.data.picture.mimeLength) { - ma_dr_flac__free_from_callbacks(pRawData, pAllocationCallbacks); - return MA_FALSE; + metadata.data.picture.type = drflac__be2host_32_ptr_unaligned(pRunningData); pRunningData += 4; + metadata.data.picture.mimeLength = drflac__be2host_32_ptr_unaligned(pRunningData); pRunningData += 4; + if ((pRunningDataEnd - pRunningData) - 24 < (drflac_int64)metadata.data.picture.mimeLength) { + drflac__free_from_callbacks(pRawData, pAllocationCallbacks); + return DRFLAC_FALSE; } metadata.data.picture.mime = pRunningData; pRunningData += metadata.data.picture.mimeLength; - metadata.data.picture.descriptionLength = ma_dr_flac__be2host_32_ptr_unaligned(pRunningData); pRunningData += 4; - if ((pRunningDataEnd - pRunningData) - 20 < (ma_int64)metadata.data.picture.descriptionLength) { - ma_dr_flac__free_from_callbacks(pRawData, pAllocationCallbacks); - return MA_FALSE; + metadata.data.picture.descriptionLength = drflac__be2host_32_ptr_unaligned(pRunningData); pRunningData += 4; + if ((pRunningDataEnd - pRunningData) - 20 < (drflac_int64)metadata.data.picture.descriptionLength) { + drflac__free_from_callbacks(pRawData, pAllocationCallbacks); + return DRFLAC_FALSE; } metadata.data.picture.description = pRunningData; pRunningData += metadata.data.picture.descriptionLength; - metadata.data.picture.width = ma_dr_flac__be2host_32_ptr_unaligned(pRunningData); pRunningData += 4; - metadata.data.picture.height = ma_dr_flac__be2host_32_ptr_unaligned(pRunningData); pRunningData += 4; - metadata.data.picture.colorDepth = ma_dr_flac__be2host_32_ptr_unaligned(pRunningData); pRunningData += 4; - metadata.data.picture.indexColorCount = ma_dr_flac__be2host_32_ptr_unaligned(pRunningData); pRunningData += 4; - metadata.data.picture.pictureDataSize = ma_dr_flac__be2host_32_ptr_unaligned(pRunningData); pRunningData += 4; - metadata.data.picture.pPictureData = (const ma_uint8*)pRunningData; - if (pRunningDataEnd - pRunningData < (ma_int64)metadata.data.picture.pictureDataSize) { - ma_dr_flac__free_from_callbacks(pRawData, pAllocationCallbacks); - return MA_FALSE; + metadata.data.picture.width = drflac__be2host_32_ptr_unaligned(pRunningData); pRunningData += 4; + metadata.data.picture.height = drflac__be2host_32_ptr_unaligned(pRunningData); pRunningData += 4; + metadata.data.picture.colorDepth = drflac__be2host_32_ptr_unaligned(pRunningData); pRunningData += 4; + metadata.data.picture.indexColorCount = drflac__be2host_32_ptr_unaligned(pRunningData); pRunningData += 4; + metadata.data.picture.pictureDataSize = drflac__be2host_32_ptr_unaligned(pRunningData); pRunningData += 4; + metadata.data.picture.pPictureData = (const drflac_uint8*)pRunningData; + if (pRunningDataEnd - pRunningData < (drflac_int64)metadata.data.picture.pictureDataSize) { + drflac__free_from_callbacks(pRawData, pAllocationCallbacks); + return DRFLAC_FALSE; } onMeta(pUserDataMD, &metadata); - ma_dr_flac__free_from_callbacks(pRawData, pAllocationCallbacks); + drflac__free_from_callbacks(pRawData, pAllocationCallbacks); } } break; - case MA_DR_FLAC_METADATA_BLOCK_TYPE_PADDING: + case DRFLAC_METADATA_BLOCK_TYPE_PADDING: { if (onMeta) { metadata.data.padding.unused = 0; - if (!onSeek(pUserData, blockSize, ma_dr_flac_seek_origin_current)) { - isLastBlock = MA_TRUE; + if (!onSeek(pUserData, blockSize, drflac_seek_origin_current)) { + isLastBlock = DRFLAC_TRUE; } else { onMeta(pUserDataMD, &metadata); } } } break; - case MA_DR_FLAC_METADATA_BLOCK_TYPE_INVALID: + case DRFLAC_METADATA_BLOCK_TYPE_INVALID: { if (onMeta) { - if (!onSeek(pUserData, blockSize, ma_dr_flac_seek_origin_current)) { - isLastBlock = MA_TRUE; + if (!onSeek(pUserData, blockSize, drflac_seek_origin_current)) { + isLastBlock = DRFLAC_TRUE; } } } break; default: { if (onMeta) { - void* pRawData = ma_dr_flac__malloc_from_callbacks(blockSize, pAllocationCallbacks); + void* pRawData = drflac__malloc_from_callbacks(blockSize, pAllocationCallbacks); if (pRawData == NULL) { - return MA_FALSE; + return DRFLAC_FALSE; } if (onRead(pUserData, pRawData, blockSize) != blockSize) { - ma_dr_flac__free_from_callbacks(pRawData, pAllocationCallbacks); - return MA_FALSE; + drflac__free_from_callbacks(pRawData, pAllocationCallbacks); + return DRFLAC_FALSE; } metadata.pRawData = pRawData; metadata.rawDataSize = blockSize; onMeta(pUserDataMD, &metadata); - ma_dr_flac__free_from_callbacks(pRawData, pAllocationCallbacks); + drflac__free_from_callbacks(pRawData, pAllocationCallbacks); } } break; } if (onMeta == NULL && blockSize > 0) { - if (!onSeek(pUserData, blockSize, ma_dr_flac_seek_origin_current)) { - isLastBlock = MA_TRUE; + if (!onSeek(pUserData, blockSize, drflac_seek_origin_current)) { + isLastBlock = DRFLAC_TRUE; } } runningFilePos += blockSize; @@ -87034,44 +85908,44 @@ static ma_bool32 ma_dr_flac__read_and_decode_metadata(ma_dr_flac_read_proc onRea } } *pSeektablePos = seektablePos; - *pSeekpointCount = seektableSize / MA_DR_FLAC_SEEKPOINT_SIZE_IN_BYTES; + *pSeekpointCount = seektableSize / DRFLAC_SEEKPOINT_SIZE_IN_BYTES; *pFirstFramePos = runningFilePos; - return MA_TRUE; + return DRFLAC_TRUE; } -static ma_bool32 ma_dr_flac__init_private__native(ma_dr_flac_init_info* pInit, ma_dr_flac_read_proc onRead, ma_dr_flac_seek_proc onSeek, ma_dr_flac_meta_proc onMeta, void* pUserData, void* pUserDataMD, ma_bool32 relaxed) +static drflac_bool32 drflac__init_private__native(drflac_init_info* pInit, drflac_read_proc onRead, drflac_seek_proc onSeek, drflac_meta_proc onMeta, void* pUserData, void* pUserDataMD, drflac_bool32 relaxed) { - ma_uint8 isLastBlock; - ma_uint8 blockType; - ma_uint32 blockSize; + drflac_uint8 isLastBlock; + drflac_uint8 blockType; + drflac_uint32 blockSize; (void)onSeek; - pInit->container = ma_dr_flac_container_native; - if (!ma_dr_flac__read_and_decode_block_header(onRead, pUserData, &isLastBlock, &blockType, &blockSize)) { - return MA_FALSE; + pInit->container = drflac_container_native; + if (!drflac__read_and_decode_block_header(onRead, pUserData, &isLastBlock, &blockType, &blockSize)) { + return DRFLAC_FALSE; } - if (blockType != MA_DR_FLAC_METADATA_BLOCK_TYPE_STREAMINFO || blockSize != 34) { + if (blockType != DRFLAC_METADATA_BLOCK_TYPE_STREAMINFO || blockSize != 34) { if (!relaxed) { - return MA_FALSE; + return DRFLAC_FALSE; } else { - pInit->hasStreamInfoBlock = MA_FALSE; - pInit->hasMetadataBlocks = MA_FALSE; - if (!ma_dr_flac__read_next_flac_frame_header(&pInit->bs, 0, &pInit->firstFrameHeader)) { - return MA_FALSE; + pInit->hasStreamInfoBlock = DRFLAC_FALSE; + pInit->hasMetadataBlocks = DRFLAC_FALSE; + if (!drflac__read_next_flac_frame_header(&pInit->bs, 0, &pInit->firstFrameHeader)) { + return DRFLAC_FALSE; } if (pInit->firstFrameHeader.bitsPerSample == 0) { - return MA_FALSE; + return DRFLAC_FALSE; } pInit->sampleRate = pInit->firstFrameHeader.sampleRate; - pInit->channels = ma_dr_flac__get_channel_count_from_channel_assignment(pInit->firstFrameHeader.channelAssignment); + pInit->channels = drflac__get_channel_count_from_channel_assignment(pInit->firstFrameHeader.channelAssignment); pInit->bitsPerSample = pInit->firstFrameHeader.bitsPerSample; pInit->maxBlockSizeInPCMFrames = 65535; - return MA_TRUE; + return DRFLAC_TRUE; } } else { - ma_dr_flac_streaminfo streaminfo; - if (!ma_dr_flac__read_streaminfo(onRead, pUserData, &streaminfo)) { - return MA_FALSE; + drflac_streaminfo streaminfo; + if (!drflac__read_streaminfo(onRead, pUserData, &streaminfo)) { + return DRFLAC_FALSE; } - pInit->hasStreamInfoBlock = MA_TRUE; + pInit->hasStreamInfoBlock = DRFLAC_TRUE; pInit->sampleRate = streaminfo.sampleRate; pInit->channels = streaminfo.channels; pInit->bitsPerSample = streaminfo.bitsPerSample; @@ -87079,26 +85953,26 @@ static ma_bool32 ma_dr_flac__init_private__native(ma_dr_flac_init_info* pInit, m pInit->maxBlockSizeInPCMFrames = streaminfo.maxBlockSizeInPCMFrames; pInit->hasMetadataBlocks = !isLastBlock; if (onMeta) { - ma_dr_flac_metadata metadata; - metadata.type = MA_DR_FLAC_METADATA_BLOCK_TYPE_STREAMINFO; + drflac_metadata metadata; + metadata.type = DRFLAC_METADATA_BLOCK_TYPE_STREAMINFO; metadata.pRawData = NULL; metadata.rawDataSize = 0; metadata.data.streaminfo = streaminfo; onMeta(pUserDataMD, &metadata); } - return MA_TRUE; + return DRFLAC_TRUE; } } -#ifndef MA_DR_FLAC_NO_OGG -#define MA_DR_FLAC_OGG_MAX_PAGE_SIZE 65307 -#define MA_DR_FLAC_OGG_CAPTURE_PATTERN_CRC32 1605413199 +#ifndef DR_FLAC_NO_OGG +#define DRFLAC_OGG_MAX_PAGE_SIZE 65307 +#define DRFLAC_OGG_CAPTURE_PATTERN_CRC32 1605413199 typedef enum { - ma_dr_flac_ogg_recover_on_crc_mismatch, - ma_dr_flac_ogg_fail_on_crc_mismatch -} ma_dr_flac_ogg_crc_mismatch_recovery; -#ifndef MA_DR_FLAC_NO_CRC -static ma_uint32 ma_dr_flac__crc32_table[] = { + drflac_ogg_recover_on_crc_mismatch, + drflac_ogg_fail_on_crc_mismatch +} drflac_ogg_crc_mismatch_recovery; +#ifndef DR_FLAC_NO_CRC +static drflac_uint32 drflac__crc32_table[] = { 0x00000000L, 0x04C11DB7L, 0x09823B6EL, 0x0D4326D9L, 0x130476DCL, 0x17C56B6BL, 0x1A864DB2L, 0x1E475005L, 0x2608EDB8L, 0x22C9F00FL, 0x2F8AD6D6L, 0x2B4BCB61L, @@ -87165,63 +86039,63 @@ static ma_uint32 ma_dr_flac__crc32_table[] = { 0xBCB4666DL, 0xB8757BDAL, 0xB5365D03L, 0xB1F740B4L }; #endif -static MA_INLINE ma_uint32 ma_dr_flac_crc32_byte(ma_uint32 crc32, ma_uint8 data) +static DRFLAC_INLINE drflac_uint32 drflac_crc32_byte(drflac_uint32 crc32, drflac_uint8 data) { -#ifndef MA_DR_FLAC_NO_CRC - return (crc32 << 8) ^ ma_dr_flac__crc32_table[(ma_uint8)((crc32 >> 24) & 0xFF) ^ data]; +#ifndef DR_FLAC_NO_CRC + return (crc32 << 8) ^ drflac__crc32_table[(drflac_uint8)((crc32 >> 24) & 0xFF) ^ data]; #else (void)data; return crc32; #endif } #if 0 -static MA_INLINE ma_uint32 ma_dr_flac_crc32_uint32(ma_uint32 crc32, ma_uint32 data) +static DRFLAC_INLINE drflac_uint32 drflac_crc32_uint32(drflac_uint32 crc32, drflac_uint32 data) { - crc32 = ma_dr_flac_crc32_byte(crc32, (ma_uint8)((data >> 24) & 0xFF)); - crc32 = ma_dr_flac_crc32_byte(crc32, (ma_uint8)((data >> 16) & 0xFF)); - crc32 = ma_dr_flac_crc32_byte(crc32, (ma_uint8)((data >> 8) & 0xFF)); - crc32 = ma_dr_flac_crc32_byte(crc32, (ma_uint8)((data >> 0) & 0xFF)); + crc32 = drflac_crc32_byte(crc32, (drflac_uint8)((data >> 24) & 0xFF)); + crc32 = drflac_crc32_byte(crc32, (drflac_uint8)((data >> 16) & 0xFF)); + crc32 = drflac_crc32_byte(crc32, (drflac_uint8)((data >> 8) & 0xFF)); + crc32 = drflac_crc32_byte(crc32, (drflac_uint8)((data >> 0) & 0xFF)); return crc32; } -static MA_INLINE ma_uint32 ma_dr_flac_crc32_uint64(ma_uint32 crc32, ma_uint64 data) +static DRFLAC_INLINE drflac_uint32 drflac_crc32_uint64(drflac_uint32 crc32, drflac_uint64 data) { - crc32 = ma_dr_flac_crc32_uint32(crc32, (ma_uint32)((data >> 32) & 0xFFFFFFFF)); - crc32 = ma_dr_flac_crc32_uint32(crc32, (ma_uint32)((data >> 0) & 0xFFFFFFFF)); + crc32 = drflac_crc32_uint32(crc32, (drflac_uint32)((data >> 32) & 0xFFFFFFFF)); + crc32 = drflac_crc32_uint32(crc32, (drflac_uint32)((data >> 0) & 0xFFFFFFFF)); return crc32; } #endif -static MA_INLINE ma_uint32 ma_dr_flac_crc32_buffer(ma_uint32 crc32, ma_uint8* pData, ma_uint32 dataSize) +static DRFLAC_INLINE drflac_uint32 drflac_crc32_buffer(drflac_uint32 crc32, drflac_uint8* pData, drflac_uint32 dataSize) { - ma_uint32 i; + drflac_uint32 i; for (i = 0; i < dataSize; ++i) { - crc32 = ma_dr_flac_crc32_byte(crc32, pData[i]); + crc32 = drflac_crc32_byte(crc32, pData[i]); } return crc32; } -static MA_INLINE ma_bool32 ma_dr_flac_ogg__is_capture_pattern(ma_uint8 pattern[4]) +static DRFLAC_INLINE drflac_bool32 drflac_ogg__is_capture_pattern(drflac_uint8 pattern[4]) { return pattern[0] == 'O' && pattern[1] == 'g' && pattern[2] == 'g' && pattern[3] == 'S'; } -static MA_INLINE ma_uint32 ma_dr_flac_ogg__get_page_header_size(ma_dr_flac_ogg_page_header* pHeader) +static DRFLAC_INLINE drflac_uint32 drflac_ogg__get_page_header_size(drflac_ogg_page_header* pHeader) { return 27 + pHeader->segmentCount; } -static MA_INLINE ma_uint32 ma_dr_flac_ogg__get_page_body_size(ma_dr_flac_ogg_page_header* pHeader) +static DRFLAC_INLINE drflac_uint32 drflac_ogg__get_page_body_size(drflac_ogg_page_header* pHeader) { - ma_uint32 pageBodySize = 0; + drflac_uint32 pageBodySize = 0; int i; for (i = 0; i < pHeader->segmentCount; ++i) { pageBodySize += pHeader->segmentTable[i]; } return pageBodySize; } -static ma_result ma_dr_flac_ogg__read_page_header_after_capture_pattern(ma_dr_flac_read_proc onRead, void* pUserData, ma_dr_flac_ogg_page_header* pHeader, ma_uint32* pBytesRead, ma_uint32* pCRC32) +static drflac_result drflac_ogg__read_page_header_after_capture_pattern(drflac_read_proc onRead, void* pUserData, drflac_ogg_page_header* pHeader, drflac_uint32* pBytesRead, drflac_uint32* pCRC32) { - ma_uint8 data[23]; - ma_uint32 i; - MA_DR_FLAC_ASSERT(*pCRC32 == MA_DR_FLAC_OGG_CAPTURE_PATTERN_CRC32); + drflac_uint8 data[23]; + drflac_uint32 i; + DRFLAC_ASSERT(*pCRC32 == DRFLAC_OGG_CAPTURE_PATTERN_CRC32); if (onRead(pUserData, data, 23) != 23) { - return MA_AT_END; + return DRFLAC_AT_END; } *pBytesRead += 23; pHeader->capturePattern[0] = 'O'; @@ -87230,44 +86104,44 @@ static ma_result ma_dr_flac_ogg__read_page_header_after_capture_pattern(ma_dr_fl pHeader->capturePattern[3] = 'S'; pHeader->structureVersion = data[0]; pHeader->headerType = data[1]; - MA_DR_FLAC_COPY_MEMORY(&pHeader->granulePosition, &data[ 2], 8); - MA_DR_FLAC_COPY_MEMORY(&pHeader->serialNumber, &data[10], 4); - MA_DR_FLAC_COPY_MEMORY(&pHeader->sequenceNumber, &data[14], 4); - MA_DR_FLAC_COPY_MEMORY(&pHeader->checksum, &data[18], 4); + DRFLAC_COPY_MEMORY(&pHeader->granulePosition, &data[ 2], 8); + DRFLAC_COPY_MEMORY(&pHeader->serialNumber, &data[10], 4); + DRFLAC_COPY_MEMORY(&pHeader->sequenceNumber, &data[14], 4); + DRFLAC_COPY_MEMORY(&pHeader->checksum, &data[18], 4); pHeader->segmentCount = data[22]; data[18] = 0; data[19] = 0; data[20] = 0; data[21] = 0; for (i = 0; i < 23; ++i) { - *pCRC32 = ma_dr_flac_crc32_byte(*pCRC32, data[i]); + *pCRC32 = drflac_crc32_byte(*pCRC32, data[i]); } if (onRead(pUserData, pHeader->segmentTable, pHeader->segmentCount) != pHeader->segmentCount) { - return MA_AT_END; + return DRFLAC_AT_END; } *pBytesRead += pHeader->segmentCount; for (i = 0; i < pHeader->segmentCount; ++i) { - *pCRC32 = ma_dr_flac_crc32_byte(*pCRC32, pHeader->segmentTable[i]); + *pCRC32 = drflac_crc32_byte(*pCRC32, pHeader->segmentTable[i]); } - return MA_SUCCESS; + return DRFLAC_SUCCESS; } -static ma_result ma_dr_flac_ogg__read_page_header(ma_dr_flac_read_proc onRead, void* pUserData, ma_dr_flac_ogg_page_header* pHeader, ma_uint32* pBytesRead, ma_uint32* pCRC32) +static drflac_result drflac_ogg__read_page_header(drflac_read_proc onRead, void* pUserData, drflac_ogg_page_header* pHeader, drflac_uint32* pBytesRead, drflac_uint32* pCRC32) { - ma_uint8 id[4]; + drflac_uint8 id[4]; *pBytesRead = 0; if (onRead(pUserData, id, 4) != 4) { - return MA_AT_END; + return DRFLAC_AT_END; } *pBytesRead += 4; for (;;) { - if (ma_dr_flac_ogg__is_capture_pattern(id)) { - ma_result result; - *pCRC32 = MA_DR_FLAC_OGG_CAPTURE_PATTERN_CRC32; - result = ma_dr_flac_ogg__read_page_header_after_capture_pattern(onRead, pUserData, pHeader, pBytesRead, pCRC32); - if (result == MA_SUCCESS) { - return MA_SUCCESS; + if (drflac_ogg__is_capture_pattern(id)) { + drflac_result result; + *pCRC32 = DRFLAC_OGG_CAPTURE_PATTERN_CRC32; + result = drflac_ogg__read_page_header_after_capture_pattern(onRead, pUserData, pHeader, pBytesRead, pCRC32); + if (result == DRFLAC_SUCCESS) { + return DRFLAC_SUCCESS; } else { - if (result == MA_CRC_MISMATCH) { + if (result == DRFLAC_CRC_MISMATCH) { continue; } else { return result; @@ -87278,7 +86152,7 @@ static ma_result ma_dr_flac_ogg__read_page_header(ma_dr_flac_read_proc onRead, v id[1] = id[2]; id[2] = id[3]; if (onRead(pUserData, &id[3], 1) != 1) { - return MA_AT_END; + return DRFLAC_AT_END; } *pBytesRead += 1; } @@ -87286,91 +86160,91 @@ static ma_result ma_dr_flac_ogg__read_page_header(ma_dr_flac_read_proc onRead, v } typedef struct { - ma_dr_flac_read_proc onRead; - ma_dr_flac_seek_proc onSeek; + drflac_read_proc onRead; + drflac_seek_proc onSeek; void* pUserData; - ma_uint64 currentBytePos; - ma_uint64 firstBytePos; - ma_uint32 serialNumber; - ma_dr_flac_ogg_page_header bosPageHeader; - ma_dr_flac_ogg_page_header currentPageHeader; - ma_uint32 bytesRemainingInPage; - ma_uint32 pageDataSize; - ma_uint8 pageData[MA_DR_FLAC_OGG_MAX_PAGE_SIZE]; -} ma_dr_flac_oggbs; -static size_t ma_dr_flac_oggbs__read_physical(ma_dr_flac_oggbs* oggbs, void* bufferOut, size_t bytesToRead) + drflac_uint64 currentBytePos; + drflac_uint64 firstBytePos; + drflac_uint32 serialNumber; + drflac_ogg_page_header bosPageHeader; + drflac_ogg_page_header currentPageHeader; + drflac_uint32 bytesRemainingInPage; + drflac_uint32 pageDataSize; + drflac_uint8 pageData[DRFLAC_OGG_MAX_PAGE_SIZE]; +} drflac_oggbs; +static size_t drflac_oggbs__read_physical(drflac_oggbs* oggbs, void* bufferOut, size_t bytesToRead) { size_t bytesActuallyRead = oggbs->onRead(oggbs->pUserData, bufferOut, bytesToRead); oggbs->currentBytePos += bytesActuallyRead; return bytesActuallyRead; } -static ma_bool32 ma_dr_flac_oggbs__seek_physical(ma_dr_flac_oggbs* oggbs, ma_uint64 offset, ma_dr_flac_seek_origin origin) +static drflac_bool32 drflac_oggbs__seek_physical(drflac_oggbs* oggbs, drflac_uint64 offset, drflac_seek_origin origin) { - if (origin == ma_dr_flac_seek_origin_start) { + if (origin == drflac_seek_origin_start) { if (offset <= 0x7FFFFFFF) { - if (!oggbs->onSeek(oggbs->pUserData, (int)offset, ma_dr_flac_seek_origin_start)) { - return MA_FALSE; + if (!oggbs->onSeek(oggbs->pUserData, (int)offset, drflac_seek_origin_start)) { + return DRFLAC_FALSE; } oggbs->currentBytePos = offset; - return MA_TRUE; + return DRFLAC_TRUE; } else { - if (!oggbs->onSeek(oggbs->pUserData, 0x7FFFFFFF, ma_dr_flac_seek_origin_start)) { - return MA_FALSE; + if (!oggbs->onSeek(oggbs->pUserData, 0x7FFFFFFF, drflac_seek_origin_start)) { + return DRFLAC_FALSE; } oggbs->currentBytePos = offset; - return ma_dr_flac_oggbs__seek_physical(oggbs, offset - 0x7FFFFFFF, ma_dr_flac_seek_origin_current); + return drflac_oggbs__seek_physical(oggbs, offset - 0x7FFFFFFF, drflac_seek_origin_current); } } else { while (offset > 0x7FFFFFFF) { - if (!oggbs->onSeek(oggbs->pUserData, 0x7FFFFFFF, ma_dr_flac_seek_origin_current)) { - return MA_FALSE; + if (!oggbs->onSeek(oggbs->pUserData, 0x7FFFFFFF, drflac_seek_origin_current)) { + return DRFLAC_FALSE; } oggbs->currentBytePos += 0x7FFFFFFF; offset -= 0x7FFFFFFF; } - if (!oggbs->onSeek(oggbs->pUserData, (int)offset, ma_dr_flac_seek_origin_current)) { - return MA_FALSE; + if (!oggbs->onSeek(oggbs->pUserData, (int)offset, drflac_seek_origin_current)) { + return DRFLAC_FALSE; } oggbs->currentBytePos += offset; - return MA_TRUE; + return DRFLAC_TRUE; } } -static ma_bool32 ma_dr_flac_oggbs__goto_next_page(ma_dr_flac_oggbs* oggbs, ma_dr_flac_ogg_crc_mismatch_recovery recoveryMethod) +static drflac_bool32 drflac_oggbs__goto_next_page(drflac_oggbs* oggbs, drflac_ogg_crc_mismatch_recovery recoveryMethod) { - ma_dr_flac_ogg_page_header header; + drflac_ogg_page_header header; for (;;) { - ma_uint32 crc32 = 0; - ma_uint32 bytesRead; - ma_uint32 pageBodySize; -#ifndef MA_DR_FLAC_NO_CRC - ma_uint32 actualCRC32; + drflac_uint32 crc32 = 0; + drflac_uint32 bytesRead; + drflac_uint32 pageBodySize; +#ifndef DR_FLAC_NO_CRC + drflac_uint32 actualCRC32; #endif - if (ma_dr_flac_ogg__read_page_header(oggbs->onRead, oggbs->pUserData, &header, &bytesRead, &crc32) != MA_SUCCESS) { - return MA_FALSE; + if (drflac_ogg__read_page_header(oggbs->onRead, oggbs->pUserData, &header, &bytesRead, &crc32) != DRFLAC_SUCCESS) { + return DRFLAC_FALSE; } oggbs->currentBytePos += bytesRead; - pageBodySize = ma_dr_flac_ogg__get_page_body_size(&header); - if (pageBodySize > MA_DR_FLAC_OGG_MAX_PAGE_SIZE) { + pageBodySize = drflac_ogg__get_page_body_size(&header); + if (pageBodySize > DRFLAC_OGG_MAX_PAGE_SIZE) { continue; } if (header.serialNumber != oggbs->serialNumber) { - if (pageBodySize > 0 && !ma_dr_flac_oggbs__seek_physical(oggbs, pageBodySize, ma_dr_flac_seek_origin_current)) { - return MA_FALSE; + if (pageBodySize > 0 && !drflac_oggbs__seek_physical(oggbs, pageBodySize, drflac_seek_origin_current)) { + return DRFLAC_FALSE; } continue; } - if (ma_dr_flac_oggbs__read_physical(oggbs, oggbs->pageData, pageBodySize) != pageBodySize) { - return MA_FALSE; + if (drflac_oggbs__read_physical(oggbs, oggbs->pageData, pageBodySize) != pageBodySize) { + return DRFLAC_FALSE; } oggbs->pageDataSize = pageBodySize; -#ifndef MA_DR_FLAC_NO_CRC - actualCRC32 = ma_dr_flac_crc32_buffer(crc32, oggbs->pageData, oggbs->pageDataSize); +#ifndef DR_FLAC_NO_CRC + actualCRC32 = drflac_crc32_buffer(crc32, oggbs->pageData, oggbs->pageDataSize); if (actualCRC32 != header.checksum) { - if (recoveryMethod == ma_dr_flac_ogg_recover_on_crc_mismatch) { + if (recoveryMethod == drflac_ogg_recover_on_crc_mismatch) { continue; } else { - ma_dr_flac_oggbs__goto_next_page(oggbs, ma_dr_flac_ogg_recover_on_crc_mismatch); - return MA_FALSE; + drflac_oggbs__goto_next_page(oggbs, drflac_ogg_recover_on_crc_mismatch); + return DRFLAC_FALSE; } } #else @@ -87378,17 +86252,17 @@ static ma_bool32 ma_dr_flac_oggbs__goto_next_page(ma_dr_flac_oggbs* oggbs, ma_dr #endif oggbs->currentPageHeader = header; oggbs->bytesRemainingInPage = pageBodySize; - return MA_TRUE; + return DRFLAC_TRUE; } } #if 0 -static ma_uint8 ma_dr_flac_oggbs__get_current_segment_index(ma_dr_flac_oggbs* oggbs, ma_uint8* pBytesRemainingInSeg) +static drflac_uint8 drflac_oggbs__get_current_segment_index(drflac_oggbs* oggbs, drflac_uint8* pBytesRemainingInSeg) { - ma_uint32 bytesConsumedInPage = ma_dr_flac_ogg__get_page_body_size(&oggbs->currentPageHeader) - oggbs->bytesRemainingInPage; - ma_uint8 iSeg = 0; - ma_uint32 iByte = 0; + drflac_uint32 bytesConsumedInPage = drflac_ogg__get_page_body_size(&oggbs->currentPageHeader) - oggbs->bytesRemainingInPage; + drflac_uint8 iSeg = 0; + drflac_uint32 iByte = 0; while (iByte < bytesConsumedInPage) { - ma_uint8 segmentSize = oggbs->currentPageHeader.segmentTable[iSeg]; + drflac_uint8 segmentSize = oggbs->currentPageHeader.segmentTable[iSeg]; if (iByte + segmentSize > bytesConsumedInPage) { break; } else { @@ -87396,92 +86270,92 @@ static ma_uint8 ma_dr_flac_oggbs__get_current_segment_index(ma_dr_flac_oggbs* og iByte += segmentSize; } } - *pBytesRemainingInSeg = oggbs->currentPageHeader.segmentTable[iSeg] - (ma_uint8)(bytesConsumedInPage - iByte); + *pBytesRemainingInSeg = oggbs->currentPageHeader.segmentTable[iSeg] - (drflac_uint8)(bytesConsumedInPage - iByte); return iSeg; } -static ma_bool32 ma_dr_flac_oggbs__seek_to_next_packet(ma_dr_flac_oggbs* oggbs) +static drflac_bool32 drflac_oggbs__seek_to_next_packet(drflac_oggbs* oggbs) { for (;;) { - ma_bool32 atEndOfPage = MA_FALSE; - ma_uint8 bytesRemainingInSeg; - ma_uint8 iFirstSeg = ma_dr_flac_oggbs__get_current_segment_index(oggbs, &bytesRemainingInSeg); - ma_uint32 bytesToEndOfPacketOrPage = bytesRemainingInSeg; - for (ma_uint8 iSeg = iFirstSeg; iSeg < oggbs->currentPageHeader.segmentCount; ++iSeg) { - ma_uint8 segmentSize = oggbs->currentPageHeader.segmentTable[iSeg]; + drflac_bool32 atEndOfPage = DRFLAC_FALSE; + drflac_uint8 bytesRemainingInSeg; + drflac_uint8 iFirstSeg = drflac_oggbs__get_current_segment_index(oggbs, &bytesRemainingInSeg); + drflac_uint32 bytesToEndOfPacketOrPage = bytesRemainingInSeg; + for (drflac_uint8 iSeg = iFirstSeg; iSeg < oggbs->currentPageHeader.segmentCount; ++iSeg) { + drflac_uint8 segmentSize = oggbs->currentPageHeader.segmentTable[iSeg]; if (segmentSize < 255) { if (iSeg == oggbs->currentPageHeader.segmentCount-1) { - atEndOfPage = MA_TRUE; + atEndOfPage = DRFLAC_TRUE; } break; } bytesToEndOfPacketOrPage += segmentSize; } - ma_dr_flac_oggbs__seek_physical(oggbs, bytesToEndOfPacketOrPage, ma_dr_flac_seek_origin_current); + drflac_oggbs__seek_physical(oggbs, bytesToEndOfPacketOrPage, drflac_seek_origin_current); oggbs->bytesRemainingInPage -= bytesToEndOfPacketOrPage; if (atEndOfPage) { - if (!ma_dr_flac_oggbs__goto_next_page(oggbs)) { - return MA_FALSE; + if (!drflac_oggbs__goto_next_page(oggbs)) { + return DRFLAC_FALSE; } if ((oggbs->currentPageHeader.headerType & 0x01) == 0) { - return MA_TRUE; + return DRFLAC_TRUE; } } else { - return MA_TRUE; + return DRFLAC_TRUE; } } } -static ma_bool32 ma_dr_flac_oggbs__seek_to_next_frame(ma_dr_flac_oggbs* oggbs) +static drflac_bool32 drflac_oggbs__seek_to_next_frame(drflac_oggbs* oggbs) { - return ma_dr_flac_oggbs__seek_to_next_packet(oggbs); + return drflac_oggbs__seek_to_next_packet(oggbs); } #endif -static size_t ma_dr_flac__on_read_ogg(void* pUserData, void* bufferOut, size_t bytesToRead) +static size_t drflac__on_read_ogg(void* pUserData, void* bufferOut, size_t bytesToRead) { - ma_dr_flac_oggbs* oggbs = (ma_dr_flac_oggbs*)pUserData; - ma_uint8* pRunningBufferOut = (ma_uint8*)bufferOut; + drflac_oggbs* oggbs = (drflac_oggbs*)pUserData; + drflac_uint8* pRunningBufferOut = (drflac_uint8*)bufferOut; size_t bytesRead = 0; - MA_DR_FLAC_ASSERT(oggbs != NULL); - MA_DR_FLAC_ASSERT(pRunningBufferOut != NULL); + DRFLAC_ASSERT(oggbs != NULL); + DRFLAC_ASSERT(pRunningBufferOut != NULL); while (bytesRead < bytesToRead) { size_t bytesRemainingToRead = bytesToRead - bytesRead; if (oggbs->bytesRemainingInPage >= bytesRemainingToRead) { - MA_DR_FLAC_COPY_MEMORY(pRunningBufferOut, oggbs->pageData + (oggbs->pageDataSize - oggbs->bytesRemainingInPage), bytesRemainingToRead); + DRFLAC_COPY_MEMORY(pRunningBufferOut, oggbs->pageData + (oggbs->pageDataSize - oggbs->bytesRemainingInPage), bytesRemainingToRead); bytesRead += bytesRemainingToRead; - oggbs->bytesRemainingInPage -= (ma_uint32)bytesRemainingToRead; + oggbs->bytesRemainingInPage -= (drflac_uint32)bytesRemainingToRead; break; } if (oggbs->bytesRemainingInPage > 0) { - MA_DR_FLAC_COPY_MEMORY(pRunningBufferOut, oggbs->pageData + (oggbs->pageDataSize - oggbs->bytesRemainingInPage), oggbs->bytesRemainingInPage); + DRFLAC_COPY_MEMORY(pRunningBufferOut, oggbs->pageData + (oggbs->pageDataSize - oggbs->bytesRemainingInPage), oggbs->bytesRemainingInPage); bytesRead += oggbs->bytesRemainingInPage; pRunningBufferOut += oggbs->bytesRemainingInPage; oggbs->bytesRemainingInPage = 0; } - MA_DR_FLAC_ASSERT(bytesRemainingToRead > 0); - if (!ma_dr_flac_oggbs__goto_next_page(oggbs, ma_dr_flac_ogg_recover_on_crc_mismatch)) { + DRFLAC_ASSERT(bytesRemainingToRead > 0); + if (!drflac_oggbs__goto_next_page(oggbs, drflac_ogg_recover_on_crc_mismatch)) { break; } } return bytesRead; } -static ma_bool32 ma_dr_flac__on_seek_ogg(void* pUserData, int offset, ma_dr_flac_seek_origin origin) +static drflac_bool32 drflac__on_seek_ogg(void* pUserData, int offset, drflac_seek_origin origin) { - ma_dr_flac_oggbs* oggbs = (ma_dr_flac_oggbs*)pUserData; + drflac_oggbs* oggbs = (drflac_oggbs*)pUserData; int bytesSeeked = 0; - MA_DR_FLAC_ASSERT(oggbs != NULL); - MA_DR_FLAC_ASSERT(offset >= 0); - if (origin == ma_dr_flac_seek_origin_start) { - if (!ma_dr_flac_oggbs__seek_physical(oggbs, (int)oggbs->firstBytePos, ma_dr_flac_seek_origin_start)) { - return MA_FALSE; + DRFLAC_ASSERT(oggbs != NULL); + DRFLAC_ASSERT(offset >= 0); + if (origin == drflac_seek_origin_start) { + if (!drflac_oggbs__seek_physical(oggbs, (int)oggbs->firstBytePos, drflac_seek_origin_start)) { + return DRFLAC_FALSE; } - if (!ma_dr_flac_oggbs__goto_next_page(oggbs, ma_dr_flac_ogg_fail_on_crc_mismatch)) { - return MA_FALSE; + if (!drflac_oggbs__goto_next_page(oggbs, drflac_ogg_fail_on_crc_mismatch)) { + return DRFLAC_FALSE; } - return ma_dr_flac__on_seek_ogg(pUserData, offset, ma_dr_flac_seek_origin_current); + return drflac__on_seek_ogg(pUserData, offset, drflac_seek_origin_current); } - MA_DR_FLAC_ASSERT(origin == ma_dr_flac_seek_origin_current); + DRFLAC_ASSERT(origin == drflac_seek_origin_current); while (bytesSeeked < offset) { int bytesRemainingToSeek = offset - bytesSeeked; - MA_DR_FLAC_ASSERT(bytesRemainingToSeek >= 0); + DRFLAC_ASSERT(bytesRemainingToSeek >= 0); if (oggbs->bytesRemainingInPage >= (size_t)bytesRemainingToSeek) { bytesSeeked += bytesRemainingToSeek; (void)bytesSeeked; @@ -87492,39 +86366,39 @@ static ma_bool32 ma_dr_flac__on_seek_ogg(void* pUserData, int offset, ma_dr_flac bytesSeeked += (int)oggbs->bytesRemainingInPage; oggbs->bytesRemainingInPage = 0; } - MA_DR_FLAC_ASSERT(bytesRemainingToSeek > 0); - if (!ma_dr_flac_oggbs__goto_next_page(oggbs, ma_dr_flac_ogg_fail_on_crc_mismatch)) { - return MA_FALSE; + DRFLAC_ASSERT(bytesRemainingToSeek > 0); + if (!drflac_oggbs__goto_next_page(oggbs, drflac_ogg_fail_on_crc_mismatch)) { + return DRFLAC_FALSE; } } - return MA_TRUE; + return DRFLAC_TRUE; } -static ma_bool32 ma_dr_flac_ogg__seek_to_pcm_frame(ma_dr_flac* pFlac, ma_uint64 pcmFrameIndex) +static drflac_bool32 drflac_ogg__seek_to_pcm_frame(drflac* pFlac, drflac_uint64 pcmFrameIndex) { - ma_dr_flac_oggbs* oggbs = (ma_dr_flac_oggbs*)pFlac->_oggbs; - ma_uint64 originalBytePos; - ma_uint64 runningGranulePosition; - ma_uint64 runningFrameBytePos; - ma_uint64 runningPCMFrameCount; - MA_DR_FLAC_ASSERT(oggbs != NULL); + drflac_oggbs* oggbs = (drflac_oggbs*)pFlac->_oggbs; + drflac_uint64 originalBytePos; + drflac_uint64 runningGranulePosition; + drflac_uint64 runningFrameBytePos; + drflac_uint64 runningPCMFrameCount; + DRFLAC_ASSERT(oggbs != NULL); originalBytePos = oggbs->currentBytePos; - if (!ma_dr_flac__seek_to_byte(&pFlac->bs, pFlac->firstFLACFramePosInBytes)) { - return MA_FALSE; + if (!drflac__seek_to_byte(&pFlac->bs, pFlac->firstFLACFramePosInBytes)) { + return DRFLAC_FALSE; } oggbs->bytesRemainingInPage = 0; runningGranulePosition = 0; for (;;) { - if (!ma_dr_flac_oggbs__goto_next_page(oggbs, ma_dr_flac_ogg_recover_on_crc_mismatch)) { - ma_dr_flac_oggbs__seek_physical(oggbs, originalBytePos, ma_dr_flac_seek_origin_start); - return MA_FALSE; + if (!drflac_oggbs__goto_next_page(oggbs, drflac_ogg_recover_on_crc_mismatch)) { + drflac_oggbs__seek_physical(oggbs, originalBytePos, drflac_seek_origin_start); + return DRFLAC_FALSE; } - runningFrameBytePos = oggbs->currentBytePos - ma_dr_flac_ogg__get_page_header_size(&oggbs->currentPageHeader) - oggbs->pageDataSize; + runningFrameBytePos = oggbs->currentBytePos - drflac_ogg__get_page_header_size(&oggbs->currentPageHeader) - oggbs->pageDataSize; if (oggbs->currentPageHeader.granulePosition >= pcmFrameIndex) { break; } if ((oggbs->currentPageHeader.headerType & 0x01) == 0) { if (oggbs->currentPageHeader.segmentTable[0] >= 2) { - ma_uint8 firstBytesInPage[2]; + drflac_uint8 firstBytesInPage[2]; firstBytesInPage[0] = oggbs->pageData[0]; firstBytesInPage[1] = oggbs->pageData[1]; if ((firstBytesInPage[0] == 0xFF) && (firstBytesInPage[1] & 0xFC) == 0xF8) { @@ -87534,120 +86408,120 @@ static ma_bool32 ma_dr_flac_ogg__seek_to_pcm_frame(ma_dr_flac* pFlac, ma_uint64 } } } - if (!ma_dr_flac_oggbs__seek_physical(oggbs, runningFrameBytePos, ma_dr_flac_seek_origin_start)) { - return MA_FALSE; + if (!drflac_oggbs__seek_physical(oggbs, runningFrameBytePos, drflac_seek_origin_start)) { + return DRFLAC_FALSE; } - if (!ma_dr_flac_oggbs__goto_next_page(oggbs, ma_dr_flac_ogg_recover_on_crc_mismatch)) { - return MA_FALSE; + if (!drflac_oggbs__goto_next_page(oggbs, drflac_ogg_recover_on_crc_mismatch)) { + return DRFLAC_FALSE; } runningPCMFrameCount = runningGranulePosition; for (;;) { - ma_uint64 firstPCMFrameInFLACFrame = 0; - ma_uint64 lastPCMFrameInFLACFrame = 0; - ma_uint64 pcmFrameCountInThisFrame; - if (!ma_dr_flac__read_next_flac_frame_header(&pFlac->bs, pFlac->bitsPerSample, &pFlac->currentFLACFrame.header)) { - return MA_FALSE; + drflac_uint64 firstPCMFrameInFLACFrame = 0; + drflac_uint64 lastPCMFrameInFLACFrame = 0; + drflac_uint64 pcmFrameCountInThisFrame; + if (!drflac__read_next_flac_frame_header(&pFlac->bs, pFlac->bitsPerSample, &pFlac->currentFLACFrame.header)) { + return DRFLAC_FALSE; } - ma_dr_flac__get_pcm_frame_range_of_current_flac_frame(pFlac, &firstPCMFrameInFLACFrame, &lastPCMFrameInFLACFrame); + drflac__get_pcm_frame_range_of_current_flac_frame(pFlac, &firstPCMFrameInFLACFrame, &lastPCMFrameInFLACFrame); pcmFrameCountInThisFrame = (lastPCMFrameInFLACFrame - firstPCMFrameInFLACFrame) + 1; if (pcmFrameIndex == pFlac->totalPCMFrameCount && (runningPCMFrameCount + pcmFrameCountInThisFrame) == pFlac->totalPCMFrameCount) { - ma_result result = ma_dr_flac__decode_flac_frame(pFlac); - if (result == MA_SUCCESS) { + drflac_result result = drflac__decode_flac_frame(pFlac); + if (result == DRFLAC_SUCCESS) { pFlac->currentPCMFrame = pcmFrameIndex; pFlac->currentFLACFrame.pcmFramesRemaining = 0; - return MA_TRUE; + return DRFLAC_TRUE; } else { - return MA_FALSE; + return DRFLAC_FALSE; } } if (pcmFrameIndex < (runningPCMFrameCount + pcmFrameCountInThisFrame)) { - ma_result result = ma_dr_flac__decode_flac_frame(pFlac); - if (result == MA_SUCCESS) { - ma_uint64 pcmFramesToDecode = (size_t)(pcmFrameIndex - runningPCMFrameCount); + drflac_result result = drflac__decode_flac_frame(pFlac); + if (result == DRFLAC_SUCCESS) { + drflac_uint64 pcmFramesToDecode = (size_t)(pcmFrameIndex - runningPCMFrameCount); if (pcmFramesToDecode == 0) { - return MA_TRUE; + return DRFLAC_TRUE; } pFlac->currentPCMFrame = runningPCMFrameCount; - return ma_dr_flac__seek_forward_by_pcm_frames(pFlac, pcmFramesToDecode) == pcmFramesToDecode; + return drflac__seek_forward_by_pcm_frames(pFlac, pcmFramesToDecode) == pcmFramesToDecode; } else { - if (result == MA_CRC_MISMATCH) { + if (result == DRFLAC_CRC_MISMATCH) { continue; } else { - return MA_FALSE; + return DRFLAC_FALSE; } } } else { - ma_result result = ma_dr_flac__seek_to_next_flac_frame(pFlac); - if (result == MA_SUCCESS) { + drflac_result result = drflac__seek_to_next_flac_frame(pFlac); + if (result == DRFLAC_SUCCESS) { runningPCMFrameCount += pcmFrameCountInThisFrame; } else { - if (result == MA_CRC_MISMATCH) { + if (result == DRFLAC_CRC_MISMATCH) { continue; } else { - return MA_FALSE; + return DRFLAC_FALSE; } } } } } -static ma_bool32 ma_dr_flac__init_private__ogg(ma_dr_flac_init_info* pInit, ma_dr_flac_read_proc onRead, ma_dr_flac_seek_proc onSeek, ma_dr_flac_meta_proc onMeta, void* pUserData, void* pUserDataMD, ma_bool32 relaxed) +static drflac_bool32 drflac__init_private__ogg(drflac_init_info* pInit, drflac_read_proc onRead, drflac_seek_proc onSeek, drflac_meta_proc onMeta, void* pUserData, void* pUserDataMD, drflac_bool32 relaxed) { - ma_dr_flac_ogg_page_header header; - ma_uint32 crc32 = MA_DR_FLAC_OGG_CAPTURE_PATTERN_CRC32; - ma_uint32 bytesRead = 0; + drflac_ogg_page_header header; + drflac_uint32 crc32 = DRFLAC_OGG_CAPTURE_PATTERN_CRC32; + drflac_uint32 bytesRead = 0; (void)relaxed; - pInit->container = ma_dr_flac_container_ogg; + pInit->container = drflac_container_ogg; pInit->oggFirstBytePos = 0; - if (ma_dr_flac_ogg__read_page_header_after_capture_pattern(onRead, pUserData, &header, &bytesRead, &crc32) != MA_SUCCESS) { - return MA_FALSE; + if (drflac_ogg__read_page_header_after_capture_pattern(onRead, pUserData, &header, &bytesRead, &crc32) != DRFLAC_SUCCESS) { + return DRFLAC_FALSE; } pInit->runningFilePos += bytesRead; for (;;) { int pageBodySize; if ((header.headerType & 0x02) == 0) { - return MA_FALSE; + return DRFLAC_FALSE; } - pageBodySize = ma_dr_flac_ogg__get_page_body_size(&header); + pageBodySize = drflac_ogg__get_page_body_size(&header); if (pageBodySize == 51) { - ma_uint32 bytesRemainingInPage = pageBodySize; - ma_uint8 packetType; + drflac_uint32 bytesRemainingInPage = pageBodySize; + drflac_uint8 packetType; if (onRead(pUserData, &packetType, 1) != 1) { - return MA_FALSE; + return DRFLAC_FALSE; } bytesRemainingInPage -= 1; if (packetType == 0x7F) { - ma_uint8 sig[4]; + drflac_uint8 sig[4]; if (onRead(pUserData, sig, 4) != 4) { - return MA_FALSE; + return DRFLAC_FALSE; } bytesRemainingInPage -= 4; if (sig[0] == 'F' && sig[1] == 'L' && sig[2] == 'A' && sig[3] == 'C') { - ma_uint8 mappingVersion[2]; + drflac_uint8 mappingVersion[2]; if (onRead(pUserData, mappingVersion, 2) != 2) { - return MA_FALSE; + return DRFLAC_FALSE; } if (mappingVersion[0] != 1) { - return MA_FALSE; + return DRFLAC_FALSE; } - if (!onSeek(pUserData, 2, ma_dr_flac_seek_origin_current)) { - return MA_FALSE; + if (!onSeek(pUserData, 2, drflac_seek_origin_current)) { + return DRFLAC_FALSE; } if (onRead(pUserData, sig, 4) != 4) { - return MA_FALSE; + return DRFLAC_FALSE; } if (sig[0] == 'f' && sig[1] == 'L' && sig[2] == 'a' && sig[3] == 'C') { - ma_dr_flac_streaminfo streaminfo; - ma_uint8 isLastBlock; - ma_uint8 blockType; - ma_uint32 blockSize; - if (!ma_dr_flac__read_and_decode_block_header(onRead, pUserData, &isLastBlock, &blockType, &blockSize)) { - return MA_FALSE; + drflac_streaminfo streaminfo; + drflac_uint8 isLastBlock; + drflac_uint8 blockType; + drflac_uint32 blockSize; + if (!drflac__read_and_decode_block_header(onRead, pUserData, &isLastBlock, &blockType, &blockSize)) { + return DRFLAC_FALSE; } - if (blockType != MA_DR_FLAC_METADATA_BLOCK_TYPE_STREAMINFO || blockSize != 34) { - return MA_FALSE; + if (blockType != DRFLAC_METADATA_BLOCK_TYPE_STREAMINFO || blockSize != 34) { + return DRFLAC_FALSE; } - if (ma_dr_flac__read_streaminfo(onRead, pUserData, &streaminfo)) { - pInit->hasStreamInfoBlock = MA_TRUE; + if (drflac__read_streaminfo(onRead, pUserData, &streaminfo)) { + pInit->hasStreamInfoBlock = DRFLAC_TRUE; pInit->sampleRate = streaminfo.sampleRate; pInit->channels = streaminfo.channels; pInit->bitsPerSample = streaminfo.bitsPerSample; @@ -87655,8 +86529,8 @@ static ma_bool32 ma_dr_flac__init_private__ogg(ma_dr_flac_init_info* pInit, ma_d pInit->maxBlockSizeInPCMFrames = streaminfo.maxBlockSizeInPCMFrames; pInit->hasMetadataBlocks = !isLastBlock; if (onMeta) { - ma_dr_flac_metadata metadata; - metadata.type = MA_DR_FLAC_METADATA_BLOCK_TYPE_STREAMINFO; + drflac_metadata metadata; + metadata.type = DRFLAC_METADATA_BLOCK_TYPE_STREAMINFO; metadata.pRawData = NULL; metadata.rawDataSize = 0; metadata.data.streaminfo = streaminfo; @@ -87668,44 +86542,44 @@ static ma_bool32 ma_dr_flac__init_private__ogg(ma_dr_flac_init_info* pInit, ma_d pInit->oggBosHeader = header; break; } else { - return MA_FALSE; + return DRFLAC_FALSE; } } else { - return MA_FALSE; + return DRFLAC_FALSE; } } else { - if (!onSeek(pUserData, bytesRemainingInPage, ma_dr_flac_seek_origin_current)) { - return MA_FALSE; + if (!onSeek(pUserData, bytesRemainingInPage, drflac_seek_origin_current)) { + return DRFLAC_FALSE; } } } else { - if (!onSeek(pUserData, bytesRemainingInPage, ma_dr_flac_seek_origin_current)) { - return MA_FALSE; + if (!onSeek(pUserData, bytesRemainingInPage, drflac_seek_origin_current)) { + return DRFLAC_FALSE; } } } else { - if (!onSeek(pUserData, pageBodySize, ma_dr_flac_seek_origin_current)) { - return MA_FALSE; + if (!onSeek(pUserData, pageBodySize, drflac_seek_origin_current)) { + return DRFLAC_FALSE; } } pInit->runningFilePos += pageBodySize; - if (ma_dr_flac_ogg__read_page_header(onRead, pUserData, &header, &bytesRead, &crc32) != MA_SUCCESS) { - return MA_FALSE; + if (drflac_ogg__read_page_header(onRead, pUserData, &header, &bytesRead, &crc32) != DRFLAC_SUCCESS) { + return DRFLAC_FALSE; } pInit->runningFilePos += bytesRead; } - pInit->hasMetadataBlocks = MA_TRUE; - return MA_TRUE; + pInit->hasMetadataBlocks = DRFLAC_TRUE; + return DRFLAC_TRUE; } #endif -static ma_bool32 ma_dr_flac__init_private(ma_dr_flac_init_info* pInit, ma_dr_flac_read_proc onRead, ma_dr_flac_seek_proc onSeek, ma_dr_flac_meta_proc onMeta, ma_dr_flac_container container, void* pUserData, void* pUserDataMD) +static drflac_bool32 drflac__init_private(drflac_init_info* pInit, drflac_read_proc onRead, drflac_seek_proc onSeek, drflac_meta_proc onMeta, drflac_container container, void* pUserData, void* pUserDataMD) { - ma_bool32 relaxed; - ma_uint8 id[4]; + drflac_bool32 relaxed; + drflac_uint8 id[4]; if (pInit == NULL || onRead == NULL || onSeek == NULL) { - return MA_FALSE; + return DRFLAC_FALSE; } - MA_DR_FLAC_ZERO_MEMORY(pInit, sizeof(*pInit)); + DRFLAC_ZERO_MEMORY(pInit, sizeof(*pInit)); pInit->onRead = onRead; pInit->onSeek = onSeek; pInit->onMeta = onMeta; @@ -87715,29 +86589,29 @@ static ma_bool32 ma_dr_flac__init_private(ma_dr_flac_init_info* pInit, ma_dr_fla pInit->bs.onRead = onRead; pInit->bs.onSeek = onSeek; pInit->bs.pUserData = pUserData; - ma_dr_flac__reset_cache(&pInit->bs); - relaxed = container != ma_dr_flac_container_unknown; + drflac__reset_cache(&pInit->bs); + relaxed = container != drflac_container_unknown; for (;;) { if (onRead(pUserData, id, 4) != 4) { - return MA_FALSE; + return DRFLAC_FALSE; } pInit->runningFilePos += 4; if (id[0] == 'I' && id[1] == 'D' && id[2] == '3') { - ma_uint8 header[6]; - ma_uint8 flags; - ma_uint32 headerSize; + drflac_uint8 header[6]; + drflac_uint8 flags; + drflac_uint32 headerSize; if (onRead(pUserData, header, 6) != 6) { - return MA_FALSE; + return DRFLAC_FALSE; } pInit->runningFilePos += 6; flags = header[1]; - MA_DR_FLAC_COPY_MEMORY(&headerSize, header+2, 4); - headerSize = ma_dr_flac__unsynchsafe_32(ma_dr_flac__be2host_32(headerSize)); + DRFLAC_COPY_MEMORY(&headerSize, header+2, 4); + headerSize = drflac__unsynchsafe_32(drflac__be2host_32(headerSize)); if (flags & 0x10) { headerSize += 10; } - if (!onSeek(pUserData, headerSize, ma_dr_flac_seek_origin_current)) { - return MA_FALSE; + if (!onSeek(pUserData, headerSize, drflac_seek_origin_current)) { + return DRFLAC_FALSE; } pInit->runningFilePos += headerSize; } else { @@ -87745,56 +86619,56 @@ static ma_bool32 ma_dr_flac__init_private(ma_dr_flac_init_info* pInit, ma_dr_fla } } if (id[0] == 'f' && id[1] == 'L' && id[2] == 'a' && id[3] == 'C') { - return ma_dr_flac__init_private__native(pInit, onRead, onSeek, onMeta, pUserData, pUserDataMD, relaxed); + return drflac__init_private__native(pInit, onRead, onSeek, onMeta, pUserData, pUserDataMD, relaxed); } -#ifndef MA_DR_FLAC_NO_OGG +#ifndef DR_FLAC_NO_OGG if (id[0] == 'O' && id[1] == 'g' && id[2] == 'g' && id[3] == 'S') { - return ma_dr_flac__init_private__ogg(pInit, onRead, onSeek, onMeta, pUserData, pUserDataMD, relaxed); + return drflac__init_private__ogg(pInit, onRead, onSeek, onMeta, pUserData, pUserDataMD, relaxed); } #endif if (relaxed) { - if (container == ma_dr_flac_container_native) { - return ma_dr_flac__init_private__native(pInit, onRead, onSeek, onMeta, pUserData, pUserDataMD, relaxed); + if (container == drflac_container_native) { + return drflac__init_private__native(pInit, onRead, onSeek, onMeta, pUserData, pUserDataMD, relaxed); } -#ifndef MA_DR_FLAC_NO_OGG - if (container == ma_dr_flac_container_ogg) { - return ma_dr_flac__init_private__ogg(pInit, onRead, onSeek, onMeta, pUserData, pUserDataMD, relaxed); +#ifndef DR_FLAC_NO_OGG + if (container == drflac_container_ogg) { + return drflac__init_private__ogg(pInit, onRead, onSeek, onMeta, pUserData, pUserDataMD, relaxed); } #endif } - return MA_FALSE; + return DRFLAC_FALSE; } -static void ma_dr_flac__init_from_info(ma_dr_flac* pFlac, const ma_dr_flac_init_info* pInit) +static void drflac__init_from_info(drflac* pFlac, const drflac_init_info* pInit) { - MA_DR_FLAC_ASSERT(pFlac != NULL); - MA_DR_FLAC_ASSERT(pInit != NULL); - MA_DR_FLAC_ZERO_MEMORY(pFlac, sizeof(*pFlac)); + DRFLAC_ASSERT(pFlac != NULL); + DRFLAC_ASSERT(pInit != NULL); + DRFLAC_ZERO_MEMORY(pFlac, sizeof(*pFlac)); pFlac->bs = pInit->bs; pFlac->onMeta = pInit->onMeta; pFlac->pUserDataMD = pInit->pUserDataMD; pFlac->maxBlockSizeInPCMFrames = pInit->maxBlockSizeInPCMFrames; pFlac->sampleRate = pInit->sampleRate; - pFlac->channels = (ma_uint8)pInit->channels; - pFlac->bitsPerSample = (ma_uint8)pInit->bitsPerSample; + pFlac->channels = (drflac_uint8)pInit->channels; + pFlac->bitsPerSample = (drflac_uint8)pInit->bitsPerSample; pFlac->totalPCMFrameCount = pInit->totalPCMFrameCount; pFlac->container = pInit->container; } -static ma_dr_flac* ma_dr_flac_open_with_metadata_private(ma_dr_flac_read_proc onRead, ma_dr_flac_seek_proc onSeek, ma_dr_flac_meta_proc onMeta, ma_dr_flac_container container, void* pUserData, void* pUserDataMD, const ma_allocation_callbacks* pAllocationCallbacks) -{ - ma_dr_flac_init_info init; - ma_uint32 allocationSize; - ma_uint32 wholeSIMDVectorCountPerChannel; - ma_uint32 decodedSamplesAllocationSize; -#ifndef MA_DR_FLAC_NO_OGG - ma_dr_flac_oggbs* pOggbs = NULL; -#endif - ma_uint64 firstFramePos; - ma_uint64 seektablePos; - ma_uint32 seekpointCount; - ma_allocation_callbacks allocationCallbacks; - ma_dr_flac* pFlac; - ma_dr_flac__init_cpu_caps(); - if (!ma_dr_flac__init_private(&init, onRead, onSeek, onMeta, container, pUserData, pUserDataMD)) { +static drflac* drflac_open_with_metadata_private(drflac_read_proc onRead, drflac_seek_proc onSeek, drflac_meta_proc onMeta, drflac_container container, void* pUserData, void* pUserDataMD, const drflac_allocation_callbacks* pAllocationCallbacks) +{ + drflac_init_info init; + drflac_uint32 allocationSize; + drflac_uint32 wholeSIMDVectorCountPerChannel; + drflac_uint32 decodedSamplesAllocationSize; +#ifndef DR_FLAC_NO_OGG + drflac_oggbs* pOggbs = NULL; +#endif + drflac_uint64 firstFramePos; + drflac_uint64 seektablePos; + drflac_uint32 seekpointCount; + drflac_allocation_callbacks allocationCallbacks; + drflac* pFlac; + drflac__init_cpu_caps(); + if (!drflac__init_private(&init, onRead, onSeek, onMeta, container, pUserData, pUserDataMD)) { return NULL; } if (pAllocationCallbacks != NULL) { @@ -87804,27 +86678,27 @@ static ma_dr_flac* ma_dr_flac_open_with_metadata_private(ma_dr_flac_read_proc on } } else { allocationCallbacks.pUserData = NULL; - allocationCallbacks.onMalloc = ma_dr_flac__malloc_default; - allocationCallbacks.onRealloc = ma_dr_flac__realloc_default; - allocationCallbacks.onFree = ma_dr_flac__free_default; + allocationCallbacks.onMalloc = drflac__malloc_default; + allocationCallbacks.onRealloc = drflac__realloc_default; + allocationCallbacks.onFree = drflac__free_default; } - allocationSize = sizeof(ma_dr_flac); - if ((init.maxBlockSizeInPCMFrames % (MA_DR_FLAC_MAX_SIMD_VECTOR_SIZE / sizeof(ma_int32))) == 0) { - wholeSIMDVectorCountPerChannel = (init.maxBlockSizeInPCMFrames / (MA_DR_FLAC_MAX_SIMD_VECTOR_SIZE / sizeof(ma_int32))); + allocationSize = sizeof(drflac); + if ((init.maxBlockSizeInPCMFrames % (DRFLAC_MAX_SIMD_VECTOR_SIZE / sizeof(drflac_int32))) == 0) { + wholeSIMDVectorCountPerChannel = (init.maxBlockSizeInPCMFrames / (DRFLAC_MAX_SIMD_VECTOR_SIZE / sizeof(drflac_int32))); } else { - wholeSIMDVectorCountPerChannel = (init.maxBlockSizeInPCMFrames / (MA_DR_FLAC_MAX_SIMD_VECTOR_SIZE / sizeof(ma_int32))) + 1; + wholeSIMDVectorCountPerChannel = (init.maxBlockSizeInPCMFrames / (DRFLAC_MAX_SIMD_VECTOR_SIZE / sizeof(drflac_int32))) + 1; } - decodedSamplesAllocationSize = wholeSIMDVectorCountPerChannel * MA_DR_FLAC_MAX_SIMD_VECTOR_SIZE * init.channels; + decodedSamplesAllocationSize = wholeSIMDVectorCountPerChannel * DRFLAC_MAX_SIMD_VECTOR_SIZE * init.channels; allocationSize += decodedSamplesAllocationSize; - allocationSize += MA_DR_FLAC_MAX_SIMD_VECTOR_SIZE; -#ifndef MA_DR_FLAC_NO_OGG - if (init.container == ma_dr_flac_container_ogg) { - allocationSize += sizeof(ma_dr_flac_oggbs); - pOggbs = (ma_dr_flac_oggbs*)ma_dr_flac__malloc_from_callbacks(sizeof(*pOggbs), &allocationCallbacks); + allocationSize += DRFLAC_MAX_SIMD_VECTOR_SIZE; +#ifndef DR_FLAC_NO_OGG + if (init.container == drflac_container_ogg) { + allocationSize += sizeof(drflac_oggbs); + pOggbs = (drflac_oggbs*)drflac__malloc_from_callbacks(sizeof(*pOggbs), &allocationCallbacks); if (pOggbs == NULL) { return NULL; } - MA_DR_FLAC_ZERO_MEMORY(pOggbs, sizeof(*pOggbs)); + DRFLAC_ZERO_MEMORY(pOggbs, sizeof(*pOggbs)); pOggbs->onRead = onRead; pOggbs->onSeek = onSeek; pOggbs->pUserData = pUserData; @@ -87839,49 +86713,49 @@ static ma_dr_flac* ma_dr_flac_open_with_metadata_private(ma_dr_flac_read_proc on seektablePos = 0; seekpointCount = 0; if (init.hasMetadataBlocks) { - ma_dr_flac_read_proc onReadOverride = onRead; - ma_dr_flac_seek_proc onSeekOverride = onSeek; + drflac_read_proc onReadOverride = onRead; + drflac_seek_proc onSeekOverride = onSeek; void* pUserDataOverride = pUserData; -#ifndef MA_DR_FLAC_NO_OGG - if (init.container == ma_dr_flac_container_ogg) { - onReadOverride = ma_dr_flac__on_read_ogg; - onSeekOverride = ma_dr_flac__on_seek_ogg; +#ifndef DR_FLAC_NO_OGG + if (init.container == drflac_container_ogg) { + onReadOverride = drflac__on_read_ogg; + onSeekOverride = drflac__on_seek_ogg; pUserDataOverride = (void*)pOggbs; } #endif - if (!ma_dr_flac__read_and_decode_metadata(onReadOverride, onSeekOverride, onMeta, pUserDataOverride, pUserDataMD, &firstFramePos, &seektablePos, &seekpointCount, &allocationCallbacks)) { - #ifndef MA_DR_FLAC_NO_OGG - ma_dr_flac__free_from_callbacks(pOggbs, &allocationCallbacks); + if (!drflac__read_and_decode_metadata(onReadOverride, onSeekOverride, onMeta, pUserDataOverride, pUserDataMD, &firstFramePos, &seektablePos, &seekpointCount, &allocationCallbacks)) { + #ifndef DR_FLAC_NO_OGG + drflac__free_from_callbacks(pOggbs, &allocationCallbacks); #endif return NULL; } - allocationSize += seekpointCount * sizeof(ma_dr_flac_seekpoint); + allocationSize += seekpointCount * sizeof(drflac_seekpoint); } - pFlac = (ma_dr_flac*)ma_dr_flac__malloc_from_callbacks(allocationSize, &allocationCallbacks); + pFlac = (drflac*)drflac__malloc_from_callbacks(allocationSize, &allocationCallbacks); if (pFlac == NULL) { - #ifndef MA_DR_FLAC_NO_OGG - ma_dr_flac__free_from_callbacks(pOggbs, &allocationCallbacks); + #ifndef DR_FLAC_NO_OGG + drflac__free_from_callbacks(pOggbs, &allocationCallbacks); #endif return NULL; } - ma_dr_flac__init_from_info(pFlac, &init); + drflac__init_from_info(pFlac, &init); pFlac->allocationCallbacks = allocationCallbacks; - pFlac->pDecodedSamples = (ma_int32*)ma_dr_flac_align((size_t)pFlac->pExtraData, MA_DR_FLAC_MAX_SIMD_VECTOR_SIZE); -#ifndef MA_DR_FLAC_NO_OGG - if (init.container == ma_dr_flac_container_ogg) { - ma_dr_flac_oggbs* pInternalOggbs = (ma_dr_flac_oggbs*)((ma_uint8*)pFlac->pDecodedSamples + decodedSamplesAllocationSize + (seekpointCount * sizeof(ma_dr_flac_seekpoint))); - MA_DR_FLAC_COPY_MEMORY(pInternalOggbs, pOggbs, sizeof(*pOggbs)); - ma_dr_flac__free_from_callbacks(pOggbs, &allocationCallbacks); + pFlac->pDecodedSamples = (drflac_int32*)drflac_align((size_t)pFlac->pExtraData, DRFLAC_MAX_SIMD_VECTOR_SIZE); +#ifndef DR_FLAC_NO_OGG + if (init.container == drflac_container_ogg) { + drflac_oggbs* pInternalOggbs = (drflac_oggbs*)((drflac_uint8*)pFlac->pDecodedSamples + decodedSamplesAllocationSize + (seekpointCount * sizeof(drflac_seekpoint))); + DRFLAC_COPY_MEMORY(pInternalOggbs, pOggbs, sizeof(*pOggbs)); + drflac__free_from_callbacks(pOggbs, &allocationCallbacks); pOggbs = NULL; - pFlac->bs.onRead = ma_dr_flac__on_read_ogg; - pFlac->bs.onSeek = ma_dr_flac__on_seek_ogg; + pFlac->bs.onRead = drflac__on_read_ogg; + pFlac->bs.onSeek = drflac__on_seek_ogg; pFlac->bs.pUserData = (void*)pInternalOggbs; pFlac->_oggbs = (void*)pInternalOggbs; } #endif pFlac->firstFLACFramePosInBytes = firstFramePos; -#ifndef MA_DR_FLAC_NO_OGG - if (init.container == ma_dr_flac_container_ogg) +#ifndef DR_FLAC_NO_OGG + if (init.container == drflac_container_ogg) { pFlac->pSeekpoints = NULL; pFlac->seekpointCount = 0; @@ -87891,24 +86765,24 @@ static ma_dr_flac* ma_dr_flac_open_with_metadata_private(ma_dr_flac_read_proc on { if (seektablePos != 0) { pFlac->seekpointCount = seekpointCount; - pFlac->pSeekpoints = (ma_dr_flac_seekpoint*)((ma_uint8*)pFlac->pDecodedSamples + decodedSamplesAllocationSize); - MA_DR_FLAC_ASSERT(pFlac->bs.onSeek != NULL); - MA_DR_FLAC_ASSERT(pFlac->bs.onRead != NULL); - if (pFlac->bs.onSeek(pFlac->bs.pUserData, (int)seektablePos, ma_dr_flac_seek_origin_start)) { - ma_uint32 iSeekpoint; + pFlac->pSeekpoints = (drflac_seekpoint*)((drflac_uint8*)pFlac->pDecodedSamples + decodedSamplesAllocationSize); + DRFLAC_ASSERT(pFlac->bs.onSeek != NULL); + DRFLAC_ASSERT(pFlac->bs.onRead != NULL); + if (pFlac->bs.onSeek(pFlac->bs.pUserData, (int)seektablePos, drflac_seek_origin_start)) { + drflac_uint32 iSeekpoint; for (iSeekpoint = 0; iSeekpoint < seekpointCount; iSeekpoint += 1) { - if (pFlac->bs.onRead(pFlac->bs.pUserData, pFlac->pSeekpoints + iSeekpoint, MA_DR_FLAC_SEEKPOINT_SIZE_IN_BYTES) == MA_DR_FLAC_SEEKPOINT_SIZE_IN_BYTES) { - pFlac->pSeekpoints[iSeekpoint].firstPCMFrame = ma_dr_flac__be2host_64(pFlac->pSeekpoints[iSeekpoint].firstPCMFrame); - pFlac->pSeekpoints[iSeekpoint].flacFrameOffset = ma_dr_flac__be2host_64(pFlac->pSeekpoints[iSeekpoint].flacFrameOffset); - pFlac->pSeekpoints[iSeekpoint].pcmFrameCount = ma_dr_flac__be2host_16(pFlac->pSeekpoints[iSeekpoint].pcmFrameCount); + if (pFlac->bs.onRead(pFlac->bs.pUserData, pFlac->pSeekpoints + iSeekpoint, DRFLAC_SEEKPOINT_SIZE_IN_BYTES) == DRFLAC_SEEKPOINT_SIZE_IN_BYTES) { + pFlac->pSeekpoints[iSeekpoint].firstPCMFrame = drflac__be2host_64(pFlac->pSeekpoints[iSeekpoint].firstPCMFrame); + pFlac->pSeekpoints[iSeekpoint].flacFrameOffset = drflac__be2host_64(pFlac->pSeekpoints[iSeekpoint].flacFrameOffset); + pFlac->pSeekpoints[iSeekpoint].pcmFrameCount = drflac__be2host_16(pFlac->pSeekpoints[iSeekpoint].pcmFrameCount); } else { pFlac->pSeekpoints = NULL; pFlac->seekpointCount = 0; break; } } - if (!pFlac->bs.onSeek(pFlac->bs.pUserData, (int)pFlac->firstFLACFramePosInBytes, ma_dr_flac_seek_origin_start)) { - ma_dr_flac__free_from_callbacks(pFlac, &allocationCallbacks); + if (!pFlac->bs.onSeek(pFlac->bs.pUserData, (int)pFlac->firstFLACFramePosInBytes, drflac_seek_origin_start)) { + drflac__free_from_callbacks(pFlac, &allocationCallbacks); return NULL; } } else { @@ -87920,18 +86794,18 @@ static ma_dr_flac* ma_dr_flac_open_with_metadata_private(ma_dr_flac_read_proc on if (!init.hasStreamInfoBlock) { pFlac->currentFLACFrame.header = init.firstFrameHeader; for (;;) { - ma_result result = ma_dr_flac__decode_flac_frame(pFlac); - if (result == MA_SUCCESS) { + drflac_result result = drflac__decode_flac_frame(pFlac); + if (result == DRFLAC_SUCCESS) { break; } else { - if (result == MA_CRC_MISMATCH) { - if (!ma_dr_flac__read_next_flac_frame_header(&pFlac->bs, pFlac->bitsPerSample, &pFlac->currentFLACFrame.header)) { - ma_dr_flac__free_from_callbacks(pFlac, &allocationCallbacks); + if (result == DRFLAC_CRC_MISMATCH) { + if (!drflac__read_next_flac_frame_header(&pFlac->bs, pFlac->bitsPerSample, &pFlac->currentFLACFrame.header)) { + drflac__free_from_callbacks(pFlac, &allocationCallbacks); return NULL; } continue; } else { - ma_dr_flac__free_from_callbacks(pFlac, &allocationCallbacks); + drflac__free_from_callbacks(pFlac, &allocationCallbacks); return NULL; } } @@ -87939,43 +86813,555 @@ static ma_dr_flac* ma_dr_flac_open_with_metadata_private(ma_dr_flac_read_proc on } return pFlac; } -#ifndef MA_DR_FLAC_NO_STDIO +#ifndef DR_FLAC_NO_STDIO #include -#ifndef MA_DR_FLAC_NO_WCHAR +#ifndef DR_FLAC_NO_WCHAR #include #endif -static size_t ma_dr_flac__on_read_stdio(void* pUserData, void* bufferOut, size_t bytesToRead) +#include +static drflac_result drflac_result_from_errno(int e) +{ + switch (e) + { + case 0: return DRFLAC_SUCCESS; + #ifdef EPERM + case EPERM: return DRFLAC_INVALID_OPERATION; + #endif + #ifdef ENOENT + case ENOENT: return DRFLAC_DOES_NOT_EXIST; + #endif + #ifdef ESRCH + case ESRCH: return DRFLAC_DOES_NOT_EXIST; + #endif + #ifdef EINTR + case EINTR: return DRFLAC_INTERRUPT; + #endif + #ifdef EIO + case EIO: return DRFLAC_IO_ERROR; + #endif + #ifdef ENXIO + case ENXIO: return DRFLAC_DOES_NOT_EXIST; + #endif + #ifdef E2BIG + case E2BIG: return DRFLAC_INVALID_ARGS; + #endif + #ifdef ENOEXEC + case ENOEXEC: return DRFLAC_INVALID_FILE; + #endif + #ifdef EBADF + case EBADF: return DRFLAC_INVALID_FILE; + #endif + #ifdef ECHILD + case ECHILD: return DRFLAC_ERROR; + #endif + #ifdef EAGAIN + case EAGAIN: return DRFLAC_UNAVAILABLE; + #endif + #ifdef ENOMEM + case ENOMEM: return DRFLAC_OUT_OF_MEMORY; + #endif + #ifdef EACCES + case EACCES: return DRFLAC_ACCESS_DENIED; + #endif + #ifdef EFAULT + case EFAULT: return DRFLAC_BAD_ADDRESS; + #endif + #ifdef ENOTBLK + case ENOTBLK: return DRFLAC_ERROR; + #endif + #ifdef EBUSY + case EBUSY: return DRFLAC_BUSY; + #endif + #ifdef EEXIST + case EEXIST: return DRFLAC_ALREADY_EXISTS; + #endif + #ifdef EXDEV + case EXDEV: return DRFLAC_ERROR; + #endif + #ifdef ENODEV + case ENODEV: return DRFLAC_DOES_NOT_EXIST; + #endif + #ifdef ENOTDIR + case ENOTDIR: return DRFLAC_NOT_DIRECTORY; + #endif + #ifdef EISDIR + case EISDIR: return DRFLAC_IS_DIRECTORY; + #endif + #ifdef EINVAL + case EINVAL: return DRFLAC_INVALID_ARGS; + #endif + #ifdef ENFILE + case ENFILE: return DRFLAC_TOO_MANY_OPEN_FILES; + #endif + #ifdef EMFILE + case EMFILE: return DRFLAC_TOO_MANY_OPEN_FILES; + #endif + #ifdef ENOTTY + case ENOTTY: return DRFLAC_INVALID_OPERATION; + #endif + #ifdef ETXTBSY + case ETXTBSY: return DRFLAC_BUSY; + #endif + #ifdef EFBIG + case EFBIG: return DRFLAC_TOO_BIG; + #endif + #ifdef ENOSPC + case ENOSPC: return DRFLAC_NO_SPACE; + #endif + #ifdef ESPIPE + case ESPIPE: return DRFLAC_BAD_SEEK; + #endif + #ifdef EROFS + case EROFS: return DRFLAC_ACCESS_DENIED; + #endif + #ifdef EMLINK + case EMLINK: return DRFLAC_TOO_MANY_LINKS; + #endif + #ifdef EPIPE + case EPIPE: return DRFLAC_BAD_PIPE; + #endif + #ifdef EDOM + case EDOM: return DRFLAC_OUT_OF_RANGE; + #endif + #ifdef ERANGE + case ERANGE: return DRFLAC_OUT_OF_RANGE; + #endif + #ifdef EDEADLK + case EDEADLK: return DRFLAC_DEADLOCK; + #endif + #ifdef ENAMETOOLONG + case ENAMETOOLONG: return DRFLAC_PATH_TOO_LONG; + #endif + #ifdef ENOLCK + case ENOLCK: return DRFLAC_ERROR; + #endif + #ifdef ENOSYS + case ENOSYS: return DRFLAC_NOT_IMPLEMENTED; + #endif + #ifdef ENOTEMPTY + case ENOTEMPTY: return DRFLAC_DIRECTORY_NOT_EMPTY; + #endif + #ifdef ELOOP + case ELOOP: return DRFLAC_TOO_MANY_LINKS; + #endif + #ifdef ENOMSG + case ENOMSG: return DRFLAC_NO_MESSAGE; + #endif + #ifdef EIDRM + case EIDRM: return DRFLAC_ERROR; + #endif + #ifdef ECHRNG + case ECHRNG: return DRFLAC_ERROR; + #endif + #ifdef EL2NSYNC + case EL2NSYNC: return DRFLAC_ERROR; + #endif + #ifdef EL3HLT + case EL3HLT: return DRFLAC_ERROR; + #endif + #ifdef EL3RST + case EL3RST: return DRFLAC_ERROR; + #endif + #ifdef ELNRNG + case ELNRNG: return DRFLAC_OUT_OF_RANGE; + #endif + #ifdef EUNATCH + case EUNATCH: return DRFLAC_ERROR; + #endif + #ifdef ENOCSI + case ENOCSI: return DRFLAC_ERROR; + #endif + #ifdef EL2HLT + case EL2HLT: return DRFLAC_ERROR; + #endif + #ifdef EBADE + case EBADE: return DRFLAC_ERROR; + #endif + #ifdef EBADR + case EBADR: return DRFLAC_ERROR; + #endif + #ifdef EXFULL + case EXFULL: return DRFLAC_ERROR; + #endif + #ifdef ENOANO + case ENOANO: return DRFLAC_ERROR; + #endif + #ifdef EBADRQC + case EBADRQC: return DRFLAC_ERROR; + #endif + #ifdef EBADSLT + case EBADSLT: return DRFLAC_ERROR; + #endif + #ifdef EBFONT + case EBFONT: return DRFLAC_INVALID_FILE; + #endif + #ifdef ENOSTR + case ENOSTR: return DRFLAC_ERROR; + #endif + #ifdef ENODATA + case ENODATA: return DRFLAC_NO_DATA_AVAILABLE; + #endif + #ifdef ETIME + case ETIME: return DRFLAC_TIMEOUT; + #endif + #ifdef ENOSR + case ENOSR: return DRFLAC_NO_DATA_AVAILABLE; + #endif + #ifdef ENONET + case ENONET: return DRFLAC_NO_NETWORK; + #endif + #ifdef ENOPKG + case ENOPKG: return DRFLAC_ERROR; + #endif + #ifdef EREMOTE + case EREMOTE: return DRFLAC_ERROR; + #endif + #ifdef ENOLINK + case ENOLINK: return DRFLAC_ERROR; + #endif + #ifdef EADV + case EADV: return DRFLAC_ERROR; + #endif + #ifdef ESRMNT + case ESRMNT: return DRFLAC_ERROR; + #endif + #ifdef ECOMM + case ECOMM: return DRFLAC_ERROR; + #endif + #ifdef EPROTO + case EPROTO: return DRFLAC_ERROR; + #endif + #ifdef EMULTIHOP + case EMULTIHOP: return DRFLAC_ERROR; + #endif + #ifdef EDOTDOT + case EDOTDOT: return DRFLAC_ERROR; + #endif + #ifdef EBADMSG + case EBADMSG: return DRFLAC_BAD_MESSAGE; + #endif + #ifdef EOVERFLOW + case EOVERFLOW: return DRFLAC_TOO_BIG; + #endif + #ifdef ENOTUNIQ + case ENOTUNIQ: return DRFLAC_NOT_UNIQUE; + #endif + #ifdef EBADFD + case EBADFD: return DRFLAC_ERROR; + #endif + #ifdef EREMCHG + case EREMCHG: return DRFLAC_ERROR; + #endif + #ifdef ELIBACC + case ELIBACC: return DRFLAC_ACCESS_DENIED; + #endif + #ifdef ELIBBAD + case ELIBBAD: return DRFLAC_INVALID_FILE; + #endif + #ifdef ELIBSCN + case ELIBSCN: return DRFLAC_INVALID_FILE; + #endif + #ifdef ELIBMAX + case ELIBMAX: return DRFLAC_ERROR; + #endif + #ifdef ELIBEXEC + case ELIBEXEC: return DRFLAC_ERROR; + #endif + #ifdef EILSEQ + case EILSEQ: return DRFLAC_INVALID_DATA; + #endif + #ifdef ERESTART + case ERESTART: return DRFLAC_ERROR; + #endif + #ifdef ESTRPIPE + case ESTRPIPE: return DRFLAC_ERROR; + #endif + #ifdef EUSERS + case EUSERS: return DRFLAC_ERROR; + #endif + #ifdef ENOTSOCK + case ENOTSOCK: return DRFLAC_NOT_SOCKET; + #endif + #ifdef EDESTADDRREQ + case EDESTADDRREQ: return DRFLAC_NO_ADDRESS; + #endif + #ifdef EMSGSIZE + case EMSGSIZE: return DRFLAC_TOO_BIG; + #endif + #ifdef EPROTOTYPE + case EPROTOTYPE: return DRFLAC_BAD_PROTOCOL; + #endif + #ifdef ENOPROTOOPT + case ENOPROTOOPT: return DRFLAC_PROTOCOL_UNAVAILABLE; + #endif + #ifdef EPROTONOSUPPORT + case EPROTONOSUPPORT: return DRFLAC_PROTOCOL_NOT_SUPPORTED; + #endif + #ifdef ESOCKTNOSUPPORT + case ESOCKTNOSUPPORT: return DRFLAC_SOCKET_NOT_SUPPORTED; + #endif + #ifdef EOPNOTSUPP + case EOPNOTSUPP: return DRFLAC_INVALID_OPERATION; + #endif + #ifdef EPFNOSUPPORT + case EPFNOSUPPORT: return DRFLAC_PROTOCOL_FAMILY_NOT_SUPPORTED; + #endif + #ifdef EAFNOSUPPORT + case EAFNOSUPPORT: return DRFLAC_ADDRESS_FAMILY_NOT_SUPPORTED; + #endif + #ifdef EADDRINUSE + case EADDRINUSE: return DRFLAC_ALREADY_IN_USE; + #endif + #ifdef EADDRNOTAVAIL + case EADDRNOTAVAIL: return DRFLAC_ERROR; + #endif + #ifdef ENETDOWN + case ENETDOWN: return DRFLAC_NO_NETWORK; + #endif + #ifdef ENETUNREACH + case ENETUNREACH: return DRFLAC_NO_NETWORK; + #endif + #ifdef ENETRESET + case ENETRESET: return DRFLAC_NO_NETWORK; + #endif + #ifdef ECONNABORTED + case ECONNABORTED: return DRFLAC_NO_NETWORK; + #endif + #ifdef ECONNRESET + case ECONNRESET: return DRFLAC_CONNECTION_RESET; + #endif + #ifdef ENOBUFS + case ENOBUFS: return DRFLAC_NO_SPACE; + #endif + #ifdef EISCONN + case EISCONN: return DRFLAC_ALREADY_CONNECTED; + #endif + #ifdef ENOTCONN + case ENOTCONN: return DRFLAC_NOT_CONNECTED; + #endif + #ifdef ESHUTDOWN + case ESHUTDOWN: return DRFLAC_ERROR; + #endif + #ifdef ETOOMANYREFS + case ETOOMANYREFS: return DRFLAC_ERROR; + #endif + #ifdef ETIMEDOUT + case ETIMEDOUT: return DRFLAC_TIMEOUT; + #endif + #ifdef ECONNREFUSED + case ECONNREFUSED: return DRFLAC_CONNECTION_REFUSED; + #endif + #ifdef EHOSTDOWN + case EHOSTDOWN: return DRFLAC_NO_HOST; + #endif + #ifdef EHOSTUNREACH + case EHOSTUNREACH: return DRFLAC_NO_HOST; + #endif + #ifdef EALREADY + case EALREADY: return DRFLAC_IN_PROGRESS; + #endif + #ifdef EINPROGRESS + case EINPROGRESS: return DRFLAC_IN_PROGRESS; + #endif + #ifdef ESTALE + case ESTALE: return DRFLAC_INVALID_FILE; + #endif + #ifdef EUCLEAN + case EUCLEAN: return DRFLAC_ERROR; + #endif + #ifdef ENOTNAM + case ENOTNAM: return DRFLAC_ERROR; + #endif + #ifdef ENAVAIL + case ENAVAIL: return DRFLAC_ERROR; + #endif + #ifdef EISNAM + case EISNAM: return DRFLAC_ERROR; + #endif + #ifdef EREMOTEIO + case EREMOTEIO: return DRFLAC_IO_ERROR; + #endif + #ifdef EDQUOT + case EDQUOT: return DRFLAC_NO_SPACE; + #endif + #ifdef ENOMEDIUM + case ENOMEDIUM: return DRFLAC_DOES_NOT_EXIST; + #endif + #ifdef EMEDIUMTYPE + case EMEDIUMTYPE: return DRFLAC_ERROR; + #endif + #ifdef ECANCELED + case ECANCELED: return DRFLAC_CANCELLED; + #endif + #ifdef ENOKEY + case ENOKEY: return DRFLAC_ERROR; + #endif + #ifdef EKEYEXPIRED + case EKEYEXPIRED: return DRFLAC_ERROR; + #endif + #ifdef EKEYREVOKED + case EKEYREVOKED: return DRFLAC_ERROR; + #endif + #ifdef EKEYREJECTED + case EKEYREJECTED: return DRFLAC_ERROR; + #endif + #ifdef EOWNERDEAD + case EOWNERDEAD: return DRFLAC_ERROR; + #endif + #ifdef ENOTRECOVERABLE + case ENOTRECOVERABLE: return DRFLAC_ERROR; + #endif + #ifdef ERFKILL + case ERFKILL: return DRFLAC_ERROR; + #endif + #ifdef EHWPOISON + case EHWPOISON: return DRFLAC_ERROR; + #endif + default: return DRFLAC_ERROR; + } +} +static drflac_result drflac_fopen(FILE** ppFile, const char* pFilePath, const char* pOpenMode) +{ +#if defined(_MSC_VER) && _MSC_VER >= 1400 + errno_t err; +#endif + if (ppFile != NULL) { + *ppFile = NULL; + } + if (pFilePath == NULL || pOpenMode == NULL || ppFile == NULL) { + return DRFLAC_INVALID_ARGS; + } +#if defined(_MSC_VER) && _MSC_VER >= 1400 + err = fopen_s(ppFile, pFilePath, pOpenMode); + if (err != 0) { + return drflac_result_from_errno(err); + } +#else +#if defined(_WIN32) || defined(__APPLE__) + *ppFile = fopen(pFilePath, pOpenMode); +#else + #if defined(_FILE_OFFSET_BITS) && _FILE_OFFSET_BITS == 64 && defined(_LARGEFILE64_SOURCE) + *ppFile = fopen64(pFilePath, pOpenMode); + #else + *ppFile = fopen(pFilePath, pOpenMode); + #endif +#endif + if (*ppFile == NULL) { + drflac_result result = drflac_result_from_errno(errno); + if (result == DRFLAC_SUCCESS) { + result = DRFLAC_ERROR; + } + return result; + } +#endif + return DRFLAC_SUCCESS; +} +#if defined(_WIN32) + #if defined(_MSC_VER) || defined(__MINGW64__) || (!defined(__STRICT_ANSI__) && !defined(_NO_EXT_KEYS)) + #define DRFLAC_HAS_WFOPEN + #endif +#endif +#ifndef DR_FLAC_NO_WCHAR +static drflac_result drflac_wfopen(FILE** ppFile, const wchar_t* pFilePath, const wchar_t* pOpenMode, const drflac_allocation_callbacks* pAllocationCallbacks) +{ + if (ppFile != NULL) { + *ppFile = NULL; + } + if (pFilePath == NULL || pOpenMode == NULL || ppFile == NULL) { + return DRFLAC_INVALID_ARGS; + } +#if defined(DRFLAC_HAS_WFOPEN) + { + #if defined(_MSC_VER) && _MSC_VER >= 1400 + errno_t err = _wfopen_s(ppFile, pFilePath, pOpenMode); + if (err != 0) { + return drflac_result_from_errno(err); + } + #else + *ppFile = _wfopen(pFilePath, pOpenMode); + if (*ppFile == NULL) { + return drflac_result_from_errno(errno); + } + #endif + (void)pAllocationCallbacks; + } +#else + #if defined(__DJGPP__) + { + } + #else + { + mbstate_t mbs; + size_t lenMB; + const wchar_t* pFilePathTemp = pFilePath; + char* pFilePathMB = NULL; + char pOpenModeMB[32] = {0}; + DRFLAC_ZERO_OBJECT(&mbs); + lenMB = wcsrtombs(NULL, &pFilePathTemp, 0, &mbs); + if (lenMB == (size_t)-1) { + return drflac_result_from_errno(errno); + } + pFilePathMB = (char*)drflac__malloc_from_callbacks(lenMB + 1, pAllocationCallbacks); + if (pFilePathMB == NULL) { + return DRFLAC_OUT_OF_MEMORY; + } + pFilePathTemp = pFilePath; + DRFLAC_ZERO_OBJECT(&mbs); + wcsrtombs(pFilePathMB, &pFilePathTemp, lenMB + 1, &mbs); + { + size_t i = 0; + for (;;) { + if (pOpenMode[i] == 0) { + pOpenModeMB[i] = '\0'; + break; + } + pOpenModeMB[i] = (char)pOpenMode[i]; + i += 1; + } + } + *ppFile = fopen(pFilePathMB, pOpenModeMB); + drflac__free_from_callbacks(pFilePathMB, pAllocationCallbacks); + } + #endif + if (*ppFile == NULL) { + return DRFLAC_ERROR; + } +#endif + return DRFLAC_SUCCESS; +} +#endif +static size_t drflac__on_read_stdio(void* pUserData, void* bufferOut, size_t bytesToRead) { return fread(bufferOut, 1, bytesToRead, (FILE*)pUserData); } -static ma_bool32 ma_dr_flac__on_seek_stdio(void* pUserData, int offset, ma_dr_flac_seek_origin origin) +static drflac_bool32 drflac__on_seek_stdio(void* pUserData, int offset, drflac_seek_origin origin) { - MA_DR_FLAC_ASSERT(offset >= 0); - return fseek((FILE*)pUserData, offset, (origin == ma_dr_flac_seek_origin_current) ? SEEK_CUR : SEEK_SET) == 0; + DRFLAC_ASSERT(offset >= 0); + return fseek((FILE*)pUserData, offset, (origin == drflac_seek_origin_current) ? SEEK_CUR : SEEK_SET) == 0; } -MA_API ma_dr_flac* ma_dr_flac_open_file(const char* pFileName, const ma_allocation_callbacks* pAllocationCallbacks) +DRFLAC_API drflac* drflac_open_file(const char* pFileName, const drflac_allocation_callbacks* pAllocationCallbacks) { - ma_dr_flac* pFlac; + drflac* pFlac; FILE* pFile; - if (ma_fopen(&pFile, pFileName, "rb") != MA_SUCCESS) { + if (drflac_fopen(&pFile, pFileName, "rb") != DRFLAC_SUCCESS) { return NULL; } - pFlac = ma_dr_flac_open(ma_dr_flac__on_read_stdio, ma_dr_flac__on_seek_stdio, (void*)pFile, pAllocationCallbacks); + pFlac = drflac_open(drflac__on_read_stdio, drflac__on_seek_stdio, (void*)pFile, pAllocationCallbacks); if (pFlac == NULL) { fclose(pFile); return NULL; } return pFlac; } -#ifndef MA_DR_FLAC_NO_WCHAR -MA_API ma_dr_flac* ma_dr_flac_open_file_w(const wchar_t* pFileName, const ma_allocation_callbacks* pAllocationCallbacks) +#ifndef DR_FLAC_NO_WCHAR +DRFLAC_API drflac* drflac_open_file_w(const wchar_t* pFileName, const drflac_allocation_callbacks* pAllocationCallbacks) { - ma_dr_flac* pFlac; + drflac* pFlac; FILE* pFile; - if (ma_wfopen(&pFile, pFileName, L"rb", pAllocationCallbacks) != MA_SUCCESS) { + if (drflac_wfopen(&pFile, pFileName, L"rb", pAllocationCallbacks) != DRFLAC_SUCCESS) { return NULL; } - pFlac = ma_dr_flac_open(ma_dr_flac__on_read_stdio, ma_dr_flac__on_seek_stdio, (void*)pFile, pAllocationCallbacks); + pFlac = drflac_open(drflac__on_read_stdio, drflac__on_seek_stdio, (void*)pFile, pAllocationCallbacks); if (pFlac == NULL) { fclose(pFile); return NULL; @@ -87983,29 +87369,29 @@ MA_API ma_dr_flac* ma_dr_flac_open_file_w(const wchar_t* pFileName, const ma_all return pFlac; } #endif -MA_API ma_dr_flac* ma_dr_flac_open_file_with_metadata(const char* pFileName, ma_dr_flac_meta_proc onMeta, void* pUserData, const ma_allocation_callbacks* pAllocationCallbacks) +DRFLAC_API drflac* drflac_open_file_with_metadata(const char* pFileName, drflac_meta_proc onMeta, void* pUserData, const drflac_allocation_callbacks* pAllocationCallbacks) { - ma_dr_flac* pFlac; + drflac* pFlac; FILE* pFile; - if (ma_fopen(&pFile, pFileName, "rb") != MA_SUCCESS) { + if (drflac_fopen(&pFile, pFileName, "rb") != DRFLAC_SUCCESS) { return NULL; } - pFlac = ma_dr_flac_open_with_metadata_private(ma_dr_flac__on_read_stdio, ma_dr_flac__on_seek_stdio, onMeta, ma_dr_flac_container_unknown, (void*)pFile, pUserData, pAllocationCallbacks); + pFlac = drflac_open_with_metadata_private(drflac__on_read_stdio, drflac__on_seek_stdio, onMeta, drflac_container_unknown, (void*)pFile, pUserData, pAllocationCallbacks); if (pFlac == NULL) { fclose(pFile); return pFlac; } return pFlac; } -#ifndef MA_DR_FLAC_NO_WCHAR -MA_API ma_dr_flac* ma_dr_flac_open_file_with_metadata_w(const wchar_t* pFileName, ma_dr_flac_meta_proc onMeta, void* pUserData, const ma_allocation_callbacks* pAllocationCallbacks) +#ifndef DR_FLAC_NO_WCHAR +DRFLAC_API drflac* drflac_open_file_with_metadata_w(const wchar_t* pFileName, drflac_meta_proc onMeta, void* pUserData, const drflac_allocation_callbacks* pAllocationCallbacks) { - ma_dr_flac* pFlac; + drflac* pFlac; FILE* pFile; - if (ma_wfopen(&pFile, pFileName, L"rb", pAllocationCallbacks) != MA_SUCCESS) { + if (drflac_wfopen(&pFile, pFileName, L"rb", pAllocationCallbacks) != DRFLAC_SUCCESS) { return NULL; } - pFlac = ma_dr_flac_open_with_metadata_private(ma_dr_flac__on_read_stdio, ma_dr_flac__on_seek_stdio, onMeta, ma_dr_flac_container_unknown, (void*)pFile, pUserData, pAllocationCallbacks); + pFlac = drflac_open_with_metadata_private(drflac__on_read_stdio, drflac__on_seek_stdio, onMeta, drflac_container_unknown, (void*)pFile, pUserData, pAllocationCallbacks); if (pFlac == NULL) { fclose(pFile); return pFlac; @@ -88014,61 +87400,61 @@ MA_API ma_dr_flac* ma_dr_flac_open_file_with_metadata_w(const wchar_t* pFileName } #endif #endif -static size_t ma_dr_flac__on_read_memory(void* pUserData, void* bufferOut, size_t bytesToRead) +static size_t drflac__on_read_memory(void* pUserData, void* bufferOut, size_t bytesToRead) { - ma_dr_flac__memory_stream* memoryStream = (ma_dr_flac__memory_stream*)pUserData; + drflac__memory_stream* memoryStream = (drflac__memory_stream*)pUserData; size_t bytesRemaining; - MA_DR_FLAC_ASSERT(memoryStream != NULL); - MA_DR_FLAC_ASSERT(memoryStream->dataSize >= memoryStream->currentReadPos); + DRFLAC_ASSERT(memoryStream != NULL); + DRFLAC_ASSERT(memoryStream->dataSize >= memoryStream->currentReadPos); bytesRemaining = memoryStream->dataSize - memoryStream->currentReadPos; if (bytesToRead > bytesRemaining) { bytesToRead = bytesRemaining; } if (bytesToRead > 0) { - MA_DR_FLAC_COPY_MEMORY(bufferOut, memoryStream->data + memoryStream->currentReadPos, bytesToRead); + DRFLAC_COPY_MEMORY(bufferOut, memoryStream->data + memoryStream->currentReadPos, bytesToRead); memoryStream->currentReadPos += bytesToRead; } return bytesToRead; } -static ma_bool32 ma_dr_flac__on_seek_memory(void* pUserData, int offset, ma_dr_flac_seek_origin origin) +static drflac_bool32 drflac__on_seek_memory(void* pUserData, int offset, drflac_seek_origin origin) { - ma_dr_flac__memory_stream* memoryStream = (ma_dr_flac__memory_stream*)pUserData; - MA_DR_FLAC_ASSERT(memoryStream != NULL); - MA_DR_FLAC_ASSERT(offset >= 0); - if (offset > (ma_int64)memoryStream->dataSize) { - return MA_FALSE; + drflac__memory_stream* memoryStream = (drflac__memory_stream*)pUserData; + DRFLAC_ASSERT(memoryStream != NULL); + DRFLAC_ASSERT(offset >= 0); + if (offset > (drflac_int64)memoryStream->dataSize) { + return DRFLAC_FALSE; } - if (origin == ma_dr_flac_seek_origin_current) { + if (origin == drflac_seek_origin_current) { if (memoryStream->currentReadPos + offset <= memoryStream->dataSize) { memoryStream->currentReadPos += offset; } else { - return MA_FALSE; + return DRFLAC_FALSE; } } else { - if ((ma_uint32)offset <= memoryStream->dataSize) { + if ((drflac_uint32)offset <= memoryStream->dataSize) { memoryStream->currentReadPos = offset; } else { - return MA_FALSE; + return DRFLAC_FALSE; } } - return MA_TRUE; + return DRFLAC_TRUE; } -MA_API ma_dr_flac* ma_dr_flac_open_memory(const void* pData, size_t dataSize, const ma_allocation_callbacks* pAllocationCallbacks) +DRFLAC_API drflac* drflac_open_memory(const void* pData, size_t dataSize, const drflac_allocation_callbacks* pAllocationCallbacks) { - ma_dr_flac__memory_stream memoryStream; - ma_dr_flac* pFlac; - memoryStream.data = (const ma_uint8*)pData; + drflac__memory_stream memoryStream; + drflac* pFlac; + memoryStream.data = (const drflac_uint8*)pData; memoryStream.dataSize = dataSize; memoryStream.currentReadPos = 0; - pFlac = ma_dr_flac_open(ma_dr_flac__on_read_memory, ma_dr_flac__on_seek_memory, &memoryStream, pAllocationCallbacks); + pFlac = drflac_open(drflac__on_read_memory, drflac__on_seek_memory, &memoryStream, pAllocationCallbacks); if (pFlac == NULL) { return NULL; } pFlac->memoryStream = memoryStream; -#ifndef MA_DR_FLAC_NO_OGG - if (pFlac->container == ma_dr_flac_container_ogg) +#ifndef DR_FLAC_NO_OGG + if (pFlac->container == drflac_container_ogg) { - ma_dr_flac_oggbs* oggbs = (ma_dr_flac_oggbs*)pFlac->_oggbs; + drflac_oggbs* oggbs = (drflac_oggbs*)pFlac->_oggbs; oggbs->pUserData = &pFlac->memoryStream; } else @@ -88078,22 +87464,22 @@ MA_API ma_dr_flac* ma_dr_flac_open_memory(const void* pData, size_t dataSize, co } return pFlac; } -MA_API ma_dr_flac* ma_dr_flac_open_memory_with_metadata(const void* pData, size_t dataSize, ma_dr_flac_meta_proc onMeta, void* pUserData, const ma_allocation_callbacks* pAllocationCallbacks) +DRFLAC_API drflac* drflac_open_memory_with_metadata(const void* pData, size_t dataSize, drflac_meta_proc onMeta, void* pUserData, const drflac_allocation_callbacks* pAllocationCallbacks) { - ma_dr_flac__memory_stream memoryStream; - ma_dr_flac* pFlac; - memoryStream.data = (const ma_uint8*)pData; + drflac__memory_stream memoryStream; + drflac* pFlac; + memoryStream.data = (const drflac_uint8*)pData; memoryStream.dataSize = dataSize; memoryStream.currentReadPos = 0; - pFlac = ma_dr_flac_open_with_metadata_private(ma_dr_flac__on_read_memory, ma_dr_flac__on_seek_memory, onMeta, ma_dr_flac_container_unknown, &memoryStream, pUserData, pAllocationCallbacks); + pFlac = drflac_open_with_metadata_private(drflac__on_read_memory, drflac__on_seek_memory, onMeta, drflac_container_unknown, &memoryStream, pUserData, pAllocationCallbacks); if (pFlac == NULL) { return NULL; } pFlac->memoryStream = memoryStream; -#ifndef MA_DR_FLAC_NO_OGG - if (pFlac->container == ma_dr_flac_container_ogg) +#ifndef DR_FLAC_NO_OGG + if (pFlac->container == drflac_container_ogg) { - ma_dr_flac_oggbs* oggbs = (ma_dr_flac_oggbs*)pFlac->_oggbs; + drflac_oggbs* oggbs = (drflac_oggbs*)pFlac->_oggbs; oggbs->pUserData = &pFlac->memoryStream; } else @@ -88103,104 +87489,104 @@ MA_API ma_dr_flac* ma_dr_flac_open_memory_with_metadata(const void* pData, size_ } return pFlac; } -MA_API ma_dr_flac* ma_dr_flac_open(ma_dr_flac_read_proc onRead, ma_dr_flac_seek_proc onSeek, void* pUserData, const ma_allocation_callbacks* pAllocationCallbacks) +DRFLAC_API drflac* drflac_open(drflac_read_proc onRead, drflac_seek_proc onSeek, void* pUserData, const drflac_allocation_callbacks* pAllocationCallbacks) { - return ma_dr_flac_open_with_metadata_private(onRead, onSeek, NULL, ma_dr_flac_container_unknown, pUserData, pUserData, pAllocationCallbacks); + return drflac_open_with_metadata_private(onRead, onSeek, NULL, drflac_container_unknown, pUserData, pUserData, pAllocationCallbacks); } -MA_API ma_dr_flac* ma_dr_flac_open_relaxed(ma_dr_flac_read_proc onRead, ma_dr_flac_seek_proc onSeek, ma_dr_flac_container container, void* pUserData, const ma_allocation_callbacks* pAllocationCallbacks) +DRFLAC_API drflac* drflac_open_relaxed(drflac_read_proc onRead, drflac_seek_proc onSeek, drflac_container container, void* pUserData, const drflac_allocation_callbacks* pAllocationCallbacks) { - return ma_dr_flac_open_with_metadata_private(onRead, onSeek, NULL, container, pUserData, pUserData, pAllocationCallbacks); + return drflac_open_with_metadata_private(onRead, onSeek, NULL, container, pUserData, pUserData, pAllocationCallbacks); } -MA_API ma_dr_flac* ma_dr_flac_open_with_metadata(ma_dr_flac_read_proc onRead, ma_dr_flac_seek_proc onSeek, ma_dr_flac_meta_proc onMeta, void* pUserData, const ma_allocation_callbacks* pAllocationCallbacks) +DRFLAC_API drflac* drflac_open_with_metadata(drflac_read_proc onRead, drflac_seek_proc onSeek, drflac_meta_proc onMeta, void* pUserData, const drflac_allocation_callbacks* pAllocationCallbacks) { - return ma_dr_flac_open_with_metadata_private(onRead, onSeek, onMeta, ma_dr_flac_container_unknown, pUserData, pUserData, pAllocationCallbacks); + return drflac_open_with_metadata_private(onRead, onSeek, onMeta, drflac_container_unknown, pUserData, pUserData, pAllocationCallbacks); } -MA_API ma_dr_flac* ma_dr_flac_open_with_metadata_relaxed(ma_dr_flac_read_proc onRead, ma_dr_flac_seek_proc onSeek, ma_dr_flac_meta_proc onMeta, ma_dr_flac_container container, void* pUserData, const ma_allocation_callbacks* pAllocationCallbacks) +DRFLAC_API drflac* drflac_open_with_metadata_relaxed(drflac_read_proc onRead, drflac_seek_proc onSeek, drflac_meta_proc onMeta, drflac_container container, void* pUserData, const drflac_allocation_callbacks* pAllocationCallbacks) { - return ma_dr_flac_open_with_metadata_private(onRead, onSeek, onMeta, container, pUserData, pUserData, pAllocationCallbacks); + return drflac_open_with_metadata_private(onRead, onSeek, onMeta, container, pUserData, pUserData, pAllocationCallbacks); } -MA_API void ma_dr_flac_close(ma_dr_flac* pFlac) +DRFLAC_API void drflac_close(drflac* pFlac) { if (pFlac == NULL) { return; } -#ifndef MA_DR_FLAC_NO_STDIO - if (pFlac->bs.onRead == ma_dr_flac__on_read_stdio) { +#ifndef DR_FLAC_NO_STDIO + if (pFlac->bs.onRead == drflac__on_read_stdio) { fclose((FILE*)pFlac->bs.pUserData); } -#ifndef MA_DR_FLAC_NO_OGG - if (pFlac->container == ma_dr_flac_container_ogg) { - ma_dr_flac_oggbs* oggbs = (ma_dr_flac_oggbs*)pFlac->_oggbs; - MA_DR_FLAC_ASSERT(pFlac->bs.onRead == ma_dr_flac__on_read_ogg); - if (oggbs->onRead == ma_dr_flac__on_read_stdio) { +#ifndef DR_FLAC_NO_OGG + if (pFlac->container == drflac_container_ogg) { + drflac_oggbs* oggbs = (drflac_oggbs*)pFlac->_oggbs; + DRFLAC_ASSERT(pFlac->bs.onRead == drflac__on_read_ogg); + if (oggbs->onRead == drflac__on_read_stdio) { fclose((FILE*)oggbs->pUserData); } } #endif #endif - ma_dr_flac__free_from_callbacks(pFlac, &pFlac->allocationCallbacks); + drflac__free_from_callbacks(pFlac, &pFlac->allocationCallbacks); } #if 0 -static MA_INLINE void ma_dr_flac_read_pcm_frames_s32__decode_left_side__reference(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, ma_int32* pOutputSamples) +static DRFLAC_INLINE void drflac_read_pcm_frames_s32__decode_left_side__reference(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int32* pOutputSamples) { - ma_uint64 i; + drflac_uint64 i; for (i = 0; i < frameCount; ++i) { - ma_uint32 left = (ma_uint32)pInputSamples0[i] << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample); - ma_uint32 side = (ma_uint32)pInputSamples1[i] << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample); - ma_uint32 right = left - side; - pOutputSamples[i*2+0] = (ma_int32)left; - pOutputSamples[i*2+1] = (ma_int32)right; + drflac_uint32 left = (drflac_uint32)pInputSamples0[i] << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample); + drflac_uint32 side = (drflac_uint32)pInputSamples1[i] << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample); + drflac_uint32 right = left - side; + pOutputSamples[i*2+0] = (drflac_int32)left; + pOutputSamples[i*2+1] = (drflac_int32)right; } } #endif -static MA_INLINE void ma_dr_flac_read_pcm_frames_s32__decode_left_side__scalar(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, ma_int32* pOutputSamples) +static DRFLAC_INLINE void drflac_read_pcm_frames_s32__decode_left_side__scalar(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int32* pOutputSamples) { - ma_uint64 i; - ma_uint64 frameCount4 = frameCount >> 2; - const ma_uint32* pInputSamples0U32 = (const ma_uint32*)pInputSamples0; - const ma_uint32* pInputSamples1U32 = (const ma_uint32*)pInputSamples1; - ma_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; - ma_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; + drflac_uint64 i; + drflac_uint64 frameCount4 = frameCount >> 2; + const drflac_uint32* pInputSamples0U32 = (const drflac_uint32*)pInputSamples0; + const drflac_uint32* pInputSamples1U32 = (const drflac_uint32*)pInputSamples1; + drflac_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; + drflac_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; for (i = 0; i < frameCount4; ++i) { - ma_uint32 left0 = pInputSamples0U32[i*4+0] << shift0; - ma_uint32 left1 = pInputSamples0U32[i*4+1] << shift0; - ma_uint32 left2 = pInputSamples0U32[i*4+2] << shift0; - ma_uint32 left3 = pInputSamples0U32[i*4+3] << shift0; - ma_uint32 side0 = pInputSamples1U32[i*4+0] << shift1; - ma_uint32 side1 = pInputSamples1U32[i*4+1] << shift1; - ma_uint32 side2 = pInputSamples1U32[i*4+2] << shift1; - ma_uint32 side3 = pInputSamples1U32[i*4+3] << shift1; - ma_uint32 right0 = left0 - side0; - ma_uint32 right1 = left1 - side1; - ma_uint32 right2 = left2 - side2; - ma_uint32 right3 = left3 - side3; - pOutputSamples[i*8+0] = (ma_int32)left0; - pOutputSamples[i*8+1] = (ma_int32)right0; - pOutputSamples[i*8+2] = (ma_int32)left1; - pOutputSamples[i*8+3] = (ma_int32)right1; - pOutputSamples[i*8+4] = (ma_int32)left2; - pOutputSamples[i*8+5] = (ma_int32)right2; - pOutputSamples[i*8+6] = (ma_int32)left3; - pOutputSamples[i*8+7] = (ma_int32)right3; + drflac_uint32 left0 = pInputSamples0U32[i*4+0] << shift0; + drflac_uint32 left1 = pInputSamples0U32[i*4+1] << shift0; + drflac_uint32 left2 = pInputSamples0U32[i*4+2] << shift0; + drflac_uint32 left3 = pInputSamples0U32[i*4+3] << shift0; + drflac_uint32 side0 = pInputSamples1U32[i*4+0] << shift1; + drflac_uint32 side1 = pInputSamples1U32[i*4+1] << shift1; + drflac_uint32 side2 = pInputSamples1U32[i*4+2] << shift1; + drflac_uint32 side3 = pInputSamples1U32[i*4+3] << shift1; + drflac_uint32 right0 = left0 - side0; + drflac_uint32 right1 = left1 - side1; + drflac_uint32 right2 = left2 - side2; + drflac_uint32 right3 = left3 - side3; + pOutputSamples[i*8+0] = (drflac_int32)left0; + pOutputSamples[i*8+1] = (drflac_int32)right0; + pOutputSamples[i*8+2] = (drflac_int32)left1; + pOutputSamples[i*8+3] = (drflac_int32)right1; + pOutputSamples[i*8+4] = (drflac_int32)left2; + pOutputSamples[i*8+5] = (drflac_int32)right2; + pOutputSamples[i*8+6] = (drflac_int32)left3; + pOutputSamples[i*8+7] = (drflac_int32)right3; } for (i = (frameCount4 << 2); i < frameCount; ++i) { - ma_uint32 left = pInputSamples0U32[i] << shift0; - ma_uint32 side = pInputSamples1U32[i] << shift1; - ma_uint32 right = left - side; - pOutputSamples[i*2+0] = (ma_int32)left; - pOutputSamples[i*2+1] = (ma_int32)right; - } -} -#if defined(MA_DR_FLAC_SUPPORT_SSE2) -static MA_INLINE void ma_dr_flac_read_pcm_frames_s32__decode_left_side__sse2(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, ma_int32* pOutputSamples) -{ - ma_uint64 i; - ma_uint64 frameCount4 = frameCount >> 2; - const ma_uint32* pInputSamples0U32 = (const ma_uint32*)pInputSamples0; - const ma_uint32* pInputSamples1U32 = (const ma_uint32*)pInputSamples1; - ma_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; - ma_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; - MA_DR_FLAC_ASSERT(pFlac->bitsPerSample <= 24); + drflac_uint32 left = pInputSamples0U32[i] << shift0; + drflac_uint32 side = pInputSamples1U32[i] << shift1; + drflac_uint32 right = left - side; + pOutputSamples[i*2+0] = (drflac_int32)left; + pOutputSamples[i*2+1] = (drflac_int32)right; + } +} +#if defined(DRFLAC_SUPPORT_SSE2) +static DRFLAC_INLINE void drflac_read_pcm_frames_s32__decode_left_side__sse2(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int32* pOutputSamples) +{ + drflac_uint64 i; + drflac_uint64 frameCount4 = frameCount >> 2; + const drflac_uint32* pInputSamples0U32 = (const drflac_uint32*)pInputSamples0; + const drflac_uint32* pInputSamples1U32 = (const drflac_uint32*)pInputSamples1; + drflac_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; + drflac_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; + DRFLAC_ASSERT(pFlac->bitsPerSample <= 24); for (i = 0; i < frameCount4; ++i) { __m128i left = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples0 + i), shift0); __m128i side = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples1 + i), shift1); @@ -88209,26 +87595,26 @@ static MA_INLINE void ma_dr_flac_read_pcm_frames_s32__decode_left_side__sse2(ma_ _mm_storeu_si128((__m128i*)(pOutputSamples + i*8 + 4), _mm_unpackhi_epi32(left, right)); } for (i = (frameCount4 << 2); i < frameCount; ++i) { - ma_uint32 left = pInputSamples0U32[i] << shift0; - ma_uint32 side = pInputSamples1U32[i] << shift1; - ma_uint32 right = left - side; - pOutputSamples[i*2+0] = (ma_int32)left; - pOutputSamples[i*2+1] = (ma_int32)right; + drflac_uint32 left = pInputSamples0U32[i] << shift0; + drflac_uint32 side = pInputSamples1U32[i] << shift1; + drflac_uint32 right = left - side; + pOutputSamples[i*2+0] = (drflac_int32)left; + pOutputSamples[i*2+1] = (drflac_int32)right; } } #endif -#if defined(MA_DR_FLAC_SUPPORT_NEON) -static MA_INLINE void ma_dr_flac_read_pcm_frames_s32__decode_left_side__neon(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, ma_int32* pOutputSamples) +#if defined(DRFLAC_SUPPORT_NEON) +static DRFLAC_INLINE void drflac_read_pcm_frames_s32__decode_left_side__neon(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int32* pOutputSamples) { - ma_uint64 i; - ma_uint64 frameCount4 = frameCount >> 2; - const ma_uint32* pInputSamples0U32 = (const ma_uint32*)pInputSamples0; - const ma_uint32* pInputSamples1U32 = (const ma_uint32*)pInputSamples1; - ma_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; - ma_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; + drflac_uint64 i; + drflac_uint64 frameCount4 = frameCount >> 2; + const drflac_uint32* pInputSamples0U32 = (const drflac_uint32*)pInputSamples0; + const drflac_uint32* pInputSamples1U32 = (const drflac_uint32*)pInputSamples1; + drflac_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; + drflac_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; int32x4_t shift0_4; int32x4_t shift1_4; - MA_DR_FLAC_ASSERT(pFlac->bitsPerSample <= 24); + DRFLAC_ASSERT(pFlac->bitsPerSample <= 24); shift0_4 = vdupq_n_s32(shift0); shift1_4 = vdupq_n_s32(shift1); for (i = 0; i < frameCount4; ++i) { @@ -88238,97 +87624,97 @@ static MA_INLINE void ma_dr_flac_read_pcm_frames_s32__decode_left_side__neon(ma_ left = vshlq_u32(vld1q_u32(pInputSamples0U32 + i*4), shift0_4); side = vshlq_u32(vld1q_u32(pInputSamples1U32 + i*4), shift1_4); right = vsubq_u32(left, side); - ma_dr_flac__vst2q_u32((ma_uint32*)pOutputSamples + i*8, vzipq_u32(left, right)); + drflac__vst2q_u32((drflac_uint32*)pOutputSamples + i*8, vzipq_u32(left, right)); } for (i = (frameCount4 << 2); i < frameCount; ++i) { - ma_uint32 left = pInputSamples0U32[i] << shift0; - ma_uint32 side = pInputSamples1U32[i] << shift1; - ma_uint32 right = left - side; - pOutputSamples[i*2+0] = (ma_int32)left; - pOutputSamples[i*2+1] = (ma_int32)right; + drflac_uint32 left = pInputSamples0U32[i] << shift0; + drflac_uint32 side = pInputSamples1U32[i] << shift1; + drflac_uint32 right = left - side; + pOutputSamples[i*2+0] = (drflac_int32)left; + pOutputSamples[i*2+1] = (drflac_int32)right; } } #endif -static MA_INLINE void ma_dr_flac_read_pcm_frames_s32__decode_left_side(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, ma_int32* pOutputSamples) +static DRFLAC_INLINE void drflac_read_pcm_frames_s32__decode_left_side(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int32* pOutputSamples) { -#if defined(MA_DR_FLAC_SUPPORT_SSE2) - if (ma_dr_flac__gIsSSE2Supported && pFlac->bitsPerSample <= 24) { - ma_dr_flac_read_pcm_frames_s32__decode_left_side__sse2(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); +#if defined(DRFLAC_SUPPORT_SSE2) + if (drflac__gIsSSE2Supported && pFlac->bitsPerSample <= 24) { + drflac_read_pcm_frames_s32__decode_left_side__sse2(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); } else -#elif defined(MA_DR_FLAC_SUPPORT_NEON) - if (ma_dr_flac__gIsNEONSupported && pFlac->bitsPerSample <= 24) { - ma_dr_flac_read_pcm_frames_s32__decode_left_side__neon(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); +#elif defined(DRFLAC_SUPPORT_NEON) + if (drflac__gIsNEONSupported && pFlac->bitsPerSample <= 24) { + drflac_read_pcm_frames_s32__decode_left_side__neon(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); } else #endif { #if 0 - ma_dr_flac_read_pcm_frames_s32__decode_left_side__reference(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); + drflac_read_pcm_frames_s32__decode_left_side__reference(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); #else - ma_dr_flac_read_pcm_frames_s32__decode_left_side__scalar(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); + drflac_read_pcm_frames_s32__decode_left_side__scalar(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); #endif } } #if 0 -static MA_INLINE void ma_dr_flac_read_pcm_frames_s32__decode_right_side__reference(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, ma_int32* pOutputSamples) +static DRFLAC_INLINE void drflac_read_pcm_frames_s32__decode_right_side__reference(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int32* pOutputSamples) { - ma_uint64 i; + drflac_uint64 i; for (i = 0; i < frameCount; ++i) { - ma_uint32 side = (ma_uint32)pInputSamples0[i] << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample); - ma_uint32 right = (ma_uint32)pInputSamples1[i] << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample); - ma_uint32 left = right + side; - pOutputSamples[i*2+0] = (ma_int32)left; - pOutputSamples[i*2+1] = (ma_int32)right; + drflac_uint32 side = (drflac_uint32)pInputSamples0[i] << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample); + drflac_uint32 right = (drflac_uint32)pInputSamples1[i] << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample); + drflac_uint32 left = right + side; + pOutputSamples[i*2+0] = (drflac_int32)left; + pOutputSamples[i*2+1] = (drflac_int32)right; } } #endif -static MA_INLINE void ma_dr_flac_read_pcm_frames_s32__decode_right_side__scalar(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, ma_int32* pOutputSamples) +static DRFLAC_INLINE void drflac_read_pcm_frames_s32__decode_right_side__scalar(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int32* pOutputSamples) { - ma_uint64 i; - ma_uint64 frameCount4 = frameCount >> 2; - const ma_uint32* pInputSamples0U32 = (const ma_uint32*)pInputSamples0; - const ma_uint32* pInputSamples1U32 = (const ma_uint32*)pInputSamples1; - ma_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; - ma_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; + drflac_uint64 i; + drflac_uint64 frameCount4 = frameCount >> 2; + const drflac_uint32* pInputSamples0U32 = (const drflac_uint32*)pInputSamples0; + const drflac_uint32* pInputSamples1U32 = (const drflac_uint32*)pInputSamples1; + drflac_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; + drflac_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; for (i = 0; i < frameCount4; ++i) { - ma_uint32 side0 = pInputSamples0U32[i*4+0] << shift0; - ma_uint32 side1 = pInputSamples0U32[i*4+1] << shift0; - ma_uint32 side2 = pInputSamples0U32[i*4+2] << shift0; - ma_uint32 side3 = pInputSamples0U32[i*4+3] << shift0; - ma_uint32 right0 = pInputSamples1U32[i*4+0] << shift1; - ma_uint32 right1 = pInputSamples1U32[i*4+1] << shift1; - ma_uint32 right2 = pInputSamples1U32[i*4+2] << shift1; - ma_uint32 right3 = pInputSamples1U32[i*4+3] << shift1; - ma_uint32 left0 = right0 + side0; - ma_uint32 left1 = right1 + side1; - ma_uint32 left2 = right2 + side2; - ma_uint32 left3 = right3 + side3; - pOutputSamples[i*8+0] = (ma_int32)left0; - pOutputSamples[i*8+1] = (ma_int32)right0; - pOutputSamples[i*8+2] = (ma_int32)left1; - pOutputSamples[i*8+3] = (ma_int32)right1; - pOutputSamples[i*8+4] = (ma_int32)left2; - pOutputSamples[i*8+5] = (ma_int32)right2; - pOutputSamples[i*8+6] = (ma_int32)left3; - pOutputSamples[i*8+7] = (ma_int32)right3; + drflac_uint32 side0 = pInputSamples0U32[i*4+0] << shift0; + drflac_uint32 side1 = pInputSamples0U32[i*4+1] << shift0; + drflac_uint32 side2 = pInputSamples0U32[i*4+2] << shift0; + drflac_uint32 side3 = pInputSamples0U32[i*4+3] << shift0; + drflac_uint32 right0 = pInputSamples1U32[i*4+0] << shift1; + drflac_uint32 right1 = pInputSamples1U32[i*4+1] << shift1; + drflac_uint32 right2 = pInputSamples1U32[i*4+2] << shift1; + drflac_uint32 right3 = pInputSamples1U32[i*4+3] << shift1; + drflac_uint32 left0 = right0 + side0; + drflac_uint32 left1 = right1 + side1; + drflac_uint32 left2 = right2 + side2; + drflac_uint32 left3 = right3 + side3; + pOutputSamples[i*8+0] = (drflac_int32)left0; + pOutputSamples[i*8+1] = (drflac_int32)right0; + pOutputSamples[i*8+2] = (drflac_int32)left1; + pOutputSamples[i*8+3] = (drflac_int32)right1; + pOutputSamples[i*8+4] = (drflac_int32)left2; + pOutputSamples[i*8+5] = (drflac_int32)right2; + pOutputSamples[i*8+6] = (drflac_int32)left3; + pOutputSamples[i*8+7] = (drflac_int32)right3; } for (i = (frameCount4 << 2); i < frameCount; ++i) { - ma_uint32 side = pInputSamples0U32[i] << shift0; - ma_uint32 right = pInputSamples1U32[i] << shift1; - ma_uint32 left = right + side; - pOutputSamples[i*2+0] = (ma_int32)left; - pOutputSamples[i*2+1] = (ma_int32)right; - } -} -#if defined(MA_DR_FLAC_SUPPORT_SSE2) -static MA_INLINE void ma_dr_flac_read_pcm_frames_s32__decode_right_side__sse2(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, ma_int32* pOutputSamples) -{ - ma_uint64 i; - ma_uint64 frameCount4 = frameCount >> 2; - const ma_uint32* pInputSamples0U32 = (const ma_uint32*)pInputSamples0; - const ma_uint32* pInputSamples1U32 = (const ma_uint32*)pInputSamples1; - ma_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; - ma_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; - MA_DR_FLAC_ASSERT(pFlac->bitsPerSample <= 24); + drflac_uint32 side = pInputSamples0U32[i] << shift0; + drflac_uint32 right = pInputSamples1U32[i] << shift1; + drflac_uint32 left = right + side; + pOutputSamples[i*2+0] = (drflac_int32)left; + pOutputSamples[i*2+1] = (drflac_int32)right; + } +} +#if defined(DRFLAC_SUPPORT_SSE2) +static DRFLAC_INLINE void drflac_read_pcm_frames_s32__decode_right_side__sse2(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int32* pOutputSamples) +{ + drflac_uint64 i; + drflac_uint64 frameCount4 = frameCount >> 2; + const drflac_uint32* pInputSamples0U32 = (const drflac_uint32*)pInputSamples0; + const drflac_uint32* pInputSamples1U32 = (const drflac_uint32*)pInputSamples1; + drflac_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; + drflac_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; + DRFLAC_ASSERT(pFlac->bitsPerSample <= 24); for (i = 0; i < frameCount4; ++i) { __m128i side = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples0 + i), shift0); __m128i right = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples1 + i), shift1); @@ -88337,26 +87723,26 @@ static MA_INLINE void ma_dr_flac_read_pcm_frames_s32__decode_right_side__sse2(ma _mm_storeu_si128((__m128i*)(pOutputSamples + i*8 + 4), _mm_unpackhi_epi32(left, right)); } for (i = (frameCount4 << 2); i < frameCount; ++i) { - ma_uint32 side = pInputSamples0U32[i] << shift0; - ma_uint32 right = pInputSamples1U32[i] << shift1; - ma_uint32 left = right + side; - pOutputSamples[i*2+0] = (ma_int32)left; - pOutputSamples[i*2+1] = (ma_int32)right; + drflac_uint32 side = pInputSamples0U32[i] << shift0; + drflac_uint32 right = pInputSamples1U32[i] << shift1; + drflac_uint32 left = right + side; + pOutputSamples[i*2+0] = (drflac_int32)left; + pOutputSamples[i*2+1] = (drflac_int32)right; } } #endif -#if defined(MA_DR_FLAC_SUPPORT_NEON) -static MA_INLINE void ma_dr_flac_read_pcm_frames_s32__decode_right_side__neon(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, ma_int32* pOutputSamples) +#if defined(DRFLAC_SUPPORT_NEON) +static DRFLAC_INLINE void drflac_read_pcm_frames_s32__decode_right_side__neon(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int32* pOutputSamples) { - ma_uint64 i; - ma_uint64 frameCount4 = frameCount >> 2; - const ma_uint32* pInputSamples0U32 = (const ma_uint32*)pInputSamples0; - const ma_uint32* pInputSamples1U32 = (const ma_uint32*)pInputSamples1; - ma_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; - ma_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; + drflac_uint64 i; + drflac_uint64 frameCount4 = frameCount >> 2; + const drflac_uint32* pInputSamples0U32 = (const drflac_uint32*)pInputSamples0; + const drflac_uint32* pInputSamples1U32 = (const drflac_uint32*)pInputSamples1; + drflac_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; + drflac_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; int32x4_t shift0_4; int32x4_t shift1_4; - MA_DR_FLAC_ASSERT(pFlac->bitsPerSample <= 24); + DRFLAC_ASSERT(pFlac->bitsPerSample <= 24); shift0_4 = vdupq_n_s32(shift0); shift1_4 = vdupq_n_s32(shift1); for (i = 0; i < frameCount4; ++i) { @@ -88366,74 +87752,74 @@ static MA_INLINE void ma_dr_flac_read_pcm_frames_s32__decode_right_side__neon(ma side = vshlq_u32(vld1q_u32(pInputSamples0U32 + i*4), shift0_4); right = vshlq_u32(vld1q_u32(pInputSamples1U32 + i*4), shift1_4); left = vaddq_u32(right, side); - ma_dr_flac__vst2q_u32((ma_uint32*)pOutputSamples + i*8, vzipq_u32(left, right)); + drflac__vst2q_u32((drflac_uint32*)pOutputSamples + i*8, vzipq_u32(left, right)); } for (i = (frameCount4 << 2); i < frameCount; ++i) { - ma_uint32 side = pInputSamples0U32[i] << shift0; - ma_uint32 right = pInputSamples1U32[i] << shift1; - ma_uint32 left = right + side; - pOutputSamples[i*2+0] = (ma_int32)left; - pOutputSamples[i*2+1] = (ma_int32)right; + drflac_uint32 side = pInputSamples0U32[i] << shift0; + drflac_uint32 right = pInputSamples1U32[i] << shift1; + drflac_uint32 left = right + side; + pOutputSamples[i*2+0] = (drflac_int32)left; + pOutputSamples[i*2+1] = (drflac_int32)right; } } #endif -static MA_INLINE void ma_dr_flac_read_pcm_frames_s32__decode_right_side(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, ma_int32* pOutputSamples) +static DRFLAC_INLINE void drflac_read_pcm_frames_s32__decode_right_side(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int32* pOutputSamples) { -#if defined(MA_DR_FLAC_SUPPORT_SSE2) - if (ma_dr_flac__gIsSSE2Supported && pFlac->bitsPerSample <= 24) { - ma_dr_flac_read_pcm_frames_s32__decode_right_side__sse2(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); +#if defined(DRFLAC_SUPPORT_SSE2) + if (drflac__gIsSSE2Supported && pFlac->bitsPerSample <= 24) { + drflac_read_pcm_frames_s32__decode_right_side__sse2(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); } else -#elif defined(MA_DR_FLAC_SUPPORT_NEON) - if (ma_dr_flac__gIsNEONSupported && pFlac->bitsPerSample <= 24) { - ma_dr_flac_read_pcm_frames_s32__decode_right_side__neon(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); +#elif defined(DRFLAC_SUPPORT_NEON) + if (drflac__gIsNEONSupported && pFlac->bitsPerSample <= 24) { + drflac_read_pcm_frames_s32__decode_right_side__neon(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); } else #endif { #if 0 - ma_dr_flac_read_pcm_frames_s32__decode_right_side__reference(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); + drflac_read_pcm_frames_s32__decode_right_side__reference(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); #else - ma_dr_flac_read_pcm_frames_s32__decode_right_side__scalar(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); + drflac_read_pcm_frames_s32__decode_right_side__scalar(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); #endif } } #if 0 -static MA_INLINE void ma_dr_flac_read_pcm_frames_s32__decode_mid_side__reference(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, ma_int32* pOutputSamples) +static DRFLAC_INLINE void drflac_read_pcm_frames_s32__decode_mid_side__reference(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int32* pOutputSamples) { - for (ma_uint64 i = 0; i < frameCount; ++i) { - ma_uint32 mid = pInputSamples0U32[i] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; - ma_uint32 side = pInputSamples1U32[i] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; + for (drflac_uint64 i = 0; i < frameCount; ++i) { + drflac_uint32 mid = pInputSamples0U32[i] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; + drflac_uint32 side = pInputSamples1U32[i] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; mid = (mid << 1) | (side & 0x01); - pOutputSamples[i*2+0] = (ma_int32)((ma_uint32)((ma_int32)(mid + side) >> 1) << unusedBitsPerSample); - pOutputSamples[i*2+1] = (ma_int32)((ma_uint32)((ma_int32)(mid - side) >> 1) << unusedBitsPerSample); + pOutputSamples[i*2+0] = (drflac_int32)((drflac_uint32)((drflac_int32)(mid + side) >> 1) << unusedBitsPerSample); + pOutputSamples[i*2+1] = (drflac_int32)((drflac_uint32)((drflac_int32)(mid - side) >> 1) << unusedBitsPerSample); } } #endif -static MA_INLINE void ma_dr_flac_read_pcm_frames_s32__decode_mid_side__scalar(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, ma_int32* pOutputSamples) +static DRFLAC_INLINE void drflac_read_pcm_frames_s32__decode_mid_side__scalar(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int32* pOutputSamples) { - ma_uint64 i; - ma_uint64 frameCount4 = frameCount >> 2; - const ma_uint32* pInputSamples0U32 = (const ma_uint32*)pInputSamples0; - const ma_uint32* pInputSamples1U32 = (const ma_uint32*)pInputSamples1; - ma_int32 shift = unusedBitsPerSample; + drflac_uint64 i; + drflac_uint64 frameCount4 = frameCount >> 2; + const drflac_uint32* pInputSamples0U32 = (const drflac_uint32*)pInputSamples0; + const drflac_uint32* pInputSamples1U32 = (const drflac_uint32*)pInputSamples1; + drflac_int32 shift = unusedBitsPerSample; if (shift > 0) { shift -= 1; for (i = 0; i < frameCount4; ++i) { - ma_uint32 temp0L; - ma_uint32 temp1L; - ma_uint32 temp2L; - ma_uint32 temp3L; - ma_uint32 temp0R; - ma_uint32 temp1R; - ma_uint32 temp2R; - ma_uint32 temp3R; - ma_uint32 mid0 = pInputSamples0U32[i*4+0] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; - ma_uint32 mid1 = pInputSamples0U32[i*4+1] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; - ma_uint32 mid2 = pInputSamples0U32[i*4+2] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; - ma_uint32 mid3 = pInputSamples0U32[i*4+3] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; - ma_uint32 side0 = pInputSamples1U32[i*4+0] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; - ma_uint32 side1 = pInputSamples1U32[i*4+1] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; - ma_uint32 side2 = pInputSamples1U32[i*4+2] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; - ma_uint32 side3 = pInputSamples1U32[i*4+3] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; + drflac_uint32 temp0L; + drflac_uint32 temp1L; + drflac_uint32 temp2L; + drflac_uint32 temp3L; + drflac_uint32 temp0R; + drflac_uint32 temp1R; + drflac_uint32 temp2R; + drflac_uint32 temp3R; + drflac_uint32 mid0 = pInputSamples0U32[i*4+0] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; + drflac_uint32 mid1 = pInputSamples0U32[i*4+1] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; + drflac_uint32 mid2 = pInputSamples0U32[i*4+2] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; + drflac_uint32 mid3 = pInputSamples0U32[i*4+3] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; + drflac_uint32 side0 = pInputSamples1U32[i*4+0] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; + drflac_uint32 side1 = pInputSamples1U32[i*4+1] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; + drflac_uint32 side2 = pInputSamples1U32[i*4+2] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; + drflac_uint32 side3 = pInputSamples1U32[i*4+3] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; mid0 = (mid0 << 1) | (side0 & 0x01); mid1 = (mid1 << 1) | (side1 & 0x01); mid2 = (mid2 << 1) | (side2 & 0x01); @@ -88446,72 +87832,72 @@ static MA_INLINE void ma_dr_flac_read_pcm_frames_s32__decode_mid_side__scalar(ma temp1R = (mid1 - side1) << shift; temp2R = (mid2 - side2) << shift; temp3R = (mid3 - side3) << shift; - pOutputSamples[i*8+0] = (ma_int32)temp0L; - pOutputSamples[i*8+1] = (ma_int32)temp0R; - pOutputSamples[i*8+2] = (ma_int32)temp1L; - pOutputSamples[i*8+3] = (ma_int32)temp1R; - pOutputSamples[i*8+4] = (ma_int32)temp2L; - pOutputSamples[i*8+5] = (ma_int32)temp2R; - pOutputSamples[i*8+6] = (ma_int32)temp3L; - pOutputSamples[i*8+7] = (ma_int32)temp3R; + pOutputSamples[i*8+0] = (drflac_int32)temp0L; + pOutputSamples[i*8+1] = (drflac_int32)temp0R; + pOutputSamples[i*8+2] = (drflac_int32)temp1L; + pOutputSamples[i*8+3] = (drflac_int32)temp1R; + pOutputSamples[i*8+4] = (drflac_int32)temp2L; + pOutputSamples[i*8+5] = (drflac_int32)temp2R; + pOutputSamples[i*8+6] = (drflac_int32)temp3L; + pOutputSamples[i*8+7] = (drflac_int32)temp3R; } } else { for (i = 0; i < frameCount4; ++i) { - ma_uint32 temp0L; - ma_uint32 temp1L; - ma_uint32 temp2L; - ma_uint32 temp3L; - ma_uint32 temp0R; - ma_uint32 temp1R; - ma_uint32 temp2R; - ma_uint32 temp3R; - ma_uint32 mid0 = pInputSamples0U32[i*4+0] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; - ma_uint32 mid1 = pInputSamples0U32[i*4+1] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; - ma_uint32 mid2 = pInputSamples0U32[i*4+2] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; - ma_uint32 mid3 = pInputSamples0U32[i*4+3] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; - ma_uint32 side0 = pInputSamples1U32[i*4+0] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; - ma_uint32 side1 = pInputSamples1U32[i*4+1] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; - ma_uint32 side2 = pInputSamples1U32[i*4+2] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; - ma_uint32 side3 = pInputSamples1U32[i*4+3] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; + drflac_uint32 temp0L; + drflac_uint32 temp1L; + drflac_uint32 temp2L; + drflac_uint32 temp3L; + drflac_uint32 temp0R; + drflac_uint32 temp1R; + drflac_uint32 temp2R; + drflac_uint32 temp3R; + drflac_uint32 mid0 = pInputSamples0U32[i*4+0] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; + drflac_uint32 mid1 = pInputSamples0U32[i*4+1] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; + drflac_uint32 mid2 = pInputSamples0U32[i*4+2] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; + drflac_uint32 mid3 = pInputSamples0U32[i*4+3] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; + drflac_uint32 side0 = pInputSamples1U32[i*4+0] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; + drflac_uint32 side1 = pInputSamples1U32[i*4+1] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; + drflac_uint32 side2 = pInputSamples1U32[i*4+2] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; + drflac_uint32 side3 = pInputSamples1U32[i*4+3] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; mid0 = (mid0 << 1) | (side0 & 0x01); mid1 = (mid1 << 1) | (side1 & 0x01); mid2 = (mid2 << 1) | (side2 & 0x01); mid3 = (mid3 << 1) | (side3 & 0x01); - temp0L = (ma_uint32)((ma_int32)(mid0 + side0) >> 1); - temp1L = (ma_uint32)((ma_int32)(mid1 + side1) >> 1); - temp2L = (ma_uint32)((ma_int32)(mid2 + side2) >> 1); - temp3L = (ma_uint32)((ma_int32)(mid3 + side3) >> 1); - temp0R = (ma_uint32)((ma_int32)(mid0 - side0) >> 1); - temp1R = (ma_uint32)((ma_int32)(mid1 - side1) >> 1); - temp2R = (ma_uint32)((ma_int32)(mid2 - side2) >> 1); - temp3R = (ma_uint32)((ma_int32)(mid3 - side3) >> 1); - pOutputSamples[i*8+0] = (ma_int32)temp0L; - pOutputSamples[i*8+1] = (ma_int32)temp0R; - pOutputSamples[i*8+2] = (ma_int32)temp1L; - pOutputSamples[i*8+3] = (ma_int32)temp1R; - pOutputSamples[i*8+4] = (ma_int32)temp2L; - pOutputSamples[i*8+5] = (ma_int32)temp2R; - pOutputSamples[i*8+6] = (ma_int32)temp3L; - pOutputSamples[i*8+7] = (ma_int32)temp3R; + temp0L = (drflac_uint32)((drflac_int32)(mid0 + side0) >> 1); + temp1L = (drflac_uint32)((drflac_int32)(mid1 + side1) >> 1); + temp2L = (drflac_uint32)((drflac_int32)(mid2 + side2) >> 1); + temp3L = (drflac_uint32)((drflac_int32)(mid3 + side3) >> 1); + temp0R = (drflac_uint32)((drflac_int32)(mid0 - side0) >> 1); + temp1R = (drflac_uint32)((drflac_int32)(mid1 - side1) >> 1); + temp2R = (drflac_uint32)((drflac_int32)(mid2 - side2) >> 1); + temp3R = (drflac_uint32)((drflac_int32)(mid3 - side3) >> 1); + pOutputSamples[i*8+0] = (drflac_int32)temp0L; + pOutputSamples[i*8+1] = (drflac_int32)temp0R; + pOutputSamples[i*8+2] = (drflac_int32)temp1L; + pOutputSamples[i*8+3] = (drflac_int32)temp1R; + pOutputSamples[i*8+4] = (drflac_int32)temp2L; + pOutputSamples[i*8+5] = (drflac_int32)temp2R; + pOutputSamples[i*8+6] = (drflac_int32)temp3L; + pOutputSamples[i*8+7] = (drflac_int32)temp3R; } } for (i = (frameCount4 << 2); i < frameCount; ++i) { - ma_uint32 mid = pInputSamples0U32[i] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; - ma_uint32 side = pInputSamples1U32[i] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; + drflac_uint32 mid = pInputSamples0U32[i] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; + drflac_uint32 side = pInputSamples1U32[i] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; mid = (mid << 1) | (side & 0x01); - pOutputSamples[i*2+0] = (ma_int32)((ma_uint32)((ma_int32)(mid + side) >> 1) << unusedBitsPerSample); - pOutputSamples[i*2+1] = (ma_int32)((ma_uint32)((ma_int32)(mid - side) >> 1) << unusedBitsPerSample); + pOutputSamples[i*2+0] = (drflac_int32)((drflac_uint32)((drflac_int32)(mid + side) >> 1) << unusedBitsPerSample); + pOutputSamples[i*2+1] = (drflac_int32)((drflac_uint32)((drflac_int32)(mid - side) >> 1) << unusedBitsPerSample); } } -#if defined(MA_DR_FLAC_SUPPORT_SSE2) -static MA_INLINE void ma_dr_flac_read_pcm_frames_s32__decode_mid_side__sse2(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, ma_int32* pOutputSamples) +#if defined(DRFLAC_SUPPORT_SSE2) +static DRFLAC_INLINE void drflac_read_pcm_frames_s32__decode_mid_side__sse2(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int32* pOutputSamples) { - ma_uint64 i; - ma_uint64 frameCount4 = frameCount >> 2; - const ma_uint32* pInputSamples0U32 = (const ma_uint32*)pInputSamples0; - const ma_uint32* pInputSamples1U32 = (const ma_uint32*)pInputSamples1; - ma_int32 shift = unusedBitsPerSample; - MA_DR_FLAC_ASSERT(pFlac->bitsPerSample <= 24); + drflac_uint64 i; + drflac_uint64 frameCount4 = frameCount >> 2; + const drflac_uint32* pInputSamples0U32 = (const drflac_uint32*)pInputSamples0; + const drflac_uint32* pInputSamples1U32 = (const drflac_uint32*)pInputSamples1; + drflac_int32 shift = unusedBitsPerSample; + DRFLAC_ASSERT(pFlac->bitsPerSample <= 24); if (shift == 0) { for (i = 0; i < frameCount4; ++i) { __m128i mid; @@ -88527,11 +87913,11 @@ static MA_INLINE void ma_dr_flac_read_pcm_frames_s32__decode_mid_side__sse2(ma_d _mm_storeu_si128((__m128i*)(pOutputSamples + i*8 + 4), _mm_unpackhi_epi32(left, right)); } for (i = (frameCount4 << 2); i < frameCount; ++i) { - ma_uint32 mid = pInputSamples0U32[i] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; - ma_uint32 side = pInputSamples1U32[i] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; + drflac_uint32 mid = pInputSamples0U32[i] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; + drflac_uint32 side = pInputSamples1U32[i] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; mid = (mid << 1) | (side & 0x01); - pOutputSamples[i*2+0] = (ma_int32)(mid + side) >> 1; - pOutputSamples[i*2+1] = (ma_int32)(mid - side) >> 1; + pOutputSamples[i*2+0] = (drflac_int32)(mid + side) >> 1; + pOutputSamples[i*2+1] = (drflac_int32)(mid - side) >> 1; } } else { shift -= 1; @@ -88549,27 +87935,27 @@ static MA_INLINE void ma_dr_flac_read_pcm_frames_s32__decode_mid_side__sse2(ma_d _mm_storeu_si128((__m128i*)(pOutputSamples + i*8 + 4), _mm_unpackhi_epi32(left, right)); } for (i = (frameCount4 << 2); i < frameCount; ++i) { - ma_uint32 mid = pInputSamples0U32[i] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; - ma_uint32 side = pInputSamples1U32[i] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; + drflac_uint32 mid = pInputSamples0U32[i] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; + drflac_uint32 side = pInputSamples1U32[i] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; mid = (mid << 1) | (side & 0x01); - pOutputSamples[i*2+0] = (ma_int32)((mid + side) << shift); - pOutputSamples[i*2+1] = (ma_int32)((mid - side) << shift); + pOutputSamples[i*2+0] = (drflac_int32)((mid + side) << shift); + pOutputSamples[i*2+1] = (drflac_int32)((mid - side) << shift); } } } #endif -#if defined(MA_DR_FLAC_SUPPORT_NEON) -static MA_INLINE void ma_dr_flac_read_pcm_frames_s32__decode_mid_side__neon(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, ma_int32* pOutputSamples) +#if defined(DRFLAC_SUPPORT_NEON) +static DRFLAC_INLINE void drflac_read_pcm_frames_s32__decode_mid_side__neon(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int32* pOutputSamples) { - ma_uint64 i; - ma_uint64 frameCount4 = frameCount >> 2; - const ma_uint32* pInputSamples0U32 = (const ma_uint32*)pInputSamples0; - const ma_uint32* pInputSamples1U32 = (const ma_uint32*)pInputSamples1; - ma_int32 shift = unusedBitsPerSample; + drflac_uint64 i; + drflac_uint64 frameCount4 = frameCount >> 2; + const drflac_uint32* pInputSamples0U32 = (const drflac_uint32*)pInputSamples0; + const drflac_uint32* pInputSamples1U32 = (const drflac_uint32*)pInputSamples1; + drflac_int32 shift = unusedBitsPerSample; int32x4_t wbpsShift0_4; int32x4_t wbpsShift1_4; uint32x4_t one4; - MA_DR_FLAC_ASSERT(pFlac->bitsPerSample <= 24); + DRFLAC_ASSERT(pFlac->bitsPerSample <= 24); wbpsShift0_4 = vdupq_n_s32(pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample); wbpsShift1_4 = vdupq_n_s32(pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample); one4 = vdupq_n_u32(1); @@ -88584,14 +87970,14 @@ static MA_INLINE void ma_dr_flac_read_pcm_frames_s32__decode_mid_side__neon(ma_d mid = vorrq_u32(vshlq_n_u32(mid, 1), vandq_u32(side, one4)); left = vshrq_n_s32(vreinterpretq_s32_u32(vaddq_u32(mid, side)), 1); right = vshrq_n_s32(vreinterpretq_s32_u32(vsubq_u32(mid, side)), 1); - ma_dr_flac__vst2q_s32(pOutputSamples + i*8, vzipq_s32(left, right)); + drflac__vst2q_s32(pOutputSamples + i*8, vzipq_s32(left, right)); } for (i = (frameCount4 << 2); i < frameCount; ++i) { - ma_uint32 mid = pInputSamples0U32[i] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; - ma_uint32 side = pInputSamples1U32[i] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; + drflac_uint32 mid = pInputSamples0U32[i] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; + drflac_uint32 side = pInputSamples1U32[i] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; mid = (mid << 1) | (side & 0x01); - pOutputSamples[i*2+0] = (ma_int32)(mid + side) >> 1; - pOutputSamples[i*2+1] = (ma_int32)(mid - side) >> 1; + pOutputSamples[i*2+0] = (drflac_int32)(mid + side) >> 1; + pOutputSamples[i*2+1] = (drflac_int32)(mid - side) >> 1; } } else { int32x4_t shift4; @@ -88607,86 +87993,86 @@ static MA_INLINE void ma_dr_flac_read_pcm_frames_s32__decode_mid_side__neon(ma_d mid = vorrq_u32(vshlq_n_u32(mid, 1), vandq_u32(side, one4)); left = vreinterpretq_s32_u32(vshlq_u32(vaddq_u32(mid, side), shift4)); right = vreinterpretq_s32_u32(vshlq_u32(vsubq_u32(mid, side), shift4)); - ma_dr_flac__vst2q_s32(pOutputSamples + i*8, vzipq_s32(left, right)); + drflac__vst2q_s32(pOutputSamples + i*8, vzipq_s32(left, right)); } for (i = (frameCount4 << 2); i < frameCount; ++i) { - ma_uint32 mid = pInputSamples0U32[i] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; - ma_uint32 side = pInputSamples1U32[i] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; + drflac_uint32 mid = pInputSamples0U32[i] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; + drflac_uint32 side = pInputSamples1U32[i] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; mid = (mid << 1) | (side & 0x01); - pOutputSamples[i*2+0] = (ma_int32)((mid + side) << shift); - pOutputSamples[i*2+1] = (ma_int32)((mid - side) << shift); + pOutputSamples[i*2+0] = (drflac_int32)((mid + side) << shift); + pOutputSamples[i*2+1] = (drflac_int32)((mid - side) << shift); } } } #endif -static MA_INLINE void ma_dr_flac_read_pcm_frames_s32__decode_mid_side(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, ma_int32* pOutputSamples) +static DRFLAC_INLINE void drflac_read_pcm_frames_s32__decode_mid_side(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int32* pOutputSamples) { -#if defined(MA_DR_FLAC_SUPPORT_SSE2) - if (ma_dr_flac__gIsSSE2Supported && pFlac->bitsPerSample <= 24) { - ma_dr_flac_read_pcm_frames_s32__decode_mid_side__sse2(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); +#if defined(DRFLAC_SUPPORT_SSE2) + if (drflac__gIsSSE2Supported && pFlac->bitsPerSample <= 24) { + drflac_read_pcm_frames_s32__decode_mid_side__sse2(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); } else -#elif defined(MA_DR_FLAC_SUPPORT_NEON) - if (ma_dr_flac__gIsNEONSupported && pFlac->bitsPerSample <= 24) { - ma_dr_flac_read_pcm_frames_s32__decode_mid_side__neon(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); +#elif defined(DRFLAC_SUPPORT_NEON) + if (drflac__gIsNEONSupported && pFlac->bitsPerSample <= 24) { + drflac_read_pcm_frames_s32__decode_mid_side__neon(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); } else #endif { #if 0 - ma_dr_flac_read_pcm_frames_s32__decode_mid_side__reference(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); + drflac_read_pcm_frames_s32__decode_mid_side__reference(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); #else - ma_dr_flac_read_pcm_frames_s32__decode_mid_side__scalar(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); + drflac_read_pcm_frames_s32__decode_mid_side__scalar(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); #endif } } #if 0 -static MA_INLINE void ma_dr_flac_read_pcm_frames_s32__decode_independent_stereo__reference(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, ma_int32* pOutputSamples) +static DRFLAC_INLINE void drflac_read_pcm_frames_s32__decode_independent_stereo__reference(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int32* pOutputSamples) { - for (ma_uint64 i = 0; i < frameCount; ++i) { - pOutputSamples[i*2+0] = (ma_int32)((ma_uint32)pInputSamples0[i] << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample)); - pOutputSamples[i*2+1] = (ma_int32)((ma_uint32)pInputSamples1[i] << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample)); + for (drflac_uint64 i = 0; i < frameCount; ++i) { + pOutputSamples[i*2+0] = (drflac_int32)((drflac_uint32)pInputSamples0[i] << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample)); + pOutputSamples[i*2+1] = (drflac_int32)((drflac_uint32)pInputSamples1[i] << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample)); } } #endif -static MA_INLINE void ma_dr_flac_read_pcm_frames_s32__decode_independent_stereo__scalar(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, ma_int32* pOutputSamples) +static DRFLAC_INLINE void drflac_read_pcm_frames_s32__decode_independent_stereo__scalar(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int32* pOutputSamples) { - ma_uint64 i; - ma_uint64 frameCount4 = frameCount >> 2; - const ma_uint32* pInputSamples0U32 = (const ma_uint32*)pInputSamples0; - const ma_uint32* pInputSamples1U32 = (const ma_uint32*)pInputSamples1; - ma_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; - ma_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; + drflac_uint64 i; + drflac_uint64 frameCount4 = frameCount >> 2; + const drflac_uint32* pInputSamples0U32 = (const drflac_uint32*)pInputSamples0; + const drflac_uint32* pInputSamples1U32 = (const drflac_uint32*)pInputSamples1; + drflac_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; + drflac_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; for (i = 0; i < frameCount4; ++i) { - ma_uint32 tempL0 = pInputSamples0U32[i*4+0] << shift0; - ma_uint32 tempL1 = pInputSamples0U32[i*4+1] << shift0; - ma_uint32 tempL2 = pInputSamples0U32[i*4+2] << shift0; - ma_uint32 tempL3 = pInputSamples0U32[i*4+3] << shift0; - ma_uint32 tempR0 = pInputSamples1U32[i*4+0] << shift1; - ma_uint32 tempR1 = pInputSamples1U32[i*4+1] << shift1; - ma_uint32 tempR2 = pInputSamples1U32[i*4+2] << shift1; - ma_uint32 tempR3 = pInputSamples1U32[i*4+3] << shift1; - pOutputSamples[i*8+0] = (ma_int32)tempL0; - pOutputSamples[i*8+1] = (ma_int32)tempR0; - pOutputSamples[i*8+2] = (ma_int32)tempL1; - pOutputSamples[i*8+3] = (ma_int32)tempR1; - pOutputSamples[i*8+4] = (ma_int32)tempL2; - pOutputSamples[i*8+5] = (ma_int32)tempR2; - pOutputSamples[i*8+6] = (ma_int32)tempL3; - pOutputSamples[i*8+7] = (ma_int32)tempR3; + drflac_uint32 tempL0 = pInputSamples0U32[i*4+0] << shift0; + drflac_uint32 tempL1 = pInputSamples0U32[i*4+1] << shift0; + drflac_uint32 tempL2 = pInputSamples0U32[i*4+2] << shift0; + drflac_uint32 tempL3 = pInputSamples0U32[i*4+3] << shift0; + drflac_uint32 tempR0 = pInputSamples1U32[i*4+0] << shift1; + drflac_uint32 tempR1 = pInputSamples1U32[i*4+1] << shift1; + drflac_uint32 tempR2 = pInputSamples1U32[i*4+2] << shift1; + drflac_uint32 tempR3 = pInputSamples1U32[i*4+3] << shift1; + pOutputSamples[i*8+0] = (drflac_int32)tempL0; + pOutputSamples[i*8+1] = (drflac_int32)tempR0; + pOutputSamples[i*8+2] = (drflac_int32)tempL1; + pOutputSamples[i*8+3] = (drflac_int32)tempR1; + pOutputSamples[i*8+4] = (drflac_int32)tempL2; + pOutputSamples[i*8+5] = (drflac_int32)tempR2; + pOutputSamples[i*8+6] = (drflac_int32)tempL3; + pOutputSamples[i*8+7] = (drflac_int32)tempR3; } for (i = (frameCount4 << 2); i < frameCount; ++i) { - pOutputSamples[i*2+0] = (ma_int32)(pInputSamples0U32[i] << shift0); - pOutputSamples[i*2+1] = (ma_int32)(pInputSamples1U32[i] << shift1); + pOutputSamples[i*2+0] = (drflac_int32)(pInputSamples0U32[i] << shift0); + pOutputSamples[i*2+1] = (drflac_int32)(pInputSamples1U32[i] << shift1); } } -#if defined(MA_DR_FLAC_SUPPORT_SSE2) -static MA_INLINE void ma_dr_flac_read_pcm_frames_s32__decode_independent_stereo__sse2(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, ma_int32* pOutputSamples) +#if defined(DRFLAC_SUPPORT_SSE2) +static DRFLAC_INLINE void drflac_read_pcm_frames_s32__decode_independent_stereo__sse2(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int32* pOutputSamples) { - ma_uint64 i; - ma_uint64 frameCount4 = frameCount >> 2; - const ma_uint32* pInputSamples0U32 = (const ma_uint32*)pInputSamples0; - const ma_uint32* pInputSamples1U32 = (const ma_uint32*)pInputSamples1; - ma_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; - ma_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; + drflac_uint64 i; + drflac_uint64 frameCount4 = frameCount >> 2; + const drflac_uint32* pInputSamples0U32 = (const drflac_uint32*)pInputSamples0; + const drflac_uint32* pInputSamples1U32 = (const drflac_uint32*)pInputSamples1; + drflac_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; + drflac_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; for (i = 0; i < frameCount4; ++i) { __m128i left = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples0 + i), shift0); __m128i right = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples1 + i), shift1); @@ -88694,20 +88080,20 @@ static MA_INLINE void ma_dr_flac_read_pcm_frames_s32__decode_independent_stereo_ _mm_storeu_si128((__m128i*)(pOutputSamples + i*8 + 4), _mm_unpackhi_epi32(left, right)); } for (i = (frameCount4 << 2); i < frameCount; ++i) { - pOutputSamples[i*2+0] = (ma_int32)(pInputSamples0U32[i] << shift0); - pOutputSamples[i*2+1] = (ma_int32)(pInputSamples1U32[i] << shift1); + pOutputSamples[i*2+0] = (drflac_int32)(pInputSamples0U32[i] << shift0); + pOutputSamples[i*2+1] = (drflac_int32)(pInputSamples1U32[i] << shift1); } } #endif -#if defined(MA_DR_FLAC_SUPPORT_NEON) -static MA_INLINE void ma_dr_flac_read_pcm_frames_s32__decode_independent_stereo__neon(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, ma_int32* pOutputSamples) +#if defined(DRFLAC_SUPPORT_NEON) +static DRFLAC_INLINE void drflac_read_pcm_frames_s32__decode_independent_stereo__neon(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int32* pOutputSamples) { - ma_uint64 i; - ma_uint64 frameCount4 = frameCount >> 2; - const ma_uint32* pInputSamples0U32 = (const ma_uint32*)pInputSamples0; - const ma_uint32* pInputSamples1U32 = (const ma_uint32*)pInputSamples1; - ma_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; - ma_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; + drflac_uint64 i; + drflac_uint64 frameCount4 = frameCount >> 2; + const drflac_uint32* pInputSamples0U32 = (const drflac_uint32*)pInputSamples0; + const drflac_uint32* pInputSamples1U32 = (const drflac_uint32*)pInputSamples1; + drflac_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; + drflac_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; int32x4_t shift4_0 = vdupq_n_s32(shift0); int32x4_t shift4_1 = vdupq_n_s32(shift1); for (i = 0; i < frameCount4; ++i) { @@ -88715,87 +88101,87 @@ static MA_INLINE void ma_dr_flac_read_pcm_frames_s32__decode_independent_stereo_ int32x4_t right; left = vreinterpretq_s32_u32(vshlq_u32(vld1q_u32(pInputSamples0U32 + i*4), shift4_0)); right = vreinterpretq_s32_u32(vshlq_u32(vld1q_u32(pInputSamples1U32 + i*4), shift4_1)); - ma_dr_flac__vst2q_s32(pOutputSamples + i*8, vzipq_s32(left, right)); + drflac__vst2q_s32(pOutputSamples + i*8, vzipq_s32(left, right)); } for (i = (frameCount4 << 2); i < frameCount; ++i) { - pOutputSamples[i*2+0] = (ma_int32)(pInputSamples0U32[i] << shift0); - pOutputSamples[i*2+1] = (ma_int32)(pInputSamples1U32[i] << shift1); + pOutputSamples[i*2+0] = (drflac_int32)(pInputSamples0U32[i] << shift0); + pOutputSamples[i*2+1] = (drflac_int32)(pInputSamples1U32[i] << shift1); } } #endif -static MA_INLINE void ma_dr_flac_read_pcm_frames_s32__decode_independent_stereo(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, ma_int32* pOutputSamples) +static DRFLAC_INLINE void drflac_read_pcm_frames_s32__decode_independent_stereo(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int32* pOutputSamples) { -#if defined(MA_DR_FLAC_SUPPORT_SSE2) - if (ma_dr_flac__gIsSSE2Supported && pFlac->bitsPerSample <= 24) { - ma_dr_flac_read_pcm_frames_s32__decode_independent_stereo__sse2(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); +#if defined(DRFLAC_SUPPORT_SSE2) + if (drflac__gIsSSE2Supported && pFlac->bitsPerSample <= 24) { + drflac_read_pcm_frames_s32__decode_independent_stereo__sse2(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); } else -#elif defined(MA_DR_FLAC_SUPPORT_NEON) - if (ma_dr_flac__gIsNEONSupported && pFlac->bitsPerSample <= 24) { - ma_dr_flac_read_pcm_frames_s32__decode_independent_stereo__neon(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); +#elif defined(DRFLAC_SUPPORT_NEON) + if (drflac__gIsNEONSupported && pFlac->bitsPerSample <= 24) { + drflac_read_pcm_frames_s32__decode_independent_stereo__neon(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); } else #endif { #if 0 - ma_dr_flac_read_pcm_frames_s32__decode_independent_stereo__reference(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); + drflac_read_pcm_frames_s32__decode_independent_stereo__reference(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); #else - ma_dr_flac_read_pcm_frames_s32__decode_independent_stereo__scalar(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); + drflac_read_pcm_frames_s32__decode_independent_stereo__scalar(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); #endif } } -MA_API ma_uint64 ma_dr_flac_read_pcm_frames_s32(ma_dr_flac* pFlac, ma_uint64 framesToRead, ma_int32* pBufferOut) +DRFLAC_API drflac_uint64 drflac_read_pcm_frames_s32(drflac* pFlac, drflac_uint64 framesToRead, drflac_int32* pBufferOut) { - ma_uint64 framesRead; - ma_uint32 unusedBitsPerSample; + drflac_uint64 framesRead; + drflac_uint32 unusedBitsPerSample; if (pFlac == NULL || framesToRead == 0) { return 0; } if (pBufferOut == NULL) { - return ma_dr_flac__seek_forward_by_pcm_frames(pFlac, framesToRead); + return drflac__seek_forward_by_pcm_frames(pFlac, framesToRead); } - MA_DR_FLAC_ASSERT(pFlac->bitsPerSample <= 32); + DRFLAC_ASSERT(pFlac->bitsPerSample <= 32); unusedBitsPerSample = 32 - pFlac->bitsPerSample; framesRead = 0; while (framesToRead > 0) { if (pFlac->currentFLACFrame.pcmFramesRemaining == 0) { - if (!ma_dr_flac__read_and_decode_next_flac_frame(pFlac)) { + if (!drflac__read_and_decode_next_flac_frame(pFlac)) { break; } } else { - unsigned int channelCount = ma_dr_flac__get_channel_count_from_channel_assignment(pFlac->currentFLACFrame.header.channelAssignment); - ma_uint64 iFirstPCMFrame = pFlac->currentFLACFrame.header.blockSizeInPCMFrames - pFlac->currentFLACFrame.pcmFramesRemaining; - ma_uint64 frameCountThisIteration = framesToRead; + unsigned int channelCount = drflac__get_channel_count_from_channel_assignment(pFlac->currentFLACFrame.header.channelAssignment); + drflac_uint64 iFirstPCMFrame = pFlac->currentFLACFrame.header.blockSizeInPCMFrames - pFlac->currentFLACFrame.pcmFramesRemaining; + drflac_uint64 frameCountThisIteration = framesToRead; if (frameCountThisIteration > pFlac->currentFLACFrame.pcmFramesRemaining) { frameCountThisIteration = pFlac->currentFLACFrame.pcmFramesRemaining; } if (channelCount == 2) { - const ma_int32* pDecodedSamples0 = pFlac->currentFLACFrame.subframes[0].pSamplesS32 + iFirstPCMFrame; - const ma_int32* pDecodedSamples1 = pFlac->currentFLACFrame.subframes[1].pSamplesS32 + iFirstPCMFrame; + const drflac_int32* pDecodedSamples0 = pFlac->currentFLACFrame.subframes[0].pSamplesS32 + iFirstPCMFrame; + const drflac_int32* pDecodedSamples1 = pFlac->currentFLACFrame.subframes[1].pSamplesS32 + iFirstPCMFrame; switch (pFlac->currentFLACFrame.header.channelAssignment) { - case MA_DR_FLAC_CHANNEL_ASSIGNMENT_LEFT_SIDE: + case DRFLAC_CHANNEL_ASSIGNMENT_LEFT_SIDE: { - ma_dr_flac_read_pcm_frames_s32__decode_left_side(pFlac, frameCountThisIteration, unusedBitsPerSample, pDecodedSamples0, pDecodedSamples1, pBufferOut); + drflac_read_pcm_frames_s32__decode_left_side(pFlac, frameCountThisIteration, unusedBitsPerSample, pDecodedSamples0, pDecodedSamples1, pBufferOut); } break; - case MA_DR_FLAC_CHANNEL_ASSIGNMENT_RIGHT_SIDE: + case DRFLAC_CHANNEL_ASSIGNMENT_RIGHT_SIDE: { - ma_dr_flac_read_pcm_frames_s32__decode_right_side(pFlac, frameCountThisIteration, unusedBitsPerSample, pDecodedSamples0, pDecodedSamples1, pBufferOut); + drflac_read_pcm_frames_s32__decode_right_side(pFlac, frameCountThisIteration, unusedBitsPerSample, pDecodedSamples0, pDecodedSamples1, pBufferOut); } break; - case MA_DR_FLAC_CHANNEL_ASSIGNMENT_MID_SIDE: + case DRFLAC_CHANNEL_ASSIGNMENT_MID_SIDE: { - ma_dr_flac_read_pcm_frames_s32__decode_mid_side(pFlac, frameCountThisIteration, unusedBitsPerSample, pDecodedSamples0, pDecodedSamples1, pBufferOut); + drflac_read_pcm_frames_s32__decode_mid_side(pFlac, frameCountThisIteration, unusedBitsPerSample, pDecodedSamples0, pDecodedSamples1, pBufferOut); } break; - case MA_DR_FLAC_CHANNEL_ASSIGNMENT_INDEPENDENT: + case DRFLAC_CHANNEL_ASSIGNMENT_INDEPENDENT: default: { - ma_dr_flac_read_pcm_frames_s32__decode_independent_stereo(pFlac, frameCountThisIteration, unusedBitsPerSample, pDecodedSamples0, pDecodedSamples1, pBufferOut); + drflac_read_pcm_frames_s32__decode_independent_stereo(pFlac, frameCountThisIteration, unusedBitsPerSample, pDecodedSamples0, pDecodedSamples1, pBufferOut); } break; } } else { - ma_uint64 i; + drflac_uint64 i; for (i = 0; i < frameCountThisIteration; ++i) { unsigned int j; for (j = 0; j < channelCount; ++j) { - pBufferOut[(i*channelCount)+j] = (ma_int32)((ma_uint32)(pFlac->currentFLACFrame.subframes[j].pSamplesS32[iFirstPCMFrame + i]) << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[j].wastedBitsPerSample)); + pBufferOut[(i*channelCount)+j] = (drflac_int32)((drflac_uint32)(pFlac->currentFLACFrame.subframes[j].pSamplesS32[iFirstPCMFrame + i]) << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[j].wastedBitsPerSample)); } } } @@ -88803,47 +88189,47 @@ MA_API ma_uint64 ma_dr_flac_read_pcm_frames_s32(ma_dr_flac* pFlac, ma_uint64 fra pBufferOut += frameCountThisIteration * channelCount; framesToRead -= frameCountThisIteration; pFlac->currentPCMFrame += frameCountThisIteration; - pFlac->currentFLACFrame.pcmFramesRemaining -= (ma_uint32)frameCountThisIteration; + pFlac->currentFLACFrame.pcmFramesRemaining -= (drflac_uint32)frameCountThisIteration; } } return framesRead; } #if 0 -static MA_INLINE void ma_dr_flac_read_pcm_frames_s16__decode_left_side__reference(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, ma_int16* pOutputSamples) +static DRFLAC_INLINE void drflac_read_pcm_frames_s16__decode_left_side__reference(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int16* pOutputSamples) { - ma_uint64 i; + drflac_uint64 i; for (i = 0; i < frameCount; ++i) { - ma_uint32 left = (ma_uint32)pInputSamples0[i] << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample); - ma_uint32 side = (ma_uint32)pInputSamples1[i] << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample); - ma_uint32 right = left - side; + drflac_uint32 left = (drflac_uint32)pInputSamples0[i] << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample); + drflac_uint32 side = (drflac_uint32)pInputSamples1[i] << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample); + drflac_uint32 right = left - side; left >>= 16; right >>= 16; - pOutputSamples[i*2+0] = (ma_int16)left; - pOutputSamples[i*2+1] = (ma_int16)right; + pOutputSamples[i*2+0] = (drflac_int16)left; + pOutputSamples[i*2+1] = (drflac_int16)right; } } #endif -static MA_INLINE void ma_dr_flac_read_pcm_frames_s16__decode_left_side__scalar(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, ma_int16* pOutputSamples) +static DRFLAC_INLINE void drflac_read_pcm_frames_s16__decode_left_side__scalar(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int16* pOutputSamples) { - ma_uint64 i; - ma_uint64 frameCount4 = frameCount >> 2; - const ma_uint32* pInputSamples0U32 = (const ma_uint32*)pInputSamples0; - const ma_uint32* pInputSamples1U32 = (const ma_uint32*)pInputSamples1; - ma_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; - ma_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; + drflac_uint64 i; + drflac_uint64 frameCount4 = frameCount >> 2; + const drflac_uint32* pInputSamples0U32 = (const drflac_uint32*)pInputSamples0; + const drflac_uint32* pInputSamples1U32 = (const drflac_uint32*)pInputSamples1; + drflac_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; + drflac_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; for (i = 0; i < frameCount4; ++i) { - ma_uint32 left0 = pInputSamples0U32[i*4+0] << shift0; - ma_uint32 left1 = pInputSamples0U32[i*4+1] << shift0; - ma_uint32 left2 = pInputSamples0U32[i*4+2] << shift0; - ma_uint32 left3 = pInputSamples0U32[i*4+3] << shift0; - ma_uint32 side0 = pInputSamples1U32[i*4+0] << shift1; - ma_uint32 side1 = pInputSamples1U32[i*4+1] << shift1; - ma_uint32 side2 = pInputSamples1U32[i*4+2] << shift1; - ma_uint32 side3 = pInputSamples1U32[i*4+3] << shift1; - ma_uint32 right0 = left0 - side0; - ma_uint32 right1 = left1 - side1; - ma_uint32 right2 = left2 - side2; - ma_uint32 right3 = left3 - side3; + drflac_uint32 left0 = pInputSamples0U32[i*4+0] << shift0; + drflac_uint32 left1 = pInputSamples0U32[i*4+1] << shift0; + drflac_uint32 left2 = pInputSamples0U32[i*4+2] << shift0; + drflac_uint32 left3 = pInputSamples0U32[i*4+3] << shift0; + drflac_uint32 side0 = pInputSamples1U32[i*4+0] << shift1; + drflac_uint32 side1 = pInputSamples1U32[i*4+1] << shift1; + drflac_uint32 side2 = pInputSamples1U32[i*4+2] << shift1; + drflac_uint32 side3 = pInputSamples1U32[i*4+3] << shift1; + drflac_uint32 right0 = left0 - side0; + drflac_uint32 right1 = left1 - side1; + drflac_uint32 right2 = left2 - side2; + drflac_uint32 right3 = left3 - side3; left0 >>= 16; left1 >>= 16; left2 >>= 16; @@ -88852,66 +88238,66 @@ static MA_INLINE void ma_dr_flac_read_pcm_frames_s16__decode_left_side__scalar(m right1 >>= 16; right2 >>= 16; right3 >>= 16; - pOutputSamples[i*8+0] = (ma_int16)left0; - pOutputSamples[i*8+1] = (ma_int16)right0; - pOutputSamples[i*8+2] = (ma_int16)left1; - pOutputSamples[i*8+3] = (ma_int16)right1; - pOutputSamples[i*8+4] = (ma_int16)left2; - pOutputSamples[i*8+5] = (ma_int16)right2; - pOutputSamples[i*8+6] = (ma_int16)left3; - pOutputSamples[i*8+7] = (ma_int16)right3; + pOutputSamples[i*8+0] = (drflac_int16)left0; + pOutputSamples[i*8+1] = (drflac_int16)right0; + pOutputSamples[i*8+2] = (drflac_int16)left1; + pOutputSamples[i*8+3] = (drflac_int16)right1; + pOutputSamples[i*8+4] = (drflac_int16)left2; + pOutputSamples[i*8+5] = (drflac_int16)right2; + pOutputSamples[i*8+6] = (drflac_int16)left3; + pOutputSamples[i*8+7] = (drflac_int16)right3; } for (i = (frameCount4 << 2); i < frameCount; ++i) { - ma_uint32 left = pInputSamples0U32[i] << shift0; - ma_uint32 side = pInputSamples1U32[i] << shift1; - ma_uint32 right = left - side; + drflac_uint32 left = pInputSamples0U32[i] << shift0; + drflac_uint32 side = pInputSamples1U32[i] << shift1; + drflac_uint32 right = left - side; left >>= 16; right >>= 16; - pOutputSamples[i*2+0] = (ma_int16)left; - pOutputSamples[i*2+1] = (ma_int16)right; + pOutputSamples[i*2+0] = (drflac_int16)left; + pOutputSamples[i*2+1] = (drflac_int16)right; } } -#if defined(MA_DR_FLAC_SUPPORT_SSE2) -static MA_INLINE void ma_dr_flac_read_pcm_frames_s16__decode_left_side__sse2(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, ma_int16* pOutputSamples) +#if defined(DRFLAC_SUPPORT_SSE2) +static DRFLAC_INLINE void drflac_read_pcm_frames_s16__decode_left_side__sse2(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int16* pOutputSamples) { - ma_uint64 i; - ma_uint64 frameCount4 = frameCount >> 2; - const ma_uint32* pInputSamples0U32 = (const ma_uint32*)pInputSamples0; - const ma_uint32* pInputSamples1U32 = (const ma_uint32*)pInputSamples1; - ma_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; - ma_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; - MA_DR_FLAC_ASSERT(pFlac->bitsPerSample <= 24); + drflac_uint64 i; + drflac_uint64 frameCount4 = frameCount >> 2; + const drflac_uint32* pInputSamples0U32 = (const drflac_uint32*)pInputSamples0; + const drflac_uint32* pInputSamples1U32 = (const drflac_uint32*)pInputSamples1; + drflac_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; + drflac_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; + DRFLAC_ASSERT(pFlac->bitsPerSample <= 24); for (i = 0; i < frameCount4; ++i) { __m128i left = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples0 + i), shift0); __m128i side = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples1 + i), shift1); __m128i right = _mm_sub_epi32(left, side); left = _mm_srai_epi32(left, 16); right = _mm_srai_epi32(right, 16); - _mm_storeu_si128((__m128i*)(pOutputSamples + i*8), ma_dr_flac__mm_packs_interleaved_epi32(left, right)); + _mm_storeu_si128((__m128i*)(pOutputSamples + i*8), drflac__mm_packs_interleaved_epi32(left, right)); } for (i = (frameCount4 << 2); i < frameCount; ++i) { - ma_uint32 left = pInputSamples0U32[i] << shift0; - ma_uint32 side = pInputSamples1U32[i] << shift1; - ma_uint32 right = left - side; + drflac_uint32 left = pInputSamples0U32[i] << shift0; + drflac_uint32 side = pInputSamples1U32[i] << shift1; + drflac_uint32 right = left - side; left >>= 16; right >>= 16; - pOutputSamples[i*2+0] = (ma_int16)left; - pOutputSamples[i*2+1] = (ma_int16)right; + pOutputSamples[i*2+0] = (drflac_int16)left; + pOutputSamples[i*2+1] = (drflac_int16)right; } } #endif -#if defined(MA_DR_FLAC_SUPPORT_NEON) -static MA_INLINE void ma_dr_flac_read_pcm_frames_s16__decode_left_side__neon(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, ma_int16* pOutputSamples) +#if defined(DRFLAC_SUPPORT_NEON) +static DRFLAC_INLINE void drflac_read_pcm_frames_s16__decode_left_side__neon(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int16* pOutputSamples) { - ma_uint64 i; - ma_uint64 frameCount4 = frameCount >> 2; - const ma_uint32* pInputSamples0U32 = (const ma_uint32*)pInputSamples0; - const ma_uint32* pInputSamples1U32 = (const ma_uint32*)pInputSamples1; - ma_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; - ma_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; + drflac_uint64 i; + drflac_uint64 frameCount4 = frameCount >> 2; + const drflac_uint32* pInputSamples0U32 = (const drflac_uint32*)pInputSamples0; + const drflac_uint32* pInputSamples1U32 = (const drflac_uint32*)pInputSamples1; + drflac_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; + drflac_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; int32x4_t shift0_4; int32x4_t shift1_4; - MA_DR_FLAC_ASSERT(pFlac->bitsPerSample <= 24); + DRFLAC_ASSERT(pFlac->bitsPerSample <= 24); shift0_4 = vdupq_n_s32(shift0); shift1_4 = vdupq_n_s32(shift1); for (i = 0; i < frameCount4; ++i) { @@ -88923,74 +88309,74 @@ static MA_INLINE void ma_dr_flac_read_pcm_frames_s16__decode_left_side__neon(ma_ right = vsubq_u32(left, side); left = vshrq_n_u32(left, 16); right = vshrq_n_u32(right, 16); - ma_dr_flac__vst2q_u16((ma_uint16*)pOutputSamples + i*8, vzip_u16(vmovn_u32(left), vmovn_u32(right))); + drflac__vst2q_u16((drflac_uint16*)pOutputSamples + i*8, vzip_u16(vmovn_u32(left), vmovn_u32(right))); } for (i = (frameCount4 << 2); i < frameCount; ++i) { - ma_uint32 left = pInputSamples0U32[i] << shift0; - ma_uint32 side = pInputSamples1U32[i] << shift1; - ma_uint32 right = left - side; + drflac_uint32 left = pInputSamples0U32[i] << shift0; + drflac_uint32 side = pInputSamples1U32[i] << shift1; + drflac_uint32 right = left - side; left >>= 16; right >>= 16; - pOutputSamples[i*2+0] = (ma_int16)left; - pOutputSamples[i*2+1] = (ma_int16)right; + pOutputSamples[i*2+0] = (drflac_int16)left; + pOutputSamples[i*2+1] = (drflac_int16)right; } } #endif -static MA_INLINE void ma_dr_flac_read_pcm_frames_s16__decode_left_side(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, ma_int16* pOutputSamples) +static DRFLAC_INLINE void drflac_read_pcm_frames_s16__decode_left_side(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int16* pOutputSamples) { -#if defined(MA_DR_FLAC_SUPPORT_SSE2) - if (ma_dr_flac__gIsSSE2Supported && pFlac->bitsPerSample <= 24) { - ma_dr_flac_read_pcm_frames_s16__decode_left_side__sse2(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); +#if defined(DRFLAC_SUPPORT_SSE2) + if (drflac__gIsSSE2Supported && pFlac->bitsPerSample <= 24) { + drflac_read_pcm_frames_s16__decode_left_side__sse2(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); } else -#elif defined(MA_DR_FLAC_SUPPORT_NEON) - if (ma_dr_flac__gIsNEONSupported && pFlac->bitsPerSample <= 24) { - ma_dr_flac_read_pcm_frames_s16__decode_left_side__neon(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); +#elif defined(DRFLAC_SUPPORT_NEON) + if (drflac__gIsNEONSupported && pFlac->bitsPerSample <= 24) { + drflac_read_pcm_frames_s16__decode_left_side__neon(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); } else #endif { #if 0 - ma_dr_flac_read_pcm_frames_s16__decode_left_side__reference(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); + drflac_read_pcm_frames_s16__decode_left_side__reference(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); #else - ma_dr_flac_read_pcm_frames_s16__decode_left_side__scalar(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); + drflac_read_pcm_frames_s16__decode_left_side__scalar(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); #endif } } #if 0 -static MA_INLINE void ma_dr_flac_read_pcm_frames_s16__decode_right_side__reference(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, ma_int16* pOutputSamples) +static DRFLAC_INLINE void drflac_read_pcm_frames_s16__decode_right_side__reference(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int16* pOutputSamples) { - ma_uint64 i; + drflac_uint64 i; for (i = 0; i < frameCount; ++i) { - ma_uint32 side = (ma_uint32)pInputSamples0[i] << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample); - ma_uint32 right = (ma_uint32)pInputSamples1[i] << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample); - ma_uint32 left = right + side; + drflac_uint32 side = (drflac_uint32)pInputSamples0[i] << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample); + drflac_uint32 right = (drflac_uint32)pInputSamples1[i] << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample); + drflac_uint32 left = right + side; left >>= 16; right >>= 16; - pOutputSamples[i*2+0] = (ma_int16)left; - pOutputSamples[i*2+1] = (ma_int16)right; + pOutputSamples[i*2+0] = (drflac_int16)left; + pOutputSamples[i*2+1] = (drflac_int16)right; } } #endif -static MA_INLINE void ma_dr_flac_read_pcm_frames_s16__decode_right_side__scalar(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, ma_int16* pOutputSamples) +static DRFLAC_INLINE void drflac_read_pcm_frames_s16__decode_right_side__scalar(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int16* pOutputSamples) { - ma_uint64 i; - ma_uint64 frameCount4 = frameCount >> 2; - const ma_uint32* pInputSamples0U32 = (const ma_uint32*)pInputSamples0; - const ma_uint32* pInputSamples1U32 = (const ma_uint32*)pInputSamples1; - ma_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; - ma_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; + drflac_uint64 i; + drflac_uint64 frameCount4 = frameCount >> 2; + const drflac_uint32* pInputSamples0U32 = (const drflac_uint32*)pInputSamples0; + const drflac_uint32* pInputSamples1U32 = (const drflac_uint32*)pInputSamples1; + drflac_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; + drflac_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; for (i = 0; i < frameCount4; ++i) { - ma_uint32 side0 = pInputSamples0U32[i*4+0] << shift0; - ma_uint32 side1 = pInputSamples0U32[i*4+1] << shift0; - ma_uint32 side2 = pInputSamples0U32[i*4+2] << shift0; - ma_uint32 side3 = pInputSamples0U32[i*4+3] << shift0; - ma_uint32 right0 = pInputSamples1U32[i*4+0] << shift1; - ma_uint32 right1 = pInputSamples1U32[i*4+1] << shift1; - ma_uint32 right2 = pInputSamples1U32[i*4+2] << shift1; - ma_uint32 right3 = pInputSamples1U32[i*4+3] << shift1; - ma_uint32 left0 = right0 + side0; - ma_uint32 left1 = right1 + side1; - ma_uint32 left2 = right2 + side2; - ma_uint32 left3 = right3 + side3; + drflac_uint32 side0 = pInputSamples0U32[i*4+0] << shift0; + drflac_uint32 side1 = pInputSamples0U32[i*4+1] << shift0; + drflac_uint32 side2 = pInputSamples0U32[i*4+2] << shift0; + drflac_uint32 side3 = pInputSamples0U32[i*4+3] << shift0; + drflac_uint32 right0 = pInputSamples1U32[i*4+0] << shift1; + drflac_uint32 right1 = pInputSamples1U32[i*4+1] << shift1; + drflac_uint32 right2 = pInputSamples1U32[i*4+2] << shift1; + drflac_uint32 right3 = pInputSamples1U32[i*4+3] << shift1; + drflac_uint32 left0 = right0 + side0; + drflac_uint32 left1 = right1 + side1; + drflac_uint32 left2 = right2 + side2; + drflac_uint32 left3 = right3 + side3; left0 >>= 16; left1 >>= 16; left2 >>= 16; @@ -88999,66 +88385,66 @@ static MA_INLINE void ma_dr_flac_read_pcm_frames_s16__decode_right_side__scalar( right1 >>= 16; right2 >>= 16; right3 >>= 16; - pOutputSamples[i*8+0] = (ma_int16)left0; - pOutputSamples[i*8+1] = (ma_int16)right0; - pOutputSamples[i*8+2] = (ma_int16)left1; - pOutputSamples[i*8+3] = (ma_int16)right1; - pOutputSamples[i*8+4] = (ma_int16)left2; - pOutputSamples[i*8+5] = (ma_int16)right2; - pOutputSamples[i*8+6] = (ma_int16)left3; - pOutputSamples[i*8+7] = (ma_int16)right3; + pOutputSamples[i*8+0] = (drflac_int16)left0; + pOutputSamples[i*8+1] = (drflac_int16)right0; + pOutputSamples[i*8+2] = (drflac_int16)left1; + pOutputSamples[i*8+3] = (drflac_int16)right1; + pOutputSamples[i*8+4] = (drflac_int16)left2; + pOutputSamples[i*8+5] = (drflac_int16)right2; + pOutputSamples[i*8+6] = (drflac_int16)left3; + pOutputSamples[i*8+7] = (drflac_int16)right3; } for (i = (frameCount4 << 2); i < frameCount; ++i) { - ma_uint32 side = pInputSamples0U32[i] << shift0; - ma_uint32 right = pInputSamples1U32[i] << shift1; - ma_uint32 left = right + side; + drflac_uint32 side = pInputSamples0U32[i] << shift0; + drflac_uint32 right = pInputSamples1U32[i] << shift1; + drflac_uint32 left = right + side; left >>= 16; right >>= 16; - pOutputSamples[i*2+0] = (ma_int16)left; - pOutputSamples[i*2+1] = (ma_int16)right; + pOutputSamples[i*2+0] = (drflac_int16)left; + pOutputSamples[i*2+1] = (drflac_int16)right; } } -#if defined(MA_DR_FLAC_SUPPORT_SSE2) -static MA_INLINE void ma_dr_flac_read_pcm_frames_s16__decode_right_side__sse2(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, ma_int16* pOutputSamples) +#if defined(DRFLAC_SUPPORT_SSE2) +static DRFLAC_INLINE void drflac_read_pcm_frames_s16__decode_right_side__sse2(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int16* pOutputSamples) { - ma_uint64 i; - ma_uint64 frameCount4 = frameCount >> 2; - const ma_uint32* pInputSamples0U32 = (const ma_uint32*)pInputSamples0; - const ma_uint32* pInputSamples1U32 = (const ma_uint32*)pInputSamples1; - ma_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; - ma_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; - MA_DR_FLAC_ASSERT(pFlac->bitsPerSample <= 24); + drflac_uint64 i; + drflac_uint64 frameCount4 = frameCount >> 2; + const drflac_uint32* pInputSamples0U32 = (const drflac_uint32*)pInputSamples0; + const drflac_uint32* pInputSamples1U32 = (const drflac_uint32*)pInputSamples1; + drflac_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; + drflac_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; + DRFLAC_ASSERT(pFlac->bitsPerSample <= 24); for (i = 0; i < frameCount4; ++i) { __m128i side = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples0 + i), shift0); __m128i right = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples1 + i), shift1); __m128i left = _mm_add_epi32(right, side); left = _mm_srai_epi32(left, 16); right = _mm_srai_epi32(right, 16); - _mm_storeu_si128((__m128i*)(pOutputSamples + i*8), ma_dr_flac__mm_packs_interleaved_epi32(left, right)); + _mm_storeu_si128((__m128i*)(pOutputSamples + i*8), drflac__mm_packs_interleaved_epi32(left, right)); } for (i = (frameCount4 << 2); i < frameCount; ++i) { - ma_uint32 side = pInputSamples0U32[i] << shift0; - ma_uint32 right = pInputSamples1U32[i] << shift1; - ma_uint32 left = right + side; + drflac_uint32 side = pInputSamples0U32[i] << shift0; + drflac_uint32 right = pInputSamples1U32[i] << shift1; + drflac_uint32 left = right + side; left >>= 16; right >>= 16; - pOutputSamples[i*2+0] = (ma_int16)left; - pOutputSamples[i*2+1] = (ma_int16)right; + pOutputSamples[i*2+0] = (drflac_int16)left; + pOutputSamples[i*2+1] = (drflac_int16)right; } } #endif -#if defined(MA_DR_FLAC_SUPPORT_NEON) -static MA_INLINE void ma_dr_flac_read_pcm_frames_s16__decode_right_side__neon(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, ma_int16* pOutputSamples) +#if defined(DRFLAC_SUPPORT_NEON) +static DRFLAC_INLINE void drflac_read_pcm_frames_s16__decode_right_side__neon(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int16* pOutputSamples) { - ma_uint64 i; - ma_uint64 frameCount4 = frameCount >> 2; - const ma_uint32* pInputSamples0U32 = (const ma_uint32*)pInputSamples0; - const ma_uint32* pInputSamples1U32 = (const ma_uint32*)pInputSamples1; - ma_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; - ma_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; + drflac_uint64 i; + drflac_uint64 frameCount4 = frameCount >> 2; + const drflac_uint32* pInputSamples0U32 = (const drflac_uint32*)pInputSamples0; + const drflac_uint32* pInputSamples1U32 = (const drflac_uint32*)pInputSamples1; + drflac_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; + drflac_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; int32x4_t shift0_4; int32x4_t shift1_4; - MA_DR_FLAC_ASSERT(pFlac->bitsPerSample <= 24); + DRFLAC_ASSERT(pFlac->bitsPerSample <= 24); shift0_4 = vdupq_n_s32(shift0); shift1_4 = vdupq_n_s32(shift1); for (i = 0; i < frameCount4; ++i) { @@ -89070,76 +88456,76 @@ static MA_INLINE void ma_dr_flac_read_pcm_frames_s16__decode_right_side__neon(ma left = vaddq_u32(right, side); left = vshrq_n_u32(left, 16); right = vshrq_n_u32(right, 16); - ma_dr_flac__vst2q_u16((ma_uint16*)pOutputSamples + i*8, vzip_u16(vmovn_u32(left), vmovn_u32(right))); + drflac__vst2q_u16((drflac_uint16*)pOutputSamples + i*8, vzip_u16(vmovn_u32(left), vmovn_u32(right))); } for (i = (frameCount4 << 2); i < frameCount; ++i) { - ma_uint32 side = pInputSamples0U32[i] << shift0; - ma_uint32 right = pInputSamples1U32[i] << shift1; - ma_uint32 left = right + side; + drflac_uint32 side = pInputSamples0U32[i] << shift0; + drflac_uint32 right = pInputSamples1U32[i] << shift1; + drflac_uint32 left = right + side; left >>= 16; right >>= 16; - pOutputSamples[i*2+0] = (ma_int16)left; - pOutputSamples[i*2+1] = (ma_int16)right; + pOutputSamples[i*2+0] = (drflac_int16)left; + pOutputSamples[i*2+1] = (drflac_int16)right; } } #endif -static MA_INLINE void ma_dr_flac_read_pcm_frames_s16__decode_right_side(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, ma_int16* pOutputSamples) +static DRFLAC_INLINE void drflac_read_pcm_frames_s16__decode_right_side(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int16* pOutputSamples) { -#if defined(MA_DR_FLAC_SUPPORT_SSE2) - if (ma_dr_flac__gIsSSE2Supported && pFlac->bitsPerSample <= 24) { - ma_dr_flac_read_pcm_frames_s16__decode_right_side__sse2(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); +#if defined(DRFLAC_SUPPORT_SSE2) + if (drflac__gIsSSE2Supported && pFlac->bitsPerSample <= 24) { + drflac_read_pcm_frames_s16__decode_right_side__sse2(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); } else -#elif defined(MA_DR_FLAC_SUPPORT_NEON) - if (ma_dr_flac__gIsNEONSupported && pFlac->bitsPerSample <= 24) { - ma_dr_flac_read_pcm_frames_s16__decode_right_side__neon(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); +#elif defined(DRFLAC_SUPPORT_NEON) + if (drflac__gIsNEONSupported && pFlac->bitsPerSample <= 24) { + drflac_read_pcm_frames_s16__decode_right_side__neon(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); } else #endif { #if 0 - ma_dr_flac_read_pcm_frames_s16__decode_right_side__reference(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); + drflac_read_pcm_frames_s16__decode_right_side__reference(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); #else - ma_dr_flac_read_pcm_frames_s16__decode_right_side__scalar(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); + drflac_read_pcm_frames_s16__decode_right_side__scalar(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); #endif } } #if 0 -static MA_INLINE void ma_dr_flac_read_pcm_frames_s16__decode_mid_side__reference(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, ma_int16* pOutputSamples) +static DRFLAC_INLINE void drflac_read_pcm_frames_s16__decode_mid_side__reference(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int16* pOutputSamples) { - for (ma_uint64 i = 0; i < frameCount; ++i) { - ma_uint32 mid = (ma_uint32)pInputSamples0[i] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; - ma_uint32 side = (ma_uint32)pInputSamples1[i] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; + for (drflac_uint64 i = 0; i < frameCount; ++i) { + drflac_uint32 mid = (drflac_uint32)pInputSamples0[i] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; + drflac_uint32 side = (drflac_uint32)pInputSamples1[i] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; mid = (mid << 1) | (side & 0x01); - pOutputSamples[i*2+0] = (ma_int16)(((ma_uint32)((ma_int32)(mid + side) >> 1) << unusedBitsPerSample) >> 16); - pOutputSamples[i*2+1] = (ma_int16)(((ma_uint32)((ma_int32)(mid - side) >> 1) << unusedBitsPerSample) >> 16); + pOutputSamples[i*2+0] = (drflac_int16)(((drflac_uint32)((drflac_int32)(mid + side) >> 1) << unusedBitsPerSample) >> 16); + pOutputSamples[i*2+1] = (drflac_int16)(((drflac_uint32)((drflac_int32)(mid - side) >> 1) << unusedBitsPerSample) >> 16); } } #endif -static MA_INLINE void ma_dr_flac_read_pcm_frames_s16__decode_mid_side__scalar(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, ma_int16* pOutputSamples) +static DRFLAC_INLINE void drflac_read_pcm_frames_s16__decode_mid_side__scalar(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int16* pOutputSamples) { - ma_uint64 i; - ma_uint64 frameCount4 = frameCount >> 2; - const ma_uint32* pInputSamples0U32 = (const ma_uint32*)pInputSamples0; - const ma_uint32* pInputSamples1U32 = (const ma_uint32*)pInputSamples1; - ma_uint32 shift = unusedBitsPerSample; + drflac_uint64 i; + drflac_uint64 frameCount4 = frameCount >> 2; + const drflac_uint32* pInputSamples0U32 = (const drflac_uint32*)pInputSamples0; + const drflac_uint32* pInputSamples1U32 = (const drflac_uint32*)pInputSamples1; + drflac_uint32 shift = unusedBitsPerSample; if (shift > 0) { shift -= 1; for (i = 0; i < frameCount4; ++i) { - ma_uint32 temp0L; - ma_uint32 temp1L; - ma_uint32 temp2L; - ma_uint32 temp3L; - ma_uint32 temp0R; - ma_uint32 temp1R; - ma_uint32 temp2R; - ma_uint32 temp3R; - ma_uint32 mid0 = pInputSamples0U32[i*4+0] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; - ma_uint32 mid1 = pInputSamples0U32[i*4+1] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; - ma_uint32 mid2 = pInputSamples0U32[i*4+2] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; - ma_uint32 mid3 = pInputSamples0U32[i*4+3] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; - ma_uint32 side0 = pInputSamples1U32[i*4+0] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; - ma_uint32 side1 = pInputSamples1U32[i*4+1] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; - ma_uint32 side2 = pInputSamples1U32[i*4+2] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; - ma_uint32 side3 = pInputSamples1U32[i*4+3] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; + drflac_uint32 temp0L; + drflac_uint32 temp1L; + drflac_uint32 temp2L; + drflac_uint32 temp3L; + drflac_uint32 temp0R; + drflac_uint32 temp1R; + drflac_uint32 temp2R; + drflac_uint32 temp3R; + drflac_uint32 mid0 = pInputSamples0U32[i*4+0] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; + drflac_uint32 mid1 = pInputSamples0U32[i*4+1] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; + drflac_uint32 mid2 = pInputSamples0U32[i*4+2] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; + drflac_uint32 mid3 = pInputSamples0U32[i*4+3] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; + drflac_uint32 side0 = pInputSamples1U32[i*4+0] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; + drflac_uint32 side1 = pInputSamples1U32[i*4+1] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; + drflac_uint32 side2 = pInputSamples1U32[i*4+2] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; + drflac_uint32 side3 = pInputSamples1U32[i*4+3] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; mid0 = (mid0 << 1) | (side0 & 0x01); mid1 = (mid1 << 1) | (side1 & 0x01); mid2 = (mid2 << 1) | (side2 & 0x01); @@ -89160,45 +88546,45 @@ static MA_INLINE void ma_dr_flac_read_pcm_frames_s16__decode_mid_side__scalar(ma temp1R >>= 16; temp2R >>= 16; temp3R >>= 16; - pOutputSamples[i*8+0] = (ma_int16)temp0L; - pOutputSamples[i*8+1] = (ma_int16)temp0R; - pOutputSamples[i*8+2] = (ma_int16)temp1L; - pOutputSamples[i*8+3] = (ma_int16)temp1R; - pOutputSamples[i*8+4] = (ma_int16)temp2L; - pOutputSamples[i*8+5] = (ma_int16)temp2R; - pOutputSamples[i*8+6] = (ma_int16)temp3L; - pOutputSamples[i*8+7] = (ma_int16)temp3R; + pOutputSamples[i*8+0] = (drflac_int16)temp0L; + pOutputSamples[i*8+1] = (drflac_int16)temp0R; + pOutputSamples[i*8+2] = (drflac_int16)temp1L; + pOutputSamples[i*8+3] = (drflac_int16)temp1R; + pOutputSamples[i*8+4] = (drflac_int16)temp2L; + pOutputSamples[i*8+5] = (drflac_int16)temp2R; + pOutputSamples[i*8+6] = (drflac_int16)temp3L; + pOutputSamples[i*8+7] = (drflac_int16)temp3R; } } else { for (i = 0; i < frameCount4; ++i) { - ma_uint32 temp0L; - ma_uint32 temp1L; - ma_uint32 temp2L; - ma_uint32 temp3L; - ma_uint32 temp0R; - ma_uint32 temp1R; - ma_uint32 temp2R; - ma_uint32 temp3R; - ma_uint32 mid0 = pInputSamples0U32[i*4+0] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; - ma_uint32 mid1 = pInputSamples0U32[i*4+1] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; - ma_uint32 mid2 = pInputSamples0U32[i*4+2] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; - ma_uint32 mid3 = pInputSamples0U32[i*4+3] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; - ma_uint32 side0 = pInputSamples1U32[i*4+0] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; - ma_uint32 side1 = pInputSamples1U32[i*4+1] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; - ma_uint32 side2 = pInputSamples1U32[i*4+2] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; - ma_uint32 side3 = pInputSamples1U32[i*4+3] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; + drflac_uint32 temp0L; + drflac_uint32 temp1L; + drflac_uint32 temp2L; + drflac_uint32 temp3L; + drflac_uint32 temp0R; + drflac_uint32 temp1R; + drflac_uint32 temp2R; + drflac_uint32 temp3R; + drflac_uint32 mid0 = pInputSamples0U32[i*4+0] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; + drflac_uint32 mid1 = pInputSamples0U32[i*4+1] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; + drflac_uint32 mid2 = pInputSamples0U32[i*4+2] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; + drflac_uint32 mid3 = pInputSamples0U32[i*4+3] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; + drflac_uint32 side0 = pInputSamples1U32[i*4+0] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; + drflac_uint32 side1 = pInputSamples1U32[i*4+1] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; + drflac_uint32 side2 = pInputSamples1U32[i*4+2] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; + drflac_uint32 side3 = pInputSamples1U32[i*4+3] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; mid0 = (mid0 << 1) | (side0 & 0x01); mid1 = (mid1 << 1) | (side1 & 0x01); mid2 = (mid2 << 1) | (side2 & 0x01); mid3 = (mid3 << 1) | (side3 & 0x01); - temp0L = ((ma_int32)(mid0 + side0) >> 1); - temp1L = ((ma_int32)(mid1 + side1) >> 1); - temp2L = ((ma_int32)(mid2 + side2) >> 1); - temp3L = ((ma_int32)(mid3 + side3) >> 1); - temp0R = ((ma_int32)(mid0 - side0) >> 1); - temp1R = ((ma_int32)(mid1 - side1) >> 1); - temp2R = ((ma_int32)(mid2 - side2) >> 1); - temp3R = ((ma_int32)(mid3 - side3) >> 1); + temp0L = ((drflac_int32)(mid0 + side0) >> 1); + temp1L = ((drflac_int32)(mid1 + side1) >> 1); + temp2L = ((drflac_int32)(mid2 + side2) >> 1); + temp3L = ((drflac_int32)(mid3 + side3) >> 1); + temp0R = ((drflac_int32)(mid0 - side0) >> 1); + temp1R = ((drflac_int32)(mid1 - side1) >> 1); + temp2R = ((drflac_int32)(mid2 - side2) >> 1); + temp3R = ((drflac_int32)(mid3 - side3) >> 1); temp0L >>= 16; temp1L >>= 16; temp2L >>= 16; @@ -89207,33 +88593,33 @@ static MA_INLINE void ma_dr_flac_read_pcm_frames_s16__decode_mid_side__scalar(ma temp1R >>= 16; temp2R >>= 16; temp3R >>= 16; - pOutputSamples[i*8+0] = (ma_int16)temp0L; - pOutputSamples[i*8+1] = (ma_int16)temp0R; - pOutputSamples[i*8+2] = (ma_int16)temp1L; - pOutputSamples[i*8+3] = (ma_int16)temp1R; - pOutputSamples[i*8+4] = (ma_int16)temp2L; - pOutputSamples[i*8+5] = (ma_int16)temp2R; - pOutputSamples[i*8+6] = (ma_int16)temp3L; - pOutputSamples[i*8+7] = (ma_int16)temp3R; + pOutputSamples[i*8+0] = (drflac_int16)temp0L; + pOutputSamples[i*8+1] = (drflac_int16)temp0R; + pOutputSamples[i*8+2] = (drflac_int16)temp1L; + pOutputSamples[i*8+3] = (drflac_int16)temp1R; + pOutputSamples[i*8+4] = (drflac_int16)temp2L; + pOutputSamples[i*8+5] = (drflac_int16)temp2R; + pOutputSamples[i*8+6] = (drflac_int16)temp3L; + pOutputSamples[i*8+7] = (drflac_int16)temp3R; } } for (i = (frameCount4 << 2); i < frameCount; ++i) { - ma_uint32 mid = pInputSamples0U32[i] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; - ma_uint32 side = pInputSamples1U32[i] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; + drflac_uint32 mid = pInputSamples0U32[i] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; + drflac_uint32 side = pInputSamples1U32[i] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; mid = (mid << 1) | (side & 0x01); - pOutputSamples[i*2+0] = (ma_int16)(((ma_uint32)((ma_int32)(mid + side) >> 1) << unusedBitsPerSample) >> 16); - pOutputSamples[i*2+1] = (ma_int16)(((ma_uint32)((ma_int32)(mid - side) >> 1) << unusedBitsPerSample) >> 16); + pOutputSamples[i*2+0] = (drflac_int16)(((drflac_uint32)((drflac_int32)(mid + side) >> 1) << unusedBitsPerSample) >> 16); + pOutputSamples[i*2+1] = (drflac_int16)(((drflac_uint32)((drflac_int32)(mid - side) >> 1) << unusedBitsPerSample) >> 16); } } -#if defined(MA_DR_FLAC_SUPPORT_SSE2) -static MA_INLINE void ma_dr_flac_read_pcm_frames_s16__decode_mid_side__sse2(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, ma_int16* pOutputSamples) +#if defined(DRFLAC_SUPPORT_SSE2) +static DRFLAC_INLINE void drflac_read_pcm_frames_s16__decode_mid_side__sse2(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int16* pOutputSamples) { - ma_uint64 i; - ma_uint64 frameCount4 = frameCount >> 2; - const ma_uint32* pInputSamples0U32 = (const ma_uint32*)pInputSamples0; - const ma_uint32* pInputSamples1U32 = (const ma_uint32*)pInputSamples1; - ma_uint32 shift = unusedBitsPerSample; - MA_DR_FLAC_ASSERT(pFlac->bitsPerSample <= 24); + drflac_uint64 i; + drflac_uint64 frameCount4 = frameCount >> 2; + const drflac_uint32* pInputSamples0U32 = (const drflac_uint32*)pInputSamples0; + const drflac_uint32* pInputSamples1U32 = (const drflac_uint32*)pInputSamples1; + drflac_uint32 shift = unusedBitsPerSample; + DRFLAC_ASSERT(pFlac->bitsPerSample <= 24); if (shift == 0) { for (i = 0; i < frameCount4; ++i) { __m128i mid; @@ -89247,14 +88633,14 @@ static MA_INLINE void ma_dr_flac_read_pcm_frames_s16__decode_mid_side__sse2(ma_d right = _mm_srai_epi32(_mm_sub_epi32(mid, side), 1); left = _mm_srai_epi32(left, 16); right = _mm_srai_epi32(right, 16); - _mm_storeu_si128((__m128i*)(pOutputSamples + i*8), ma_dr_flac__mm_packs_interleaved_epi32(left, right)); + _mm_storeu_si128((__m128i*)(pOutputSamples + i*8), drflac__mm_packs_interleaved_epi32(left, right)); } for (i = (frameCount4 << 2); i < frameCount; ++i) { - ma_uint32 mid = pInputSamples0U32[i] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; - ma_uint32 side = pInputSamples1U32[i] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; + drflac_uint32 mid = pInputSamples0U32[i] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; + drflac_uint32 side = pInputSamples1U32[i] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; mid = (mid << 1) | (side & 0x01); - pOutputSamples[i*2+0] = (ma_int16)(((ma_int32)(mid + side) >> 1) >> 16); - pOutputSamples[i*2+1] = (ma_int16)(((ma_int32)(mid - side) >> 1) >> 16); + pOutputSamples[i*2+0] = (drflac_int16)(((drflac_int32)(mid + side) >> 1) >> 16); + pOutputSamples[i*2+1] = (drflac_int16)(((drflac_int32)(mid - side) >> 1) >> 16); } } else { shift -= 1; @@ -89270,29 +88656,29 @@ static MA_INLINE void ma_dr_flac_read_pcm_frames_s16__decode_mid_side__sse2(ma_d right = _mm_slli_epi32(_mm_sub_epi32(mid, side), shift); left = _mm_srai_epi32(left, 16); right = _mm_srai_epi32(right, 16); - _mm_storeu_si128((__m128i*)(pOutputSamples + i*8), ma_dr_flac__mm_packs_interleaved_epi32(left, right)); + _mm_storeu_si128((__m128i*)(pOutputSamples + i*8), drflac__mm_packs_interleaved_epi32(left, right)); } for (i = (frameCount4 << 2); i < frameCount; ++i) { - ma_uint32 mid = pInputSamples0U32[i] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; - ma_uint32 side = pInputSamples1U32[i] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; + drflac_uint32 mid = pInputSamples0U32[i] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; + drflac_uint32 side = pInputSamples1U32[i] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; mid = (mid << 1) | (side & 0x01); - pOutputSamples[i*2+0] = (ma_int16)(((mid + side) << shift) >> 16); - pOutputSamples[i*2+1] = (ma_int16)(((mid - side) << shift) >> 16); + pOutputSamples[i*2+0] = (drflac_int16)(((mid + side) << shift) >> 16); + pOutputSamples[i*2+1] = (drflac_int16)(((mid - side) << shift) >> 16); } } } #endif -#if defined(MA_DR_FLAC_SUPPORT_NEON) -static MA_INLINE void ma_dr_flac_read_pcm_frames_s16__decode_mid_side__neon(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, ma_int16* pOutputSamples) +#if defined(DRFLAC_SUPPORT_NEON) +static DRFLAC_INLINE void drflac_read_pcm_frames_s16__decode_mid_side__neon(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int16* pOutputSamples) { - ma_uint64 i; - ma_uint64 frameCount4 = frameCount >> 2; - const ma_uint32* pInputSamples0U32 = (const ma_uint32*)pInputSamples0; - const ma_uint32* pInputSamples1U32 = (const ma_uint32*)pInputSamples1; - ma_uint32 shift = unusedBitsPerSample; + drflac_uint64 i; + drflac_uint64 frameCount4 = frameCount >> 2; + const drflac_uint32* pInputSamples0U32 = (const drflac_uint32*)pInputSamples0; + const drflac_uint32* pInputSamples1U32 = (const drflac_uint32*)pInputSamples1; + drflac_uint32 shift = unusedBitsPerSample; int32x4_t wbpsShift0_4; int32x4_t wbpsShift1_4; - MA_DR_FLAC_ASSERT(pFlac->bitsPerSample <= 24); + DRFLAC_ASSERT(pFlac->bitsPerSample <= 24); wbpsShift0_4 = vdupq_n_s32(pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample); wbpsShift1_4 = vdupq_n_s32(pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample); if (shift == 0) { @@ -89308,14 +88694,14 @@ static MA_INLINE void ma_dr_flac_read_pcm_frames_s16__decode_mid_side__neon(ma_d right = vshrq_n_s32(vreinterpretq_s32_u32(vsubq_u32(mid, side)), 1); left = vshrq_n_s32(left, 16); right = vshrq_n_s32(right, 16); - ma_dr_flac__vst2q_s16(pOutputSamples + i*8, vzip_s16(vmovn_s32(left), vmovn_s32(right))); + drflac__vst2q_s16(pOutputSamples + i*8, vzip_s16(vmovn_s32(left), vmovn_s32(right))); } for (i = (frameCount4 << 2); i < frameCount; ++i) { - ma_uint32 mid = pInputSamples0U32[i] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; - ma_uint32 side = pInputSamples1U32[i] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; + drflac_uint32 mid = pInputSamples0U32[i] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; + drflac_uint32 side = pInputSamples1U32[i] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; mid = (mid << 1) | (side & 0x01); - pOutputSamples[i*2+0] = (ma_int16)(((ma_int32)(mid + side) >> 1) >> 16); - pOutputSamples[i*2+1] = (ma_int16)(((ma_int32)(mid - side) >> 1) >> 16); + pOutputSamples[i*2+0] = (drflac_int16)(((drflac_int32)(mid + side) >> 1) >> 16); + pOutputSamples[i*2+1] = (drflac_int16)(((drflac_int32)(mid - side) >> 1) >> 16); } } else { int32x4_t shift4; @@ -89333,63 +88719,63 @@ static MA_INLINE void ma_dr_flac_read_pcm_frames_s16__decode_mid_side__neon(ma_d right = vreinterpretq_s32_u32(vshlq_u32(vsubq_u32(mid, side), shift4)); left = vshrq_n_s32(left, 16); right = vshrq_n_s32(right, 16); - ma_dr_flac__vst2q_s16(pOutputSamples + i*8, vzip_s16(vmovn_s32(left), vmovn_s32(right))); + drflac__vst2q_s16(pOutputSamples + i*8, vzip_s16(vmovn_s32(left), vmovn_s32(right))); } for (i = (frameCount4 << 2); i < frameCount; ++i) { - ma_uint32 mid = pInputSamples0U32[i] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; - ma_uint32 side = pInputSamples1U32[i] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; + drflac_uint32 mid = pInputSamples0U32[i] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; + drflac_uint32 side = pInputSamples1U32[i] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; mid = (mid << 1) | (side & 0x01); - pOutputSamples[i*2+0] = (ma_int16)(((mid + side) << shift) >> 16); - pOutputSamples[i*2+1] = (ma_int16)(((mid - side) << shift) >> 16); + pOutputSamples[i*2+0] = (drflac_int16)(((mid + side) << shift) >> 16); + pOutputSamples[i*2+1] = (drflac_int16)(((mid - side) << shift) >> 16); } } } #endif -static MA_INLINE void ma_dr_flac_read_pcm_frames_s16__decode_mid_side(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, ma_int16* pOutputSamples) +static DRFLAC_INLINE void drflac_read_pcm_frames_s16__decode_mid_side(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int16* pOutputSamples) { -#if defined(MA_DR_FLAC_SUPPORT_SSE2) - if (ma_dr_flac__gIsSSE2Supported && pFlac->bitsPerSample <= 24) { - ma_dr_flac_read_pcm_frames_s16__decode_mid_side__sse2(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); +#if defined(DRFLAC_SUPPORT_SSE2) + if (drflac__gIsSSE2Supported && pFlac->bitsPerSample <= 24) { + drflac_read_pcm_frames_s16__decode_mid_side__sse2(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); } else -#elif defined(MA_DR_FLAC_SUPPORT_NEON) - if (ma_dr_flac__gIsNEONSupported && pFlac->bitsPerSample <= 24) { - ma_dr_flac_read_pcm_frames_s16__decode_mid_side__neon(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); +#elif defined(DRFLAC_SUPPORT_NEON) + if (drflac__gIsNEONSupported && pFlac->bitsPerSample <= 24) { + drflac_read_pcm_frames_s16__decode_mid_side__neon(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); } else #endif { #if 0 - ma_dr_flac_read_pcm_frames_s16__decode_mid_side__reference(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); + drflac_read_pcm_frames_s16__decode_mid_side__reference(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); #else - ma_dr_flac_read_pcm_frames_s16__decode_mid_side__scalar(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); + drflac_read_pcm_frames_s16__decode_mid_side__scalar(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); #endif } } #if 0 -static MA_INLINE void ma_dr_flac_read_pcm_frames_s16__decode_independent_stereo__reference(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, ma_int16* pOutputSamples) +static DRFLAC_INLINE void drflac_read_pcm_frames_s16__decode_independent_stereo__reference(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int16* pOutputSamples) { - for (ma_uint64 i = 0; i < frameCount; ++i) { - pOutputSamples[i*2+0] = (ma_int16)((ma_int32)((ma_uint32)pInputSamples0[i] << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample)) >> 16); - pOutputSamples[i*2+1] = (ma_int16)((ma_int32)((ma_uint32)pInputSamples1[i] << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample)) >> 16); + for (drflac_uint64 i = 0; i < frameCount; ++i) { + pOutputSamples[i*2+0] = (drflac_int16)((drflac_int32)((drflac_uint32)pInputSamples0[i] << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample)) >> 16); + pOutputSamples[i*2+1] = (drflac_int16)((drflac_int32)((drflac_uint32)pInputSamples1[i] << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample)) >> 16); } } #endif -static MA_INLINE void ma_dr_flac_read_pcm_frames_s16__decode_independent_stereo__scalar(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, ma_int16* pOutputSamples) +static DRFLAC_INLINE void drflac_read_pcm_frames_s16__decode_independent_stereo__scalar(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int16* pOutputSamples) { - ma_uint64 i; - ma_uint64 frameCount4 = frameCount >> 2; - const ma_uint32* pInputSamples0U32 = (const ma_uint32*)pInputSamples0; - const ma_uint32* pInputSamples1U32 = (const ma_uint32*)pInputSamples1; - ma_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; - ma_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; + drflac_uint64 i; + drflac_uint64 frameCount4 = frameCount >> 2; + const drflac_uint32* pInputSamples0U32 = (const drflac_uint32*)pInputSamples0; + const drflac_uint32* pInputSamples1U32 = (const drflac_uint32*)pInputSamples1; + drflac_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; + drflac_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; for (i = 0; i < frameCount4; ++i) { - ma_uint32 tempL0 = pInputSamples0U32[i*4+0] << shift0; - ma_uint32 tempL1 = pInputSamples0U32[i*4+1] << shift0; - ma_uint32 tempL2 = pInputSamples0U32[i*4+2] << shift0; - ma_uint32 tempL3 = pInputSamples0U32[i*4+3] << shift0; - ma_uint32 tempR0 = pInputSamples1U32[i*4+0] << shift1; - ma_uint32 tempR1 = pInputSamples1U32[i*4+1] << shift1; - ma_uint32 tempR2 = pInputSamples1U32[i*4+2] << shift1; - ma_uint32 tempR3 = pInputSamples1U32[i*4+3] << shift1; + drflac_uint32 tempL0 = pInputSamples0U32[i*4+0] << shift0; + drflac_uint32 tempL1 = pInputSamples0U32[i*4+1] << shift0; + drflac_uint32 tempL2 = pInputSamples0U32[i*4+2] << shift0; + drflac_uint32 tempL3 = pInputSamples0U32[i*4+3] << shift0; + drflac_uint32 tempR0 = pInputSamples1U32[i*4+0] << shift1; + drflac_uint32 tempR1 = pInputSamples1U32[i*4+1] << shift1; + drflac_uint32 tempR2 = pInputSamples1U32[i*4+2] << shift1; + drflac_uint32 tempR3 = pInputSamples1U32[i*4+3] << shift1; tempL0 >>= 16; tempL1 >>= 16; tempL2 >>= 16; @@ -89398,51 +88784,51 @@ static MA_INLINE void ma_dr_flac_read_pcm_frames_s16__decode_independent_stereo_ tempR1 >>= 16; tempR2 >>= 16; tempR3 >>= 16; - pOutputSamples[i*8+0] = (ma_int16)tempL0; - pOutputSamples[i*8+1] = (ma_int16)tempR0; - pOutputSamples[i*8+2] = (ma_int16)tempL1; - pOutputSamples[i*8+3] = (ma_int16)tempR1; - pOutputSamples[i*8+4] = (ma_int16)tempL2; - pOutputSamples[i*8+5] = (ma_int16)tempR2; - pOutputSamples[i*8+6] = (ma_int16)tempL3; - pOutputSamples[i*8+7] = (ma_int16)tempR3; + pOutputSamples[i*8+0] = (drflac_int16)tempL0; + pOutputSamples[i*8+1] = (drflac_int16)tempR0; + pOutputSamples[i*8+2] = (drflac_int16)tempL1; + pOutputSamples[i*8+3] = (drflac_int16)tempR1; + pOutputSamples[i*8+4] = (drflac_int16)tempL2; + pOutputSamples[i*8+5] = (drflac_int16)tempR2; + pOutputSamples[i*8+6] = (drflac_int16)tempL3; + pOutputSamples[i*8+7] = (drflac_int16)tempR3; } for (i = (frameCount4 << 2); i < frameCount; ++i) { - pOutputSamples[i*2+0] = (ma_int16)((pInputSamples0U32[i] << shift0) >> 16); - pOutputSamples[i*2+1] = (ma_int16)((pInputSamples1U32[i] << shift1) >> 16); + pOutputSamples[i*2+0] = (drflac_int16)((pInputSamples0U32[i] << shift0) >> 16); + pOutputSamples[i*2+1] = (drflac_int16)((pInputSamples1U32[i] << shift1) >> 16); } } -#if defined(MA_DR_FLAC_SUPPORT_SSE2) -static MA_INLINE void ma_dr_flac_read_pcm_frames_s16__decode_independent_stereo__sse2(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, ma_int16* pOutputSamples) +#if defined(DRFLAC_SUPPORT_SSE2) +static DRFLAC_INLINE void drflac_read_pcm_frames_s16__decode_independent_stereo__sse2(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int16* pOutputSamples) { - ma_uint64 i; - ma_uint64 frameCount4 = frameCount >> 2; - const ma_uint32* pInputSamples0U32 = (const ma_uint32*)pInputSamples0; - const ma_uint32* pInputSamples1U32 = (const ma_uint32*)pInputSamples1; - ma_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; - ma_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; + drflac_uint64 i; + drflac_uint64 frameCount4 = frameCount >> 2; + const drflac_uint32* pInputSamples0U32 = (const drflac_uint32*)pInputSamples0; + const drflac_uint32* pInputSamples1U32 = (const drflac_uint32*)pInputSamples1; + drflac_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; + drflac_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; for (i = 0; i < frameCount4; ++i) { __m128i left = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples0 + i), shift0); __m128i right = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples1 + i), shift1); left = _mm_srai_epi32(left, 16); right = _mm_srai_epi32(right, 16); - _mm_storeu_si128((__m128i*)(pOutputSamples + i*8), ma_dr_flac__mm_packs_interleaved_epi32(left, right)); + _mm_storeu_si128((__m128i*)(pOutputSamples + i*8), drflac__mm_packs_interleaved_epi32(left, right)); } for (i = (frameCount4 << 2); i < frameCount; ++i) { - pOutputSamples[i*2+0] = (ma_int16)((pInputSamples0U32[i] << shift0) >> 16); - pOutputSamples[i*2+1] = (ma_int16)((pInputSamples1U32[i] << shift1) >> 16); + pOutputSamples[i*2+0] = (drflac_int16)((pInputSamples0U32[i] << shift0) >> 16); + pOutputSamples[i*2+1] = (drflac_int16)((pInputSamples1U32[i] << shift1) >> 16); } } #endif -#if defined(MA_DR_FLAC_SUPPORT_NEON) -static MA_INLINE void ma_dr_flac_read_pcm_frames_s16__decode_independent_stereo__neon(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, ma_int16* pOutputSamples) +#if defined(DRFLAC_SUPPORT_NEON) +static DRFLAC_INLINE void drflac_read_pcm_frames_s16__decode_independent_stereo__neon(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int16* pOutputSamples) { - ma_uint64 i; - ma_uint64 frameCount4 = frameCount >> 2; - const ma_uint32* pInputSamples0U32 = (const ma_uint32*)pInputSamples0; - const ma_uint32* pInputSamples1U32 = (const ma_uint32*)pInputSamples1; - ma_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; - ma_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; + drflac_uint64 i; + drflac_uint64 frameCount4 = frameCount >> 2; + const drflac_uint32* pInputSamples0U32 = (const drflac_uint32*)pInputSamples0; + const drflac_uint32* pInputSamples1U32 = (const drflac_uint32*)pInputSamples1; + drflac_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; + drflac_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; int32x4_t shift0_4 = vdupq_n_s32(shift0); int32x4_t shift1_4 = vdupq_n_s32(shift1); for (i = 0; i < frameCount4; ++i) { @@ -89452,88 +88838,88 @@ static MA_INLINE void ma_dr_flac_read_pcm_frames_s16__decode_independent_stereo_ right = vreinterpretq_s32_u32(vshlq_u32(vld1q_u32(pInputSamples1U32 + i*4), shift1_4)); left = vshrq_n_s32(left, 16); right = vshrq_n_s32(right, 16); - ma_dr_flac__vst2q_s16(pOutputSamples + i*8, vzip_s16(vmovn_s32(left), vmovn_s32(right))); + drflac__vst2q_s16(pOutputSamples + i*8, vzip_s16(vmovn_s32(left), vmovn_s32(right))); } for (i = (frameCount4 << 2); i < frameCount; ++i) { - pOutputSamples[i*2+0] = (ma_int16)((pInputSamples0U32[i] << shift0) >> 16); - pOutputSamples[i*2+1] = (ma_int16)((pInputSamples1U32[i] << shift1) >> 16); + pOutputSamples[i*2+0] = (drflac_int16)((pInputSamples0U32[i] << shift0) >> 16); + pOutputSamples[i*2+1] = (drflac_int16)((pInputSamples1U32[i] << shift1) >> 16); } } #endif -static MA_INLINE void ma_dr_flac_read_pcm_frames_s16__decode_independent_stereo(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, ma_int16* pOutputSamples) +static DRFLAC_INLINE void drflac_read_pcm_frames_s16__decode_independent_stereo(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int16* pOutputSamples) { -#if defined(MA_DR_FLAC_SUPPORT_SSE2) - if (ma_dr_flac__gIsSSE2Supported && pFlac->bitsPerSample <= 24) { - ma_dr_flac_read_pcm_frames_s16__decode_independent_stereo__sse2(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); +#if defined(DRFLAC_SUPPORT_SSE2) + if (drflac__gIsSSE2Supported && pFlac->bitsPerSample <= 24) { + drflac_read_pcm_frames_s16__decode_independent_stereo__sse2(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); } else -#elif defined(MA_DR_FLAC_SUPPORT_NEON) - if (ma_dr_flac__gIsNEONSupported && pFlac->bitsPerSample <= 24) { - ma_dr_flac_read_pcm_frames_s16__decode_independent_stereo__neon(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); +#elif defined(DRFLAC_SUPPORT_NEON) + if (drflac__gIsNEONSupported && pFlac->bitsPerSample <= 24) { + drflac_read_pcm_frames_s16__decode_independent_stereo__neon(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); } else #endif { #if 0 - ma_dr_flac_read_pcm_frames_s16__decode_independent_stereo__reference(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); + drflac_read_pcm_frames_s16__decode_independent_stereo__reference(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); #else - ma_dr_flac_read_pcm_frames_s16__decode_independent_stereo__scalar(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); + drflac_read_pcm_frames_s16__decode_independent_stereo__scalar(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); #endif } } -MA_API ma_uint64 ma_dr_flac_read_pcm_frames_s16(ma_dr_flac* pFlac, ma_uint64 framesToRead, ma_int16* pBufferOut) +DRFLAC_API drflac_uint64 drflac_read_pcm_frames_s16(drflac* pFlac, drflac_uint64 framesToRead, drflac_int16* pBufferOut) { - ma_uint64 framesRead; - ma_uint32 unusedBitsPerSample; + drflac_uint64 framesRead; + drflac_uint32 unusedBitsPerSample; if (pFlac == NULL || framesToRead == 0) { return 0; } if (pBufferOut == NULL) { - return ma_dr_flac__seek_forward_by_pcm_frames(pFlac, framesToRead); + return drflac__seek_forward_by_pcm_frames(pFlac, framesToRead); } - MA_DR_FLAC_ASSERT(pFlac->bitsPerSample <= 32); + DRFLAC_ASSERT(pFlac->bitsPerSample <= 32); unusedBitsPerSample = 32 - pFlac->bitsPerSample; framesRead = 0; while (framesToRead > 0) { if (pFlac->currentFLACFrame.pcmFramesRemaining == 0) { - if (!ma_dr_flac__read_and_decode_next_flac_frame(pFlac)) { + if (!drflac__read_and_decode_next_flac_frame(pFlac)) { break; } } else { - unsigned int channelCount = ma_dr_flac__get_channel_count_from_channel_assignment(pFlac->currentFLACFrame.header.channelAssignment); - ma_uint64 iFirstPCMFrame = pFlac->currentFLACFrame.header.blockSizeInPCMFrames - pFlac->currentFLACFrame.pcmFramesRemaining; - ma_uint64 frameCountThisIteration = framesToRead; + unsigned int channelCount = drflac__get_channel_count_from_channel_assignment(pFlac->currentFLACFrame.header.channelAssignment); + drflac_uint64 iFirstPCMFrame = pFlac->currentFLACFrame.header.blockSizeInPCMFrames - pFlac->currentFLACFrame.pcmFramesRemaining; + drflac_uint64 frameCountThisIteration = framesToRead; if (frameCountThisIteration > pFlac->currentFLACFrame.pcmFramesRemaining) { frameCountThisIteration = pFlac->currentFLACFrame.pcmFramesRemaining; } if (channelCount == 2) { - const ma_int32* pDecodedSamples0 = pFlac->currentFLACFrame.subframes[0].pSamplesS32 + iFirstPCMFrame; - const ma_int32* pDecodedSamples1 = pFlac->currentFLACFrame.subframes[1].pSamplesS32 + iFirstPCMFrame; + const drflac_int32* pDecodedSamples0 = pFlac->currentFLACFrame.subframes[0].pSamplesS32 + iFirstPCMFrame; + const drflac_int32* pDecodedSamples1 = pFlac->currentFLACFrame.subframes[1].pSamplesS32 + iFirstPCMFrame; switch (pFlac->currentFLACFrame.header.channelAssignment) { - case MA_DR_FLAC_CHANNEL_ASSIGNMENT_LEFT_SIDE: + case DRFLAC_CHANNEL_ASSIGNMENT_LEFT_SIDE: { - ma_dr_flac_read_pcm_frames_s16__decode_left_side(pFlac, frameCountThisIteration, unusedBitsPerSample, pDecodedSamples0, pDecodedSamples1, pBufferOut); + drflac_read_pcm_frames_s16__decode_left_side(pFlac, frameCountThisIteration, unusedBitsPerSample, pDecodedSamples0, pDecodedSamples1, pBufferOut); } break; - case MA_DR_FLAC_CHANNEL_ASSIGNMENT_RIGHT_SIDE: + case DRFLAC_CHANNEL_ASSIGNMENT_RIGHT_SIDE: { - ma_dr_flac_read_pcm_frames_s16__decode_right_side(pFlac, frameCountThisIteration, unusedBitsPerSample, pDecodedSamples0, pDecodedSamples1, pBufferOut); + drflac_read_pcm_frames_s16__decode_right_side(pFlac, frameCountThisIteration, unusedBitsPerSample, pDecodedSamples0, pDecodedSamples1, pBufferOut); } break; - case MA_DR_FLAC_CHANNEL_ASSIGNMENT_MID_SIDE: + case DRFLAC_CHANNEL_ASSIGNMENT_MID_SIDE: { - ma_dr_flac_read_pcm_frames_s16__decode_mid_side(pFlac, frameCountThisIteration, unusedBitsPerSample, pDecodedSamples0, pDecodedSamples1, pBufferOut); + drflac_read_pcm_frames_s16__decode_mid_side(pFlac, frameCountThisIteration, unusedBitsPerSample, pDecodedSamples0, pDecodedSamples1, pBufferOut); } break; - case MA_DR_FLAC_CHANNEL_ASSIGNMENT_INDEPENDENT: + case DRFLAC_CHANNEL_ASSIGNMENT_INDEPENDENT: default: { - ma_dr_flac_read_pcm_frames_s16__decode_independent_stereo(pFlac, frameCountThisIteration, unusedBitsPerSample, pDecodedSamples0, pDecodedSamples1, pBufferOut); + drflac_read_pcm_frames_s16__decode_independent_stereo(pFlac, frameCountThisIteration, unusedBitsPerSample, pDecodedSamples0, pDecodedSamples1, pBufferOut); } break; } } else { - ma_uint64 i; + drflac_uint64 i; for (i = 0; i < frameCountThisIteration; ++i) { unsigned int j; for (j = 0; j < channelCount; ++j) { - ma_int32 sampleS32 = (ma_int32)((ma_uint32)(pFlac->currentFLACFrame.subframes[j].pSamplesS32[iFirstPCMFrame + i]) << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[j].wastedBitsPerSample)); - pBufferOut[(i*channelCount)+j] = (ma_int16)(sampleS32 >> 16); + drflac_int32 sampleS32 = (drflac_int32)((drflac_uint32)(pFlac->currentFLACFrame.subframes[j].pSamplesS32[iFirstPCMFrame + i]) << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[j].wastedBitsPerSample)); + pBufferOut[(i*channelCount)+j] = (drflac_int16)(sampleS32 >> 16); } } } @@ -89541,74 +88927,74 @@ MA_API ma_uint64 ma_dr_flac_read_pcm_frames_s16(ma_dr_flac* pFlac, ma_uint64 fra pBufferOut += frameCountThisIteration * channelCount; framesToRead -= frameCountThisIteration; pFlac->currentPCMFrame += frameCountThisIteration; - pFlac->currentFLACFrame.pcmFramesRemaining -= (ma_uint32)frameCountThisIteration; + pFlac->currentFLACFrame.pcmFramesRemaining -= (drflac_uint32)frameCountThisIteration; } } return framesRead; } #if 0 -static MA_INLINE void ma_dr_flac_read_pcm_frames_f32__decode_left_side__reference(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, float* pOutputSamples) +static DRFLAC_INLINE void drflac_read_pcm_frames_f32__decode_left_side__reference(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, float* pOutputSamples) { - ma_uint64 i; + drflac_uint64 i; for (i = 0; i < frameCount; ++i) { - ma_uint32 left = (ma_uint32)pInputSamples0[i] << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample); - ma_uint32 side = (ma_uint32)pInputSamples1[i] << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample); - ma_uint32 right = left - side; - pOutputSamples[i*2+0] = (float)((ma_int32)left / 2147483648.0); - pOutputSamples[i*2+1] = (float)((ma_int32)right / 2147483648.0); + drflac_uint32 left = (drflac_uint32)pInputSamples0[i] << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample); + drflac_uint32 side = (drflac_uint32)pInputSamples1[i] << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample); + drflac_uint32 right = left - side; + pOutputSamples[i*2+0] = (float)((drflac_int32)left / 2147483648.0); + pOutputSamples[i*2+1] = (float)((drflac_int32)right / 2147483648.0); } } #endif -static MA_INLINE void ma_dr_flac_read_pcm_frames_f32__decode_left_side__scalar(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, float* pOutputSamples) +static DRFLAC_INLINE void drflac_read_pcm_frames_f32__decode_left_side__scalar(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, float* pOutputSamples) { - ma_uint64 i; - ma_uint64 frameCount4 = frameCount >> 2; - const ma_uint32* pInputSamples0U32 = (const ma_uint32*)pInputSamples0; - const ma_uint32* pInputSamples1U32 = (const ma_uint32*)pInputSamples1; - ma_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; - ma_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; + drflac_uint64 i; + drflac_uint64 frameCount4 = frameCount >> 2; + const drflac_uint32* pInputSamples0U32 = (const drflac_uint32*)pInputSamples0; + const drflac_uint32* pInputSamples1U32 = (const drflac_uint32*)pInputSamples1; + drflac_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; + drflac_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; float factor = 1 / 2147483648.0; for (i = 0; i < frameCount4; ++i) { - ma_uint32 left0 = pInputSamples0U32[i*4+0] << shift0; - ma_uint32 left1 = pInputSamples0U32[i*4+1] << shift0; - ma_uint32 left2 = pInputSamples0U32[i*4+2] << shift0; - ma_uint32 left3 = pInputSamples0U32[i*4+3] << shift0; - ma_uint32 side0 = pInputSamples1U32[i*4+0] << shift1; - ma_uint32 side1 = pInputSamples1U32[i*4+1] << shift1; - ma_uint32 side2 = pInputSamples1U32[i*4+2] << shift1; - ma_uint32 side3 = pInputSamples1U32[i*4+3] << shift1; - ma_uint32 right0 = left0 - side0; - ma_uint32 right1 = left1 - side1; - ma_uint32 right2 = left2 - side2; - ma_uint32 right3 = left3 - side3; - pOutputSamples[i*8+0] = (ma_int32)left0 * factor; - pOutputSamples[i*8+1] = (ma_int32)right0 * factor; - pOutputSamples[i*8+2] = (ma_int32)left1 * factor; - pOutputSamples[i*8+3] = (ma_int32)right1 * factor; - pOutputSamples[i*8+4] = (ma_int32)left2 * factor; - pOutputSamples[i*8+5] = (ma_int32)right2 * factor; - pOutputSamples[i*8+6] = (ma_int32)left3 * factor; - pOutputSamples[i*8+7] = (ma_int32)right3 * factor; + drflac_uint32 left0 = pInputSamples0U32[i*4+0] << shift0; + drflac_uint32 left1 = pInputSamples0U32[i*4+1] << shift0; + drflac_uint32 left2 = pInputSamples0U32[i*4+2] << shift0; + drflac_uint32 left3 = pInputSamples0U32[i*4+3] << shift0; + drflac_uint32 side0 = pInputSamples1U32[i*4+0] << shift1; + drflac_uint32 side1 = pInputSamples1U32[i*4+1] << shift1; + drflac_uint32 side2 = pInputSamples1U32[i*4+2] << shift1; + drflac_uint32 side3 = pInputSamples1U32[i*4+3] << shift1; + drflac_uint32 right0 = left0 - side0; + drflac_uint32 right1 = left1 - side1; + drflac_uint32 right2 = left2 - side2; + drflac_uint32 right3 = left3 - side3; + pOutputSamples[i*8+0] = (drflac_int32)left0 * factor; + pOutputSamples[i*8+1] = (drflac_int32)right0 * factor; + pOutputSamples[i*8+2] = (drflac_int32)left1 * factor; + pOutputSamples[i*8+3] = (drflac_int32)right1 * factor; + pOutputSamples[i*8+4] = (drflac_int32)left2 * factor; + pOutputSamples[i*8+5] = (drflac_int32)right2 * factor; + pOutputSamples[i*8+6] = (drflac_int32)left3 * factor; + pOutputSamples[i*8+7] = (drflac_int32)right3 * factor; } for (i = (frameCount4 << 2); i < frameCount; ++i) { - ma_uint32 left = pInputSamples0U32[i] << shift0; - ma_uint32 side = pInputSamples1U32[i] << shift1; - ma_uint32 right = left - side; - pOutputSamples[i*2+0] = (ma_int32)left * factor; - pOutputSamples[i*2+1] = (ma_int32)right * factor; - } -} -#if defined(MA_DR_FLAC_SUPPORT_SSE2) -static MA_INLINE void ma_dr_flac_read_pcm_frames_f32__decode_left_side__sse2(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, float* pOutputSamples) -{ - ma_uint64 i; - ma_uint64 frameCount4 = frameCount >> 2; - const ma_uint32* pInputSamples0U32 = (const ma_uint32*)pInputSamples0; - const ma_uint32* pInputSamples1U32 = (const ma_uint32*)pInputSamples1; - ma_uint32 shift0 = (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample) - 8; - ma_uint32 shift1 = (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample) - 8; + drflac_uint32 left = pInputSamples0U32[i] << shift0; + drflac_uint32 side = pInputSamples1U32[i] << shift1; + drflac_uint32 right = left - side; + pOutputSamples[i*2+0] = (drflac_int32)left * factor; + pOutputSamples[i*2+1] = (drflac_int32)right * factor; + } +} +#if defined(DRFLAC_SUPPORT_SSE2) +static DRFLAC_INLINE void drflac_read_pcm_frames_f32__decode_left_side__sse2(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, float* pOutputSamples) +{ + drflac_uint64 i; + drflac_uint64 frameCount4 = frameCount >> 2; + const drflac_uint32* pInputSamples0U32 = (const drflac_uint32*)pInputSamples0; + const drflac_uint32* pInputSamples1U32 = (const drflac_uint32*)pInputSamples1; + drflac_uint32 shift0 = (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample) - 8; + drflac_uint32 shift1 = (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample) - 8; __m128 factor; - MA_DR_FLAC_ASSERT(pFlac->bitsPerSample <= 24); + DRFLAC_ASSERT(pFlac->bitsPerSample <= 24); factor = _mm_set1_ps(1.0f / 8388608.0f); for (i = 0; i < frameCount4; ++i) { __m128i left = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples0 + i), shift0); @@ -89620,27 +89006,27 @@ static MA_INLINE void ma_dr_flac_read_pcm_frames_f32__decode_left_side__sse2(ma_ _mm_storeu_ps(pOutputSamples + i*8 + 4, _mm_unpackhi_ps(leftf, rightf)); } for (i = (frameCount4 << 2); i < frameCount; ++i) { - ma_uint32 left = pInputSamples0U32[i] << shift0; - ma_uint32 side = pInputSamples1U32[i] << shift1; - ma_uint32 right = left - side; - pOutputSamples[i*2+0] = (ma_int32)left / 8388608.0f; - pOutputSamples[i*2+1] = (ma_int32)right / 8388608.0f; + drflac_uint32 left = pInputSamples0U32[i] << shift0; + drflac_uint32 side = pInputSamples1U32[i] << shift1; + drflac_uint32 right = left - side; + pOutputSamples[i*2+0] = (drflac_int32)left / 8388608.0f; + pOutputSamples[i*2+1] = (drflac_int32)right / 8388608.0f; } } #endif -#if defined(MA_DR_FLAC_SUPPORT_NEON) -static MA_INLINE void ma_dr_flac_read_pcm_frames_f32__decode_left_side__neon(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, float* pOutputSamples) +#if defined(DRFLAC_SUPPORT_NEON) +static DRFLAC_INLINE void drflac_read_pcm_frames_f32__decode_left_side__neon(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, float* pOutputSamples) { - ma_uint64 i; - ma_uint64 frameCount4 = frameCount >> 2; - const ma_uint32* pInputSamples0U32 = (const ma_uint32*)pInputSamples0; - const ma_uint32* pInputSamples1U32 = (const ma_uint32*)pInputSamples1; - ma_uint32 shift0 = (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample) - 8; - ma_uint32 shift1 = (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample) - 8; + drflac_uint64 i; + drflac_uint64 frameCount4 = frameCount >> 2; + const drflac_uint32* pInputSamples0U32 = (const drflac_uint32*)pInputSamples0; + const drflac_uint32* pInputSamples1U32 = (const drflac_uint32*)pInputSamples1; + drflac_uint32 shift0 = (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample) - 8; + drflac_uint32 shift1 = (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample) - 8; float32x4_t factor4; int32x4_t shift0_4; int32x4_t shift1_4; - MA_DR_FLAC_ASSERT(pFlac->bitsPerSample <= 24); + DRFLAC_ASSERT(pFlac->bitsPerSample <= 24); factor4 = vdupq_n_f32(1.0f / 8388608.0f); shift0_4 = vdupq_n_s32(shift0); shift1_4 = vdupq_n_s32(shift1); @@ -89655,99 +89041,99 @@ static MA_INLINE void ma_dr_flac_read_pcm_frames_f32__decode_left_side__neon(ma_ right = vsubq_u32(left, side); leftf = vmulq_f32(vcvtq_f32_s32(vreinterpretq_s32_u32(left)), factor4); rightf = vmulq_f32(vcvtq_f32_s32(vreinterpretq_s32_u32(right)), factor4); - ma_dr_flac__vst2q_f32(pOutputSamples + i*8, vzipq_f32(leftf, rightf)); + drflac__vst2q_f32(pOutputSamples + i*8, vzipq_f32(leftf, rightf)); } for (i = (frameCount4 << 2); i < frameCount; ++i) { - ma_uint32 left = pInputSamples0U32[i] << shift0; - ma_uint32 side = pInputSamples1U32[i] << shift1; - ma_uint32 right = left - side; - pOutputSamples[i*2+0] = (ma_int32)left / 8388608.0f; - pOutputSamples[i*2+1] = (ma_int32)right / 8388608.0f; + drflac_uint32 left = pInputSamples0U32[i] << shift0; + drflac_uint32 side = pInputSamples1U32[i] << shift1; + drflac_uint32 right = left - side; + pOutputSamples[i*2+0] = (drflac_int32)left / 8388608.0f; + pOutputSamples[i*2+1] = (drflac_int32)right / 8388608.0f; } } #endif -static MA_INLINE void ma_dr_flac_read_pcm_frames_f32__decode_left_side(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, float* pOutputSamples) +static DRFLAC_INLINE void drflac_read_pcm_frames_f32__decode_left_side(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, float* pOutputSamples) { -#if defined(MA_DR_FLAC_SUPPORT_SSE2) - if (ma_dr_flac__gIsSSE2Supported && pFlac->bitsPerSample <= 24) { - ma_dr_flac_read_pcm_frames_f32__decode_left_side__sse2(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); +#if defined(DRFLAC_SUPPORT_SSE2) + if (drflac__gIsSSE2Supported && pFlac->bitsPerSample <= 24) { + drflac_read_pcm_frames_f32__decode_left_side__sse2(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); } else -#elif defined(MA_DR_FLAC_SUPPORT_NEON) - if (ma_dr_flac__gIsNEONSupported && pFlac->bitsPerSample <= 24) { - ma_dr_flac_read_pcm_frames_f32__decode_left_side__neon(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); +#elif defined(DRFLAC_SUPPORT_NEON) + if (drflac__gIsNEONSupported && pFlac->bitsPerSample <= 24) { + drflac_read_pcm_frames_f32__decode_left_side__neon(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); } else #endif { #if 0 - ma_dr_flac_read_pcm_frames_f32__decode_left_side__reference(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); + drflac_read_pcm_frames_f32__decode_left_side__reference(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); #else - ma_dr_flac_read_pcm_frames_f32__decode_left_side__scalar(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); + drflac_read_pcm_frames_f32__decode_left_side__scalar(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); #endif } } #if 0 -static MA_INLINE void ma_dr_flac_read_pcm_frames_f32__decode_right_side__reference(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, float* pOutputSamples) +static DRFLAC_INLINE void drflac_read_pcm_frames_f32__decode_right_side__reference(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, float* pOutputSamples) { - ma_uint64 i; + drflac_uint64 i; for (i = 0; i < frameCount; ++i) { - ma_uint32 side = (ma_uint32)pInputSamples0[i] << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample); - ma_uint32 right = (ma_uint32)pInputSamples1[i] << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample); - ma_uint32 left = right + side; - pOutputSamples[i*2+0] = (float)((ma_int32)left / 2147483648.0); - pOutputSamples[i*2+1] = (float)((ma_int32)right / 2147483648.0); + drflac_uint32 side = (drflac_uint32)pInputSamples0[i] << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample); + drflac_uint32 right = (drflac_uint32)pInputSamples1[i] << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample); + drflac_uint32 left = right + side; + pOutputSamples[i*2+0] = (float)((drflac_int32)left / 2147483648.0); + pOutputSamples[i*2+1] = (float)((drflac_int32)right / 2147483648.0); } } #endif -static MA_INLINE void ma_dr_flac_read_pcm_frames_f32__decode_right_side__scalar(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, float* pOutputSamples) +static DRFLAC_INLINE void drflac_read_pcm_frames_f32__decode_right_side__scalar(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, float* pOutputSamples) { - ma_uint64 i; - ma_uint64 frameCount4 = frameCount >> 2; - const ma_uint32* pInputSamples0U32 = (const ma_uint32*)pInputSamples0; - const ma_uint32* pInputSamples1U32 = (const ma_uint32*)pInputSamples1; - ma_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; - ma_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; + drflac_uint64 i; + drflac_uint64 frameCount4 = frameCount >> 2; + const drflac_uint32* pInputSamples0U32 = (const drflac_uint32*)pInputSamples0; + const drflac_uint32* pInputSamples1U32 = (const drflac_uint32*)pInputSamples1; + drflac_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; + drflac_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; float factor = 1 / 2147483648.0; for (i = 0; i < frameCount4; ++i) { - ma_uint32 side0 = pInputSamples0U32[i*4+0] << shift0; - ma_uint32 side1 = pInputSamples0U32[i*4+1] << shift0; - ma_uint32 side2 = pInputSamples0U32[i*4+2] << shift0; - ma_uint32 side3 = pInputSamples0U32[i*4+3] << shift0; - ma_uint32 right0 = pInputSamples1U32[i*4+0] << shift1; - ma_uint32 right1 = pInputSamples1U32[i*4+1] << shift1; - ma_uint32 right2 = pInputSamples1U32[i*4+2] << shift1; - ma_uint32 right3 = pInputSamples1U32[i*4+3] << shift1; - ma_uint32 left0 = right0 + side0; - ma_uint32 left1 = right1 + side1; - ma_uint32 left2 = right2 + side2; - ma_uint32 left3 = right3 + side3; - pOutputSamples[i*8+0] = (ma_int32)left0 * factor; - pOutputSamples[i*8+1] = (ma_int32)right0 * factor; - pOutputSamples[i*8+2] = (ma_int32)left1 * factor; - pOutputSamples[i*8+3] = (ma_int32)right1 * factor; - pOutputSamples[i*8+4] = (ma_int32)left2 * factor; - pOutputSamples[i*8+5] = (ma_int32)right2 * factor; - pOutputSamples[i*8+6] = (ma_int32)left3 * factor; - pOutputSamples[i*8+7] = (ma_int32)right3 * factor; + drflac_uint32 side0 = pInputSamples0U32[i*4+0] << shift0; + drflac_uint32 side1 = pInputSamples0U32[i*4+1] << shift0; + drflac_uint32 side2 = pInputSamples0U32[i*4+2] << shift0; + drflac_uint32 side3 = pInputSamples0U32[i*4+3] << shift0; + drflac_uint32 right0 = pInputSamples1U32[i*4+0] << shift1; + drflac_uint32 right1 = pInputSamples1U32[i*4+1] << shift1; + drflac_uint32 right2 = pInputSamples1U32[i*4+2] << shift1; + drflac_uint32 right3 = pInputSamples1U32[i*4+3] << shift1; + drflac_uint32 left0 = right0 + side0; + drflac_uint32 left1 = right1 + side1; + drflac_uint32 left2 = right2 + side2; + drflac_uint32 left3 = right3 + side3; + pOutputSamples[i*8+0] = (drflac_int32)left0 * factor; + pOutputSamples[i*8+1] = (drflac_int32)right0 * factor; + pOutputSamples[i*8+2] = (drflac_int32)left1 * factor; + pOutputSamples[i*8+3] = (drflac_int32)right1 * factor; + pOutputSamples[i*8+4] = (drflac_int32)left2 * factor; + pOutputSamples[i*8+5] = (drflac_int32)right2 * factor; + pOutputSamples[i*8+6] = (drflac_int32)left3 * factor; + pOutputSamples[i*8+7] = (drflac_int32)right3 * factor; } for (i = (frameCount4 << 2); i < frameCount; ++i) { - ma_uint32 side = pInputSamples0U32[i] << shift0; - ma_uint32 right = pInputSamples1U32[i] << shift1; - ma_uint32 left = right + side; - pOutputSamples[i*2+0] = (ma_int32)left * factor; - pOutputSamples[i*2+1] = (ma_int32)right * factor; - } -} -#if defined(MA_DR_FLAC_SUPPORT_SSE2) -static MA_INLINE void ma_dr_flac_read_pcm_frames_f32__decode_right_side__sse2(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, float* pOutputSamples) -{ - ma_uint64 i; - ma_uint64 frameCount4 = frameCount >> 2; - const ma_uint32* pInputSamples0U32 = (const ma_uint32*)pInputSamples0; - const ma_uint32* pInputSamples1U32 = (const ma_uint32*)pInputSamples1; - ma_uint32 shift0 = (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample) - 8; - ma_uint32 shift1 = (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample) - 8; + drflac_uint32 side = pInputSamples0U32[i] << shift0; + drflac_uint32 right = pInputSamples1U32[i] << shift1; + drflac_uint32 left = right + side; + pOutputSamples[i*2+0] = (drflac_int32)left * factor; + pOutputSamples[i*2+1] = (drflac_int32)right * factor; + } +} +#if defined(DRFLAC_SUPPORT_SSE2) +static DRFLAC_INLINE void drflac_read_pcm_frames_f32__decode_right_side__sse2(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, float* pOutputSamples) +{ + drflac_uint64 i; + drflac_uint64 frameCount4 = frameCount >> 2; + const drflac_uint32* pInputSamples0U32 = (const drflac_uint32*)pInputSamples0; + const drflac_uint32* pInputSamples1U32 = (const drflac_uint32*)pInputSamples1; + drflac_uint32 shift0 = (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample) - 8; + drflac_uint32 shift1 = (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample) - 8; __m128 factor; - MA_DR_FLAC_ASSERT(pFlac->bitsPerSample <= 24); + DRFLAC_ASSERT(pFlac->bitsPerSample <= 24); factor = _mm_set1_ps(1.0f / 8388608.0f); for (i = 0; i < frameCount4; ++i) { __m128i side = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples0 + i), shift0); @@ -89759,27 +89145,27 @@ static MA_INLINE void ma_dr_flac_read_pcm_frames_f32__decode_right_side__sse2(ma _mm_storeu_ps(pOutputSamples + i*8 + 4, _mm_unpackhi_ps(leftf, rightf)); } for (i = (frameCount4 << 2); i < frameCount; ++i) { - ma_uint32 side = pInputSamples0U32[i] << shift0; - ma_uint32 right = pInputSamples1U32[i] << shift1; - ma_uint32 left = right + side; - pOutputSamples[i*2+0] = (ma_int32)left / 8388608.0f; - pOutputSamples[i*2+1] = (ma_int32)right / 8388608.0f; + drflac_uint32 side = pInputSamples0U32[i] << shift0; + drflac_uint32 right = pInputSamples1U32[i] << shift1; + drflac_uint32 left = right + side; + pOutputSamples[i*2+0] = (drflac_int32)left / 8388608.0f; + pOutputSamples[i*2+1] = (drflac_int32)right / 8388608.0f; } } #endif -#if defined(MA_DR_FLAC_SUPPORT_NEON) -static MA_INLINE void ma_dr_flac_read_pcm_frames_f32__decode_right_side__neon(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, float* pOutputSamples) +#if defined(DRFLAC_SUPPORT_NEON) +static DRFLAC_INLINE void drflac_read_pcm_frames_f32__decode_right_side__neon(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, float* pOutputSamples) { - ma_uint64 i; - ma_uint64 frameCount4 = frameCount >> 2; - const ma_uint32* pInputSamples0U32 = (const ma_uint32*)pInputSamples0; - const ma_uint32* pInputSamples1U32 = (const ma_uint32*)pInputSamples1; - ma_uint32 shift0 = (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample) - 8; - ma_uint32 shift1 = (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample) - 8; + drflac_uint64 i; + drflac_uint64 frameCount4 = frameCount >> 2; + const drflac_uint32* pInputSamples0U32 = (const drflac_uint32*)pInputSamples0; + const drflac_uint32* pInputSamples1U32 = (const drflac_uint32*)pInputSamples1; + drflac_uint32 shift0 = (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample) - 8; + drflac_uint32 shift1 = (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample) - 8; float32x4_t factor4; int32x4_t shift0_4; int32x4_t shift1_4; - MA_DR_FLAC_ASSERT(pFlac->bitsPerSample <= 24); + DRFLAC_ASSERT(pFlac->bitsPerSample <= 24); factor4 = vdupq_n_f32(1.0f / 8388608.0f); shift0_4 = vdupq_n_s32(shift0); shift1_4 = vdupq_n_s32(shift1); @@ -89794,75 +89180,75 @@ static MA_INLINE void ma_dr_flac_read_pcm_frames_f32__decode_right_side__neon(ma left = vaddq_u32(right, side); leftf = vmulq_f32(vcvtq_f32_s32(vreinterpretq_s32_u32(left)), factor4); rightf = vmulq_f32(vcvtq_f32_s32(vreinterpretq_s32_u32(right)), factor4); - ma_dr_flac__vst2q_f32(pOutputSamples + i*8, vzipq_f32(leftf, rightf)); + drflac__vst2q_f32(pOutputSamples + i*8, vzipq_f32(leftf, rightf)); } for (i = (frameCount4 << 2); i < frameCount; ++i) { - ma_uint32 side = pInputSamples0U32[i] << shift0; - ma_uint32 right = pInputSamples1U32[i] << shift1; - ma_uint32 left = right + side; - pOutputSamples[i*2+0] = (ma_int32)left / 8388608.0f; - pOutputSamples[i*2+1] = (ma_int32)right / 8388608.0f; + drflac_uint32 side = pInputSamples0U32[i] << shift0; + drflac_uint32 right = pInputSamples1U32[i] << shift1; + drflac_uint32 left = right + side; + pOutputSamples[i*2+0] = (drflac_int32)left / 8388608.0f; + pOutputSamples[i*2+1] = (drflac_int32)right / 8388608.0f; } } #endif -static MA_INLINE void ma_dr_flac_read_pcm_frames_f32__decode_right_side(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, float* pOutputSamples) +static DRFLAC_INLINE void drflac_read_pcm_frames_f32__decode_right_side(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, float* pOutputSamples) { -#if defined(MA_DR_FLAC_SUPPORT_SSE2) - if (ma_dr_flac__gIsSSE2Supported && pFlac->bitsPerSample <= 24) { - ma_dr_flac_read_pcm_frames_f32__decode_right_side__sse2(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); +#if defined(DRFLAC_SUPPORT_SSE2) + if (drflac__gIsSSE2Supported && pFlac->bitsPerSample <= 24) { + drflac_read_pcm_frames_f32__decode_right_side__sse2(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); } else -#elif defined(MA_DR_FLAC_SUPPORT_NEON) - if (ma_dr_flac__gIsNEONSupported && pFlac->bitsPerSample <= 24) { - ma_dr_flac_read_pcm_frames_f32__decode_right_side__neon(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); +#elif defined(DRFLAC_SUPPORT_NEON) + if (drflac__gIsNEONSupported && pFlac->bitsPerSample <= 24) { + drflac_read_pcm_frames_f32__decode_right_side__neon(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); } else #endif { #if 0 - ma_dr_flac_read_pcm_frames_f32__decode_right_side__reference(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); + drflac_read_pcm_frames_f32__decode_right_side__reference(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); #else - ma_dr_flac_read_pcm_frames_f32__decode_right_side__scalar(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); + drflac_read_pcm_frames_f32__decode_right_side__scalar(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); #endif } } #if 0 -static MA_INLINE void ma_dr_flac_read_pcm_frames_f32__decode_mid_side__reference(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, float* pOutputSamples) +static DRFLAC_INLINE void drflac_read_pcm_frames_f32__decode_mid_side__reference(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, float* pOutputSamples) { - for (ma_uint64 i = 0; i < frameCount; ++i) { - ma_uint32 mid = (ma_uint32)pInputSamples0[i] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; - ma_uint32 side = (ma_uint32)pInputSamples1[i] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; + for (drflac_uint64 i = 0; i < frameCount; ++i) { + drflac_uint32 mid = (drflac_uint32)pInputSamples0[i] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; + drflac_uint32 side = (drflac_uint32)pInputSamples1[i] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; mid = (mid << 1) | (side & 0x01); - pOutputSamples[i*2+0] = (float)((((ma_int32)(mid + side) >> 1) << (unusedBitsPerSample)) / 2147483648.0); - pOutputSamples[i*2+1] = (float)((((ma_int32)(mid - side) >> 1) << (unusedBitsPerSample)) / 2147483648.0); + pOutputSamples[i*2+0] = (float)((((drflac_int32)(mid + side) >> 1) << (unusedBitsPerSample)) / 2147483648.0); + pOutputSamples[i*2+1] = (float)((((drflac_int32)(mid - side) >> 1) << (unusedBitsPerSample)) / 2147483648.0); } } #endif -static MA_INLINE void ma_dr_flac_read_pcm_frames_f32__decode_mid_side__scalar(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, float* pOutputSamples) +static DRFLAC_INLINE void drflac_read_pcm_frames_f32__decode_mid_side__scalar(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, float* pOutputSamples) { - ma_uint64 i; - ma_uint64 frameCount4 = frameCount >> 2; - const ma_uint32* pInputSamples0U32 = (const ma_uint32*)pInputSamples0; - const ma_uint32* pInputSamples1U32 = (const ma_uint32*)pInputSamples1; - ma_uint32 shift = unusedBitsPerSample; + drflac_uint64 i; + drflac_uint64 frameCount4 = frameCount >> 2; + const drflac_uint32* pInputSamples0U32 = (const drflac_uint32*)pInputSamples0; + const drflac_uint32* pInputSamples1U32 = (const drflac_uint32*)pInputSamples1; + drflac_uint32 shift = unusedBitsPerSample; float factor = 1 / 2147483648.0; if (shift > 0) { shift -= 1; for (i = 0; i < frameCount4; ++i) { - ma_uint32 temp0L; - ma_uint32 temp1L; - ma_uint32 temp2L; - ma_uint32 temp3L; - ma_uint32 temp0R; - ma_uint32 temp1R; - ma_uint32 temp2R; - ma_uint32 temp3R; - ma_uint32 mid0 = pInputSamples0U32[i*4+0] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; - ma_uint32 mid1 = pInputSamples0U32[i*4+1] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; - ma_uint32 mid2 = pInputSamples0U32[i*4+2] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; - ma_uint32 mid3 = pInputSamples0U32[i*4+3] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; - ma_uint32 side0 = pInputSamples1U32[i*4+0] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; - ma_uint32 side1 = pInputSamples1U32[i*4+1] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; - ma_uint32 side2 = pInputSamples1U32[i*4+2] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; - ma_uint32 side3 = pInputSamples1U32[i*4+3] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; + drflac_uint32 temp0L; + drflac_uint32 temp1L; + drflac_uint32 temp2L; + drflac_uint32 temp3L; + drflac_uint32 temp0R; + drflac_uint32 temp1R; + drflac_uint32 temp2R; + drflac_uint32 temp3R; + drflac_uint32 mid0 = pInputSamples0U32[i*4+0] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; + drflac_uint32 mid1 = pInputSamples0U32[i*4+1] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; + drflac_uint32 mid2 = pInputSamples0U32[i*4+2] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; + drflac_uint32 mid3 = pInputSamples0U32[i*4+3] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; + drflac_uint32 side0 = pInputSamples1U32[i*4+0] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; + drflac_uint32 side1 = pInputSamples1U32[i*4+1] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; + drflac_uint32 side2 = pInputSamples1U32[i*4+2] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; + drflac_uint32 side3 = pInputSamples1U32[i*4+3] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; mid0 = (mid0 << 1) | (side0 & 0x01); mid1 = (mid1 << 1) | (side1 & 0x01); mid2 = (mid2 << 1) | (side2 & 0x01); @@ -89875,74 +89261,74 @@ static MA_INLINE void ma_dr_flac_read_pcm_frames_f32__decode_mid_side__scalar(ma temp1R = (mid1 - side1) << shift; temp2R = (mid2 - side2) << shift; temp3R = (mid3 - side3) << shift; - pOutputSamples[i*8+0] = (ma_int32)temp0L * factor; - pOutputSamples[i*8+1] = (ma_int32)temp0R * factor; - pOutputSamples[i*8+2] = (ma_int32)temp1L * factor; - pOutputSamples[i*8+3] = (ma_int32)temp1R * factor; - pOutputSamples[i*8+4] = (ma_int32)temp2L * factor; - pOutputSamples[i*8+5] = (ma_int32)temp2R * factor; - pOutputSamples[i*8+6] = (ma_int32)temp3L * factor; - pOutputSamples[i*8+7] = (ma_int32)temp3R * factor; + pOutputSamples[i*8+0] = (drflac_int32)temp0L * factor; + pOutputSamples[i*8+1] = (drflac_int32)temp0R * factor; + pOutputSamples[i*8+2] = (drflac_int32)temp1L * factor; + pOutputSamples[i*8+3] = (drflac_int32)temp1R * factor; + pOutputSamples[i*8+4] = (drflac_int32)temp2L * factor; + pOutputSamples[i*8+5] = (drflac_int32)temp2R * factor; + pOutputSamples[i*8+6] = (drflac_int32)temp3L * factor; + pOutputSamples[i*8+7] = (drflac_int32)temp3R * factor; } } else { for (i = 0; i < frameCount4; ++i) { - ma_uint32 temp0L; - ma_uint32 temp1L; - ma_uint32 temp2L; - ma_uint32 temp3L; - ma_uint32 temp0R; - ma_uint32 temp1R; - ma_uint32 temp2R; - ma_uint32 temp3R; - ma_uint32 mid0 = pInputSamples0U32[i*4+0] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; - ma_uint32 mid1 = pInputSamples0U32[i*4+1] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; - ma_uint32 mid2 = pInputSamples0U32[i*4+2] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; - ma_uint32 mid3 = pInputSamples0U32[i*4+3] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; - ma_uint32 side0 = pInputSamples1U32[i*4+0] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; - ma_uint32 side1 = pInputSamples1U32[i*4+1] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; - ma_uint32 side2 = pInputSamples1U32[i*4+2] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; - ma_uint32 side3 = pInputSamples1U32[i*4+3] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; + drflac_uint32 temp0L; + drflac_uint32 temp1L; + drflac_uint32 temp2L; + drflac_uint32 temp3L; + drflac_uint32 temp0R; + drflac_uint32 temp1R; + drflac_uint32 temp2R; + drflac_uint32 temp3R; + drflac_uint32 mid0 = pInputSamples0U32[i*4+0] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; + drflac_uint32 mid1 = pInputSamples0U32[i*4+1] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; + drflac_uint32 mid2 = pInputSamples0U32[i*4+2] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; + drflac_uint32 mid3 = pInputSamples0U32[i*4+3] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; + drflac_uint32 side0 = pInputSamples1U32[i*4+0] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; + drflac_uint32 side1 = pInputSamples1U32[i*4+1] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; + drflac_uint32 side2 = pInputSamples1U32[i*4+2] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; + drflac_uint32 side3 = pInputSamples1U32[i*4+3] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; mid0 = (mid0 << 1) | (side0 & 0x01); mid1 = (mid1 << 1) | (side1 & 0x01); mid2 = (mid2 << 1) | (side2 & 0x01); mid3 = (mid3 << 1) | (side3 & 0x01); - temp0L = (ma_uint32)((ma_int32)(mid0 + side0) >> 1); - temp1L = (ma_uint32)((ma_int32)(mid1 + side1) >> 1); - temp2L = (ma_uint32)((ma_int32)(mid2 + side2) >> 1); - temp3L = (ma_uint32)((ma_int32)(mid3 + side3) >> 1); - temp0R = (ma_uint32)((ma_int32)(mid0 - side0) >> 1); - temp1R = (ma_uint32)((ma_int32)(mid1 - side1) >> 1); - temp2R = (ma_uint32)((ma_int32)(mid2 - side2) >> 1); - temp3R = (ma_uint32)((ma_int32)(mid3 - side3) >> 1); - pOutputSamples[i*8+0] = (ma_int32)temp0L * factor; - pOutputSamples[i*8+1] = (ma_int32)temp0R * factor; - pOutputSamples[i*8+2] = (ma_int32)temp1L * factor; - pOutputSamples[i*8+3] = (ma_int32)temp1R * factor; - pOutputSamples[i*8+4] = (ma_int32)temp2L * factor; - pOutputSamples[i*8+5] = (ma_int32)temp2R * factor; - pOutputSamples[i*8+6] = (ma_int32)temp3L * factor; - pOutputSamples[i*8+7] = (ma_int32)temp3R * factor; + temp0L = (drflac_uint32)((drflac_int32)(mid0 + side0) >> 1); + temp1L = (drflac_uint32)((drflac_int32)(mid1 + side1) >> 1); + temp2L = (drflac_uint32)((drflac_int32)(mid2 + side2) >> 1); + temp3L = (drflac_uint32)((drflac_int32)(mid3 + side3) >> 1); + temp0R = (drflac_uint32)((drflac_int32)(mid0 - side0) >> 1); + temp1R = (drflac_uint32)((drflac_int32)(mid1 - side1) >> 1); + temp2R = (drflac_uint32)((drflac_int32)(mid2 - side2) >> 1); + temp3R = (drflac_uint32)((drflac_int32)(mid3 - side3) >> 1); + pOutputSamples[i*8+0] = (drflac_int32)temp0L * factor; + pOutputSamples[i*8+1] = (drflac_int32)temp0R * factor; + pOutputSamples[i*8+2] = (drflac_int32)temp1L * factor; + pOutputSamples[i*8+3] = (drflac_int32)temp1R * factor; + pOutputSamples[i*8+4] = (drflac_int32)temp2L * factor; + pOutputSamples[i*8+5] = (drflac_int32)temp2R * factor; + pOutputSamples[i*8+6] = (drflac_int32)temp3L * factor; + pOutputSamples[i*8+7] = (drflac_int32)temp3R * factor; } } for (i = (frameCount4 << 2); i < frameCount; ++i) { - ma_uint32 mid = pInputSamples0U32[i] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; - ma_uint32 side = pInputSamples1U32[i] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; + drflac_uint32 mid = pInputSamples0U32[i] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; + drflac_uint32 side = pInputSamples1U32[i] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; mid = (mid << 1) | (side & 0x01); - pOutputSamples[i*2+0] = (ma_int32)((ma_uint32)((ma_int32)(mid + side) >> 1) << unusedBitsPerSample) * factor; - pOutputSamples[i*2+1] = (ma_int32)((ma_uint32)((ma_int32)(mid - side) >> 1) << unusedBitsPerSample) * factor; + pOutputSamples[i*2+0] = (drflac_int32)((drflac_uint32)((drflac_int32)(mid + side) >> 1) << unusedBitsPerSample) * factor; + pOutputSamples[i*2+1] = (drflac_int32)((drflac_uint32)((drflac_int32)(mid - side) >> 1) << unusedBitsPerSample) * factor; } } -#if defined(MA_DR_FLAC_SUPPORT_SSE2) -static MA_INLINE void ma_dr_flac_read_pcm_frames_f32__decode_mid_side__sse2(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, float* pOutputSamples) +#if defined(DRFLAC_SUPPORT_SSE2) +static DRFLAC_INLINE void drflac_read_pcm_frames_f32__decode_mid_side__sse2(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, float* pOutputSamples) { - ma_uint64 i; - ma_uint64 frameCount4 = frameCount >> 2; - const ma_uint32* pInputSamples0U32 = (const ma_uint32*)pInputSamples0; - const ma_uint32* pInputSamples1U32 = (const ma_uint32*)pInputSamples1; - ma_uint32 shift = unusedBitsPerSample - 8; + drflac_uint64 i; + drflac_uint64 frameCount4 = frameCount >> 2; + const drflac_uint32* pInputSamples0U32 = (const drflac_uint32*)pInputSamples0; + const drflac_uint32* pInputSamples1U32 = (const drflac_uint32*)pInputSamples1; + drflac_uint32 shift = unusedBitsPerSample - 8; float factor; __m128 factor128; - MA_DR_FLAC_ASSERT(pFlac->bitsPerSample <= 24); + DRFLAC_ASSERT(pFlac->bitsPerSample <= 24); factor = 1.0f / 8388608.0f; factor128 = _mm_set1_ps(factor); if (shift == 0) { @@ -89964,11 +89350,11 @@ static MA_INLINE void ma_dr_flac_read_pcm_frames_f32__decode_mid_side__sse2(ma_d _mm_storeu_ps(pOutputSamples + i*8 + 4, _mm_unpackhi_ps(leftf, rightf)); } for (i = (frameCount4 << 2); i < frameCount; ++i) { - ma_uint32 mid = pInputSamples0U32[i] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; - ma_uint32 side = pInputSamples1U32[i] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; + drflac_uint32 mid = pInputSamples0U32[i] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; + drflac_uint32 side = pInputSamples1U32[i] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; mid = (mid << 1) | (side & 0x01); - pOutputSamples[i*2+0] = ((ma_int32)(mid + side) >> 1) * factor; - pOutputSamples[i*2+1] = ((ma_int32)(mid - side) >> 1) * factor; + pOutputSamples[i*2+0] = ((drflac_int32)(mid + side) >> 1) * factor; + pOutputSamples[i*2+1] = ((drflac_int32)(mid - side) >> 1) * factor; } } else { shift -= 1; @@ -89990,29 +89376,29 @@ static MA_INLINE void ma_dr_flac_read_pcm_frames_f32__decode_mid_side__sse2(ma_d _mm_storeu_ps(pOutputSamples + i*8 + 4, _mm_unpackhi_ps(leftf, rightf)); } for (i = (frameCount4 << 2); i < frameCount; ++i) { - ma_uint32 mid = pInputSamples0U32[i] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; - ma_uint32 side = pInputSamples1U32[i] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; + drflac_uint32 mid = pInputSamples0U32[i] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; + drflac_uint32 side = pInputSamples1U32[i] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; mid = (mid << 1) | (side & 0x01); - pOutputSamples[i*2+0] = (ma_int32)((mid + side) << shift) * factor; - pOutputSamples[i*2+1] = (ma_int32)((mid - side) << shift) * factor; + pOutputSamples[i*2+0] = (drflac_int32)((mid + side) << shift) * factor; + pOutputSamples[i*2+1] = (drflac_int32)((mid - side) << shift) * factor; } } } #endif -#if defined(MA_DR_FLAC_SUPPORT_NEON) -static MA_INLINE void ma_dr_flac_read_pcm_frames_f32__decode_mid_side__neon(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, float* pOutputSamples) +#if defined(DRFLAC_SUPPORT_NEON) +static DRFLAC_INLINE void drflac_read_pcm_frames_f32__decode_mid_side__neon(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, float* pOutputSamples) { - ma_uint64 i; - ma_uint64 frameCount4 = frameCount >> 2; - const ma_uint32* pInputSamples0U32 = (const ma_uint32*)pInputSamples0; - const ma_uint32* pInputSamples1U32 = (const ma_uint32*)pInputSamples1; - ma_uint32 shift = unusedBitsPerSample - 8; + drflac_uint64 i; + drflac_uint64 frameCount4 = frameCount >> 2; + const drflac_uint32* pInputSamples0U32 = (const drflac_uint32*)pInputSamples0; + const drflac_uint32* pInputSamples1U32 = (const drflac_uint32*)pInputSamples1; + drflac_uint32 shift = unusedBitsPerSample - 8; float factor; float32x4_t factor4; int32x4_t shift4; int32x4_t wbps0_4; int32x4_t wbps1_4; - MA_DR_FLAC_ASSERT(pFlac->bitsPerSample <= 24); + DRFLAC_ASSERT(pFlac->bitsPerSample <= 24); factor = 1.0f / 8388608.0f; factor4 = vdupq_n_f32(factor); wbps0_4 = vdupq_n_s32(pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample); @@ -90030,14 +89416,14 @@ static MA_INLINE void ma_dr_flac_read_pcm_frames_f32__decode_mid_side__neon(ma_d righti = vshrq_n_s32(vreinterpretq_s32_u32(vsubq_u32(mid, side)), 1); leftf = vmulq_f32(vcvtq_f32_s32(lefti), factor4); rightf = vmulq_f32(vcvtq_f32_s32(righti), factor4); - ma_dr_flac__vst2q_f32(pOutputSamples + i*8, vzipq_f32(leftf, rightf)); + drflac__vst2q_f32(pOutputSamples + i*8, vzipq_f32(leftf, rightf)); } for (i = (frameCount4 << 2); i < frameCount; ++i) { - ma_uint32 mid = pInputSamples0U32[i] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; - ma_uint32 side = pInputSamples1U32[i] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; + drflac_uint32 mid = pInputSamples0U32[i] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; + drflac_uint32 side = pInputSamples1U32[i] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; mid = (mid << 1) | (side & 0x01); - pOutputSamples[i*2+0] = ((ma_int32)(mid + side) >> 1) * factor; - pOutputSamples[i*2+1] = ((ma_int32)(mid - side) >> 1) * factor; + pOutputSamples[i*2+0] = ((drflac_int32)(mid + side) >> 1) * factor; + pOutputSamples[i*2+1] = ((drflac_int32)(mid - side) >> 1) * factor; } } else { shift -= 1; @@ -90056,87 +89442,87 @@ static MA_INLINE void ma_dr_flac_read_pcm_frames_f32__decode_mid_side__neon(ma_d righti = vreinterpretq_s32_u32(vshlq_u32(vsubq_u32(mid, side), shift4)); leftf = vmulq_f32(vcvtq_f32_s32(lefti), factor4); rightf = vmulq_f32(vcvtq_f32_s32(righti), factor4); - ma_dr_flac__vst2q_f32(pOutputSamples + i*8, vzipq_f32(leftf, rightf)); + drflac__vst2q_f32(pOutputSamples + i*8, vzipq_f32(leftf, rightf)); } for (i = (frameCount4 << 2); i < frameCount; ++i) { - ma_uint32 mid = pInputSamples0U32[i] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; - ma_uint32 side = pInputSamples1U32[i] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; + drflac_uint32 mid = pInputSamples0U32[i] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; + drflac_uint32 side = pInputSamples1U32[i] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; mid = (mid << 1) | (side & 0x01); - pOutputSamples[i*2+0] = (ma_int32)((mid + side) << shift) * factor; - pOutputSamples[i*2+1] = (ma_int32)((mid - side) << shift) * factor; + pOutputSamples[i*2+0] = (drflac_int32)((mid + side) << shift) * factor; + pOutputSamples[i*2+1] = (drflac_int32)((mid - side) << shift) * factor; } } } #endif -static MA_INLINE void ma_dr_flac_read_pcm_frames_f32__decode_mid_side(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, float* pOutputSamples) +static DRFLAC_INLINE void drflac_read_pcm_frames_f32__decode_mid_side(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, float* pOutputSamples) { -#if defined(MA_DR_FLAC_SUPPORT_SSE2) - if (ma_dr_flac__gIsSSE2Supported && pFlac->bitsPerSample <= 24) { - ma_dr_flac_read_pcm_frames_f32__decode_mid_side__sse2(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); +#if defined(DRFLAC_SUPPORT_SSE2) + if (drflac__gIsSSE2Supported && pFlac->bitsPerSample <= 24) { + drflac_read_pcm_frames_f32__decode_mid_side__sse2(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); } else -#elif defined(MA_DR_FLAC_SUPPORT_NEON) - if (ma_dr_flac__gIsNEONSupported && pFlac->bitsPerSample <= 24) { - ma_dr_flac_read_pcm_frames_f32__decode_mid_side__neon(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); +#elif defined(DRFLAC_SUPPORT_NEON) + if (drflac__gIsNEONSupported && pFlac->bitsPerSample <= 24) { + drflac_read_pcm_frames_f32__decode_mid_side__neon(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); } else #endif { #if 0 - ma_dr_flac_read_pcm_frames_f32__decode_mid_side__reference(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); + drflac_read_pcm_frames_f32__decode_mid_side__reference(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); #else - ma_dr_flac_read_pcm_frames_f32__decode_mid_side__scalar(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); + drflac_read_pcm_frames_f32__decode_mid_side__scalar(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); #endif } } #if 0 -static MA_INLINE void ma_dr_flac_read_pcm_frames_f32__decode_independent_stereo__reference(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, float* pOutputSamples) +static DRFLAC_INLINE void drflac_read_pcm_frames_f32__decode_independent_stereo__reference(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, float* pOutputSamples) { - for (ma_uint64 i = 0; i < frameCount; ++i) { - pOutputSamples[i*2+0] = (float)((ma_int32)((ma_uint32)pInputSamples0[i] << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample)) / 2147483648.0); - pOutputSamples[i*2+1] = (float)((ma_int32)((ma_uint32)pInputSamples1[i] << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample)) / 2147483648.0); + for (drflac_uint64 i = 0; i < frameCount; ++i) { + pOutputSamples[i*2+0] = (float)((drflac_int32)((drflac_uint32)pInputSamples0[i] << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample)) / 2147483648.0); + pOutputSamples[i*2+1] = (float)((drflac_int32)((drflac_uint32)pInputSamples1[i] << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample)) / 2147483648.0); } } #endif -static MA_INLINE void ma_dr_flac_read_pcm_frames_f32__decode_independent_stereo__scalar(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, float* pOutputSamples) +static DRFLAC_INLINE void drflac_read_pcm_frames_f32__decode_independent_stereo__scalar(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, float* pOutputSamples) { - ma_uint64 i; - ma_uint64 frameCount4 = frameCount >> 2; - const ma_uint32* pInputSamples0U32 = (const ma_uint32*)pInputSamples0; - const ma_uint32* pInputSamples1U32 = (const ma_uint32*)pInputSamples1; - ma_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; - ma_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; + drflac_uint64 i; + drflac_uint64 frameCount4 = frameCount >> 2; + const drflac_uint32* pInputSamples0U32 = (const drflac_uint32*)pInputSamples0; + const drflac_uint32* pInputSamples1U32 = (const drflac_uint32*)pInputSamples1; + drflac_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; + drflac_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; float factor = 1 / 2147483648.0; for (i = 0; i < frameCount4; ++i) { - ma_uint32 tempL0 = pInputSamples0U32[i*4+0] << shift0; - ma_uint32 tempL1 = pInputSamples0U32[i*4+1] << shift0; - ma_uint32 tempL2 = pInputSamples0U32[i*4+2] << shift0; - ma_uint32 tempL3 = pInputSamples0U32[i*4+3] << shift0; - ma_uint32 tempR0 = pInputSamples1U32[i*4+0] << shift1; - ma_uint32 tempR1 = pInputSamples1U32[i*4+1] << shift1; - ma_uint32 tempR2 = pInputSamples1U32[i*4+2] << shift1; - ma_uint32 tempR3 = pInputSamples1U32[i*4+3] << shift1; - pOutputSamples[i*8+0] = (ma_int32)tempL0 * factor; - pOutputSamples[i*8+1] = (ma_int32)tempR0 * factor; - pOutputSamples[i*8+2] = (ma_int32)tempL1 * factor; - pOutputSamples[i*8+3] = (ma_int32)tempR1 * factor; - pOutputSamples[i*8+4] = (ma_int32)tempL2 * factor; - pOutputSamples[i*8+5] = (ma_int32)tempR2 * factor; - pOutputSamples[i*8+6] = (ma_int32)tempL3 * factor; - pOutputSamples[i*8+7] = (ma_int32)tempR3 * factor; + drflac_uint32 tempL0 = pInputSamples0U32[i*4+0] << shift0; + drflac_uint32 tempL1 = pInputSamples0U32[i*4+1] << shift0; + drflac_uint32 tempL2 = pInputSamples0U32[i*4+2] << shift0; + drflac_uint32 tempL3 = pInputSamples0U32[i*4+3] << shift0; + drflac_uint32 tempR0 = pInputSamples1U32[i*4+0] << shift1; + drflac_uint32 tempR1 = pInputSamples1U32[i*4+1] << shift1; + drflac_uint32 tempR2 = pInputSamples1U32[i*4+2] << shift1; + drflac_uint32 tempR3 = pInputSamples1U32[i*4+3] << shift1; + pOutputSamples[i*8+0] = (drflac_int32)tempL0 * factor; + pOutputSamples[i*8+1] = (drflac_int32)tempR0 * factor; + pOutputSamples[i*8+2] = (drflac_int32)tempL1 * factor; + pOutputSamples[i*8+3] = (drflac_int32)tempR1 * factor; + pOutputSamples[i*8+4] = (drflac_int32)tempL2 * factor; + pOutputSamples[i*8+5] = (drflac_int32)tempR2 * factor; + pOutputSamples[i*8+6] = (drflac_int32)tempL3 * factor; + pOutputSamples[i*8+7] = (drflac_int32)tempR3 * factor; } for (i = (frameCount4 << 2); i < frameCount; ++i) { - pOutputSamples[i*2+0] = (ma_int32)(pInputSamples0U32[i] << shift0) * factor; - pOutputSamples[i*2+1] = (ma_int32)(pInputSamples1U32[i] << shift1) * factor; + pOutputSamples[i*2+0] = (drflac_int32)(pInputSamples0U32[i] << shift0) * factor; + pOutputSamples[i*2+1] = (drflac_int32)(pInputSamples1U32[i] << shift1) * factor; } } -#if defined(MA_DR_FLAC_SUPPORT_SSE2) -static MA_INLINE void ma_dr_flac_read_pcm_frames_f32__decode_independent_stereo__sse2(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, float* pOutputSamples) +#if defined(DRFLAC_SUPPORT_SSE2) +static DRFLAC_INLINE void drflac_read_pcm_frames_f32__decode_independent_stereo__sse2(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, float* pOutputSamples) { - ma_uint64 i; - ma_uint64 frameCount4 = frameCount >> 2; - const ma_uint32* pInputSamples0U32 = (const ma_uint32*)pInputSamples0; - const ma_uint32* pInputSamples1U32 = (const ma_uint32*)pInputSamples1; - ma_uint32 shift0 = (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample) - 8; - ma_uint32 shift1 = (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample) - 8; + drflac_uint64 i; + drflac_uint64 frameCount4 = frameCount >> 2; + const drflac_uint32* pInputSamples0U32 = (const drflac_uint32*)pInputSamples0; + const drflac_uint32* pInputSamples1U32 = (const drflac_uint32*)pInputSamples1; + drflac_uint32 shift0 = (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample) - 8; + drflac_uint32 shift1 = (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample) - 8; float factor = 1.0f / 8388608.0f; __m128 factor128 = _mm_set1_ps(factor); for (i = 0; i < frameCount4; ++i) { @@ -90152,20 +89538,20 @@ static MA_INLINE void ma_dr_flac_read_pcm_frames_f32__decode_independent_stereo_ _mm_storeu_ps(pOutputSamples + i*8 + 4, _mm_unpackhi_ps(leftf, rightf)); } for (i = (frameCount4 << 2); i < frameCount; ++i) { - pOutputSamples[i*2+0] = (ma_int32)(pInputSamples0U32[i] << shift0) * factor; - pOutputSamples[i*2+1] = (ma_int32)(pInputSamples1U32[i] << shift1) * factor; + pOutputSamples[i*2+0] = (drflac_int32)(pInputSamples0U32[i] << shift0) * factor; + pOutputSamples[i*2+1] = (drflac_int32)(pInputSamples1U32[i] << shift1) * factor; } } #endif -#if defined(MA_DR_FLAC_SUPPORT_NEON) -static MA_INLINE void ma_dr_flac_read_pcm_frames_f32__decode_independent_stereo__neon(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, float* pOutputSamples) +#if defined(DRFLAC_SUPPORT_NEON) +static DRFLAC_INLINE void drflac_read_pcm_frames_f32__decode_independent_stereo__neon(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, float* pOutputSamples) { - ma_uint64 i; - ma_uint64 frameCount4 = frameCount >> 2; - const ma_uint32* pInputSamples0U32 = (const ma_uint32*)pInputSamples0; - const ma_uint32* pInputSamples1U32 = (const ma_uint32*)pInputSamples1; - ma_uint32 shift0 = (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample) - 8; - ma_uint32 shift1 = (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample) - 8; + drflac_uint64 i; + drflac_uint64 frameCount4 = frameCount >> 2; + const drflac_uint32* pInputSamples0U32 = (const drflac_uint32*)pInputSamples0; + const drflac_uint32* pInputSamples1U32 = (const drflac_uint32*)pInputSamples1; + drflac_uint32 shift0 = (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample) - 8; + drflac_uint32 shift1 = (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample) - 8; float factor = 1.0f / 8388608.0f; float32x4_t factor4 = vdupq_n_f32(factor); int32x4_t shift0_4 = vdupq_n_s32(shift0); @@ -90179,87 +89565,87 @@ static MA_INLINE void ma_dr_flac_read_pcm_frames_f32__decode_independent_stereo_ righti = vreinterpretq_s32_u32(vshlq_u32(vld1q_u32(pInputSamples1U32 + i*4), shift1_4)); leftf = vmulq_f32(vcvtq_f32_s32(lefti), factor4); rightf = vmulq_f32(vcvtq_f32_s32(righti), factor4); - ma_dr_flac__vst2q_f32(pOutputSamples + i*8, vzipq_f32(leftf, rightf)); + drflac__vst2q_f32(pOutputSamples + i*8, vzipq_f32(leftf, rightf)); } for (i = (frameCount4 << 2); i < frameCount; ++i) { - pOutputSamples[i*2+0] = (ma_int32)(pInputSamples0U32[i] << shift0) * factor; - pOutputSamples[i*2+1] = (ma_int32)(pInputSamples1U32[i] << shift1) * factor; + pOutputSamples[i*2+0] = (drflac_int32)(pInputSamples0U32[i] << shift0) * factor; + pOutputSamples[i*2+1] = (drflac_int32)(pInputSamples1U32[i] << shift1) * factor; } } #endif -static MA_INLINE void ma_dr_flac_read_pcm_frames_f32__decode_independent_stereo(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, float* pOutputSamples) +static DRFLAC_INLINE void drflac_read_pcm_frames_f32__decode_independent_stereo(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, float* pOutputSamples) { -#if defined(MA_DR_FLAC_SUPPORT_SSE2) - if (ma_dr_flac__gIsSSE2Supported && pFlac->bitsPerSample <= 24) { - ma_dr_flac_read_pcm_frames_f32__decode_independent_stereo__sse2(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); +#if defined(DRFLAC_SUPPORT_SSE2) + if (drflac__gIsSSE2Supported && pFlac->bitsPerSample <= 24) { + drflac_read_pcm_frames_f32__decode_independent_stereo__sse2(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); } else -#elif defined(MA_DR_FLAC_SUPPORT_NEON) - if (ma_dr_flac__gIsNEONSupported && pFlac->bitsPerSample <= 24) { - ma_dr_flac_read_pcm_frames_f32__decode_independent_stereo__neon(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); +#elif defined(DRFLAC_SUPPORT_NEON) + if (drflac__gIsNEONSupported && pFlac->bitsPerSample <= 24) { + drflac_read_pcm_frames_f32__decode_independent_stereo__neon(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); } else #endif { #if 0 - ma_dr_flac_read_pcm_frames_f32__decode_independent_stereo__reference(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); + drflac_read_pcm_frames_f32__decode_independent_stereo__reference(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); #else - ma_dr_flac_read_pcm_frames_f32__decode_independent_stereo__scalar(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); + drflac_read_pcm_frames_f32__decode_independent_stereo__scalar(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); #endif } } -MA_API ma_uint64 ma_dr_flac_read_pcm_frames_f32(ma_dr_flac* pFlac, ma_uint64 framesToRead, float* pBufferOut) +DRFLAC_API drflac_uint64 drflac_read_pcm_frames_f32(drflac* pFlac, drflac_uint64 framesToRead, float* pBufferOut) { - ma_uint64 framesRead; - ma_uint32 unusedBitsPerSample; + drflac_uint64 framesRead; + drflac_uint32 unusedBitsPerSample; if (pFlac == NULL || framesToRead == 0) { return 0; } if (pBufferOut == NULL) { - return ma_dr_flac__seek_forward_by_pcm_frames(pFlac, framesToRead); + return drflac__seek_forward_by_pcm_frames(pFlac, framesToRead); } - MA_DR_FLAC_ASSERT(pFlac->bitsPerSample <= 32); + DRFLAC_ASSERT(pFlac->bitsPerSample <= 32); unusedBitsPerSample = 32 - pFlac->bitsPerSample; framesRead = 0; while (framesToRead > 0) { if (pFlac->currentFLACFrame.pcmFramesRemaining == 0) { - if (!ma_dr_flac__read_and_decode_next_flac_frame(pFlac)) { + if (!drflac__read_and_decode_next_flac_frame(pFlac)) { break; } } else { - unsigned int channelCount = ma_dr_flac__get_channel_count_from_channel_assignment(pFlac->currentFLACFrame.header.channelAssignment); - ma_uint64 iFirstPCMFrame = pFlac->currentFLACFrame.header.blockSizeInPCMFrames - pFlac->currentFLACFrame.pcmFramesRemaining; - ma_uint64 frameCountThisIteration = framesToRead; + unsigned int channelCount = drflac__get_channel_count_from_channel_assignment(pFlac->currentFLACFrame.header.channelAssignment); + drflac_uint64 iFirstPCMFrame = pFlac->currentFLACFrame.header.blockSizeInPCMFrames - pFlac->currentFLACFrame.pcmFramesRemaining; + drflac_uint64 frameCountThisIteration = framesToRead; if (frameCountThisIteration > pFlac->currentFLACFrame.pcmFramesRemaining) { frameCountThisIteration = pFlac->currentFLACFrame.pcmFramesRemaining; } if (channelCount == 2) { - const ma_int32* pDecodedSamples0 = pFlac->currentFLACFrame.subframes[0].pSamplesS32 + iFirstPCMFrame; - const ma_int32* pDecodedSamples1 = pFlac->currentFLACFrame.subframes[1].pSamplesS32 + iFirstPCMFrame; + const drflac_int32* pDecodedSamples0 = pFlac->currentFLACFrame.subframes[0].pSamplesS32 + iFirstPCMFrame; + const drflac_int32* pDecodedSamples1 = pFlac->currentFLACFrame.subframes[1].pSamplesS32 + iFirstPCMFrame; switch (pFlac->currentFLACFrame.header.channelAssignment) { - case MA_DR_FLAC_CHANNEL_ASSIGNMENT_LEFT_SIDE: + case DRFLAC_CHANNEL_ASSIGNMENT_LEFT_SIDE: { - ma_dr_flac_read_pcm_frames_f32__decode_left_side(pFlac, frameCountThisIteration, unusedBitsPerSample, pDecodedSamples0, pDecodedSamples1, pBufferOut); + drflac_read_pcm_frames_f32__decode_left_side(pFlac, frameCountThisIteration, unusedBitsPerSample, pDecodedSamples0, pDecodedSamples1, pBufferOut); } break; - case MA_DR_FLAC_CHANNEL_ASSIGNMENT_RIGHT_SIDE: + case DRFLAC_CHANNEL_ASSIGNMENT_RIGHT_SIDE: { - ma_dr_flac_read_pcm_frames_f32__decode_right_side(pFlac, frameCountThisIteration, unusedBitsPerSample, pDecodedSamples0, pDecodedSamples1, pBufferOut); + drflac_read_pcm_frames_f32__decode_right_side(pFlac, frameCountThisIteration, unusedBitsPerSample, pDecodedSamples0, pDecodedSamples1, pBufferOut); } break; - case MA_DR_FLAC_CHANNEL_ASSIGNMENT_MID_SIDE: + case DRFLAC_CHANNEL_ASSIGNMENT_MID_SIDE: { - ma_dr_flac_read_pcm_frames_f32__decode_mid_side(pFlac, frameCountThisIteration, unusedBitsPerSample, pDecodedSamples0, pDecodedSamples1, pBufferOut); + drflac_read_pcm_frames_f32__decode_mid_side(pFlac, frameCountThisIteration, unusedBitsPerSample, pDecodedSamples0, pDecodedSamples1, pBufferOut); } break; - case MA_DR_FLAC_CHANNEL_ASSIGNMENT_INDEPENDENT: + case DRFLAC_CHANNEL_ASSIGNMENT_INDEPENDENT: default: { - ma_dr_flac_read_pcm_frames_f32__decode_independent_stereo(pFlac, frameCountThisIteration, unusedBitsPerSample, pDecodedSamples0, pDecodedSamples1, pBufferOut); + drflac_read_pcm_frames_f32__decode_independent_stereo(pFlac, frameCountThisIteration, unusedBitsPerSample, pDecodedSamples0, pDecodedSamples1, pBufferOut); } break; } } else { - ma_uint64 i; + drflac_uint64 i; for (i = 0; i < frameCountThisIteration; ++i) { unsigned int j; for (j = 0; j < channelCount; ++j) { - ma_int32 sampleS32 = (ma_int32)((ma_uint32)(pFlac->currentFLACFrame.subframes[j].pSamplesS32[iFirstPCMFrame + i]) << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[j].wastedBitsPerSample)); + drflac_int32 sampleS32 = (drflac_int32)((drflac_uint32)(pFlac->currentFLACFrame.subframes[j].pSamplesS32[iFirstPCMFrame + i]) << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[j].wastedBitsPerSample)); pBufferOut[(i*channelCount)+j] = (float)(sampleS32 / 2147483648.0); } } @@ -90273,102 +89659,111 @@ MA_API ma_uint64 ma_dr_flac_read_pcm_frames_f32(ma_dr_flac* pFlac, ma_uint64 fra } return framesRead; } -MA_API ma_bool32 ma_dr_flac_seek_to_pcm_frame(ma_dr_flac* pFlac, ma_uint64 pcmFrameIndex) +DRFLAC_API drflac_bool32 drflac_seek_to_pcm_frame(drflac* pFlac, drflac_uint64 pcmFrameIndex) { if (pFlac == NULL) { - return MA_FALSE; + return DRFLAC_FALSE; } if (pFlac->currentPCMFrame == pcmFrameIndex) { - return MA_TRUE; + return DRFLAC_TRUE; } if (pFlac->firstFLACFramePosInBytes == 0) { - return MA_FALSE; + return DRFLAC_FALSE; } if (pcmFrameIndex == 0) { pFlac->currentPCMFrame = 0; - return ma_dr_flac__seek_to_first_frame(pFlac); + return drflac__seek_to_first_frame(pFlac); } else { - ma_bool32 wasSuccessful = MA_FALSE; - ma_uint64 originalPCMFrame = pFlac->currentPCMFrame; + drflac_bool32 wasSuccessful = DRFLAC_FALSE; + drflac_uint64 originalPCMFrame = pFlac->currentPCMFrame; if (pcmFrameIndex > pFlac->totalPCMFrameCount) { pcmFrameIndex = pFlac->totalPCMFrameCount; } if (pcmFrameIndex > pFlac->currentPCMFrame) { - ma_uint32 offset = (ma_uint32)(pcmFrameIndex - pFlac->currentPCMFrame); + drflac_uint32 offset = (drflac_uint32)(pcmFrameIndex - pFlac->currentPCMFrame); if (pFlac->currentFLACFrame.pcmFramesRemaining > offset) { pFlac->currentFLACFrame.pcmFramesRemaining -= offset; pFlac->currentPCMFrame = pcmFrameIndex; - return MA_TRUE; + return DRFLAC_TRUE; } } else { - ma_uint32 offsetAbs = (ma_uint32)(pFlac->currentPCMFrame - pcmFrameIndex); - ma_uint32 currentFLACFramePCMFrameCount = pFlac->currentFLACFrame.header.blockSizeInPCMFrames; - ma_uint32 currentFLACFramePCMFramesConsumed = currentFLACFramePCMFrameCount - pFlac->currentFLACFrame.pcmFramesRemaining; + drflac_uint32 offsetAbs = (drflac_uint32)(pFlac->currentPCMFrame - pcmFrameIndex); + drflac_uint32 currentFLACFramePCMFrameCount = pFlac->currentFLACFrame.header.blockSizeInPCMFrames; + drflac_uint32 currentFLACFramePCMFramesConsumed = currentFLACFramePCMFrameCount - pFlac->currentFLACFrame.pcmFramesRemaining; if (currentFLACFramePCMFramesConsumed > offsetAbs) { pFlac->currentFLACFrame.pcmFramesRemaining += offsetAbs; pFlac->currentPCMFrame = pcmFrameIndex; - return MA_TRUE; + return DRFLAC_TRUE; } } -#ifndef MA_DR_FLAC_NO_OGG - if (pFlac->container == ma_dr_flac_container_ogg) +#ifndef DR_FLAC_NO_OGG + if (pFlac->container == drflac_container_ogg) { - wasSuccessful = ma_dr_flac_ogg__seek_to_pcm_frame(pFlac, pcmFrameIndex); + wasSuccessful = drflac_ogg__seek_to_pcm_frame(pFlac, pcmFrameIndex); } else #endif { if (!pFlac->_noSeekTableSeek) { - wasSuccessful = ma_dr_flac__seek_to_pcm_frame__seek_table(pFlac, pcmFrameIndex); + wasSuccessful = drflac__seek_to_pcm_frame__seek_table(pFlac, pcmFrameIndex); } -#if !defined(MA_DR_FLAC_NO_CRC) +#if !defined(DR_FLAC_NO_CRC) if (!wasSuccessful && !pFlac->_noBinarySearchSeek && pFlac->totalPCMFrameCount > 0) { - wasSuccessful = ma_dr_flac__seek_to_pcm_frame__binary_search(pFlac, pcmFrameIndex); + wasSuccessful = drflac__seek_to_pcm_frame__binary_search(pFlac, pcmFrameIndex); } #endif if (!wasSuccessful && !pFlac->_noBruteForceSeek) { - wasSuccessful = ma_dr_flac__seek_to_pcm_frame__brute_force(pFlac, pcmFrameIndex); + wasSuccessful = drflac__seek_to_pcm_frame__brute_force(pFlac, pcmFrameIndex); } } if (wasSuccessful) { pFlac->currentPCMFrame = pcmFrameIndex; } else { - if (ma_dr_flac_seek_to_pcm_frame(pFlac, originalPCMFrame) == MA_FALSE) { - ma_dr_flac_seek_to_pcm_frame(pFlac, 0); + if (drflac_seek_to_pcm_frame(pFlac, originalPCMFrame) == DRFLAC_FALSE) { + drflac_seek_to_pcm_frame(pFlac, 0); } } return wasSuccessful; } } -#define MA_DR_FLAC_DEFINE_FULL_READ_AND_CLOSE(extension, type) \ -static type* ma_dr_flac__full_read_and_close_ ## extension (ma_dr_flac* pFlac, unsigned int* channelsOut, unsigned int* sampleRateOut, ma_uint64* totalPCMFrameCountOut)\ +#if defined(SIZE_MAX) + #define DRFLAC_SIZE_MAX SIZE_MAX +#else + #if defined(DRFLAC_64BIT) + #define DRFLAC_SIZE_MAX ((drflac_uint64)0xFFFFFFFFFFFFFFFF) + #else + #define DRFLAC_SIZE_MAX 0xFFFFFFFF + #endif +#endif +#define DRFLAC_DEFINE_FULL_READ_AND_CLOSE(extension, type) \ +static type* drflac__full_read_and_close_ ## extension (drflac* pFlac, unsigned int* channelsOut, unsigned int* sampleRateOut, drflac_uint64* totalPCMFrameCountOut)\ { \ type* pSampleData = NULL; \ - ma_uint64 totalPCMFrameCount; \ + drflac_uint64 totalPCMFrameCount; \ \ - MA_DR_FLAC_ASSERT(pFlac != NULL); \ + DRFLAC_ASSERT(pFlac != NULL); \ \ totalPCMFrameCount = pFlac->totalPCMFrameCount; \ \ if (totalPCMFrameCount == 0) { \ type buffer[4096]; \ - ma_uint64 pcmFramesRead; \ + drflac_uint64 pcmFramesRead; \ size_t sampleDataBufferSize = sizeof(buffer); \ \ - pSampleData = (type*)ma_dr_flac__malloc_from_callbacks(sampleDataBufferSize, &pFlac->allocationCallbacks); \ + pSampleData = (type*)drflac__malloc_from_callbacks(sampleDataBufferSize, &pFlac->allocationCallbacks); \ if (pSampleData == NULL) { \ goto on_error; \ } \ \ - while ((pcmFramesRead = (ma_uint64)ma_dr_flac_read_pcm_frames_##extension(pFlac, sizeof(buffer)/sizeof(buffer[0])/pFlac->channels, buffer)) > 0) { \ + while ((pcmFramesRead = (drflac_uint64)drflac_read_pcm_frames_##extension(pFlac, sizeof(buffer)/sizeof(buffer[0])/pFlac->channels, buffer)) > 0) { \ if (((totalPCMFrameCount + pcmFramesRead) * pFlac->channels * sizeof(type)) > sampleDataBufferSize) { \ type* pNewSampleData; \ size_t newSampleDataBufferSize; \ \ newSampleDataBufferSize = sampleDataBufferSize * 2; \ - pNewSampleData = (type*)ma_dr_flac__realloc_from_callbacks(pSampleData, newSampleDataBufferSize, sampleDataBufferSize, &pFlac->allocationCallbacks); \ + pNewSampleData = (type*)drflac__realloc_from_callbacks(pSampleData, newSampleDataBufferSize, sampleDataBufferSize, &pFlac->allocationCallbacks); \ if (pNewSampleData == NULL) { \ - ma_dr_flac__free_from_callbacks(pSampleData, &pFlac->allocationCallbacks); \ + drflac__free_from_callbacks(pSampleData, &pFlac->allocationCallbacks); \ goto on_error; \ } \ \ @@ -90376,43 +89771,43 @@ static type* ma_dr_flac__full_read_and_close_ ## extension (ma_dr_flac* pFlac, u pSampleData = pNewSampleData; \ } \ \ - MA_DR_FLAC_COPY_MEMORY(pSampleData + (totalPCMFrameCount*pFlac->channels), buffer, (size_t)(pcmFramesRead*pFlac->channels*sizeof(type))); \ + DRFLAC_COPY_MEMORY(pSampleData + (totalPCMFrameCount*pFlac->channels), buffer, (size_t)(pcmFramesRead*pFlac->channels*sizeof(type))); \ totalPCMFrameCount += pcmFramesRead; \ } \ \ \ - MA_DR_FLAC_ZERO_MEMORY(pSampleData + (totalPCMFrameCount*pFlac->channels), (size_t)(sampleDataBufferSize - totalPCMFrameCount*pFlac->channels*sizeof(type))); \ + DRFLAC_ZERO_MEMORY(pSampleData + (totalPCMFrameCount*pFlac->channels), (size_t)(sampleDataBufferSize - totalPCMFrameCount*pFlac->channels*sizeof(type))); \ } else { \ - ma_uint64 dataSize = totalPCMFrameCount*pFlac->channels*sizeof(type); \ - if (dataSize > (ma_uint64)MA_SIZE_MAX) { \ + drflac_uint64 dataSize = totalPCMFrameCount*pFlac->channels*sizeof(type); \ + if (dataSize > (drflac_uint64)DRFLAC_SIZE_MAX) { \ goto on_error; \ } \ \ - pSampleData = (type*)ma_dr_flac__malloc_from_callbacks((size_t)dataSize, &pFlac->allocationCallbacks); \ + pSampleData = (type*)drflac__malloc_from_callbacks((size_t)dataSize, &pFlac->allocationCallbacks); \ if (pSampleData == NULL) { \ goto on_error; \ } \ \ - totalPCMFrameCount = ma_dr_flac_read_pcm_frames_##extension(pFlac, pFlac->totalPCMFrameCount, pSampleData); \ + totalPCMFrameCount = drflac_read_pcm_frames_##extension(pFlac, pFlac->totalPCMFrameCount, pSampleData); \ } \ \ if (sampleRateOut) *sampleRateOut = pFlac->sampleRate; \ if (channelsOut) *channelsOut = pFlac->channels; \ if (totalPCMFrameCountOut) *totalPCMFrameCountOut = totalPCMFrameCount; \ \ - ma_dr_flac_close(pFlac); \ + drflac_close(pFlac); \ return pSampleData; \ \ on_error: \ - ma_dr_flac_close(pFlac); \ + drflac_close(pFlac); \ return NULL; \ } -MA_DR_FLAC_DEFINE_FULL_READ_AND_CLOSE(s32, ma_int32) -MA_DR_FLAC_DEFINE_FULL_READ_AND_CLOSE(s16, ma_int16) -MA_DR_FLAC_DEFINE_FULL_READ_AND_CLOSE(f32, float) -MA_API ma_int32* ma_dr_flac_open_and_read_pcm_frames_s32(ma_dr_flac_read_proc onRead, ma_dr_flac_seek_proc onSeek, void* pUserData, unsigned int* channelsOut, unsigned int* sampleRateOut, ma_uint64* totalPCMFrameCountOut, const ma_allocation_callbacks* pAllocationCallbacks) +DRFLAC_DEFINE_FULL_READ_AND_CLOSE(s32, drflac_int32) +DRFLAC_DEFINE_FULL_READ_AND_CLOSE(s16, drflac_int16) +DRFLAC_DEFINE_FULL_READ_AND_CLOSE(f32, float) +DRFLAC_API drflac_int32* drflac_open_and_read_pcm_frames_s32(drflac_read_proc onRead, drflac_seek_proc onSeek, void* pUserData, unsigned int* channelsOut, unsigned int* sampleRateOut, drflac_uint64* totalPCMFrameCountOut, const drflac_allocation_callbacks* pAllocationCallbacks) { - ma_dr_flac* pFlac; + drflac* pFlac; if (channelsOut) { *channelsOut = 0; } @@ -90422,15 +89817,15 @@ MA_API ma_int32* ma_dr_flac_open_and_read_pcm_frames_s32(ma_dr_flac_read_proc on if (totalPCMFrameCountOut) { *totalPCMFrameCountOut = 0; } - pFlac = ma_dr_flac_open(onRead, onSeek, pUserData, pAllocationCallbacks); + pFlac = drflac_open(onRead, onSeek, pUserData, pAllocationCallbacks); if (pFlac == NULL) { return NULL; } - return ma_dr_flac__full_read_and_close_s32(pFlac, channelsOut, sampleRateOut, totalPCMFrameCountOut); + return drflac__full_read_and_close_s32(pFlac, channelsOut, sampleRateOut, totalPCMFrameCountOut); } -MA_API ma_int16* ma_dr_flac_open_and_read_pcm_frames_s16(ma_dr_flac_read_proc onRead, ma_dr_flac_seek_proc onSeek, void* pUserData, unsigned int* channelsOut, unsigned int* sampleRateOut, ma_uint64* totalPCMFrameCountOut, const ma_allocation_callbacks* pAllocationCallbacks) +DRFLAC_API drflac_int16* drflac_open_and_read_pcm_frames_s16(drflac_read_proc onRead, drflac_seek_proc onSeek, void* pUserData, unsigned int* channelsOut, unsigned int* sampleRateOut, drflac_uint64* totalPCMFrameCountOut, const drflac_allocation_callbacks* pAllocationCallbacks) { - ma_dr_flac* pFlac; + drflac* pFlac; if (channelsOut) { *channelsOut = 0; } @@ -90440,15 +89835,15 @@ MA_API ma_int16* ma_dr_flac_open_and_read_pcm_frames_s16(ma_dr_flac_read_proc on if (totalPCMFrameCountOut) { *totalPCMFrameCountOut = 0; } - pFlac = ma_dr_flac_open(onRead, onSeek, pUserData, pAllocationCallbacks); + pFlac = drflac_open(onRead, onSeek, pUserData, pAllocationCallbacks); if (pFlac == NULL) { return NULL; } - return ma_dr_flac__full_read_and_close_s16(pFlac, channelsOut, sampleRateOut, totalPCMFrameCountOut); + return drflac__full_read_and_close_s16(pFlac, channelsOut, sampleRateOut, totalPCMFrameCountOut); } -MA_API float* ma_dr_flac_open_and_read_pcm_frames_f32(ma_dr_flac_read_proc onRead, ma_dr_flac_seek_proc onSeek, void* pUserData, unsigned int* channelsOut, unsigned int* sampleRateOut, ma_uint64* totalPCMFrameCountOut, const ma_allocation_callbacks* pAllocationCallbacks) +DRFLAC_API float* drflac_open_and_read_pcm_frames_f32(drflac_read_proc onRead, drflac_seek_proc onSeek, void* pUserData, unsigned int* channelsOut, unsigned int* sampleRateOut, drflac_uint64* totalPCMFrameCountOut, const drflac_allocation_callbacks* pAllocationCallbacks) { - ma_dr_flac* pFlac; + drflac* pFlac; if (channelsOut) { *channelsOut = 0; } @@ -90458,16 +89853,16 @@ MA_API float* ma_dr_flac_open_and_read_pcm_frames_f32(ma_dr_flac_read_proc onRea if (totalPCMFrameCountOut) { *totalPCMFrameCountOut = 0; } - pFlac = ma_dr_flac_open(onRead, onSeek, pUserData, pAllocationCallbacks); + pFlac = drflac_open(onRead, onSeek, pUserData, pAllocationCallbacks); if (pFlac == NULL) { return NULL; } - return ma_dr_flac__full_read_and_close_f32(pFlac, channelsOut, sampleRateOut, totalPCMFrameCountOut); + return drflac__full_read_and_close_f32(pFlac, channelsOut, sampleRateOut, totalPCMFrameCountOut); } -#ifndef MA_DR_FLAC_NO_STDIO -MA_API ma_int32* ma_dr_flac_open_file_and_read_pcm_frames_s32(const char* filename, unsigned int* channels, unsigned int* sampleRate, ma_uint64* totalPCMFrameCount, const ma_allocation_callbacks* pAllocationCallbacks) +#ifndef DR_FLAC_NO_STDIO +DRFLAC_API drflac_int32* drflac_open_file_and_read_pcm_frames_s32(const char* filename, unsigned int* channels, unsigned int* sampleRate, drflac_uint64* totalPCMFrameCount, const drflac_allocation_callbacks* pAllocationCallbacks) { - ma_dr_flac* pFlac; + drflac* pFlac; if (sampleRate) { *sampleRate = 0; } @@ -90477,15 +89872,15 @@ MA_API ma_int32* ma_dr_flac_open_file_and_read_pcm_frames_s32(const char* filena if (totalPCMFrameCount) { *totalPCMFrameCount = 0; } - pFlac = ma_dr_flac_open_file(filename, pAllocationCallbacks); + pFlac = drflac_open_file(filename, pAllocationCallbacks); if (pFlac == NULL) { return NULL; } - return ma_dr_flac__full_read_and_close_s32(pFlac, channels, sampleRate, totalPCMFrameCount); + return drflac__full_read_and_close_s32(pFlac, channels, sampleRate, totalPCMFrameCount); } -MA_API ma_int16* ma_dr_flac_open_file_and_read_pcm_frames_s16(const char* filename, unsigned int* channels, unsigned int* sampleRate, ma_uint64* totalPCMFrameCount, const ma_allocation_callbacks* pAllocationCallbacks) +DRFLAC_API drflac_int16* drflac_open_file_and_read_pcm_frames_s16(const char* filename, unsigned int* channels, unsigned int* sampleRate, drflac_uint64* totalPCMFrameCount, const drflac_allocation_callbacks* pAllocationCallbacks) { - ma_dr_flac* pFlac; + drflac* pFlac; if (sampleRate) { *sampleRate = 0; } @@ -90495,15 +89890,15 @@ MA_API ma_int16* ma_dr_flac_open_file_and_read_pcm_frames_s16(const char* filena if (totalPCMFrameCount) { *totalPCMFrameCount = 0; } - pFlac = ma_dr_flac_open_file(filename, pAllocationCallbacks); + pFlac = drflac_open_file(filename, pAllocationCallbacks); if (pFlac == NULL) { return NULL; } - return ma_dr_flac__full_read_and_close_s16(pFlac, channels, sampleRate, totalPCMFrameCount); + return drflac__full_read_and_close_s16(pFlac, channels, sampleRate, totalPCMFrameCount); } -MA_API float* ma_dr_flac_open_file_and_read_pcm_frames_f32(const char* filename, unsigned int* channels, unsigned int* sampleRate, ma_uint64* totalPCMFrameCount, const ma_allocation_callbacks* pAllocationCallbacks) +DRFLAC_API float* drflac_open_file_and_read_pcm_frames_f32(const char* filename, unsigned int* channels, unsigned int* sampleRate, drflac_uint64* totalPCMFrameCount, const drflac_allocation_callbacks* pAllocationCallbacks) { - ma_dr_flac* pFlac; + drflac* pFlac; if (sampleRate) { *sampleRate = 0; } @@ -90513,16 +89908,16 @@ MA_API float* ma_dr_flac_open_file_and_read_pcm_frames_f32(const char* filename, if (totalPCMFrameCount) { *totalPCMFrameCount = 0; } - pFlac = ma_dr_flac_open_file(filename, pAllocationCallbacks); + pFlac = drflac_open_file(filename, pAllocationCallbacks); if (pFlac == NULL) { return NULL; } - return ma_dr_flac__full_read_and_close_f32(pFlac, channels, sampleRate, totalPCMFrameCount); + return drflac__full_read_and_close_f32(pFlac, channels, sampleRate, totalPCMFrameCount); } #endif -MA_API ma_int32* ma_dr_flac_open_memory_and_read_pcm_frames_s32(const void* data, size_t dataSize, unsigned int* channels, unsigned int* sampleRate, ma_uint64* totalPCMFrameCount, const ma_allocation_callbacks* pAllocationCallbacks) +DRFLAC_API drflac_int32* drflac_open_memory_and_read_pcm_frames_s32(const void* data, size_t dataSize, unsigned int* channels, unsigned int* sampleRate, drflac_uint64* totalPCMFrameCount, const drflac_allocation_callbacks* pAllocationCallbacks) { - ma_dr_flac* pFlac; + drflac* pFlac; if (sampleRate) { *sampleRate = 0; } @@ -90532,15 +89927,15 @@ MA_API ma_int32* ma_dr_flac_open_memory_and_read_pcm_frames_s32(const void* data if (totalPCMFrameCount) { *totalPCMFrameCount = 0; } - pFlac = ma_dr_flac_open_memory(data, dataSize, pAllocationCallbacks); + pFlac = drflac_open_memory(data, dataSize, pAllocationCallbacks); if (pFlac == NULL) { return NULL; } - return ma_dr_flac__full_read_and_close_s32(pFlac, channels, sampleRate, totalPCMFrameCount); + return drflac__full_read_and_close_s32(pFlac, channels, sampleRate, totalPCMFrameCount); } -MA_API ma_int16* ma_dr_flac_open_memory_and_read_pcm_frames_s16(const void* data, size_t dataSize, unsigned int* channels, unsigned int* sampleRate, ma_uint64* totalPCMFrameCount, const ma_allocation_callbacks* pAllocationCallbacks) +DRFLAC_API drflac_int16* drflac_open_memory_and_read_pcm_frames_s16(const void* data, size_t dataSize, unsigned int* channels, unsigned int* sampleRate, drflac_uint64* totalPCMFrameCount, const drflac_allocation_callbacks* pAllocationCallbacks) { - ma_dr_flac* pFlac; + drflac* pFlac; if (sampleRate) { *sampleRate = 0; } @@ -90550,15 +89945,15 @@ MA_API ma_int16* ma_dr_flac_open_memory_and_read_pcm_frames_s16(const void* data if (totalPCMFrameCount) { *totalPCMFrameCount = 0; } - pFlac = ma_dr_flac_open_memory(data, dataSize, pAllocationCallbacks); + pFlac = drflac_open_memory(data, dataSize, pAllocationCallbacks); if (pFlac == NULL) { return NULL; } - return ma_dr_flac__full_read_and_close_s16(pFlac, channels, sampleRate, totalPCMFrameCount); + return drflac__full_read_and_close_s16(pFlac, channels, sampleRate, totalPCMFrameCount); } -MA_API float* ma_dr_flac_open_memory_and_read_pcm_frames_f32(const void* data, size_t dataSize, unsigned int* channels, unsigned int* sampleRate, ma_uint64* totalPCMFrameCount, const ma_allocation_callbacks* pAllocationCallbacks) +DRFLAC_API float* drflac_open_memory_and_read_pcm_frames_f32(const void* data, size_t dataSize, unsigned int* channels, unsigned int* sampleRate, drflac_uint64* totalPCMFrameCount, const drflac_allocation_callbacks* pAllocationCallbacks) { - ma_dr_flac* pFlac; + drflac* pFlac; if (sampleRate) { *sampleRate = 0; } @@ -90568,21 +89963,21 @@ MA_API float* ma_dr_flac_open_memory_and_read_pcm_frames_f32(const void* data, s if (totalPCMFrameCount) { *totalPCMFrameCount = 0; } - pFlac = ma_dr_flac_open_memory(data, dataSize, pAllocationCallbacks); + pFlac = drflac_open_memory(data, dataSize, pAllocationCallbacks); if (pFlac == NULL) { return NULL; } - return ma_dr_flac__full_read_and_close_f32(pFlac, channels, sampleRate, totalPCMFrameCount); + return drflac__full_read_and_close_f32(pFlac, channels, sampleRate, totalPCMFrameCount); } -MA_API void ma_dr_flac_free(void* p, const ma_allocation_callbacks* pAllocationCallbacks) +DRFLAC_API void drflac_free(void* p, const drflac_allocation_callbacks* pAllocationCallbacks) { if (pAllocationCallbacks != NULL) { - ma_dr_flac__free_from_callbacks(p, pAllocationCallbacks); + drflac__free_from_callbacks(p, pAllocationCallbacks); } else { - ma_dr_flac__free_default(p, NULL); + drflac__free_default(p, NULL); } } -MA_API void ma_dr_flac_init_vorbis_comment_iterator(ma_dr_flac_vorbis_comment_iterator* pIter, ma_uint32 commentCount, const void* pComments) +DRFLAC_API void drflac_init_vorbis_comment_iterator(drflac_vorbis_comment_iterator* pIter, drflac_uint32 commentCount, const void* pComments) { if (pIter == NULL) { return; @@ -90590,9 +89985,9 @@ MA_API void ma_dr_flac_init_vorbis_comment_iterator(ma_dr_flac_vorbis_comment_it pIter->countRemaining = commentCount; pIter->pRunningData = (const char*)pComments; } -MA_API const char* ma_dr_flac_next_vorbis_comment(ma_dr_flac_vorbis_comment_iterator* pIter, ma_uint32* pCommentLengthOut) +DRFLAC_API const char* drflac_next_vorbis_comment(drflac_vorbis_comment_iterator* pIter, drflac_uint32* pCommentLengthOut) { - ma_int32 length; + drflac_int32 length; const char* pComment; if (pCommentLengthOut) { *pCommentLengthOut = 0; @@ -90600,7 +89995,7 @@ MA_API const char* ma_dr_flac_next_vorbis_comment(ma_dr_flac_vorbis_comment_iter if (pIter == NULL || pIter->countRemaining == 0 || pIter->pRunningData == NULL) { return NULL; } - length = ma_dr_flac__le2host_32_ptr_unaligned(pIter->pRunningData); + length = drflac__le2host_32_ptr_unaligned(pIter->pRunningData); pIter->pRunningData += 4; pComment = pIter->pRunningData; pIter->pRunningData += length; @@ -90610,7 +90005,7 @@ MA_API const char* ma_dr_flac_next_vorbis_comment(ma_dr_flac_vorbis_comment_iter } return pComment; } -MA_API void ma_dr_flac_init_cuesheet_track_iterator(ma_dr_flac_cuesheet_track_iterator* pIter, ma_uint32 trackCount, const void* pTrackData) +DRFLAC_API void drflac_init_cuesheet_track_iterator(drflac_cuesheet_track_iterator* pIter, drflac_uint32 trackCount, const void* pTrackData) { if (pIter == NULL) { return; @@ -90618,127 +90013,127 @@ MA_API void ma_dr_flac_init_cuesheet_track_iterator(ma_dr_flac_cuesheet_track_it pIter->countRemaining = trackCount; pIter->pRunningData = (const char*)pTrackData; } -MA_API ma_bool32 ma_dr_flac_next_cuesheet_track(ma_dr_flac_cuesheet_track_iterator* pIter, ma_dr_flac_cuesheet_track* pCuesheetTrack) +DRFLAC_API drflac_bool32 drflac_next_cuesheet_track(drflac_cuesheet_track_iterator* pIter, drflac_cuesheet_track* pCuesheetTrack) { - ma_dr_flac_cuesheet_track cuesheetTrack; + drflac_cuesheet_track cuesheetTrack; const char* pRunningData; - ma_uint64 offsetHi; - ma_uint64 offsetLo; + drflac_uint64 offsetHi; + drflac_uint64 offsetLo; if (pIter == NULL || pIter->countRemaining == 0 || pIter->pRunningData == NULL) { - return MA_FALSE; + return DRFLAC_FALSE; } pRunningData = pIter->pRunningData; - offsetHi = ma_dr_flac__be2host_32(*(const ma_uint32*)pRunningData); pRunningData += 4; - offsetLo = ma_dr_flac__be2host_32(*(const ma_uint32*)pRunningData); pRunningData += 4; + offsetHi = drflac__be2host_32(*(const drflac_uint32*)pRunningData); pRunningData += 4; + offsetLo = drflac__be2host_32(*(const drflac_uint32*)pRunningData); pRunningData += 4; cuesheetTrack.offset = offsetLo | (offsetHi << 32); cuesheetTrack.trackNumber = pRunningData[0]; pRunningData += 1; - MA_DR_FLAC_COPY_MEMORY(cuesheetTrack.ISRC, pRunningData, sizeof(cuesheetTrack.ISRC)); pRunningData += 12; + DRFLAC_COPY_MEMORY(cuesheetTrack.ISRC, pRunningData, sizeof(cuesheetTrack.ISRC)); pRunningData += 12; cuesheetTrack.isAudio = (pRunningData[0] & 0x80) != 0; cuesheetTrack.preEmphasis = (pRunningData[0] & 0x40) != 0; pRunningData += 14; cuesheetTrack.indexCount = pRunningData[0]; pRunningData += 1; - cuesheetTrack.pIndexPoints = (const ma_dr_flac_cuesheet_track_index*)pRunningData; pRunningData += cuesheetTrack.indexCount * sizeof(ma_dr_flac_cuesheet_track_index); + cuesheetTrack.pIndexPoints = (const drflac_cuesheet_track_index*)pRunningData; pRunningData += cuesheetTrack.indexCount * sizeof(drflac_cuesheet_track_index); pIter->pRunningData = pRunningData; pIter->countRemaining -= 1; if (pCuesheetTrack) { *pCuesheetTrack = cuesheetTrack; } - return MA_TRUE; + return DRFLAC_TRUE; } #if defined(__clang__) || (defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6))) #pragma GCC diagnostic pop #endif #endif /* dr_flac_c end */ -#endif /* MA_DR_FLAC_IMPLEMENTATION */ +#endif /* DRFLAC_IMPLEMENTATION */ #endif /* MA_NO_FLAC */ #if !defined(MA_NO_MP3) && !defined(MA_NO_DECODING) -#if !defined(MA_DR_MP3_IMPLEMENTATION) +#if !defined(DR_MP3_IMPLEMENTATION) && !defined(DRMP3_IMPLEMENTATION) /* For backwards compatibility. Will be removed in version 0.11 for cleanliness. */ /* dr_mp3_c begin */ -#ifndef ma_dr_mp3_c -#define ma_dr_mp3_c +#ifndef dr_mp3_c +#define dr_mp3_c #include #include #include -MA_API void ma_dr_mp3_version(ma_uint32* pMajor, ma_uint32* pMinor, ma_uint32* pRevision) +DRMP3_API void drmp3_version(drmp3_uint32* pMajor, drmp3_uint32* pMinor, drmp3_uint32* pRevision) { if (pMajor) { - *pMajor = MA_DR_MP3_VERSION_MAJOR; + *pMajor = DRMP3_VERSION_MAJOR; } if (pMinor) { - *pMinor = MA_DR_MP3_VERSION_MINOR; + *pMinor = DRMP3_VERSION_MINOR; } if (pRevision) { - *pRevision = MA_DR_MP3_VERSION_REVISION; + *pRevision = DRMP3_VERSION_REVISION; } } -MA_API const char* ma_dr_mp3_version_string(void) +DRMP3_API const char* drmp3_version_string(void) { - return MA_DR_MP3_VERSION_STRING; + return DRMP3_VERSION_STRING; } #if defined(__TINYC__) -#define MA_DR_MP3_NO_SIMD -#endif -#define MA_DR_MP3_OFFSET_PTR(p, offset) ((void*)((ma_uint8*)(p) + (offset))) -#define MA_DR_MP3_MAX_FREE_FORMAT_FRAME_SIZE 2304 -#ifndef MA_DR_MP3_MAX_FRAME_SYNC_MATCHES -#define MA_DR_MP3_MAX_FRAME_SYNC_MATCHES 10 -#endif -#define MA_DR_MP3_MAX_L3_FRAME_PAYLOAD_BYTES MA_DR_MP3_MAX_FREE_FORMAT_FRAME_SIZE -#define MA_DR_MP3_MAX_BITRESERVOIR_BYTES 511 -#define MA_DR_MP3_SHORT_BLOCK_TYPE 2 -#define MA_DR_MP3_STOP_BLOCK_TYPE 3 -#define MA_DR_MP3_MODE_MONO 3 -#define MA_DR_MP3_MODE_JOINT_STEREO 1 -#define MA_DR_MP3_HDR_SIZE 4 -#define MA_DR_MP3_HDR_IS_MONO(h) (((h[3]) & 0xC0) == 0xC0) -#define MA_DR_MP3_HDR_IS_MS_STEREO(h) (((h[3]) & 0xE0) == 0x60) -#define MA_DR_MP3_HDR_IS_FREE_FORMAT(h) (((h[2]) & 0xF0) == 0) -#define MA_DR_MP3_HDR_IS_CRC(h) (!((h[1]) & 1)) -#define MA_DR_MP3_HDR_TEST_PADDING(h) ((h[2]) & 0x2) -#define MA_DR_MP3_HDR_TEST_MPEG1(h) ((h[1]) & 0x8) -#define MA_DR_MP3_HDR_TEST_NOT_MPEG25(h) ((h[1]) & 0x10) -#define MA_DR_MP3_HDR_TEST_I_STEREO(h) ((h[3]) & 0x10) -#define MA_DR_MP3_HDR_TEST_MS_STEREO(h) ((h[3]) & 0x20) -#define MA_DR_MP3_HDR_GET_STEREO_MODE(h) (((h[3]) >> 6) & 3) -#define MA_DR_MP3_HDR_GET_STEREO_MODE_EXT(h) (((h[3]) >> 4) & 3) -#define MA_DR_MP3_HDR_GET_LAYER(h) (((h[1]) >> 1) & 3) -#define MA_DR_MP3_HDR_GET_BITRATE(h) ((h[2]) >> 4) -#define MA_DR_MP3_HDR_GET_SAMPLE_RATE(h) (((h[2]) >> 2) & 3) -#define MA_DR_MP3_HDR_GET_MY_SAMPLE_RATE(h) (MA_DR_MP3_HDR_GET_SAMPLE_RATE(h) + (((h[1] >> 3) & 1) + ((h[1] >> 4) & 1))*3) -#define MA_DR_MP3_HDR_IS_FRAME_576(h) ((h[1] & 14) == 2) -#define MA_DR_MP3_HDR_IS_LAYER_1(h) ((h[1] & 6) == 6) -#define MA_DR_MP3_BITS_DEQUANTIZER_OUT -1 -#define MA_DR_MP3_MAX_SCF (255 + MA_DR_MP3_BITS_DEQUANTIZER_OUT*4 - 210) -#define MA_DR_MP3_MAX_SCFI ((MA_DR_MP3_MAX_SCF + 3) & ~3) -#define MA_DR_MP3_MIN(a, b) ((a) > (b) ? (b) : (a)) -#define MA_DR_MP3_MAX(a, b) ((a) < (b) ? (b) : (a)) -#if !defined(MA_DR_MP3_NO_SIMD) -#if !defined(MA_DR_MP3_ONLY_SIMD) && (defined(_M_X64) || defined(__x86_64__) || defined(__aarch64__) || defined(_M_ARM64) || defined(_M_ARM64EC)) -#define MA_DR_MP3_ONLY_SIMD +#define DR_MP3_NO_SIMD +#endif +#define DRMP3_OFFSET_PTR(p, offset) ((void*)((drmp3_uint8*)(p) + (offset))) +#define DRMP3_MAX_FREE_FORMAT_FRAME_SIZE 2304 +#ifndef DRMP3_MAX_FRAME_SYNC_MATCHES +#define DRMP3_MAX_FRAME_SYNC_MATCHES 10 +#endif +#define DRMP3_MAX_L3_FRAME_PAYLOAD_BYTES DRMP3_MAX_FREE_FORMAT_FRAME_SIZE +#define DRMP3_MAX_BITRESERVOIR_BYTES 511 +#define DRMP3_SHORT_BLOCK_TYPE 2 +#define DRMP3_STOP_BLOCK_TYPE 3 +#define DRMP3_MODE_MONO 3 +#define DRMP3_MODE_JOINT_STEREO 1 +#define DRMP3_HDR_SIZE 4 +#define DRMP3_HDR_IS_MONO(h) (((h[3]) & 0xC0) == 0xC0) +#define DRMP3_HDR_IS_MS_STEREO(h) (((h[3]) & 0xE0) == 0x60) +#define DRMP3_HDR_IS_FREE_FORMAT(h) (((h[2]) & 0xF0) == 0) +#define DRMP3_HDR_IS_CRC(h) (!((h[1]) & 1)) +#define DRMP3_HDR_TEST_PADDING(h) ((h[2]) & 0x2) +#define DRMP3_HDR_TEST_MPEG1(h) ((h[1]) & 0x8) +#define DRMP3_HDR_TEST_NOT_MPEG25(h) ((h[1]) & 0x10) +#define DRMP3_HDR_TEST_I_STEREO(h) ((h[3]) & 0x10) +#define DRMP3_HDR_TEST_MS_STEREO(h) ((h[3]) & 0x20) +#define DRMP3_HDR_GET_STEREO_MODE(h) (((h[3]) >> 6) & 3) +#define DRMP3_HDR_GET_STEREO_MODE_EXT(h) (((h[3]) >> 4) & 3) +#define DRMP3_HDR_GET_LAYER(h) (((h[1]) >> 1) & 3) +#define DRMP3_HDR_GET_BITRATE(h) ((h[2]) >> 4) +#define DRMP3_HDR_GET_SAMPLE_RATE(h) (((h[2]) >> 2) & 3) +#define DRMP3_HDR_GET_MY_SAMPLE_RATE(h) (DRMP3_HDR_GET_SAMPLE_RATE(h) + (((h[1] >> 3) & 1) + ((h[1] >> 4) & 1))*3) +#define DRMP3_HDR_IS_FRAME_576(h) ((h[1] & 14) == 2) +#define DRMP3_HDR_IS_LAYER_1(h) ((h[1] & 6) == 6) +#define DRMP3_BITS_DEQUANTIZER_OUT -1 +#define DRMP3_MAX_SCF (255 + DRMP3_BITS_DEQUANTIZER_OUT*4 - 210) +#define DRMP3_MAX_SCFI ((DRMP3_MAX_SCF + 3) & ~3) +#define DRMP3_MIN(a, b) ((a) > (b) ? (b) : (a)) +#define DRMP3_MAX(a, b) ((a) < (b) ? (b) : (a)) +#if !defined(DR_MP3_NO_SIMD) +#if !defined(DR_MP3_ONLY_SIMD) && (defined(_M_X64) || defined(__x86_64__) || defined(__aarch64__) || defined(_M_ARM64)) +#define DR_MP3_ONLY_SIMD #endif #if ((defined(_MSC_VER) && _MSC_VER >= 1400) && defined(_M_X64)) || ((defined(__i386) || defined(_M_IX86) || defined(__i386__) || defined(__x86_64__)) && ((defined(_M_IX86_FP) && _M_IX86_FP == 2) || defined(__SSE2__))) #if defined(_MSC_VER) #include #endif #include -#define MA_DR_MP3_HAVE_SSE 1 -#define MA_DR_MP3_HAVE_SIMD 1 -#define MA_DR_MP3_VSTORE _mm_storeu_ps -#define MA_DR_MP3_VLD _mm_loadu_ps -#define MA_DR_MP3_VSET _mm_set1_ps -#define MA_DR_MP3_VADD _mm_add_ps -#define MA_DR_MP3_VSUB _mm_sub_ps -#define MA_DR_MP3_VMUL _mm_mul_ps -#define MA_DR_MP3_VMAC(a, x, y) _mm_add_ps(a, _mm_mul_ps(x, y)) -#define MA_DR_MP3_VMSB(a, x, y) _mm_sub_ps(a, _mm_mul_ps(x, y)) -#define MA_DR_MP3_VMUL_S(x, s) _mm_mul_ps(x, _mm_set1_ps(s)) -#define MA_DR_MP3_VREV(x) _mm_shuffle_ps(x, x, _MM_SHUFFLE(0, 1, 2, 3)) -typedef __m128 ma_dr_mp3_f4; -#if defined(_MSC_VER) || defined(MA_DR_MP3_ONLY_SIMD) -#define ma_dr_mp3_cpuid __cpuid +#define DRMP3_HAVE_SSE 1 +#define DRMP3_HAVE_SIMD 1 +#define DRMP3_VSTORE _mm_storeu_ps +#define DRMP3_VLD _mm_loadu_ps +#define DRMP3_VSET _mm_set1_ps +#define DRMP3_VADD _mm_add_ps +#define DRMP3_VSUB _mm_sub_ps +#define DRMP3_VMUL _mm_mul_ps +#define DRMP3_VMAC(a, x, y) _mm_add_ps(a, _mm_mul_ps(x, y)) +#define DRMP3_VMSB(a, x, y) _mm_sub_ps(a, _mm_mul_ps(x, y)) +#define DRMP3_VMUL_S(x, s) _mm_mul_ps(x, _mm_set1_ps(s)) +#define DRMP3_VREV(x) _mm_shuffle_ps(x, x, _MM_SHUFFLE(0, 1, 2, 3)) +typedef __m128 drmp3_f4; +#if defined(_MSC_VER) || defined(DR_MP3_ONLY_SIMD) +#define drmp3_cpuid __cpuid #else -static __inline__ __attribute__((always_inline)) void ma_dr_mp3_cpuid(int CPUInfo[], const int InfoType) +static __inline__ __attribute__((always_inline)) void drmp3_cpuid(int CPUInfo[], const int InfoType) { #if defined(__PIC__) __asm__ __volatile__( @@ -90762,9 +90157,9 @@ static __inline__ __attribute__((always_inline)) void ma_dr_mp3_cpuid(int CPUInf #endif } #endif -static int ma_dr_mp3_have_simd(void) +static int drmp3_have_simd(void) { -#ifdef MA_DR_MP3_ONLY_SIMD +#ifdef DR_MP3_ONLY_SIMD return 1; #else static int g_have_simd; @@ -90776,10 +90171,10 @@ static int ma_dr_mp3_have_simd(void) #endif if (g_have_simd) goto end; - ma_dr_mp3_cpuid(CPUInfo, 0); + drmp3_cpuid(CPUInfo, 0); if (CPUInfo[0] > 0) { - ma_dr_mp3_cpuid(CPUInfo, 1); + drmp3_cpuid(CPUInfo, 1); g_have_simd = (CPUInfo[3] & (1 << 26)) + 1; return g_have_simd - 1; } @@ -90787,110 +90182,110 @@ static int ma_dr_mp3_have_simd(void) return g_have_simd - 1; #endif } -#elif defined(__ARM_NEON) || defined(__aarch64__) || defined(_M_ARM64) || defined(_M_ARM64EC) +#elif defined(__ARM_NEON) || defined(__aarch64__) || defined(_M_ARM64) #include -#define MA_DR_MP3_HAVE_SSE 0 -#define MA_DR_MP3_HAVE_SIMD 1 -#define MA_DR_MP3_VSTORE vst1q_f32 -#define MA_DR_MP3_VLD vld1q_f32 -#define MA_DR_MP3_VSET vmovq_n_f32 -#define MA_DR_MP3_VADD vaddq_f32 -#define MA_DR_MP3_VSUB vsubq_f32 -#define MA_DR_MP3_VMUL vmulq_f32 -#define MA_DR_MP3_VMAC(a, x, y) vmlaq_f32(a, x, y) -#define MA_DR_MP3_VMSB(a, x, y) vmlsq_f32(a, x, y) -#define MA_DR_MP3_VMUL_S(x, s) vmulq_f32(x, vmovq_n_f32(s)) -#define MA_DR_MP3_VREV(x) vcombine_f32(vget_high_f32(vrev64q_f32(x)), vget_low_f32(vrev64q_f32(x))) -typedef float32x4_t ma_dr_mp3_f4; -static int ma_dr_mp3_have_simd(void) +#define DRMP3_HAVE_SSE 0 +#define DRMP3_HAVE_SIMD 1 +#define DRMP3_VSTORE vst1q_f32 +#define DRMP3_VLD vld1q_f32 +#define DRMP3_VSET vmovq_n_f32 +#define DRMP3_VADD vaddq_f32 +#define DRMP3_VSUB vsubq_f32 +#define DRMP3_VMUL vmulq_f32 +#define DRMP3_VMAC(a, x, y) vmlaq_f32(a, x, y) +#define DRMP3_VMSB(a, x, y) vmlsq_f32(a, x, y) +#define DRMP3_VMUL_S(x, s) vmulq_f32(x, vmovq_n_f32(s)) +#define DRMP3_VREV(x) vcombine_f32(vget_high_f32(vrev64q_f32(x)), vget_low_f32(vrev64q_f32(x))) +typedef float32x4_t drmp3_f4; +static int drmp3_have_simd(void) { return 1; } #else -#define MA_DR_MP3_HAVE_SSE 0 -#define MA_DR_MP3_HAVE_SIMD 0 -#ifdef MA_DR_MP3_ONLY_SIMD -#error MA_DR_MP3_ONLY_SIMD used, but SSE/NEON not enabled +#define DRMP3_HAVE_SSE 0 +#define DRMP3_HAVE_SIMD 0 +#ifdef DR_MP3_ONLY_SIMD +#error DR_MP3_ONLY_SIMD used, but SSE/NEON not enabled #endif #endif #else -#define MA_DR_MP3_HAVE_SIMD 0 +#define DRMP3_HAVE_SIMD 0 #endif -#if defined(__ARM_ARCH) && (__ARM_ARCH >= 6) && !defined(__aarch64__) && !defined(_M_ARM64) && !defined(_M_ARM64EC) && !defined(__ARM_ARCH_6M__) -#define MA_DR_MP3_HAVE_ARMV6 1 -static __inline__ __attribute__((always_inline)) ma_int32 ma_dr_mp3_clip_int16_arm(ma_int32 a) +#if defined(__ARM_ARCH) && (__ARM_ARCH >= 6) && !defined(__aarch64__) && !defined(_M_ARM64) +#define DRMP3_HAVE_ARMV6 1 +static __inline__ __attribute__((always_inline)) drmp3_int32 drmp3_clip_int16_arm(drmp3_int32 a) { - ma_int32 x = 0; + drmp3_int32 x = 0; __asm__ ("ssat %0, #16, %1" : "=r"(x) : "r"(a)); return x; } #else -#define MA_DR_MP3_HAVE_ARMV6 0 +#define DRMP3_HAVE_ARMV6 0 #endif -#ifndef MA_DR_MP3_ASSERT +#ifndef DRMP3_ASSERT #include -#define MA_DR_MP3_ASSERT(expression) assert(expression) +#define DRMP3_ASSERT(expression) assert(expression) #endif -#ifndef MA_DR_MP3_COPY_MEMORY -#define MA_DR_MP3_COPY_MEMORY(dst, src, sz) memcpy((dst), (src), (sz)) +#ifndef DRMP3_COPY_MEMORY +#define DRMP3_COPY_MEMORY(dst, src, sz) memcpy((dst), (src), (sz)) #endif -#ifndef MA_DR_MP3_MOVE_MEMORY -#define MA_DR_MP3_MOVE_MEMORY(dst, src, sz) memmove((dst), (src), (sz)) +#ifndef DRMP3_MOVE_MEMORY +#define DRMP3_MOVE_MEMORY(dst, src, sz) memmove((dst), (src), (sz)) #endif -#ifndef MA_DR_MP3_ZERO_MEMORY -#define MA_DR_MP3_ZERO_MEMORY(p, sz) memset((p), 0, (sz)) +#ifndef DRMP3_ZERO_MEMORY +#define DRMP3_ZERO_MEMORY(p, sz) memset((p), 0, (sz)) #endif -#define MA_DR_MP3_ZERO_OBJECT(p) MA_DR_MP3_ZERO_MEMORY((p), sizeof(*(p))) -#ifndef MA_DR_MP3_MALLOC -#define MA_DR_MP3_MALLOC(sz) malloc((sz)) +#define DRMP3_ZERO_OBJECT(p) DRMP3_ZERO_MEMORY((p), sizeof(*(p))) +#ifndef DRMP3_MALLOC +#define DRMP3_MALLOC(sz) malloc((sz)) #endif -#ifndef MA_DR_MP3_REALLOC -#define MA_DR_MP3_REALLOC(p, sz) realloc((p), (sz)) +#ifndef DRMP3_REALLOC +#define DRMP3_REALLOC(p, sz) realloc((p), (sz)) #endif -#ifndef MA_DR_MP3_FREE -#define MA_DR_MP3_FREE(p) free((p)) +#ifndef DRMP3_FREE +#define DRMP3_FREE(p) free((p)) #endif typedef struct { - const ma_uint8 *buf; + const drmp3_uint8 *buf; int pos, limit; -} ma_dr_mp3_bs; +} drmp3_bs; typedef struct { float scf[3*64]; - ma_uint8 total_bands, stereo_bands, bitalloc[64], scfcod[64]; -} ma_dr_mp3_L12_scale_info; + drmp3_uint8 total_bands, stereo_bands, bitalloc[64], scfcod[64]; +} drmp3_L12_scale_info; typedef struct { - ma_uint8 tab_offset, code_tab_width, band_count; -} ma_dr_mp3_L12_subband_alloc; + drmp3_uint8 tab_offset, code_tab_width, band_count; +} drmp3_L12_subband_alloc; typedef struct { - const ma_uint8 *sfbtab; - ma_uint16 part_23_length, big_values, scalefac_compress; - ma_uint8 global_gain, block_type, mixed_block_flag, n_long_sfb, n_short_sfb; - ma_uint8 table_select[3], region_count[3], subblock_gain[3]; - ma_uint8 preflag, scalefac_scale, count1_table, scfsi; -} ma_dr_mp3_L3_gr_info; + const drmp3_uint8 *sfbtab; + drmp3_uint16 part_23_length, big_values, scalefac_compress; + drmp3_uint8 global_gain, block_type, mixed_block_flag, n_long_sfb, n_short_sfb; + drmp3_uint8 table_select[3], region_count[3], subblock_gain[3]; + drmp3_uint8 preflag, scalefac_scale, count1_table, scfsi; +} drmp3_L3_gr_info; typedef struct { - ma_dr_mp3_bs bs; - ma_uint8 maindata[MA_DR_MP3_MAX_BITRESERVOIR_BYTES + MA_DR_MP3_MAX_L3_FRAME_PAYLOAD_BYTES]; - ma_dr_mp3_L3_gr_info gr_info[4]; + drmp3_bs bs; + drmp3_uint8 maindata[DRMP3_MAX_BITRESERVOIR_BYTES + DRMP3_MAX_L3_FRAME_PAYLOAD_BYTES]; + drmp3_L3_gr_info gr_info[4]; float grbuf[2][576], scf[40], syn[18 + 15][2*32]; - ma_uint8 ist_pos[2][39]; -} ma_dr_mp3dec_scratch; -static void ma_dr_mp3_bs_init(ma_dr_mp3_bs *bs, const ma_uint8 *data, int bytes) + drmp3_uint8 ist_pos[2][39]; +} drmp3dec_scratch; +static void drmp3_bs_init(drmp3_bs *bs, const drmp3_uint8 *data, int bytes) { bs->buf = data; bs->pos = 0; bs->limit = bytes*8; } -static ma_uint32 ma_dr_mp3_bs_get_bits(ma_dr_mp3_bs *bs, int n) +static drmp3_uint32 drmp3_bs_get_bits(drmp3_bs *bs, int n) { - ma_uint32 next, cache = 0, s = bs->pos & 7; + drmp3_uint32 next, cache = 0, s = bs->pos & 7; int shl = n + s; - const ma_uint8 *p = bs->buf + (bs->pos >> 3); + const drmp3_uint8 *p = bs->buf + (bs->pos >> 3); if ((bs->pos += n) > bs->limit) return 0; next = *p++ & (255 >> s); @@ -90901,72 +90296,72 @@ static ma_uint32 ma_dr_mp3_bs_get_bits(ma_dr_mp3_bs *bs, int n) } return cache | (next >> -shl); } -static int ma_dr_mp3_hdr_valid(const ma_uint8 *h) +static int drmp3_hdr_valid(const drmp3_uint8 *h) { return h[0] == 0xff && ((h[1] & 0xF0) == 0xf0 || (h[1] & 0xFE) == 0xe2) && - (MA_DR_MP3_HDR_GET_LAYER(h) != 0) && - (MA_DR_MP3_HDR_GET_BITRATE(h) != 15) && - (MA_DR_MP3_HDR_GET_SAMPLE_RATE(h) != 3); + (DRMP3_HDR_GET_LAYER(h) != 0) && + (DRMP3_HDR_GET_BITRATE(h) != 15) && + (DRMP3_HDR_GET_SAMPLE_RATE(h) != 3); } -static int ma_dr_mp3_hdr_compare(const ma_uint8 *h1, const ma_uint8 *h2) +static int drmp3_hdr_compare(const drmp3_uint8 *h1, const drmp3_uint8 *h2) { - return ma_dr_mp3_hdr_valid(h2) && + return drmp3_hdr_valid(h2) && ((h1[1] ^ h2[1]) & 0xFE) == 0 && ((h1[2] ^ h2[2]) & 0x0C) == 0 && - !(MA_DR_MP3_HDR_IS_FREE_FORMAT(h1) ^ MA_DR_MP3_HDR_IS_FREE_FORMAT(h2)); + !(DRMP3_HDR_IS_FREE_FORMAT(h1) ^ DRMP3_HDR_IS_FREE_FORMAT(h2)); } -static unsigned ma_dr_mp3_hdr_bitrate_kbps(const ma_uint8 *h) +static unsigned drmp3_hdr_bitrate_kbps(const drmp3_uint8 *h) { - static const ma_uint8 halfrate[2][3][15] = { + static const drmp3_uint8 halfrate[2][3][15] = { { { 0,4,8,12,16,20,24,28,32,40,48,56,64,72,80 }, { 0,4,8,12,16,20,24,28,32,40,48,56,64,72,80 }, { 0,16,24,28,32,40,48,56,64,72,80,88,96,112,128 } }, { { 0,16,20,24,28,32,40,48,56,64,80,96,112,128,160 }, { 0,16,24,28,32,40,48,56,64,80,96,112,128,160,192 }, { 0,16,32,48,64,80,96,112,128,144,160,176,192,208,224 } }, }; - return 2*halfrate[!!MA_DR_MP3_HDR_TEST_MPEG1(h)][MA_DR_MP3_HDR_GET_LAYER(h) - 1][MA_DR_MP3_HDR_GET_BITRATE(h)]; + return 2*halfrate[!!DRMP3_HDR_TEST_MPEG1(h)][DRMP3_HDR_GET_LAYER(h) - 1][DRMP3_HDR_GET_BITRATE(h)]; } -static unsigned ma_dr_mp3_hdr_sample_rate_hz(const ma_uint8 *h) +static unsigned drmp3_hdr_sample_rate_hz(const drmp3_uint8 *h) { static const unsigned g_hz[3] = { 44100, 48000, 32000 }; - return g_hz[MA_DR_MP3_HDR_GET_SAMPLE_RATE(h)] >> (int)!MA_DR_MP3_HDR_TEST_MPEG1(h) >> (int)!MA_DR_MP3_HDR_TEST_NOT_MPEG25(h); + return g_hz[DRMP3_HDR_GET_SAMPLE_RATE(h)] >> (int)!DRMP3_HDR_TEST_MPEG1(h) >> (int)!DRMP3_HDR_TEST_NOT_MPEG25(h); } -static unsigned ma_dr_mp3_hdr_frame_samples(const ma_uint8 *h) +static unsigned drmp3_hdr_frame_samples(const drmp3_uint8 *h) { - return MA_DR_MP3_HDR_IS_LAYER_1(h) ? 384 : (1152 >> (int)MA_DR_MP3_HDR_IS_FRAME_576(h)); + return DRMP3_HDR_IS_LAYER_1(h) ? 384 : (1152 >> (int)DRMP3_HDR_IS_FRAME_576(h)); } -static int ma_dr_mp3_hdr_frame_bytes(const ma_uint8 *h, int free_format_size) +static int drmp3_hdr_frame_bytes(const drmp3_uint8 *h, int free_format_size) { - int frame_bytes = ma_dr_mp3_hdr_frame_samples(h)*ma_dr_mp3_hdr_bitrate_kbps(h)*125/ma_dr_mp3_hdr_sample_rate_hz(h); - if (MA_DR_MP3_HDR_IS_LAYER_1(h)) + int frame_bytes = drmp3_hdr_frame_samples(h)*drmp3_hdr_bitrate_kbps(h)*125/drmp3_hdr_sample_rate_hz(h); + if (DRMP3_HDR_IS_LAYER_1(h)) { frame_bytes &= ~3; } return frame_bytes ? frame_bytes : free_format_size; } -static int ma_dr_mp3_hdr_padding(const ma_uint8 *h) +static int drmp3_hdr_padding(const drmp3_uint8 *h) { - return MA_DR_MP3_HDR_TEST_PADDING(h) ? (MA_DR_MP3_HDR_IS_LAYER_1(h) ? 4 : 1) : 0; + return DRMP3_HDR_TEST_PADDING(h) ? (DRMP3_HDR_IS_LAYER_1(h) ? 4 : 1) : 0; } -#ifndef MA_DR_MP3_ONLY_MP3 -static const ma_dr_mp3_L12_subband_alloc *ma_dr_mp3_L12_subband_alloc_table(const ma_uint8 *hdr, ma_dr_mp3_L12_scale_info *sci) +#ifndef DR_MP3_ONLY_MP3 +static const drmp3_L12_subband_alloc *drmp3_L12_subband_alloc_table(const drmp3_uint8 *hdr, drmp3_L12_scale_info *sci) { - const ma_dr_mp3_L12_subband_alloc *alloc; - int mode = MA_DR_MP3_HDR_GET_STEREO_MODE(hdr); - int nbands, stereo_bands = (mode == MA_DR_MP3_MODE_MONO) ? 0 : (mode == MA_DR_MP3_MODE_JOINT_STEREO) ? (MA_DR_MP3_HDR_GET_STEREO_MODE_EXT(hdr) << 2) + 4 : 32; - if (MA_DR_MP3_HDR_IS_LAYER_1(hdr)) + const drmp3_L12_subband_alloc *alloc; + int mode = DRMP3_HDR_GET_STEREO_MODE(hdr); + int nbands, stereo_bands = (mode == DRMP3_MODE_MONO) ? 0 : (mode == DRMP3_MODE_JOINT_STEREO) ? (DRMP3_HDR_GET_STEREO_MODE_EXT(hdr) << 2) + 4 : 32; + if (DRMP3_HDR_IS_LAYER_1(hdr)) { - static const ma_dr_mp3_L12_subband_alloc g_alloc_L1[] = { { 76, 4, 32 } }; + static const drmp3_L12_subband_alloc g_alloc_L1[] = { { 76, 4, 32 } }; alloc = g_alloc_L1; nbands = 32; - } else if (!MA_DR_MP3_HDR_TEST_MPEG1(hdr)) + } else if (!DRMP3_HDR_TEST_MPEG1(hdr)) { - static const ma_dr_mp3_L12_subband_alloc g_alloc_L2M2[] = { { 60, 4, 4 }, { 44, 3, 7 }, { 44, 2, 19 } }; + static const drmp3_L12_subband_alloc g_alloc_L2M2[] = { { 60, 4, 4 }, { 44, 3, 7 }, { 44, 2, 19 } }; alloc = g_alloc_L2M2; nbands = 30; } else { - static const ma_dr_mp3_L12_subband_alloc g_alloc_L2M1[] = { { 0, 4, 3 }, { 16, 4, 8 }, { 32, 3, 12 }, { 40, 2, 7 } }; - int sample_rate_idx = MA_DR_MP3_HDR_GET_SAMPLE_RATE(hdr); - unsigned kbps = ma_dr_mp3_hdr_bitrate_kbps(hdr) >> (int)(mode != MA_DR_MP3_MODE_MONO); + static const drmp3_L12_subband_alloc g_alloc_L2M1[] = { { 0, 4, 3 }, { 16, 4, 8 }, { 32, 3, 12 }, { 40, 2, 7 } }; + int sample_rate_idx = DRMP3_HDR_GET_SAMPLE_RATE(hdr); + unsigned kbps = drmp3_hdr_bitrate_kbps(hdr) >> (int)(mode != DRMP3_MODE_MONO); if (!kbps) { kbps = 192; @@ -90975,7 +90370,7 @@ static const ma_dr_mp3_L12_subband_alloc *ma_dr_mp3_L12_subband_alloc_table(cons nbands = 27; if (kbps < 56) { - static const ma_dr_mp3_L12_subband_alloc g_alloc_L2M1_lowrate[] = { { 44, 4, 2 }, { 44, 3, 10 } }; + static const drmp3_L12_subband_alloc g_alloc_L2M1_lowrate[] = { { 44, 4, 2 }, { 44, 3, 10 } }; alloc = g_alloc_L2M1_lowrate; nbands = sample_rate_idx == 2 ? 12 : 8; } else if (kbps >= 96 && sample_rate_idx != 1) @@ -90983,15 +90378,15 @@ static const ma_dr_mp3_L12_subband_alloc *ma_dr_mp3_L12_subband_alloc_table(cons nbands = 30; } } - sci->total_bands = (ma_uint8)nbands; - sci->stereo_bands = (ma_uint8)MA_DR_MP3_MIN(stereo_bands, nbands); + sci->total_bands = (drmp3_uint8)nbands; + sci->stereo_bands = (drmp3_uint8)DRMP3_MIN(stereo_bands, nbands); return alloc; } -static void ma_dr_mp3_L12_read_scalefactors(ma_dr_mp3_bs *bs, ma_uint8 *pba, ma_uint8 *scfcod, int bands, float *scf) +static void drmp3_L12_read_scalefactors(drmp3_bs *bs, drmp3_uint8 *pba, drmp3_uint8 *scfcod, int bands, float *scf) { static const float g_deq_L12[18*3] = { -#define MA_DR_MP3_DQ(x) 9.53674316e-07f/x, 7.56931807e-07f/x, 6.00777173e-07f/x - MA_DR_MP3_DQ(3),MA_DR_MP3_DQ(7),MA_DR_MP3_DQ(15),MA_DR_MP3_DQ(31),MA_DR_MP3_DQ(63),MA_DR_MP3_DQ(127),MA_DR_MP3_DQ(255),MA_DR_MP3_DQ(511),MA_DR_MP3_DQ(1023),MA_DR_MP3_DQ(2047),MA_DR_MP3_DQ(4095),MA_DR_MP3_DQ(8191),MA_DR_MP3_DQ(16383),MA_DR_MP3_DQ(32767),MA_DR_MP3_DQ(65535),MA_DR_MP3_DQ(3),MA_DR_MP3_DQ(5),MA_DR_MP3_DQ(9) +#define DRMP3_DQ(x) 9.53674316e-07f/x, 7.56931807e-07f/x, 6.00777173e-07f/x + DRMP3_DQ(3),DRMP3_DQ(7),DRMP3_DQ(15),DRMP3_DQ(31),DRMP3_DQ(63),DRMP3_DQ(127),DRMP3_DQ(255),DRMP3_DQ(511),DRMP3_DQ(1023),DRMP3_DQ(2047),DRMP3_DQ(4095),DRMP3_DQ(8191),DRMP3_DQ(16383),DRMP3_DQ(32767),DRMP3_DQ(65535),DRMP3_DQ(3),DRMP3_DQ(5),DRMP3_DQ(9) }; int i, m; for (i = 0; i < bands; i++) @@ -91003,16 +90398,16 @@ static void ma_dr_mp3_L12_read_scalefactors(ma_dr_mp3_bs *bs, ma_uint8 *pba, ma_ { if (mask & m) { - int b = ma_dr_mp3_bs_get_bits(bs, 6); + int b = drmp3_bs_get_bits(bs, 6); s = g_deq_L12[ba*3 - 6 + b % 3]*(int)(1 << 21 >> b/3); } *scf++ = s; } } } -static void ma_dr_mp3_L12_read_scale_info(const ma_uint8 *hdr, ma_dr_mp3_bs *bs, ma_dr_mp3_L12_scale_info *sci) +static void drmp3_L12_read_scale_info(const drmp3_uint8 *hdr, drmp3_bs *bs, drmp3_L12_scale_info *sci) { - static const ma_uint8 g_bitalloc_code_tab[] = { + static const drmp3_uint8 g_bitalloc_code_tab[] = { 0,17, 3, 4, 5,6,7, 8,9,10,11,12,13,14,15,16, 0,17,18, 3,19,4,5, 6,7, 8, 9,10,11,12,13,16, 0,17,18, 3,19,4,5,16, @@ -91021,12 +90416,12 @@ static void ma_dr_mp3_L12_read_scale_info(const ma_uint8 *hdr, ma_dr_mp3_bs *bs, 0,17,18, 3,19,4,5, 6,7, 8, 9,10,11,12,13,14, 0, 2, 3, 4, 5,6,7, 8,9,10,11,12,13,14,15,16 }; - const ma_dr_mp3_L12_subband_alloc *subband_alloc = ma_dr_mp3_L12_subband_alloc_table(hdr, sci); + const drmp3_L12_subband_alloc *subband_alloc = drmp3_L12_subband_alloc_table(hdr, sci); int i, k = 0, ba_bits = 0; - const ma_uint8 *ba_code_tab = g_bitalloc_code_tab; + const drmp3_uint8 *ba_code_tab = g_bitalloc_code_tab; for (i = 0; i < sci->total_bands; i++) { - ma_uint8 ba; + drmp3_uint8 ba; if (i == k) { k += subband_alloc->band_count; @@ -91034,25 +90429,25 @@ static void ma_dr_mp3_L12_read_scale_info(const ma_uint8 *hdr, ma_dr_mp3_bs *bs, ba_code_tab = g_bitalloc_code_tab + subband_alloc->tab_offset; subband_alloc++; } - ba = ba_code_tab[ma_dr_mp3_bs_get_bits(bs, ba_bits)]; + ba = ba_code_tab[drmp3_bs_get_bits(bs, ba_bits)]; sci->bitalloc[2*i] = ba; if (i < sci->stereo_bands) { - ba = ba_code_tab[ma_dr_mp3_bs_get_bits(bs, ba_bits)]; + ba = ba_code_tab[drmp3_bs_get_bits(bs, ba_bits)]; } sci->bitalloc[2*i + 1] = sci->stereo_bands ? ba : 0; } for (i = 0; i < 2*sci->total_bands; i++) { - sci->scfcod[i] = (ma_uint8)(sci->bitalloc[i] ? MA_DR_MP3_HDR_IS_LAYER_1(hdr) ? 2 : ma_dr_mp3_bs_get_bits(bs, 2) : 6); + sci->scfcod[i] = (drmp3_uint8)(sci->bitalloc[i] ? DRMP3_HDR_IS_LAYER_1(hdr) ? 2 : drmp3_bs_get_bits(bs, 2) : 6); } - ma_dr_mp3_L12_read_scalefactors(bs, sci->bitalloc, sci->scfcod, sci->total_bands*2, sci->scf); + drmp3_L12_read_scalefactors(bs, sci->bitalloc, sci->scfcod, sci->total_bands*2, sci->scf); for (i = sci->stereo_bands; i < sci->total_bands; i++) { sci->bitalloc[2*i + 1] = 0; } } -static int ma_dr_mp3_L12_dequantize_granule(float *grbuf, ma_dr_mp3_bs *bs, ma_dr_mp3_L12_scale_info *sci, int group_size) +static int drmp3_L12_dequantize_granule(float *grbuf, drmp3_bs *bs, drmp3_L12_scale_info *sci, int group_size) { int i, j, k, choff = 576; for (j = 0; j < 4; j++) @@ -91068,12 +90463,12 @@ static int ma_dr_mp3_L12_dequantize_granule(float *grbuf, ma_dr_mp3_bs *bs, ma_d int half = (1 << (ba - 1)) - 1; for (k = 0; k < group_size; k++) { - dst[k] = (float)((int)ma_dr_mp3_bs_get_bits(bs, ba) - half); + dst[k] = (float)((int)drmp3_bs_get_bits(bs, ba) - half); } } else { unsigned mod = (2 << (ba - 17)) + 1; - unsigned code = ma_dr_mp3_bs_get_bits(bs, mod + 2 - (mod >> 3)); + unsigned code = drmp3_bs_get_bits(bs, mod + 2 - (mod >> 3)); for (k = 0; k < group_size; k++, code /= mod) { dst[k] = (float)((int)(code % mod - mod/2)); @@ -91086,10 +90481,10 @@ static int ma_dr_mp3_L12_dequantize_granule(float *grbuf, ma_dr_mp3_bs *bs, ma_d } return group_size*4; } -static void ma_dr_mp3_L12_apply_scf_384(ma_dr_mp3_L12_scale_info *sci, const float *scf, float *dst) +static void drmp3_L12_apply_scf_384(drmp3_L12_scale_info *sci, const float *scf, float *dst) { int i, k; - MA_DR_MP3_COPY_MEMORY(dst + 576 + sci->stereo_bands*18, dst + sci->stereo_bands*18, (sci->total_bands - sci->stereo_bands)*18*sizeof(float)); + DRMP3_COPY_MEMORY(dst + 576 + sci->stereo_bands*18, dst + sci->stereo_bands*18, (sci->total_bands - sci->stereo_bands)*18*sizeof(float)); for (i = 0; i < sci->total_bands; i++, dst += 18, scf += 6) { for (k = 0; k < 12; k++) @@ -91100,9 +90495,9 @@ static void ma_dr_mp3_L12_apply_scf_384(ma_dr_mp3_L12_scale_info *sci, const flo } } #endif -static int ma_dr_mp3_L3_read_side_info(ma_dr_mp3_bs *bs, ma_dr_mp3_L3_gr_info *gr, const ma_uint8 *hdr) +static int drmp3_L3_read_side_info(drmp3_bs *bs, drmp3_L3_gr_info *gr, const drmp3_uint8 *hdr) { - static const ma_uint8 g_scf_long[8][23] = { + static const drmp3_uint8 g_scf_long[8][23] = { { 6,6,6,6,6,6,8,10,12,14,16,20,24,28,32,38,46,52,60,68,58,54,0 }, { 12,12,12,12,12,12,16,20,24,28,32,40,48,56,64,76,90,2,2,2,2,2,0 }, { 6,6,6,6,6,6,8,10,12,14,16,20,24,28,32,38,46,52,60,68,58,54,0 }, @@ -91112,7 +90507,7 @@ static int ma_dr_mp3_L3_read_side_info(ma_dr_mp3_bs *bs, ma_dr_mp3_L3_gr_info *g { 4,4,4,4,4,4,6,6,6,8,10,12,16,18,22,28,34,40,46,54,54,192,0 }, { 4,4,4,4,4,4,6,6,8,10,12,16,20,24,30,38,46,56,68,84,102,26,0 } }; - static const ma_uint8 g_scf_short[8][40] = { + static const drmp3_uint8 g_scf_short[8][40] = { { 4,4,4,4,4,4,4,4,4,6,6,6,8,8,8,10,10,10,12,12,12,14,14,14,18,18,18,24,24,24,30,30,30,40,40,40,18,18,18,0 }, { 8,8,8,8,8,8,8,8,8,12,12,12,16,16,16,20,20,20,24,24,24,28,28,28,36,36,36,2,2,2,2,2,2,2,2,2,26,26,26,0 }, { 4,4,4,4,4,4,4,4,4,6,6,6,6,6,6,8,8,8,10,10,10,14,14,14,18,18,18,26,26,26,32,32,32,42,42,42,18,18,18,0 }, @@ -91122,7 +90517,7 @@ static int ma_dr_mp3_L3_read_side_info(ma_dr_mp3_bs *bs, ma_dr_mp3_L3_gr_info *g { 4,4,4,4,4,4,4,4,4,4,4,4,6,6,6,6,6,6,10,10,10,12,12,12,14,14,14,16,16,16,20,20,20,26,26,26,66,66,66,0 }, { 4,4,4,4,4,4,4,4,4,4,4,4,6,6,6,8,8,8,12,12,12,16,16,16,20,20,20,26,26,26,34,34,34,42,42,42,12,12,12,0 } }; - static const ma_uint8 g_scf_mixed[8][40] = { + static const drmp3_uint8 g_scf_mixed[8][40] = { { 6,6,6,6,6,6,6,6,6,8,8,8,10,10,10,12,12,12,14,14,14,18,18,18,24,24,24,30,30,30,40,40,40,18,18,18,0 }, { 12,12,12,4,4,4,8,8,8,12,12,12,16,16,16,20,20,20,24,24,24,28,28,28,36,36,36,2,2,2,2,2,2,2,2,2,26,26,26,0 }, { 6,6,6,6,6,6,6,6,6,6,6,6,8,8,8,10,10,10,14,14,14,18,18,18,26,26,26,32,32,32,42,42,42,18,18,18,0 }, @@ -91134,46 +90529,46 @@ static int ma_dr_mp3_L3_read_side_info(ma_dr_mp3_bs *bs, ma_dr_mp3_L3_gr_info *g }; unsigned tables, scfsi = 0; int main_data_begin, part_23_sum = 0; - int gr_count = MA_DR_MP3_HDR_IS_MONO(hdr) ? 1 : 2; - int sr_idx = MA_DR_MP3_HDR_GET_MY_SAMPLE_RATE(hdr); sr_idx -= (sr_idx != 0); - if (MA_DR_MP3_HDR_TEST_MPEG1(hdr)) + int gr_count = DRMP3_HDR_IS_MONO(hdr) ? 1 : 2; + int sr_idx = DRMP3_HDR_GET_MY_SAMPLE_RATE(hdr); sr_idx -= (sr_idx != 0); + if (DRMP3_HDR_TEST_MPEG1(hdr)) { gr_count *= 2; - main_data_begin = ma_dr_mp3_bs_get_bits(bs, 9); - scfsi = ma_dr_mp3_bs_get_bits(bs, 7 + gr_count); + main_data_begin = drmp3_bs_get_bits(bs, 9); + scfsi = drmp3_bs_get_bits(bs, 7 + gr_count); } else { - main_data_begin = ma_dr_mp3_bs_get_bits(bs, 8 + gr_count) >> gr_count; + main_data_begin = drmp3_bs_get_bits(bs, 8 + gr_count) >> gr_count; } do { - if (MA_DR_MP3_HDR_IS_MONO(hdr)) + if (DRMP3_HDR_IS_MONO(hdr)) { scfsi <<= 4; } - gr->part_23_length = (ma_uint16)ma_dr_mp3_bs_get_bits(bs, 12); + gr->part_23_length = (drmp3_uint16)drmp3_bs_get_bits(bs, 12); part_23_sum += gr->part_23_length; - gr->big_values = (ma_uint16)ma_dr_mp3_bs_get_bits(bs, 9); + gr->big_values = (drmp3_uint16)drmp3_bs_get_bits(bs, 9); if (gr->big_values > 288) { return -1; } - gr->global_gain = (ma_uint8)ma_dr_mp3_bs_get_bits(bs, 8); - gr->scalefac_compress = (ma_uint16)ma_dr_mp3_bs_get_bits(bs, MA_DR_MP3_HDR_TEST_MPEG1(hdr) ? 4 : 9); + gr->global_gain = (drmp3_uint8)drmp3_bs_get_bits(bs, 8); + gr->scalefac_compress = (drmp3_uint16)drmp3_bs_get_bits(bs, DRMP3_HDR_TEST_MPEG1(hdr) ? 4 : 9); gr->sfbtab = g_scf_long[sr_idx]; gr->n_long_sfb = 22; gr->n_short_sfb = 0; - if (ma_dr_mp3_bs_get_bits(bs, 1)) + if (drmp3_bs_get_bits(bs, 1)) { - gr->block_type = (ma_uint8)ma_dr_mp3_bs_get_bits(bs, 2); + gr->block_type = (drmp3_uint8)drmp3_bs_get_bits(bs, 2); if (!gr->block_type) { return -1; } - gr->mixed_block_flag = (ma_uint8)ma_dr_mp3_bs_get_bits(bs, 1); + gr->mixed_block_flag = (drmp3_uint8)drmp3_bs_get_bits(bs, 1); gr->region_count[0] = 7; gr->region_count[1] = 255; - if (gr->block_type == MA_DR_MP3_SHORT_BLOCK_TYPE) + if (gr->block_type == DRMP3_SHORT_BLOCK_TYPE) { scfsi &= 0x0F0F; if (!gr->mixed_block_flag) @@ -91185,31 +90580,31 @@ static int ma_dr_mp3_L3_read_side_info(ma_dr_mp3_bs *bs, ma_dr_mp3_L3_gr_info *g } else { gr->sfbtab = g_scf_mixed[sr_idx]; - gr->n_long_sfb = MA_DR_MP3_HDR_TEST_MPEG1(hdr) ? 8 : 6; + gr->n_long_sfb = DRMP3_HDR_TEST_MPEG1(hdr) ? 8 : 6; gr->n_short_sfb = 30; } } - tables = ma_dr_mp3_bs_get_bits(bs, 10); + tables = drmp3_bs_get_bits(bs, 10); tables <<= 5; - gr->subblock_gain[0] = (ma_uint8)ma_dr_mp3_bs_get_bits(bs, 3); - gr->subblock_gain[1] = (ma_uint8)ma_dr_mp3_bs_get_bits(bs, 3); - gr->subblock_gain[2] = (ma_uint8)ma_dr_mp3_bs_get_bits(bs, 3); + gr->subblock_gain[0] = (drmp3_uint8)drmp3_bs_get_bits(bs, 3); + gr->subblock_gain[1] = (drmp3_uint8)drmp3_bs_get_bits(bs, 3); + gr->subblock_gain[2] = (drmp3_uint8)drmp3_bs_get_bits(bs, 3); } else { gr->block_type = 0; gr->mixed_block_flag = 0; - tables = ma_dr_mp3_bs_get_bits(bs, 15); - gr->region_count[0] = (ma_uint8)ma_dr_mp3_bs_get_bits(bs, 4); - gr->region_count[1] = (ma_uint8)ma_dr_mp3_bs_get_bits(bs, 3); + tables = drmp3_bs_get_bits(bs, 15); + gr->region_count[0] = (drmp3_uint8)drmp3_bs_get_bits(bs, 4); + gr->region_count[1] = (drmp3_uint8)drmp3_bs_get_bits(bs, 3); gr->region_count[2] = 255; } - gr->table_select[0] = (ma_uint8)(tables >> 10); - gr->table_select[1] = (ma_uint8)((tables >> 5) & 31); - gr->table_select[2] = (ma_uint8)((tables) & 31); - gr->preflag = (ma_uint8)(MA_DR_MP3_HDR_TEST_MPEG1(hdr) ? ma_dr_mp3_bs_get_bits(bs, 1) : (gr->scalefac_compress >= 500)); - gr->scalefac_scale = (ma_uint8)ma_dr_mp3_bs_get_bits(bs, 1); - gr->count1_table = (ma_uint8)ma_dr_mp3_bs_get_bits(bs, 1); - gr->scfsi = (ma_uint8)((scfsi >> 12) & 15); + gr->table_select[0] = (drmp3_uint8)(tables >> 10); + gr->table_select[1] = (drmp3_uint8)((tables >> 5) & 31); + gr->table_select[2] = (drmp3_uint8)((tables) & 31); + gr->preflag = (drmp3_uint8)(DRMP3_HDR_TEST_MPEG1(hdr) ? drmp3_bs_get_bits(bs, 1) : (gr->scalefac_compress >= 500)); + gr->scalefac_scale = (drmp3_uint8)drmp3_bs_get_bits(bs, 1); + gr->count1_table = (drmp3_uint8)drmp3_bs_get_bits(bs, 1); + gr->scfsi = (drmp3_uint8)((scfsi >> 12) & 15); scfsi <<= 4; gr++; } while(--gr_count); @@ -91219,7 +90614,7 @@ static int ma_dr_mp3_L3_read_side_info(ma_dr_mp3_bs *bs, ma_dr_mp3_L3_gr_info *g } return main_data_begin; } -static void ma_dr_mp3_L3_read_scalefactors(ma_uint8 *scf, ma_uint8 *ist_pos, const ma_uint8 *scf_size, const ma_uint8 *scf_count, ma_dr_mp3_bs *bitbuf, int scfsi) +static void drmp3_L3_read_scalefactors(drmp3_uint8 *scf, drmp3_uint8 *ist_pos, const drmp3_uint8 *scf_size, const drmp3_uint8 *scf_count, drmp3_bs *bitbuf, int scfsi) { int i, k; for (i = 0; i < 4 && scf_count[i]; i++, scfsi *= 2) @@ -91227,22 +90622,22 @@ static void ma_dr_mp3_L3_read_scalefactors(ma_uint8 *scf, ma_uint8 *ist_pos, con int cnt = scf_count[i]; if (scfsi & 8) { - MA_DR_MP3_COPY_MEMORY(scf, ist_pos, cnt); + DRMP3_COPY_MEMORY(scf, ist_pos, cnt); } else { int bits = scf_size[i]; if (!bits) { - MA_DR_MP3_ZERO_MEMORY(scf, cnt); - MA_DR_MP3_ZERO_MEMORY(ist_pos, cnt); + DRMP3_ZERO_MEMORY(scf, cnt); + DRMP3_ZERO_MEMORY(ist_pos, cnt); } else { int max_scf = (scfsi < 0) ? (1 << bits) - 1 : -1; for (k = 0; k < cnt; k++) { - int s = ma_dr_mp3_bs_get_bits(bitbuf, bits); - ist_pos[k] = (ma_uint8)(s == max_scf ? -1 : s); - scf[k] = (ma_uint8)s; + int s = drmp3_bs_get_bits(bitbuf, bits); + ist_pos[k] = (drmp3_uint8)(s == max_scf ? -1 : s); + scf[k] = (drmp3_uint8)s; } } } @@ -91251,86 +90646,86 @@ static void ma_dr_mp3_L3_read_scalefactors(ma_uint8 *scf, ma_uint8 *ist_pos, con } scf[0] = scf[1] = scf[2] = 0; } -static float ma_dr_mp3_L3_ldexp_q2(float y, int exp_q2) +static float drmp3_L3_ldexp_q2(float y, int exp_q2) { static const float g_expfrac[4] = { 9.31322575e-10f,7.83145814e-10f,6.58544508e-10f,5.53767716e-10f }; int e; do { - e = MA_DR_MP3_MIN(30*4, exp_q2); + e = DRMP3_MIN(30*4, exp_q2); y *= g_expfrac[e & 3]*(1 << 30 >> (e >> 2)); } while ((exp_q2 -= e) > 0); return y; } -static void ma_dr_mp3_L3_decode_scalefactors(const ma_uint8 *hdr, ma_uint8 *ist_pos, ma_dr_mp3_bs *bs, const ma_dr_mp3_L3_gr_info *gr, float *scf, int ch) +static void drmp3_L3_decode_scalefactors(const drmp3_uint8 *hdr, drmp3_uint8 *ist_pos, drmp3_bs *bs, const drmp3_L3_gr_info *gr, float *scf, int ch) { - static const ma_uint8 g_scf_partitions[3][28] = { + static const drmp3_uint8 g_scf_partitions[3][28] = { { 6,5,5, 5,6,5,5,5,6,5, 7,3,11,10,0,0, 7, 7, 7,0, 6, 6,6,3, 8, 8,5,0 }, { 8,9,6,12,6,9,9,9,6,9,12,6,15,18,0,0, 6,15,12,0, 6,12,9,6, 6,18,9,0 }, { 9,9,6,12,9,9,9,9,9,9,12,6,18,18,0,0,12,12,12,0,12, 9,9,6,15,12,9,0 } }; - const ma_uint8 *scf_partition = g_scf_partitions[!!gr->n_short_sfb + !gr->n_long_sfb]; - ma_uint8 scf_size[4], iscf[40]; + const drmp3_uint8 *scf_partition = g_scf_partitions[!!gr->n_short_sfb + !gr->n_long_sfb]; + drmp3_uint8 scf_size[4], iscf[40]; int i, scf_shift = gr->scalefac_scale + 1, gain_exp, scfsi = gr->scfsi; float gain; - if (MA_DR_MP3_HDR_TEST_MPEG1(hdr)) + if (DRMP3_HDR_TEST_MPEG1(hdr)) { - static const ma_uint8 g_scfc_decode[16] = { 0,1,2,3, 12,5,6,7, 9,10,11,13, 14,15,18,19 }; + static const drmp3_uint8 g_scfc_decode[16] = { 0,1,2,3, 12,5,6,7, 9,10,11,13, 14,15,18,19 }; int part = g_scfc_decode[gr->scalefac_compress]; - scf_size[1] = scf_size[0] = (ma_uint8)(part >> 2); - scf_size[3] = scf_size[2] = (ma_uint8)(part & 3); + scf_size[1] = scf_size[0] = (drmp3_uint8)(part >> 2); + scf_size[3] = scf_size[2] = (drmp3_uint8)(part & 3); } else { - static const ma_uint8 g_mod[6*4] = { 5,5,4,4,5,5,4,1,4,3,1,1,5,6,6,1,4,4,4,1,4,3,1,1 }; - int k, modprod, sfc, ist = MA_DR_MP3_HDR_TEST_I_STEREO(hdr) && ch; + static const drmp3_uint8 g_mod[6*4] = { 5,5,4,4,5,5,4,1,4,3,1,1,5,6,6,1,4,4,4,1,4,3,1,1 }; + int k, modprod, sfc, ist = DRMP3_HDR_TEST_I_STEREO(hdr) && ch; sfc = gr->scalefac_compress >> ist; for (k = ist*3*4; sfc >= 0; sfc -= modprod, k += 4) { for (modprod = 1, i = 3; i >= 0; i--) { - scf_size[i] = (ma_uint8)(sfc / modprod % g_mod[k + i]); + scf_size[i] = (drmp3_uint8)(sfc / modprod % g_mod[k + i]); modprod *= g_mod[k + i]; } } scf_partition += k; scfsi = -16; } - ma_dr_mp3_L3_read_scalefactors(iscf, ist_pos, scf_size, scf_partition, bs, scfsi); + drmp3_L3_read_scalefactors(iscf, ist_pos, scf_size, scf_partition, bs, scfsi); if (gr->n_short_sfb) { int sh = 3 - scf_shift; for (i = 0; i < gr->n_short_sfb; i += 3) { - iscf[gr->n_long_sfb + i + 0] = (ma_uint8)(iscf[gr->n_long_sfb + i + 0] + (gr->subblock_gain[0] << sh)); - iscf[gr->n_long_sfb + i + 1] = (ma_uint8)(iscf[gr->n_long_sfb + i + 1] + (gr->subblock_gain[1] << sh)); - iscf[gr->n_long_sfb + i + 2] = (ma_uint8)(iscf[gr->n_long_sfb + i + 2] + (gr->subblock_gain[2] << sh)); + iscf[gr->n_long_sfb + i + 0] = (drmp3_uint8)(iscf[gr->n_long_sfb + i + 0] + (gr->subblock_gain[0] << sh)); + iscf[gr->n_long_sfb + i + 1] = (drmp3_uint8)(iscf[gr->n_long_sfb + i + 1] + (gr->subblock_gain[1] << sh)); + iscf[gr->n_long_sfb + i + 2] = (drmp3_uint8)(iscf[gr->n_long_sfb + i + 2] + (gr->subblock_gain[2] << sh)); } } else if (gr->preflag) { - static const ma_uint8 g_preamp[10] = { 1,1,1,1,2,2,3,3,3,2 }; + static const drmp3_uint8 g_preamp[10] = { 1,1,1,1,2,2,3,3,3,2 }; for (i = 0; i < 10; i++) { - iscf[11 + i] = (ma_uint8)(iscf[11 + i] + g_preamp[i]); + iscf[11 + i] = (drmp3_uint8)(iscf[11 + i] + g_preamp[i]); } } - gain_exp = gr->global_gain + MA_DR_MP3_BITS_DEQUANTIZER_OUT*4 - 210 - (MA_DR_MP3_HDR_IS_MS_STEREO(hdr) ? 2 : 0); - gain = ma_dr_mp3_L3_ldexp_q2(1 << (MA_DR_MP3_MAX_SCFI/4), MA_DR_MP3_MAX_SCFI - gain_exp); + gain_exp = gr->global_gain + DRMP3_BITS_DEQUANTIZER_OUT*4 - 210 - (DRMP3_HDR_IS_MS_STEREO(hdr) ? 2 : 0); + gain = drmp3_L3_ldexp_q2(1 << (DRMP3_MAX_SCFI/4), DRMP3_MAX_SCFI - gain_exp); for (i = 0; i < (int)(gr->n_long_sfb + gr->n_short_sfb); i++) { - scf[i] = ma_dr_mp3_L3_ldexp_q2(gain, iscf[i] << scf_shift); + scf[i] = drmp3_L3_ldexp_q2(gain, iscf[i] << scf_shift); } } -static const float g_ma_dr_mp3_pow43[129 + 16] = { +static const float g_drmp3_pow43[129 + 16] = { 0,-1,-2.519842f,-4.326749f,-6.349604f,-8.549880f,-10.902724f,-13.390518f,-16.000000f,-18.720754f,-21.544347f,-24.463781f,-27.473142f,-30.567351f,-33.741992f,-36.993181f, 0,1,2.519842f,4.326749f,6.349604f,8.549880f,10.902724f,13.390518f,16.000000f,18.720754f,21.544347f,24.463781f,27.473142f,30.567351f,33.741992f,36.993181f,40.317474f,43.711787f,47.173345f,50.699631f,54.288352f,57.937408f,61.644865f,65.408941f,69.227979f,73.100443f,77.024898f,81.000000f,85.024491f,89.097188f,93.216975f,97.382800f,101.593667f,105.848633f,110.146801f,114.487321f,118.869381f,123.292209f,127.755065f,132.257246f,136.798076f,141.376907f,145.993119f,150.646117f,155.335327f,160.060199f,164.820202f,169.614826f,174.443577f,179.305980f,184.201575f,189.129918f,194.090580f,199.083145f,204.107210f,209.162385f,214.248292f,219.364564f,224.510845f,229.686789f,234.892058f,240.126328f,245.389280f,250.680604f,256.000000f,261.347174f,266.721841f,272.123723f,277.552547f,283.008049f,288.489971f,293.998060f,299.532071f,305.091761f,310.676898f,316.287249f,321.922592f,327.582707f,333.267377f,338.976394f,344.709550f,350.466646f,356.247482f,362.051866f,367.879608f,373.730522f,379.604427f,385.501143f,391.420496f,397.362314f,403.326427f,409.312672f,415.320884f,421.350905f,427.402579f,433.475750f,439.570269f,445.685987f,451.822757f,457.980436f,464.158883f,470.357960f,476.577530f,482.817459f,489.077615f,495.357868f,501.658090f,507.978156f,514.317941f,520.677324f,527.056184f,533.454404f,539.871867f,546.308458f,552.764065f,559.238575f,565.731879f,572.243870f,578.774440f,585.323483f,591.890898f,598.476581f,605.080431f,611.702349f,618.342238f,625.000000f,631.675540f,638.368763f,645.079578f }; -static float ma_dr_mp3_L3_pow_43(int x) +static float drmp3_L3_pow_43(int x) { float frac; int sign, mult = 256; if (x < 129) { - return g_ma_dr_mp3_pow43[16 + x]; + return g_drmp3_pow43[16 + x]; } if (x < 1024) { @@ -91339,11 +90734,11 @@ static float ma_dr_mp3_L3_pow_43(int x) } sign = 2*x & 64; frac = (float)((x & 63) - sign) / ((x & ~63) + sign); - return g_ma_dr_mp3_pow43[16 + ((x + sign) >> 6)]*(1.f + frac*((4.f/3) + frac*(2.f/9)))*mult; + return g_drmp3_pow43[16 + ((x + sign) >> 6)]*(1.f + frac*((4.f/3) + frac*(2.f/9)))*mult; } -static void ma_dr_mp3_L3_huffman(float *dst, ma_dr_mp3_bs *bs, const ma_dr_mp3_L3_gr_info *gr_info, const float *scf, int layer3gr_limit) +static void drmp3_L3_huffman(float *dst, drmp3_bs *bs, const drmp3_L3_gr_info *gr_info, const float *scf, int layer3gr_limit) { - static const ma_int16 tabs[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + static const drmp3_int16 tabs[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 785,785,785,785,784,784,784,784,513,513,513,513,513,513,513,513,256,256,256,256,256,256,256,256,256,256,256,256,256,256,256,256, -255,1313,1298,1282,785,785,785,785,784,784,784,784,769,769,769,769,256,256,256,256,256,256,256,256,256,256,256,256,256,256,256,256,290,288, -255,1313,1298,1282,769,769,769,769,529,529,529,529,529,529,529,529,528,528,528,528,528,528,528,528,512,512,512,512,512,512,512,512,290,288, @@ -91359,61 +90754,61 @@ static void ma_dr_mp3_L3_huffman(float *dst, ma_dr_mp3_bs *bs, const ma_dr_mp3_L -250,-1179,-1579,-1836,-1996,-2124,-2253,-2333,-2413,-2477,-2542,-2574,-2607,-2622,-2655,1314,1313,1298,1312,1282,785,785,785,785,1040,1040,1025,1025,768,768,768,768,-766,-798,-830,-862,-895,-911,-927,-943,-959,-975,-991,-1007,-1023,-1039,-1055,-1070,1724,1647,-1103,-1119,1631,1767,1662,1738,1708,1723,-1135,1780,1615,1779,1599,1677,1646,1778,1583,-1151,1777,1567,1737,1692,1765,1722,1707,1630,1751,1661,1764,1614,1736,1676,1763,1750,1645,1598,1721,1691,1762,1706,1582,1761,1566,-1167,1749,1629,767,766,751,765,494,494,735,764,719,749,734,763,447,447,748,718,477,506,431,491,446,476,461,505,415,430,475,445,504,399,460,489,414,503,383,474,429,459,502,502,746,752,488,398,501,473,413,472,486,271,480,270,-1439,-1455,1357,-1471,-1487,-1503,1341,1325,-1519,1489,1463,1403,1309,-1535,1372,1448,1418,1476,1356,1462,1387,-1551,1475,1340,1447,1402,1386,-1567,1068,1068,1474,1461,455,380,468,440,395,425,410,454,364,467,466,464,453,269,409,448,268,432,1371,1473,1432,1417,1308,1460,1355,1446,1459,1431,1083,1083,1401,1416,1458,1445,1067,1067,1370,1457,1051,1051,1291,1430,1385,1444,1354,1415,1400,1443,1082,1082,1173,1113,1186,1066,1185,1050,-1967,1158,1128,1172,1097,1171,1081,-1983,1157,1112,416,266,375,400,1170,1142,1127,1065,793,793,1169,1033,1156,1096,1141,1111,1155,1080,1126,1140,898,898,808,808,897,897,792,792,1095,1152,1032,1125,1110,1139,1079,1124,882,807,838,881,853,791,-2319,867,368,263,822,852,837,866,806,865,-2399,851,352,262,534,534,821,836,594,594,549,549,593,593,533,533,848,773,579,579,564,578,548,563,276,276,577,576,306,291,516,560,305,305,275,259, -251,-892,-2058,-2620,-2828,-2957,-3023,-3039,1041,1041,1040,1040,769,769,769,769,256,256,256,256,256,256,256,256,256,256,256,256,256,256,256,256,-511,-527,-543,-559,1530,-575,-591,1528,1527,1407,1526,1391,1023,1023,1023,1023,1525,1375,1268,1268,1103,1103,1087,1087,1039,1039,1523,-604,815,815,815,815,510,495,509,479,508,463,507,447,431,505,415,399,-734,-782,1262,-815,1259,1244,-831,1258,1228,-847,-863,1196,-879,1253,987,987,748,-767,493,493,462,477,414,414,686,669,478,446,461,445,474,429,487,458,412,471,1266,1264,1009,1009,799,799,-1019,-1276,-1452,-1581,-1677,-1757,-1821,-1886,-1933,-1997,1257,1257,1483,1468,1512,1422,1497,1406,1467,1496,1421,1510,1134,1134,1225,1225,1466,1451,1374,1405,1252,1252,1358,1480,1164,1164,1251,1251,1238,1238,1389,1465,-1407,1054,1101,-1423,1207,-1439,830,830,1248,1038,1237,1117,1223,1148,1236,1208,411,426,395,410,379,269,1193,1222,1132,1235,1221,1116,976,976,1192,1162,1177,1220,1131,1191,963,963,-1647,961,780,-1663,558,558,994,993,437,408,393,407,829,978,813,797,947,-1743,721,721,377,392,844,950,828,890,706,706,812,859,796,960,948,843,934,874,571,571,-1919,690,555,689,421,346,539,539,944,779,918,873,932,842,903,888,570,570,931,917,674,674,-2575,1562,-2591,1609,-2607,1654,1322,1322,1441,1441,1696,1546,1683,1593,1669,1624,1426,1426,1321,1321,1639,1680,1425,1425,1305,1305,1545,1668,1608,1623,1667,1592,1638,1666,1320,1320,1652,1607,1409,1409,1304,1304,1288,1288,1664,1637,1395,1395,1335,1335,1622,1636,1394,1394,1319,1319,1606,1621,1392,1392,1137,1137,1137,1137,345,390,360,375,404,373,1047,-2751,-2767,-2783,1062,1121,1046,-2799,1077,-2815,1106,1061,789,789,1105,1104,263,355,310,340,325,354,352,262,339,324,1091,1076,1029,1090,1060,1075,833,833,788,788,1088,1028,818,818,803,803,561,561,531,531,816,771,546,546,289,274,288,258, -253,-317,-381,-446,-478,-509,1279,1279,-811,-1179,-1451,-1756,-1900,-2028,-2189,-2253,-2333,-2414,-2445,-2511,-2526,1313,1298,-2559,1041,1041,1040,1040,1025,1025,1024,1024,1022,1007,1021,991,1020,975,1019,959,687,687,1018,1017,671,671,655,655,1016,1015,639,639,758,758,623,623,757,607,756,591,755,575,754,559,543,543,1009,783,-575,-621,-685,-749,496,-590,750,749,734,748,974,989,1003,958,988,973,1002,942,987,957,972,1001,926,986,941,971,956,1000,910,985,925,999,894,970,-1071,-1087,-1102,1390,-1135,1436,1509,1451,1374,-1151,1405,1358,1480,1420,-1167,1507,1494,1389,1342,1465,1435,1450,1326,1505,1310,1493,1373,1479,1404,1492,1464,1419,428,443,472,397,736,526,464,464,486,457,442,471,484,482,1357,1449,1434,1478,1388,1491,1341,1490,1325,1489,1463,1403,1309,1477,1372,1448,1418,1433,1476,1356,1462,1387,-1439,1475,1340,1447,1402,1474,1324,1461,1371,1473,269,448,1432,1417,1308,1460,-1711,1459,-1727,1441,1099,1099,1446,1386,1431,1401,-1743,1289,1083,1083,1160,1160,1458,1445,1067,1067,1370,1457,1307,1430,1129,1129,1098,1098,268,432,267,416,266,400,-1887,1144,1187,1082,1173,1113,1186,1066,1050,1158,1128,1143,1172,1097,1171,1081,420,391,1157,1112,1170,1142,1127,1065,1169,1049,1156,1096,1141,1111,1155,1080,1126,1154,1064,1153,1140,1095,1048,-2159,1125,1110,1137,-2175,823,823,1139,1138,807,807,384,264,368,263,868,838,853,791,867,822,852,837,866,806,865,790,-2319,851,821,836,352,262,850,805,849,-2399,533,533,835,820,336,261,578,548,563,577,532,532,832,772,562,562,547,547,305,275,560,515,290,290,288,258 }; - static const ma_uint8 tab32[] = { 130,162,193,209,44,28,76,140,9,9,9,9,9,9,9,9,190,254,222,238,126,94,157,157,109,61,173,205}; - static const ma_uint8 tab33[] = { 252,236,220,204,188,172,156,140,124,108,92,76,60,44,28,12 }; - static const ma_int16 tabindex[2*16] = { 0,32,64,98,0,132,180,218,292,364,426,538,648,746,0,1126,1460,1460,1460,1460,1460,1460,1460,1460,1842,1842,1842,1842,1842,1842,1842,1842 }; - static const ma_uint8 g_linbits[] = { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,2,3,4,6,8,10,13,4,5,6,7,8,9,11,13 }; -#define MA_DR_MP3_PEEK_BITS(n) (bs_cache >> (32 - (n))) -#define MA_DR_MP3_FLUSH_BITS(n) { bs_cache <<= (n); bs_sh += (n); } -#define MA_DR_MP3_CHECK_BITS while (bs_sh >= 0) { bs_cache |= (ma_uint32)*bs_next_ptr++ << bs_sh; bs_sh -= 8; } -#define MA_DR_MP3_BSPOS ((bs_next_ptr - bs->buf)*8 - 24 + bs_sh) + static const drmp3_uint8 tab32[] = { 130,162,193,209,44,28,76,140,9,9,9,9,9,9,9,9,190,254,222,238,126,94,157,157,109,61,173,205}; + static const drmp3_uint8 tab33[] = { 252,236,220,204,188,172,156,140,124,108,92,76,60,44,28,12 }; + static const drmp3_int16 tabindex[2*16] = { 0,32,64,98,0,132,180,218,292,364,426,538,648,746,0,1126,1460,1460,1460,1460,1460,1460,1460,1460,1842,1842,1842,1842,1842,1842,1842,1842 }; + static const drmp3_uint8 g_linbits[] = { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,2,3,4,6,8,10,13,4,5,6,7,8,9,11,13 }; +#define DRMP3_PEEK_BITS(n) (bs_cache >> (32 - (n))) +#define DRMP3_FLUSH_BITS(n) { bs_cache <<= (n); bs_sh += (n); } +#define DRMP3_CHECK_BITS while (bs_sh >= 0) { bs_cache |= (drmp3_uint32)*bs_next_ptr++ << bs_sh; bs_sh -= 8; } +#define DRMP3_BSPOS ((bs_next_ptr - bs->buf)*8 - 24 + bs_sh) float one = 0.0f; int ireg = 0, big_val_cnt = gr_info->big_values; - const ma_uint8 *sfb = gr_info->sfbtab; - const ma_uint8 *bs_next_ptr = bs->buf + bs->pos/8; - ma_uint32 bs_cache = (((bs_next_ptr[0]*256u + bs_next_ptr[1])*256u + bs_next_ptr[2])*256u + bs_next_ptr[3]) << (bs->pos & 7); + const drmp3_uint8 *sfb = gr_info->sfbtab; + const drmp3_uint8 *bs_next_ptr = bs->buf + bs->pos/8; + drmp3_uint32 bs_cache = (((bs_next_ptr[0]*256u + bs_next_ptr[1])*256u + bs_next_ptr[2])*256u + bs_next_ptr[3]) << (bs->pos & 7); int pairs_to_decode, np, bs_sh = (bs->pos & 7) - 8; bs_next_ptr += 4; while (big_val_cnt > 0) { int tab_num = gr_info->table_select[ireg]; int sfb_cnt = gr_info->region_count[ireg++]; - const ma_int16 *codebook = tabs + tabindex[tab_num]; + const drmp3_int16 *codebook = tabs + tabindex[tab_num]; int linbits = g_linbits[tab_num]; if (linbits) { do { np = *sfb++ / 2; - pairs_to_decode = MA_DR_MP3_MIN(big_val_cnt, np); + pairs_to_decode = DRMP3_MIN(big_val_cnt, np); one = *scf++; do { int j, w = 5; - int leaf = codebook[MA_DR_MP3_PEEK_BITS(w)]; + int leaf = codebook[DRMP3_PEEK_BITS(w)]; while (leaf < 0) { - MA_DR_MP3_FLUSH_BITS(w); + DRMP3_FLUSH_BITS(w); w = leaf & 7; - leaf = codebook[MA_DR_MP3_PEEK_BITS(w) - (leaf >> 3)]; + leaf = codebook[DRMP3_PEEK_BITS(w) - (leaf >> 3)]; } - MA_DR_MP3_FLUSH_BITS(leaf >> 8); + DRMP3_FLUSH_BITS(leaf >> 8); for (j = 0; j < 2; j++, dst++, leaf >>= 4) { int lsb = leaf & 0x0F; if (lsb == 15) { - lsb += MA_DR_MP3_PEEK_BITS(linbits); - MA_DR_MP3_FLUSH_BITS(linbits); - MA_DR_MP3_CHECK_BITS; - *dst = one*ma_dr_mp3_L3_pow_43(lsb)*((ma_int32)bs_cache < 0 ? -1: 1); + lsb += DRMP3_PEEK_BITS(linbits); + DRMP3_FLUSH_BITS(linbits); + DRMP3_CHECK_BITS; + *dst = one*drmp3_L3_pow_43(lsb)*((drmp3_int32)bs_cache < 0 ? -1: 1); } else { - *dst = g_ma_dr_mp3_pow43[16 + lsb - 16*(bs_cache >> 31)]*one; + *dst = g_drmp3_pow43[16 + lsb - 16*(bs_cache >> 31)]*one; } - MA_DR_MP3_FLUSH_BITS(lsb ? 1 : 0); + DRMP3_FLUSH_BITS(lsb ? 1 : 0); } - MA_DR_MP3_CHECK_BITS; + DRMP3_CHECK_BITS; } while (--pairs_to_decode); } while ((big_val_cnt -= np) > 0 && --sfb_cnt >= 0); } else @@ -91421,68 +90816,68 @@ static void ma_dr_mp3_L3_huffman(float *dst, ma_dr_mp3_bs *bs, const ma_dr_mp3_L do { np = *sfb++ / 2; - pairs_to_decode = MA_DR_MP3_MIN(big_val_cnt, np); + pairs_to_decode = DRMP3_MIN(big_val_cnt, np); one = *scf++; do { int j, w = 5; - int leaf = codebook[MA_DR_MP3_PEEK_BITS(w)]; + int leaf = codebook[DRMP3_PEEK_BITS(w)]; while (leaf < 0) { - MA_DR_MP3_FLUSH_BITS(w); + DRMP3_FLUSH_BITS(w); w = leaf & 7; - leaf = codebook[MA_DR_MP3_PEEK_BITS(w) - (leaf >> 3)]; + leaf = codebook[DRMP3_PEEK_BITS(w) - (leaf >> 3)]; } - MA_DR_MP3_FLUSH_BITS(leaf >> 8); + DRMP3_FLUSH_BITS(leaf >> 8); for (j = 0; j < 2; j++, dst++, leaf >>= 4) { int lsb = leaf & 0x0F; - *dst = g_ma_dr_mp3_pow43[16 + lsb - 16*(bs_cache >> 31)]*one; - MA_DR_MP3_FLUSH_BITS(lsb ? 1 : 0); + *dst = g_drmp3_pow43[16 + lsb - 16*(bs_cache >> 31)]*one; + DRMP3_FLUSH_BITS(lsb ? 1 : 0); } - MA_DR_MP3_CHECK_BITS; + DRMP3_CHECK_BITS; } while (--pairs_to_decode); } while ((big_val_cnt -= np) > 0 && --sfb_cnt >= 0); } } for (np = 1 - big_val_cnt;; dst += 4) { - const ma_uint8 *codebook_count1 = (gr_info->count1_table) ? tab33 : tab32; - int leaf = codebook_count1[MA_DR_MP3_PEEK_BITS(4)]; + const drmp3_uint8 *codebook_count1 = (gr_info->count1_table) ? tab33 : tab32; + int leaf = codebook_count1[DRMP3_PEEK_BITS(4)]; if (!(leaf & 8)) { leaf = codebook_count1[(leaf >> 3) + (bs_cache << 4 >> (32 - (leaf & 3)))]; } - MA_DR_MP3_FLUSH_BITS(leaf & 7); - if (MA_DR_MP3_BSPOS > layer3gr_limit) + DRMP3_FLUSH_BITS(leaf & 7); + if (DRMP3_BSPOS > layer3gr_limit) { break; } -#define MA_DR_MP3_RELOAD_SCALEFACTOR if (!--np) { np = *sfb++/2; if (!np) break; one = *scf++; } -#define MA_DR_MP3_DEQ_COUNT1(s) if (leaf & (128 >> s)) { dst[s] = ((ma_int32)bs_cache < 0) ? -one : one; MA_DR_MP3_FLUSH_BITS(1) } - MA_DR_MP3_RELOAD_SCALEFACTOR; - MA_DR_MP3_DEQ_COUNT1(0); - MA_DR_MP3_DEQ_COUNT1(1); - MA_DR_MP3_RELOAD_SCALEFACTOR; - MA_DR_MP3_DEQ_COUNT1(2); - MA_DR_MP3_DEQ_COUNT1(3); - MA_DR_MP3_CHECK_BITS; +#define DRMP3_RELOAD_SCALEFACTOR if (!--np) { np = *sfb++/2; if (!np) break; one = *scf++; } +#define DRMP3_DEQ_COUNT1(s) if (leaf & (128 >> s)) { dst[s] = ((drmp3_int32)bs_cache < 0) ? -one : one; DRMP3_FLUSH_BITS(1) } + DRMP3_RELOAD_SCALEFACTOR; + DRMP3_DEQ_COUNT1(0); + DRMP3_DEQ_COUNT1(1); + DRMP3_RELOAD_SCALEFACTOR; + DRMP3_DEQ_COUNT1(2); + DRMP3_DEQ_COUNT1(3); + DRMP3_CHECK_BITS; } bs->pos = layer3gr_limit; } -static void ma_dr_mp3_L3_midside_stereo(float *left, int n) +static void drmp3_L3_midside_stereo(float *left, int n) { int i = 0; float *right = left + 576; -#if MA_DR_MP3_HAVE_SIMD - if (ma_dr_mp3_have_simd()) +#if DRMP3_HAVE_SIMD + if (drmp3_have_simd()) { for (; i < n - 3; i += 4) { - ma_dr_mp3_f4 vl = MA_DR_MP3_VLD(left + i); - ma_dr_mp3_f4 vr = MA_DR_MP3_VLD(right + i); - MA_DR_MP3_VSTORE(left + i, MA_DR_MP3_VADD(vl, vr)); - MA_DR_MP3_VSTORE(right + i, MA_DR_MP3_VSUB(vl, vr)); + drmp3_f4 vl = DRMP3_VLD(left + i); + drmp3_f4 vr = DRMP3_VLD(right + i); + DRMP3_VSTORE(left + i, DRMP3_VADD(vl, vr)); + DRMP3_VSTORE(right + i, DRMP3_VSUB(vl, vr)); } #ifdef __GNUC__ if (__builtin_constant_p(n % 4 == 0) && n % 4 == 0) @@ -91498,7 +90893,7 @@ static void ma_dr_mp3_L3_midside_stereo(float *left, int n) right[i] = a - b; } } -static void ma_dr_mp3_L3_intensity_stereo_band(float *left, int n, float kl, float kr) +static void drmp3_L3_intensity_stereo_band(float *left, int n, float kl, float kr) { int i; for (i = 0; i < n; i++) @@ -91507,7 +90902,7 @@ static void ma_dr_mp3_L3_intensity_stereo_band(float *left, int n, float kl, flo left[i] = left[i]*kl; } } -static void ma_dr_mp3_L3_stereo_top_band(const float *right, const ma_uint8 *sfb, int nbands, int max_band[3]) +static void drmp3_L3_stereo_top_band(const float *right, const drmp3_uint8 *sfb, int nbands, int max_band[3]) { int i, k; max_band[0] = max_band[1] = max_band[2] = -1; @@ -91524,57 +90919,57 @@ static void ma_dr_mp3_L3_stereo_top_band(const float *right, const ma_uint8 *sfb right += sfb[i]; } } -static void ma_dr_mp3_L3_stereo_process(float *left, const ma_uint8 *ist_pos, const ma_uint8 *sfb, const ma_uint8 *hdr, int max_band[3], int mpeg2_sh) +static void drmp3_L3_stereo_process(float *left, const drmp3_uint8 *ist_pos, const drmp3_uint8 *sfb, const drmp3_uint8 *hdr, int max_band[3], int mpeg2_sh) { static const float g_pan[7*2] = { 0,1,0.21132487f,0.78867513f,0.36602540f,0.63397460f,0.5f,0.5f,0.63397460f,0.36602540f,0.78867513f,0.21132487f,1,0 }; - unsigned i, max_pos = MA_DR_MP3_HDR_TEST_MPEG1(hdr) ? 7 : 64; + unsigned i, max_pos = DRMP3_HDR_TEST_MPEG1(hdr) ? 7 : 64; for (i = 0; sfb[i]; i++) { unsigned ipos = ist_pos[i]; if ((int)i > max_band[i % 3] && ipos < max_pos) { - float kl, kr, s = MA_DR_MP3_HDR_TEST_MS_STEREO(hdr) ? 1.41421356f : 1; - if (MA_DR_MP3_HDR_TEST_MPEG1(hdr)) + float kl, kr, s = DRMP3_HDR_TEST_MS_STEREO(hdr) ? 1.41421356f : 1; + if (DRMP3_HDR_TEST_MPEG1(hdr)) { kl = g_pan[2*ipos]; kr = g_pan[2*ipos + 1]; } else { kl = 1; - kr = ma_dr_mp3_L3_ldexp_q2(1, (ipos + 1) >> 1 << mpeg2_sh); + kr = drmp3_L3_ldexp_q2(1, (ipos + 1) >> 1 << mpeg2_sh); if (ipos & 1) { kl = kr; kr = 1; } } - ma_dr_mp3_L3_intensity_stereo_band(left, sfb[i], kl*s, kr*s); - } else if (MA_DR_MP3_HDR_TEST_MS_STEREO(hdr)) + drmp3_L3_intensity_stereo_band(left, sfb[i], kl*s, kr*s); + } else if (DRMP3_HDR_TEST_MS_STEREO(hdr)) { - ma_dr_mp3_L3_midside_stereo(left, sfb[i]); + drmp3_L3_midside_stereo(left, sfb[i]); } left += sfb[i]; } } -static void ma_dr_mp3_L3_intensity_stereo(float *left, ma_uint8 *ist_pos, const ma_dr_mp3_L3_gr_info *gr, const ma_uint8 *hdr) +static void drmp3_L3_intensity_stereo(float *left, drmp3_uint8 *ist_pos, const drmp3_L3_gr_info *gr, const drmp3_uint8 *hdr) { int max_band[3], n_sfb = gr->n_long_sfb + gr->n_short_sfb; int i, max_blocks = gr->n_short_sfb ? 3 : 1; - ma_dr_mp3_L3_stereo_top_band(left + 576, gr->sfbtab, n_sfb, max_band); + drmp3_L3_stereo_top_band(left + 576, gr->sfbtab, n_sfb, max_band); if (gr->n_long_sfb) { - max_band[0] = max_band[1] = max_band[2] = MA_DR_MP3_MAX(MA_DR_MP3_MAX(max_band[0], max_band[1]), max_band[2]); + max_band[0] = max_band[1] = max_band[2] = DRMP3_MAX(DRMP3_MAX(max_band[0], max_band[1]), max_band[2]); } for (i = 0; i < max_blocks; i++) { - int default_pos = MA_DR_MP3_HDR_TEST_MPEG1(hdr) ? 3 : 0; + int default_pos = DRMP3_HDR_TEST_MPEG1(hdr) ? 3 : 0; int itop = n_sfb - max_blocks + i; int prev = itop - max_blocks; - ist_pos[itop] = (ma_uint8)(max_band[i] >= prev ? default_pos : ist_pos[prev]); + ist_pos[itop] = (drmp3_uint8)(max_band[i] >= prev ? default_pos : ist_pos[prev]); } - ma_dr_mp3_L3_stereo_process(left, ist_pos, gr->sfbtab, hdr, max_band, gr[1].scalefac_compress & 1); + drmp3_L3_stereo_process(left, ist_pos, gr->sfbtab, hdr, max_band, gr[1].scalefac_compress & 1); } -static void ma_dr_mp3_L3_reorder(float *grbuf, float *scratch, const ma_uint8 *sfb) +static void drmp3_L3_reorder(float *grbuf, float *scratch, const drmp3_uint8 *sfb) { int i, len; float *src = grbuf, *dst = scratch; @@ -91587,9 +90982,9 @@ static void ma_dr_mp3_L3_reorder(float *grbuf, float *scratch, const ma_uint8 *s *dst++ = src[2*len]; } } - MA_DR_MP3_COPY_MEMORY(grbuf, scratch, (dst - scratch)*sizeof(float)); + DRMP3_COPY_MEMORY(grbuf, scratch, (dst - scratch)*sizeof(float)); } -static void ma_dr_mp3_L3_antialias(float *grbuf, int nbands) +static void drmp3_L3_antialias(float *grbuf, int nbands) { static const float g_aa[2][8] = { {0.85749293f,0.88174200f,0.94962865f,0.98331459f,0.99551782f,0.99916056f,0.99989920f,0.99999316f}, @@ -91598,20 +90993,20 @@ static void ma_dr_mp3_L3_antialias(float *grbuf, int nbands) for (; nbands > 0; nbands--, grbuf += 18) { int i = 0; -#if MA_DR_MP3_HAVE_SIMD - if (ma_dr_mp3_have_simd()) for (; i < 8; i += 4) +#if DRMP3_HAVE_SIMD + if (drmp3_have_simd()) for (; i < 8; i += 4) { - ma_dr_mp3_f4 vu = MA_DR_MP3_VLD(grbuf + 18 + i); - ma_dr_mp3_f4 vd = MA_DR_MP3_VLD(grbuf + 14 - i); - ma_dr_mp3_f4 vc0 = MA_DR_MP3_VLD(g_aa[0] + i); - ma_dr_mp3_f4 vc1 = MA_DR_MP3_VLD(g_aa[1] + i); - vd = MA_DR_MP3_VREV(vd); - MA_DR_MP3_VSTORE(grbuf + 18 + i, MA_DR_MP3_VSUB(MA_DR_MP3_VMUL(vu, vc0), MA_DR_MP3_VMUL(vd, vc1))); - vd = MA_DR_MP3_VADD(MA_DR_MP3_VMUL(vu, vc1), MA_DR_MP3_VMUL(vd, vc0)); - MA_DR_MP3_VSTORE(grbuf + 14 - i, MA_DR_MP3_VREV(vd)); + drmp3_f4 vu = DRMP3_VLD(grbuf + 18 + i); + drmp3_f4 vd = DRMP3_VLD(grbuf + 14 - i); + drmp3_f4 vc0 = DRMP3_VLD(g_aa[0] + i); + drmp3_f4 vc1 = DRMP3_VLD(g_aa[1] + i); + vd = DRMP3_VREV(vd); + DRMP3_VSTORE(grbuf + 18 + i, DRMP3_VSUB(DRMP3_VMUL(vu, vc0), DRMP3_VMUL(vd, vc1))); + vd = DRMP3_VADD(DRMP3_VMUL(vu, vc1), DRMP3_VMUL(vd, vc0)); + DRMP3_VSTORE(grbuf + 14 - i, DRMP3_VREV(vd)); } #endif -#ifndef MA_DR_MP3_ONLY_SIMD +#ifndef DR_MP3_ONLY_SIMD for(; i < 8; i++) { float u = grbuf[18 + i]; @@ -91622,7 +91017,7 @@ static void ma_dr_mp3_L3_antialias(float *grbuf, int nbands) #endif } } -static void ma_dr_mp3_L3_dct3_9(float *y) +static void drmp3_L3_dct3_9(float *y) { float s0, s1, s2, s3, s4, s5, s6, s7, s8, t0, t2, t4; s0 = y[0]; s2 = y[2]; s4 = y[4]; s6 = y[6]; s8 = y[8]; @@ -91655,7 +91050,7 @@ static void ma_dr_mp3_L3_dct3_9(float *y) y[7] = s2 - s1; y[8] = s4 + s7; } -static void ma_dr_mp3_L3_imdct36(float *grbuf, float *overlap, const float *window, int nbands) +static void drmp3_L3_imdct36(float *grbuf, float *overlap, const float *window, int nbands) { int i, j; static const float g_twid9[18] = { @@ -91673,28 +91068,28 @@ static void ma_dr_mp3_L3_imdct36(float *grbuf, float *overlap, const float *wind si[7 - 2*i] = grbuf[4*i + 4] - grbuf[4*i + 3]; co[2 + 2*i] = -(grbuf[4*i + 3] + grbuf[4*i + 4]); } - ma_dr_mp3_L3_dct3_9(co); - ma_dr_mp3_L3_dct3_9(si); + drmp3_L3_dct3_9(co); + drmp3_L3_dct3_9(si); si[1] = -si[1]; si[3] = -si[3]; si[5] = -si[5]; si[7] = -si[7]; i = 0; -#if MA_DR_MP3_HAVE_SIMD - if (ma_dr_mp3_have_simd()) for (; i < 8; i += 4) +#if DRMP3_HAVE_SIMD + if (drmp3_have_simd()) for (; i < 8; i += 4) { - ma_dr_mp3_f4 vovl = MA_DR_MP3_VLD(overlap + i); - ma_dr_mp3_f4 vc = MA_DR_MP3_VLD(co + i); - ma_dr_mp3_f4 vs = MA_DR_MP3_VLD(si + i); - ma_dr_mp3_f4 vr0 = MA_DR_MP3_VLD(g_twid9 + i); - ma_dr_mp3_f4 vr1 = MA_DR_MP3_VLD(g_twid9 + 9 + i); - ma_dr_mp3_f4 vw0 = MA_DR_MP3_VLD(window + i); - ma_dr_mp3_f4 vw1 = MA_DR_MP3_VLD(window + 9 + i); - ma_dr_mp3_f4 vsum = MA_DR_MP3_VADD(MA_DR_MP3_VMUL(vc, vr1), MA_DR_MP3_VMUL(vs, vr0)); - MA_DR_MP3_VSTORE(overlap + i, MA_DR_MP3_VSUB(MA_DR_MP3_VMUL(vc, vr0), MA_DR_MP3_VMUL(vs, vr1))); - MA_DR_MP3_VSTORE(grbuf + i, MA_DR_MP3_VSUB(MA_DR_MP3_VMUL(vovl, vw0), MA_DR_MP3_VMUL(vsum, vw1))); - vsum = MA_DR_MP3_VADD(MA_DR_MP3_VMUL(vovl, vw1), MA_DR_MP3_VMUL(vsum, vw0)); - MA_DR_MP3_VSTORE(grbuf + 14 - i, MA_DR_MP3_VREV(vsum)); + drmp3_f4 vovl = DRMP3_VLD(overlap + i); + drmp3_f4 vc = DRMP3_VLD(co + i); + drmp3_f4 vs = DRMP3_VLD(si + i); + drmp3_f4 vr0 = DRMP3_VLD(g_twid9 + i); + drmp3_f4 vr1 = DRMP3_VLD(g_twid9 + 9 + i); + drmp3_f4 vw0 = DRMP3_VLD(window + i); + drmp3_f4 vw1 = DRMP3_VLD(window + 9 + i); + drmp3_f4 vsum = DRMP3_VADD(DRMP3_VMUL(vc, vr1), DRMP3_VMUL(vs, vr0)); + DRMP3_VSTORE(overlap + i, DRMP3_VSUB(DRMP3_VMUL(vc, vr0), DRMP3_VMUL(vs, vr1))); + DRMP3_VSTORE(grbuf + i, DRMP3_VSUB(DRMP3_VMUL(vovl, vw0), DRMP3_VMUL(vsum, vw1))); + vsum = DRMP3_VADD(DRMP3_VMUL(vovl, vw1), DRMP3_VMUL(vsum, vw0)); + DRMP3_VSTORE(grbuf + 14 - i, DRMP3_VREV(vsum)); } #endif for (; i < 9; i++) @@ -91707,7 +91102,7 @@ static void ma_dr_mp3_L3_imdct36(float *grbuf, float *overlap, const float *wind } } } -static void ma_dr_mp3_L3_idct3(float x0, float x1, float x2, float *dst) +static void drmp3_L3_idct3(float x0, float x1, float x2, float *dst) { float m1 = x1*0.86602540f; float a1 = x0 - x2*0.5f; @@ -91715,13 +91110,13 @@ static void ma_dr_mp3_L3_idct3(float x0, float x1, float x2, float *dst) dst[0] = a1 + m1; dst[2] = a1 - m1; } -static void ma_dr_mp3_L3_imdct12(float *x, float *dst, float *overlap) +static void drmp3_L3_imdct12(float *x, float *dst, float *overlap) { static const float g_twid3[6] = { 0.79335334f,0.92387953f,0.99144486f, 0.60876143f,0.38268343f,0.13052619f }; float co[3], si[3]; int i; - ma_dr_mp3_L3_idct3(-x[0], x[6] + x[3], x[12] + x[9], co); - ma_dr_mp3_L3_idct3(x[15], x[12] - x[9], x[6] - x[3], si); + drmp3_L3_idct3(-x[0], x[6] + x[3], x[12] + x[9], co); + drmp3_L3_idct3(x[15], x[12] - x[9], x[6] - x[3], si); si[1] = -si[1]; for (i = 0; i < 3; i++) { @@ -91732,26 +91127,26 @@ static void ma_dr_mp3_L3_imdct12(float *x, float *dst, float *overlap) dst[5 - i] = ovl*g_twid3[5 - i] + sum*g_twid3[2 - i]; } } -static void ma_dr_mp3_L3_imdct_short(float *grbuf, float *overlap, int nbands) +static void drmp3_L3_imdct_short(float *grbuf, float *overlap, int nbands) { for (;nbands > 0; nbands--, overlap += 9, grbuf += 18) { float tmp[18]; - MA_DR_MP3_COPY_MEMORY(tmp, grbuf, sizeof(tmp)); - MA_DR_MP3_COPY_MEMORY(grbuf, overlap, 6*sizeof(float)); - ma_dr_mp3_L3_imdct12(tmp, grbuf + 6, overlap + 6); - ma_dr_mp3_L3_imdct12(tmp + 1, grbuf + 12, overlap + 6); - ma_dr_mp3_L3_imdct12(tmp + 2, overlap, overlap + 6); + DRMP3_COPY_MEMORY(tmp, grbuf, sizeof(tmp)); + DRMP3_COPY_MEMORY(grbuf, overlap, 6*sizeof(float)); + drmp3_L3_imdct12(tmp, grbuf + 6, overlap + 6); + drmp3_L3_imdct12(tmp + 1, grbuf + 12, overlap + 6); + drmp3_L3_imdct12(tmp + 2, overlap, overlap + 6); } } -static void ma_dr_mp3_L3_change_sign(float *grbuf) +static void drmp3_L3_change_sign(float *grbuf) { int b, i; for (b = 0, grbuf += 18; b < 32; b += 2, grbuf += 36) for (i = 1; i < 18; i += 2) grbuf[i] = -grbuf[i]; } -static void ma_dr_mp3_L3_imdct_gr(float *grbuf, float *overlap, unsigned block_type, unsigned n_long_bands) +static void drmp3_L3_imdct_gr(float *grbuf, float *overlap, unsigned block_type, unsigned n_long_bands) { static const float g_mdct_window[2][18] = { { 0.99904822f,0.99144486f,0.97629601f,0.95371695f,0.92387953f,0.88701083f,0.84339145f,0.79335334f,0.73727734f,0.04361938f,0.13052619f,0.21643961f,0.30070580f,0.38268343f,0.46174861f,0.53729961f,0.60876143f,0.67559021f }, @@ -91759,159 +91154,159 @@ static void ma_dr_mp3_L3_imdct_gr(float *grbuf, float *overlap, unsigned block_t }; if (n_long_bands) { - ma_dr_mp3_L3_imdct36(grbuf, overlap, g_mdct_window[0], n_long_bands); + drmp3_L3_imdct36(grbuf, overlap, g_mdct_window[0], n_long_bands); grbuf += 18*n_long_bands; overlap += 9*n_long_bands; } - if (block_type == MA_DR_MP3_SHORT_BLOCK_TYPE) - ma_dr_mp3_L3_imdct_short(grbuf, overlap, 32 - n_long_bands); + if (block_type == DRMP3_SHORT_BLOCK_TYPE) + drmp3_L3_imdct_short(grbuf, overlap, 32 - n_long_bands); else - ma_dr_mp3_L3_imdct36(grbuf, overlap, g_mdct_window[block_type == MA_DR_MP3_STOP_BLOCK_TYPE], 32 - n_long_bands); + drmp3_L3_imdct36(grbuf, overlap, g_mdct_window[block_type == DRMP3_STOP_BLOCK_TYPE], 32 - n_long_bands); } -static void ma_dr_mp3_L3_save_reservoir(ma_dr_mp3dec *h, ma_dr_mp3dec_scratch *s) +static void drmp3_L3_save_reservoir(drmp3dec *h, drmp3dec_scratch *s) { int pos = (s->bs.pos + 7)/8u; int remains = s->bs.limit/8u - pos; - if (remains > MA_DR_MP3_MAX_BITRESERVOIR_BYTES) + if (remains > DRMP3_MAX_BITRESERVOIR_BYTES) { - pos += remains - MA_DR_MP3_MAX_BITRESERVOIR_BYTES; - remains = MA_DR_MP3_MAX_BITRESERVOIR_BYTES; + pos += remains - DRMP3_MAX_BITRESERVOIR_BYTES; + remains = DRMP3_MAX_BITRESERVOIR_BYTES; } if (remains > 0) { - MA_DR_MP3_MOVE_MEMORY(h->reserv_buf, s->maindata + pos, remains); + DRMP3_MOVE_MEMORY(h->reserv_buf, s->maindata + pos, remains); } h->reserv = remains; } -static int ma_dr_mp3_L3_restore_reservoir(ma_dr_mp3dec *h, ma_dr_mp3_bs *bs, ma_dr_mp3dec_scratch *s, int main_data_begin) +static int drmp3_L3_restore_reservoir(drmp3dec *h, drmp3_bs *bs, drmp3dec_scratch *s, int main_data_begin) { int frame_bytes = (bs->limit - bs->pos)/8; - int bytes_have = MA_DR_MP3_MIN(h->reserv, main_data_begin); - MA_DR_MP3_COPY_MEMORY(s->maindata, h->reserv_buf + MA_DR_MP3_MAX(0, h->reserv - main_data_begin), MA_DR_MP3_MIN(h->reserv, main_data_begin)); - MA_DR_MP3_COPY_MEMORY(s->maindata + bytes_have, bs->buf + bs->pos/8, frame_bytes); - ma_dr_mp3_bs_init(&s->bs, s->maindata, bytes_have + frame_bytes); + int bytes_have = DRMP3_MIN(h->reserv, main_data_begin); + DRMP3_COPY_MEMORY(s->maindata, h->reserv_buf + DRMP3_MAX(0, h->reserv - main_data_begin), DRMP3_MIN(h->reserv, main_data_begin)); + DRMP3_COPY_MEMORY(s->maindata + bytes_have, bs->buf + bs->pos/8, frame_bytes); + drmp3_bs_init(&s->bs, s->maindata, bytes_have + frame_bytes); return h->reserv >= main_data_begin; } -static void ma_dr_mp3_L3_decode(ma_dr_mp3dec *h, ma_dr_mp3dec_scratch *s, ma_dr_mp3_L3_gr_info *gr_info, int nch) +static void drmp3_L3_decode(drmp3dec *h, drmp3dec_scratch *s, drmp3_L3_gr_info *gr_info, int nch) { int ch; for (ch = 0; ch < nch; ch++) { int layer3gr_limit = s->bs.pos + gr_info[ch].part_23_length; - ma_dr_mp3_L3_decode_scalefactors(h->header, s->ist_pos[ch], &s->bs, gr_info + ch, s->scf, ch); - ma_dr_mp3_L3_huffman(s->grbuf[ch], &s->bs, gr_info + ch, s->scf, layer3gr_limit); + drmp3_L3_decode_scalefactors(h->header, s->ist_pos[ch], &s->bs, gr_info + ch, s->scf, ch); + drmp3_L3_huffman(s->grbuf[ch], &s->bs, gr_info + ch, s->scf, layer3gr_limit); } - if (MA_DR_MP3_HDR_TEST_I_STEREO(h->header)) + if (DRMP3_HDR_TEST_I_STEREO(h->header)) { - ma_dr_mp3_L3_intensity_stereo(s->grbuf[0], s->ist_pos[1], gr_info, h->header); - } else if (MA_DR_MP3_HDR_IS_MS_STEREO(h->header)) + drmp3_L3_intensity_stereo(s->grbuf[0], s->ist_pos[1], gr_info, h->header); + } else if (DRMP3_HDR_IS_MS_STEREO(h->header)) { - ma_dr_mp3_L3_midside_stereo(s->grbuf[0], 576); + drmp3_L3_midside_stereo(s->grbuf[0], 576); } for (ch = 0; ch < nch; ch++, gr_info++) { int aa_bands = 31; - int n_long_bands = (gr_info->mixed_block_flag ? 2 : 0) << (int)(MA_DR_MP3_HDR_GET_MY_SAMPLE_RATE(h->header) == 2); + int n_long_bands = (gr_info->mixed_block_flag ? 2 : 0) << (int)(DRMP3_HDR_GET_MY_SAMPLE_RATE(h->header) == 2); if (gr_info->n_short_sfb) { aa_bands = n_long_bands - 1; - ma_dr_mp3_L3_reorder(s->grbuf[ch] + n_long_bands*18, s->syn[0], gr_info->sfbtab + gr_info->n_long_sfb); + drmp3_L3_reorder(s->grbuf[ch] + n_long_bands*18, s->syn[0], gr_info->sfbtab + gr_info->n_long_sfb); } - ma_dr_mp3_L3_antialias(s->grbuf[ch], aa_bands); - ma_dr_mp3_L3_imdct_gr(s->grbuf[ch], h->mdct_overlap[ch], gr_info->block_type, n_long_bands); - ma_dr_mp3_L3_change_sign(s->grbuf[ch]); + drmp3_L3_antialias(s->grbuf[ch], aa_bands); + drmp3_L3_imdct_gr(s->grbuf[ch], h->mdct_overlap[ch], gr_info->block_type, n_long_bands); + drmp3_L3_change_sign(s->grbuf[ch]); } } -static void ma_dr_mp3d_DCT_II(float *grbuf, int n) +static void drmp3d_DCT_II(float *grbuf, int n) { static const float g_sec[24] = { 10.19000816f,0.50060302f,0.50241929f,3.40760851f,0.50547093f,0.52249861f,2.05778098f,0.51544732f,0.56694406f,1.48416460f,0.53104258f,0.64682180f,1.16943991f,0.55310392f,0.78815460f,0.97256821f,0.58293498f,1.06067765f,0.83934963f,0.62250412f,1.72244716f,0.74453628f,0.67480832f,5.10114861f }; int i, k = 0; -#if MA_DR_MP3_HAVE_SIMD - if (ma_dr_mp3_have_simd()) for (; k < n; k += 4) +#if DRMP3_HAVE_SIMD + if (drmp3_have_simd()) for (; k < n; k += 4) { - ma_dr_mp3_f4 t[4][8], *x; + drmp3_f4 t[4][8], *x; float *y = grbuf + k; for (x = t[0], i = 0; i < 8; i++, x++) { - ma_dr_mp3_f4 x0 = MA_DR_MP3_VLD(&y[i*18]); - ma_dr_mp3_f4 x1 = MA_DR_MP3_VLD(&y[(15 - i)*18]); - ma_dr_mp3_f4 x2 = MA_DR_MP3_VLD(&y[(16 + i)*18]); - ma_dr_mp3_f4 x3 = MA_DR_MP3_VLD(&y[(31 - i)*18]); - ma_dr_mp3_f4 t0 = MA_DR_MP3_VADD(x0, x3); - ma_dr_mp3_f4 t1 = MA_DR_MP3_VADD(x1, x2); - ma_dr_mp3_f4 t2 = MA_DR_MP3_VMUL_S(MA_DR_MP3_VSUB(x1, x2), g_sec[3*i + 0]); - ma_dr_mp3_f4 t3 = MA_DR_MP3_VMUL_S(MA_DR_MP3_VSUB(x0, x3), g_sec[3*i + 1]); - x[0] = MA_DR_MP3_VADD(t0, t1); - x[8] = MA_DR_MP3_VMUL_S(MA_DR_MP3_VSUB(t0, t1), g_sec[3*i + 2]); - x[16] = MA_DR_MP3_VADD(t3, t2); - x[24] = MA_DR_MP3_VMUL_S(MA_DR_MP3_VSUB(t3, t2), g_sec[3*i + 2]); + drmp3_f4 x0 = DRMP3_VLD(&y[i*18]); + drmp3_f4 x1 = DRMP3_VLD(&y[(15 - i)*18]); + drmp3_f4 x2 = DRMP3_VLD(&y[(16 + i)*18]); + drmp3_f4 x3 = DRMP3_VLD(&y[(31 - i)*18]); + drmp3_f4 t0 = DRMP3_VADD(x0, x3); + drmp3_f4 t1 = DRMP3_VADD(x1, x2); + drmp3_f4 t2 = DRMP3_VMUL_S(DRMP3_VSUB(x1, x2), g_sec[3*i + 0]); + drmp3_f4 t3 = DRMP3_VMUL_S(DRMP3_VSUB(x0, x3), g_sec[3*i + 1]); + x[0] = DRMP3_VADD(t0, t1); + x[8] = DRMP3_VMUL_S(DRMP3_VSUB(t0, t1), g_sec[3*i + 2]); + x[16] = DRMP3_VADD(t3, t2); + x[24] = DRMP3_VMUL_S(DRMP3_VSUB(t3, t2), g_sec[3*i + 2]); } for (x = t[0], i = 0; i < 4; i++, x += 8) { - ma_dr_mp3_f4 x0 = x[0], x1 = x[1], x2 = x[2], x3 = x[3], x4 = x[4], x5 = x[5], x6 = x[6], x7 = x[7], xt; - xt = MA_DR_MP3_VSUB(x0, x7); x0 = MA_DR_MP3_VADD(x0, x7); - x7 = MA_DR_MP3_VSUB(x1, x6); x1 = MA_DR_MP3_VADD(x1, x6); - x6 = MA_DR_MP3_VSUB(x2, x5); x2 = MA_DR_MP3_VADD(x2, x5); - x5 = MA_DR_MP3_VSUB(x3, x4); x3 = MA_DR_MP3_VADD(x3, x4); - x4 = MA_DR_MP3_VSUB(x0, x3); x0 = MA_DR_MP3_VADD(x0, x3); - x3 = MA_DR_MP3_VSUB(x1, x2); x1 = MA_DR_MP3_VADD(x1, x2); - x[0] = MA_DR_MP3_VADD(x0, x1); - x[4] = MA_DR_MP3_VMUL_S(MA_DR_MP3_VSUB(x0, x1), 0.70710677f); - x5 = MA_DR_MP3_VADD(x5, x6); - x6 = MA_DR_MP3_VMUL_S(MA_DR_MP3_VADD(x6, x7), 0.70710677f); - x7 = MA_DR_MP3_VADD(x7, xt); - x3 = MA_DR_MP3_VMUL_S(MA_DR_MP3_VADD(x3, x4), 0.70710677f); - x5 = MA_DR_MP3_VSUB(x5, MA_DR_MP3_VMUL_S(x7, 0.198912367f)); - x7 = MA_DR_MP3_VADD(x7, MA_DR_MP3_VMUL_S(x5, 0.382683432f)); - x5 = MA_DR_MP3_VSUB(x5, MA_DR_MP3_VMUL_S(x7, 0.198912367f)); - x0 = MA_DR_MP3_VSUB(xt, x6); xt = MA_DR_MP3_VADD(xt, x6); - x[1] = MA_DR_MP3_VMUL_S(MA_DR_MP3_VADD(xt, x7), 0.50979561f); - x[2] = MA_DR_MP3_VMUL_S(MA_DR_MP3_VADD(x4, x3), 0.54119611f); - x[3] = MA_DR_MP3_VMUL_S(MA_DR_MP3_VSUB(x0, x5), 0.60134488f); - x[5] = MA_DR_MP3_VMUL_S(MA_DR_MP3_VADD(x0, x5), 0.89997619f); - x[6] = MA_DR_MP3_VMUL_S(MA_DR_MP3_VSUB(x4, x3), 1.30656302f); - x[7] = MA_DR_MP3_VMUL_S(MA_DR_MP3_VSUB(xt, x7), 2.56291556f); + drmp3_f4 x0 = x[0], x1 = x[1], x2 = x[2], x3 = x[3], x4 = x[4], x5 = x[5], x6 = x[6], x7 = x[7], xt; + xt = DRMP3_VSUB(x0, x7); x0 = DRMP3_VADD(x0, x7); + x7 = DRMP3_VSUB(x1, x6); x1 = DRMP3_VADD(x1, x6); + x6 = DRMP3_VSUB(x2, x5); x2 = DRMP3_VADD(x2, x5); + x5 = DRMP3_VSUB(x3, x4); x3 = DRMP3_VADD(x3, x4); + x4 = DRMP3_VSUB(x0, x3); x0 = DRMP3_VADD(x0, x3); + x3 = DRMP3_VSUB(x1, x2); x1 = DRMP3_VADD(x1, x2); + x[0] = DRMP3_VADD(x0, x1); + x[4] = DRMP3_VMUL_S(DRMP3_VSUB(x0, x1), 0.70710677f); + x5 = DRMP3_VADD(x5, x6); + x6 = DRMP3_VMUL_S(DRMP3_VADD(x6, x7), 0.70710677f); + x7 = DRMP3_VADD(x7, xt); + x3 = DRMP3_VMUL_S(DRMP3_VADD(x3, x4), 0.70710677f); + x5 = DRMP3_VSUB(x5, DRMP3_VMUL_S(x7, 0.198912367f)); + x7 = DRMP3_VADD(x7, DRMP3_VMUL_S(x5, 0.382683432f)); + x5 = DRMP3_VSUB(x5, DRMP3_VMUL_S(x7, 0.198912367f)); + x0 = DRMP3_VSUB(xt, x6); xt = DRMP3_VADD(xt, x6); + x[1] = DRMP3_VMUL_S(DRMP3_VADD(xt, x7), 0.50979561f); + x[2] = DRMP3_VMUL_S(DRMP3_VADD(x4, x3), 0.54119611f); + x[3] = DRMP3_VMUL_S(DRMP3_VSUB(x0, x5), 0.60134488f); + x[5] = DRMP3_VMUL_S(DRMP3_VADD(x0, x5), 0.89997619f); + x[6] = DRMP3_VMUL_S(DRMP3_VSUB(x4, x3), 1.30656302f); + x[7] = DRMP3_VMUL_S(DRMP3_VSUB(xt, x7), 2.56291556f); } if (k > n - 3) { -#if MA_DR_MP3_HAVE_SSE -#define MA_DR_MP3_VSAVE2(i, v) _mm_storel_pi((__m64 *)(void*)&y[i*18], v) +#if DRMP3_HAVE_SSE +#define DRMP3_VSAVE2(i, v) _mm_storel_pi((__m64 *)(void*)&y[i*18], v) #else -#define MA_DR_MP3_VSAVE2(i, v) vst1_f32((float32_t *)&y[(i)*18], vget_low_f32(v)) +#define DRMP3_VSAVE2(i, v) vst1_f32((float32_t *)&y[(i)*18], vget_low_f32(v)) #endif for (i = 0; i < 7; i++, y += 4*18) { - ma_dr_mp3_f4 s = MA_DR_MP3_VADD(t[3][i], t[3][i + 1]); - MA_DR_MP3_VSAVE2(0, t[0][i]); - MA_DR_MP3_VSAVE2(1, MA_DR_MP3_VADD(t[2][i], s)); - MA_DR_MP3_VSAVE2(2, MA_DR_MP3_VADD(t[1][i], t[1][i + 1])); - MA_DR_MP3_VSAVE2(3, MA_DR_MP3_VADD(t[2][1 + i], s)); - } - MA_DR_MP3_VSAVE2(0, t[0][7]); - MA_DR_MP3_VSAVE2(1, MA_DR_MP3_VADD(t[2][7], t[3][7])); - MA_DR_MP3_VSAVE2(2, t[1][7]); - MA_DR_MP3_VSAVE2(3, t[3][7]); + drmp3_f4 s = DRMP3_VADD(t[3][i], t[3][i + 1]); + DRMP3_VSAVE2(0, t[0][i]); + DRMP3_VSAVE2(1, DRMP3_VADD(t[2][i], s)); + DRMP3_VSAVE2(2, DRMP3_VADD(t[1][i], t[1][i + 1])); + DRMP3_VSAVE2(3, DRMP3_VADD(t[2][1 + i], s)); + } + DRMP3_VSAVE2(0, t[0][7]); + DRMP3_VSAVE2(1, DRMP3_VADD(t[2][7], t[3][7])); + DRMP3_VSAVE2(2, t[1][7]); + DRMP3_VSAVE2(3, t[3][7]); } else { -#define MA_DR_MP3_VSAVE4(i, v) MA_DR_MP3_VSTORE(&y[(i)*18], v) +#define DRMP3_VSAVE4(i, v) DRMP3_VSTORE(&y[(i)*18], v) for (i = 0; i < 7; i++, y += 4*18) { - ma_dr_mp3_f4 s = MA_DR_MP3_VADD(t[3][i], t[3][i + 1]); - MA_DR_MP3_VSAVE4(0, t[0][i]); - MA_DR_MP3_VSAVE4(1, MA_DR_MP3_VADD(t[2][i], s)); - MA_DR_MP3_VSAVE4(2, MA_DR_MP3_VADD(t[1][i], t[1][i + 1])); - MA_DR_MP3_VSAVE4(3, MA_DR_MP3_VADD(t[2][1 + i], s)); + drmp3_f4 s = DRMP3_VADD(t[3][i], t[3][i + 1]); + DRMP3_VSAVE4(0, t[0][i]); + DRMP3_VSAVE4(1, DRMP3_VADD(t[2][i], s)); + DRMP3_VSAVE4(2, DRMP3_VADD(t[1][i], t[1][i + 1])); + DRMP3_VSAVE4(3, DRMP3_VADD(t[2][1 + i], s)); } - MA_DR_MP3_VSAVE4(0, t[0][7]); - MA_DR_MP3_VSAVE4(1, MA_DR_MP3_VADD(t[2][7], t[3][7])); - MA_DR_MP3_VSAVE4(2, t[1][7]); - MA_DR_MP3_VSAVE4(3, t[3][7]); + DRMP3_VSAVE4(0, t[0][7]); + DRMP3_VSAVE4(1, DRMP3_VADD(t[2][7], t[3][7])); + DRMP3_VSAVE4(2, t[1][7]); + DRMP3_VSAVE4(3, t[3][7]); } } else #endif -#ifdef MA_DR_MP3_ONLY_SIMD +#ifdef DR_MP3_ONLY_SIMD {} #else for (; k < n; k++) @@ -91972,31 +91367,31 @@ static void ma_dr_mp3d_DCT_II(float *grbuf, int n) } #endif } -#ifndef MA_DR_MP3_FLOAT_OUTPUT -typedef ma_int16 ma_dr_mp3d_sample_t; -static ma_int16 ma_dr_mp3d_scale_pcm(float sample) +#ifndef DR_MP3_FLOAT_OUTPUT +typedef drmp3_int16 drmp3d_sample_t; +static drmp3_int16 drmp3d_scale_pcm(float sample) { - ma_int16 s; -#if MA_DR_MP3_HAVE_ARMV6 - ma_int32 s32 = (ma_int32)(sample + .5f); + drmp3_int16 s; +#if DRMP3_HAVE_ARMV6 + drmp3_int32 s32 = (drmp3_int32)(sample + .5f); s32 -= (s32 < 0); - s = (ma_int16)ma_dr_mp3_clip_int16_arm(s32); + s = (drmp3_int16)drmp3_clip_int16_arm(s32); #else - if (sample >= 32766.5f) return (ma_int16) 32767; - if (sample <= -32767.5f) return (ma_int16)-32768; - s = (ma_int16)(sample + .5f); + if (sample >= 32766.5) return (drmp3_int16) 32767; + if (sample <= -32767.5) return (drmp3_int16)-32768; + s = (drmp3_int16)(sample + .5f); s -= (s < 0); #endif return s; } #else -typedef float ma_dr_mp3d_sample_t; -static float ma_dr_mp3d_scale_pcm(float sample) +typedef float drmp3d_sample_t; +static float drmp3d_scale_pcm(float sample) { return sample*(1.f/32768.f); } #endif -static void ma_dr_mp3d_synth_pair(ma_dr_mp3d_sample_t *pcm, int nch, const float *z) +static void drmp3d_synth_pair(drmp3d_sample_t *pcm, int nch, const float *z) { float a; a = (z[14*64] - z[ 0]) * 29; @@ -92007,7 +91402,7 @@ static void ma_dr_mp3d_synth_pair(ma_dr_mp3d_sample_t *pcm, int nch, const float a += (z[ 5*64] + z[ 9*64]) * 6574; a += (z[ 8*64] - z[ 6*64]) * 37489; a += z[ 7*64] * 75038; - pcm[0] = ma_dr_mp3d_scale_pcm(a); + pcm[0] = drmp3d_scale_pcm(a); z += 2; a = z[14*64] * 104; a += z[12*64] * 1567; @@ -92017,13 +91412,13 @@ static void ma_dr_mp3d_synth_pair(ma_dr_mp3d_sample_t *pcm, int nch, const float a += z[ 4*64] * -45; a += z[ 2*64] * 146; a += z[ 0*64] * -5; - pcm[16*nch] = ma_dr_mp3d_scale_pcm(a); + pcm[16*nch] = drmp3d_scale_pcm(a); } -static void ma_dr_mp3d_synth(float *xl, ma_dr_mp3d_sample_t *dstl, int nch, float *lins) +static void drmp3d_synth(float *xl, drmp3d_sample_t *dstl, int nch, float *lins) { int i; float *xr = xl + 576*(nch - 1); - ma_dr_mp3d_sample_t *dstr = dstl + (nch - 1); + drmp3d_sample_t *dstr = dstl + (nch - 1); static const float g_win[] = { -1,26,-31,208,218,401,-519,2063,2000,4788,-5517,7134,5959,35640,-39336,74992, -1,24,-35,202,222,347,-581,2080,1952,4425,-5879,7640,5288,33791,-41176,74856, @@ -92051,18 +91446,18 @@ static void ma_dr_mp3d_synth(float *xl, ma_dr_mp3d_sample_t *dstl, int nch, floa zlin[4*31 + 1] = xr[1 + 18*16]; zlin[4*31 + 2] = xl[1]; zlin[4*31 + 3] = xr[1]; - ma_dr_mp3d_synth_pair(dstr, nch, lins + 4*15 + 1); - ma_dr_mp3d_synth_pair(dstr + 32*nch, nch, lins + 4*15 + 64 + 1); - ma_dr_mp3d_synth_pair(dstl, nch, lins + 4*15); - ma_dr_mp3d_synth_pair(dstl + 32*nch, nch, lins + 4*15 + 64); -#if MA_DR_MP3_HAVE_SIMD - if (ma_dr_mp3_have_simd()) for (i = 14; i >= 0; i--) + drmp3d_synth_pair(dstr, nch, lins + 4*15 + 1); + drmp3d_synth_pair(dstr + 32*nch, nch, lins + 4*15 + 64 + 1); + drmp3d_synth_pair(dstl, nch, lins + 4*15); + drmp3d_synth_pair(dstl + 32*nch, nch, lins + 4*15 + 64); +#if DRMP3_HAVE_SIMD + if (drmp3_have_simd()) for (i = 14; i >= 0; i--) { -#define MA_DR_MP3_VLOAD(k) ma_dr_mp3_f4 w0 = MA_DR_MP3_VSET(*w++); ma_dr_mp3_f4 w1 = MA_DR_MP3_VSET(*w++); ma_dr_mp3_f4 vz = MA_DR_MP3_VLD(&zlin[4*i - 64*k]); ma_dr_mp3_f4 vy = MA_DR_MP3_VLD(&zlin[4*i - 64*(15 - k)]); -#define MA_DR_MP3_V0(k) { MA_DR_MP3_VLOAD(k) b = MA_DR_MP3_VADD(MA_DR_MP3_VMUL(vz, w1), MA_DR_MP3_VMUL(vy, w0)) ; a = MA_DR_MP3_VSUB(MA_DR_MP3_VMUL(vz, w0), MA_DR_MP3_VMUL(vy, w1)); } -#define MA_DR_MP3_V1(k) { MA_DR_MP3_VLOAD(k) b = MA_DR_MP3_VADD(b, MA_DR_MP3_VADD(MA_DR_MP3_VMUL(vz, w1), MA_DR_MP3_VMUL(vy, w0))); a = MA_DR_MP3_VADD(a, MA_DR_MP3_VSUB(MA_DR_MP3_VMUL(vz, w0), MA_DR_MP3_VMUL(vy, w1))); } -#define MA_DR_MP3_V2(k) { MA_DR_MP3_VLOAD(k) b = MA_DR_MP3_VADD(b, MA_DR_MP3_VADD(MA_DR_MP3_VMUL(vz, w1), MA_DR_MP3_VMUL(vy, w0))); a = MA_DR_MP3_VADD(a, MA_DR_MP3_VSUB(MA_DR_MP3_VMUL(vy, w1), MA_DR_MP3_VMUL(vz, w0))); } - ma_dr_mp3_f4 a, b; +#define DRMP3_VLOAD(k) drmp3_f4 w0 = DRMP3_VSET(*w++); drmp3_f4 w1 = DRMP3_VSET(*w++); drmp3_f4 vz = DRMP3_VLD(&zlin[4*i - 64*k]); drmp3_f4 vy = DRMP3_VLD(&zlin[4*i - 64*(15 - k)]); +#define DRMP3_V0(k) { DRMP3_VLOAD(k) b = DRMP3_VADD(DRMP3_VMUL(vz, w1), DRMP3_VMUL(vy, w0)) ; a = DRMP3_VSUB(DRMP3_VMUL(vz, w0), DRMP3_VMUL(vy, w1)); } +#define DRMP3_V1(k) { DRMP3_VLOAD(k) b = DRMP3_VADD(b, DRMP3_VADD(DRMP3_VMUL(vz, w1), DRMP3_VMUL(vy, w0))); a = DRMP3_VADD(a, DRMP3_VSUB(DRMP3_VMUL(vz, w0), DRMP3_VMUL(vy, w1))); } +#define DRMP3_V2(k) { DRMP3_VLOAD(k) b = DRMP3_VADD(b, DRMP3_VADD(DRMP3_VMUL(vz, w1), DRMP3_VMUL(vy, w0))); a = DRMP3_VADD(a, DRMP3_VSUB(DRMP3_VMUL(vy, w1), DRMP3_VMUL(vz, w0))); } + drmp3_f4 a, b; zlin[4*i] = xl[18*(31 - i)]; zlin[4*i + 1] = xr[18*(31 - i)]; zlin[4*i + 2] = xl[1 + 18*(31 - i)]; @@ -92071,28 +91466,28 @@ static void ma_dr_mp3d_synth(float *xl, ma_dr_mp3d_sample_t *dstl, int nch, floa zlin[4*i + 64 + 1] = xr[1 + 18*(1 + i)]; zlin[4*i - 64 + 2] = xl[18*(1 + i)]; zlin[4*i - 64 + 3] = xr[18*(1 + i)]; - MA_DR_MP3_V0(0) MA_DR_MP3_V2(1) MA_DR_MP3_V1(2) MA_DR_MP3_V2(3) MA_DR_MP3_V1(4) MA_DR_MP3_V2(5) MA_DR_MP3_V1(6) MA_DR_MP3_V2(7) + DRMP3_V0(0) DRMP3_V2(1) DRMP3_V1(2) DRMP3_V2(3) DRMP3_V1(4) DRMP3_V2(5) DRMP3_V1(6) DRMP3_V2(7) { -#ifndef MA_DR_MP3_FLOAT_OUTPUT -#if MA_DR_MP3_HAVE_SSE - static const ma_dr_mp3_f4 g_max = { 32767.0f, 32767.0f, 32767.0f, 32767.0f }; - static const ma_dr_mp3_f4 g_min = { -32768.0f, -32768.0f, -32768.0f, -32768.0f }; +#ifndef DR_MP3_FLOAT_OUTPUT +#if DRMP3_HAVE_SSE + static const drmp3_f4 g_max = { 32767.0f, 32767.0f, 32767.0f, 32767.0f }; + static const drmp3_f4 g_min = { -32768.0f, -32768.0f, -32768.0f, -32768.0f }; __m128i pcm8 = _mm_packs_epi32(_mm_cvtps_epi32(_mm_max_ps(_mm_min_ps(a, g_max), g_min)), _mm_cvtps_epi32(_mm_max_ps(_mm_min_ps(b, g_max), g_min))); - dstr[(15 - i)*nch] = (ma_int16)_mm_extract_epi16(pcm8, 1); - dstr[(17 + i)*nch] = (ma_int16)_mm_extract_epi16(pcm8, 5); - dstl[(15 - i)*nch] = (ma_int16)_mm_extract_epi16(pcm8, 0); - dstl[(17 + i)*nch] = (ma_int16)_mm_extract_epi16(pcm8, 4); - dstr[(47 - i)*nch] = (ma_int16)_mm_extract_epi16(pcm8, 3); - dstr[(49 + i)*nch] = (ma_int16)_mm_extract_epi16(pcm8, 7); - dstl[(47 - i)*nch] = (ma_int16)_mm_extract_epi16(pcm8, 2); - dstl[(49 + i)*nch] = (ma_int16)_mm_extract_epi16(pcm8, 6); + dstr[(15 - i)*nch] = (drmp3_int16)_mm_extract_epi16(pcm8, 1); + dstr[(17 + i)*nch] = (drmp3_int16)_mm_extract_epi16(pcm8, 5); + dstl[(15 - i)*nch] = (drmp3_int16)_mm_extract_epi16(pcm8, 0); + dstl[(17 + i)*nch] = (drmp3_int16)_mm_extract_epi16(pcm8, 4); + dstr[(47 - i)*nch] = (drmp3_int16)_mm_extract_epi16(pcm8, 3); + dstr[(49 + i)*nch] = (drmp3_int16)_mm_extract_epi16(pcm8, 7); + dstl[(47 - i)*nch] = (drmp3_int16)_mm_extract_epi16(pcm8, 2); + dstl[(49 + i)*nch] = (drmp3_int16)_mm_extract_epi16(pcm8, 6); #else int16x4_t pcma, pcmb; - a = MA_DR_MP3_VADD(a, MA_DR_MP3_VSET(0.5f)); - b = MA_DR_MP3_VADD(b, MA_DR_MP3_VSET(0.5f)); - pcma = vqmovn_s32(vqaddq_s32(vcvtq_s32_f32(a), vreinterpretq_s32_u32(vcltq_f32(a, MA_DR_MP3_VSET(0))))); - pcmb = vqmovn_s32(vqaddq_s32(vcvtq_s32_f32(b), vreinterpretq_s32_u32(vcltq_f32(b, MA_DR_MP3_VSET(0))))); + a = DRMP3_VADD(a, DRMP3_VSET(0.5f)); + b = DRMP3_VADD(b, DRMP3_VSET(0.5f)); + pcma = vqmovn_s32(vqaddq_s32(vcvtq_s32_f32(a), vreinterpretq_s32_u32(vcltq_f32(a, DRMP3_VSET(0))))); + pcmb = vqmovn_s32(vqaddq_s32(vcvtq_s32_f32(b), vreinterpretq_s32_u32(vcltq_f32(b, DRMP3_VSET(0))))); vst1_lane_s16(dstr + (15 - i)*nch, pcma, 1); vst1_lane_s16(dstr + (17 + i)*nch, pcmb, 1); vst1_lane_s16(dstl + (15 - i)*nch, pcma, 0); @@ -92103,14 +91498,14 @@ static void ma_dr_mp3d_synth(float *xl, ma_dr_mp3d_sample_t *dstl, int nch, floa vst1_lane_s16(dstl + (49 + i)*nch, pcmb, 2); #endif #else - #if MA_DR_MP3_HAVE_SSE - static const ma_dr_mp3_f4 g_scale = { 1.0f/32768.0f, 1.0f/32768.0f, 1.0f/32768.0f, 1.0f/32768.0f }; + #if DRMP3_HAVE_SSE + static const drmp3_f4 g_scale = { 1.0f/32768.0f, 1.0f/32768.0f, 1.0f/32768.0f, 1.0f/32768.0f }; #else - const ma_dr_mp3_f4 g_scale = vdupq_n_f32(1.0f/32768.0f); + const drmp3_f4 g_scale = vdupq_n_f32(1.0f/32768.0f); #endif - a = MA_DR_MP3_VMUL(a, g_scale); - b = MA_DR_MP3_VMUL(b, g_scale); -#if MA_DR_MP3_HAVE_SSE + a = DRMP3_VMUL(a, g_scale); + b = DRMP3_VMUL(b, g_scale); +#if DRMP3_HAVE_SSE _mm_store_ss(dstr + (15 - i)*nch, _mm_shuffle_ps(a, a, _MM_SHUFFLE(1, 1, 1, 1))); _mm_store_ss(dstr + (17 + i)*nch, _mm_shuffle_ps(b, b, _MM_SHUFFLE(1, 1, 1, 1))); _mm_store_ss(dstl + (15 - i)*nch, _mm_shuffle_ps(a, a, _MM_SHUFFLE(0, 0, 0, 0))); @@ -92133,15 +91528,15 @@ static void ma_dr_mp3d_synth(float *xl, ma_dr_mp3d_sample_t *dstl, int nch, floa } } else #endif -#ifdef MA_DR_MP3_ONLY_SIMD +#ifdef DR_MP3_ONLY_SIMD {} #else for (i = 14; i >= 0; i--) { -#define MA_DR_MP3_LOAD(k) float w0 = *w++; float w1 = *w++; float *vz = &zlin[4*i - k*64]; float *vy = &zlin[4*i - (15 - k)*64]; -#define MA_DR_MP3_S0(k) { int j; MA_DR_MP3_LOAD(k); for (j = 0; j < 4; j++) b[j] = vz[j]*w1 + vy[j]*w0, a[j] = vz[j]*w0 - vy[j]*w1; } -#define MA_DR_MP3_S1(k) { int j; MA_DR_MP3_LOAD(k); for (j = 0; j < 4; j++) b[j] += vz[j]*w1 + vy[j]*w0, a[j] += vz[j]*w0 - vy[j]*w1; } -#define MA_DR_MP3_S2(k) { int j; MA_DR_MP3_LOAD(k); for (j = 0; j < 4; j++) b[j] += vz[j]*w1 + vy[j]*w0, a[j] += vy[j]*w1 - vz[j]*w0; } +#define DRMP3_LOAD(k) float w0 = *w++; float w1 = *w++; float *vz = &zlin[4*i - k*64]; float *vy = &zlin[4*i - (15 - k)*64]; +#define DRMP3_S0(k) { int j; DRMP3_LOAD(k); for (j = 0; j < 4; j++) b[j] = vz[j]*w1 + vy[j]*w0, a[j] = vz[j]*w0 - vy[j]*w1; } +#define DRMP3_S1(k) { int j; DRMP3_LOAD(k); for (j = 0; j < 4; j++) b[j] += vz[j]*w1 + vy[j]*w0, a[j] += vz[j]*w0 - vy[j]*w1; } +#define DRMP3_S2(k) { int j; DRMP3_LOAD(k); for (j = 0; j < 4; j++) b[j] += vz[j]*w1 + vy[j]*w0, a[j] += vy[j]*w1 - vz[j]*w0; } float a[4], b[4]; zlin[4*i] = xl[18*(31 - i)]; zlin[4*i + 1] = xr[18*(31 - i)]; @@ -92151,31 +91546,31 @@ static void ma_dr_mp3d_synth(float *xl, ma_dr_mp3d_sample_t *dstl, int nch, floa zlin[4*(i + 16) + 1] = xr[1 + 18*(1 + i)]; zlin[4*(i - 16) + 2] = xl[18*(1 + i)]; zlin[4*(i - 16) + 3] = xr[18*(1 + i)]; - MA_DR_MP3_S0(0) MA_DR_MP3_S2(1) MA_DR_MP3_S1(2) MA_DR_MP3_S2(3) MA_DR_MP3_S1(4) MA_DR_MP3_S2(5) MA_DR_MP3_S1(6) MA_DR_MP3_S2(7) - dstr[(15 - i)*nch] = ma_dr_mp3d_scale_pcm(a[1]); - dstr[(17 + i)*nch] = ma_dr_mp3d_scale_pcm(b[1]); - dstl[(15 - i)*nch] = ma_dr_mp3d_scale_pcm(a[0]); - dstl[(17 + i)*nch] = ma_dr_mp3d_scale_pcm(b[0]); - dstr[(47 - i)*nch] = ma_dr_mp3d_scale_pcm(a[3]); - dstr[(49 + i)*nch] = ma_dr_mp3d_scale_pcm(b[3]); - dstl[(47 - i)*nch] = ma_dr_mp3d_scale_pcm(a[2]); - dstl[(49 + i)*nch] = ma_dr_mp3d_scale_pcm(b[2]); + DRMP3_S0(0) DRMP3_S2(1) DRMP3_S1(2) DRMP3_S2(3) DRMP3_S1(4) DRMP3_S2(5) DRMP3_S1(6) DRMP3_S2(7) + dstr[(15 - i)*nch] = drmp3d_scale_pcm(a[1]); + dstr[(17 + i)*nch] = drmp3d_scale_pcm(b[1]); + dstl[(15 - i)*nch] = drmp3d_scale_pcm(a[0]); + dstl[(17 + i)*nch] = drmp3d_scale_pcm(b[0]); + dstr[(47 - i)*nch] = drmp3d_scale_pcm(a[3]); + dstr[(49 + i)*nch] = drmp3d_scale_pcm(b[3]); + dstl[(47 - i)*nch] = drmp3d_scale_pcm(a[2]); + dstl[(49 + i)*nch] = drmp3d_scale_pcm(b[2]); } #endif } -static void ma_dr_mp3d_synth_granule(float *qmf_state, float *grbuf, int nbands, int nch, ma_dr_mp3d_sample_t *pcm, float *lins) +static void drmp3d_synth_granule(float *qmf_state, float *grbuf, int nbands, int nch, drmp3d_sample_t *pcm, float *lins) { int i; for (i = 0; i < nch; i++) { - ma_dr_mp3d_DCT_II(grbuf + 576*i, nbands); + drmp3d_DCT_II(grbuf + 576*i, nbands); } - MA_DR_MP3_COPY_MEMORY(lins, qmf_state, sizeof(float)*15*64); + DRMP3_COPY_MEMORY(lins, qmf_state, sizeof(float)*15*64); for (i = 0; i < nbands; i += 2) { - ma_dr_mp3d_synth(grbuf + i, pcm + 32*nch*i, nch, lins + i*64); + drmp3d_synth(grbuf + i, pcm + 32*nch*i, nch, lins + i*64); } -#ifndef MA_DR_MP3_NONSTANDARD_BUT_LOGICAL +#ifndef DR_MP3_NONSTANDARD_BUT_LOGICAL if (nch == 1) { for (i = 0; i < 15*64; i += 2) @@ -92185,38 +91580,38 @@ static void ma_dr_mp3d_synth_granule(float *qmf_state, float *grbuf, int nbands, } else #endif { - MA_DR_MP3_COPY_MEMORY(qmf_state, lins + nbands*64, sizeof(float)*15*64); + DRMP3_COPY_MEMORY(qmf_state, lins + nbands*64, sizeof(float)*15*64); } } -static int ma_dr_mp3d_match_frame(const ma_uint8 *hdr, int mp3_bytes, int frame_bytes) +static int drmp3d_match_frame(const drmp3_uint8 *hdr, int mp3_bytes, int frame_bytes) { int i, nmatch; - for (i = 0, nmatch = 0; nmatch < MA_DR_MP3_MAX_FRAME_SYNC_MATCHES; nmatch++) + for (i = 0, nmatch = 0; nmatch < DRMP3_MAX_FRAME_SYNC_MATCHES; nmatch++) { - i += ma_dr_mp3_hdr_frame_bytes(hdr + i, frame_bytes) + ma_dr_mp3_hdr_padding(hdr + i); - if (i + MA_DR_MP3_HDR_SIZE > mp3_bytes) + i += drmp3_hdr_frame_bytes(hdr + i, frame_bytes) + drmp3_hdr_padding(hdr + i); + if (i + DRMP3_HDR_SIZE > mp3_bytes) return nmatch > 0; - if (!ma_dr_mp3_hdr_compare(hdr, hdr + i)) + if (!drmp3_hdr_compare(hdr, hdr + i)) return 0; } return 1; } -static int ma_dr_mp3d_find_frame(const ma_uint8 *mp3, int mp3_bytes, int *free_format_bytes, int *ptr_frame_bytes) +static int drmp3d_find_frame(const drmp3_uint8 *mp3, int mp3_bytes, int *free_format_bytes, int *ptr_frame_bytes) { int i, k; - for (i = 0; i < mp3_bytes - MA_DR_MP3_HDR_SIZE; i++, mp3++) + for (i = 0; i < mp3_bytes - DRMP3_HDR_SIZE; i++, mp3++) { - if (ma_dr_mp3_hdr_valid(mp3)) + if (drmp3_hdr_valid(mp3)) { - int frame_bytes = ma_dr_mp3_hdr_frame_bytes(mp3, *free_format_bytes); - int frame_and_padding = frame_bytes + ma_dr_mp3_hdr_padding(mp3); - for (k = MA_DR_MP3_HDR_SIZE; !frame_bytes && k < MA_DR_MP3_MAX_FREE_FORMAT_FRAME_SIZE && i + 2*k < mp3_bytes - MA_DR_MP3_HDR_SIZE; k++) + int frame_bytes = drmp3_hdr_frame_bytes(mp3, *free_format_bytes); + int frame_and_padding = frame_bytes + drmp3_hdr_padding(mp3); + for (k = DRMP3_HDR_SIZE; !frame_bytes && k < DRMP3_MAX_FREE_FORMAT_FRAME_SIZE && i + 2*k < mp3_bytes - DRMP3_HDR_SIZE; k++) { - if (ma_dr_mp3_hdr_compare(mp3, mp3 + k)) + if (drmp3_hdr_compare(mp3, mp3 + k)) { - int fb = k - ma_dr_mp3_hdr_padding(mp3); - int nextfb = fb + ma_dr_mp3_hdr_padding(mp3 + k); - if (i + k + nextfb + MA_DR_MP3_HDR_SIZE > mp3_bytes || !ma_dr_mp3_hdr_compare(mp3, mp3 + k + nextfb)) + int fb = k - drmp3_hdr_padding(mp3); + int nextfb = fb + drmp3_hdr_padding(mp3 + k); + if (i + k + nextfb + DRMP3_HDR_SIZE > mp3_bytes || !drmp3_hdr_compare(mp3, mp3 + k + nextfb)) continue; frame_and_padding = k; frame_bytes = fb; @@ -92224,7 +91619,7 @@ static int ma_dr_mp3d_find_frame(const ma_uint8 *mp3, int mp3_bytes, int *free_f } } if ((frame_bytes && i + frame_and_padding <= mp3_bytes && - ma_dr_mp3d_match_frame(mp3, mp3_bytes - i, frame_bytes)) || + drmp3d_match_frame(mp3, mp3_bytes - i, frame_bytes)) || (!i && frame_and_padding == mp3_bytes)) { *ptr_frame_bytes = frame_and_padding; @@ -92236,28 +91631,28 @@ static int ma_dr_mp3d_find_frame(const ma_uint8 *mp3, int mp3_bytes, int *free_f *ptr_frame_bytes = 0; return mp3_bytes; } -MA_API void ma_dr_mp3dec_init(ma_dr_mp3dec *dec) +DRMP3_API void drmp3dec_init(drmp3dec *dec) { dec->header[0] = 0; } -MA_API int ma_dr_mp3dec_decode_frame(ma_dr_mp3dec *dec, const ma_uint8 *mp3, int mp3_bytes, void *pcm, ma_dr_mp3dec_frame_info *info) +DRMP3_API int drmp3dec_decode_frame(drmp3dec *dec, const drmp3_uint8 *mp3, int mp3_bytes, void *pcm, drmp3dec_frame_info *info) { int i = 0, igr, frame_size = 0, success = 1; - const ma_uint8 *hdr; - ma_dr_mp3_bs bs_frame[1]; - ma_dr_mp3dec_scratch scratch; - if (mp3_bytes > 4 && dec->header[0] == 0xff && ma_dr_mp3_hdr_compare(dec->header, mp3)) + const drmp3_uint8 *hdr; + drmp3_bs bs_frame[1]; + drmp3dec_scratch scratch; + if (mp3_bytes > 4 && dec->header[0] == 0xff && drmp3_hdr_compare(dec->header, mp3)) { - frame_size = ma_dr_mp3_hdr_frame_bytes(mp3, dec->free_format_bytes) + ma_dr_mp3_hdr_padding(mp3); - if (frame_size != mp3_bytes && (frame_size + MA_DR_MP3_HDR_SIZE > mp3_bytes || !ma_dr_mp3_hdr_compare(mp3, mp3 + frame_size))) + frame_size = drmp3_hdr_frame_bytes(mp3, dec->free_format_bytes) + drmp3_hdr_padding(mp3); + if (frame_size != mp3_bytes && (frame_size + DRMP3_HDR_SIZE > mp3_bytes || !drmp3_hdr_compare(mp3, mp3 + frame_size))) { frame_size = 0; } } if (!frame_size) { - MA_DR_MP3_ZERO_MEMORY(dec, sizeof(ma_dr_mp3dec)); - i = ma_dr_mp3d_find_frame(mp3, mp3_bytes, &dec->free_format_bytes, &frame_size); + DRMP3_ZERO_MEMORY(dec, sizeof(drmp3dec)); + i = drmp3d_find_frame(mp3, mp3_bytes, &dec->free_format_bytes, &frame_size); if (!frame_size || i + frame_size > mp3_bytes) { info->frame_bytes = i; @@ -92265,96 +91660,96 @@ MA_API int ma_dr_mp3dec_decode_frame(ma_dr_mp3dec *dec, const ma_uint8 *mp3, int } } hdr = mp3 + i; - MA_DR_MP3_COPY_MEMORY(dec->header, hdr, MA_DR_MP3_HDR_SIZE); + DRMP3_COPY_MEMORY(dec->header, hdr, DRMP3_HDR_SIZE); info->frame_bytes = i + frame_size; - info->channels = MA_DR_MP3_HDR_IS_MONO(hdr) ? 1 : 2; - info->hz = ma_dr_mp3_hdr_sample_rate_hz(hdr); - info->layer = 4 - MA_DR_MP3_HDR_GET_LAYER(hdr); - info->bitrate_kbps = ma_dr_mp3_hdr_bitrate_kbps(hdr); - ma_dr_mp3_bs_init(bs_frame, hdr + MA_DR_MP3_HDR_SIZE, frame_size - MA_DR_MP3_HDR_SIZE); - if (MA_DR_MP3_HDR_IS_CRC(hdr)) + info->channels = DRMP3_HDR_IS_MONO(hdr) ? 1 : 2; + info->hz = drmp3_hdr_sample_rate_hz(hdr); + info->layer = 4 - DRMP3_HDR_GET_LAYER(hdr); + info->bitrate_kbps = drmp3_hdr_bitrate_kbps(hdr); + drmp3_bs_init(bs_frame, hdr + DRMP3_HDR_SIZE, frame_size - DRMP3_HDR_SIZE); + if (DRMP3_HDR_IS_CRC(hdr)) { - ma_dr_mp3_bs_get_bits(bs_frame, 16); + drmp3_bs_get_bits(bs_frame, 16); } if (info->layer == 3) { - int main_data_begin = ma_dr_mp3_L3_read_side_info(bs_frame, scratch.gr_info, hdr); + int main_data_begin = drmp3_L3_read_side_info(bs_frame, scratch.gr_info, hdr); if (main_data_begin < 0 || bs_frame->pos > bs_frame->limit) { - ma_dr_mp3dec_init(dec); + drmp3dec_init(dec); return 0; } - success = ma_dr_mp3_L3_restore_reservoir(dec, bs_frame, &scratch, main_data_begin); + success = drmp3_L3_restore_reservoir(dec, bs_frame, &scratch, main_data_begin); if (success && pcm != NULL) { - for (igr = 0; igr < (MA_DR_MP3_HDR_TEST_MPEG1(hdr) ? 2 : 1); igr++, pcm = MA_DR_MP3_OFFSET_PTR(pcm, sizeof(ma_dr_mp3d_sample_t)*576*info->channels)) + for (igr = 0; igr < (DRMP3_HDR_TEST_MPEG1(hdr) ? 2 : 1); igr++, pcm = DRMP3_OFFSET_PTR(pcm, sizeof(drmp3d_sample_t)*576*info->channels)) { - MA_DR_MP3_ZERO_MEMORY(scratch.grbuf[0], 576*2*sizeof(float)); - ma_dr_mp3_L3_decode(dec, &scratch, scratch.gr_info + igr*info->channels, info->channels); - ma_dr_mp3d_synth_granule(dec->qmf_state, scratch.grbuf[0], 18, info->channels, (ma_dr_mp3d_sample_t*)pcm, scratch.syn[0]); + DRMP3_ZERO_MEMORY(scratch.grbuf[0], 576*2*sizeof(float)); + drmp3_L3_decode(dec, &scratch, scratch.gr_info + igr*info->channels, info->channels); + drmp3d_synth_granule(dec->qmf_state, scratch.grbuf[0], 18, info->channels, (drmp3d_sample_t*)pcm, scratch.syn[0]); } } - ma_dr_mp3_L3_save_reservoir(dec, &scratch); + drmp3_L3_save_reservoir(dec, &scratch); } else { -#ifdef MA_DR_MP3_ONLY_MP3 +#ifdef DR_MP3_ONLY_MP3 return 0; #else - ma_dr_mp3_L12_scale_info sci[1]; + drmp3_L12_scale_info sci[1]; if (pcm == NULL) { - return ma_dr_mp3_hdr_frame_samples(hdr); + return drmp3_hdr_frame_samples(hdr); } - ma_dr_mp3_L12_read_scale_info(hdr, bs_frame, sci); - MA_DR_MP3_ZERO_MEMORY(scratch.grbuf[0], 576*2*sizeof(float)); + drmp3_L12_read_scale_info(hdr, bs_frame, sci); + DRMP3_ZERO_MEMORY(scratch.grbuf[0], 576*2*sizeof(float)); for (i = 0, igr = 0; igr < 3; igr++) { - if (12 == (i += ma_dr_mp3_L12_dequantize_granule(scratch.grbuf[0] + i, bs_frame, sci, info->layer | 1))) + if (12 == (i += drmp3_L12_dequantize_granule(scratch.grbuf[0] + i, bs_frame, sci, info->layer | 1))) { i = 0; - ma_dr_mp3_L12_apply_scf_384(sci, sci->scf + igr, scratch.grbuf[0]); - ma_dr_mp3d_synth_granule(dec->qmf_state, scratch.grbuf[0], 12, info->channels, (ma_dr_mp3d_sample_t*)pcm, scratch.syn[0]); - MA_DR_MP3_ZERO_MEMORY(scratch.grbuf[0], 576*2*sizeof(float)); - pcm = MA_DR_MP3_OFFSET_PTR(pcm, sizeof(ma_dr_mp3d_sample_t)*384*info->channels); + drmp3_L12_apply_scf_384(sci, sci->scf + igr, scratch.grbuf[0]); + drmp3d_synth_granule(dec->qmf_state, scratch.grbuf[0], 12, info->channels, (drmp3d_sample_t*)pcm, scratch.syn[0]); + DRMP3_ZERO_MEMORY(scratch.grbuf[0], 576*2*sizeof(float)); + pcm = DRMP3_OFFSET_PTR(pcm, sizeof(drmp3d_sample_t)*384*info->channels); } if (bs_frame->pos > bs_frame->limit) { - ma_dr_mp3dec_init(dec); + drmp3dec_init(dec); return 0; } } #endif } - return success*ma_dr_mp3_hdr_frame_samples(dec->header); + return success*drmp3_hdr_frame_samples(dec->header); } -MA_API void ma_dr_mp3dec_f32_to_s16(const float *in, ma_int16 *out, size_t num_samples) +DRMP3_API void drmp3dec_f32_to_s16(const float *in, drmp3_int16 *out, size_t num_samples) { size_t i = 0; -#if MA_DR_MP3_HAVE_SIMD +#if DRMP3_HAVE_SIMD size_t aligned_count = num_samples & ~7; for(; i < aligned_count; i+=8) { - ma_dr_mp3_f4 scale = MA_DR_MP3_VSET(32768.0f); - ma_dr_mp3_f4 a = MA_DR_MP3_VMUL(MA_DR_MP3_VLD(&in[i ]), scale); - ma_dr_mp3_f4 b = MA_DR_MP3_VMUL(MA_DR_MP3_VLD(&in[i+4]), scale); -#if MA_DR_MP3_HAVE_SSE - ma_dr_mp3_f4 s16max = MA_DR_MP3_VSET( 32767.0f); - ma_dr_mp3_f4 s16min = MA_DR_MP3_VSET(-32768.0f); + drmp3_f4 scale = DRMP3_VSET(32768.0f); + drmp3_f4 a = DRMP3_VMUL(DRMP3_VLD(&in[i ]), scale); + drmp3_f4 b = DRMP3_VMUL(DRMP3_VLD(&in[i+4]), scale); +#if DRMP3_HAVE_SSE + drmp3_f4 s16max = DRMP3_VSET( 32767.0f); + drmp3_f4 s16min = DRMP3_VSET(-32768.0f); __m128i pcm8 = _mm_packs_epi32(_mm_cvtps_epi32(_mm_max_ps(_mm_min_ps(a, s16max), s16min)), _mm_cvtps_epi32(_mm_max_ps(_mm_min_ps(b, s16max), s16min))); - out[i ] = (ma_int16)_mm_extract_epi16(pcm8, 0); - out[i+1] = (ma_int16)_mm_extract_epi16(pcm8, 1); - out[i+2] = (ma_int16)_mm_extract_epi16(pcm8, 2); - out[i+3] = (ma_int16)_mm_extract_epi16(pcm8, 3); - out[i+4] = (ma_int16)_mm_extract_epi16(pcm8, 4); - out[i+5] = (ma_int16)_mm_extract_epi16(pcm8, 5); - out[i+6] = (ma_int16)_mm_extract_epi16(pcm8, 6); - out[i+7] = (ma_int16)_mm_extract_epi16(pcm8, 7); + out[i ] = (drmp3_int16)_mm_extract_epi16(pcm8, 0); + out[i+1] = (drmp3_int16)_mm_extract_epi16(pcm8, 1); + out[i+2] = (drmp3_int16)_mm_extract_epi16(pcm8, 2); + out[i+3] = (drmp3_int16)_mm_extract_epi16(pcm8, 3); + out[i+4] = (drmp3_int16)_mm_extract_epi16(pcm8, 4); + out[i+5] = (drmp3_int16)_mm_extract_epi16(pcm8, 5); + out[i+6] = (drmp3_int16)_mm_extract_epi16(pcm8, 6); + out[i+7] = (drmp3_int16)_mm_extract_epi16(pcm8, 7); #else int16x4_t pcma, pcmb; - a = MA_DR_MP3_VADD(a, MA_DR_MP3_VSET(0.5f)); - b = MA_DR_MP3_VADD(b, MA_DR_MP3_VSET(0.5f)); - pcma = vqmovn_s32(vqaddq_s32(vcvtq_s32_f32(a), vreinterpretq_s32_u32(vcltq_f32(a, MA_DR_MP3_VSET(0))))); - pcmb = vqmovn_s32(vqaddq_s32(vcvtq_s32_f32(b), vreinterpretq_s32_u32(vcltq_f32(b, MA_DR_MP3_VSET(0))))); + a = DRMP3_VADD(a, DRMP3_VSET(0.5f)); + b = DRMP3_VADD(b, DRMP3_VSET(0.5f)); + pcma = vqmovn_s32(vqaddq_s32(vcvtq_s32_f32(a), vreinterpretq_s32_u32(vcltq_f32(a, DRMP3_VSET(0))))); + pcmb = vqmovn_s32(vqaddq_s32(vcvtq_s32_f32(b), vreinterpretq_s32_u32(vcltq_f32(b, DRMP3_VSET(0))))); vst1_lane_s16(out+i , pcma, 0); vst1_lane_s16(out+i+1, pcma, 1); vst1_lane_s16(out+i+2, pcma, 2); @@ -92369,70 +91764,79 @@ MA_API void ma_dr_mp3dec_f32_to_s16(const float *in, ma_int16 *out, size_t num_s for(; i < num_samples; i++) { float sample = in[i] * 32768.0f; - if (sample >= 32766.5f) - out[i] = (ma_int16) 32767; - else if (sample <= -32767.5f) - out[i] = (ma_int16)-32768; + if (sample >= 32766.5) + out[i] = (drmp3_int16) 32767; + else if (sample <= -32767.5) + out[i] = (drmp3_int16)-32768; else { - short s = (ma_int16)(sample + .5f); + short s = (drmp3_int16)(sample + .5f); s -= (s < 0); out[i] = s; } } } -#ifndef MA_DR_MP3_SEEK_LEADING_MP3_FRAMES -#define MA_DR_MP3_SEEK_LEADING_MP3_FRAMES 2 +#if defined(SIZE_MAX) + #define DRMP3_SIZE_MAX SIZE_MAX +#else + #if defined(_WIN64) || defined(_LP64) || defined(__LP64__) + #define DRMP3_SIZE_MAX ((drmp3_uint64)0xFFFFFFFFFFFFFFFF) + #else + #define DRMP3_SIZE_MAX 0xFFFFFFFF + #endif #endif -#define MA_DR_MP3_MIN_DATA_CHUNK_SIZE 16384 -#ifndef MA_DR_MP3_DATA_CHUNK_SIZE -#define MA_DR_MP3_DATA_CHUNK_SIZE (MA_DR_MP3_MIN_DATA_CHUNK_SIZE*4) +#ifndef DRMP3_SEEK_LEADING_MP3_FRAMES +#define DRMP3_SEEK_LEADING_MP3_FRAMES 2 #endif -#define MA_DR_MP3_COUNTOF(x) (sizeof(x) / sizeof(x[0])) -#define MA_DR_MP3_CLAMP(x, lo, hi) (MA_DR_MP3_MAX(lo, MA_DR_MP3_MIN(x, hi))) -#ifndef MA_DR_MP3_PI_D -#define MA_DR_MP3_PI_D 3.14159265358979323846264 +#define DRMP3_MIN_DATA_CHUNK_SIZE 16384 +#ifndef DRMP3_DATA_CHUNK_SIZE +#define DRMP3_DATA_CHUNK_SIZE (DRMP3_MIN_DATA_CHUNK_SIZE*4) #endif -#define MA_DR_MP3_DEFAULT_RESAMPLER_LPF_ORDER 2 -static MA_INLINE float ma_dr_mp3_mix_f32(float x, float y, float a) +#define DRMP3_COUNTOF(x) (sizeof(x) / sizeof(x[0])) +#define DRMP3_CLAMP(x, lo, hi) (DRMP3_MAX(lo, DRMP3_MIN(x, hi))) +#ifndef DRMP3_PI_D +#define DRMP3_PI_D 3.14159265358979323846264 +#endif +#define DRMP3_DEFAULT_RESAMPLER_LPF_ORDER 2 +static DRMP3_INLINE float drmp3_mix_f32(float x, float y, float a) { return x*(1-a) + y*a; } -static MA_INLINE float ma_dr_mp3_mix_f32_fast(float x, float y, float a) +static DRMP3_INLINE float drmp3_mix_f32_fast(float x, float y, float a) { float r0 = (y - x); float r1 = r0*a; return x + r1; } -static MA_INLINE ma_uint32 ma_dr_mp3_gcf_u32(ma_uint32 a, ma_uint32 b) +static DRMP3_INLINE drmp3_uint32 drmp3_gcf_u32(drmp3_uint32 a, drmp3_uint32 b) { for (;;) { if (b == 0) { break; } else { - ma_uint32 t = a; + drmp3_uint32 t = a; a = b; b = t % a; } } return a; } -static void* ma_dr_mp3__malloc_default(size_t sz, void* pUserData) +static void* drmp3__malloc_default(size_t sz, void* pUserData) { (void)pUserData; - return MA_DR_MP3_MALLOC(sz); + return DRMP3_MALLOC(sz); } -static void* ma_dr_mp3__realloc_default(void* p, size_t sz, void* pUserData) +static void* drmp3__realloc_default(void* p, size_t sz, void* pUserData) { (void)pUserData; - return MA_DR_MP3_REALLOC(p, sz); + return DRMP3_REALLOC(p, sz); } -static void ma_dr_mp3__free_default(void* p, void* pUserData) +static void drmp3__free_default(void* p, void* pUserData) { (void)pUserData; - MA_DR_MP3_FREE(p); + DRMP3_FREE(p); } -static void* ma_dr_mp3__malloc_from_callbacks(size_t sz, const ma_allocation_callbacks* pAllocationCallbacks) +static void* drmp3__malloc_from_callbacks(size_t sz, const drmp3_allocation_callbacks* pAllocationCallbacks) { if (pAllocationCallbacks == NULL) { return NULL; @@ -92445,7 +91849,7 @@ static void* ma_dr_mp3__malloc_from_callbacks(size_t sz, const ma_allocation_cal } return NULL; } -static void* ma_dr_mp3__realloc_from_callbacks(void* p, size_t szNew, size_t szOld, const ma_allocation_callbacks* pAllocationCallbacks) +static void* drmp3__realloc_from_callbacks(void* p, size_t szNew, size_t szOld, const drmp3_allocation_callbacks* pAllocationCallbacks) { if (pAllocationCallbacks == NULL) { return NULL; @@ -92460,14 +91864,14 @@ static void* ma_dr_mp3__realloc_from_callbacks(void* p, size_t szNew, size_t szO return NULL; } if (p != NULL) { - MA_DR_MP3_COPY_MEMORY(p2, p, szOld); + DRMP3_COPY_MEMORY(p2, p, szOld); pAllocationCallbacks->onFree(p, pAllocationCallbacks->pUserData); } return p2; } return NULL; } -static void ma_dr_mp3__free_from_callbacks(void* p, const ma_allocation_callbacks* pAllocationCallbacks) +static void drmp3__free_from_callbacks(void* p, const drmp3_allocation_callbacks* pAllocationCallbacks) { if (p == NULL || pAllocationCallbacks == NULL) { return; @@ -92476,114 +91880,111 @@ static void ma_dr_mp3__free_from_callbacks(void* p, const ma_allocation_callback pAllocationCallbacks->onFree(p, pAllocationCallbacks->pUserData); } } -static ma_allocation_callbacks ma_dr_mp3_copy_allocation_callbacks_or_defaults(const ma_allocation_callbacks* pAllocationCallbacks) +static drmp3_allocation_callbacks drmp3_copy_allocation_callbacks_or_defaults(const drmp3_allocation_callbacks* pAllocationCallbacks) { if (pAllocationCallbacks != NULL) { return *pAllocationCallbacks; } else { - ma_allocation_callbacks allocationCallbacks; + drmp3_allocation_callbacks allocationCallbacks; allocationCallbacks.pUserData = NULL; - allocationCallbacks.onMalloc = ma_dr_mp3__malloc_default; - allocationCallbacks.onRealloc = ma_dr_mp3__realloc_default; - allocationCallbacks.onFree = ma_dr_mp3__free_default; + allocationCallbacks.onMalloc = drmp3__malloc_default; + allocationCallbacks.onRealloc = drmp3__realloc_default; + allocationCallbacks.onFree = drmp3__free_default; return allocationCallbacks; } } -static size_t ma_dr_mp3__on_read(ma_dr_mp3* pMP3, void* pBufferOut, size_t bytesToRead) +static size_t drmp3__on_read(drmp3* pMP3, void* pBufferOut, size_t bytesToRead) { size_t bytesRead = pMP3->onRead(pMP3->pUserData, pBufferOut, bytesToRead); pMP3->streamCursor += bytesRead; return bytesRead; } -static ma_bool32 ma_dr_mp3__on_seek(ma_dr_mp3* pMP3, int offset, ma_dr_mp3_seek_origin origin) +static drmp3_bool32 drmp3__on_seek(drmp3* pMP3, int offset, drmp3_seek_origin origin) { - MA_DR_MP3_ASSERT(offset >= 0); + DRMP3_ASSERT(offset >= 0); if (!pMP3->onSeek(pMP3->pUserData, offset, origin)) { - return MA_FALSE; + return DRMP3_FALSE; } - if (origin == ma_dr_mp3_seek_origin_start) { - pMP3->streamCursor = (ma_uint64)offset; + if (origin == drmp3_seek_origin_start) { + pMP3->streamCursor = (drmp3_uint64)offset; } else { pMP3->streamCursor += offset; } - return MA_TRUE; + return DRMP3_TRUE; } -static ma_bool32 ma_dr_mp3__on_seek_64(ma_dr_mp3* pMP3, ma_uint64 offset, ma_dr_mp3_seek_origin origin) +static drmp3_bool32 drmp3__on_seek_64(drmp3* pMP3, drmp3_uint64 offset, drmp3_seek_origin origin) { if (offset <= 0x7FFFFFFF) { - return ma_dr_mp3__on_seek(pMP3, (int)offset, origin); + return drmp3__on_seek(pMP3, (int)offset, origin); } - if (!ma_dr_mp3__on_seek(pMP3, 0x7FFFFFFF, ma_dr_mp3_seek_origin_start)) { - return MA_FALSE; + if (!drmp3__on_seek(pMP3, 0x7FFFFFFF, drmp3_seek_origin_start)) { + return DRMP3_FALSE; } offset -= 0x7FFFFFFF; while (offset > 0) { if (offset <= 0x7FFFFFFF) { - if (!ma_dr_mp3__on_seek(pMP3, (int)offset, ma_dr_mp3_seek_origin_current)) { - return MA_FALSE; + if (!drmp3__on_seek(pMP3, (int)offset, drmp3_seek_origin_current)) { + return DRMP3_FALSE; } offset = 0; } else { - if (!ma_dr_mp3__on_seek(pMP3, 0x7FFFFFFF, ma_dr_mp3_seek_origin_current)) { - return MA_FALSE; + if (!drmp3__on_seek(pMP3, 0x7FFFFFFF, drmp3_seek_origin_current)) { + return DRMP3_FALSE; } offset -= 0x7FFFFFFF; } } - return MA_TRUE; + return DRMP3_TRUE; } -static ma_uint32 ma_dr_mp3_decode_next_frame_ex__callbacks(ma_dr_mp3* pMP3, ma_dr_mp3d_sample_t* pPCMFrames) +static drmp3_uint32 drmp3_decode_next_frame_ex__callbacks(drmp3* pMP3, drmp3d_sample_t* pPCMFrames) { - ma_uint32 pcmFramesRead = 0; - MA_DR_MP3_ASSERT(pMP3 != NULL); - MA_DR_MP3_ASSERT(pMP3->onRead != NULL); + drmp3_uint32 pcmFramesRead = 0; + DRMP3_ASSERT(pMP3 != NULL); + DRMP3_ASSERT(pMP3->onRead != NULL); if (pMP3->atEnd) { return 0; } for (;;) { - ma_dr_mp3dec_frame_info info; - if (pMP3->dataSize < MA_DR_MP3_MIN_DATA_CHUNK_SIZE) { + drmp3dec_frame_info info; + if (pMP3->dataSize < DRMP3_MIN_DATA_CHUNK_SIZE) { size_t bytesRead; if (pMP3->pData != NULL) { - MA_DR_MP3_MOVE_MEMORY(pMP3->pData, pMP3->pData + pMP3->dataConsumed, pMP3->dataSize); + DRMP3_MOVE_MEMORY(pMP3->pData, pMP3->pData + pMP3->dataConsumed, pMP3->dataSize); } pMP3->dataConsumed = 0; - if (pMP3->dataCapacity < MA_DR_MP3_DATA_CHUNK_SIZE) { - ma_uint8* pNewData; + if (pMP3->dataCapacity < DRMP3_DATA_CHUNK_SIZE) { + drmp3_uint8* pNewData; size_t newDataCap; - newDataCap = MA_DR_MP3_DATA_CHUNK_SIZE; - pNewData = (ma_uint8*)ma_dr_mp3__realloc_from_callbacks(pMP3->pData, newDataCap, pMP3->dataCapacity, &pMP3->allocationCallbacks); + newDataCap = DRMP3_DATA_CHUNK_SIZE; + pNewData = (drmp3_uint8*)drmp3__realloc_from_callbacks(pMP3->pData, newDataCap, pMP3->dataCapacity, &pMP3->allocationCallbacks); if (pNewData == NULL) { return 0; } pMP3->pData = pNewData; pMP3->dataCapacity = newDataCap; } - bytesRead = ma_dr_mp3__on_read(pMP3, pMP3->pData + pMP3->dataSize, (pMP3->dataCapacity - pMP3->dataSize)); + bytesRead = drmp3__on_read(pMP3, pMP3->pData + pMP3->dataSize, (pMP3->dataCapacity - pMP3->dataSize)); if (bytesRead == 0) { if (pMP3->dataSize == 0) { - pMP3->atEnd = MA_TRUE; + pMP3->atEnd = DRMP3_TRUE; return 0; } } pMP3->dataSize += bytesRead; } if (pMP3->dataSize > INT_MAX) { - pMP3->atEnd = MA_TRUE; - return 0; - } - MA_DR_MP3_ASSERT(pMP3->pData != NULL); - MA_DR_MP3_ASSERT(pMP3->dataCapacity > 0); - if (pMP3->pData == NULL) { + pMP3->atEnd = DRMP3_TRUE; return 0; } - pcmFramesRead = ma_dr_mp3dec_decode_frame(&pMP3->decoder, pMP3->pData + pMP3->dataConsumed, (int)pMP3->dataSize, pPCMFrames, &info); + DRMP3_ASSERT(pMP3->pData != NULL); + DRMP3_ASSERT(pMP3->dataCapacity > 0); + pcmFramesRead = drmp3dec_decode_frame(&pMP3->decoder, pMP3->pData + pMP3->dataConsumed, (int)pMP3->dataSize, pPCMFrames, &info); if (info.frame_bytes > 0) { pMP3->dataConsumed += (size_t)info.frame_bytes; pMP3->dataSize -= (size_t)info.frame_bytes; } if (pcmFramesRead > 0) { - pcmFramesRead = ma_dr_mp3_hdr_frame_samples(pMP3->decoder.header); + pcmFramesRead = drmp3_hdr_frame_samples(pMP3->decoder.header); pMP3->pcmFramesConsumedInMP3Frame = 0; pMP3->pcmFramesRemainingInMP3Frame = pcmFramesRead; pMP3->mp3FrameChannels = info.channels; @@ -92591,22 +91992,22 @@ static ma_uint32 ma_dr_mp3_decode_next_frame_ex__callbacks(ma_dr_mp3* pMP3, ma_d break; } else if (info.frame_bytes == 0) { size_t bytesRead; - MA_DR_MP3_MOVE_MEMORY(pMP3->pData, pMP3->pData + pMP3->dataConsumed, pMP3->dataSize); + DRMP3_MOVE_MEMORY(pMP3->pData, pMP3->pData + pMP3->dataConsumed, pMP3->dataSize); pMP3->dataConsumed = 0; if (pMP3->dataCapacity == pMP3->dataSize) { - ma_uint8* pNewData; + drmp3_uint8* pNewData; size_t newDataCap; - newDataCap = pMP3->dataCapacity + MA_DR_MP3_DATA_CHUNK_SIZE; - pNewData = (ma_uint8*)ma_dr_mp3__realloc_from_callbacks(pMP3->pData, newDataCap, pMP3->dataCapacity, &pMP3->allocationCallbacks); + newDataCap = pMP3->dataCapacity + DRMP3_DATA_CHUNK_SIZE; + pNewData = (drmp3_uint8*)drmp3__realloc_from_callbacks(pMP3->pData, newDataCap, pMP3->dataCapacity, &pMP3->allocationCallbacks); if (pNewData == NULL) { return 0; } pMP3->pData = pNewData; pMP3->dataCapacity = newDataCap; } - bytesRead = ma_dr_mp3__on_read(pMP3, pMP3->pData + pMP3->dataSize, (pMP3->dataCapacity - pMP3->dataSize)); + bytesRead = drmp3__on_read(pMP3, pMP3->pData + pMP3->dataSize, (pMP3->dataCapacity - pMP3->dataSize)); if (bytesRead == 0) { - pMP3->atEnd = MA_TRUE; + pMP3->atEnd = DRMP3_TRUE; return 0; } pMP3->dataSize += bytesRead; @@ -92614,19 +92015,19 @@ static ma_uint32 ma_dr_mp3_decode_next_frame_ex__callbacks(ma_dr_mp3* pMP3, ma_d }; return pcmFramesRead; } -static ma_uint32 ma_dr_mp3_decode_next_frame_ex__memory(ma_dr_mp3* pMP3, ma_dr_mp3d_sample_t* pPCMFrames) +static drmp3_uint32 drmp3_decode_next_frame_ex__memory(drmp3* pMP3, drmp3d_sample_t* pPCMFrames) { - ma_uint32 pcmFramesRead = 0; - ma_dr_mp3dec_frame_info info; - MA_DR_MP3_ASSERT(pMP3 != NULL); - MA_DR_MP3_ASSERT(pMP3->memory.pData != NULL); + drmp3_uint32 pcmFramesRead = 0; + drmp3dec_frame_info info; + DRMP3_ASSERT(pMP3 != NULL); + DRMP3_ASSERT(pMP3->memory.pData != NULL); if (pMP3->atEnd) { return 0; } for (;;) { - pcmFramesRead = ma_dr_mp3dec_decode_frame(&pMP3->decoder, pMP3->memory.pData + pMP3->memory.currentReadPos, (int)(pMP3->memory.dataSize - pMP3->memory.currentReadPos), pPCMFrames, &info); + pcmFramesRead = drmp3dec_decode_frame(&pMP3->decoder, pMP3->memory.pData + pMP3->memory.currentReadPos, (int)(pMP3->memory.dataSize - pMP3->memory.currentReadPos), pPCMFrames, &info); if (pcmFramesRead > 0) { - pcmFramesRead = ma_dr_mp3_hdr_frame_samples(pMP3->decoder.header); + pcmFramesRead = drmp3_hdr_frame_samples(pMP3->decoder.header); pMP3->pcmFramesConsumedInMP3Frame = 0; pMP3->pcmFramesRemainingInMP3Frame = pcmFramesRead; pMP3->mp3FrameChannels = info.channels; @@ -92641,25 +92042,25 @@ static ma_uint32 ma_dr_mp3_decode_next_frame_ex__memory(ma_dr_mp3* pMP3, ma_dr_m pMP3->memory.currentReadPos += (size_t)info.frame_bytes; return pcmFramesRead; } -static ma_uint32 ma_dr_mp3_decode_next_frame_ex(ma_dr_mp3* pMP3, ma_dr_mp3d_sample_t* pPCMFrames) +static drmp3_uint32 drmp3_decode_next_frame_ex(drmp3* pMP3, drmp3d_sample_t* pPCMFrames) { if (pMP3->memory.pData != NULL && pMP3->memory.dataSize > 0) { - return ma_dr_mp3_decode_next_frame_ex__memory(pMP3, pPCMFrames); + return drmp3_decode_next_frame_ex__memory(pMP3, pPCMFrames); } else { - return ma_dr_mp3_decode_next_frame_ex__callbacks(pMP3, pPCMFrames); + return drmp3_decode_next_frame_ex__callbacks(pMP3, pPCMFrames); } } -static ma_uint32 ma_dr_mp3_decode_next_frame(ma_dr_mp3* pMP3) +static drmp3_uint32 drmp3_decode_next_frame(drmp3* pMP3) { - MA_DR_MP3_ASSERT(pMP3 != NULL); - return ma_dr_mp3_decode_next_frame_ex(pMP3, (ma_dr_mp3d_sample_t*)pMP3->pcmFrames); + DRMP3_ASSERT(pMP3 != NULL); + return drmp3_decode_next_frame_ex(pMP3, (drmp3d_sample_t*)pMP3->pcmFrames); } #if 0 -static ma_uint32 ma_dr_mp3_seek_next_frame(ma_dr_mp3* pMP3) +static drmp3_uint32 drmp3_seek_next_frame(drmp3* pMP3) { - ma_uint32 pcmFrameCount; - MA_DR_MP3_ASSERT(pMP3 != NULL); - pcmFrameCount = ma_dr_mp3_decode_next_frame_ex(pMP3, NULL); + drmp3_uint32 pcmFrameCount; + DRMP3_ASSERT(pMP3 != NULL); + pcmFrameCount = drmp3_decode_next_frame_ex(pMP3, NULL); if (pcmFrameCount == 0) { return 0; } @@ -92669,55 +92070,55 @@ static ma_uint32 ma_dr_mp3_seek_next_frame(ma_dr_mp3* pMP3) return pcmFrameCount; } #endif -static ma_bool32 ma_dr_mp3_init_internal(ma_dr_mp3* pMP3, ma_dr_mp3_read_proc onRead, ma_dr_mp3_seek_proc onSeek, void* pUserData, const ma_allocation_callbacks* pAllocationCallbacks) +static drmp3_bool32 drmp3_init_internal(drmp3* pMP3, drmp3_read_proc onRead, drmp3_seek_proc onSeek, void* pUserData, const drmp3_allocation_callbacks* pAllocationCallbacks) { - MA_DR_MP3_ASSERT(pMP3 != NULL); - MA_DR_MP3_ASSERT(onRead != NULL); - ma_dr_mp3dec_init(&pMP3->decoder); + DRMP3_ASSERT(pMP3 != NULL); + DRMP3_ASSERT(onRead != NULL); + drmp3dec_init(&pMP3->decoder); pMP3->onRead = onRead; pMP3->onSeek = onSeek; pMP3->pUserData = pUserData; - pMP3->allocationCallbacks = ma_dr_mp3_copy_allocation_callbacks_or_defaults(pAllocationCallbacks); + pMP3->allocationCallbacks = drmp3_copy_allocation_callbacks_or_defaults(pAllocationCallbacks); if (pMP3->allocationCallbacks.onFree == NULL || (pMP3->allocationCallbacks.onMalloc == NULL && pMP3->allocationCallbacks.onRealloc == NULL)) { - return MA_FALSE; + return DRMP3_FALSE; } - if (ma_dr_mp3_decode_next_frame(pMP3) == 0) { - ma_dr_mp3__free_from_callbacks(pMP3->pData, &pMP3->allocationCallbacks); - return MA_FALSE; + if (drmp3_decode_next_frame(pMP3) == 0) { + drmp3__free_from_callbacks(pMP3->pData, &pMP3->allocationCallbacks); + return DRMP3_FALSE; } pMP3->channels = pMP3->mp3FrameChannels; pMP3->sampleRate = pMP3->mp3FrameSampleRate; - return MA_TRUE; + return DRMP3_TRUE; } -MA_API ma_bool32 ma_dr_mp3_init(ma_dr_mp3* pMP3, ma_dr_mp3_read_proc onRead, ma_dr_mp3_seek_proc onSeek, void* pUserData, const ma_allocation_callbacks* pAllocationCallbacks) +DRMP3_API drmp3_bool32 drmp3_init(drmp3* pMP3, drmp3_read_proc onRead, drmp3_seek_proc onSeek, void* pUserData, const drmp3_allocation_callbacks* pAllocationCallbacks) { if (pMP3 == NULL || onRead == NULL) { - return MA_FALSE; + return DRMP3_FALSE; } - MA_DR_MP3_ZERO_OBJECT(pMP3); - return ma_dr_mp3_init_internal(pMP3, onRead, onSeek, pUserData, pAllocationCallbacks); + DRMP3_ZERO_OBJECT(pMP3); + return drmp3_init_internal(pMP3, onRead, onSeek, pUserData, pAllocationCallbacks); } -static size_t ma_dr_mp3__on_read_memory(void* pUserData, void* pBufferOut, size_t bytesToRead) +static size_t drmp3__on_read_memory(void* pUserData, void* pBufferOut, size_t bytesToRead) { - ma_dr_mp3* pMP3 = (ma_dr_mp3*)pUserData; + drmp3* pMP3 = (drmp3*)pUserData; size_t bytesRemaining; - MA_DR_MP3_ASSERT(pMP3 != NULL); - MA_DR_MP3_ASSERT(pMP3->memory.dataSize >= pMP3->memory.currentReadPos); + DRMP3_ASSERT(pMP3 != NULL); + DRMP3_ASSERT(pMP3->memory.dataSize >= pMP3->memory.currentReadPos); bytesRemaining = pMP3->memory.dataSize - pMP3->memory.currentReadPos; if (bytesToRead > bytesRemaining) { bytesToRead = bytesRemaining; } if (bytesToRead > 0) { - MA_DR_MP3_COPY_MEMORY(pBufferOut, pMP3->memory.pData + pMP3->memory.currentReadPos, bytesToRead); + DRMP3_COPY_MEMORY(pBufferOut, pMP3->memory.pData + pMP3->memory.currentReadPos, bytesToRead); pMP3->memory.currentReadPos += bytesToRead; } return bytesToRead; } -static ma_bool32 ma_dr_mp3__on_seek_memory(void* pUserData, int byteOffset, ma_dr_mp3_seek_origin origin) +static drmp3_bool32 drmp3__on_seek_memory(void* pUserData, int byteOffset, drmp3_seek_origin origin) { - ma_dr_mp3* pMP3 = (ma_dr_mp3*)pUserData; - MA_DR_MP3_ASSERT(pMP3 != NULL); - if (origin == ma_dr_mp3_seek_origin_current) { + drmp3* pMP3 = (drmp3*)pUserData; + DRMP3_ASSERT(pMP3 != NULL); + if (origin == drmp3_seek_origin_current) { if (byteOffset > 0) { if (pMP3->memory.currentReadPos + byteOffset > pMP3->memory.dataSize) { byteOffset = (int)(pMP3->memory.dataSize - pMP3->memory.currentReadPos); @@ -92729,75 +92130,585 @@ static ma_bool32 ma_dr_mp3__on_seek_memory(void* pUserData, int byteOffset, ma_d } pMP3->memory.currentReadPos += byteOffset; } else { - if ((ma_uint32)byteOffset <= pMP3->memory.dataSize) { + if ((drmp3_uint32)byteOffset <= pMP3->memory.dataSize) { pMP3->memory.currentReadPos = byteOffset; } else { pMP3->memory.currentReadPos = pMP3->memory.dataSize; } } - return MA_TRUE; + return DRMP3_TRUE; } -MA_API ma_bool32 ma_dr_mp3_init_memory(ma_dr_mp3* pMP3, const void* pData, size_t dataSize, const ma_allocation_callbacks* pAllocationCallbacks) +DRMP3_API drmp3_bool32 drmp3_init_memory(drmp3* pMP3, const void* pData, size_t dataSize, const drmp3_allocation_callbacks* pAllocationCallbacks) { if (pMP3 == NULL) { - return MA_FALSE; + return DRMP3_FALSE; } - MA_DR_MP3_ZERO_OBJECT(pMP3); + DRMP3_ZERO_OBJECT(pMP3); if (pData == NULL || dataSize == 0) { - return MA_FALSE; + return DRMP3_FALSE; } - pMP3->memory.pData = (const ma_uint8*)pData; + pMP3->memory.pData = (const drmp3_uint8*)pData; pMP3->memory.dataSize = dataSize; pMP3->memory.currentReadPos = 0; - return ma_dr_mp3_init_internal(pMP3, ma_dr_mp3__on_read_memory, ma_dr_mp3__on_seek_memory, pMP3, pAllocationCallbacks); + return drmp3_init_internal(pMP3, drmp3__on_read_memory, drmp3__on_seek_memory, pMP3, pAllocationCallbacks); } -#ifndef MA_DR_MP3_NO_STDIO +#ifndef DR_MP3_NO_STDIO #include #include -static size_t ma_dr_mp3__on_read_stdio(void* pUserData, void* pBufferOut, size_t bytesToRead) +#include +static drmp3_result drmp3_result_from_errno(int e) +{ + switch (e) + { + case 0: return DRMP3_SUCCESS; + #ifdef EPERM + case EPERM: return DRMP3_INVALID_OPERATION; + #endif + #ifdef ENOENT + case ENOENT: return DRMP3_DOES_NOT_EXIST; + #endif + #ifdef ESRCH + case ESRCH: return DRMP3_DOES_NOT_EXIST; + #endif + #ifdef EINTR + case EINTR: return DRMP3_INTERRUPT; + #endif + #ifdef EIO + case EIO: return DRMP3_IO_ERROR; + #endif + #ifdef ENXIO + case ENXIO: return DRMP3_DOES_NOT_EXIST; + #endif + #ifdef E2BIG + case E2BIG: return DRMP3_INVALID_ARGS; + #endif + #ifdef ENOEXEC + case ENOEXEC: return DRMP3_INVALID_FILE; + #endif + #ifdef EBADF + case EBADF: return DRMP3_INVALID_FILE; + #endif + #ifdef ECHILD + case ECHILD: return DRMP3_ERROR; + #endif + #ifdef EAGAIN + case EAGAIN: return DRMP3_UNAVAILABLE; + #endif + #ifdef ENOMEM + case ENOMEM: return DRMP3_OUT_OF_MEMORY; + #endif + #ifdef EACCES + case EACCES: return DRMP3_ACCESS_DENIED; + #endif + #ifdef EFAULT + case EFAULT: return DRMP3_BAD_ADDRESS; + #endif + #ifdef ENOTBLK + case ENOTBLK: return DRMP3_ERROR; + #endif + #ifdef EBUSY + case EBUSY: return DRMP3_BUSY; + #endif + #ifdef EEXIST + case EEXIST: return DRMP3_ALREADY_EXISTS; + #endif + #ifdef EXDEV + case EXDEV: return DRMP3_ERROR; + #endif + #ifdef ENODEV + case ENODEV: return DRMP3_DOES_NOT_EXIST; + #endif + #ifdef ENOTDIR + case ENOTDIR: return DRMP3_NOT_DIRECTORY; + #endif + #ifdef EISDIR + case EISDIR: return DRMP3_IS_DIRECTORY; + #endif + #ifdef EINVAL + case EINVAL: return DRMP3_INVALID_ARGS; + #endif + #ifdef ENFILE + case ENFILE: return DRMP3_TOO_MANY_OPEN_FILES; + #endif + #ifdef EMFILE + case EMFILE: return DRMP3_TOO_MANY_OPEN_FILES; + #endif + #ifdef ENOTTY + case ENOTTY: return DRMP3_INVALID_OPERATION; + #endif + #ifdef ETXTBSY + case ETXTBSY: return DRMP3_BUSY; + #endif + #ifdef EFBIG + case EFBIG: return DRMP3_TOO_BIG; + #endif + #ifdef ENOSPC + case ENOSPC: return DRMP3_NO_SPACE; + #endif + #ifdef ESPIPE + case ESPIPE: return DRMP3_BAD_SEEK; + #endif + #ifdef EROFS + case EROFS: return DRMP3_ACCESS_DENIED; + #endif + #ifdef EMLINK + case EMLINK: return DRMP3_TOO_MANY_LINKS; + #endif + #ifdef EPIPE + case EPIPE: return DRMP3_BAD_PIPE; + #endif + #ifdef EDOM + case EDOM: return DRMP3_OUT_OF_RANGE; + #endif + #ifdef ERANGE + case ERANGE: return DRMP3_OUT_OF_RANGE; + #endif + #ifdef EDEADLK + case EDEADLK: return DRMP3_DEADLOCK; + #endif + #ifdef ENAMETOOLONG + case ENAMETOOLONG: return DRMP3_PATH_TOO_LONG; + #endif + #ifdef ENOLCK + case ENOLCK: return DRMP3_ERROR; + #endif + #ifdef ENOSYS + case ENOSYS: return DRMP3_NOT_IMPLEMENTED; + #endif + #ifdef ENOTEMPTY + case ENOTEMPTY: return DRMP3_DIRECTORY_NOT_EMPTY; + #endif + #ifdef ELOOP + case ELOOP: return DRMP3_TOO_MANY_LINKS; + #endif + #ifdef ENOMSG + case ENOMSG: return DRMP3_NO_MESSAGE; + #endif + #ifdef EIDRM + case EIDRM: return DRMP3_ERROR; + #endif + #ifdef ECHRNG + case ECHRNG: return DRMP3_ERROR; + #endif + #ifdef EL2NSYNC + case EL2NSYNC: return DRMP3_ERROR; + #endif + #ifdef EL3HLT + case EL3HLT: return DRMP3_ERROR; + #endif + #ifdef EL3RST + case EL3RST: return DRMP3_ERROR; + #endif + #ifdef ELNRNG + case ELNRNG: return DRMP3_OUT_OF_RANGE; + #endif + #ifdef EUNATCH + case EUNATCH: return DRMP3_ERROR; + #endif + #ifdef ENOCSI + case ENOCSI: return DRMP3_ERROR; + #endif + #ifdef EL2HLT + case EL2HLT: return DRMP3_ERROR; + #endif + #ifdef EBADE + case EBADE: return DRMP3_ERROR; + #endif + #ifdef EBADR + case EBADR: return DRMP3_ERROR; + #endif + #ifdef EXFULL + case EXFULL: return DRMP3_ERROR; + #endif + #ifdef ENOANO + case ENOANO: return DRMP3_ERROR; + #endif + #ifdef EBADRQC + case EBADRQC: return DRMP3_ERROR; + #endif + #ifdef EBADSLT + case EBADSLT: return DRMP3_ERROR; + #endif + #ifdef EBFONT + case EBFONT: return DRMP3_INVALID_FILE; + #endif + #ifdef ENOSTR + case ENOSTR: return DRMP3_ERROR; + #endif + #ifdef ENODATA + case ENODATA: return DRMP3_NO_DATA_AVAILABLE; + #endif + #ifdef ETIME + case ETIME: return DRMP3_TIMEOUT; + #endif + #ifdef ENOSR + case ENOSR: return DRMP3_NO_DATA_AVAILABLE; + #endif + #ifdef ENONET + case ENONET: return DRMP3_NO_NETWORK; + #endif + #ifdef ENOPKG + case ENOPKG: return DRMP3_ERROR; + #endif + #ifdef EREMOTE + case EREMOTE: return DRMP3_ERROR; + #endif + #ifdef ENOLINK + case ENOLINK: return DRMP3_ERROR; + #endif + #ifdef EADV + case EADV: return DRMP3_ERROR; + #endif + #ifdef ESRMNT + case ESRMNT: return DRMP3_ERROR; + #endif + #ifdef ECOMM + case ECOMM: return DRMP3_ERROR; + #endif + #ifdef EPROTO + case EPROTO: return DRMP3_ERROR; + #endif + #ifdef EMULTIHOP + case EMULTIHOP: return DRMP3_ERROR; + #endif + #ifdef EDOTDOT + case EDOTDOT: return DRMP3_ERROR; + #endif + #ifdef EBADMSG + case EBADMSG: return DRMP3_BAD_MESSAGE; + #endif + #ifdef EOVERFLOW + case EOVERFLOW: return DRMP3_TOO_BIG; + #endif + #ifdef ENOTUNIQ + case ENOTUNIQ: return DRMP3_NOT_UNIQUE; + #endif + #ifdef EBADFD + case EBADFD: return DRMP3_ERROR; + #endif + #ifdef EREMCHG + case EREMCHG: return DRMP3_ERROR; + #endif + #ifdef ELIBACC + case ELIBACC: return DRMP3_ACCESS_DENIED; + #endif + #ifdef ELIBBAD + case ELIBBAD: return DRMP3_INVALID_FILE; + #endif + #ifdef ELIBSCN + case ELIBSCN: return DRMP3_INVALID_FILE; + #endif + #ifdef ELIBMAX + case ELIBMAX: return DRMP3_ERROR; + #endif + #ifdef ELIBEXEC + case ELIBEXEC: return DRMP3_ERROR; + #endif + #ifdef EILSEQ + case EILSEQ: return DRMP3_INVALID_DATA; + #endif + #ifdef ERESTART + case ERESTART: return DRMP3_ERROR; + #endif + #ifdef ESTRPIPE + case ESTRPIPE: return DRMP3_ERROR; + #endif + #ifdef EUSERS + case EUSERS: return DRMP3_ERROR; + #endif + #ifdef ENOTSOCK + case ENOTSOCK: return DRMP3_NOT_SOCKET; + #endif + #ifdef EDESTADDRREQ + case EDESTADDRREQ: return DRMP3_NO_ADDRESS; + #endif + #ifdef EMSGSIZE + case EMSGSIZE: return DRMP3_TOO_BIG; + #endif + #ifdef EPROTOTYPE + case EPROTOTYPE: return DRMP3_BAD_PROTOCOL; + #endif + #ifdef ENOPROTOOPT + case ENOPROTOOPT: return DRMP3_PROTOCOL_UNAVAILABLE; + #endif + #ifdef EPROTONOSUPPORT + case EPROTONOSUPPORT: return DRMP3_PROTOCOL_NOT_SUPPORTED; + #endif + #ifdef ESOCKTNOSUPPORT + case ESOCKTNOSUPPORT: return DRMP3_SOCKET_NOT_SUPPORTED; + #endif + #ifdef EOPNOTSUPP + case EOPNOTSUPP: return DRMP3_INVALID_OPERATION; + #endif + #ifdef EPFNOSUPPORT + case EPFNOSUPPORT: return DRMP3_PROTOCOL_FAMILY_NOT_SUPPORTED; + #endif + #ifdef EAFNOSUPPORT + case EAFNOSUPPORT: return DRMP3_ADDRESS_FAMILY_NOT_SUPPORTED; + #endif + #ifdef EADDRINUSE + case EADDRINUSE: return DRMP3_ALREADY_IN_USE; + #endif + #ifdef EADDRNOTAVAIL + case EADDRNOTAVAIL: return DRMP3_ERROR; + #endif + #ifdef ENETDOWN + case ENETDOWN: return DRMP3_NO_NETWORK; + #endif + #ifdef ENETUNREACH + case ENETUNREACH: return DRMP3_NO_NETWORK; + #endif + #ifdef ENETRESET + case ENETRESET: return DRMP3_NO_NETWORK; + #endif + #ifdef ECONNABORTED + case ECONNABORTED: return DRMP3_NO_NETWORK; + #endif + #ifdef ECONNRESET + case ECONNRESET: return DRMP3_CONNECTION_RESET; + #endif + #ifdef ENOBUFS + case ENOBUFS: return DRMP3_NO_SPACE; + #endif + #ifdef EISCONN + case EISCONN: return DRMP3_ALREADY_CONNECTED; + #endif + #ifdef ENOTCONN + case ENOTCONN: return DRMP3_NOT_CONNECTED; + #endif + #ifdef ESHUTDOWN + case ESHUTDOWN: return DRMP3_ERROR; + #endif + #ifdef ETOOMANYREFS + case ETOOMANYREFS: return DRMP3_ERROR; + #endif + #ifdef ETIMEDOUT + case ETIMEDOUT: return DRMP3_TIMEOUT; + #endif + #ifdef ECONNREFUSED + case ECONNREFUSED: return DRMP3_CONNECTION_REFUSED; + #endif + #ifdef EHOSTDOWN + case EHOSTDOWN: return DRMP3_NO_HOST; + #endif + #ifdef EHOSTUNREACH + case EHOSTUNREACH: return DRMP3_NO_HOST; + #endif + #ifdef EALREADY + case EALREADY: return DRMP3_IN_PROGRESS; + #endif + #ifdef EINPROGRESS + case EINPROGRESS: return DRMP3_IN_PROGRESS; + #endif + #ifdef ESTALE + case ESTALE: return DRMP3_INVALID_FILE; + #endif + #ifdef EUCLEAN + case EUCLEAN: return DRMP3_ERROR; + #endif + #ifdef ENOTNAM + case ENOTNAM: return DRMP3_ERROR; + #endif + #ifdef ENAVAIL + case ENAVAIL: return DRMP3_ERROR; + #endif + #ifdef EISNAM + case EISNAM: return DRMP3_ERROR; + #endif + #ifdef EREMOTEIO + case EREMOTEIO: return DRMP3_IO_ERROR; + #endif + #ifdef EDQUOT + case EDQUOT: return DRMP3_NO_SPACE; + #endif + #ifdef ENOMEDIUM + case ENOMEDIUM: return DRMP3_DOES_NOT_EXIST; + #endif + #ifdef EMEDIUMTYPE + case EMEDIUMTYPE: return DRMP3_ERROR; + #endif + #ifdef ECANCELED + case ECANCELED: return DRMP3_CANCELLED; + #endif + #ifdef ENOKEY + case ENOKEY: return DRMP3_ERROR; + #endif + #ifdef EKEYEXPIRED + case EKEYEXPIRED: return DRMP3_ERROR; + #endif + #ifdef EKEYREVOKED + case EKEYREVOKED: return DRMP3_ERROR; + #endif + #ifdef EKEYREJECTED + case EKEYREJECTED: return DRMP3_ERROR; + #endif + #ifdef EOWNERDEAD + case EOWNERDEAD: return DRMP3_ERROR; + #endif + #ifdef ENOTRECOVERABLE + case ENOTRECOVERABLE: return DRMP3_ERROR; + #endif + #ifdef ERFKILL + case ERFKILL: return DRMP3_ERROR; + #endif + #ifdef EHWPOISON + case EHWPOISON: return DRMP3_ERROR; + #endif + default: return DRMP3_ERROR; + } +} +static drmp3_result drmp3_fopen(FILE** ppFile, const char* pFilePath, const char* pOpenMode) +{ +#if defined(_MSC_VER) && _MSC_VER >= 1400 + errno_t err; +#endif + if (ppFile != NULL) { + *ppFile = NULL; + } + if (pFilePath == NULL || pOpenMode == NULL || ppFile == NULL) { + return DRMP3_INVALID_ARGS; + } +#if defined(_MSC_VER) && _MSC_VER >= 1400 + err = fopen_s(ppFile, pFilePath, pOpenMode); + if (err != 0) { + return drmp3_result_from_errno(err); + } +#else +#if defined(_WIN32) || defined(__APPLE__) + *ppFile = fopen(pFilePath, pOpenMode); +#else + #if defined(_FILE_OFFSET_BITS) && _FILE_OFFSET_BITS == 64 && defined(_LARGEFILE64_SOURCE) + *ppFile = fopen64(pFilePath, pOpenMode); + #else + *ppFile = fopen(pFilePath, pOpenMode); + #endif +#endif + if (*ppFile == NULL) { + drmp3_result result = drmp3_result_from_errno(errno); + if (result == DRMP3_SUCCESS) { + result = DRMP3_ERROR; + } + return result; + } +#endif + return DRMP3_SUCCESS; +} +#if defined(_WIN32) + #if defined(_MSC_VER) || defined(__MINGW64__) || (!defined(__STRICT_ANSI__) && !defined(_NO_EXT_KEYS)) + #define DRMP3_HAS_WFOPEN + #endif +#endif +static drmp3_result drmp3_wfopen(FILE** ppFile, const wchar_t* pFilePath, const wchar_t* pOpenMode, const drmp3_allocation_callbacks* pAllocationCallbacks) +{ + if (ppFile != NULL) { + *ppFile = NULL; + } + if (pFilePath == NULL || pOpenMode == NULL || ppFile == NULL) { + return DRMP3_INVALID_ARGS; + } +#if defined(DRMP3_HAS_WFOPEN) + { + #if defined(_MSC_VER) && _MSC_VER >= 1400 + errno_t err = _wfopen_s(ppFile, pFilePath, pOpenMode); + if (err != 0) { + return drmp3_result_from_errno(err); + } + #else + *ppFile = _wfopen(pFilePath, pOpenMode); + if (*ppFile == NULL) { + return drmp3_result_from_errno(errno); + } + #endif + (void)pAllocationCallbacks; + } +#else + #if defined(__DJGPP__) + { + } + #else + { + mbstate_t mbs; + size_t lenMB; + const wchar_t* pFilePathTemp = pFilePath; + char* pFilePathMB = NULL; + char pOpenModeMB[32] = {0}; + DRMP3_ZERO_OBJECT(&mbs); + lenMB = wcsrtombs(NULL, &pFilePathTemp, 0, &mbs); + if (lenMB == (size_t)-1) { + return drmp3_result_from_errno(errno); + } + pFilePathMB = (char*)drmp3__malloc_from_callbacks(lenMB + 1, pAllocationCallbacks); + if (pFilePathMB == NULL) { + return DRMP3_OUT_OF_MEMORY; + } + pFilePathTemp = pFilePath; + DRMP3_ZERO_OBJECT(&mbs); + wcsrtombs(pFilePathMB, &pFilePathTemp, lenMB + 1, &mbs); + { + size_t i = 0; + for (;;) { + if (pOpenMode[i] == 0) { + pOpenModeMB[i] = '\0'; + break; + } + pOpenModeMB[i] = (char)pOpenMode[i]; + i += 1; + } + } + *ppFile = fopen(pFilePathMB, pOpenModeMB); + drmp3__free_from_callbacks(pFilePathMB, pAllocationCallbacks); + } + #endif + if (*ppFile == NULL) { + return DRMP3_ERROR; + } +#endif + return DRMP3_SUCCESS; +} +static size_t drmp3__on_read_stdio(void* pUserData, void* pBufferOut, size_t bytesToRead) { return fread(pBufferOut, 1, bytesToRead, (FILE*)pUserData); } -static ma_bool32 ma_dr_mp3__on_seek_stdio(void* pUserData, int offset, ma_dr_mp3_seek_origin origin) +static drmp3_bool32 drmp3__on_seek_stdio(void* pUserData, int offset, drmp3_seek_origin origin) { - return fseek((FILE*)pUserData, offset, (origin == ma_dr_mp3_seek_origin_current) ? SEEK_CUR : SEEK_SET) == 0; + return fseek((FILE*)pUserData, offset, (origin == drmp3_seek_origin_current) ? SEEK_CUR : SEEK_SET) == 0; } -MA_API ma_bool32 ma_dr_mp3_init_file(ma_dr_mp3* pMP3, const char* pFilePath, const ma_allocation_callbacks* pAllocationCallbacks) +DRMP3_API drmp3_bool32 drmp3_init_file(drmp3* pMP3, const char* pFilePath, const drmp3_allocation_callbacks* pAllocationCallbacks) { - ma_bool32 result; + drmp3_bool32 result; FILE* pFile; - if (ma_fopen(&pFile, pFilePath, "rb") != MA_SUCCESS) { - return MA_FALSE; + if (drmp3_fopen(&pFile, pFilePath, "rb") != DRMP3_SUCCESS) { + return DRMP3_FALSE; } - result = ma_dr_mp3_init(pMP3, ma_dr_mp3__on_read_stdio, ma_dr_mp3__on_seek_stdio, (void*)pFile, pAllocationCallbacks); - if (result != MA_TRUE) { + result = drmp3_init(pMP3, drmp3__on_read_stdio, drmp3__on_seek_stdio, (void*)pFile, pAllocationCallbacks); + if (result != DRMP3_TRUE) { fclose(pFile); return result; } - return MA_TRUE; + return DRMP3_TRUE; } -MA_API ma_bool32 ma_dr_mp3_init_file_w(ma_dr_mp3* pMP3, const wchar_t* pFilePath, const ma_allocation_callbacks* pAllocationCallbacks) +DRMP3_API drmp3_bool32 drmp3_init_file_w(drmp3* pMP3, const wchar_t* pFilePath, const drmp3_allocation_callbacks* pAllocationCallbacks) { - ma_bool32 result; + drmp3_bool32 result; FILE* pFile; - if (ma_wfopen(&pFile, pFilePath, L"rb", pAllocationCallbacks) != MA_SUCCESS) { - return MA_FALSE; + if (drmp3_wfopen(&pFile, pFilePath, L"rb", pAllocationCallbacks) != DRMP3_SUCCESS) { + return DRMP3_FALSE; } - result = ma_dr_mp3_init(pMP3, ma_dr_mp3__on_read_stdio, ma_dr_mp3__on_seek_stdio, (void*)pFile, pAllocationCallbacks); - if (result != MA_TRUE) { + result = drmp3_init(pMP3, drmp3__on_read_stdio, drmp3__on_seek_stdio, (void*)pFile, pAllocationCallbacks); + if (result != DRMP3_TRUE) { fclose(pFile); return result; } - return MA_TRUE; + return DRMP3_TRUE; } #endif -MA_API void ma_dr_mp3_uninit(ma_dr_mp3* pMP3) +DRMP3_API void drmp3_uninit(drmp3* pMP3) { if (pMP3 == NULL) { return; } -#ifndef MA_DR_MP3_NO_STDIO - if (pMP3->onRead == ma_dr_mp3__on_read_stdio) { +#ifndef DR_MP3_NO_STDIO + if (pMP3->onRead == drmp3__on_read_stdio) { FILE* pFile = (FILE*)pMP3->pUserData; if (pFile != NULL) { fclose(pFile); @@ -92805,14 +92716,14 @@ MA_API void ma_dr_mp3_uninit(ma_dr_mp3* pMP3) } } #endif - ma_dr_mp3__free_from_callbacks(pMP3->pData, &pMP3->allocationCallbacks); + drmp3__free_from_callbacks(pMP3->pData, &pMP3->allocationCallbacks); } -#if defined(MA_DR_MP3_FLOAT_OUTPUT) -static void ma_dr_mp3_f32_to_s16(ma_int16* dst, const float* src, ma_uint64 sampleCount) +#if defined(DR_MP3_FLOAT_OUTPUT) +static void drmp3_f32_to_s16(drmp3_int16* dst, const float* src, drmp3_uint64 sampleCount) { - ma_uint64 i; - ma_uint64 i4; - ma_uint64 sampleCount4; + drmp3_uint64 i; + drmp3_uint64 i4; + drmp3_uint64 sampleCount4; i = 0; sampleCount4 = sampleCount >> 2; for (i4 = 0; i4 < sampleCount4; i4 += 1) { @@ -92828,24 +92739,24 @@ static void ma_dr_mp3_f32_to_s16(ma_int16* dst, const float* src, ma_uint64 samp x1 = x1 * 32767.0f; x2 = x2 * 32767.0f; x3 = x3 * 32767.0f; - dst[i+0] = (ma_int16)x0; - dst[i+1] = (ma_int16)x1; - dst[i+2] = (ma_int16)x2; - dst[i+3] = (ma_int16)x3; + dst[i+0] = (drmp3_int16)x0; + dst[i+1] = (drmp3_int16)x1; + dst[i+2] = (drmp3_int16)x2; + dst[i+3] = (drmp3_int16)x3; i += 4; } for (; i < sampleCount; i += 1) { float x = src[i]; x = ((x < -1) ? -1 : ((x > 1) ? 1 : x)); x = x * 32767.0f; - dst[i] = (ma_int16)x; + dst[i] = (drmp3_int16)x; } } #endif -#if !defined(MA_DR_MP3_FLOAT_OUTPUT) -static void ma_dr_mp3_s16_to_f32(float* dst, const ma_int16* src, ma_uint64 sampleCount) +#if !defined(DR_MP3_FLOAT_OUTPUT) +static void drmp3_s16_to_f32(float* dst, const drmp3_int16* src, drmp3_uint64 sampleCount) { - ma_uint64 i; + drmp3_uint64 i; for (i = 0; i < sampleCount; i += 1) { float x = (float)src[i]; x = x * 0.000030517578125f; @@ -92853,22 +92764,22 @@ static void ma_dr_mp3_s16_to_f32(float* dst, const ma_int16* src, ma_uint64 samp } } #endif -static ma_uint64 ma_dr_mp3_read_pcm_frames_raw(ma_dr_mp3* pMP3, ma_uint64 framesToRead, void* pBufferOut) +static drmp3_uint64 drmp3_read_pcm_frames_raw(drmp3* pMP3, drmp3_uint64 framesToRead, void* pBufferOut) { - ma_uint64 totalFramesRead = 0; - MA_DR_MP3_ASSERT(pMP3 != NULL); - MA_DR_MP3_ASSERT(pMP3->onRead != NULL); + drmp3_uint64 totalFramesRead = 0; + DRMP3_ASSERT(pMP3 != NULL); + DRMP3_ASSERT(pMP3->onRead != NULL); while (framesToRead > 0) { - ma_uint32 framesToConsume = (ma_uint32)MA_DR_MP3_MIN(pMP3->pcmFramesRemainingInMP3Frame, framesToRead); + drmp3_uint32 framesToConsume = (drmp3_uint32)DRMP3_MIN(pMP3->pcmFramesRemainingInMP3Frame, framesToRead); if (pBufferOut != NULL) { - #if defined(MA_DR_MP3_FLOAT_OUTPUT) - float* pFramesOutF32 = (float*)MA_DR_MP3_OFFSET_PTR(pBufferOut, sizeof(float) * totalFramesRead * pMP3->channels); - float* pFramesInF32 = (float*)MA_DR_MP3_OFFSET_PTR(&pMP3->pcmFrames[0], sizeof(float) * pMP3->pcmFramesConsumedInMP3Frame * pMP3->mp3FrameChannels); - MA_DR_MP3_COPY_MEMORY(pFramesOutF32, pFramesInF32, sizeof(float) * framesToConsume * pMP3->channels); + #if defined(DR_MP3_FLOAT_OUTPUT) + float* pFramesOutF32 = (float*)DRMP3_OFFSET_PTR(pBufferOut, sizeof(float) * totalFramesRead * pMP3->channels); + float* pFramesInF32 = (float*)DRMP3_OFFSET_PTR(&pMP3->pcmFrames[0], sizeof(float) * pMP3->pcmFramesConsumedInMP3Frame * pMP3->mp3FrameChannels); + DRMP3_COPY_MEMORY(pFramesOutF32, pFramesInF32, sizeof(float) * framesToConsume * pMP3->channels); #else - ma_int16* pFramesOutS16 = (ma_int16*)MA_DR_MP3_OFFSET_PTR(pBufferOut, sizeof(ma_int16) * totalFramesRead * pMP3->channels); - ma_int16* pFramesInS16 = (ma_int16*)MA_DR_MP3_OFFSET_PTR(&pMP3->pcmFrames[0], sizeof(ma_int16) * pMP3->pcmFramesConsumedInMP3Frame * pMP3->mp3FrameChannels); - MA_DR_MP3_COPY_MEMORY(pFramesOutS16, pFramesInS16, sizeof(ma_int16) * framesToConsume * pMP3->channels); + drmp3_int16* pFramesOutS16 = (drmp3_int16*)DRMP3_OFFSET_PTR(pBufferOut, sizeof(drmp3_int16) * totalFramesRead * pMP3->channels); + drmp3_int16* pFramesInS16 = (drmp3_int16*)DRMP3_OFFSET_PTR(&pMP3->pcmFrames[0], sizeof(drmp3_int16) * pMP3->pcmFramesConsumedInMP3Frame * pMP3->mp3FrameChannels); + DRMP3_COPY_MEMORY(pFramesOutS16, pFramesInS16, sizeof(drmp3_int16) * framesToConsume * pMP3->channels); #endif } pMP3->currentPCMFrame += framesToConsume; @@ -92879,125 +92790,125 @@ static ma_uint64 ma_dr_mp3_read_pcm_frames_raw(ma_dr_mp3* pMP3, ma_uint64 frames if (framesToRead == 0) { break; } - MA_DR_MP3_ASSERT(pMP3->pcmFramesRemainingInMP3Frame == 0); - if (ma_dr_mp3_decode_next_frame(pMP3) == 0) { + DRMP3_ASSERT(pMP3->pcmFramesRemainingInMP3Frame == 0); + if (drmp3_decode_next_frame(pMP3) == 0) { break; } } return totalFramesRead; } -MA_API ma_uint64 ma_dr_mp3_read_pcm_frames_f32(ma_dr_mp3* pMP3, ma_uint64 framesToRead, float* pBufferOut) +DRMP3_API drmp3_uint64 drmp3_read_pcm_frames_f32(drmp3* pMP3, drmp3_uint64 framesToRead, float* pBufferOut) { if (pMP3 == NULL || pMP3->onRead == NULL) { return 0; } -#if defined(MA_DR_MP3_FLOAT_OUTPUT) - return ma_dr_mp3_read_pcm_frames_raw(pMP3, framesToRead, pBufferOut); +#if defined(DR_MP3_FLOAT_OUTPUT) + return drmp3_read_pcm_frames_raw(pMP3, framesToRead, pBufferOut); #else { - ma_int16 pTempS16[8192]; - ma_uint64 totalPCMFramesRead = 0; + drmp3_int16 pTempS16[8192]; + drmp3_uint64 totalPCMFramesRead = 0; while (totalPCMFramesRead < framesToRead) { - ma_uint64 framesJustRead; - ma_uint64 framesRemaining = framesToRead - totalPCMFramesRead; - ma_uint64 framesToReadNow = MA_DR_MP3_COUNTOF(pTempS16) / pMP3->channels; + drmp3_uint64 framesJustRead; + drmp3_uint64 framesRemaining = framesToRead - totalPCMFramesRead; + drmp3_uint64 framesToReadNow = DRMP3_COUNTOF(pTempS16) / pMP3->channels; if (framesToReadNow > framesRemaining) { framesToReadNow = framesRemaining; } - framesJustRead = ma_dr_mp3_read_pcm_frames_raw(pMP3, framesToReadNow, pTempS16); + framesJustRead = drmp3_read_pcm_frames_raw(pMP3, framesToReadNow, pTempS16); if (framesJustRead == 0) { break; } - ma_dr_mp3_s16_to_f32((float*)MA_DR_MP3_OFFSET_PTR(pBufferOut, sizeof(float) * totalPCMFramesRead * pMP3->channels), pTempS16, framesJustRead * pMP3->channels); + drmp3_s16_to_f32((float*)DRMP3_OFFSET_PTR(pBufferOut, sizeof(float) * totalPCMFramesRead * pMP3->channels), pTempS16, framesJustRead * pMP3->channels); totalPCMFramesRead += framesJustRead; } return totalPCMFramesRead; } #endif } -MA_API ma_uint64 ma_dr_mp3_read_pcm_frames_s16(ma_dr_mp3* pMP3, ma_uint64 framesToRead, ma_int16* pBufferOut) +DRMP3_API drmp3_uint64 drmp3_read_pcm_frames_s16(drmp3* pMP3, drmp3_uint64 framesToRead, drmp3_int16* pBufferOut) { if (pMP3 == NULL || pMP3->onRead == NULL) { return 0; } -#if !defined(MA_DR_MP3_FLOAT_OUTPUT) - return ma_dr_mp3_read_pcm_frames_raw(pMP3, framesToRead, pBufferOut); +#if !defined(DR_MP3_FLOAT_OUTPUT) + return drmp3_read_pcm_frames_raw(pMP3, framesToRead, pBufferOut); #else { float pTempF32[4096]; - ma_uint64 totalPCMFramesRead = 0; + drmp3_uint64 totalPCMFramesRead = 0; while (totalPCMFramesRead < framesToRead) { - ma_uint64 framesJustRead; - ma_uint64 framesRemaining = framesToRead - totalPCMFramesRead; - ma_uint64 framesToReadNow = MA_DR_MP3_COUNTOF(pTempF32) / pMP3->channels; + drmp3_uint64 framesJustRead; + drmp3_uint64 framesRemaining = framesToRead - totalPCMFramesRead; + drmp3_uint64 framesToReadNow = DRMP3_COUNTOF(pTempF32) / pMP3->channels; if (framesToReadNow > framesRemaining) { framesToReadNow = framesRemaining; } - framesJustRead = ma_dr_mp3_read_pcm_frames_raw(pMP3, framesToReadNow, pTempF32); + framesJustRead = drmp3_read_pcm_frames_raw(pMP3, framesToReadNow, pTempF32); if (framesJustRead == 0) { break; } - ma_dr_mp3_f32_to_s16((ma_int16*)MA_DR_MP3_OFFSET_PTR(pBufferOut, sizeof(ma_int16) * totalPCMFramesRead * pMP3->channels), pTempF32, framesJustRead * pMP3->channels); + drmp3_f32_to_s16((drmp3_int16*)DRMP3_OFFSET_PTR(pBufferOut, sizeof(drmp3_int16) * totalPCMFramesRead * pMP3->channels), pTempF32, framesJustRead * pMP3->channels); totalPCMFramesRead += framesJustRead; } return totalPCMFramesRead; } #endif } -static void ma_dr_mp3_reset(ma_dr_mp3* pMP3) +static void drmp3_reset(drmp3* pMP3) { - MA_DR_MP3_ASSERT(pMP3 != NULL); + DRMP3_ASSERT(pMP3 != NULL); pMP3->pcmFramesConsumedInMP3Frame = 0; pMP3->pcmFramesRemainingInMP3Frame = 0; pMP3->currentPCMFrame = 0; pMP3->dataSize = 0; - pMP3->atEnd = MA_FALSE; - ma_dr_mp3dec_init(&pMP3->decoder); + pMP3->atEnd = DRMP3_FALSE; + drmp3dec_init(&pMP3->decoder); } -static ma_bool32 ma_dr_mp3_seek_to_start_of_stream(ma_dr_mp3* pMP3) +static drmp3_bool32 drmp3_seek_to_start_of_stream(drmp3* pMP3) { - MA_DR_MP3_ASSERT(pMP3 != NULL); - MA_DR_MP3_ASSERT(pMP3->onSeek != NULL); - if (!ma_dr_mp3__on_seek(pMP3, 0, ma_dr_mp3_seek_origin_start)) { - return MA_FALSE; + DRMP3_ASSERT(pMP3 != NULL); + DRMP3_ASSERT(pMP3->onSeek != NULL); + if (!drmp3__on_seek(pMP3, 0, drmp3_seek_origin_start)) { + return DRMP3_FALSE; } - ma_dr_mp3_reset(pMP3); - return MA_TRUE; + drmp3_reset(pMP3); + return DRMP3_TRUE; } -static ma_bool32 ma_dr_mp3_seek_forward_by_pcm_frames__brute_force(ma_dr_mp3* pMP3, ma_uint64 frameOffset) +static drmp3_bool32 drmp3_seek_forward_by_pcm_frames__brute_force(drmp3* pMP3, drmp3_uint64 frameOffset) { - ma_uint64 framesRead; -#if defined(MA_DR_MP3_FLOAT_OUTPUT) - framesRead = ma_dr_mp3_read_pcm_frames_f32(pMP3, frameOffset, NULL); + drmp3_uint64 framesRead; +#if defined(DR_MP3_FLOAT_OUTPUT) + framesRead = drmp3_read_pcm_frames_f32(pMP3, frameOffset, NULL); #else - framesRead = ma_dr_mp3_read_pcm_frames_s16(pMP3, frameOffset, NULL); + framesRead = drmp3_read_pcm_frames_s16(pMP3, frameOffset, NULL); #endif if (framesRead != frameOffset) { - return MA_FALSE; + return DRMP3_FALSE; } - return MA_TRUE; + return DRMP3_TRUE; } -static ma_bool32 ma_dr_mp3_seek_to_pcm_frame__brute_force(ma_dr_mp3* pMP3, ma_uint64 frameIndex) +static drmp3_bool32 drmp3_seek_to_pcm_frame__brute_force(drmp3* pMP3, drmp3_uint64 frameIndex) { - MA_DR_MP3_ASSERT(pMP3 != NULL); + DRMP3_ASSERT(pMP3 != NULL); if (frameIndex == pMP3->currentPCMFrame) { - return MA_TRUE; + return DRMP3_TRUE; } if (frameIndex < pMP3->currentPCMFrame) { - if (!ma_dr_mp3_seek_to_start_of_stream(pMP3)) { - return MA_FALSE; + if (!drmp3_seek_to_start_of_stream(pMP3)) { + return DRMP3_FALSE; } } - MA_DR_MP3_ASSERT(frameIndex >= pMP3->currentPCMFrame); - return ma_dr_mp3_seek_forward_by_pcm_frames__brute_force(pMP3, (frameIndex - pMP3->currentPCMFrame)); + DRMP3_ASSERT(frameIndex >= pMP3->currentPCMFrame); + return drmp3_seek_forward_by_pcm_frames__brute_force(pMP3, (frameIndex - pMP3->currentPCMFrame)); } -static ma_bool32 ma_dr_mp3_find_closest_seek_point(ma_dr_mp3* pMP3, ma_uint64 frameIndex, ma_uint32* pSeekPointIndex) +static drmp3_bool32 drmp3_find_closest_seek_point(drmp3* pMP3, drmp3_uint64 frameIndex, drmp3_uint32* pSeekPointIndex) { - ma_uint32 iSeekPoint; - MA_DR_MP3_ASSERT(pSeekPointIndex != NULL); + drmp3_uint32 iSeekPoint; + DRMP3_ASSERT(pSeekPointIndex != NULL); *pSeekPointIndex = 0; if (frameIndex < pMP3->pSeekPoints[0].pcmFrameIndex) { - return MA_FALSE; + return DRMP3_FALSE; } for (iSeekPoint = 0; iSeekPoint < pMP3->seekPointCount; ++iSeekPoint) { if (pMP3->pSeekPoints[iSeekPoint].pcmFrameIndex > frameIndex) { @@ -93005,18 +92916,18 @@ static ma_bool32 ma_dr_mp3_find_closest_seek_point(ma_dr_mp3* pMP3, ma_uint64 fr } *pSeekPointIndex = iSeekPoint; } - return MA_TRUE; + return DRMP3_TRUE; } -static ma_bool32 ma_dr_mp3_seek_to_pcm_frame__seek_table(ma_dr_mp3* pMP3, ma_uint64 frameIndex) +static drmp3_bool32 drmp3_seek_to_pcm_frame__seek_table(drmp3* pMP3, drmp3_uint64 frameIndex) { - ma_dr_mp3_seek_point seekPoint; - ma_uint32 priorSeekPointIndex; - ma_uint16 iMP3Frame; - ma_uint64 leftoverFrames; - MA_DR_MP3_ASSERT(pMP3 != NULL); - MA_DR_MP3_ASSERT(pMP3->pSeekPoints != NULL); - MA_DR_MP3_ASSERT(pMP3->seekPointCount > 0); - if (ma_dr_mp3_find_closest_seek_point(pMP3, frameIndex, &priorSeekPointIndex)) { + drmp3_seek_point seekPoint; + drmp3_uint32 priorSeekPointIndex; + drmp3_uint16 iMP3Frame; + drmp3_uint64 leftoverFrames; + DRMP3_ASSERT(pMP3 != NULL); + DRMP3_ASSERT(pMP3->pSeekPoints != NULL); + DRMP3_ASSERT(pMP3->seekPointCount > 0); + if (drmp3_find_closest_seek_point(pMP3, frameIndex, &priorSeekPointIndex)) { seekPoint = pMP3->pSeekPoints[priorSeekPointIndex]; } else { seekPoint.seekPosInBytes = 0; @@ -93024,71 +92935,71 @@ static ma_bool32 ma_dr_mp3_seek_to_pcm_frame__seek_table(ma_dr_mp3* pMP3, ma_uin seekPoint.mp3FramesToDiscard = 0; seekPoint.pcmFramesToDiscard = 0; } - if (!ma_dr_mp3__on_seek_64(pMP3, seekPoint.seekPosInBytes, ma_dr_mp3_seek_origin_start)) { - return MA_FALSE; + if (!drmp3__on_seek_64(pMP3, seekPoint.seekPosInBytes, drmp3_seek_origin_start)) { + return DRMP3_FALSE; } - ma_dr_mp3_reset(pMP3); + drmp3_reset(pMP3); for (iMP3Frame = 0; iMP3Frame < seekPoint.mp3FramesToDiscard; ++iMP3Frame) { - ma_uint32 pcmFramesRead; - ma_dr_mp3d_sample_t* pPCMFrames; + drmp3_uint32 pcmFramesRead; + drmp3d_sample_t* pPCMFrames; pPCMFrames = NULL; if (iMP3Frame == seekPoint.mp3FramesToDiscard-1) { - pPCMFrames = (ma_dr_mp3d_sample_t*)pMP3->pcmFrames; + pPCMFrames = (drmp3d_sample_t*)pMP3->pcmFrames; } - pcmFramesRead = ma_dr_mp3_decode_next_frame_ex(pMP3, pPCMFrames); + pcmFramesRead = drmp3_decode_next_frame_ex(pMP3, pPCMFrames); if (pcmFramesRead == 0) { - return MA_FALSE; + return DRMP3_FALSE; } } pMP3->currentPCMFrame = seekPoint.pcmFrameIndex - seekPoint.pcmFramesToDiscard; leftoverFrames = frameIndex - pMP3->currentPCMFrame; - return ma_dr_mp3_seek_forward_by_pcm_frames__brute_force(pMP3, leftoverFrames); + return drmp3_seek_forward_by_pcm_frames__brute_force(pMP3, leftoverFrames); } -MA_API ma_bool32 ma_dr_mp3_seek_to_pcm_frame(ma_dr_mp3* pMP3, ma_uint64 frameIndex) +DRMP3_API drmp3_bool32 drmp3_seek_to_pcm_frame(drmp3* pMP3, drmp3_uint64 frameIndex) { if (pMP3 == NULL || pMP3->onSeek == NULL) { - return MA_FALSE; + return DRMP3_FALSE; } if (frameIndex == 0) { - return ma_dr_mp3_seek_to_start_of_stream(pMP3); + return drmp3_seek_to_start_of_stream(pMP3); } if (pMP3->pSeekPoints != NULL && pMP3->seekPointCount > 0) { - return ma_dr_mp3_seek_to_pcm_frame__seek_table(pMP3, frameIndex); + return drmp3_seek_to_pcm_frame__seek_table(pMP3, frameIndex); } else { - return ma_dr_mp3_seek_to_pcm_frame__brute_force(pMP3, frameIndex); + return drmp3_seek_to_pcm_frame__brute_force(pMP3, frameIndex); } } -MA_API ma_bool32 ma_dr_mp3_get_mp3_and_pcm_frame_count(ma_dr_mp3* pMP3, ma_uint64* pMP3FrameCount, ma_uint64* pPCMFrameCount) +DRMP3_API drmp3_bool32 drmp3_get_mp3_and_pcm_frame_count(drmp3* pMP3, drmp3_uint64* pMP3FrameCount, drmp3_uint64* pPCMFrameCount) { - ma_uint64 currentPCMFrame; - ma_uint64 totalPCMFrameCount; - ma_uint64 totalMP3FrameCount; + drmp3_uint64 currentPCMFrame; + drmp3_uint64 totalPCMFrameCount; + drmp3_uint64 totalMP3FrameCount; if (pMP3 == NULL) { - return MA_FALSE; + return DRMP3_FALSE; } if (pMP3->onSeek == NULL) { - return MA_FALSE; + return DRMP3_FALSE; } currentPCMFrame = pMP3->currentPCMFrame; - if (!ma_dr_mp3_seek_to_start_of_stream(pMP3)) { - return MA_FALSE; + if (!drmp3_seek_to_start_of_stream(pMP3)) { + return DRMP3_FALSE; } totalPCMFrameCount = 0; totalMP3FrameCount = 0; for (;;) { - ma_uint32 pcmFramesInCurrentMP3Frame; - pcmFramesInCurrentMP3Frame = ma_dr_mp3_decode_next_frame_ex(pMP3, NULL); + drmp3_uint32 pcmFramesInCurrentMP3Frame; + pcmFramesInCurrentMP3Frame = drmp3_decode_next_frame_ex(pMP3, NULL); if (pcmFramesInCurrentMP3Frame == 0) { break; } totalPCMFrameCount += pcmFramesInCurrentMP3Frame; totalMP3FrameCount += 1; } - if (!ma_dr_mp3_seek_to_start_of_stream(pMP3)) { - return MA_FALSE; + if (!drmp3_seek_to_start_of_stream(pMP3)) { + return DRMP3_FALSE; } - if (!ma_dr_mp3_seek_to_pcm_frame(pMP3, currentPCMFrame)) { - return MA_FALSE; + if (!drmp3_seek_to_pcm_frame(pMP3, currentPCMFrame)) { + return DRMP3_FALSE; } if (pMP3FrameCount != NULL) { *pMP3FrameCount = totalMP3FrameCount; @@ -93096,89 +93007,89 @@ MA_API ma_bool32 ma_dr_mp3_get_mp3_and_pcm_frame_count(ma_dr_mp3* pMP3, ma_uint6 if (pPCMFrameCount != NULL) { *pPCMFrameCount = totalPCMFrameCount; } - return MA_TRUE; + return DRMP3_TRUE; } -MA_API ma_uint64 ma_dr_mp3_get_pcm_frame_count(ma_dr_mp3* pMP3) +DRMP3_API drmp3_uint64 drmp3_get_pcm_frame_count(drmp3* pMP3) { - ma_uint64 totalPCMFrameCount; - if (!ma_dr_mp3_get_mp3_and_pcm_frame_count(pMP3, NULL, &totalPCMFrameCount)) { + drmp3_uint64 totalPCMFrameCount; + if (!drmp3_get_mp3_and_pcm_frame_count(pMP3, NULL, &totalPCMFrameCount)) { return 0; } return totalPCMFrameCount; } -MA_API ma_uint64 ma_dr_mp3_get_mp3_frame_count(ma_dr_mp3* pMP3) +DRMP3_API drmp3_uint64 drmp3_get_mp3_frame_count(drmp3* pMP3) { - ma_uint64 totalMP3FrameCount; - if (!ma_dr_mp3_get_mp3_and_pcm_frame_count(pMP3, &totalMP3FrameCount, NULL)) { + drmp3_uint64 totalMP3FrameCount; + if (!drmp3_get_mp3_and_pcm_frame_count(pMP3, &totalMP3FrameCount, NULL)) { return 0; } return totalMP3FrameCount; } -static void ma_dr_mp3__accumulate_running_pcm_frame_count(ma_dr_mp3* pMP3, ma_uint32 pcmFrameCountIn, ma_uint64* pRunningPCMFrameCount, float* pRunningPCMFrameCountFractionalPart) +static void drmp3__accumulate_running_pcm_frame_count(drmp3* pMP3, drmp3_uint32 pcmFrameCountIn, drmp3_uint64* pRunningPCMFrameCount, float* pRunningPCMFrameCountFractionalPart) { float srcRatio; float pcmFrameCountOutF; - ma_uint32 pcmFrameCountOut; + drmp3_uint32 pcmFrameCountOut; srcRatio = (float)pMP3->mp3FrameSampleRate / (float)pMP3->sampleRate; - MA_DR_MP3_ASSERT(srcRatio > 0); + DRMP3_ASSERT(srcRatio > 0); pcmFrameCountOutF = *pRunningPCMFrameCountFractionalPart + (pcmFrameCountIn / srcRatio); - pcmFrameCountOut = (ma_uint32)pcmFrameCountOutF; + pcmFrameCountOut = (drmp3_uint32)pcmFrameCountOutF; *pRunningPCMFrameCountFractionalPart = pcmFrameCountOutF - pcmFrameCountOut; *pRunningPCMFrameCount += pcmFrameCountOut; } typedef struct { - ma_uint64 bytePos; - ma_uint64 pcmFrameIndex; -} ma_dr_mp3__seeking_mp3_frame_info; -MA_API ma_bool32 ma_dr_mp3_calculate_seek_points(ma_dr_mp3* pMP3, ma_uint32* pSeekPointCount, ma_dr_mp3_seek_point* pSeekPoints) + drmp3_uint64 bytePos; + drmp3_uint64 pcmFrameIndex; +} drmp3__seeking_mp3_frame_info; +DRMP3_API drmp3_bool32 drmp3_calculate_seek_points(drmp3* pMP3, drmp3_uint32* pSeekPointCount, drmp3_seek_point* pSeekPoints) { - ma_uint32 seekPointCount; - ma_uint64 currentPCMFrame; - ma_uint64 totalMP3FrameCount; - ma_uint64 totalPCMFrameCount; + drmp3_uint32 seekPointCount; + drmp3_uint64 currentPCMFrame; + drmp3_uint64 totalMP3FrameCount; + drmp3_uint64 totalPCMFrameCount; if (pMP3 == NULL || pSeekPointCount == NULL || pSeekPoints == NULL) { - return MA_FALSE; + return DRMP3_FALSE; } seekPointCount = *pSeekPointCount; if (seekPointCount == 0) { - return MA_FALSE; + return DRMP3_FALSE; } currentPCMFrame = pMP3->currentPCMFrame; - if (!ma_dr_mp3_get_mp3_and_pcm_frame_count(pMP3, &totalMP3FrameCount, &totalPCMFrameCount)) { - return MA_FALSE; + if (!drmp3_get_mp3_and_pcm_frame_count(pMP3, &totalMP3FrameCount, &totalPCMFrameCount)) { + return DRMP3_FALSE; } - if (totalMP3FrameCount < MA_DR_MP3_SEEK_LEADING_MP3_FRAMES+1) { + if (totalMP3FrameCount < DRMP3_SEEK_LEADING_MP3_FRAMES+1) { seekPointCount = 1; pSeekPoints[0].seekPosInBytes = 0; pSeekPoints[0].pcmFrameIndex = 0; pSeekPoints[0].mp3FramesToDiscard = 0; pSeekPoints[0].pcmFramesToDiscard = 0; } else { - ma_uint64 pcmFramesBetweenSeekPoints; - ma_dr_mp3__seeking_mp3_frame_info mp3FrameInfo[MA_DR_MP3_SEEK_LEADING_MP3_FRAMES+1]; - ma_uint64 runningPCMFrameCount = 0; + drmp3_uint64 pcmFramesBetweenSeekPoints; + drmp3__seeking_mp3_frame_info mp3FrameInfo[DRMP3_SEEK_LEADING_MP3_FRAMES+1]; + drmp3_uint64 runningPCMFrameCount = 0; float runningPCMFrameCountFractionalPart = 0; - ma_uint64 nextTargetPCMFrame; - ma_uint32 iMP3Frame; - ma_uint32 iSeekPoint; + drmp3_uint64 nextTargetPCMFrame; + drmp3_uint32 iMP3Frame; + drmp3_uint32 iSeekPoint; if (seekPointCount > totalMP3FrameCount-1) { - seekPointCount = (ma_uint32)totalMP3FrameCount-1; + seekPointCount = (drmp3_uint32)totalMP3FrameCount-1; } pcmFramesBetweenSeekPoints = totalPCMFrameCount / (seekPointCount+1); - if (!ma_dr_mp3_seek_to_start_of_stream(pMP3)) { - return MA_FALSE; + if (!drmp3_seek_to_start_of_stream(pMP3)) { + return DRMP3_FALSE; } - for (iMP3Frame = 0; iMP3Frame < MA_DR_MP3_SEEK_LEADING_MP3_FRAMES+1; ++iMP3Frame) { - ma_uint32 pcmFramesInCurrentMP3FrameIn; - MA_DR_MP3_ASSERT(pMP3->streamCursor >= pMP3->dataSize); + for (iMP3Frame = 0; iMP3Frame < DRMP3_SEEK_LEADING_MP3_FRAMES+1; ++iMP3Frame) { + drmp3_uint32 pcmFramesInCurrentMP3FrameIn; + DRMP3_ASSERT(pMP3->streamCursor >= pMP3->dataSize); mp3FrameInfo[iMP3Frame].bytePos = pMP3->streamCursor - pMP3->dataSize; mp3FrameInfo[iMP3Frame].pcmFrameIndex = runningPCMFrameCount; - pcmFramesInCurrentMP3FrameIn = ma_dr_mp3_decode_next_frame_ex(pMP3, NULL); + pcmFramesInCurrentMP3FrameIn = drmp3_decode_next_frame_ex(pMP3, NULL); if (pcmFramesInCurrentMP3FrameIn == 0) { - return MA_FALSE; + return DRMP3_FALSE; } - ma_dr_mp3__accumulate_running_pcm_frame_count(pMP3, pcmFramesInCurrentMP3FrameIn, &runningPCMFrameCount, &runningPCMFrameCountFractionalPart); + drmp3__accumulate_running_pcm_frame_count(pMP3, pcmFramesInCurrentMP3FrameIn, &runningPCMFrameCount, &runningPCMFrameCountFractionalPart); } nextTargetPCMFrame = 0; for (iSeekPoint = 0; iSeekPoint < seekPointCount; ++iSeekPoint) { @@ -93187,43 +93098,43 @@ MA_API ma_bool32 ma_dr_mp3_calculate_seek_points(ma_dr_mp3* pMP3, ma_uint32* pSe if (nextTargetPCMFrame < runningPCMFrameCount) { pSeekPoints[iSeekPoint].seekPosInBytes = mp3FrameInfo[0].bytePos; pSeekPoints[iSeekPoint].pcmFrameIndex = nextTargetPCMFrame; - pSeekPoints[iSeekPoint].mp3FramesToDiscard = MA_DR_MP3_SEEK_LEADING_MP3_FRAMES; - pSeekPoints[iSeekPoint].pcmFramesToDiscard = (ma_uint16)(nextTargetPCMFrame - mp3FrameInfo[MA_DR_MP3_SEEK_LEADING_MP3_FRAMES-1].pcmFrameIndex); + pSeekPoints[iSeekPoint].mp3FramesToDiscard = DRMP3_SEEK_LEADING_MP3_FRAMES; + pSeekPoints[iSeekPoint].pcmFramesToDiscard = (drmp3_uint16)(nextTargetPCMFrame - mp3FrameInfo[DRMP3_SEEK_LEADING_MP3_FRAMES-1].pcmFrameIndex); break; } else { size_t i; - ma_uint32 pcmFramesInCurrentMP3FrameIn; - for (i = 0; i < MA_DR_MP3_COUNTOF(mp3FrameInfo)-1; ++i) { + drmp3_uint32 pcmFramesInCurrentMP3FrameIn; + for (i = 0; i < DRMP3_COUNTOF(mp3FrameInfo)-1; ++i) { mp3FrameInfo[i] = mp3FrameInfo[i+1]; } - mp3FrameInfo[MA_DR_MP3_COUNTOF(mp3FrameInfo)-1].bytePos = pMP3->streamCursor - pMP3->dataSize; - mp3FrameInfo[MA_DR_MP3_COUNTOF(mp3FrameInfo)-1].pcmFrameIndex = runningPCMFrameCount; - pcmFramesInCurrentMP3FrameIn = ma_dr_mp3_decode_next_frame_ex(pMP3, NULL); + mp3FrameInfo[DRMP3_COUNTOF(mp3FrameInfo)-1].bytePos = pMP3->streamCursor - pMP3->dataSize; + mp3FrameInfo[DRMP3_COUNTOF(mp3FrameInfo)-1].pcmFrameIndex = runningPCMFrameCount; + pcmFramesInCurrentMP3FrameIn = drmp3_decode_next_frame_ex(pMP3, NULL); if (pcmFramesInCurrentMP3FrameIn == 0) { pSeekPoints[iSeekPoint].seekPosInBytes = mp3FrameInfo[0].bytePos; pSeekPoints[iSeekPoint].pcmFrameIndex = nextTargetPCMFrame; - pSeekPoints[iSeekPoint].mp3FramesToDiscard = MA_DR_MP3_SEEK_LEADING_MP3_FRAMES; - pSeekPoints[iSeekPoint].pcmFramesToDiscard = (ma_uint16)(nextTargetPCMFrame - mp3FrameInfo[MA_DR_MP3_SEEK_LEADING_MP3_FRAMES-1].pcmFrameIndex); + pSeekPoints[iSeekPoint].mp3FramesToDiscard = DRMP3_SEEK_LEADING_MP3_FRAMES; + pSeekPoints[iSeekPoint].pcmFramesToDiscard = (drmp3_uint16)(nextTargetPCMFrame - mp3FrameInfo[DRMP3_SEEK_LEADING_MP3_FRAMES-1].pcmFrameIndex); break; } - ma_dr_mp3__accumulate_running_pcm_frame_count(pMP3, pcmFramesInCurrentMP3FrameIn, &runningPCMFrameCount, &runningPCMFrameCountFractionalPart); + drmp3__accumulate_running_pcm_frame_count(pMP3, pcmFramesInCurrentMP3FrameIn, &runningPCMFrameCount, &runningPCMFrameCountFractionalPart); } } } - if (!ma_dr_mp3_seek_to_start_of_stream(pMP3)) { - return MA_FALSE; + if (!drmp3_seek_to_start_of_stream(pMP3)) { + return DRMP3_FALSE; } - if (!ma_dr_mp3_seek_to_pcm_frame(pMP3, currentPCMFrame)) { - return MA_FALSE; + if (!drmp3_seek_to_pcm_frame(pMP3, currentPCMFrame)) { + return DRMP3_FALSE; } } *pSeekPointCount = seekPointCount; - return MA_TRUE; + return DRMP3_TRUE; } -MA_API ma_bool32 ma_dr_mp3_bind_seek_table(ma_dr_mp3* pMP3, ma_uint32 seekPointCount, ma_dr_mp3_seek_point* pSeekPoints) +DRMP3_API drmp3_bool32 drmp3_bind_seek_table(drmp3* pMP3, drmp3_uint32 seekPointCount, drmp3_seek_point* pSeekPoints) { if (pMP3 == NULL) { - return MA_FALSE; + return DRMP3_FALSE; } if (seekPointCount == 0 || pSeekPoints == NULL) { pMP3->seekPointCount = 0; @@ -93232,25 +93143,25 @@ MA_API ma_bool32 ma_dr_mp3_bind_seek_table(ma_dr_mp3* pMP3, ma_uint32 seekPointC pMP3->seekPointCount = seekPointCount; pMP3->pSeekPoints = pSeekPoints; } - return MA_TRUE; + return DRMP3_TRUE; } -static float* ma_dr_mp3__full_read_and_close_f32(ma_dr_mp3* pMP3, ma_dr_mp3_config* pConfig, ma_uint64* pTotalFrameCount) +static float* drmp3__full_read_and_close_f32(drmp3* pMP3, drmp3_config* pConfig, drmp3_uint64* pTotalFrameCount) { - ma_uint64 totalFramesRead = 0; - ma_uint64 framesCapacity = 0; + drmp3_uint64 totalFramesRead = 0; + drmp3_uint64 framesCapacity = 0; float* pFrames = NULL; float temp[4096]; - MA_DR_MP3_ASSERT(pMP3 != NULL); + DRMP3_ASSERT(pMP3 != NULL); for (;;) { - ma_uint64 framesToReadRightNow = MA_DR_MP3_COUNTOF(temp) / pMP3->channels; - ma_uint64 framesJustRead = ma_dr_mp3_read_pcm_frames_f32(pMP3, framesToReadRightNow, temp); + drmp3_uint64 framesToReadRightNow = DRMP3_COUNTOF(temp) / pMP3->channels; + drmp3_uint64 framesJustRead = drmp3_read_pcm_frames_f32(pMP3, framesToReadRightNow, temp); if (framesJustRead == 0) { break; } if (framesCapacity < totalFramesRead + framesJustRead) { - ma_uint64 oldFramesBufferSize; - ma_uint64 newFramesBufferSize; - ma_uint64 newFramesCap; + drmp3_uint64 oldFramesBufferSize; + drmp3_uint64 newFramesBufferSize; + drmp3_uint64 newFramesCap; float* pNewFrames; newFramesCap = framesCapacity * 2; if (newFramesCap < totalFramesRead + framesJustRead) { @@ -93258,18 +93169,18 @@ static float* ma_dr_mp3__full_read_and_close_f32(ma_dr_mp3* pMP3, ma_dr_mp3_conf } oldFramesBufferSize = framesCapacity * pMP3->channels * sizeof(float); newFramesBufferSize = newFramesCap * pMP3->channels * sizeof(float); - if (newFramesBufferSize > (ma_uint64)MA_SIZE_MAX) { + if (newFramesBufferSize > (drmp3_uint64)DRMP3_SIZE_MAX) { break; } - pNewFrames = (float*)ma_dr_mp3__realloc_from_callbacks(pFrames, (size_t)newFramesBufferSize, (size_t)oldFramesBufferSize, &pMP3->allocationCallbacks); + pNewFrames = (float*)drmp3__realloc_from_callbacks(pFrames, (size_t)newFramesBufferSize, (size_t)oldFramesBufferSize, &pMP3->allocationCallbacks); if (pNewFrames == NULL) { - ma_dr_mp3__free_from_callbacks(pFrames, &pMP3->allocationCallbacks); + drmp3__free_from_callbacks(pFrames, &pMP3->allocationCallbacks); break; } pFrames = pNewFrames; framesCapacity = newFramesCap; } - MA_DR_MP3_COPY_MEMORY(pFrames + totalFramesRead*pMP3->channels, temp, (size_t)(framesJustRead*pMP3->channels*sizeof(float))); + DRMP3_COPY_MEMORY(pFrames + totalFramesRead*pMP3->channels, temp, (size_t)(framesJustRead*pMP3->channels*sizeof(float))); totalFramesRead += framesJustRead; if (framesJustRead != framesToReadRightNow) { break; @@ -93279,48 +93190,48 @@ static float* ma_dr_mp3__full_read_and_close_f32(ma_dr_mp3* pMP3, ma_dr_mp3_conf pConfig->channels = pMP3->channels; pConfig->sampleRate = pMP3->sampleRate; } - ma_dr_mp3_uninit(pMP3); + drmp3_uninit(pMP3); if (pTotalFrameCount) { *pTotalFrameCount = totalFramesRead; } return pFrames; } -static ma_int16* ma_dr_mp3__full_read_and_close_s16(ma_dr_mp3* pMP3, ma_dr_mp3_config* pConfig, ma_uint64* pTotalFrameCount) +static drmp3_int16* drmp3__full_read_and_close_s16(drmp3* pMP3, drmp3_config* pConfig, drmp3_uint64* pTotalFrameCount) { - ma_uint64 totalFramesRead = 0; - ma_uint64 framesCapacity = 0; - ma_int16* pFrames = NULL; - ma_int16 temp[4096]; - MA_DR_MP3_ASSERT(pMP3 != NULL); + drmp3_uint64 totalFramesRead = 0; + drmp3_uint64 framesCapacity = 0; + drmp3_int16* pFrames = NULL; + drmp3_int16 temp[4096]; + DRMP3_ASSERT(pMP3 != NULL); for (;;) { - ma_uint64 framesToReadRightNow = MA_DR_MP3_COUNTOF(temp) / pMP3->channels; - ma_uint64 framesJustRead = ma_dr_mp3_read_pcm_frames_s16(pMP3, framesToReadRightNow, temp); + drmp3_uint64 framesToReadRightNow = DRMP3_COUNTOF(temp) / pMP3->channels; + drmp3_uint64 framesJustRead = drmp3_read_pcm_frames_s16(pMP3, framesToReadRightNow, temp); if (framesJustRead == 0) { break; } if (framesCapacity < totalFramesRead + framesJustRead) { - ma_uint64 newFramesBufferSize; - ma_uint64 oldFramesBufferSize; - ma_uint64 newFramesCap; - ma_int16* pNewFrames; + drmp3_uint64 newFramesBufferSize; + drmp3_uint64 oldFramesBufferSize; + drmp3_uint64 newFramesCap; + drmp3_int16* pNewFrames; newFramesCap = framesCapacity * 2; if (newFramesCap < totalFramesRead + framesJustRead) { newFramesCap = totalFramesRead + framesJustRead; } - oldFramesBufferSize = framesCapacity * pMP3->channels * sizeof(ma_int16); - newFramesBufferSize = newFramesCap * pMP3->channels * sizeof(ma_int16); - if (newFramesBufferSize > (ma_uint64)MA_SIZE_MAX) { + oldFramesBufferSize = framesCapacity * pMP3->channels * sizeof(drmp3_int16); + newFramesBufferSize = newFramesCap * pMP3->channels * sizeof(drmp3_int16); + if (newFramesBufferSize > (drmp3_uint64)DRMP3_SIZE_MAX) { break; } - pNewFrames = (ma_int16*)ma_dr_mp3__realloc_from_callbacks(pFrames, (size_t)newFramesBufferSize, (size_t)oldFramesBufferSize, &pMP3->allocationCallbacks); + pNewFrames = (drmp3_int16*)drmp3__realloc_from_callbacks(pFrames, (size_t)newFramesBufferSize, (size_t)oldFramesBufferSize, &pMP3->allocationCallbacks); if (pNewFrames == NULL) { - ma_dr_mp3__free_from_callbacks(pFrames, &pMP3->allocationCallbacks); + drmp3__free_from_callbacks(pFrames, &pMP3->allocationCallbacks); break; } pFrames = pNewFrames; framesCapacity = newFramesCap; } - MA_DR_MP3_COPY_MEMORY(pFrames + totalFramesRead*pMP3->channels, temp, (size_t)(framesJustRead*pMP3->channels*sizeof(ma_int16))); + DRMP3_COPY_MEMORY(pFrames + totalFramesRead*pMP3->channels, temp, (size_t)(framesJustRead*pMP3->channels*sizeof(drmp3_int16))); totalFramesRead += framesJustRead; if (framesJustRead != framesToReadRightNow) { break; @@ -93330,81 +93241,81 @@ static ma_int16* ma_dr_mp3__full_read_and_close_s16(ma_dr_mp3* pMP3, ma_dr_mp3_c pConfig->channels = pMP3->channels; pConfig->sampleRate = pMP3->sampleRate; } - ma_dr_mp3_uninit(pMP3); + drmp3_uninit(pMP3); if (pTotalFrameCount) { *pTotalFrameCount = totalFramesRead; } return pFrames; } -MA_API float* ma_dr_mp3_open_and_read_pcm_frames_f32(ma_dr_mp3_read_proc onRead, ma_dr_mp3_seek_proc onSeek, void* pUserData, ma_dr_mp3_config* pConfig, ma_uint64* pTotalFrameCount, const ma_allocation_callbacks* pAllocationCallbacks) +DRMP3_API float* drmp3_open_and_read_pcm_frames_f32(drmp3_read_proc onRead, drmp3_seek_proc onSeek, void* pUserData, drmp3_config* pConfig, drmp3_uint64* pTotalFrameCount, const drmp3_allocation_callbacks* pAllocationCallbacks) { - ma_dr_mp3 mp3; - if (!ma_dr_mp3_init(&mp3, onRead, onSeek, pUserData, pAllocationCallbacks)) { + drmp3 mp3; + if (!drmp3_init(&mp3, onRead, onSeek, pUserData, pAllocationCallbacks)) { return NULL; } - return ma_dr_mp3__full_read_and_close_f32(&mp3, pConfig, pTotalFrameCount); + return drmp3__full_read_and_close_f32(&mp3, pConfig, pTotalFrameCount); } -MA_API ma_int16* ma_dr_mp3_open_and_read_pcm_frames_s16(ma_dr_mp3_read_proc onRead, ma_dr_mp3_seek_proc onSeek, void* pUserData, ma_dr_mp3_config* pConfig, ma_uint64* pTotalFrameCount, const ma_allocation_callbacks* pAllocationCallbacks) +DRMP3_API drmp3_int16* drmp3_open_and_read_pcm_frames_s16(drmp3_read_proc onRead, drmp3_seek_proc onSeek, void* pUserData, drmp3_config* pConfig, drmp3_uint64* pTotalFrameCount, const drmp3_allocation_callbacks* pAllocationCallbacks) { - ma_dr_mp3 mp3; - if (!ma_dr_mp3_init(&mp3, onRead, onSeek, pUserData, pAllocationCallbacks)) { + drmp3 mp3; + if (!drmp3_init(&mp3, onRead, onSeek, pUserData, pAllocationCallbacks)) { return NULL; } - return ma_dr_mp3__full_read_and_close_s16(&mp3, pConfig, pTotalFrameCount); + return drmp3__full_read_and_close_s16(&mp3, pConfig, pTotalFrameCount); } -MA_API float* ma_dr_mp3_open_memory_and_read_pcm_frames_f32(const void* pData, size_t dataSize, ma_dr_mp3_config* pConfig, ma_uint64* pTotalFrameCount, const ma_allocation_callbacks* pAllocationCallbacks) +DRMP3_API float* drmp3_open_memory_and_read_pcm_frames_f32(const void* pData, size_t dataSize, drmp3_config* pConfig, drmp3_uint64* pTotalFrameCount, const drmp3_allocation_callbacks* pAllocationCallbacks) { - ma_dr_mp3 mp3; - if (!ma_dr_mp3_init_memory(&mp3, pData, dataSize, pAllocationCallbacks)) { + drmp3 mp3; + if (!drmp3_init_memory(&mp3, pData, dataSize, pAllocationCallbacks)) { return NULL; } - return ma_dr_mp3__full_read_and_close_f32(&mp3, pConfig, pTotalFrameCount); + return drmp3__full_read_and_close_f32(&mp3, pConfig, pTotalFrameCount); } -MA_API ma_int16* ma_dr_mp3_open_memory_and_read_pcm_frames_s16(const void* pData, size_t dataSize, ma_dr_mp3_config* pConfig, ma_uint64* pTotalFrameCount, const ma_allocation_callbacks* pAllocationCallbacks) +DRMP3_API drmp3_int16* drmp3_open_memory_and_read_pcm_frames_s16(const void* pData, size_t dataSize, drmp3_config* pConfig, drmp3_uint64* pTotalFrameCount, const drmp3_allocation_callbacks* pAllocationCallbacks) { - ma_dr_mp3 mp3; - if (!ma_dr_mp3_init_memory(&mp3, pData, dataSize, pAllocationCallbacks)) { + drmp3 mp3; + if (!drmp3_init_memory(&mp3, pData, dataSize, pAllocationCallbacks)) { return NULL; } - return ma_dr_mp3__full_read_and_close_s16(&mp3, pConfig, pTotalFrameCount); + return drmp3__full_read_and_close_s16(&mp3, pConfig, pTotalFrameCount); } -#ifndef MA_DR_MP3_NO_STDIO -MA_API float* ma_dr_mp3_open_file_and_read_pcm_frames_f32(const char* filePath, ma_dr_mp3_config* pConfig, ma_uint64* pTotalFrameCount, const ma_allocation_callbacks* pAllocationCallbacks) +#ifndef DR_MP3_NO_STDIO +DRMP3_API float* drmp3_open_file_and_read_pcm_frames_f32(const char* filePath, drmp3_config* pConfig, drmp3_uint64* pTotalFrameCount, const drmp3_allocation_callbacks* pAllocationCallbacks) { - ma_dr_mp3 mp3; - if (!ma_dr_mp3_init_file(&mp3, filePath, pAllocationCallbacks)) { + drmp3 mp3; + if (!drmp3_init_file(&mp3, filePath, pAllocationCallbacks)) { return NULL; } - return ma_dr_mp3__full_read_and_close_f32(&mp3, pConfig, pTotalFrameCount); + return drmp3__full_read_and_close_f32(&mp3, pConfig, pTotalFrameCount); } -MA_API ma_int16* ma_dr_mp3_open_file_and_read_pcm_frames_s16(const char* filePath, ma_dr_mp3_config* pConfig, ma_uint64* pTotalFrameCount, const ma_allocation_callbacks* pAllocationCallbacks) +DRMP3_API drmp3_int16* drmp3_open_file_and_read_pcm_frames_s16(const char* filePath, drmp3_config* pConfig, drmp3_uint64* pTotalFrameCount, const drmp3_allocation_callbacks* pAllocationCallbacks) { - ma_dr_mp3 mp3; - if (!ma_dr_mp3_init_file(&mp3, filePath, pAllocationCallbacks)) { + drmp3 mp3; + if (!drmp3_init_file(&mp3, filePath, pAllocationCallbacks)) { return NULL; } - return ma_dr_mp3__full_read_and_close_s16(&mp3, pConfig, pTotalFrameCount); + return drmp3__full_read_and_close_s16(&mp3, pConfig, pTotalFrameCount); } #endif -MA_API void* ma_dr_mp3_malloc(size_t sz, const ma_allocation_callbacks* pAllocationCallbacks) +DRMP3_API void* drmp3_malloc(size_t sz, const drmp3_allocation_callbacks* pAllocationCallbacks) { if (pAllocationCallbacks != NULL) { - return ma_dr_mp3__malloc_from_callbacks(sz, pAllocationCallbacks); + return drmp3__malloc_from_callbacks(sz, pAllocationCallbacks); } else { - return ma_dr_mp3__malloc_default(sz, NULL); + return drmp3__malloc_default(sz, NULL); } } -MA_API void ma_dr_mp3_free(void* p, const ma_allocation_callbacks* pAllocationCallbacks) +DRMP3_API void drmp3_free(void* p, const drmp3_allocation_callbacks* pAllocationCallbacks) { if (pAllocationCallbacks != NULL) { - ma_dr_mp3__free_from_callbacks(p, pAllocationCallbacks); + drmp3__free_from_callbacks(p, pAllocationCallbacks); } else { - ma_dr_mp3__free_default(p, NULL); + drmp3__free_default(p, NULL); } } #endif /* dr_mp3_c end */ -#endif /* MA_DR_MP3_IMPLEMENTATION */ +#endif /* DRMP3_IMPLEMENTATION */ #endif /* MA_NO_MP3 */ @@ -93449,7 +93360,7 @@ For more information, please refer to =============================================================================== ALTERNATIVE 2 - MIT No Attribution =============================================================================== -Copyright 2025 David Reid +Copyright 2023 David Reid 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 diff --git a/tests-cmake/pipeline/CMakeLists.txt b/tests-cmake/pipeline/CMakeLists.txt deleted file mode 100644 index 2b41a673cd..0000000000 --- a/tests-cmake/pipeline/CMakeLists.txt +++ /dev/null @@ -1,37 +0,0 @@ -cmake_minimum_required(VERSION 3.20) - - -# set the project name -project(pipeline) -set (CMAKE_CXX_STANDARD 11) -set (DCMAKE_CXX_FLAGS "-Werror") -set (CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -O0 -ldl -lpthread -lm") -set (CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} -O0 -ldl -lpthread -lm") - -# Emulator is not necessary for -DIS_MIN_DESKTOP -set(ADD_ARDUINO_EMULATOR OFF CACHE BOOL "Add Arduino Emulator Library") -set(ADD_PORTAUDIO OFF CACHE BOOL "No Portaudio") - - -# Build with arduino-audio-tools -if(CMAKE_SOURCE_DIR STREQUAL CMAKE_CURRENT_SOURCE_DIR) - add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/../.. ${CMAKE_CURRENT_BINARY_DIR}/arduino-audio-tools ) -endif() - -# Download miniaudio.h -file(DOWNLOAD https://raw.githubusercontent.com/mackron/miniaudio/master/miniaudio.h - ${CMAKE_CURRENT_SOURCE_DIR}/miniaudio.h) - - -# build sketch as executable -add_executable (pipeline pipeline.cpp) - -# set preprocessor defines -target_compile_definitions(pipeline PUBLIC -DIS_MIN_DESKTOP) - -# specify libraries -target_link_libraries(pipeline arduino-audio-tools) - -# access to miniaudio in sketch directory -target_include_directories(pipeline PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}) - diff --git a/tests-cmake/pipeline/miniaudio.h b/tests-cmake/pipeline/miniaudio.h deleted file mode 100644 index c74bebeb3c..0000000000 --- a/tests-cmake/pipeline/miniaudio.h +++ /dev/null @@ -1,93468 +0,0 @@ -/* -Audio playback and capture library. Choice of public domain or MIT-0. See license statements at the end of this file. -miniaudio - v0.11.22 - 2025-02-24 - -David Reid - mackron@gmail.com - -Website: https://miniaud.io -Documentation: https://miniaud.io/docs -GitHub: https://github.com/mackron/miniaudio -*/ - -/* -1. Introduction -=============== -To use miniaudio, include "miniaudio.h": - - ```c - #include "miniaudio.h" - ``` - -The implementation is contained in "miniaudio.c". Just compile this like any other source file. You -can include miniaudio.c if you want to compile your project as a single translation unit: - - ```c - #include "miniaudio.c" - ``` - -miniaudio includes both low level and high level APIs. The low level API is good for those who want -to do all of their mixing themselves and only require a light weight interface to the underlying -audio device. The high level API is good for those who have complex mixing and effect requirements. - -In miniaudio, objects are transparent structures. Unlike many other libraries, there are no handles -to opaque objects which means you need to allocate memory for objects yourself. In the examples -presented in this documentation you will often see objects declared on the stack. You need to be -careful when translating these examples to your own code so that you don't accidentally declare -your objects on the stack and then cause them to become invalid once the function returns. In -addition, you must ensure the memory address of your objects remain the same throughout their -lifetime. You therefore cannot be making copies of your objects. - -A config/init pattern is used throughout the entire library. The idea is that you set up a config -object and pass that into the initialization routine. The advantage to this system is that the -config object can be initialized with logical defaults and new properties added to it without -breaking the API. The config object can be allocated on the stack and does not need to be -maintained after initialization of the corresponding object. - - -1.1. Low Level API ------------------- -The low level API gives you access to the raw audio data of an audio device. It supports playback, -capture, full-duplex and loopback (WASAPI only). You can enumerate over devices to determine which -physical device(s) you want to connect to. - -The low level API uses the concept of a "device" as the abstraction for physical devices. The idea -is that you choose a physical device to emit or capture audio from, and then move data to/from the -device when miniaudio tells you to. Data is delivered to and from devices asynchronously via a -callback which you specify when initializing the device. - -When initializing the device you first need to configure it. The device configuration allows you to -specify things like the format of the data delivered via the callback, the size of the internal -buffer and the ID of the device you want to emit or capture audio from. - -Once you have the device configuration set up you can initialize the device. When initializing a -device you need to allocate memory for the device object beforehand. This gives the application -complete control over how the memory is allocated. In the example below we initialize a playback -device on the stack, but you could allocate it on the heap if that suits your situation better. - - ```c - void data_callback(ma_device* pDevice, void* pOutput, const void* pInput, ma_uint32 frameCount) - { - // In playback mode copy data to pOutput. In capture mode read data from pInput. In full-duplex mode, both - // pOutput and pInput will be valid and you can move data from pInput into pOutput. Never process more than - // frameCount frames. - } - - int main() - { - ma_device_config config = ma_device_config_init(ma_device_type_playback); - config.playback.format = ma_format_f32; // Set to ma_format_unknown to use the device's native format. - config.playback.channels = 2; // Set to 0 to use the device's native channel count. - config.sampleRate = 48000; // Set to 0 to use the device's native sample rate. - config.dataCallback = data_callback; // This function will be called when miniaudio needs more data. - config.pUserData = pMyCustomData; // Can be accessed from the device object (device.pUserData). - - ma_device device; - if (ma_device_init(NULL, &config, &device) != MA_SUCCESS) { - return -1; // Failed to initialize the device. - } - - ma_device_start(&device); // The device is sleeping by default so you'll need to start it manually. - - // Do something here. Probably your program's main loop. - - ma_device_uninit(&device); - return 0; - } - ``` - -In the example above, `data_callback()` is where audio data is written and read from the device. -The idea is in playback mode you cause sound to be emitted from the speakers by writing audio data -to the output buffer (`pOutput` in the example). In capture mode you read data from the input -buffer (`pInput`) to extract sound captured by the microphone. The `frameCount` parameter tells you -how many frames can be written to the output buffer and read from the input buffer. A "frame" is -one sample for each channel. For example, in a stereo stream (2 channels), one frame is 2 -samples: one for the left, one for the right. The channel count is defined by the device config. -The size in bytes of an individual sample is defined by the sample format which is also specified -in the device config. Multi-channel audio data is always interleaved, which means the samples for -each frame are stored next to each other in memory. For example, in a stereo stream the first pair -of samples will be the left and right samples for the first frame, the second pair of samples will -be the left and right samples for the second frame, etc. - -The configuration of the device is defined by the `ma_device_config` structure. The config object -is always initialized with `ma_device_config_init()`. It's important to always initialize the -config with this function as it initializes it with logical defaults and ensures your program -doesn't break when new members are added to the `ma_device_config` structure. The example above -uses a fairly simple and standard device configuration. The call to `ma_device_config_init()` takes -a single parameter, which is whether or not the device is a playback, capture, duplex or loopback -device (loopback devices are not supported on all backends). The `config.playback.format` member -sets the sample format which can be one of the following (all formats are native-endian): - - +---------------+----------------------------------------+---------------------------+ - | Symbol | Description | Range | - +---------------+----------------------------------------+---------------------------+ - | ma_format_f32 | 32-bit floating point | [-1, 1] | - | ma_format_s16 | 16-bit signed integer | [-32768, 32767] | - | ma_format_s24 | 24-bit signed integer (tightly packed) | [-8388608, 8388607] | - | ma_format_s32 | 32-bit signed integer | [-2147483648, 2147483647] | - | ma_format_u8 | 8-bit unsigned integer | [0, 255] | - +---------------+----------------------------------------+---------------------------+ - -The `config.playback.channels` member sets the number of channels to use with the device. The -channel count cannot exceed MA_MAX_CHANNELS. The `config.sampleRate` member sets the sample rate -(which must be the same for both playback and capture in full-duplex configurations). This is -usually set to 44100 or 48000, but can be set to anything. It's recommended to keep this between -8000 and 384000, however. - -Note that leaving the format, channel count and/or sample rate at their default values will result -in the internal device's native configuration being used which is useful if you want to avoid the -overhead of miniaudio's automatic data conversion. - -In addition to the sample format, channel count and sample rate, the data callback and user data -pointer are also set via the config. The user data pointer is not passed into the callback as a -parameter, but is instead set to the `pUserData` member of `ma_device` which you can access -directly since all miniaudio structures are transparent. - -Initializing the device is done with `ma_device_init()`. This will return a result code telling you -what went wrong, if anything. On success it will return `MA_SUCCESS`. After initialization is -complete the device will be in a stopped state. To start it, use `ma_device_start()`. -Uninitializing the device will stop it, which is what the example above does, but you can also stop -the device with `ma_device_stop()`. To resume the device simply call `ma_device_start()` again. -Note that it's important to never stop or start the device from inside the callback. This will -result in a deadlock. Instead you set a variable or signal an event indicating that the device -needs to stop and handle it in a different thread. The following APIs must never be called inside -the callback: - - ```c - ma_device_init() - ma_device_init_ex() - ma_device_uninit() - ma_device_start() - ma_device_stop() - ``` - -You must never try uninitializing and reinitializing a device inside the callback. You must also -never try to stop and start it from inside the callback. There are a few other things you shouldn't -do in the callback depending on your requirements, however this isn't so much a thread-safety -thing, but rather a real-time processing thing which is beyond the scope of this introduction. - -The example above demonstrates the initialization of a playback device, but it works exactly the -same for capture. All you need to do is change the device type from `ma_device_type_playback` to -`ma_device_type_capture` when setting up the config, like so: - - ```c - ma_device_config config = ma_device_config_init(ma_device_type_capture); - config.capture.format = MY_FORMAT; - config.capture.channels = MY_CHANNEL_COUNT; - ``` - -In the data callback you just read from the input buffer (`pInput` in the example above) and leave -the output buffer alone (it will be set to NULL when the device type is set to -`ma_device_type_capture`). - -These are the available device types and how you should handle the buffers in the callback: - - +-------------------------+--------------------------------------------------------+ - | Device Type | Callback Behavior | - +-------------------------+--------------------------------------------------------+ - | ma_device_type_playback | Write to output buffer, leave input buffer untouched. | - | ma_device_type_capture | Read from input buffer, leave output buffer untouched. | - | ma_device_type_duplex | Read from input buffer, write to output buffer. | - | ma_device_type_loopback | Read from input buffer, leave output buffer untouched. | - +-------------------------+--------------------------------------------------------+ - -You will notice in the example above that the sample format and channel count is specified -separately for playback and capture. This is to support different data formats between the playback -and capture devices in a full-duplex system. An example may be that you want to capture audio data -as a monaural stream (one channel), but output sound to a stereo speaker system. Note that if you -use different formats between playback and capture in a full-duplex configuration you will need to -convert the data yourself. There are functions available to help you do this which will be -explained later. - -The example above did not specify a physical device to connect to which means it will use the -operating system's default device. If you have multiple physical devices connected and you want to -use a specific one you will need to specify the device ID in the configuration, like so: - - ```c - config.playback.pDeviceID = pMyPlaybackDeviceID; // Only if requesting a playback or duplex device. - config.capture.pDeviceID = pMyCaptureDeviceID; // Only if requesting a capture, duplex or loopback device. - ``` - -To retrieve the device ID you will need to perform device enumeration, however this requires the -use of a new concept called the "context". Conceptually speaking the context sits above the device. -There is one context to many devices. The purpose of the context is to represent the backend at a -more global level and to perform operations outside the scope of an individual device. Mainly it is -used for performing run-time linking against backend libraries, initializing backends and -enumerating devices. The example below shows how to enumerate devices. - - ```c - ma_context context; - if (ma_context_init(NULL, 0, NULL, &context) != MA_SUCCESS) { - // Error. - } - - ma_device_info* pPlaybackInfos; - ma_uint32 playbackCount; - ma_device_info* pCaptureInfos; - ma_uint32 captureCount; - if (ma_context_get_devices(&context, &pPlaybackInfos, &playbackCount, &pCaptureInfos, &captureCount) != MA_SUCCESS) { - // Error. - } - - // Loop over each device info and do something with it. Here we just print the name with their index. You may want - // to give the user the opportunity to choose which device they'd prefer. - for (ma_uint32 iDevice = 0; iDevice < playbackCount; iDevice += 1) { - printf("%d - %s\n", iDevice, pPlaybackInfos[iDevice].name); - } - - ma_device_config config = ma_device_config_init(ma_device_type_playback); - config.playback.pDeviceID = &pPlaybackInfos[chosenPlaybackDeviceIndex].id; - config.playback.format = MY_FORMAT; - config.playback.channels = MY_CHANNEL_COUNT; - config.sampleRate = MY_SAMPLE_RATE; - config.dataCallback = data_callback; - config.pUserData = pMyCustomData; - - ma_device device; - if (ma_device_init(&context, &config, &device) != MA_SUCCESS) { - // Error - } - - ... - - ma_device_uninit(&device); - ma_context_uninit(&context); - ``` - -The first thing we do in this example is initialize a `ma_context` object with `ma_context_init()`. -The first parameter is a pointer to a list of `ma_backend` values which are used to override the -default backend priorities. When this is NULL, as in this example, miniaudio's default priorities -are used. The second parameter is the number of backends listed in the array pointed to by the -first parameter. The third parameter is a pointer to a `ma_context_config` object which can be -NULL, in which case defaults are used. The context configuration is used for setting the logging -callback, custom memory allocation callbacks, user-defined data and some backend-specific -configurations. - -Once the context has been initialized you can enumerate devices. In the example above we use the -simpler `ma_context_get_devices()`, however you can also use a callback for handling devices by -using `ma_context_enumerate_devices()`. When using `ma_context_get_devices()` you provide a pointer -to a pointer that will, upon output, be set to a pointer to a buffer containing a list of -`ma_device_info` structures. You also provide a pointer to an unsigned integer that will receive -the number of items in the returned buffer. Do not free the returned buffers as their memory is -managed internally by miniaudio. - -The `ma_device_info` structure contains an `id` member which is the ID you pass to the device -config. It also contains the name of the device which is useful for presenting a list of devices -to the user via the UI. - -When creating your own context you will want to pass it to `ma_device_init()` when initializing the -device. Passing in NULL, like we do in the first example, will result in miniaudio creating the -context for you, which you don't want to do since you've already created a context. Note that -internally the context is only tracked by it's pointer which means you must not change the location -of the `ma_context` object. If this is an issue, consider using `malloc()` to allocate memory for -the context. - - -1.2. High Level API -------------------- -The high level API consists of three main parts: - - * Resource management for loading and streaming sounds. - * A node graph for advanced mixing and effect processing. - * A high level "engine" that wraps around the resource manager and node graph. - -The resource manager (`ma_resource_manager`) is used for loading sounds. It supports loading sounds -fully into memory and also streaming. It will also deal with reference counting for you which -avoids the same sound being loaded multiple times. - -The node graph is used for mixing and effect processing. The idea is that you connect a number of -nodes into the graph by connecting each node's outputs to another node's inputs. Each node can -implement its own effect. By chaining nodes together, advanced mixing and effect processing can -be achieved. - -The engine encapsulates both the resource manager and the node graph to create a simple, easy to -use high level API. The resource manager and node graph APIs are covered in more later sections of -this manual. - -The code below shows how you can initialize an engine using it's default configuration. - - ```c - ma_result result; - ma_engine engine; - - result = ma_engine_init(NULL, &engine); - if (result != MA_SUCCESS) { - return result; // Failed to initialize the engine. - } - ``` - -This creates an engine instance which will initialize a device internally which you can access with -`ma_engine_get_device()`. It will also initialize a resource manager for you which can be accessed -with `ma_engine_get_resource_manager()`. The engine itself is a node graph (`ma_node_graph`) which -means you can pass a pointer to the engine object into any of the `ma_node_graph` APIs (with a -cast). Alternatively, you can use `ma_engine_get_node_graph()` instead of a cast. - -Note that all objects in miniaudio, including the `ma_engine` object in the example above, are -transparent structures. There are no handles to opaque structures in miniaudio which means you need -to be mindful of how you declare them. In the example above we are declaring it on the stack, but -this will result in the struct being invalidated once the function encapsulating it returns. If -allocating the engine on the heap is more appropriate, you can easily do so with a standard call -to `malloc()` or whatever heap allocation routine you like: - - ```c - ma_engine* pEngine = malloc(sizeof(*pEngine)); - ``` - -The `ma_engine` API uses the same config/init pattern used all throughout miniaudio. To configure -an engine, you can fill out a `ma_engine_config` object and pass it into the first parameter of -`ma_engine_init()`: - - ```c - ma_result result; - ma_engine engine; - ma_engine_config engineConfig; - - engineConfig = ma_engine_config_init(); - engineConfig.pResourceManager = &myCustomResourceManager; // <-- Initialized as some earlier stage. - - result = ma_engine_init(&engineConfig, &engine); - if (result != MA_SUCCESS) { - return result; - } - ``` - -This creates an engine instance using a custom config. In this particular example it's showing how -you can specify a custom resource manager rather than having the engine initialize one internally. -This is particularly useful if you want to have multiple engine's share the same resource manager. - -The engine must be uninitialized with `ma_engine_uninit()` when it's no longer needed. - -By default the engine will be started, but nothing will be playing because no sounds have been -initialized. The easiest but least flexible way of playing a sound is like so: - - ```c - ma_engine_play_sound(&engine, "my_sound.wav", NULL); - ``` - -This plays what miniaudio calls an "inline" sound. It plays the sound once, and then puts the -internal sound up for recycling. The last parameter is used to specify which sound group the sound -should be associated with which will be explained later. This particular way of playing a sound is -simple, but lacks flexibility and features. A more flexible way of playing a sound is to first -initialize a sound: - - ```c - ma_result result; - ma_sound sound; - - result = ma_sound_init_from_file(&engine, "my_sound.wav", 0, NULL, NULL, &sound); - if (result != MA_SUCCESS) { - return result; - } - - ma_sound_start(&sound); - ``` - -This returns a `ma_sound` object which represents a single instance of the specified sound file. If -you want to play the same file multiple times simultaneously, you need to create one sound for each -instance. - -Sounds should be uninitialized with `ma_sound_uninit()`. - -Sounds are not started by default. Start a sound with `ma_sound_start()` and stop it with -`ma_sound_stop()`. When a sound is stopped, it is not rewound to the start. Use -`ma_sound_seek_to_pcm_frame(&sound, 0)` to seek back to the start of a sound. By default, starting -and stopping sounds happens immediately, but sometimes it might be convenient to schedule the sound -the be started and/or stopped at a specific time. This can be done with the following functions: - - ```c - ma_sound_set_start_time_in_pcm_frames() - ma_sound_set_start_time_in_milliseconds() - ma_sound_set_stop_time_in_pcm_frames() - ma_sound_set_stop_time_in_milliseconds() - ``` - -The start/stop time needs to be specified based on the absolute timer which is controlled by the -engine. The current global time in PCM frames can be retrieved with -`ma_engine_get_time_in_pcm_frames()`. The engine's global time can be changed with -`ma_engine_set_time_in_pcm_frames()` for synchronization purposes if required. Note that scheduling -a start time still requires an explicit call to `ma_sound_start()` before anything will play: - - ```c - ma_sound_set_start_time_in_pcm_frames(&sound, ma_engine_get_time_in_pcm_frames(&engine) + (ma_engine_get_sample_rate(&engine) * 2); - ma_sound_start(&sound); - ``` - -The third parameter of `ma_sound_init_from_file()` is a set of flags that control how the sound be -loaded and a few options on which features should be enabled for that sound. By default, the sound -is synchronously loaded fully into memory straight from the file system without any kind of -decoding. If you want to decode the sound before storing it in memory, you need to specify the -`MA_SOUND_FLAG_DECODE` flag. This is useful if you want to incur the cost of decoding at an earlier -stage, such as a loading stage. Without this option, decoding will happen dynamically at mixing -time which might be too expensive on the audio thread. - -If you want to load the sound asynchronously, you can specify the `MA_SOUND_FLAG_ASYNC` flag. This -will result in `ma_sound_init_from_file()` returning quickly, but the sound will not start playing -until the sound has had some audio decoded. - -The fourth parameter is a pointer to sound group. A sound group is used as a mechanism to organise -sounds into groups which have their own effect processing and volume control. An example is a game -which might have separate groups for sfx, voice and music. Each of these groups have their own -independent volume control. Use `ma_sound_group_init()` or `ma_sound_group_init_ex()` to initialize -a sound group. - -Sounds and sound groups are nodes in the engine's node graph and can be plugged into any `ma_node` -API. This makes it possible to connect sounds and sound groups to effect nodes to produce complex -effect chains. - -A sound can have its volume changed with `ma_sound_set_volume()`. If you prefer decibel volume -control you can use `ma_volume_db_to_linear()` to convert from decibel representation to linear. - -Panning and pitching is supported with `ma_sound_set_pan()` and `ma_sound_set_pitch()`. If you know -a sound will never have its pitch changed with `ma_sound_set_pitch()` or via the doppler effect, -you can specify the `MA_SOUND_FLAG_NO_PITCH` flag when initializing the sound for an optimization. - -By default, sounds and sound groups have spatialization enabled. If you don't ever want to -spatialize your sounds, initialize the sound with the `MA_SOUND_FLAG_NO_SPATIALIZATION` flag. The -spatialization model is fairly simple and is roughly on feature parity with OpenAL. HRTF and -environmental occlusion are not currently supported, but planned for the future. The supported -features include: - - * Sound and listener positioning and orientation with cones - * Attenuation models: none, inverse, linear and exponential - * Doppler effect - -Sounds can be faded in and out with `ma_sound_set_fade_in_pcm_frames()`. - -To check if a sound is currently playing, you can use `ma_sound_is_playing()`. To check if a sound -is at the end, use `ma_sound_at_end()`. Looping of a sound can be controlled with -`ma_sound_set_looping()`. Use `ma_sound_is_looping()` to check whether or not the sound is looping. - - - -2. Building -=========== -miniaudio should work cleanly out of the box without the need to download or install any -dependencies. See below for platform-specific details. - -Note that GCC and Clang require `-msse2`, `-mavx2`, etc. for SIMD optimizations. - -If you get errors about undefined references to `__sync_val_compare_and_swap_8`, `__atomic_load_8`, -etc. you need to link with `-latomic`. - - -2.1. Windows ------------- -The Windows build should compile cleanly on all popular compilers without the need to configure any -include paths nor link to any libraries. - -The UWP build may require linking to mmdevapi.lib if you get errors about an unresolved external -symbol for `ActivateAudioInterfaceAsync()`. - - -2.2. macOS and iOS ------------------- -The macOS build should compile cleanly without the need to download any dependencies nor link to -any libraries or frameworks. The iOS build needs to be compiled as Objective-C and will need to -link the relevant frameworks but should compile cleanly out of the box with Xcode. Compiling -through the command line requires linking to `-lpthread` and `-lm`. - -Due to the way miniaudio links to frameworks at runtime, your application may not pass Apple's -notarization process. To fix this there are two options. The first is to compile with -`-DMA_NO_RUNTIME_LINKING` which in turn will require linking with -`-framework CoreFoundation -framework CoreAudio -framework AudioToolbox`. If you get errors about -AudioToolbox, try with `-framework AudioUnit` instead. You may get this when using older versions -of iOS. Alternatively, if you would rather keep using runtime linking you can add the following to -your entitlements.xcent file: - - ``` - com.apple.security.cs.allow-dyld-environment-variables - - com.apple.security.cs.allow-unsigned-executable-memory - - ``` - -See this discussion for more info: https://github.com/mackron/miniaudio/issues/203. - - -2.3. Linux ----------- -The Linux build only requires linking to `-ldl`, `-lpthread` and `-lm`. You do not need any -development packages. You may need to link with `-latomic` if you're compiling for 32-bit ARM. - - -2.4. BSD --------- -The BSD build only requires linking to `-lpthread` and `-lm`. NetBSD uses audio(4), OpenBSD uses -sndio and FreeBSD uses OSS. You may need to link with `-latomic` if you're compiling for 32-bit -ARM. - - -2.5. Android ------------- -AAudio is the highest priority backend on Android. This should work out of the box without needing -any kind of compiler configuration. Support for AAudio starts with Android 8 which means older -versions will fall back to OpenSL|ES which requires API level 16+. - -There have been reports that the OpenSL|ES backend fails to initialize on some Android based -devices due to `dlopen()` failing to open "libOpenSLES.so". If this happens on your platform -you'll need to disable run-time linking with `MA_NO_RUNTIME_LINKING` and link with -lOpenSLES. - - -2.6. Emscripten ---------------- -The Emscripten build emits Web Audio JavaScript directly and should compile cleanly out of the box. -You cannot use `-std=c*` compiler flags, nor `-ansi`. - -You can enable the use of AudioWorkets by defining `MA_ENABLE_AUDIO_WORKLETS` and then compiling -with the following options: - - -sAUDIO_WORKLET=1 -sWASM_WORKERS=1 -sASYNCIFY - -An example for compiling with AudioWorklet support might look like this: - - emcc program.c -o bin/program.html -DMA_ENABLE_AUDIO_WORKLETS -sAUDIO_WORKLET=1 -sWASM_WORKERS=1 -sASYNCIFY - -To run locally, you'll need to use emrun: - - emrun bin/program.html - - - -2.7. Build Options ------------------- -`#define` these options before including miniaudio.c, or pass them as compiler flags: - - +----------------------------------+--------------------------------------------------------------------+ - | Option | Description | - +----------------------------------+--------------------------------------------------------------------+ - | MA_NO_WASAPI | Disables the WASAPI backend. | - +----------------------------------+--------------------------------------------------------------------+ - | MA_NO_DSOUND | Disables the DirectSound backend. | - +----------------------------------+--------------------------------------------------------------------+ - | MA_NO_WINMM | Disables the WinMM backend. | - +----------------------------------+--------------------------------------------------------------------+ - | MA_NO_ALSA | Disables the ALSA backend. | - +----------------------------------+--------------------------------------------------------------------+ - | MA_NO_PULSEAUDIO | Disables the PulseAudio backend. | - +----------------------------------+--------------------------------------------------------------------+ - | MA_NO_JACK | Disables the JACK backend. | - +----------------------------------+--------------------------------------------------------------------+ - | MA_NO_COREAUDIO | Disables the Core Audio backend. | - +----------------------------------+--------------------------------------------------------------------+ - | MA_NO_SNDIO | Disables the sndio backend. | - +----------------------------------+--------------------------------------------------------------------+ - | MA_NO_AUDIO4 | Disables the audio(4) backend. | - +----------------------------------+--------------------------------------------------------------------+ - | MA_NO_OSS | Disables the OSS backend. | - +----------------------------------+--------------------------------------------------------------------+ - | MA_NO_AAUDIO | Disables the AAudio backend. | - +----------------------------------+--------------------------------------------------------------------+ - | MA_NO_OPENSL | Disables the OpenSL|ES backend. | - +----------------------------------+--------------------------------------------------------------------+ - | MA_NO_WEBAUDIO | Disables the Web Audio backend. | - +----------------------------------+--------------------------------------------------------------------+ - | MA_NO_CUSTOM | Disables support for custom backends. | - +----------------------------------+--------------------------------------------------------------------+ - | MA_NO_NULL | Disables the null backend. | - +----------------------------------+--------------------------------------------------------------------+ - | MA_ENABLE_ONLY_SPECIFIC_BACKENDS | Disables all backends by default and requires `MA_ENABLE_*` to | - | | enable specific backends. | - +----------------------------------+--------------------------------------------------------------------+ - | MA_ENABLE_WASAPI | Used in conjunction with MA_ENABLE_ONLY_SPECIFIC_BACKENDS to | - | | enable the WASAPI backend. | - +----------------------------------+--------------------------------------------------------------------+ - | MA_ENABLE_DSOUND | Used in conjunction with MA_ENABLE_ONLY_SPECIFIC_BACKENDS to | - | | enable the DirectSound backend. | - +----------------------------------+--------------------------------------------------------------------+ - | MA_ENABLE_WINMM | Used in conjunction with MA_ENABLE_ONLY_SPECIFIC_BACKENDS to | - | | enable the WinMM backend. | - +----------------------------------+--------------------------------------------------------------------+ - | MA_ENABLE_ALSA | Used in conjunction with MA_ENABLE_ONLY_SPECIFIC_BACKENDS to | - | | enable the ALSA backend. | - +----------------------------------+--------------------------------------------------------------------+ - | MA_ENABLE_PULSEAUDIO | Used in conjunction with MA_ENABLE_ONLY_SPECIFIC_BACKENDS to | - | | enable the PulseAudio backend. | - +----------------------------------+--------------------------------------------------------------------+ - | MA_ENABLE_JACK | Used in conjunction with MA_ENABLE_ONLY_SPECIFIC_BACKENDS to | - | | enable the JACK backend. | - +----------------------------------+--------------------------------------------------------------------+ - | MA_ENABLE_COREAUDIO | Used in conjunction with MA_ENABLE_ONLY_SPECIFIC_BACKENDS to | - | | enable the Core Audio backend. | - +----------------------------------+--------------------------------------------------------------------+ - | MA_ENABLE_SNDIO | Used in conjunction with MA_ENABLE_ONLY_SPECIFIC_BACKENDS to | - | | enable the sndio backend. | - +----------------------------------+--------------------------------------------------------------------+ - | MA_ENABLE_AUDIO4 | Used in conjunction with MA_ENABLE_ONLY_SPECIFIC_BACKENDS to | - | | enable the audio(4) backend. | - +----------------------------------+--------------------------------------------------------------------+ - | MA_ENABLE_OSS | Used in conjunction with MA_ENABLE_ONLY_SPECIFIC_BACKENDS to | - | | enable the OSS backend. | - +----------------------------------+--------------------------------------------------------------------+ - | MA_ENABLE_AAUDIO | Used in conjunction with MA_ENABLE_ONLY_SPECIFIC_BACKENDS to | - | | enable the AAudio backend. | - +----------------------------------+--------------------------------------------------------------------+ - | MA_ENABLE_OPENSL | Used in conjunction with MA_ENABLE_ONLY_SPECIFIC_BACKENDS to | - | | enable the OpenSL|ES backend. | - +----------------------------------+--------------------------------------------------------------------+ - | MA_ENABLE_WEBAUDIO | Used in conjunction with MA_ENABLE_ONLY_SPECIFIC_BACKENDS to | - | | enable the Web Audio backend. | - +----------------------------------+--------------------------------------------------------------------+ - | MA_ENABLE_CUSTOM | Used in conjunction with MA_ENABLE_ONLY_SPECIFIC_BACKENDS to | - | | enable custom backends. | - +----------------------------------+--------------------------------------------------------------------+ - | MA_ENABLE_NULL | Used in conjunction with MA_ENABLE_ONLY_SPECIFIC_BACKENDS to | - | | enable the null backend. | - +----------------------------------+--------------------------------------------------------------------+ - | MA_NO_DECODING | Disables decoding APIs. | - +----------------------------------+--------------------------------------------------------------------+ - | MA_NO_ENCODING | Disables encoding APIs. | - +----------------------------------+--------------------------------------------------------------------+ - | MA_NO_WAV | Disables the built-in WAV decoder and encoder. | - +----------------------------------+--------------------------------------------------------------------+ - | MA_NO_FLAC | Disables the built-in FLAC decoder. | - +----------------------------------+--------------------------------------------------------------------+ - | MA_NO_MP3 | Disables the built-in MP3 decoder. | - +----------------------------------+--------------------------------------------------------------------+ - | MA_NO_DEVICE_IO | Disables playback and recording. This will disable `ma_context` | - | | and `ma_device` APIs. This is useful if you only want to use | - | | miniaudio's data conversion and/or decoding APIs. | - +----------------------------------+--------------------------------------------------------------------+ - | MA_NO_RESOURCE_MANAGER | Disables the resource manager. When using the engine this will | - | | also disable the following functions: | - | | | - | | ``` | - | | ma_sound_init_from_file() | - | | ma_sound_init_from_file_w() | - | | ma_sound_init_copy() | - | | ma_engine_play_sound_ex() | - | | ma_engine_play_sound() | - | | ``` | - | | | - | | The only way to initialize a `ma_sound` object is to initialize it | - | | from a data source. | - +----------------------------------+--------------------------------------------------------------------+ - | MA_NO_NODE_GRAPH | Disables the node graph API. This will also disable the engine API | - | | because it depends on the node graph. | - +----------------------------------+--------------------------------------------------------------------+ - | MA_NO_ENGINE | Disables the engine API. | - +----------------------------------+--------------------------------------------------------------------+ - | MA_NO_THREADING | Disables the `ma_thread`, `ma_mutex`, `ma_semaphore` and | - | | `ma_event` APIs. This option is useful if you only need to use | - | | miniaudio for data conversion, decoding and/or encoding. Some | - | | families of APIs require threading which means the following | - | | options must also be set: | - | | | - | | ``` | - | | MA_NO_DEVICE_IO | - | | ``` | - +----------------------------------+--------------------------------------------------------------------+ - | MA_NO_GENERATION | Disables generation APIs such a `ma_waveform` and `ma_noise`. | - +----------------------------------+--------------------------------------------------------------------+ - | MA_NO_SSE2 | Disables SSE2 optimizations. | - +----------------------------------+--------------------------------------------------------------------+ - | MA_NO_AVX2 | Disables AVX2 optimizations. | - +----------------------------------+--------------------------------------------------------------------+ - | MA_NO_NEON | Disables NEON optimizations. | - +----------------------------------+--------------------------------------------------------------------+ - | MA_NO_RUNTIME_LINKING | Disables runtime linking. This is useful for passing Apple's | - | | notarization process. When enabling this, you may need to avoid | - | | using `-std=c89` or `-std=c99` on Linux builds or else you may end | - | | up with compilation errors due to conflicts with `timespec` and | - | | `timeval` data types. | - | | | - | | You may need to enable this if your target platform does not allow | - | | runtime linking via `dlopen()`. | - +----------------------------------+--------------------------------------------------------------------+ - | MA_USE_STDINT | (Pass this in as a compiler flag. Do not `#define` this before | - | | miniaudio.c) Forces the use of stdint.h for sized types. | - +----------------------------------+--------------------------------------------------------------------+ - | MA_DEBUG_OUTPUT | Enable `printf()` output of debug logs (`MA_LOG_LEVEL_DEBUG`). | - +----------------------------------+--------------------------------------------------------------------+ - | MA_COINIT_VALUE | Windows only. The value to pass to internal calls to | - | | `CoInitializeEx()`. Defaults to `COINIT_MULTITHREADED`. | - +----------------------------------+--------------------------------------------------------------------+ - | MA_FORCE_UWP | Windows only. Affects only the WASAPI backend. Will force the | - | | WASAPI backend to use the UWP code path instead of the regular | - | | desktop path. This is normally auto-detected and should rarely be | - | | needed to be used explicitly, but can be useful for debugging. | - +----------------------------------+--------------------------------------------------------------------+ - | MA_ON_THREAD_ENTRY | Defines some code that will be executed as soon as an internal | - | | miniaudio-managed thread is created. This will be the first thing | - | | to be executed by the thread entry point. | - +----------------------------------+--------------------------------------------------------------------+ - | MA_ON_THREAD_EXIT | Defines some code that will be executed from the entry point of an | - | | internal miniaudio-managed thread upon exit. This will be the last | - | | thing to be executed before the thread's entry point exits. | - +----------------------------------+--------------------------------------------------------------------+ - | MA_THREAD_DEFAULT_STACK_SIZE | If set, specifies the default stack size used by miniaudio-managed | - | | threads. | - +----------------------------------+--------------------------------------------------------------------+ - | MA_API | Controls how public APIs should be decorated. Default is `extern`. | - +----------------------------------+--------------------------------------------------------------------+ - - -3. Definitions -============== -This section defines common terms used throughout miniaudio. Unfortunately there is often ambiguity -in the use of terms throughout the audio space, so this section is intended to clarify how miniaudio -uses each term. - -3.1. Sample ------------ -A sample is a single unit of audio data. If the sample format is f32, then one sample is one 32-bit -floating point number. - -3.2. Frame / PCM Frame ----------------------- -A frame is a group of samples equal to the number of channels. For a stereo stream a frame is 2 -samples, a mono frame is 1 sample, a 5.1 surround sound frame is 6 samples, etc. The terms "frame" -and "PCM frame" are the same thing in miniaudio. Note that this is different to a compressed frame. -If ever miniaudio needs to refer to a compressed frame, such as a FLAC frame, it will always -clarify what it's referring to with something like "FLAC frame". - -3.3. Channel ------------- -A stream of monaural audio that is emitted from an individual speaker in a speaker system, or -received from an individual microphone in a microphone system. A stereo stream has two channels (a -left channel, and a right channel), a 5.1 surround sound system has 6 channels, etc. Some audio -systems refer to a channel as a complex audio stream that's mixed with other channels to produce -the final mix - this is completely different to miniaudio's use of the term "channel" and should -not be confused. - -3.4. Sample Rate ----------------- -The sample rate in miniaudio is always expressed in Hz, such as 44100, 48000, etc. It's the number -of PCM frames that are processed per second. - -3.5. Formats ------------- -Throughout miniaudio you will see references to different sample formats: - - +---------------+----------------------------------------+---------------------------+ - | Symbol | Description | Range | - +---------------+----------------------------------------+---------------------------+ - | ma_format_f32 | 32-bit floating point | [-1, 1] | - | ma_format_s16 | 16-bit signed integer | [-32768, 32767] | - | ma_format_s24 | 24-bit signed integer (tightly packed) | [-8388608, 8388607] | - | ma_format_s32 | 32-bit signed integer | [-2147483648, 2147483647] | - | ma_format_u8 | 8-bit unsigned integer | [0, 255] | - +---------------+----------------------------------------+---------------------------+ - -All formats are native-endian. - - - -4. Data Sources -=============== -The data source abstraction in miniaudio is used for retrieving audio data from some source. A few -examples include `ma_decoder`, `ma_noise` and `ma_waveform`. You will need to be familiar with data -sources in order to make sense of some of the higher level concepts in miniaudio. - -The `ma_data_source` API is a generic interface for reading from a data source. Any object that -implements the data source interface can be plugged into any `ma_data_source` function. - -To read data from a data source: - - ```c - ma_result result; - ma_uint64 framesRead; - - result = ma_data_source_read_pcm_frames(pDataSource, pFramesOut, frameCount, &framesRead); - if (result != MA_SUCCESS) { - return result; // Failed to read data from the data source. - } - ``` - -If you don't need the number of frames that were successfully read you can pass in `NULL` to the -`pFramesRead` parameter. If this returns a value less than the number of frames requested it means -the end of the file has been reached. `MA_AT_END` will be returned only when the number of frames -read is 0. - -When calling any data source function, with the exception of `ma_data_source_init()` and -`ma_data_source_uninit()`, you can pass in any object that implements a data source. For example, -you could plug in a decoder like so: - - ```c - ma_result result; - ma_uint64 framesRead; - ma_decoder decoder; // <-- This would be initialized with `ma_decoder_init_*()`. - - result = ma_data_source_read_pcm_frames(&decoder, pFramesOut, frameCount, &framesRead); - if (result != MA_SUCCESS) { - return result; // Failed to read data from the decoder. - } - ``` - -If you want to seek forward you can pass in `NULL` to the `pFramesOut` parameter. Alternatively you -can use `ma_data_source_seek_pcm_frames()`. - -To seek to a specific PCM frame: - - ```c - result = ma_data_source_seek_to_pcm_frame(pDataSource, frameIndex); - if (result != MA_SUCCESS) { - return result; // Failed to seek to PCM frame. - } - ``` - -You can retrieve the total length of a data source in PCM frames, but note that some data sources -may not have the notion of a length, such as noise and waveforms, and others may just not have a -way of determining the length such as some decoders. To retrieve the length: - - ```c - ma_uint64 length; - - result = ma_data_source_get_length_in_pcm_frames(pDataSource, &length); - if (result != MA_SUCCESS) { - return result; // Failed to retrieve the length. - } - ``` - -Care should be taken when retrieving the length of a data source where the underlying decoder is -pulling data from a data stream with an undefined length, such as internet radio or some kind of -broadcast. If you do this, `ma_data_source_get_length_in_pcm_frames()` may never return. - -The current position of the cursor in PCM frames can also be retrieved: - - ```c - ma_uint64 cursor; - - result = ma_data_source_get_cursor_in_pcm_frames(pDataSource, &cursor); - if (result != MA_SUCCESS) { - return result; // Failed to retrieve the cursor. - } - ``` - -You will often need to know the data format that will be returned after reading. This can be -retrieved like so: - - ```c - ma_format format; - ma_uint32 channels; - ma_uint32 sampleRate; - ma_channel channelMap[MA_MAX_CHANNELS]; - - result = ma_data_source_get_data_format(pDataSource, &format, &channels, &sampleRate, channelMap, MA_MAX_CHANNELS); - if (result != MA_SUCCESS) { - return result; // Failed to retrieve data format. - } - ``` - -If you do not need a specific data format property, just pass in NULL to the respective parameter. - -There may be cases where you want to implement something like a sound bank where you only want to -read data within a certain range of the underlying data. To do this you can use a range: - - ```c - result = ma_data_source_set_range_in_pcm_frames(pDataSource, rangeBegInFrames, rangeEndInFrames); - if (result != MA_SUCCESS) { - return result; // Failed to set the range. - } - ``` - -This is useful if you have a sound bank where many sounds are stored in the same file and you want -the data source to only play one of those sub-sounds. Note that once the range is set, everything -that takes a position, such as cursors and loop points, should always be relatvie to the start of -the range. When the range is set, any previously defined loop point will be reset. - -Custom loop points can also be used with data sources. By default, data sources will loop after -they reach the end of the data source, but if you need to loop at a specific location, you can do -the following: - - ```c - result = ma_data_set_loop_point_in_pcm_frames(pDataSource, loopBegInFrames, loopEndInFrames); - if (result != MA_SUCCESS) { - return result; // Failed to set the loop point. - } - ``` - -The loop point is relative to the current range. - -It's sometimes useful to chain data sources together so that a seamless transition can be achieved. -To do this, you can use chaining: - - ```c - ma_decoder decoder1; - ma_decoder decoder2; - - // ... initialize decoders with ma_decoder_init_*() ... - - result = ma_data_source_set_next(&decoder1, &decoder2); - if (result != MA_SUCCESS) { - return result; // Failed to set the next data source. - } - - result = ma_data_source_read_pcm_frames(&decoder1, pFramesOut, frameCount, pFramesRead); - if (result != MA_SUCCESS) { - return result; // Failed to read from the decoder. - } - ``` - -In the example above we're using decoders. When reading from a chain, you always want to read from -the top level data source in the chain. In the example above, `decoder1` is the top level data -source in the chain. When `decoder1` reaches the end, `decoder2` will start seamlessly without any -gaps. - -Note that when looping is enabled, only the current data source will be looped. You can loop the -entire chain by linking in a loop like so: - - ```c - ma_data_source_set_next(&decoder1, &decoder2); // decoder1 -> decoder2 - ma_data_source_set_next(&decoder2, &decoder1); // decoder2 -> decoder1 (loop back to the start). - ``` - -Note that setting up chaining is not thread safe, so care needs to be taken if you're dynamically -changing links while the audio thread is in the middle of reading. - -Do not use `ma_decoder_seek_to_pcm_frame()` as a means to reuse a data source to play multiple -instances of the same sound simultaneously. This can be extremely inefficient depending on the type -of data source and can result in glitching due to subtle changes to the state of internal filters. -Instead, initialize multiple data sources for each instance. - - -4.1. Custom Data Sources ------------------------- -You can implement a custom data source by implementing the functions in `ma_data_source_vtable`. -Your custom object must have `ma_data_source_base` as it's first member: - - ```c - struct my_data_source - { - ma_data_source_base base; - ... - }; - ``` - -In your initialization routine, you need to call `ma_data_source_init()` in order to set up the -base object (`ma_data_source_base`): - - ```c - static ma_result my_data_source_read(ma_data_source* pDataSource, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead) - { - // Read data here. Output in the same format returned by my_data_source_get_data_format(). - } - - static ma_result my_data_source_seek(ma_data_source* pDataSource, ma_uint64 frameIndex) - { - // Seek to a specific PCM frame here. Return MA_NOT_IMPLEMENTED if seeking is not supported. - } - - static ma_result my_data_source_get_data_format(ma_data_source* pDataSource, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate, ma_channel* pChannelMap, size_t channelMapCap) - { - // Return the format of the data here. - } - - static ma_result my_data_source_get_cursor(ma_data_source* pDataSource, ma_uint64* pCursor) - { - // Retrieve the current position of the cursor here. Return MA_NOT_IMPLEMENTED and set *pCursor to 0 if there is no notion of a cursor. - } - - static ma_result my_data_source_get_length(ma_data_source* pDataSource, ma_uint64* pLength) - { - // Retrieve the length in PCM frames here. Return MA_NOT_IMPLEMENTED and set *pLength to 0 if there is no notion of a length or if the length is unknown. - } - - static ma_data_source_vtable g_my_data_source_vtable = - { - my_data_source_read, - my_data_source_seek, - my_data_source_get_data_format, - my_data_source_get_cursor, - my_data_source_get_length - }; - - ma_result my_data_source_init(my_data_source* pMyDataSource) - { - ma_result result; - ma_data_source_config baseConfig; - - baseConfig = ma_data_source_config_init(); - baseConfig.vtable = &g_my_data_source_vtable; - - result = ma_data_source_init(&baseConfig, &pMyDataSource->base); - if (result != MA_SUCCESS) { - return result; - } - - // ... do the initialization of your custom data source here ... - - return MA_SUCCESS; - } - - void my_data_source_uninit(my_data_source* pMyDataSource) - { - // ... do the uninitialization of your custom data source here ... - - // You must uninitialize the base data source. - ma_data_source_uninit(&pMyDataSource->base); - } - ``` - -Note that `ma_data_source_init()` and `ma_data_source_uninit()` are never called directly outside -of the custom data source. It's up to the custom data source itself to call these within their own -init/uninit functions. - - - -5. Engine -========= -The `ma_engine` API is a high level API for managing and mixing sounds and effect processing. The -`ma_engine` object encapsulates a resource manager and a node graph, both of which will be -explained in more detail later. - -Sounds are called `ma_sound` and are created from an engine. Sounds can be associated with a mixing -group called `ma_sound_group` which are also created from the engine. Both `ma_sound` and -`ma_sound_group` objects are nodes within the engine's node graph. - -When the engine is initialized, it will normally create a device internally. If you would rather -manage the device yourself, you can do so and just pass a pointer to it via the engine config when -you initialize the engine. You can also just use the engine without a device, which again can be -configured via the engine config. - -The most basic way to initialize the engine is with a default config, like so: - - ```c - ma_result result; - ma_engine engine; - - result = ma_engine_init(NULL, &engine); - if (result != MA_SUCCESS) { - return result; // Failed to initialize the engine. - } - ``` - -This will result in the engine initializing a playback device using the operating system's default -device. This will be sufficient for many use cases, but if you need more flexibility you'll want to -configure the engine with an engine config: - - ```c - ma_result result; - ma_engine engine; - ma_engine_config engineConfig; - - engineConfig = ma_engine_config_init(); - engineConfig.pDevice = &myDevice; - - result = ma_engine_init(&engineConfig, &engine); - if (result != MA_SUCCESS) { - return result; // Failed to initialize the engine. - } - ``` - -In the example above we're passing in a pre-initialized device. Since the caller is the one in -control of the device's data callback, it's their responsibility to manually call -`ma_engine_read_pcm_frames()` from inside their data callback: - - ```c - void playback_data_callback(ma_device* pDevice, void* pOutput, const void* pInput, ma_uint32 frameCount) - { - ma_engine_read_pcm_frames(&g_Engine, pOutput, frameCount, NULL); - } - ``` - -You can also use the engine independent of a device entirely: - - ```c - ma_result result; - ma_engine engine; - ma_engine_config engineConfig; - - engineConfig = ma_engine_config_init(); - engineConfig.noDevice = MA_TRUE; - engineConfig.channels = 2; // Must be set when not using a device. - engineConfig.sampleRate = 48000; // Must be set when not using a device. - - result = ma_engine_init(&engineConfig, &engine); - if (result != MA_SUCCESS) { - return result; // Failed to initialize the engine. - } - ``` - -Note that when you're not using a device, you must set the channel count and sample rate in the -config or else miniaudio won't know what to use (miniaudio will use the device to determine this -normally). When not using a device, you need to use `ma_engine_read_pcm_frames()` to process audio -data from the engine. This kind of setup is useful if you want to do something like offline -processing or want to use a different audio system for playback such as SDL. - -When a sound is loaded it goes through a resource manager. By default the engine will initialize a -resource manager internally, but you can also specify a pre-initialized resource manager: - - ```c - ma_result result; - ma_engine engine1; - ma_engine engine2; - ma_engine_config engineConfig; - - engineConfig = ma_engine_config_init(); - engineConfig.pResourceManager = &myResourceManager; - - ma_engine_init(&engineConfig, &engine1); - ma_engine_init(&engineConfig, &engine2); - ``` - -In this example we are initializing two engines, both of which are sharing the same resource -manager. This is especially useful for saving memory when loading the same file across multiple -engines. If you were not to use a shared resource manager, each engine instance would use their own -which would result in any sounds that are used between both engine's being loaded twice. By using -a shared resource manager, it would only be loaded once. Using multiple engine's is useful when you -need to output to multiple playback devices, such as in a local multiplayer game where each player -is using their own set of headphones. - -By default an engine will be in a started state. To make it so the engine is not automatically -started you can configure it as such: - - ```c - engineConfig.noAutoStart = MA_TRUE; - - // The engine will need to be started manually. - ma_engine_start(&engine); - - // Later on the engine can be stopped with ma_engine_stop(). - ma_engine_stop(&engine); - ``` - -The concept of starting or stopping an engine is only relevant when using the engine with a -device. Attempting to start or stop an engine that is not associated with a device will result in -`MA_INVALID_OPERATION`. - -The master volume of the engine can be controlled with `ma_engine_set_volume()` which takes a -linear scale, with 0 resulting in silence and anything above 1 resulting in amplification. If you -prefer decibel based volume control, use `ma_volume_db_to_linear()` to convert from dB to linear. - -When a sound is spatialized, it is done so relative to a listener. An engine can be configured to -have multiple listeners which can be configured via the config: - - ```c - engineConfig.listenerCount = 2; - ``` - -The maximum number of listeners is restricted to `MA_ENGINE_MAX_LISTENERS`. By default, when a -sound is spatialized, it will be done so relative to the closest listener. You can also pin a sound -to a specific listener which will be explained later. Listener's have a position, direction, cone, -and velocity (for doppler effect). A listener is referenced by an index, the meaning of which is up -to the caller (the index is 0 based and cannot go beyond the listener count, minus 1). The -position, direction and velocity are all specified in absolute terms: - - ```c - ma_engine_listener_set_position(&engine, listenerIndex, worldPosX, worldPosY, worldPosZ); - ``` - -The direction of the listener represents it's forward vector. The listener's up vector can also be -specified and defaults to +1 on the Y axis. - - ```c - ma_engine_listener_set_direction(&engine, listenerIndex, forwardX, forwardY, forwardZ); - ma_engine_listener_set_world_up(&engine, listenerIndex, 0, 1, 0); - ``` - -The engine supports directional attenuation. The listener can have a cone the controls how sound is -attenuated based on the listener's direction. When a sound is between the inner and outer cones, it -will be attenuated between 1 and the cone's outer gain: - - ```c - ma_engine_listener_set_cone(&engine, listenerIndex, innerAngleInRadians, outerAngleInRadians, outerGain); - ``` - -When a sound is inside the inner code, no directional attenuation is applied. When the sound is -outside of the outer cone, the attenuation will be set to `outerGain` in the example above. When -the sound is in between the inner and outer cones, the attenuation will be interpolated between 1 -and the outer gain. - -The engine's coordinate system follows the OpenGL coordinate system where positive X points right, -positive Y points up and negative Z points forward. - -The simplest and least flexible way to play a sound is like so: - - ```c - ma_engine_play_sound(&engine, "my_sound.wav", pGroup); - ``` - -This is a "fire and forget" style of function. The engine will manage the `ma_sound` object -internally. When the sound finishes playing, it'll be put up for recycling. For more flexibility -you'll want to initialize a sound object: - - ```c - ma_sound sound; - - result = ma_sound_init_from_file(&engine, "my_sound.wav", flags, pGroup, NULL, &sound); - if (result != MA_SUCCESS) { - return result; // Failed to load sound. - } - ``` - -Sounds need to be uninitialized with `ma_sound_uninit()`. - -The example above loads a sound from a file. If the resource manager has been disabled you will not -be able to use this function and instead you'll need to initialize a sound directly from a data -source: - - ```c - ma_sound sound; - - result = ma_sound_init_from_data_source(&engine, &dataSource, flags, pGroup, &sound); - if (result != MA_SUCCESS) { - return result; - } - ``` - -Each `ma_sound` object represents a single instance of the sound. If you want to play the same -sound multiple times at the same time, you need to initialize a separate `ma_sound` object. - -For the most flexibility when initializing sounds, use `ma_sound_init_ex()`. This uses miniaudio's -standard config/init pattern: - - ```c - ma_sound sound; - ma_sound_config soundConfig; - - soundConfig = ma_sound_config_init(); - soundConfig.pFilePath = NULL; // Set this to load from a file path. - soundConfig.pDataSource = NULL; // Set this to initialize from an existing data source. - soundConfig.pInitialAttachment = &someNodeInTheNodeGraph; - soundConfig.initialAttachmentInputBusIndex = 0; - soundConfig.channelsIn = 1; - soundConfig.channelsOut = 0; // Set to 0 to use the engine's native channel count. - - result = ma_sound_init_ex(&soundConfig, &sound); - if (result != MA_SUCCESS) { - return result; - } - ``` - -In the example above, the sound is being initialized without a file nor a data source. This is -valid, in which case the sound acts as a node in the middle of the node graph. This means you can -connect other sounds to this sound and allow it to act like a sound group. Indeed, this is exactly -what a `ma_sound_group` is. - -When loading a sound, you specify a set of flags that control how the sound is loaded and what -features are enabled for that sound. When no flags are set, the sound will be fully loaded into -memory in exactly the same format as how it's stored on the file system. The resource manager will -allocate a block of memory and then load the file directly into it. When reading audio data, it -will be decoded dynamically on the fly. In order to save processing time on the audio thread, it -might be beneficial to pre-decode the sound. You can do this with the `MA_SOUND_FLAG_DECODE` flag: - - ```c - ma_sound_init_from_file(&engine, "my_sound.wav", MA_SOUND_FLAG_DECODE, pGroup, NULL, &sound); - ``` - -By default, sounds will be loaded synchronously, meaning `ma_sound_init_*()` will not return until -the sound has been fully loaded. If this is prohibitive you can instead load sounds asynchronously -by specifying the `MA_SOUND_FLAG_ASYNC` flag: - - ```c - ma_sound_init_from_file(&engine, "my_sound.wav", MA_SOUND_FLAG_DECODE | MA_SOUND_FLAG_ASYNC, pGroup, NULL, &sound); - ``` - -This will result in `ma_sound_init_*()` returning quickly, but the sound won't yet have been fully -loaded. When you start the sound, it won't output anything until some sound is available. The sound -will start outputting audio before the sound has been fully decoded when the `MA_SOUND_FLAG_DECODE` -is specified. - -If you need to wait for an asynchronously loaded sound to be fully loaded, you can use a fence. A -fence in miniaudio is a simple synchronization mechanism which simply blocks until it's internal -counter hit's zero. You can specify a fence like so: - - ```c - ma_result result; - ma_fence fence; - ma_sound sounds[4]; - - result = ma_fence_init(&fence); - if (result != MA_SUCCESS) { - return result; - } - - // Load some sounds asynchronously. - for (int iSound = 0; iSound < 4; iSound += 1) { - ma_sound_init_from_file(&engine, mySoundFilesPaths[iSound], MA_SOUND_FLAG_DECODE | MA_SOUND_FLAG_ASYNC, pGroup, &fence, &sounds[iSound]); - } - - // ... do some other stuff here in the mean time ... - - // Wait for all sounds to finish loading. - ma_fence_wait(&fence); - ``` - -If loading the entire sound into memory is prohibitive, you can also configure the engine to stream -the audio data: - - ```c - ma_sound_init_from_file(&engine, "my_sound.wav", MA_SOUND_FLAG_STREAM, pGroup, NULL, &sound); - ``` - -When streaming sounds, 2 seconds worth of audio data is stored in memory. Although it should work -fine, it's inefficient to use streaming for short sounds. Streaming is useful for things like music -tracks in games. - -When loading a sound from a file path, the engine will reference count the file to prevent it from -being loaded if it's already in memory. When you uninitialize a sound, the reference count will be -decremented, and if it hits zero, the sound will be unloaded from memory. This reference counting -system is not used for streams. The engine will use a 64-bit hash of the file name when comparing -file paths which means there's a small chance you might encounter a name collision. If this is an -issue, you'll need to use a different name for one of the colliding file paths, or just not load -from files and instead load from a data source. - -You can use `ma_sound_init_copy()` to initialize a copy of another sound. Note, however, that this -only works for sounds that were initialized with `ma_sound_init_from_file()` and without the -`MA_SOUND_FLAG_STREAM` flag. - -When you initialize a sound, if you specify a sound group the sound will be attached to that group -automatically. If you set it to NULL, it will be automatically attached to the engine's endpoint. -If you would instead rather leave the sound unattached by default, you can specify the -`MA_SOUND_FLAG_NO_DEFAULT_ATTACHMENT` flag. This is useful if you want to set up a complex node -graph. - -Sounds are not started by default. To start a sound, use `ma_sound_start()`. Stop a sound with -`ma_sound_stop()`. - -Sounds can have their volume controlled with `ma_sound_set_volume()` in the same way as the -engine's master volume. - -Sounds support stereo panning and pitching. Set the pan with `ma_sound_set_pan()`. Setting the pan -to 0 will result in an unpanned sound. Setting it to -1 will shift everything to the left, whereas -+1 will shift it to the right. The pitch can be controlled with `ma_sound_set_pitch()`. A larger -value will result in a higher pitch. The pitch must be greater than 0. - -The engine supports 3D spatialization of sounds. By default sounds will have spatialization -enabled, but if a sound does not need to be spatialized it's best to disable it. There are two ways -to disable spatialization of a sound: - - ```c - // Disable spatialization at initialization time via a flag: - ma_sound_init_from_file(&engine, "my_sound.wav", MA_SOUND_FLAG_NO_SPATIALIZATION, NULL, NULL, &sound); - - // Dynamically disable or enable spatialization post-initialization: - ma_sound_set_spatialization_enabled(&sound, isSpatializationEnabled); - ``` - -By default sounds will be spatialized based on the closest listener. If a sound should always be -spatialized relative to a specific listener it can be pinned to one: - - ```c - ma_sound_set_pinned_listener_index(&sound, listenerIndex); - ``` - -Like listeners, sounds have a position. By default, the position of a sound is in absolute space, -but it can be changed to be relative to a listener: - - ```c - ma_sound_set_positioning(&sound, ma_positioning_relative); - ``` - -Note that relative positioning of a sound only makes sense if there is either only one listener, or -the sound is pinned to a specific listener. To set the position of a sound: - - ```c - ma_sound_set_position(&sound, posX, posY, posZ); - ``` - -The direction works the same way as a listener and represents the sound's forward direction: - - ```c - ma_sound_set_direction(&sound, forwardX, forwardY, forwardZ); - ``` - -Sound's also have a cone for controlling directional attenuation. This works exactly the same as -listeners: - - ```c - ma_sound_set_cone(&sound, innerAngleInRadians, outerAngleInRadians, outerGain); - ``` - -The velocity of a sound is used for doppler effect and can be set as such: - - ```c - ma_sound_set_velocity(&sound, velocityX, velocityY, velocityZ); - ``` - -The engine supports different attenuation models which can be configured on a per-sound basis. By -default the attenuation model is set to `ma_attenuation_model_inverse` which is the equivalent to -OpenAL's `AL_INVERSE_DISTANCE_CLAMPED`. Configure the attenuation model like so: - - ```c - ma_sound_set_attenuation_model(&sound, ma_attenuation_model_inverse); - ``` - -The supported attenuation models include the following: - - +----------------------------------+----------------------------------------------+ - | ma_attenuation_model_none | No distance attenuation. | - +----------------------------------+----------------------------------------------+ - | ma_attenuation_model_inverse | Equivalent to `AL_INVERSE_DISTANCE_CLAMPED`. | - +----------------------------------+----------------------------------------------+ - | ma_attenuation_model_linear | Linear attenuation. | - +----------------------------------+----------------------------------------------+ - | ma_attenuation_model_exponential | Exponential attenuation. | - +----------------------------------+----------------------------------------------+ - -To control how quickly a sound rolls off as it moves away from the listener, you need to configure -the rolloff: - - ```c - ma_sound_set_rolloff(&sound, rolloff); - ``` - -You can control the minimum and maximum gain to apply from spatialization: - - ```c - ma_sound_set_min_gain(&sound, minGain); - ma_sound_set_max_gain(&sound, maxGain); - ``` - -Likewise, in the calculation of attenuation, you can control the minimum and maximum distances for -the attenuation calculation. This is useful if you want to ensure sounds don't drop below a certain -volume after the listener moves further away and to have sounds play a maximum volume when the -listener is within a certain distance: - - ```c - ma_sound_set_min_distance(&sound, minDistance); - ma_sound_set_max_distance(&sound, maxDistance); - ``` - -The engine's spatialization system supports doppler effect. The doppler factor can be configure on -a per-sound basis like so: - - ```c - ma_sound_set_doppler_factor(&sound, dopplerFactor); - ``` - -You can fade sounds in and out with `ma_sound_set_fade_in_pcm_frames()` and -`ma_sound_set_fade_in_milliseconds()`. Set the volume to -1 to use the current volume as the -starting volume: - - ```c - // Fade in over 1 second. - ma_sound_set_fade_in_milliseconds(&sound, 0, 1, 1000); - - // ... sometime later ... - - // Fade out over 1 second, starting from the current volume. - ma_sound_set_fade_in_milliseconds(&sound, -1, 0, 1000); - ``` - -By default sounds will start immediately, but sometimes for timing and synchronization purposes it -can be useful to schedule a sound to start or stop: - - ```c - // Start the sound in 1 second from now. - ma_sound_set_start_time_in_pcm_frames(&sound, ma_engine_get_time_in_pcm_frames(&engine) + (ma_engine_get_sample_rate(&engine) * 1)); - - // Stop the sound in 2 seconds from now. - ma_sound_set_stop_time_in_pcm_frames(&sound, ma_engine_get_time_in_pcm_frames(&engine) + (ma_engine_get_sample_rate(&engine) * 2)); - ``` - -Note that scheduling a start time still requires an explicit call to `ma_sound_start()` before -anything will play. - -The time is specified in global time which is controlled by the engine. You can get the engine's -current time with `ma_engine_get_time_in_pcm_frames()`. The engine's global time is incremented -automatically as audio data is read, but it can be reset with `ma_engine_set_time_in_pcm_frames()` -in case it needs to be resynchronized for some reason. - -To determine whether or not a sound is currently playing, use `ma_sound_is_playing()`. This will -take the scheduled start and stop times into account. - -Whether or not a sound should loop can be controlled with `ma_sound_set_looping()`. Sounds will not -be looping by default. Use `ma_sound_is_looping()` to determine whether or not a sound is looping. - -Use `ma_sound_at_end()` to determine whether or not a sound is currently at the end. For a looping -sound this should never return true. Alternatively, you can configure a callback that will be fired -when the sound reaches the end. Note that the callback is fired from the audio thread which means -you cannot be uninitializing sound from the callback. To set the callback you can use -`ma_sound_set_end_callback()`. Alternatively, if you're using `ma_sound_init_ex()`, you can pass it -into the config like so: - - ```c - soundConfig.endCallback = my_end_callback; - soundConfig.pEndCallbackUserData = pMyEndCallbackUserData; - ``` - -The end callback is declared like so: - - ```c - void my_end_callback(void* pUserData, ma_sound* pSound) - { - ... - } - ``` - -Internally a sound wraps around a data source. Some APIs exist to control the underlying data -source, mainly for convenience: - - ```c - ma_sound_seek_to_pcm_frame(&sound, frameIndex); - ma_sound_get_data_format(&sound, &format, &channels, &sampleRate, pChannelMap, channelMapCapacity); - ma_sound_get_cursor_in_pcm_frames(&sound, &cursor); - ma_sound_get_length_in_pcm_frames(&sound, &length); - ``` - -Sound groups have the same API as sounds, only they are called `ma_sound_group`, and since they do -not have any notion of a data source, anything relating to a data source is unavailable. - -Internally, sound data is loaded via the `ma_decoder` API which means by default it only supports -file formats that have built-in support in miniaudio. You can extend this to support any kind of -file format through the use of custom decoders. To do this you'll need to use a self-managed -resource manager and configure it appropriately. See the "Resource Management" section below for -details on how to set this up. - - -6. Resource Management -====================== -Many programs will want to manage sound resources for things such as reference counting and -streaming. This is supported by miniaudio via the `ma_resource_manager` API. - -The resource manager is mainly responsible for the following: - - * Loading of sound files into memory with reference counting. - * Streaming of sound data. - -When loading a sound file, the resource manager will give you back a `ma_data_source` compatible -object called `ma_resource_manager_data_source`. This object can be passed into any -`ma_data_source` API which is how you can read and seek audio data. When loading a sound file, you -specify whether or not you want the sound to be fully loaded into memory (and optionally -pre-decoded) or streamed. When loading into memory, you can also specify whether or not you want -the data to be loaded asynchronously. - -The example below is how you can initialize a resource manager using it's default configuration: - - ```c - ma_resource_manager_config config; - ma_resource_manager resourceManager; - - config = ma_resource_manager_config_init(); - result = ma_resource_manager_init(&config, &resourceManager); - if (result != MA_SUCCESS) { - ma_device_uninit(&device); - printf("Failed to initialize the resource manager."); - return -1; - } - ``` - -You can configure the format, channels and sample rate of the decoded audio data. By default it -will use the file's native data format, but you can configure it to use a consistent format. This -is useful for offloading the cost of data conversion to load time rather than dynamically -converting at mixing time. To do this, you configure the decoded format, channels and sample rate -like the code below: - - ```c - config = ma_resource_manager_config_init(); - config.decodedFormat = device.playback.format; - config.decodedChannels = device.playback.channels; - config.decodedSampleRate = device.sampleRate; - ``` - -In the code above, the resource manager will be configured so that any decoded audio data will be -pre-converted at load time to the device's native data format. If instead you used defaults and -the data format of the file did not match the device's data format, you would need to convert the -data at mixing time which may be prohibitive in high-performance and large scale scenarios like -games. - -Internally the resource manager uses the `ma_decoder` API to load sounds. This means by default it -only supports decoders that are built into miniaudio. It's possible to support additional encoding -formats through the use of custom decoders. To do so, pass in your `ma_decoding_backend_vtable` -vtables into the resource manager config: - - ```c - ma_decoding_backend_vtable* pCustomBackendVTables[] = - { - &g_ma_decoding_backend_vtable_libvorbis, - &g_ma_decoding_backend_vtable_libopus - }; - - ... - - resourceManagerConfig.ppCustomDecodingBackendVTables = pCustomBackendVTables; - resourceManagerConfig.customDecodingBackendCount = sizeof(pCustomBackendVTables) / sizeof(pCustomBackendVTables[0]); - resourceManagerConfig.pCustomDecodingBackendUserData = NULL; - ``` - -This system can allow you to support any kind of file format. See the "Decoding" section for -details on how to implement custom decoders. The miniaudio repository includes examples for Opus -via libopus and libopusfile and Vorbis via libvorbis and libvorbisfile. - -Asynchronicity is achieved via a job system. When an operation needs to be performed, such as the -decoding of a page, a job will be posted to a queue which will then be processed by a job thread. -By default there will be only one job thread running, but this can be configured, like so: - - ```c - config = ma_resource_manager_config_init(); - config.jobThreadCount = MY_JOB_THREAD_COUNT; - ``` - -By default job threads are managed internally by the resource manager, however you can also self -manage your job threads if, for example, you want to integrate the job processing into your -existing job infrastructure, or if you simply don't like the way the resource manager does it. To -do this, just set the job thread count to 0 and process jobs manually. To process jobs, you first -need to retrieve a job using `ma_resource_manager_next_job()` and then process it using -`ma_job_process()`: - - ```c - config = ma_resource_manager_config_init(); - config.jobThreadCount = 0; // Don't manage any job threads internally. - config.flags = MA_RESOURCE_MANAGER_FLAG_NON_BLOCKING; // Optional. Makes `ma_resource_manager_next_job()` non-blocking. - - // ... Initialize your custom job threads ... - - void my_custom_job_thread(...) - { - for (;;) { - ma_job job; - ma_result result = ma_resource_manager_next_job(pMyResourceManager, &job); - if (result != MA_SUCCESS) { - if (result == MA_NO_DATA_AVAILABLE) { - // No jobs are available. Keep going. Will only get this if the resource manager was initialized - // with MA_RESOURCE_MANAGER_FLAG_NON_BLOCKING. - continue; - } else if (result == MA_CANCELLED) { - // MA_JOB_TYPE_QUIT was posted. Exit. - break; - } else { - // Some other error occurred. - break; - } - } - - ma_job_process(&job); - } - } - ``` - -In the example above, the `MA_JOB_TYPE_QUIT` event is the used as the termination -indicator, but you can use whatever you would like to terminate the thread. The call to -`ma_resource_manager_next_job()` is blocking by default, but can be configured to be non-blocking -by initializing the resource manager with the `MA_RESOURCE_MANAGER_FLAG_NON_BLOCKING` configuration -flag. Note that the `MA_JOB_TYPE_QUIT` will never be removed from the job queue. This -is to give every thread the opportunity to catch the event and terminate naturally. - -When loading a file, it's sometimes convenient to be able to customize how files are opened and -read instead of using standard `fopen()`, `fclose()`, etc. which is what miniaudio will use by -default. This can be done by setting `pVFS` member of the resource manager's config: - - ```c - // Initialize your custom VFS object. See documentation for VFS for information on how to do this. - my_custom_vfs vfs = my_custom_vfs_init(); - - config = ma_resource_manager_config_init(); - config.pVFS = &vfs; - ``` - -This is particularly useful in programs like games where you want to read straight from an archive -rather than the normal file system. If you do not specify a custom VFS, the resource manager will -use the operating system's normal file operations. - -To load a sound file and create a data source, call `ma_resource_manager_data_source_init()`. When -loading a sound you need to specify the file path and options for how the sounds should be loaded. -By default a sound will be loaded synchronously. The returned data source is owned by the caller -which means the caller is responsible for the allocation and freeing of the data source. Below is -an example for initializing a data source: - - ```c - ma_resource_manager_data_source dataSource; - ma_result result = ma_resource_manager_data_source_init(pResourceManager, pFilePath, flags, &dataSource); - if (result != MA_SUCCESS) { - // Error. - } - - // ... - - // A ma_resource_manager_data_source object is compatible with the `ma_data_source` API. To read data, just call - // the `ma_data_source_read_pcm_frames()` like you would with any normal data source. - result = ma_data_source_read_pcm_frames(&dataSource, pDecodedData, frameCount, &framesRead); - if (result != MA_SUCCESS) { - // Failed to read PCM frames. - } - - // ... - - ma_resource_manager_data_source_uninit(&dataSource); - ``` - -The `flags` parameter specifies how you want to perform loading of the sound file. It can be a -combination of the following flags: - - ``` - MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_STREAM - MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_DECODE - MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_ASYNC - MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_WAIT_INIT - MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_LOOPING - ``` - -When no flags are specified (set to 0), the sound will be fully loaded into memory, but not -decoded, meaning the raw file data will be stored in memory, and then dynamically decoded when -`ma_data_source_read_pcm_frames()` is called. To instead decode the audio data before storing it in -memory, use the `MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_DECODE` flag. By default, the sound file will -be loaded synchronously, meaning `ma_resource_manager_data_source_init()` will only return after -the entire file has been loaded. This is good for simplicity, but can be prohibitively slow. You -can instead load the sound asynchronously using the `MA_RESOURCE_MANAGER_DATA_SOURCE_ASYNC` flag. -This will result in `ma_resource_manager_data_source_init()` returning quickly, but no data will be -returned by `ma_data_source_read_pcm_frames()` until some data is available. When no data is -available because the asynchronous decoding hasn't caught up, `MA_BUSY` will be returned by -`ma_data_source_read_pcm_frames()`. - -For large sounds, it's often prohibitive to store the entire file in memory. To mitigate this, you -can instead stream audio data which you can do by specifying the -`MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_STREAM` flag. When streaming, data will be decoded in 1 -second pages. When a new page needs to be decoded, a job will be posted to the job queue and then -subsequently processed in a job thread. - -The `MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_LOOPING` flag can be used so that the sound will loop -when it reaches the end by default. It's recommended you use this flag when you want to have a -looping streaming sound. If you try loading a very short sound as a stream, you will get a glitch. -This is because the resource manager needs to pre-fill the initial buffer at initialization time, -and if you don't specify the `MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_LOOPING` flag, the resource -manager will assume the sound is not looping and will stop filling the buffer when it reaches the -end, therefore resulting in a discontinuous buffer. - -For in-memory sounds, reference counting is used to ensure the data is loaded only once. This means -multiple calls to `ma_resource_manager_data_source_init()` with the same file path will result in -the file data only being loaded once. Each call to `ma_resource_manager_data_source_init()` must be -matched up with a call to `ma_resource_manager_data_source_uninit()`. Sometimes it can be useful -for a program to register self-managed raw audio data and associate it with a file path. Use the -`ma_resource_manager_register_*()` and `ma_resource_manager_unregister_*()` APIs to do this. -`ma_resource_manager_register_decoded_data()` is used to associate a pointer to raw, self-managed -decoded audio data in the specified data format with the specified name. Likewise, -`ma_resource_manager_register_encoded_data()` is used to associate a pointer to raw self-managed -encoded audio data (the raw file data) with the specified name. Note that these names need not be -actual file paths. When `ma_resource_manager_data_source_init()` is called (without the -`MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_STREAM` flag), the resource manager will look for these -explicitly registered data buffers and, if found, will use it as the backing data for the data -source. Note that the resource manager does *not* make a copy of this data so it is up to the -caller to ensure the pointer stays valid for its lifetime. Use -`ma_resource_manager_unregister_data()` to unregister the self-managed data. You can also use -`ma_resource_manager_register_file()` and `ma_resource_manager_unregister_file()` to register and -unregister a file. It does not make sense to use the `MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_STREAM` -flag with a self-managed data pointer. - - -6.1. Asynchronous Loading and Synchronization ---------------------------------------------- -When loading asynchronously, it can be useful to poll whether or not loading has finished. Use -`ma_resource_manager_data_source_result()` to determine this. For in-memory sounds, this will -return `MA_SUCCESS` when the file has been *entirely* decoded. If the sound is still being decoded, -`MA_BUSY` will be returned. Otherwise, some other error code will be returned if the sound failed -to load. For streaming data sources, `MA_SUCCESS` will be returned when the first page has been -decoded and the sound is ready to be played. If the first page is still being decoded, `MA_BUSY` -will be returned. Otherwise, some other error code will be returned if the sound failed to load. - -In addition to polling, you can also use a simple synchronization object called a "fence" to wait -for asynchronously loaded sounds to finish. This is called `ma_fence`. The advantage to using a -fence is that it can be used to wait for a group of sounds to finish loading rather than waiting -for sounds on an individual basis. There are two stages to loading a sound: - - * Initialization of the internal decoder; and - * Completion of decoding of the file (the file is fully decoded) - -You can specify separate fences for each of the different stages. Waiting for the initialization -of the internal decoder is important for when you need to know the sample format, channels and -sample rate of the file. - -The example below shows how you could use a fence when loading a number of sounds: - - ```c - // This fence will be released when all sounds are finished loading entirely. - ma_fence fence; - ma_fence_init(&fence); - - // This will be passed into the initialization routine for each sound. - ma_resource_manager_pipeline_notifications notifications = ma_resource_manager_pipeline_notifications_init(); - notifications.done.pFence = &fence; - - // Now load a bunch of sounds: - for (iSound = 0; iSound < soundCount; iSound += 1) { - ma_resource_manager_data_source_init(pResourceManager, pSoundFilePaths[iSound], flags, ¬ifications, &pSoundSources[iSound]); - } - - // ... DO SOMETHING ELSE WHILE SOUNDS ARE LOADING ... - - // Wait for loading of sounds to finish. - ma_fence_wait(&fence); - ``` - -In the example above we used a fence for waiting until the entire file has been fully decoded. If -you only need to wait for the initialization of the internal decoder to complete, you can use the -`init` member of the `ma_resource_manager_pipeline_notifications` object: - - ```c - notifications.init.pFence = &fence; - ``` - -If a fence is not appropriate for your situation, you can instead use a callback that is fired on -an individual sound basis. This is done in a very similar way to fences: - - ```c - typedef struct - { - ma_async_notification_callbacks cb; - void* pMyData; - } my_notification; - - void my_notification_callback(ma_async_notification* pNotification) - { - my_notification* pMyNotification = (my_notification*)pNotification; - - // Do something in response to the sound finishing loading. - } - - ... - - my_notification myCallback; - myCallback.cb.onSignal = my_notification_callback; - myCallback.pMyData = pMyData; - - ma_resource_manager_pipeline_notifications notifications = ma_resource_manager_pipeline_notifications_init(); - notifications.done.pNotification = &myCallback; - - ma_resource_manager_data_source_init(pResourceManager, "my_sound.wav", flags, ¬ifications, &mySound); - ``` - -In the example above we just extend the `ma_async_notification_callbacks` object and pass an -instantiation into the `ma_resource_manager_pipeline_notifications` in the same way as we did with -the fence, only we set `pNotification` instead of `pFence`. You can set both of these at the same -time and they should both work as expected. If using the `pNotification` system, you need to ensure -your `ma_async_notification_callbacks` object stays valid. - - - -6.2. Resource Manager Implementation Details --------------------------------------------- -Resources are managed in two main ways: - - * By storing the entire sound inside an in-memory buffer (referred to as a data buffer) - * By streaming audio data on the fly (referred to as a data stream) - -A resource managed data source (`ma_resource_manager_data_source`) encapsulates a data buffer or -data stream, depending on whether or not the data source was initialized with the -`MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_STREAM` flag. If so, it will make use of a -`ma_resource_manager_data_stream` object. Otherwise it will use a `ma_resource_manager_data_buffer` -object. Both of these objects are data sources which means they can be used with any -`ma_data_source_*()` API. - -Another major feature of the resource manager is the ability to asynchronously decode audio files. -This relieves the audio thread of time-consuming decoding which can negatively affect scalability -due to the audio thread needing to complete it's work extremely quickly to avoid glitching. -Asynchronous decoding is achieved through a job system. There is a central multi-producer, -multi-consumer, fixed-capacity job queue. When some asynchronous work needs to be done, a job is -posted to the queue which is then read by a job thread. The number of job threads can be -configured for improved scalability, and job threads can all run in parallel without needing to -worry about the order of execution (how this is achieved is explained below). - -When a sound is being loaded asynchronously, playback can begin before the sound has been fully -decoded. This enables the application to start playback of the sound quickly, while at the same -time allowing to resource manager to keep loading in the background. Since there may be less -threads than the number of sounds being loaded at a given time, a simple scheduling system is used -to keep decoding time balanced and fair. The resource manager solves this by splitting decoding -into chunks called pages. By default, each page is 1 second long. When a page has been decoded, a -new job will be posted to start decoding the next page. By dividing up decoding into pages, an -individual sound shouldn't ever delay every other sound from having their first page decoded. Of -course, when loading many sounds at the same time, there will always be an amount of time required -to process jobs in the queue so in heavy load situations there will still be some delay. To -determine if a data source is ready to have some frames read, use -`ma_resource_manager_data_source_get_available_frames()`. This will return the number of frames -available starting from the current position. - - -6.2.1. Job Queue ----------------- -The resource manager uses a job queue which is multi-producer, multi-consumer, and fixed-capacity. -This job queue is not currently lock-free, and instead uses a spinlock to achieve thread-safety. -Only a fixed number of jobs can be allocated and inserted into the queue which is done through a -lock-free data structure for allocating an index into a fixed sized array, with reference counting -for mitigation of the ABA problem. The reference count is 32-bit. - -For many types of jobs it's important that they execute in a specific order. In these cases, jobs -are executed serially. For the resource manager, serial execution of jobs is only required on a -per-object basis (per data buffer or per data stream). Each of these objects stores an execution -counter. When a job is posted it is associated with an execution counter. When the job is -processed, it checks if the execution counter of the job equals the execution counter of the -owning object and if so, processes the job. If the counters are not equal, the job will be posted -back onto the job queue for later processing. When the job finishes processing the execution order -of the main object is incremented. This system means the no matter how many job threads are -executing, decoding of an individual sound will always get processed serially. The advantage to -having multiple threads comes into play when loading multiple sounds at the same time. - -The resource manager's job queue is not 100% lock-free and will use a spinlock to achieve -thread-safety for a very small section of code. This is only relevant when the resource manager -uses more than one job thread. If only using a single job thread, which is the default, the -lock should never actually wait in practice. The amount of time spent locking should be quite -short, but it's something to be aware of for those who have pedantic lock-free requirements and -need to use more than one job thread. There are plans to remove this lock in a future version. - -In addition, posting a job will release a semaphore, which on Win32 is implemented with -`ReleaseSemaphore` and on POSIX platforms via a condition variable: - - ```c - pthread_mutex_lock(&pSemaphore->lock); - { - pSemaphore->value += 1; - pthread_cond_signal(&pSemaphore->cond); - } - pthread_mutex_unlock(&pSemaphore->lock); - ``` - -Again, this is relevant for those with strict lock-free requirements in the audio thread. To avoid -this, you can use non-blocking mode (via the `MA_JOB_QUEUE_FLAG_NON_BLOCKING` -flag) and implement your own job processing routine (see the "Resource Manager" section above for -details on how to do this). - - - -6.2.2. Data Buffers -------------------- -When the `MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_STREAM` flag is excluded at initialization time, the -resource manager will try to load the data into an in-memory data buffer. Before doing so, however, -it will first check if the specified file is already loaded. If so, it will increment a reference -counter and just use the already loaded data. This saves both time and memory. When the data buffer -is uninitialized, the reference counter will be decremented. If the counter hits zero, the file -will be unloaded. This is a detail to keep in mind because it could result in excessive loading and -unloading of a sound. For example, the following sequence will result in a file be loaded twice, -once after the other: - - ```c - ma_resource_manager_data_source_init(pResourceManager, "my_file", ..., &myDataBuffer0); // Refcount = 1. Initial load. - ma_resource_manager_data_source_uninit(&myDataBuffer0); // Refcount = 0. Unloaded. - - ma_resource_manager_data_source_init(pResourceManager, "my_file", ..., &myDataBuffer1); // Refcount = 1. Reloaded because previous uninit() unloaded it. - ma_resource_manager_data_source_uninit(&myDataBuffer1); // Refcount = 0. Unloaded. - ``` - -A binary search tree (BST) is used for storing data buffers as it has good balance between -efficiency and simplicity. The key of the BST is a 64-bit hash of the file path that was passed -into `ma_resource_manager_data_source_init()`. The advantage of using a hash is that it saves -memory over storing the entire path, has faster comparisons, and results in a mostly balanced BST -due to the random nature of the hash. The disadvantages are that file names are case-sensitive and -there's a small chance of name collisions. If case-sensitivity is an issue, you should normalize -your file names to upper- or lower-case before initializing your data sources. If name collisions -become an issue, you'll need to change the name of one of the colliding names or just not use the -resource manager. - -When a sound file has not already been loaded and the `MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_ASYNC` -flag is excluded, the file will be decoded synchronously by the calling thread. There are two -options for controlling how the audio is stored in the data buffer - encoded or decoded. When the -`MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_DECODE` option is excluded, the raw file data will be stored -in memory. Otherwise the sound will be decoded before storing it in memory. Synchronous loading is -a very simple and standard process of simply adding an item to the BST, allocating a block of -memory and then decoding (if `MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_DECODE` is specified). - -When the `MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_ASYNC` flag is specified, loading of the data buffer -is done asynchronously. In this case, a job is posted to the queue to start loading and then the -function immediately returns, setting an internal result code to `MA_BUSY`. This result code is -returned when the program calls `ma_resource_manager_data_source_result()`. When decoding has fully -completed `MA_SUCCESS` will be returned. This can be used to know if loading has fully completed. - -When loading asynchronously, a single job is posted to the queue of the type -`MA_JOB_TYPE_RESOURCE_MANAGER_LOAD_DATA_BUFFER_NODE`. This involves making a copy of the file path and -associating it with job. When the job is processed by the job thread, it will first load the file -using the VFS associated with the resource manager. When using a custom VFS, it's important that it -be completely thread-safe because it will be used from one or more job threads at the same time. -Individual files should only ever be accessed by one thread at a time, however. After opening the -file via the VFS, the job will determine whether or not the file is being decoded. If not, it -simply allocates a block of memory and loads the raw file contents into it and returns. On the -other hand, when the file is being decoded, it will first allocate a decoder on the heap and -initialize it. Then it will check if the length of the file is known. If so it will allocate a -block of memory to store the decoded output and initialize it to silence. If the size is unknown, -it will allocate room for one page. After memory has been allocated, the first page will be -decoded. If the sound is shorter than a page, the result code will be set to `MA_SUCCESS` and the -completion event will be signalled and loading is now complete. If, however, there is more to -decode, a job with the code `MA_JOB_TYPE_RESOURCE_MANAGER_PAGE_DATA_BUFFER_NODE` is posted. This job -will decode the next page and perform the same process if it reaches the end. If there is more to -decode, the job will post another `MA_JOB_TYPE_RESOURCE_MANAGER_PAGE_DATA_BUFFER_NODE` job which will -keep on happening until the sound has been fully decoded. For sounds of an unknown length, each -page will be linked together as a linked list. Internally this is implemented via the -`ma_paged_audio_buffer` object. - - -6.2.3. Data Streams -------------------- -Data streams only ever store two pages worth of data for each instance. They are most useful for -large sounds like music tracks in games that would consume too much memory if fully decoded in -memory. After every frame from a page has been read, a job will be posted to load the next page -which is done from the VFS. - -For data streams, the `MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_ASYNC` flag will determine whether or -not initialization of the data source waits until the two pages have been decoded. When unset, -`ma_resource_manager_data_source_init()` will wait until the two pages have been loaded, otherwise -it will return immediately. - -When frames are read from a data stream using `ma_resource_manager_data_source_read_pcm_frames()`, -`MA_BUSY` will be returned if there are no frames available. If there are some frames available, -but less than the number requested, `MA_SUCCESS` will be returned, but the actual number of frames -read will be less than the number requested. Due to the asynchronous nature of data streams, -seeking is also asynchronous. If the data stream is in the middle of a seek, `MA_BUSY` will be -returned when trying to read frames. - -When `ma_resource_manager_data_source_read_pcm_frames()` results in a page getting fully consumed -a job is posted to load the next page. This will be posted from the same thread that called -`ma_resource_manager_data_source_read_pcm_frames()`. - -Data streams are uninitialized by posting a job to the queue, but the function won't return until -that job has been processed. The reason for this is that the caller owns the data stream object and -therefore miniaudio needs to ensure everything completes before handing back control to the caller. -Also, if the data stream is uninitialized while pages are in the middle of decoding, they must -complete before destroying any underlying object and the job system handles this cleanly. - -Note that when a new page needs to be loaded, a job will be posted to the resource manager's job -thread from the audio thread. You must keep in mind the details mentioned in the "Job Queue" -section above regarding locking when posting an event if you require a strictly lock-free audio -thread. - - - -7. Node Graph -============= -miniaudio's routing infrastructure follows a node graph paradigm. The idea is that you create a -node whose outputs are attached to inputs of another node, thereby creating a graph. There are -different types of nodes, with each node in the graph processing input data to produce output, -which is then fed through the chain. Each node in the graph can apply their own custom effects. At -the start of the graph will usually be one or more data source nodes which have no inputs and -instead pull their data from a data source. At the end of the graph is an endpoint which represents -the end of the chain and is where the final output is ultimately extracted from. - -Each node has a number of input buses and a number of output buses. An output bus from a node is -attached to an input bus of another. Multiple nodes can connect their output buses to another -node's input bus, in which case their outputs will be mixed before processing by the node. Below is -a diagram that illustrates a hypothetical node graph setup: - - ``` - >>>>>>>>>>>>>>>>>>>>>>>>>>>>>> Data flows left to right >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> - - +---------------+ +-----------------+ - | Data Source 1 =----+ +----------+ +----= Low Pass Filter =----+ - +---------------+ | | =----+ +-----------------+ | +----------+ - +----= Splitter | +----= ENDPOINT | - +---------------+ | | =----+ +-----------------+ | +----------+ - | Data Source 2 =----+ +----------+ +----= Echo / Delay =----+ - +---------------+ +-----------------+ - ``` - -In the above graph, it starts with two data sources whose outputs are attached to the input of a -splitter node. It's at this point that the two data sources are mixed. After mixing, the splitter -performs it's processing routine and produces two outputs which is simply a duplication of the -input stream. One output is attached to a low pass filter, whereas the other output is attached to -a echo/delay. The outputs of the low pass filter and the echo are attached to the endpoint, and -since they're both connected to the same input bus, they'll be mixed. - -Each input bus must be configured to accept the same number of channels, but the number of channels -used by input buses can be different to the number of channels for output buses in which case -miniaudio will automatically convert the input data to the output channel count before processing. -The number of channels of an output bus of one node must match the channel count of the input bus -it's attached to. The channel counts cannot be changed after the node has been initialized. If you -attempt to attach an output bus to an input bus with a different channel count, attachment will -fail. - -To use a node graph, you first need to initialize a `ma_node_graph` object. This is essentially a -container around the entire graph. The `ma_node_graph` object is required for some thread-safety -issues which will be explained later. A `ma_node_graph` object is initialized using miniaudio's -standard config/init system: - - ```c - ma_node_graph_config nodeGraphConfig = ma_node_graph_config_init(myChannelCount); - - result = ma_node_graph_init(&nodeGraphConfig, NULL, &nodeGraph); // Second parameter is a pointer to allocation callbacks. - if (result != MA_SUCCESS) { - // Failed to initialize node graph. - } - ``` - -When you initialize the node graph, you're specifying the channel count of the endpoint. The -endpoint is a special node which has one input bus and one output bus, both of which have the -same channel count, which is specified in the config. Any nodes that connect directly to the -endpoint must be configured such that their output buses have the same channel count. When you read -audio data from the node graph, it'll have the channel count you specified in the config. To read -data from the graph: - - ```c - ma_uint32 framesRead; - result = ma_node_graph_read_pcm_frames(&nodeGraph, pFramesOut, frameCount, &framesRead); - if (result != MA_SUCCESS) { - // Failed to read data from the node graph. - } - ``` - -When you read audio data, miniaudio starts at the node graph's endpoint node which then pulls in -data from its input attachments, which in turn recursively pull in data from their inputs, and so -on. At the start of the graph there will be some kind of data source node which will have zero -inputs and will instead read directly from a data source. The base nodes don't literally need to -read from a `ma_data_source` object, but they will always have some kind of underlying object that -sources some kind of audio. The `ma_data_source_node` node can be used to read from a -`ma_data_source`. Data is always in floating-point format and in the number of channels you -specified when the graph was initialized. The sample rate is defined by the underlying data sources. -It's up to you to ensure they use a consistent and appropriate sample rate. - -The `ma_node` API is designed to allow custom nodes to be implemented with relative ease, but -miniaudio includes a few stock nodes for common functionality. This is how you would initialize a -node which reads directly from a data source (`ma_data_source_node`) which is an example of one -of the stock nodes that comes with miniaudio: - - ```c - ma_data_source_node_config config = ma_data_source_node_config_init(pMyDataSource); - - ma_data_source_node dataSourceNode; - result = ma_data_source_node_init(&nodeGraph, &config, NULL, &dataSourceNode); - if (result != MA_SUCCESS) { - // Failed to create data source node. - } - ``` - -The data source node will use the output channel count to determine the channel count of the output -bus. There will be 1 output bus and 0 input buses (data will be drawn directly from the data -source). The data source must output to floating-point (`ma_format_f32`) or else an error will be -returned from `ma_data_source_node_init()`. - -By default the node will not be attached to the graph. To do so, use `ma_node_attach_output_bus()`: - - ```c - result = ma_node_attach_output_bus(&dataSourceNode, 0, ma_node_graph_get_endpoint(&nodeGraph), 0); - if (result != MA_SUCCESS) { - // Failed to attach node. - } - ``` - -The code above connects the data source node directly to the endpoint. Since the data source node -has only a single output bus, the index will always be 0. Likewise, the endpoint only has a single -input bus which means the input bus index will also always be 0. - -To detach a specific output bus, use `ma_node_detach_output_bus()`. To detach all output buses, use -`ma_node_detach_all_output_buses()`. If you want to just move the output bus from one attachment to -another, you do not need to detach first. You can just call `ma_node_attach_output_bus()` and it'll -deal with it for you. - -Less frequently you may want to create a specialized node. This will be a node where you implement -your own processing callback to apply a custom effect of some kind. This is similar to initializing -one of the stock node types, only this time you need to specify a pointer to a vtable containing a -pointer to the processing function and the number of input and output buses. Example: - - ```c - static void my_custom_node_process_pcm_frames(ma_node* pNode, const float** ppFramesIn, ma_uint32* pFrameCountIn, float** ppFramesOut, ma_uint32* pFrameCountOut) - { - // Do some processing of ppFramesIn (one stream of audio data per input bus) - const float* pFramesIn_0 = ppFramesIn[0]; // Input bus @ index 0. - const float* pFramesIn_1 = ppFramesIn[1]; // Input bus @ index 1. - float* pFramesOut_0 = ppFramesOut[0]; // Output bus @ index 0. - - // Do some processing. On input, `pFrameCountIn` will be the number of input frames in each - // buffer in `ppFramesIn` and `pFrameCountOut` will be the capacity of each of the buffers - // in `ppFramesOut`. On output, `pFrameCountIn` should be set to the number of input frames - // your node consumed and `pFrameCountOut` should be set the number of output frames that - // were produced. - // - // You should process as many frames as you can. If your effect consumes input frames at the - // same rate as output frames (always the case, unless you're doing resampling), you need - // only look at `ppFramesOut` and process that exact number of frames. If you're doing - // resampling, you'll need to be sure to set both `pFrameCountIn` and `pFrameCountOut` - // properly. - } - - static ma_node_vtable my_custom_node_vtable = - { - my_custom_node_process_pcm_frames, // The function that will be called to process your custom node. This is where you'd implement your effect processing. - NULL, // Optional. A callback for calculating the number of input frames that are required to process a specified number of output frames. - 2, // 2 input buses. - 1, // 1 output bus. - 0 // Default flags. - }; - - ... - - // Each bus needs to have a channel count specified. To do this you need to specify the channel - // counts in an array and then pass that into the node config. - ma_uint32 inputChannels[2]; // Equal in size to the number of input channels specified in the vtable. - ma_uint32 outputChannels[1]; // Equal in size to the number of output channels specified in the vtable. - - inputChannels[0] = channelsIn; - inputChannels[1] = channelsIn; - outputChannels[0] = channelsOut; - - ma_node_config nodeConfig = ma_node_config_init(); - nodeConfig.vtable = &my_custom_node_vtable; - nodeConfig.pInputChannels = inputChannels; - nodeConfig.pOutputChannels = outputChannels; - - ma_node_base node; - result = ma_node_init(&nodeGraph, &nodeConfig, NULL, &node); - if (result != MA_SUCCESS) { - // Failed to initialize node. - } - ``` - -When initializing a custom node, as in the code above, you'll normally just place your vtable in -static space. The number of input and output buses are specified as part of the vtable. If you need -a variable number of buses on a per-node bases, the vtable should have the relevant bus count set -to `MA_NODE_BUS_COUNT_UNKNOWN`. In this case, the bus count should be set in the node config: - - ```c - static ma_node_vtable my_custom_node_vtable = - { - my_custom_node_process_pcm_frames, // The function that will be called process your custom node. This is where you'd implement your effect processing. - NULL, // Optional. A callback for calculating the number of input frames that are required to process a specified number of output frames. - MA_NODE_BUS_COUNT_UNKNOWN, // The number of input buses is determined on a per-node basis. - 1, // 1 output bus. - 0 // Default flags. - }; - - ... - - ma_node_config nodeConfig = ma_node_config_init(); - nodeConfig.vtable = &my_custom_node_vtable; - nodeConfig.inputBusCount = myBusCount; // <-- Since the vtable specifies MA_NODE_BUS_COUNT_UNKNOWN, the input bus count should be set here. - nodeConfig.pInputChannels = inputChannels; // <-- Make sure there are nodeConfig.inputBusCount elements in this array. - nodeConfig.pOutputChannels = outputChannels; // <-- The vtable specifies 1 output bus, so there must be 1 element in this array. - ``` - -In the above example it's important to never set the `inputBusCount` and `outputBusCount` members -to anything other than their defaults if the vtable specifies an explicit count. They can only be -set if the vtable specifies MA_NODE_BUS_COUNT_UNKNOWN in the relevant bus count. - -Most often you'll want to create a structure to encapsulate your node with some extra data. You -need to make sure the `ma_node_base` object is your first member of the structure: - - ```c - typedef struct - { - ma_node_base base; // <-- Make sure this is always the first member. - float someCustomData; - } my_custom_node; - ``` - -By doing this, your object will be compatible with all `ma_node` APIs and you can attach it to the -graph just like any other node. - -In the custom processing callback (`my_custom_node_process_pcm_frames()` in the example above), the -number of channels for each bus is what was specified by the config when the node was initialized -with `ma_node_init()`. In addition, all attachments to each of the input buses will have been -pre-mixed by miniaudio. The config allows you to specify different channel counts for each -individual input and output bus. It's up to the effect to handle it appropriate, and if it can't, -return an error in it's initialization routine. - -Custom nodes can be assigned some flags to describe their behaviour. These are set via the vtable -and include the following: - - +-----------------------------------------+---------------------------------------------------+ - | Flag Name | Description | - +-----------------------------------------+---------------------------------------------------+ - | MA_NODE_FLAG_PASSTHROUGH | Useful for nodes that do not do any kind of audio | - | | processing, but are instead used for tracking | - | | time, handling events, etc. Also used by the | - | | internal endpoint node. It reads directly from | - | | the input bus to the output bus. Nodes with this | - | | flag must have exactly 1 input bus and 1 output | - | | bus, and both buses must have the same channel | - | | counts. | - +-----------------------------------------+---------------------------------------------------+ - | MA_NODE_FLAG_CONTINUOUS_PROCESSING | Causes the processing callback to be called even | - | | when no data is available to be read from input | - | | attachments. When a node has at least one input | - | | bus, but there are no inputs attached or the | - | | inputs do not deliver any data, the node's | - | | processing callback will not get fired. This flag | - | | will make it so the callback is always fired | - | | regardless of whether or not any input data is | - | | received. This is useful for effects like | - | | echos where there will be a tail of audio data | - | | that still needs to be processed even when the | - | | original data sources have reached their ends. It | - | | may also be useful for nodes that must always | - | | have their processing callback fired when there | - | | are no inputs attached. | - +-----------------------------------------+---------------------------------------------------+ - | MA_NODE_FLAG_ALLOW_NULL_INPUT | Used in conjunction with | - | | `MA_NODE_FLAG_CONTINUOUS_PROCESSING`. When this | - | | is set, the `ppFramesIn` parameter of the | - | | processing callback will be set to NULL when | - | | there are no input frames are available. When | - | | this is unset, silence will be posted to the | - | | processing callback. | - +-----------------------------------------+---------------------------------------------------+ - | MA_NODE_FLAG_DIFFERENT_PROCESSING_RATES | Used to tell miniaudio that input and output | - | | frames are processed at different rates. You | - | | should set this for any nodes that perform | - | | resampling. | - +-----------------------------------------+---------------------------------------------------+ - | MA_NODE_FLAG_SILENT_OUTPUT | Used to tell miniaudio that a node produces only | - | | silent output. This is useful for nodes where you | - | | don't want the output to contribute to the final | - | | mix. An example might be if you want split your | - | | stream and have one branch be output to a file. | - | | When using this flag, you should avoid writing to | - | | the output buffer of the node's processing | - | | callback because miniaudio will ignore it anyway. | - +-----------------------------------------+---------------------------------------------------+ - - -If you need to make a copy of an audio stream for effect processing you can use a splitter node -called `ma_splitter_node`. This takes has 1 input bus and splits the stream into 2 output buses. -You can use it like this: - - ```c - ma_splitter_node_config splitterNodeConfig = ma_splitter_node_config_init(channels); - - ma_splitter_node splitterNode; - result = ma_splitter_node_init(&nodeGraph, &splitterNodeConfig, NULL, &splitterNode); - if (result != MA_SUCCESS) { - // Failed to create node. - } - - // Attach your output buses to two different input buses (can be on two different nodes). - ma_node_attach_output_bus(&splitterNode, 0, ma_node_graph_get_endpoint(&nodeGraph), 0); // Attach directly to the endpoint. - ma_node_attach_output_bus(&splitterNode, 1, &myEffectNode, 0); // Attach to input bus 0 of some effect node. - ``` - -The volume of an output bus can be configured on a per-bus basis: - - ```c - ma_node_set_output_bus_volume(&splitterNode, 0, 0.5f); - ma_node_set_output_bus_volume(&splitterNode, 1, 0.5f); - ``` - -In the code above we're using the splitter node from before and changing the volume of each of the -copied streams. - -You can start and stop a node with the following: - - ```c - ma_node_set_state(&splitterNode, ma_node_state_started); // The default state. - ma_node_set_state(&splitterNode, ma_node_state_stopped); - ``` - -By default the node is in a started state, but since it won't be connected to anything won't -actually be invoked by the node graph until it's connected. When you stop a node, data will not be -read from any of its input connections. You can use this property to stop a group of sounds -atomically. - -You can configure the initial state of a node in it's config: - - ```c - nodeConfig.initialState = ma_node_state_stopped; - ``` - -Note that for the stock specialized nodes, all of their configs will have a `nodeConfig` member -which is the config to use with the base node. This is where the initial state can be configured -for specialized nodes: - - ```c - dataSourceNodeConfig.nodeConfig.initialState = ma_node_state_stopped; - ``` - -When using a specialized node like `ma_data_source_node` or `ma_splitter_node`, be sure to not -modify the `vtable` member of the `nodeConfig` object. - - -7.1. Timing ------------ -The node graph supports starting and stopping nodes at scheduled times. This is especially useful -for data source nodes where you want to get the node set up, but only start playback at a specific -time. There are two clocks: local and global. - -A local clock is per-node, whereas the global clock is per graph. Scheduling starts and stops can -only be done based on the global clock because the local clock will not be running while the node -is stopped. The global clocks advances whenever `ma_node_graph_read_pcm_frames()` is called. On the -other hand, the local clock only advances when the node's processing callback is fired, and is -advanced based on the output frame count. - -To retrieve the global time, use `ma_node_graph_get_time()`. The global time can be set with -`ma_node_graph_set_time()` which might be useful if you want to do seeking on a global timeline. -Getting and setting the local time is similar. Use `ma_node_get_time()` to retrieve the local time, -and `ma_node_set_time()` to set the local time. The global and local times will be advanced by the -audio thread, so care should be taken to avoid data races. Ideally you should avoid calling these -outside of the node processing callbacks which are always run on the audio thread. - -There is basic support for scheduling the starting and stopping of nodes. You can only schedule one -start and one stop at a time. This is mainly intended for putting nodes into a started or stopped -state in a frame-exact manner. Without this mechanism, starting and stopping of a node is limited -to the resolution of a call to `ma_node_graph_read_pcm_frames()` which would typically be in blocks -of several milliseconds. The following APIs can be used for scheduling node states: - - ```c - ma_node_set_state_time() - ma_node_get_state_time() - ``` - -The time is absolute and must be based on the global clock. An example is below: - - ```c - ma_node_set_state_time(&myNode, ma_node_state_started, sampleRate*1); // Delay starting to 1 second. - ma_node_set_state_time(&myNode, ma_node_state_stopped, sampleRate*5); // Delay stopping to 5 seconds. - ``` - -An example for changing the state using a relative time. - - ```c - ma_node_set_state_time(&myNode, ma_node_state_started, sampleRate*1 + ma_node_graph_get_time(&myNodeGraph)); - ma_node_set_state_time(&myNode, ma_node_state_stopped, sampleRate*5 + ma_node_graph_get_time(&myNodeGraph)); - ``` - -Note that due to the nature of multi-threading the times may not be 100% exact. If this is an -issue, consider scheduling state changes from within a processing callback. An idea might be to -have some kind of passthrough trigger node that is used specifically for tracking time and handling -events. - - - -7.2. Thread Safety and Locking ------------------------------- -When processing audio, it's ideal not to have any kind of locking in the audio thread. Since it's -expected that `ma_node_graph_read_pcm_frames()` would be run on the audio thread, it does so -without the use of any locks. This section discusses the implementation used by miniaudio and goes -over some of the compromises employed by miniaudio to achieve this goal. Note that the current -implementation may not be ideal - feedback and critiques are most welcome. - -The node graph API is not *entirely* lock-free. Only `ma_node_graph_read_pcm_frames()` is expected -to be lock-free. Attachment, detachment and uninitialization of nodes use locks to simplify the -implementation, but are crafted in a way such that such locking is not required when reading audio -data from the graph. Locking in these areas are achieved by means of spinlocks. - -The main complication with keeping `ma_node_graph_read_pcm_frames()` lock-free stems from the fact -that a node can be uninitialized, and it's memory potentially freed, while in the middle of being -processed on the audio thread. There are times when the audio thread will be referencing a node, -which means the uninitialization process of a node needs to make sure it delays returning until the -audio thread is finished so that control is not handed back to the caller thereby giving them a -chance to free the node's memory. - -When the audio thread is processing a node, it does so by reading from each of the output buses of -the node. In order for a node to process data for one of its output buses, it needs to read from -each of its input buses, and so on an so forth. It follows that once all output buses of a node -are detached, the node as a whole will be disconnected and no further processing will occur unless -it's output buses are reattached, which won't be happening when the node is being uninitialized. -By having `ma_node_detach_output_bus()` wait until the audio thread is finished with it, we can -simplify a few things, at the expense of making `ma_node_detach_output_bus()` a bit slower. By -doing this, the implementation of `ma_node_uninit()` becomes trivial - just detach all output -nodes, followed by each of the attachments to each of its input nodes, and then do any final clean -up. - -With the above design, the worst-case scenario is `ma_node_detach_output_bus()` taking as long as -it takes to process the output bus being detached. This will happen if it's called at just the -wrong moment where the audio thread has just iterated it and has just started processing. The -caller of `ma_node_detach_output_bus()` will stall until the audio thread is finished, which -includes the cost of recursively processing its inputs. This is the biggest compromise made with -the approach taken by miniaudio for its lock-free processing system. The cost of detaching nodes -earlier in the pipeline (data sources, for example) will be cheaper than the cost of detaching -higher level nodes, such as some kind of final post-processing endpoint. If you need to do mass -detachments, detach starting from the lowest level nodes and work your way towards the final -endpoint node (but don't try detaching the node graph's endpoint). If the audio thread is not -running, detachment will be fast and detachment in any order will be the same. The reason nodes -need to wait for their input attachments to complete is due to the potential for desyncs between -data sources. If the node was to terminate processing mid way through processing its inputs, -there's a chance that some of the underlying data sources will have been read, but then others not. -That will then result in a potential desynchronization when detaching and reattaching higher-level -nodes. A possible solution to this is to have an option when detaching to terminate processing -before processing all input attachments which should be fairly simple. - -Another compromise, albeit less significant, is locking when attaching and detaching nodes. This -locking is achieved by means of a spinlock in order to reduce memory overhead. A lock is present -for each input bus and output bus. When an output bus is connected to an input bus, both the output -bus and input bus is locked. This locking is specifically for attaching and detaching across -different threads and does not affect `ma_node_graph_read_pcm_frames()` in any way. The locking and -unlocking is mostly self-explanatory, but a slightly less intuitive aspect comes into it when -considering that iterating over attachments must not break as a result of attaching or detaching a -node while iteration is occurring. - -Attaching and detaching are both quite simple. When an output bus of a node is attached to an input -bus of another node, it's added to a linked list. Basically, an input bus is a linked list, where -each item in the list is and output bus. We have some intentional (and convenient) restrictions on -what can done with the linked list in order to simplify the implementation. First of all, whenever -something needs to iterate over the list, it must do so in a forward direction. Backwards iteration -is not supported. Also, items can only be added to the start of the list. - -The linked list is a doubly-linked list where each item in the list (an output bus) holds a pointer -to the next item in the list, and another to the previous item. A pointer to the previous item is -only required for fast detachment of the node - it is never used in iteration. This is an -important property because it means from the perspective of iteration, attaching and detaching of -an item can be done with a single atomic assignment. This is exploited by both the attachment and -detachment process. When attaching the node, the first thing that is done is the setting of the -local "next" and "previous" pointers of the node. After that, the item is "attached" to the list -by simply performing an atomic exchange with the head pointer. After that, the node is "attached" -to the list from the perspective of iteration. Even though the "previous" pointer of the next item -hasn't yet been set, from the perspective of iteration it's been attached because iteration will -only be happening in a forward direction which means the "previous" pointer won't actually ever get -used. The same general process applies to detachment. See `ma_node_attach_output_bus()` and -`ma_node_detach_output_bus()` for the implementation of this mechanism. - - - -8. Decoding -=========== -The `ma_decoder` API is used for reading audio files. Decoders are completely decoupled from -devices and can be used independently. Built-in support is included for the following formats: - - +---------+ - | Format | - +---------+ - | WAV | - | MP3 | - | FLAC | - +---------+ - -You can disable the built-in decoders by specifying one or more of the following options before the -miniaudio implementation: - - ```c - #define MA_NO_WAV - #define MA_NO_MP3 - #define MA_NO_FLAC - ``` - -miniaudio supports the ability to plug in custom decoders. See the section below for details on how -to use custom decoders. - -A decoder can be initialized from a file with `ma_decoder_init_file()`, a block of memory with -`ma_decoder_init_memory()`, or from data delivered via callbacks with `ma_decoder_init()`. Here is -an example for loading a decoder from a file: - - ```c - ma_decoder decoder; - ma_result result = ma_decoder_init_file("MySong.mp3", NULL, &decoder); - if (result != MA_SUCCESS) { - return false; // An error occurred. - } - - ... - - ma_decoder_uninit(&decoder); - ``` - -When initializing a decoder, you can optionally pass in a pointer to a `ma_decoder_config` object -(the `NULL` argument in the example above) which allows you to configure the output format, channel -count, sample rate and channel map: - - ```c - ma_decoder_config config = ma_decoder_config_init(ma_format_f32, 2, 48000); - ``` - -When passing in `NULL` for decoder config in `ma_decoder_init*()`, the output format will be the -same as that defined by the decoding backend. - -Data is read from the decoder as PCM frames. This will output the number of PCM frames actually -read. If this is less than the requested number of PCM frames it means you've reached the end. The -return value will be `MA_AT_END` if no samples have been read and the end has been reached. - - ```c - ma_result result = ma_decoder_read_pcm_frames(pDecoder, pFrames, framesToRead, &framesRead); - if (framesRead < framesToRead) { - // Reached the end. - } - ``` - -You can also seek to a specific frame like so: - - ```c - ma_result result = ma_decoder_seek_to_pcm_frame(pDecoder, targetFrame); - if (result != MA_SUCCESS) { - return false; // An error occurred. - } - ``` - -If you want to loop back to the start, you can simply seek back to the first PCM frame: - - ```c - ma_decoder_seek_to_pcm_frame(pDecoder, 0); - ``` - -When loading a decoder, miniaudio uses a trial and error technique to find the appropriate decoding -backend. This can be unnecessarily inefficient if the type is already known. In this case you can -use `encodingFormat` variable in the device config to specify a specific encoding format you want -to decode: - - ```c - decoderConfig.encodingFormat = ma_encoding_format_wav; - ``` - -See the `ma_encoding_format` enum for possible encoding formats. - -The `ma_decoder_init_file()` API will try using the file extension to determine which decoding -backend to prefer. - - -8.1. Custom Decoders --------------------- -It's possible to implement a custom decoder and plug it into miniaudio. This is extremely useful -when you want to use the `ma_decoder` API, but need to support an encoding format that's not one of -the stock formats supported by miniaudio. This can be put to particularly good use when using the -`ma_engine` and/or `ma_resource_manager` APIs because they use `ma_decoder` internally. If, for -example, you wanted to support Opus, you can do so with a custom decoder (there if a reference -Opus decoder in the "extras" folder of the miniaudio repository which uses libopus + libopusfile). - -A custom decoder must implement a data source. A vtable called `ma_decoding_backend_vtable` needs -to be implemented which is then passed into the decoder config: - - ```c - ma_decoding_backend_vtable* pCustomBackendVTables[] = - { - &g_ma_decoding_backend_vtable_libvorbis, - &g_ma_decoding_backend_vtable_libopus - }; - - ... - - decoderConfig = ma_decoder_config_init_default(); - decoderConfig.pCustomBackendUserData = NULL; - decoderConfig.ppCustomBackendVTables = pCustomBackendVTables; - decoderConfig.customBackendCount = sizeof(pCustomBackendVTables) / sizeof(pCustomBackendVTables[0]); - ``` - -The `ma_decoding_backend_vtable` vtable has the following functions: - - ``` - onInit - onInitFile - onInitFileW - onInitMemory - onUninit - ``` - -There are only two functions that must be implemented - `onInit` and `onUninit`. The other -functions can be implemented for a small optimization for loading from a file path or memory. If -these are not specified, miniaudio will deal with it for you via a generic implementation. - -When you initialize a custom data source (by implementing the `onInit` function in the vtable) you -will need to output a pointer to a `ma_data_source` which implements your custom decoder. See the -section about data sources for details on how to implement this. Alternatively, see the -"custom_decoders" example in the miniaudio repository. - -The `onInit` function takes a pointer to some callbacks for the purpose of reading raw audio data -from some arbitrary source. You'll use these functions to read from the raw data and perform the -decoding. When you call them, you will pass in the `pReadSeekTellUserData` pointer to the relevant -parameter. - -The `pConfig` parameter in `onInit` can be used to configure the backend if appropriate. It's only -used as a hint and can be ignored. However, if any of the properties are relevant to your decoder, -an optimal implementation will handle the relevant properties appropriately. - -If memory allocation is required, it should be done so via the specified allocation callbacks if -possible (the `pAllocationCallbacks` parameter). - -If an error occurs when initializing the decoder, you should leave `ppBackend` unset, or set to -NULL, and make sure everything is cleaned up appropriately and an appropriate result code returned. -When multiple custom backends are specified, miniaudio will cycle through the vtables in the order -they're listed in the array that's passed into the decoder config so it's important that your -initialization routine is clean. - -When a decoder is uninitialized, the `onUninit` callback will be fired which will give you an -opportunity to clean up and internal data. - - - -9. Encoding -=========== -The `ma_encoding` API is used for writing audio files. The only supported output format is WAV. -This can be disabled by specifying the following option before the implementation of miniaudio: - - ```c - #define MA_NO_WAV - ``` - -An encoder can be initialized to write to a file with `ma_encoder_init_file()` or from data -delivered via callbacks with `ma_encoder_init()`. Below is an example for initializing an encoder -to output to a file. - - ```c - ma_encoder_config config = ma_encoder_config_init(ma_encoding_format_wav, FORMAT, CHANNELS, SAMPLE_RATE); - ma_encoder encoder; - ma_result result = ma_encoder_init_file("my_file.wav", &config, &encoder); - if (result != MA_SUCCESS) { - // Error - } - - ... - - ma_encoder_uninit(&encoder); - ``` - -When initializing an encoder you must specify a config which is initialized with -`ma_encoder_config_init()`. Here you must specify the file type, the output sample format, output -channel count and output sample rate. The following file types are supported: - - +------------------------+-------------+ - | Enum | Description | - +------------------------+-------------+ - | ma_encoding_format_wav | WAV | - +------------------------+-------------+ - -If the format, channel count or sample rate is not supported by the output file type an error will -be returned. The encoder will not perform data conversion so you will need to convert it before -outputting any audio data. To output audio data, use `ma_encoder_write_pcm_frames()`, like in the -example below: - - ```c - ma_uint64 framesWritten; - result = ma_encoder_write_pcm_frames(&encoder, pPCMFramesToWrite, framesToWrite, &framesWritten); - if (result != MA_SUCCESS) { - ... handle error ... - } - ``` - -The `framesWritten` variable will contain the number of PCM frames that were actually written. This -is optionally and you can pass in `NULL` if you need this. - -Encoders must be uninitialized with `ma_encoder_uninit()`. - - - -10. Data Conversion -=================== -A data conversion API is included with miniaudio which supports the majority of data conversion -requirements. This supports conversion between sample formats, channel counts (with channel -mapping) and sample rates. - - -10.1. Sample Format Conversion ------------------------------- -Conversion between sample formats is achieved with the `ma_pcm_*_to_*()`, `ma_pcm_convert()` and -`ma_convert_pcm_frames_format()` APIs. Use `ma_pcm_*_to_*()` to convert between two specific -formats. Use `ma_pcm_convert()` to convert based on a `ma_format` variable. Use -`ma_convert_pcm_frames_format()` to convert PCM frames where you want to specify the frame count -and channel count as a variable instead of the total sample count. - - -10.1.1. Dithering ------------------ -Dithering can be set using the ditherMode parameter. - -The different dithering modes include the following, in order of efficiency: - - +-----------+--------------------------+ - | Type | Enum Token | - +-----------+--------------------------+ - | None | ma_dither_mode_none | - | Rectangle | ma_dither_mode_rectangle | - | Triangle | ma_dither_mode_triangle | - +-----------+--------------------------+ - -Note that even if the dither mode is set to something other than `ma_dither_mode_none`, it will be -ignored for conversions where dithering is not needed. Dithering is available for the following -conversions: - - ``` - s16 -> u8 - s24 -> u8 - s32 -> u8 - f32 -> u8 - s24 -> s16 - s32 -> s16 - f32 -> s16 - ``` - -Note that it is not an error to pass something other than ma_dither_mode_none for conversions where -dither is not used. It will just be ignored. - - - -10.2. Channel Conversion ------------------------- -Channel conversion is used for channel rearrangement and conversion from one channel count to -another. The `ma_channel_converter` API is used for channel conversion. Below is an example of -initializing a simple channel converter which converts from mono to stereo. - - ```c - ma_channel_converter_config config = ma_channel_converter_config_init( - ma_format, // Sample format - 1, // Input channels - NULL, // Input channel map - 2, // Output channels - NULL, // Output channel map - ma_channel_mix_mode_default); // The mixing algorithm to use when combining channels. - - result = ma_channel_converter_init(&config, NULL, &converter); - if (result != MA_SUCCESS) { - // Error. - } - ``` - -To perform the conversion simply call `ma_channel_converter_process_pcm_frames()` like so: - - ```c - ma_result result = ma_channel_converter_process_pcm_frames(&converter, pFramesOut, pFramesIn, frameCount); - if (result != MA_SUCCESS) { - // Error. - } - ``` - -It is up to the caller to ensure the output buffer is large enough to accommodate the new PCM -frames. - -Input and output PCM frames are always interleaved. Deinterleaved layouts are not supported. - - -10.2.1. Channel Mapping ------------------------ -In addition to converting from one channel count to another, like the example above, the channel -converter can also be used to rearrange channels. When initializing the channel converter, you can -optionally pass in channel maps for both the input and output frames. If the channel counts are the -same, and each channel map contains the same channel positions with the exception that they're in -a different order, a simple shuffling of the channels will be performed. If, however, there is not -a 1:1 mapping of channel positions, or the channel counts differ, the input channels will be mixed -based on a mixing mode which is specified when initializing the `ma_channel_converter_config` -object. - -When converting from mono to multi-channel, the mono channel is simply copied to each output -channel. When going the other way around, the audio of each output channel is simply averaged and -copied to the mono channel. - -In more complicated cases blending is used. The `ma_channel_mix_mode_simple` mode will drop excess -channels and silence extra channels. For example, converting from 4 to 2 channels, the 3rd and 4th -channels will be dropped, whereas converting from 2 to 4 channels will put silence into the 3rd and -4th channels. - -The `ma_channel_mix_mode_rectangle` mode uses spacial locality based on a rectangle to compute a -simple distribution between input and output. Imagine sitting in the middle of a room, with -speakers on the walls representing channel positions. The `MA_CHANNEL_FRONT_LEFT` position can be -thought of as being in the corner of the front and left walls. - -Finally, the `ma_channel_mix_mode_custom_weights` mode can be used to use custom user-defined -weights. Custom weights can be passed in as the last parameter of -`ma_channel_converter_config_init()`. - -Predefined channel maps can be retrieved with `ma_channel_map_init_standard()`. This takes a -`ma_standard_channel_map` enum as its first parameter, which can be one of the following: - - +-----------------------------------+-----------------------------------------------------------+ - | Name | Description | - +-----------------------------------+-----------------------------------------------------------+ - | ma_standard_channel_map_default | Default channel map used by miniaudio. See below. | - | ma_standard_channel_map_microsoft | Channel map used by Microsoft's bitfield channel maps. | - | ma_standard_channel_map_alsa | Default ALSA channel map. | - | ma_standard_channel_map_rfc3551 | RFC 3551. Based on AIFF. | - | ma_standard_channel_map_flac | FLAC channel map. | - | ma_standard_channel_map_vorbis | Vorbis channel map. | - | ma_standard_channel_map_sound4 | FreeBSD's sound(4). | - | ma_standard_channel_map_sndio | sndio channel map. http://www.sndio.org/tips.html. | - | ma_standard_channel_map_webaudio | https://webaudio.github.io/web-audio-api/#ChannelOrdering | - +-----------------------------------+-----------------------------------------------------------+ - -Below are the channel maps used by default in miniaudio (`ma_standard_channel_map_default`): - - +---------------+---------------------------------+ - | Channel Count | Mapping | - +---------------+---------------------------------+ - | 1 (Mono) | 0: MA_CHANNEL_MONO | - +---------------+---------------------------------+ - | 2 (Stereo) | 0: MA_CHANNEL_FRONT_LEFT
| - | | 1: MA_CHANNEL_FRONT_RIGHT | - +---------------+---------------------------------+ - | 3 | 0: MA_CHANNEL_FRONT_LEFT
| - | | 1: MA_CHANNEL_FRONT_RIGHT
| - | | 2: MA_CHANNEL_FRONT_CENTER | - +---------------+---------------------------------+ - | 4 (Surround) | 0: MA_CHANNEL_FRONT_LEFT
| - | | 1: MA_CHANNEL_FRONT_RIGHT
| - | | 2: MA_CHANNEL_FRONT_CENTER
| - | | 3: MA_CHANNEL_BACK_CENTER | - +---------------+---------------------------------+ - | 5 | 0: MA_CHANNEL_FRONT_LEFT
| - | | 1: MA_CHANNEL_FRONT_RIGHT
| - | | 2: MA_CHANNEL_FRONT_CENTER
| - | | 3: MA_CHANNEL_BACK_LEFT
| - | | 4: MA_CHANNEL_BACK_RIGHT | - +---------------+---------------------------------+ - | 6 (5.1) | 0: MA_CHANNEL_FRONT_LEFT
| - | | 1: MA_CHANNEL_FRONT_RIGHT
| - | | 2: MA_CHANNEL_FRONT_CENTER
| - | | 3: MA_CHANNEL_LFE
| - | | 4: MA_CHANNEL_SIDE_LEFT
| - | | 5: MA_CHANNEL_SIDE_RIGHT | - +---------------+---------------------------------+ - | 7 | 0: MA_CHANNEL_FRONT_LEFT
| - | | 1: MA_CHANNEL_FRONT_RIGHT
| - | | 2: MA_CHANNEL_FRONT_CENTER
| - | | 3: MA_CHANNEL_LFE
| - | | 4: MA_CHANNEL_BACK_CENTER
| - | | 4: MA_CHANNEL_SIDE_LEFT
| - | | 5: MA_CHANNEL_SIDE_RIGHT | - +---------------+---------------------------------+ - | 8 (7.1) | 0: MA_CHANNEL_FRONT_LEFT
| - | | 1: MA_CHANNEL_FRONT_RIGHT
| - | | 2: MA_CHANNEL_FRONT_CENTER
| - | | 3: MA_CHANNEL_LFE
| - | | 4: MA_CHANNEL_BACK_LEFT
| - | | 5: MA_CHANNEL_BACK_RIGHT
| - | | 6: MA_CHANNEL_SIDE_LEFT
| - | | 7: MA_CHANNEL_SIDE_RIGHT | - +---------------+---------------------------------+ - | Other | All channels set to 0. This | - | | is equivalent to the same | - | | mapping as the device. | - +---------------+---------------------------------+ - - - -10.3. Resampling ----------------- -Resampling is achieved with the `ma_resampler` object. To create a resampler object, do something -like the following: - - ```c - ma_resampler_config config = ma_resampler_config_init( - ma_format_s16, - channels, - sampleRateIn, - sampleRateOut, - ma_resample_algorithm_linear); - - ma_resampler resampler; - ma_result result = ma_resampler_init(&config, NULL, &resampler); - if (result != MA_SUCCESS) { - // An error occurred... - } - ``` - -Do the following to uninitialize the resampler: - - ```c - ma_resampler_uninit(&resampler); - ``` - -The following example shows how data can be processed - - ```c - ma_uint64 frameCountIn = 1000; - ma_uint64 frameCountOut = 2000; - ma_result result = ma_resampler_process_pcm_frames(&resampler, pFramesIn, &frameCountIn, pFramesOut, &frameCountOut); - if (result != MA_SUCCESS) { - // An error occurred... - } - - // At this point, frameCountIn contains the number of input frames that were consumed and frameCountOut contains the - // number of output frames written. - ``` - -To initialize the resampler you first need to set up a config (`ma_resampler_config`) with -`ma_resampler_config_init()`. You need to specify the sample format you want to use, the number of -channels, the input and output sample rate, and the algorithm. - -The sample format can be either `ma_format_s16` or `ma_format_f32`. If you need a different format -you will need to perform pre- and post-conversions yourself where necessary. Note that the format -is the same for both input and output. The format cannot be changed after initialization. - -The resampler supports multiple channels and is always interleaved (both input and output). The -channel count cannot be changed after initialization. - -The sample rates can be anything other than zero, and are always specified in hertz. They should be -set to something like 44100, etc. The sample rate is the only configuration property that can be -changed after initialization. - -The miniaudio resampler has built-in support for the following algorithms: - - +-----------+------------------------------+ - | Algorithm | Enum Token | - +-----------+------------------------------+ - | Linear | ma_resample_algorithm_linear | - | Custom | ma_resample_algorithm_custom | - +-----------+------------------------------+ - -The algorithm cannot be changed after initialization. - -Processing always happens on a per PCM frame basis and always assumes interleaved input and output. -De-interleaved processing is not supported. To process frames, use -`ma_resampler_process_pcm_frames()`. On input, this function takes the number of output frames you -can fit in the output buffer and the number of input frames contained in the input buffer. On -output these variables contain the number of output frames that were written to the output buffer -and the number of input frames that were consumed in the process. You can pass in NULL for the -input buffer in which case it will be treated as an infinitely large buffer of zeros. The output -buffer can also be NULL, in which case the processing will be treated as seek. - -The sample rate can be changed dynamically on the fly. You can change this with explicit sample -rates with `ma_resampler_set_rate()` and also with a decimal ratio with -`ma_resampler_set_rate_ratio()`. The ratio is in/out. - -Sometimes it's useful to know exactly how many input frames will be required to output a specific -number of frames. You can calculate this with `ma_resampler_get_required_input_frame_count()`. -Likewise, it's sometimes useful to know exactly how many frames would be output given a certain -number of input frames. You can do this with `ma_resampler_get_expected_output_frame_count()`. - -Due to the nature of how resampling works, the resampler introduces some latency. This can be -retrieved in terms of both the input rate and the output rate with -`ma_resampler_get_input_latency()` and `ma_resampler_get_output_latency()`. - - -10.3.1. Resampling Algorithms ------------------------------ -The choice of resampling algorithm depends on your situation and requirements. - - -10.3.1.1. Linear Resampling ---------------------------- -The linear resampler is the fastest, but comes at the expense of poorer quality. There is, however, -some control over the quality of the linear resampler which may make it a suitable option depending -on your requirements. - -The linear resampler performs low-pass filtering before or after downsampling or upsampling, -depending on the sample rates you're converting between. When decreasing the sample rate, the -low-pass filter will be applied before downsampling. When increasing the rate it will be performed -after upsampling. By default a fourth order low-pass filter will be applied. This can be configured -via the `lpfOrder` configuration variable. Setting this to 0 will disable filtering. - -The low-pass filter has a cutoff frequency which defaults to half the sample rate of the lowest of -the input and output sample rates (Nyquist Frequency). - -The API for the linear resampler is the same as the main resampler API, only it's called -`ma_linear_resampler`. - - -10.3.2. Custom Resamplers -------------------------- -You can implement a custom resampler by using the `ma_resample_algorithm_custom` resampling -algorithm and setting a vtable in the resampler config: - - ```c - ma_resampler_config config = ma_resampler_config_init(..., ma_resample_algorithm_custom); - config.pBackendVTable = &g_customResamplerVTable; - ``` - -Custom resamplers are useful if the stock algorithms are not appropriate for your use case. You -need to implement the required functions in `ma_resampling_backend_vtable`. Note that not all -functions in the vtable need to be implemented, but if it's possible to implement, they should be. - -You can use the `ma_linear_resampler` object for an example on how to implement the vtable. The -`onGetHeapSize` callback is used to calculate the size of any internal heap allocation the custom -resampler will need to make given the supplied config. When you initialize the resampler via the -`onInit` callback, you'll be given a pointer to a heap allocation which is where you should store -the heap allocated data. You should not free this data in `onUninit` because miniaudio will manage -it for you. - -The `onProcess` callback is where the actual resampling takes place. On input, `pFrameCountIn` -points to a variable containing the number of frames in the `pFramesIn` buffer and -`pFrameCountOut` points to a variable containing the capacity in frames of the `pFramesOut` buffer. -On output, `pFrameCountIn` should be set to the number of input frames that were fully consumed, -whereas `pFrameCountOut` should be set to the number of frames that were written to `pFramesOut`. - -The `onSetRate` callback is optional and is used for dynamically changing the sample rate. If -dynamic rate changes are not supported, you can set this callback to NULL. - -The `onGetInputLatency` and `onGetOutputLatency` functions are used for retrieving the latency in -input and output rates respectively. These can be NULL in which case latency calculations will be -assumed to be NULL. - -The `onGetRequiredInputFrameCount` callback is used to give miniaudio a hint as to how many input -frames are required to be available to produce the given number of output frames. Likewise, the -`onGetExpectedOutputFrameCount` callback is used to determine how many output frames will be -produced given the specified number of input frames. miniaudio will use these as a hint, but they -are optional and can be set to NULL if you're unable to implement them. - - - -10.4. General Data Conversion ------------------------------ -The `ma_data_converter` API can be used to wrap sample format conversion, channel conversion and -resampling into one operation. This is what miniaudio uses internally to convert between the format -requested when the device was initialized and the format of the backend's native device. The API -for general data conversion is very similar to the resampling API. Create a `ma_data_converter` -object like this: - - ```c - ma_data_converter_config config = ma_data_converter_config_init( - inputFormat, - outputFormat, - inputChannels, - outputChannels, - inputSampleRate, - outputSampleRate - ); - - ma_data_converter converter; - ma_result result = ma_data_converter_init(&config, NULL, &converter); - if (result != MA_SUCCESS) { - // An error occurred... - } - ``` - -In the example above we use `ma_data_converter_config_init()` to initialize the config, however -there's many more properties that can be configured, such as channel maps and resampling quality. -Something like the following may be more suitable depending on your requirements: - - ```c - ma_data_converter_config config = ma_data_converter_config_init_default(); - config.formatIn = inputFormat; - config.formatOut = outputFormat; - config.channelsIn = inputChannels; - config.channelsOut = outputChannels; - config.sampleRateIn = inputSampleRate; - config.sampleRateOut = outputSampleRate; - ma_channel_map_init_standard(ma_standard_channel_map_flac, config.channelMapIn, sizeof(config.channelMapIn)/sizeof(config.channelMapIn[0]), config.channelCountIn); - config.resampling.linear.lpfOrder = MA_MAX_FILTER_ORDER; - ``` - -Do the following to uninitialize the data converter: - - ```c - ma_data_converter_uninit(&converter, NULL); - ``` - -The following example shows how data can be processed - - ```c - ma_uint64 frameCountIn = 1000; - ma_uint64 frameCountOut = 2000; - ma_result result = ma_data_converter_process_pcm_frames(&converter, pFramesIn, &frameCountIn, pFramesOut, &frameCountOut); - if (result != MA_SUCCESS) { - // An error occurred... - } - - // At this point, frameCountIn contains the number of input frames that were consumed and frameCountOut contains the number - // of output frames written. - ``` - -The data converter supports multiple channels and is always interleaved (both input and output). -The channel count cannot be changed after initialization. - -Sample rates can be anything other than zero, and are always specified in hertz. They should be set -to something like 44100, etc. The sample rate is the only configuration property that can be -changed after initialization, but only if the `resampling.allowDynamicSampleRate` member of -`ma_data_converter_config` is set to `MA_TRUE`. To change the sample rate, use -`ma_data_converter_set_rate()` or `ma_data_converter_set_rate_ratio()`. The ratio must be in/out. -The resampling algorithm cannot be changed after initialization. - -Processing always happens on a per PCM frame basis and always assumes interleaved input and output. -De-interleaved processing is not supported. To process frames, use -`ma_data_converter_process_pcm_frames()`. On input, this function takes the number of output frames -you can fit in the output buffer and the number of input frames contained in the input buffer. On -output these variables contain the number of output frames that were written to the output buffer -and the number of input frames that were consumed in the process. You can pass in NULL for the -input buffer in which case it will be treated as an infinitely large -buffer of zeros. The output buffer can also be NULL, in which case the processing will be treated -as seek. - -Sometimes it's useful to know exactly how many input frames will be required to output a specific -number of frames. You can calculate this with `ma_data_converter_get_required_input_frame_count()`. -Likewise, it's sometimes useful to know exactly how many frames would be output given a certain -number of input frames. You can do this with `ma_data_converter_get_expected_output_frame_count()`. - -Due to the nature of how resampling works, the data converter introduces some latency if resampling -is required. This can be retrieved in terms of both the input rate and the output rate with -`ma_data_converter_get_input_latency()` and `ma_data_converter_get_output_latency()`. - - - -11. Filtering -============= - -11.1. Biquad Filtering ----------------------- -Biquad filtering is achieved with the `ma_biquad` API. Example: - - ```c - ma_biquad_config config = ma_biquad_config_init(ma_format_f32, channels, b0, b1, b2, a0, a1, a2); - ma_result result = ma_biquad_init(&config, NULL, &biquad); - if (result != MA_SUCCESS) { - // Error. - } - - ... - - ma_biquad_process_pcm_frames(&biquad, pFramesOut, pFramesIn, frameCount); - ``` - -Biquad filtering is implemented using transposed direct form 2. The numerator coefficients are b0, -b1 and b2, and the denominator coefficients are a0, a1 and a2. The a0 coefficient is required and -coefficients must not be pre-normalized. - -Supported formats are `ma_format_s16` and `ma_format_f32`. If you need to use a different format -you need to convert it yourself beforehand. When using `ma_format_s16` the biquad filter will use -fixed point arithmetic. When using `ma_format_f32`, floating point arithmetic will be used. - -Input and output frames are always interleaved. - -Filtering can be applied in-place by passing in the same pointer for both the input and output -buffers, like so: - - ```c - ma_biquad_process_pcm_frames(&biquad, pMyData, pMyData, frameCount); - ``` - -If you need to change the values of the coefficients, but maintain the values in the registers you -can do so with `ma_biquad_reinit()`. This is useful if you need to change the properties of the -filter while keeping the values of registers valid to avoid glitching. Do not use -`ma_biquad_init()` for this as it will do a full initialization which involves clearing the -registers to 0. Note that changing the format or channel count after initialization is invalid and -will result in an error. - - -11.2. Low-Pass Filtering ------------------------- -Low-pass filtering is achieved with the following APIs: - - +---------+------------------------------------------+ - | API | Description | - +---------+------------------------------------------+ - | ma_lpf1 | First order low-pass filter | - | ma_lpf2 | Second order low-pass filter | - | ma_lpf | High order low-pass filter (Butterworth) | - +---------+------------------------------------------+ - -Low-pass filter example: - - ```c - ma_lpf_config config = ma_lpf_config_init(ma_format_f32, channels, sampleRate, cutoffFrequency, order); - ma_result result = ma_lpf_init(&config, &lpf); - if (result != MA_SUCCESS) { - // Error. - } - - ... - - ma_lpf_process_pcm_frames(&lpf, pFramesOut, pFramesIn, frameCount); - ``` - -Supported formats are `ma_format_s16` and` ma_format_f32`. If you need to use a different format -you need to convert it yourself beforehand. Input and output frames are always interleaved. - -Filtering can be applied in-place by passing in the same pointer for both the input and output -buffers, like so: - - ```c - ma_lpf_process_pcm_frames(&lpf, pMyData, pMyData, frameCount); - ``` - -The maximum filter order is limited to `MA_MAX_FILTER_ORDER` which is set to 8. If you need more, -you can chain first and second order filters together. - - ```c - for (iFilter = 0; iFilter < filterCount; iFilter += 1) { - ma_lpf2_process_pcm_frames(&lpf2[iFilter], pMyData, pMyData, frameCount); - } - ``` - -If you need to change the configuration of the filter, but need to maintain the state of internal -registers you can do so with `ma_lpf_reinit()`. This may be useful if you need to change the sample -rate and/or cutoff frequency dynamically while maintaining smooth transitions. Note that changing the -format or channel count after initialization is invalid and will result in an error. - -The `ma_lpf` object supports a configurable order, but if you only need a first order filter you -may want to consider using `ma_lpf1`. Likewise, if you only need a second order filter you can use -`ma_lpf2`. The advantage of this is that they're lighter weight and a bit more efficient. - -If an even filter order is specified, a series of second order filters will be processed in a -chain. If an odd filter order is specified, a first order filter will be applied, followed by a -series of second order filters in a chain. - - -11.3. High-Pass Filtering -------------------------- -High-pass filtering is achieved with the following APIs: - - +---------+-------------------------------------------+ - | API | Description | - +---------+-------------------------------------------+ - | ma_hpf1 | First order high-pass filter | - | ma_hpf2 | Second order high-pass filter | - | ma_hpf | High order high-pass filter (Butterworth) | - +---------+-------------------------------------------+ - -High-pass filters work exactly the same as low-pass filters, only the APIs are called `ma_hpf1`, -`ma_hpf2` and `ma_hpf`. See example code for low-pass filters for example usage. - - -11.4. Band-Pass Filtering -------------------------- -Band-pass filtering is achieved with the following APIs: - - +---------+-------------------------------+ - | API | Description | - +---------+-------------------------------+ - | ma_bpf2 | Second order band-pass filter | - | ma_bpf | High order band-pass filter | - +---------+-------------------------------+ - -Band-pass filters work exactly the same as low-pass filters, only the APIs are called `ma_bpf2` and -`ma_hpf`. See example code for low-pass filters for example usage. Note that the order for -band-pass filters must be an even number which means there is no first order band-pass filter, -unlike low-pass and high-pass filters. - - -11.5. Notch Filtering ---------------------- -Notch filtering is achieved with the following APIs: - - +-----------+------------------------------------------+ - | API | Description | - +-----------+------------------------------------------+ - | ma_notch2 | Second order notching filter | - +-----------+------------------------------------------+ - - -11.6. Peaking EQ Filtering -------------------------- -Peaking filtering is achieved with the following APIs: - - +----------+------------------------------------------+ - | API | Description | - +----------+------------------------------------------+ - | ma_peak2 | Second order peaking filter | - +----------+------------------------------------------+ - - -11.7. Low Shelf Filtering -------------------------- -Low shelf filtering is achieved with the following APIs: - - +-------------+------------------------------------------+ - | API | Description | - +-------------+------------------------------------------+ - | ma_loshelf2 | Second order low shelf filter | - +-------------+------------------------------------------+ - -Where a high-pass filter is used to eliminate lower frequencies, a low shelf filter can be used to -just turn them down rather than eliminate them entirely. - - -11.8. High Shelf Filtering --------------------------- -High shelf filtering is achieved with the following APIs: - - +-------------+------------------------------------------+ - | API | Description | - +-------------+------------------------------------------+ - | ma_hishelf2 | Second order high shelf filter | - +-------------+------------------------------------------+ - -The high shelf filter has the same API as the low shelf filter, only you would use `ma_hishelf` -instead of `ma_loshelf`. Where a low shelf filter is used to adjust the volume of low frequencies, -the high shelf filter does the same thing for high frequencies. - - - - -12. Waveform and Noise Generation -================================= - -12.1. Waveforms ---------------- -miniaudio supports generation of sine, square, triangle and sawtooth waveforms. This is achieved -with the `ma_waveform` API. Example: - - ```c - ma_waveform_config config = ma_waveform_config_init( - FORMAT, - CHANNELS, - SAMPLE_RATE, - ma_waveform_type_sine, - amplitude, - frequency); - - ma_waveform waveform; - ma_result result = ma_waveform_init(&config, &waveform); - if (result != MA_SUCCESS) { - // Error. - } - - ... - - ma_waveform_read_pcm_frames(&waveform, pOutput, frameCount); - ``` - -The amplitude, frequency, type, and sample rate can be changed dynamically with -`ma_waveform_set_amplitude()`, `ma_waveform_set_frequency()`, `ma_waveform_set_type()`, and -`ma_waveform_set_sample_rate()` respectively. - -You can invert the waveform by setting the amplitude to a negative value. You can use this to -control whether or not a sawtooth has a positive or negative ramp, for example. - -Below are the supported waveform types: - - +---------------------------+ - | Enum Name | - +---------------------------+ - | ma_waveform_type_sine | - | ma_waveform_type_square | - | ma_waveform_type_triangle | - | ma_waveform_type_sawtooth | - +---------------------------+ - - - -12.2. Noise ------------ -miniaudio supports generation of white, pink and Brownian noise via the `ma_noise` API. Example: - - ```c - ma_noise_config config = ma_noise_config_init( - FORMAT, - CHANNELS, - ma_noise_type_white, - SEED, - amplitude); - - ma_noise noise; - ma_result result = ma_noise_init(&config, &noise); - if (result != MA_SUCCESS) { - // Error. - } - - ... - - ma_noise_read_pcm_frames(&noise, pOutput, frameCount); - ``` - -The noise API uses simple LCG random number generation. It supports a custom seed which is useful -for things like automated testing requiring reproducibility. Setting the seed to zero will default -to `MA_DEFAULT_LCG_SEED`. - -The amplitude and seed can be changed dynamically with `ma_noise_set_amplitude()` and -`ma_noise_set_seed()` respectively. - -By default, the noise API will use different values for different channels. So, for example, the -left side in a stereo stream will be different to the right side. To instead have each channel use -the same random value, set the `duplicateChannels` member of the noise config to true, like so: - - ```c - config.duplicateChannels = MA_TRUE; - ``` - -Below are the supported noise types. - - +------------------------+ - | Enum Name | - +------------------------+ - | ma_noise_type_white | - | ma_noise_type_pink | - | ma_noise_type_brownian | - +------------------------+ - - - -13. Audio Buffers -================= -miniaudio supports reading from a buffer of raw audio data via the `ma_audio_buffer` API. This can -read from memory that's managed by the application, but can also handle the memory management for -you internally. Memory management is flexible and should support most use cases. - -Audio buffers are initialized using the standard configuration system used everywhere in miniaudio: - - ```c - ma_audio_buffer_config config = ma_audio_buffer_config_init( - format, - channels, - sizeInFrames, - pExistingData, - &allocationCallbacks); - - ma_audio_buffer buffer; - result = ma_audio_buffer_init(&config, &buffer); - if (result != MA_SUCCESS) { - // Error. - } - - ... - - ma_audio_buffer_uninit(&buffer); - ``` - -In the example above, the memory pointed to by `pExistingData` will *not* be copied and is how an -application can do self-managed memory allocation. If you would rather make a copy of the data, use -`ma_audio_buffer_init_copy()`. To uninitialize the buffer, use `ma_audio_buffer_uninit()`. - -Sometimes it can be convenient to allocate the memory for the `ma_audio_buffer` structure and the -raw audio data in a contiguous block of memory. That is, the raw audio data will be located -immediately after the `ma_audio_buffer` structure. To do this, use -`ma_audio_buffer_alloc_and_init()`: - - ```c - ma_audio_buffer_config config = ma_audio_buffer_config_init( - format, - channels, - sizeInFrames, - pExistingData, - &allocationCallbacks); - - ma_audio_buffer* pBuffer - result = ma_audio_buffer_alloc_and_init(&config, &pBuffer); - if (result != MA_SUCCESS) { - // Error - } - - ... - - ma_audio_buffer_uninit_and_free(&buffer); - ``` - -If you initialize the buffer with `ma_audio_buffer_alloc_and_init()` you should uninitialize it -with `ma_audio_buffer_uninit_and_free()`. In the example above, the memory pointed to by -`pExistingData` will be copied into the buffer, which is contrary to the behavior of -`ma_audio_buffer_init()`. - -An audio buffer has a playback cursor just like a decoder. As you read frames from the buffer, the -cursor moves forward. The last parameter (`loop`) can be used to determine if the buffer should -loop. The return value is the number of frames actually read. If this is less than the number of -frames requested it means the end has been reached. This should never happen if the `loop` -parameter is set to true. If you want to manually loop back to the start, you can do so with with -`ma_audio_buffer_seek_to_pcm_frame(pAudioBuffer, 0)`. Below is an example for reading data from an -audio buffer. - - ```c - ma_uint64 framesRead = ma_audio_buffer_read_pcm_frames(pAudioBuffer, pFramesOut, desiredFrameCount, isLooping); - if (framesRead < desiredFrameCount) { - // If not looping, this means the end has been reached. This should never happen in looping mode with valid input. - } - ``` - -Sometimes you may want to avoid the cost of data movement between the internal buffer and the -output buffer. Instead you can use memory mapping to retrieve a pointer to a segment of data: - - ```c - void* pMappedFrames; - ma_uint64 frameCount = frameCountToTryMapping; - ma_result result = ma_audio_buffer_map(pAudioBuffer, &pMappedFrames, &frameCount); - if (result == MA_SUCCESS) { - // Map was successful. The value in frameCount will be how many frames were _actually_ mapped, which may be - // less due to the end of the buffer being reached. - ma_copy_pcm_frames(pFramesOut, pMappedFrames, frameCount, pAudioBuffer->format, pAudioBuffer->channels); - - // You must unmap the buffer. - ma_audio_buffer_unmap(pAudioBuffer, frameCount); - } - ``` - -When you use memory mapping, the read cursor is increment by the frame count passed in to -`ma_audio_buffer_unmap()`. If you decide not to process every frame you can pass in a value smaller -than the value returned by `ma_audio_buffer_map()`. The disadvantage to using memory mapping is -that it does not handle looping for you. You can determine if the buffer is at the end for the -purpose of looping with `ma_audio_buffer_at_end()` or by inspecting the return value of -`ma_audio_buffer_unmap()` and checking if it equals `MA_AT_END`. You should not treat `MA_AT_END` -as an error when returned by `ma_audio_buffer_unmap()`. - - - -14. Ring Buffers -================ -miniaudio supports lock free (single producer, single consumer) ring buffers which are exposed via -the `ma_rb` and `ma_pcm_rb` APIs. The `ma_rb` API operates on bytes, whereas the `ma_pcm_rb` -operates on PCM frames. They are otherwise identical as `ma_pcm_rb` is just a wrapper around -`ma_rb`. - -Unlike most other APIs in miniaudio, ring buffers support both interleaved and deinterleaved -streams. The caller can also allocate their own backing memory for the ring buffer to use -internally for added flexibility. Otherwise the ring buffer will manage it's internal memory for -you. - -The examples below use the PCM frame variant of the ring buffer since that's most likely the one -you will want to use. To initialize a ring buffer, do something like the following: - - ```c - ma_pcm_rb rb; - ma_result result = ma_pcm_rb_init(FORMAT, CHANNELS, BUFFER_SIZE_IN_FRAMES, NULL, NULL, &rb); - if (result != MA_SUCCESS) { - // Error - } - ``` - -The `ma_pcm_rb_init()` function takes the sample format and channel count as parameters because -it's the PCM variant of the ring buffer API. For the regular ring buffer that operates on bytes you -would call `ma_rb_init()` which leaves these out and just takes the size of the buffer in bytes -instead of frames. The fourth parameter is an optional pre-allocated buffer and the fifth parameter -is a pointer to a `ma_allocation_callbacks` structure for custom memory allocation routines. -Passing in `NULL` for this results in `MA_MALLOC()` and `MA_FREE()` being used. - -Use `ma_pcm_rb_init_ex()` if you need a deinterleaved buffer. The data for each sub-buffer is -offset from each other based on the stride. To manage your sub-buffers you can use -`ma_pcm_rb_get_subbuffer_stride()`, `ma_pcm_rb_get_subbuffer_offset()` and -`ma_pcm_rb_get_subbuffer_ptr()`. - -Use `ma_pcm_rb_acquire_read()` and `ma_pcm_rb_acquire_write()` to retrieve a pointer to a section -of the ring buffer. You specify the number of frames you need, and on output it will set to what -was actually acquired. If the read or write pointer is positioned such that the number of frames -requested will require a loop, it will be clamped to the end of the buffer. Therefore, the number -of frames you're given may be less than the number you requested. - -After calling `ma_pcm_rb_acquire_read()` or `ma_pcm_rb_acquire_write()`, you do your work on the -buffer and then "commit" it with `ma_pcm_rb_commit_read()` or `ma_pcm_rb_commit_write()`. This is -where the read/write pointers are updated. When you commit you need to pass in the buffer that was -returned by the earlier call to `ma_pcm_rb_acquire_read()` or `ma_pcm_rb_acquire_write()` and is -only used for validation. The number of frames passed to `ma_pcm_rb_commit_read()` and -`ma_pcm_rb_commit_write()` is what's used to increment the pointers, and can be less that what was -originally requested. - -If you want to correct for drift between the write pointer and the read pointer you can use a -combination of `ma_pcm_rb_pointer_distance()`, `ma_pcm_rb_seek_read()` and -`ma_pcm_rb_seek_write()`. Note that you can only move the pointers forward, and you should only -move the read pointer forward via the consumer thread, and the write pointer forward by the -producer thread. If there is too much space between the pointers, move the read pointer forward. If -there is too little space between the pointers, move the write pointer forward. - -You can use a ring buffer at the byte level instead of the PCM frame level by using the `ma_rb` -API. This is exactly the same, only you will use the `ma_rb` functions instead of `ma_pcm_rb` and -instead of frame counts you will pass around byte counts. - -The maximum size of the buffer in bytes is `0x7FFFFFFF-(MA_SIMD_ALIGNMENT-1)` due to the most -significant bit being used to encode a loop flag and the internally managed buffers always being -aligned to `MA_SIMD_ALIGNMENT`. - -Note that the ring buffer is only thread safe when used by a single consumer thread and single -producer thread. - - - -15. Backends -============ -The following backends are supported by miniaudio. These are listed in order of default priority. -When no backend is specified when initializing a context or device, miniaudio will attempt to use -each of these backends in the order listed in the table below. - -Note that backends that are not usable by the build target will not be included in the build. For -example, ALSA, which is specific to Linux, will not be included in the Windows build. - - +-------------+-----------------------+--------------------------------------------------------+ - | Name | Enum Name | Supported Operating Systems | - +-------------+-----------------------+--------------------------------------------------------+ - | WASAPI | ma_backend_wasapi | Windows Vista+ | - | DirectSound | ma_backend_dsound | Windows XP+ | - | WinMM | ma_backend_winmm | Windows 95+ | - | Core Audio | ma_backend_coreaudio | macOS, iOS | - | sndio | ma_backend_sndio | OpenBSD | - | audio(4) | ma_backend_audio4 | NetBSD, OpenBSD | - | OSS | ma_backend_oss | FreeBSD | - | PulseAudio | ma_backend_pulseaudio | Cross Platform (disabled on Windows, BSD and Android) | - | ALSA | ma_backend_alsa | Linux | - | JACK | ma_backend_jack | Cross Platform (disabled on BSD and Android) | - | AAudio | ma_backend_aaudio | Android 8+ | - | OpenSL ES | ma_backend_opensl | Android (API level 16+) | - | Web Audio | ma_backend_webaudio | Web (via Emscripten) | - | Custom | ma_backend_custom | Cross Platform | - | Null | ma_backend_null | Cross Platform (not used on Web) | - +-------------+-----------------------+--------------------------------------------------------+ - -Some backends have some nuance details you may want to be aware of. - -15.1. WASAPI ------------- -- Low-latency shared mode will be disabled when using an application-defined sample rate which is - different to the device's native sample rate. To work around this, set `wasapi.noAutoConvertSRC` - to true in the device config. This is due to IAudioClient3_InitializeSharedAudioStream() failing - when the `AUDCLNT_STREAMFLAGS_AUTOCONVERTPCM` flag is specified. Setting wasapi.noAutoConvertSRC - will result in miniaudio's internal resampler being used instead which will in turn enable the - use of low-latency shared mode. - -15.2. PulseAudio ----------------- -- If you experience bad glitching/noise on Arch Linux, consider this fix from the Arch wiki: - https://wiki.archlinux.org/index.php/PulseAudio/Troubleshooting#Glitches,_skips_or_crackling. - Alternatively, consider using a different backend such as ALSA. - -15.3. Android -------------- -- To capture audio on Android, remember to add the RECORD_AUDIO permission to your manifest: - `` -- With OpenSL|ES, only a single ma_context can be active at any given time. This is due to a - limitation with OpenSL|ES. -- With AAudio, only default devices are enumerated. This is due to AAudio not having an enumeration - API (devices are enumerated through Java). You can however perform your own device enumeration - through Java and then set the ID in the ma_device_id structure (ma_device_id.aaudio) and pass it - to ma_device_init(). -- The backend API will perform resampling where possible. The reason for this as opposed to using - miniaudio's built-in resampler is to take advantage of any potential device-specific - optimizations the driver may implement. - -BSD ---- -- The sndio backend is currently only enabled on OpenBSD builds. -- The audio(4) backend is supported on OpenBSD, but you may need to disable sndiod before you can - use it. - -15.4. UWP ---------- -- UWP only supports default playback and capture devices. -- UWP requires the Microphone capability to be enabled in the application's manifest (Package.appxmanifest): - - ``` - - ... - - - - - ``` - -15.5. Web Audio / Emscripten ----------------------------- -- You cannot use `-std=c*` compiler flags, nor `-ansi`. This only applies to the Emscripten build. -- The first time a context is initialized it will create a global object called "miniaudio" whose - primary purpose is to act as a factory for device objects. -- Currently the Web Audio backend uses ScriptProcessorNode's, but this may need to change later as - they've been deprecated. -- Google has implemented a policy in their browsers that prevent automatic media output without - first receiving some kind of user input. The following web page has additional details: - https://developers.google.com/web/updates/2017/09/autoplay-policy-changes. Starting the device - may fail if you try to start playback without first handling some kind of user input. - - - -16. Optimization Tips -===================== -See below for some tips on improving performance. - -16.1. Low Level API -------------------- -- In the data callback, if your data is already clipped prior to copying it into the output buffer, - set the `noClip` config option in the device config to true. This will disable miniaudio's built - in clipping function. -- By default, miniaudio will pre-silence the data callback's output buffer. If you know that you - will always write valid data to the output buffer you can disable pre-silencing by setting the - `noPreSilence` config option in the device config to true. - -16.2. High Level API --------------------- -- If a sound does not require doppler or pitch shifting, consider disabling pitching by - initializing the sound with the `MA_SOUND_FLAG_NO_PITCH` flag. -- If a sound does not require spatialization, disable it by initializing the sound with the - `MA_SOUND_FLAG_NO_SPATIALIZATION` flag. It can be re-enabled again post-initialization with - `ma_sound_set_spatialization_enabled()`. -- If you know all of your sounds will always be the same sample rate, set the engine's sample - rate to match that of the sounds. Likewise, if you're using a self-managed resource manager, - consider setting the decoded sample rate to match your sounds. By configuring everything to - use a consistent sample rate, sample rate conversion can be avoided. - - - -17. Miscellaneous Notes -======================= -- Automatic stream routing is enabled on a per-backend basis. Support is explicitly enabled for - WASAPI and Core Audio, however other backends such as PulseAudio may naturally support it, though - not all have been tested. -- When compiling with VC6 and earlier, decoding is restricted to files less than 2GB in size. This - is due to 64-bit file APIs not being available. -*/ - -#ifndef miniaudio_h -#define miniaudio_h - -#ifdef __cplusplus -extern "C" { -#endif - -#define MA_STRINGIFY(x) #x -#define MA_XSTRINGIFY(x) MA_STRINGIFY(x) - -#define MA_VERSION_MAJOR 0 -#define MA_VERSION_MINOR 11 -#define MA_VERSION_REVISION 22 -#define MA_VERSION_STRING MA_XSTRINGIFY(MA_VERSION_MAJOR) "." MA_XSTRINGIFY(MA_VERSION_MINOR) "." MA_XSTRINGIFY(MA_VERSION_REVISION) - -#if defined(_MSC_VER) && !defined(__clang__) - #pragma warning(push) - #pragma warning(disable:4201) /* nonstandard extension used: nameless struct/union */ - #pragma warning(disable:4214) /* nonstandard extension used: bit field types other than int */ - #pragma warning(disable:4324) /* structure was padded due to alignment specifier */ -#elif defined(__clang__) || (defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 8))) - #pragma GCC diagnostic push - #pragma GCC diagnostic ignored "-Wpedantic" /* For ISO C99 doesn't support unnamed structs/unions [-Wpedantic] */ - #if defined(__clang__) - #pragma GCC diagnostic ignored "-Wc11-extensions" /* anonymous unions are a C11 extension */ - #endif -#endif - - -#if defined(__LP64__) || defined(_WIN64) || (defined(__x86_64__) && !defined(__ILP32__)) || defined(_M_X64) || defined(__ia64) || defined(_M_IA64) || defined(__aarch64__) || defined(_M_ARM64) || defined(__powerpc64__) || defined(__ppc64__) - #define MA_SIZEOF_PTR 8 -#else - #define MA_SIZEOF_PTR 4 -#endif - -#include /* For size_t. */ - -/* Sized types. */ -#if defined(MA_USE_STDINT) - #include - typedef int8_t ma_int8; - typedef uint8_t ma_uint8; - typedef int16_t ma_int16; - typedef uint16_t ma_uint16; - typedef int32_t ma_int32; - typedef uint32_t ma_uint32; - typedef int64_t ma_int64; - typedef uint64_t ma_uint64; -#else - typedef signed char ma_int8; - typedef unsigned char ma_uint8; - typedef signed short ma_int16; - typedef unsigned short ma_uint16; - typedef signed int ma_int32; - typedef unsigned int ma_uint32; - #if defined(_MSC_VER) && !defined(__clang__) - typedef signed __int64 ma_int64; - typedef unsigned __int64 ma_uint64; - #else - #if defined(__clang__) || (defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6))) - #pragma GCC diagnostic push - #pragma GCC diagnostic ignored "-Wlong-long" - #if defined(__clang__) - #pragma GCC diagnostic ignored "-Wc++11-long-long" - #endif - #endif - typedef signed long long ma_int64; - typedef unsigned long long ma_uint64; - #if defined(__clang__) || (defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6))) - #pragma GCC diagnostic pop - #endif - #endif -#endif /* MA_USE_STDINT */ - -#if MA_SIZEOF_PTR == 8 - typedef ma_uint64 ma_uintptr; -#else - typedef ma_uint32 ma_uintptr; -#endif - -typedef ma_uint8 ma_bool8; -typedef ma_uint32 ma_bool32; -#define MA_TRUE 1 -#define MA_FALSE 0 - -/* These float types are not used universally by miniaudio. It's to simplify some macro expansion for atomic types. */ -typedef float ma_float; -typedef double ma_double; - -typedef void* ma_handle; -typedef void* ma_ptr; - -/* -ma_proc is annoying because when compiling with GCC we get pedantic warnings about converting -between `void*` and `void (*)()`. We can't use `void (*)()` with MSVC however, because we'll get -warning C4191 about "type cast between incompatible function types". To work around this I'm going -to use a different data type depending on the compiler. -*/ -#if defined(__GNUC__) -typedef void (*ma_proc)(void); -#else -typedef void* ma_proc; -#endif - -#if defined(_MSC_VER) && !defined(_WCHAR_T_DEFINED) -typedef ma_uint16 wchar_t; -#endif - -/* Define NULL for some compilers. */ -#ifndef NULL -#define NULL 0 -#endif - -#if defined(SIZE_MAX) - #define MA_SIZE_MAX SIZE_MAX -#else - #define MA_SIZE_MAX 0xFFFFFFFF /* When SIZE_MAX is not defined by the standard library just default to the maximum 32-bit unsigned integer. */ -#endif - - -/* Platform/backend detection. */ -#if defined(_WIN32) || defined(__COSMOPOLITAN__) - #define MA_WIN32 - #if defined(MA_FORCE_UWP) || (defined(WINAPI_FAMILY) && ((defined(WINAPI_FAMILY_PC_APP) && WINAPI_FAMILY == WINAPI_FAMILY_PC_APP) || (defined(WINAPI_FAMILY_PHONE_APP) && WINAPI_FAMILY == WINAPI_FAMILY_PHONE_APP))) - #define MA_WIN32_UWP - #elif defined(WINAPI_FAMILY) && (defined(WINAPI_FAMILY_GAMES) && WINAPI_FAMILY == WINAPI_FAMILY_GAMES) - #define MA_WIN32_GDK - #else - #define MA_WIN32_DESKTOP - #endif -#endif -#if !defined(_WIN32) /* If it's not Win32, assume POSIX. */ - #define MA_POSIX - - /* - Use the MA_NO_PTHREAD_IN_HEADER option at your own risk. This is intentionally undocumented. - You can use this to avoid including pthread.h in the header section. The downside is that it - results in some fixed sized structures being declared for the various types that are used in - miniaudio. The risk here is that these types might be too small for a given platform. This - risk is yours to take and no support will be offered if you enable this option. - */ - #ifndef MA_NO_PTHREAD_IN_HEADER - #include /* Unfortunate #include, but needed for pthread_t, pthread_mutex_t and pthread_cond_t types. */ - typedef pthread_t ma_pthread_t; - typedef pthread_mutex_t ma_pthread_mutex_t; - typedef pthread_cond_t ma_pthread_cond_t; - #else - typedef ma_uintptr ma_pthread_t; - typedef union ma_pthread_mutex_t { char __data[40]; ma_uint64 __alignment; } ma_pthread_mutex_t; - typedef union ma_pthread_cond_t { char __data[48]; ma_uint64 __alignment; } ma_pthread_cond_t; - #endif - - #if defined(__unix__) - #define MA_UNIX - #endif - #if defined(__linux__) - #define MA_LINUX - #endif - #if defined(__APPLE__) - #define MA_APPLE - #endif - #if defined(__DragonFly__) || defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__) - #define MA_BSD - #endif - #if defined(__ANDROID__) - #define MA_ANDROID - #endif - #if defined(__EMSCRIPTEN__) - #define MA_EMSCRIPTEN - #endif - #if defined(__ORBIS__) - #define MA_ORBIS - #endif - #if defined(__PROSPERO__) - #define MA_PROSPERO - #endif - #if defined(__NX__) - #define MA_NX - #endif - #if defined(__BEOS__) || defined(__HAIKU__) - #define MA_BEOS - #endif - #if defined(__HAIKU__) - #define MA_HAIKU - #endif -#endif - -#if defined(__has_c_attribute) - #if __has_c_attribute(fallthrough) - #define MA_FALLTHROUGH [[fallthrough]] - #endif -#endif -#if !defined(MA_FALLTHROUGH) && defined(__has_attribute) && (defined(__clang__) || defined(__GNUC__)) - #if __has_attribute(fallthrough) - #define MA_FALLTHROUGH __attribute__((fallthrough)) - #endif -#endif -#if !defined(MA_FALLTHROUGH) - #define MA_FALLTHROUGH ((void)0) -#endif - -#ifdef _MSC_VER - #define MA_INLINE __forceinline - - /* noinline was introduced in Visual Studio 2005. */ - #if _MSC_VER >= 1400 - #define MA_NO_INLINE __declspec(noinline) - #else - #define MA_NO_INLINE - #endif -#elif defined(__GNUC__) - /* - I've had a bug report where GCC is emitting warnings about functions possibly not being inlineable. This warning happens when - the __attribute__((always_inline)) attribute is defined without an "inline" statement. I think therefore there must be some - case where "__inline__" is not always defined, thus the compiler emitting these warnings. When using -std=c89 or -ansi on the - command line, we cannot use the "inline" keyword and instead need to use "__inline__". In an attempt to work around this issue - I am using "__inline__" only when we're compiling in strict ANSI mode. - */ - #if defined(__STRICT_ANSI__) - #define MA_GNUC_INLINE_HINT __inline__ - #else - #define MA_GNUC_INLINE_HINT inline - #endif - - #if (__GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 2)) || defined(__clang__) - #define MA_INLINE MA_GNUC_INLINE_HINT __attribute__((always_inline)) - #define MA_NO_INLINE __attribute__((noinline)) - #else - #define MA_INLINE MA_GNUC_INLINE_HINT - #define MA_NO_INLINE __attribute__((noinline)) - #endif -#elif defined(__WATCOMC__) - #define MA_INLINE __inline - #define MA_NO_INLINE -#else - #define MA_INLINE - #define MA_NO_INLINE -#endif - -/* MA_DLL is not officially supported. You're on your own if you want to use this. */ -#if defined(MA_DLL) - #if defined(_WIN32) - #define MA_DLL_IMPORT __declspec(dllimport) - #define MA_DLL_EXPORT __declspec(dllexport) - #define MA_DLL_PRIVATE static - #else - #if defined(__GNUC__) && __GNUC__ >= 4 - #define MA_DLL_IMPORT __attribute__((visibility("default"))) - #define MA_DLL_EXPORT __attribute__((visibility("default"))) - #define MA_DLL_PRIVATE __attribute__((visibility("hidden"))) - #else - #define MA_DLL_IMPORT - #define MA_DLL_EXPORT - #define MA_DLL_PRIVATE static - #endif - #endif -#endif - -#if !defined(MA_API) - #if defined(MA_DLL) - #if defined(MINIAUDIO_IMPLEMENTATION) || defined(MA_IMPLEMENTATION) - #define MA_API MA_DLL_EXPORT - #else - #define MA_API MA_DLL_IMPORT - #endif - #else - #define MA_API extern - #endif -#endif - -#if !defined(MA_STATIC) - #if defined(MA_DLL) - #define MA_PRIVATE MA_DLL_PRIVATE - #else - #define MA_PRIVATE static - #endif -#endif - - -/* SIMD alignment in bytes. Currently set to 32 bytes in preparation for future AVX optimizations. */ -#define MA_SIMD_ALIGNMENT 32 - -/* -Special wchar_t type to ensure any structures in the public sections that reference it have a -consistent size across all platforms. - -On Windows, wchar_t is 2 bytes, whereas everywhere else it's 4 bytes. Since Windows likes to use -wchar_t for its IDs, we need a special explicitly sized wchar type that is always 2 bytes on all -platforms. -*/ -#if !defined(MA_POSIX) && defined(MA_WIN32) -typedef wchar_t ma_wchar_win32; -#else -typedef ma_uint16 ma_wchar_win32; -#endif - - - -/* -Logging Levels -============== -Log levels are only used to give logging callbacks some context as to the severity of a log message -so they can do filtering. All log levels will be posted to registered logging callbacks. If you -don't want to output a certain log level you can discriminate against the log level in the callback. - -MA_LOG_LEVEL_DEBUG - Used for debugging. Useful for debug and test builds, but should be disabled in release builds. - -MA_LOG_LEVEL_INFO - Informational logging. Useful for debugging. This will never be called from within the data - callback. - -MA_LOG_LEVEL_WARNING - Warnings. You should enable this in you development builds and action them when encountered. These - logs usually indicate a potential problem or misconfiguration, but still allow you to keep - running. This will never be called from within the data callback. - -MA_LOG_LEVEL_ERROR - Error logging. This will be fired when an operation fails and is subsequently aborted. This can - be fired from within the data callback, in which case the device will be stopped. You should - always have this log level enabled. -*/ -typedef enum -{ - MA_LOG_LEVEL_DEBUG = 4, - MA_LOG_LEVEL_INFO = 3, - MA_LOG_LEVEL_WARNING = 2, - MA_LOG_LEVEL_ERROR = 1 -} ma_log_level; - -/* -Variables needing to be accessed atomically should be declared with this macro for two reasons: - - 1) It allows people who read the code to identify a variable as such; and - 2) It forces alignment on platforms where it's required or optimal. - -Note that for x86/64, alignment is not strictly necessary, but does have some performance -implications. Where supported by the compiler, alignment will be used, but otherwise if the CPU -architecture does not require it, it will simply leave it unaligned. This is the case with old -versions of Visual Studio, which I've confirmed with at least VC6. -*/ -#if !defined(_MSC_VER) && defined (__STDC_VERSION__) && (__STDC_VERSION__ >= 201112L) - #include - #define MA_ATOMIC(alignment, type) _Alignas(alignment) type -#else - #if defined(__GNUC__) - /* GCC-style compilers. */ - #define MA_ATOMIC(alignment, type) type __attribute__((aligned(alignment))) - #elif defined(_MSC_VER) && _MSC_VER > 1200 /* 1200 = VC6. Alignment not supported, but not necessary because x86 is the only supported target. */ - /* MSVC. */ - #define MA_ATOMIC(alignment, type) __declspec(align(alignment)) type - #else - /* Other compilers. */ - #define MA_ATOMIC(alignment, type) type - #endif -#endif - -typedef struct ma_context ma_context; -typedef struct ma_device ma_device; - -typedef ma_uint8 ma_channel; -typedef enum -{ - MA_CHANNEL_NONE = 0, - MA_CHANNEL_MONO = 1, - MA_CHANNEL_FRONT_LEFT = 2, - MA_CHANNEL_FRONT_RIGHT = 3, - MA_CHANNEL_FRONT_CENTER = 4, - MA_CHANNEL_LFE = 5, - MA_CHANNEL_BACK_LEFT = 6, - MA_CHANNEL_BACK_RIGHT = 7, - MA_CHANNEL_FRONT_LEFT_CENTER = 8, - MA_CHANNEL_FRONT_RIGHT_CENTER = 9, - MA_CHANNEL_BACK_CENTER = 10, - MA_CHANNEL_SIDE_LEFT = 11, - MA_CHANNEL_SIDE_RIGHT = 12, - MA_CHANNEL_TOP_CENTER = 13, - MA_CHANNEL_TOP_FRONT_LEFT = 14, - MA_CHANNEL_TOP_FRONT_CENTER = 15, - MA_CHANNEL_TOP_FRONT_RIGHT = 16, - MA_CHANNEL_TOP_BACK_LEFT = 17, - MA_CHANNEL_TOP_BACK_CENTER = 18, - MA_CHANNEL_TOP_BACK_RIGHT = 19, - MA_CHANNEL_AUX_0 = 20, - MA_CHANNEL_AUX_1 = 21, - MA_CHANNEL_AUX_2 = 22, - MA_CHANNEL_AUX_3 = 23, - MA_CHANNEL_AUX_4 = 24, - MA_CHANNEL_AUX_5 = 25, - MA_CHANNEL_AUX_6 = 26, - MA_CHANNEL_AUX_7 = 27, - MA_CHANNEL_AUX_8 = 28, - MA_CHANNEL_AUX_9 = 29, - MA_CHANNEL_AUX_10 = 30, - MA_CHANNEL_AUX_11 = 31, - MA_CHANNEL_AUX_12 = 32, - MA_CHANNEL_AUX_13 = 33, - MA_CHANNEL_AUX_14 = 34, - MA_CHANNEL_AUX_15 = 35, - MA_CHANNEL_AUX_16 = 36, - MA_CHANNEL_AUX_17 = 37, - MA_CHANNEL_AUX_18 = 38, - MA_CHANNEL_AUX_19 = 39, - MA_CHANNEL_AUX_20 = 40, - MA_CHANNEL_AUX_21 = 41, - MA_CHANNEL_AUX_22 = 42, - MA_CHANNEL_AUX_23 = 43, - MA_CHANNEL_AUX_24 = 44, - MA_CHANNEL_AUX_25 = 45, - MA_CHANNEL_AUX_26 = 46, - MA_CHANNEL_AUX_27 = 47, - MA_CHANNEL_AUX_28 = 48, - MA_CHANNEL_AUX_29 = 49, - MA_CHANNEL_AUX_30 = 50, - MA_CHANNEL_AUX_31 = 51, - MA_CHANNEL_LEFT = MA_CHANNEL_FRONT_LEFT, - MA_CHANNEL_RIGHT = MA_CHANNEL_FRONT_RIGHT, - MA_CHANNEL_POSITION_COUNT = (MA_CHANNEL_AUX_31 + 1) -} _ma_channel_position; /* Do not use `_ma_channel_position` directly. Use `ma_channel` instead. */ - -typedef enum -{ - MA_SUCCESS = 0, - MA_ERROR = -1, /* A generic error. */ - MA_INVALID_ARGS = -2, - MA_INVALID_OPERATION = -3, - MA_OUT_OF_MEMORY = -4, - MA_OUT_OF_RANGE = -5, - MA_ACCESS_DENIED = -6, - MA_DOES_NOT_EXIST = -7, - MA_ALREADY_EXISTS = -8, - MA_TOO_MANY_OPEN_FILES = -9, - MA_INVALID_FILE = -10, - MA_TOO_BIG = -11, - MA_PATH_TOO_LONG = -12, - MA_NAME_TOO_LONG = -13, - MA_NOT_DIRECTORY = -14, - MA_IS_DIRECTORY = -15, - MA_DIRECTORY_NOT_EMPTY = -16, - MA_AT_END = -17, - MA_NO_SPACE = -18, - MA_BUSY = -19, - MA_IO_ERROR = -20, - MA_INTERRUPT = -21, - MA_UNAVAILABLE = -22, - MA_ALREADY_IN_USE = -23, - MA_BAD_ADDRESS = -24, - MA_BAD_SEEK = -25, - MA_BAD_PIPE = -26, - MA_DEADLOCK = -27, - MA_TOO_MANY_LINKS = -28, - MA_NOT_IMPLEMENTED = -29, - MA_NO_MESSAGE = -30, - MA_BAD_MESSAGE = -31, - MA_NO_DATA_AVAILABLE = -32, - MA_INVALID_DATA = -33, - MA_TIMEOUT = -34, - MA_NO_NETWORK = -35, - MA_NOT_UNIQUE = -36, - MA_NOT_SOCKET = -37, - MA_NO_ADDRESS = -38, - MA_BAD_PROTOCOL = -39, - MA_PROTOCOL_UNAVAILABLE = -40, - MA_PROTOCOL_NOT_SUPPORTED = -41, - MA_PROTOCOL_FAMILY_NOT_SUPPORTED = -42, - MA_ADDRESS_FAMILY_NOT_SUPPORTED = -43, - MA_SOCKET_NOT_SUPPORTED = -44, - MA_CONNECTION_RESET = -45, - MA_ALREADY_CONNECTED = -46, - MA_NOT_CONNECTED = -47, - MA_CONNECTION_REFUSED = -48, - MA_NO_HOST = -49, - MA_IN_PROGRESS = -50, - MA_CANCELLED = -51, - MA_MEMORY_ALREADY_MAPPED = -52, - - /* General non-standard errors. */ - MA_CRC_MISMATCH = -100, - - /* General miniaudio-specific errors. */ - MA_FORMAT_NOT_SUPPORTED = -200, - MA_DEVICE_TYPE_NOT_SUPPORTED = -201, - MA_SHARE_MODE_NOT_SUPPORTED = -202, - MA_NO_BACKEND = -203, - MA_NO_DEVICE = -204, - MA_API_NOT_FOUND = -205, - MA_INVALID_DEVICE_CONFIG = -206, - MA_LOOP = -207, - MA_BACKEND_NOT_ENABLED = -208, - - /* State errors. */ - MA_DEVICE_NOT_INITIALIZED = -300, - MA_DEVICE_ALREADY_INITIALIZED = -301, - MA_DEVICE_NOT_STARTED = -302, - MA_DEVICE_NOT_STOPPED = -303, - - /* Operation errors. */ - MA_FAILED_TO_INIT_BACKEND = -400, - MA_FAILED_TO_OPEN_BACKEND_DEVICE = -401, - MA_FAILED_TO_START_BACKEND_DEVICE = -402, - MA_FAILED_TO_STOP_BACKEND_DEVICE = -403 -} ma_result; - - -#define MA_MIN_CHANNELS 1 -#ifndef MA_MAX_CHANNELS -#define MA_MAX_CHANNELS 254 -#endif - -#ifndef MA_MAX_FILTER_ORDER -#define MA_MAX_FILTER_ORDER 8 -#endif - -typedef enum -{ - ma_stream_format_pcm = 0 -} ma_stream_format; - -typedef enum -{ - ma_stream_layout_interleaved = 0, - ma_stream_layout_deinterleaved -} ma_stream_layout; - -typedef enum -{ - ma_dither_mode_none = 0, - ma_dither_mode_rectangle, - ma_dither_mode_triangle -} ma_dither_mode; - -typedef enum -{ - /* - I like to keep these explicitly defined because they're used as a key into a lookup table. When items are - added to this, make sure there are no gaps and that they're added to the lookup table in ma_get_bytes_per_sample(). - */ - ma_format_unknown = 0, /* Mainly used for indicating an error, but also used as the default for the output format for decoders. */ - ma_format_u8 = 1, - ma_format_s16 = 2, /* Seems to be the most widely supported format. */ - ma_format_s24 = 3, /* Tightly packed. 3 bytes per sample. */ - ma_format_s32 = 4, - ma_format_f32 = 5, - ma_format_count -} ma_format; - -typedef enum -{ - /* Standard rates need to be in priority order. */ - ma_standard_sample_rate_48000 = 48000, /* Most common */ - ma_standard_sample_rate_44100 = 44100, - - ma_standard_sample_rate_32000 = 32000, /* Lows */ - ma_standard_sample_rate_24000 = 24000, - ma_standard_sample_rate_22050 = 22050, - - ma_standard_sample_rate_88200 = 88200, /* Highs */ - ma_standard_sample_rate_96000 = 96000, - ma_standard_sample_rate_176400 = 176400, - ma_standard_sample_rate_192000 = 192000, - - ma_standard_sample_rate_16000 = 16000, /* Extreme lows */ - ma_standard_sample_rate_11025 = 11025, - ma_standard_sample_rate_8000 = 8000, - - ma_standard_sample_rate_352800 = 352800, /* Extreme highs */ - ma_standard_sample_rate_384000 = 384000, - - ma_standard_sample_rate_min = ma_standard_sample_rate_8000, - ma_standard_sample_rate_max = ma_standard_sample_rate_384000, - ma_standard_sample_rate_count = 14 /* Need to maintain the count manually. Make sure this is updated if items are added to enum. */ -} ma_standard_sample_rate; - - -typedef enum -{ - ma_channel_mix_mode_rectangular = 0, /* Simple averaging based on the plane(s) the channel is sitting on. */ - ma_channel_mix_mode_simple, /* Drop excess channels; zeroed out extra channels. */ - ma_channel_mix_mode_custom_weights, /* Use custom weights specified in ma_channel_converter_config. */ - ma_channel_mix_mode_default = ma_channel_mix_mode_rectangular -} ma_channel_mix_mode; - -typedef enum -{ - ma_standard_channel_map_microsoft, - ma_standard_channel_map_alsa, - ma_standard_channel_map_rfc3551, /* Based off AIFF. */ - ma_standard_channel_map_flac, - ma_standard_channel_map_vorbis, - ma_standard_channel_map_sound4, /* FreeBSD's sound(4). */ - ma_standard_channel_map_sndio, /* www.sndio.org/tips.html */ - ma_standard_channel_map_webaudio = ma_standard_channel_map_flac, /* https://webaudio.github.io/web-audio-api/#ChannelOrdering. Only 1, 2, 4 and 6 channels are defined, but can fill in the gaps with logical assumptions. */ - ma_standard_channel_map_default = ma_standard_channel_map_microsoft -} ma_standard_channel_map; - -typedef enum -{ - ma_performance_profile_low_latency = 0, - ma_performance_profile_conservative -} ma_performance_profile; - - -typedef struct -{ - void* pUserData; - void* (* onMalloc)(size_t sz, void* pUserData); - void* (* onRealloc)(void* p, size_t sz, void* pUserData); - void (* onFree)(void* p, void* pUserData); -} ma_allocation_callbacks; - -typedef struct -{ - ma_int32 state; -} ma_lcg; - - -/* -Atomics. - -These are typesafe structures to prevent errors as a result of forgetting to reference variables atomically. It's too -easy to introduce subtle bugs where you accidentally do a regular assignment instead of an atomic load/store, etc. By -using a struct we can enforce the use of atomics at compile time. - -These types are declared in the header section because we need to reference them in structs below, but functions for -using them are only exposed in the implementation section. I do not want these to be part of the public API. - -There's a few downsides to this system. The first is that you need to declare a new struct for each type. Below are -some macros to help with the declarations. They will be named like so: - - ma_atomic_uint32 - atomic ma_uint32 - ma_atomic_int32 - atomic ma_int32 - ma_atomic_uint64 - atomic ma_uint64 - ma_atomic_float - atomic float - ma_atomic_bool32 - atomic ma_bool32 - -The other downside is that atomic pointers are extremely messy. You need to declare a new struct for each specific -type of pointer you need to make atomic. For example, an atomic ma_node* will look like this: - - MA_ATOMIC_SAFE_TYPE_IMPL_PTR(node) - -Which will declare a type struct that's named like so: - - ma_atomic_ptr_node - -Functions to use the atomic types are declared in the implementation section. All atomic functions are prefixed with -the name of the struct. For example: - - ma_atomic_uint32_set() - Atomic store of ma_uint32 - ma_atomic_uint32_get() - Atomic load of ma_uint32 - etc. - -For pointer types it's the same, which makes them a bit messy to use due to the length of each function name, but in -return you get type safety and enforcement of atomic operations. -*/ -#define MA_ATOMIC_SAFE_TYPE_DECL(c89TypeExtension, typeSize, type) \ - typedef struct \ - { \ - MA_ATOMIC(typeSize, ma_##type) value; \ - } ma_atomic_##type; \ - -#define MA_ATOMIC_SAFE_TYPE_DECL_PTR(type) \ - typedef struct \ - { \ - MA_ATOMIC(MA_SIZEOF_PTR, ma_##type*) value; \ - } ma_atomic_ptr_##type; \ - -MA_ATOMIC_SAFE_TYPE_DECL(32, 4, uint32) -MA_ATOMIC_SAFE_TYPE_DECL(i32, 4, int32) -MA_ATOMIC_SAFE_TYPE_DECL(64, 8, uint64) -MA_ATOMIC_SAFE_TYPE_DECL(f32, 4, float) -MA_ATOMIC_SAFE_TYPE_DECL(32, 4, bool32) - - -/* Spinlocks are 32-bit for compatibility reasons. */ -typedef ma_uint32 ma_spinlock; - -#ifndef MA_NO_THREADING - /* Thread priorities should be ordered such that the default priority of the worker thread is 0. */ - typedef enum - { - ma_thread_priority_idle = -5, - ma_thread_priority_lowest = -4, - ma_thread_priority_low = -3, - ma_thread_priority_normal = -2, - ma_thread_priority_high = -1, - ma_thread_priority_highest = 0, - ma_thread_priority_realtime = 1, - ma_thread_priority_default = 0 - } ma_thread_priority; - - #if defined(MA_POSIX) - typedef ma_pthread_t ma_thread; - #elif defined(MA_WIN32) - typedef ma_handle ma_thread; - #endif - - #if defined(MA_POSIX) - typedef ma_pthread_mutex_t ma_mutex; - #elif defined(MA_WIN32) - typedef ma_handle ma_mutex; - #endif - - #if defined(MA_POSIX) - typedef struct - { - ma_uint32 value; - ma_pthread_mutex_t lock; - ma_pthread_cond_t cond; - } ma_event; - #elif defined(MA_WIN32) - typedef ma_handle ma_event; - #endif - - #if defined(MA_POSIX) - typedef struct - { - int value; - ma_pthread_mutex_t lock; - ma_pthread_cond_t cond; - } ma_semaphore; - #elif defined(MA_WIN32) - typedef ma_handle ma_semaphore; - #endif -#else - /* MA_NO_THREADING is set which means threading is disabled. Threading is required by some API families. If any of these are enabled we need to throw an error. */ - #ifndef MA_NO_DEVICE_IO - #error "MA_NO_THREADING cannot be used without MA_NO_DEVICE_IO"; - #endif -#endif /* MA_NO_THREADING */ - - -/* -Retrieves the version of miniaudio as separated integers. Each component can be NULL if it's not required. -*/ -MA_API void ma_version(ma_uint32* pMajor, ma_uint32* pMinor, ma_uint32* pRevision); - -/* -Retrieves the version of miniaudio as a string which can be useful for logging purposes. -*/ -MA_API const char* ma_version_string(void); - - -/************************************************************************************************************************************************************** - -Logging - -**************************************************************************************************************************************************************/ -#include /* For va_list. */ - -#if defined(__has_attribute) - #if __has_attribute(format) - #define MA_ATTRIBUTE_FORMAT(fmt, va) __attribute__((format(printf, fmt, va))) - #endif -#endif -#ifndef MA_ATTRIBUTE_FORMAT -#define MA_ATTRIBUTE_FORMAT(fmt, va) -#endif - -#ifndef MA_MAX_LOG_CALLBACKS -#define MA_MAX_LOG_CALLBACKS 4 -#endif - - -/* -The callback for handling log messages. - - -Parameters ----------- -pUserData (in) - The user data pointer that was passed into ma_log_register_callback(). - -logLevel (in) - The log level. This can be one of the following: - - +----------------------+ - | Log Level | - +----------------------+ - | MA_LOG_LEVEL_DEBUG | - | MA_LOG_LEVEL_INFO | - | MA_LOG_LEVEL_WARNING | - | MA_LOG_LEVEL_ERROR | - +----------------------+ - -pMessage (in) - The log message. -*/ -typedef void (* ma_log_callback_proc)(void* pUserData, ma_uint32 level, const char* pMessage); - -typedef struct -{ - ma_log_callback_proc onLog; - void* pUserData; -} ma_log_callback; - -MA_API ma_log_callback ma_log_callback_init(ma_log_callback_proc onLog, void* pUserData); - - -typedef struct -{ - ma_log_callback callbacks[MA_MAX_LOG_CALLBACKS]; - ma_uint32 callbackCount; - ma_allocation_callbacks allocationCallbacks; /* Need to store these persistently because ma_log_postv() might need to allocate a buffer on the heap. */ -#ifndef MA_NO_THREADING - ma_mutex lock; /* For thread safety just to make it easier and safer for the logging implementation. */ -#endif -} ma_log; - -MA_API ma_result ma_log_init(const ma_allocation_callbacks* pAllocationCallbacks, ma_log* pLog); -MA_API void ma_log_uninit(ma_log* pLog); -MA_API ma_result ma_log_register_callback(ma_log* pLog, ma_log_callback callback); -MA_API ma_result ma_log_unregister_callback(ma_log* pLog, ma_log_callback callback); -MA_API ma_result ma_log_post(ma_log* pLog, ma_uint32 level, const char* pMessage); -MA_API ma_result ma_log_postv(ma_log* pLog, ma_uint32 level, const char* pFormat, va_list args); -MA_API ma_result ma_log_postf(ma_log* pLog, ma_uint32 level, const char* pFormat, ...) MA_ATTRIBUTE_FORMAT(3, 4); - - -/************************************************************************************************************************************************************** - -Biquad Filtering - -**************************************************************************************************************************************************************/ -typedef union -{ - float f32; - ma_int32 s32; -} ma_biquad_coefficient; - -typedef struct -{ - ma_format format; - ma_uint32 channels; - double b0; - double b1; - double b2; - double a0; - double a1; - double a2; -} ma_biquad_config; - -MA_API ma_biquad_config ma_biquad_config_init(ma_format format, ma_uint32 channels, double b0, double b1, double b2, double a0, double a1, double a2); - -typedef struct -{ - ma_format format; - ma_uint32 channels; - ma_biquad_coefficient b0; - ma_biquad_coefficient b1; - ma_biquad_coefficient b2; - ma_biquad_coefficient a1; - ma_biquad_coefficient a2; - ma_biquad_coefficient* pR1; - ma_biquad_coefficient* pR2; - - /* Memory management. */ - void* _pHeap; - ma_bool32 _ownsHeap; -} ma_biquad; - -MA_API ma_result ma_biquad_get_heap_size(const ma_biquad_config* pConfig, size_t* pHeapSizeInBytes); -MA_API ma_result ma_biquad_init_preallocated(const ma_biquad_config* pConfig, void* pHeap, ma_biquad* pBQ); -MA_API ma_result ma_biquad_init(const ma_biquad_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_biquad* pBQ); -MA_API void ma_biquad_uninit(ma_biquad* pBQ, const ma_allocation_callbacks* pAllocationCallbacks); -MA_API ma_result ma_biquad_reinit(const ma_biquad_config* pConfig, ma_biquad* pBQ); -MA_API ma_result ma_biquad_clear_cache(ma_biquad* pBQ); -MA_API ma_result ma_biquad_process_pcm_frames(ma_biquad* pBQ, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount); -MA_API ma_uint32 ma_biquad_get_latency(const ma_biquad* pBQ); - - -/************************************************************************************************************************************************************** - -Low-Pass Filtering - -**************************************************************************************************************************************************************/ -typedef struct -{ - ma_format format; - ma_uint32 channels; - ma_uint32 sampleRate; - double cutoffFrequency; - double q; -} ma_lpf1_config, ma_lpf2_config; - -MA_API ma_lpf1_config ma_lpf1_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRate, double cutoffFrequency); -MA_API ma_lpf2_config ma_lpf2_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRate, double cutoffFrequency, double q); - -typedef struct -{ - ma_format format; - ma_uint32 channels; - ma_biquad_coefficient a; - ma_biquad_coefficient* pR1; - - /* Memory management. */ - void* _pHeap; - ma_bool32 _ownsHeap; -} ma_lpf1; - -MA_API ma_result ma_lpf1_get_heap_size(const ma_lpf1_config* pConfig, size_t* pHeapSizeInBytes); -MA_API ma_result ma_lpf1_init_preallocated(const ma_lpf1_config* pConfig, void* pHeap, ma_lpf1* pLPF); -MA_API ma_result ma_lpf1_init(const ma_lpf1_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_lpf1* pLPF); -MA_API void ma_lpf1_uninit(ma_lpf1* pLPF, const ma_allocation_callbacks* pAllocationCallbacks); -MA_API ma_result ma_lpf1_reinit(const ma_lpf1_config* pConfig, ma_lpf1* pLPF); -MA_API ma_result ma_lpf1_clear_cache(ma_lpf1* pLPF); -MA_API ma_result ma_lpf1_process_pcm_frames(ma_lpf1* pLPF, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount); -MA_API ma_uint32 ma_lpf1_get_latency(const ma_lpf1* pLPF); - -typedef struct -{ - ma_biquad bq; /* The second order low-pass filter is implemented as a biquad filter. */ -} ma_lpf2; - -MA_API ma_result ma_lpf2_get_heap_size(const ma_lpf2_config* pConfig, size_t* pHeapSizeInBytes); -MA_API ma_result ma_lpf2_init_preallocated(const ma_lpf2_config* pConfig, void* pHeap, ma_lpf2* pHPF); -MA_API ma_result ma_lpf2_init(const ma_lpf2_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_lpf2* pLPF); -MA_API void ma_lpf2_uninit(ma_lpf2* pLPF, const ma_allocation_callbacks* pAllocationCallbacks); -MA_API ma_result ma_lpf2_reinit(const ma_lpf2_config* pConfig, ma_lpf2* pLPF); -MA_API ma_result ma_lpf2_clear_cache(ma_lpf2* pLPF); -MA_API ma_result ma_lpf2_process_pcm_frames(ma_lpf2* pLPF, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount); -MA_API ma_uint32 ma_lpf2_get_latency(const ma_lpf2* pLPF); - - -typedef struct -{ - ma_format format; - ma_uint32 channels; - ma_uint32 sampleRate; - double cutoffFrequency; - ma_uint32 order; /* If set to 0, will be treated as a passthrough (no filtering will be applied). */ -} ma_lpf_config; - -MA_API ma_lpf_config ma_lpf_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRate, double cutoffFrequency, ma_uint32 order); - -typedef struct -{ - ma_format format; - ma_uint32 channels; - ma_uint32 sampleRate; - ma_uint32 lpf1Count; - ma_uint32 lpf2Count; - ma_lpf1* pLPF1; - ma_lpf2* pLPF2; - - /* Memory management. */ - void* _pHeap; - ma_bool32 _ownsHeap; -} ma_lpf; - -MA_API ma_result ma_lpf_get_heap_size(const ma_lpf_config* pConfig, size_t* pHeapSizeInBytes); -MA_API ma_result ma_lpf_init_preallocated(const ma_lpf_config* pConfig, void* pHeap, ma_lpf* pLPF); -MA_API ma_result ma_lpf_init(const ma_lpf_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_lpf* pLPF); -MA_API void ma_lpf_uninit(ma_lpf* pLPF, const ma_allocation_callbacks* pAllocationCallbacks); -MA_API ma_result ma_lpf_reinit(const ma_lpf_config* pConfig, ma_lpf* pLPF); -MA_API ma_result ma_lpf_clear_cache(ma_lpf* pLPF); -MA_API ma_result ma_lpf_process_pcm_frames(ma_lpf* pLPF, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount); -MA_API ma_uint32 ma_lpf_get_latency(const ma_lpf* pLPF); - - -/************************************************************************************************************************************************************** - -High-Pass Filtering - -**************************************************************************************************************************************************************/ -typedef struct -{ - ma_format format; - ma_uint32 channels; - ma_uint32 sampleRate; - double cutoffFrequency; - double q; -} ma_hpf1_config, ma_hpf2_config; - -MA_API ma_hpf1_config ma_hpf1_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRate, double cutoffFrequency); -MA_API ma_hpf2_config ma_hpf2_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRate, double cutoffFrequency, double q); - -typedef struct -{ - ma_format format; - ma_uint32 channels; - ma_biquad_coefficient a; - ma_biquad_coefficient* pR1; - - /* Memory management. */ - void* _pHeap; - ma_bool32 _ownsHeap; -} ma_hpf1; - -MA_API ma_result ma_hpf1_get_heap_size(const ma_hpf1_config* pConfig, size_t* pHeapSizeInBytes); -MA_API ma_result ma_hpf1_init_preallocated(const ma_hpf1_config* pConfig, void* pHeap, ma_hpf1* pLPF); -MA_API ma_result ma_hpf1_init(const ma_hpf1_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_hpf1* pHPF); -MA_API void ma_hpf1_uninit(ma_hpf1* pHPF, const ma_allocation_callbacks* pAllocationCallbacks); -MA_API ma_result ma_hpf1_reinit(const ma_hpf1_config* pConfig, ma_hpf1* pHPF); -MA_API ma_result ma_hpf1_process_pcm_frames(ma_hpf1* pHPF, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount); -MA_API ma_uint32 ma_hpf1_get_latency(const ma_hpf1* pHPF); - -typedef struct -{ - ma_biquad bq; /* The second order high-pass filter is implemented as a biquad filter. */ -} ma_hpf2; - -MA_API ma_result ma_hpf2_get_heap_size(const ma_hpf2_config* pConfig, size_t* pHeapSizeInBytes); -MA_API ma_result ma_hpf2_init_preallocated(const ma_hpf2_config* pConfig, void* pHeap, ma_hpf2* pHPF); -MA_API ma_result ma_hpf2_init(const ma_hpf2_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_hpf2* pHPF); -MA_API void ma_hpf2_uninit(ma_hpf2* pHPF, const ma_allocation_callbacks* pAllocationCallbacks); -MA_API ma_result ma_hpf2_reinit(const ma_hpf2_config* pConfig, ma_hpf2* pHPF); -MA_API ma_result ma_hpf2_process_pcm_frames(ma_hpf2* pHPF, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount); -MA_API ma_uint32 ma_hpf2_get_latency(const ma_hpf2* pHPF); - - -typedef struct -{ - ma_format format; - ma_uint32 channels; - ma_uint32 sampleRate; - double cutoffFrequency; - ma_uint32 order; /* If set to 0, will be treated as a passthrough (no filtering will be applied). */ -} ma_hpf_config; - -MA_API ma_hpf_config ma_hpf_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRate, double cutoffFrequency, ma_uint32 order); - -typedef struct -{ - ma_format format; - ma_uint32 channels; - ma_uint32 sampleRate; - ma_uint32 hpf1Count; - ma_uint32 hpf2Count; - ma_hpf1* pHPF1; - ma_hpf2* pHPF2; - - /* Memory management. */ - void* _pHeap; - ma_bool32 _ownsHeap; -} ma_hpf; - -MA_API ma_result ma_hpf_get_heap_size(const ma_hpf_config* pConfig, size_t* pHeapSizeInBytes); -MA_API ma_result ma_hpf_init_preallocated(const ma_hpf_config* pConfig, void* pHeap, ma_hpf* pLPF); -MA_API ma_result ma_hpf_init(const ma_hpf_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_hpf* pHPF); -MA_API void ma_hpf_uninit(ma_hpf* pHPF, const ma_allocation_callbacks* pAllocationCallbacks); -MA_API ma_result ma_hpf_reinit(const ma_hpf_config* pConfig, ma_hpf* pHPF); -MA_API ma_result ma_hpf_process_pcm_frames(ma_hpf* pHPF, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount); -MA_API ma_uint32 ma_hpf_get_latency(const ma_hpf* pHPF); - - -/************************************************************************************************************************************************************** - -Band-Pass Filtering - -**************************************************************************************************************************************************************/ -typedef struct -{ - ma_format format; - ma_uint32 channels; - ma_uint32 sampleRate; - double cutoffFrequency; - double q; -} ma_bpf2_config; - -MA_API ma_bpf2_config ma_bpf2_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRate, double cutoffFrequency, double q); - -typedef struct -{ - ma_biquad bq; /* The second order band-pass filter is implemented as a biquad filter. */ -} ma_bpf2; - -MA_API ma_result ma_bpf2_get_heap_size(const ma_bpf2_config* pConfig, size_t* pHeapSizeInBytes); -MA_API ma_result ma_bpf2_init_preallocated(const ma_bpf2_config* pConfig, void* pHeap, ma_bpf2* pBPF); -MA_API ma_result ma_bpf2_init(const ma_bpf2_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_bpf2* pBPF); -MA_API void ma_bpf2_uninit(ma_bpf2* pBPF, const ma_allocation_callbacks* pAllocationCallbacks); -MA_API ma_result ma_bpf2_reinit(const ma_bpf2_config* pConfig, ma_bpf2* pBPF); -MA_API ma_result ma_bpf2_process_pcm_frames(ma_bpf2* pBPF, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount); -MA_API ma_uint32 ma_bpf2_get_latency(const ma_bpf2* pBPF); - - -typedef struct -{ - ma_format format; - ma_uint32 channels; - ma_uint32 sampleRate; - double cutoffFrequency; - ma_uint32 order; /* If set to 0, will be treated as a passthrough (no filtering will be applied). */ -} ma_bpf_config; - -MA_API ma_bpf_config ma_bpf_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRate, double cutoffFrequency, ma_uint32 order); - -typedef struct -{ - ma_format format; - ma_uint32 channels; - ma_uint32 bpf2Count; - ma_bpf2* pBPF2; - - /* Memory management. */ - void* _pHeap; - ma_bool32 _ownsHeap; -} ma_bpf; - -MA_API ma_result ma_bpf_get_heap_size(const ma_bpf_config* pConfig, size_t* pHeapSizeInBytes); -MA_API ma_result ma_bpf_init_preallocated(const ma_bpf_config* pConfig, void* pHeap, ma_bpf* pBPF); -MA_API ma_result ma_bpf_init(const ma_bpf_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_bpf* pBPF); -MA_API void ma_bpf_uninit(ma_bpf* pBPF, const ma_allocation_callbacks* pAllocationCallbacks); -MA_API ma_result ma_bpf_reinit(const ma_bpf_config* pConfig, ma_bpf* pBPF); -MA_API ma_result ma_bpf_process_pcm_frames(ma_bpf* pBPF, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount); -MA_API ma_uint32 ma_bpf_get_latency(const ma_bpf* pBPF); - - -/************************************************************************************************************************************************************** - -Notching Filter - -**************************************************************************************************************************************************************/ -typedef struct -{ - ma_format format; - ma_uint32 channels; - ma_uint32 sampleRate; - double q; - double frequency; -} ma_notch2_config, ma_notch_config; - -MA_API ma_notch2_config ma_notch2_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRate, double q, double frequency); - -typedef struct -{ - ma_biquad bq; -} ma_notch2; - -MA_API ma_result ma_notch2_get_heap_size(const ma_notch2_config* pConfig, size_t* pHeapSizeInBytes); -MA_API ma_result ma_notch2_init_preallocated(const ma_notch2_config* pConfig, void* pHeap, ma_notch2* pFilter); -MA_API ma_result ma_notch2_init(const ma_notch2_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_notch2* pFilter); -MA_API void ma_notch2_uninit(ma_notch2* pFilter, const ma_allocation_callbacks* pAllocationCallbacks); -MA_API ma_result ma_notch2_reinit(const ma_notch2_config* pConfig, ma_notch2* pFilter); -MA_API ma_result ma_notch2_process_pcm_frames(ma_notch2* pFilter, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount); -MA_API ma_uint32 ma_notch2_get_latency(const ma_notch2* pFilter); - - -/************************************************************************************************************************************************************** - -Peaking EQ Filter - -**************************************************************************************************************************************************************/ -typedef struct -{ - ma_format format; - ma_uint32 channels; - ma_uint32 sampleRate; - double gainDB; - double q; - double frequency; -} ma_peak2_config, ma_peak_config; - -MA_API ma_peak2_config ma_peak2_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRate, double gainDB, double q, double frequency); - -typedef struct -{ - ma_biquad bq; -} ma_peak2; - -MA_API ma_result ma_peak2_get_heap_size(const ma_peak2_config* pConfig, size_t* pHeapSizeInBytes); -MA_API ma_result ma_peak2_init_preallocated(const ma_peak2_config* pConfig, void* pHeap, ma_peak2* pFilter); -MA_API ma_result ma_peak2_init(const ma_peak2_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_peak2* pFilter); -MA_API void ma_peak2_uninit(ma_peak2* pFilter, const ma_allocation_callbacks* pAllocationCallbacks); -MA_API ma_result ma_peak2_reinit(const ma_peak2_config* pConfig, ma_peak2* pFilter); -MA_API ma_result ma_peak2_process_pcm_frames(ma_peak2* pFilter, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount); -MA_API ma_uint32 ma_peak2_get_latency(const ma_peak2* pFilter); - - -/************************************************************************************************************************************************************** - -Low Shelf Filter - -**************************************************************************************************************************************************************/ -typedef struct -{ - ma_format format; - ma_uint32 channels; - ma_uint32 sampleRate; - double gainDB; - double shelfSlope; - double frequency; -} ma_loshelf2_config, ma_loshelf_config; - -MA_API ma_loshelf2_config ma_loshelf2_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRate, double gainDB, double shelfSlope, double frequency); - -typedef struct -{ - ma_biquad bq; -} ma_loshelf2; - -MA_API ma_result ma_loshelf2_get_heap_size(const ma_loshelf2_config* pConfig, size_t* pHeapSizeInBytes); -MA_API ma_result ma_loshelf2_init_preallocated(const ma_loshelf2_config* pConfig, void* pHeap, ma_loshelf2* pFilter); -MA_API ma_result ma_loshelf2_init(const ma_loshelf2_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_loshelf2* pFilter); -MA_API void ma_loshelf2_uninit(ma_loshelf2* pFilter, const ma_allocation_callbacks* pAllocationCallbacks); -MA_API ma_result ma_loshelf2_reinit(const ma_loshelf2_config* pConfig, ma_loshelf2* pFilter); -MA_API ma_result ma_loshelf2_process_pcm_frames(ma_loshelf2* pFilter, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount); -MA_API ma_uint32 ma_loshelf2_get_latency(const ma_loshelf2* pFilter); - - -/************************************************************************************************************************************************************** - -High Shelf Filter - -**************************************************************************************************************************************************************/ -typedef struct -{ - ma_format format; - ma_uint32 channels; - ma_uint32 sampleRate; - double gainDB; - double shelfSlope; - double frequency; -} ma_hishelf2_config, ma_hishelf_config; - -MA_API ma_hishelf2_config ma_hishelf2_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRate, double gainDB, double shelfSlope, double frequency); - -typedef struct -{ - ma_biquad bq; -} ma_hishelf2; - -MA_API ma_result ma_hishelf2_get_heap_size(const ma_hishelf2_config* pConfig, size_t* pHeapSizeInBytes); -MA_API ma_result ma_hishelf2_init_preallocated(const ma_hishelf2_config* pConfig, void* pHeap, ma_hishelf2* pFilter); -MA_API ma_result ma_hishelf2_init(const ma_hishelf2_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_hishelf2* pFilter); -MA_API void ma_hishelf2_uninit(ma_hishelf2* pFilter, const ma_allocation_callbacks* pAllocationCallbacks); -MA_API ma_result ma_hishelf2_reinit(const ma_hishelf2_config* pConfig, ma_hishelf2* pFilter); -MA_API ma_result ma_hishelf2_process_pcm_frames(ma_hishelf2* pFilter, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount); -MA_API ma_uint32 ma_hishelf2_get_latency(const ma_hishelf2* pFilter); - - - -/* -Delay -*/ -typedef struct -{ - ma_uint32 channels; - ma_uint32 sampleRate; - ma_uint32 delayInFrames; - ma_bool32 delayStart; /* Set to true to delay the start of the output; false otherwise. */ - float wet; /* 0..1. Default = 1. */ - float dry; /* 0..1. Default = 1. */ - float decay; /* 0..1. Default = 0 (no feedback). Feedback decay. Use this for echo. */ -} ma_delay_config; - -MA_API ma_delay_config ma_delay_config_init(ma_uint32 channels, ma_uint32 sampleRate, ma_uint32 delayInFrames, float decay); - - -typedef struct -{ - ma_delay_config config; - ma_uint32 cursor; /* Feedback is written to this cursor. Always equal or in front of the read cursor. */ - ma_uint32 bufferSizeInFrames; - float* pBuffer; -} ma_delay; - -MA_API ma_result ma_delay_init(const ma_delay_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_delay* pDelay); -MA_API void ma_delay_uninit(ma_delay* pDelay, const ma_allocation_callbacks* pAllocationCallbacks); -MA_API ma_result ma_delay_process_pcm_frames(ma_delay* pDelay, void* pFramesOut, const void* pFramesIn, ma_uint32 frameCount); -MA_API void ma_delay_set_wet(ma_delay* pDelay, float value); -MA_API float ma_delay_get_wet(const ma_delay* pDelay); -MA_API void ma_delay_set_dry(ma_delay* pDelay, float value); -MA_API float ma_delay_get_dry(const ma_delay* pDelay); -MA_API void ma_delay_set_decay(ma_delay* pDelay, float value); -MA_API float ma_delay_get_decay(const ma_delay* pDelay); - - -/* Gainer for smooth volume changes. */ -typedef struct -{ - ma_uint32 channels; - ma_uint32 smoothTimeInFrames; -} ma_gainer_config; - -MA_API ma_gainer_config ma_gainer_config_init(ma_uint32 channels, ma_uint32 smoothTimeInFrames); - - -typedef struct -{ - ma_gainer_config config; - ma_uint32 t; - float masterVolume; - float* pOldGains; - float* pNewGains; - - /* Memory management. */ - void* _pHeap; - ma_bool32 _ownsHeap; -} ma_gainer; - -MA_API ma_result ma_gainer_get_heap_size(const ma_gainer_config* pConfig, size_t* pHeapSizeInBytes); -MA_API ma_result ma_gainer_init_preallocated(const ma_gainer_config* pConfig, void* pHeap, ma_gainer* pGainer); -MA_API ma_result ma_gainer_init(const ma_gainer_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_gainer* pGainer); -MA_API void ma_gainer_uninit(ma_gainer* pGainer, const ma_allocation_callbacks* pAllocationCallbacks); -MA_API ma_result ma_gainer_process_pcm_frames(ma_gainer* pGainer, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount); -MA_API ma_result ma_gainer_set_gain(ma_gainer* pGainer, float newGain); -MA_API ma_result ma_gainer_set_gains(ma_gainer* pGainer, float* pNewGains); -MA_API ma_result ma_gainer_set_master_volume(ma_gainer* pGainer, float volume); -MA_API ma_result ma_gainer_get_master_volume(const ma_gainer* pGainer, float* pVolume); - - - -/* Stereo panner. */ -typedef enum -{ - ma_pan_mode_balance = 0, /* Does not blend one side with the other. Technically just a balance. Compatible with other popular audio engines and therefore the default. */ - ma_pan_mode_pan /* A true pan. The sound from one side will "move" to the other side and blend with it. */ -} ma_pan_mode; - -typedef struct -{ - ma_format format; - ma_uint32 channels; - ma_pan_mode mode; - float pan; -} ma_panner_config; - -MA_API ma_panner_config ma_panner_config_init(ma_format format, ma_uint32 channels); - - -typedef struct -{ - ma_format format; - ma_uint32 channels; - ma_pan_mode mode; - float pan; /* -1..1 where 0 is no pan, -1 is left side, +1 is right side. Defaults to 0. */ -} ma_panner; - -MA_API ma_result ma_panner_init(const ma_panner_config* pConfig, ma_panner* pPanner); -MA_API ma_result ma_panner_process_pcm_frames(ma_panner* pPanner, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount); -MA_API void ma_panner_set_mode(ma_panner* pPanner, ma_pan_mode mode); -MA_API ma_pan_mode ma_panner_get_mode(const ma_panner* pPanner); -MA_API void ma_panner_set_pan(ma_panner* pPanner, float pan); -MA_API float ma_panner_get_pan(const ma_panner* pPanner); - - - -/* Fader. */ -typedef struct -{ - ma_format format; - ma_uint32 channels; - ma_uint32 sampleRate; -} ma_fader_config; - -MA_API ma_fader_config ma_fader_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRate); - -typedef struct -{ - ma_fader_config config; - float volumeBeg; /* If volumeBeg and volumeEnd is equal to 1, no fading happens (ma_fader_process_pcm_frames() will run as a passthrough). */ - float volumeEnd; - ma_uint64 lengthInFrames; /* The total length of the fade. */ - ma_int64 cursorInFrames; /* The current time in frames. Incremented by ma_fader_process_pcm_frames(). Signed because it'll be offset by startOffsetInFrames in set_fade_ex(). */ -} ma_fader; - -MA_API ma_result ma_fader_init(const ma_fader_config* pConfig, ma_fader* pFader); -MA_API ma_result ma_fader_process_pcm_frames(ma_fader* pFader, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount); -MA_API void ma_fader_get_data_format(const ma_fader* pFader, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate); -MA_API void ma_fader_set_fade(ma_fader* pFader, float volumeBeg, float volumeEnd, ma_uint64 lengthInFrames); -MA_API void ma_fader_set_fade_ex(ma_fader* pFader, float volumeBeg, float volumeEnd, ma_uint64 lengthInFrames, ma_int64 startOffsetInFrames); -MA_API float ma_fader_get_current_volume(const ma_fader* pFader); - - - -/* Spatializer. */ -typedef struct -{ - float x; - float y; - float z; -} ma_vec3f; - -typedef struct -{ - ma_vec3f v; - ma_spinlock lock; -} ma_atomic_vec3f; - -typedef enum -{ - ma_attenuation_model_none, /* No distance attenuation and no spatialization. */ - ma_attenuation_model_inverse, /* Equivalent to OpenAL's AL_INVERSE_DISTANCE_CLAMPED. */ - ma_attenuation_model_linear, /* Linear attenuation. Equivalent to OpenAL's AL_LINEAR_DISTANCE_CLAMPED. */ - ma_attenuation_model_exponential /* Exponential attenuation. Equivalent to OpenAL's AL_EXPONENT_DISTANCE_CLAMPED. */ -} ma_attenuation_model; - -typedef enum -{ - ma_positioning_absolute, - ma_positioning_relative -} ma_positioning; - -typedef enum -{ - ma_handedness_right, - ma_handedness_left -} ma_handedness; - - -typedef struct -{ - ma_uint32 channelsOut; - ma_channel* pChannelMapOut; - ma_handedness handedness; /* Defaults to right. Forward is -1 on the Z axis. In a left handed system, forward is +1 on the Z axis. */ - float coneInnerAngleInRadians; - float coneOuterAngleInRadians; - float coneOuterGain; - float speedOfSound; - ma_vec3f worldUp; -} ma_spatializer_listener_config; - -MA_API ma_spatializer_listener_config ma_spatializer_listener_config_init(ma_uint32 channelsOut); - - -typedef struct -{ - ma_spatializer_listener_config config; - ma_atomic_vec3f position; /* The absolute position of the listener. */ - ma_atomic_vec3f direction; /* The direction the listener is facing. The world up vector is config.worldUp. */ - ma_atomic_vec3f velocity; - ma_bool32 isEnabled; - - /* Memory management. */ - ma_bool32 _ownsHeap; - void* _pHeap; -} ma_spatializer_listener; - -MA_API ma_result ma_spatializer_listener_get_heap_size(const ma_spatializer_listener_config* pConfig, size_t* pHeapSizeInBytes); -MA_API ma_result ma_spatializer_listener_init_preallocated(const ma_spatializer_listener_config* pConfig, void* pHeap, ma_spatializer_listener* pListener); -MA_API ma_result ma_spatializer_listener_init(const ma_spatializer_listener_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_spatializer_listener* pListener); -MA_API void ma_spatializer_listener_uninit(ma_spatializer_listener* pListener, const ma_allocation_callbacks* pAllocationCallbacks); -MA_API ma_channel* ma_spatializer_listener_get_channel_map(ma_spatializer_listener* pListener); -MA_API void ma_spatializer_listener_set_cone(ma_spatializer_listener* pListener, float innerAngleInRadians, float outerAngleInRadians, float outerGain); -MA_API void ma_spatializer_listener_get_cone(const ma_spatializer_listener* pListener, float* pInnerAngleInRadians, float* pOuterAngleInRadians, float* pOuterGain); -MA_API void ma_spatializer_listener_set_position(ma_spatializer_listener* pListener, float x, float y, float z); -MA_API ma_vec3f ma_spatializer_listener_get_position(const ma_spatializer_listener* pListener); -MA_API void ma_spatializer_listener_set_direction(ma_spatializer_listener* pListener, float x, float y, float z); -MA_API ma_vec3f ma_spatializer_listener_get_direction(const ma_spatializer_listener* pListener); -MA_API void ma_spatializer_listener_set_velocity(ma_spatializer_listener* pListener, float x, float y, float z); -MA_API ma_vec3f ma_spatializer_listener_get_velocity(const ma_spatializer_listener* pListener); -MA_API void ma_spatializer_listener_set_speed_of_sound(ma_spatializer_listener* pListener, float speedOfSound); -MA_API float ma_spatializer_listener_get_speed_of_sound(const ma_spatializer_listener* pListener); -MA_API void ma_spatializer_listener_set_world_up(ma_spatializer_listener* pListener, float x, float y, float z); -MA_API ma_vec3f ma_spatializer_listener_get_world_up(const ma_spatializer_listener* pListener); -MA_API void ma_spatializer_listener_set_enabled(ma_spatializer_listener* pListener, ma_bool32 isEnabled); -MA_API ma_bool32 ma_spatializer_listener_is_enabled(const ma_spatializer_listener* pListener); - - -typedef struct -{ - ma_uint32 channelsIn; - ma_uint32 channelsOut; - ma_channel* pChannelMapIn; - ma_attenuation_model attenuationModel; - ma_positioning positioning; - ma_handedness handedness; /* Defaults to right. Forward is -1 on the Z axis. In a left handed system, forward is +1 on the Z axis. */ - float minGain; - float maxGain; - float minDistance; - float maxDistance; - float rolloff; - float coneInnerAngleInRadians; - float coneOuterAngleInRadians; - float coneOuterGain; - float dopplerFactor; /* Set to 0 to disable doppler effect. */ - float directionalAttenuationFactor; /* Set to 0 to disable directional attenuation. */ - float minSpatializationChannelGain; /* The minimal scaling factor to apply to channel gains when accounting for the direction of the sound relative to the listener. Must be in the range of 0..1. Smaller values means more aggressive directional panning, larger values means more subtle directional panning. */ - ma_uint32 gainSmoothTimeInFrames; /* When the gain of a channel changes during spatialization, the transition will be linearly interpolated over this number of frames. */ -} ma_spatializer_config; - -MA_API ma_spatializer_config ma_spatializer_config_init(ma_uint32 channelsIn, ma_uint32 channelsOut); - - -typedef struct -{ - ma_uint32 channelsIn; - ma_uint32 channelsOut; - ma_channel* pChannelMapIn; - ma_attenuation_model attenuationModel; - ma_positioning positioning; - ma_handedness handedness; /* Defaults to right. Forward is -1 on the Z axis. In a left handed system, forward is +1 on the Z axis. */ - float minGain; - float maxGain; - float minDistance; - float maxDistance; - float rolloff; - float coneInnerAngleInRadians; - float coneOuterAngleInRadians; - float coneOuterGain; - float dopplerFactor; /* Set to 0 to disable doppler effect. */ - float directionalAttenuationFactor; /* Set to 0 to disable directional attenuation. */ - ma_uint32 gainSmoothTimeInFrames; /* When the gain of a channel changes during spatialization, the transition will be linearly interpolated over this number of frames. */ - ma_atomic_vec3f position; - ma_atomic_vec3f direction; - ma_atomic_vec3f velocity; /* For doppler effect. */ - float dopplerPitch; /* Will be updated by ma_spatializer_process_pcm_frames() and can be used by higher level functions to apply a pitch shift for doppler effect. */ - float minSpatializationChannelGain; - ma_gainer gainer; /* For smooth gain transitions. */ - float* pNewChannelGainsOut; /* An offset of _pHeap. Used by ma_spatializer_process_pcm_frames() to store new channel gains. The number of elements in this array is equal to config.channelsOut. */ - - /* Memory management. */ - void* _pHeap; - ma_bool32 _ownsHeap; -} ma_spatializer; - -MA_API ma_result ma_spatializer_get_heap_size(const ma_spatializer_config* pConfig, size_t* pHeapSizeInBytes); -MA_API ma_result ma_spatializer_init_preallocated(const ma_spatializer_config* pConfig, void* pHeap, ma_spatializer* pSpatializer); -MA_API ma_result ma_spatializer_init(const ma_spatializer_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_spatializer* pSpatializer); -MA_API void ma_spatializer_uninit(ma_spatializer* pSpatializer, const ma_allocation_callbacks* pAllocationCallbacks); -MA_API ma_result ma_spatializer_process_pcm_frames(ma_spatializer* pSpatializer, ma_spatializer_listener* pListener, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount); -MA_API ma_result ma_spatializer_set_master_volume(ma_spatializer* pSpatializer, float volume); -MA_API ma_result ma_spatializer_get_master_volume(const ma_spatializer* pSpatializer, float* pVolume); -MA_API ma_uint32 ma_spatializer_get_input_channels(const ma_spatializer* pSpatializer); -MA_API ma_uint32 ma_spatializer_get_output_channels(const ma_spatializer* pSpatializer); -MA_API void ma_spatializer_set_attenuation_model(ma_spatializer* pSpatializer, ma_attenuation_model attenuationModel); -MA_API ma_attenuation_model ma_spatializer_get_attenuation_model(const ma_spatializer* pSpatializer); -MA_API void ma_spatializer_set_positioning(ma_spatializer* pSpatializer, ma_positioning positioning); -MA_API ma_positioning ma_spatializer_get_positioning(const ma_spatializer* pSpatializer); -MA_API void ma_spatializer_set_rolloff(ma_spatializer* pSpatializer, float rolloff); -MA_API float ma_spatializer_get_rolloff(const ma_spatializer* pSpatializer); -MA_API void ma_spatializer_set_min_gain(ma_spatializer* pSpatializer, float minGain); -MA_API float ma_spatializer_get_min_gain(const ma_spatializer* pSpatializer); -MA_API void ma_spatializer_set_max_gain(ma_spatializer* pSpatializer, float maxGain); -MA_API float ma_spatializer_get_max_gain(const ma_spatializer* pSpatializer); -MA_API void ma_spatializer_set_min_distance(ma_spatializer* pSpatializer, float minDistance); -MA_API float ma_spatializer_get_min_distance(const ma_spatializer* pSpatializer); -MA_API void ma_spatializer_set_max_distance(ma_spatializer* pSpatializer, float maxDistance); -MA_API float ma_spatializer_get_max_distance(const ma_spatializer* pSpatializer); -MA_API void ma_spatializer_set_cone(ma_spatializer* pSpatializer, float innerAngleInRadians, float outerAngleInRadians, float outerGain); -MA_API void ma_spatializer_get_cone(const ma_spatializer* pSpatializer, float* pInnerAngleInRadians, float* pOuterAngleInRadians, float* pOuterGain); -MA_API void ma_spatializer_set_doppler_factor(ma_spatializer* pSpatializer, float dopplerFactor); -MA_API float ma_spatializer_get_doppler_factor(const ma_spatializer* pSpatializer); -MA_API void ma_spatializer_set_directional_attenuation_factor(ma_spatializer* pSpatializer, float directionalAttenuationFactor); -MA_API float ma_spatializer_get_directional_attenuation_factor(const ma_spatializer* pSpatializer); -MA_API void ma_spatializer_set_position(ma_spatializer* pSpatializer, float x, float y, float z); -MA_API ma_vec3f ma_spatializer_get_position(const ma_spatializer* pSpatializer); -MA_API void ma_spatializer_set_direction(ma_spatializer* pSpatializer, float x, float y, float z); -MA_API ma_vec3f ma_spatializer_get_direction(const ma_spatializer* pSpatializer); -MA_API void ma_spatializer_set_velocity(ma_spatializer* pSpatializer, float x, float y, float z); -MA_API ma_vec3f ma_spatializer_get_velocity(const ma_spatializer* pSpatializer); -MA_API void ma_spatializer_get_relative_position_and_direction(const ma_spatializer* pSpatializer, const ma_spatializer_listener* pListener, ma_vec3f* pRelativePos, ma_vec3f* pRelativeDir); - - - -/************************************************************************************************************************************************************ -************************************************************************************************************************************************************* - -DATA CONVERSION -=============== - -This section contains the APIs for data conversion. You will find everything here for channel mapping, sample format conversion, resampling, etc. - -************************************************************************************************************************************************************* -************************************************************************************************************************************************************/ - -/************************************************************************************************************************************************************** - -Resampling - -**************************************************************************************************************************************************************/ -typedef struct -{ - ma_format format; - ma_uint32 channels; - ma_uint32 sampleRateIn; - ma_uint32 sampleRateOut; - ma_uint32 lpfOrder; /* The low-pass filter order. Setting this to 0 will disable low-pass filtering. */ - double lpfNyquistFactor; /* 0..1. Defaults to 1. 1 = Half the sampling frequency (Nyquist Frequency), 0.5 = Quarter the sampling frequency (half Nyquest Frequency), etc. */ -} ma_linear_resampler_config; - -MA_API ma_linear_resampler_config ma_linear_resampler_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRateIn, ma_uint32 sampleRateOut); - -typedef struct -{ - ma_linear_resampler_config config; - ma_uint32 inAdvanceInt; - ma_uint32 inAdvanceFrac; - ma_uint32 inTimeInt; - ma_uint32 inTimeFrac; - union - { - float* f32; - ma_int16* s16; - } x0; /* The previous input frame. */ - union - { - float* f32; - ma_int16* s16; - } x1; /* The next input frame. */ - ma_lpf lpf; - - /* Memory management. */ - void* _pHeap; - ma_bool32 _ownsHeap; -} ma_linear_resampler; - -MA_API ma_result ma_linear_resampler_get_heap_size(const ma_linear_resampler_config* pConfig, size_t* pHeapSizeInBytes); -MA_API ma_result ma_linear_resampler_init_preallocated(const ma_linear_resampler_config* pConfig, void* pHeap, ma_linear_resampler* pResampler); -MA_API ma_result ma_linear_resampler_init(const ma_linear_resampler_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_linear_resampler* pResampler); -MA_API void ma_linear_resampler_uninit(ma_linear_resampler* pResampler, const ma_allocation_callbacks* pAllocationCallbacks); -MA_API ma_result ma_linear_resampler_process_pcm_frames(ma_linear_resampler* pResampler, const void* pFramesIn, ma_uint64* pFrameCountIn, void* pFramesOut, ma_uint64* pFrameCountOut); -MA_API ma_result ma_linear_resampler_set_rate(ma_linear_resampler* pResampler, ma_uint32 sampleRateIn, ma_uint32 sampleRateOut); -MA_API ma_result ma_linear_resampler_set_rate_ratio(ma_linear_resampler* pResampler, float ratioInOut); -MA_API ma_uint64 ma_linear_resampler_get_input_latency(const ma_linear_resampler* pResampler); -MA_API ma_uint64 ma_linear_resampler_get_output_latency(const ma_linear_resampler* pResampler); -MA_API ma_result ma_linear_resampler_get_required_input_frame_count(const ma_linear_resampler* pResampler, ma_uint64 outputFrameCount, ma_uint64* pInputFrameCount); -MA_API ma_result ma_linear_resampler_get_expected_output_frame_count(const ma_linear_resampler* pResampler, ma_uint64 inputFrameCount, ma_uint64* pOutputFrameCount); -MA_API ma_result ma_linear_resampler_reset(ma_linear_resampler* pResampler); - - -typedef struct ma_resampler_config ma_resampler_config; - -typedef void ma_resampling_backend; -typedef struct -{ - ma_result (* onGetHeapSize )(void* pUserData, const ma_resampler_config* pConfig, size_t* pHeapSizeInBytes); - ma_result (* onInit )(void* pUserData, const ma_resampler_config* pConfig, void* pHeap, ma_resampling_backend** ppBackend); - void (* onUninit )(void* pUserData, ma_resampling_backend* pBackend, const ma_allocation_callbacks* pAllocationCallbacks); - ma_result (* onProcess )(void* pUserData, ma_resampling_backend* pBackend, const void* pFramesIn, ma_uint64* pFrameCountIn, void* pFramesOut, ma_uint64* pFrameCountOut); - ma_result (* onSetRate )(void* pUserData, ma_resampling_backend* pBackend, ma_uint32 sampleRateIn, ma_uint32 sampleRateOut); /* Optional. Rate changes will be disabled. */ - ma_uint64 (* onGetInputLatency )(void* pUserData, const ma_resampling_backend* pBackend); /* Optional. Latency will be reported as 0. */ - ma_uint64 (* onGetOutputLatency )(void* pUserData, const ma_resampling_backend* pBackend); /* Optional. Latency will be reported as 0. */ - ma_result (* onGetRequiredInputFrameCount )(void* pUserData, const ma_resampling_backend* pBackend, ma_uint64 outputFrameCount, ma_uint64* pInputFrameCount); /* Optional. Latency mitigation will be disabled. */ - ma_result (* onGetExpectedOutputFrameCount)(void* pUserData, const ma_resampling_backend* pBackend, ma_uint64 inputFrameCount, ma_uint64* pOutputFrameCount); /* Optional. Latency mitigation will be disabled. */ - ma_result (* onReset )(void* pUserData, ma_resampling_backend* pBackend); -} ma_resampling_backend_vtable; - -typedef enum -{ - ma_resample_algorithm_linear = 0, /* Fastest, lowest quality. Optional low-pass filtering. Default. */ - ma_resample_algorithm_custom, -} ma_resample_algorithm; - -struct ma_resampler_config -{ - ma_format format; /* Must be either ma_format_f32 or ma_format_s16. */ - ma_uint32 channels; - ma_uint32 sampleRateIn; - ma_uint32 sampleRateOut; - ma_resample_algorithm algorithm; /* When set to ma_resample_algorithm_custom, pBackendVTable will be used. */ - ma_resampling_backend_vtable* pBackendVTable; - void* pBackendUserData; - struct - { - ma_uint32 lpfOrder; - } linear; -}; - -MA_API ma_resampler_config ma_resampler_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRateIn, ma_uint32 sampleRateOut, ma_resample_algorithm algorithm); - -typedef struct -{ - ma_resampling_backend* pBackend; - ma_resampling_backend_vtable* pBackendVTable; - void* pBackendUserData; - ma_format format; - ma_uint32 channels; - ma_uint32 sampleRateIn; - ma_uint32 sampleRateOut; - union - { - ma_linear_resampler linear; - } state; /* State for stock resamplers so we can avoid a malloc. For stock resamplers, pBackend will point here. */ - - /* Memory management. */ - void* _pHeap; - ma_bool32 _ownsHeap; -} ma_resampler; - -MA_API ma_result ma_resampler_get_heap_size(const ma_resampler_config* pConfig, size_t* pHeapSizeInBytes); -MA_API ma_result ma_resampler_init_preallocated(const ma_resampler_config* pConfig, void* pHeap, ma_resampler* pResampler); - -/* -Initializes a new resampler object from a config. -*/ -MA_API ma_result ma_resampler_init(const ma_resampler_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_resampler* pResampler); - -/* -Uninitializes a resampler. -*/ -MA_API void ma_resampler_uninit(ma_resampler* pResampler, const ma_allocation_callbacks* pAllocationCallbacks); - -/* -Converts the given input data. - -Both the input and output frames must be in the format specified in the config when the resampler was initialized. - -On input, [pFrameCountOut] contains the number of output frames to process. On output it contains the number of output frames that -were actually processed, which may be less than the requested amount which will happen if there's not enough input data. You can use -ma_resampler_get_expected_output_frame_count() to know how many output frames will be processed for a given number of input frames. - -On input, [pFrameCountIn] contains the number of input frames contained in [pFramesIn]. On output it contains the number of whole -input frames that were actually processed. You can use ma_resampler_get_required_input_frame_count() to know how many input frames -you should provide for a given number of output frames. [pFramesIn] can be NULL, in which case zeroes will be used instead. - -If [pFramesOut] is NULL, a seek is performed. In this case, if [pFrameCountOut] is not NULL it will seek by the specified number of -output frames. Otherwise, if [pFramesCountOut] is NULL and [pFrameCountIn] is not NULL, it will seek by the specified number of input -frames. When seeking, [pFramesIn] is allowed to NULL, in which case the internal timing state will be updated, but no input will be -processed. In this case, any internal filter state will be updated as if zeroes were passed in. - -It is an error for [pFramesOut] to be non-NULL and [pFrameCountOut] to be NULL. - -It is an error for both [pFrameCountOut] and [pFrameCountIn] to be NULL. -*/ -MA_API ma_result ma_resampler_process_pcm_frames(ma_resampler* pResampler, const void* pFramesIn, ma_uint64* pFrameCountIn, void* pFramesOut, ma_uint64* pFrameCountOut); - - -/* -Sets the input and output sample rate. -*/ -MA_API ma_result ma_resampler_set_rate(ma_resampler* pResampler, ma_uint32 sampleRateIn, ma_uint32 sampleRateOut); - -/* -Sets the input and output sample rate as a ratio. - -The ration is in/out. -*/ -MA_API ma_result ma_resampler_set_rate_ratio(ma_resampler* pResampler, float ratio); - -/* -Retrieves the latency introduced by the resampler in input frames. -*/ -MA_API ma_uint64 ma_resampler_get_input_latency(const ma_resampler* pResampler); - -/* -Retrieves the latency introduced by the resampler in output frames. -*/ -MA_API ma_uint64 ma_resampler_get_output_latency(const ma_resampler* pResampler); - -/* -Calculates the number of whole input frames that would need to be read from the client in order to output the specified -number of output frames. - -The returned value does not include cached input frames. It only returns the number of extra frames that would need to be -read from the input buffer in order to output the specified number of output frames. -*/ -MA_API ma_result ma_resampler_get_required_input_frame_count(const ma_resampler* pResampler, ma_uint64 outputFrameCount, ma_uint64* pInputFrameCount); - -/* -Calculates the number of whole output frames that would be output after fully reading and consuming the specified number of -input frames. -*/ -MA_API ma_result ma_resampler_get_expected_output_frame_count(const ma_resampler* pResampler, ma_uint64 inputFrameCount, ma_uint64* pOutputFrameCount); - -/* -Resets the resampler's timer and clears its internal cache. -*/ -MA_API ma_result ma_resampler_reset(ma_resampler* pResampler); - - -/************************************************************************************************************************************************************** - -Channel Conversion - -**************************************************************************************************************************************************************/ -typedef enum -{ - ma_channel_conversion_path_unknown, - ma_channel_conversion_path_passthrough, - ma_channel_conversion_path_mono_out, /* Converting to mono. */ - ma_channel_conversion_path_mono_in, /* Converting from mono. */ - ma_channel_conversion_path_shuffle, /* Simple shuffle. Will use this when all channels are present in both input and output channel maps, but just in a different order. */ - ma_channel_conversion_path_weights /* Blended based on weights. */ -} ma_channel_conversion_path; - -typedef enum -{ - ma_mono_expansion_mode_duplicate = 0, /* The default. */ - ma_mono_expansion_mode_average, /* Average the mono channel across all channels. */ - ma_mono_expansion_mode_stereo_only, /* Duplicate to the left and right channels only and ignore the others. */ - ma_mono_expansion_mode_default = ma_mono_expansion_mode_duplicate -} ma_mono_expansion_mode; - -typedef struct -{ - ma_format format; - ma_uint32 channelsIn; - ma_uint32 channelsOut; - const ma_channel* pChannelMapIn; - const ma_channel* pChannelMapOut; - ma_channel_mix_mode mixingMode; - ma_bool32 calculateLFEFromSpatialChannels; /* When an output LFE channel is present, but no input LFE, set to true to set the output LFE to the average of all spatial channels (LR, FR, etc.). Ignored when an input LFE is present. */ - float** ppWeights; /* [in][out]. Only used when mixingMode is set to ma_channel_mix_mode_custom_weights. */ -} ma_channel_converter_config; - -MA_API ma_channel_converter_config ma_channel_converter_config_init(ma_format format, ma_uint32 channelsIn, const ma_channel* pChannelMapIn, ma_uint32 channelsOut, const ma_channel* pChannelMapOut, ma_channel_mix_mode mixingMode); - -typedef struct -{ - ma_format format; - ma_uint32 channelsIn; - ma_uint32 channelsOut; - ma_channel_mix_mode mixingMode; - ma_channel_conversion_path conversionPath; - ma_channel* pChannelMapIn; - ma_channel* pChannelMapOut; - ma_uint8* pShuffleTable; /* Indexed by output channel index. */ - union - { - float** f32; - ma_int32** s16; - } weights; /* [in][out] */ - - /* Memory management. */ - void* _pHeap; - ma_bool32 _ownsHeap; -} ma_channel_converter; - -MA_API ma_result ma_channel_converter_get_heap_size(const ma_channel_converter_config* pConfig, size_t* pHeapSizeInBytes); -MA_API ma_result ma_channel_converter_init_preallocated(const ma_channel_converter_config* pConfig, void* pHeap, ma_channel_converter* pConverter); -MA_API ma_result ma_channel_converter_init(const ma_channel_converter_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_channel_converter* pConverter); -MA_API void ma_channel_converter_uninit(ma_channel_converter* pConverter, const ma_allocation_callbacks* pAllocationCallbacks); -MA_API ma_result ma_channel_converter_process_pcm_frames(ma_channel_converter* pConverter, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount); -MA_API ma_result ma_channel_converter_get_input_channel_map(const ma_channel_converter* pConverter, ma_channel* pChannelMap, size_t channelMapCap); -MA_API ma_result ma_channel_converter_get_output_channel_map(const ma_channel_converter* pConverter, ma_channel* pChannelMap, size_t channelMapCap); - - -/************************************************************************************************************************************************************** - -Data Conversion - -**************************************************************************************************************************************************************/ -typedef struct -{ - ma_format formatIn; - ma_format formatOut; - ma_uint32 channelsIn; - ma_uint32 channelsOut; - ma_uint32 sampleRateIn; - ma_uint32 sampleRateOut; - ma_channel* pChannelMapIn; - ma_channel* pChannelMapOut; - ma_dither_mode ditherMode; - ma_channel_mix_mode channelMixMode; - ma_bool32 calculateLFEFromSpatialChannels; /* When an output LFE channel is present, but no input LFE, set to true to set the output LFE to the average of all spatial channels (LR, FR, etc.). Ignored when an input LFE is present. */ - float** ppChannelWeights; /* [in][out]. Only used when mixingMode is set to ma_channel_mix_mode_custom_weights. */ - ma_bool32 allowDynamicSampleRate; - ma_resampler_config resampling; -} ma_data_converter_config; - -MA_API ma_data_converter_config ma_data_converter_config_init_default(void); -MA_API ma_data_converter_config ma_data_converter_config_init(ma_format formatIn, ma_format formatOut, ma_uint32 channelsIn, ma_uint32 channelsOut, ma_uint32 sampleRateIn, ma_uint32 sampleRateOut); - - -typedef enum -{ - ma_data_converter_execution_path_passthrough, /* No conversion. */ - ma_data_converter_execution_path_format_only, /* Only format conversion. */ - ma_data_converter_execution_path_channels_only, /* Only channel conversion. */ - ma_data_converter_execution_path_resample_only, /* Only resampling. */ - ma_data_converter_execution_path_resample_first, /* All conversions, but resample as the first step. */ - ma_data_converter_execution_path_channels_first /* All conversions, but channels as the first step. */ -} ma_data_converter_execution_path; - -typedef struct -{ - ma_format formatIn; - ma_format formatOut; - ma_uint32 channelsIn; - ma_uint32 channelsOut; - ma_uint32 sampleRateIn; - ma_uint32 sampleRateOut; - ma_dither_mode ditherMode; - ma_data_converter_execution_path executionPath; /* The execution path the data converter will follow when processing. */ - ma_channel_converter channelConverter; - ma_resampler resampler; - ma_bool8 hasPreFormatConversion; - ma_bool8 hasPostFormatConversion; - ma_bool8 hasChannelConverter; - ma_bool8 hasResampler; - ma_bool8 isPassthrough; - - /* Memory management. */ - ma_bool8 _ownsHeap; - void* _pHeap; -} ma_data_converter; - -MA_API ma_result ma_data_converter_get_heap_size(const ma_data_converter_config* pConfig, size_t* pHeapSizeInBytes); -MA_API ma_result ma_data_converter_init_preallocated(const ma_data_converter_config* pConfig, void* pHeap, ma_data_converter* pConverter); -MA_API ma_result ma_data_converter_init(const ma_data_converter_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_data_converter* pConverter); -MA_API void ma_data_converter_uninit(ma_data_converter* pConverter, const ma_allocation_callbacks* pAllocationCallbacks); -MA_API ma_result ma_data_converter_process_pcm_frames(ma_data_converter* pConverter, const void* pFramesIn, ma_uint64* pFrameCountIn, void* pFramesOut, ma_uint64* pFrameCountOut); -MA_API ma_result ma_data_converter_set_rate(ma_data_converter* pConverter, ma_uint32 sampleRateIn, ma_uint32 sampleRateOut); -MA_API ma_result ma_data_converter_set_rate_ratio(ma_data_converter* pConverter, float ratioInOut); -MA_API ma_uint64 ma_data_converter_get_input_latency(const ma_data_converter* pConverter); -MA_API ma_uint64 ma_data_converter_get_output_latency(const ma_data_converter* pConverter); -MA_API ma_result ma_data_converter_get_required_input_frame_count(const ma_data_converter* pConverter, ma_uint64 outputFrameCount, ma_uint64* pInputFrameCount); -MA_API ma_result ma_data_converter_get_expected_output_frame_count(const ma_data_converter* pConverter, ma_uint64 inputFrameCount, ma_uint64* pOutputFrameCount); -MA_API ma_result ma_data_converter_get_input_channel_map(const ma_data_converter* pConverter, ma_channel* pChannelMap, size_t channelMapCap); -MA_API ma_result ma_data_converter_get_output_channel_map(const ma_data_converter* pConverter, ma_channel* pChannelMap, size_t channelMapCap); -MA_API ma_result ma_data_converter_reset(ma_data_converter* pConverter); - - -/************************************************************************************************************************************************************ - -Format Conversion - -************************************************************************************************************************************************************/ -MA_API void ma_pcm_u8_to_s16(void* pOut, const void* pIn, ma_uint64 count, ma_dither_mode ditherMode); -MA_API void ma_pcm_u8_to_s24(void* pOut, const void* pIn, ma_uint64 count, ma_dither_mode ditherMode); -MA_API void ma_pcm_u8_to_s32(void* pOut, const void* pIn, ma_uint64 count, ma_dither_mode ditherMode); -MA_API void ma_pcm_u8_to_f32(void* pOut, const void* pIn, ma_uint64 count, ma_dither_mode ditherMode); -MA_API void ma_pcm_s16_to_u8(void* pOut, const void* pIn, ma_uint64 count, ma_dither_mode ditherMode); -MA_API void ma_pcm_s16_to_s24(void* pOut, const void* pIn, ma_uint64 count, ma_dither_mode ditherMode); -MA_API void ma_pcm_s16_to_s32(void* pOut, const void* pIn, ma_uint64 count, ma_dither_mode ditherMode); -MA_API void ma_pcm_s16_to_f32(void* pOut, const void* pIn, ma_uint64 count, ma_dither_mode ditherMode); -MA_API void ma_pcm_s24_to_u8(void* pOut, const void* pIn, ma_uint64 count, ma_dither_mode ditherMode); -MA_API void ma_pcm_s24_to_s16(void* pOut, const void* pIn, ma_uint64 count, ma_dither_mode ditherMode); -MA_API void ma_pcm_s24_to_s32(void* pOut, const void* pIn, ma_uint64 count, ma_dither_mode ditherMode); -MA_API void ma_pcm_s24_to_f32(void* pOut, const void* pIn, ma_uint64 count, ma_dither_mode ditherMode); -MA_API void ma_pcm_s32_to_u8(void* pOut, const void* pIn, ma_uint64 count, ma_dither_mode ditherMode); -MA_API void ma_pcm_s32_to_s16(void* pOut, const void* pIn, ma_uint64 count, ma_dither_mode ditherMode); -MA_API void ma_pcm_s32_to_s24(void* pOut, const void* pIn, ma_uint64 count, ma_dither_mode ditherMode); -MA_API void ma_pcm_s32_to_f32(void* pOut, const void* pIn, ma_uint64 count, ma_dither_mode ditherMode); -MA_API void ma_pcm_f32_to_u8(void* pOut, const void* pIn, ma_uint64 count, ma_dither_mode ditherMode); -MA_API void ma_pcm_f32_to_s16(void* pOut, const void* pIn, ma_uint64 count, ma_dither_mode ditherMode); -MA_API void ma_pcm_f32_to_s24(void* pOut, const void* pIn, ma_uint64 count, ma_dither_mode ditherMode); -MA_API void ma_pcm_f32_to_s32(void* pOut, const void* pIn, ma_uint64 count, ma_dither_mode ditherMode); -MA_API void ma_pcm_convert(void* pOut, ma_format formatOut, const void* pIn, ma_format formatIn, ma_uint64 sampleCount, ma_dither_mode ditherMode); -MA_API void ma_convert_pcm_frames_format(void* pOut, ma_format formatOut, const void* pIn, ma_format formatIn, ma_uint64 frameCount, ma_uint32 channels, ma_dither_mode ditherMode); - -/* -Deinterleaves an interleaved buffer. -*/ -MA_API void ma_deinterleave_pcm_frames(ma_format format, ma_uint32 channels, ma_uint64 frameCount, const void* pInterleavedPCMFrames, void** ppDeinterleavedPCMFrames); - -/* -Interleaves a group of deinterleaved buffers. -*/ -MA_API void ma_interleave_pcm_frames(ma_format format, ma_uint32 channels, ma_uint64 frameCount, const void** ppDeinterleavedPCMFrames, void* pInterleavedPCMFrames); - - -/************************************************************************************************************************************************************ - -Channel Maps - -************************************************************************************************************************************************************/ -/* -This is used in the shuffle table to indicate that the channel index is undefined and should be ignored. -*/ -#define MA_CHANNEL_INDEX_NULL 255 - -/* -Retrieves the channel position of the specified channel in the given channel map. - -The pChannelMap parameter can be null, in which case miniaudio's default channel map will be assumed. -*/ -MA_API ma_channel ma_channel_map_get_channel(const ma_channel* pChannelMap, ma_uint32 channelCount, ma_uint32 channelIndex); - -/* -Initializes a blank channel map. - -When a blank channel map is specified anywhere it indicates that the native channel map should be used. -*/ -MA_API void ma_channel_map_init_blank(ma_channel* pChannelMap, ma_uint32 channels); - -/* -Helper for retrieving a standard channel map. - -The output channel map buffer must have a capacity of at least `channelMapCap`. -*/ -MA_API void ma_channel_map_init_standard(ma_standard_channel_map standardChannelMap, ma_channel* pChannelMap, size_t channelMapCap, ma_uint32 channels); - -/* -Copies a channel map. - -Both input and output channel map buffers must have a capacity of at least `channels`. -*/ -MA_API void ma_channel_map_copy(ma_channel* pOut, const ma_channel* pIn, ma_uint32 channels); - -/* -Copies a channel map if one is specified, otherwise copies the default channel map. - -The output buffer must have a capacity of at least `channels`. If not NULL, the input channel map must also have a capacity of at least `channels`. -*/ -MA_API void ma_channel_map_copy_or_default(ma_channel* pOut, size_t channelMapCapOut, const ma_channel* pIn, ma_uint32 channels); - - -/* -Determines whether or not a channel map is valid. - -A blank channel map is valid (all channels set to MA_CHANNEL_NONE). The way a blank channel map is handled is context specific, but -is usually treated as a passthrough. - -Invalid channel maps: - - A channel map with no channels - - A channel map with more than one channel and a mono channel - -The channel map buffer must have a capacity of at least `channels`. -*/ -MA_API ma_bool32 ma_channel_map_is_valid(const ma_channel* pChannelMap, ma_uint32 channels); - -/* -Helper for comparing two channel maps for equality. - -This assumes the channel count is the same between the two. - -Both channels map buffers must have a capacity of at least `channels`. -*/ -MA_API ma_bool32 ma_channel_map_is_equal(const ma_channel* pChannelMapA, const ma_channel* pChannelMapB, ma_uint32 channels); - -/* -Helper for determining if a channel map is blank (all channels set to MA_CHANNEL_NONE). - -The channel map buffer must have a capacity of at least `channels`. -*/ -MA_API ma_bool32 ma_channel_map_is_blank(const ma_channel* pChannelMap, ma_uint32 channels); - -/* -Helper for determining whether or not a channel is present in the given channel map. - -The channel map buffer must have a capacity of at least `channels`. -*/ -MA_API ma_bool32 ma_channel_map_contains_channel_position(ma_uint32 channels, const ma_channel* pChannelMap, ma_channel channelPosition); - -/* -Find a channel position in the given channel map. Returns MA_TRUE if the channel is found; MA_FALSE otherwise. The -index of the channel is output to `pChannelIndex`. - -The channel map buffer must have a capacity of at least `channels`. -*/ -MA_API ma_bool32 ma_channel_map_find_channel_position(ma_uint32 channels, const ma_channel* pChannelMap, ma_channel channelPosition, ma_uint32* pChannelIndex); - -/* -Generates a string representing the given channel map. - -This is for printing and debugging purposes, not serialization/deserialization. - -Returns the length of the string, not including the null terminator. -*/ -MA_API size_t ma_channel_map_to_string(const ma_channel* pChannelMap, ma_uint32 channels, char* pBufferOut, size_t bufferCap); - -/* -Retrieves a human readable version of a channel position. -*/ -MA_API const char* ma_channel_position_to_string(ma_channel channel); - - -/************************************************************************************************************************************************************ - -Conversion Helpers - -************************************************************************************************************************************************************/ - -/* -High-level helper for doing a full format conversion in one go. Returns the number of output frames. Call this with pOut set to NULL to -determine the required size of the output buffer. frameCountOut should be set to the capacity of pOut. If pOut is NULL, frameCountOut is -ignored. - -A return value of 0 indicates an error. - -This function is useful for one-off bulk conversions, but if you're streaming data you should use the ma_data_converter APIs instead. -*/ -MA_API ma_uint64 ma_convert_frames(void* pOut, ma_uint64 frameCountOut, ma_format formatOut, ma_uint32 channelsOut, ma_uint32 sampleRateOut, const void* pIn, ma_uint64 frameCountIn, ma_format formatIn, ma_uint32 channelsIn, ma_uint32 sampleRateIn); -MA_API ma_uint64 ma_convert_frames_ex(void* pOut, ma_uint64 frameCountOut, const void* pIn, ma_uint64 frameCountIn, const ma_data_converter_config* pConfig); - - -/************************************************************************************************************************************************************ - -Data Source - -************************************************************************************************************************************************************/ -typedef void ma_data_source; - -#define MA_DATA_SOURCE_SELF_MANAGED_RANGE_AND_LOOP_POINT 0x00000001 - -typedef struct -{ - ma_result (* onRead)(ma_data_source* pDataSource, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead); - ma_result (* onSeek)(ma_data_source* pDataSource, ma_uint64 frameIndex); - ma_result (* onGetDataFormat)(ma_data_source* pDataSource, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate, ma_channel* pChannelMap, size_t channelMapCap); - ma_result (* onGetCursor)(ma_data_source* pDataSource, ma_uint64* pCursor); - ma_result (* onGetLength)(ma_data_source* pDataSource, ma_uint64* pLength); - ma_result (* onSetLooping)(ma_data_source* pDataSource, ma_bool32 isLooping); - ma_uint32 flags; -} ma_data_source_vtable; - -typedef ma_data_source* (* ma_data_source_get_next_proc)(ma_data_source* pDataSource); - -typedef struct -{ - const ma_data_source_vtable* vtable; -} ma_data_source_config; - -MA_API ma_data_source_config ma_data_source_config_init(void); - - -typedef struct -{ - const ma_data_source_vtable* vtable; - ma_uint64 rangeBegInFrames; - ma_uint64 rangeEndInFrames; /* Set to -1 for unranged (default). */ - ma_uint64 loopBegInFrames; /* Relative to rangeBegInFrames. */ - ma_uint64 loopEndInFrames; /* Relative to rangeBegInFrames. Set to -1 for the end of the range. */ - ma_data_source* pCurrent; /* When non-NULL, the data source being initialized will act as a proxy and will route all operations to pCurrent. Used in conjunction with pNext/onGetNext for seamless chaining. */ - ma_data_source* pNext; /* When set to NULL, onGetNext will be used. */ - ma_data_source_get_next_proc onGetNext; /* Will be used when pNext is NULL. If both are NULL, no next will be used. */ - MA_ATOMIC(4, ma_bool32) isLooping; -} ma_data_source_base; - -MA_API ma_result ma_data_source_init(const ma_data_source_config* pConfig, ma_data_source* pDataSource); -MA_API void ma_data_source_uninit(ma_data_source* pDataSource); -MA_API ma_result ma_data_source_read_pcm_frames(ma_data_source* pDataSource, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead); /* Must support pFramesOut = NULL in which case a forward seek should be performed. */ -MA_API ma_result ma_data_source_seek_pcm_frames(ma_data_source* pDataSource, ma_uint64 frameCount, ma_uint64* pFramesSeeked); /* Can only seek forward. Equivalent to ma_data_source_read_pcm_frames(pDataSource, NULL, frameCount, &framesRead); */ -MA_API ma_result ma_data_source_seek_to_pcm_frame(ma_data_source* pDataSource, ma_uint64 frameIndex); -MA_API ma_result ma_data_source_seek_seconds(ma_data_source* pDataSource, float secondCount, float* pSecondsSeeked); /* Can only seek forward. Abstraction to ma_data_source_seek_pcm_frames() */ -MA_API ma_result ma_data_source_seek_to_second(ma_data_source* pDataSource, float seekPointInSeconds); /* Abstraction to ma_data_source_seek_to_pcm_frame() */ -MA_API ma_result ma_data_source_get_data_format(ma_data_source* pDataSource, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate, ma_channel* pChannelMap, size_t channelMapCap); -MA_API ma_result ma_data_source_get_cursor_in_pcm_frames(ma_data_source* pDataSource, ma_uint64* pCursor); -MA_API ma_result ma_data_source_get_length_in_pcm_frames(ma_data_source* pDataSource, ma_uint64* pLength); /* Returns MA_NOT_IMPLEMENTED if the length is unknown or cannot be determined. Decoders can return this. */ -MA_API ma_result ma_data_source_get_cursor_in_seconds(ma_data_source* pDataSource, float* pCursor); -MA_API ma_result ma_data_source_get_length_in_seconds(ma_data_source* pDataSource, float* pLength); -MA_API ma_result ma_data_source_set_looping(ma_data_source* pDataSource, ma_bool32 isLooping); -MA_API ma_bool32 ma_data_source_is_looping(const ma_data_source* pDataSource); -MA_API ma_result ma_data_source_set_range_in_pcm_frames(ma_data_source* pDataSource, ma_uint64 rangeBegInFrames, ma_uint64 rangeEndInFrames); -MA_API void ma_data_source_get_range_in_pcm_frames(const ma_data_source* pDataSource, ma_uint64* pRangeBegInFrames, ma_uint64* pRangeEndInFrames); -MA_API ma_result ma_data_source_set_loop_point_in_pcm_frames(ma_data_source* pDataSource, ma_uint64 loopBegInFrames, ma_uint64 loopEndInFrames); -MA_API void ma_data_source_get_loop_point_in_pcm_frames(const ma_data_source* pDataSource, ma_uint64* pLoopBegInFrames, ma_uint64* pLoopEndInFrames); -MA_API ma_result ma_data_source_set_current(ma_data_source* pDataSource, ma_data_source* pCurrentDataSource); -MA_API ma_data_source* ma_data_source_get_current(const ma_data_source* pDataSource); -MA_API ma_result ma_data_source_set_next(ma_data_source* pDataSource, ma_data_source* pNextDataSource); -MA_API ma_data_source* ma_data_source_get_next(const ma_data_source* pDataSource); -MA_API ma_result ma_data_source_set_next_callback(ma_data_source* pDataSource, ma_data_source_get_next_proc onGetNext); -MA_API ma_data_source_get_next_proc ma_data_source_get_next_callback(const ma_data_source* pDataSource); - - -typedef struct -{ - ma_data_source_base ds; - ma_format format; - ma_uint32 channels; - ma_uint32 sampleRate; - ma_uint64 cursor; - ma_uint64 sizeInFrames; - const void* pData; -} ma_audio_buffer_ref; - -MA_API ma_result ma_audio_buffer_ref_init(ma_format format, ma_uint32 channels, const void* pData, ma_uint64 sizeInFrames, ma_audio_buffer_ref* pAudioBufferRef); -MA_API void ma_audio_buffer_ref_uninit(ma_audio_buffer_ref* pAudioBufferRef); -MA_API ma_result ma_audio_buffer_ref_set_data(ma_audio_buffer_ref* pAudioBufferRef, const void* pData, ma_uint64 sizeInFrames); -MA_API ma_uint64 ma_audio_buffer_ref_read_pcm_frames(ma_audio_buffer_ref* pAudioBufferRef, void* pFramesOut, ma_uint64 frameCount, ma_bool32 loop); -MA_API ma_result ma_audio_buffer_ref_seek_to_pcm_frame(ma_audio_buffer_ref* pAudioBufferRef, ma_uint64 frameIndex); -MA_API ma_result ma_audio_buffer_ref_map(ma_audio_buffer_ref* pAudioBufferRef, void** ppFramesOut, ma_uint64* pFrameCount); -MA_API ma_result ma_audio_buffer_ref_unmap(ma_audio_buffer_ref* pAudioBufferRef, ma_uint64 frameCount); /* Returns MA_AT_END if the end has been reached. This should be considered successful. */ -MA_API ma_bool32 ma_audio_buffer_ref_at_end(const ma_audio_buffer_ref* pAudioBufferRef); -MA_API ma_result ma_audio_buffer_ref_get_cursor_in_pcm_frames(const ma_audio_buffer_ref* pAudioBufferRef, ma_uint64* pCursor); -MA_API ma_result ma_audio_buffer_ref_get_length_in_pcm_frames(const ma_audio_buffer_ref* pAudioBufferRef, ma_uint64* pLength); -MA_API ma_result ma_audio_buffer_ref_get_available_frames(const ma_audio_buffer_ref* pAudioBufferRef, ma_uint64* pAvailableFrames); - - - -typedef struct -{ - ma_format format; - ma_uint32 channels; - ma_uint32 sampleRate; - ma_uint64 sizeInFrames; - const void* pData; /* If set to NULL, will allocate a block of memory for you. */ - ma_allocation_callbacks allocationCallbacks; -} ma_audio_buffer_config; - -MA_API ma_audio_buffer_config ma_audio_buffer_config_init(ma_format format, ma_uint32 channels, ma_uint64 sizeInFrames, const void* pData, const ma_allocation_callbacks* pAllocationCallbacks); - -typedef struct -{ - ma_audio_buffer_ref ref; - ma_allocation_callbacks allocationCallbacks; - ma_bool32 ownsData; /* Used to control whether or not miniaudio owns the data buffer. If set to true, pData will be freed in ma_audio_buffer_uninit(). */ - ma_uint8 _pExtraData[1]; /* For allocating a buffer with the memory located directly after the other memory of the structure. */ -} ma_audio_buffer; - -MA_API ma_result ma_audio_buffer_init(const ma_audio_buffer_config* pConfig, ma_audio_buffer* pAudioBuffer); -MA_API ma_result ma_audio_buffer_init_copy(const ma_audio_buffer_config* pConfig, ma_audio_buffer* pAudioBuffer); -MA_API ma_result ma_audio_buffer_alloc_and_init(const ma_audio_buffer_config* pConfig, ma_audio_buffer** ppAudioBuffer); /* Always copies the data. Doesn't make sense to use this otherwise. Use ma_audio_buffer_uninit_and_free() to uninit. */ -MA_API void ma_audio_buffer_uninit(ma_audio_buffer* pAudioBuffer); -MA_API void ma_audio_buffer_uninit_and_free(ma_audio_buffer* pAudioBuffer); -MA_API ma_uint64 ma_audio_buffer_read_pcm_frames(ma_audio_buffer* pAudioBuffer, void* pFramesOut, ma_uint64 frameCount, ma_bool32 loop); -MA_API ma_result ma_audio_buffer_seek_to_pcm_frame(ma_audio_buffer* pAudioBuffer, ma_uint64 frameIndex); -MA_API ma_result ma_audio_buffer_map(ma_audio_buffer* pAudioBuffer, void** ppFramesOut, ma_uint64* pFrameCount); -MA_API ma_result ma_audio_buffer_unmap(ma_audio_buffer* pAudioBuffer, ma_uint64 frameCount); /* Returns MA_AT_END if the end has been reached. This should be considered successful. */ -MA_API ma_bool32 ma_audio_buffer_at_end(const ma_audio_buffer* pAudioBuffer); -MA_API ma_result ma_audio_buffer_get_cursor_in_pcm_frames(const ma_audio_buffer* pAudioBuffer, ma_uint64* pCursor); -MA_API ma_result ma_audio_buffer_get_length_in_pcm_frames(const ma_audio_buffer* pAudioBuffer, ma_uint64* pLength); -MA_API ma_result ma_audio_buffer_get_available_frames(const ma_audio_buffer* pAudioBuffer, ma_uint64* pAvailableFrames); - - -/* -Paged Audio Buffer -================== -A paged audio buffer is made up of a linked list of pages. It's expandable, but not shrinkable. It -can be used for cases where audio data is streamed in asynchronously while allowing data to be read -at the same time. - -This is lock-free, but not 100% thread safe. You can append a page and read from the buffer across -simultaneously across different threads, however only one thread at a time can append, and only one -thread at a time can read and seek. -*/ -typedef struct ma_paged_audio_buffer_page ma_paged_audio_buffer_page; -struct ma_paged_audio_buffer_page -{ - MA_ATOMIC(MA_SIZEOF_PTR, ma_paged_audio_buffer_page*) pNext; - ma_uint64 sizeInFrames; - ma_uint8 pAudioData[1]; -}; - -typedef struct -{ - ma_format format; - ma_uint32 channels; - ma_paged_audio_buffer_page head; /* Dummy head for the lock-free algorithm. Always has a size of 0. */ - MA_ATOMIC(MA_SIZEOF_PTR, ma_paged_audio_buffer_page*) pTail; /* Never null. Initially set to &head. */ -} ma_paged_audio_buffer_data; - -MA_API ma_result ma_paged_audio_buffer_data_init(ma_format format, ma_uint32 channels, ma_paged_audio_buffer_data* pData); -MA_API void ma_paged_audio_buffer_data_uninit(ma_paged_audio_buffer_data* pData, const ma_allocation_callbacks* pAllocationCallbacks); -MA_API ma_paged_audio_buffer_page* ma_paged_audio_buffer_data_get_head(ma_paged_audio_buffer_data* pData); -MA_API ma_paged_audio_buffer_page* ma_paged_audio_buffer_data_get_tail(ma_paged_audio_buffer_data* pData); -MA_API ma_result ma_paged_audio_buffer_data_get_length_in_pcm_frames(ma_paged_audio_buffer_data* pData, ma_uint64* pLength); -MA_API ma_result ma_paged_audio_buffer_data_allocate_page(ma_paged_audio_buffer_data* pData, ma_uint64 pageSizeInFrames, const void* pInitialData, const ma_allocation_callbacks* pAllocationCallbacks, ma_paged_audio_buffer_page** ppPage); -MA_API ma_result ma_paged_audio_buffer_data_free_page(ma_paged_audio_buffer_data* pData, ma_paged_audio_buffer_page* pPage, const ma_allocation_callbacks* pAllocationCallbacks); -MA_API ma_result ma_paged_audio_buffer_data_append_page(ma_paged_audio_buffer_data* pData, ma_paged_audio_buffer_page* pPage); -MA_API ma_result ma_paged_audio_buffer_data_allocate_and_append_page(ma_paged_audio_buffer_data* pData, ma_uint32 pageSizeInFrames, const void* pInitialData, const ma_allocation_callbacks* pAllocationCallbacks); - - -typedef struct -{ - ma_paged_audio_buffer_data* pData; /* Must not be null. */ -} ma_paged_audio_buffer_config; - -MA_API ma_paged_audio_buffer_config ma_paged_audio_buffer_config_init(ma_paged_audio_buffer_data* pData); - - -typedef struct -{ - ma_data_source_base ds; - ma_paged_audio_buffer_data* pData; /* Audio data is read from here. Cannot be null. */ - ma_paged_audio_buffer_page* pCurrent; - ma_uint64 relativeCursor; /* Relative to the current page. */ - ma_uint64 absoluteCursor; -} ma_paged_audio_buffer; - -MA_API ma_result ma_paged_audio_buffer_init(const ma_paged_audio_buffer_config* pConfig, ma_paged_audio_buffer* pPagedAudioBuffer); -MA_API void ma_paged_audio_buffer_uninit(ma_paged_audio_buffer* pPagedAudioBuffer); -MA_API ma_result ma_paged_audio_buffer_read_pcm_frames(ma_paged_audio_buffer* pPagedAudioBuffer, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead); /* Returns MA_AT_END if no more pages available. */ -MA_API ma_result ma_paged_audio_buffer_seek_to_pcm_frame(ma_paged_audio_buffer* pPagedAudioBuffer, ma_uint64 frameIndex); -MA_API ma_result ma_paged_audio_buffer_get_cursor_in_pcm_frames(ma_paged_audio_buffer* pPagedAudioBuffer, ma_uint64* pCursor); -MA_API ma_result ma_paged_audio_buffer_get_length_in_pcm_frames(ma_paged_audio_buffer* pPagedAudioBuffer, ma_uint64* pLength); - - - -/************************************************************************************************************************************************************ - -Ring Buffer - -************************************************************************************************************************************************************/ -typedef struct -{ - void* pBuffer; - ma_uint32 subbufferSizeInBytes; - ma_uint32 subbufferCount; - ma_uint32 subbufferStrideInBytes; - MA_ATOMIC(4, ma_uint32) encodedReadOffset; /* Most significant bit is the loop flag. Lower 31 bits contains the actual offset in bytes. Must be used atomically. */ - MA_ATOMIC(4, ma_uint32) encodedWriteOffset; /* Most significant bit is the loop flag. Lower 31 bits contains the actual offset in bytes. Must be used atomically. */ - ma_bool8 ownsBuffer; /* Used to know whether or not miniaudio is responsible for free()-ing the buffer. */ - ma_bool8 clearOnWriteAcquire; /* When set, clears the acquired write buffer before returning from ma_rb_acquire_write(). */ - ma_allocation_callbacks allocationCallbacks; -} ma_rb; - -MA_API ma_result ma_rb_init_ex(size_t subbufferSizeInBytes, size_t subbufferCount, size_t subbufferStrideInBytes, void* pOptionalPreallocatedBuffer, const ma_allocation_callbacks* pAllocationCallbacks, ma_rb* pRB); -MA_API ma_result ma_rb_init(size_t bufferSizeInBytes, void* pOptionalPreallocatedBuffer, const ma_allocation_callbacks* pAllocationCallbacks, ma_rb* pRB); -MA_API void ma_rb_uninit(ma_rb* pRB); -MA_API void ma_rb_reset(ma_rb* pRB); -MA_API ma_result ma_rb_acquire_read(ma_rb* pRB, size_t* pSizeInBytes, void** ppBufferOut); -MA_API ma_result ma_rb_commit_read(ma_rb* pRB, size_t sizeInBytes); -MA_API ma_result ma_rb_acquire_write(ma_rb* pRB, size_t* pSizeInBytes, void** ppBufferOut); -MA_API ma_result ma_rb_commit_write(ma_rb* pRB, size_t sizeInBytes); -MA_API ma_result ma_rb_seek_read(ma_rb* pRB, size_t offsetInBytes); -MA_API ma_result ma_rb_seek_write(ma_rb* pRB, size_t offsetInBytes); -MA_API ma_int32 ma_rb_pointer_distance(ma_rb* pRB); /* Returns the distance between the write pointer and the read pointer. Should never be negative for a correct program. Will return the number of bytes that can be read before the read pointer hits the write pointer. */ -MA_API ma_uint32 ma_rb_available_read(ma_rb* pRB); -MA_API ma_uint32 ma_rb_available_write(ma_rb* pRB); -MA_API size_t ma_rb_get_subbuffer_size(ma_rb* pRB); -MA_API size_t ma_rb_get_subbuffer_stride(ma_rb* pRB); -MA_API size_t ma_rb_get_subbuffer_offset(ma_rb* pRB, size_t subbufferIndex); -MA_API void* ma_rb_get_subbuffer_ptr(ma_rb* pRB, size_t subbufferIndex, void* pBuffer); - - -typedef struct -{ - ma_data_source_base ds; - ma_rb rb; - ma_format format; - ma_uint32 channels; - ma_uint32 sampleRate; /* Not required for the ring buffer itself, but useful for associating the data with some sample rate, particularly for data sources. */ -} ma_pcm_rb; - -MA_API ma_result ma_pcm_rb_init_ex(ma_format format, ma_uint32 channels, ma_uint32 subbufferSizeInFrames, ma_uint32 subbufferCount, ma_uint32 subbufferStrideInFrames, void* pOptionalPreallocatedBuffer, const ma_allocation_callbacks* pAllocationCallbacks, ma_pcm_rb* pRB); -MA_API ma_result ma_pcm_rb_init(ma_format format, ma_uint32 channels, ma_uint32 bufferSizeInFrames, void* pOptionalPreallocatedBuffer, const ma_allocation_callbacks* pAllocationCallbacks, ma_pcm_rb* pRB); -MA_API void ma_pcm_rb_uninit(ma_pcm_rb* pRB); -MA_API void ma_pcm_rb_reset(ma_pcm_rb* pRB); -MA_API ma_result ma_pcm_rb_acquire_read(ma_pcm_rb* pRB, ma_uint32* pSizeInFrames, void** ppBufferOut); -MA_API ma_result ma_pcm_rb_commit_read(ma_pcm_rb* pRB, ma_uint32 sizeInFrames); -MA_API ma_result ma_pcm_rb_acquire_write(ma_pcm_rb* pRB, ma_uint32* pSizeInFrames, void** ppBufferOut); -MA_API ma_result ma_pcm_rb_commit_write(ma_pcm_rb* pRB, ma_uint32 sizeInFrames); -MA_API ma_result ma_pcm_rb_seek_read(ma_pcm_rb* pRB, ma_uint32 offsetInFrames); -MA_API ma_result ma_pcm_rb_seek_write(ma_pcm_rb* pRB, ma_uint32 offsetInFrames); -MA_API ma_int32 ma_pcm_rb_pointer_distance(ma_pcm_rb* pRB); /* Return value is in frames. */ -MA_API ma_uint32 ma_pcm_rb_available_read(ma_pcm_rb* pRB); -MA_API ma_uint32 ma_pcm_rb_available_write(ma_pcm_rb* pRB); -MA_API ma_uint32 ma_pcm_rb_get_subbuffer_size(ma_pcm_rb* pRB); -MA_API ma_uint32 ma_pcm_rb_get_subbuffer_stride(ma_pcm_rb* pRB); -MA_API ma_uint32 ma_pcm_rb_get_subbuffer_offset(ma_pcm_rb* pRB, ma_uint32 subbufferIndex); -MA_API void* ma_pcm_rb_get_subbuffer_ptr(ma_pcm_rb* pRB, ma_uint32 subbufferIndex, void* pBuffer); -MA_API ma_format ma_pcm_rb_get_format(const ma_pcm_rb* pRB); -MA_API ma_uint32 ma_pcm_rb_get_channels(const ma_pcm_rb* pRB); -MA_API ma_uint32 ma_pcm_rb_get_sample_rate(const ma_pcm_rb* pRB); -MA_API void ma_pcm_rb_set_sample_rate(ma_pcm_rb* pRB, ma_uint32 sampleRate); - - -/* -The idea of the duplex ring buffer is to act as the intermediary buffer when running two asynchronous devices in a duplex set up. The -capture device writes to it, and then a playback device reads from it. - -At the moment this is just a simple naive implementation, but in the future I want to implement some dynamic resampling to seamlessly -handle desyncs. Note that the API is work in progress and may change at any time in any version. - -The size of the buffer is based on the capture side since that's what'll be written to the buffer. It is based on the capture period size -in frames. The internal sample rate of the capture device is also needed in order to calculate the size. -*/ -typedef struct -{ - ma_pcm_rb rb; -} ma_duplex_rb; - -MA_API ma_result ma_duplex_rb_init(ma_format captureFormat, ma_uint32 captureChannels, ma_uint32 sampleRate, ma_uint32 captureInternalSampleRate, ma_uint32 captureInternalPeriodSizeInFrames, const ma_allocation_callbacks* pAllocationCallbacks, ma_duplex_rb* pRB); -MA_API ma_result ma_duplex_rb_uninit(ma_duplex_rb* pRB); - - -/************************************************************************************************************************************************************ - -Miscellaneous Helpers - -************************************************************************************************************************************************************/ -/* -Retrieves a human readable description of the given result code. -*/ -MA_API const char* ma_result_description(ma_result result); - -/* -malloc() -*/ -MA_API void* ma_malloc(size_t sz, const ma_allocation_callbacks* pAllocationCallbacks); - -/* -calloc() -*/ -MA_API void* ma_calloc(size_t sz, const ma_allocation_callbacks* pAllocationCallbacks); - -/* -realloc() -*/ -MA_API void* ma_realloc(void* p, size_t sz, const ma_allocation_callbacks* pAllocationCallbacks); - -/* -free() -*/ -MA_API void ma_free(void* p, const ma_allocation_callbacks* pAllocationCallbacks); - -/* -Performs an aligned malloc, with the assumption that the alignment is a power of 2. -*/ -MA_API void* ma_aligned_malloc(size_t sz, size_t alignment, const ma_allocation_callbacks* pAllocationCallbacks); - -/* -Free's an aligned malloc'd buffer. -*/ -MA_API void ma_aligned_free(void* p, const ma_allocation_callbacks* pAllocationCallbacks); - -/* -Retrieves a friendly name for a format. -*/ -MA_API const char* ma_get_format_name(ma_format format); - -/* -Blends two frames in floating point format. -*/ -MA_API void ma_blend_f32(float* pOut, float* pInA, float* pInB, float factor, ma_uint32 channels); - -/* -Retrieves the size of a sample in bytes for the given format. - -This API is efficient and is implemented using a lookup table. - -Thread Safety: SAFE - This API is pure. -*/ -MA_API ma_uint32 ma_get_bytes_per_sample(ma_format format); -static MA_INLINE ma_uint32 ma_get_bytes_per_frame(ma_format format, ma_uint32 channels) { return ma_get_bytes_per_sample(format) * channels; } - -/* -Converts a log level to a string. -*/ -MA_API const char* ma_log_level_to_string(ma_uint32 logLevel); - - - - -/************************************************************************************************************************************************************ - -Synchronization - -************************************************************************************************************************************************************/ -/* -Locks a spinlock. -*/ -MA_API ma_result ma_spinlock_lock(volatile ma_spinlock* pSpinlock); - -/* -Locks a spinlock, but does not yield() when looping. -*/ -MA_API ma_result ma_spinlock_lock_noyield(volatile ma_spinlock* pSpinlock); - -/* -Unlocks a spinlock. -*/ -MA_API ma_result ma_spinlock_unlock(volatile ma_spinlock* pSpinlock); - - -#ifndef MA_NO_THREADING - -/* -Creates a mutex. - -A mutex must be created from a valid context. A mutex is initially unlocked. -*/ -MA_API ma_result ma_mutex_init(ma_mutex* pMutex); - -/* -Deletes a mutex. -*/ -MA_API void ma_mutex_uninit(ma_mutex* pMutex); - -/* -Locks a mutex with an infinite timeout. -*/ -MA_API void ma_mutex_lock(ma_mutex* pMutex); - -/* -Unlocks a mutex. -*/ -MA_API void ma_mutex_unlock(ma_mutex* pMutex); - - -/* -Initializes an auto-reset event. -*/ -MA_API ma_result ma_event_init(ma_event* pEvent); - -/* -Uninitializes an auto-reset event. -*/ -MA_API void ma_event_uninit(ma_event* pEvent); - -/* -Waits for the specified auto-reset event to become signalled. -*/ -MA_API ma_result ma_event_wait(ma_event* pEvent); - -/* -Signals the specified auto-reset event. -*/ -MA_API ma_result ma_event_signal(ma_event* pEvent); - - -MA_API ma_result ma_semaphore_init(int initialValue, ma_semaphore* pSemaphore); -MA_API void ma_semaphore_uninit(ma_semaphore* pSemaphore); -MA_API ma_result ma_semaphore_wait(ma_semaphore* pSemaphore); -MA_API ma_result ma_semaphore_release(ma_semaphore* pSemaphore); -#endif /* MA_NO_THREADING */ - - -/* -Fence -===== -This locks while the counter is larger than 0. Counter can be incremented and decremented by any -thread, but care needs to be taken when waiting. It is possible for one thread to acquire the -fence just as another thread returns from ma_fence_wait(). - -The idea behind a fence is to allow you to wait for a group of operations to complete. When an -operation starts, the counter is incremented which locks the fence. When the operation completes, -the fence will be released which decrements the counter. ma_fence_wait() will block until the -counter hits zero. - -If threading is disabled, ma_fence_wait() will spin on the counter. -*/ -typedef struct -{ -#ifndef MA_NO_THREADING - ma_event e; -#endif - ma_uint32 counter; -} ma_fence; - -MA_API ma_result ma_fence_init(ma_fence* pFence); -MA_API void ma_fence_uninit(ma_fence* pFence); -MA_API ma_result ma_fence_acquire(ma_fence* pFence); /* Increment counter. */ -MA_API ma_result ma_fence_release(ma_fence* pFence); /* Decrement counter. */ -MA_API ma_result ma_fence_wait(ma_fence* pFence); /* Wait for counter to reach 0. */ - - - -/* -Notification callback for asynchronous operations. -*/ -typedef void ma_async_notification; - -typedef struct -{ - void (* onSignal)(ma_async_notification* pNotification); -} ma_async_notification_callbacks; - -MA_API ma_result ma_async_notification_signal(ma_async_notification* pNotification); - - -/* -Simple polling notification. - -This just sets a variable when the notification has been signalled which is then polled with ma_async_notification_poll_is_signalled() -*/ -typedef struct -{ - ma_async_notification_callbacks cb; - ma_bool32 signalled; -} ma_async_notification_poll; - -MA_API ma_result ma_async_notification_poll_init(ma_async_notification_poll* pNotificationPoll); -MA_API ma_bool32 ma_async_notification_poll_is_signalled(const ma_async_notification_poll* pNotificationPoll); - - -/* -Event Notification - -This uses an ma_event. If threading is disabled (MA_NO_THREADING), initialization will fail. -*/ -typedef struct -{ - ma_async_notification_callbacks cb; -#ifndef MA_NO_THREADING - ma_event e; -#endif -} ma_async_notification_event; - -MA_API ma_result ma_async_notification_event_init(ma_async_notification_event* pNotificationEvent); -MA_API ma_result ma_async_notification_event_uninit(ma_async_notification_event* pNotificationEvent); -MA_API ma_result ma_async_notification_event_wait(ma_async_notification_event* pNotificationEvent); -MA_API ma_result ma_async_notification_event_signal(ma_async_notification_event* pNotificationEvent); - - - - -/************************************************************************************************************************************************************ - -Job Queue - -************************************************************************************************************************************************************/ - -/* -Slot Allocator --------------- -The idea of the slot allocator is for it to be used in conjunction with a fixed sized buffer. You use the slot allocator to allocate an index that can be used -as the insertion point for an object. - -Slots are reference counted to help mitigate the ABA problem in the lock-free queue we use for tracking jobs. - -The slot index is stored in the low 32 bits. The reference counter is stored in the high 32 bits: - - +-----------------+-----------------+ - | 32 Bits | 32 Bits | - +-----------------+-----------------+ - | Reference Count | Slot Index | - +-----------------+-----------------+ -*/ -typedef struct -{ - ma_uint32 capacity; /* The number of slots to make available. */ -} ma_slot_allocator_config; - -MA_API ma_slot_allocator_config ma_slot_allocator_config_init(ma_uint32 capacity); - - -typedef struct -{ - MA_ATOMIC(4, ma_uint32) bitfield; /* Must be used atomically because the allocation and freeing routines need to make copies of this which must never be optimized away by the compiler. */ -} ma_slot_allocator_group; - -typedef struct -{ - ma_slot_allocator_group* pGroups; /* Slots are grouped in chunks of 32. */ - ma_uint32* pSlots; /* 32 bits for reference counting for ABA mitigation. */ - ma_uint32 count; /* Allocation count. */ - ma_uint32 capacity; - - /* Memory management. */ - ma_bool32 _ownsHeap; - void* _pHeap; -} ma_slot_allocator; - -MA_API ma_result ma_slot_allocator_get_heap_size(const ma_slot_allocator_config* pConfig, size_t* pHeapSizeInBytes); -MA_API ma_result ma_slot_allocator_init_preallocated(const ma_slot_allocator_config* pConfig, void* pHeap, ma_slot_allocator* pAllocator); -MA_API ma_result ma_slot_allocator_init(const ma_slot_allocator_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_slot_allocator* pAllocator); -MA_API void ma_slot_allocator_uninit(ma_slot_allocator* pAllocator, const ma_allocation_callbacks* pAllocationCallbacks); -MA_API ma_result ma_slot_allocator_alloc(ma_slot_allocator* pAllocator, ma_uint64* pSlot); -MA_API ma_result ma_slot_allocator_free(ma_slot_allocator* pAllocator, ma_uint64 slot); - - -typedef struct ma_job ma_job; - -/* -Callback for processing a job. Each job type will have their own processing callback which will be -called by ma_job_process(). -*/ -typedef ma_result (* ma_job_proc)(ma_job* pJob); - -/* When a job type is added here an callback needs to be added go "g_jobVTable" in the implementation section. */ -typedef enum -{ - /* Miscellaneous. */ - MA_JOB_TYPE_QUIT = 0, - MA_JOB_TYPE_CUSTOM, - - /* Resource Manager. */ - MA_JOB_TYPE_RESOURCE_MANAGER_LOAD_DATA_BUFFER_NODE, - MA_JOB_TYPE_RESOURCE_MANAGER_FREE_DATA_BUFFER_NODE, - MA_JOB_TYPE_RESOURCE_MANAGER_PAGE_DATA_BUFFER_NODE, - MA_JOB_TYPE_RESOURCE_MANAGER_LOAD_DATA_BUFFER, - MA_JOB_TYPE_RESOURCE_MANAGER_FREE_DATA_BUFFER, - MA_JOB_TYPE_RESOURCE_MANAGER_LOAD_DATA_STREAM, - MA_JOB_TYPE_RESOURCE_MANAGER_FREE_DATA_STREAM, - MA_JOB_TYPE_RESOURCE_MANAGER_PAGE_DATA_STREAM, - MA_JOB_TYPE_RESOURCE_MANAGER_SEEK_DATA_STREAM, - - /* Device. */ - MA_JOB_TYPE_DEVICE_AAUDIO_REROUTE, - - /* Count. Must always be last. */ - MA_JOB_TYPE_COUNT -} ma_job_type; - -struct ma_job -{ - union - { - struct - { - ma_uint16 code; /* Job type. */ - ma_uint16 slot; /* Index into a ma_slot_allocator. */ - ma_uint32 refcount; - } breakup; - ma_uint64 allocation; - } toc; /* 8 bytes. We encode the job code into the slot allocation data to save space. */ - MA_ATOMIC(8, ma_uint64) next; /* refcount + slot for the next item. Does not include the job code. */ - ma_uint32 order; /* Execution order. Used to create a data dependency and ensure a job is executed in order. Usage is contextual depending on the job type. */ - - union - { - /* Miscellaneous. */ - struct - { - ma_job_proc proc; - ma_uintptr data0; - ma_uintptr data1; - } custom; - - /* Resource Manager */ - union - { - struct - { - /*ma_resource_manager**/ void* pResourceManager; - /*ma_resource_manager_data_buffer_node**/ void* pDataBufferNode; - char* pFilePath; - wchar_t* pFilePathW; - ma_uint32 flags; /* Resource manager data source flags that were used when initializing the data buffer. */ - ma_async_notification* pInitNotification; /* Signalled when the data buffer has been initialized and the format/channels/rate can be retrieved. */ - ma_async_notification* pDoneNotification; /* Signalled when the data buffer has been fully decoded. Will be passed through to MA_JOB_TYPE_RESOURCE_MANAGER_PAGE_DATA_BUFFER_NODE when decoding. */ - ma_fence* pInitFence; /* Released when initialization of the decoder is complete. */ - ma_fence* pDoneFence; /* Released if initialization of the decoder fails. Passed through to PAGE_DATA_BUFFER_NODE untouched if init is successful. */ - } loadDataBufferNode; - struct - { - /*ma_resource_manager**/ void* pResourceManager; - /*ma_resource_manager_data_buffer_node**/ void* pDataBufferNode; - ma_async_notification* pDoneNotification; - ma_fence* pDoneFence; - } freeDataBufferNode; - struct - { - /*ma_resource_manager**/ void* pResourceManager; - /*ma_resource_manager_data_buffer_node**/ void* pDataBufferNode; - /*ma_decoder**/ void* pDecoder; - ma_async_notification* pDoneNotification; /* Signalled when the data buffer has been fully decoded. */ - ma_fence* pDoneFence; /* Passed through from LOAD_DATA_BUFFER_NODE and released when the data buffer completes decoding or an error occurs. */ - } pageDataBufferNode; - - struct - { - /*ma_resource_manager_data_buffer**/ void* pDataBuffer; - ma_async_notification* pInitNotification; /* Signalled when the data buffer has been initialized and the format/channels/rate can be retrieved. */ - ma_async_notification* pDoneNotification; /* Signalled when the data buffer has been fully decoded. */ - ma_fence* pInitFence; /* Released when the data buffer has been initialized and the format/channels/rate can be retrieved. */ - ma_fence* pDoneFence; /* Released when the data buffer has been fully decoded. */ - ma_uint64 rangeBegInPCMFrames; - ma_uint64 rangeEndInPCMFrames; - ma_uint64 loopPointBegInPCMFrames; - ma_uint64 loopPointEndInPCMFrames; - ma_uint32 isLooping; - } loadDataBuffer; - struct - { - /*ma_resource_manager_data_buffer**/ void* pDataBuffer; - ma_async_notification* pDoneNotification; - ma_fence* pDoneFence; - } freeDataBuffer; - - struct - { - /*ma_resource_manager_data_stream**/ void* pDataStream; - char* pFilePath; /* Allocated when the job is posted, freed by the job thread after loading. */ - wchar_t* pFilePathW; /* ^ As above ^. Only used if pFilePath is NULL. */ - ma_uint64 initialSeekPoint; - ma_async_notification* pInitNotification; /* Signalled after the first two pages have been decoded and frames can be read from the stream. */ - ma_fence* pInitFence; - } loadDataStream; - struct - { - /*ma_resource_manager_data_stream**/ void* pDataStream; - ma_async_notification* pDoneNotification; - ma_fence* pDoneFence; - } freeDataStream; - struct - { - /*ma_resource_manager_data_stream**/ void* pDataStream; - ma_uint32 pageIndex; /* The index of the page to decode into. */ - } pageDataStream; - struct - { - /*ma_resource_manager_data_stream**/ void* pDataStream; - ma_uint64 frameIndex; - } seekDataStream; - } resourceManager; - - /* Device. */ - union - { - union - { - struct - { - /*ma_device**/ void* pDevice; - /*ma_device_type*/ ma_uint32 deviceType; - } reroute; - } aaudio; - } device; - } data; -}; - -MA_API ma_job ma_job_init(ma_uint16 code); -MA_API ma_result ma_job_process(ma_job* pJob); - - -/* -When set, ma_job_queue_next() will not wait and no semaphore will be signaled in -ma_job_queue_post(). ma_job_queue_next() will return MA_NO_DATA_AVAILABLE if nothing is available. - -This flag should always be used for platforms that do not support multithreading. -*/ -typedef enum -{ - MA_JOB_QUEUE_FLAG_NON_BLOCKING = 0x00000001 -} ma_job_queue_flags; - -typedef struct -{ - ma_uint32 flags; - ma_uint32 capacity; /* The maximum number of jobs that can fit in the queue at a time. */ -} ma_job_queue_config; - -MA_API ma_job_queue_config ma_job_queue_config_init(ma_uint32 flags, ma_uint32 capacity); - - -typedef struct -{ - ma_uint32 flags; /* Flags passed in at initialization time. */ - ma_uint32 capacity; /* The maximum number of jobs that can fit in the queue at a time. Set by the config. */ - MA_ATOMIC(8, ma_uint64) head; /* The first item in the list. Required for removing from the top of the list. */ - MA_ATOMIC(8, ma_uint64) tail; /* The last item in the list. Required for appending to the end of the list. */ -#ifndef MA_NO_THREADING - ma_semaphore sem; /* Only used when MA_JOB_QUEUE_FLAG_NON_BLOCKING is unset. */ -#endif - ma_slot_allocator allocator; - ma_job* pJobs; -#ifndef MA_USE_EXPERIMENTAL_LOCK_FREE_JOB_QUEUE - ma_spinlock lock; -#endif - - /* Memory management. */ - void* _pHeap; - ma_bool32 _ownsHeap; -} ma_job_queue; - -MA_API ma_result ma_job_queue_get_heap_size(const ma_job_queue_config* pConfig, size_t* pHeapSizeInBytes); -MA_API ma_result ma_job_queue_init_preallocated(const ma_job_queue_config* pConfig, void* pHeap, ma_job_queue* pQueue); -MA_API ma_result ma_job_queue_init(const ma_job_queue_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_job_queue* pQueue); -MA_API void ma_job_queue_uninit(ma_job_queue* pQueue, const ma_allocation_callbacks* pAllocationCallbacks); -MA_API ma_result ma_job_queue_post(ma_job_queue* pQueue, const ma_job* pJob); -MA_API ma_result ma_job_queue_next(ma_job_queue* pQueue, ma_job* pJob); /* Returns MA_CANCELLED if the next job is a quit job. */ - - - -/************************************************************************************************************************************************************ -************************************************************************************************************************************************************* - -DEVICE I/O -========== - -This section contains the APIs for device playback and capture. Here is where you'll find ma_device_init(), etc. - -************************************************************************************************************************************************************* -************************************************************************************************************************************************************/ -#ifndef MA_NO_DEVICE_IO -/* Some backends are only supported on certain platforms. */ -#if defined(MA_WIN32) - #define MA_SUPPORT_WASAPI - - #if defined(MA_WIN32_DESKTOP) /* DirectSound and WinMM backends are only supported on desktops. */ - #define MA_SUPPORT_DSOUND - #define MA_SUPPORT_WINMM - - /* Don't enable JACK here if compiling with Cosmopolitan. It'll be enabled in the Linux section below. */ - #if !defined(__COSMOPOLITAN__) - #define MA_SUPPORT_JACK /* JACK is technically supported on Windows, but I don't know how many people use it in practice... */ - #endif - #endif -#endif -#if defined(MA_UNIX) && !defined(MA_ORBIS) && !defined(MA_PROSPERO) - #if defined(MA_LINUX) - #if !defined(MA_ANDROID) && !defined(__COSMOPOLITAN__) /* ALSA is not supported on Android. */ - #define MA_SUPPORT_ALSA - #endif - #endif - #if !defined(MA_BSD) && !defined(MA_ANDROID) && !defined(MA_EMSCRIPTEN) - #define MA_SUPPORT_PULSEAUDIO - #define MA_SUPPORT_JACK - #endif - #if defined(__OpenBSD__) /* <-- Change this to "#if defined(MA_BSD)" to enable sndio on all BSD flavors. */ - #define MA_SUPPORT_SNDIO /* sndio is only supported on OpenBSD for now. May be expanded later if there's demand. */ - #endif - #if defined(__NetBSD__) || defined(__OpenBSD__) - #define MA_SUPPORT_AUDIO4 /* Only support audio(4) on platforms with known support. */ - #endif - #if defined(__FreeBSD__) || defined(__DragonFly__) - #define MA_SUPPORT_OSS /* Only support OSS on specific platforms with known support. */ - #endif -#endif -#if defined(MA_ANDROID) - #define MA_SUPPORT_AAUDIO - #define MA_SUPPORT_OPENSL -#endif -#if defined(MA_APPLE) - #define MA_SUPPORT_COREAUDIO -#endif -#if defined(MA_EMSCRIPTEN) - #define MA_SUPPORT_WEBAUDIO -#endif - -/* All platforms should support custom backends. */ -#define MA_SUPPORT_CUSTOM - -/* Explicitly disable the Null backend for Emscripten because it uses a background thread which is not properly supported right now. */ -#if !defined(MA_EMSCRIPTEN) -#define MA_SUPPORT_NULL -#endif - - -#if defined(MA_SUPPORT_WASAPI) && !defined(MA_NO_WASAPI) && (!defined(MA_ENABLE_ONLY_SPECIFIC_BACKENDS) || defined(MA_ENABLE_WASAPI)) - #define MA_HAS_WASAPI -#endif -#if defined(MA_SUPPORT_DSOUND) && !defined(MA_NO_DSOUND) && (!defined(MA_ENABLE_ONLY_SPECIFIC_BACKENDS) || defined(MA_ENABLE_DSOUND)) - #define MA_HAS_DSOUND -#endif -#if defined(MA_SUPPORT_WINMM) && !defined(MA_NO_WINMM) && (!defined(MA_ENABLE_ONLY_SPECIFIC_BACKENDS) || defined(MA_ENABLE_WINMM)) - #define MA_HAS_WINMM -#endif -#if defined(MA_SUPPORT_ALSA) && !defined(MA_NO_ALSA) && (!defined(MA_ENABLE_ONLY_SPECIFIC_BACKENDS) || defined(MA_ENABLE_ALSA)) - #define MA_HAS_ALSA -#endif -#if defined(MA_SUPPORT_PULSEAUDIO) && !defined(MA_NO_PULSEAUDIO) && (!defined(MA_ENABLE_ONLY_SPECIFIC_BACKENDS) || defined(MA_ENABLE_PULSEAUDIO)) - #define MA_HAS_PULSEAUDIO -#endif -#if defined(MA_SUPPORT_JACK) && !defined(MA_NO_JACK) && (!defined(MA_ENABLE_ONLY_SPECIFIC_BACKENDS) || defined(MA_ENABLE_JACK)) - #define MA_HAS_JACK -#endif -#if defined(MA_SUPPORT_COREAUDIO) && !defined(MA_NO_COREAUDIO) && (!defined(MA_ENABLE_ONLY_SPECIFIC_BACKENDS) || defined(MA_ENABLE_COREAUDIO)) - #define MA_HAS_COREAUDIO -#endif -#if defined(MA_SUPPORT_SNDIO) && !defined(MA_NO_SNDIO) && (!defined(MA_ENABLE_ONLY_SPECIFIC_BACKENDS) || defined(MA_ENABLE_SNDIO)) - #define MA_HAS_SNDIO -#endif -#if defined(MA_SUPPORT_AUDIO4) && !defined(MA_NO_AUDIO4) && (!defined(MA_ENABLE_ONLY_SPECIFIC_BACKENDS) || defined(MA_ENABLE_AUDIO4)) - #define MA_HAS_AUDIO4 -#endif -#if defined(MA_SUPPORT_OSS) && !defined(MA_NO_OSS) && (!defined(MA_ENABLE_ONLY_SPECIFIC_BACKENDS) || defined(MA_ENABLE_OSS)) - #define MA_HAS_OSS -#endif -#if defined(MA_SUPPORT_AAUDIO) && !defined(MA_NO_AAUDIO) && (!defined(MA_ENABLE_ONLY_SPECIFIC_BACKENDS) || defined(MA_ENABLE_AAUDIO)) - #define MA_HAS_AAUDIO -#endif -#if defined(MA_SUPPORT_OPENSL) && !defined(MA_NO_OPENSL) && (!defined(MA_ENABLE_ONLY_SPECIFIC_BACKENDS) || defined(MA_ENABLE_OPENSL)) - #define MA_HAS_OPENSL -#endif -#if defined(MA_SUPPORT_WEBAUDIO) && !defined(MA_NO_WEBAUDIO) && (!defined(MA_ENABLE_ONLY_SPECIFIC_BACKENDS) || defined(MA_ENABLE_WEBAUDIO)) - #define MA_HAS_WEBAUDIO -#endif -#if defined(MA_SUPPORT_CUSTOM) && !defined(MA_NO_CUSTOM) && (!defined(MA_ENABLE_ONLY_SPECIFIC_BACKENDS) || defined(MA_ENABLE_CUSTOM)) - #define MA_HAS_CUSTOM -#endif -#if defined(MA_SUPPORT_NULL) && !defined(MA_NO_NULL) && (!defined(MA_ENABLE_ONLY_SPECIFIC_BACKENDS) || defined(MA_ENABLE_NULL)) - #define MA_HAS_NULL -#endif - -typedef enum -{ - ma_device_state_uninitialized = 0, - ma_device_state_stopped = 1, /* The device's default state after initialization. */ - ma_device_state_started = 2, /* The device is started and is requesting and/or delivering audio data. */ - ma_device_state_starting = 3, /* Transitioning from a stopped state to started. */ - ma_device_state_stopping = 4 /* Transitioning from a started state to stopped. */ -} ma_device_state; - -MA_ATOMIC_SAFE_TYPE_DECL(i32, 4, device_state) - - -#ifdef MA_SUPPORT_WASAPI -/* We need a IMMNotificationClient object for WASAPI. */ -typedef struct -{ - void* lpVtbl; - ma_uint32 counter; - ma_device* pDevice; -} ma_IMMNotificationClient; -#endif - -/* Backend enums must be in priority order. */ -typedef enum -{ - ma_backend_wasapi, - ma_backend_dsound, - ma_backend_winmm, - ma_backend_coreaudio, - ma_backend_sndio, - ma_backend_audio4, - ma_backend_oss, - ma_backend_pulseaudio, - ma_backend_alsa, - ma_backend_jack, - ma_backend_aaudio, - ma_backend_opensl, - ma_backend_webaudio, - ma_backend_custom, /* <-- Custom backend, with callbacks defined by the context config. */ - ma_backend_null /* <-- Must always be the last item. Lowest priority, and used as the terminator for backend enumeration. */ -} ma_backend; - -#define MA_BACKEND_COUNT (ma_backend_null+1) - - -/* -Device job thread. This is used by backends that require asynchronous processing of certain -operations. It is not used by all backends. - -The device job thread is made up of a thread and a job queue. You can post a job to the thread with -ma_device_job_thread_post(). The thread will do the processing of the job. -*/ -typedef struct -{ - ma_bool32 noThread; /* Set this to true if you want to process jobs yourself. */ - ma_uint32 jobQueueCapacity; - ma_uint32 jobQueueFlags; -} ma_device_job_thread_config; - -MA_API ma_device_job_thread_config ma_device_job_thread_config_init(void); - -typedef struct -{ - ma_thread thread; - ma_job_queue jobQueue; - ma_bool32 _hasThread; -} ma_device_job_thread; - -MA_API ma_result ma_device_job_thread_init(const ma_device_job_thread_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_device_job_thread* pJobThread); -MA_API void ma_device_job_thread_uninit(ma_device_job_thread* pJobThread, const ma_allocation_callbacks* pAllocationCallbacks); -MA_API ma_result ma_device_job_thread_post(ma_device_job_thread* pJobThread, const ma_job* pJob); -MA_API ma_result ma_device_job_thread_next(ma_device_job_thread* pJobThread, ma_job* pJob); - - - -/* Device notification types. */ -typedef enum -{ - ma_device_notification_type_started, - ma_device_notification_type_stopped, - ma_device_notification_type_rerouted, - ma_device_notification_type_interruption_began, - ma_device_notification_type_interruption_ended, - ma_device_notification_type_unlocked -} ma_device_notification_type; - -typedef struct -{ - ma_device* pDevice; - ma_device_notification_type type; - union - { - struct - { - int _unused; - } started; - struct - { - int _unused; - } stopped; - struct - { - int _unused; - } rerouted; - struct - { - int _unused; - } interruption; - } data; -} ma_device_notification; - -/* -The notification callback for when the application should be notified of a change to the device. - -This callback is used for notifying the application of changes such as when the device has started, -stopped, rerouted or an interruption has occurred. Note that not all backends will post all -notification types. For example, some backends will perform automatic stream routing without any -kind of notification to the host program which means miniaudio will never know about it and will -never be able to fire the rerouted notification. You should keep this in mind when designing your -program. - -The stopped notification will *not* get fired when a device is rerouted. - - -Parameters ----------- -pNotification (in) - A pointer to a structure containing information about the event. Use the `pDevice` member of - this object to retrieve the relevant device. The `type` member can be used to discriminate - against each of the notification types. - - -Remarks -------- -Do not restart or uninitialize the device from the callback. - -Not all notifications will be triggered by all backends, however the started and stopped events -should be reliable for all backends. Some backends do not have a good way to detect device -stoppages due to unplugging the device which may result in the stopped callback not getting -fired. This has been observed with at least one BSD variant. - -The rerouted notification is fired *after* the reroute has occurred. The stopped notification will -*not* get fired when a device is rerouted. The following backends are known to do automatic stream -rerouting, but do not have a way to be notified of the change: - - * DirectSound - -The interruption notifications are used on mobile platforms for detecting when audio is interrupted -due to things like an incoming phone call. Currently this is only implemented on iOS. None of the -Android backends will report this notification. -*/ -typedef void (* ma_device_notification_proc)(const ma_device_notification* pNotification); - - -/* -The callback for processing audio data from the device. - -The data callback is fired by miniaudio whenever the device needs to have more data delivered to a playback device, or when a capture device has some data -available. This is called as soon as the backend asks for more data which means it may be called with inconsistent frame counts. You cannot assume the -callback will be fired with a consistent frame count. - - -Parameters ----------- -pDevice (in) - A pointer to the relevant device. - -pOutput (out) - A pointer to the output buffer that will receive audio data that will later be played back through the speakers. This will be non-null for a playback or - full-duplex device and null for a capture and loopback device. - -pInput (in) - A pointer to the buffer containing input data from a recording device. This will be non-null for a capture, full-duplex or loopback device and null for a - playback device. - -frameCount (in) - The number of PCM frames to process. Note that this will not necessarily be equal to what you requested when you initialized the device. The - `periodSizeInFrames` and `periodSizeInMilliseconds` members of the device config are just hints, and are not necessarily exactly what you'll get. You must - not assume this will always be the same value each time the callback is fired. - - -Remarks -------- -You cannot stop and start the device from inside the callback or else you'll get a deadlock. You must also not uninitialize the device from inside the -callback. The following APIs cannot be called from inside the callback: - - ma_device_init() - ma_device_init_ex() - ma_device_uninit() - ma_device_start() - ma_device_stop() - -The proper way to stop the device is to call `ma_device_stop()` from a different thread, normally the main application thread. -*/ -typedef void (* ma_device_data_proc)(ma_device* pDevice, void* pOutput, const void* pInput, ma_uint32 frameCount); - - - - -/* -DEPRECATED. Use ma_device_notification_proc instead. - -The callback for when the device has been stopped. - -This will be called when the device is stopped explicitly with `ma_device_stop()` and also called implicitly when the device is stopped through external forces -such as being unplugged or an internal error occurring. - - -Parameters ----------- -pDevice (in) - A pointer to the device that has just stopped. - - -Remarks -------- -Do not restart or uninitialize the device from the callback. -*/ -typedef void (* ma_stop_proc)(ma_device* pDevice); /* DEPRECATED. Use ma_device_notification_proc instead. */ - -typedef enum -{ - ma_device_type_playback = 1, - ma_device_type_capture = 2, - ma_device_type_duplex = ma_device_type_playback | ma_device_type_capture, /* 3 */ - ma_device_type_loopback = 4 -} ma_device_type; - -typedef enum -{ - ma_share_mode_shared = 0, - ma_share_mode_exclusive -} ma_share_mode; - -/* iOS/tvOS/watchOS session categories. */ -typedef enum -{ - ma_ios_session_category_default = 0, /* AVAudioSessionCategoryPlayAndRecord. */ - ma_ios_session_category_none, /* Leave the session category unchanged. */ - ma_ios_session_category_ambient, /* AVAudioSessionCategoryAmbient */ - ma_ios_session_category_solo_ambient, /* AVAudioSessionCategorySoloAmbient */ - ma_ios_session_category_playback, /* AVAudioSessionCategoryPlayback */ - ma_ios_session_category_record, /* AVAudioSessionCategoryRecord */ - ma_ios_session_category_play_and_record, /* AVAudioSessionCategoryPlayAndRecord */ - ma_ios_session_category_multi_route /* AVAudioSessionCategoryMultiRoute */ -} ma_ios_session_category; - -/* iOS/tvOS/watchOS session category options */ -typedef enum -{ - ma_ios_session_category_option_mix_with_others = 0x01, /* AVAudioSessionCategoryOptionMixWithOthers */ - ma_ios_session_category_option_duck_others = 0x02, /* AVAudioSessionCategoryOptionDuckOthers */ - ma_ios_session_category_option_allow_bluetooth = 0x04, /* AVAudioSessionCategoryOptionAllowBluetooth */ - ma_ios_session_category_option_default_to_speaker = 0x08, /* AVAudioSessionCategoryOptionDefaultToSpeaker */ - ma_ios_session_category_option_interrupt_spoken_audio_and_mix_with_others = 0x11, /* AVAudioSessionCategoryOptionInterruptSpokenAudioAndMixWithOthers */ - ma_ios_session_category_option_allow_bluetooth_a2dp = 0x20, /* AVAudioSessionCategoryOptionAllowBluetoothA2DP */ - ma_ios_session_category_option_allow_air_play = 0x40, /* AVAudioSessionCategoryOptionAllowAirPlay */ -} ma_ios_session_category_option; - -/* OpenSL stream types. */ -typedef enum -{ - ma_opensl_stream_type_default = 0, /* Leaves the stream type unset. */ - ma_opensl_stream_type_voice, /* SL_ANDROID_STREAM_VOICE */ - ma_opensl_stream_type_system, /* SL_ANDROID_STREAM_SYSTEM */ - ma_opensl_stream_type_ring, /* SL_ANDROID_STREAM_RING */ - ma_opensl_stream_type_media, /* SL_ANDROID_STREAM_MEDIA */ - ma_opensl_stream_type_alarm, /* SL_ANDROID_STREAM_ALARM */ - ma_opensl_stream_type_notification /* SL_ANDROID_STREAM_NOTIFICATION */ -} ma_opensl_stream_type; - -/* OpenSL recording presets. */ -typedef enum -{ - ma_opensl_recording_preset_default = 0, /* Leaves the input preset unset. */ - ma_opensl_recording_preset_generic, /* SL_ANDROID_RECORDING_PRESET_GENERIC */ - ma_opensl_recording_preset_camcorder, /* SL_ANDROID_RECORDING_PRESET_CAMCORDER */ - ma_opensl_recording_preset_voice_recognition, /* SL_ANDROID_RECORDING_PRESET_VOICE_RECOGNITION */ - ma_opensl_recording_preset_voice_communication, /* SL_ANDROID_RECORDING_PRESET_VOICE_COMMUNICATION */ - ma_opensl_recording_preset_voice_unprocessed /* SL_ANDROID_RECORDING_PRESET_UNPROCESSED */ -} ma_opensl_recording_preset; - -/* WASAPI audio thread priority characteristics. */ -typedef enum -{ - ma_wasapi_usage_default = 0, - ma_wasapi_usage_games, - ma_wasapi_usage_pro_audio, -} ma_wasapi_usage; - -/* AAudio usage types. */ -typedef enum -{ - ma_aaudio_usage_default = 0, /* Leaves the usage type unset. */ - ma_aaudio_usage_media, /* AAUDIO_USAGE_MEDIA */ - ma_aaudio_usage_voice_communication, /* AAUDIO_USAGE_VOICE_COMMUNICATION */ - ma_aaudio_usage_voice_communication_signalling, /* AAUDIO_USAGE_VOICE_COMMUNICATION_SIGNALLING */ - ma_aaudio_usage_alarm, /* AAUDIO_USAGE_ALARM */ - ma_aaudio_usage_notification, /* AAUDIO_USAGE_NOTIFICATION */ - ma_aaudio_usage_notification_ringtone, /* AAUDIO_USAGE_NOTIFICATION_RINGTONE */ - ma_aaudio_usage_notification_event, /* AAUDIO_USAGE_NOTIFICATION_EVENT */ - ma_aaudio_usage_assistance_accessibility, /* AAUDIO_USAGE_ASSISTANCE_ACCESSIBILITY */ - ma_aaudio_usage_assistance_navigation_guidance, /* AAUDIO_USAGE_ASSISTANCE_NAVIGATION_GUIDANCE */ - ma_aaudio_usage_assistance_sonification, /* AAUDIO_USAGE_ASSISTANCE_SONIFICATION */ - ma_aaudio_usage_game, /* AAUDIO_USAGE_GAME */ - ma_aaudio_usage_assitant, /* AAUDIO_USAGE_ASSISTANT */ - ma_aaudio_usage_emergency, /* AAUDIO_SYSTEM_USAGE_EMERGENCY */ - ma_aaudio_usage_safety, /* AAUDIO_SYSTEM_USAGE_SAFETY */ - ma_aaudio_usage_vehicle_status, /* AAUDIO_SYSTEM_USAGE_VEHICLE_STATUS */ - ma_aaudio_usage_announcement /* AAUDIO_SYSTEM_USAGE_ANNOUNCEMENT */ -} ma_aaudio_usage; - -/* AAudio content types. */ -typedef enum -{ - ma_aaudio_content_type_default = 0, /* Leaves the content type unset. */ - ma_aaudio_content_type_speech, /* AAUDIO_CONTENT_TYPE_SPEECH */ - ma_aaudio_content_type_music, /* AAUDIO_CONTENT_TYPE_MUSIC */ - ma_aaudio_content_type_movie, /* AAUDIO_CONTENT_TYPE_MOVIE */ - ma_aaudio_content_type_sonification /* AAUDIO_CONTENT_TYPE_SONIFICATION */ -} ma_aaudio_content_type; - -/* AAudio input presets. */ -typedef enum -{ - ma_aaudio_input_preset_default = 0, /* Leaves the input preset unset. */ - ma_aaudio_input_preset_generic, /* AAUDIO_INPUT_PRESET_GENERIC */ - ma_aaudio_input_preset_camcorder, /* AAUDIO_INPUT_PRESET_CAMCORDER */ - ma_aaudio_input_preset_voice_recognition, /* AAUDIO_INPUT_PRESET_VOICE_RECOGNITION */ - ma_aaudio_input_preset_voice_communication, /* AAUDIO_INPUT_PRESET_VOICE_COMMUNICATION */ - ma_aaudio_input_preset_unprocessed, /* AAUDIO_INPUT_PRESET_UNPROCESSED */ - ma_aaudio_input_preset_voice_performance /* AAUDIO_INPUT_PRESET_VOICE_PERFORMANCE */ -} ma_aaudio_input_preset; - -typedef enum -{ - ma_aaudio_allow_capture_default = 0, /* Leaves the allowed capture policy unset. */ - ma_aaudio_allow_capture_by_all, /* AAUDIO_ALLOW_CAPTURE_BY_ALL */ - ma_aaudio_allow_capture_by_system, /* AAUDIO_ALLOW_CAPTURE_BY_SYSTEM */ - ma_aaudio_allow_capture_by_none /* AAUDIO_ALLOW_CAPTURE_BY_NONE */ -} ma_aaudio_allowed_capture_policy; - -typedef union -{ - ma_int64 counter; - double counterD; -} ma_timer; - -typedef union -{ - ma_wchar_win32 wasapi[64]; /* WASAPI uses a wchar_t string for identification. */ - ma_uint8 dsound[16]; /* DirectSound uses a GUID for identification. */ - /*UINT_PTR*/ ma_uint32 winmm; /* When creating a device, WinMM expects a Win32 UINT_PTR for device identification. In practice it's actually just a UINT. */ - char alsa[256]; /* ALSA uses a name string for identification. */ - char pulse[256]; /* PulseAudio uses a name string for identification. */ - int jack; /* JACK always uses default devices. */ - char coreaudio[256]; /* Core Audio uses a string for identification. */ - char sndio[256]; /* "snd/0", etc. */ - char audio4[256]; /* "/dev/audio", etc. */ - char oss[64]; /* "dev/dsp0", etc. "dev/dsp" for the default device. */ - ma_int32 aaudio; /* AAudio uses a 32-bit integer for identification. */ - ma_uint32 opensl; /* OpenSL|ES uses a 32-bit unsigned integer for identification. */ - char webaudio[32]; /* Web Audio always uses default devices for now, but if this changes it'll be a GUID. */ - union - { - int i; - char s[256]; - void* p; - } custom; /* The custom backend could be anything. Give them a few options. */ - int nullbackend; /* The null backend uses an integer for device IDs. */ -} ma_device_id; - -MA_API ma_bool32 ma_device_id_equal(const ma_device_id* pA, const ma_device_id* pB); - - -typedef struct ma_context_config ma_context_config; -typedef struct ma_device_config ma_device_config; -typedef struct ma_backend_callbacks ma_backend_callbacks; - -#define MA_DATA_FORMAT_FLAG_EXCLUSIVE_MODE (1U << 1) /* If set, this is supported in exclusive mode. Otherwise not natively supported by exclusive mode. */ - -#ifndef MA_MAX_DEVICE_NAME_LENGTH -#define MA_MAX_DEVICE_NAME_LENGTH 255 -#endif - -typedef struct -{ - /* Basic info. This is the only information guaranteed to be filled in during device enumeration. */ - ma_device_id id; - char name[MA_MAX_DEVICE_NAME_LENGTH + 1]; /* +1 for null terminator. */ - ma_bool32 isDefault; - - ma_uint32 nativeDataFormatCount; - struct - { - ma_format format; /* Sample format. If set to ma_format_unknown, all sample formats are supported. */ - ma_uint32 channels; /* If set to 0, all channels are supported. */ - ma_uint32 sampleRate; /* If set to 0, all sample rates are supported. */ - ma_uint32 flags; /* A combination of MA_DATA_FORMAT_FLAG_* flags. */ - } nativeDataFormats[/*ma_format_count * ma_standard_sample_rate_count * MA_MAX_CHANNELS*/ 64]; /* Not sure how big to make this. There can be *many* permutations for virtual devices which can support anything. */ -} ma_device_info; - -struct ma_device_config -{ - ma_device_type deviceType; - ma_uint32 sampleRate; - ma_uint32 periodSizeInFrames; - ma_uint32 periodSizeInMilliseconds; - ma_uint32 periods; - ma_performance_profile performanceProfile; - ma_bool8 noPreSilencedOutputBuffer; /* When set to true, the contents of the output buffer passed into the data callback will be left undefined rather than initialized to silence. */ - ma_bool8 noClip; /* When set to true, the contents of the output buffer passed into the data callback will not be clipped after returning. Only applies when the playback sample format is f32. */ - ma_bool8 noDisableDenormals; /* Do not disable denormals when firing the data callback. */ - ma_bool8 noFixedSizedCallback; /* Disables strict fixed-sized data callbacks. Setting this to true will result in the period size being treated only as a hint to the backend. This is an optimization for those who don't need fixed sized callbacks. */ - ma_device_data_proc dataCallback; - ma_device_notification_proc notificationCallback; - ma_stop_proc stopCallback; - void* pUserData; - ma_resampler_config resampling; - struct - { - const ma_device_id* pDeviceID; - ma_format format; - ma_uint32 channels; - ma_channel* pChannelMap; - ma_channel_mix_mode channelMixMode; - ma_bool32 calculateLFEFromSpatialChannels; /* When an output LFE channel is present, but no input LFE, set to true to set the output LFE to the average of all spatial channels (LR, FR, etc.). Ignored when an input LFE is present. */ - ma_share_mode shareMode; - } playback; - struct - { - const ma_device_id* pDeviceID; - ma_format format; - ma_uint32 channels; - ma_channel* pChannelMap; - ma_channel_mix_mode channelMixMode; - ma_bool32 calculateLFEFromSpatialChannels; /* When an output LFE channel is present, but no input LFE, set to true to set the output LFE to the average of all spatial channels (LR, FR, etc.). Ignored when an input LFE is present. */ - ma_share_mode shareMode; - } capture; - - struct - { - ma_wasapi_usage usage; /* When configured, uses Avrt APIs to set the thread characteristics. */ - ma_bool8 noAutoConvertSRC; /* When set to true, disables the use of AUDCLNT_STREAMFLAGS_AUTOCONVERTPCM. */ - ma_bool8 noDefaultQualitySRC; /* When set to true, disables the use of AUDCLNT_STREAMFLAGS_SRC_DEFAULT_QUALITY. */ - ma_bool8 noAutoStreamRouting; /* Disables automatic stream routing. */ - ma_bool8 noHardwareOffloading; /* Disables WASAPI's hardware offloading feature. */ - ma_uint32 loopbackProcessID; /* The process ID to include or exclude for loopback mode. Set to 0 to capture audio from all processes. Ignored when an explicit device ID is specified. */ - ma_bool8 loopbackProcessExclude; /* When set to true, excludes the process specified by loopbackProcessID. By default, the process will be included. */ - } wasapi; - struct - { - ma_bool32 noMMap; /* Disables MMap mode. */ - ma_bool32 noAutoFormat; /* Opens the ALSA device with SND_PCM_NO_AUTO_FORMAT. */ - ma_bool32 noAutoChannels; /* Opens the ALSA device with SND_PCM_NO_AUTO_CHANNELS. */ - ma_bool32 noAutoResample; /* Opens the ALSA device with SND_PCM_NO_AUTO_RESAMPLE. */ - } alsa; - struct - { - const char* pStreamNamePlayback; - const char* pStreamNameCapture; - int channelMap; - } pulse; - struct - { - ma_bool32 allowNominalSampleRateChange; /* Desktop only. When enabled, allows changing of the sample rate at the operating system level. */ - } coreaudio; - struct - { - ma_opensl_stream_type streamType; - ma_opensl_recording_preset recordingPreset; - ma_bool32 enableCompatibilityWorkarounds; - } opensl; - struct - { - ma_aaudio_usage usage; - ma_aaudio_content_type contentType; - ma_aaudio_input_preset inputPreset; - ma_aaudio_allowed_capture_policy allowedCapturePolicy; - ma_bool32 noAutoStartAfterReroute; - ma_bool32 enableCompatibilityWorkarounds; - ma_bool32 allowSetBufferCapacity; - } aaudio; -}; - - -/* -The callback for handling device enumeration. This is fired from `ma_context_enumerate_devices()`. - - -Parameters ----------- -pContext (in) - A pointer to the context performing the enumeration. - -deviceType (in) - The type of the device being enumerated. This will always be either `ma_device_type_playback` or `ma_device_type_capture`. - -pInfo (in) - A pointer to a `ma_device_info` containing the ID and name of the enumerated device. Note that this will not include detailed information about the device, - only basic information (ID and name). The reason for this is that it would otherwise require opening the backend device to probe for the information which - is too inefficient. - -pUserData (in) - The user data pointer passed into `ma_context_enumerate_devices()`. -*/ -typedef ma_bool32 (* ma_enum_devices_callback_proc)(ma_context* pContext, ma_device_type deviceType, const ma_device_info* pInfo, void* pUserData); - - -/* -Describes some basic details about a playback or capture device. -*/ -typedef struct -{ - const ma_device_id* pDeviceID; - ma_share_mode shareMode; - ma_format format; - ma_uint32 channels; - ma_uint32 sampleRate; - ma_channel channelMap[MA_MAX_CHANNELS]; - ma_uint32 periodSizeInFrames; - ma_uint32 periodSizeInMilliseconds; - ma_uint32 periodCount; -} ma_device_descriptor; - -/* -These are the callbacks required to be implemented for a backend. These callbacks are grouped into two parts: context and device. There is one context -to many devices. A device is created from a context. - -The general flow goes like this: - - 1) A context is created with `onContextInit()` - 1a) Available devices can be enumerated with `onContextEnumerateDevices()` if required. - 1b) Detailed information about a device can be queried with `onContextGetDeviceInfo()` if required. - 2) A device is created from the context that was created in the first step using `onDeviceInit()`, and optionally a device ID that was - selected from device enumeration via `onContextEnumerateDevices()`. - 3) A device is started or stopped with `onDeviceStart()` / `onDeviceStop()` - 4) Data is delivered to and from the device by the backend. This is always done based on the native format returned by the prior call - to `onDeviceInit()`. Conversion between the device's native format and the format requested by the application will be handled by - miniaudio internally. - -Initialization of the context is quite simple. You need to do any necessary initialization of internal objects and then output the -callbacks defined in this structure. - -Once the context has been initialized you can initialize a device. Before doing so, however, the application may want to know which -physical devices are available. This is where `onContextEnumerateDevices()` comes in. This is fairly simple. For each device, fire the -given callback with, at a minimum, the basic information filled out in `ma_device_info`. When the callback returns `MA_FALSE`, enumeration -needs to stop and the `onContextEnumerateDevices()` function returns with a success code. - -Detailed device information can be retrieved from a device ID using `onContextGetDeviceInfo()`. This takes as input the device type and ID, -and on output returns detailed information about the device in `ma_device_info`. The `onContextGetDeviceInfo()` callback must handle the -case when the device ID is NULL, in which case information about the default device needs to be retrieved. - -Once the context has been created and the device ID retrieved (if using anything other than the default device), the device can be created. -This is a little bit more complicated than initialization of the context due to its more complicated configuration. When initializing a -device, a duplex device may be requested. This means a separate data format needs to be specified for both playback and capture. On input, -the data format is set to what the application wants. On output it's set to the native format which should match as closely as possible to -the requested format. The conversion between the format requested by the application and the device's native format will be handled -internally by miniaudio. - -On input, if the sample format is set to `ma_format_unknown`, the backend is free to use whatever sample format it desires, so long as it's -supported by miniaudio. When the channel count is set to 0, the backend should use the device's native channel count. The same applies for -sample rate. For the channel map, the default should be used when `ma_channel_map_is_blank()` returns true (all channels set to -`MA_CHANNEL_NONE`). On input, the `periodSizeInFrames` or `periodSizeInMilliseconds` option should always be set. The backend should -inspect both of these variables. If `periodSizeInFrames` is set, it should take priority, otherwise it needs to be derived from the period -size in milliseconds (`periodSizeInMilliseconds`) and the sample rate, keeping in mind that the sample rate may be 0, in which case the -sample rate will need to be determined before calculating the period size in frames. On output, all members of the `ma_device_descriptor` -object should be set to a valid value, except for `periodSizeInMilliseconds` which is optional (`periodSizeInFrames` *must* be set). - -Starting and stopping of the device is done with `onDeviceStart()` and `onDeviceStop()` and should be self-explanatory. If the backend uses -asynchronous reading and writing, `onDeviceStart()` and `onDeviceStop()` should always be implemented. - -The handling of data delivery between the application and the device is the most complicated part of the process. To make this a bit -easier, some helper callbacks are available. If the backend uses a blocking read/write style of API, the `onDeviceRead()` and -`onDeviceWrite()` callbacks can optionally be implemented. These are blocking and work just like reading and writing from a file. If the -backend uses a callback for data delivery, that callback must call `ma_device_handle_backend_data_callback()` from within its callback. -This allows miniaudio to then process any necessary data conversion and then pass it to the miniaudio data callback. - -If the backend requires absolute flexibility with its data delivery, it can optionally implement the `onDeviceDataLoop()` callback -which will allow it to implement the logic that will run on the audio thread. This is much more advanced and is completely optional. - -The audio thread should run data delivery logic in a loop while `ma_device_get_state() == ma_device_state_started` and no errors have been -encountered. Do not start or stop the device here. That will be handled from outside the `onDeviceDataLoop()` callback. - -The invocation of the `onDeviceDataLoop()` callback will be handled by miniaudio. When you start the device, miniaudio will fire this -callback. When the device is stopped, the `ma_device_get_state() == ma_device_state_started` condition will fail and the loop will be terminated -which will then fall through to the part that stops the device. For an example on how to implement the `onDeviceDataLoop()` callback, -look at `ma_device_audio_thread__default_read_write()`. Implement the `onDeviceDataLoopWakeup()` callback if you need a mechanism to -wake up the audio thread. - -If the backend supports an optimized retrieval of device information from an initialized `ma_device` object, it should implement the -`onDeviceGetInfo()` callback. This is optional, in which case it will fall back to `onContextGetDeviceInfo()` which is less efficient. -*/ -struct ma_backend_callbacks -{ - ma_result (* onContextInit)(ma_context* pContext, const ma_context_config* pConfig, ma_backend_callbacks* pCallbacks); - ma_result (* onContextUninit)(ma_context* pContext); - ma_result (* onContextEnumerateDevices)(ma_context* pContext, ma_enum_devices_callback_proc callback, void* pUserData); - ma_result (* onContextGetDeviceInfo)(ma_context* pContext, ma_device_type deviceType, const ma_device_id* pDeviceID, ma_device_info* pDeviceInfo); - ma_result (* onDeviceInit)(ma_device* pDevice, const ma_device_config* pConfig, ma_device_descriptor* pDescriptorPlayback, ma_device_descriptor* pDescriptorCapture); - ma_result (* onDeviceUninit)(ma_device* pDevice); - ma_result (* onDeviceStart)(ma_device* pDevice); - ma_result (* onDeviceStop)(ma_device* pDevice); - ma_result (* onDeviceRead)(ma_device* pDevice, void* pFrames, ma_uint32 frameCount, ma_uint32* pFramesRead); - ma_result (* onDeviceWrite)(ma_device* pDevice, const void* pFrames, ma_uint32 frameCount, ma_uint32* pFramesWritten); - ma_result (* onDeviceDataLoop)(ma_device* pDevice); - ma_result (* onDeviceDataLoopWakeup)(ma_device* pDevice); - ma_result (* onDeviceGetInfo)(ma_device* pDevice, ma_device_type type, ma_device_info* pDeviceInfo); -}; - -struct ma_context_config -{ - ma_log* pLog; - ma_thread_priority threadPriority; - size_t threadStackSize; - void* pUserData; - ma_allocation_callbacks allocationCallbacks; - struct - { - ma_handle hWnd; /* HWND. Optional window handle to pass into SetCooperativeLevel(). Will default to the foreground window, and if that fails, the desktop window. */ - } dsound; - struct - { - ma_bool32 useVerboseDeviceEnumeration; - } alsa; - struct - { - const char* pApplicationName; - const char* pServerName; - ma_bool32 tryAutoSpawn; /* Enables autospawning of the PulseAudio daemon if necessary. */ - } pulse; - struct - { - ma_ios_session_category sessionCategory; - ma_uint32 sessionCategoryOptions; - ma_bool32 noAudioSessionActivate; /* iOS only. When set to true, does not perform an explicit [[AVAudioSession sharedInstace] setActive:true] on initialization. */ - ma_bool32 noAudioSessionDeactivate; /* iOS only. When set to true, does not perform an explicit [[AVAudioSession sharedInstace] setActive:false] on uninitialization. */ - } coreaudio; - struct - { - const char* pClientName; - ma_bool32 tryStartServer; - } jack; - ma_backend_callbacks custom; -}; - -/* WASAPI specific structure for some commands which must run on a common thread due to bugs in WASAPI. */ -typedef struct -{ - int code; - ma_event* pEvent; /* This will be signalled when the event is complete. */ - union - { - struct - { - int _unused; - } quit; - struct - { - ma_device_type deviceType; - void* pAudioClient; - void** ppAudioClientService; - ma_result* pResult; /* The result from creating the audio client service. */ - } createAudioClient; - struct - { - ma_device* pDevice; - ma_device_type deviceType; - } releaseAudioClient; - } data; -} ma_context_command__wasapi; - -struct ma_context -{ - ma_backend_callbacks callbacks; - ma_backend backend; /* DirectSound, ALSA, etc. */ - ma_log* pLog; - ma_log log; /* Only used if the log is owned by the context. The pLog member will be set to &log in this case. */ - ma_thread_priority threadPriority; - size_t threadStackSize; - void* pUserData; - ma_allocation_callbacks allocationCallbacks; - ma_mutex deviceEnumLock; /* Used to make ma_context_get_devices() thread safe. */ - ma_mutex deviceInfoLock; /* Used to make ma_context_get_device_info() thread safe. */ - ma_uint32 deviceInfoCapacity; /* Total capacity of pDeviceInfos. */ - ma_uint32 playbackDeviceInfoCount; - ma_uint32 captureDeviceInfoCount; - ma_device_info* pDeviceInfos; /* Playback devices first, then capture. */ - - union - { -#ifdef MA_SUPPORT_WASAPI - struct - { - ma_thread commandThread; - ma_mutex commandLock; - ma_semaphore commandSem; - ma_uint32 commandIndex; - ma_uint32 commandCount; - ma_context_command__wasapi commands[4]; - ma_handle hAvrt; - ma_proc AvSetMmThreadCharacteristicsA; - ma_proc AvRevertMmThreadcharacteristics; - ma_handle hMMDevapi; - ma_proc ActivateAudioInterfaceAsync; - } wasapi; -#endif -#ifdef MA_SUPPORT_DSOUND - struct - { - ma_handle hWnd; /* Can be null. */ - ma_handle hDSoundDLL; - ma_proc DirectSoundCreate; - ma_proc DirectSoundEnumerateA; - ma_proc DirectSoundCaptureCreate; - ma_proc DirectSoundCaptureEnumerateA; - } dsound; -#endif -#ifdef MA_SUPPORT_WINMM - struct - { - ma_handle hWinMM; - ma_proc waveOutGetNumDevs; - ma_proc waveOutGetDevCapsA; - ma_proc waveOutOpen; - ma_proc waveOutClose; - ma_proc waveOutPrepareHeader; - ma_proc waveOutUnprepareHeader; - ma_proc waveOutWrite; - ma_proc waveOutReset; - ma_proc waveInGetNumDevs; - ma_proc waveInGetDevCapsA; - ma_proc waveInOpen; - ma_proc waveInClose; - ma_proc waveInPrepareHeader; - ma_proc waveInUnprepareHeader; - ma_proc waveInAddBuffer; - ma_proc waveInStart; - ma_proc waveInReset; - } winmm; -#endif -#ifdef MA_SUPPORT_ALSA - struct - { - ma_handle asoundSO; - ma_proc snd_pcm_open; - ma_proc snd_pcm_close; - ma_proc snd_pcm_hw_params_sizeof; - ma_proc snd_pcm_hw_params_any; - ma_proc snd_pcm_hw_params_set_format; - ma_proc snd_pcm_hw_params_set_format_first; - ma_proc snd_pcm_hw_params_get_format_mask; - ma_proc snd_pcm_hw_params_set_channels; - ma_proc snd_pcm_hw_params_set_channels_near; - ma_proc snd_pcm_hw_params_set_channels_minmax; - ma_proc snd_pcm_hw_params_set_rate_resample; - ma_proc snd_pcm_hw_params_set_rate; - ma_proc snd_pcm_hw_params_set_rate_near; - ma_proc snd_pcm_hw_params_set_buffer_size_near; - ma_proc snd_pcm_hw_params_set_periods_near; - ma_proc snd_pcm_hw_params_set_access; - ma_proc snd_pcm_hw_params_get_format; - ma_proc snd_pcm_hw_params_get_channels; - ma_proc snd_pcm_hw_params_get_channels_min; - ma_proc snd_pcm_hw_params_get_channels_max; - ma_proc snd_pcm_hw_params_get_rate; - ma_proc snd_pcm_hw_params_get_rate_min; - ma_proc snd_pcm_hw_params_get_rate_max; - ma_proc snd_pcm_hw_params_get_buffer_size; - ma_proc snd_pcm_hw_params_get_periods; - ma_proc snd_pcm_hw_params_get_access; - ma_proc snd_pcm_hw_params_test_format; - ma_proc snd_pcm_hw_params_test_channels; - ma_proc snd_pcm_hw_params_test_rate; - ma_proc snd_pcm_hw_params; - ma_proc snd_pcm_sw_params_sizeof; - ma_proc snd_pcm_sw_params_current; - ma_proc snd_pcm_sw_params_get_boundary; - ma_proc snd_pcm_sw_params_set_avail_min; - ma_proc snd_pcm_sw_params_set_start_threshold; - ma_proc snd_pcm_sw_params_set_stop_threshold; - ma_proc snd_pcm_sw_params; - ma_proc snd_pcm_format_mask_sizeof; - ma_proc snd_pcm_format_mask_test; - ma_proc snd_pcm_get_chmap; - ma_proc snd_pcm_state; - ma_proc snd_pcm_prepare; - ma_proc snd_pcm_start; - ma_proc snd_pcm_drop; - ma_proc snd_pcm_drain; - ma_proc snd_pcm_reset; - ma_proc snd_device_name_hint; - ma_proc snd_device_name_get_hint; - ma_proc snd_card_get_index; - ma_proc snd_device_name_free_hint; - ma_proc snd_pcm_mmap_begin; - ma_proc snd_pcm_mmap_commit; - ma_proc snd_pcm_recover; - ma_proc snd_pcm_readi; - ma_proc snd_pcm_writei; - ma_proc snd_pcm_avail; - ma_proc snd_pcm_avail_update; - ma_proc snd_pcm_wait; - ma_proc snd_pcm_nonblock; - ma_proc snd_pcm_info; - ma_proc snd_pcm_info_sizeof; - ma_proc snd_pcm_info_get_name; - ma_proc snd_pcm_poll_descriptors; - ma_proc snd_pcm_poll_descriptors_count; - ma_proc snd_pcm_poll_descriptors_revents; - ma_proc snd_config_update_free_global; - - ma_mutex internalDeviceEnumLock; - ma_bool32 useVerboseDeviceEnumeration; - } alsa; -#endif -#ifdef MA_SUPPORT_PULSEAUDIO - struct - { - ma_handle pulseSO; - ma_proc pa_mainloop_new; - ma_proc pa_mainloop_free; - ma_proc pa_mainloop_quit; - ma_proc pa_mainloop_get_api; - ma_proc pa_mainloop_iterate; - ma_proc pa_mainloop_wakeup; - ma_proc pa_threaded_mainloop_new; - ma_proc pa_threaded_mainloop_free; - ma_proc pa_threaded_mainloop_start; - ma_proc pa_threaded_mainloop_stop; - ma_proc pa_threaded_mainloop_lock; - ma_proc pa_threaded_mainloop_unlock; - ma_proc pa_threaded_mainloop_wait; - ma_proc pa_threaded_mainloop_signal; - ma_proc pa_threaded_mainloop_accept; - ma_proc pa_threaded_mainloop_get_retval; - ma_proc pa_threaded_mainloop_get_api; - ma_proc pa_threaded_mainloop_in_thread; - ma_proc pa_threaded_mainloop_set_name; - ma_proc pa_context_new; - ma_proc pa_context_unref; - ma_proc pa_context_connect; - ma_proc pa_context_disconnect; - ma_proc pa_context_set_state_callback; - ma_proc pa_context_get_state; - ma_proc pa_context_get_sink_info_list; - ma_proc pa_context_get_source_info_list; - ma_proc pa_context_get_sink_info_by_name; - ma_proc pa_context_get_source_info_by_name; - ma_proc pa_operation_unref; - ma_proc pa_operation_get_state; - ma_proc pa_channel_map_init_extend; - ma_proc pa_channel_map_valid; - ma_proc pa_channel_map_compatible; - ma_proc pa_stream_new; - ma_proc pa_stream_unref; - ma_proc pa_stream_connect_playback; - ma_proc pa_stream_connect_record; - ma_proc pa_stream_disconnect; - ma_proc pa_stream_get_state; - ma_proc pa_stream_get_sample_spec; - ma_proc pa_stream_get_channel_map; - ma_proc pa_stream_get_buffer_attr; - ma_proc pa_stream_set_buffer_attr; - ma_proc pa_stream_get_device_name; - ma_proc pa_stream_set_write_callback; - ma_proc pa_stream_set_read_callback; - ma_proc pa_stream_set_suspended_callback; - ma_proc pa_stream_set_moved_callback; - ma_proc pa_stream_is_suspended; - ma_proc pa_stream_flush; - ma_proc pa_stream_drain; - ma_proc pa_stream_is_corked; - ma_proc pa_stream_cork; - ma_proc pa_stream_trigger; - ma_proc pa_stream_begin_write; - ma_proc pa_stream_write; - ma_proc pa_stream_peek; - ma_proc pa_stream_drop; - ma_proc pa_stream_writable_size; - ma_proc pa_stream_readable_size; - - /*pa_mainloop**/ ma_ptr pMainLoop; - /*pa_context**/ ma_ptr pPulseContext; - char* pApplicationName; /* Set when the context is initialized. Used by devices for their local pa_context objects. */ - char* pServerName; /* Set when the context is initialized. Used by devices for their local pa_context objects. */ - } pulse; -#endif -#ifdef MA_SUPPORT_JACK - struct - { - ma_handle jackSO; - ma_proc jack_client_open; - ma_proc jack_client_close; - ma_proc jack_client_name_size; - ma_proc jack_set_process_callback; - ma_proc jack_set_buffer_size_callback; - ma_proc jack_on_shutdown; - ma_proc jack_get_sample_rate; - ma_proc jack_get_buffer_size; - ma_proc jack_get_ports; - ma_proc jack_activate; - ma_proc jack_deactivate; - ma_proc jack_connect; - ma_proc jack_port_register; - ma_proc jack_port_name; - ma_proc jack_port_get_buffer; - ma_proc jack_free; - - char* pClientName; - ma_bool32 tryStartServer; - } jack; -#endif -#ifdef MA_SUPPORT_COREAUDIO - struct - { - ma_handle hCoreFoundation; - ma_proc CFStringGetCString; - ma_proc CFRelease; - - ma_handle hCoreAudio; - ma_proc AudioObjectGetPropertyData; - ma_proc AudioObjectGetPropertyDataSize; - ma_proc AudioObjectSetPropertyData; - ma_proc AudioObjectAddPropertyListener; - ma_proc AudioObjectRemovePropertyListener; - - ma_handle hAudioUnit; /* Could possibly be set to AudioToolbox on later versions of macOS. */ - ma_proc AudioComponentFindNext; - ma_proc AudioComponentInstanceDispose; - ma_proc AudioComponentInstanceNew; - ma_proc AudioOutputUnitStart; - ma_proc AudioOutputUnitStop; - ma_proc AudioUnitAddPropertyListener; - ma_proc AudioUnitGetPropertyInfo; - ma_proc AudioUnitGetProperty; - ma_proc AudioUnitSetProperty; - ma_proc AudioUnitInitialize; - ma_proc AudioUnitRender; - - /*AudioComponent*/ ma_ptr component; - ma_bool32 noAudioSessionDeactivate; /* For tracking whether or not the iOS audio session should be explicitly deactivated. Set from the config in ma_context_init__coreaudio(). */ - } coreaudio; -#endif -#ifdef MA_SUPPORT_SNDIO - struct - { - ma_handle sndioSO; - ma_proc sio_open; - ma_proc sio_close; - ma_proc sio_setpar; - ma_proc sio_getpar; - ma_proc sio_getcap; - ma_proc sio_start; - ma_proc sio_stop; - ma_proc sio_read; - ma_proc sio_write; - ma_proc sio_onmove; - ma_proc sio_nfds; - ma_proc sio_pollfd; - ma_proc sio_revents; - ma_proc sio_eof; - ma_proc sio_setvol; - ma_proc sio_onvol; - ma_proc sio_initpar; - } sndio; -#endif -#ifdef MA_SUPPORT_AUDIO4 - struct - { - int _unused; - } audio4; -#endif -#ifdef MA_SUPPORT_OSS - struct - { - int versionMajor; - int versionMinor; - } oss; -#endif -#ifdef MA_SUPPORT_AAUDIO - struct - { - ma_handle hAAudio; /* libaaudio.so */ - ma_proc AAudio_createStreamBuilder; - ma_proc AAudioStreamBuilder_delete; - ma_proc AAudioStreamBuilder_setDeviceId; - ma_proc AAudioStreamBuilder_setDirection; - ma_proc AAudioStreamBuilder_setSharingMode; - ma_proc AAudioStreamBuilder_setFormat; - ma_proc AAudioStreamBuilder_setChannelCount; - ma_proc AAudioStreamBuilder_setSampleRate; - ma_proc AAudioStreamBuilder_setBufferCapacityInFrames; - ma_proc AAudioStreamBuilder_setFramesPerDataCallback; - ma_proc AAudioStreamBuilder_setDataCallback; - ma_proc AAudioStreamBuilder_setErrorCallback; - ma_proc AAudioStreamBuilder_setPerformanceMode; - ma_proc AAudioStreamBuilder_setUsage; - ma_proc AAudioStreamBuilder_setContentType; - ma_proc AAudioStreamBuilder_setInputPreset; - ma_proc AAudioStreamBuilder_setAllowedCapturePolicy; - ma_proc AAudioStreamBuilder_openStream; - ma_proc AAudioStream_close; - ma_proc AAudioStream_getState; - ma_proc AAudioStream_waitForStateChange; - ma_proc AAudioStream_getFormat; - ma_proc AAudioStream_getChannelCount; - ma_proc AAudioStream_getSampleRate; - ma_proc AAudioStream_getBufferCapacityInFrames; - ma_proc AAudioStream_getFramesPerDataCallback; - ma_proc AAudioStream_getFramesPerBurst; - ma_proc AAudioStream_requestStart; - ma_proc AAudioStream_requestStop; - ma_device_job_thread jobThread; /* For processing operations outside of the error callback, specifically device disconnections and rerouting. */ - } aaudio; -#endif -#ifdef MA_SUPPORT_OPENSL - struct - { - ma_handle libOpenSLES; - ma_handle SL_IID_ENGINE; - ma_handle SL_IID_AUDIOIODEVICECAPABILITIES; - ma_handle SL_IID_ANDROIDSIMPLEBUFFERQUEUE; - ma_handle SL_IID_RECORD; - ma_handle SL_IID_PLAY; - ma_handle SL_IID_OUTPUTMIX; - ma_handle SL_IID_ANDROIDCONFIGURATION; - ma_proc slCreateEngine; - } opensl; -#endif -#ifdef MA_SUPPORT_WEBAUDIO - struct - { - int _unused; - } webaudio; -#endif -#ifdef MA_SUPPORT_NULL - struct - { - int _unused; - } null_backend; -#endif - }; - - union - { -#if defined(MA_WIN32) - struct - { - /*HMODULE*/ ma_handle hOle32DLL; - ma_proc CoInitialize; - ma_proc CoInitializeEx; - ma_proc CoUninitialize; - ma_proc CoCreateInstance; - ma_proc CoTaskMemFree; - ma_proc PropVariantClear; - ma_proc StringFromGUID2; - - /*HMODULE*/ ma_handle hUser32DLL; - ma_proc GetForegroundWindow; - ma_proc GetDesktopWindow; - - /*HMODULE*/ ma_handle hAdvapi32DLL; - ma_proc RegOpenKeyExA; - ma_proc RegCloseKey; - ma_proc RegQueryValueExA; - - /*HRESULT*/ long CoInitializeResult; - } win32; -#endif -#ifdef MA_POSIX - struct - { - int _unused; - } posix; -#endif - int _unused; - }; -}; - -struct ma_device -{ - ma_context* pContext; - ma_device_type type; - ma_uint32 sampleRate; - ma_atomic_device_state state; /* The state of the device is variable and can change at any time on any thread. Must be used atomically. */ - ma_device_data_proc onData; /* Set once at initialization time and should not be changed after. */ - ma_device_notification_proc onNotification; /* Set once at initialization time and should not be changed after. */ - ma_stop_proc onStop; /* DEPRECATED. Use the notification callback instead. Set once at initialization time and should not be changed after. */ - void* pUserData; /* Application defined data. */ - ma_mutex startStopLock; - ma_event wakeupEvent; - ma_event startEvent; - ma_event stopEvent; - ma_thread thread; - ma_result workResult; /* This is set by the worker thread after it's finished doing a job. */ - ma_bool8 isOwnerOfContext; /* When set to true, uninitializing the device will also uninitialize the context. Set to true when NULL is passed into ma_device_init(). */ - ma_bool8 noPreSilencedOutputBuffer; - ma_bool8 noClip; - ma_bool8 noDisableDenormals; - ma_bool8 noFixedSizedCallback; - ma_atomic_float masterVolumeFactor; /* Linear 0..1. Can be read and written simultaneously by different threads. Must be used atomically. */ - ma_duplex_rb duplexRB; /* Intermediary buffer for duplex device on asynchronous backends. */ - struct - { - ma_resample_algorithm algorithm; - ma_resampling_backend_vtable* pBackendVTable; - void* pBackendUserData; - struct - { - ma_uint32 lpfOrder; - } linear; - } resampling; - struct - { - ma_device_id* pID; /* Set to NULL if using default ID, otherwise set to the address of "id". */ - ma_device_id id; /* If using an explicit device, will be set to a copy of the ID used for initialization. Otherwise cleared to 0. */ - char name[MA_MAX_DEVICE_NAME_LENGTH + 1]; /* Maybe temporary. Likely to be replaced with a query API. */ - ma_share_mode shareMode; /* Set to whatever was passed in when the device was initialized. */ - ma_format format; - ma_uint32 channels; - ma_channel channelMap[MA_MAX_CHANNELS]; - ma_format internalFormat; - ma_uint32 internalChannels; - ma_uint32 internalSampleRate; - ma_channel internalChannelMap[MA_MAX_CHANNELS]; - ma_uint32 internalPeriodSizeInFrames; - ma_uint32 internalPeriods; - ma_channel_mix_mode channelMixMode; - ma_bool32 calculateLFEFromSpatialChannels; - ma_data_converter converter; - void* pIntermediaryBuffer; /* For implementing fixed sized buffer callbacks. Will be null if using variable sized callbacks. */ - ma_uint32 intermediaryBufferCap; - ma_uint32 intermediaryBufferLen; /* How many valid frames are sitting in the intermediary buffer. */ - void* pInputCache; /* In external format. Can be null. */ - ma_uint64 inputCacheCap; - ma_uint64 inputCacheConsumed; - ma_uint64 inputCacheRemaining; - } playback; - struct - { - ma_device_id* pID; /* Set to NULL if using default ID, otherwise set to the address of "id". */ - ma_device_id id; /* If using an explicit device, will be set to a copy of the ID used for initialization. Otherwise cleared to 0. */ - char name[MA_MAX_DEVICE_NAME_LENGTH + 1]; /* Maybe temporary. Likely to be replaced with a query API. */ - ma_share_mode shareMode; /* Set to whatever was passed in when the device was initialized. */ - ma_format format; - ma_uint32 channels; - ma_channel channelMap[MA_MAX_CHANNELS]; - ma_format internalFormat; - ma_uint32 internalChannels; - ma_uint32 internalSampleRate; - ma_channel internalChannelMap[MA_MAX_CHANNELS]; - ma_uint32 internalPeriodSizeInFrames; - ma_uint32 internalPeriods; - ma_channel_mix_mode channelMixMode; - ma_bool32 calculateLFEFromSpatialChannels; - ma_data_converter converter; - void* pIntermediaryBuffer; /* For implementing fixed sized buffer callbacks. Will be null if using variable sized callbacks. */ - ma_uint32 intermediaryBufferCap; - ma_uint32 intermediaryBufferLen; /* How many valid frames are sitting in the intermediary buffer. */ - } capture; - - union - { -#ifdef MA_SUPPORT_WASAPI - struct - { - /*IAudioClient**/ ma_ptr pAudioClientPlayback; - /*IAudioClient**/ ma_ptr pAudioClientCapture; - /*IAudioRenderClient**/ ma_ptr pRenderClient; - /*IAudioCaptureClient**/ ma_ptr pCaptureClient; - /*IMMDeviceEnumerator**/ ma_ptr pDeviceEnumerator; /* Used for IMMNotificationClient notifications. Required for detecting default device changes. */ - ma_IMMNotificationClient notificationClient; - /*HANDLE*/ ma_handle hEventPlayback; /* Auto reset. Initialized to signaled. */ - /*HANDLE*/ ma_handle hEventCapture; /* Auto reset. Initialized to unsignaled. */ - ma_uint32 actualBufferSizeInFramesPlayback; /* Value from GetBufferSize(). internalPeriodSizeInFrames is not set to the _actual_ buffer size when low-latency shared mode is being used due to the way the IAudioClient3 API works. */ - ma_uint32 actualBufferSizeInFramesCapture; - ma_uint32 originalPeriodSizeInFrames; - ma_uint32 originalPeriodSizeInMilliseconds; - ma_uint32 originalPeriods; - ma_performance_profile originalPerformanceProfile; - ma_uint32 periodSizeInFramesPlayback; - ma_uint32 periodSizeInFramesCapture; - void* pMappedBufferCapture; - ma_uint32 mappedBufferCaptureCap; - ma_uint32 mappedBufferCaptureLen; - void* pMappedBufferPlayback; - ma_uint32 mappedBufferPlaybackCap; - ma_uint32 mappedBufferPlaybackLen; - ma_atomic_bool32 isStartedCapture; /* Can be read and written simultaneously across different threads. Must be used atomically, and must be 32-bit. */ - ma_atomic_bool32 isStartedPlayback; /* Can be read and written simultaneously across different threads. Must be used atomically, and must be 32-bit. */ - ma_uint32 loopbackProcessID; - ma_bool8 loopbackProcessExclude; - ma_bool8 noAutoConvertSRC; /* When set to true, disables the use of AUDCLNT_STREAMFLAGS_AUTOCONVERTPCM. */ - ma_bool8 noDefaultQualitySRC; /* When set to true, disables the use of AUDCLNT_STREAMFLAGS_SRC_DEFAULT_QUALITY. */ - ma_bool8 noHardwareOffloading; - ma_bool8 allowCaptureAutoStreamRouting; - ma_bool8 allowPlaybackAutoStreamRouting; - ma_bool8 isDetachedPlayback; - ma_bool8 isDetachedCapture; - ma_wasapi_usage usage; - void* hAvrtHandle; - ma_mutex rerouteLock; - } wasapi; -#endif -#ifdef MA_SUPPORT_DSOUND - struct - { - /*LPDIRECTSOUND*/ ma_ptr pPlayback; - /*LPDIRECTSOUNDBUFFER*/ ma_ptr pPlaybackPrimaryBuffer; - /*LPDIRECTSOUNDBUFFER*/ ma_ptr pPlaybackBuffer; - /*LPDIRECTSOUNDCAPTURE*/ ma_ptr pCapture; - /*LPDIRECTSOUNDCAPTUREBUFFER*/ ma_ptr pCaptureBuffer; - } dsound; -#endif -#ifdef MA_SUPPORT_WINMM - struct - { - /*HWAVEOUT*/ ma_handle hDevicePlayback; - /*HWAVEIN*/ ma_handle hDeviceCapture; - /*HANDLE*/ ma_handle hEventPlayback; - /*HANDLE*/ ma_handle hEventCapture; - ma_uint32 fragmentSizeInFrames; - ma_uint32 iNextHeaderPlayback; /* [0,periods). Used as an index into pWAVEHDRPlayback. */ - ma_uint32 iNextHeaderCapture; /* [0,periods). Used as an index into pWAVEHDRCapture. */ - ma_uint32 headerFramesConsumedPlayback; /* The number of PCM frames consumed in the buffer in pWAVEHEADER[iNextHeader]. */ - ma_uint32 headerFramesConsumedCapture; /* ^^^ */ - /*WAVEHDR**/ ma_uint8* pWAVEHDRPlayback; /* One instantiation for each period. */ - /*WAVEHDR**/ ma_uint8* pWAVEHDRCapture; /* One instantiation for each period. */ - ma_uint8* pIntermediaryBufferPlayback; - ma_uint8* pIntermediaryBufferCapture; - ma_uint8* _pHeapData; /* Used internally and is used for the heap allocated data for the intermediary buffer and the WAVEHDR structures. */ - } winmm; -#endif -#ifdef MA_SUPPORT_ALSA - struct - { - /*snd_pcm_t**/ ma_ptr pPCMPlayback; - /*snd_pcm_t**/ ma_ptr pPCMCapture; - /*struct pollfd**/ void* pPollDescriptorsPlayback; - /*struct pollfd**/ void* pPollDescriptorsCapture; - int pollDescriptorCountPlayback; - int pollDescriptorCountCapture; - int wakeupfdPlayback; /* eventfd for waking up from poll() when the playback device is stopped. */ - int wakeupfdCapture; /* eventfd for waking up from poll() when the capture device is stopped. */ - ma_bool8 isUsingMMapPlayback; - ma_bool8 isUsingMMapCapture; - } alsa; -#endif -#ifdef MA_SUPPORT_PULSEAUDIO - struct - { - /*pa_mainloop**/ ma_ptr pMainLoop; - /*pa_context**/ ma_ptr pPulseContext; - /*pa_stream**/ ma_ptr pStreamPlayback; - /*pa_stream**/ ma_ptr pStreamCapture; - } pulse; -#endif -#ifdef MA_SUPPORT_JACK - struct - { - /*jack_client_t**/ ma_ptr pClient; - /*jack_port_t**/ ma_ptr* ppPortsPlayback; - /*jack_port_t**/ ma_ptr* ppPortsCapture; - float* pIntermediaryBufferPlayback; /* Typed as a float because JACK is always floating point. */ - float* pIntermediaryBufferCapture; - } jack; -#endif -#ifdef MA_SUPPORT_COREAUDIO - struct - { - ma_uint32 deviceObjectIDPlayback; - ma_uint32 deviceObjectIDCapture; - /*AudioUnit*/ ma_ptr audioUnitPlayback; - /*AudioUnit*/ ma_ptr audioUnitCapture; - /*AudioBufferList**/ ma_ptr pAudioBufferList; /* Only used for input devices. */ - ma_uint32 audioBufferCapInFrames; /* Only used for input devices. The capacity in frames of each buffer in pAudioBufferList. */ - ma_event stopEvent; - ma_uint32 originalPeriodSizeInFrames; - ma_uint32 originalPeriodSizeInMilliseconds; - ma_uint32 originalPeriods; - ma_performance_profile originalPerformanceProfile; - ma_bool32 isDefaultPlaybackDevice; - ma_bool32 isDefaultCaptureDevice; - ma_bool32 isSwitchingPlaybackDevice; /* <-- Set to true when the default device has changed and miniaudio is in the process of switching. */ - ma_bool32 isSwitchingCaptureDevice; /* <-- Set to true when the default device has changed and miniaudio is in the process of switching. */ - void* pNotificationHandler; /* Only used on mobile platforms. Obj-C object for handling route changes. */ - } coreaudio; -#endif -#ifdef MA_SUPPORT_SNDIO - struct - { - ma_ptr handlePlayback; - ma_ptr handleCapture; - ma_bool32 isStartedPlayback; - ma_bool32 isStartedCapture; - } sndio; -#endif -#ifdef MA_SUPPORT_AUDIO4 - struct - { - int fdPlayback; - int fdCapture; - } audio4; -#endif -#ifdef MA_SUPPORT_OSS - struct - { - int fdPlayback; - int fdCapture; - } oss; -#endif -#ifdef MA_SUPPORT_AAUDIO - struct - { - /*AAudioStream**/ ma_ptr pStreamPlayback; - /*AAudioStream**/ ma_ptr pStreamCapture; - ma_mutex rerouteLock; - ma_aaudio_usage usage; - ma_aaudio_content_type contentType; - ma_aaudio_input_preset inputPreset; - ma_aaudio_allowed_capture_policy allowedCapturePolicy; - ma_bool32 noAutoStartAfterReroute; - } aaudio; -#endif -#ifdef MA_SUPPORT_OPENSL - struct - { - /*SLObjectItf*/ ma_ptr pOutputMixObj; - /*SLOutputMixItf*/ ma_ptr pOutputMix; - /*SLObjectItf*/ ma_ptr pAudioPlayerObj; - /*SLPlayItf*/ ma_ptr pAudioPlayer; - /*SLObjectItf*/ ma_ptr pAudioRecorderObj; - /*SLRecordItf*/ ma_ptr pAudioRecorder; - /*SLAndroidSimpleBufferQueueItf*/ ma_ptr pBufferQueuePlayback; - /*SLAndroidSimpleBufferQueueItf*/ ma_ptr pBufferQueueCapture; - ma_bool32 isDrainingCapture; - ma_bool32 isDrainingPlayback; - ma_uint32 currentBufferIndexPlayback; - ma_uint32 currentBufferIndexCapture; - ma_uint8* pBufferPlayback; /* This is malloc()'d and is used for storing audio data. Typed as ma_uint8 for easy offsetting. */ - ma_uint8* pBufferCapture; - } opensl; -#endif -#ifdef MA_SUPPORT_WEBAUDIO - struct - { - /* AudioWorklets path. */ - /* EMSCRIPTEN_WEBAUDIO_T */ int audioContext; - /* EMSCRIPTEN_WEBAUDIO_T */ int audioWorklet; - float* pIntermediaryBuffer; - void* pStackBuffer; - ma_result initResult; /* Set to MA_BUSY while initialization is in progress. */ - int deviceIndex; /* We store the device in a list on the JavaScript side. This is used to map our C object to the JS object. */ - } webaudio; -#endif -#ifdef MA_SUPPORT_NULL - struct - { - ma_thread deviceThread; - ma_event operationEvent; - ma_event operationCompletionEvent; - ma_semaphore operationSemaphore; - ma_uint32 operation; - ma_result operationResult; - ma_timer timer; - double priorRunTime; - ma_uint32 currentPeriodFramesRemainingPlayback; - ma_uint32 currentPeriodFramesRemainingCapture; - ma_uint64 lastProcessedFramePlayback; - ma_uint64 lastProcessedFrameCapture; - ma_atomic_bool32 isStarted; /* Read and written by multiple threads. Must be used atomically, and must be 32-bit for compiler compatibility. */ - } null_device; -#endif - }; -}; -#if defined(_MSC_VER) && !defined(__clang__) - #pragma warning(pop) -#elif defined(__clang__) || (defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 8))) - #pragma GCC diagnostic pop /* For ISO C99 doesn't support unnamed structs/unions [-Wpedantic] */ -#endif - -/* -Initializes a `ma_context_config` object. - - -Return Value ------------- -A `ma_context_config` initialized to defaults. - - -Remarks -------- -You must always use this to initialize the default state of the `ma_context_config` object. Not using this will result in your program breaking when miniaudio -is updated and new members are added to `ma_context_config`. It also sets logical defaults. - -You can override members of the returned object by changing it's members directly. - - -See Also --------- -ma_context_init() -*/ -MA_API ma_context_config ma_context_config_init(void); - -/* -Initializes a context. - -The context is used for selecting and initializing an appropriate backend and to represent the backend at a more global level than that of an individual -device. There is one context to many devices, and a device is created from a context. A context is required to enumerate devices. - - -Parameters ----------- -backends (in, optional) - A list of backends to try initializing, in priority order. Can be NULL, in which case it uses default priority order. - -backendCount (in, optional) - The number of items in `backend`. Ignored if `backend` is NULL. - -pConfig (in, optional) - The context configuration. - -pContext (in) - A pointer to the context object being initialized. - - -Return Value ------------- -MA_SUCCESS if successful; any other error code otherwise. - - -Thread Safety -------------- -Unsafe. Do not call this function across multiple threads as some backends read and write to global state. - - -Remarks -------- -When `backends` is NULL, the default priority order will be used. Below is a list of backends in priority order: - - |-------------|-----------------------|--------------------------------------------------------| - | Name | Enum Name | Supported Operating Systems | - |-------------|-----------------------|--------------------------------------------------------| - | WASAPI | ma_backend_wasapi | Windows Vista+ | - | DirectSound | ma_backend_dsound | Windows XP+ | - | WinMM | ma_backend_winmm | Windows XP+ (may work on older versions, but untested) | - | Core Audio | ma_backend_coreaudio | macOS, iOS | - | ALSA | ma_backend_alsa | Linux | - | PulseAudio | ma_backend_pulseaudio | Cross Platform (disabled on Windows, BSD and Android) | - | JACK | ma_backend_jack | Cross Platform (disabled on BSD and Android) | - | sndio | ma_backend_sndio | OpenBSD | - | audio(4) | ma_backend_audio4 | NetBSD, OpenBSD | - | OSS | ma_backend_oss | FreeBSD | - | AAudio | ma_backend_aaudio | Android 8+ | - | OpenSL|ES | ma_backend_opensl | Android (API level 16+) | - | Web Audio | ma_backend_webaudio | Web (via Emscripten) | - | Null | ma_backend_null | Cross Platform (not used on Web) | - |-------------|-----------------------|--------------------------------------------------------| - -The context can be configured via the `pConfig` argument. The config object is initialized with `ma_context_config_init()`. Individual configuration settings -can then be set directly on the structure. Below are the members of the `ma_context_config` object. - - pLog - A pointer to the `ma_log` to post log messages to. Can be NULL if the application does not - require logging. See the `ma_log` API for details on how to use the logging system. - - threadPriority - The desired priority to use for the audio thread. Allowable values include the following: - - |--------------------------------------| - | Thread Priority | - |--------------------------------------| - | ma_thread_priority_idle | - | ma_thread_priority_lowest | - | ma_thread_priority_low | - | ma_thread_priority_normal | - | ma_thread_priority_high | - | ma_thread_priority_highest (default) | - | ma_thread_priority_realtime | - | ma_thread_priority_default | - |--------------------------------------| - - threadStackSize - The desired size of the stack for the audio thread. Defaults to the operating system's default. - - pUserData - A pointer to application-defined data. This can be accessed from the context object directly such as `context.pUserData`. - - allocationCallbacks - Structure containing custom allocation callbacks. Leaving this at defaults will cause it to use MA_MALLOC, MA_REALLOC and MA_FREE. These allocation - callbacks will be used for anything tied to the context, including devices. - - alsa.useVerboseDeviceEnumeration - ALSA will typically enumerate many different devices which can be intrusive and not user-friendly. To combat this, miniaudio will enumerate only unique - card/device pairs by default. The problem with this is that you lose a bit of flexibility and control. Setting alsa.useVerboseDeviceEnumeration makes - it so the ALSA backend includes all devices. Defaults to false. - - pulse.pApplicationName - PulseAudio only. The application name to use when initializing the PulseAudio context with `pa_context_new()`. - - pulse.pServerName - PulseAudio only. The name of the server to connect to with `pa_context_connect()`. - - pulse.tryAutoSpawn - PulseAudio only. Whether or not to try automatically starting the PulseAudio daemon. Defaults to false. If you set this to true, keep in mind that - miniaudio uses a trial and error method to find the most appropriate backend, and this will result in the PulseAudio daemon starting which may be - intrusive for the end user. - - coreaudio.sessionCategory - iOS only. The session category to use for the shared AudioSession instance. Below is a list of allowable values and their Core Audio equivalents. - - |-----------------------------------------|-------------------------------------| - | miniaudio Token | Core Audio Token | - |-----------------------------------------|-------------------------------------| - | ma_ios_session_category_ambient | AVAudioSessionCategoryAmbient | - | ma_ios_session_category_solo_ambient | AVAudioSessionCategorySoloAmbient | - | ma_ios_session_category_playback | AVAudioSessionCategoryPlayback | - | ma_ios_session_category_record | AVAudioSessionCategoryRecord | - | ma_ios_session_category_play_and_record | AVAudioSessionCategoryPlayAndRecord | - | ma_ios_session_category_multi_route | AVAudioSessionCategoryMultiRoute | - | ma_ios_session_category_none | AVAudioSessionCategoryAmbient | - | ma_ios_session_category_default | AVAudioSessionCategoryAmbient | - |-----------------------------------------|-------------------------------------| - - coreaudio.sessionCategoryOptions - iOS only. Session category options to use with the shared AudioSession instance. Below is a list of allowable values and their Core Audio equivalents. - - |---------------------------------------------------------------------------|------------------------------------------------------------------| - | miniaudio Token | Core Audio Token | - |---------------------------------------------------------------------------|------------------------------------------------------------------| - | ma_ios_session_category_option_mix_with_others | AVAudioSessionCategoryOptionMixWithOthers | - | ma_ios_session_category_option_duck_others | AVAudioSessionCategoryOptionDuckOthers | - | ma_ios_session_category_option_allow_bluetooth | AVAudioSessionCategoryOptionAllowBluetooth | - | ma_ios_session_category_option_default_to_speaker | AVAudioSessionCategoryOptionDefaultToSpeaker | - | ma_ios_session_category_option_interrupt_spoken_audio_and_mix_with_others | AVAudioSessionCategoryOptionInterruptSpokenAudioAndMixWithOthers | - | ma_ios_session_category_option_allow_bluetooth_a2dp | AVAudioSessionCategoryOptionAllowBluetoothA2DP | - | ma_ios_session_category_option_allow_air_play | AVAudioSessionCategoryOptionAllowAirPlay | - |---------------------------------------------------------------------------|------------------------------------------------------------------| - - coreaudio.noAudioSessionActivate - iOS only. When set to true, does not perform an explicit [[AVAudioSession sharedInstace] setActive:true] on initialization. - - coreaudio.noAudioSessionDeactivate - iOS only. When set to true, does not perform an explicit [[AVAudioSession sharedInstace] setActive:false] on uninitialization. - - jack.pClientName - The name of the client to pass to `jack_client_open()`. - - jack.tryStartServer - Whether or not to try auto-starting the JACK server. Defaults to false. - - -It is recommended that only a single context is active at any given time because it's a bulky data structure which performs run-time linking for the -relevant backends every time it's initialized. - -The location of the context cannot change throughout it's lifetime. Consider allocating the `ma_context` object with `malloc()` if this is an issue. The -reason for this is that a pointer to the context is stored in the `ma_device` structure. - - -Example 1 - Default Initialization ----------------------------------- -The example below shows how to initialize the context using the default configuration. - -```c -ma_context context; -ma_result result = ma_context_init(NULL, 0, NULL, &context); -if (result != MA_SUCCESS) { - // Error. -} -``` - - -Example 2 - Custom Configuration --------------------------------- -The example below shows how to initialize the context using custom backend priorities and a custom configuration. In this hypothetical example, the program -wants to prioritize ALSA over PulseAudio on Linux. They also want to avoid using the WinMM backend on Windows because it's latency is too high. They also -want an error to be returned if no valid backend is available which they achieve by excluding the Null backend. - -For the configuration, the program wants to capture any log messages so they can, for example, route it to a log file and user interface. - -```c -ma_backend backends[] = { - ma_backend_alsa, - ma_backend_pulseaudio, - ma_backend_wasapi, - ma_backend_dsound -}; - -ma_log log; -ma_log_init(&log); -ma_log_register_callback(&log, ma_log_callback_init(my_log_callbac, pMyLogUserData)); - -ma_context_config config = ma_context_config_init(); -config.pLog = &log; // Specify a custom log object in the config so any logs that are posted from ma_context_init() are captured. - -ma_context context; -ma_result result = ma_context_init(backends, sizeof(backends)/sizeof(backends[0]), &config, &context); -if (result != MA_SUCCESS) { - // Error. - if (result == MA_NO_BACKEND) { - // Couldn't find an appropriate backend. - } -} - -// You could also attach a log callback post-initialization: -ma_log_register_callback(ma_context_get_log(&context), ma_log_callback_init(my_log_callback, pMyLogUserData)); -``` - - -See Also --------- -ma_context_config_init() -ma_context_uninit() -*/ -MA_API ma_result ma_context_init(const ma_backend backends[], ma_uint32 backendCount, const ma_context_config* pConfig, ma_context* pContext); - -/* -Uninitializes a context. - - -Return Value ------------- -MA_SUCCESS if successful; any other error code otherwise. - - -Thread Safety -------------- -Unsafe. Do not call this function across multiple threads as some backends read and write to global state. - - -Remarks -------- -Results are undefined if you call this while any device created by this context is still active. - - -See Also --------- -ma_context_init() -*/ -MA_API ma_result ma_context_uninit(ma_context* pContext); - -/* -Retrieves the size of the ma_context object. - -This is mainly for the purpose of bindings to know how much memory to allocate. -*/ -MA_API size_t ma_context_sizeof(void); - -/* -Retrieves a pointer to the log object associated with this context. - - -Remarks -------- -Pass the returned pointer to `ma_log_post()`, `ma_log_postv()` or `ma_log_postf()` to post a log -message. - -You can attach your own logging callback to the log with `ma_log_register_callback()` - - -Return Value ------------- -A pointer to the `ma_log` object that the context uses to post log messages. If some error occurs, -NULL will be returned. -*/ -MA_API ma_log* ma_context_get_log(ma_context* pContext); - -/* -Enumerates over every device (both playback and capture). - -This is a lower-level enumeration function to the easier to use `ma_context_get_devices()`. Use `ma_context_enumerate_devices()` if you would rather not incur -an internal heap allocation, or it simply suits your code better. - -Note that this only retrieves the ID and name/description of the device. The reason for only retrieving basic information is that it would otherwise require -opening the backend device in order to probe it for more detailed information which can be inefficient. Consider using `ma_context_get_device_info()` for this, -but don't call it from within the enumeration callback. - -Returning false from the callback will stop enumeration. Returning true will continue enumeration. - - -Parameters ----------- -pContext (in) - A pointer to the context performing the enumeration. - -callback (in) - The callback to fire for each enumerated device. - -pUserData (in) - A pointer to application-defined data passed to the callback. - - -Return Value ------------- -MA_SUCCESS if successful; any other error code otherwise. - - -Thread Safety -------------- -Safe. This is guarded using a simple mutex lock. - - -Remarks -------- -Do _not_ assume the first enumerated device of a given type is the default device. - -Some backends and platforms may only support default playback and capture devices. - -In general, you should not do anything complicated from within the callback. In particular, do not try initializing a device from within the callback. Also, -do not try to call `ma_context_get_device_info()` from within the callback. - -Consider using `ma_context_get_devices()` for a simpler and safer API, albeit at the expense of an internal heap allocation. - - -Example 1 - Simple Enumeration ------------------------------- -ma_bool32 ma_device_enum_callback(ma_context* pContext, ma_device_type deviceType, const ma_device_info* pInfo, void* pUserData) -{ - printf("Device Name: %s\n", pInfo->name); - return MA_TRUE; -} - -ma_result result = ma_context_enumerate_devices(&context, my_device_enum_callback, pMyUserData); -if (result != MA_SUCCESS) { - // Error. -} - - -See Also --------- -ma_context_get_devices() -*/ -MA_API ma_result ma_context_enumerate_devices(ma_context* pContext, ma_enum_devices_callback_proc callback, void* pUserData); - -/* -Retrieves basic information about every active playback and/or capture device. - -This function will allocate memory internally for the device lists and return a pointer to them through the `ppPlaybackDeviceInfos` and `ppCaptureDeviceInfos` -parameters. If you do not want to incur the overhead of these allocations consider using `ma_context_enumerate_devices()` which will instead use a callback. - -Note that this only retrieves the ID and name/description of the device. The reason for only retrieving basic information is that it would otherwise require -opening the backend device in order to probe it for more detailed information which can be inefficient. Consider using `ma_context_get_device_info()` for this, -but don't call it from within the enumeration callback. - - -Parameters ----------- -pContext (in) - A pointer to the context performing the enumeration. - -ppPlaybackDeviceInfos (out) - A pointer to a pointer that will receive the address of a buffer containing the list of `ma_device_info` structures for playback devices. - -pPlaybackDeviceCount (out) - A pointer to an unsigned integer that will receive the number of playback devices. - -ppCaptureDeviceInfos (out) - A pointer to a pointer that will receive the address of a buffer containing the list of `ma_device_info` structures for capture devices. - -pCaptureDeviceCount (out) - A pointer to an unsigned integer that will receive the number of capture devices. - - -Return Value ------------- -MA_SUCCESS if successful; any other error code otherwise. - - -Thread Safety -------------- -Unsafe. Since each call to this function invalidates the pointers from the previous call, you should not be calling this simultaneously across multiple -threads. Instead, you need to make a copy of the returned data with your own higher level synchronization. - - -Remarks -------- -It is _not_ safe to assume the first device in the list is the default device. - -You can pass in NULL for the playback or capture lists in which case they'll be ignored. - -The returned pointers will become invalid upon the next call this this function, or when the context is uninitialized. Do not free the returned pointers. - - -See Also --------- -ma_context_enumerate_devices() -*/ -MA_API ma_result ma_context_get_devices(ma_context* pContext, ma_device_info** ppPlaybackDeviceInfos, ma_uint32* pPlaybackDeviceCount, ma_device_info** ppCaptureDeviceInfos, ma_uint32* pCaptureDeviceCount); - -/* -Retrieves information about a device of the given type, with the specified ID and share mode. - - -Parameters ----------- -pContext (in) - A pointer to the context performing the query. - -deviceType (in) - The type of the device being queried. Must be either `ma_device_type_playback` or `ma_device_type_capture`. - -pDeviceID (in) - The ID of the device being queried. - -pDeviceInfo (out) - A pointer to the `ma_device_info` structure that will receive the device information. - - -Return Value ------------- -MA_SUCCESS if successful; any other error code otherwise. - - -Thread Safety -------------- -Safe. This is guarded using a simple mutex lock. - - -Remarks -------- -Do _not_ call this from within the `ma_context_enumerate_devices()` callback. - -It's possible for a device to have different information and capabilities depending on whether or not it's opened in shared or exclusive mode. For example, in -shared mode, WASAPI always uses floating point samples for mixing, but in exclusive mode it can be anything. Therefore, this function allows you to specify -which share mode you want information for. Note that not all backends and devices support shared or exclusive mode, in which case this function will fail if -the requested share mode is unsupported. - -This leaves pDeviceInfo unmodified in the result of an error. -*/ -MA_API ma_result ma_context_get_device_info(ma_context* pContext, ma_device_type deviceType, const ma_device_id* pDeviceID, ma_device_info* pDeviceInfo); - -/* -Determines if the given context supports loopback mode. - - -Parameters ----------- -pContext (in) - A pointer to the context getting queried. - - -Return Value ------------- -MA_TRUE if the context supports loopback mode; MA_FALSE otherwise. -*/ -MA_API ma_bool32 ma_context_is_loopback_supported(ma_context* pContext); - - - -/* -Initializes a device config with default settings. - - -Parameters ----------- -deviceType (in) - The type of the device this config is being initialized for. This must set to one of the following: - - |-------------------------| - | Device Type | - |-------------------------| - | ma_device_type_playback | - | ma_device_type_capture | - | ma_device_type_duplex | - | ma_device_type_loopback | - |-------------------------| - - -Return Value ------------- -A new device config object with default settings. You will typically want to adjust the config after this function returns. See remarks. - - -Thread Safety -------------- -Safe. - - -Callback Safety ---------------- -Safe, but don't try initializing a device in a callback. - - -Remarks -------- -The returned config will be initialized to defaults. You will normally want to customize a few variables before initializing the device. See Example 1 for a -typical configuration which sets the sample format, channel count, sample rate, data callback and user data. These are usually things you will want to change -before initializing the device. - -See `ma_device_init()` for details on specific configuration options. - - -Example 1 - Simple Configuration --------------------------------- -The example below is what a program will typically want to configure for each device at a minimum. Notice how `ma_device_config_init()` is called first, and -then the returned object is modified directly. This is important because it ensures that your program continues to work as new configuration options are added -to the `ma_device_config` structure. - -```c -ma_device_config config = ma_device_config_init(ma_device_type_playback); -config.playback.format = ma_format_f32; -config.playback.channels = 2; -config.sampleRate = 48000; -config.dataCallback = ma_data_callback; -config.pUserData = pMyUserData; -``` - - -See Also --------- -ma_device_init() -ma_device_init_ex() -*/ -MA_API ma_device_config ma_device_config_init(ma_device_type deviceType); - - -/* -Initializes a device. - -A device represents a physical audio device. The idea is you send or receive audio data from the device to either play it back through a speaker, or capture it -from a microphone. Whether or not you should send or receive data from the device (or both) depends on the type of device you are initializing which can be -playback, capture, full-duplex or loopback. (Note that loopback mode is only supported on select backends.) Sending and receiving audio data to and from the -device is done via a callback which is fired by miniaudio at periodic time intervals. - -The frequency at which data is delivered to and from a device depends on the size of its period. The size of the period can be defined in terms of PCM frames -or milliseconds, whichever is more convenient. Generally speaking, the smaller the period, the lower the latency at the expense of higher CPU usage and -increased risk of glitching due to the more frequent and granular data deliver intervals. The size of a period will depend on your requirements, but -miniaudio's defaults should work fine for most scenarios. If you're building a game you should leave this fairly small, whereas if you're building a simple -media player you can make it larger. Note that the period size you request is actually just a hint - miniaudio will tell the backend what you want, but the -backend is ultimately responsible for what it gives you. You cannot assume you will get exactly what you ask for. - -When delivering data to and from a device you need to make sure it's in the correct format which you can set through the device configuration. You just set the -format that you want to use and miniaudio will perform all of the necessary conversion for you internally. When delivering data to and from the callback you -can assume the format is the same as what you requested when you initialized the device. See Remarks for more details on miniaudio's data conversion pipeline. - - -Parameters ----------- -pContext (in, optional) - A pointer to the context that owns the device. This can be null, in which case it creates a default context internally. - -pConfig (in) - A pointer to the device configuration. Cannot be null. See remarks for details. - -pDevice (out) - A pointer to the device object being initialized. - - -Return Value ------------- -MA_SUCCESS if successful; any other error code otherwise. - - -Thread Safety -------------- -Unsafe. It is not safe to call this function simultaneously for different devices because some backends depend on and mutate global state. The same applies to -calling this at the same time as `ma_device_uninit()`. - - -Callback Safety ---------------- -Unsafe. It is not safe to call this inside any callback. - - -Remarks -------- -Setting `pContext` to NULL will result in miniaudio creating a default context internally and is equivalent to passing in a context initialized like so: - - ```c - ma_context_init(NULL, 0, NULL, &context); - ``` - -Do not set `pContext` to NULL if you are needing to open multiple devices. You can, however, use NULL when initializing the first device, and then use -device.pContext for the initialization of other devices. - -The device can be configured via the `pConfig` argument. The config object is initialized with `ma_device_config_init()`. Individual configuration settings can -then be set directly on the structure. Below are the members of the `ma_device_config` object. - - deviceType - Must be `ma_device_type_playback`, `ma_device_type_capture`, `ma_device_type_duplex` of `ma_device_type_loopback`. - - sampleRate - The sample rate, in hertz. The most common sample rates are 48000 and 44100. Setting this to 0 will use the device's native sample rate. - - periodSizeInFrames - The desired size of a period in PCM frames. If this is 0, `periodSizeInMilliseconds` will be used instead. If both are 0 the default buffer size will - be used depending on the selected performance profile. This value affects latency. See below for details. - - periodSizeInMilliseconds - The desired size of a period in milliseconds. If this is 0, `periodSizeInFrames` will be used instead. If both are 0 the default buffer size will be - used depending on the selected performance profile. The value affects latency. See below for details. - - periods - The number of periods making up the device's entire buffer. The total buffer size is `periodSizeInFrames` or `periodSizeInMilliseconds` multiplied by - this value. This is just a hint as backends will be the ones who ultimately decide how your periods will be configured. - - performanceProfile - A hint to miniaudio as to the performance requirements of your program. Can be either `ma_performance_profile_low_latency` (default) or - `ma_performance_profile_conservative`. This mainly affects the size of default buffers and can usually be left at its default value. - - noPreSilencedOutputBuffer - When set to true, the contents of the output buffer passed into the data callback will be left undefined. When set to false (default), the contents of - the output buffer will be cleared the zero. You can use this to avoid the overhead of zeroing out the buffer if you can guarantee that your data - callback will write to every sample in the output buffer, or if you are doing your own clearing. - - noClip - When set to true, the contents of the output buffer are left alone after returning and it will be left up to the backend itself to decide whether or - not to clip. When set to false (default), the contents of the output buffer passed into the data callback will be clipped after returning. This only - applies when the playback sample format is f32. - - noDisableDenormals - By default, miniaudio will disable denormals when the data callback is called. Setting this to true will prevent the disabling of denormals. - - noFixedSizedCallback - Allows miniaudio to fire the data callback with any frame count. When this is set to false (the default), the data callback will be fired with a - consistent frame count as specified by `periodSizeInFrames` or `periodSizeInMilliseconds`. When set to true, miniaudio will fire the callback with - whatever the backend requests, which could be anything. - - dataCallback - The callback to fire whenever data is ready to be delivered to or from the device. - - notificationCallback - The callback to fire when something has changed with the device, such as whether or not it has been started or stopped. - - pUserData - The user data pointer to use with the device. You can access this directly from the device object like `device.pUserData`. - - resampling.algorithm - The resampling algorithm to use when miniaudio needs to perform resampling between the rate specified by `sampleRate` and the device's native rate. The - default value is `ma_resample_algorithm_linear`, and the quality can be configured with `resampling.linear.lpfOrder`. - - resampling.pBackendVTable - A pointer to an optional vtable that can be used for plugging in a custom resampler. - - resampling.pBackendUserData - A pointer that will passed to callbacks in pBackendVTable. - - resampling.linear.lpfOrder - The linear resampler applies a low-pass filter as part of its processing for anti-aliasing. This setting controls the order of the filter. The higher - the value, the better the quality, in general. Setting this to 0 will disable low-pass filtering altogether. The maximum value is - `MA_MAX_FILTER_ORDER`. The default value is `min(4, MA_MAX_FILTER_ORDER)`. - - playback.pDeviceID - A pointer to a `ma_device_id` structure containing the ID of the playback device to initialize. Setting this NULL (default) will use the system's - default playback device. Retrieve the device ID from the `ma_device_info` structure, which can be retrieved using device enumeration. - - playback.format - The sample format to use for playback. When set to `ma_format_unknown` the device's native format will be used. This can be retrieved after - initialization from the device object directly with `device.playback.format`. - - playback.channels - The number of channels to use for playback. When set to 0 the device's native channel count will be used. This can be retrieved after initialization - from the device object directly with `device.playback.channels`. - - playback.pChannelMap - The channel map to use for playback. When left empty, the device's native channel map will be used. This can be retrieved after initialization from the - device object direct with `device.playback.pChannelMap`. When set, the buffer should contain `channels` items. - - playback.shareMode - The preferred share mode to use for playback. Can be either `ma_share_mode_shared` (default) or `ma_share_mode_exclusive`. Note that if you specify - exclusive mode, but it's not supported by the backend, initialization will fail. You can then fall back to shared mode if desired by changing this to - ma_share_mode_shared and reinitializing. - - capture.pDeviceID - A pointer to a `ma_device_id` structure containing the ID of the capture device to initialize. Setting this NULL (default) will use the system's - default capture device. Retrieve the device ID from the `ma_device_info` structure, which can be retrieved using device enumeration. - - capture.format - The sample format to use for capture. When set to `ma_format_unknown` the device's native format will be used. This can be retrieved after - initialization from the device object directly with `device.capture.format`. - - capture.channels - The number of channels to use for capture. When set to 0 the device's native channel count will be used. This can be retrieved after initialization - from the device object directly with `device.capture.channels`. - - capture.pChannelMap - The channel map to use for capture. When left empty, the device's native channel map will be used. This can be retrieved after initialization from the - device object direct with `device.capture.pChannelMap`. When set, the buffer should contain `channels` items. - - capture.shareMode - The preferred share mode to use for capture. Can be either `ma_share_mode_shared` (default) or `ma_share_mode_exclusive`. Note that if you specify - exclusive mode, but it's not supported by the backend, initialization will fail. You can then fall back to shared mode if desired by changing this to - ma_share_mode_shared and reinitializing. - - wasapi.noAutoConvertSRC - WASAPI only. When set to true, disables WASAPI's automatic resampling and forces the use of miniaudio's resampler. Defaults to false. - - wasapi.noDefaultQualitySRC - WASAPI only. Only used when `wasapi.noAutoConvertSRC` is set to false. When set to true, disables the use of `AUDCLNT_STREAMFLAGS_SRC_DEFAULT_QUALITY`. - You should usually leave this set to false, which is the default. - - wasapi.noAutoStreamRouting - WASAPI only. When set to true, disables automatic stream routing on the WASAPI backend. Defaults to false. - - wasapi.noHardwareOffloading - WASAPI only. When set to true, disables the use of WASAPI's hardware offloading feature. Defaults to false. - - alsa.noMMap - ALSA only. When set to true, disables MMap mode. Defaults to false. - - alsa.noAutoFormat - ALSA only. When set to true, disables ALSA's automatic format conversion by including the SND_PCM_NO_AUTO_FORMAT flag. Defaults to false. - - alsa.noAutoChannels - ALSA only. When set to true, disables ALSA's automatic channel conversion by including the SND_PCM_NO_AUTO_CHANNELS flag. Defaults to false. - - alsa.noAutoResample - ALSA only. When set to true, disables ALSA's automatic resampling by including the SND_PCM_NO_AUTO_RESAMPLE flag. Defaults to false. - - pulse.pStreamNamePlayback - PulseAudio only. Sets the stream name for playback. - - pulse.pStreamNameCapture - PulseAudio only. Sets the stream name for capture. - - pulse.channelMap - PulseAudio only. Sets the channel map that is requested from PulseAudio. See MA_PA_CHANNEL_MAP_* constants. Defaults to MA_PA_CHANNEL_MAP_AIFF. - - coreaudio.allowNominalSampleRateChange - Core Audio only. Desktop only. When enabled, allows the sample rate of the device to be changed at the operating system level. This - is disabled by default in order to prevent intrusive changes to the user's system. This is useful if you want to use a sample rate - that is known to be natively supported by the hardware thereby avoiding the cost of resampling. When set to true, miniaudio will - find the closest match between the sample rate requested in the device config and the sample rates natively supported by the - hardware. When set to false, the sample rate currently set by the operating system will always be used. - - opensl.streamType - OpenSL only. Explicitly sets the stream type. If left unset (`ma_opensl_stream_type_default`), the - stream type will be left unset. Think of this as the type of audio you're playing. - - opensl.recordingPreset - OpenSL only. Explicitly sets the type of recording your program will be doing. When left - unset, the recording preset will be left unchanged. - - aaudio.usage - AAudio only. Explicitly sets the nature of the audio the program will be consuming. When - left unset, the usage will be left unchanged. - - aaudio.contentType - AAudio only. Sets the content type. When left unset, the content type will be left unchanged. - - aaudio.inputPreset - AAudio only. Explicitly sets the type of recording your program will be doing. When left - unset, the input preset will be left unchanged. - - aaudio.noAutoStartAfterReroute - AAudio only. Controls whether or not the device should be automatically restarted after a - stream reroute. When set to false (default) the device will be restarted automatically; - otherwise the device will be stopped. - - -Once initialized, the device's config is immutable. If you need to change the config you will need to initialize a new device. - -After initializing the device it will be in a stopped state. To start it, use `ma_device_start()`. - -If both `periodSizeInFrames` and `periodSizeInMilliseconds` are set to zero, it will default to `MA_DEFAULT_PERIOD_SIZE_IN_MILLISECONDS_LOW_LATENCY` or -`MA_DEFAULT_PERIOD_SIZE_IN_MILLISECONDS_CONSERVATIVE`, depending on whether or not `performanceProfile` is set to `ma_performance_profile_low_latency` or -`ma_performance_profile_conservative`. - -If you request exclusive mode and the backend does not support it an error will be returned. For robustness, you may want to first try initializing the device -in exclusive mode, and then fall back to shared mode if required. Alternatively you can just request shared mode (the default if you leave it unset in the -config) which is the most reliable option. Some backends do not have a practical way of choosing whether or not the device should be exclusive or not (ALSA, -for example) in which case it just acts as a hint. Unless you have special requirements you should try avoiding exclusive mode as it's intrusive to the user. -Starting with Windows 10, miniaudio will use low-latency shared mode where possible which may make exclusive mode unnecessary. - -When sending or receiving data to/from a device, miniaudio will internally perform a format conversion to convert between the format specified by the config -and the format used internally by the backend. If you pass in 0 for the sample format, channel count, sample rate _and_ channel map, data transmission will run -on an optimized pass-through fast path. You can retrieve the format, channel count and sample rate by inspecting the `playback/capture.format`, -`playback/capture.channels` and `sampleRate` members of the device object. - -When compiling for UWP you must ensure you call this function on the main UI thread because the operating system may need to present the user with a message -asking for permissions. Please refer to the official documentation for ActivateAudioInterfaceAsync() for more information. - -ALSA Specific: When initializing the default device, requesting shared mode will try using the "dmix" device for playback and the "dsnoop" device for capture. -If these fail it will try falling back to the "hw" device. - - -Example 1 - Simple Initialization ---------------------------------- -This example shows how to initialize a simple playback device using a standard configuration. If you are just needing to do simple playback from the default -playback device this is usually all you need. - -```c -ma_device_config config = ma_device_config_init(ma_device_type_playback); -config.playback.format = ma_format_f32; -config.playback.channels = 2; -config.sampleRate = 48000; -config.dataCallback = ma_data_callback; -config.pMyUserData = pMyUserData; - -ma_device device; -ma_result result = ma_device_init(NULL, &config, &device); -if (result != MA_SUCCESS) { - // Error -} -``` - - -Example 2 - Advanced Initialization ------------------------------------ -This example shows how you might do some more advanced initialization. In this hypothetical example we want to control the latency by setting the buffer size -and period count. We also want to allow the user to be able to choose which device to output from which means we need a context so we can perform device -enumeration. - -```c -ma_context context; -ma_result result = ma_context_init(NULL, 0, NULL, &context); -if (result != MA_SUCCESS) { - // Error -} - -ma_device_info* pPlaybackDeviceInfos; -ma_uint32 playbackDeviceCount; -result = ma_context_get_devices(&context, &pPlaybackDeviceInfos, &playbackDeviceCount, NULL, NULL); -if (result != MA_SUCCESS) { - // Error -} - -// ... choose a device from pPlaybackDeviceInfos ... - -ma_device_config config = ma_device_config_init(ma_device_type_playback); -config.playback.pDeviceID = pMyChosenDeviceID; // <-- Get this from the `id` member of one of the `ma_device_info` objects returned by ma_context_get_devices(). -config.playback.format = ma_format_f32; -config.playback.channels = 2; -config.sampleRate = 48000; -config.dataCallback = ma_data_callback; -config.pUserData = pMyUserData; -config.periodSizeInMilliseconds = 10; -config.periods = 3; - -ma_device device; -result = ma_device_init(&context, &config, &device); -if (result != MA_SUCCESS) { - // Error -} -``` - - -See Also --------- -ma_device_config_init() -ma_device_uninit() -ma_device_start() -ma_context_init() -ma_context_get_devices() -ma_context_enumerate_devices() -*/ -MA_API ma_result ma_device_init(ma_context* pContext, const ma_device_config* pConfig, ma_device* pDevice); - -/* -Initializes a device without a context, with extra parameters for controlling the configuration of the internal self-managed context. - -This is the same as `ma_device_init()`, only instead of a context being passed in, the parameters from `ma_context_init()` are passed in instead. This function -allows you to configure the internally created context. - - -Parameters ----------- -backends (in, optional) - A list of backends to try initializing, in priority order. Can be NULL, in which case it uses default priority order. - -backendCount (in, optional) - The number of items in `backend`. Ignored if `backend` is NULL. - -pContextConfig (in, optional) - The context configuration. - -pConfig (in) - A pointer to the device configuration. Cannot be null. See remarks for details. - -pDevice (out) - A pointer to the device object being initialized. - - -Return Value ------------- -MA_SUCCESS if successful; any other error code otherwise. - - -Thread Safety -------------- -Unsafe. It is not safe to call this function simultaneously for different devices because some backends depend on and mutate global state. The same applies to -calling this at the same time as `ma_device_uninit()`. - - -Callback Safety ---------------- -Unsafe. It is not safe to call this inside any callback. - - -Remarks -------- -You only need to use this function if you want to configure the context differently to its defaults. You should never use this function if you want to manage -your own context. - -See the documentation for `ma_context_init()` for information on the different context configuration options. - - -See Also --------- -ma_device_init() -ma_device_uninit() -ma_device_config_init() -ma_context_init() -*/ -MA_API ma_result ma_device_init_ex(const ma_backend backends[], ma_uint32 backendCount, const ma_context_config* pContextConfig, const ma_device_config* pConfig, ma_device* pDevice); - -/* -Uninitializes a device. - -This will explicitly stop the device. You do not need to call `ma_device_stop()` beforehand, but it's harmless if you do. - - -Parameters ----------- -pDevice (in) - A pointer to the device to stop. - - -Return Value ------------- -Nothing - - -Thread Safety -------------- -Unsafe. As soon as this API is called the device should be considered undefined. - - -Callback Safety ---------------- -Unsafe. It is not safe to call this inside any callback. Doing this will result in a deadlock. - - -See Also --------- -ma_device_init() -ma_device_stop() -*/ -MA_API void ma_device_uninit(ma_device* pDevice); - - -/* -Retrieves a pointer to the context that owns the given device. -*/ -MA_API ma_context* ma_device_get_context(ma_device* pDevice); - -/* -Helper function for retrieving the log object associated with the context that owns this device. -*/ -MA_API ma_log* ma_device_get_log(ma_device* pDevice); - - -/* -Retrieves information about the device. - - -Parameters ----------- -pDevice (in) - A pointer to the device whose information is being retrieved. - -type (in) - The device type. This parameter is required for duplex devices. When retrieving device - information, you are doing so for an individual playback or capture device. - -pDeviceInfo (out) - A pointer to the `ma_device_info` that will receive the device information. - - -Return Value ------------- -MA_SUCCESS if successful; any other error code otherwise. - - -Thread Safety -------------- -Unsafe. This should be considered unsafe because it may be calling into the backend which may or -may not be safe. - - -Callback Safety ---------------- -Unsafe. You should avoid calling this in the data callback because it may call into the backend -which may or may not be safe. -*/ -MA_API ma_result ma_device_get_info(ma_device* pDevice, ma_device_type type, ma_device_info* pDeviceInfo); - - -/* -Retrieves the name of the device. - - -Parameters ----------- -pDevice (in) - A pointer to the device whose information is being retrieved. - -type (in) - The device type. This parameter is required for duplex devices. When retrieving device - information, you are doing so for an individual playback or capture device. - -pName (out) - A pointer to the buffer that will receive the name. - -nameCap (in) - The capacity of the output buffer, including space for the null terminator. - -pLengthNotIncludingNullTerminator (out, optional) - A pointer to the variable that will receive the length of the name, not including the null - terminator. - - -Return Value ------------- -MA_SUCCESS if successful; any other error code otherwise. - - -Thread Safety -------------- -Unsafe. This should be considered unsafe because it may be calling into the backend which may or -may not be safe. - - -Callback Safety ---------------- -Unsafe. You should avoid calling this in the data callback because it may call into the backend -which may or may not be safe. - - -Remarks -------- -If the name does not fully fit into the output buffer, it'll be truncated. You can pass in NULL to -`pName` if you want to first get the length of the name for the purpose of memory allocation of the -output buffer. Allocating a buffer of size `MA_MAX_DEVICE_NAME_LENGTH + 1` should be enough for -most cases and will avoid the need for the inefficiency of calling this function twice. - -This is implemented in terms of `ma_device_get_info()`. -*/ -MA_API ma_result ma_device_get_name(ma_device* pDevice, ma_device_type type, char* pName, size_t nameCap, size_t* pLengthNotIncludingNullTerminator); - - -/* -Starts the device. For playback devices this begins playback. For capture devices it begins recording. - -Use `ma_device_stop()` to stop the device. - - -Parameters ----------- -pDevice (in) - A pointer to the device to start. - - -Return Value ------------- -MA_SUCCESS if successful; any other error code otherwise. - - -Thread Safety -------------- -Safe. It's safe to call this from any thread with the exception of the callback thread. - - -Callback Safety ---------------- -Unsafe. It is not safe to call this inside any callback. - - -Remarks -------- -For a playback device, this will retrieve an initial chunk of audio data from the client before returning. The reason for this is to ensure there is valid -audio data in the buffer, which needs to be done before the device begins playback. - -This API waits until the backend device has been started for real by the worker thread. It also waits on a mutex for thread-safety. - -Do not call this in any callback. - - -See Also --------- -ma_device_stop() -*/ -MA_API ma_result ma_device_start(ma_device* pDevice); - -/* -Stops the device. For playback devices this stops playback. For capture devices it stops recording. - -Use `ma_device_start()` to start the device again. - - -Parameters ----------- -pDevice (in) - A pointer to the device to stop. - - -Return Value ------------- -MA_SUCCESS if successful; any other error code otherwise. - - -Thread Safety -------------- -Safe. It's safe to call this from any thread with the exception of the callback thread. - - -Callback Safety ---------------- -Unsafe. It is not safe to call this inside any callback. Doing this will result in a deadlock. - - -Remarks -------- -This API needs to wait on the worker thread to stop the backend device properly before returning. It also waits on a mutex for thread-safety. In addition, some -backends need to wait for the device to finish playback/recording of the current fragment which can take some time (usually proportionate to the buffer size -that was specified at initialization time). - -Backends are required to either pause the stream in-place or drain the buffer if pausing is not possible. The reason for this is that stopping the device and -the resuming it with ma_device_start() (which you might do when your program loses focus) may result in a situation where those samples are never output to the -speakers or received from the microphone which can in turn result in de-syncs. - -Do not call this in any callback. - - -See Also --------- -ma_device_start() -*/ -MA_API ma_result ma_device_stop(ma_device* pDevice); - -/* -Determines whether or not the device is started. - - -Parameters ----------- -pDevice (in) - A pointer to the device whose start state is being retrieved. - - -Return Value ------------- -True if the device is started, false otherwise. - - -Thread Safety -------------- -Safe. If another thread calls `ma_device_start()` or `ma_device_stop()` at this same time as this function is called, there's a very small chance the return -value will be out of sync. - - -Callback Safety ---------------- -Safe. This is implemented as a simple accessor. - - -See Also --------- -ma_device_start() -ma_device_stop() -*/ -MA_API ma_bool32 ma_device_is_started(const ma_device* pDevice); - - -/* -Retrieves the state of the device. - - -Parameters ----------- -pDevice (in) - A pointer to the device whose state is being retrieved. - - -Return Value ------------- -The current state of the device. The return value will be one of the following: - - +-------------------------------+------------------------------------------------------------------------------+ - | ma_device_state_uninitialized | Will only be returned if the device is in the middle of initialization. | - +-------------------------------+------------------------------------------------------------------------------+ - | ma_device_state_stopped | The device is stopped. The initial state of the device after initialization. | - +-------------------------------+------------------------------------------------------------------------------+ - | ma_device_state_started | The device started and requesting and/or delivering audio data. | - +-------------------------------+------------------------------------------------------------------------------+ - | ma_device_state_starting | The device is in the process of starting. | - +-------------------------------+------------------------------------------------------------------------------+ - | ma_device_state_stopping | The device is in the process of stopping. | - +-------------------------------+------------------------------------------------------------------------------+ - - -Thread Safety -------------- -Safe. This is implemented as a simple accessor. Note that if the device is started or stopped at the same time as this function is called, -there's a possibility the return value could be out of sync. See remarks. - - -Callback Safety ---------------- -Safe. This is implemented as a simple accessor. - - -Remarks -------- -The general flow of a devices state goes like this: - - ``` - ma_device_init() -> ma_device_state_uninitialized -> ma_device_state_stopped - ma_device_start() -> ma_device_state_starting -> ma_device_state_started - ma_device_stop() -> ma_device_state_stopping -> ma_device_state_stopped - ``` - -When the state of the device is changed with `ma_device_start()` or `ma_device_stop()` at this same time as this function is called, the -value returned by this function could potentially be out of sync. If this is significant to your program you need to implement your own -synchronization. -*/ -MA_API ma_device_state ma_device_get_state(const ma_device* pDevice); - - -/* -Performs post backend initialization routines for setting up internal data conversion. - -This should be called whenever the backend is initialized. The only time this should be called from -outside of miniaudio is if you're implementing a custom backend, and you would only do it if you -are reinitializing the backend due to rerouting or reinitializing for some reason. - - -Parameters ----------- -pDevice [in] - A pointer to the device. - -deviceType [in] - The type of the device that was just reinitialized. - -pPlaybackDescriptor [in] - The descriptor of the playback device containing the internal data format and buffer sizes. - -pPlaybackDescriptor [in] - The descriptor of the capture device containing the internal data format and buffer sizes. - - -Return Value ------------- -MA_SUCCESS if successful; any other error otherwise. - - -Thread Safety -------------- -Unsafe. This will be reinitializing internal data converters which may be in use by another thread. - - -Callback Safety ---------------- -Unsafe. This will be reinitializing internal data converters which may be in use by the callback. - - -Remarks -------- -For a duplex device, you can call this for only one side of the system. This is why the deviceType -is specified as a parameter rather than deriving it from the device. - -You do not need to call this manually unless you are doing a custom backend, in which case you need -only do it if you're manually performing rerouting or reinitialization. -*/ -MA_API ma_result ma_device_post_init(ma_device* pDevice, ma_device_type deviceType, const ma_device_descriptor* pPlaybackDescriptor, const ma_device_descriptor* pCaptureDescriptor); - - -/* -Sets the master volume factor for the device. - -The volume factor must be between 0 (silence) and 1 (full volume). Use `ma_device_set_master_volume_db()` to use decibel notation, where 0 is full volume and -values less than 0 decreases the volume. - - -Parameters ----------- -pDevice (in) - A pointer to the device whose volume is being set. - -volume (in) - The new volume factor. Must be >= 0. - - -Return Value ------------- -MA_SUCCESS if the volume was set successfully. -MA_INVALID_ARGS if pDevice is NULL. -MA_INVALID_ARGS if volume is negative. - - -Thread Safety -------------- -Safe. This just sets a local member of the device object. - - -Callback Safety ---------------- -Safe. If you set the volume in the data callback, that data written to the output buffer will have the new volume applied. - - -Remarks -------- -This applies the volume factor across all channels. - -This does not change the operating system's volume. It only affects the volume for the given `ma_device` object's audio stream. - - -See Also --------- -ma_device_get_master_volume() -ma_device_set_master_volume_db() -ma_device_get_master_volume_db() -*/ -MA_API ma_result ma_device_set_master_volume(ma_device* pDevice, float volume); - -/* -Retrieves the master volume factor for the device. - - -Parameters ----------- -pDevice (in) - A pointer to the device whose volume factor is being retrieved. - -pVolume (in) - A pointer to the variable that will receive the volume factor. The returned value will be in the range of [0, 1]. - - -Return Value ------------- -MA_SUCCESS if successful. -MA_INVALID_ARGS if pDevice is NULL. -MA_INVALID_ARGS if pVolume is NULL. - - -Thread Safety -------------- -Safe. This just a simple member retrieval. - - -Callback Safety ---------------- -Safe. - - -Remarks -------- -If an error occurs, `*pVolume` will be set to 0. - - -See Also --------- -ma_device_set_master_volume() -ma_device_set_master_volume_gain_db() -ma_device_get_master_volume_gain_db() -*/ -MA_API ma_result ma_device_get_master_volume(ma_device* pDevice, float* pVolume); - -/* -Sets the master volume for the device as gain in decibels. - -A gain of 0 is full volume, whereas a gain of < 0 will decrease the volume. - - -Parameters ----------- -pDevice (in) - A pointer to the device whose gain is being set. - -gainDB (in) - The new volume as gain in decibels. Must be less than or equal to 0, where 0 is full volume and anything less than 0 decreases the volume. - - -Return Value ------------- -MA_SUCCESS if the volume was set successfully. -MA_INVALID_ARGS if pDevice is NULL. -MA_INVALID_ARGS if the gain is > 0. - - -Thread Safety -------------- -Safe. This just sets a local member of the device object. - - -Callback Safety ---------------- -Safe. If you set the volume in the data callback, that data written to the output buffer will have the new volume applied. - - -Remarks -------- -This applies the gain across all channels. - -This does not change the operating system's volume. It only affects the volume for the given `ma_device` object's audio stream. - - -See Also --------- -ma_device_get_master_volume_gain_db() -ma_device_set_master_volume() -ma_device_get_master_volume() -*/ -MA_API ma_result ma_device_set_master_volume_db(ma_device* pDevice, float gainDB); - -/* -Retrieves the master gain in decibels. - - -Parameters ----------- -pDevice (in) - A pointer to the device whose gain is being retrieved. - -pGainDB (in) - A pointer to the variable that will receive the gain in decibels. The returned value will be <= 0. - - -Return Value ------------- -MA_SUCCESS if successful. -MA_INVALID_ARGS if pDevice is NULL. -MA_INVALID_ARGS if pGainDB is NULL. - - -Thread Safety -------------- -Safe. This just a simple member retrieval. - - -Callback Safety ---------------- -Safe. - - -Remarks -------- -If an error occurs, `*pGainDB` will be set to 0. - - -See Also --------- -ma_device_set_master_volume_db() -ma_device_set_master_volume() -ma_device_get_master_volume() -*/ -MA_API ma_result ma_device_get_master_volume_db(ma_device* pDevice, float* pGainDB); - - -/* -Called from the data callback of asynchronous backends to allow miniaudio to process the data and fire the miniaudio data callback. - - -Parameters ----------- -pDevice (in) - A pointer to device whose processing the data callback. - -pOutput (out) - A pointer to the buffer that will receive the output PCM frame data. On a playback device this must not be NULL. On a duplex device - this can be NULL, in which case pInput must not be NULL. - -pInput (in) - A pointer to the buffer containing input PCM frame data. On a capture device this must not be NULL. On a duplex device this can be - NULL, in which case `pOutput` must not be NULL. - -frameCount (in) - The number of frames being processed. - - -Return Value ------------- -MA_SUCCESS if successful; any other result code otherwise. - - -Thread Safety -------------- -This function should only ever be called from the internal data callback of the backend. It is safe to call this simultaneously between a -playback and capture device in duplex setups. - - -Callback Safety ---------------- -Do not call this from the miniaudio data callback. It should only ever be called from the internal data callback of the backend. - - -Remarks -------- -If both `pOutput` and `pInput` are NULL, and error will be returned. In duplex scenarios, both `pOutput` and `pInput` can be non-NULL, in -which case `pInput` will be processed first, followed by `pOutput`. - -If you are implementing a custom backend, and that backend uses a callback for data delivery, you'll need to call this from inside that -callback. -*/ -MA_API ma_result ma_device_handle_backend_data_callback(ma_device* pDevice, void* pOutput, const void* pInput, ma_uint32 frameCount); - - -/* -Calculates an appropriate buffer size from a descriptor, native sample rate and performance profile. - -This function is used by backends for helping determine an appropriately sized buffer to use with -the device depending on the values of `periodSizeInFrames` and `periodSizeInMilliseconds` in the -`pDescriptor` object. Since buffer size calculations based on time depends on the sample rate, a -best guess at the device's native sample rate is also required which is where `nativeSampleRate` -comes in. In addition, the performance profile is also needed for cases where both the period size -in frames and milliseconds are both zero. - - -Parameters ----------- -pDescriptor (in) - A pointer to device descriptor whose `periodSizeInFrames` and `periodSizeInMilliseconds` members - will be used for the calculation of the buffer size. - -nativeSampleRate (in) - The device's native sample rate. This is only ever used when the `periodSizeInFrames` member of - `pDescriptor` is zero. In this case, `periodSizeInMilliseconds` will be used instead, in which - case a sample rate is required to convert to a size in frames. - -performanceProfile (in) - When both the `periodSizeInFrames` and `periodSizeInMilliseconds` members of `pDescriptor` are - zero, miniaudio will fall back to a buffer size based on the performance profile. The profile - to use for this calculation is determine by this parameter. - - -Return Value ------------- -The calculated buffer size in frames. - - -Thread Safety -------------- -This is safe so long as nothing modifies `pDescriptor` at the same time. However, this function -should only ever be called from within the backend's device initialization routine and therefore -shouldn't have any multithreading concerns. - - -Callback Safety ---------------- -This is safe to call within the data callback, but there is no reason to ever do this. - - -Remarks -------- -If `nativeSampleRate` is zero, this function will fall back to `pDescriptor->sampleRate`. If that -is also zero, `MA_DEFAULT_SAMPLE_RATE` will be used instead. -*/ -MA_API ma_uint32 ma_calculate_buffer_size_in_frames_from_descriptor(const ma_device_descriptor* pDescriptor, ma_uint32 nativeSampleRate, ma_performance_profile performanceProfile); - - - -/* -Retrieves a friendly name for a backend. -*/ -MA_API const char* ma_get_backend_name(ma_backend backend); - -/* -Retrieves the backend enum from the given name. -*/ -MA_API ma_result ma_get_backend_from_name(const char* pBackendName, ma_backend* pBackend); - -/* -Determines whether or not the given backend is available by the compilation environment. -*/ -MA_API ma_bool32 ma_is_backend_enabled(ma_backend backend); - -/* -Retrieves compile-time enabled backends. - - -Parameters ----------- -pBackends (out, optional) - A pointer to the buffer that will receive the enabled backends. Set to NULL to retrieve the backend count. Setting - the capacity of the buffer to `MA_BUFFER_COUNT` will guarantee it's large enough for all backends. - -backendCap (in) - The capacity of the `pBackends` buffer. - -pBackendCount (out) - A pointer to the variable that will receive the enabled backend count. - - -Return Value ------------- -MA_SUCCESS if successful. -MA_INVALID_ARGS if `pBackendCount` is NULL. -MA_NO_SPACE if the capacity of `pBackends` is not large enough. - -If `MA_NO_SPACE` is returned, the `pBackends` buffer will be filled with `*pBackendCount` values. - - -Thread Safety -------------- -Safe. - - -Callback Safety ---------------- -Safe. - - -Remarks -------- -If you want to retrieve the number of backends so you can determine the capacity of `pBackends` buffer, you can call -this function with `pBackends` set to NULL. - -This will also enumerate the null backend. If you don't want to include this you need to check for `ma_backend_null` -when you enumerate over the returned backends and handle it appropriately. Alternatively, you can disable it at -compile time with `MA_NO_NULL`. - -The returned backends are determined based on compile time settings, not the platform it's currently running on. For -example, PulseAudio will be returned if it was enabled at compile time, even when the user doesn't actually have -PulseAudio installed. - - -Example 1 ---------- -The example below retrieves the enabled backend count using a fixed sized buffer allocated on the stack. The buffer is -given a capacity of `MA_BACKEND_COUNT` which will guarantee it'll be large enough to store all available backends. -Since `MA_BACKEND_COUNT` is always a relatively small value, this should be suitable for most scenarios. - -``` -ma_backend enabledBackends[MA_BACKEND_COUNT]; -size_t enabledBackendCount; - -result = ma_get_enabled_backends(enabledBackends, MA_BACKEND_COUNT, &enabledBackendCount); -if (result != MA_SUCCESS) { - // Failed to retrieve enabled backends. Should never happen in this example since all inputs are valid. -} -``` - - -See Also --------- -ma_is_backend_enabled() -*/ -MA_API ma_result ma_get_enabled_backends(ma_backend* pBackends, size_t backendCap, size_t* pBackendCount); - -/* -Determines whether or not loopback mode is support by a backend. -*/ -MA_API ma_bool32 ma_is_loopback_supported(ma_backend backend); - -#endif /* MA_NO_DEVICE_IO */ - - - -/************************************************************************************************************************************************************ - -Utilities - -************************************************************************************************************************************************************/ - -/* -Calculates a buffer size in milliseconds (rounded up) from the specified number of frames and sample rate. -*/ -MA_API ma_uint32 ma_calculate_buffer_size_in_milliseconds_from_frames(ma_uint32 bufferSizeInFrames, ma_uint32 sampleRate); - -/* -Calculates a buffer size in frames from the specified number of milliseconds and sample rate. -*/ -MA_API ma_uint32 ma_calculate_buffer_size_in_frames_from_milliseconds(ma_uint32 bufferSizeInMilliseconds, ma_uint32 sampleRate); - -/* -Copies PCM frames from one buffer to another. -*/ -MA_API void ma_copy_pcm_frames(void* dst, const void* src, ma_uint64 frameCount, ma_format format, ma_uint32 channels); - -/* -Copies silent frames into the given buffer. - -Remarks -------- -For all formats except `ma_format_u8`, the output buffer will be filled with 0. For `ma_format_u8` it will be filled with 128. The reason for this is that it -makes more sense for the purpose of mixing to initialize it to the center point. -*/ -MA_API void ma_silence_pcm_frames(void* p, ma_uint64 frameCount, ma_format format, ma_uint32 channels); - - -/* -Offsets a pointer by the specified number of PCM frames. -*/ -MA_API void* ma_offset_pcm_frames_ptr(void* p, ma_uint64 offsetInFrames, ma_format format, ma_uint32 channels); -MA_API const void* ma_offset_pcm_frames_const_ptr(const void* p, ma_uint64 offsetInFrames, ma_format format, ma_uint32 channels); -static MA_INLINE float* ma_offset_pcm_frames_ptr_f32(float* p, ma_uint64 offsetInFrames, ma_uint32 channels) { return (float*)ma_offset_pcm_frames_ptr((void*)p, offsetInFrames, ma_format_f32, channels); } -static MA_INLINE const float* ma_offset_pcm_frames_const_ptr_f32(const float* p, ma_uint64 offsetInFrames, ma_uint32 channels) { return (const float*)ma_offset_pcm_frames_const_ptr((const void*)p, offsetInFrames, ma_format_f32, channels); } - - -/* -Clips samples. -*/ -MA_API void ma_clip_samples_u8(ma_uint8* pDst, const ma_int16* pSrc, ma_uint64 count); -MA_API void ma_clip_samples_s16(ma_int16* pDst, const ma_int32* pSrc, ma_uint64 count); -MA_API void ma_clip_samples_s24(ma_uint8* pDst, const ma_int64* pSrc, ma_uint64 count); -MA_API void ma_clip_samples_s32(ma_int32* pDst, const ma_int64* pSrc, ma_uint64 count); -MA_API void ma_clip_samples_f32(float* pDst, const float* pSrc, ma_uint64 count); -MA_API void ma_clip_pcm_frames(void* pDst, const void* pSrc, ma_uint64 frameCount, ma_format format, ma_uint32 channels); - -/* -Helper for applying a volume factor to samples. - -Note that the source and destination buffers can be the same, in which case it'll perform the operation in-place. -*/ -MA_API void ma_copy_and_apply_volume_factor_u8(ma_uint8* pSamplesOut, const ma_uint8* pSamplesIn, ma_uint64 sampleCount, float factor); -MA_API void ma_copy_and_apply_volume_factor_s16(ma_int16* pSamplesOut, const ma_int16* pSamplesIn, ma_uint64 sampleCount, float factor); -MA_API void ma_copy_and_apply_volume_factor_s24(void* pSamplesOut, const void* pSamplesIn, ma_uint64 sampleCount, float factor); -MA_API void ma_copy_and_apply_volume_factor_s32(ma_int32* pSamplesOut, const ma_int32* pSamplesIn, ma_uint64 sampleCount, float factor); -MA_API void ma_copy_and_apply_volume_factor_f32(float* pSamplesOut, const float* pSamplesIn, ma_uint64 sampleCount, float factor); - -MA_API void ma_apply_volume_factor_u8(ma_uint8* pSamples, ma_uint64 sampleCount, float factor); -MA_API void ma_apply_volume_factor_s16(ma_int16* pSamples, ma_uint64 sampleCount, float factor); -MA_API void ma_apply_volume_factor_s24(void* pSamples, ma_uint64 sampleCount, float factor); -MA_API void ma_apply_volume_factor_s32(ma_int32* pSamples, ma_uint64 sampleCount, float factor); -MA_API void ma_apply_volume_factor_f32(float* pSamples, ma_uint64 sampleCount, float factor); - -MA_API void ma_copy_and_apply_volume_factor_pcm_frames_u8(ma_uint8* pFramesOut, const ma_uint8* pFramesIn, ma_uint64 frameCount, ma_uint32 channels, float factor); -MA_API void ma_copy_and_apply_volume_factor_pcm_frames_s16(ma_int16* pFramesOut, const ma_int16* pFramesIn, ma_uint64 frameCount, ma_uint32 channels, float factor); -MA_API void ma_copy_and_apply_volume_factor_pcm_frames_s24(void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount, ma_uint32 channels, float factor); -MA_API void ma_copy_and_apply_volume_factor_pcm_frames_s32(ma_int32* pFramesOut, const ma_int32* pFramesIn, ma_uint64 frameCount, ma_uint32 channels, float factor); -MA_API void ma_copy_and_apply_volume_factor_pcm_frames_f32(float* pFramesOut, const float* pFramesIn, ma_uint64 frameCount, ma_uint32 channels, float factor); -MA_API void ma_copy_and_apply_volume_factor_pcm_frames(void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount, ma_format format, ma_uint32 channels, float factor); - -MA_API void ma_apply_volume_factor_pcm_frames_u8(ma_uint8* pFrames, ma_uint64 frameCount, ma_uint32 channels, float factor); -MA_API void ma_apply_volume_factor_pcm_frames_s16(ma_int16* pFrames, ma_uint64 frameCount, ma_uint32 channels, float factor); -MA_API void ma_apply_volume_factor_pcm_frames_s24(void* pFrames, ma_uint64 frameCount, ma_uint32 channels, float factor); -MA_API void ma_apply_volume_factor_pcm_frames_s32(ma_int32* pFrames, ma_uint64 frameCount, ma_uint32 channels, float factor); -MA_API void ma_apply_volume_factor_pcm_frames_f32(float* pFrames, ma_uint64 frameCount, ma_uint32 channels, float factor); -MA_API void ma_apply_volume_factor_pcm_frames(void* pFrames, ma_uint64 frameCount, ma_format format, ma_uint32 channels, float factor); - -MA_API void ma_copy_and_apply_volume_factor_per_channel_f32(float* pFramesOut, const float* pFramesIn, ma_uint64 frameCount, ma_uint32 channels, float* pChannelGains); - - -MA_API void ma_copy_and_apply_volume_and_clip_samples_u8(ma_uint8* pDst, const ma_int16* pSrc, ma_uint64 count, float volume); -MA_API void ma_copy_and_apply_volume_and_clip_samples_s16(ma_int16* pDst, const ma_int32* pSrc, ma_uint64 count, float volume); -MA_API void ma_copy_and_apply_volume_and_clip_samples_s24(ma_uint8* pDst, const ma_int64* pSrc, ma_uint64 count, float volume); -MA_API void ma_copy_and_apply_volume_and_clip_samples_s32(ma_int32* pDst, const ma_int64* pSrc, ma_uint64 count, float volume); -MA_API void ma_copy_and_apply_volume_and_clip_samples_f32(float* pDst, const float* pSrc, ma_uint64 count, float volume); -MA_API void ma_copy_and_apply_volume_and_clip_pcm_frames(void* pDst, const void* pSrc, ma_uint64 frameCount, ma_format format, ma_uint32 channels, float volume); - - -/* -Helper for converting a linear factor to gain in decibels. -*/ -MA_API float ma_volume_linear_to_db(float factor); - -/* -Helper for converting gain in decibels to a linear factor. -*/ -MA_API float ma_volume_db_to_linear(float gain); - - -/* -Mixes the specified number of frames in floating point format with a volume factor. - -This will run on an optimized path when the volume is equal to 1. -*/ -MA_API ma_result ma_mix_pcm_frames_f32(float* pDst, const float* pSrc, ma_uint64 frameCount, ma_uint32 channels, float volume); - - - - -/************************************************************************************************************************************************************ - -VFS -=== - -The VFS object (virtual file system) is what's used to customize file access. This is useful in cases where stdio FILE* based APIs may not be entirely -appropriate for a given situation. - -************************************************************************************************************************************************************/ -typedef void ma_vfs; -typedef ma_handle ma_vfs_file; - -typedef enum -{ - MA_OPEN_MODE_READ = 0x00000001, - MA_OPEN_MODE_WRITE = 0x00000002 -} ma_open_mode_flags; - -typedef enum -{ - ma_seek_origin_start, - ma_seek_origin_current, - ma_seek_origin_end /* Not used by decoders. */ -} ma_seek_origin; - -typedef struct -{ - ma_uint64 sizeInBytes; -} ma_file_info; - -typedef struct -{ - ma_result (* onOpen) (ma_vfs* pVFS, const char* pFilePath, ma_uint32 openMode, ma_vfs_file* pFile); - ma_result (* onOpenW)(ma_vfs* pVFS, const wchar_t* pFilePath, ma_uint32 openMode, ma_vfs_file* pFile); - ma_result (* onClose)(ma_vfs* pVFS, ma_vfs_file file); - ma_result (* onRead) (ma_vfs* pVFS, ma_vfs_file file, void* pDst, size_t sizeInBytes, size_t* pBytesRead); - ma_result (* onWrite)(ma_vfs* pVFS, ma_vfs_file file, const void* pSrc, size_t sizeInBytes, size_t* pBytesWritten); - ma_result (* onSeek) (ma_vfs* pVFS, ma_vfs_file file, ma_int64 offset, ma_seek_origin origin); - ma_result (* onTell) (ma_vfs* pVFS, ma_vfs_file file, ma_int64* pCursor); - ma_result (* onInfo) (ma_vfs* pVFS, ma_vfs_file file, ma_file_info* pInfo); -} ma_vfs_callbacks; - -MA_API ma_result ma_vfs_open(ma_vfs* pVFS, const char* pFilePath, ma_uint32 openMode, ma_vfs_file* pFile); -MA_API ma_result ma_vfs_open_w(ma_vfs* pVFS, const wchar_t* pFilePath, ma_uint32 openMode, ma_vfs_file* pFile); -MA_API ma_result ma_vfs_close(ma_vfs* pVFS, ma_vfs_file file); -MA_API ma_result ma_vfs_read(ma_vfs* pVFS, ma_vfs_file file, void* pDst, size_t sizeInBytes, size_t* pBytesRead); -MA_API ma_result ma_vfs_write(ma_vfs* pVFS, ma_vfs_file file, const void* pSrc, size_t sizeInBytes, size_t* pBytesWritten); -MA_API ma_result ma_vfs_seek(ma_vfs* pVFS, ma_vfs_file file, ma_int64 offset, ma_seek_origin origin); -MA_API ma_result ma_vfs_tell(ma_vfs* pVFS, ma_vfs_file file, ma_int64* pCursor); -MA_API ma_result ma_vfs_info(ma_vfs* pVFS, ma_vfs_file file, ma_file_info* pInfo); -MA_API ma_result ma_vfs_open_and_read_file(ma_vfs* pVFS, const char* pFilePath, void** ppData, size_t* pSize, const ma_allocation_callbacks* pAllocationCallbacks); - -typedef struct -{ - ma_vfs_callbacks cb; - ma_allocation_callbacks allocationCallbacks; /* Only used for the wchar_t version of open() on non-Windows platforms. */ -} ma_default_vfs; - -MA_API ma_result ma_default_vfs_init(ma_default_vfs* pVFS, const ma_allocation_callbacks* pAllocationCallbacks); - - - -typedef ma_result (* ma_read_proc)(void* pUserData, void* pBufferOut, size_t bytesToRead, size_t* pBytesRead); -typedef ma_result (* ma_seek_proc)(void* pUserData, ma_int64 offset, ma_seek_origin origin); -typedef ma_result (* ma_tell_proc)(void* pUserData, ma_int64* pCursor); - - - -#if !defined(MA_NO_DECODING) || !defined(MA_NO_ENCODING) -typedef enum -{ - ma_encoding_format_unknown = 0, - ma_encoding_format_wav, - ma_encoding_format_flac, - ma_encoding_format_mp3, - ma_encoding_format_vorbis -} ma_encoding_format; -#endif - -/************************************************************************************************************************************************************ - -Decoding -======== - -Decoders are independent of the main device API. Decoding APIs can be called freely inside the device's data callback, but they are not thread safe unless -you do your own synchronization. - -************************************************************************************************************************************************************/ -#ifndef MA_NO_DECODING -typedef struct ma_decoder ma_decoder; - - -typedef struct -{ - ma_format preferredFormat; - ma_uint32 seekPointCount; /* Set to > 0 to generate a seektable if the decoding backend supports it. */ -} ma_decoding_backend_config; - -MA_API ma_decoding_backend_config ma_decoding_backend_config_init(ma_format preferredFormat, ma_uint32 seekPointCount); - - -typedef struct -{ - ma_result (* onInit )(void* pUserData, ma_read_proc onRead, ma_seek_proc onSeek, ma_tell_proc onTell, void* pReadSeekTellUserData, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_data_source** ppBackend); - ma_result (* onInitFile )(void* pUserData, const char* pFilePath, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_data_source** ppBackend); /* Optional. */ - ma_result (* onInitFileW )(void* pUserData, const wchar_t* pFilePath, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_data_source** ppBackend); /* Optional. */ - ma_result (* onInitMemory)(void* pUserData, const void* pData, size_t dataSize, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_data_source** ppBackend); /* Optional. */ - void (* onUninit )(void* pUserData, ma_data_source* pBackend, const ma_allocation_callbacks* pAllocationCallbacks); -} ma_decoding_backend_vtable; - - -typedef ma_result (* ma_decoder_read_proc)(ma_decoder* pDecoder, void* pBufferOut, size_t bytesToRead, size_t* pBytesRead); /* Returns the number of bytes read. */ -typedef ma_result (* ma_decoder_seek_proc)(ma_decoder* pDecoder, ma_int64 byteOffset, ma_seek_origin origin); -typedef ma_result (* ma_decoder_tell_proc)(ma_decoder* pDecoder, ma_int64* pCursor); - -typedef struct -{ - ma_format format; /* Set to 0 or ma_format_unknown to use the stream's internal format. */ - ma_uint32 channels; /* Set to 0 to use the stream's internal channels. */ - ma_uint32 sampleRate; /* Set to 0 to use the stream's internal sample rate. */ - ma_channel* pChannelMap; - ma_channel_mix_mode channelMixMode; - ma_dither_mode ditherMode; - ma_resampler_config resampling; - ma_allocation_callbacks allocationCallbacks; - ma_encoding_format encodingFormat; - ma_uint32 seekPointCount; /* When set to > 0, specifies the number of seek points to use for the generation of a seek table. Not all decoding backends support this. */ - ma_decoding_backend_vtable** ppCustomBackendVTables; - ma_uint32 customBackendCount; - void* pCustomBackendUserData; -} ma_decoder_config; - -struct ma_decoder -{ - ma_data_source_base ds; - ma_data_source* pBackend; /* The decoding backend we'll be pulling data from. */ - const ma_decoding_backend_vtable* pBackendVTable; /* The vtable for the decoding backend. This needs to be stored so we can access the onUninit() callback. */ - void* pBackendUserData; - ma_decoder_read_proc onRead; - ma_decoder_seek_proc onSeek; - ma_decoder_tell_proc onTell; - void* pUserData; - ma_uint64 readPointerInPCMFrames; /* In output sample rate. Used for keeping track of how many frames are available for decoding. */ - ma_format outputFormat; - ma_uint32 outputChannels; - ma_uint32 outputSampleRate; - ma_data_converter converter; /* Data conversion is achieved by running frames through this. */ - void* pInputCache; /* In input format. Can be null if it's not needed. */ - ma_uint64 inputCacheCap; /* The capacity of the input cache. */ - ma_uint64 inputCacheConsumed; /* The number of frames that have been consumed in the cache. Used for determining the next valid frame. */ - ma_uint64 inputCacheRemaining; /* The number of valid frames remaining in the cache. */ - ma_allocation_callbacks allocationCallbacks; - union - { - struct - { - ma_vfs* pVFS; - ma_vfs_file file; - } vfs; - struct - { - const ma_uint8* pData; - size_t dataSize; - size_t currentReadPos; - } memory; /* Only used for decoders that were opened against a block of memory. */ - } data; -}; - -MA_API ma_decoder_config ma_decoder_config_init(ma_format outputFormat, ma_uint32 outputChannels, ma_uint32 outputSampleRate); -MA_API ma_decoder_config ma_decoder_config_init_default(void); - -MA_API ma_result ma_decoder_init(ma_decoder_read_proc onRead, ma_decoder_seek_proc onSeek, void* pUserData, const ma_decoder_config* pConfig, ma_decoder* pDecoder); -MA_API ma_result ma_decoder_init_memory(const void* pData, size_t dataSize, const ma_decoder_config* pConfig, ma_decoder* pDecoder); -MA_API ma_result ma_decoder_init_vfs(ma_vfs* pVFS, const char* pFilePath, const ma_decoder_config* pConfig, ma_decoder* pDecoder); -MA_API ma_result ma_decoder_init_vfs_w(ma_vfs* pVFS, const wchar_t* pFilePath, const ma_decoder_config* pConfig, ma_decoder* pDecoder); -MA_API ma_result ma_decoder_init_file(const char* pFilePath, const ma_decoder_config* pConfig, ma_decoder* pDecoder); -MA_API ma_result ma_decoder_init_file_w(const wchar_t* pFilePath, const ma_decoder_config* pConfig, ma_decoder* pDecoder); - -/* -Uninitializes a decoder. -*/ -MA_API ma_result ma_decoder_uninit(ma_decoder* pDecoder); - -/* -Reads PCM frames from the given decoder. - -This is not thread safe without your own synchronization. -*/ -MA_API ma_result ma_decoder_read_pcm_frames(ma_decoder* pDecoder, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead); - -/* -Seeks to a PCM frame based on its absolute index. - -This is not thread safe without your own synchronization. -*/ -MA_API ma_result ma_decoder_seek_to_pcm_frame(ma_decoder* pDecoder, ma_uint64 frameIndex); - -/* -Retrieves the decoder's output data format. -*/ -MA_API ma_result ma_decoder_get_data_format(ma_decoder* pDecoder, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate, ma_channel* pChannelMap, size_t channelMapCap); - -/* -Retrieves the current position of the read cursor in PCM frames. -*/ -MA_API ma_result ma_decoder_get_cursor_in_pcm_frames(ma_decoder* pDecoder, ma_uint64* pCursor); - -/* -Retrieves the length of the decoder in PCM frames. - -Do not call this on streams of an undefined length, such as internet radio. - -If the length is unknown or an error occurs, 0 will be returned. - -This will always return 0 for Vorbis decoders. This is due to a limitation with stb_vorbis in push mode which is what miniaudio -uses internally. - -For MP3's, this will decode the entire file. Do not call this in time critical scenarios. - -This function is not thread safe without your own synchronization. -*/ -MA_API ma_result ma_decoder_get_length_in_pcm_frames(ma_decoder* pDecoder, ma_uint64* pLength); - -/* -Retrieves the number of frames that can be read before reaching the end. - -This calls `ma_decoder_get_length_in_pcm_frames()` so you need to be aware of the rules for that function, in -particular ensuring you do not call it on streams of an undefined length, such as internet radio. - -If the total length of the decoder cannot be retrieved, such as with Vorbis decoders, `MA_NOT_IMPLEMENTED` will be -returned. -*/ -MA_API ma_result ma_decoder_get_available_frames(ma_decoder* pDecoder, ma_uint64* pAvailableFrames); - -/* -Helper for opening and decoding a file into a heap allocated block of memory. Free the returned pointer with ma_free(). On input, -pConfig should be set to what you want. On output it will be set to what you got. -*/ -MA_API ma_result ma_decode_from_vfs(ma_vfs* pVFS, const char* pFilePath, ma_decoder_config* pConfig, ma_uint64* pFrameCountOut, void** ppPCMFramesOut); -MA_API ma_result ma_decode_file(const char* pFilePath, ma_decoder_config* pConfig, ma_uint64* pFrameCountOut, void** ppPCMFramesOut); -MA_API ma_result ma_decode_memory(const void* pData, size_t dataSize, ma_decoder_config* pConfig, ma_uint64* pFrameCountOut, void** ppPCMFramesOut); - -#endif /* MA_NO_DECODING */ - - -/************************************************************************************************************************************************************ - -Encoding -======== - -Encoders do not perform any format conversion for you. If your target format does not support the format, and error will be returned. - -************************************************************************************************************************************************************/ -#ifndef MA_NO_ENCODING -typedef struct ma_encoder ma_encoder; - -typedef ma_result (* ma_encoder_write_proc) (ma_encoder* pEncoder, const void* pBufferIn, size_t bytesToWrite, size_t* pBytesWritten); -typedef ma_result (* ma_encoder_seek_proc) (ma_encoder* pEncoder, ma_int64 offset, ma_seek_origin origin); -typedef ma_result (* ma_encoder_init_proc) (ma_encoder* pEncoder); -typedef void (* ma_encoder_uninit_proc) (ma_encoder* pEncoder); -typedef ma_result (* ma_encoder_write_pcm_frames_proc)(ma_encoder* pEncoder, const void* pFramesIn, ma_uint64 frameCount, ma_uint64* pFramesWritten); - -typedef struct -{ - ma_encoding_format encodingFormat; - ma_format format; - ma_uint32 channels; - ma_uint32 sampleRate; - ma_allocation_callbacks allocationCallbacks; -} ma_encoder_config; - -MA_API ma_encoder_config ma_encoder_config_init(ma_encoding_format encodingFormat, ma_format format, ma_uint32 channels, ma_uint32 sampleRate); - -struct ma_encoder -{ - ma_encoder_config config; - ma_encoder_write_proc onWrite; - ma_encoder_seek_proc onSeek; - ma_encoder_init_proc onInit; - ma_encoder_uninit_proc onUninit; - ma_encoder_write_pcm_frames_proc onWritePCMFrames; - void* pUserData; - void* pInternalEncoder; - union - { - struct - { - ma_vfs* pVFS; - ma_vfs_file file; - } vfs; - } data; -}; - -MA_API ma_result ma_encoder_init(ma_encoder_write_proc onWrite, ma_encoder_seek_proc onSeek, void* pUserData, const ma_encoder_config* pConfig, ma_encoder* pEncoder); -MA_API ma_result ma_encoder_init_vfs(ma_vfs* pVFS, const char* pFilePath, const ma_encoder_config* pConfig, ma_encoder* pEncoder); -MA_API ma_result ma_encoder_init_vfs_w(ma_vfs* pVFS, const wchar_t* pFilePath, const ma_encoder_config* pConfig, ma_encoder* pEncoder); -MA_API ma_result ma_encoder_init_file(const char* pFilePath, const ma_encoder_config* pConfig, ma_encoder* pEncoder); -MA_API ma_result ma_encoder_init_file_w(const wchar_t* pFilePath, const ma_encoder_config* pConfig, ma_encoder* pEncoder); -MA_API void ma_encoder_uninit(ma_encoder* pEncoder); -MA_API ma_result ma_encoder_write_pcm_frames(ma_encoder* pEncoder, const void* pFramesIn, ma_uint64 frameCount, ma_uint64* pFramesWritten); - -#endif /* MA_NO_ENCODING */ - - -/************************************************************************************************************************************************************ - -Generation - -************************************************************************************************************************************************************/ -#ifndef MA_NO_GENERATION -typedef enum -{ - ma_waveform_type_sine, - ma_waveform_type_square, - ma_waveform_type_triangle, - ma_waveform_type_sawtooth -} ma_waveform_type; - -typedef struct -{ - ma_format format; - ma_uint32 channels; - ma_uint32 sampleRate; - ma_waveform_type type; - double amplitude; - double frequency; -} ma_waveform_config; - -MA_API ma_waveform_config ma_waveform_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRate, ma_waveform_type type, double amplitude, double frequency); - -typedef struct -{ - ma_data_source_base ds; - ma_waveform_config config; - double advance; - double time; -} ma_waveform; - -MA_API ma_result ma_waveform_init(const ma_waveform_config* pConfig, ma_waveform* pWaveform); -MA_API void ma_waveform_uninit(ma_waveform* pWaveform); -MA_API ma_result ma_waveform_read_pcm_frames(ma_waveform* pWaveform, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead); -MA_API ma_result ma_waveform_seek_to_pcm_frame(ma_waveform* pWaveform, ma_uint64 frameIndex); -MA_API ma_result ma_waveform_set_amplitude(ma_waveform* pWaveform, double amplitude); -MA_API ma_result ma_waveform_set_frequency(ma_waveform* pWaveform, double frequency); -MA_API ma_result ma_waveform_set_type(ma_waveform* pWaveform, ma_waveform_type type); -MA_API ma_result ma_waveform_set_sample_rate(ma_waveform* pWaveform, ma_uint32 sampleRate); - -typedef struct -{ - ma_format format; - ma_uint32 channels; - ma_uint32 sampleRate; - double dutyCycle; - double amplitude; - double frequency; -} ma_pulsewave_config; - -MA_API ma_pulsewave_config ma_pulsewave_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRate, double dutyCycle, double amplitude, double frequency); - -typedef struct -{ - ma_waveform waveform; - ma_pulsewave_config config; -} ma_pulsewave; - -MA_API ma_result ma_pulsewave_init(const ma_pulsewave_config* pConfig, ma_pulsewave* pWaveform); -MA_API void ma_pulsewave_uninit(ma_pulsewave* pWaveform); -MA_API ma_result ma_pulsewave_read_pcm_frames(ma_pulsewave* pWaveform, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead); -MA_API ma_result ma_pulsewave_seek_to_pcm_frame(ma_pulsewave* pWaveform, ma_uint64 frameIndex); -MA_API ma_result ma_pulsewave_set_amplitude(ma_pulsewave* pWaveform, double amplitude); -MA_API ma_result ma_pulsewave_set_frequency(ma_pulsewave* pWaveform, double frequency); -MA_API ma_result ma_pulsewave_set_sample_rate(ma_pulsewave* pWaveform, ma_uint32 sampleRate); -MA_API ma_result ma_pulsewave_set_duty_cycle(ma_pulsewave* pWaveform, double dutyCycle); - -typedef enum -{ - ma_noise_type_white, - ma_noise_type_pink, - ma_noise_type_brownian -} ma_noise_type; - - -typedef struct -{ - ma_format format; - ma_uint32 channels; - ma_noise_type type; - ma_int32 seed; - double amplitude; - ma_bool32 duplicateChannels; -} ma_noise_config; - -MA_API ma_noise_config ma_noise_config_init(ma_format format, ma_uint32 channels, ma_noise_type type, ma_int32 seed, double amplitude); - -typedef struct -{ - ma_data_source_base ds; - ma_noise_config config; - ma_lcg lcg; - union - { - struct - { - double** bin; - double* accumulation; - ma_uint32* counter; - } pink; - struct - { - double* accumulation; - } brownian; - } state; - - /* Memory management. */ - void* _pHeap; - ma_bool32 _ownsHeap; -} ma_noise; - -MA_API ma_result ma_noise_get_heap_size(const ma_noise_config* pConfig, size_t* pHeapSizeInBytes); -MA_API ma_result ma_noise_init_preallocated(const ma_noise_config* pConfig, void* pHeap, ma_noise* pNoise); -MA_API ma_result ma_noise_init(const ma_noise_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_noise* pNoise); -MA_API void ma_noise_uninit(ma_noise* pNoise, const ma_allocation_callbacks* pAllocationCallbacks); -MA_API ma_result ma_noise_read_pcm_frames(ma_noise* pNoise, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead); -MA_API ma_result ma_noise_set_amplitude(ma_noise* pNoise, double amplitude); -MA_API ma_result ma_noise_set_seed(ma_noise* pNoise, ma_int32 seed); -MA_API ma_result ma_noise_set_type(ma_noise* pNoise, ma_noise_type type); - -#endif /* MA_NO_GENERATION */ - - - -/************************************************************************************************************************************************************ - -Resource Manager - -************************************************************************************************************************************************************/ -/* The resource manager cannot be enabled if there is no decoder. */ -#if !defined(MA_NO_RESOURCE_MANAGER) && defined(MA_NO_DECODING) -#define MA_NO_RESOURCE_MANAGER -#endif - -#ifndef MA_NO_RESOURCE_MANAGER -typedef struct ma_resource_manager ma_resource_manager; -typedef struct ma_resource_manager_data_buffer_node ma_resource_manager_data_buffer_node; -typedef struct ma_resource_manager_data_buffer ma_resource_manager_data_buffer; -typedef struct ma_resource_manager_data_stream ma_resource_manager_data_stream; -typedef struct ma_resource_manager_data_source ma_resource_manager_data_source; - -typedef enum -{ - MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_STREAM = 0x00000001, /* When set, does not load the entire data source in memory. Disk I/O will happen on job threads. */ - MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_DECODE = 0x00000002, /* Decode data before storing in memory. When set, decoding is done at the resource manager level rather than the mixing thread. Results in faster mixing, but higher memory usage. */ - MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_ASYNC = 0x00000004, /* When set, the resource manager will load the data source asynchronously. */ - MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_WAIT_INIT = 0x00000008, /* When set, waits for initialization of the underlying data source before returning from ma_resource_manager_data_source_init(). */ - MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_UNKNOWN_LENGTH = 0x00000010, /* Gives the resource manager a hint that the length of the data source is unknown and calling `ma_data_source_get_length_in_pcm_frames()` should be avoided. */ - MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_LOOPING = 0x00000020 /* When set, configures the data source to loop by default. */ -} ma_resource_manager_data_source_flags; - - -/* -Pipeline notifications used by the resource manager. Made up of both an async notification and a fence, both of which are optional. -*/ -typedef struct -{ - ma_async_notification* pNotification; - ma_fence* pFence; -} ma_resource_manager_pipeline_stage_notification; - -typedef struct -{ - ma_resource_manager_pipeline_stage_notification init; /* Initialization of the decoder. */ - ma_resource_manager_pipeline_stage_notification done; /* Decoding fully completed. */ -} ma_resource_manager_pipeline_notifications; - -MA_API ma_resource_manager_pipeline_notifications ma_resource_manager_pipeline_notifications_init(void); - - - -/* BEGIN BACKWARDS COMPATIBILITY */ -/* TODO: Remove this block in version 0.12. */ -#if 1 -#define ma_resource_manager_job ma_job -#define ma_resource_manager_job_init ma_job_init -#define MA_JOB_TYPE_RESOURCE_MANAGER_QUEUE_FLAG_NON_BLOCKING MA_JOB_QUEUE_FLAG_NON_BLOCKING -#define ma_resource_manager_job_queue_config ma_job_queue_config -#define ma_resource_manager_job_queue_config_init ma_job_queue_config_init -#define ma_resource_manager_job_queue ma_job_queue -#define ma_resource_manager_job_queue_get_heap_size ma_job_queue_get_heap_size -#define ma_resource_manager_job_queue_init_preallocated ma_job_queue_init_preallocated -#define ma_resource_manager_job_queue_init ma_job_queue_init -#define ma_resource_manager_job_queue_uninit ma_job_queue_uninit -#define ma_resource_manager_job_queue_post ma_job_queue_post -#define ma_resource_manager_job_queue_next ma_job_queue_next -#endif -/* END BACKWARDS COMPATIBILITY */ - - - - -/* Maximum job thread count will be restricted to this, but this may be removed later and replaced with a heap allocation thereby removing any limitation. */ -#ifndef MA_RESOURCE_MANAGER_MAX_JOB_THREAD_COUNT -#define MA_RESOURCE_MANAGER_MAX_JOB_THREAD_COUNT 64 -#endif - -typedef enum -{ - /* Indicates ma_resource_manager_next_job() should not block. Only valid when the job thread count is 0. */ - MA_RESOURCE_MANAGER_FLAG_NON_BLOCKING = 0x00000001, - - /* Disables any kind of multithreading. Implicitly enables MA_RESOURCE_MANAGER_FLAG_NON_BLOCKING. */ - MA_RESOURCE_MANAGER_FLAG_NO_THREADING = 0x00000002 -} ma_resource_manager_flags; - -typedef struct -{ - const char* pFilePath; - const wchar_t* pFilePathW; - const ma_resource_manager_pipeline_notifications* pNotifications; - ma_uint64 initialSeekPointInPCMFrames; - ma_uint64 rangeBegInPCMFrames; - ma_uint64 rangeEndInPCMFrames; - ma_uint64 loopPointBegInPCMFrames; - ma_uint64 loopPointEndInPCMFrames; - ma_uint32 flags; - ma_bool32 isLooping; /* Deprecated. Use the MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_LOOPING flag in `flags` instead. */ -} ma_resource_manager_data_source_config; - -MA_API ma_resource_manager_data_source_config ma_resource_manager_data_source_config_init(void); - - -typedef enum -{ - ma_resource_manager_data_supply_type_unknown = 0, /* Used for determining whether or the data supply has been initialized. */ - ma_resource_manager_data_supply_type_encoded, /* Data supply is an encoded buffer. Connector is ma_decoder. */ - ma_resource_manager_data_supply_type_decoded, /* Data supply is a decoded buffer. Connector is ma_audio_buffer. */ - ma_resource_manager_data_supply_type_decoded_paged /* Data supply is a linked list of decoded buffers. Connector is ma_paged_audio_buffer. */ -} ma_resource_manager_data_supply_type; - -typedef struct -{ - MA_ATOMIC(4, ma_resource_manager_data_supply_type) type; /* Read and written from different threads so needs to be accessed atomically. */ - union - { - struct - { - const void* pData; - size_t sizeInBytes; - } encoded; - struct - { - const void* pData; - ma_uint64 totalFrameCount; - ma_uint64 decodedFrameCount; - ma_format format; - ma_uint32 channels; - ma_uint32 sampleRate; - } decoded; - struct - { - ma_paged_audio_buffer_data data; - ma_uint64 decodedFrameCount; - ma_uint32 sampleRate; - } decodedPaged; - } backend; -} ma_resource_manager_data_supply; - -struct ma_resource_manager_data_buffer_node -{ - ma_uint32 hashedName32; /* The hashed name. This is the key. */ - ma_uint32 refCount; - MA_ATOMIC(4, ma_result) result; /* Result from asynchronous loading. When loading set to MA_BUSY. When fully loaded set to MA_SUCCESS. When deleting set to MA_UNAVAILABLE. */ - MA_ATOMIC(4, ma_uint32) executionCounter; /* For allocating execution orders for jobs. */ - MA_ATOMIC(4, ma_uint32) executionPointer; /* For managing the order of execution for asynchronous jobs relating to this object. Incremented as jobs complete processing. */ - ma_bool32 isDataOwnedByResourceManager; /* Set to true when the underlying data buffer was allocated the resource manager. Set to false if it is owned by the application (via ma_resource_manager_register_*()). */ - ma_resource_manager_data_supply data; - ma_resource_manager_data_buffer_node* pParent; - ma_resource_manager_data_buffer_node* pChildLo; - ma_resource_manager_data_buffer_node* pChildHi; -}; - -struct ma_resource_manager_data_buffer -{ - ma_data_source_base ds; /* Base data source. A data buffer is a data source. */ - ma_resource_manager* pResourceManager; /* A pointer to the resource manager that owns this buffer. */ - ma_resource_manager_data_buffer_node* pNode; /* The data node. This is reference counted and is what supplies the data. */ - ma_uint32 flags; /* The flags that were passed used to initialize the buffer. */ - MA_ATOMIC(4, ma_uint32) executionCounter; /* For allocating execution orders for jobs. */ - MA_ATOMIC(4, ma_uint32) executionPointer; /* For managing the order of execution for asynchronous jobs relating to this object. Incremented as jobs complete processing. */ - ma_uint64 seekTargetInPCMFrames; /* Only updated by the public API. Never written nor read from the job thread. */ - ma_bool32 seekToCursorOnNextRead; /* On the next read we need to seek to the frame cursor. */ - MA_ATOMIC(4, ma_result) result; /* Keeps track of a result of decoding. Set to MA_BUSY while the buffer is still loading. Set to MA_SUCCESS when loading is finished successfully. Otherwise set to some other code. */ - MA_ATOMIC(4, ma_bool32) isLooping; /* Can be read and written by different threads at the same time. Must be used atomically. */ - ma_atomic_bool32 isConnectorInitialized; /* Used for asynchronous loading to ensure we don't try to initialize the connector multiple times while waiting for the node to fully load. */ - union - { - ma_decoder decoder; /* Supply type is ma_resource_manager_data_supply_type_encoded */ - ma_audio_buffer buffer; /* Supply type is ma_resource_manager_data_supply_type_decoded */ - ma_paged_audio_buffer pagedBuffer; /* Supply type is ma_resource_manager_data_supply_type_decoded_paged */ - } connector; /* Connects this object to the node's data supply. */ -}; - -struct ma_resource_manager_data_stream -{ - ma_data_source_base ds; /* Base data source. A data stream is a data source. */ - ma_resource_manager* pResourceManager; /* A pointer to the resource manager that owns this data stream. */ - ma_uint32 flags; /* The flags that were passed used to initialize the stream. */ - ma_decoder decoder; /* Used for filling pages with data. This is only ever accessed by the job thread. The public API should never touch this. */ - ma_bool32 isDecoderInitialized; /* Required for determining whether or not the decoder should be uninitialized in MA_JOB_TYPE_RESOURCE_MANAGER_FREE_DATA_STREAM. */ - ma_uint64 totalLengthInPCMFrames; /* This is calculated when first loaded by the MA_JOB_TYPE_RESOURCE_MANAGER_LOAD_DATA_STREAM. */ - ma_uint32 relativeCursor; /* The playback cursor, relative to the current page. Only ever accessed by the public API. Never accessed by the job thread. */ - MA_ATOMIC(8, ma_uint64) absoluteCursor; /* The playback cursor, in absolute position starting from the start of the file. */ - ma_uint32 currentPageIndex; /* Toggles between 0 and 1. Index 0 is the first half of pPageData. Index 1 is the second half. Only ever accessed by the public API. Never accessed by the job thread. */ - MA_ATOMIC(4, ma_uint32) executionCounter; /* For allocating execution orders for jobs. */ - MA_ATOMIC(4, ma_uint32) executionPointer; /* For managing the order of execution for asynchronous jobs relating to this object. Incremented as jobs complete processing. */ - - /* Written by the public API, read by the job thread. */ - MA_ATOMIC(4, ma_bool32) isLooping; /* Whether or not the stream is looping. It's important to set the looping flag at the data stream level for smooth loop transitions. */ - - /* Written by the job thread, read by the public API. */ - void* pPageData; /* Buffer containing the decoded data of each page. Allocated once at initialization time. */ - MA_ATOMIC(4, ma_uint32) pageFrameCount[2]; /* The number of valid PCM frames in each page. Used to determine the last valid frame. */ - - /* Written and read by both the public API and the job thread. These must be atomic. */ - MA_ATOMIC(4, ma_result) result; /* Result from asynchronous loading. When loading set to MA_BUSY. When initialized set to MA_SUCCESS. When deleting set to MA_UNAVAILABLE. If an error occurs when loading, set to an error code. */ - MA_ATOMIC(4, ma_bool32) isDecoderAtEnd; /* Whether or not the decoder has reached the end. */ - MA_ATOMIC(4, ma_bool32) isPageValid[2]; /* Booleans to indicate whether or not a page is valid. Set to false by the public API, set to true by the job thread. Set to false as the pages are consumed, true when they are filled. */ - MA_ATOMIC(4, ma_bool32) seekCounter; /* When 0, no seeking is being performed. When > 0, a seek is being performed and reading should be delayed with MA_BUSY. */ -}; - -struct ma_resource_manager_data_source -{ - union - { - ma_resource_manager_data_buffer buffer; - ma_resource_manager_data_stream stream; - } backend; /* Must be the first item because we need the first item to be the data source callbacks for the buffer or stream. */ - - ma_uint32 flags; /* The flags that were passed in to ma_resource_manager_data_source_init(). */ - MA_ATOMIC(4, ma_uint32) executionCounter; /* For allocating execution orders for jobs. */ - MA_ATOMIC(4, ma_uint32) executionPointer; /* For managing the order of execution for asynchronous jobs relating to this object. Incremented as jobs complete processing. */ -}; - -typedef struct -{ - ma_allocation_callbacks allocationCallbacks; - ma_log* pLog; - ma_format decodedFormat; /* The decoded format to use. Set to ma_format_unknown (default) to use the file's native format. */ - ma_uint32 decodedChannels; /* The decoded channel count to use. Set to 0 (default) to use the file's native channel count. */ - ma_uint32 decodedSampleRate; /* the decoded sample rate to use. Set to 0 (default) to use the file's native sample rate. */ - ma_uint32 jobThreadCount; /* Set to 0 if you want to self-manage your job threads. Defaults to 1. */ - size_t jobThreadStackSize; - ma_uint32 jobQueueCapacity; /* The maximum number of jobs that can fit in the queue at a time. Defaults to MA_JOB_TYPE_RESOURCE_MANAGER_QUEUE_CAPACITY. Cannot be zero. */ - ma_uint32 flags; - ma_vfs* pVFS; /* Can be NULL in which case defaults will be used. */ - ma_decoding_backend_vtable** ppCustomDecodingBackendVTables; - ma_uint32 customDecodingBackendCount; - void* pCustomDecodingBackendUserData; -} ma_resource_manager_config; - -MA_API ma_resource_manager_config ma_resource_manager_config_init(void); - -struct ma_resource_manager -{ - ma_resource_manager_config config; - ma_resource_manager_data_buffer_node* pRootDataBufferNode; /* The root buffer in the binary tree. */ -#ifndef MA_NO_THREADING - ma_mutex dataBufferBSTLock; /* For synchronizing access to the data buffer binary tree. */ - ma_thread jobThreads[MA_RESOURCE_MANAGER_MAX_JOB_THREAD_COUNT]; /* The threads for executing jobs. */ -#endif - ma_job_queue jobQueue; /* Multi-consumer, multi-producer job queue for managing jobs for asynchronous decoding and streaming. */ - ma_default_vfs defaultVFS; /* Only used if a custom VFS is not specified. */ - ma_log log; /* Only used if no log was specified in the config. */ -}; - -/* Init. */ -MA_API ma_result ma_resource_manager_init(const ma_resource_manager_config* pConfig, ma_resource_manager* pResourceManager); -MA_API void ma_resource_manager_uninit(ma_resource_manager* pResourceManager); -MA_API ma_log* ma_resource_manager_get_log(ma_resource_manager* pResourceManager); - -/* Registration. */ -MA_API ma_result ma_resource_manager_register_file(ma_resource_manager* pResourceManager, const char* pFilePath, ma_uint32 flags); -MA_API ma_result ma_resource_manager_register_file_w(ma_resource_manager* pResourceManager, const wchar_t* pFilePath, ma_uint32 flags); -MA_API ma_result ma_resource_manager_register_decoded_data(ma_resource_manager* pResourceManager, const char* pName, const void* pData, ma_uint64 frameCount, ma_format format, ma_uint32 channels, ma_uint32 sampleRate); /* Does not copy. Increments the reference count if already exists and returns MA_SUCCESS. */ -MA_API ma_result ma_resource_manager_register_decoded_data_w(ma_resource_manager* pResourceManager, const wchar_t* pName, const void* pData, ma_uint64 frameCount, ma_format format, ma_uint32 channels, ma_uint32 sampleRate); -MA_API ma_result ma_resource_manager_register_encoded_data(ma_resource_manager* pResourceManager, const char* pName, const void* pData, size_t sizeInBytes); /* Does not copy. Increments the reference count if already exists and returns MA_SUCCESS. */ -MA_API ma_result ma_resource_manager_register_encoded_data_w(ma_resource_manager* pResourceManager, const wchar_t* pName, const void* pData, size_t sizeInBytes); -MA_API ma_result ma_resource_manager_unregister_file(ma_resource_manager* pResourceManager, const char* pFilePath); -MA_API ma_result ma_resource_manager_unregister_file_w(ma_resource_manager* pResourceManager, const wchar_t* pFilePath); -MA_API ma_result ma_resource_manager_unregister_data(ma_resource_manager* pResourceManager, const char* pName); -MA_API ma_result ma_resource_manager_unregister_data_w(ma_resource_manager* pResourceManager, const wchar_t* pName); - -/* Data Buffers. */ -MA_API ma_result ma_resource_manager_data_buffer_init_ex(ma_resource_manager* pResourceManager, const ma_resource_manager_data_source_config* pConfig, ma_resource_manager_data_buffer* pDataBuffer); -MA_API ma_result ma_resource_manager_data_buffer_init(ma_resource_manager* pResourceManager, const char* pFilePath, ma_uint32 flags, const ma_resource_manager_pipeline_notifications* pNotifications, ma_resource_manager_data_buffer* pDataBuffer); -MA_API ma_result ma_resource_manager_data_buffer_init_w(ma_resource_manager* pResourceManager, const wchar_t* pFilePath, ma_uint32 flags, const ma_resource_manager_pipeline_notifications* pNotifications, ma_resource_manager_data_buffer* pDataBuffer); -MA_API ma_result ma_resource_manager_data_buffer_init_copy(ma_resource_manager* pResourceManager, const ma_resource_manager_data_buffer* pExistingDataBuffer, ma_resource_manager_data_buffer* pDataBuffer); -MA_API ma_result ma_resource_manager_data_buffer_uninit(ma_resource_manager_data_buffer* pDataBuffer); -MA_API ma_result ma_resource_manager_data_buffer_read_pcm_frames(ma_resource_manager_data_buffer* pDataBuffer, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead); -MA_API ma_result ma_resource_manager_data_buffer_seek_to_pcm_frame(ma_resource_manager_data_buffer* pDataBuffer, ma_uint64 frameIndex); -MA_API ma_result ma_resource_manager_data_buffer_get_data_format(ma_resource_manager_data_buffer* pDataBuffer, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate, ma_channel* pChannelMap, size_t channelMapCap); -MA_API ma_result ma_resource_manager_data_buffer_get_cursor_in_pcm_frames(ma_resource_manager_data_buffer* pDataBuffer, ma_uint64* pCursor); -MA_API ma_result ma_resource_manager_data_buffer_get_length_in_pcm_frames(ma_resource_manager_data_buffer* pDataBuffer, ma_uint64* pLength); -MA_API ma_result ma_resource_manager_data_buffer_result(const ma_resource_manager_data_buffer* pDataBuffer); -MA_API ma_result ma_resource_manager_data_buffer_set_looping(ma_resource_manager_data_buffer* pDataBuffer, ma_bool32 isLooping); -MA_API ma_bool32 ma_resource_manager_data_buffer_is_looping(const ma_resource_manager_data_buffer* pDataBuffer); -MA_API ma_result ma_resource_manager_data_buffer_get_available_frames(ma_resource_manager_data_buffer* pDataBuffer, ma_uint64* pAvailableFrames); - -/* Data Streams. */ -MA_API ma_result ma_resource_manager_data_stream_init_ex(ma_resource_manager* pResourceManager, const ma_resource_manager_data_source_config* pConfig, ma_resource_manager_data_stream* pDataStream); -MA_API ma_result ma_resource_manager_data_stream_init(ma_resource_manager* pResourceManager, const char* pFilePath, ma_uint32 flags, const ma_resource_manager_pipeline_notifications* pNotifications, ma_resource_manager_data_stream* pDataStream); -MA_API ma_result ma_resource_manager_data_stream_init_w(ma_resource_manager* pResourceManager, const wchar_t* pFilePath, ma_uint32 flags, const ma_resource_manager_pipeline_notifications* pNotifications, ma_resource_manager_data_stream* pDataStream); -MA_API ma_result ma_resource_manager_data_stream_uninit(ma_resource_manager_data_stream* pDataStream); -MA_API ma_result ma_resource_manager_data_stream_read_pcm_frames(ma_resource_manager_data_stream* pDataStream, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead); -MA_API ma_result ma_resource_manager_data_stream_seek_to_pcm_frame(ma_resource_manager_data_stream* pDataStream, ma_uint64 frameIndex); -MA_API ma_result ma_resource_manager_data_stream_get_data_format(ma_resource_manager_data_stream* pDataStream, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate, ma_channel* pChannelMap, size_t channelMapCap); -MA_API ma_result ma_resource_manager_data_stream_get_cursor_in_pcm_frames(ma_resource_manager_data_stream* pDataStream, ma_uint64* pCursor); -MA_API ma_result ma_resource_manager_data_stream_get_length_in_pcm_frames(ma_resource_manager_data_stream* pDataStream, ma_uint64* pLength); -MA_API ma_result ma_resource_manager_data_stream_result(const ma_resource_manager_data_stream* pDataStream); -MA_API ma_result ma_resource_manager_data_stream_set_looping(ma_resource_manager_data_stream* pDataStream, ma_bool32 isLooping); -MA_API ma_bool32 ma_resource_manager_data_stream_is_looping(const ma_resource_manager_data_stream* pDataStream); -MA_API ma_result ma_resource_manager_data_stream_get_available_frames(ma_resource_manager_data_stream* pDataStream, ma_uint64* pAvailableFrames); - -/* Data Sources. */ -MA_API ma_result ma_resource_manager_data_source_init_ex(ma_resource_manager* pResourceManager, const ma_resource_manager_data_source_config* pConfig, ma_resource_manager_data_source* pDataSource); -MA_API ma_result ma_resource_manager_data_source_init(ma_resource_manager* pResourceManager, const char* pName, ma_uint32 flags, const ma_resource_manager_pipeline_notifications* pNotifications, ma_resource_manager_data_source* pDataSource); -MA_API ma_result ma_resource_manager_data_source_init_w(ma_resource_manager* pResourceManager, const wchar_t* pName, ma_uint32 flags, const ma_resource_manager_pipeline_notifications* pNotifications, ma_resource_manager_data_source* pDataSource); -MA_API ma_result ma_resource_manager_data_source_init_copy(ma_resource_manager* pResourceManager, const ma_resource_manager_data_source* pExistingDataSource, ma_resource_manager_data_source* pDataSource); -MA_API ma_result ma_resource_manager_data_source_uninit(ma_resource_manager_data_source* pDataSource); -MA_API ma_result ma_resource_manager_data_source_read_pcm_frames(ma_resource_manager_data_source* pDataSource, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead); -MA_API ma_result ma_resource_manager_data_source_seek_to_pcm_frame(ma_resource_manager_data_source* pDataSource, ma_uint64 frameIndex); -MA_API ma_result ma_resource_manager_data_source_get_data_format(ma_resource_manager_data_source* pDataSource, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate, ma_channel* pChannelMap, size_t channelMapCap); -MA_API ma_result ma_resource_manager_data_source_get_cursor_in_pcm_frames(ma_resource_manager_data_source* pDataSource, ma_uint64* pCursor); -MA_API ma_result ma_resource_manager_data_source_get_length_in_pcm_frames(ma_resource_manager_data_source* pDataSource, ma_uint64* pLength); -MA_API ma_result ma_resource_manager_data_source_result(const ma_resource_manager_data_source* pDataSource); -MA_API ma_result ma_resource_manager_data_source_set_looping(ma_resource_manager_data_source* pDataSource, ma_bool32 isLooping); -MA_API ma_bool32 ma_resource_manager_data_source_is_looping(const ma_resource_manager_data_source* pDataSource); -MA_API ma_result ma_resource_manager_data_source_get_available_frames(ma_resource_manager_data_source* pDataSource, ma_uint64* pAvailableFrames); - -/* Job management. */ -MA_API ma_result ma_resource_manager_post_job(ma_resource_manager* pResourceManager, const ma_job* pJob); -MA_API ma_result ma_resource_manager_post_job_quit(ma_resource_manager* pResourceManager); /* Helper for posting a quit job. */ -MA_API ma_result ma_resource_manager_next_job(ma_resource_manager* pResourceManager, ma_job* pJob); -MA_API ma_result ma_resource_manager_process_job(ma_resource_manager* pResourceManager, ma_job* pJob); /* DEPRECATED. Use ma_job_process(). Will be removed in version 0.12. */ -MA_API ma_result ma_resource_manager_process_next_job(ma_resource_manager* pResourceManager); /* Returns MA_CANCELLED if a MA_JOB_TYPE_QUIT job is found. In non-blocking mode, returns MA_NO_DATA_AVAILABLE if no jobs are available. */ -#endif /* MA_NO_RESOURCE_MANAGER */ - - - -/************************************************************************************************************************************************************ - -Node Graph - -************************************************************************************************************************************************************/ -#ifndef MA_NO_NODE_GRAPH -/* Must never exceed 254. */ -#ifndef MA_MAX_NODE_BUS_COUNT -#define MA_MAX_NODE_BUS_COUNT 254 -#endif - -/* Used internally by miniaudio for memory management. Must never exceed MA_MAX_NODE_BUS_COUNT. */ -#ifndef MA_MAX_NODE_LOCAL_BUS_COUNT -#define MA_MAX_NODE_LOCAL_BUS_COUNT 2 -#endif - -/* Use this when the bus count is determined by the node instance rather than the vtable. */ -#define MA_NODE_BUS_COUNT_UNKNOWN 255 - - -/* For some internal memory management of ma_node_graph. */ -typedef struct -{ - size_t offset; - size_t sizeInBytes; - unsigned char _data[1]; -} ma_stack; - - -typedef struct ma_node_graph ma_node_graph; -typedef void ma_node; - - -/* Node flags. */ -typedef enum -{ - MA_NODE_FLAG_PASSTHROUGH = 0x00000001, - MA_NODE_FLAG_CONTINUOUS_PROCESSING = 0x00000002, - MA_NODE_FLAG_ALLOW_NULL_INPUT = 0x00000004, - MA_NODE_FLAG_DIFFERENT_PROCESSING_RATES = 0x00000008, - MA_NODE_FLAG_SILENT_OUTPUT = 0x00000010 -} ma_node_flags; - - -/* The playback state of a node. Either started or stopped. */ -typedef enum -{ - ma_node_state_started = 0, - ma_node_state_stopped = 1 -} ma_node_state; - - -typedef struct -{ - /* - Extended processing callback. This callback is used for effects that process input and output - at different rates (i.e. they perform resampling). This is similar to the simple version, only - they take two separate frame counts: one for input, and one for output. - - On input, `pFrameCountOut` is equal to the capacity of the output buffer for each bus, whereas - `pFrameCountIn` will be equal to the number of PCM frames in each of the buffers in `ppFramesIn`. - - On output, set `pFrameCountOut` to the number of PCM frames that were actually output and set - `pFrameCountIn` to the number of input frames that were consumed. - */ - void (* onProcess)(ma_node* pNode, const float** ppFramesIn, ma_uint32* pFrameCountIn, float** ppFramesOut, ma_uint32* pFrameCountOut); - - /* - A callback for retrieving the number of input frames that are required to output the - specified number of output frames. You would only want to implement this when the node performs - resampling. This is optional, even for nodes that perform resampling, but it does offer a - small reduction in latency as it allows miniaudio to calculate the exact number of input frames - to read at a time instead of having to estimate. - */ - ma_result (* onGetRequiredInputFrameCount)(ma_node* pNode, ma_uint32 outputFrameCount, ma_uint32* pInputFrameCount); - - /* - The number of input buses. This is how many sub-buffers will be contained in the `ppFramesIn` - parameters of the callbacks above. - */ - ma_uint8 inputBusCount; - - /* - The number of output buses. This is how many sub-buffers will be contained in the `ppFramesOut` - parameters of the callbacks above. - */ - ma_uint8 outputBusCount; - - /* - Flags describing characteristics of the node. This is currently just a placeholder for some - ideas for later on. - */ - ma_uint32 flags; -} ma_node_vtable; - -typedef struct -{ - const ma_node_vtable* vtable; /* Should never be null. Initialization of the node will fail if so. */ - ma_node_state initialState; /* Defaults to ma_node_state_started. */ - ma_uint32 inputBusCount; /* Only used if the vtable specifies an input bus count of `MA_NODE_BUS_COUNT_UNKNOWN`, otherwise must be set to `MA_NODE_BUS_COUNT_UNKNOWN` (default). */ - ma_uint32 outputBusCount; /* Only used if the vtable specifies an output bus count of `MA_NODE_BUS_COUNT_UNKNOWN`, otherwise be set to `MA_NODE_BUS_COUNT_UNKNOWN` (default). */ - const ma_uint32* pInputChannels; /* The number of elements are determined by the input bus count as determined by the vtable, or `inputBusCount` if the vtable specifies `MA_NODE_BUS_COUNT_UNKNOWN`. */ - const ma_uint32* pOutputChannels; /* The number of elements are determined by the output bus count as determined by the vtable, or `outputBusCount` if the vtable specifies `MA_NODE_BUS_COUNT_UNKNOWN`. */ -} ma_node_config; - -MA_API ma_node_config ma_node_config_init(void); - - -/* -A node has multiple output buses. An output bus is attached to an input bus as an item in a linked -list. Think of the input bus as a linked list, with the output bus being an item in that list. -*/ -typedef struct ma_node_output_bus ma_node_output_bus; -struct ma_node_output_bus -{ - /* Immutable. */ - ma_node* pNode; /* The node that owns this output bus. The input node. Will be null for dummy head and tail nodes. */ - ma_uint8 outputBusIndex; /* The index of the output bus on pNode that this output bus represents. */ - ma_uint8 channels; /* The number of channels in the audio stream for this bus. */ - - /* Mutable via multiple threads. Must be used atomically. The weird ordering here is for packing reasons. */ - ma_uint8 inputNodeInputBusIndex; /* The index of the input bus on the input. Required for detaching. Will only be used within the spinlock so does not need to be atomic. */ - MA_ATOMIC(4, ma_uint32) flags; /* Some state flags for tracking the read state of the output buffer. A combination of MA_NODE_OUTPUT_BUS_FLAG_*. */ - MA_ATOMIC(4, ma_uint32) refCount; /* Reference count for some thread-safety when detaching. */ - MA_ATOMIC(4, ma_bool32) isAttached; /* This is used to prevent iteration of nodes that are in the middle of being detached. Used for thread safety. */ - MA_ATOMIC(4, ma_spinlock) lock; /* Unfortunate lock, but significantly simplifies the implementation. Required for thread-safe attaching and detaching. */ - MA_ATOMIC(4, float) volume; /* Linear. */ - MA_ATOMIC(MA_SIZEOF_PTR, ma_node_output_bus*) pNext; /* If null, it's the tail node or detached. */ - MA_ATOMIC(MA_SIZEOF_PTR, ma_node_output_bus*) pPrev; /* If null, it's the head node or detached. */ - MA_ATOMIC(MA_SIZEOF_PTR, ma_node*) pInputNode; /* The node that this output bus is attached to. Required for detaching. */ -}; - -/* -A node has multiple input buses. The output buses of a node are connecting to the input busses of -another. An input bus is essentially just a linked list of output buses. -*/ -typedef struct ma_node_input_bus ma_node_input_bus; -struct ma_node_input_bus -{ - /* Mutable via multiple threads. */ - ma_node_output_bus head; /* Dummy head node for simplifying some lock-free thread-safety stuff. */ - MA_ATOMIC(4, ma_uint32) nextCounter; /* This is used to determine whether or not the input bus is finding the next node in the list. Used for thread safety when detaching output buses. */ - MA_ATOMIC(4, ma_spinlock) lock; /* Unfortunate lock, but significantly simplifies the implementation. Required for thread-safe attaching and detaching. */ - - /* Set once at startup. */ - ma_uint8 channels; /* The number of channels in the audio stream for this bus. */ -}; - - -typedef struct ma_node_base ma_node_base; -struct ma_node_base -{ - /* These variables are set once at startup. */ - ma_node_graph* pNodeGraph; /* The graph this node belongs to. */ - const ma_node_vtable* vtable; - ma_uint32 inputBusCount; - ma_uint32 outputBusCount; - ma_node_input_bus* pInputBuses; - ma_node_output_bus* pOutputBuses; - float* pCachedData; /* Allocated on the heap. Fixed size. Needs to be stored on the heap because reading from output buses is done in separate function calls. */ - ma_uint16 cachedDataCapInFramesPerBus; /* The capacity of the input data cache in frames, per bus. */ - - /* These variables are read and written only from the audio thread. */ - ma_uint16 cachedFrameCountOut; - ma_uint16 cachedFrameCountIn; - ma_uint16 consumedFrameCountIn; - - /* These variables are read and written between different threads. */ - MA_ATOMIC(4, ma_node_state) state; /* When set to stopped, nothing will be read, regardless of the times in stateTimes. */ - MA_ATOMIC(8, ma_uint64) stateTimes[2]; /* Indexed by ma_node_state. Specifies the time based on the global clock that a node should be considered to be in the relevant state. */ - MA_ATOMIC(8, ma_uint64) localTime; /* The node's local clock. This is just a running sum of the number of output frames that have been processed. Can be modified by any thread with `ma_node_set_time()`. */ - - /* Memory management. */ - ma_node_input_bus _inputBuses[MA_MAX_NODE_LOCAL_BUS_COUNT]; - ma_node_output_bus _outputBuses[MA_MAX_NODE_LOCAL_BUS_COUNT]; - void* _pHeap; /* A heap allocation for internal use only. pInputBuses and/or pOutputBuses will point to this if the bus count exceeds MA_MAX_NODE_LOCAL_BUS_COUNT. */ - ma_bool32 _ownsHeap; /* If set to true, the node owns the heap allocation and _pHeap will be freed in ma_node_uninit(). */ -}; - -MA_API ma_result ma_node_get_heap_size(ma_node_graph* pNodeGraph, const ma_node_config* pConfig, size_t* pHeapSizeInBytes); -MA_API ma_result ma_node_init_preallocated(ma_node_graph* pNodeGraph, const ma_node_config* pConfig, void* pHeap, ma_node* pNode); -MA_API ma_result ma_node_init(ma_node_graph* pNodeGraph, const ma_node_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_node* pNode); -MA_API void ma_node_uninit(ma_node* pNode, const ma_allocation_callbacks* pAllocationCallbacks); -MA_API ma_node_graph* ma_node_get_node_graph(const ma_node* pNode); -MA_API ma_uint32 ma_node_get_input_bus_count(const ma_node* pNode); -MA_API ma_uint32 ma_node_get_output_bus_count(const ma_node* pNode); -MA_API ma_uint32 ma_node_get_input_channels(const ma_node* pNode, ma_uint32 inputBusIndex); -MA_API ma_uint32 ma_node_get_output_channels(const ma_node* pNode, ma_uint32 outputBusIndex); -MA_API ma_result ma_node_attach_output_bus(ma_node* pNode, ma_uint32 outputBusIndex, ma_node* pOtherNode, ma_uint32 otherNodeInputBusIndex); -MA_API ma_result ma_node_detach_output_bus(ma_node* pNode, ma_uint32 outputBusIndex); -MA_API ma_result ma_node_detach_all_output_buses(ma_node* pNode); -MA_API ma_result ma_node_set_output_bus_volume(ma_node* pNode, ma_uint32 outputBusIndex, float volume); -MA_API float ma_node_get_output_bus_volume(const ma_node* pNode, ma_uint32 outputBusIndex); -MA_API ma_result ma_node_set_state(ma_node* pNode, ma_node_state state); -MA_API ma_node_state ma_node_get_state(const ma_node* pNode); -MA_API ma_result ma_node_set_state_time(ma_node* pNode, ma_node_state state, ma_uint64 globalTime); -MA_API ma_uint64 ma_node_get_state_time(const ma_node* pNode, ma_node_state state); -MA_API ma_node_state ma_node_get_state_by_time(const ma_node* pNode, ma_uint64 globalTime); -MA_API ma_node_state ma_node_get_state_by_time_range(const ma_node* pNode, ma_uint64 globalTimeBeg, ma_uint64 globalTimeEnd); -MA_API ma_uint64 ma_node_get_time(const ma_node* pNode); -MA_API ma_result ma_node_set_time(ma_node* pNode, ma_uint64 localTime); - - -typedef struct -{ - ma_uint32 channels; - ma_uint32 processingSizeInFrames; /* This is the preferred processing size for node processing callbacks unless overridden by a node itself. Can be 0 in which case it will be based on the frame count passed into ma_node_graph_read_pcm_frames(), but will not be well defined. */ - size_t preMixStackSizeInBytes; /* Defaults to 512KB per channel. Reducing this will save memory, but the depth of your node graph will be more restricted. */ -} ma_node_graph_config; - -MA_API ma_node_graph_config ma_node_graph_config_init(ma_uint32 channels); - - -struct ma_node_graph -{ - /* Immutable. */ - ma_node_base base; /* The node graph itself is a node so it can be connected as an input to different node graph. This has zero inputs and calls ma_node_graph_read_pcm_frames() to generate it's output. */ - ma_node_base endpoint; /* Special node that all nodes eventually connect to. Data is read from this node in ma_node_graph_read_pcm_frames(). */ - float* pProcessingCache; /* This will be allocated when processingSizeInFrames is non-zero. This is needed because ma_node_graph_read_pcm_frames() can be called with a variable number of frames, and we may need to do some buffering in situations where the caller requests a frame count that's not a multiple of processingSizeInFrames. */ - ma_uint32 processingCacheFramesRemaining; - ma_uint32 processingSizeInFrames; - - /* Read and written by multiple threads. */ - MA_ATOMIC(4, ma_bool32) isReading; - - /* Modified only by the audio thread. */ - ma_stack* pPreMixStack; -}; - -MA_API ma_result ma_node_graph_init(const ma_node_graph_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_node_graph* pNodeGraph); -MA_API void ma_node_graph_uninit(ma_node_graph* pNodeGraph, const ma_allocation_callbacks* pAllocationCallbacks); -MA_API ma_node* ma_node_graph_get_endpoint(ma_node_graph* pNodeGraph); -MA_API ma_result ma_node_graph_read_pcm_frames(ma_node_graph* pNodeGraph, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead); -MA_API ma_uint32 ma_node_graph_get_channels(const ma_node_graph* pNodeGraph); -MA_API ma_uint64 ma_node_graph_get_time(const ma_node_graph* pNodeGraph); -MA_API ma_result ma_node_graph_set_time(ma_node_graph* pNodeGraph, ma_uint64 globalTime); - - - -/* Data source node. 0 input buses, 1 output bus. Used for reading from a data source. */ -typedef struct -{ - ma_node_config nodeConfig; - ma_data_source* pDataSource; -} ma_data_source_node_config; - -MA_API ma_data_source_node_config ma_data_source_node_config_init(ma_data_source* pDataSource); - - -typedef struct -{ - ma_node_base base; - ma_data_source* pDataSource; -} ma_data_source_node; - -MA_API ma_result ma_data_source_node_init(ma_node_graph* pNodeGraph, const ma_data_source_node_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_data_source_node* pDataSourceNode); -MA_API void ma_data_source_node_uninit(ma_data_source_node* pDataSourceNode, const ma_allocation_callbacks* pAllocationCallbacks); -MA_API ma_result ma_data_source_node_set_looping(ma_data_source_node* pDataSourceNode, ma_bool32 isLooping); -MA_API ma_bool32 ma_data_source_node_is_looping(ma_data_source_node* pDataSourceNode); - - -/* Splitter Node. 1 input, many outputs. Used for splitting/copying a stream so it can be as input into two separate output nodes. */ -typedef struct -{ - ma_node_config nodeConfig; - ma_uint32 channels; - ma_uint32 outputBusCount; -} ma_splitter_node_config; - -MA_API ma_splitter_node_config ma_splitter_node_config_init(ma_uint32 channels); - - -typedef struct -{ - ma_node_base base; -} ma_splitter_node; - -MA_API ma_result ma_splitter_node_init(ma_node_graph* pNodeGraph, const ma_splitter_node_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_splitter_node* pSplitterNode); -MA_API void ma_splitter_node_uninit(ma_splitter_node* pSplitterNode, const ma_allocation_callbacks* pAllocationCallbacks); - - -/* -Biquad Node -*/ -typedef struct -{ - ma_node_config nodeConfig; - ma_biquad_config biquad; -} ma_biquad_node_config; - -MA_API ma_biquad_node_config ma_biquad_node_config_init(ma_uint32 channels, float b0, float b1, float b2, float a0, float a1, float a2); - - -typedef struct -{ - ma_node_base baseNode; - ma_biquad biquad; -} ma_biquad_node; - -MA_API ma_result ma_biquad_node_init(ma_node_graph* pNodeGraph, const ma_biquad_node_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_biquad_node* pNode); -MA_API ma_result ma_biquad_node_reinit(const ma_biquad_config* pConfig, ma_biquad_node* pNode); -MA_API void ma_biquad_node_uninit(ma_biquad_node* pNode, const ma_allocation_callbacks* pAllocationCallbacks); - - -/* -Low Pass Filter Node -*/ -typedef struct -{ - ma_node_config nodeConfig; - ma_lpf_config lpf; -} ma_lpf_node_config; - -MA_API ma_lpf_node_config ma_lpf_node_config_init(ma_uint32 channels, ma_uint32 sampleRate, double cutoffFrequency, ma_uint32 order); - - -typedef struct -{ - ma_node_base baseNode; - ma_lpf lpf; -} ma_lpf_node; - -MA_API ma_result ma_lpf_node_init(ma_node_graph* pNodeGraph, const ma_lpf_node_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_lpf_node* pNode); -MA_API ma_result ma_lpf_node_reinit(const ma_lpf_config* pConfig, ma_lpf_node* pNode); -MA_API void ma_lpf_node_uninit(ma_lpf_node* pNode, const ma_allocation_callbacks* pAllocationCallbacks); - - -/* -High Pass Filter Node -*/ -typedef struct -{ - ma_node_config nodeConfig; - ma_hpf_config hpf; -} ma_hpf_node_config; - -MA_API ma_hpf_node_config ma_hpf_node_config_init(ma_uint32 channels, ma_uint32 sampleRate, double cutoffFrequency, ma_uint32 order); - - -typedef struct -{ - ma_node_base baseNode; - ma_hpf hpf; -} ma_hpf_node; - -MA_API ma_result ma_hpf_node_init(ma_node_graph* pNodeGraph, const ma_hpf_node_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_hpf_node* pNode); -MA_API ma_result ma_hpf_node_reinit(const ma_hpf_config* pConfig, ma_hpf_node* pNode); -MA_API void ma_hpf_node_uninit(ma_hpf_node* pNode, const ma_allocation_callbacks* pAllocationCallbacks); - - -/* -Band Pass Filter Node -*/ -typedef struct -{ - ma_node_config nodeConfig; - ma_bpf_config bpf; -} ma_bpf_node_config; - -MA_API ma_bpf_node_config ma_bpf_node_config_init(ma_uint32 channels, ma_uint32 sampleRate, double cutoffFrequency, ma_uint32 order); - - -typedef struct -{ - ma_node_base baseNode; - ma_bpf bpf; -} ma_bpf_node; - -MA_API ma_result ma_bpf_node_init(ma_node_graph* pNodeGraph, const ma_bpf_node_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_bpf_node* pNode); -MA_API ma_result ma_bpf_node_reinit(const ma_bpf_config* pConfig, ma_bpf_node* pNode); -MA_API void ma_bpf_node_uninit(ma_bpf_node* pNode, const ma_allocation_callbacks* pAllocationCallbacks); - - -/* -Notching Filter Node -*/ -typedef struct -{ - ma_node_config nodeConfig; - ma_notch_config notch; -} ma_notch_node_config; - -MA_API ma_notch_node_config ma_notch_node_config_init(ma_uint32 channels, ma_uint32 sampleRate, double q, double frequency); - - -typedef struct -{ - ma_node_base baseNode; - ma_notch2 notch; -} ma_notch_node; - -MA_API ma_result ma_notch_node_init(ma_node_graph* pNodeGraph, const ma_notch_node_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_notch_node* pNode); -MA_API ma_result ma_notch_node_reinit(const ma_notch_config* pConfig, ma_notch_node* pNode); -MA_API void ma_notch_node_uninit(ma_notch_node* pNode, const ma_allocation_callbacks* pAllocationCallbacks); - - -/* -Peaking Filter Node -*/ -typedef struct -{ - ma_node_config nodeConfig; - ma_peak_config peak; -} ma_peak_node_config; - -MA_API ma_peak_node_config ma_peak_node_config_init(ma_uint32 channels, ma_uint32 sampleRate, double gainDB, double q, double frequency); - - -typedef struct -{ - ma_node_base baseNode; - ma_peak2 peak; -} ma_peak_node; - -MA_API ma_result ma_peak_node_init(ma_node_graph* pNodeGraph, const ma_peak_node_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_peak_node* pNode); -MA_API ma_result ma_peak_node_reinit(const ma_peak_config* pConfig, ma_peak_node* pNode); -MA_API void ma_peak_node_uninit(ma_peak_node* pNode, const ma_allocation_callbacks* pAllocationCallbacks); - - -/* -Low Shelf Filter Node -*/ -typedef struct -{ - ma_node_config nodeConfig; - ma_loshelf_config loshelf; -} ma_loshelf_node_config; - -MA_API ma_loshelf_node_config ma_loshelf_node_config_init(ma_uint32 channels, ma_uint32 sampleRate, double gainDB, double q, double frequency); - - -typedef struct -{ - ma_node_base baseNode; - ma_loshelf2 loshelf; -} ma_loshelf_node; - -MA_API ma_result ma_loshelf_node_init(ma_node_graph* pNodeGraph, const ma_loshelf_node_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_loshelf_node* pNode); -MA_API ma_result ma_loshelf_node_reinit(const ma_loshelf_config* pConfig, ma_loshelf_node* pNode); -MA_API void ma_loshelf_node_uninit(ma_loshelf_node* pNode, const ma_allocation_callbacks* pAllocationCallbacks); - - -/* -High Shelf Filter Node -*/ -typedef struct -{ - ma_node_config nodeConfig; - ma_hishelf_config hishelf; -} ma_hishelf_node_config; - -MA_API ma_hishelf_node_config ma_hishelf_node_config_init(ma_uint32 channels, ma_uint32 sampleRate, double gainDB, double q, double frequency); - - -typedef struct -{ - ma_node_base baseNode; - ma_hishelf2 hishelf; -} ma_hishelf_node; - -MA_API ma_result ma_hishelf_node_init(ma_node_graph* pNodeGraph, const ma_hishelf_node_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_hishelf_node* pNode); -MA_API ma_result ma_hishelf_node_reinit(const ma_hishelf_config* pConfig, ma_hishelf_node* pNode); -MA_API void ma_hishelf_node_uninit(ma_hishelf_node* pNode, const ma_allocation_callbacks* pAllocationCallbacks); - - -typedef struct -{ - ma_node_config nodeConfig; - ma_delay_config delay; -} ma_delay_node_config; - -MA_API ma_delay_node_config ma_delay_node_config_init(ma_uint32 channels, ma_uint32 sampleRate, ma_uint32 delayInFrames, float decay); - - -typedef struct -{ - ma_node_base baseNode; - ma_delay delay; -} ma_delay_node; - -MA_API ma_result ma_delay_node_init(ma_node_graph* pNodeGraph, const ma_delay_node_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_delay_node* pDelayNode); -MA_API void ma_delay_node_uninit(ma_delay_node* pDelayNode, const ma_allocation_callbacks* pAllocationCallbacks); -MA_API void ma_delay_node_set_wet(ma_delay_node* pDelayNode, float value); -MA_API float ma_delay_node_get_wet(const ma_delay_node* pDelayNode); -MA_API void ma_delay_node_set_dry(ma_delay_node* pDelayNode, float value); -MA_API float ma_delay_node_get_dry(const ma_delay_node* pDelayNode); -MA_API void ma_delay_node_set_decay(ma_delay_node* pDelayNode, float value); -MA_API float ma_delay_node_get_decay(const ma_delay_node* pDelayNode); -#endif /* MA_NO_NODE_GRAPH */ - - -/* SECTION: miniaudio_engine.h */ -/************************************************************************************************************************************************************ - -Engine - -************************************************************************************************************************************************************/ -#if !defined(MA_NO_ENGINE) && !defined(MA_NO_NODE_GRAPH) -typedef struct ma_engine ma_engine; -typedef struct ma_sound ma_sound; - - -/* Sound flags. */ -typedef enum -{ - /* Resource manager flags. */ - MA_SOUND_FLAG_STREAM = 0x00000001, /* MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_STREAM */ - MA_SOUND_FLAG_DECODE = 0x00000002, /* MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_DECODE */ - MA_SOUND_FLAG_ASYNC = 0x00000004, /* MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_ASYNC */ - MA_SOUND_FLAG_WAIT_INIT = 0x00000008, /* MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_WAIT_INIT */ - MA_SOUND_FLAG_UNKNOWN_LENGTH = 0x00000010, /* MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_UNKNOWN_LENGTH */ - MA_SOUND_FLAG_LOOPING = 0x00000020, /* MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_LOOPING */ - - /* ma_sound specific flags. */ - MA_SOUND_FLAG_NO_DEFAULT_ATTACHMENT = 0x00001000, /* Do not attach to the endpoint by default. Useful for when setting up nodes in a complex graph system. */ - MA_SOUND_FLAG_NO_PITCH = 0x00002000, /* Disable pitch shifting with ma_sound_set_pitch() and ma_sound_group_set_pitch(). This is an optimization. */ - MA_SOUND_FLAG_NO_SPATIALIZATION = 0x00004000 /* Disable spatialization. */ -} ma_sound_flags; - -#ifndef MA_ENGINE_MAX_LISTENERS -#define MA_ENGINE_MAX_LISTENERS 4 -#endif - -#define MA_LISTENER_INDEX_CLOSEST ((ma_uint8)-1) - -typedef enum -{ - ma_engine_node_type_sound, - ma_engine_node_type_group -} ma_engine_node_type; - -typedef struct -{ - ma_engine* pEngine; - ma_engine_node_type type; - ma_uint32 channelsIn; - ma_uint32 channelsOut; - ma_uint32 sampleRate; /* Only used when the type is set to ma_engine_node_type_sound. */ - ma_uint32 volumeSmoothTimeInPCMFrames; /* The number of frames to smooth over volume changes. Defaults to 0 in which case no smoothing is used. */ - ma_mono_expansion_mode monoExpansionMode; - ma_bool8 isPitchDisabled; /* Pitching can be explicitly disabled with MA_SOUND_FLAG_NO_PITCH to optimize processing. */ - ma_bool8 isSpatializationDisabled; /* Spatialization can be explicitly disabled with MA_SOUND_FLAG_NO_SPATIALIZATION. */ - ma_uint8 pinnedListenerIndex; /* The index of the listener this node should always use for spatialization. If set to MA_LISTENER_INDEX_CLOSEST the engine will use the closest listener. */ -} ma_engine_node_config; - -MA_API ma_engine_node_config ma_engine_node_config_init(ma_engine* pEngine, ma_engine_node_type type, ma_uint32 flags); - - -/* Base node object for both ma_sound and ma_sound_group. */ -typedef struct -{ - ma_node_base baseNode; /* Must be the first member for compatibility with the ma_node API. */ - ma_engine* pEngine; /* A pointer to the engine. Set based on the value from the config. */ - ma_uint32 sampleRate; /* The sample rate of the input data. For sounds backed by a data source, this will be the data source's sample rate. Otherwise it'll be the engine's sample rate. */ - ma_uint32 volumeSmoothTimeInPCMFrames; - ma_mono_expansion_mode monoExpansionMode; - ma_fader fader; - ma_linear_resampler resampler; /* For pitch shift. */ - ma_spatializer spatializer; - ma_panner panner; - ma_gainer volumeGainer; /* This will only be used if volumeSmoothTimeInPCMFrames is > 0. */ - ma_atomic_float volume; /* Defaults to 1. */ - MA_ATOMIC(4, float) pitch; - float oldPitch; /* For determining whether or not the resampler needs to be updated to reflect the new pitch. The resampler will be updated on the mixing thread. */ - float oldDopplerPitch; /* For determining whether or not the resampler needs to be updated to take a new doppler pitch into account. */ - MA_ATOMIC(4, ma_bool32) isPitchDisabled; /* When set to true, pitching will be disabled which will allow the resampler to be bypassed to save some computation. */ - MA_ATOMIC(4, ma_bool32) isSpatializationDisabled; /* Set to false by default. When set to false, will not have spatialisation applied. */ - MA_ATOMIC(4, ma_uint32) pinnedListenerIndex; /* The index of the listener this node should always use for spatialization. If set to MA_LISTENER_INDEX_CLOSEST the engine will use the closest listener. */ - - /* When setting a fade, it's not done immediately in ma_sound_set_fade(). It's deferred to the audio thread which means we need to store the settings here. */ - struct - { - ma_atomic_float volumeBeg; - ma_atomic_float volumeEnd; - ma_atomic_uint64 fadeLengthInFrames; /* <-- Defaults to (~(ma_uint64)0) which is used to indicate that no fade should be applied. */ - ma_atomic_uint64 absoluteGlobalTimeInFrames; /* <-- The time to start the fade. */ - } fadeSettings; - - /* Memory management. */ - ma_bool8 _ownsHeap; - void* _pHeap; -} ma_engine_node; - -MA_API ma_result ma_engine_node_get_heap_size(const ma_engine_node_config* pConfig, size_t* pHeapSizeInBytes); -MA_API ma_result ma_engine_node_init_preallocated(const ma_engine_node_config* pConfig, void* pHeap, ma_engine_node* pEngineNode); -MA_API ma_result ma_engine_node_init(const ma_engine_node_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_engine_node* pEngineNode); -MA_API void ma_engine_node_uninit(ma_engine_node* pEngineNode, const ma_allocation_callbacks* pAllocationCallbacks); - - -#define MA_SOUND_SOURCE_CHANNEL_COUNT 0xFFFFFFFF - -/* Callback for when a sound reaches the end. */ -typedef void (* ma_sound_end_proc)(void* pUserData, ma_sound* pSound); - -typedef struct -{ - const char* pFilePath; /* Set this to load from the resource manager. */ - const wchar_t* pFilePathW; /* Set this to load from the resource manager. */ - ma_data_source* pDataSource; /* Set this to load from an existing data source. */ - ma_node* pInitialAttachment; /* If set, the sound will be attached to an input of this node. This can be set to a ma_sound. If set to NULL, the sound will be attached directly to the endpoint unless MA_SOUND_FLAG_NO_DEFAULT_ATTACHMENT is set in `flags`. */ - ma_uint32 initialAttachmentInputBusIndex; /* The index of the input bus of pInitialAttachment to attach the sound to. */ - ma_uint32 channelsIn; /* Ignored if using a data source as input (the data source's channel count will be used always). Otherwise, setting to 0 will cause the engine's channel count to be used. */ - ma_uint32 channelsOut; /* Set this to 0 (default) to use the engine's channel count. Set to MA_SOUND_SOURCE_CHANNEL_COUNT to use the data source's channel count (only used if using a data source as input). */ - ma_mono_expansion_mode monoExpansionMode; /* Controls how the mono channel should be expanded to other channels when spatialization is disabled on a sound. */ - ma_uint32 flags; /* A combination of MA_SOUND_FLAG_* flags. */ - ma_uint32 volumeSmoothTimeInPCMFrames; /* The number of frames to smooth over volume changes. Defaults to 0 in which case no smoothing is used. */ - ma_uint64 initialSeekPointInPCMFrames; /* Initializes the sound such that it's seeked to this location by default. */ - ma_uint64 rangeBegInPCMFrames; - ma_uint64 rangeEndInPCMFrames; - ma_uint64 loopPointBegInPCMFrames; - ma_uint64 loopPointEndInPCMFrames; - ma_sound_end_proc endCallback; /* Fired when the sound reaches the end. Will be fired from the audio thread. Do not restart, uninitialize or otherwise change the state of the sound from here. Instead fire an event or set a variable to indicate to a different thread to change the start of the sound. Will not be fired in response to a scheduled stop with ma_sound_set_stop_time_*(). */ - void* pEndCallbackUserData; -#ifndef MA_NO_RESOURCE_MANAGER - ma_resource_manager_pipeline_notifications initNotifications; -#endif - ma_fence* pDoneFence; /* Deprecated. Use initNotifications instead. Released when the resource manager has finished decoding the entire sound. Not used with streams. */ - ma_bool32 isLooping; /* Deprecated. Use the MA_SOUND_FLAG_LOOPING flag in `flags` instead. */ -} ma_sound_config; - -MA_API ma_sound_config ma_sound_config_init(void); /* Deprecated. Will be removed in version 0.12. Use ma_sound_config_2() instead. */ -MA_API ma_sound_config ma_sound_config_init_2(ma_engine* pEngine); /* Will be renamed to ma_sound_config_init() in version 0.12. */ - -struct ma_sound -{ - ma_engine_node engineNode; /* Must be the first member for compatibility with the ma_node API. */ - ma_data_source* pDataSource; - MA_ATOMIC(8, ma_uint64) seekTarget; /* The PCM frame index to seek to in the mixing thread. Set to (~(ma_uint64)0) to not perform any seeking. */ - MA_ATOMIC(4, ma_bool32) atEnd; - ma_sound_end_proc endCallback; - void* pEndCallbackUserData; - ma_bool8 ownsDataSource; - - /* - We're declaring a resource manager data source object here to save us a malloc when loading a - sound via the resource manager, which I *think* will be the most common scenario. - */ -#ifndef MA_NO_RESOURCE_MANAGER - ma_resource_manager_data_source* pResourceManagerDataSource; -#endif -}; - -/* Structure specifically for sounds played with ma_engine_play_sound(). Making this a separate structure to reduce overhead. */ -typedef struct ma_sound_inlined ma_sound_inlined; -struct ma_sound_inlined -{ - ma_sound sound; - ma_sound_inlined* pNext; - ma_sound_inlined* pPrev; -}; - -/* A sound group is just a sound. */ -typedef ma_sound_config ma_sound_group_config; -typedef ma_sound ma_sound_group; - -MA_API ma_sound_group_config ma_sound_group_config_init(void); /* Deprecated. Will be removed in version 0.12. Use ma_sound_config_2() instead. */ -MA_API ma_sound_group_config ma_sound_group_config_init_2(ma_engine* pEngine); /* Will be renamed to ma_sound_config_init() in version 0.12. */ - -typedef void (* ma_engine_process_proc)(void* pUserData, float* pFramesOut, ma_uint64 frameCount); - -typedef struct -{ -#if !defined(MA_NO_RESOURCE_MANAGER) - ma_resource_manager* pResourceManager; /* Can be null in which case a resource manager will be created for you. */ -#endif -#if !defined(MA_NO_DEVICE_IO) - ma_context* pContext; - ma_device* pDevice; /* If set, the caller is responsible for calling ma_engine_data_callback() in the device's data callback. */ - ma_device_id* pPlaybackDeviceID; /* The ID of the playback device to use with the default listener. */ - ma_device_data_proc dataCallback; /* Can be null. Can be used to provide a custom device data callback. */ - ma_device_notification_proc notificationCallback; -#endif - ma_log* pLog; /* When set to NULL, will use the context's log. */ - ma_uint32 listenerCount; /* Must be between 1 and MA_ENGINE_MAX_LISTENERS. */ - ma_uint32 channels; /* The number of channels to use when mixing and spatializing. When set to 0, will use the native channel count of the device. */ - ma_uint32 sampleRate; /* The sample rate. When set to 0 will use the native channel count of the device. */ - ma_uint32 periodSizeInFrames; /* If set to something other than 0, updates will always be exactly this size. The underlying device may be a different size, but from the perspective of the mixer that won't matter.*/ - ma_uint32 periodSizeInMilliseconds; /* Used if periodSizeInFrames is unset. */ - ma_uint32 gainSmoothTimeInFrames; /* The number of frames to interpolate the gain of spatialized sounds across. If set to 0, will use gainSmoothTimeInMilliseconds. */ - ma_uint32 gainSmoothTimeInMilliseconds; /* When set to 0, gainSmoothTimeInFrames will be used. If both are set to 0, a default value will be used. */ - ma_uint32 defaultVolumeSmoothTimeInPCMFrames; /* Defaults to 0. Controls the default amount of smoothing to apply to volume changes to sounds. High values means more smoothing at the expense of high latency (will take longer to reach the new volume). */ - ma_uint32 preMixStackSizeInBytes; /* A stack is used for internal processing in the node graph. This allows you to configure the size of this stack. Smaller values will reduce the maximum depth of your node graph. You should rarely need to modify this. */ - ma_allocation_callbacks allocationCallbacks; - ma_bool32 noAutoStart; /* When set to true, requires an explicit call to ma_engine_start(). This is false by default, meaning the engine will be started automatically in ma_engine_init(). */ - ma_bool32 noDevice; /* When set to true, don't create a default device. ma_engine_read_pcm_frames() can be called manually to read data. */ - ma_mono_expansion_mode monoExpansionMode; /* Controls how the mono channel should be expanded to other channels when spatialization is disabled on a sound. */ - ma_vfs* pResourceManagerVFS; /* A pointer to a pre-allocated VFS object to use with the resource manager. This is ignored if pResourceManager is not NULL. */ - ma_engine_process_proc onProcess; /* Fired at the end of each call to ma_engine_read_pcm_frames(). For engine's that manage their own internal device (the default configuration), this will be fired from the audio thread, and you do not need to call ma_engine_read_pcm_frames() manually in order to trigger this. */ - void* pProcessUserData; /* User data that's passed into onProcess. */ -} ma_engine_config; - -MA_API ma_engine_config ma_engine_config_init(void); - - -struct ma_engine -{ - ma_node_graph nodeGraph; /* An engine is a node graph. It should be able to be plugged into any ma_node_graph API (with a cast) which means this must be the first member of this struct. */ -#if !defined(MA_NO_RESOURCE_MANAGER) - ma_resource_manager* pResourceManager; -#endif -#if !defined(MA_NO_DEVICE_IO) - ma_device* pDevice; /* Optionally set via the config, otherwise allocated by the engine in ma_engine_init(). */ -#endif - ma_log* pLog; - ma_uint32 sampleRate; - ma_uint32 listenerCount; - ma_spatializer_listener listeners[MA_ENGINE_MAX_LISTENERS]; - ma_allocation_callbacks allocationCallbacks; - ma_bool8 ownsResourceManager; - ma_bool8 ownsDevice; - ma_spinlock inlinedSoundLock; /* For synchronizing access to the inlined sound list. */ - ma_sound_inlined* pInlinedSoundHead; /* The first inlined sound. Inlined sounds are tracked in a linked list. */ - MA_ATOMIC(4, ma_uint32) inlinedSoundCount; /* The total number of allocated inlined sound objects. Used for debugging. */ - ma_uint32 gainSmoothTimeInFrames; /* The number of frames to interpolate the gain of spatialized sounds across. */ - ma_uint32 defaultVolumeSmoothTimeInPCMFrames; - ma_mono_expansion_mode monoExpansionMode; - ma_engine_process_proc onProcess; - void* pProcessUserData; -}; - -MA_API ma_result ma_engine_init(const ma_engine_config* pConfig, ma_engine* pEngine); -MA_API void ma_engine_uninit(ma_engine* pEngine); -MA_API ma_result ma_engine_read_pcm_frames(ma_engine* pEngine, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead); -MA_API ma_node_graph* ma_engine_get_node_graph(ma_engine* pEngine); -#if !defined(MA_NO_RESOURCE_MANAGER) -MA_API ma_resource_manager* ma_engine_get_resource_manager(ma_engine* pEngine); -#endif -MA_API ma_device* ma_engine_get_device(ma_engine* pEngine); -MA_API ma_log* ma_engine_get_log(ma_engine* pEngine); -MA_API ma_node* ma_engine_get_endpoint(ma_engine* pEngine); -MA_API ma_uint64 ma_engine_get_time_in_pcm_frames(const ma_engine* pEngine); -MA_API ma_uint64 ma_engine_get_time_in_milliseconds(const ma_engine* pEngine); -MA_API ma_result ma_engine_set_time_in_pcm_frames(ma_engine* pEngine, ma_uint64 globalTime); -MA_API ma_result ma_engine_set_time_in_milliseconds(ma_engine* pEngine, ma_uint64 globalTime); -MA_API ma_uint64 ma_engine_get_time(const ma_engine* pEngine); /* Deprecated. Use ma_engine_get_time_in_pcm_frames(). Will be removed in version 0.12. */ -MA_API ma_result ma_engine_set_time(ma_engine* pEngine, ma_uint64 globalTime); /* Deprecated. Use ma_engine_set_time_in_pcm_frames(). Will be removed in version 0.12. */ -MA_API ma_uint32 ma_engine_get_channels(const ma_engine* pEngine); -MA_API ma_uint32 ma_engine_get_sample_rate(const ma_engine* pEngine); - -MA_API ma_result ma_engine_start(ma_engine* pEngine); -MA_API ma_result ma_engine_stop(ma_engine* pEngine); -MA_API ma_result ma_engine_set_volume(ma_engine* pEngine, float volume); -MA_API float ma_engine_get_volume(ma_engine* pEngine); -MA_API ma_result ma_engine_set_gain_db(ma_engine* pEngine, float gainDB); -MA_API float ma_engine_get_gain_db(ma_engine* pEngine); - -MA_API ma_uint32 ma_engine_get_listener_count(const ma_engine* pEngine); -MA_API ma_uint32 ma_engine_find_closest_listener(const ma_engine* pEngine, float absolutePosX, float absolutePosY, float absolutePosZ); -MA_API void ma_engine_listener_set_position(ma_engine* pEngine, ma_uint32 listenerIndex, float x, float y, float z); -MA_API ma_vec3f ma_engine_listener_get_position(const ma_engine* pEngine, ma_uint32 listenerIndex); -MA_API void ma_engine_listener_set_direction(ma_engine* pEngine, ma_uint32 listenerIndex, float x, float y, float z); -MA_API ma_vec3f ma_engine_listener_get_direction(const ma_engine* pEngine, ma_uint32 listenerIndex); -MA_API void ma_engine_listener_set_velocity(ma_engine* pEngine, ma_uint32 listenerIndex, float x, float y, float z); -MA_API ma_vec3f ma_engine_listener_get_velocity(const ma_engine* pEngine, ma_uint32 listenerIndex); -MA_API void ma_engine_listener_set_cone(ma_engine* pEngine, ma_uint32 listenerIndex, float innerAngleInRadians, float outerAngleInRadians, float outerGain); -MA_API void ma_engine_listener_get_cone(const ma_engine* pEngine, ma_uint32 listenerIndex, float* pInnerAngleInRadians, float* pOuterAngleInRadians, float* pOuterGain); -MA_API void ma_engine_listener_set_world_up(ma_engine* pEngine, ma_uint32 listenerIndex, float x, float y, float z); -MA_API ma_vec3f ma_engine_listener_get_world_up(const ma_engine* pEngine, ma_uint32 listenerIndex); -MA_API void ma_engine_listener_set_enabled(ma_engine* pEngine, ma_uint32 listenerIndex, ma_bool32 isEnabled); -MA_API ma_bool32 ma_engine_listener_is_enabled(const ma_engine* pEngine, ma_uint32 listenerIndex); - -#ifndef MA_NO_RESOURCE_MANAGER -MA_API ma_result ma_engine_play_sound_ex(ma_engine* pEngine, const char* pFilePath, ma_node* pNode, ma_uint32 nodeInputBusIndex); -MA_API ma_result ma_engine_play_sound(ma_engine* pEngine, const char* pFilePath, ma_sound_group* pGroup); /* Fire and forget. */ -#endif - -#ifndef MA_NO_RESOURCE_MANAGER -MA_API ma_result ma_sound_init_from_file(ma_engine* pEngine, const char* pFilePath, ma_uint32 flags, ma_sound_group* pGroup, ma_fence* pDoneFence, ma_sound* pSound); -MA_API ma_result ma_sound_init_from_file_w(ma_engine* pEngine, const wchar_t* pFilePath, ma_uint32 flags, ma_sound_group* pGroup, ma_fence* pDoneFence, ma_sound* pSound); -MA_API ma_result ma_sound_init_copy(ma_engine* pEngine, const ma_sound* pExistingSound, ma_uint32 flags, ma_sound_group* pGroup, ma_sound* pSound); -#endif -MA_API ma_result ma_sound_init_from_data_source(ma_engine* pEngine, ma_data_source* pDataSource, ma_uint32 flags, ma_sound_group* pGroup, ma_sound* pSound); -MA_API ma_result ma_sound_init_ex(ma_engine* pEngine, const ma_sound_config* pConfig, ma_sound* pSound); -MA_API void ma_sound_uninit(ma_sound* pSound); -MA_API ma_engine* ma_sound_get_engine(const ma_sound* pSound); -MA_API ma_data_source* ma_sound_get_data_source(const ma_sound* pSound); -MA_API ma_result ma_sound_start(ma_sound* pSound); -MA_API ma_result ma_sound_stop(ma_sound* pSound); -MA_API ma_result ma_sound_stop_with_fade_in_pcm_frames(ma_sound* pSound, ma_uint64 fadeLengthInFrames); /* Will overwrite any scheduled stop and fade. */ -MA_API ma_result ma_sound_stop_with_fade_in_milliseconds(ma_sound* pSound, ma_uint64 fadeLengthInFrames); /* Will overwrite any scheduled stop and fade. */ -MA_API void ma_sound_set_volume(ma_sound* pSound, float volume); -MA_API float ma_sound_get_volume(const ma_sound* pSound); -MA_API void ma_sound_set_pan(ma_sound* pSound, float pan); -MA_API float ma_sound_get_pan(const ma_sound* pSound); -MA_API void ma_sound_set_pan_mode(ma_sound* pSound, ma_pan_mode panMode); -MA_API ma_pan_mode ma_sound_get_pan_mode(const ma_sound* pSound); -MA_API void ma_sound_set_pitch(ma_sound* pSound, float pitch); -MA_API float ma_sound_get_pitch(const ma_sound* pSound); -MA_API void ma_sound_set_spatialization_enabled(ma_sound* pSound, ma_bool32 enabled); -MA_API ma_bool32 ma_sound_is_spatialization_enabled(const ma_sound* pSound); -MA_API void ma_sound_set_pinned_listener_index(ma_sound* pSound, ma_uint32 listenerIndex); -MA_API ma_uint32 ma_sound_get_pinned_listener_index(const ma_sound* pSound); -MA_API ma_uint32 ma_sound_get_listener_index(const ma_sound* pSound); -MA_API ma_vec3f ma_sound_get_direction_to_listener(const ma_sound* pSound); -MA_API void ma_sound_set_position(ma_sound* pSound, float x, float y, float z); -MA_API ma_vec3f ma_sound_get_position(const ma_sound* pSound); -MA_API void ma_sound_set_direction(ma_sound* pSound, float x, float y, float z); -MA_API ma_vec3f ma_sound_get_direction(const ma_sound* pSound); -MA_API void ma_sound_set_velocity(ma_sound* pSound, float x, float y, float z); -MA_API ma_vec3f ma_sound_get_velocity(const ma_sound* pSound); -MA_API void ma_sound_set_attenuation_model(ma_sound* pSound, ma_attenuation_model attenuationModel); -MA_API ma_attenuation_model ma_sound_get_attenuation_model(const ma_sound* pSound); -MA_API void ma_sound_set_positioning(ma_sound* pSound, ma_positioning positioning); -MA_API ma_positioning ma_sound_get_positioning(const ma_sound* pSound); -MA_API void ma_sound_set_rolloff(ma_sound* pSound, float rolloff); -MA_API float ma_sound_get_rolloff(const ma_sound* pSound); -MA_API void ma_sound_set_min_gain(ma_sound* pSound, float minGain); -MA_API float ma_sound_get_min_gain(const ma_sound* pSound); -MA_API void ma_sound_set_max_gain(ma_sound* pSound, float maxGain); -MA_API float ma_sound_get_max_gain(const ma_sound* pSound); -MA_API void ma_sound_set_min_distance(ma_sound* pSound, float minDistance); -MA_API float ma_sound_get_min_distance(const ma_sound* pSound); -MA_API void ma_sound_set_max_distance(ma_sound* pSound, float maxDistance); -MA_API float ma_sound_get_max_distance(const ma_sound* pSound); -MA_API void ma_sound_set_cone(ma_sound* pSound, float innerAngleInRadians, float outerAngleInRadians, float outerGain); -MA_API void ma_sound_get_cone(const ma_sound* pSound, float* pInnerAngleInRadians, float* pOuterAngleInRadians, float* pOuterGain); -MA_API void ma_sound_set_doppler_factor(ma_sound* pSound, float dopplerFactor); -MA_API float ma_sound_get_doppler_factor(const ma_sound* pSound); -MA_API void ma_sound_set_directional_attenuation_factor(ma_sound* pSound, float directionalAttenuationFactor); -MA_API float ma_sound_get_directional_attenuation_factor(const ma_sound* pSound); -MA_API void ma_sound_set_fade_in_pcm_frames(ma_sound* pSound, float volumeBeg, float volumeEnd, ma_uint64 fadeLengthInFrames); -MA_API void ma_sound_set_fade_in_milliseconds(ma_sound* pSound, float volumeBeg, float volumeEnd, ma_uint64 fadeLengthInMilliseconds); -MA_API void ma_sound_set_fade_start_in_pcm_frames(ma_sound* pSound, float volumeBeg, float volumeEnd, ma_uint64 fadeLengthInFrames, ma_uint64 absoluteGlobalTimeInFrames); -MA_API void ma_sound_set_fade_start_in_milliseconds(ma_sound* pSound, float volumeBeg, float volumeEnd, ma_uint64 fadeLengthInMilliseconds, ma_uint64 absoluteGlobalTimeInMilliseconds); -MA_API float ma_sound_get_current_fade_volume(const ma_sound* pSound); -MA_API void ma_sound_set_start_time_in_pcm_frames(ma_sound* pSound, ma_uint64 absoluteGlobalTimeInFrames); -MA_API void ma_sound_set_start_time_in_milliseconds(ma_sound* pSound, ma_uint64 absoluteGlobalTimeInMilliseconds); -MA_API void ma_sound_set_stop_time_in_pcm_frames(ma_sound* pSound, ma_uint64 absoluteGlobalTimeInFrames); -MA_API void ma_sound_set_stop_time_in_milliseconds(ma_sound* pSound, ma_uint64 absoluteGlobalTimeInMilliseconds); -MA_API void ma_sound_set_stop_time_with_fade_in_pcm_frames(ma_sound* pSound, ma_uint64 stopAbsoluteGlobalTimeInFrames, ma_uint64 fadeLengthInFrames); -MA_API void ma_sound_set_stop_time_with_fade_in_milliseconds(ma_sound* pSound, ma_uint64 stopAbsoluteGlobalTimeInMilliseconds, ma_uint64 fadeLengthInMilliseconds); -MA_API ma_bool32 ma_sound_is_playing(const ma_sound* pSound); -MA_API ma_uint64 ma_sound_get_time_in_pcm_frames(const ma_sound* pSound); -MA_API ma_uint64 ma_sound_get_time_in_milliseconds(const ma_sound* pSound); -MA_API void ma_sound_set_looping(ma_sound* pSound, ma_bool32 isLooping); -MA_API ma_bool32 ma_sound_is_looping(const ma_sound* pSound); -MA_API ma_bool32 ma_sound_at_end(const ma_sound* pSound); -MA_API ma_result ma_sound_seek_to_pcm_frame(ma_sound* pSound, ma_uint64 frameIndex); /* Just a wrapper around ma_data_source_seek_to_pcm_frame(). */ -MA_API ma_result ma_sound_seek_to_second(ma_sound* pSound, float seekPointInSeconds); /* Abstraction to ma_sound_seek_to_pcm_frame() */ -MA_API ma_result ma_sound_get_data_format(ma_sound* pSound, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate, ma_channel* pChannelMap, size_t channelMapCap); -MA_API ma_result ma_sound_get_cursor_in_pcm_frames(ma_sound* pSound, ma_uint64* pCursor); -MA_API ma_result ma_sound_get_length_in_pcm_frames(ma_sound* pSound, ma_uint64* pLength); -MA_API ma_result ma_sound_get_cursor_in_seconds(ma_sound* pSound, float* pCursor); -MA_API ma_result ma_sound_get_length_in_seconds(ma_sound* pSound, float* pLength); -MA_API ma_result ma_sound_set_end_callback(ma_sound* pSound, ma_sound_end_proc callback, void* pUserData); - -MA_API ma_result ma_sound_group_init(ma_engine* pEngine, ma_uint32 flags, ma_sound_group* pParentGroup, ma_sound_group* pGroup); -MA_API ma_result ma_sound_group_init_ex(ma_engine* pEngine, const ma_sound_group_config* pConfig, ma_sound_group* pGroup); -MA_API void ma_sound_group_uninit(ma_sound_group* pGroup); -MA_API ma_engine* ma_sound_group_get_engine(const ma_sound_group* pGroup); -MA_API ma_result ma_sound_group_start(ma_sound_group* pGroup); -MA_API ma_result ma_sound_group_stop(ma_sound_group* pGroup); -MA_API void ma_sound_group_set_volume(ma_sound_group* pGroup, float volume); -MA_API float ma_sound_group_get_volume(const ma_sound_group* pGroup); -MA_API void ma_sound_group_set_pan(ma_sound_group* pGroup, float pan); -MA_API float ma_sound_group_get_pan(const ma_sound_group* pGroup); -MA_API void ma_sound_group_set_pan_mode(ma_sound_group* pGroup, ma_pan_mode panMode); -MA_API ma_pan_mode ma_sound_group_get_pan_mode(const ma_sound_group* pGroup); -MA_API void ma_sound_group_set_pitch(ma_sound_group* pGroup, float pitch); -MA_API float ma_sound_group_get_pitch(const ma_sound_group* pGroup); -MA_API void ma_sound_group_set_spatialization_enabled(ma_sound_group* pGroup, ma_bool32 enabled); -MA_API ma_bool32 ma_sound_group_is_spatialization_enabled(const ma_sound_group* pGroup); -MA_API void ma_sound_group_set_pinned_listener_index(ma_sound_group* pGroup, ma_uint32 listenerIndex); -MA_API ma_uint32 ma_sound_group_get_pinned_listener_index(const ma_sound_group* pGroup); -MA_API ma_uint32 ma_sound_group_get_listener_index(const ma_sound_group* pGroup); -MA_API ma_vec3f ma_sound_group_get_direction_to_listener(const ma_sound_group* pGroup); -MA_API void ma_sound_group_set_position(ma_sound_group* pGroup, float x, float y, float z); -MA_API ma_vec3f ma_sound_group_get_position(const ma_sound_group* pGroup); -MA_API void ma_sound_group_set_direction(ma_sound_group* pGroup, float x, float y, float z); -MA_API ma_vec3f ma_sound_group_get_direction(const ma_sound_group* pGroup); -MA_API void ma_sound_group_set_velocity(ma_sound_group* pGroup, float x, float y, float z); -MA_API ma_vec3f ma_sound_group_get_velocity(const ma_sound_group* pGroup); -MA_API void ma_sound_group_set_attenuation_model(ma_sound_group* pGroup, ma_attenuation_model attenuationModel); -MA_API ma_attenuation_model ma_sound_group_get_attenuation_model(const ma_sound_group* pGroup); -MA_API void ma_sound_group_set_positioning(ma_sound_group* pGroup, ma_positioning positioning); -MA_API ma_positioning ma_sound_group_get_positioning(const ma_sound_group* pGroup); -MA_API void ma_sound_group_set_rolloff(ma_sound_group* pGroup, float rolloff); -MA_API float ma_sound_group_get_rolloff(const ma_sound_group* pGroup); -MA_API void ma_sound_group_set_min_gain(ma_sound_group* pGroup, float minGain); -MA_API float ma_sound_group_get_min_gain(const ma_sound_group* pGroup); -MA_API void ma_sound_group_set_max_gain(ma_sound_group* pGroup, float maxGain); -MA_API float ma_sound_group_get_max_gain(const ma_sound_group* pGroup); -MA_API void ma_sound_group_set_min_distance(ma_sound_group* pGroup, float minDistance); -MA_API float ma_sound_group_get_min_distance(const ma_sound_group* pGroup); -MA_API void ma_sound_group_set_max_distance(ma_sound_group* pGroup, float maxDistance); -MA_API float ma_sound_group_get_max_distance(const ma_sound_group* pGroup); -MA_API void ma_sound_group_set_cone(ma_sound_group* pGroup, float innerAngleInRadians, float outerAngleInRadians, float outerGain); -MA_API void ma_sound_group_get_cone(const ma_sound_group* pGroup, float* pInnerAngleInRadians, float* pOuterAngleInRadians, float* pOuterGain); -MA_API void ma_sound_group_set_doppler_factor(ma_sound_group* pGroup, float dopplerFactor); -MA_API float ma_sound_group_get_doppler_factor(const ma_sound_group* pGroup); -MA_API void ma_sound_group_set_directional_attenuation_factor(ma_sound_group* pGroup, float directionalAttenuationFactor); -MA_API float ma_sound_group_get_directional_attenuation_factor(const ma_sound_group* pGroup); -MA_API void ma_sound_group_set_fade_in_pcm_frames(ma_sound_group* pGroup, float volumeBeg, float volumeEnd, ma_uint64 fadeLengthInFrames); -MA_API void ma_sound_group_set_fade_in_milliseconds(ma_sound_group* pGroup, float volumeBeg, float volumeEnd, ma_uint64 fadeLengthInMilliseconds); -MA_API float ma_sound_group_get_current_fade_volume(ma_sound_group* pGroup); -MA_API void ma_sound_group_set_start_time_in_pcm_frames(ma_sound_group* pGroup, ma_uint64 absoluteGlobalTimeInFrames); -MA_API void ma_sound_group_set_start_time_in_milliseconds(ma_sound_group* pGroup, ma_uint64 absoluteGlobalTimeInMilliseconds); -MA_API void ma_sound_group_set_stop_time_in_pcm_frames(ma_sound_group* pGroup, ma_uint64 absoluteGlobalTimeInFrames); -MA_API void ma_sound_group_set_stop_time_in_milliseconds(ma_sound_group* pGroup, ma_uint64 absoluteGlobalTimeInMilliseconds); -MA_API ma_bool32 ma_sound_group_is_playing(const ma_sound_group* pGroup); -MA_API ma_uint64 ma_sound_group_get_time_in_pcm_frames(const ma_sound_group* pGroup); -#endif /* MA_NO_ENGINE */ -/* END SECTION: miniaudio_engine.h */ - -#ifdef __cplusplus -} -#endif -#endif /* miniaudio_h */ - - -/* -This is for preventing greying out of the implementation section. -*/ -#if defined(Q_CREATOR_RUN) || defined(__INTELLISENSE__) || defined(__CDT_PARSER__) -#define MINIAUDIO_IMPLEMENTATION -#endif - -/************************************************************************************************************************************************************ -************************************************************************************************************************************************************* - -IMPLEMENTATION - -************************************************************************************************************************************************************* -************************************************************************************************************************************************************/ -#if defined(MINIAUDIO_IMPLEMENTATION) || defined(MA_IMPLEMENTATION) -#ifndef miniaudio_c -#define miniaudio_c - -#include -#include /* For INT_MAX */ -#include /* sin(), etc. */ -#include /* For malloc(), free(), wcstombs(). */ -#include /* For memset() */ - -#include -#include -#if !defined(_MSC_VER) && !defined(__DMC__) - #include /* For strcasecmp(). */ - #include /* For wcslen(), wcsrtombs() */ -#endif -#ifdef _MSC_VER - #include /* For _controlfp_s constants */ -#endif - -#if defined(MA_WIN32) - #include - - /* - There's a possibility that WIN32_LEAN_AND_MEAN has been defined which will exclude some symbols - such as STGM_READ and CLSCTL_ALL. We need to check these and define them ourselves if they're - unavailable. - */ - #ifndef STGM_READ - #define STGM_READ 0x00000000L - #endif - #ifndef CLSCTX_ALL - #define CLSCTX_ALL 23 - #endif - - /* IUnknown is used by both the WASAPI and DirectSound backends. It easier to just declare our version here. */ - typedef struct ma_IUnknown ma_IUnknown; -#endif - -#if !defined(MA_WIN32) -#include -#include /* select() (used for ma_sleep()). */ -#include -#endif - -#ifdef MA_NX -#include /* For nanosleep() */ -#endif - -#include /* For fstat(), etc. */ - -#ifdef MA_EMSCRIPTEN -#include -#endif - - -/* Architecture Detection */ -#if !defined(MA_64BIT) && !defined(MA_32BIT) -#ifdef _WIN32 -#ifdef _WIN64 -#define MA_64BIT -#else -#define MA_32BIT -#endif -#endif -#endif - -#if !defined(MA_64BIT) && !defined(MA_32BIT) -#ifdef __GNUC__ -#ifdef __LP64__ -#define MA_64BIT -#else -#define MA_32BIT -#endif -#endif -#endif - -#if !defined(MA_64BIT) && !defined(MA_32BIT) -#include -#if INTPTR_MAX == INT64_MAX -#define MA_64BIT -#else -#define MA_32BIT -#endif -#endif - -#if defined(__arm__) || defined(_M_ARM) -#define MA_ARM32 -#endif -#if defined(__arm64) || defined(__arm64__) || defined(__aarch64__) || defined(_M_ARM64) -#define MA_ARM64 -#endif - -#if defined(__x86_64__) || defined(_M_X64) -#define MA_X64 -#elif defined(__i386) || defined(_M_IX86) -#define MA_X86 -#elif defined(MA_ARM32) || defined(MA_ARM64) -#define MA_ARM -#endif - -/* Intrinsics Support */ -#if (defined(MA_X64) || defined(MA_X86)) && !defined(__COSMOPOLITAN__) - #if defined(_MSC_VER) && !defined(__clang__) - /* MSVC. */ - #if _MSC_VER >= 1400 && !defined(MA_NO_SSE2) /* 2005 */ - #define MA_SUPPORT_SSE2 - #endif - /*#if _MSC_VER >= 1600 && !defined(MA_NO_AVX)*/ /* 2010 */ - /* #define MA_SUPPORT_AVX*/ - /*#endif*/ - #if _MSC_VER >= 1700 && !defined(MA_NO_AVX2) /* 2012 */ - #define MA_SUPPORT_AVX2 - #endif - #else - /* Assume GNUC-style. */ - #if defined(__SSE2__) && !defined(MA_NO_SSE2) - #define MA_SUPPORT_SSE2 - #endif - /*#if defined(__AVX__) && !defined(MA_NO_AVX)*/ - /* #define MA_SUPPORT_AVX*/ - /*#endif*/ - #if defined(__AVX2__) && !defined(MA_NO_AVX2) - #define MA_SUPPORT_AVX2 - #endif - #endif - - /* If at this point we still haven't determined compiler support for the intrinsics just fall back to __has_include. */ - #if !defined(__GNUC__) && !defined(__clang__) && defined(__has_include) - #if !defined(MA_SUPPORT_SSE2) && !defined(MA_NO_SSE2) && __has_include() - #define MA_SUPPORT_SSE2 - #endif - /*#if !defined(MA_SUPPORT_AVX) && !defined(MA_NO_AVX) && __has_include()*/ - /* #define MA_SUPPORT_AVX*/ - /*#endif*/ - #if !defined(MA_SUPPORT_AVX2) && !defined(MA_NO_AVX2) && __has_include() - #define MA_SUPPORT_AVX2 - #endif - #endif - - #if defined(MA_SUPPORT_AVX2) || defined(MA_SUPPORT_AVX) - #include - #elif defined(MA_SUPPORT_SSE2) - #include - #endif -#endif - -#if defined(MA_ARM) - #if !defined(MA_NO_NEON) && (defined(__ARM_NEON) || defined(__aarch64__) || defined(_M_ARM64)) - #define MA_SUPPORT_NEON - #include - #endif -#endif - -/* Begin globally disabled warnings. */ -#if defined(_MSC_VER) - #pragma warning(push) - #pragma warning(disable:4752) /* found Intel(R) Advanced Vector Extensions; consider using /arch:AVX */ - #pragma warning(disable:4049) /* compiler limit : terminating line number emission */ -#endif - -#if defined(MA_X64) || defined(MA_X86) - #if defined(_MSC_VER) && !defined(__clang__) - #if _MSC_VER >= 1400 - #include - static MA_INLINE void ma_cpuid(int info[4], int fid) - { - __cpuid(info, fid); - } - #else - #define MA_NO_CPUID - #endif - - #if _MSC_VER >= 1600 && (defined(_MSC_FULL_VER) && _MSC_FULL_VER >= 160040219) - static MA_INLINE unsigned __int64 ma_xgetbv(int reg) - { - return _xgetbv(reg); - } - #else - #define MA_NO_XGETBV - #endif - #elif (defined(__GNUC__) || defined(__clang__)) && !defined(MA_ANDROID) - static MA_INLINE void ma_cpuid(int info[4], int fid) - { - /* - It looks like the -fPIC option uses the ebx register which GCC complains about. We can work around this by just using a different register, the - specific register of which I'm letting the compiler decide on. The "k" prefix is used to specify a 32-bit register. The {...} syntax is for - supporting different assembly dialects. - - What's basically happening is that we're saving and restoring the ebx register manually. - */ - #if defined(MA_X86) && defined(__PIC__) - __asm__ __volatile__ ( - "xchg{l} {%%}ebx, %k1;" - "cpuid;" - "xchg{l} {%%}ebx, %k1;" - : "=a"(info[0]), "=&r"(info[1]), "=c"(info[2]), "=d"(info[3]) : "a"(fid), "c"(0) - ); - #else - __asm__ __volatile__ ( - "cpuid" : "=a"(info[0]), "=b"(info[1]), "=c"(info[2]), "=d"(info[3]) : "a"(fid), "c"(0) - ); - #endif - } - - static MA_INLINE ma_uint64 ma_xgetbv(int reg) - { - unsigned int hi; - unsigned int lo; - - __asm__ __volatile__ ( - "xgetbv" : "=a"(lo), "=d"(hi) : "c"(reg) - ); - - return ((ma_uint64)hi << 32) | (ma_uint64)lo; - } - #else - #define MA_NO_CPUID - #define MA_NO_XGETBV - #endif -#else - #define MA_NO_CPUID - #define MA_NO_XGETBV -#endif - -static MA_INLINE ma_bool32 ma_has_sse2(void) -{ -#if defined(MA_SUPPORT_SSE2) - #if (defined(MA_X64) || defined(MA_X86)) && !defined(MA_NO_SSE2) - #if defined(MA_X64) - return MA_TRUE; /* 64-bit targets always support SSE2. */ - #elif (defined(_M_IX86_FP) && _M_IX86_FP == 2) || defined(__SSE2__) - return MA_TRUE; /* If the compiler is allowed to freely generate SSE2 code we can assume support. */ - #else - #if defined(MA_NO_CPUID) - return MA_FALSE; - #else - int info[4]; - ma_cpuid(info, 1); - return (info[3] & (1 << 26)) != 0; - #endif - #endif - #else - return MA_FALSE; /* SSE2 is only supported on x86 and x64 architectures. */ - #endif -#else - return MA_FALSE; /* No compiler support. */ -#endif -} - -#if 0 -static MA_INLINE ma_bool32 ma_has_avx() -{ -#if defined(MA_SUPPORT_AVX) - #if (defined(MA_X64) || defined(MA_X86)) && !defined(MA_NO_AVX) - #if defined(_AVX_) || defined(__AVX__) - return MA_TRUE; /* If the compiler is allowed to freely generate AVX code we can assume support. */ - #else - /* AVX requires both CPU and OS support. */ - #if defined(MA_NO_CPUID) || defined(MA_NO_XGETBV) - return MA_FALSE; - #else - int info[4]; - ma_cpuid(info, 1); - if (((info[2] & (1 << 27)) != 0) && ((info[2] & (1 << 28)) != 0)) { - ma_uint64 xrc = ma_xgetbv(0); - if ((xrc & 0x06) == 0x06) { - return MA_TRUE; - } else { - return MA_FALSE; - } - } else { - return MA_FALSE; - } - #endif - #endif - #else - return MA_FALSE; /* AVX is only supported on x86 and x64 architectures. */ - #endif -#else - return MA_FALSE; /* No compiler support. */ -#endif -} -#endif - -static MA_INLINE ma_bool32 ma_has_avx2(void) -{ -#if defined(MA_SUPPORT_AVX2) - #if (defined(MA_X64) || defined(MA_X86)) && !defined(MA_NO_AVX2) - #if defined(_AVX2_) || defined(__AVX2__) - return MA_TRUE; /* If the compiler is allowed to freely generate AVX2 code we can assume support. */ - #else - /* AVX2 requires both CPU and OS support. */ - #if defined(MA_NO_CPUID) || defined(MA_NO_XGETBV) - return MA_FALSE; - #else - int info1[4]; - int info7[4]; - ma_cpuid(info1, 1); - ma_cpuid(info7, 7); - if (((info1[2] & (1 << 27)) != 0) && ((info7[1] & (1 << 5)) != 0)) { - ma_uint64 xrc = ma_xgetbv(0); - if ((xrc & 0x06) == 0x06) { - return MA_TRUE; - } else { - return MA_FALSE; - } - } else { - return MA_FALSE; - } - #endif - #endif - #else - return MA_FALSE; /* AVX2 is only supported on x86 and x64 architectures. */ - #endif -#else - return MA_FALSE; /* No compiler support. */ -#endif -} - -static MA_INLINE ma_bool32 ma_has_neon(void) -{ -#if defined(MA_SUPPORT_NEON) - #if defined(MA_ARM) && !defined(MA_NO_NEON) - #if (defined(__ARM_NEON) || defined(__aarch64__) || defined(_M_ARM64)) - return MA_TRUE; /* If the compiler is allowed to freely generate NEON code we can assume support. */ - #else - /* TODO: Runtime check. */ - return MA_FALSE; - #endif - #else - return MA_FALSE; /* NEON is only supported on ARM architectures. */ - #endif -#else - return MA_FALSE; /* No compiler support. */ -#endif -} - -#if defined(__has_builtin) - #define MA_COMPILER_HAS_BUILTIN(x) __has_builtin(x) -#else - #define MA_COMPILER_HAS_BUILTIN(x) 0 -#endif - -#ifndef MA_ASSUME - #if MA_COMPILER_HAS_BUILTIN(__builtin_assume) - #define MA_ASSUME(x) __builtin_assume(x) - #elif MA_COMPILER_HAS_BUILTIN(__builtin_unreachable) - #define MA_ASSUME(x) do { if (!(x)) __builtin_unreachable(); } while (0) - #elif defined(_MSC_VER) - #define MA_ASSUME(x) __assume(x) - #else - #define MA_ASSUME(x) (void)(x) - #endif -#endif - -#ifndef MA_RESTRICT - #if defined(__clang__) || defined(__GNUC__) || defined(_MSC_VER) - #define MA_RESTRICT __restrict - #else - #define MA_RESTRICT - #endif -#endif - -#if defined(_MSC_VER) && _MSC_VER >= 1400 - #define MA_HAS_BYTESWAP16_INTRINSIC - #define MA_HAS_BYTESWAP32_INTRINSIC - #define MA_HAS_BYTESWAP64_INTRINSIC -#elif defined(__clang__) - #if MA_COMPILER_HAS_BUILTIN(__builtin_bswap16) - #define MA_HAS_BYTESWAP16_INTRINSIC - #endif - #if MA_COMPILER_HAS_BUILTIN(__builtin_bswap32) - #define MA_HAS_BYTESWAP32_INTRINSIC - #endif - #if MA_COMPILER_HAS_BUILTIN(__builtin_bswap64) - #define MA_HAS_BYTESWAP64_INTRINSIC - #endif -#elif defined(__GNUC__) - #if ((__GNUC__ > 4) || (__GNUC__ == 4 && __GNUC_MINOR__ >= 3)) - #define MA_HAS_BYTESWAP32_INTRINSIC - #define MA_HAS_BYTESWAP64_INTRINSIC - #endif - #if ((__GNUC__ > 4) || (__GNUC__ == 4 && __GNUC_MINOR__ >= 8)) - #define MA_HAS_BYTESWAP16_INTRINSIC - #endif -#endif - - -static MA_INLINE ma_bool32 ma_is_little_endian(void) -{ -#if defined(MA_X86) || defined(MA_X64) - return MA_TRUE; -#else - int n = 1; - return (*(char*)&n) == 1; -#endif -} - -static MA_INLINE ma_bool32 ma_is_big_endian(void) -{ - return !ma_is_little_endian(); -} - - -static MA_INLINE ma_uint32 ma_swap_endian_uint32(ma_uint32 n) -{ -#ifdef MA_HAS_BYTESWAP32_INTRINSIC - #if defined(_MSC_VER) - return _byteswap_ulong(n); - #elif defined(__GNUC__) || defined(__clang__) - #if defined(MA_ARM) && (defined(__ARM_ARCH) && __ARM_ARCH >= 6) && !defined(MA_64BIT) /* <-- 64-bit inline assembly has not been tested, so disabling for now. */ - /* Inline assembly optimized implementation for ARM. In my testing, GCC does not generate optimized code with __builtin_bswap32(). */ - ma_uint32 r; - __asm__ __volatile__ ( - #if defined(MA_64BIT) - "rev %w[out], %w[in]" : [out]"=r"(r) : [in]"r"(n) /* <-- This is untested. If someone in the community could test this, that would be appreciated! */ - #else - "rev %[out], %[in]" : [out]"=r"(r) : [in]"r"(n) - #endif - ); - return r; - #else - return __builtin_bswap32(n); - #endif - #else - #error "This compiler does not support the byte swap intrinsic." - #endif -#else - return ((n & 0xFF000000) >> 24) | - ((n & 0x00FF0000) >> 8) | - ((n & 0x0000FF00) << 8) | - ((n & 0x000000FF) << 24); -#endif -} - - -#if !defined(MA_EMSCRIPTEN) -#ifdef MA_WIN32 -static void ma_sleep__win32(ma_uint32 milliseconds) -{ - Sleep((DWORD)milliseconds); -} -#endif -#ifdef MA_POSIX -static void ma_sleep__posix(ma_uint32 milliseconds) -{ -#ifdef MA_EMSCRIPTEN - (void)milliseconds; - MA_ASSERT(MA_FALSE); /* The Emscripten build should never sleep. */ -#else - #if (defined(_POSIX_C_SOURCE) && _POSIX_C_SOURCE >= 199309L) || defined(MA_NX) - struct timespec ts; - ts.tv_sec = milliseconds / 1000; - ts.tv_nsec = milliseconds % 1000 * 1000000; - nanosleep(&ts, NULL); - #else - struct timeval tv; - tv.tv_sec = milliseconds / 1000; - tv.tv_usec = milliseconds % 1000 * 1000; - select(0, NULL, NULL, NULL, &tv); - #endif -#endif -} -#endif - -static MA_INLINE void ma_sleep(ma_uint32 milliseconds) -{ -#ifdef MA_WIN32 - ma_sleep__win32(milliseconds); -#endif -#ifdef MA_POSIX - ma_sleep__posix(milliseconds); -#endif -} -#endif - -static MA_INLINE void ma_yield(void) -{ -#if defined(__i386) || defined(_M_IX86) || defined(__x86_64__) || defined(_M_X64) - /* x86/x64 */ - #if (defined(_MSC_VER) || defined(__WATCOMC__) || defined(__DMC__)) && !defined(__clang__) - #if _MSC_VER >= 1400 - _mm_pause(); - #else - #if defined(__DMC__) - /* Digital Mars does not recognize the PAUSE opcode. Fall back to NOP. */ - __asm nop; - #else - __asm pause; - #endif - #endif - #else - __asm__ __volatile__ ("pause"); - #endif -#elif (defined(__arm__) && defined(__ARM_ARCH) && __ARM_ARCH >= 7) || defined(_M_ARM64) || (defined(_M_ARM) && _M_ARM >= 7) || defined(__ARM_ARCH_6K__) || defined(__ARM_ARCH_6T2__) - /* ARM */ - #if defined(_MSC_VER) - /* Apparently there is a __yield() intrinsic that's compatible with ARM, but I cannot find documentation for it nor can I find where it's declared. */ - __yield(); - #else - __asm__ __volatile__ ("yield"); /* ARMv6K/ARMv6T2 and above. */ - #endif -#else - /* Unknown or unsupported architecture. No-op. */ -#endif -} - - -#define MA_MM_DENORMALS_ZERO_MASK 0x0040 -#define MA_MM_FLUSH_ZERO_MASK 0x8000 - -static MA_INLINE unsigned int ma_disable_denormals(void) -{ - unsigned int prevState; - - #if defined(_MSC_VER) - { - /* - Older versions of Visual Studio don't support the "safe" versions of _controlfp_s(). I don't - know which version of Visual Studio first added support for _controlfp_s(), but I do know - that VC6 lacks support. _MSC_VER = 1200 is VC6, but if you get compilation errors on older - versions of Visual Studio, let me know and I'll make the necessary adjustment. - */ - #if _MSC_VER <= 1200 - { - prevState = _statusfp(); - _controlfp(prevState | _DN_FLUSH, _MCW_DN); - } - #else - { - unsigned int unused; - _controlfp_s(&prevState, 0, 0); - _controlfp_s(&unused, prevState | _DN_FLUSH, _MCW_DN); - } - #endif - } - #elif defined(MA_X86) || defined(MA_X64) - { - #if defined(__SSE2__) && !(defined(__TINYC__) || defined(__WATCOMC__) || defined(__COSMOPOLITAN__)) /* <-- Add compilers that lack support for _mm_getcsr() and _mm_setcsr() to this list. */ - { - prevState = _mm_getcsr(); - _mm_setcsr(prevState | MA_MM_DENORMALS_ZERO_MASK | MA_MM_FLUSH_ZERO_MASK); - } - #else - { - /* x88/64, but no support for _mm_getcsr()/_mm_setcsr(). May need to fall back to inlined assembly here. */ - prevState = 0; - } - #endif - } - #else - { - /* Unknown or unsupported architecture. No-op. */ - prevState = 0; - } - #endif - - return prevState; -} - -static MA_INLINE void ma_restore_denormals(unsigned int prevState) -{ - #if defined(_MSC_VER) - { - /* Older versions of Visual Studio do not support _controlfp_s(). See ma_disable_denormals(). */ - #if _MSC_VER <= 1200 - { - _controlfp(prevState, _MCW_DN); - } - #else - { - unsigned int unused; - _controlfp_s(&unused, prevState, _MCW_DN); - } - #endif - } - #elif defined(MA_X86) || defined(MA_X64) - { - #if defined(__SSE2__) && !(defined(__TINYC__) || defined(__WATCOMC__) || defined(__COSMOPOLITAN__)) /* <-- Add compilers that lack support for _mm_getcsr() and _mm_setcsr() to this list. */ - { - _mm_setcsr(prevState); - } - #else - { - /* x88/64, but no support for _mm_getcsr()/_mm_setcsr(). May need to fall back to inlined assembly here. */ - (void)prevState; - } - #endif - } - #else - { - /* Unknown or unsupported architecture. No-op. */ - (void)prevState; - } - #endif -} - - -#ifdef MA_ANDROID -#include - -int ma_android_sdk_version() -{ - char sdkVersion[PROP_VALUE_MAX + 1] = {0, }; - if (__system_property_get("ro.build.version.sdk", sdkVersion)) { - return atoi(sdkVersion); - } - - return 0; -} -#endif - - -#ifndef MA_COINIT_VALUE -#define MA_COINIT_VALUE 0 /* 0 = COINIT_MULTITHREADED */ -#endif - - -#ifndef MA_FLT_MAX - #ifdef FLT_MAX - #define MA_FLT_MAX FLT_MAX - #else - #define MA_FLT_MAX 3.402823466e+38F - #endif -#endif - - -#ifndef MA_PI -#define MA_PI 3.14159265358979323846264f -#endif -#ifndef MA_PI_D -#define MA_PI_D 3.14159265358979323846264 -#endif -#ifndef MA_TAU -#define MA_TAU 6.28318530717958647693f -#endif -#ifndef MA_TAU_D -#define MA_TAU_D 6.28318530717958647693 -#endif - - -/* The default format when ma_format_unknown (0) is requested when initializing a device. */ -#ifndef MA_DEFAULT_FORMAT -#define MA_DEFAULT_FORMAT ma_format_f32 -#endif - -/* The default channel count to use when 0 is used when initializing a device. */ -#ifndef MA_DEFAULT_CHANNELS -#define MA_DEFAULT_CHANNELS 2 -#endif - -/* The default sample rate to use when 0 is used when initializing a device. */ -#ifndef MA_DEFAULT_SAMPLE_RATE -#define MA_DEFAULT_SAMPLE_RATE 48000 -#endif - -/* Default periods when none is specified in ma_device_init(). More periods means more work on the CPU. */ -#ifndef MA_DEFAULT_PERIODS -#define MA_DEFAULT_PERIODS 3 -#endif - -/* The default period size in milliseconds for low latency mode. */ -#ifndef MA_DEFAULT_PERIOD_SIZE_IN_MILLISECONDS_LOW_LATENCY -#define MA_DEFAULT_PERIOD_SIZE_IN_MILLISECONDS_LOW_LATENCY 10 -#endif - -/* The default buffer size in milliseconds for conservative mode. */ -#ifndef MA_DEFAULT_PERIOD_SIZE_IN_MILLISECONDS_CONSERVATIVE -#define MA_DEFAULT_PERIOD_SIZE_IN_MILLISECONDS_CONSERVATIVE 100 -#endif - -/* The default LPF filter order for linear resampling. Note that this is clamped to MA_MAX_FILTER_ORDER. */ -#ifndef MA_DEFAULT_RESAMPLER_LPF_ORDER - #if MA_MAX_FILTER_ORDER >= 4 - #define MA_DEFAULT_RESAMPLER_LPF_ORDER 4 - #else - #define MA_DEFAULT_RESAMPLER_LPF_ORDER MA_MAX_FILTER_ORDER - #endif -#endif - - -#if defined(__clang__) || (defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6))) - #pragma GCC diagnostic push - #pragma GCC diagnostic ignored "-Wunused-variable" -#endif - -/* Standard sample rates, in order of priority. */ -static ma_uint32 g_maStandardSampleRatePriorities[] = { - (ma_uint32)ma_standard_sample_rate_48000, - (ma_uint32)ma_standard_sample_rate_44100, - - (ma_uint32)ma_standard_sample_rate_32000, - (ma_uint32)ma_standard_sample_rate_24000, - (ma_uint32)ma_standard_sample_rate_22050, - - (ma_uint32)ma_standard_sample_rate_88200, - (ma_uint32)ma_standard_sample_rate_96000, - (ma_uint32)ma_standard_sample_rate_176400, - (ma_uint32)ma_standard_sample_rate_192000, - - (ma_uint32)ma_standard_sample_rate_16000, - (ma_uint32)ma_standard_sample_rate_11025, - (ma_uint32)ma_standard_sample_rate_8000, - - (ma_uint32)ma_standard_sample_rate_352800, - (ma_uint32)ma_standard_sample_rate_384000 -}; - -static MA_INLINE ma_bool32 ma_is_standard_sample_rate(ma_uint32 sampleRate) -{ - ma_uint32 iSampleRate; - - for (iSampleRate = 0; iSampleRate < sizeof(g_maStandardSampleRatePriorities) / sizeof(g_maStandardSampleRatePriorities[0]); iSampleRate += 1) { - if (g_maStandardSampleRatePriorities[iSampleRate] == sampleRate) { - return MA_TRUE; - } - } - - /* Getting here means the sample rate is not supported. */ - return MA_FALSE; -} - - -static ma_format g_maFormatPriorities[] = { - ma_format_s16, /* Most common */ - ma_format_f32, - - /*ma_format_s24_32,*/ /* Clean alignment */ - ma_format_s32, - - ma_format_s24, /* Unclean alignment */ - - ma_format_u8 /* Low quality */ -}; -#if defined(__clang__) || (defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6))) - #pragma GCC diagnostic pop -#endif - - -MA_API void ma_version(ma_uint32* pMajor, ma_uint32* pMinor, ma_uint32* pRevision) -{ - if (pMajor) { - *pMajor = MA_VERSION_MAJOR; - } - - if (pMinor) { - *pMinor = MA_VERSION_MINOR; - } - - if (pRevision) { - *pRevision = MA_VERSION_REVISION; - } -} - -MA_API const char* ma_version_string(void) -{ - return MA_VERSION_STRING; -} - - -/****************************************************************************** - -Standard Library Stuff - -******************************************************************************/ -#ifndef MA_ASSERT -#define MA_ASSERT(condition) assert(condition) -#endif - -#ifndef MA_MALLOC -#define MA_MALLOC(sz) malloc((sz)) -#endif -#ifndef MA_REALLOC -#define MA_REALLOC(p, sz) realloc((p), (sz)) -#endif -#ifndef MA_FREE -#define MA_FREE(p) free((p)) -#endif - -static MA_INLINE void ma_zero_memory_default(void* p, size_t sz) -{ - if (p == NULL) { - MA_ASSERT(sz == 0); /* If this is triggered there's an error with the calling code. */ - return; - } - - if (sz > 0) { - memset(p, 0, sz); - } -} - - -#ifndef MA_ZERO_MEMORY -#define MA_ZERO_MEMORY(p, sz) ma_zero_memory_default((p), (sz)) -#endif -#ifndef MA_COPY_MEMORY -#define MA_COPY_MEMORY(dst, src, sz) memcpy((dst), (src), (sz)) -#endif -#ifndef MA_MOVE_MEMORY -#define MA_MOVE_MEMORY(dst, src, sz) memmove((dst), (src), (sz)) -#endif - -#define MA_ZERO_OBJECT(p) MA_ZERO_MEMORY((p), sizeof(*(p))) - -#define ma_countof(x) (sizeof(x) / sizeof(x[0])) -#define ma_max(x, y) (((x) > (y)) ? (x) : (y)) -#define ma_min(x, y) (((x) < (y)) ? (x) : (y)) -#define ma_abs(x) (((x) > 0) ? (x) : -(x)) -#define ma_clamp(x, lo, hi) (ma_max(lo, ma_min(x, hi))) -#define ma_offset_ptr(p, offset) (((ma_uint8*)(p)) + (offset)) -#define ma_align(x, a) (((x) + ((a)-1)) & ~((a)-1)) -#define ma_align_64(x) ma_align(x, 8) - -#define ma_buffer_frame_capacity(buffer, channels, format) (sizeof(buffer) / ma_get_bytes_per_sample(format) / (channels)) - -static MA_INLINE double ma_sind(double x) -{ - /* TODO: Implement custom sin(x). */ - return sin(x); -} - -static MA_INLINE double ma_expd(double x) -{ - /* TODO: Implement custom exp(x). */ - return exp(x); -} - -static MA_INLINE double ma_logd(double x) -{ - /* TODO: Implement custom log(x). */ - return log(x); -} - -static MA_INLINE double ma_powd(double x, double y) -{ - /* TODO: Implement custom pow(x, y). */ - return pow(x, y); -} - -static MA_INLINE double ma_sqrtd(double x) -{ - /* TODO: Implement custom sqrt(x). */ - return sqrt(x); -} - - -static MA_INLINE float ma_rsqrtf(float x) -{ - #if defined(MA_SUPPORT_SSE2) && !defined(MA_NO_SSE2) && (defined(MA_X64) || (defined(_M_IX86_FP) && _M_IX86_FP == 2) || defined(__SSE2__)) - { - /* - For SSE we can use RSQRTSS. - - This Stack Overflow post suggests that compilers don't necessarily generate optimal code - when using intrinsics: - - https://web.archive.org/web/20221211012522/https://stackoverflow.com/questions/32687079/getting-fewest-instructions-for-rsqrtss-wrapper - - I'm going to do something similar here, but a bit simpler. - */ - #if defined(__GNUC__) || defined(__clang__) - { - float result; - __asm__ __volatile__("rsqrtss %1, %0" : "=x"(result) : "x"(x)); - return result; - } - #else - { - return _mm_cvtss_f32(_mm_rsqrt_ss(_mm_set_ps1(x))); - } - #endif - } - #else - { - return 1 / (float)ma_sqrtd(x); - } - #endif -} - - -static MA_INLINE float ma_sinf(float x) -{ - return (float)ma_sind((float)x); -} - -static MA_INLINE double ma_cosd(double x) -{ - return ma_sind((MA_PI_D*0.5) - x); -} - -static MA_INLINE float ma_cosf(float x) -{ - return (float)ma_cosd((float)x); -} - -static MA_INLINE double ma_log10d(double x) -{ - return ma_logd(x) * 0.43429448190325182765; -} - -static MA_INLINE float ma_powf(float x, float y) -{ - return (float)ma_powd((double)x, (double)y); -} - -static MA_INLINE float ma_log10f(float x) -{ - return (float)ma_log10d((double)x); -} - - -static MA_INLINE double ma_degrees_to_radians(double degrees) -{ - return degrees * 0.01745329252; -} - -static MA_INLINE double ma_radians_to_degrees(double radians) -{ - return radians * 57.295779512896; -} - -static MA_INLINE float ma_degrees_to_radians_f(float degrees) -{ - return degrees * 0.01745329252f; -} - -static MA_INLINE float ma_radians_to_degrees_f(float radians) -{ - return radians * 57.295779512896f; -} - - -/* -Return Values: - 0: Success - 22: EINVAL - 34: ERANGE - -Not using symbolic constants for errors because I want to avoid #including errno.h - -These are marked as no-inline because of some bad code generation by Clang. None of these functions -are used in any performance-critical code within miniaudio. -*/ -MA_API MA_NO_INLINE int ma_strcpy_s(char* dst, size_t dstSizeInBytes, const char* src) -{ - size_t i; - - if (dst == 0) { - return 22; - } - if (dstSizeInBytes == 0) { - return 34; - } - if (src == 0) { - dst[0] = '\0'; - return 22; - } - - for (i = 0; i < dstSizeInBytes && src[i] != '\0'; ++i) { - dst[i] = src[i]; - } - - if (i < dstSizeInBytes) { - dst[i] = '\0'; - return 0; - } - - dst[0] = '\0'; - return 34; -} - -MA_API MA_NO_INLINE int ma_wcscpy_s(wchar_t* dst, size_t dstCap, const wchar_t* src) -{ - size_t i; - - if (dst == 0) { - return 22; - } - if (dstCap == 0) { - return 34; - } - if (src == 0) { - dst[0] = '\0'; - return 22; - } - - for (i = 0; i < dstCap && src[i] != '\0'; ++i) { - dst[i] = src[i]; - } - - if (i < dstCap) { - dst[i] = '\0'; - return 0; - } - - dst[0] = '\0'; - return 34; -} - - -MA_API MA_NO_INLINE int ma_strncpy_s(char* dst, size_t dstSizeInBytes, const char* src, size_t count) -{ - size_t maxcount; - size_t i; - - if (dst == 0) { - return 22; - } - if (dstSizeInBytes == 0) { - return 34; - } - if (src == 0) { - dst[0] = '\0'; - return 22; - } - - maxcount = count; - if (count == ((size_t)-1) || count >= dstSizeInBytes) { /* -1 = _TRUNCATE */ - maxcount = dstSizeInBytes - 1; - } - - for (i = 0; i < maxcount && src[i] != '\0'; ++i) { - dst[i] = src[i]; - } - - if (src[i] == '\0' || i == count || count == ((size_t)-1)) { - dst[i] = '\0'; - return 0; - } - - dst[0] = '\0'; - return 34; -} - -MA_API MA_NO_INLINE int ma_strcat_s(char* dst, size_t dstSizeInBytes, const char* src) -{ - char* dstorig; - - if (dst == 0) { - return 22; - } - if (dstSizeInBytes == 0) { - return 34; - } - if (src == 0) { - dst[0] = '\0'; - return 22; - } - - dstorig = dst; - - while (dstSizeInBytes > 0 && dst[0] != '\0') { - dst += 1; - dstSizeInBytes -= 1; - } - - if (dstSizeInBytes == 0) { - return 22; /* Unterminated. */ - } - - - while (dstSizeInBytes > 0 && src[0] != '\0') { - *dst++ = *src++; - dstSizeInBytes -= 1; - } - - if (dstSizeInBytes > 0) { - dst[0] = '\0'; - } else { - dstorig[0] = '\0'; - return 34; - } - - return 0; -} - -MA_API MA_NO_INLINE int ma_strncat_s(char* dst, size_t dstSizeInBytes, const char* src, size_t count) -{ - char* dstorig; - - if (dst == 0) { - return 22; - } - if (dstSizeInBytes == 0) { - return 34; - } - if (src == 0) { - return 22; - } - - dstorig = dst; - - while (dstSizeInBytes > 0 && dst[0] != '\0') { - dst += 1; - dstSizeInBytes -= 1; - } - - if (dstSizeInBytes == 0) { - return 22; /* Unterminated. */ - } - - - if (count == ((size_t)-1)) { /* _TRUNCATE */ - count = dstSizeInBytes - 1; - } - - while (dstSizeInBytes > 0 && src[0] != '\0' && count > 0) { - *dst++ = *src++; - dstSizeInBytes -= 1; - count -= 1; - } - - if (dstSizeInBytes > 0) { - dst[0] = '\0'; - } else { - dstorig[0] = '\0'; - return 34; - } - - return 0; -} - -MA_API MA_NO_INLINE int ma_itoa_s(int value, char* dst, size_t dstSizeInBytes, int radix) -{ - int sign; - unsigned int valueU; - char* dstEnd; - - if (dst == NULL || dstSizeInBytes == 0) { - return 22; - } - if (radix < 2 || radix > 36) { - dst[0] = '\0'; - return 22; - } - - sign = (value < 0 && radix == 10) ? -1 : 1; /* The negative sign is only used when the base is 10. */ - - if (value < 0) { - valueU = -value; - } else { - valueU = value; - } - - dstEnd = dst; - do - { - int remainder = valueU % radix; - if (remainder > 9) { - *dstEnd = (char)((remainder - 10) + 'a'); - } else { - *dstEnd = (char)(remainder + '0'); - } - - dstEnd += 1; - dstSizeInBytes -= 1; - valueU /= radix; - } while (dstSizeInBytes > 0 && valueU > 0); - - if (dstSizeInBytes == 0) { - dst[0] = '\0'; - return 22; /* Ran out of room in the output buffer. */ - } - - if (sign < 0) { - *dstEnd++ = '-'; - dstSizeInBytes -= 1; - } - - if (dstSizeInBytes == 0) { - dst[0] = '\0'; - return 22; /* Ran out of room in the output buffer. */ - } - - *dstEnd = '\0'; - - - /* At this point the string will be reversed. */ - dstEnd -= 1; - while (dst < dstEnd) { - char temp = *dst; - *dst = *dstEnd; - *dstEnd = temp; - - dst += 1; - dstEnd -= 1; - } - - return 0; -} - -MA_API MA_NO_INLINE int ma_strcmp(const char* str1, const char* str2) -{ - if (str1 == str2) return 0; - - /* These checks differ from the standard implementation. It's not important, but I prefer it just for sanity. */ - if (str1 == NULL) return -1; - if (str2 == NULL) return 1; - - for (;;) { - if (str1[0] == '\0') { - break; - } - if (str1[0] != str2[0]) { - break; - } - - str1 += 1; - str2 += 1; - } - - return ((unsigned char*)str1)[0] - ((unsigned char*)str2)[0]; -} - -MA_API MA_NO_INLINE int ma_strappend(char* dst, size_t dstSize, const char* srcA, const char* srcB) -{ - int result; - - result = ma_strncpy_s(dst, dstSize, srcA, (size_t)-1); - if (result != 0) { - return result; - } - - result = ma_strncat_s(dst, dstSize, srcB, (size_t)-1); - if (result != 0) { - return result; - } - - return result; -} - -MA_API MA_NO_INLINE char* ma_copy_string(const char* src, const ma_allocation_callbacks* pAllocationCallbacks) -{ - size_t sz; - char* dst; - - if (src == NULL) { - return NULL; - } - - sz = strlen(src)+1; - dst = (char*)ma_malloc(sz, pAllocationCallbacks); - if (dst == NULL) { - return NULL; - } - - ma_strcpy_s(dst, sz, src); - - return dst; -} - -MA_API MA_NO_INLINE wchar_t* ma_copy_string_w(const wchar_t* src, const ma_allocation_callbacks* pAllocationCallbacks) -{ - size_t sz = wcslen(src)+1; - wchar_t* dst = (wchar_t*)ma_malloc(sz * sizeof(*dst), pAllocationCallbacks); - if (dst == NULL) { - return NULL; - } - - ma_wcscpy_s(dst, sz, src); - - return dst; -} - - - -#include -static ma_result ma_result_from_errno(int e) -{ - if (e == 0) { - return MA_SUCCESS; - } -#ifdef EPERM - else if (e == EPERM) { return MA_INVALID_OPERATION; } -#endif -#ifdef ENOENT - else if (e == ENOENT) { return MA_DOES_NOT_EXIST; } -#endif -#ifdef ESRCH - else if (e == ESRCH) { return MA_DOES_NOT_EXIST; } -#endif -#ifdef EINTR - else if (e == EINTR) { return MA_INTERRUPT; } -#endif -#ifdef EIO - else if (e == EIO) { return MA_IO_ERROR; } -#endif -#ifdef ENXIO - else if (e == ENXIO) { return MA_DOES_NOT_EXIST; } -#endif -#ifdef E2BIG - else if (e == E2BIG) { return MA_INVALID_ARGS; } -#endif -#ifdef ENOEXEC - else if (e == ENOEXEC) { return MA_INVALID_FILE; } -#endif -#ifdef EBADF - else if (e == EBADF) { return MA_INVALID_FILE; } -#endif -#ifdef ECHILD - else if (e == ECHILD) { return MA_ERROR; } -#endif -#ifdef EAGAIN - else if (e == EAGAIN) { return MA_UNAVAILABLE; } -#endif -#ifdef ENOMEM - else if (e == ENOMEM) { return MA_OUT_OF_MEMORY; } -#endif -#ifdef EACCES - else if (e == EACCES) { return MA_ACCESS_DENIED; } -#endif -#ifdef EFAULT - else if (e == EFAULT) { return MA_BAD_ADDRESS; } -#endif -#ifdef ENOTBLK - else if (e == ENOTBLK) { return MA_ERROR; } -#endif -#ifdef EBUSY - else if (e == EBUSY) { return MA_BUSY; } -#endif -#ifdef EEXIST - else if (e == EEXIST) { return MA_ALREADY_EXISTS; } -#endif -#ifdef EXDEV - else if (e == EXDEV) { return MA_ERROR; } -#endif -#ifdef ENODEV - else if (e == ENODEV) { return MA_DOES_NOT_EXIST; } -#endif -#ifdef ENOTDIR - else if (e == ENOTDIR) { return MA_NOT_DIRECTORY; } -#endif -#ifdef EISDIR - else if (e == EISDIR) { return MA_IS_DIRECTORY; } -#endif -#ifdef EINVAL - else if (e == EINVAL) { return MA_INVALID_ARGS; } -#endif -#ifdef ENFILE - else if (e == ENFILE) { return MA_TOO_MANY_OPEN_FILES; } -#endif -#ifdef EMFILE - else if (e == EMFILE) { return MA_TOO_MANY_OPEN_FILES; } -#endif -#ifdef ENOTTY - else if (e == ENOTTY) { return MA_INVALID_OPERATION; } -#endif -#ifdef ETXTBSY - else if (e == ETXTBSY) { return MA_BUSY; } -#endif -#ifdef EFBIG - else if (e == EFBIG) { return MA_TOO_BIG; } -#endif -#ifdef ENOSPC - else if (e == ENOSPC) { return MA_NO_SPACE; } -#endif -#ifdef ESPIPE - else if (e == ESPIPE) { return MA_BAD_SEEK; } -#endif -#ifdef EROFS - else if (e == EROFS) { return MA_ACCESS_DENIED; } -#endif -#ifdef EMLINK - else if (e == EMLINK) { return MA_TOO_MANY_LINKS; } -#endif -#ifdef EPIPE - else if (e == EPIPE) { return MA_BAD_PIPE; } -#endif -#ifdef EDOM - else if (e == EDOM) { return MA_OUT_OF_RANGE; } -#endif -#ifdef ERANGE - else if (e == ERANGE) { return MA_OUT_OF_RANGE; } -#endif -#ifdef EDEADLK - else if (e == EDEADLK) { return MA_DEADLOCK; } -#endif -#ifdef ENAMETOOLONG - else if (e == ENAMETOOLONG) { return MA_PATH_TOO_LONG; } -#endif -#ifdef ENOLCK - else if (e == ENOLCK) { return MA_ERROR; } -#endif -#ifdef ENOSYS - else if (e == ENOSYS) { return MA_NOT_IMPLEMENTED; } -#endif -#ifdef ENOTEMPTY - else if (e == ENOTEMPTY) { return MA_DIRECTORY_NOT_EMPTY; } -#endif -#ifdef ELOOP - else if (e == ELOOP) { return MA_TOO_MANY_LINKS; } -#endif -#ifdef ENOMSG - else if (e == ENOMSG) { return MA_NO_MESSAGE; } -#endif -#ifdef EIDRM - else if (e == EIDRM) { return MA_ERROR; } -#endif -#ifdef ECHRNG - else if (e == ECHRNG) { return MA_ERROR; } -#endif -#ifdef EL2NSYNC - else if (e == EL2NSYNC) { return MA_ERROR; } -#endif -#ifdef EL3HLT - else if (e == EL3HLT) { return MA_ERROR; } -#endif -#ifdef EL3RST - else if (e == EL3RST) { return MA_ERROR; } -#endif -#ifdef ELNRNG - else if (e == ELNRNG) { return MA_OUT_OF_RANGE; } -#endif -#ifdef EUNATCH - else if (e == EUNATCH) { return MA_ERROR; } -#endif -#ifdef ENOCSI - else if (e == ENOCSI) { return MA_ERROR; } -#endif -#ifdef EL2HLT - else if (e == EL2HLT) { return MA_ERROR; } -#endif -#ifdef EBADE - else if (e == EBADE) { return MA_ERROR; } -#endif -#ifdef EBADR - else if (e == EBADR) { return MA_ERROR; } -#endif -#ifdef EXFULL - else if (e == EXFULL) { return MA_ERROR; } -#endif -#ifdef ENOANO - else if (e == ENOANO) { return MA_ERROR; } -#endif -#ifdef EBADRQC - else if (e == EBADRQC) { return MA_ERROR; } -#endif -#ifdef EBADSLT - else if (e == EBADSLT) { return MA_ERROR; } -#endif -#ifdef EBFONT - else if (e == EBFONT) { return MA_INVALID_FILE; } -#endif -#ifdef ENOSTR - else if (e == ENOSTR) { return MA_ERROR; } -#endif -#ifdef ENODATA - else if (e == ENODATA) { return MA_NO_DATA_AVAILABLE; } -#endif -#ifdef ETIME - else if (e == ETIME) { return MA_TIMEOUT; } -#endif -#ifdef ENOSR - else if (e == ENOSR) { return MA_NO_DATA_AVAILABLE; } -#endif -#ifdef ENONET - else if (e == ENONET) { return MA_NO_NETWORK; } -#endif -#ifdef ENOPKG - else if (e == ENOPKG) { return MA_ERROR; } -#endif -#ifdef EREMOTE - else if (e == EREMOTE) { return MA_ERROR; } -#endif -#ifdef ENOLINK - else if (e == ENOLINK) { return MA_ERROR; } -#endif -#ifdef EADV - else if (e == EADV) { return MA_ERROR; } -#endif -#ifdef ESRMNT - else if (e == ESRMNT) { return MA_ERROR; } -#endif -#ifdef ECOMM - else if (e == ECOMM) { return MA_ERROR; } -#endif -#ifdef EPROTO - else if (e == EPROTO) { return MA_ERROR; } -#endif -#ifdef EMULTIHOP - else if (e == EMULTIHOP) { return MA_ERROR; } -#endif -#ifdef EDOTDOT - else if (e == EDOTDOT) { return MA_ERROR; } -#endif -#ifdef EBADMSG - else if (e == EBADMSG) { return MA_BAD_MESSAGE; } -#endif -#ifdef EOVERFLOW - else if (e == EOVERFLOW) { return MA_TOO_BIG; } -#endif -#ifdef ENOTUNIQ - else if (e == ENOTUNIQ) { return MA_NOT_UNIQUE; } -#endif -#ifdef EBADFD - else if (e == EBADFD) { return MA_ERROR; } -#endif -#ifdef EREMCHG - else if (e == EREMCHG) { return MA_ERROR; } -#endif -#ifdef ELIBACC - else if (e == ELIBACC) { return MA_ACCESS_DENIED; } -#endif -#ifdef ELIBBAD - else if (e == ELIBBAD) { return MA_INVALID_FILE; } -#endif -#ifdef ELIBSCN - else if (e == ELIBSCN) { return MA_INVALID_FILE; } -#endif -#ifdef ELIBMAX - else if (e == ELIBMAX) { return MA_ERROR; } -#endif -#ifdef ELIBEXEC - else if (e == ELIBEXEC) { return MA_ERROR; } -#endif -#ifdef EILSEQ - else if (e == EILSEQ) { return MA_INVALID_DATA; } -#endif -#ifdef ERESTART - else if (e == ERESTART) { return MA_ERROR; } -#endif -#ifdef ESTRPIPE - else if (e == ESTRPIPE) { return MA_ERROR; } -#endif -#ifdef EUSERS - else if (e == EUSERS) { return MA_ERROR; } -#endif -#ifdef ENOTSOCK - else if (e == ENOTSOCK) { return MA_NOT_SOCKET; } -#endif -#ifdef EDESTADDRREQ - else if (e == EDESTADDRREQ) { return MA_NO_ADDRESS; } -#endif -#ifdef EMSGSIZE - else if (e == EMSGSIZE) { return MA_TOO_BIG; } -#endif -#ifdef EPROTOTYPE - else if (e == EPROTOTYPE) { return MA_BAD_PROTOCOL; } -#endif -#ifdef ENOPROTOOPT - else if (e == ENOPROTOOPT) { return MA_PROTOCOL_UNAVAILABLE; } -#endif -#ifdef EPROTONOSUPPORT - else if (e == EPROTONOSUPPORT) { return MA_PROTOCOL_NOT_SUPPORTED; } -#endif -#ifdef ESOCKTNOSUPPORT - else if (e == ESOCKTNOSUPPORT) { return MA_SOCKET_NOT_SUPPORTED; } -#endif -#ifdef EOPNOTSUPP - else if (e == EOPNOTSUPP) { return MA_INVALID_OPERATION; } -#endif -#ifdef EPFNOSUPPORT - else if (e == EPFNOSUPPORT) { return MA_PROTOCOL_FAMILY_NOT_SUPPORTED; } -#endif -#ifdef EAFNOSUPPORT - else if (e == EAFNOSUPPORT) { return MA_ADDRESS_FAMILY_NOT_SUPPORTED; } -#endif -#ifdef EADDRINUSE - else if (e == EADDRINUSE) { return MA_ALREADY_IN_USE; } -#endif -#ifdef EADDRNOTAVAIL - else if (e == EADDRNOTAVAIL) { return MA_ERROR; } -#endif -#ifdef ENETDOWN - else if (e == ENETDOWN) { return MA_NO_NETWORK; } -#endif -#ifdef ENETUNREACH - else if (e == ENETUNREACH) { return MA_NO_NETWORK; } -#endif -#ifdef ENETRESET - else if (e == ENETRESET) { return MA_NO_NETWORK; } -#endif -#ifdef ECONNABORTED - else if (e == ECONNABORTED) { return MA_NO_NETWORK; } -#endif -#ifdef ECONNRESET - else if (e == ECONNRESET) { return MA_CONNECTION_RESET; } -#endif -#ifdef ENOBUFS - else if (e == ENOBUFS) { return MA_NO_SPACE; } -#endif -#ifdef EISCONN - else if (e == EISCONN) { return MA_ALREADY_CONNECTED; } -#endif -#ifdef ENOTCONN - else if (e == ENOTCONN) { return MA_NOT_CONNECTED; } -#endif -#ifdef ESHUTDOWN - else if (e == ESHUTDOWN) { return MA_ERROR; } -#endif -#ifdef ETOOMANYREFS - else if (e == ETOOMANYREFS) { return MA_ERROR; } -#endif -#ifdef ETIMEDOUT - else if (e == ETIMEDOUT) { return MA_TIMEOUT; } -#endif -#ifdef ECONNREFUSED - else if (e == ECONNREFUSED) { return MA_CONNECTION_REFUSED; } -#endif -#ifdef EHOSTDOWN - else if (e == EHOSTDOWN) { return MA_NO_HOST; } -#endif -#ifdef EHOSTUNREACH - else if (e == EHOSTUNREACH) { return MA_NO_HOST; } -#endif -#ifdef EALREADY - else if (e == EALREADY) { return MA_IN_PROGRESS; } -#endif -#ifdef EINPROGRESS - else if (e == EINPROGRESS) { return MA_IN_PROGRESS; } -#endif -#ifdef ESTALE - else if (e == ESTALE) { return MA_INVALID_FILE; } -#endif -#ifdef EUCLEAN - else if (e == EUCLEAN) { return MA_ERROR; } -#endif -#ifdef ENOTNAM - else if (e == ENOTNAM) { return MA_ERROR; } -#endif -#ifdef ENAVAIL - else if (e == ENAVAIL) { return MA_ERROR; } -#endif -#ifdef EISNAM - else if (e == EISNAM) { return MA_ERROR; } -#endif -#ifdef EREMOTEIO - else if (e == EREMOTEIO) { return MA_IO_ERROR; } -#endif -#ifdef EDQUOT - else if (e == EDQUOT) { return MA_NO_SPACE; } -#endif -#ifdef ENOMEDIUM - else if (e == ENOMEDIUM) { return MA_DOES_NOT_EXIST; } -#endif -#ifdef EMEDIUMTYPE - else if (e == EMEDIUMTYPE) { return MA_ERROR; } -#endif -#ifdef ECANCELED - else if (e == ECANCELED) { return MA_CANCELLED; } -#endif -#ifdef ENOKEY - else if (e == ENOKEY) { return MA_ERROR; } -#endif -#ifdef EKEYEXPIRED - else if (e == EKEYEXPIRED) { return MA_ERROR; } -#endif -#ifdef EKEYREVOKED - else if (e == EKEYREVOKED) { return MA_ERROR; } -#endif -#ifdef EKEYREJECTED - else if (e == EKEYREJECTED) { return MA_ERROR; } -#endif -#ifdef EOWNERDEAD - else if (e == EOWNERDEAD) { return MA_ERROR; } -#endif -#ifdef ENOTRECOVERABLE - else if (e == ENOTRECOVERABLE) { return MA_ERROR; } -#endif -#ifdef ERFKILL - else if (e == ERFKILL) { return MA_ERROR; } -#endif -#ifdef EHWPOISON - else if (e == EHWPOISON) { return MA_ERROR; } -#endif - else { - return MA_ERROR; - } -} - -MA_API ma_result ma_fopen(FILE** ppFile, const char* pFilePath, const char* pOpenMode) -{ -#if defined(_MSC_VER) && _MSC_VER >= 1400 - errno_t err; -#endif - - if (ppFile != NULL) { - *ppFile = NULL; /* Safety. */ - } - - if (pFilePath == NULL || pOpenMode == NULL || ppFile == NULL) { - return MA_INVALID_ARGS; - } - -#if defined(_MSC_VER) && _MSC_VER >= 1400 - err = fopen_s(ppFile, pFilePath, pOpenMode); - if (err != 0) { - return ma_result_from_errno(err); - } -#else -#if defined(_WIN32) || defined(__APPLE__) - *ppFile = fopen(pFilePath, pOpenMode); -#else - #if defined(_FILE_OFFSET_BITS) && _FILE_OFFSET_BITS == 64 && defined(_LARGEFILE64_SOURCE) - *ppFile = fopen64(pFilePath, pOpenMode); - #else - *ppFile = fopen(pFilePath, pOpenMode); - #endif -#endif - if (*ppFile == NULL) { - ma_result result = ma_result_from_errno(errno); - if (result == MA_SUCCESS) { - result = MA_ERROR; /* Just a safety check to make sure we never ever return success when pFile == NULL. */ - } - - return result; - } -#endif - - return MA_SUCCESS; -} - - - -/* -_wfopen() isn't always available in all compilation environments. - - * Windows only. - * MSVC seems to support it universally as far back as VC6 from what I can tell (haven't checked further back). - * MinGW-64 (both 32- and 64-bit) seems to support it. - * MinGW wraps it in !defined(__STRICT_ANSI__). - * OpenWatcom wraps it in !defined(_NO_EXT_KEYS). - -This can be reviewed as compatibility issues arise. The preference is to use _wfopen_s() and _wfopen() as opposed to the wcsrtombs() -fallback, so if you notice your compiler not detecting this properly I'm happy to look at adding support. -*/ -#if defined(_WIN32) - #if defined(_MSC_VER) || defined(__MINGW64__) || (!defined(__STRICT_ANSI__) && !defined(_NO_EXT_KEYS)) - #define MA_HAS_WFOPEN - #endif -#endif - -MA_API ma_result ma_wfopen(FILE** ppFile, const wchar_t* pFilePath, const wchar_t* pOpenMode, const ma_allocation_callbacks* pAllocationCallbacks) -{ - if (ppFile != NULL) { - *ppFile = NULL; /* Safety. */ - } - - if (pFilePath == NULL || pOpenMode == NULL || ppFile == NULL) { - return MA_INVALID_ARGS; - } - -#if defined(MA_HAS_WFOPEN) - { - /* Use _wfopen() on Windows. */ - #if defined(_MSC_VER) && _MSC_VER >= 1400 - errno_t err = _wfopen_s(ppFile, pFilePath, pOpenMode); - if (err != 0) { - return ma_result_from_errno(err); - } - #else - *ppFile = _wfopen(pFilePath, pOpenMode); - if (*ppFile == NULL) { - return ma_result_from_errno(errno); - } - #endif - (void)pAllocationCallbacks; - } -#else - /* - Use fopen() on anything other than Windows. Requires a conversion. This is annoying because fopen() is locale specific. The only real way I can - think of to do this is with wcsrtombs(). Note that wcstombs() is apparently not thread-safe because it uses a static global mbstate_t object for - maintaining state. I've checked this with -std=c89 and it works, but if somebody get's a compiler error I'll look into improving compatibility. - */ - { - mbstate_t mbs; - size_t lenMB; - const wchar_t* pFilePathTemp = pFilePath; - char* pFilePathMB = NULL; - char pOpenModeMB[32] = {0}; - - /* Get the length first. */ - MA_ZERO_OBJECT(&mbs); - lenMB = wcsrtombs(NULL, &pFilePathTemp, 0, &mbs); - if (lenMB == (size_t)-1) { - return ma_result_from_errno(errno); - } - - pFilePathMB = (char*)ma_malloc(lenMB + 1, pAllocationCallbacks); - if (pFilePathMB == NULL) { - return MA_OUT_OF_MEMORY; - } - - pFilePathTemp = pFilePath; - MA_ZERO_OBJECT(&mbs); - wcsrtombs(pFilePathMB, &pFilePathTemp, lenMB + 1, &mbs); - - /* The open mode should always consist of ASCII characters so we should be able to do a trivial conversion. */ - { - size_t i = 0; - for (;;) { - if (pOpenMode[i] == 0) { - pOpenModeMB[i] = '\0'; - break; - } - - pOpenModeMB[i] = (char)pOpenMode[i]; - i += 1; - } - } - - *ppFile = fopen(pFilePathMB, pOpenModeMB); - - ma_free(pFilePathMB, pAllocationCallbacks); - } - - if (*ppFile == NULL) { - return MA_ERROR; - } -#endif - - return MA_SUCCESS; -} - - - -static MA_INLINE void ma_copy_memory_64(void* dst, const void* src, ma_uint64 sizeInBytes) -{ -#if 0xFFFFFFFFFFFFFFFF <= MA_SIZE_MAX - MA_COPY_MEMORY(dst, src, (size_t)sizeInBytes); -#else - while (sizeInBytes > 0) { - ma_uint64 bytesToCopyNow = sizeInBytes; - if (bytesToCopyNow > MA_SIZE_MAX) { - bytesToCopyNow = MA_SIZE_MAX; - } - - MA_COPY_MEMORY(dst, src, (size_t)bytesToCopyNow); /* Safe cast to size_t. */ - - sizeInBytes -= bytesToCopyNow; - dst = ( void*)(( ma_uint8*)dst + bytesToCopyNow); - src = (const void*)((const ma_uint8*)src + bytesToCopyNow); - } -#endif -} - -static MA_INLINE void ma_zero_memory_64(void* dst, ma_uint64 sizeInBytes) -{ -#if 0xFFFFFFFFFFFFFFFF <= MA_SIZE_MAX - MA_ZERO_MEMORY(dst, (size_t)sizeInBytes); -#else - while (sizeInBytes > 0) { - ma_uint64 bytesToZeroNow = sizeInBytes; - if (bytesToZeroNow > MA_SIZE_MAX) { - bytesToZeroNow = MA_SIZE_MAX; - } - - MA_ZERO_MEMORY(dst, (size_t)bytesToZeroNow); /* Safe cast to size_t. */ - - sizeInBytes -= bytesToZeroNow; - dst = (void*)((ma_uint8*)dst + bytesToZeroNow); - } -#endif -} - - -/* Thanks to good old Bit Twiddling Hacks for this one: http://graphics.stanford.edu/~seander/bithacks.html#RoundUpPowerOf2 */ -static MA_INLINE unsigned int ma_next_power_of_2(unsigned int x) -{ - x--; - x |= x >> 1; - x |= x >> 2; - x |= x >> 4; - x |= x >> 8; - x |= x >> 16; - x++; - - return x; -} - -static MA_INLINE unsigned int ma_prev_power_of_2(unsigned int x) -{ - return ma_next_power_of_2(x) >> 1; -} - -static MA_INLINE unsigned int ma_round_to_power_of_2(unsigned int x) -{ - unsigned int prev = ma_prev_power_of_2(x); - unsigned int next = ma_next_power_of_2(x); - if ((next - x) > (x - prev)) { - return prev; - } else { - return next; - } -} - -static MA_INLINE unsigned int ma_count_set_bits(unsigned int x) -{ - unsigned int count = 0; - while (x != 0) { - if (x & 1) { - count += 1; - } - - x = x >> 1; - } - - return count; -} - - - -/************************************************************************************************************************************************************** - -Allocation Callbacks - -**************************************************************************************************************************************************************/ -static void* ma__malloc_default(size_t sz, void* pUserData) -{ - (void)pUserData; - return MA_MALLOC(sz); -} - -static void* ma__realloc_default(void* p, size_t sz, void* pUserData) -{ - (void)pUserData; - return MA_REALLOC(p, sz); -} - -static void ma__free_default(void* p, void* pUserData) -{ - (void)pUserData; - MA_FREE(p); -} - -static ma_allocation_callbacks ma_allocation_callbacks_init_default(void) -{ - ma_allocation_callbacks callbacks; - callbacks.pUserData = NULL; - callbacks.onMalloc = ma__malloc_default; - callbacks.onRealloc = ma__realloc_default; - callbacks.onFree = ma__free_default; - - return callbacks; -} - -static ma_result ma_allocation_callbacks_init_copy(ma_allocation_callbacks* pDst, const ma_allocation_callbacks* pSrc) -{ - if (pDst == NULL) { - return MA_INVALID_ARGS; - } - - if (pSrc == NULL) { - *pDst = ma_allocation_callbacks_init_default(); - } else { - if (pSrc->pUserData == NULL && pSrc->onFree == NULL && pSrc->onMalloc == NULL && pSrc->onRealloc == NULL) { - *pDst = ma_allocation_callbacks_init_default(); - } else { - if (pSrc->onFree == NULL || (pSrc->onMalloc == NULL && pSrc->onRealloc == NULL)) { - return MA_INVALID_ARGS; /* Invalid allocation callbacks. */ - } else { - *pDst = *pSrc; - } - } - } - - return MA_SUCCESS; -} - - - - -/************************************************************************************************************************************************************** - -Logging - -**************************************************************************************************************************************************************/ -MA_API const char* ma_log_level_to_string(ma_uint32 logLevel) -{ - switch (logLevel) - { - case MA_LOG_LEVEL_DEBUG: return "DEBUG"; - case MA_LOG_LEVEL_INFO: return "INFO"; - case MA_LOG_LEVEL_WARNING: return "WARNING"; - case MA_LOG_LEVEL_ERROR: return "ERROR"; - default: return "ERROR"; - } -} - -#if defined(MA_DEBUG_OUTPUT) -#if defined(MA_ANDROID) - #include -#endif - -/* Customize this to use a specific tag in __android_log_print() for debug output messages. */ -#ifndef MA_ANDROID_LOG_TAG -#define MA_ANDROID_LOG_TAG "miniaudio" -#endif - -void ma_log_callback_debug(void* pUserData, ma_uint32 level, const char* pMessage) -{ - (void)pUserData; - - /* Special handling for some platforms. */ - #if defined(MA_ANDROID) - { - /* Android. */ - __android_log_print(ANDROID_LOG_DEBUG, MA_ANDROID_LOG_TAG, "%s: %s", ma_log_level_to_string(level), pMessage); - } - #else - { - /* Everything else. */ - printf("%s: %s", ma_log_level_to_string(level), pMessage); - } - #endif -} -#endif - -MA_API ma_log_callback ma_log_callback_init(ma_log_callback_proc onLog, void* pUserData) -{ - ma_log_callback callback; - - MA_ZERO_OBJECT(&callback); - callback.onLog = onLog; - callback.pUserData = pUserData; - - return callback; -} - - -MA_API ma_result ma_log_init(const ma_allocation_callbacks* pAllocationCallbacks, ma_log* pLog) -{ - if (pLog == NULL) { - return MA_INVALID_ARGS; - } - - MA_ZERO_OBJECT(pLog); - ma_allocation_callbacks_init_copy(&pLog->allocationCallbacks, pAllocationCallbacks); - - /* We need a mutex for thread safety. */ - #ifndef MA_NO_THREADING - { - ma_result result = ma_mutex_init(&pLog->lock); - if (result != MA_SUCCESS) { - return result; - } - } - #endif - - /* If we're using debug output, enable it. */ - #if defined(MA_DEBUG_OUTPUT) - { - ma_log_register_callback(pLog, ma_log_callback_init(ma_log_callback_debug, NULL)); /* Doesn't really matter if this fails. */ - } - #endif - - return MA_SUCCESS; -} - -MA_API void ma_log_uninit(ma_log* pLog) -{ - if (pLog == NULL) { - return; - } - -#ifndef MA_NO_THREADING - ma_mutex_uninit(&pLog->lock); -#endif -} - -static void ma_log_lock(ma_log* pLog) -{ -#ifndef MA_NO_THREADING - ma_mutex_lock(&pLog->lock); -#else - (void)pLog; -#endif -} - -static void ma_log_unlock(ma_log* pLog) -{ -#ifndef MA_NO_THREADING - ma_mutex_unlock(&pLog->lock); -#else - (void)pLog; -#endif -} - -MA_API ma_result ma_log_register_callback(ma_log* pLog, ma_log_callback callback) -{ - ma_result result = MA_SUCCESS; - - if (pLog == NULL || callback.onLog == NULL) { - return MA_INVALID_ARGS; - } - - ma_log_lock(pLog); - { - if (pLog->callbackCount == ma_countof(pLog->callbacks)) { - result = MA_OUT_OF_MEMORY; /* Reached the maximum allowed log callbacks. */ - } else { - pLog->callbacks[pLog->callbackCount] = callback; - pLog->callbackCount += 1; - } - } - ma_log_unlock(pLog); - - return result; -} - -MA_API ma_result ma_log_unregister_callback(ma_log* pLog, ma_log_callback callback) -{ - if (pLog == NULL) { - return MA_INVALID_ARGS; - } - - ma_log_lock(pLog); - { - ma_uint32 iLog; - for (iLog = 0; iLog < pLog->callbackCount; ) { - if (pLog->callbacks[iLog].onLog == callback.onLog) { - /* Found. Move everything down a slot. */ - ma_uint32 jLog; - for (jLog = iLog; jLog < pLog->callbackCount-1; jLog += 1) { - pLog->callbacks[jLog] = pLog->callbacks[jLog + 1]; - } - - pLog->callbackCount -= 1; - } else { - /* Not found. */ - iLog += 1; - } - } - } - ma_log_unlock(pLog); - - return MA_SUCCESS; -} - -MA_API ma_result ma_log_post(ma_log* pLog, ma_uint32 level, const char* pMessage) -{ - if (pLog == NULL || pMessage == NULL) { - return MA_INVALID_ARGS; - } - - ma_log_lock(pLog); - { - ma_uint32 iLog; - for (iLog = 0; iLog < pLog->callbackCount; iLog += 1) { - if (pLog->callbacks[iLog].onLog) { - pLog->callbacks[iLog].onLog(pLog->callbacks[iLog].pUserData, level, pMessage); - } - } - } - ma_log_unlock(pLog); - - return MA_SUCCESS; -} - - -/* -We need to emulate _vscprintf() for the VC6 build. This can be more efficient, but since it's only VC6, and it's just a -logging function, I'm happy to keep this simple. In the VC6 build we can implement this in terms of _vsnprintf(). -*/ -#if defined(_MSC_VER) && _MSC_VER < 1900 -static int ma_vscprintf(const ma_allocation_callbacks* pAllocationCallbacks, const char* format, va_list args) -{ -#if _MSC_VER > 1200 - return _vscprintf(format, args); -#else - int result; - char* pTempBuffer = NULL; - size_t tempBufferCap = 1024; - - if (format == NULL) { - errno = EINVAL; - return -1; - } - - for (;;) { - char* pNewTempBuffer = (char*)ma_realloc(pTempBuffer, tempBufferCap, pAllocationCallbacks); - if (pNewTempBuffer == NULL) { - ma_free(pTempBuffer, pAllocationCallbacks); - errno = ENOMEM; - return -1; /* Out of memory. */ - } - - pTempBuffer = pNewTempBuffer; - - result = _vsnprintf(pTempBuffer, tempBufferCap, format, args); - ma_free(pTempBuffer, NULL); - - if (result != -1) { - break; /* Got it. */ - } - - /* Buffer wasn't big enough. Ideally it'd be nice to use an error code to know the reason for sure, but this is reliable enough. */ - tempBufferCap *= 2; - } - - return result; -#endif -} -#endif - -MA_API ma_result ma_log_postv(ma_log* pLog, ma_uint32 level, const char* pFormat, va_list args) -{ - if (pLog == NULL || pFormat == NULL) { - return MA_INVALID_ARGS; - } - - #if (defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L) || ((!defined(_MSC_VER) || _MSC_VER >= 1900) && !defined(__STRICT_ANSI__) && !defined(_NO_EXT_KEYS)) || (defined(__cplusplus) && __cplusplus >= 201103L) - { - ma_result result; - int length; - char pFormattedMessageStack[1024]; - char* pFormattedMessageHeap = NULL; - - /* First try formatting into our fixed sized stack allocated buffer. If this is too small we'll fallback to a heap allocation. */ - length = vsnprintf(pFormattedMessageStack, sizeof(pFormattedMessageStack), pFormat, args); - if (length < 0) { - return MA_INVALID_OPERATION; /* An error occurred when trying to convert the buffer. */ - } - - if ((size_t)length < sizeof(pFormattedMessageStack)) { - /* The string was written to the stack. */ - result = ma_log_post(pLog, level, pFormattedMessageStack); - } else { - /* The stack buffer was too small, try the heap. */ - pFormattedMessageHeap = (char*)ma_malloc(length + 1, &pLog->allocationCallbacks); - if (pFormattedMessageHeap == NULL) { - return MA_OUT_OF_MEMORY; - } - - length = vsnprintf(pFormattedMessageHeap, length + 1, pFormat, args); - if (length < 0) { - ma_free(pFormattedMessageHeap, &pLog->allocationCallbacks); - return MA_INVALID_OPERATION; - } - - result = ma_log_post(pLog, level, pFormattedMessageHeap); - ma_free(pFormattedMessageHeap, &pLog->allocationCallbacks); - } - - return result; - } - #else - { - /* - Without snprintf() we need to first measure the string and then heap allocate it. I'm only aware of Visual Studio having support for this without snprintf(), so we'll - need to restrict this branch to Visual Studio. For other compilers we need to just not support formatted logging because I don't want the security risk of overflowing - a fixed sized stack allocated buffer. - */ - #if defined(_MSC_VER) && _MSC_VER >= 1200 /* 1200 = VC6 */ - { - ma_result result; - int formattedLen; - char* pFormattedMessage = NULL; - va_list args2; - - #if _MSC_VER >= 1800 - { - va_copy(args2, args); - } - #else - { - args2 = args; - } - #endif - - formattedLen = ma_vscprintf(&pLog->allocationCallbacks, pFormat, args2); - va_end(args2); - - if (formattedLen <= 0) { - return MA_INVALID_OPERATION; - } - - pFormattedMessage = (char*)ma_malloc(formattedLen + 1, &pLog->allocationCallbacks); - if (pFormattedMessage == NULL) { - return MA_OUT_OF_MEMORY; - } - - /* We'll get errors on newer versions of Visual Studio if we try to use vsprintf(). */ - #if _MSC_VER >= 1400 /* 1400 = Visual Studio 2005 */ - { - vsprintf_s(pFormattedMessage, formattedLen + 1, pFormat, args); - } - #else - { - vsprintf(pFormattedMessage, pFormat, args); - } - #endif - - result = ma_log_post(pLog, level, pFormattedMessage); - ma_free(pFormattedMessage, &pLog->allocationCallbacks); - - return result; - } - #else - { - /* Can't do anything because we don't have a safe way of to emulate vsnprintf() without a manual solution. */ - (void)level; - (void)args; - - return MA_INVALID_OPERATION; - } - #endif - } - #endif -} - -MA_API ma_result ma_log_postf(ma_log* pLog, ma_uint32 level, const char* pFormat, ...) -{ - ma_result result; - va_list args; - - if (pLog == NULL || pFormat == NULL) { - return MA_INVALID_ARGS; - } - - va_start(args, pFormat); - { - result = ma_log_postv(pLog, level, pFormat, args); - } - va_end(args); - - return result; -} - - - -static MA_INLINE ma_uint8 ma_clip_u8(ma_int32 x) -{ - return (ma_uint8)(ma_clamp(x, -128, 127) + 128); -} - -static MA_INLINE ma_int16 ma_clip_s16(ma_int32 x) -{ - return (ma_int16)ma_clamp(x, -32768, 32767); -} - -static MA_INLINE ma_int64 ma_clip_s24(ma_int64 x) -{ - return (ma_int64)ma_clamp(x, -8388608, 8388607); -} - -static MA_INLINE ma_int32 ma_clip_s32(ma_int64 x) -{ - /* This dance is to silence warnings with -std=c89. A good compiler should be able to optimize this away. */ - ma_int64 clipMin; - ma_int64 clipMax; - clipMin = -((ma_int64)2147483647 + 1); - clipMax = (ma_int64)2147483647; - - return (ma_int32)ma_clamp(x, clipMin, clipMax); -} - -static MA_INLINE float ma_clip_f32(float x) -{ - if (x < -1) return -1; - if (x > +1) return +1; - return x; -} - - -static MA_INLINE float ma_mix_f32(float x, float y, float a) -{ - return x*(1-a) + y*a; -} -static MA_INLINE float ma_mix_f32_fast(float x, float y, float a) -{ - float r0 = (y - x); - float r1 = r0*a; - return x + r1; - /*return x + (y - x)*a;*/ -} - -#if defined(MA_SUPPORT_SSE2) -static MA_INLINE __m128 ma_mix_f32_fast__sse2(__m128 x, __m128 y, __m128 a) -{ - return _mm_add_ps(x, _mm_mul_ps(_mm_sub_ps(y, x), a)); -} -#endif -#if defined(MA_SUPPORT_AVX2) -static MA_INLINE __m256 ma_mix_f32_fast__avx2(__m256 x, __m256 y, __m256 a) -{ - return _mm256_add_ps(x, _mm256_mul_ps(_mm256_sub_ps(y, x), a)); -} -#endif -#if defined(MA_SUPPORT_NEON) -static MA_INLINE float32x4_t ma_mix_f32_fast__neon(float32x4_t x, float32x4_t y, float32x4_t a) -{ - return vaddq_f32(x, vmulq_f32(vsubq_f32(y, x), a)); -} -#endif - - -static MA_INLINE double ma_mix_f64(double x, double y, double a) -{ - return x*(1-a) + y*a; -} -static MA_INLINE double ma_mix_f64_fast(double x, double y, double a) -{ - return x + (y - x)*a; -} - -static MA_INLINE float ma_scale_to_range_f32(float x, float lo, float hi) -{ - return lo + x*(hi-lo); -} - - -/* -Greatest common factor using Euclid's algorithm iteratively. -*/ -static MA_INLINE ma_uint32 ma_gcf_u32(ma_uint32 a, ma_uint32 b) -{ - for (;;) { - if (b == 0) { - break; - } else { - ma_uint32 t = a; - a = b; - b = t % a; - } - } - - return a; -} - - -static ma_uint32 ma_ffs_32(ma_uint32 x) -{ - ma_uint32 i; - - /* Just a naive implementation just to get things working for now. Will optimize this later. */ - for (i = 0; i < 32; i += 1) { - if ((x & (1U << i)) != 0) { - return i; - } - } - - return i; -} - -static MA_INLINE ma_int16 ma_float_to_fixed_16(float x) -{ - return (ma_int16)(x * (1 << 8)); -} - - - -/* -Random Number Generation - -miniaudio uses the LCG random number generation algorithm. This is good enough for audio. - -Note that miniaudio's global LCG implementation uses global state which is _not_ thread-local. When this is called across -multiple threads, results will be unpredictable. However, it won't crash and results will still be random enough for -miniaudio's purposes. -*/ -#ifndef MA_DEFAULT_LCG_SEED -#define MA_DEFAULT_LCG_SEED 4321 -#endif - -#define MA_LCG_M 2147483647 -#define MA_LCG_A 48271 -#define MA_LCG_C 0 - -static ma_lcg g_maLCG = {MA_DEFAULT_LCG_SEED}; /* Non-zero initial seed. Use ma_seed() to use an explicit seed. */ - -static MA_INLINE void ma_lcg_seed(ma_lcg* pLCG, ma_int32 seed) -{ - MA_ASSERT(pLCG != NULL); - pLCG->state = seed; -} - -static MA_INLINE ma_int32 ma_lcg_rand_s32(ma_lcg* pLCG) -{ - pLCG->state = (MA_LCG_A * pLCG->state + MA_LCG_C) % MA_LCG_M; - return pLCG->state; -} - -static MA_INLINE ma_uint32 ma_lcg_rand_u32(ma_lcg* pLCG) -{ - return (ma_uint32)ma_lcg_rand_s32(pLCG); -} - -static MA_INLINE ma_int16 ma_lcg_rand_s16(ma_lcg* pLCG) -{ - return (ma_int16)(ma_lcg_rand_s32(pLCG) & 0xFFFF); -} - -static MA_INLINE double ma_lcg_rand_f64(ma_lcg* pLCG) -{ - return ma_lcg_rand_s32(pLCG) / (double)0x7FFFFFFF; -} - -static MA_INLINE float ma_lcg_rand_f32(ma_lcg* pLCG) -{ - return (float)ma_lcg_rand_f64(pLCG); -} - -static MA_INLINE float ma_lcg_rand_range_f32(ma_lcg* pLCG, float lo, float hi) -{ - return ma_scale_to_range_f32(ma_lcg_rand_f32(pLCG), lo, hi); -} - -static MA_INLINE ma_int32 ma_lcg_rand_range_s32(ma_lcg* pLCG, ma_int32 lo, ma_int32 hi) -{ - if (lo == hi) { - return lo; - } - - return lo + ma_lcg_rand_u32(pLCG) / (0xFFFFFFFF / (hi - lo + 1) + 1); -} - - - -static MA_INLINE void ma_seed(ma_int32 seed) -{ - ma_lcg_seed(&g_maLCG, seed); -} - -static MA_INLINE ma_int32 ma_rand_s32(void) -{ - return ma_lcg_rand_s32(&g_maLCG); -} - -static MA_INLINE ma_uint32 ma_rand_u32(void) -{ - return ma_lcg_rand_u32(&g_maLCG); -} - -static MA_INLINE double ma_rand_f64(void) -{ - return ma_lcg_rand_f64(&g_maLCG); -} - -static MA_INLINE float ma_rand_f32(void) -{ - return ma_lcg_rand_f32(&g_maLCG); -} - -static MA_INLINE float ma_rand_range_f32(float lo, float hi) -{ - return ma_lcg_rand_range_f32(&g_maLCG, lo, hi); -} - -static MA_INLINE ma_int32 ma_rand_range_s32(ma_int32 lo, ma_int32 hi) -{ - return ma_lcg_rand_range_s32(&g_maLCG, lo, hi); -} - - -static MA_INLINE float ma_dither_f32_rectangle(float ditherMin, float ditherMax) -{ - return ma_rand_range_f32(ditherMin, ditherMax); -} - -static MA_INLINE float ma_dither_f32_triangle(float ditherMin, float ditherMax) -{ - float a = ma_rand_range_f32(ditherMin, 0); - float b = ma_rand_range_f32(0, ditherMax); - return a + b; -} - -static MA_INLINE float ma_dither_f32(ma_dither_mode ditherMode, float ditherMin, float ditherMax) -{ - if (ditherMode == ma_dither_mode_rectangle) { - return ma_dither_f32_rectangle(ditherMin, ditherMax); - } - if (ditherMode == ma_dither_mode_triangle) { - return ma_dither_f32_triangle(ditherMin, ditherMax); - } - - return 0; -} - -static MA_INLINE ma_int32 ma_dither_s32(ma_dither_mode ditherMode, ma_int32 ditherMin, ma_int32 ditherMax) -{ - if (ditherMode == ma_dither_mode_rectangle) { - ma_int32 a = ma_rand_range_s32(ditherMin, ditherMax); - return a; - } - if (ditherMode == ma_dither_mode_triangle) { - ma_int32 a = ma_rand_range_s32(ditherMin, 0); - ma_int32 b = ma_rand_range_s32(0, ditherMax); - return a + b; - } - - return 0; -} - - -/************************************************************************************************************************************************************** - -Atomics - -**************************************************************************************************************************************************************/ -/* c89atomic.h begin */ -#ifndef ma_atomic_h -#if defined(__cplusplus) -extern "C" { -#endif -#if defined(__clang__) || (defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6))) - #pragma GCC diagnostic push - #pragma GCC diagnostic ignored "-Wlong-long" - #if defined(__clang__) - #pragma GCC diagnostic ignored "-Wc++11-long-long" - #endif -#endif -typedef int ma_atomic_memory_order; -#define MA_ATOMIC_HAS_8 -#define MA_ATOMIC_HAS_16 -#define MA_ATOMIC_HAS_32 -#define MA_ATOMIC_HAS_64 -#if (defined(_MSC_VER) ) || defined(__WATCOMC__) || defined(__DMC__) - #define MA_ATOMIC_MSVC_ARM_INTRINSIC(dst, src, order, intrin, ma_atomicType, msvcType) \ - ma_atomicType result; \ - switch (order) \ - { \ - case ma_atomic_memory_order_relaxed: \ - { \ - result = (ma_atomicType)intrin##_nf((volatile msvcType*)dst, (msvcType)src); \ - } break; \ - case ma_atomic_memory_order_consume: \ - case ma_atomic_memory_order_acquire: \ - { \ - result = (ma_atomicType)intrin##_acq((volatile msvcType*)dst, (msvcType)src); \ - } break; \ - case ma_atomic_memory_order_release: \ - { \ - result = (ma_atomicType)intrin##_rel((volatile msvcType*)dst, (msvcType)src); \ - } break; \ - case ma_atomic_memory_order_acq_rel: \ - case ma_atomic_memory_order_seq_cst: \ - default: \ - { \ - result = (ma_atomicType)intrin((volatile msvcType*)dst, (msvcType)src); \ - } break; \ - } \ - return result; - #define MA_ATOMIC_MSVC_ARM_INTRINSIC_COMPARE_EXCHANGE(ptr, expected, desired, order, intrin, ma_atomicType, msvcType) \ - ma_atomicType result; \ - switch (order) \ - { \ - case ma_atomic_memory_order_relaxed: \ - { \ - result = (ma_atomicType)intrin##_nf((volatile msvcType*)ptr, (msvcType)expected, (msvcType)desired); \ - } break; \ - case ma_atomic_memory_order_consume: \ - case ma_atomic_memory_order_acquire: \ - { \ - result = (ma_atomicType)intrin##_acq((volatile msvcType*)ptr, (msvcType)expected, (msvcType)desired); \ - } break; \ - case ma_atomic_memory_order_release: \ - { \ - result = (ma_atomicType)intrin##_rel((volatile msvcType*)ptr, (msvcType)expected, (msvcType)desired); \ - } break; \ - case ma_atomic_memory_order_acq_rel: \ - case ma_atomic_memory_order_seq_cst: \ - default: \ - { \ - result = (ma_atomicType)intrin((volatile msvcType*)ptr, (msvcType)expected, (msvcType)desired); \ - } break; \ - } \ - return result; - #define ma_atomic_memory_order_relaxed 0 - #define ma_atomic_memory_order_consume 1 - #define ma_atomic_memory_order_acquire 2 - #define ma_atomic_memory_order_release 3 - #define ma_atomic_memory_order_acq_rel 4 - #define ma_atomic_memory_order_seq_cst 5 - #if _MSC_VER < 1600 && defined(MA_X86) - #define MA_ATOMIC_MSVC_USE_INLINED_ASSEMBLY - #endif - #if _MSC_VER < 1600 - #undef MA_ATOMIC_HAS_8 - #undef MA_ATOMIC_HAS_16 - #endif - #if !defined(MA_ATOMIC_MSVC_USE_INLINED_ASSEMBLY) - #include - #endif - #if defined(MA_ATOMIC_MSVC_USE_INLINED_ASSEMBLY) - #if defined(MA_ATOMIC_HAS_8) - static MA_INLINE ma_uint8 __stdcall ma_atomic_compare_and_swap_8(volatile ma_uint8* dst, ma_uint8 expected, ma_uint8 desired) - { - ma_uint8 result = 0; - __asm { - mov ecx, dst - mov al, expected - mov dl, desired - lock cmpxchg [ecx], dl - mov result, al - } - return result; - } - #endif - #if defined(MA_ATOMIC_HAS_16) - static MA_INLINE ma_uint16 __stdcall ma_atomic_compare_and_swap_16(volatile ma_uint16* dst, ma_uint16 expected, ma_uint16 desired) - { - ma_uint16 result = 0; - __asm { - mov ecx, dst - mov ax, expected - mov dx, desired - lock cmpxchg [ecx], dx - mov result, ax - } - return result; - } - #endif - #if defined(MA_ATOMIC_HAS_32) - static MA_INLINE ma_uint32 __stdcall ma_atomic_compare_and_swap_32(volatile ma_uint32* dst, ma_uint32 expected, ma_uint32 desired) - { - ma_uint32 result = 0; - __asm { - mov ecx, dst - mov eax, expected - mov edx, desired - lock cmpxchg [ecx], edx - mov result, eax - } - return result; - } - #endif - #if defined(MA_ATOMIC_HAS_64) - static MA_INLINE ma_uint64 __stdcall ma_atomic_compare_and_swap_64(volatile ma_uint64* dst, ma_uint64 expected, ma_uint64 desired) - { - ma_uint32 resultEAX = 0; - ma_uint32 resultEDX = 0; - __asm { - mov esi, dst - mov eax, dword ptr expected - mov edx, dword ptr expected + 4 - mov ebx, dword ptr desired - mov ecx, dword ptr desired + 4 - lock cmpxchg8b qword ptr [esi] - mov resultEAX, eax - mov resultEDX, edx - } - return ((ma_uint64)resultEDX << 32) | resultEAX; - } - #endif - #else - #if defined(MA_ATOMIC_HAS_8) - #define ma_atomic_compare_and_swap_8( dst, expected, desired) (ma_uint8 )_InterlockedCompareExchange8((volatile char*)dst, (char)desired, (char)expected) - #endif - #if defined(MA_ATOMIC_HAS_16) - #define ma_atomic_compare_and_swap_16(dst, expected, desired) (ma_uint16)_InterlockedCompareExchange16((volatile short*)dst, (short)desired, (short)expected) - #endif - #if defined(MA_ATOMIC_HAS_32) - #define ma_atomic_compare_and_swap_32(dst, expected, desired) (ma_uint32)_InterlockedCompareExchange((volatile long*)dst, (long)desired, (long)expected) - #endif - #if defined(MA_ATOMIC_HAS_64) - #define ma_atomic_compare_and_swap_64(dst, expected, desired) (ma_uint64)_InterlockedCompareExchange64((volatile ma_int64*)dst, (ma_int64)desired, (ma_int64)expected) - #endif - #endif - #if defined(MA_ATOMIC_MSVC_USE_INLINED_ASSEMBLY) - #if defined(MA_ATOMIC_HAS_8) - static MA_INLINE ma_uint8 __stdcall ma_atomic_exchange_explicit_8(volatile ma_uint8* dst, ma_uint8 src, ma_atomic_memory_order order) - { - ma_uint8 result = 0; - (void)order; - __asm { - mov ecx, dst - mov al, src - lock xchg [ecx], al - mov result, al - } - return result; - } - #endif - #if defined(MA_ATOMIC_HAS_16) - static MA_INLINE ma_uint16 __stdcall ma_atomic_exchange_explicit_16(volatile ma_uint16* dst, ma_uint16 src, ma_atomic_memory_order order) - { - ma_uint16 result = 0; - (void)order; - __asm { - mov ecx, dst - mov ax, src - lock xchg [ecx], ax - mov result, ax - } - return result; - } - #endif - #if defined(MA_ATOMIC_HAS_32) - static MA_INLINE ma_uint32 __stdcall ma_atomic_exchange_explicit_32(volatile ma_uint32* dst, ma_uint32 src, ma_atomic_memory_order order) - { - ma_uint32 result = 0; - (void)order; - __asm { - mov ecx, dst - mov eax, src - lock xchg [ecx], eax - mov result, eax - } - return result; - } - #endif - #else - #if defined(MA_ATOMIC_HAS_8) - static MA_INLINE ma_uint8 __stdcall ma_atomic_exchange_explicit_8(volatile ma_uint8* dst, ma_uint8 src, ma_atomic_memory_order order) - { - #if defined(MA_ARM) - MA_ATOMIC_MSVC_ARM_INTRINSIC(dst, src, order, _InterlockedExchange8, ma_uint8, char); - #else - (void)order; - return (ma_uint8)_InterlockedExchange8((volatile char*)dst, (char)src); - #endif - } - #endif - #if defined(MA_ATOMIC_HAS_16) - static MA_INLINE ma_uint16 __stdcall ma_atomic_exchange_explicit_16(volatile ma_uint16* dst, ma_uint16 src, ma_atomic_memory_order order) - { - #if defined(MA_ARM) - MA_ATOMIC_MSVC_ARM_INTRINSIC(dst, src, order, _InterlockedExchange16, ma_uint16, short); - #else - (void)order; - return (ma_uint16)_InterlockedExchange16((volatile short*)dst, (short)src); - #endif - } - #endif - #if defined(MA_ATOMIC_HAS_32) - static MA_INLINE ma_uint32 __stdcall ma_atomic_exchange_explicit_32(volatile ma_uint32* dst, ma_uint32 src, ma_atomic_memory_order order) - { - #if defined(MA_ARM) - MA_ATOMIC_MSVC_ARM_INTRINSIC(dst, src, order, _InterlockedExchange, ma_uint32, long); - #else - (void)order; - return (ma_uint32)_InterlockedExchange((volatile long*)dst, (long)src); - #endif - } - #endif - #if defined(MA_ATOMIC_HAS_64) && defined(MA_64BIT) - static MA_INLINE ma_uint64 __stdcall ma_atomic_exchange_explicit_64(volatile ma_uint64* dst, ma_uint64 src, ma_atomic_memory_order order) - { - #if defined(MA_ARM) - MA_ATOMIC_MSVC_ARM_INTRINSIC(dst, src, order, _InterlockedExchange64, ma_uint64, long long); - #else - (void)order; - return (ma_uint64)_InterlockedExchange64((volatile long long*)dst, (long long)src); - #endif - } - #else - #endif - #endif - #if defined(MA_ATOMIC_HAS_64) && !defined(MA_64BIT) - static MA_INLINE ma_uint64 __stdcall ma_atomic_exchange_explicit_64(volatile ma_uint64* dst, ma_uint64 src, ma_atomic_memory_order order) - { - ma_uint64 oldValue; - do { - oldValue = *dst; - } while (ma_atomic_compare_and_swap_64(dst, oldValue, src) != oldValue); - (void)order; - return oldValue; - } - #endif - #if defined(MA_ATOMIC_MSVC_USE_INLINED_ASSEMBLY) - #if defined(MA_ATOMIC_HAS_8) - static MA_INLINE ma_uint8 __stdcall ma_atomic_fetch_add_explicit_8(volatile ma_uint8* dst, ma_uint8 src, ma_atomic_memory_order order) - { - ma_uint8 result = 0; - (void)order; - __asm { - mov ecx, dst - mov al, src - lock xadd [ecx], al - mov result, al - } - return result; - } - #endif - #if defined(MA_ATOMIC_HAS_16) - static MA_INLINE ma_uint16 __stdcall ma_atomic_fetch_add_explicit_16(volatile ma_uint16* dst, ma_uint16 src, ma_atomic_memory_order order) - { - ma_uint16 result = 0; - (void)order; - __asm { - mov ecx, dst - mov ax, src - lock xadd [ecx], ax - mov result, ax - } - return result; - } - #endif - #if defined(MA_ATOMIC_HAS_32) - static MA_INLINE ma_uint32 __stdcall ma_atomic_fetch_add_explicit_32(volatile ma_uint32* dst, ma_uint32 src, ma_atomic_memory_order order) - { - ma_uint32 result = 0; - (void)order; - __asm { - mov ecx, dst - mov eax, src - lock xadd [ecx], eax - mov result, eax - } - return result; - } - #endif - #else - #if defined(MA_ATOMIC_HAS_8) - static MA_INLINE ma_uint8 __stdcall ma_atomic_fetch_add_explicit_8(volatile ma_uint8* dst, ma_uint8 src, ma_atomic_memory_order order) - { - #if defined(MA_ARM) - MA_ATOMIC_MSVC_ARM_INTRINSIC(dst, src, order, _InterlockedExchangeAdd8, ma_uint8, char); - #else - (void)order; - return (ma_uint8)_InterlockedExchangeAdd8((volatile char*)dst, (char)src); - #endif - } - #endif - #if defined(MA_ATOMIC_HAS_16) - static MA_INLINE ma_uint16 __stdcall ma_atomic_fetch_add_explicit_16(volatile ma_uint16* dst, ma_uint16 src, ma_atomic_memory_order order) - { - #if defined(MA_ARM) - MA_ATOMIC_MSVC_ARM_INTRINSIC(dst, src, order, _InterlockedExchangeAdd16, ma_uint16, short); - #else - (void)order; - return (ma_uint16)_InterlockedExchangeAdd16((volatile short*)dst, (short)src); - #endif - } - #endif - #if defined(MA_ATOMIC_HAS_32) - static MA_INLINE ma_uint32 __stdcall ma_atomic_fetch_add_explicit_32(volatile ma_uint32* dst, ma_uint32 src, ma_atomic_memory_order order) - { - #if defined(MA_ARM) - MA_ATOMIC_MSVC_ARM_INTRINSIC(dst, src, order, _InterlockedExchangeAdd, ma_uint32, long); - #else - (void)order; - return (ma_uint32)_InterlockedExchangeAdd((volatile long*)dst, (long)src); - #endif - } - #endif - #if defined(MA_ATOMIC_HAS_64) && defined(MA_64BIT) - static MA_INLINE ma_uint64 __stdcall ma_atomic_fetch_add_explicit_64(volatile ma_uint64* dst, ma_uint64 src, ma_atomic_memory_order order) - { - #if defined(MA_ARM) - MA_ATOMIC_MSVC_ARM_INTRINSIC(dst, src, order, _InterlockedExchangeAdd64, ma_uint64, long long); - #else - (void)order; - return (ma_uint64)_InterlockedExchangeAdd64((volatile long long*)dst, (long long)src); - #endif - } - #else - #endif - #endif - #if defined(MA_ATOMIC_HAS_64) && !defined(MA_64BIT) - static MA_INLINE ma_uint64 __stdcall ma_atomic_fetch_add_explicit_64(volatile ma_uint64* dst, ma_uint64 src, ma_atomic_memory_order order) - { - ma_uint64 oldValue; - ma_uint64 newValue; - do { - oldValue = *dst; - newValue = oldValue + src; - } while (ma_atomic_compare_and_swap_64(dst, oldValue, newValue) != oldValue); - (void)order; - return oldValue; - } - #endif - #if defined(MA_ATOMIC_MSVC_USE_INLINED_ASSEMBLY) - static MA_INLINE void __stdcall ma_atomic_thread_fence(ma_atomic_memory_order order) - { - (void)order; - __asm { - lock add [esp], 0 - } - } - #else - #if defined(MA_X64) - #define ma_atomic_thread_fence(order) __faststorefence(), (void)order - #elif defined(MA_ARM64) - #define ma_atomic_thread_fence(order) __dmb(_ARM64_BARRIER_ISH), (void)order - #else - static MA_INLINE void ma_atomic_thread_fence(ma_atomic_memory_order order) - { - volatile ma_uint32 barrier = 0; - ma_atomic_fetch_add_explicit_32(&barrier, 0, order); - } - #endif - #endif - #define ma_atomic_compiler_fence() ma_atomic_thread_fence(ma_atomic_memory_order_seq_cst) - #define ma_atomic_signal_fence(order) ma_atomic_thread_fence(order) - #if defined(MA_ATOMIC_HAS_8) - static MA_INLINE ma_uint8 ma_atomic_load_explicit_8(volatile const ma_uint8* ptr, ma_atomic_memory_order order) - { - #if defined(MA_ARM) - MA_ATOMIC_MSVC_ARM_INTRINSIC_COMPARE_EXCHANGE(ptr, 0, 0, order, _InterlockedCompareExchange8, ma_uint8, char); - #else - (void)order; - return ma_atomic_compare_and_swap_8((volatile ma_uint8*)ptr, 0, 0); - #endif - } - #endif - #if defined(MA_ATOMIC_HAS_16) - static MA_INLINE ma_uint16 ma_atomic_load_explicit_16(volatile const ma_uint16* ptr, ma_atomic_memory_order order) - { - #if defined(MA_ARM) - MA_ATOMIC_MSVC_ARM_INTRINSIC_COMPARE_EXCHANGE(ptr, 0, 0, order, _InterlockedCompareExchange16, ma_uint16, short); - #else - (void)order; - return ma_atomic_compare_and_swap_16((volatile ma_uint16*)ptr, 0, 0); - #endif - } - #endif - #if defined(MA_ATOMIC_HAS_32) - static MA_INLINE ma_uint32 ma_atomic_load_explicit_32(volatile const ma_uint32* ptr, ma_atomic_memory_order order) - { - #if defined(MA_ARM) - MA_ATOMIC_MSVC_ARM_INTRINSIC_COMPARE_EXCHANGE(ptr, 0, 0, order, _InterlockedCompareExchange, ma_uint32, long); - #else - (void)order; - return ma_atomic_compare_and_swap_32((volatile ma_uint32*)ptr, 0, 0); - #endif - } - #endif - #if defined(MA_ATOMIC_HAS_64) - static MA_INLINE ma_uint64 ma_atomic_load_explicit_64(volatile const ma_uint64* ptr, ma_atomic_memory_order order) - { - #if defined(MA_ARM) - MA_ATOMIC_MSVC_ARM_INTRINSIC_COMPARE_EXCHANGE(ptr, 0, 0, order, _InterlockedCompareExchange64, ma_uint64, long long); - #else - (void)order; - return ma_atomic_compare_and_swap_64((volatile ma_uint64*)ptr, 0, 0); - #endif - } - #endif - #if defined(MA_ATOMIC_HAS_8) - #define ma_atomic_store_explicit_8( dst, src, order) (void)ma_atomic_exchange_explicit_8 (dst, src, order) - #endif - #if defined(MA_ATOMIC_HAS_16) - #define ma_atomic_store_explicit_16(dst, src, order) (void)ma_atomic_exchange_explicit_16(dst, src, order) - #endif - #if defined(MA_ATOMIC_HAS_32) - #define ma_atomic_store_explicit_32(dst, src, order) (void)ma_atomic_exchange_explicit_32(dst, src, order) - #endif - #if defined(MA_ATOMIC_HAS_64) - #define ma_atomic_store_explicit_64(dst, src, order) (void)ma_atomic_exchange_explicit_64(dst, src, order) - #endif - #if defined(MA_ATOMIC_HAS_8) - static MA_INLINE ma_uint8 __stdcall ma_atomic_fetch_sub_explicit_8(volatile ma_uint8* dst, ma_uint8 src, ma_atomic_memory_order order) - { - ma_uint8 oldValue; - ma_uint8 newValue; - do { - oldValue = *dst; - newValue = (ma_uint8)(oldValue - src); - } while (ma_atomic_compare_and_swap_8(dst, oldValue, newValue) != oldValue); - (void)order; - return oldValue; - } - #endif - #if defined(MA_ATOMIC_HAS_16) - static MA_INLINE ma_uint16 __stdcall ma_atomic_fetch_sub_explicit_16(volatile ma_uint16* dst, ma_uint16 src, ma_atomic_memory_order order) - { - ma_uint16 oldValue; - ma_uint16 newValue; - do { - oldValue = *dst; - newValue = (ma_uint16)(oldValue - src); - } while (ma_atomic_compare_and_swap_16(dst, oldValue, newValue) != oldValue); - (void)order; - return oldValue; - } - #endif - #if defined(MA_ATOMIC_HAS_32) - static MA_INLINE ma_uint32 __stdcall ma_atomic_fetch_sub_explicit_32(volatile ma_uint32* dst, ma_uint32 src, ma_atomic_memory_order order) - { - ma_uint32 oldValue; - ma_uint32 newValue; - do { - oldValue = *dst; - newValue = oldValue - src; - } while (ma_atomic_compare_and_swap_32(dst, oldValue, newValue) != oldValue); - (void)order; - return oldValue; - } - #endif - #if defined(MA_ATOMIC_HAS_64) - static MA_INLINE ma_uint64 __stdcall ma_atomic_fetch_sub_explicit_64(volatile ma_uint64* dst, ma_uint64 src, ma_atomic_memory_order order) - { - ma_uint64 oldValue; - ma_uint64 newValue; - do { - oldValue = *dst; - newValue = oldValue - src; - } while (ma_atomic_compare_and_swap_64(dst, oldValue, newValue) != oldValue); - (void)order; - return oldValue; - } - #endif - #if defined(MA_ATOMIC_HAS_8) - static MA_INLINE ma_uint8 __stdcall ma_atomic_fetch_and_explicit_8(volatile ma_uint8* dst, ma_uint8 src, ma_atomic_memory_order order) - { - #if defined(MA_ARM) - MA_ATOMIC_MSVC_ARM_INTRINSIC(dst, src, order, _InterlockedAnd8, ma_uint8, char); - #else - ma_uint8 oldValue; - ma_uint8 newValue; - do { - oldValue = *dst; - newValue = (ma_uint8)(oldValue & src); - } while (ma_atomic_compare_and_swap_8(dst, oldValue, newValue) != oldValue); - (void)order; - return oldValue; - #endif - } - #endif - #if defined(MA_ATOMIC_HAS_16) - static MA_INLINE ma_uint16 __stdcall ma_atomic_fetch_and_explicit_16(volatile ma_uint16* dst, ma_uint16 src, ma_atomic_memory_order order) - { - #if defined(MA_ARM) - MA_ATOMIC_MSVC_ARM_INTRINSIC(dst, src, order, _InterlockedAnd16, ma_uint16, short); - #else - ma_uint16 oldValue; - ma_uint16 newValue; - do { - oldValue = *dst; - newValue = (ma_uint16)(oldValue & src); - } while (ma_atomic_compare_and_swap_16(dst, oldValue, newValue) != oldValue); - (void)order; - return oldValue; - #endif - } - #endif - #if defined(MA_ATOMIC_HAS_32) - static MA_INLINE ma_uint32 __stdcall ma_atomic_fetch_and_explicit_32(volatile ma_uint32* dst, ma_uint32 src, ma_atomic_memory_order order) - { - #if defined(MA_ARM) - MA_ATOMIC_MSVC_ARM_INTRINSIC(dst, src, order, _InterlockedAnd, ma_uint32, long); - #else - ma_uint32 oldValue; - ma_uint32 newValue; - do { - oldValue = *dst; - newValue = oldValue & src; - } while (ma_atomic_compare_and_swap_32(dst, oldValue, newValue) != oldValue); - (void)order; - return oldValue; - #endif - } - #endif - #if defined(MA_ATOMIC_HAS_64) - static MA_INLINE ma_uint64 __stdcall ma_atomic_fetch_and_explicit_64(volatile ma_uint64* dst, ma_uint64 src, ma_atomic_memory_order order) - { - #if defined(MA_ARM) - MA_ATOMIC_MSVC_ARM_INTRINSIC(dst, src, order, _InterlockedAnd64, ma_uint64, long long); - #else - ma_uint64 oldValue; - ma_uint64 newValue; - do { - oldValue = *dst; - newValue = oldValue & src; - } while (ma_atomic_compare_and_swap_64(dst, oldValue, newValue) != oldValue); - (void)order; - return oldValue; - #endif - } - #endif - #if defined(MA_ATOMIC_HAS_8) - static MA_INLINE ma_uint8 __stdcall ma_atomic_fetch_xor_explicit_8(volatile ma_uint8* dst, ma_uint8 src, ma_atomic_memory_order order) - { - #if defined(MA_ARM) - MA_ATOMIC_MSVC_ARM_INTRINSIC(dst, src, order, _InterlockedXor8, ma_uint8, char); - #else - ma_uint8 oldValue; - ma_uint8 newValue; - do { - oldValue = *dst; - newValue = (ma_uint8)(oldValue ^ src); - } while (ma_atomic_compare_and_swap_8(dst, oldValue, newValue) != oldValue); - (void)order; - return oldValue; - #endif - } - #endif - #if defined(MA_ATOMIC_HAS_16) - static MA_INLINE ma_uint16 __stdcall ma_atomic_fetch_xor_explicit_16(volatile ma_uint16* dst, ma_uint16 src, ma_atomic_memory_order order) - { - #if defined(MA_ARM) - MA_ATOMIC_MSVC_ARM_INTRINSIC(dst, src, order, _InterlockedXor16, ma_uint16, short); - #else - ma_uint16 oldValue; - ma_uint16 newValue; - do { - oldValue = *dst; - newValue = (ma_uint16)(oldValue ^ src); - } while (ma_atomic_compare_and_swap_16(dst, oldValue, newValue) != oldValue); - (void)order; - return oldValue; - #endif - } - #endif - #if defined(MA_ATOMIC_HAS_32) - static MA_INLINE ma_uint32 __stdcall ma_atomic_fetch_xor_explicit_32(volatile ma_uint32* dst, ma_uint32 src, ma_atomic_memory_order order) - { - #if defined(MA_ARM) - MA_ATOMIC_MSVC_ARM_INTRINSIC(dst, src, order, _InterlockedXor, ma_uint32, long); - #else - ma_uint32 oldValue; - ma_uint32 newValue; - do { - oldValue = *dst; - newValue = oldValue ^ src; - } while (ma_atomic_compare_and_swap_32(dst, oldValue, newValue) != oldValue); - (void)order; - return oldValue; - #endif - } - #endif - #if defined(MA_ATOMIC_HAS_64) - static MA_INLINE ma_uint64 __stdcall ma_atomic_fetch_xor_explicit_64(volatile ma_uint64* dst, ma_uint64 src, ma_atomic_memory_order order) - { - #if defined(MA_ARM) - MA_ATOMIC_MSVC_ARM_INTRINSIC(dst, src, order, _InterlockedXor64, ma_uint64, long long); - #else - ma_uint64 oldValue; - ma_uint64 newValue; - do { - oldValue = *dst; - newValue = oldValue ^ src; - } while (ma_atomic_compare_and_swap_64(dst, oldValue, newValue) != oldValue); - (void)order; - return oldValue; - #endif - } - #endif - #if defined(MA_ATOMIC_HAS_8) - static MA_INLINE ma_uint8 __stdcall ma_atomic_fetch_or_explicit_8(volatile ma_uint8* dst, ma_uint8 src, ma_atomic_memory_order order) - { - #if defined(MA_ARM) - MA_ATOMIC_MSVC_ARM_INTRINSIC(dst, src, order, _InterlockedOr8, ma_uint8, char); - #else - ma_uint8 oldValue; - ma_uint8 newValue; - do { - oldValue = *dst; - newValue = (ma_uint8)(oldValue | src); - } while (ma_atomic_compare_and_swap_8(dst, oldValue, newValue) != oldValue); - (void)order; - return oldValue; - #endif - } - #endif - #if defined(MA_ATOMIC_HAS_16) - static MA_INLINE ma_uint16 __stdcall ma_atomic_fetch_or_explicit_16(volatile ma_uint16* dst, ma_uint16 src, ma_atomic_memory_order order) - { - #if defined(MA_ARM) - MA_ATOMIC_MSVC_ARM_INTRINSIC(dst, src, order, _InterlockedOr16, ma_uint16, short); - #else - ma_uint16 oldValue; - ma_uint16 newValue; - do { - oldValue = *dst; - newValue = (ma_uint16)(oldValue | src); - } while (ma_atomic_compare_and_swap_16(dst, oldValue, newValue) != oldValue); - (void)order; - return oldValue; - #endif - } - #endif - #if defined(MA_ATOMIC_HAS_32) - static MA_INLINE ma_uint32 __stdcall ma_atomic_fetch_or_explicit_32(volatile ma_uint32* dst, ma_uint32 src, ma_atomic_memory_order order) - { - #if defined(MA_ARM) - MA_ATOMIC_MSVC_ARM_INTRINSIC(dst, src, order, _InterlockedOr, ma_uint32, long); - #else - ma_uint32 oldValue; - ma_uint32 newValue; - do { - oldValue = *dst; - newValue = oldValue | src; - } while (ma_atomic_compare_and_swap_32(dst, oldValue, newValue) != oldValue); - (void)order; - return oldValue; - #endif - } - #endif - #if defined(MA_ATOMIC_HAS_64) - static MA_INLINE ma_uint64 __stdcall ma_atomic_fetch_or_explicit_64(volatile ma_uint64* dst, ma_uint64 src, ma_atomic_memory_order order) - { - #if defined(MA_ARM) - MA_ATOMIC_MSVC_ARM_INTRINSIC(dst, src, order, _InterlockedOr64, ma_uint64, long long); - #else - ma_uint64 oldValue; - ma_uint64 newValue; - do { - oldValue = *dst; - newValue = oldValue | src; - } while (ma_atomic_compare_and_swap_64(dst, oldValue, newValue) != oldValue); - (void)order; - return oldValue; - #endif - } - #endif - #if defined(MA_ATOMIC_HAS_8) - #define ma_atomic_test_and_set_explicit_8( dst, order) ma_atomic_exchange_explicit_8 (dst, 1, order) - #endif - #if defined(MA_ATOMIC_HAS_16) - #define ma_atomic_test_and_set_explicit_16(dst, order) ma_atomic_exchange_explicit_16(dst, 1, order) - #endif - #if defined(MA_ATOMIC_HAS_32) - #define ma_atomic_test_and_set_explicit_32(dst, order) ma_atomic_exchange_explicit_32(dst, 1, order) - #endif - #if defined(MA_ATOMIC_HAS_64) - #define ma_atomic_test_and_set_explicit_64(dst, order) ma_atomic_exchange_explicit_64(dst, 1, order) - #endif - #if defined(MA_ATOMIC_HAS_8) - #define ma_atomic_clear_explicit_8( dst, order) ma_atomic_store_explicit_8 (dst, 0, order) - #endif - #if defined(MA_ATOMIC_HAS_16) - #define ma_atomic_clear_explicit_16(dst, order) ma_atomic_store_explicit_16(dst, 0, order) - #endif - #if defined(MA_ATOMIC_HAS_32) - #define ma_atomic_clear_explicit_32(dst, order) ma_atomic_store_explicit_32(dst, 0, order) - #endif - #if defined(MA_ATOMIC_HAS_64) - #define ma_atomic_clear_explicit_64(dst, order) ma_atomic_store_explicit_64(dst, 0, order) - #endif - #if defined(MA_ATOMIC_HAS_8) - typedef ma_uint8 ma_atomic_flag; - #define ma_atomic_flag_test_and_set_explicit(ptr, order) (ma_bool32)ma_atomic_test_and_set_explicit_8(ptr, order) - #define ma_atomic_flag_clear_explicit(ptr, order) ma_atomic_clear_explicit_8(ptr, order) - #define ma_atomic_flag_load_explicit(ptr, order) ma_atomic_load_explicit_8(ptr, order) - #else - typedef ma_uint32 ma_atomic_flag; - #define ma_atomic_flag_test_and_set_explicit(ptr, order) (ma_bool32)ma_atomic_test_and_set_explicit_32(ptr, order) - #define ma_atomic_flag_clear_explicit(ptr, order) ma_atomic_clear_explicit_32(ptr, order) - #define ma_atomic_flag_load_explicit(ptr, order) ma_atomic_load_explicit_32(ptr, order) - #endif -#elif defined(__clang__) || (defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 7))) - #define MA_ATOMIC_HAS_NATIVE_COMPARE_EXCHANGE - #define MA_ATOMIC_HAS_NATIVE_IS_LOCK_FREE - #define ma_atomic_memory_order_relaxed __ATOMIC_RELAXED - #define ma_atomic_memory_order_consume __ATOMIC_CONSUME - #define ma_atomic_memory_order_acquire __ATOMIC_ACQUIRE - #define ma_atomic_memory_order_release __ATOMIC_RELEASE - #define ma_atomic_memory_order_acq_rel __ATOMIC_ACQ_REL - #define ma_atomic_memory_order_seq_cst __ATOMIC_SEQ_CST - #define ma_atomic_compiler_fence() __asm__ __volatile__("":::"memory") - #define ma_atomic_thread_fence(order) __atomic_thread_fence(order) - #define ma_atomic_signal_fence(order) __atomic_signal_fence(order) - #define ma_atomic_is_lock_free_8(ptr) __atomic_is_lock_free(1, ptr) - #define ma_atomic_is_lock_free_16(ptr) __atomic_is_lock_free(2, ptr) - #define ma_atomic_is_lock_free_32(ptr) __atomic_is_lock_free(4, ptr) - #define ma_atomic_is_lock_free_64(ptr) __atomic_is_lock_free(8, ptr) - #define ma_atomic_test_and_set_explicit_8( dst, order) __atomic_exchange_n(dst, 1, order) - #define ma_atomic_test_and_set_explicit_16(dst, order) __atomic_exchange_n(dst, 1, order) - #define ma_atomic_test_and_set_explicit_32(dst, order) __atomic_exchange_n(dst, 1, order) - #define ma_atomic_test_and_set_explicit_64(dst, order) __atomic_exchange_n(dst, 1, order) - #define ma_atomic_clear_explicit_8( dst, order) __atomic_store_n(dst, 0, order) - #define ma_atomic_clear_explicit_16(dst, order) __atomic_store_n(dst, 0, order) - #define ma_atomic_clear_explicit_32(dst, order) __atomic_store_n(dst, 0, order) - #define ma_atomic_clear_explicit_64(dst, order) __atomic_store_n(dst, 0, order) - #define ma_atomic_store_explicit_8( dst, src, order) __atomic_store_n(dst, src, order) - #define ma_atomic_store_explicit_16(dst, src, order) __atomic_store_n(dst, src, order) - #define ma_atomic_store_explicit_32(dst, src, order) __atomic_store_n(dst, src, order) - #define ma_atomic_store_explicit_64(dst, src, order) __atomic_store_n(dst, src, order) - #define ma_atomic_load_explicit_8( dst, order) __atomic_load_n(dst, order) - #define ma_atomic_load_explicit_16(dst, order) __atomic_load_n(dst, order) - #define ma_atomic_load_explicit_32(dst, order) __atomic_load_n(dst, order) - #define ma_atomic_load_explicit_64(dst, order) __atomic_load_n(dst, order) - #define ma_atomic_exchange_explicit_8( dst, src, order) __atomic_exchange_n(dst, src, order) - #define ma_atomic_exchange_explicit_16(dst, src, order) __atomic_exchange_n(dst, src, order) - #define ma_atomic_exchange_explicit_32(dst, src, order) __atomic_exchange_n(dst, src, order) - #define ma_atomic_exchange_explicit_64(dst, src, order) __atomic_exchange_n(dst, src, order) - #define ma_atomic_compare_exchange_strong_explicit_8( dst, expected, desired, successOrder, failureOrder) __atomic_compare_exchange_n(dst, expected, desired, 0, successOrder, failureOrder) - #define ma_atomic_compare_exchange_strong_explicit_16(dst, expected, desired, successOrder, failureOrder) __atomic_compare_exchange_n(dst, expected, desired, 0, successOrder, failureOrder) - #define ma_atomic_compare_exchange_strong_explicit_32(dst, expected, desired, successOrder, failureOrder) __atomic_compare_exchange_n(dst, expected, desired, 0, successOrder, failureOrder) - #define ma_atomic_compare_exchange_strong_explicit_64(dst, expected, desired, successOrder, failureOrder) __atomic_compare_exchange_n(dst, expected, desired, 0, successOrder, failureOrder) - #define ma_atomic_compare_exchange_weak_explicit_8( dst, expected, desired, successOrder, failureOrder) __atomic_compare_exchange_n(dst, expected, desired, 1, successOrder, failureOrder) - #define ma_atomic_compare_exchange_weak_explicit_16(dst, expected, desired, successOrder, failureOrder) __atomic_compare_exchange_n(dst, expected, desired, 1, successOrder, failureOrder) - #define ma_atomic_compare_exchange_weak_explicit_32(dst, expected, desired, successOrder, failureOrder) __atomic_compare_exchange_n(dst, expected, desired, 1, successOrder, failureOrder) - #define ma_atomic_compare_exchange_weak_explicit_64(dst, expected, desired, successOrder, failureOrder) __atomic_compare_exchange_n(dst, expected, desired, 1, successOrder, failureOrder) - #define ma_atomic_fetch_add_explicit_8( dst, src, order) __atomic_fetch_add(dst, src, order) - #define ma_atomic_fetch_add_explicit_16(dst, src, order) __atomic_fetch_add(dst, src, order) - #define ma_atomic_fetch_add_explicit_32(dst, src, order) __atomic_fetch_add(dst, src, order) - #define ma_atomic_fetch_add_explicit_64(dst, src, order) __atomic_fetch_add(dst, src, order) - #define ma_atomic_fetch_sub_explicit_8( dst, src, order) __atomic_fetch_sub(dst, src, order) - #define ma_atomic_fetch_sub_explicit_16(dst, src, order) __atomic_fetch_sub(dst, src, order) - #define ma_atomic_fetch_sub_explicit_32(dst, src, order) __atomic_fetch_sub(dst, src, order) - #define ma_atomic_fetch_sub_explicit_64(dst, src, order) __atomic_fetch_sub(dst, src, order) - #define ma_atomic_fetch_or_explicit_8( dst, src, order) __atomic_fetch_or(dst, src, order) - #define ma_atomic_fetch_or_explicit_16(dst, src, order) __atomic_fetch_or(dst, src, order) - #define ma_atomic_fetch_or_explicit_32(dst, src, order) __atomic_fetch_or(dst, src, order) - #define ma_atomic_fetch_or_explicit_64(dst, src, order) __atomic_fetch_or(dst, src, order) - #define ma_atomic_fetch_xor_explicit_8( dst, src, order) __atomic_fetch_xor(dst, src, order) - #define ma_atomic_fetch_xor_explicit_16(dst, src, order) __atomic_fetch_xor(dst, src, order) - #define ma_atomic_fetch_xor_explicit_32(dst, src, order) __atomic_fetch_xor(dst, src, order) - #define ma_atomic_fetch_xor_explicit_64(dst, src, order) __atomic_fetch_xor(dst, src, order) - #define ma_atomic_fetch_and_explicit_8( dst, src, order) __atomic_fetch_and(dst, src, order) - #define ma_atomic_fetch_and_explicit_16(dst, src, order) __atomic_fetch_and(dst, src, order) - #define ma_atomic_fetch_and_explicit_32(dst, src, order) __atomic_fetch_and(dst, src, order) - #define ma_atomic_fetch_and_explicit_64(dst, src, order) __atomic_fetch_and(dst, src, order) - static MA_INLINE ma_uint8 ma_atomic_compare_and_swap_8(volatile ma_uint8* dst, ma_uint8 expected, ma_uint8 desired) - { - __atomic_compare_exchange_n(dst, &expected, desired, 0, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST); - return expected; - } - static MA_INLINE ma_uint16 ma_atomic_compare_and_swap_16(volatile ma_uint16* dst, ma_uint16 expected, ma_uint16 desired) - { - __atomic_compare_exchange_n(dst, &expected, desired, 0, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST); - return expected; - } - static MA_INLINE ma_uint32 ma_atomic_compare_and_swap_32(volatile ma_uint32* dst, ma_uint32 expected, ma_uint32 desired) - { - __atomic_compare_exchange_n(dst, &expected, desired, 0, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST); - return expected; - } - #if defined(__clang__) - #pragma clang diagnostic push - #if __clang_major__ >= 8 - #pragma clang diagnostic ignored "-Watomic-alignment" - #endif - #endif - static MA_INLINE ma_uint64 ma_atomic_compare_and_swap_64(volatile ma_uint64* dst, ma_uint64 expected, ma_uint64 desired) - { - __atomic_compare_exchange_n(dst, &expected, desired, 0, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST); - return expected; - } - #if defined(__clang__) - #pragma clang diagnostic pop - #endif - typedef ma_uint8 ma_atomic_flag; - #define ma_atomic_flag_test_and_set_explicit(dst, order) (ma_bool32)__atomic_test_and_set(dst, order) - #define ma_atomic_flag_clear_explicit(dst, order) __atomic_clear(dst, order) - #define ma_atomic_flag_load_explicit(ptr, order) ma_atomic_load_explicit_8(ptr, order) -#else - #define ma_atomic_memory_order_relaxed 1 - #define ma_atomic_memory_order_consume 2 - #define ma_atomic_memory_order_acquire 3 - #define ma_atomic_memory_order_release 4 - #define ma_atomic_memory_order_acq_rel 5 - #define ma_atomic_memory_order_seq_cst 6 - #define ma_atomic_compiler_fence() __asm__ __volatile__("":::"memory") - #if defined(__GNUC__) - #define ma_atomic_thread_fence(order) __sync_synchronize(), (void)order - static MA_INLINE ma_uint8 ma_atomic_exchange_explicit_8(volatile ma_uint8* dst, ma_uint8 src, ma_atomic_memory_order order) - { - if (order > ma_atomic_memory_order_acquire) { - __sync_synchronize(); - } - return __sync_lock_test_and_set(dst, src); - } - static MA_INLINE ma_uint16 ma_atomic_exchange_explicit_16(volatile ma_uint16* dst, ma_uint16 src, ma_atomic_memory_order order) - { - ma_uint16 oldValue; - do { - oldValue = *dst; - } while (__sync_val_compare_and_swap(dst, oldValue, src) != oldValue); - (void)order; - return oldValue; - } - static MA_INLINE ma_uint32 ma_atomic_exchange_explicit_32(volatile ma_uint32* dst, ma_uint32 src, ma_atomic_memory_order order) - { - ma_uint32 oldValue; - do { - oldValue = *dst; - } while (__sync_val_compare_and_swap(dst, oldValue, src) != oldValue); - (void)order; - return oldValue; - } - static MA_INLINE ma_uint64 ma_atomic_exchange_explicit_64(volatile ma_uint64* dst, ma_uint64 src, ma_atomic_memory_order order) - { - ma_uint64 oldValue; - do { - oldValue = *dst; - } while (__sync_val_compare_and_swap(dst, oldValue, src) != oldValue); - (void)order; - return oldValue; - } - static MA_INLINE ma_uint8 ma_atomic_fetch_add_explicit_8(volatile ma_uint8* dst, ma_uint8 src, ma_atomic_memory_order order) - { - (void)order; - return __sync_fetch_and_add(dst, src); - } - static MA_INLINE ma_uint16 ma_atomic_fetch_add_explicit_16(volatile ma_uint16* dst, ma_uint16 src, ma_atomic_memory_order order) - { - (void)order; - return __sync_fetch_and_add(dst, src); - } - static MA_INLINE ma_uint32 ma_atomic_fetch_add_explicit_32(volatile ma_uint32* dst, ma_uint32 src, ma_atomic_memory_order order) - { - (void)order; - return __sync_fetch_and_add(dst, src); - } - static MA_INLINE ma_uint64 ma_atomic_fetch_add_explicit_64(volatile ma_uint64* dst, ma_uint64 src, ma_atomic_memory_order order) - { - (void)order; - return __sync_fetch_and_add(dst, src); - } - static MA_INLINE ma_uint8 ma_atomic_fetch_sub_explicit_8(volatile ma_uint8* dst, ma_uint8 src, ma_atomic_memory_order order) - { - (void)order; - return __sync_fetch_and_sub(dst, src); - } - static MA_INLINE ma_uint16 ma_atomic_fetch_sub_explicit_16(volatile ma_uint16* dst, ma_uint16 src, ma_atomic_memory_order order) - { - (void)order; - return __sync_fetch_and_sub(dst, src); - } - static MA_INLINE ma_uint32 ma_atomic_fetch_sub_explicit_32(volatile ma_uint32* dst, ma_uint32 src, ma_atomic_memory_order order) - { - (void)order; - return __sync_fetch_and_sub(dst, src); - } - static MA_INLINE ma_uint64 ma_atomic_fetch_sub_explicit_64(volatile ma_uint64* dst, ma_uint64 src, ma_atomic_memory_order order) - { - (void)order; - return __sync_fetch_and_sub(dst, src); - } - static MA_INLINE ma_uint8 ma_atomic_fetch_or_explicit_8(volatile ma_uint8* dst, ma_uint8 src, ma_atomic_memory_order order) - { - (void)order; - return __sync_fetch_and_or(dst, src); - } - static MA_INLINE ma_uint16 ma_atomic_fetch_or_explicit_16(volatile ma_uint16* dst, ma_uint16 src, ma_atomic_memory_order order) - { - (void)order; - return __sync_fetch_and_or(dst, src); - } - static MA_INLINE ma_uint32 ma_atomic_fetch_or_explicit_32(volatile ma_uint32* dst, ma_uint32 src, ma_atomic_memory_order order) - { - (void)order; - return __sync_fetch_and_or(dst, src); - } - static MA_INLINE ma_uint64 ma_atomic_fetch_or_explicit_64(volatile ma_uint64* dst, ma_uint64 src, ma_atomic_memory_order order) - { - (void)order; - return __sync_fetch_and_or(dst, src); - } - static MA_INLINE ma_uint8 ma_atomic_fetch_xor_explicit_8(volatile ma_uint8* dst, ma_uint8 src, ma_atomic_memory_order order) - { - (void)order; - return __sync_fetch_and_xor(dst, src); - } - static MA_INLINE ma_uint16 ma_atomic_fetch_xor_explicit_16(volatile ma_uint16* dst, ma_uint16 src, ma_atomic_memory_order order) - { - (void)order; - return __sync_fetch_and_xor(dst, src); - } - static MA_INLINE ma_uint32 ma_atomic_fetch_xor_explicit_32(volatile ma_uint32* dst, ma_uint32 src, ma_atomic_memory_order order) - { - (void)order; - return __sync_fetch_and_xor(dst, src); - } - static MA_INLINE ma_uint64 ma_atomic_fetch_xor_explicit_64(volatile ma_uint64* dst, ma_uint64 src, ma_atomic_memory_order order) - { - (void)order; - return __sync_fetch_and_xor(dst, src); - } - static MA_INLINE ma_uint8 ma_atomic_fetch_and_explicit_8(volatile ma_uint8* dst, ma_uint8 src, ma_atomic_memory_order order) - { - (void)order; - return __sync_fetch_and_and(dst, src); - } - static MA_INLINE ma_uint16 ma_atomic_fetch_and_explicit_16(volatile ma_uint16* dst, ma_uint16 src, ma_atomic_memory_order order) - { - (void)order; - return __sync_fetch_and_and(dst, src); - } - static MA_INLINE ma_uint32 ma_atomic_fetch_and_explicit_32(volatile ma_uint32* dst, ma_uint32 src, ma_atomic_memory_order order) - { - (void)order; - return __sync_fetch_and_and(dst, src); - } - static MA_INLINE ma_uint64 ma_atomic_fetch_and_explicit_64(volatile ma_uint64* dst, ma_uint64 src, ma_atomic_memory_order order) - { - (void)order; - return __sync_fetch_and_and(dst, src); - } - #define ma_atomic_compare_and_swap_8( dst, expected, desired) __sync_val_compare_and_swap(dst, expected, desired) - #define ma_atomic_compare_and_swap_16(dst, expected, desired) __sync_val_compare_and_swap(dst, expected, desired) - #define ma_atomic_compare_and_swap_32(dst, expected, desired) __sync_val_compare_and_swap(dst, expected, desired) - #define ma_atomic_compare_and_swap_64(dst, expected, desired) __sync_val_compare_and_swap(dst, expected, desired) - #else - #if defined(MA_X86) - #define ma_atomic_thread_fence(order) __asm__ __volatile__("lock; addl $0, (%%esp)" ::: "memory", "cc") - #elif defined(MA_X64) - #define ma_atomic_thread_fence(order) __asm__ __volatile__("lock; addq $0, (%%rsp)" ::: "memory", "cc") - #else - #error Unsupported architecture. Please submit a feature request. - #endif - static MA_INLINE ma_uint8 ma_atomic_compare_and_swap_8(volatile ma_uint8* dst, ma_uint8 expected, ma_uint8 desired) - { - ma_uint8 result; - #if defined(MA_X86) || defined(MA_X64) - __asm__ __volatile__("lock; cmpxchg %3, %0" : "+m"(*dst), "=a"(result) : "a"(expected), "d"(desired) : "cc"); - #else - #error Unsupported architecture. Please submit a feature request. - #endif - return result; - } - static MA_INLINE ma_uint16 ma_atomic_compare_and_swap_16(volatile ma_uint16* dst, ma_uint16 expected, ma_uint16 desired) - { - ma_uint16 result; - #if defined(MA_X86) || defined(MA_X64) - __asm__ __volatile__("lock; cmpxchg %3, %0" : "+m"(*dst), "=a"(result) : "a"(expected), "d"(desired) : "cc"); - #else - #error Unsupported architecture. Please submit a feature request. - #endif - return result; - } - static MA_INLINE ma_uint32 ma_atomic_compare_and_swap_32(volatile ma_uint32* dst, ma_uint32 expected, ma_uint32 desired) - { - ma_uint32 result; - #if defined(MA_X86) || defined(MA_X64) - __asm__ __volatile__("lock; cmpxchg %3, %0" : "+m"(*dst), "=a"(result) : "a"(expected), "d"(desired) : "cc"); - #else - #error Unsupported architecture. Please submit a feature request. - #endif - return result; - } - static MA_INLINE ma_uint64 ma_atomic_compare_and_swap_64(volatile ma_uint64* dst, ma_uint64 expected, ma_uint64 desired) - { - volatile ma_uint64 result; - #if defined(MA_X86) - ma_uint32 resultEAX; - ma_uint32 resultEDX; - __asm__ __volatile__("push %%ebx; xchg %5, %%ebx; lock; cmpxchg8b %0; pop %%ebx" : "+m"(*dst), "=a"(resultEAX), "=d"(resultEDX) : "a"(expected & 0xFFFFFFFF), "d"(expected >> 32), "r"(desired & 0xFFFFFFFF), "c"(desired >> 32) : "cc"); - result = ((ma_uint64)resultEDX << 32) | resultEAX; - #elif defined(MA_X64) - __asm__ __volatile__("lock; cmpxchg %3, %0" : "+m"(*dst), "=a"(result) : "a"(expected), "d"(desired) : "cc"); - #else - #error Unsupported architecture. Please submit a feature request. - #endif - return result; - } - static MA_INLINE ma_uint8 ma_atomic_exchange_explicit_8(volatile ma_uint8* dst, ma_uint8 src, ma_atomic_memory_order order) - { - ma_uint8 result = 0; - (void)order; - #if defined(MA_X86) || defined(MA_X64) - __asm__ __volatile__("lock; xchg %1, %0" : "+m"(*dst), "=a"(result) : "a"(src)); - #else - #error Unsupported architecture. Please submit a feature request. - #endif - return result; - } - static MA_INLINE ma_uint16 ma_atomic_exchange_explicit_16(volatile ma_uint16* dst, ma_uint16 src, ma_atomic_memory_order order) - { - ma_uint16 result = 0; - (void)order; - #if defined(MA_X86) || defined(MA_X64) - __asm__ __volatile__("lock; xchg %1, %0" : "+m"(*dst), "=a"(result) : "a"(src)); - #else - #error Unsupported architecture. Please submit a feature request. - #endif - return result; - } - static MA_INLINE ma_uint32 ma_atomic_exchange_explicit_32(volatile ma_uint32* dst, ma_uint32 src, ma_atomic_memory_order order) - { - ma_uint32 result; - (void)order; - #if defined(MA_X86) || defined(MA_X64) - __asm__ __volatile__("lock; xchg %1, %0" : "+m"(*dst), "=a"(result) : "a"(src)); - #else - #error Unsupported architecture. Please submit a feature request. - #endif - return result; - } - static MA_INLINE ma_uint64 ma_atomic_exchange_explicit_64(volatile ma_uint64* dst, ma_uint64 src, ma_atomic_memory_order order) - { - ma_uint64 result; - (void)order; - #if defined(MA_X86) - do { - result = *dst; - } while (ma_atomic_compare_and_swap_64(dst, result, src) != result); - #elif defined(MA_X64) - __asm__ __volatile__("lock; xchg %1, %0" : "+m"(*dst), "=a"(result) : "a"(src)); - #else - #error Unsupported architecture. Please submit a feature request. - #endif - return result; - } - static MA_INLINE ma_uint8 ma_atomic_fetch_add_explicit_8(volatile ma_uint8* dst, ma_uint8 src, ma_atomic_memory_order order) - { - ma_uint8 result; - (void)order; - #if defined(MA_X86) || defined(MA_X64) - __asm__ __volatile__("lock; xadd %1, %0" : "+m"(*dst), "=a"(result) : "a"(src) : "cc"); - #else - #error Unsupported architecture. Please submit a feature request. - #endif - return result; - } - static MA_INLINE ma_uint16 ma_atomic_fetch_add_explicit_16(volatile ma_uint16* dst, ma_uint16 src, ma_atomic_memory_order order) - { - ma_uint16 result; - (void)order; - #if defined(MA_X86) || defined(MA_X64) - __asm__ __volatile__("lock; xadd %1, %0" : "+m"(*dst), "=a"(result) : "a"(src) : "cc"); - #else - #error Unsupported architecture. Please submit a feature request. - #endif - return result; - } - static MA_INLINE ma_uint32 ma_atomic_fetch_add_explicit_32(volatile ma_uint32* dst, ma_uint32 src, ma_atomic_memory_order order) - { - ma_uint32 result; - (void)order; - #if defined(MA_X86) || defined(MA_X64) - __asm__ __volatile__("lock; xadd %1, %0" : "+m"(*dst), "=a"(result) : "a"(src) : "cc"); - #else - #error Unsupported architecture. Please submit a feature request. - #endif - return result; - } - static MA_INLINE ma_uint64 ma_atomic_fetch_add_explicit_64(volatile ma_uint64* dst, ma_uint64 src, ma_atomic_memory_order order) - { - #if defined(MA_X86) - ma_uint64 oldValue; - ma_uint64 newValue; - (void)order; - do { - oldValue = *dst; - newValue = oldValue + src; - } while (ma_atomic_compare_and_swap_64(dst, oldValue, newValue) != oldValue); - return oldValue; - #elif defined(MA_X64) - ma_uint64 result; - (void)order; - __asm__ __volatile__("lock; xadd %1, %0" : "+m"(*dst), "=a"(result) : "a"(src) : "cc"); - return result; - #endif - } - static MA_INLINE ma_uint8 ma_atomic_fetch_sub_explicit_8(volatile ma_uint8* dst, ma_uint8 src, ma_atomic_memory_order order) - { - ma_uint8 oldValue; - ma_uint8 newValue; - do { - oldValue = *dst; - newValue = (ma_uint8)(oldValue - src); - } while (ma_atomic_compare_and_swap_8(dst, oldValue, newValue) != oldValue); - (void)order; - return oldValue; - } - static MA_INLINE ma_uint16 ma_atomic_fetch_sub_explicit_16(volatile ma_uint16* dst, ma_uint16 src, ma_atomic_memory_order order) - { - ma_uint16 oldValue; - ma_uint16 newValue; - do { - oldValue = *dst; - newValue = (ma_uint16)(oldValue - src); - } while (ma_atomic_compare_and_swap_16(dst, oldValue, newValue) != oldValue); - (void)order; - return oldValue; - } - static MA_INLINE ma_uint32 ma_atomic_fetch_sub_explicit_32(volatile ma_uint32* dst, ma_uint32 src, ma_atomic_memory_order order) - { - ma_uint32 oldValue; - ma_uint32 newValue; - do { - oldValue = *dst; - newValue = oldValue - src; - } while (ma_atomic_compare_and_swap_32(dst, oldValue, newValue) != oldValue); - (void)order; - return oldValue; - } - static MA_INLINE ma_uint64 ma_atomic_fetch_sub_explicit_64(volatile ma_uint64* dst, ma_uint64 src, ma_atomic_memory_order order) - { - ma_uint64 oldValue; - ma_uint64 newValue; - do { - oldValue = *dst; - newValue = oldValue - src; - } while (ma_atomic_compare_and_swap_64(dst, oldValue, newValue) != oldValue); - (void)order; - return oldValue; - } - static MA_INLINE ma_uint8 ma_atomic_fetch_and_explicit_8(volatile ma_uint8* dst, ma_uint8 src, ma_atomic_memory_order order) - { - ma_uint8 oldValue; - ma_uint8 newValue; - do { - oldValue = *dst; - newValue = (ma_uint8)(oldValue & src); - } while (ma_atomic_compare_and_swap_8(dst, oldValue, newValue) != oldValue); - (void)order; - return oldValue; - } - static MA_INLINE ma_uint16 ma_atomic_fetch_and_explicit_16(volatile ma_uint16* dst, ma_uint16 src, ma_atomic_memory_order order) - { - ma_uint16 oldValue; - ma_uint16 newValue; - do { - oldValue = *dst; - newValue = (ma_uint16)(oldValue & src); - } while (ma_atomic_compare_and_swap_16(dst, oldValue, newValue) != oldValue); - (void)order; - return oldValue; - } - static MA_INLINE ma_uint32 ma_atomic_fetch_and_explicit_32(volatile ma_uint32* dst, ma_uint32 src, ma_atomic_memory_order order) - { - ma_uint32 oldValue; - ma_uint32 newValue; - do { - oldValue = *dst; - newValue = oldValue & src; - } while (ma_atomic_compare_and_swap_32(dst, oldValue, newValue) != oldValue); - (void)order; - return oldValue; - } - static MA_INLINE ma_uint64 ma_atomic_fetch_and_explicit_64(volatile ma_uint64* dst, ma_uint64 src, ma_atomic_memory_order order) - { - ma_uint64 oldValue; - ma_uint64 newValue; - do { - oldValue = *dst; - newValue = oldValue & src; - } while (ma_atomic_compare_and_swap_64(dst, oldValue, newValue) != oldValue); - (void)order; - return oldValue; - } - static MA_INLINE ma_uint8 ma_atomic_fetch_xor_explicit_8(volatile ma_uint8* dst, ma_uint8 src, ma_atomic_memory_order order) - { - ma_uint8 oldValue; - ma_uint8 newValue; - do { - oldValue = *dst; - newValue = (ma_uint8)(oldValue ^ src); - } while (ma_atomic_compare_and_swap_8(dst, oldValue, newValue) != oldValue); - (void)order; - return oldValue; - } - static MA_INLINE ma_uint16 ma_atomic_fetch_xor_explicit_16(volatile ma_uint16* dst, ma_uint16 src, ma_atomic_memory_order order) - { - ma_uint16 oldValue; - ma_uint16 newValue; - do { - oldValue = *dst; - newValue = (ma_uint16)(oldValue ^ src); - } while (ma_atomic_compare_and_swap_16(dst, oldValue, newValue) != oldValue); - (void)order; - return oldValue; - } - static MA_INLINE ma_uint32 ma_atomic_fetch_xor_explicit_32(volatile ma_uint32* dst, ma_uint32 src, ma_atomic_memory_order order) - { - ma_uint32 oldValue; - ma_uint32 newValue; - do { - oldValue = *dst; - newValue = oldValue ^ src; - } while (ma_atomic_compare_and_swap_32(dst, oldValue, newValue) != oldValue); - (void)order; - return oldValue; - } - static MA_INLINE ma_uint64 ma_atomic_fetch_xor_explicit_64(volatile ma_uint64* dst, ma_uint64 src, ma_atomic_memory_order order) - { - ma_uint64 oldValue; - ma_uint64 newValue; - do { - oldValue = *dst; - newValue = oldValue ^ src; - } while (ma_atomic_compare_and_swap_64(dst, oldValue, newValue) != oldValue); - (void)order; - return oldValue; - } - static MA_INLINE ma_uint8 ma_atomic_fetch_or_explicit_8(volatile ma_uint8* dst, ma_uint8 src, ma_atomic_memory_order order) - { - ma_uint8 oldValue; - ma_uint8 newValue; - do { - oldValue = *dst; - newValue = (ma_uint8)(oldValue | src); - } while (ma_atomic_compare_and_swap_8(dst, oldValue, newValue) != oldValue); - (void)order; - return oldValue; - } - static MA_INLINE ma_uint16 ma_atomic_fetch_or_explicit_16(volatile ma_uint16* dst, ma_uint16 src, ma_atomic_memory_order order) - { - ma_uint16 oldValue; - ma_uint16 newValue; - do { - oldValue = *dst; - newValue = (ma_uint16)(oldValue | src); - } while (ma_atomic_compare_and_swap_16(dst, oldValue, newValue) != oldValue); - (void)order; - return oldValue; - } - static MA_INLINE ma_uint32 ma_atomic_fetch_or_explicit_32(volatile ma_uint32* dst, ma_uint32 src, ma_atomic_memory_order order) - { - ma_uint32 oldValue; - ma_uint32 newValue; - do { - oldValue = *dst; - newValue = oldValue | src; - } while (ma_atomic_compare_and_swap_32(dst, oldValue, newValue) != oldValue); - (void)order; - return oldValue; - } - static MA_INLINE ma_uint64 ma_atomic_fetch_or_explicit_64(volatile ma_uint64* dst, ma_uint64 src, ma_atomic_memory_order order) - { - ma_uint64 oldValue; - ma_uint64 newValue; - do { - oldValue = *dst; - newValue = oldValue | src; - } while (ma_atomic_compare_and_swap_64(dst, oldValue, newValue) != oldValue); - (void)order; - return oldValue; - } - #endif - #define ma_atomic_signal_fence(order) ma_atomic_thread_fence(order) - static MA_INLINE ma_uint8 ma_atomic_load_explicit_8(volatile const ma_uint8* ptr, ma_atomic_memory_order order) - { - (void)order; - return ma_atomic_compare_and_swap_8((ma_uint8*)ptr, 0, 0); - } - static MA_INLINE ma_uint16 ma_atomic_load_explicit_16(volatile const ma_uint16* ptr, ma_atomic_memory_order order) - { - (void)order; - return ma_atomic_compare_and_swap_16((ma_uint16*)ptr, 0, 0); - } - static MA_INLINE ma_uint32 ma_atomic_load_explicit_32(volatile const ma_uint32* ptr, ma_atomic_memory_order order) - { - (void)order; - return ma_atomic_compare_and_swap_32((ma_uint32*)ptr, 0, 0); - } - static MA_INLINE ma_uint64 ma_atomic_load_explicit_64(volatile const ma_uint64* ptr, ma_atomic_memory_order order) - { - (void)order; - return ma_atomic_compare_and_swap_64((ma_uint64*)ptr, 0, 0); - } - #define ma_atomic_store_explicit_8( dst, src, order) (void)ma_atomic_exchange_explicit_8 (dst, src, order) - #define ma_atomic_store_explicit_16(dst, src, order) (void)ma_atomic_exchange_explicit_16(dst, src, order) - #define ma_atomic_store_explicit_32(dst, src, order) (void)ma_atomic_exchange_explicit_32(dst, src, order) - #define ma_atomic_store_explicit_64(dst, src, order) (void)ma_atomic_exchange_explicit_64(dst, src, order) - #define ma_atomic_test_and_set_explicit_8( dst, order) ma_atomic_exchange_explicit_8 (dst, 1, order) - #define ma_atomic_test_and_set_explicit_16(dst, order) ma_atomic_exchange_explicit_16(dst, 1, order) - #define ma_atomic_test_and_set_explicit_32(dst, order) ma_atomic_exchange_explicit_32(dst, 1, order) - #define ma_atomic_test_and_set_explicit_64(dst, order) ma_atomic_exchange_explicit_64(dst, 1, order) - #define ma_atomic_clear_explicit_8( dst, order) ma_atomic_store_explicit_8 (dst, 0, order) - #define ma_atomic_clear_explicit_16(dst, order) ma_atomic_store_explicit_16(dst, 0, order) - #define ma_atomic_clear_explicit_32(dst, order) ma_atomic_store_explicit_32(dst, 0, order) - #define ma_atomic_clear_explicit_64(dst, order) ma_atomic_store_explicit_64(dst, 0, order) - typedef ma_uint8 ma_atomic_flag; - #define ma_atomic_flag_test_and_set_explicit(ptr, order) (ma_bool32)ma_atomic_test_and_set_explicit_8(ptr, order) - #define ma_atomic_flag_clear_explicit(ptr, order) ma_atomic_clear_explicit_8(ptr, order) - #define ma_atomic_flag_load_explicit(ptr, order) ma_atomic_load_explicit_8(ptr, order) -#endif -#if !defined(MA_ATOMIC_HAS_NATIVE_COMPARE_EXCHANGE) - #if defined(MA_ATOMIC_HAS_8) - static MA_INLINE ma_bool32 ma_atomic_compare_exchange_strong_explicit_8(volatile ma_uint8* dst, ma_uint8* expected, ma_uint8 desired, ma_atomic_memory_order successOrder, ma_atomic_memory_order failureOrder) - { - ma_uint8 expectedValue; - ma_uint8 result; - (void)successOrder; - (void)failureOrder; - expectedValue = ma_atomic_load_explicit_8(expected, ma_atomic_memory_order_seq_cst); - result = ma_atomic_compare_and_swap_8(dst, expectedValue, desired); - if (result == expectedValue) { - return 1; - } else { - ma_atomic_store_explicit_8(expected, result, failureOrder); - return 0; - } - } - #endif - #if defined(MA_ATOMIC_HAS_16) - static MA_INLINE ma_bool32 ma_atomic_compare_exchange_strong_explicit_16(volatile ma_uint16* dst, ma_uint16* expected, ma_uint16 desired, ma_atomic_memory_order successOrder, ma_atomic_memory_order failureOrder) - { - ma_uint16 expectedValue; - ma_uint16 result; - (void)successOrder; - (void)failureOrder; - expectedValue = ma_atomic_load_explicit_16(expected, ma_atomic_memory_order_seq_cst); - result = ma_atomic_compare_and_swap_16(dst, expectedValue, desired); - if (result == expectedValue) { - return 1; - } else { - ma_atomic_store_explicit_16(expected, result, failureOrder); - return 0; - } - } - #endif - #if defined(MA_ATOMIC_HAS_32) - static MA_INLINE ma_bool32 ma_atomic_compare_exchange_strong_explicit_32(volatile ma_uint32* dst, ma_uint32* expected, ma_uint32 desired, ma_atomic_memory_order successOrder, ma_atomic_memory_order failureOrder) - { - ma_uint32 expectedValue; - ma_uint32 result; - (void)successOrder; - (void)failureOrder; - expectedValue = ma_atomic_load_explicit_32(expected, ma_atomic_memory_order_seq_cst); - result = ma_atomic_compare_and_swap_32(dst, expectedValue, desired); - if (result == expectedValue) { - return 1; - } else { - ma_atomic_store_explicit_32(expected, result, failureOrder); - return 0; - } - } - #endif - #if defined(MA_ATOMIC_HAS_64) - static MA_INLINE ma_bool32 ma_atomic_compare_exchange_strong_explicit_64(volatile ma_uint64* dst, volatile ma_uint64* expected, ma_uint64 desired, ma_atomic_memory_order successOrder, ma_atomic_memory_order failureOrder) - { - ma_uint64 expectedValue; - ma_uint64 result; - (void)successOrder; - (void)failureOrder; - expectedValue = ma_atomic_load_explicit_64(expected, ma_atomic_memory_order_seq_cst); - result = ma_atomic_compare_and_swap_64(dst, expectedValue, desired); - if (result == expectedValue) { - return 1; - } else { - ma_atomic_store_explicit_64(expected, result, failureOrder); - return 0; - } - } - #endif - #define ma_atomic_compare_exchange_weak_explicit_8( dst, expected, desired, successOrder, failureOrder) ma_atomic_compare_exchange_strong_explicit_8 (dst, expected, desired, successOrder, failureOrder) - #define ma_atomic_compare_exchange_weak_explicit_16(dst, expected, desired, successOrder, failureOrder) ma_atomic_compare_exchange_strong_explicit_16(dst, expected, desired, successOrder, failureOrder) - #define ma_atomic_compare_exchange_weak_explicit_32(dst, expected, desired, successOrder, failureOrder) ma_atomic_compare_exchange_strong_explicit_32(dst, expected, desired, successOrder, failureOrder) - #define ma_atomic_compare_exchange_weak_explicit_64(dst, expected, desired, successOrder, failureOrder) ma_atomic_compare_exchange_strong_explicit_64(dst, expected, desired, successOrder, failureOrder) -#endif -#if !defined(MA_ATOMIC_HAS_NATIVE_IS_LOCK_FREE) - static MA_INLINE ma_bool32 ma_atomic_is_lock_free_8(volatile void* ptr) - { - (void)ptr; - return 1; - } - static MA_INLINE ma_bool32 ma_atomic_is_lock_free_16(volatile void* ptr) - { - (void)ptr; - return 1; - } - static MA_INLINE ma_bool32 ma_atomic_is_lock_free_32(volatile void* ptr) - { - (void)ptr; - return 1; - } - static MA_INLINE ma_bool32 ma_atomic_is_lock_free_64(volatile void* ptr) - { - (void)ptr; - #if defined(MA_64BIT) - return 1; - #else - #if defined(MA_X86) || defined(MA_X64) - return 1; - #else - return 0; - #endif - #endif - } -#endif -#if defined(MA_64BIT) - static MA_INLINE ma_bool32 ma_atomic_is_lock_free_ptr(volatile void** ptr) - { - return ma_atomic_is_lock_free_64((volatile ma_uint64*)ptr); - } - static MA_INLINE void* ma_atomic_load_explicit_ptr(volatile void** ptr, ma_atomic_memory_order order) - { - return (void*)ma_atomic_load_explicit_64((volatile ma_uint64*)ptr, order); - } - static MA_INLINE void ma_atomic_store_explicit_ptr(volatile void** dst, void* src, ma_atomic_memory_order order) - { - ma_atomic_store_explicit_64((volatile ma_uint64*)dst, (ma_uint64)src, order); - } - static MA_INLINE void* ma_atomic_exchange_explicit_ptr(volatile void** dst, void* src, ma_atomic_memory_order order) - { - return (void*)ma_atomic_exchange_explicit_64((volatile ma_uint64*)dst, (ma_uint64)src, order); - } - static MA_INLINE ma_bool32 ma_atomic_compare_exchange_strong_explicit_ptr(volatile void** dst, void** expected, void* desired, ma_atomic_memory_order successOrder, ma_atomic_memory_order failureOrder) - { - return ma_atomic_compare_exchange_strong_explicit_64((volatile ma_uint64*)dst, (ma_uint64*)expected, (ma_uint64)desired, successOrder, failureOrder); - } - static MA_INLINE ma_bool32 ma_atomic_compare_exchange_weak_explicit_ptr(volatile void** dst, void** expected, void* desired, ma_atomic_memory_order successOrder, ma_atomic_memory_order failureOrder) - { - return ma_atomic_compare_exchange_weak_explicit_64((volatile ma_uint64*)dst, (ma_uint64*)expected, (ma_uint64)desired, successOrder, failureOrder); - } - static MA_INLINE void* ma_atomic_compare_and_swap_ptr(volatile void** dst, void* expected, void* desired) - { - return (void*)ma_atomic_compare_and_swap_64((volatile ma_uint64*)dst, (ma_uint64)expected, (ma_uint64)desired); - } -#elif defined(MA_32BIT) - static MA_INLINE ma_bool32 ma_atomic_is_lock_free_ptr(volatile void** ptr) - { - return ma_atomic_is_lock_free_32((volatile ma_uint32*)ptr); - } - static MA_INLINE void* ma_atomic_load_explicit_ptr(volatile void** ptr, ma_atomic_memory_order order) - { - return (void*)ma_atomic_load_explicit_32((volatile ma_uint32*)ptr, order); - } - static MA_INLINE void ma_atomic_store_explicit_ptr(volatile void** dst, void* src, ma_atomic_memory_order order) - { - ma_atomic_store_explicit_32((volatile ma_uint32*)dst, (ma_uint32)src, order); - } - static MA_INLINE void* ma_atomic_exchange_explicit_ptr(volatile void** dst, void* src, ma_atomic_memory_order order) - { - return (void*)ma_atomic_exchange_explicit_32((volatile ma_uint32*)dst, (ma_uint32)src, order); - } - static MA_INLINE ma_bool32 ma_atomic_compare_exchange_strong_explicit_ptr(volatile void** dst, void** expected, void* desired, ma_atomic_memory_order successOrder, ma_atomic_memory_order failureOrder) - { - return ma_atomic_compare_exchange_strong_explicit_32((volatile ma_uint32*)dst, (ma_uint32*)expected, (ma_uint32)desired, successOrder, failureOrder); - } - static MA_INLINE ma_bool32 ma_atomic_compare_exchange_weak_explicit_ptr(volatile void** dst, void** expected, void* desired, ma_atomic_memory_order successOrder, ma_atomic_memory_order failureOrder) - { - return ma_atomic_compare_exchange_weak_explicit_32((volatile ma_uint32*)dst, (ma_uint32*)expected, (ma_uint32)desired, successOrder, failureOrder); - } - static MA_INLINE void* ma_atomic_compare_and_swap_ptr(volatile void** dst, void* expected, void* desired) - { - return (void*)ma_atomic_compare_and_swap_32((volatile ma_uint32*)dst, (ma_uint32)expected, (ma_uint32)desired); - } -#else - #error Unsupported architecture. -#endif -#define ma_atomic_flag_test_and_set(ptr) ma_atomic_flag_test_and_set_explicit(ptr, ma_atomic_memory_order_seq_cst) -#define ma_atomic_flag_clear(ptr) ma_atomic_flag_clear_explicit(ptr, ma_atomic_memory_order_seq_cst) -#define ma_atomic_store_ptr(dst, src) ma_atomic_store_explicit_ptr((volatile void**)dst, (void*)src, ma_atomic_memory_order_seq_cst) -#define ma_atomic_load_ptr(ptr) ma_atomic_load_explicit_ptr((volatile void**)ptr, ma_atomic_memory_order_seq_cst) -#define ma_atomic_exchange_ptr(dst, src) ma_atomic_exchange_explicit_ptr((volatile void**)dst, (void*)src, ma_atomic_memory_order_seq_cst) -#define ma_atomic_compare_exchange_strong_ptr(dst, expected, desired) ma_atomic_compare_exchange_strong_explicit_ptr((volatile void**)dst, (void**)expected, (void*)desired, ma_atomic_memory_order_seq_cst, ma_atomic_memory_order_seq_cst) -#define ma_atomic_compare_exchange_weak_ptr(dst, expected, desired) ma_atomic_compare_exchange_weak_explicit_ptr((volatile void**)dst, (void**)expected, (void*)desired, ma_atomic_memory_order_seq_cst, ma_atomic_memory_order_seq_cst) -#define ma_atomic_test_and_set_8( ptr) ma_atomic_test_and_set_explicit_8( ptr, ma_atomic_memory_order_seq_cst) -#define ma_atomic_test_and_set_16(ptr) ma_atomic_test_and_set_explicit_16(ptr, ma_atomic_memory_order_seq_cst) -#define ma_atomic_test_and_set_32(ptr) ma_atomic_test_and_set_explicit_32(ptr, ma_atomic_memory_order_seq_cst) -#define ma_atomic_test_and_set_64(ptr) ma_atomic_test_and_set_explicit_64(ptr, ma_atomic_memory_order_seq_cst) -#define ma_atomic_clear_8( ptr) ma_atomic_clear_explicit_8( ptr, ma_atomic_memory_order_seq_cst) -#define ma_atomic_clear_16(ptr) ma_atomic_clear_explicit_16(ptr, ma_atomic_memory_order_seq_cst) -#define ma_atomic_clear_32(ptr) ma_atomic_clear_explicit_32(ptr, ma_atomic_memory_order_seq_cst) -#define ma_atomic_clear_64(ptr) ma_atomic_clear_explicit_64(ptr, ma_atomic_memory_order_seq_cst) -#define ma_atomic_store_8( dst, src) ma_atomic_store_explicit_8( dst, src, ma_atomic_memory_order_seq_cst) -#define ma_atomic_store_16(dst, src) ma_atomic_store_explicit_16(dst, src, ma_atomic_memory_order_seq_cst) -#define ma_atomic_store_32(dst, src) ma_atomic_store_explicit_32(dst, src, ma_atomic_memory_order_seq_cst) -#define ma_atomic_store_64(dst, src) ma_atomic_store_explicit_64(dst, src, ma_atomic_memory_order_seq_cst) -#define ma_atomic_load_8( ptr) ma_atomic_load_explicit_8( ptr, ma_atomic_memory_order_seq_cst) -#define ma_atomic_load_16(ptr) ma_atomic_load_explicit_16(ptr, ma_atomic_memory_order_seq_cst) -#define ma_atomic_load_32(ptr) ma_atomic_load_explicit_32(ptr, ma_atomic_memory_order_seq_cst) -#define ma_atomic_load_64(ptr) ma_atomic_load_explicit_64(ptr, ma_atomic_memory_order_seq_cst) -#define ma_atomic_exchange_8( dst, src) ma_atomic_exchange_explicit_8( dst, src, ma_atomic_memory_order_seq_cst) -#define ma_atomic_exchange_16(dst, src) ma_atomic_exchange_explicit_16(dst, src, ma_atomic_memory_order_seq_cst) -#define ma_atomic_exchange_32(dst, src) ma_atomic_exchange_explicit_32(dst, src, ma_atomic_memory_order_seq_cst) -#define ma_atomic_exchange_64(dst, src) ma_atomic_exchange_explicit_64(dst, src, ma_atomic_memory_order_seq_cst) -#define ma_atomic_compare_exchange_strong_8( dst, expected, desired) ma_atomic_compare_exchange_strong_explicit_8( dst, expected, desired, ma_atomic_memory_order_seq_cst, ma_atomic_memory_order_seq_cst) -#define ma_atomic_compare_exchange_strong_16(dst, expected, desired) ma_atomic_compare_exchange_strong_explicit_16(dst, expected, desired, ma_atomic_memory_order_seq_cst, ma_atomic_memory_order_seq_cst) -#define ma_atomic_compare_exchange_strong_32(dst, expected, desired) ma_atomic_compare_exchange_strong_explicit_32(dst, expected, desired, ma_atomic_memory_order_seq_cst, ma_atomic_memory_order_seq_cst) -#define ma_atomic_compare_exchange_strong_64(dst, expected, desired) ma_atomic_compare_exchange_strong_explicit_64(dst, expected, desired, ma_atomic_memory_order_seq_cst, ma_atomic_memory_order_seq_cst) -#define ma_atomic_compare_exchange_weak_8( dst, expected, desired) ma_atomic_compare_exchange_weak_explicit_8( dst, expected, desired, ma_atomic_memory_order_seq_cst, ma_atomic_memory_order_seq_cst) -#define ma_atomic_compare_exchange_weak_16( dst, expected, desired) ma_atomic_compare_exchange_weak_explicit_16(dst, expected, desired, ma_atomic_memory_order_seq_cst, ma_atomic_memory_order_seq_cst) -#define ma_atomic_compare_exchange_weak_32( dst, expected, desired) ma_atomic_compare_exchange_weak_explicit_32(dst, expected, desired, ma_atomic_memory_order_seq_cst, ma_atomic_memory_order_seq_cst) -#define ma_atomic_compare_exchange_weak_64( dst, expected, desired) ma_atomic_compare_exchange_weak_explicit_64(dst, expected, desired, ma_atomic_memory_order_seq_cst, ma_atomic_memory_order_seq_cst) -#define ma_atomic_fetch_add_8( dst, src) ma_atomic_fetch_add_explicit_8( dst, src, ma_atomic_memory_order_seq_cst) -#define ma_atomic_fetch_add_16(dst, src) ma_atomic_fetch_add_explicit_16(dst, src, ma_atomic_memory_order_seq_cst) -#define ma_atomic_fetch_add_32(dst, src) ma_atomic_fetch_add_explicit_32(dst, src, ma_atomic_memory_order_seq_cst) -#define ma_atomic_fetch_add_64(dst, src) ma_atomic_fetch_add_explicit_64(dst, src, ma_atomic_memory_order_seq_cst) -#define ma_atomic_fetch_sub_8( dst, src) ma_atomic_fetch_sub_explicit_8( dst, src, ma_atomic_memory_order_seq_cst) -#define ma_atomic_fetch_sub_16(dst, src) ma_atomic_fetch_sub_explicit_16(dst, src, ma_atomic_memory_order_seq_cst) -#define ma_atomic_fetch_sub_32(dst, src) ma_atomic_fetch_sub_explicit_32(dst, src, ma_atomic_memory_order_seq_cst) -#define ma_atomic_fetch_sub_64(dst, src) ma_atomic_fetch_sub_explicit_64(dst, src, ma_atomic_memory_order_seq_cst) -#define ma_atomic_fetch_or_8( dst, src) ma_atomic_fetch_or_explicit_8( dst, src, ma_atomic_memory_order_seq_cst) -#define ma_atomic_fetch_or_16(dst, src) ma_atomic_fetch_or_explicit_16(dst, src, ma_atomic_memory_order_seq_cst) -#define ma_atomic_fetch_or_32(dst, src) ma_atomic_fetch_or_explicit_32(dst, src, ma_atomic_memory_order_seq_cst) -#define ma_atomic_fetch_or_64(dst, src) ma_atomic_fetch_or_explicit_64(dst, src, ma_atomic_memory_order_seq_cst) -#define ma_atomic_fetch_xor_8( dst, src) ma_atomic_fetch_xor_explicit_8( dst, src, ma_atomic_memory_order_seq_cst) -#define ma_atomic_fetch_xor_16(dst, src) ma_atomic_fetch_xor_explicit_16(dst, src, ma_atomic_memory_order_seq_cst) -#define ma_atomic_fetch_xor_32(dst, src) ma_atomic_fetch_xor_explicit_32(dst, src, ma_atomic_memory_order_seq_cst) -#define ma_atomic_fetch_xor_64(dst, src) ma_atomic_fetch_xor_explicit_64(dst, src, ma_atomic_memory_order_seq_cst) -#define ma_atomic_fetch_and_8( dst, src) ma_atomic_fetch_and_explicit_8 (dst, src, ma_atomic_memory_order_seq_cst) -#define ma_atomic_fetch_and_16(dst, src) ma_atomic_fetch_and_explicit_16(dst, src, ma_atomic_memory_order_seq_cst) -#define ma_atomic_fetch_and_32(dst, src) ma_atomic_fetch_and_explicit_32(dst, src, ma_atomic_memory_order_seq_cst) -#define ma_atomic_fetch_and_64(dst, src) ma_atomic_fetch_and_explicit_64(dst, src, ma_atomic_memory_order_seq_cst) -#define ma_atomic_test_and_set_explicit_i8( ptr, order) (ma_int8 )ma_atomic_test_and_set_explicit_8( (ma_uint8* )ptr, order) -#define ma_atomic_test_and_set_explicit_i16(ptr, order) (ma_int16)ma_atomic_test_and_set_explicit_16((ma_uint16*)ptr, order) -#define ma_atomic_test_and_set_explicit_i32(ptr, order) (ma_int32)ma_atomic_test_and_set_explicit_32((ma_uint32*)ptr, order) -#define ma_atomic_test_and_set_explicit_i64(ptr, order) (ma_int64)ma_atomic_test_and_set_explicit_64((ma_uint64*)ptr, order) -#define ma_atomic_clear_explicit_i8( ptr, order) ma_atomic_clear_explicit_8( (ma_uint8* )ptr, order) -#define ma_atomic_clear_explicit_i16(ptr, order) ma_atomic_clear_explicit_16((ma_uint16*)ptr, order) -#define ma_atomic_clear_explicit_i32(ptr, order) ma_atomic_clear_explicit_32((ma_uint32*)ptr, order) -#define ma_atomic_clear_explicit_i64(ptr, order) ma_atomic_clear_explicit_64((ma_uint64*)ptr, order) -#define ma_atomic_store_explicit_i8( dst, src, order) ma_atomic_store_explicit_8( (ma_uint8* )dst, (ma_uint8 )src, order) -#define ma_atomic_store_explicit_i16(dst, src, order) ma_atomic_store_explicit_16((ma_uint16*)dst, (ma_uint16)src, order) -#define ma_atomic_store_explicit_i32(dst, src, order) ma_atomic_store_explicit_32((ma_uint32*)dst, (ma_uint32)src, order) -#define ma_atomic_store_explicit_i64(dst, src, order) ma_atomic_store_explicit_64((ma_uint64*)dst, (ma_uint64)src, order) -#define ma_atomic_load_explicit_i8( ptr, order) (ma_int8 )ma_atomic_load_explicit_8( (ma_uint8* )ptr, order) -#define ma_atomic_load_explicit_i16(ptr, order) (ma_int16)ma_atomic_load_explicit_16((ma_uint16*)ptr, order) -#define ma_atomic_load_explicit_i32(ptr, order) (ma_int32)ma_atomic_load_explicit_32((ma_uint32*)ptr, order) -#define ma_atomic_load_explicit_i64(ptr, order) (ma_int64)ma_atomic_load_explicit_64((ma_uint64*)ptr, order) -#define ma_atomic_exchange_explicit_i8( dst, src, order) (ma_int8 )ma_atomic_exchange_explicit_8 ((ma_uint8* )dst, (ma_uint8 )src, order) -#define ma_atomic_exchange_explicit_i16(dst, src, order) (ma_int16)ma_atomic_exchange_explicit_16((ma_uint16*)dst, (ma_uint16)src, order) -#define ma_atomic_exchange_explicit_i32(dst, src, order) (ma_int32)ma_atomic_exchange_explicit_32((ma_uint32*)dst, (ma_uint32)src, order) -#define ma_atomic_exchange_explicit_i64(dst, src, order) (ma_int64)ma_atomic_exchange_explicit_64((ma_uint64*)dst, (ma_uint64)src, order) -#define ma_atomic_compare_exchange_strong_explicit_i8( dst, expected, desired, successOrder, failureOrder) ma_atomic_compare_exchange_strong_explicit_8( (ma_uint8* )dst, (ma_uint8* )expected, (ma_uint8 )desired, successOrder, failureOrder) -#define ma_atomic_compare_exchange_strong_explicit_i16(dst, expected, desired, successOrder, failureOrder) ma_atomic_compare_exchange_strong_explicit_16((ma_uint16*)dst, (ma_uint16*)expected, (ma_uint16)desired, successOrder, failureOrder) -#define ma_atomic_compare_exchange_strong_explicit_i32(dst, expected, desired, successOrder, failureOrder) ma_atomic_compare_exchange_strong_explicit_32((ma_uint32*)dst, (ma_uint32*)expected, (ma_uint32)desired, successOrder, failureOrder) -#define ma_atomic_compare_exchange_strong_explicit_i64(dst, expected, desired, successOrder, failureOrder) ma_atomic_compare_exchange_strong_explicit_64((ma_uint64*)dst, (ma_uint64*)expected, (ma_uint64)desired, successOrder, failureOrder) -#define ma_atomic_compare_exchange_weak_explicit_i8( dst, expected, desired, successOrder, failureOrder) ma_atomic_compare_exchange_weak_explicit_8( (ma_uint8* )dst, (ma_uint8* )expected, (ma_uint8 )desired, successOrder, failureOrder) -#define ma_atomic_compare_exchange_weak_explicit_i16(dst, expected, desired, successOrder, failureOrder) ma_atomic_compare_exchange_weak_explicit_16((ma_uint16*)dst, (ma_uint16*)expected, (ma_uint16)desired, successOrder, failureOrder) -#define ma_atomic_compare_exchange_weak_explicit_i32(dst, expected, desired, successOrder, failureOrder) ma_atomic_compare_exchange_weak_explicit_32((ma_uint32*)dst, (ma_uint32*)expected, (ma_uint32)desired, successOrder, failureOrder) -#define ma_atomic_compare_exchange_weak_explicit_i64(dst, expected, desired, successOrder, failureOrder) ma_atomic_compare_exchange_weak_explicit_64((ma_uint64*)dst, (ma_uint64*)expected, (ma_uint64)desired, successOrder, failureOrder) -#define ma_atomic_fetch_add_explicit_i8( dst, src, order) (ma_int8 )ma_atomic_fetch_add_explicit_8( (ma_uint8* )dst, (ma_uint8 )src, order) -#define ma_atomic_fetch_add_explicit_i16(dst, src, order) (ma_int16)ma_atomic_fetch_add_explicit_16((ma_uint16*)dst, (ma_uint16)src, order) -#define ma_atomic_fetch_add_explicit_i32(dst, src, order) (ma_int32)ma_atomic_fetch_add_explicit_32((ma_uint32*)dst, (ma_uint32)src, order) -#define ma_atomic_fetch_add_explicit_i64(dst, src, order) (ma_int64)ma_atomic_fetch_add_explicit_64((ma_uint64*)dst, (ma_uint64)src, order) -#define ma_atomic_fetch_sub_explicit_i8( dst, src, order) (ma_int8 )ma_atomic_fetch_sub_explicit_8( (ma_uint8* )dst, (ma_uint8 )src, order) -#define ma_atomic_fetch_sub_explicit_i16(dst, src, order) (ma_int16)ma_atomic_fetch_sub_explicit_16((ma_uint16*)dst, (ma_uint16)src, order) -#define ma_atomic_fetch_sub_explicit_i32(dst, src, order) (ma_int32)ma_atomic_fetch_sub_explicit_32((ma_uint32*)dst, (ma_uint32)src, order) -#define ma_atomic_fetch_sub_explicit_i64(dst, src, order) (ma_int64)ma_atomic_fetch_sub_explicit_64((ma_uint64*)dst, (ma_uint64)src, order) -#define ma_atomic_fetch_or_explicit_i8( dst, src, order) (ma_int8 )ma_atomic_fetch_or_explicit_8( (ma_uint8* )dst, (ma_uint8 )src, order) -#define ma_atomic_fetch_or_explicit_i16(dst, src, order) (ma_int16)ma_atomic_fetch_or_explicit_16((ma_uint16*)dst, (ma_uint16)src, order) -#define ma_atomic_fetch_or_explicit_i32(dst, src, order) (ma_int32)ma_atomic_fetch_or_explicit_32((ma_uint32*)dst, (ma_uint32)src, order) -#define ma_atomic_fetch_or_explicit_i64(dst, src, order) (ma_int64)ma_atomic_fetch_or_explicit_64((ma_uint64*)dst, (ma_uint64)src, order) -#define ma_atomic_fetch_xor_explicit_i8( dst, src, order) (ma_int8 )ma_atomic_fetch_xor_explicit_8( (ma_uint8* )dst, (ma_uint8 )src, order) -#define ma_atomic_fetch_xor_explicit_i16(dst, src, order) (ma_int16)ma_atomic_fetch_xor_explicit_16((ma_uint16*)dst, (ma_uint16)src, order) -#define ma_atomic_fetch_xor_explicit_i32(dst, src, order) (ma_int32)ma_atomic_fetch_xor_explicit_32((ma_uint32*)dst, (ma_uint32)src, order) -#define ma_atomic_fetch_xor_explicit_i64(dst, src, order) (ma_int64)ma_atomic_fetch_xor_explicit_64((ma_uint64*)dst, (ma_uint64)src, order) -#define ma_atomic_fetch_and_explicit_i8( dst, src, order) (ma_int8 )ma_atomic_fetch_and_explicit_8( (ma_uint8* )dst, (ma_uint8 )src, order) -#define ma_atomic_fetch_and_explicit_i16(dst, src, order) (ma_int16)ma_atomic_fetch_and_explicit_16((ma_uint16*)dst, (ma_uint16)src, order) -#define ma_atomic_fetch_and_explicit_i32(dst, src, order) (ma_int32)ma_atomic_fetch_and_explicit_32((ma_uint32*)dst, (ma_uint32)src, order) -#define ma_atomic_fetch_and_explicit_i64(dst, src, order) (ma_int64)ma_atomic_fetch_and_explicit_64((ma_uint64*)dst, (ma_uint64)src, order) -#define ma_atomic_test_and_set_i8( ptr) ma_atomic_test_and_set_explicit_i8( ptr, ma_atomic_memory_order_seq_cst) -#define ma_atomic_test_and_set_i16(ptr) ma_atomic_test_and_set_explicit_i16(ptr, ma_atomic_memory_order_seq_cst) -#define ma_atomic_test_and_set_i32(ptr) ma_atomic_test_and_set_explicit_i32(ptr, ma_atomic_memory_order_seq_cst) -#define ma_atomic_test_and_set_i64(ptr) ma_atomic_test_and_set_explicit_i64(ptr, ma_atomic_memory_order_seq_cst) -#define ma_atomic_clear_i8( ptr) ma_atomic_clear_explicit_i8( ptr, ma_atomic_memory_order_seq_cst) -#define ma_atomic_clear_i16(ptr) ma_atomic_clear_explicit_i16(ptr, ma_atomic_memory_order_seq_cst) -#define ma_atomic_clear_i32(ptr) ma_atomic_clear_explicit_i32(ptr, ma_atomic_memory_order_seq_cst) -#define ma_atomic_clear_i64(ptr) ma_atomic_clear_explicit_i64(ptr, ma_atomic_memory_order_seq_cst) -#define ma_atomic_store_i8( dst, src) ma_atomic_store_explicit_i8( dst, src, ma_atomic_memory_order_seq_cst) -#define ma_atomic_store_i16(dst, src) ma_atomic_store_explicit_i16(dst, src, ma_atomic_memory_order_seq_cst) -#define ma_atomic_store_i32(dst, src) ma_atomic_store_explicit_i32(dst, src, ma_atomic_memory_order_seq_cst) -#define ma_atomic_store_i64(dst, src) ma_atomic_store_explicit_i64(dst, src, ma_atomic_memory_order_seq_cst) -#define ma_atomic_load_i8( ptr) ma_atomic_load_explicit_i8( ptr, ma_atomic_memory_order_seq_cst) -#define ma_atomic_load_i16(ptr) ma_atomic_load_explicit_i16(ptr, ma_atomic_memory_order_seq_cst) -#define ma_atomic_load_i32(ptr) ma_atomic_load_explicit_i32(ptr, ma_atomic_memory_order_seq_cst) -#define ma_atomic_load_i64(ptr) ma_atomic_load_explicit_i64(ptr, ma_atomic_memory_order_seq_cst) -#define ma_atomic_exchange_i8( dst, src) ma_atomic_exchange_explicit_i8( dst, src, ma_atomic_memory_order_seq_cst) -#define ma_atomic_exchange_i16(dst, src) ma_atomic_exchange_explicit_i16(dst, src, ma_atomic_memory_order_seq_cst) -#define ma_atomic_exchange_i32(dst, src) ma_atomic_exchange_explicit_i32(dst, src, ma_atomic_memory_order_seq_cst) -#define ma_atomic_exchange_i64(dst, src) ma_atomic_exchange_explicit_i64(dst, src, ma_atomic_memory_order_seq_cst) -#define ma_atomic_compare_exchange_strong_i8( dst, expected, desired) ma_atomic_compare_exchange_strong_explicit_i8( dst, expected, desired, ma_atomic_memory_order_seq_cst, ma_atomic_memory_order_seq_cst) -#define ma_atomic_compare_exchange_strong_i16(dst, expected, desired) ma_atomic_compare_exchange_strong_explicit_i16(dst, expected, desired, ma_atomic_memory_order_seq_cst, ma_atomic_memory_order_seq_cst) -#define ma_atomic_compare_exchange_strong_i32(dst, expected, desired) ma_atomic_compare_exchange_strong_explicit_i32(dst, expected, desired, ma_atomic_memory_order_seq_cst, ma_atomic_memory_order_seq_cst) -#define ma_atomic_compare_exchange_strong_i64(dst, expected, desired) ma_atomic_compare_exchange_strong_explicit_i64(dst, expected, desired, ma_atomic_memory_order_seq_cst, ma_atomic_memory_order_seq_cst) -#define ma_atomic_compare_exchange_weak_i8( dst, expected, desired) ma_atomic_compare_exchange_weak_explicit_i8( dst, expected, desired, ma_atomic_memory_order_seq_cst, ma_atomic_memory_order_seq_cst) -#define ma_atomic_compare_exchange_weak_i16(dst, expected, desired) ma_atomic_compare_exchange_weak_explicit_i16(dst, expected, desired, ma_atomic_memory_order_seq_cst, ma_atomic_memory_order_seq_cst) -#define ma_atomic_compare_exchange_weak_i32(dst, expected, desired) ma_atomic_compare_exchange_weak_explicit_i32(dst, expected, desired, ma_atomic_memory_order_seq_cst, ma_atomic_memory_order_seq_cst) -#define ma_atomic_compare_exchange_weak_i64(dst, expected, desired) ma_atomic_compare_exchange_weak_explicit_i64(dst, expected, desired, ma_atomic_memory_order_seq_cst, ma_atomic_memory_order_seq_cst) -#define ma_atomic_fetch_add_i8( dst, src) ma_atomic_fetch_add_explicit_i8( dst, src, ma_atomic_memory_order_seq_cst) -#define ma_atomic_fetch_add_i16(dst, src) ma_atomic_fetch_add_explicit_i16(dst, src, ma_atomic_memory_order_seq_cst) -#define ma_atomic_fetch_add_i32(dst, src) ma_atomic_fetch_add_explicit_i32(dst, src, ma_atomic_memory_order_seq_cst) -#define ma_atomic_fetch_add_i64(dst, src) ma_atomic_fetch_add_explicit_i64(dst, src, ma_atomic_memory_order_seq_cst) -#define ma_atomic_fetch_sub_i8( dst, src) ma_atomic_fetch_sub_explicit_i8( dst, src, ma_atomic_memory_order_seq_cst) -#define ma_atomic_fetch_sub_i16(dst, src) ma_atomic_fetch_sub_explicit_i16(dst, src, ma_atomic_memory_order_seq_cst) -#define ma_atomic_fetch_sub_i32(dst, src) ma_atomic_fetch_sub_explicit_i32(dst, src, ma_atomic_memory_order_seq_cst) -#define ma_atomic_fetch_sub_i64(dst, src) ma_atomic_fetch_sub_explicit_i64(dst, src, ma_atomic_memory_order_seq_cst) -#define ma_atomic_fetch_or_i8( dst, src) ma_atomic_fetch_or_explicit_i8( dst, src, ma_atomic_memory_order_seq_cst) -#define ma_atomic_fetch_or_i16(dst, src) ma_atomic_fetch_or_explicit_i16(dst, src, ma_atomic_memory_order_seq_cst) -#define ma_atomic_fetch_or_i32(dst, src) ma_atomic_fetch_or_explicit_i32(dst, src, ma_atomic_memory_order_seq_cst) -#define ma_atomic_fetch_or_i64(dst, src) ma_atomic_fetch_or_explicit_i64(dst, src, ma_atomic_memory_order_seq_cst) -#define ma_atomic_fetch_xor_i8( dst, src) ma_atomic_fetch_xor_explicit_i8( dst, src, ma_atomic_memory_order_seq_cst) -#define ma_atomic_fetch_xor_i16(dst, src) ma_atomic_fetch_xor_explicit_i16(dst, src, ma_atomic_memory_order_seq_cst) -#define ma_atomic_fetch_xor_i32(dst, src) ma_atomic_fetch_xor_explicit_i32(dst, src, ma_atomic_memory_order_seq_cst) -#define ma_atomic_fetch_xor_i64(dst, src) ma_atomic_fetch_xor_explicit_i64(dst, src, ma_atomic_memory_order_seq_cst) -#define ma_atomic_fetch_and_i8( dst, src) ma_atomic_fetch_and_explicit_i8( dst, src, ma_atomic_memory_order_seq_cst) -#define ma_atomic_fetch_and_i16(dst, src) ma_atomic_fetch_and_explicit_i16(dst, src, ma_atomic_memory_order_seq_cst) -#define ma_atomic_fetch_and_i32(dst, src) ma_atomic_fetch_and_explicit_i32(dst, src, ma_atomic_memory_order_seq_cst) -#define ma_atomic_fetch_and_i64(dst, src) ma_atomic_fetch_and_explicit_i64(dst, src, ma_atomic_memory_order_seq_cst) -#define ma_atomic_compare_and_swap_i8( dst, expected, dedsired) (ma_int8 )ma_atomic_compare_and_swap_8( (ma_uint8* )dst, (ma_uint8 )expected, (ma_uint8 )dedsired) -#define ma_atomic_compare_and_swap_i16(dst, expected, dedsired) (ma_int16)ma_atomic_compare_and_swap_16((ma_uint16*)dst, (ma_uint16)expected, (ma_uint16)dedsired) -#define ma_atomic_compare_and_swap_i32(dst, expected, dedsired) (ma_int32)ma_atomic_compare_and_swap_32((ma_uint32*)dst, (ma_uint32)expected, (ma_uint32)dedsired) -#define ma_atomic_compare_and_swap_i64(dst, expected, dedsired) (ma_int64)ma_atomic_compare_and_swap_64((ma_uint64*)dst, (ma_uint64)expected, (ma_uint64)dedsired) -typedef union -{ - ma_uint32 i; - float f; -} ma_atomic_if32; -typedef union -{ - ma_uint64 i; - double f; -} ma_atomic_if64; -#define ma_atomic_clear_explicit_f32(ptr, order) ma_atomic_clear_explicit_32((ma_uint32*)ptr, order) -#define ma_atomic_clear_explicit_f64(ptr, order) ma_atomic_clear_explicit_64((ma_uint64*)ptr, order) -static MA_INLINE void ma_atomic_store_explicit_f32(volatile float* dst, float src, ma_atomic_memory_order order) -{ - ma_atomic_if32 x; - x.f = src; - ma_atomic_store_explicit_32((volatile ma_uint32*)dst, x.i, order); -} -static MA_INLINE void ma_atomic_store_explicit_f64(volatile double* dst, double src, ma_atomic_memory_order order) -{ - ma_atomic_if64 x; - x.f = src; - ma_atomic_store_explicit_64((volatile ma_uint64*)dst, x.i, order); -} -static MA_INLINE float ma_atomic_load_explicit_f32(volatile const float* ptr, ma_atomic_memory_order order) -{ - ma_atomic_if32 r; - r.i = ma_atomic_load_explicit_32((volatile const ma_uint32*)ptr, order); - return r.f; -} -static MA_INLINE double ma_atomic_load_explicit_f64(volatile const double* ptr, ma_atomic_memory_order order) -{ - ma_atomic_if64 r; - r.i = ma_atomic_load_explicit_64((volatile const ma_uint64*)ptr, order); - return r.f; -} -static MA_INLINE float ma_atomic_exchange_explicit_f32(volatile float* dst, float src, ma_atomic_memory_order order) -{ - ma_atomic_if32 r; - ma_atomic_if32 x; - x.f = src; - r.i = ma_atomic_exchange_explicit_32((volatile ma_uint32*)dst, x.i, order); - return r.f; -} -static MA_INLINE double ma_atomic_exchange_explicit_f64(volatile double* dst, double src, ma_atomic_memory_order order) -{ - ma_atomic_if64 r; - ma_atomic_if64 x; - x.f = src; - r.i = ma_atomic_exchange_explicit_64((volatile ma_uint64*)dst, x.i, order); - return r.f; -} -static MA_INLINE ma_bool32 ma_atomic_compare_exchange_strong_explicit_f32(volatile float* dst, float* expected, float desired, ma_atomic_memory_order successOrder, ma_atomic_memory_order failureOrder) -{ - ma_atomic_if32 d; - d.f = desired; - return ma_atomic_compare_exchange_strong_explicit_32((volatile ma_uint32*)dst, (ma_uint32*)expected, d.i, successOrder, failureOrder); -} -static MA_INLINE ma_bool32 ma_atomic_compare_exchange_strong_explicit_f64(volatile double* dst, double* expected, double desired, ma_atomic_memory_order successOrder, ma_atomic_memory_order failureOrder) -{ - ma_atomic_if64 d; - d.f = desired; - return ma_atomic_compare_exchange_strong_explicit_64((volatile ma_uint64*)dst, (ma_uint64*)expected, d.i, successOrder, failureOrder); -} -static MA_INLINE ma_bool32 ma_atomic_compare_exchange_weak_explicit_f32(volatile float* dst, float* expected, float desired, ma_atomic_memory_order successOrder, ma_atomic_memory_order failureOrder) -{ - ma_atomic_if32 d; - d.f = desired; - return ma_atomic_compare_exchange_weak_explicit_32((volatile ma_uint32*)dst, (ma_uint32*)expected, d.i, successOrder, failureOrder); -} -static MA_INLINE ma_bool32 ma_atomic_compare_exchange_weak_explicit_f64(volatile double* dst, double* expected, double desired, ma_atomic_memory_order successOrder, ma_atomic_memory_order failureOrder) -{ - ma_atomic_if64 d; - d.f = desired; - return ma_atomic_compare_exchange_weak_explicit_64((volatile ma_uint64*)dst, (ma_uint64*)expected, d.i, successOrder, failureOrder); -} -static MA_INLINE float ma_atomic_fetch_add_explicit_f32(volatile float* dst, float src, ma_atomic_memory_order order) -{ - ma_atomic_if32 r; - ma_atomic_if32 x; - x.f = src; - r.i = ma_atomic_fetch_add_explicit_32((volatile ma_uint32*)dst, x.i, order); - return r.f; -} -static MA_INLINE double ma_atomic_fetch_add_explicit_f64(volatile double* dst, double src, ma_atomic_memory_order order) -{ - ma_atomic_if64 r; - ma_atomic_if64 x; - x.f = src; - r.i = ma_atomic_fetch_add_explicit_64((volatile ma_uint64*)dst, x.i, order); - return r.f; -} -static MA_INLINE float ma_atomic_fetch_sub_explicit_f32(volatile float* dst, float src, ma_atomic_memory_order order) -{ - ma_atomic_if32 r; - ma_atomic_if32 x; - x.f = src; - r.i = ma_atomic_fetch_sub_explicit_32((volatile ma_uint32*)dst, x.i, order); - return r.f; -} -static MA_INLINE double ma_atomic_fetch_sub_explicit_f64(volatile double* dst, double src, ma_atomic_memory_order order) -{ - ma_atomic_if64 r; - ma_atomic_if64 x; - x.f = src; - r.i = ma_atomic_fetch_sub_explicit_64((volatile ma_uint64*)dst, x.i, order); - return r.f; -} -static MA_INLINE float ma_atomic_fetch_or_explicit_f32(volatile float* dst, float src, ma_atomic_memory_order order) -{ - ma_atomic_if32 r; - ma_atomic_if32 x; - x.f = src; - r.i = ma_atomic_fetch_or_explicit_32((volatile ma_uint32*)dst, x.i, order); - return r.f; -} -static MA_INLINE double ma_atomic_fetch_or_explicit_f64(volatile double* dst, double src, ma_atomic_memory_order order) -{ - ma_atomic_if64 r; - ma_atomic_if64 x; - x.f = src; - r.i = ma_atomic_fetch_or_explicit_64((volatile ma_uint64*)dst, x.i, order); - return r.f; -} -static MA_INLINE float ma_atomic_fetch_xor_explicit_f32(volatile float* dst, float src, ma_atomic_memory_order order) -{ - ma_atomic_if32 r; - ma_atomic_if32 x; - x.f = src; - r.i = ma_atomic_fetch_xor_explicit_32((volatile ma_uint32*)dst, x.i, order); - return r.f; -} -static MA_INLINE double ma_atomic_fetch_xor_explicit_f64(volatile double* dst, double src, ma_atomic_memory_order order) -{ - ma_atomic_if64 r; - ma_atomic_if64 x; - x.f = src; - r.i = ma_atomic_fetch_xor_explicit_64((volatile ma_uint64*)dst, x.i, order); - return r.f; -} -static MA_INLINE float ma_atomic_fetch_and_explicit_f32(volatile float* dst, float src, ma_atomic_memory_order order) -{ - ma_atomic_if32 r; - ma_atomic_if32 x; - x.f = src; - r.i = ma_atomic_fetch_and_explicit_32((volatile ma_uint32*)dst, x.i, order); - return r.f; -} -static MA_INLINE double ma_atomic_fetch_and_explicit_f64(volatile double* dst, double src, ma_atomic_memory_order order) -{ - ma_atomic_if64 r; - ma_atomic_if64 x; - x.f = src; - r.i = ma_atomic_fetch_and_explicit_64((volatile ma_uint64*)dst, x.i, order); - return r.f; -} -#define ma_atomic_clear_f32(ptr) (float )ma_atomic_clear_explicit_f32(ptr, ma_atomic_memory_order_seq_cst) -#define ma_atomic_clear_f64(ptr) (double)ma_atomic_clear_explicit_f64(ptr, ma_atomic_memory_order_seq_cst) -#define ma_atomic_store_f32(dst, src) ma_atomic_store_explicit_f32(dst, src, ma_atomic_memory_order_seq_cst) -#define ma_atomic_store_f64(dst, src) ma_atomic_store_explicit_f64(dst, src, ma_atomic_memory_order_seq_cst) -#define ma_atomic_load_f32(ptr) (float )ma_atomic_load_explicit_f32(ptr, ma_atomic_memory_order_seq_cst) -#define ma_atomic_load_f64(ptr) (double)ma_atomic_load_explicit_f64(ptr, ma_atomic_memory_order_seq_cst) -#define ma_atomic_exchange_f32(dst, src) (float )ma_atomic_exchange_explicit_f32(dst, src, ma_atomic_memory_order_seq_cst) -#define ma_atomic_exchange_f64(dst, src) (double)ma_atomic_exchange_explicit_f64(dst, src, ma_atomic_memory_order_seq_cst) -#define ma_atomic_compare_exchange_strong_f32(dst, expected, desired) ma_atomic_compare_exchange_strong_explicit_f32(dst, expected, desired, ma_atomic_memory_order_seq_cst, ma_atomic_memory_order_seq_cst) -#define ma_atomic_compare_exchange_strong_f64(dst, expected, desired) ma_atomic_compare_exchange_strong_explicit_f64(dst, expected, desired, ma_atomic_memory_order_seq_cst, ma_atomic_memory_order_seq_cst) -#define ma_atomic_compare_exchange_weak_f32(dst, expected, desired) ma_atomic_compare_exchange_weak_explicit_f32(dst, expected, desired, ma_atomic_memory_order_seq_cst, ma_atomic_memory_order_seq_cst) -#define ma_atomic_compare_exchange_weak_f64(dst, expected, desired) ma_atomic_compare_exchange_weak_explicit_f64(dst, expected, desired, ma_atomic_memory_order_seq_cst, ma_atomic_memory_order_seq_cst) -#define ma_atomic_fetch_add_f32(dst, src) ma_atomic_fetch_add_explicit_f32(dst, src, ma_atomic_memory_order_seq_cst) -#define ma_atomic_fetch_add_f64(dst, src) ma_atomic_fetch_add_explicit_f64(dst, src, ma_atomic_memory_order_seq_cst) -#define ma_atomic_fetch_sub_f32(dst, src) ma_atomic_fetch_sub_explicit_f32(dst, src, ma_atomic_memory_order_seq_cst) -#define ma_atomic_fetch_sub_f64(dst, src) ma_atomic_fetch_sub_explicit_f64(dst, src, ma_atomic_memory_order_seq_cst) -#define ma_atomic_fetch_or_f32(dst, src) ma_atomic_fetch_or_explicit_f32(dst, src, ma_atomic_memory_order_seq_cst) -#define ma_atomic_fetch_or_f64(dst, src) ma_atomic_fetch_or_explicit_f64(dst, src, ma_atomic_memory_order_seq_cst) -#define ma_atomic_fetch_xor_f32(dst, src) ma_atomic_fetch_xor_explicit_f32(dst, src, ma_atomic_memory_order_seq_cst) -#define ma_atomic_fetch_xor_f64(dst, src) ma_atomic_fetch_xor_explicit_f64(dst, src, ma_atomic_memory_order_seq_cst) -#define ma_atomic_fetch_and_f32(dst, src) ma_atomic_fetch_and_explicit_f32(dst, src, ma_atomic_memory_order_seq_cst) -#define ma_atomic_fetch_and_f64(dst, src) ma_atomic_fetch_and_explicit_f64(dst, src, ma_atomic_memory_order_seq_cst) -static MA_INLINE float ma_atomic_compare_and_swap_f32(volatile float* dst, float expected, float desired) -{ - ma_atomic_if32 r; - ma_atomic_if32 e, d; - e.f = expected; - d.f = desired; - r.i = ma_atomic_compare_and_swap_32((volatile ma_uint32*)dst, e.i, d.i); - return r.f; -} -static MA_INLINE double ma_atomic_compare_and_swap_f64(volatile double* dst, double expected, double desired) -{ - ma_atomic_if64 r; - ma_atomic_if64 e, d; - e.f = expected; - d.f = desired; - r.i = ma_atomic_compare_and_swap_64((volatile ma_uint64*)dst, e.i, d.i); - return r.f; -} -typedef ma_atomic_flag ma_atomic_spinlock; -static MA_INLINE void ma_atomic_spinlock_lock(volatile ma_atomic_spinlock* pSpinlock) -{ - for (;;) { - if (ma_atomic_flag_test_and_set_explicit(pSpinlock, ma_atomic_memory_order_acquire) == 0) { - break; - } - while (ma_atomic_flag_load_explicit(pSpinlock, ma_atomic_memory_order_relaxed) == 1) { - } - } -} -static MA_INLINE void ma_atomic_spinlock_unlock(volatile ma_atomic_spinlock* pSpinlock) -{ - ma_atomic_flag_clear_explicit(pSpinlock, ma_atomic_memory_order_release); -} -#if defined(__clang__) || (defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6))) - #pragma GCC diagnostic pop -#endif -#if defined(__cplusplus) -} -#endif -#endif -/* c89atomic.h end */ - -#define MA_ATOMIC_SAFE_TYPE_IMPL(c89TypeExtension, type) \ - static MA_INLINE ma_##type ma_atomic_##type##_get(ma_atomic_##type* x) \ - { \ - return (ma_##type)ma_atomic_load_##c89TypeExtension(&x->value); \ - } \ - static MA_INLINE void ma_atomic_##type##_set(ma_atomic_##type* x, ma_##type value) \ - { \ - ma_atomic_store_##c89TypeExtension(&x->value, value); \ - } \ - static MA_INLINE ma_##type ma_atomic_##type##_exchange(ma_atomic_##type* x, ma_##type value) \ - { \ - return (ma_##type)ma_atomic_exchange_##c89TypeExtension(&x->value, value); \ - } \ - static MA_INLINE ma_bool32 ma_atomic_##type##_compare_exchange(ma_atomic_##type* x, ma_##type* expected, ma_##type desired) \ - { \ - return ma_atomic_compare_exchange_weak_##c89TypeExtension(&x->value, expected, desired); \ - } \ - static MA_INLINE ma_##type ma_atomic_##type##_fetch_add(ma_atomic_##type* x, ma_##type y) \ - { \ - return (ma_##type)ma_atomic_fetch_add_##c89TypeExtension(&x->value, y); \ - } \ - static MA_INLINE ma_##type ma_atomic_##type##_fetch_sub(ma_atomic_##type* x, ma_##type y) \ - { \ - return (ma_##type)ma_atomic_fetch_sub_##c89TypeExtension(&x->value, y); \ - } \ - static MA_INLINE ma_##type ma_atomic_##type##_fetch_or(ma_atomic_##type* x, ma_##type y) \ - { \ - return (ma_##type)ma_atomic_fetch_or_##c89TypeExtension(&x->value, y); \ - } \ - static MA_INLINE ma_##type ma_atomic_##type##_fetch_xor(ma_atomic_##type* x, ma_##type y) \ - { \ - return (ma_##type)ma_atomic_fetch_xor_##c89TypeExtension(&x->value, y); \ - } \ - static MA_INLINE ma_##type ma_atomic_##type##_fetch_and(ma_atomic_##type* x, ma_##type y) \ - { \ - return (ma_##type)ma_atomic_fetch_and_##c89TypeExtension(&x->value, y); \ - } \ - static MA_INLINE ma_##type ma_atomic_##type##_compare_and_swap(ma_atomic_##type* x, ma_##type expected, ma_##type desired) \ - { \ - return (ma_##type)ma_atomic_compare_and_swap_##c89TypeExtension(&x->value, expected, desired); \ - } \ - -#define MA_ATOMIC_SAFE_TYPE_IMPL_PTR(type) \ - static MA_INLINE ma_##type* ma_atomic_ptr_##type##_get(ma_atomic_ptr_##type* x) \ - { \ - return ma_atomic_load_ptr((void**)&x->value); \ - } \ - static MA_INLINE void ma_atomic_ptr_##type##_set(ma_atomic_ptr_##type* x, ma_##type* value) \ - { \ - ma_atomic_store_ptr((void**)&x->value, (void*)value); \ - } \ - static MA_INLINE ma_##type* ma_atomic_ptr_##type##_exchange(ma_atomic_ptr_##type* x, ma_##type* value) \ - { \ - return ma_atomic_exchange_ptr((void**)&x->value, (void*)value); \ - } \ - static MA_INLINE ma_bool32 ma_atomic_ptr_##type##_compare_exchange(ma_atomic_ptr_##type* x, ma_##type** expected, ma_##type* desired) \ - { \ - return ma_atomic_compare_exchange_weak_ptr((void**)&x->value, (void*)expected, (void*)desired); \ - } \ - static MA_INLINE ma_##type* ma_atomic_ptr_##type##_compare_and_swap(ma_atomic_ptr_##type* x, ma_##type* expected, ma_##type* desired) \ - { \ - return (ma_##type*)ma_atomic_compare_and_swap_ptr((void**)&x->value, (void*)expected, (void*)desired); \ - } \ - -MA_ATOMIC_SAFE_TYPE_IMPL(32, uint32) -MA_ATOMIC_SAFE_TYPE_IMPL(i32, int32) -MA_ATOMIC_SAFE_TYPE_IMPL(64, uint64) -MA_ATOMIC_SAFE_TYPE_IMPL(f32, float) -MA_ATOMIC_SAFE_TYPE_IMPL(32, bool32) - -#if !defined(MA_NO_DEVICE_IO) -MA_ATOMIC_SAFE_TYPE_IMPL(i32, device_state) -#endif - - -MA_API ma_uint64 ma_calculate_frame_count_after_resampling(ma_uint32 sampleRateOut, ma_uint32 sampleRateIn, ma_uint64 frameCountIn) -{ - /* This is based on the calculation in ma_linear_resampler_get_expected_output_frame_count(). */ - ma_uint64 outputFrameCount; - ma_uint64 preliminaryInputFrameCountFromFrac; - ma_uint64 preliminaryInputFrameCount; - - if (sampleRateIn == 0 || sampleRateOut == 0 || frameCountIn == 0) { - return 0; - } - - if (sampleRateOut == sampleRateIn) { - return frameCountIn; - } - - outputFrameCount = (frameCountIn * sampleRateOut) / sampleRateIn; - - preliminaryInputFrameCountFromFrac = (outputFrameCount * (sampleRateIn / sampleRateOut)) / sampleRateOut; - preliminaryInputFrameCount = (outputFrameCount * (sampleRateIn % sampleRateOut)) + preliminaryInputFrameCountFromFrac; - - if (preliminaryInputFrameCount <= frameCountIn) { - outputFrameCount += 1; - } - - return outputFrameCount; -} - -#ifndef MA_DATA_CONVERTER_STACK_BUFFER_SIZE -#define MA_DATA_CONVERTER_STACK_BUFFER_SIZE 4096 -#endif - - - -#if defined(MA_WIN32) -static ma_result ma_result_from_GetLastError(DWORD error) -{ - switch (error) - { - case ERROR_SUCCESS: return MA_SUCCESS; - case ERROR_PATH_NOT_FOUND: return MA_DOES_NOT_EXIST; - case ERROR_TOO_MANY_OPEN_FILES: return MA_TOO_MANY_OPEN_FILES; - case ERROR_NOT_ENOUGH_MEMORY: return MA_OUT_OF_MEMORY; - case ERROR_DISK_FULL: return MA_NO_SPACE; - case ERROR_HANDLE_EOF: return MA_AT_END; - case ERROR_NEGATIVE_SEEK: return MA_BAD_SEEK; - case ERROR_INVALID_PARAMETER: return MA_INVALID_ARGS; - case ERROR_ACCESS_DENIED: return MA_ACCESS_DENIED; - case ERROR_SEM_TIMEOUT: return MA_TIMEOUT; - case ERROR_FILE_NOT_FOUND: return MA_DOES_NOT_EXIST; - default: break; - } - - return MA_ERROR; -} -#endif /* MA_WIN32 */ - - -/******************************************************************************* - -Threading - -*******************************************************************************/ -static MA_INLINE ma_result ma_spinlock_lock_ex(volatile ma_spinlock* pSpinlock, ma_bool32 yield) -{ - if (pSpinlock == NULL) { - return MA_INVALID_ARGS; - } - - for (;;) { - if (ma_atomic_exchange_explicit_32(pSpinlock, 1, ma_atomic_memory_order_acquire) == 0) { - break; - } - - while (ma_atomic_load_explicit_32(pSpinlock, ma_atomic_memory_order_relaxed) == 1) { - if (yield) { - ma_yield(); - } - } - } - - return MA_SUCCESS; -} - -MA_API ma_result ma_spinlock_lock(volatile ma_spinlock* pSpinlock) -{ - return ma_spinlock_lock_ex(pSpinlock, MA_TRUE); -} - -MA_API ma_result ma_spinlock_lock_noyield(volatile ma_spinlock* pSpinlock) -{ - return ma_spinlock_lock_ex(pSpinlock, MA_FALSE); -} - -MA_API ma_result ma_spinlock_unlock(volatile ma_spinlock* pSpinlock) -{ - if (pSpinlock == NULL) { - return MA_INVALID_ARGS; - } - - ma_atomic_store_explicit_32(pSpinlock, 0, ma_atomic_memory_order_release); - return MA_SUCCESS; -} - - -#ifndef MA_NO_THREADING -#if defined(MA_POSIX) - #define MA_THREADCALL - typedef void* ma_thread_result; -#elif defined(MA_WIN32) - #define MA_THREADCALL WINAPI - typedef unsigned long ma_thread_result; -#endif - -typedef ma_thread_result (MA_THREADCALL * ma_thread_entry_proc)(void* pData); - -#ifdef MA_POSIX -static ma_result ma_thread_create__posix(ma_thread* pThread, ma_thread_priority priority, size_t stackSize, ma_thread_entry_proc entryProc, void* pData) -{ - int result; - pthread_attr_t* pAttr = NULL; - -#if !defined(__EMSCRIPTEN__) && !defined(__3DS__) - /* Try setting the thread priority. It's not critical if anything fails here. */ - pthread_attr_t attr; - if (pthread_attr_init(&attr) == 0) { - int scheduler = -1; - - /* We successfully initialized our attributes object so we can assign the pointer so it's passed into pthread_create(). */ - pAttr = &attr; - - /* We need to set the scheduler policy. Only do this if the OS supports pthread_attr_setschedpolicy() */ - #if !defined(MA_BEOS) - { - if (priority == ma_thread_priority_idle) { - #ifdef SCHED_IDLE - if (pthread_attr_setschedpolicy(&attr, SCHED_IDLE) == 0) { - scheduler = SCHED_IDLE; - } - #endif - } else if (priority == ma_thread_priority_realtime) { - #ifdef SCHED_FIFO - if (pthread_attr_setschedpolicy(&attr, SCHED_FIFO) == 0) { - scheduler = SCHED_FIFO; - } - #endif - #ifdef MA_LINUX - } else { - scheduler = sched_getscheduler(0); - #endif - } - } - #endif - - if (stackSize > 0) { - pthread_attr_setstacksize(&attr, stackSize); - } - - if (scheduler != -1) { - int priorityMin = sched_get_priority_min(scheduler); - int priorityMax = sched_get_priority_max(scheduler); - int priorityStep = (priorityMax - priorityMin) / 7; /* 7 = number of priorities supported by miniaudio. */ - - struct sched_param sched; - if (pthread_attr_getschedparam(&attr, &sched) == 0) { - if (priority == ma_thread_priority_idle) { - sched.sched_priority = priorityMin; - } else if (priority == ma_thread_priority_realtime) { - #if defined(MA_PTHREAD_REALTIME_THREAD_PRIORITY) - { - sched.sched_priority = MA_PTHREAD_REALTIME_THREAD_PRIORITY; - } - #else - { - sched.sched_priority = priorityMax; - } - #endif - } else { - sched.sched_priority += ((int)priority + 5) * priorityStep; /* +5 because the lowest priority is -5. */ - } - - if (sched.sched_priority < priorityMin) { - sched.sched_priority = priorityMin; - } - if (sched.sched_priority > priorityMax) { - sched.sched_priority = priorityMax; - } - - /* I'm not treating a failure of setting the priority as a critical error so not aborting on failure here. */ - if (pthread_attr_setschedparam(&attr, &sched) == 0) { - #if !defined(MA_ANDROID) || (defined(__ANDROID_API__) && __ANDROID_API__ >= 28) - { - pthread_attr_setinheritsched(&attr, PTHREAD_EXPLICIT_SCHED); - } - #endif - } - } - } - } -#else - /* It's the emscripten build. We'll have a few unused parameters. */ - (void)priority; - (void)stackSize; -#endif - - result = pthread_create((pthread_t*)pThread, pAttr, entryProc, pData); - - /* The thread attributes object is no longer required. */ - if (pAttr != NULL) { - pthread_attr_destroy(pAttr); - } - - if (result != 0) { - return ma_result_from_errno(result); - } - - return MA_SUCCESS; -} - -static void ma_thread_wait__posix(ma_thread* pThread) -{ - pthread_join((pthread_t)*pThread, NULL); -} - - -static ma_result ma_mutex_init__posix(ma_mutex* pMutex) -{ - int result; - - if (pMutex == NULL) { - return MA_INVALID_ARGS; - } - - MA_ZERO_OBJECT(pMutex); - - result = pthread_mutex_init((pthread_mutex_t*)pMutex, NULL); - if (result != 0) { - return ma_result_from_errno(result); - } - - return MA_SUCCESS; -} - -static void ma_mutex_uninit__posix(ma_mutex* pMutex) -{ - pthread_mutex_destroy((pthread_mutex_t*)pMutex); -} - -static void ma_mutex_lock__posix(ma_mutex* pMutex) -{ - pthread_mutex_lock((pthread_mutex_t*)pMutex); -} - -static void ma_mutex_unlock__posix(ma_mutex* pMutex) -{ - pthread_mutex_unlock((pthread_mutex_t*)pMutex); -} - - -static ma_result ma_event_init__posix(ma_event* pEvent) -{ - int result; - - result = pthread_mutex_init((pthread_mutex_t*)&pEvent->lock, NULL); - if (result != 0) { - return ma_result_from_errno(result); - } - - result = pthread_cond_init((pthread_cond_t*)&pEvent->cond, NULL); - if (result != 0) { - pthread_mutex_destroy((pthread_mutex_t*)&pEvent->lock); - return ma_result_from_errno(result); - } - - pEvent->value = 0; - return MA_SUCCESS; -} - -static void ma_event_uninit__posix(ma_event* pEvent) -{ - pthread_cond_destroy((pthread_cond_t*)&pEvent->cond); - pthread_mutex_destroy((pthread_mutex_t*)&pEvent->lock); -} - -static ma_result ma_event_wait__posix(ma_event* pEvent) -{ - pthread_mutex_lock((pthread_mutex_t*)&pEvent->lock); - { - while (pEvent->value == 0) { - pthread_cond_wait((pthread_cond_t*)&pEvent->cond, (pthread_mutex_t*)&pEvent->lock); - } - pEvent->value = 0; /* Auto-reset. */ - } - pthread_mutex_unlock((pthread_mutex_t*)&pEvent->lock); - - return MA_SUCCESS; -} - -static ma_result ma_event_signal__posix(ma_event* pEvent) -{ - pthread_mutex_lock((pthread_mutex_t*)&pEvent->lock); - { - pEvent->value = 1; - pthread_cond_signal((pthread_cond_t*)&pEvent->cond); - } - pthread_mutex_unlock((pthread_mutex_t*)&pEvent->lock); - - return MA_SUCCESS; -} - - -static ma_result ma_semaphore_init__posix(int initialValue, ma_semaphore* pSemaphore) -{ - int result; - - if (pSemaphore == NULL) { - return MA_INVALID_ARGS; - } - - pSemaphore->value = initialValue; - - result = pthread_mutex_init((pthread_mutex_t*)&pSemaphore->lock, NULL); - if (result != 0) { - return ma_result_from_errno(result); /* Failed to create mutex. */ - } - - result = pthread_cond_init((pthread_cond_t*)&pSemaphore->cond, NULL); - if (result != 0) { - pthread_mutex_destroy((pthread_mutex_t*)&pSemaphore->lock); - return ma_result_from_errno(result); /* Failed to create condition variable. */ - } - - return MA_SUCCESS; -} - -static void ma_semaphore_uninit__posix(ma_semaphore* pSemaphore) -{ - if (pSemaphore == NULL) { - return; - } - - pthread_cond_destroy((pthread_cond_t*)&pSemaphore->cond); - pthread_mutex_destroy((pthread_mutex_t*)&pSemaphore->lock); -} - -static ma_result ma_semaphore_wait__posix(ma_semaphore* pSemaphore) -{ - if (pSemaphore == NULL) { - return MA_INVALID_ARGS; - } - - pthread_mutex_lock((pthread_mutex_t*)&pSemaphore->lock); - { - /* We need to wait on a condition variable before escaping. We can't return from this function until the semaphore has been signaled. */ - while (pSemaphore->value == 0) { - pthread_cond_wait((pthread_cond_t*)&pSemaphore->cond, (pthread_mutex_t*)&pSemaphore->lock); - } - - pSemaphore->value -= 1; - } - pthread_mutex_unlock((pthread_mutex_t*)&pSemaphore->lock); - - return MA_SUCCESS; -} - -static ma_result ma_semaphore_release__posix(ma_semaphore* pSemaphore) -{ - if (pSemaphore == NULL) { - return MA_INVALID_ARGS; - } - - pthread_mutex_lock((pthread_mutex_t*)&pSemaphore->lock); - { - pSemaphore->value += 1; - pthread_cond_signal((pthread_cond_t*)&pSemaphore->cond); - } - pthread_mutex_unlock((pthread_mutex_t*)&pSemaphore->lock); - - return MA_SUCCESS; -} -#elif defined(MA_WIN32) -static int ma_thread_priority_to_win32(ma_thread_priority priority) -{ - switch (priority) { - case ma_thread_priority_idle: return THREAD_PRIORITY_IDLE; - case ma_thread_priority_lowest: return THREAD_PRIORITY_LOWEST; - case ma_thread_priority_low: return THREAD_PRIORITY_BELOW_NORMAL; - case ma_thread_priority_normal: return THREAD_PRIORITY_NORMAL; - case ma_thread_priority_high: return THREAD_PRIORITY_ABOVE_NORMAL; - case ma_thread_priority_highest: return THREAD_PRIORITY_HIGHEST; - case ma_thread_priority_realtime: return THREAD_PRIORITY_TIME_CRITICAL; - default: return THREAD_PRIORITY_NORMAL; - } -} - -static ma_result ma_thread_create__win32(ma_thread* pThread, ma_thread_priority priority, size_t stackSize, ma_thread_entry_proc entryProc, void* pData) -{ - DWORD threadID; /* Not used. Only used for passing into CreateThread() so it doesn't fail on Windows 98. */ - - *pThread = CreateThread(NULL, stackSize, entryProc, pData, 0, &threadID); - if (*pThread == NULL) { - return ma_result_from_GetLastError(GetLastError()); - } - - SetThreadPriority((HANDLE)*pThread, ma_thread_priority_to_win32(priority)); - - return MA_SUCCESS; -} - -static void ma_thread_wait__win32(ma_thread* pThread) -{ - WaitForSingleObject((HANDLE)*pThread, INFINITE); - CloseHandle((HANDLE)*pThread); -} - - -static ma_result ma_mutex_init__win32(ma_mutex* pMutex) -{ - *pMutex = CreateEventA(NULL, FALSE, TRUE, NULL); - if (*pMutex == NULL) { - return ma_result_from_GetLastError(GetLastError()); - } - - return MA_SUCCESS; -} - -static void ma_mutex_uninit__win32(ma_mutex* pMutex) -{ - CloseHandle((HANDLE)*pMutex); -} - -static void ma_mutex_lock__win32(ma_mutex* pMutex) -{ - WaitForSingleObject((HANDLE)*pMutex, INFINITE); -} - -static void ma_mutex_unlock__win32(ma_mutex* pMutex) -{ - SetEvent((HANDLE)*pMutex); -} - - -static ma_result ma_event_init__win32(ma_event* pEvent) -{ - *pEvent = CreateEventA(NULL, FALSE, FALSE, NULL); - if (*pEvent == NULL) { - return ma_result_from_GetLastError(GetLastError()); - } - - return MA_SUCCESS; -} - -static void ma_event_uninit__win32(ma_event* pEvent) -{ - CloseHandle((HANDLE)*pEvent); -} - -static ma_result ma_event_wait__win32(ma_event* pEvent) -{ - DWORD result = WaitForSingleObject((HANDLE)*pEvent, INFINITE); - if (result == WAIT_OBJECT_0) { - return MA_SUCCESS; - } - - if (result == WAIT_TIMEOUT) { - return MA_TIMEOUT; - } - - return ma_result_from_GetLastError(GetLastError()); -} - -static ma_result ma_event_signal__win32(ma_event* pEvent) -{ - BOOL result = SetEvent((HANDLE)*pEvent); - if (result == 0) { - return ma_result_from_GetLastError(GetLastError()); - } - - return MA_SUCCESS; -} - - -static ma_result ma_semaphore_init__win32(int initialValue, ma_semaphore* pSemaphore) -{ - *pSemaphore = CreateSemaphoreW(NULL, (LONG)initialValue, LONG_MAX, NULL); - if (*pSemaphore == NULL) { - return ma_result_from_GetLastError(GetLastError()); - } - - return MA_SUCCESS; -} - -static void ma_semaphore_uninit__win32(ma_semaphore* pSemaphore) -{ - CloseHandle((HANDLE)*pSemaphore); -} - -static ma_result ma_semaphore_wait__win32(ma_semaphore* pSemaphore) -{ - DWORD result = WaitForSingleObject((HANDLE)*pSemaphore, INFINITE); - if (result == WAIT_OBJECT_0) { - return MA_SUCCESS; - } - - if (result == WAIT_TIMEOUT) { - return MA_TIMEOUT; - } - - return ma_result_from_GetLastError(GetLastError()); -} - -static ma_result ma_semaphore_release__win32(ma_semaphore* pSemaphore) -{ - BOOL result = ReleaseSemaphore((HANDLE)*pSemaphore, 1, NULL); - if (result == 0) { - return ma_result_from_GetLastError(GetLastError()); - } - - return MA_SUCCESS; -} -#endif - -typedef struct -{ - ma_thread_entry_proc entryProc; - void* pData; - ma_allocation_callbacks allocationCallbacks; -} ma_thread_proxy_data; - -static ma_thread_result MA_THREADCALL ma_thread_entry_proxy(void* pData) -{ - ma_thread_proxy_data* pProxyData = (ma_thread_proxy_data*)pData; - ma_thread_entry_proc entryProc; - void* pEntryProcData; - ma_thread_result result; - - #if defined(MA_ON_THREAD_ENTRY) - MA_ON_THREAD_ENTRY - #endif - - entryProc = pProxyData->entryProc; - pEntryProcData = pProxyData->pData; - - /* Free the proxy data before getting into the real thread entry proc. */ - ma_free(pProxyData, &pProxyData->allocationCallbacks); - - result = entryProc(pEntryProcData); - - #if defined(MA_ON_THREAD_EXIT) - MA_ON_THREAD_EXIT - #endif - - return result; -} - -static ma_result ma_thread_create(ma_thread* pThread, ma_thread_priority priority, size_t stackSize, ma_thread_entry_proc entryProc, void* pData, const ma_allocation_callbacks* pAllocationCallbacks) -{ - ma_result result; - ma_thread_proxy_data* pProxyData; - - if (pThread == NULL || entryProc == NULL) { - return MA_INVALID_ARGS; - } - - pProxyData = (ma_thread_proxy_data*)ma_malloc(sizeof(*pProxyData), pAllocationCallbacks); /* Will be freed by the proxy entry proc. */ - if (pProxyData == NULL) { - return MA_OUT_OF_MEMORY; - } - -#if defined(MA_THREAD_DEFAULT_STACK_SIZE) - if (stackSize == 0) { - stackSize = MA_THREAD_DEFAULT_STACK_SIZE; - } -#endif - - pProxyData->entryProc = entryProc; - pProxyData->pData = pData; - ma_allocation_callbacks_init_copy(&pProxyData->allocationCallbacks, pAllocationCallbacks); - -#if defined(MA_POSIX) - result = ma_thread_create__posix(pThread, priority, stackSize, ma_thread_entry_proxy, pProxyData); -#elif defined(MA_WIN32) - result = ma_thread_create__win32(pThread, priority, stackSize, ma_thread_entry_proxy, pProxyData); -#endif - - if (result != MA_SUCCESS) { - ma_free(pProxyData, pAllocationCallbacks); - return result; - } - - return MA_SUCCESS; -} - -static void ma_thread_wait(ma_thread* pThread) -{ - if (pThread == NULL) { - return; - } - -#if defined(MA_POSIX) - ma_thread_wait__posix(pThread); -#elif defined(MA_WIN32) - ma_thread_wait__win32(pThread); -#endif -} - - -MA_API ma_result ma_mutex_init(ma_mutex* pMutex) -{ - if (pMutex == NULL) { - MA_ASSERT(MA_FALSE); /* Fire an assert so the caller is aware of this bug. */ - return MA_INVALID_ARGS; - } - -#if defined(MA_POSIX) - return ma_mutex_init__posix(pMutex); -#elif defined(MA_WIN32) - return ma_mutex_init__win32(pMutex); -#endif -} - -MA_API void ma_mutex_uninit(ma_mutex* pMutex) -{ - if (pMutex == NULL) { - return; - } - -#if defined(MA_POSIX) - ma_mutex_uninit__posix(pMutex); -#elif defined(MA_WIN32) - ma_mutex_uninit__win32(pMutex); -#endif -} - -MA_API void ma_mutex_lock(ma_mutex* pMutex) -{ - if (pMutex == NULL) { - MA_ASSERT(MA_FALSE); /* Fire an assert so the caller is aware of this bug. */ - return; - } - -#if defined(MA_POSIX) - ma_mutex_lock__posix(pMutex); -#elif defined(MA_WIN32) - ma_mutex_lock__win32(pMutex); -#endif -} - -MA_API void ma_mutex_unlock(ma_mutex* pMutex) -{ - if (pMutex == NULL) { - MA_ASSERT(MA_FALSE); /* Fire an assert so the caller is aware of this bug. */ - return; - } - -#if defined(MA_POSIX) - ma_mutex_unlock__posix(pMutex); -#elif defined(MA_WIN32) - ma_mutex_unlock__win32(pMutex); -#endif -} - - -MA_API ma_result ma_event_init(ma_event* pEvent) -{ - if (pEvent == NULL) { - MA_ASSERT(MA_FALSE); /* Fire an assert so the caller is aware of this bug. */ - return MA_INVALID_ARGS; - } - -#if defined(MA_POSIX) - return ma_event_init__posix(pEvent); -#elif defined(MA_WIN32) - return ma_event_init__win32(pEvent); -#endif -} - -#if 0 -static ma_result ma_event_alloc_and_init(ma_event** ppEvent, ma_allocation_callbacks* pAllocationCallbacks) -{ - ma_result result; - ma_event* pEvent; - - if (ppEvent == NULL) { - return MA_INVALID_ARGS; - } - - *ppEvent = NULL; - - pEvent = ma_malloc(sizeof(*pEvent), pAllocationCallbacks); - if (pEvent == NULL) { - return MA_OUT_OF_MEMORY; - } - - result = ma_event_init(pEvent); - if (result != MA_SUCCESS) { - ma_free(pEvent, pAllocationCallbacks); - return result; - } - - *ppEvent = pEvent; - return result; -} -#endif - -MA_API void ma_event_uninit(ma_event* pEvent) -{ - if (pEvent == NULL) { - return; - } - -#if defined(MA_POSIX) - ma_event_uninit__posix(pEvent); -#elif defined(MA_WIN32) - ma_event_uninit__win32(pEvent); -#endif -} - -#if 0 -static void ma_event_uninit_and_free(ma_event* pEvent, ma_allocation_callbacks* pAllocationCallbacks) -{ - if (pEvent == NULL) { - return; - } - - ma_event_uninit(pEvent); - ma_free(pEvent, pAllocationCallbacks); -} -#endif - -MA_API ma_result ma_event_wait(ma_event* pEvent) -{ - if (pEvent == NULL) { - MA_ASSERT(MA_FALSE); /* Fire an assert to the caller is aware of this bug. */ - return MA_INVALID_ARGS; - } - -#if defined(MA_POSIX) - return ma_event_wait__posix(pEvent); -#elif defined(MA_WIN32) - return ma_event_wait__win32(pEvent); -#endif -} - -MA_API ma_result ma_event_signal(ma_event* pEvent) -{ - if (pEvent == NULL) { - MA_ASSERT(MA_FALSE); /* Fire an assert to the caller is aware of this bug. */ - return MA_INVALID_ARGS; - } - -#if defined(MA_POSIX) - return ma_event_signal__posix(pEvent); -#elif defined(MA_WIN32) - return ma_event_signal__win32(pEvent); -#endif -} - - -MA_API ma_result ma_semaphore_init(int initialValue, ma_semaphore* pSemaphore) -{ - if (pSemaphore == NULL) { - MA_ASSERT(MA_FALSE); /* Fire an assert so the caller is aware of this bug. */ - return MA_INVALID_ARGS; - } - -#if defined(MA_POSIX) - return ma_semaphore_init__posix(initialValue, pSemaphore); -#elif defined(MA_WIN32) - return ma_semaphore_init__win32(initialValue, pSemaphore); -#endif -} - -MA_API void ma_semaphore_uninit(ma_semaphore* pSemaphore) -{ - if (pSemaphore == NULL) { - MA_ASSERT(MA_FALSE); /* Fire an assert so the caller is aware of this bug. */ - return; - } - -#if defined(MA_POSIX) - ma_semaphore_uninit__posix(pSemaphore); -#elif defined(MA_WIN32) - ma_semaphore_uninit__win32(pSemaphore); -#endif -} - -MA_API ma_result ma_semaphore_wait(ma_semaphore* pSemaphore) -{ - if (pSemaphore == NULL) { - MA_ASSERT(MA_FALSE); /* Fire an assert so the caller is aware of this bug. */ - return MA_INVALID_ARGS; - } - -#if defined(MA_POSIX) - return ma_semaphore_wait__posix(pSemaphore); -#elif defined(MA_WIN32) - return ma_semaphore_wait__win32(pSemaphore); -#endif -} - -MA_API ma_result ma_semaphore_release(ma_semaphore* pSemaphore) -{ - if (pSemaphore == NULL) { - MA_ASSERT(MA_FALSE); /* Fire an assert so the caller is aware of this bug. */ - return MA_INVALID_ARGS; - } - -#if defined(MA_POSIX) - return ma_semaphore_release__posix(pSemaphore); -#elif defined(MA_WIN32) - return ma_semaphore_release__win32(pSemaphore); -#endif -} -#else -/* MA_NO_THREADING is set which means threading is disabled. Threading is required by some API families. If any of these are enabled we need to throw an error. */ -#ifndef MA_NO_DEVICE_IO -#error "MA_NO_THREADING cannot be used without MA_NO_DEVICE_IO"; -#endif -#endif /* MA_NO_THREADING */ - - - -#define MA_FENCE_COUNTER_MAX 0x7FFFFFFF - -MA_API ma_result ma_fence_init(ma_fence* pFence) -{ - if (pFence == NULL) { - return MA_INVALID_ARGS; - } - - MA_ZERO_OBJECT(pFence); - pFence->counter = 0; - - #ifndef MA_NO_THREADING - { - ma_result result; - - result = ma_event_init(&pFence->e); - if (result != MA_SUCCESS) { - return result; - } - } - #endif - - return MA_SUCCESS; -} - -MA_API void ma_fence_uninit(ma_fence* pFence) -{ - if (pFence == NULL) { - return; - } - - #ifndef MA_NO_THREADING - { - ma_event_uninit(&pFence->e); - } - #endif - - MA_ZERO_OBJECT(pFence); -} - -MA_API ma_result ma_fence_acquire(ma_fence* pFence) -{ - if (pFence == NULL) { - return MA_INVALID_ARGS; - } - - for (;;) { - ma_uint32 oldCounter = ma_atomic_load_32(&pFence->counter); - ma_uint32 newCounter = oldCounter + 1; - - /* Make sure we're not about to exceed our maximum value. */ - if (newCounter > MA_FENCE_COUNTER_MAX) { - MA_ASSERT(MA_FALSE); - return MA_OUT_OF_RANGE; - } - - if (ma_atomic_compare_exchange_weak_32(&pFence->counter, &oldCounter, newCounter)) { - return MA_SUCCESS; - } else { - if (oldCounter == MA_FENCE_COUNTER_MAX) { - MA_ASSERT(MA_FALSE); - return MA_OUT_OF_RANGE; /* The other thread took the last available slot. Abort. */ - } - } - } - - /* Should never get here. */ - /*return MA_SUCCESS;*/ -} - -MA_API ma_result ma_fence_release(ma_fence* pFence) -{ - if (pFence == NULL) { - return MA_INVALID_ARGS; - } - - for (;;) { - ma_uint32 oldCounter = ma_atomic_load_32(&pFence->counter); - ma_uint32 newCounter = oldCounter - 1; - - if (oldCounter == 0) { - MA_ASSERT(MA_FALSE); - return MA_INVALID_OPERATION; /* Acquire/release mismatch. */ - } - - if (ma_atomic_compare_exchange_weak_32(&pFence->counter, &oldCounter, newCounter)) { - #ifndef MA_NO_THREADING - { - if (newCounter == 0) { - ma_event_signal(&pFence->e); /* <-- ma_fence_wait() will be waiting on this. */ - } - } - #endif - - return MA_SUCCESS; - } else { - if (oldCounter == 0) { - MA_ASSERT(MA_FALSE); - return MA_INVALID_OPERATION; /* Another thread has taken the 0 slot. Acquire/release mismatch. */ - } - } - } - - /* Should never get here. */ - /*return MA_SUCCESS;*/ -} - -MA_API ma_result ma_fence_wait(ma_fence* pFence) -{ - if (pFence == NULL) { - return MA_INVALID_ARGS; - } - - for (;;) { - ma_uint32 counter; - - counter = ma_atomic_load_32(&pFence->counter); - if (counter == 0) { - /* - Counter has hit zero. By the time we get here some other thread may have acquired the - fence again, but that is where the caller needs to take care with how they se the fence. - */ - return MA_SUCCESS; - } - - /* Getting here means the counter is > 0. We'll need to wait for something to happen. */ - #ifndef MA_NO_THREADING - { - ma_result result; - - result = ma_event_wait(&pFence->e); - if (result != MA_SUCCESS) { - return result; - } - } - #endif - } - - /* Should never get here. */ - /*return MA_INVALID_OPERATION;*/ -} - - -MA_API ma_result ma_async_notification_signal(ma_async_notification* pNotification) -{ - ma_async_notification_callbacks* pNotificationCallbacks = (ma_async_notification_callbacks*)pNotification; - - if (pNotification == NULL) { - return MA_INVALID_ARGS; - } - - if (pNotificationCallbacks->onSignal == NULL) { - return MA_NOT_IMPLEMENTED; - } - - pNotificationCallbacks->onSignal(pNotification); - return MA_INVALID_ARGS; -} - - -static void ma_async_notification_poll__on_signal(ma_async_notification* pNotification) -{ - ((ma_async_notification_poll*)pNotification)->signalled = MA_TRUE; -} - -MA_API ma_result ma_async_notification_poll_init(ma_async_notification_poll* pNotificationPoll) -{ - if (pNotificationPoll == NULL) { - return MA_INVALID_ARGS; - } - - pNotificationPoll->cb.onSignal = ma_async_notification_poll__on_signal; - pNotificationPoll->signalled = MA_FALSE; - - return MA_SUCCESS; -} - -MA_API ma_bool32 ma_async_notification_poll_is_signalled(const ma_async_notification_poll* pNotificationPoll) -{ - if (pNotificationPoll == NULL) { - return MA_FALSE; - } - - return pNotificationPoll->signalled; -} - - -static void ma_async_notification_event__on_signal(ma_async_notification* pNotification) -{ - ma_async_notification_event_signal((ma_async_notification_event*)pNotification); -} - -MA_API ma_result ma_async_notification_event_init(ma_async_notification_event* pNotificationEvent) -{ - if (pNotificationEvent == NULL) { - return MA_INVALID_ARGS; - } - - pNotificationEvent->cb.onSignal = ma_async_notification_event__on_signal; - - #ifndef MA_NO_THREADING - { - ma_result result; - - result = ma_event_init(&pNotificationEvent->e); - if (result != MA_SUCCESS) { - return result; - } - - return MA_SUCCESS; - } - #else - { - return MA_NOT_IMPLEMENTED; /* Threading is disabled. */ - } - #endif -} - -MA_API ma_result ma_async_notification_event_uninit(ma_async_notification_event* pNotificationEvent) -{ - if (pNotificationEvent == NULL) { - return MA_INVALID_ARGS; - } - - #ifndef MA_NO_THREADING - { - ma_event_uninit(&pNotificationEvent->e); - return MA_SUCCESS; - } - #else - { - return MA_NOT_IMPLEMENTED; /* Threading is disabled. */ - } - #endif -} - -MA_API ma_result ma_async_notification_event_wait(ma_async_notification_event* pNotificationEvent) -{ - if (pNotificationEvent == NULL) { - return MA_INVALID_ARGS; - } - - #ifndef MA_NO_THREADING - { - return ma_event_wait(&pNotificationEvent->e); - } - #else - { - return MA_NOT_IMPLEMENTED; /* Threading is disabled. */ - } - #endif -} - -MA_API ma_result ma_async_notification_event_signal(ma_async_notification_event* pNotificationEvent) -{ - if (pNotificationEvent == NULL) { - return MA_INVALID_ARGS; - } - - #ifndef MA_NO_THREADING - { - return ma_event_signal(&pNotificationEvent->e); - } - #else - { - return MA_NOT_IMPLEMENTED; /* Threading is disabled. */ - } - #endif -} - - - -/************************************************************************************************************************************************************ - -Job Queue - -************************************************************************************************************************************************************/ -MA_API ma_slot_allocator_config ma_slot_allocator_config_init(ma_uint32 capacity) -{ - ma_slot_allocator_config config; - - MA_ZERO_OBJECT(&config); - config.capacity = capacity; - - return config; -} - - -static MA_INLINE ma_uint32 ma_slot_allocator_calculate_group_capacity(ma_uint32 slotCapacity) -{ - ma_uint32 cap = slotCapacity / 32; - if ((slotCapacity % 32) != 0) { - cap += 1; - } - - return cap; -} - -static MA_INLINE ma_uint32 ma_slot_allocator_group_capacity(const ma_slot_allocator* pAllocator) -{ - return ma_slot_allocator_calculate_group_capacity(pAllocator->capacity); -} - - -typedef struct -{ - size_t sizeInBytes; - size_t groupsOffset; - size_t slotsOffset; -} ma_slot_allocator_heap_layout; - -static ma_result ma_slot_allocator_get_heap_layout(const ma_slot_allocator_config* pConfig, ma_slot_allocator_heap_layout* pHeapLayout) -{ - MA_ASSERT(pHeapLayout != NULL); - - MA_ZERO_OBJECT(pHeapLayout); - - if (pConfig == NULL) { - return MA_INVALID_ARGS; - } - - if (pConfig->capacity == 0) { - return MA_INVALID_ARGS; - } - - pHeapLayout->sizeInBytes = 0; - - /* Groups. */ - pHeapLayout->groupsOffset = pHeapLayout->sizeInBytes; - pHeapLayout->sizeInBytes += ma_align_64(ma_slot_allocator_calculate_group_capacity(pConfig->capacity) * sizeof(ma_slot_allocator_group)); - - /* Slots. */ - pHeapLayout->slotsOffset = pHeapLayout->sizeInBytes; - pHeapLayout->sizeInBytes += ma_align_64(pConfig->capacity * sizeof(ma_uint32)); - - return MA_SUCCESS; -} - -MA_API ma_result ma_slot_allocator_get_heap_size(const ma_slot_allocator_config* pConfig, size_t* pHeapSizeInBytes) -{ - ma_result result; - ma_slot_allocator_heap_layout layout; - - if (pHeapSizeInBytes == NULL) { - return MA_INVALID_ARGS; - } - - *pHeapSizeInBytes = 0; - - result = ma_slot_allocator_get_heap_layout(pConfig, &layout); - if (result != MA_SUCCESS) { - return result; - } - - *pHeapSizeInBytes = layout.sizeInBytes; - - return result; -} - -MA_API ma_result ma_slot_allocator_init_preallocated(const ma_slot_allocator_config* pConfig, void* pHeap, ma_slot_allocator* pAllocator) -{ - ma_result result; - ma_slot_allocator_heap_layout heapLayout; - - if (pAllocator == NULL) { - return MA_INVALID_ARGS; - } - - MA_ZERO_OBJECT(pAllocator); - - if (pHeap == NULL) { - return MA_INVALID_ARGS; - } - - result = ma_slot_allocator_get_heap_layout(pConfig, &heapLayout); - if (result != MA_SUCCESS) { - return result; - } - - pAllocator->_pHeap = pHeap; - MA_ZERO_MEMORY(pHeap, heapLayout.sizeInBytes); - - pAllocator->pGroups = (ma_slot_allocator_group*)ma_offset_ptr(pHeap, heapLayout.groupsOffset); - pAllocator->pSlots = (ma_uint32*)ma_offset_ptr(pHeap, heapLayout.slotsOffset); - pAllocator->capacity = pConfig->capacity; - - return MA_SUCCESS; -} - -MA_API ma_result ma_slot_allocator_init(const ma_slot_allocator_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_slot_allocator* pAllocator) -{ - ma_result result; - size_t heapSizeInBytes; - void* pHeap; - - result = ma_slot_allocator_get_heap_size(pConfig, &heapSizeInBytes); - if (result != MA_SUCCESS) { - return result; /* Failed to retrieve the size of the heap allocation. */ - } - - if (heapSizeInBytes > 0) { - pHeap = ma_malloc(heapSizeInBytes, pAllocationCallbacks); - if (pHeap == NULL) { - return MA_OUT_OF_MEMORY; - } - } else { - pHeap = NULL; - } - - result = ma_slot_allocator_init_preallocated(pConfig, pHeap, pAllocator); - if (result != MA_SUCCESS) { - ma_free(pHeap, pAllocationCallbacks); - return result; - } - - pAllocator->_ownsHeap = MA_TRUE; - return MA_SUCCESS; -} - -MA_API void ma_slot_allocator_uninit(ma_slot_allocator* pAllocator, const ma_allocation_callbacks* pAllocationCallbacks) -{ - if (pAllocator == NULL) { - return; - } - - if (pAllocator->_ownsHeap) { - ma_free(pAllocator->_pHeap, pAllocationCallbacks); - } -} - -MA_API ma_result ma_slot_allocator_alloc(ma_slot_allocator* pAllocator, ma_uint64* pSlot) -{ - ma_uint32 iAttempt; - const ma_uint32 maxAttempts = 2; /* The number of iterations to perform until returning MA_OUT_OF_MEMORY if no slots can be found. */ - - if (pAllocator == NULL || pSlot == NULL) { - return MA_INVALID_ARGS; - } - - for (iAttempt = 0; iAttempt < maxAttempts; iAttempt += 1) { - /* We need to acquire a suitable bitfield first. This is a bitfield that's got an available slot within it. */ - ma_uint32 iGroup; - for (iGroup = 0; iGroup < ma_slot_allocator_group_capacity(pAllocator); iGroup += 1) { - /* CAS */ - for (;;) { - ma_uint32 oldBitfield; - ma_uint32 newBitfield; - ma_uint32 bitOffset; - - oldBitfield = ma_atomic_load_32(&pAllocator->pGroups[iGroup].bitfield); /* <-- This copy must happen. The compiler must not optimize this away. */ - - /* Fast check to see if anything is available. */ - if (oldBitfield == 0xFFFFFFFF) { - break; /* No available bits in this bitfield. */ - } - - bitOffset = ma_ffs_32(~oldBitfield); - MA_ASSERT(bitOffset < 32); - - newBitfield = oldBitfield | (1 << bitOffset); - - if (ma_atomic_compare_and_swap_32(&pAllocator->pGroups[iGroup].bitfield, oldBitfield, newBitfield) == oldBitfield) { - ma_uint32 slotIndex; - - /* Increment the counter as soon as possible to have other threads report out-of-memory sooner than later. */ - ma_atomic_fetch_add_32(&pAllocator->count, 1); - - /* The slot index is required for constructing the output value. */ - slotIndex = (iGroup << 5) + bitOffset; /* iGroup << 5 = iGroup * 32 */ - if (slotIndex >= pAllocator->capacity) { - return MA_OUT_OF_MEMORY; - } - - /* Increment the reference count before constructing the output value. */ - pAllocator->pSlots[slotIndex] += 1; - - /* Construct the output value. */ - *pSlot = (((ma_uint64)pAllocator->pSlots[slotIndex] << 32) | slotIndex); - - return MA_SUCCESS; - } - } - } - - /* We weren't able to find a slot. If it's because we've reached our capacity we need to return MA_OUT_OF_MEMORY. Otherwise we need to do another iteration and try again. */ - if (pAllocator->count < pAllocator->capacity) { - ma_yield(); - } else { - return MA_OUT_OF_MEMORY; - } - } - - /* We couldn't find a slot within the maximum number of attempts. */ - return MA_OUT_OF_MEMORY; -} - -MA_API ma_result ma_slot_allocator_free(ma_slot_allocator* pAllocator, ma_uint64 slot) -{ - ma_uint32 iGroup; - ma_uint32 iBit; - - if (pAllocator == NULL) { - return MA_INVALID_ARGS; - } - - iGroup = (ma_uint32)((slot & 0xFFFFFFFF) >> 5); /* slot / 32 */ - iBit = (ma_uint32)((slot & 0xFFFFFFFF) & 31); /* slot % 32 */ - - if (iGroup >= ma_slot_allocator_group_capacity(pAllocator)) { - return MA_INVALID_ARGS; - } - - MA_ASSERT(iBit < 32); /* This must be true due to the logic we used to actually calculate it. */ - - while (ma_atomic_load_32(&pAllocator->count) > 0) { - /* CAS */ - ma_uint32 oldBitfield; - ma_uint32 newBitfield; - - oldBitfield = ma_atomic_load_32(&pAllocator->pGroups[iGroup].bitfield); /* <-- This copy must happen. The compiler must not optimize this away. */ - newBitfield = oldBitfield & ~(1 << iBit); - - /* Debugging for checking for double-frees. */ - #if defined(MA_DEBUG_OUTPUT) - { - if ((oldBitfield & (1 << iBit)) == 0) { - MA_ASSERT(MA_FALSE); /* Double free detected.*/ - } - } - #endif - - if (ma_atomic_compare_and_swap_32(&pAllocator->pGroups[iGroup].bitfield, oldBitfield, newBitfield) == oldBitfield) { - ma_atomic_fetch_sub_32(&pAllocator->count, 1); - return MA_SUCCESS; - } - } - - /* Getting here means there are no allocations available for freeing. */ - return MA_INVALID_OPERATION; -} - - -#define MA_JOB_ID_NONE ~((ma_uint64)0) -#define MA_JOB_SLOT_NONE (ma_uint16)(~0) - -static MA_INLINE ma_uint32 ma_job_extract_refcount(ma_uint64 toc) -{ - return (ma_uint32)(toc >> 32); -} - -static MA_INLINE ma_uint16 ma_job_extract_slot(ma_uint64 toc) -{ - return (ma_uint16)(toc & 0x0000FFFF); -} - -static MA_INLINE ma_uint16 ma_job_extract_code(ma_uint64 toc) -{ - return (ma_uint16)((toc & 0xFFFF0000) >> 16); -} - -static MA_INLINE ma_uint64 ma_job_toc_to_allocation(ma_uint64 toc) -{ - return ((ma_uint64)ma_job_extract_refcount(toc) << 32) | (ma_uint64)ma_job_extract_slot(toc); -} - -static MA_INLINE ma_uint64 ma_job_set_refcount(ma_uint64 toc, ma_uint32 refcount) -{ - /* Clear the reference count first. */ - toc = toc & ~((ma_uint64)0xFFFFFFFF << 32); - toc = toc | ((ma_uint64)refcount << 32); - - return toc; -} - - -MA_API ma_job ma_job_init(ma_uint16 code) -{ - ma_job job; - - MA_ZERO_OBJECT(&job); - job.toc.breakup.code = code; - job.toc.breakup.slot = MA_JOB_SLOT_NONE; /* Temp value. Will be allocated when posted to a queue. */ - job.next = MA_JOB_ID_NONE; - - return job; -} - - -static ma_result ma_job_process__noop(ma_job* pJob); -static ma_result ma_job_process__quit(ma_job* pJob); -static ma_result ma_job_process__custom(ma_job* pJob); -static ma_result ma_job_process__resource_manager__load_data_buffer_node(ma_job* pJob); -static ma_result ma_job_process__resource_manager__free_data_buffer_node(ma_job* pJob); -static ma_result ma_job_process__resource_manager__page_data_buffer_node(ma_job* pJob); -static ma_result ma_job_process__resource_manager__load_data_buffer(ma_job* pJob); -static ma_result ma_job_process__resource_manager__free_data_buffer(ma_job* pJob); -static ma_result ma_job_process__resource_manager__load_data_stream(ma_job* pJob); -static ma_result ma_job_process__resource_manager__free_data_stream(ma_job* pJob); -static ma_result ma_job_process__resource_manager__page_data_stream(ma_job* pJob); -static ma_result ma_job_process__resource_manager__seek_data_stream(ma_job* pJob); - -#if !defined(MA_NO_DEVICE_IO) -static ma_result ma_job_process__device__aaudio_reroute(ma_job* pJob); -#endif - -static ma_job_proc g_jobVTable[MA_JOB_TYPE_COUNT] = -{ - /* Miscellaneous. */ - ma_job_process__quit, /* MA_JOB_TYPE_QUIT */ - ma_job_process__custom, /* MA_JOB_TYPE_CUSTOM */ - - /* Resource Manager. */ - ma_job_process__resource_manager__load_data_buffer_node, /* MA_JOB_TYPE_RESOURCE_MANAGER_LOAD_DATA_BUFFER_NODE */ - ma_job_process__resource_manager__free_data_buffer_node, /* MA_JOB_TYPE_RESOURCE_MANAGER_FREE_DATA_BUFFER_NODE */ - ma_job_process__resource_manager__page_data_buffer_node, /* MA_JOB_TYPE_RESOURCE_MANAGER_PAGE_DATA_BUFFER_NODE */ - ma_job_process__resource_manager__load_data_buffer, /* MA_JOB_TYPE_RESOURCE_MANAGER_LOAD_DATA_BUFFER */ - ma_job_process__resource_manager__free_data_buffer, /* MA_JOB_TYPE_RESOURCE_MANAGER_FREE_DATA_BUFFER */ - ma_job_process__resource_manager__load_data_stream, /* MA_JOB_TYPE_RESOURCE_MANAGER_LOAD_DATA_STREAM */ - ma_job_process__resource_manager__free_data_stream, /* MA_JOB_TYPE_RESOURCE_MANAGER_FREE_DATA_STREAM */ - ma_job_process__resource_manager__page_data_stream, /* MA_JOB_TYPE_RESOURCE_MANAGER_PAGE_DATA_STREAM */ - ma_job_process__resource_manager__seek_data_stream, /* MA_JOB_TYPE_RESOURCE_MANAGER_SEEK_DATA_STREAM */ - - /* Device. */ -#if !defined(MA_NO_DEVICE_IO) - ma_job_process__device__aaudio_reroute /* MA_JOB_TYPE_DEVICE_AAUDIO_REROUTE */ -#endif -}; - -MA_API ma_result ma_job_process(ma_job* pJob) -{ - if (pJob == NULL) { - return MA_INVALID_ARGS; - } - - if (pJob->toc.breakup.code >= MA_JOB_TYPE_COUNT) { - return MA_INVALID_OPERATION; - } - - return g_jobVTable[pJob->toc.breakup.code](pJob); -} - -static ma_result ma_job_process__noop(ma_job* pJob) -{ - MA_ASSERT(pJob != NULL); - - /* No-op. */ - (void)pJob; - - return MA_SUCCESS; -} - -static ma_result ma_job_process__quit(ma_job* pJob) -{ - return ma_job_process__noop(pJob); -} - -static ma_result ma_job_process__custom(ma_job* pJob) -{ - MA_ASSERT(pJob != NULL); - - /* No-op if there's no callback. */ - if (pJob->data.custom.proc == NULL) { - return MA_SUCCESS; - } - - return pJob->data.custom.proc(pJob); -} - - - -MA_API ma_job_queue_config ma_job_queue_config_init(ma_uint32 flags, ma_uint32 capacity) -{ - ma_job_queue_config config; - - config.flags = flags; - config.capacity = capacity; - - return config; -} - - -typedef struct -{ - size_t sizeInBytes; - size_t allocatorOffset; - size_t jobsOffset; -} ma_job_queue_heap_layout; - -static ma_result ma_job_queue_get_heap_layout(const ma_job_queue_config* pConfig, ma_job_queue_heap_layout* pHeapLayout) -{ - ma_result result; - - MA_ASSERT(pHeapLayout != NULL); - - MA_ZERO_OBJECT(pHeapLayout); - - if (pConfig == NULL) { - return MA_INVALID_ARGS; - } - - if (pConfig->capacity == 0) { - return MA_INVALID_ARGS; - } - - pHeapLayout->sizeInBytes = 0; - - /* Allocator. */ - { - ma_slot_allocator_config allocatorConfig; - size_t allocatorHeapSizeInBytes; - - allocatorConfig = ma_slot_allocator_config_init(pConfig->capacity); - result = ma_slot_allocator_get_heap_size(&allocatorConfig, &allocatorHeapSizeInBytes); - if (result != MA_SUCCESS) { - return result; - } - - pHeapLayout->allocatorOffset = pHeapLayout->sizeInBytes; - pHeapLayout->sizeInBytes += allocatorHeapSizeInBytes; - } - - /* Jobs. */ - pHeapLayout->jobsOffset = pHeapLayout->sizeInBytes; - pHeapLayout->sizeInBytes += ma_align_64(pConfig->capacity * sizeof(ma_job)); - - return MA_SUCCESS; -} - -MA_API ma_result ma_job_queue_get_heap_size(const ma_job_queue_config* pConfig, size_t* pHeapSizeInBytes) -{ - ma_result result; - ma_job_queue_heap_layout layout; - - if (pHeapSizeInBytes == NULL) { - return MA_INVALID_ARGS; - } - - *pHeapSizeInBytes = 0; - - result = ma_job_queue_get_heap_layout(pConfig, &layout); - if (result != MA_SUCCESS) { - return result; - } - - *pHeapSizeInBytes = layout.sizeInBytes; - - return MA_SUCCESS; -} - -MA_API ma_result ma_job_queue_init_preallocated(const ma_job_queue_config* pConfig, void* pHeap, ma_job_queue* pQueue) -{ - ma_result result; - ma_job_queue_heap_layout heapLayout; - ma_slot_allocator_config allocatorConfig; - - if (pQueue == NULL) { - return MA_INVALID_ARGS; - } - - MA_ZERO_OBJECT(pQueue); - - result = ma_job_queue_get_heap_layout(pConfig, &heapLayout); - if (result != MA_SUCCESS) { - return result; - } - - pQueue->_pHeap = pHeap; - MA_ZERO_MEMORY(pHeap, heapLayout.sizeInBytes); - - pQueue->flags = pConfig->flags; - pQueue->capacity = pConfig->capacity; - pQueue->pJobs = (ma_job*)ma_offset_ptr(pHeap, heapLayout.jobsOffset); - - allocatorConfig = ma_slot_allocator_config_init(pConfig->capacity); - result = ma_slot_allocator_init_preallocated(&allocatorConfig, ma_offset_ptr(pHeap, heapLayout.allocatorOffset), &pQueue->allocator); - if (result != MA_SUCCESS) { - return result; - } - - /* We need a semaphore if we're running in non-blocking mode. If threading is disabled we need to return an error. */ - if ((pQueue->flags & MA_JOB_QUEUE_FLAG_NON_BLOCKING) == 0) { - #ifndef MA_NO_THREADING - { - ma_semaphore_init(0, &pQueue->sem); - } - #else - { - /* Threading is disabled and we've requested non-blocking mode. */ - return MA_INVALID_OPERATION; - } - #endif - } - - /* - Our queue needs to be initialized with a free standing node. This should always be slot 0. Required for the lock free algorithm. The first job in the queue is - just a dummy item for giving us the first item in the list which is stored in the "next" member. - */ - ma_slot_allocator_alloc(&pQueue->allocator, &pQueue->head); /* Will never fail. */ - pQueue->pJobs[ma_job_extract_slot(pQueue->head)].next = MA_JOB_ID_NONE; - pQueue->tail = pQueue->head; - - return MA_SUCCESS; -} - -MA_API ma_result ma_job_queue_init(const ma_job_queue_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_job_queue* pQueue) -{ - ma_result result; - size_t heapSizeInBytes; - void* pHeap; - - result = ma_job_queue_get_heap_size(pConfig, &heapSizeInBytes); - if (result != MA_SUCCESS) { - return result; - } - - if (heapSizeInBytes > 0) { - pHeap = ma_malloc(heapSizeInBytes, pAllocationCallbacks); - if (pHeap == NULL) { - return MA_OUT_OF_MEMORY; - } - } else { - pHeap = NULL; - } - - result = ma_job_queue_init_preallocated(pConfig, pHeap, pQueue); - if (result != MA_SUCCESS) { - ma_free(pHeap, pAllocationCallbacks); - return result; - } - - pQueue->_ownsHeap = MA_TRUE; - return MA_SUCCESS; -} - -MA_API void ma_job_queue_uninit(ma_job_queue* pQueue, const ma_allocation_callbacks* pAllocationCallbacks) -{ - if (pQueue == NULL) { - return; - } - - /* All we need to do is uninitialize the semaphore. */ - if ((pQueue->flags & MA_JOB_QUEUE_FLAG_NON_BLOCKING) == 0) { - #ifndef MA_NO_THREADING - { - ma_semaphore_uninit(&pQueue->sem); - } - #else - { - MA_ASSERT(MA_FALSE); /* Should never get here. Should have been checked at initialization time. */ - } - #endif - } - - ma_slot_allocator_uninit(&pQueue->allocator, pAllocationCallbacks); - - if (pQueue->_ownsHeap) { - ma_free(pQueue->_pHeap, pAllocationCallbacks); - } -} - -static ma_bool32 ma_job_queue_cas(volatile ma_uint64* dst, ma_uint64 expected, ma_uint64 desired) -{ - /* The new counter is taken from the expected value. */ - return ma_atomic_compare_and_swap_64(dst, expected, ma_job_set_refcount(desired, ma_job_extract_refcount(expected) + 1)) == expected; -} - -MA_API ma_result ma_job_queue_post(ma_job_queue* pQueue, const ma_job* pJob) -{ - /* - Lock free queue implementation based on the paper by Michael and Scott: Nonblocking Algorithms and Preemption-Safe Locking on Multiprogrammed Shared Memory Multiprocessors - */ - ma_result result; - ma_uint64 slot; - ma_uint64 tail; - ma_uint64 next; - - if (pQueue == NULL || pJob == NULL) { - return MA_INVALID_ARGS; - } - - /* We need a new slot. */ - result = ma_slot_allocator_alloc(&pQueue->allocator, &slot); - if (result != MA_SUCCESS) { - return result; /* Probably ran out of slots. If so, MA_OUT_OF_MEMORY will be returned. */ - } - - /* At this point we should have a slot to place the job. */ - MA_ASSERT(ma_job_extract_slot(slot) < pQueue->capacity); - - /* We need to put the job into memory before we do anything. */ - pQueue->pJobs[ma_job_extract_slot(slot)] = *pJob; - pQueue->pJobs[ma_job_extract_slot(slot)].toc.allocation = slot; /* This will overwrite the job code. */ - pQueue->pJobs[ma_job_extract_slot(slot)].toc.breakup.code = pJob->toc.breakup.code; /* The job code needs to be applied again because the line above overwrote it. */ - pQueue->pJobs[ma_job_extract_slot(slot)].next = MA_JOB_ID_NONE; /* Reset for safety. */ - - #ifndef MA_USE_EXPERIMENTAL_LOCK_FREE_JOB_QUEUE - ma_spinlock_lock(&pQueue->lock); - #endif - { - /* The job is stored in memory so now we need to add it to our linked list. We only ever add items to the end of the list. */ - for (;;) { - tail = ma_atomic_load_64(&pQueue->tail); - next = ma_atomic_load_64(&pQueue->pJobs[ma_job_extract_slot(tail)].next); - - if (ma_job_toc_to_allocation(tail) == ma_job_toc_to_allocation(ma_atomic_load_64(&pQueue->tail))) { - if (ma_job_extract_slot(next) == 0xFFFF) { - if (ma_job_queue_cas(&pQueue->pJobs[ma_job_extract_slot(tail)].next, next, slot)) { - break; - } - } else { - ma_job_queue_cas(&pQueue->tail, tail, ma_job_extract_slot(next)); - } - } - } - ma_job_queue_cas(&pQueue->tail, tail, slot); - } - #ifndef MA_USE_EXPERIMENTAL_LOCK_FREE_JOB_QUEUE - ma_spinlock_unlock(&pQueue->lock); - #endif - - - /* Signal the semaphore as the last step if we're using synchronous mode. */ - if ((pQueue->flags & MA_JOB_QUEUE_FLAG_NON_BLOCKING) == 0) { - #ifndef MA_NO_THREADING - { - ma_semaphore_release(&pQueue->sem); - } - #else - { - MA_ASSERT(MA_FALSE); /* Should never get here. Should have been checked at initialization time. */ - } - #endif - } - - return MA_SUCCESS; -} - -MA_API ma_result ma_job_queue_next(ma_job_queue* pQueue, ma_job* pJob) -{ - ma_uint64 head; - ma_uint64 tail; - ma_uint64 next; - - if (pQueue == NULL || pJob == NULL) { - return MA_INVALID_ARGS; - } - - /* If we're running in synchronous mode we'll need to wait on a semaphore. */ - if ((pQueue->flags & MA_JOB_QUEUE_FLAG_NON_BLOCKING) == 0) { - #ifndef MA_NO_THREADING - { - ma_semaphore_wait(&pQueue->sem); - } - #else - { - MA_ASSERT(MA_FALSE); /* Should never get here. Should have been checked at initialization time. */ - } - #endif - } - - #ifndef MA_USE_EXPERIMENTAL_LOCK_FREE_JOB_QUEUE - ma_spinlock_lock(&pQueue->lock); - #endif - { - /* - BUG: In lock-free mode, multiple threads can be in this section of code. The "head" variable in the loop below - is stored. One thread can fall through to the freeing of this item while another is still using "head" for the - retrieval of the "next" variable. - - The slot allocator might need to make use of some reference counting to ensure it's only truly freed when - there are no more references to the item. This must be fixed before removing these locks. - */ - - /* Now we need to remove the root item from the list. */ - for (;;) { - head = ma_atomic_load_64(&pQueue->head); - tail = ma_atomic_load_64(&pQueue->tail); - next = ma_atomic_load_64(&pQueue->pJobs[ma_job_extract_slot(head)].next); - - if (ma_job_toc_to_allocation(head) == ma_job_toc_to_allocation(ma_atomic_load_64(&pQueue->head))) { - if (ma_job_extract_slot(head) == ma_job_extract_slot(tail)) { - if (ma_job_extract_slot(next) == 0xFFFF) { - #ifndef MA_USE_EXPERIMENTAL_LOCK_FREE_JOB_QUEUE - ma_spinlock_unlock(&pQueue->lock); - #endif - return MA_NO_DATA_AVAILABLE; - } - ma_job_queue_cas(&pQueue->tail, tail, ma_job_extract_slot(next)); - } else { - *pJob = pQueue->pJobs[ma_job_extract_slot(next)]; - if (ma_job_queue_cas(&pQueue->head, head, ma_job_extract_slot(next))) { - break; - } - } - } - } - } - #ifndef MA_USE_EXPERIMENTAL_LOCK_FREE_JOB_QUEUE - ma_spinlock_unlock(&pQueue->lock); - #endif - - ma_slot_allocator_free(&pQueue->allocator, head); - - /* - If it's a quit job make sure it's put back on the queue to ensure other threads have an opportunity to detect it and terminate naturally. We - could instead just leave it on the queue, but that would involve fiddling with the lock-free code above and I want to keep that as simple as - possible. - */ - if (pJob->toc.breakup.code == MA_JOB_TYPE_QUIT) { - ma_job_queue_post(pQueue, pJob); - return MA_CANCELLED; /* Return a cancelled status just in case the thread is checking return codes and not properly checking for a quit job. */ - } - - return MA_SUCCESS; -} - - - -/******************************************************************************* - -Dynamic Linking - -*******************************************************************************/ -#ifdef MA_POSIX - /* No need for dlfcn.h if we're not using runtime linking. */ - #ifndef MA_NO_RUNTIME_LINKING - #include - #endif -#endif - -MA_API ma_handle ma_dlopen(ma_log* pLog, const char* filename) -{ -#ifndef MA_NO_RUNTIME_LINKING - ma_handle handle; - - ma_log_postf(pLog, MA_LOG_LEVEL_DEBUG, "Loading library: %s\n", filename); - - #ifdef MA_WIN32 - /* From MSDN: Desktop applications cannot use LoadPackagedLibrary; if a desktop application calls this function it fails with APPMODEL_ERROR_NO_PACKAGE.*/ - #if !defined(MA_WIN32_UWP) || !(defined(WINAPI_FAMILY) && ((defined(WINAPI_FAMILY_PHONE_APP) && WINAPI_FAMILY == WINAPI_FAMILY_PHONE_APP))) - handle = (ma_handle)LoadLibraryA(filename); - #else - /* *sigh* It appears there is no ANSI version of LoadPackagedLibrary()... */ - WCHAR filenameW[4096]; - if (MultiByteToWideChar(CP_UTF8, 0, filename, -1, filenameW, sizeof(filenameW)) == 0) { - handle = NULL; - } else { - handle = (ma_handle)LoadPackagedLibrary(filenameW, 0); - } - #endif - #else - handle = (ma_handle)dlopen(filename, RTLD_NOW); - #endif - - /* - I'm not considering failure to load a library an error nor a warning because seamlessly falling through to a lower-priority - backend is a deliberate design choice. Instead I'm logging it as an informational message. - */ - if (handle == NULL) { - ma_log_postf(pLog, MA_LOG_LEVEL_INFO, "Failed to load library: %s\n", filename); - } - - return handle; -#else - /* Runtime linking is disabled. */ - (void)pLog; - (void)filename; - return NULL; -#endif -} - -MA_API void ma_dlclose(ma_log* pLog, ma_handle handle) -{ -#ifndef MA_NO_RUNTIME_LINKING - #ifdef MA_WIN32 - FreeLibrary((HMODULE)handle); - #else - /* Hack for Android bug (see https://github.com/android/ndk/issues/360). Calling dlclose() pre-API 28 may segfault. */ - #if !defined(MA_ANDROID) || (defined(__ANDROID_API__) && __ANDROID_API__ >= 28) - { - dlclose((void*)handle); - } - #else - { - (void)handle; - } - #endif - #endif - - (void)pLog; -#else - /* Runtime linking is disabled. */ - (void)pLog; - (void)handle; -#endif -} - -MA_API ma_proc ma_dlsym(ma_log* pLog, ma_handle handle, const char* symbol) -{ -#ifndef MA_NO_RUNTIME_LINKING - ma_proc proc; - - ma_log_postf(pLog, MA_LOG_LEVEL_DEBUG, "Loading symbol: %s\n", symbol); - -#ifdef _WIN32 - proc = (ma_proc)GetProcAddress((HMODULE)handle, symbol); -#else -#if (defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 8))) || defined(__clang__) - #pragma GCC diagnostic push - #pragma GCC diagnostic ignored "-Wpedantic" -#endif - proc = (ma_proc)dlsym((void*)handle, symbol); -#if (defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 8))) || defined(__clang__) - #pragma GCC diagnostic pop -#endif -#endif - - if (proc == NULL) { - ma_log_postf(pLog, MA_LOG_LEVEL_WARNING, "Failed to load symbol: %s\n", symbol); - } - - (void)pLog; /* It's possible for pContext to be unused. */ - return proc; -#else - /* Runtime linking is disabled. */ - (void)pLog; - (void)handle; - (void)symbol; - return NULL; -#endif -} - - - -/************************************************************************************************************************************************************ -************************************************************************************************************************************************************* - -DEVICE I/O -========== - -************************************************************************************************************************************************************* -************************************************************************************************************************************************************/ - -/* Disable run-time linking on certain backends and platforms. */ -#ifndef MA_NO_RUNTIME_LINKING - #if defined(MA_EMSCRIPTEN) || defined(MA_ORBIS) || defined(MA_PROSPERO) - #define MA_NO_RUNTIME_LINKING - #endif -#endif - -#ifdef MA_APPLE - #include -#endif - -#ifndef MA_NO_DEVICE_IO - -#if defined(MA_APPLE) && (MAC_OS_X_VERSION_MIN_REQUIRED < 101200) - #include /* For mach_absolute_time() */ -#endif - -#ifdef MA_POSIX - #include - #include - - /* No need for dlfcn.h if we're not using runtime linking. */ - #ifndef MA_NO_RUNTIME_LINKING - #include - #endif -#endif - -/* This must be set to at least 26. */ -#ifndef MA_AAUDIO_MIN_ANDROID_SDK_VERSION -#define MA_AAUDIO_MIN_ANDROID_SDK_VERSION 27 -#endif - - -MA_API void ma_device_info_add_native_data_format(ma_device_info* pDeviceInfo, ma_format format, ma_uint32 channels, ma_uint32 sampleRate, ma_uint32 flags) -{ - if (pDeviceInfo == NULL) { - return; - } - - if (pDeviceInfo->nativeDataFormatCount < ma_countof(pDeviceInfo->nativeDataFormats)) { - pDeviceInfo->nativeDataFormats[pDeviceInfo->nativeDataFormatCount].format = format; - pDeviceInfo->nativeDataFormats[pDeviceInfo->nativeDataFormatCount].channels = channels; - pDeviceInfo->nativeDataFormats[pDeviceInfo->nativeDataFormatCount].sampleRate = sampleRate; - pDeviceInfo->nativeDataFormats[pDeviceInfo->nativeDataFormatCount].flags = flags; - pDeviceInfo->nativeDataFormatCount += 1; - } -} - - -typedef struct -{ - ma_backend backend; - const char* pName; -} ma_backend_info; - -static ma_backend_info gBackendInfo[] = /* Indexed by the backend enum. Must be in the order backends are declared in the ma_backend enum. */ -{ - {ma_backend_wasapi, "WASAPI"}, - {ma_backend_dsound, "DirectSound"}, - {ma_backend_winmm, "WinMM"}, - {ma_backend_coreaudio, "Core Audio"}, - {ma_backend_sndio, "sndio"}, - {ma_backend_audio4, "audio(4)"}, - {ma_backend_oss, "OSS"}, - {ma_backend_pulseaudio, "PulseAudio"}, - {ma_backend_alsa, "ALSA"}, - {ma_backend_jack, "JACK"}, - {ma_backend_aaudio, "AAudio"}, - {ma_backend_opensl, "OpenSL|ES"}, - {ma_backend_webaudio, "Web Audio"}, - {ma_backend_custom, "Custom"}, - {ma_backend_null, "Null"} -}; - -MA_API const char* ma_get_backend_name(ma_backend backend) -{ - if (backend < 0 || backend >= (int)ma_countof(gBackendInfo)) { - return "Unknown"; - } - - return gBackendInfo[backend].pName; -} - -MA_API ma_result ma_get_backend_from_name(const char* pBackendName, ma_backend* pBackend) -{ - size_t iBackend; - - if (pBackendName == NULL) { - return MA_INVALID_ARGS; - } - - for (iBackend = 0; iBackend < ma_countof(gBackendInfo); iBackend += 1) { - if (ma_strcmp(pBackendName, gBackendInfo[iBackend].pName) == 0) { - if (pBackend != NULL) { - *pBackend = gBackendInfo[iBackend].backend; - } - - return MA_SUCCESS; - } - } - - /* Getting here means the backend name is unknown. */ - return MA_INVALID_ARGS; -} - -MA_API ma_bool32 ma_is_backend_enabled(ma_backend backend) -{ - /* - This looks a little bit gross, but we want all backends to be included in the switch to avoid warnings on some compilers - about some enums not being handled by the switch statement. - */ - switch (backend) - { - case ma_backend_wasapi: - #if defined(MA_HAS_WASAPI) - return MA_TRUE; - #else - return MA_FALSE; - #endif - case ma_backend_dsound: - #if defined(MA_HAS_DSOUND) - return MA_TRUE; - #else - return MA_FALSE; - #endif - case ma_backend_winmm: - #if defined(MA_HAS_WINMM) - return MA_TRUE; - #else - return MA_FALSE; - #endif - case ma_backend_coreaudio: - #if defined(MA_HAS_COREAUDIO) - return MA_TRUE; - #else - return MA_FALSE; - #endif - case ma_backend_sndio: - #if defined(MA_HAS_SNDIO) - return MA_TRUE; - #else - return MA_FALSE; - #endif - case ma_backend_audio4: - #if defined(MA_HAS_AUDIO4) - return MA_TRUE; - #else - return MA_FALSE; - #endif - case ma_backend_oss: - #if defined(MA_HAS_OSS) - return MA_TRUE; - #else - return MA_FALSE; - #endif - case ma_backend_pulseaudio: - #if defined(MA_HAS_PULSEAUDIO) - return MA_TRUE; - #else - return MA_FALSE; - #endif - case ma_backend_alsa: - #if defined(MA_HAS_ALSA) - return MA_TRUE; - #else - return MA_FALSE; - #endif - case ma_backend_jack: - #if defined(MA_HAS_JACK) - return MA_TRUE; - #else - return MA_FALSE; - #endif - case ma_backend_aaudio: - #if defined(MA_HAS_AAUDIO) - #if defined(MA_ANDROID) - { - return ma_android_sdk_version() >= MA_AAUDIO_MIN_ANDROID_SDK_VERSION; - } - #else - return MA_FALSE; - #endif - #else - return MA_FALSE; - #endif - case ma_backend_opensl: - #if defined(MA_HAS_OPENSL) - #if defined(MA_ANDROID) - { - return ma_android_sdk_version() >= 9; - } - #else - return MA_TRUE; - #endif - #else - return MA_FALSE; - #endif - case ma_backend_webaudio: - #if defined(MA_HAS_WEBAUDIO) - return MA_TRUE; - #else - return MA_FALSE; - #endif - case ma_backend_custom: - #if defined(MA_HAS_CUSTOM) - return MA_TRUE; - #else - return MA_FALSE; - #endif - case ma_backend_null: - #if defined(MA_HAS_NULL) - return MA_TRUE; - #else - return MA_FALSE; - #endif - - default: return MA_FALSE; - } -} - -MA_API ma_result ma_get_enabled_backends(ma_backend* pBackends, size_t backendCap, size_t* pBackendCount) -{ - size_t backendCount; - size_t iBackend; - ma_result result = MA_SUCCESS; - - if (pBackendCount == NULL) { - return MA_INVALID_ARGS; - } - - backendCount = 0; - - for (iBackend = 0; iBackend <= ma_backend_null; iBackend += 1) { - ma_backend backend = (ma_backend)iBackend; - - if (ma_is_backend_enabled(backend)) { - /* The backend is enabled. Try adding it to the list. If there's no room, MA_NO_SPACE needs to be returned. */ - if (backendCount == backendCap) { - result = MA_NO_SPACE; - break; - } else { - pBackends[backendCount] = backend; - backendCount += 1; - } - } - } - - if (pBackendCount != NULL) { - *pBackendCount = backendCount; - } - - return result; -} - -MA_API ma_bool32 ma_is_loopback_supported(ma_backend backend) -{ - switch (backend) - { - case ma_backend_wasapi: return MA_TRUE; - case ma_backend_dsound: return MA_FALSE; - case ma_backend_winmm: return MA_FALSE; - case ma_backend_coreaudio: return MA_FALSE; - case ma_backend_sndio: return MA_FALSE; - case ma_backend_audio4: return MA_FALSE; - case ma_backend_oss: return MA_FALSE; - case ma_backend_pulseaudio: return MA_FALSE; - case ma_backend_alsa: return MA_FALSE; - case ma_backend_jack: return MA_FALSE; - case ma_backend_aaudio: return MA_FALSE; - case ma_backend_opensl: return MA_FALSE; - case ma_backend_webaudio: return MA_FALSE; - case ma_backend_custom: return MA_FALSE; /* <-- Will depend on the implementation of the backend. */ - case ma_backend_null: return MA_FALSE; - default: return MA_FALSE; - } -} - - - -#if defined(MA_WIN32) -/* WASAPI error codes. */ -#define MA_AUDCLNT_E_NOT_INITIALIZED ((HRESULT)0x88890001) -#define MA_AUDCLNT_E_ALREADY_INITIALIZED ((HRESULT)0x88890002) -#define MA_AUDCLNT_E_WRONG_ENDPOINT_TYPE ((HRESULT)0x88890003) -#define MA_AUDCLNT_E_DEVICE_INVALIDATED ((HRESULT)0x88890004) -#define MA_AUDCLNT_E_NOT_STOPPED ((HRESULT)0x88890005) -#define MA_AUDCLNT_E_BUFFER_TOO_LARGE ((HRESULT)0x88890006) -#define MA_AUDCLNT_E_OUT_OF_ORDER ((HRESULT)0x88890007) -#define MA_AUDCLNT_E_UNSUPPORTED_FORMAT ((HRESULT)0x88890008) -#define MA_AUDCLNT_E_INVALID_SIZE ((HRESULT)0x88890009) -#define MA_AUDCLNT_E_DEVICE_IN_USE ((HRESULT)0x8889000A) -#define MA_AUDCLNT_E_BUFFER_OPERATION_PENDING ((HRESULT)0x8889000B) -#define MA_AUDCLNT_E_THREAD_NOT_REGISTERED ((HRESULT)0x8889000C) -#define MA_AUDCLNT_E_NO_SINGLE_PROCESS ((HRESULT)0x8889000D) -#define MA_AUDCLNT_E_EXCLUSIVE_MODE_NOT_ALLOWED ((HRESULT)0x8889000E) -#define MA_AUDCLNT_E_ENDPOINT_CREATE_FAILED ((HRESULT)0x8889000F) -#define MA_AUDCLNT_E_SERVICE_NOT_RUNNING ((HRESULT)0x88890010) -#define MA_AUDCLNT_E_EVENTHANDLE_NOT_EXPECTED ((HRESULT)0x88890011) -#define MA_AUDCLNT_E_EXCLUSIVE_MODE_ONLY ((HRESULT)0x88890012) -#define MA_AUDCLNT_E_BUFDURATION_PERIOD_NOT_EQUAL ((HRESULT)0x88890013) -#define MA_AUDCLNT_E_EVENTHANDLE_NOT_SET ((HRESULT)0x88890014) -#define MA_AUDCLNT_E_INCORRECT_BUFFER_SIZE ((HRESULT)0x88890015) -#define MA_AUDCLNT_E_BUFFER_SIZE_ERROR ((HRESULT)0x88890016) -#define MA_AUDCLNT_E_CPUUSAGE_EXCEEDED ((HRESULT)0x88890017) -#define MA_AUDCLNT_E_BUFFER_ERROR ((HRESULT)0x88890018) -#define MA_AUDCLNT_E_BUFFER_SIZE_NOT_ALIGNED ((HRESULT)0x88890019) -#define MA_AUDCLNT_E_INVALID_DEVICE_PERIOD ((HRESULT)0x88890020) -#define MA_AUDCLNT_E_INVALID_STREAM_FLAG ((HRESULT)0x88890021) -#define MA_AUDCLNT_E_ENDPOINT_OFFLOAD_NOT_CAPABLE ((HRESULT)0x88890022) -#define MA_AUDCLNT_E_OUT_OF_OFFLOAD_RESOURCES ((HRESULT)0x88890023) -#define MA_AUDCLNT_E_OFFLOAD_MODE_ONLY ((HRESULT)0x88890024) -#define MA_AUDCLNT_E_NONOFFLOAD_MODE_ONLY ((HRESULT)0x88890025) -#define MA_AUDCLNT_E_RESOURCES_INVALIDATED ((HRESULT)0x88890026) -#define MA_AUDCLNT_E_RAW_MODE_UNSUPPORTED ((HRESULT)0x88890027) -#define MA_AUDCLNT_E_ENGINE_PERIODICITY_LOCKED ((HRESULT)0x88890028) -#define MA_AUDCLNT_E_ENGINE_FORMAT_LOCKED ((HRESULT)0x88890029) -#define MA_AUDCLNT_E_HEADTRACKING_ENABLED ((HRESULT)0x88890030) -#define MA_AUDCLNT_E_HEADTRACKING_UNSUPPORTED ((HRESULT)0x88890040) -#define MA_AUDCLNT_S_BUFFER_EMPTY ((HRESULT)0x08890001) -#define MA_AUDCLNT_S_THREAD_ALREADY_REGISTERED ((HRESULT)0x08890002) -#define MA_AUDCLNT_S_POSITION_STALLED ((HRESULT)0x08890003) - -#define MA_DS_OK ((HRESULT)0) -#define MA_DS_NO_VIRTUALIZATION ((HRESULT)0x0878000A) -#define MA_DSERR_ALLOCATED ((HRESULT)0x8878000A) -#define MA_DSERR_CONTROLUNAVAIL ((HRESULT)0x8878001E) -#define MA_DSERR_INVALIDPARAM ((HRESULT)0x80070057) /*E_INVALIDARG*/ -#define MA_DSERR_INVALIDCALL ((HRESULT)0x88780032) -#define MA_DSERR_GENERIC ((HRESULT)0x80004005) /*E_FAIL*/ -#define MA_DSERR_PRIOLEVELNEEDED ((HRESULT)0x88780046) -#define MA_DSERR_OUTOFMEMORY ((HRESULT)0x8007000E) /*E_OUTOFMEMORY*/ -#define MA_DSERR_BADFORMAT ((HRESULT)0x88780064) -#define MA_DSERR_UNSUPPORTED ((HRESULT)0x80004001) /*E_NOTIMPL*/ -#define MA_DSERR_NODRIVER ((HRESULT)0x88780078) -#define MA_DSERR_ALREADYINITIALIZED ((HRESULT)0x88780082) -#define MA_DSERR_NOAGGREGATION ((HRESULT)0x80040110) /*CLASS_E_NOAGGREGATION*/ -#define MA_DSERR_BUFFERLOST ((HRESULT)0x88780096) -#define MA_DSERR_OTHERAPPHASPRIO ((HRESULT)0x887800A0) -#define MA_DSERR_UNINITIALIZED ((HRESULT)0x887800AA) -#define MA_DSERR_NOINTERFACE ((HRESULT)0x80004002) /*E_NOINTERFACE*/ -#define MA_DSERR_ACCESSDENIED ((HRESULT)0x80070005) /*E_ACCESSDENIED*/ -#define MA_DSERR_BUFFERTOOSMALL ((HRESULT)0x887800B4) -#define MA_DSERR_DS8_REQUIRED ((HRESULT)0x887800BE) -#define MA_DSERR_SENDLOOP ((HRESULT)0x887800C8) -#define MA_DSERR_BADSENDBUFFERGUID ((HRESULT)0x887800D2) -#define MA_DSERR_OBJECTNOTFOUND ((HRESULT)0x88781161) -#define MA_DSERR_FXUNAVAILABLE ((HRESULT)0x887800DC) - -static ma_result ma_result_from_HRESULT(HRESULT hr) -{ - switch (hr) - { - case NOERROR: return MA_SUCCESS; - /*case S_OK: return MA_SUCCESS;*/ - - case E_POINTER: return MA_INVALID_ARGS; - case E_UNEXPECTED: return MA_ERROR; - case E_NOTIMPL: return MA_NOT_IMPLEMENTED; - case E_OUTOFMEMORY: return MA_OUT_OF_MEMORY; - case E_INVALIDARG: return MA_INVALID_ARGS; - case E_NOINTERFACE: return MA_API_NOT_FOUND; - case E_HANDLE: return MA_INVALID_ARGS; - case E_ABORT: return MA_ERROR; - case E_FAIL: return MA_ERROR; - case E_ACCESSDENIED: return MA_ACCESS_DENIED; - - /* WASAPI */ - case MA_AUDCLNT_E_NOT_INITIALIZED: return MA_DEVICE_NOT_INITIALIZED; - case MA_AUDCLNT_E_ALREADY_INITIALIZED: return MA_DEVICE_ALREADY_INITIALIZED; - case MA_AUDCLNT_E_WRONG_ENDPOINT_TYPE: return MA_INVALID_ARGS; - case MA_AUDCLNT_E_DEVICE_INVALIDATED: return MA_UNAVAILABLE; - case MA_AUDCLNT_E_NOT_STOPPED: return MA_DEVICE_NOT_STOPPED; - case MA_AUDCLNT_E_BUFFER_TOO_LARGE: return MA_TOO_BIG; - case MA_AUDCLNT_E_OUT_OF_ORDER: return MA_INVALID_OPERATION; - case MA_AUDCLNT_E_UNSUPPORTED_FORMAT: return MA_FORMAT_NOT_SUPPORTED; - case MA_AUDCLNT_E_INVALID_SIZE: return MA_INVALID_ARGS; - case MA_AUDCLNT_E_DEVICE_IN_USE: return MA_BUSY; - case MA_AUDCLNT_E_BUFFER_OPERATION_PENDING: return MA_INVALID_OPERATION; - case MA_AUDCLNT_E_THREAD_NOT_REGISTERED: return MA_DOES_NOT_EXIST; - case MA_AUDCLNT_E_NO_SINGLE_PROCESS: return MA_INVALID_OPERATION; - case MA_AUDCLNT_E_EXCLUSIVE_MODE_NOT_ALLOWED: return MA_SHARE_MODE_NOT_SUPPORTED; - case MA_AUDCLNT_E_ENDPOINT_CREATE_FAILED: return MA_FAILED_TO_OPEN_BACKEND_DEVICE; - case MA_AUDCLNT_E_SERVICE_NOT_RUNNING: return MA_NOT_CONNECTED; - case MA_AUDCLNT_E_EVENTHANDLE_NOT_EXPECTED: return MA_INVALID_ARGS; - case MA_AUDCLNT_E_EXCLUSIVE_MODE_ONLY: return MA_SHARE_MODE_NOT_SUPPORTED; - case MA_AUDCLNT_E_BUFDURATION_PERIOD_NOT_EQUAL: return MA_INVALID_ARGS; - case MA_AUDCLNT_E_EVENTHANDLE_NOT_SET: return MA_INVALID_ARGS; - case MA_AUDCLNT_E_INCORRECT_BUFFER_SIZE: return MA_INVALID_ARGS; - case MA_AUDCLNT_E_BUFFER_SIZE_ERROR: return MA_INVALID_ARGS; - case MA_AUDCLNT_E_CPUUSAGE_EXCEEDED: return MA_ERROR; - case MA_AUDCLNT_E_BUFFER_ERROR: return MA_ERROR; - case MA_AUDCLNT_E_BUFFER_SIZE_NOT_ALIGNED: return MA_INVALID_ARGS; - case MA_AUDCLNT_E_INVALID_DEVICE_PERIOD: return MA_INVALID_ARGS; - case MA_AUDCLNT_E_INVALID_STREAM_FLAG: return MA_INVALID_ARGS; - case MA_AUDCLNT_E_ENDPOINT_OFFLOAD_NOT_CAPABLE: return MA_INVALID_OPERATION; - case MA_AUDCLNT_E_OUT_OF_OFFLOAD_RESOURCES: return MA_OUT_OF_MEMORY; - case MA_AUDCLNT_E_OFFLOAD_MODE_ONLY: return MA_INVALID_OPERATION; - case MA_AUDCLNT_E_NONOFFLOAD_MODE_ONLY: return MA_INVALID_OPERATION; - case MA_AUDCLNT_E_RESOURCES_INVALIDATED: return MA_INVALID_DATA; - case MA_AUDCLNT_E_RAW_MODE_UNSUPPORTED: return MA_INVALID_OPERATION; - case MA_AUDCLNT_E_ENGINE_PERIODICITY_LOCKED: return MA_INVALID_OPERATION; - case MA_AUDCLNT_E_ENGINE_FORMAT_LOCKED: return MA_INVALID_OPERATION; - case MA_AUDCLNT_E_HEADTRACKING_ENABLED: return MA_INVALID_OPERATION; - case MA_AUDCLNT_E_HEADTRACKING_UNSUPPORTED: return MA_INVALID_OPERATION; - case MA_AUDCLNT_S_BUFFER_EMPTY: return MA_NO_SPACE; - case MA_AUDCLNT_S_THREAD_ALREADY_REGISTERED: return MA_ALREADY_EXISTS; - case MA_AUDCLNT_S_POSITION_STALLED: return MA_ERROR; - - /* DirectSound */ - /*case MA_DS_OK: return MA_SUCCESS;*/ /* S_OK */ - case MA_DS_NO_VIRTUALIZATION: return MA_SUCCESS; - case MA_DSERR_ALLOCATED: return MA_ALREADY_IN_USE; - case MA_DSERR_CONTROLUNAVAIL: return MA_INVALID_OPERATION; - /*case MA_DSERR_INVALIDPARAM: return MA_INVALID_ARGS;*/ /* E_INVALIDARG */ - case MA_DSERR_INVALIDCALL: return MA_INVALID_OPERATION; - /*case MA_DSERR_GENERIC: return MA_ERROR;*/ /* E_FAIL */ - case MA_DSERR_PRIOLEVELNEEDED: return MA_INVALID_OPERATION; - /*case MA_DSERR_OUTOFMEMORY: return MA_OUT_OF_MEMORY;*/ /* E_OUTOFMEMORY */ - case MA_DSERR_BADFORMAT: return MA_FORMAT_NOT_SUPPORTED; - /*case MA_DSERR_UNSUPPORTED: return MA_NOT_IMPLEMENTED;*/ /* E_NOTIMPL */ - case MA_DSERR_NODRIVER: return MA_FAILED_TO_INIT_BACKEND; - case MA_DSERR_ALREADYINITIALIZED: return MA_DEVICE_ALREADY_INITIALIZED; - case MA_DSERR_NOAGGREGATION: return MA_ERROR; - case MA_DSERR_BUFFERLOST: return MA_UNAVAILABLE; - case MA_DSERR_OTHERAPPHASPRIO: return MA_ACCESS_DENIED; - case MA_DSERR_UNINITIALIZED: return MA_DEVICE_NOT_INITIALIZED; - /*case MA_DSERR_NOINTERFACE: return MA_API_NOT_FOUND;*/ /* E_NOINTERFACE */ - /*case MA_DSERR_ACCESSDENIED: return MA_ACCESS_DENIED;*/ /* E_ACCESSDENIED */ - case MA_DSERR_BUFFERTOOSMALL: return MA_NO_SPACE; - case MA_DSERR_DS8_REQUIRED: return MA_INVALID_OPERATION; - case MA_DSERR_SENDLOOP: return MA_DEADLOCK; - case MA_DSERR_BADSENDBUFFERGUID: return MA_INVALID_ARGS; - case MA_DSERR_OBJECTNOTFOUND: return MA_NO_DEVICE; - case MA_DSERR_FXUNAVAILABLE: return MA_UNAVAILABLE; - - default: return MA_ERROR; - } -} - -/* PROPVARIANT */ -#define MA_VT_LPWSTR 31 -#define MA_VT_BLOB 65 - -#if defined(_MSC_VER) && !defined(__clang__) - #pragma warning(push) - #pragma warning(disable:4201) /* nonstandard extension used: nameless struct/union */ -#elif defined(__clang__) || (defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 8))) - #pragma GCC diagnostic push - #pragma GCC diagnostic ignored "-Wpedantic" /* For ISO C99 doesn't support unnamed structs/unions [-Wpedantic] */ - #if defined(__clang__) - #pragma GCC diagnostic ignored "-Wc11-extensions" /* anonymous unions are a C11 extension */ - #endif -#endif -typedef struct -{ - WORD vt; - WORD wReserved1; - WORD wReserved2; - WORD wReserved3; - union - { - struct - { - ULONG cbSize; - BYTE* pBlobData; - } blob; - WCHAR* pwszVal; - char pad[16]; /* Just to ensure the size of the struct matches the official version. */ - }; -} MA_PROPVARIANT; -#if defined(_MSC_VER) && !defined(__clang__) - #pragma warning(pop) -#elif defined(__clang__) || (defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 8))) - #pragma GCC diagnostic pop -#endif - -typedef HRESULT (WINAPI * MA_PFN_CoInitialize)(void* pvReserved); -typedef HRESULT (WINAPI * MA_PFN_CoInitializeEx)(void* pvReserved, DWORD dwCoInit); -typedef void (WINAPI * MA_PFN_CoUninitialize)(void); -typedef HRESULT (WINAPI * MA_PFN_CoCreateInstance)(const IID* rclsid, void* pUnkOuter, DWORD dwClsContext, const IID* riid, void* ppv); -typedef void (WINAPI * MA_PFN_CoTaskMemFree)(void* pv); -typedef HRESULT (WINAPI * MA_PFN_PropVariantClear)(MA_PROPVARIANT *pvar); -typedef int (WINAPI * MA_PFN_StringFromGUID2)(const GUID* const rguid, WCHAR* lpsz, int cchMax); - -typedef HWND (WINAPI * MA_PFN_GetForegroundWindow)(void); -typedef HWND (WINAPI * MA_PFN_GetDesktopWindow)(void); - -#if defined(MA_WIN32_DESKTOP) -/* Microsoft documents these APIs as returning LSTATUS, but the Win32 API shipping with some compilers do not define it. It's just a LONG. */ -typedef LONG (WINAPI * MA_PFN_RegOpenKeyExA)(HKEY hKey, const char* lpSubKey, DWORD ulOptions, DWORD samDesired, HKEY* phkResult); -typedef LONG (WINAPI * MA_PFN_RegCloseKey)(HKEY hKey); -typedef LONG (WINAPI * MA_PFN_RegQueryValueExA)(HKEY hKey, const char* lpValueName, DWORD* lpReserved, DWORD* lpType, BYTE* lpData, DWORD* lpcbData); -#endif /* MA_WIN32_DESKTOP */ - -MA_API size_t ma_strlen_WCHAR(const WCHAR* str) -{ - size_t len = 0; - while (str[len] != '\0') { - len += 1; - } - - return len; -} - -MA_API int ma_strcmp_WCHAR(const WCHAR *s1, const WCHAR *s2) -{ - while (*s1 != '\0' && *s1 == *s2) { - s1 += 1; - s2 += 1; - } - - return *s1 - *s2; -} - -MA_API int ma_strcpy_s_WCHAR(WCHAR* dst, size_t dstCap, const WCHAR* src) -{ - size_t i; - - if (dst == 0) { - return 22; - } - if (dstCap == 0) { - return 34; - } - if (src == 0) { - dst[0] = '\0'; - return 22; - } - - for (i = 0; i < dstCap && src[i] != '\0'; ++i) { - dst[i] = src[i]; - } - - if (i < dstCap) { - dst[i] = '\0'; - return 0; - } - - dst[0] = '\0'; - return 34; -} -#endif /* MA_WIN32 */ - - -#define MA_DEFAULT_PLAYBACK_DEVICE_NAME "Default Playback Device" -#define MA_DEFAULT_CAPTURE_DEVICE_NAME "Default Capture Device" - - - - -/******************************************************************************* - -Timing - -*******************************************************************************/ -#if defined(MA_WIN32) && !defined(MA_POSIX) - static LARGE_INTEGER g_ma_TimerFrequency; /* <-- Initialized to zero since it's static. */ - static void ma_timer_init(ma_timer* pTimer) - { - LARGE_INTEGER counter; - - if (g_ma_TimerFrequency.QuadPart == 0) { - QueryPerformanceFrequency(&g_ma_TimerFrequency); - } - - QueryPerformanceCounter(&counter); - pTimer->counter = counter.QuadPart; - } - - static double ma_timer_get_time_in_seconds(ma_timer* pTimer) - { - LARGE_INTEGER counter; - if (!QueryPerformanceCounter(&counter)) { - return 0; - } - - return (double)(counter.QuadPart - pTimer->counter) / g_ma_TimerFrequency.QuadPart; - } -#elif defined(MA_APPLE) && (MAC_OS_X_VERSION_MIN_REQUIRED < 101200) - static ma_uint64 g_ma_TimerFrequency = 0; - static void ma_timer_init(ma_timer* pTimer) - { - mach_timebase_info_data_t baseTime; - mach_timebase_info(&baseTime); - g_ma_TimerFrequency = (baseTime.denom * 1e9) / baseTime.numer; - - pTimer->counter = mach_absolute_time(); - } - - static double ma_timer_get_time_in_seconds(ma_timer* pTimer) - { - ma_uint64 newTimeCounter = mach_absolute_time(); - ma_uint64 oldTimeCounter = pTimer->counter; - - return (newTimeCounter - oldTimeCounter) / g_ma_TimerFrequency; - } -#elif defined(MA_EMSCRIPTEN) - static MA_INLINE void ma_timer_init(ma_timer* pTimer) - { - pTimer->counterD = emscripten_get_now(); - } - - static MA_INLINE double ma_timer_get_time_in_seconds(ma_timer* pTimer) - { - return (emscripten_get_now() - pTimer->counterD) / 1000; /* Emscripten is in milliseconds. */ - } -#else - #if defined(_POSIX_C_SOURCE) && _POSIX_C_SOURCE >= 199309L - #if defined(CLOCK_MONOTONIC) - #define MA_CLOCK_ID CLOCK_MONOTONIC - #else - #define MA_CLOCK_ID CLOCK_REALTIME - #endif - - static void ma_timer_init(ma_timer* pTimer) - { - struct timespec newTime; - clock_gettime(MA_CLOCK_ID, &newTime); - - pTimer->counter = (newTime.tv_sec * 1000000000) + newTime.tv_nsec; - } - - static double ma_timer_get_time_in_seconds(ma_timer* pTimer) - { - ma_uint64 newTimeCounter; - ma_uint64 oldTimeCounter; - - struct timespec newTime; - clock_gettime(MA_CLOCK_ID, &newTime); - - newTimeCounter = (newTime.tv_sec * 1000000000) + newTime.tv_nsec; - oldTimeCounter = pTimer->counter; - - return (newTimeCounter - oldTimeCounter) / 1000000000.0; - } - #else - static void ma_timer_init(ma_timer* pTimer) - { - struct timeval newTime; - gettimeofday(&newTime, NULL); - - pTimer->counter = (newTime.tv_sec * 1000000) + newTime.tv_usec; - } - - static double ma_timer_get_time_in_seconds(ma_timer* pTimer) - { - ma_uint64 newTimeCounter; - ma_uint64 oldTimeCounter; - - struct timeval newTime; - gettimeofday(&newTime, NULL); - - newTimeCounter = (newTime.tv_sec * 1000000) + newTime.tv_usec; - oldTimeCounter = pTimer->counter; - - return (newTimeCounter - oldTimeCounter) / 1000000.0; - } - #endif -#endif - - - -#if 0 -static ma_uint32 ma_get_closest_standard_sample_rate(ma_uint32 sampleRateIn) -{ - ma_uint32 closestRate = 0; - ma_uint32 closestDiff = 0xFFFFFFFF; - size_t iStandardRate; - - for (iStandardRate = 0; iStandardRate < ma_countof(g_maStandardSampleRatePriorities); ++iStandardRate) { - ma_uint32 standardRate = g_maStandardSampleRatePriorities[iStandardRate]; - ma_uint32 diff; - - if (sampleRateIn > standardRate) { - diff = sampleRateIn - standardRate; - } else { - diff = standardRate - sampleRateIn; - } - - if (diff == 0) { - return standardRate; /* The input sample rate is a standard rate. */ - } - - if (closestDiff > diff) { - closestDiff = diff; - closestRate = standardRate; - } - } - - return closestRate; -} -#endif - - -static MA_INLINE unsigned int ma_device_disable_denormals(ma_device* pDevice) -{ - MA_ASSERT(pDevice != NULL); - - if (!pDevice->noDisableDenormals) { - return ma_disable_denormals(); - } else { - return 0; - } -} - -static MA_INLINE void ma_device_restore_denormals(ma_device* pDevice, unsigned int prevState) -{ - MA_ASSERT(pDevice != NULL); - - if (!pDevice->noDisableDenormals) { - ma_restore_denormals(prevState); - } else { - /* Do nothing. */ - (void)prevState; - } -} - -static ma_device_notification ma_device_notification_init(ma_device* pDevice, ma_device_notification_type type) -{ - ma_device_notification notification; - - MA_ZERO_OBJECT(¬ification); - notification.pDevice = pDevice; - notification.type = type; - - return notification; -} - -static void ma_device__on_notification(ma_device_notification notification) -{ - MA_ASSERT(notification.pDevice != NULL); - - if (notification.pDevice->onNotification != NULL) { - notification.pDevice->onNotification(¬ification); - } - - /* TEMP FOR COMPATIBILITY: If it's a stopped notification, fire the onStop callback as well. This is only for backwards compatibility and will be removed. */ - if (notification.pDevice->onStop != NULL && notification.type == ma_device_notification_type_stopped) { - notification.pDevice->onStop(notification.pDevice); - } -} - -static void ma_device__on_notification_started(ma_device* pDevice) -{ - ma_device__on_notification(ma_device_notification_init(pDevice, ma_device_notification_type_started)); -} - -static void ma_device__on_notification_stopped(ma_device* pDevice) -{ - ma_device__on_notification(ma_device_notification_init(pDevice, ma_device_notification_type_stopped)); -} - -/* Not all platforms support reroute notifications. */ -#if !defined(MA_EMSCRIPTEN) -static void ma_device__on_notification_rerouted(ma_device* pDevice) -{ - ma_device__on_notification(ma_device_notification_init(pDevice, ma_device_notification_type_rerouted)); -} -#endif - -#if defined(MA_EMSCRIPTEN) -#ifdef __cplusplus -extern "C" { -#endif -void EMSCRIPTEN_KEEPALIVE ma_device__on_notification_unlocked(ma_device* pDevice) -{ - ma_device__on_notification(ma_device_notification_init(pDevice, ma_device_notification_type_unlocked)); -} -#ifdef __cplusplus -} -#endif -#endif - - -static void ma_device__on_data_inner(ma_device* pDevice, void* pFramesOut, const void* pFramesIn, ma_uint32 frameCount) -{ - MA_ASSERT(pDevice != NULL); - MA_ASSERT(pDevice->onData != NULL); - - if (!pDevice->noPreSilencedOutputBuffer && pFramesOut != NULL) { - ma_silence_pcm_frames(pFramesOut, frameCount, pDevice->playback.format, pDevice->playback.channels); - } - - pDevice->onData(pDevice, pFramesOut, pFramesIn, frameCount); -} - -static void ma_device__on_data(ma_device* pDevice, void* pFramesOut, const void* pFramesIn, ma_uint32 frameCount) -{ - MA_ASSERT(pDevice != NULL); - - /* Don't read more data from the client if we're in the process of stopping. */ - if (ma_device_get_state(pDevice) == ma_device_state_stopping) { - return; - } - - if (pDevice->noFixedSizedCallback) { - /* Fast path. Not using a fixed sized callback. Process directly from the specified buffers. */ - ma_device__on_data_inner(pDevice, pFramesOut, pFramesIn, frameCount); - } else { - /* Slow path. Using a fixed sized callback. Need to use the intermediary buffer. */ - ma_uint32 totalFramesProcessed = 0; - - while (totalFramesProcessed < frameCount) { - ma_uint32 totalFramesRemaining = frameCount - totalFramesProcessed; - ma_uint32 framesToProcessThisIteration = 0; - - if (pFramesIn != NULL) { - /* Capturing. Write to the intermediary buffer. If there's no room, fire the callback to empty it. */ - if (pDevice->capture.intermediaryBufferLen < pDevice->capture.intermediaryBufferCap) { - /* There's some room left in the intermediary buffer. Write to it without firing the callback. */ - framesToProcessThisIteration = totalFramesRemaining; - if (framesToProcessThisIteration > pDevice->capture.intermediaryBufferCap - pDevice->capture.intermediaryBufferLen) { - framesToProcessThisIteration = pDevice->capture.intermediaryBufferCap - pDevice->capture.intermediaryBufferLen; - } - - ma_copy_pcm_frames( - ma_offset_pcm_frames_ptr(pDevice->capture.pIntermediaryBuffer, pDevice->capture.intermediaryBufferLen, pDevice->capture.format, pDevice->capture.channels), - ma_offset_pcm_frames_const_ptr(pFramesIn, totalFramesProcessed, pDevice->capture.format, pDevice->capture.channels), - framesToProcessThisIteration, - pDevice->capture.format, pDevice->capture.channels); - - pDevice->capture.intermediaryBufferLen += framesToProcessThisIteration; - } - - if (pDevice->capture.intermediaryBufferLen == pDevice->capture.intermediaryBufferCap) { - /* No room left in the intermediary buffer. Fire the data callback. */ - if (pDevice->type == ma_device_type_duplex) { - /* We'll do the duplex data callback later after we've processed the playback data. */ - } else { - ma_device__on_data_inner(pDevice, NULL, pDevice->capture.pIntermediaryBuffer, pDevice->capture.intermediaryBufferCap); - - /* The intermediary buffer has just been drained. */ - pDevice->capture.intermediaryBufferLen = 0; - } - } - } - - if (pFramesOut != NULL) { - /* Playing back. Read from the intermediary buffer. If there's nothing in it, fire the callback to fill it. */ - if (pDevice->playback.intermediaryBufferLen > 0) { - /* There's some content in the intermediary buffer. Read from that without firing the callback. */ - if (pDevice->type == ma_device_type_duplex) { - /* The frames processed this iteration for a duplex device will always be based on the capture side. Leave it unmodified. */ - } else { - framesToProcessThisIteration = totalFramesRemaining; - if (framesToProcessThisIteration > pDevice->playback.intermediaryBufferLen) { - framesToProcessThisIteration = pDevice->playback.intermediaryBufferLen; - } - } - - ma_copy_pcm_frames( - ma_offset_pcm_frames_ptr(pFramesOut, totalFramesProcessed, pDevice->playback.format, pDevice->playback.channels), - ma_offset_pcm_frames_ptr(pDevice->playback.pIntermediaryBuffer, pDevice->playback.intermediaryBufferCap - pDevice->playback.intermediaryBufferLen, pDevice->playback.format, pDevice->playback.channels), - framesToProcessThisIteration, - pDevice->playback.format, pDevice->playback.channels); - - pDevice->playback.intermediaryBufferLen -= framesToProcessThisIteration; - } - - if (pDevice->playback.intermediaryBufferLen == 0) { - /* There's nothing in the intermediary buffer. Fire the data callback to fill it. */ - if (pDevice->type == ma_device_type_duplex) { - /* In duplex mode, the data callback will be fired later. Nothing to do here. */ - } else { - ma_device__on_data_inner(pDevice, pDevice->playback.pIntermediaryBuffer, NULL, pDevice->playback.intermediaryBufferCap); - - /* The intermediary buffer has just been filled. */ - pDevice->playback.intermediaryBufferLen = pDevice->playback.intermediaryBufferCap; - } - } - } - - /* If we're in duplex mode we might need to do a refill of the data. */ - if (pDevice->type == ma_device_type_duplex) { - if (pDevice->capture.intermediaryBufferLen == pDevice->capture.intermediaryBufferCap) { - ma_device__on_data_inner(pDevice, pDevice->playback.pIntermediaryBuffer, pDevice->capture.pIntermediaryBuffer, pDevice->capture.intermediaryBufferCap); - - pDevice->playback.intermediaryBufferLen = pDevice->playback.intermediaryBufferCap; /* The playback buffer will have just been filled. */ - pDevice->capture.intermediaryBufferLen = 0; /* The intermediary buffer has just been drained. */ - } - } - - /* Make sure this is only incremented once in the duplex case. */ - totalFramesProcessed += framesToProcessThisIteration; - } - } -} - -static void ma_device__handle_data_callback(ma_device* pDevice, void* pFramesOut, const void* pFramesIn, ma_uint32 frameCount) -{ - float masterVolumeFactor; - - ma_device_get_master_volume(pDevice, &masterVolumeFactor); /* Use ma_device_get_master_volume() to ensure the volume is loaded atomically. */ - - if (pDevice->onData) { - unsigned int prevDenormalState = ma_device_disable_denormals(pDevice); - { - /* Volume control of input makes things a bit awkward because the input buffer is read-only. We'll need to use a temp buffer and loop in this case. */ - if (pFramesIn != NULL && masterVolumeFactor != 1) { - ma_uint8 tempFramesIn[MA_DATA_CONVERTER_STACK_BUFFER_SIZE]; - ma_uint32 bpfCapture = ma_get_bytes_per_frame(pDevice->capture.format, pDevice->capture.channels); - ma_uint32 bpfPlayback = ma_get_bytes_per_frame(pDevice->playback.format, pDevice->playback.channels); - ma_uint32 totalFramesProcessed = 0; - while (totalFramesProcessed < frameCount) { - ma_uint32 framesToProcessThisIteration = frameCount - totalFramesProcessed; - if (framesToProcessThisIteration > sizeof(tempFramesIn)/bpfCapture) { - framesToProcessThisIteration = sizeof(tempFramesIn)/bpfCapture; - } - - ma_copy_and_apply_volume_factor_pcm_frames(tempFramesIn, ma_offset_ptr(pFramesIn, totalFramesProcessed*bpfCapture), framesToProcessThisIteration, pDevice->capture.format, pDevice->capture.channels, masterVolumeFactor); - - ma_device__on_data(pDevice, ma_offset_ptr(pFramesOut, totalFramesProcessed*bpfPlayback), tempFramesIn, framesToProcessThisIteration); - - totalFramesProcessed += framesToProcessThisIteration; - } - } else { - ma_device__on_data(pDevice, pFramesOut, pFramesIn, frameCount); - } - - /* Volume control and clipping for playback devices. */ - if (pFramesOut != NULL) { - if (masterVolumeFactor != 1) { - if (pFramesIn == NULL) { /* <-- In full-duplex situations, the volume will have been applied to the input samples before the data callback. Applying it again post-callback will incorrectly compound it. */ - ma_apply_volume_factor_pcm_frames(pFramesOut, frameCount, pDevice->playback.format, pDevice->playback.channels, masterVolumeFactor); - } - } - - if (!pDevice->noClip && pDevice->playback.format == ma_format_f32) { - ma_clip_samples_f32((float*)pFramesOut, (const float*)pFramesOut, frameCount * pDevice->playback.channels); /* Intentionally specifying the same pointer for both input and output for in-place processing. */ - } - } - } - ma_device_restore_denormals(pDevice, prevDenormalState); - } else { - /* No data callback. Just silence the output. */ - if (pFramesOut != NULL) { - ma_silence_pcm_frames(pFramesOut, frameCount, pDevice->playback.format, pDevice->playback.channels); - } - } -} - - - -/* A helper function for reading sample data from the client. */ -static void ma_device__read_frames_from_client(ma_device* pDevice, ma_uint32 frameCount, void* pFramesOut) -{ - MA_ASSERT(pDevice != NULL); - MA_ASSERT(frameCount > 0); - MA_ASSERT(pFramesOut != NULL); - - if (pDevice->playback.converter.isPassthrough) { - ma_device__handle_data_callback(pDevice, pFramesOut, NULL, frameCount); - } else { - ma_result result; - ma_uint64 totalFramesReadOut; - void* pRunningFramesOut; - - totalFramesReadOut = 0; - pRunningFramesOut = pFramesOut; - - /* - We run slightly different logic depending on whether or not we're using a heap-allocated - buffer for caching input data. This will be the case if the data converter does not have - the ability to retrieve the required input frame count for a given output frame count. - */ - if (pDevice->playback.pInputCache != NULL) { - while (totalFramesReadOut < frameCount) { - ma_uint64 framesToReadThisIterationIn; - ma_uint64 framesToReadThisIterationOut; - - /* If there's any data available in the cache, that needs to get processed first. */ - if (pDevice->playback.inputCacheRemaining > 0) { - framesToReadThisIterationOut = (frameCount - totalFramesReadOut); - framesToReadThisIterationIn = framesToReadThisIterationOut; - if (framesToReadThisIterationIn > pDevice->playback.inputCacheRemaining) { - framesToReadThisIterationIn = pDevice->playback.inputCacheRemaining; - } - - result = ma_data_converter_process_pcm_frames(&pDevice->playback.converter, ma_offset_pcm_frames_ptr(pDevice->playback.pInputCache, pDevice->playback.inputCacheConsumed, pDevice->playback.format, pDevice->playback.channels), &framesToReadThisIterationIn, pRunningFramesOut, &framesToReadThisIterationOut); - if (result != MA_SUCCESS) { - break; - } - - pDevice->playback.inputCacheConsumed += framesToReadThisIterationIn; - pDevice->playback.inputCacheRemaining -= framesToReadThisIterationIn; - - totalFramesReadOut += framesToReadThisIterationOut; - pRunningFramesOut = ma_offset_ptr(pRunningFramesOut, framesToReadThisIterationOut * ma_get_bytes_per_frame(pDevice->playback.internalFormat, pDevice->playback.internalChannels)); - - if (framesToReadThisIterationIn == 0 && framesToReadThisIterationOut == 0) { - break; /* We're done. */ - } - } - - /* Getting here means there's no data in the cache and we need to fill it up with data from the client. */ - if (pDevice->playback.inputCacheRemaining == 0) { - ma_device__handle_data_callback(pDevice, pDevice->playback.pInputCache, NULL, (ma_uint32)pDevice->playback.inputCacheCap); - - pDevice->playback.inputCacheConsumed = 0; - pDevice->playback.inputCacheRemaining = pDevice->playback.inputCacheCap; - } - } - } else { - while (totalFramesReadOut < frameCount) { - ma_uint8 pIntermediaryBuffer[MA_DATA_CONVERTER_STACK_BUFFER_SIZE]; /* In client format. */ - ma_uint64 intermediaryBufferCap = sizeof(pIntermediaryBuffer) / ma_get_bytes_per_frame(pDevice->playback.format, pDevice->playback.channels); - ma_uint64 framesToReadThisIterationIn; - ma_uint64 framesReadThisIterationIn; - ma_uint64 framesToReadThisIterationOut; - ma_uint64 framesReadThisIterationOut; - ma_uint64 requiredInputFrameCount; - - framesToReadThisIterationOut = (frameCount - totalFramesReadOut); - framesToReadThisIterationIn = framesToReadThisIterationOut; - if (framesToReadThisIterationIn > intermediaryBufferCap) { - framesToReadThisIterationIn = intermediaryBufferCap; - } - - ma_data_converter_get_required_input_frame_count(&pDevice->playback.converter, framesToReadThisIterationOut, &requiredInputFrameCount); - if (framesToReadThisIterationIn > requiredInputFrameCount) { - framesToReadThisIterationIn = requiredInputFrameCount; - } - - ma_device__handle_data_callback(pDevice, pIntermediaryBuffer, NULL, (ma_uint32)framesToReadThisIterationIn); - - /* - At this point we have our decoded data in input format and now we need to convert to output format. Note that even if we didn't read any - input frames, we still want to try processing frames because there may some output frames generated from cached input data. - */ - framesReadThisIterationIn = framesToReadThisIterationIn; - framesReadThisIterationOut = framesToReadThisIterationOut; - result = ma_data_converter_process_pcm_frames(&pDevice->playback.converter, pIntermediaryBuffer, &framesReadThisIterationIn, pRunningFramesOut, &framesReadThisIterationOut); - if (result != MA_SUCCESS) { - break; - } - - totalFramesReadOut += framesReadThisIterationOut; - pRunningFramesOut = ma_offset_ptr(pRunningFramesOut, framesReadThisIterationOut * ma_get_bytes_per_frame(pDevice->playback.internalFormat, pDevice->playback.internalChannels)); - - if (framesReadThisIterationIn == 0 && framesReadThisIterationOut == 0) { - break; /* We're done. */ - } - } - } - } -} - -/* A helper for sending sample data to the client. */ -static void ma_device__send_frames_to_client(ma_device* pDevice, ma_uint32 frameCountInDeviceFormat, const void* pFramesInDeviceFormat) -{ - MA_ASSERT(pDevice != NULL); - MA_ASSERT(frameCountInDeviceFormat > 0); - MA_ASSERT(pFramesInDeviceFormat != NULL); - - if (pDevice->capture.converter.isPassthrough) { - ma_device__handle_data_callback(pDevice, NULL, pFramesInDeviceFormat, frameCountInDeviceFormat); - } else { - ma_result result; - ma_uint8 pFramesInClientFormat[MA_DATA_CONVERTER_STACK_BUFFER_SIZE]; - ma_uint64 framesInClientFormatCap = sizeof(pFramesInClientFormat) / ma_get_bytes_per_frame(pDevice->capture.format, pDevice->capture.channels); - ma_uint64 totalDeviceFramesProcessed = 0; - ma_uint64 totalClientFramesProcessed = 0; - const void* pRunningFramesInDeviceFormat = pFramesInDeviceFormat; - - /* We just keep going until we've exhausted all of our input frames and cannot generate any more output frames. */ - for (;;) { - ma_uint64 deviceFramesProcessedThisIteration; - ma_uint64 clientFramesProcessedThisIteration; - - deviceFramesProcessedThisIteration = (frameCountInDeviceFormat - totalDeviceFramesProcessed); - clientFramesProcessedThisIteration = framesInClientFormatCap; - - result = ma_data_converter_process_pcm_frames(&pDevice->capture.converter, pRunningFramesInDeviceFormat, &deviceFramesProcessedThisIteration, pFramesInClientFormat, &clientFramesProcessedThisIteration); - if (result != MA_SUCCESS) { - break; - } - - if (clientFramesProcessedThisIteration > 0) { - ma_device__handle_data_callback(pDevice, NULL, pFramesInClientFormat, (ma_uint32)clientFramesProcessedThisIteration); /* Safe cast. */ - } - - pRunningFramesInDeviceFormat = ma_offset_ptr(pRunningFramesInDeviceFormat, deviceFramesProcessedThisIteration * ma_get_bytes_per_frame(pDevice->capture.internalFormat, pDevice->capture.internalChannels)); - totalDeviceFramesProcessed += deviceFramesProcessedThisIteration; - totalClientFramesProcessed += clientFramesProcessedThisIteration; - - /* This is just to silence a warning. I might want to use this variable later so leaving in place for now. */ - (void)totalClientFramesProcessed; - - if (deviceFramesProcessedThisIteration == 0 && clientFramesProcessedThisIteration == 0) { - break; /* We're done. */ - } - } - } -} - -static ma_result ma_device__handle_duplex_callback_capture(ma_device* pDevice, ma_uint32 frameCountInDeviceFormat, const void* pFramesInDeviceFormat, ma_pcm_rb* pRB) -{ - ma_result result; - ma_uint32 totalDeviceFramesProcessed = 0; - const void* pRunningFramesInDeviceFormat = pFramesInDeviceFormat; - - MA_ASSERT(pDevice != NULL); - MA_ASSERT(frameCountInDeviceFormat > 0); - MA_ASSERT(pFramesInDeviceFormat != NULL); - MA_ASSERT(pRB != NULL); - - /* Write to the ring buffer. The ring buffer is in the client format which means we need to convert. */ - for (;;) { - ma_uint32 framesToProcessInDeviceFormat = (frameCountInDeviceFormat - totalDeviceFramesProcessed); - ma_uint32 framesToProcessInClientFormat = MA_DATA_CONVERTER_STACK_BUFFER_SIZE / ma_get_bytes_per_frame(pDevice->capture.format, pDevice->capture.channels); - ma_uint64 framesProcessedInDeviceFormat; - ma_uint64 framesProcessedInClientFormat; - void* pFramesInClientFormat; - - result = ma_pcm_rb_acquire_write(pRB, &framesToProcessInClientFormat, &pFramesInClientFormat); - if (result != MA_SUCCESS) { - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "Failed to acquire capture PCM frames from ring buffer."); - break; - } - - if (framesToProcessInClientFormat == 0) { - if (ma_pcm_rb_pointer_distance(pRB) == (ma_int32)ma_pcm_rb_get_subbuffer_size(pRB)) { - break; /* Overrun. Not enough room in the ring buffer for input frame. Excess frames are dropped. */ - } - } - - /* Convert. */ - framesProcessedInDeviceFormat = framesToProcessInDeviceFormat; - framesProcessedInClientFormat = framesToProcessInClientFormat; - result = ma_data_converter_process_pcm_frames(&pDevice->capture.converter, pRunningFramesInDeviceFormat, &framesProcessedInDeviceFormat, pFramesInClientFormat, &framesProcessedInClientFormat); - if (result != MA_SUCCESS) { - break; - } - - result = ma_pcm_rb_commit_write(pRB, (ma_uint32)framesProcessedInClientFormat); /* Safe cast. */ - if (result != MA_SUCCESS) { - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "Failed to commit capture PCM frames to ring buffer."); - break; - } - - pRunningFramesInDeviceFormat = ma_offset_ptr(pRunningFramesInDeviceFormat, framesProcessedInDeviceFormat * ma_get_bytes_per_frame(pDevice->capture.internalFormat, pDevice->capture.internalChannels)); - totalDeviceFramesProcessed += (ma_uint32)framesProcessedInDeviceFormat; /* Safe cast. */ - - /* We're done when we're unable to process any client nor device frames. */ - if (framesProcessedInClientFormat == 0 && framesProcessedInDeviceFormat == 0) { - break; /* Done. */ - } - } - - return MA_SUCCESS; -} - -static ma_result ma_device__handle_duplex_callback_playback(ma_device* pDevice, ma_uint32 frameCount, void* pFramesInInternalFormat, ma_pcm_rb* pRB) -{ - ma_result result; - ma_uint8 silentInputFrames[MA_DATA_CONVERTER_STACK_BUFFER_SIZE]; - ma_uint32 totalFramesReadOut = 0; - - MA_ASSERT(pDevice != NULL); - MA_ASSERT(frameCount > 0); - MA_ASSERT(pFramesInInternalFormat != NULL); - MA_ASSERT(pRB != NULL); - MA_ASSERT(pDevice->playback.pInputCache != NULL); - - /* - Sitting in the ring buffer should be captured data from the capture callback in external format. If there's not enough data in there for - the whole frameCount frames we just use silence instead for the input data. - */ - MA_ZERO_MEMORY(silentInputFrames, sizeof(silentInputFrames)); - - while (totalFramesReadOut < frameCount && ma_device_is_started(pDevice)) { - /* - We should have a buffer allocated on the heap. Any playback frames still sitting in there - need to be sent to the internal device before we process any more data from the client. - */ - if (pDevice->playback.inputCacheRemaining > 0) { - ma_uint64 framesConvertedIn = pDevice->playback.inputCacheRemaining; - ma_uint64 framesConvertedOut = (frameCount - totalFramesReadOut); - ma_data_converter_process_pcm_frames(&pDevice->playback.converter, ma_offset_pcm_frames_ptr(pDevice->playback.pInputCache, pDevice->playback.inputCacheConsumed, pDevice->playback.format, pDevice->playback.channels), &framesConvertedIn, pFramesInInternalFormat, &framesConvertedOut); - - pDevice->playback.inputCacheConsumed += framesConvertedIn; - pDevice->playback.inputCacheRemaining -= framesConvertedIn; - - totalFramesReadOut += (ma_uint32)framesConvertedOut; /* Safe cast. */ - pFramesInInternalFormat = ma_offset_ptr(pFramesInInternalFormat, framesConvertedOut * ma_get_bytes_per_frame(pDevice->playback.internalFormat, pDevice->playback.internalChannels)); - } - - /* If there's no more data in the cache we'll need to fill it with some. */ - if (totalFramesReadOut < frameCount && pDevice->playback.inputCacheRemaining == 0) { - ma_uint32 inputFrameCount; - void* pInputFrames; - - inputFrameCount = (ma_uint32)pDevice->playback.inputCacheCap; - result = ma_pcm_rb_acquire_read(pRB, &inputFrameCount, &pInputFrames); - if (result == MA_SUCCESS) { - if (inputFrameCount > 0) { - ma_device__handle_data_callback(pDevice, pDevice->playback.pInputCache, pInputFrames, inputFrameCount); - } else { - if (ma_pcm_rb_pointer_distance(pRB) == 0) { - break; /* Underrun. */ - } - } - } else { - /* No capture data available. Feed in silence. */ - inputFrameCount = (ma_uint32)ma_min(pDevice->playback.inputCacheCap, sizeof(silentInputFrames) / ma_get_bytes_per_frame(pDevice->capture.format, pDevice->capture.channels)); - ma_device__handle_data_callback(pDevice, pDevice->playback.pInputCache, silentInputFrames, inputFrameCount); - } - - pDevice->playback.inputCacheConsumed = 0; - pDevice->playback.inputCacheRemaining = inputFrameCount; - - result = ma_pcm_rb_commit_read(pRB, inputFrameCount); - if (result != MA_SUCCESS) { - return result; /* Should never happen. */ - } - } - } - - return MA_SUCCESS; -} - -/* A helper for changing the state of the device. */ -static MA_INLINE void ma_device__set_state(ma_device* pDevice, ma_device_state newState) -{ - ma_atomic_device_state_set(&pDevice->state, newState); -} - - -#if defined(MA_WIN32) - static GUID MA_GUID_KSDATAFORMAT_SUBTYPE_PCM = {0x00000001, 0x0000, 0x0010, {0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71}}; - static GUID MA_GUID_KSDATAFORMAT_SUBTYPE_IEEE_FLOAT = {0x00000003, 0x0000, 0x0010, {0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71}}; - /*static GUID MA_GUID_KSDATAFORMAT_SUBTYPE_ALAW = {0x00000006, 0x0000, 0x0010, {0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71}};*/ - /*static GUID MA_GUID_KSDATAFORMAT_SUBTYPE_MULAW = {0x00000007, 0x0000, 0x0010, {0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71}};*/ -#endif - - - -MA_API ma_uint32 ma_get_format_priority_index(ma_format format) /* Lower = better. */ -{ - ma_uint32 i; - for (i = 0; i < ma_countof(g_maFormatPriorities); ++i) { - if (g_maFormatPriorities[i] == format) { - return i; - } - } - - /* Getting here means the format could not be found or is equal to ma_format_unknown. */ - return (ma_uint32)-1; -} - -static ma_result ma_device__post_init_setup(ma_device* pDevice, ma_device_type deviceType); - -static ma_bool32 ma_device_descriptor_is_valid(const ma_device_descriptor* pDeviceDescriptor) -{ - if (pDeviceDescriptor == NULL) { - return MA_FALSE; - } - - if (pDeviceDescriptor->format == ma_format_unknown) { - return MA_FALSE; - } - - if (pDeviceDescriptor->channels == 0 || pDeviceDescriptor->channels > MA_MAX_CHANNELS) { - return MA_FALSE; - } - - if (pDeviceDescriptor->sampleRate == 0) { - return MA_FALSE; - } - - return MA_TRUE; -} - - -static ma_result ma_device_audio_thread__default_read_write(ma_device* pDevice) -{ - ma_result result = MA_SUCCESS; - ma_bool32 exitLoop = MA_FALSE; - ma_uint8 capturedDeviceData[MA_DATA_CONVERTER_STACK_BUFFER_SIZE]; - ma_uint8 playbackDeviceData[MA_DATA_CONVERTER_STACK_BUFFER_SIZE]; - ma_uint32 capturedDeviceDataCapInFrames = 0; - ma_uint32 playbackDeviceDataCapInFrames = 0; - - MA_ASSERT(pDevice != NULL); - - /* Just some quick validation on the device type and the available callbacks. */ - if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex || pDevice->type == ma_device_type_loopback) { - if (pDevice->pContext->callbacks.onDeviceRead == NULL) { - return MA_NOT_IMPLEMENTED; - } - - capturedDeviceDataCapInFrames = sizeof(capturedDeviceData) / ma_get_bytes_per_frame(pDevice->capture.internalFormat, pDevice->capture.internalChannels); - } - - if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) { - if (pDevice->pContext->callbacks.onDeviceWrite == NULL) { - return MA_NOT_IMPLEMENTED; - } - - playbackDeviceDataCapInFrames = sizeof(playbackDeviceData) / ma_get_bytes_per_frame(pDevice->playback.internalFormat, pDevice->playback.internalChannels); - } - - /* NOTE: The device was started outside of this function, in the worker thread. */ - - while (ma_device_get_state(pDevice) == ma_device_state_started && !exitLoop) { - switch (pDevice->type) { - case ma_device_type_duplex: - { - /* The process is: onDeviceRead() -> convert -> callback -> convert -> onDeviceWrite() */ - ma_uint32 totalCapturedDeviceFramesProcessed = 0; - ma_uint32 capturedDevicePeriodSizeInFrames = ma_min(pDevice->capture.internalPeriodSizeInFrames, pDevice->playback.internalPeriodSizeInFrames); - - while (totalCapturedDeviceFramesProcessed < capturedDevicePeriodSizeInFrames) { - ma_uint32 capturedDeviceFramesRemaining; - ma_uint32 capturedDeviceFramesProcessed; - ma_uint32 capturedDeviceFramesToProcess; - ma_uint32 capturedDeviceFramesToTryProcessing = capturedDevicePeriodSizeInFrames - totalCapturedDeviceFramesProcessed; - if (capturedDeviceFramesToTryProcessing > capturedDeviceDataCapInFrames) { - capturedDeviceFramesToTryProcessing = capturedDeviceDataCapInFrames; - } - - result = pDevice->pContext->callbacks.onDeviceRead(pDevice, capturedDeviceData, capturedDeviceFramesToTryProcessing, &capturedDeviceFramesToProcess); - if (result != MA_SUCCESS) { - exitLoop = MA_TRUE; - break; - } - - capturedDeviceFramesRemaining = capturedDeviceFramesToProcess; - capturedDeviceFramesProcessed = 0; - - /* At this point we have our captured data in device format and we now need to convert it to client format. */ - for (;;) { - ma_uint8 capturedClientData[MA_DATA_CONVERTER_STACK_BUFFER_SIZE]; - ma_uint8 playbackClientData[MA_DATA_CONVERTER_STACK_BUFFER_SIZE]; - ma_uint32 capturedClientDataCapInFrames = sizeof(capturedClientData) / ma_get_bytes_per_frame(pDevice->capture.format, pDevice->capture.channels); - ma_uint32 playbackClientDataCapInFrames = sizeof(playbackClientData) / ma_get_bytes_per_frame(pDevice->playback.format, pDevice->playback.channels); - ma_uint64 capturedClientFramesToProcessThisIteration = ma_min(capturedClientDataCapInFrames, playbackClientDataCapInFrames); - ma_uint64 capturedDeviceFramesToProcessThisIteration = capturedDeviceFramesRemaining; - ma_uint8* pRunningCapturedDeviceFrames = ma_offset_ptr(capturedDeviceData, capturedDeviceFramesProcessed * ma_get_bytes_per_frame(pDevice->capture.internalFormat, pDevice->capture.internalChannels)); - - /* Convert capture data from device format to client format. */ - result = ma_data_converter_process_pcm_frames(&pDevice->capture.converter, pRunningCapturedDeviceFrames, &capturedDeviceFramesToProcessThisIteration, capturedClientData, &capturedClientFramesToProcessThisIteration); - if (result != MA_SUCCESS) { - break; - } - - /* - If we weren't able to generate any output frames it must mean we've exhausted all of our input. The only time this would not be the case is if capturedClientData was too small - which should never be the case when it's of the size MA_DATA_CONVERTER_STACK_BUFFER_SIZE. - */ - if (capturedClientFramesToProcessThisIteration == 0) { - break; - } - - ma_device__handle_data_callback(pDevice, playbackClientData, capturedClientData, (ma_uint32)capturedClientFramesToProcessThisIteration); /* Safe cast .*/ - - capturedDeviceFramesProcessed += (ma_uint32)capturedDeviceFramesToProcessThisIteration; /* Safe cast. */ - capturedDeviceFramesRemaining -= (ma_uint32)capturedDeviceFramesToProcessThisIteration; /* Safe cast. */ - - /* At this point the playbackClientData buffer should be holding data that needs to be written to the device. */ - for (;;) { - ma_uint64 convertedClientFrameCount = capturedClientFramesToProcessThisIteration; - ma_uint64 convertedDeviceFrameCount = playbackDeviceDataCapInFrames; - result = ma_data_converter_process_pcm_frames(&pDevice->playback.converter, playbackClientData, &convertedClientFrameCount, playbackDeviceData, &convertedDeviceFrameCount); - if (result != MA_SUCCESS) { - break; - } - - result = pDevice->pContext->callbacks.onDeviceWrite(pDevice, playbackDeviceData, (ma_uint32)convertedDeviceFrameCount, NULL); /* Safe cast. */ - if (result != MA_SUCCESS) { - exitLoop = MA_TRUE; - break; - } - - capturedClientFramesToProcessThisIteration -= (ma_uint32)convertedClientFrameCount; /* Safe cast. */ - if (capturedClientFramesToProcessThisIteration == 0) { - break; - } - } - - /* In case an error happened from ma_device_write__null()... */ - if (result != MA_SUCCESS) { - exitLoop = MA_TRUE; - break; - } - } - - /* Make sure we don't get stuck in the inner loop. */ - if (capturedDeviceFramesProcessed == 0) { - break; - } - - totalCapturedDeviceFramesProcessed += capturedDeviceFramesProcessed; - } - } break; - - case ma_device_type_capture: - case ma_device_type_loopback: - { - ma_uint32 periodSizeInFrames = pDevice->capture.internalPeriodSizeInFrames; - ma_uint32 framesReadThisPeriod = 0; - while (framesReadThisPeriod < periodSizeInFrames) { - ma_uint32 framesRemainingInPeriod = periodSizeInFrames - framesReadThisPeriod; - ma_uint32 framesProcessed; - ma_uint32 framesToReadThisIteration = framesRemainingInPeriod; - if (framesToReadThisIteration > capturedDeviceDataCapInFrames) { - framesToReadThisIteration = capturedDeviceDataCapInFrames; - } - - result = pDevice->pContext->callbacks.onDeviceRead(pDevice, capturedDeviceData, framesToReadThisIteration, &framesProcessed); - if (result != MA_SUCCESS) { - exitLoop = MA_TRUE; - break; - } - - /* Make sure we don't get stuck in the inner loop. */ - if (framesProcessed == 0) { - break; - } - - ma_device__send_frames_to_client(pDevice, framesProcessed, capturedDeviceData); - - framesReadThisPeriod += framesProcessed; - } - } break; - - case ma_device_type_playback: - { - /* We write in chunks of the period size, but use a stack allocated buffer for the intermediary. */ - ma_uint32 periodSizeInFrames = pDevice->playback.internalPeriodSizeInFrames; - ma_uint32 framesWrittenThisPeriod = 0; - while (framesWrittenThisPeriod < periodSizeInFrames) { - ma_uint32 framesRemainingInPeriod = periodSizeInFrames - framesWrittenThisPeriod; - ma_uint32 framesProcessed; - ma_uint32 framesToWriteThisIteration = framesRemainingInPeriod; - if (framesToWriteThisIteration > playbackDeviceDataCapInFrames) { - framesToWriteThisIteration = playbackDeviceDataCapInFrames; - } - - ma_device__read_frames_from_client(pDevice, framesToWriteThisIteration, playbackDeviceData); - - result = pDevice->pContext->callbacks.onDeviceWrite(pDevice, playbackDeviceData, framesToWriteThisIteration, &framesProcessed); - if (result != MA_SUCCESS) { - exitLoop = MA_TRUE; - break; - } - - /* Make sure we don't get stuck in the inner loop. */ - if (framesProcessed == 0) { - break; - } - - framesWrittenThisPeriod += framesProcessed; - } - } break; - - /* Should never get here. */ - default: break; - } - } - - return result; -} - - - -/******************************************************************************* - -Null Backend - -*******************************************************************************/ -#ifdef MA_HAS_NULL - -#define MA_DEVICE_OP_NONE__NULL 0 -#define MA_DEVICE_OP_START__NULL 1 -#define MA_DEVICE_OP_SUSPEND__NULL 2 -#define MA_DEVICE_OP_KILL__NULL 3 - -static ma_thread_result MA_THREADCALL ma_device_thread__null(void* pData) -{ - ma_device* pDevice = (ma_device*)pData; - MA_ASSERT(pDevice != NULL); - - for (;;) { /* Keep the thread alive until the device is uninitialized. */ - ma_uint32 operation; - - /* Wait for an operation to be requested. */ - ma_event_wait(&pDevice->null_device.operationEvent); - - /* At this point an event should have been triggered. */ - operation = pDevice->null_device.operation; - - /* Starting the device needs to put the thread into a loop. */ - if (operation == MA_DEVICE_OP_START__NULL) { - /* Reset the timer just in case. */ - ma_timer_init(&pDevice->null_device.timer); - - /* Getting here means a suspend or kill operation has been requested. */ - pDevice->null_device.operationResult = MA_SUCCESS; - ma_event_signal(&pDevice->null_device.operationCompletionEvent); - ma_semaphore_release(&pDevice->null_device.operationSemaphore); - continue; - } - - /* Suspending the device means we need to stop the timer and just continue the loop. */ - if (operation == MA_DEVICE_OP_SUSPEND__NULL) { - /* We need to add the current run time to the prior run time, then reset the timer. */ - pDevice->null_device.priorRunTime += ma_timer_get_time_in_seconds(&pDevice->null_device.timer); - ma_timer_init(&pDevice->null_device.timer); - - /* We're done. */ - pDevice->null_device.operationResult = MA_SUCCESS; - ma_event_signal(&pDevice->null_device.operationCompletionEvent); - ma_semaphore_release(&pDevice->null_device.operationSemaphore); - continue; - } - - /* Killing the device means we need to get out of this loop so that this thread can terminate. */ - if (operation == MA_DEVICE_OP_KILL__NULL) { - pDevice->null_device.operationResult = MA_SUCCESS; - ma_event_signal(&pDevice->null_device.operationCompletionEvent); - ma_semaphore_release(&pDevice->null_device.operationSemaphore); - break; - } - - /* Getting a signal on a "none" operation probably means an error. Return invalid operation. */ - if (operation == MA_DEVICE_OP_NONE__NULL) { - MA_ASSERT(MA_FALSE); /* <-- Trigger this in debug mode to ensure developers are aware they're doing something wrong (or there's a bug in a miniaudio). */ - pDevice->null_device.operationResult = MA_INVALID_OPERATION; - ma_event_signal(&pDevice->null_device.operationCompletionEvent); - ma_semaphore_release(&pDevice->null_device.operationSemaphore); - continue; /* Continue the loop. Don't terminate. */ - } - } - - return (ma_thread_result)0; -} - -static ma_result ma_device_do_operation__null(ma_device* pDevice, ma_uint32 operation) -{ - ma_result result; - - /* - TODO: Need to review this and consider just using mutual exclusion. I think the original motivation - for this was to just post the event to a queue and return immediately, but that has since changed - and now this function is synchronous. I think this can be simplified to just use a mutex. - */ - - /* - The first thing to do is wait for an operation slot to become available. We only have a single slot for this, but we could extend this later - to support queuing of operations. - */ - result = ma_semaphore_wait(&pDevice->null_device.operationSemaphore); - if (result != MA_SUCCESS) { - return result; /* Failed to wait for the event. */ - } - - /* - When we get here it means the background thread is not referencing the operation code and it can be changed. After changing this we need to - signal an event to the worker thread to let it know that it can start work. - */ - pDevice->null_device.operation = operation; - - /* Once the operation code has been set, the worker thread can start work. */ - if (ma_event_signal(&pDevice->null_device.operationEvent) != MA_SUCCESS) { - return MA_ERROR; - } - - /* We want everything to be synchronous so we're going to wait for the worker thread to complete it's operation. */ - if (ma_event_wait(&pDevice->null_device.operationCompletionEvent) != MA_SUCCESS) { - return MA_ERROR; - } - - return pDevice->null_device.operationResult; -} - -static ma_uint64 ma_device_get_total_run_time_in_frames__null(ma_device* pDevice) -{ - ma_uint32 internalSampleRate; - if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) { - internalSampleRate = pDevice->capture.internalSampleRate; - } else { - internalSampleRate = pDevice->playback.internalSampleRate; - } - - return (ma_uint64)((pDevice->null_device.priorRunTime + ma_timer_get_time_in_seconds(&pDevice->null_device.timer)) * internalSampleRate); -} - -static ma_result ma_context_enumerate_devices__null(ma_context* pContext, ma_enum_devices_callback_proc callback, void* pUserData) -{ - ma_bool32 cbResult = MA_TRUE; - - MA_ASSERT(pContext != NULL); - MA_ASSERT(callback != NULL); - - /* Playback. */ - if (cbResult) { - ma_device_info deviceInfo; - MA_ZERO_OBJECT(&deviceInfo); - ma_strncpy_s(deviceInfo.name, sizeof(deviceInfo.name), "NULL Playback Device", (size_t)-1); - deviceInfo.isDefault = MA_TRUE; /* Only one playback and capture device for the null backend, so might as well mark as default. */ - cbResult = callback(pContext, ma_device_type_playback, &deviceInfo, pUserData); - } - - /* Capture. */ - if (cbResult) { - ma_device_info deviceInfo; - MA_ZERO_OBJECT(&deviceInfo); - ma_strncpy_s(deviceInfo.name, sizeof(deviceInfo.name), "NULL Capture Device", (size_t)-1); - deviceInfo.isDefault = MA_TRUE; /* Only one playback and capture device for the null backend, so might as well mark as default. */ - cbResult = callback(pContext, ma_device_type_capture, &deviceInfo, pUserData); - } - - (void)cbResult; /* Silence a static analysis warning. */ - - return MA_SUCCESS; -} - -static ma_result ma_context_get_device_info__null(ma_context* pContext, ma_device_type deviceType, const ma_device_id* pDeviceID, ma_device_info* pDeviceInfo) -{ - MA_ASSERT(pContext != NULL); - - if (pDeviceID != NULL && pDeviceID->nullbackend != 0) { - return MA_NO_DEVICE; /* Don't know the device. */ - } - - /* Name / Description */ - if (deviceType == ma_device_type_playback) { - ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), "NULL Playback Device", (size_t)-1); - } else { - ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), "NULL Capture Device", (size_t)-1); - } - - pDeviceInfo->isDefault = MA_TRUE; /* Only one playback and capture device for the null backend, so might as well mark as default. */ - - /* Support everything on the null backend. */ - pDeviceInfo->nativeDataFormats[0].format = ma_format_unknown; - pDeviceInfo->nativeDataFormats[0].channels = 0; - pDeviceInfo->nativeDataFormats[0].sampleRate = 0; - pDeviceInfo->nativeDataFormats[0].flags = 0; - pDeviceInfo->nativeDataFormatCount = 1; - - (void)pContext; - return MA_SUCCESS; -} - - -static ma_result ma_device_uninit__null(ma_device* pDevice) -{ - MA_ASSERT(pDevice != NULL); - - /* Keep it clean and wait for the device thread to finish before returning. */ - ma_device_do_operation__null(pDevice, MA_DEVICE_OP_KILL__NULL); - - /* Wait for the thread to finish before continuing. */ - ma_thread_wait(&pDevice->null_device.deviceThread); - - /* At this point the loop in the device thread is as good as terminated so we can uninitialize our events. */ - ma_semaphore_uninit(&pDevice->null_device.operationSemaphore); - ma_event_uninit(&pDevice->null_device.operationCompletionEvent); - ma_event_uninit(&pDevice->null_device.operationEvent); - - return MA_SUCCESS; -} - -static ma_result ma_device_init__null(ma_device* pDevice, const ma_device_config* pConfig, ma_device_descriptor* pDescriptorPlayback, ma_device_descriptor* pDescriptorCapture) -{ - ma_result result; - - MA_ASSERT(pDevice != NULL); - - MA_ZERO_OBJECT(&pDevice->null_device); - - if (pConfig->deviceType == ma_device_type_loopback) { - return MA_DEVICE_TYPE_NOT_SUPPORTED; - } - - /* The null backend supports everything exactly as we specify it. */ - if (pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) { - pDescriptorCapture->format = (pDescriptorCapture->format != ma_format_unknown) ? pDescriptorCapture->format : MA_DEFAULT_FORMAT; - pDescriptorCapture->channels = (pDescriptorCapture->channels != 0) ? pDescriptorCapture->channels : MA_DEFAULT_CHANNELS; - pDescriptorCapture->sampleRate = (pDescriptorCapture->sampleRate != 0) ? pDescriptorCapture->sampleRate : MA_DEFAULT_SAMPLE_RATE; - - if (pDescriptorCapture->channelMap[0] == MA_CHANNEL_NONE) { - ma_channel_map_init_standard(ma_standard_channel_map_default, pDescriptorCapture->channelMap, ma_countof(pDescriptorCapture->channelMap), pDescriptorCapture->channels); - } - - pDescriptorCapture->periodSizeInFrames = ma_calculate_buffer_size_in_frames_from_descriptor(pDescriptorCapture, pDescriptorCapture->sampleRate, pConfig->performanceProfile); - } - - if (pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) { - pDescriptorPlayback->format = (pDescriptorPlayback->format != ma_format_unknown) ? pDescriptorPlayback->format : MA_DEFAULT_FORMAT; - pDescriptorPlayback->channels = (pDescriptorPlayback->channels != 0) ? pDescriptorPlayback->channels : MA_DEFAULT_CHANNELS; - pDescriptorPlayback->sampleRate = (pDescriptorPlayback->sampleRate != 0) ? pDescriptorPlayback->sampleRate : MA_DEFAULT_SAMPLE_RATE; - - if (pDescriptorPlayback->channelMap[0] == MA_CHANNEL_NONE) { - ma_channel_map_init_standard(ma_standard_channel_map_default, pDescriptorPlayback->channelMap, ma_countof(pDescriptorCapture->channelMap), pDescriptorPlayback->channels); - } - - pDescriptorPlayback->periodSizeInFrames = ma_calculate_buffer_size_in_frames_from_descriptor(pDescriptorPlayback, pDescriptorPlayback->sampleRate, pConfig->performanceProfile); - } - - /* - In order to get timing right, we need to create a thread that does nothing but keeps track of the timer. This timer is started when the - first period is "written" to it, and then stopped in ma_device_stop__null(). - */ - result = ma_event_init(&pDevice->null_device.operationEvent); - if (result != MA_SUCCESS) { - return result; - } - - result = ma_event_init(&pDevice->null_device.operationCompletionEvent); - if (result != MA_SUCCESS) { - return result; - } - - result = ma_semaphore_init(1, &pDevice->null_device.operationSemaphore); /* <-- It's important that the initial value is set to 1. */ - if (result != MA_SUCCESS) { - return result; - } - - result = ma_thread_create(&pDevice->null_device.deviceThread, pDevice->pContext->threadPriority, 0, ma_device_thread__null, pDevice, &pDevice->pContext->allocationCallbacks); - if (result != MA_SUCCESS) { - return result; - } - - return MA_SUCCESS; -} - -static ma_result ma_device_start__null(ma_device* pDevice) -{ - MA_ASSERT(pDevice != NULL); - - ma_device_do_operation__null(pDevice, MA_DEVICE_OP_START__NULL); - - ma_atomic_bool32_set(&pDevice->null_device.isStarted, MA_TRUE); - return MA_SUCCESS; -} - -static ma_result ma_device_stop__null(ma_device* pDevice) -{ - MA_ASSERT(pDevice != NULL); - - ma_device_do_operation__null(pDevice, MA_DEVICE_OP_SUSPEND__NULL); - - ma_atomic_bool32_set(&pDevice->null_device.isStarted, MA_FALSE); - return MA_SUCCESS; -} - -static ma_bool32 ma_device_is_started__null(ma_device* pDevice) -{ - MA_ASSERT(pDevice != NULL); - - return ma_atomic_bool32_get(&pDevice->null_device.isStarted); -} - -static ma_result ma_device_write__null(ma_device* pDevice, const void* pPCMFrames, ma_uint32 frameCount, ma_uint32* pFramesWritten) -{ - ma_result result = MA_SUCCESS; - ma_uint32 totalPCMFramesProcessed; - ma_bool32 wasStartedOnEntry; - - if (pFramesWritten != NULL) { - *pFramesWritten = 0; - } - - wasStartedOnEntry = ma_device_is_started__null(pDevice); - - /* Keep going until everything has been read. */ - totalPCMFramesProcessed = 0; - while (totalPCMFramesProcessed < frameCount) { - ma_uint64 targetFrame; - - /* If there are any frames remaining in the current period, consume those first. */ - if (pDevice->null_device.currentPeriodFramesRemainingPlayback > 0) { - ma_uint32 framesRemaining = (frameCount - totalPCMFramesProcessed); - ma_uint32 framesToProcess = pDevice->null_device.currentPeriodFramesRemainingPlayback; - if (framesToProcess > framesRemaining) { - framesToProcess = framesRemaining; - } - - /* We don't actually do anything with pPCMFrames, so just mark it as unused to prevent a warning. */ - (void)pPCMFrames; - - pDevice->null_device.currentPeriodFramesRemainingPlayback -= framesToProcess; - totalPCMFramesProcessed += framesToProcess; - } - - /* If we've consumed the current period we'll need to mark it as such an ensure the device is started if it's not already. */ - if (pDevice->null_device.currentPeriodFramesRemainingPlayback == 0) { - pDevice->null_device.currentPeriodFramesRemainingPlayback = 0; - - if (!ma_device_is_started__null(pDevice) && !wasStartedOnEntry) { - result = ma_device_start__null(pDevice); - if (result != MA_SUCCESS) { - break; - } - } - } - - /* If we've consumed the whole buffer we can return now. */ - MA_ASSERT(totalPCMFramesProcessed <= frameCount); - if (totalPCMFramesProcessed == frameCount) { - break; - } - - /* Getting here means we've still got more frames to consume, we but need to wait for it to become available. */ - targetFrame = pDevice->null_device.lastProcessedFramePlayback; - for (;;) { - ma_uint64 currentFrame; - - /* Stop waiting if the device has been stopped. */ - if (!ma_device_is_started__null(pDevice)) { - break; - } - - currentFrame = ma_device_get_total_run_time_in_frames__null(pDevice); - if (currentFrame >= targetFrame) { - break; - } - - /* Getting here means we haven't yet reached the target sample, so continue waiting. */ - ma_sleep(10); - } - - pDevice->null_device.lastProcessedFramePlayback += pDevice->playback.internalPeriodSizeInFrames; - pDevice->null_device.currentPeriodFramesRemainingPlayback = pDevice->playback.internalPeriodSizeInFrames; - } - - if (pFramesWritten != NULL) { - *pFramesWritten = totalPCMFramesProcessed; - } - - return result; -} - -static ma_result ma_device_read__null(ma_device* pDevice, void* pPCMFrames, ma_uint32 frameCount, ma_uint32* pFramesRead) -{ - ma_result result = MA_SUCCESS; - ma_uint32 totalPCMFramesProcessed; - - if (pFramesRead != NULL) { - *pFramesRead = 0; - } - - /* Keep going until everything has been read. */ - totalPCMFramesProcessed = 0; - while (totalPCMFramesProcessed < frameCount) { - ma_uint64 targetFrame; - - /* If there are any frames remaining in the current period, consume those first. */ - if (pDevice->null_device.currentPeriodFramesRemainingCapture > 0) { - ma_uint32 bpf = ma_get_bytes_per_frame(pDevice->capture.internalFormat, pDevice->capture.internalChannels); - ma_uint32 framesRemaining = (frameCount - totalPCMFramesProcessed); - ma_uint32 framesToProcess = pDevice->null_device.currentPeriodFramesRemainingCapture; - if (framesToProcess > framesRemaining) { - framesToProcess = framesRemaining; - } - - /* We need to ensure the output buffer is zeroed. */ - MA_ZERO_MEMORY(ma_offset_ptr(pPCMFrames, totalPCMFramesProcessed*bpf), framesToProcess*bpf); - - pDevice->null_device.currentPeriodFramesRemainingCapture -= framesToProcess; - totalPCMFramesProcessed += framesToProcess; - } - - /* If we've consumed the current period we'll need to mark it as such an ensure the device is started if it's not already. */ - if (pDevice->null_device.currentPeriodFramesRemainingCapture == 0) { - pDevice->null_device.currentPeriodFramesRemainingCapture = 0; - } - - /* If we've consumed the whole buffer we can return now. */ - MA_ASSERT(totalPCMFramesProcessed <= frameCount); - if (totalPCMFramesProcessed == frameCount) { - break; - } - - /* Getting here means we've still got more frames to consume, we but need to wait for it to become available. */ - targetFrame = pDevice->null_device.lastProcessedFrameCapture + pDevice->capture.internalPeriodSizeInFrames; - for (;;) { - ma_uint64 currentFrame; - - /* Stop waiting if the device has been stopped. */ - if (!ma_device_is_started__null(pDevice)) { - break; - } - - currentFrame = ma_device_get_total_run_time_in_frames__null(pDevice); - if (currentFrame >= targetFrame) { - break; - } - - /* Getting here means we haven't yet reached the target sample, so continue waiting. */ - ma_sleep(10); - } - - pDevice->null_device.lastProcessedFrameCapture += pDevice->capture.internalPeriodSizeInFrames; - pDevice->null_device.currentPeriodFramesRemainingCapture = pDevice->capture.internalPeriodSizeInFrames; - } - - if (pFramesRead != NULL) { - *pFramesRead = totalPCMFramesProcessed; - } - - return result; -} - -static ma_result ma_context_uninit__null(ma_context* pContext) -{ - MA_ASSERT(pContext != NULL); - MA_ASSERT(pContext->backend == ma_backend_null); - - (void)pContext; - return MA_SUCCESS; -} - -static ma_result ma_context_init__null(ma_context* pContext, const ma_context_config* pConfig, ma_backend_callbacks* pCallbacks) -{ - MA_ASSERT(pContext != NULL); - - (void)pConfig; - (void)pContext; - - pCallbacks->onContextInit = ma_context_init__null; - pCallbacks->onContextUninit = ma_context_uninit__null; - pCallbacks->onContextEnumerateDevices = ma_context_enumerate_devices__null; - pCallbacks->onContextGetDeviceInfo = ma_context_get_device_info__null; - pCallbacks->onDeviceInit = ma_device_init__null; - pCallbacks->onDeviceUninit = ma_device_uninit__null; - pCallbacks->onDeviceStart = ma_device_start__null; - pCallbacks->onDeviceStop = ma_device_stop__null; - pCallbacks->onDeviceRead = ma_device_read__null; - pCallbacks->onDeviceWrite = ma_device_write__null; - pCallbacks->onDeviceDataLoop = NULL; /* Our backend is asynchronous with a blocking read-write API which means we can get miniaudio to deal with the audio thread. */ - - /* The null backend always works. */ - return MA_SUCCESS; -} -#endif - - - -/******************************************************************************* - -WIN32 COMMON - -*******************************************************************************/ -#if defined(MA_WIN32) -#if defined(MA_WIN32_DESKTOP) || defined(MA_WIN32_GDK) - #define ma_CoInitializeEx(pContext, pvReserved, dwCoInit) ((pContext->win32.CoInitializeEx) ? ((MA_PFN_CoInitializeEx)pContext->win32.CoInitializeEx)(pvReserved, dwCoInit) : ((MA_PFN_CoInitialize)pContext->win32.CoInitialize)(pvReserved)) - #define ma_CoUninitialize(pContext) ((MA_PFN_CoUninitialize)pContext->win32.CoUninitialize)() - #define ma_CoCreateInstance(pContext, rclsid, pUnkOuter, dwClsContext, riid, ppv) ((MA_PFN_CoCreateInstance)pContext->win32.CoCreateInstance)(rclsid, pUnkOuter, dwClsContext, riid, ppv) - #define ma_CoTaskMemFree(pContext, pv) ((MA_PFN_CoTaskMemFree)pContext->win32.CoTaskMemFree)(pv) - #define ma_PropVariantClear(pContext, pvar) ((MA_PFN_PropVariantClear)pContext->win32.PropVariantClear)(pvar) -#else - #define ma_CoInitializeEx(pContext, pvReserved, dwCoInit) CoInitializeEx(pvReserved, dwCoInit) - #define ma_CoUninitialize(pContext) CoUninitialize() - #define ma_CoCreateInstance(pContext, rclsid, pUnkOuter, dwClsContext, riid, ppv) CoCreateInstance(rclsid, pUnkOuter, dwClsContext, riid, ppv) - #define ma_CoTaskMemFree(pContext, pv) CoTaskMemFree(pv) - #define ma_PropVariantClear(pContext, pvar) PropVariantClear(pvar) -#endif - -#if !defined(MAXULONG_PTR) && !defined(__WATCOMC__) -typedef size_t DWORD_PTR; -#endif - -#if !defined(WAVE_FORMAT_1M08) -#define WAVE_FORMAT_1M08 0x00000001 -#define WAVE_FORMAT_1S08 0x00000002 -#define WAVE_FORMAT_1M16 0x00000004 -#define WAVE_FORMAT_1S16 0x00000008 -#define WAVE_FORMAT_2M08 0x00000010 -#define WAVE_FORMAT_2S08 0x00000020 -#define WAVE_FORMAT_2M16 0x00000040 -#define WAVE_FORMAT_2S16 0x00000080 -#define WAVE_FORMAT_4M08 0x00000100 -#define WAVE_FORMAT_4S08 0x00000200 -#define WAVE_FORMAT_4M16 0x00000400 -#define WAVE_FORMAT_4S16 0x00000800 -#endif - -#if !defined(WAVE_FORMAT_44M08) -#define WAVE_FORMAT_44M08 0x00000100 -#define WAVE_FORMAT_44S08 0x00000200 -#define WAVE_FORMAT_44M16 0x00000400 -#define WAVE_FORMAT_44S16 0x00000800 -#define WAVE_FORMAT_48M08 0x00001000 -#define WAVE_FORMAT_48S08 0x00002000 -#define WAVE_FORMAT_48M16 0x00004000 -#define WAVE_FORMAT_48S16 0x00008000 -#define WAVE_FORMAT_96M08 0x00010000 -#define WAVE_FORMAT_96S08 0x00020000 -#define WAVE_FORMAT_96M16 0x00040000 -#define WAVE_FORMAT_96S16 0x00080000 -#endif - -#ifndef SPEAKER_FRONT_LEFT -#define SPEAKER_FRONT_LEFT 0x1 -#define SPEAKER_FRONT_RIGHT 0x2 -#define SPEAKER_FRONT_CENTER 0x4 -#define SPEAKER_LOW_FREQUENCY 0x8 -#define SPEAKER_BACK_LEFT 0x10 -#define SPEAKER_BACK_RIGHT 0x20 -#define SPEAKER_FRONT_LEFT_OF_CENTER 0x40 -#define SPEAKER_FRONT_RIGHT_OF_CENTER 0x80 -#define SPEAKER_BACK_CENTER 0x100 -#define SPEAKER_SIDE_LEFT 0x200 -#define SPEAKER_SIDE_RIGHT 0x400 -#define SPEAKER_TOP_CENTER 0x800 -#define SPEAKER_TOP_FRONT_LEFT 0x1000 -#define SPEAKER_TOP_FRONT_CENTER 0x2000 -#define SPEAKER_TOP_FRONT_RIGHT 0x4000 -#define SPEAKER_TOP_BACK_LEFT 0x8000 -#define SPEAKER_TOP_BACK_CENTER 0x10000 -#define SPEAKER_TOP_BACK_RIGHT 0x20000 -#endif - -/* -Implement our own version of MA_WAVEFORMATEXTENSIBLE so we can avoid a header. Be careful with this -because MA_WAVEFORMATEX has an extra two bytes over standard WAVEFORMATEX due to padding. The -standard version uses tight packing, but for compiler compatibility we're not doing that with ours. -*/ -typedef struct -{ - WORD wFormatTag; - WORD nChannels; - DWORD nSamplesPerSec; - DWORD nAvgBytesPerSec; - WORD nBlockAlign; - WORD wBitsPerSample; - WORD cbSize; -} MA_WAVEFORMATEX; - -typedef struct -{ - WORD wFormatTag; - WORD nChannels; - DWORD nSamplesPerSec; - DWORD nAvgBytesPerSec; - WORD nBlockAlign; - WORD wBitsPerSample; - WORD cbSize; - union - { - WORD wValidBitsPerSample; - WORD wSamplesPerBlock; - WORD wReserved; - } Samples; - DWORD dwChannelMask; - GUID SubFormat; -} MA_WAVEFORMATEXTENSIBLE; - - - -#ifndef WAVE_FORMAT_EXTENSIBLE -#define WAVE_FORMAT_EXTENSIBLE 0xFFFE -#endif - -#ifndef WAVE_FORMAT_PCM -#define WAVE_FORMAT_PCM 1 -#endif - -#ifndef WAVE_FORMAT_IEEE_FLOAT -#define WAVE_FORMAT_IEEE_FLOAT 0x0003 -#endif - -/* Converts an individual Win32-style channel identifier (SPEAKER_FRONT_LEFT, etc.) to miniaudio. */ -static ma_uint8 ma_channel_id_to_ma__win32(DWORD id) -{ - switch (id) - { - case SPEAKER_FRONT_LEFT: return MA_CHANNEL_FRONT_LEFT; - case SPEAKER_FRONT_RIGHT: return MA_CHANNEL_FRONT_RIGHT; - case SPEAKER_FRONT_CENTER: return MA_CHANNEL_FRONT_CENTER; - case SPEAKER_LOW_FREQUENCY: return MA_CHANNEL_LFE; - case SPEAKER_BACK_LEFT: return MA_CHANNEL_BACK_LEFT; - case SPEAKER_BACK_RIGHT: return MA_CHANNEL_BACK_RIGHT; - case SPEAKER_FRONT_LEFT_OF_CENTER: return MA_CHANNEL_FRONT_LEFT_CENTER; - case SPEAKER_FRONT_RIGHT_OF_CENTER: return MA_CHANNEL_FRONT_RIGHT_CENTER; - case SPEAKER_BACK_CENTER: return MA_CHANNEL_BACK_CENTER; - case SPEAKER_SIDE_LEFT: return MA_CHANNEL_SIDE_LEFT; - case SPEAKER_SIDE_RIGHT: return MA_CHANNEL_SIDE_RIGHT; - case SPEAKER_TOP_CENTER: return MA_CHANNEL_TOP_CENTER; - case SPEAKER_TOP_FRONT_LEFT: return MA_CHANNEL_TOP_FRONT_LEFT; - case SPEAKER_TOP_FRONT_CENTER: return MA_CHANNEL_TOP_FRONT_CENTER; - case SPEAKER_TOP_FRONT_RIGHT: return MA_CHANNEL_TOP_FRONT_RIGHT; - case SPEAKER_TOP_BACK_LEFT: return MA_CHANNEL_TOP_BACK_LEFT; - case SPEAKER_TOP_BACK_CENTER: return MA_CHANNEL_TOP_BACK_CENTER; - case SPEAKER_TOP_BACK_RIGHT: return MA_CHANNEL_TOP_BACK_RIGHT; - default: return 0; - } -} - -/* Converts an individual miniaudio channel identifier (MA_CHANNEL_FRONT_LEFT, etc.) to Win32-style. */ -static DWORD ma_channel_id_to_win32(DWORD id) -{ - switch (id) - { - case MA_CHANNEL_MONO: return SPEAKER_FRONT_CENTER; - case MA_CHANNEL_FRONT_LEFT: return SPEAKER_FRONT_LEFT; - case MA_CHANNEL_FRONT_RIGHT: return SPEAKER_FRONT_RIGHT; - case MA_CHANNEL_FRONT_CENTER: return SPEAKER_FRONT_CENTER; - case MA_CHANNEL_LFE: return SPEAKER_LOW_FREQUENCY; - case MA_CHANNEL_BACK_LEFT: return SPEAKER_BACK_LEFT; - case MA_CHANNEL_BACK_RIGHT: return SPEAKER_BACK_RIGHT; - case MA_CHANNEL_FRONT_LEFT_CENTER: return SPEAKER_FRONT_LEFT_OF_CENTER; - case MA_CHANNEL_FRONT_RIGHT_CENTER: return SPEAKER_FRONT_RIGHT_OF_CENTER; - case MA_CHANNEL_BACK_CENTER: return SPEAKER_BACK_CENTER; - case MA_CHANNEL_SIDE_LEFT: return SPEAKER_SIDE_LEFT; - case MA_CHANNEL_SIDE_RIGHT: return SPEAKER_SIDE_RIGHT; - case MA_CHANNEL_TOP_CENTER: return SPEAKER_TOP_CENTER; - case MA_CHANNEL_TOP_FRONT_LEFT: return SPEAKER_TOP_FRONT_LEFT; - case MA_CHANNEL_TOP_FRONT_CENTER: return SPEAKER_TOP_FRONT_CENTER; - case MA_CHANNEL_TOP_FRONT_RIGHT: return SPEAKER_TOP_FRONT_RIGHT; - case MA_CHANNEL_TOP_BACK_LEFT: return SPEAKER_TOP_BACK_LEFT; - case MA_CHANNEL_TOP_BACK_CENTER: return SPEAKER_TOP_BACK_CENTER; - case MA_CHANNEL_TOP_BACK_RIGHT: return SPEAKER_TOP_BACK_RIGHT; - default: return 0; - } -} - -/* Converts a channel mapping to a Win32-style channel mask. */ -static DWORD ma_channel_map_to_channel_mask__win32(const ma_channel* pChannelMap, ma_uint32 channels) -{ - DWORD dwChannelMask = 0; - ma_uint32 iChannel; - - for (iChannel = 0; iChannel < channels; ++iChannel) { - dwChannelMask |= ma_channel_id_to_win32(pChannelMap[iChannel]); - } - - return dwChannelMask; -} - -/* Converts a Win32-style channel mask to a miniaudio channel map. */ -static void ma_channel_mask_to_channel_map__win32(DWORD dwChannelMask, ma_uint32 channels, ma_channel* pChannelMap) -{ - /* If the channel mask is set to 0, just assume a default Win32 channel map. */ - if (dwChannelMask == 0) { - ma_channel_map_init_standard(ma_standard_channel_map_microsoft, pChannelMap, channels, channels); - } else { - if (channels == 1 && (dwChannelMask & SPEAKER_FRONT_CENTER) != 0) { - pChannelMap[0] = MA_CHANNEL_MONO; - } else { - /* Just iterate over each bit. */ - ma_uint32 iChannel = 0; - ma_uint32 iBit; - - for (iBit = 0; iBit < 32 && iChannel < channels; ++iBit) { - DWORD bitValue = (dwChannelMask & (1UL << iBit)); - if (bitValue != 0) { - /* The bit is set. */ - pChannelMap[iChannel] = ma_channel_id_to_ma__win32(bitValue); - iChannel += 1; - } - } - } - } -} - -#ifdef __cplusplus -static ma_bool32 ma_is_guid_equal(const void* a, const void* b) -{ - return IsEqualGUID(*(const GUID*)a, *(const GUID*)b); -} -#else -#define ma_is_guid_equal(a, b) IsEqualGUID((const GUID*)a, (const GUID*)b) -#endif - -static MA_INLINE ma_bool32 ma_is_guid_null(const void* guid) -{ - static GUID nullguid = {0x00000000, 0x0000, 0x0000, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}; - return ma_is_guid_equal(guid, &nullguid); -} - -static ma_format ma_format_from_WAVEFORMATEX(const MA_WAVEFORMATEX* pWF) -{ - MA_ASSERT(pWF != NULL); - - if (pWF->wFormatTag == WAVE_FORMAT_EXTENSIBLE) { - const MA_WAVEFORMATEXTENSIBLE* pWFEX = (const MA_WAVEFORMATEXTENSIBLE*)pWF; - if (ma_is_guid_equal(&pWFEX->SubFormat, &MA_GUID_KSDATAFORMAT_SUBTYPE_PCM)) { - if (pWFEX->Samples.wValidBitsPerSample == 32) { - return ma_format_s32; - } - if (pWFEX->Samples.wValidBitsPerSample == 24) { - if (pWFEX->wBitsPerSample == 32) { - return ma_format_s32; - } - if (pWFEX->wBitsPerSample == 24) { - return ma_format_s24; - } - } - if (pWFEX->Samples.wValidBitsPerSample == 16) { - return ma_format_s16; - } - if (pWFEX->Samples.wValidBitsPerSample == 8) { - return ma_format_u8; - } - } - if (ma_is_guid_equal(&pWFEX->SubFormat, &MA_GUID_KSDATAFORMAT_SUBTYPE_IEEE_FLOAT)) { - if (pWFEX->Samples.wValidBitsPerSample == 32) { - return ma_format_f32; - } - /* - if (pWFEX->Samples.wValidBitsPerSample == 64) { - return ma_format_f64; - } - */ - } - } else { - if (pWF->wFormatTag == WAVE_FORMAT_PCM) { - if (pWF->wBitsPerSample == 32) { - return ma_format_s32; - } - if (pWF->wBitsPerSample == 24) { - return ma_format_s24; - } - if (pWF->wBitsPerSample == 16) { - return ma_format_s16; - } - if (pWF->wBitsPerSample == 8) { - return ma_format_u8; - } - } - if (pWF->wFormatTag == WAVE_FORMAT_IEEE_FLOAT) { - if (pWF->wBitsPerSample == 32) { - return ma_format_f32; - } - if (pWF->wBitsPerSample == 64) { - /*return ma_format_f64;*/ - } - } - } - - return ma_format_unknown; -} -#endif - - -/******************************************************************************* - -WASAPI Backend - -*******************************************************************************/ -#ifdef MA_HAS_WASAPI -#if 0 -#if defined(_MSC_VER) - #pragma warning(push) - #pragma warning(disable:4091) /* 'typedef ': ignored on left of '' when no variable is declared */ -#endif -#include -#include -#if defined(_MSC_VER) - #pragma warning(pop) -#endif -#endif /* 0 */ - -static ma_result ma_device_reroute__wasapi(ma_device* pDevice, ma_device_type deviceType); - -/* Some compilers don't define VerifyVersionInfoW. Need to write this ourselves. */ -#define MA_WIN32_WINNT_VISTA 0x0600 -#define MA_VER_MINORVERSION 0x01 -#define MA_VER_MAJORVERSION 0x02 -#define MA_VER_SERVICEPACKMAJOR 0x20 -#define MA_VER_GREATER_EQUAL 0x03 - -typedef struct { - DWORD dwOSVersionInfoSize; - DWORD dwMajorVersion; - DWORD dwMinorVersion; - DWORD dwBuildNumber; - DWORD dwPlatformId; - WCHAR szCSDVersion[128]; - WORD wServicePackMajor; - WORD wServicePackMinor; - WORD wSuiteMask; - BYTE wProductType; - BYTE wReserved; -} ma_OSVERSIONINFOEXW; - -typedef BOOL (WINAPI * ma_PFNVerifyVersionInfoW) (ma_OSVERSIONINFOEXW* lpVersionInfo, DWORD dwTypeMask, DWORDLONG dwlConditionMask); -typedef ULONGLONG (WINAPI * ma_PFNVerSetConditionMask)(ULONGLONG dwlConditionMask, DWORD dwTypeBitMask, BYTE dwConditionMask); - - -#ifndef PROPERTYKEY_DEFINED -#define PROPERTYKEY_DEFINED -#ifndef __WATCOMC__ -typedef struct -{ - GUID fmtid; - DWORD pid; -} PROPERTYKEY; -#endif -#endif - -/* Some compilers don't define PropVariantInit(). We just do this ourselves since it's just a memset(). */ -static MA_INLINE void ma_PropVariantInit(MA_PROPVARIANT* pProp) -{ - MA_ZERO_OBJECT(pProp); -} - - -static const PROPERTYKEY MA_PKEY_Device_FriendlyName = {{0xA45C254E, 0xDF1C, 0x4EFD, {0x80, 0x20, 0x67, 0xD1, 0x46, 0xA8, 0x50, 0xE0}}, 14}; -static const PROPERTYKEY MA_PKEY_AudioEngine_DeviceFormat = {{0xF19F064D, 0x82C, 0x4E27, {0xBC, 0x73, 0x68, 0x82, 0xA1, 0xBB, 0x8E, 0x4C}}, 0}; - -static const IID MA_IID_IUnknown = {0x00000000, 0x0000, 0x0000, {0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46}}; /* 00000000-0000-0000-C000-000000000046 */ -#if !defined(MA_WIN32_DESKTOP) && !defined(MA_WIN32_GDK) -static const IID MA_IID_IAgileObject = {0x94EA2B94, 0xE9CC, 0x49E0, {0xC0, 0xFF, 0xEE, 0x64, 0xCA, 0x8F, 0x5B, 0x90}}; /* 94EA2B94-E9CC-49E0-C0FF-EE64CA8F5B90 */ -#endif - -static const IID MA_IID_IAudioClient = {0x1CB9AD4C, 0xDBFA, 0x4C32, {0xB1, 0x78, 0xC2, 0xF5, 0x68, 0xA7, 0x03, 0xB2}}; /* 1CB9AD4C-DBFA-4C32-B178-C2F568A703B2 = __uuidof(IAudioClient) */ -static const IID MA_IID_IAudioClient2 = {0x726778CD, 0xF60A, 0x4EDA, {0x82, 0xDE, 0xE4, 0x76, 0x10, 0xCD, 0x78, 0xAA}}; /* 726778CD-F60A-4EDA-82DE-E47610CD78AA = __uuidof(IAudioClient2) */ -static const IID MA_IID_IAudioClient3 = {0x7ED4EE07, 0x8E67, 0x4CD4, {0x8C, 0x1A, 0x2B, 0x7A, 0x59, 0x87, 0xAD, 0x42}}; /* 7ED4EE07-8E67-4CD4-8C1A-2B7A5987AD42 = __uuidof(IAudioClient3) */ -static const IID MA_IID_IAudioRenderClient = {0xF294ACFC, 0x3146, 0x4483, {0xA7, 0xBF, 0xAD, 0xDC, 0xA7, 0xC2, 0x60, 0xE2}}; /* F294ACFC-3146-4483-A7BF-ADDCA7C260E2 = __uuidof(IAudioRenderClient) */ -static const IID MA_IID_IAudioCaptureClient = {0xC8ADBD64, 0xE71E, 0x48A0, {0xA4, 0xDE, 0x18, 0x5C, 0x39, 0x5C, 0xD3, 0x17}}; /* C8ADBD64-E71E-48A0-A4DE-185C395CD317 = __uuidof(IAudioCaptureClient) */ -static const IID MA_IID_IMMNotificationClient = {0x7991EEC9, 0x7E89, 0x4D85, {0x83, 0x90, 0x6C, 0x70, 0x3C, 0xEC, 0x60, 0xC0}}; /* 7991EEC9-7E89-4D85-8390-6C703CEC60C0 = __uuidof(IMMNotificationClient) */ -#if !defined(MA_WIN32_DESKTOP) && !defined(MA_WIN32_GDK) -static const IID MA_IID_DEVINTERFACE_AUDIO_RENDER = {0xE6327CAD, 0xDCEC, 0x4949, {0xAE, 0x8A, 0x99, 0x1E, 0x97, 0x6A, 0x79, 0xD2}}; /* E6327CAD-DCEC-4949-AE8A-991E976A79D2 */ -static const IID MA_IID_DEVINTERFACE_AUDIO_CAPTURE = {0x2EEF81BE, 0x33FA, 0x4800, {0x96, 0x70, 0x1C, 0xD4, 0x74, 0x97, 0x2C, 0x3F}}; /* 2EEF81BE-33FA-4800-9670-1CD474972C3F */ -static const IID MA_IID_IActivateAudioInterfaceCompletionHandler = {0x41D949AB, 0x9862, 0x444A, {0x80, 0xF6, 0xC2, 0x61, 0x33, 0x4D, 0xA5, 0xEB}}; /* 41D949AB-9862-444A-80F6-C261334DA5EB */ -#endif - -static const IID MA_CLSID_MMDeviceEnumerator = {0xBCDE0395, 0xE52F, 0x467C, {0x8E, 0x3D, 0xC4, 0x57, 0x92, 0x91, 0x69, 0x2E}}; /* BCDE0395-E52F-467C-8E3D-C4579291692E = __uuidof(MMDeviceEnumerator) */ -static const IID MA_IID_IMMDeviceEnumerator = {0xA95664D2, 0x9614, 0x4F35, {0xA7, 0x46, 0xDE, 0x8D, 0xB6, 0x36, 0x17, 0xE6}}; /* A95664D2-9614-4F35-A746-DE8DB63617E6 = __uuidof(IMMDeviceEnumerator) */ - -#if defined(MA_WIN32_DESKTOP) || defined(MA_WIN32_GDK) -#define MA_MM_DEVICE_STATE_ACTIVE 1 -#define MA_MM_DEVICE_STATE_DISABLED 2 -#define MA_MM_DEVICE_STATE_NOTPRESENT 4 -#define MA_MM_DEVICE_STATE_UNPLUGGED 8 - -typedef struct ma_IMMDeviceEnumerator ma_IMMDeviceEnumerator; -typedef struct ma_IMMDeviceCollection ma_IMMDeviceCollection; -typedef struct ma_IMMDevice ma_IMMDevice; -#else -typedef struct ma_IActivateAudioInterfaceCompletionHandler ma_IActivateAudioInterfaceCompletionHandler; -typedef struct ma_IActivateAudioInterfaceAsyncOperation ma_IActivateAudioInterfaceAsyncOperation; -#endif -typedef struct ma_IPropertyStore ma_IPropertyStore; -typedef struct ma_IAudioClient ma_IAudioClient; -typedef struct ma_IAudioClient2 ma_IAudioClient2; -typedef struct ma_IAudioClient3 ma_IAudioClient3; -typedef struct ma_IAudioRenderClient ma_IAudioRenderClient; -typedef struct ma_IAudioCaptureClient ma_IAudioCaptureClient; - -typedef ma_int64 MA_REFERENCE_TIME; - -#define MA_AUDCLNT_STREAMFLAGS_CROSSPROCESS 0x00010000 -#define MA_AUDCLNT_STREAMFLAGS_LOOPBACK 0x00020000 -#define MA_AUDCLNT_STREAMFLAGS_EVENTCALLBACK 0x00040000 -#define MA_AUDCLNT_STREAMFLAGS_NOPERSIST 0x00080000 -#define MA_AUDCLNT_STREAMFLAGS_RATEADJUST 0x00100000 -#define MA_AUDCLNT_STREAMFLAGS_SRC_DEFAULT_QUALITY 0x08000000 -#define MA_AUDCLNT_STREAMFLAGS_AUTOCONVERTPCM 0x80000000 -#define MA_AUDCLNT_SESSIONFLAGS_EXPIREWHENUNOWNED 0x10000000 -#define MA_AUDCLNT_SESSIONFLAGS_DISPLAY_HIDE 0x20000000 -#define MA_AUDCLNT_SESSIONFLAGS_DISPLAY_HIDEWHENEXPIRED 0x40000000 - -/* Buffer flags. */ -#define MA_AUDCLNT_BUFFERFLAGS_DATA_DISCONTINUITY 1 -#define MA_AUDCLNT_BUFFERFLAGS_SILENT 2 -#define MA_AUDCLNT_BUFFERFLAGS_TIMESTAMP_ERROR 4 - -typedef enum -{ - ma_eRender = 0, - ma_eCapture = 1, - ma_eAll = 2 -} ma_EDataFlow; - -typedef enum -{ - ma_eConsole = 0, - ma_eMultimedia = 1, - ma_eCommunications = 2 -} ma_ERole; - -typedef enum -{ - MA_AUDCLNT_SHAREMODE_SHARED, - MA_AUDCLNT_SHAREMODE_EXCLUSIVE -} MA_AUDCLNT_SHAREMODE; - -typedef enum -{ - MA_AudioCategory_Other = 0 /* <-- miniaudio is only caring about Other. */ -} MA_AUDIO_STREAM_CATEGORY; - -typedef struct -{ - ma_uint32 cbSize; - BOOL bIsOffload; - MA_AUDIO_STREAM_CATEGORY eCategory; -} ma_AudioClientProperties; - -/* IUnknown */ -typedef struct -{ - /* IUnknown */ - HRESULT (STDMETHODCALLTYPE * QueryInterface)(ma_IUnknown* pThis, const IID* const riid, void** ppObject); - ULONG (STDMETHODCALLTYPE * AddRef) (ma_IUnknown* pThis); - ULONG (STDMETHODCALLTYPE * Release) (ma_IUnknown* pThis); -} ma_IUnknownVtbl; -struct ma_IUnknown -{ - ma_IUnknownVtbl* lpVtbl; -}; -static MA_INLINE HRESULT ma_IUnknown_QueryInterface(ma_IUnknown* pThis, const IID* const riid, void** ppObject) { return pThis->lpVtbl->QueryInterface(pThis, riid, ppObject); } -static MA_INLINE ULONG ma_IUnknown_AddRef(ma_IUnknown* pThis) { return pThis->lpVtbl->AddRef(pThis); } -static MA_INLINE ULONG ma_IUnknown_Release(ma_IUnknown* pThis) { return pThis->lpVtbl->Release(pThis); } - -#if defined(MA_WIN32_DESKTOP) || defined(MA_WIN32_GDK) - /* IMMNotificationClient */ - typedef struct - { - /* IUnknown */ - HRESULT (STDMETHODCALLTYPE * QueryInterface)(ma_IMMNotificationClient* pThis, const IID* const riid, void** ppObject); - ULONG (STDMETHODCALLTYPE * AddRef) (ma_IMMNotificationClient* pThis); - ULONG (STDMETHODCALLTYPE * Release) (ma_IMMNotificationClient* pThis); - - /* IMMNotificationClient */ - HRESULT (STDMETHODCALLTYPE * OnDeviceStateChanged) (ma_IMMNotificationClient* pThis, const WCHAR* pDeviceID, DWORD dwNewState); - HRESULT (STDMETHODCALLTYPE * OnDeviceAdded) (ma_IMMNotificationClient* pThis, const WCHAR* pDeviceID); - HRESULT (STDMETHODCALLTYPE * OnDeviceRemoved) (ma_IMMNotificationClient* pThis, const WCHAR* pDeviceID); - HRESULT (STDMETHODCALLTYPE * OnDefaultDeviceChanged)(ma_IMMNotificationClient* pThis, ma_EDataFlow dataFlow, ma_ERole role, const WCHAR* pDefaultDeviceID); - HRESULT (STDMETHODCALLTYPE * OnPropertyValueChanged)(ma_IMMNotificationClient* pThis, const WCHAR* pDeviceID, const PROPERTYKEY key); - } ma_IMMNotificationClientVtbl; - - /* IMMDeviceEnumerator */ - typedef struct - { - /* IUnknown */ - HRESULT (STDMETHODCALLTYPE * QueryInterface)(ma_IMMDeviceEnumerator* pThis, const IID* const riid, void** ppObject); - ULONG (STDMETHODCALLTYPE * AddRef) (ma_IMMDeviceEnumerator* pThis); - ULONG (STDMETHODCALLTYPE * Release) (ma_IMMDeviceEnumerator* pThis); - - /* IMMDeviceEnumerator */ - HRESULT (STDMETHODCALLTYPE * EnumAudioEndpoints) (ma_IMMDeviceEnumerator* pThis, ma_EDataFlow dataFlow, DWORD dwStateMask, ma_IMMDeviceCollection** ppDevices); - HRESULT (STDMETHODCALLTYPE * GetDefaultAudioEndpoint) (ma_IMMDeviceEnumerator* pThis, ma_EDataFlow dataFlow, ma_ERole role, ma_IMMDevice** ppEndpoint); - HRESULT (STDMETHODCALLTYPE * GetDevice) (ma_IMMDeviceEnumerator* pThis, const WCHAR* pID, ma_IMMDevice** ppDevice); - HRESULT (STDMETHODCALLTYPE * RegisterEndpointNotificationCallback) (ma_IMMDeviceEnumerator* pThis, ma_IMMNotificationClient* pClient); - HRESULT (STDMETHODCALLTYPE * UnregisterEndpointNotificationCallback)(ma_IMMDeviceEnumerator* pThis, ma_IMMNotificationClient* pClient); - } ma_IMMDeviceEnumeratorVtbl; - struct ma_IMMDeviceEnumerator - { - ma_IMMDeviceEnumeratorVtbl* lpVtbl; - }; - static MA_INLINE HRESULT ma_IMMDeviceEnumerator_QueryInterface(ma_IMMDeviceEnumerator* pThis, const IID* const riid, void** ppObject) { return pThis->lpVtbl->QueryInterface(pThis, riid, ppObject); } - static MA_INLINE ULONG ma_IMMDeviceEnumerator_AddRef(ma_IMMDeviceEnumerator* pThis) { return pThis->lpVtbl->AddRef(pThis); } - static MA_INLINE ULONG ma_IMMDeviceEnumerator_Release(ma_IMMDeviceEnumerator* pThis) { return pThis->lpVtbl->Release(pThis); } - static MA_INLINE HRESULT ma_IMMDeviceEnumerator_EnumAudioEndpoints(ma_IMMDeviceEnumerator* pThis, ma_EDataFlow dataFlow, DWORD dwStateMask, ma_IMMDeviceCollection** ppDevices) { return pThis->lpVtbl->EnumAudioEndpoints(pThis, dataFlow, dwStateMask, ppDevices); } - static MA_INLINE HRESULT ma_IMMDeviceEnumerator_GetDefaultAudioEndpoint(ma_IMMDeviceEnumerator* pThis, ma_EDataFlow dataFlow, ma_ERole role, ma_IMMDevice** ppEndpoint) { return pThis->lpVtbl->GetDefaultAudioEndpoint(pThis, dataFlow, role, ppEndpoint); } - static MA_INLINE HRESULT ma_IMMDeviceEnumerator_GetDevice(ma_IMMDeviceEnumerator* pThis, const WCHAR* pID, ma_IMMDevice** ppDevice) { return pThis->lpVtbl->GetDevice(pThis, pID, ppDevice); } - static MA_INLINE HRESULT ma_IMMDeviceEnumerator_RegisterEndpointNotificationCallback(ma_IMMDeviceEnumerator* pThis, ma_IMMNotificationClient* pClient) { return pThis->lpVtbl->RegisterEndpointNotificationCallback(pThis, pClient); } - static MA_INLINE HRESULT ma_IMMDeviceEnumerator_UnregisterEndpointNotificationCallback(ma_IMMDeviceEnumerator* pThis, ma_IMMNotificationClient* pClient) { return pThis->lpVtbl->UnregisterEndpointNotificationCallback(pThis, pClient); } - - - /* IMMDeviceCollection */ - typedef struct - { - /* IUnknown */ - HRESULT (STDMETHODCALLTYPE * QueryInterface)(ma_IMMDeviceCollection* pThis, const IID* const riid, void** ppObject); - ULONG (STDMETHODCALLTYPE * AddRef) (ma_IMMDeviceCollection* pThis); - ULONG (STDMETHODCALLTYPE * Release) (ma_IMMDeviceCollection* pThis); - - /* IMMDeviceCollection */ - HRESULT (STDMETHODCALLTYPE * GetCount)(ma_IMMDeviceCollection* pThis, UINT* pDevices); - HRESULT (STDMETHODCALLTYPE * Item) (ma_IMMDeviceCollection* pThis, UINT nDevice, ma_IMMDevice** ppDevice); - } ma_IMMDeviceCollectionVtbl; - struct ma_IMMDeviceCollection - { - ma_IMMDeviceCollectionVtbl* lpVtbl; - }; - static MA_INLINE HRESULT ma_IMMDeviceCollection_QueryInterface(ma_IMMDeviceCollection* pThis, const IID* const riid, void** ppObject) { return pThis->lpVtbl->QueryInterface(pThis, riid, ppObject); } - static MA_INLINE ULONG ma_IMMDeviceCollection_AddRef(ma_IMMDeviceCollection* pThis) { return pThis->lpVtbl->AddRef(pThis); } - static MA_INLINE ULONG ma_IMMDeviceCollection_Release(ma_IMMDeviceCollection* pThis) { return pThis->lpVtbl->Release(pThis); } - static MA_INLINE HRESULT ma_IMMDeviceCollection_GetCount(ma_IMMDeviceCollection* pThis, UINT* pDevices) { return pThis->lpVtbl->GetCount(pThis, pDevices); } - static MA_INLINE HRESULT ma_IMMDeviceCollection_Item(ma_IMMDeviceCollection* pThis, UINT nDevice, ma_IMMDevice** ppDevice) { return pThis->lpVtbl->Item(pThis, nDevice, ppDevice); } - - - /* IMMDevice */ - typedef struct - { - /* IUnknown */ - HRESULT (STDMETHODCALLTYPE * QueryInterface)(ma_IMMDevice* pThis, const IID* const riid, void** ppObject); - ULONG (STDMETHODCALLTYPE * AddRef) (ma_IMMDevice* pThis); - ULONG (STDMETHODCALLTYPE * Release) (ma_IMMDevice* pThis); - - /* IMMDevice */ - HRESULT (STDMETHODCALLTYPE * Activate) (ma_IMMDevice* pThis, const IID* const iid, DWORD dwClsCtx, MA_PROPVARIANT* pActivationParams, void** ppInterface); - HRESULT (STDMETHODCALLTYPE * OpenPropertyStore)(ma_IMMDevice* pThis, DWORD stgmAccess, ma_IPropertyStore** ppProperties); - HRESULT (STDMETHODCALLTYPE * GetId) (ma_IMMDevice* pThis, WCHAR** pID); - HRESULT (STDMETHODCALLTYPE * GetState) (ma_IMMDevice* pThis, DWORD *pState); - } ma_IMMDeviceVtbl; - struct ma_IMMDevice - { - ma_IMMDeviceVtbl* lpVtbl; - }; - static MA_INLINE HRESULT ma_IMMDevice_QueryInterface(ma_IMMDevice* pThis, const IID* const riid, void** ppObject) { return pThis->lpVtbl->QueryInterface(pThis, riid, ppObject); } - static MA_INLINE ULONG ma_IMMDevice_AddRef(ma_IMMDevice* pThis) { return pThis->lpVtbl->AddRef(pThis); } - static MA_INLINE ULONG ma_IMMDevice_Release(ma_IMMDevice* pThis) { return pThis->lpVtbl->Release(pThis); } - static MA_INLINE HRESULT ma_IMMDevice_Activate(ma_IMMDevice* pThis, const IID* const iid, DWORD dwClsCtx, MA_PROPVARIANT* pActivationParams, void** ppInterface) { return pThis->lpVtbl->Activate(pThis, iid, dwClsCtx, pActivationParams, ppInterface); } - static MA_INLINE HRESULT ma_IMMDevice_OpenPropertyStore(ma_IMMDevice* pThis, DWORD stgmAccess, ma_IPropertyStore** ppProperties) { return pThis->lpVtbl->OpenPropertyStore(pThis, stgmAccess, ppProperties); } - static MA_INLINE HRESULT ma_IMMDevice_GetId(ma_IMMDevice* pThis, WCHAR** pID) { return pThis->lpVtbl->GetId(pThis, pID); } - static MA_INLINE HRESULT ma_IMMDevice_GetState(ma_IMMDevice* pThis, DWORD *pState) { return pThis->lpVtbl->GetState(pThis, pState); } -#else - /* IActivateAudioInterfaceAsyncOperation */ - typedef struct - { - /* IUnknown */ - HRESULT (STDMETHODCALLTYPE * QueryInterface)(ma_IActivateAudioInterfaceAsyncOperation* pThis, const IID* const riid, void** ppObject); - ULONG (STDMETHODCALLTYPE * AddRef) (ma_IActivateAudioInterfaceAsyncOperation* pThis); - ULONG (STDMETHODCALLTYPE * Release) (ma_IActivateAudioInterfaceAsyncOperation* pThis); - - /* IActivateAudioInterfaceAsyncOperation */ - HRESULT (STDMETHODCALLTYPE * GetActivateResult)(ma_IActivateAudioInterfaceAsyncOperation* pThis, HRESULT *pActivateResult, ma_IUnknown** ppActivatedInterface); - } ma_IActivateAudioInterfaceAsyncOperationVtbl; - struct ma_IActivateAudioInterfaceAsyncOperation - { - ma_IActivateAudioInterfaceAsyncOperationVtbl* lpVtbl; - }; - static MA_INLINE HRESULT ma_IActivateAudioInterfaceAsyncOperation_QueryInterface(ma_IActivateAudioInterfaceAsyncOperation* pThis, const IID* const riid, void** ppObject) { return pThis->lpVtbl->QueryInterface(pThis, riid, ppObject); } - static MA_INLINE ULONG ma_IActivateAudioInterfaceAsyncOperation_AddRef(ma_IActivateAudioInterfaceAsyncOperation* pThis) { return pThis->lpVtbl->AddRef(pThis); } - static MA_INLINE ULONG ma_IActivateAudioInterfaceAsyncOperation_Release(ma_IActivateAudioInterfaceAsyncOperation* pThis) { return pThis->lpVtbl->Release(pThis); } - static MA_INLINE HRESULT ma_IActivateAudioInterfaceAsyncOperation_GetActivateResult(ma_IActivateAudioInterfaceAsyncOperation* pThis, HRESULT *pActivateResult, ma_IUnknown** ppActivatedInterface) { return pThis->lpVtbl->GetActivateResult(pThis, pActivateResult, ppActivatedInterface); } -#endif - -/* IPropertyStore */ -typedef struct -{ - /* IUnknown */ - HRESULT (STDMETHODCALLTYPE * QueryInterface)(ma_IPropertyStore* pThis, const IID* const riid, void** ppObject); - ULONG (STDMETHODCALLTYPE * AddRef) (ma_IPropertyStore* pThis); - ULONG (STDMETHODCALLTYPE * Release) (ma_IPropertyStore* pThis); - - /* IPropertyStore */ - HRESULT (STDMETHODCALLTYPE * GetCount)(ma_IPropertyStore* pThis, DWORD* pPropCount); - HRESULT (STDMETHODCALLTYPE * GetAt) (ma_IPropertyStore* pThis, DWORD propIndex, PROPERTYKEY* pPropKey); - HRESULT (STDMETHODCALLTYPE * GetValue)(ma_IPropertyStore* pThis, const PROPERTYKEY* const pKey, MA_PROPVARIANT* pPropVar); - HRESULT (STDMETHODCALLTYPE * SetValue)(ma_IPropertyStore* pThis, const PROPERTYKEY* const pKey, const MA_PROPVARIANT* const pPropVar); - HRESULT (STDMETHODCALLTYPE * Commit) (ma_IPropertyStore* pThis); -} ma_IPropertyStoreVtbl; -struct ma_IPropertyStore -{ - ma_IPropertyStoreVtbl* lpVtbl; -}; -static MA_INLINE HRESULT ma_IPropertyStore_QueryInterface(ma_IPropertyStore* pThis, const IID* const riid, void** ppObject) { return pThis->lpVtbl->QueryInterface(pThis, riid, ppObject); } -static MA_INLINE ULONG ma_IPropertyStore_AddRef(ma_IPropertyStore* pThis) { return pThis->lpVtbl->AddRef(pThis); } -static MA_INLINE ULONG ma_IPropertyStore_Release(ma_IPropertyStore* pThis) { return pThis->lpVtbl->Release(pThis); } -static MA_INLINE HRESULT ma_IPropertyStore_GetCount(ma_IPropertyStore* pThis, DWORD* pPropCount) { return pThis->lpVtbl->GetCount(pThis, pPropCount); } -static MA_INLINE HRESULT ma_IPropertyStore_GetAt(ma_IPropertyStore* pThis, DWORD propIndex, PROPERTYKEY* pPropKey) { return pThis->lpVtbl->GetAt(pThis, propIndex, pPropKey); } -static MA_INLINE HRESULT ma_IPropertyStore_GetValue(ma_IPropertyStore* pThis, const PROPERTYKEY* const pKey, MA_PROPVARIANT* pPropVar) { return pThis->lpVtbl->GetValue(pThis, pKey, pPropVar); } -static MA_INLINE HRESULT ma_IPropertyStore_SetValue(ma_IPropertyStore* pThis, const PROPERTYKEY* const pKey, const MA_PROPVARIANT* const pPropVar) { return pThis->lpVtbl->SetValue(pThis, pKey, pPropVar); } -static MA_INLINE HRESULT ma_IPropertyStore_Commit(ma_IPropertyStore* pThis) { return pThis->lpVtbl->Commit(pThis); } - - -/* IAudioClient */ -typedef struct -{ - /* IUnknown */ - HRESULT (STDMETHODCALLTYPE * QueryInterface)(ma_IAudioClient* pThis, const IID* const riid, void** ppObject); - ULONG (STDMETHODCALLTYPE * AddRef) (ma_IAudioClient* pThis); - ULONG (STDMETHODCALLTYPE * Release) (ma_IAudioClient* pThis); - - /* IAudioClient */ - HRESULT (STDMETHODCALLTYPE * Initialize) (ma_IAudioClient* pThis, MA_AUDCLNT_SHAREMODE shareMode, DWORD streamFlags, MA_REFERENCE_TIME bufferDuration, MA_REFERENCE_TIME periodicity, const MA_WAVEFORMATEX* pFormat, const GUID* pAudioSessionGuid); - HRESULT (STDMETHODCALLTYPE * GetBufferSize) (ma_IAudioClient* pThis, ma_uint32* pNumBufferFrames); - HRESULT (STDMETHODCALLTYPE * GetStreamLatency) (ma_IAudioClient* pThis, MA_REFERENCE_TIME* pLatency); - HRESULT (STDMETHODCALLTYPE * GetCurrentPadding)(ma_IAudioClient* pThis, ma_uint32* pNumPaddingFrames); - HRESULT (STDMETHODCALLTYPE * IsFormatSupported)(ma_IAudioClient* pThis, MA_AUDCLNT_SHAREMODE shareMode, const MA_WAVEFORMATEX* pFormat, MA_WAVEFORMATEX** ppClosestMatch); - HRESULT (STDMETHODCALLTYPE * GetMixFormat) (ma_IAudioClient* pThis, MA_WAVEFORMATEX** ppDeviceFormat); - HRESULT (STDMETHODCALLTYPE * GetDevicePeriod) (ma_IAudioClient* pThis, MA_REFERENCE_TIME* pDefaultDevicePeriod, MA_REFERENCE_TIME* pMinimumDevicePeriod); - HRESULT (STDMETHODCALLTYPE * Start) (ma_IAudioClient* pThis); - HRESULT (STDMETHODCALLTYPE * Stop) (ma_IAudioClient* pThis); - HRESULT (STDMETHODCALLTYPE * Reset) (ma_IAudioClient* pThis); - HRESULT (STDMETHODCALLTYPE * SetEventHandle) (ma_IAudioClient* pThis, HANDLE eventHandle); - HRESULT (STDMETHODCALLTYPE * GetService) (ma_IAudioClient* pThis, const IID* const riid, void** pp); -} ma_IAudioClientVtbl; -struct ma_IAudioClient -{ - ma_IAudioClientVtbl* lpVtbl; -}; -static MA_INLINE HRESULT ma_IAudioClient_QueryInterface(ma_IAudioClient* pThis, const IID* const riid, void** ppObject) { return pThis->lpVtbl->QueryInterface(pThis, riid, ppObject); } -static MA_INLINE ULONG ma_IAudioClient_AddRef(ma_IAudioClient* pThis) { return pThis->lpVtbl->AddRef(pThis); } -static MA_INLINE ULONG ma_IAudioClient_Release(ma_IAudioClient* pThis) { return pThis->lpVtbl->Release(pThis); } -static MA_INLINE HRESULT ma_IAudioClient_Initialize(ma_IAudioClient* pThis, MA_AUDCLNT_SHAREMODE shareMode, DWORD streamFlags, MA_REFERENCE_TIME bufferDuration, MA_REFERENCE_TIME periodicity, const MA_WAVEFORMATEX* pFormat, const GUID* pAudioSessionGuid) { return pThis->lpVtbl->Initialize(pThis, shareMode, streamFlags, bufferDuration, periodicity, pFormat, pAudioSessionGuid); } -static MA_INLINE HRESULT ma_IAudioClient_GetBufferSize(ma_IAudioClient* pThis, ma_uint32* pNumBufferFrames) { return pThis->lpVtbl->GetBufferSize(pThis, pNumBufferFrames); } -static MA_INLINE HRESULT ma_IAudioClient_GetStreamLatency(ma_IAudioClient* pThis, MA_REFERENCE_TIME* pLatency) { return pThis->lpVtbl->GetStreamLatency(pThis, pLatency); } -static MA_INLINE HRESULT ma_IAudioClient_GetCurrentPadding(ma_IAudioClient* pThis, ma_uint32* pNumPaddingFrames) { return pThis->lpVtbl->GetCurrentPadding(pThis, pNumPaddingFrames); } -static MA_INLINE HRESULT ma_IAudioClient_IsFormatSupported(ma_IAudioClient* pThis, MA_AUDCLNT_SHAREMODE shareMode, const MA_WAVEFORMATEX* pFormat, MA_WAVEFORMATEX** ppClosestMatch) { return pThis->lpVtbl->IsFormatSupported(pThis, shareMode, pFormat, ppClosestMatch); } -static MA_INLINE HRESULT ma_IAudioClient_GetMixFormat(ma_IAudioClient* pThis, MA_WAVEFORMATEX** ppDeviceFormat) { return pThis->lpVtbl->GetMixFormat(pThis, ppDeviceFormat); } -static MA_INLINE HRESULT ma_IAudioClient_GetDevicePeriod(ma_IAudioClient* pThis, MA_REFERENCE_TIME* pDefaultDevicePeriod, MA_REFERENCE_TIME* pMinimumDevicePeriod) { return pThis->lpVtbl->GetDevicePeriod(pThis, pDefaultDevicePeriod, pMinimumDevicePeriod); } -static MA_INLINE HRESULT ma_IAudioClient_Start(ma_IAudioClient* pThis) { return pThis->lpVtbl->Start(pThis); } -static MA_INLINE HRESULT ma_IAudioClient_Stop(ma_IAudioClient* pThis) { return pThis->lpVtbl->Stop(pThis); } -static MA_INLINE HRESULT ma_IAudioClient_Reset(ma_IAudioClient* pThis) { return pThis->lpVtbl->Reset(pThis); } -static MA_INLINE HRESULT ma_IAudioClient_SetEventHandle(ma_IAudioClient* pThis, HANDLE eventHandle) { return pThis->lpVtbl->SetEventHandle(pThis, eventHandle); } -static MA_INLINE HRESULT ma_IAudioClient_GetService(ma_IAudioClient* pThis, const IID* const riid, void** pp) { return pThis->lpVtbl->GetService(pThis, riid, pp); } - -/* IAudioClient2 */ -typedef struct -{ - /* IUnknown */ - HRESULT (STDMETHODCALLTYPE * QueryInterface)(ma_IAudioClient2* pThis, const IID* const riid, void** ppObject); - ULONG (STDMETHODCALLTYPE * AddRef) (ma_IAudioClient2* pThis); - ULONG (STDMETHODCALLTYPE * Release) (ma_IAudioClient2* pThis); - - /* IAudioClient */ - HRESULT (STDMETHODCALLTYPE * Initialize) (ma_IAudioClient2* pThis, MA_AUDCLNT_SHAREMODE shareMode, DWORD streamFlags, MA_REFERENCE_TIME bufferDuration, MA_REFERENCE_TIME periodicity, const MA_WAVEFORMATEX* pFormat, const GUID* pAudioSessionGuid); - HRESULT (STDMETHODCALLTYPE * GetBufferSize) (ma_IAudioClient2* pThis, ma_uint32* pNumBufferFrames); - HRESULT (STDMETHODCALLTYPE * GetStreamLatency) (ma_IAudioClient2* pThis, MA_REFERENCE_TIME* pLatency); - HRESULT (STDMETHODCALLTYPE * GetCurrentPadding)(ma_IAudioClient2* pThis, ma_uint32* pNumPaddingFrames); - HRESULT (STDMETHODCALLTYPE * IsFormatSupported)(ma_IAudioClient2* pThis, MA_AUDCLNT_SHAREMODE shareMode, const MA_WAVEFORMATEX* pFormat, MA_WAVEFORMATEX** ppClosestMatch); - HRESULT (STDMETHODCALLTYPE * GetMixFormat) (ma_IAudioClient2* pThis, MA_WAVEFORMATEX** ppDeviceFormat); - HRESULT (STDMETHODCALLTYPE * GetDevicePeriod) (ma_IAudioClient2* pThis, MA_REFERENCE_TIME* pDefaultDevicePeriod, MA_REFERENCE_TIME* pMinimumDevicePeriod); - HRESULT (STDMETHODCALLTYPE * Start) (ma_IAudioClient2* pThis); - HRESULT (STDMETHODCALLTYPE * Stop) (ma_IAudioClient2* pThis); - HRESULT (STDMETHODCALLTYPE * Reset) (ma_IAudioClient2* pThis); - HRESULT (STDMETHODCALLTYPE * SetEventHandle) (ma_IAudioClient2* pThis, HANDLE eventHandle); - HRESULT (STDMETHODCALLTYPE * GetService) (ma_IAudioClient2* pThis, const IID* const riid, void** pp); - - /* IAudioClient2 */ - HRESULT (STDMETHODCALLTYPE * IsOffloadCapable) (ma_IAudioClient2* pThis, MA_AUDIO_STREAM_CATEGORY category, BOOL* pOffloadCapable); - HRESULT (STDMETHODCALLTYPE * SetClientProperties)(ma_IAudioClient2* pThis, const ma_AudioClientProperties* pProperties); - HRESULT (STDMETHODCALLTYPE * GetBufferSizeLimits)(ma_IAudioClient2* pThis, const MA_WAVEFORMATEX* pFormat, BOOL eventDriven, MA_REFERENCE_TIME* pMinBufferDuration, MA_REFERENCE_TIME* pMaxBufferDuration); -} ma_IAudioClient2Vtbl; -struct ma_IAudioClient2 -{ - ma_IAudioClient2Vtbl* lpVtbl; -}; -static MA_INLINE HRESULT ma_IAudioClient2_QueryInterface(ma_IAudioClient2* pThis, const IID* const riid, void** ppObject) { return pThis->lpVtbl->QueryInterface(pThis, riid, ppObject); } -static MA_INLINE ULONG ma_IAudioClient2_AddRef(ma_IAudioClient2* pThis) { return pThis->lpVtbl->AddRef(pThis); } -static MA_INLINE ULONG ma_IAudioClient2_Release(ma_IAudioClient2* pThis) { return pThis->lpVtbl->Release(pThis); } -static MA_INLINE HRESULT ma_IAudioClient2_Initialize(ma_IAudioClient2* pThis, MA_AUDCLNT_SHAREMODE shareMode, DWORD streamFlags, MA_REFERENCE_TIME bufferDuration, MA_REFERENCE_TIME periodicity, const MA_WAVEFORMATEX* pFormat, const GUID* pAudioSessionGuid) { return pThis->lpVtbl->Initialize(pThis, shareMode, streamFlags, bufferDuration, periodicity, pFormat, pAudioSessionGuid); } -static MA_INLINE HRESULT ma_IAudioClient2_GetBufferSize(ma_IAudioClient2* pThis, ma_uint32* pNumBufferFrames) { return pThis->lpVtbl->GetBufferSize(pThis, pNumBufferFrames); } -static MA_INLINE HRESULT ma_IAudioClient2_GetStreamLatency(ma_IAudioClient2* pThis, MA_REFERENCE_TIME* pLatency) { return pThis->lpVtbl->GetStreamLatency(pThis, pLatency); } -static MA_INLINE HRESULT ma_IAudioClient2_GetCurrentPadding(ma_IAudioClient2* pThis, ma_uint32* pNumPaddingFrames) { return pThis->lpVtbl->GetCurrentPadding(pThis, pNumPaddingFrames); } -static MA_INLINE HRESULT ma_IAudioClient2_IsFormatSupported(ma_IAudioClient2* pThis, MA_AUDCLNT_SHAREMODE shareMode, const MA_WAVEFORMATEX* pFormat, MA_WAVEFORMATEX** ppClosestMatch) { return pThis->lpVtbl->IsFormatSupported(pThis, shareMode, pFormat, ppClosestMatch); } -static MA_INLINE HRESULT ma_IAudioClient2_GetMixFormat(ma_IAudioClient2* pThis, MA_WAVEFORMATEX** ppDeviceFormat) { return pThis->lpVtbl->GetMixFormat(pThis, ppDeviceFormat); } -static MA_INLINE HRESULT ma_IAudioClient2_GetDevicePeriod(ma_IAudioClient2* pThis, MA_REFERENCE_TIME* pDefaultDevicePeriod, MA_REFERENCE_TIME* pMinimumDevicePeriod) { return pThis->lpVtbl->GetDevicePeriod(pThis, pDefaultDevicePeriod, pMinimumDevicePeriod); } -static MA_INLINE HRESULT ma_IAudioClient2_Start(ma_IAudioClient2* pThis) { return pThis->lpVtbl->Start(pThis); } -static MA_INLINE HRESULT ma_IAudioClient2_Stop(ma_IAudioClient2* pThis) { return pThis->lpVtbl->Stop(pThis); } -static MA_INLINE HRESULT ma_IAudioClient2_Reset(ma_IAudioClient2* pThis) { return pThis->lpVtbl->Reset(pThis); } -static MA_INLINE HRESULT ma_IAudioClient2_SetEventHandle(ma_IAudioClient2* pThis, HANDLE eventHandle) { return pThis->lpVtbl->SetEventHandle(pThis, eventHandle); } -static MA_INLINE HRESULT ma_IAudioClient2_GetService(ma_IAudioClient2* pThis, const IID* const riid, void** pp) { return pThis->lpVtbl->GetService(pThis, riid, pp); } -static MA_INLINE HRESULT ma_IAudioClient2_IsOffloadCapable(ma_IAudioClient2* pThis, MA_AUDIO_STREAM_CATEGORY category, BOOL* pOffloadCapable) { return pThis->lpVtbl->IsOffloadCapable(pThis, category, pOffloadCapable); } -static MA_INLINE HRESULT ma_IAudioClient2_SetClientProperties(ma_IAudioClient2* pThis, const ma_AudioClientProperties* pProperties) { return pThis->lpVtbl->SetClientProperties(pThis, pProperties); } -static MA_INLINE HRESULT ma_IAudioClient2_GetBufferSizeLimits(ma_IAudioClient2* pThis, const MA_WAVEFORMATEX* pFormat, BOOL eventDriven, MA_REFERENCE_TIME* pMinBufferDuration, MA_REFERENCE_TIME* pMaxBufferDuration) { return pThis->lpVtbl->GetBufferSizeLimits(pThis, pFormat, eventDriven, pMinBufferDuration, pMaxBufferDuration); } - - -/* IAudioClient3 */ -typedef struct -{ - /* IUnknown */ - HRESULT (STDMETHODCALLTYPE * QueryInterface)(ma_IAudioClient3* pThis, const IID* const riid, void** ppObject); - ULONG (STDMETHODCALLTYPE * AddRef) (ma_IAudioClient3* pThis); - ULONG (STDMETHODCALLTYPE * Release) (ma_IAudioClient3* pThis); - - /* IAudioClient */ - HRESULT (STDMETHODCALLTYPE * Initialize) (ma_IAudioClient3* pThis, MA_AUDCLNT_SHAREMODE shareMode, DWORD streamFlags, MA_REFERENCE_TIME bufferDuration, MA_REFERENCE_TIME periodicity, const MA_WAVEFORMATEX* pFormat, const GUID* pAudioSessionGuid); - HRESULT (STDMETHODCALLTYPE * GetBufferSize) (ma_IAudioClient3* pThis, ma_uint32* pNumBufferFrames); - HRESULT (STDMETHODCALLTYPE * GetStreamLatency) (ma_IAudioClient3* pThis, MA_REFERENCE_TIME* pLatency); - HRESULT (STDMETHODCALLTYPE * GetCurrentPadding)(ma_IAudioClient3* pThis, ma_uint32* pNumPaddingFrames); - HRESULT (STDMETHODCALLTYPE * IsFormatSupported)(ma_IAudioClient3* pThis, MA_AUDCLNT_SHAREMODE shareMode, const MA_WAVEFORMATEX* pFormat, MA_WAVEFORMATEX** ppClosestMatch); - HRESULT (STDMETHODCALLTYPE * GetMixFormat) (ma_IAudioClient3* pThis, MA_WAVEFORMATEX** ppDeviceFormat); - HRESULT (STDMETHODCALLTYPE * GetDevicePeriod) (ma_IAudioClient3* pThis, MA_REFERENCE_TIME* pDefaultDevicePeriod, MA_REFERENCE_TIME* pMinimumDevicePeriod); - HRESULT (STDMETHODCALLTYPE * Start) (ma_IAudioClient3* pThis); - HRESULT (STDMETHODCALLTYPE * Stop) (ma_IAudioClient3* pThis); - HRESULT (STDMETHODCALLTYPE * Reset) (ma_IAudioClient3* pThis); - HRESULT (STDMETHODCALLTYPE * SetEventHandle) (ma_IAudioClient3* pThis, HANDLE eventHandle); - HRESULT (STDMETHODCALLTYPE * GetService) (ma_IAudioClient3* pThis, const IID* const riid, void** pp); - - /* IAudioClient2 */ - HRESULT (STDMETHODCALLTYPE * IsOffloadCapable) (ma_IAudioClient3* pThis, MA_AUDIO_STREAM_CATEGORY category, BOOL* pOffloadCapable); - HRESULT (STDMETHODCALLTYPE * SetClientProperties)(ma_IAudioClient3* pThis, const ma_AudioClientProperties* pProperties); - HRESULT (STDMETHODCALLTYPE * GetBufferSizeLimits)(ma_IAudioClient3* pThis, const MA_WAVEFORMATEX* pFormat, BOOL eventDriven, MA_REFERENCE_TIME* pMinBufferDuration, MA_REFERENCE_TIME* pMaxBufferDuration); - - /* IAudioClient3 */ - HRESULT (STDMETHODCALLTYPE * GetSharedModeEnginePeriod) (ma_IAudioClient3* pThis, const MA_WAVEFORMATEX* pFormat, ma_uint32* pDefaultPeriodInFrames, ma_uint32* pFundamentalPeriodInFrames, ma_uint32* pMinPeriodInFrames, ma_uint32* pMaxPeriodInFrames); - HRESULT (STDMETHODCALLTYPE * GetCurrentSharedModeEnginePeriod)(ma_IAudioClient3* pThis, MA_WAVEFORMATEX** ppFormat, ma_uint32* pCurrentPeriodInFrames); - HRESULT (STDMETHODCALLTYPE * InitializeSharedAudioStream) (ma_IAudioClient3* pThis, DWORD streamFlags, ma_uint32 periodInFrames, const MA_WAVEFORMATEX* pFormat, const GUID* pAudioSessionGuid); -} ma_IAudioClient3Vtbl; -struct ma_IAudioClient3 -{ - ma_IAudioClient3Vtbl* lpVtbl; -}; -static MA_INLINE HRESULT ma_IAudioClient3_QueryInterface(ma_IAudioClient3* pThis, const IID* const riid, void** ppObject) { return pThis->lpVtbl->QueryInterface(pThis, riid, ppObject); } -static MA_INLINE ULONG ma_IAudioClient3_AddRef(ma_IAudioClient3* pThis) { return pThis->lpVtbl->AddRef(pThis); } -static MA_INLINE ULONG ma_IAudioClient3_Release(ma_IAudioClient3* pThis) { return pThis->lpVtbl->Release(pThis); } -static MA_INLINE HRESULT ma_IAudioClient3_Initialize(ma_IAudioClient3* pThis, MA_AUDCLNT_SHAREMODE shareMode, DWORD streamFlags, MA_REFERENCE_TIME bufferDuration, MA_REFERENCE_TIME periodicity, const MA_WAVEFORMATEX* pFormat, const GUID* pAudioSessionGuid) { return pThis->lpVtbl->Initialize(pThis, shareMode, streamFlags, bufferDuration, periodicity, pFormat, pAudioSessionGuid); } -static MA_INLINE HRESULT ma_IAudioClient3_GetBufferSize(ma_IAudioClient3* pThis, ma_uint32* pNumBufferFrames) { return pThis->lpVtbl->GetBufferSize(pThis, pNumBufferFrames); } -static MA_INLINE HRESULT ma_IAudioClient3_GetStreamLatency(ma_IAudioClient3* pThis, MA_REFERENCE_TIME* pLatency) { return pThis->lpVtbl->GetStreamLatency(pThis, pLatency); } -static MA_INLINE HRESULT ma_IAudioClient3_GetCurrentPadding(ma_IAudioClient3* pThis, ma_uint32* pNumPaddingFrames) { return pThis->lpVtbl->GetCurrentPadding(pThis, pNumPaddingFrames); } -static MA_INLINE HRESULT ma_IAudioClient3_IsFormatSupported(ma_IAudioClient3* pThis, MA_AUDCLNT_SHAREMODE shareMode, const MA_WAVEFORMATEX* pFormat, MA_WAVEFORMATEX** ppClosestMatch) { return pThis->lpVtbl->IsFormatSupported(pThis, shareMode, pFormat, ppClosestMatch); } -static MA_INLINE HRESULT ma_IAudioClient3_GetMixFormat(ma_IAudioClient3* pThis, MA_WAVEFORMATEX** ppDeviceFormat) { return pThis->lpVtbl->GetMixFormat(pThis, ppDeviceFormat); } -static MA_INLINE HRESULT ma_IAudioClient3_GetDevicePeriod(ma_IAudioClient3* pThis, MA_REFERENCE_TIME* pDefaultDevicePeriod, MA_REFERENCE_TIME* pMinimumDevicePeriod) { return pThis->lpVtbl->GetDevicePeriod(pThis, pDefaultDevicePeriod, pMinimumDevicePeriod); } -static MA_INLINE HRESULT ma_IAudioClient3_Start(ma_IAudioClient3* pThis) { return pThis->lpVtbl->Start(pThis); } -static MA_INLINE HRESULT ma_IAudioClient3_Stop(ma_IAudioClient3* pThis) { return pThis->lpVtbl->Stop(pThis); } -static MA_INLINE HRESULT ma_IAudioClient3_Reset(ma_IAudioClient3* pThis) { return pThis->lpVtbl->Reset(pThis); } -static MA_INLINE HRESULT ma_IAudioClient3_SetEventHandle(ma_IAudioClient3* pThis, HANDLE eventHandle) { return pThis->lpVtbl->SetEventHandle(pThis, eventHandle); } -static MA_INLINE HRESULT ma_IAudioClient3_GetService(ma_IAudioClient3* pThis, const IID* const riid, void** pp) { return pThis->lpVtbl->GetService(pThis, riid, pp); } -static MA_INLINE HRESULT ma_IAudioClient3_IsOffloadCapable(ma_IAudioClient3* pThis, MA_AUDIO_STREAM_CATEGORY category, BOOL* pOffloadCapable) { return pThis->lpVtbl->IsOffloadCapable(pThis, category, pOffloadCapable); } -static MA_INLINE HRESULT ma_IAudioClient3_SetClientProperties(ma_IAudioClient3* pThis, const ma_AudioClientProperties* pProperties) { return pThis->lpVtbl->SetClientProperties(pThis, pProperties); } -static MA_INLINE HRESULT ma_IAudioClient3_GetBufferSizeLimits(ma_IAudioClient3* pThis, const MA_WAVEFORMATEX* pFormat, BOOL eventDriven, MA_REFERENCE_TIME* pMinBufferDuration, MA_REFERENCE_TIME* pMaxBufferDuration) { return pThis->lpVtbl->GetBufferSizeLimits(pThis, pFormat, eventDriven, pMinBufferDuration, pMaxBufferDuration); } -static MA_INLINE HRESULT ma_IAudioClient3_GetSharedModeEnginePeriod(ma_IAudioClient3* pThis, const MA_WAVEFORMATEX* pFormat, ma_uint32* pDefaultPeriodInFrames, ma_uint32* pFundamentalPeriodInFrames, ma_uint32* pMinPeriodInFrames, ma_uint32* pMaxPeriodInFrames) { return pThis->lpVtbl->GetSharedModeEnginePeriod(pThis, pFormat, pDefaultPeriodInFrames, pFundamentalPeriodInFrames, pMinPeriodInFrames, pMaxPeriodInFrames); } -static MA_INLINE HRESULT ma_IAudioClient3_GetCurrentSharedModeEnginePeriod(ma_IAudioClient3* pThis, MA_WAVEFORMATEX** ppFormat, ma_uint32* pCurrentPeriodInFrames) { return pThis->lpVtbl->GetCurrentSharedModeEnginePeriod(pThis, ppFormat, pCurrentPeriodInFrames); } -static MA_INLINE HRESULT ma_IAudioClient3_InitializeSharedAudioStream(ma_IAudioClient3* pThis, DWORD streamFlags, ma_uint32 periodInFrames, const MA_WAVEFORMATEX* pFormat, const GUID* pAudioSessionGUID) { return pThis->lpVtbl->InitializeSharedAudioStream(pThis, streamFlags, periodInFrames, pFormat, pAudioSessionGUID); } - - -/* IAudioRenderClient */ -typedef struct -{ - /* IUnknown */ - HRESULT (STDMETHODCALLTYPE * QueryInterface)(ma_IAudioRenderClient* pThis, const IID* const riid, void** ppObject); - ULONG (STDMETHODCALLTYPE * AddRef) (ma_IAudioRenderClient* pThis); - ULONG (STDMETHODCALLTYPE * Release) (ma_IAudioRenderClient* pThis); - - /* IAudioRenderClient */ - HRESULT (STDMETHODCALLTYPE * GetBuffer) (ma_IAudioRenderClient* pThis, ma_uint32 numFramesRequested, BYTE** ppData); - HRESULT (STDMETHODCALLTYPE * ReleaseBuffer)(ma_IAudioRenderClient* pThis, ma_uint32 numFramesWritten, DWORD dwFlags); -} ma_IAudioRenderClientVtbl; -struct ma_IAudioRenderClient -{ - ma_IAudioRenderClientVtbl* lpVtbl; -}; -static MA_INLINE HRESULT ma_IAudioRenderClient_QueryInterface(ma_IAudioRenderClient* pThis, const IID* const riid, void** ppObject) { return pThis->lpVtbl->QueryInterface(pThis, riid, ppObject); } -static MA_INLINE ULONG ma_IAudioRenderClient_AddRef(ma_IAudioRenderClient* pThis) { return pThis->lpVtbl->AddRef(pThis); } -static MA_INLINE ULONG ma_IAudioRenderClient_Release(ma_IAudioRenderClient* pThis) { return pThis->lpVtbl->Release(pThis); } -static MA_INLINE HRESULT ma_IAudioRenderClient_GetBuffer(ma_IAudioRenderClient* pThis, ma_uint32 numFramesRequested, BYTE** ppData) { return pThis->lpVtbl->GetBuffer(pThis, numFramesRequested, ppData); } -static MA_INLINE HRESULT ma_IAudioRenderClient_ReleaseBuffer(ma_IAudioRenderClient* pThis, ma_uint32 numFramesWritten, DWORD dwFlags) { return pThis->lpVtbl->ReleaseBuffer(pThis, numFramesWritten, dwFlags); } - - -/* IAudioCaptureClient */ -typedef struct -{ - /* IUnknown */ - HRESULT (STDMETHODCALLTYPE * QueryInterface)(ma_IAudioCaptureClient* pThis, const IID* const riid, void** ppObject); - ULONG (STDMETHODCALLTYPE * AddRef) (ma_IAudioCaptureClient* pThis); - ULONG (STDMETHODCALLTYPE * Release) (ma_IAudioCaptureClient* pThis); - - /* IAudioRenderClient */ - HRESULT (STDMETHODCALLTYPE * GetBuffer) (ma_IAudioCaptureClient* pThis, BYTE** ppData, ma_uint32* pNumFramesToRead, DWORD* pFlags, ma_uint64* pDevicePosition, ma_uint64* pQPCPosition); - HRESULT (STDMETHODCALLTYPE * ReleaseBuffer) (ma_IAudioCaptureClient* pThis, ma_uint32 numFramesRead); - HRESULT (STDMETHODCALLTYPE * GetNextPacketSize)(ma_IAudioCaptureClient* pThis, ma_uint32* pNumFramesInNextPacket); -} ma_IAudioCaptureClientVtbl; -struct ma_IAudioCaptureClient -{ - ma_IAudioCaptureClientVtbl* lpVtbl; -}; -static MA_INLINE HRESULT ma_IAudioCaptureClient_QueryInterface(ma_IAudioCaptureClient* pThis, const IID* const riid, void** ppObject) { return pThis->lpVtbl->QueryInterface(pThis, riid, ppObject); } -static MA_INLINE ULONG ma_IAudioCaptureClient_AddRef(ma_IAudioCaptureClient* pThis) { return pThis->lpVtbl->AddRef(pThis); } -static MA_INLINE ULONG ma_IAudioCaptureClient_Release(ma_IAudioCaptureClient* pThis) { return pThis->lpVtbl->Release(pThis); } -static MA_INLINE HRESULT ma_IAudioCaptureClient_GetBuffer(ma_IAudioCaptureClient* pThis, BYTE** ppData, ma_uint32* pNumFramesToRead, DWORD* pFlags, ma_uint64* pDevicePosition, ma_uint64* pQPCPosition) { return pThis->lpVtbl->GetBuffer(pThis, ppData, pNumFramesToRead, pFlags, pDevicePosition, pQPCPosition); } -static MA_INLINE HRESULT ma_IAudioCaptureClient_ReleaseBuffer(ma_IAudioCaptureClient* pThis, ma_uint32 numFramesRead) { return pThis->lpVtbl->ReleaseBuffer(pThis, numFramesRead); } -static MA_INLINE HRESULT ma_IAudioCaptureClient_GetNextPacketSize(ma_IAudioCaptureClient* pThis, ma_uint32* pNumFramesInNextPacket) { return pThis->lpVtbl->GetNextPacketSize(pThis, pNumFramesInNextPacket); } - -#if defined(MA_WIN32_UWP) -/* mmdevapi Functions */ -typedef HRESULT (WINAPI * MA_PFN_ActivateAudioInterfaceAsync)(const wchar_t* deviceInterfacePath, const IID* riid, MA_PROPVARIANT* activationParams, ma_IActivateAudioInterfaceCompletionHandler* completionHandler, ma_IActivateAudioInterfaceAsyncOperation** activationOperation); -#endif - -/* Avrt Functions */ -typedef HANDLE (WINAPI * MA_PFN_AvSetMmThreadCharacteristicsA)(const char* TaskName, DWORD* TaskIndex); -typedef BOOL (WINAPI * MA_PFN_AvRevertMmThreadCharacteristics)(HANDLE AvrtHandle); - -#if !defined(MA_WIN32_DESKTOP) && !defined(MA_WIN32_GDK) -typedef struct ma_completion_handler_uwp ma_completion_handler_uwp; - -typedef struct -{ - /* IUnknown */ - HRESULT (STDMETHODCALLTYPE * QueryInterface)(ma_completion_handler_uwp* pThis, const IID* const riid, void** ppObject); - ULONG (STDMETHODCALLTYPE * AddRef) (ma_completion_handler_uwp* pThis); - ULONG (STDMETHODCALLTYPE * Release) (ma_completion_handler_uwp* pThis); - - /* IActivateAudioInterfaceCompletionHandler */ - HRESULT (STDMETHODCALLTYPE * ActivateCompleted)(ma_completion_handler_uwp* pThis, ma_IActivateAudioInterfaceAsyncOperation* pActivateOperation); -} ma_completion_handler_uwp_vtbl; -struct ma_completion_handler_uwp -{ - ma_completion_handler_uwp_vtbl* lpVtbl; - MA_ATOMIC(4, ma_uint32) counter; - HANDLE hEvent; -}; - -static HRESULT STDMETHODCALLTYPE ma_completion_handler_uwp_QueryInterface(ma_completion_handler_uwp* pThis, const IID* const riid, void** ppObject) -{ - /* - We need to "implement" IAgileObject which is just an indicator that's used internally by WASAPI for some multithreading management. To - "implement" this, we just make sure we return pThis when the IAgileObject is requested. - */ - if (!ma_is_guid_equal(riid, &MA_IID_IUnknown) && !ma_is_guid_equal(riid, &MA_IID_IActivateAudioInterfaceCompletionHandler) && !ma_is_guid_equal(riid, &MA_IID_IAgileObject)) { - *ppObject = NULL; - return E_NOINTERFACE; - } - - /* Getting here means the IID is IUnknown or IMMNotificationClient. */ - *ppObject = (void*)pThis; - ((ma_completion_handler_uwp_vtbl*)pThis->lpVtbl)->AddRef(pThis); - return S_OK; -} - -static ULONG STDMETHODCALLTYPE ma_completion_handler_uwp_AddRef(ma_completion_handler_uwp* pThis) -{ - return (ULONG)ma_atomic_fetch_add_32(&pThis->counter, 1) + 1; -} - -static ULONG STDMETHODCALLTYPE ma_completion_handler_uwp_Release(ma_completion_handler_uwp* pThis) -{ - ma_uint32 newRefCount = ma_atomic_fetch_sub_32(&pThis->counter, 1) - 1; - if (newRefCount == 0) { - return 0; /* We don't free anything here because we never allocate the object on the heap. */ - } - - return (ULONG)newRefCount; -} - -static HRESULT STDMETHODCALLTYPE ma_completion_handler_uwp_ActivateCompleted(ma_completion_handler_uwp* pThis, ma_IActivateAudioInterfaceAsyncOperation* pActivateOperation) -{ - (void)pActivateOperation; - SetEvent(pThis->hEvent); - return S_OK; -} - - -static ma_completion_handler_uwp_vtbl g_maCompletionHandlerVtblInstance = { - ma_completion_handler_uwp_QueryInterface, - ma_completion_handler_uwp_AddRef, - ma_completion_handler_uwp_Release, - ma_completion_handler_uwp_ActivateCompleted -}; - -static ma_result ma_completion_handler_uwp_init(ma_completion_handler_uwp* pHandler) -{ - MA_ASSERT(pHandler != NULL); - MA_ZERO_OBJECT(pHandler); - - pHandler->lpVtbl = &g_maCompletionHandlerVtblInstance; - pHandler->counter = 1; - pHandler->hEvent = CreateEventA(NULL, FALSE, FALSE, NULL); - if (pHandler->hEvent == NULL) { - return ma_result_from_GetLastError(GetLastError()); - } - - return MA_SUCCESS; -} - -static void ma_completion_handler_uwp_uninit(ma_completion_handler_uwp* pHandler) -{ - if (pHandler->hEvent != NULL) { - CloseHandle(pHandler->hEvent); - } -} - -static void ma_completion_handler_uwp_wait(ma_completion_handler_uwp* pHandler) -{ - WaitForSingleObject((HANDLE)pHandler->hEvent, INFINITE); -} -#endif /* !MA_WIN32_DESKTOP */ - -/* We need a virtual table for our notification client object that's used for detecting changes to the default device. */ -#if defined(MA_WIN32_DESKTOP) || defined(MA_WIN32_GDK) -static HRESULT STDMETHODCALLTYPE ma_IMMNotificationClient_QueryInterface(ma_IMMNotificationClient* pThis, const IID* const riid, void** ppObject) -{ - /* - We care about two interfaces - IUnknown and IMMNotificationClient. If the requested IID is something else - we just return E_NOINTERFACE. Otherwise we need to increment the reference counter and return S_OK. - */ - if (!ma_is_guid_equal(riid, &MA_IID_IUnknown) && !ma_is_guid_equal(riid, &MA_IID_IMMNotificationClient)) { - *ppObject = NULL; - return E_NOINTERFACE; - } - - /* Getting here means the IID is IUnknown or IMMNotificationClient. */ - *ppObject = (void*)pThis; - ((ma_IMMNotificationClientVtbl*)pThis->lpVtbl)->AddRef(pThis); - return S_OK; -} - -static ULONG STDMETHODCALLTYPE ma_IMMNotificationClient_AddRef(ma_IMMNotificationClient* pThis) -{ - return (ULONG)ma_atomic_fetch_add_32(&pThis->counter, 1) + 1; -} - -static ULONG STDMETHODCALLTYPE ma_IMMNotificationClient_Release(ma_IMMNotificationClient* pThis) -{ - ma_uint32 newRefCount = ma_atomic_fetch_sub_32(&pThis->counter, 1) - 1; - if (newRefCount == 0) { - return 0; /* We don't free anything here because we never allocate the object on the heap. */ - } - - return (ULONG)newRefCount; -} - -static HRESULT STDMETHODCALLTYPE ma_IMMNotificationClient_OnDeviceStateChanged(ma_IMMNotificationClient* pThis, const WCHAR* pDeviceID, DWORD dwNewState) -{ - ma_bool32 isThisDevice = MA_FALSE; - ma_bool32 isCapture = MA_FALSE; - ma_bool32 isPlayback = MA_FALSE; - -#ifdef MA_DEBUG_OUTPUT - /*ma_log_postf(ma_device_get_log(pThis->pDevice), MA_LOG_LEVEL_DEBUG, "IMMNotificationClient_OnDeviceStateChanged(pDeviceID=%S, dwNewState=%u)\n", (pDeviceID != NULL) ? pDeviceID : L"(NULL)", (unsigned int)dwNewState);*/ -#endif - - /* - There have been reports of a hang when a playback device is disconnected. The idea with this code is to explicitly stop the device if we detect - that the device is disabled or has been unplugged. - */ - if (pThis->pDevice->wasapi.allowCaptureAutoStreamRouting && (pThis->pDevice->type == ma_device_type_capture || pThis->pDevice->type == ma_device_type_duplex || pThis->pDevice->type == ma_device_type_loopback)) { - isCapture = MA_TRUE; - if (ma_strcmp_WCHAR(pThis->pDevice->capture.id.wasapi, pDeviceID) == 0) { - isThisDevice = MA_TRUE; - } - } - - if (pThis->pDevice->wasapi.allowPlaybackAutoStreamRouting && (pThis->pDevice->type == ma_device_type_playback || pThis->pDevice->type == ma_device_type_duplex)) { - isPlayback = MA_TRUE; - if (ma_strcmp_WCHAR(pThis->pDevice->playback.id.wasapi, pDeviceID) == 0) { - isThisDevice = MA_TRUE; - } - } - - - /* - If the device ID matches our device we need to mark our device as detached and stop it. When a - device is added in OnDeviceAdded(), we'll restart it. We only mark it as detached if the device - was started at the time of being removed. - */ - if (isThisDevice) { - if ((dwNewState & MA_MM_DEVICE_STATE_ACTIVE) == 0) { - /* - Unplugged or otherwise unavailable. Mark as detached if we were in a playing state. We'll - use this to determine whether or not we need to automatically start the device when it's - plugged back in again. - */ - if (ma_device_get_state(pThis->pDevice) == ma_device_state_started) { - if (isPlayback) { - pThis->pDevice->wasapi.isDetachedPlayback = MA_TRUE; - } - if (isCapture) { - pThis->pDevice->wasapi.isDetachedCapture = MA_TRUE; - } - - ma_device_stop(pThis->pDevice); - } - } - - if ((dwNewState & MA_MM_DEVICE_STATE_ACTIVE) != 0) { - /* The device was activated. If we were detached, we need to start it again. */ - ma_bool8 tryRestartingDevice = MA_FALSE; - - if (isPlayback) { - if (pThis->pDevice->wasapi.isDetachedPlayback) { - pThis->pDevice->wasapi.isDetachedPlayback = MA_FALSE; - ma_device_reroute__wasapi(pThis->pDevice, ma_device_type_playback); - tryRestartingDevice = MA_TRUE; - } - } - - if (isCapture) { - if (pThis->pDevice->wasapi.isDetachedCapture) { - pThis->pDevice->wasapi.isDetachedCapture = MA_FALSE; - ma_device_reroute__wasapi(pThis->pDevice, (pThis->pDevice->type == ma_device_type_loopback) ? ma_device_type_loopback : ma_device_type_capture); - tryRestartingDevice = MA_TRUE; - } - } - - if (tryRestartingDevice) { - if (pThis->pDevice->wasapi.isDetachedPlayback == MA_FALSE && pThis->pDevice->wasapi.isDetachedCapture == MA_FALSE) { - ma_device_start(pThis->pDevice); - } - } - } - } - - return S_OK; -} - -static HRESULT STDMETHODCALLTYPE ma_IMMNotificationClient_OnDeviceAdded(ma_IMMNotificationClient* pThis, const WCHAR* pDeviceID) -{ -#ifdef MA_DEBUG_OUTPUT - /*ma_log_postf(ma_device_get_log(pThis->pDevice), MA_LOG_LEVEL_DEBUG, "IMMNotificationClient_OnDeviceAdded(pDeviceID=%S)\n", (pDeviceID != NULL) ? pDeviceID : L"(NULL)");*/ -#endif - - /* We don't need to worry about this event for our purposes. */ - (void)pThis; - (void)pDeviceID; - return S_OK; -} - -static HRESULT STDMETHODCALLTYPE ma_IMMNotificationClient_OnDeviceRemoved(ma_IMMNotificationClient* pThis, const WCHAR* pDeviceID) -{ -#ifdef MA_DEBUG_OUTPUT - /*ma_log_postf(ma_device_get_log(pThis->pDevice), MA_LOG_LEVEL_DEBUG, "IMMNotificationClient_OnDeviceRemoved(pDeviceID=%S)\n", (pDeviceID != NULL) ? pDeviceID : L"(NULL)");*/ -#endif - - /* We don't need to worry about this event for our purposes. */ - (void)pThis; - (void)pDeviceID; - return S_OK; -} - -static HRESULT STDMETHODCALLTYPE ma_IMMNotificationClient_OnDefaultDeviceChanged(ma_IMMNotificationClient* pThis, ma_EDataFlow dataFlow, ma_ERole role, const WCHAR* pDefaultDeviceID) -{ -#ifdef MA_DEBUG_OUTPUT - /*ma_log_postf(ma_device_get_log(pThis->pDevice), MA_LOG_LEVEL_DEBUG, "IMMNotificationClient_OnDefaultDeviceChanged(dataFlow=%d, role=%d, pDefaultDeviceID=%S)\n", dataFlow, role, (pDefaultDeviceID != NULL) ? pDefaultDeviceID : L"(NULL)");*/ -#endif - - (void)role; - - /* We only care about devices with the same data flow as the current device. */ - if ((pThis->pDevice->type == ma_device_type_playback && dataFlow != ma_eRender) || - (pThis->pDevice->type == ma_device_type_capture && dataFlow != ma_eCapture) || - (pThis->pDevice->type == ma_device_type_loopback && dataFlow != ma_eRender)) { - ma_log_postf(ma_device_get_log(pThis->pDevice), MA_LOG_LEVEL_DEBUG, "[WASAPI] Stream rerouting abandoned because dataFlow does match device type.\n"); - return S_OK; - } - - /* We need to consider dataFlow as ma_eCapture if device is ma_device_type_loopback */ - if (pThis->pDevice->type == ma_device_type_loopback) { - dataFlow = ma_eCapture; - } - - /* Don't do automatic stream routing if we're not allowed. */ - if ((dataFlow == ma_eRender && pThis->pDevice->wasapi.allowPlaybackAutoStreamRouting == MA_FALSE) || - (dataFlow == ma_eCapture && pThis->pDevice->wasapi.allowCaptureAutoStreamRouting == MA_FALSE)) { - ma_log_postf(ma_device_get_log(pThis->pDevice), MA_LOG_LEVEL_DEBUG, "[WASAPI] Stream rerouting abandoned because automatic stream routing has been disabled by the device config.\n"); - return S_OK; - } - - /* - Not currently supporting automatic stream routing in exclusive mode. This is not working correctly on my machine due to - AUDCLNT_E_DEVICE_IN_USE errors when reinitializing the device. If this is a bug in miniaudio, we can try re-enabling this once - it's fixed. - */ - if ((dataFlow == ma_eRender && pThis->pDevice->playback.shareMode == ma_share_mode_exclusive) || - (dataFlow == ma_eCapture && pThis->pDevice->capture.shareMode == ma_share_mode_exclusive)) { - ma_log_postf(ma_device_get_log(pThis->pDevice), MA_LOG_LEVEL_DEBUG, "[WASAPI] Stream rerouting abandoned because the device shared mode is exclusive.\n"); - return S_OK; - } - - - - /* - Second attempt at device rerouting. We're going to retrieve the device's state at the time of - the route change. We're then going to stop the device, reinitialize the device, and then start - it again if the state before stopping was ma_device_state_started. - */ - { - ma_uint32 previousState = ma_device_get_state(pThis->pDevice); - ma_bool8 restartDevice = MA_FALSE; - - if (previousState == ma_device_state_uninitialized || previousState == ma_device_state_starting) { - ma_log_postf(ma_device_get_log(pThis->pDevice), MA_LOG_LEVEL_DEBUG, "[WASAPI] Stream rerouting abandoned because the device is in the process of starting.\n"); - return S_OK; - } - - if (previousState == ma_device_state_started) { - ma_device_stop(pThis->pDevice); - restartDevice = MA_TRUE; - } - - if (pDefaultDeviceID != NULL) { /* <-- The input device ID will be null if there's no other device available. */ - ma_mutex_lock(&pThis->pDevice->wasapi.rerouteLock); - { - if (dataFlow == ma_eRender) { - ma_device_reroute__wasapi(pThis->pDevice, ma_device_type_playback); - - if (pThis->pDevice->wasapi.isDetachedPlayback) { - pThis->pDevice->wasapi.isDetachedPlayback = MA_FALSE; - - if (pThis->pDevice->type == ma_device_type_duplex && pThis->pDevice->wasapi.isDetachedCapture) { - restartDevice = MA_FALSE; /* It's a duplex device and the capture side is detached. We cannot be restarting the device just yet. */ - } - else { - restartDevice = MA_TRUE; /* It's not a duplex device, or the capture side is also attached so we can go ahead and restart the device. */ - } - } - } - else { - ma_device_reroute__wasapi(pThis->pDevice, (pThis->pDevice->type == ma_device_type_loopback) ? ma_device_type_loopback : ma_device_type_capture); - - if (pThis->pDevice->wasapi.isDetachedCapture) { - pThis->pDevice->wasapi.isDetachedCapture = MA_FALSE; - - if (pThis->pDevice->type == ma_device_type_duplex && pThis->pDevice->wasapi.isDetachedPlayback) { - restartDevice = MA_FALSE; /* It's a duplex device and the playback side is detached. We cannot be restarting the device just yet. */ - } - else { - restartDevice = MA_TRUE; /* It's not a duplex device, or the playback side is also attached so we can go ahead and restart the device. */ - } - } - } - } - ma_mutex_unlock(&pThis->pDevice->wasapi.rerouteLock); - - if (restartDevice) { - ma_device_start(pThis->pDevice); - } - } - } - - return S_OK; -} - -static HRESULT STDMETHODCALLTYPE ma_IMMNotificationClient_OnPropertyValueChanged(ma_IMMNotificationClient* pThis, const WCHAR* pDeviceID, const PROPERTYKEY key) -{ -#ifdef MA_DEBUG_OUTPUT - /*ma_log_postf(ma_device_get_log(pThis->pDevice), MA_LOG_LEVEL_DEBUG, "IMMNotificationClient_OnPropertyValueChanged(pDeviceID=%S)\n", (pDeviceID != NULL) ? pDeviceID : L"(NULL)");*/ -#endif - - (void)pThis; - (void)pDeviceID; - (void)key; - return S_OK; -} - -static ma_IMMNotificationClientVtbl g_maNotificationCientVtbl = { - ma_IMMNotificationClient_QueryInterface, - ma_IMMNotificationClient_AddRef, - ma_IMMNotificationClient_Release, - ma_IMMNotificationClient_OnDeviceStateChanged, - ma_IMMNotificationClient_OnDeviceAdded, - ma_IMMNotificationClient_OnDeviceRemoved, - ma_IMMNotificationClient_OnDefaultDeviceChanged, - ma_IMMNotificationClient_OnPropertyValueChanged -}; -#endif /* MA_WIN32_DESKTOP */ - -static const char* ma_to_usage_string__wasapi(ma_wasapi_usage usage) -{ - switch (usage) - { - case ma_wasapi_usage_default: return NULL; - case ma_wasapi_usage_games: return "Games"; - case ma_wasapi_usage_pro_audio: return "Pro Audio"; - default: break; - } - - return NULL; -} - -#if defined(MA_WIN32_DESKTOP) || defined(MA_WIN32_GDK) -typedef ma_IMMDevice ma_WASAPIDeviceInterface; -#else -typedef ma_IUnknown ma_WASAPIDeviceInterface; -#endif - - -#define MA_CONTEXT_COMMAND_QUIT__WASAPI 1 -#define MA_CONTEXT_COMMAND_CREATE_IAUDIOCLIENT__WASAPI 2 -#define MA_CONTEXT_COMMAND_RELEASE_IAUDIOCLIENT__WASAPI 3 - -static ma_context_command__wasapi ma_context_init_command__wasapi(int code) -{ - ma_context_command__wasapi cmd; - - MA_ZERO_OBJECT(&cmd); - cmd.code = code; - - return cmd; -} - -static ma_result ma_context_post_command__wasapi(ma_context* pContext, const ma_context_command__wasapi* pCmd) -{ - /* For now we are doing everything synchronously, but I might relax this later if the need arises. */ - ma_result result; - ma_bool32 isUsingLocalEvent = MA_FALSE; - ma_event localEvent; - - MA_ASSERT(pContext != NULL); - MA_ASSERT(pCmd != NULL); - - if (pCmd->pEvent == NULL) { - isUsingLocalEvent = MA_TRUE; - - result = ma_event_init(&localEvent); - if (result != MA_SUCCESS) { - return result; /* Failed to create the event for this command. */ - } - } - - /* Here is where we add the command to the list. If there's not enough room we'll spin until there is. */ - ma_mutex_lock(&pContext->wasapi.commandLock); - { - ma_uint32 index; - - /* Spin until we've got some space available. */ - while (pContext->wasapi.commandCount == ma_countof(pContext->wasapi.commands)) { - ma_yield(); - } - - /* Space is now available. Can safely add to the list. */ - index = (pContext->wasapi.commandIndex + pContext->wasapi.commandCount) % ma_countof(pContext->wasapi.commands); - pContext->wasapi.commands[index] = *pCmd; - pContext->wasapi.commands[index].pEvent = &localEvent; - pContext->wasapi.commandCount += 1; - - /* Now that the command has been added, release the semaphore so ma_context_next_command__wasapi() can return. */ - ma_semaphore_release(&pContext->wasapi.commandSem); - } - ma_mutex_unlock(&pContext->wasapi.commandLock); - - if (isUsingLocalEvent) { - ma_event_wait(&localEvent); - ma_event_uninit(&localEvent); - } - - return MA_SUCCESS; -} - -static ma_result ma_context_next_command__wasapi(ma_context* pContext, ma_context_command__wasapi* pCmd) -{ - ma_result result = MA_SUCCESS; - - MA_ASSERT(pContext != NULL); - MA_ASSERT(pCmd != NULL); - - result = ma_semaphore_wait(&pContext->wasapi.commandSem); - if (result == MA_SUCCESS) { - ma_mutex_lock(&pContext->wasapi.commandLock); - { - *pCmd = pContext->wasapi.commands[pContext->wasapi.commandIndex]; - pContext->wasapi.commandIndex = (pContext->wasapi.commandIndex + 1) % ma_countof(pContext->wasapi.commands); - pContext->wasapi.commandCount -= 1; - } - ma_mutex_unlock(&pContext->wasapi.commandLock); - } - - return result; -} - -static ma_thread_result MA_THREADCALL ma_context_command_thread__wasapi(void* pUserData) -{ - ma_result result; - ma_context* pContext = (ma_context*)pUserData; - MA_ASSERT(pContext != NULL); - - for (;;) { - ma_context_command__wasapi cmd; - result = ma_context_next_command__wasapi(pContext, &cmd); - if (result != MA_SUCCESS) { - break; - } - - switch (cmd.code) - { - case MA_CONTEXT_COMMAND_QUIT__WASAPI: - { - /* Do nothing. Handled after the switch. */ - } break; - - case MA_CONTEXT_COMMAND_CREATE_IAUDIOCLIENT__WASAPI: - { - if (cmd.data.createAudioClient.deviceType == ma_device_type_playback) { - *cmd.data.createAudioClient.pResult = ma_result_from_HRESULT(ma_IAudioClient_GetService((ma_IAudioClient*)cmd.data.createAudioClient.pAudioClient, &MA_IID_IAudioRenderClient, cmd.data.createAudioClient.ppAudioClientService)); - } else { - *cmd.data.createAudioClient.pResult = ma_result_from_HRESULT(ma_IAudioClient_GetService((ma_IAudioClient*)cmd.data.createAudioClient.pAudioClient, &MA_IID_IAudioCaptureClient, cmd.data.createAudioClient.ppAudioClientService)); - } - } break; - - case MA_CONTEXT_COMMAND_RELEASE_IAUDIOCLIENT__WASAPI: - { - if (cmd.data.releaseAudioClient.deviceType == ma_device_type_playback) { - if (cmd.data.releaseAudioClient.pDevice->wasapi.pAudioClientPlayback != NULL) { - ma_IAudioClient_Release((ma_IAudioClient*)cmd.data.releaseAudioClient.pDevice->wasapi.pAudioClientPlayback); - cmd.data.releaseAudioClient.pDevice->wasapi.pAudioClientPlayback = NULL; - } - } - - if (cmd.data.releaseAudioClient.deviceType == ma_device_type_capture) { - if (cmd.data.releaseAudioClient.pDevice->wasapi.pAudioClientCapture != NULL) { - ma_IAudioClient_Release((ma_IAudioClient*)cmd.data.releaseAudioClient.pDevice->wasapi.pAudioClientCapture); - cmd.data.releaseAudioClient.pDevice->wasapi.pAudioClientCapture = NULL; - } - } - } break; - - default: - { - /* Unknown command. Ignore it, but trigger an assert in debug mode so we're aware of it. */ - MA_ASSERT(MA_FALSE); - } break; - } - - if (cmd.pEvent != NULL) { - ma_event_signal(cmd.pEvent); - } - - if (cmd.code == MA_CONTEXT_COMMAND_QUIT__WASAPI) { - break; /* Received a quit message. Get out of here. */ - } - } - - return (ma_thread_result)0; -} - -static ma_result ma_device_create_IAudioClient_service__wasapi(ma_context* pContext, ma_device_type deviceType, ma_IAudioClient* pAudioClient, void** ppAudioClientService) -{ - ma_result result; - ma_result cmdResult; - ma_context_command__wasapi cmd = ma_context_init_command__wasapi(MA_CONTEXT_COMMAND_CREATE_IAUDIOCLIENT__WASAPI); - cmd.data.createAudioClient.deviceType = deviceType; - cmd.data.createAudioClient.pAudioClient = (void*)pAudioClient; - cmd.data.createAudioClient.ppAudioClientService = ppAudioClientService; - cmd.data.createAudioClient.pResult = &cmdResult; /* Declared locally, but won't be dereferenced after this function returns since execution of the command will wait here. */ - - result = ma_context_post_command__wasapi(pContext, &cmd); /* This will not return until the command has actually been run. */ - if (result != MA_SUCCESS) { - return result; - } - - return *cmd.data.createAudioClient.pResult; -} - -#if 0 /* Not used at the moment, but leaving here for future use. */ -static ma_result ma_device_release_IAudioClient_service__wasapi(ma_device* pDevice, ma_device_type deviceType) -{ - ma_result result; - ma_context_command__wasapi cmd = ma_context_init_command__wasapi(MA_CONTEXT_COMMAND_RELEASE_IAUDIOCLIENT__WASAPI); - cmd.data.releaseAudioClient.pDevice = pDevice; - cmd.data.releaseAudioClient.deviceType = deviceType; - - result = ma_context_post_command__wasapi(pDevice->pContext, &cmd); /* This will not return until the command has actually been run. */ - if (result != MA_SUCCESS) { - return result; - } - - return MA_SUCCESS; -} -#endif - - -static void ma_add_native_data_format_to_device_info_from_WAVEFORMATEX(const MA_WAVEFORMATEX* pWF, ma_share_mode shareMode, ma_device_info* pInfo) -{ - MA_ASSERT(pWF != NULL); - MA_ASSERT(pInfo != NULL); - - if (pInfo->nativeDataFormatCount >= ma_countof(pInfo->nativeDataFormats)) { - return; /* Too many data formats. Need to ignore this one. Don't think this should ever happen with WASAPI. */ - } - - pInfo->nativeDataFormats[pInfo->nativeDataFormatCount].format = ma_format_from_WAVEFORMATEX(pWF); - pInfo->nativeDataFormats[pInfo->nativeDataFormatCount].channels = pWF->nChannels; - pInfo->nativeDataFormats[pInfo->nativeDataFormatCount].sampleRate = pWF->nSamplesPerSec; - pInfo->nativeDataFormats[pInfo->nativeDataFormatCount].flags = (shareMode == ma_share_mode_exclusive) ? MA_DATA_FORMAT_FLAG_EXCLUSIVE_MODE : 0; - pInfo->nativeDataFormatCount += 1; -} - -static ma_result ma_context_get_device_info_from_IAudioClient__wasapi(ma_context* pContext, /*ma_IMMDevice**/void* pMMDevice, ma_IAudioClient* pAudioClient, ma_device_info* pInfo) -{ - HRESULT hr; - MA_WAVEFORMATEX* pWF = NULL; - - MA_ASSERT(pAudioClient != NULL); - MA_ASSERT(pInfo != NULL); - - /* Shared Mode. We use GetMixFormat() here. */ - hr = ma_IAudioClient_GetMixFormat((ma_IAudioClient*)pAudioClient, (MA_WAVEFORMATEX**)&pWF); - if (SUCCEEDED(hr)) { - ma_add_native_data_format_to_device_info_from_WAVEFORMATEX(pWF, ma_share_mode_shared, pInfo); - } else { - ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to retrieve mix format for device info retrieval."); - return ma_result_from_HRESULT(hr); - } - - /* - Exclusive Mode. We repeatedly call IsFormatSupported() here. This is not currently supported on - UWP. Failure to retrieve the exclusive mode format is not considered an error, so from here on - out, MA_SUCCESS is guaranteed to be returned. - */ - #if defined(MA_WIN32_DESKTOP) || defined(MA_WIN32_GDK) - { - ma_IPropertyStore *pProperties; - - /* - The first thing to do is get the format from PKEY_AudioEngine_DeviceFormat. This should give us a channel count we assume is - correct which will simplify our searching. - */ - hr = ma_IMMDevice_OpenPropertyStore((ma_IMMDevice*)pMMDevice, STGM_READ, &pProperties); - if (SUCCEEDED(hr)) { - MA_PROPVARIANT var; - ma_PropVariantInit(&var); - - hr = ma_IPropertyStore_GetValue(pProperties, &MA_PKEY_AudioEngine_DeviceFormat, &var); - if (SUCCEEDED(hr)) { - pWF = (MA_WAVEFORMATEX*)var.blob.pBlobData; - - /* - In my testing, the format returned by PKEY_AudioEngine_DeviceFormat is suitable for exclusive mode so we check this format - first. If this fails, fall back to a search. - */ - hr = ma_IAudioClient_IsFormatSupported((ma_IAudioClient*)pAudioClient, MA_AUDCLNT_SHAREMODE_EXCLUSIVE, pWF, NULL); - if (SUCCEEDED(hr)) { - /* The format returned by PKEY_AudioEngine_DeviceFormat is supported. */ - ma_add_native_data_format_to_device_info_from_WAVEFORMATEX(pWF, ma_share_mode_exclusive, pInfo); - } else { - /* - The format returned by PKEY_AudioEngine_DeviceFormat is not supported, so fall back to a search. We assume the channel - count returned by MA_PKEY_AudioEngine_DeviceFormat is valid and correct. For simplicity we're only returning one format. - */ - ma_uint32 channels = pWF->nChannels; - ma_channel defaultChannelMap[MA_MAX_CHANNELS]; - MA_WAVEFORMATEXTENSIBLE wf; - ma_bool32 found; - ma_uint32 iFormat; - - /* Make sure we don't overflow the channel map. */ - if (channels > MA_MAX_CHANNELS) { - channels = MA_MAX_CHANNELS; - } - - ma_channel_map_init_standard(ma_standard_channel_map_microsoft, defaultChannelMap, ma_countof(defaultChannelMap), channels); - - MA_ZERO_OBJECT(&wf); - wf.cbSize = sizeof(wf); - wf.wFormatTag = WAVE_FORMAT_EXTENSIBLE; - wf.nChannels = (WORD)channels; - wf.dwChannelMask = ma_channel_map_to_channel_mask__win32(defaultChannelMap, channels); - - found = MA_FALSE; - for (iFormat = 0; iFormat < ma_countof(g_maFormatPriorities); ++iFormat) { - ma_format format = g_maFormatPriorities[iFormat]; - ma_uint32 iSampleRate; - - wf.wBitsPerSample = (WORD)(ma_get_bytes_per_sample(format)*8); - wf.nBlockAlign = (WORD)(wf.nChannels * wf.wBitsPerSample / 8); - wf.nAvgBytesPerSec = wf.nBlockAlign * wf.nSamplesPerSec; - wf.Samples.wValidBitsPerSample = /*(format == ma_format_s24_32) ? 24 :*/ wf.wBitsPerSample; - if (format == ma_format_f32) { - wf.SubFormat = MA_GUID_KSDATAFORMAT_SUBTYPE_IEEE_FLOAT; - } else { - wf.SubFormat = MA_GUID_KSDATAFORMAT_SUBTYPE_PCM; - } - - for (iSampleRate = 0; iSampleRate < ma_countof(g_maStandardSampleRatePriorities); ++iSampleRate) { - wf.nSamplesPerSec = g_maStandardSampleRatePriorities[iSampleRate]; - - hr = ma_IAudioClient_IsFormatSupported((ma_IAudioClient*)pAudioClient, MA_AUDCLNT_SHAREMODE_EXCLUSIVE, (MA_WAVEFORMATEX*)&wf, NULL); - if (SUCCEEDED(hr)) { - ma_add_native_data_format_to_device_info_from_WAVEFORMATEX((MA_WAVEFORMATEX*)&wf, ma_share_mode_exclusive, pInfo); - found = MA_TRUE; - break; - } - } - - if (found) { - break; - } - } - - ma_PropVariantClear(pContext, &var); - - if (!found) { - ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_WARNING, "[WASAPI] Failed to find suitable device format for device info retrieval."); - } - } - } else { - ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_WARNING, "[WASAPI] Failed to retrieve device format for device info retrieval."); - } - - ma_IPropertyStore_Release(pProperties); - } else { - ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_WARNING, "[WASAPI] Failed to open property store for device info retrieval."); - } - } - #else - { - (void)pMMDevice; /* Unused. */ - } - #endif - - return MA_SUCCESS; -} - -#if defined(MA_WIN32_DESKTOP) || defined(MA_WIN32_GDK) -static ma_EDataFlow ma_device_type_to_EDataFlow(ma_device_type deviceType) -{ - if (deviceType == ma_device_type_playback) { - return ma_eRender; - } else if (deviceType == ma_device_type_capture) { - return ma_eCapture; - } else { - MA_ASSERT(MA_FALSE); - return ma_eRender; /* Should never hit this. */ - } -} - -static ma_result ma_context_create_IMMDeviceEnumerator__wasapi(ma_context* pContext, ma_IMMDeviceEnumerator** ppDeviceEnumerator) -{ - HRESULT hr; - ma_IMMDeviceEnumerator* pDeviceEnumerator; - - MA_ASSERT(pContext != NULL); - MA_ASSERT(ppDeviceEnumerator != NULL); - - *ppDeviceEnumerator = NULL; /* Safety. */ - - hr = ma_CoCreateInstance(pContext, &MA_CLSID_MMDeviceEnumerator, NULL, CLSCTX_ALL, &MA_IID_IMMDeviceEnumerator, (void**)&pDeviceEnumerator); - if (FAILED(hr)) { - ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to create device enumerator."); - return ma_result_from_HRESULT(hr); - } - - *ppDeviceEnumerator = pDeviceEnumerator; - - return MA_SUCCESS; -} - -static WCHAR* ma_context_get_default_device_id_from_IMMDeviceEnumerator__wasapi(ma_context* pContext, ma_IMMDeviceEnumerator* pDeviceEnumerator, ma_device_type deviceType) -{ - HRESULT hr; - ma_IMMDevice* pMMDefaultDevice = NULL; - WCHAR* pDefaultDeviceID = NULL; - ma_EDataFlow dataFlow; - ma_ERole role; - - MA_ASSERT(pContext != NULL); - MA_ASSERT(pDeviceEnumerator != NULL); - - (void)pContext; - - /* Grab the EDataFlow type from the device type. */ - dataFlow = ma_device_type_to_EDataFlow(deviceType); - - /* The role is always eConsole, but we may make this configurable later. */ - role = ma_eConsole; - - hr = ma_IMMDeviceEnumerator_GetDefaultAudioEndpoint(pDeviceEnumerator, dataFlow, role, &pMMDefaultDevice); - if (FAILED(hr)) { - return NULL; - } - - hr = ma_IMMDevice_GetId(pMMDefaultDevice, &pDefaultDeviceID); - - ma_IMMDevice_Release(pMMDefaultDevice); - pMMDefaultDevice = NULL; - - if (FAILED(hr)) { - return NULL; - } - - return pDefaultDeviceID; -} - -static WCHAR* ma_context_get_default_device_id__wasapi(ma_context* pContext, ma_device_type deviceType) /* Free the returned pointer with ma_CoTaskMemFree() */ -{ - ma_result result; - ma_IMMDeviceEnumerator* pDeviceEnumerator; - WCHAR* pDefaultDeviceID = NULL; - - MA_ASSERT(pContext != NULL); - - result = ma_context_create_IMMDeviceEnumerator__wasapi(pContext, &pDeviceEnumerator); - if (result != MA_SUCCESS) { - return NULL; - } - - pDefaultDeviceID = ma_context_get_default_device_id_from_IMMDeviceEnumerator__wasapi(pContext, pDeviceEnumerator, deviceType); - - ma_IMMDeviceEnumerator_Release(pDeviceEnumerator); - return pDefaultDeviceID; -} - -static ma_result ma_context_get_MMDevice__wasapi(ma_context* pContext, ma_device_type deviceType, const ma_device_id* pDeviceID, ma_IMMDevice** ppMMDevice) -{ - ma_IMMDeviceEnumerator* pDeviceEnumerator; - HRESULT hr; - - MA_ASSERT(pContext != NULL); - MA_ASSERT(ppMMDevice != NULL); - - /* - This weird COM init/uninit here is a hack to work around a crash when changing devices. What is happening is - WASAPI fires a callback from another thread when the device is changed. It's from that thread where this - function is getting called. What I'm suspecting is that the other thread is not initializing COM which in turn - results in CoCreateInstance() failing. - - The community has reported that this seems to fix the crash. There are future plans to move all WASAPI operation - over to a single thread to make everything safer, but in the meantime while we wait for that to come online I'm - happy enough to use this hack instead. - */ - ma_CoInitializeEx(pContext, NULL, MA_COINIT_VALUE); - { - hr = ma_CoCreateInstance(pContext, &MA_CLSID_MMDeviceEnumerator, NULL, CLSCTX_ALL, &MA_IID_IMMDeviceEnumerator, (void**)&pDeviceEnumerator); - } - ma_CoUninitialize(pContext); - - if (FAILED(hr)) { /* <-- This is checking the call above to ma_CoCreateInstance(). */ - ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to create IMMDeviceEnumerator.\n"); - return ma_result_from_HRESULT(hr); - } - - if (pDeviceID == NULL) { - hr = ma_IMMDeviceEnumerator_GetDefaultAudioEndpoint(pDeviceEnumerator, (deviceType == ma_device_type_capture) ? ma_eCapture : ma_eRender, ma_eConsole, ppMMDevice); - } else { - hr = ma_IMMDeviceEnumerator_GetDevice(pDeviceEnumerator, pDeviceID->wasapi, ppMMDevice); - } - - ma_IMMDeviceEnumerator_Release(pDeviceEnumerator); - if (FAILED(hr)) { - ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to retrieve IMMDevice.\n"); - return ma_result_from_HRESULT(hr); - } - - return MA_SUCCESS; -} - -static ma_result ma_context_get_device_id_from_MMDevice__wasapi(ma_context* pContext, ma_IMMDevice* pMMDevice, ma_device_id* pDeviceID) -{ - WCHAR* pDeviceIDString; - HRESULT hr; - - MA_ASSERT(pDeviceID != NULL); - - hr = ma_IMMDevice_GetId(pMMDevice, &pDeviceIDString); - if (SUCCEEDED(hr)) { - size_t idlen = ma_strlen_WCHAR(pDeviceIDString); - if (idlen+1 > ma_countof(pDeviceID->wasapi)) { - ma_CoTaskMemFree(pContext, pDeviceIDString); - MA_ASSERT(MA_FALSE); /* NOTE: If this is triggered, please report it. It means the format of the ID must have changed and is too long to fit in our fixed sized buffer. */ - return MA_ERROR; - } - - MA_COPY_MEMORY(pDeviceID->wasapi, pDeviceIDString, idlen * sizeof(wchar_t)); - pDeviceID->wasapi[idlen] = '\0'; - - ma_CoTaskMemFree(pContext, pDeviceIDString); - - return MA_SUCCESS; - } - - return MA_ERROR; -} - -static ma_result ma_context_get_device_info_from_MMDevice__wasapi(ma_context* pContext, ma_IMMDevice* pMMDevice, WCHAR* pDefaultDeviceID, ma_bool32 onlySimpleInfo, ma_device_info* pInfo) -{ - ma_result result; - HRESULT hr; - - MA_ASSERT(pContext != NULL); - MA_ASSERT(pMMDevice != NULL); - MA_ASSERT(pInfo != NULL); - - /* ID. */ - result = ma_context_get_device_id_from_MMDevice__wasapi(pContext, pMMDevice, &pInfo->id); - if (result == MA_SUCCESS) { - if (pDefaultDeviceID != NULL) { - if (ma_strcmp_WCHAR(pInfo->id.wasapi, pDefaultDeviceID) == 0) { - pInfo->isDefault = MA_TRUE; - } - } - } - - /* Description / Friendly Name */ - { - ma_IPropertyStore *pProperties; - hr = ma_IMMDevice_OpenPropertyStore(pMMDevice, STGM_READ, &pProperties); - if (SUCCEEDED(hr)) { - MA_PROPVARIANT var; - - ma_PropVariantInit(&var); - hr = ma_IPropertyStore_GetValue(pProperties, &MA_PKEY_Device_FriendlyName, &var); - if (SUCCEEDED(hr)) { - WideCharToMultiByte(CP_UTF8, 0, var.pwszVal, -1, pInfo->name, sizeof(pInfo->name), 0, FALSE); - ma_PropVariantClear(pContext, &var); - } - - ma_IPropertyStore_Release(pProperties); - } - } - - /* Format */ - if (!onlySimpleInfo) { - ma_IAudioClient* pAudioClient; - hr = ma_IMMDevice_Activate(pMMDevice, &MA_IID_IAudioClient, CLSCTX_ALL, NULL, (void**)&pAudioClient); - if (SUCCEEDED(hr)) { - result = ma_context_get_device_info_from_IAudioClient__wasapi(pContext, pMMDevice, pAudioClient, pInfo); - - ma_IAudioClient_Release(pAudioClient); - return result; - } else { - ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to activate audio client for device info retrieval."); - return ma_result_from_HRESULT(hr); - } - } - - return MA_SUCCESS; -} - -static ma_result ma_context_enumerate_devices_by_type__wasapi(ma_context* pContext, ma_IMMDeviceEnumerator* pDeviceEnumerator, ma_device_type deviceType, ma_enum_devices_callback_proc callback, void* pUserData) -{ - ma_result result = MA_SUCCESS; - UINT deviceCount; - HRESULT hr; - ma_uint32 iDevice; - WCHAR* pDefaultDeviceID = NULL; - ma_IMMDeviceCollection* pDeviceCollection = NULL; - - MA_ASSERT(pContext != NULL); - MA_ASSERT(callback != NULL); - - /* Grab the default device. We use this to know whether or not flag the returned device info as being the default. */ - pDefaultDeviceID = ma_context_get_default_device_id_from_IMMDeviceEnumerator__wasapi(pContext, pDeviceEnumerator, deviceType); - - /* We need to enumerate the devices which returns a device collection. */ - hr = ma_IMMDeviceEnumerator_EnumAudioEndpoints(pDeviceEnumerator, ma_device_type_to_EDataFlow(deviceType), MA_MM_DEVICE_STATE_ACTIVE, &pDeviceCollection); - if (SUCCEEDED(hr)) { - hr = ma_IMMDeviceCollection_GetCount(pDeviceCollection, &deviceCount); - if (FAILED(hr)) { - ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to get device count.\n"); - result = ma_result_from_HRESULT(hr); - goto done; - } - - for (iDevice = 0; iDevice < deviceCount; ++iDevice) { - ma_device_info deviceInfo; - ma_IMMDevice* pMMDevice; - - MA_ZERO_OBJECT(&deviceInfo); - - hr = ma_IMMDeviceCollection_Item(pDeviceCollection, iDevice, &pMMDevice); - if (SUCCEEDED(hr)) { - result = ma_context_get_device_info_from_MMDevice__wasapi(pContext, pMMDevice, pDefaultDeviceID, MA_TRUE, &deviceInfo); /* MA_TRUE = onlySimpleInfo. */ - - ma_IMMDevice_Release(pMMDevice); - if (result == MA_SUCCESS) { - ma_bool32 cbResult = callback(pContext, deviceType, &deviceInfo, pUserData); - if (cbResult == MA_FALSE) { - break; - } - } - } - } - } - -done: - if (pDefaultDeviceID != NULL) { - ma_CoTaskMemFree(pContext, pDefaultDeviceID); - pDefaultDeviceID = NULL; - } - - if (pDeviceCollection != NULL) { - ma_IMMDeviceCollection_Release(pDeviceCollection); - pDeviceCollection = NULL; - } - - return result; -} - -static ma_result ma_context_get_IAudioClient_Desktop__wasapi(ma_context* pContext, ma_device_type deviceType, const ma_device_id* pDeviceID, MA_PROPVARIANT* pActivationParams, ma_IAudioClient** ppAudioClient, ma_IMMDevice** ppMMDevice) -{ - ma_result result; - HRESULT hr; - - MA_ASSERT(pContext != NULL); - MA_ASSERT(ppAudioClient != NULL); - MA_ASSERT(ppMMDevice != NULL); - - result = ma_context_get_MMDevice__wasapi(pContext, deviceType, pDeviceID, ppMMDevice); - if (result != MA_SUCCESS) { - return result; - } - - hr = ma_IMMDevice_Activate(*ppMMDevice, &MA_IID_IAudioClient, CLSCTX_ALL, pActivationParams, (void**)ppAudioClient); - if (FAILED(hr)) { - return ma_result_from_HRESULT(hr); - } - - return MA_SUCCESS; -} -#else -static ma_result ma_context_get_IAudioClient_UWP__wasapi(ma_context* pContext, ma_device_type deviceType, const ma_device_id* pDeviceID, MA_PROPVARIANT* pActivationParams, ma_IAudioClient** ppAudioClient, ma_IUnknown** ppActivatedInterface) -{ - ma_IActivateAudioInterfaceAsyncOperation *pAsyncOp = NULL; - ma_completion_handler_uwp completionHandler; - IID iid; - WCHAR* iidStr; - HRESULT hr; - ma_result result; - HRESULT activateResult; - ma_IUnknown* pActivatedInterface; - - MA_ASSERT(pContext != NULL); - MA_ASSERT(ppAudioClient != NULL); - - if (pDeviceID != NULL) { - iidStr = (WCHAR*)pDeviceID->wasapi; - } else { - if (deviceType == ma_device_type_capture) { - iid = MA_IID_DEVINTERFACE_AUDIO_CAPTURE; - } else { - iid = MA_IID_DEVINTERFACE_AUDIO_RENDER; - } - - #if defined(__cplusplus) - hr = StringFromIID(iid, &iidStr); - #else - hr = StringFromIID(&iid, &iidStr); - #endif - if (FAILED(hr)) { - ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to convert device IID to string for ActivateAudioInterfaceAsync(). Out of memory.\n"); - return ma_result_from_HRESULT(hr); - } - } - - result = ma_completion_handler_uwp_init(&completionHandler); - if (result != MA_SUCCESS) { - ma_CoTaskMemFree(pContext, iidStr); - ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to create event for waiting for ActivateAudioInterfaceAsync().\n"); - return result; - } - - hr = ((MA_PFN_ActivateAudioInterfaceAsync)pContext->wasapi.ActivateAudioInterfaceAsync)(iidStr, &MA_IID_IAudioClient, pActivationParams, (ma_IActivateAudioInterfaceCompletionHandler*)&completionHandler, (ma_IActivateAudioInterfaceAsyncOperation**)&pAsyncOp); - if (FAILED(hr)) { - ma_completion_handler_uwp_uninit(&completionHandler); - ma_CoTaskMemFree(pContext, iidStr); - ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[WASAPI] ActivateAudioInterfaceAsync() failed.\n"); - return ma_result_from_HRESULT(hr); - } - - if (pDeviceID == NULL) { - ma_CoTaskMemFree(pContext, iidStr); - } - - /* Wait for the async operation for finish. */ - ma_completion_handler_uwp_wait(&completionHandler); - ma_completion_handler_uwp_uninit(&completionHandler); - - hr = ma_IActivateAudioInterfaceAsyncOperation_GetActivateResult(pAsyncOp, &activateResult, &pActivatedInterface); - ma_IActivateAudioInterfaceAsyncOperation_Release(pAsyncOp); - - if (FAILED(hr) || FAILED(activateResult)) { - ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to activate device.\n"); - return FAILED(hr) ? ma_result_from_HRESULT(hr) : ma_result_from_HRESULT(activateResult); - } - - /* Here is where we grab the IAudioClient interface. */ - hr = ma_IUnknown_QueryInterface(pActivatedInterface, &MA_IID_IAudioClient, (void**)ppAudioClient); - if (FAILED(hr)) { - ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to query IAudioClient interface.\n"); - return ma_result_from_HRESULT(hr); - } - - if (ppActivatedInterface) { - *ppActivatedInterface = pActivatedInterface; - } else { - ma_IUnknown_Release(pActivatedInterface); - } - - return MA_SUCCESS; -} -#endif - - -/* https://docs.microsoft.com/en-us/windows/win32/api/audioclientactivationparams/ne-audioclientactivationparams-audioclient_activation_type */ -typedef enum -{ - MA_AUDIOCLIENT_ACTIVATION_TYPE_DEFAULT, - MA_AUDIOCLIENT_ACTIVATION_TYPE_PROCESS_LOOPBACK -} MA_AUDIOCLIENT_ACTIVATION_TYPE; - -/* https://docs.microsoft.com/en-us/windows/win32/api/audioclientactivationparams/ne-audioclientactivationparams-process_loopback_mode */ -typedef enum -{ - MA_PROCESS_LOOPBACK_MODE_INCLUDE_TARGET_PROCESS_TREE, - MA_PROCESS_LOOPBACK_MODE_EXCLUDE_TARGET_PROCESS_TREE -} MA_PROCESS_LOOPBACK_MODE; - -/* https://docs.microsoft.com/en-us/windows/win32/api/audioclientactivationparams/ns-audioclientactivationparams-audioclient_process_loopback_params */ -typedef struct -{ - DWORD TargetProcessId; - MA_PROCESS_LOOPBACK_MODE ProcessLoopbackMode; -} MA_AUDIOCLIENT_PROCESS_LOOPBACK_PARAMS; - -#if defined(_MSC_VER) && !defined(__clang__) - #pragma warning(push) - #pragma warning(disable:4201) /* nonstandard extension used: nameless struct/union */ -#elif defined(__clang__) || (defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 8))) - #pragma GCC diagnostic push - #pragma GCC diagnostic ignored "-Wpedantic" /* For ISO C99 doesn't support unnamed structs/unions [-Wpedantic] */ - #if defined(__clang__) - #pragma GCC diagnostic ignored "-Wc11-extensions" /* anonymous unions are a C11 extension */ - #endif -#endif -/* https://docs.microsoft.com/en-us/windows/win32/api/audioclientactivationparams/ns-audioclientactivationparams-audioclient_activation_params */ -typedef struct -{ - MA_AUDIOCLIENT_ACTIVATION_TYPE ActivationType; - union - { - MA_AUDIOCLIENT_PROCESS_LOOPBACK_PARAMS ProcessLoopbackParams; - }; -} MA_AUDIOCLIENT_ACTIVATION_PARAMS; -#if defined(_MSC_VER) && !defined(__clang__) - #pragma warning(pop) -#elif defined(__clang__) || (defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 8))) - #pragma GCC diagnostic pop -#endif - -#define MA_VIRTUAL_AUDIO_DEVICE_PROCESS_LOOPBACK L"VAD\\Process_Loopback" - -static ma_result ma_context_get_IAudioClient__wasapi(ma_context* pContext, ma_device_type deviceType, const ma_device_id* pDeviceID, ma_uint32 loopbackProcessID, ma_bool32 loopbackProcessExclude, ma_IAudioClient** ppAudioClient, ma_WASAPIDeviceInterface** ppDeviceInterface) -{ - ma_result result; - ma_bool32 usingProcessLoopback = MA_FALSE; - MA_AUDIOCLIENT_ACTIVATION_PARAMS audioclientActivationParams; - MA_PROPVARIANT activationParams; - MA_PROPVARIANT* pActivationParams = NULL; - ma_device_id virtualDeviceID; - - /* Activation parameters specific to loopback mode. Note that process-specific loopback will only work when a default device ID is specified. */ - if (deviceType == ma_device_type_loopback && loopbackProcessID != 0 && pDeviceID == NULL) { - usingProcessLoopback = MA_TRUE; - } - - if (usingProcessLoopback) { - MA_ZERO_OBJECT(&audioclientActivationParams); - audioclientActivationParams.ActivationType = MA_AUDIOCLIENT_ACTIVATION_TYPE_PROCESS_LOOPBACK; - audioclientActivationParams.ProcessLoopbackParams.ProcessLoopbackMode = (loopbackProcessExclude) ? MA_PROCESS_LOOPBACK_MODE_EXCLUDE_TARGET_PROCESS_TREE : MA_PROCESS_LOOPBACK_MODE_INCLUDE_TARGET_PROCESS_TREE; - audioclientActivationParams.ProcessLoopbackParams.TargetProcessId = (DWORD)loopbackProcessID; - - ma_PropVariantInit(&activationParams); - activationParams.vt = MA_VT_BLOB; - activationParams.blob.cbSize = sizeof(audioclientActivationParams); - activationParams.blob.pBlobData = (BYTE*)&audioclientActivationParams; - pActivationParams = &activationParams; - - /* When requesting a specific device ID we need to use a special device ID. */ - MA_COPY_MEMORY(virtualDeviceID.wasapi, MA_VIRTUAL_AUDIO_DEVICE_PROCESS_LOOPBACK, (wcslen(MA_VIRTUAL_AUDIO_DEVICE_PROCESS_LOOPBACK) + 1) * sizeof(wchar_t)); /* +1 for the null terminator. */ - pDeviceID = &virtualDeviceID; - } else { - pActivationParams = NULL; /* No activation parameters required. */ - } - -#if defined(MA_WIN32_DESKTOP) || defined(MA_WIN32_GDK) - result = ma_context_get_IAudioClient_Desktop__wasapi(pContext, deviceType, pDeviceID, pActivationParams, ppAudioClient, ppDeviceInterface); -#else - result = ma_context_get_IAudioClient_UWP__wasapi(pContext, deviceType, pDeviceID, pActivationParams, ppAudioClient, ppDeviceInterface); -#endif - - /* - If loopback mode was requested with a process ID and initialization failed, it could be because it's - trying to run on an older version of Windows where it's not supported. We need to let the caller - know about this with a log message. - */ - if (result != MA_SUCCESS) { - if (usingProcessLoopback) { - ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[WASAPI] Loopback mode requested to %s process ID %u, but initialization failed. Support for this feature begins with Windows 10 Build 20348. Confirm your version of Windows or consider not using process-specific loopback.\n", (loopbackProcessExclude) ? "exclude" : "include", loopbackProcessID); - } - } - - return result; -} - - -static ma_result ma_context_enumerate_devices__wasapi(ma_context* pContext, ma_enum_devices_callback_proc callback, void* pUserData) -{ - /* Different enumeration for desktop and UWP. */ -#if defined(MA_WIN32_DESKTOP) || defined(MA_WIN32_GDK) - /* Desktop */ - HRESULT hr; - ma_IMMDeviceEnumerator* pDeviceEnumerator; - - hr = ma_CoCreateInstance(pContext, &MA_CLSID_MMDeviceEnumerator, NULL, CLSCTX_ALL, &MA_IID_IMMDeviceEnumerator, (void**)&pDeviceEnumerator); - if (FAILED(hr)) { - ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to create device enumerator."); - return ma_result_from_HRESULT(hr); - } - - ma_context_enumerate_devices_by_type__wasapi(pContext, pDeviceEnumerator, ma_device_type_playback, callback, pUserData); - ma_context_enumerate_devices_by_type__wasapi(pContext, pDeviceEnumerator, ma_device_type_capture, callback, pUserData); - - ma_IMMDeviceEnumerator_Release(pDeviceEnumerator); -#else - /* - UWP - - The MMDevice API is only supported on desktop applications. For now, while I'm still figuring out how to properly enumerate - over devices without using MMDevice, I'm restricting devices to defaults. - - Hint: DeviceInformation::FindAllAsync() with DeviceClass.AudioCapture/AudioRender. https://blogs.windows.com/buildingapps/2014/05/15/real-time-audio-in-windows-store-and-windows-phone-apps/ - */ - if (callback) { - ma_bool32 cbResult = MA_TRUE; - - /* Playback. */ - if (cbResult) { - ma_device_info deviceInfo; - MA_ZERO_OBJECT(&deviceInfo); - ma_strncpy_s(deviceInfo.name, sizeof(deviceInfo.name), MA_DEFAULT_PLAYBACK_DEVICE_NAME, (size_t)-1); - deviceInfo.isDefault = MA_TRUE; - cbResult = callback(pContext, ma_device_type_playback, &deviceInfo, pUserData); - } - - /* Capture. */ - if (cbResult) { - ma_device_info deviceInfo; - MA_ZERO_OBJECT(&deviceInfo); - ma_strncpy_s(deviceInfo.name, sizeof(deviceInfo.name), MA_DEFAULT_CAPTURE_DEVICE_NAME, (size_t)-1); - deviceInfo.isDefault = MA_TRUE; - cbResult = callback(pContext, ma_device_type_capture, &deviceInfo, pUserData); - } - } -#endif - - return MA_SUCCESS; -} - -static ma_result ma_context_get_device_info__wasapi(ma_context* pContext, ma_device_type deviceType, const ma_device_id* pDeviceID, ma_device_info* pDeviceInfo) -{ -#if defined(MA_WIN32_DESKTOP) || defined(MA_WIN32_GDK) - ma_result result; - ma_IMMDevice* pMMDevice = NULL; - WCHAR* pDefaultDeviceID = NULL; - - result = ma_context_get_MMDevice__wasapi(pContext, deviceType, pDeviceID, &pMMDevice); - if (result != MA_SUCCESS) { - return result; - } - - /* We need the default device ID so we can set the isDefault flag in the device info. */ - pDefaultDeviceID = ma_context_get_default_device_id__wasapi(pContext, deviceType); - - result = ma_context_get_device_info_from_MMDevice__wasapi(pContext, pMMDevice, pDefaultDeviceID, MA_FALSE, pDeviceInfo); /* MA_FALSE = !onlySimpleInfo. */ - - if (pDefaultDeviceID != NULL) { - ma_CoTaskMemFree(pContext, pDefaultDeviceID); - pDefaultDeviceID = NULL; - } - - ma_IMMDevice_Release(pMMDevice); - - return result; -#else - ma_IAudioClient* pAudioClient; - ma_result result; - - /* UWP currently only uses default devices. */ - if (deviceType == ma_device_type_playback) { - ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), MA_DEFAULT_PLAYBACK_DEVICE_NAME, (size_t)-1); - } else { - ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), MA_DEFAULT_CAPTURE_DEVICE_NAME, (size_t)-1); - } - - result = ma_context_get_IAudioClient_UWP__wasapi(pContext, deviceType, pDeviceID, NULL, &pAudioClient, NULL); - if (result != MA_SUCCESS) { - return result; - } - - result = ma_context_get_device_info_from_IAudioClient__wasapi(pContext, NULL, pAudioClient, pDeviceInfo); - - pDeviceInfo->isDefault = MA_TRUE; /* UWP only supports default devices. */ - - ma_IAudioClient_Release(pAudioClient); - return result; -#endif -} - -static ma_result ma_device_uninit__wasapi(ma_device* pDevice) -{ - MA_ASSERT(pDevice != NULL); - - #if defined(MA_WIN32_DESKTOP) || defined(MA_WIN32_GDK) - { - if (pDevice->wasapi.pDeviceEnumerator) { - ((ma_IMMDeviceEnumerator*)pDevice->wasapi.pDeviceEnumerator)->lpVtbl->UnregisterEndpointNotificationCallback((ma_IMMDeviceEnumerator*)pDevice->wasapi.pDeviceEnumerator, &pDevice->wasapi.notificationClient); - ma_IMMDeviceEnumerator_Release((ma_IMMDeviceEnumerator*)pDevice->wasapi.pDeviceEnumerator); - } - - ma_mutex_uninit(&pDevice->wasapi.rerouteLock); - } - #endif - - if (pDevice->wasapi.pRenderClient) { - if (pDevice->wasapi.pMappedBufferPlayback != NULL) { - ma_IAudioRenderClient_ReleaseBuffer((ma_IAudioRenderClient*)pDevice->wasapi.pRenderClient, pDevice->wasapi.mappedBufferPlaybackCap, 0); - pDevice->wasapi.pMappedBufferPlayback = NULL; - pDevice->wasapi.mappedBufferPlaybackCap = 0; - pDevice->wasapi.mappedBufferPlaybackLen = 0; - } - - ma_IAudioRenderClient_Release((ma_IAudioRenderClient*)pDevice->wasapi.pRenderClient); - } - if (pDevice->wasapi.pCaptureClient) { - if (pDevice->wasapi.pMappedBufferCapture != NULL) { - ma_IAudioCaptureClient_ReleaseBuffer((ma_IAudioCaptureClient*)pDevice->wasapi.pCaptureClient, pDevice->wasapi.mappedBufferCaptureCap); - pDevice->wasapi.pMappedBufferCapture = NULL; - pDevice->wasapi.mappedBufferCaptureCap = 0; - pDevice->wasapi.mappedBufferCaptureLen = 0; - } - - ma_IAudioCaptureClient_Release((ma_IAudioCaptureClient*)pDevice->wasapi.pCaptureClient); - } - - if (pDevice->wasapi.pAudioClientPlayback) { - ma_IAudioClient_Release((ma_IAudioClient*)pDevice->wasapi.pAudioClientPlayback); - } - if (pDevice->wasapi.pAudioClientCapture) { - ma_IAudioClient_Release((ma_IAudioClient*)pDevice->wasapi.pAudioClientCapture); - } - - if (pDevice->wasapi.hEventPlayback) { - CloseHandle((HANDLE)pDevice->wasapi.hEventPlayback); - } - if (pDevice->wasapi.hEventCapture) { - CloseHandle((HANDLE)pDevice->wasapi.hEventCapture); - } - - return MA_SUCCESS; -} - - -typedef struct -{ - /* Input. */ - ma_format formatIn; - ma_uint32 channelsIn; - ma_uint32 sampleRateIn; - ma_channel channelMapIn[MA_MAX_CHANNELS]; - ma_uint32 periodSizeInFramesIn; - ma_uint32 periodSizeInMillisecondsIn; - ma_uint32 periodsIn; - ma_share_mode shareMode; - ma_performance_profile performanceProfile; - ma_bool32 noAutoConvertSRC; - ma_bool32 noDefaultQualitySRC; - ma_bool32 noHardwareOffloading; - ma_uint32 loopbackProcessID; - ma_bool32 loopbackProcessExclude; - - /* Output. */ - ma_IAudioClient* pAudioClient; - ma_IAudioRenderClient* pRenderClient; - ma_IAudioCaptureClient* pCaptureClient; - ma_format formatOut; - ma_uint32 channelsOut; - ma_uint32 sampleRateOut; - ma_channel channelMapOut[MA_MAX_CHANNELS]; - ma_uint32 periodSizeInFramesOut; - ma_uint32 periodsOut; - ma_bool32 usingAudioClient3; - char deviceName[256]; - ma_device_id id; -} ma_device_init_internal_data__wasapi; - -static ma_result ma_device_init_internal__wasapi(ma_context* pContext, ma_device_type deviceType, const ma_device_id* pDeviceID, ma_device_init_internal_data__wasapi* pData) -{ - HRESULT hr; - ma_result result = MA_SUCCESS; - const char* errorMsg = ""; - MA_AUDCLNT_SHAREMODE shareMode = MA_AUDCLNT_SHAREMODE_SHARED; - DWORD streamFlags = 0; - MA_REFERENCE_TIME periodDurationInMicroseconds; - ma_bool32 wasInitializedUsingIAudioClient3 = MA_FALSE; - MA_WAVEFORMATEXTENSIBLE wf; - ma_WASAPIDeviceInterface* pDeviceInterface = NULL; - ma_IAudioClient2* pAudioClient2; - ma_uint32 nativeSampleRate; - ma_bool32 usingProcessLoopback = MA_FALSE; - - MA_ASSERT(pContext != NULL); - MA_ASSERT(pData != NULL); - - /* This function is only used to initialize one device type: either playback, capture or loopback. Never full-duplex. */ - if (deviceType == ma_device_type_duplex) { - return MA_INVALID_ARGS; - } - - usingProcessLoopback = deviceType == ma_device_type_loopback && pData->loopbackProcessID != 0 && pDeviceID == NULL; - - pData->pAudioClient = NULL; - pData->pRenderClient = NULL; - pData->pCaptureClient = NULL; - - streamFlags = MA_AUDCLNT_STREAMFLAGS_EVENTCALLBACK; - if (!pData->noAutoConvertSRC && pData->sampleRateIn != 0 && pData->shareMode != ma_share_mode_exclusive) { /* <-- Exclusive streams must use the native sample rate. */ - streamFlags |= MA_AUDCLNT_STREAMFLAGS_AUTOCONVERTPCM; - } - if (!pData->noDefaultQualitySRC && pData->sampleRateIn != 0 && (streamFlags & MA_AUDCLNT_STREAMFLAGS_AUTOCONVERTPCM) != 0) { - streamFlags |= MA_AUDCLNT_STREAMFLAGS_SRC_DEFAULT_QUALITY; - } - if (deviceType == ma_device_type_loopback) { - streamFlags |= MA_AUDCLNT_STREAMFLAGS_LOOPBACK; - } - - result = ma_context_get_IAudioClient__wasapi(pContext, deviceType, pDeviceID, pData->loopbackProcessID, pData->loopbackProcessExclude, &pData->pAudioClient, &pDeviceInterface); - if (result != MA_SUCCESS) { - goto done; - } - - MA_ZERO_OBJECT(&wf); - - /* Try enabling hardware offloading. */ - if (!pData->noHardwareOffloading) { - hr = ma_IAudioClient_QueryInterface(pData->pAudioClient, &MA_IID_IAudioClient2, (void**)&pAudioClient2); - if (SUCCEEDED(hr)) { - BOOL isHardwareOffloadingSupported = 0; - hr = ma_IAudioClient2_IsOffloadCapable(pAudioClient2, MA_AudioCategory_Other, &isHardwareOffloadingSupported); - if (SUCCEEDED(hr) && isHardwareOffloadingSupported) { - ma_AudioClientProperties clientProperties; - MA_ZERO_OBJECT(&clientProperties); - clientProperties.cbSize = sizeof(clientProperties); - clientProperties.bIsOffload = 1; - clientProperties.eCategory = MA_AudioCategory_Other; - ma_IAudioClient2_SetClientProperties(pAudioClient2, &clientProperties); - } - - pAudioClient2->lpVtbl->Release(pAudioClient2); - } - } - - /* Here is where we try to determine the best format to use with the device. If the client if wanting exclusive mode, first try finding the best format for that. If this fails, fall back to shared mode. */ - result = MA_FORMAT_NOT_SUPPORTED; - if (pData->shareMode == ma_share_mode_exclusive) { - #if defined(MA_WIN32_DESKTOP) || defined(MA_WIN32_GDK) - /* In exclusive mode on desktop we always use the backend's native format. */ - ma_IPropertyStore* pStore = NULL; - hr = ma_IMMDevice_OpenPropertyStore(pDeviceInterface, STGM_READ, &pStore); - if (SUCCEEDED(hr)) { - MA_PROPVARIANT prop; - ma_PropVariantInit(&prop); - hr = ma_IPropertyStore_GetValue(pStore, &MA_PKEY_AudioEngine_DeviceFormat, &prop); - if (SUCCEEDED(hr)) { - MA_WAVEFORMATEX* pActualFormat = (MA_WAVEFORMATEX*)prop.blob.pBlobData; - hr = ma_IAudioClient_IsFormatSupported((ma_IAudioClient*)pData->pAudioClient, MA_AUDCLNT_SHAREMODE_EXCLUSIVE, pActualFormat, NULL); - if (SUCCEEDED(hr)) { - MA_COPY_MEMORY(&wf, pActualFormat, sizeof(MA_WAVEFORMATEXTENSIBLE)); - } - - ma_PropVariantClear(pContext, &prop); - } - - ma_IPropertyStore_Release(pStore); - } - #else - /* - I do not know how to query the device's native format on UWP so for now I'm just disabling support for - exclusive mode. The alternative is to enumerate over different formats and check IsFormatSupported() - until you find one that works. - - TODO: Add support for exclusive mode to UWP. - */ - hr = S_FALSE; - #endif - - if (hr == S_OK) { - shareMode = MA_AUDCLNT_SHAREMODE_EXCLUSIVE; - result = MA_SUCCESS; - } else { - result = MA_SHARE_MODE_NOT_SUPPORTED; - } - } else { - /* In shared mode we are always using the format reported by the operating system. */ - MA_WAVEFORMATEXTENSIBLE* pNativeFormat = NULL; - hr = ma_IAudioClient_GetMixFormat((ma_IAudioClient*)pData->pAudioClient, (MA_WAVEFORMATEX**)&pNativeFormat); - if (hr != S_OK) { - /* When using process-specific loopback, GetMixFormat() seems to always fail. */ - if (usingProcessLoopback) { - wf.wFormatTag = WAVE_FORMAT_IEEE_FLOAT; - wf.nChannels = 2; - wf.nSamplesPerSec = 44100; - wf.wBitsPerSample = 32; - wf.nBlockAlign = wf.nChannels * wf.wBitsPerSample / 8; - wf.nAvgBytesPerSec = wf.nSamplesPerSec * wf.nBlockAlign; - wf.cbSize = sizeof(MA_WAVEFORMATEX); - - result = MA_SUCCESS; - } else { - result = MA_FORMAT_NOT_SUPPORTED; - } - } else { - /* - I've seen cases where cbSize will be set to sizeof(WAVEFORMATEX) even though the structure itself - is given the format tag of WAVE_FORMAT_EXTENSIBLE. If the format tag is WAVE_FORMAT_EXTENSIBLE - want to make sure we copy the whole WAVEFORMATEXTENSIBLE structure. Otherwise we'll have to be - safe and only copy the WAVEFORMATEX part. - */ - if (pNativeFormat->wFormatTag == WAVE_FORMAT_EXTENSIBLE) { - MA_COPY_MEMORY(&wf, pNativeFormat, sizeof(MA_WAVEFORMATEXTENSIBLE)); - } else { - /* I've seen a case where cbSize was set to 0. Assume sizeof(WAVEFORMATEX) in this case. */ - size_t cbSize = pNativeFormat->cbSize; - if (cbSize == 0) { - cbSize = sizeof(MA_WAVEFORMATEX); - } - - /* Make sure we don't copy more than the capacity of `wf`. */ - if (cbSize > sizeof(wf)) { - cbSize = sizeof(wf); - } - - MA_COPY_MEMORY(&wf, pNativeFormat, cbSize); - } - - result = MA_SUCCESS; - } - - ma_CoTaskMemFree(pContext, pNativeFormat); - - shareMode = MA_AUDCLNT_SHAREMODE_SHARED; - } - - /* Return an error if we still haven't found a format. */ - if (result != MA_SUCCESS) { - errorMsg = "[WASAPI] Failed to find best device mix format."; - goto done; - } - - /* - Override the native sample rate with the one requested by the caller, but only if we're not using the default sample rate. We'll use - WASAPI to perform the sample rate conversion. - */ - nativeSampleRate = wf.nSamplesPerSec; - if (streamFlags & MA_AUDCLNT_STREAMFLAGS_AUTOCONVERTPCM) { - wf.nSamplesPerSec = (pData->sampleRateIn != 0) ? pData->sampleRateIn : MA_DEFAULT_SAMPLE_RATE; - wf.nAvgBytesPerSec = wf.nSamplesPerSec * wf.nBlockAlign; - } - - pData->formatOut = ma_format_from_WAVEFORMATEX((MA_WAVEFORMATEX*)&wf); - if (pData->formatOut == ma_format_unknown) { - /* - The format isn't supported. This is almost certainly because the exclusive mode format isn't supported by miniaudio. We need to return MA_SHARE_MODE_NOT_SUPPORTED - in this case so that the caller can detect it and fall back to shared mode if desired. We should never get here if shared mode was requested, but just for - completeness we'll check for it and return MA_FORMAT_NOT_SUPPORTED. - */ - if (shareMode == MA_AUDCLNT_SHAREMODE_EXCLUSIVE) { - result = MA_SHARE_MODE_NOT_SUPPORTED; - } else { - result = MA_FORMAT_NOT_SUPPORTED; - } - - errorMsg = "[WASAPI] Native format not supported."; - goto done; - } - - pData->channelsOut = wf.nChannels; - pData->sampleRateOut = wf.nSamplesPerSec; - - /* - Get the internal channel map based on the channel mask. There is a possibility that GetMixFormat() returns - a WAVEFORMATEX instead of a WAVEFORMATEXTENSIBLE, in which case the channel mask will be undefined. In this - case we'll just use the default channel map. - */ - if (wf.wFormatTag == WAVE_FORMAT_EXTENSIBLE || wf.cbSize >= sizeof(MA_WAVEFORMATEXTENSIBLE)) { - ma_channel_mask_to_channel_map__win32(wf.dwChannelMask, pData->channelsOut, pData->channelMapOut); - } else { - ma_channel_map_init_standard(ma_standard_channel_map_microsoft, pData->channelMapOut, ma_countof(pData->channelMapOut), pData->channelsOut); - } - - /* Period size. */ - pData->periodsOut = (pData->periodsIn != 0) ? pData->periodsIn : MA_DEFAULT_PERIODS; - pData->periodSizeInFramesOut = pData->periodSizeInFramesIn; - if (pData->periodSizeInFramesOut == 0) { - if (pData->periodSizeInMillisecondsIn == 0) { - if (pData->performanceProfile == ma_performance_profile_low_latency) { - pData->periodSizeInFramesOut = ma_calculate_buffer_size_in_frames_from_milliseconds(MA_DEFAULT_PERIOD_SIZE_IN_MILLISECONDS_LOW_LATENCY, wf.nSamplesPerSec); - } else { - pData->periodSizeInFramesOut = ma_calculate_buffer_size_in_frames_from_milliseconds(MA_DEFAULT_PERIOD_SIZE_IN_MILLISECONDS_CONSERVATIVE, wf.nSamplesPerSec); - } - } else { - pData->periodSizeInFramesOut = ma_calculate_buffer_size_in_frames_from_milliseconds(pData->periodSizeInMillisecondsIn, wf.nSamplesPerSec); - } - } - - periodDurationInMicroseconds = ((ma_uint64)pData->periodSizeInFramesOut * 1000 * 1000) / wf.nSamplesPerSec; - - - /* Slightly different initialization for shared and exclusive modes. We try exclusive mode first, and if it fails, fall back to shared mode. */ - if (shareMode == MA_AUDCLNT_SHAREMODE_EXCLUSIVE) { - MA_REFERENCE_TIME bufferDuration = periodDurationInMicroseconds * pData->periodsOut * 10; - - /* - If the periodicity is too small, Initialize() will fail with AUDCLNT_E_INVALID_DEVICE_PERIOD. In this case we should just keep increasing - it and trying it again. - */ - hr = E_FAIL; - for (;;) { - hr = ma_IAudioClient_Initialize((ma_IAudioClient*)pData->pAudioClient, shareMode, streamFlags, bufferDuration, bufferDuration, (MA_WAVEFORMATEX*)&wf, NULL); - if (hr == MA_AUDCLNT_E_INVALID_DEVICE_PERIOD) { - if (bufferDuration > 500*10000) { - break; - } else { - if (bufferDuration == 0) { /* <-- Just a sanity check to prevent an infinite loop. Should never happen, but it makes me feel better. */ - break; - } - - bufferDuration = bufferDuration * 2; - continue; - } - } else { - break; - } - } - - if (hr == MA_AUDCLNT_E_BUFFER_SIZE_NOT_ALIGNED) { - ma_uint32 bufferSizeInFrames; - hr = ma_IAudioClient_GetBufferSize((ma_IAudioClient*)pData->pAudioClient, &bufferSizeInFrames); - if (SUCCEEDED(hr)) { - bufferDuration = (MA_REFERENCE_TIME)((10000.0 * 1000 / wf.nSamplesPerSec * bufferSizeInFrames) + 0.5); - - /* Unfortunately we need to release and re-acquire the audio client according to MSDN. Seems silly - why not just call IAudioClient_Initialize() again?! */ - ma_IAudioClient_Release((ma_IAudioClient*)pData->pAudioClient); - - #if defined(MA_WIN32_DESKTOP) || defined(MA_WIN32_GDK) - hr = ma_IMMDevice_Activate(pDeviceInterface, &MA_IID_IAudioClient, CLSCTX_ALL, NULL, (void**)&pData->pAudioClient); - #else - hr = ma_IUnknown_QueryInterface(pDeviceInterface, &MA_IID_IAudioClient, (void**)&pData->pAudioClient); - #endif - - if (SUCCEEDED(hr)) { - hr = ma_IAudioClient_Initialize((ma_IAudioClient*)pData->pAudioClient, shareMode, streamFlags, bufferDuration, bufferDuration, (MA_WAVEFORMATEX*)&wf, NULL); - } - } - } - - if (FAILED(hr)) { - /* Failed to initialize in exclusive mode. Don't fall back to shared mode - instead tell the client about it. They can reinitialize in shared mode if they want. */ - if (hr == E_ACCESSDENIED) { - errorMsg = "[WASAPI] Failed to initialize device in exclusive mode. Access denied.", result = MA_ACCESS_DENIED; - } else if (hr == MA_AUDCLNT_E_DEVICE_IN_USE) { - errorMsg = "[WASAPI] Failed to initialize device in exclusive mode. Device in use.", result = MA_BUSY; - } else { - errorMsg = "[WASAPI] Failed to initialize device in exclusive mode."; result = ma_result_from_HRESULT(hr); - } - goto done; - } - } - - if (shareMode == MA_AUDCLNT_SHAREMODE_SHARED) { - /* - Low latency shared mode via IAudioClient3. - - NOTE - ==== - Contrary to the documentation on MSDN (https://docs.microsoft.com/en-us/windows/win32/api/audioclient/nf-audioclient-iaudioclient3-initializesharedaudiostream), the - use of AUDCLNT_STREAMFLAGS_AUTOCONVERTPCM and AUDCLNT_STREAMFLAGS_SRC_DEFAULT_QUALITY with IAudioClient3_InitializeSharedAudioStream() absolutely does not work. Using - any of these flags will result in HRESULT code 0x88890021. The other problem is that calling IAudioClient3_GetSharedModeEnginePeriod() with a sample rate different to - that returned by IAudioClient_GetMixFormat() also results in an error. I'm therefore disabling low-latency shared mode with AUDCLNT_STREAMFLAGS_AUTOCONVERTPCM. - */ - #ifndef MA_WASAPI_NO_LOW_LATENCY_SHARED_MODE - { - if ((streamFlags & MA_AUDCLNT_STREAMFLAGS_AUTOCONVERTPCM) == 0 || nativeSampleRate == wf.nSamplesPerSec) { - ma_IAudioClient3* pAudioClient3 = NULL; - hr = ma_IAudioClient_QueryInterface(pData->pAudioClient, &MA_IID_IAudioClient3, (void**)&pAudioClient3); - if (SUCCEEDED(hr)) { - ma_uint32 defaultPeriodInFrames; - ma_uint32 fundamentalPeriodInFrames; - ma_uint32 minPeriodInFrames; - ma_uint32 maxPeriodInFrames; - hr = ma_IAudioClient3_GetSharedModeEnginePeriod(pAudioClient3, (MA_WAVEFORMATEX*)&wf, &defaultPeriodInFrames, &fundamentalPeriodInFrames, &minPeriodInFrames, &maxPeriodInFrames); - if (SUCCEEDED(hr)) { - ma_uint32 desiredPeriodInFrames = pData->periodSizeInFramesOut; - ma_uint32 actualPeriodInFrames = desiredPeriodInFrames; - - /* Make sure the period size is a multiple of fundamentalPeriodInFrames. */ - actualPeriodInFrames = actualPeriodInFrames / fundamentalPeriodInFrames; - actualPeriodInFrames = actualPeriodInFrames * fundamentalPeriodInFrames; - - /* The period needs to be clamped between minPeriodInFrames and maxPeriodInFrames. */ - actualPeriodInFrames = ma_clamp(actualPeriodInFrames, minPeriodInFrames, maxPeriodInFrames); - - ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_DEBUG, "[WASAPI] Trying IAudioClient3_InitializeSharedAudioStream(actualPeriodInFrames=%d)\n", actualPeriodInFrames); - ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_DEBUG, " defaultPeriodInFrames=%d\n", defaultPeriodInFrames); - ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_DEBUG, " fundamentalPeriodInFrames=%d\n", fundamentalPeriodInFrames); - ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_DEBUG, " minPeriodInFrames=%d\n", minPeriodInFrames); - ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_DEBUG, " maxPeriodInFrames=%d\n", maxPeriodInFrames); - - /* If the client requested a largish buffer than we don't actually want to use low latency shared mode because it forces small buffers. */ - if (actualPeriodInFrames >= desiredPeriodInFrames) { - /* - MA_AUDCLNT_STREAMFLAGS_AUTOCONVERTPCM | MA_AUDCLNT_STREAMFLAGS_SRC_DEFAULT_QUALITY must not be in the stream flags. If either of these are specified, - IAudioClient3_InitializeSharedAudioStream() will fail. - */ - hr = ma_IAudioClient3_InitializeSharedAudioStream(pAudioClient3, streamFlags & ~(MA_AUDCLNT_STREAMFLAGS_AUTOCONVERTPCM | MA_AUDCLNT_STREAMFLAGS_SRC_DEFAULT_QUALITY), actualPeriodInFrames, (MA_WAVEFORMATEX*)&wf, NULL); - if (SUCCEEDED(hr)) { - wasInitializedUsingIAudioClient3 = MA_TRUE; - pData->periodSizeInFramesOut = actualPeriodInFrames; - - ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_DEBUG, "[WASAPI] Using IAudioClient3\n"); - ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_DEBUG, " periodSizeInFramesOut=%d\n", pData->periodSizeInFramesOut); - } else { - ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_DEBUG, "[WASAPI] IAudioClient3_InitializeSharedAudioStream failed. Falling back to IAudioClient.\n"); - } - } else { - ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_DEBUG, "[WASAPI] Not using IAudioClient3 because the desired period size is larger than the maximum supported by IAudioClient3.\n"); - } - } else { - ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_DEBUG, "[WASAPI] IAudioClient3_GetSharedModeEnginePeriod failed. Falling back to IAudioClient.\n"); - } - - ma_IAudioClient3_Release(pAudioClient3); - pAudioClient3 = NULL; - } - } - } - #else - { - ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_DEBUG, "[WASAPI] Not using IAudioClient3 because MA_WASAPI_NO_LOW_LATENCY_SHARED_MODE is enabled.\n"); - } - #endif - - /* If we don't have an IAudioClient3 then we need to use the normal initialization routine. */ - if (!wasInitializedUsingIAudioClient3) { - MA_REFERENCE_TIME bufferDuration = periodDurationInMicroseconds * pData->periodsOut * 10; /* <-- Multiply by 10 for microseconds to 100-nanoseconds. */ - hr = ma_IAudioClient_Initialize((ma_IAudioClient*)pData->pAudioClient, shareMode, streamFlags, bufferDuration, 0, (const MA_WAVEFORMATEX*)&wf, NULL); - if (FAILED(hr)) { - if (hr == E_ACCESSDENIED) { - errorMsg = "[WASAPI] Failed to initialize device. Access denied.", result = MA_ACCESS_DENIED; - } else if (hr == MA_AUDCLNT_E_DEVICE_IN_USE) { - errorMsg = "[WASAPI] Failed to initialize device. Device in use.", result = MA_BUSY; - } else { - errorMsg = "[WASAPI] Failed to initialize device.", result = ma_result_from_HRESULT(hr); - } - - goto done; - } - } - } - - if (!wasInitializedUsingIAudioClient3) { - ma_uint32 bufferSizeInFrames = 0; - hr = ma_IAudioClient_GetBufferSize((ma_IAudioClient*)pData->pAudioClient, &bufferSizeInFrames); - if (FAILED(hr)) { - errorMsg = "[WASAPI] Failed to get audio client's actual buffer size.", result = ma_result_from_HRESULT(hr); - goto done; - } - - /* - When using process loopback mode, retrieval of the buffer size seems to result in totally - incorrect values. In this case we'll just assume it's the same size as what we requested - when we initialized the client. - */ - if (usingProcessLoopback) { - bufferSizeInFrames = (ma_uint32)((periodDurationInMicroseconds * pData->periodsOut) * pData->sampleRateOut / 1000000); - } - - pData->periodSizeInFramesOut = bufferSizeInFrames / pData->periodsOut; - } - - pData->usingAudioClient3 = wasInitializedUsingIAudioClient3; - - - if (deviceType == ma_device_type_playback) { - result = ma_device_create_IAudioClient_service__wasapi(pContext, deviceType, (ma_IAudioClient*)pData->pAudioClient, (void**)&pData->pRenderClient); - } else { - result = ma_device_create_IAudioClient_service__wasapi(pContext, deviceType, (ma_IAudioClient*)pData->pAudioClient, (void**)&pData->pCaptureClient); - } - - /*if (FAILED(hr)) {*/ - if (result != MA_SUCCESS) { - errorMsg = "[WASAPI] Failed to get audio client service."; - goto done; - } - - - /* Grab the name of the device. */ - #if defined(MA_WIN32_DESKTOP) || defined(MA_WIN32_GDK) - { - ma_IPropertyStore *pProperties; - hr = ma_IMMDevice_OpenPropertyStore(pDeviceInterface, STGM_READ, &pProperties); - if (SUCCEEDED(hr)) { - MA_PROPVARIANT varName; - ma_PropVariantInit(&varName); - hr = ma_IPropertyStore_GetValue(pProperties, &MA_PKEY_Device_FriendlyName, &varName); - if (SUCCEEDED(hr)) { - WideCharToMultiByte(CP_UTF8, 0, varName.pwszVal, -1, pData->deviceName, sizeof(pData->deviceName), 0, FALSE); - ma_PropVariantClear(pContext, &varName); - } - - ma_IPropertyStore_Release(pProperties); - } - } - #endif - - /* - For the WASAPI backend we need to know the actual IDs of the device in order to do automatic - stream routing so that IDs can be compared and we can determine which device has been detached - and whether or not it matches with our ma_device. - */ - #if defined(MA_WIN32_DESKTOP) || defined(MA_WIN32_GDK) - { - /* Desktop */ - ma_context_get_device_id_from_MMDevice__wasapi(pContext, pDeviceInterface, &pData->id); - } - #else - { - /* UWP */ - /* TODO: Implement me. Need to figure out how to get the ID of the default device. */ - } - #endif - -done: - /* Clean up. */ -#if defined(MA_WIN32_DESKTOP) || defined(MA_WIN32_GDK) - if (pDeviceInterface != NULL) { - ma_IMMDevice_Release(pDeviceInterface); - } -#else - if (pDeviceInterface != NULL) { - ma_IUnknown_Release(pDeviceInterface); - } -#endif - - if (result != MA_SUCCESS) { - if (pData->pRenderClient) { - ma_IAudioRenderClient_Release((ma_IAudioRenderClient*)pData->pRenderClient); - pData->pRenderClient = NULL; - } - if (pData->pCaptureClient) { - ma_IAudioCaptureClient_Release((ma_IAudioCaptureClient*)pData->pCaptureClient); - pData->pCaptureClient = NULL; - } - if (pData->pAudioClient) { - ma_IAudioClient_Release((ma_IAudioClient*)pData->pAudioClient); - pData->pAudioClient = NULL; - } - - if (errorMsg != NULL && errorMsg[0] != '\0') { - ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "%s\n", errorMsg); - } - - return result; - } else { - return MA_SUCCESS; - } -} - -static ma_result ma_device_reinit__wasapi(ma_device* pDevice, ma_device_type deviceType) -{ - ma_device_init_internal_data__wasapi data; - ma_result result; - - MA_ASSERT(pDevice != NULL); - - /* We only re-initialize the playback or capture device. Never a full-duplex device. */ - if (deviceType == ma_device_type_duplex) { - return MA_INVALID_ARGS; - } - - - /* - Before reinitializing the device we need to free the previous audio clients. - - There's a known memory leak here. We will be calling this from the routing change callback that - is fired by WASAPI. If we attempt to release the IAudioClient we will deadlock. In my opinion - this is a bug. I'm not sure what I need to do to handle this cleanly, but I think we'll probably - need some system where we post an event, but delay the execution of it until the callback has - returned. I'm not sure how to do this reliably, however. I have set up some infrastructure for - a command thread which might be useful for this. - */ - if (deviceType == ma_device_type_capture || deviceType == ma_device_type_loopback) { - if (pDevice->wasapi.pCaptureClient) { - ma_IAudioCaptureClient_Release((ma_IAudioCaptureClient*)pDevice->wasapi.pCaptureClient); - pDevice->wasapi.pCaptureClient = NULL; - } - - if (pDevice->wasapi.pAudioClientCapture) { - /*ma_device_release_IAudioClient_service__wasapi(pDevice, ma_device_type_capture);*/ - pDevice->wasapi.pAudioClientCapture = NULL; - } - } - - if (deviceType == ma_device_type_playback) { - if (pDevice->wasapi.pRenderClient) { - ma_IAudioRenderClient_Release((ma_IAudioRenderClient*)pDevice->wasapi.pRenderClient); - pDevice->wasapi.pRenderClient = NULL; - } - - if (pDevice->wasapi.pAudioClientPlayback) { - /*ma_device_release_IAudioClient_service__wasapi(pDevice, ma_device_type_playback);*/ - pDevice->wasapi.pAudioClientPlayback = NULL; - } - } - - - if (deviceType == ma_device_type_playback) { - data.formatIn = pDevice->playback.format; - data.channelsIn = pDevice->playback.channels; - MA_COPY_MEMORY(data.channelMapIn, pDevice->playback.channelMap, sizeof(pDevice->playback.channelMap)); - data.shareMode = pDevice->playback.shareMode; - } else { - data.formatIn = pDevice->capture.format; - data.channelsIn = pDevice->capture.channels; - MA_COPY_MEMORY(data.channelMapIn, pDevice->capture.channelMap, sizeof(pDevice->capture.channelMap)); - data.shareMode = pDevice->capture.shareMode; - } - - data.sampleRateIn = pDevice->sampleRate; - data.periodSizeInFramesIn = pDevice->wasapi.originalPeriodSizeInFrames; - data.periodSizeInMillisecondsIn = pDevice->wasapi.originalPeriodSizeInMilliseconds; - data.periodsIn = pDevice->wasapi.originalPeriods; - data.performanceProfile = pDevice->wasapi.originalPerformanceProfile; - data.noAutoConvertSRC = pDevice->wasapi.noAutoConvertSRC; - data.noDefaultQualitySRC = pDevice->wasapi.noDefaultQualitySRC; - data.noHardwareOffloading = pDevice->wasapi.noHardwareOffloading; - data.loopbackProcessID = pDevice->wasapi.loopbackProcessID; - data.loopbackProcessExclude = pDevice->wasapi.loopbackProcessExclude; - result = ma_device_init_internal__wasapi(pDevice->pContext, deviceType, NULL, &data); - if (result != MA_SUCCESS) { - return result; - } - - /* At this point we have some new objects ready to go. We need to uninitialize the previous ones and then set the new ones. */ - if (deviceType == ma_device_type_capture || deviceType == ma_device_type_loopback) { - pDevice->wasapi.pAudioClientCapture = data.pAudioClient; - pDevice->wasapi.pCaptureClient = data.pCaptureClient; - - pDevice->capture.internalFormat = data.formatOut; - pDevice->capture.internalChannels = data.channelsOut; - pDevice->capture.internalSampleRate = data.sampleRateOut; - MA_COPY_MEMORY(pDevice->capture.internalChannelMap, data.channelMapOut, sizeof(data.channelMapOut)); - pDevice->capture.internalPeriodSizeInFrames = data.periodSizeInFramesOut; - pDevice->capture.internalPeriods = data.periodsOut; - ma_strcpy_s(pDevice->capture.name, sizeof(pDevice->capture.name), data.deviceName); - - ma_IAudioClient_SetEventHandle((ma_IAudioClient*)pDevice->wasapi.pAudioClientCapture, (HANDLE)pDevice->wasapi.hEventCapture); - - pDevice->wasapi.periodSizeInFramesCapture = data.periodSizeInFramesOut; - ma_IAudioClient_GetBufferSize((ma_IAudioClient*)pDevice->wasapi.pAudioClientCapture, &pDevice->wasapi.actualBufferSizeInFramesCapture); - - /* We must always have a valid ID. */ - ma_strcpy_s_WCHAR(pDevice->capture.id.wasapi, sizeof(pDevice->capture.id.wasapi), data.id.wasapi); - } - - if (deviceType == ma_device_type_playback) { - pDevice->wasapi.pAudioClientPlayback = data.pAudioClient; - pDevice->wasapi.pRenderClient = data.pRenderClient; - - pDevice->playback.internalFormat = data.formatOut; - pDevice->playback.internalChannels = data.channelsOut; - pDevice->playback.internalSampleRate = data.sampleRateOut; - MA_COPY_MEMORY(pDevice->playback.internalChannelMap, data.channelMapOut, sizeof(data.channelMapOut)); - pDevice->playback.internalPeriodSizeInFrames = data.periodSizeInFramesOut; - pDevice->playback.internalPeriods = data.periodsOut; - ma_strcpy_s(pDevice->playback.name, sizeof(pDevice->playback.name), data.deviceName); - - ma_IAudioClient_SetEventHandle((ma_IAudioClient*)pDevice->wasapi.pAudioClientPlayback, (HANDLE)pDevice->wasapi.hEventPlayback); - - pDevice->wasapi.periodSizeInFramesPlayback = data.periodSizeInFramesOut; - ma_IAudioClient_GetBufferSize((ma_IAudioClient*)pDevice->wasapi.pAudioClientPlayback, &pDevice->wasapi.actualBufferSizeInFramesPlayback); - - /* We must always have a valid ID because rerouting will look at it. */ - ma_strcpy_s_WCHAR(pDevice->playback.id.wasapi, sizeof(pDevice->playback.id.wasapi), data.id.wasapi); - } - - return MA_SUCCESS; -} - -static ma_result ma_device_init__wasapi(ma_device* pDevice, const ma_device_config* pConfig, ma_device_descriptor* pDescriptorPlayback, ma_device_descriptor* pDescriptorCapture) -{ - ma_result result = MA_SUCCESS; - -#if defined(MA_WIN32_DESKTOP) || defined(MA_WIN32_GDK) - HRESULT hr; - ma_IMMDeviceEnumerator* pDeviceEnumerator; -#endif - - MA_ASSERT(pDevice != NULL); - - MA_ZERO_OBJECT(&pDevice->wasapi); - pDevice->wasapi.usage = pConfig->wasapi.usage; - pDevice->wasapi.noAutoConvertSRC = pConfig->wasapi.noAutoConvertSRC; - pDevice->wasapi.noDefaultQualitySRC = pConfig->wasapi.noDefaultQualitySRC; - pDevice->wasapi.noHardwareOffloading = pConfig->wasapi.noHardwareOffloading; - pDevice->wasapi.loopbackProcessID = pConfig->wasapi.loopbackProcessID; - pDevice->wasapi.loopbackProcessExclude = pConfig->wasapi.loopbackProcessExclude; - - /* Exclusive mode is not allowed with loopback. */ - if (pConfig->deviceType == ma_device_type_loopback && pConfig->playback.shareMode == ma_share_mode_exclusive) { - return MA_INVALID_DEVICE_CONFIG; - } - - if (pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex || pConfig->deviceType == ma_device_type_loopback) { - ma_device_init_internal_data__wasapi data; - data.formatIn = pDescriptorCapture->format; - data.channelsIn = pDescriptorCapture->channels; - data.sampleRateIn = pDescriptorCapture->sampleRate; - MA_COPY_MEMORY(data.channelMapIn, pDescriptorCapture->channelMap, sizeof(pDescriptorCapture->channelMap)); - data.periodSizeInFramesIn = pDescriptorCapture->periodSizeInFrames; - data.periodSizeInMillisecondsIn = pDescriptorCapture->periodSizeInMilliseconds; - data.periodsIn = pDescriptorCapture->periodCount; - data.shareMode = pDescriptorCapture->shareMode; - data.performanceProfile = pConfig->performanceProfile; - data.noAutoConvertSRC = pConfig->wasapi.noAutoConvertSRC; - data.noDefaultQualitySRC = pConfig->wasapi.noDefaultQualitySRC; - data.noHardwareOffloading = pConfig->wasapi.noHardwareOffloading; - data.loopbackProcessID = pConfig->wasapi.loopbackProcessID; - data.loopbackProcessExclude = pConfig->wasapi.loopbackProcessExclude; - - result = ma_device_init_internal__wasapi(pDevice->pContext, (pConfig->deviceType == ma_device_type_loopback) ? ma_device_type_loopback : ma_device_type_capture, pDescriptorCapture->pDeviceID, &data); - if (result != MA_SUCCESS) { - return result; - } - - pDevice->wasapi.pAudioClientCapture = data.pAudioClient; - pDevice->wasapi.pCaptureClient = data.pCaptureClient; - pDevice->wasapi.originalPeriodSizeInMilliseconds = pDescriptorCapture->periodSizeInMilliseconds; - pDevice->wasapi.originalPeriodSizeInFrames = pDescriptorCapture->periodSizeInFrames; - pDevice->wasapi.originalPeriods = pDescriptorCapture->periodCount; - pDevice->wasapi.originalPerformanceProfile = pConfig->performanceProfile; - - /* - The event for capture needs to be manual reset for the same reason as playback. We keep the initial state set to unsignaled, - however, because we want to block until we actually have something for the first call to ma_device_read(). - */ - pDevice->wasapi.hEventCapture = (ma_handle)CreateEventA(NULL, FALSE, FALSE, NULL); /* Auto reset, unsignaled by default. */ - if (pDevice->wasapi.hEventCapture == NULL) { - result = ma_result_from_GetLastError(GetLastError()); - - if (pDevice->wasapi.pCaptureClient != NULL) { - ma_IAudioCaptureClient_Release((ma_IAudioCaptureClient*)pDevice->wasapi.pCaptureClient); - pDevice->wasapi.pCaptureClient = NULL; - } - if (pDevice->wasapi.pAudioClientCapture != NULL) { - ma_IAudioClient_Release((ma_IAudioClient*)pDevice->wasapi.pAudioClientCapture); - pDevice->wasapi.pAudioClientCapture = NULL; - } - - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to create event for capture."); - return result; - } - ma_IAudioClient_SetEventHandle((ma_IAudioClient*)pDevice->wasapi.pAudioClientCapture, (HANDLE)pDevice->wasapi.hEventCapture); - - pDevice->wasapi.periodSizeInFramesCapture = data.periodSizeInFramesOut; - ma_IAudioClient_GetBufferSize((ma_IAudioClient*)pDevice->wasapi.pAudioClientCapture, &pDevice->wasapi.actualBufferSizeInFramesCapture); - - /* We must always have a valid ID. */ - ma_strcpy_s_WCHAR(pDevice->capture.id.wasapi, sizeof(pDevice->capture.id.wasapi), data.id.wasapi); - - /* The descriptor needs to be updated with actual values. */ - pDescriptorCapture->format = data.formatOut; - pDescriptorCapture->channels = data.channelsOut; - pDescriptorCapture->sampleRate = data.sampleRateOut; - MA_COPY_MEMORY(pDescriptorCapture->channelMap, data.channelMapOut, sizeof(data.channelMapOut)); - pDescriptorCapture->periodSizeInFrames = data.periodSizeInFramesOut; - pDescriptorCapture->periodCount = data.periodsOut; - } - - if (pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) { - ma_device_init_internal_data__wasapi data; - data.formatIn = pDescriptorPlayback->format; - data.channelsIn = pDescriptorPlayback->channels; - data.sampleRateIn = pDescriptorPlayback->sampleRate; - MA_COPY_MEMORY(data.channelMapIn, pDescriptorPlayback->channelMap, sizeof(pDescriptorPlayback->channelMap)); - data.periodSizeInFramesIn = pDescriptorPlayback->periodSizeInFrames; - data.periodSizeInMillisecondsIn = pDescriptorPlayback->periodSizeInMilliseconds; - data.periodsIn = pDescriptorPlayback->periodCount; - data.shareMode = pDescriptorPlayback->shareMode; - data.performanceProfile = pConfig->performanceProfile; - data.noAutoConvertSRC = pConfig->wasapi.noAutoConvertSRC; - data.noDefaultQualitySRC = pConfig->wasapi.noDefaultQualitySRC; - data.noHardwareOffloading = pConfig->wasapi.noHardwareOffloading; - data.loopbackProcessID = pConfig->wasapi.loopbackProcessID; - data.loopbackProcessExclude = pConfig->wasapi.loopbackProcessExclude; - - result = ma_device_init_internal__wasapi(pDevice->pContext, ma_device_type_playback, pDescriptorPlayback->pDeviceID, &data); - if (result != MA_SUCCESS) { - if (pConfig->deviceType == ma_device_type_duplex) { - if (pDevice->wasapi.pCaptureClient != NULL) { - ma_IAudioCaptureClient_Release((ma_IAudioCaptureClient*)pDevice->wasapi.pCaptureClient); - pDevice->wasapi.pCaptureClient = NULL; - } - if (pDevice->wasapi.pAudioClientCapture != NULL) { - ma_IAudioClient_Release((ma_IAudioClient*)pDevice->wasapi.pAudioClientCapture); - pDevice->wasapi.pAudioClientCapture = NULL; - } - - CloseHandle((HANDLE)pDevice->wasapi.hEventCapture); - pDevice->wasapi.hEventCapture = NULL; - } - return result; - } - - pDevice->wasapi.pAudioClientPlayback = data.pAudioClient; - pDevice->wasapi.pRenderClient = data.pRenderClient; - pDevice->wasapi.originalPeriodSizeInMilliseconds = pDescriptorPlayback->periodSizeInMilliseconds; - pDevice->wasapi.originalPeriodSizeInFrames = pDescriptorPlayback->periodSizeInFrames; - pDevice->wasapi.originalPeriods = pDescriptorPlayback->periodCount; - pDevice->wasapi.originalPerformanceProfile = pConfig->performanceProfile; - - /* - The event for playback is needs to be manual reset because we want to explicitly control the fact that it becomes signalled - only after the whole available space has been filled, never before. - - The playback event also needs to be initially set to a signaled state so that the first call to ma_device_write() is able - to get passed WaitForMultipleObjects(). - */ - pDevice->wasapi.hEventPlayback = (ma_handle)CreateEventA(NULL, FALSE, TRUE, NULL); /* Auto reset, signaled by default. */ - if (pDevice->wasapi.hEventPlayback == NULL) { - result = ma_result_from_GetLastError(GetLastError()); - - if (pConfig->deviceType == ma_device_type_duplex) { - if (pDevice->wasapi.pCaptureClient != NULL) { - ma_IAudioCaptureClient_Release((ma_IAudioCaptureClient*)pDevice->wasapi.pCaptureClient); - pDevice->wasapi.pCaptureClient = NULL; - } - if (pDevice->wasapi.pAudioClientCapture != NULL) { - ma_IAudioClient_Release((ma_IAudioClient*)pDevice->wasapi.pAudioClientCapture); - pDevice->wasapi.pAudioClientCapture = NULL; - } - - CloseHandle((HANDLE)pDevice->wasapi.hEventCapture); - pDevice->wasapi.hEventCapture = NULL; - } - - if (pDevice->wasapi.pRenderClient != NULL) { - ma_IAudioRenderClient_Release((ma_IAudioRenderClient*)pDevice->wasapi.pRenderClient); - pDevice->wasapi.pRenderClient = NULL; - } - if (pDevice->wasapi.pAudioClientPlayback != NULL) { - ma_IAudioClient_Release((ma_IAudioClient*)pDevice->wasapi.pAudioClientPlayback); - pDevice->wasapi.pAudioClientPlayback = NULL; - } - - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to create event for playback."); - return result; - } - ma_IAudioClient_SetEventHandle((ma_IAudioClient*)pDevice->wasapi.pAudioClientPlayback, (HANDLE)pDevice->wasapi.hEventPlayback); - - pDevice->wasapi.periodSizeInFramesPlayback = data.periodSizeInFramesOut; - ma_IAudioClient_GetBufferSize((ma_IAudioClient*)pDevice->wasapi.pAudioClientPlayback, &pDevice->wasapi.actualBufferSizeInFramesPlayback); - - /* We must always have a valid ID because rerouting will look at it. */ - ma_strcpy_s_WCHAR(pDevice->playback.id.wasapi, sizeof(pDevice->playback.id.wasapi), data.id.wasapi); - - /* The descriptor needs to be updated with actual values. */ - pDescriptorPlayback->format = data.formatOut; - pDescriptorPlayback->channels = data.channelsOut; - pDescriptorPlayback->sampleRate = data.sampleRateOut; - MA_COPY_MEMORY(pDescriptorPlayback->channelMap, data.channelMapOut, sizeof(data.channelMapOut)); - pDescriptorPlayback->periodSizeInFrames = data.periodSizeInFramesOut; - pDescriptorPlayback->periodCount = data.periodsOut; - } - - /* - We need to register a notification client to detect when the device has been disabled, unplugged or re-routed (when the default device changes). When - we are connecting to the default device we want to do automatic stream routing when the device is disabled or unplugged. Otherwise we want to just - stop the device outright and let the application handle it. - */ -#if defined(MA_WIN32_DESKTOP) || defined(MA_WIN32_GDK) - if (pConfig->wasapi.noAutoStreamRouting == MA_FALSE) { - if ((pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex || pConfig->deviceType == ma_device_type_loopback) && pConfig->capture.pDeviceID == NULL) { - pDevice->wasapi.allowCaptureAutoStreamRouting = MA_TRUE; - } - if ((pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) && pConfig->playback.pDeviceID == NULL) { - pDevice->wasapi.allowPlaybackAutoStreamRouting = MA_TRUE; - } - } - - ma_mutex_init(&pDevice->wasapi.rerouteLock); - - hr = ma_CoCreateInstance(pDevice->pContext, &MA_CLSID_MMDeviceEnumerator, NULL, CLSCTX_ALL, &MA_IID_IMMDeviceEnumerator, (void**)&pDeviceEnumerator); - if (FAILED(hr)) { - ma_device_uninit__wasapi(pDevice); - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to create device enumerator."); - return ma_result_from_HRESULT(hr); - } - - pDevice->wasapi.notificationClient.lpVtbl = (void*)&g_maNotificationCientVtbl; - pDevice->wasapi.notificationClient.counter = 1; - pDevice->wasapi.notificationClient.pDevice = pDevice; - - hr = pDeviceEnumerator->lpVtbl->RegisterEndpointNotificationCallback(pDeviceEnumerator, &pDevice->wasapi.notificationClient); - if (SUCCEEDED(hr)) { - pDevice->wasapi.pDeviceEnumerator = (ma_ptr)pDeviceEnumerator; - } else { - /* Not the end of the world if we fail to register the notification callback. We just won't support automatic stream routing. */ - ma_IMMDeviceEnumerator_Release(pDeviceEnumerator); - } -#endif - - ma_atomic_bool32_set(&pDevice->wasapi.isStartedCapture, MA_FALSE); - ma_atomic_bool32_set(&pDevice->wasapi.isStartedPlayback, MA_FALSE); - - return MA_SUCCESS; -} - -static ma_result ma_device__get_available_frames__wasapi(ma_device* pDevice, ma_IAudioClient* pAudioClient, ma_uint32* pFrameCount) -{ - ma_uint32 paddingFramesCount; - HRESULT hr; - ma_share_mode shareMode; - - MA_ASSERT(pDevice != NULL); - MA_ASSERT(pFrameCount != NULL); - - *pFrameCount = 0; - - if ((ma_ptr)pAudioClient != pDevice->wasapi.pAudioClientPlayback && (ma_ptr)pAudioClient != pDevice->wasapi.pAudioClientCapture) { - return MA_INVALID_OPERATION; - } - - /* - I've had a report that GetCurrentPadding() is returning a frame count of 0 which is preventing - higher level function calls from doing anything because it thinks nothing is available. I have - taken a look at the documentation and it looks like this is unnecessary in exclusive mode. - - From Microsoft's documentation: - - For an exclusive-mode rendering or capture stream that was initialized with the - AUDCLNT_STREAMFLAGS_EVENTCALLBACK flag, the client typically has no use for the padding - value reported by GetCurrentPadding. Instead, the client accesses an entire buffer during - each processing pass. - - Considering this, I'm going to skip GetCurrentPadding() for exclusive mode and just report the - entire buffer. This depends on the caller making sure they wait on the event handler. - */ - shareMode = ((ma_ptr)pAudioClient == pDevice->wasapi.pAudioClientPlayback) ? pDevice->playback.shareMode : pDevice->capture.shareMode; - if (shareMode == ma_share_mode_shared) { - /* Shared mode. */ - hr = ma_IAudioClient_GetCurrentPadding(pAudioClient, &paddingFramesCount); - if (FAILED(hr)) { - return ma_result_from_HRESULT(hr); - } - - if ((ma_ptr)pAudioClient == pDevice->wasapi.pAudioClientPlayback) { - *pFrameCount = pDevice->wasapi.actualBufferSizeInFramesPlayback - paddingFramesCount; - } else { - *pFrameCount = paddingFramesCount; - } - } else { - /* Exclusive mode. */ - if ((ma_ptr)pAudioClient == pDevice->wasapi.pAudioClientPlayback) { - *pFrameCount = pDevice->wasapi.actualBufferSizeInFramesPlayback; - } else { - *pFrameCount = pDevice->wasapi.actualBufferSizeInFramesCapture; - } - } - - return MA_SUCCESS; -} - - -static ma_result ma_device_reroute__wasapi(ma_device* pDevice, ma_device_type deviceType) -{ - ma_result result; - - if (deviceType == ma_device_type_duplex) { - return MA_INVALID_ARGS; - } - - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "=== CHANGING DEVICE ===\n"); - - result = ma_device_reinit__wasapi(pDevice, deviceType); - if (result != MA_SUCCESS) { - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_WARNING, "[WASAPI] Reinitializing device after route change failed.\n"); - return result; - } - - ma_device__post_init_setup(pDevice, deviceType); - ma_device__on_notification_rerouted(pDevice); - - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "=== DEVICE CHANGED ===\n"); - - return MA_SUCCESS; -} - -static ma_result ma_device_start__wasapi_nolock(ma_device* pDevice) -{ - HRESULT hr; - - if (pDevice->pContext->wasapi.hAvrt) { - const char* pTaskName = ma_to_usage_string__wasapi(pDevice->wasapi.usage); - if (pTaskName) { - DWORD idx = 0; - pDevice->wasapi.hAvrtHandle = (ma_handle)((MA_PFN_AvSetMmThreadCharacteristicsA)pDevice->pContext->wasapi.AvSetMmThreadCharacteristicsA)(pTaskName, &idx); - } - } - - if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex || pDevice->type == ma_device_type_loopback) { - hr = ma_IAudioClient_Start((ma_IAudioClient*)pDevice->wasapi.pAudioClientCapture); - if (FAILED(hr)) { - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to start internal capture device. HRESULT = %d.", (int)hr); - return ma_result_from_HRESULT(hr); - } - - ma_atomic_bool32_set(&pDevice->wasapi.isStartedCapture, MA_TRUE); - } - - if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) { - hr = ma_IAudioClient_Start((ma_IAudioClient*)pDevice->wasapi.pAudioClientPlayback); - if (FAILED(hr)) { - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to start internal playback device. HRESULT = %d.", (int)hr); - return ma_result_from_HRESULT(hr); - } - - ma_atomic_bool32_set(&pDevice->wasapi.isStartedPlayback, MA_TRUE); - } - - return MA_SUCCESS; -} - -static ma_result ma_device_start__wasapi(ma_device* pDevice) -{ - ma_result result; - - MA_ASSERT(pDevice != NULL); - - /* Wait for any rerouting to finish before attempting to start the device. */ - ma_mutex_lock(&pDevice->wasapi.rerouteLock); - { - result = ma_device_start__wasapi_nolock(pDevice); - } - ma_mutex_unlock(&pDevice->wasapi.rerouteLock); - - return result; -} - -static ma_result ma_device_stop__wasapi_nolock(ma_device* pDevice) -{ - ma_result result; - HRESULT hr; - - MA_ASSERT(pDevice != NULL); - - if (pDevice->wasapi.hAvrtHandle) { - ((MA_PFN_AvRevertMmThreadCharacteristics)pDevice->pContext->wasapi.AvRevertMmThreadcharacteristics)((HANDLE)pDevice->wasapi.hAvrtHandle); - pDevice->wasapi.hAvrtHandle = NULL; - } - - if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex || pDevice->type == ma_device_type_loopback) { - /* If we have a mapped buffer we need to release it. */ - if (pDevice->wasapi.pMappedBufferCapture != NULL) { - ma_IAudioCaptureClient_ReleaseBuffer((ma_IAudioCaptureClient*)pDevice->wasapi.pCaptureClient, pDevice->wasapi.mappedBufferCaptureCap); - pDevice->wasapi.pMappedBufferCapture = NULL; - pDevice->wasapi.mappedBufferCaptureCap = 0; - pDevice->wasapi.mappedBufferCaptureLen = 0; - } - - hr = ma_IAudioClient_Stop((ma_IAudioClient*)pDevice->wasapi.pAudioClientCapture); - if (FAILED(hr)) { - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to stop internal capture device."); - return ma_result_from_HRESULT(hr); - } - - /* The audio client needs to be reset otherwise restarting will fail. */ - hr = ma_IAudioClient_Reset((ma_IAudioClient*)pDevice->wasapi.pAudioClientCapture); - if (FAILED(hr)) { - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to reset internal capture device."); - return ma_result_from_HRESULT(hr); - } - - ma_atomic_bool32_set(&pDevice->wasapi.isStartedCapture, MA_FALSE); - } - - if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) { - if (pDevice->wasapi.pMappedBufferPlayback != NULL) { - ma_silence_pcm_frames( - ma_offset_pcm_frames_ptr(pDevice->wasapi.pMappedBufferPlayback, pDevice->wasapi.mappedBufferPlaybackLen, pDevice->playback.internalFormat, pDevice->playback.internalChannels), - pDevice->wasapi.mappedBufferPlaybackCap - pDevice->wasapi.mappedBufferPlaybackLen, - pDevice->playback.internalFormat, pDevice->playback.internalChannels - ); - ma_IAudioRenderClient_ReleaseBuffer((ma_IAudioRenderClient*)pDevice->wasapi.pRenderClient, pDevice->wasapi.mappedBufferPlaybackCap, 0); - pDevice->wasapi.pMappedBufferPlayback = NULL; - pDevice->wasapi.mappedBufferPlaybackCap = 0; - pDevice->wasapi.mappedBufferPlaybackLen = 0; - } - - /* - The buffer needs to be drained before stopping the device. Not doing this will result in the last few frames not getting output to - the speakers. This is a problem for very short sounds because it'll result in a significant portion of it not getting played. - */ - if (ma_atomic_bool32_get(&pDevice->wasapi.isStartedPlayback)) { - /* We need to make sure we put a timeout here or else we'll risk getting stuck in a deadlock in some cases. */ - DWORD waitTime = (pDevice->wasapi.actualBufferSizeInFramesPlayback * 1000) / pDevice->playback.internalSampleRate; - - if (pDevice->playback.shareMode == ma_share_mode_exclusive) { - WaitForSingleObject((HANDLE)pDevice->wasapi.hEventPlayback, waitTime); - } else { - ma_uint32 prevFramesAvailablePlayback = (ma_uint32)-1; - ma_uint32 framesAvailablePlayback; - for (;;) { - result = ma_device__get_available_frames__wasapi(pDevice, (ma_IAudioClient*)pDevice->wasapi.pAudioClientPlayback, &framesAvailablePlayback); - if (result != MA_SUCCESS) { - break; - } - - if (framesAvailablePlayback >= pDevice->wasapi.actualBufferSizeInFramesPlayback) { - break; - } - - /* - Just a safety check to avoid an infinite loop. If this iteration results in a situation where the number of available frames - has not changed, get out of the loop. I don't think this should ever happen, but I think it's nice to have just in case. - */ - if (framesAvailablePlayback == prevFramesAvailablePlayback) { - break; - } - prevFramesAvailablePlayback = framesAvailablePlayback; - - ResetEvent((HANDLE)pDevice->wasapi.hEventPlayback); /* Manual reset. */ - WaitForSingleObject((HANDLE)pDevice->wasapi.hEventPlayback, waitTime); - } - } - } - - hr = ma_IAudioClient_Stop((ma_IAudioClient*)pDevice->wasapi.pAudioClientPlayback); - if (FAILED(hr)) { - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to stop internal playback device."); - return ma_result_from_HRESULT(hr); - } - - /* The audio client needs to be reset otherwise restarting will fail. */ - { - ma_int32 retries = 5; - - while ((hr = ma_IAudioClient_Reset((ma_IAudioClient*)pDevice->wasapi.pAudioClientPlayback)) == MA_AUDCLNT_E_BUFFER_OPERATION_PENDING && retries > 0) { - ma_sleep(10); - retries -= 1; - } - } - - if (FAILED(hr)) { - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to reset internal playback device."); - return ma_result_from_HRESULT(hr); - } - - ma_atomic_bool32_set(&pDevice->wasapi.isStartedPlayback, MA_FALSE); - } - - return MA_SUCCESS; -} - -static ma_result ma_device_stop__wasapi(ma_device* pDevice) -{ - ma_result result; - - MA_ASSERT(pDevice != NULL); - - /* Wait for any rerouting to finish before attempting to stop the device. */ - ma_mutex_lock(&pDevice->wasapi.rerouteLock); - { - result = ma_device_stop__wasapi_nolock(pDevice); - } - ma_mutex_unlock(&pDevice->wasapi.rerouteLock); - - return result; -} - - -#ifndef MA_WASAPI_WAIT_TIMEOUT_MILLISECONDS -#define MA_WASAPI_WAIT_TIMEOUT_MILLISECONDS 5000 -#endif - -static ma_result ma_device_read__wasapi(ma_device* pDevice, void* pFrames, ma_uint32 frameCount, ma_uint32* pFramesRead) -{ - ma_result result = MA_SUCCESS; - ma_uint32 totalFramesProcessed = 0; - - /* - When reading, we need to get a buffer and process all of it before releasing it. Because the - frame count (frameCount) can be different to the size of the buffer, we'll need to cache the - pointer to the buffer. - */ - - /* Keep running until we've processed the requested number of frames. */ - while (ma_device_get_state(pDevice) == ma_device_state_started && totalFramesProcessed < frameCount) { - ma_uint32 framesRemaining = frameCount - totalFramesProcessed; - - /* If we have a mapped data buffer, consume that first. */ - if (pDevice->wasapi.pMappedBufferCapture != NULL) { - /* We have a cached data pointer so consume that before grabbing another one from WASAPI. */ - ma_uint32 framesToProcessNow = framesRemaining; - if (framesToProcessNow > pDevice->wasapi.mappedBufferCaptureLen) { - framesToProcessNow = pDevice->wasapi.mappedBufferCaptureLen; - } - - /* Now just copy the data over to the output buffer. */ - ma_copy_pcm_frames( - ma_offset_pcm_frames_ptr(pFrames, totalFramesProcessed, pDevice->capture.internalFormat, pDevice->capture.internalChannels), - ma_offset_pcm_frames_const_ptr(pDevice->wasapi.pMappedBufferCapture, pDevice->wasapi.mappedBufferCaptureCap - pDevice->wasapi.mappedBufferCaptureLen, pDevice->capture.internalFormat, pDevice->capture.internalChannels), - framesToProcessNow, - pDevice->capture.internalFormat, pDevice->capture.internalChannels - ); - - totalFramesProcessed += framesToProcessNow; - pDevice->wasapi.mappedBufferCaptureLen -= framesToProcessNow; - - /* If the data buffer has been fully consumed we need to release it. */ - if (pDevice->wasapi.mappedBufferCaptureLen == 0) { - ma_IAudioCaptureClient_ReleaseBuffer((ma_IAudioCaptureClient*)pDevice->wasapi.pCaptureClient, pDevice->wasapi.mappedBufferCaptureCap); - pDevice->wasapi.pMappedBufferCapture = NULL; - pDevice->wasapi.mappedBufferCaptureCap = 0; - } - } else { - /* We don't have any cached data pointer, so grab another one. */ - HRESULT hr; - DWORD flags = 0; - - /* First just ask WASAPI for a data buffer. If it's not available, we'll wait for more. */ - hr = ma_IAudioCaptureClient_GetBuffer((ma_IAudioCaptureClient*)pDevice->wasapi.pCaptureClient, (BYTE**)&pDevice->wasapi.pMappedBufferCapture, &pDevice->wasapi.mappedBufferCaptureCap, &flags, NULL, NULL); - if (hr == S_OK) { - /* We got a data buffer. Continue to the next loop iteration which will then read from the mapped pointer. */ - pDevice->wasapi.mappedBufferCaptureLen = pDevice->wasapi.mappedBufferCaptureCap; - - /* - There have been reports that indicate that at times the AUDCLNT_BUFFERFLAGS_DATA_DISCONTINUITY is reported for every - call to IAudioCaptureClient_GetBuffer() above which results in spamming of the debug messages below. To partially - work around this, I'm only outputting these messages when MA_DEBUG_OUTPUT is explicitly defined. The better solution - would be to figure out why the flag is always getting reported. - */ - #if defined(MA_DEBUG_OUTPUT) - { - if (flags != 0) { - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "[WASAPI] Capture Flags: %ld\n", flags); - - if ((flags & MA_AUDCLNT_BUFFERFLAGS_DATA_DISCONTINUITY) != 0) { - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "[WASAPI] Data discontinuity (possible overrun). Attempting recovery. mappedBufferCaptureCap=%d\n", pDevice->wasapi.mappedBufferCaptureCap); - } - } - } - #endif - - /* Overrun detection. */ - if ((flags & MA_AUDCLNT_BUFFERFLAGS_DATA_DISCONTINUITY) != 0) { - /* Glitched. Probably due to an overrun. */ - - /* - If we got an overrun it probably means we're straddling the end of the buffer. In normal capture - mode this is the fault of the client application because they're responsible for ensuring data is - processed fast enough. In duplex mode, however, the processing of audio is tied to the playback - device, so this can possibly be the result of a timing de-sync. - - In capture mode we're not going to do any kind of recovery because the real fix is for the client - application to process faster. In duplex mode, we'll treat this as a desync and reset the buffers - to prevent a never-ending sequence of glitches due to straddling the end of the buffer. - */ - if (pDevice->type == ma_device_type_duplex) { - /* - Experiment: - - If we empty out the *entire* buffer we may end up putting ourselves into an underrun position - which isn't really any better than the overrun we're probably in right now. Instead we'll just - empty out about half. - */ - ma_uint32 i; - ma_uint32 periodCount = (pDevice->wasapi.actualBufferSizeInFramesCapture / pDevice->wasapi.periodSizeInFramesCapture); - ma_uint32 iterationCount = periodCount / 2; - if ((periodCount % 2) > 0) { - iterationCount += 1; - } - - for (i = 0; i < iterationCount; i += 1) { - hr = ma_IAudioCaptureClient_ReleaseBuffer((ma_IAudioCaptureClient*)pDevice->wasapi.pCaptureClient, pDevice->wasapi.mappedBufferCaptureCap); - if (FAILED(hr)) { - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "[WASAPI] Data discontinuity recovery: IAudioCaptureClient_ReleaseBuffer() failed with %ld.\n", hr); - break; - } - - flags = 0; - hr = ma_IAudioCaptureClient_GetBuffer((ma_IAudioCaptureClient*)pDevice->wasapi.pCaptureClient, (BYTE**)&pDevice->wasapi.pMappedBufferCapture, &pDevice->wasapi.mappedBufferCaptureCap, &flags, NULL, NULL); - if (hr == MA_AUDCLNT_S_BUFFER_EMPTY || FAILED(hr)) { - /* - The buffer has been completely emptied or an error occurred. In this case we'll need - to reset the state of the mapped buffer which will trigger the next iteration to get - a fresh buffer from WASAPI. - */ - pDevice->wasapi.pMappedBufferCapture = NULL; - pDevice->wasapi.mappedBufferCaptureCap = 0; - pDevice->wasapi.mappedBufferCaptureLen = 0; - - if (hr == MA_AUDCLNT_S_BUFFER_EMPTY) { - if ((flags & MA_AUDCLNT_BUFFERFLAGS_DATA_DISCONTINUITY) != 0) { - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "[WASAPI] Data discontinuity recovery: Buffer emptied, and data discontinuity still reported.\n"); - } else { - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "[WASAPI] Data discontinuity recovery: Buffer emptied.\n"); - } - } - - if (FAILED(hr)) { - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "[WASAPI] Data discontinuity recovery: IAudioCaptureClient_GetBuffer() failed with %ld.\n", hr); - } - - break; - } - } - - /* If at this point we have a valid buffer mapped, make sure the buffer length is set appropriately. */ - if (pDevice->wasapi.pMappedBufferCapture != NULL) { - pDevice->wasapi.mappedBufferCaptureLen = pDevice->wasapi.mappedBufferCaptureCap; - } - } - } - - continue; - } else { - if (hr == MA_AUDCLNT_S_BUFFER_EMPTY || hr == MA_AUDCLNT_E_BUFFER_ERROR) { - /* - No data is available. We need to wait for more. There's two situations to consider - here. The first is normal capture mode. If this times out it probably means the - microphone isn't delivering data for whatever reason. In this case we'll just - abort the read and return whatever we were able to get. The other situations is - loopback mode, in which case a timeout probably just means the nothing is playing - through the speakers. - */ - - /* Experiment: Use a shorter timeout for loopback mode. */ - DWORD timeoutInMilliseconds = MA_WASAPI_WAIT_TIMEOUT_MILLISECONDS; - if (pDevice->type == ma_device_type_loopback) { - timeoutInMilliseconds = 10; - } - - if (WaitForSingleObject((HANDLE)pDevice->wasapi.hEventCapture, timeoutInMilliseconds) != WAIT_OBJECT_0) { - if (pDevice->type == ma_device_type_loopback) { - continue; /* Keep waiting in loopback mode. */ - } else { - result = MA_ERROR; - break; /* Wait failed. */ - } - } - - /* At this point we should be able to loop back to the start of the loop and try retrieving a data buffer again. */ - } else { - /* An error occurred and we need to abort. */ - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to retrieve internal buffer from capture device in preparation for reading from the device. HRESULT = %d. Stopping device.\n", (int)hr); - result = ma_result_from_HRESULT(hr); - break; - } - } - } - } - - /* - If we were unable to process the entire requested frame count, but we still have a mapped buffer, - there's a good chance either an error occurred or the device was stopped mid-read. In this case - we'll need to make sure the buffer is released. - */ - if (totalFramesProcessed < frameCount && pDevice->wasapi.pMappedBufferCapture != NULL) { - ma_IAudioCaptureClient_ReleaseBuffer((ma_IAudioCaptureClient*)pDevice->wasapi.pCaptureClient, pDevice->wasapi.mappedBufferCaptureCap); - pDevice->wasapi.pMappedBufferCapture = NULL; - pDevice->wasapi.mappedBufferCaptureCap = 0; - pDevice->wasapi.mappedBufferCaptureLen = 0; - } - - if (pFramesRead != NULL) { - *pFramesRead = totalFramesProcessed; - } - - return result; -} - -static ma_result ma_device_write__wasapi(ma_device* pDevice, const void* pFrames, ma_uint32 frameCount, ma_uint32* pFramesWritten) -{ - ma_result result = MA_SUCCESS; - ma_uint32 totalFramesProcessed = 0; - - /* Keep writing to the device until it's stopped or we've consumed all of our input. */ - while (ma_device_get_state(pDevice) == ma_device_state_started && totalFramesProcessed < frameCount) { - ma_uint32 framesRemaining = frameCount - totalFramesProcessed; - - /* - We're going to do this in a similar way to capture. We'll first check if the cached data pointer - is valid, and if so, read from that. Otherwise We will call IAudioRenderClient_GetBuffer() with - a requested buffer size equal to our actual period size. If it returns AUDCLNT_E_BUFFER_TOO_LARGE - it means we need to wait for some data to become available. - */ - if (pDevice->wasapi.pMappedBufferPlayback != NULL) { - /* We still have some space available in the mapped data buffer. Write to it. */ - ma_uint32 framesToProcessNow = framesRemaining; - if (framesToProcessNow > (pDevice->wasapi.mappedBufferPlaybackCap - pDevice->wasapi.mappedBufferPlaybackLen)) { - framesToProcessNow = (pDevice->wasapi.mappedBufferPlaybackCap - pDevice->wasapi.mappedBufferPlaybackLen); - } - - /* Now just copy the data over to the output buffer. */ - ma_copy_pcm_frames( - ma_offset_pcm_frames_ptr(pDevice->wasapi.pMappedBufferPlayback, pDevice->wasapi.mappedBufferPlaybackLen, pDevice->playback.internalFormat, pDevice->playback.internalChannels), - ma_offset_pcm_frames_const_ptr(pFrames, totalFramesProcessed, pDevice->playback.internalFormat, pDevice->playback.internalChannels), - framesToProcessNow, - pDevice->playback.internalFormat, pDevice->playback.internalChannels - ); - - totalFramesProcessed += framesToProcessNow; - pDevice->wasapi.mappedBufferPlaybackLen += framesToProcessNow; - - /* If the data buffer has been fully consumed we need to release it. */ - if (pDevice->wasapi.mappedBufferPlaybackLen == pDevice->wasapi.mappedBufferPlaybackCap) { - ma_IAudioRenderClient_ReleaseBuffer((ma_IAudioRenderClient*)pDevice->wasapi.pRenderClient, pDevice->wasapi.mappedBufferPlaybackCap, 0); - pDevice->wasapi.pMappedBufferPlayback = NULL; - pDevice->wasapi.mappedBufferPlaybackCap = 0; - pDevice->wasapi.mappedBufferPlaybackLen = 0; - - /* - In exclusive mode we need to wait here. Exclusive mode is weird because GetBuffer() never - seems to return AUDCLNT_E_BUFFER_TOO_LARGE, which is what we normally use to determine - whether or not we need to wait for more data. - */ - if (pDevice->playback.shareMode == ma_share_mode_exclusive) { - if (WaitForSingleObject((HANDLE)pDevice->wasapi.hEventPlayback, MA_WASAPI_WAIT_TIMEOUT_MILLISECONDS) != WAIT_OBJECT_0) { - result = MA_ERROR; - break; /* Wait failed. Probably timed out. */ - } - } - } - } else { - /* We don't have a mapped data buffer so we'll need to get one. */ - HRESULT hr; - ma_uint32 bufferSizeInFrames; - - /* Special rules for exclusive mode. */ - if (pDevice->playback.shareMode == ma_share_mode_exclusive) { - bufferSizeInFrames = pDevice->wasapi.actualBufferSizeInFramesPlayback; - } else { - bufferSizeInFrames = pDevice->wasapi.periodSizeInFramesPlayback; - } - - hr = ma_IAudioRenderClient_GetBuffer((ma_IAudioRenderClient*)pDevice->wasapi.pRenderClient, bufferSizeInFrames, (BYTE**)&pDevice->wasapi.pMappedBufferPlayback); - if (hr == S_OK) { - /* We have data available. */ - pDevice->wasapi.mappedBufferPlaybackCap = bufferSizeInFrames; - pDevice->wasapi.mappedBufferPlaybackLen = 0; - } else { - if (hr == MA_AUDCLNT_E_BUFFER_TOO_LARGE || hr == MA_AUDCLNT_E_BUFFER_ERROR) { - /* Not enough data available. We need to wait for more. */ - if (WaitForSingleObject((HANDLE)pDevice->wasapi.hEventPlayback, MA_WASAPI_WAIT_TIMEOUT_MILLISECONDS) != WAIT_OBJECT_0) { - result = MA_ERROR; - break; /* Wait failed. Probably timed out. */ - } - } else { - /* Some error occurred. We'll need to abort. */ - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to retrieve internal buffer from playback device in preparation for writing to the device. HRESULT = %d. Stopping device.\n", (int)hr); - result = ma_result_from_HRESULT(hr); - break; - } - } - } - } - - if (pFramesWritten != NULL) { - *pFramesWritten = totalFramesProcessed; - } - - return result; -} - -static ma_result ma_device_data_loop_wakeup__wasapi(ma_device* pDevice) -{ - MA_ASSERT(pDevice != NULL); - - if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex || pDevice->type == ma_device_type_loopback) { - SetEvent((HANDLE)pDevice->wasapi.hEventCapture); - } - - if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) { - SetEvent((HANDLE)pDevice->wasapi.hEventPlayback); - } - - return MA_SUCCESS; -} - - -static ma_result ma_context_uninit__wasapi(ma_context* pContext) -{ - ma_context_command__wasapi cmd = ma_context_init_command__wasapi(MA_CONTEXT_COMMAND_QUIT__WASAPI); - - MA_ASSERT(pContext != NULL); - MA_ASSERT(pContext->backend == ma_backend_wasapi); - - ma_context_post_command__wasapi(pContext, &cmd); - ma_thread_wait(&pContext->wasapi.commandThread); - - if (pContext->wasapi.hAvrt) { - ma_dlclose(ma_context_get_log(pContext), pContext->wasapi.hAvrt); - pContext->wasapi.hAvrt = NULL; - } - - #if defined(MA_WIN32_UWP) - { - if (pContext->wasapi.hMMDevapi) { - ma_dlclose(ma_context_get_log(pContext), pContext->wasapi.hMMDevapi); - pContext->wasapi.hMMDevapi = NULL; - } - } - #endif - - /* Only after the thread has been terminated can we uninitialize the sync objects for the command thread. */ - ma_semaphore_uninit(&pContext->wasapi.commandSem); - ma_mutex_uninit(&pContext->wasapi.commandLock); - - return MA_SUCCESS; -} - -static ma_result ma_context_init__wasapi(ma_context* pContext, const ma_context_config* pConfig, ma_backend_callbacks* pCallbacks) -{ - ma_result result = MA_SUCCESS; - - MA_ASSERT(pContext != NULL); - - (void)pConfig; - -#ifdef MA_WIN32_DESKTOP - /* - WASAPI is only supported in Vista SP1 and newer. The reason for SP1 and not the base version of Vista is that event-driven - exclusive mode does not work until SP1. - - Unfortunately older compilers don't define these functions so we need to dynamically load them in order to avoid a link error. - */ - { - ma_OSVERSIONINFOEXW osvi; - ma_handle kernel32DLL; - ma_PFNVerifyVersionInfoW _VerifyVersionInfoW; - ma_PFNVerSetConditionMask _VerSetConditionMask; - - kernel32DLL = ma_dlopen(ma_context_get_log(pContext), "kernel32.dll"); - if (kernel32DLL == NULL) { - return MA_NO_BACKEND; - } - - _VerifyVersionInfoW = (ma_PFNVerifyVersionInfoW )ma_dlsym(ma_context_get_log(pContext), kernel32DLL, "VerifyVersionInfoW"); - _VerSetConditionMask = (ma_PFNVerSetConditionMask)ma_dlsym(ma_context_get_log(pContext), kernel32DLL, "VerSetConditionMask"); - if (_VerifyVersionInfoW == NULL || _VerSetConditionMask == NULL) { - ma_dlclose(ma_context_get_log(pContext), kernel32DLL); - return MA_NO_BACKEND; - } - - MA_ZERO_OBJECT(&osvi); - osvi.dwOSVersionInfoSize = sizeof(osvi); - osvi.dwMajorVersion = ((MA_WIN32_WINNT_VISTA >> 8) & 0xFF); - osvi.dwMinorVersion = ((MA_WIN32_WINNT_VISTA >> 0) & 0xFF); - osvi.wServicePackMajor = 1; - if (_VerifyVersionInfoW(&osvi, MA_VER_MAJORVERSION | MA_VER_MINORVERSION | MA_VER_SERVICEPACKMAJOR, _VerSetConditionMask(_VerSetConditionMask(_VerSetConditionMask(0, MA_VER_MAJORVERSION, MA_VER_GREATER_EQUAL), MA_VER_MINORVERSION, MA_VER_GREATER_EQUAL), MA_VER_SERVICEPACKMAJOR, MA_VER_GREATER_EQUAL))) { - result = MA_SUCCESS; - } else { - result = MA_NO_BACKEND; - } - - ma_dlclose(ma_context_get_log(pContext), kernel32DLL); - } -#endif - - if (result != MA_SUCCESS) { - return result; - } - - MA_ZERO_OBJECT(&pContext->wasapi); - - - #if defined(MA_WIN32_UWP) - { - /* Link to mmdevapi so we can get access to ActivateAudioInterfaceAsync(). */ - pContext->wasapi.hMMDevapi = ma_dlopen(ma_context_get_log(pContext), "mmdevapi.dll"); - if (pContext->wasapi.hMMDevapi) { - pContext->wasapi.ActivateAudioInterfaceAsync = ma_dlsym(ma_context_get_log(pContext), pContext->wasapi.hMMDevapi, "ActivateAudioInterfaceAsync"); - if (pContext->wasapi.ActivateAudioInterfaceAsync == NULL) { - ma_dlclose(ma_context_get_log(pContext), pContext->wasapi.hMMDevapi); - return MA_NO_BACKEND; /* ActivateAudioInterfaceAsync() could not be loaded. */ - } - } else { - return MA_NO_BACKEND; /* Failed to load mmdevapi.dll which is required for ActivateAudioInterfaceAsync() */ - } - } - #endif - - /* Optionally use the Avrt API to specify the audio thread's latency sensitivity requirements */ - pContext->wasapi.hAvrt = ma_dlopen(ma_context_get_log(pContext), "avrt.dll"); - if (pContext->wasapi.hAvrt) { - pContext->wasapi.AvSetMmThreadCharacteristicsA = ma_dlsym(ma_context_get_log(pContext), pContext->wasapi.hAvrt, "AvSetMmThreadCharacteristicsA"); - pContext->wasapi.AvRevertMmThreadcharacteristics = ma_dlsym(ma_context_get_log(pContext), pContext->wasapi.hAvrt, "AvRevertMmThreadCharacteristics"); - - /* If either function could not be found, disable use of avrt entirely. */ - if (!pContext->wasapi.AvSetMmThreadCharacteristicsA || !pContext->wasapi.AvRevertMmThreadcharacteristics) { - pContext->wasapi.AvSetMmThreadCharacteristicsA = NULL; - pContext->wasapi.AvRevertMmThreadcharacteristics = NULL; - ma_dlclose(ma_context_get_log(pContext), pContext->wasapi.hAvrt); - pContext->wasapi.hAvrt = NULL; - } - } - - - /* - Annoyingly, WASAPI does not allow you to release an IAudioClient object from a different thread - than the one that retrieved it with GetService(). This can result in a deadlock in two - situations: - - 1) When calling ma_device_uninit() from a different thread to ma_device_init(); and - 2) When uninitializing and reinitializing the internal IAudioClient object in response to - automatic stream routing. - - We could define ma_device_uninit() such that it must be called on the same thread as - ma_device_init(). We could also just not release the IAudioClient when performing automatic - stream routing to avoid the deadlock. Neither of these are acceptable solutions in my view so - we're going to have to work around this with a worker thread. This is not ideal, but I can't - think of a better way to do this. - - More information about this can be found here: - - https://docs.microsoft.com/en-us/windows/win32/api/audioclient/nn-audioclient-iaudiorenderclient - - Note this section: - - When releasing an IAudioRenderClient interface instance, the client must call the interface's - Release method from the same thread as the call to IAudioClient::GetService that created the - object. - */ - { - result = ma_mutex_init(&pContext->wasapi.commandLock); - if (result != MA_SUCCESS) { - return result; - } - - result = ma_semaphore_init(0, &pContext->wasapi.commandSem); - if (result != MA_SUCCESS) { - ma_mutex_uninit(&pContext->wasapi.commandLock); - return result; - } - - result = ma_thread_create(&pContext->wasapi.commandThread, ma_thread_priority_normal, 0, ma_context_command_thread__wasapi, pContext, &pContext->allocationCallbacks); - if (result != MA_SUCCESS) { - ma_semaphore_uninit(&pContext->wasapi.commandSem); - ma_mutex_uninit(&pContext->wasapi.commandLock); - return result; - } - } - - - pCallbacks->onContextInit = ma_context_init__wasapi; - pCallbacks->onContextUninit = ma_context_uninit__wasapi; - pCallbacks->onContextEnumerateDevices = ma_context_enumerate_devices__wasapi; - pCallbacks->onContextGetDeviceInfo = ma_context_get_device_info__wasapi; - pCallbacks->onDeviceInit = ma_device_init__wasapi; - pCallbacks->onDeviceUninit = ma_device_uninit__wasapi; - pCallbacks->onDeviceStart = ma_device_start__wasapi; - pCallbacks->onDeviceStop = ma_device_stop__wasapi; - pCallbacks->onDeviceRead = ma_device_read__wasapi; - pCallbacks->onDeviceWrite = ma_device_write__wasapi; - pCallbacks->onDeviceDataLoop = NULL; - pCallbacks->onDeviceDataLoopWakeup = ma_device_data_loop_wakeup__wasapi; - - return MA_SUCCESS; -} -#endif - -/****************************************************************************** - -DirectSound Backend - -******************************************************************************/ -#ifdef MA_HAS_DSOUND -/*#include */ - -/*static const GUID MA_GUID_IID_DirectSoundNotify = {0xb0210783, 0x89cd, 0x11d0, {0xaf, 0x08, 0x00, 0xa0, 0xc9, 0x25, 0xcd, 0x16}};*/ - -/* miniaudio only uses priority or exclusive modes. */ -#define MA_DSSCL_NORMAL 1 -#define MA_DSSCL_PRIORITY 2 -#define MA_DSSCL_EXCLUSIVE 3 -#define MA_DSSCL_WRITEPRIMARY 4 - -#define MA_DSCAPS_PRIMARYMONO 0x00000001 -#define MA_DSCAPS_PRIMARYSTEREO 0x00000002 -#define MA_DSCAPS_PRIMARY8BIT 0x00000004 -#define MA_DSCAPS_PRIMARY16BIT 0x00000008 -#define MA_DSCAPS_CONTINUOUSRATE 0x00000010 -#define MA_DSCAPS_EMULDRIVER 0x00000020 -#define MA_DSCAPS_CERTIFIED 0x00000040 -#define MA_DSCAPS_SECONDARYMONO 0x00000100 -#define MA_DSCAPS_SECONDARYSTEREO 0x00000200 -#define MA_DSCAPS_SECONDARY8BIT 0x00000400 -#define MA_DSCAPS_SECONDARY16BIT 0x00000800 - -#define MA_DSBCAPS_PRIMARYBUFFER 0x00000001 -#define MA_DSBCAPS_STATIC 0x00000002 -#define MA_DSBCAPS_LOCHARDWARE 0x00000004 -#define MA_DSBCAPS_LOCSOFTWARE 0x00000008 -#define MA_DSBCAPS_CTRL3D 0x00000010 -#define MA_DSBCAPS_CTRLFREQUENCY 0x00000020 -#define MA_DSBCAPS_CTRLPAN 0x00000040 -#define MA_DSBCAPS_CTRLVOLUME 0x00000080 -#define MA_DSBCAPS_CTRLPOSITIONNOTIFY 0x00000100 -#define MA_DSBCAPS_CTRLFX 0x00000200 -#define MA_DSBCAPS_STICKYFOCUS 0x00004000 -#define MA_DSBCAPS_GLOBALFOCUS 0x00008000 -#define MA_DSBCAPS_GETCURRENTPOSITION2 0x00010000 -#define MA_DSBCAPS_MUTE3DATMAXDISTANCE 0x00020000 -#define MA_DSBCAPS_LOCDEFER 0x00040000 -#define MA_DSBCAPS_TRUEPLAYPOSITION 0x00080000 - -#define MA_DSBPLAY_LOOPING 0x00000001 -#define MA_DSBPLAY_LOCHARDWARE 0x00000002 -#define MA_DSBPLAY_LOCSOFTWARE 0x00000004 -#define MA_DSBPLAY_TERMINATEBY_TIME 0x00000008 -#define MA_DSBPLAY_TERMINATEBY_DISTANCE 0x00000010 -#define MA_DSBPLAY_TERMINATEBY_PRIORITY 0x00000020 - -#define MA_DSBSTATUS_PLAYING 0x00000001 -#define MA_DSBSTATUS_BUFFERLOST 0x00000002 -#define MA_DSBSTATUS_LOOPING 0x00000004 -#define MA_DSBSTATUS_LOCHARDWARE 0x00000008 -#define MA_DSBSTATUS_LOCSOFTWARE 0x00000010 -#define MA_DSBSTATUS_TERMINATED 0x00000020 - -#define MA_DSCBSTART_LOOPING 0x00000001 - -typedef struct -{ - DWORD dwSize; - DWORD dwFlags; - DWORD dwBufferBytes; - DWORD dwReserved; - MA_WAVEFORMATEX* lpwfxFormat; - GUID guid3DAlgorithm; -} MA_DSBUFFERDESC; - -typedef struct -{ - DWORD dwSize; - DWORD dwFlags; - DWORD dwBufferBytes; - DWORD dwReserved; - MA_WAVEFORMATEX* lpwfxFormat; - DWORD dwFXCount; - void* lpDSCFXDesc; /* <-- miniaudio doesn't use this, so set to void*. */ -} MA_DSCBUFFERDESC; - -typedef struct -{ - DWORD dwSize; - DWORD dwFlags; - DWORD dwMinSecondarySampleRate; - DWORD dwMaxSecondarySampleRate; - DWORD dwPrimaryBuffers; - DWORD dwMaxHwMixingAllBuffers; - DWORD dwMaxHwMixingStaticBuffers; - DWORD dwMaxHwMixingStreamingBuffers; - DWORD dwFreeHwMixingAllBuffers; - DWORD dwFreeHwMixingStaticBuffers; - DWORD dwFreeHwMixingStreamingBuffers; - DWORD dwMaxHw3DAllBuffers; - DWORD dwMaxHw3DStaticBuffers; - DWORD dwMaxHw3DStreamingBuffers; - DWORD dwFreeHw3DAllBuffers; - DWORD dwFreeHw3DStaticBuffers; - DWORD dwFreeHw3DStreamingBuffers; - DWORD dwTotalHwMemBytes; - DWORD dwFreeHwMemBytes; - DWORD dwMaxContigFreeHwMemBytes; - DWORD dwUnlockTransferRateHwBuffers; - DWORD dwPlayCpuOverheadSwBuffers; - DWORD dwReserved1; - DWORD dwReserved2; -} MA_DSCAPS; - -typedef struct -{ - DWORD dwSize; - DWORD dwFlags; - DWORD dwBufferBytes; - DWORD dwUnlockTransferRate; - DWORD dwPlayCpuOverhead; -} MA_DSBCAPS; - -typedef struct -{ - DWORD dwSize; - DWORD dwFlags; - DWORD dwFormats; - DWORD dwChannels; -} MA_DSCCAPS; - -typedef struct -{ - DWORD dwSize; - DWORD dwFlags; - DWORD dwBufferBytes; - DWORD dwReserved; -} MA_DSCBCAPS; - -typedef struct -{ - DWORD dwOffset; - HANDLE hEventNotify; -} MA_DSBPOSITIONNOTIFY; - -typedef struct ma_IDirectSound ma_IDirectSound; -typedef struct ma_IDirectSoundBuffer ma_IDirectSoundBuffer; -typedef struct ma_IDirectSoundCapture ma_IDirectSoundCapture; -typedef struct ma_IDirectSoundCaptureBuffer ma_IDirectSoundCaptureBuffer; -typedef struct ma_IDirectSoundNotify ma_IDirectSoundNotify; - - -/* -COM objects. The way these work is that you have a vtable (a list of function pointers, kind of -like how C++ works internally), and then you have a structure with a single member, which is a -pointer to the vtable. The vtable is where the methods of the object are defined. Methods need -to be in a specific order, and parent classes need to have their methods declared first. -*/ - -/* IDirectSound */ -typedef struct -{ - /* IUnknown */ - HRESULT (STDMETHODCALLTYPE * QueryInterface)(ma_IDirectSound* pThis, const IID* const riid, void** ppObject); - ULONG (STDMETHODCALLTYPE * AddRef) (ma_IDirectSound* pThis); - ULONG (STDMETHODCALLTYPE * Release) (ma_IDirectSound* pThis); - - /* IDirectSound */ - HRESULT (STDMETHODCALLTYPE * CreateSoundBuffer) (ma_IDirectSound* pThis, const MA_DSBUFFERDESC* pDSBufferDesc, ma_IDirectSoundBuffer** ppDSBuffer, void* pUnkOuter); - HRESULT (STDMETHODCALLTYPE * GetCaps) (ma_IDirectSound* pThis, MA_DSCAPS* pDSCaps); - HRESULT (STDMETHODCALLTYPE * DuplicateSoundBuffer)(ma_IDirectSound* pThis, ma_IDirectSoundBuffer* pDSBufferOriginal, ma_IDirectSoundBuffer** ppDSBufferDuplicate); - HRESULT (STDMETHODCALLTYPE * SetCooperativeLevel) (ma_IDirectSound* pThis, HWND hwnd, DWORD dwLevel); - HRESULT (STDMETHODCALLTYPE * Compact) (ma_IDirectSound* pThis); - HRESULT (STDMETHODCALLTYPE * GetSpeakerConfig) (ma_IDirectSound* pThis, DWORD* pSpeakerConfig); - HRESULT (STDMETHODCALLTYPE * SetSpeakerConfig) (ma_IDirectSound* pThis, DWORD dwSpeakerConfig); - HRESULT (STDMETHODCALLTYPE * Initialize) (ma_IDirectSound* pThis, const GUID* pGuidDevice); -} ma_IDirectSoundVtbl; -struct ma_IDirectSound -{ - ma_IDirectSoundVtbl* lpVtbl; -}; -static MA_INLINE HRESULT ma_IDirectSound_QueryInterface(ma_IDirectSound* pThis, const IID* const riid, void** ppObject) { return pThis->lpVtbl->QueryInterface(pThis, riid, ppObject); } -static MA_INLINE ULONG ma_IDirectSound_AddRef(ma_IDirectSound* pThis) { return pThis->lpVtbl->AddRef(pThis); } -static MA_INLINE ULONG ma_IDirectSound_Release(ma_IDirectSound* pThis) { return pThis->lpVtbl->Release(pThis); } -static MA_INLINE HRESULT ma_IDirectSound_CreateSoundBuffer(ma_IDirectSound* pThis, const MA_DSBUFFERDESC* pDSBufferDesc, ma_IDirectSoundBuffer** ppDSBuffer, void* pUnkOuter) { return pThis->lpVtbl->CreateSoundBuffer(pThis, pDSBufferDesc, ppDSBuffer, pUnkOuter); } -static MA_INLINE HRESULT ma_IDirectSound_GetCaps(ma_IDirectSound* pThis, MA_DSCAPS* pDSCaps) { return pThis->lpVtbl->GetCaps(pThis, pDSCaps); } -static MA_INLINE HRESULT ma_IDirectSound_DuplicateSoundBuffer(ma_IDirectSound* pThis, ma_IDirectSoundBuffer* pDSBufferOriginal, ma_IDirectSoundBuffer** ppDSBufferDuplicate) { return pThis->lpVtbl->DuplicateSoundBuffer(pThis, pDSBufferOriginal, ppDSBufferDuplicate); } -static MA_INLINE HRESULT ma_IDirectSound_SetCooperativeLevel(ma_IDirectSound* pThis, HWND hwnd, DWORD dwLevel) { return pThis->lpVtbl->SetCooperativeLevel(pThis, hwnd, dwLevel); } -static MA_INLINE HRESULT ma_IDirectSound_Compact(ma_IDirectSound* pThis) { return pThis->lpVtbl->Compact(pThis); } -static MA_INLINE HRESULT ma_IDirectSound_GetSpeakerConfig(ma_IDirectSound* pThis, DWORD* pSpeakerConfig) { return pThis->lpVtbl->GetSpeakerConfig(pThis, pSpeakerConfig); } -static MA_INLINE HRESULT ma_IDirectSound_SetSpeakerConfig(ma_IDirectSound* pThis, DWORD dwSpeakerConfig) { return pThis->lpVtbl->SetSpeakerConfig(pThis, dwSpeakerConfig); } -static MA_INLINE HRESULT ma_IDirectSound_Initialize(ma_IDirectSound* pThis, const GUID* pGuidDevice) { return pThis->lpVtbl->Initialize(pThis, pGuidDevice); } - - -/* IDirectSoundBuffer */ -typedef struct -{ - /* IUnknown */ - HRESULT (STDMETHODCALLTYPE * QueryInterface)(ma_IDirectSoundBuffer* pThis, const IID* const riid, void** ppObject); - ULONG (STDMETHODCALLTYPE * AddRef) (ma_IDirectSoundBuffer* pThis); - ULONG (STDMETHODCALLTYPE * Release) (ma_IDirectSoundBuffer* pThis); - - /* IDirectSoundBuffer */ - HRESULT (STDMETHODCALLTYPE * GetCaps) (ma_IDirectSoundBuffer* pThis, MA_DSBCAPS* pDSBufferCaps); - HRESULT (STDMETHODCALLTYPE * GetCurrentPosition)(ma_IDirectSoundBuffer* pThis, DWORD* pCurrentPlayCursor, DWORD* pCurrentWriteCursor); - HRESULT (STDMETHODCALLTYPE * GetFormat) (ma_IDirectSoundBuffer* pThis, MA_WAVEFORMATEX* pFormat, DWORD dwSizeAllocated, DWORD* pSizeWritten); - HRESULT (STDMETHODCALLTYPE * GetVolume) (ma_IDirectSoundBuffer* pThis, LONG* pVolume); - HRESULT (STDMETHODCALLTYPE * GetPan) (ma_IDirectSoundBuffer* pThis, LONG* pPan); - HRESULT (STDMETHODCALLTYPE * GetFrequency) (ma_IDirectSoundBuffer* pThis, DWORD* pFrequency); - HRESULT (STDMETHODCALLTYPE * GetStatus) (ma_IDirectSoundBuffer* pThis, DWORD* pStatus); - HRESULT (STDMETHODCALLTYPE * Initialize) (ma_IDirectSoundBuffer* pThis, ma_IDirectSound* pDirectSound, const MA_DSBUFFERDESC* pDSBufferDesc); - HRESULT (STDMETHODCALLTYPE * Lock) (ma_IDirectSoundBuffer* pThis, DWORD dwOffset, DWORD dwBytes, void** ppAudioPtr1, DWORD* pAudioBytes1, void** ppAudioPtr2, DWORD* pAudioBytes2, DWORD dwFlags); - HRESULT (STDMETHODCALLTYPE * Play) (ma_IDirectSoundBuffer* pThis, DWORD dwReserved1, DWORD dwPriority, DWORD dwFlags); - HRESULT (STDMETHODCALLTYPE * SetCurrentPosition)(ma_IDirectSoundBuffer* pThis, DWORD dwNewPosition); - HRESULT (STDMETHODCALLTYPE * SetFormat) (ma_IDirectSoundBuffer* pThis, const MA_WAVEFORMATEX* pFormat); - HRESULT (STDMETHODCALLTYPE * SetVolume) (ma_IDirectSoundBuffer* pThis, LONG volume); - HRESULT (STDMETHODCALLTYPE * SetPan) (ma_IDirectSoundBuffer* pThis, LONG pan); - HRESULT (STDMETHODCALLTYPE * SetFrequency) (ma_IDirectSoundBuffer* pThis, DWORD dwFrequency); - HRESULT (STDMETHODCALLTYPE * Stop) (ma_IDirectSoundBuffer* pThis); - HRESULT (STDMETHODCALLTYPE * Unlock) (ma_IDirectSoundBuffer* pThis, void* pAudioPtr1, DWORD dwAudioBytes1, void* pAudioPtr2, DWORD dwAudioBytes2); - HRESULT (STDMETHODCALLTYPE * Restore) (ma_IDirectSoundBuffer* pThis); -} ma_IDirectSoundBufferVtbl; -struct ma_IDirectSoundBuffer -{ - ma_IDirectSoundBufferVtbl* lpVtbl; -}; -static MA_INLINE HRESULT ma_IDirectSoundBuffer_QueryInterface(ma_IDirectSoundBuffer* pThis, const IID* const riid, void** ppObject) { return pThis->lpVtbl->QueryInterface(pThis, riid, ppObject); } -static MA_INLINE ULONG ma_IDirectSoundBuffer_AddRef(ma_IDirectSoundBuffer* pThis) { return pThis->lpVtbl->AddRef(pThis); } -static MA_INLINE ULONG ma_IDirectSoundBuffer_Release(ma_IDirectSoundBuffer* pThis) { return pThis->lpVtbl->Release(pThis); } -static MA_INLINE HRESULT ma_IDirectSoundBuffer_GetCaps(ma_IDirectSoundBuffer* pThis, MA_DSBCAPS* pDSBufferCaps) { return pThis->lpVtbl->GetCaps(pThis, pDSBufferCaps); } -static MA_INLINE HRESULT ma_IDirectSoundBuffer_GetCurrentPosition(ma_IDirectSoundBuffer* pThis, DWORD* pCurrentPlayCursor, DWORD* pCurrentWriteCursor) { return pThis->lpVtbl->GetCurrentPosition(pThis, pCurrentPlayCursor, pCurrentWriteCursor); } -static MA_INLINE HRESULT ma_IDirectSoundBuffer_GetFormat(ma_IDirectSoundBuffer* pThis, MA_WAVEFORMATEX* pFormat, DWORD dwSizeAllocated, DWORD* pSizeWritten) { return pThis->lpVtbl->GetFormat(pThis, pFormat, dwSizeAllocated, pSizeWritten); } -static MA_INLINE HRESULT ma_IDirectSoundBuffer_GetVolume(ma_IDirectSoundBuffer* pThis, LONG* pVolume) { return pThis->lpVtbl->GetVolume(pThis, pVolume); } -static MA_INLINE HRESULT ma_IDirectSoundBuffer_GetPan(ma_IDirectSoundBuffer* pThis, LONG* pPan) { return pThis->lpVtbl->GetPan(pThis, pPan); } -static MA_INLINE HRESULT ma_IDirectSoundBuffer_GetFrequency(ma_IDirectSoundBuffer* pThis, DWORD* pFrequency) { return pThis->lpVtbl->GetFrequency(pThis, pFrequency); } -static MA_INLINE HRESULT ma_IDirectSoundBuffer_GetStatus(ma_IDirectSoundBuffer* pThis, DWORD* pStatus) { return pThis->lpVtbl->GetStatus(pThis, pStatus); } -static MA_INLINE HRESULT ma_IDirectSoundBuffer_Initialize(ma_IDirectSoundBuffer* pThis, ma_IDirectSound* pDirectSound, const MA_DSBUFFERDESC* pDSBufferDesc) { return pThis->lpVtbl->Initialize(pThis, pDirectSound, pDSBufferDesc); } -static MA_INLINE HRESULT ma_IDirectSoundBuffer_Lock(ma_IDirectSoundBuffer* pThis, DWORD dwOffset, DWORD dwBytes, void** ppAudioPtr1, DWORD* pAudioBytes1, void** ppAudioPtr2, DWORD* pAudioBytes2, DWORD dwFlags) { return pThis->lpVtbl->Lock(pThis, dwOffset, dwBytes, ppAudioPtr1, pAudioBytes1, ppAudioPtr2, pAudioBytes2, dwFlags); } -static MA_INLINE HRESULT ma_IDirectSoundBuffer_Play(ma_IDirectSoundBuffer* pThis, DWORD dwReserved1, DWORD dwPriority, DWORD dwFlags) { return pThis->lpVtbl->Play(pThis, dwReserved1, dwPriority, dwFlags); } -static MA_INLINE HRESULT ma_IDirectSoundBuffer_SetCurrentPosition(ma_IDirectSoundBuffer* pThis, DWORD dwNewPosition) { return pThis->lpVtbl->SetCurrentPosition(pThis, dwNewPosition); } -static MA_INLINE HRESULT ma_IDirectSoundBuffer_SetFormat(ma_IDirectSoundBuffer* pThis, const MA_WAVEFORMATEX* pFormat) { return pThis->lpVtbl->SetFormat(pThis, pFormat); } -static MA_INLINE HRESULT ma_IDirectSoundBuffer_SetVolume(ma_IDirectSoundBuffer* pThis, LONG volume) { return pThis->lpVtbl->SetVolume(pThis, volume); } -static MA_INLINE HRESULT ma_IDirectSoundBuffer_SetPan(ma_IDirectSoundBuffer* pThis, LONG pan) { return pThis->lpVtbl->SetPan(pThis, pan); } -static MA_INLINE HRESULT ma_IDirectSoundBuffer_SetFrequency(ma_IDirectSoundBuffer* pThis, DWORD dwFrequency) { return pThis->lpVtbl->SetFrequency(pThis, dwFrequency); } -static MA_INLINE HRESULT ma_IDirectSoundBuffer_Stop(ma_IDirectSoundBuffer* pThis) { return pThis->lpVtbl->Stop(pThis); } -static MA_INLINE HRESULT ma_IDirectSoundBuffer_Unlock(ma_IDirectSoundBuffer* pThis, void* pAudioPtr1, DWORD dwAudioBytes1, void* pAudioPtr2, DWORD dwAudioBytes2) { return pThis->lpVtbl->Unlock(pThis, pAudioPtr1, dwAudioBytes1, pAudioPtr2, dwAudioBytes2); } -static MA_INLINE HRESULT ma_IDirectSoundBuffer_Restore(ma_IDirectSoundBuffer* pThis) { return pThis->lpVtbl->Restore(pThis); } - - -/* IDirectSoundCapture */ -typedef struct -{ - /* IUnknown */ - HRESULT (STDMETHODCALLTYPE * QueryInterface)(ma_IDirectSoundCapture* pThis, const IID* const riid, void** ppObject); - ULONG (STDMETHODCALLTYPE * AddRef) (ma_IDirectSoundCapture* pThis); - ULONG (STDMETHODCALLTYPE * Release) (ma_IDirectSoundCapture* pThis); - - /* IDirectSoundCapture */ - HRESULT (STDMETHODCALLTYPE * CreateCaptureBuffer)(ma_IDirectSoundCapture* pThis, const MA_DSCBUFFERDESC* pDSCBufferDesc, ma_IDirectSoundCaptureBuffer** ppDSCBuffer, void* pUnkOuter); - HRESULT (STDMETHODCALLTYPE * GetCaps) (ma_IDirectSoundCapture* pThis, MA_DSCCAPS* pDSCCaps); - HRESULT (STDMETHODCALLTYPE * Initialize) (ma_IDirectSoundCapture* pThis, const GUID* pGuidDevice); -} ma_IDirectSoundCaptureVtbl; -struct ma_IDirectSoundCapture -{ - ma_IDirectSoundCaptureVtbl* lpVtbl; -}; -static MA_INLINE HRESULT ma_IDirectSoundCapture_QueryInterface (ma_IDirectSoundCapture* pThis, const IID* const riid, void** ppObject) { return pThis->lpVtbl->QueryInterface(pThis, riid, ppObject); } -static MA_INLINE ULONG ma_IDirectSoundCapture_AddRef (ma_IDirectSoundCapture* pThis) { return pThis->lpVtbl->AddRef(pThis); } -static MA_INLINE ULONG ma_IDirectSoundCapture_Release (ma_IDirectSoundCapture* pThis) { return pThis->lpVtbl->Release(pThis); } -static MA_INLINE HRESULT ma_IDirectSoundCapture_CreateCaptureBuffer(ma_IDirectSoundCapture* pThis, const MA_DSCBUFFERDESC* pDSCBufferDesc, ma_IDirectSoundCaptureBuffer** ppDSCBuffer, void* pUnkOuter) { return pThis->lpVtbl->CreateCaptureBuffer(pThis, pDSCBufferDesc, ppDSCBuffer, pUnkOuter); } -static MA_INLINE HRESULT ma_IDirectSoundCapture_GetCaps (ma_IDirectSoundCapture* pThis, MA_DSCCAPS* pDSCCaps) { return pThis->lpVtbl->GetCaps(pThis, pDSCCaps); } -static MA_INLINE HRESULT ma_IDirectSoundCapture_Initialize (ma_IDirectSoundCapture* pThis, const GUID* pGuidDevice) { return pThis->lpVtbl->Initialize(pThis, pGuidDevice); } - - -/* IDirectSoundCaptureBuffer */ -typedef struct -{ - /* IUnknown */ - HRESULT (STDMETHODCALLTYPE * QueryInterface)(ma_IDirectSoundCaptureBuffer* pThis, const IID* const riid, void** ppObject); - ULONG (STDMETHODCALLTYPE * AddRef) (ma_IDirectSoundCaptureBuffer* pThis); - ULONG (STDMETHODCALLTYPE * Release) (ma_IDirectSoundCaptureBuffer* pThis); - - /* IDirectSoundCaptureBuffer */ - HRESULT (STDMETHODCALLTYPE * GetCaps) (ma_IDirectSoundCaptureBuffer* pThis, MA_DSCBCAPS* pDSCBCaps); - HRESULT (STDMETHODCALLTYPE * GetCurrentPosition)(ma_IDirectSoundCaptureBuffer* pThis, DWORD* pCapturePosition, DWORD* pReadPosition); - HRESULT (STDMETHODCALLTYPE * GetFormat) (ma_IDirectSoundCaptureBuffer* pThis, MA_WAVEFORMATEX* pFormat, DWORD dwSizeAllocated, DWORD* pSizeWritten); - HRESULT (STDMETHODCALLTYPE * GetStatus) (ma_IDirectSoundCaptureBuffer* pThis, DWORD* pStatus); - HRESULT (STDMETHODCALLTYPE * Initialize) (ma_IDirectSoundCaptureBuffer* pThis, ma_IDirectSoundCapture* pDirectSoundCapture, const MA_DSCBUFFERDESC* pDSCBufferDesc); - HRESULT (STDMETHODCALLTYPE * Lock) (ma_IDirectSoundCaptureBuffer* pThis, DWORD dwOffset, DWORD dwBytes, void** ppAudioPtr1, DWORD* pAudioBytes1, void** ppAudioPtr2, DWORD* pAudioBytes2, DWORD dwFlags); - HRESULT (STDMETHODCALLTYPE * Start) (ma_IDirectSoundCaptureBuffer* pThis, DWORD dwFlags); - HRESULT (STDMETHODCALLTYPE * Stop) (ma_IDirectSoundCaptureBuffer* pThis); - HRESULT (STDMETHODCALLTYPE * Unlock) (ma_IDirectSoundCaptureBuffer* pThis, void* pAudioPtr1, DWORD dwAudioBytes1, void* pAudioPtr2, DWORD dwAudioBytes2); -} ma_IDirectSoundCaptureBufferVtbl; -struct ma_IDirectSoundCaptureBuffer -{ - ma_IDirectSoundCaptureBufferVtbl* lpVtbl; -}; -static MA_INLINE HRESULT ma_IDirectSoundCaptureBuffer_QueryInterface(ma_IDirectSoundCaptureBuffer* pThis, const IID* const riid, void** ppObject) { return pThis->lpVtbl->QueryInterface(pThis, riid, ppObject); } -static MA_INLINE ULONG ma_IDirectSoundCaptureBuffer_AddRef(ma_IDirectSoundCaptureBuffer* pThis) { return pThis->lpVtbl->AddRef(pThis); } -static MA_INLINE ULONG ma_IDirectSoundCaptureBuffer_Release(ma_IDirectSoundCaptureBuffer* pThis) { return pThis->lpVtbl->Release(pThis); } -static MA_INLINE HRESULT ma_IDirectSoundCaptureBuffer_GetCaps(ma_IDirectSoundCaptureBuffer* pThis, MA_DSCBCAPS* pDSCBCaps) { return pThis->lpVtbl->GetCaps(pThis, pDSCBCaps); } -static MA_INLINE HRESULT ma_IDirectSoundCaptureBuffer_GetCurrentPosition(ma_IDirectSoundCaptureBuffer* pThis, DWORD* pCapturePosition, DWORD* pReadPosition) { return pThis->lpVtbl->GetCurrentPosition(pThis, pCapturePosition, pReadPosition); } -static MA_INLINE HRESULT ma_IDirectSoundCaptureBuffer_GetFormat(ma_IDirectSoundCaptureBuffer* pThis, MA_WAVEFORMATEX* pFormat, DWORD dwSizeAllocated, DWORD* pSizeWritten) { return pThis->lpVtbl->GetFormat(pThis, pFormat, dwSizeAllocated, pSizeWritten); } -static MA_INLINE HRESULT ma_IDirectSoundCaptureBuffer_GetStatus(ma_IDirectSoundCaptureBuffer* pThis, DWORD* pStatus) { return pThis->lpVtbl->GetStatus(pThis, pStatus); } -static MA_INLINE HRESULT ma_IDirectSoundCaptureBuffer_Initialize(ma_IDirectSoundCaptureBuffer* pThis, ma_IDirectSoundCapture* pDirectSoundCapture, const MA_DSCBUFFERDESC* pDSCBufferDesc) { return pThis->lpVtbl->Initialize(pThis, pDirectSoundCapture, pDSCBufferDesc); } -static MA_INLINE HRESULT ma_IDirectSoundCaptureBuffer_Lock(ma_IDirectSoundCaptureBuffer* pThis, DWORD dwOffset, DWORD dwBytes, void** ppAudioPtr1, DWORD* pAudioBytes1, void** ppAudioPtr2, DWORD* pAudioBytes2, DWORD dwFlags) { return pThis->lpVtbl->Lock(pThis, dwOffset, dwBytes, ppAudioPtr1, pAudioBytes1, ppAudioPtr2, pAudioBytes2, dwFlags); } -static MA_INLINE HRESULT ma_IDirectSoundCaptureBuffer_Start(ma_IDirectSoundCaptureBuffer* pThis, DWORD dwFlags) { return pThis->lpVtbl->Start(pThis, dwFlags); } -static MA_INLINE HRESULT ma_IDirectSoundCaptureBuffer_Stop(ma_IDirectSoundCaptureBuffer* pThis) { return pThis->lpVtbl->Stop(pThis); } -static MA_INLINE HRESULT ma_IDirectSoundCaptureBuffer_Unlock(ma_IDirectSoundCaptureBuffer* pThis, void* pAudioPtr1, DWORD dwAudioBytes1, void* pAudioPtr2, DWORD dwAudioBytes2) { return pThis->lpVtbl->Unlock(pThis, pAudioPtr1, dwAudioBytes1, pAudioPtr2, dwAudioBytes2); } - - -/* IDirectSoundNotify */ -typedef struct -{ - /* IUnknown */ - HRESULT (STDMETHODCALLTYPE * QueryInterface)(ma_IDirectSoundNotify* pThis, const IID* const riid, void** ppObject); - ULONG (STDMETHODCALLTYPE * AddRef) (ma_IDirectSoundNotify* pThis); - ULONG (STDMETHODCALLTYPE * Release) (ma_IDirectSoundNotify* pThis); - - /* IDirectSoundNotify */ - HRESULT (STDMETHODCALLTYPE * SetNotificationPositions)(ma_IDirectSoundNotify* pThis, DWORD dwPositionNotifies, const MA_DSBPOSITIONNOTIFY* pPositionNotifies); -} ma_IDirectSoundNotifyVtbl; -struct ma_IDirectSoundNotify -{ - ma_IDirectSoundNotifyVtbl* lpVtbl; -}; -static MA_INLINE HRESULT ma_IDirectSoundNotify_QueryInterface(ma_IDirectSoundNotify* pThis, const IID* const riid, void** ppObject) { return pThis->lpVtbl->QueryInterface(pThis, riid, ppObject); } -static MA_INLINE ULONG ma_IDirectSoundNotify_AddRef(ma_IDirectSoundNotify* pThis) { return pThis->lpVtbl->AddRef(pThis); } -static MA_INLINE ULONG ma_IDirectSoundNotify_Release(ma_IDirectSoundNotify* pThis) { return pThis->lpVtbl->Release(pThis); } -static MA_INLINE HRESULT ma_IDirectSoundNotify_SetNotificationPositions(ma_IDirectSoundNotify* pThis, DWORD dwPositionNotifies, const MA_DSBPOSITIONNOTIFY* pPositionNotifies) { return pThis->lpVtbl->SetNotificationPositions(pThis, dwPositionNotifies, pPositionNotifies); } - - -typedef BOOL (CALLBACK * ma_DSEnumCallbackAProc) (GUID* pDeviceGUID, const char* pDeviceDescription, const char* pModule, void* pContext); -typedef HRESULT (WINAPI * ma_DirectSoundCreateProc) (const GUID* pcGuidDevice, ma_IDirectSound** ppDS8, ma_IUnknown* pUnkOuter); -typedef HRESULT (WINAPI * ma_DirectSoundEnumerateAProc) (ma_DSEnumCallbackAProc pDSEnumCallback, void* pContext); -typedef HRESULT (WINAPI * ma_DirectSoundCaptureCreateProc) (const GUID* pcGuidDevice, ma_IDirectSoundCapture** ppDSC8, ma_IUnknown* pUnkOuter); -typedef HRESULT (WINAPI * ma_DirectSoundCaptureEnumerateAProc)(ma_DSEnumCallbackAProc pDSEnumCallback, void* pContext); - -static ma_uint32 ma_get_best_sample_rate_within_range(ma_uint32 sampleRateMin, ma_uint32 sampleRateMax) -{ - /* Normalize the range in case we were given something stupid. */ - if (sampleRateMin < (ma_uint32)ma_standard_sample_rate_min) { - sampleRateMin = (ma_uint32)ma_standard_sample_rate_min; - } - if (sampleRateMax > (ma_uint32)ma_standard_sample_rate_max) { - sampleRateMax = (ma_uint32)ma_standard_sample_rate_max; - } - if (sampleRateMin > sampleRateMax) { - sampleRateMin = sampleRateMax; - } - - if (sampleRateMin == sampleRateMax) { - return sampleRateMax; - } else { - size_t iStandardRate; - for (iStandardRate = 0; iStandardRate < ma_countof(g_maStandardSampleRatePriorities); ++iStandardRate) { - ma_uint32 standardRate = g_maStandardSampleRatePriorities[iStandardRate]; - if (standardRate >= sampleRateMin && standardRate <= sampleRateMax) { - return standardRate; - } - } - } - - /* Should never get here. */ - MA_ASSERT(MA_FALSE); - return 0; -} - -/* -Retrieves the channel count and channel map for the given speaker configuration. If the speaker configuration is unknown, -the channel count and channel map will be left unmodified. -*/ -static void ma_get_channels_from_speaker_config__dsound(DWORD speakerConfig, WORD* pChannelsOut, DWORD* pChannelMapOut) -{ - WORD channels; - DWORD channelMap; - - channels = 0; - if (pChannelsOut != NULL) { - channels = *pChannelsOut; - } - - channelMap = 0; - if (pChannelMapOut != NULL) { - channelMap = *pChannelMapOut; - } - - /* - The speaker configuration is a combination of speaker config and speaker geometry. The lower 8 bits is what we care about. The upper - 16 bits is for the geometry. - */ - switch ((BYTE)(speakerConfig)) { - case 1 /*DSSPEAKER_HEADPHONE*/: channels = 2; channelMap = SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT; break; - case 2 /*DSSPEAKER_MONO*/: channels = 1; channelMap = SPEAKER_FRONT_CENTER; break; - case 3 /*DSSPEAKER_QUAD*/: channels = 4; channelMap = SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT | SPEAKER_BACK_LEFT | SPEAKER_BACK_RIGHT; break; - case 4 /*DSSPEAKER_STEREO*/: channels = 2; channelMap = SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT; break; - case 5 /*DSSPEAKER_SURROUND*/: channels = 4; channelMap = SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT | SPEAKER_FRONT_CENTER | SPEAKER_BACK_CENTER; break; - case 6 /*DSSPEAKER_5POINT1_BACK*/ /*DSSPEAKER_5POINT1*/: channels = 6; channelMap = SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT | SPEAKER_FRONT_CENTER | SPEAKER_LOW_FREQUENCY | SPEAKER_BACK_LEFT | SPEAKER_BACK_RIGHT; break; - case 7 /*DSSPEAKER_7POINT1_WIDE*/ /*DSSPEAKER_7POINT1*/: channels = 8; channelMap = SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT | SPEAKER_FRONT_CENTER | SPEAKER_LOW_FREQUENCY | SPEAKER_BACK_LEFT | SPEAKER_BACK_RIGHT | SPEAKER_FRONT_LEFT_OF_CENTER | SPEAKER_FRONT_RIGHT_OF_CENTER; break; - case 8 /*DSSPEAKER_7POINT1_SURROUND*/: channels = 8; channelMap = SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT | SPEAKER_FRONT_CENTER | SPEAKER_LOW_FREQUENCY | SPEAKER_BACK_LEFT | SPEAKER_BACK_RIGHT | SPEAKER_SIDE_LEFT | SPEAKER_SIDE_RIGHT; break; - case 9 /*DSSPEAKER_5POINT1_SURROUND*/: channels = 6; channelMap = SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT | SPEAKER_FRONT_CENTER | SPEAKER_LOW_FREQUENCY | SPEAKER_SIDE_LEFT | SPEAKER_SIDE_RIGHT; break; - default: break; - } - - if (pChannelsOut != NULL) { - *pChannelsOut = channels; - } - - if (pChannelMapOut != NULL) { - *pChannelMapOut = channelMap; - } -} - - -static ma_result ma_context_create_IDirectSound__dsound(ma_context* pContext, ma_share_mode shareMode, const ma_device_id* pDeviceID, ma_IDirectSound** ppDirectSound) -{ - ma_IDirectSound* pDirectSound; - HWND hWnd; - HRESULT hr; - - MA_ASSERT(pContext != NULL); - MA_ASSERT(ppDirectSound != NULL); - - *ppDirectSound = NULL; - pDirectSound = NULL; - - if (FAILED(((ma_DirectSoundCreateProc)pContext->dsound.DirectSoundCreate)((pDeviceID == NULL) ? NULL : (const GUID*)pDeviceID->dsound, &pDirectSound, NULL))) { - ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[DirectSound] DirectSoundCreate() failed for playback device."); - return MA_FAILED_TO_OPEN_BACKEND_DEVICE; - } - - /* The cooperative level must be set before doing anything else. */ - hWnd = (HWND)pContext->dsound.hWnd; - if (hWnd == 0) { - hWnd = ((MA_PFN_GetForegroundWindow)pContext->win32.GetForegroundWindow)(); - if (hWnd == 0) { - hWnd = ((MA_PFN_GetDesktopWindow)pContext->win32.GetDesktopWindow)(); - } - } - - hr = ma_IDirectSound_SetCooperativeLevel(pDirectSound, hWnd, (shareMode == ma_share_mode_exclusive) ? MA_DSSCL_EXCLUSIVE : MA_DSSCL_PRIORITY); - if (FAILED(hr)) { - ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[DirectSound] IDirectSound_SetCooperateiveLevel() failed for playback device."); - return ma_result_from_HRESULT(hr); - } - - *ppDirectSound = pDirectSound; - return MA_SUCCESS; -} - -static ma_result ma_context_create_IDirectSoundCapture__dsound(ma_context* pContext, ma_share_mode shareMode, const ma_device_id* pDeviceID, ma_IDirectSoundCapture** ppDirectSoundCapture) -{ - ma_IDirectSoundCapture* pDirectSoundCapture; - HRESULT hr; - - MA_ASSERT(pContext != NULL); - MA_ASSERT(ppDirectSoundCapture != NULL); - - /* DirectSound does not support exclusive mode for capture. */ - if (shareMode == ma_share_mode_exclusive) { - return MA_SHARE_MODE_NOT_SUPPORTED; - } - - *ppDirectSoundCapture = NULL; - pDirectSoundCapture = NULL; - - hr = ((ma_DirectSoundCaptureCreateProc)pContext->dsound.DirectSoundCaptureCreate)((pDeviceID == NULL) ? NULL : (const GUID*)pDeviceID->dsound, &pDirectSoundCapture, NULL); - if (FAILED(hr)) { - ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[DirectSound] DirectSoundCaptureCreate() failed for capture device."); - return ma_result_from_HRESULT(hr); - } - - *ppDirectSoundCapture = pDirectSoundCapture; - return MA_SUCCESS; -} - -static ma_result ma_context_get_format_info_for_IDirectSoundCapture__dsound(ma_context* pContext, ma_IDirectSoundCapture* pDirectSoundCapture, WORD* pChannels, WORD* pBitsPerSample, DWORD* pSampleRate) -{ - HRESULT hr; - MA_DSCCAPS caps; - WORD bitsPerSample; - DWORD sampleRate; - - MA_ASSERT(pContext != NULL); - MA_ASSERT(pDirectSoundCapture != NULL); - - if (pChannels) { - *pChannels = 0; - } - if (pBitsPerSample) { - *pBitsPerSample = 0; - } - if (pSampleRate) { - *pSampleRate = 0; - } - - MA_ZERO_OBJECT(&caps); - caps.dwSize = sizeof(caps); - hr = ma_IDirectSoundCapture_GetCaps(pDirectSoundCapture, &caps); - if (FAILED(hr)) { - ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[DirectSound] IDirectSoundCapture_GetCaps() failed for capture device."); - return ma_result_from_HRESULT(hr); - } - - if (pChannels) { - *pChannels = (WORD)caps.dwChannels; - } - - /* The device can support multiple formats. We just go through the different formats in order of priority and pick the first one. This the same type of system as the WinMM backend. */ - bitsPerSample = 16; - sampleRate = 48000; - - if (caps.dwChannels == 1) { - if ((caps.dwFormats & WAVE_FORMAT_48M16) != 0) { - sampleRate = 48000; - } else if ((caps.dwFormats & WAVE_FORMAT_44M16) != 0) { - sampleRate = 44100; - } else if ((caps.dwFormats & WAVE_FORMAT_2M16) != 0) { - sampleRate = 22050; - } else if ((caps.dwFormats & WAVE_FORMAT_1M16) != 0) { - sampleRate = 11025; - } else if ((caps.dwFormats & WAVE_FORMAT_96M16) != 0) { - sampleRate = 96000; - } else { - bitsPerSample = 8; - if ((caps.dwFormats & WAVE_FORMAT_48M08) != 0) { - sampleRate = 48000; - } else if ((caps.dwFormats & WAVE_FORMAT_44M08) != 0) { - sampleRate = 44100; - } else if ((caps.dwFormats & WAVE_FORMAT_2M08) != 0) { - sampleRate = 22050; - } else if ((caps.dwFormats & WAVE_FORMAT_1M08) != 0) { - sampleRate = 11025; - } else if ((caps.dwFormats & WAVE_FORMAT_96M08) != 0) { - sampleRate = 96000; - } else { - bitsPerSample = 16; /* Didn't find it. Just fall back to 16-bit. */ - } - } - } else if (caps.dwChannels == 2) { - if ((caps.dwFormats & WAVE_FORMAT_48S16) != 0) { - sampleRate = 48000; - } else if ((caps.dwFormats & WAVE_FORMAT_44S16) != 0) { - sampleRate = 44100; - } else if ((caps.dwFormats & WAVE_FORMAT_2S16) != 0) { - sampleRate = 22050; - } else if ((caps.dwFormats & WAVE_FORMAT_1S16) != 0) { - sampleRate = 11025; - } else if ((caps.dwFormats & WAVE_FORMAT_96S16) != 0) { - sampleRate = 96000; - } else { - bitsPerSample = 8; - if ((caps.dwFormats & WAVE_FORMAT_48S08) != 0) { - sampleRate = 48000; - } else if ((caps.dwFormats & WAVE_FORMAT_44S08) != 0) { - sampleRate = 44100; - } else if ((caps.dwFormats & WAVE_FORMAT_2S08) != 0) { - sampleRate = 22050; - } else if ((caps.dwFormats & WAVE_FORMAT_1S08) != 0) { - sampleRate = 11025; - } else if ((caps.dwFormats & WAVE_FORMAT_96S08) != 0) { - sampleRate = 96000; - } else { - bitsPerSample = 16; /* Didn't find it. Just fall back to 16-bit. */ - } - } - } - - if (pBitsPerSample) { - *pBitsPerSample = bitsPerSample; - } - if (pSampleRate) { - *pSampleRate = sampleRate; - } - - return MA_SUCCESS; -} - - -typedef struct -{ - ma_context* pContext; - ma_device_type deviceType; - ma_enum_devices_callback_proc callback; - void* pUserData; - ma_bool32 terminated; -} ma_context_enumerate_devices_callback_data__dsound; - -static BOOL CALLBACK ma_context_enumerate_devices_callback__dsound(GUID* lpGuid, const char* lpcstrDescription, const char* lpcstrModule, void* lpContext) -{ - ma_context_enumerate_devices_callback_data__dsound* pData = (ma_context_enumerate_devices_callback_data__dsound*)lpContext; - ma_device_info deviceInfo; - - (void)lpcstrModule; - - MA_ZERO_OBJECT(&deviceInfo); - - /* ID. */ - if (lpGuid != NULL) { - MA_COPY_MEMORY(deviceInfo.id.dsound, lpGuid, 16); - } else { - MA_ZERO_MEMORY(deviceInfo.id.dsound, 16); - deviceInfo.isDefault = MA_TRUE; - } - - /* Name / Description */ - ma_strncpy_s(deviceInfo.name, sizeof(deviceInfo.name), lpcstrDescription, (size_t)-1); - - - /* Call the callback function, but make sure we stop enumerating if the callee requested so. */ - MA_ASSERT(pData != NULL); - pData->terminated = (pData->callback(pData->pContext, pData->deviceType, &deviceInfo, pData->pUserData) == MA_FALSE); - if (pData->terminated) { - return FALSE; /* Stop enumeration. */ - } else { - return TRUE; /* Continue enumeration. */ - } -} - -static ma_result ma_context_enumerate_devices__dsound(ma_context* pContext, ma_enum_devices_callback_proc callback, void* pUserData) -{ - ma_context_enumerate_devices_callback_data__dsound data; - - MA_ASSERT(pContext != NULL); - MA_ASSERT(callback != NULL); - - data.pContext = pContext; - data.callback = callback; - data.pUserData = pUserData; - data.terminated = MA_FALSE; - - /* Playback. */ - if (!data.terminated) { - data.deviceType = ma_device_type_playback; - ((ma_DirectSoundEnumerateAProc)pContext->dsound.DirectSoundEnumerateA)(ma_context_enumerate_devices_callback__dsound, &data); - } - - /* Capture. */ - if (!data.terminated) { - data.deviceType = ma_device_type_capture; - ((ma_DirectSoundCaptureEnumerateAProc)pContext->dsound.DirectSoundCaptureEnumerateA)(ma_context_enumerate_devices_callback__dsound, &data); - } - - return MA_SUCCESS; -} - - -typedef struct -{ - const ma_device_id* pDeviceID; - ma_device_info* pDeviceInfo; - ma_bool32 found; -} ma_context_get_device_info_callback_data__dsound; - -static BOOL CALLBACK ma_context_get_device_info_callback__dsound(GUID* lpGuid, const char* lpcstrDescription, const char* lpcstrModule, void* lpContext) -{ - ma_context_get_device_info_callback_data__dsound* pData = (ma_context_get_device_info_callback_data__dsound*)lpContext; - MA_ASSERT(pData != NULL); - - if ((pData->pDeviceID == NULL || ma_is_guid_null(pData->pDeviceID->dsound)) && (lpGuid == NULL || ma_is_guid_null(lpGuid))) { - /* Default device. */ - ma_strncpy_s(pData->pDeviceInfo->name, sizeof(pData->pDeviceInfo->name), lpcstrDescription, (size_t)-1); - pData->pDeviceInfo->isDefault = MA_TRUE; - pData->found = MA_TRUE; - return FALSE; /* Stop enumeration. */ - } else { - /* Not the default device. */ - if (lpGuid != NULL && pData->pDeviceID != NULL) { - if (memcmp(pData->pDeviceID->dsound, lpGuid, sizeof(pData->pDeviceID->dsound)) == 0) { - ma_strncpy_s(pData->pDeviceInfo->name, sizeof(pData->pDeviceInfo->name), lpcstrDescription, (size_t)-1); - pData->found = MA_TRUE; - return FALSE; /* Stop enumeration. */ - } - } - } - - (void)lpcstrModule; - return TRUE; -} - -static ma_result ma_context_get_device_info__dsound(ma_context* pContext, ma_device_type deviceType, const ma_device_id* pDeviceID, ma_device_info* pDeviceInfo) -{ - ma_result result; - HRESULT hr; - - if (pDeviceID != NULL) { - ma_context_get_device_info_callback_data__dsound data; - - /* ID. */ - MA_COPY_MEMORY(pDeviceInfo->id.dsound, pDeviceID->dsound, 16); - - /* Name / Description. This is retrieved by enumerating over each device until we find that one that matches the input ID. */ - data.pDeviceID = pDeviceID; - data.pDeviceInfo = pDeviceInfo; - data.found = MA_FALSE; - if (deviceType == ma_device_type_playback) { - ((ma_DirectSoundEnumerateAProc)pContext->dsound.DirectSoundEnumerateA)(ma_context_get_device_info_callback__dsound, &data); - } else { - ((ma_DirectSoundCaptureEnumerateAProc)pContext->dsound.DirectSoundCaptureEnumerateA)(ma_context_get_device_info_callback__dsound, &data); - } - - if (!data.found) { - return MA_NO_DEVICE; - } - } else { - /* I don't think there's a way to get the name of the default device with DirectSound. In this case we just need to use defaults. */ - - /* ID */ - MA_ZERO_MEMORY(pDeviceInfo->id.dsound, 16); - - /* Name / Description */ - if (deviceType == ma_device_type_playback) { - ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), MA_DEFAULT_PLAYBACK_DEVICE_NAME, (size_t)-1); - } else { - ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), MA_DEFAULT_CAPTURE_DEVICE_NAME, (size_t)-1); - } - - pDeviceInfo->isDefault = MA_TRUE; - } - - /* Retrieving detailed information is slightly different depending on the device type. */ - if (deviceType == ma_device_type_playback) { - /* Playback. */ - ma_IDirectSound* pDirectSound; - MA_DSCAPS caps; - WORD channels; - - result = ma_context_create_IDirectSound__dsound(pContext, ma_share_mode_shared, pDeviceID, &pDirectSound); - if (result != MA_SUCCESS) { - return result; - } - - MA_ZERO_OBJECT(&caps); - caps.dwSize = sizeof(caps); - hr = ma_IDirectSound_GetCaps(pDirectSound, &caps); - if (FAILED(hr)) { - ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[DirectSound] IDirectSound_GetCaps() failed for playback device."); - return ma_result_from_HRESULT(hr); - } - - - /* Channels. Only a single channel count is reported for DirectSound. */ - if ((caps.dwFlags & MA_DSCAPS_PRIMARYSTEREO) != 0) { - /* It supports at least stereo, but could support more. */ - DWORD speakerConfig; - - channels = 2; - - /* Look at the speaker configuration to get a better idea on the channel count. */ - hr = ma_IDirectSound_GetSpeakerConfig(pDirectSound, &speakerConfig); - if (SUCCEEDED(hr)) { - ma_get_channels_from_speaker_config__dsound(speakerConfig, &channels, NULL); - } - } else { - /* It does not support stereo, which means we are stuck with mono. */ - channels = 1; - } - - - /* - In DirectSound, our native formats are centered around sample rates. All formats are supported, and we're only reporting a single channel - count. However, DirectSound can report a range of supported sample rates. We're only going to include standard rates known by miniaudio - in order to keep the size of this within reason. - */ - if ((caps.dwFlags & MA_DSCAPS_CONTINUOUSRATE) != 0) { - /* Multiple sample rates are supported. We'll report in order of our preferred sample rates. */ - size_t iStandardSampleRate; - for (iStandardSampleRate = 0; iStandardSampleRate < ma_countof(g_maStandardSampleRatePriorities); iStandardSampleRate += 1) { - ma_uint32 sampleRate = g_maStandardSampleRatePriorities[iStandardSampleRate]; - if (sampleRate >= caps.dwMinSecondarySampleRate && sampleRate <= caps.dwMaxSecondarySampleRate) { - pDeviceInfo->nativeDataFormats[pDeviceInfo->nativeDataFormatCount].format = ma_format_unknown; - pDeviceInfo->nativeDataFormats[pDeviceInfo->nativeDataFormatCount].channels = channels; - pDeviceInfo->nativeDataFormats[pDeviceInfo->nativeDataFormatCount].sampleRate = sampleRate; - pDeviceInfo->nativeDataFormats[pDeviceInfo->nativeDataFormatCount].flags = 0; - pDeviceInfo->nativeDataFormatCount += 1; - } - } - } else { - /* Only a single sample rate is supported. */ - pDeviceInfo->nativeDataFormats[pDeviceInfo->nativeDataFormatCount].format = ma_format_unknown; - pDeviceInfo->nativeDataFormats[pDeviceInfo->nativeDataFormatCount].channels = channels; - pDeviceInfo->nativeDataFormats[pDeviceInfo->nativeDataFormatCount].sampleRate = caps.dwMaxSecondarySampleRate; - pDeviceInfo->nativeDataFormats[pDeviceInfo->nativeDataFormatCount].flags = 0; - pDeviceInfo->nativeDataFormatCount += 1; - } - - ma_IDirectSound_Release(pDirectSound); - } else { - /* - Capture. This is a little different to playback due to the say the supported formats are reported. Technically capture - devices can support a number of different formats, but for simplicity and consistency with ma_device_init() I'm just - reporting the best format. - */ - ma_IDirectSoundCapture* pDirectSoundCapture; - WORD channels; - WORD bitsPerSample; - DWORD sampleRate; - - result = ma_context_create_IDirectSoundCapture__dsound(pContext, ma_share_mode_shared, pDeviceID, &pDirectSoundCapture); - if (result != MA_SUCCESS) { - return result; - } - - result = ma_context_get_format_info_for_IDirectSoundCapture__dsound(pContext, pDirectSoundCapture, &channels, &bitsPerSample, &sampleRate); - if (result != MA_SUCCESS) { - ma_IDirectSoundCapture_Release(pDirectSoundCapture); - return result; - } - - ma_IDirectSoundCapture_Release(pDirectSoundCapture); - - /* The format is always an integer format and is based on the bits per sample. */ - if (bitsPerSample == 8) { - pDeviceInfo->nativeDataFormats[0].format = ma_format_u8; - } else if (bitsPerSample == 16) { - pDeviceInfo->nativeDataFormats[0].format = ma_format_s16; - } else if (bitsPerSample == 24) { - pDeviceInfo->nativeDataFormats[0].format = ma_format_s24; - } else if (bitsPerSample == 32) { - pDeviceInfo->nativeDataFormats[0].format = ma_format_s32; - } else { - return MA_FORMAT_NOT_SUPPORTED; - } - - pDeviceInfo->nativeDataFormats[0].channels = channels; - pDeviceInfo->nativeDataFormats[0].sampleRate = sampleRate; - pDeviceInfo->nativeDataFormats[0].flags = 0; - pDeviceInfo->nativeDataFormatCount = 1; - } - - return MA_SUCCESS; -} - - - -static ma_result ma_device_uninit__dsound(ma_device* pDevice) -{ - MA_ASSERT(pDevice != NULL); - - if (pDevice->dsound.pCaptureBuffer != NULL) { - ma_IDirectSoundCaptureBuffer_Release((ma_IDirectSoundCaptureBuffer*)pDevice->dsound.pCaptureBuffer); - } - if (pDevice->dsound.pCapture != NULL) { - ma_IDirectSoundCapture_Release((ma_IDirectSoundCapture*)pDevice->dsound.pCapture); - } - - if (pDevice->dsound.pPlaybackBuffer != NULL) { - ma_IDirectSoundBuffer_Release((ma_IDirectSoundBuffer*)pDevice->dsound.pPlaybackBuffer); - } - if (pDevice->dsound.pPlaybackPrimaryBuffer != NULL) { - ma_IDirectSoundBuffer_Release((ma_IDirectSoundBuffer*)pDevice->dsound.pPlaybackPrimaryBuffer); - } - if (pDevice->dsound.pPlayback != NULL) { - ma_IDirectSound_Release((ma_IDirectSound*)pDevice->dsound.pPlayback); - } - - return MA_SUCCESS; -} - -static ma_result ma_config_to_WAVEFORMATEXTENSIBLE(ma_format format, ma_uint32 channels, ma_uint32 sampleRate, const ma_channel* pChannelMap, MA_WAVEFORMATEXTENSIBLE* pWF) -{ - GUID subformat; - - if (format == ma_format_unknown) { - format = MA_DEFAULT_FORMAT; - } - - if (channels == 0) { - channels = MA_DEFAULT_CHANNELS; - } - - if (sampleRate == 0) { - sampleRate = MA_DEFAULT_SAMPLE_RATE; - } - - switch (format) - { - case ma_format_u8: - case ma_format_s16: - case ma_format_s24: - /*case ma_format_s24_32:*/ - case ma_format_s32: - { - subformat = MA_GUID_KSDATAFORMAT_SUBTYPE_PCM; - } break; - - case ma_format_f32: - { - subformat = MA_GUID_KSDATAFORMAT_SUBTYPE_IEEE_FLOAT; - } break; - - default: - return MA_FORMAT_NOT_SUPPORTED; - } - - MA_ZERO_OBJECT(pWF); - pWF->cbSize = sizeof(*pWF); - pWF->wFormatTag = WAVE_FORMAT_EXTENSIBLE; - pWF->nChannels = (WORD)channels; - pWF->nSamplesPerSec = (DWORD)sampleRate; - pWF->wBitsPerSample = (WORD)(ma_get_bytes_per_sample(format)*8); - pWF->nBlockAlign = (WORD)(pWF->nChannels * pWF->wBitsPerSample / 8); - pWF->nAvgBytesPerSec = pWF->nBlockAlign * pWF->nSamplesPerSec; - pWF->Samples.wValidBitsPerSample = pWF->wBitsPerSample; - pWF->dwChannelMask = ma_channel_map_to_channel_mask__win32(pChannelMap, channels); - pWF->SubFormat = subformat; - - return MA_SUCCESS; -} - -static ma_uint32 ma_calculate_period_size_in_frames_from_descriptor__dsound(const ma_device_descriptor* pDescriptor, ma_uint32 nativeSampleRate, ma_performance_profile performanceProfile) -{ - /* - DirectSound has a minimum period size of 20ms. In practice, this doesn't seem to be enough for - reliable glitch-free processing so going to use 30ms instead. - */ - ma_uint32 minPeriodSizeInFrames = ma_calculate_buffer_size_in_frames_from_milliseconds(30, nativeSampleRate); - ma_uint32 periodSizeInFrames; - - periodSizeInFrames = ma_calculate_buffer_size_in_frames_from_descriptor(pDescriptor, nativeSampleRate, performanceProfile); - if (periodSizeInFrames < minPeriodSizeInFrames) { - periodSizeInFrames = minPeriodSizeInFrames; - } - - return periodSizeInFrames; -} - -static ma_result ma_device_init__dsound(ma_device* pDevice, const ma_device_config* pConfig, ma_device_descriptor* pDescriptorPlayback, ma_device_descriptor* pDescriptorCapture) -{ - ma_result result; - HRESULT hr; - - MA_ASSERT(pDevice != NULL); - - MA_ZERO_OBJECT(&pDevice->dsound); - - if (pConfig->deviceType == ma_device_type_loopback) { - return MA_DEVICE_TYPE_NOT_SUPPORTED; - } - - /* - Unfortunately DirectSound uses different APIs and data structures for playback and capture devices. We need to initialize - the capture device first because we'll want to match its buffer size and period count on the playback side if we're using - full-duplex mode. - */ - if (pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) { - MA_WAVEFORMATEXTENSIBLE wf; - MA_DSCBUFFERDESC descDS; - ma_uint32 periodSizeInFrames; - ma_uint32 periodCount; - char rawdata[1024]; /* <-- Ugly hack to avoid a malloc() due to a crappy DirectSound API. */ - MA_WAVEFORMATEXTENSIBLE* pActualFormat; - - result = ma_config_to_WAVEFORMATEXTENSIBLE(pDescriptorCapture->format, pDescriptorCapture->channels, pDescriptorCapture->sampleRate, pDescriptorCapture->channelMap, &wf); - if (result != MA_SUCCESS) { - return result; - } - - result = ma_context_create_IDirectSoundCapture__dsound(pDevice->pContext, pDescriptorCapture->shareMode, pDescriptorCapture->pDeviceID, (ma_IDirectSoundCapture**)&pDevice->dsound.pCapture); - if (result != MA_SUCCESS) { - ma_device_uninit__dsound(pDevice); - return result; - } - - result = ma_context_get_format_info_for_IDirectSoundCapture__dsound(pDevice->pContext, (ma_IDirectSoundCapture*)pDevice->dsound.pCapture, &wf.nChannels, &wf.wBitsPerSample, &wf.nSamplesPerSec); - if (result != MA_SUCCESS) { - ma_device_uninit__dsound(pDevice); - return result; - } - - wf.nBlockAlign = (WORD)(wf.nChannels * wf.wBitsPerSample / 8); - wf.nAvgBytesPerSec = wf.nBlockAlign * wf.nSamplesPerSec; - wf.Samples.wValidBitsPerSample = wf.wBitsPerSample; - wf.SubFormat = MA_GUID_KSDATAFORMAT_SUBTYPE_PCM; - - /* The size of the buffer must be a clean multiple of the period count. */ - periodSizeInFrames = ma_calculate_period_size_in_frames_from_descriptor__dsound(pDescriptorCapture, wf.nSamplesPerSec, pConfig->performanceProfile); - periodCount = (pDescriptorCapture->periodCount > 0) ? pDescriptorCapture->periodCount : MA_DEFAULT_PERIODS; - - MA_ZERO_OBJECT(&descDS); - descDS.dwSize = sizeof(descDS); - descDS.dwFlags = 0; - descDS.dwBufferBytes = periodSizeInFrames * periodCount * wf.nBlockAlign; - descDS.lpwfxFormat = (MA_WAVEFORMATEX*)&wf; - hr = ma_IDirectSoundCapture_CreateCaptureBuffer((ma_IDirectSoundCapture*)pDevice->dsound.pCapture, &descDS, (ma_IDirectSoundCaptureBuffer**)&pDevice->dsound.pCaptureBuffer, NULL); - if (FAILED(hr)) { - ma_device_uninit__dsound(pDevice); - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[DirectSound] IDirectSoundCapture_CreateCaptureBuffer() failed for capture device."); - return ma_result_from_HRESULT(hr); - } - - /* Get the _actual_ properties of the buffer. */ - pActualFormat = (MA_WAVEFORMATEXTENSIBLE*)rawdata; - hr = ma_IDirectSoundCaptureBuffer_GetFormat((ma_IDirectSoundCaptureBuffer*)pDevice->dsound.pCaptureBuffer, (MA_WAVEFORMATEX*)pActualFormat, sizeof(rawdata), NULL); - if (FAILED(hr)) { - ma_device_uninit__dsound(pDevice); - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[DirectSound] Failed to retrieve the actual format of the capture device's buffer."); - return ma_result_from_HRESULT(hr); - } - - /* We can now start setting the output data formats. */ - pDescriptorCapture->format = ma_format_from_WAVEFORMATEX((MA_WAVEFORMATEX*)pActualFormat); - pDescriptorCapture->channels = pActualFormat->nChannels; - pDescriptorCapture->sampleRate = pActualFormat->nSamplesPerSec; - - /* Get the native channel map based on the channel mask. */ - if (pActualFormat->wFormatTag == WAVE_FORMAT_EXTENSIBLE) { - ma_channel_mask_to_channel_map__win32(pActualFormat->dwChannelMask, pDescriptorCapture->channels, pDescriptorCapture->channelMap); - } else { - ma_channel_mask_to_channel_map__win32(wf.dwChannelMask, pDescriptorCapture->channels, pDescriptorCapture->channelMap); - } - - /* - After getting the actual format the size of the buffer in frames may have actually changed. However, we want this to be as close to what the - user has asked for as possible, so let's go ahead and release the old capture buffer and create a new one in this case. - */ - if (periodSizeInFrames != (descDS.dwBufferBytes / ma_get_bytes_per_frame(pDescriptorCapture->format, pDescriptorCapture->channels) / periodCount)) { - descDS.dwBufferBytes = periodSizeInFrames * ma_get_bytes_per_frame(pDescriptorCapture->format, pDescriptorCapture->channels) * periodCount; - ma_IDirectSoundCaptureBuffer_Release((ma_IDirectSoundCaptureBuffer*)pDevice->dsound.pCaptureBuffer); - - hr = ma_IDirectSoundCapture_CreateCaptureBuffer((ma_IDirectSoundCapture*)pDevice->dsound.pCapture, &descDS, (ma_IDirectSoundCaptureBuffer**)&pDevice->dsound.pCaptureBuffer, NULL); - if (FAILED(hr)) { - ma_device_uninit__dsound(pDevice); - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[DirectSound] Second attempt at IDirectSoundCapture_CreateCaptureBuffer() failed for capture device."); - return ma_result_from_HRESULT(hr); - } - } - - /* DirectSound should give us a buffer exactly the size we asked for. */ - pDescriptorCapture->periodSizeInFrames = periodSizeInFrames; - pDescriptorCapture->periodCount = periodCount; - } - - if (pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) { - MA_WAVEFORMATEXTENSIBLE wf; - MA_DSBUFFERDESC descDSPrimary; - MA_DSCAPS caps; - char rawdata[1024]; /* <-- Ugly hack to avoid a malloc() due to a crappy DirectSound API. */ - MA_WAVEFORMATEXTENSIBLE* pActualFormat; - ma_uint32 periodSizeInFrames; - ma_uint32 periodCount; - MA_DSBUFFERDESC descDS; - WORD nativeChannelCount; - DWORD nativeChannelMask = 0; - - result = ma_config_to_WAVEFORMATEXTENSIBLE(pDescriptorPlayback->format, pDescriptorPlayback->channels, pDescriptorPlayback->sampleRate, pDescriptorPlayback->channelMap, &wf); - if (result != MA_SUCCESS) { - return result; - } - - result = ma_context_create_IDirectSound__dsound(pDevice->pContext, pDescriptorPlayback->shareMode, pDescriptorPlayback->pDeviceID, (ma_IDirectSound**)&pDevice->dsound.pPlayback); - if (result != MA_SUCCESS) { - ma_device_uninit__dsound(pDevice); - return result; - } - - MA_ZERO_OBJECT(&descDSPrimary); - descDSPrimary.dwSize = sizeof(MA_DSBUFFERDESC); - descDSPrimary.dwFlags = MA_DSBCAPS_PRIMARYBUFFER | MA_DSBCAPS_CTRLVOLUME; - hr = ma_IDirectSound_CreateSoundBuffer((ma_IDirectSound*)pDevice->dsound.pPlayback, &descDSPrimary, (ma_IDirectSoundBuffer**)&pDevice->dsound.pPlaybackPrimaryBuffer, NULL); - if (FAILED(hr)) { - ma_device_uninit__dsound(pDevice); - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[DirectSound] IDirectSound_CreateSoundBuffer() failed for playback device's primary buffer."); - return ma_result_from_HRESULT(hr); - } - - - /* We may want to make some adjustments to the format if we are using defaults. */ - MA_ZERO_OBJECT(&caps); - caps.dwSize = sizeof(caps); - hr = ma_IDirectSound_GetCaps((ma_IDirectSound*)pDevice->dsound.pPlayback, &caps); - if (FAILED(hr)) { - ma_device_uninit__dsound(pDevice); - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[DirectSound] IDirectSound_GetCaps() failed for playback device."); - return ma_result_from_HRESULT(hr); - } - - if ((caps.dwFlags & MA_DSCAPS_PRIMARYSTEREO) != 0) { - DWORD speakerConfig; - - /* It supports at least stereo, but could support more. */ - nativeChannelCount = 2; - - /* Look at the speaker configuration to get a better idea on the channel count. */ - if (SUCCEEDED(ma_IDirectSound_GetSpeakerConfig((ma_IDirectSound*)pDevice->dsound.pPlayback, &speakerConfig))) { - ma_get_channels_from_speaker_config__dsound(speakerConfig, &nativeChannelCount, &nativeChannelMask); - } - } else { - /* It does not support stereo, which means we are stuck with mono. */ - nativeChannelCount = 1; - nativeChannelMask = 0x00000001; - } - - if (pDescriptorPlayback->channels == 0) { - wf.nChannels = nativeChannelCount; - wf.dwChannelMask = nativeChannelMask; - } - - if (pDescriptorPlayback->sampleRate == 0) { - /* We base the sample rate on the values returned by GetCaps(). */ - if ((caps.dwFlags & MA_DSCAPS_CONTINUOUSRATE) != 0) { - wf.nSamplesPerSec = ma_get_best_sample_rate_within_range(caps.dwMinSecondarySampleRate, caps.dwMaxSecondarySampleRate); - } else { - wf.nSamplesPerSec = caps.dwMaxSecondarySampleRate; - } - } - - wf.nBlockAlign = (WORD)(wf.nChannels * wf.wBitsPerSample / 8); - wf.nAvgBytesPerSec = wf.nBlockAlign * wf.nSamplesPerSec; - - /* - From MSDN: - - The method succeeds even if the hardware does not support the requested format; DirectSound sets the buffer to the closest - supported format. To determine whether this has happened, an application can call the GetFormat method for the primary buffer - and compare the result with the format that was requested with the SetFormat method. - */ - hr = ma_IDirectSoundBuffer_SetFormat((ma_IDirectSoundBuffer*)pDevice->dsound.pPlaybackPrimaryBuffer, (MA_WAVEFORMATEX*)&wf); - if (FAILED(hr)) { - /* - If setting of the format failed we'll try again with some fallback settings. On Windows 98 I have - observed that IEEE_FLOAT does not work. We'll therefore enforce PCM. I also had issues where a - sample rate of 48000 did not work correctly. Not sure if it was a driver issue or not, but will - use 44100 for the sample rate. - */ - wf.cbSize = 18; /* NOTE: Don't use sizeof(MA_WAVEFORMATEX) here because it's got an extra 2 bytes due to padding. */ - wf.wFormatTag = WAVE_FORMAT_PCM; - wf.wBitsPerSample = 16; - wf.nChannels = nativeChannelCount; - wf.nSamplesPerSec = 44100; - wf.nBlockAlign = wf.nChannels * (wf.wBitsPerSample / 8); - wf.nAvgBytesPerSec = wf.nSamplesPerSec * wf.nBlockAlign; - - hr = ma_IDirectSoundBuffer_SetFormat((ma_IDirectSoundBuffer*)pDevice->dsound.pPlaybackPrimaryBuffer, (MA_WAVEFORMATEX*)&wf); - if (FAILED(hr)) { - ma_device_uninit__dsound(pDevice); - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[DirectSound] Failed to set format of playback device's primary buffer."); - return ma_result_from_HRESULT(hr); - } - } - - /* Get the _actual_ properties of the buffer. */ - pActualFormat = (MA_WAVEFORMATEXTENSIBLE*)rawdata; - hr = ma_IDirectSoundBuffer_GetFormat((ma_IDirectSoundBuffer*)pDevice->dsound.pPlaybackPrimaryBuffer, (MA_WAVEFORMATEX*)pActualFormat, sizeof(rawdata), NULL); - if (FAILED(hr)) { - ma_device_uninit__dsound(pDevice); - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[DirectSound] Failed to retrieve the actual format of the playback device's primary buffer."); - return ma_result_from_HRESULT(hr); - } - - /* We now have enough information to start setting some output properties. */ - pDescriptorPlayback->format = ma_format_from_WAVEFORMATEX((MA_WAVEFORMATEX*)pActualFormat); - pDescriptorPlayback->channels = pActualFormat->nChannels; - pDescriptorPlayback->sampleRate = pActualFormat->nSamplesPerSec; - - /* Get the internal channel map based on the channel mask. */ - if (pActualFormat->wFormatTag == WAVE_FORMAT_EXTENSIBLE) { - ma_channel_mask_to_channel_map__win32(pActualFormat->dwChannelMask, pDescriptorPlayback->channels, pDescriptorPlayback->channelMap); - } else { - ma_channel_mask_to_channel_map__win32(wf.dwChannelMask, pDescriptorPlayback->channels, pDescriptorPlayback->channelMap); - } - - /* The size of the buffer must be a clean multiple of the period count. */ - periodSizeInFrames = ma_calculate_period_size_in_frames_from_descriptor__dsound(pDescriptorPlayback, pDescriptorPlayback->sampleRate, pConfig->performanceProfile); - periodCount = (pDescriptorPlayback->periodCount > 0) ? pDescriptorPlayback->periodCount : MA_DEFAULT_PERIODS; - - /* - Meaning of dwFlags (from MSDN): - - DSBCAPS_CTRLPOSITIONNOTIFY - The buffer has position notification capability. - - DSBCAPS_GLOBALFOCUS - With this flag set, an application using DirectSound can continue to play its buffers if the user switches focus to - another application, even if the new application uses DirectSound. - - DSBCAPS_GETCURRENTPOSITION2 - In the first version of DirectSound, the play cursor was significantly ahead of the actual playing sound on emulated - sound cards; it was directly behind the write cursor. Now, if the DSBCAPS_GETCURRENTPOSITION2 flag is specified, the - application can get a more accurate play cursor. - */ - MA_ZERO_OBJECT(&descDS); - descDS.dwSize = sizeof(descDS); - descDS.dwFlags = MA_DSBCAPS_CTRLPOSITIONNOTIFY | MA_DSBCAPS_GLOBALFOCUS | MA_DSBCAPS_GETCURRENTPOSITION2; - descDS.dwBufferBytes = periodSizeInFrames * periodCount * ma_get_bytes_per_frame(pDescriptorPlayback->format, pDescriptorPlayback->channels); - descDS.lpwfxFormat = (MA_WAVEFORMATEX*)pActualFormat; - hr = ma_IDirectSound_CreateSoundBuffer((ma_IDirectSound*)pDevice->dsound.pPlayback, &descDS, (ma_IDirectSoundBuffer**)&pDevice->dsound.pPlaybackBuffer, NULL); - if (FAILED(hr)) { - ma_device_uninit__dsound(pDevice); - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[DirectSound] IDirectSound_CreateSoundBuffer() failed for playback device's secondary buffer."); - return ma_result_from_HRESULT(hr); - } - - /* DirectSound should give us a buffer exactly the size we asked for. */ - pDescriptorPlayback->periodSizeInFrames = periodSizeInFrames; - pDescriptorPlayback->periodCount = periodCount; - } - - return MA_SUCCESS; -} - - -static ma_result ma_device_data_loop__dsound(ma_device* pDevice) -{ - ma_result result = MA_SUCCESS; - ma_uint32 bpfDeviceCapture = ma_get_bytes_per_frame(pDevice->capture.internalFormat, pDevice->capture.internalChannels); - ma_uint32 bpfDevicePlayback = ma_get_bytes_per_frame(pDevice->playback.internalFormat, pDevice->playback.internalChannels); - HRESULT hr; - DWORD lockOffsetInBytesCapture; - DWORD lockSizeInBytesCapture; - DWORD mappedSizeInBytesCapture; - DWORD mappedDeviceFramesProcessedCapture; - void* pMappedDeviceBufferCapture; - DWORD lockOffsetInBytesPlayback; - DWORD lockSizeInBytesPlayback; - DWORD mappedSizeInBytesPlayback; - void* pMappedDeviceBufferPlayback; - DWORD prevReadCursorInBytesCapture = 0; - DWORD prevPlayCursorInBytesPlayback = 0; - ma_bool32 physicalPlayCursorLoopFlagPlayback = 0; - DWORD virtualWriteCursorInBytesPlayback = 0; - ma_bool32 virtualWriteCursorLoopFlagPlayback = 0; - ma_bool32 isPlaybackDeviceStarted = MA_FALSE; - ma_uint32 framesWrittenToPlaybackDevice = 0; /* For knowing whether or not the playback device needs to be started. */ - ma_uint32 waitTimeInMilliseconds = 1; - DWORD playbackBufferStatus = 0; - - MA_ASSERT(pDevice != NULL); - - /* The first thing to do is start the capture device. The playback device is only started after the first period is written. */ - if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) { - hr = ma_IDirectSoundCaptureBuffer_Start((ma_IDirectSoundCaptureBuffer*)pDevice->dsound.pCaptureBuffer, MA_DSCBSTART_LOOPING); - if (FAILED(hr)) { - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[DirectSound] IDirectSoundCaptureBuffer_Start() failed."); - return ma_result_from_HRESULT(hr); - } - } - - while (ma_device_get_state(pDevice) == ma_device_state_started) { - switch (pDevice->type) - { - case ma_device_type_duplex: - { - DWORD physicalCaptureCursorInBytes; - DWORD physicalReadCursorInBytes; - hr = ma_IDirectSoundCaptureBuffer_GetCurrentPosition((ma_IDirectSoundCaptureBuffer*)pDevice->dsound.pCaptureBuffer, &physicalCaptureCursorInBytes, &physicalReadCursorInBytes); - if (FAILED(hr)) { - return ma_result_from_HRESULT(hr); - } - - /* If nothing is available we just sleep for a bit and return from this iteration. */ - if (physicalReadCursorInBytes == prevReadCursorInBytesCapture) { - ma_sleep(waitTimeInMilliseconds); - continue; /* Nothing is available in the capture buffer. */ - } - - /* - The current position has moved. We need to map all of the captured samples and write them to the playback device, making sure - we don't return until every frame has been copied over. - */ - if (prevReadCursorInBytesCapture < physicalReadCursorInBytes) { - /* The capture position has not looped. This is the simple case. */ - lockOffsetInBytesCapture = prevReadCursorInBytesCapture; - lockSizeInBytesCapture = (physicalReadCursorInBytes - prevReadCursorInBytesCapture); - } else { - /* - The capture position has looped. This is the more complex case. Map to the end of the buffer. If this does not return anything, - do it again from the start. - */ - if (prevReadCursorInBytesCapture < pDevice->capture.internalPeriodSizeInFrames*pDevice->capture.internalPeriods*bpfDeviceCapture) { - /* Lock up to the end of the buffer. */ - lockOffsetInBytesCapture = prevReadCursorInBytesCapture; - lockSizeInBytesCapture = (pDevice->capture.internalPeriodSizeInFrames*pDevice->capture.internalPeriods*bpfDeviceCapture) - prevReadCursorInBytesCapture; - } else { - /* Lock starting from the start of the buffer. */ - lockOffsetInBytesCapture = 0; - lockSizeInBytesCapture = physicalReadCursorInBytes; - } - } - - if (lockSizeInBytesCapture == 0) { - ma_sleep(waitTimeInMilliseconds); - continue; /* Nothing is available in the capture buffer. */ - } - - hr = ma_IDirectSoundCaptureBuffer_Lock((ma_IDirectSoundCaptureBuffer*)pDevice->dsound.pCaptureBuffer, lockOffsetInBytesCapture, lockSizeInBytesCapture, &pMappedDeviceBufferCapture, &mappedSizeInBytesCapture, NULL, NULL, 0); - if (FAILED(hr)) { - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[DirectSound] Failed to map buffer from capture device in preparation for writing to the device."); - return ma_result_from_HRESULT(hr); - } - - - /* At this point we have some input data that we need to output. We do not return until every mapped frame of the input data is written to the playback device. */ - mappedDeviceFramesProcessedCapture = 0; - - for (;;) { /* Keep writing to the playback device. */ - ma_uint8 inputFramesInClientFormat[MA_DATA_CONVERTER_STACK_BUFFER_SIZE]; - ma_uint32 inputFramesInClientFormatCap = sizeof(inputFramesInClientFormat) / ma_get_bytes_per_frame(pDevice->capture.format, pDevice->capture.channels); - ma_uint8 outputFramesInClientFormat[MA_DATA_CONVERTER_STACK_BUFFER_SIZE]; - ma_uint32 outputFramesInClientFormatCap = sizeof(outputFramesInClientFormat) / ma_get_bytes_per_frame(pDevice->playback.format, pDevice->playback.channels); - ma_uint32 outputFramesInClientFormatCount; - ma_uint32 outputFramesInClientFormatConsumed = 0; - ma_uint64 clientCapturedFramesToProcess = ma_min(inputFramesInClientFormatCap, outputFramesInClientFormatCap); - ma_uint64 deviceCapturedFramesToProcess = (mappedSizeInBytesCapture / bpfDeviceCapture) - mappedDeviceFramesProcessedCapture; - void* pRunningMappedDeviceBufferCapture = ma_offset_ptr(pMappedDeviceBufferCapture, mappedDeviceFramesProcessedCapture * bpfDeviceCapture); - - result = ma_data_converter_process_pcm_frames(&pDevice->capture.converter, pRunningMappedDeviceBufferCapture, &deviceCapturedFramesToProcess, inputFramesInClientFormat, &clientCapturedFramesToProcess); - if (result != MA_SUCCESS) { - break; - } - - outputFramesInClientFormatCount = (ma_uint32)clientCapturedFramesToProcess; - mappedDeviceFramesProcessedCapture += (ma_uint32)deviceCapturedFramesToProcess; - - ma_device__handle_data_callback(pDevice, outputFramesInClientFormat, inputFramesInClientFormat, (ma_uint32)clientCapturedFramesToProcess); - - /* At this point we have input and output data in client format. All we need to do now is convert it to the output device format. This may take a few passes. */ - for (;;) { - ma_uint32 framesWrittenThisIteration; - DWORD physicalPlayCursorInBytes; - DWORD physicalWriteCursorInBytes; - DWORD availableBytesPlayback; - DWORD silentPaddingInBytes = 0; /* <-- Must be initialized to 0. */ - - /* We need the physical play and write cursors. */ - if (FAILED(ma_IDirectSoundBuffer_GetCurrentPosition((ma_IDirectSoundBuffer*)pDevice->dsound.pPlaybackBuffer, &physicalPlayCursorInBytes, &physicalWriteCursorInBytes))) { - break; - } - - if (physicalPlayCursorInBytes < prevPlayCursorInBytesPlayback) { - physicalPlayCursorLoopFlagPlayback = !physicalPlayCursorLoopFlagPlayback; - } - prevPlayCursorInBytesPlayback = physicalPlayCursorInBytes; - - /* If there's any bytes available for writing we can do that now. The space between the virtual cursor position and play cursor. */ - if (physicalPlayCursorLoopFlagPlayback == virtualWriteCursorLoopFlagPlayback) { - /* Same loop iteration. The available bytes wraps all the way around from the virtual write cursor to the physical play cursor. */ - if (physicalPlayCursorInBytes <= virtualWriteCursorInBytesPlayback) { - availableBytesPlayback = (pDevice->playback.internalPeriodSizeInFrames*pDevice->playback.internalPeriods*bpfDevicePlayback) - virtualWriteCursorInBytesPlayback; - availableBytesPlayback += physicalPlayCursorInBytes; /* Wrap around. */ - } else { - /* This is an error. */ - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_WARNING, "[DirectSound] (Duplex/Playback): Play cursor has moved in front of the write cursor (same loop iteration). physicalPlayCursorInBytes=%ld, virtualWriteCursorInBytes=%ld.\n", physicalPlayCursorInBytes, virtualWriteCursorInBytesPlayback); - availableBytesPlayback = 0; - } - } else { - /* Different loop iterations. The available bytes only goes from the virtual write cursor to the physical play cursor. */ - if (physicalPlayCursorInBytes >= virtualWriteCursorInBytesPlayback) { - availableBytesPlayback = physicalPlayCursorInBytes - virtualWriteCursorInBytesPlayback; - } else { - /* This is an error. */ - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_WARNING, "[DirectSound] (Duplex/Playback): Write cursor has moved behind the play cursor (different loop iterations). physicalPlayCursorInBytes=%ld, virtualWriteCursorInBytes=%ld.\n", physicalPlayCursorInBytes, virtualWriteCursorInBytesPlayback); - availableBytesPlayback = 0; - } - } - - /* If there's no room available for writing we need to wait for more. */ - if (availableBytesPlayback == 0) { - /* If we haven't started the device yet, this will never get beyond 0. In this case we need to get the device started. */ - if (!isPlaybackDeviceStarted) { - hr = ma_IDirectSoundBuffer_Play((ma_IDirectSoundBuffer*)pDevice->dsound.pPlaybackBuffer, 0, 0, MA_DSBPLAY_LOOPING); - if (FAILED(hr)) { - ma_IDirectSoundCaptureBuffer_Stop((ma_IDirectSoundCaptureBuffer*)pDevice->dsound.pCaptureBuffer); - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[DirectSound] IDirectSoundBuffer_Play() failed."); - return ma_result_from_HRESULT(hr); - } - isPlaybackDeviceStarted = MA_TRUE; - } else { - ma_sleep(waitTimeInMilliseconds); - continue; - } - } - - - /* Getting here means there room available somewhere. We limit this to either the end of the buffer or the physical play cursor, whichever is closest. */ - lockOffsetInBytesPlayback = virtualWriteCursorInBytesPlayback; - if (physicalPlayCursorLoopFlagPlayback == virtualWriteCursorLoopFlagPlayback) { - /* Same loop iteration. Go up to the end of the buffer. */ - lockSizeInBytesPlayback = (pDevice->playback.internalPeriodSizeInFrames*pDevice->playback.internalPeriods*bpfDevicePlayback) - virtualWriteCursorInBytesPlayback; - } else { - /* Different loop iterations. Go up to the physical play cursor. */ - lockSizeInBytesPlayback = physicalPlayCursorInBytes - virtualWriteCursorInBytesPlayback; - } - - hr = ma_IDirectSoundBuffer_Lock((ma_IDirectSoundBuffer*)pDevice->dsound.pPlaybackBuffer, lockOffsetInBytesPlayback, lockSizeInBytesPlayback, &pMappedDeviceBufferPlayback, &mappedSizeInBytesPlayback, NULL, NULL, 0); - if (FAILED(hr)) { - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[DirectSound] Failed to map buffer from playback device in preparation for writing to the device."); - result = ma_result_from_HRESULT(hr); - break; - } - - /* - Experiment: If the playback buffer is being starved, pad it with some silence to get it back in sync. This will cause a glitch, but it may prevent - endless glitching due to it constantly running out of data. - */ - if (isPlaybackDeviceStarted) { - DWORD bytesQueuedForPlayback = (pDevice->playback.internalPeriodSizeInFrames*pDevice->playback.internalPeriods*bpfDevicePlayback) - availableBytesPlayback; - if (bytesQueuedForPlayback < (pDevice->playback.internalPeriodSizeInFrames*bpfDevicePlayback)) { - silentPaddingInBytes = (pDevice->playback.internalPeriodSizeInFrames*2*bpfDevicePlayback) - bytesQueuedForPlayback; - if (silentPaddingInBytes > lockSizeInBytesPlayback) { - silentPaddingInBytes = lockSizeInBytesPlayback; - } - - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_WARNING, "[DirectSound] (Duplex/Playback) Playback buffer starved. availableBytesPlayback=%ld, silentPaddingInBytes=%ld\n", availableBytesPlayback, silentPaddingInBytes); - } - } - - /* At this point we have a buffer for output. */ - if (silentPaddingInBytes > 0) { - MA_ZERO_MEMORY(pMappedDeviceBufferPlayback, silentPaddingInBytes); - framesWrittenThisIteration = silentPaddingInBytes/bpfDevicePlayback; - } else { - ma_uint64 convertedFrameCountIn = (outputFramesInClientFormatCount - outputFramesInClientFormatConsumed); - ma_uint64 convertedFrameCountOut = mappedSizeInBytesPlayback/bpfDevicePlayback; - void* pConvertedFramesIn = ma_offset_ptr(outputFramesInClientFormat, outputFramesInClientFormatConsumed * bpfDevicePlayback); - void* pConvertedFramesOut = pMappedDeviceBufferPlayback; - - result = ma_data_converter_process_pcm_frames(&pDevice->playback.converter, pConvertedFramesIn, &convertedFrameCountIn, pConvertedFramesOut, &convertedFrameCountOut); - if (result != MA_SUCCESS) { - break; - } - - outputFramesInClientFormatConsumed += (ma_uint32)convertedFrameCountOut; - framesWrittenThisIteration = (ma_uint32)convertedFrameCountOut; - } - - - hr = ma_IDirectSoundBuffer_Unlock((ma_IDirectSoundBuffer*)pDevice->dsound.pPlaybackBuffer, pMappedDeviceBufferPlayback, framesWrittenThisIteration*bpfDevicePlayback, NULL, 0); - if (FAILED(hr)) { - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[DirectSound] Failed to unlock internal buffer from playback device after writing to the device."); - result = ma_result_from_HRESULT(hr); - break; - } - - virtualWriteCursorInBytesPlayback += framesWrittenThisIteration*bpfDevicePlayback; - if ((virtualWriteCursorInBytesPlayback/bpfDevicePlayback) == pDevice->playback.internalPeriodSizeInFrames*pDevice->playback.internalPeriods) { - virtualWriteCursorInBytesPlayback = 0; - virtualWriteCursorLoopFlagPlayback = !virtualWriteCursorLoopFlagPlayback; - } - - /* - We may need to start the device. We want two full periods to be written before starting the playback device. Having an extra period adds - a bit of a buffer to prevent the playback buffer from getting starved. - */ - framesWrittenToPlaybackDevice += framesWrittenThisIteration; - if (!isPlaybackDeviceStarted && framesWrittenToPlaybackDevice >= (pDevice->playback.internalPeriodSizeInFrames*2)) { - hr = ma_IDirectSoundBuffer_Play((ma_IDirectSoundBuffer*)pDevice->dsound.pPlaybackBuffer, 0, 0, MA_DSBPLAY_LOOPING); - if (FAILED(hr)) { - ma_IDirectSoundCaptureBuffer_Stop((ma_IDirectSoundCaptureBuffer*)pDevice->dsound.pCaptureBuffer); - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[DirectSound] IDirectSoundBuffer_Play() failed."); - return ma_result_from_HRESULT(hr); - } - isPlaybackDeviceStarted = MA_TRUE; - } - - if (framesWrittenThisIteration < mappedSizeInBytesPlayback/bpfDevicePlayback) { - break; /* We're finished with the output data.*/ - } - } - - if (clientCapturedFramesToProcess == 0) { - break; /* We just consumed every input sample. */ - } - } - - - /* At this point we're done with the mapped portion of the capture buffer. */ - hr = ma_IDirectSoundCaptureBuffer_Unlock((ma_IDirectSoundCaptureBuffer*)pDevice->dsound.pCaptureBuffer, pMappedDeviceBufferCapture, mappedSizeInBytesCapture, NULL, 0); - if (FAILED(hr)) { - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[DirectSound] Failed to unlock internal buffer from capture device after reading from the device."); - return ma_result_from_HRESULT(hr); - } - prevReadCursorInBytesCapture = (lockOffsetInBytesCapture + mappedSizeInBytesCapture); - } break; - - - - case ma_device_type_capture: - { - DWORD physicalCaptureCursorInBytes; - DWORD physicalReadCursorInBytes; - hr = ma_IDirectSoundCaptureBuffer_GetCurrentPosition((ma_IDirectSoundCaptureBuffer*)pDevice->dsound.pCaptureBuffer, &physicalCaptureCursorInBytes, &physicalReadCursorInBytes); - if (FAILED(hr)) { - return MA_ERROR; - } - - /* If the previous capture position is the same as the current position we need to wait a bit longer. */ - if (prevReadCursorInBytesCapture == physicalReadCursorInBytes) { - ma_sleep(waitTimeInMilliseconds); - continue; - } - - /* Getting here means we have capture data available. */ - if (prevReadCursorInBytesCapture < physicalReadCursorInBytes) { - /* The capture position has not looped. This is the simple case. */ - lockOffsetInBytesCapture = prevReadCursorInBytesCapture; - lockSizeInBytesCapture = (physicalReadCursorInBytes - prevReadCursorInBytesCapture); - } else { - /* - The capture position has looped. This is the more complex case. Map to the end of the buffer. If this does not return anything, - do it again from the start. - */ - if (prevReadCursorInBytesCapture < pDevice->capture.internalPeriodSizeInFrames*pDevice->capture.internalPeriods*bpfDeviceCapture) { - /* Lock up to the end of the buffer. */ - lockOffsetInBytesCapture = prevReadCursorInBytesCapture; - lockSizeInBytesCapture = (pDevice->capture.internalPeriodSizeInFrames*pDevice->capture.internalPeriods*bpfDeviceCapture) - prevReadCursorInBytesCapture; - } else { - /* Lock starting from the start of the buffer. */ - lockOffsetInBytesCapture = 0; - lockSizeInBytesCapture = physicalReadCursorInBytes; - } - } - - if (lockSizeInBytesCapture < pDevice->capture.internalPeriodSizeInFrames) { - ma_sleep(waitTimeInMilliseconds); - continue; /* Nothing is available in the capture buffer. */ - } - - hr = ma_IDirectSoundCaptureBuffer_Lock((ma_IDirectSoundCaptureBuffer*)pDevice->dsound.pCaptureBuffer, lockOffsetInBytesCapture, lockSizeInBytesCapture, &pMappedDeviceBufferCapture, &mappedSizeInBytesCapture, NULL, NULL, 0); - if (FAILED(hr)) { - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[DirectSound] Failed to map buffer from capture device in preparation for writing to the device."); - result = ma_result_from_HRESULT(hr); - } - - if (lockSizeInBytesCapture != mappedSizeInBytesCapture) { - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "[DirectSound] (Capture) lockSizeInBytesCapture=%ld != mappedSizeInBytesCapture=%ld\n", lockSizeInBytesCapture, mappedSizeInBytesCapture); - } - - ma_device__send_frames_to_client(pDevice, mappedSizeInBytesCapture/bpfDeviceCapture, pMappedDeviceBufferCapture); - - hr = ma_IDirectSoundCaptureBuffer_Unlock((ma_IDirectSoundCaptureBuffer*)pDevice->dsound.pCaptureBuffer, pMappedDeviceBufferCapture, mappedSizeInBytesCapture, NULL, 0); - if (FAILED(hr)) { - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[DirectSound] Failed to unlock internal buffer from capture device after reading from the device."); - return ma_result_from_HRESULT(hr); - } - prevReadCursorInBytesCapture = lockOffsetInBytesCapture + mappedSizeInBytesCapture; - - if (prevReadCursorInBytesCapture == (pDevice->capture.internalPeriodSizeInFrames*pDevice->capture.internalPeriods*bpfDeviceCapture)) { - prevReadCursorInBytesCapture = 0; - } - } break; - - - - case ma_device_type_playback: - { - DWORD availableBytesPlayback; - DWORD physicalPlayCursorInBytes; - DWORD physicalWriteCursorInBytes; - hr = ma_IDirectSoundBuffer_GetCurrentPosition((ma_IDirectSoundBuffer*)pDevice->dsound.pPlaybackBuffer, &physicalPlayCursorInBytes, &physicalWriteCursorInBytes); - if (FAILED(hr)) { - break; - } - - hr = ma_IDirectSoundBuffer_GetStatus((ma_IDirectSoundBuffer*)pDevice->dsound.pPlaybackBuffer, &playbackBufferStatus); - if (SUCCEEDED(hr) && (playbackBufferStatus & MA_DSBSTATUS_PLAYING) == 0 && isPlaybackDeviceStarted) { - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, "[DirectSound] Attempting to resume audio due to state: %d.", (int)playbackBufferStatus); - hr = ma_IDirectSoundBuffer_Play((ma_IDirectSoundBuffer*)pDevice->dsound.pPlaybackBuffer, 0, 0, MA_DSBPLAY_LOOPING); - if (FAILED(hr)) { - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[DirectSound] IDirectSoundBuffer_Play() failed after attempting to resume from state %d.", (int)playbackBufferStatus); - return ma_result_from_HRESULT(hr); - } - - isPlaybackDeviceStarted = MA_TRUE; - ma_sleep(waitTimeInMilliseconds); - continue; - } - - if (physicalPlayCursorInBytes < prevPlayCursorInBytesPlayback) { - physicalPlayCursorLoopFlagPlayback = !physicalPlayCursorLoopFlagPlayback; - } - prevPlayCursorInBytesPlayback = physicalPlayCursorInBytes; - - /* If there's any bytes available for writing we can do that now. The space between the virtual cursor position and play cursor. */ - if (physicalPlayCursorLoopFlagPlayback == virtualWriteCursorLoopFlagPlayback) { - /* Same loop iteration. The available bytes wraps all the way around from the virtual write cursor to the physical play cursor. */ - if (physicalPlayCursorInBytes <= virtualWriteCursorInBytesPlayback) { - availableBytesPlayback = (pDevice->playback.internalPeriodSizeInFrames*pDevice->playback.internalPeriods*bpfDevicePlayback) - virtualWriteCursorInBytesPlayback; - availableBytesPlayback += physicalPlayCursorInBytes; /* Wrap around. */ - } else { - /* This is an error. */ - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_WARNING, "[DirectSound] (Playback): Play cursor has moved in front of the write cursor (same loop iterations). physicalPlayCursorInBytes=%ld, virtualWriteCursorInBytes=%ld.\n", physicalPlayCursorInBytes, virtualWriteCursorInBytesPlayback); - availableBytesPlayback = 0; - } - } else { - /* Different loop iterations. The available bytes only goes from the virtual write cursor to the physical play cursor. */ - if (physicalPlayCursorInBytes >= virtualWriteCursorInBytesPlayback) { - availableBytesPlayback = physicalPlayCursorInBytes - virtualWriteCursorInBytesPlayback; - } else { - /* This is an error. */ - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_WARNING, "[DirectSound] (Playback): Write cursor has moved behind the play cursor (different loop iterations). physicalPlayCursorInBytes=%ld, virtualWriteCursorInBytes=%ld.\n", physicalPlayCursorInBytes, virtualWriteCursorInBytesPlayback); - availableBytesPlayback = 0; - } - } - - /* If there's no room available for writing we need to wait for more. */ - if (availableBytesPlayback < pDevice->playback.internalPeriodSizeInFrames) { - /* If we haven't started the device yet, this will never get beyond 0. In this case we need to get the device started. */ - if (availableBytesPlayback == 0 && !isPlaybackDeviceStarted) { - hr = ma_IDirectSoundBuffer_Play((ma_IDirectSoundBuffer*)pDevice->dsound.pPlaybackBuffer, 0, 0, MA_DSBPLAY_LOOPING); - if (FAILED(hr)) { - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[DirectSound] IDirectSoundBuffer_Play() failed."); - return ma_result_from_HRESULT(hr); - } - isPlaybackDeviceStarted = MA_TRUE; - } else { - ma_sleep(waitTimeInMilliseconds); - continue; - } - } - - /* Getting here means there room available somewhere. We limit this to either the end of the buffer or the physical play cursor, whichever is closest. */ - lockOffsetInBytesPlayback = virtualWriteCursorInBytesPlayback; - if (physicalPlayCursorLoopFlagPlayback == virtualWriteCursorLoopFlagPlayback) { - /* Same loop iteration. Go up to the end of the buffer. */ - lockSizeInBytesPlayback = (pDevice->playback.internalPeriodSizeInFrames*pDevice->playback.internalPeriods*bpfDevicePlayback) - virtualWriteCursorInBytesPlayback; - } else { - /* Different loop iterations. Go up to the physical play cursor. */ - lockSizeInBytesPlayback = physicalPlayCursorInBytes - virtualWriteCursorInBytesPlayback; - } - - hr = ma_IDirectSoundBuffer_Lock((ma_IDirectSoundBuffer*)pDevice->dsound.pPlaybackBuffer, lockOffsetInBytesPlayback, lockSizeInBytesPlayback, &pMappedDeviceBufferPlayback, &mappedSizeInBytesPlayback, NULL, NULL, 0); - if (FAILED(hr)) { - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[DirectSound] Failed to map buffer from playback device in preparation for writing to the device."); - result = ma_result_from_HRESULT(hr); - break; - } - - /* At this point we have a buffer for output. */ - ma_device__read_frames_from_client(pDevice, (mappedSizeInBytesPlayback/bpfDevicePlayback), pMappedDeviceBufferPlayback); - - hr = ma_IDirectSoundBuffer_Unlock((ma_IDirectSoundBuffer*)pDevice->dsound.pPlaybackBuffer, pMappedDeviceBufferPlayback, mappedSizeInBytesPlayback, NULL, 0); - if (FAILED(hr)) { - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[DirectSound] Failed to unlock internal buffer from playback device after writing to the device."); - result = ma_result_from_HRESULT(hr); - break; - } - - virtualWriteCursorInBytesPlayback += mappedSizeInBytesPlayback; - if (virtualWriteCursorInBytesPlayback == pDevice->playback.internalPeriodSizeInFrames*pDevice->playback.internalPeriods*bpfDevicePlayback) { - virtualWriteCursorInBytesPlayback = 0; - virtualWriteCursorLoopFlagPlayback = !virtualWriteCursorLoopFlagPlayback; - } - - /* - We may need to start the device. We want two full periods to be written before starting the playback device. Having an extra period adds - a bit of a buffer to prevent the playback buffer from getting starved. - */ - framesWrittenToPlaybackDevice += mappedSizeInBytesPlayback/bpfDevicePlayback; - if (!isPlaybackDeviceStarted && framesWrittenToPlaybackDevice >= pDevice->playback.internalPeriodSizeInFrames) { - hr = ma_IDirectSoundBuffer_Play((ma_IDirectSoundBuffer*)pDevice->dsound.pPlaybackBuffer, 0, 0, MA_DSBPLAY_LOOPING); - if (FAILED(hr)) { - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[DirectSound] IDirectSoundBuffer_Play() failed."); - return ma_result_from_HRESULT(hr); - } - isPlaybackDeviceStarted = MA_TRUE; - } - } break; - - - default: return MA_INVALID_ARGS; /* Invalid device type. */ - } - - if (result != MA_SUCCESS) { - return result; - } - } - - /* Getting here means the device is being stopped. */ - if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) { - hr = ma_IDirectSoundCaptureBuffer_Stop((ma_IDirectSoundCaptureBuffer*)pDevice->dsound.pCaptureBuffer); - if (FAILED(hr)) { - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[DirectSound] IDirectSoundCaptureBuffer_Stop() failed."); - return ma_result_from_HRESULT(hr); - } - } - - if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) { - /* The playback device should be drained before stopping. All we do is wait until the available bytes is equal to the size of the buffer. */ - if (isPlaybackDeviceStarted) { - for (;;) { - DWORD availableBytesPlayback = 0; - DWORD physicalPlayCursorInBytes; - DWORD physicalWriteCursorInBytes; - hr = ma_IDirectSoundBuffer_GetCurrentPosition((ma_IDirectSoundBuffer*)pDevice->dsound.pPlaybackBuffer, &physicalPlayCursorInBytes, &physicalWriteCursorInBytes); - if (FAILED(hr)) { - break; - } - - if (physicalPlayCursorInBytes < prevPlayCursorInBytesPlayback) { - physicalPlayCursorLoopFlagPlayback = !physicalPlayCursorLoopFlagPlayback; - } - prevPlayCursorInBytesPlayback = physicalPlayCursorInBytes; - - if (physicalPlayCursorLoopFlagPlayback == virtualWriteCursorLoopFlagPlayback) { - /* Same loop iteration. The available bytes wraps all the way around from the virtual write cursor to the physical play cursor. */ - if (physicalPlayCursorInBytes <= virtualWriteCursorInBytesPlayback) { - availableBytesPlayback = (pDevice->playback.internalPeriodSizeInFrames*pDevice->playback.internalPeriods*bpfDevicePlayback) - virtualWriteCursorInBytesPlayback; - availableBytesPlayback += physicalPlayCursorInBytes; /* Wrap around. */ - } else { - break; - } - } else { - /* Different loop iterations. The available bytes only goes from the virtual write cursor to the physical play cursor. */ - if (physicalPlayCursorInBytes >= virtualWriteCursorInBytesPlayback) { - availableBytesPlayback = physicalPlayCursorInBytes - virtualWriteCursorInBytesPlayback; - } else { - break; - } - } - - if (availableBytesPlayback >= (pDevice->playback.internalPeriodSizeInFrames*pDevice->playback.internalPeriods*bpfDevicePlayback)) { - break; - } - - ma_sleep(waitTimeInMilliseconds); - } - } - - hr = ma_IDirectSoundBuffer_Stop((ma_IDirectSoundBuffer*)pDevice->dsound.pPlaybackBuffer); - if (FAILED(hr)) { - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[DirectSound] IDirectSoundBuffer_Stop() failed."); - return ma_result_from_HRESULT(hr); - } - - ma_IDirectSoundBuffer_SetCurrentPosition((ma_IDirectSoundBuffer*)pDevice->dsound.pPlaybackBuffer, 0); - } - - return MA_SUCCESS; -} - -static ma_result ma_context_uninit__dsound(ma_context* pContext) -{ - MA_ASSERT(pContext != NULL); - MA_ASSERT(pContext->backend == ma_backend_dsound); - - ma_dlclose(ma_context_get_log(pContext), pContext->dsound.hDSoundDLL); - - return MA_SUCCESS; -} - -static ma_result ma_context_init__dsound(ma_context* pContext, const ma_context_config* pConfig, ma_backend_callbacks* pCallbacks) -{ - MA_ASSERT(pContext != NULL); - - (void)pConfig; - - pContext->dsound.hDSoundDLL = ma_dlopen(ma_context_get_log(pContext), "dsound.dll"); - if (pContext->dsound.hDSoundDLL == NULL) { - return MA_API_NOT_FOUND; - } - - pContext->dsound.DirectSoundCreate = ma_dlsym(ma_context_get_log(pContext), pContext->dsound.hDSoundDLL, "DirectSoundCreate"); - pContext->dsound.DirectSoundEnumerateA = ma_dlsym(ma_context_get_log(pContext), pContext->dsound.hDSoundDLL, "DirectSoundEnumerateA"); - pContext->dsound.DirectSoundCaptureCreate = ma_dlsym(ma_context_get_log(pContext), pContext->dsound.hDSoundDLL, "DirectSoundCaptureCreate"); - pContext->dsound.DirectSoundCaptureEnumerateA = ma_dlsym(ma_context_get_log(pContext), pContext->dsound.hDSoundDLL, "DirectSoundCaptureEnumerateA"); - - /* - We need to support all functions or nothing. DirectSound with Windows 95 seems to not work too - well in my testing. For example, it's missing DirectSoundCaptureEnumerateA(). This is a convenient - place to just disable the DirectSound backend for Windows 95. - */ - if (pContext->dsound.DirectSoundCreate == NULL || - pContext->dsound.DirectSoundEnumerateA == NULL || - pContext->dsound.DirectSoundCaptureCreate == NULL || - pContext->dsound.DirectSoundCaptureEnumerateA == NULL) { - return MA_API_NOT_FOUND; - } - - pContext->dsound.hWnd = pConfig->dsound.hWnd; - - pCallbacks->onContextInit = ma_context_init__dsound; - pCallbacks->onContextUninit = ma_context_uninit__dsound; - pCallbacks->onContextEnumerateDevices = ma_context_enumerate_devices__dsound; - pCallbacks->onContextGetDeviceInfo = ma_context_get_device_info__dsound; - pCallbacks->onDeviceInit = ma_device_init__dsound; - pCallbacks->onDeviceUninit = ma_device_uninit__dsound; - pCallbacks->onDeviceStart = NULL; /* Not used. Started in onDeviceDataLoop. */ - pCallbacks->onDeviceStop = NULL; /* Not used. Stopped in onDeviceDataLoop. */ - pCallbacks->onDeviceRead = NULL; /* Not used. Data is read directly in onDeviceDataLoop. */ - pCallbacks->onDeviceWrite = NULL; /* Not used. Data is written directly in onDeviceDataLoop. */ - pCallbacks->onDeviceDataLoop = ma_device_data_loop__dsound; - - return MA_SUCCESS; -} -#endif - - - -/****************************************************************************** - -WinMM Backend - -******************************************************************************/ -#ifdef MA_HAS_WINMM - -/* -Some build configurations will exclude the WinMM API. An example is when WIN32_LEAN_AND_MEAN -is defined. We need to define the types and functions we need manually. -*/ -#define MA_MMSYSERR_NOERROR 0 -#define MA_MMSYSERR_ERROR 1 -#define MA_MMSYSERR_BADDEVICEID 2 -#define MA_MMSYSERR_INVALHANDLE 5 -#define MA_MMSYSERR_NOMEM 7 -#define MA_MMSYSERR_INVALFLAG 10 -#define MA_MMSYSERR_INVALPARAM 11 -#define MA_MMSYSERR_HANDLEBUSY 12 - -#define MA_CALLBACK_EVENT 0x00050000 -#define MA_WAVE_ALLOWSYNC 0x0002 - -#define MA_WHDR_DONE 0x00000001 -#define MA_WHDR_PREPARED 0x00000002 -#define MA_WHDR_BEGINLOOP 0x00000004 -#define MA_WHDR_ENDLOOP 0x00000008 -#define MA_WHDR_INQUEUE 0x00000010 - -#define MA_MAXPNAMELEN 32 - -typedef void* MA_HWAVEIN; -typedef void* MA_HWAVEOUT; -typedef UINT MA_MMRESULT; -typedef UINT MA_MMVERSION; - -typedef struct -{ - WORD wMid; - WORD wPid; - MA_MMVERSION vDriverVersion; - CHAR szPname[MA_MAXPNAMELEN]; - DWORD dwFormats; - WORD wChannels; - WORD wReserved1; -} MA_WAVEINCAPSA; - -typedef struct -{ - WORD wMid; - WORD wPid; - MA_MMVERSION vDriverVersion; - CHAR szPname[MA_MAXPNAMELEN]; - DWORD dwFormats; - WORD wChannels; - WORD wReserved1; - DWORD dwSupport; -} MA_WAVEOUTCAPSA; - -typedef struct tagWAVEHDR -{ - char* lpData; - DWORD dwBufferLength; - DWORD dwBytesRecorded; - DWORD_PTR dwUser; - DWORD dwFlags; - DWORD dwLoops; - struct tagWAVEHDR* lpNext; - DWORD_PTR reserved; -} MA_WAVEHDR; - -typedef struct -{ - WORD wMid; - WORD wPid; - MA_MMVERSION vDriverVersion; - CHAR szPname[MA_MAXPNAMELEN]; - DWORD dwFormats; - WORD wChannels; - WORD wReserved1; - DWORD dwSupport; - GUID ManufacturerGuid; - GUID ProductGuid; - GUID NameGuid; -} MA_WAVEOUTCAPS2A; - -typedef struct -{ - WORD wMid; - WORD wPid; - MA_MMVERSION vDriverVersion; - CHAR szPname[MA_MAXPNAMELEN]; - DWORD dwFormats; - WORD wChannels; - WORD wReserved1; - GUID ManufacturerGuid; - GUID ProductGuid; - GUID NameGuid; -} MA_WAVEINCAPS2A; - -typedef UINT (WINAPI * MA_PFN_waveOutGetNumDevs)(void); -typedef MA_MMRESULT (WINAPI * MA_PFN_waveOutGetDevCapsA)(ma_uintptr uDeviceID, MA_WAVEOUTCAPSA* pwoc, UINT cbwoc); -typedef MA_MMRESULT (WINAPI * MA_PFN_waveOutOpen)(MA_HWAVEOUT* phwo, UINT uDeviceID, const MA_WAVEFORMATEX* pwfx, DWORD_PTR dwCallback, DWORD_PTR dwInstance, DWORD fdwOpen); -typedef MA_MMRESULT (WINAPI * MA_PFN_waveOutClose)(MA_HWAVEOUT hwo); -typedef MA_MMRESULT (WINAPI * MA_PFN_waveOutPrepareHeader)(MA_HWAVEOUT hwo, MA_WAVEHDR* pwh, UINT cbwh); -typedef MA_MMRESULT (WINAPI * MA_PFN_waveOutUnprepareHeader)(MA_HWAVEOUT hwo, MA_WAVEHDR* pwh, UINT cbwh); -typedef MA_MMRESULT (WINAPI * MA_PFN_waveOutWrite)(MA_HWAVEOUT hwo, MA_WAVEHDR* pwh, UINT cbwh); -typedef MA_MMRESULT (WINAPI * MA_PFN_waveOutReset)(MA_HWAVEOUT hwo); -typedef UINT (WINAPI * MA_PFN_waveInGetNumDevs)(void); -typedef MA_MMRESULT (WINAPI * MA_PFN_waveInGetDevCapsA)(ma_uintptr uDeviceID, MA_WAVEINCAPSA* pwic, UINT cbwic); -typedef MA_MMRESULT (WINAPI * MA_PFN_waveInOpen)(MA_HWAVEIN* phwi, UINT uDeviceID, const MA_WAVEFORMATEX* pwfx, DWORD_PTR dwCallback, DWORD_PTR dwInstance, DWORD fdwOpen); -typedef MA_MMRESULT (WINAPI * MA_PFN_waveInClose)(MA_HWAVEIN hwi); -typedef MA_MMRESULT (WINAPI * MA_PFN_waveInPrepareHeader)(MA_HWAVEIN hwi, MA_WAVEHDR* pwh, UINT cbwh); -typedef MA_MMRESULT (WINAPI * MA_PFN_waveInUnprepareHeader)(MA_HWAVEIN hwi, MA_WAVEHDR* pwh, UINT cbwh); -typedef MA_MMRESULT (WINAPI * MA_PFN_waveInAddBuffer)(MA_HWAVEIN hwi, MA_WAVEHDR* pwh, UINT cbwh); -typedef MA_MMRESULT (WINAPI * MA_PFN_waveInStart)(MA_HWAVEIN hwi); -typedef MA_MMRESULT (WINAPI * MA_PFN_waveInReset)(MA_HWAVEIN hwi); - -static ma_result ma_result_from_MMRESULT(MA_MMRESULT resultMM) -{ - switch (resultMM) - { - case MA_MMSYSERR_NOERROR: return MA_SUCCESS; - case MA_MMSYSERR_BADDEVICEID: return MA_INVALID_ARGS; - case MA_MMSYSERR_INVALHANDLE: return MA_INVALID_ARGS; - case MA_MMSYSERR_NOMEM: return MA_OUT_OF_MEMORY; - case MA_MMSYSERR_INVALFLAG: return MA_INVALID_ARGS; - case MA_MMSYSERR_INVALPARAM: return MA_INVALID_ARGS; - case MA_MMSYSERR_HANDLEBUSY: return MA_BUSY; - case MA_MMSYSERR_ERROR: return MA_ERROR; - default: return MA_ERROR; - } -} - -static char* ma_find_last_character(char* str, char ch) -{ - char* last; - - if (str == NULL) { - return NULL; - } - - last = NULL; - while (*str != '\0') { - if (*str == ch) { - last = str; - } - - str += 1; - } - - return last; -} - -static ma_uint32 ma_get_period_size_in_bytes(ma_uint32 periodSizeInFrames, ma_format format, ma_uint32 channels) -{ - return periodSizeInFrames * ma_get_bytes_per_frame(format, channels); -} - - -/* -Our own "WAVECAPS" structure that contains generic information shared between WAVEOUTCAPS2 and WAVEINCAPS2 so -we can do things generically and typesafely. Names are being kept the same for consistency. -*/ -typedef struct -{ - CHAR szPname[MA_MAXPNAMELEN]; - DWORD dwFormats; - WORD wChannels; - GUID NameGuid; -} MA_WAVECAPSA; - -static ma_result ma_get_best_info_from_formats_flags__winmm(DWORD dwFormats, WORD channels, WORD* pBitsPerSample, DWORD* pSampleRate) -{ - WORD bitsPerSample = 0; - DWORD sampleRate = 0; - - if (pBitsPerSample) { - *pBitsPerSample = 0; - } - if (pSampleRate) { - *pSampleRate = 0; - } - - if (channels == 1) { - bitsPerSample = 16; - if ((dwFormats & WAVE_FORMAT_48M16) != 0) { - sampleRate = 48000; - } else if ((dwFormats & WAVE_FORMAT_44M16) != 0) { - sampleRate = 44100; - } else if ((dwFormats & WAVE_FORMAT_2M16) != 0) { - sampleRate = 22050; - } else if ((dwFormats & WAVE_FORMAT_1M16) != 0) { - sampleRate = 11025; - } else if ((dwFormats & WAVE_FORMAT_96M16) != 0) { - sampleRate = 96000; - } else { - bitsPerSample = 8; - if ((dwFormats & WAVE_FORMAT_48M08) != 0) { - sampleRate = 48000; - } else if ((dwFormats & WAVE_FORMAT_44M08) != 0) { - sampleRate = 44100; - } else if ((dwFormats & WAVE_FORMAT_2M08) != 0) { - sampleRate = 22050; - } else if ((dwFormats & WAVE_FORMAT_1M08) != 0) { - sampleRate = 11025; - } else if ((dwFormats & WAVE_FORMAT_96M08) != 0) { - sampleRate = 96000; - } else { - return MA_FORMAT_NOT_SUPPORTED; - } - } - } else { - bitsPerSample = 16; - if ((dwFormats & WAVE_FORMAT_48S16) != 0) { - sampleRate = 48000; - } else if ((dwFormats & WAVE_FORMAT_44S16) != 0) { - sampleRate = 44100; - } else if ((dwFormats & WAVE_FORMAT_2S16) != 0) { - sampleRate = 22050; - } else if ((dwFormats & WAVE_FORMAT_1S16) != 0) { - sampleRate = 11025; - } else if ((dwFormats & WAVE_FORMAT_96S16) != 0) { - sampleRate = 96000; - } else { - bitsPerSample = 8; - if ((dwFormats & WAVE_FORMAT_48S08) != 0) { - sampleRate = 48000; - } else if ((dwFormats & WAVE_FORMAT_44S08) != 0) { - sampleRate = 44100; - } else if ((dwFormats & WAVE_FORMAT_2S08) != 0) { - sampleRate = 22050; - } else if ((dwFormats & WAVE_FORMAT_1S08) != 0) { - sampleRate = 11025; - } else if ((dwFormats & WAVE_FORMAT_96S08) != 0) { - sampleRate = 96000; - } else { - return MA_FORMAT_NOT_SUPPORTED; - } - } - } - - if (pBitsPerSample) { - *pBitsPerSample = bitsPerSample; - } - if (pSampleRate) { - *pSampleRate = sampleRate; - } - - return MA_SUCCESS; -} - -static ma_result ma_formats_flags_to_WAVEFORMATEX__winmm(DWORD dwFormats, WORD channels, MA_WAVEFORMATEX* pWF) -{ - ma_result result; - - MA_ASSERT(pWF != NULL); - - MA_ZERO_OBJECT(pWF); - pWF->cbSize = sizeof(*pWF); - pWF->wFormatTag = WAVE_FORMAT_PCM; - pWF->nChannels = (WORD)channels; - if (pWF->nChannels > 2) { - pWF->nChannels = 2; - } - - result = ma_get_best_info_from_formats_flags__winmm(dwFormats, channels, &pWF->wBitsPerSample, &pWF->nSamplesPerSec); - if (result != MA_SUCCESS) { - return result; - } - - pWF->nBlockAlign = (WORD)(pWF->nChannels * pWF->wBitsPerSample / 8); - pWF->nAvgBytesPerSec = pWF->nBlockAlign * pWF->nSamplesPerSec; - - return MA_SUCCESS; -} - -static ma_result ma_context_get_device_info_from_WAVECAPS(ma_context* pContext, MA_WAVECAPSA* pCaps, ma_device_info* pDeviceInfo) -{ - WORD bitsPerSample; - DWORD sampleRate; - ma_result result; - - MA_ASSERT(pContext != NULL); - MA_ASSERT(pCaps != NULL); - MA_ASSERT(pDeviceInfo != NULL); - - /* - Name / Description - - Unfortunately the name specified in WAVE(OUT/IN)CAPS2 is limited to 31 characters. This results in an unprofessional looking - situation where the names of the devices are truncated. To help work around this, we need to look at the name GUID and try - looking in the registry for the full name. If we can't find it there, we need to just fall back to the default name. - */ - - /* Set the default to begin with. */ - ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), pCaps->szPname, (size_t)-1); - - /* - Now try the registry. There's a few things to consider here: - - The name GUID can be null, in which we case we just need to stick to the original 31 characters. - - If the name GUID is not present in the registry we'll also need to stick to the original 31 characters. - - I like consistency, so I want the returned device names to be consistent with those returned by WASAPI and DirectSound. The - problem, however is that WASAPI and DirectSound use " ()" format (such as "Speakers (High Definition Audio)"), - but WinMM does not specify the component name. From my admittedly limited testing, I've notice the component name seems to - usually fit within the 31 characters of the fixed sized buffer, so what I'm going to do is parse that string for the component - name, and then concatenate the name from the registry. - */ - if (!ma_is_guid_null(&pCaps->NameGuid)) { - WCHAR guidStrW[256]; - if (((MA_PFN_StringFromGUID2)pContext->win32.StringFromGUID2)(&pCaps->NameGuid, guidStrW, ma_countof(guidStrW)) > 0) { - char guidStr[256]; - char keyStr[1024]; - HKEY hKey; - - WideCharToMultiByte(CP_UTF8, 0, guidStrW, -1, guidStr, sizeof(guidStr), 0, FALSE); - - ma_strcpy_s(keyStr, sizeof(keyStr), "SYSTEM\\CurrentControlSet\\Control\\MediaCategories\\"); - ma_strcat_s(keyStr, sizeof(keyStr), guidStr); - - if (((MA_PFN_RegOpenKeyExA)pContext->win32.RegOpenKeyExA)(HKEY_LOCAL_MACHINE, keyStr, 0, KEY_READ, &hKey) == ERROR_SUCCESS) { - BYTE nameFromReg[512]; - DWORD nameFromRegSize = sizeof(nameFromReg); - LONG resultWin32 = ((MA_PFN_RegQueryValueExA)pContext->win32.RegQueryValueExA)(hKey, "Name", 0, NULL, (BYTE*)nameFromReg, (DWORD*)&nameFromRegSize); - ((MA_PFN_RegCloseKey)pContext->win32.RegCloseKey)(hKey); - - if (resultWin32 == ERROR_SUCCESS) { - /* We have the value from the registry, so now we need to construct the name string. */ - char name[1024]; - if (ma_strcpy_s(name, sizeof(name), pDeviceInfo->name) == 0) { - char* nameBeg = ma_find_last_character(name, '('); - if (nameBeg != NULL) { - size_t leadingLen = (nameBeg - name); - ma_strncpy_s(nameBeg + 1, sizeof(name) - leadingLen, (const char*)nameFromReg, (size_t)-1); - - /* The closing ")", if it can fit. */ - if (leadingLen + nameFromRegSize < sizeof(name)-1) { - ma_strcat_s(name, sizeof(name), ")"); - } - - ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), name, (size_t)-1); - } - } - } - } - } - } - - - result = ma_get_best_info_from_formats_flags__winmm(pCaps->dwFormats, pCaps->wChannels, &bitsPerSample, &sampleRate); - if (result != MA_SUCCESS) { - return result; - } - - if (bitsPerSample == 8) { - pDeviceInfo->nativeDataFormats[0].format = ma_format_u8; - } else if (bitsPerSample == 16) { - pDeviceInfo->nativeDataFormats[0].format = ma_format_s16; - } else if (bitsPerSample == 24) { - pDeviceInfo->nativeDataFormats[0].format = ma_format_s24; - } else if (bitsPerSample == 32) { - pDeviceInfo->nativeDataFormats[0].format = ma_format_s32; - } else { - return MA_FORMAT_NOT_SUPPORTED; - } - pDeviceInfo->nativeDataFormats[0].channels = pCaps->wChannels; - pDeviceInfo->nativeDataFormats[0].sampleRate = sampleRate; - pDeviceInfo->nativeDataFormats[0].flags = 0; - pDeviceInfo->nativeDataFormatCount = 1; - - return MA_SUCCESS; -} - -static ma_result ma_context_get_device_info_from_WAVEOUTCAPS2(ma_context* pContext, MA_WAVEOUTCAPS2A* pCaps, ma_device_info* pDeviceInfo) -{ - MA_WAVECAPSA caps; - - MA_ASSERT(pContext != NULL); - MA_ASSERT(pCaps != NULL); - MA_ASSERT(pDeviceInfo != NULL); - - MA_COPY_MEMORY(caps.szPname, pCaps->szPname, sizeof(caps.szPname)); - caps.dwFormats = pCaps->dwFormats; - caps.wChannels = pCaps->wChannels; - caps.NameGuid = pCaps->NameGuid; - return ma_context_get_device_info_from_WAVECAPS(pContext, &caps, pDeviceInfo); -} - -static ma_result ma_context_get_device_info_from_WAVEINCAPS2(ma_context* pContext, MA_WAVEINCAPS2A* pCaps, ma_device_info* pDeviceInfo) -{ - MA_WAVECAPSA caps; - - MA_ASSERT(pContext != NULL); - MA_ASSERT(pCaps != NULL); - MA_ASSERT(pDeviceInfo != NULL); - - MA_COPY_MEMORY(caps.szPname, pCaps->szPname, sizeof(caps.szPname)); - caps.dwFormats = pCaps->dwFormats; - caps.wChannels = pCaps->wChannels; - caps.NameGuid = pCaps->NameGuid; - return ma_context_get_device_info_from_WAVECAPS(pContext, &caps, pDeviceInfo); -} - - -static ma_result ma_context_enumerate_devices__winmm(ma_context* pContext, ma_enum_devices_callback_proc callback, void* pUserData) -{ - UINT playbackDeviceCount; - UINT captureDeviceCount; - UINT iPlaybackDevice; - UINT iCaptureDevice; - - MA_ASSERT(pContext != NULL); - MA_ASSERT(callback != NULL); - - /* Playback. */ - playbackDeviceCount = ((MA_PFN_waveOutGetNumDevs)pContext->winmm.waveOutGetNumDevs)(); - for (iPlaybackDevice = 0; iPlaybackDevice < playbackDeviceCount; ++iPlaybackDevice) { - MA_MMRESULT result; - MA_WAVEOUTCAPS2A caps; - - MA_ZERO_OBJECT(&caps); - - result = ((MA_PFN_waveOutGetDevCapsA)pContext->winmm.waveOutGetDevCapsA)(iPlaybackDevice, (MA_WAVEOUTCAPSA*)&caps, sizeof(caps)); - if (result == MA_MMSYSERR_NOERROR) { - ma_device_info deviceInfo; - - MA_ZERO_OBJECT(&deviceInfo); - deviceInfo.id.winmm = iPlaybackDevice; - - /* The first enumerated device is the default device. */ - if (iPlaybackDevice == 0) { - deviceInfo.isDefault = MA_TRUE; - } - - if (ma_context_get_device_info_from_WAVEOUTCAPS2(pContext, &caps, &deviceInfo) == MA_SUCCESS) { - ma_bool32 cbResult = callback(pContext, ma_device_type_playback, &deviceInfo, pUserData); - if (cbResult == MA_FALSE) { - return MA_SUCCESS; /* Enumeration was stopped. */ - } - } - } - } - - /* Capture. */ - captureDeviceCount = ((MA_PFN_waveInGetNumDevs)pContext->winmm.waveInGetNumDevs)(); - for (iCaptureDevice = 0; iCaptureDevice < captureDeviceCount; ++iCaptureDevice) { - MA_MMRESULT result; - MA_WAVEINCAPS2A caps; - - MA_ZERO_OBJECT(&caps); - - result = ((MA_PFN_waveInGetDevCapsA)pContext->winmm.waveInGetDevCapsA)(iCaptureDevice, (MA_WAVEINCAPSA*)&caps, sizeof(caps)); - if (result == MA_MMSYSERR_NOERROR) { - ma_device_info deviceInfo; - - MA_ZERO_OBJECT(&deviceInfo); - deviceInfo.id.winmm = iCaptureDevice; - - /* The first enumerated device is the default device. */ - if (iCaptureDevice == 0) { - deviceInfo.isDefault = MA_TRUE; - } - - if (ma_context_get_device_info_from_WAVEINCAPS2(pContext, &caps, &deviceInfo) == MA_SUCCESS) { - ma_bool32 cbResult = callback(pContext, ma_device_type_capture, &deviceInfo, pUserData); - if (cbResult == MA_FALSE) { - return MA_SUCCESS; /* Enumeration was stopped. */ - } - } - } - } - - return MA_SUCCESS; -} - -static ma_result ma_context_get_device_info__winmm(ma_context* pContext, ma_device_type deviceType, const ma_device_id* pDeviceID, ma_device_info* pDeviceInfo) -{ - UINT winMMDeviceID; - - MA_ASSERT(pContext != NULL); - - winMMDeviceID = 0; - if (pDeviceID != NULL) { - winMMDeviceID = (UINT)pDeviceID->winmm; - } - - pDeviceInfo->id.winmm = winMMDeviceID; - - /* The first ID is the default device. */ - if (winMMDeviceID == 0) { - pDeviceInfo->isDefault = MA_TRUE; - } - - if (deviceType == ma_device_type_playback) { - MA_MMRESULT result; - MA_WAVEOUTCAPS2A caps; - - MA_ZERO_OBJECT(&caps); - - result = ((MA_PFN_waveOutGetDevCapsA)pContext->winmm.waveOutGetDevCapsA)(winMMDeviceID, (MA_WAVEOUTCAPSA*)&caps, sizeof(caps)); - if (result == MA_MMSYSERR_NOERROR) { - return ma_context_get_device_info_from_WAVEOUTCAPS2(pContext, &caps, pDeviceInfo); - } - } else { - MA_MMRESULT result; - MA_WAVEINCAPS2A caps; - - MA_ZERO_OBJECT(&caps); - - result = ((MA_PFN_waveInGetDevCapsA)pContext->winmm.waveInGetDevCapsA)(winMMDeviceID, (MA_WAVEINCAPSA*)&caps, sizeof(caps)); - if (result == MA_MMSYSERR_NOERROR) { - return ma_context_get_device_info_from_WAVEINCAPS2(pContext, &caps, pDeviceInfo); - } - } - - return MA_NO_DEVICE; -} - - -static ma_result ma_device_uninit__winmm(ma_device* pDevice) -{ - MA_ASSERT(pDevice != NULL); - - if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) { - ((MA_PFN_waveInClose)pDevice->pContext->winmm.waveInClose)((MA_HWAVEIN)pDevice->winmm.hDeviceCapture); - CloseHandle((HANDLE)pDevice->winmm.hEventCapture); - } - - if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) { - ((MA_PFN_waveOutReset)pDevice->pContext->winmm.waveOutReset)((MA_HWAVEOUT)pDevice->winmm.hDevicePlayback); - ((MA_PFN_waveOutClose)pDevice->pContext->winmm.waveOutClose)((MA_HWAVEOUT)pDevice->winmm.hDevicePlayback); - CloseHandle((HANDLE)pDevice->winmm.hEventPlayback); - } - - ma_free(pDevice->winmm._pHeapData, &pDevice->pContext->allocationCallbacks); - - MA_ZERO_OBJECT(&pDevice->winmm); /* Safety. */ - - return MA_SUCCESS; -} - -static ma_uint32 ma_calculate_period_size_in_frames_from_descriptor__winmm(const ma_device_descriptor* pDescriptor, ma_uint32 nativeSampleRate, ma_performance_profile performanceProfile) -{ - /* WinMM has a minimum period size of 40ms. */ - ma_uint32 minPeriodSizeInFrames = ma_calculate_buffer_size_in_frames_from_milliseconds(40, nativeSampleRate); - ma_uint32 periodSizeInFrames; - - periodSizeInFrames = ma_calculate_buffer_size_in_frames_from_descriptor(pDescriptor, nativeSampleRate, performanceProfile); - if (periodSizeInFrames < minPeriodSizeInFrames) { - periodSizeInFrames = minPeriodSizeInFrames; - } - - return periodSizeInFrames; -} - -static ma_result ma_device_init__winmm(ma_device* pDevice, const ma_device_config* pConfig, ma_device_descriptor* pDescriptorPlayback, ma_device_descriptor* pDescriptorCapture) -{ - const char* errorMsg = ""; - ma_result errorCode = MA_ERROR; - ma_result result = MA_SUCCESS; - ma_uint32 heapSize; - UINT winMMDeviceIDPlayback = 0; - UINT winMMDeviceIDCapture = 0; - - MA_ASSERT(pDevice != NULL); - - MA_ZERO_OBJECT(&pDevice->winmm); - - if (pConfig->deviceType == ma_device_type_loopback) { - return MA_DEVICE_TYPE_NOT_SUPPORTED; - } - - /* No exclusive mode with WinMM. */ - if (((pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) && pDescriptorPlayback->shareMode == ma_share_mode_exclusive) || - ((pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) && pDescriptorCapture->shareMode == ma_share_mode_exclusive)) { - return MA_SHARE_MODE_NOT_SUPPORTED; - } - - if (pDescriptorPlayback->pDeviceID != NULL) { - winMMDeviceIDPlayback = (UINT)pDescriptorPlayback->pDeviceID->winmm; - } - if (pDescriptorCapture->pDeviceID != NULL) { - winMMDeviceIDCapture = (UINT)pDescriptorCapture->pDeviceID->winmm; - } - - /* The capture device needs to be initialized first. */ - if (pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) { - MA_WAVEINCAPSA caps; - MA_WAVEFORMATEX wf; - MA_MMRESULT resultMM; - - /* We use an event to know when a new fragment needs to be enqueued. */ - pDevice->winmm.hEventCapture = (ma_handle)CreateEventA(NULL, TRUE, TRUE, NULL); - if (pDevice->winmm.hEventCapture == NULL) { - errorMsg = "[WinMM] Failed to create event for fragment enqueuing for the capture device.", errorCode = ma_result_from_GetLastError(GetLastError()); - goto on_error; - } - - /* The format should be based on the device's actual format. */ - if (((MA_PFN_waveInGetDevCapsA)pDevice->pContext->winmm.waveInGetDevCapsA)(winMMDeviceIDCapture, &caps, sizeof(caps)) != MA_MMSYSERR_NOERROR) { - errorMsg = "[WinMM] Failed to retrieve internal device caps.", errorCode = MA_FORMAT_NOT_SUPPORTED; - goto on_error; - } - - result = ma_formats_flags_to_WAVEFORMATEX__winmm(caps.dwFormats, caps.wChannels, &wf); - if (result != MA_SUCCESS) { - errorMsg = "[WinMM] Could not find appropriate format for internal device.", errorCode = result; - goto on_error; - } - - resultMM = ((MA_PFN_waveInOpen)pDevice->pContext->winmm.waveInOpen)((MA_HWAVEIN*)&pDevice->winmm.hDeviceCapture, winMMDeviceIDCapture, &wf, (DWORD_PTR)pDevice->winmm.hEventCapture, (DWORD_PTR)pDevice, MA_CALLBACK_EVENT | MA_WAVE_ALLOWSYNC); - if (resultMM != MA_MMSYSERR_NOERROR) { - errorMsg = "[WinMM] Failed to open capture device.", errorCode = MA_FAILED_TO_OPEN_BACKEND_DEVICE; - goto on_error; - } - - pDescriptorCapture->format = ma_format_from_WAVEFORMATEX(&wf); - pDescriptorCapture->channels = wf.nChannels; - pDescriptorCapture->sampleRate = wf.nSamplesPerSec; - ma_channel_map_init_standard(ma_standard_channel_map_microsoft, pDescriptorCapture->channelMap, ma_countof(pDescriptorCapture->channelMap), pDescriptorCapture->channels); - pDescriptorCapture->periodCount = pDescriptorCapture->periodCount; - pDescriptorCapture->periodSizeInFrames = ma_calculate_period_size_in_frames_from_descriptor__winmm(pDescriptorCapture, pDescriptorCapture->sampleRate, pConfig->performanceProfile); - } - - if (pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) { - MA_WAVEOUTCAPSA caps; - MA_WAVEFORMATEX wf; - MA_MMRESULT resultMM; - - /* We use an event to know when a new fragment needs to be enqueued. */ - pDevice->winmm.hEventPlayback = (ma_handle)CreateEventA(NULL, TRUE, TRUE, NULL); - if (pDevice->winmm.hEventPlayback == NULL) { - errorMsg = "[WinMM] Failed to create event for fragment enqueuing for the playback device.", errorCode = ma_result_from_GetLastError(GetLastError()); - goto on_error; - } - - /* The format should be based on the device's actual format. */ - if (((MA_PFN_waveOutGetDevCapsA)pDevice->pContext->winmm.waveOutGetDevCapsA)(winMMDeviceIDPlayback, &caps, sizeof(caps)) != MA_MMSYSERR_NOERROR) { - errorMsg = "[WinMM] Failed to retrieve internal device caps.", errorCode = MA_FORMAT_NOT_SUPPORTED; - goto on_error; - } - - result = ma_formats_flags_to_WAVEFORMATEX__winmm(caps.dwFormats, caps.wChannels, &wf); - if (result != MA_SUCCESS) { - errorMsg = "[WinMM] Could not find appropriate format for internal device.", errorCode = result; - goto on_error; - } - - resultMM = ((MA_PFN_waveOutOpen)pDevice->pContext->winmm.waveOutOpen)((MA_HWAVEOUT*)&pDevice->winmm.hDevicePlayback, winMMDeviceIDPlayback, &wf, (DWORD_PTR)pDevice->winmm.hEventPlayback, (DWORD_PTR)pDevice, MA_CALLBACK_EVENT | MA_WAVE_ALLOWSYNC); - if (resultMM != MA_MMSYSERR_NOERROR) { - errorMsg = "[WinMM] Failed to open playback device.", errorCode = MA_FAILED_TO_OPEN_BACKEND_DEVICE; - goto on_error; - } - - pDescriptorPlayback->format = ma_format_from_WAVEFORMATEX(&wf); - pDescriptorPlayback->channels = wf.nChannels; - pDescriptorPlayback->sampleRate = wf.nSamplesPerSec; - ma_channel_map_init_standard(ma_standard_channel_map_microsoft, pDescriptorPlayback->channelMap, ma_countof(pDescriptorPlayback->channelMap), pDescriptorPlayback->channels); - pDescriptorPlayback->periodCount = pDescriptorPlayback->periodCount; - pDescriptorPlayback->periodSizeInFrames = ma_calculate_period_size_in_frames_from_descriptor__winmm(pDescriptorPlayback, pDescriptorPlayback->sampleRate, pConfig->performanceProfile); - } - - /* - The heap allocated data is allocated like so: - - [Capture WAVEHDRs][Playback WAVEHDRs][Capture Intermediary Buffer][Playback Intermediary Buffer] - */ - heapSize = 0; - if (pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) { - heapSize += sizeof(MA_WAVEHDR)*pDescriptorCapture->periodCount + (pDescriptorCapture->periodSizeInFrames * pDescriptorCapture->periodCount * ma_get_bytes_per_frame(pDescriptorCapture->format, pDescriptorCapture->channels)); - } - if (pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) { - heapSize += sizeof(MA_WAVEHDR)*pDescriptorPlayback->periodCount + (pDescriptorPlayback->periodSizeInFrames * pDescriptorPlayback->periodCount * ma_get_bytes_per_frame(pDescriptorPlayback->format, pDescriptorPlayback->channels)); - } - - pDevice->winmm._pHeapData = (ma_uint8*)ma_calloc(heapSize, &pDevice->pContext->allocationCallbacks); - if (pDevice->winmm._pHeapData == NULL) { - errorMsg = "[WinMM] Failed to allocate memory for the intermediary buffer.", errorCode = MA_OUT_OF_MEMORY; - goto on_error; - } - - MA_ZERO_MEMORY(pDevice->winmm._pHeapData, heapSize); - - if (pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) { - ma_uint32 iPeriod; - - if (pConfig->deviceType == ma_device_type_capture) { - pDevice->winmm.pWAVEHDRCapture = pDevice->winmm._pHeapData; - pDevice->winmm.pIntermediaryBufferCapture = pDevice->winmm._pHeapData + (sizeof(MA_WAVEHDR)*(pDescriptorCapture->periodCount)); - } else { - pDevice->winmm.pWAVEHDRCapture = pDevice->winmm._pHeapData; - pDevice->winmm.pIntermediaryBufferCapture = pDevice->winmm._pHeapData + (sizeof(MA_WAVEHDR)*(pDescriptorCapture->periodCount + pDescriptorPlayback->periodCount)); - } - - /* Prepare headers. */ - for (iPeriod = 0; iPeriod < pDescriptorCapture->periodCount; ++iPeriod) { - ma_uint32 periodSizeInBytes = ma_get_period_size_in_bytes(pDescriptorCapture->periodSizeInFrames, pDescriptorCapture->format, pDescriptorCapture->channels); - - ((MA_WAVEHDR*)pDevice->winmm.pWAVEHDRCapture)[iPeriod].lpData = (char*)(pDevice->winmm.pIntermediaryBufferCapture + (periodSizeInBytes*iPeriod)); - ((MA_WAVEHDR*)pDevice->winmm.pWAVEHDRCapture)[iPeriod].dwBufferLength = periodSizeInBytes; - ((MA_WAVEHDR*)pDevice->winmm.pWAVEHDRCapture)[iPeriod].dwFlags = 0L; - ((MA_WAVEHDR*)pDevice->winmm.pWAVEHDRCapture)[iPeriod].dwLoops = 0L; - ((MA_PFN_waveInPrepareHeader)pDevice->pContext->winmm.waveInPrepareHeader)((MA_HWAVEIN)pDevice->winmm.hDeviceCapture, &((MA_WAVEHDR*)pDevice->winmm.pWAVEHDRCapture)[iPeriod], sizeof(MA_WAVEHDR)); - - /* - The user data of the MA_WAVEHDR structure is a single flag the controls whether or not it is ready for writing. Consider it to be named "isLocked". A value of 0 means - it's unlocked and available for writing. A value of 1 means it's locked. - */ - ((MA_WAVEHDR*)pDevice->winmm.pWAVEHDRCapture)[iPeriod].dwUser = 0; - } - } - - if (pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) { - ma_uint32 iPeriod; - - if (pConfig->deviceType == ma_device_type_playback) { - pDevice->winmm.pWAVEHDRPlayback = pDevice->winmm._pHeapData; - pDevice->winmm.pIntermediaryBufferPlayback = pDevice->winmm._pHeapData + (sizeof(MA_WAVEHDR)*pDescriptorPlayback->periodCount); - } else { - pDevice->winmm.pWAVEHDRPlayback = pDevice->winmm._pHeapData + (sizeof(MA_WAVEHDR)*(pDescriptorCapture->periodCount)); - pDevice->winmm.pIntermediaryBufferPlayback = pDevice->winmm._pHeapData + (sizeof(MA_WAVEHDR)*(pDescriptorCapture->periodCount + pDescriptorPlayback->periodCount)) + (pDescriptorCapture->periodSizeInFrames*pDescriptorCapture->periodCount*ma_get_bytes_per_frame(pDescriptorCapture->format, pDescriptorCapture->channels)); - } - - /* Prepare headers. */ - for (iPeriod = 0; iPeriod < pDescriptorPlayback->periodCount; ++iPeriod) { - ma_uint32 periodSizeInBytes = ma_get_period_size_in_bytes(pDescriptorPlayback->periodSizeInFrames, pDescriptorPlayback->format, pDescriptorPlayback->channels); - - ((MA_WAVEHDR*)pDevice->winmm.pWAVEHDRPlayback)[iPeriod].lpData = (char*)(pDevice->winmm.pIntermediaryBufferPlayback + (periodSizeInBytes*iPeriod)); - ((MA_WAVEHDR*)pDevice->winmm.pWAVEHDRPlayback)[iPeriod].dwBufferLength = periodSizeInBytes; - ((MA_WAVEHDR*)pDevice->winmm.pWAVEHDRPlayback)[iPeriod].dwFlags = 0L; - ((MA_WAVEHDR*)pDevice->winmm.pWAVEHDRPlayback)[iPeriod].dwLoops = 0L; - ((MA_PFN_waveOutPrepareHeader)pDevice->pContext->winmm.waveOutPrepareHeader)((MA_HWAVEOUT)pDevice->winmm.hDevicePlayback, &((MA_WAVEHDR*)pDevice->winmm.pWAVEHDRPlayback)[iPeriod], sizeof(MA_WAVEHDR)); - - /* - The user data of the MA_WAVEHDR structure is a single flag the controls whether or not it is ready for writing. Consider it to be named "isLocked". A value of 0 means - it's unlocked and available for writing. A value of 1 means it's locked. - */ - ((MA_WAVEHDR*)pDevice->winmm.pWAVEHDRPlayback)[iPeriod].dwUser = 0; - } - } - - return MA_SUCCESS; - -on_error: - if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) { - if (pDevice->winmm.pWAVEHDRCapture != NULL) { - ma_uint32 iPeriod; - for (iPeriod = 0; iPeriod < pDescriptorCapture->periodCount; ++iPeriod) { - ((MA_PFN_waveInUnprepareHeader)pDevice->pContext->winmm.waveInUnprepareHeader)((MA_HWAVEIN)pDevice->winmm.hDeviceCapture, &((MA_WAVEHDR*)pDevice->winmm.pWAVEHDRCapture)[iPeriod], sizeof(MA_WAVEHDR)); - } - } - - ((MA_PFN_waveInClose)pDevice->pContext->winmm.waveInClose)((MA_HWAVEIN)pDevice->winmm.hDeviceCapture); - } - - if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) { - if (pDevice->winmm.pWAVEHDRCapture != NULL) { - ma_uint32 iPeriod; - for (iPeriod = 0; iPeriod < pDescriptorPlayback->periodCount; ++iPeriod) { - ((MA_PFN_waveOutUnprepareHeader)pDevice->pContext->winmm.waveOutUnprepareHeader)((MA_HWAVEOUT)pDevice->winmm.hDevicePlayback, &((MA_WAVEHDR*)pDevice->winmm.pWAVEHDRPlayback)[iPeriod], sizeof(MA_WAVEHDR)); - } - } - - ((MA_PFN_waveOutClose)pDevice->pContext->winmm.waveOutClose)((MA_HWAVEOUT)pDevice->winmm.hDevicePlayback); - } - - ma_free(pDevice->winmm._pHeapData, &pDevice->pContext->allocationCallbacks); - - if (errorMsg != NULL && errorMsg[0] != '\0') { - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "%s", errorMsg); - } - - return errorCode; -} - -static ma_result ma_device_start__winmm(ma_device* pDevice) -{ - MA_ASSERT(pDevice != NULL); - - if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) { - MA_MMRESULT resultMM; - MA_WAVEHDR* pWAVEHDR; - ma_uint32 iPeriod; - - pWAVEHDR = (MA_WAVEHDR*)pDevice->winmm.pWAVEHDRCapture; - - /* Make sure the event is reset to a non-signaled state to ensure we don't prematurely return from WaitForSingleObject(). */ - ResetEvent((HANDLE)pDevice->winmm.hEventCapture); - - /* To start the device we attach all of the buffers and then start it. As the buffers are filled with data we will get notifications. */ - for (iPeriod = 0; iPeriod < pDevice->capture.internalPeriods; ++iPeriod) { - resultMM = ((MA_PFN_waveInAddBuffer)pDevice->pContext->winmm.waveInAddBuffer)((MA_HWAVEIN)pDevice->winmm.hDeviceCapture, &((MA_WAVEHDR*)pDevice->winmm.pWAVEHDRCapture)[iPeriod], sizeof(MA_WAVEHDR)); - if (resultMM != MA_MMSYSERR_NOERROR) { - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[WinMM] Failed to attach input buffers to capture device in preparation for capture."); - return ma_result_from_MMRESULT(resultMM); - } - - /* Make sure all of the buffers start out locked. We don't want to access them until the backend tells us we can. */ - pWAVEHDR[iPeriod].dwUser = 1; /* 1 = locked. */ - } - - /* Capture devices need to be explicitly started, unlike playback devices. */ - resultMM = ((MA_PFN_waveInStart)pDevice->pContext->winmm.waveInStart)((MA_HWAVEIN)pDevice->winmm.hDeviceCapture); - if (resultMM != MA_MMSYSERR_NOERROR) { - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[WinMM] Failed to start backend device."); - return ma_result_from_MMRESULT(resultMM); - } - } - - if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) { - /* Don't need to do anything for playback. It'll be started automatically in ma_device_start__winmm(). */ - } - - return MA_SUCCESS; -} - -static ma_result ma_device_stop__winmm(ma_device* pDevice) -{ - MA_MMRESULT resultMM; - - MA_ASSERT(pDevice != NULL); - - if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) { - if (pDevice->winmm.hDeviceCapture == NULL) { - return MA_INVALID_ARGS; - } - - resultMM = ((MA_PFN_waveInReset)pDevice->pContext->winmm.waveInReset)((MA_HWAVEIN)pDevice->winmm.hDeviceCapture); - if (resultMM != MA_MMSYSERR_NOERROR) { - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_WARNING, "[WinMM] WARNING: Failed to reset capture device."); - } - } - - if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) { - ma_uint32 iPeriod; - MA_WAVEHDR* pWAVEHDR; - - if (pDevice->winmm.hDevicePlayback == NULL) { - return MA_INVALID_ARGS; - } - - /* We need to drain the device. To do this we just loop over each header and if it's locked just wait for the event. */ - pWAVEHDR = (MA_WAVEHDR*)pDevice->winmm.pWAVEHDRPlayback; - for (iPeriod = 0; iPeriod < pDevice->playback.internalPeriods; iPeriod += 1) { - if (pWAVEHDR[iPeriod].dwUser == 1) { /* 1 = locked. */ - if (WaitForSingleObject((HANDLE)pDevice->winmm.hEventPlayback, INFINITE) != WAIT_OBJECT_0) { - break; /* An error occurred so just abandon ship and stop the device without draining. */ - } - - pWAVEHDR[iPeriod].dwUser = 0; - } - } - - resultMM = ((MA_PFN_waveOutReset)pDevice->pContext->winmm.waveOutReset)((MA_HWAVEOUT)pDevice->winmm.hDevicePlayback); - if (resultMM != MA_MMSYSERR_NOERROR) { - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_WARNING, "[WinMM] WARNING: Failed to reset playback device."); - } - } - - return MA_SUCCESS; -} - -static ma_result ma_device_write__winmm(ma_device* pDevice, const void* pPCMFrames, ma_uint32 frameCount, ma_uint32* pFramesWritten) -{ - ma_result result = MA_SUCCESS; - MA_MMRESULT resultMM; - ma_uint32 totalFramesWritten; - MA_WAVEHDR* pWAVEHDR; - - MA_ASSERT(pDevice != NULL); - MA_ASSERT(pPCMFrames != NULL); - - if (pFramesWritten != NULL) { - *pFramesWritten = 0; - } - - pWAVEHDR = (MA_WAVEHDR*)pDevice->winmm.pWAVEHDRPlayback; - - /* Keep processing as much data as possible. */ - totalFramesWritten = 0; - while (totalFramesWritten < frameCount) { - /* If the current header has some space available we need to write part of it. */ - if (pWAVEHDR[pDevice->winmm.iNextHeaderPlayback].dwUser == 0) { /* 0 = unlocked. */ - /* - This header has room in it. We copy as much of it as we can. If we end up fully consuming the buffer we need to - write it out and move on to the next iteration. - */ - ma_uint32 bpf = ma_get_bytes_per_frame(pDevice->playback.internalFormat, pDevice->playback.internalChannels); - ma_uint32 framesRemainingInHeader = (pWAVEHDR[pDevice->winmm.iNextHeaderPlayback].dwBufferLength/bpf) - pDevice->winmm.headerFramesConsumedPlayback; - - ma_uint32 framesToCopy = ma_min(framesRemainingInHeader, (frameCount - totalFramesWritten)); - const void* pSrc = ma_offset_ptr(pPCMFrames, totalFramesWritten*bpf); - void* pDst = ma_offset_ptr(pWAVEHDR[pDevice->winmm.iNextHeaderPlayback].lpData, pDevice->winmm.headerFramesConsumedPlayback*bpf); - MA_COPY_MEMORY(pDst, pSrc, framesToCopy*bpf); - - pDevice->winmm.headerFramesConsumedPlayback += framesToCopy; - totalFramesWritten += framesToCopy; - - /* If we've consumed the buffer entirely we need to write it out to the device. */ - if (pDevice->winmm.headerFramesConsumedPlayback == (pWAVEHDR[pDevice->winmm.iNextHeaderPlayback].dwBufferLength/bpf)) { - pWAVEHDR[pDevice->winmm.iNextHeaderPlayback].dwUser = 1; /* 1 = locked. */ - pWAVEHDR[pDevice->winmm.iNextHeaderPlayback].dwFlags &= ~MA_WHDR_DONE; /* <-- Need to make sure the WHDR_DONE flag is unset. */ - - /* Make sure the event is reset to a non-signaled state to ensure we don't prematurely return from WaitForSingleObject(). */ - ResetEvent((HANDLE)pDevice->winmm.hEventPlayback); - - /* The device will be started here. */ - resultMM = ((MA_PFN_waveOutWrite)pDevice->pContext->winmm.waveOutWrite)((MA_HWAVEOUT)pDevice->winmm.hDevicePlayback, &pWAVEHDR[pDevice->winmm.iNextHeaderPlayback], sizeof(MA_WAVEHDR)); - if (resultMM != MA_MMSYSERR_NOERROR) { - result = ma_result_from_MMRESULT(resultMM); - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[WinMM] waveOutWrite() failed."); - break; - } - - /* Make sure we move to the next header. */ - pDevice->winmm.iNextHeaderPlayback = (pDevice->winmm.iNextHeaderPlayback + 1) % pDevice->playback.internalPeriods; - pDevice->winmm.headerFramesConsumedPlayback = 0; - } - - /* If at this point we have consumed the entire input buffer we can return. */ - MA_ASSERT(totalFramesWritten <= frameCount); - if (totalFramesWritten == frameCount) { - break; - } - - /* Getting here means there's more to process. */ - continue; - } - - /* Getting here means there isn't enough room in the buffer and we need to wait for one to become available. */ - if (WaitForSingleObject((HANDLE)pDevice->winmm.hEventPlayback, INFINITE) != WAIT_OBJECT_0) { - result = MA_ERROR; - break; - } - - /* Something happened. If the next buffer has been marked as done we need to reset a bit of state. */ - if ((pWAVEHDR[pDevice->winmm.iNextHeaderPlayback].dwFlags & MA_WHDR_DONE) != 0) { - pWAVEHDR[pDevice->winmm.iNextHeaderPlayback].dwUser = 0; /* 0 = unlocked (make it available for writing). */ - pDevice->winmm.headerFramesConsumedPlayback = 0; - } - - /* If the device has been stopped we need to break. */ - if (ma_device_get_state(pDevice) != ma_device_state_started) { - break; - } - } - - if (pFramesWritten != NULL) { - *pFramesWritten = totalFramesWritten; - } - - return result; -} - -static ma_result ma_device_read__winmm(ma_device* pDevice, void* pPCMFrames, ma_uint32 frameCount, ma_uint32* pFramesRead) -{ - ma_result result = MA_SUCCESS; - MA_MMRESULT resultMM; - ma_uint32 totalFramesRead; - MA_WAVEHDR* pWAVEHDR; - - MA_ASSERT(pDevice != NULL); - MA_ASSERT(pPCMFrames != NULL); - - if (pFramesRead != NULL) { - *pFramesRead = 0; - } - - pWAVEHDR = (MA_WAVEHDR*)pDevice->winmm.pWAVEHDRCapture; - - /* Keep processing as much data as possible. */ - totalFramesRead = 0; - while (totalFramesRead < frameCount) { - /* If the current header has some space available we need to write part of it. */ - if (pWAVEHDR[pDevice->winmm.iNextHeaderCapture].dwUser == 0) { /* 0 = unlocked. */ - /* The buffer is available for reading. If we fully consume it we need to add it back to the buffer. */ - ma_uint32 bpf = ma_get_bytes_per_frame(pDevice->capture.internalFormat, pDevice->capture.internalChannels); - ma_uint32 framesRemainingInHeader = (pWAVEHDR[pDevice->winmm.iNextHeaderCapture].dwBufferLength/bpf) - pDevice->winmm.headerFramesConsumedCapture; - - ma_uint32 framesToCopy = ma_min(framesRemainingInHeader, (frameCount - totalFramesRead)); - const void* pSrc = ma_offset_ptr(pWAVEHDR[pDevice->winmm.iNextHeaderCapture].lpData, pDevice->winmm.headerFramesConsumedCapture*bpf); - void* pDst = ma_offset_ptr(pPCMFrames, totalFramesRead*bpf); - MA_COPY_MEMORY(pDst, pSrc, framesToCopy*bpf); - - pDevice->winmm.headerFramesConsumedCapture += framesToCopy; - totalFramesRead += framesToCopy; - - /* If we've consumed the buffer entirely we need to add it back to the device. */ - if (pDevice->winmm.headerFramesConsumedCapture == (pWAVEHDR[pDevice->winmm.iNextHeaderCapture].dwBufferLength/bpf)) { - pWAVEHDR[pDevice->winmm.iNextHeaderCapture].dwUser = 1; /* 1 = locked. */ - pWAVEHDR[pDevice->winmm.iNextHeaderCapture].dwFlags &= ~MA_WHDR_DONE; /* <-- Need to make sure the WHDR_DONE flag is unset. */ - - /* Make sure the event is reset to a non-signaled state to ensure we don't prematurely return from WaitForSingleObject(). */ - ResetEvent((HANDLE)pDevice->winmm.hEventCapture); - - /* The device will be started here. */ - resultMM = ((MA_PFN_waveInAddBuffer)pDevice->pContext->winmm.waveInAddBuffer)((MA_HWAVEIN)pDevice->winmm.hDeviceCapture, &((MA_WAVEHDR*)pDevice->winmm.pWAVEHDRCapture)[pDevice->winmm.iNextHeaderCapture], sizeof(MA_WAVEHDR)); - if (resultMM != MA_MMSYSERR_NOERROR) { - result = ma_result_from_MMRESULT(resultMM); - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[WinMM] waveInAddBuffer() failed."); - break; - } - - /* Make sure we move to the next header. */ - pDevice->winmm.iNextHeaderCapture = (pDevice->winmm.iNextHeaderCapture + 1) % pDevice->capture.internalPeriods; - pDevice->winmm.headerFramesConsumedCapture = 0; - } - - /* If at this point we have filled the entire input buffer we can return. */ - MA_ASSERT(totalFramesRead <= frameCount); - if (totalFramesRead == frameCount) { - break; - } - - /* Getting here means there's more to process. */ - continue; - } - - /* Getting here means there isn't enough any data left to send to the client which means we need to wait for more. */ - if (WaitForSingleObject((HANDLE)pDevice->winmm.hEventCapture, INFINITE) != WAIT_OBJECT_0) { - result = MA_ERROR; - break; - } - - /* Something happened. If the next buffer has been marked as done we need to reset a bit of state. */ - if ((pWAVEHDR[pDevice->winmm.iNextHeaderCapture].dwFlags & MA_WHDR_DONE) != 0) { - pWAVEHDR[pDevice->winmm.iNextHeaderCapture].dwUser = 0; /* 0 = unlocked (make it available for reading). */ - pDevice->winmm.headerFramesConsumedCapture = 0; - } - - /* If the device has been stopped we need to break. */ - if (ma_device_get_state(pDevice) != ma_device_state_started) { - break; - } - } - - if (pFramesRead != NULL) { - *pFramesRead = totalFramesRead; - } - - return result; -} - -static ma_result ma_context_uninit__winmm(ma_context* pContext) -{ - MA_ASSERT(pContext != NULL); - MA_ASSERT(pContext->backend == ma_backend_winmm); - - ma_dlclose(ma_context_get_log(pContext), pContext->winmm.hWinMM); - return MA_SUCCESS; -} - -static ma_result ma_context_init__winmm(ma_context* pContext, const ma_context_config* pConfig, ma_backend_callbacks* pCallbacks) -{ - MA_ASSERT(pContext != NULL); - - (void)pConfig; - - pContext->winmm.hWinMM = ma_dlopen(ma_context_get_log(pContext), "winmm.dll"); - if (pContext->winmm.hWinMM == NULL) { - return MA_NO_BACKEND; - } - - pContext->winmm.waveOutGetNumDevs = ma_dlsym(ma_context_get_log(pContext), pContext->winmm.hWinMM, "waveOutGetNumDevs"); - pContext->winmm.waveOutGetDevCapsA = ma_dlsym(ma_context_get_log(pContext), pContext->winmm.hWinMM, "waveOutGetDevCapsA"); - pContext->winmm.waveOutOpen = ma_dlsym(ma_context_get_log(pContext), pContext->winmm.hWinMM, "waveOutOpen"); - pContext->winmm.waveOutClose = ma_dlsym(ma_context_get_log(pContext), pContext->winmm.hWinMM, "waveOutClose"); - pContext->winmm.waveOutPrepareHeader = ma_dlsym(ma_context_get_log(pContext), pContext->winmm.hWinMM, "waveOutPrepareHeader"); - pContext->winmm.waveOutUnprepareHeader = ma_dlsym(ma_context_get_log(pContext), pContext->winmm.hWinMM, "waveOutUnprepareHeader"); - pContext->winmm.waveOutWrite = ma_dlsym(ma_context_get_log(pContext), pContext->winmm.hWinMM, "waveOutWrite"); - pContext->winmm.waveOutReset = ma_dlsym(ma_context_get_log(pContext), pContext->winmm.hWinMM, "waveOutReset"); - pContext->winmm.waveInGetNumDevs = ma_dlsym(ma_context_get_log(pContext), pContext->winmm.hWinMM, "waveInGetNumDevs"); - pContext->winmm.waveInGetDevCapsA = ma_dlsym(ma_context_get_log(pContext), pContext->winmm.hWinMM, "waveInGetDevCapsA"); - pContext->winmm.waveInOpen = ma_dlsym(ma_context_get_log(pContext), pContext->winmm.hWinMM, "waveInOpen"); - pContext->winmm.waveInClose = ma_dlsym(ma_context_get_log(pContext), pContext->winmm.hWinMM, "waveInClose"); - pContext->winmm.waveInPrepareHeader = ma_dlsym(ma_context_get_log(pContext), pContext->winmm.hWinMM, "waveInPrepareHeader"); - pContext->winmm.waveInUnprepareHeader = ma_dlsym(ma_context_get_log(pContext), pContext->winmm.hWinMM, "waveInUnprepareHeader"); - pContext->winmm.waveInAddBuffer = ma_dlsym(ma_context_get_log(pContext), pContext->winmm.hWinMM, "waveInAddBuffer"); - pContext->winmm.waveInStart = ma_dlsym(ma_context_get_log(pContext), pContext->winmm.hWinMM, "waveInStart"); - pContext->winmm.waveInReset = ma_dlsym(ma_context_get_log(pContext), pContext->winmm.hWinMM, "waveInReset"); - - pCallbacks->onContextInit = ma_context_init__winmm; - pCallbacks->onContextUninit = ma_context_uninit__winmm; - pCallbacks->onContextEnumerateDevices = ma_context_enumerate_devices__winmm; - pCallbacks->onContextGetDeviceInfo = ma_context_get_device_info__winmm; - pCallbacks->onDeviceInit = ma_device_init__winmm; - pCallbacks->onDeviceUninit = ma_device_uninit__winmm; - pCallbacks->onDeviceStart = ma_device_start__winmm; - pCallbacks->onDeviceStop = ma_device_stop__winmm; - pCallbacks->onDeviceRead = ma_device_read__winmm; - pCallbacks->onDeviceWrite = ma_device_write__winmm; - pCallbacks->onDeviceDataLoop = NULL; /* This is a blocking read-write API, so this can be NULL since miniaudio will manage the audio thread for us. */ - - return MA_SUCCESS; -} -#endif - - - - -/****************************************************************************** - -ALSA Backend - -******************************************************************************/ -#ifdef MA_HAS_ALSA - -#include /* poll(), struct pollfd */ -#include /* eventfd() */ - -#ifdef MA_NO_RUNTIME_LINKING - -/* asoundlib.h marks some functions with "inline" which isn't always supported. Need to emulate it. */ -#if !defined(__cplusplus) - #if defined(__STRICT_ANSI__) - #if !defined(inline) - #define inline __inline__ __attribute__((always_inline)) - #define MA_INLINE_DEFINED - #endif - #endif -#endif -#include -#if defined(MA_INLINE_DEFINED) - #undef inline - #undef MA_INLINE_DEFINED -#endif - -typedef snd_pcm_uframes_t ma_snd_pcm_uframes_t; -typedef snd_pcm_sframes_t ma_snd_pcm_sframes_t; -typedef snd_pcm_stream_t ma_snd_pcm_stream_t; -typedef snd_pcm_format_t ma_snd_pcm_format_t; -typedef snd_pcm_access_t ma_snd_pcm_access_t; -typedef snd_pcm_t ma_snd_pcm_t; -typedef snd_pcm_hw_params_t ma_snd_pcm_hw_params_t; -typedef snd_pcm_sw_params_t ma_snd_pcm_sw_params_t; -typedef snd_pcm_format_mask_t ma_snd_pcm_format_mask_t; -typedef snd_pcm_info_t ma_snd_pcm_info_t; -typedef snd_pcm_channel_area_t ma_snd_pcm_channel_area_t; -typedef snd_pcm_chmap_t ma_snd_pcm_chmap_t; -typedef snd_pcm_state_t ma_snd_pcm_state_t; - -/* snd_pcm_stream_t */ -#define MA_SND_PCM_STREAM_PLAYBACK SND_PCM_STREAM_PLAYBACK -#define MA_SND_PCM_STREAM_CAPTURE SND_PCM_STREAM_CAPTURE - -/* snd_pcm_format_t */ -#define MA_SND_PCM_FORMAT_UNKNOWN SND_PCM_FORMAT_UNKNOWN -#define MA_SND_PCM_FORMAT_U8 SND_PCM_FORMAT_U8 -#define MA_SND_PCM_FORMAT_S16_LE SND_PCM_FORMAT_S16_LE -#define MA_SND_PCM_FORMAT_S16_BE SND_PCM_FORMAT_S16_BE -#define MA_SND_PCM_FORMAT_S24_LE SND_PCM_FORMAT_S24_LE -#define MA_SND_PCM_FORMAT_S24_BE SND_PCM_FORMAT_S24_BE -#define MA_SND_PCM_FORMAT_S32_LE SND_PCM_FORMAT_S32_LE -#define MA_SND_PCM_FORMAT_S32_BE SND_PCM_FORMAT_S32_BE -#define MA_SND_PCM_FORMAT_FLOAT_LE SND_PCM_FORMAT_FLOAT_LE -#define MA_SND_PCM_FORMAT_FLOAT_BE SND_PCM_FORMAT_FLOAT_BE -#define MA_SND_PCM_FORMAT_FLOAT64_LE SND_PCM_FORMAT_FLOAT64_LE -#define MA_SND_PCM_FORMAT_FLOAT64_BE SND_PCM_FORMAT_FLOAT64_BE -#define MA_SND_PCM_FORMAT_MU_LAW SND_PCM_FORMAT_MU_LAW -#define MA_SND_PCM_FORMAT_A_LAW SND_PCM_FORMAT_A_LAW -#define MA_SND_PCM_FORMAT_S24_3LE SND_PCM_FORMAT_S24_3LE -#define MA_SND_PCM_FORMAT_S24_3BE SND_PCM_FORMAT_S24_3BE - -/* ma_snd_pcm_access_t */ -#define MA_SND_PCM_ACCESS_MMAP_INTERLEAVED SND_PCM_ACCESS_MMAP_INTERLEAVED -#define MA_SND_PCM_ACCESS_MMAP_NONINTERLEAVED SND_PCM_ACCESS_MMAP_NONINTERLEAVED -#define MA_SND_PCM_ACCESS_MMAP_COMPLEX SND_PCM_ACCESS_MMAP_COMPLEX -#define MA_SND_PCM_ACCESS_RW_INTERLEAVED SND_PCM_ACCESS_RW_INTERLEAVED -#define MA_SND_PCM_ACCESS_RW_NONINTERLEAVED SND_PCM_ACCESS_RW_NONINTERLEAVED - -/* Channel positions. */ -#define MA_SND_CHMAP_UNKNOWN SND_CHMAP_UNKNOWN -#define MA_SND_CHMAP_NA SND_CHMAP_NA -#define MA_SND_CHMAP_MONO SND_CHMAP_MONO -#define MA_SND_CHMAP_FL SND_CHMAP_FL -#define MA_SND_CHMAP_FR SND_CHMAP_FR -#define MA_SND_CHMAP_RL SND_CHMAP_RL -#define MA_SND_CHMAP_RR SND_CHMAP_RR -#define MA_SND_CHMAP_FC SND_CHMAP_FC -#define MA_SND_CHMAP_LFE SND_CHMAP_LFE -#define MA_SND_CHMAP_SL SND_CHMAP_SL -#define MA_SND_CHMAP_SR SND_CHMAP_SR -#define MA_SND_CHMAP_RC SND_CHMAP_RC -#define MA_SND_CHMAP_FLC SND_CHMAP_FLC -#define MA_SND_CHMAP_FRC SND_CHMAP_FRC -#define MA_SND_CHMAP_RLC SND_CHMAP_RLC -#define MA_SND_CHMAP_RRC SND_CHMAP_RRC -#define MA_SND_CHMAP_FLW SND_CHMAP_FLW -#define MA_SND_CHMAP_FRW SND_CHMAP_FRW -#define MA_SND_CHMAP_FLH SND_CHMAP_FLH -#define MA_SND_CHMAP_FCH SND_CHMAP_FCH -#define MA_SND_CHMAP_FRH SND_CHMAP_FRH -#define MA_SND_CHMAP_TC SND_CHMAP_TC -#define MA_SND_CHMAP_TFL SND_CHMAP_TFL -#define MA_SND_CHMAP_TFR SND_CHMAP_TFR -#define MA_SND_CHMAP_TFC SND_CHMAP_TFC -#define MA_SND_CHMAP_TRL SND_CHMAP_TRL -#define MA_SND_CHMAP_TRR SND_CHMAP_TRR -#define MA_SND_CHMAP_TRC SND_CHMAP_TRC -#define MA_SND_CHMAP_TFLC SND_CHMAP_TFLC -#define MA_SND_CHMAP_TFRC SND_CHMAP_TFRC -#define MA_SND_CHMAP_TSL SND_CHMAP_TSL -#define MA_SND_CHMAP_TSR SND_CHMAP_TSR -#define MA_SND_CHMAP_LLFE SND_CHMAP_LLFE -#define MA_SND_CHMAP_RLFE SND_CHMAP_RLFE -#define MA_SND_CHMAP_BC SND_CHMAP_BC -#define MA_SND_CHMAP_BLC SND_CHMAP_BLC -#define MA_SND_CHMAP_BRC SND_CHMAP_BRC - -/* Open mode flags. */ -#define MA_SND_PCM_NO_AUTO_RESAMPLE SND_PCM_NO_AUTO_RESAMPLE -#define MA_SND_PCM_NO_AUTO_CHANNELS SND_PCM_NO_AUTO_CHANNELS -#define MA_SND_PCM_NO_AUTO_FORMAT SND_PCM_NO_AUTO_FORMAT -#else -#include /* For EPIPE, etc. */ -typedef unsigned long ma_snd_pcm_uframes_t; -typedef long ma_snd_pcm_sframes_t; -typedef int ma_snd_pcm_stream_t; -typedef int ma_snd_pcm_format_t; -typedef int ma_snd_pcm_access_t; -typedef int ma_snd_pcm_state_t; -typedef struct ma_snd_pcm_t ma_snd_pcm_t; -typedef struct ma_snd_pcm_hw_params_t ma_snd_pcm_hw_params_t; -typedef struct ma_snd_pcm_sw_params_t ma_snd_pcm_sw_params_t; -typedef struct ma_snd_pcm_format_mask_t ma_snd_pcm_format_mask_t; -typedef struct ma_snd_pcm_info_t ma_snd_pcm_info_t; -typedef struct -{ - void* addr; - unsigned int first; - unsigned int step; -} ma_snd_pcm_channel_area_t; -typedef struct -{ - unsigned int channels; - unsigned int pos[1]; -} ma_snd_pcm_chmap_t; - -/* snd_pcm_state_t */ -#define MA_SND_PCM_STATE_OPEN 0 -#define MA_SND_PCM_STATE_SETUP 1 -#define MA_SND_PCM_STATE_PREPARED 2 -#define MA_SND_PCM_STATE_RUNNING 3 -#define MA_SND_PCM_STATE_XRUN 4 -#define MA_SND_PCM_STATE_DRAINING 5 -#define MA_SND_PCM_STATE_PAUSED 6 -#define MA_SND_PCM_STATE_SUSPENDED 7 -#define MA_SND_PCM_STATE_DISCONNECTED 8 - -/* snd_pcm_stream_t */ -#define MA_SND_PCM_STREAM_PLAYBACK 0 -#define MA_SND_PCM_STREAM_CAPTURE 1 - -/* snd_pcm_format_t */ -#define MA_SND_PCM_FORMAT_UNKNOWN -1 -#define MA_SND_PCM_FORMAT_U8 1 -#define MA_SND_PCM_FORMAT_S16_LE 2 -#define MA_SND_PCM_FORMAT_S16_BE 3 -#define MA_SND_PCM_FORMAT_S24_LE 6 -#define MA_SND_PCM_FORMAT_S24_BE 7 -#define MA_SND_PCM_FORMAT_S32_LE 10 -#define MA_SND_PCM_FORMAT_S32_BE 11 -#define MA_SND_PCM_FORMAT_FLOAT_LE 14 -#define MA_SND_PCM_FORMAT_FLOAT_BE 15 -#define MA_SND_PCM_FORMAT_FLOAT64_LE 16 -#define MA_SND_PCM_FORMAT_FLOAT64_BE 17 -#define MA_SND_PCM_FORMAT_MU_LAW 20 -#define MA_SND_PCM_FORMAT_A_LAW 21 -#define MA_SND_PCM_FORMAT_S24_3LE 32 -#define MA_SND_PCM_FORMAT_S24_3BE 33 - -/* snd_pcm_access_t */ -#define MA_SND_PCM_ACCESS_MMAP_INTERLEAVED 0 -#define MA_SND_PCM_ACCESS_MMAP_NONINTERLEAVED 1 -#define MA_SND_PCM_ACCESS_MMAP_COMPLEX 2 -#define MA_SND_PCM_ACCESS_RW_INTERLEAVED 3 -#define MA_SND_PCM_ACCESS_RW_NONINTERLEAVED 4 - -/* Channel positions. */ -#define MA_SND_CHMAP_UNKNOWN 0 -#define MA_SND_CHMAP_NA 1 -#define MA_SND_CHMAP_MONO 2 -#define MA_SND_CHMAP_FL 3 -#define MA_SND_CHMAP_FR 4 -#define MA_SND_CHMAP_RL 5 -#define MA_SND_CHMAP_RR 6 -#define MA_SND_CHMAP_FC 7 -#define MA_SND_CHMAP_LFE 8 -#define MA_SND_CHMAP_SL 9 -#define MA_SND_CHMAP_SR 10 -#define MA_SND_CHMAP_RC 11 -#define MA_SND_CHMAP_FLC 12 -#define MA_SND_CHMAP_FRC 13 -#define MA_SND_CHMAP_RLC 14 -#define MA_SND_CHMAP_RRC 15 -#define MA_SND_CHMAP_FLW 16 -#define MA_SND_CHMAP_FRW 17 -#define MA_SND_CHMAP_FLH 18 -#define MA_SND_CHMAP_FCH 19 -#define MA_SND_CHMAP_FRH 20 -#define MA_SND_CHMAP_TC 21 -#define MA_SND_CHMAP_TFL 22 -#define MA_SND_CHMAP_TFR 23 -#define MA_SND_CHMAP_TFC 24 -#define MA_SND_CHMAP_TRL 25 -#define MA_SND_CHMAP_TRR 26 -#define MA_SND_CHMAP_TRC 27 -#define MA_SND_CHMAP_TFLC 28 -#define MA_SND_CHMAP_TFRC 29 -#define MA_SND_CHMAP_TSL 30 -#define MA_SND_CHMAP_TSR 31 -#define MA_SND_CHMAP_LLFE 32 -#define MA_SND_CHMAP_RLFE 33 -#define MA_SND_CHMAP_BC 34 -#define MA_SND_CHMAP_BLC 35 -#define MA_SND_CHMAP_BRC 36 - -/* Open mode flags. */ -#define MA_SND_PCM_NO_AUTO_RESAMPLE 0x00010000 -#define MA_SND_PCM_NO_AUTO_CHANNELS 0x00020000 -#define MA_SND_PCM_NO_AUTO_FORMAT 0x00040000 -#endif - -typedef int (* ma_snd_pcm_open_proc) (ma_snd_pcm_t **pcm, const char *name, ma_snd_pcm_stream_t stream, int mode); -typedef int (* ma_snd_pcm_close_proc) (ma_snd_pcm_t *pcm); -typedef size_t (* ma_snd_pcm_hw_params_sizeof_proc) (void); -typedef int (* ma_snd_pcm_hw_params_any_proc) (ma_snd_pcm_t *pcm, ma_snd_pcm_hw_params_t *params); -typedef int (* ma_snd_pcm_hw_params_set_format_proc) (ma_snd_pcm_t *pcm, ma_snd_pcm_hw_params_t *params, ma_snd_pcm_format_t val); -typedef int (* ma_snd_pcm_hw_params_set_format_first_proc) (ma_snd_pcm_t *pcm, ma_snd_pcm_hw_params_t *params, ma_snd_pcm_format_t *format); -typedef void (* ma_snd_pcm_hw_params_get_format_mask_proc) (ma_snd_pcm_hw_params_t *params, ma_snd_pcm_format_mask_t *mask); -typedef int (* ma_snd_pcm_hw_params_set_channels_proc) (ma_snd_pcm_t *pcm, ma_snd_pcm_hw_params_t *params, unsigned int val); -typedef int (* ma_snd_pcm_hw_params_set_channels_near_proc) (ma_snd_pcm_t *pcm, ma_snd_pcm_hw_params_t *params, unsigned int *val); -typedef int (* ma_snd_pcm_hw_params_set_channels_minmax_proc) (ma_snd_pcm_t *pcm, ma_snd_pcm_hw_params_t *params, unsigned int *minimum, unsigned int *maximum); -typedef int (* ma_snd_pcm_hw_params_set_rate_resample_proc) (ma_snd_pcm_t *pcm, ma_snd_pcm_hw_params_t *params, unsigned int val); -typedef int (* ma_snd_pcm_hw_params_set_rate_proc) (ma_snd_pcm_t *pcm, ma_snd_pcm_hw_params_t *params, unsigned int val, int dir); -typedef int (* ma_snd_pcm_hw_params_set_rate_near_proc) (ma_snd_pcm_t *pcm, ma_snd_pcm_hw_params_t *params, unsigned int *val, int *dir); -typedef int (* ma_snd_pcm_hw_params_set_buffer_size_near_proc)(ma_snd_pcm_t *pcm, ma_snd_pcm_hw_params_t *params, ma_snd_pcm_uframes_t *val); -typedef int (* ma_snd_pcm_hw_params_set_periods_near_proc) (ma_snd_pcm_t *pcm, ma_snd_pcm_hw_params_t *params, unsigned int *val, int *dir); -typedef int (* ma_snd_pcm_hw_params_set_access_proc) (ma_snd_pcm_t *pcm, ma_snd_pcm_hw_params_t *params, ma_snd_pcm_access_t _access); -typedef int (* ma_snd_pcm_hw_params_get_format_proc) (const ma_snd_pcm_hw_params_t *params, ma_snd_pcm_format_t *format); -typedef int (* ma_snd_pcm_hw_params_get_channels_proc) (const ma_snd_pcm_hw_params_t *params, unsigned int *val); -typedef int (* ma_snd_pcm_hw_params_get_channels_min_proc) (const ma_snd_pcm_hw_params_t *params, unsigned int *val); -typedef int (* ma_snd_pcm_hw_params_get_channels_max_proc) (const ma_snd_pcm_hw_params_t *params, unsigned int *val); -typedef int (* ma_snd_pcm_hw_params_get_rate_proc) (const ma_snd_pcm_hw_params_t *params, unsigned int *rate, int *dir); -typedef int (* ma_snd_pcm_hw_params_get_rate_min_proc) (const ma_snd_pcm_hw_params_t *params, unsigned int *rate, int *dir); -typedef int (* ma_snd_pcm_hw_params_get_rate_max_proc) (const ma_snd_pcm_hw_params_t *params, unsigned int *rate, int *dir); -typedef int (* ma_snd_pcm_hw_params_get_buffer_size_proc) (const ma_snd_pcm_hw_params_t *params, ma_snd_pcm_uframes_t *val); -typedef int (* ma_snd_pcm_hw_params_get_periods_proc) (const ma_snd_pcm_hw_params_t *params, unsigned int *val, int *dir); -typedef int (* ma_snd_pcm_hw_params_get_access_proc) (const ma_snd_pcm_hw_params_t *params, ma_snd_pcm_access_t *_access); -typedef int (* ma_snd_pcm_hw_params_test_format_proc) (ma_snd_pcm_t *pcm, ma_snd_pcm_hw_params_t *params, ma_snd_pcm_format_t val); -typedef int (* ma_snd_pcm_hw_params_test_channels_proc) (ma_snd_pcm_t *pcm, ma_snd_pcm_hw_params_t *params, unsigned int val); -typedef int (* ma_snd_pcm_hw_params_test_rate_proc) (ma_snd_pcm_t *pcm, ma_snd_pcm_hw_params_t *params, unsigned int val, int dir); -typedef int (* ma_snd_pcm_hw_params_proc) (ma_snd_pcm_t *pcm, ma_snd_pcm_hw_params_t *params); -typedef size_t (* ma_snd_pcm_sw_params_sizeof_proc) (void); -typedef int (* ma_snd_pcm_sw_params_current_proc) (ma_snd_pcm_t *pcm, ma_snd_pcm_sw_params_t *params); -typedef int (* ma_snd_pcm_sw_params_get_boundary_proc) (const ma_snd_pcm_sw_params_t *params, ma_snd_pcm_uframes_t* val); -typedef int (* ma_snd_pcm_sw_params_set_avail_min_proc) (ma_snd_pcm_t *pcm, ma_snd_pcm_sw_params_t *params, ma_snd_pcm_uframes_t val); -typedef int (* ma_snd_pcm_sw_params_set_start_threshold_proc) (ma_snd_pcm_t *pcm, ma_snd_pcm_sw_params_t *params, ma_snd_pcm_uframes_t val); -typedef int (* ma_snd_pcm_sw_params_set_stop_threshold_proc) (ma_snd_pcm_t *pcm, ma_snd_pcm_sw_params_t *params, ma_snd_pcm_uframes_t val); -typedef int (* ma_snd_pcm_sw_params_proc) (ma_snd_pcm_t *pcm, ma_snd_pcm_sw_params_t *params); -typedef size_t (* ma_snd_pcm_format_mask_sizeof_proc) (void); -typedef int (* ma_snd_pcm_format_mask_test_proc) (const ma_snd_pcm_format_mask_t *mask, ma_snd_pcm_format_t val); -typedef ma_snd_pcm_chmap_t * (* ma_snd_pcm_get_chmap_proc) (ma_snd_pcm_t *pcm); -typedef ma_snd_pcm_state_t (* ma_snd_pcm_state_proc) (ma_snd_pcm_t *pcm); -typedef int (* ma_snd_pcm_prepare_proc) (ma_snd_pcm_t *pcm); -typedef int (* ma_snd_pcm_start_proc) (ma_snd_pcm_t *pcm); -typedef int (* ma_snd_pcm_drop_proc) (ma_snd_pcm_t *pcm); -typedef int (* ma_snd_pcm_drain_proc) (ma_snd_pcm_t *pcm); -typedef int (* ma_snd_pcm_reset_proc) (ma_snd_pcm_t *pcm); -typedef int (* ma_snd_device_name_hint_proc) (int card, const char *iface, void ***hints); -typedef char * (* ma_snd_device_name_get_hint_proc) (const void *hint, const char *id); -typedef int (* ma_snd_card_get_index_proc) (const char *name); -typedef int (* ma_snd_device_name_free_hint_proc) (void **hints); -typedef int (* ma_snd_pcm_mmap_begin_proc) (ma_snd_pcm_t *pcm, const ma_snd_pcm_channel_area_t **areas, ma_snd_pcm_uframes_t *offset, ma_snd_pcm_uframes_t *frames); -typedef ma_snd_pcm_sframes_t (* ma_snd_pcm_mmap_commit_proc) (ma_snd_pcm_t *pcm, ma_snd_pcm_uframes_t offset, ma_snd_pcm_uframes_t frames); -typedef int (* ma_snd_pcm_recover_proc) (ma_snd_pcm_t *pcm, int err, int silent); -typedef ma_snd_pcm_sframes_t (* ma_snd_pcm_readi_proc) (ma_snd_pcm_t *pcm, void *buffer, ma_snd_pcm_uframes_t size); -typedef ma_snd_pcm_sframes_t (* ma_snd_pcm_writei_proc) (ma_snd_pcm_t *pcm, const void *buffer, ma_snd_pcm_uframes_t size); -typedef ma_snd_pcm_sframes_t (* ma_snd_pcm_avail_proc) (ma_snd_pcm_t *pcm); -typedef ma_snd_pcm_sframes_t (* ma_snd_pcm_avail_update_proc) (ma_snd_pcm_t *pcm); -typedef int (* ma_snd_pcm_wait_proc) (ma_snd_pcm_t *pcm, int timeout); -typedef int (* ma_snd_pcm_nonblock_proc) (ma_snd_pcm_t *pcm, int nonblock); -typedef int (* ma_snd_pcm_info_proc) (ma_snd_pcm_t *pcm, ma_snd_pcm_info_t* info); -typedef size_t (* ma_snd_pcm_info_sizeof_proc) (void); -typedef const char* (* ma_snd_pcm_info_get_name_proc) (const ma_snd_pcm_info_t* info); -typedef int (* ma_snd_pcm_poll_descriptors_proc) (ma_snd_pcm_t *pcm, struct pollfd *pfds, unsigned int space); -typedef int (* ma_snd_pcm_poll_descriptors_count_proc) (ma_snd_pcm_t *pcm); -typedef int (* ma_snd_pcm_poll_descriptors_revents_proc) (ma_snd_pcm_t *pcm, struct pollfd *pfds, unsigned int nfds, unsigned short *revents); -typedef int (* ma_snd_config_update_free_global_proc) (void); - -/* This array specifies each of the common devices that can be used for both playback and capture. */ -static const char* g_maCommonDeviceNamesALSA[] = { - "default", - "null", - "pulse", - "jack" -}; - -/* This array allows us to blacklist specific playback devices. */ -static const char* g_maBlacklistedPlaybackDeviceNamesALSA[] = { - "" -}; - -/* This array allows us to blacklist specific capture devices. */ -static const char* g_maBlacklistedCaptureDeviceNamesALSA[] = { - "" -}; - - -static ma_snd_pcm_format_t ma_convert_ma_format_to_alsa_format(ma_format format) -{ - ma_snd_pcm_format_t ALSAFormats[] = { - MA_SND_PCM_FORMAT_UNKNOWN, /* ma_format_unknown */ - MA_SND_PCM_FORMAT_U8, /* ma_format_u8 */ - MA_SND_PCM_FORMAT_S16_LE, /* ma_format_s16 */ - MA_SND_PCM_FORMAT_S24_3LE, /* ma_format_s24 */ - MA_SND_PCM_FORMAT_S32_LE, /* ma_format_s32 */ - MA_SND_PCM_FORMAT_FLOAT_LE /* ma_format_f32 */ - }; - - if (ma_is_big_endian()) { - ALSAFormats[0] = MA_SND_PCM_FORMAT_UNKNOWN; - ALSAFormats[1] = MA_SND_PCM_FORMAT_U8; - ALSAFormats[2] = MA_SND_PCM_FORMAT_S16_BE; - ALSAFormats[3] = MA_SND_PCM_FORMAT_S24_3BE; - ALSAFormats[4] = MA_SND_PCM_FORMAT_S32_BE; - ALSAFormats[5] = MA_SND_PCM_FORMAT_FLOAT_BE; - } - - return ALSAFormats[format]; -} - -static ma_format ma_format_from_alsa(ma_snd_pcm_format_t formatALSA) -{ - if (ma_is_little_endian()) { - switch (formatALSA) { - case MA_SND_PCM_FORMAT_S16_LE: return ma_format_s16; - case MA_SND_PCM_FORMAT_S24_3LE: return ma_format_s24; - case MA_SND_PCM_FORMAT_S32_LE: return ma_format_s32; - case MA_SND_PCM_FORMAT_FLOAT_LE: return ma_format_f32; - default: break; - } - } else { - switch (formatALSA) { - case MA_SND_PCM_FORMAT_S16_BE: return ma_format_s16; - case MA_SND_PCM_FORMAT_S24_3BE: return ma_format_s24; - case MA_SND_PCM_FORMAT_S32_BE: return ma_format_s32; - case MA_SND_PCM_FORMAT_FLOAT_BE: return ma_format_f32; - default: break; - } - } - - /* Endian agnostic. */ - switch (formatALSA) { - case MA_SND_PCM_FORMAT_U8: return ma_format_u8; - default: return ma_format_unknown; - } -} - -static ma_channel ma_convert_alsa_channel_position_to_ma_channel(unsigned int alsaChannelPos) -{ - switch (alsaChannelPos) - { - case MA_SND_CHMAP_MONO: return MA_CHANNEL_MONO; - case MA_SND_CHMAP_FL: return MA_CHANNEL_FRONT_LEFT; - case MA_SND_CHMAP_FR: return MA_CHANNEL_FRONT_RIGHT; - case MA_SND_CHMAP_RL: return MA_CHANNEL_BACK_LEFT; - case MA_SND_CHMAP_RR: return MA_CHANNEL_BACK_RIGHT; - case MA_SND_CHMAP_FC: return MA_CHANNEL_FRONT_CENTER; - case MA_SND_CHMAP_LFE: return MA_CHANNEL_LFE; - case MA_SND_CHMAP_SL: return MA_CHANNEL_SIDE_LEFT; - case MA_SND_CHMAP_SR: return MA_CHANNEL_SIDE_RIGHT; - case MA_SND_CHMAP_RC: return MA_CHANNEL_BACK_CENTER; - case MA_SND_CHMAP_FLC: return MA_CHANNEL_FRONT_LEFT_CENTER; - case MA_SND_CHMAP_FRC: return MA_CHANNEL_FRONT_RIGHT_CENTER; - case MA_SND_CHMAP_RLC: return 0; - case MA_SND_CHMAP_RRC: return 0; - case MA_SND_CHMAP_FLW: return 0; - case MA_SND_CHMAP_FRW: return 0; - case MA_SND_CHMAP_FLH: return 0; - case MA_SND_CHMAP_FCH: return 0; - case MA_SND_CHMAP_FRH: return 0; - case MA_SND_CHMAP_TC: return MA_CHANNEL_TOP_CENTER; - case MA_SND_CHMAP_TFL: return MA_CHANNEL_TOP_FRONT_LEFT; - case MA_SND_CHMAP_TFR: return MA_CHANNEL_TOP_FRONT_RIGHT; - case MA_SND_CHMAP_TFC: return MA_CHANNEL_TOP_FRONT_CENTER; - case MA_SND_CHMAP_TRL: return MA_CHANNEL_TOP_BACK_LEFT; - case MA_SND_CHMAP_TRR: return MA_CHANNEL_TOP_BACK_RIGHT; - case MA_SND_CHMAP_TRC: return MA_CHANNEL_TOP_BACK_CENTER; - default: break; - } - - return 0; -} - -static ma_bool32 ma_is_common_device_name__alsa(const char* name) -{ - size_t iName; - for (iName = 0; iName < ma_countof(g_maCommonDeviceNamesALSA); ++iName) { - if (ma_strcmp(name, g_maCommonDeviceNamesALSA[iName]) == 0) { - return MA_TRUE; - } - } - - return MA_FALSE; -} - - -static ma_bool32 ma_is_playback_device_blacklisted__alsa(const char* name) -{ - size_t iName; - for (iName = 0; iName < ma_countof(g_maBlacklistedPlaybackDeviceNamesALSA); ++iName) { - if (ma_strcmp(name, g_maBlacklistedPlaybackDeviceNamesALSA[iName]) == 0) { - return MA_TRUE; - } - } - - return MA_FALSE; -} - -static ma_bool32 ma_is_capture_device_blacklisted__alsa(const char* name) -{ - size_t iName; - for (iName = 0; iName < ma_countof(g_maBlacklistedCaptureDeviceNamesALSA); ++iName) { - if (ma_strcmp(name, g_maBlacklistedCaptureDeviceNamesALSA[iName]) == 0) { - return MA_TRUE; - } - } - - return MA_FALSE; -} - -static ma_bool32 ma_is_device_blacklisted__alsa(ma_device_type deviceType, const char* name) -{ - if (deviceType == ma_device_type_playback) { - return ma_is_playback_device_blacklisted__alsa(name); - } else { - return ma_is_capture_device_blacklisted__alsa(name); - } -} - - -static const char* ma_find_char(const char* str, char c, int* index) -{ - int i = 0; - for (;;) { - if (str[i] == '\0') { - if (index) *index = -1; - return NULL; - } - - if (str[i] == c) { - if (index) *index = i; - return str + i; - } - - i += 1; - } - - /* Should never get here, but treat it as though the character was not found to make me feel better inside. */ - if (index) *index = -1; - return NULL; -} - -static ma_bool32 ma_is_device_name_in_hw_format__alsa(const char* hwid) -{ - /* This function is just checking whether or not hwid is in "hw:%d,%d" format. */ - - int commaPos; - const char* dev; - int i; - - if (hwid == NULL) { - return MA_FALSE; - } - - if (hwid[0] != 'h' || hwid[1] != 'w' || hwid[2] != ':') { - return MA_FALSE; - } - - hwid += 3; - - dev = ma_find_char(hwid, ',', &commaPos); - if (dev == NULL) { - return MA_FALSE; - } else { - dev += 1; /* Skip past the ",". */ - } - - /* Check if the part between the ":" and the "," contains only numbers. If not, return false. */ - for (i = 0; i < commaPos; ++i) { - if (hwid[i] < '0' || hwid[i] > '9') { - return MA_FALSE; - } - } - - /* Check if everything after the "," is numeric. If not, return false. */ - i = 0; - while (dev[i] != '\0') { - if (dev[i] < '0' || dev[i] > '9') { - return MA_FALSE; - } - i += 1; - } - - return MA_TRUE; -} - -static int ma_convert_device_name_to_hw_format__alsa(ma_context* pContext, char* dst, size_t dstSize, const char* src) /* Returns 0 on success, non-0 on error. */ -{ - /* src should look something like this: "hw:CARD=I82801AAICH,DEV=0" */ - - int colonPos; - int commaPos; - char card[256]; - const char* dev; - int cardIndex; - - if (dst == NULL) { - return -1; - } - if (dstSize < 7) { - return -1; /* Absolute minimum size of the output buffer is 7 bytes. */ - } - - *dst = '\0'; /* Safety. */ - if (src == NULL) { - return -1; - } - - /* If the input name is already in "hw:%d,%d" format, just return that verbatim. */ - if (ma_is_device_name_in_hw_format__alsa(src)) { - return ma_strcpy_s(dst, dstSize, src); - } - - src = ma_find_char(src, ':', &colonPos); - if (src == NULL) { - return -1; /* Couldn't find a colon */ - } - - dev = ma_find_char(src, ',', &commaPos); - if (dev == NULL) { - dev = "0"; - ma_strncpy_s(card, sizeof(card), src+6, (size_t)-1); /* +6 = ":CARD=" */ - } else { - dev = dev + 5; /* +5 = ",DEV=" */ - ma_strncpy_s(card, sizeof(card), src+6, commaPos-6); /* +6 = ":CARD=" */ - } - - cardIndex = ((ma_snd_card_get_index_proc)pContext->alsa.snd_card_get_index)(card); - if (cardIndex < 0) { - return -2; /* Failed to retrieve the card index. */ - } - - - /* Construction. */ - dst[0] = 'h'; dst[1] = 'w'; dst[2] = ':'; - if (ma_itoa_s(cardIndex, dst+3, dstSize-3, 10) != 0) { - return -3; - } - if (ma_strcat_s(dst, dstSize, ",") != 0) { - return -3; - } - if (ma_strcat_s(dst, dstSize, dev) != 0) { - return -3; - } - - return 0; -} - -static ma_bool32 ma_does_id_exist_in_list__alsa(ma_device_id* pUniqueIDs, ma_uint32 count, const char* pHWID) -{ - ma_uint32 i; - - MA_ASSERT(pHWID != NULL); - - for (i = 0; i < count; ++i) { - if (ma_strcmp(pUniqueIDs[i].alsa, pHWID) == 0) { - return MA_TRUE; - } - } - - return MA_FALSE; -} - - -static ma_result ma_context_open_pcm__alsa(ma_context* pContext, ma_share_mode shareMode, ma_device_type deviceType, const ma_device_id* pDeviceID, int openMode, ma_snd_pcm_t** ppPCM) -{ - ma_snd_pcm_t* pPCM; - ma_snd_pcm_stream_t stream; - - MA_ASSERT(pContext != NULL); - MA_ASSERT(ppPCM != NULL); - - *ppPCM = NULL; - pPCM = NULL; - - stream = (deviceType == ma_device_type_playback) ? MA_SND_PCM_STREAM_PLAYBACK : MA_SND_PCM_STREAM_CAPTURE; - - if (pDeviceID == NULL) { - ma_bool32 isDeviceOpen; - size_t i; - - /* - We're opening the default device. I don't know if trying anything other than "default" is necessary, but it makes - me feel better to try as hard as we can get to get _something_ working. - */ - const char* defaultDeviceNames[] = { - "default", - NULL, - NULL, - NULL, - NULL, - NULL, - NULL - }; - - if (shareMode == ma_share_mode_exclusive) { - defaultDeviceNames[1] = "hw"; - defaultDeviceNames[2] = "hw:0"; - defaultDeviceNames[3] = "hw:0,0"; - } else { - if (deviceType == ma_device_type_playback) { - defaultDeviceNames[1] = "dmix"; - defaultDeviceNames[2] = "dmix:0"; - defaultDeviceNames[3] = "dmix:0,0"; - } else { - defaultDeviceNames[1] = "dsnoop"; - defaultDeviceNames[2] = "dsnoop:0"; - defaultDeviceNames[3] = "dsnoop:0,0"; - } - defaultDeviceNames[4] = "hw"; - defaultDeviceNames[5] = "hw:0"; - defaultDeviceNames[6] = "hw:0,0"; - } - - isDeviceOpen = MA_FALSE; - for (i = 0; i < ma_countof(defaultDeviceNames); ++i) { - if (defaultDeviceNames[i] != NULL && defaultDeviceNames[i][0] != '\0') { - if (((ma_snd_pcm_open_proc)pContext->alsa.snd_pcm_open)(&pPCM, defaultDeviceNames[i], stream, openMode) == 0) { - isDeviceOpen = MA_TRUE; - break; - } - } - } - - if (!isDeviceOpen) { - ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[ALSA] snd_pcm_open() failed when trying to open an appropriate default device."); - return MA_FAILED_TO_OPEN_BACKEND_DEVICE; - } - } else { - /* - We're trying to open a specific device. There's a few things to consider here: - - miniaudio recognizes a special format of device id that excludes the "hw", "dmix", etc. prefix. It looks like this: ":0,0", ":0,1", etc. When - an ID of this format is specified, it indicates to miniaudio that it can try different combinations of plugins ("hw", "dmix", etc.) until it - finds an appropriate one that works. This comes in very handy when trying to open a device in shared mode ("dmix"), vs exclusive mode ("hw"). - */ - - /* May end up needing to make small adjustments to the ID, so make a copy. */ - ma_device_id deviceID = *pDeviceID; - int resultALSA = -ENODEV; - - if (deviceID.alsa[0] != ':') { - /* The ID is not in ":0,0" format. Use the ID exactly as-is. */ - resultALSA = ((ma_snd_pcm_open_proc)pContext->alsa.snd_pcm_open)(&pPCM, deviceID.alsa, stream, openMode); - } else { - char hwid[256]; - - /* The ID is in ":0,0" format. Try different plugins depending on the shared mode. */ - if (deviceID.alsa[1] == '\0') { - deviceID.alsa[0] = '\0'; /* An ID of ":" should be converted to "". */ - } - - if (shareMode == ma_share_mode_shared) { - if (deviceType == ma_device_type_playback) { - ma_strcpy_s(hwid, sizeof(hwid), "dmix"); - } else { - ma_strcpy_s(hwid, sizeof(hwid), "dsnoop"); - } - - if (ma_strcat_s(hwid, sizeof(hwid), deviceID.alsa) == 0) { - resultALSA = ((ma_snd_pcm_open_proc)pContext->alsa.snd_pcm_open)(&pPCM, hwid, stream, openMode); - } - } - - /* If at this point we still don't have an open device it means we're either preferencing exclusive mode or opening with "dmix"/"dsnoop" failed. */ - if (resultALSA != 0) { - ma_strcpy_s(hwid, sizeof(hwid), "hw"); - if (ma_strcat_s(hwid, sizeof(hwid), deviceID.alsa) == 0) { - resultALSA = ((ma_snd_pcm_open_proc)pContext->alsa.snd_pcm_open)(&pPCM, hwid, stream, openMode); - } - } - } - - if (resultALSA < 0) { - ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[ALSA] snd_pcm_open() failed."); - return ma_result_from_errno(-resultALSA); - } - } - - *ppPCM = pPCM; - return MA_SUCCESS; -} - - -static ma_result ma_context_enumerate_devices__alsa(ma_context* pContext, ma_enum_devices_callback_proc callback, void* pUserData) -{ - int resultALSA; - ma_bool32 cbResult = MA_TRUE; - char** ppDeviceHints; - ma_device_id* pUniqueIDs = NULL; - ma_uint32 uniqueIDCount = 0; - char** ppNextDeviceHint; - - MA_ASSERT(pContext != NULL); - MA_ASSERT(callback != NULL); - - ma_mutex_lock(&pContext->alsa.internalDeviceEnumLock); - - resultALSA = ((ma_snd_device_name_hint_proc)pContext->alsa.snd_device_name_hint)(-1, "pcm", (void***)&ppDeviceHints); - if (resultALSA < 0) { - ma_mutex_unlock(&pContext->alsa.internalDeviceEnumLock); - return ma_result_from_errno(-resultALSA); - } - - ppNextDeviceHint = ppDeviceHints; - while (*ppNextDeviceHint != NULL) { - char* NAME = ((ma_snd_device_name_get_hint_proc)pContext->alsa.snd_device_name_get_hint)(*ppNextDeviceHint, "NAME"); - char* DESC = ((ma_snd_device_name_get_hint_proc)pContext->alsa.snd_device_name_get_hint)(*ppNextDeviceHint, "DESC"); - char* IOID = ((ma_snd_device_name_get_hint_proc)pContext->alsa.snd_device_name_get_hint)(*ppNextDeviceHint, "IOID"); - ma_device_type deviceType = ma_device_type_playback; - ma_bool32 stopEnumeration = MA_FALSE; - char hwid[sizeof(pUniqueIDs->alsa)]; - ma_device_info deviceInfo; - - if ((IOID == NULL || ma_strcmp(IOID, "Output") == 0)) { - deviceType = ma_device_type_playback; - } - if ((IOID != NULL && ma_strcmp(IOID, "Input" ) == 0)) { - deviceType = ma_device_type_capture; - } - - if (NAME != NULL) { - if (pContext->alsa.useVerboseDeviceEnumeration) { - /* Verbose mode. Use the name exactly as-is. */ - ma_strncpy_s(hwid, sizeof(hwid), NAME, (size_t)-1); - } else { - /* Simplified mode. Use ":%d,%d" format. */ - if (ma_convert_device_name_to_hw_format__alsa(pContext, hwid, sizeof(hwid), NAME) == 0) { - /* - At this point, hwid looks like "hw:0,0". In simplified enumeration mode, we actually want to strip off the - plugin name so it looks like ":0,0". The reason for this is that this special format is detected at device - initialization time and is used as an indicator to try to use the most appropriate plugin depending on the - device type and sharing mode. - */ - char* dst = hwid; - char* src = hwid+2; - while ((*dst++ = *src++)); - } else { - /* Conversion to "hw:%d,%d" failed. Just use the name as-is. */ - ma_strncpy_s(hwid, sizeof(hwid), NAME, (size_t)-1); - } - - if (ma_does_id_exist_in_list__alsa(pUniqueIDs, uniqueIDCount, hwid)) { - goto next_device; /* The device has already been enumerated. Move on to the next one. */ - } else { - /* The device has not yet been enumerated. Make sure it's added to our list so that it's not enumerated again. */ - size_t newCapacity = sizeof(*pUniqueIDs) * (uniqueIDCount + 1); - ma_device_id* pNewUniqueIDs = (ma_device_id*)ma_realloc(pUniqueIDs, newCapacity, &pContext->allocationCallbacks); - if (pNewUniqueIDs == NULL) { - goto next_device; /* Failed to allocate memory. */ - } - - pUniqueIDs = pNewUniqueIDs; - MA_COPY_MEMORY(pUniqueIDs[uniqueIDCount].alsa, hwid, sizeof(hwid)); - uniqueIDCount += 1; - } - } - } else { - MA_ZERO_MEMORY(hwid, sizeof(hwid)); - } - - MA_ZERO_OBJECT(&deviceInfo); - ma_strncpy_s(deviceInfo.id.alsa, sizeof(deviceInfo.id.alsa), hwid, (size_t)-1); - - /* - There's no good way to determine whether or not a device is the default on Linux. We're just going to do something simple and - just use the name of "default" as the indicator. - */ - if (ma_strcmp(deviceInfo.id.alsa, "default") == 0) { - deviceInfo.isDefault = MA_TRUE; - } - - - /* - DESC is the friendly name. We treat this slightly differently depending on whether or not we are using verbose - device enumeration. In verbose mode we want to take the entire description so that the end-user can distinguish - between the subdevices of each card/dev pair. In simplified mode, however, we only want the first part of the - description. - - The value in DESC seems to be split into two lines, with the first line being the name of the device and the - second line being a description of the device. I don't like having the description be across two lines because - it makes formatting ugly and annoying. I'm therefore deciding to put it all on a single line with the second line - being put into parentheses. In simplified mode I'm just stripping the second line entirely. - */ - if (DESC != NULL) { - int lfPos; - const char* line2 = ma_find_char(DESC, '\n', &lfPos); - if (line2 != NULL) { - line2 += 1; /* Skip past the new-line character. */ - - if (pContext->alsa.useVerboseDeviceEnumeration) { - /* Verbose mode. Put the second line in brackets. */ - ma_strncpy_s(deviceInfo.name, sizeof(deviceInfo.name), DESC, lfPos); - ma_strcat_s (deviceInfo.name, sizeof(deviceInfo.name), " ("); - ma_strcat_s (deviceInfo.name, sizeof(deviceInfo.name), line2); - ma_strcat_s (deviceInfo.name, sizeof(deviceInfo.name), ")"); - } else { - /* Simplified mode. Strip the second line entirely. */ - ma_strncpy_s(deviceInfo.name, sizeof(deviceInfo.name), DESC, lfPos); - } - } else { - /* There's no second line. Just copy the whole description. */ - ma_strncpy_s(deviceInfo.name, sizeof(deviceInfo.name), DESC, (size_t)-1); - } - } - - if (!ma_is_device_blacklisted__alsa(deviceType, NAME)) { - cbResult = callback(pContext, deviceType, &deviceInfo, pUserData); - } - - /* - Some devices are both playback and capture, but they are only enumerated by ALSA once. We need to fire the callback - again for the other device type in this case. We do this for known devices and where the IOID hint is NULL, which - means both Input and Output. - */ - if (cbResult) { - if (ma_is_common_device_name__alsa(NAME) || IOID == NULL) { - if (deviceType == ma_device_type_playback) { - if (!ma_is_capture_device_blacklisted__alsa(NAME)) { - cbResult = callback(pContext, ma_device_type_capture, &deviceInfo, pUserData); - } - } else { - if (!ma_is_playback_device_blacklisted__alsa(NAME)) { - cbResult = callback(pContext, ma_device_type_playback, &deviceInfo, pUserData); - } - } - } - } - - if (cbResult == MA_FALSE) { - stopEnumeration = MA_TRUE; - } - - next_device: - free(NAME); - free(DESC); - free(IOID); - ppNextDeviceHint += 1; - - /* We need to stop enumeration if the callback returned false. */ - if (stopEnumeration) { - break; - } - } - - ma_free(pUniqueIDs, &pContext->allocationCallbacks); - ((ma_snd_device_name_free_hint_proc)pContext->alsa.snd_device_name_free_hint)((void**)ppDeviceHints); - - ma_mutex_unlock(&pContext->alsa.internalDeviceEnumLock); - - return MA_SUCCESS; -} - - -typedef struct -{ - ma_device_type deviceType; - const ma_device_id* pDeviceID; - ma_share_mode shareMode; - ma_device_info* pDeviceInfo; - ma_bool32 foundDevice; -} ma_context_get_device_info_enum_callback_data__alsa; - -static ma_bool32 ma_context_get_device_info_enum_callback__alsa(ma_context* pContext, ma_device_type deviceType, const ma_device_info* pDeviceInfo, void* pUserData) -{ - ma_context_get_device_info_enum_callback_data__alsa* pData = (ma_context_get_device_info_enum_callback_data__alsa*)pUserData; - MA_ASSERT(pData != NULL); - - (void)pContext; - - if (pData->pDeviceID == NULL && ma_strcmp(pDeviceInfo->id.alsa, "default") == 0) { - ma_strncpy_s(pData->pDeviceInfo->name, sizeof(pData->pDeviceInfo->name), pDeviceInfo->name, (size_t)-1); - pData->foundDevice = MA_TRUE; - } else { - if (pData->deviceType == deviceType && (pData->pDeviceID != NULL && ma_strcmp(pData->pDeviceID->alsa, pDeviceInfo->id.alsa) == 0)) { - ma_strncpy_s(pData->pDeviceInfo->name, sizeof(pData->pDeviceInfo->name), pDeviceInfo->name, (size_t)-1); - pData->foundDevice = MA_TRUE; - } - } - - /* Keep enumerating until we have found the device. */ - return !pData->foundDevice; -} - -static void ma_context_test_rate_and_add_native_data_format__alsa(ma_context* pContext, ma_snd_pcm_t* pPCM, ma_snd_pcm_hw_params_t* pHWParams, ma_format format, ma_uint32 channels, ma_uint32 sampleRate, ma_uint32 flags, ma_device_info* pDeviceInfo) -{ - MA_ASSERT(pPCM != NULL); - MA_ASSERT(pHWParams != NULL); - MA_ASSERT(pDeviceInfo != NULL); - - if (pDeviceInfo->nativeDataFormatCount < ma_countof(pDeviceInfo->nativeDataFormats) && ((ma_snd_pcm_hw_params_test_rate_proc)pContext->alsa.snd_pcm_hw_params_test_rate)(pPCM, pHWParams, sampleRate, 0) == 0) { - pDeviceInfo->nativeDataFormats[pDeviceInfo->nativeDataFormatCount].format = format; - pDeviceInfo->nativeDataFormats[pDeviceInfo->nativeDataFormatCount].channels = channels; - pDeviceInfo->nativeDataFormats[pDeviceInfo->nativeDataFormatCount].sampleRate = sampleRate; - pDeviceInfo->nativeDataFormats[pDeviceInfo->nativeDataFormatCount].flags = flags; - pDeviceInfo->nativeDataFormatCount += 1; - } -} - -static void ma_context_iterate_rates_and_add_native_data_format__alsa(ma_context* pContext, ma_snd_pcm_t* pPCM, ma_snd_pcm_hw_params_t* pHWParams, ma_format format, ma_uint32 channels, ma_uint32 flags, ma_device_info* pDeviceInfo) -{ - ma_uint32 iSampleRate; - unsigned int minSampleRate; - unsigned int maxSampleRate; - int sampleRateDir; /* Not used. Just passed into snd_pcm_hw_params_get_rate_min/max(). */ - - /* There could be a range. */ - ((ma_snd_pcm_hw_params_get_rate_min_proc)pContext->alsa.snd_pcm_hw_params_get_rate_min)(pHWParams, &minSampleRate, &sampleRateDir); - ((ma_snd_pcm_hw_params_get_rate_max_proc)pContext->alsa.snd_pcm_hw_params_get_rate_max)(pHWParams, &maxSampleRate, &sampleRateDir); - - /* Make sure our sample rates are clamped to sane values. Stupid devices like "pulse" will reports rates like "1" which is ridiculous. */ - minSampleRate = ma_clamp(minSampleRate, (unsigned int)ma_standard_sample_rate_min, (unsigned int)ma_standard_sample_rate_max); - maxSampleRate = ma_clamp(maxSampleRate, (unsigned int)ma_standard_sample_rate_min, (unsigned int)ma_standard_sample_rate_max); - - for (iSampleRate = 0; iSampleRate < ma_countof(g_maStandardSampleRatePriorities); iSampleRate += 1) { - ma_uint32 standardSampleRate = g_maStandardSampleRatePriorities[iSampleRate]; - - if (standardSampleRate >= minSampleRate && standardSampleRate <= maxSampleRate) { - ma_context_test_rate_and_add_native_data_format__alsa(pContext, pPCM, pHWParams, format, channels, standardSampleRate, flags, pDeviceInfo); - } - } - - /* Now make sure our min and max rates are included just in case they aren't in the range of our standard rates. */ - if (!ma_is_standard_sample_rate(minSampleRate)) { - ma_context_test_rate_and_add_native_data_format__alsa(pContext, pPCM, pHWParams, format, channels, minSampleRate, flags, pDeviceInfo); - } - - if (!ma_is_standard_sample_rate(maxSampleRate) && maxSampleRate != minSampleRate) { - ma_context_test_rate_and_add_native_data_format__alsa(pContext, pPCM, pHWParams, format, channels, maxSampleRate, flags, pDeviceInfo); - } -} - -static ma_result ma_context_get_device_info__alsa(ma_context* pContext, ma_device_type deviceType, const ma_device_id* pDeviceID, ma_device_info* pDeviceInfo) -{ - ma_context_get_device_info_enum_callback_data__alsa data; - ma_result result; - int resultALSA; - ma_snd_pcm_t* pPCM; - ma_snd_pcm_hw_params_t* pHWParams; - ma_uint32 iFormat; - ma_uint32 iChannel; - - MA_ASSERT(pContext != NULL); - - /* We just enumerate to find basic information about the device. */ - data.deviceType = deviceType; - data.pDeviceID = pDeviceID; - data.pDeviceInfo = pDeviceInfo; - data.foundDevice = MA_FALSE; - result = ma_context_enumerate_devices__alsa(pContext, ma_context_get_device_info_enum_callback__alsa, &data); - if (result != MA_SUCCESS) { - return result; - } - - if (!data.foundDevice) { - return MA_NO_DEVICE; - } - - if (ma_strcmp(pDeviceInfo->id.alsa, "default") == 0) { - pDeviceInfo->isDefault = MA_TRUE; - } - - /* For detailed info we need to open the device. */ - result = ma_context_open_pcm__alsa(pContext, ma_share_mode_shared, deviceType, pDeviceID, 0, &pPCM); - if (result != MA_SUCCESS) { - return result; - } - - /* We need to initialize a HW parameters object in order to know what formats are supported. */ - pHWParams = (ma_snd_pcm_hw_params_t*)ma_calloc(((ma_snd_pcm_hw_params_sizeof_proc)pContext->alsa.snd_pcm_hw_params_sizeof)(), &pContext->allocationCallbacks); - if (pHWParams == NULL) { - ((ma_snd_pcm_close_proc)pContext->alsa.snd_pcm_close)(pPCM); - return MA_OUT_OF_MEMORY; - } - - resultALSA = ((ma_snd_pcm_hw_params_any_proc)pContext->alsa.snd_pcm_hw_params_any)(pPCM, pHWParams); - if (resultALSA < 0) { - ma_free(pHWParams, &pContext->allocationCallbacks); - ((ma_snd_pcm_close_proc)pContext->alsa.snd_pcm_close)(pPCM); - ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[ALSA] Failed to initialize hardware parameters. snd_pcm_hw_params_any() failed."); - return ma_result_from_errno(-resultALSA); - } - - /* - Some ALSA devices can support many permutations of formats, channels and rates. We only support - a fixed number of permutations which means we need to employ some strategies to ensure the best - combinations are returned. An example is the "pulse" device which can do its own data conversion - in software and as a result can support any combination of format, channels and rate. - - We want to ensure that the first data formats are the best. We have a list of favored sample - formats and sample rates, so these will be the basis of our iteration. - */ - - /* Formats. We just iterate over our standard formats and test them, making sure we reset the configuration space each iteration. */ - for (iFormat = 0; iFormat < ma_countof(g_maFormatPriorities); iFormat += 1) { - ma_format format = g_maFormatPriorities[iFormat]; - - /* - For each format we need to make sure we reset the configuration space so we don't return - channel counts and rates that aren't compatible with a format. - */ - ((ma_snd_pcm_hw_params_any_proc)pContext->alsa.snd_pcm_hw_params_any)(pPCM, pHWParams); - - /* Test the format first. If this fails it means the format is not supported and we can skip it. */ - if (((ma_snd_pcm_hw_params_test_format_proc)pContext->alsa.snd_pcm_hw_params_test_format)(pPCM, pHWParams, ma_convert_ma_format_to_alsa_format(format)) == 0) { - /* The format is supported. */ - unsigned int minChannels; - unsigned int maxChannels; - - /* - The configuration space needs to be restricted to this format so we can get an accurate - picture of which sample rates and channel counts are support with this format. - */ - ((ma_snd_pcm_hw_params_set_format_proc)pContext->alsa.snd_pcm_hw_params_set_format)(pPCM, pHWParams, ma_convert_ma_format_to_alsa_format(format)); - - /* Now we need to check for supported channels. */ - ((ma_snd_pcm_hw_params_get_channels_min_proc)pContext->alsa.snd_pcm_hw_params_get_channels_min)(pHWParams, &minChannels); - ((ma_snd_pcm_hw_params_get_channels_max_proc)pContext->alsa.snd_pcm_hw_params_get_channels_max)(pHWParams, &maxChannels); - - if (minChannels > MA_MAX_CHANNELS) { - continue; /* Too many channels. */ - } - if (maxChannels < MA_MIN_CHANNELS) { - continue; /* Not enough channels. */ - } - - /* - Make sure the channel count is clamped. This is mainly intended for the max channels - because some devices can report an unbound maximum. - */ - minChannels = ma_clamp(minChannels, MA_MIN_CHANNELS, MA_MAX_CHANNELS); - maxChannels = ma_clamp(maxChannels, MA_MIN_CHANNELS, MA_MAX_CHANNELS); - - if (minChannels == MA_MIN_CHANNELS && maxChannels == MA_MAX_CHANNELS) { - /* The device supports all channels. Don't iterate over every single one. Instead just set the channels to 0 which means all channels are supported. */ - ma_context_iterate_rates_and_add_native_data_format__alsa(pContext, pPCM, pHWParams, format, 0, 0, pDeviceInfo); /* Intentionally setting the channel count to 0 as that means all channels are supported. */ - } else { - /* The device only supports a specific set of channels. We need to iterate over all of them. */ - for (iChannel = minChannels; iChannel <= maxChannels; iChannel += 1) { - /* Test the channel before applying it to the configuration space. */ - unsigned int channels = iChannel; - - /* Make sure our channel range is reset before testing again or else we'll always fail the test. */ - ((ma_snd_pcm_hw_params_any_proc)pContext->alsa.snd_pcm_hw_params_any)(pPCM, pHWParams); - ((ma_snd_pcm_hw_params_set_format_proc)pContext->alsa.snd_pcm_hw_params_set_format)(pPCM, pHWParams, ma_convert_ma_format_to_alsa_format(format)); - - if (((ma_snd_pcm_hw_params_test_channels_proc)pContext->alsa.snd_pcm_hw_params_test_channels)(pPCM, pHWParams, channels) == 0) { - /* The channel count is supported. */ - - /* The configuration space now needs to be restricted to the channel count before extracting the sample rate. */ - ((ma_snd_pcm_hw_params_set_channels_proc)pContext->alsa.snd_pcm_hw_params_set_channels)(pPCM, pHWParams, channels); - - /* Only after the configuration space has been restricted to the specific channel count should we iterate over our sample rates. */ - ma_context_iterate_rates_and_add_native_data_format__alsa(pContext, pPCM, pHWParams, format, channels, 0, pDeviceInfo); - } else { - /* The channel count is not supported. Skip. */ - } - } - } - } else { - /* The format is not supported. Skip. */ - } - } - - ma_free(pHWParams, &pContext->allocationCallbacks); - - ((ma_snd_pcm_close_proc)pContext->alsa.snd_pcm_close)(pPCM); - return MA_SUCCESS; -} - -static ma_result ma_device_uninit__alsa(ma_device* pDevice) -{ - MA_ASSERT(pDevice != NULL); - - if ((ma_snd_pcm_t*)pDevice->alsa.pPCMCapture) { - ((ma_snd_pcm_close_proc)pDevice->pContext->alsa.snd_pcm_close)((ma_snd_pcm_t*)pDevice->alsa.pPCMCapture); - close(pDevice->alsa.wakeupfdCapture); - ma_free(pDevice->alsa.pPollDescriptorsCapture, &pDevice->pContext->allocationCallbacks); - } - - if ((ma_snd_pcm_t*)pDevice->alsa.pPCMPlayback) { - ((ma_snd_pcm_close_proc)pDevice->pContext->alsa.snd_pcm_close)((ma_snd_pcm_t*)pDevice->alsa.pPCMPlayback); - close(pDevice->alsa.wakeupfdPlayback); - ma_free(pDevice->alsa.pPollDescriptorsPlayback, &pDevice->pContext->allocationCallbacks); - } - - return MA_SUCCESS; -} - -static ma_result ma_device_init_by_type__alsa(ma_device* pDevice, const ma_device_config* pConfig, ma_device_descriptor* pDescriptor, ma_device_type deviceType) -{ - ma_result result; - int resultALSA; - ma_snd_pcm_t* pPCM; - ma_bool32 isUsingMMap; - ma_snd_pcm_format_t formatALSA; - ma_format internalFormat; - ma_uint32 internalChannels; - ma_uint32 internalSampleRate; - ma_channel internalChannelMap[MA_MAX_CHANNELS]; - ma_uint32 internalPeriodSizeInFrames; - ma_uint32 internalPeriods; - int openMode; - ma_snd_pcm_hw_params_t* pHWParams; - ma_snd_pcm_sw_params_t* pSWParams; - ma_snd_pcm_uframes_t bufferBoundary; - int pollDescriptorCount; - struct pollfd* pPollDescriptors; - int wakeupfd; - - MA_ASSERT(pConfig != NULL); - MA_ASSERT(deviceType != ma_device_type_duplex); /* This function should only be called for playback _or_ capture, never duplex. */ - MA_ASSERT(pDevice != NULL); - - formatALSA = ma_convert_ma_format_to_alsa_format(pDescriptor->format); - - openMode = 0; - if (pConfig->alsa.noAutoResample) { - openMode |= MA_SND_PCM_NO_AUTO_RESAMPLE; - } - if (pConfig->alsa.noAutoChannels) { - openMode |= MA_SND_PCM_NO_AUTO_CHANNELS; - } - if (pConfig->alsa.noAutoFormat) { - openMode |= MA_SND_PCM_NO_AUTO_FORMAT; - } - - result = ma_context_open_pcm__alsa(pDevice->pContext, pDescriptor->shareMode, deviceType, pDescriptor->pDeviceID, openMode, &pPCM); - if (result != MA_SUCCESS) { - return result; - } - - - /* Hardware parameters. */ - pHWParams = (ma_snd_pcm_hw_params_t*)ma_calloc(((ma_snd_pcm_hw_params_sizeof_proc)pDevice->pContext->alsa.snd_pcm_hw_params_sizeof)(), &pDevice->pContext->allocationCallbacks); - if (pHWParams == NULL) { - ((ma_snd_pcm_close_proc)pDevice->pContext->alsa.snd_pcm_close)(pPCM); - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[ALSA] Failed to allocate memory for hardware parameters."); - return MA_OUT_OF_MEMORY; - } - - resultALSA = ((ma_snd_pcm_hw_params_any_proc)pDevice->pContext->alsa.snd_pcm_hw_params_any)(pPCM, pHWParams); - if (resultALSA < 0) { - ma_free(pHWParams, &pDevice->pContext->allocationCallbacks); - ((ma_snd_pcm_close_proc)pDevice->pContext->alsa.snd_pcm_close)(pPCM); - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[ALSA] Failed to initialize hardware parameters. snd_pcm_hw_params_any() failed."); - return ma_result_from_errno(-resultALSA); - } - - /* MMAP Mode. Try using interleaved MMAP access. If this fails, fall back to standard readi/writei. */ - isUsingMMap = MA_FALSE; -#if 0 /* NOTE: MMAP mode temporarily disabled. */ - if (deviceType != ma_device_type_capture) { /* <-- Disabling MMAP mode for capture devices because I apparently do not have a device that supports it which means I can't test it... Contributions welcome. */ - if (!pConfig->alsa.noMMap) { - if (((ma_snd_pcm_hw_params_set_access_proc)pDevice->pContext->alsa.snd_pcm_hw_params_set_access)(pPCM, pHWParams, MA_SND_PCM_ACCESS_MMAP_INTERLEAVED) == 0) { - pDevice->alsa.isUsingMMap = MA_TRUE; - } - } - } -#endif - - if (!isUsingMMap) { - resultALSA = ((ma_snd_pcm_hw_params_set_access_proc)pDevice->pContext->alsa.snd_pcm_hw_params_set_access)(pPCM, pHWParams, MA_SND_PCM_ACCESS_RW_INTERLEAVED); - if (resultALSA < 0) { - ma_free(pHWParams, &pDevice->pContext->allocationCallbacks); - ((ma_snd_pcm_close_proc)pDevice->pContext->alsa.snd_pcm_close)(pPCM); - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[ALSA] Failed to set access mode to neither SND_PCM_ACCESS_MMAP_INTERLEAVED nor SND_PCM_ACCESS_RW_INTERLEAVED. snd_pcm_hw_params_set_access() failed."); - return ma_result_from_errno(-resultALSA); - } - } - - /* - Most important properties first. The documentation for OSS (yes, I know this is ALSA!) recommends format, channels, then sample rate. I can't - find any documentation for ALSA specifically, so I'm going to copy the recommendation for OSS. - */ - - /* Format. */ - { - /* - At this point we should have a list of supported formats, so now we need to find the best one. We first check if the requested format is - supported, and if so, use that one. If it's not supported, we just run though a list of formats and try to find the best one. - */ - if (formatALSA == MA_SND_PCM_FORMAT_UNKNOWN || ((ma_snd_pcm_hw_params_test_format_proc)pDevice->pContext->alsa.snd_pcm_hw_params_test_format)(pPCM, pHWParams, formatALSA) != 0) { - /* We're either requesting the native format or the specified format is not supported. */ - size_t iFormat; - - formatALSA = MA_SND_PCM_FORMAT_UNKNOWN; - for (iFormat = 0; iFormat < ma_countof(g_maFormatPriorities); ++iFormat) { - if (((ma_snd_pcm_hw_params_test_format_proc)pDevice->pContext->alsa.snd_pcm_hw_params_test_format)(pPCM, pHWParams, ma_convert_ma_format_to_alsa_format(g_maFormatPriorities[iFormat])) == 0) { - formatALSA = ma_convert_ma_format_to_alsa_format(g_maFormatPriorities[iFormat]); - break; - } - } - - if (formatALSA == MA_SND_PCM_FORMAT_UNKNOWN) { - ma_free(pHWParams, &pDevice->pContext->allocationCallbacks); - ((ma_snd_pcm_close_proc)pDevice->pContext->alsa.snd_pcm_close)(pPCM); - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[ALSA] Format not supported. The device does not support any miniaudio formats."); - return MA_FORMAT_NOT_SUPPORTED; - } - } - - resultALSA = ((ma_snd_pcm_hw_params_set_format_proc)pDevice->pContext->alsa.snd_pcm_hw_params_set_format)(pPCM, pHWParams, formatALSA); - if (resultALSA < 0) { - ma_free(pHWParams, &pDevice->pContext->allocationCallbacks); - ((ma_snd_pcm_close_proc)pDevice->pContext->alsa.snd_pcm_close)(pPCM); - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[ALSA] Format not supported. snd_pcm_hw_params_set_format() failed."); - return ma_result_from_errno(-resultALSA); - } - - internalFormat = ma_format_from_alsa(formatALSA); - if (internalFormat == ma_format_unknown) { - ma_free(pHWParams, &pDevice->pContext->allocationCallbacks); - ((ma_snd_pcm_close_proc)pDevice->pContext->alsa.snd_pcm_close)(pPCM); - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[ALSA] The chosen format is not supported by miniaudio."); - return MA_FORMAT_NOT_SUPPORTED; - } - } - - /* Channels. */ - { - unsigned int channels = pDescriptor->channels; - if (channels == 0) { - channels = MA_DEFAULT_CHANNELS; - } - - resultALSA = ((ma_snd_pcm_hw_params_set_channels_near_proc)pDevice->pContext->alsa.snd_pcm_hw_params_set_channels_near)(pPCM, pHWParams, &channels); - if (resultALSA < 0) { - ma_free(pHWParams, &pDevice->pContext->allocationCallbacks); - ((ma_snd_pcm_close_proc)pDevice->pContext->alsa.snd_pcm_close)(pPCM); - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[ALSA] Failed to set channel count. snd_pcm_hw_params_set_channels_near() failed."); - return ma_result_from_errno(-resultALSA); - } - - internalChannels = (ma_uint32)channels; - } - - /* Sample Rate */ - { - unsigned int sampleRate; - - /* - It appears there's either a bug in ALSA, a bug in some drivers, or I'm doing something silly; but having resampling enabled causes - problems with some device configurations when used in conjunction with MMAP access mode. To fix this problem we need to disable - resampling. - - To reproduce this problem, open the "plug:dmix" device, and set the sample rate to 44100. Internally, it looks like dmix uses a - sample rate of 48000. The hardware parameters will get set correctly with no errors, but it looks like the 44100 -> 48000 resampling - doesn't work properly - but only with MMAP access mode. You will notice skipping/crackling in the audio, and it'll run at a slightly - faster rate. - - miniaudio has built-in support for sample rate conversion (albeit low quality at the moment), so disabling resampling should be fine - for us. The only problem is that it won't be taking advantage of any kind of hardware-accelerated resampling and it won't be very - good quality until I get a chance to improve the quality of miniaudio's software sample rate conversion. - - I don't currently know if the dmix plugin is the only one with this error. Indeed, this is the only one I've been able to reproduce - this error with. In the future, we may want to restrict the disabling of resampling to only known bad plugins. - */ - ((ma_snd_pcm_hw_params_set_rate_resample_proc)pDevice->pContext->alsa.snd_pcm_hw_params_set_rate_resample)(pPCM, pHWParams, 0); - - sampleRate = pDescriptor->sampleRate; - if (sampleRate == 0) { - sampleRate = MA_DEFAULT_SAMPLE_RATE; - } - - resultALSA = ((ma_snd_pcm_hw_params_set_rate_near_proc)pDevice->pContext->alsa.snd_pcm_hw_params_set_rate_near)(pPCM, pHWParams, &sampleRate, 0); - if (resultALSA < 0) { - ma_free(pHWParams, &pDevice->pContext->allocationCallbacks); - ((ma_snd_pcm_close_proc)pDevice->pContext->alsa.snd_pcm_close)(pPCM); - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[ALSA] Sample rate not supported. snd_pcm_hw_params_set_rate_near() failed."); - return ma_result_from_errno(-resultALSA); - } - - internalSampleRate = (ma_uint32)sampleRate; - } - - /* Periods. */ - { - ma_uint32 periods = pDescriptor->periodCount; - - resultALSA = ((ma_snd_pcm_hw_params_set_periods_near_proc)pDevice->pContext->alsa.snd_pcm_hw_params_set_periods_near)(pPCM, pHWParams, &periods, NULL); - if (resultALSA < 0) { - ma_free(pHWParams, &pDevice->pContext->allocationCallbacks); - ((ma_snd_pcm_close_proc)pDevice->pContext->alsa.snd_pcm_close)(pPCM); - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[ALSA] Failed to set period count. snd_pcm_hw_params_set_periods_near() failed."); - return ma_result_from_errno(-resultALSA); - } - - internalPeriods = periods; - } - - /* Buffer Size */ - { - ma_snd_pcm_uframes_t actualBufferSizeInFrames = ma_calculate_buffer_size_in_frames_from_descriptor(pDescriptor, internalSampleRate, pConfig->performanceProfile) * internalPeriods; - - resultALSA = ((ma_snd_pcm_hw_params_set_buffer_size_near_proc)pDevice->pContext->alsa.snd_pcm_hw_params_set_buffer_size_near)(pPCM, pHWParams, &actualBufferSizeInFrames); - if (resultALSA < 0) { - ma_free(pHWParams, &pDevice->pContext->allocationCallbacks); - ((ma_snd_pcm_close_proc)pDevice->pContext->alsa.snd_pcm_close)(pPCM); - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[ALSA] Failed to set buffer size for device. snd_pcm_hw_params_set_buffer_size() failed."); - return ma_result_from_errno(-resultALSA); - } - - internalPeriodSizeInFrames = actualBufferSizeInFrames / internalPeriods; - } - - /* Apply hardware parameters. */ - resultALSA = ((ma_snd_pcm_hw_params_proc)pDevice->pContext->alsa.snd_pcm_hw_params)(pPCM, pHWParams); - if (resultALSA < 0) { - ma_free(pHWParams, &pDevice->pContext->allocationCallbacks); - ((ma_snd_pcm_close_proc)pDevice->pContext->alsa.snd_pcm_close)(pPCM); - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[ALSA] Failed to set hardware parameters. snd_pcm_hw_params() failed."); - return ma_result_from_errno(-resultALSA); - } - - ma_free(pHWParams, &pDevice->pContext->allocationCallbacks); - pHWParams = NULL; - - - /* Software parameters. */ - pSWParams = (ma_snd_pcm_sw_params_t*)ma_calloc(((ma_snd_pcm_sw_params_sizeof_proc)pDevice->pContext->alsa.snd_pcm_sw_params_sizeof)(), &pDevice->pContext->allocationCallbacks); - if (pSWParams == NULL) { - ((ma_snd_pcm_close_proc)pDevice->pContext->alsa.snd_pcm_close)(pPCM); - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[ALSA] Failed to allocate memory for software parameters."); - return MA_OUT_OF_MEMORY; - } - - resultALSA = ((ma_snd_pcm_sw_params_current_proc)pDevice->pContext->alsa.snd_pcm_sw_params_current)(pPCM, pSWParams); - if (resultALSA < 0) { - ma_free(pSWParams, &pDevice->pContext->allocationCallbacks); - ((ma_snd_pcm_close_proc)pDevice->pContext->alsa.snd_pcm_close)(pPCM); - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[ALSA] Failed to initialize software parameters. snd_pcm_sw_params_current() failed."); - return ma_result_from_errno(-resultALSA); - } - - resultALSA = ((ma_snd_pcm_sw_params_set_avail_min_proc)pDevice->pContext->alsa.snd_pcm_sw_params_set_avail_min)(pPCM, pSWParams, ma_prev_power_of_2(internalPeriodSizeInFrames)); - if (resultALSA < 0) { - ma_free(pSWParams, &pDevice->pContext->allocationCallbacks); - ((ma_snd_pcm_close_proc)pDevice->pContext->alsa.snd_pcm_close)(pPCM); - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[ALSA] snd_pcm_sw_params_set_avail_min() failed."); - return ma_result_from_errno(-resultALSA); - } - - resultALSA = ((ma_snd_pcm_sw_params_get_boundary_proc)pDevice->pContext->alsa.snd_pcm_sw_params_get_boundary)(pSWParams, &bufferBoundary); - if (resultALSA < 0) { - bufferBoundary = internalPeriodSizeInFrames * internalPeriods; - } - - if (deviceType == ma_device_type_playback && !isUsingMMap) { /* Only playback devices in writei/readi mode need a start threshold. */ - /* - Subtle detail here with the start threshold. When in playback-only mode (no full-duplex) we can set the start threshold to - the size of a period. But for full-duplex we need to set it such that it is at least two periods. - */ - resultALSA = ((ma_snd_pcm_sw_params_set_start_threshold_proc)pDevice->pContext->alsa.snd_pcm_sw_params_set_start_threshold)(pPCM, pSWParams, internalPeriodSizeInFrames*2); - if (resultALSA < 0) { - ma_free(pSWParams, &pDevice->pContext->allocationCallbacks); - ((ma_snd_pcm_close_proc)pDevice->pContext->alsa.snd_pcm_close)(pPCM); - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[ALSA] Failed to set start threshold for playback device. snd_pcm_sw_params_set_start_threshold() failed."); - return ma_result_from_errno(-resultALSA); - } - - resultALSA = ((ma_snd_pcm_sw_params_set_stop_threshold_proc)pDevice->pContext->alsa.snd_pcm_sw_params_set_stop_threshold)(pPCM, pSWParams, bufferBoundary); - if (resultALSA < 0) { /* Set to boundary to loop instead of stop in the event of an xrun. */ - ma_free(pSWParams, &pDevice->pContext->allocationCallbacks); - ((ma_snd_pcm_close_proc)pDevice->pContext->alsa.snd_pcm_close)(pPCM); - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[ALSA] Failed to set stop threshold for playback device. snd_pcm_sw_params_set_stop_threshold() failed."); - return ma_result_from_errno(-resultALSA); - } - } - - resultALSA = ((ma_snd_pcm_sw_params_proc)pDevice->pContext->alsa.snd_pcm_sw_params)(pPCM, pSWParams); - if (resultALSA < 0) { - ma_free(pSWParams, &pDevice->pContext->allocationCallbacks); - ((ma_snd_pcm_close_proc)pDevice->pContext->alsa.snd_pcm_close)(pPCM); - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[ALSA] Failed to set software parameters. snd_pcm_sw_params() failed."); - return ma_result_from_errno(-resultALSA); - } - - ma_free(pSWParams, &pDevice->pContext->allocationCallbacks); - pSWParams = NULL; - - - /* Grab the internal channel map. For now we're not going to bother trying to change the channel map and instead just do it ourselves. */ - { - ma_snd_pcm_chmap_t* pChmap = NULL; - if (pDevice->pContext->alsa.snd_pcm_get_chmap != NULL) { - pChmap = ((ma_snd_pcm_get_chmap_proc)pDevice->pContext->alsa.snd_pcm_get_chmap)(pPCM); - } - - if (pChmap != NULL) { - ma_uint32 iChannel; - - /* There are cases where the returned channel map can have a different channel count than was returned by snd_pcm_hw_params_set_channels_near(). */ - if (pChmap->channels >= internalChannels) { - /* Drop excess channels. */ - for (iChannel = 0; iChannel < internalChannels; ++iChannel) { - internalChannelMap[iChannel] = ma_convert_alsa_channel_position_to_ma_channel(pChmap->pos[iChannel]); - } - } else { - ma_uint32 i; - - /* - Excess channels use defaults. Do an initial fill with defaults, overwrite the first pChmap->channels, validate to ensure there are no duplicate - channels. If validation fails, fall back to defaults. - */ - ma_bool32 isValid = MA_TRUE; - - /* Fill with defaults. */ - ma_channel_map_init_standard(ma_standard_channel_map_alsa, internalChannelMap, ma_countof(internalChannelMap), internalChannels); - - /* Overwrite first pChmap->channels channels. */ - for (iChannel = 0; iChannel < pChmap->channels; ++iChannel) { - internalChannelMap[iChannel] = ma_convert_alsa_channel_position_to_ma_channel(pChmap->pos[iChannel]); - } - - /* Validate. */ - for (i = 0; i < internalChannels && isValid; ++i) { - ma_uint32 j; - for (j = i+1; j < internalChannels; ++j) { - if (internalChannelMap[i] == internalChannelMap[j]) { - isValid = MA_FALSE; - break; - } - } - } - - /* If our channel map is invalid, fall back to defaults. */ - if (!isValid) { - ma_channel_map_init_standard(ma_standard_channel_map_alsa, internalChannelMap, ma_countof(internalChannelMap), internalChannels); - } - } - - free(pChmap); - pChmap = NULL; - } else { - /* Could not retrieve the channel map. Fall back to a hard-coded assumption. */ - ma_channel_map_init_standard(ma_standard_channel_map_alsa, internalChannelMap, ma_countof(internalChannelMap), internalChannels); - } - } - - - /* - We need to retrieve the poll descriptors so we can use poll() to wait for data to become - available for reading or writing. There's no well defined maximum for this so we're just going - to allocate this on the heap. - */ - pollDescriptorCount = ((ma_snd_pcm_poll_descriptors_count_proc)pDevice->pContext->alsa.snd_pcm_poll_descriptors_count)(pPCM); - if (pollDescriptorCount <= 0) { - ((ma_snd_pcm_close_proc)pDevice->pContext->alsa.snd_pcm_close)(pPCM); - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[ALSA] Failed to retrieve poll descriptors count."); - return MA_ERROR; - } - - pPollDescriptors = (struct pollfd*)ma_malloc(sizeof(*pPollDescriptors) * (pollDescriptorCount + 1), &pDevice->pContext->allocationCallbacks); /* +1 because we want room for the wakeup descriptor. */ - if (pPollDescriptors == NULL) { - ((ma_snd_pcm_close_proc)pDevice->pContext->alsa.snd_pcm_close)(pPCM); - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[ALSA] Failed to allocate memory for poll descriptors."); - return MA_OUT_OF_MEMORY; - } - - /* - We need an eventfd to wakeup from poll() and avoid a deadlock in situations where the driver - never returns from writei() and readi(). This has been observed with the "pulse" device. - */ - wakeupfd = eventfd(0, 0); - if (wakeupfd < 0) { - ma_free(pPollDescriptors, &pDevice->pContext->allocationCallbacks); - ((ma_snd_pcm_close_proc)pDevice->pContext->alsa.snd_pcm_close)(pPCM); - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[ALSA] Failed to create eventfd for poll wakeup."); - return ma_result_from_errno(errno); - } - - /* We'll place the wakeup fd at the start of the buffer. */ - pPollDescriptors[0].fd = wakeupfd; - pPollDescriptors[0].events = POLLIN; /* We only care about waiting to read from the wakeup file descriptor. */ - pPollDescriptors[0].revents = 0; - - /* We can now extract the PCM poll descriptors which we place after the wakeup descriptor. */ - pollDescriptorCount = ((ma_snd_pcm_poll_descriptors_proc)pDevice->pContext->alsa.snd_pcm_poll_descriptors)(pPCM, pPollDescriptors + 1, pollDescriptorCount); /* +1 because we want to place these descriptors after the wakeup descriptor. */ - if (pollDescriptorCount <= 0) { - close(wakeupfd); - ma_free(pPollDescriptors, &pDevice->pContext->allocationCallbacks); - ((ma_snd_pcm_close_proc)pDevice->pContext->alsa.snd_pcm_close)(pPCM); - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[ALSA] Failed to retrieve poll descriptors."); - return MA_ERROR; - } - - if (deviceType == ma_device_type_capture) { - pDevice->alsa.pollDescriptorCountCapture = pollDescriptorCount; - pDevice->alsa.pPollDescriptorsCapture = pPollDescriptors; - pDevice->alsa.wakeupfdCapture = wakeupfd; - } else { - pDevice->alsa.pollDescriptorCountPlayback = pollDescriptorCount; - pDevice->alsa.pPollDescriptorsPlayback = pPollDescriptors; - pDevice->alsa.wakeupfdPlayback = wakeupfd; - } - - - /* We're done. Prepare the device. */ - resultALSA = ((ma_snd_pcm_prepare_proc)pDevice->pContext->alsa.snd_pcm_prepare)(pPCM); - if (resultALSA < 0) { - close(wakeupfd); - ma_free(pPollDescriptors, &pDevice->pContext->allocationCallbacks); - ((ma_snd_pcm_close_proc)pDevice->pContext->alsa.snd_pcm_close)(pPCM); - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[ALSA] Failed to prepare device."); - return ma_result_from_errno(-resultALSA); - } - - - if (deviceType == ma_device_type_capture) { - pDevice->alsa.pPCMCapture = (ma_ptr)pPCM; - pDevice->alsa.isUsingMMapCapture = isUsingMMap; - } else { - pDevice->alsa.pPCMPlayback = (ma_ptr)pPCM; - pDevice->alsa.isUsingMMapPlayback = isUsingMMap; - } - - pDescriptor->format = internalFormat; - pDescriptor->channels = internalChannels; - pDescriptor->sampleRate = internalSampleRate; - ma_channel_map_copy(pDescriptor->channelMap, internalChannelMap, ma_min(internalChannels, MA_MAX_CHANNELS)); - pDescriptor->periodSizeInFrames = internalPeriodSizeInFrames; - pDescriptor->periodCount = internalPeriods; - - return MA_SUCCESS; -} - -static ma_result ma_device_init__alsa(ma_device* pDevice, const ma_device_config* pConfig, ma_device_descriptor* pDescriptorPlayback, ma_device_descriptor* pDescriptorCapture) -{ - MA_ASSERT(pDevice != NULL); - - MA_ZERO_OBJECT(&pDevice->alsa); - - if (pConfig->deviceType == ma_device_type_loopback) { - return MA_DEVICE_TYPE_NOT_SUPPORTED; - } - - if (pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) { - ma_result result = ma_device_init_by_type__alsa(pDevice, pConfig, pDescriptorCapture, ma_device_type_capture); - if (result != MA_SUCCESS) { - return result; - } - } - - if (pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) { - ma_result result = ma_device_init_by_type__alsa(pDevice, pConfig, pDescriptorPlayback, ma_device_type_playback); - if (result != MA_SUCCESS) { - return result; - } - } - - return MA_SUCCESS; -} - -static ma_result ma_device_start__alsa(ma_device* pDevice) -{ - int resultALSA; - - if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) { - resultALSA = ((ma_snd_pcm_start_proc)pDevice->pContext->alsa.snd_pcm_start)((ma_snd_pcm_t*)pDevice->alsa.pPCMCapture); - if (resultALSA < 0) { - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[ALSA] Failed to start capture device."); - return ma_result_from_errno(-resultALSA); - } - } - - if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) { - /* - When data is written to the device we wait for the device to get ready to receive data with poll(). In my testing - I have observed that poll() can sometimes block forever unless the device is started explicitly with snd_pcm_start() - or some data is written with snd_pcm_writei(). - - To resolve this I've decided to do an explicit start with snd_pcm_start(). The problem with this is that the device - is started without any data in the internal buffer which will result in an immediate underrun. If instead we were - to call into snd_pcm_writei() in an attempt to prevent the underrun, we would run the risk of a weird deadlock - issue as documented inside ma_device_write__alsa(). - */ - resultALSA = ((ma_snd_pcm_start_proc)pDevice->pContext->alsa.snd_pcm_start)((ma_snd_pcm_t*)pDevice->alsa.pPCMPlayback); - if (resultALSA < 0) { - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[ALSA] Failed to start playback device."); - return ma_result_from_errno(-resultALSA); - } - } - - return MA_SUCCESS; -} - -static ma_result ma_device_stop__alsa(ma_device* pDevice) -{ - /* - The stop callback will get called on the worker thread after read/write__alsa() has returned. At this point there is - a small chance that our wakeupfd has not been cleared. We'll clear that out now if applicable. - */ - int resultPoll; - int resultRead; - - if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) { - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "[ALSA] Dropping capture device...\n"); - ((ma_snd_pcm_drop_proc)pDevice->pContext->alsa.snd_pcm_drop)((ma_snd_pcm_t*)pDevice->alsa.pPCMCapture); - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "[ALSA] Dropping capture device successful.\n"); - - /* We need to prepare the device again, otherwise we won't be able to restart the device. */ - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "[ALSA] Preparing capture device...\n"); - if (((ma_snd_pcm_prepare_proc)pDevice->pContext->alsa.snd_pcm_prepare)((ma_snd_pcm_t*)pDevice->alsa.pPCMCapture) < 0) { - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "[ALSA] Preparing capture device failed.\n"); - } else { - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "[ALSA] Preparing capture device successful.\n"); - } - - /* Clear the wakeupfd. */ - resultPoll = poll((struct pollfd*)pDevice->alsa.pPollDescriptorsCapture, 1, 0); - if (resultPoll > 0) { - ma_uint64 t; - resultRead = read(((struct pollfd*)pDevice->alsa.pPollDescriptorsCapture)[0].fd, &t, sizeof(t)); - if (resultRead != sizeof(t)) { - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "[ALSA] Failed to read from capture wakeupfd. read() = %d\n", resultRead); - } - } - } - - if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) { - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "[ALSA] Dropping playback device...\n"); - ((ma_snd_pcm_drop_proc)pDevice->pContext->alsa.snd_pcm_drop)((ma_snd_pcm_t*)pDevice->alsa.pPCMPlayback); - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "[ALSA] Dropping playback device successful.\n"); - - /* We need to prepare the device again, otherwise we won't be able to restart the device. */ - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "[ALSA] Preparing playback device...\n"); - if (((ma_snd_pcm_prepare_proc)pDevice->pContext->alsa.snd_pcm_prepare)((ma_snd_pcm_t*)pDevice->alsa.pPCMPlayback) < 0) { - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "[ALSA] Preparing playback device failed.\n"); - } else { - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "[ALSA] Preparing playback device successful.\n"); - } - - /* Clear the wakeupfd. */ - resultPoll = poll((struct pollfd*)pDevice->alsa.pPollDescriptorsPlayback, 1, 0); - if (resultPoll > 0) { - ma_uint64 t; - resultRead = read(((struct pollfd*)pDevice->alsa.pPollDescriptorsPlayback)[0].fd, &t, sizeof(t)); - if (resultRead != sizeof(t)) { - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "[ALSA] Failed to read from playback wakeupfd. read() = %d\n", resultRead); - } - } - } - - return MA_SUCCESS; -} - -static ma_result ma_device_wait__alsa(ma_device* pDevice, ma_snd_pcm_t* pPCM, struct pollfd* pPollDescriptors, int pollDescriptorCount, short requiredEvent) -{ - for (;;) { - unsigned short revents; - int resultALSA; - int resultPoll = poll(pPollDescriptors, pollDescriptorCount, -1); - if (resultPoll < 0) { - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_WARNING, "[ALSA] poll() failed.\n"); - - /* - There have been reports that poll() is returning an error randomly and that instead of - returning an error, simply trying again will work. I'm experimenting with adopting this - advice. - */ - continue; - /*return ma_result_from_errno(errno);*/ - } - - /* - Before checking the ALSA poll descriptor flag we need to check if the wakeup descriptor - has had it's POLLIN flag set. If so, we need to actually read the data and then exit the - function. The wakeup descriptor will be the first item in the descriptors buffer. - */ - if ((pPollDescriptors[0].revents & POLLIN) != 0) { - ma_uint64 t; - int resultRead = read(pPollDescriptors[0].fd, &t, sizeof(t)); /* <-- Important that we read here so that the next write() does not block. */ - if (resultRead < 0) { - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[ALSA] read() failed.\n"); - return ma_result_from_errno(errno); - } - - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "[ALSA] POLLIN set for wakeupfd\n"); - return MA_DEVICE_NOT_STARTED; - } - - /* - Getting here means that some data should be able to be read. We need to use ALSA to - translate the revents flags for us. - */ - resultALSA = ((ma_snd_pcm_poll_descriptors_revents_proc)pDevice->pContext->alsa.snd_pcm_poll_descriptors_revents)(pPCM, pPollDescriptors + 1, pollDescriptorCount - 1, &revents); /* +1, -1 to ignore the wakeup descriptor. */ - if (resultALSA < 0) { - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[ALSA] snd_pcm_poll_descriptors_revents() failed.\n"); - return ma_result_from_errno(-resultALSA); - } - - if ((revents & POLLERR) != 0) { - ma_snd_pcm_state_t state = ((ma_snd_pcm_state_proc)pDevice->pContext->alsa.snd_pcm_state)(pPCM); - if (state == MA_SND_PCM_STATE_XRUN) { - /* The PCM is in a xrun state. This will be recovered from at a higher level. We can disregard this. */ - } else { - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_WARNING, "[ALSA] POLLERR detected. status = %d\n", ((ma_snd_pcm_state_proc)pDevice->pContext->alsa.snd_pcm_state)(pPCM)); - } - } - - if ((revents & requiredEvent) == requiredEvent) { - break; /* We're done. Data available for reading or writing. */ - } - } - - return MA_SUCCESS; -} - -static ma_result ma_device_wait_read__alsa(ma_device* pDevice) -{ - return ma_device_wait__alsa(pDevice, (ma_snd_pcm_t*)pDevice->alsa.pPCMCapture, (struct pollfd*)pDevice->alsa.pPollDescriptorsCapture, pDevice->alsa.pollDescriptorCountCapture + 1, POLLIN); /* +1 to account for the wakeup descriptor. */ -} - -static ma_result ma_device_wait_write__alsa(ma_device* pDevice) -{ - return ma_device_wait__alsa(pDevice, (ma_snd_pcm_t*)pDevice->alsa.pPCMPlayback, (struct pollfd*)pDevice->alsa.pPollDescriptorsPlayback, pDevice->alsa.pollDescriptorCountPlayback + 1, POLLOUT); /* +1 to account for the wakeup descriptor. */ -} - -static ma_result ma_device_read__alsa(ma_device* pDevice, void* pFramesOut, ma_uint32 frameCount, ma_uint32* pFramesRead) -{ - ma_snd_pcm_sframes_t resultALSA = 0; - - MA_ASSERT(pDevice != NULL); - MA_ASSERT(pFramesOut != NULL); - - if (pFramesRead != NULL) { - *pFramesRead = 0; - } - - while (ma_device_get_state(pDevice) == ma_device_state_started) { - ma_result result; - - /* The first thing to do is wait for data to become available for reading. This will return an error code if the device has been stopped. */ - result = ma_device_wait_read__alsa(pDevice); - if (result != MA_SUCCESS) { - return result; - } - - /* Getting here means we should have data available. */ - resultALSA = ((ma_snd_pcm_readi_proc)pDevice->pContext->alsa.snd_pcm_readi)((ma_snd_pcm_t*)pDevice->alsa.pPCMCapture, pFramesOut, frameCount); - if (resultALSA >= 0) { - break; /* Success. */ - } else { - if (resultALSA == -EAGAIN) { - /*ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "EGAIN (read)\n");*/ - continue; /* Try again. */ - } else if (resultALSA == -EPIPE) { - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "EPIPE (read)\n"); - - /* Overrun. Recover and try again. If this fails we need to return an error. */ - resultALSA = ((ma_snd_pcm_recover_proc)pDevice->pContext->alsa.snd_pcm_recover)((ma_snd_pcm_t*)pDevice->alsa.pPCMCapture, resultALSA, MA_TRUE); - if (resultALSA < 0) { - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[ALSA] Failed to recover device after overrun."); - return ma_result_from_errno((int)-resultALSA); - } - - resultALSA = ((ma_snd_pcm_start_proc)pDevice->pContext->alsa.snd_pcm_start)((ma_snd_pcm_t*)pDevice->alsa.pPCMCapture); - if (resultALSA < 0) { - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[ALSA] Failed to start device after underrun."); - return ma_result_from_errno((int)-resultALSA); - } - - continue; /* Try reading again. */ - } - } - } - - if (pFramesRead != NULL) { - *pFramesRead = resultALSA; - } - - return MA_SUCCESS; -} - -static ma_result ma_device_write__alsa(ma_device* pDevice, const void* pFrames, ma_uint32 frameCount, ma_uint32* pFramesWritten) -{ - ma_snd_pcm_sframes_t resultALSA = 0; - - MA_ASSERT(pDevice != NULL); - MA_ASSERT(pFrames != NULL); - - if (pFramesWritten != NULL) { - *pFramesWritten = 0; - } - - while (ma_device_get_state(pDevice) == ma_device_state_started) { - ma_result result; - - /* The first thing to do is wait for space to become available for writing. This will return an error code if the device has been stopped. */ - result = ma_device_wait_write__alsa(pDevice); - if (result != MA_SUCCESS) { - return result; - } - - resultALSA = ((ma_snd_pcm_writei_proc)pDevice->pContext->alsa.snd_pcm_writei)((ma_snd_pcm_t*)pDevice->alsa.pPCMPlayback, pFrames, frameCount); - if (resultALSA >= 0) { - break; /* Success. */ - } else { - if (resultALSA == -EAGAIN) { - /*ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "EGAIN (write)\n");*/ - continue; /* Try again. */ - } else if (resultALSA == -EPIPE) { - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "EPIPE (write)\n"); - - /* Underrun. Recover and try again. If this fails we need to return an error. */ - resultALSA = ((ma_snd_pcm_recover_proc)pDevice->pContext->alsa.snd_pcm_recover)((ma_snd_pcm_t*)pDevice->alsa.pPCMPlayback, resultALSA, MA_TRUE); /* MA_TRUE=silent (don't print anything on error). */ - if (resultALSA < 0) { - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[ALSA] Failed to recover device after underrun."); - return ma_result_from_errno((int)-resultALSA); - } - - /* - In my testing I have had a situation where writei() does not automatically restart the device even though I've set it - up as such in the software parameters. What will happen is writei() will block indefinitely even though the number of - frames is well beyond the auto-start threshold. To work around this I've needed to add an explicit start here. Not sure - if this is me just being stupid and not recovering the device properly, but this definitely feels like something isn't - quite right here. - */ - resultALSA = ((ma_snd_pcm_start_proc)pDevice->pContext->alsa.snd_pcm_start)((ma_snd_pcm_t*)pDevice->alsa.pPCMPlayback); - if (resultALSA < 0) { - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[ALSA] Failed to start device after underrun."); - return ma_result_from_errno((int)-resultALSA); - } - - continue; /* Try writing again. */ - } - } - } - - if (pFramesWritten != NULL) { - *pFramesWritten = resultALSA; - } - - return MA_SUCCESS; -} - -static ma_result ma_device_data_loop_wakeup__alsa(ma_device* pDevice) -{ - ma_uint64 t = 1; - int resultWrite = 0; - - MA_ASSERT(pDevice != NULL); - - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "[ALSA] Waking up...\n"); - - /* Write to an eventfd to trigger a wakeup from poll() and abort any reading or writing. */ - if (pDevice->alsa.pPollDescriptorsCapture != NULL) { - resultWrite = write(pDevice->alsa.wakeupfdCapture, &t, sizeof(t)); - } - if (pDevice->alsa.pPollDescriptorsPlayback != NULL) { - resultWrite = write(pDevice->alsa.wakeupfdPlayback, &t, sizeof(t)); - } - - if (resultWrite < 0) { - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[ALSA] write() failed.\n"); - return ma_result_from_errno(errno); - } - - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "[ALSA] Waking up completed successfully.\n"); - - return MA_SUCCESS; -} - -static ma_result ma_context_uninit__alsa(ma_context* pContext) -{ - MA_ASSERT(pContext != NULL); - MA_ASSERT(pContext->backend == ma_backend_alsa); - - /* Clean up memory for memory leak checkers. */ - ((ma_snd_config_update_free_global_proc)pContext->alsa.snd_config_update_free_global)(); - -#ifndef MA_NO_RUNTIME_LINKING - ma_dlclose(ma_context_get_log(pContext), pContext->alsa.asoundSO); -#endif - - ma_mutex_uninit(&pContext->alsa.internalDeviceEnumLock); - - return MA_SUCCESS; -} - -static ma_result ma_context_init__alsa(ma_context* pContext, const ma_context_config* pConfig, ma_backend_callbacks* pCallbacks) -{ - ma_result result; -#ifndef MA_NO_RUNTIME_LINKING - const char* libasoundNames[] = { - "libasound.so.2", - "libasound.so" - }; - size_t i; - - for (i = 0; i < ma_countof(libasoundNames); ++i) { - pContext->alsa.asoundSO = ma_dlopen(ma_context_get_log(pContext), libasoundNames[i]); - if (pContext->alsa.asoundSO != NULL) { - break; - } - } - - if (pContext->alsa.asoundSO == NULL) { - ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_DEBUG, "[ALSA] Failed to open shared object.\n"); - return MA_NO_BACKEND; - } - - pContext->alsa.snd_pcm_open = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_open"); - pContext->alsa.snd_pcm_close = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_close"); - pContext->alsa.snd_pcm_hw_params_sizeof = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_hw_params_sizeof"); - pContext->alsa.snd_pcm_hw_params_any = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_hw_params_any"); - pContext->alsa.snd_pcm_hw_params_set_format = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_hw_params_set_format"); - pContext->alsa.snd_pcm_hw_params_set_format_first = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_hw_params_set_format_first"); - pContext->alsa.snd_pcm_hw_params_get_format_mask = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_hw_params_get_format_mask"); - pContext->alsa.snd_pcm_hw_params_set_channels = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_hw_params_set_channels"); - pContext->alsa.snd_pcm_hw_params_set_channels_near = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_hw_params_set_channels_near"); - pContext->alsa.snd_pcm_hw_params_set_channels_minmax = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_hw_params_set_channels_minmax"); - pContext->alsa.snd_pcm_hw_params_set_rate_resample = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_hw_params_set_rate_resample"); - pContext->alsa.snd_pcm_hw_params_set_rate = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_hw_params_set_rate"); - pContext->alsa.snd_pcm_hw_params_set_rate_near = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_hw_params_set_rate_near"); - pContext->alsa.snd_pcm_hw_params_set_buffer_size_near = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_hw_params_set_buffer_size_near"); - pContext->alsa.snd_pcm_hw_params_set_periods_near = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_hw_params_set_periods_near"); - pContext->alsa.snd_pcm_hw_params_set_access = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_hw_params_set_access"); - pContext->alsa.snd_pcm_hw_params_get_format = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_hw_params_get_format"); - pContext->alsa.snd_pcm_hw_params_get_channels = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_hw_params_get_channels"); - pContext->alsa.snd_pcm_hw_params_get_channels_min = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_hw_params_get_channels_min"); - pContext->alsa.snd_pcm_hw_params_get_channels_max = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_hw_params_get_channels_max"); - pContext->alsa.snd_pcm_hw_params_get_rate = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_hw_params_get_rate"); - pContext->alsa.snd_pcm_hw_params_get_rate_min = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_hw_params_get_rate_min"); - pContext->alsa.snd_pcm_hw_params_get_rate_max = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_hw_params_get_rate_max"); - pContext->alsa.snd_pcm_hw_params_get_buffer_size = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_hw_params_get_buffer_size"); - pContext->alsa.snd_pcm_hw_params_get_periods = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_hw_params_get_periods"); - pContext->alsa.snd_pcm_hw_params_get_access = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_hw_params_get_access"); - pContext->alsa.snd_pcm_hw_params_test_format = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_hw_params_test_format"); - pContext->alsa.snd_pcm_hw_params_test_channels = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_hw_params_test_channels"); - pContext->alsa.snd_pcm_hw_params_test_rate = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_hw_params_test_rate"); - pContext->alsa.snd_pcm_hw_params = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_hw_params"); - pContext->alsa.snd_pcm_sw_params_sizeof = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_sw_params_sizeof"); - pContext->alsa.snd_pcm_sw_params_current = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_sw_params_current"); - pContext->alsa.snd_pcm_sw_params_get_boundary = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_sw_params_get_boundary"); - pContext->alsa.snd_pcm_sw_params_set_avail_min = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_sw_params_set_avail_min"); - pContext->alsa.snd_pcm_sw_params_set_start_threshold = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_sw_params_set_start_threshold"); - pContext->alsa.snd_pcm_sw_params_set_stop_threshold = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_sw_params_set_stop_threshold"); - pContext->alsa.snd_pcm_sw_params = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_sw_params"); - pContext->alsa.snd_pcm_format_mask_sizeof = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_format_mask_sizeof"); - pContext->alsa.snd_pcm_format_mask_test = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_format_mask_test"); - pContext->alsa.snd_pcm_get_chmap = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_get_chmap"); - pContext->alsa.snd_pcm_state = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_state"); - pContext->alsa.snd_pcm_prepare = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_prepare"); - pContext->alsa.snd_pcm_start = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_start"); - pContext->alsa.snd_pcm_drop = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_drop"); - pContext->alsa.snd_pcm_drain = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_drain"); - pContext->alsa.snd_pcm_reset = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_reset"); - pContext->alsa.snd_device_name_hint = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_device_name_hint"); - pContext->alsa.snd_device_name_get_hint = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_device_name_get_hint"); - pContext->alsa.snd_card_get_index = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_card_get_index"); - pContext->alsa.snd_device_name_free_hint = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_device_name_free_hint"); - pContext->alsa.snd_pcm_mmap_begin = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_mmap_begin"); - pContext->alsa.snd_pcm_mmap_commit = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_mmap_commit"); - pContext->alsa.snd_pcm_recover = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_recover"); - pContext->alsa.snd_pcm_readi = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_readi"); - pContext->alsa.snd_pcm_writei = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_writei"); - pContext->alsa.snd_pcm_avail = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_avail"); - pContext->alsa.snd_pcm_avail_update = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_avail_update"); - pContext->alsa.snd_pcm_wait = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_wait"); - pContext->alsa.snd_pcm_nonblock = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_nonblock"); - pContext->alsa.snd_pcm_info = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_info"); - pContext->alsa.snd_pcm_info_sizeof = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_info_sizeof"); - pContext->alsa.snd_pcm_info_get_name = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_info_get_name"); - pContext->alsa.snd_pcm_poll_descriptors = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_poll_descriptors"); - pContext->alsa.snd_pcm_poll_descriptors_count = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_poll_descriptors_count"); - pContext->alsa.snd_pcm_poll_descriptors_revents = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_poll_descriptors_revents"); - pContext->alsa.snd_config_update_free_global = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_config_update_free_global"); -#else - /* The system below is just for type safety. */ - ma_snd_pcm_open_proc _snd_pcm_open = snd_pcm_open; - ma_snd_pcm_close_proc _snd_pcm_close = snd_pcm_close; - ma_snd_pcm_hw_params_sizeof_proc _snd_pcm_hw_params_sizeof = snd_pcm_hw_params_sizeof; - ma_snd_pcm_hw_params_any_proc _snd_pcm_hw_params_any = snd_pcm_hw_params_any; - ma_snd_pcm_hw_params_set_format_proc _snd_pcm_hw_params_set_format = snd_pcm_hw_params_set_format; - ma_snd_pcm_hw_params_set_format_first_proc _snd_pcm_hw_params_set_format_first = snd_pcm_hw_params_set_format_first; - ma_snd_pcm_hw_params_get_format_mask_proc _snd_pcm_hw_params_get_format_mask = snd_pcm_hw_params_get_format_mask; - ma_snd_pcm_hw_params_set_channels_proc _snd_pcm_hw_params_set_channels = snd_pcm_hw_params_set_channels; - ma_snd_pcm_hw_params_set_channels_near_proc _snd_pcm_hw_params_set_channels_near = snd_pcm_hw_params_set_channels_near; - ma_snd_pcm_hw_params_set_rate_resample_proc _snd_pcm_hw_params_set_rate_resample = snd_pcm_hw_params_set_rate_resample; - ma_snd_pcm_hw_params_set_rate_near _snd_pcm_hw_params_set_rate = snd_pcm_hw_params_set_rate; - ma_snd_pcm_hw_params_set_rate_near_proc _snd_pcm_hw_params_set_rate_near = snd_pcm_hw_params_set_rate_near; - ma_snd_pcm_hw_params_set_rate_minmax_proc _snd_pcm_hw_params_set_rate_minmax = snd_pcm_hw_params_set_rate_minmax; - ma_snd_pcm_hw_params_set_buffer_size_near_proc _snd_pcm_hw_params_set_buffer_size_near = snd_pcm_hw_params_set_buffer_size_near; - ma_snd_pcm_hw_params_set_periods_near_proc _snd_pcm_hw_params_set_periods_near = snd_pcm_hw_params_set_periods_near; - ma_snd_pcm_hw_params_set_access_proc _snd_pcm_hw_params_set_access = snd_pcm_hw_params_set_access; - ma_snd_pcm_hw_params_get_format_proc _snd_pcm_hw_params_get_format = snd_pcm_hw_params_get_format; - ma_snd_pcm_hw_params_get_channels_proc _snd_pcm_hw_params_get_channels = snd_pcm_hw_params_get_channels; - ma_snd_pcm_hw_params_get_channels_min_proc _snd_pcm_hw_params_get_channels_min = snd_pcm_hw_params_get_channels_min; - ma_snd_pcm_hw_params_get_channels_max_proc _snd_pcm_hw_params_get_channels_max = snd_pcm_hw_params_get_channels_max; - ma_snd_pcm_hw_params_get_rate_proc _snd_pcm_hw_params_get_rate = snd_pcm_hw_params_get_rate; - ma_snd_pcm_hw_params_get_rate_min_proc _snd_pcm_hw_params_get_rate_min = snd_pcm_hw_params_get_rate_min; - ma_snd_pcm_hw_params_get_rate_max_proc _snd_pcm_hw_params_get_rate_max = snd_pcm_hw_params_get_rate_max; - ma_snd_pcm_hw_params_get_buffer_size_proc _snd_pcm_hw_params_get_buffer_size = snd_pcm_hw_params_get_buffer_size; - ma_snd_pcm_hw_params_get_periods_proc _snd_pcm_hw_params_get_periods = snd_pcm_hw_params_get_periods; - ma_snd_pcm_hw_params_get_access_proc _snd_pcm_hw_params_get_access = snd_pcm_hw_params_get_access; - ma_snd_pcm_hw_params_test_format_proc _snd_pcm_hw_params_test_format = snd_pcm_hw_params_test_format; - ma_snd_pcm_hw_params_test_channels_proc _snd_pcm_hw_params_test_channels = snd_pcm_hw_params_test_channels; - ma_snd_pcm_hw_params_test_rate_proc _snd_pcm_hw_params_test_rate = snd_pcm_hw_params_test_rate; - ma_snd_pcm_hw_params_proc _snd_pcm_hw_params = snd_pcm_hw_params; - ma_snd_pcm_sw_params_sizeof_proc _snd_pcm_sw_params_sizeof = snd_pcm_sw_params_sizeof; - ma_snd_pcm_sw_params_current_proc _snd_pcm_sw_params_current = snd_pcm_sw_params_current; - ma_snd_pcm_sw_params_get_boundary_proc _snd_pcm_sw_params_get_boundary = snd_pcm_sw_params_get_boundary; - ma_snd_pcm_sw_params_set_avail_min_proc _snd_pcm_sw_params_set_avail_min = snd_pcm_sw_params_set_avail_min; - ma_snd_pcm_sw_params_set_start_threshold_proc _snd_pcm_sw_params_set_start_threshold = snd_pcm_sw_params_set_start_threshold; - ma_snd_pcm_sw_params_set_stop_threshold_proc _snd_pcm_sw_params_set_stop_threshold = snd_pcm_sw_params_set_stop_threshold; - ma_snd_pcm_sw_params_proc _snd_pcm_sw_params = snd_pcm_sw_params; - ma_snd_pcm_format_mask_sizeof_proc _snd_pcm_format_mask_sizeof = snd_pcm_format_mask_sizeof; - ma_snd_pcm_format_mask_test_proc _snd_pcm_format_mask_test = snd_pcm_format_mask_test; - ma_snd_pcm_get_chmap_proc _snd_pcm_get_chmap = snd_pcm_get_chmap; - ma_snd_pcm_state_proc _snd_pcm_state = snd_pcm_state; - ma_snd_pcm_prepare_proc _snd_pcm_prepare = snd_pcm_prepare; - ma_snd_pcm_start_proc _snd_pcm_start = snd_pcm_start; - ma_snd_pcm_drop_proc _snd_pcm_drop = snd_pcm_drop; - ma_snd_pcm_drain_proc _snd_pcm_drain = snd_pcm_drain; - ma_snd_pcm_reset_proc _snd_pcm_reset = snd_pcm_reset; - ma_snd_device_name_hint_proc _snd_device_name_hint = snd_device_name_hint; - ma_snd_device_name_get_hint_proc _snd_device_name_get_hint = snd_device_name_get_hint; - ma_snd_card_get_index_proc _snd_card_get_index = snd_card_get_index; - ma_snd_device_name_free_hint_proc _snd_device_name_free_hint = snd_device_name_free_hint; - ma_snd_pcm_mmap_begin_proc _snd_pcm_mmap_begin = snd_pcm_mmap_begin; - ma_snd_pcm_mmap_commit_proc _snd_pcm_mmap_commit = snd_pcm_mmap_commit; - ma_snd_pcm_recover_proc _snd_pcm_recover = snd_pcm_recover; - ma_snd_pcm_readi_proc _snd_pcm_readi = snd_pcm_readi; - ma_snd_pcm_writei_proc _snd_pcm_writei = snd_pcm_writei; - ma_snd_pcm_avail_proc _snd_pcm_avail = snd_pcm_avail; - ma_snd_pcm_avail_update_proc _snd_pcm_avail_update = snd_pcm_avail_update; - ma_snd_pcm_wait_proc _snd_pcm_wait = snd_pcm_wait; - ma_snd_pcm_nonblock_proc _snd_pcm_nonblock = snd_pcm_nonblock; - ma_snd_pcm_info_proc _snd_pcm_info = snd_pcm_info; - ma_snd_pcm_info_sizeof_proc _snd_pcm_info_sizeof = snd_pcm_info_sizeof; - ma_snd_pcm_info_get_name_proc _snd_pcm_info_get_name = snd_pcm_info_get_name; - ma_snd_pcm_poll_descriptors _snd_pcm_poll_descriptors = snd_pcm_poll_descriptors; - ma_snd_pcm_poll_descriptors_count _snd_pcm_poll_descriptors_count = snd_pcm_poll_descriptors_count; - ma_snd_pcm_poll_descriptors_revents _snd_pcm_poll_descriptors_revents = snd_pcm_poll_descriptors_revents; - ma_snd_config_update_free_global_proc _snd_config_update_free_global = snd_config_update_free_global; - - pContext->alsa.snd_pcm_open = (ma_proc)_snd_pcm_open; - pContext->alsa.snd_pcm_close = (ma_proc)_snd_pcm_close; - pContext->alsa.snd_pcm_hw_params_sizeof = (ma_proc)_snd_pcm_hw_params_sizeof; - pContext->alsa.snd_pcm_hw_params_any = (ma_proc)_snd_pcm_hw_params_any; - pContext->alsa.snd_pcm_hw_params_set_format = (ma_proc)_snd_pcm_hw_params_set_format; - pContext->alsa.snd_pcm_hw_params_set_format_first = (ma_proc)_snd_pcm_hw_params_set_format_first; - pContext->alsa.snd_pcm_hw_params_get_format_mask = (ma_proc)_snd_pcm_hw_params_get_format_mask; - pContext->alsa.snd_pcm_hw_params_set_channels = (ma_proc)_snd_pcm_hw_params_set_channels; - pContext->alsa.snd_pcm_hw_params_set_channels_near = (ma_proc)_snd_pcm_hw_params_set_channels_near; - pContext->alsa.snd_pcm_hw_params_set_channels_minmax = (ma_proc)_snd_pcm_hw_params_set_channels_minmax; - pContext->alsa.snd_pcm_hw_params_set_rate_resample = (ma_proc)_snd_pcm_hw_params_set_rate_resample; - pContext->alsa.snd_pcm_hw_params_set_rate = (ma_proc)_snd_pcm_hw_params_set_rate; - pContext->alsa.snd_pcm_hw_params_set_rate_near = (ma_proc)_snd_pcm_hw_params_set_rate_near; - pContext->alsa.snd_pcm_hw_params_set_buffer_size_near = (ma_proc)_snd_pcm_hw_params_set_buffer_size_near; - pContext->alsa.snd_pcm_hw_params_set_periods_near = (ma_proc)_snd_pcm_hw_params_set_periods_near; - pContext->alsa.snd_pcm_hw_params_set_access = (ma_proc)_snd_pcm_hw_params_set_access; - pContext->alsa.snd_pcm_hw_params_get_format = (ma_proc)_snd_pcm_hw_params_get_format; - pContext->alsa.snd_pcm_hw_params_get_channels = (ma_proc)_snd_pcm_hw_params_get_channels; - pContext->alsa.snd_pcm_hw_params_get_channels_min = (ma_proc)_snd_pcm_hw_params_get_channels_min; - pContext->alsa.snd_pcm_hw_params_get_channels_max = (ma_proc)_snd_pcm_hw_params_get_channels_max; - pContext->alsa.snd_pcm_hw_params_get_rate = (ma_proc)_snd_pcm_hw_params_get_rate; - pContext->alsa.snd_pcm_hw_params_get_rate_min = (ma_proc)_snd_pcm_hw_params_get_rate_min; - pContext->alsa.snd_pcm_hw_params_get_rate_max = (ma_proc)_snd_pcm_hw_params_get_rate_max; - pContext->alsa.snd_pcm_hw_params_get_buffer_size = (ma_proc)_snd_pcm_hw_params_get_buffer_size; - pContext->alsa.snd_pcm_hw_params_get_periods = (ma_proc)_snd_pcm_hw_params_get_periods; - pContext->alsa.snd_pcm_hw_params_get_access = (ma_proc)_snd_pcm_hw_params_get_access; - pContext->alsa.snd_pcm_hw_params_test_format = (ma_proc)_snd_pcm_hw_params_test_format; - pContext->alsa.snd_pcm_hw_params_test_channels = (ma_proc)_snd_pcm_hw_params_test_channels; - pContext->alsa.snd_pcm_hw_params_test_rate = (ma_proc)_snd_pcm_hw_params_test_rate; - pContext->alsa.snd_pcm_hw_params = (ma_proc)_snd_pcm_hw_params; - pContext->alsa.snd_pcm_sw_params_sizeof = (ma_proc)_snd_pcm_sw_params_sizeof; - pContext->alsa.snd_pcm_sw_params_current = (ma_proc)_snd_pcm_sw_params_current; - pContext->alsa.snd_pcm_sw_params_get_boundary = (ma_proc)_snd_pcm_sw_params_get_boundary; - pContext->alsa.snd_pcm_sw_params_set_avail_min = (ma_proc)_snd_pcm_sw_params_set_avail_min; - pContext->alsa.snd_pcm_sw_params_set_start_threshold = (ma_proc)_snd_pcm_sw_params_set_start_threshold; - pContext->alsa.snd_pcm_sw_params_set_stop_threshold = (ma_proc)_snd_pcm_sw_params_set_stop_threshold; - pContext->alsa.snd_pcm_sw_params = (ma_proc)_snd_pcm_sw_params; - pContext->alsa.snd_pcm_format_mask_sizeof = (ma_proc)_snd_pcm_format_mask_sizeof; - pContext->alsa.snd_pcm_format_mask_test = (ma_proc)_snd_pcm_format_mask_test; - pContext->alsa.snd_pcm_get_chmap = (ma_proc)_snd_pcm_get_chmap; - pContext->alsa.snd_pcm_state = (ma_proc)_snd_pcm_state; - pContext->alsa.snd_pcm_prepare = (ma_proc)_snd_pcm_prepare; - pContext->alsa.snd_pcm_start = (ma_proc)_snd_pcm_start; - pContext->alsa.snd_pcm_drop = (ma_proc)_snd_pcm_drop; - pContext->alsa.snd_pcm_drain = (ma_proc)_snd_pcm_drain; - pContext->alsa.snd_pcm_reset = (ma_proc)_snd_pcm_reset; - pContext->alsa.snd_device_name_hint = (ma_proc)_snd_device_name_hint; - pContext->alsa.snd_device_name_get_hint = (ma_proc)_snd_device_name_get_hint; - pContext->alsa.snd_card_get_index = (ma_proc)_snd_card_get_index; - pContext->alsa.snd_device_name_free_hint = (ma_proc)_snd_device_name_free_hint; - pContext->alsa.snd_pcm_mmap_begin = (ma_proc)_snd_pcm_mmap_begin; - pContext->alsa.snd_pcm_mmap_commit = (ma_proc)_snd_pcm_mmap_commit; - pContext->alsa.snd_pcm_recover = (ma_proc)_snd_pcm_recover; - pContext->alsa.snd_pcm_readi = (ma_proc)_snd_pcm_readi; - pContext->alsa.snd_pcm_writei = (ma_proc)_snd_pcm_writei; - pContext->alsa.snd_pcm_avail = (ma_proc)_snd_pcm_avail; - pContext->alsa.snd_pcm_avail_update = (ma_proc)_snd_pcm_avail_update; - pContext->alsa.snd_pcm_wait = (ma_proc)_snd_pcm_wait; - pContext->alsa.snd_pcm_nonblock = (ma_proc)_snd_pcm_nonblock; - pContext->alsa.snd_pcm_info = (ma_proc)_snd_pcm_info; - pContext->alsa.snd_pcm_info_sizeof = (ma_proc)_snd_pcm_info_sizeof; - pContext->alsa.snd_pcm_info_get_name = (ma_proc)_snd_pcm_info_get_name; - pContext->alsa.snd_pcm_poll_descriptors = (ma_proc)_snd_pcm_poll_descriptors; - pContext->alsa.snd_pcm_poll_descriptors_count = (ma_proc)_snd_pcm_poll_descriptors_count; - pContext->alsa.snd_pcm_poll_descriptors_revents = (ma_proc)_snd_pcm_poll_descriptors_revents; - pContext->alsa.snd_config_update_free_global = (ma_proc)_snd_config_update_free_global; -#endif - - pContext->alsa.useVerboseDeviceEnumeration = pConfig->alsa.useVerboseDeviceEnumeration; - - result = ma_mutex_init(&pContext->alsa.internalDeviceEnumLock); - if (result != MA_SUCCESS) { - ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[ALSA] WARNING: Failed to initialize mutex for internal device enumeration."); - return result; - } - - pCallbacks->onContextInit = ma_context_init__alsa; - pCallbacks->onContextUninit = ma_context_uninit__alsa; - pCallbacks->onContextEnumerateDevices = ma_context_enumerate_devices__alsa; - pCallbacks->onContextGetDeviceInfo = ma_context_get_device_info__alsa; - pCallbacks->onDeviceInit = ma_device_init__alsa; - pCallbacks->onDeviceUninit = ma_device_uninit__alsa; - pCallbacks->onDeviceStart = ma_device_start__alsa; - pCallbacks->onDeviceStop = ma_device_stop__alsa; - pCallbacks->onDeviceRead = ma_device_read__alsa; - pCallbacks->onDeviceWrite = ma_device_write__alsa; - pCallbacks->onDeviceDataLoop = NULL; - pCallbacks->onDeviceDataLoopWakeup = ma_device_data_loop_wakeup__alsa; - - return MA_SUCCESS; -} -#endif /* MA_HAS_ALSA */ - - - -/****************************************************************************** - -PulseAudio Backend - -******************************************************************************/ -#ifdef MA_HAS_PULSEAUDIO -/* -The PulseAudio API, along with Apple's Core Audio, is the worst of the mainstream audio APIs. This is a brief description of what's going on -in the PulseAudio backend. I apologize if this gets a bit ranty for your liking - you might want to skip this discussion. - -PulseAudio has something they call the "Simple API", which unfortunately isn't suitable for miniaudio. I've not seen anywhere where it -allows you to enumerate over devices, nor does it seem to support the ability to stop and start streams. Looking at the documentation, it -appears as though the stream is constantly running and you prevent sound from being emitted or captured by simply not calling the read or -write functions. This is not a professional solution as it would be much better to *actually* stop the underlying stream. Perhaps the -simple API has some smarts to do this automatically, but I'm not sure. Another limitation with the simple API is that it seems inefficient -when you want to have multiple streams to a single context. For these reasons, miniaudio is not using the simple API. - -Since we're not using the simple API, we're left with the asynchronous API as our only other option. And boy, is this where it starts to -get fun, and I don't mean that in a good way... - -The problems start with the very name of the API - "asynchronous". Yes, this is an asynchronous oriented API which means your commands -don't immediately take effect. You instead need to issue your commands, and then wait for them to complete. The waiting mechanism is -enabled through the use of a "main loop". In the asynchronous API you cannot get away from the main loop, and the main loop is where almost -all of PulseAudio's problems stem from. - -When you first initialize PulseAudio you need an object referred to as "main loop". You can implement this yourself by defining your own -vtable, but it's much easier to just use one of the built-in main loop implementations. There's two generic implementations called -pa_mainloop and pa_threaded_mainloop, and another implementation specific to GLib called pa_glib_mainloop. We're using pa_threaded_mainloop -because it simplifies management of the worker thread. The idea of the main loop object is pretty self explanatory - you're supposed to use -it to implement a worker thread which runs in a loop. The main loop is where operations are actually executed. - -To initialize the main loop, you just use `pa_threaded_mainloop_new()`. This is the first function you'll call. You can then get a pointer -to the vtable with `pa_threaded_mainloop_get_api()` (the main loop vtable is called `pa_mainloop_api`). Again, you can bypass the threaded -main loop object entirely and just implement `pa_mainloop_api` directly, but there's no need for it unless you're doing something extremely -specialized such as if you want to integrate it into your application's existing main loop infrastructure. - -(EDIT 2021-01-26: miniaudio is no longer using `pa_threaded_mainloop` due to this issue: https://github.com/mackron/miniaudio/issues/262. -It is now using `pa_mainloop` which turns out to be a simpler solution anyway. The rest of this rant still applies, however.) - -Once you have your main loop vtable (the `pa_mainloop_api` object) you can create the PulseAudio context. This is very similar to -miniaudio's context and they map to each other quite well. You have one context to many streams, which is basically the same as miniaudio's -one `ma_context` to many `ma_device`s. Here's where it starts to get annoying, however. When you first create the PulseAudio context, which -is done with `pa_context_new()`, it's not actually connected to anything. When you connect, you call `pa_context_connect()`. However, if -you remember, PulseAudio is an asynchronous API. That means you cannot just assume the context is connected after `pa_context_context()` -has returned. You instead need to wait for it to connect. To do this, you need to either wait for a callback to get fired, which you can -set with `pa_context_set_state_callback()`, or you can continuously poll the context's state. Either way, you need to run this in a loop. -All objects from here out are created from the context, and, I believe, you can't be creating these objects until the context is connected. -This waiting loop is therefore unavoidable. In order for the waiting to ever complete, however, the main loop needs to be running. Before -attempting to connect the context, the main loop needs to be started with `pa_threaded_mainloop_start()`. - -The reason for this asynchronous design is to support cases where you're connecting to a remote server, say through a local network or an -internet connection. However, the *VAST* majority of cases don't involve this at all - they just connect to a local "server" running on the -host machine. The fact that this would be the default rather than making `pa_context_connect()` synchronous tends to boggle the mind. - -Once the context has been created and connected you can start creating a stream. A PulseAudio stream is analogous to miniaudio's device. -The initialization of a stream is fairly standard - you configure some attributes (analogous to miniaudio's device config) and then call -`pa_stream_new()` to actually create it. Here is where we start to get into "operations". When configuring the stream, you can get -information about the source (such as sample format, sample rate, etc.), however it's not synchronous. Instead, a `pa_operation` object -is returned from `pa_context_get_source_info_by_name()` (capture) or `pa_context_get_sink_info_by_name()` (playback). Then, you need to -run a loop (again!) to wait for the operation to complete which you can determine via a callback or polling, just like we did with the -context. Then, as an added bonus, you need to decrement the reference counter of the `pa_operation` object to ensure memory is cleaned up. -All of that just to retrieve basic information about a device! - -Once the basic information about the device has been retrieved, miniaudio can now create the stream with `ma_stream_new()`. Like the -context, this needs to be connected. But we need to be careful here, because we're now about to introduce one of the most horrific design -choices in PulseAudio. - -PulseAudio allows you to specify a callback that is fired when data can be written to or read from a stream. The language is important here -because PulseAudio takes it literally, specifically the "can be". You would think these callbacks would be appropriate as the place for -writing and reading data to and from the stream, and that would be right, except when it's not. When you initialize the stream, you can -set a flag that tells PulseAudio to not start the stream automatically. This is required because miniaudio does not auto-start devices -straight after initialization - you need to call `ma_device_start()` manually. The problem is that even when this flag is specified, -PulseAudio will immediately fire its write or read callback. This is *technically* correct (based on the wording in the documentation) -because indeed, data *can* be written at this point. The problem is that it's not *practical*. It makes sense that the write/read callback -would be where a program will want to write or read data to or from the stream, but when it's called before the application has even -requested that the stream be started, it's just not practical because the program probably isn't ready for any kind of data delivery at -that point (it may still need to load files or whatnot). Instead, this callback should only be fired when the application requests the -stream be started which is how it works with literally *every* other callback-based audio API. Since miniaudio forbids firing of the data -callback until the device has been started (as it should be with *all* callback based APIs), logic needs to be added to ensure miniaudio -doesn't just blindly fire the application-defined data callback from within the PulseAudio callback before the stream has actually been -started. The device state is used for this - if the state is anything other than `ma_device_state_starting` or `ma_device_state_started`, the main data -callback is not fired. - -This, unfortunately, is not the end of the problems with the PulseAudio write callback. Any normal callback based audio API will -continuously fire the callback at regular intervals based on the size of the internal buffer. This will only ever be fired when the device -is running, and will be fired regardless of whether or not the user actually wrote anything to the device/stream. This not the case in -PulseAudio. In PulseAudio, the data callback will *only* be called if you wrote something to it previously. That means, if you don't call -`pa_stream_write()`, the callback will not get fired. On the surface you wouldn't think this would matter because you should be always -writing data, and if you don't have anything to write, just write silence. That's fine until you want to drain the stream. You see, if -you're continuously writing data to the stream, the stream will never get drained! That means in order to drain the stream, you need to -*not* write data to it! But remember, when you don't write data to the stream, the callback won't get fired again! Why is draining -important? Because that's how we've defined stopping to work in miniaudio. In miniaudio, stopping the device requires it to be drained -before returning from ma_device_stop(). So we've stopped the device, which requires us to drain, but draining requires us to *not* write -data to the stream (or else it won't ever complete draining), but not writing to the stream means the callback won't get fired again! - -This becomes a problem when stopping and then restarting the device. When the device is stopped, it's drained, which requires us to *not* -write anything to the stream. But then, since we didn't write anything to it, the write callback will *never* get called again if we just -resume the stream naively. This means that starting the stream requires us to write data to the stream from outside the callback. This -disconnect is something PulseAudio has got seriously wrong - there should only ever be a single source of data delivery, that being the -callback. (I have tried using `pa_stream_flush()` to trigger the write callback to fire, but this just doesn't work for some reason.) - -Once you've created the stream, you need to connect it which involves the whole waiting procedure. This is the same process as the context, -only this time you'll poll for the state with `pa_stream_get_status()`. The starting and stopping of a streaming is referred to as -"corking" in PulseAudio. The analogy is corking a barrel. To start the stream, you uncork it, to stop it you cork it. Personally I think -it's silly - why would you not just call it "starting" and "stopping" like any other normal audio API? Anyway, the act of corking is, you -guessed it, asynchronous. This means you'll need our waiting loop as usual. Again, why this asynchronous design is the default is -absolutely beyond me. Would it really be that hard to just make it run synchronously? - -Teardown is pretty simple (what?!). It's just a matter of calling the relevant `_unref()` function on each object in reverse order that -they were initialized in. - -That's about it from the PulseAudio side. A bit ranty, I know, but they really need to fix that main loop and callback system. They're -embarrassingly unpractical. The main loop thing is an easy fix - have synchronous versions of all APIs. If an application wants these to -run asynchronously, they can execute them in a separate thread themselves. The desire to run these asynchronously is such a niche -requirement - it makes no sense to make it the default. The stream write callback needs to be change, or an alternative provided, that is -constantly fired, regardless of whether or not `pa_stream_write()` has been called, and it needs to take a pointer to a buffer as a -parameter which the program just writes to directly rather than having to call `pa_stream_writable_size()` and `pa_stream_write()`. These -changes alone will change PulseAudio from one of the worst audio APIs to one of the best. -*/ - - -/* -It is assumed pulseaudio.h is available when linking at compile time. When linking at compile time, we use the declarations in the header -to check for type safety. We cannot do this when linking at run time because the header might not be available. -*/ -#ifdef MA_NO_RUNTIME_LINKING - -/* pulseaudio.h marks some functions with "inline" which isn't always supported. Need to emulate it. */ -#if !defined(__cplusplus) - #if defined(__STRICT_ANSI__) - #if !defined(inline) - #define inline __inline__ __attribute__((always_inline)) - #define MA_INLINE_DEFINED - #endif - #endif -#endif -#include -#if defined(MA_INLINE_DEFINED) - #undef inline - #undef MA_INLINE_DEFINED -#endif - -#define MA_PA_OK PA_OK -#define MA_PA_ERR_ACCESS PA_ERR_ACCESS -#define MA_PA_ERR_INVALID PA_ERR_INVALID -#define MA_PA_ERR_NOENTITY PA_ERR_NOENTITY -#define MA_PA_ERR_NOTSUPPORTED PA_ERR_NOTSUPPORTED - -#define MA_PA_CHANNELS_MAX PA_CHANNELS_MAX -#define MA_PA_RATE_MAX PA_RATE_MAX - -typedef pa_context_flags_t ma_pa_context_flags_t; -#define MA_PA_CONTEXT_NOFLAGS PA_CONTEXT_NOFLAGS -#define MA_PA_CONTEXT_NOAUTOSPAWN PA_CONTEXT_NOAUTOSPAWN -#define MA_PA_CONTEXT_NOFAIL PA_CONTEXT_NOFAIL - -typedef pa_stream_flags_t ma_pa_stream_flags_t; -#define MA_PA_STREAM_NOFLAGS PA_STREAM_NOFLAGS -#define MA_PA_STREAM_START_CORKED PA_STREAM_START_CORKED -#define MA_PA_STREAM_INTERPOLATE_TIMING PA_STREAM_INTERPOLATE_TIMING -#define MA_PA_STREAM_NOT_MONOTONIC PA_STREAM_NOT_MONOTONIC -#define MA_PA_STREAM_AUTO_TIMING_UPDATE PA_STREAM_AUTO_TIMING_UPDATE -#define MA_PA_STREAM_NO_REMAP_CHANNELS PA_STREAM_NO_REMAP_CHANNELS -#define MA_PA_STREAM_NO_REMIX_CHANNELS PA_STREAM_NO_REMIX_CHANNELS -#define MA_PA_STREAM_FIX_FORMAT PA_STREAM_FIX_FORMAT -#define MA_PA_STREAM_FIX_RATE PA_STREAM_FIX_RATE -#define MA_PA_STREAM_FIX_CHANNELS PA_STREAM_FIX_CHANNELS -#define MA_PA_STREAM_DONT_MOVE PA_STREAM_DONT_MOVE -#define MA_PA_STREAM_VARIABLE_RATE PA_STREAM_VARIABLE_RATE -#define MA_PA_STREAM_PEAK_DETECT PA_STREAM_PEAK_DETECT -#define MA_PA_STREAM_START_MUTED PA_STREAM_START_MUTED -#define MA_PA_STREAM_ADJUST_LATENCY PA_STREAM_ADJUST_LATENCY -#define MA_PA_STREAM_EARLY_REQUESTS PA_STREAM_EARLY_REQUESTS -#define MA_PA_STREAM_DONT_INHIBIT_AUTO_SUSPEND PA_STREAM_DONT_INHIBIT_AUTO_SUSPEND -#define MA_PA_STREAM_START_UNMUTED PA_STREAM_START_UNMUTED -#define MA_PA_STREAM_FAIL_ON_SUSPEND PA_STREAM_FAIL_ON_SUSPEND -#define MA_PA_STREAM_RELATIVE_VOLUME PA_STREAM_RELATIVE_VOLUME -#define MA_PA_STREAM_PASSTHROUGH PA_STREAM_PASSTHROUGH - -typedef pa_sink_flags_t ma_pa_sink_flags_t; -#define MA_PA_SINK_NOFLAGS PA_SINK_NOFLAGS -#define MA_PA_SINK_HW_VOLUME_CTRL PA_SINK_HW_VOLUME_CTRL -#define MA_PA_SINK_LATENCY PA_SINK_LATENCY -#define MA_PA_SINK_HARDWARE PA_SINK_HARDWARE -#define MA_PA_SINK_NETWORK PA_SINK_NETWORK -#define MA_PA_SINK_HW_MUTE_CTRL PA_SINK_HW_MUTE_CTRL -#define MA_PA_SINK_DECIBEL_VOLUME PA_SINK_DECIBEL_VOLUME -#define MA_PA_SINK_FLAT_VOLUME PA_SINK_FLAT_VOLUME -#define MA_PA_SINK_DYNAMIC_LATENCY PA_SINK_DYNAMIC_LATENCY -#define MA_PA_SINK_SET_FORMATS PA_SINK_SET_FORMATS - -typedef pa_source_flags_t ma_pa_source_flags_t; -#define MA_PA_SOURCE_NOFLAGS PA_SOURCE_NOFLAGS -#define MA_PA_SOURCE_HW_VOLUME_CTRL PA_SOURCE_HW_VOLUME_CTRL -#define MA_PA_SOURCE_LATENCY PA_SOURCE_LATENCY -#define MA_PA_SOURCE_HARDWARE PA_SOURCE_HARDWARE -#define MA_PA_SOURCE_NETWORK PA_SOURCE_NETWORK -#define MA_PA_SOURCE_HW_MUTE_CTRL PA_SOURCE_HW_MUTE_CTRL -#define MA_PA_SOURCE_DECIBEL_VOLUME PA_SOURCE_DECIBEL_VOLUME -#define MA_PA_SOURCE_DYNAMIC_LATENCY PA_SOURCE_DYNAMIC_LATENCY -#define MA_PA_SOURCE_FLAT_VOLUME PA_SOURCE_FLAT_VOLUME - -typedef pa_context_state_t ma_pa_context_state_t; -#define MA_PA_CONTEXT_UNCONNECTED PA_CONTEXT_UNCONNECTED -#define MA_PA_CONTEXT_CONNECTING PA_CONTEXT_CONNECTING -#define MA_PA_CONTEXT_AUTHORIZING PA_CONTEXT_AUTHORIZING -#define MA_PA_CONTEXT_SETTING_NAME PA_CONTEXT_SETTING_NAME -#define MA_PA_CONTEXT_READY PA_CONTEXT_READY -#define MA_PA_CONTEXT_FAILED PA_CONTEXT_FAILED -#define MA_PA_CONTEXT_TERMINATED PA_CONTEXT_TERMINATED - -typedef pa_stream_state_t ma_pa_stream_state_t; -#define MA_PA_STREAM_UNCONNECTED PA_STREAM_UNCONNECTED -#define MA_PA_STREAM_CREATING PA_STREAM_CREATING -#define MA_PA_STREAM_READY PA_STREAM_READY -#define MA_PA_STREAM_FAILED PA_STREAM_FAILED -#define MA_PA_STREAM_TERMINATED PA_STREAM_TERMINATED - -typedef pa_operation_state_t ma_pa_operation_state_t; -#define MA_PA_OPERATION_RUNNING PA_OPERATION_RUNNING -#define MA_PA_OPERATION_DONE PA_OPERATION_DONE -#define MA_PA_OPERATION_CANCELLED PA_OPERATION_CANCELLED - -typedef pa_sink_state_t ma_pa_sink_state_t; -#define MA_PA_SINK_INVALID_STATE PA_SINK_INVALID_STATE -#define MA_PA_SINK_RUNNING PA_SINK_RUNNING -#define MA_PA_SINK_IDLE PA_SINK_IDLE -#define MA_PA_SINK_SUSPENDED PA_SINK_SUSPENDED - -typedef pa_source_state_t ma_pa_source_state_t; -#define MA_PA_SOURCE_INVALID_STATE PA_SOURCE_INVALID_STATE -#define MA_PA_SOURCE_RUNNING PA_SOURCE_RUNNING -#define MA_PA_SOURCE_IDLE PA_SOURCE_IDLE -#define MA_PA_SOURCE_SUSPENDED PA_SOURCE_SUSPENDED - -typedef pa_seek_mode_t ma_pa_seek_mode_t; -#define MA_PA_SEEK_RELATIVE PA_SEEK_RELATIVE -#define MA_PA_SEEK_ABSOLUTE PA_SEEK_ABSOLUTE -#define MA_PA_SEEK_RELATIVE_ON_READ PA_SEEK_RELATIVE_ON_READ -#define MA_PA_SEEK_RELATIVE_END PA_SEEK_RELATIVE_END - -typedef pa_channel_position_t ma_pa_channel_position_t; -#define MA_PA_CHANNEL_POSITION_INVALID PA_CHANNEL_POSITION_INVALID -#define MA_PA_CHANNEL_POSITION_MONO PA_CHANNEL_POSITION_MONO -#define MA_PA_CHANNEL_POSITION_FRONT_LEFT PA_CHANNEL_POSITION_FRONT_LEFT -#define MA_PA_CHANNEL_POSITION_FRONT_RIGHT PA_CHANNEL_POSITION_FRONT_RIGHT -#define MA_PA_CHANNEL_POSITION_FRONT_CENTER PA_CHANNEL_POSITION_FRONT_CENTER -#define MA_PA_CHANNEL_POSITION_REAR_CENTER PA_CHANNEL_POSITION_REAR_CENTER -#define MA_PA_CHANNEL_POSITION_REAR_LEFT PA_CHANNEL_POSITION_REAR_LEFT -#define MA_PA_CHANNEL_POSITION_REAR_RIGHT PA_CHANNEL_POSITION_REAR_RIGHT -#define MA_PA_CHANNEL_POSITION_LFE PA_CHANNEL_POSITION_LFE -#define MA_PA_CHANNEL_POSITION_FRONT_LEFT_OF_CENTER PA_CHANNEL_POSITION_FRONT_LEFT_OF_CENTER -#define MA_PA_CHANNEL_POSITION_FRONT_RIGHT_OF_CENTER PA_CHANNEL_POSITION_FRONT_RIGHT_OF_CENTER -#define MA_PA_CHANNEL_POSITION_SIDE_LEFT PA_CHANNEL_POSITION_SIDE_LEFT -#define MA_PA_CHANNEL_POSITION_SIDE_RIGHT PA_CHANNEL_POSITION_SIDE_RIGHT -#define MA_PA_CHANNEL_POSITION_AUX0 PA_CHANNEL_POSITION_AUX0 -#define MA_PA_CHANNEL_POSITION_AUX1 PA_CHANNEL_POSITION_AUX1 -#define MA_PA_CHANNEL_POSITION_AUX2 PA_CHANNEL_POSITION_AUX2 -#define MA_PA_CHANNEL_POSITION_AUX3 PA_CHANNEL_POSITION_AUX3 -#define MA_PA_CHANNEL_POSITION_AUX4 PA_CHANNEL_POSITION_AUX4 -#define MA_PA_CHANNEL_POSITION_AUX5 PA_CHANNEL_POSITION_AUX5 -#define MA_PA_CHANNEL_POSITION_AUX6 PA_CHANNEL_POSITION_AUX6 -#define MA_PA_CHANNEL_POSITION_AUX7 PA_CHANNEL_POSITION_AUX7 -#define MA_PA_CHANNEL_POSITION_AUX8 PA_CHANNEL_POSITION_AUX8 -#define MA_PA_CHANNEL_POSITION_AUX9 PA_CHANNEL_POSITION_AUX9 -#define MA_PA_CHANNEL_POSITION_AUX10 PA_CHANNEL_POSITION_AUX10 -#define MA_PA_CHANNEL_POSITION_AUX11 PA_CHANNEL_POSITION_AUX11 -#define MA_PA_CHANNEL_POSITION_AUX12 PA_CHANNEL_POSITION_AUX12 -#define MA_PA_CHANNEL_POSITION_AUX13 PA_CHANNEL_POSITION_AUX13 -#define MA_PA_CHANNEL_POSITION_AUX14 PA_CHANNEL_POSITION_AUX14 -#define MA_PA_CHANNEL_POSITION_AUX15 PA_CHANNEL_POSITION_AUX15 -#define MA_PA_CHANNEL_POSITION_AUX16 PA_CHANNEL_POSITION_AUX16 -#define MA_PA_CHANNEL_POSITION_AUX17 PA_CHANNEL_POSITION_AUX17 -#define MA_PA_CHANNEL_POSITION_AUX18 PA_CHANNEL_POSITION_AUX18 -#define MA_PA_CHANNEL_POSITION_AUX19 PA_CHANNEL_POSITION_AUX19 -#define MA_PA_CHANNEL_POSITION_AUX20 PA_CHANNEL_POSITION_AUX20 -#define MA_PA_CHANNEL_POSITION_AUX21 PA_CHANNEL_POSITION_AUX21 -#define MA_PA_CHANNEL_POSITION_AUX22 PA_CHANNEL_POSITION_AUX22 -#define MA_PA_CHANNEL_POSITION_AUX23 PA_CHANNEL_POSITION_AUX23 -#define MA_PA_CHANNEL_POSITION_AUX24 PA_CHANNEL_POSITION_AUX24 -#define MA_PA_CHANNEL_POSITION_AUX25 PA_CHANNEL_POSITION_AUX25 -#define MA_PA_CHANNEL_POSITION_AUX26 PA_CHANNEL_POSITION_AUX26 -#define MA_PA_CHANNEL_POSITION_AUX27 PA_CHANNEL_POSITION_AUX27 -#define MA_PA_CHANNEL_POSITION_AUX28 PA_CHANNEL_POSITION_AUX28 -#define MA_PA_CHANNEL_POSITION_AUX29 PA_CHANNEL_POSITION_AUX29 -#define MA_PA_CHANNEL_POSITION_AUX30 PA_CHANNEL_POSITION_AUX30 -#define MA_PA_CHANNEL_POSITION_AUX31 PA_CHANNEL_POSITION_AUX31 -#define MA_PA_CHANNEL_POSITION_TOP_CENTER PA_CHANNEL_POSITION_TOP_CENTER -#define MA_PA_CHANNEL_POSITION_TOP_FRONT_LEFT PA_CHANNEL_POSITION_TOP_FRONT_LEFT -#define MA_PA_CHANNEL_POSITION_TOP_FRONT_RIGHT PA_CHANNEL_POSITION_TOP_FRONT_RIGHT -#define MA_PA_CHANNEL_POSITION_TOP_FRONT_CENTER PA_CHANNEL_POSITION_TOP_FRONT_CENTER -#define MA_PA_CHANNEL_POSITION_TOP_REAR_LEFT PA_CHANNEL_POSITION_TOP_REAR_LEFT -#define MA_PA_CHANNEL_POSITION_TOP_REAR_RIGHT PA_CHANNEL_POSITION_TOP_REAR_RIGHT -#define MA_PA_CHANNEL_POSITION_TOP_REAR_CENTER PA_CHANNEL_POSITION_TOP_REAR_CENTER -#define MA_PA_CHANNEL_POSITION_LEFT PA_CHANNEL_POSITION_LEFT -#define MA_PA_CHANNEL_POSITION_RIGHT PA_CHANNEL_POSITION_RIGHT -#define MA_PA_CHANNEL_POSITION_CENTER PA_CHANNEL_POSITION_CENTER -#define MA_PA_CHANNEL_POSITION_SUBWOOFER PA_CHANNEL_POSITION_SUBWOOFER - -typedef pa_channel_map_def_t ma_pa_channel_map_def_t; -#define MA_PA_CHANNEL_MAP_AIFF PA_CHANNEL_MAP_AIFF -#define MA_PA_CHANNEL_MAP_ALSA PA_CHANNEL_MAP_ALSA -#define MA_PA_CHANNEL_MAP_AUX PA_CHANNEL_MAP_AUX -#define MA_PA_CHANNEL_MAP_WAVEEX PA_CHANNEL_MAP_WAVEEX -#define MA_PA_CHANNEL_MAP_OSS PA_CHANNEL_MAP_OSS -#define MA_PA_CHANNEL_MAP_DEFAULT PA_CHANNEL_MAP_DEFAULT - -typedef pa_sample_format_t ma_pa_sample_format_t; -#define MA_PA_SAMPLE_INVALID PA_SAMPLE_INVALID -#define MA_PA_SAMPLE_U8 PA_SAMPLE_U8 -#define MA_PA_SAMPLE_ALAW PA_SAMPLE_ALAW -#define MA_PA_SAMPLE_ULAW PA_SAMPLE_ULAW -#define MA_PA_SAMPLE_S16LE PA_SAMPLE_S16LE -#define MA_PA_SAMPLE_S16BE PA_SAMPLE_S16BE -#define MA_PA_SAMPLE_FLOAT32LE PA_SAMPLE_FLOAT32LE -#define MA_PA_SAMPLE_FLOAT32BE PA_SAMPLE_FLOAT32BE -#define MA_PA_SAMPLE_S32LE PA_SAMPLE_S32LE -#define MA_PA_SAMPLE_S32BE PA_SAMPLE_S32BE -#define MA_PA_SAMPLE_S24LE PA_SAMPLE_S24LE -#define MA_PA_SAMPLE_S24BE PA_SAMPLE_S24BE -#define MA_PA_SAMPLE_S24_32LE PA_SAMPLE_S24_32LE -#define MA_PA_SAMPLE_S24_32BE PA_SAMPLE_S24_32BE - -typedef pa_mainloop ma_pa_mainloop; -typedef pa_threaded_mainloop ma_pa_threaded_mainloop; -typedef pa_mainloop_api ma_pa_mainloop_api; -typedef pa_context ma_pa_context; -typedef pa_operation ma_pa_operation; -typedef pa_stream ma_pa_stream; -typedef pa_spawn_api ma_pa_spawn_api; -typedef pa_buffer_attr ma_pa_buffer_attr; -typedef pa_channel_map ma_pa_channel_map; -typedef pa_cvolume ma_pa_cvolume; -typedef pa_sample_spec ma_pa_sample_spec; -typedef pa_sink_info ma_pa_sink_info; -typedef pa_source_info ma_pa_source_info; - -typedef pa_context_notify_cb_t ma_pa_context_notify_cb_t; -typedef pa_sink_info_cb_t ma_pa_sink_info_cb_t; -typedef pa_source_info_cb_t ma_pa_source_info_cb_t; -typedef pa_stream_success_cb_t ma_pa_stream_success_cb_t; -typedef pa_stream_request_cb_t ma_pa_stream_request_cb_t; -typedef pa_stream_notify_cb_t ma_pa_stream_notify_cb_t; -typedef pa_free_cb_t ma_pa_free_cb_t; -#else -#define MA_PA_OK 0 -#define MA_PA_ERR_ACCESS 1 -#define MA_PA_ERR_INVALID 2 -#define MA_PA_ERR_NOENTITY 5 -#define MA_PA_ERR_NOTSUPPORTED 19 - -#define MA_PA_CHANNELS_MAX 32 -#define MA_PA_RATE_MAX 384000 - -typedef int ma_pa_context_flags_t; -#define MA_PA_CONTEXT_NOFLAGS 0x00000000 -#define MA_PA_CONTEXT_NOAUTOSPAWN 0x00000001 -#define MA_PA_CONTEXT_NOFAIL 0x00000002 - -typedef int ma_pa_stream_flags_t; -#define MA_PA_STREAM_NOFLAGS 0x00000000 -#define MA_PA_STREAM_START_CORKED 0x00000001 -#define MA_PA_STREAM_INTERPOLATE_TIMING 0x00000002 -#define MA_PA_STREAM_NOT_MONOTONIC 0x00000004 -#define MA_PA_STREAM_AUTO_TIMING_UPDATE 0x00000008 -#define MA_PA_STREAM_NO_REMAP_CHANNELS 0x00000010 -#define MA_PA_STREAM_NO_REMIX_CHANNELS 0x00000020 -#define MA_PA_STREAM_FIX_FORMAT 0x00000040 -#define MA_PA_STREAM_FIX_RATE 0x00000080 -#define MA_PA_STREAM_FIX_CHANNELS 0x00000100 -#define MA_PA_STREAM_DONT_MOVE 0x00000200 -#define MA_PA_STREAM_VARIABLE_RATE 0x00000400 -#define MA_PA_STREAM_PEAK_DETECT 0x00000800 -#define MA_PA_STREAM_START_MUTED 0x00001000 -#define MA_PA_STREAM_ADJUST_LATENCY 0x00002000 -#define MA_PA_STREAM_EARLY_REQUESTS 0x00004000 -#define MA_PA_STREAM_DONT_INHIBIT_AUTO_SUSPEND 0x00008000 -#define MA_PA_STREAM_START_UNMUTED 0x00010000 -#define MA_PA_STREAM_FAIL_ON_SUSPEND 0x00020000 -#define MA_PA_STREAM_RELATIVE_VOLUME 0x00040000 -#define MA_PA_STREAM_PASSTHROUGH 0x00080000 - -typedef int ma_pa_sink_flags_t; -#define MA_PA_SINK_NOFLAGS 0x00000000 -#define MA_PA_SINK_HW_VOLUME_CTRL 0x00000001 -#define MA_PA_SINK_LATENCY 0x00000002 -#define MA_PA_SINK_HARDWARE 0x00000004 -#define MA_PA_SINK_NETWORK 0x00000008 -#define MA_PA_SINK_HW_MUTE_CTRL 0x00000010 -#define MA_PA_SINK_DECIBEL_VOLUME 0x00000020 -#define MA_PA_SINK_FLAT_VOLUME 0x00000040 -#define MA_PA_SINK_DYNAMIC_LATENCY 0x00000080 -#define MA_PA_SINK_SET_FORMATS 0x00000100 - -typedef int ma_pa_source_flags_t; -#define MA_PA_SOURCE_NOFLAGS 0x00000000 -#define MA_PA_SOURCE_HW_VOLUME_CTRL 0x00000001 -#define MA_PA_SOURCE_LATENCY 0x00000002 -#define MA_PA_SOURCE_HARDWARE 0x00000004 -#define MA_PA_SOURCE_NETWORK 0x00000008 -#define MA_PA_SOURCE_HW_MUTE_CTRL 0x00000010 -#define MA_PA_SOURCE_DECIBEL_VOLUME 0x00000020 -#define MA_PA_SOURCE_DYNAMIC_LATENCY 0x00000040 -#define MA_PA_SOURCE_FLAT_VOLUME 0x00000080 - -typedef int ma_pa_context_state_t; -#define MA_PA_CONTEXT_UNCONNECTED 0 -#define MA_PA_CONTEXT_CONNECTING 1 -#define MA_PA_CONTEXT_AUTHORIZING 2 -#define MA_PA_CONTEXT_SETTING_NAME 3 -#define MA_PA_CONTEXT_READY 4 -#define MA_PA_CONTEXT_FAILED 5 -#define MA_PA_CONTEXT_TERMINATED 6 - -typedef int ma_pa_stream_state_t; -#define MA_PA_STREAM_UNCONNECTED 0 -#define MA_PA_STREAM_CREATING 1 -#define MA_PA_STREAM_READY 2 -#define MA_PA_STREAM_FAILED 3 -#define MA_PA_STREAM_TERMINATED 4 - -typedef int ma_pa_operation_state_t; -#define MA_PA_OPERATION_RUNNING 0 -#define MA_PA_OPERATION_DONE 1 -#define MA_PA_OPERATION_CANCELLED 2 - -typedef int ma_pa_sink_state_t; -#define MA_PA_SINK_INVALID_STATE -1 -#define MA_PA_SINK_RUNNING 0 -#define MA_PA_SINK_IDLE 1 -#define MA_PA_SINK_SUSPENDED 2 - -typedef int ma_pa_source_state_t; -#define MA_PA_SOURCE_INVALID_STATE -1 -#define MA_PA_SOURCE_RUNNING 0 -#define MA_PA_SOURCE_IDLE 1 -#define MA_PA_SOURCE_SUSPENDED 2 - -typedef int ma_pa_seek_mode_t; -#define MA_PA_SEEK_RELATIVE 0 -#define MA_PA_SEEK_ABSOLUTE 1 -#define MA_PA_SEEK_RELATIVE_ON_READ 2 -#define MA_PA_SEEK_RELATIVE_END 3 - -typedef int ma_pa_channel_position_t; -#define MA_PA_CHANNEL_POSITION_INVALID -1 -#define MA_PA_CHANNEL_POSITION_MONO 0 -#define MA_PA_CHANNEL_POSITION_FRONT_LEFT 1 -#define MA_PA_CHANNEL_POSITION_FRONT_RIGHT 2 -#define MA_PA_CHANNEL_POSITION_FRONT_CENTER 3 -#define MA_PA_CHANNEL_POSITION_REAR_CENTER 4 -#define MA_PA_CHANNEL_POSITION_REAR_LEFT 5 -#define MA_PA_CHANNEL_POSITION_REAR_RIGHT 6 -#define MA_PA_CHANNEL_POSITION_LFE 7 -#define MA_PA_CHANNEL_POSITION_FRONT_LEFT_OF_CENTER 8 -#define MA_PA_CHANNEL_POSITION_FRONT_RIGHT_OF_CENTER 9 -#define MA_PA_CHANNEL_POSITION_SIDE_LEFT 10 -#define MA_PA_CHANNEL_POSITION_SIDE_RIGHT 11 -#define MA_PA_CHANNEL_POSITION_AUX0 12 -#define MA_PA_CHANNEL_POSITION_AUX1 13 -#define MA_PA_CHANNEL_POSITION_AUX2 14 -#define MA_PA_CHANNEL_POSITION_AUX3 15 -#define MA_PA_CHANNEL_POSITION_AUX4 16 -#define MA_PA_CHANNEL_POSITION_AUX5 17 -#define MA_PA_CHANNEL_POSITION_AUX6 18 -#define MA_PA_CHANNEL_POSITION_AUX7 19 -#define MA_PA_CHANNEL_POSITION_AUX8 20 -#define MA_PA_CHANNEL_POSITION_AUX9 21 -#define MA_PA_CHANNEL_POSITION_AUX10 22 -#define MA_PA_CHANNEL_POSITION_AUX11 23 -#define MA_PA_CHANNEL_POSITION_AUX12 24 -#define MA_PA_CHANNEL_POSITION_AUX13 25 -#define MA_PA_CHANNEL_POSITION_AUX14 26 -#define MA_PA_CHANNEL_POSITION_AUX15 27 -#define MA_PA_CHANNEL_POSITION_AUX16 28 -#define MA_PA_CHANNEL_POSITION_AUX17 29 -#define MA_PA_CHANNEL_POSITION_AUX18 30 -#define MA_PA_CHANNEL_POSITION_AUX19 31 -#define MA_PA_CHANNEL_POSITION_AUX20 32 -#define MA_PA_CHANNEL_POSITION_AUX21 33 -#define MA_PA_CHANNEL_POSITION_AUX22 34 -#define MA_PA_CHANNEL_POSITION_AUX23 35 -#define MA_PA_CHANNEL_POSITION_AUX24 36 -#define MA_PA_CHANNEL_POSITION_AUX25 37 -#define MA_PA_CHANNEL_POSITION_AUX26 38 -#define MA_PA_CHANNEL_POSITION_AUX27 39 -#define MA_PA_CHANNEL_POSITION_AUX28 40 -#define MA_PA_CHANNEL_POSITION_AUX29 41 -#define MA_PA_CHANNEL_POSITION_AUX30 42 -#define MA_PA_CHANNEL_POSITION_AUX31 43 -#define MA_PA_CHANNEL_POSITION_TOP_CENTER 44 -#define MA_PA_CHANNEL_POSITION_TOP_FRONT_LEFT 45 -#define MA_PA_CHANNEL_POSITION_TOP_FRONT_RIGHT 46 -#define MA_PA_CHANNEL_POSITION_TOP_FRONT_CENTER 47 -#define MA_PA_CHANNEL_POSITION_TOP_REAR_LEFT 48 -#define MA_PA_CHANNEL_POSITION_TOP_REAR_RIGHT 49 -#define MA_PA_CHANNEL_POSITION_TOP_REAR_CENTER 50 -#define MA_PA_CHANNEL_POSITION_LEFT MA_PA_CHANNEL_POSITION_FRONT_LEFT -#define MA_PA_CHANNEL_POSITION_RIGHT MA_PA_CHANNEL_POSITION_FRONT_RIGHT -#define MA_PA_CHANNEL_POSITION_CENTER MA_PA_CHANNEL_POSITION_FRONT_CENTER -#define MA_PA_CHANNEL_POSITION_SUBWOOFER MA_PA_CHANNEL_POSITION_LFE - -typedef int ma_pa_channel_map_def_t; -#define MA_PA_CHANNEL_MAP_AIFF 0 -#define MA_PA_CHANNEL_MAP_ALSA 1 -#define MA_PA_CHANNEL_MAP_AUX 2 -#define MA_PA_CHANNEL_MAP_WAVEEX 3 -#define MA_PA_CHANNEL_MAP_OSS 4 -#define MA_PA_CHANNEL_MAP_DEFAULT MA_PA_CHANNEL_MAP_AIFF - -typedef int ma_pa_sample_format_t; -#define MA_PA_SAMPLE_INVALID -1 -#define MA_PA_SAMPLE_U8 0 -#define MA_PA_SAMPLE_ALAW 1 -#define MA_PA_SAMPLE_ULAW 2 -#define MA_PA_SAMPLE_S16LE 3 -#define MA_PA_SAMPLE_S16BE 4 -#define MA_PA_SAMPLE_FLOAT32LE 5 -#define MA_PA_SAMPLE_FLOAT32BE 6 -#define MA_PA_SAMPLE_S32LE 7 -#define MA_PA_SAMPLE_S32BE 8 -#define MA_PA_SAMPLE_S24LE 9 -#define MA_PA_SAMPLE_S24BE 10 -#define MA_PA_SAMPLE_S24_32LE 11 -#define MA_PA_SAMPLE_S24_32BE 12 - -typedef struct ma_pa_mainloop ma_pa_mainloop; -typedef struct ma_pa_threaded_mainloop ma_pa_threaded_mainloop; -typedef struct ma_pa_mainloop_api ma_pa_mainloop_api; -typedef struct ma_pa_context ma_pa_context; -typedef struct ma_pa_operation ma_pa_operation; -typedef struct ma_pa_stream ma_pa_stream; -typedef struct ma_pa_spawn_api ma_pa_spawn_api; - -typedef struct -{ - ma_uint32 maxlength; - ma_uint32 tlength; - ma_uint32 prebuf; - ma_uint32 minreq; - ma_uint32 fragsize; -} ma_pa_buffer_attr; - -typedef struct -{ - ma_uint8 channels; - ma_pa_channel_position_t map[MA_PA_CHANNELS_MAX]; -} ma_pa_channel_map; - -typedef struct -{ - ma_uint8 channels; - ma_uint32 values[MA_PA_CHANNELS_MAX]; -} ma_pa_cvolume; - -typedef struct -{ - ma_pa_sample_format_t format; - ma_uint32 rate; - ma_uint8 channels; -} ma_pa_sample_spec; - -typedef struct -{ - const char* name; - ma_uint32 index; - const char* description; - ma_pa_sample_spec sample_spec; - ma_pa_channel_map channel_map; - ma_uint32 owner_module; - ma_pa_cvolume volume; - int mute; - ma_uint32 monitor_source; - const char* monitor_source_name; - ma_uint64 latency; - const char* driver; - ma_pa_sink_flags_t flags; - void* proplist; - ma_uint64 configured_latency; - ma_uint32 base_volume; - ma_pa_sink_state_t state; - ma_uint32 n_volume_steps; - ma_uint32 card; - ma_uint32 n_ports; - void** ports; - void* active_port; - ma_uint8 n_formats; - void** formats; -} ma_pa_sink_info; - -typedef struct -{ - const char *name; - ma_uint32 index; - const char *description; - ma_pa_sample_spec sample_spec; - ma_pa_channel_map channel_map; - ma_uint32 owner_module; - ma_pa_cvolume volume; - int mute; - ma_uint32 monitor_of_sink; - const char *monitor_of_sink_name; - ma_uint64 latency; - const char *driver; - ma_pa_source_flags_t flags; - void* proplist; - ma_uint64 configured_latency; - ma_uint32 base_volume; - ma_pa_source_state_t state; - ma_uint32 n_volume_steps; - ma_uint32 card; - ma_uint32 n_ports; - void** ports; - void* active_port; - ma_uint8 n_formats; - void** formats; -} ma_pa_source_info; - -typedef void (* ma_pa_context_notify_cb_t)(ma_pa_context* c, void* userdata); -typedef void (* ma_pa_sink_info_cb_t) (ma_pa_context* c, const ma_pa_sink_info* i, int eol, void* userdata); -typedef void (* ma_pa_source_info_cb_t) (ma_pa_context* c, const ma_pa_source_info* i, int eol, void* userdata); -typedef void (* ma_pa_stream_success_cb_t)(ma_pa_stream* s, int success, void* userdata); -typedef void (* ma_pa_stream_request_cb_t)(ma_pa_stream* s, size_t nbytes, void* userdata); -typedef void (* ma_pa_stream_notify_cb_t) (ma_pa_stream* s, void* userdata); -typedef void (* ma_pa_free_cb_t) (void* p); -#endif - - -typedef ma_pa_mainloop* (* ma_pa_mainloop_new_proc) (void); -typedef void (* ma_pa_mainloop_free_proc) (ma_pa_mainloop* m); -typedef void (* ma_pa_mainloop_quit_proc) (ma_pa_mainloop* m, int retval); -typedef ma_pa_mainloop_api* (* ma_pa_mainloop_get_api_proc) (ma_pa_mainloop* m); -typedef int (* ma_pa_mainloop_iterate_proc) (ma_pa_mainloop* m, int block, int* retval); -typedef void (* ma_pa_mainloop_wakeup_proc) (ma_pa_mainloop* m); -typedef ma_pa_threaded_mainloop* (* ma_pa_threaded_mainloop_new_proc) (void); -typedef void (* ma_pa_threaded_mainloop_free_proc) (ma_pa_threaded_mainloop* m); -typedef int (* ma_pa_threaded_mainloop_start_proc) (ma_pa_threaded_mainloop* m); -typedef void (* ma_pa_threaded_mainloop_stop_proc) (ma_pa_threaded_mainloop* m); -typedef void (* ma_pa_threaded_mainloop_lock_proc) (ma_pa_threaded_mainloop* m); -typedef void (* ma_pa_threaded_mainloop_unlock_proc) (ma_pa_threaded_mainloop* m); -typedef void (* ma_pa_threaded_mainloop_wait_proc) (ma_pa_threaded_mainloop* m); -typedef void (* ma_pa_threaded_mainloop_signal_proc) (ma_pa_threaded_mainloop* m, int wait_for_accept); -typedef void (* ma_pa_threaded_mainloop_accept_proc) (ma_pa_threaded_mainloop* m); -typedef int (* ma_pa_threaded_mainloop_get_retval_proc) (ma_pa_threaded_mainloop* m); -typedef ma_pa_mainloop_api* (* ma_pa_threaded_mainloop_get_api_proc) (ma_pa_threaded_mainloop* m); -typedef int (* ma_pa_threaded_mainloop_in_thread_proc) (ma_pa_threaded_mainloop* m); -typedef void (* ma_pa_threaded_mainloop_set_name_proc) (ma_pa_threaded_mainloop* m, const char* name); -typedef ma_pa_context* (* ma_pa_context_new_proc) (ma_pa_mainloop_api* mainloop, const char* name); -typedef void (* ma_pa_context_unref_proc) (ma_pa_context* c); -typedef int (* ma_pa_context_connect_proc) (ma_pa_context* c, const char* server, ma_pa_context_flags_t flags, const ma_pa_spawn_api* api); -typedef void (* ma_pa_context_disconnect_proc) (ma_pa_context* c); -typedef void (* ma_pa_context_set_state_callback_proc) (ma_pa_context* c, ma_pa_context_notify_cb_t cb, void* userdata); -typedef ma_pa_context_state_t (* ma_pa_context_get_state_proc) (ma_pa_context* c); -typedef ma_pa_operation* (* ma_pa_context_get_sink_info_list_proc) (ma_pa_context* c, ma_pa_sink_info_cb_t cb, void* userdata); -typedef ma_pa_operation* (* ma_pa_context_get_source_info_list_proc) (ma_pa_context* c, ma_pa_source_info_cb_t cb, void* userdata); -typedef ma_pa_operation* (* ma_pa_context_get_sink_info_by_name_proc) (ma_pa_context* c, const char* name, ma_pa_sink_info_cb_t cb, void* userdata); -typedef ma_pa_operation* (* ma_pa_context_get_source_info_by_name_proc)(ma_pa_context* c, const char* name, ma_pa_source_info_cb_t cb, void* userdata); -typedef void (* ma_pa_operation_unref_proc) (ma_pa_operation* o); -typedef ma_pa_operation_state_t (* ma_pa_operation_get_state_proc) (ma_pa_operation* o); -typedef ma_pa_channel_map* (* ma_pa_channel_map_init_extend_proc) (ma_pa_channel_map* m, unsigned channels, ma_pa_channel_map_def_t def); -typedef int (* ma_pa_channel_map_valid_proc) (const ma_pa_channel_map* m); -typedef int (* ma_pa_channel_map_compatible_proc) (const ma_pa_channel_map* m, const ma_pa_sample_spec* ss); -typedef ma_pa_stream* (* ma_pa_stream_new_proc) (ma_pa_context* c, const char* name, const ma_pa_sample_spec* ss, const ma_pa_channel_map* map); -typedef void (* ma_pa_stream_unref_proc) (ma_pa_stream* s); -typedef int (* ma_pa_stream_connect_playback_proc) (ma_pa_stream* s, const char* dev, const ma_pa_buffer_attr* attr, ma_pa_stream_flags_t flags, const ma_pa_cvolume* volume, ma_pa_stream* sync_stream); -typedef int (* ma_pa_stream_connect_record_proc) (ma_pa_stream* s, const char* dev, const ma_pa_buffer_attr* attr, ma_pa_stream_flags_t flags); -typedef int (* ma_pa_stream_disconnect_proc) (ma_pa_stream* s); -typedef ma_pa_stream_state_t (* ma_pa_stream_get_state_proc) (ma_pa_stream* s); -typedef const ma_pa_sample_spec* (* ma_pa_stream_get_sample_spec_proc) (ma_pa_stream* s); -typedef const ma_pa_channel_map* (* ma_pa_stream_get_channel_map_proc) (ma_pa_stream* s); -typedef const ma_pa_buffer_attr* (* ma_pa_stream_get_buffer_attr_proc) (ma_pa_stream* s); -typedef ma_pa_operation* (* ma_pa_stream_set_buffer_attr_proc) (ma_pa_stream* s, const ma_pa_buffer_attr* attr, ma_pa_stream_success_cb_t cb, void* userdata); -typedef const char* (* ma_pa_stream_get_device_name_proc) (ma_pa_stream* s); -typedef void (* ma_pa_stream_set_write_callback_proc) (ma_pa_stream* s, ma_pa_stream_request_cb_t cb, void* userdata); -typedef void (* ma_pa_stream_set_read_callback_proc) (ma_pa_stream* s, ma_pa_stream_request_cb_t cb, void* userdata); -typedef void (* ma_pa_stream_set_suspended_callback_proc) (ma_pa_stream* s, ma_pa_stream_notify_cb_t cb, void* userdata); -typedef void (* ma_pa_stream_set_moved_callback_proc) (ma_pa_stream* s, ma_pa_stream_notify_cb_t cb, void* userdata); -typedef int (* ma_pa_stream_is_suspended_proc) (const ma_pa_stream* s); -typedef ma_pa_operation* (* ma_pa_stream_flush_proc) (ma_pa_stream* s, ma_pa_stream_success_cb_t cb, void* userdata); -typedef ma_pa_operation* (* ma_pa_stream_drain_proc) (ma_pa_stream* s, ma_pa_stream_success_cb_t cb, void* userdata); -typedef int (* ma_pa_stream_is_corked_proc) (ma_pa_stream* s); -typedef ma_pa_operation* (* ma_pa_stream_cork_proc) (ma_pa_stream* s, int b, ma_pa_stream_success_cb_t cb, void* userdata); -typedef ma_pa_operation* (* ma_pa_stream_trigger_proc) (ma_pa_stream* s, ma_pa_stream_success_cb_t cb, void* userdata); -typedef int (* ma_pa_stream_begin_write_proc) (ma_pa_stream* s, void** data, size_t* nbytes); -typedef int (* ma_pa_stream_write_proc) (ma_pa_stream* s, const void* data, size_t nbytes, ma_pa_free_cb_t free_cb, int64_t offset, ma_pa_seek_mode_t seek); -typedef int (* ma_pa_stream_peek_proc) (ma_pa_stream* s, const void** data, size_t* nbytes); -typedef int (* ma_pa_stream_drop_proc) (ma_pa_stream* s); -typedef size_t (* ma_pa_stream_writable_size_proc) (ma_pa_stream* s); -typedef size_t (* ma_pa_stream_readable_size_proc) (ma_pa_stream* s); - -typedef struct -{ - ma_uint32 count; - ma_uint32 capacity; - ma_device_info* pInfo; -} ma_pulse_device_enum_data; - -static ma_result ma_result_from_pulse(int result) -{ - if (result < 0) { - return MA_ERROR; - } - - switch (result) { - case MA_PA_OK: return MA_SUCCESS; - case MA_PA_ERR_ACCESS: return MA_ACCESS_DENIED; - case MA_PA_ERR_INVALID: return MA_INVALID_ARGS; - case MA_PA_ERR_NOENTITY: return MA_NO_DEVICE; - default: return MA_ERROR; - } -} - -#if 0 -static ma_pa_sample_format_t ma_format_to_pulse(ma_format format) -{ - if (ma_is_little_endian()) { - switch (format) { - case ma_format_s16: return MA_PA_SAMPLE_S16LE; - case ma_format_s24: return MA_PA_SAMPLE_S24LE; - case ma_format_s32: return MA_PA_SAMPLE_S32LE; - case ma_format_f32: return MA_PA_SAMPLE_FLOAT32LE; - default: break; - } - } else { - switch (format) { - case ma_format_s16: return MA_PA_SAMPLE_S16BE; - case ma_format_s24: return MA_PA_SAMPLE_S24BE; - case ma_format_s32: return MA_PA_SAMPLE_S32BE; - case ma_format_f32: return MA_PA_SAMPLE_FLOAT32BE; - default: break; - } - } - - /* Endian agnostic. */ - switch (format) { - case ma_format_u8: return MA_PA_SAMPLE_U8; - default: return MA_PA_SAMPLE_INVALID; - } -} -#endif - -static ma_format ma_format_from_pulse(ma_pa_sample_format_t format) -{ - if (ma_is_little_endian()) { - switch (format) { - case MA_PA_SAMPLE_S16LE: return ma_format_s16; - case MA_PA_SAMPLE_S24LE: return ma_format_s24; - case MA_PA_SAMPLE_S32LE: return ma_format_s32; - case MA_PA_SAMPLE_FLOAT32LE: return ma_format_f32; - default: break; - } - } else { - switch (format) { - case MA_PA_SAMPLE_S16BE: return ma_format_s16; - case MA_PA_SAMPLE_S24BE: return ma_format_s24; - case MA_PA_SAMPLE_S32BE: return ma_format_s32; - case MA_PA_SAMPLE_FLOAT32BE: return ma_format_f32; - default: break; - } - } - - /* Endian agnostic. */ - switch (format) { - case MA_PA_SAMPLE_U8: return ma_format_u8; - default: return ma_format_unknown; - } -} - -static ma_channel ma_channel_position_from_pulse(ma_pa_channel_position_t position) -{ - switch (position) - { - case MA_PA_CHANNEL_POSITION_INVALID: return MA_CHANNEL_NONE; - case MA_PA_CHANNEL_POSITION_MONO: return MA_CHANNEL_MONO; - case MA_PA_CHANNEL_POSITION_FRONT_LEFT: return MA_CHANNEL_FRONT_LEFT; - case MA_PA_CHANNEL_POSITION_FRONT_RIGHT: return MA_CHANNEL_FRONT_RIGHT; - case MA_PA_CHANNEL_POSITION_FRONT_CENTER: return MA_CHANNEL_FRONT_CENTER; - case MA_PA_CHANNEL_POSITION_REAR_CENTER: return MA_CHANNEL_BACK_CENTER; - case MA_PA_CHANNEL_POSITION_REAR_LEFT: return MA_CHANNEL_BACK_LEFT; - case MA_PA_CHANNEL_POSITION_REAR_RIGHT: return MA_CHANNEL_BACK_RIGHT; - case MA_PA_CHANNEL_POSITION_LFE: return MA_CHANNEL_LFE; - case MA_PA_CHANNEL_POSITION_FRONT_LEFT_OF_CENTER: return MA_CHANNEL_FRONT_LEFT_CENTER; - case MA_PA_CHANNEL_POSITION_FRONT_RIGHT_OF_CENTER: return MA_CHANNEL_FRONT_RIGHT_CENTER; - case MA_PA_CHANNEL_POSITION_SIDE_LEFT: return MA_CHANNEL_SIDE_LEFT; - case MA_PA_CHANNEL_POSITION_SIDE_RIGHT: return MA_CHANNEL_SIDE_RIGHT; - case MA_PA_CHANNEL_POSITION_AUX0: return MA_CHANNEL_AUX_0; - case MA_PA_CHANNEL_POSITION_AUX1: return MA_CHANNEL_AUX_1; - case MA_PA_CHANNEL_POSITION_AUX2: return MA_CHANNEL_AUX_2; - case MA_PA_CHANNEL_POSITION_AUX3: return MA_CHANNEL_AUX_3; - case MA_PA_CHANNEL_POSITION_AUX4: return MA_CHANNEL_AUX_4; - case MA_PA_CHANNEL_POSITION_AUX5: return MA_CHANNEL_AUX_5; - case MA_PA_CHANNEL_POSITION_AUX6: return MA_CHANNEL_AUX_6; - case MA_PA_CHANNEL_POSITION_AUX7: return MA_CHANNEL_AUX_7; - case MA_PA_CHANNEL_POSITION_AUX8: return MA_CHANNEL_AUX_8; - case MA_PA_CHANNEL_POSITION_AUX9: return MA_CHANNEL_AUX_9; - case MA_PA_CHANNEL_POSITION_AUX10: return MA_CHANNEL_AUX_10; - case MA_PA_CHANNEL_POSITION_AUX11: return MA_CHANNEL_AUX_11; - case MA_PA_CHANNEL_POSITION_AUX12: return MA_CHANNEL_AUX_12; - case MA_PA_CHANNEL_POSITION_AUX13: return MA_CHANNEL_AUX_13; - case MA_PA_CHANNEL_POSITION_AUX14: return MA_CHANNEL_AUX_14; - case MA_PA_CHANNEL_POSITION_AUX15: return MA_CHANNEL_AUX_15; - case MA_PA_CHANNEL_POSITION_AUX16: return MA_CHANNEL_AUX_16; - case MA_PA_CHANNEL_POSITION_AUX17: return MA_CHANNEL_AUX_17; - case MA_PA_CHANNEL_POSITION_AUX18: return MA_CHANNEL_AUX_18; - case MA_PA_CHANNEL_POSITION_AUX19: return MA_CHANNEL_AUX_19; - case MA_PA_CHANNEL_POSITION_AUX20: return MA_CHANNEL_AUX_20; - case MA_PA_CHANNEL_POSITION_AUX21: return MA_CHANNEL_AUX_21; - case MA_PA_CHANNEL_POSITION_AUX22: return MA_CHANNEL_AUX_22; - case MA_PA_CHANNEL_POSITION_AUX23: return MA_CHANNEL_AUX_23; - case MA_PA_CHANNEL_POSITION_AUX24: return MA_CHANNEL_AUX_24; - case MA_PA_CHANNEL_POSITION_AUX25: return MA_CHANNEL_AUX_25; - case MA_PA_CHANNEL_POSITION_AUX26: return MA_CHANNEL_AUX_26; - case MA_PA_CHANNEL_POSITION_AUX27: return MA_CHANNEL_AUX_27; - case MA_PA_CHANNEL_POSITION_AUX28: return MA_CHANNEL_AUX_28; - case MA_PA_CHANNEL_POSITION_AUX29: return MA_CHANNEL_AUX_29; - case MA_PA_CHANNEL_POSITION_AUX30: return MA_CHANNEL_AUX_30; - case MA_PA_CHANNEL_POSITION_AUX31: return MA_CHANNEL_AUX_31; - case MA_PA_CHANNEL_POSITION_TOP_CENTER: return MA_CHANNEL_TOP_CENTER; - case MA_PA_CHANNEL_POSITION_TOP_FRONT_LEFT: return MA_CHANNEL_TOP_FRONT_LEFT; - case MA_PA_CHANNEL_POSITION_TOP_FRONT_RIGHT: return MA_CHANNEL_TOP_FRONT_RIGHT; - case MA_PA_CHANNEL_POSITION_TOP_FRONT_CENTER: return MA_CHANNEL_TOP_FRONT_CENTER; - case MA_PA_CHANNEL_POSITION_TOP_REAR_LEFT: return MA_CHANNEL_TOP_BACK_LEFT; - case MA_PA_CHANNEL_POSITION_TOP_REAR_RIGHT: return MA_CHANNEL_TOP_BACK_RIGHT; - case MA_PA_CHANNEL_POSITION_TOP_REAR_CENTER: return MA_CHANNEL_TOP_BACK_CENTER; - default: return MA_CHANNEL_NONE; - } -} - -#if 0 -static ma_pa_channel_position_t ma_channel_position_to_pulse(ma_channel position) -{ - switch (position) - { - case MA_CHANNEL_NONE: return MA_PA_CHANNEL_POSITION_INVALID; - case MA_CHANNEL_FRONT_LEFT: return MA_PA_CHANNEL_POSITION_FRONT_LEFT; - case MA_CHANNEL_FRONT_RIGHT: return MA_PA_CHANNEL_POSITION_FRONT_RIGHT; - case MA_CHANNEL_FRONT_CENTER: return MA_PA_CHANNEL_POSITION_FRONT_CENTER; - case MA_CHANNEL_LFE: return MA_PA_CHANNEL_POSITION_LFE; - case MA_CHANNEL_BACK_LEFT: return MA_PA_CHANNEL_POSITION_REAR_LEFT; - case MA_CHANNEL_BACK_RIGHT: return MA_PA_CHANNEL_POSITION_REAR_RIGHT; - case MA_CHANNEL_FRONT_LEFT_CENTER: return MA_PA_CHANNEL_POSITION_FRONT_LEFT_OF_CENTER; - case MA_CHANNEL_FRONT_RIGHT_CENTER: return MA_PA_CHANNEL_POSITION_FRONT_RIGHT_OF_CENTER; - case MA_CHANNEL_BACK_CENTER: return MA_PA_CHANNEL_POSITION_REAR_CENTER; - case MA_CHANNEL_SIDE_LEFT: return MA_PA_CHANNEL_POSITION_SIDE_LEFT; - case MA_CHANNEL_SIDE_RIGHT: return MA_PA_CHANNEL_POSITION_SIDE_RIGHT; - case MA_CHANNEL_TOP_CENTER: return MA_PA_CHANNEL_POSITION_TOP_CENTER; - case MA_CHANNEL_TOP_FRONT_LEFT: return MA_PA_CHANNEL_POSITION_TOP_FRONT_LEFT; - case MA_CHANNEL_TOP_FRONT_CENTER: return MA_PA_CHANNEL_POSITION_TOP_FRONT_CENTER; - case MA_CHANNEL_TOP_FRONT_RIGHT: return MA_PA_CHANNEL_POSITION_TOP_FRONT_RIGHT; - case MA_CHANNEL_TOP_BACK_LEFT: return MA_PA_CHANNEL_POSITION_TOP_REAR_LEFT; - case MA_CHANNEL_TOP_BACK_CENTER: return MA_PA_CHANNEL_POSITION_TOP_REAR_CENTER; - case MA_CHANNEL_TOP_BACK_RIGHT: return MA_PA_CHANNEL_POSITION_TOP_REAR_RIGHT; - case MA_CHANNEL_19: return MA_PA_CHANNEL_POSITION_AUX18; - case MA_CHANNEL_20: return MA_PA_CHANNEL_POSITION_AUX19; - case MA_CHANNEL_21: return MA_PA_CHANNEL_POSITION_AUX20; - case MA_CHANNEL_22: return MA_PA_CHANNEL_POSITION_AUX21; - case MA_CHANNEL_23: return MA_PA_CHANNEL_POSITION_AUX22; - case MA_CHANNEL_24: return MA_PA_CHANNEL_POSITION_AUX23; - case MA_CHANNEL_25: return MA_PA_CHANNEL_POSITION_AUX24; - case MA_CHANNEL_26: return MA_PA_CHANNEL_POSITION_AUX25; - case MA_CHANNEL_27: return MA_PA_CHANNEL_POSITION_AUX26; - case MA_CHANNEL_28: return MA_PA_CHANNEL_POSITION_AUX27; - case MA_CHANNEL_29: return MA_PA_CHANNEL_POSITION_AUX28; - case MA_CHANNEL_30: return MA_PA_CHANNEL_POSITION_AUX29; - case MA_CHANNEL_31: return MA_PA_CHANNEL_POSITION_AUX30; - case MA_CHANNEL_32: return MA_PA_CHANNEL_POSITION_AUX31; - default: return (ma_pa_channel_position_t)position; - } -} -#endif - -static ma_result ma_wait_for_operation__pulse(ma_context* pContext, ma_ptr pMainLoop, ma_pa_operation* pOP) -{ - int resultPA; - ma_pa_operation_state_t state; - - MA_ASSERT(pContext != NULL); - MA_ASSERT(pOP != NULL); - - for (;;) { - state = ((ma_pa_operation_get_state_proc)pContext->pulse.pa_operation_get_state)(pOP); - if (state != MA_PA_OPERATION_RUNNING) { - break; /* Done. */ - } - - resultPA = ((ma_pa_mainloop_iterate_proc)pContext->pulse.pa_mainloop_iterate)((ma_pa_mainloop*)pMainLoop, 1, NULL); - if (resultPA < 0) { - return ma_result_from_pulse(resultPA); - } - } - - return MA_SUCCESS; -} - -static ma_result ma_wait_for_operation_and_unref__pulse(ma_context* pContext, ma_ptr pMainLoop, ma_pa_operation* pOP) -{ - ma_result result; - - if (pOP == NULL) { - return MA_INVALID_ARGS; - } - - result = ma_wait_for_operation__pulse(pContext, pMainLoop, pOP); - ((ma_pa_operation_unref_proc)pContext->pulse.pa_operation_unref)(pOP); - - return result; -} - -static ma_result ma_wait_for_pa_context_to_connect__pulse(ma_context* pContext, ma_ptr pMainLoop, ma_ptr pPulseContext) -{ - int resultPA; - ma_pa_context_state_t state; - - for (;;) { - state = ((ma_pa_context_get_state_proc)pContext->pulse.pa_context_get_state)((ma_pa_context*)pPulseContext); - if (state == MA_PA_CONTEXT_READY) { - break; /* Done. */ - } - - if (state == MA_PA_CONTEXT_FAILED || state == MA_PA_CONTEXT_TERMINATED) { - ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[PulseAudio] An error occurred while connecting the PulseAudio context."); - return MA_ERROR; - } - - resultPA = ((ma_pa_mainloop_iterate_proc)pContext->pulse.pa_mainloop_iterate)((ma_pa_mainloop*)pMainLoop, 1, NULL); - if (resultPA < 0) { - return ma_result_from_pulse(resultPA); - } - } - - /* Should never get here. */ - return MA_SUCCESS; -} - -static ma_result ma_wait_for_pa_stream_to_connect__pulse(ma_context* pContext, ma_ptr pMainLoop, ma_ptr pStream) -{ - int resultPA; - ma_pa_stream_state_t state; - - for (;;) { - state = ((ma_pa_stream_get_state_proc)pContext->pulse.pa_stream_get_state)((ma_pa_stream*)pStream); - if (state == MA_PA_STREAM_READY) { - break; /* Done. */ - } - - if (state == MA_PA_STREAM_FAILED || state == MA_PA_STREAM_TERMINATED) { - ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[PulseAudio] An error occurred while connecting the PulseAudio stream."); - return MA_ERROR; - } - - resultPA = ((ma_pa_mainloop_iterate_proc)pContext->pulse.pa_mainloop_iterate)((ma_pa_mainloop*)pMainLoop, 1, NULL); - if (resultPA < 0) { - return ma_result_from_pulse(resultPA); - } - } - - return MA_SUCCESS; -} - - -static ma_result ma_init_pa_mainloop_and_pa_context__pulse(ma_context* pContext, const char* pApplicationName, const char* pServerName, ma_bool32 tryAutoSpawn, ma_ptr* ppMainLoop, ma_ptr* ppPulseContext) -{ - ma_result result; - ma_ptr pMainLoop; - ma_ptr pPulseContext; - - MA_ASSERT(ppMainLoop != NULL); - MA_ASSERT(ppPulseContext != NULL); - - /* The PulseAudio context maps well to miniaudio's notion of a context. The pa_context object will be initialized as part of the ma_context. */ - pMainLoop = ((ma_pa_mainloop_new_proc)pContext->pulse.pa_mainloop_new)(); - if (pMainLoop == NULL) { - ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[PulseAudio] Failed to create mainloop."); - return MA_FAILED_TO_INIT_BACKEND; - } - - pPulseContext = ((ma_pa_context_new_proc)pContext->pulse.pa_context_new)(((ma_pa_mainloop_get_api_proc)pContext->pulse.pa_mainloop_get_api)((ma_pa_mainloop*)pMainLoop), pApplicationName); - if (pPulseContext == NULL) { - ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[PulseAudio] Failed to create PulseAudio context."); - ((ma_pa_mainloop_free_proc)pContext->pulse.pa_mainloop_free)((ma_pa_mainloop*)(pMainLoop)); - return MA_FAILED_TO_INIT_BACKEND; - } - - /* Now we need to connect to the context. Everything is asynchronous so we need to wait for it to connect before returning. */ - result = ma_result_from_pulse(((ma_pa_context_connect_proc)pContext->pulse.pa_context_connect)((ma_pa_context*)pPulseContext, pServerName, (tryAutoSpawn) ? 0 : MA_PA_CONTEXT_NOAUTOSPAWN, NULL)); - if (result != MA_SUCCESS) { - ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[PulseAudio] Failed to connect PulseAudio context."); - ((ma_pa_mainloop_free_proc)pContext->pulse.pa_mainloop_free)((ma_pa_mainloop*)(pMainLoop)); - return result; - } - - /* Since ma_context_init() runs synchronously we need to wait for the PulseAudio context to connect before we return. */ - result = ma_wait_for_pa_context_to_connect__pulse(pContext, pMainLoop, pPulseContext); - if (result != MA_SUCCESS) { - ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[PulseAudio] Waiting for connection failed."); - ((ma_pa_mainloop_free_proc)pContext->pulse.pa_mainloop_free)((ma_pa_mainloop*)(pMainLoop)); - return result; - } - - *ppMainLoop = pMainLoop; - *ppPulseContext = pPulseContext; - - return MA_SUCCESS; -} - - -static void ma_device_sink_info_callback(ma_pa_context* pPulseContext, const ma_pa_sink_info* pInfo, int endOfList, void* pUserData) -{ - ma_pa_sink_info* pInfoOut; - - if (endOfList > 0) { - return; - } - - /* - There has been a report that indicates that pInfo can be null which results - in a null pointer dereference below. We'll check for this for safety. - */ - if (pInfo == NULL) { - return; - } - - pInfoOut = (ma_pa_sink_info*)pUserData; - MA_ASSERT(pInfoOut != NULL); - - *pInfoOut = *pInfo; - - (void)pPulseContext; /* Unused. */ -} - -static void ma_device_source_info_callback(ma_pa_context* pPulseContext, const ma_pa_source_info* pInfo, int endOfList, void* pUserData) -{ - ma_pa_source_info* pInfoOut; - - if (endOfList > 0) { - return; - } - - /* - There has been a report that indicates that pInfo can be null which results - in a null pointer dereference below. We'll check for this for safety. - */ - if (pInfo == NULL) { - return; - } - - pInfoOut = (ma_pa_source_info*)pUserData; - MA_ASSERT(pInfoOut != NULL); - - *pInfoOut = *pInfo; - - (void)pPulseContext; /* Unused. */ -} - -#if 0 -static void ma_device_sink_name_callback(ma_pa_context* pPulseContext, const ma_pa_sink_info* pInfo, int endOfList, void* pUserData) -{ - ma_device* pDevice; - - if (endOfList > 0) { - return; - } - - pDevice = (ma_device*)pUserData; - MA_ASSERT(pDevice != NULL); - - ma_strncpy_s(pDevice->playback.name, sizeof(pDevice->playback.name), pInfo->description, (size_t)-1); - - (void)pPulseContext; /* Unused. */ -} - -static void ma_device_source_name_callback(ma_pa_context* pPulseContext, const ma_pa_source_info* pInfo, int endOfList, void* pUserData) -{ - ma_device* pDevice; - - if (endOfList > 0) { - return; - } - - pDevice = (ma_device*)pUserData; - MA_ASSERT(pDevice != NULL); - - ma_strncpy_s(pDevice->capture.name, sizeof(pDevice->capture.name), pInfo->description, (size_t)-1); - - (void)pPulseContext; /* Unused. */ -} -#endif - -static ma_result ma_context_get_sink_info__pulse(ma_context* pContext, const char* pDeviceName, ma_pa_sink_info* pSinkInfo) -{ - ma_pa_operation* pOP; - - pOP = ((ma_pa_context_get_sink_info_by_name_proc)pContext->pulse.pa_context_get_sink_info_by_name)((ma_pa_context*)pContext->pulse.pPulseContext, pDeviceName, ma_device_sink_info_callback, pSinkInfo); - if (pOP == NULL) { - return MA_ERROR; - } - - return ma_wait_for_operation_and_unref__pulse(pContext, pContext->pulse.pMainLoop, pOP); -} - -static ma_result ma_context_get_source_info__pulse(ma_context* pContext, const char* pDeviceName, ma_pa_source_info* pSourceInfo) -{ - ma_pa_operation* pOP; - - pOP = ((ma_pa_context_get_source_info_by_name_proc)pContext->pulse.pa_context_get_source_info_by_name)((ma_pa_context*)pContext->pulse.pPulseContext, pDeviceName, ma_device_source_info_callback, pSourceInfo); - if (pOP == NULL) { - return MA_ERROR; - } - - return ma_wait_for_operation_and_unref__pulse(pContext, pContext->pulse.pMainLoop, pOP); -} - -static ma_result ma_context_get_default_device_index__pulse(ma_context* pContext, ma_device_type deviceType, ma_uint32* pIndex) -{ - ma_result result; - - MA_ASSERT(pContext != NULL); - MA_ASSERT(pIndex != NULL); - - if (pIndex != NULL) { - *pIndex = (ma_uint32)-1; - } - - if (deviceType == ma_device_type_playback) { - ma_pa_sink_info sinkInfo; - result = ma_context_get_sink_info__pulse(pContext, NULL, &sinkInfo); - if (result != MA_SUCCESS) { - return result; - } - - if (pIndex != NULL) { - *pIndex = sinkInfo.index; - } - } - - if (deviceType == ma_device_type_capture) { - ma_pa_source_info sourceInfo; - result = ma_context_get_source_info__pulse(pContext, NULL, &sourceInfo); - if (result != MA_SUCCESS) { - return result; - } - - if (pIndex != NULL) { - *pIndex = sourceInfo.index; - } - } - - return MA_SUCCESS; -} - - -typedef struct -{ - ma_context* pContext; - ma_enum_devices_callback_proc callback; - void* pUserData; - ma_bool32 isTerminated; - ma_uint32 defaultDeviceIndexPlayback; - ma_uint32 defaultDeviceIndexCapture; -} ma_context_enumerate_devices_callback_data__pulse; - -static void ma_context_enumerate_devices_sink_callback__pulse(ma_pa_context* pPulseContext, const ma_pa_sink_info* pSinkInfo, int endOfList, void* pUserData) -{ - ma_context_enumerate_devices_callback_data__pulse* pData = (ma_context_enumerate_devices_callback_data__pulse*)pUserData; - ma_device_info deviceInfo; - - MA_ASSERT(pData != NULL); - - if (endOfList || pData->isTerminated) { - return; - } - - MA_ZERO_OBJECT(&deviceInfo); - - /* The name from PulseAudio is the ID for miniaudio. */ - if (pSinkInfo->name != NULL) { - ma_strncpy_s(deviceInfo.id.pulse, sizeof(deviceInfo.id.pulse), pSinkInfo->name, (size_t)-1); - } - - /* The description from PulseAudio is the name for miniaudio. */ - if (pSinkInfo->description != NULL) { - ma_strncpy_s(deviceInfo.name, sizeof(deviceInfo.name), pSinkInfo->description, (size_t)-1); - } - - if (pSinkInfo->index == pData->defaultDeviceIndexPlayback) { - deviceInfo.isDefault = MA_TRUE; - } - - pData->isTerminated = !pData->callback(pData->pContext, ma_device_type_playback, &deviceInfo, pData->pUserData); - - (void)pPulseContext; /* Unused. */ -} - -static void ma_context_enumerate_devices_source_callback__pulse(ma_pa_context* pPulseContext, const ma_pa_source_info* pSourceInfo, int endOfList, void* pUserData) -{ - ma_context_enumerate_devices_callback_data__pulse* pData = (ma_context_enumerate_devices_callback_data__pulse*)pUserData; - ma_device_info deviceInfo; - - MA_ASSERT(pData != NULL); - - if (endOfList || pData->isTerminated) { - return; - } - - MA_ZERO_OBJECT(&deviceInfo); - - /* The name from PulseAudio is the ID for miniaudio. */ - if (pSourceInfo->name != NULL) { - ma_strncpy_s(deviceInfo.id.pulse, sizeof(deviceInfo.id.pulse), pSourceInfo->name, (size_t)-1); - } - - /* The description from PulseAudio is the name for miniaudio. */ - if (pSourceInfo->description != NULL) { - ma_strncpy_s(deviceInfo.name, sizeof(deviceInfo.name), pSourceInfo->description, (size_t)-1); - } - - if (pSourceInfo->index == pData->defaultDeviceIndexCapture) { - deviceInfo.isDefault = MA_TRUE; - } - - pData->isTerminated = !pData->callback(pData->pContext, ma_device_type_capture, &deviceInfo, pData->pUserData); - - (void)pPulseContext; /* Unused. */ -} - -static ma_result ma_context_enumerate_devices__pulse(ma_context* pContext, ma_enum_devices_callback_proc callback, void* pUserData) -{ - ma_result result = MA_SUCCESS; - ma_context_enumerate_devices_callback_data__pulse callbackData; - ma_pa_operation* pOP = NULL; - - MA_ASSERT(pContext != NULL); - MA_ASSERT(callback != NULL); - - callbackData.pContext = pContext; - callbackData.callback = callback; - callbackData.pUserData = pUserData; - callbackData.isTerminated = MA_FALSE; - callbackData.defaultDeviceIndexPlayback = (ma_uint32)-1; - callbackData.defaultDeviceIndexCapture = (ma_uint32)-1; - - /* We need to get the index of the default devices. */ - ma_context_get_default_device_index__pulse(pContext, ma_device_type_playback, &callbackData.defaultDeviceIndexPlayback); - ma_context_get_default_device_index__pulse(pContext, ma_device_type_capture, &callbackData.defaultDeviceIndexCapture); - - /* Playback. */ - if (!callbackData.isTerminated) { - pOP = ((ma_pa_context_get_sink_info_list_proc)pContext->pulse.pa_context_get_sink_info_list)((ma_pa_context*)(pContext->pulse.pPulseContext), ma_context_enumerate_devices_sink_callback__pulse, &callbackData); - if (pOP == NULL) { - result = MA_ERROR; - goto done; - } - - result = ma_wait_for_operation__pulse(pContext, pContext->pulse.pMainLoop, pOP); - ((ma_pa_operation_unref_proc)pContext->pulse.pa_operation_unref)(pOP); - - if (result != MA_SUCCESS) { - goto done; - } - } - - - /* Capture. */ - if (!callbackData.isTerminated) { - pOP = ((ma_pa_context_get_source_info_list_proc)pContext->pulse.pa_context_get_source_info_list)((ma_pa_context*)(pContext->pulse.pPulseContext), ma_context_enumerate_devices_source_callback__pulse, &callbackData); - if (pOP == NULL) { - result = MA_ERROR; - goto done; - } - - result = ma_wait_for_operation__pulse(pContext, pContext->pulse.pMainLoop, pOP); - ((ma_pa_operation_unref_proc)pContext->pulse.pa_operation_unref)(pOP); - - if (result != MA_SUCCESS) { - goto done; - } - } - -done: - return result; -} - - -typedef struct -{ - ma_device_info* pDeviceInfo; - ma_uint32 defaultDeviceIndex; - ma_bool32 foundDevice; -} ma_context_get_device_info_callback_data__pulse; - -static void ma_context_get_device_info_sink_callback__pulse(ma_pa_context* pPulseContext, const ma_pa_sink_info* pInfo, int endOfList, void* pUserData) -{ - ma_context_get_device_info_callback_data__pulse* pData = (ma_context_get_device_info_callback_data__pulse*)pUserData; - - if (endOfList > 0) { - return; - } - - MA_ASSERT(pData != NULL); - pData->foundDevice = MA_TRUE; - - if (pInfo->name != NULL) { - ma_strncpy_s(pData->pDeviceInfo->id.pulse, sizeof(pData->pDeviceInfo->id.pulse), pInfo->name, (size_t)-1); - } - - if (pInfo->description != NULL) { - ma_strncpy_s(pData->pDeviceInfo->name, sizeof(pData->pDeviceInfo->name), pInfo->description, (size_t)-1); - } - - /* - We're just reporting a single data format here. I think technically PulseAudio might support - all formats, but I don't trust that PulseAudio will do *anything* right, so I'm just going to - report the "native" device format. - */ - pData->pDeviceInfo->nativeDataFormats[0].format = ma_format_from_pulse(pInfo->sample_spec.format); - pData->pDeviceInfo->nativeDataFormats[0].channels = pInfo->sample_spec.channels; - pData->pDeviceInfo->nativeDataFormats[0].sampleRate = pInfo->sample_spec.rate; - pData->pDeviceInfo->nativeDataFormats[0].flags = 0; - pData->pDeviceInfo->nativeDataFormatCount = 1; - - if (pData->defaultDeviceIndex == pInfo->index) { - pData->pDeviceInfo->isDefault = MA_TRUE; - } - - (void)pPulseContext; /* Unused. */ -} - -static void ma_context_get_device_info_source_callback__pulse(ma_pa_context* pPulseContext, const ma_pa_source_info* pInfo, int endOfList, void* pUserData) -{ - ma_context_get_device_info_callback_data__pulse* pData = (ma_context_get_device_info_callback_data__pulse*)pUserData; - - if (endOfList > 0) { - return; - } - - MA_ASSERT(pData != NULL); - pData->foundDevice = MA_TRUE; - - if (pInfo->name != NULL) { - ma_strncpy_s(pData->pDeviceInfo->id.pulse, sizeof(pData->pDeviceInfo->id.pulse), pInfo->name, (size_t)-1); - } - - if (pInfo->description != NULL) { - ma_strncpy_s(pData->pDeviceInfo->name, sizeof(pData->pDeviceInfo->name), pInfo->description, (size_t)-1); - } - - /* - We're just reporting a single data format here. I think technically PulseAudio might support - all formats, but I don't trust that PulseAudio will do *anything* right, so I'm just going to - report the "native" device format. - */ - pData->pDeviceInfo->nativeDataFormats[0].format = ma_format_from_pulse(pInfo->sample_spec.format); - pData->pDeviceInfo->nativeDataFormats[0].channels = pInfo->sample_spec.channels; - pData->pDeviceInfo->nativeDataFormats[0].sampleRate = pInfo->sample_spec.rate; - pData->pDeviceInfo->nativeDataFormats[0].flags = 0; - pData->pDeviceInfo->nativeDataFormatCount = 1; - - if (pData->defaultDeviceIndex == pInfo->index) { - pData->pDeviceInfo->isDefault = MA_TRUE; - } - - (void)pPulseContext; /* Unused. */ -} - -static ma_result ma_context_get_device_info__pulse(ma_context* pContext, ma_device_type deviceType, const ma_device_id* pDeviceID, ma_device_info* pDeviceInfo) -{ - ma_result result = MA_SUCCESS; - ma_context_get_device_info_callback_data__pulse callbackData; - ma_pa_operation* pOP = NULL; - const char* pDeviceName = NULL; - - MA_ASSERT(pContext != NULL); - - callbackData.pDeviceInfo = pDeviceInfo; - callbackData.foundDevice = MA_FALSE; - - if (pDeviceID != NULL) { - pDeviceName = pDeviceID->pulse; - } else { - pDeviceName = NULL; - } - - result = ma_context_get_default_device_index__pulse(pContext, deviceType, &callbackData.defaultDeviceIndex); - - if (deviceType == ma_device_type_playback) { - pOP = ((ma_pa_context_get_sink_info_by_name_proc)pContext->pulse.pa_context_get_sink_info_by_name)((ma_pa_context*)(pContext->pulse.pPulseContext), pDeviceName, ma_context_get_device_info_sink_callback__pulse, &callbackData); - } else { - pOP = ((ma_pa_context_get_source_info_by_name_proc)pContext->pulse.pa_context_get_source_info_by_name)((ma_pa_context*)(pContext->pulse.pPulseContext), pDeviceName, ma_context_get_device_info_source_callback__pulse, &callbackData); - } - - if (pOP != NULL) { - ma_wait_for_operation_and_unref__pulse(pContext, pContext->pulse.pMainLoop, pOP); - } else { - result = MA_ERROR; - goto done; - } - - if (!callbackData.foundDevice) { - result = MA_NO_DEVICE; - goto done; - } - -done: - return result; -} - -static ma_result ma_device_uninit__pulse(ma_device* pDevice) -{ - ma_context* pContext; - - MA_ASSERT(pDevice != NULL); - - pContext = pDevice->pContext; - MA_ASSERT(pContext != NULL); - - if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) { - ((ma_pa_stream_disconnect_proc)pContext->pulse.pa_stream_disconnect)((ma_pa_stream*)pDevice->pulse.pStreamCapture); - ((ma_pa_stream_unref_proc)pContext->pulse.pa_stream_unref)((ma_pa_stream*)pDevice->pulse.pStreamCapture); - } - - if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) { - ((ma_pa_stream_disconnect_proc)pContext->pulse.pa_stream_disconnect)((ma_pa_stream*)pDevice->pulse.pStreamPlayback); - ((ma_pa_stream_unref_proc)pContext->pulse.pa_stream_unref)((ma_pa_stream*)pDevice->pulse.pStreamPlayback); - } - - if (pDevice->type == ma_device_type_duplex) { - ma_duplex_rb_uninit(&pDevice->duplexRB); - } - - ((ma_pa_context_disconnect_proc)pContext->pulse.pa_context_disconnect)((ma_pa_context*)pDevice->pulse.pPulseContext); - ((ma_pa_context_unref_proc)pContext->pulse.pa_context_unref)((ma_pa_context*)pDevice->pulse.pPulseContext); - ((ma_pa_mainloop_free_proc)pContext->pulse.pa_mainloop_free)((ma_pa_mainloop*)pDevice->pulse.pMainLoop); - - return MA_SUCCESS; -} - -static ma_pa_buffer_attr ma_device__pa_buffer_attr_new(ma_uint32 periodSizeInFrames, ma_uint32 periods, const ma_pa_sample_spec* ss) -{ - ma_pa_buffer_attr attr; - attr.maxlength = periodSizeInFrames * periods * ma_get_bytes_per_frame(ma_format_from_pulse(ss->format), ss->channels); - attr.tlength = attr.maxlength / periods; - attr.prebuf = (ma_uint32)-1; - attr.minreq = (ma_uint32)-1; - attr.fragsize = attr.maxlength / periods; - - return attr; -} - -static ma_pa_stream* ma_device__pa_stream_new__pulse(ma_device* pDevice, const char* pStreamName, const ma_pa_sample_spec* ss, const ma_pa_channel_map* cmap) -{ - static ma_atomic_uint32 g_StreamCounter = { 0 }; - char actualStreamName[256]; - - if (pStreamName != NULL) { - ma_strncpy_s(actualStreamName, sizeof(actualStreamName), pStreamName, (size_t)-1); - } else { - const char* pBaseName = "miniaudio:"; - size_t baseNameLen = strlen(pBaseName); - ma_strcpy_s(actualStreamName, sizeof(actualStreamName), pBaseName); - ma_itoa_s((int)ma_atomic_uint32_get(&g_StreamCounter), actualStreamName + baseNameLen, sizeof(actualStreamName)-baseNameLen, 10); - } - ma_atomic_uint32_fetch_add(&g_StreamCounter, 1); - - return ((ma_pa_stream_new_proc)pDevice->pContext->pulse.pa_stream_new)((ma_pa_context*)pDevice->pulse.pPulseContext, actualStreamName, ss, cmap); -} - - -static void ma_device_on_read__pulse(ma_pa_stream* pStream, size_t byteCount, void* pUserData) -{ - ma_device* pDevice = (ma_device*)pUserData; - ma_uint32 bpf; - ma_uint32 deviceState; - ma_uint64 frameCount; - ma_uint64 framesProcessed; - - MA_ASSERT(pDevice != NULL); - - /* - Don't do anything if the device isn't initialized yet. Yes, this can happen because PulseAudio - can fire this callback before the stream has even started. Ridiculous. - */ - deviceState = ma_device_get_state(pDevice); - if (deviceState != ma_device_state_starting && deviceState != ma_device_state_started) { - return; - } - - bpf = ma_get_bytes_per_frame(pDevice->capture.internalFormat, pDevice->capture.internalChannels); - MA_ASSERT(bpf > 0); - - frameCount = byteCount / bpf; - framesProcessed = 0; - - while (ma_device_get_state(pDevice) == ma_device_state_started && framesProcessed < frameCount) { - const void* pMappedPCMFrames; - size_t bytesMapped; - ma_uint64 framesMapped; - - int pulseResult = ((ma_pa_stream_peek_proc)pDevice->pContext->pulse.pa_stream_peek)(pStream, &pMappedPCMFrames, &bytesMapped); - if (pulseResult < 0) { - break; /* Failed to map. Abort. */ - } - - framesMapped = bytesMapped / bpf; - if (framesMapped > 0) { - if (pMappedPCMFrames != NULL) { - ma_device_handle_backend_data_callback(pDevice, NULL, pMappedPCMFrames, framesMapped); - } else { - /* It's a hole. */ - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "[PulseAudio] ma_device_on_read__pulse: Hole.\n"); - } - - pulseResult = ((ma_pa_stream_drop_proc)pDevice->pContext->pulse.pa_stream_drop)(pStream); - if (pulseResult < 0) { - break; /* Failed to drop the buffer. */ - } - - framesProcessed += framesMapped; - - } else { - /* Nothing was mapped. Just abort. */ - break; - } - } -} - -static ma_result ma_device_write_to_stream__pulse(ma_device* pDevice, ma_pa_stream* pStream, ma_uint64* pFramesProcessed) -{ - ma_result result = MA_SUCCESS; - ma_uint64 framesProcessed = 0; - size_t bytesMapped; - ma_uint32 bpf; - ma_uint32 deviceState; - - MA_ASSERT(pDevice != NULL); - MA_ASSERT(pStream != NULL); - - bpf = ma_get_bytes_per_frame(pDevice->playback.internalFormat, pDevice->playback.internalChannels); - MA_ASSERT(bpf > 0); - - deviceState = ma_device_get_state(pDevice); - - bytesMapped = ((ma_pa_stream_writable_size_proc)pDevice->pContext->pulse.pa_stream_writable_size)(pStream); - if (bytesMapped != (size_t)-1) { - if (bytesMapped > 0) { - ma_uint64 framesMapped; - void* pMappedPCMFrames; - int pulseResult = ((ma_pa_stream_begin_write_proc)pDevice->pContext->pulse.pa_stream_begin_write)(pStream, &pMappedPCMFrames, &bytesMapped); - if (pulseResult < 0) { - result = ma_result_from_pulse(pulseResult); - goto done; - } - - framesMapped = bytesMapped / bpf; - - if (deviceState == ma_device_state_started || deviceState == ma_device_state_starting) { /* Check for starting state just in case this is being used to do the initial fill. */ - ma_device_handle_backend_data_callback(pDevice, pMappedPCMFrames, NULL, framesMapped); - } else { - /* Device is not started. Write silence. */ - ma_silence_pcm_frames(pMappedPCMFrames, framesMapped, pDevice->playback.format, pDevice->playback.channels); - } - - pulseResult = ((ma_pa_stream_write_proc)pDevice->pContext->pulse.pa_stream_write)(pStream, pMappedPCMFrames, bytesMapped, NULL, 0, MA_PA_SEEK_RELATIVE); - if (pulseResult < 0) { - result = ma_result_from_pulse(pulseResult); - goto done; /* Failed to write data to stream. */ - } - - framesProcessed += framesMapped; - } else { - result = MA_SUCCESS; /* No data available for writing. */ - goto done; - } - } else { - result = MA_ERROR; /* Failed to retrieve the writable size. Abort. */ - goto done; - } - -done: - if (pFramesProcessed != NULL) { - *pFramesProcessed = framesProcessed; - } - - return result; -} - -static void ma_device_on_write__pulse(ma_pa_stream* pStream, size_t byteCount, void* pUserData) -{ - ma_device* pDevice = (ma_device*)pUserData; - ma_uint32 bpf; - ma_uint64 frameCount; - ma_uint64 framesProcessed; - ma_uint32 deviceState; - ma_result result; - - MA_ASSERT(pDevice != NULL); - - /* - Don't do anything if the device isn't initialized yet. Yes, this can happen because PulseAudio - can fire this callback before the stream has even started. Ridiculous. - */ - deviceState = ma_device_get_state(pDevice); - if (deviceState != ma_device_state_starting && deviceState != ma_device_state_started) { - return; - } - - bpf = ma_get_bytes_per_frame(pDevice->playback.internalFormat, pDevice->playback.internalChannels); - MA_ASSERT(bpf > 0); - - frameCount = byteCount / bpf; - framesProcessed = 0; - - while (framesProcessed < frameCount) { - ma_uint64 framesProcessedThisIteration; - - /* Don't keep trying to process frames if the device isn't started. */ - deviceState = ma_device_get_state(pDevice); - if (deviceState != ma_device_state_starting && deviceState != ma_device_state_started) { - break; - } - - result = ma_device_write_to_stream__pulse(pDevice, pStream, &framesProcessedThisIteration); - if (result != MA_SUCCESS) { - break; - } - - framesProcessed += framesProcessedThisIteration; - } -} - -static void ma_device_on_suspended__pulse(ma_pa_stream* pStream, void* pUserData) -{ - ma_device* pDevice = (ma_device*)pUserData; - int suspended; - - (void)pStream; - - suspended = ((ma_pa_stream_is_suspended_proc)pDevice->pContext->pulse.pa_stream_is_suspended)(pStream); - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "[Pulse] Device suspended state changed. pa_stream_is_suspended() returned %d.\n", suspended); - - if (suspended < 0) { - return; - } - - if (suspended == 1) { - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "[Pulse] Device suspended state changed. Suspended.\n"); - ma_device__on_notification_stopped(pDevice); - } else { - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "[Pulse] Device suspended state changed. Resumed.\n"); - ma_device__on_notification_started(pDevice); - } -} - -static void ma_device_on_rerouted__pulse(ma_pa_stream* pStream, void* pUserData) -{ - ma_device* pDevice = (ma_device*)pUserData; - - (void)pStream; - (void)pUserData; - - ma_device__on_notification_rerouted(pDevice); -} - -static ma_uint32 ma_calculate_period_size_in_frames_from_descriptor__pulse(const ma_device_descriptor* pDescriptor, ma_uint32 nativeSampleRate, ma_performance_profile performanceProfile) -{ - /* - There have been reports from users where buffers of < ~20ms result glitches when running through - PipeWire. To work around this we're going to have to use a different default buffer size. - */ - const ma_uint32 defaultPeriodSizeInMilliseconds_LowLatency = 25; - const ma_uint32 defaultPeriodSizeInMilliseconds_Conservative = MA_DEFAULT_PERIOD_SIZE_IN_MILLISECONDS_CONSERVATIVE; - - MA_ASSERT(nativeSampleRate != 0); - - if (pDescriptor->periodSizeInFrames == 0) { - if (pDescriptor->periodSizeInMilliseconds == 0) { - if (performanceProfile == ma_performance_profile_low_latency) { - return ma_calculate_buffer_size_in_frames_from_milliseconds(defaultPeriodSizeInMilliseconds_LowLatency, nativeSampleRate); - } else { - return ma_calculate_buffer_size_in_frames_from_milliseconds(defaultPeriodSizeInMilliseconds_Conservative, nativeSampleRate); - } - } else { - return ma_calculate_buffer_size_in_frames_from_milliseconds(pDescriptor->periodSizeInMilliseconds, nativeSampleRate); - } - } else { - return pDescriptor->periodSizeInFrames; - } -} - -static ma_result ma_device_init__pulse(ma_device* pDevice, const ma_device_config* pConfig, ma_device_descriptor* pDescriptorPlayback, ma_device_descriptor* pDescriptorCapture) -{ - /* - Notes for PulseAudio: - - - When both the period size in frames and milliseconds are 0, we default to miniaudio's - default buffer sizes rather than leaving it up to PulseAudio because I don't trust - PulseAudio to give us any kind of reasonable latency by default. - - - Do not ever, *ever* forget to use MA_PA_STREAM_ADJUST_LATENCY. If you don't specify this - flag, capture mode will just not work properly until you open another PulseAudio app. - */ - - ma_result result = MA_SUCCESS; - int error = 0; - const char* devPlayback = NULL; - const char* devCapture = NULL; - ma_format format = ma_format_unknown; - ma_uint32 channels = 0; - ma_uint32 sampleRate = 0; - ma_pa_sink_info sinkInfo; - ma_pa_source_info sourceInfo; - ma_pa_sample_spec ss; - ma_pa_channel_map cmap; - ma_pa_buffer_attr attr; - const ma_pa_sample_spec* pActualSS = NULL; - const ma_pa_buffer_attr* pActualAttr = NULL; - const ma_pa_channel_map* pActualChannelMap = NULL; - ma_uint32 iChannel; - ma_pa_stream_flags_t streamFlags; - - MA_ASSERT(pDevice != NULL); - MA_ZERO_OBJECT(&pDevice->pulse); - - if (pConfig->deviceType == ma_device_type_loopback) { - return MA_DEVICE_TYPE_NOT_SUPPORTED; - } - - /* No exclusive mode with the PulseAudio backend. */ - if (((pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) && pConfig->playback.shareMode == ma_share_mode_exclusive) || - ((pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) && pConfig->capture.shareMode == ma_share_mode_exclusive)) { - return MA_SHARE_MODE_NOT_SUPPORTED; - } - - if (pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) { - if (pDescriptorPlayback->pDeviceID != NULL) { - devPlayback = pDescriptorPlayback->pDeviceID->pulse; - } - - format = pDescriptorPlayback->format; - channels = pDescriptorPlayback->channels; - sampleRate = pDescriptorPlayback->sampleRate; - } - - if (pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) { - if (pDescriptorCapture->pDeviceID != NULL) { - devCapture = pDescriptorCapture->pDeviceID->pulse; - } - - format = pDescriptorCapture->format; - channels = pDescriptorCapture->channels; - sampleRate = pDescriptorCapture->sampleRate; - } - - - - result = ma_init_pa_mainloop_and_pa_context__pulse(pDevice->pContext, pDevice->pContext->pulse.pApplicationName, pDevice->pContext->pulse.pServerName, MA_FALSE, &pDevice->pulse.pMainLoop, &pDevice->pulse.pPulseContext); - if (result != MA_SUCCESS) { - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[PulseAudio] Failed to initialize PA mainloop and context for device.\n"); - return result; - } - - if (pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) { - result = ma_context_get_source_info__pulse(pDevice->pContext, devCapture, &sourceInfo); - if (result != MA_SUCCESS) { - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[PulseAudio] Failed to retrieve source info for capture device."); - goto on_error0; - } - - ss = sourceInfo.sample_spec; - cmap = sourceInfo.channel_map; - - /* Use the requested channel count if we have one. */ - if (pDescriptorCapture->channels != 0) { - ss.channels = pDescriptorCapture->channels; - } - - /* Use a default channel map. */ - ((ma_pa_channel_map_init_extend_proc)pDevice->pContext->pulse.pa_channel_map_init_extend)(&cmap, ss.channels, pConfig->pulse.channelMap); - - /* Use the requested sample rate if one was specified. */ - if (pDescriptorCapture->sampleRate != 0) { - ss.rate = pDescriptorCapture->sampleRate; - } - streamFlags = MA_PA_STREAM_START_CORKED | MA_PA_STREAM_ADJUST_LATENCY; - - if (ma_format_from_pulse(ss.format) == ma_format_unknown) { - if (ma_is_little_endian()) { - ss.format = MA_PA_SAMPLE_FLOAT32LE; - } else { - ss.format = MA_PA_SAMPLE_FLOAT32BE; - } - streamFlags |= MA_PA_STREAM_FIX_FORMAT; - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, "[PulseAudio] sample_spec.format not supported by miniaudio. Defaulting to PA_SAMPLE_FLOAT32.\n"); - } - if (ss.rate == 0) { - ss.rate = MA_DEFAULT_SAMPLE_RATE; - streamFlags |= MA_PA_STREAM_FIX_RATE; - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, "[PulseAudio] sample_spec.rate = 0. Defaulting to %d.\n", ss.rate); - } - if (ss.channels == 0) { - ss.channels = MA_DEFAULT_CHANNELS; - streamFlags |= MA_PA_STREAM_FIX_CHANNELS; - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, "[PulseAudio] sample_spec.channels = 0. Defaulting to %d.\n", ss.channels); - } - - /* We now have enough information to calculate our actual period size in frames. */ - pDescriptorCapture->periodSizeInFrames = ma_calculate_period_size_in_frames_from_descriptor__pulse(pDescriptorCapture, ss.rate, pConfig->performanceProfile); - - attr = ma_device__pa_buffer_attr_new(pDescriptorCapture->periodSizeInFrames, pDescriptorCapture->periodCount, &ss); - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, "[PulseAudio] Capture attr: maxlength=%d, tlength=%d, prebuf=%d, minreq=%d, fragsize=%d; periodSizeInFrames=%d\n", attr.maxlength, attr.tlength, attr.prebuf, attr.minreq, attr.fragsize, pDescriptorCapture->periodSizeInFrames); - - pDevice->pulse.pStreamCapture = ma_device__pa_stream_new__pulse(pDevice, pConfig->pulse.pStreamNameCapture, &ss, &cmap); - if (pDevice->pulse.pStreamCapture == NULL) { - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[PulseAudio] Failed to create PulseAudio capture stream.\n"); - result = MA_ERROR; - goto on_error0; - } - - - /* The callback needs to be set before connecting the stream. */ - ((ma_pa_stream_set_read_callback_proc)pDevice->pContext->pulse.pa_stream_set_read_callback)((ma_pa_stream*)pDevice->pulse.pStreamCapture, ma_device_on_read__pulse, pDevice); - - /* State callback for checking when the device has been corked. */ - ((ma_pa_stream_set_suspended_callback_proc)pDevice->pContext->pulse.pa_stream_set_suspended_callback)((ma_pa_stream*)pDevice->pulse.pStreamCapture, ma_device_on_suspended__pulse, pDevice); - - /* Rerouting notification. */ - ((ma_pa_stream_set_moved_callback_proc)pDevice->pContext->pulse.pa_stream_set_moved_callback)((ma_pa_stream*)pDevice->pulse.pStreamCapture, ma_device_on_rerouted__pulse, pDevice); - - - /* Connect after we've got all of our internal state set up. */ - if (devCapture != NULL) { - streamFlags |= MA_PA_STREAM_DONT_MOVE; - } - - error = ((ma_pa_stream_connect_record_proc)pDevice->pContext->pulse.pa_stream_connect_record)((ma_pa_stream*)pDevice->pulse.pStreamCapture, devCapture, &attr, streamFlags); - if (error != MA_PA_OK) { - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[PulseAudio] Failed to connect PulseAudio capture stream."); - result = ma_result_from_pulse(error); - goto on_error1; - } - - result = ma_wait_for_pa_stream_to_connect__pulse(pDevice->pContext, pDevice->pulse.pMainLoop, (ma_pa_stream*)pDevice->pulse.pStreamCapture); - if (result != MA_SUCCESS) { - goto on_error2; - } - - - /* Internal format. */ - pActualSS = ((ma_pa_stream_get_sample_spec_proc)pDevice->pContext->pulse.pa_stream_get_sample_spec)((ma_pa_stream*)pDevice->pulse.pStreamCapture); - if (pActualSS != NULL) { - ss = *pActualSS; - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, "[PulseAudio] Capture sample spec: format=%s, channels=%d, rate=%d\n", ma_get_format_name(ma_format_from_pulse(ss.format)), ss.channels, ss.rate); - } else { - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, "[PulseAudio] Failed to retrieve capture sample spec.\n"); - } - - pDescriptorCapture->format = ma_format_from_pulse(ss.format); - pDescriptorCapture->channels = ss.channels; - pDescriptorCapture->sampleRate = ss.rate; - - if (pDescriptorCapture->format == ma_format_unknown || pDescriptorCapture->channels == 0 || pDescriptorCapture->sampleRate == 0) { - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[PulseAudio] Capture sample spec is invalid. Device unusable by miniaudio. format=%s, channels=%d, sampleRate=%d.\n", ma_get_format_name(pDescriptorCapture->format), pDescriptorCapture->channels, pDescriptorCapture->sampleRate); - result = MA_ERROR; - goto on_error4; - } - - - /* Internal channel map. */ - pActualChannelMap = ((ma_pa_stream_get_channel_map_proc)pDevice->pContext->pulse.pa_stream_get_channel_map)((ma_pa_stream*)pDevice->pulse.pStreamCapture); - if (pActualChannelMap == NULL) { - pActualChannelMap = &cmap; /* Fallback just in case. */ - } - - /* - Bug in PipeWire. There have been reports that PipeWire is returning AUX channels when reporting - the channel map. To somewhat workaround this, I'm hacking in a hard coded channel map for mono - and stereo. In this case it should be safe to assume mono = MONO and stereo = LEFT/RIGHT. For - all other channel counts we need to just put up with whatever PipeWire reports and hope it gets - fixed sooner than later. I might remove this hack later. - */ - if (pDescriptorCapture->channels > 2) { - for (iChannel = 0; iChannel < pDescriptorCapture->channels; iChannel += 1) { - pDescriptorCapture->channelMap[iChannel] = ma_channel_position_from_pulse(pActualChannelMap->map[iChannel]); - } - } else { - /* Hack for mono and stereo. */ - if (pDescriptorCapture->channels == 1) { - pDescriptorCapture->channelMap[0] = MA_CHANNEL_MONO; - } else if (pDescriptorCapture->channels == 2) { - pDescriptorCapture->channelMap[0] = MA_CHANNEL_FRONT_LEFT; - pDescriptorCapture->channelMap[1] = MA_CHANNEL_FRONT_RIGHT; - } else { - MA_ASSERT(MA_FALSE); /* Should never hit this. */ - } - } - - - /* Buffer. */ - pActualAttr = ((ma_pa_stream_get_buffer_attr_proc)pDevice->pContext->pulse.pa_stream_get_buffer_attr)((ma_pa_stream*)pDevice->pulse.pStreamCapture); - if (pActualAttr != NULL) { - attr = *pActualAttr; - } - - if (attr.fragsize > 0) { - pDescriptorCapture->periodCount = ma_max(attr.maxlength / attr.fragsize, 1); - } else { - pDescriptorCapture->periodCount = 1; - } - - pDescriptorCapture->periodSizeInFrames = attr.maxlength / ma_get_bytes_per_frame(pDescriptorCapture->format, pDescriptorCapture->channels) / pDescriptorCapture->periodCount; - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, "[PulseAudio] Capture actual attr: maxlength=%d, tlength=%d, prebuf=%d, minreq=%d, fragsize=%d; periodSizeInFrames=%d\n", attr.maxlength, attr.tlength, attr.prebuf, attr.minreq, attr.fragsize, pDescriptorCapture->periodSizeInFrames); - } - - if (pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) { - result = ma_context_get_sink_info__pulse(pDevice->pContext, devPlayback, &sinkInfo); - if (result != MA_SUCCESS) { - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[PulseAudio] Failed to retrieve sink info for playback device.\n"); - goto on_error2; - } - - ss = sinkInfo.sample_spec; - cmap = sinkInfo.channel_map; - - /* Use the requested channel count if we have one. */ - if (pDescriptorPlayback->channels != 0) { - ss.channels = pDescriptorPlayback->channels; - } - - /* Use a default channel map. */ - ((ma_pa_channel_map_init_extend_proc)pDevice->pContext->pulse.pa_channel_map_init_extend)(&cmap, ss.channels, pConfig->pulse.channelMap); - - - /* Use the requested sample rate if one was specified. */ - if (pDescriptorPlayback->sampleRate != 0) { - ss.rate = pDescriptorPlayback->sampleRate; - } - - streamFlags = MA_PA_STREAM_START_CORKED | MA_PA_STREAM_ADJUST_LATENCY; - if (ma_format_from_pulse(ss.format) == ma_format_unknown) { - if (ma_is_little_endian()) { - ss.format = MA_PA_SAMPLE_FLOAT32LE; - } else { - ss.format = MA_PA_SAMPLE_FLOAT32BE; - } - streamFlags |= MA_PA_STREAM_FIX_FORMAT; - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, "[PulseAudio] sample_spec.format not supported by miniaudio. Defaulting to PA_SAMPLE_FLOAT32.\n"); - } - if (ss.rate == 0) { - ss.rate = MA_DEFAULT_SAMPLE_RATE; - streamFlags |= MA_PA_STREAM_FIX_RATE; - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, "[PulseAudio] sample_spec.rate = 0. Defaulting to %d.\n", ss.rate); - } - if (ss.channels == 0) { - ss.channels = MA_DEFAULT_CHANNELS; - streamFlags |= MA_PA_STREAM_FIX_CHANNELS; - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, "[PulseAudio] sample_spec.channels = 0. Defaulting to %d.\n", ss.channels); - } - - /* We now have enough information to calculate the actual buffer size in frames. */ - pDescriptorPlayback->periodSizeInFrames = ma_calculate_period_size_in_frames_from_descriptor__pulse(pDescriptorPlayback, ss.rate, pConfig->performanceProfile); - - attr = ma_device__pa_buffer_attr_new(pDescriptorPlayback->periodSizeInFrames, pDescriptorPlayback->periodCount, &ss); - - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, "[PulseAudio] Playback attr: maxlength=%d, tlength=%d, prebuf=%d, minreq=%d, fragsize=%d; periodSizeInFrames=%d\n", attr.maxlength, attr.tlength, attr.prebuf, attr.minreq, attr.fragsize, pDescriptorPlayback->periodSizeInFrames); - - pDevice->pulse.pStreamPlayback = ma_device__pa_stream_new__pulse(pDevice, pConfig->pulse.pStreamNamePlayback, &ss, &cmap); - if (pDevice->pulse.pStreamPlayback == NULL) { - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[PulseAudio] Failed to create PulseAudio playback stream.\n"); - result = MA_ERROR; - goto on_error2; - } - - - /* - Note that this callback will be fired as soon as the stream is connected, even though it's started as corked. The callback needs to handle a - device state of ma_device_state_uninitialized. - */ - ((ma_pa_stream_set_write_callback_proc)pDevice->pContext->pulse.pa_stream_set_write_callback)((ma_pa_stream*)pDevice->pulse.pStreamPlayback, ma_device_on_write__pulse, pDevice); - - /* State callback for checking when the device has been corked. */ - ((ma_pa_stream_set_suspended_callback_proc)pDevice->pContext->pulse.pa_stream_set_suspended_callback)((ma_pa_stream*)pDevice->pulse.pStreamPlayback, ma_device_on_suspended__pulse, pDevice); - - /* Rerouting notification. */ - ((ma_pa_stream_set_moved_callback_proc)pDevice->pContext->pulse.pa_stream_set_moved_callback)((ma_pa_stream*)pDevice->pulse.pStreamPlayback, ma_device_on_rerouted__pulse, pDevice); - - - /* Connect after we've got all of our internal state set up. */ - if (devPlayback != NULL) { - streamFlags |= MA_PA_STREAM_DONT_MOVE; - } - - error = ((ma_pa_stream_connect_playback_proc)pDevice->pContext->pulse.pa_stream_connect_playback)((ma_pa_stream*)pDevice->pulse.pStreamPlayback, devPlayback, &attr, streamFlags, NULL, NULL); - if (error != MA_PA_OK) { - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[PulseAudio] Failed to connect PulseAudio playback stream."); - result = ma_result_from_pulse(error); - goto on_error3; - } - - result = ma_wait_for_pa_stream_to_connect__pulse(pDevice->pContext, pDevice->pulse.pMainLoop, (ma_pa_stream*)pDevice->pulse.pStreamPlayback); - if (result != MA_SUCCESS) { - goto on_error3; - } - - - /* Internal format. */ - pActualSS = ((ma_pa_stream_get_sample_spec_proc)pDevice->pContext->pulse.pa_stream_get_sample_spec)((ma_pa_stream*)pDevice->pulse.pStreamPlayback); - if (pActualSS != NULL) { - ss = *pActualSS; - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, "[PulseAudio] Playback sample spec: format=%s, channels=%d, rate=%d\n", ma_get_format_name(ma_format_from_pulse(ss.format)), ss.channels, ss.rate); - } else { - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, "[PulseAudio] Failed to retrieve playback sample spec.\n"); - } - - pDescriptorPlayback->format = ma_format_from_pulse(ss.format); - pDescriptorPlayback->channels = ss.channels; - pDescriptorPlayback->sampleRate = ss.rate; - - if (pDescriptorPlayback->format == ma_format_unknown || pDescriptorPlayback->channels == 0 || pDescriptorPlayback->sampleRate == 0) { - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[PulseAudio] Playback sample spec is invalid. Device unusable by miniaudio. format=%s, channels=%d, sampleRate=%d.\n", ma_get_format_name(pDescriptorPlayback->format), pDescriptorPlayback->channels, pDescriptorPlayback->sampleRate); - result = MA_ERROR; - goto on_error4; - } - - - /* Internal channel map. */ - pActualChannelMap = ((ma_pa_stream_get_channel_map_proc)pDevice->pContext->pulse.pa_stream_get_channel_map)((ma_pa_stream*)pDevice->pulse.pStreamPlayback); - if (pActualChannelMap == NULL) { - pActualChannelMap = &cmap; /* Fallback just in case. */ - } - - /* - Bug in PipeWire. There have been reports that PipeWire is returning AUX channels when reporting - the channel map. To somewhat workaround this, I'm hacking in a hard coded channel map for mono - and stereo. In this case it should be safe to assume mono = MONO and stereo = LEFT/RIGHT. For - all other channel counts we need to just put up with whatever PipeWire reports and hope it gets - fixed sooner than later. I might remove this hack later. - */ - if (pDescriptorPlayback->channels > 2) { - for (iChannel = 0; iChannel < pDescriptorPlayback->channels; iChannel += 1) { - pDescriptorPlayback->channelMap[iChannel] = ma_channel_position_from_pulse(pActualChannelMap->map[iChannel]); - } - } else { - /* Hack for mono and stereo. */ - if (pDescriptorPlayback->channels == 1) { - pDescriptorPlayback->channelMap[0] = MA_CHANNEL_MONO; - } else if (pDescriptorPlayback->channels == 2) { - pDescriptorPlayback->channelMap[0] = MA_CHANNEL_FRONT_LEFT; - pDescriptorPlayback->channelMap[1] = MA_CHANNEL_FRONT_RIGHT; - } else { - MA_ASSERT(MA_FALSE); /* Should never hit this. */ - } - } - - - /* Buffer. */ - pActualAttr = ((ma_pa_stream_get_buffer_attr_proc)pDevice->pContext->pulse.pa_stream_get_buffer_attr)((ma_pa_stream*)pDevice->pulse.pStreamPlayback); - if (pActualAttr != NULL) { - attr = *pActualAttr; - } - - if (attr.tlength > 0) { - pDescriptorPlayback->periodCount = ma_max(attr.maxlength / attr.tlength, 1); - } else { - pDescriptorPlayback->periodCount = 1; - } - - pDescriptorPlayback->periodSizeInFrames = attr.maxlength / ma_get_bytes_per_frame(pDescriptorPlayback->format, pDescriptorPlayback->channels) / pDescriptorPlayback->periodCount; - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, "[PulseAudio] Playback actual attr: maxlength=%d, tlength=%d, prebuf=%d, minreq=%d, fragsize=%d; internalPeriodSizeInFrames=%d\n", attr.maxlength, attr.tlength, attr.prebuf, attr.minreq, attr.fragsize, pDescriptorPlayback->periodSizeInFrames); - } - - - /* - We need a ring buffer for handling duplex mode. We can use the main duplex ring buffer in the main - part of the ma_device struct. We cannot, however, depend on ma_device_init() initializing this for - us later on because that will only do it if it's a fully asynchronous backend - i.e. the - onDeviceDataLoop callback is NULL, which is not the case for PulseAudio. - */ - if (pConfig->deviceType == ma_device_type_duplex) { - ma_format rbFormat = (format != ma_format_unknown) ? format : pDescriptorCapture->format; - ma_uint32 rbChannels = (channels > 0) ? channels : pDescriptorCapture->channels; - ma_uint32 rbSampleRate = (sampleRate > 0) ? sampleRate : pDescriptorCapture->sampleRate; - - result = ma_duplex_rb_init(rbFormat, rbChannels, rbSampleRate, pDescriptorCapture->sampleRate, pDescriptorCapture->periodSizeInFrames, &pDevice->pContext->allocationCallbacks, &pDevice->duplexRB); - if (result != MA_SUCCESS) { - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[PulseAudio] Failed to initialize ring buffer. %s.\n", ma_result_description(result)); - goto on_error4; - } - } - - return MA_SUCCESS; - - -on_error4: - if (pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) { - ((ma_pa_stream_disconnect_proc)pDevice->pContext->pulse.pa_stream_disconnect)((ma_pa_stream*)pDevice->pulse.pStreamPlayback); - } -on_error3: - if (pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) { - ((ma_pa_stream_unref_proc)pDevice->pContext->pulse.pa_stream_unref)((ma_pa_stream*)pDevice->pulse.pStreamPlayback); - } -on_error2: - if (pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) { - ((ma_pa_stream_disconnect_proc)pDevice->pContext->pulse.pa_stream_disconnect)((ma_pa_stream*)pDevice->pulse.pStreamCapture); - } -on_error1: - if (pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) { - ((ma_pa_stream_unref_proc)pDevice->pContext->pulse.pa_stream_unref)((ma_pa_stream*)pDevice->pulse.pStreamCapture); - } -on_error0: - return result; -} - - -static void ma_pulse_operation_complete_callback(ma_pa_stream* pStream, int success, void* pUserData) -{ - ma_bool32* pIsSuccessful = (ma_bool32*)pUserData; - MA_ASSERT(pIsSuccessful != NULL); - - *pIsSuccessful = (ma_bool32)success; - - (void)pStream; /* Unused. */ -} - -static ma_result ma_device__cork_stream__pulse(ma_device* pDevice, ma_device_type deviceType, int cork) -{ - ma_context* pContext = pDevice->pContext; - ma_bool32 wasSuccessful; - ma_pa_stream* pStream; - ma_pa_operation* pOP; - ma_result result; - - /* This should not be called with a duplex device type. */ - if (deviceType == ma_device_type_duplex) { - return MA_INVALID_ARGS; - } - - wasSuccessful = MA_FALSE; - - pStream = (ma_pa_stream*)((deviceType == ma_device_type_capture) ? pDevice->pulse.pStreamCapture : pDevice->pulse.pStreamPlayback); - MA_ASSERT(pStream != NULL); - - pOP = ((ma_pa_stream_cork_proc)pContext->pulse.pa_stream_cork)(pStream, cork, ma_pulse_operation_complete_callback, &wasSuccessful); - if (pOP == NULL) { - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[PulseAudio] Failed to cork PulseAudio stream."); - return MA_ERROR; - } - - result = ma_wait_for_operation_and_unref__pulse(pDevice->pContext, pDevice->pulse.pMainLoop, pOP); - if (result != MA_SUCCESS) { - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[PulseAudio] An error occurred while waiting for the PulseAudio stream to cork."); - return result; - } - - if (!wasSuccessful) { - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[PulseAudio] Failed to %s PulseAudio stream.", (cork) ? "stop" : "start"); - return MA_ERROR; - } - - return MA_SUCCESS; -} - -static ma_result ma_device_start__pulse(ma_device* pDevice) -{ - ma_result result; - - MA_ASSERT(pDevice != NULL); - - if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) { - result = ma_device__cork_stream__pulse(pDevice, ma_device_type_capture, 0); - if (result != MA_SUCCESS) { - return result; - } - } - - if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) { - /* - We need to fill some data before uncorking. Not doing this will result in the write callback - never getting fired. We're not going to abort if writing fails because I still want the device - to get uncorked. - */ - ma_device_write_to_stream__pulse(pDevice, (ma_pa_stream*)(pDevice->pulse.pStreamPlayback), NULL); /* No need to check the result here. Always want to fall through an uncork.*/ - - result = ma_device__cork_stream__pulse(pDevice, ma_device_type_playback, 0); - if (result != MA_SUCCESS) { - return result; - } - } - - return MA_SUCCESS; -} - -static ma_result ma_device_stop__pulse(ma_device* pDevice) -{ - ma_result result; - - MA_ASSERT(pDevice != NULL); - - if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) { - result = ma_device__cork_stream__pulse(pDevice, ma_device_type_capture, 1); - if (result != MA_SUCCESS) { - return result; - } - } - - if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) { - /* - Ideally we would drain the device here, but there's been cases where PulseAudio seems to be - broken on some systems to the point where no audio processing seems to happen. When this - happens, draining never completes and we get stuck here. For now I'm disabling draining of - the device so we don't just freeze the application. - */ - #if 0 - ma_pa_operation* pOP = ((ma_pa_stream_drain_proc)pDevice->pContext->pulse.pa_stream_drain)((ma_pa_stream*)pDevice->pulse.pStreamPlayback, ma_pulse_operation_complete_callback, &wasSuccessful); - ma_wait_for_operation_and_unref__pulse(pDevice->pContext, pDevice->pulse.pMainLoop, pOP); - #endif - - result = ma_device__cork_stream__pulse(pDevice, ma_device_type_playback, 1); - if (result != MA_SUCCESS) { - return result; - } - } - - return MA_SUCCESS; -} - -static ma_result ma_device_data_loop__pulse(ma_device* pDevice) -{ - int resultPA; - - MA_ASSERT(pDevice != NULL); - - /* NOTE: Don't start the device here. It'll be done at a higher level. */ - - /* - All data is handled through callbacks. All we need to do is iterate over the main loop and let - the callbacks deal with it. - */ - while (ma_device_get_state(pDevice) == ma_device_state_started) { - resultPA = ((ma_pa_mainloop_iterate_proc)pDevice->pContext->pulse.pa_mainloop_iterate)((ma_pa_mainloop*)pDevice->pulse.pMainLoop, 1, NULL); - if (resultPA < 0) { - break; - } - } - - /* NOTE: Don't stop the device here. It'll be done at a higher level. */ - return MA_SUCCESS; -} - -static ma_result ma_device_data_loop_wakeup__pulse(ma_device* pDevice) -{ - MA_ASSERT(pDevice != NULL); - - ((ma_pa_mainloop_wakeup_proc)pDevice->pContext->pulse.pa_mainloop_wakeup)((ma_pa_mainloop*)pDevice->pulse.pMainLoop); - - return MA_SUCCESS; -} - -static ma_result ma_context_uninit__pulse(ma_context* pContext) -{ - MA_ASSERT(pContext != NULL); - MA_ASSERT(pContext->backend == ma_backend_pulseaudio); - - ((ma_pa_context_disconnect_proc)pContext->pulse.pa_context_disconnect)((ma_pa_context*)pContext->pulse.pPulseContext); - ((ma_pa_context_unref_proc)pContext->pulse.pa_context_unref)((ma_pa_context*)pContext->pulse.pPulseContext); - ((ma_pa_mainloop_free_proc)pContext->pulse.pa_mainloop_free)((ma_pa_mainloop*)pContext->pulse.pMainLoop); - - ma_free(pContext->pulse.pServerName, &pContext->allocationCallbacks); - ma_free(pContext->pulse.pApplicationName, &pContext->allocationCallbacks); - -#ifndef MA_NO_RUNTIME_LINKING - ma_dlclose(ma_context_get_log(pContext), pContext->pulse.pulseSO); -#endif - - return MA_SUCCESS; -} - -static ma_result ma_context_init__pulse(ma_context* pContext, const ma_context_config* pConfig, ma_backend_callbacks* pCallbacks) -{ - ma_result result; -#ifndef MA_NO_RUNTIME_LINKING - const char* libpulseNames[] = { - "libpulse.so", - "libpulse.so.0" - }; - size_t i; - - for (i = 0; i < ma_countof(libpulseNames); ++i) { - pContext->pulse.pulseSO = ma_dlopen(ma_context_get_log(pContext), libpulseNames[i]); - if (pContext->pulse.pulseSO != NULL) { - break; - } - } - - if (pContext->pulse.pulseSO == NULL) { - return MA_NO_BACKEND; - } - - pContext->pulse.pa_mainloop_new = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_mainloop_new"); - pContext->pulse.pa_mainloop_free = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_mainloop_free"); - pContext->pulse.pa_mainloop_quit = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_mainloop_quit"); - pContext->pulse.pa_mainloop_get_api = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_mainloop_get_api"); - pContext->pulse.pa_mainloop_iterate = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_mainloop_iterate"); - pContext->pulse.pa_mainloop_wakeup = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_mainloop_wakeup"); - pContext->pulse.pa_threaded_mainloop_new = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_threaded_mainloop_new"); - pContext->pulse.pa_threaded_mainloop_free = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_threaded_mainloop_free"); - pContext->pulse.pa_threaded_mainloop_start = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_threaded_mainloop_start"); - pContext->pulse.pa_threaded_mainloop_stop = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_threaded_mainloop_stop"); - pContext->pulse.pa_threaded_mainloop_lock = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_threaded_mainloop_lock"); - pContext->pulse.pa_threaded_mainloop_unlock = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_threaded_mainloop_unlock"); - pContext->pulse.pa_threaded_mainloop_wait = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_threaded_mainloop_wait"); - pContext->pulse.pa_threaded_mainloop_signal = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_threaded_mainloop_signal"); - pContext->pulse.pa_threaded_mainloop_accept = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_threaded_mainloop_accept"); - pContext->pulse.pa_threaded_mainloop_get_retval = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_threaded_mainloop_get_retval"); - pContext->pulse.pa_threaded_mainloop_get_api = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_threaded_mainloop_get_api"); - pContext->pulse.pa_threaded_mainloop_in_thread = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_threaded_mainloop_in_thread"); - pContext->pulse.pa_threaded_mainloop_set_name = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_threaded_mainloop_set_name"); - pContext->pulse.pa_context_new = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_context_new"); - pContext->pulse.pa_context_unref = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_context_unref"); - pContext->pulse.pa_context_connect = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_context_connect"); - pContext->pulse.pa_context_disconnect = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_context_disconnect"); - pContext->pulse.pa_context_set_state_callback = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_context_set_state_callback"); - pContext->pulse.pa_context_get_state = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_context_get_state"); - pContext->pulse.pa_context_get_sink_info_list = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_context_get_sink_info_list"); - pContext->pulse.pa_context_get_source_info_list = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_context_get_source_info_list"); - pContext->pulse.pa_context_get_sink_info_by_name = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_context_get_sink_info_by_name"); - pContext->pulse.pa_context_get_source_info_by_name = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_context_get_source_info_by_name"); - pContext->pulse.pa_operation_unref = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_operation_unref"); - pContext->pulse.pa_operation_get_state = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_operation_get_state"); - pContext->pulse.pa_channel_map_init_extend = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_channel_map_init_extend"); - pContext->pulse.pa_channel_map_valid = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_channel_map_valid"); - pContext->pulse.pa_channel_map_compatible = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_channel_map_compatible"); - pContext->pulse.pa_stream_new = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_stream_new"); - pContext->pulse.pa_stream_unref = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_stream_unref"); - pContext->pulse.pa_stream_connect_playback = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_stream_connect_playback"); - pContext->pulse.pa_stream_connect_record = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_stream_connect_record"); - pContext->pulse.pa_stream_disconnect = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_stream_disconnect"); - pContext->pulse.pa_stream_get_state = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_stream_get_state"); - pContext->pulse.pa_stream_get_sample_spec = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_stream_get_sample_spec"); - pContext->pulse.pa_stream_get_channel_map = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_stream_get_channel_map"); - pContext->pulse.pa_stream_get_buffer_attr = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_stream_get_buffer_attr"); - pContext->pulse.pa_stream_set_buffer_attr = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_stream_set_buffer_attr"); - pContext->pulse.pa_stream_get_device_name = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_stream_get_device_name"); - pContext->pulse.pa_stream_set_write_callback = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_stream_set_write_callback"); - pContext->pulse.pa_stream_set_read_callback = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_stream_set_read_callback"); - pContext->pulse.pa_stream_set_suspended_callback = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_stream_set_suspended_callback"); - pContext->pulse.pa_stream_set_moved_callback = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_stream_set_moved_callback"); - pContext->pulse.pa_stream_is_suspended = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_stream_is_suspended"); - pContext->pulse.pa_stream_flush = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_stream_flush"); - pContext->pulse.pa_stream_drain = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_stream_drain"); - pContext->pulse.pa_stream_is_corked = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_stream_is_corked"); - pContext->pulse.pa_stream_cork = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_stream_cork"); - pContext->pulse.pa_stream_trigger = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_stream_trigger"); - pContext->pulse.pa_stream_begin_write = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_stream_begin_write"); - pContext->pulse.pa_stream_write = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_stream_write"); - pContext->pulse.pa_stream_peek = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_stream_peek"); - pContext->pulse.pa_stream_drop = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_stream_drop"); - pContext->pulse.pa_stream_writable_size = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_stream_writable_size"); - pContext->pulse.pa_stream_readable_size = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_stream_readable_size"); -#else - /* This strange assignment system is just for type safety. */ - ma_pa_mainloop_new_proc _pa_mainloop_new = pa_mainloop_new; - ma_pa_mainloop_free_proc _pa_mainloop_free = pa_mainloop_free; - ma_pa_mainloop_quit_proc _pa_mainloop_quit = pa_mainloop_quit; - ma_pa_mainloop_get_api_proc _pa_mainloop_get_api = pa_mainloop_get_api; - ma_pa_mainloop_iterate_proc _pa_mainloop_iterate = pa_mainloop_iterate; - ma_pa_mainloop_wakeup_proc _pa_mainloop_wakeup = pa_mainloop_wakeup; - ma_pa_threaded_mainloop_new_proc _pa_threaded_mainloop_new = pa_threaded_mainloop_new; - ma_pa_threaded_mainloop_free_proc _pa_threaded_mainloop_free = pa_threaded_mainloop_free; - ma_pa_threaded_mainloop_start_proc _pa_threaded_mainloop_start = pa_threaded_mainloop_start; - ma_pa_threaded_mainloop_stop_proc _pa_threaded_mainloop_stop = pa_threaded_mainloop_stop; - ma_pa_threaded_mainloop_lock_proc _pa_threaded_mainloop_lock = pa_threaded_mainloop_lock; - ma_pa_threaded_mainloop_unlock_proc _pa_threaded_mainloop_unlock = pa_threaded_mainloop_unlock; - ma_pa_threaded_mainloop_wait_proc _pa_threaded_mainloop_wait = pa_threaded_mainloop_wait; - ma_pa_threaded_mainloop_signal_proc _pa_threaded_mainloop_signal = pa_threaded_mainloop_signal; - ma_pa_threaded_mainloop_accept_proc _pa_threaded_mainloop_accept = pa_threaded_mainloop_accept; - ma_pa_threaded_mainloop_get_retval_proc _pa_threaded_mainloop_get_retval = pa_threaded_mainloop_get_retval; - ma_pa_threaded_mainloop_get_api_proc _pa_threaded_mainloop_get_api = pa_threaded_mainloop_get_api; - ma_pa_threaded_mainloop_in_thread_proc _pa_threaded_mainloop_in_thread = pa_threaded_mainloop_in_thread; - ma_pa_threaded_mainloop_set_name_proc _pa_threaded_mainloop_set_name = pa_threaded_mainloop_set_name; - ma_pa_context_new_proc _pa_context_new = pa_context_new; - ma_pa_context_unref_proc _pa_context_unref = pa_context_unref; - ma_pa_context_connect_proc _pa_context_connect = pa_context_connect; - ma_pa_context_disconnect_proc _pa_context_disconnect = pa_context_disconnect; - ma_pa_context_set_state_callback_proc _pa_context_set_state_callback = pa_context_set_state_callback; - ma_pa_context_get_state_proc _pa_context_get_state = pa_context_get_state; - ma_pa_context_get_sink_info_list_proc _pa_context_get_sink_info_list = pa_context_get_sink_info_list; - ma_pa_context_get_source_info_list_proc _pa_context_get_source_info_list = pa_context_get_source_info_list; - ma_pa_context_get_sink_info_by_name_proc _pa_context_get_sink_info_by_name = pa_context_get_sink_info_by_name; - ma_pa_context_get_source_info_by_name_proc _pa_context_get_source_info_by_name= pa_context_get_source_info_by_name; - ma_pa_operation_unref_proc _pa_operation_unref = pa_operation_unref; - ma_pa_operation_get_state_proc _pa_operation_get_state = pa_operation_get_state; - ma_pa_channel_map_init_extend_proc _pa_channel_map_init_extend = pa_channel_map_init_extend; - ma_pa_channel_map_valid_proc _pa_channel_map_valid = pa_channel_map_valid; - ma_pa_channel_map_compatible_proc _pa_channel_map_compatible = pa_channel_map_compatible; - ma_pa_stream_new_proc _pa_stream_new = pa_stream_new; - ma_pa_stream_unref_proc _pa_stream_unref = pa_stream_unref; - ma_pa_stream_connect_playback_proc _pa_stream_connect_playback = pa_stream_connect_playback; - ma_pa_stream_connect_record_proc _pa_stream_connect_record = pa_stream_connect_record; - ma_pa_stream_disconnect_proc _pa_stream_disconnect = pa_stream_disconnect; - ma_pa_stream_get_state_proc _pa_stream_get_state = pa_stream_get_state; - ma_pa_stream_get_sample_spec_proc _pa_stream_get_sample_spec = pa_stream_get_sample_spec; - ma_pa_stream_get_channel_map_proc _pa_stream_get_channel_map = pa_stream_get_channel_map; - ma_pa_stream_get_buffer_attr_proc _pa_stream_get_buffer_attr = pa_stream_get_buffer_attr; - ma_pa_stream_set_buffer_attr_proc _pa_stream_set_buffer_attr = pa_stream_set_buffer_attr; - ma_pa_stream_get_device_name_proc _pa_stream_get_device_name = pa_stream_get_device_name; - ma_pa_stream_set_write_callback_proc _pa_stream_set_write_callback = pa_stream_set_write_callback; - ma_pa_stream_set_read_callback_proc _pa_stream_set_read_callback = pa_stream_set_read_callback; - ma_pa_stream_set_suspended_callback_proc _pa_stream_set_suspended_callback = pa_stream_set_suspended_callback; - ma_pa_stream_set_moved_callback_proc _pa_stream_set_moved_callback = pa_stream_set_moved_callback; - ma_pa_stream_is_suspended_proc _pa_stream_is_suspended = pa_stream_is_suspended; - ma_pa_stream_flush_proc _pa_stream_flush = pa_stream_flush; - ma_pa_stream_drain_proc _pa_stream_drain = pa_stream_drain; - ma_pa_stream_is_corked_proc _pa_stream_is_corked = pa_stream_is_corked; - ma_pa_stream_cork_proc _pa_stream_cork = pa_stream_cork; - ma_pa_stream_trigger_proc _pa_stream_trigger = pa_stream_trigger; - ma_pa_stream_begin_write_proc _pa_stream_begin_write = pa_stream_begin_write; - ma_pa_stream_write_proc _pa_stream_write = pa_stream_write; - ma_pa_stream_peek_proc _pa_stream_peek = pa_stream_peek; - ma_pa_stream_drop_proc _pa_stream_drop = pa_stream_drop; - ma_pa_stream_writable_size_proc _pa_stream_writable_size = pa_stream_writable_size; - ma_pa_stream_readable_size_proc _pa_stream_readable_size = pa_stream_readable_size; - - pContext->pulse.pa_mainloop_new = (ma_proc)_pa_mainloop_new; - pContext->pulse.pa_mainloop_free = (ma_proc)_pa_mainloop_free; - pContext->pulse.pa_mainloop_quit = (ma_proc)_pa_mainloop_quit; - pContext->pulse.pa_mainloop_get_api = (ma_proc)_pa_mainloop_get_api; - pContext->pulse.pa_mainloop_iterate = (ma_proc)_pa_mainloop_iterate; - pContext->pulse.pa_mainloop_wakeup = (ma_proc)_pa_mainloop_wakeup; - pContext->pulse.pa_threaded_mainloop_new = (ma_proc)_pa_threaded_mainloop_new; - pContext->pulse.pa_threaded_mainloop_free = (ma_proc)_pa_threaded_mainloop_free; - pContext->pulse.pa_threaded_mainloop_start = (ma_proc)_pa_threaded_mainloop_start; - pContext->pulse.pa_threaded_mainloop_stop = (ma_proc)_pa_threaded_mainloop_stop; - pContext->pulse.pa_threaded_mainloop_lock = (ma_proc)_pa_threaded_mainloop_lock; - pContext->pulse.pa_threaded_mainloop_unlock = (ma_proc)_pa_threaded_mainloop_unlock; - pContext->pulse.pa_threaded_mainloop_wait = (ma_proc)_pa_threaded_mainloop_wait; - pContext->pulse.pa_threaded_mainloop_signal = (ma_proc)_pa_threaded_mainloop_signal; - pContext->pulse.pa_threaded_mainloop_accept = (ma_proc)_pa_threaded_mainloop_accept; - pContext->pulse.pa_threaded_mainloop_get_retval = (ma_proc)_pa_threaded_mainloop_get_retval; - pContext->pulse.pa_threaded_mainloop_get_api = (ma_proc)_pa_threaded_mainloop_get_api; - pContext->pulse.pa_threaded_mainloop_in_thread = (ma_proc)_pa_threaded_mainloop_in_thread; - pContext->pulse.pa_threaded_mainloop_set_name = (ma_proc)_pa_threaded_mainloop_set_name; - pContext->pulse.pa_context_new = (ma_proc)_pa_context_new; - pContext->pulse.pa_context_unref = (ma_proc)_pa_context_unref; - pContext->pulse.pa_context_connect = (ma_proc)_pa_context_connect; - pContext->pulse.pa_context_disconnect = (ma_proc)_pa_context_disconnect; - pContext->pulse.pa_context_set_state_callback = (ma_proc)_pa_context_set_state_callback; - pContext->pulse.pa_context_get_state = (ma_proc)_pa_context_get_state; - pContext->pulse.pa_context_get_sink_info_list = (ma_proc)_pa_context_get_sink_info_list; - pContext->pulse.pa_context_get_source_info_list = (ma_proc)_pa_context_get_source_info_list; - pContext->pulse.pa_context_get_sink_info_by_name = (ma_proc)_pa_context_get_sink_info_by_name; - pContext->pulse.pa_context_get_source_info_by_name = (ma_proc)_pa_context_get_source_info_by_name; - pContext->pulse.pa_operation_unref = (ma_proc)_pa_operation_unref; - pContext->pulse.pa_operation_get_state = (ma_proc)_pa_operation_get_state; - pContext->pulse.pa_channel_map_init_extend = (ma_proc)_pa_channel_map_init_extend; - pContext->pulse.pa_channel_map_valid = (ma_proc)_pa_channel_map_valid; - pContext->pulse.pa_channel_map_compatible = (ma_proc)_pa_channel_map_compatible; - pContext->pulse.pa_stream_new = (ma_proc)_pa_stream_new; - pContext->pulse.pa_stream_unref = (ma_proc)_pa_stream_unref; - pContext->pulse.pa_stream_connect_playback = (ma_proc)_pa_stream_connect_playback; - pContext->pulse.pa_stream_connect_record = (ma_proc)_pa_stream_connect_record; - pContext->pulse.pa_stream_disconnect = (ma_proc)_pa_stream_disconnect; - pContext->pulse.pa_stream_get_state = (ma_proc)_pa_stream_get_state; - pContext->pulse.pa_stream_get_sample_spec = (ma_proc)_pa_stream_get_sample_spec; - pContext->pulse.pa_stream_get_channel_map = (ma_proc)_pa_stream_get_channel_map; - pContext->pulse.pa_stream_get_buffer_attr = (ma_proc)_pa_stream_get_buffer_attr; - pContext->pulse.pa_stream_set_buffer_attr = (ma_proc)_pa_stream_set_buffer_attr; - pContext->pulse.pa_stream_get_device_name = (ma_proc)_pa_stream_get_device_name; - pContext->pulse.pa_stream_set_write_callback = (ma_proc)_pa_stream_set_write_callback; - pContext->pulse.pa_stream_set_read_callback = (ma_proc)_pa_stream_set_read_callback; - pContext->pulse.pa_stream_set_suspended_callback = (ma_proc)_pa_stream_set_suspended_callback; - pContext->pulse.pa_stream_set_moved_callback = (ma_proc)_pa_stream_set_moved_callback; - pContext->pulse.pa_stream_is_suspended = (ma_proc)_pa_stream_is_suspended; - pContext->pulse.pa_stream_flush = (ma_proc)_pa_stream_flush; - pContext->pulse.pa_stream_drain = (ma_proc)_pa_stream_drain; - pContext->pulse.pa_stream_is_corked = (ma_proc)_pa_stream_is_corked; - pContext->pulse.pa_stream_cork = (ma_proc)_pa_stream_cork; - pContext->pulse.pa_stream_trigger = (ma_proc)_pa_stream_trigger; - pContext->pulse.pa_stream_begin_write = (ma_proc)_pa_stream_begin_write; - pContext->pulse.pa_stream_write = (ma_proc)_pa_stream_write; - pContext->pulse.pa_stream_peek = (ma_proc)_pa_stream_peek; - pContext->pulse.pa_stream_drop = (ma_proc)_pa_stream_drop; - pContext->pulse.pa_stream_writable_size = (ma_proc)_pa_stream_writable_size; - pContext->pulse.pa_stream_readable_size = (ma_proc)_pa_stream_readable_size; -#endif - - /* We need to make a copy of the application and server names so we can pass them to the pa_context of each device. */ - pContext->pulse.pApplicationName = ma_copy_string(pConfig->pulse.pApplicationName, &pContext->allocationCallbacks); - if (pContext->pulse.pApplicationName == NULL && pConfig->pulse.pApplicationName != NULL) { - return MA_OUT_OF_MEMORY; - } - - pContext->pulse.pServerName = ma_copy_string(pConfig->pulse.pServerName, &pContext->allocationCallbacks); - if (pContext->pulse.pServerName == NULL && pConfig->pulse.pServerName != NULL) { - ma_free(pContext->pulse.pApplicationName, &pContext->allocationCallbacks); - return MA_OUT_OF_MEMORY; - } - - result = ma_init_pa_mainloop_and_pa_context__pulse(pContext, pConfig->pulse.pApplicationName, pConfig->pulse.pServerName, pConfig->pulse.tryAutoSpawn, &pContext->pulse.pMainLoop, &pContext->pulse.pPulseContext); - if (result != MA_SUCCESS) { - ma_free(pContext->pulse.pServerName, &pContext->allocationCallbacks); - ma_free(pContext->pulse.pApplicationName, &pContext->allocationCallbacks); - #ifndef MA_NO_RUNTIME_LINKING - ma_dlclose(ma_context_get_log(pContext), pContext->pulse.pulseSO); - #endif - return result; - } - - /* With pa_mainloop we run a synchronous backend, but we implement our own main loop. */ - pCallbacks->onContextInit = ma_context_init__pulse; - pCallbacks->onContextUninit = ma_context_uninit__pulse; - pCallbacks->onContextEnumerateDevices = ma_context_enumerate_devices__pulse; - pCallbacks->onContextGetDeviceInfo = ma_context_get_device_info__pulse; - pCallbacks->onDeviceInit = ma_device_init__pulse; - pCallbacks->onDeviceUninit = ma_device_uninit__pulse; - pCallbacks->onDeviceStart = ma_device_start__pulse; - pCallbacks->onDeviceStop = ma_device_stop__pulse; - pCallbacks->onDeviceRead = NULL; /* Not used because we're implementing onDeviceDataLoop. */ - pCallbacks->onDeviceWrite = NULL; /* Not used because we're implementing onDeviceDataLoop. */ - pCallbacks->onDeviceDataLoop = ma_device_data_loop__pulse; - pCallbacks->onDeviceDataLoopWakeup = ma_device_data_loop_wakeup__pulse; - - return MA_SUCCESS; -} -#endif - - -/****************************************************************************** - -JACK Backend - -******************************************************************************/ -#ifdef MA_HAS_JACK - -/* It is assumed jack.h is available when compile-time linking is being used. */ -#ifdef MA_NO_RUNTIME_LINKING -#include - -typedef jack_nframes_t ma_jack_nframes_t; -typedef jack_options_t ma_jack_options_t; -typedef jack_status_t ma_jack_status_t; -typedef jack_client_t ma_jack_client_t; -typedef jack_port_t ma_jack_port_t; -typedef JackProcessCallback ma_JackProcessCallback; -typedef JackBufferSizeCallback ma_JackBufferSizeCallback; -typedef JackShutdownCallback ma_JackShutdownCallback; -#define MA_JACK_DEFAULT_AUDIO_TYPE JACK_DEFAULT_AUDIO_TYPE -#define ma_JackNoStartServer JackNoStartServer -#define ma_JackPortIsInput JackPortIsInput -#define ma_JackPortIsOutput JackPortIsOutput -#define ma_JackPortIsPhysical JackPortIsPhysical -#else -typedef ma_uint32 ma_jack_nframes_t; -typedef int ma_jack_options_t; -typedef int ma_jack_status_t; -typedef struct ma_jack_client_t ma_jack_client_t; -typedef struct ma_jack_port_t ma_jack_port_t; -typedef int (* ma_JackProcessCallback) (ma_jack_nframes_t nframes, void* arg); -typedef int (* ma_JackBufferSizeCallback)(ma_jack_nframes_t nframes, void* arg); -typedef void (* ma_JackShutdownCallback) (void* arg); -#define MA_JACK_DEFAULT_AUDIO_TYPE "32 bit float mono audio" -#define ma_JackNoStartServer 1 -#define ma_JackPortIsInput 1 -#define ma_JackPortIsOutput 2 -#define ma_JackPortIsPhysical 4 -#endif - -typedef ma_jack_client_t* (* ma_jack_client_open_proc) (const char* client_name, ma_jack_options_t options, ma_jack_status_t* status, ...); -typedef int (* ma_jack_client_close_proc) (ma_jack_client_t* client); -typedef int (* ma_jack_client_name_size_proc) (void); -typedef int (* ma_jack_set_process_callback_proc) (ma_jack_client_t* client, ma_JackProcessCallback process_callback, void* arg); -typedef int (* ma_jack_set_buffer_size_callback_proc)(ma_jack_client_t* client, ma_JackBufferSizeCallback bufsize_callback, void* arg); -typedef void (* ma_jack_on_shutdown_proc) (ma_jack_client_t* client, ma_JackShutdownCallback function, void* arg); -typedef ma_jack_nframes_t (* ma_jack_get_sample_rate_proc) (ma_jack_client_t* client); -typedef ma_jack_nframes_t (* ma_jack_get_buffer_size_proc) (ma_jack_client_t* client); -typedef const char** (* ma_jack_get_ports_proc) (ma_jack_client_t* client, const char* port_name_pattern, const char* type_name_pattern, unsigned long flags); -typedef int (* ma_jack_activate_proc) (ma_jack_client_t* client); -typedef int (* ma_jack_deactivate_proc) (ma_jack_client_t* client); -typedef int (* ma_jack_connect_proc) (ma_jack_client_t* client, const char* source_port, const char* destination_port); -typedef ma_jack_port_t* (* ma_jack_port_register_proc) (ma_jack_client_t* client, const char* port_name, const char* port_type, unsigned long flags, unsigned long buffer_size); -typedef const char* (* ma_jack_port_name_proc) (const ma_jack_port_t* port); -typedef void* (* ma_jack_port_get_buffer_proc) (ma_jack_port_t* port, ma_jack_nframes_t nframes); -typedef void (* ma_jack_free_proc) (void* ptr); - -static ma_result ma_context_open_client__jack(ma_context* pContext, ma_jack_client_t** ppClient) -{ - size_t maxClientNameSize; - char clientName[256]; - ma_jack_status_t status; - ma_jack_client_t* pClient; - - MA_ASSERT(pContext != NULL); - MA_ASSERT(ppClient != NULL); - - if (ppClient) { - *ppClient = NULL; - } - - maxClientNameSize = ((ma_jack_client_name_size_proc)pContext->jack.jack_client_name_size)(); /* Includes null terminator. */ - ma_strncpy_s(clientName, ma_min(sizeof(clientName), maxClientNameSize), (pContext->jack.pClientName != NULL) ? pContext->jack.pClientName : "miniaudio", (size_t)-1); - - pClient = ((ma_jack_client_open_proc)pContext->jack.jack_client_open)(clientName, (pContext->jack.tryStartServer) ? 0 : ma_JackNoStartServer, &status, NULL); - if (pClient == NULL) { - return MA_FAILED_TO_OPEN_BACKEND_DEVICE; - } - - if (ppClient) { - *ppClient = pClient; - } - - return MA_SUCCESS; -} - - -static ma_result ma_context_enumerate_devices__jack(ma_context* pContext, ma_enum_devices_callback_proc callback, void* pUserData) -{ - ma_bool32 cbResult = MA_TRUE; - - MA_ASSERT(pContext != NULL); - MA_ASSERT(callback != NULL); - - /* Playback. */ - if (cbResult) { - ma_device_info deviceInfo; - MA_ZERO_OBJECT(&deviceInfo); - ma_strncpy_s(deviceInfo.name, sizeof(deviceInfo.name), MA_DEFAULT_PLAYBACK_DEVICE_NAME, (size_t)-1); - deviceInfo.isDefault = MA_TRUE; /* JACK only uses default devices. */ - cbResult = callback(pContext, ma_device_type_playback, &deviceInfo, pUserData); - } - - /* Capture. */ - if (cbResult) { - ma_device_info deviceInfo; - MA_ZERO_OBJECT(&deviceInfo); - ma_strncpy_s(deviceInfo.name, sizeof(deviceInfo.name), MA_DEFAULT_CAPTURE_DEVICE_NAME, (size_t)-1); - deviceInfo.isDefault = MA_TRUE; /* JACK only uses default devices. */ - cbResult = callback(pContext, ma_device_type_capture, &deviceInfo, pUserData); - } - - (void)cbResult; /* For silencing a static analysis warning. */ - - return MA_SUCCESS; -} - -static ma_result ma_context_get_device_info__jack(ma_context* pContext, ma_device_type deviceType, const ma_device_id* pDeviceID, ma_device_info* pDeviceInfo) -{ - ma_jack_client_t* pClient; - ma_result result; - const char** ppPorts; - - MA_ASSERT(pContext != NULL); - - if (pDeviceID != NULL && pDeviceID->jack != 0) { - return MA_NO_DEVICE; /* Don't know the device. */ - } - - /* Name / Description */ - if (deviceType == ma_device_type_playback) { - ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), MA_DEFAULT_PLAYBACK_DEVICE_NAME, (size_t)-1); - } else { - ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), MA_DEFAULT_CAPTURE_DEVICE_NAME, (size_t)-1); - } - - /* Jack only uses default devices. */ - pDeviceInfo->isDefault = MA_TRUE; - - /* Jack only supports f32 and has a specific channel count and sample rate. */ - pDeviceInfo->nativeDataFormats[0].format = ma_format_f32; - - /* The channel count and sample rate can only be determined by opening the device. */ - result = ma_context_open_client__jack(pContext, &pClient); - if (result != MA_SUCCESS) { - ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[JACK] Failed to open client."); - return result; - } - - pDeviceInfo->nativeDataFormats[0].sampleRate = ((ma_jack_get_sample_rate_proc)pContext->jack.jack_get_sample_rate)((ma_jack_client_t*)pClient); - pDeviceInfo->nativeDataFormats[0].channels = 0; - - ppPorts = ((ma_jack_get_ports_proc)pContext->jack.jack_get_ports)((ma_jack_client_t*)pClient, NULL, MA_JACK_DEFAULT_AUDIO_TYPE, ma_JackPortIsPhysical | ((deviceType == ma_device_type_playback) ? ma_JackPortIsInput : ma_JackPortIsOutput)); - if (ppPorts == NULL) { - ((ma_jack_client_close_proc)pContext->jack.jack_client_close)((ma_jack_client_t*)pClient); - ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[JACK] Failed to query physical ports."); - return MA_FAILED_TO_OPEN_BACKEND_DEVICE; - } - - while (ppPorts[pDeviceInfo->nativeDataFormats[0].channels] != NULL) { - pDeviceInfo->nativeDataFormats[0].channels += 1; - } - - pDeviceInfo->nativeDataFormats[0].flags = 0; - pDeviceInfo->nativeDataFormatCount = 1; - - ((ma_jack_free_proc)pContext->jack.jack_free)((void*)ppPorts); - ((ma_jack_client_close_proc)pContext->jack.jack_client_close)((ma_jack_client_t*)pClient); - - (void)pContext; - return MA_SUCCESS; -} - - -static ma_result ma_device_uninit__jack(ma_device* pDevice) -{ - ma_context* pContext; - - MA_ASSERT(pDevice != NULL); - - pContext = pDevice->pContext; - MA_ASSERT(pContext != NULL); - - if (pDevice->jack.pClient != NULL) { - ((ma_jack_client_close_proc)pContext->jack.jack_client_close)((ma_jack_client_t*)pDevice->jack.pClient); - } - - if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) { - ma_free(pDevice->jack.pIntermediaryBufferCapture, &pDevice->pContext->allocationCallbacks); - ma_free(pDevice->jack.ppPortsCapture, &pDevice->pContext->allocationCallbacks); - } - - if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) { - ma_free(pDevice->jack.pIntermediaryBufferPlayback, &pDevice->pContext->allocationCallbacks); - ma_free(pDevice->jack.ppPortsPlayback, &pDevice->pContext->allocationCallbacks); - } - - return MA_SUCCESS; -} - -static void ma_device__jack_shutdown_callback(void* pUserData) -{ - /* JACK died. Stop the device. */ - ma_device* pDevice = (ma_device*)pUserData; - MA_ASSERT(pDevice != NULL); - - ma_device_stop(pDevice); -} - -static int ma_device__jack_buffer_size_callback(ma_jack_nframes_t frameCount, void* pUserData) -{ - ma_device* pDevice = (ma_device*)pUserData; - MA_ASSERT(pDevice != NULL); - - if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) { - size_t newBufferSize = frameCount * (pDevice->capture.internalChannels * ma_get_bytes_per_sample(pDevice->capture.internalFormat)); - float* pNewBuffer = (float*)ma_calloc(newBufferSize, &pDevice->pContext->allocationCallbacks); - if (pNewBuffer == NULL) { - return MA_OUT_OF_MEMORY; - } - - ma_free(pDevice->jack.pIntermediaryBufferCapture, &pDevice->pContext->allocationCallbacks); - - pDevice->jack.pIntermediaryBufferCapture = pNewBuffer; - pDevice->playback.internalPeriodSizeInFrames = frameCount; - } - - if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) { - size_t newBufferSize = frameCount * (pDevice->playback.internalChannels * ma_get_bytes_per_sample(pDevice->playback.internalFormat)); - float* pNewBuffer = (float*)ma_calloc(newBufferSize, &pDevice->pContext->allocationCallbacks); - if (pNewBuffer == NULL) { - return MA_OUT_OF_MEMORY; - } - - ma_free(pDevice->jack.pIntermediaryBufferPlayback, &pDevice->pContext->allocationCallbacks); - - pDevice->jack.pIntermediaryBufferPlayback = pNewBuffer; - pDevice->playback.internalPeriodSizeInFrames = frameCount; - } - - return 0; -} - -static int ma_device__jack_process_callback(ma_jack_nframes_t frameCount, void* pUserData) -{ - ma_device* pDevice; - ma_context* pContext; - ma_uint32 iChannel; - - pDevice = (ma_device*)pUserData; - MA_ASSERT(pDevice != NULL); - - pContext = pDevice->pContext; - MA_ASSERT(pContext != NULL); - - if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) { - /* Channels need to be interleaved. */ - for (iChannel = 0; iChannel < pDevice->capture.internalChannels; ++iChannel) { - const float* pSrc = (const float*)((ma_jack_port_get_buffer_proc)pContext->jack.jack_port_get_buffer)((ma_jack_port_t*)pDevice->jack.ppPortsCapture[iChannel], frameCount); - if (pSrc != NULL) { - float* pDst = pDevice->jack.pIntermediaryBufferCapture + iChannel; - ma_jack_nframes_t iFrame; - for (iFrame = 0; iFrame < frameCount; ++iFrame) { - *pDst = *pSrc; - - pDst += pDevice->capture.internalChannels; - pSrc += 1; - } - } - } - - ma_device_handle_backend_data_callback(pDevice, NULL, pDevice->jack.pIntermediaryBufferCapture, frameCount); - } - - if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) { - ma_device_handle_backend_data_callback(pDevice, pDevice->jack.pIntermediaryBufferPlayback, NULL, frameCount); - - /* Channels need to be deinterleaved. */ - for (iChannel = 0; iChannel < pDevice->playback.internalChannels; ++iChannel) { - float* pDst = (float*)((ma_jack_port_get_buffer_proc)pContext->jack.jack_port_get_buffer)((ma_jack_port_t*)pDevice->jack.ppPortsPlayback[iChannel], frameCount); - if (pDst != NULL) { - const float* pSrc = pDevice->jack.pIntermediaryBufferPlayback + iChannel; - ma_jack_nframes_t iFrame; - for (iFrame = 0; iFrame < frameCount; ++iFrame) { - *pDst = *pSrc; - - pDst += 1; - pSrc += pDevice->playback.internalChannels; - } - } - } - } - - return 0; -} - -static ma_result ma_device_init__jack(ma_device* pDevice, const ma_device_config* pConfig, ma_device_descriptor* pDescriptorPlayback, ma_device_descriptor* pDescriptorCapture) -{ - ma_result result; - ma_uint32 periodSizeInFrames; - - MA_ASSERT(pConfig != NULL); - MA_ASSERT(pDevice != NULL); - - if (pConfig->deviceType == ma_device_type_loopback) { - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[JACK] Loopback mode not supported."); - return MA_DEVICE_TYPE_NOT_SUPPORTED; - } - - /* Only supporting default devices with JACK. */ - if (((pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) && pDescriptorPlayback->pDeviceID != NULL && pDescriptorPlayback->pDeviceID->jack != 0) || - ((pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) && pDescriptorCapture->pDeviceID != NULL && pDescriptorCapture->pDeviceID->jack != 0)) { - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[JACK] Only default devices are supported."); - return MA_NO_DEVICE; - } - - /* No exclusive mode with the JACK backend. */ - if (((pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) && pDescriptorPlayback->shareMode == ma_share_mode_exclusive) || - ((pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) && pDescriptorCapture->shareMode == ma_share_mode_exclusive)) { - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[JACK] Exclusive mode not supported."); - return MA_SHARE_MODE_NOT_SUPPORTED; - } - - /* Open the client. */ - result = ma_context_open_client__jack(pDevice->pContext, (ma_jack_client_t**)&pDevice->jack.pClient); - if (result != MA_SUCCESS) { - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[JACK] Failed to open client."); - return result; - } - - /* Callbacks. */ - if (((ma_jack_set_process_callback_proc)pDevice->pContext->jack.jack_set_process_callback)((ma_jack_client_t*)pDevice->jack.pClient, ma_device__jack_process_callback, pDevice) != 0) { - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[JACK] Failed to set process callback."); - return MA_FAILED_TO_OPEN_BACKEND_DEVICE; - } - if (((ma_jack_set_buffer_size_callback_proc)pDevice->pContext->jack.jack_set_buffer_size_callback)((ma_jack_client_t*)pDevice->jack.pClient, ma_device__jack_buffer_size_callback, pDevice) != 0) { - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[JACK] Failed to set buffer size callback."); - return MA_FAILED_TO_OPEN_BACKEND_DEVICE; - } - - ((ma_jack_on_shutdown_proc)pDevice->pContext->jack.jack_on_shutdown)((ma_jack_client_t*)pDevice->jack.pClient, ma_device__jack_shutdown_callback, pDevice); - - - /* The buffer size in frames can change. */ - periodSizeInFrames = ((ma_jack_get_buffer_size_proc)pDevice->pContext->jack.jack_get_buffer_size)((ma_jack_client_t*)pDevice->jack.pClient); - - if (pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) { - ma_uint32 iPort; - const char** ppPorts; - - pDescriptorCapture->format = ma_format_f32; - pDescriptorCapture->channels = 0; - pDescriptorCapture->sampleRate = ((ma_jack_get_sample_rate_proc)pDevice->pContext->jack.jack_get_sample_rate)((ma_jack_client_t*)pDevice->jack.pClient); - ma_channel_map_init_standard(ma_standard_channel_map_alsa, pDescriptorCapture->channelMap, ma_countof(pDescriptorCapture->channelMap), pDescriptorCapture->channels); - - ppPorts = ((ma_jack_get_ports_proc)pDevice->pContext->jack.jack_get_ports)((ma_jack_client_t*)pDevice->jack.pClient, NULL, MA_JACK_DEFAULT_AUDIO_TYPE, ma_JackPortIsPhysical | ma_JackPortIsOutput); - if (ppPorts == NULL) { - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[JACK] Failed to query physical ports."); - return MA_FAILED_TO_OPEN_BACKEND_DEVICE; - } - - /* Need to count the number of ports first so we can allocate some memory. */ - while (ppPorts[pDescriptorCapture->channels] != NULL) { - pDescriptorCapture->channels += 1; - } - - pDevice->jack.ppPortsCapture = (ma_ptr*)ma_malloc(sizeof(*pDevice->jack.ppPortsCapture) * pDescriptorCapture->channels, &pDevice->pContext->allocationCallbacks); - if (pDevice->jack.ppPortsCapture == NULL) { - return MA_OUT_OF_MEMORY; - } - - for (iPort = 0; iPort < pDescriptorCapture->channels; iPort += 1) { - char name[64]; - ma_strcpy_s(name, sizeof(name), "capture"); - ma_itoa_s((int)iPort, name+7, sizeof(name)-7, 10); /* 7 = length of "capture" */ - - pDevice->jack.ppPortsCapture[iPort] = ((ma_jack_port_register_proc)pDevice->pContext->jack.jack_port_register)((ma_jack_client_t*)pDevice->jack.pClient, name, MA_JACK_DEFAULT_AUDIO_TYPE, ma_JackPortIsInput, 0); - if (pDevice->jack.ppPortsCapture[iPort] == NULL) { - ((ma_jack_free_proc)pDevice->pContext->jack.jack_free)((void*)ppPorts); - ma_device_uninit__jack(pDevice); - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[JACK] Failed to register ports."); - return MA_FAILED_TO_OPEN_BACKEND_DEVICE; - } - } - - ((ma_jack_free_proc)pDevice->pContext->jack.jack_free)((void*)ppPorts); - - pDescriptorCapture->periodSizeInFrames = periodSizeInFrames; - pDescriptorCapture->periodCount = 1; /* There's no notion of a period in JACK. Just set to 1. */ - - pDevice->jack.pIntermediaryBufferCapture = (float*)ma_calloc(pDescriptorCapture->periodSizeInFrames * ma_get_bytes_per_frame(pDescriptorCapture->format, pDescriptorCapture->channels), &pDevice->pContext->allocationCallbacks); - if (pDevice->jack.pIntermediaryBufferCapture == NULL) { - ma_device_uninit__jack(pDevice); - return MA_OUT_OF_MEMORY; - } - } - - if (pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) { - ma_uint32 iPort; - const char** ppPorts; - - pDescriptorPlayback->format = ma_format_f32; - pDescriptorPlayback->channels = 0; - pDescriptorPlayback->sampleRate = ((ma_jack_get_sample_rate_proc)pDevice->pContext->jack.jack_get_sample_rate)((ma_jack_client_t*)pDevice->jack.pClient); - ma_channel_map_init_standard(ma_standard_channel_map_alsa, pDescriptorPlayback->channelMap, ma_countof(pDescriptorPlayback->channelMap), pDescriptorPlayback->channels); - - ppPorts = ((ma_jack_get_ports_proc)pDevice->pContext->jack.jack_get_ports)((ma_jack_client_t*)pDevice->jack.pClient, NULL, MA_JACK_DEFAULT_AUDIO_TYPE, ma_JackPortIsPhysical | ma_JackPortIsInput); - if (ppPorts == NULL) { - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[JACK] Failed to query physical ports."); - return MA_FAILED_TO_OPEN_BACKEND_DEVICE; - } - - /* Need to count the number of ports first so we can allocate some memory. */ - while (ppPorts[pDescriptorPlayback->channels] != NULL) { - pDescriptorPlayback->channels += 1; - } - - pDevice->jack.ppPortsPlayback = (ma_ptr*)ma_malloc(sizeof(*pDevice->jack.ppPortsPlayback) * pDescriptorPlayback->channels, &pDevice->pContext->allocationCallbacks); - if (pDevice->jack.ppPortsPlayback == NULL) { - ma_free(pDevice->jack.ppPortsCapture, &pDevice->pContext->allocationCallbacks); - return MA_OUT_OF_MEMORY; - } - - for (iPort = 0; iPort < pDescriptorPlayback->channels; iPort += 1) { - char name[64]; - ma_strcpy_s(name, sizeof(name), "playback"); - ma_itoa_s((int)iPort, name+8, sizeof(name)-8, 10); /* 8 = length of "playback" */ - - pDevice->jack.ppPortsPlayback[iPort] = ((ma_jack_port_register_proc)pDevice->pContext->jack.jack_port_register)((ma_jack_client_t*)pDevice->jack.pClient, name, MA_JACK_DEFAULT_AUDIO_TYPE, ma_JackPortIsOutput, 0); - if (pDevice->jack.ppPortsPlayback[iPort] == NULL) { - ((ma_jack_free_proc)pDevice->pContext->jack.jack_free)((void*)ppPorts); - ma_device_uninit__jack(pDevice); - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[JACK] Failed to register ports."); - return MA_FAILED_TO_OPEN_BACKEND_DEVICE; - } - } - - ((ma_jack_free_proc)pDevice->pContext->jack.jack_free)((void*)ppPorts); - - pDescriptorPlayback->periodSizeInFrames = periodSizeInFrames; - pDescriptorPlayback->periodCount = 1; /* There's no notion of a period in JACK. Just set to 1. */ - - pDevice->jack.pIntermediaryBufferPlayback = (float*)ma_calloc(pDescriptorPlayback->periodSizeInFrames * ma_get_bytes_per_frame(pDescriptorPlayback->format, pDescriptorPlayback->channels), &pDevice->pContext->allocationCallbacks); - if (pDevice->jack.pIntermediaryBufferPlayback == NULL) { - ma_device_uninit__jack(pDevice); - return MA_OUT_OF_MEMORY; - } - } - - return MA_SUCCESS; -} - - -static ma_result ma_device_start__jack(ma_device* pDevice) -{ - ma_context* pContext = pDevice->pContext; - int resultJACK; - size_t i; - - resultJACK = ((ma_jack_activate_proc)pContext->jack.jack_activate)((ma_jack_client_t*)pDevice->jack.pClient); - if (resultJACK != 0) { - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[JACK] Failed to activate the JACK client."); - return MA_FAILED_TO_START_BACKEND_DEVICE; - } - - if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) { - const char** ppServerPorts = ((ma_jack_get_ports_proc)pContext->jack.jack_get_ports)((ma_jack_client_t*)pDevice->jack.pClient, NULL, MA_JACK_DEFAULT_AUDIO_TYPE, ma_JackPortIsPhysical | ma_JackPortIsOutput); - if (ppServerPorts == NULL) { - ((ma_jack_deactivate_proc)pContext->jack.jack_deactivate)((ma_jack_client_t*)pDevice->jack.pClient); - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[JACK] Failed to retrieve physical ports."); - return MA_ERROR; - } - - for (i = 0; ppServerPorts[i] != NULL; ++i) { - const char* pServerPort = ppServerPorts[i]; - const char* pClientPort = ((ma_jack_port_name_proc)pContext->jack.jack_port_name)((ma_jack_port_t*)pDevice->jack.ppPortsCapture[i]); - - resultJACK = ((ma_jack_connect_proc)pContext->jack.jack_connect)((ma_jack_client_t*)pDevice->jack.pClient, pServerPort, pClientPort); - if (resultJACK != 0) { - ((ma_jack_free_proc)pContext->jack.jack_free)((void*)ppServerPorts); - ((ma_jack_deactivate_proc)pContext->jack.jack_deactivate)((ma_jack_client_t*)pDevice->jack.pClient); - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[JACK] Failed to connect ports."); - return MA_ERROR; - } - } - - ((ma_jack_free_proc)pContext->jack.jack_free)((void*)ppServerPorts); - } - - if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) { - const char** ppServerPorts = ((ma_jack_get_ports_proc)pContext->jack.jack_get_ports)((ma_jack_client_t*)pDevice->jack.pClient, NULL, MA_JACK_DEFAULT_AUDIO_TYPE, ma_JackPortIsPhysical | ma_JackPortIsInput); - if (ppServerPorts == NULL) { - ((ma_jack_deactivate_proc)pContext->jack.jack_deactivate)((ma_jack_client_t*)pDevice->jack.pClient); - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[JACK] Failed to retrieve physical ports."); - return MA_ERROR; - } - - for (i = 0; ppServerPorts[i] != NULL; ++i) { - const char* pServerPort = ppServerPorts[i]; - const char* pClientPort = ((ma_jack_port_name_proc)pContext->jack.jack_port_name)((ma_jack_port_t*)pDevice->jack.ppPortsPlayback[i]); - - resultJACK = ((ma_jack_connect_proc)pContext->jack.jack_connect)((ma_jack_client_t*)pDevice->jack.pClient, pClientPort, pServerPort); - if (resultJACK != 0) { - ((ma_jack_free_proc)pContext->jack.jack_free)((void*)ppServerPorts); - ((ma_jack_deactivate_proc)pContext->jack.jack_deactivate)((ma_jack_client_t*)pDevice->jack.pClient); - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[JACK] Failed to connect ports."); - return MA_ERROR; - } - } - - ((ma_jack_free_proc)pContext->jack.jack_free)((void*)ppServerPorts); - } - - return MA_SUCCESS; -} - -static ma_result ma_device_stop__jack(ma_device* pDevice) -{ - ma_context* pContext = pDevice->pContext; - - if (((ma_jack_deactivate_proc)pContext->jack.jack_deactivate)((ma_jack_client_t*)pDevice->jack.pClient) != 0) { - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[JACK] An error occurred when deactivating the JACK client."); - return MA_ERROR; - } - - ma_device__on_notification_stopped(pDevice); - - return MA_SUCCESS; -} - - -static ma_result ma_context_uninit__jack(ma_context* pContext) -{ - MA_ASSERT(pContext != NULL); - MA_ASSERT(pContext->backend == ma_backend_jack); - - ma_free(pContext->jack.pClientName, &pContext->allocationCallbacks); - pContext->jack.pClientName = NULL; - -#ifndef MA_NO_RUNTIME_LINKING - ma_dlclose(ma_context_get_log(pContext), pContext->jack.jackSO); -#endif - - return MA_SUCCESS; -} - -static ma_result ma_context_init__jack(ma_context* pContext, const ma_context_config* pConfig, ma_backend_callbacks* pCallbacks) -{ -#ifndef MA_NO_RUNTIME_LINKING - const char* libjackNames[] = { -#if defined(MA_WIN32) - "libjack.dll", - "libjack64.dll" -#endif -#if defined(MA_UNIX) - "libjack.so", - "libjack.so.0" -#endif - }; - size_t i; - - for (i = 0; i < ma_countof(libjackNames); ++i) { - pContext->jack.jackSO = ma_dlopen(ma_context_get_log(pContext), libjackNames[i]); - if (pContext->jack.jackSO != NULL) { - break; - } - } - - if (pContext->jack.jackSO == NULL) { - return MA_NO_BACKEND; - } - - pContext->jack.jack_client_open = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->jack.jackSO, "jack_client_open"); - pContext->jack.jack_client_close = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->jack.jackSO, "jack_client_close"); - pContext->jack.jack_client_name_size = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->jack.jackSO, "jack_client_name_size"); - pContext->jack.jack_set_process_callback = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->jack.jackSO, "jack_set_process_callback"); - pContext->jack.jack_set_buffer_size_callback = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->jack.jackSO, "jack_set_buffer_size_callback"); - pContext->jack.jack_on_shutdown = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->jack.jackSO, "jack_on_shutdown"); - pContext->jack.jack_get_sample_rate = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->jack.jackSO, "jack_get_sample_rate"); - pContext->jack.jack_get_buffer_size = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->jack.jackSO, "jack_get_buffer_size"); - pContext->jack.jack_get_ports = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->jack.jackSO, "jack_get_ports"); - pContext->jack.jack_activate = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->jack.jackSO, "jack_activate"); - pContext->jack.jack_deactivate = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->jack.jackSO, "jack_deactivate"); - pContext->jack.jack_connect = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->jack.jackSO, "jack_connect"); - pContext->jack.jack_port_register = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->jack.jackSO, "jack_port_register"); - pContext->jack.jack_port_name = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->jack.jackSO, "jack_port_name"); - pContext->jack.jack_port_get_buffer = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->jack.jackSO, "jack_port_get_buffer"); - pContext->jack.jack_free = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->jack.jackSO, "jack_free"); -#else - /* - This strange assignment system is here just to ensure type safety of miniaudio's function pointer - types. If anything differs slightly the compiler should throw a warning. - */ - ma_jack_client_open_proc _jack_client_open = jack_client_open; - ma_jack_client_close_proc _jack_client_close = jack_client_close; - ma_jack_client_name_size_proc _jack_client_name_size = jack_client_name_size; - ma_jack_set_process_callback_proc _jack_set_process_callback = jack_set_process_callback; - ma_jack_set_buffer_size_callback_proc _jack_set_buffer_size_callback = jack_set_buffer_size_callback; - ma_jack_on_shutdown_proc _jack_on_shutdown = jack_on_shutdown; - ma_jack_get_sample_rate_proc _jack_get_sample_rate = jack_get_sample_rate; - ma_jack_get_buffer_size_proc _jack_get_buffer_size = jack_get_buffer_size; - ma_jack_get_ports_proc _jack_get_ports = jack_get_ports; - ma_jack_activate_proc _jack_activate = jack_activate; - ma_jack_deactivate_proc _jack_deactivate = jack_deactivate; - ma_jack_connect_proc _jack_connect = jack_connect; - ma_jack_port_register_proc _jack_port_register = jack_port_register; - ma_jack_port_name_proc _jack_port_name = jack_port_name; - ma_jack_port_get_buffer_proc _jack_port_get_buffer = jack_port_get_buffer; - ma_jack_free_proc _jack_free = jack_free; - - pContext->jack.jack_client_open = (ma_proc)_jack_client_open; - pContext->jack.jack_client_close = (ma_proc)_jack_client_close; - pContext->jack.jack_client_name_size = (ma_proc)_jack_client_name_size; - pContext->jack.jack_set_process_callback = (ma_proc)_jack_set_process_callback; - pContext->jack.jack_set_buffer_size_callback = (ma_proc)_jack_set_buffer_size_callback; - pContext->jack.jack_on_shutdown = (ma_proc)_jack_on_shutdown; - pContext->jack.jack_get_sample_rate = (ma_proc)_jack_get_sample_rate; - pContext->jack.jack_get_buffer_size = (ma_proc)_jack_get_buffer_size; - pContext->jack.jack_get_ports = (ma_proc)_jack_get_ports; - pContext->jack.jack_activate = (ma_proc)_jack_activate; - pContext->jack.jack_deactivate = (ma_proc)_jack_deactivate; - pContext->jack.jack_connect = (ma_proc)_jack_connect; - pContext->jack.jack_port_register = (ma_proc)_jack_port_register; - pContext->jack.jack_port_name = (ma_proc)_jack_port_name; - pContext->jack.jack_port_get_buffer = (ma_proc)_jack_port_get_buffer; - pContext->jack.jack_free = (ma_proc)_jack_free; -#endif - - if (pConfig->jack.pClientName != NULL) { - pContext->jack.pClientName = ma_copy_string(pConfig->jack.pClientName, &pContext->allocationCallbacks); - } - pContext->jack.tryStartServer = pConfig->jack.tryStartServer; - - /* - Getting here means the JACK library is installed, but it doesn't necessarily mean it's usable. We need to quickly test this by connecting - a temporary client. - */ - { - ma_jack_client_t* pDummyClient; - ma_result result = ma_context_open_client__jack(pContext, &pDummyClient); - if (result != MA_SUCCESS) { - ma_free(pContext->jack.pClientName, &pContext->allocationCallbacks); - #ifndef MA_NO_RUNTIME_LINKING - ma_dlclose(ma_context_get_log(pContext), pContext->jack.jackSO); - #endif - return MA_NO_BACKEND; - } - - ((ma_jack_client_close_proc)pContext->jack.jack_client_close)((ma_jack_client_t*)pDummyClient); - } - - - pCallbacks->onContextInit = ma_context_init__jack; - pCallbacks->onContextUninit = ma_context_uninit__jack; - pCallbacks->onContextEnumerateDevices = ma_context_enumerate_devices__jack; - pCallbacks->onContextGetDeviceInfo = ma_context_get_device_info__jack; - pCallbacks->onDeviceInit = ma_device_init__jack; - pCallbacks->onDeviceUninit = ma_device_uninit__jack; - pCallbacks->onDeviceStart = ma_device_start__jack; - pCallbacks->onDeviceStop = ma_device_stop__jack; - pCallbacks->onDeviceRead = NULL; /* Not used because JACK is asynchronous. */ - pCallbacks->onDeviceWrite = NULL; /* Not used because JACK is asynchronous. */ - pCallbacks->onDeviceDataLoop = NULL; /* Not used because JACK is asynchronous. */ - - return MA_SUCCESS; -} -#endif /* MA_HAS_JACK */ - - - -/****************************************************************************** - -Core Audio Backend - -References -========== -- Technical Note TN2091: Device input using the HAL Output Audio Unit - https://developer.apple.com/library/archive/technotes/tn2091/_index.html - -******************************************************************************/ -#ifdef MA_HAS_COREAUDIO -#include - -#if defined(TARGET_OS_IPHONE) && TARGET_OS_IPHONE == 1 - #define MA_APPLE_MOBILE - #if defined(TARGET_OS_TV) && TARGET_OS_TV == 1 - #define MA_APPLE_TV - #endif - #if defined(TARGET_OS_WATCH) && TARGET_OS_WATCH == 1 - #define MA_APPLE_WATCH - #endif - #if __has_feature(objc_arc) - #define MA_BRIDGE_TRANSFER __bridge_transfer - #define MA_BRIDGE_RETAINED __bridge_retained - #else - #define MA_BRIDGE_TRANSFER - #define MA_BRIDGE_RETAINED - #endif -#else - #define MA_APPLE_DESKTOP -#endif - -#if defined(MA_APPLE_DESKTOP) -#include -#else -#include -#endif - -#include - -/* CoreFoundation */ -typedef Boolean (* ma_CFStringGetCString_proc)(CFStringRef theString, char* buffer, CFIndex bufferSize, CFStringEncoding encoding); -typedef void (* ma_CFRelease_proc)(CFTypeRef cf); - -/* CoreAudio */ -#if defined(MA_APPLE_DESKTOP) -typedef OSStatus (* ma_AudioObjectGetPropertyData_proc)(AudioObjectID inObjectID, const AudioObjectPropertyAddress* inAddress, UInt32 inQualifierDataSize, const void* inQualifierData, UInt32* ioDataSize, void* outData); -typedef OSStatus (* ma_AudioObjectGetPropertyDataSize_proc)(AudioObjectID inObjectID, const AudioObjectPropertyAddress* inAddress, UInt32 inQualifierDataSize, const void* inQualifierData, UInt32* outDataSize); -typedef OSStatus (* ma_AudioObjectSetPropertyData_proc)(AudioObjectID inObjectID, const AudioObjectPropertyAddress* inAddress, UInt32 inQualifierDataSize, const void* inQualifierData, UInt32 inDataSize, const void* inData); -typedef OSStatus (* ma_AudioObjectAddPropertyListener_proc)(AudioObjectID inObjectID, const AudioObjectPropertyAddress* inAddress, AudioObjectPropertyListenerProc inListener, void* inClientData); -typedef OSStatus (* ma_AudioObjectRemovePropertyListener_proc)(AudioObjectID inObjectID, const AudioObjectPropertyAddress* inAddress, AudioObjectPropertyListenerProc inListener, void* inClientData); -#endif - -/* AudioToolbox */ -typedef AudioComponent (* ma_AudioComponentFindNext_proc)(AudioComponent inComponent, const AudioComponentDescription* inDesc); -typedef OSStatus (* ma_AudioComponentInstanceDispose_proc)(AudioComponentInstance inInstance); -typedef OSStatus (* ma_AudioComponentInstanceNew_proc)(AudioComponent inComponent, AudioComponentInstance* outInstance); -typedef OSStatus (* ma_AudioOutputUnitStart_proc)(AudioUnit inUnit); -typedef OSStatus (* ma_AudioOutputUnitStop_proc)(AudioUnit inUnit); -typedef OSStatus (* ma_AudioUnitAddPropertyListener_proc)(AudioUnit inUnit, AudioUnitPropertyID inID, AudioUnitPropertyListenerProc inProc, void* inProcUserData); -typedef OSStatus (* ma_AudioUnitGetPropertyInfo_proc)(AudioUnit inUnit, AudioUnitPropertyID inID, AudioUnitScope inScope, AudioUnitElement inElement, UInt32* outDataSize, Boolean* outWriteable); -typedef OSStatus (* ma_AudioUnitGetProperty_proc)(AudioUnit inUnit, AudioUnitPropertyID inID, AudioUnitScope inScope, AudioUnitElement inElement, void* outData, UInt32* ioDataSize); -typedef OSStatus (* ma_AudioUnitSetProperty_proc)(AudioUnit inUnit, AudioUnitPropertyID inID, AudioUnitScope inScope, AudioUnitElement inElement, const void* inData, UInt32 inDataSize); -typedef OSStatus (* ma_AudioUnitInitialize_proc)(AudioUnit inUnit); -typedef OSStatus (* ma_AudioUnitRender_proc)(AudioUnit inUnit, AudioUnitRenderActionFlags* ioActionFlags, const AudioTimeStamp* inTimeStamp, UInt32 inOutputBusNumber, UInt32 inNumberFrames, AudioBufferList* ioData); - - -#define MA_COREAUDIO_OUTPUT_BUS 0 -#define MA_COREAUDIO_INPUT_BUS 1 - -#if defined(MA_APPLE_DESKTOP) -static ma_result ma_device_reinit_internal__coreaudio(ma_device* pDevice, ma_device_type deviceType, ma_bool32 disposePreviousAudioUnit); -#endif - -/* -Core Audio - -So far, Core Audio has been the worst backend to work with due to being both unintuitive and having almost no documentation -apart from comments in the headers (which admittedly are quite good). For my own purposes, and for anybody out there whose -needing to figure out how this darn thing works, I'm going to outline a few things here. - -Since miniaudio is a fairly low-level API, one of the things it needs is control over specific devices, and it needs to be -able to identify whether or not it can be used as playback and/or capture. The AudioObject API is the only one I've seen -that supports this level of detail. There was some public domain sample code I stumbled across that used the AudioComponent -and AudioUnit APIs, but I couldn't see anything that gave low-level control over device selection and capabilities (the -distinction between playback and capture in particular). Therefore, miniaudio is using the AudioObject API. - -Most (all?) functions in the AudioObject API take a AudioObjectID as its input. This is the device identifier. When -retrieving global information, such as the device list, you use kAudioObjectSystemObject. When retrieving device-specific -data, you pass in the ID for that device. In order to retrieve device-specific IDs you need to enumerate over each of the -devices. This is done using the AudioObjectGetPropertyDataSize() and AudioObjectGetPropertyData() APIs which seem to be -the central APIs for retrieving information about the system and specific devices. - -To use the AudioObjectGetPropertyData() API you need to use the notion of a property address. A property address is a -structure with three variables and is used to identify which property you are getting or setting. The first is the "selector" -which is basically the specific property that you're wanting to retrieve or set. The second is the "scope", which is -typically set to kAudioObjectPropertyScopeGlobal, kAudioObjectPropertyScopeInput for input-specific properties and -kAudioObjectPropertyScopeOutput for output-specific properties. The last is the "element" which is always set to -kAudioObjectPropertyElementMain in miniaudio's case. I don't know of any cases where this would be set to anything different. - -Back to the earlier issue of device retrieval, you first use the AudioObjectGetPropertyDataSize() API to retrieve the size -of the raw data which is just a list of AudioDeviceID's. You use the kAudioObjectSystemObject AudioObjectID, and a property -address with the kAudioHardwarePropertyDevices selector and the kAudioObjectPropertyScopeGlobal scope. Once you have the -size, allocate a block of memory of that size and then call AudioObjectGetPropertyData(). The data is just a list of -AudioDeviceID's so just do "dataSize/sizeof(AudioDeviceID)" to know the device count. -*/ - -#if defined(MA_APPLE_MOBILE) -static void ma_device__on_notification_interruption_began(ma_device* pDevice) -{ - ma_device__on_notification(ma_device_notification_init(pDevice, ma_device_notification_type_interruption_began)); -} - -static void ma_device__on_notification_interruption_ended(ma_device* pDevice) -{ - ma_device__on_notification(ma_device_notification_init(pDevice, ma_device_notification_type_interruption_ended)); -} -#endif - -static ma_result ma_result_from_OSStatus(OSStatus status) -{ - switch (status) - { - case noErr: return MA_SUCCESS; - #if defined(MA_APPLE_DESKTOP) - case kAudioHardwareNotRunningError: return MA_DEVICE_NOT_STARTED; - case kAudioHardwareUnspecifiedError: return MA_ERROR; - case kAudioHardwareUnknownPropertyError: return MA_INVALID_ARGS; - case kAudioHardwareBadPropertySizeError: return MA_INVALID_OPERATION; - case kAudioHardwareIllegalOperationError: return MA_INVALID_OPERATION; - case kAudioHardwareBadObjectError: return MA_INVALID_ARGS; - case kAudioHardwareBadDeviceError: return MA_INVALID_ARGS; - case kAudioHardwareBadStreamError: return MA_INVALID_ARGS; - case kAudioHardwareUnsupportedOperationError: return MA_INVALID_OPERATION; - case kAudioDeviceUnsupportedFormatError: return MA_FORMAT_NOT_SUPPORTED; - case kAudioDevicePermissionsError: return MA_ACCESS_DENIED; - #endif - default: return MA_ERROR; - } -} - -#if 0 -static ma_channel ma_channel_from_AudioChannelBitmap(AudioChannelBitmap bit) -{ - switch (bit) - { - case kAudioChannelBit_Left: return MA_CHANNEL_LEFT; - case kAudioChannelBit_Right: return MA_CHANNEL_RIGHT; - case kAudioChannelBit_Center: return MA_CHANNEL_FRONT_CENTER; - case kAudioChannelBit_LFEScreen: return MA_CHANNEL_LFE; - case kAudioChannelBit_LeftSurround: return MA_CHANNEL_BACK_LEFT; - case kAudioChannelBit_RightSurround: return MA_CHANNEL_BACK_RIGHT; - case kAudioChannelBit_LeftCenter: return MA_CHANNEL_FRONT_LEFT_CENTER; - case kAudioChannelBit_RightCenter: return MA_CHANNEL_FRONT_RIGHT_CENTER; - case kAudioChannelBit_CenterSurround: return MA_CHANNEL_BACK_CENTER; - case kAudioChannelBit_LeftSurroundDirect: return MA_CHANNEL_SIDE_LEFT; - case kAudioChannelBit_RightSurroundDirect: return MA_CHANNEL_SIDE_RIGHT; - case kAudioChannelBit_TopCenterSurround: return MA_CHANNEL_TOP_CENTER; - case kAudioChannelBit_VerticalHeightLeft: return MA_CHANNEL_TOP_FRONT_LEFT; - case kAudioChannelBit_VerticalHeightCenter: return MA_CHANNEL_TOP_FRONT_CENTER; - case kAudioChannelBit_VerticalHeightRight: return MA_CHANNEL_TOP_FRONT_RIGHT; - case kAudioChannelBit_TopBackLeft: return MA_CHANNEL_TOP_BACK_LEFT; - case kAudioChannelBit_TopBackCenter: return MA_CHANNEL_TOP_BACK_CENTER; - case kAudioChannelBit_TopBackRight: return MA_CHANNEL_TOP_BACK_RIGHT; - default: return MA_CHANNEL_NONE; - } -} -#endif - -static ma_result ma_format_from_AudioStreamBasicDescription(const AudioStreamBasicDescription* pDescription, ma_format* pFormatOut) -{ - MA_ASSERT(pDescription != NULL); - MA_ASSERT(pFormatOut != NULL); - - *pFormatOut = ma_format_unknown; /* Safety. */ - - /* There's a few things miniaudio doesn't support. */ - if (pDescription->mFormatID != kAudioFormatLinearPCM) { - return MA_FORMAT_NOT_SUPPORTED; - } - - /* We don't support any non-packed formats that are aligned high. */ - if ((pDescription->mFormatFlags & kLinearPCMFormatFlagIsAlignedHigh) != 0) { - return MA_FORMAT_NOT_SUPPORTED; - } - - /* Only supporting native-endian. */ - if ((ma_is_little_endian() && (pDescription->mFormatFlags & kAudioFormatFlagIsBigEndian) != 0) || (ma_is_big_endian() && (pDescription->mFormatFlags & kAudioFormatFlagIsBigEndian) == 0)) { - return MA_FORMAT_NOT_SUPPORTED; - } - - /* We are not currently supporting non-interleaved formats (this will be added in a future version of miniaudio). */ - /*if ((pDescription->mFormatFlags & kAudioFormatFlagIsNonInterleaved) != 0) { - return MA_FORMAT_NOT_SUPPORTED; - }*/ - - if ((pDescription->mFormatFlags & kLinearPCMFormatFlagIsFloat) != 0) { - if (pDescription->mBitsPerChannel == 32) { - *pFormatOut = ma_format_f32; - return MA_SUCCESS; - } - } else { - if ((pDescription->mFormatFlags & kLinearPCMFormatFlagIsSignedInteger) != 0) { - if (pDescription->mBitsPerChannel == 16) { - *pFormatOut = ma_format_s16; - return MA_SUCCESS; - } else if (pDescription->mBitsPerChannel == 24) { - if (pDescription->mBytesPerFrame == (pDescription->mBitsPerChannel/8 * pDescription->mChannelsPerFrame)) { - *pFormatOut = ma_format_s24; - return MA_SUCCESS; - } else { - if (pDescription->mBytesPerFrame/pDescription->mChannelsPerFrame == sizeof(ma_int32)) { - /* TODO: Implement ma_format_s24_32. */ - /**pFormatOut = ma_format_s24_32;*/ - /*return MA_SUCCESS;*/ - return MA_FORMAT_NOT_SUPPORTED; - } - } - } else if (pDescription->mBitsPerChannel == 32) { - *pFormatOut = ma_format_s32; - return MA_SUCCESS; - } - } else { - if (pDescription->mBitsPerChannel == 8) { - *pFormatOut = ma_format_u8; - return MA_SUCCESS; - } - } - } - - /* Getting here means the format is not supported. */ - return MA_FORMAT_NOT_SUPPORTED; -} - -#if defined(MA_APPLE_DESKTOP) -static ma_channel ma_channel_from_AudioChannelLabel(AudioChannelLabel label) -{ - switch (label) - { - case kAudioChannelLabel_Unknown: return MA_CHANNEL_NONE; - case kAudioChannelLabel_Unused: return MA_CHANNEL_NONE; - case kAudioChannelLabel_UseCoordinates: return MA_CHANNEL_NONE; - case kAudioChannelLabel_Left: return MA_CHANNEL_LEFT; - case kAudioChannelLabel_Right: return MA_CHANNEL_RIGHT; - case kAudioChannelLabel_Center: return MA_CHANNEL_FRONT_CENTER; - case kAudioChannelLabel_LFEScreen: return MA_CHANNEL_LFE; - case kAudioChannelLabel_LeftSurround: return MA_CHANNEL_BACK_LEFT; - case kAudioChannelLabel_RightSurround: return MA_CHANNEL_BACK_RIGHT; - case kAudioChannelLabel_LeftCenter: return MA_CHANNEL_FRONT_LEFT_CENTER; - case kAudioChannelLabel_RightCenter: return MA_CHANNEL_FRONT_RIGHT_CENTER; - case kAudioChannelLabel_CenterSurround: return MA_CHANNEL_BACK_CENTER; - case kAudioChannelLabel_LeftSurroundDirect: return MA_CHANNEL_SIDE_LEFT; - case kAudioChannelLabel_RightSurroundDirect: return MA_CHANNEL_SIDE_RIGHT; - case kAudioChannelLabel_TopCenterSurround: return MA_CHANNEL_TOP_CENTER; - case kAudioChannelLabel_VerticalHeightLeft: return MA_CHANNEL_TOP_FRONT_LEFT; - case kAudioChannelLabel_VerticalHeightCenter: return MA_CHANNEL_TOP_FRONT_CENTER; - case kAudioChannelLabel_VerticalHeightRight: return MA_CHANNEL_TOP_FRONT_RIGHT; - case kAudioChannelLabel_TopBackLeft: return MA_CHANNEL_TOP_BACK_LEFT; - case kAudioChannelLabel_TopBackCenter: return MA_CHANNEL_TOP_BACK_CENTER; - case kAudioChannelLabel_TopBackRight: return MA_CHANNEL_TOP_BACK_RIGHT; - case kAudioChannelLabel_RearSurroundLeft: return MA_CHANNEL_BACK_LEFT; - case kAudioChannelLabel_RearSurroundRight: return MA_CHANNEL_BACK_RIGHT; - case kAudioChannelLabel_LeftWide: return MA_CHANNEL_SIDE_LEFT; - case kAudioChannelLabel_RightWide: return MA_CHANNEL_SIDE_RIGHT; - case kAudioChannelLabel_LFE2: return MA_CHANNEL_LFE; - case kAudioChannelLabel_LeftTotal: return MA_CHANNEL_LEFT; - case kAudioChannelLabel_RightTotal: return MA_CHANNEL_RIGHT; - case kAudioChannelLabel_HearingImpaired: return MA_CHANNEL_NONE; - case kAudioChannelLabel_Narration: return MA_CHANNEL_MONO; - case kAudioChannelLabel_Mono: return MA_CHANNEL_MONO; - case kAudioChannelLabel_DialogCentricMix: return MA_CHANNEL_MONO; - case kAudioChannelLabel_CenterSurroundDirect: return MA_CHANNEL_BACK_CENTER; - case kAudioChannelLabel_Haptic: return MA_CHANNEL_NONE; - case kAudioChannelLabel_Ambisonic_W: return MA_CHANNEL_NONE; - case kAudioChannelLabel_Ambisonic_X: return MA_CHANNEL_NONE; - case kAudioChannelLabel_Ambisonic_Y: return MA_CHANNEL_NONE; - case kAudioChannelLabel_Ambisonic_Z: return MA_CHANNEL_NONE; - case kAudioChannelLabel_MS_Mid: return MA_CHANNEL_LEFT; - case kAudioChannelLabel_MS_Side: return MA_CHANNEL_RIGHT; - case kAudioChannelLabel_XY_X: return MA_CHANNEL_LEFT; - case kAudioChannelLabel_XY_Y: return MA_CHANNEL_RIGHT; - case kAudioChannelLabel_HeadphonesLeft: return MA_CHANNEL_LEFT; - case kAudioChannelLabel_HeadphonesRight: return MA_CHANNEL_RIGHT; - case kAudioChannelLabel_ClickTrack: return MA_CHANNEL_NONE; - case kAudioChannelLabel_ForeignLanguage: return MA_CHANNEL_NONE; - case kAudioChannelLabel_Discrete: return MA_CHANNEL_NONE; - case kAudioChannelLabel_Discrete_0: return MA_CHANNEL_AUX_0; - case kAudioChannelLabel_Discrete_1: return MA_CHANNEL_AUX_1; - case kAudioChannelLabel_Discrete_2: return MA_CHANNEL_AUX_2; - case kAudioChannelLabel_Discrete_3: return MA_CHANNEL_AUX_3; - case kAudioChannelLabel_Discrete_4: return MA_CHANNEL_AUX_4; - case kAudioChannelLabel_Discrete_5: return MA_CHANNEL_AUX_5; - case kAudioChannelLabel_Discrete_6: return MA_CHANNEL_AUX_6; - case kAudioChannelLabel_Discrete_7: return MA_CHANNEL_AUX_7; - case kAudioChannelLabel_Discrete_8: return MA_CHANNEL_AUX_8; - case kAudioChannelLabel_Discrete_9: return MA_CHANNEL_AUX_9; - case kAudioChannelLabel_Discrete_10: return MA_CHANNEL_AUX_10; - case kAudioChannelLabel_Discrete_11: return MA_CHANNEL_AUX_11; - case kAudioChannelLabel_Discrete_12: return MA_CHANNEL_AUX_12; - case kAudioChannelLabel_Discrete_13: return MA_CHANNEL_AUX_13; - case kAudioChannelLabel_Discrete_14: return MA_CHANNEL_AUX_14; - case kAudioChannelLabel_Discrete_15: return MA_CHANNEL_AUX_15; - case kAudioChannelLabel_Discrete_65535: return MA_CHANNEL_NONE; - - #if 0 /* Introduced in a later version of macOS. */ - case kAudioChannelLabel_HOA_ACN: return MA_CHANNEL_NONE; - case kAudioChannelLabel_HOA_ACN_0: return MA_CHANNEL_AUX_0; - case kAudioChannelLabel_HOA_ACN_1: return MA_CHANNEL_AUX_1; - case kAudioChannelLabel_HOA_ACN_2: return MA_CHANNEL_AUX_2; - case kAudioChannelLabel_HOA_ACN_3: return MA_CHANNEL_AUX_3; - case kAudioChannelLabel_HOA_ACN_4: return MA_CHANNEL_AUX_4; - case kAudioChannelLabel_HOA_ACN_5: return MA_CHANNEL_AUX_5; - case kAudioChannelLabel_HOA_ACN_6: return MA_CHANNEL_AUX_6; - case kAudioChannelLabel_HOA_ACN_7: return MA_CHANNEL_AUX_7; - case kAudioChannelLabel_HOA_ACN_8: return MA_CHANNEL_AUX_8; - case kAudioChannelLabel_HOA_ACN_9: return MA_CHANNEL_AUX_9; - case kAudioChannelLabel_HOA_ACN_10: return MA_CHANNEL_AUX_10; - case kAudioChannelLabel_HOA_ACN_11: return MA_CHANNEL_AUX_11; - case kAudioChannelLabel_HOA_ACN_12: return MA_CHANNEL_AUX_12; - case kAudioChannelLabel_HOA_ACN_13: return MA_CHANNEL_AUX_13; - case kAudioChannelLabel_HOA_ACN_14: return MA_CHANNEL_AUX_14; - case kAudioChannelLabel_HOA_ACN_15: return MA_CHANNEL_AUX_15; - case kAudioChannelLabel_HOA_ACN_65024: return MA_CHANNEL_NONE; - #endif - - default: return MA_CHANNEL_NONE; - } -} - -static ma_result ma_get_channel_map_from_AudioChannelLayout(AudioChannelLayout* pChannelLayout, ma_channel* pChannelMap, size_t channelMapCap) -{ - MA_ASSERT(pChannelLayout != NULL); - - if (pChannelLayout->mChannelLayoutTag == kAudioChannelLayoutTag_UseChannelDescriptions) { - UInt32 iChannel; - for (iChannel = 0; iChannel < pChannelLayout->mNumberChannelDescriptions && iChannel < channelMapCap; ++iChannel) { - pChannelMap[iChannel] = ma_channel_from_AudioChannelLabel(pChannelLayout->mChannelDescriptions[iChannel].mChannelLabel); - } - } else -#if 0 - if (pChannelLayout->mChannelLayoutTag == kAudioChannelLayoutTag_UseChannelBitmap) { - /* This is the same kind of system that's used by Windows audio APIs. */ - UInt32 iChannel = 0; - UInt32 iBit; - AudioChannelBitmap bitmap = pChannelLayout->mChannelBitmap; - for (iBit = 0; iBit < 32 && iChannel < channelMapCap; ++iBit) { - AudioChannelBitmap bit = bitmap & (1 << iBit); - if (bit != 0) { - pChannelMap[iChannel++] = ma_channel_from_AudioChannelBit(bit); - } - } - } else -#endif - { - /* - Need to use the tag to determine the channel map. For now I'm just assuming a default channel map, but later on this should - be updated to determine the mapping based on the tag. - */ - UInt32 channelCount; - - /* Our channel map retrieval APIs below take 32-bit integers, so we'll want to clamp the channel map capacity. */ - if (channelMapCap > 0xFFFFFFFF) { - channelMapCap = 0xFFFFFFFF; - } - - channelCount = ma_min(AudioChannelLayoutTag_GetNumberOfChannels(pChannelLayout->mChannelLayoutTag), (UInt32)channelMapCap); - - switch (pChannelLayout->mChannelLayoutTag) - { - case kAudioChannelLayoutTag_Mono: - case kAudioChannelLayoutTag_Stereo: - case kAudioChannelLayoutTag_StereoHeadphones: - case kAudioChannelLayoutTag_MatrixStereo: - case kAudioChannelLayoutTag_MidSide: - case kAudioChannelLayoutTag_XY: - case kAudioChannelLayoutTag_Binaural: - case kAudioChannelLayoutTag_Ambisonic_B_Format: - { - ma_channel_map_init_standard(ma_standard_channel_map_default, pChannelMap, channelMapCap, channelCount); - } break; - - case kAudioChannelLayoutTag_Octagonal: - { - pChannelMap[7] = MA_CHANNEL_SIDE_RIGHT; - pChannelMap[6] = MA_CHANNEL_SIDE_LEFT; - } MA_FALLTHROUGH; /* Intentional fallthrough. */ - case kAudioChannelLayoutTag_Hexagonal: - { - pChannelMap[5] = MA_CHANNEL_BACK_CENTER; - } MA_FALLTHROUGH; /* Intentional fallthrough. */ - case kAudioChannelLayoutTag_Pentagonal: - { - pChannelMap[4] = MA_CHANNEL_FRONT_CENTER; - } MA_FALLTHROUGH; /* Intentional fallthrough. */ - case kAudioChannelLayoutTag_Quadraphonic: - { - pChannelMap[3] = MA_CHANNEL_BACK_RIGHT; - pChannelMap[2] = MA_CHANNEL_BACK_LEFT; - pChannelMap[1] = MA_CHANNEL_RIGHT; - pChannelMap[0] = MA_CHANNEL_LEFT; - } break; - - /* TODO: Add support for more tags here. */ - - default: - { - ma_channel_map_init_standard(ma_standard_channel_map_default, pChannelMap, channelMapCap, channelCount); - } break; - } - } - - return MA_SUCCESS; -} - -#if (defined(MAC_OS_VERSION_12_0) && MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_VERSION_12_0) || \ - (defined(__IPHONE_15_0) && __IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_15_0) -#define AUDIO_OBJECT_PROPERTY_ELEMENT kAudioObjectPropertyElementMain -#else -/* kAudioObjectPropertyElementMaster is deprecated. */ -#define AUDIO_OBJECT_PROPERTY_ELEMENT kAudioObjectPropertyElementMaster -#endif - -/* kAudioDevicePropertyScope* were renamed to kAudioObjectPropertyScope* in 10.8. */ -#if !defined(MAC_OS_X_VERSION_10_8) || (MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_8) -#define kAudioObjectPropertyScopeInput kAudioDevicePropertyScopeInput -#define kAudioObjectPropertyScopeOutput kAudioDevicePropertyScopeOutput -#endif - -static ma_result ma_get_device_object_ids__coreaudio(ma_context* pContext, UInt32* pDeviceCount, AudioObjectID** ppDeviceObjectIDs) /* NOTE: Free the returned buffer with ma_free(). */ -{ - AudioObjectPropertyAddress propAddressDevices; - UInt32 deviceObjectsDataSize; - OSStatus status; - AudioObjectID* pDeviceObjectIDs; - - MA_ASSERT(pContext != NULL); - MA_ASSERT(pDeviceCount != NULL); - MA_ASSERT(ppDeviceObjectIDs != NULL); - - /* Safety. */ - *pDeviceCount = 0; - *ppDeviceObjectIDs = NULL; - - propAddressDevices.mSelector = kAudioHardwarePropertyDevices; - propAddressDevices.mScope = kAudioObjectPropertyScopeGlobal; - propAddressDevices.mElement = AUDIO_OBJECT_PROPERTY_ELEMENT; - - status = ((ma_AudioObjectGetPropertyDataSize_proc)pContext->coreaudio.AudioObjectGetPropertyDataSize)(kAudioObjectSystemObject, &propAddressDevices, 0, NULL, &deviceObjectsDataSize); - if (status != noErr) { - return ma_result_from_OSStatus(status); - } - - pDeviceObjectIDs = (AudioObjectID*)ma_malloc(deviceObjectsDataSize, &pContext->allocationCallbacks); - if (pDeviceObjectIDs == NULL) { - return MA_OUT_OF_MEMORY; - } - - status = ((ma_AudioObjectGetPropertyData_proc)pContext->coreaudio.AudioObjectGetPropertyData)(kAudioObjectSystemObject, &propAddressDevices, 0, NULL, &deviceObjectsDataSize, pDeviceObjectIDs); - if (status != noErr) { - ma_free(pDeviceObjectIDs, &pContext->allocationCallbacks); - return ma_result_from_OSStatus(status); - } - - *pDeviceCount = deviceObjectsDataSize / sizeof(AudioObjectID); - *ppDeviceObjectIDs = pDeviceObjectIDs; - - return MA_SUCCESS; -} - -static ma_result ma_get_AudioObject_uid_as_CFStringRef(ma_context* pContext, AudioObjectID objectID, CFStringRef* pUID) -{ - AudioObjectPropertyAddress propAddress; - UInt32 dataSize; - OSStatus status; - - MA_ASSERT(pContext != NULL); - - propAddress.mSelector = kAudioDevicePropertyDeviceUID; - propAddress.mScope = kAudioObjectPropertyScopeGlobal; - propAddress.mElement = AUDIO_OBJECT_PROPERTY_ELEMENT; - - dataSize = sizeof(*pUID); - status = ((ma_AudioObjectGetPropertyData_proc)pContext->coreaudio.AudioObjectGetPropertyData)(objectID, &propAddress, 0, NULL, &dataSize, pUID); - if (status != noErr) { - return ma_result_from_OSStatus(status); - } - - return MA_SUCCESS; -} - -static ma_result ma_get_AudioObject_uid(ma_context* pContext, AudioObjectID objectID, size_t bufferSize, char* bufferOut) -{ - CFStringRef uid; - ma_result result; - - MA_ASSERT(pContext != NULL); - - result = ma_get_AudioObject_uid_as_CFStringRef(pContext, objectID, &uid); - if (result != MA_SUCCESS) { - return result; - } - - if (!((ma_CFStringGetCString_proc)pContext->coreaudio.CFStringGetCString)(uid, bufferOut, bufferSize, kCFStringEncodingUTF8)) { - return MA_ERROR; - } - - ((ma_CFRelease_proc)pContext->coreaudio.CFRelease)(uid); - return MA_SUCCESS; -} - -static ma_result ma_get_AudioObject_name(ma_context* pContext, AudioObjectID objectID, size_t bufferSize, char* bufferOut) -{ - AudioObjectPropertyAddress propAddress; - CFStringRef deviceName = NULL; - UInt32 dataSize; - OSStatus status; - - MA_ASSERT(pContext != NULL); - - propAddress.mSelector = kAudioDevicePropertyDeviceNameCFString; - propAddress.mScope = kAudioObjectPropertyScopeGlobal; - propAddress.mElement = AUDIO_OBJECT_PROPERTY_ELEMENT; - - dataSize = sizeof(deviceName); - status = ((ma_AudioObjectGetPropertyData_proc)pContext->coreaudio.AudioObjectGetPropertyData)(objectID, &propAddress, 0, NULL, &dataSize, &deviceName); - if (status != noErr) { - return ma_result_from_OSStatus(status); - } - - if (!((ma_CFStringGetCString_proc)pContext->coreaudio.CFStringGetCString)(deviceName, bufferOut, bufferSize, kCFStringEncodingUTF8)) { - return MA_ERROR; - } - - ((ma_CFRelease_proc)pContext->coreaudio.CFRelease)(deviceName); - return MA_SUCCESS; -} - -static ma_bool32 ma_does_AudioObject_support_scope(ma_context* pContext, AudioObjectID deviceObjectID, AudioObjectPropertyScope scope) -{ - AudioObjectPropertyAddress propAddress; - UInt32 dataSize; - OSStatus status; - AudioBufferList* pBufferList; - ma_bool32 isSupported; - - MA_ASSERT(pContext != NULL); - - /* To know whether or not a device is an input device we need ot look at the stream configuration. If it has an output channel it's a playback device. */ - propAddress.mSelector = kAudioDevicePropertyStreamConfiguration; - propAddress.mScope = scope; - propAddress.mElement = AUDIO_OBJECT_PROPERTY_ELEMENT; - - status = ((ma_AudioObjectGetPropertyDataSize_proc)pContext->coreaudio.AudioObjectGetPropertyDataSize)(deviceObjectID, &propAddress, 0, NULL, &dataSize); - if (status != noErr) { - return MA_FALSE; - } - - pBufferList = (AudioBufferList*)ma_malloc(dataSize, &pContext->allocationCallbacks); - if (pBufferList == NULL) { - return MA_FALSE; /* Out of memory. */ - } - - status = ((ma_AudioObjectGetPropertyData_proc)pContext->coreaudio.AudioObjectGetPropertyData)(deviceObjectID, &propAddress, 0, NULL, &dataSize, pBufferList); - if (status != noErr) { - ma_free(pBufferList, &pContext->allocationCallbacks); - return MA_FALSE; - } - - isSupported = MA_FALSE; - if (pBufferList->mNumberBuffers > 0) { - isSupported = MA_TRUE; - } - - ma_free(pBufferList, &pContext->allocationCallbacks); - return isSupported; -} - -static ma_bool32 ma_does_AudioObject_support_playback(ma_context* pContext, AudioObjectID deviceObjectID) -{ - return ma_does_AudioObject_support_scope(pContext, deviceObjectID, kAudioObjectPropertyScopeOutput); -} - -static ma_bool32 ma_does_AudioObject_support_capture(ma_context* pContext, AudioObjectID deviceObjectID) -{ - return ma_does_AudioObject_support_scope(pContext, deviceObjectID, kAudioObjectPropertyScopeInput); -} - - -static ma_result ma_get_AudioObject_stream_descriptions(ma_context* pContext, AudioObjectID deviceObjectID, ma_device_type deviceType, UInt32* pDescriptionCount, AudioStreamRangedDescription** ppDescriptions) /* NOTE: Free the returned pointer with ma_free(). */ -{ - AudioObjectPropertyAddress propAddress; - UInt32 dataSize; - OSStatus status; - AudioStreamRangedDescription* pDescriptions; - - MA_ASSERT(pContext != NULL); - MA_ASSERT(pDescriptionCount != NULL); - MA_ASSERT(ppDescriptions != NULL); - - /* - TODO: Experiment with kAudioStreamPropertyAvailablePhysicalFormats instead of (or in addition to) kAudioStreamPropertyAvailableVirtualFormats. My - MacBook Pro uses s24/32 format, however, which miniaudio does not currently support. - */ - propAddress.mSelector = kAudioStreamPropertyAvailableVirtualFormats; /*kAudioStreamPropertyAvailablePhysicalFormats;*/ - propAddress.mScope = (deviceType == ma_device_type_playback) ? kAudioObjectPropertyScopeOutput : kAudioObjectPropertyScopeInput; - propAddress.mElement = AUDIO_OBJECT_PROPERTY_ELEMENT; - - status = ((ma_AudioObjectGetPropertyDataSize_proc)pContext->coreaudio.AudioObjectGetPropertyDataSize)(deviceObjectID, &propAddress, 0, NULL, &dataSize); - if (status != noErr) { - return ma_result_from_OSStatus(status); - } - - pDescriptions = (AudioStreamRangedDescription*)ma_malloc(dataSize, &pContext->allocationCallbacks); - if (pDescriptions == NULL) { - return MA_OUT_OF_MEMORY; - } - - status = ((ma_AudioObjectGetPropertyData_proc)pContext->coreaudio.AudioObjectGetPropertyData)(deviceObjectID, &propAddress, 0, NULL, &dataSize, pDescriptions); - if (status != noErr) { - ma_free(pDescriptions, &pContext->allocationCallbacks); - return ma_result_from_OSStatus(status); - } - - *pDescriptionCount = dataSize / sizeof(*pDescriptions); - *ppDescriptions = pDescriptions; - return MA_SUCCESS; -} - - -static ma_result ma_get_AudioObject_channel_layout(ma_context* pContext, AudioObjectID deviceObjectID, ma_device_type deviceType, AudioChannelLayout** ppChannelLayout) /* NOTE: Free the returned pointer with ma_free(). */ -{ - AudioObjectPropertyAddress propAddress; - UInt32 dataSize; - OSStatus status; - AudioChannelLayout* pChannelLayout; - - MA_ASSERT(pContext != NULL); - MA_ASSERT(ppChannelLayout != NULL); - - *ppChannelLayout = NULL; /* Safety. */ - - propAddress.mSelector = kAudioDevicePropertyPreferredChannelLayout; - propAddress.mScope = (deviceType == ma_device_type_playback) ? kAudioObjectPropertyScopeOutput : kAudioObjectPropertyScopeInput; - propAddress.mElement = AUDIO_OBJECT_PROPERTY_ELEMENT; - - status = ((ma_AudioObjectGetPropertyDataSize_proc)pContext->coreaudio.AudioObjectGetPropertyDataSize)(deviceObjectID, &propAddress, 0, NULL, &dataSize); - if (status != noErr) { - return ma_result_from_OSStatus(status); - } - - pChannelLayout = (AudioChannelLayout*)ma_malloc(dataSize, &pContext->allocationCallbacks); - if (pChannelLayout == NULL) { - return MA_OUT_OF_MEMORY; - } - - status = ((ma_AudioObjectGetPropertyData_proc)pContext->coreaudio.AudioObjectGetPropertyData)(deviceObjectID, &propAddress, 0, NULL, &dataSize, pChannelLayout); - if (status != noErr) { - ma_free(pChannelLayout, &pContext->allocationCallbacks); - return ma_result_from_OSStatus(status); - } - - *ppChannelLayout = pChannelLayout; - return MA_SUCCESS; -} - -static ma_result ma_get_AudioObject_channel_count(ma_context* pContext, AudioObjectID deviceObjectID, ma_device_type deviceType, ma_uint32* pChannelCount) -{ - AudioChannelLayout* pChannelLayout; - ma_result result; - - MA_ASSERT(pContext != NULL); - MA_ASSERT(pChannelCount != NULL); - - *pChannelCount = 0; /* Safety. */ - - result = ma_get_AudioObject_channel_layout(pContext, deviceObjectID, deviceType, &pChannelLayout); - if (result != MA_SUCCESS) { - return result; - } - - if (pChannelLayout->mChannelLayoutTag == kAudioChannelLayoutTag_UseChannelDescriptions) { - *pChannelCount = pChannelLayout->mNumberChannelDescriptions; - } else if (pChannelLayout->mChannelLayoutTag == kAudioChannelLayoutTag_UseChannelBitmap) { - *pChannelCount = ma_count_set_bits(pChannelLayout->mChannelBitmap); - } else { - *pChannelCount = AudioChannelLayoutTag_GetNumberOfChannels(pChannelLayout->mChannelLayoutTag); - } - - ma_free(pChannelLayout, &pContext->allocationCallbacks); - return MA_SUCCESS; -} - -#if 0 -static ma_result ma_get_AudioObject_channel_map(ma_context* pContext, AudioObjectID deviceObjectID, ma_device_type deviceType, ma_channel* pChannelMap, size_t channelMapCap) -{ - AudioChannelLayout* pChannelLayout; - ma_result result; - - MA_ASSERT(pContext != NULL); - - result = ma_get_AudioObject_channel_layout(pContext, deviceObjectID, deviceType, &pChannelLayout); - if (result != MA_SUCCESS) { - return result; /* Rather than always failing here, would it be more robust to simply assume a default? */ - } - - result = ma_get_channel_map_from_AudioChannelLayout(pChannelLayout, pChannelMap, channelMapCap); - if (result != MA_SUCCESS) { - ma_free(pChannelLayout, &pContext->allocationCallbacks); - return result; - } - - ma_free(pChannelLayout, &pContext->allocationCallbacks); - return result; -} -#endif - -static ma_result ma_get_AudioObject_sample_rates(ma_context* pContext, AudioObjectID deviceObjectID, ma_device_type deviceType, UInt32* pSampleRateRangesCount, AudioValueRange** ppSampleRateRanges) /* NOTE: Free the returned pointer with ma_free(). */ -{ - AudioObjectPropertyAddress propAddress; - UInt32 dataSize; - OSStatus status; - AudioValueRange* pSampleRateRanges; - - MA_ASSERT(pContext != NULL); - MA_ASSERT(pSampleRateRangesCount != NULL); - MA_ASSERT(ppSampleRateRanges != NULL); - - /* Safety. */ - *pSampleRateRangesCount = 0; - *ppSampleRateRanges = NULL; - - propAddress.mSelector = kAudioDevicePropertyAvailableNominalSampleRates; - propAddress.mScope = (deviceType == ma_device_type_playback) ? kAudioObjectPropertyScopeOutput : kAudioObjectPropertyScopeInput; - propAddress.mElement = AUDIO_OBJECT_PROPERTY_ELEMENT; - - status = ((ma_AudioObjectGetPropertyDataSize_proc)pContext->coreaudio.AudioObjectGetPropertyDataSize)(deviceObjectID, &propAddress, 0, NULL, &dataSize); - if (status != noErr) { - return ma_result_from_OSStatus(status); - } - - pSampleRateRanges = (AudioValueRange*)ma_malloc(dataSize, &pContext->allocationCallbacks); - if (pSampleRateRanges == NULL) { - return MA_OUT_OF_MEMORY; - } - - status = ((ma_AudioObjectGetPropertyData_proc)pContext->coreaudio.AudioObjectGetPropertyData)(deviceObjectID, &propAddress, 0, NULL, &dataSize, pSampleRateRanges); - if (status != noErr) { - ma_free(pSampleRateRanges, &pContext->allocationCallbacks); - return ma_result_from_OSStatus(status); - } - - *pSampleRateRangesCount = dataSize / sizeof(*pSampleRateRanges); - *ppSampleRateRanges = pSampleRateRanges; - return MA_SUCCESS; -} - -#if 0 -static ma_result ma_get_AudioObject_get_closest_sample_rate(ma_context* pContext, AudioObjectID deviceObjectID, ma_device_type deviceType, ma_uint32 sampleRateIn, ma_uint32* pSampleRateOut) -{ - UInt32 sampleRateRangeCount; - AudioValueRange* pSampleRateRanges; - ma_result result; - - MA_ASSERT(pContext != NULL); - MA_ASSERT(pSampleRateOut != NULL); - - *pSampleRateOut = 0; /* Safety. */ - - result = ma_get_AudioObject_sample_rates(pContext, deviceObjectID, deviceType, &sampleRateRangeCount, &pSampleRateRanges); - if (result != MA_SUCCESS) { - return result; - } - - if (sampleRateRangeCount == 0) { - ma_free(pSampleRateRanges, &pContext->allocationCallbacks); - return MA_ERROR; /* Should never hit this case should we? */ - } - - if (sampleRateIn == 0) { - /* Search in order of miniaudio's preferred priority. */ - UInt32 iMALSampleRate; - for (iMALSampleRate = 0; iMALSampleRate < ma_countof(g_maStandardSampleRatePriorities); ++iMALSampleRate) { - ma_uint32 malSampleRate = g_maStandardSampleRatePriorities[iMALSampleRate]; - UInt32 iCASampleRate; - for (iCASampleRate = 0; iCASampleRate < sampleRateRangeCount; ++iCASampleRate) { - AudioValueRange caSampleRate = pSampleRateRanges[iCASampleRate]; - if (caSampleRate.mMinimum <= malSampleRate && caSampleRate.mMaximum >= malSampleRate) { - *pSampleRateOut = malSampleRate; - ma_free(pSampleRateRanges, &pContext->allocationCallbacks); - return MA_SUCCESS; - } - } - } - - /* - If we get here it means none of miniaudio's standard sample rates matched any of the supported sample rates from the device. In this - case we just fall back to the first one reported by Core Audio. - */ - MA_ASSERT(sampleRateRangeCount > 0); - - *pSampleRateOut = pSampleRateRanges[0].mMinimum; - ma_free(pSampleRateRanges, &pContext->allocationCallbacks); - return MA_SUCCESS; - } else { - /* Find the closest match to this sample rate. */ - UInt32 currentAbsoluteDifference = INT32_MAX; - UInt32 iCurrentClosestRange = (UInt32)-1; - UInt32 iRange; - for (iRange = 0; iRange < sampleRateRangeCount; ++iRange) { - if (pSampleRateRanges[iRange].mMinimum <= sampleRateIn && pSampleRateRanges[iRange].mMaximum >= sampleRateIn) { - *pSampleRateOut = sampleRateIn; - ma_free(pSampleRateRanges, &pContext->allocationCallbacks); - return MA_SUCCESS; - } else { - UInt32 absoluteDifference; - if (pSampleRateRanges[iRange].mMinimum > sampleRateIn) { - absoluteDifference = pSampleRateRanges[iRange].mMinimum - sampleRateIn; - } else { - absoluteDifference = sampleRateIn - pSampleRateRanges[iRange].mMaximum; - } - - if (currentAbsoluteDifference > absoluteDifference) { - currentAbsoluteDifference = absoluteDifference; - iCurrentClosestRange = iRange; - } - } - } - - MA_ASSERT(iCurrentClosestRange != (UInt32)-1); - - *pSampleRateOut = pSampleRateRanges[iCurrentClosestRange].mMinimum; - ma_free(pSampleRateRanges, &pContext->allocationCallbacks); - return MA_SUCCESS; - } - - /* Should never get here, but it would mean we weren't able to find any suitable sample rates. */ - /*ma_free(pSampleRateRanges, &pContext->allocationCallbacks);*/ - /*return MA_ERROR;*/ -} -#endif - -static ma_result ma_get_AudioObject_closest_buffer_size_in_frames(ma_context* pContext, AudioObjectID deviceObjectID, ma_device_type deviceType, ma_uint32 bufferSizeInFramesIn, ma_uint32* pBufferSizeInFramesOut) -{ - AudioObjectPropertyAddress propAddress; - AudioValueRange bufferSizeRange; - UInt32 dataSize; - OSStatus status; - - MA_ASSERT(pContext != NULL); - MA_ASSERT(pBufferSizeInFramesOut != NULL); - - *pBufferSizeInFramesOut = 0; /* Safety. */ - - propAddress.mSelector = kAudioDevicePropertyBufferFrameSizeRange; - propAddress.mScope = (deviceType == ma_device_type_playback) ? kAudioObjectPropertyScopeOutput : kAudioObjectPropertyScopeInput; - propAddress.mElement = AUDIO_OBJECT_PROPERTY_ELEMENT; - - dataSize = sizeof(bufferSizeRange); - status = ((ma_AudioObjectGetPropertyData_proc)pContext->coreaudio.AudioObjectGetPropertyData)(deviceObjectID, &propAddress, 0, NULL, &dataSize, &bufferSizeRange); - if (status != noErr) { - return ma_result_from_OSStatus(status); - } - - /* This is just a clamp. */ - if (bufferSizeInFramesIn < bufferSizeRange.mMinimum) { - *pBufferSizeInFramesOut = (ma_uint32)bufferSizeRange.mMinimum; - } else if (bufferSizeInFramesIn > bufferSizeRange.mMaximum) { - *pBufferSizeInFramesOut = (ma_uint32)bufferSizeRange.mMaximum; - } else { - *pBufferSizeInFramesOut = bufferSizeInFramesIn; - } - - return MA_SUCCESS; -} - -static ma_result ma_set_AudioObject_buffer_size_in_frames(ma_context* pContext, AudioObjectID deviceObjectID, ma_device_type deviceType, ma_uint32* pPeriodSizeInOut) -{ - ma_result result; - ma_uint32 chosenBufferSizeInFrames; - AudioObjectPropertyAddress propAddress; - UInt32 dataSize; - OSStatus status; - - MA_ASSERT(pContext != NULL); - - result = ma_get_AudioObject_closest_buffer_size_in_frames(pContext, deviceObjectID, deviceType, *pPeriodSizeInOut, &chosenBufferSizeInFrames); - if (result != MA_SUCCESS) { - return result; - } - - /* Try setting the size of the buffer... If this fails we just use whatever is currently set. */ - propAddress.mSelector = kAudioDevicePropertyBufferFrameSize; - propAddress.mScope = (deviceType == ma_device_type_playback) ? kAudioObjectPropertyScopeOutput : kAudioObjectPropertyScopeInput; - propAddress.mElement = AUDIO_OBJECT_PROPERTY_ELEMENT; - - ((ma_AudioObjectSetPropertyData_proc)pContext->coreaudio.AudioObjectSetPropertyData)(deviceObjectID, &propAddress, 0, NULL, sizeof(chosenBufferSizeInFrames), &chosenBufferSizeInFrames); - - /* Get the actual size of the buffer. */ - dataSize = sizeof(*pPeriodSizeInOut); - status = ((ma_AudioObjectGetPropertyData_proc)pContext->coreaudio.AudioObjectGetPropertyData)(deviceObjectID, &propAddress, 0, NULL, &dataSize, &chosenBufferSizeInFrames); - if (status != noErr) { - return ma_result_from_OSStatus(status); - } - - *pPeriodSizeInOut = chosenBufferSizeInFrames; - return MA_SUCCESS; -} - -static ma_result ma_find_default_AudioObjectID(ma_context* pContext, ma_device_type deviceType, AudioObjectID* pDeviceObjectID) -{ - AudioObjectPropertyAddress propAddressDefaultDevice; - UInt32 defaultDeviceObjectIDSize = sizeof(AudioObjectID); - AudioObjectID defaultDeviceObjectID; - OSStatus status; - - MA_ASSERT(pContext != NULL); - MA_ASSERT(pDeviceObjectID != NULL); - - /* Safety. */ - *pDeviceObjectID = 0; - - propAddressDefaultDevice.mScope = kAudioObjectPropertyScopeGlobal; - propAddressDefaultDevice.mElement = AUDIO_OBJECT_PROPERTY_ELEMENT; - if (deviceType == ma_device_type_playback) { - propAddressDefaultDevice.mSelector = kAudioHardwarePropertyDefaultOutputDevice; - } else { - propAddressDefaultDevice.mSelector = kAudioHardwarePropertyDefaultInputDevice; - } - - defaultDeviceObjectIDSize = sizeof(AudioObjectID); - status = ((ma_AudioObjectGetPropertyData_proc)pContext->coreaudio.AudioObjectGetPropertyData)(kAudioObjectSystemObject, &propAddressDefaultDevice, 0, NULL, &defaultDeviceObjectIDSize, &defaultDeviceObjectID); - if (status == noErr) { - *pDeviceObjectID = defaultDeviceObjectID; - return MA_SUCCESS; - } - - /* If we get here it means we couldn't find the device. */ - return MA_NO_DEVICE; -} - -static ma_result ma_find_AudioObjectID(ma_context* pContext, ma_device_type deviceType, const ma_device_id* pDeviceID, AudioObjectID* pDeviceObjectID) -{ - MA_ASSERT(pContext != NULL); - MA_ASSERT(pDeviceObjectID != NULL); - - /* Safety. */ - *pDeviceObjectID = 0; - - if (pDeviceID == NULL) { - /* Default device. */ - return ma_find_default_AudioObjectID(pContext, deviceType, pDeviceObjectID); - } else { - /* Explicit device. */ - UInt32 deviceCount; - AudioObjectID* pDeviceObjectIDs; - ma_result result; - UInt32 iDevice; - - result = ma_get_device_object_ids__coreaudio(pContext, &deviceCount, &pDeviceObjectIDs); - if (result != MA_SUCCESS) { - return result; - } - - for (iDevice = 0; iDevice < deviceCount; ++iDevice) { - AudioObjectID deviceObjectID = pDeviceObjectIDs[iDevice]; - - char uid[256]; - if (ma_get_AudioObject_uid(pContext, deviceObjectID, sizeof(uid), uid) != MA_SUCCESS) { - continue; - } - - if (deviceType == ma_device_type_playback) { - if (ma_does_AudioObject_support_playback(pContext, deviceObjectID)) { - if (strcmp(uid, pDeviceID->coreaudio) == 0) { - *pDeviceObjectID = deviceObjectID; - ma_free(pDeviceObjectIDs, &pContext->allocationCallbacks); - return MA_SUCCESS; - } - } - } else { - if (ma_does_AudioObject_support_capture(pContext, deviceObjectID)) { - if (strcmp(uid, pDeviceID->coreaudio) == 0) { - *pDeviceObjectID = deviceObjectID; - ma_free(pDeviceObjectIDs, &pContext->allocationCallbacks); - return MA_SUCCESS; - } - } - } - } - - ma_free(pDeviceObjectIDs, &pContext->allocationCallbacks); - } - - /* If we get here it means we couldn't find the device. */ - return MA_NO_DEVICE; -} - - -static ma_result ma_find_best_format__coreaudio(ma_context* pContext, AudioObjectID deviceObjectID, ma_device_type deviceType, ma_format format, ma_uint32 channels, ma_uint32 sampleRate, const AudioStreamBasicDescription* pOrigFormat, AudioStreamBasicDescription* pFormat) -{ - UInt32 deviceFormatDescriptionCount; - AudioStreamRangedDescription* pDeviceFormatDescriptions; - ma_result result; - ma_uint32 desiredSampleRate; - ma_uint32 desiredChannelCount; - ma_format desiredFormat; - AudioStreamBasicDescription bestDeviceFormatSoFar; - ma_bool32 hasSupportedFormat; - UInt32 iFormat; - - result = ma_get_AudioObject_stream_descriptions(pContext, deviceObjectID, deviceType, &deviceFormatDescriptionCount, &pDeviceFormatDescriptions); - if (result != MA_SUCCESS) { - return result; - } - - desiredSampleRate = sampleRate; - if (desiredSampleRate == 0) { - desiredSampleRate = (ma_uint32)pOrigFormat->mSampleRate; - } - - desiredChannelCount = channels; - if (desiredChannelCount == 0) { - desiredChannelCount = pOrigFormat->mChannelsPerFrame; - } - - desiredFormat = format; - if (desiredFormat == ma_format_unknown) { - result = ma_format_from_AudioStreamBasicDescription(pOrigFormat, &desiredFormat); - if (result != MA_SUCCESS || desiredFormat == ma_format_unknown) { - desiredFormat = g_maFormatPriorities[0]; - } - } - - /* - If we get here it means we don't have an exact match to what the client is asking for. We'll need to find the closest one. The next - loop will check for formats that have the same sample rate to what we're asking for. If there is, we prefer that one in all cases. - */ - MA_ZERO_OBJECT(&bestDeviceFormatSoFar); - - hasSupportedFormat = MA_FALSE; - for (iFormat = 0; iFormat < deviceFormatDescriptionCount; ++iFormat) { - ma_format formatFromDescription; - ma_result formatResult = ma_format_from_AudioStreamBasicDescription(&pDeviceFormatDescriptions[iFormat].mFormat, &formatFromDescription); - if (formatResult == MA_SUCCESS && formatFromDescription != ma_format_unknown) { - hasSupportedFormat = MA_TRUE; - bestDeviceFormatSoFar = pDeviceFormatDescriptions[iFormat].mFormat; - break; - } - } - - if (!hasSupportedFormat) { - ma_free(pDeviceFormatDescriptions, &pContext->allocationCallbacks); - return MA_FORMAT_NOT_SUPPORTED; - } - - - for (iFormat = 0; iFormat < deviceFormatDescriptionCount; ++iFormat) { - AudioStreamBasicDescription thisDeviceFormat = pDeviceFormatDescriptions[iFormat].mFormat; - ma_format thisSampleFormat; - ma_result formatResult; - ma_format bestSampleFormatSoFar; - - /* If the format is not supported by miniaudio we need to skip this one entirely. */ - formatResult = ma_format_from_AudioStreamBasicDescription(&pDeviceFormatDescriptions[iFormat].mFormat, &thisSampleFormat); - if (formatResult != MA_SUCCESS || thisSampleFormat == ma_format_unknown) { - continue; /* The format is not supported by miniaudio. Skip. */ - } - - ma_format_from_AudioStreamBasicDescription(&bestDeviceFormatSoFar, &bestSampleFormatSoFar); - - /* Getting here means the format is supported by miniaudio which makes this format a candidate. */ - if (thisDeviceFormat.mSampleRate != desiredSampleRate) { - /* - The sample rate does not match, but this format could still be usable, although it's a very low priority. If the best format - so far has an equal sample rate we can just ignore this one. - */ - if (bestDeviceFormatSoFar.mSampleRate == desiredSampleRate) { - continue; /* The best sample rate so far has the same sample rate as what we requested which means it's still the best so far. Skip this format. */ - } else { - /* In this case, neither the best format so far nor this one have the same sample rate. Check the channel count next. */ - if (thisDeviceFormat.mChannelsPerFrame != desiredChannelCount) { - /* This format has a different sample rate _and_ a different channel count. */ - if (bestDeviceFormatSoFar.mChannelsPerFrame == desiredChannelCount) { - continue; /* No change to the best format. */ - } else { - /* - Both this format and the best so far have different sample rates and different channel counts. Whichever has the - best format is the new best. - */ - if (ma_get_format_priority_index(thisSampleFormat) < ma_get_format_priority_index(bestSampleFormatSoFar)) { - bestDeviceFormatSoFar = thisDeviceFormat; - continue; - } else { - continue; /* No change to the best format. */ - } - } - } else { - /* This format has a different sample rate but the desired channel count. */ - if (bestDeviceFormatSoFar.mChannelsPerFrame == desiredChannelCount) { - /* Both this format and the best so far have the desired channel count. Whichever has the best format is the new best. */ - if (ma_get_format_priority_index(thisSampleFormat) < ma_get_format_priority_index(bestSampleFormatSoFar)) { - bestDeviceFormatSoFar = thisDeviceFormat; - continue; - } else { - continue; /* No change to the best format for now. */ - } - } else { - /* This format has the desired channel count, but the best so far does not. We have a new best. */ - bestDeviceFormatSoFar = thisDeviceFormat; - continue; - } - } - } - } else { - /* - The sample rates match which makes this format a very high priority contender. If the best format so far has a different - sample rate it needs to be replaced with this one. - */ - if (bestDeviceFormatSoFar.mSampleRate != desiredSampleRate) { - bestDeviceFormatSoFar = thisDeviceFormat; - continue; - } else { - /* In this case both this format and the best format so far have the same sample rate. Check the channel count next. */ - if (thisDeviceFormat.mChannelsPerFrame == desiredChannelCount) { - /* - In this case this format has the same channel count as what the client is requesting. If the best format so far has - a different count, this one becomes the new best. - */ - if (bestDeviceFormatSoFar.mChannelsPerFrame != desiredChannelCount) { - bestDeviceFormatSoFar = thisDeviceFormat; - continue; - } else { - /* In this case both this format and the best so far have the ideal sample rate and channel count. Check the format. */ - if (thisSampleFormat == desiredFormat) { - bestDeviceFormatSoFar = thisDeviceFormat; - break; /* Found the exact match. */ - } else { - /* The formats are different. The new best format is the one with the highest priority format according to miniaudio. */ - if (ma_get_format_priority_index(thisSampleFormat) < ma_get_format_priority_index(bestSampleFormatSoFar)) { - bestDeviceFormatSoFar = thisDeviceFormat; - continue; - } else { - continue; /* No change to the best format for now. */ - } - } - } - } else { - /* - In this case the channel count is different to what the client has requested. If the best so far has the same channel - count as the requested count then it remains the best. - */ - if (bestDeviceFormatSoFar.mChannelsPerFrame == desiredChannelCount) { - continue; - } else { - /* - This is the case where both have the same sample rate (good) but different channel counts. Right now both have about - the same priority, but we need to compare the format now. - */ - if (thisSampleFormat == bestSampleFormatSoFar) { - if (ma_get_format_priority_index(thisSampleFormat) < ma_get_format_priority_index(bestSampleFormatSoFar)) { - bestDeviceFormatSoFar = thisDeviceFormat; - continue; - } else { - continue; /* No change to the best format for now. */ - } - } - } - } - } - } - } - - *pFormat = bestDeviceFormatSoFar; - - ma_free(pDeviceFormatDescriptions, &pContext->allocationCallbacks); - return MA_SUCCESS; -} - -static ma_result ma_get_AudioUnit_channel_map(ma_context* pContext, AudioUnit audioUnit, ma_device_type deviceType, ma_channel* pChannelMap, size_t channelMapCap) -{ - AudioUnitScope deviceScope; - AudioUnitElement deviceBus; - UInt32 channelLayoutSize; - OSStatus status; - AudioChannelLayout* pChannelLayout; - ma_result result; - - MA_ASSERT(pContext != NULL); - - if (deviceType == ma_device_type_playback) { - deviceScope = kAudioUnitScope_Input; - deviceBus = MA_COREAUDIO_OUTPUT_BUS; - } else { - deviceScope = kAudioUnitScope_Output; - deviceBus = MA_COREAUDIO_INPUT_BUS; - } - - status = ((ma_AudioUnitGetPropertyInfo_proc)pContext->coreaudio.AudioUnitGetPropertyInfo)(audioUnit, kAudioUnitProperty_AudioChannelLayout, deviceScope, deviceBus, &channelLayoutSize, NULL); - if (status != noErr) { - return ma_result_from_OSStatus(status); - } - - pChannelLayout = (AudioChannelLayout*)ma_malloc(channelLayoutSize, &pContext->allocationCallbacks); - if (pChannelLayout == NULL) { - return MA_OUT_OF_MEMORY; - } - - status = ((ma_AudioUnitGetProperty_proc)pContext->coreaudio.AudioUnitGetProperty)(audioUnit, kAudioUnitProperty_AudioChannelLayout, deviceScope, deviceBus, pChannelLayout, &channelLayoutSize); - if (status != noErr) { - ma_free(pChannelLayout, &pContext->allocationCallbacks); - return ma_result_from_OSStatus(status); - } - - result = ma_get_channel_map_from_AudioChannelLayout(pChannelLayout, pChannelMap, channelMapCap); - if (result != MA_SUCCESS) { - ma_free(pChannelLayout, &pContext->allocationCallbacks); - return result; - } - - ma_free(pChannelLayout, &pContext->allocationCallbacks); - return MA_SUCCESS; -} -#endif /* MA_APPLE_DESKTOP */ - - -#if !defined(MA_APPLE_DESKTOP) -static void ma_AVAudioSessionPortDescription_to_device_info(AVAudioSessionPortDescription* pPortDesc, ma_device_info* pInfo) -{ - MA_ZERO_OBJECT(pInfo); - ma_strncpy_s(pInfo->name, sizeof(pInfo->name), [pPortDesc.portName UTF8String], (size_t)-1); - ma_strncpy_s(pInfo->id.coreaudio, sizeof(pInfo->id.coreaudio), [pPortDesc.UID UTF8String], (size_t)-1); -} -#endif - -static ma_result ma_context_enumerate_devices__coreaudio(ma_context* pContext, ma_enum_devices_callback_proc callback, void* pUserData) -{ -#if defined(MA_APPLE_DESKTOP) - UInt32 deviceCount; - AudioObjectID* pDeviceObjectIDs; - AudioObjectID defaultDeviceObjectIDPlayback; - AudioObjectID defaultDeviceObjectIDCapture; - ma_result result; - UInt32 iDevice; - - ma_find_default_AudioObjectID(pContext, ma_device_type_playback, &defaultDeviceObjectIDPlayback); /* OK if this fails. */ - ma_find_default_AudioObjectID(pContext, ma_device_type_capture, &defaultDeviceObjectIDCapture); /* OK if this fails. */ - - result = ma_get_device_object_ids__coreaudio(pContext, &deviceCount, &pDeviceObjectIDs); - if (result != MA_SUCCESS) { - return result; - } - - for (iDevice = 0; iDevice < deviceCount; ++iDevice) { - AudioObjectID deviceObjectID = pDeviceObjectIDs[iDevice]; - ma_device_info info; - - MA_ZERO_OBJECT(&info); - if (ma_get_AudioObject_uid(pContext, deviceObjectID, sizeof(info.id.coreaudio), info.id.coreaudio) != MA_SUCCESS) { - continue; - } - if (ma_get_AudioObject_name(pContext, deviceObjectID, sizeof(info.name), info.name) != MA_SUCCESS) { - continue; - } - - if (ma_does_AudioObject_support_playback(pContext, deviceObjectID)) { - if (deviceObjectID == defaultDeviceObjectIDPlayback) { - info.isDefault = MA_TRUE; - } - - if (!callback(pContext, ma_device_type_playback, &info, pUserData)) { - break; - } - } - if (ma_does_AudioObject_support_capture(pContext, deviceObjectID)) { - if (deviceObjectID == defaultDeviceObjectIDCapture) { - info.isDefault = MA_TRUE; - } - - if (!callback(pContext, ma_device_type_capture, &info, pUserData)) { - break; - } - } - } - - ma_free(pDeviceObjectIDs, &pContext->allocationCallbacks); -#else - ma_device_info info; - NSArray *pInputs = [[[AVAudioSession sharedInstance] currentRoute] inputs]; - NSArray *pOutputs = [[[AVAudioSession sharedInstance] currentRoute] outputs]; - - for (AVAudioSessionPortDescription* pPortDesc in pOutputs) { - ma_AVAudioSessionPortDescription_to_device_info(pPortDesc, &info); - if (!callback(pContext, ma_device_type_playback, &info, pUserData)) { - return MA_SUCCESS; - } - } - - for (AVAudioSessionPortDescription* pPortDesc in pInputs) { - ma_AVAudioSessionPortDescription_to_device_info(pPortDesc, &info); - if (!callback(pContext, ma_device_type_capture, &info, pUserData)) { - return MA_SUCCESS; - } - } -#endif - - return MA_SUCCESS; -} - -static ma_result ma_context_get_device_info__coreaudio(ma_context* pContext, ma_device_type deviceType, const ma_device_id* pDeviceID, ma_device_info* pDeviceInfo) -{ - ma_result result; - - MA_ASSERT(pContext != NULL); - -#if defined(MA_APPLE_DESKTOP) - /* Desktop */ - { - AudioObjectID deviceObjectID; - AudioObjectID defaultDeviceObjectID; - UInt32 streamDescriptionCount; - AudioStreamRangedDescription* pStreamDescriptions; - UInt32 iStreamDescription; - UInt32 sampleRateRangeCount; - AudioValueRange* pSampleRateRanges; - - ma_find_default_AudioObjectID(pContext, deviceType, &defaultDeviceObjectID); /* OK if this fails. */ - - result = ma_find_AudioObjectID(pContext, deviceType, pDeviceID, &deviceObjectID); - if (result != MA_SUCCESS) { - return result; - } - - result = ma_get_AudioObject_uid(pContext, deviceObjectID, sizeof(pDeviceInfo->id.coreaudio), pDeviceInfo->id.coreaudio); - if (result != MA_SUCCESS) { - return result; - } - - result = ma_get_AudioObject_name(pContext, deviceObjectID, sizeof(pDeviceInfo->name), pDeviceInfo->name); - if (result != MA_SUCCESS) { - return result; - } - - if (deviceObjectID == defaultDeviceObjectID) { - pDeviceInfo->isDefault = MA_TRUE; - } - - /* - There could be a large number of permutations here. Fortunately there is only a single channel count - being reported which reduces this quite a bit. For sample rates we're only reporting those that are - one of miniaudio's recognized "standard" rates. If there are still more formats than can fit into - our fixed sized array we'll just need to truncate them. This is unlikely and will probably only happen - if some driver performs software data conversion and therefore reports every possible format and - sample rate. - */ - pDeviceInfo->nativeDataFormatCount = 0; - - /* Formats. */ - { - ma_format uniqueFormats[ma_format_count]; - ma_uint32 uniqueFormatCount = 0; - ma_uint32 channels; - - /* Channels. */ - result = ma_get_AudioObject_channel_count(pContext, deviceObjectID, deviceType, &channels); - if (result != MA_SUCCESS) { - return result; - } - - /* Formats. */ - result = ma_get_AudioObject_stream_descriptions(pContext, deviceObjectID, deviceType, &streamDescriptionCount, &pStreamDescriptions); - if (result != MA_SUCCESS) { - return result; - } - - for (iStreamDescription = 0; iStreamDescription < streamDescriptionCount; ++iStreamDescription) { - ma_format format; - ma_bool32 hasFormatBeenHandled = MA_FALSE; - ma_uint32 iOutputFormat; - ma_uint32 iSampleRate; - - result = ma_format_from_AudioStreamBasicDescription(&pStreamDescriptions[iStreamDescription].mFormat, &format); - if (result != MA_SUCCESS) { - continue; - } - - MA_ASSERT(format != ma_format_unknown); - - /* Make sure the format isn't already in the output list. */ - for (iOutputFormat = 0; iOutputFormat < uniqueFormatCount; ++iOutputFormat) { - if (uniqueFormats[iOutputFormat] == format) { - hasFormatBeenHandled = MA_TRUE; - break; - } - } - - /* If we've already handled this format just skip it. */ - if (hasFormatBeenHandled) { - continue; - } - - uniqueFormats[uniqueFormatCount] = format; - uniqueFormatCount += 1; - - /* Sample Rates */ - result = ma_get_AudioObject_sample_rates(pContext, deviceObjectID, deviceType, &sampleRateRangeCount, &pSampleRateRanges); - if (result != MA_SUCCESS) { - return result; - } - - /* - Annoyingly Core Audio reports a sample rate range. We just get all the standard rates that are - between this range. - */ - for (iSampleRate = 0; iSampleRate < sampleRateRangeCount; ++iSampleRate) { - ma_uint32 iStandardSampleRate; - for (iStandardSampleRate = 0; iStandardSampleRate < ma_countof(g_maStandardSampleRatePriorities); iStandardSampleRate += 1) { - ma_uint32 standardSampleRate = g_maStandardSampleRatePriorities[iStandardSampleRate]; - if (standardSampleRate >= pSampleRateRanges[iSampleRate].mMinimum && standardSampleRate <= pSampleRateRanges[iSampleRate].mMaximum) { - /* We have a new data format. Add it to the list. */ - pDeviceInfo->nativeDataFormats[pDeviceInfo->nativeDataFormatCount].format = format; - pDeviceInfo->nativeDataFormats[pDeviceInfo->nativeDataFormatCount].channels = channels; - pDeviceInfo->nativeDataFormats[pDeviceInfo->nativeDataFormatCount].sampleRate = standardSampleRate; - pDeviceInfo->nativeDataFormats[pDeviceInfo->nativeDataFormatCount].flags = 0; - pDeviceInfo->nativeDataFormatCount += 1; - - if (pDeviceInfo->nativeDataFormatCount >= ma_countof(pDeviceInfo->nativeDataFormats)) { - break; /* No more room for any more formats. */ - } - } - } - } - - ma_free(pSampleRateRanges, &pContext->allocationCallbacks); - - if (pDeviceInfo->nativeDataFormatCount >= ma_countof(pDeviceInfo->nativeDataFormats)) { - break; /* No more room for any more formats. */ - } - } - - ma_free(pStreamDescriptions, &pContext->allocationCallbacks); - } - } -#else - /* Mobile */ - { - AudioComponentDescription desc; - AudioComponent component; - AudioUnit audioUnit; - OSStatus status; - AudioUnitScope formatScope; - AudioUnitElement formatElement; - AudioStreamBasicDescription bestFormat; - UInt32 propSize; - - /* We want to ensure we use a consistent device name to device enumeration. */ - if (pDeviceID != NULL && pDeviceID->coreaudio[0] != '\0') { - ma_bool32 found = MA_FALSE; - if (deviceType == ma_device_type_playback) { - NSArray *pOutputs = [[[AVAudioSession sharedInstance] currentRoute] outputs]; - for (AVAudioSessionPortDescription* pPortDesc in pOutputs) { - if (strcmp(pDeviceID->coreaudio, [pPortDesc.UID UTF8String]) == 0) { - ma_AVAudioSessionPortDescription_to_device_info(pPortDesc, pDeviceInfo); - found = MA_TRUE; - break; - } - } - } else { - NSArray *pInputs = [[[AVAudioSession sharedInstance] currentRoute] inputs]; - for (AVAudioSessionPortDescription* pPortDesc in pInputs) { - if (strcmp(pDeviceID->coreaudio, [pPortDesc.UID UTF8String]) == 0) { - ma_AVAudioSessionPortDescription_to_device_info(pPortDesc, pDeviceInfo); - found = MA_TRUE; - break; - } - } - } - - if (!found) { - return MA_DOES_NOT_EXIST; - } - } else { - if (deviceType == ma_device_type_playback) { - ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), MA_DEFAULT_PLAYBACK_DEVICE_NAME, (size_t)-1); - } else { - ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), MA_DEFAULT_CAPTURE_DEVICE_NAME, (size_t)-1); - } - } - - - /* - Retrieving device information is more annoying on mobile than desktop. For simplicity I'm locking this down to whatever format is - reported on a temporary I/O unit. The problem, however, is that this doesn't return a value for the sample rate which we need to - retrieve from the AVAudioSession shared instance. - */ - desc.componentType = kAudioUnitType_Output; - desc.componentSubType = kAudioUnitSubType_RemoteIO; - desc.componentManufacturer = kAudioUnitManufacturer_Apple; - desc.componentFlags = 0; - desc.componentFlagsMask = 0; - - component = ((ma_AudioComponentFindNext_proc)pContext->coreaudio.AudioComponentFindNext)(NULL, &desc); - if (component == NULL) { - return MA_FAILED_TO_INIT_BACKEND; - } - - status = ((ma_AudioComponentInstanceNew_proc)pContext->coreaudio.AudioComponentInstanceNew)(component, &audioUnit); - if (status != noErr) { - return ma_result_from_OSStatus(status); - } - - formatScope = (deviceType == ma_device_type_playback) ? kAudioUnitScope_Input : kAudioUnitScope_Output; - formatElement = (deviceType == ma_device_type_playback) ? MA_COREAUDIO_OUTPUT_BUS : MA_COREAUDIO_INPUT_BUS; - - propSize = sizeof(bestFormat); - status = ((ma_AudioUnitGetProperty_proc)pContext->coreaudio.AudioUnitGetProperty)(audioUnit, kAudioUnitProperty_StreamFormat, formatScope, formatElement, &bestFormat, &propSize); - if (status != noErr) { - ((ma_AudioComponentInstanceDispose_proc)pContext->coreaudio.AudioComponentInstanceDispose)(audioUnit); - return ma_result_from_OSStatus(status); - } - - ((ma_AudioComponentInstanceDispose_proc)pContext->coreaudio.AudioComponentInstanceDispose)(audioUnit); - audioUnit = NULL; - - /* Only a single format is being reported for iOS. */ - pDeviceInfo->nativeDataFormatCount = 1; - - result = ma_format_from_AudioStreamBasicDescription(&bestFormat, &pDeviceInfo->nativeDataFormats[0].format); - if (result != MA_SUCCESS) { - return result; - } - - pDeviceInfo->nativeDataFormats[0].channels = bestFormat.mChannelsPerFrame; - - /* - It looks like Apple are wanting to push the whole AVAudioSession thing. Thus, we need to use that to determine device settings. To do - this we just get the shared instance and inspect. - */ - @autoreleasepool { - AVAudioSession* pAudioSession = [AVAudioSession sharedInstance]; - MA_ASSERT(pAudioSession != NULL); - - pDeviceInfo->nativeDataFormats[0].sampleRate = (ma_uint32)pAudioSession.sampleRate; - } - } -#endif - - (void)pDeviceInfo; /* Unused. */ - return MA_SUCCESS; -} - -static AudioBufferList* ma_allocate_AudioBufferList__coreaudio(ma_uint32 sizeInFrames, ma_format format, ma_uint32 channels, ma_stream_layout layout, const ma_allocation_callbacks* pAllocationCallbacks) -{ - AudioBufferList* pBufferList; - UInt32 audioBufferSizeInBytes; - size_t allocationSize; - - MA_ASSERT(sizeInFrames > 0); - MA_ASSERT(format != ma_format_unknown); - MA_ASSERT(channels > 0); - - allocationSize = sizeof(AudioBufferList) - sizeof(AudioBuffer); /* Subtract sizeof(AudioBuffer) because that part is dynamically sized. */ - if (layout == ma_stream_layout_interleaved) { - /* Interleaved case. This is the simple case because we just have one buffer. */ - allocationSize += sizeof(AudioBuffer) * 1; - } else { - /* Non-interleaved case. This is the more complex case because there's more than one buffer. */ - allocationSize += sizeof(AudioBuffer) * channels; - } - - allocationSize += sizeInFrames * ma_get_bytes_per_frame(format, channels); - - pBufferList = (AudioBufferList*)ma_malloc(allocationSize, pAllocationCallbacks); - if (pBufferList == NULL) { - return NULL; - } - - audioBufferSizeInBytes = (UInt32)(sizeInFrames * ma_get_bytes_per_sample(format)); - - if (layout == ma_stream_layout_interleaved) { - pBufferList->mNumberBuffers = 1; - pBufferList->mBuffers[0].mNumberChannels = channels; - pBufferList->mBuffers[0].mDataByteSize = audioBufferSizeInBytes * channels; - pBufferList->mBuffers[0].mData = (ma_uint8*)pBufferList + sizeof(AudioBufferList); - } else { - ma_uint32 iBuffer; - pBufferList->mNumberBuffers = channels; - for (iBuffer = 0; iBuffer < pBufferList->mNumberBuffers; ++iBuffer) { - pBufferList->mBuffers[iBuffer].mNumberChannels = 1; - pBufferList->mBuffers[iBuffer].mDataByteSize = audioBufferSizeInBytes; - pBufferList->mBuffers[iBuffer].mData = (ma_uint8*)pBufferList + ((sizeof(AudioBufferList) - sizeof(AudioBuffer)) + (sizeof(AudioBuffer) * channels)) + (audioBufferSizeInBytes * iBuffer); - } - } - - return pBufferList; -} - -static ma_result ma_device_realloc_AudioBufferList__coreaudio(ma_device* pDevice, ma_uint32 sizeInFrames, ma_format format, ma_uint32 channels, ma_stream_layout layout) -{ - MA_ASSERT(pDevice != NULL); - MA_ASSERT(format != ma_format_unknown); - MA_ASSERT(channels > 0); - - /* Only resize the buffer if necessary. */ - if (pDevice->coreaudio.audioBufferCapInFrames < sizeInFrames) { - AudioBufferList* pNewAudioBufferList; - - pNewAudioBufferList = ma_allocate_AudioBufferList__coreaudio(sizeInFrames, format, channels, layout, &pDevice->pContext->allocationCallbacks); - if (pNewAudioBufferList == NULL) { - return MA_OUT_OF_MEMORY; - } - - /* At this point we'll have a new AudioBufferList and we can free the old one. */ - ma_free(pDevice->coreaudio.pAudioBufferList, &pDevice->pContext->allocationCallbacks); - pDevice->coreaudio.pAudioBufferList = pNewAudioBufferList; - pDevice->coreaudio.audioBufferCapInFrames = sizeInFrames; - } - - /* Getting here means the capacity of the audio is fine. */ - return MA_SUCCESS; -} - - -static OSStatus ma_on_output__coreaudio(void* pUserData, AudioUnitRenderActionFlags* pActionFlags, const AudioTimeStamp* pTimeStamp, UInt32 busNumber, UInt32 frameCount, AudioBufferList* pBufferList) -{ - ma_device* pDevice = (ma_device*)pUserData; - ma_stream_layout layout; - - MA_ASSERT(pDevice != NULL); - - /*ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "INFO: Output Callback: busNumber=%d, frameCount=%d, mNumberBuffers=%d\n", (int)busNumber, (int)frameCount, (int)pBufferList->mNumberBuffers);*/ - - /* We need to check whether or not we are outputting interleaved or non-interleaved samples. The way we do this is slightly different for each type. */ - layout = ma_stream_layout_interleaved; - if (pBufferList->mBuffers[0].mNumberChannels != pDevice->playback.internalChannels) { - layout = ma_stream_layout_deinterleaved; - } - - if (layout == ma_stream_layout_interleaved) { - /* For now we can assume everything is interleaved. */ - UInt32 iBuffer; - for (iBuffer = 0; iBuffer < pBufferList->mNumberBuffers; ++iBuffer) { - if (pBufferList->mBuffers[iBuffer].mNumberChannels == pDevice->playback.internalChannels) { - ma_uint32 frameCountForThisBuffer = pBufferList->mBuffers[iBuffer].mDataByteSize / ma_get_bytes_per_frame(pDevice->playback.internalFormat, pDevice->playback.internalChannels); - if (frameCountForThisBuffer > 0) { - ma_device_handle_backend_data_callback(pDevice, pBufferList->mBuffers[iBuffer].mData, NULL, frameCountForThisBuffer); - } - - /*a_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, " frameCount=%d, mNumberChannels=%d, mDataByteSize=%d\n", (int)frameCount, (int)pBufferList->mBuffers[iBuffer].mNumberChannels, (int)pBufferList->mBuffers[iBuffer].mDataByteSize);*/ - } else { - /* - This case is where the number of channels in the output buffer do not match our internal channels. It could mean that it's - not interleaved, in which case we can't handle right now since miniaudio does not yet support non-interleaved streams. We just - output silence here. - */ - MA_ZERO_MEMORY(pBufferList->mBuffers[iBuffer].mData, pBufferList->mBuffers[iBuffer].mDataByteSize); - /*ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, " WARNING: Outputting silence. frameCount=%d, mNumberChannels=%d, mDataByteSize=%d\n", (int)frameCount, (int)pBufferList->mBuffers[iBuffer].mNumberChannels, (int)pBufferList->mBuffers[iBuffer].mDataByteSize);*/ - } - } - } else { - /* This is the deinterleaved case. We need to update each buffer in groups of internalChannels. This assumes each buffer is the same size. */ - MA_ASSERT(pDevice->playback.internalChannels <= MA_MAX_CHANNELS); /* This should have been validated at initialization time. */ - - /* - For safety we'll check that the internal channels is a multiple of the buffer count. If it's not it means something - very strange has happened and we're not going to support it. - */ - if ((pBufferList->mNumberBuffers % pDevice->playback.internalChannels) == 0) { - ma_uint8 tempBuffer[4096]; - UInt32 iBuffer; - - for (iBuffer = 0; iBuffer < pBufferList->mNumberBuffers; iBuffer += pDevice->playback.internalChannels) { - ma_uint32 frameCountPerBuffer = pBufferList->mBuffers[iBuffer].mDataByteSize / ma_get_bytes_per_sample(pDevice->playback.internalFormat); - ma_uint32 framesRemaining = frameCountPerBuffer; - - while (framesRemaining > 0) { - void* ppDeinterleavedBuffers[MA_MAX_CHANNELS]; - ma_uint32 iChannel; - ma_uint32 framesToRead = sizeof(tempBuffer) / ma_get_bytes_per_frame(pDevice->playback.internalFormat, pDevice->playback.internalChannels); - if (framesToRead > framesRemaining) { - framesToRead = framesRemaining; - } - - ma_device_handle_backend_data_callback(pDevice, tempBuffer, NULL, framesToRead); - - for (iChannel = 0; iChannel < pDevice->playback.internalChannels; ++iChannel) { - ppDeinterleavedBuffers[iChannel] = (void*)ma_offset_ptr(pBufferList->mBuffers[iBuffer+iChannel].mData, (frameCountPerBuffer - framesRemaining) * ma_get_bytes_per_sample(pDevice->playback.internalFormat)); - } - - ma_deinterleave_pcm_frames(pDevice->playback.internalFormat, pDevice->playback.internalChannels, framesToRead, tempBuffer, ppDeinterleavedBuffers); - - framesRemaining -= framesToRead; - } - } - } - } - - (void)pActionFlags; - (void)pTimeStamp; - (void)busNumber; - (void)frameCount; - - return noErr; -} - -static OSStatus ma_on_input__coreaudio(void* pUserData, AudioUnitRenderActionFlags* pActionFlags, const AudioTimeStamp* pTimeStamp, UInt32 busNumber, UInt32 frameCount, AudioBufferList* pUnusedBufferList) -{ - ma_device* pDevice = (ma_device*)pUserData; - AudioBufferList* pRenderedBufferList; - ma_result result; - ma_stream_layout layout; - ma_uint32 iBuffer; - OSStatus status; - - MA_ASSERT(pDevice != NULL); - - pRenderedBufferList = (AudioBufferList*)pDevice->coreaudio.pAudioBufferList; - MA_ASSERT(pRenderedBufferList); - - /* We need to check whether or not we are outputting interleaved or non-interleaved samples. The way we do this is slightly different for each type. */ - layout = ma_stream_layout_interleaved; - if (pRenderedBufferList->mBuffers[0].mNumberChannels != pDevice->capture.internalChannels) { - layout = ma_stream_layout_deinterleaved; - } - - /*ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "INFO: Input Callback: busNumber=%d, frameCount=%d, mNumberBuffers=%d\n", (int)busNumber, (int)frameCount, (int)pRenderedBufferList->mNumberBuffers);*/ - - /* - There has been a situation reported where frame count passed into this function is greater than the capacity of - our capture buffer. There doesn't seem to be a reliable way to determine what the maximum frame count will be, - so we need to instead resort to dynamically reallocating our buffer to ensure it's large enough to capture the - number of frames requested by this callback. - */ - result = ma_device_realloc_AudioBufferList__coreaudio(pDevice, frameCount, pDevice->capture.internalFormat, pDevice->capture.internalChannels, layout); - if (result != MA_SUCCESS) { - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "Failed to allocate AudioBufferList for capture.\n"); - return noErr; - } - - pRenderedBufferList = (AudioBufferList*)pDevice->coreaudio.pAudioBufferList; - MA_ASSERT(pRenderedBufferList); - - /* - When you call AudioUnitRender(), Core Audio tries to be helpful by setting the mDataByteSize to the number of bytes - that were actually rendered. The problem with this is that the next call can fail with -50 due to the size no longer - being set to the capacity of the buffer, but instead the size in bytes of the previous render. This will cause a - problem when a future call to this callback specifies a larger number of frames. - - To work around this we need to explicitly set the size of each buffer to their respective size in bytes. - */ - for (iBuffer = 0; iBuffer < pRenderedBufferList->mNumberBuffers; ++iBuffer) { - pRenderedBufferList->mBuffers[iBuffer].mDataByteSize = pDevice->coreaudio.audioBufferCapInFrames * ma_get_bytes_per_sample(pDevice->capture.internalFormat) * pRenderedBufferList->mBuffers[iBuffer].mNumberChannels; - /*printf("DEBUG: nDataByteSize = %d\n", (int)pRenderedBufferList->mBuffers[iBuffer].mDataByteSize);*/ - } - - status = ((ma_AudioUnitRender_proc)pDevice->pContext->coreaudio.AudioUnitRender)((AudioUnit)pDevice->coreaudio.audioUnitCapture, pActionFlags, pTimeStamp, busNumber, frameCount, pRenderedBufferList); - if (status != noErr) { - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "ERROR: AudioUnitRender() failed with %d.\n", (int)status); - return status; - } - - if (layout == ma_stream_layout_interleaved) { - for (iBuffer = 0; iBuffer < pRenderedBufferList->mNumberBuffers; ++iBuffer) { - if (pRenderedBufferList->mBuffers[iBuffer].mNumberChannels == pDevice->capture.internalChannels) { - ma_device_handle_backend_data_callback(pDevice, NULL, pRenderedBufferList->mBuffers[iBuffer].mData, frameCount); - /*ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, " mDataByteSize=%d.\n", (int)pRenderedBufferList->mBuffers[iBuffer].mDataByteSize);*/ - } else { - /* - This case is where the number of channels in the output buffer do not match our internal channels. It could mean that it's - not interleaved, in which case we can't handle right now since miniaudio does not yet support non-interleaved streams. - */ - ma_uint8 silentBuffer[4096]; - ma_uint32 framesRemaining; - - MA_ZERO_MEMORY(silentBuffer, sizeof(silentBuffer)); - - framesRemaining = frameCount; - while (framesRemaining > 0) { - ma_uint32 framesToSend = sizeof(silentBuffer) / ma_get_bytes_per_frame(pDevice->capture.internalFormat, pDevice->capture.internalChannels); - if (framesToSend > framesRemaining) { - framesToSend = framesRemaining; - } - - ma_device_handle_backend_data_callback(pDevice, NULL, silentBuffer, framesToSend); - - framesRemaining -= framesToSend; - } - - /*ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, " WARNING: Outputting silence. frameCount=%d, mNumberChannels=%d, mDataByteSize=%d\n", (int)frameCount, (int)pRenderedBufferList->mBuffers[iBuffer].mNumberChannels, (int)pRenderedBufferList->mBuffers[iBuffer].mDataByteSize);*/ - } - } - } else { - /* This is the deinterleaved case. We need to interleave the audio data before sending it to the client. This assumes each buffer is the same size. */ - MA_ASSERT(pDevice->capture.internalChannels <= MA_MAX_CHANNELS); /* This should have been validated at initialization time. */ - - /* - For safety we'll check that the internal channels is a multiple of the buffer count. If it's not it means something - very strange has happened and we're not going to support it. - */ - if ((pRenderedBufferList->mNumberBuffers % pDevice->capture.internalChannels) == 0) { - ma_uint8 tempBuffer[4096]; - for (iBuffer = 0; iBuffer < pRenderedBufferList->mNumberBuffers; iBuffer += pDevice->capture.internalChannels) { - ma_uint32 framesRemaining = frameCount; - while (framesRemaining > 0) { - void* ppDeinterleavedBuffers[MA_MAX_CHANNELS]; - ma_uint32 iChannel; - ma_uint32 framesToSend = sizeof(tempBuffer) / ma_get_bytes_per_frame(pDevice->capture.internalFormat, pDevice->capture.internalChannels); - if (framesToSend > framesRemaining) { - framesToSend = framesRemaining; - } - - for (iChannel = 0; iChannel < pDevice->capture.internalChannels; ++iChannel) { - ppDeinterleavedBuffers[iChannel] = (void*)ma_offset_ptr(pRenderedBufferList->mBuffers[iBuffer+iChannel].mData, (frameCount - framesRemaining) * ma_get_bytes_per_sample(pDevice->capture.internalFormat)); - } - - ma_interleave_pcm_frames(pDevice->capture.internalFormat, pDevice->capture.internalChannels, framesToSend, (const void**)ppDeinterleavedBuffers, tempBuffer); - ma_device_handle_backend_data_callback(pDevice, NULL, tempBuffer, framesToSend); - - framesRemaining -= framesToSend; - } - } - } - } - - (void)pActionFlags; - (void)pTimeStamp; - (void)busNumber; - (void)frameCount; - (void)pUnusedBufferList; - - return noErr; -} - -static void on_start_stop__coreaudio(void* pUserData, AudioUnit audioUnit, AudioUnitPropertyID propertyID, AudioUnitScope scope, AudioUnitElement element) -{ - ma_device* pDevice = (ma_device*)pUserData; - MA_ASSERT(pDevice != NULL); - - /* Don't do anything if it looks like we're just reinitializing due to a device switch. */ - if (((audioUnit == pDevice->coreaudio.audioUnitPlayback) && pDevice->coreaudio.isSwitchingPlaybackDevice) || - ((audioUnit == pDevice->coreaudio.audioUnitCapture) && pDevice->coreaudio.isSwitchingCaptureDevice)) { - return; - } - - /* - There's been a report of a deadlock here when triggered by ma_device_uninit(). It looks like - AudioUnitGetProprty (called below) and AudioComponentInstanceDispose (called in ma_device_uninit) - can try waiting on the same lock. I'm going to try working around this by not calling any Core - Audio APIs in the callback when the device has been stopped or uninitialized. - */ - if (ma_device_get_state(pDevice) == ma_device_state_uninitialized || ma_device_get_state(pDevice) == ma_device_state_stopping || ma_device_get_state(pDevice) == ma_device_state_stopped) { - ma_device__on_notification_stopped(pDevice); - } else { - UInt32 isRunning; - UInt32 isRunningSize = sizeof(isRunning); - OSStatus status = ((ma_AudioUnitGetProperty_proc)pDevice->pContext->coreaudio.AudioUnitGetProperty)(audioUnit, kAudioOutputUnitProperty_IsRunning, scope, element, &isRunning, &isRunningSize); - if (status != noErr) { - goto done; /* Don't really know what to do in this case... just ignore it, I suppose... */ - } - - if (!isRunning) { - /* - The stop event is a bit annoying in Core Audio because it will be called when we automatically switch the default device. Some scenarios to consider: - - 1) When the device is unplugged, this will be called _before_ the default device change notification. - 2) When the device is changed via the default device change notification, this will be called _after_ the switch. - - For case #1, we just check if there's a new default device available. If so, we just ignore the stop event. For case #2 we check a flag. - */ - if (((audioUnit == pDevice->coreaudio.audioUnitPlayback) && pDevice->coreaudio.isDefaultPlaybackDevice) || - ((audioUnit == pDevice->coreaudio.audioUnitCapture) && pDevice->coreaudio.isDefaultCaptureDevice)) { - /* - It looks like the device is switching through an external event, such as the user unplugging the device or changing the default device - via the operating system's sound settings. If we're re-initializing the device, we just terminate because we want the stopping of the - device to be seamless to the client (we don't want them receiving the stopped event and thinking that the device has stopped when it - hasn't!). - */ - if (((audioUnit == pDevice->coreaudio.audioUnitPlayback) && pDevice->coreaudio.isSwitchingPlaybackDevice) || - ((audioUnit == pDevice->coreaudio.audioUnitCapture) && pDevice->coreaudio.isSwitchingCaptureDevice)) { - goto done; - } - - /* - Getting here means the device is not reinitializing which means it may have been unplugged. From what I can see, it looks like Core Audio - will try switching to the new default device seamlessly. We need to somehow find a way to determine whether or not Core Audio will most - likely be successful in switching to the new device. - - TODO: Try to predict if Core Audio will switch devices. If not, the stopped callback needs to be posted. - */ - goto done; - } - - /* Getting here means we need to stop the device. */ - ma_device__on_notification_stopped(pDevice); - } - } - - (void)propertyID; /* Unused. */ - -done: - /* Always signal the stop event. It's possible for the "else" case to get hit which can happen during an interruption. */ - ma_event_signal(&pDevice->coreaudio.stopEvent); -} - -#if defined(MA_APPLE_DESKTOP) -static ma_spinlock g_DeviceTrackingInitLock_CoreAudio = 0; /* A spinlock for mutal exclusion of the init/uninit of the global tracking data. Initialization to 0 is what we need. */ -static ma_uint32 g_DeviceTrackingInitCounter_CoreAudio = 0; -static ma_mutex g_DeviceTrackingMutex_CoreAudio; -static ma_device** g_ppTrackedDevices_CoreAudio = NULL; -static ma_uint32 g_TrackedDeviceCap_CoreAudio = 0; -static ma_uint32 g_TrackedDeviceCount_CoreAudio = 0; - -static OSStatus ma_default_device_changed__coreaudio(AudioObjectID objectID, UInt32 addressCount, const AudioObjectPropertyAddress* pAddresses, void* pUserData) -{ - ma_device_type deviceType; - - /* Not sure if I really need to check this, but it makes me feel better. */ - if (addressCount == 0) { - return noErr; - } - - if (pAddresses[0].mSelector == kAudioHardwarePropertyDefaultOutputDevice) { - deviceType = ma_device_type_playback; - } else if (pAddresses[0].mSelector == kAudioHardwarePropertyDefaultInputDevice) { - deviceType = ma_device_type_capture; - } else { - return noErr; /* Should never hit this. */ - } - - ma_mutex_lock(&g_DeviceTrackingMutex_CoreAudio); - { - ma_uint32 iDevice; - for (iDevice = 0; iDevice < g_TrackedDeviceCount_CoreAudio; iDevice += 1) { - ma_result reinitResult; - ma_device* pDevice; - - pDevice = g_ppTrackedDevices_CoreAudio[iDevice]; - if (pDevice->type == deviceType || pDevice->type == ma_device_type_duplex) { - if (deviceType == ma_device_type_playback) { - pDevice->coreaudio.isSwitchingPlaybackDevice = MA_TRUE; - reinitResult = ma_device_reinit_internal__coreaudio(pDevice, deviceType, MA_TRUE); - pDevice->coreaudio.isSwitchingPlaybackDevice = MA_FALSE; - } else { - pDevice->coreaudio.isSwitchingCaptureDevice = MA_TRUE; - reinitResult = ma_device_reinit_internal__coreaudio(pDevice, deviceType, MA_TRUE); - pDevice->coreaudio.isSwitchingCaptureDevice = MA_FALSE; - } - - if (reinitResult == MA_SUCCESS) { - ma_device__post_init_setup(pDevice, deviceType); - - /* Restart the device if required. If this fails we need to stop the device entirely. */ - if (ma_device_get_state(pDevice) == ma_device_state_started) { - OSStatus status; - if (deviceType == ma_device_type_playback) { - status = ((ma_AudioOutputUnitStart_proc)pDevice->pContext->coreaudio.AudioOutputUnitStart)((AudioUnit)pDevice->coreaudio.audioUnitPlayback); - if (status != noErr) { - if (pDevice->type == ma_device_type_duplex) { - ((ma_AudioOutputUnitStop_proc)pDevice->pContext->coreaudio.AudioOutputUnitStop)((AudioUnit)pDevice->coreaudio.audioUnitCapture); - } - ma_device__set_state(pDevice, ma_device_state_stopped); - } - } else if (deviceType == ma_device_type_capture) { - status = ((ma_AudioOutputUnitStart_proc)pDevice->pContext->coreaudio.AudioOutputUnitStart)((AudioUnit)pDevice->coreaudio.audioUnitCapture); - if (status != noErr) { - if (pDevice->type == ma_device_type_duplex) { - ((ma_AudioOutputUnitStop_proc)pDevice->pContext->coreaudio.AudioOutputUnitStop)((AudioUnit)pDevice->coreaudio.audioUnitPlayback); - } - ma_device__set_state(pDevice, ma_device_state_stopped); - } - } - } - - ma_device__on_notification_rerouted(pDevice); - } - } - } - } - ma_mutex_unlock(&g_DeviceTrackingMutex_CoreAudio); - - /* Unused parameters. */ - (void)objectID; - (void)pUserData; - - return noErr; -} - -static ma_result ma_context__init_device_tracking__coreaudio(ma_context* pContext) -{ - MA_ASSERT(pContext != NULL); - - ma_spinlock_lock(&g_DeviceTrackingInitLock_CoreAudio); - { - /* Don't do anything if we've already initialized device tracking. */ - if (g_DeviceTrackingInitCounter_CoreAudio == 0) { - AudioObjectPropertyAddress propAddress; - propAddress.mScope = kAudioObjectPropertyScopeGlobal; - propAddress.mElement = AUDIO_OBJECT_PROPERTY_ELEMENT; - - ma_mutex_init(&g_DeviceTrackingMutex_CoreAudio); - - propAddress.mSelector = kAudioHardwarePropertyDefaultInputDevice; - ((ma_AudioObjectAddPropertyListener_proc)pContext->coreaudio.AudioObjectAddPropertyListener)(kAudioObjectSystemObject, &propAddress, &ma_default_device_changed__coreaudio, NULL); - - propAddress.mSelector = kAudioHardwarePropertyDefaultOutputDevice; - ((ma_AudioObjectAddPropertyListener_proc)pContext->coreaudio.AudioObjectAddPropertyListener)(kAudioObjectSystemObject, &propAddress, &ma_default_device_changed__coreaudio, NULL); - - } - g_DeviceTrackingInitCounter_CoreAudio += 1; - } - ma_spinlock_unlock(&g_DeviceTrackingInitLock_CoreAudio); - - return MA_SUCCESS; -} - -static ma_result ma_context__uninit_device_tracking__coreaudio(ma_context* pContext) -{ - MA_ASSERT(pContext != NULL); - - ma_spinlock_lock(&g_DeviceTrackingInitLock_CoreAudio); - { - if (g_DeviceTrackingInitCounter_CoreAudio > 0) - g_DeviceTrackingInitCounter_CoreAudio -= 1; - - if (g_DeviceTrackingInitCounter_CoreAudio == 0) { - AudioObjectPropertyAddress propAddress; - propAddress.mScope = kAudioObjectPropertyScopeGlobal; - propAddress.mElement = AUDIO_OBJECT_PROPERTY_ELEMENT; - - propAddress.mSelector = kAudioHardwarePropertyDefaultInputDevice; - ((ma_AudioObjectRemovePropertyListener_proc)pContext->coreaudio.AudioObjectRemovePropertyListener)(kAudioObjectSystemObject, &propAddress, &ma_default_device_changed__coreaudio, NULL); - - propAddress.mSelector = kAudioHardwarePropertyDefaultOutputDevice; - ((ma_AudioObjectRemovePropertyListener_proc)pContext->coreaudio.AudioObjectRemovePropertyListener)(kAudioObjectSystemObject, &propAddress, &ma_default_device_changed__coreaudio, NULL); - - /* At this point there should be no tracked devices. If not there's an error somewhere. */ - if (g_ppTrackedDevices_CoreAudio != NULL) { - ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_WARNING, "You have uninitialized all contexts while an associated device is still active."); - ma_spinlock_unlock(&g_DeviceTrackingInitLock_CoreAudio); - return MA_INVALID_OPERATION; - } - - ma_mutex_uninit(&g_DeviceTrackingMutex_CoreAudio); - } - } - ma_spinlock_unlock(&g_DeviceTrackingInitLock_CoreAudio); - - return MA_SUCCESS; -} - -static ma_result ma_device__track__coreaudio(ma_device* pDevice) -{ - MA_ASSERT(pDevice != NULL); - - ma_mutex_lock(&g_DeviceTrackingMutex_CoreAudio); - { - /* Allocate memory if required. */ - if (g_TrackedDeviceCap_CoreAudio <= g_TrackedDeviceCount_CoreAudio) { - ma_uint32 newCap; - ma_device** ppNewDevices; - - newCap = g_TrackedDeviceCap_CoreAudio * 2; - if (newCap == 0) { - newCap = 1; - } - - ppNewDevices = (ma_device**)ma_realloc(g_ppTrackedDevices_CoreAudio, sizeof(*g_ppTrackedDevices_CoreAudio)*newCap, &pDevice->pContext->allocationCallbacks); - if (ppNewDevices == NULL) { - ma_mutex_unlock(&g_DeviceTrackingMutex_CoreAudio); - return MA_OUT_OF_MEMORY; - } - - g_ppTrackedDevices_CoreAudio = ppNewDevices; - g_TrackedDeviceCap_CoreAudio = newCap; - } - - g_ppTrackedDevices_CoreAudio[g_TrackedDeviceCount_CoreAudio] = pDevice; - g_TrackedDeviceCount_CoreAudio += 1; - } - ma_mutex_unlock(&g_DeviceTrackingMutex_CoreAudio); - - return MA_SUCCESS; -} - -static ma_result ma_device__untrack__coreaudio(ma_device* pDevice) -{ - MA_ASSERT(pDevice != NULL); - - ma_mutex_lock(&g_DeviceTrackingMutex_CoreAudio); - { - ma_uint32 iDevice; - for (iDevice = 0; iDevice < g_TrackedDeviceCount_CoreAudio; iDevice += 1) { - if (g_ppTrackedDevices_CoreAudio[iDevice] == pDevice) { - /* We've found the device. We now need to remove it from the list. */ - ma_uint32 jDevice; - for (jDevice = iDevice; jDevice < g_TrackedDeviceCount_CoreAudio-1; jDevice += 1) { - g_ppTrackedDevices_CoreAudio[jDevice] = g_ppTrackedDevices_CoreAudio[jDevice+1]; - } - - g_TrackedDeviceCount_CoreAudio -= 1; - - /* If there's nothing else in the list we need to free memory. */ - if (g_TrackedDeviceCount_CoreAudio == 0) { - ma_free(g_ppTrackedDevices_CoreAudio, &pDevice->pContext->allocationCallbacks); - g_ppTrackedDevices_CoreAudio = NULL; - g_TrackedDeviceCap_CoreAudio = 0; - } - - break; - } - } - } - ma_mutex_unlock(&g_DeviceTrackingMutex_CoreAudio); - - return MA_SUCCESS; -} -#endif - -#if defined(MA_APPLE_MOBILE) -@interface ma_ios_notification_handler:NSObject { - ma_device* m_pDevice; -} -@end - -@implementation ma_ios_notification_handler --(id)init:(ma_device*)pDevice -{ - self = [super init]; - m_pDevice = pDevice; - - /* For route changes. */ - [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(handle_route_change:) name:AVAudioSessionRouteChangeNotification object:[AVAudioSession sharedInstance]]; - - /* For interruptions. */ - [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(handle_interruption:) name:AVAudioSessionInterruptionNotification object:[AVAudioSession sharedInstance]]; - - return self; -} - --(void)dealloc -{ - [self remove_handler]; - - #if defined(__has_feature) - #if !__has_feature(objc_arc) - [super dealloc]; - #endif - #endif -} - --(void)remove_handler -{ - [[NSNotificationCenter defaultCenter] removeObserver:self name:AVAudioSessionRouteChangeNotification object:nil]; - [[NSNotificationCenter defaultCenter] removeObserver:self name:AVAudioSessionInterruptionNotification object:nil]; -} - --(void)handle_interruption:(NSNotification*)pNotification -{ - NSInteger type = [[[pNotification userInfo] objectForKey:AVAudioSessionInterruptionTypeKey] integerValue]; - switch (type) - { - case AVAudioSessionInterruptionTypeBegan: - { - ma_log_postf(ma_device_get_log(m_pDevice), MA_LOG_LEVEL_INFO, "[Core Audio] Interruption: AVAudioSessionInterruptionTypeBegan\n"); - - /* - Core Audio will have stopped the internal device automatically, but we need explicitly - stop it at a higher level to ensure miniaudio-specific state is updated for consistency. - */ - ma_device_stop(m_pDevice); - - /* - Fire the notification after the device has been stopped to ensure it's in the correct - state when the notification handler is invoked. - */ - ma_device__on_notification_interruption_began(m_pDevice); - } break; - - case AVAudioSessionInterruptionTypeEnded: - { - ma_log_postf(ma_device_get_log(m_pDevice), MA_LOG_LEVEL_INFO, "[Core Audio] Interruption: AVAudioSessionInterruptionTypeEnded\n"); - ma_device__on_notification_interruption_ended(m_pDevice); - } break; - } -} - --(void)handle_route_change:(NSNotification*)pNotification -{ - AVAudioSession* pSession = [AVAudioSession sharedInstance]; - - NSInteger reason = [[[pNotification userInfo] objectForKey:AVAudioSessionRouteChangeReasonKey] integerValue]; - switch (reason) - { - case AVAudioSessionRouteChangeReasonOldDeviceUnavailable: - { - ma_log_postf(ma_device_get_log(m_pDevice), MA_LOG_LEVEL_INFO, "[Core Audio] Route Changed: AVAudioSessionRouteChangeReasonOldDeviceUnavailable\n"); - } break; - - case AVAudioSessionRouteChangeReasonNewDeviceAvailable: - { - ma_log_postf(ma_device_get_log(m_pDevice), MA_LOG_LEVEL_INFO, "[Core Audio] Route Changed: AVAudioSessionRouteChangeReasonNewDeviceAvailable\n"); - } break; - - case AVAudioSessionRouteChangeReasonNoSuitableRouteForCategory: - { - ma_log_postf(ma_device_get_log(m_pDevice), MA_LOG_LEVEL_INFO, "[Core Audio] Route Changed: AVAudioSessionRouteChangeReasonNoSuitableRouteForCategory\n"); - } break; - - case AVAudioSessionRouteChangeReasonWakeFromSleep: - { - ma_log_postf(ma_device_get_log(m_pDevice), MA_LOG_LEVEL_INFO, "[Core Audio] Route Changed: AVAudioSessionRouteChangeReasonWakeFromSleep\n"); - } break; - - case AVAudioSessionRouteChangeReasonOverride: - { - ma_log_postf(ma_device_get_log(m_pDevice), MA_LOG_LEVEL_INFO, "[Core Audio] Route Changed: AVAudioSessionRouteChangeReasonOverride\n"); - } break; - - case AVAudioSessionRouteChangeReasonCategoryChange: - { - ma_log_postf(ma_device_get_log(m_pDevice), MA_LOG_LEVEL_INFO, "[Core Audio] Route Changed: AVAudioSessionRouteChangeReasonCategoryChange\n"); - } break; - - case AVAudioSessionRouteChangeReasonUnknown: - default: - { - ma_log_postf(ma_device_get_log(m_pDevice), MA_LOG_LEVEL_INFO, "[Core Audio] Route Changed: AVAudioSessionRouteChangeReasonUnknown\n"); - } break; - } - - ma_log_postf(ma_device_get_log(m_pDevice), MA_LOG_LEVEL_DEBUG, "[Core Audio] Changing Route. inputNumberChannels=%d; outputNumberOfChannels=%d\n", (int)pSession.inputNumberOfChannels, (int)pSession.outputNumberOfChannels); - - /* Let the application know about the route change. */ - ma_device__on_notification_rerouted(m_pDevice); -} -@end -#endif - -static ma_result ma_device_uninit__coreaudio(ma_device* pDevice) -{ - MA_ASSERT(pDevice != NULL); - MA_ASSERT(ma_device_get_state(pDevice) == ma_device_state_uninitialized); - -#if defined(MA_APPLE_DESKTOP) - /* - Make sure we're no longer tracking the device. It doesn't matter if we call this for a non-default device because it'll - just gracefully ignore it. - */ - ma_device__untrack__coreaudio(pDevice); -#endif -#if defined(MA_APPLE_MOBILE) - if (pDevice->coreaudio.pNotificationHandler != NULL) { - ma_ios_notification_handler* pNotificationHandler = (MA_BRIDGE_TRANSFER ma_ios_notification_handler*)pDevice->coreaudio.pNotificationHandler; - [pNotificationHandler remove_handler]; - } -#endif - - if (pDevice->coreaudio.audioUnitCapture != NULL) { - ((ma_AudioComponentInstanceDispose_proc)pDevice->pContext->coreaudio.AudioComponentInstanceDispose)((AudioUnit)pDevice->coreaudio.audioUnitCapture); - } - if (pDevice->coreaudio.audioUnitPlayback != NULL) { - ((ma_AudioComponentInstanceDispose_proc)pDevice->pContext->coreaudio.AudioComponentInstanceDispose)((AudioUnit)pDevice->coreaudio.audioUnitPlayback); - } - - if (pDevice->coreaudio.pAudioBufferList) { - ma_free(pDevice->coreaudio.pAudioBufferList, &pDevice->pContext->allocationCallbacks); - } - - return MA_SUCCESS; -} - -typedef struct -{ - ma_bool32 allowNominalSampleRateChange; - - /* Input. */ - ma_format formatIn; - ma_uint32 channelsIn; - ma_uint32 sampleRateIn; - ma_channel channelMapIn[MA_MAX_CHANNELS]; - ma_uint32 periodSizeInFramesIn; - ma_uint32 periodSizeInMillisecondsIn; - ma_uint32 periodsIn; - ma_share_mode shareMode; - ma_performance_profile performanceProfile; - ma_bool32 registerStopEvent; - - /* Output. */ -#if defined(MA_APPLE_DESKTOP) - AudioObjectID deviceObjectID; -#endif - AudioComponent component; - AudioUnit audioUnit; - AudioBufferList* pAudioBufferList; /* Only used for input devices. */ - ma_format formatOut; - ma_uint32 channelsOut; - ma_uint32 sampleRateOut; - ma_channel channelMapOut[MA_MAX_CHANNELS]; - ma_uint32 periodSizeInFramesOut; - ma_uint32 periodsOut; - char deviceName[256]; -} ma_device_init_internal_data__coreaudio; - -static ma_result ma_device_init_internal__coreaudio(ma_context* pContext, ma_device_type deviceType, const ma_device_id* pDeviceID, ma_device_init_internal_data__coreaudio* pData, void* pDevice_DoNotReference) /* <-- pDevice is typed as void* intentionally so as to avoid accidentally referencing it. */ -{ - ma_result result = MA_SUCCESS; - OSStatus status; - UInt32 enableIOFlag; - AudioStreamBasicDescription bestFormat; - ma_uint32 actualPeriodSizeInFrames; - AURenderCallbackStruct callbackInfo; -#if defined(MA_APPLE_DESKTOP) - AudioObjectID deviceObjectID; -#endif - - /* This API should only be used for a single device type: playback or capture. No full-duplex mode. */ - if (deviceType == ma_device_type_duplex) { - return MA_INVALID_ARGS; - } - - MA_ASSERT(pContext != NULL); - MA_ASSERT(deviceType == ma_device_type_playback || deviceType == ma_device_type_capture); - -#if defined(MA_APPLE_DESKTOP) - pData->deviceObjectID = 0; -#endif - pData->component = NULL; - pData->audioUnit = NULL; - pData->pAudioBufferList = NULL; - -#if defined(MA_APPLE_DESKTOP) - result = ma_find_AudioObjectID(pContext, deviceType, pDeviceID, &deviceObjectID); - if (result != MA_SUCCESS) { - return result; - } - - pData->deviceObjectID = deviceObjectID; -#endif - - /* Core audio doesn't really use the notion of a period so we can leave this unmodified, but not too over the top. */ - pData->periodsOut = pData->periodsIn; - if (pData->periodsOut == 0) { - pData->periodsOut = MA_DEFAULT_PERIODS; - } - if (pData->periodsOut > 16) { - pData->periodsOut = 16; - } - - - /* Audio unit. */ - status = ((ma_AudioComponentInstanceNew_proc)pContext->coreaudio.AudioComponentInstanceNew)((AudioComponent)pContext->coreaudio.component, (AudioUnit*)&pData->audioUnit); - if (status != noErr) { - return ma_result_from_OSStatus(status); - } - - - /* The input/output buses need to be explicitly enabled and disabled. We set the flag based on the output unit first, then we just swap it for input. */ - enableIOFlag = 1; - if (deviceType == ma_device_type_capture) { - enableIOFlag = 0; - } - - status = ((ma_AudioUnitSetProperty_proc)pContext->coreaudio.AudioUnitSetProperty)(pData->audioUnit, kAudioOutputUnitProperty_EnableIO, kAudioUnitScope_Output, MA_COREAUDIO_OUTPUT_BUS, &enableIOFlag, sizeof(enableIOFlag)); - if (status != noErr) { - ((ma_AudioComponentInstanceDispose_proc)pContext->coreaudio.AudioComponentInstanceDispose)(pData->audioUnit); - return ma_result_from_OSStatus(status); - } - - enableIOFlag = (enableIOFlag == 0) ? 1 : 0; - status = ((ma_AudioUnitSetProperty_proc)pContext->coreaudio.AudioUnitSetProperty)(pData->audioUnit, kAudioOutputUnitProperty_EnableIO, kAudioUnitScope_Input, MA_COREAUDIO_INPUT_BUS, &enableIOFlag, sizeof(enableIOFlag)); - if (status != noErr) { - ((ma_AudioComponentInstanceDispose_proc)pContext->coreaudio.AudioComponentInstanceDispose)(pData->audioUnit); - return ma_result_from_OSStatus(status); - } - - - /* Set the device to use with this audio unit. This is only used on desktop since we are using defaults on mobile. */ -#if defined(MA_APPLE_DESKTOP) - status = ((ma_AudioUnitSetProperty_proc)pContext->coreaudio.AudioUnitSetProperty)(pData->audioUnit, kAudioOutputUnitProperty_CurrentDevice, kAudioUnitScope_Global, 0, &deviceObjectID, sizeof(deviceObjectID)); - if (status != noErr) { - ((ma_AudioComponentInstanceDispose_proc)pContext->coreaudio.AudioComponentInstanceDispose)(pData->audioUnit); - return ma_result_from_OSStatus(result); - } -#else - /* - For some reason it looks like Apple is only allowing selection of the input device. There does not appear to be any way to change - the default output route. I have no idea why this is like this, but for now we'll only be able to configure capture devices. - */ - if (pDeviceID != NULL) { - if (deviceType == ma_device_type_capture) { - ma_bool32 found = MA_FALSE; - NSArray *pInputs = [[[AVAudioSession sharedInstance] currentRoute] inputs]; - for (AVAudioSessionPortDescription* pPortDesc in pInputs) { - if (strcmp(pDeviceID->coreaudio, [pPortDesc.UID UTF8String]) == 0) { - [[AVAudioSession sharedInstance] setPreferredInput:pPortDesc error:nil]; - found = MA_TRUE; - break; - } - } - - if (found == MA_FALSE) { - return MA_DOES_NOT_EXIST; - } - } - } -#endif - - /* - Format. This is the hardest part of initialization because there's a few variables to take into account. - 1) The format must be supported by the device. - 2) The format must be supported miniaudio. - 3) There's a priority that miniaudio prefers. - - Ideally we would like to use a format that's as close to the hardware as possible so we can get as close to a passthrough as possible. The - most important property is the sample rate. miniaudio can do format conversion for any sample rate and channel count, but cannot do the same - for the sample data format. If the sample data format is not supported by miniaudio it must be ignored completely. - - On mobile platforms this is a bit different. We just force the use of whatever the audio unit's current format is set to. - */ - { - AudioStreamBasicDescription origFormat; - UInt32 origFormatSize = sizeof(origFormat); - AudioUnitScope formatScope = (deviceType == ma_device_type_playback) ? kAudioUnitScope_Input : kAudioUnitScope_Output; - AudioUnitElement formatElement = (deviceType == ma_device_type_playback) ? MA_COREAUDIO_OUTPUT_BUS : MA_COREAUDIO_INPUT_BUS; - - if (deviceType == ma_device_type_playback) { - status = ((ma_AudioUnitGetProperty_proc)pContext->coreaudio.AudioUnitGetProperty)(pData->audioUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Output, MA_COREAUDIO_OUTPUT_BUS, &origFormat, &origFormatSize); - } else { - status = ((ma_AudioUnitGetProperty_proc)pContext->coreaudio.AudioUnitGetProperty)(pData->audioUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Input, MA_COREAUDIO_INPUT_BUS, &origFormat, &origFormatSize); - } - if (status != noErr) { - ((ma_AudioComponentInstanceDispose_proc)pContext->coreaudio.AudioComponentInstanceDispose)(pData->audioUnit); - return ma_result_from_OSStatus(status); - } - - #if defined(MA_APPLE_DESKTOP) - result = ma_find_best_format__coreaudio(pContext, deviceObjectID, deviceType, pData->formatIn, pData->channelsIn, pData->sampleRateIn, &origFormat, &bestFormat); - if (result != MA_SUCCESS) { - ((ma_AudioComponentInstanceDispose_proc)pContext->coreaudio.AudioComponentInstanceDispose)(pData->audioUnit); - return result; - } - - /* - Technical Note TN2091: Device input using the HAL Output Audio Unit - https://developer.apple.com/library/archive/technotes/tn2091/_index.html - - This documentation says the following: - - The internal AudioConverter can handle any *simple* conversion. Typically, this means that a client can specify ANY - variant of the PCM formats. Consequently, the device's sample rate should match the desired sample rate. If sample rate - conversion is needed, it can be accomplished by buffering the input and converting the data on a separate thread with - another AudioConverter. - - The important part here is the mention that it can handle *simple* conversions, which does *not* include sample rate. We - therefore want to ensure the sample rate stays consistent. This document is specifically for input, but I'm going to play it - safe and apply the same rule to output as well. - - I have tried going against the documentation by setting the sample rate anyway, but this just results in AudioUnitRender() - returning a result code of -10863. I have also tried changing the format directly on the input scope on the input bus, but - this just results in `ca_require: IsStreamFormatWritable(inScope, inElement) NotWritable` when trying to set the format. - - Something that does seem to work, however, has been setting the nominal sample rate on the device object. The problem with - this, however, is that it actually changes the sample rate at the operating system level and not just the application. This - could be intrusive to the user, however, so I don't think it's wise to make this the default. Instead I'm making this a - configuration option. When the `coreaudio.allowNominalSampleRateChange` config option is set to true, changing the sample - rate will be allowed. Otherwise it'll be fixed to the current sample rate. To check the system-defined sample rate, run - the Audio MIDI Setup program that comes installed on macOS and observe how the sample rate changes as the sample rate is - changed by miniaudio. - */ - if (pData->allowNominalSampleRateChange) { - AudioValueRange sampleRateRange; - AudioObjectPropertyAddress propAddress; - - sampleRateRange.mMinimum = bestFormat.mSampleRate; - sampleRateRange.mMaximum = bestFormat.mSampleRate; - - propAddress.mSelector = kAudioDevicePropertyNominalSampleRate; - propAddress.mScope = (deviceType == ma_device_type_playback) ? kAudioObjectPropertyScopeOutput : kAudioObjectPropertyScopeInput; - propAddress.mElement = AUDIO_OBJECT_PROPERTY_ELEMENT; - - status = ((ma_AudioObjectSetPropertyData_proc)pContext->coreaudio.AudioObjectSetPropertyData)(deviceObjectID, &propAddress, 0, NULL, sizeof(sampleRateRange), &sampleRateRange); - if (status != noErr) { - bestFormat.mSampleRate = origFormat.mSampleRate; - } - } else { - bestFormat.mSampleRate = origFormat.mSampleRate; - } - - status = ((ma_AudioUnitSetProperty_proc)pContext->coreaudio.AudioUnitSetProperty)(pData->audioUnit, kAudioUnitProperty_StreamFormat, formatScope, formatElement, &bestFormat, sizeof(bestFormat)); - if (status != noErr) { - /* We failed to set the format, so fall back to the current format of the audio unit. */ - bestFormat = origFormat; - } - #else - bestFormat = origFormat; - - /* - Sample rate is a little different here because for some reason kAudioUnitProperty_StreamFormat returns 0... Oh well. We need to instead try - setting the sample rate to what the user has requested and then just see the results of it. Need to use some Objective-C here for this since - it depends on Apple's AVAudioSession API. To do this we just get the shared AVAudioSession instance and then set it. Note that from what I - can tell, it looks like the sample rate is shared between playback and capture for everything. - */ - @autoreleasepool { - AVAudioSession* pAudioSession = [AVAudioSession sharedInstance]; - MA_ASSERT(pAudioSession != NULL); - - [pAudioSession setPreferredSampleRate:(double)pData->sampleRateIn error:nil]; - bestFormat.mSampleRate = pAudioSession.sampleRate; - - /* - I've had a report that the channel count returned by AudioUnitGetProperty above is inconsistent with - AVAudioSession outputNumberOfChannels. I'm going to try using the AVAudioSession values instead. - - UPDATE 20/02/2025: - When testing on the simulator with an iPhone 15 and iOS 17 I get an error when initializing the audio - unit if set the input channels to pAudioSession.inputNumberOfChannels. What is happening is the channel - count returned from AudioUnitGetProperty() above is set to 2, but pAudioSession is reporting a channel - count of 1. When this happens, the call to AudioUnitSetProprty() below just down below will succeed, but - AudioUnitInitialize() further down will fail. The only solution I have come up with is to not set the - channel count to pAudioSession.inputNumberOfChannels. - */ - if (deviceType == ma_device_type_playback) { - bestFormat.mChannelsPerFrame = (UInt32)pAudioSession.outputNumberOfChannels; - } - - #if 0 - if (deviceType == ma_device_type_capture) { - /*printf("DEBUG: bestFormat.mChannelsPerFrame = %d; pAudioSession.inputNumberOfChannels = %d\n", (int)bestFormat.mChannelsPerFrame, (int)pAudioSession.inputNumberOfChannels);*/ - bestFormat.mChannelsPerFrame = (UInt32)pAudioSession.inputNumberOfChannels; - } - #endif - } - - - status = ((ma_AudioUnitSetProperty_proc)pContext->coreaudio.AudioUnitSetProperty)(pData->audioUnit, kAudioUnitProperty_StreamFormat, formatScope, formatElement, &bestFormat, sizeof(bestFormat)); - if (status != noErr) { - ((ma_AudioComponentInstanceDispose_proc)pContext->coreaudio.AudioComponentInstanceDispose)(pData->audioUnit); - return ma_result_from_OSStatus(status); - } - #endif - - result = ma_format_from_AudioStreamBasicDescription(&bestFormat, &pData->formatOut); - if (result != MA_SUCCESS) { - ((ma_AudioComponentInstanceDispose_proc)pContext->coreaudio.AudioComponentInstanceDispose)(pData->audioUnit); - return result; - } - - if (pData->formatOut == ma_format_unknown) { - ((ma_AudioComponentInstanceDispose_proc)pContext->coreaudio.AudioComponentInstanceDispose)(pData->audioUnit); - return MA_FORMAT_NOT_SUPPORTED; - } - - pData->channelsOut = bestFormat.mChannelsPerFrame; - pData->sampleRateOut = (ma_uint32)bestFormat.mSampleRate; - } - - /* Clamp the channel count for safety. */ - if (pData->channelsOut > MA_MAX_CHANNELS) { - pData->channelsOut = MA_MAX_CHANNELS; - } - - /* - Internal channel map. This is weird in my testing. If I use the AudioObject to get the - channel map, the channel descriptions are set to "Unknown" for some reason. To work around - this it looks like retrieving it from the AudioUnit will work. However, and this is where - it gets weird, it doesn't seem to work with capture devices, nor at all on iOS... Therefore - I'm going to fall back to a default assumption in these cases. - */ -#if defined(MA_APPLE_DESKTOP) - result = ma_get_AudioUnit_channel_map(pContext, pData->audioUnit, deviceType, pData->channelMapOut, pData->channelsOut); - if (result != MA_SUCCESS) { - #if 0 - /* Try falling back to the channel map from the AudioObject. */ - result = ma_get_AudioObject_channel_map(pContext, deviceObjectID, deviceType, pData->channelMapOut, pData->channelsOut); - if (result != MA_SUCCESS) { - return result; - } - #else - /* Fall back to default assumptions. */ - ma_channel_map_init_standard(ma_standard_channel_map_default, pData->channelMapOut, ma_countof(pData->channelMapOut), pData->channelsOut); - #endif - } -#else - /* TODO: Figure out how to get the channel map using AVAudioSession. */ - ma_channel_map_init_standard(ma_standard_channel_map_default, pData->channelMapOut, ma_countof(pData->channelMapOut), pData->channelsOut); -#endif - - - /* Buffer size. Not allowing this to be configurable on iOS. */ - if (pData->periodSizeInFramesIn == 0) { - if (pData->periodSizeInMillisecondsIn == 0) { - if (pData->performanceProfile == ma_performance_profile_low_latency) { - actualPeriodSizeInFrames = ma_calculate_buffer_size_in_frames_from_milliseconds(MA_DEFAULT_PERIOD_SIZE_IN_MILLISECONDS_LOW_LATENCY, pData->sampleRateOut); - } else { - actualPeriodSizeInFrames = ma_calculate_buffer_size_in_frames_from_milliseconds(MA_DEFAULT_PERIOD_SIZE_IN_MILLISECONDS_CONSERVATIVE, pData->sampleRateOut); - } - } else { - actualPeriodSizeInFrames = ma_calculate_buffer_size_in_frames_from_milliseconds(pData->periodSizeInMillisecondsIn, pData->sampleRateOut); - } - } else { - actualPeriodSizeInFrames = pData->periodSizeInFramesIn; - } - -#if defined(MA_APPLE_DESKTOP) - result = ma_set_AudioObject_buffer_size_in_frames(pContext, deviceObjectID, deviceType, &actualPeriodSizeInFrames); - if (result != MA_SUCCESS) { - return result; - } -#else - /* - On iOS, the size of the IO buffer needs to be specified in seconds and is a floating point - number. I don't trust any potential truncation errors due to converting from float to integer - so I'm going to explicitly set the actual period size to the next power of 2. - */ - @autoreleasepool { - AVAudioSession* pAudioSession = [AVAudioSession sharedInstance]; - MA_ASSERT(pAudioSession != NULL); - - [pAudioSession setPreferredIOBufferDuration:((float)actualPeriodSizeInFrames / pAudioSession.sampleRate) error:nil]; - actualPeriodSizeInFrames = ma_next_power_of_2((ma_uint32)(pAudioSession.IOBufferDuration * pAudioSession.sampleRate)); - } -#endif - - - /* - During testing I discovered that the buffer size can be too big. You'll get an error like this: - - kAudioUnitErr_TooManyFramesToProcess : inFramesToProcess=4096, mMaxFramesPerSlice=512 - - Note how inFramesToProcess is smaller than mMaxFramesPerSlice. To fix, we need to set kAudioUnitProperty_MaximumFramesPerSlice to that - of the size of our buffer, or do it the other way around and set our buffer size to the kAudioUnitProperty_MaximumFramesPerSlice. - */ - status = ((ma_AudioUnitSetProperty_proc)pContext->coreaudio.AudioUnitSetProperty)(pData->audioUnit, kAudioUnitProperty_MaximumFramesPerSlice, kAudioUnitScope_Global, 0, &actualPeriodSizeInFrames, sizeof(actualPeriodSizeInFrames)); - if (status != noErr) { - ((ma_AudioComponentInstanceDispose_proc)pContext->coreaudio.AudioComponentInstanceDispose)(pData->audioUnit); - return ma_result_from_OSStatus(status); - } - - pData->periodSizeInFramesOut = (ma_uint32)actualPeriodSizeInFrames; - - /* We need a buffer list if this is an input device. We render into this in the input callback. */ - if (deviceType == ma_device_type_capture) { - ma_bool32 isInterleaved = (bestFormat.mFormatFlags & kAudioFormatFlagIsNonInterleaved) == 0; - AudioBufferList* pBufferList; - - pBufferList = ma_allocate_AudioBufferList__coreaudio(pData->periodSizeInFramesOut, pData->formatOut, pData->channelsOut, (isInterleaved) ? ma_stream_layout_interleaved : ma_stream_layout_deinterleaved, &pContext->allocationCallbacks); - if (pBufferList == NULL) { - ((ma_AudioComponentInstanceDispose_proc)pContext->coreaudio.AudioComponentInstanceDispose)(pData->audioUnit); - return MA_OUT_OF_MEMORY; - } - - pData->pAudioBufferList = pBufferList; - } - - /* Callbacks. */ - callbackInfo.inputProcRefCon = pDevice_DoNotReference; - if (deviceType == ma_device_type_playback) { - callbackInfo.inputProc = ma_on_output__coreaudio; - status = ((ma_AudioUnitSetProperty_proc)pContext->coreaudio.AudioUnitSetProperty)(pData->audioUnit, kAudioUnitProperty_SetRenderCallback, kAudioUnitScope_Global, 0, &callbackInfo, sizeof(callbackInfo)); - if (status != noErr) { - ((ma_AudioComponentInstanceDispose_proc)pContext->coreaudio.AudioComponentInstanceDispose)(pData->audioUnit); - return ma_result_from_OSStatus(status); - } - } else { - callbackInfo.inputProc = ma_on_input__coreaudio; - status = ((ma_AudioUnitSetProperty_proc)pContext->coreaudio.AudioUnitSetProperty)(pData->audioUnit, kAudioOutputUnitProperty_SetInputCallback, kAudioUnitScope_Global, 0, &callbackInfo, sizeof(callbackInfo)); - if (status != noErr) { - ((ma_AudioComponentInstanceDispose_proc)pContext->coreaudio.AudioComponentInstanceDispose)(pData->audioUnit); - return ma_result_from_OSStatus(status); - } - } - - /* We need to listen for stop events. */ - if (pData->registerStopEvent) { - status = ((ma_AudioUnitAddPropertyListener_proc)pContext->coreaudio.AudioUnitAddPropertyListener)(pData->audioUnit, kAudioOutputUnitProperty_IsRunning, on_start_stop__coreaudio, pDevice_DoNotReference); - if (status != noErr) { - ((ma_AudioComponentInstanceDispose_proc)pContext->coreaudio.AudioComponentInstanceDispose)(pData->audioUnit); - return ma_result_from_OSStatus(status); - } - } - - /* Initialize the audio unit. */ - status = ((ma_AudioUnitInitialize_proc)pContext->coreaudio.AudioUnitInitialize)(pData->audioUnit); - if (status != noErr) { - ma_free(pData->pAudioBufferList, &pContext->allocationCallbacks); - pData->pAudioBufferList = NULL; - ((ma_AudioComponentInstanceDispose_proc)pContext->coreaudio.AudioComponentInstanceDispose)(pData->audioUnit); - return ma_result_from_OSStatus(status); - } - - /* Grab the name. */ -#if defined(MA_APPLE_DESKTOP) - ma_get_AudioObject_name(pContext, deviceObjectID, sizeof(pData->deviceName), pData->deviceName); -#else - if (deviceType == ma_device_type_playback) { - ma_strcpy_s(pData->deviceName, sizeof(pData->deviceName), MA_DEFAULT_PLAYBACK_DEVICE_NAME); - } else { - ma_strcpy_s(pData->deviceName, sizeof(pData->deviceName), MA_DEFAULT_CAPTURE_DEVICE_NAME); - } -#endif - - return result; -} - -#if defined(MA_APPLE_DESKTOP) -static ma_result ma_device_reinit_internal__coreaudio(ma_device* pDevice, ma_device_type deviceType, ma_bool32 disposePreviousAudioUnit) -{ - ma_device_init_internal_data__coreaudio data; - ma_result result; - - /* This should only be called for playback or capture, not duplex. */ - if (deviceType == ma_device_type_duplex) { - return MA_INVALID_ARGS; - } - - data.allowNominalSampleRateChange = MA_FALSE; /* Don't change the nominal sample rate when switching devices. */ - - if (deviceType == ma_device_type_capture) { - data.formatIn = pDevice->capture.format; - data.channelsIn = pDevice->capture.channels; - data.sampleRateIn = pDevice->sampleRate; - MA_COPY_MEMORY(data.channelMapIn, pDevice->capture.channelMap, sizeof(pDevice->capture.channelMap)); - data.shareMode = pDevice->capture.shareMode; - data.performanceProfile = pDevice->coreaudio.originalPerformanceProfile; - data.registerStopEvent = MA_TRUE; - - if (disposePreviousAudioUnit) { - ((ma_AudioOutputUnitStop_proc)pDevice->pContext->coreaudio.AudioOutputUnitStop)((AudioUnit)pDevice->coreaudio.audioUnitCapture); - ((ma_AudioComponentInstanceDispose_proc)pDevice->pContext->coreaudio.AudioComponentInstanceDispose)((AudioUnit)pDevice->coreaudio.audioUnitCapture); - } - if (pDevice->coreaudio.pAudioBufferList) { - ma_free(pDevice->coreaudio.pAudioBufferList, &pDevice->pContext->allocationCallbacks); - } - } else if (deviceType == ma_device_type_playback) { - data.formatIn = pDevice->playback.format; - data.channelsIn = pDevice->playback.channels; - data.sampleRateIn = pDevice->sampleRate; - MA_COPY_MEMORY(data.channelMapIn, pDevice->playback.channelMap, sizeof(pDevice->playback.channelMap)); - data.shareMode = pDevice->playback.shareMode; - data.performanceProfile = pDevice->coreaudio.originalPerformanceProfile; - data.registerStopEvent = (pDevice->type != ma_device_type_duplex); - - if (disposePreviousAudioUnit) { - ((ma_AudioOutputUnitStop_proc)pDevice->pContext->coreaudio.AudioOutputUnitStop)((AudioUnit)pDevice->coreaudio.audioUnitPlayback); - ((ma_AudioComponentInstanceDispose_proc)pDevice->pContext->coreaudio.AudioComponentInstanceDispose)((AudioUnit)pDevice->coreaudio.audioUnitPlayback); - } - } - data.periodSizeInFramesIn = pDevice->coreaudio.originalPeriodSizeInFrames; - data.periodSizeInMillisecondsIn = pDevice->coreaudio.originalPeriodSizeInMilliseconds; - data.periodsIn = pDevice->coreaudio.originalPeriods; - - /* Need at least 3 periods for duplex. */ - if (data.periodsIn < 3 && pDevice->type == ma_device_type_duplex) { - data.periodsIn = 3; - } - - result = ma_device_init_internal__coreaudio(pDevice->pContext, deviceType, NULL, &data, (void*)pDevice); - if (result != MA_SUCCESS) { - return result; - } - - if (deviceType == ma_device_type_capture) { - #if defined(MA_APPLE_DESKTOP) - pDevice->coreaudio.deviceObjectIDCapture = (ma_uint32)data.deviceObjectID; - ma_get_AudioObject_uid(pDevice->pContext, pDevice->coreaudio.deviceObjectIDCapture, sizeof(pDevice->capture.id.coreaudio), pDevice->capture.id.coreaudio); - #endif - pDevice->coreaudio.audioUnitCapture = (ma_ptr)data.audioUnit; - pDevice->coreaudio.pAudioBufferList = (ma_ptr)data.pAudioBufferList; - pDevice->coreaudio.audioBufferCapInFrames = data.periodSizeInFramesOut; - - pDevice->capture.internalFormat = data.formatOut; - pDevice->capture.internalChannels = data.channelsOut; - pDevice->capture.internalSampleRate = data.sampleRateOut; - MA_COPY_MEMORY(pDevice->capture.internalChannelMap, data.channelMapOut, sizeof(data.channelMapOut)); - pDevice->capture.internalPeriodSizeInFrames = data.periodSizeInFramesOut; - pDevice->capture.internalPeriods = data.periodsOut; - } else if (deviceType == ma_device_type_playback) { - #if defined(MA_APPLE_DESKTOP) - pDevice->coreaudio.deviceObjectIDPlayback = (ma_uint32)data.deviceObjectID; - ma_get_AudioObject_uid(pDevice->pContext, pDevice->coreaudio.deviceObjectIDPlayback, sizeof(pDevice->playback.id.coreaudio), pDevice->playback.id.coreaudio); - #endif - pDevice->coreaudio.audioUnitPlayback = (ma_ptr)data.audioUnit; - - pDevice->playback.internalFormat = data.formatOut; - pDevice->playback.internalChannels = data.channelsOut; - pDevice->playback.internalSampleRate = data.sampleRateOut; - MA_COPY_MEMORY(pDevice->playback.internalChannelMap, data.channelMapOut, sizeof(data.channelMapOut)); - pDevice->playback.internalPeriodSizeInFrames = data.periodSizeInFramesOut; - pDevice->playback.internalPeriods = data.periodsOut; - } - - return MA_SUCCESS; -} -#endif /* MA_APPLE_DESKTOP */ - -static ma_result ma_device_init__coreaudio(ma_device* pDevice, const ma_device_config* pConfig, ma_device_descriptor* pDescriptorPlayback, ma_device_descriptor* pDescriptorCapture) -{ - ma_result result; - - MA_ASSERT(pDevice != NULL); - MA_ASSERT(pConfig != NULL); - - if (pConfig->deviceType == ma_device_type_loopback) { - return MA_DEVICE_TYPE_NOT_SUPPORTED; - } - - /* No exclusive mode with the Core Audio backend for now. */ - if (((pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) && pDescriptorCapture->shareMode == ma_share_mode_exclusive) || - ((pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) && pDescriptorPlayback->shareMode == ma_share_mode_exclusive)) { - return MA_SHARE_MODE_NOT_SUPPORTED; - } - - /* Capture needs to be initialized first. */ - if (pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) { - ma_device_init_internal_data__coreaudio data; - data.allowNominalSampleRateChange = pConfig->coreaudio.allowNominalSampleRateChange; - data.formatIn = pDescriptorCapture->format; - data.channelsIn = pDescriptorCapture->channels; - data.sampleRateIn = pDescriptorCapture->sampleRate; - MA_COPY_MEMORY(data.channelMapIn, pDescriptorCapture->channelMap, sizeof(pDescriptorCapture->channelMap)); - data.periodSizeInFramesIn = pDescriptorCapture->periodSizeInFrames; - data.periodSizeInMillisecondsIn = pDescriptorCapture->periodSizeInMilliseconds; - data.periodsIn = pDescriptorCapture->periodCount; - data.shareMode = pDescriptorCapture->shareMode; - data.performanceProfile = pConfig->performanceProfile; - data.registerStopEvent = MA_TRUE; - - /* Need at least 3 periods for duplex. */ - if (data.periodsIn < 3 && pConfig->deviceType == ma_device_type_duplex) { - data.periodsIn = 3; - } - - result = ma_device_init_internal__coreaudio(pDevice->pContext, ma_device_type_capture, pDescriptorCapture->pDeviceID, &data, (void*)pDevice); - if (result != MA_SUCCESS) { - return result; - } - - pDevice->coreaudio.isDefaultCaptureDevice = (pConfig->capture.pDeviceID == NULL); - #if defined(MA_APPLE_DESKTOP) - pDevice->coreaudio.deviceObjectIDCapture = (ma_uint32)data.deviceObjectID; - #endif - pDevice->coreaudio.audioUnitCapture = (ma_ptr)data.audioUnit; - pDevice->coreaudio.pAudioBufferList = (ma_ptr)data.pAudioBufferList; - pDevice->coreaudio.audioBufferCapInFrames = data.periodSizeInFramesOut; - pDevice->coreaudio.originalPeriodSizeInFrames = pDescriptorCapture->periodSizeInFrames; - pDevice->coreaudio.originalPeriodSizeInMilliseconds = pDescriptorCapture->periodSizeInMilliseconds; - pDevice->coreaudio.originalPeriods = pDescriptorCapture->periodCount; - pDevice->coreaudio.originalPerformanceProfile = pConfig->performanceProfile; - - pDescriptorCapture->format = data.formatOut; - pDescriptorCapture->channels = data.channelsOut; - pDescriptorCapture->sampleRate = data.sampleRateOut; - MA_COPY_MEMORY(pDescriptorCapture->channelMap, data.channelMapOut, sizeof(data.channelMapOut)); - pDescriptorCapture->periodSizeInFrames = data.periodSizeInFramesOut; - pDescriptorCapture->periodCount = data.periodsOut; - - #if defined(MA_APPLE_DESKTOP) - ma_get_AudioObject_uid(pDevice->pContext, pDevice->coreaudio.deviceObjectIDCapture, sizeof(pDevice->capture.id.coreaudio), pDevice->capture.id.coreaudio); - - /* - If we are using the default device we'll need to listen for changes to the system's default device so we can seamlessly - switch the device in the background. - */ - if (pConfig->capture.pDeviceID == NULL) { - ma_device__track__coreaudio(pDevice); - } - #endif - } - - /* Playback. */ - if (pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) { - ma_device_init_internal_data__coreaudio data; - data.allowNominalSampleRateChange = pConfig->coreaudio.allowNominalSampleRateChange; - data.formatIn = pDescriptorPlayback->format; - data.channelsIn = pDescriptorPlayback->channels; - data.sampleRateIn = pDescriptorPlayback->sampleRate; - MA_COPY_MEMORY(data.channelMapIn, pDescriptorPlayback->channelMap, sizeof(pDescriptorPlayback->channelMap)); - data.shareMode = pDescriptorPlayback->shareMode; - data.performanceProfile = pConfig->performanceProfile; - - /* In full-duplex mode we want the playback buffer to be the same size as the capture buffer. */ - if (pConfig->deviceType == ma_device_type_duplex) { - data.periodSizeInFramesIn = pDescriptorCapture->periodSizeInFrames; - data.periodsIn = pDescriptorCapture->periodCount; - data.registerStopEvent = MA_FALSE; - } else { - data.periodSizeInFramesIn = pDescriptorPlayback->periodSizeInFrames; - data.periodSizeInMillisecondsIn = pDescriptorPlayback->periodSizeInMilliseconds; - data.periodsIn = pDescriptorPlayback->periodCount; - data.registerStopEvent = MA_TRUE; - } - - result = ma_device_init_internal__coreaudio(pDevice->pContext, ma_device_type_playback, pDescriptorPlayback->pDeviceID, &data, (void*)pDevice); - if (result != MA_SUCCESS) { - if (pConfig->deviceType == ma_device_type_duplex) { - ((ma_AudioComponentInstanceDispose_proc)pDevice->pContext->coreaudio.AudioComponentInstanceDispose)((AudioUnit)pDevice->coreaudio.audioUnitCapture); - if (pDevice->coreaudio.pAudioBufferList) { - ma_free(pDevice->coreaudio.pAudioBufferList, &pDevice->pContext->allocationCallbacks); - } - } - return result; - } - - pDevice->coreaudio.isDefaultPlaybackDevice = (pConfig->playback.pDeviceID == NULL); - #if defined(MA_APPLE_DESKTOP) - pDevice->coreaudio.deviceObjectIDPlayback = (ma_uint32)data.deviceObjectID; - #endif - pDevice->coreaudio.audioUnitPlayback = (ma_ptr)data.audioUnit; - pDevice->coreaudio.originalPeriodSizeInFrames = pDescriptorPlayback->periodSizeInFrames; - pDevice->coreaudio.originalPeriodSizeInMilliseconds = pDescriptorPlayback->periodSizeInMilliseconds; - pDevice->coreaudio.originalPeriods = pDescriptorPlayback->periodCount; - pDevice->coreaudio.originalPerformanceProfile = pConfig->performanceProfile; - - pDescriptorPlayback->format = data.formatOut; - pDescriptorPlayback->channels = data.channelsOut; - pDescriptorPlayback->sampleRate = data.sampleRateOut; - MA_COPY_MEMORY(pDescriptorPlayback->channelMap, data.channelMapOut, sizeof(data.channelMapOut)); - pDescriptorPlayback->periodSizeInFrames = data.periodSizeInFramesOut; - pDescriptorPlayback->periodCount = data.periodsOut; - - #if defined(MA_APPLE_DESKTOP) - ma_get_AudioObject_uid(pDevice->pContext, pDevice->coreaudio.deviceObjectIDPlayback, sizeof(pDevice->playback.id.coreaudio), pDevice->playback.id.coreaudio); - - /* - If we are using the default device we'll need to listen for changes to the system's default device so we can seamlessly - switch the device in the background. - */ - if (pDescriptorPlayback->pDeviceID == NULL && (pConfig->deviceType != ma_device_type_duplex || pDescriptorCapture->pDeviceID != NULL)) { - ma_device__track__coreaudio(pDevice); - } - #endif - } - - - - /* - When stopping the device, a callback is called on another thread. We need to wait for this callback - before returning from ma_device_stop(). This event is used for this. - */ - ma_event_init(&pDevice->coreaudio.stopEvent); - - /* - We need to detect when a route has changed so we can update the data conversion pipeline accordingly. This is done - differently on non-Desktop Apple platforms. - */ -#if defined(MA_APPLE_MOBILE) - pDevice->coreaudio.pNotificationHandler = (MA_BRIDGE_RETAINED void*)[[ma_ios_notification_handler alloc] init:pDevice]; -#endif - - return MA_SUCCESS; -} - - -static ma_result ma_device_start__coreaudio(ma_device* pDevice) -{ - MA_ASSERT(pDevice != NULL); - - if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) { - OSStatus status = ((ma_AudioOutputUnitStart_proc)pDevice->pContext->coreaudio.AudioOutputUnitStart)((AudioUnit)pDevice->coreaudio.audioUnitCapture); - if (status != noErr) { - return ma_result_from_OSStatus(status); - } - } - - if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) { - OSStatus status = ((ma_AudioOutputUnitStart_proc)pDevice->pContext->coreaudio.AudioOutputUnitStart)((AudioUnit)pDevice->coreaudio.audioUnitPlayback); - if (status != noErr) { - if (pDevice->type == ma_device_type_duplex) { - ((ma_AudioOutputUnitStop_proc)pDevice->pContext->coreaudio.AudioOutputUnitStop)((AudioUnit)pDevice->coreaudio.audioUnitCapture); - } - return ma_result_from_OSStatus(status); - } - } - - return MA_SUCCESS; -} - -static ma_result ma_device_stop__coreaudio(ma_device* pDevice) -{ - MA_ASSERT(pDevice != NULL); - - /* It's not clear from the documentation whether or not AudioOutputUnitStop() actually drains the device or not. */ - - if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) { - OSStatus status = ((ma_AudioOutputUnitStop_proc)pDevice->pContext->coreaudio.AudioOutputUnitStop)((AudioUnit)pDevice->coreaudio.audioUnitCapture); - if (status != noErr) { - return ma_result_from_OSStatus(status); - } - } - - if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) { - OSStatus status = ((ma_AudioOutputUnitStop_proc)pDevice->pContext->coreaudio.AudioOutputUnitStop)((AudioUnit)pDevice->coreaudio.audioUnitPlayback); - if (status != noErr) { - return ma_result_from_OSStatus(status); - } - } - - /* We need to wait for the callback to finish before returning. */ - ma_event_wait(&pDevice->coreaudio.stopEvent); - return MA_SUCCESS; -} - - -static ma_result ma_context_uninit__coreaudio(ma_context* pContext) -{ - MA_ASSERT(pContext != NULL); - MA_ASSERT(pContext->backend == ma_backend_coreaudio); - -#if defined(MA_APPLE_MOBILE) - if (!pContext->coreaudio.noAudioSessionDeactivate) { - if (![[AVAudioSession sharedInstance] setActive:false error:nil]) { - ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "Failed to deactivate audio session."); - return MA_FAILED_TO_INIT_BACKEND; - } - } -#endif - -#if !defined(MA_NO_RUNTIME_LINKING) && !defined(MA_APPLE_MOBILE) - ma_dlclose(ma_context_get_log(pContext), pContext->coreaudio.hAudioUnit); - ma_dlclose(ma_context_get_log(pContext), pContext->coreaudio.hCoreAudio); - ma_dlclose(ma_context_get_log(pContext), pContext->coreaudio.hCoreFoundation); -#endif - -#if !defined(MA_APPLE_MOBILE) - ma_context__uninit_device_tracking__coreaudio(pContext); -#endif - - (void)pContext; - return MA_SUCCESS; -} - -#if defined(MA_APPLE_MOBILE) && defined(__IPHONE_12_0) -static AVAudioSessionCategory ma_to_AVAudioSessionCategory(ma_ios_session_category category) -{ - /* The "default" and "none" categories are treated different and should not be used as an input into this function. */ - MA_ASSERT(category != ma_ios_session_category_default); - MA_ASSERT(category != ma_ios_session_category_none); - - switch (category) { - case ma_ios_session_category_ambient: return AVAudioSessionCategoryAmbient; - case ma_ios_session_category_solo_ambient: return AVAudioSessionCategorySoloAmbient; - case ma_ios_session_category_playback: return AVAudioSessionCategoryPlayback; - case ma_ios_session_category_record: return AVAudioSessionCategoryRecord; - case ma_ios_session_category_play_and_record: return AVAudioSessionCategoryPlayAndRecord; - case ma_ios_session_category_multi_route: return AVAudioSessionCategoryMultiRoute; - case ma_ios_session_category_none: return AVAudioSessionCategoryAmbient; - case ma_ios_session_category_default: return AVAudioSessionCategoryAmbient; - default: return AVAudioSessionCategoryAmbient; - } -} -#endif - -static ma_result ma_context_init__coreaudio(ma_context* pContext, const ma_context_config* pConfig, ma_backend_callbacks* pCallbacks) -{ -#if !defined(MA_APPLE_MOBILE) - ma_result result; -#endif - - MA_ASSERT(pConfig != NULL); - MA_ASSERT(pContext != NULL); - -#if defined(MA_APPLE_MOBILE) - @autoreleasepool { - AVAudioSession* pAudioSession = [AVAudioSession sharedInstance]; - AVAudioSessionCategoryOptions options = pConfig->coreaudio.sessionCategoryOptions; - - MA_ASSERT(pAudioSession != NULL); - - if (pConfig->coreaudio.sessionCategory == ma_ios_session_category_default) { - /* - I'm going to use trial and error to determine our default session category. First we'll try PlayAndRecord. If that fails - we'll try Playback and if that fails we'll try record. If all of these fail we'll just not set the category. - */ - #if !defined(MA_APPLE_TV) && !defined(MA_APPLE_WATCH) - options |= AVAudioSessionCategoryOptionDefaultToSpeaker; - #endif - - if ([pAudioSession setCategory: AVAudioSessionCategoryPlayAndRecord withOptions:options error:nil]) { - /* Using PlayAndRecord */ - } else if ([pAudioSession setCategory: AVAudioSessionCategoryPlayback withOptions:options error:nil]) { - /* Using Playback */ - } else if ([pAudioSession setCategory: AVAudioSessionCategoryRecord withOptions:options error:nil]) { - /* Using Record */ - } else { - /* Leave as default? */ - } - } else { - if (pConfig->coreaudio.sessionCategory != ma_ios_session_category_none) { - #if defined(__IPHONE_12_0) - if (![pAudioSession setCategory: ma_to_AVAudioSessionCategory(pConfig->coreaudio.sessionCategory) withOptions:options error:nil]) { - return MA_INVALID_OPERATION; /* Failed to set session category. */ - } - #else - /* Ignore the session category on version 11 and older, but post a warning. */ - ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_WARNING, "Session category only supported in iOS 12 and newer."); - #endif - } - } - - if (!pConfig->coreaudio.noAudioSessionActivate) { - if (![pAudioSession setActive:true error:nil]) { - ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "Failed to activate audio session."); - return MA_FAILED_TO_INIT_BACKEND; - } - } - } -#endif - -#if !defined(MA_NO_RUNTIME_LINKING) && !defined(MA_APPLE_MOBILE) - pContext->coreaudio.hCoreFoundation = ma_dlopen(ma_context_get_log(pContext), "/System/Library/Frameworks/CoreFoundation.framework/CoreFoundation"); - if (pContext->coreaudio.hCoreFoundation == NULL) { - return MA_API_NOT_FOUND; - } - - pContext->coreaudio.CFStringGetCString = ma_dlsym(ma_context_get_log(pContext), pContext->coreaudio.hCoreFoundation, "CFStringGetCString"); - pContext->coreaudio.CFRelease = ma_dlsym(ma_context_get_log(pContext), pContext->coreaudio.hCoreFoundation, "CFRelease"); - - - pContext->coreaudio.hCoreAudio = ma_dlopen(ma_context_get_log(pContext), "/System/Library/Frameworks/CoreAudio.framework/CoreAudio"); - if (pContext->coreaudio.hCoreAudio == NULL) { - ma_dlclose(ma_context_get_log(pContext), pContext->coreaudio.hCoreFoundation); - return MA_API_NOT_FOUND; - } - - pContext->coreaudio.AudioObjectGetPropertyData = ma_dlsym(ma_context_get_log(pContext), pContext->coreaudio.hCoreAudio, "AudioObjectGetPropertyData"); - pContext->coreaudio.AudioObjectGetPropertyDataSize = ma_dlsym(ma_context_get_log(pContext), pContext->coreaudio.hCoreAudio, "AudioObjectGetPropertyDataSize"); - pContext->coreaudio.AudioObjectSetPropertyData = ma_dlsym(ma_context_get_log(pContext), pContext->coreaudio.hCoreAudio, "AudioObjectSetPropertyData"); - pContext->coreaudio.AudioObjectAddPropertyListener = ma_dlsym(ma_context_get_log(pContext), pContext->coreaudio.hCoreAudio, "AudioObjectAddPropertyListener"); - pContext->coreaudio.AudioObjectRemovePropertyListener = ma_dlsym(ma_context_get_log(pContext), pContext->coreaudio.hCoreAudio, "AudioObjectRemovePropertyListener"); - - /* - It looks like Apple has moved some APIs from AudioUnit into AudioToolbox on more recent versions of macOS. They are still - defined in AudioUnit, but just in case they decide to remove them from there entirely I'm going to implement a fallback. - The way it'll work is that it'll first try AudioUnit, and if the required symbols are not present there we'll fall back to - AudioToolbox. - */ - pContext->coreaudio.hAudioUnit = ma_dlopen(ma_context_get_log(pContext), "/System/Library/Frameworks/AudioUnit.framework/AudioUnit"); - if (pContext->coreaudio.hAudioUnit == NULL) { - ma_dlclose(ma_context_get_log(pContext), pContext->coreaudio.hCoreAudio); - ma_dlclose(ma_context_get_log(pContext), pContext->coreaudio.hCoreFoundation); - return MA_API_NOT_FOUND; - } - - if (ma_dlsym(ma_context_get_log(pContext), pContext->coreaudio.hAudioUnit, "AudioComponentFindNext") == NULL) { - /* Couldn't find the required symbols in AudioUnit, so fall back to AudioToolbox. */ - ma_dlclose(ma_context_get_log(pContext), pContext->coreaudio.hAudioUnit); - pContext->coreaudio.hAudioUnit = ma_dlopen(ma_context_get_log(pContext), "/System/Library/Frameworks/AudioToolbox.framework/AudioToolbox"); - if (pContext->coreaudio.hAudioUnit == NULL) { - ma_dlclose(ma_context_get_log(pContext), pContext->coreaudio.hCoreAudio); - ma_dlclose(ma_context_get_log(pContext), pContext->coreaudio.hCoreFoundation); - return MA_API_NOT_FOUND; - } - } - - pContext->coreaudio.AudioComponentFindNext = ma_dlsym(ma_context_get_log(pContext), pContext->coreaudio.hAudioUnit, "AudioComponentFindNext"); - pContext->coreaudio.AudioComponentInstanceDispose = ma_dlsym(ma_context_get_log(pContext), pContext->coreaudio.hAudioUnit, "AudioComponentInstanceDispose"); - pContext->coreaudio.AudioComponentInstanceNew = ma_dlsym(ma_context_get_log(pContext), pContext->coreaudio.hAudioUnit, "AudioComponentInstanceNew"); - pContext->coreaudio.AudioOutputUnitStart = ma_dlsym(ma_context_get_log(pContext), pContext->coreaudio.hAudioUnit, "AudioOutputUnitStart"); - pContext->coreaudio.AudioOutputUnitStop = ma_dlsym(ma_context_get_log(pContext), pContext->coreaudio.hAudioUnit, "AudioOutputUnitStop"); - pContext->coreaudio.AudioUnitAddPropertyListener = ma_dlsym(ma_context_get_log(pContext), pContext->coreaudio.hAudioUnit, "AudioUnitAddPropertyListener"); - pContext->coreaudio.AudioUnitGetPropertyInfo = ma_dlsym(ma_context_get_log(pContext), pContext->coreaudio.hAudioUnit, "AudioUnitGetPropertyInfo"); - pContext->coreaudio.AudioUnitGetProperty = ma_dlsym(ma_context_get_log(pContext), pContext->coreaudio.hAudioUnit, "AudioUnitGetProperty"); - pContext->coreaudio.AudioUnitSetProperty = ma_dlsym(ma_context_get_log(pContext), pContext->coreaudio.hAudioUnit, "AudioUnitSetProperty"); - pContext->coreaudio.AudioUnitInitialize = ma_dlsym(ma_context_get_log(pContext), pContext->coreaudio.hAudioUnit, "AudioUnitInitialize"); - pContext->coreaudio.AudioUnitRender = ma_dlsym(ma_context_get_log(pContext), pContext->coreaudio.hAudioUnit, "AudioUnitRender"); -#else - pContext->coreaudio.CFStringGetCString = (ma_proc)CFStringGetCString; - pContext->coreaudio.CFRelease = (ma_proc)CFRelease; - - #if defined(MA_APPLE_DESKTOP) - pContext->coreaudio.AudioObjectGetPropertyData = (ma_proc)AudioObjectGetPropertyData; - pContext->coreaudio.AudioObjectGetPropertyDataSize = (ma_proc)AudioObjectGetPropertyDataSize; - pContext->coreaudio.AudioObjectSetPropertyData = (ma_proc)AudioObjectSetPropertyData; - pContext->coreaudio.AudioObjectAddPropertyListener = (ma_proc)AudioObjectAddPropertyListener; - pContext->coreaudio.AudioObjectRemovePropertyListener = (ma_proc)AudioObjectRemovePropertyListener; - #endif - - pContext->coreaudio.AudioComponentFindNext = (ma_proc)AudioComponentFindNext; - pContext->coreaudio.AudioComponentInstanceDispose = (ma_proc)AudioComponentInstanceDispose; - pContext->coreaudio.AudioComponentInstanceNew = (ma_proc)AudioComponentInstanceNew; - pContext->coreaudio.AudioOutputUnitStart = (ma_proc)AudioOutputUnitStart; - pContext->coreaudio.AudioOutputUnitStop = (ma_proc)AudioOutputUnitStop; - pContext->coreaudio.AudioUnitAddPropertyListener = (ma_proc)AudioUnitAddPropertyListener; - pContext->coreaudio.AudioUnitGetPropertyInfo = (ma_proc)AudioUnitGetPropertyInfo; - pContext->coreaudio.AudioUnitGetProperty = (ma_proc)AudioUnitGetProperty; - pContext->coreaudio.AudioUnitSetProperty = (ma_proc)AudioUnitSetProperty; - pContext->coreaudio.AudioUnitInitialize = (ma_proc)AudioUnitInitialize; - pContext->coreaudio.AudioUnitRender = (ma_proc)AudioUnitRender; -#endif - - /* Audio component. */ - { - AudioComponentDescription desc; - desc.componentType = kAudioUnitType_Output; - #if defined(MA_APPLE_DESKTOP) - desc.componentSubType = kAudioUnitSubType_HALOutput; - #else - desc.componentSubType = kAudioUnitSubType_RemoteIO; - #endif - desc.componentManufacturer = kAudioUnitManufacturer_Apple; - desc.componentFlags = 0; - desc.componentFlagsMask = 0; - - pContext->coreaudio.component = ((ma_AudioComponentFindNext_proc)pContext->coreaudio.AudioComponentFindNext)(NULL, &desc); - if (pContext->coreaudio.component == NULL) { - #if !defined(MA_NO_RUNTIME_LINKING) && !defined(MA_APPLE_MOBILE) - ma_dlclose(ma_context_get_log(pContext), pContext->coreaudio.hAudioUnit); - ma_dlclose(ma_context_get_log(pContext), pContext->coreaudio.hCoreAudio); - ma_dlclose(ma_context_get_log(pContext), pContext->coreaudio.hCoreFoundation); - #endif - return MA_FAILED_TO_INIT_BACKEND; - } - } - -#if !defined(MA_APPLE_MOBILE) - result = ma_context__init_device_tracking__coreaudio(pContext); - if (result != MA_SUCCESS) { - #if !defined(MA_NO_RUNTIME_LINKING) && !defined(MA_APPLE_MOBILE) - ma_dlclose(ma_context_get_log(pContext), pContext->coreaudio.hAudioUnit); - ma_dlclose(ma_context_get_log(pContext), pContext->coreaudio.hCoreAudio); - ma_dlclose(ma_context_get_log(pContext), pContext->coreaudio.hCoreFoundation); - #endif - return result; - } -#endif - - pContext->coreaudio.noAudioSessionDeactivate = pConfig->coreaudio.noAudioSessionDeactivate; - - pCallbacks->onContextInit = ma_context_init__coreaudio; - pCallbacks->onContextUninit = ma_context_uninit__coreaudio; - pCallbacks->onContextEnumerateDevices = ma_context_enumerate_devices__coreaudio; - pCallbacks->onContextGetDeviceInfo = ma_context_get_device_info__coreaudio; - pCallbacks->onDeviceInit = ma_device_init__coreaudio; - pCallbacks->onDeviceUninit = ma_device_uninit__coreaudio; - pCallbacks->onDeviceStart = ma_device_start__coreaudio; - pCallbacks->onDeviceStop = ma_device_stop__coreaudio; - pCallbacks->onDeviceRead = NULL; - pCallbacks->onDeviceWrite = NULL; - pCallbacks->onDeviceDataLoop = NULL; - - return MA_SUCCESS; -} -#endif /* MA_HAS_COREAUDIO */ - - - -/****************************************************************************** - -sndio Backend - -******************************************************************************/ -#ifdef MA_HAS_SNDIO -#include - -/* -Only supporting OpenBSD. This did not work very well at all on FreeBSD when I tried it. Not sure if this is due -to miniaudio's implementation or if it's some kind of system configuration issue, but basically the default device -just doesn't emit any sound, or at times you'll hear tiny pieces. I will consider enabling this when there's -demand for it or if I can get it tested and debugged more thoroughly. -*/ -#if 0 -#if defined(__NetBSD__) || defined(__OpenBSD__) -#include -#endif -#if defined(__FreeBSD__) || defined(__DragonFly__) -#include -#endif -#endif - -#define MA_SIO_DEVANY "default" -#define MA_SIO_PLAY 1 -#define MA_SIO_REC 2 -#define MA_SIO_NENC 8 -#define MA_SIO_NCHAN 8 -#define MA_SIO_NRATE 16 -#define MA_SIO_NCONF 4 - -struct ma_sio_hdl; /* <-- Opaque */ - -struct ma_sio_par -{ - unsigned int bits; - unsigned int bps; - unsigned int sig; - unsigned int le; - unsigned int msb; - unsigned int rchan; - unsigned int pchan; - unsigned int rate; - unsigned int bufsz; - unsigned int xrun; - unsigned int round; - unsigned int appbufsz; - int __pad[3]; - unsigned int __magic; -}; - -struct ma_sio_enc -{ - unsigned int bits; - unsigned int bps; - unsigned int sig; - unsigned int le; - unsigned int msb; -}; - -struct ma_sio_conf -{ - unsigned int enc; - unsigned int rchan; - unsigned int pchan; - unsigned int rate; -}; - -struct ma_sio_cap -{ - struct ma_sio_enc enc[MA_SIO_NENC]; - unsigned int rchan[MA_SIO_NCHAN]; - unsigned int pchan[MA_SIO_NCHAN]; - unsigned int rate[MA_SIO_NRATE]; - int __pad[7]; - unsigned int nconf; - struct ma_sio_conf confs[MA_SIO_NCONF]; -}; - -typedef struct ma_sio_hdl* (* ma_sio_open_proc) (const char*, unsigned int, int); -typedef void (* ma_sio_close_proc) (struct ma_sio_hdl*); -typedef int (* ma_sio_setpar_proc) (struct ma_sio_hdl*, struct ma_sio_par*); -typedef int (* ma_sio_getpar_proc) (struct ma_sio_hdl*, struct ma_sio_par*); -typedef int (* ma_sio_getcap_proc) (struct ma_sio_hdl*, struct ma_sio_cap*); -typedef size_t (* ma_sio_write_proc) (struct ma_sio_hdl*, const void*, size_t); -typedef size_t (* ma_sio_read_proc) (struct ma_sio_hdl*, void*, size_t); -typedef int (* ma_sio_start_proc) (struct ma_sio_hdl*); -typedef int (* ma_sio_stop_proc) (struct ma_sio_hdl*); -typedef int (* ma_sio_initpar_proc)(struct ma_sio_par*); - -static ma_uint32 ma_get_standard_sample_rate_priority_index__sndio(ma_uint32 sampleRate) /* Lower = higher priority */ -{ - ma_uint32 i; - for (i = 0; i < ma_countof(g_maStandardSampleRatePriorities); ++i) { - if (g_maStandardSampleRatePriorities[i] == sampleRate) { - return i; - } - } - - return (ma_uint32)-1; -} - -static ma_format ma_format_from_sio_enc__sndio(unsigned int bits, unsigned int bps, unsigned int sig, unsigned int le, unsigned int msb) -{ - /* We only support native-endian right now. */ - if ((ma_is_little_endian() && le == 0) || (ma_is_big_endian() && le == 1)) { - return ma_format_unknown; - } - - if (bits == 8 && bps == 1 && sig == 0) { - return ma_format_u8; - } - if (bits == 16 && bps == 2 && sig == 1) { - return ma_format_s16; - } - if (bits == 24 && bps == 3 && sig == 1) { - return ma_format_s24; - } - if (bits == 24 && bps == 4 && sig == 1 && msb == 0) { - /*return ma_format_s24_32;*/ - } - if (bits == 32 && bps == 4 && sig == 1) { - return ma_format_s32; - } - - return ma_format_unknown; -} - -static ma_format ma_find_best_format_from_sio_cap__sndio(struct ma_sio_cap* caps) -{ - ma_format bestFormat; - unsigned int iConfig; - - MA_ASSERT(caps != NULL); - - bestFormat = ma_format_unknown; - for (iConfig = 0; iConfig < caps->nconf; iConfig += 1) { - unsigned int iEncoding; - for (iEncoding = 0; iEncoding < MA_SIO_NENC; iEncoding += 1) { - unsigned int bits; - unsigned int bps; - unsigned int sig; - unsigned int le; - unsigned int msb; - ma_format format; - - if ((caps->confs[iConfig].enc & (1UL << iEncoding)) == 0) { - continue; - } - - bits = caps->enc[iEncoding].bits; - bps = caps->enc[iEncoding].bps; - sig = caps->enc[iEncoding].sig; - le = caps->enc[iEncoding].le; - msb = caps->enc[iEncoding].msb; - format = ma_format_from_sio_enc__sndio(bits, bps, sig, le, msb); - if (format == ma_format_unknown) { - continue; /* Format not supported. */ - } - - if (bestFormat == ma_format_unknown) { - bestFormat = format; - } else { - if (ma_get_format_priority_index(bestFormat) > ma_get_format_priority_index(format)) { /* <-- Lower = better. */ - bestFormat = format; - } - } - } - } - - return bestFormat; -} - -static ma_uint32 ma_find_best_channels_from_sio_cap__sndio(struct ma_sio_cap* caps, ma_device_type deviceType, ma_format requiredFormat) -{ - ma_uint32 maxChannels; - unsigned int iConfig; - - MA_ASSERT(caps != NULL); - MA_ASSERT(requiredFormat != ma_format_unknown); - - /* Just pick whatever configuration has the most channels. */ - maxChannels = 0; - for (iConfig = 0; iConfig < caps->nconf; iConfig += 1) { - /* The encoding should be of requiredFormat. */ - unsigned int iEncoding; - for (iEncoding = 0; iEncoding < MA_SIO_NENC; iEncoding += 1) { - unsigned int iChannel; - unsigned int bits; - unsigned int bps; - unsigned int sig; - unsigned int le; - unsigned int msb; - ma_format format; - - if ((caps->confs[iConfig].enc & (1UL << iEncoding)) == 0) { - continue; - } - - bits = caps->enc[iEncoding].bits; - bps = caps->enc[iEncoding].bps; - sig = caps->enc[iEncoding].sig; - le = caps->enc[iEncoding].le; - msb = caps->enc[iEncoding].msb; - format = ma_format_from_sio_enc__sndio(bits, bps, sig, le, msb); - if (format != requiredFormat) { - continue; - } - - /* Getting here means the format is supported. Iterate over each channel count and grab the biggest one. */ - for (iChannel = 0; iChannel < MA_SIO_NCHAN; iChannel += 1) { - unsigned int chan = 0; - unsigned int channels; - - if (deviceType == ma_device_type_playback) { - chan = caps->confs[iConfig].pchan; - } else { - chan = caps->confs[iConfig].rchan; - } - - if ((chan & (1UL << iChannel)) == 0) { - continue; - } - - if (deviceType == ma_device_type_playback) { - channels = caps->pchan[iChannel]; - } else { - channels = caps->rchan[iChannel]; - } - - if (maxChannels < channels) { - maxChannels = channels; - } - } - } - } - - return maxChannels; -} - -static ma_uint32 ma_find_best_sample_rate_from_sio_cap__sndio(struct ma_sio_cap* caps, ma_device_type deviceType, ma_format requiredFormat, ma_uint32 requiredChannels) -{ - ma_uint32 firstSampleRate; - ma_uint32 bestSampleRate; - unsigned int iConfig; - - MA_ASSERT(caps != NULL); - MA_ASSERT(requiredFormat != ma_format_unknown); - MA_ASSERT(requiredChannels > 0); - MA_ASSERT(requiredChannels <= MA_MAX_CHANNELS); - - firstSampleRate = 0; /* <-- If the device does not support a standard rate we'll fall back to the first one that's found. */ - bestSampleRate = 0; - - for (iConfig = 0; iConfig < caps->nconf; iConfig += 1) { - /* The encoding should be of requiredFormat. */ - unsigned int iEncoding; - for (iEncoding = 0; iEncoding < MA_SIO_NENC; iEncoding += 1) { - unsigned int iChannel; - unsigned int bits; - unsigned int bps; - unsigned int sig; - unsigned int le; - unsigned int msb; - ma_format format; - - if ((caps->confs[iConfig].enc & (1UL << iEncoding)) == 0) { - continue; - } - - bits = caps->enc[iEncoding].bits; - bps = caps->enc[iEncoding].bps; - sig = caps->enc[iEncoding].sig; - le = caps->enc[iEncoding].le; - msb = caps->enc[iEncoding].msb; - format = ma_format_from_sio_enc__sndio(bits, bps, sig, le, msb); - if (format != requiredFormat) { - continue; - } - - /* Getting here means the format is supported. Iterate over each channel count and grab the biggest one. */ - for (iChannel = 0; iChannel < MA_SIO_NCHAN; iChannel += 1) { - unsigned int chan = 0; - unsigned int channels; - unsigned int iRate; - - if (deviceType == ma_device_type_playback) { - chan = caps->confs[iConfig].pchan; - } else { - chan = caps->confs[iConfig].rchan; - } - - if ((chan & (1UL << iChannel)) == 0) { - continue; - } - - if (deviceType == ma_device_type_playback) { - channels = caps->pchan[iChannel]; - } else { - channels = caps->rchan[iChannel]; - } - - if (channels != requiredChannels) { - continue; - } - - /* Getting here means we have found a compatible encoding/channel pair. */ - for (iRate = 0; iRate < MA_SIO_NRATE; iRate += 1) { - ma_uint32 rate = (ma_uint32)caps->rate[iRate]; - ma_uint32 ratePriority; - - if (firstSampleRate == 0) { - firstSampleRate = rate; - } - - /* Disregard this rate if it's not a standard one. */ - ratePriority = ma_get_standard_sample_rate_priority_index__sndio(rate); - if (ratePriority == (ma_uint32)-1) { - continue; - } - - if (ma_get_standard_sample_rate_priority_index__sndio(bestSampleRate) > ratePriority) { /* Lower = better. */ - bestSampleRate = rate; - } - } - } - } - } - - /* If a standard sample rate was not found just fall back to the first one that was iterated. */ - if (bestSampleRate == 0) { - bestSampleRate = firstSampleRate; - } - - return bestSampleRate; -} - - -static ma_result ma_context_enumerate_devices__sndio(ma_context* pContext, ma_enum_devices_callback_proc callback, void* pUserData) -{ - ma_bool32 isTerminating = MA_FALSE; - struct ma_sio_hdl* handle; - - MA_ASSERT(pContext != NULL); - MA_ASSERT(callback != NULL); - - /* sndio doesn't seem to have a good device enumeration API, so I'm therefore only enumerating over default devices for now. */ - - /* Playback. */ - if (!isTerminating) { - handle = ((ma_sio_open_proc)pContext->sndio.sio_open)(MA_SIO_DEVANY, MA_SIO_PLAY, 0); - if (handle != NULL) { - /* Supports playback. */ - ma_device_info deviceInfo; - MA_ZERO_OBJECT(&deviceInfo); - ma_strcpy_s(deviceInfo.id.sndio, sizeof(deviceInfo.id.sndio), MA_SIO_DEVANY); - ma_strcpy_s(deviceInfo.name, sizeof(deviceInfo.name), MA_DEFAULT_PLAYBACK_DEVICE_NAME); - - isTerminating = !callback(pContext, ma_device_type_playback, &deviceInfo, pUserData); - - ((ma_sio_close_proc)pContext->sndio.sio_close)(handle); - } - } - - /* Capture. */ - if (!isTerminating) { - handle = ((ma_sio_open_proc)pContext->sndio.sio_open)(MA_SIO_DEVANY, MA_SIO_REC, 0); - if (handle != NULL) { - /* Supports capture. */ - ma_device_info deviceInfo; - MA_ZERO_OBJECT(&deviceInfo); - ma_strcpy_s(deviceInfo.id.sndio, sizeof(deviceInfo.id.sndio), "default"); - ma_strcpy_s(deviceInfo.name, sizeof(deviceInfo.name), MA_DEFAULT_CAPTURE_DEVICE_NAME); - - isTerminating = !callback(pContext, ma_device_type_capture, &deviceInfo, pUserData); - - ((ma_sio_close_proc)pContext->sndio.sio_close)(handle); - } - } - - return MA_SUCCESS; -} - -static ma_result ma_context_get_device_info__sndio(ma_context* pContext, ma_device_type deviceType, const ma_device_id* pDeviceID, ma_device_info* pDeviceInfo) -{ - char devid[256]; - struct ma_sio_hdl* handle; - struct ma_sio_cap caps; - unsigned int iConfig; - - MA_ASSERT(pContext != NULL); - - /* We need to open the device before we can get information about it. */ - if (pDeviceID == NULL) { - ma_strcpy_s(devid, sizeof(devid), MA_SIO_DEVANY); - ma_strcpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), (deviceType == ma_device_type_playback) ? MA_DEFAULT_PLAYBACK_DEVICE_NAME : MA_DEFAULT_CAPTURE_DEVICE_NAME); - } else { - ma_strcpy_s(devid, sizeof(devid), pDeviceID->sndio); - ma_strcpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), devid); - } - - handle = ((ma_sio_open_proc)pContext->sndio.sio_open)(devid, (deviceType == ma_device_type_playback) ? MA_SIO_PLAY : MA_SIO_REC, 0); - if (handle == NULL) { - return MA_NO_DEVICE; - } - - if (((ma_sio_getcap_proc)pContext->sndio.sio_getcap)(handle, &caps) == 0) { - return MA_ERROR; - } - - pDeviceInfo->nativeDataFormatCount = 0; - - for (iConfig = 0; iConfig < caps.nconf; iConfig += 1) { - /* - The main thing we care about is that the encoding is supported by miniaudio. If it is, we want to give - preference to some formats over others. - */ - unsigned int iEncoding; - unsigned int iChannel; - unsigned int iRate; - - for (iEncoding = 0; iEncoding < MA_SIO_NENC; iEncoding += 1) { - unsigned int bits; - unsigned int bps; - unsigned int sig; - unsigned int le; - unsigned int msb; - ma_format format; - - if ((caps.confs[iConfig].enc & (1UL << iEncoding)) == 0) { - continue; - } - - bits = caps.enc[iEncoding].bits; - bps = caps.enc[iEncoding].bps; - sig = caps.enc[iEncoding].sig; - le = caps.enc[iEncoding].le; - msb = caps.enc[iEncoding].msb; - format = ma_format_from_sio_enc__sndio(bits, bps, sig, le, msb); - if (format == ma_format_unknown) { - continue; /* Format not supported. */ - } - - - /* Channels. */ - for (iChannel = 0; iChannel < MA_SIO_NCHAN; iChannel += 1) { - unsigned int chan = 0; - unsigned int channels; - - if (deviceType == ma_device_type_playback) { - chan = caps.confs[iConfig].pchan; - } else { - chan = caps.confs[iConfig].rchan; - } - - if ((chan & (1UL << iChannel)) == 0) { - continue; - } - - if (deviceType == ma_device_type_playback) { - channels = caps.pchan[iChannel]; - } else { - channels = caps.rchan[iChannel]; - } - - - /* Sample Rates. */ - for (iRate = 0; iRate < MA_SIO_NRATE; iRate += 1) { - if ((caps.confs[iConfig].rate & (1UL << iRate)) != 0) { - ma_device_info_add_native_data_format(pDeviceInfo, format, channels, caps.rate[iRate], 0); - } - } - } - } - } - - ((ma_sio_close_proc)pContext->sndio.sio_close)(handle); - return MA_SUCCESS; -} - -static ma_result ma_device_uninit__sndio(ma_device* pDevice) -{ - MA_ASSERT(pDevice != NULL); - - if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) { - ((ma_sio_close_proc)pDevice->pContext->sndio.sio_close)((struct ma_sio_hdl*)pDevice->sndio.handleCapture); - } - - if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) { - ((ma_sio_close_proc)pDevice->pContext->sndio.sio_close)((struct ma_sio_hdl*)pDevice->sndio.handlePlayback); - } - - return MA_SUCCESS; -} - -static ma_result ma_device_init_handle__sndio(ma_device* pDevice, const ma_device_config* pConfig, ma_device_descriptor* pDescriptor, ma_device_type deviceType) -{ - const char* pDeviceName; - ma_ptr handle; - int openFlags = 0; - struct ma_sio_cap caps; - struct ma_sio_par par; - const ma_device_id* pDeviceID; - ma_format format; - ma_uint32 channels; - ma_uint32 sampleRate; - ma_format internalFormat; - ma_uint32 internalChannels; - ma_uint32 internalSampleRate; - ma_uint32 internalPeriodSizeInFrames; - ma_uint32 internalPeriods; - - MA_ASSERT(pConfig != NULL); - MA_ASSERT(deviceType != ma_device_type_duplex); - MA_ASSERT(pDevice != NULL); - - if (deviceType == ma_device_type_capture) { - openFlags = MA_SIO_REC; - } else { - openFlags = MA_SIO_PLAY; - } - - pDeviceID = pDescriptor->pDeviceID; - format = pDescriptor->format; - channels = pDescriptor->channels; - sampleRate = pDescriptor->sampleRate; - - pDeviceName = MA_SIO_DEVANY; - if (pDeviceID != NULL) { - pDeviceName = pDeviceID->sndio; - } - - handle = (ma_ptr)((ma_sio_open_proc)pDevice->pContext->sndio.sio_open)(pDeviceName, openFlags, 0); - if (handle == NULL) { - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[sndio] Failed to open device."); - return MA_FAILED_TO_OPEN_BACKEND_DEVICE; - } - - /* We need to retrieve the device caps to determine the most appropriate format to use. */ - if (((ma_sio_getcap_proc)pDevice->pContext->sndio.sio_getcap)((struct ma_sio_hdl*)handle, &caps) == 0) { - ((ma_sio_close_proc)pDevice->pContext->sndio.sio_close)((struct ma_sio_hdl*)handle); - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[sndio] Failed to retrieve device caps."); - return MA_ERROR; - } - - /* - Note: sndio reports a huge range of available channels. This is inconvenient for us because there's no real - way, as far as I can tell, to get the _actual_ channel count of the device. I'm therefore restricting this - to the requested channels, regardless of whether or not the default channel count is requested. - - For hardware devices, I'm suspecting only a single channel count will be reported and we can safely use the - value returned by ma_find_best_channels_from_sio_cap__sndio(). - */ - if (deviceType == ma_device_type_capture) { - if (format == ma_format_unknown) { - format = ma_find_best_format_from_sio_cap__sndio(&caps); - } - - if (channels == 0) { - if (strlen(pDeviceName) > strlen("rsnd/") && strncmp(pDeviceName, "rsnd/", strlen("rsnd/")) == 0) { - channels = ma_find_best_channels_from_sio_cap__sndio(&caps, deviceType, format); - } else { - channels = MA_DEFAULT_CHANNELS; - } - } - } else { - if (format == ma_format_unknown) { - format = ma_find_best_format_from_sio_cap__sndio(&caps); - } - - if (channels == 0) { - if (strlen(pDeviceName) > strlen("rsnd/") && strncmp(pDeviceName, "rsnd/", strlen("rsnd/")) == 0) { - channels = ma_find_best_channels_from_sio_cap__sndio(&caps, deviceType, format); - } else { - channels = MA_DEFAULT_CHANNELS; - } - } - } - - if (sampleRate == 0) { - sampleRate = ma_find_best_sample_rate_from_sio_cap__sndio(&caps, pConfig->deviceType, format, channels); - } - - - ((ma_sio_initpar_proc)pDevice->pContext->sndio.sio_initpar)(&par); - par.msb = 0; - par.le = ma_is_little_endian(); - - switch (format) { - case ma_format_u8: - { - par.bits = 8; - par.bps = 1; - par.sig = 0; - } break; - - case ma_format_s24: - { - par.bits = 24; - par.bps = 3; - par.sig = 1; - } break; - - case ma_format_s32: - { - par.bits = 32; - par.bps = 4; - par.sig = 1; - } break; - - case ma_format_s16: - case ma_format_f32: - case ma_format_unknown: - default: - { - par.bits = 16; - par.bps = 2; - par.sig = 1; - } break; - } - - if (deviceType == ma_device_type_capture) { - par.rchan = channels; - } else { - par.pchan = channels; - } - - par.rate = sampleRate; - - internalPeriodSizeInFrames = ma_calculate_buffer_size_in_frames_from_descriptor(pDescriptor, par.rate, pConfig->performanceProfile); - - par.round = internalPeriodSizeInFrames; - par.appbufsz = par.round * pDescriptor->periodCount; - - if (((ma_sio_setpar_proc)pDevice->pContext->sndio.sio_setpar)((struct ma_sio_hdl*)handle, &par) == 0) { - ((ma_sio_close_proc)pDevice->pContext->sndio.sio_close)((struct ma_sio_hdl*)handle); - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[sndio] Failed to set buffer size."); - return MA_ERROR; - } - - if (((ma_sio_getpar_proc)pDevice->pContext->sndio.sio_getpar)((struct ma_sio_hdl*)handle, &par) == 0) { - ((ma_sio_close_proc)pDevice->pContext->sndio.sio_close)((struct ma_sio_hdl*)handle); - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[sndio] Failed to retrieve buffer size."); - return MA_ERROR; - } - - internalFormat = ma_format_from_sio_enc__sndio(par.bits, par.bps, par.sig, par.le, par.msb); - internalChannels = (deviceType == ma_device_type_capture) ? par.rchan : par.pchan; - internalSampleRate = par.rate; - internalPeriods = par.appbufsz / par.round; - internalPeriodSizeInFrames = par.round; - - if (deviceType == ma_device_type_capture) { - pDevice->sndio.handleCapture = handle; - } else { - pDevice->sndio.handlePlayback = handle; - } - - pDescriptor->format = internalFormat; - pDescriptor->channels = internalChannels; - pDescriptor->sampleRate = internalSampleRate; - ma_channel_map_init_standard(ma_standard_channel_map_sndio, pDescriptor->channelMap, ma_countof(pDescriptor->channelMap), internalChannels); - pDescriptor->periodSizeInFrames = internalPeriodSizeInFrames; - pDescriptor->periodCount = internalPeriods; - - return MA_SUCCESS; -} - -static ma_result ma_device_init__sndio(ma_device* pDevice, const ma_device_config* pConfig, ma_device_descriptor* pDescriptorPlayback, ma_device_descriptor* pDescriptorCapture) -{ - MA_ASSERT(pDevice != NULL); - - MA_ZERO_OBJECT(&pDevice->sndio); - - if (pConfig->deviceType == ma_device_type_loopback) { - return MA_DEVICE_TYPE_NOT_SUPPORTED; - } - - if (pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) { - ma_result result = ma_device_init_handle__sndio(pDevice, pConfig, pDescriptorCapture, ma_device_type_capture); - if (result != MA_SUCCESS) { - return result; - } - } - - if (pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) { - ma_result result = ma_device_init_handle__sndio(pDevice, pConfig, pDescriptorPlayback, ma_device_type_playback); - if (result != MA_SUCCESS) { - return result; - } - } - - return MA_SUCCESS; -} - -static ma_result ma_device_start__sndio(ma_device* pDevice) -{ - MA_ASSERT(pDevice != NULL); - - if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) { - ((ma_sio_start_proc)pDevice->pContext->sndio.sio_start)((struct ma_sio_hdl*)pDevice->sndio.handleCapture); - } - - if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) { - ((ma_sio_start_proc)pDevice->pContext->sndio.sio_start)((struct ma_sio_hdl*)pDevice->sndio.handlePlayback); /* <-- Doesn't actually playback until data is written. */ - } - - return MA_SUCCESS; -} - -static ma_result ma_device_stop__sndio(ma_device* pDevice) -{ - MA_ASSERT(pDevice != NULL); - - /* - From the documentation: - - The sio_stop() function puts the audio subsystem in the same state as before sio_start() is called. It stops recording, drains the play buffer and then - stops playback. If samples to play are queued but playback hasn't started yet then playback is forced immediately; playback will actually stop once the - buffer is drained. In no case are samples in the play buffer discarded. - - Therefore, sio_stop() performs all of the necessary draining for us. - */ - - if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) { - ((ma_sio_stop_proc)pDevice->pContext->sndio.sio_stop)((struct ma_sio_hdl*)pDevice->sndio.handleCapture); - } - - if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) { - ((ma_sio_stop_proc)pDevice->pContext->sndio.sio_stop)((struct ma_sio_hdl*)pDevice->sndio.handlePlayback); - } - - return MA_SUCCESS; -} - -static ma_result ma_device_write__sndio(ma_device* pDevice, const void* pPCMFrames, ma_uint32 frameCount, ma_uint32* pFramesWritten) -{ - int result; - - if (pFramesWritten != NULL) { - *pFramesWritten = 0; - } - - result = ((ma_sio_write_proc)pDevice->pContext->sndio.sio_write)((struct ma_sio_hdl*)pDevice->sndio.handlePlayback, pPCMFrames, frameCount * ma_get_bytes_per_frame(pDevice->playback.internalFormat, pDevice->playback.internalChannels)); - if (result == 0) { - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[sndio] Failed to send data from the client to the device."); - return MA_IO_ERROR; - } - - if (pFramesWritten != NULL) { - *pFramesWritten = frameCount; - } - - return MA_SUCCESS; -} - -static ma_result ma_device_read__sndio(ma_device* pDevice, void* pPCMFrames, ma_uint32 frameCount, ma_uint32* pFramesRead) -{ - int result; - - if (pFramesRead != NULL) { - *pFramesRead = 0; - } - - result = ((ma_sio_read_proc)pDevice->pContext->sndio.sio_read)((struct ma_sio_hdl*)pDevice->sndio.handleCapture, pPCMFrames, frameCount * ma_get_bytes_per_frame(pDevice->capture.internalFormat, pDevice->capture.internalChannels)); - if (result == 0) { - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[sndio] Failed to read data from the device to be sent to the device."); - return MA_IO_ERROR; - } - - if (pFramesRead != NULL) { - *pFramesRead = frameCount; - } - - return MA_SUCCESS; -} - -static ma_result ma_context_uninit__sndio(ma_context* pContext) -{ - MA_ASSERT(pContext != NULL); - MA_ASSERT(pContext->backend == ma_backend_sndio); - - (void)pContext; - return MA_SUCCESS; -} - -static ma_result ma_context_init__sndio(ma_context* pContext, const ma_context_config* pConfig, ma_backend_callbacks* pCallbacks) -{ -#ifndef MA_NO_RUNTIME_LINKING - const char* libsndioNames[] = { - "libsndio.so" - }; - size_t i; - - for (i = 0; i < ma_countof(libsndioNames); ++i) { - pContext->sndio.sndioSO = ma_dlopen(ma_context_get_log(pContext), libsndioNames[i]); - if (pContext->sndio.sndioSO != NULL) { - break; - } - } - - if (pContext->sndio.sndioSO == NULL) { - return MA_NO_BACKEND; - } - - pContext->sndio.sio_open = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->sndio.sndioSO, "sio_open"); - pContext->sndio.sio_close = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->sndio.sndioSO, "sio_close"); - pContext->sndio.sio_setpar = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->sndio.sndioSO, "sio_setpar"); - pContext->sndio.sio_getpar = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->sndio.sndioSO, "sio_getpar"); - pContext->sndio.sio_getcap = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->sndio.sndioSO, "sio_getcap"); - pContext->sndio.sio_write = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->sndio.sndioSO, "sio_write"); - pContext->sndio.sio_read = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->sndio.sndioSO, "sio_read"); - pContext->sndio.sio_start = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->sndio.sndioSO, "sio_start"); - pContext->sndio.sio_stop = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->sndio.sndioSO, "sio_stop"); - pContext->sndio.sio_initpar = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->sndio.sndioSO, "sio_initpar"); -#else - pContext->sndio.sio_open = sio_open; - pContext->sndio.sio_close = sio_close; - pContext->sndio.sio_setpar = sio_setpar; - pContext->sndio.sio_getpar = sio_getpar; - pContext->sndio.sio_getcap = sio_getcap; - pContext->sndio.sio_write = sio_write; - pContext->sndio.sio_read = sio_read; - pContext->sndio.sio_start = sio_start; - pContext->sndio.sio_stop = sio_stop; - pContext->sndio.sio_initpar = sio_initpar; -#endif - - pCallbacks->onContextInit = ma_context_init__sndio; - pCallbacks->onContextUninit = ma_context_uninit__sndio; - pCallbacks->onContextEnumerateDevices = ma_context_enumerate_devices__sndio; - pCallbacks->onContextGetDeviceInfo = ma_context_get_device_info__sndio; - pCallbacks->onDeviceInit = ma_device_init__sndio; - pCallbacks->onDeviceUninit = ma_device_uninit__sndio; - pCallbacks->onDeviceStart = ma_device_start__sndio; - pCallbacks->onDeviceStop = ma_device_stop__sndio; - pCallbacks->onDeviceRead = ma_device_read__sndio; - pCallbacks->onDeviceWrite = ma_device_write__sndio; - pCallbacks->onDeviceDataLoop = NULL; - - (void)pConfig; - return MA_SUCCESS; -} -#endif /* MA_HAS_SNDIO */ - - - -/****************************************************************************** - -audio(4) Backend - -******************************************************************************/ -#ifdef MA_HAS_AUDIO4 -#include -#include -#include -#include -#include -#include -#include - -#ifdef __NetBSD__ -#include -#endif - -#if defined(__OpenBSD__) - #include - #if defined(OpenBSD) && OpenBSD >= 201709 - #define MA_AUDIO4_USE_NEW_API - #endif -#endif - -static void ma_construct_device_id__audio4(char* id, size_t idSize, const char* base, int deviceIndex) -{ - size_t baseLen; - - MA_ASSERT(id != NULL); - MA_ASSERT(idSize > 0); - MA_ASSERT(deviceIndex >= 0); - - baseLen = strlen(base); - MA_ASSERT(idSize > baseLen); - - ma_strcpy_s(id, idSize, base); - ma_itoa_s(deviceIndex, id+baseLen, idSize-baseLen, 10); -} - -static ma_result ma_extract_device_index_from_id__audio4(const char* id, const char* base, int* pIndexOut) -{ - size_t idLen; - size_t baseLen; - const char* deviceIndexStr; - - MA_ASSERT(id != NULL); - MA_ASSERT(base != NULL); - MA_ASSERT(pIndexOut != NULL); - - idLen = strlen(id); - baseLen = strlen(base); - if (idLen <= baseLen) { - return MA_ERROR; /* Doesn't look like the id starts with the base. */ - } - - if (strncmp(id, base, baseLen) != 0) { - return MA_ERROR; /* ID does not begin with base. */ - } - - deviceIndexStr = id + baseLen; - if (deviceIndexStr[0] == '\0') { - return MA_ERROR; /* No index specified in the ID. */ - } - - if (pIndexOut) { - *pIndexOut = atoi(deviceIndexStr); - } - - return MA_SUCCESS; -} - - -#if !defined(MA_AUDIO4_USE_NEW_API) /* Old API */ -static ma_format ma_format_from_encoding__audio4(unsigned int encoding, unsigned int precision) -{ - if (precision == 8 && (encoding == AUDIO_ENCODING_ULINEAR || encoding == AUDIO_ENCODING_ULINEAR || encoding == AUDIO_ENCODING_ULINEAR_LE || encoding == AUDIO_ENCODING_ULINEAR_BE)) { - return ma_format_u8; - } else { - if (ma_is_little_endian() && encoding == AUDIO_ENCODING_SLINEAR_LE) { - if (precision == 16) { - return ma_format_s16; - } else if (precision == 24) { - return ma_format_s24; - } else if (precision == 32) { - return ma_format_s32; - } - } else if (ma_is_big_endian() && encoding == AUDIO_ENCODING_SLINEAR_BE) { - if (precision == 16) { - return ma_format_s16; - } else if (precision == 24) { - return ma_format_s24; - } else if (precision == 32) { - return ma_format_s32; - } - } - } - - return ma_format_unknown; /* Encoding not supported. */ -} - -static void ma_encoding_from_format__audio4(ma_format format, unsigned int* pEncoding, unsigned int* pPrecision) -{ - MA_ASSERT(pEncoding != NULL); - MA_ASSERT(pPrecision != NULL); - - switch (format) - { - case ma_format_u8: - { - *pEncoding = AUDIO_ENCODING_ULINEAR; - *pPrecision = 8; - } break; - - case ma_format_s24: - { - *pEncoding = (ma_is_little_endian()) ? AUDIO_ENCODING_SLINEAR_LE : AUDIO_ENCODING_SLINEAR_BE; - *pPrecision = 24; - } break; - - case ma_format_s32: - { - *pEncoding = (ma_is_little_endian()) ? AUDIO_ENCODING_SLINEAR_LE : AUDIO_ENCODING_SLINEAR_BE; - *pPrecision = 32; - } break; - - case ma_format_s16: - case ma_format_f32: - case ma_format_unknown: - default: - { - *pEncoding = (ma_is_little_endian()) ? AUDIO_ENCODING_SLINEAR_LE : AUDIO_ENCODING_SLINEAR_BE; - *pPrecision = 16; - } break; - } -} - -static ma_format ma_format_from_prinfo__audio4(struct audio_prinfo* prinfo) -{ - return ma_format_from_encoding__audio4(prinfo->encoding, prinfo->precision); -} - -static ma_format ma_best_format_from_fd__audio4(int fd, ma_format preferredFormat) -{ - audio_encoding_t encoding; - ma_uint32 iFormat; - int counter = 0; - - /* First check to see if the preferred format is supported. */ - if (preferredFormat != ma_format_unknown) { - counter = 0; - for (;;) { - MA_ZERO_OBJECT(&encoding); - encoding.index = counter; - if (ioctl(fd, AUDIO_GETENC, &encoding) < 0) { - break; - } - - if (preferredFormat == ma_format_from_encoding__audio4(encoding.encoding, encoding.precision)) { - return preferredFormat; /* Found the preferred format. */ - } - - /* Getting here means this encoding does not match our preferred format so we need to more on to the next encoding. */ - counter += 1; - } - } - - /* Getting here means our preferred format is not supported, so fall back to our standard priorities. */ - for (iFormat = 0; iFormat < ma_countof(g_maFormatPriorities); iFormat += 1) { - ma_format format = g_maFormatPriorities[iFormat]; - - counter = 0; - for (;;) { - MA_ZERO_OBJECT(&encoding); - encoding.index = counter; - if (ioctl(fd, AUDIO_GETENC, &encoding) < 0) { - break; - } - - if (format == ma_format_from_encoding__audio4(encoding.encoding, encoding.precision)) { - return format; /* Found a workable format. */ - } - - /* Getting here means this encoding does not match our preferred format so we need to more on to the next encoding. */ - counter += 1; - } - } - - /* Getting here means not appropriate format was found. */ - return ma_format_unknown; -} -#else -static ma_format ma_format_from_swpar__audio4(struct audio_swpar* par) -{ - if (par->bits == 8 && par->bps == 1 && par->sig == 0) { - return ma_format_u8; - } - if (par->bits == 16 && par->bps == 2 && par->sig == 1 && par->le == ma_is_little_endian()) { - return ma_format_s16; - } - if (par->bits == 24 && par->bps == 3 && par->sig == 1 && par->le == ma_is_little_endian()) { - return ma_format_s24; - } - if (par->bits == 32 && par->bps == 4 && par->sig == 1 && par->le == ma_is_little_endian()) { - return ma_format_f32; - } - - /* Format not supported. */ - return ma_format_unknown; -} -#endif - -static ma_result ma_context_get_device_info_from_fd__audio4(ma_context* pContext, ma_device_type deviceType, int fd, ma_device_info* pDeviceInfo) -{ - audio_device_t fdDevice; - - MA_ASSERT(pContext != NULL); - MA_ASSERT(fd >= 0); - MA_ASSERT(pDeviceInfo != NULL); - - (void)pContext; - (void)deviceType; - - if (ioctl(fd, AUDIO_GETDEV, &fdDevice) < 0) { - return MA_ERROR; /* Failed to retrieve device info. */ - } - - /* Name. */ - ma_strcpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), fdDevice.name); - - #if !defined(MA_AUDIO4_USE_NEW_API) - { - audio_info_t fdInfo; - int counter = 0; - ma_uint32 channels; - ma_uint32 sampleRate; - -#if defined(__NetBSD__) && (__NetBSD_Version__ >= 900000000) - if (ioctl(fd, AUDIO_GETFORMAT, &fdInfo) < 0) { - return MA_ERROR; - } -#else - if (ioctl(fd, AUDIO_GETINFO, &fdInfo) < 0) { - return MA_ERROR; - } -#endif - - if (deviceType == ma_device_type_playback) { - channels = fdInfo.play.channels; - sampleRate = fdInfo.play.sample_rate; - } else { - channels = fdInfo.record.channels; - sampleRate = fdInfo.record.sample_rate; - } - - /* Supported formats. We get this by looking at the encodings. */ - pDeviceInfo->nativeDataFormatCount = 0; - for (;;) { - audio_encoding_t encoding; - ma_format format; - - MA_ZERO_OBJECT(&encoding); - encoding.index = counter; - if (ioctl(fd, AUDIO_GETENC, &encoding) < 0) { - break; - } - - format = ma_format_from_encoding__audio4(encoding.encoding, encoding.precision); - if (format != ma_format_unknown) { - ma_device_info_add_native_data_format(pDeviceInfo, format, channels, sampleRate, 0); - } - - counter += 1; - } - } - #else - { - struct audio_swpar fdPar; - ma_format format; - ma_uint32 channels; - ma_uint32 sampleRate; - - if (ioctl(fd, AUDIO_GETPAR, &fdPar) < 0) { - return MA_ERROR; - } - - format = ma_format_from_swpar__audio4(&fdPar); - if (format == ma_format_unknown) { - return MA_FORMAT_NOT_SUPPORTED; - } - - if (deviceType == ma_device_type_playback) { - channels = fdPar.pchan; - } else { - channels = fdPar.rchan; - } - - sampleRate = fdPar.rate; - - pDeviceInfo->nativeDataFormatCount = 0; - ma_device_info_add_native_data_format(pDeviceInfo, format, channels, sampleRate, 0); - } - #endif - - return MA_SUCCESS; -} - -static ma_result ma_context_enumerate_devices__audio4(ma_context* pContext, ma_enum_devices_callback_proc callback, void* pUserData) -{ - const int maxDevices = 64; - char devpath[256]; - int iDevice; - - MA_ASSERT(pContext != NULL); - MA_ASSERT(callback != NULL); - - /* - Every device will be named "/dev/audioN", with a "/dev/audioctlN" equivalent. We use the "/dev/audioctlN" - version here since we can open it even when another process has control of the "/dev/audioN" device. - */ - for (iDevice = 0; iDevice < maxDevices; ++iDevice) { - struct stat st; - int fd; - ma_bool32 isTerminating = MA_FALSE; - - ma_strcpy_s(devpath, sizeof(devpath), "/dev/audioctl"); - ma_itoa_s(iDevice, devpath+strlen(devpath), sizeof(devpath)-strlen(devpath), 10); - - if (stat(devpath, &st) < 0) { - break; - } - - /* The device exists, but we need to check if it's usable as playback and/or capture. */ - - /* Playback. */ - if (!isTerminating) { - fd = open(devpath, O_RDONLY, 0); - if (fd >= 0) { - /* Supports playback. */ - ma_device_info deviceInfo; - MA_ZERO_OBJECT(&deviceInfo); - ma_construct_device_id__audio4(deviceInfo.id.audio4, sizeof(deviceInfo.id.audio4), "/dev/audio", iDevice); - if (ma_context_get_device_info_from_fd__audio4(pContext, ma_device_type_playback, fd, &deviceInfo) == MA_SUCCESS) { - isTerminating = !callback(pContext, ma_device_type_playback, &deviceInfo, pUserData); - } - - close(fd); - } - } - - /* Capture. */ - if (!isTerminating) { - fd = open(devpath, O_WRONLY, 0); - if (fd >= 0) { - /* Supports capture. */ - ma_device_info deviceInfo; - MA_ZERO_OBJECT(&deviceInfo); - ma_construct_device_id__audio4(deviceInfo.id.audio4, sizeof(deviceInfo.id.audio4), "/dev/audio", iDevice); - if (ma_context_get_device_info_from_fd__audio4(pContext, ma_device_type_capture, fd, &deviceInfo) == MA_SUCCESS) { - isTerminating = !callback(pContext, ma_device_type_capture, &deviceInfo, pUserData); - } - - close(fd); - } - } - - if (isTerminating) { - break; - } - } - - return MA_SUCCESS; -} - -static ma_result ma_context_get_device_info__audio4(ma_context* pContext, ma_device_type deviceType, const ma_device_id* pDeviceID, ma_device_info* pDeviceInfo) -{ - int fd = -1; - int deviceIndex = -1; - char ctlid[256]; - ma_result result; - - MA_ASSERT(pContext != NULL); - - /* - We need to open the "/dev/audioctlN" device to get the info. To do this we need to extract the number - from the device ID which will be in "/dev/audioN" format. - */ - if (pDeviceID == NULL) { - /* Default device. */ - ma_strcpy_s(ctlid, sizeof(ctlid), "/dev/audioctl"); - } else { - /* Specific device. We need to convert from "/dev/audioN" to "/dev/audioctlN". */ - result = ma_extract_device_index_from_id__audio4(pDeviceID->audio4, "/dev/audio", &deviceIndex); - if (result != MA_SUCCESS) { - return result; - } - - ma_construct_device_id__audio4(ctlid, sizeof(ctlid), "/dev/audioctl", deviceIndex); - } - - fd = open(ctlid, (deviceType == ma_device_type_playback) ? O_WRONLY : O_RDONLY, 0); - if (fd == -1) { - return MA_NO_DEVICE; - } - - if (deviceIndex == -1) { - ma_strcpy_s(pDeviceInfo->id.audio4, sizeof(pDeviceInfo->id.audio4), "/dev/audio"); - } else { - ma_construct_device_id__audio4(pDeviceInfo->id.audio4, sizeof(pDeviceInfo->id.audio4), "/dev/audio", deviceIndex); - } - - result = ma_context_get_device_info_from_fd__audio4(pContext, deviceType, fd, pDeviceInfo); - - close(fd); - return result; -} - -static ma_result ma_device_uninit__audio4(ma_device* pDevice) -{ - MA_ASSERT(pDevice != NULL); - - if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) { - close(pDevice->audio4.fdCapture); - } - - if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) { - close(pDevice->audio4.fdPlayback); - } - - return MA_SUCCESS; -} - -static ma_result ma_device_init_fd__audio4(ma_device* pDevice, const ma_device_config* pConfig, ma_device_descriptor* pDescriptor, ma_device_type deviceType) -{ - const char* pDefaultDeviceNames[] = { - "/dev/audio", - "/dev/audio0" - }; - const char* pDefaultDeviceCtlNames[] = { - "/dev/audioctl", - "/dev/audioctl0" - }; - int fd; - int fdFlags = 0; - size_t iDefaultDevice = (size_t)-1; - ma_format internalFormat; - ma_uint32 internalChannels; - ma_uint32 internalSampleRate; - ma_uint32 internalPeriodSizeInFrames; - ma_uint32 internalPeriods; - - MA_ASSERT(pConfig != NULL); - MA_ASSERT(deviceType != ma_device_type_duplex); - MA_ASSERT(pDevice != NULL); - - /* The first thing to do is open the file. */ - if (deviceType == ma_device_type_capture) { - fdFlags = O_RDONLY; - } else { - fdFlags = O_WRONLY; - } - /*fdFlags |= O_NONBLOCK;*/ - - /* Find the index of the default device as a start. We'll use this index later. Set it to (size_t)-1 otherwise. */ - if (pDescriptor->pDeviceID == NULL) { - /* Default device. */ - for (iDefaultDevice = 0; iDefaultDevice < ma_countof(pDefaultDeviceNames); ++iDefaultDevice) { - fd = open(pDefaultDeviceNames[iDefaultDevice], fdFlags, 0); - if (fd != -1) { - break; - } - } - } else { - /* Specific device. */ - fd = open(pDescriptor->pDeviceID->audio4, fdFlags, 0); - - for (iDefaultDevice = 0; iDefaultDevice < ma_countof(pDefaultDeviceNames); iDefaultDevice += 1) { - if (ma_strcmp(pDefaultDeviceNames[iDefaultDevice], pDescriptor->pDeviceID->audio4) == 0) { - break; - } - } - - if (iDefaultDevice == ma_countof(pDefaultDeviceNames)) { - iDefaultDevice = (size_t)-1; - } - } - - if (fd == -1) { - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[audio4] Failed to open device."); - return ma_result_from_errno(errno); - } - - #if !defined(MA_AUDIO4_USE_NEW_API) /* Old API */ - { - audio_info_t fdInfo; - int fdInfoResult = -1; - - /* - The documentation is a little bit unclear to me as to how it handles formats. It says the - following: - - Regardless of formats supported by underlying driver, the audio driver accepts the - following formats. - - By then the next sentence says this: - - `encoding` and `precision` are one of the values obtained by AUDIO_GETENC. - - It sounds like a direct contradiction to me. I'm going to play this safe any only use the - best sample format returned by AUDIO_GETENC. If the requested format is supported we'll - use that, but otherwise we'll just use our standard format priorities to pick an - appropriate one. - */ - AUDIO_INITINFO(&fdInfo); - - /* - Get the default format from the audioctl file if we're asking for a default device. If we - retrieve it from /dev/audio it'll default to mono 8000Hz. - */ - if (iDefaultDevice != (size_t)-1) { - /* We're using a default device. Get the info from the /dev/audioctl file instead of /dev/audio. */ - int fdctl = open(pDefaultDeviceCtlNames[iDefaultDevice], fdFlags, 0); - if (fdctl != -1) { -#if defined(__NetBSD__) && (__NetBSD_Version__ >= 900000000) - fdInfoResult = ioctl(fdctl, AUDIO_GETFORMAT, &fdInfo); -#else - fdInfoResult = ioctl(fdctl, AUDIO_GETINFO, &fdInfo); -#endif - close(fdctl); - } - } - - if (fdInfoResult == -1) { - /* We still don't have the default device info so just retrieve it from the main audio device. */ - if (ioctl(fd, AUDIO_GETINFO, &fdInfo) < 0) { - close(fd); - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[audio4] AUDIO_GETINFO failed."); - return ma_result_from_errno(errno); - } - } - - /* We get the driver to do as much of the data conversion as possible. */ - if (deviceType == ma_device_type_capture) { - fdInfo.mode = AUMODE_RECORD; - ma_encoding_from_format__audio4(ma_best_format_from_fd__audio4(fd, pDescriptor->format), &fdInfo.record.encoding, &fdInfo.record.precision); - - if (pDescriptor->channels != 0) { - fdInfo.record.channels = ma_clamp(pDescriptor->channels, 1, 12); /* From the documentation: `channels` ranges from 1 to 12. */ - } - - if (pDescriptor->sampleRate != 0) { - fdInfo.record.sample_rate = ma_clamp(pDescriptor->sampleRate, 1000, 192000); /* From the documentation: `frequency` ranges from 1000Hz to 192000Hz. (They mean `sample_rate` instead of `frequency`.) */ - } - } else { - fdInfo.mode = AUMODE_PLAY; - ma_encoding_from_format__audio4(ma_best_format_from_fd__audio4(fd, pDescriptor->format), &fdInfo.play.encoding, &fdInfo.play.precision); - - if (pDescriptor->channels != 0) { - fdInfo.play.channels = ma_clamp(pDescriptor->channels, 1, 12); /* From the documentation: `channels` ranges from 1 to 12. */ - } - - if (pDescriptor->sampleRate != 0) { - fdInfo.play.sample_rate = ma_clamp(pDescriptor->sampleRate, 1000, 192000); /* From the documentation: `frequency` ranges from 1000Hz to 192000Hz. (They mean `sample_rate` instead of `frequency`.) */ - } - } - - if (ioctl(fd, AUDIO_SETINFO, &fdInfo) < 0) { - close(fd); - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[audio4] Failed to set device format. AUDIO_SETINFO failed."); - return ma_result_from_errno(errno); - } - - if (ioctl(fd, AUDIO_GETINFO, &fdInfo) < 0) { - close(fd); - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[audio4] AUDIO_GETINFO failed."); - return ma_result_from_errno(errno); - } - - if (deviceType == ma_device_type_capture) { - internalFormat = ma_format_from_prinfo__audio4(&fdInfo.record); - internalChannels = fdInfo.record.channels; - internalSampleRate = fdInfo.record.sample_rate; - } else { - internalFormat = ma_format_from_prinfo__audio4(&fdInfo.play); - internalChannels = fdInfo.play.channels; - internalSampleRate = fdInfo.play.sample_rate; - } - - if (internalFormat == ma_format_unknown) { - close(fd); - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[audio4] The device's internal device format is not supported by miniaudio. The device is unusable."); - return MA_FORMAT_NOT_SUPPORTED; - } - - /* Buffer. */ - { - ma_uint32 internalPeriodSizeInBytes; - - internalPeriodSizeInFrames = ma_calculate_buffer_size_in_frames_from_descriptor(pDescriptor, internalSampleRate, pConfig->performanceProfile); - - internalPeriodSizeInBytes = internalPeriodSizeInFrames * ma_get_bytes_per_frame(internalFormat, internalChannels); - if (internalPeriodSizeInBytes < 16) { - internalPeriodSizeInBytes = 16; - } - - internalPeriods = pDescriptor->periodCount; - if (internalPeriods < 2) { - internalPeriods = 2; - } - - /* What miniaudio calls a period, audio4 calls a block. */ - AUDIO_INITINFO(&fdInfo); - fdInfo.hiwat = internalPeriods; - fdInfo.lowat = internalPeriods-1; - fdInfo.blocksize = internalPeriodSizeInBytes; - if (ioctl(fd, AUDIO_SETINFO, &fdInfo) < 0) { - close(fd); - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[audio4] Failed to set internal buffer size. AUDIO_SETINFO failed."); - return ma_result_from_errno(errno); - } - - internalPeriods = fdInfo.hiwat; - internalPeriodSizeInFrames = fdInfo.blocksize / ma_get_bytes_per_frame(internalFormat, internalChannels); - } - } - #else - { - struct audio_swpar fdPar; - - /* We need to retrieve the format of the device so we can know the channel count and sample rate. Then we can calculate the buffer size. */ - if (ioctl(fd, AUDIO_GETPAR, &fdPar) < 0) { - close(fd); - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[audio4] Failed to retrieve initial device parameters."); - return ma_result_from_errno(errno); - } - - internalFormat = ma_format_from_swpar__audio4(&fdPar); - internalChannels = (deviceType == ma_device_type_capture) ? fdPar.rchan : fdPar.pchan; - internalSampleRate = fdPar.rate; - - if (internalFormat == ma_format_unknown) { - close(fd); - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[audio4] The device's internal device format is not supported by miniaudio. The device is unusable."); - return MA_FORMAT_NOT_SUPPORTED; - } - - /* Buffer. */ - { - ma_uint32 internalPeriodSizeInBytes; - - internalPeriodSizeInFrames = ma_calculate_buffer_size_in_frames_from_descriptor(pDescriptor, internalSampleRate, pConfig->performanceProfile); - - /* What miniaudio calls a period, audio4 calls a block. */ - internalPeriodSizeInBytes = internalPeriodSizeInFrames * ma_get_bytes_per_frame(internalFormat, internalChannels); - if (internalPeriodSizeInBytes < 16) { - internalPeriodSizeInBytes = 16; - } - - fdPar.nblks = pDescriptor->periodCount; - fdPar.round = internalPeriodSizeInBytes; - - if (ioctl(fd, AUDIO_SETPAR, &fdPar) < 0) { - close(fd); - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[audio4] Failed to set device parameters."); - return ma_result_from_errno(errno); - } - - if (ioctl(fd, AUDIO_GETPAR, &fdPar) < 0) { - close(fd); - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[audio4] Failed to retrieve actual device parameters."); - return ma_result_from_errno(errno); - } - } - - internalFormat = ma_format_from_swpar__audio4(&fdPar); - internalChannels = (deviceType == ma_device_type_capture) ? fdPar.rchan : fdPar.pchan; - internalSampleRate = fdPar.rate; - internalPeriods = fdPar.nblks; - internalPeriodSizeInFrames = fdPar.round / ma_get_bytes_per_frame(internalFormat, internalChannels); - } - #endif - - if (internalFormat == ma_format_unknown) { - close(fd); - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[audio4] The device's internal device format is not supported by miniaudio. The device is unusable."); - return MA_FORMAT_NOT_SUPPORTED; - } - - if (deviceType == ma_device_type_capture) { - pDevice->audio4.fdCapture = fd; - } else { - pDevice->audio4.fdPlayback = fd; - } - - pDescriptor->format = internalFormat; - pDescriptor->channels = internalChannels; - pDescriptor->sampleRate = internalSampleRate; - ma_channel_map_init_standard(ma_standard_channel_map_sound4, pDescriptor->channelMap, ma_countof(pDescriptor->channelMap), internalChannels); - pDescriptor->periodSizeInFrames = internalPeriodSizeInFrames; - pDescriptor->periodCount = internalPeriods; - - return MA_SUCCESS; -} - -static ma_result ma_device_init__audio4(ma_device* pDevice, const ma_device_config* pConfig, ma_device_descriptor* pDescriptorPlayback, ma_device_descriptor* pDescriptorCapture) -{ - MA_ASSERT(pDevice != NULL); - - MA_ZERO_OBJECT(&pDevice->audio4); - - if (pConfig->deviceType == ma_device_type_loopback) { - return MA_DEVICE_TYPE_NOT_SUPPORTED; - } - - pDevice->audio4.fdCapture = -1; - pDevice->audio4.fdPlayback = -1; - - /* - The version of the operating system dictates whether or not the device is exclusive or shared. NetBSD - introduced in-kernel mixing which means it's shared. All other BSD flavours are exclusive as far as - I'm aware. - */ -#if defined(__NetBSD_Version__) && __NetBSD_Version__ >= 800000000 - /* NetBSD 8.0+ */ - if (((pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) && pDescriptorPlayback->shareMode == ma_share_mode_exclusive) || - ((pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) && pDescriptorCapture->shareMode == ma_share_mode_exclusive)) { - return MA_SHARE_MODE_NOT_SUPPORTED; - } -#else - /* All other flavors. */ -#endif - - if (pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) { - ma_result result = ma_device_init_fd__audio4(pDevice, pConfig, pDescriptorCapture, ma_device_type_capture); - if (result != MA_SUCCESS) { - return result; - } - } - - if (pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) { - ma_result result = ma_device_init_fd__audio4(pDevice, pConfig, pDescriptorPlayback, ma_device_type_playback); - if (result != MA_SUCCESS) { - if (pConfig->deviceType == ma_device_type_duplex) { - close(pDevice->audio4.fdCapture); - } - return result; - } - } - - return MA_SUCCESS; -} - -static ma_result ma_device_start__audio4(ma_device* pDevice) -{ - MA_ASSERT(pDevice != NULL); - - if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) { - if (pDevice->audio4.fdCapture == -1) { - return MA_INVALID_ARGS; - } - } - - if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) { - if (pDevice->audio4.fdPlayback == -1) { - return MA_INVALID_ARGS; - } - } - - return MA_SUCCESS; -} - -static ma_result ma_device_stop_fd__audio4(ma_device* pDevice, int fd) -{ - if (fd == -1) { - return MA_INVALID_ARGS; - } - -#if !defined(MA_AUDIO4_USE_NEW_API) - if (ioctl(fd, AUDIO_FLUSH, 0) < 0) { - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[audio4] Failed to stop device. AUDIO_FLUSH failed."); - return ma_result_from_errno(errno); - } -#else - if (ioctl(fd, AUDIO_STOP, 0) < 0) { - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[audio4] Failed to stop device. AUDIO_STOP failed."); - return ma_result_from_errno(errno); - } -#endif - - return MA_SUCCESS; -} - -static ma_result ma_device_stop__audio4(ma_device* pDevice) -{ - MA_ASSERT(pDevice != NULL); - - if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) { - ma_result result; - - result = ma_device_stop_fd__audio4(pDevice, pDevice->audio4.fdCapture); - if (result != MA_SUCCESS) { - return result; - } - } - - if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) { - ma_result result; - - /* Drain the device first. If this fails we'll just need to flush without draining. Unfortunately draining isn't available on newer version of OpenBSD. */ - #if !defined(MA_AUDIO4_USE_NEW_API) - ioctl(pDevice->audio4.fdPlayback, AUDIO_DRAIN, 0); - #endif - - /* Here is where the device is stopped immediately. */ - result = ma_device_stop_fd__audio4(pDevice, pDevice->audio4.fdPlayback); - if (result != MA_SUCCESS) { - return result; - } - } - - return MA_SUCCESS; -} - -static ma_result ma_device_write__audio4(ma_device* pDevice, const void* pPCMFrames, ma_uint32 frameCount, ma_uint32* pFramesWritten) -{ - int result; - - if (pFramesWritten != NULL) { - *pFramesWritten = 0; - } - - result = write(pDevice->audio4.fdPlayback, pPCMFrames, frameCount * ma_get_bytes_per_frame(pDevice->playback.internalFormat, pDevice->playback.internalChannels)); - if (result < 0) { - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[audio4] Failed to write data to the device."); - return ma_result_from_errno(errno); - } - - if (pFramesWritten != NULL) { - *pFramesWritten = (ma_uint32)result / ma_get_bytes_per_frame(pDevice->playback.internalFormat, pDevice->playback.internalChannels); - } - - return MA_SUCCESS; -} - -static ma_result ma_device_read__audio4(ma_device* pDevice, void* pPCMFrames, ma_uint32 frameCount, ma_uint32* pFramesRead) -{ - int result; - - if (pFramesRead != NULL) { - *pFramesRead = 0; - } - - result = read(pDevice->audio4.fdCapture, pPCMFrames, frameCount * ma_get_bytes_per_frame(pDevice->capture.internalFormat, pDevice->capture.internalChannels)); - if (result < 0) { - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[audio4] Failed to read data from the device."); - return ma_result_from_errno(errno); - } - - if (pFramesRead != NULL) { - *pFramesRead = (ma_uint32)result / ma_get_bytes_per_frame(pDevice->capture.internalFormat, pDevice->capture.internalChannels); - } - - return MA_SUCCESS; -} - -static ma_result ma_context_uninit__audio4(ma_context* pContext) -{ - MA_ASSERT(pContext != NULL); - MA_ASSERT(pContext->backend == ma_backend_audio4); - - (void)pContext; - return MA_SUCCESS; -} - -static ma_result ma_context_init__audio4(ma_context* pContext, const ma_context_config* pConfig, ma_backend_callbacks* pCallbacks) -{ - MA_ASSERT(pContext != NULL); - - (void)pConfig; - - pCallbacks->onContextInit = ma_context_init__audio4; - pCallbacks->onContextUninit = ma_context_uninit__audio4; - pCallbacks->onContextEnumerateDevices = ma_context_enumerate_devices__audio4; - pCallbacks->onContextGetDeviceInfo = ma_context_get_device_info__audio4; - pCallbacks->onDeviceInit = ma_device_init__audio4; - pCallbacks->onDeviceUninit = ma_device_uninit__audio4; - pCallbacks->onDeviceStart = ma_device_start__audio4; - pCallbacks->onDeviceStop = ma_device_stop__audio4; - pCallbacks->onDeviceRead = ma_device_read__audio4; - pCallbacks->onDeviceWrite = ma_device_write__audio4; - pCallbacks->onDeviceDataLoop = NULL; - - return MA_SUCCESS; -} -#endif /* MA_HAS_AUDIO4 */ - - -/****************************************************************************** - -OSS Backend - -******************************************************************************/ -#ifdef MA_HAS_OSS -#include -#include -#include -#include - -#ifndef SNDCTL_DSP_HALT -#define SNDCTL_DSP_HALT SNDCTL_DSP_RESET -#endif - -#define MA_OSS_DEFAULT_DEVICE_NAME "/dev/dsp" - -static int ma_open_temp_device__oss() -{ - /* The OSS sample code uses "/dev/mixer" as the device for getting system properties so I'm going to do the same. */ - int fd = open("/dev/mixer", O_RDONLY, 0); - if (fd >= 0) { - return fd; - } - - return -1; -} - -static ma_result ma_context_open_device__oss(ma_context* pContext, ma_device_type deviceType, const ma_device_id* pDeviceID, ma_share_mode shareMode, int* pfd) -{ - const char* deviceName; - int flags; - - MA_ASSERT(pContext != NULL); - MA_ASSERT(pfd != NULL); - (void)pContext; - - *pfd = -1; - - /* This function should only be called for playback or capture, not duplex. */ - if (deviceType == ma_device_type_duplex) { - return MA_INVALID_ARGS; - } - - deviceName = MA_OSS_DEFAULT_DEVICE_NAME; - if (pDeviceID != NULL) { - deviceName = pDeviceID->oss; - } - - flags = (deviceType == ma_device_type_playback) ? O_WRONLY : O_RDONLY; - if (shareMode == ma_share_mode_exclusive) { - flags |= O_EXCL; - } - - *pfd = open(deviceName, flags, 0); - if (*pfd == -1) { - return ma_result_from_errno(errno); - } - - return MA_SUCCESS; -} - -static ma_result ma_context_enumerate_devices__oss(ma_context* pContext, ma_enum_devices_callback_proc callback, void* pUserData) -{ - int fd; - oss_sysinfo si; - int result; - - MA_ASSERT(pContext != NULL); - MA_ASSERT(callback != NULL); - - fd = ma_open_temp_device__oss(); - if (fd == -1) { - ma_log_post(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[OSS] Failed to open a temporary device for retrieving system information used for device enumeration."); - return MA_NO_BACKEND; - } - - result = ioctl(fd, SNDCTL_SYSINFO, &si); - if (result != -1) { - int iAudioDevice; - for (iAudioDevice = 0; iAudioDevice < si.numaudios; ++iAudioDevice) { - oss_audioinfo ai; - ai.dev = iAudioDevice; - result = ioctl(fd, SNDCTL_AUDIOINFO, &ai); - if (result != -1) { - if (ai.devnode[0] != '\0') { /* <-- Can be blank, according to documentation. */ - ma_device_info deviceInfo; - ma_bool32 isTerminating = MA_FALSE; - - MA_ZERO_OBJECT(&deviceInfo); - - /* ID */ - ma_strncpy_s(deviceInfo.id.oss, sizeof(deviceInfo.id.oss), ai.devnode, (size_t)-1); - - /* - The human readable device name should be in the "ai.handle" variable, but it can - sometimes be empty in which case we just fall back to "ai.name" which is less user - friendly, but usually has a value. - */ - if (ai.handle[0] != '\0') { - ma_strncpy_s(deviceInfo.name, sizeof(deviceInfo.name), ai.handle, (size_t)-1); - } else { - ma_strncpy_s(deviceInfo.name, sizeof(deviceInfo.name), ai.name, (size_t)-1); - } - - /* The device can be both playback and capture. */ - if (!isTerminating && (ai.caps & PCM_CAP_OUTPUT) != 0) { - isTerminating = !callback(pContext, ma_device_type_playback, &deviceInfo, pUserData); - } - if (!isTerminating && (ai.caps & PCM_CAP_INPUT) != 0) { - isTerminating = !callback(pContext, ma_device_type_capture, &deviceInfo, pUserData); - } - - if (isTerminating) { - break; - } - } - } - } - } else { - close(fd); - ma_log_post(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[OSS] Failed to retrieve system information for device enumeration."); - return MA_NO_BACKEND; - } - - close(fd); - return MA_SUCCESS; -} - -static void ma_context_add_native_data_format__oss(ma_context* pContext, oss_audioinfo* pAudioInfo, ma_format format, ma_device_info* pDeviceInfo) -{ - unsigned int minChannels; - unsigned int maxChannels; - unsigned int iRate; - - MA_ASSERT(pContext != NULL); - MA_ASSERT(pAudioInfo != NULL); - MA_ASSERT(pDeviceInfo != NULL); - - /* If we support all channels we just report 0. */ - minChannels = ma_clamp(pAudioInfo->min_channels, MA_MIN_CHANNELS, MA_MAX_CHANNELS); - maxChannels = ma_clamp(pAudioInfo->max_channels, MA_MIN_CHANNELS, MA_MAX_CHANNELS); - - /* - OSS has this annoying thing where sample rates can be reported in two ways. We prefer explicitness, - which OSS has in the form of nrates/rates, however there are times where nrates can be 0, in which - case we'll need to use min_rate and max_rate and report only standard rates. - */ - if (pAudioInfo->nrates > 0) { - for (iRate = 0; iRate < pAudioInfo->nrates; iRate += 1) { - unsigned int rate = pAudioInfo->rates[iRate]; - - if (minChannels == MA_MIN_CHANNELS && maxChannels == MA_MAX_CHANNELS) { - ma_device_info_add_native_data_format(pDeviceInfo, format, 0, rate, 0); /* Set the channel count to 0 to indicate that all channel counts are supported. */ - } else { - unsigned int iChannel; - for (iChannel = minChannels; iChannel <= maxChannels; iChannel += 1) { - ma_device_info_add_native_data_format(pDeviceInfo, format, iChannel, rate, 0); - } - } - } - } else { - for (iRate = 0; iRate < ma_countof(g_maStandardSampleRatePriorities); iRate += 1) { - ma_uint32 standardRate = g_maStandardSampleRatePriorities[iRate]; - - if (standardRate >= (ma_uint32)pAudioInfo->min_rate && standardRate <= (ma_uint32)pAudioInfo->max_rate) { - if (minChannels == MA_MIN_CHANNELS && maxChannels == MA_MAX_CHANNELS) { - ma_device_info_add_native_data_format(pDeviceInfo, format, 0, standardRate, 0); /* Set the channel count to 0 to indicate that all channel counts are supported. */ - } else { - unsigned int iChannel; - for (iChannel = minChannels; iChannel <= maxChannels; iChannel += 1) { - ma_device_info_add_native_data_format(pDeviceInfo, format, iChannel, standardRate, 0); - } - } - } - } - } -} - -static ma_result ma_context_get_device_info__oss(ma_context* pContext, ma_device_type deviceType, const ma_device_id* pDeviceID, ma_device_info* pDeviceInfo) -{ - ma_bool32 foundDevice; - int fdTemp; - oss_sysinfo si; - int result; - - MA_ASSERT(pContext != NULL); - - /* Handle the default device a little differently. */ - if (pDeviceID == NULL) { - if (deviceType == ma_device_type_playback) { - ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), MA_DEFAULT_PLAYBACK_DEVICE_NAME, (size_t)-1); - } else { - ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), MA_DEFAULT_CAPTURE_DEVICE_NAME, (size_t)-1); - } - - return MA_SUCCESS; - } - - - /* If we get here it means we are _not_ using the default device. */ - foundDevice = MA_FALSE; - - fdTemp = ma_open_temp_device__oss(); - if (fdTemp == -1) { - ma_log_post(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[OSS] Failed to open a temporary device for retrieving system information used for device enumeration."); - return MA_NO_BACKEND; - } - - result = ioctl(fdTemp, SNDCTL_SYSINFO, &si); - if (result != -1) { - int iAudioDevice; - for (iAudioDevice = 0; iAudioDevice < si.numaudios; ++iAudioDevice) { - oss_audioinfo ai; - ai.dev = iAudioDevice; - result = ioctl(fdTemp, SNDCTL_AUDIOINFO, &ai); - if (result != -1) { - if (ma_strcmp(ai.devnode, pDeviceID->oss) == 0) { - /* It has the same name, so now just confirm the type. */ - if ((deviceType == ma_device_type_playback && ((ai.caps & PCM_CAP_OUTPUT) != 0)) || - (deviceType == ma_device_type_capture && ((ai.caps & PCM_CAP_INPUT) != 0))) { - unsigned int formatMask; - - /* ID */ - ma_strncpy_s(pDeviceInfo->id.oss, sizeof(pDeviceInfo->id.oss), ai.devnode, (size_t)-1); - - /* - The human readable device name should be in the "ai.handle" variable, but it can - sometimes be empty in which case we just fall back to "ai.name" which is less user - friendly, but usually has a value. - */ - if (ai.handle[0] != '\0') { - ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), ai.handle, (size_t)-1); - } else { - ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), ai.name, (size_t)-1); - } - - - pDeviceInfo->nativeDataFormatCount = 0; - - if (deviceType == ma_device_type_playback) { - formatMask = ai.oformats; - } else { - formatMask = ai.iformats; - } - - if (((formatMask & AFMT_S16_LE) != 0 && ma_is_little_endian()) || (AFMT_S16_BE && ma_is_big_endian())) { - ma_context_add_native_data_format__oss(pContext, &ai, ma_format_s16, pDeviceInfo); - } - if (((formatMask & AFMT_S32_LE) != 0 && ma_is_little_endian()) || (AFMT_S32_BE && ma_is_big_endian())) { - ma_context_add_native_data_format__oss(pContext, &ai, ma_format_s32, pDeviceInfo); - } - if ((formatMask & AFMT_U8) != 0) { - ma_context_add_native_data_format__oss(pContext, &ai, ma_format_u8, pDeviceInfo); - } - - foundDevice = MA_TRUE; - break; - } - } - } - } - } else { - close(fdTemp); - ma_log_post(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[OSS] Failed to retrieve system information for device enumeration."); - return MA_NO_BACKEND; - } - - - close(fdTemp); - - if (!foundDevice) { - return MA_NO_DEVICE; - } - - return MA_SUCCESS; -} - -static ma_result ma_device_uninit__oss(ma_device* pDevice) -{ - MA_ASSERT(pDevice != NULL); - - if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) { - close(pDevice->oss.fdCapture); - } - - if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) { - close(pDevice->oss.fdPlayback); - } - - return MA_SUCCESS; -} - -static int ma_format_to_oss(ma_format format) -{ - int ossFormat = AFMT_U8; - switch (format) { - case ma_format_s16: ossFormat = (ma_is_little_endian()) ? AFMT_S16_LE : AFMT_S16_BE; break; - case ma_format_s24: ossFormat = (ma_is_little_endian()) ? AFMT_S32_LE : AFMT_S32_BE; break; - case ma_format_s32: ossFormat = (ma_is_little_endian()) ? AFMT_S32_LE : AFMT_S32_BE; break; - case ma_format_f32: ossFormat = (ma_is_little_endian()) ? AFMT_S16_LE : AFMT_S16_BE; break; - case ma_format_u8: - default: ossFormat = AFMT_U8; break; - } - - return ossFormat; -} - -static ma_format ma_format_from_oss(int ossFormat) -{ - if (ossFormat == AFMT_U8) { - return ma_format_u8; - } else { - if (ma_is_little_endian()) { - switch (ossFormat) { - case AFMT_S16_LE: return ma_format_s16; - case AFMT_S32_LE: return ma_format_s32; - default: return ma_format_unknown; - } - } else { - switch (ossFormat) { - case AFMT_S16_BE: return ma_format_s16; - case AFMT_S32_BE: return ma_format_s32; - default: return ma_format_unknown; - } - } - } - - return ma_format_unknown; -} - -static ma_result ma_device_init_fd__oss(ma_device* pDevice, const ma_device_config* pConfig, ma_device_descriptor* pDescriptor, ma_device_type deviceType) -{ - ma_result result; - int ossResult; - int fd; - const ma_device_id* pDeviceID = NULL; - ma_share_mode shareMode; - int ossFormat; - int ossChannels; - int ossSampleRate; - int ossFragment; - - MA_ASSERT(pDevice != NULL); - MA_ASSERT(pConfig != NULL); - MA_ASSERT(deviceType != ma_device_type_duplex); - - pDeviceID = pDescriptor->pDeviceID; - shareMode = pDescriptor->shareMode; - ossFormat = ma_format_to_oss((pDescriptor->format != ma_format_unknown) ? pDescriptor->format : ma_format_s16); /* Use s16 by default because OSS doesn't like floating point. */ - ossChannels = (int)(pDescriptor->channels > 0) ? pDescriptor->channels : MA_DEFAULT_CHANNELS; - ossSampleRate = (int)(pDescriptor->sampleRate > 0) ? pDescriptor->sampleRate : MA_DEFAULT_SAMPLE_RATE; - - result = ma_context_open_device__oss(pDevice->pContext, deviceType, pDeviceID, shareMode, &fd); - if (result != MA_SUCCESS) { - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[OSS] Failed to open device."); - return result; - } - - /* - The OSS documentation is very clear about the order we should be initializing the device's properties: - 1) Format - 2) Channels - 3) Sample rate. - */ - - /* Format. */ - ossResult = ioctl(fd, SNDCTL_DSP_SETFMT, &ossFormat); - if (ossResult == -1) { - close(fd); - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[OSS] Failed to set format."); - return ma_result_from_errno(errno); - } - - /* Channels. */ - ossResult = ioctl(fd, SNDCTL_DSP_CHANNELS, &ossChannels); - if (ossResult == -1) { - close(fd); - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[OSS] Failed to set channel count."); - return ma_result_from_errno(errno); - } - - /* Sample Rate. */ - ossResult = ioctl(fd, SNDCTL_DSP_SPEED, &ossSampleRate); - if (ossResult == -1) { - close(fd); - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[OSS] Failed to set sample rate."); - return ma_result_from_errno(errno); - } - - /* - Buffer. - - The documentation says that the fragment settings should be set as soon as possible, but I'm not sure if - it should be done before or after format/channels/rate. - - OSS wants the fragment size in bytes and a power of 2. When setting, we specify the power, not the actual - value. - */ - { - ma_uint32 periodSizeInFrames; - ma_uint32 periodSizeInBytes; - ma_uint32 ossFragmentSizePower; - - periodSizeInFrames = ma_calculate_buffer_size_in_frames_from_descriptor(pDescriptor, (ma_uint32)ossSampleRate, pConfig->performanceProfile); - - periodSizeInBytes = ma_round_to_power_of_2(periodSizeInFrames * ma_get_bytes_per_frame(ma_format_from_oss(ossFormat), ossChannels)); - if (periodSizeInBytes < 16) { - periodSizeInBytes = 16; - } - - ossFragmentSizePower = 4; - periodSizeInBytes >>= 4; - while (periodSizeInBytes >>= 1) { - ossFragmentSizePower += 1; - } - - ossFragment = (int)((pConfig->periods << 16) | ossFragmentSizePower); - ossResult = ioctl(fd, SNDCTL_DSP_SETFRAGMENT, &ossFragment); - if (ossResult == -1) { - close(fd); - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[OSS] Failed to set fragment size and period count."); - return ma_result_from_errno(errno); - } - } - - /* Internal settings. */ - if (deviceType == ma_device_type_capture) { - pDevice->oss.fdCapture = fd; - } else { - pDevice->oss.fdPlayback = fd; - } - - pDescriptor->format = ma_format_from_oss(ossFormat); - pDescriptor->channels = ossChannels; - pDescriptor->sampleRate = ossSampleRate; - ma_channel_map_init_standard(ma_standard_channel_map_sound4, pDescriptor->channelMap, ma_countof(pDescriptor->channelMap), pDescriptor->channels); - pDescriptor->periodCount = (ma_uint32)(ossFragment >> 16); - pDescriptor->periodSizeInFrames = (ma_uint32)(1 << (ossFragment & 0xFFFF)) / ma_get_bytes_per_frame(pDescriptor->format, pDescriptor->channels); - - if (pDescriptor->format == ma_format_unknown) { - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[OSS] The device's internal format is not supported by miniaudio."); - return MA_FORMAT_NOT_SUPPORTED; - } - - return MA_SUCCESS; -} - -static ma_result ma_device_init__oss(ma_device* pDevice, const ma_device_config* pConfig, ma_device_descriptor* pDescriptorPlayback, ma_device_descriptor* pDescriptorCapture) -{ - MA_ASSERT(pDevice != NULL); - MA_ASSERT(pConfig != NULL); - - MA_ZERO_OBJECT(&pDevice->oss); - - if (pConfig->deviceType == ma_device_type_loopback) { - return MA_DEVICE_TYPE_NOT_SUPPORTED; - } - - if (pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) { - ma_result result = ma_device_init_fd__oss(pDevice, pConfig, pDescriptorCapture, ma_device_type_capture); - if (result != MA_SUCCESS) { - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[OSS] Failed to open device."); - return result; - } - } - - if (pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) { - ma_result result = ma_device_init_fd__oss(pDevice, pConfig, pDescriptorPlayback, ma_device_type_playback); - if (result != MA_SUCCESS) { - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[OSS] Failed to open device."); - return result; - } - } - - return MA_SUCCESS; -} - -/* -Note on Starting and Stopping -============================= -In the past I was using SNDCTL_DSP_HALT to stop the device, however this results in issues when -trying to resume the device again. If we use SNDCTL_DSP_HALT, the next write() or read() will -fail. Instead what we need to do is just not write or read to and from the device when the -device is not running. - -As a result, both the start and stop functions for OSS are just empty stubs. The starting and -stopping logic is handled by ma_device_write__oss() and ma_device_read__oss(). These will check -the device state, and if the device is stopped they will simply not do any kind of processing. - -The downside to this technique is that I've noticed a fairly lengthy delay in stopping the -device, up to a second. This is on a virtual machine, and as such might just be due to the -virtual drivers, but I'm not fully sure. I am not sure how to work around this problem so for -the moment that's just how it's going to have to be. - -When starting the device, OSS will automatically start it when write() or read() is called. -*/ -static ma_result ma_device_start__oss(ma_device* pDevice) -{ - MA_ASSERT(pDevice != NULL); - - /* The device is automatically started with reading and writing. */ - (void)pDevice; - - return MA_SUCCESS; -} - -static ma_result ma_device_stop__oss(ma_device* pDevice) -{ - MA_ASSERT(pDevice != NULL); - - /* See note above on why this is empty. */ - (void)pDevice; - - return MA_SUCCESS; -} - -static ma_result ma_device_write__oss(ma_device* pDevice, const void* pPCMFrames, ma_uint32 frameCount, ma_uint32* pFramesWritten) -{ - int resultOSS; - ma_uint32 deviceState; - - if (pFramesWritten != NULL) { - *pFramesWritten = 0; - } - - /* Don't do any processing if the device is stopped. */ - deviceState = ma_device_get_state(pDevice); - if (deviceState != ma_device_state_started && deviceState != ma_device_state_starting) { - return MA_SUCCESS; - } - - resultOSS = write(pDevice->oss.fdPlayback, pPCMFrames, frameCount * ma_get_bytes_per_frame(pDevice->playback.internalFormat, pDevice->playback.internalChannels)); - if (resultOSS < 0) { - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[OSS] Failed to send data from the client to the device."); - return ma_result_from_errno(errno); - } - - if (pFramesWritten != NULL) { - *pFramesWritten = (ma_uint32)resultOSS / ma_get_bytes_per_frame(pDevice->playback.internalFormat, pDevice->playback.internalChannels); - } - - return MA_SUCCESS; -} - -static ma_result ma_device_read__oss(ma_device* pDevice, void* pPCMFrames, ma_uint32 frameCount, ma_uint32* pFramesRead) -{ - int resultOSS; - ma_uint32 deviceState; - - if (pFramesRead != NULL) { - *pFramesRead = 0; - } - - /* Don't do any processing if the device is stopped. */ - deviceState = ma_device_get_state(pDevice); - if (deviceState != ma_device_state_started && deviceState != ma_device_state_starting) { - return MA_SUCCESS; - } - - resultOSS = read(pDevice->oss.fdCapture, pPCMFrames, frameCount * ma_get_bytes_per_frame(pDevice->capture.internalFormat, pDevice->capture.internalChannels)); - if (resultOSS < 0) { - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[OSS] Failed to read data from the device to be sent to the client."); - return ma_result_from_errno(errno); - } - - if (pFramesRead != NULL) { - *pFramesRead = (ma_uint32)resultOSS / ma_get_bytes_per_frame(pDevice->capture.internalFormat, pDevice->capture.internalChannels); - } - - return MA_SUCCESS; -} - -static ma_result ma_context_uninit__oss(ma_context* pContext) -{ - MA_ASSERT(pContext != NULL); - MA_ASSERT(pContext->backend == ma_backend_oss); - - (void)pContext; - return MA_SUCCESS; -} - -static ma_result ma_context_init__oss(ma_context* pContext, const ma_context_config* pConfig, ma_backend_callbacks* pCallbacks) -{ - int fd; - int ossVersion; - int result; - - MA_ASSERT(pContext != NULL); - - (void)pConfig; - - /* Try opening a temporary device first so we can get version information. This is closed at the end. */ - fd = ma_open_temp_device__oss(); - if (fd == -1) { - ma_log_post(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[OSS] Failed to open temporary device for retrieving system properties."); /* Looks liks OSS isn't installed, or there are no available devices. */ - return MA_NO_BACKEND; - } - - /* Grab the OSS version. */ - ossVersion = 0; - result = ioctl(fd, OSS_GETVERSION, &ossVersion); - if (result == -1) { - close(fd); - ma_log_post(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[OSS] Failed to retrieve OSS version."); - return MA_NO_BACKEND; - } - - /* The file handle to temp device is no longer needed. Close ASAP. */ - close(fd); - - pContext->oss.versionMajor = ((ossVersion & 0xFF0000) >> 16); - pContext->oss.versionMinor = ((ossVersion & 0x00FF00) >> 8); - - pCallbacks->onContextInit = ma_context_init__oss; - pCallbacks->onContextUninit = ma_context_uninit__oss; - pCallbacks->onContextEnumerateDevices = ma_context_enumerate_devices__oss; - pCallbacks->onContextGetDeviceInfo = ma_context_get_device_info__oss; - pCallbacks->onDeviceInit = ma_device_init__oss; - pCallbacks->onDeviceUninit = ma_device_uninit__oss; - pCallbacks->onDeviceStart = ma_device_start__oss; - pCallbacks->onDeviceStop = ma_device_stop__oss; - pCallbacks->onDeviceRead = ma_device_read__oss; - pCallbacks->onDeviceWrite = ma_device_write__oss; - pCallbacks->onDeviceDataLoop = NULL; - - return MA_SUCCESS; -} -#endif /* MA_HAS_OSS */ - - - - - -/****************************************************************************** - -AAudio Backend - -******************************************************************************/ -#ifdef MA_HAS_AAUDIO - -#ifdef MA_NO_RUNTIME_LINKING - #include -#endif - -typedef int32_t ma_aaudio_result_t; -typedef int32_t ma_aaudio_direction_t; -typedef int32_t ma_aaudio_sharing_mode_t; -typedef int32_t ma_aaudio_format_t; -typedef int32_t ma_aaudio_stream_state_t; -typedef int32_t ma_aaudio_performance_mode_t; -typedef int32_t ma_aaudio_usage_t; -typedef int32_t ma_aaudio_content_type_t; -typedef int32_t ma_aaudio_input_preset_t; -typedef int32_t ma_aaudio_allowed_capture_policy_t; -typedef int32_t ma_aaudio_data_callback_result_t; -typedef struct ma_AAudioStreamBuilder_t* ma_AAudioStreamBuilder; -typedef struct ma_AAudioStream_t* ma_AAudioStream; - -#define MA_AAUDIO_UNSPECIFIED 0 - -/* Result codes. miniaudio only cares about the success code. */ -#define MA_AAUDIO_OK 0 - -/* Directions. */ -#define MA_AAUDIO_DIRECTION_OUTPUT 0 -#define MA_AAUDIO_DIRECTION_INPUT 1 - -/* Sharing modes. */ -#define MA_AAUDIO_SHARING_MODE_EXCLUSIVE 0 -#define MA_AAUDIO_SHARING_MODE_SHARED 1 - -/* Formats. */ -#define MA_AAUDIO_FORMAT_PCM_I16 1 -#define MA_AAUDIO_FORMAT_PCM_FLOAT 2 - -/* Stream states. */ -#define MA_AAUDIO_STREAM_STATE_UNINITIALIZED 0 -#define MA_AAUDIO_STREAM_STATE_UNKNOWN 1 -#define MA_AAUDIO_STREAM_STATE_OPEN 2 -#define MA_AAUDIO_STREAM_STATE_STARTING 3 -#define MA_AAUDIO_STREAM_STATE_STARTED 4 -#define MA_AAUDIO_STREAM_STATE_PAUSING 5 -#define MA_AAUDIO_STREAM_STATE_PAUSED 6 -#define MA_AAUDIO_STREAM_STATE_FLUSHING 7 -#define MA_AAUDIO_STREAM_STATE_FLUSHED 8 -#define MA_AAUDIO_STREAM_STATE_STOPPING 9 -#define MA_AAUDIO_STREAM_STATE_STOPPED 10 -#define MA_AAUDIO_STREAM_STATE_CLOSING 11 -#define MA_AAUDIO_STREAM_STATE_CLOSED 12 -#define MA_AAUDIO_STREAM_STATE_DISCONNECTED 13 - -/* Performance modes. */ -#define MA_AAUDIO_PERFORMANCE_MODE_NONE 10 -#define MA_AAUDIO_PERFORMANCE_MODE_POWER_SAVING 11 -#define MA_AAUDIO_PERFORMANCE_MODE_LOW_LATENCY 12 - -/* Usage types. */ -#define MA_AAUDIO_USAGE_MEDIA 1 -#define MA_AAUDIO_USAGE_VOICE_COMMUNICATION 2 -#define MA_AAUDIO_USAGE_VOICE_COMMUNICATION_SIGNALLING 3 -#define MA_AAUDIO_USAGE_ALARM 4 -#define MA_AAUDIO_USAGE_NOTIFICATION 5 -#define MA_AAUDIO_USAGE_NOTIFICATION_RINGTONE 6 -#define MA_AAUDIO_USAGE_NOTIFICATION_EVENT 10 -#define MA_AAUDIO_USAGE_ASSISTANCE_ACCESSIBILITY 11 -#define MA_AAUDIO_USAGE_ASSISTANCE_NAVIGATION_GUIDANCE 12 -#define MA_AAUDIO_USAGE_ASSISTANCE_SONIFICATION 13 -#define MA_AAUDIO_USAGE_GAME 14 -#define MA_AAUDIO_USAGE_ASSISTANT 16 -#define MA_AAUDIO_SYSTEM_USAGE_EMERGENCY 1000 -#define MA_AAUDIO_SYSTEM_USAGE_SAFETY 1001 -#define MA_AAUDIO_SYSTEM_USAGE_VEHICLE_STATUS 1002 -#define MA_AAUDIO_SYSTEM_USAGE_ANNOUNCEMENT 1003 - -/* Content types. */ -#define MA_AAUDIO_CONTENT_TYPE_SPEECH 1 -#define MA_AAUDIO_CONTENT_TYPE_MUSIC 2 -#define MA_AAUDIO_CONTENT_TYPE_MOVIE 3 -#define MA_AAUDIO_CONTENT_TYPE_SONIFICATION 4 - -/* Input presets. */ -#define MA_AAUDIO_INPUT_PRESET_GENERIC 1 -#define MA_AAUDIO_INPUT_PRESET_CAMCORDER 5 -#define MA_AAUDIO_INPUT_PRESET_VOICE_RECOGNITION 6 -#define MA_AAUDIO_INPUT_PRESET_VOICE_COMMUNICATION 7 -#define MA_AAUDIO_INPUT_PRESET_UNPROCESSED 9 -#define MA_AAUDIO_INPUT_PRESET_VOICE_PERFORMANCE 10 - -/* Allowed Capture Policies */ -#define MA_AAUDIO_ALLOW_CAPTURE_BY_ALL 1 -#define MA_AAUDIO_ALLOW_CAPTURE_BY_SYSTEM 2 -#define MA_AAUDIO_ALLOW_CAPTURE_BY_NONE 3 - -/* Callback results. */ -#define MA_AAUDIO_CALLBACK_RESULT_CONTINUE 0 -#define MA_AAUDIO_CALLBACK_RESULT_STOP 1 - - -typedef ma_aaudio_data_callback_result_t (* ma_AAudioStream_dataCallback) (ma_AAudioStream* pStream, void* pUserData, void* pAudioData, int32_t numFrames); -typedef void (* ma_AAudioStream_errorCallback)(ma_AAudioStream *pStream, void *pUserData, ma_aaudio_result_t error); - -typedef ma_aaudio_result_t (* MA_PFN_AAudio_createStreamBuilder) (ma_AAudioStreamBuilder** ppBuilder); -typedef ma_aaudio_result_t (* MA_PFN_AAudioStreamBuilder_delete) (ma_AAudioStreamBuilder* pBuilder); -typedef void (* MA_PFN_AAudioStreamBuilder_setDeviceId) (ma_AAudioStreamBuilder* pBuilder, int32_t deviceId); -typedef void (* MA_PFN_AAudioStreamBuilder_setDirection) (ma_AAudioStreamBuilder* pBuilder, ma_aaudio_direction_t direction); -typedef void (* MA_PFN_AAudioStreamBuilder_setSharingMode) (ma_AAudioStreamBuilder* pBuilder, ma_aaudio_sharing_mode_t sharingMode); -typedef void (* MA_PFN_AAudioStreamBuilder_setFormat) (ma_AAudioStreamBuilder* pBuilder, ma_aaudio_format_t format); -typedef void (* MA_PFN_AAudioStreamBuilder_setChannelCount) (ma_AAudioStreamBuilder* pBuilder, int32_t channelCount); -typedef void (* MA_PFN_AAudioStreamBuilder_setSampleRate) (ma_AAudioStreamBuilder* pBuilder, int32_t sampleRate); -typedef void (* MA_PFN_AAudioStreamBuilder_setBufferCapacityInFrames)(ma_AAudioStreamBuilder* pBuilder, int32_t numFrames); -typedef void (* MA_PFN_AAudioStreamBuilder_setFramesPerDataCallback) (ma_AAudioStreamBuilder* pBuilder, int32_t numFrames); -typedef void (* MA_PFN_AAudioStreamBuilder_setDataCallback) (ma_AAudioStreamBuilder* pBuilder, ma_AAudioStream_dataCallback callback, void* pUserData); -typedef void (* MA_PFN_AAudioStreamBuilder_setErrorCallback) (ma_AAudioStreamBuilder* pBuilder, ma_AAudioStream_errorCallback callback, void* pUserData); -typedef void (* MA_PFN_AAudioStreamBuilder_setPerformanceMode) (ma_AAudioStreamBuilder* pBuilder, ma_aaudio_performance_mode_t mode); -typedef void (* MA_PFN_AAudioStreamBuilder_setUsage) (ma_AAudioStreamBuilder* pBuilder, ma_aaudio_usage_t contentType); -typedef void (* MA_PFN_AAudioStreamBuilder_setContentType) (ma_AAudioStreamBuilder* pBuilder, ma_aaudio_content_type_t contentType); -typedef void (* MA_PFN_AAudioStreamBuilder_setInputPreset) (ma_AAudioStreamBuilder* pBuilder, ma_aaudio_input_preset_t inputPreset); -typedef void (* MA_PFN_AAudioStreamBuilder_setAllowedCapturePolicy) (ma_AAudioStreamBuilder* pBuilder, ma_aaudio_allowed_capture_policy_t policy); -typedef ma_aaudio_result_t (* MA_PFN_AAudioStreamBuilder_openStream) (ma_AAudioStreamBuilder* pBuilder, ma_AAudioStream** ppStream); -typedef ma_aaudio_result_t (* MA_PFN_AAudioStream_close) (ma_AAudioStream* pStream); -typedef ma_aaudio_stream_state_t (* MA_PFN_AAudioStream_getState) (ma_AAudioStream* pStream); -typedef ma_aaudio_result_t (* MA_PFN_AAudioStream_waitForStateChange) (ma_AAudioStream* pStream, ma_aaudio_stream_state_t inputState, ma_aaudio_stream_state_t* pNextState, int64_t timeoutInNanoseconds); -typedef ma_aaudio_format_t (* MA_PFN_AAudioStream_getFormat) (ma_AAudioStream* pStream); -typedef int32_t (* MA_PFN_AAudioStream_getChannelCount) (ma_AAudioStream* pStream); -typedef int32_t (* MA_PFN_AAudioStream_getSampleRate) (ma_AAudioStream* pStream); -typedef int32_t (* MA_PFN_AAudioStream_getBufferCapacityInFrames) (ma_AAudioStream* pStream); -typedef int32_t (* MA_PFN_AAudioStream_getFramesPerDataCallback) (ma_AAudioStream* pStream); -typedef int32_t (* MA_PFN_AAudioStream_getFramesPerBurst) (ma_AAudioStream* pStream); -typedef ma_aaudio_result_t (* MA_PFN_AAudioStream_requestStart) (ma_AAudioStream* pStream); -typedef ma_aaudio_result_t (* MA_PFN_AAudioStream_requestStop) (ma_AAudioStream* pStream); - -static ma_result ma_result_from_aaudio(ma_aaudio_result_t resultAA) -{ - switch (resultAA) - { - case MA_AAUDIO_OK: return MA_SUCCESS; - default: break; - } - - return MA_ERROR; -} - -static ma_aaudio_usage_t ma_to_usage__aaudio(ma_aaudio_usage usage) -{ - switch (usage) { - case ma_aaudio_usage_media: return MA_AAUDIO_USAGE_MEDIA; - case ma_aaudio_usage_voice_communication: return MA_AAUDIO_USAGE_VOICE_COMMUNICATION; - case ma_aaudio_usage_voice_communication_signalling: return MA_AAUDIO_USAGE_VOICE_COMMUNICATION_SIGNALLING; - case ma_aaudio_usage_alarm: return MA_AAUDIO_USAGE_ALARM; - case ma_aaudio_usage_notification: return MA_AAUDIO_USAGE_NOTIFICATION; - case ma_aaudio_usage_notification_ringtone: return MA_AAUDIO_USAGE_NOTIFICATION_RINGTONE; - case ma_aaudio_usage_notification_event: return MA_AAUDIO_USAGE_NOTIFICATION_EVENT; - case ma_aaudio_usage_assistance_accessibility: return MA_AAUDIO_USAGE_ASSISTANCE_ACCESSIBILITY; - case ma_aaudio_usage_assistance_navigation_guidance: return MA_AAUDIO_USAGE_ASSISTANCE_NAVIGATION_GUIDANCE; - case ma_aaudio_usage_assistance_sonification: return MA_AAUDIO_USAGE_ASSISTANCE_SONIFICATION; - case ma_aaudio_usage_game: return MA_AAUDIO_USAGE_GAME; - case ma_aaudio_usage_assitant: return MA_AAUDIO_USAGE_ASSISTANT; - case ma_aaudio_usage_emergency: return MA_AAUDIO_SYSTEM_USAGE_EMERGENCY; - case ma_aaudio_usage_safety: return MA_AAUDIO_SYSTEM_USAGE_SAFETY; - case ma_aaudio_usage_vehicle_status: return MA_AAUDIO_SYSTEM_USAGE_VEHICLE_STATUS; - case ma_aaudio_usage_announcement: return MA_AAUDIO_SYSTEM_USAGE_ANNOUNCEMENT; - default: break; - } - - return MA_AAUDIO_USAGE_MEDIA; -} - -static ma_aaudio_content_type_t ma_to_content_type__aaudio(ma_aaudio_content_type contentType) -{ - switch (contentType) { - case ma_aaudio_content_type_speech: return MA_AAUDIO_CONTENT_TYPE_SPEECH; - case ma_aaudio_content_type_music: return MA_AAUDIO_CONTENT_TYPE_MUSIC; - case ma_aaudio_content_type_movie: return MA_AAUDIO_CONTENT_TYPE_MOVIE; - case ma_aaudio_content_type_sonification: return MA_AAUDIO_CONTENT_TYPE_SONIFICATION; - default: break; - } - - return MA_AAUDIO_CONTENT_TYPE_SPEECH; -} - -static ma_aaudio_input_preset_t ma_to_input_preset__aaudio(ma_aaudio_input_preset inputPreset) -{ - switch (inputPreset) { - case ma_aaudio_input_preset_generic: return MA_AAUDIO_INPUT_PRESET_GENERIC; - case ma_aaudio_input_preset_camcorder: return MA_AAUDIO_INPUT_PRESET_CAMCORDER; - case ma_aaudio_input_preset_voice_recognition: return MA_AAUDIO_INPUT_PRESET_VOICE_RECOGNITION; - case ma_aaudio_input_preset_voice_communication: return MA_AAUDIO_INPUT_PRESET_VOICE_COMMUNICATION; - case ma_aaudio_input_preset_unprocessed: return MA_AAUDIO_INPUT_PRESET_UNPROCESSED; - case ma_aaudio_input_preset_voice_performance: return MA_AAUDIO_INPUT_PRESET_VOICE_PERFORMANCE; - default: break; - } - - return MA_AAUDIO_INPUT_PRESET_GENERIC; -} - -static ma_aaudio_allowed_capture_policy_t ma_to_allowed_capture_policy__aaudio(ma_aaudio_allowed_capture_policy allowedCapturePolicy) -{ - switch (allowedCapturePolicy) { - case ma_aaudio_allow_capture_by_all: return MA_AAUDIO_ALLOW_CAPTURE_BY_ALL; - case ma_aaudio_allow_capture_by_system: return MA_AAUDIO_ALLOW_CAPTURE_BY_SYSTEM; - case ma_aaudio_allow_capture_by_none: return MA_AAUDIO_ALLOW_CAPTURE_BY_NONE; - default: break; - } - - return MA_AAUDIO_ALLOW_CAPTURE_BY_ALL; -} - -static void ma_stream_error_callback__aaudio(ma_AAudioStream* pStream, void* pUserData, ma_aaudio_result_t error) -{ - ma_result result; - ma_job job; - ma_device* pDevice = (ma_device*)pUserData; - MA_ASSERT(pDevice != NULL); - - (void)error; - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, "[AAudio] ERROR CALLBACK: error=%d, AAudioStream_getState()=%d\n", error, ((MA_PFN_AAudioStream_getState)pDevice->pContext->aaudio.AAudioStream_getState)(pStream)); - /* - When we get an error, we'll assume that the stream is in an erroneous state and needs to be restarted. From the documentation, - we cannot do this from the error callback. Therefore we are going to use an event thread for the AAudio backend to do this - cleanly and safely. - */ - job = ma_job_init(MA_JOB_TYPE_DEVICE_AAUDIO_REROUTE); - job.data.device.aaudio.reroute.pDevice = pDevice; - - if (pStream == pDevice->aaudio.pStreamCapture) { - job.data.device.aaudio.reroute.deviceType = ma_device_type_capture; - } - else { - job.data.device.aaudio.reroute.deviceType = ma_device_type_playback; - } - - result = ma_device_job_thread_post(&pDevice->pContext->aaudio.jobThread, &job); - if (result != MA_SUCCESS) { - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, "[AAudio] Device Disconnected. Failed to post job for rerouting.\n"); - return; - } -} - -static ma_aaudio_data_callback_result_t ma_stream_data_callback_capture__aaudio(ma_AAudioStream* pStream, void* pUserData, void* pAudioData, int32_t frameCount) -{ - ma_device* pDevice = (ma_device*)pUserData; - MA_ASSERT(pDevice != NULL); - - if (frameCount > 0) { - ma_device_handle_backend_data_callback(pDevice, NULL, pAudioData, (ma_uint32)frameCount); - } - - (void)pStream; - return MA_AAUDIO_CALLBACK_RESULT_CONTINUE; -} - -static ma_aaudio_data_callback_result_t ma_stream_data_callback_playback__aaudio(ma_AAudioStream* pStream, void* pUserData, void* pAudioData, int32_t frameCount) -{ - ma_device* pDevice = (ma_device*)pUserData; - MA_ASSERT(pDevice != NULL); - - /* - I've had a report that AAudio can sometimes post a frame count of 0. We need to check for that here - so we don't get any errors at a deeper level. I'm doing the same with the capture side for safety, - though I've not yet had any reports about that one. - */ - if (frameCount > 0) { - ma_device_handle_backend_data_callback(pDevice, pAudioData, NULL, (ma_uint32)frameCount); - } - - (void)pStream; - return MA_AAUDIO_CALLBACK_RESULT_CONTINUE; -} - -static ma_result ma_create_and_configure_AAudioStreamBuilder__aaudio(ma_context* pContext, const ma_device_id* pDeviceID, ma_device_type deviceType, ma_share_mode shareMode, const ma_device_descriptor* pDescriptor, const ma_device_config* pConfig, ma_device* pDevice, ma_AAudioStreamBuilder** ppBuilder) -{ - ma_AAudioStreamBuilder* pBuilder; - ma_aaudio_result_t resultAA; - - /* Safety. */ - *ppBuilder = NULL; - - resultAA = ((MA_PFN_AAudio_createStreamBuilder)pContext->aaudio.AAudio_createStreamBuilder)(&pBuilder); - if (resultAA != MA_AAUDIO_OK) { - return ma_result_from_aaudio(resultAA); - } - - if (pDeviceID != NULL) { - ((MA_PFN_AAudioStreamBuilder_setDeviceId)pContext->aaudio.AAudioStreamBuilder_setDeviceId)(pBuilder, pDeviceID->aaudio); - } - - ((MA_PFN_AAudioStreamBuilder_setDirection)pContext->aaudio.AAudioStreamBuilder_setDirection)(pBuilder, (deviceType == ma_device_type_playback) ? MA_AAUDIO_DIRECTION_OUTPUT : MA_AAUDIO_DIRECTION_INPUT); - ((MA_PFN_AAudioStreamBuilder_setSharingMode)pContext->aaudio.AAudioStreamBuilder_setSharingMode)(pBuilder, (shareMode == ma_share_mode_shared) ? MA_AAUDIO_SHARING_MODE_SHARED : MA_AAUDIO_SHARING_MODE_EXCLUSIVE); - - - /* If we have a device descriptor make sure we configure the stream builder to take our requested parameters. */ - if (pDescriptor != NULL) { - MA_ASSERT(pConfig != NULL); /* We must have a device config if we also have a descriptor. The config is required for AAudio specific configuration options. */ - - if (pDescriptor->sampleRate != 0) { - ((MA_PFN_AAudioStreamBuilder_setSampleRate)pContext->aaudio.AAudioStreamBuilder_setSampleRate)(pBuilder, pDescriptor->sampleRate); - } - - if (pDescriptor->channels != 0) { - ((MA_PFN_AAudioStreamBuilder_setChannelCount)pContext->aaudio.AAudioStreamBuilder_setChannelCount)(pBuilder, pDescriptor->channels); - } - - if (pDescriptor->format != ma_format_unknown) { - ((MA_PFN_AAudioStreamBuilder_setFormat)pContext->aaudio.AAudioStreamBuilder_setFormat)(pBuilder, (pDescriptor->format == ma_format_s16) ? MA_AAUDIO_FORMAT_PCM_I16 : MA_AAUDIO_FORMAT_PCM_FLOAT); - } - - - /* - There have been reports where setting the frames per data callback results in an error. - In particular, re-routing may inadvertently switch from low-latency mode, resulting in a less stable - stream from the legacy path (AudioStreamLegacy). To address this, we simply don't set the value. It - can still be set if it's explicitly requested via the aaudio.allowSetBufferCapacity variable in the - device config. - */ - if ((!pConfig->aaudio.enableCompatibilityWorkarounds || ma_android_sdk_version() > 30) && pConfig->aaudio.allowSetBufferCapacity) { - /* - AAudio is annoying when it comes to its buffer calculation stuff because it doesn't let you - retrieve the actual sample rate until after you've opened the stream. But you need to configure - the buffer capacity before you open the stream... :/ - - To solve, we're just going to assume MA_DEFAULT_SAMPLE_RATE (48000) and move on. - */ - ma_uint32 bufferCapacityInFrames = ma_calculate_buffer_size_in_frames_from_descriptor(pDescriptor, pDescriptor->sampleRate, pConfig->performanceProfile) * pDescriptor->periodCount; - - ((MA_PFN_AAudioStreamBuilder_setBufferCapacityInFrames)pContext->aaudio.AAudioStreamBuilder_setBufferCapacityInFrames)(pBuilder, bufferCapacityInFrames); - ((MA_PFN_AAudioStreamBuilder_setFramesPerDataCallback)pContext->aaudio.AAudioStreamBuilder_setFramesPerDataCallback)(pBuilder, bufferCapacityInFrames / pDescriptor->periodCount); - } - - if (deviceType == ma_device_type_capture) { - if (pConfig->aaudio.inputPreset != ma_aaudio_input_preset_default && pContext->aaudio.AAudioStreamBuilder_setInputPreset != NULL) { - ((MA_PFN_AAudioStreamBuilder_setInputPreset)pContext->aaudio.AAudioStreamBuilder_setInputPreset)(pBuilder, ma_to_input_preset__aaudio(pConfig->aaudio.inputPreset)); - } - - ((MA_PFN_AAudioStreamBuilder_setDataCallback)pContext->aaudio.AAudioStreamBuilder_setDataCallback)(pBuilder, ma_stream_data_callback_capture__aaudio, (void*)pDevice); - } else { - if (pConfig->aaudio.usage != ma_aaudio_usage_default && pContext->aaudio.AAudioStreamBuilder_setUsage != NULL) { - ((MA_PFN_AAudioStreamBuilder_setUsage)pContext->aaudio.AAudioStreamBuilder_setUsage)(pBuilder, ma_to_usage__aaudio(pConfig->aaudio.usage)); - } - - if (pConfig->aaudio.contentType != ma_aaudio_content_type_default && pContext->aaudio.AAudioStreamBuilder_setContentType != NULL) { - ((MA_PFN_AAudioStreamBuilder_setContentType)pContext->aaudio.AAudioStreamBuilder_setContentType)(pBuilder, ma_to_content_type__aaudio(pConfig->aaudio.contentType)); - } - - if (pConfig->aaudio.allowedCapturePolicy != ma_aaudio_allow_capture_default && pContext->aaudio.AAudioStreamBuilder_setAllowedCapturePolicy != NULL) { - ((MA_PFN_AAudioStreamBuilder_setAllowedCapturePolicy)pContext->aaudio.AAudioStreamBuilder_setAllowedCapturePolicy)(pBuilder, ma_to_allowed_capture_policy__aaudio(pConfig->aaudio.allowedCapturePolicy)); - } - - ((MA_PFN_AAudioStreamBuilder_setDataCallback)pContext->aaudio.AAudioStreamBuilder_setDataCallback)(pBuilder, ma_stream_data_callback_playback__aaudio, (void*)pDevice); - } - - /* - If we set AAUDIO_PERFORMANCE_MODE_LOW_LATENCY, we allow for MMAP (non-legacy path). - Since there's a mapping between miniaudio's performance profiles and AAudio's performance modes, let's use it. - Beware though, with a conservative performance profile, AAudio will indeed take the legacy path. - */ - ((MA_PFN_AAudioStreamBuilder_setPerformanceMode)pContext->aaudio.AAudioStreamBuilder_setPerformanceMode)(pBuilder, (pConfig->performanceProfile == ma_performance_profile_low_latency) ? MA_AAUDIO_PERFORMANCE_MODE_LOW_LATENCY : MA_AAUDIO_PERFORMANCE_MODE_NONE); - - /* We need to set an error callback to detect device changes. */ - if (pDevice != NULL) { /* <-- pDevice should never be null if pDescriptor is not null, which is always the case if we hit this branch. Check anyway for safety. */ - ((MA_PFN_AAudioStreamBuilder_setErrorCallback)pContext->aaudio.AAudioStreamBuilder_setErrorCallback)(pBuilder, ma_stream_error_callback__aaudio, (void*)pDevice); - } - } - - *ppBuilder = pBuilder; - - return MA_SUCCESS; -} - -static ma_result ma_open_stream_and_close_builder__aaudio(ma_context* pContext, ma_AAudioStreamBuilder* pBuilder, ma_AAudioStream** ppStream) -{ - ma_result result; - - result = ma_result_from_aaudio(((MA_PFN_AAudioStreamBuilder_openStream)pContext->aaudio.AAudioStreamBuilder_openStream)(pBuilder, ppStream)); - ((MA_PFN_AAudioStreamBuilder_delete)pContext->aaudio.AAudioStreamBuilder_delete)(pBuilder); - - return result; -} - -static ma_result ma_open_stream_basic__aaudio(ma_context* pContext, const ma_device_id* pDeviceID, ma_device_type deviceType, ma_share_mode shareMode, ma_AAudioStream** ppStream) -{ - ma_result result; - ma_AAudioStreamBuilder* pBuilder; - - *ppStream = NULL; - - result = ma_create_and_configure_AAudioStreamBuilder__aaudio(pContext, pDeviceID, deviceType, shareMode, NULL, NULL, NULL, &pBuilder); - if (result != MA_SUCCESS) { - return result; - } - - /* Let's give AAudio a hint to avoid the legacy path (AudioStreamLegacy). */ - ((MA_PFN_AAudioStreamBuilder_setPerformanceMode)pContext->aaudio.AAudioStreamBuilder_setPerformanceMode)(pBuilder, MA_AAUDIO_PERFORMANCE_MODE_LOW_LATENCY); - - return ma_open_stream_and_close_builder__aaudio(pContext, pBuilder, ppStream); -} - -static ma_result ma_open_stream__aaudio(ma_device* pDevice, const ma_device_config* pConfig, ma_device_type deviceType, const ma_device_descriptor* pDescriptor, ma_AAudioStream** ppStream) -{ - ma_result result; - ma_AAudioStreamBuilder* pBuilder; - - MA_ASSERT(pDevice != NULL); - MA_ASSERT(pDescriptor != NULL); - MA_ASSERT(deviceType != ma_device_type_duplex); /* This function should not be called for a full-duplex device type. */ - - *ppStream = NULL; - - result = ma_create_and_configure_AAudioStreamBuilder__aaudio(pDevice->pContext, pDescriptor->pDeviceID, deviceType, pDescriptor->shareMode, pDescriptor, pConfig, pDevice, &pBuilder); - if (result != MA_SUCCESS) { - return result; - } - - return ma_open_stream_and_close_builder__aaudio(pDevice->pContext, pBuilder, ppStream); -} - -static ma_result ma_close_stream__aaudio(ma_context* pContext, ma_AAudioStream* pStream) -{ - if (pStream == NULL) { - return MA_INVALID_ARGS; - } - - return ma_result_from_aaudio(((MA_PFN_AAudioStream_close)pContext->aaudio.AAudioStream_close)(pStream)); -} - -static ma_bool32 ma_has_default_device__aaudio(ma_context* pContext, ma_device_type deviceType) -{ - /* The only way to know this is to try creating a stream. */ - ma_AAudioStream* pStream; - ma_result result = ma_open_stream_basic__aaudio(pContext, NULL, deviceType, ma_share_mode_shared, &pStream); - if (result != MA_SUCCESS) { - return MA_FALSE; - } - - ma_close_stream__aaudio(pContext, pStream); - return MA_TRUE; -} - -static ma_result ma_wait_for_simple_state_transition__aaudio(ma_context* pContext, ma_AAudioStream* pStream, ma_aaudio_stream_state_t oldState, ma_aaudio_stream_state_t newState) -{ - ma_aaudio_stream_state_t actualNewState; - ma_aaudio_result_t resultAA = ((MA_PFN_AAudioStream_waitForStateChange)pContext->aaudio.AAudioStream_waitForStateChange)(pStream, oldState, &actualNewState, 5000000000); /* 5 second timeout. */ - if (resultAA != MA_AAUDIO_OK) { - return ma_result_from_aaudio(resultAA); - } - - if (newState != actualNewState) { - return MA_ERROR; /* Failed to transition into the expected state. */ - } - - return MA_SUCCESS; -} - - -static ma_result ma_context_enumerate_devices__aaudio(ma_context* pContext, ma_enum_devices_callback_proc callback, void* pUserData) -{ - ma_bool32 cbResult = MA_TRUE; - - MA_ASSERT(pContext != NULL); - MA_ASSERT(callback != NULL); - - /* Unfortunately AAudio does not have an enumeration API. Therefore I'm only going to report default devices, but only if it can instantiate a stream. */ - - /* Playback. */ - if (cbResult) { - ma_device_info deviceInfo; - MA_ZERO_OBJECT(&deviceInfo); - deviceInfo.id.aaudio = MA_AAUDIO_UNSPECIFIED; - ma_strncpy_s(deviceInfo.name, sizeof(deviceInfo.name), MA_DEFAULT_PLAYBACK_DEVICE_NAME, (size_t)-1); - - if (ma_has_default_device__aaudio(pContext, ma_device_type_playback)) { - cbResult = callback(pContext, ma_device_type_playback, &deviceInfo, pUserData); - } - } - - /* Capture. */ - if (cbResult) { - ma_device_info deviceInfo; - MA_ZERO_OBJECT(&deviceInfo); - deviceInfo.id.aaudio = MA_AAUDIO_UNSPECIFIED; - ma_strncpy_s(deviceInfo.name, sizeof(deviceInfo.name), MA_DEFAULT_CAPTURE_DEVICE_NAME, (size_t)-1); - - if (ma_has_default_device__aaudio(pContext, ma_device_type_capture)) { - cbResult = callback(pContext, ma_device_type_capture, &deviceInfo, pUserData); - } - } - - return MA_SUCCESS; -} - -static void ma_context_add_native_data_format_from_AAudioStream_ex__aaudio(ma_context* pContext, ma_AAudioStream* pStream, ma_format format, ma_uint32 flags, ma_device_info* pDeviceInfo) -{ - MA_ASSERT(pContext != NULL); - MA_ASSERT(pStream != NULL); - MA_ASSERT(pDeviceInfo != NULL); - - pDeviceInfo->nativeDataFormats[pDeviceInfo->nativeDataFormatCount].format = format; - pDeviceInfo->nativeDataFormats[pDeviceInfo->nativeDataFormatCount].channels = ((MA_PFN_AAudioStream_getChannelCount)pContext->aaudio.AAudioStream_getChannelCount)(pStream); - pDeviceInfo->nativeDataFormats[pDeviceInfo->nativeDataFormatCount].sampleRate = ((MA_PFN_AAudioStream_getSampleRate)pContext->aaudio.AAudioStream_getSampleRate)(pStream); - pDeviceInfo->nativeDataFormats[pDeviceInfo->nativeDataFormatCount].flags = flags; - pDeviceInfo->nativeDataFormatCount += 1; -} - -static void ma_context_add_native_data_format_from_AAudioStream__aaudio(ma_context* pContext, ma_AAudioStream* pStream, ma_uint32 flags, ma_device_info* pDeviceInfo) -{ - /* AAudio supports s16 and f32. */ - ma_context_add_native_data_format_from_AAudioStream_ex__aaudio(pContext, pStream, ma_format_f32, flags, pDeviceInfo); - ma_context_add_native_data_format_from_AAudioStream_ex__aaudio(pContext, pStream, ma_format_s16, flags, pDeviceInfo); -} - -static ma_result ma_context_get_device_info__aaudio(ma_context* pContext, ma_device_type deviceType, const ma_device_id* pDeviceID, ma_device_info* pDeviceInfo) -{ - ma_AAudioStream* pStream; - ma_result result; - - MA_ASSERT(pContext != NULL); - - /* ID */ - if (pDeviceID != NULL) { - pDeviceInfo->id.aaudio = pDeviceID->aaudio; - } else { - pDeviceInfo->id.aaudio = MA_AAUDIO_UNSPECIFIED; - } - - /* Name */ - if (deviceType == ma_device_type_playback) { - ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), MA_DEFAULT_PLAYBACK_DEVICE_NAME, (size_t)-1); - } else { - ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), MA_DEFAULT_CAPTURE_DEVICE_NAME, (size_t)-1); - } - - - pDeviceInfo->nativeDataFormatCount = 0; - - /* We'll need to open the device to get accurate sample rate and channel count information. */ - result = ma_open_stream_basic__aaudio(pContext, pDeviceID, deviceType, ma_share_mode_shared, &pStream); - if (result != MA_SUCCESS) { - return result; - } - - ma_context_add_native_data_format_from_AAudioStream__aaudio(pContext, pStream, 0, pDeviceInfo); - - ma_close_stream__aaudio(pContext, pStream); - pStream = NULL; - - return MA_SUCCESS; -} - -static ma_result ma_close_streams__aaudio(ma_device* pDevice) -{ - MA_ASSERT(pDevice != NULL); - - /* When re-routing, streams may have been closed and never re-opened. Hence the extra checks below. */ - if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) { - ma_close_stream__aaudio(pDevice->pContext, (ma_AAudioStream*)pDevice->aaudio.pStreamCapture); - pDevice->aaudio.pStreamCapture = NULL; - } - if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) { - ma_close_stream__aaudio(pDevice->pContext, (ma_AAudioStream*)pDevice->aaudio.pStreamPlayback); - pDevice->aaudio.pStreamPlayback = NULL; - } - - return MA_SUCCESS; -} - -static ma_result ma_device_uninit__aaudio(ma_device* pDevice) -{ - MA_ASSERT(pDevice != NULL); - - /* Wait for any rerouting to finish before attempting to close the streams. */ - ma_mutex_lock(&pDevice->aaudio.rerouteLock); - { - ma_close_streams__aaudio(pDevice); - } - ma_mutex_unlock(&pDevice->aaudio.rerouteLock); - - /* Destroy re-routing lock. */ - ma_mutex_uninit(&pDevice->aaudio.rerouteLock); - - return MA_SUCCESS; -} - -static ma_result ma_device_init_by_type__aaudio(ma_device* pDevice, const ma_device_config* pConfig, ma_device_type deviceType, ma_device_descriptor* pDescriptor, ma_AAudioStream** ppStream) -{ - ma_result result; - int32_t bufferCapacityInFrames; - int32_t framesPerDataCallback; - ma_AAudioStream* pStream; - - MA_ASSERT(pDevice != NULL); - MA_ASSERT(pConfig != NULL); - MA_ASSERT(pDescriptor != NULL); - - *ppStream = NULL; /* Safety. */ - - /* First step is to open the stream. From there we'll be able to extract the internal configuration. */ - result = ma_open_stream__aaudio(pDevice, pConfig, deviceType, pDescriptor, &pStream); - if (result != MA_SUCCESS) { - return result; /* Failed to open the AAudio stream. */ - } - - /* Now extract the internal configuration. */ - pDescriptor->format = (((MA_PFN_AAudioStream_getFormat)pDevice->pContext->aaudio.AAudioStream_getFormat)(pStream) == MA_AAUDIO_FORMAT_PCM_I16) ? ma_format_s16 : ma_format_f32; - pDescriptor->channels = ((MA_PFN_AAudioStream_getChannelCount)pDevice->pContext->aaudio.AAudioStream_getChannelCount)(pStream); - pDescriptor->sampleRate = ((MA_PFN_AAudioStream_getSampleRate)pDevice->pContext->aaudio.AAudioStream_getSampleRate)(pStream); - - /* For the channel map we need to be sure we don't overflow any buffers. */ - if (pDescriptor->channels <= MA_MAX_CHANNELS) { - ma_channel_map_init_standard(ma_standard_channel_map_default, pDescriptor->channelMap, ma_countof(pDescriptor->channelMap), pDescriptor->channels); /* <-- Cannot find info on channel order, so assuming a default. */ - } else { - ma_channel_map_init_blank(pDescriptor->channelMap, MA_MAX_CHANNELS); /* Too many channels. Use a blank channel map. */ - } - - bufferCapacityInFrames = ((MA_PFN_AAudioStream_getBufferCapacityInFrames)pDevice->pContext->aaudio.AAudioStream_getBufferCapacityInFrames)(pStream); - framesPerDataCallback = ((MA_PFN_AAudioStream_getFramesPerDataCallback)pDevice->pContext->aaudio.AAudioStream_getFramesPerDataCallback)(pStream); - - if (framesPerDataCallback > 0) { - pDescriptor->periodSizeInFrames = framesPerDataCallback; - pDescriptor->periodCount = bufferCapacityInFrames / framesPerDataCallback; - } else { - pDescriptor->periodSizeInFrames = bufferCapacityInFrames; - pDescriptor->periodCount = 1; - } - - *ppStream = pStream; - - return MA_SUCCESS; -} - -static ma_result ma_device_init_streams__aaudio(ma_device* pDevice, const ma_device_config* pConfig, ma_device_descriptor* pDescriptorPlayback, ma_device_descriptor* pDescriptorCapture) -{ - ma_result result; - - MA_ASSERT(pDevice != NULL); - - if (pConfig->deviceType == ma_device_type_loopback) { - return MA_DEVICE_TYPE_NOT_SUPPORTED; - } - - pDevice->aaudio.usage = pConfig->aaudio.usage; - pDevice->aaudio.contentType = pConfig->aaudio.contentType; - pDevice->aaudio.inputPreset = pConfig->aaudio.inputPreset; - pDevice->aaudio.allowedCapturePolicy = pConfig->aaudio.allowedCapturePolicy; - pDevice->aaudio.noAutoStartAfterReroute = pConfig->aaudio.noAutoStartAfterReroute; - - if (pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) { - result = ma_device_init_by_type__aaudio(pDevice, pConfig, ma_device_type_capture, pDescriptorCapture, (ma_AAudioStream**)&pDevice->aaudio.pStreamCapture); - if (result != MA_SUCCESS) { - return result; - } - } - - if (pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) { - result = ma_device_init_by_type__aaudio(pDevice, pConfig, ma_device_type_playback, pDescriptorPlayback, (ma_AAudioStream**)&pDevice->aaudio.pStreamPlayback); - if (result != MA_SUCCESS) { - return result; - } - } - - return MA_SUCCESS; -} - -static ma_result ma_device_init__aaudio(ma_device* pDevice, const ma_device_config* pConfig, ma_device_descriptor* pDescriptorPlayback, ma_device_descriptor* pDescriptorCapture) -{ - ma_result result; - - MA_ASSERT(pDevice != NULL); - - result = ma_device_init_streams__aaudio(pDevice, pConfig, pDescriptorPlayback, pDescriptorCapture); - if (result != MA_SUCCESS) { - return result; - } - - result = ma_mutex_init(&pDevice->aaudio.rerouteLock); - if (result != MA_SUCCESS) { - return result; - } - - return MA_SUCCESS; -} - -static ma_result ma_device_start_stream__aaudio(ma_device* pDevice, ma_AAudioStream* pStream) -{ - ma_aaudio_result_t resultAA; - ma_aaudio_stream_state_t currentState; - - MA_ASSERT(pDevice != NULL); - - if (pStream == NULL) { - return MA_INVALID_ARGS; - } - - resultAA = ((MA_PFN_AAudioStream_requestStart)pDevice->pContext->aaudio.AAudioStream_requestStart)(pStream); - if (resultAA != MA_AAUDIO_OK) { - return ma_result_from_aaudio(resultAA); - } - - /* Do we actually need to wait for the device to transition into its started state? */ - - /* The device should be in either a starting or started state. If it's not set to started we need to wait for it to transition. It should go from starting to started. */ - currentState = ((MA_PFN_AAudioStream_getState)pDevice->pContext->aaudio.AAudioStream_getState)(pStream); - if (currentState != MA_AAUDIO_STREAM_STATE_STARTED) { - ma_result result; - - if (currentState != MA_AAUDIO_STREAM_STATE_STARTING) { - return MA_ERROR; /* Expecting the stream to be a starting or started state. */ - } - - result = ma_wait_for_simple_state_transition__aaudio(pDevice->pContext, pStream, currentState, MA_AAUDIO_STREAM_STATE_STARTED); - if (result != MA_SUCCESS) { - return result; - } - } - - return MA_SUCCESS; -} - -static ma_result ma_device_stop_stream__aaudio(ma_device* pDevice, ma_AAudioStream* pStream) -{ - ma_aaudio_result_t resultAA; - ma_aaudio_stream_state_t currentState; - - MA_ASSERT(pDevice != NULL); - - if (pStream == NULL) { - return MA_INVALID_ARGS; - } - - /* - From the AAudio documentation: - - The stream will stop after all of the data currently buffered has been played. - - This maps with miniaudio's requirement that device's be drained which means we don't need to implement any draining logic. - */ - currentState = ((MA_PFN_AAudioStream_getState)pDevice->pContext->aaudio.AAudioStream_getState)(pStream); - if (currentState == MA_AAUDIO_STREAM_STATE_DISCONNECTED) { - return MA_SUCCESS; /* The device is disconnected. Don't try stopping it. */ - } - - resultAA = ((MA_PFN_AAudioStream_requestStop)pDevice->pContext->aaudio.AAudioStream_requestStop)(pStream); - if (resultAA != MA_AAUDIO_OK) { - return ma_result_from_aaudio(resultAA); - } - - /* The device should be in either a stopping or stopped state. If it's not set to started we need to wait for it to transition. It should go from stopping to stopped. */ - currentState = ((MA_PFN_AAudioStream_getState)pDevice->pContext->aaudio.AAudioStream_getState)(pStream); - if (currentState != MA_AAUDIO_STREAM_STATE_STOPPED) { - ma_result result; - - if (currentState != MA_AAUDIO_STREAM_STATE_STOPPING) { - return MA_ERROR; /* Expecting the stream to be a stopping or stopped state. */ - } - - result = ma_wait_for_simple_state_transition__aaudio(pDevice->pContext, pStream, currentState, MA_AAUDIO_STREAM_STATE_STOPPED); - if (result != MA_SUCCESS) { - return result; - } - } - - return MA_SUCCESS; -} - -static ma_result ma_device_start__aaudio(ma_device* pDevice) -{ - MA_ASSERT(pDevice != NULL); - - if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) { - ma_result result = ma_device_start_stream__aaudio(pDevice, (ma_AAudioStream*)pDevice->aaudio.pStreamCapture); - if (result != MA_SUCCESS) { - return result; - } - } - - if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) { - ma_result result = ma_device_start_stream__aaudio(pDevice, (ma_AAudioStream*)pDevice->aaudio.pStreamPlayback); - if (result != MA_SUCCESS) { - if (pDevice->type == ma_device_type_duplex) { - ma_device_stop_stream__aaudio(pDevice, (ma_AAudioStream*)pDevice->aaudio.pStreamCapture); - } - return result; - } - } - - return MA_SUCCESS; -} - -static ma_result ma_device_stop__aaudio(ma_device* pDevice) -{ - MA_ASSERT(pDevice != NULL); - - if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) { - ma_result result = ma_device_stop_stream__aaudio(pDevice, (ma_AAudioStream*)pDevice->aaudio.pStreamCapture); - if (result != MA_SUCCESS) { - return result; - } - } - - if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) { - ma_result result = ma_device_stop_stream__aaudio(pDevice, (ma_AAudioStream*)pDevice->aaudio.pStreamPlayback); - if (result != MA_SUCCESS) { - return result; - } - } - - ma_device__on_notification_stopped(pDevice); - - return MA_SUCCESS; -} - -static ma_result ma_device_reinit__aaudio(ma_device* pDevice, ma_device_type deviceType) -{ - ma_result result; - int32_t retries = 0; - - MA_ASSERT(pDevice != NULL); - - /* - TODO: Stop retrying if main thread is about to uninit device. - */ - ma_mutex_lock(&pDevice->aaudio.rerouteLock); - { -error_disconnected: - /* The first thing to do is close the streams. */ - ma_close_streams__aaudio(pDevice); - - /* Now we need to reinitialize each streams. The hardest part with this is just filling output the config and descriptors. */ - ma_device_config deviceConfig; - ma_device_descriptor descriptorPlayback; - ma_device_descriptor descriptorCapture; - - deviceConfig = ma_device_config_init(deviceType); - deviceConfig.playback.pDeviceID = NULL; /* Only doing rerouting with default devices. */ - deviceConfig.playback.shareMode = pDevice->playback.shareMode; - deviceConfig.playback.format = pDevice->playback.format; - deviceConfig.playback.channels = pDevice->playback.channels; - deviceConfig.capture.pDeviceID = NULL; /* Only doing rerouting with default devices. */ - deviceConfig.capture.shareMode = pDevice->capture.shareMode; - deviceConfig.capture.format = pDevice->capture.format; - deviceConfig.capture.channels = pDevice->capture.channels; - deviceConfig.sampleRate = pDevice->sampleRate; - deviceConfig.aaudio.usage = pDevice->aaudio.usage; - deviceConfig.aaudio.contentType = pDevice->aaudio.contentType; - deviceConfig.aaudio.inputPreset = pDevice->aaudio.inputPreset; - deviceConfig.aaudio.allowedCapturePolicy = pDevice->aaudio.allowedCapturePolicy; - deviceConfig.aaudio.noAutoStartAfterReroute = pDevice->aaudio.noAutoStartAfterReroute; - deviceConfig.periods = 1; - - /* Try to get an accurate period size. */ - if (deviceType == ma_device_type_playback || deviceType == ma_device_type_duplex) { - deviceConfig.periodSizeInFrames = pDevice->playback.internalPeriodSizeInFrames; - } else { - deviceConfig.periodSizeInFrames = pDevice->capture.internalPeriodSizeInFrames; - } - - if (deviceType == ma_device_type_capture || deviceType == ma_device_type_duplex || deviceType == ma_device_type_loopback) { - descriptorCapture.pDeviceID = deviceConfig.capture.pDeviceID; - descriptorCapture.shareMode = deviceConfig.capture.shareMode; - descriptorCapture.format = deviceConfig.capture.format; - descriptorCapture.channels = deviceConfig.capture.channels; - descriptorCapture.sampleRate = deviceConfig.sampleRate; - descriptorCapture.periodSizeInFrames = deviceConfig.periodSizeInFrames; - descriptorCapture.periodCount = deviceConfig.periods; - } - - if (deviceType == ma_device_type_playback || deviceType == ma_device_type_duplex) { - descriptorPlayback.pDeviceID = deviceConfig.playback.pDeviceID; - descriptorPlayback.shareMode = deviceConfig.playback.shareMode; - descriptorPlayback.format = deviceConfig.playback.format; - descriptorPlayback.channels = deviceConfig.playback.channels; - descriptorPlayback.sampleRate = deviceConfig.sampleRate; - descriptorPlayback.periodSizeInFrames = deviceConfig.periodSizeInFrames; - descriptorPlayback.periodCount = deviceConfig.periods; - } - - result = ma_device_init_streams__aaudio(pDevice, &deviceConfig, &descriptorPlayback, &descriptorCapture); - if (result != MA_SUCCESS) { - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_WARNING, "[AAudio] Failed to create stream after route change."); - goto done; - } - - result = ma_device_post_init(pDevice, deviceType, &descriptorPlayback, &descriptorCapture); - if (result != MA_SUCCESS) { - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_WARNING, "[AAudio] Failed to initialize device after route change."); - ma_close_streams__aaudio(pDevice); - goto done; - } - - /* We'll only ever do this in response to a reroute. */ - ma_device__on_notification_rerouted(pDevice); - - /* If the device is started, start the streams. Maybe make this configurable? */ - if (ma_device_get_state(pDevice) == ma_device_state_started) { - if (pDevice->aaudio.noAutoStartAfterReroute == MA_FALSE) { - result = ma_device_start__aaudio(pDevice); - if (result != MA_SUCCESS) { - /* We got disconnected! Retry a few times, until we find a connected device! */ - retries += 1; - if (retries <= 3) { - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, "[AAudio] Failed to start stream after route change, retrying(%d)", retries); - goto error_disconnected; - } - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, "[AAudio] Failed to start stream after route change."); - goto done; - } - } else { - ma_device_stop(pDevice); /* Do a full device stop so we set internal state correctly. */ - } - } - - result = MA_SUCCESS; - } -done: - /* Re-routing done */ - ma_mutex_unlock(&pDevice->aaudio.rerouteLock); - - return result; -} - -static ma_result ma_device_get_info__aaudio(ma_device* pDevice, ma_device_type type, ma_device_info* pDeviceInfo) -{ - ma_AAudioStream* pStream = NULL; - - MA_ASSERT(pDevice != NULL); - MA_ASSERT(type != ma_device_type_duplex); - MA_ASSERT(pDeviceInfo != NULL); - - if (type == ma_device_type_capture) { - pStream = (ma_AAudioStream*)pDevice->aaudio.pStreamCapture; - pDeviceInfo->id.aaudio = pDevice->capture.id.aaudio; - ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), MA_DEFAULT_CAPTURE_DEVICE_NAME, (size_t)-1); /* Only supporting default devices. */ - } - if (type == ma_device_type_playback) { - pStream = (ma_AAudioStream*)pDevice->aaudio.pStreamPlayback; - pDeviceInfo->id.aaudio = pDevice->playback.id.aaudio; - ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), MA_DEFAULT_PLAYBACK_DEVICE_NAME, (size_t)-1); /* Only supporting default devices. */ - } - - /* Safety. Should never happen. */ - if (pStream == NULL) { - return MA_INVALID_OPERATION; - } - - pDeviceInfo->nativeDataFormatCount = 0; - ma_context_add_native_data_format_from_AAudioStream__aaudio(pDevice->pContext, pStream, 0, pDeviceInfo); - - return MA_SUCCESS; -} - - -static ma_result ma_context_uninit__aaudio(ma_context* pContext) -{ - MA_ASSERT(pContext != NULL); - MA_ASSERT(pContext->backend == ma_backend_aaudio); - - ma_device_job_thread_uninit(&pContext->aaudio.jobThread, &pContext->allocationCallbacks); - - ma_dlclose(ma_context_get_log(pContext), pContext->aaudio.hAAudio); - pContext->aaudio.hAAudio = NULL; - - return MA_SUCCESS; -} - -static ma_result ma_context_init__aaudio(ma_context* pContext, const ma_context_config* pConfig, ma_backend_callbacks* pCallbacks) -{ -#if !defined(MA_NO_RUNTIME_LINKING) - size_t i; - const char* libNames[] = { - "libaaudio.so" - }; - - for (i = 0; i < ma_countof(libNames); ++i) { - pContext->aaudio.hAAudio = ma_dlopen(ma_context_get_log(pContext), libNames[i]); - if (pContext->aaudio.hAAudio != NULL) { - break; - } - } - - if (pContext->aaudio.hAAudio == NULL) { - return MA_FAILED_TO_INIT_BACKEND; - } - - pContext->aaudio.AAudio_createStreamBuilder = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->aaudio.hAAudio, "AAudio_createStreamBuilder"); - pContext->aaudio.AAudioStreamBuilder_delete = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->aaudio.hAAudio, "AAudioStreamBuilder_delete"); - pContext->aaudio.AAudioStreamBuilder_setDeviceId = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->aaudio.hAAudio, "AAudioStreamBuilder_setDeviceId"); - pContext->aaudio.AAudioStreamBuilder_setDirection = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->aaudio.hAAudio, "AAudioStreamBuilder_setDirection"); - pContext->aaudio.AAudioStreamBuilder_setSharingMode = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->aaudio.hAAudio, "AAudioStreamBuilder_setSharingMode"); - pContext->aaudio.AAudioStreamBuilder_setFormat = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->aaudio.hAAudio, "AAudioStreamBuilder_setFormat"); - pContext->aaudio.AAudioStreamBuilder_setChannelCount = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->aaudio.hAAudio, "AAudioStreamBuilder_setChannelCount"); - pContext->aaudio.AAudioStreamBuilder_setSampleRate = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->aaudio.hAAudio, "AAudioStreamBuilder_setSampleRate"); - pContext->aaudio.AAudioStreamBuilder_setBufferCapacityInFrames = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->aaudio.hAAudio, "AAudioStreamBuilder_setBufferCapacityInFrames"); - pContext->aaudio.AAudioStreamBuilder_setFramesPerDataCallback = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->aaudio.hAAudio, "AAudioStreamBuilder_setFramesPerDataCallback"); - pContext->aaudio.AAudioStreamBuilder_setDataCallback = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->aaudio.hAAudio, "AAudioStreamBuilder_setDataCallback"); - pContext->aaudio.AAudioStreamBuilder_setErrorCallback = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->aaudio.hAAudio, "AAudioStreamBuilder_setErrorCallback"); - pContext->aaudio.AAudioStreamBuilder_setPerformanceMode = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->aaudio.hAAudio, "AAudioStreamBuilder_setPerformanceMode"); - pContext->aaudio.AAudioStreamBuilder_setUsage = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->aaudio.hAAudio, "AAudioStreamBuilder_setUsage"); - pContext->aaudio.AAudioStreamBuilder_setContentType = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->aaudio.hAAudio, "AAudioStreamBuilder_setContentType"); - pContext->aaudio.AAudioStreamBuilder_setInputPreset = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->aaudio.hAAudio, "AAudioStreamBuilder_setInputPreset"); - pContext->aaudio.AAudioStreamBuilder_setAllowedCapturePolicy = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->aaudio.hAAudio, "AAudioStreamBuilder_setAllowedCapturePolicy"); - pContext->aaudio.AAudioStreamBuilder_openStream = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->aaudio.hAAudio, "AAudioStreamBuilder_openStream"); - pContext->aaudio.AAudioStream_close = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->aaudio.hAAudio, "AAudioStream_close"); - pContext->aaudio.AAudioStream_getState = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->aaudio.hAAudio, "AAudioStream_getState"); - pContext->aaudio.AAudioStream_waitForStateChange = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->aaudio.hAAudio, "AAudioStream_waitForStateChange"); - pContext->aaudio.AAudioStream_getFormat = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->aaudio.hAAudio, "AAudioStream_getFormat"); - pContext->aaudio.AAudioStream_getChannelCount = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->aaudio.hAAudio, "AAudioStream_getChannelCount"); - pContext->aaudio.AAudioStream_getSampleRate = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->aaudio.hAAudio, "AAudioStream_getSampleRate"); - pContext->aaudio.AAudioStream_getBufferCapacityInFrames = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->aaudio.hAAudio, "AAudioStream_getBufferCapacityInFrames"); - pContext->aaudio.AAudioStream_getFramesPerDataCallback = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->aaudio.hAAudio, "AAudioStream_getFramesPerDataCallback"); - pContext->aaudio.AAudioStream_getFramesPerBurst = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->aaudio.hAAudio, "AAudioStream_getFramesPerBurst"); - pContext->aaudio.AAudioStream_requestStart = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->aaudio.hAAudio, "AAudioStream_requestStart"); - pContext->aaudio.AAudioStream_requestStop = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->aaudio.hAAudio, "AAudioStream_requestStop"); -#else - pContext->aaudio.AAudio_createStreamBuilder = (ma_proc)AAudio_createStreamBuilder; - pContext->aaudio.AAudioStreamBuilder_delete = (ma_proc)AAudioStreamBuilder_delete; - pContext->aaudio.AAudioStreamBuilder_setDeviceId = (ma_proc)AAudioStreamBuilder_setDeviceId; - pContext->aaudio.AAudioStreamBuilder_setDirection = (ma_proc)AAudioStreamBuilder_setDirection; - pContext->aaudio.AAudioStreamBuilder_setSharingMode = (ma_proc)AAudioStreamBuilder_setSharingMode; - pContext->aaudio.AAudioStreamBuilder_setFormat = (ma_proc)AAudioStreamBuilder_setFormat; - pContext->aaudio.AAudioStreamBuilder_setChannelCount = (ma_proc)AAudioStreamBuilder_setChannelCount; - pContext->aaudio.AAudioStreamBuilder_setSampleRate = (ma_proc)AAudioStreamBuilder_setSampleRate; - pContext->aaudio.AAudioStreamBuilder_setBufferCapacityInFrames = (ma_proc)AAudioStreamBuilder_setBufferCapacityInFrames; - pContext->aaudio.AAudioStreamBuilder_setFramesPerDataCallback = (ma_proc)AAudioStreamBuilder_setFramesPerDataCallback; - pContext->aaudio.AAudioStreamBuilder_setDataCallback = (ma_proc)AAudioStreamBuilder_setDataCallback; - pContext->aaudio.AAudioStreamBuilder_setErrorCallback = (ma_proc)AAudioStreamBuilder_setErrorCallback; - pContext->aaudio.AAudioStreamBuilder_setPerformanceMode = (ma_proc)AAudioStreamBuilder_setPerformanceMode; - pContext->aaudio.AAudioStreamBuilder_setUsage = (ma_proc)AAudioStreamBuilder_setUsage; - pContext->aaudio.AAudioStreamBuilder_setContentType = (ma_proc)AAudioStreamBuilder_setContentType; - pContext->aaudio.AAudioStreamBuilder_setInputPreset = (ma_proc)AAudioStreamBuilder_setInputPreset; - #if defined(__ANDROID_API__) && __ANDROID_API__ >= 29 - pContext->aaudio.AAudioStreamBuilder_setAllowedCapturePolicy = (ma_proc)AAudioStreamBuilder_setAllowedCapturePolicy; - #endif - pContext->aaudio.AAudioStreamBuilder_openStream = (ma_proc)AAudioStreamBuilder_openStream; - pContext->aaudio.AAudioStream_close = (ma_proc)AAudioStream_close; - pContext->aaudio.AAudioStream_getState = (ma_proc)AAudioStream_getState; - pContext->aaudio.AAudioStream_waitForStateChange = (ma_proc)AAudioStream_waitForStateChange; - pContext->aaudio.AAudioStream_getFormat = (ma_proc)AAudioStream_getFormat; - pContext->aaudio.AAudioStream_getChannelCount = (ma_proc)AAudioStream_getChannelCount; - pContext->aaudio.AAudioStream_getSampleRate = (ma_proc)AAudioStream_getSampleRate; - pContext->aaudio.AAudioStream_getBufferCapacityInFrames = (ma_proc)AAudioStream_getBufferCapacityInFrames; - pContext->aaudio.AAudioStream_getFramesPerDataCallback = (ma_proc)AAudioStream_getFramesPerDataCallback; - pContext->aaudio.AAudioStream_getFramesPerBurst = (ma_proc)AAudioStream_getFramesPerBurst; - pContext->aaudio.AAudioStream_requestStart = (ma_proc)AAudioStream_requestStart; - pContext->aaudio.AAudioStream_requestStop = (ma_proc)AAudioStream_requestStop; -#endif - - pCallbacks->onContextInit = ma_context_init__aaudio; - pCallbacks->onContextUninit = ma_context_uninit__aaudio; - pCallbacks->onContextEnumerateDevices = ma_context_enumerate_devices__aaudio; - pCallbacks->onContextGetDeviceInfo = ma_context_get_device_info__aaudio; - pCallbacks->onDeviceInit = ma_device_init__aaudio; - pCallbacks->onDeviceUninit = ma_device_uninit__aaudio; - pCallbacks->onDeviceStart = ma_device_start__aaudio; - pCallbacks->onDeviceStop = ma_device_stop__aaudio; - pCallbacks->onDeviceRead = NULL; /* Not used because AAudio is asynchronous. */ - pCallbacks->onDeviceWrite = NULL; /* Not used because AAudio is asynchronous. */ - pCallbacks->onDeviceDataLoop = NULL; /* Not used because AAudio is asynchronous. */ - pCallbacks->onDeviceGetInfo = ma_device_get_info__aaudio; - - - /* We need a job thread so we can deal with rerouting. */ - { - ma_result result; - ma_device_job_thread_config jobThreadConfig; - - jobThreadConfig = ma_device_job_thread_config_init(); - - result = ma_device_job_thread_init(&jobThreadConfig, &pContext->allocationCallbacks, &pContext->aaudio.jobThread); - if (result != MA_SUCCESS) { - ma_dlclose(ma_context_get_log(pContext), pContext->aaudio.hAAudio); - pContext->aaudio.hAAudio = NULL; - return result; - } - } - - - (void)pConfig; - return MA_SUCCESS; -} - -static ma_result ma_job_process__device__aaudio_reroute(ma_job* pJob) -{ - ma_result result; - ma_device* pDevice; - - MA_ASSERT(pJob != NULL); - - pDevice = (ma_device*)pJob->data.device.aaudio.reroute.pDevice; - MA_ASSERT(pDevice != NULL); - - /* Here is where we need to reroute the device. To do this we need to uninitialize the stream and reinitialize it. */ - result = ma_device_reinit__aaudio(pDevice, (ma_device_type)pJob->data.device.aaudio.reroute.deviceType); - if (result != MA_SUCCESS) { - /* - Getting here means we failed to reroute the device. The best thing I can think of here is to - just stop the device. - */ - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[AAudio] Stopping device due to reroute failure."); - ma_device_stop(pDevice); - return result; - } - - return MA_SUCCESS; -} -#else -/* Getting here means there is no AAudio backend so we need a no-op job implementation. */ -static ma_result ma_job_process__device__aaudio_reroute(ma_job* pJob) -{ - return ma_job_process__noop(pJob); -} -#endif /* AAudio */ - - -/****************************************************************************** - -OpenSL|ES Backend - -******************************************************************************/ -#ifdef MA_HAS_OPENSL -#include -#ifdef MA_ANDROID -#include -#endif - -typedef SLresult (SLAPIENTRY * ma_slCreateEngine_proc)(SLObjectItf* pEngine, SLuint32 numOptions, SLEngineOption* pEngineOptions, SLuint32 numInterfaces, SLInterfaceID* pInterfaceIds, SLboolean* pInterfaceRequired); - -/* OpenSL|ES has one-per-application objects :( */ -static SLObjectItf g_maEngineObjectSL = NULL; -static SLEngineItf g_maEngineSL = NULL; -static ma_uint32 g_maOpenSLInitCounter = 0; -static ma_spinlock g_maOpenSLSpinlock = 0; /* For init/uninit. */ - -#define MA_OPENSL_OBJ(p) (*((SLObjectItf)(p))) -#define MA_OPENSL_OUTPUTMIX(p) (*((SLOutputMixItf)(p))) -#define MA_OPENSL_PLAY(p) (*((SLPlayItf)(p))) -#define MA_OPENSL_RECORD(p) (*((SLRecordItf)(p))) - -#ifdef MA_ANDROID -#define MA_OPENSL_BUFFERQUEUE(p) (*((SLAndroidSimpleBufferQueueItf)(p))) -#else -#define MA_OPENSL_BUFFERQUEUE(p) (*((SLBufferQueueItf)(p))) -#endif - -static ma_result ma_result_from_OpenSL(SLuint32 result) -{ - switch (result) - { - case SL_RESULT_SUCCESS: return MA_SUCCESS; - case SL_RESULT_PRECONDITIONS_VIOLATED: return MA_ERROR; - case SL_RESULT_PARAMETER_INVALID: return MA_INVALID_ARGS; - case SL_RESULT_MEMORY_FAILURE: return MA_OUT_OF_MEMORY; - case SL_RESULT_RESOURCE_ERROR: return MA_INVALID_DATA; - case SL_RESULT_RESOURCE_LOST: return MA_ERROR; - case SL_RESULT_IO_ERROR: return MA_IO_ERROR; - case SL_RESULT_BUFFER_INSUFFICIENT: return MA_NO_SPACE; - case SL_RESULT_CONTENT_CORRUPTED: return MA_INVALID_DATA; - case SL_RESULT_CONTENT_UNSUPPORTED: return MA_FORMAT_NOT_SUPPORTED; - case SL_RESULT_CONTENT_NOT_FOUND: return MA_ERROR; - case SL_RESULT_PERMISSION_DENIED: return MA_ACCESS_DENIED; - case SL_RESULT_FEATURE_UNSUPPORTED: return MA_NOT_IMPLEMENTED; - case SL_RESULT_INTERNAL_ERROR: return MA_ERROR; - case SL_RESULT_UNKNOWN_ERROR: return MA_ERROR; - case SL_RESULT_OPERATION_ABORTED: return MA_ERROR; - case SL_RESULT_CONTROL_LOST: return MA_ERROR; - default: return MA_ERROR; - } -} - -/* Converts an individual OpenSL-style channel identifier (SL_SPEAKER_FRONT_LEFT, etc.) to miniaudio. */ -static ma_uint8 ma_channel_id_to_ma__opensl(SLuint32 id) -{ - switch (id) - { - case SL_SPEAKER_FRONT_LEFT: return MA_CHANNEL_FRONT_LEFT; - case SL_SPEAKER_FRONT_RIGHT: return MA_CHANNEL_FRONT_RIGHT; - case SL_SPEAKER_FRONT_CENTER: return MA_CHANNEL_FRONT_CENTER; - case SL_SPEAKER_LOW_FREQUENCY: return MA_CHANNEL_LFE; - case SL_SPEAKER_BACK_LEFT: return MA_CHANNEL_BACK_LEFT; - case SL_SPEAKER_BACK_RIGHT: return MA_CHANNEL_BACK_RIGHT; - case SL_SPEAKER_FRONT_LEFT_OF_CENTER: return MA_CHANNEL_FRONT_LEFT_CENTER; - case SL_SPEAKER_FRONT_RIGHT_OF_CENTER: return MA_CHANNEL_FRONT_RIGHT_CENTER; - case SL_SPEAKER_BACK_CENTER: return MA_CHANNEL_BACK_CENTER; - case SL_SPEAKER_SIDE_LEFT: return MA_CHANNEL_SIDE_LEFT; - case SL_SPEAKER_SIDE_RIGHT: return MA_CHANNEL_SIDE_RIGHT; - case SL_SPEAKER_TOP_CENTER: return MA_CHANNEL_TOP_CENTER; - case SL_SPEAKER_TOP_FRONT_LEFT: return MA_CHANNEL_TOP_FRONT_LEFT; - case SL_SPEAKER_TOP_FRONT_CENTER: return MA_CHANNEL_TOP_FRONT_CENTER; - case SL_SPEAKER_TOP_FRONT_RIGHT: return MA_CHANNEL_TOP_FRONT_RIGHT; - case SL_SPEAKER_TOP_BACK_LEFT: return MA_CHANNEL_TOP_BACK_LEFT; - case SL_SPEAKER_TOP_BACK_CENTER: return MA_CHANNEL_TOP_BACK_CENTER; - case SL_SPEAKER_TOP_BACK_RIGHT: return MA_CHANNEL_TOP_BACK_RIGHT; - default: return 0; - } -} - -/* Converts an individual miniaudio channel identifier (MA_CHANNEL_FRONT_LEFT, etc.) to OpenSL-style. */ -static SLuint32 ma_channel_id_to_opensl(ma_uint8 id) -{ - switch (id) - { - case MA_CHANNEL_MONO: return SL_SPEAKER_FRONT_CENTER; - case MA_CHANNEL_FRONT_LEFT: return SL_SPEAKER_FRONT_LEFT; - case MA_CHANNEL_FRONT_RIGHT: return SL_SPEAKER_FRONT_RIGHT; - case MA_CHANNEL_FRONT_CENTER: return SL_SPEAKER_FRONT_CENTER; - case MA_CHANNEL_LFE: return SL_SPEAKER_LOW_FREQUENCY; - case MA_CHANNEL_BACK_LEFT: return SL_SPEAKER_BACK_LEFT; - case MA_CHANNEL_BACK_RIGHT: return SL_SPEAKER_BACK_RIGHT; - case MA_CHANNEL_FRONT_LEFT_CENTER: return SL_SPEAKER_FRONT_LEFT_OF_CENTER; - case MA_CHANNEL_FRONT_RIGHT_CENTER: return SL_SPEAKER_FRONT_RIGHT_OF_CENTER; - case MA_CHANNEL_BACK_CENTER: return SL_SPEAKER_BACK_CENTER; - case MA_CHANNEL_SIDE_LEFT: return SL_SPEAKER_SIDE_LEFT; - case MA_CHANNEL_SIDE_RIGHT: return SL_SPEAKER_SIDE_RIGHT; - case MA_CHANNEL_TOP_CENTER: return SL_SPEAKER_TOP_CENTER; - case MA_CHANNEL_TOP_FRONT_LEFT: return SL_SPEAKER_TOP_FRONT_LEFT; - case MA_CHANNEL_TOP_FRONT_CENTER: return SL_SPEAKER_TOP_FRONT_CENTER; - case MA_CHANNEL_TOP_FRONT_RIGHT: return SL_SPEAKER_TOP_FRONT_RIGHT; - case MA_CHANNEL_TOP_BACK_LEFT: return SL_SPEAKER_TOP_BACK_LEFT; - case MA_CHANNEL_TOP_BACK_CENTER: return SL_SPEAKER_TOP_BACK_CENTER; - case MA_CHANNEL_TOP_BACK_RIGHT: return SL_SPEAKER_TOP_BACK_RIGHT; - default: return 0; - } -} - -/* Converts a channel mapping to an OpenSL-style channel mask. */ -static SLuint32 ma_channel_map_to_channel_mask__opensl(const ma_channel* pChannelMap, ma_uint32 channels) -{ - SLuint32 channelMask = 0; - ma_uint32 iChannel; - for (iChannel = 0; iChannel < channels; ++iChannel) { - channelMask |= ma_channel_id_to_opensl(pChannelMap[iChannel]); - } - - return channelMask; -} - -/* Converts an OpenSL-style channel mask to a miniaudio channel map. */ -static void ma_channel_mask_to_channel_map__opensl(SLuint32 channelMask, ma_uint32 channels, ma_channel* pChannelMap) -{ - if (channels == 1 && channelMask == 0) { - pChannelMap[0] = MA_CHANNEL_MONO; - } else if (channels == 2 && channelMask == 0) { - pChannelMap[0] = MA_CHANNEL_FRONT_LEFT; - pChannelMap[1] = MA_CHANNEL_FRONT_RIGHT; - } else { - if (channels == 1 && (channelMask & SL_SPEAKER_FRONT_CENTER) != 0) { - pChannelMap[0] = MA_CHANNEL_MONO; - } else { - /* Just iterate over each bit. */ - ma_uint32 iChannel = 0; - ma_uint32 iBit; - for (iBit = 0; iBit < 32 && iChannel < channels; ++iBit) { - SLuint32 bitValue = (channelMask & (1UL << iBit)); - if (bitValue != 0) { - /* The bit is set. */ - pChannelMap[iChannel] = ma_channel_id_to_ma__opensl(bitValue); - iChannel += 1; - } - } - } - } -} - -static SLuint32 ma_round_to_standard_sample_rate__opensl(SLuint32 samplesPerSec) -{ - if (samplesPerSec <= SL_SAMPLINGRATE_8) { - return SL_SAMPLINGRATE_8; - } - if (samplesPerSec <= SL_SAMPLINGRATE_11_025) { - return SL_SAMPLINGRATE_11_025; - } - if (samplesPerSec <= SL_SAMPLINGRATE_12) { - return SL_SAMPLINGRATE_12; - } - if (samplesPerSec <= SL_SAMPLINGRATE_16) { - return SL_SAMPLINGRATE_16; - } - if (samplesPerSec <= SL_SAMPLINGRATE_22_05) { - return SL_SAMPLINGRATE_22_05; - } - if (samplesPerSec <= SL_SAMPLINGRATE_24) { - return SL_SAMPLINGRATE_24; - } - if (samplesPerSec <= SL_SAMPLINGRATE_32) { - return SL_SAMPLINGRATE_32; - } - if (samplesPerSec <= SL_SAMPLINGRATE_44_1) { - return SL_SAMPLINGRATE_44_1; - } - if (samplesPerSec <= SL_SAMPLINGRATE_48) { - return SL_SAMPLINGRATE_48; - } - - /* Android doesn't support more than 48000. */ -#ifndef MA_ANDROID - if (samplesPerSec <= SL_SAMPLINGRATE_64) { - return SL_SAMPLINGRATE_64; - } - if (samplesPerSec <= SL_SAMPLINGRATE_88_2) { - return SL_SAMPLINGRATE_88_2; - } - if (samplesPerSec <= SL_SAMPLINGRATE_96) { - return SL_SAMPLINGRATE_96; - } - if (samplesPerSec <= SL_SAMPLINGRATE_192) { - return SL_SAMPLINGRATE_192; - } -#endif - - return SL_SAMPLINGRATE_16; -} - - -static SLint32 ma_to_stream_type__opensl(ma_opensl_stream_type streamType) -{ - switch (streamType) { - case ma_opensl_stream_type_voice: return SL_ANDROID_STREAM_VOICE; - case ma_opensl_stream_type_system: return SL_ANDROID_STREAM_SYSTEM; - case ma_opensl_stream_type_ring: return SL_ANDROID_STREAM_RING; - case ma_opensl_stream_type_media: return SL_ANDROID_STREAM_MEDIA; - case ma_opensl_stream_type_alarm: return SL_ANDROID_STREAM_ALARM; - case ma_opensl_stream_type_notification: return SL_ANDROID_STREAM_NOTIFICATION; - default: break; - } - - return SL_ANDROID_STREAM_VOICE; -} - -static SLint32 ma_to_recording_preset__opensl(ma_opensl_recording_preset recordingPreset) -{ - switch (recordingPreset) { - case ma_opensl_recording_preset_generic: return SL_ANDROID_RECORDING_PRESET_GENERIC; - case ma_opensl_recording_preset_camcorder: return SL_ANDROID_RECORDING_PRESET_CAMCORDER; - case ma_opensl_recording_preset_voice_recognition: return SL_ANDROID_RECORDING_PRESET_VOICE_RECOGNITION; - case ma_opensl_recording_preset_voice_communication: return SL_ANDROID_RECORDING_PRESET_VOICE_COMMUNICATION; - case ma_opensl_recording_preset_voice_unprocessed: return SL_ANDROID_RECORDING_PRESET_UNPROCESSED; - default: break; - } - - return SL_ANDROID_RECORDING_PRESET_NONE; -} - - -static ma_result ma_context_enumerate_devices__opensl(ma_context* pContext, ma_enum_devices_callback_proc callback, void* pUserData) -{ - ma_bool32 cbResult; - - MA_ASSERT(pContext != NULL); - MA_ASSERT(callback != NULL); - - MA_ASSERT(g_maOpenSLInitCounter > 0); /* <-- If you trigger this it means you've either not initialized the context, or you've uninitialized it and then attempted to enumerate devices. */ - if (g_maOpenSLInitCounter == 0) { - return MA_INVALID_OPERATION; - } - - /* - TODO: Test Me. - - This is currently untested, so for now we are just returning default devices. - */ -#if 0 && !defined(MA_ANDROID) - ma_bool32 isTerminated = MA_FALSE; - - SLuint32 pDeviceIDs[128]; - SLint32 deviceCount = sizeof(pDeviceIDs) / sizeof(pDeviceIDs[0]); - - SLAudioIODeviceCapabilitiesItf deviceCaps; - SLresult resultSL = (*g_maEngineObjectSL)->GetInterface(g_maEngineObjectSL, (SLInterfaceID)pContext->opensl.SL_IID_AUDIOIODEVICECAPABILITIES, &deviceCaps); - if (resultSL != SL_RESULT_SUCCESS) { - /* The interface may not be supported so just report a default device. */ - goto return_default_device; - } - - /* Playback */ - if (!isTerminated) { - resultSL = (*deviceCaps)->GetAvailableAudioOutputs(deviceCaps, &deviceCount, pDeviceIDs); - if (resultSL != SL_RESULT_SUCCESS) { - return ma_result_from_OpenSL(resultSL); - } - - for (SLint32 iDevice = 0; iDevice < deviceCount; ++iDevice) { - ma_device_info deviceInfo; - MA_ZERO_OBJECT(&deviceInfo); - deviceInfo.id.opensl = pDeviceIDs[iDevice]; - - SLAudioOutputDescriptor desc; - resultSL = (*deviceCaps)->QueryAudioOutputCapabilities(deviceCaps, deviceInfo.id.opensl, &desc); - if (resultSL == SL_RESULT_SUCCESS) { - ma_strncpy_s(deviceInfo.name, sizeof(deviceInfo.name), (const char*)desc.pDeviceName, (size_t)-1); - - ma_bool32 cbResult = callback(pContext, ma_device_type_playback, &deviceInfo, pUserData); - if (cbResult == MA_FALSE) { - isTerminated = MA_TRUE; - break; - } - } - } - } - - /* Capture */ - if (!isTerminated) { - resultSL = (*deviceCaps)->GetAvailableAudioInputs(deviceCaps, &deviceCount, pDeviceIDs); - if (resultSL != SL_RESULT_SUCCESS) { - return ma_result_from_OpenSL(resultSL); - } - - for (SLint32 iDevice = 0; iDevice < deviceCount; ++iDevice) { - ma_device_info deviceInfo; - MA_ZERO_OBJECT(&deviceInfo); - deviceInfo.id.opensl = pDeviceIDs[iDevice]; - - SLAudioInputDescriptor desc; - resultSL = (*deviceCaps)->QueryAudioInputCapabilities(deviceCaps, deviceInfo.id.opensl, &desc); - if (resultSL == SL_RESULT_SUCCESS) { - ma_strncpy_s(deviceInfo.name, sizeof(deviceInfo.name), (const char*)desc.deviceName, (size_t)-1); - - ma_bool32 cbResult = callback(pContext, ma_device_type_capture, &deviceInfo, pUserData); - if (cbResult == MA_FALSE) { - isTerminated = MA_TRUE; - break; - } - } - } - } - - return MA_SUCCESS; -#else - goto return_default_device; -#endif - -return_default_device:; - cbResult = MA_TRUE; - - /* Playback. */ - if (cbResult) { - ma_device_info deviceInfo; - MA_ZERO_OBJECT(&deviceInfo); - deviceInfo.id.opensl = SL_DEFAULTDEVICEID_AUDIOOUTPUT; - ma_strncpy_s(deviceInfo.name, sizeof(deviceInfo.name), MA_DEFAULT_PLAYBACK_DEVICE_NAME, (size_t)-1); - cbResult = callback(pContext, ma_device_type_playback, &deviceInfo, pUserData); - } - - /* Capture. */ - if (cbResult) { - ma_device_info deviceInfo; - MA_ZERO_OBJECT(&deviceInfo); - deviceInfo.id.opensl = SL_DEFAULTDEVICEID_AUDIOINPUT; - ma_strncpy_s(deviceInfo.name, sizeof(deviceInfo.name), MA_DEFAULT_CAPTURE_DEVICE_NAME, (size_t)-1); - cbResult = callback(pContext, ma_device_type_capture, &deviceInfo, pUserData); - } - - return MA_SUCCESS; -} - -static void ma_context_add_data_format_ex__opensl(ma_context* pContext, ma_format format, ma_uint32 channels, ma_uint32 sampleRate, ma_device_info* pDeviceInfo) -{ - MA_ASSERT(pContext != NULL); - MA_ASSERT(pDeviceInfo != NULL); - - pDeviceInfo->nativeDataFormats[pDeviceInfo->nativeDataFormatCount].format = format; - pDeviceInfo->nativeDataFormats[pDeviceInfo->nativeDataFormatCount].channels = channels; - pDeviceInfo->nativeDataFormats[pDeviceInfo->nativeDataFormatCount].sampleRate = sampleRate; - pDeviceInfo->nativeDataFormats[pDeviceInfo->nativeDataFormatCount].flags = 0; - pDeviceInfo->nativeDataFormatCount += 1; -} - -static void ma_context_add_data_format__opensl(ma_context* pContext, ma_format format, ma_device_info* pDeviceInfo) -{ - ma_uint32 minChannels = 1; - ma_uint32 maxChannels = 2; - ma_uint32 minSampleRate = (ma_uint32)ma_standard_sample_rate_8000; - ma_uint32 maxSampleRate = (ma_uint32)ma_standard_sample_rate_48000; - ma_uint32 iChannel; - ma_uint32 iSampleRate; - - MA_ASSERT(pContext != NULL); - MA_ASSERT(pDeviceInfo != NULL); - - /* - Each sample format can support mono and stereo, and we'll support a small subset of standard - rates (up to 48000). A better solution would be to somehow find a native sample rate. - */ - for (iChannel = minChannels; iChannel < maxChannels; iChannel += 1) { - for (iSampleRate = 0; iSampleRate < ma_countof(g_maStandardSampleRatePriorities); iSampleRate += 1) { - ma_uint32 standardSampleRate = g_maStandardSampleRatePriorities[iSampleRate]; - if (standardSampleRate >= minSampleRate && standardSampleRate <= maxSampleRate) { - ma_context_add_data_format_ex__opensl(pContext, format, iChannel, standardSampleRate, pDeviceInfo); - } - } - } -} - -static ma_result ma_context_get_device_info__opensl(ma_context* pContext, ma_device_type deviceType, const ma_device_id* pDeviceID, ma_device_info* pDeviceInfo) -{ - MA_ASSERT(pContext != NULL); - - MA_ASSERT(g_maOpenSLInitCounter > 0); /* <-- If you trigger this it means you've either not initialized the context, or you've uninitialized it and then attempted to get device info. */ - if (g_maOpenSLInitCounter == 0) { - return MA_INVALID_OPERATION; - } - - /* - TODO: Test Me. - - This is currently untested, so for now we are just returning default devices. - */ -#if 0 && !defined(MA_ANDROID) - SLAudioIODeviceCapabilitiesItf deviceCaps; - SLresult resultSL = (*g_maEngineObjectSL)->GetInterface(g_maEngineObjectSL, (SLInterfaceID)pContext->opensl.SL_IID_AUDIOIODEVICECAPABILITIES, &deviceCaps); - if (resultSL != SL_RESULT_SUCCESS) { - /* The interface may not be supported so just report a default device. */ - goto return_default_device; - } - - if (deviceType == ma_device_type_playback) { - SLAudioOutputDescriptor desc; - resultSL = (*deviceCaps)->QueryAudioOutputCapabilities(deviceCaps, pDeviceID->opensl, &desc); - if (resultSL != SL_RESULT_SUCCESS) { - return ma_result_from_OpenSL(resultSL); - } - - ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), (const char*)desc.pDeviceName, (size_t)-1); - } else { - SLAudioInputDescriptor desc; - resultSL = (*deviceCaps)->QueryAudioInputCapabilities(deviceCaps, pDeviceID->opensl, &desc); - if (resultSL != SL_RESULT_SUCCESS) { - return ma_result_from_OpenSL(resultSL); - } - - ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), (const char*)desc.deviceName, (size_t)-1); - } - - goto return_detailed_info; -#else - goto return_default_device; -#endif - -return_default_device: - if (pDeviceID != NULL) { - if ((deviceType == ma_device_type_playback && pDeviceID->opensl != SL_DEFAULTDEVICEID_AUDIOOUTPUT) || - (deviceType == ma_device_type_capture && pDeviceID->opensl != SL_DEFAULTDEVICEID_AUDIOINPUT)) { - return MA_NO_DEVICE; /* Don't know the device. */ - } - } - - /* ID and Name / Description */ - if (deviceType == ma_device_type_playback) { - pDeviceInfo->id.opensl = SL_DEFAULTDEVICEID_AUDIOOUTPUT; - ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), MA_DEFAULT_PLAYBACK_DEVICE_NAME, (size_t)-1); - } else { - pDeviceInfo->id.opensl = SL_DEFAULTDEVICEID_AUDIOINPUT; - ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), MA_DEFAULT_CAPTURE_DEVICE_NAME, (size_t)-1); - } - - pDeviceInfo->isDefault = MA_TRUE; - - goto return_detailed_info; - - -return_detailed_info: - - /* - For now we're just outputting a set of values that are supported by the API but not necessarily supported - by the device natively. Later on we should work on this so that it more closely reflects the device's - actual native format. - */ - pDeviceInfo->nativeDataFormatCount = 0; -#if defined(MA_ANDROID) && __ANDROID_API__ >= 21 - ma_context_add_data_format__opensl(pContext, ma_format_f32, pDeviceInfo); -#endif - ma_context_add_data_format__opensl(pContext, ma_format_s16, pDeviceInfo); - ma_context_add_data_format__opensl(pContext, ma_format_u8, pDeviceInfo); - - return MA_SUCCESS; -} - - -#ifdef MA_ANDROID -/*void ma_buffer_queue_callback_capture__opensl_android(SLAndroidSimpleBufferQueueItf pBufferQueue, SLuint32 eventFlags, const void* pBuffer, SLuint32 bufferSize, SLuint32 dataUsed, void* pContext)*/ -static void ma_buffer_queue_callback_capture__opensl_android(SLAndroidSimpleBufferQueueItf pBufferQueue, void* pUserData) -{ - ma_device* pDevice = (ma_device*)pUserData; - size_t periodSizeInBytes; - ma_uint8* pBuffer; - SLresult resultSL; - - MA_ASSERT(pDevice != NULL); - - (void)pBufferQueue; - - /* - For now, don't do anything unless the buffer was fully processed. From what I can tell, it looks like - OpenSL|ES 1.1 improves on buffer queues to the point that we could much more intelligently handle this, - but unfortunately it looks like Android is only supporting OpenSL|ES 1.0.1 for now :( - */ - - /* Don't do anything if the device is not started. */ - if (ma_device_get_state(pDevice) != ma_device_state_started) { - return; - } - - /* Don't do anything if the device is being drained. */ - if (pDevice->opensl.isDrainingCapture) { - return; - } - - periodSizeInBytes = pDevice->capture.internalPeriodSizeInFrames * ma_get_bytes_per_frame(pDevice->capture.internalFormat, pDevice->capture.internalChannels); - pBuffer = pDevice->opensl.pBufferCapture + (pDevice->opensl.currentBufferIndexCapture * periodSizeInBytes); - - ma_device_handle_backend_data_callback(pDevice, NULL, pBuffer, pDevice->capture.internalPeriodSizeInFrames); - - resultSL = MA_OPENSL_BUFFERQUEUE(pDevice->opensl.pBufferQueueCapture)->Enqueue((SLAndroidSimpleBufferQueueItf)pDevice->opensl.pBufferQueueCapture, pBuffer, periodSizeInBytes); - if (resultSL != SL_RESULT_SUCCESS) { - return; - } - - pDevice->opensl.currentBufferIndexCapture = (pDevice->opensl.currentBufferIndexCapture + 1) % pDevice->capture.internalPeriods; -} - -static void ma_buffer_queue_callback_playback__opensl_android(SLAndroidSimpleBufferQueueItf pBufferQueue, void* pUserData) -{ - ma_device* pDevice = (ma_device*)pUserData; - size_t periodSizeInBytes; - ma_uint8* pBuffer; - SLresult resultSL; - - MA_ASSERT(pDevice != NULL); - - (void)pBufferQueue; - - /* Don't do anything if the device is not started. */ - if (ma_device_get_state(pDevice) != ma_device_state_started) { - return; - } - - /* Don't do anything if the device is being drained. */ - if (pDevice->opensl.isDrainingPlayback) { - return; - } - - periodSizeInBytes = pDevice->playback.internalPeriodSizeInFrames * ma_get_bytes_per_frame(pDevice->playback.internalFormat, pDevice->playback.internalChannels); - pBuffer = pDevice->opensl.pBufferPlayback + (pDevice->opensl.currentBufferIndexPlayback * periodSizeInBytes); - - ma_device_handle_backend_data_callback(pDevice, pBuffer, NULL, pDevice->playback.internalPeriodSizeInFrames); - - resultSL = MA_OPENSL_BUFFERQUEUE(pDevice->opensl.pBufferQueuePlayback)->Enqueue((SLAndroidSimpleBufferQueueItf)pDevice->opensl.pBufferQueuePlayback, pBuffer, periodSizeInBytes); - if (resultSL != SL_RESULT_SUCCESS) { - return; - } - - pDevice->opensl.currentBufferIndexPlayback = (pDevice->opensl.currentBufferIndexPlayback + 1) % pDevice->playback.internalPeriods; -} -#endif - -static ma_result ma_device_uninit__opensl(ma_device* pDevice) -{ - MA_ASSERT(pDevice != NULL); - - MA_ASSERT(g_maOpenSLInitCounter > 0); /* <-- If you trigger this it means you've either not initialized the context, or you've uninitialized it before uninitializing the device. */ - if (g_maOpenSLInitCounter == 0) { - return MA_INVALID_OPERATION; - } - - if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) { - if (pDevice->opensl.pAudioRecorderObj) { - MA_OPENSL_OBJ(pDevice->opensl.pAudioRecorderObj)->Destroy((SLObjectItf)pDevice->opensl.pAudioRecorderObj); - } - - ma_free(pDevice->opensl.pBufferCapture, &pDevice->pContext->allocationCallbacks); - } - - if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) { - if (pDevice->opensl.pAudioPlayerObj) { - MA_OPENSL_OBJ(pDevice->opensl.pAudioPlayerObj)->Destroy((SLObjectItf)pDevice->opensl.pAudioPlayerObj); - } - if (pDevice->opensl.pOutputMixObj) { - MA_OPENSL_OBJ(pDevice->opensl.pOutputMixObj)->Destroy((SLObjectItf)pDevice->opensl.pOutputMixObj); - } - - ma_free(pDevice->opensl.pBufferPlayback, &pDevice->pContext->allocationCallbacks); - } - - return MA_SUCCESS; -} - -#if defined(MA_ANDROID) && __ANDROID_API__ >= 21 -typedef SLAndroidDataFormat_PCM_EX ma_SLDataFormat_PCM; -#else -typedef SLDataFormat_PCM ma_SLDataFormat_PCM; -#endif - -static ma_result ma_SLDataFormat_PCM_init__opensl(ma_format format, ma_uint32 channels, ma_uint32 sampleRate, const ma_channel* channelMap, ma_SLDataFormat_PCM* pDataFormat) -{ - /* We need to convert our format/channels/rate so that they aren't set to default. */ - if (format == ma_format_unknown) { - format = MA_DEFAULT_FORMAT; - } - if (channels == 0) { - channels = MA_DEFAULT_CHANNELS; - } - if (sampleRate == 0) { - sampleRate = MA_DEFAULT_SAMPLE_RATE; - } - -#if defined(MA_ANDROID) && __ANDROID_API__ >= 21 - if (format == ma_format_f32) { - pDataFormat->formatType = SL_ANDROID_DATAFORMAT_PCM_EX; - pDataFormat->representation = SL_ANDROID_PCM_REPRESENTATION_FLOAT; - } else { - pDataFormat->formatType = SL_DATAFORMAT_PCM; - } -#else - pDataFormat->formatType = SL_DATAFORMAT_PCM; -#endif - - pDataFormat->numChannels = channels; - ((SLDataFormat_PCM*)pDataFormat)->samplesPerSec = ma_round_to_standard_sample_rate__opensl(sampleRate * 1000); /* In millihertz. Annoyingly, the sample rate variable is named differently between SLAndroidDataFormat_PCM_EX and SLDataFormat_PCM */ - pDataFormat->bitsPerSample = ma_get_bytes_per_sample(format) * 8; - pDataFormat->channelMask = ma_channel_map_to_channel_mask__opensl(channelMap, channels); - pDataFormat->endianness = (ma_is_little_endian()) ? SL_BYTEORDER_LITTLEENDIAN : SL_BYTEORDER_BIGENDIAN; - - /* - Android has a few restrictions on the format as documented here: https://developer.android.com/ndk/guides/audio/opensl-for-android.html - - Only mono and stereo is supported. - - Only u8 and s16 formats are supported. - - Maximum sample rate of 48000. - */ -#ifdef MA_ANDROID - if (pDataFormat->numChannels > 2) { - pDataFormat->numChannels = 2; - } -#if __ANDROID_API__ >= 21 - if (pDataFormat->formatType == SL_ANDROID_DATAFORMAT_PCM_EX) { - /* It's floating point. */ - MA_ASSERT(pDataFormat->representation == SL_ANDROID_PCM_REPRESENTATION_FLOAT); - if (pDataFormat->bitsPerSample > 32) { - pDataFormat->bitsPerSample = 32; - } - } else { - if (pDataFormat->bitsPerSample > 16) { - pDataFormat->bitsPerSample = 16; - } - } -#else - if (pDataFormat->bitsPerSample > 16) { - pDataFormat->bitsPerSample = 16; - } -#endif - if (((SLDataFormat_PCM*)pDataFormat)->samplesPerSec > SL_SAMPLINGRATE_48) { - ((SLDataFormat_PCM*)pDataFormat)->samplesPerSec = SL_SAMPLINGRATE_48; - } -#endif - - pDataFormat->containerSize = pDataFormat->bitsPerSample; /* Always tightly packed for now. */ - - return MA_SUCCESS; -} - -static ma_result ma_deconstruct_SLDataFormat_PCM__opensl(ma_SLDataFormat_PCM* pDataFormat, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate, ma_channel* pChannelMap, size_t channelMapCap) -{ - ma_bool32 isFloatingPoint = MA_FALSE; -#if defined(MA_ANDROID) && __ANDROID_API__ >= 21 - if (pDataFormat->formatType == SL_ANDROID_DATAFORMAT_PCM_EX) { - MA_ASSERT(pDataFormat->representation == SL_ANDROID_PCM_REPRESENTATION_FLOAT); - isFloatingPoint = MA_TRUE; - } -#endif - if (isFloatingPoint) { - if (pDataFormat->bitsPerSample == 32) { - *pFormat = ma_format_f32; - } - } else { - if (pDataFormat->bitsPerSample == 8) { - *pFormat = ma_format_u8; - } else if (pDataFormat->bitsPerSample == 16) { - *pFormat = ma_format_s16; - } else if (pDataFormat->bitsPerSample == 24) { - *pFormat = ma_format_s24; - } else if (pDataFormat->bitsPerSample == 32) { - *pFormat = ma_format_s32; - } - } - - *pChannels = pDataFormat->numChannels; - *pSampleRate = ((SLDataFormat_PCM*)pDataFormat)->samplesPerSec / 1000; - ma_channel_mask_to_channel_map__opensl(pDataFormat->channelMask, ma_min(pDataFormat->numChannels, channelMapCap), pChannelMap); - - return MA_SUCCESS; -} - -static ma_result ma_device_init__opensl(ma_device* pDevice, const ma_device_config* pConfig, ma_device_descriptor* pDescriptorPlayback, ma_device_descriptor* pDescriptorCapture) -{ -#ifdef MA_ANDROID - SLDataLocator_AndroidSimpleBufferQueue queue; - SLresult resultSL; - size_t bufferSizeInBytes; - SLInterfaceID itfIDs[2]; - const SLboolean itfIDsRequired[] = { - SL_BOOLEAN_TRUE, /* SL_IID_ANDROIDSIMPLEBUFFERQUEUE */ - SL_BOOLEAN_FALSE /* SL_IID_ANDROIDCONFIGURATION */ - }; -#endif - - MA_ASSERT(g_maOpenSLInitCounter > 0); /* <-- If you trigger this it means you've either not initialized the context, or you've uninitialized it and then attempted to initialize a new device. */ - if (g_maOpenSLInitCounter == 0) { - return MA_INVALID_OPERATION; - } - - if (pConfig->deviceType == ma_device_type_loopback) { - return MA_DEVICE_TYPE_NOT_SUPPORTED; - } - - /* - For now, only supporting Android implementations of OpenSL|ES since that's the only one I've - been able to test with and I currently depend on Android-specific extensions (simple buffer - queues). - */ -#ifdef MA_ANDROID - itfIDs[0] = (SLInterfaceID)pDevice->pContext->opensl.SL_IID_ANDROIDSIMPLEBUFFERQUEUE; - itfIDs[1] = (SLInterfaceID)pDevice->pContext->opensl.SL_IID_ANDROIDCONFIGURATION; - - /* No exclusive mode with OpenSL|ES. */ - if (((pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) && pDescriptorPlayback->shareMode == ma_share_mode_exclusive) || - ((pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) && pDescriptorCapture->shareMode == ma_share_mode_exclusive)) { - return MA_SHARE_MODE_NOT_SUPPORTED; - } - - /* Now we can start initializing the device properly. */ - MA_ASSERT(pDevice != NULL); - MA_ZERO_OBJECT(&pDevice->opensl); - - queue.locatorType = SL_DATALOCATOR_ANDROIDSIMPLEBUFFERQUEUE; - - if (pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) { - ma_SLDataFormat_PCM pcm; - SLDataLocator_IODevice locatorDevice; - SLDataSource source; - SLDataSink sink; - SLAndroidConfigurationItf pRecorderConfig; - - ma_SLDataFormat_PCM_init__opensl(pDescriptorCapture->format, pDescriptorCapture->channels, pDescriptorCapture->sampleRate, pDescriptorCapture->channelMap, &pcm); - - locatorDevice.locatorType = SL_DATALOCATOR_IODEVICE; - locatorDevice.deviceType = SL_IODEVICE_AUDIOINPUT; - locatorDevice.deviceID = SL_DEFAULTDEVICEID_AUDIOINPUT; /* Must always use the default device with Android. */ - locatorDevice.device = NULL; - - source.pLocator = &locatorDevice; - source.pFormat = NULL; - - queue.numBuffers = pDescriptorCapture->periodCount; - - sink.pLocator = &queue; - sink.pFormat = (SLDataFormat_PCM*)&pcm; - - resultSL = (*g_maEngineSL)->CreateAudioRecorder(g_maEngineSL, (SLObjectItf*)&pDevice->opensl.pAudioRecorderObj, &source, &sink, ma_countof(itfIDs), itfIDs, itfIDsRequired); - if (resultSL == SL_RESULT_CONTENT_UNSUPPORTED || resultSL == SL_RESULT_PARAMETER_INVALID) { - /* Unsupported format. Fall back to something safer and try again. If this fails, just abort. */ - pcm.formatType = SL_DATAFORMAT_PCM; - pcm.numChannels = 1; - ((SLDataFormat_PCM*)&pcm)->samplesPerSec = SL_SAMPLINGRATE_16; /* The name of the sample rate variable is different between SLAndroidDataFormat_PCM_EX and SLDataFormat_PCM. */ - pcm.bitsPerSample = 16; - pcm.containerSize = pcm.bitsPerSample; /* Always tightly packed for now. */ - pcm.channelMask = 0; - resultSL = (*g_maEngineSL)->CreateAudioRecorder(g_maEngineSL, (SLObjectItf*)&pDevice->opensl.pAudioRecorderObj, &source, &sink, ma_countof(itfIDs), itfIDs, itfIDsRequired); - } - - if (resultSL != SL_RESULT_SUCCESS) { - ma_device_uninit__opensl(pDevice); - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[OpenSL] Failed to create audio recorder."); - return ma_result_from_OpenSL(resultSL); - } - - - /* Set the recording preset before realizing the player. */ - if (pConfig->opensl.recordingPreset != ma_opensl_recording_preset_default) { - resultSL = MA_OPENSL_OBJ(pDevice->opensl.pAudioRecorderObj)->GetInterface((SLObjectItf)pDevice->opensl.pAudioRecorderObj, (SLInterfaceID)pDevice->pContext->opensl.SL_IID_ANDROIDCONFIGURATION, &pRecorderConfig); - if (resultSL == SL_RESULT_SUCCESS) { - SLint32 recordingPreset = ma_to_recording_preset__opensl(pConfig->opensl.recordingPreset); - resultSL = (*pRecorderConfig)->SetConfiguration(pRecorderConfig, SL_ANDROID_KEY_RECORDING_PRESET, &recordingPreset, sizeof(SLint32)); - if (resultSL != SL_RESULT_SUCCESS) { - /* Failed to set the configuration. Just keep going. */ - } - } - } - - resultSL = MA_OPENSL_OBJ(pDevice->opensl.pAudioRecorderObj)->Realize((SLObjectItf)pDevice->opensl.pAudioRecorderObj, SL_BOOLEAN_FALSE); - if (resultSL != SL_RESULT_SUCCESS) { - ma_device_uninit__opensl(pDevice); - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[OpenSL] Failed to realize audio recorder."); - return ma_result_from_OpenSL(resultSL); - } - - resultSL = MA_OPENSL_OBJ(pDevice->opensl.pAudioRecorderObj)->GetInterface((SLObjectItf)pDevice->opensl.pAudioRecorderObj, (SLInterfaceID)pDevice->pContext->opensl.SL_IID_RECORD, &pDevice->opensl.pAudioRecorder); - if (resultSL != SL_RESULT_SUCCESS) { - ma_device_uninit__opensl(pDevice); - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[OpenSL] Failed to retrieve SL_IID_RECORD interface."); - return ma_result_from_OpenSL(resultSL); - } - - resultSL = MA_OPENSL_OBJ(pDevice->opensl.pAudioRecorderObj)->GetInterface((SLObjectItf)pDevice->opensl.pAudioRecorderObj, (SLInterfaceID)pDevice->pContext->opensl.SL_IID_ANDROIDSIMPLEBUFFERQUEUE, &pDevice->opensl.pBufferQueueCapture); - if (resultSL != SL_RESULT_SUCCESS) { - ma_device_uninit__opensl(pDevice); - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[OpenSL] Failed to retrieve SL_IID_ANDROIDSIMPLEBUFFERQUEUE interface."); - return ma_result_from_OpenSL(resultSL); - } - - resultSL = MA_OPENSL_BUFFERQUEUE(pDevice->opensl.pBufferQueueCapture)->RegisterCallback((SLAndroidSimpleBufferQueueItf)pDevice->opensl.pBufferQueueCapture, ma_buffer_queue_callback_capture__opensl_android, pDevice); - if (resultSL != SL_RESULT_SUCCESS) { - ma_device_uninit__opensl(pDevice); - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[OpenSL] Failed to register buffer queue callback."); - return ma_result_from_OpenSL(resultSL); - } - - /* The internal format is determined by the "pcm" object. */ - ma_deconstruct_SLDataFormat_PCM__opensl(&pcm, &pDescriptorCapture->format, &pDescriptorCapture->channels, &pDescriptorCapture->sampleRate, pDescriptorCapture->channelMap, ma_countof(pDescriptorCapture->channelMap)); - - /* Buffer. */ - pDescriptorCapture->periodSizeInFrames = ma_calculate_buffer_size_in_frames_from_descriptor(pDescriptorCapture, pDescriptorCapture->sampleRate, pConfig->performanceProfile); - pDevice->opensl.currentBufferIndexCapture = 0; - - bufferSizeInBytes = pDescriptorCapture->periodSizeInFrames * ma_get_bytes_per_frame(pDescriptorCapture->format, pDescriptorCapture->channels) * pDescriptorCapture->periodCount; - pDevice->opensl.pBufferCapture = (ma_uint8*)ma_calloc(bufferSizeInBytes, &pDevice->pContext->allocationCallbacks); - if (pDevice->opensl.pBufferCapture == NULL) { - ma_device_uninit__opensl(pDevice); - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[OpenSL] Failed to allocate memory for data buffer."); - return MA_OUT_OF_MEMORY; - } - MA_ZERO_MEMORY(pDevice->opensl.pBufferCapture, bufferSizeInBytes); - } - - if (pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) { - ma_SLDataFormat_PCM pcm; - SLDataSource source; - SLDataLocator_OutputMix outmixLocator; - SLDataSink sink; - SLAndroidConfigurationItf pPlayerConfig; - - ma_SLDataFormat_PCM_init__opensl(pDescriptorPlayback->format, pDescriptorPlayback->channels, pDescriptorPlayback->sampleRate, pDescriptorPlayback->channelMap, &pcm); - - resultSL = (*g_maEngineSL)->CreateOutputMix(g_maEngineSL, (SLObjectItf*)&pDevice->opensl.pOutputMixObj, 0, NULL, NULL); - if (resultSL != SL_RESULT_SUCCESS) { - ma_device_uninit__opensl(pDevice); - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[OpenSL] Failed to create output mix."); - return ma_result_from_OpenSL(resultSL); - } - - resultSL = MA_OPENSL_OBJ(pDevice->opensl.pOutputMixObj)->Realize((SLObjectItf)pDevice->opensl.pOutputMixObj, SL_BOOLEAN_FALSE); - if (resultSL != SL_RESULT_SUCCESS) { - ma_device_uninit__opensl(pDevice); - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[OpenSL] Failed to realize output mix object."); - return ma_result_from_OpenSL(resultSL); - } - - resultSL = MA_OPENSL_OBJ(pDevice->opensl.pOutputMixObj)->GetInterface((SLObjectItf)pDevice->opensl.pOutputMixObj, (SLInterfaceID)pDevice->pContext->opensl.SL_IID_OUTPUTMIX, &pDevice->opensl.pOutputMix); - if (resultSL != SL_RESULT_SUCCESS) { - ma_device_uninit__opensl(pDevice); - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[OpenSL] Failed to retrieve SL_IID_OUTPUTMIX interface."); - return ma_result_from_OpenSL(resultSL); - } - - /* Set the output device. */ - if (pDescriptorPlayback->pDeviceID != NULL) { - SLuint32 deviceID_OpenSL = pDescriptorPlayback->pDeviceID->opensl; - MA_OPENSL_OUTPUTMIX(pDevice->opensl.pOutputMix)->ReRoute((SLOutputMixItf)pDevice->opensl.pOutputMix, 1, &deviceID_OpenSL); - } - - queue.numBuffers = pDescriptorPlayback->periodCount; - - source.pLocator = &queue; - source.pFormat = (SLDataFormat_PCM*)&pcm; - - outmixLocator.locatorType = SL_DATALOCATOR_OUTPUTMIX; - outmixLocator.outputMix = (SLObjectItf)pDevice->opensl.pOutputMixObj; - - sink.pLocator = &outmixLocator; - sink.pFormat = NULL; - - resultSL = (*g_maEngineSL)->CreateAudioPlayer(g_maEngineSL, (SLObjectItf*)&pDevice->opensl.pAudioPlayerObj, &source, &sink, ma_countof(itfIDs), itfIDs, itfIDsRequired); - if (resultSL == SL_RESULT_CONTENT_UNSUPPORTED || resultSL == SL_RESULT_PARAMETER_INVALID) { - /* Unsupported format. Fall back to something safer and try again. If this fails, just abort. */ - pcm.formatType = SL_DATAFORMAT_PCM; - pcm.numChannels = 2; - ((SLDataFormat_PCM*)&pcm)->samplesPerSec = SL_SAMPLINGRATE_16; - pcm.bitsPerSample = 16; - pcm.containerSize = pcm.bitsPerSample; /* Always tightly packed for now. */ - pcm.channelMask = SL_SPEAKER_FRONT_LEFT | SL_SPEAKER_FRONT_RIGHT; - resultSL = (*g_maEngineSL)->CreateAudioPlayer(g_maEngineSL, (SLObjectItf*)&pDevice->opensl.pAudioPlayerObj, &source, &sink, ma_countof(itfIDs), itfIDs, itfIDsRequired); - } - - if (resultSL != SL_RESULT_SUCCESS) { - ma_device_uninit__opensl(pDevice); - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[OpenSL] Failed to create audio player."); - return ma_result_from_OpenSL(resultSL); - } - - - /* Set the stream type before realizing the player. */ - if (pConfig->opensl.streamType != ma_opensl_stream_type_default) { - resultSL = MA_OPENSL_OBJ(pDevice->opensl.pAudioPlayerObj)->GetInterface((SLObjectItf)pDevice->opensl.pAudioPlayerObj, (SLInterfaceID)pDevice->pContext->opensl.SL_IID_ANDROIDCONFIGURATION, &pPlayerConfig); - if (resultSL == SL_RESULT_SUCCESS) { - SLint32 streamType = ma_to_stream_type__opensl(pConfig->opensl.streamType); - resultSL = (*pPlayerConfig)->SetConfiguration(pPlayerConfig, SL_ANDROID_KEY_STREAM_TYPE, &streamType, sizeof(SLint32)); - if (resultSL != SL_RESULT_SUCCESS) { - /* Failed to set the configuration. Just keep going. */ - } - } - } - - resultSL = MA_OPENSL_OBJ(pDevice->opensl.pAudioPlayerObj)->Realize((SLObjectItf)pDevice->opensl.pAudioPlayerObj, SL_BOOLEAN_FALSE); - if (resultSL != SL_RESULT_SUCCESS) { - ma_device_uninit__opensl(pDevice); - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[OpenSL] Failed to realize audio player."); - return ma_result_from_OpenSL(resultSL); - } - - resultSL = MA_OPENSL_OBJ(pDevice->opensl.pAudioPlayerObj)->GetInterface((SLObjectItf)pDevice->opensl.pAudioPlayerObj, (SLInterfaceID)pDevice->pContext->opensl.SL_IID_PLAY, &pDevice->opensl.pAudioPlayer); - if (resultSL != SL_RESULT_SUCCESS) { - ma_device_uninit__opensl(pDevice); - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[OpenSL] Failed to retrieve SL_IID_PLAY interface."); - return ma_result_from_OpenSL(resultSL); - } - - resultSL = MA_OPENSL_OBJ(pDevice->opensl.pAudioPlayerObj)->GetInterface((SLObjectItf)pDevice->opensl.pAudioPlayerObj, (SLInterfaceID)pDevice->pContext->opensl.SL_IID_ANDROIDSIMPLEBUFFERQUEUE, &pDevice->opensl.pBufferQueuePlayback); - if (resultSL != SL_RESULT_SUCCESS) { - ma_device_uninit__opensl(pDevice); - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[OpenSL] Failed to retrieve SL_IID_ANDROIDSIMPLEBUFFERQUEUE interface."); - return ma_result_from_OpenSL(resultSL); - } - - resultSL = MA_OPENSL_BUFFERQUEUE(pDevice->opensl.pBufferQueuePlayback)->RegisterCallback((SLAndroidSimpleBufferQueueItf)pDevice->opensl.pBufferQueuePlayback, ma_buffer_queue_callback_playback__opensl_android, pDevice); - if (resultSL != SL_RESULT_SUCCESS) { - ma_device_uninit__opensl(pDevice); - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[OpenSL] Failed to register buffer queue callback."); - return ma_result_from_OpenSL(resultSL); - } - - /* The internal format is determined by the "pcm" object. */ - ma_deconstruct_SLDataFormat_PCM__opensl(&pcm, &pDescriptorPlayback->format, &pDescriptorPlayback->channels, &pDescriptorPlayback->sampleRate, pDescriptorPlayback->channelMap, ma_countof(pDescriptorPlayback->channelMap)); - - /* Buffer. */ - pDescriptorPlayback->periodSizeInFrames = ma_calculate_buffer_size_in_frames_from_descriptor(pDescriptorPlayback, pDescriptorPlayback->sampleRate, pConfig->performanceProfile); - pDevice->opensl.currentBufferIndexPlayback = 0; - - bufferSizeInBytes = pDescriptorPlayback->periodSizeInFrames * ma_get_bytes_per_frame(pDescriptorPlayback->format, pDescriptorPlayback->channels) * pDescriptorPlayback->periodCount; - pDevice->opensl.pBufferPlayback = (ma_uint8*)ma_calloc(bufferSizeInBytes, &pDevice->pContext->allocationCallbacks); - if (pDevice->opensl.pBufferPlayback == NULL) { - ma_device_uninit__opensl(pDevice); - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[OpenSL] Failed to allocate memory for data buffer."); - return MA_OUT_OF_MEMORY; - } - MA_ZERO_MEMORY(pDevice->opensl.pBufferPlayback, bufferSizeInBytes); - } - - return MA_SUCCESS; -#else - return MA_NO_BACKEND; /* Non-Android implementations are not supported. */ -#endif -} - -static ma_result ma_device_start__opensl(ma_device* pDevice) -{ - SLresult resultSL; - size_t periodSizeInBytes; - ma_uint32 iPeriod; - - MA_ASSERT(pDevice != NULL); - - MA_ASSERT(g_maOpenSLInitCounter > 0); /* <-- If you trigger this it means you've either not initialized the context, or you've uninitialized it and then attempted to start the device. */ - if (g_maOpenSLInitCounter == 0) { - return MA_INVALID_OPERATION; - } - - if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) { - resultSL = MA_OPENSL_RECORD(pDevice->opensl.pAudioRecorder)->SetRecordState((SLRecordItf)pDevice->opensl.pAudioRecorder, SL_RECORDSTATE_RECORDING); - if (resultSL != SL_RESULT_SUCCESS) { - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[OpenSL] Failed to start internal capture device."); - return ma_result_from_OpenSL(resultSL); - } - - periodSizeInBytes = pDevice->capture.internalPeriodSizeInFrames * ma_get_bytes_per_frame(pDevice->capture.internalFormat, pDevice->capture.internalChannels); - for (iPeriod = 0; iPeriod < pDevice->capture.internalPeriods; ++iPeriod) { - resultSL = MA_OPENSL_BUFFERQUEUE(pDevice->opensl.pBufferQueueCapture)->Enqueue((SLAndroidSimpleBufferQueueItf)pDevice->opensl.pBufferQueueCapture, pDevice->opensl.pBufferCapture + (periodSizeInBytes * iPeriod), periodSizeInBytes); - if (resultSL != SL_RESULT_SUCCESS) { - MA_OPENSL_RECORD(pDevice->opensl.pAudioRecorder)->SetRecordState((SLRecordItf)pDevice->opensl.pAudioRecorder, SL_RECORDSTATE_STOPPED); - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[OpenSL] Failed to enqueue buffer for capture device."); - return ma_result_from_OpenSL(resultSL); - } - } - } - - if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) { - resultSL = MA_OPENSL_PLAY(pDevice->opensl.pAudioPlayer)->SetPlayState((SLPlayItf)pDevice->opensl.pAudioPlayer, SL_PLAYSTATE_PLAYING); - if (resultSL != SL_RESULT_SUCCESS) { - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[OpenSL] Failed to start internal playback device."); - return ma_result_from_OpenSL(resultSL); - } - - /* In playback mode (no duplex) we need to load some initial buffers. In duplex mode we need to enqueue silent buffers. */ - if (pDevice->type == ma_device_type_duplex) { - MA_ZERO_MEMORY(pDevice->opensl.pBufferPlayback, pDevice->playback.internalPeriodSizeInFrames * pDevice->playback.internalPeriods * ma_get_bytes_per_frame(pDevice->playback.internalFormat, pDevice->playback.internalChannels)); - } else { - ma_device__read_frames_from_client(pDevice, pDevice->playback.internalPeriodSizeInFrames * pDevice->playback.internalPeriods, pDevice->opensl.pBufferPlayback); - } - - periodSizeInBytes = pDevice->playback.internalPeriodSizeInFrames * ma_get_bytes_per_frame(pDevice->playback.internalFormat, pDevice->playback.internalChannels); - for (iPeriod = 0; iPeriod < pDevice->playback.internalPeriods; ++iPeriod) { - resultSL = MA_OPENSL_BUFFERQUEUE(pDevice->opensl.pBufferQueuePlayback)->Enqueue((SLAndroidSimpleBufferQueueItf)pDevice->opensl.pBufferQueuePlayback, pDevice->opensl.pBufferPlayback + (periodSizeInBytes * iPeriod), periodSizeInBytes); - if (resultSL != SL_RESULT_SUCCESS) { - MA_OPENSL_PLAY(pDevice->opensl.pAudioPlayer)->SetPlayState((SLPlayItf)pDevice->opensl.pAudioPlayer, SL_PLAYSTATE_STOPPED); - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[OpenSL] Failed to enqueue buffer for playback device."); - return ma_result_from_OpenSL(resultSL); - } - } - } - - return MA_SUCCESS; -} - -static ma_result ma_device_drain__opensl(ma_device* pDevice, ma_device_type deviceType) -{ - SLAndroidSimpleBufferQueueItf pBufferQueue; - - MA_ASSERT(deviceType == ma_device_type_capture || deviceType == ma_device_type_playback); - - if (pDevice->type == ma_device_type_capture) { - pBufferQueue = (SLAndroidSimpleBufferQueueItf)pDevice->opensl.pBufferQueueCapture; - pDevice->opensl.isDrainingCapture = MA_TRUE; - } else { - pBufferQueue = (SLAndroidSimpleBufferQueueItf)pDevice->opensl.pBufferQueuePlayback; - pDevice->opensl.isDrainingPlayback = MA_TRUE; - } - - for (;;) { - SLAndroidSimpleBufferQueueState state; - - MA_OPENSL_BUFFERQUEUE(pBufferQueue)->GetState(pBufferQueue, &state); - if (state.count == 0) { - break; - } - - ma_sleep(10); - } - - if (pDevice->type == ma_device_type_capture) { - pDevice->opensl.isDrainingCapture = MA_FALSE; - } else { - pDevice->opensl.isDrainingPlayback = MA_FALSE; - } - - return MA_SUCCESS; -} - -static ma_result ma_device_stop__opensl(ma_device* pDevice) -{ - SLresult resultSL; - - MA_ASSERT(pDevice != NULL); - - MA_ASSERT(g_maOpenSLInitCounter > 0); /* <-- If you trigger this it means you've either not initialized the context, or you've uninitialized it before stopping/uninitializing the device. */ - if (g_maOpenSLInitCounter == 0) { - return MA_INVALID_OPERATION; - } - - if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) { - ma_device_drain__opensl(pDevice, ma_device_type_capture); - - resultSL = MA_OPENSL_RECORD(pDevice->opensl.pAudioRecorder)->SetRecordState((SLRecordItf)pDevice->opensl.pAudioRecorder, SL_RECORDSTATE_STOPPED); - if (resultSL != SL_RESULT_SUCCESS) { - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[OpenSL] Failed to stop internal capture device."); - return ma_result_from_OpenSL(resultSL); - } - - MA_OPENSL_BUFFERQUEUE(pDevice->opensl.pBufferQueueCapture)->Clear((SLAndroidSimpleBufferQueueItf)pDevice->opensl.pBufferQueueCapture); - } - - if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) { - ma_device_drain__opensl(pDevice, ma_device_type_playback); - - resultSL = MA_OPENSL_PLAY(pDevice->opensl.pAudioPlayer)->SetPlayState((SLPlayItf)pDevice->opensl.pAudioPlayer, SL_PLAYSTATE_STOPPED); - if (resultSL != SL_RESULT_SUCCESS) { - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[OpenSL] Failed to stop internal playback device."); - return ma_result_from_OpenSL(resultSL); - } - - MA_OPENSL_BUFFERQUEUE(pDevice->opensl.pBufferQueuePlayback)->Clear((SLAndroidSimpleBufferQueueItf)pDevice->opensl.pBufferQueuePlayback); - } - - /* Make sure the client is aware that the device has stopped. There may be an OpenSL|ES callback for this, but I haven't found it. */ - ma_device__on_notification_stopped(pDevice); - - return MA_SUCCESS; -} - - -static ma_result ma_context_uninit__opensl(ma_context* pContext) -{ - MA_ASSERT(pContext != NULL); - MA_ASSERT(pContext->backend == ma_backend_opensl); - (void)pContext; - - /* Uninit global data. */ - ma_spinlock_lock(&g_maOpenSLSpinlock); - { - MA_ASSERT(g_maOpenSLInitCounter > 0); /* If you've triggered this, it means you have ma_context_init/uninit mismatch. Each successful call to ma_context_init() must be matched up with a call to ma_context_uninit(). */ - - g_maOpenSLInitCounter -= 1; - if (g_maOpenSLInitCounter == 0) { - (*g_maEngineObjectSL)->Destroy(g_maEngineObjectSL); - } - } - ma_spinlock_unlock(&g_maOpenSLSpinlock); - - return MA_SUCCESS; -} - -static ma_result ma_dlsym_SLInterfaceID__opensl(ma_context* pContext, const char* pName, ma_handle* pHandle) -{ - /* We need to return an error if the symbol cannot be found. This is important because there have been reports that some symbols do not exist. */ - ma_handle* p = (ma_handle*)ma_dlsym(ma_context_get_log(pContext), pContext->opensl.libOpenSLES, pName); - if (p == NULL) { - ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_INFO, "[OpenSL] Cannot find symbol %s", pName); - return MA_NO_BACKEND; - } - - *pHandle = *p; - return MA_SUCCESS; -} - -static ma_result ma_context_init_engine_nolock__opensl(ma_context* pContext) -{ - g_maOpenSLInitCounter += 1; - if (g_maOpenSLInitCounter == 1) { - SLresult resultSL; - - resultSL = ((ma_slCreateEngine_proc)pContext->opensl.slCreateEngine)(&g_maEngineObjectSL, 0, NULL, 0, NULL, NULL); - if (resultSL != SL_RESULT_SUCCESS) { - g_maOpenSLInitCounter -= 1; - return ma_result_from_OpenSL(resultSL); - } - - (*g_maEngineObjectSL)->Realize(g_maEngineObjectSL, SL_BOOLEAN_FALSE); - - resultSL = (*g_maEngineObjectSL)->GetInterface(g_maEngineObjectSL, (SLInterfaceID)pContext->opensl.SL_IID_ENGINE, &g_maEngineSL); - if (resultSL != SL_RESULT_SUCCESS) { - (*g_maEngineObjectSL)->Destroy(g_maEngineObjectSL); - g_maOpenSLInitCounter -= 1; - return ma_result_from_OpenSL(resultSL); - } - } - - return MA_SUCCESS; -} - -static ma_result ma_context_init__opensl(ma_context* pContext, const ma_context_config* pConfig, ma_backend_callbacks* pCallbacks) -{ - ma_result result; - -#if !defined(MA_NO_RUNTIME_LINKING) - size_t i; - const char* libOpenSLESNames[] = { - "libOpenSLES.so" - }; -#endif - - MA_ASSERT(pContext != NULL); - - (void)pConfig; - -#if !defined(MA_NO_RUNTIME_LINKING) - /* - Dynamically link against libOpenSLES.so. I have now had multiple reports that SL_IID_ANDROIDSIMPLEBUFFERQUEUE cannot be found. One - report was happening at compile time and another at runtime. To try working around this, I'm going to link to libOpenSLES at runtime - and extract the symbols rather than reference them directly. This should, hopefully, fix these issues as the compiler won't see any - references to the symbols and will hopefully skip the checks. - */ - for (i = 0; i < ma_countof(libOpenSLESNames); i += 1) { - pContext->opensl.libOpenSLES = ma_dlopen(ma_context_get_log(pContext), libOpenSLESNames[i]); - if (pContext->opensl.libOpenSLES != NULL) { - break; - } - } - - if (pContext->opensl.libOpenSLES == NULL) { - ma_log_post(ma_context_get_log(pContext), MA_LOG_LEVEL_INFO, "[OpenSL] Could not find libOpenSLES.so"); - return MA_NO_BACKEND; - } - - result = ma_dlsym_SLInterfaceID__opensl(pContext, "SL_IID_ENGINE", &pContext->opensl.SL_IID_ENGINE); - if (result != MA_SUCCESS) { - ma_dlclose(ma_context_get_log(pContext), pContext->opensl.libOpenSLES); - return result; - } - - result = ma_dlsym_SLInterfaceID__opensl(pContext, "SL_IID_AUDIOIODEVICECAPABILITIES", &pContext->opensl.SL_IID_AUDIOIODEVICECAPABILITIES); - if (result != MA_SUCCESS) { - ma_dlclose(ma_context_get_log(pContext), pContext->opensl.libOpenSLES); - return result; - } - - result = ma_dlsym_SLInterfaceID__opensl(pContext, "SL_IID_ANDROIDSIMPLEBUFFERQUEUE", &pContext->opensl.SL_IID_ANDROIDSIMPLEBUFFERQUEUE); - if (result != MA_SUCCESS) { - ma_dlclose(ma_context_get_log(pContext), pContext->opensl.libOpenSLES); - return result; - } - - result = ma_dlsym_SLInterfaceID__opensl(pContext, "SL_IID_RECORD", &pContext->opensl.SL_IID_RECORD); - if (result != MA_SUCCESS) { - ma_dlclose(ma_context_get_log(pContext), pContext->opensl.libOpenSLES); - return result; - } - - result = ma_dlsym_SLInterfaceID__opensl(pContext, "SL_IID_PLAY", &pContext->opensl.SL_IID_PLAY); - if (result != MA_SUCCESS) { - ma_dlclose(ma_context_get_log(pContext), pContext->opensl.libOpenSLES); - return result; - } - - result = ma_dlsym_SLInterfaceID__opensl(pContext, "SL_IID_OUTPUTMIX", &pContext->opensl.SL_IID_OUTPUTMIX); - if (result != MA_SUCCESS) { - ma_dlclose(ma_context_get_log(pContext), pContext->opensl.libOpenSLES); - return result; - } - - result = ma_dlsym_SLInterfaceID__opensl(pContext, "SL_IID_ANDROIDCONFIGURATION", &pContext->opensl.SL_IID_ANDROIDCONFIGURATION); - if (result != MA_SUCCESS) { - ma_dlclose(ma_context_get_log(pContext), pContext->opensl.libOpenSLES); - return result; - } - - pContext->opensl.slCreateEngine = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->opensl.libOpenSLES, "slCreateEngine"); - if (pContext->opensl.slCreateEngine == NULL) { - ma_dlclose(ma_context_get_log(pContext), pContext->opensl.libOpenSLES); - ma_log_post(ma_context_get_log(pContext), MA_LOG_LEVEL_INFO, "[OpenSL] Cannot find symbol slCreateEngine."); - return MA_NO_BACKEND; - } -#else - pContext->opensl.SL_IID_ENGINE = (ma_handle)SL_IID_ENGINE; - pContext->opensl.SL_IID_AUDIOIODEVICECAPABILITIES = (ma_handle)SL_IID_AUDIOIODEVICECAPABILITIES; - pContext->opensl.SL_IID_ANDROIDSIMPLEBUFFERQUEUE = (ma_handle)SL_IID_ANDROIDSIMPLEBUFFERQUEUE; - pContext->opensl.SL_IID_RECORD = (ma_handle)SL_IID_RECORD; - pContext->opensl.SL_IID_PLAY = (ma_handle)SL_IID_PLAY; - pContext->opensl.SL_IID_OUTPUTMIX = (ma_handle)SL_IID_OUTPUTMIX; - pContext->opensl.SL_IID_ANDROIDCONFIGURATION = (ma_handle)SL_IID_ANDROIDCONFIGURATION; - pContext->opensl.slCreateEngine = (ma_proc)slCreateEngine; -#endif - - - /* Initialize global data first if applicable. */ - ma_spinlock_lock(&g_maOpenSLSpinlock); - { - result = ma_context_init_engine_nolock__opensl(pContext); - } - ma_spinlock_unlock(&g_maOpenSLSpinlock); - - if (result != MA_SUCCESS) { - ma_dlclose(ma_context_get_log(pContext), pContext->opensl.libOpenSLES); - ma_log_post(ma_context_get_log(pContext), MA_LOG_LEVEL_INFO, "[OpenSL] Failed to initialize OpenSL engine."); - return result; - } - - pCallbacks->onContextInit = ma_context_init__opensl; - pCallbacks->onContextUninit = ma_context_uninit__opensl; - pCallbacks->onContextEnumerateDevices = ma_context_enumerate_devices__opensl; - pCallbacks->onContextGetDeviceInfo = ma_context_get_device_info__opensl; - pCallbacks->onDeviceInit = ma_device_init__opensl; - pCallbacks->onDeviceUninit = ma_device_uninit__opensl; - pCallbacks->onDeviceStart = ma_device_start__opensl; - pCallbacks->onDeviceStop = ma_device_stop__opensl; - pCallbacks->onDeviceRead = NULL; /* Not needed because OpenSL|ES is asynchronous. */ - pCallbacks->onDeviceWrite = NULL; /* Not needed because OpenSL|ES is asynchronous. */ - pCallbacks->onDeviceDataLoop = NULL; /* Not needed because OpenSL|ES is asynchronous. */ - - return MA_SUCCESS; -} -#endif /* OpenSL|ES */ - - -/****************************************************************************** - -Web Audio Backend - -******************************************************************************/ -#ifdef MA_HAS_WEBAUDIO -#include - -#if (__EMSCRIPTEN_major__ > 3) || (__EMSCRIPTEN_major__ == 3 && (__EMSCRIPTEN_minor__ > 1 || (__EMSCRIPTEN_minor__ == 1 && __EMSCRIPTEN_tiny__ >= 32))) - #include - #define MA_SUPPORT_AUDIO_WORKLETS - - #if (__EMSCRIPTEN_major__ > 3) || (__EMSCRIPTEN_major__ == 3 && (__EMSCRIPTEN_minor__ > 1 || (__EMSCRIPTEN_minor__ == 1 && __EMSCRIPTEN_tiny__ >= 70))) - #define MA_SUPPORT_AUDIO_WORKLETS_VARIABLE_BUFFER_SIZE - #endif -#endif - -/* -TODO: Version 0.12: Swap this logic around so that AudioWorklets are used by default. Add MA_NO_AUDIO_WORKLETS. -*/ -#if defined(MA_ENABLE_AUDIO_WORKLETS) && defined(MA_SUPPORT_AUDIO_WORKLETS) - #define MA_USE_AUDIO_WORKLETS -#endif - -/* The thread stack size must be a multiple of 16. */ -#ifndef MA_AUDIO_WORKLETS_THREAD_STACK_SIZE -#define MA_AUDIO_WORKLETS_THREAD_STACK_SIZE 131072 -#endif - -#if defined(MA_USE_AUDIO_WORKLETS) -#define MA_WEBAUDIO_LATENCY_HINT_BALANCED "balanced" -#define MA_WEBAUDIO_LATENCY_HINT_INTERACTIVE "interactive" -#define MA_WEBAUDIO_LATENCY_HINT_PLAYBACK "playback" -#endif - -static ma_bool32 ma_is_capture_supported__webaudio() -{ - return EM_ASM_INT({ - return (navigator.mediaDevices !== undefined && navigator.mediaDevices.getUserMedia !== undefined); - }, 0) != 0; /* Must pass in a dummy argument for C99 compatibility. */ -} - -#ifdef __cplusplus -extern "C" { -#endif -void* EMSCRIPTEN_KEEPALIVE ma_malloc_emscripten(size_t sz, const ma_allocation_callbacks* pAllocationCallbacks) -{ - return ma_malloc(sz, pAllocationCallbacks); -} - -void EMSCRIPTEN_KEEPALIVE ma_free_emscripten(void* p, const ma_allocation_callbacks* pAllocationCallbacks) -{ - ma_free(p, pAllocationCallbacks); -} - -void EMSCRIPTEN_KEEPALIVE ma_device_process_pcm_frames_capture__webaudio(ma_device* pDevice, int frameCount, float* pFrames) -{ - ma_device_handle_backend_data_callback(pDevice, NULL, pFrames, (ma_uint32)frameCount); -} - -void EMSCRIPTEN_KEEPALIVE ma_device_process_pcm_frames_playback__webaudio(ma_device* pDevice, int frameCount, float* pFrames) -{ - ma_device_handle_backend_data_callback(pDevice, pFrames, NULL, (ma_uint32)frameCount); -} -#ifdef __cplusplus -} -#endif - -static ma_result ma_context_enumerate_devices__webaudio(ma_context* pContext, ma_enum_devices_callback_proc callback, void* pUserData) -{ - ma_bool32 cbResult = MA_TRUE; - - MA_ASSERT(pContext != NULL); - MA_ASSERT(callback != NULL); - - /* Only supporting default devices for now. */ - - /* Playback. */ - if (cbResult) { - ma_device_info deviceInfo; - MA_ZERO_OBJECT(&deviceInfo); - ma_strncpy_s(deviceInfo.name, sizeof(deviceInfo.name), MA_DEFAULT_PLAYBACK_DEVICE_NAME, (size_t)-1); - deviceInfo.isDefault = MA_TRUE; /* Only supporting default devices. */ - cbResult = callback(pContext, ma_device_type_playback, &deviceInfo, pUserData); - } - - /* Capture. */ - if (cbResult) { - if (ma_is_capture_supported__webaudio()) { - ma_device_info deviceInfo; - MA_ZERO_OBJECT(&deviceInfo); - ma_strncpy_s(deviceInfo.name, sizeof(deviceInfo.name), MA_DEFAULT_CAPTURE_DEVICE_NAME, (size_t)-1); - deviceInfo.isDefault = MA_TRUE; /* Only supporting default devices. */ - cbResult = callback(pContext, ma_device_type_capture, &deviceInfo, pUserData); - } - } - - return MA_SUCCESS; -} - -static ma_result ma_context_get_device_info__webaudio(ma_context* pContext, ma_device_type deviceType, const ma_device_id* pDeviceID, ma_device_info* pDeviceInfo) -{ - MA_ASSERT(pContext != NULL); - - if (deviceType == ma_device_type_capture && !ma_is_capture_supported__webaudio()) { - return MA_NO_DEVICE; - } - - MA_ZERO_MEMORY(pDeviceInfo->id.webaudio, sizeof(pDeviceInfo->id.webaudio)); - - /* Only supporting default devices for now. */ - (void)pDeviceID; - if (deviceType == ma_device_type_playback) { - ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), MA_DEFAULT_PLAYBACK_DEVICE_NAME, (size_t)-1); - } else { - ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), MA_DEFAULT_CAPTURE_DEVICE_NAME, (size_t)-1); - } - - /* Only supporting default devices. */ - pDeviceInfo->isDefault = MA_TRUE; - - /* Web Audio can support any number of channels and sample rates. It only supports f32 formats, however. */ - pDeviceInfo->nativeDataFormats[0].flags = 0; - pDeviceInfo->nativeDataFormats[0].format = ma_format_unknown; - pDeviceInfo->nativeDataFormats[0].channels = 0; /* All channels are supported. */ - pDeviceInfo->nativeDataFormats[0].sampleRate = EM_ASM_INT({ - try { - var temp = new (window.AudioContext || window.webkitAudioContext)(); - var sampleRate = temp.sampleRate; - temp.close(); - return sampleRate; - } catch(e) { - return 0; - } - }, 0); /* Must pass in a dummy argument for C99 compatibility. */ - - if (pDeviceInfo->nativeDataFormats[0].sampleRate == 0) { - return MA_NO_DEVICE; - } - - pDeviceInfo->nativeDataFormatCount = 1; - - return MA_SUCCESS; -} - -static ma_result ma_device_uninit__webaudio(ma_device* pDevice) -{ - MA_ASSERT(pDevice != NULL); - - #if defined(MA_USE_AUDIO_WORKLETS) - { - EM_ASM({ - var device = window.miniaudio.get_device_by_index($0); - - if (device.streamNode !== undefined) { - device.streamNode.disconnect(); - device.streamNode = undefined; - } - - device.pDevice = undefined; - }, pDevice->webaudio.deviceIndex); - - emscripten_destroy_web_audio_node(pDevice->webaudio.audioWorklet); - emscripten_destroy_audio_context(pDevice->webaudio.audioContext); - ma_free(pDevice->webaudio.pStackBuffer, &pDevice->pContext->allocationCallbacks); - } - #else - { - EM_ASM({ - var device = window.miniaudio.get_device_by_index($0); - - /* Make sure all nodes are disconnected and marked for collection. */ - if (device.scriptNode !== undefined) { - device.scriptNode.onaudioprocess = function(e) {}; /* We want to reset the callback to ensure it doesn't get called after AudioContext.close() has returned. Shouldn't happen since we're disconnecting, but just to be safe... */ - device.scriptNode.disconnect(); - device.scriptNode = undefined; - } - - if (device.streamNode !== undefined) { - device.streamNode.disconnect(); - device.streamNode = undefined; - } - - /* - Stop the device. I think there is a chance the callback could get fired after calling this, hence why we want - to clear the callback before closing. - */ - device.webaudio.close(); - device.webaudio = undefined; - device.pDevice = undefined; - }, pDevice->webaudio.deviceIndex); - } - #endif - - /* Clean up the device on the JS side. */ - EM_ASM({ - window.miniaudio.untrack_device_by_index($0); - }, pDevice->webaudio.deviceIndex); - - ma_free(pDevice->webaudio.pIntermediaryBuffer, &pDevice->pContext->allocationCallbacks); - - return MA_SUCCESS; -} - -#if !defined(MA_USE_AUDIO_WORKLETS) -static ma_uint32 ma_calculate_period_size_in_frames_from_descriptor__webaudio(const ma_device_descriptor* pDescriptor, ma_uint32 nativeSampleRate, ma_performance_profile performanceProfile) -{ - /* - There have been reports of the default buffer size being too small on some browsers. If we're using - the default buffer size, we'll make sure the period size is bigger than our standard defaults. - */ - ma_uint32 periodSizeInFrames; - - if (nativeSampleRate == 0) { - nativeSampleRate = MA_DEFAULT_SAMPLE_RATE; - } - - if (pDescriptor->periodSizeInFrames == 0) { - if (pDescriptor->periodSizeInMilliseconds == 0) { - if (performanceProfile == ma_performance_profile_low_latency) { - periodSizeInFrames = ma_calculate_buffer_size_in_frames_from_milliseconds(33, nativeSampleRate); /* 1 frame @ 30 FPS */ - } else { - periodSizeInFrames = ma_calculate_buffer_size_in_frames_from_milliseconds(333, nativeSampleRate); - } - } else { - periodSizeInFrames = ma_calculate_buffer_size_in_frames_from_milliseconds(pDescriptor->periodSizeInMilliseconds, nativeSampleRate); - } - } else { - periodSizeInFrames = pDescriptor->periodSizeInFrames; - } - - /* The size of the buffer must be a power of 2 and between 256 and 16384. */ - if (periodSizeInFrames < 256) { - periodSizeInFrames = 256; - } else if (periodSizeInFrames > 16384) { - periodSizeInFrames = 16384; - } else { - periodSizeInFrames = ma_next_power_of_2(periodSizeInFrames); - } - - return periodSizeInFrames; -} -#endif - - -#if defined(MA_USE_AUDIO_WORKLETS) -typedef struct -{ - ma_device* pDevice; - const ma_device_config* pConfig; - ma_device_descriptor* pDescriptorPlayback; - ma_device_descriptor* pDescriptorCapture; -} ma_audio_worklet_thread_initialized_data; - -static EM_BOOL ma_audio_worklet_process_callback__webaudio(int inputCount, const AudioSampleFrame* pInputs, int outputCount, AudioSampleFrame* pOutputs, int paramCount, const AudioParamFrame* pParams, void* pUserData) -{ - ma_device* pDevice = (ma_device*)pUserData; - ma_uint32 frameCount; - - (void)paramCount; - (void)pParams; - - /* - The Emscripten documentation says that it'll always be 128 frames being passed in. Hard coding it like that feels - like a very bad idea to me. Even if it's hard coded in the backend, the API and documentation should always refer - to variables instead of a hard coded number. In any case, will follow along for the time being. - - Unfortunately the audio data is not interleaved so we'll need to convert it before we give the data to miniaudio - for further processing. - */ - if (pDevice->type == ma_device_type_playback) { - frameCount = pDevice->playback.internalPeriodSizeInFrames; - } else { - frameCount = pDevice->capture.internalPeriodSizeInFrames; - } - - if (ma_device_get_state(pDevice) != ma_device_state_started) { - /* Fill the output buffer with zero to avoid a noise sound */ - for (int i = 0; i < outputCount; i += 1) { - MA_ZERO_MEMORY(pOutputs[i].data, pOutputs[i].numberOfChannels * frameCount * sizeof(float)); - } - - return EM_TRUE; - } - - if (inputCount > 0) { - /* Input data needs to be interleaved before we hand it to the client. */ - for (ma_uint32 iChannel = 0; iChannel < pDevice->capture.internalChannels; iChannel += 1) { - for (ma_uint32 iFrame = 0; iFrame < frameCount; iFrame += 1) { - pDevice->webaudio.pIntermediaryBuffer[iFrame*pDevice->capture.internalChannels + iChannel] = pInputs[0].data[frameCount*iChannel + iFrame]; - } - } - - ma_device_process_pcm_frames_capture__webaudio(pDevice, frameCount, pDevice->webaudio.pIntermediaryBuffer); - } - - if (outputCount > 0) { - /* If it's a capture-only device, we'll need to output silence. */ - if (pDevice->type == ma_device_type_capture) { - MA_ZERO_MEMORY(pOutputs[0].data, frameCount * pDevice->playback.internalChannels * sizeof(float)); - } else { - ma_device_process_pcm_frames_playback__webaudio(pDevice, frameCount, pDevice->webaudio.pIntermediaryBuffer); - - /* We've read the data from the client. Now we need to deinterleave the buffer and output to the output buffer. */ - for (ma_uint32 iChannel = 0; iChannel < pDevice->playback.internalChannels; iChannel += 1) { - for (ma_uint32 iFrame = 0; iFrame < frameCount; iFrame += 1) { - pOutputs[0].data[frameCount*iChannel + iFrame] = pDevice->webaudio.pIntermediaryBuffer[iFrame*pDevice->playback.internalChannels + iChannel]; - } - } - } - } - - return EM_TRUE; -} - - -static void ma_audio_worklet_processor_created__webaudio(EMSCRIPTEN_WEBAUDIO_T audioContext, EM_BOOL success, void* pUserData) -{ - ma_audio_worklet_thread_initialized_data* pParameters = (ma_audio_worklet_thread_initialized_data*)pUserData; - EmscriptenAudioWorkletNodeCreateOptions audioWorkletOptions; - int channels = 0; - size_t intermediaryBufferSizeInFrames; - int sampleRate; - - if (success == EM_FALSE) { - pParameters->pDevice->webaudio.initResult = MA_ERROR; - ma_free(pParameters, &pParameters->pDevice->pContext->allocationCallbacks); - return; - } - - /* The next step is to initialize the audio worklet node. */ - MA_ZERO_OBJECT(&audioWorkletOptions); - - /* - The way channel counts work with Web Audio is confusing. As far as I can tell, there's no way to know the channel - count from MediaStreamAudioSourceNode (what we use for capture)? The only way to have control is to configure an - output channel count on the capture side. This is slightly confusing for capture mode because intuitively you - wouldn't actually connect an output to an input-only node, but this is what we'll have to do in order to have - proper control over the channel count. In the capture case, we'll have to output silence to its output node. - */ - if (pParameters->pConfig->deviceType == ma_device_type_capture) { - channels = (int)((pParameters->pDescriptorCapture->channels > 0) ? pParameters->pDescriptorCapture->channels : MA_DEFAULT_CHANNELS); - audioWorkletOptions.numberOfInputs = 1; - } else { - channels = (int)((pParameters->pDescriptorPlayback->channels > 0) ? pParameters->pDescriptorPlayback->channels : MA_DEFAULT_CHANNELS); - - if (pParameters->pConfig->deviceType == ma_device_type_duplex) { - audioWorkletOptions.numberOfInputs = 1; - } else { - audioWorkletOptions.numberOfInputs = 0; - } - } - - audioWorkletOptions.numberOfOutputs = 1; - audioWorkletOptions.outputChannelCounts = &channels; - - - /* - Now that we know the channel count to use we can allocate the intermediary buffer. The - intermediary buffer is used for interleaving and deinterleaving. - */ - #if defined(MA_SUPPORT_AUDIO_WORKLETS_VARIABLE_BUFFER_SIZE) - { - intermediaryBufferSizeInFrames = (size_t)emscripten_audio_context_quantum_size(audioContext); - } - #else - { - intermediaryBufferSizeInFrames = 128; - } - #endif - - pParameters->pDevice->webaudio.pIntermediaryBuffer = (float*)ma_malloc(intermediaryBufferSizeInFrames * (ma_uint32)channels * sizeof(float), &pParameters->pDevice->pContext->allocationCallbacks); - if (pParameters->pDevice->webaudio.pIntermediaryBuffer == NULL) { - pParameters->pDevice->webaudio.initResult = MA_OUT_OF_MEMORY; - ma_free(pParameters, &pParameters->pDevice->pContext->allocationCallbacks); - return; - } - - pParameters->pDevice->webaudio.audioWorklet = emscripten_create_wasm_audio_worklet_node(audioContext, "miniaudio", &audioWorkletOptions, &ma_audio_worklet_process_callback__webaudio, pParameters->pDevice); - - /* With the audio worklet initialized we can now attach it to the graph. */ - if (pParameters->pConfig->deviceType == ma_device_type_capture || pParameters->pConfig->deviceType == ma_device_type_duplex) { - ma_result attachmentResult = (ma_result)EM_ASM_INT({ - var getUserMediaResult = 0; - var audioWorklet = emscriptenGetAudioObject($0); - var audioContext = emscriptenGetAudioObject($1); - - navigator.mediaDevices.getUserMedia({audio:true, video:false}) - .then(function(stream) { - audioContext.streamNode = audioContext.createMediaStreamSource(stream); - audioContext.streamNode.connect(audioWorklet); - audioWorklet.connect(audioContext.destination); - getUserMediaResult = 0; /* 0 = MA_SUCCESS */ - }) - .catch(function(error) { - console.log("navigator.mediaDevices.getUserMedia Failed: " + error); - getUserMediaResult = -1; /* -1 = MA_ERROR */ - }); - - return getUserMediaResult; - }, pParameters->pDevice->webaudio.audioWorklet, audioContext); - - if (attachmentResult != MA_SUCCESS) { - ma_log_postf(ma_device_get_log(pParameters->pDevice), MA_LOG_LEVEL_ERROR, "Web Audio: Failed to connect capture node."); - emscripten_destroy_web_audio_node(pParameters->pDevice->webaudio.audioWorklet); - pParameters->pDevice->webaudio.initResult = attachmentResult; - ma_free(pParameters, &pParameters->pDevice->pContext->allocationCallbacks); - return; - } - } - - /* If it's playback only we can now attach the worklet node to the graph. This has already been done for the duplex case. */ - if (pParameters->pConfig->deviceType == ma_device_type_playback) { - ma_result attachmentResult = (ma_result)EM_ASM_INT({ - var audioWorklet = emscriptenGetAudioObject($0); - var audioContext = emscriptenGetAudioObject($1); - audioWorklet.connect(audioContext.destination); - return 0; /* 0 = MA_SUCCESS */ - }, pParameters->pDevice->webaudio.audioWorklet, audioContext); - - if (attachmentResult != MA_SUCCESS) { - ma_log_postf(ma_device_get_log(pParameters->pDevice), MA_LOG_LEVEL_ERROR, "Web Audio: Failed to connect playback node."); - pParameters->pDevice->webaudio.initResult = attachmentResult; - ma_free(pParameters, &pParameters->pDevice->pContext->allocationCallbacks); - return; - } - } - - /* We need to update the descriptors so that they reflect the internal data format. Both capture and playback should be the same. */ - sampleRate = EM_ASM_INT({ return emscriptenGetAudioObject($0).sampleRate; }, audioContext); - - if (pParameters->pDescriptorCapture != NULL) { - pParameters->pDescriptorCapture->format = ma_format_f32; - pParameters->pDescriptorCapture->channels = (ma_uint32)channels; - pParameters->pDescriptorCapture->sampleRate = (ma_uint32)sampleRate; - ma_channel_map_init_standard(ma_standard_channel_map_webaudio, pParameters->pDescriptorCapture->channelMap, ma_countof(pParameters->pDescriptorCapture->channelMap), pParameters->pDescriptorCapture->channels); - pParameters->pDescriptorCapture->periodSizeInFrames = intermediaryBufferSizeInFrames; - pParameters->pDescriptorCapture->periodCount = 1; - } - - if (pParameters->pDescriptorPlayback != NULL) { - pParameters->pDescriptorPlayback->format = ma_format_f32; - pParameters->pDescriptorPlayback->channels = (ma_uint32)channels; - pParameters->pDescriptorPlayback->sampleRate = (ma_uint32)sampleRate; - ma_channel_map_init_standard(ma_standard_channel_map_webaudio, pParameters->pDescriptorPlayback->channelMap, ma_countof(pParameters->pDescriptorPlayback->channelMap), pParameters->pDescriptorPlayback->channels); - pParameters->pDescriptorPlayback->periodSizeInFrames = intermediaryBufferSizeInFrames; - pParameters->pDescriptorPlayback->periodCount = 1; - } - - /* At this point we're done and we can return. */ - ma_log_postf(ma_device_get_log(pParameters->pDevice), MA_LOG_LEVEL_DEBUG, "AudioWorklets: Created worklet node: %d\n", pParameters->pDevice->webaudio.audioWorklet); - pParameters->pDevice->webaudio.initResult = MA_SUCCESS; - ma_free(pParameters, &pParameters->pDevice->pContext->allocationCallbacks); -} - -static void ma_audio_worklet_thread_initialized__webaudio(EMSCRIPTEN_WEBAUDIO_T audioContext, EM_BOOL success, void* pUserData) -{ - ma_audio_worklet_thread_initialized_data* pParameters = (ma_audio_worklet_thread_initialized_data*)pUserData; - WebAudioWorkletProcessorCreateOptions workletProcessorOptions; - - MA_ASSERT(pParameters != NULL); - - if (success == EM_FALSE) { - pParameters->pDevice->webaudio.initResult = MA_ERROR; - return; - } - - MA_ZERO_OBJECT(&workletProcessorOptions); - workletProcessorOptions.name = "miniaudio"; /* I'm not entirely sure what to call this. Does this need to be globally unique, or does it need only be unique for a given AudioContext? */ - - emscripten_create_wasm_audio_worklet_processor_async(audioContext, &workletProcessorOptions, ma_audio_worklet_processor_created__webaudio, pParameters); -} -#endif - -static ma_result ma_device_init__webaudio(ma_device* pDevice, const ma_device_config* pConfig, ma_device_descriptor* pDescriptorPlayback, ma_device_descriptor* pDescriptorCapture) -{ - if (pConfig->deviceType == ma_device_type_loopback) { - return MA_DEVICE_TYPE_NOT_SUPPORTED; - } - - /* No exclusive mode with Web Audio. */ - if (((pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) && pDescriptorPlayback->shareMode == ma_share_mode_exclusive) || - ((pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) && pDescriptorCapture->shareMode == ma_share_mode_exclusive)) { - return MA_SHARE_MODE_NOT_SUPPORTED; - } - - /* - With AudioWorklets we'll have just a single AudioContext. I'm not sure why I'm not doing this for ScriptProcessorNode so - it might be worthwhile to look into that as well. - */ - #if defined(MA_USE_AUDIO_WORKLETS) - { - EmscriptenWebAudioCreateAttributes audioContextAttributes; - ma_audio_worklet_thread_initialized_data* pInitParameters; - void* pStackBuffer; - - if (pConfig->performanceProfile == ma_performance_profile_conservative) { - audioContextAttributes.latencyHint = MA_WEBAUDIO_LATENCY_HINT_PLAYBACK; - } else { - audioContextAttributes.latencyHint = MA_WEBAUDIO_LATENCY_HINT_INTERACTIVE; - } - - /* - In my testing, Firefox does not seem to capture audio data properly if the sample rate is set - to anything other than 48K. This does not seem to be the case for other browsers. For this reason, - if the device type is anything other than playback, we'll leave the sample rate as-is and let the - browser pick the appropriate rate for us. - */ - if (pConfig->deviceType == ma_device_type_playback) { - audioContextAttributes.sampleRate = pDescriptorPlayback->sampleRate; - } else { - audioContextAttributes.sampleRate = 0; - } - - /* It's not clear if this can return an error. None of the tests in the Emscripten repository check for this, so neither am I for now. */ - pDevice->webaudio.audioContext = emscripten_create_audio_context(&audioContextAttributes); - - /* - With the context created we can now create the worklet. We can only have a single worklet per audio - context which means we'll need to craft this appropriately to handle duplex devices correctly. - */ - - /* - We now need to create a worker thread. This is a bit weird because we need to allocate our - own buffer for the thread's stack. The stack needs to be aligned to 16 bytes. I'm going to - allocate this on the heap to keep it simple. - */ - pStackBuffer = ma_aligned_malloc(MA_AUDIO_WORKLETS_THREAD_STACK_SIZE, 16, &pDevice->pContext->allocationCallbacks); - if (pStackBuffer == NULL) { - emscripten_destroy_audio_context(pDevice->webaudio.audioContext); - return MA_OUT_OF_MEMORY; - } - - /* Our thread initialization parameters need to be allocated on the heap so they don't go out of scope. */ - pInitParameters = (ma_audio_worklet_thread_initialized_data*)ma_malloc(sizeof(*pInitParameters), &pDevice->pContext->allocationCallbacks); - if (pInitParameters == NULL) { - ma_free(pStackBuffer, &pDevice->pContext->allocationCallbacks); - emscripten_destroy_audio_context(pDevice->webaudio.audioContext); - return MA_OUT_OF_MEMORY; - } - - pInitParameters->pDevice = pDevice; - pInitParameters->pConfig = pConfig; - pInitParameters->pDescriptorPlayback = pDescriptorPlayback; - pInitParameters->pDescriptorCapture = pDescriptorCapture; - - /* - We need to flag the device as not yet initialized so we can wait on it later. Unfortunately all of - the Emscripten WebAudio stuff is asynchronous. - */ - pDevice->webaudio.initResult = MA_BUSY; - { - emscripten_start_wasm_audio_worklet_thread_async(pDevice->webaudio.audioContext, pStackBuffer, MA_AUDIO_WORKLETS_THREAD_STACK_SIZE, ma_audio_worklet_thread_initialized__webaudio, pInitParameters); - } - while (pDevice->webaudio.initResult == MA_BUSY) { emscripten_sleep(1); } /* We must wait for initialization to complete. We're just spinning here. The emscripten_sleep() call is why we need to build with `-sASYNCIFY`. */ - - /* Initialization is now complete. Descriptors were updated when the worklet was initialized. */ - if (pDevice->webaudio.initResult != MA_SUCCESS) { - ma_free(pStackBuffer, &pDevice->pContext->allocationCallbacks); - emscripten_destroy_audio_context(pDevice->webaudio.audioContext); - return pDevice->webaudio.initResult; - } - - /* We need to add an entry to the miniaudio.devices list on the JS side so we can do some JS/C interop. */ - pDevice->webaudio.deviceIndex = EM_ASM_INT({ - return window.miniaudio.track_device({ - webaudio: emscriptenGetAudioObject($0), - state: 1, /* 1 = ma_device_state_stopped */ - pDevice: $1 - }); - }, pDevice->webaudio.audioContext, pDevice); - - return MA_SUCCESS; - } - #else - { - /* ScriptProcessorNode. This path requires us to do almost everything in JS, but we'll do as much as we can in C. */ - ma_uint32 deviceIndex; - ma_uint32 channels; - ma_uint32 sampleRate; - ma_uint32 periodSizeInFrames; - - /* The channel count will depend on the device type. If it's a capture, use its, otherwise use the playback side. */ - if (pConfig->deviceType == ma_device_type_capture) { - channels = (pDescriptorCapture->channels > 0) ? pDescriptorCapture->channels : MA_DEFAULT_CHANNELS; - } else { - channels = (pDescriptorPlayback->channels > 0) ? pDescriptorPlayback->channels : MA_DEFAULT_CHANNELS; - } - - /* - When testing in Firefox, I've seen it where capture mode fails if the sample rate is changed to anything other than it's - native rate. For this reason we're leaving the sample rate untouched for capture devices. - */ - if (pConfig->deviceType == ma_device_type_playback) { - sampleRate = pDescriptorPlayback->sampleRate; - } else { - sampleRate = 0; /* Let the browser decide when capturing. */ - } - - /* The period size needs to be a power of 2. */ - if (pConfig->deviceType == ma_device_type_capture) { - periodSizeInFrames = ma_calculate_period_size_in_frames_from_descriptor__webaudio(pDescriptorCapture, sampleRate, pConfig->performanceProfile); - } else { - periodSizeInFrames = ma_calculate_period_size_in_frames_from_descriptor__webaudio(pDescriptorPlayback, sampleRate, pConfig->performanceProfile); - } - - /* We need an intermediary buffer for doing interleaving and deinterleaving. */ - pDevice->webaudio.pIntermediaryBuffer = (float*)ma_malloc(periodSizeInFrames * channels * sizeof(float), &pDevice->pContext->allocationCallbacks); - if (pDevice->webaudio.pIntermediaryBuffer == NULL) { - return MA_OUT_OF_MEMORY; - } - - deviceIndex = EM_ASM_INT({ - var deviceType = $0; - var channels = $1; - var sampleRate = $2; - var bufferSize = $3; - var pIntermediaryBuffer = $4; - var pDevice = $5; - - if (typeof(window.miniaudio) === 'undefined') { - return -1; /* Context not initialized. */ - } - - var device = {}; - - /* First thing we need is an AudioContext. */ - var audioContextOptions = {}; - if (deviceType == window.miniaudio.device_type.playback && sampleRate != 0) { - audioContextOptions.sampleRate = sampleRate; - } - - device.webaudio = new (window.AudioContext || window.webkitAudioContext)(audioContextOptions); - device.webaudio.suspend(); /* The AudioContext must be created in a suspended state. */ - device.state = window.miniaudio.device_state.stopped; - - /* - We need to create a ScriptProcessorNode. The channel situation is the same as the AudioWorklet path in that we - need to specify an output and configure the channel count there. - */ - var channelCountIn = 0; - var channelCountOut = channels; - if (deviceType != window.miniaudio.device_type.playback) { - channelCountIn = channels; - } - - device.scriptNode = device.webaudio.createScriptProcessor(bufferSize, channelCountIn, channelCountOut); - - /* The node processing callback. */ - device.scriptNode.onaudioprocess = function(e) { - if (device.intermediaryBufferView == null || device.intermediaryBufferView.length == 0) { - device.intermediaryBufferView = new Float32Array(HEAPF32.buffer, pIntermediaryBuffer, bufferSize * channels); - } - - /* Do the capture side first. */ - if (deviceType == window.miniaudio.device_type.capture || deviceType == window.miniaudio.device_type.duplex) { - /* The data must be interleaved before being processed miniaudio. */ - for (var iChannel = 0; iChannel < channels; iChannel += 1) { - var inputBuffer = e.inputBuffer.getChannelData(iChannel); - var intermediaryBuffer = device.intermediaryBufferView; - - for (var iFrame = 0; iFrame < bufferSize; iFrame += 1) { - intermediaryBuffer[iFrame*channels + iChannel] = inputBuffer[iFrame]; - } - } - - _ma_device_process_pcm_frames_capture__webaudio(pDevice, bufferSize, pIntermediaryBuffer); - } - - if (deviceType == window.miniaudio.device_type.playback || deviceType == window.miniaudio.device_type.duplex) { - _ma_device_process_pcm_frames_playback__webaudio(pDevice, bufferSize, pIntermediaryBuffer); - - for (var iChannel = 0; iChannel < e.outputBuffer.numberOfChannels; ++iChannel) { - var outputBuffer = e.outputBuffer.getChannelData(iChannel); - var intermediaryBuffer = device.intermediaryBufferView; - - for (var iFrame = 0; iFrame < bufferSize; iFrame += 1) { - outputBuffer[iFrame] = intermediaryBuffer[iFrame*channels + iChannel]; - } - } - } else { - /* It's a capture-only device. Make sure the output is silenced. */ - for (var iChannel = 0; iChannel < e.outputBuffer.numberOfChannels; ++iChannel) { - e.outputBuffer.getChannelData(iChannel).fill(0.0); - } - } - }; - - /* Now we need to connect our node to the graph. */ - if (deviceType == window.miniaudio.device_type.capture || deviceType == window.miniaudio.device_type.duplex) { - navigator.mediaDevices.getUserMedia({audio:true, video:false}) - .then(function(stream) { - device.streamNode = device.webaudio.createMediaStreamSource(stream); - device.streamNode.connect(device.scriptNode); - device.scriptNode.connect(device.webaudio.destination); - }) - .catch(function(error) { - console.log("Failed to get user media: " + error); - }); - } - - if (deviceType == window.miniaudio.device_type.playback) { - device.scriptNode.connect(device.webaudio.destination); - } - - device.pDevice = pDevice; - - return window.miniaudio.track_device(device); - }, pConfig->deviceType, channels, sampleRate, periodSizeInFrames, pDevice->webaudio.pIntermediaryBuffer, pDevice); - - if (deviceIndex < 0) { - return MA_FAILED_TO_OPEN_BACKEND_DEVICE; - } - - pDevice->webaudio.deviceIndex = deviceIndex; - - /* Grab the sample rate from the audio context directly. */ - sampleRate = (ma_uint32)EM_ASM_INT({ return window.miniaudio.get_device_by_index($0).webaudio.sampleRate; }, deviceIndex); - - if (pDescriptorCapture != NULL) { - pDescriptorCapture->format = ma_format_f32; - pDescriptorCapture->channels = channels; - pDescriptorCapture->sampleRate = sampleRate; - ma_channel_map_init_standard(ma_standard_channel_map_webaudio, pDescriptorCapture->channelMap, ma_countof(pDescriptorCapture->channelMap), pDescriptorCapture->channels); - pDescriptorCapture->periodSizeInFrames = periodSizeInFrames; - pDescriptorCapture->periodCount = 1; - } - - if (pDescriptorPlayback != NULL) { - pDescriptorPlayback->format = ma_format_f32; - pDescriptorPlayback->channels = channels; - pDescriptorPlayback->sampleRate = sampleRate; - ma_channel_map_init_standard(ma_standard_channel_map_webaudio, pDescriptorPlayback->channelMap, ma_countof(pDescriptorPlayback->channelMap), pDescriptorPlayback->channels); - pDescriptorPlayback->periodSizeInFrames = periodSizeInFrames; - pDescriptorPlayback->periodCount = 1; - } - - return MA_SUCCESS; - } - #endif -} - -static ma_result ma_device_start__webaudio(ma_device* pDevice) -{ - MA_ASSERT(pDevice != NULL); - - EM_ASM({ - var device = window.miniaudio.get_device_by_index($0); - device.webaudio.resume(); - device.state = window.miniaudio.device_state.started; - }, pDevice->webaudio.deviceIndex); - - return MA_SUCCESS; -} - -static ma_result ma_device_stop__webaudio(ma_device* pDevice) -{ - MA_ASSERT(pDevice != NULL); - - /* - From the WebAudio API documentation for AudioContext.suspend(): - - Suspends the progression of AudioContext's currentTime, allows any current context processing blocks that are already processed to be played to the - destination, and then allows the system to release its claim on audio hardware. - - I read this to mean that "any current context processing blocks" are processed by suspend() - i.e. They they are drained. We therefore shouldn't need to - do any kind of explicit draining. - */ - EM_ASM({ - var device = window.miniaudio.get_device_by_index($0); - device.webaudio.suspend(); - device.state = window.miniaudio.device_state.stopped; - }, pDevice->webaudio.deviceIndex); - - ma_device__on_notification_stopped(pDevice); - - return MA_SUCCESS; -} - -static ma_result ma_context_uninit__webaudio(ma_context* pContext) -{ - MA_ASSERT(pContext != NULL); - MA_ASSERT(pContext->backend == ma_backend_webaudio); - - (void)pContext; /* Unused. */ - - /* Remove the global miniaudio object from window if there are no more references to it. */ - EM_ASM({ - if (typeof(window.miniaudio) !== 'undefined') { - miniaudio.unlock_event_types.map(function(event_type) { - document.removeEventListener(event_type, miniaudio.unlock, true); - }); - - window.miniaudio.referenceCount -= 1; - if (window.miniaudio.referenceCount === 0) { - delete window.miniaudio; - } - } - }); - - return MA_SUCCESS; -} - -static ma_result ma_context_init__webaudio(ma_context* pContext, const ma_context_config* pConfig, ma_backend_callbacks* pCallbacks) -{ - int resultFromJS; - - MA_ASSERT(pContext != NULL); - - (void)pConfig; /* Unused. */ - - /* Here is where our global JavaScript object is initialized. */ - resultFromJS = EM_ASM_INT({ - if (typeof window === 'undefined' || (window.AudioContext || window.webkitAudioContext) === undefined) { - return 0; /* Web Audio not supported. */ - } - - if (typeof(window.miniaudio) === 'undefined') { - window.miniaudio = { - referenceCount: 0 - }; - - /* Device types. */ - window.miniaudio.device_type = {}; - window.miniaudio.device_type.playback = $0; - window.miniaudio.device_type.capture = $1; - window.miniaudio.device_type.duplex = $2; - - /* Device states. */ - window.miniaudio.device_state = {}; - window.miniaudio.device_state.stopped = $3; - window.miniaudio.device_state.started = $4; - - /* Device cache for mapping devices to indexes for JavaScript/C interop. */ - let miniaudio = window.miniaudio; - miniaudio.devices = []; - - miniaudio.track_device = function(device) { - /* Try inserting into a free slot first. */ - for (var iDevice = 0; iDevice < miniaudio.devices.length; ++iDevice) { - if (miniaudio.devices[iDevice] == null) { - miniaudio.devices[iDevice] = device; - return iDevice; - } - } - - /* Getting here means there is no empty slots in the array so we just push to the end. */ - miniaudio.devices.push(device); - return miniaudio.devices.length - 1; - }; - - miniaudio.untrack_device_by_index = function(deviceIndex) { - /* We just set the device's slot to null. The slot will get reused in the next call to ma_track_device. */ - miniaudio.devices[deviceIndex] = null; - - /* Trim the array if possible. */ - while (miniaudio.devices.length > 0) { - if (miniaudio.devices[miniaudio.devices.length-1] == null) { - miniaudio.devices.pop(); - } else { - break; - } - } - }; - - miniaudio.untrack_device = function(device) { - for (var iDevice = 0; iDevice < miniaudio.devices.length; ++iDevice) { - if (miniaudio.devices[iDevice] == device) { - return miniaudio.untrack_device_by_index(iDevice); - } - } - }; - - miniaudio.get_device_by_index = function(deviceIndex) { - return miniaudio.devices[deviceIndex]; - }; - - miniaudio.unlock_event_types = (function(){ - return ['touchend', 'click']; - })(); - - miniaudio.unlock = function() { - for(var i = 0; i < miniaudio.devices.length; ++i) { - var device = miniaudio.devices[i]; - if (device != null && - device.webaudio != null && - device.state === miniaudio.device_state.started) { - - device.webaudio.resume().then(() => { - _ma_device__on_notification_unlocked(device.pDevice); - }, - (error) => {console.error("Failed to resume audiocontext", error); - }); - } - } - miniaudio.unlock_event_types.map(function(event_type) { - document.removeEventListener(event_type, miniaudio.unlock, true); - }); - }; - - miniaudio.unlock_event_types.map(function(event_type) { - document.addEventListener(event_type, miniaudio.unlock, true); - }); - } - - window.miniaudio.referenceCount += 1; - - return 1; - }, ma_device_type_playback, ma_device_type_capture, ma_device_type_duplex, ma_device_state_stopped, ma_device_state_started); - - if (resultFromJS != 1) { - return MA_FAILED_TO_INIT_BACKEND; - } - - pCallbacks->onContextInit = ma_context_init__webaudio; - pCallbacks->onContextUninit = ma_context_uninit__webaudio; - pCallbacks->onContextEnumerateDevices = ma_context_enumerate_devices__webaudio; - pCallbacks->onContextGetDeviceInfo = ma_context_get_device_info__webaudio; - pCallbacks->onDeviceInit = ma_device_init__webaudio; - pCallbacks->onDeviceUninit = ma_device_uninit__webaudio; - pCallbacks->onDeviceStart = ma_device_start__webaudio; - pCallbacks->onDeviceStop = ma_device_stop__webaudio; - pCallbacks->onDeviceRead = NULL; /* Not needed because WebAudio is asynchronous. */ - pCallbacks->onDeviceWrite = NULL; /* Not needed because WebAudio is asynchronous. */ - pCallbacks->onDeviceDataLoop = NULL; /* Not needed because WebAudio is asynchronous. */ - - return MA_SUCCESS; -} -#endif /* MA_HAS_WEBAUDIO */ - - - -static ma_bool32 ma__is_channel_map_valid(const ma_channel* pChannelMap, ma_uint32 channels) -{ - /* A blank channel map should be allowed, in which case it should use an appropriate default which will depend on context. */ - if (pChannelMap != NULL && pChannelMap[0] != MA_CHANNEL_NONE) { - ma_uint32 iChannel; - - if (channels == 0 || channels > MA_MAX_CHANNELS) { - return MA_FALSE; /* Channel count out of range. */ - } - - /* A channel cannot be present in the channel map more than once. */ - for (iChannel = 0; iChannel < channels; ++iChannel) { - ma_uint32 jChannel; - for (jChannel = iChannel + 1; jChannel < channels; ++jChannel) { - if (pChannelMap[iChannel] == pChannelMap[jChannel]) { - return MA_FALSE; - } - } - } - } - - return MA_TRUE; -} - - -static ma_bool32 ma_context_is_backend_asynchronous(ma_context* pContext) -{ - MA_ASSERT(pContext != NULL); - - if (pContext->callbacks.onDeviceRead == NULL && pContext->callbacks.onDeviceWrite == NULL) { - if (pContext->callbacks.onDeviceDataLoop == NULL) { - return MA_TRUE; - } else { - return MA_FALSE; - } - } else { - return MA_FALSE; - } -} - - -static ma_result ma_device__post_init_setup(ma_device* pDevice, ma_device_type deviceType) -{ - ma_result result; - - MA_ASSERT(pDevice != NULL); - - if (deviceType == ma_device_type_capture || deviceType == ma_device_type_duplex || deviceType == ma_device_type_loopback) { - if (pDevice->capture.format == ma_format_unknown) { - pDevice->capture.format = pDevice->capture.internalFormat; - } - if (pDevice->capture.channels == 0) { - pDevice->capture.channels = pDevice->capture.internalChannels; - } - if (pDevice->capture.channelMap[0] == MA_CHANNEL_NONE) { - MA_ASSERT(pDevice->capture.channels <= MA_MAX_CHANNELS); - if (pDevice->capture.internalChannels == pDevice->capture.channels) { - ma_channel_map_copy(pDevice->capture.channelMap, pDevice->capture.internalChannelMap, pDevice->capture.channels); - } else { - if (pDevice->capture.channelMixMode == ma_channel_mix_mode_simple) { - ma_channel_map_init_blank(pDevice->capture.channelMap, pDevice->capture.channels); - } else { - ma_channel_map_init_standard(ma_standard_channel_map_default, pDevice->capture.channelMap, ma_countof(pDevice->capture.channelMap), pDevice->capture.channels); - } - } - } - } - - if (deviceType == ma_device_type_playback || deviceType == ma_device_type_duplex) { - if (pDevice->playback.format == ma_format_unknown) { - pDevice->playback.format = pDevice->playback.internalFormat; - } - if (pDevice->playback.channels == 0) { - pDevice->playback.channels = pDevice->playback.internalChannels; - } - if (pDevice->playback.channelMap[0] == MA_CHANNEL_NONE) { - MA_ASSERT(pDevice->playback.channels <= MA_MAX_CHANNELS); - if (pDevice->playback.internalChannels == pDevice->playback.channels) { - ma_channel_map_copy(pDevice->playback.channelMap, pDevice->playback.internalChannelMap, pDevice->playback.channels); - } else { - if (pDevice->playback.channelMixMode == ma_channel_mix_mode_simple) { - ma_channel_map_init_blank(pDevice->playback.channelMap, pDevice->playback.channels); - } else { - ma_channel_map_init_standard(ma_standard_channel_map_default, pDevice->playback.channelMap, ma_countof(pDevice->playback.channelMap), pDevice->playback.channels); - } - } - } - } - - if (pDevice->sampleRate == 0) { - if (deviceType == ma_device_type_capture || deviceType == ma_device_type_duplex || deviceType == ma_device_type_loopback) { - pDevice->sampleRate = pDevice->capture.internalSampleRate; - } else { - pDevice->sampleRate = pDevice->playback.internalSampleRate; - } - } - - /* Data converters. */ - if (deviceType == ma_device_type_capture || deviceType == ma_device_type_duplex || deviceType == ma_device_type_loopback) { - /* Converting from internal device format to client format. */ - ma_data_converter_config converterConfig = ma_data_converter_config_init_default(); - converterConfig.formatIn = pDevice->capture.internalFormat; - converterConfig.channelsIn = pDevice->capture.internalChannels; - converterConfig.sampleRateIn = pDevice->capture.internalSampleRate; - converterConfig.pChannelMapIn = pDevice->capture.internalChannelMap; - converterConfig.formatOut = pDevice->capture.format; - converterConfig.channelsOut = pDevice->capture.channels; - converterConfig.sampleRateOut = pDevice->sampleRate; - converterConfig.pChannelMapOut = pDevice->capture.channelMap; - converterConfig.channelMixMode = pDevice->capture.channelMixMode; - converterConfig.calculateLFEFromSpatialChannels = pDevice->capture.calculateLFEFromSpatialChannels; - converterConfig.allowDynamicSampleRate = MA_FALSE; - converterConfig.resampling.algorithm = pDevice->resampling.algorithm; - converterConfig.resampling.linear.lpfOrder = pDevice->resampling.linear.lpfOrder; - converterConfig.resampling.pBackendVTable = pDevice->resampling.pBackendVTable; - converterConfig.resampling.pBackendUserData = pDevice->resampling.pBackendUserData; - - /* Make sure the old converter is uninitialized first. */ - if (ma_device_get_state(pDevice) != ma_device_state_uninitialized) { - ma_data_converter_uninit(&pDevice->capture.converter, &pDevice->pContext->allocationCallbacks); - } - - result = ma_data_converter_init(&converterConfig, &pDevice->pContext->allocationCallbacks, &pDevice->capture.converter); - if (result != MA_SUCCESS) { - return result; - } - } - - if (deviceType == ma_device_type_playback || deviceType == ma_device_type_duplex) { - /* Converting from client format to device format. */ - ma_data_converter_config converterConfig = ma_data_converter_config_init_default(); - converterConfig.formatIn = pDevice->playback.format; - converterConfig.channelsIn = pDevice->playback.channels; - converterConfig.sampleRateIn = pDevice->sampleRate; - converterConfig.pChannelMapIn = pDevice->playback.channelMap; - converterConfig.formatOut = pDevice->playback.internalFormat; - converterConfig.channelsOut = pDevice->playback.internalChannels; - converterConfig.sampleRateOut = pDevice->playback.internalSampleRate; - converterConfig.pChannelMapOut = pDevice->playback.internalChannelMap; - converterConfig.channelMixMode = pDevice->playback.channelMixMode; - converterConfig.calculateLFEFromSpatialChannels = pDevice->playback.calculateLFEFromSpatialChannels; - converterConfig.allowDynamicSampleRate = MA_FALSE; - converterConfig.resampling.algorithm = pDevice->resampling.algorithm; - converterConfig.resampling.linear.lpfOrder = pDevice->resampling.linear.lpfOrder; - converterConfig.resampling.pBackendVTable = pDevice->resampling.pBackendVTable; - converterConfig.resampling.pBackendUserData = pDevice->resampling.pBackendUserData; - - /* Make sure the old converter is uninitialized first. */ - if (ma_device_get_state(pDevice) != ma_device_state_uninitialized) { - ma_data_converter_uninit(&pDevice->playback.converter, &pDevice->pContext->allocationCallbacks); - } - - result = ma_data_converter_init(&converterConfig, &pDevice->pContext->allocationCallbacks, &pDevice->playback.converter); - if (result != MA_SUCCESS) { - return result; - } - } - - - /* - If the device is doing playback (ma_device_type_playback or ma_device_type_duplex), there's - a couple of situations where we'll need a heap allocated cache. - - The first is a duplex device for backends that use a callback for data delivery. The reason - this is needed is that the input stage needs to have a buffer to place the input data while it - waits for the playback stage, after which the miniaudio data callback will get fired. This is - not needed for backends that use a blocking API because miniaudio manages temporary buffers on - the stack to achieve this. - - The other situation is when the data converter does not have the ability to query the number - of input frames that are required in order to process a given number of output frames. When - performing data conversion, it's useful if miniaudio know exactly how many frames it needs - from the client in order to generate a given number of output frames. This way, only exactly - the number of frames are needed to be read from the client which means no cache is necessary. - On the other hand, if miniaudio doesn't know how many frames to read, it is forced to read - in fixed sized chunks and then cache any residual unused input frames, those of which will be - processed at a later stage. - */ - if (deviceType == ma_device_type_playback || deviceType == ma_device_type_duplex) { - ma_uint64 unused; - - pDevice->playback.inputCacheConsumed = 0; - pDevice->playback.inputCacheRemaining = 0; - - if (pDevice->type == ma_device_type_duplex || /* Duplex. backend may decide to use ma_device_handle_backend_data_callback() which will require this cache. */ - ma_data_converter_get_required_input_frame_count(&pDevice->playback.converter, 1, &unused) != MA_SUCCESS) /* Data conversion required input frame calculation not supported. */ - { - /* We need a heap allocated cache. We want to size this based on the period size. */ - void* pNewInputCache; - ma_uint64 newInputCacheCap; - ma_uint64 newInputCacheSizeInBytes; - - newInputCacheCap = ma_calculate_frame_count_after_resampling(pDevice->playback.internalSampleRate, pDevice->sampleRate, pDevice->playback.internalPeriodSizeInFrames); - - newInputCacheSizeInBytes = newInputCacheCap * ma_get_bytes_per_frame(pDevice->playback.format, pDevice->playback.channels); - if (newInputCacheSizeInBytes > MA_SIZE_MAX) { - ma_free(pDevice->playback.pInputCache, &pDevice->pContext->allocationCallbacks); - pDevice->playback.pInputCache = NULL; - pDevice->playback.inputCacheCap = 0; - return MA_OUT_OF_MEMORY; /* Allocation too big. Should never hit this, but makes the cast below safer for 32-bit builds. */ - } - - pNewInputCache = ma_realloc(pDevice->playback.pInputCache, (size_t)newInputCacheSizeInBytes, &pDevice->pContext->allocationCallbacks); - if (pNewInputCache == NULL) { - ma_free(pDevice->playback.pInputCache, &pDevice->pContext->allocationCallbacks); - pDevice->playback.pInputCache = NULL; - pDevice->playback.inputCacheCap = 0; - return MA_OUT_OF_MEMORY; - } - - pDevice->playback.pInputCache = pNewInputCache; - pDevice->playback.inputCacheCap = newInputCacheCap; - } else { - /* Heap allocation not required. Make sure we clear out the old cache just in case this function was called in response to a route change. */ - ma_free(pDevice->playback.pInputCache, &pDevice->pContext->allocationCallbacks); - pDevice->playback.pInputCache = NULL; - pDevice->playback.inputCacheCap = 0; - } - } - - return MA_SUCCESS; -} - -MA_API ma_result ma_device_post_init(ma_device* pDevice, ma_device_type deviceType, const ma_device_descriptor* pDescriptorPlayback, const ma_device_descriptor* pDescriptorCapture) -{ - ma_result result; - - if (pDevice == NULL) { - return MA_INVALID_ARGS; - } - - /* Capture. */ - if (deviceType == ma_device_type_capture || deviceType == ma_device_type_duplex || deviceType == ma_device_type_loopback) { - if (ma_device_descriptor_is_valid(pDescriptorCapture) == MA_FALSE) { - return MA_INVALID_ARGS; - } - - pDevice->capture.internalFormat = pDescriptorCapture->format; - pDevice->capture.internalChannels = pDescriptorCapture->channels; - pDevice->capture.internalSampleRate = pDescriptorCapture->sampleRate; - MA_COPY_MEMORY(pDevice->capture.internalChannelMap, pDescriptorCapture->channelMap, sizeof(pDescriptorCapture->channelMap)); - pDevice->capture.internalPeriodSizeInFrames = pDescriptorCapture->periodSizeInFrames; - pDevice->capture.internalPeriods = pDescriptorCapture->periodCount; - - if (pDevice->capture.internalPeriodSizeInFrames == 0) { - pDevice->capture.internalPeriodSizeInFrames = ma_calculate_buffer_size_in_frames_from_milliseconds(pDescriptorCapture->periodSizeInMilliseconds, pDescriptorCapture->sampleRate); - } - } - - /* Playback. */ - if (deviceType == ma_device_type_playback || deviceType == ma_device_type_duplex) { - if (ma_device_descriptor_is_valid(pDescriptorPlayback) == MA_FALSE) { - return MA_INVALID_ARGS; - } - - pDevice->playback.internalFormat = pDescriptorPlayback->format; - pDevice->playback.internalChannels = pDescriptorPlayback->channels; - pDevice->playback.internalSampleRate = pDescriptorPlayback->sampleRate; - MA_COPY_MEMORY(pDevice->playback.internalChannelMap, pDescriptorPlayback->channelMap, sizeof(pDescriptorPlayback->channelMap)); - pDevice->playback.internalPeriodSizeInFrames = pDescriptorPlayback->periodSizeInFrames; - pDevice->playback.internalPeriods = pDescriptorPlayback->periodCount; - - if (pDevice->playback.internalPeriodSizeInFrames == 0) { - pDevice->playback.internalPeriodSizeInFrames = ma_calculate_buffer_size_in_frames_from_milliseconds(pDescriptorPlayback->periodSizeInMilliseconds, pDescriptorPlayback->sampleRate); - } - } - - /* - The name of the device can be retrieved from device info. This may be temporary and replaced with a `ma_device_get_info(pDevice, deviceType)` instead. - For loopback devices, we need to retrieve the name of the playback device. - */ - { - ma_device_info deviceInfo; - - if (deviceType == ma_device_type_capture || deviceType == ma_device_type_duplex || deviceType == ma_device_type_loopback) { - result = ma_device_get_info(pDevice, ma_device_type_capture, &deviceInfo); - if (result == MA_SUCCESS) { - ma_strncpy_s(pDevice->capture.name, sizeof(pDevice->capture.name), deviceInfo.name, (size_t)-1); - } else { - /* We failed to retrieve the device info. Fall back to a default name. */ - if (pDescriptorCapture->pDeviceID == NULL) { - ma_strncpy_s(pDevice->capture.name, sizeof(pDevice->capture.name), MA_DEFAULT_CAPTURE_DEVICE_NAME, (size_t)-1); - } else { - ma_strncpy_s(pDevice->capture.name, sizeof(pDevice->capture.name), "Capture Device", (size_t)-1); - } - } - } - - if (deviceType == ma_device_type_playback || deviceType == ma_device_type_duplex) { - result = ma_device_get_info(pDevice, ma_device_type_playback, &deviceInfo); - if (result == MA_SUCCESS) { - ma_strncpy_s(pDevice->playback.name, sizeof(pDevice->playback.name), deviceInfo.name, (size_t)-1); - } else { - /* We failed to retrieve the device info. Fall back to a default name. */ - if (pDescriptorPlayback->pDeviceID == NULL) { - ma_strncpy_s(pDevice->playback.name, sizeof(pDevice->playback.name), MA_DEFAULT_PLAYBACK_DEVICE_NAME, (size_t)-1); - } else { - ma_strncpy_s(pDevice->playback.name, sizeof(pDevice->playback.name), "Playback Device", (size_t)-1); - } - } - } - } - - /* Update data conversion. */ - return ma_device__post_init_setup(pDevice, deviceType); /* TODO: Should probably rename ma_device__post_init_setup() to something better. */ -} - - -static ma_thread_result MA_THREADCALL ma_worker_thread(void* pData) -{ - ma_device* pDevice = (ma_device*)pData; -#ifdef MA_WIN32 - HRESULT CoInitializeResult; -#endif - - MA_ASSERT(pDevice != NULL); - -#ifdef MA_WIN32 - CoInitializeResult = ma_CoInitializeEx(pDevice->pContext, NULL, MA_COINIT_VALUE); -#endif - - /* - When the device is being initialized its initial state is set to ma_device_state_uninitialized. Before returning from - ma_device_init(), the state needs to be set to something valid. In miniaudio the device's default state immediately - after initialization is stopped, so therefore we need to mark the device as such. miniaudio will wait on the worker - thread to signal an event to know when the worker thread is ready for action. - */ - ma_device__set_state(pDevice, ma_device_state_stopped); - ma_event_signal(&pDevice->stopEvent); - - for (;;) { /* <-- This loop just keeps the thread alive. The main audio loop is inside. */ - ma_result startResult; - ma_result stopResult; /* <-- This will store the result from onDeviceStop(). If it returns an error, we don't fire the stopped notification callback. */ - - /* We wait on an event to know when something has requested that the device be started and the main loop entered. */ - ma_event_wait(&pDevice->wakeupEvent); - - /* Default result code. */ - pDevice->workResult = MA_SUCCESS; - - /* If the reason for the wake up is that we are terminating, just break from the loop. */ - if (ma_device_get_state(pDevice) == ma_device_state_uninitialized) { - break; - } - - /* - Getting to this point means the device is wanting to get started. The function that has requested that the device - be started will be waiting on an event (pDevice->startEvent) which means we need to make sure we signal the event - in both the success and error case. It's important that the state of the device is set _before_ signaling the event. - */ - MA_ASSERT(ma_device_get_state(pDevice) == ma_device_state_starting); - - /* If the device has a start callback, start it now. */ - if (pDevice->pContext->callbacks.onDeviceStart != NULL) { - startResult = pDevice->pContext->callbacks.onDeviceStart(pDevice); - } else { - startResult = MA_SUCCESS; - } - - /* - If starting was not successful we'll need to loop back to the start and wait for something - to happen (pDevice->wakeupEvent). - */ - if (startResult != MA_SUCCESS) { - pDevice->workResult = startResult; - ma_event_signal(&pDevice->startEvent); /* <-- Always signal the start event so ma_device_start() can return as it'll be waiting on it. */ - continue; - } - - /* Make sure the state is set appropriately. */ - ma_device__set_state(pDevice, ma_device_state_started); /* <-- Set this before signaling the event so that the state is always guaranteed to be good after ma_device_start() has returned. */ - ma_event_signal(&pDevice->startEvent); - - ma_device__on_notification_started(pDevice); - - if (pDevice->pContext->callbacks.onDeviceDataLoop != NULL) { - pDevice->pContext->callbacks.onDeviceDataLoop(pDevice); - } else { - /* The backend is not using a custom main loop implementation, so now fall back to the blocking read-write implementation. */ - ma_device_audio_thread__default_read_write(pDevice); - } - - /* Getting here means we have broken from the main loop which happens the application has requested that device be stopped. */ - if (pDevice->pContext->callbacks.onDeviceStop != NULL) { - stopResult = pDevice->pContext->callbacks.onDeviceStop(pDevice); - } else { - stopResult = MA_SUCCESS; /* No stop callback with the backend. Just assume successful. */ - } - - /* - After the device has stopped, make sure an event is posted. Don't post a stopped event if - stopping failed. This can happen on some backends when the underlying stream has been - stopped due to the device being physically unplugged or disabled via an OS setting. - */ - if (stopResult == MA_SUCCESS) { - ma_device__on_notification_stopped(pDevice); - } - - /* If we stopped because the device has been uninitialized, abort now. */ - if (ma_device_get_state(pDevice) == ma_device_state_uninitialized) { - break; - } - - /* A function somewhere is waiting for the device to have stopped for real so we need to signal an event to allow it to continue. */ - ma_device__set_state(pDevice, ma_device_state_stopped); - ma_event_signal(&pDevice->stopEvent); - } - -#ifdef MA_WIN32 - if (CoInitializeResult == S_OK) { - ma_CoUninitialize(pDevice->pContext); - } -#endif - - return (ma_thread_result)0; -} - - -/* Helper for determining whether or not the given device is initialized. */ -static ma_bool32 ma_device__is_initialized(ma_device* pDevice) -{ - if (pDevice == NULL) { - return MA_FALSE; - } - - return ma_device_get_state(pDevice) != ma_device_state_uninitialized; -} - - -#ifdef MA_WIN32 -static ma_result ma_context_uninit_backend_apis__win32(ma_context* pContext) -{ - /* For some reason UWP complains when CoUninitialize() is called. I'm just not going to call it on UWP. */ -#if defined(MA_WIN32_DESKTOP) || defined(MA_WIN32_GDK) - if (pContext->win32.CoInitializeResult == S_OK) { - ma_CoUninitialize(pContext); - } - - #if defined(MA_WIN32_DESKTOP) - ma_dlclose(ma_context_get_log(pContext), pContext->win32.hUser32DLL); - ma_dlclose(ma_context_get_log(pContext), pContext->win32.hAdvapi32DLL); - #endif - - ma_dlclose(ma_context_get_log(pContext), pContext->win32.hOle32DLL); -#else - (void)pContext; -#endif - - return MA_SUCCESS; -} - -static ma_result ma_context_init_backend_apis__win32(ma_context* pContext) -{ -#if defined(MA_WIN32_DESKTOP) || defined(MA_WIN32_GDK) - #if defined(MA_WIN32_DESKTOP) - /* User32.dll */ - pContext->win32.hUser32DLL = ma_dlopen(ma_context_get_log(pContext), "user32.dll"); - if (pContext->win32.hUser32DLL == NULL) { - return MA_FAILED_TO_INIT_BACKEND; - } - - pContext->win32.GetForegroundWindow = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->win32.hUser32DLL, "GetForegroundWindow"); - pContext->win32.GetDesktopWindow = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->win32.hUser32DLL, "GetDesktopWindow"); - - - /* Advapi32.dll */ - pContext->win32.hAdvapi32DLL = ma_dlopen(ma_context_get_log(pContext), "advapi32.dll"); - if (pContext->win32.hAdvapi32DLL == NULL) { - return MA_FAILED_TO_INIT_BACKEND; - } - - pContext->win32.RegOpenKeyExA = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->win32.hAdvapi32DLL, "RegOpenKeyExA"); - pContext->win32.RegCloseKey = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->win32.hAdvapi32DLL, "RegCloseKey"); - pContext->win32.RegQueryValueExA = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->win32.hAdvapi32DLL, "RegQueryValueExA"); - #endif - - /* Ole32.dll */ - pContext->win32.hOle32DLL = ma_dlopen(ma_context_get_log(pContext), "ole32.dll"); - if (pContext->win32.hOle32DLL == NULL) { - return MA_FAILED_TO_INIT_BACKEND; - } - - pContext->win32.CoInitialize = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->win32.hOle32DLL, "CoInitialize"); - pContext->win32.CoInitializeEx = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->win32.hOle32DLL, "CoInitializeEx"); - pContext->win32.CoUninitialize = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->win32.hOle32DLL, "CoUninitialize"); - pContext->win32.CoCreateInstance = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->win32.hOle32DLL, "CoCreateInstance"); - pContext->win32.CoTaskMemFree = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->win32.hOle32DLL, "CoTaskMemFree"); - pContext->win32.PropVariantClear = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->win32.hOle32DLL, "PropVariantClear"); - pContext->win32.StringFromGUID2 = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->win32.hOle32DLL, "StringFromGUID2"); -#else - (void)pContext; /* Unused. */ -#endif - - pContext->win32.CoInitializeResult = ma_CoInitializeEx(pContext, NULL, MA_COINIT_VALUE); - return MA_SUCCESS; -} -#else -static ma_result ma_context_uninit_backend_apis__nix(ma_context* pContext) -{ - (void)pContext; - - return MA_SUCCESS; -} - -static ma_result ma_context_init_backend_apis__nix(ma_context* pContext) -{ - (void)pContext; - - return MA_SUCCESS; -} -#endif - -static ma_result ma_context_init_backend_apis(ma_context* pContext) -{ - ma_result result; -#ifdef MA_WIN32 - result = ma_context_init_backend_apis__win32(pContext); -#else - result = ma_context_init_backend_apis__nix(pContext); -#endif - - return result; -} - -static ma_result ma_context_uninit_backend_apis(ma_context* pContext) -{ - ma_result result; -#ifdef MA_WIN32 - result = ma_context_uninit_backend_apis__win32(pContext); -#else - result = ma_context_uninit_backend_apis__nix(pContext); -#endif - - return result; -} - - -/* The default capacity doesn't need to be too big. */ -#ifndef MA_DEFAULT_DEVICE_JOB_QUEUE_CAPACITY -#define MA_DEFAULT_DEVICE_JOB_QUEUE_CAPACITY 32 -#endif - -MA_API ma_device_job_thread_config ma_device_job_thread_config_init(void) -{ - ma_device_job_thread_config config; - - MA_ZERO_OBJECT(&config); - config.noThread = MA_FALSE; - config.jobQueueCapacity = MA_DEFAULT_DEVICE_JOB_QUEUE_CAPACITY; - config.jobQueueFlags = 0; - - return config; -} - - -static ma_thread_result MA_THREADCALL ma_device_job_thread_entry(void* pUserData) -{ - ma_device_job_thread* pJobThread = (ma_device_job_thread*)pUserData; - MA_ASSERT(pJobThread != NULL); - - for (;;) { - ma_result result; - ma_job job; - - result = ma_device_job_thread_next(pJobThread, &job); - if (result != MA_SUCCESS) { - break; - } - - if (job.toc.breakup.code == MA_JOB_TYPE_QUIT) { - break; - } - - ma_job_process(&job); - } - - return (ma_thread_result)0; -} - -MA_API ma_result ma_device_job_thread_init(const ma_device_job_thread_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_device_job_thread* pJobThread) -{ - ma_result result; - ma_job_queue_config jobQueueConfig; - - if (pJobThread == NULL) { - return MA_INVALID_ARGS; - } - - MA_ZERO_OBJECT(pJobThread); - - if (pConfig == NULL) { - return MA_INVALID_ARGS; - } - - - /* Initialize the job queue before the thread to ensure it's in a valid state. */ - jobQueueConfig = ma_job_queue_config_init(pConfig->jobQueueFlags, pConfig->jobQueueCapacity); - - result = ma_job_queue_init(&jobQueueConfig, pAllocationCallbacks, &pJobThread->jobQueue); - if (result != MA_SUCCESS) { - return result; /* Failed to initialize job queue. */ - } - - - /* The thread needs to be initialized after the job queue to ensure the thread doesn't try to access it prematurely. */ - if (pConfig->noThread == MA_FALSE) { - result = ma_thread_create(&pJobThread->thread, ma_thread_priority_normal, 0, ma_device_job_thread_entry, pJobThread, pAllocationCallbacks); - if (result != MA_SUCCESS) { - ma_job_queue_uninit(&pJobThread->jobQueue, pAllocationCallbacks); - return result; /* Failed to create the job thread. */ - } - - pJobThread->_hasThread = MA_TRUE; - } else { - pJobThread->_hasThread = MA_FALSE; - } - - - return MA_SUCCESS; -} - -MA_API void ma_device_job_thread_uninit(ma_device_job_thread* pJobThread, const ma_allocation_callbacks* pAllocationCallbacks) -{ - if (pJobThread == NULL) { - return; - } - - /* The first thing to do is post a quit message to the job queue. If we're using a thread we'll need to wait for it. */ - { - ma_job job = ma_job_init(MA_JOB_TYPE_QUIT); - ma_device_job_thread_post(pJobThread, &job); - } - - /* Wait for the thread to terminate naturally. */ - if (pJobThread->_hasThread) { - ma_thread_wait(&pJobThread->thread); - } - - /* At this point the thread should be terminated so we can safely uninitialize the job queue. */ - ma_job_queue_uninit(&pJobThread->jobQueue, pAllocationCallbacks); -} - -MA_API ma_result ma_device_job_thread_post(ma_device_job_thread* pJobThread, const ma_job* pJob) -{ - if (pJobThread == NULL || pJob == NULL) { - return MA_INVALID_ARGS; - } - - return ma_job_queue_post(&pJobThread->jobQueue, pJob); -} - -MA_API ma_result ma_device_job_thread_next(ma_device_job_thread* pJobThread, ma_job* pJob) -{ - if (pJob == NULL) { - return MA_INVALID_ARGS; - } - - MA_ZERO_OBJECT(pJob); - - if (pJobThread == NULL) { - return MA_INVALID_ARGS; - } - - return ma_job_queue_next(&pJobThread->jobQueue, pJob); -} - - -MA_API ma_bool32 ma_device_id_equal(const ma_device_id* pA, const ma_device_id* pB) -{ - size_t i; - - if (pA == NULL || pB == NULL) { - return MA_FALSE; - } - - for (i = 0; i < sizeof(ma_device_id); i += 1) { - if (((const char*)pA)[i] != ((const char*)pB)[i]) { - return MA_FALSE; - } - } - - return MA_TRUE; -} - - - -MA_API ma_context_config ma_context_config_init(void) -{ - ma_context_config config; - MA_ZERO_OBJECT(&config); - - return config; -} - -MA_API ma_result ma_context_init(const ma_backend backends[], ma_uint32 backendCount, const ma_context_config* pConfig, ma_context* pContext) -{ - ma_result result; - ma_context_config defaultConfig; - ma_backend defaultBackends[ma_backend_null+1]; - ma_uint32 iBackend; - ma_backend* pBackendsToIterate; - ma_uint32 backendsToIterateCount; - - if (pContext == NULL) { - return MA_INVALID_ARGS; - } - - MA_ZERO_OBJECT(pContext); - - /* Always make sure the config is set first to ensure properties are available as soon as possible. */ - if (pConfig == NULL) { - defaultConfig = ma_context_config_init(); - pConfig = &defaultConfig; - } - - /* Allocation callbacks need to come first because they'll be passed around to other areas. */ - result = ma_allocation_callbacks_init_copy(&pContext->allocationCallbacks, &pConfig->allocationCallbacks); - if (result != MA_SUCCESS) { - return result; - } - - /* Get a lot set up first so we can start logging ASAP. */ - if (pConfig->pLog != NULL) { - pContext->pLog = pConfig->pLog; - } else { - result = ma_log_init(&pContext->allocationCallbacks, &pContext->log); - if (result == MA_SUCCESS) { - pContext->pLog = &pContext->log; - } else { - pContext->pLog = NULL; /* Logging is not available. */ - } - } - - pContext->threadPriority = pConfig->threadPriority; - pContext->threadStackSize = pConfig->threadStackSize; - pContext->pUserData = pConfig->pUserData; - - /* Backend APIs need to be initialized first. This is where external libraries will be loaded and linked. */ - result = ma_context_init_backend_apis(pContext); - if (result != MA_SUCCESS) { - return result; - } - - for (iBackend = 0; iBackend <= ma_backend_null; ++iBackend) { - defaultBackends[iBackend] = (ma_backend)iBackend; - } - - pBackendsToIterate = (ma_backend*)backends; - backendsToIterateCount = backendCount; - if (pBackendsToIterate == NULL) { - pBackendsToIterate = (ma_backend*)defaultBackends; - backendsToIterateCount = ma_countof(defaultBackends); - } - - MA_ASSERT(pBackendsToIterate != NULL); - - for (iBackend = 0; iBackend < backendsToIterateCount; iBackend += 1) { - ma_backend backend = pBackendsToIterate[iBackend]; - - /* Make sure all callbacks are reset so we don't accidentally drag in any from previously failed initialization attempts. */ - MA_ZERO_OBJECT(&pContext->callbacks); - - /* These backends are using the new callback system. */ - switch (backend) { - #ifdef MA_HAS_WASAPI - case ma_backend_wasapi: - { - pContext->callbacks.onContextInit = ma_context_init__wasapi; - } break; - #endif - #ifdef MA_HAS_DSOUND - case ma_backend_dsound: - { - pContext->callbacks.onContextInit = ma_context_init__dsound; - } break; - #endif - #ifdef MA_HAS_WINMM - case ma_backend_winmm: - { - pContext->callbacks.onContextInit = ma_context_init__winmm; - } break; - #endif - #ifdef MA_HAS_COREAUDIO - case ma_backend_coreaudio: - { - pContext->callbacks.onContextInit = ma_context_init__coreaudio; - } break; - #endif - #ifdef MA_HAS_SNDIO - case ma_backend_sndio: - { - pContext->callbacks.onContextInit = ma_context_init__sndio; - } break; - #endif - #ifdef MA_HAS_AUDIO4 - case ma_backend_audio4: - { - pContext->callbacks.onContextInit = ma_context_init__audio4; - } break; - #endif - #ifdef MA_HAS_OSS - case ma_backend_oss: - { - pContext->callbacks.onContextInit = ma_context_init__oss; - } break; - #endif - #ifdef MA_HAS_PULSEAUDIO - case ma_backend_pulseaudio: - { - pContext->callbacks.onContextInit = ma_context_init__pulse; - } break; - #endif - #ifdef MA_HAS_ALSA - case ma_backend_alsa: - { - pContext->callbacks.onContextInit = ma_context_init__alsa; - } break; - #endif - #ifdef MA_HAS_JACK - case ma_backend_jack: - { - pContext->callbacks.onContextInit = ma_context_init__jack; - } break; - #endif - #ifdef MA_HAS_AAUDIO - case ma_backend_aaudio: - { - if (ma_is_backend_enabled(backend)) { - pContext->callbacks.onContextInit = ma_context_init__aaudio; - } - } break; - #endif - #ifdef MA_HAS_OPENSL - case ma_backend_opensl: - { - if (ma_is_backend_enabled(backend)) { - pContext->callbacks.onContextInit = ma_context_init__opensl; - } - } break; - #endif - #ifdef MA_HAS_WEBAUDIO - case ma_backend_webaudio: - { - pContext->callbacks.onContextInit = ma_context_init__webaudio; - } break; - #endif - #ifdef MA_HAS_CUSTOM - case ma_backend_custom: - { - /* Slightly different logic for custom backends. Custom backends can optionally set all of their callbacks in the config. */ - pContext->callbacks = pConfig->custom; - } break; - #endif - #ifdef MA_HAS_NULL - case ma_backend_null: - { - pContext->callbacks.onContextInit = ma_context_init__null; - } break; - #endif - - default: break; - } - - if (pContext->callbacks.onContextInit != NULL) { - ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_DEBUG, "Attempting to initialize %s backend...\n", ma_get_backend_name(backend)); - result = pContext->callbacks.onContextInit(pContext, pConfig, &pContext->callbacks); - } else { - /* Getting here means the onContextInit callback is not set which means the backend is not enabled. Special case for the custom backend. */ - if (backend != ma_backend_custom) { - result = MA_BACKEND_NOT_ENABLED; - } else { - #if !defined(MA_HAS_CUSTOM) - result = MA_BACKEND_NOT_ENABLED; - #else - result = MA_NO_BACKEND; - #endif - } - } - - /* If this iteration was successful, return. */ - if (result == MA_SUCCESS) { - result = ma_mutex_init(&pContext->deviceEnumLock); - if (result != MA_SUCCESS) { - ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_WARNING, "Failed to initialize mutex for device enumeration. ma_context_get_devices() is not thread safe.\n"); - } - - result = ma_mutex_init(&pContext->deviceInfoLock); - if (result != MA_SUCCESS) { - ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_WARNING, "Failed to initialize mutex for device info retrieval. ma_context_get_device_info() is not thread safe.\n"); - } - - ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_DEBUG, "System Architecture:\n"); - ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_DEBUG, " Endian: %s\n", ma_is_little_endian() ? "LE" : "BE"); - ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_DEBUG, " SSE2: %s\n", ma_has_sse2() ? "YES" : "NO"); - ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_DEBUG, " AVX2: %s\n", ma_has_avx2() ? "YES" : "NO"); - ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_DEBUG, " NEON: %s\n", ma_has_neon() ? "YES" : "NO"); - - pContext->backend = backend; - return result; - } else { - if (result == MA_BACKEND_NOT_ENABLED) { - ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_DEBUG, "%s backend is disabled.\n", ma_get_backend_name(backend)); - } else { - ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_DEBUG, "Failed to initialize %s backend.\n", ma_get_backend_name(backend)); - } - } - } - - /* If we get here it means an error occurred. */ - MA_ZERO_OBJECT(pContext); /* Safety. */ - return MA_NO_BACKEND; -} - -MA_API ma_result ma_context_uninit(ma_context* pContext) -{ - if (pContext == NULL) { - return MA_INVALID_ARGS; - } - - if (pContext->callbacks.onContextUninit != NULL) { - pContext->callbacks.onContextUninit(pContext); - } - - ma_mutex_uninit(&pContext->deviceEnumLock); - ma_mutex_uninit(&pContext->deviceInfoLock); - ma_free(pContext->pDeviceInfos, &pContext->allocationCallbacks); - ma_context_uninit_backend_apis(pContext); - - if (pContext->pLog == &pContext->log) { - ma_log_uninit(&pContext->log); - } - - return MA_SUCCESS; -} - -MA_API size_t ma_context_sizeof(void) -{ - return sizeof(ma_context); -} - - -MA_API ma_log* ma_context_get_log(ma_context* pContext) -{ - if (pContext == NULL) { - return NULL; - } - - return pContext->pLog; -} - - -MA_API ma_result ma_context_enumerate_devices(ma_context* pContext, ma_enum_devices_callback_proc callback, void* pUserData) -{ - ma_result result; - - if (pContext == NULL || callback == NULL) { - return MA_INVALID_ARGS; - } - - if (pContext->callbacks.onContextEnumerateDevices == NULL) { - return MA_INVALID_OPERATION; - } - - ma_mutex_lock(&pContext->deviceEnumLock); - { - result = pContext->callbacks.onContextEnumerateDevices(pContext, callback, pUserData); - } - ma_mutex_unlock(&pContext->deviceEnumLock); - - return result; -} - - -static ma_bool32 ma_context_get_devices__enum_callback(ma_context* pContext, ma_device_type deviceType, const ma_device_info* pInfo, void* pUserData) -{ - /* - We need to insert the device info into our main internal buffer. Where it goes depends on the device type. If it's a capture device - it's just appended to the end. If it's a playback device it's inserted just before the first capture device. - */ - - /* - First make sure we have room. Since the number of devices we add to the list is usually relatively small I've decided to use a - simple fixed size increment for buffer expansion. - */ - const ma_uint32 bufferExpansionCount = 2; - const ma_uint32 totalDeviceInfoCount = pContext->playbackDeviceInfoCount + pContext->captureDeviceInfoCount; - - if (totalDeviceInfoCount >= pContext->deviceInfoCapacity) { - ma_uint32 newCapacity = pContext->deviceInfoCapacity + bufferExpansionCount; - ma_device_info* pNewInfos = (ma_device_info*)ma_realloc(pContext->pDeviceInfos, sizeof(*pContext->pDeviceInfos)*newCapacity, &pContext->allocationCallbacks); - if (pNewInfos == NULL) { - return MA_FALSE; /* Out of memory. */ - } - - pContext->pDeviceInfos = pNewInfos; - pContext->deviceInfoCapacity = newCapacity; - } - - if (deviceType == ma_device_type_playback) { - /* Playback. Insert just before the first capture device. */ - - /* The first thing to do is move all of the capture devices down a slot. */ - ma_uint32 iFirstCaptureDevice = pContext->playbackDeviceInfoCount; - size_t iCaptureDevice; - for (iCaptureDevice = totalDeviceInfoCount; iCaptureDevice > iFirstCaptureDevice; --iCaptureDevice) { - pContext->pDeviceInfos[iCaptureDevice] = pContext->pDeviceInfos[iCaptureDevice-1]; - } - - /* Now just insert where the first capture device was before moving it down a slot. */ - pContext->pDeviceInfos[iFirstCaptureDevice] = *pInfo; - pContext->playbackDeviceInfoCount += 1; - } else { - /* Capture. Insert at the end. */ - pContext->pDeviceInfos[totalDeviceInfoCount] = *pInfo; - pContext->captureDeviceInfoCount += 1; - } - - (void)pUserData; - return MA_TRUE; -} - -MA_API ma_result ma_context_get_devices(ma_context* pContext, ma_device_info** ppPlaybackDeviceInfos, ma_uint32* pPlaybackDeviceCount, ma_device_info** ppCaptureDeviceInfos, ma_uint32* pCaptureDeviceCount) -{ - ma_result result; - - /* Safety. */ - if (ppPlaybackDeviceInfos != NULL) *ppPlaybackDeviceInfos = NULL; - if (pPlaybackDeviceCount != NULL) *pPlaybackDeviceCount = 0; - if (ppCaptureDeviceInfos != NULL) *ppCaptureDeviceInfos = NULL; - if (pCaptureDeviceCount != NULL) *pCaptureDeviceCount = 0; - - if (pContext == NULL) { - return MA_INVALID_ARGS; - } - - if (pContext->callbacks.onContextEnumerateDevices == NULL) { - return MA_INVALID_OPERATION; - } - - /* Note that we don't use ma_context_enumerate_devices() here because we want to do locking at a higher level. */ - ma_mutex_lock(&pContext->deviceEnumLock); - { - /* Reset everything first. */ - pContext->playbackDeviceInfoCount = 0; - pContext->captureDeviceInfoCount = 0; - - /* Now enumerate over available devices. */ - result = pContext->callbacks.onContextEnumerateDevices(pContext, ma_context_get_devices__enum_callback, NULL); - if (result == MA_SUCCESS) { - /* Playback devices. */ - if (ppPlaybackDeviceInfos != NULL) { - *ppPlaybackDeviceInfos = pContext->pDeviceInfos; - } - if (pPlaybackDeviceCount != NULL) { - *pPlaybackDeviceCount = pContext->playbackDeviceInfoCount; - } - - /* Capture devices. */ - if (ppCaptureDeviceInfos != NULL) { - *ppCaptureDeviceInfos = pContext->pDeviceInfos; - /* Capture devices come after playback devices. */ - if (pContext->playbackDeviceInfoCount > 0) { - /* Conditional, because NULL+0 is undefined behavior. */ - *ppCaptureDeviceInfos += pContext->playbackDeviceInfoCount; - } - } - if (pCaptureDeviceCount != NULL) { - *pCaptureDeviceCount = pContext->captureDeviceInfoCount; - } - } - } - ma_mutex_unlock(&pContext->deviceEnumLock); - - return result; -} - -MA_API ma_result ma_context_get_device_info(ma_context* pContext, ma_device_type deviceType, const ma_device_id* pDeviceID, ma_device_info* pDeviceInfo) -{ - ma_result result; - ma_device_info deviceInfo; - - /* NOTE: Do not clear pDeviceInfo on entry. The reason is the pDeviceID may actually point to pDeviceInfo->id which will break things. */ - if (pContext == NULL || pDeviceInfo == NULL) { - return MA_INVALID_ARGS; - } - - MA_ZERO_OBJECT(&deviceInfo); - - /* Help the backend out by copying over the device ID if we have one. */ - if (pDeviceID != NULL) { - MA_COPY_MEMORY(&deviceInfo.id, pDeviceID, sizeof(*pDeviceID)); - } - - if (pContext->callbacks.onContextGetDeviceInfo == NULL) { - return MA_INVALID_OPERATION; - } - - ma_mutex_lock(&pContext->deviceInfoLock); - { - result = pContext->callbacks.onContextGetDeviceInfo(pContext, deviceType, pDeviceID, &deviceInfo); - } - ma_mutex_unlock(&pContext->deviceInfoLock); - - *pDeviceInfo = deviceInfo; - return result; -} - -MA_API ma_bool32 ma_context_is_loopback_supported(ma_context* pContext) -{ - if (pContext == NULL) { - return MA_FALSE; - } - - return ma_is_loopback_supported(pContext->backend); -} - - -MA_API ma_device_config ma_device_config_init(ma_device_type deviceType) -{ - ma_device_config config; - MA_ZERO_OBJECT(&config); - config.deviceType = deviceType; - config.resampling = ma_resampler_config_init(ma_format_unknown, 0, 0, 0, ma_resample_algorithm_linear); /* Format/channels/rate don't matter here. */ - - return config; -} - -MA_API ma_result ma_device_init(ma_context* pContext, const ma_device_config* pConfig, ma_device* pDevice) -{ - ma_result result; - ma_device_descriptor descriptorPlayback; - ma_device_descriptor descriptorCapture; - - /* The context can be null, in which case we self-manage it. */ - if (pContext == NULL) { - return ma_device_init_ex(NULL, 0, NULL, pConfig, pDevice); - } - - if (pDevice == NULL) { - return MA_INVALID_ARGS; - } - - MA_ZERO_OBJECT(pDevice); - - if (pConfig == NULL) { - return MA_INVALID_ARGS; - } - - /* Check that we have our callbacks defined. */ - if (pContext->callbacks.onDeviceInit == NULL) { - return MA_INVALID_OPERATION; - } - - /* Basic config validation. */ - if (pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) { - if (pConfig->capture.channels > MA_MAX_CHANNELS) { - return MA_INVALID_ARGS; - } - - if (!ma__is_channel_map_valid(pConfig->capture.pChannelMap, pConfig->capture.channels)) { - return MA_INVALID_ARGS; - } - } - - if (pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex || pConfig->deviceType == ma_device_type_loopback) { - if (pConfig->playback.channels > MA_MAX_CHANNELS) { - return MA_INVALID_ARGS; - } - - if (!ma__is_channel_map_valid(pConfig->playback.pChannelMap, pConfig->playback.channels)) { - return MA_INVALID_ARGS; - } - } - - pDevice->pContext = pContext; - - /* Set the user data and log callback ASAP to ensure it is available for the entire initialization process. */ - pDevice->pUserData = pConfig->pUserData; - pDevice->onData = pConfig->dataCallback; - pDevice->onNotification = pConfig->notificationCallback; - pDevice->onStop = pConfig->stopCallback; - - if (pConfig->playback.pDeviceID != NULL) { - MA_COPY_MEMORY(&pDevice->playback.id, pConfig->playback.pDeviceID, sizeof(pDevice->playback.id)); - pDevice->playback.pID = &pDevice->playback.id; - } else { - pDevice->playback.pID = NULL; - } - - if (pConfig->capture.pDeviceID != NULL) { - MA_COPY_MEMORY(&pDevice->capture.id, pConfig->capture.pDeviceID, sizeof(pDevice->capture.id)); - pDevice->capture.pID = &pDevice->capture.id; - } else { - pDevice->capture.pID = NULL; - } - - pDevice->noPreSilencedOutputBuffer = pConfig->noPreSilencedOutputBuffer; - pDevice->noClip = pConfig->noClip; - pDevice->noDisableDenormals = pConfig->noDisableDenormals; - pDevice->noFixedSizedCallback = pConfig->noFixedSizedCallback; - ma_atomic_float_set(&pDevice->masterVolumeFactor, 1); - - pDevice->type = pConfig->deviceType; - pDevice->sampleRate = pConfig->sampleRate; - pDevice->resampling.algorithm = pConfig->resampling.algorithm; - pDevice->resampling.linear.lpfOrder = pConfig->resampling.linear.lpfOrder; - pDevice->resampling.pBackendVTable = pConfig->resampling.pBackendVTable; - pDevice->resampling.pBackendUserData = pConfig->resampling.pBackendUserData; - - pDevice->capture.shareMode = pConfig->capture.shareMode; - pDevice->capture.format = pConfig->capture.format; - pDevice->capture.channels = pConfig->capture.channels; - ma_channel_map_copy_or_default(pDevice->capture.channelMap, ma_countof(pDevice->capture.channelMap), pConfig->capture.pChannelMap, pConfig->capture.channels); - pDevice->capture.channelMixMode = pConfig->capture.channelMixMode; - pDevice->capture.calculateLFEFromSpatialChannels = pConfig->capture.calculateLFEFromSpatialChannels; - - pDevice->playback.shareMode = pConfig->playback.shareMode; - pDevice->playback.format = pConfig->playback.format; - pDevice->playback.channels = pConfig->playback.channels; - ma_channel_map_copy_or_default(pDevice->playback.channelMap, ma_countof(pDevice->playback.channelMap), pConfig->playback.pChannelMap, pConfig->playback.channels); - pDevice->playback.channelMixMode = pConfig->playback.channelMixMode; - pDevice->playback.calculateLFEFromSpatialChannels = pConfig->playback.calculateLFEFromSpatialChannels; - - result = ma_mutex_init(&pDevice->startStopLock); - if (result != MA_SUCCESS) { - return result; - } - - /* - When the device is started, the worker thread is the one that does the actual startup of the backend device. We - use a semaphore to wait for the background thread to finish the work. The same applies for stopping the device. - - Each of these semaphores is released internally by the worker thread when the work is completed. The start - semaphore is also used to wake up the worker thread. - */ - result = ma_event_init(&pDevice->wakeupEvent); - if (result != MA_SUCCESS) { - ma_mutex_uninit(&pDevice->startStopLock); - return result; - } - - result = ma_event_init(&pDevice->startEvent); - if (result != MA_SUCCESS) { - ma_event_uninit(&pDevice->wakeupEvent); - ma_mutex_uninit(&pDevice->startStopLock); - return result; - } - - result = ma_event_init(&pDevice->stopEvent); - if (result != MA_SUCCESS) { - ma_event_uninit(&pDevice->startEvent); - ma_event_uninit(&pDevice->wakeupEvent); - ma_mutex_uninit(&pDevice->startStopLock); - return result; - } - - - MA_ZERO_OBJECT(&descriptorPlayback); - descriptorPlayback.pDeviceID = pConfig->playback.pDeviceID; - descriptorPlayback.shareMode = pConfig->playback.shareMode; - descriptorPlayback.format = pConfig->playback.format; - descriptorPlayback.channels = pConfig->playback.channels; - descriptorPlayback.sampleRate = pConfig->sampleRate; - ma_channel_map_copy_or_default(descriptorPlayback.channelMap, ma_countof(descriptorPlayback.channelMap), pConfig->playback.pChannelMap, pConfig->playback.channels); - descriptorPlayback.periodSizeInFrames = pConfig->periodSizeInFrames; - descriptorPlayback.periodSizeInMilliseconds = pConfig->periodSizeInMilliseconds; - descriptorPlayback.periodCount = pConfig->periods; - - if (descriptorPlayback.periodCount == 0) { - descriptorPlayback.periodCount = MA_DEFAULT_PERIODS; - } - - - MA_ZERO_OBJECT(&descriptorCapture); - descriptorCapture.pDeviceID = pConfig->capture.pDeviceID; - descriptorCapture.shareMode = pConfig->capture.shareMode; - descriptorCapture.format = pConfig->capture.format; - descriptorCapture.channels = pConfig->capture.channels; - descriptorCapture.sampleRate = pConfig->sampleRate; - ma_channel_map_copy_or_default(descriptorCapture.channelMap, ma_countof(descriptorCapture.channelMap), pConfig->capture.pChannelMap, pConfig->capture.channels); - descriptorCapture.periodSizeInFrames = pConfig->periodSizeInFrames; - descriptorCapture.periodSizeInMilliseconds = pConfig->periodSizeInMilliseconds; - descriptorCapture.periodCount = pConfig->periods; - - if (descriptorCapture.periodCount == 0) { - descriptorCapture.periodCount = MA_DEFAULT_PERIODS; - } - - - result = pContext->callbacks.onDeviceInit(pDevice, pConfig, &descriptorPlayback, &descriptorCapture); - if (result != MA_SUCCESS) { - ma_event_uninit(&pDevice->startEvent); - ma_event_uninit(&pDevice->wakeupEvent); - ma_mutex_uninit(&pDevice->startStopLock); - return result; - } - -#if 0 - /* - On output the descriptors will contain the *actual* data format of the device. We need this to know how to convert the data between - the requested format and the internal format. - */ - if (pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex || pConfig->deviceType == ma_device_type_loopback) { - if (!ma_device_descriptor_is_valid(&descriptorCapture)) { - ma_device_uninit(pDevice); - return MA_INVALID_ARGS; - } - - pDevice->capture.internalFormat = descriptorCapture.format; - pDevice->capture.internalChannels = descriptorCapture.channels; - pDevice->capture.internalSampleRate = descriptorCapture.sampleRate; - ma_channel_map_copy(pDevice->capture.internalChannelMap, descriptorCapture.channelMap, descriptorCapture.channels); - pDevice->capture.internalPeriodSizeInFrames = descriptorCapture.periodSizeInFrames; - pDevice->capture.internalPeriods = descriptorCapture.periodCount; - - if (pDevice->capture.internalPeriodSizeInFrames == 0) { - pDevice->capture.internalPeriodSizeInFrames = ma_calculate_buffer_size_in_frames_from_milliseconds(descriptorCapture.periodSizeInMilliseconds, descriptorCapture.sampleRate); - } - } - - if (pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) { - if (!ma_device_descriptor_is_valid(&descriptorPlayback)) { - ma_device_uninit(pDevice); - return MA_INVALID_ARGS; - } - - pDevice->playback.internalFormat = descriptorPlayback.format; - pDevice->playback.internalChannels = descriptorPlayback.channels; - pDevice->playback.internalSampleRate = descriptorPlayback.sampleRate; - ma_channel_map_copy(pDevice->playback.internalChannelMap, descriptorPlayback.channelMap, descriptorPlayback.channels); - pDevice->playback.internalPeriodSizeInFrames = descriptorPlayback.periodSizeInFrames; - pDevice->playback.internalPeriods = descriptorPlayback.periodCount; - - if (pDevice->playback.internalPeriodSizeInFrames == 0) { - pDevice->playback.internalPeriodSizeInFrames = ma_calculate_buffer_size_in_frames_from_milliseconds(descriptorPlayback.periodSizeInMilliseconds, descriptorPlayback.sampleRate); - } - } - - - /* - The name of the device can be retrieved from device info. This may be temporary and replaced with a `ma_device_get_info(pDevice, deviceType)` instead. - For loopback devices, we need to retrieve the name of the playback device. - */ - { - ma_device_info deviceInfo; - - if (pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex || pConfig->deviceType == ma_device_type_loopback) { - result = ma_device_get_info(pDevice, (pConfig->deviceType == ma_device_type_loopback) ? ma_device_type_playback : ma_device_type_capture, &deviceInfo); - if (result == MA_SUCCESS) { - ma_strncpy_s(pDevice->capture.name, sizeof(pDevice->capture.name), deviceInfo.name, (size_t)-1); - } else { - /* We failed to retrieve the device info. Fall back to a default name. */ - if (descriptorCapture.pDeviceID == NULL) { - ma_strncpy_s(pDevice->capture.name, sizeof(pDevice->capture.name), MA_DEFAULT_CAPTURE_DEVICE_NAME, (size_t)-1); - } else { - ma_strncpy_s(pDevice->capture.name, sizeof(pDevice->capture.name), "Capture Device", (size_t)-1); - } - } - } - - if (pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) { - result = ma_device_get_info(pDevice, ma_device_type_playback, &deviceInfo); - if (result == MA_SUCCESS) { - ma_strncpy_s(pDevice->playback.name, sizeof(pDevice->playback.name), deviceInfo.name, (size_t)-1); - } else { - /* We failed to retrieve the device info. Fall back to a default name. */ - if (descriptorPlayback.pDeviceID == NULL) { - ma_strncpy_s(pDevice->playback.name, sizeof(pDevice->playback.name), MA_DEFAULT_PLAYBACK_DEVICE_NAME, (size_t)-1); - } else { - ma_strncpy_s(pDevice->playback.name, sizeof(pDevice->playback.name), "Playback Device", (size_t)-1); - } - } - } - } - - - ma_device__post_init_setup(pDevice, pConfig->deviceType); -#endif - - result = ma_device_post_init(pDevice, pConfig->deviceType, &descriptorPlayback, &descriptorCapture); - if (result != MA_SUCCESS) { - ma_device_uninit(pDevice); - return result; - } - - - /* - If we're using fixed sized callbacks we'll need to make use of an intermediary buffer. Needs to - be done after post_init_setup() because we'll need access to the sample rate. - */ - if (pConfig->noFixedSizedCallback == MA_FALSE) { - /* We're using a fixed sized data callback so we'll need an intermediary buffer. */ - ma_uint32 intermediaryBufferCap = pConfig->periodSizeInFrames; - if (intermediaryBufferCap == 0) { - intermediaryBufferCap = ma_calculate_buffer_size_in_frames_from_milliseconds(pConfig->periodSizeInMilliseconds, pDevice->sampleRate); - } - - if (pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex || pConfig->deviceType == ma_device_type_loopback) { - ma_uint32 intermediaryBufferSizeInBytes; - - pDevice->capture.intermediaryBufferLen = 0; - pDevice->capture.intermediaryBufferCap = intermediaryBufferCap; - if (pDevice->capture.intermediaryBufferCap == 0) { - pDevice->capture.intermediaryBufferCap = pDevice->capture.internalPeriodSizeInFrames; - } - - intermediaryBufferSizeInBytes = pDevice->capture.intermediaryBufferCap * ma_get_bytes_per_frame(pDevice->capture.format, pDevice->capture.channels); - - pDevice->capture.pIntermediaryBuffer = ma_malloc((size_t)intermediaryBufferSizeInBytes, &pContext->allocationCallbacks); - if (pDevice->capture.pIntermediaryBuffer == NULL) { - ma_device_uninit(pDevice); - return MA_OUT_OF_MEMORY; - } - - /* Silence the buffer for safety. */ - ma_silence_pcm_frames(pDevice->capture.pIntermediaryBuffer, pDevice->capture.intermediaryBufferCap, pDevice->capture.format, pDevice->capture.channels); - pDevice->capture.intermediaryBufferLen = pDevice->capture.intermediaryBufferCap; - } - - if (pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) { - ma_uint64 intermediaryBufferSizeInBytes; - - pDevice->playback.intermediaryBufferLen = 0; - if (pConfig->deviceType == ma_device_type_duplex) { - pDevice->playback.intermediaryBufferCap = pDevice->capture.intermediaryBufferCap; /* In duplex mode, make sure the intermediary buffer is always the same size as the capture side. */ - } else { - pDevice->playback.intermediaryBufferCap = intermediaryBufferCap; - if (pDevice->playback.intermediaryBufferCap == 0) { - pDevice->playback.intermediaryBufferCap = pDevice->playback.internalPeriodSizeInFrames; - } - } - - intermediaryBufferSizeInBytes = pDevice->playback.intermediaryBufferCap * ma_get_bytes_per_frame(pDevice->playback.format, pDevice->playback.channels); - - pDevice->playback.pIntermediaryBuffer = ma_malloc((size_t)intermediaryBufferSizeInBytes, &pContext->allocationCallbacks); - if (pDevice->playback.pIntermediaryBuffer == NULL) { - ma_device_uninit(pDevice); - return MA_OUT_OF_MEMORY; - } - - /* Silence the buffer for safety. */ - ma_silence_pcm_frames(pDevice->playback.pIntermediaryBuffer, pDevice->playback.intermediaryBufferCap, pDevice->playback.format, pDevice->playback.channels); - pDevice->playback.intermediaryBufferLen = 0; - } - } else { - /* Not using a fixed sized data callback so no need for an intermediary buffer. */ - } - - - /* Some backends don't require the worker thread. */ - if (!ma_context_is_backend_asynchronous(pContext)) { - /* The worker thread. */ - result = ma_thread_create(&pDevice->thread, pContext->threadPriority, pContext->threadStackSize, ma_worker_thread, pDevice, &pContext->allocationCallbacks); - if (result != MA_SUCCESS) { - ma_device_uninit(pDevice); - return result; - } - - /* Wait for the worker thread to put the device into its stopped state for real. */ - ma_event_wait(&pDevice->stopEvent); - MA_ASSERT(ma_device_get_state(pDevice) == ma_device_state_stopped); - } else { - /* - If the backend is asynchronous and the device is duplex, we'll need an intermediary ring buffer. Note that this needs to be done - after ma_device__post_init_setup(). - */ - if (ma_context_is_backend_asynchronous(pContext)) { - if (pConfig->deviceType == ma_device_type_duplex) { - result = ma_duplex_rb_init(pDevice->capture.format, pDevice->capture.channels, pDevice->sampleRate, pDevice->capture.internalSampleRate, pDevice->capture.internalPeriodSizeInFrames, &pDevice->pContext->allocationCallbacks, &pDevice->duplexRB); - if (result != MA_SUCCESS) { - ma_device_uninit(pDevice); - return result; - } - } - } - - ma_device__set_state(pDevice, ma_device_state_stopped); - } - - /* Log device information. */ - { - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, "[%s]\n", ma_get_backend_name(pDevice->pContext->backend)); - if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex || pDevice->type == ma_device_type_loopback) { - char name[MA_MAX_DEVICE_NAME_LENGTH + 1]; - ma_device_get_name(pDevice, ma_device_type_capture, name, sizeof(name), NULL); - - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, " %s (%s)\n", name, "Capture"); - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, " Format: %s -> %s\n", ma_get_format_name(pDevice->capture.internalFormat), ma_get_format_name(pDevice->capture.format)); - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, " Channels: %d -> %d\n", pDevice->capture.internalChannels, pDevice->capture.channels); - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, " Sample Rate: %d -> %d\n", pDevice->capture.internalSampleRate, pDevice->sampleRate); - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, " Buffer Size: %d*%d (%d)\n", pDevice->capture.internalPeriodSizeInFrames, pDevice->capture.internalPeriods, (pDevice->capture.internalPeriodSizeInFrames * pDevice->capture.internalPeriods)); - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, " Conversion:\n"); - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, " Pre Format Conversion: %s\n", pDevice->capture.converter.hasPreFormatConversion ? "YES" : "NO"); - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, " Post Format Conversion: %s\n", pDevice->capture.converter.hasPostFormatConversion ? "YES" : "NO"); - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, " Channel Routing: %s\n", pDevice->capture.converter.hasChannelConverter ? "YES" : "NO"); - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, " Resampling: %s\n", pDevice->capture.converter.hasResampler ? "YES" : "NO"); - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, " Passthrough: %s\n", pDevice->capture.converter.isPassthrough ? "YES" : "NO"); - { - char channelMapStr[1024]; - ma_channel_map_to_string(pDevice->capture.internalChannelMap, pDevice->capture.internalChannels, channelMapStr, sizeof(channelMapStr)); - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, " Channel Map In: {%s}\n", channelMapStr); - - ma_channel_map_to_string(pDevice->capture.channelMap, pDevice->capture.channels, channelMapStr, sizeof(channelMapStr)); - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, " Channel Map Out: {%s}\n", channelMapStr); - } - } - if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) { - char name[MA_MAX_DEVICE_NAME_LENGTH + 1]; - ma_device_get_name(pDevice, ma_device_type_playback, name, sizeof(name), NULL); - - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, " %s (%s)\n", name, "Playback"); - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, " Format: %s -> %s\n", ma_get_format_name(pDevice->playback.format), ma_get_format_name(pDevice->playback.internalFormat)); - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, " Channels: %d -> %d\n", pDevice->playback.channels, pDevice->playback.internalChannels); - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, " Sample Rate: %d -> %d\n", pDevice->sampleRate, pDevice->playback.internalSampleRate); - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, " Buffer Size: %d*%d (%d)\n", pDevice->playback.internalPeriodSizeInFrames, pDevice->playback.internalPeriods, (pDevice->playback.internalPeriodSizeInFrames * pDevice->playback.internalPeriods)); - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, " Conversion:\n"); - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, " Pre Format Conversion: %s\n", pDevice->playback.converter.hasPreFormatConversion ? "YES" : "NO"); - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, " Post Format Conversion: %s\n", pDevice->playback.converter.hasPostFormatConversion ? "YES" : "NO"); - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, " Channel Routing: %s\n", pDevice->playback.converter.hasChannelConverter ? "YES" : "NO"); - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, " Resampling: %s\n", pDevice->playback.converter.hasResampler ? "YES" : "NO"); - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, " Passthrough: %s\n", pDevice->playback.converter.isPassthrough ? "YES" : "NO"); - { - char channelMapStr[1024]; - ma_channel_map_to_string(pDevice->playback.channelMap, pDevice->playback.channels, channelMapStr, sizeof(channelMapStr)); - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, " Channel Map In: {%s}\n", channelMapStr); - - ma_channel_map_to_string(pDevice->playback.internalChannelMap, pDevice->playback.internalChannels, channelMapStr, sizeof(channelMapStr)); - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, " Channel Map Out: {%s}\n", channelMapStr); - } - } - } - - MA_ASSERT(ma_device_get_state(pDevice) == ma_device_state_stopped); - return MA_SUCCESS; -} - -MA_API ma_result ma_device_init_ex(const ma_backend backends[], ma_uint32 backendCount, const ma_context_config* pContextConfig, const ma_device_config* pConfig, ma_device* pDevice) -{ - ma_result result; - ma_context* pContext; - ma_backend defaultBackends[ma_backend_null+1]; - ma_uint32 iBackend; - ma_backend* pBackendsToIterate; - ma_uint32 backendsToIterateCount; - ma_allocation_callbacks allocationCallbacks; - - if (pConfig == NULL) { - return MA_INVALID_ARGS; - } - - if (pContextConfig != NULL) { - result = ma_allocation_callbacks_init_copy(&allocationCallbacks, &pContextConfig->allocationCallbacks); - if (result != MA_SUCCESS) { - return result; - } - } else { - allocationCallbacks = ma_allocation_callbacks_init_default(); - } - - pContext = (ma_context*)ma_malloc(sizeof(*pContext), &allocationCallbacks); - if (pContext == NULL) { - return MA_OUT_OF_MEMORY; - } - - for (iBackend = 0; iBackend <= ma_backend_null; ++iBackend) { - defaultBackends[iBackend] = (ma_backend)iBackend; - } - - pBackendsToIterate = (ma_backend*)backends; - backendsToIterateCount = backendCount; - if (pBackendsToIterate == NULL) { - pBackendsToIterate = (ma_backend*)defaultBackends; - backendsToIterateCount = ma_countof(defaultBackends); - } - - result = MA_NO_BACKEND; - - for (iBackend = 0; iBackend < backendsToIterateCount; ++iBackend) { - /* - This is a hack for iOS. If the context config is null, there's a good chance the - `ma_device_init(NULL, &deviceConfig, pDevice);` pattern is being used. In this - case, set the session category based on the device type. - */ - #if defined(MA_APPLE_MOBILE) - ma_context_config contextConfig; - - if (pContextConfig == NULL) { - contextConfig = ma_context_config_init(); - switch (pConfig->deviceType) { - case ma_device_type_duplex: { - contextConfig.coreaudio.sessionCategory = ma_ios_session_category_play_and_record; - } break; - case ma_device_type_capture: { - contextConfig.coreaudio.sessionCategory = ma_ios_session_category_record; - } break; - case ma_device_type_playback: - default: { - contextConfig.coreaudio.sessionCategory = ma_ios_session_category_playback; - } break; - } - - pContextConfig = &contextConfig; - } - #endif - - result = ma_context_init(&pBackendsToIterate[iBackend], 1, pContextConfig, pContext); - if (result == MA_SUCCESS) { - result = ma_device_init(pContext, pConfig, pDevice); - if (result == MA_SUCCESS) { - break; /* Success. */ - } else { - ma_context_uninit(pContext); /* Failure. */ - } - } - } - - if (result != MA_SUCCESS) { - ma_free(pContext, &allocationCallbacks); - return result; - } - - pDevice->isOwnerOfContext = MA_TRUE; - return result; -} - -MA_API void ma_device_uninit(ma_device* pDevice) -{ - if (!ma_device__is_initialized(pDevice)) { - return; - } - - /* - It's possible for the miniaudio side of the device and the backend to not be in sync due to - system-level situations such as the computer being put into sleep mode and the backend not - notifying miniaudio of the fact the device has stopped. It's possible for this to result in a - deadlock due to miniaudio thinking the device is in a running state, when in fact it's not - running at all. For this reason I am no longer explicitly stopping the device. I don't think - this should affect anyone in practice since uninitializing the backend will naturally stop the - device anyway. - */ - #if 0 - { - /* Make sure the device is stopped first. The backends will probably handle this naturally, but I like to do it explicitly for my own sanity. */ - if (ma_device_is_started(pDevice)) { - ma_device_stop(pDevice); - } - } - #endif - - /* Putting the device into an uninitialized state will make the worker thread return. */ - ma_device__set_state(pDevice, ma_device_state_uninitialized); - - /* Wake up the worker thread and wait for it to properly terminate. */ - if (!ma_context_is_backend_asynchronous(pDevice->pContext)) { - ma_event_signal(&pDevice->wakeupEvent); - ma_thread_wait(&pDevice->thread); - } - - if (pDevice->pContext->callbacks.onDeviceUninit != NULL) { - pDevice->pContext->callbacks.onDeviceUninit(pDevice); - } - - - ma_event_uninit(&pDevice->stopEvent); - ma_event_uninit(&pDevice->startEvent); - ma_event_uninit(&pDevice->wakeupEvent); - ma_mutex_uninit(&pDevice->startStopLock); - - if (ma_context_is_backend_asynchronous(pDevice->pContext)) { - if (pDevice->type == ma_device_type_duplex) { - ma_duplex_rb_uninit(&pDevice->duplexRB); - } - } - - if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex || pDevice->type == ma_device_type_loopback) { - ma_data_converter_uninit(&pDevice->capture.converter, &pDevice->pContext->allocationCallbacks); - } - if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) { - ma_data_converter_uninit(&pDevice->playback.converter, &pDevice->pContext->allocationCallbacks); - } - - if (pDevice->playback.pInputCache != NULL) { - ma_free(pDevice->playback.pInputCache, &pDevice->pContext->allocationCallbacks); - } - - if (pDevice->capture.pIntermediaryBuffer != NULL) { - ma_free(pDevice->capture.pIntermediaryBuffer, &pDevice->pContext->allocationCallbacks); - } - if (pDevice->playback.pIntermediaryBuffer != NULL) { - ma_free(pDevice->playback.pIntermediaryBuffer, &pDevice->pContext->allocationCallbacks); - } - - if (pDevice->isOwnerOfContext) { - ma_allocation_callbacks allocationCallbacks = pDevice->pContext->allocationCallbacks; - - ma_context_uninit(pDevice->pContext); - ma_free(pDevice->pContext, &allocationCallbacks); - } - - MA_ZERO_OBJECT(pDevice); -} - -MA_API ma_context* ma_device_get_context(ma_device* pDevice) -{ - if (pDevice == NULL) { - return NULL; - } - - return pDevice->pContext; -} - -MA_API ma_log* ma_device_get_log(ma_device* pDevice) -{ - return ma_context_get_log(ma_device_get_context(pDevice)); -} - -MA_API ma_result ma_device_get_info(ma_device* pDevice, ma_device_type type, ma_device_info* pDeviceInfo) -{ - if (pDeviceInfo == NULL) { - return MA_INVALID_ARGS; - } - - MA_ZERO_OBJECT(pDeviceInfo); - - if (pDevice == NULL) { - return MA_INVALID_ARGS; - } - - /* If the onDeviceGetInfo() callback is set, use that. Otherwise we'll fall back to ma_context_get_device_info(). */ - if (pDevice->pContext->callbacks.onDeviceGetInfo != NULL) { - return pDevice->pContext->callbacks.onDeviceGetInfo(pDevice, type, pDeviceInfo); - } - - /* Getting here means onDeviceGetInfo is not implemented so we need to fall back to an alternative. */ - if (type == ma_device_type_playback) { - return ma_context_get_device_info(pDevice->pContext, type, pDevice->playback.pID, pDeviceInfo); - } else { - /* - Here we're getting the capture side, which is the branch we'll be entering for a loopback - device, since loopback is capturing. However, if the device is using the default device ID, - it won't get the correct information because it'll think we're asking for the default - capture device, where in fact for loopback we want the default *playback* device. We'll do - a bit of a hack here to make sure we get the correct info. - */ - if (pDevice->type == ma_device_type_loopback && pDevice->capture.pID == NULL) { - type = ma_device_type_playback; - } - - return ma_context_get_device_info(pDevice->pContext, type, pDevice->capture.pID, pDeviceInfo); - } -} - -MA_API ma_result ma_device_get_name(ma_device* pDevice, ma_device_type type, char* pName, size_t nameCap, size_t* pLengthNotIncludingNullTerminator) -{ - ma_result result; - ma_device_info deviceInfo; - - if (pLengthNotIncludingNullTerminator != NULL) { - *pLengthNotIncludingNullTerminator = 0; - } - - if (pName != NULL && nameCap > 0) { - pName[0] = '\0'; - } - - result = ma_device_get_info(pDevice, type, &deviceInfo); - if (result != MA_SUCCESS) { - return result; - } - - if (pName != NULL) { - ma_strncpy_s(pName, nameCap, deviceInfo.name, (size_t)-1); - - /* - For safety, make sure the length is based on the truncated output string rather than the - source. Otherwise the caller might assume the output buffer contains more content than it - actually does. - */ - if (pLengthNotIncludingNullTerminator != NULL) { - *pLengthNotIncludingNullTerminator = strlen(pName); - } - } else { - /* Name not specified. Just report the length of the source string. */ - if (pLengthNotIncludingNullTerminator != NULL) { - *pLengthNotIncludingNullTerminator = strlen(deviceInfo.name); - } - } - - return MA_SUCCESS; -} - -MA_API ma_result ma_device_start(ma_device* pDevice) -{ - ma_result result; - - if (pDevice == NULL) { - return MA_INVALID_ARGS; - } - - if (ma_device_get_state(pDevice) == ma_device_state_uninitialized) { - return MA_INVALID_OPERATION; /* Not initialized. */ - } - - if (ma_device_get_state(pDevice) == ma_device_state_started) { - return MA_SUCCESS; /* Already started. */ - } - - ma_mutex_lock(&pDevice->startStopLock); - { - /* - We need to check again if the device is in a started state because it's possible for one thread to have started the device - while another was waiting on the mutex. - */ - if (ma_device_get_state(pDevice) == ma_device_state_started) { - ma_mutex_unlock(&pDevice->startStopLock); - return MA_SUCCESS; /* Already started. */ - } - - /* Starting and stopping are wrapped in a mutex which means we can assert that the device is in a stopped or paused state. */ - MA_ASSERT(ma_device_get_state(pDevice) == ma_device_state_stopped); - - ma_device__set_state(pDevice, ma_device_state_starting); - - /* Asynchronous backends need to be handled differently. */ - if (ma_context_is_backend_asynchronous(pDevice->pContext)) { - if (pDevice->pContext->callbacks.onDeviceStart != NULL) { - result = pDevice->pContext->callbacks.onDeviceStart(pDevice); - } else { - result = MA_INVALID_OPERATION; - } - - if (result == MA_SUCCESS) { - ma_device__set_state(pDevice, ma_device_state_started); - ma_device__on_notification_started(pDevice); - } - } else { - /* - Synchronous backends are started by signaling an event that's being waited on in the worker thread. We first wake up the - thread and then wait for the start event. - */ - ma_event_signal(&pDevice->wakeupEvent); - - /* - Wait for the worker thread to finish starting the device. Note that the worker thread will be the one who puts the device - into the started state. Don't call ma_device__set_state() here. - */ - ma_event_wait(&pDevice->startEvent); - result = pDevice->workResult; - } - - /* We changed the state from stopped to started, so if we failed, make sure we put the state back to stopped. */ - if (result != MA_SUCCESS) { - ma_device__set_state(pDevice, ma_device_state_stopped); - } - } - ma_mutex_unlock(&pDevice->startStopLock); - - return result; -} - -MA_API ma_result ma_device_stop(ma_device* pDevice) -{ - ma_result result; - - if (pDevice == NULL) { - return MA_INVALID_ARGS; - } - - if (ma_device_get_state(pDevice) == ma_device_state_uninitialized) { - return MA_INVALID_OPERATION; /* Not initialized. */ - } - - if (ma_device_get_state(pDevice) == ma_device_state_stopped) { - return MA_SUCCESS; /* Already stopped. */ - } - - ma_mutex_lock(&pDevice->startStopLock); - { - /* - We need to check again if the device is in a stopped state because it's possible for one thread to have stopped the device - while another was waiting on the mutex. - */ - if (ma_device_get_state(pDevice) == ma_device_state_stopped) { - ma_mutex_unlock(&pDevice->startStopLock); - return MA_SUCCESS; /* Already stopped. */ - } - - /* Starting and stopping are wrapped in a mutex which means we can assert that the device is in a started or paused state. */ - MA_ASSERT(ma_device_get_state(pDevice) == ma_device_state_started); - - ma_device__set_state(pDevice, ma_device_state_stopping); - - /* Asynchronous backends need to be handled differently. */ - if (ma_context_is_backend_asynchronous(pDevice->pContext)) { - /* Asynchronous backends must have a stop operation. */ - if (pDevice->pContext->callbacks.onDeviceStop != NULL) { - result = pDevice->pContext->callbacks.onDeviceStop(pDevice); - } else { - result = MA_INVALID_OPERATION; - } - - ma_device__set_state(pDevice, ma_device_state_stopped); - } else { - /* - Synchronous backends. The stop callback is always called from the worker thread. Do not call the stop callback here. If - the backend is implementing its own audio thread loop we'll need to wake it up if required. Note that we need to make - sure the state of the device is *not* playing right now, which it shouldn't be since we set it above. This is super - important though, so I'm asserting it here as well for extra safety in case we accidentally change something later. - */ - MA_ASSERT(ma_device_get_state(pDevice) != ma_device_state_started); - - if (pDevice->pContext->callbacks.onDeviceDataLoopWakeup != NULL) { - pDevice->pContext->callbacks.onDeviceDataLoopWakeup(pDevice); - } - - /* - We need to wait for the worker thread to become available for work before returning. Note that the worker thread will be - the one who puts the device into the stopped state. Don't call ma_device__set_state() here. - */ - ma_event_wait(&pDevice->stopEvent); - result = MA_SUCCESS; - } - - /* - This is a safety measure to ensure the internal buffer has been cleared so any leftover - does not get played the next time the device starts. Ideally this should be drained by - the backend first. - */ - pDevice->playback.intermediaryBufferLen = 0; - pDevice->playback.inputCacheConsumed = 0; - pDevice->playback.inputCacheRemaining = 0; - } - ma_mutex_unlock(&pDevice->startStopLock); - - return result; -} - -MA_API ma_bool32 ma_device_is_started(const ma_device* pDevice) -{ - return ma_device_get_state(pDevice) == ma_device_state_started; -} - -MA_API ma_device_state ma_device_get_state(const ma_device* pDevice) -{ - if (pDevice == NULL) { - return ma_device_state_uninitialized; - } - - return ma_atomic_device_state_get((ma_atomic_device_state*)&pDevice->state); /* Naughty cast to get rid of a const warning. */ -} - -MA_API ma_result ma_device_set_master_volume(ma_device* pDevice, float volume) -{ - if (pDevice == NULL) { - return MA_INVALID_ARGS; - } - - if (volume < 0.0f) { - return MA_INVALID_ARGS; - } - - ma_atomic_float_set(&pDevice->masterVolumeFactor, volume); - - return MA_SUCCESS; -} - -MA_API ma_result ma_device_get_master_volume(ma_device* pDevice, float* pVolume) -{ - if (pVolume == NULL) { - return MA_INVALID_ARGS; - } - - if (pDevice == NULL) { - *pVolume = 0; - return MA_INVALID_ARGS; - } - - *pVolume = ma_atomic_float_get(&pDevice->masterVolumeFactor); - - return MA_SUCCESS; -} - -MA_API ma_result ma_device_set_master_volume_db(ma_device* pDevice, float gainDB) -{ - if (gainDB > 0) { - return MA_INVALID_ARGS; - } - - return ma_device_set_master_volume(pDevice, ma_volume_db_to_linear(gainDB)); -} - -MA_API ma_result ma_device_get_master_volume_db(ma_device* pDevice, float* pGainDB) -{ - float factor; - ma_result result; - - if (pGainDB == NULL) { - return MA_INVALID_ARGS; - } - - result = ma_device_get_master_volume(pDevice, &factor); - if (result != MA_SUCCESS) { - *pGainDB = 0; - return result; - } - - *pGainDB = ma_volume_linear_to_db(factor); - - return MA_SUCCESS; -} - - -MA_API ma_result ma_device_handle_backend_data_callback(ma_device* pDevice, void* pOutput, const void* pInput, ma_uint32 frameCount) -{ - if (pDevice == NULL) { - return MA_INVALID_ARGS; - } - - if (pOutput == NULL && pInput == NULL) { - return MA_INVALID_ARGS; - } - - /* - There is an assert deeper in the code that checks that frameCount > 0. Since this is a public facing - API we'll need to check for that here. I've had reports that AAudio can sometimes post a frame count - of 0. - */ - if (frameCount == 0) { - return MA_INVALID_ARGS; - } - - if (pDevice->type == ma_device_type_duplex) { - if (pInput != NULL) { - ma_device__handle_duplex_callback_capture(pDevice, frameCount, pInput, &pDevice->duplexRB.rb); - } - - if (pOutput != NULL) { - ma_device__handle_duplex_callback_playback(pDevice, frameCount, pOutput, &pDevice->duplexRB.rb); - } - } else { - if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_loopback) { - if (pInput == NULL) { - return MA_INVALID_ARGS; - } - - ma_device__send_frames_to_client(pDevice, frameCount, pInput); - } - - if (pDevice->type == ma_device_type_playback) { - if (pOutput == NULL) { - return MA_INVALID_ARGS; - } - - ma_device__read_frames_from_client(pDevice, frameCount, pOutput); - } - } - - return MA_SUCCESS; -} - -MA_API ma_uint32 ma_calculate_buffer_size_in_frames_from_descriptor(const ma_device_descriptor* pDescriptor, ma_uint32 nativeSampleRate, ma_performance_profile performanceProfile) -{ - if (pDescriptor == NULL) { - return 0; - } - - /* - We must have a non-0 native sample rate, but some backends don't allow retrieval of this at the - time when the size of the buffer needs to be determined. In this case we need to just take a best - guess and move on. We'll try using the sample rate in pDescriptor first. If that's not set we'll - just fall back to MA_DEFAULT_SAMPLE_RATE. - */ - if (nativeSampleRate == 0) { - nativeSampleRate = pDescriptor->sampleRate; - } - if (nativeSampleRate == 0) { - nativeSampleRate = MA_DEFAULT_SAMPLE_RATE; - } - - MA_ASSERT(nativeSampleRate != 0); - - if (pDescriptor->periodSizeInFrames == 0) { - if (pDescriptor->periodSizeInMilliseconds == 0) { - if (performanceProfile == ma_performance_profile_low_latency) { - return ma_calculate_buffer_size_in_frames_from_milliseconds(MA_DEFAULT_PERIOD_SIZE_IN_MILLISECONDS_LOW_LATENCY, nativeSampleRate); - } else { - return ma_calculate_buffer_size_in_frames_from_milliseconds(MA_DEFAULT_PERIOD_SIZE_IN_MILLISECONDS_CONSERVATIVE, nativeSampleRate); - } - } else { - return ma_calculate_buffer_size_in_frames_from_milliseconds(pDescriptor->periodSizeInMilliseconds, nativeSampleRate); - } - } else { - return pDescriptor->periodSizeInFrames; - } -} -#endif /* MA_NO_DEVICE_IO */ - - -MA_API ma_uint32 ma_calculate_buffer_size_in_milliseconds_from_frames(ma_uint32 bufferSizeInFrames, ma_uint32 sampleRate) -{ - /* Prevent a division by zero. */ - if (sampleRate == 0) { - return 0; - } - - return (bufferSizeInFrames*1000 + (sampleRate - 1)) / sampleRate; -} - -MA_API ma_uint32 ma_calculate_buffer_size_in_frames_from_milliseconds(ma_uint32 bufferSizeInMilliseconds, ma_uint32 sampleRate) -{ - /* Prevent a division by zero. */ - if (sampleRate == 0) { - return 0; - } - - return bufferSizeInMilliseconds*sampleRate / 1000; -} - -MA_API void ma_copy_pcm_frames(void* dst, const void* src, ma_uint64 frameCount, ma_format format, ma_uint32 channels) -{ - if (dst == src) { - return; /* No-op. */ - } - - ma_copy_memory_64(dst, src, frameCount * ma_get_bytes_per_frame(format, channels)); -} - -MA_API void ma_silence_pcm_frames(void* p, ma_uint64 frameCount, ma_format format, ma_uint32 channels) -{ - if (format == ma_format_u8) { - ma_uint64 sampleCount = frameCount * channels; - ma_uint64 iSample; - for (iSample = 0; iSample < sampleCount; iSample += 1) { - ((ma_uint8*)p)[iSample] = 128; - } - } else { - ma_zero_memory_64(p, frameCount * ma_get_bytes_per_frame(format, channels)); - } -} - -MA_API void* ma_offset_pcm_frames_ptr(void* p, ma_uint64 offsetInFrames, ma_format format, ma_uint32 channels) -{ - return ma_offset_ptr(p, offsetInFrames * ma_get_bytes_per_frame(format, channels)); -} - -MA_API const void* ma_offset_pcm_frames_const_ptr(const void* p, ma_uint64 offsetInFrames, ma_format format, ma_uint32 channels) -{ - return ma_offset_ptr(p, offsetInFrames * ma_get_bytes_per_frame(format, channels)); -} - - -MA_API void ma_clip_samples_u8(ma_uint8* pDst, const ma_int16* pSrc, ma_uint64 count) -{ - ma_uint64 iSample; - - MA_ASSERT(pDst != NULL); - MA_ASSERT(pSrc != NULL); - - for (iSample = 0; iSample < count; iSample += 1) { - pDst[iSample] = ma_clip_u8(pSrc[iSample]); - } -} - -MA_API void ma_clip_samples_s16(ma_int16* pDst, const ma_int32* pSrc, ma_uint64 count) -{ - ma_uint64 iSample; - - MA_ASSERT(pDst != NULL); - MA_ASSERT(pSrc != NULL); - - for (iSample = 0; iSample < count; iSample += 1) { - pDst[iSample] = ma_clip_s16(pSrc[iSample]); - } -} - -MA_API void ma_clip_samples_s24(ma_uint8* pDst, const ma_int64* pSrc, ma_uint64 count) -{ - ma_uint64 iSample; - - MA_ASSERT(pDst != NULL); - MA_ASSERT(pSrc != NULL); - - for (iSample = 0; iSample < count; iSample += 1) { - ma_int64 s = ma_clip_s24(pSrc[iSample]); - pDst[iSample*3 + 0] = (ma_uint8)((s & 0x000000FF) >> 0); - pDst[iSample*3 + 1] = (ma_uint8)((s & 0x0000FF00) >> 8); - pDst[iSample*3 + 2] = (ma_uint8)((s & 0x00FF0000) >> 16); - } -} - -MA_API void ma_clip_samples_s32(ma_int32* pDst, const ma_int64* pSrc, ma_uint64 count) -{ - ma_uint64 iSample; - - MA_ASSERT(pDst != NULL); - MA_ASSERT(pSrc != NULL); - - for (iSample = 0; iSample < count; iSample += 1) { - pDst[iSample] = ma_clip_s32(pSrc[iSample]); - } -} - -MA_API void ma_clip_samples_f32(float* pDst, const float* pSrc, ma_uint64 count) -{ - ma_uint64 iSample; - - MA_ASSERT(pDst != NULL); - MA_ASSERT(pSrc != NULL); - - for (iSample = 0; iSample < count; iSample += 1) { - pDst[iSample] = ma_clip_f32(pSrc[iSample]); - } -} - -MA_API void ma_clip_pcm_frames(void* pDst, const void* pSrc, ma_uint64 frameCount, ma_format format, ma_uint32 channels) -{ - ma_uint64 sampleCount; - - MA_ASSERT(pDst != NULL); - MA_ASSERT(pSrc != NULL); - - sampleCount = frameCount * channels; - - switch (format) { - case ma_format_u8: ma_clip_samples_u8( (ma_uint8*)pDst, (const ma_int16*)pSrc, sampleCount); break; - case ma_format_s16: ma_clip_samples_s16((ma_int16*)pDst, (const ma_int32*)pSrc, sampleCount); break; - case ma_format_s24: ma_clip_samples_s24((ma_uint8*)pDst, (const ma_int64*)pSrc, sampleCount); break; - case ma_format_s32: ma_clip_samples_s32((ma_int32*)pDst, (const ma_int64*)pSrc, sampleCount); break; - case ma_format_f32: ma_clip_samples_f32(( float*)pDst, (const float*)pSrc, sampleCount); break; - - /* Do nothing if we don't know the format. We're including these here to silence a compiler warning about enums not being handled by the switch. */ - case ma_format_unknown: - case ma_format_count: - break; - } -} - - -MA_API void ma_copy_and_apply_volume_factor_u8(ma_uint8* pSamplesOut, const ma_uint8* pSamplesIn, ma_uint64 sampleCount, float factor) -{ - ma_uint64 iSample; - - if (pSamplesOut == NULL || pSamplesIn == NULL) { - return; - } - - for (iSample = 0; iSample < sampleCount; iSample += 1) { - pSamplesOut[iSample] = (ma_uint8)(pSamplesIn[iSample] * factor); - } -} - -MA_API void ma_copy_and_apply_volume_factor_s16(ma_int16* pSamplesOut, const ma_int16* pSamplesIn, ma_uint64 sampleCount, float factor) -{ - ma_uint64 iSample; - - if (pSamplesOut == NULL || pSamplesIn == NULL) { - return; - } - - for (iSample = 0; iSample < sampleCount; iSample += 1) { - pSamplesOut[iSample] = (ma_int16)(pSamplesIn[iSample] * factor); - } -} - -MA_API void ma_copy_and_apply_volume_factor_s24(void* pSamplesOut, const void* pSamplesIn, ma_uint64 sampleCount, float factor) -{ - ma_uint64 iSample; - ma_uint8* pSamplesOut8; - ma_uint8* pSamplesIn8; - - if (pSamplesOut == NULL || pSamplesIn == NULL) { - return; - } - - pSamplesOut8 = (ma_uint8*)pSamplesOut; - pSamplesIn8 = (ma_uint8*)pSamplesIn; - - for (iSample = 0; iSample < sampleCount; iSample += 1) { - ma_int32 sampleS32; - - sampleS32 = (ma_int32)(((ma_uint32)(pSamplesIn8[iSample*3+0]) << 8) | ((ma_uint32)(pSamplesIn8[iSample*3+1]) << 16) | ((ma_uint32)(pSamplesIn8[iSample*3+2])) << 24); - sampleS32 = (ma_int32)(sampleS32 * factor); - - pSamplesOut8[iSample*3+0] = (ma_uint8)(((ma_uint32)sampleS32 & 0x0000FF00) >> 8); - pSamplesOut8[iSample*3+1] = (ma_uint8)(((ma_uint32)sampleS32 & 0x00FF0000) >> 16); - pSamplesOut8[iSample*3+2] = (ma_uint8)(((ma_uint32)sampleS32 & 0xFF000000) >> 24); - } -} - -MA_API void ma_copy_and_apply_volume_factor_s32(ma_int32* pSamplesOut, const ma_int32* pSamplesIn, ma_uint64 sampleCount, float factor) -{ - ma_uint64 iSample; - - if (pSamplesOut == NULL || pSamplesIn == NULL) { - return; - } - - for (iSample = 0; iSample < sampleCount; iSample += 1) { - pSamplesOut[iSample] = (ma_int32)(pSamplesIn[iSample] * factor); - } -} - -MA_API void ma_copy_and_apply_volume_factor_f32(float* pSamplesOut, const float* pSamplesIn, ma_uint64 sampleCount, float factor) -{ - ma_uint64 iSample; - - if (pSamplesOut == NULL || pSamplesIn == NULL) { - return; - } - - if (factor == 1) { - if (pSamplesOut == pSamplesIn) { - /* In place. No-op. */ - } else { - /* Just a copy. */ - for (iSample = 0; iSample < sampleCount; iSample += 1) { - pSamplesOut[iSample] = pSamplesIn[iSample]; - } - } - } else { - for (iSample = 0; iSample < sampleCount; iSample += 1) { - pSamplesOut[iSample] = pSamplesIn[iSample] * factor; - } - } -} - -MA_API void ma_apply_volume_factor_u8(ma_uint8* pSamples, ma_uint64 sampleCount, float factor) -{ - ma_copy_and_apply_volume_factor_u8(pSamples, pSamples, sampleCount, factor); -} - -MA_API void ma_apply_volume_factor_s16(ma_int16* pSamples, ma_uint64 sampleCount, float factor) -{ - ma_copy_and_apply_volume_factor_s16(pSamples, pSamples, sampleCount, factor); -} - -MA_API void ma_apply_volume_factor_s24(void* pSamples, ma_uint64 sampleCount, float factor) -{ - ma_copy_and_apply_volume_factor_s24(pSamples, pSamples, sampleCount, factor); -} - -MA_API void ma_apply_volume_factor_s32(ma_int32* pSamples, ma_uint64 sampleCount, float factor) -{ - ma_copy_and_apply_volume_factor_s32(pSamples, pSamples, sampleCount, factor); -} - -MA_API void ma_apply_volume_factor_f32(float* pSamples, ma_uint64 sampleCount, float factor) -{ - ma_copy_and_apply_volume_factor_f32(pSamples, pSamples, sampleCount, factor); -} - -MA_API void ma_copy_and_apply_volume_factor_pcm_frames_u8(ma_uint8* pFramesOut, const ma_uint8* pFramesIn, ma_uint64 frameCount, ma_uint32 channels, float factor) -{ - ma_copy_and_apply_volume_factor_u8(pFramesOut, pFramesIn, frameCount*channels, factor); -} - -MA_API void ma_copy_and_apply_volume_factor_pcm_frames_s16(ma_int16* pFramesOut, const ma_int16* pFramesIn, ma_uint64 frameCount, ma_uint32 channels, float factor) -{ - ma_copy_and_apply_volume_factor_s16(pFramesOut, pFramesIn, frameCount*channels, factor); -} - -MA_API void ma_copy_and_apply_volume_factor_pcm_frames_s24(void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount, ma_uint32 channels, float factor) -{ - ma_copy_and_apply_volume_factor_s24(pFramesOut, pFramesIn, frameCount*channels, factor); -} - -MA_API void ma_copy_and_apply_volume_factor_pcm_frames_s32(ma_int32* pFramesOut, const ma_int32* pFramesIn, ma_uint64 frameCount, ma_uint32 channels, float factor) -{ - ma_copy_and_apply_volume_factor_s32(pFramesOut, pFramesIn, frameCount*channels, factor); -} - -MA_API void ma_copy_and_apply_volume_factor_pcm_frames_f32(float* pFramesOut, const float* pFramesIn, ma_uint64 frameCount, ma_uint32 channels, float factor) -{ - ma_copy_and_apply_volume_factor_f32(pFramesOut, pFramesIn, frameCount*channels, factor); -} - -MA_API void ma_copy_and_apply_volume_factor_pcm_frames(void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount, ma_format format, ma_uint32 channels, float factor) -{ - switch (format) - { - case ma_format_u8: ma_copy_and_apply_volume_factor_pcm_frames_u8 ((ma_uint8*)pFramesOut, (const ma_uint8*)pFramesIn, frameCount, channels, factor); return; - case ma_format_s16: ma_copy_and_apply_volume_factor_pcm_frames_s16((ma_int16*)pFramesOut, (const ma_int16*)pFramesIn, frameCount, channels, factor); return; - case ma_format_s24: ma_copy_and_apply_volume_factor_pcm_frames_s24( pFramesOut, pFramesIn, frameCount, channels, factor); return; - case ma_format_s32: ma_copy_and_apply_volume_factor_pcm_frames_s32((ma_int32*)pFramesOut, (const ma_int32*)pFramesIn, frameCount, channels, factor); return; - case ma_format_f32: ma_copy_and_apply_volume_factor_pcm_frames_f32( (float*)pFramesOut, (const float*)pFramesIn, frameCount, channels, factor); return; - default: return; /* Do nothing. */ - } -} - -MA_API void ma_apply_volume_factor_pcm_frames_u8(ma_uint8* pFrames, ma_uint64 frameCount, ma_uint32 channels, float factor) -{ - ma_copy_and_apply_volume_factor_pcm_frames_u8(pFrames, pFrames, frameCount, channels, factor); -} - -MA_API void ma_apply_volume_factor_pcm_frames_s16(ma_int16* pFrames, ma_uint64 frameCount, ma_uint32 channels, float factor) -{ - ma_copy_and_apply_volume_factor_pcm_frames_s16(pFrames, pFrames, frameCount, channels, factor); -} - -MA_API void ma_apply_volume_factor_pcm_frames_s24(void* pFrames, ma_uint64 frameCount, ma_uint32 channels, float factor) -{ - ma_copy_and_apply_volume_factor_pcm_frames_s24(pFrames, pFrames, frameCount, channels, factor); -} - -MA_API void ma_apply_volume_factor_pcm_frames_s32(ma_int32* pFrames, ma_uint64 frameCount, ma_uint32 channels, float factor) -{ - ma_copy_and_apply_volume_factor_pcm_frames_s32(pFrames, pFrames, frameCount, channels, factor); -} - -MA_API void ma_apply_volume_factor_pcm_frames_f32(float* pFrames, ma_uint64 frameCount, ma_uint32 channels, float factor) -{ - ma_copy_and_apply_volume_factor_pcm_frames_f32(pFrames, pFrames, frameCount, channels, factor); -} - -MA_API void ma_apply_volume_factor_pcm_frames(void* pFramesOut, ma_uint64 frameCount, ma_format format, ma_uint32 channels, float factor) -{ - ma_copy_and_apply_volume_factor_pcm_frames(pFramesOut, pFramesOut, frameCount, format, channels, factor); -} - - -MA_API void ma_copy_and_apply_volume_factor_per_channel_f32(float* pFramesOut, const float* pFramesIn, ma_uint64 frameCount, ma_uint32 channels, float* pChannelGains) -{ - ma_uint64 iFrame; - - if (channels == 2) { - /* TODO: Do an optimized implementation for stereo and mono. Can do a SIMD optimized implementation as well. */ - } - - for (iFrame = 0; iFrame < frameCount; iFrame += 1) { - ma_uint32 iChannel; - for (iChannel = 0; iChannel < channels; iChannel += 1) { - pFramesOut[iFrame * channels + iChannel] = pFramesIn[iFrame * channels + iChannel] * pChannelGains[iChannel]; - } - } -} - - - -static MA_INLINE ma_int16 ma_apply_volume_unclipped_u8(ma_int16 x, ma_int16 volume) -{ - return (ma_int16)(((ma_int32)x * (ma_int32)volume) >> 8); -} - -static MA_INLINE ma_int32 ma_apply_volume_unclipped_s16(ma_int32 x, ma_int16 volume) -{ - return (ma_int32)((x * volume) >> 8); -} - -static MA_INLINE ma_int64 ma_apply_volume_unclipped_s24(ma_int64 x, ma_int16 volume) -{ - return (ma_int64)((x * volume) >> 8); -} - -static MA_INLINE ma_int64 ma_apply_volume_unclipped_s32(ma_int64 x, ma_int16 volume) -{ - return (ma_int64)((x * volume) >> 8); -} - -static MA_INLINE float ma_apply_volume_unclipped_f32(float x, float volume) -{ - return x * volume; -} - - -MA_API void ma_copy_and_apply_volume_and_clip_samples_u8(ma_uint8* pDst, const ma_int16* pSrc, ma_uint64 count, float volume) -{ - ma_uint64 iSample; - ma_int16 volumeFixed; - - MA_ASSERT(pDst != NULL); - MA_ASSERT(pSrc != NULL); - - volumeFixed = ma_float_to_fixed_16(volume); - - for (iSample = 0; iSample < count; iSample += 1) { - pDst[iSample] = ma_clip_u8(ma_apply_volume_unclipped_u8(pSrc[iSample], volumeFixed)); - } -} - -MA_API void ma_copy_and_apply_volume_and_clip_samples_s16(ma_int16* pDst, const ma_int32* pSrc, ma_uint64 count, float volume) -{ - ma_uint64 iSample; - ma_int16 volumeFixed; - - MA_ASSERT(pDst != NULL); - MA_ASSERT(pSrc != NULL); - - volumeFixed = ma_float_to_fixed_16(volume); - - for (iSample = 0; iSample < count; iSample += 1) { - pDst[iSample] = ma_clip_s16(ma_apply_volume_unclipped_s16(pSrc[iSample], volumeFixed)); - } -} - -MA_API void ma_copy_and_apply_volume_and_clip_samples_s24(ma_uint8* pDst, const ma_int64* pSrc, ma_uint64 count, float volume) -{ - ma_uint64 iSample; - ma_int16 volumeFixed; - - MA_ASSERT(pDst != NULL); - MA_ASSERT(pSrc != NULL); - - volumeFixed = ma_float_to_fixed_16(volume); - - for (iSample = 0; iSample < count; iSample += 1) { - ma_int64 s = ma_clip_s24(ma_apply_volume_unclipped_s24(pSrc[iSample], volumeFixed)); - pDst[iSample*3 + 0] = (ma_uint8)((s & 0x000000FF) >> 0); - pDst[iSample*3 + 1] = (ma_uint8)((s & 0x0000FF00) >> 8); - pDst[iSample*3 + 2] = (ma_uint8)((s & 0x00FF0000) >> 16); - } -} - -MA_API void ma_copy_and_apply_volume_and_clip_samples_s32(ma_int32* pDst, const ma_int64* pSrc, ma_uint64 count, float volume) -{ - ma_uint64 iSample; - ma_int16 volumeFixed; - - MA_ASSERT(pDst != NULL); - MA_ASSERT(pSrc != NULL); - - volumeFixed = ma_float_to_fixed_16(volume); - - for (iSample = 0; iSample < count; iSample += 1) { - pDst[iSample] = ma_clip_s32(ma_apply_volume_unclipped_s32(pSrc[iSample], volumeFixed)); - } -} - -MA_API void ma_copy_and_apply_volume_and_clip_samples_f32(float* pDst, const float* pSrc, ma_uint64 count, float volume) -{ - ma_uint64 iSample; - - MA_ASSERT(pDst != NULL); - MA_ASSERT(pSrc != NULL); - - /* For the f32 case we need to make sure this supports in-place processing where the input and output buffers are the same. */ - - for (iSample = 0; iSample < count; iSample += 1) { - pDst[iSample] = ma_clip_f32(ma_apply_volume_unclipped_f32(pSrc[iSample], volume)); - } -} - -MA_API void ma_copy_and_apply_volume_and_clip_pcm_frames(void* pDst, const void* pSrc, ma_uint64 frameCount, ma_format format, ma_uint32 channels, float volume) -{ - MA_ASSERT(pDst != NULL); - MA_ASSERT(pSrc != NULL); - - if (volume == 1) { - ma_clip_pcm_frames(pDst, pSrc, frameCount, format, channels); /* Optimized case for volume = 1. */ - } else if (volume == 0) { - ma_silence_pcm_frames(pDst, frameCount, format, channels); /* Optimized case for volume = 0. */ - } else { - ma_uint64 sampleCount = frameCount * channels; - - switch (format) { - case ma_format_u8: ma_copy_and_apply_volume_and_clip_samples_u8( (ma_uint8*)pDst, (const ma_int16*)pSrc, sampleCount, volume); break; - case ma_format_s16: ma_copy_and_apply_volume_and_clip_samples_s16((ma_int16*)pDst, (const ma_int32*)pSrc, sampleCount, volume); break; - case ma_format_s24: ma_copy_and_apply_volume_and_clip_samples_s24((ma_uint8*)pDst, (const ma_int64*)pSrc, sampleCount, volume); break; - case ma_format_s32: ma_copy_and_apply_volume_and_clip_samples_s32((ma_int32*)pDst, (const ma_int64*)pSrc, sampleCount, volume); break; - case ma_format_f32: ma_copy_and_apply_volume_and_clip_samples_f32(( float*)pDst, (const float*)pSrc, sampleCount, volume); break; - - /* Do nothing if we don't know the format. We're including these here to silence a compiler warning about enums not being handled by the switch. */ - case ma_format_unknown: - case ma_format_count: - break; - } - } -} - - - -MA_API float ma_volume_linear_to_db(float factor) -{ - return 20*ma_log10f(factor); -} - -MA_API float ma_volume_db_to_linear(float gain) -{ - return ma_powf(10, gain/20.0f); -} - - -MA_API ma_result ma_mix_pcm_frames_f32(float* pDst, const float* pSrc, ma_uint64 frameCount, ma_uint32 channels, float volume) -{ - ma_uint64 iSample; - ma_uint64 sampleCount; - - if (pDst == NULL || pSrc == NULL || channels == 0) { - return MA_INVALID_ARGS; - } - - if (volume == 0) { - return MA_SUCCESS; /* No changes if the volume is 0. */ - } - - sampleCount = frameCount * channels; - - if (volume == 1) { - for (iSample = 0; iSample < sampleCount; iSample += 1) { - pDst[iSample] += pSrc[iSample]; - } - } else { - for (iSample = 0; iSample < sampleCount; iSample += 1) { - pDst[iSample] += ma_apply_volume_unclipped_f32(pSrc[iSample], volume); - } - } - - return MA_SUCCESS; -} - - - -/************************************************************************************************************************************************************** - -Format Conversion - -**************************************************************************************************************************************************************/ - -static MA_INLINE ma_int16 ma_pcm_sample_f32_to_s16(float x) -{ - return (ma_int16)(x * 32767.0f); -} - -static MA_INLINE ma_int16 ma_pcm_sample_u8_to_s16_no_scale(ma_uint8 x) -{ - return (ma_int16)((ma_int16)x - 128); -} - -static MA_INLINE ma_int64 ma_pcm_sample_s24_to_s32_no_scale(const ma_uint8* x) -{ - return (ma_int64)(((ma_uint64)x[0] << 40) | ((ma_uint64)x[1] << 48) | ((ma_uint64)x[2] << 56)) >> 40; /* Make sure the sign bits are maintained. */ -} - -static MA_INLINE void ma_pcm_sample_s32_to_s24_no_scale(ma_int64 x, ma_uint8* s24) -{ - s24[0] = (ma_uint8)((x & 0x000000FF) >> 0); - s24[1] = (ma_uint8)((x & 0x0000FF00) >> 8); - s24[2] = (ma_uint8)((x & 0x00FF0000) >> 16); -} - - -/* u8 */ -MA_API void ma_pcm_u8_to_u8(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) -{ - (void)ditherMode; - ma_copy_memory_64(dst, src, count * sizeof(ma_uint8)); -} - - -static MA_INLINE void ma_pcm_u8_to_s16__reference(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) -{ - ma_int16* dst_s16 = (ma_int16*)dst; - const ma_uint8* src_u8 = (const ma_uint8*)src; - - ma_uint64 i; - for (i = 0; i < count; i += 1) { - ma_int16 x = src_u8[i]; - x = (ma_int16)(x - 128); - x = (ma_int16)(x << 8); - dst_s16[i] = x; - } - - (void)ditherMode; -} - -static MA_INLINE void ma_pcm_u8_to_s16__optimized(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) -{ - ma_pcm_u8_to_s16__reference(dst, src, count, ditherMode); -} - -#if defined(MA_SUPPORT_SSE2) -static MA_INLINE void ma_pcm_u8_to_s16__sse2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) -{ - ma_pcm_u8_to_s16__optimized(dst, src, count, ditherMode); -} -#endif -#if defined(MA_SUPPORT_NEON) -static MA_INLINE void ma_pcm_u8_to_s16__neon(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) -{ - ma_pcm_u8_to_s16__optimized(dst, src, count, ditherMode); -} -#endif - -MA_API void ma_pcm_u8_to_s16(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) -{ -#ifdef MA_USE_REFERENCE_CONVERSION_APIS - ma_pcm_u8_to_s16__reference(dst, src, count, ditherMode); -#else - # if defined(MA_SUPPORT_SSE2) - if (ma_has_sse2()) { - ma_pcm_u8_to_s16__sse2(dst, src, count, ditherMode); - } else - #elif defined(MA_SUPPORT_NEON) - if (ma_has_neon()) { - ma_pcm_u8_to_s16__neon(dst, src, count, ditherMode); - } else - #endif - { - ma_pcm_u8_to_s16__optimized(dst, src, count, ditherMode); - } -#endif -} - - -static MA_INLINE void ma_pcm_u8_to_s24__reference(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) -{ - ma_uint8* dst_s24 = (ma_uint8*)dst; - const ma_uint8* src_u8 = (const ma_uint8*)src; - - ma_uint64 i; - for (i = 0; i < count; i += 1) { - ma_int16 x = src_u8[i]; - x = (ma_int16)(x - 128); - - dst_s24[i*3+0] = 0; - dst_s24[i*3+1] = 0; - dst_s24[i*3+2] = (ma_uint8)((ma_int8)x); - } - - (void)ditherMode; -} - -static MA_INLINE void ma_pcm_u8_to_s24__optimized(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) -{ - ma_pcm_u8_to_s24__reference(dst, src, count, ditherMode); -} - -#if defined(MA_SUPPORT_SSE2) -static MA_INLINE void ma_pcm_u8_to_s24__sse2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) -{ - ma_pcm_u8_to_s24__optimized(dst, src, count, ditherMode); -} -#endif -#if defined(MA_SUPPORT_NEON) -static MA_INLINE void ma_pcm_u8_to_s24__neon(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) -{ - ma_pcm_u8_to_s24__optimized(dst, src, count, ditherMode); -} -#endif - -MA_API void ma_pcm_u8_to_s24(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) -{ -#ifdef MA_USE_REFERENCE_CONVERSION_APIS - ma_pcm_u8_to_s24__reference(dst, src, count, ditherMode); -#else - # if defined(MA_SUPPORT_SSE2) - if (ma_has_sse2()) { - ma_pcm_u8_to_s24__sse2(dst, src, count, ditherMode); - } else - #elif defined(MA_SUPPORT_NEON) - if (ma_has_neon()) { - ma_pcm_u8_to_s24__neon(dst, src, count, ditherMode); - } else - #endif - { - ma_pcm_u8_to_s24__optimized(dst, src, count, ditherMode); - } -#endif -} - - -static MA_INLINE void ma_pcm_u8_to_s32__reference(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) -{ - ma_int32* dst_s32 = (ma_int32*)dst; - const ma_uint8* src_u8 = (const ma_uint8*)src; - - ma_uint64 i; - for (i = 0; i < count; i += 1) { - ma_int32 x = src_u8[i]; - x = x - 128; - x = x << 24; - dst_s32[i] = x; - } - - (void)ditherMode; -} - -static MA_INLINE void ma_pcm_u8_to_s32__optimized(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) -{ - ma_pcm_u8_to_s32__reference(dst, src, count, ditherMode); -} - -#if defined(MA_SUPPORT_SSE2) -static MA_INLINE void ma_pcm_u8_to_s32__sse2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) -{ - ma_pcm_u8_to_s32__optimized(dst, src, count, ditherMode); -} -#endif -#if defined(MA_SUPPORT_NEON) -static MA_INLINE void ma_pcm_u8_to_s32__neon(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) -{ - ma_pcm_u8_to_s32__optimized(dst, src, count, ditherMode); -} -#endif - -MA_API void ma_pcm_u8_to_s32(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) -{ -#ifdef MA_USE_REFERENCE_CONVERSION_APIS - ma_pcm_u8_to_s32__reference(dst, src, count, ditherMode); -#else - # if defined(MA_SUPPORT_SSE2) - if (ma_has_sse2()) { - ma_pcm_u8_to_s32__sse2(dst, src, count, ditherMode); - } else - #elif defined(MA_SUPPORT_NEON) - if (ma_has_neon()) { - ma_pcm_u8_to_s32__neon(dst, src, count, ditherMode); - } else - #endif - { - ma_pcm_u8_to_s32__optimized(dst, src, count, ditherMode); - } -#endif -} - - -static MA_INLINE void ma_pcm_u8_to_f32__reference(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) -{ - float* dst_f32 = (float*)dst; - const ma_uint8* src_u8 = (const ma_uint8*)src; - - ma_uint64 i; - for (i = 0; i < count; i += 1) { - float x = (float)src_u8[i]; - x = x * 0.00784313725490196078f; /* 0..255 to 0..2 */ - x = x - 1; /* 0..2 to -1..1 */ - - dst_f32[i] = x; - } - - (void)ditherMode; -} - -static MA_INLINE void ma_pcm_u8_to_f32__optimized(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) -{ - ma_pcm_u8_to_f32__reference(dst, src, count, ditherMode); -} - -#if defined(MA_SUPPORT_SSE2) -static MA_INLINE void ma_pcm_u8_to_f32__sse2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) -{ - ma_pcm_u8_to_f32__optimized(dst, src, count, ditherMode); -} -#endif -#if defined(MA_SUPPORT_NEON) -static MA_INLINE void ma_pcm_u8_to_f32__neon(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) -{ - ma_pcm_u8_to_f32__optimized(dst, src, count, ditherMode); -} -#endif - -MA_API void ma_pcm_u8_to_f32(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) -{ -#ifdef MA_USE_REFERENCE_CONVERSION_APIS - ma_pcm_u8_to_f32__reference(dst, src, count, ditherMode); -#else - # if defined(MA_SUPPORT_SSE2) - if (ma_has_sse2()) { - ma_pcm_u8_to_f32__sse2(dst, src, count, ditherMode); - } else - #elif defined(MA_SUPPORT_NEON) - if (ma_has_neon()) { - ma_pcm_u8_to_f32__neon(dst, src, count, ditherMode); - } else - #endif - { - ma_pcm_u8_to_f32__optimized(dst, src, count, ditherMode); - } -#endif -} - - -#ifdef MA_USE_REFERENCE_CONVERSION_APIS -static MA_INLINE void ma_pcm_interleave_u8__reference(void* dst, const void** src, ma_uint64 frameCount, ma_uint32 channels) -{ - ma_uint8* dst_u8 = (ma_uint8*)dst; - const ma_uint8** src_u8 = (const ma_uint8**)src; - - ma_uint64 iFrame; - for (iFrame = 0; iFrame < frameCount; iFrame += 1) { - ma_uint32 iChannel; - for (iChannel = 0; iChannel < channels; iChannel += 1) { - dst_u8[iFrame*channels + iChannel] = src_u8[iChannel][iFrame]; - } - } -} -#else -static MA_INLINE void ma_pcm_interleave_u8__optimized(void* dst, const void** src, ma_uint64 frameCount, ma_uint32 channels) -{ - ma_uint8* dst_u8 = (ma_uint8*)dst; - const ma_uint8** src_u8 = (const ma_uint8**)src; - - if (channels == 1) { - ma_copy_memory_64(dst, src[0], frameCount * sizeof(ma_uint8)); - } else if (channels == 2) { - ma_uint64 iFrame; - for (iFrame = 0; iFrame < frameCount; iFrame += 1) { - dst_u8[iFrame*2 + 0] = src_u8[0][iFrame]; - dst_u8[iFrame*2 + 1] = src_u8[1][iFrame]; - } - } else { - ma_uint64 iFrame; - for (iFrame = 0; iFrame < frameCount; iFrame += 1) { - ma_uint32 iChannel; - for (iChannel = 0; iChannel < channels; iChannel += 1) { - dst_u8[iFrame*channels + iChannel] = src_u8[iChannel][iFrame]; - } - } - } -} -#endif - -MA_API void ma_pcm_interleave_u8(void* dst, const void** src, ma_uint64 frameCount, ma_uint32 channels) -{ -#ifdef MA_USE_REFERENCE_CONVERSION_APIS - ma_pcm_interleave_u8__reference(dst, src, frameCount, channels); -#else - ma_pcm_interleave_u8__optimized(dst, src, frameCount, channels); -#endif -} - - -static MA_INLINE void ma_pcm_deinterleave_u8__reference(void** dst, const void* src, ma_uint64 frameCount, ma_uint32 channels) -{ - ma_uint8** dst_u8 = (ma_uint8**)dst; - const ma_uint8* src_u8 = (const ma_uint8*)src; - - ma_uint64 iFrame; - for (iFrame = 0; iFrame < frameCount; iFrame += 1) { - ma_uint32 iChannel; - for (iChannel = 0; iChannel < channels; iChannel += 1) { - dst_u8[iChannel][iFrame] = src_u8[iFrame*channels + iChannel]; - } - } -} - -static MA_INLINE void ma_pcm_deinterleave_u8__optimized(void** dst, const void* src, ma_uint64 frameCount, ma_uint32 channels) -{ - ma_pcm_deinterleave_u8__reference(dst, src, frameCount, channels); -} - -MA_API void ma_pcm_deinterleave_u8(void** dst, const void* src, ma_uint64 frameCount, ma_uint32 channels) -{ -#ifdef MA_USE_REFERENCE_CONVERSION_APIS - ma_pcm_deinterleave_u8__reference(dst, src, frameCount, channels); -#else - ma_pcm_deinterleave_u8__optimized(dst, src, frameCount, channels); -#endif -} - - -/* s16 */ -static MA_INLINE void ma_pcm_s16_to_u8__reference(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) -{ - ma_uint8* dst_u8 = (ma_uint8*)dst; - const ma_int16* src_s16 = (const ma_int16*)src; - - if (ditherMode == ma_dither_mode_none) { - ma_uint64 i; - for (i = 0; i < count; i += 1) { - ma_int16 x = src_s16[i]; - x = (ma_int16)(x >> 8); - x = (ma_int16)(x + 128); - dst_u8[i] = (ma_uint8)x; - } - } else { - ma_uint64 i; - for (i = 0; i < count; i += 1) { - ma_int16 x = src_s16[i]; - - /* Dither. Don't overflow. */ - ma_int32 dither = ma_dither_s32(ditherMode, -0x80, 0x7F); - if ((x + dither) <= 0x7FFF) { - x = (ma_int16)(x + dither); - } else { - x = 0x7FFF; - } - - x = (ma_int16)(x >> 8); - x = (ma_int16)(x + 128); - dst_u8[i] = (ma_uint8)x; - } - } -} - -static MA_INLINE void ma_pcm_s16_to_u8__optimized(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) -{ - ma_pcm_s16_to_u8__reference(dst, src, count, ditherMode); -} - -#if defined(MA_SUPPORT_SSE2) -static MA_INLINE void ma_pcm_s16_to_u8__sse2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) -{ - ma_pcm_s16_to_u8__optimized(dst, src, count, ditherMode); -} -#endif -#if defined(MA_SUPPORT_NEON) -static MA_INLINE void ma_pcm_s16_to_u8__neon(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) -{ - ma_pcm_s16_to_u8__optimized(dst, src, count, ditherMode); -} -#endif - -MA_API void ma_pcm_s16_to_u8(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) -{ -#ifdef MA_USE_REFERENCE_CONVERSION_APIS - ma_pcm_s16_to_u8__reference(dst, src, count, ditherMode); -#else - # if defined(MA_SUPPORT_SSE2) - if (ma_has_sse2()) { - ma_pcm_s16_to_u8__sse2(dst, src, count, ditherMode); - } else - #elif defined(MA_SUPPORT_NEON) - if (ma_has_neon()) { - ma_pcm_s16_to_u8__neon(dst, src, count, ditherMode); - } else - #endif - { - ma_pcm_s16_to_u8__optimized(dst, src, count, ditherMode); - } -#endif -} - - -MA_API void ma_pcm_s16_to_s16(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) -{ - (void)ditherMode; - ma_copy_memory_64(dst, src, count * sizeof(ma_int16)); -} - - -static MA_INLINE void ma_pcm_s16_to_s24__reference(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) -{ - ma_uint8* dst_s24 = (ma_uint8*)dst; - const ma_int16* src_s16 = (const ma_int16*)src; - - ma_uint64 i; - for (i = 0; i < count; i += 1) { - dst_s24[i*3+0] = 0; - dst_s24[i*3+1] = (ma_uint8)(src_s16[i] & 0xFF); - dst_s24[i*3+2] = (ma_uint8)(src_s16[i] >> 8); - } - - (void)ditherMode; -} - -static MA_INLINE void ma_pcm_s16_to_s24__optimized(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) -{ - ma_pcm_s16_to_s24__reference(dst, src, count, ditherMode); -} - -#if defined(MA_SUPPORT_SSE2) -static MA_INLINE void ma_pcm_s16_to_s24__sse2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) -{ - ma_pcm_s16_to_s24__optimized(dst, src, count, ditherMode); -} -#endif -#if defined(MA_SUPPORT_NEON) -static MA_INLINE void ma_pcm_s16_to_s24__neon(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) -{ - ma_pcm_s16_to_s24__optimized(dst, src, count, ditherMode); -} -#endif - -MA_API void ma_pcm_s16_to_s24(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) -{ -#ifdef MA_USE_REFERENCE_CONVERSION_APIS - ma_pcm_s16_to_s24__reference(dst, src, count, ditherMode); -#else - # if defined(MA_SUPPORT_SSE2) - if (ma_has_sse2()) { - ma_pcm_s16_to_s24__sse2(dst, src, count, ditherMode); - } else - #elif defined(MA_SUPPORT_NEON) - if (ma_has_neon()) { - ma_pcm_s16_to_s24__neon(dst, src, count, ditherMode); - } else - #endif - { - ma_pcm_s16_to_s24__optimized(dst, src, count, ditherMode); - } -#endif -} - - -static MA_INLINE void ma_pcm_s16_to_s32__reference(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) -{ - ma_int32* dst_s32 = (ma_int32*)dst; - const ma_int16* src_s16 = (const ma_int16*)src; - - ma_uint64 i; - for (i = 0; i < count; i += 1) { - dst_s32[i] = src_s16[i] << 16; - } - - (void)ditherMode; -} - -static MA_INLINE void ma_pcm_s16_to_s32__optimized(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) -{ - ma_pcm_s16_to_s32__reference(dst, src, count, ditherMode); -} - -#if defined(MA_SUPPORT_SSE2) -static MA_INLINE void ma_pcm_s16_to_s32__sse2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) -{ - ma_pcm_s16_to_s32__optimized(dst, src, count, ditherMode); -} -#endif -#if defined(MA_SUPPORT_NEON) -static MA_INLINE void ma_pcm_s16_to_s32__neon(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) -{ - ma_pcm_s16_to_s32__optimized(dst, src, count, ditherMode); -} -#endif - -MA_API void ma_pcm_s16_to_s32(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) -{ -#ifdef MA_USE_REFERENCE_CONVERSION_APIS - ma_pcm_s16_to_s32__reference(dst, src, count, ditherMode); -#else - # if defined(MA_SUPPORT_SSE2) - if (ma_has_sse2()) { - ma_pcm_s16_to_s32__sse2(dst, src, count, ditherMode); - } else - #elif defined(MA_SUPPORT_NEON) - if (ma_has_neon()) { - ma_pcm_s16_to_s32__neon(dst, src, count, ditherMode); - } else - #endif - { - ma_pcm_s16_to_s32__optimized(dst, src, count, ditherMode); - } -#endif -} - - -static MA_INLINE void ma_pcm_s16_to_f32__reference(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) -{ - float* dst_f32 = (float*)dst; - const ma_int16* src_s16 = (const ma_int16*)src; - - ma_uint64 i; - for (i = 0; i < count; i += 1) { - float x = (float)src_s16[i]; - -#if 0 - /* The accurate way. */ - x = x + 32768.0f; /* -32768..32767 to 0..65535 */ - x = x * 0.00003051804379339284f; /* 0..65535 to 0..2 */ - x = x - 1; /* 0..2 to -1..1 */ -#else - /* The fast way. */ - x = x * 0.000030517578125f; /* -32768..32767 to -1..0.999969482421875 */ -#endif - - dst_f32[i] = x; - } - - (void)ditherMode; -} - -static MA_INLINE void ma_pcm_s16_to_f32__optimized(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) -{ - ma_pcm_s16_to_f32__reference(dst, src, count, ditherMode); -} - -#if defined(MA_SUPPORT_SSE2) -static MA_INLINE void ma_pcm_s16_to_f32__sse2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) -{ - ma_pcm_s16_to_f32__optimized(dst, src, count, ditherMode); -} -#endif -#if defined(MA_SUPPORT_NEON) -static MA_INLINE void ma_pcm_s16_to_f32__neon(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) -{ - ma_pcm_s16_to_f32__optimized(dst, src, count, ditherMode); -} -#endif - -MA_API void ma_pcm_s16_to_f32(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) -{ -#ifdef MA_USE_REFERENCE_CONVERSION_APIS - ma_pcm_s16_to_f32__reference(dst, src, count, ditherMode); -#else - # if defined(MA_SUPPORT_SSE2) - if (ma_has_sse2()) { - ma_pcm_s16_to_f32__sse2(dst, src, count, ditherMode); - } else - #elif defined(MA_SUPPORT_NEON) - if (ma_has_neon()) { - ma_pcm_s16_to_f32__neon(dst, src, count, ditherMode); - } else - #endif - { - ma_pcm_s16_to_f32__optimized(dst, src, count, ditherMode); - } -#endif -} - - -static MA_INLINE void ma_pcm_interleave_s16__reference(void* dst, const void** src, ma_uint64 frameCount, ma_uint32 channels) -{ - ma_int16* dst_s16 = (ma_int16*)dst; - const ma_int16** src_s16 = (const ma_int16**)src; - - ma_uint64 iFrame; - for (iFrame = 0; iFrame < frameCount; iFrame += 1) { - ma_uint32 iChannel; - for (iChannel = 0; iChannel < channels; iChannel += 1) { - dst_s16[iFrame*channels + iChannel] = src_s16[iChannel][iFrame]; - } - } -} - -static MA_INLINE void ma_pcm_interleave_s16__optimized(void* dst, const void** src, ma_uint64 frameCount, ma_uint32 channels) -{ - ma_pcm_interleave_s16__reference(dst, src, frameCount, channels); -} - -MA_API void ma_pcm_interleave_s16(void* dst, const void** src, ma_uint64 frameCount, ma_uint32 channels) -{ -#ifdef MA_USE_REFERENCE_CONVERSION_APIS - ma_pcm_interleave_s16__reference(dst, src, frameCount, channels); -#else - ma_pcm_interleave_s16__optimized(dst, src, frameCount, channels); -#endif -} - - -static MA_INLINE void ma_pcm_deinterleave_s16__reference(void** dst, const void* src, ma_uint64 frameCount, ma_uint32 channels) -{ - ma_int16** dst_s16 = (ma_int16**)dst; - const ma_int16* src_s16 = (const ma_int16*)src; - - ma_uint64 iFrame; - for (iFrame = 0; iFrame < frameCount; iFrame += 1) { - ma_uint32 iChannel; - for (iChannel = 0; iChannel < channels; iChannel += 1) { - dst_s16[iChannel][iFrame] = src_s16[iFrame*channels + iChannel]; - } - } -} - -static MA_INLINE void ma_pcm_deinterleave_s16__optimized(void** dst, const void* src, ma_uint64 frameCount, ma_uint32 channels) -{ - ma_pcm_deinterleave_s16__reference(dst, src, frameCount, channels); -} - -MA_API void ma_pcm_deinterleave_s16(void** dst, const void* src, ma_uint64 frameCount, ma_uint32 channels) -{ -#ifdef MA_USE_REFERENCE_CONVERSION_APIS - ma_pcm_deinterleave_s16__reference(dst, src, frameCount, channels); -#else - ma_pcm_deinterleave_s16__optimized(dst, src, frameCount, channels); -#endif -} - - -/* s24 */ -static MA_INLINE void ma_pcm_s24_to_u8__reference(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) -{ - ma_uint8* dst_u8 = (ma_uint8*)dst; - const ma_uint8* src_s24 = (const ma_uint8*)src; - - if (ditherMode == ma_dither_mode_none) { - ma_uint64 i; - for (i = 0; i < count; i += 1) { - dst_u8[i] = (ma_uint8)((ma_int8)src_s24[i*3 + 2] + 128); - } - } else { - ma_uint64 i; - for (i = 0; i < count; i += 1) { - ma_int32 x = (ma_int32)(((ma_uint32)(src_s24[i*3+0]) << 8) | ((ma_uint32)(src_s24[i*3+1]) << 16) | ((ma_uint32)(src_s24[i*3+2])) << 24); - - /* Dither. Don't overflow. */ - ma_int32 dither = ma_dither_s32(ditherMode, -0x800000, 0x7FFFFF); - if ((ma_int64)x + dither <= 0x7FFFFFFF) { - x = x + dither; - } else { - x = 0x7FFFFFFF; - } - - x = x >> 24; - x = x + 128; - dst_u8[i] = (ma_uint8)x; - } - } -} - -static MA_INLINE void ma_pcm_s24_to_u8__optimized(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) -{ - ma_pcm_s24_to_u8__reference(dst, src, count, ditherMode); -} - -#if defined(MA_SUPPORT_SSE2) -static MA_INLINE void ma_pcm_s24_to_u8__sse2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) -{ - ma_pcm_s24_to_u8__optimized(dst, src, count, ditherMode); -} -#endif -#if defined(MA_SUPPORT_NEON) -static MA_INLINE void ma_pcm_s24_to_u8__neon(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) -{ - ma_pcm_s24_to_u8__optimized(dst, src, count, ditherMode); -} -#endif - -MA_API void ma_pcm_s24_to_u8(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) -{ -#ifdef MA_USE_REFERENCE_CONVERSION_APIS - ma_pcm_s24_to_u8__reference(dst, src, count, ditherMode); -#else - # if defined(MA_SUPPORT_SSE2) - if (ma_has_sse2()) { - ma_pcm_s24_to_u8__sse2(dst, src, count, ditherMode); - } else - #elif defined(MA_SUPPORT_NEON) - if (ma_has_neon()) { - ma_pcm_s24_to_u8__neon(dst, src, count, ditherMode); - } else - #endif - { - ma_pcm_s24_to_u8__optimized(dst, src, count, ditherMode); - } -#endif -} - - -static MA_INLINE void ma_pcm_s24_to_s16__reference(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) -{ - ma_int16* dst_s16 = (ma_int16*)dst; - const ma_uint8* src_s24 = (const ma_uint8*)src; - - if (ditherMode == ma_dither_mode_none) { - ma_uint64 i; - for (i = 0; i < count; i += 1) { - ma_uint16 dst_lo = ((ma_uint16)src_s24[i*3 + 1]); - ma_uint16 dst_hi = (ma_uint16)((ma_uint16)src_s24[i*3 + 2] << 8); - dst_s16[i] = (ma_int16)(dst_lo | dst_hi); - } - } else { - ma_uint64 i; - for (i = 0; i < count; i += 1) { - ma_int32 x = (ma_int32)(((ma_uint32)(src_s24[i*3+0]) << 8) | ((ma_uint32)(src_s24[i*3+1]) << 16) | ((ma_uint32)(src_s24[i*3+2])) << 24); - - /* Dither. Don't overflow. */ - ma_int32 dither = ma_dither_s32(ditherMode, -0x8000, 0x7FFF); - if ((ma_int64)x + dither <= 0x7FFFFFFF) { - x = x + dither; - } else { - x = 0x7FFFFFFF; - } - - x = x >> 16; - dst_s16[i] = (ma_int16)x; - } - } -} - -static MA_INLINE void ma_pcm_s24_to_s16__optimized(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) -{ - ma_pcm_s24_to_s16__reference(dst, src, count, ditherMode); -} - -#if defined(MA_SUPPORT_SSE2) -static MA_INLINE void ma_pcm_s24_to_s16__sse2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) -{ - ma_pcm_s24_to_s16__optimized(dst, src, count, ditherMode); -} -#endif -#if defined(MA_SUPPORT_NEON) -static MA_INLINE void ma_pcm_s24_to_s16__neon(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) -{ - ma_pcm_s24_to_s16__optimized(dst, src, count, ditherMode); -} -#endif - -MA_API void ma_pcm_s24_to_s16(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) -{ -#ifdef MA_USE_REFERENCE_CONVERSION_APIS - ma_pcm_s24_to_s16__reference(dst, src, count, ditherMode); -#else - # if defined(MA_SUPPORT_SSE2) - if (ma_has_sse2()) { - ma_pcm_s24_to_s16__sse2(dst, src, count, ditherMode); - } else - #elif defined(MA_SUPPORT_NEON) - if (ma_has_neon()) { - ma_pcm_s24_to_s16__neon(dst, src, count, ditherMode); - } else - #endif - { - ma_pcm_s24_to_s16__optimized(dst, src, count, ditherMode); - } -#endif -} - - -MA_API void ma_pcm_s24_to_s24(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) -{ - (void)ditherMode; - - ma_copy_memory_64(dst, src, count * 3); -} - - -static MA_INLINE void ma_pcm_s24_to_s32__reference(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) -{ - ma_int32* dst_s32 = (ma_int32*)dst; - const ma_uint8* src_s24 = (const ma_uint8*)src; - - ma_uint64 i; - for (i = 0; i < count; i += 1) { - dst_s32[i] = (ma_int32)(((ma_uint32)(src_s24[i*3+0]) << 8) | ((ma_uint32)(src_s24[i*3+1]) << 16) | ((ma_uint32)(src_s24[i*3+2])) << 24); - } - - (void)ditherMode; -} - -static MA_INLINE void ma_pcm_s24_to_s32__optimized(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) -{ - ma_pcm_s24_to_s32__reference(dst, src, count, ditherMode); -} - -#if defined(MA_SUPPORT_SSE2) -static MA_INLINE void ma_pcm_s24_to_s32__sse2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) -{ - ma_pcm_s24_to_s32__optimized(dst, src, count, ditherMode); -} -#endif -#if defined(MA_SUPPORT_NEON) -static MA_INLINE void ma_pcm_s24_to_s32__neon(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) -{ - ma_pcm_s24_to_s32__optimized(dst, src, count, ditherMode); -} -#endif - -MA_API void ma_pcm_s24_to_s32(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) -{ -#ifdef MA_USE_REFERENCE_CONVERSION_APIS - ma_pcm_s24_to_s32__reference(dst, src, count, ditherMode); -#else - # if defined(MA_SUPPORT_SSE2) - if (ma_has_sse2()) { - ma_pcm_s24_to_s32__sse2(dst, src, count, ditherMode); - } else - #elif defined(MA_SUPPORT_NEON) - if (ma_has_neon()) { - ma_pcm_s24_to_s32__neon(dst, src, count, ditherMode); - } else - #endif - { - ma_pcm_s24_to_s32__optimized(dst, src, count, ditherMode); - } -#endif -} - - -static MA_INLINE void ma_pcm_s24_to_f32__reference(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) -{ - float* dst_f32 = (float*)dst; - const ma_uint8* src_s24 = (const ma_uint8*)src; - - ma_uint64 i; - for (i = 0; i < count; i += 1) { - float x = (float)(((ma_int32)(((ma_uint32)(src_s24[i*3+0]) << 8) | ((ma_uint32)(src_s24[i*3+1]) << 16) | ((ma_uint32)(src_s24[i*3+2])) << 24)) >> 8); - -#if 0 - /* The accurate way. */ - x = x + 8388608.0f; /* -8388608..8388607 to 0..16777215 */ - x = x * 0.00000011920929665621f; /* 0..16777215 to 0..2 */ - x = x - 1; /* 0..2 to -1..1 */ -#else - /* The fast way. */ - x = x * 0.00000011920928955078125f; /* -8388608..8388607 to -1..0.999969482421875 */ -#endif - - dst_f32[i] = x; - } - - (void)ditherMode; -} - -static MA_INLINE void ma_pcm_s24_to_f32__optimized(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) -{ - ma_pcm_s24_to_f32__reference(dst, src, count, ditherMode); -} - -#if defined(MA_SUPPORT_SSE2) -static MA_INLINE void ma_pcm_s24_to_f32__sse2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) -{ - ma_pcm_s24_to_f32__optimized(dst, src, count, ditherMode); -} -#endif -#if defined(MA_SUPPORT_NEON) -static MA_INLINE void ma_pcm_s24_to_f32__neon(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) -{ - ma_pcm_s24_to_f32__optimized(dst, src, count, ditherMode); -} -#endif - -MA_API void ma_pcm_s24_to_f32(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) -{ -#ifdef MA_USE_REFERENCE_CONVERSION_APIS - ma_pcm_s24_to_f32__reference(dst, src, count, ditherMode); -#else - # if defined(MA_SUPPORT_SSE2) - if (ma_has_sse2()) { - ma_pcm_s24_to_f32__sse2(dst, src, count, ditherMode); - } else - #elif defined(MA_SUPPORT_NEON) - if (ma_has_neon()) { - ma_pcm_s24_to_f32__neon(dst, src, count, ditherMode); - } else - #endif - { - ma_pcm_s24_to_f32__optimized(dst, src, count, ditherMode); - } -#endif -} - - -static MA_INLINE void ma_pcm_interleave_s24__reference(void* dst, const void** src, ma_uint64 frameCount, ma_uint32 channels) -{ - ma_uint8* dst8 = (ma_uint8*)dst; - const ma_uint8** src8 = (const ma_uint8**)src; - - ma_uint64 iFrame; - for (iFrame = 0; iFrame < frameCount; iFrame += 1) { - ma_uint32 iChannel; - for (iChannel = 0; iChannel < channels; iChannel += 1) { - dst8[iFrame*3*channels + iChannel*3 + 0] = src8[iChannel][iFrame*3 + 0]; - dst8[iFrame*3*channels + iChannel*3 + 1] = src8[iChannel][iFrame*3 + 1]; - dst8[iFrame*3*channels + iChannel*3 + 2] = src8[iChannel][iFrame*3 + 2]; - } - } -} - -static MA_INLINE void ma_pcm_interleave_s24__optimized(void* dst, const void** src, ma_uint64 frameCount, ma_uint32 channels) -{ - ma_pcm_interleave_s24__reference(dst, src, frameCount, channels); -} - -MA_API void ma_pcm_interleave_s24(void* dst, const void** src, ma_uint64 frameCount, ma_uint32 channels) -{ -#ifdef MA_USE_REFERENCE_CONVERSION_APIS - ma_pcm_interleave_s24__reference(dst, src, frameCount, channels); -#else - ma_pcm_interleave_s24__optimized(dst, src, frameCount, channels); -#endif -} - - -static MA_INLINE void ma_pcm_deinterleave_s24__reference(void** dst, const void* src, ma_uint64 frameCount, ma_uint32 channels) -{ - ma_uint8** dst8 = (ma_uint8**)dst; - const ma_uint8* src8 = (const ma_uint8*)src; - - ma_uint32 iFrame; - for (iFrame = 0; iFrame < frameCount; iFrame += 1) { - ma_uint32 iChannel; - for (iChannel = 0; iChannel < channels; iChannel += 1) { - dst8[iChannel][iFrame*3 + 0] = src8[iFrame*3*channels + iChannel*3 + 0]; - dst8[iChannel][iFrame*3 + 1] = src8[iFrame*3*channels + iChannel*3 + 1]; - dst8[iChannel][iFrame*3 + 2] = src8[iFrame*3*channels + iChannel*3 + 2]; - } - } -} - -static MA_INLINE void ma_pcm_deinterleave_s24__optimized(void** dst, const void* src, ma_uint64 frameCount, ma_uint32 channels) -{ - ma_pcm_deinterleave_s24__reference(dst, src, frameCount, channels); -} - -MA_API void ma_pcm_deinterleave_s24(void** dst, const void* src, ma_uint64 frameCount, ma_uint32 channels) -{ -#ifdef MA_USE_REFERENCE_CONVERSION_APIS - ma_pcm_deinterleave_s24__reference(dst, src, frameCount, channels); -#else - ma_pcm_deinterleave_s24__optimized(dst, src, frameCount, channels); -#endif -} - - - -/* s32 */ -static MA_INLINE void ma_pcm_s32_to_u8__reference(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) -{ - ma_uint8* dst_u8 = (ma_uint8*)dst; - const ma_int32* src_s32 = (const ma_int32*)src; - - if (ditherMode == ma_dither_mode_none) { - ma_uint64 i; - for (i = 0; i < count; i += 1) { - ma_int32 x = src_s32[i]; - x = x >> 24; - x = x + 128; - dst_u8[i] = (ma_uint8)x; - } - } else { - ma_uint64 i; - for (i = 0; i < count; i += 1) { - ma_int32 x = src_s32[i]; - - /* Dither. Don't overflow. */ - ma_int32 dither = ma_dither_s32(ditherMode, -0x800000, 0x7FFFFF); - if ((ma_int64)x + dither <= 0x7FFFFFFF) { - x = x + dither; - } else { - x = 0x7FFFFFFF; - } - - x = x >> 24; - x = x + 128; - dst_u8[i] = (ma_uint8)x; - } - } -} - -static MA_INLINE void ma_pcm_s32_to_u8__optimized(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) -{ - ma_pcm_s32_to_u8__reference(dst, src, count, ditherMode); -} - -#if defined(MA_SUPPORT_SSE2) -static MA_INLINE void ma_pcm_s32_to_u8__sse2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) -{ - ma_pcm_s32_to_u8__optimized(dst, src, count, ditherMode); -} -#endif -#if defined(MA_SUPPORT_NEON) -static MA_INLINE void ma_pcm_s32_to_u8__neon(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) -{ - ma_pcm_s32_to_u8__optimized(dst, src, count, ditherMode); -} -#endif - -MA_API void ma_pcm_s32_to_u8(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) -{ -#ifdef MA_USE_REFERENCE_CONVERSION_APIS - ma_pcm_s32_to_u8__reference(dst, src, count, ditherMode); -#else - # if defined(MA_SUPPORT_SSE2) - if (ma_has_sse2()) { - ma_pcm_s32_to_u8__sse2(dst, src, count, ditherMode); - } else - #elif defined(MA_SUPPORT_NEON) - if (ma_has_neon()) { - ma_pcm_s32_to_u8__neon(dst, src, count, ditherMode); - } else - #endif - { - ma_pcm_s32_to_u8__optimized(dst, src, count, ditherMode); - } -#endif -} - - -static MA_INLINE void ma_pcm_s32_to_s16__reference(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) -{ - ma_int16* dst_s16 = (ma_int16*)dst; - const ma_int32* src_s32 = (const ma_int32*)src; - - if (ditherMode == ma_dither_mode_none) { - ma_uint64 i; - for (i = 0; i < count; i += 1) { - ma_int32 x = src_s32[i]; - x = x >> 16; - dst_s16[i] = (ma_int16)x; - } - } else { - ma_uint64 i; - for (i = 0; i < count; i += 1) { - ma_int32 x = src_s32[i]; - - /* Dither. Don't overflow. */ - ma_int32 dither = ma_dither_s32(ditherMode, -0x8000, 0x7FFF); - if ((ma_int64)x + dither <= 0x7FFFFFFF) { - x = x + dither; - } else { - x = 0x7FFFFFFF; - } - - x = x >> 16; - dst_s16[i] = (ma_int16)x; - } - } -} - -static MA_INLINE void ma_pcm_s32_to_s16__optimized(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) -{ - ma_pcm_s32_to_s16__reference(dst, src, count, ditherMode); -} - -#if defined(MA_SUPPORT_SSE2) -static MA_INLINE void ma_pcm_s32_to_s16__sse2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) -{ - ma_pcm_s32_to_s16__optimized(dst, src, count, ditherMode); -} -#endif -#if defined(MA_SUPPORT_NEON) -static MA_INLINE void ma_pcm_s32_to_s16__neon(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) -{ - ma_pcm_s32_to_s16__optimized(dst, src, count, ditherMode); -} -#endif - -MA_API void ma_pcm_s32_to_s16(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) -{ -#ifdef MA_USE_REFERENCE_CONVERSION_APIS - ma_pcm_s32_to_s16__reference(dst, src, count, ditherMode); -#else - # if defined(MA_SUPPORT_SSE2) - if (ma_has_sse2()) { - ma_pcm_s32_to_s16__sse2(dst, src, count, ditherMode); - } else - #elif defined(MA_SUPPORT_NEON) - if (ma_has_neon()) { - ma_pcm_s32_to_s16__neon(dst, src, count, ditherMode); - } else - #endif - { - ma_pcm_s32_to_s16__optimized(dst, src, count, ditherMode); - } -#endif -} - - -static MA_INLINE void ma_pcm_s32_to_s24__reference(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) -{ - ma_uint8* dst_s24 = (ma_uint8*)dst; - const ma_int32* src_s32 = (const ma_int32*)src; - - ma_uint64 i; - for (i = 0; i < count; i += 1) { - ma_uint32 x = (ma_uint32)src_s32[i]; - dst_s24[i*3+0] = (ma_uint8)((x & 0x0000FF00) >> 8); - dst_s24[i*3+1] = (ma_uint8)((x & 0x00FF0000) >> 16); - dst_s24[i*3+2] = (ma_uint8)((x & 0xFF000000) >> 24); - } - - (void)ditherMode; /* No dithering for s32 -> s24. */ -} - -static MA_INLINE void ma_pcm_s32_to_s24__optimized(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) -{ - ma_pcm_s32_to_s24__reference(dst, src, count, ditherMode); -} - -#if defined(MA_SUPPORT_SSE2) -static MA_INLINE void ma_pcm_s32_to_s24__sse2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) -{ - ma_pcm_s32_to_s24__optimized(dst, src, count, ditherMode); -} -#endif -#if defined(MA_SUPPORT_NEON) -static MA_INLINE void ma_pcm_s32_to_s24__neon(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) -{ - ma_pcm_s32_to_s24__optimized(dst, src, count, ditherMode); -} -#endif - -MA_API void ma_pcm_s32_to_s24(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) -{ -#ifdef MA_USE_REFERENCE_CONVERSION_APIS - ma_pcm_s32_to_s24__reference(dst, src, count, ditherMode); -#else - # if defined(MA_SUPPORT_SSE2) - if (ma_has_sse2()) { - ma_pcm_s32_to_s24__sse2(dst, src, count, ditherMode); - } else - #elif defined(MA_SUPPORT_NEON) - if (ma_has_neon()) { - ma_pcm_s32_to_s24__neon(dst, src, count, ditherMode); - } else - #endif - { - ma_pcm_s32_to_s24__optimized(dst, src, count, ditherMode); - } -#endif -} - - -MA_API void ma_pcm_s32_to_s32(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) -{ - (void)ditherMode; - - ma_copy_memory_64(dst, src, count * sizeof(ma_int32)); -} - - -static MA_INLINE void ma_pcm_s32_to_f32__reference(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) -{ - float* dst_f32 = (float*)dst; - const ma_int32* src_s32 = (const ma_int32*)src; - - ma_uint64 i; - for (i = 0; i < count; i += 1) { - double x = src_s32[i]; - -#if 0 - x = x + 2147483648.0; - x = x * 0.0000000004656612873077392578125; - x = x - 1; -#else - x = x / 2147483648.0; -#endif - - dst_f32[i] = (float)x; - } - - (void)ditherMode; /* No dithering for s32 -> f32. */ -} - -static MA_INLINE void ma_pcm_s32_to_f32__optimized(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) -{ - ma_pcm_s32_to_f32__reference(dst, src, count, ditherMode); -} - -#if defined(MA_SUPPORT_SSE2) -static MA_INLINE void ma_pcm_s32_to_f32__sse2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) -{ - ma_pcm_s32_to_f32__optimized(dst, src, count, ditherMode); -} -#endif -#if defined(MA_SUPPORT_NEON) -static MA_INLINE void ma_pcm_s32_to_f32__neon(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) -{ - ma_pcm_s32_to_f32__optimized(dst, src, count, ditherMode); -} -#endif - -MA_API void ma_pcm_s32_to_f32(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) -{ -#ifdef MA_USE_REFERENCE_CONVERSION_APIS - ma_pcm_s32_to_f32__reference(dst, src, count, ditherMode); -#else - # if defined(MA_SUPPORT_SSE2) - if (ma_has_sse2()) { - ma_pcm_s32_to_f32__sse2(dst, src, count, ditherMode); - } else - #elif defined(MA_SUPPORT_NEON) - if (ma_has_neon()) { - ma_pcm_s32_to_f32__neon(dst, src, count, ditherMode); - } else - #endif - { - ma_pcm_s32_to_f32__optimized(dst, src, count, ditherMode); - } -#endif -} - - -static MA_INLINE void ma_pcm_interleave_s32__reference(void* dst, const void** src, ma_uint64 frameCount, ma_uint32 channels) -{ - ma_int32* dst_s32 = (ma_int32*)dst; - const ma_int32** src_s32 = (const ma_int32**)src; - - ma_uint64 iFrame; - for (iFrame = 0; iFrame < frameCount; iFrame += 1) { - ma_uint32 iChannel; - for (iChannel = 0; iChannel < channels; iChannel += 1) { - dst_s32[iFrame*channels + iChannel] = src_s32[iChannel][iFrame]; - } - } -} - -static MA_INLINE void ma_pcm_interleave_s32__optimized(void* dst, const void** src, ma_uint64 frameCount, ma_uint32 channels) -{ - ma_pcm_interleave_s32__reference(dst, src, frameCount, channels); -} - -MA_API void ma_pcm_interleave_s32(void* dst, const void** src, ma_uint64 frameCount, ma_uint32 channels) -{ -#ifdef MA_USE_REFERENCE_CONVERSION_APIS - ma_pcm_interleave_s32__reference(dst, src, frameCount, channels); -#else - ma_pcm_interleave_s32__optimized(dst, src, frameCount, channels); -#endif -} - - -static MA_INLINE void ma_pcm_deinterleave_s32__reference(void** dst, const void* src, ma_uint64 frameCount, ma_uint32 channels) -{ - ma_int32** dst_s32 = (ma_int32**)dst; - const ma_int32* src_s32 = (const ma_int32*)src; - - ma_uint64 iFrame; - for (iFrame = 0; iFrame < frameCount; iFrame += 1) { - ma_uint32 iChannel; - for (iChannel = 0; iChannel < channels; iChannel += 1) { - dst_s32[iChannel][iFrame] = src_s32[iFrame*channels + iChannel]; - } - } -} - -static MA_INLINE void ma_pcm_deinterleave_s32__optimized(void** dst, const void* src, ma_uint64 frameCount, ma_uint32 channels) -{ - ma_pcm_deinterleave_s32__reference(dst, src, frameCount, channels); -} - -MA_API void ma_pcm_deinterleave_s32(void** dst, const void* src, ma_uint64 frameCount, ma_uint32 channels) -{ -#ifdef MA_USE_REFERENCE_CONVERSION_APIS - ma_pcm_deinterleave_s32__reference(dst, src, frameCount, channels); -#else - ma_pcm_deinterleave_s32__optimized(dst, src, frameCount, channels); -#endif -} - - -/* f32 */ -static MA_INLINE void ma_pcm_f32_to_u8__reference(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) -{ - ma_uint64 i; - - ma_uint8* dst_u8 = (ma_uint8*)dst; - const float* src_f32 = (const float*)src; - - float ditherMin = 0; - float ditherMax = 0; - if (ditherMode != ma_dither_mode_none) { - ditherMin = 1.0f / -128; - ditherMax = 1.0f / 127; - } - - for (i = 0; i < count; i += 1) { - float x = src_f32[i]; - x = x + ma_dither_f32(ditherMode, ditherMin, ditherMax); - x = ((x < -1) ? -1 : ((x > 1) ? 1 : x)); /* clip */ - x = x + 1; /* -1..1 to 0..2 */ - x = x * 127.5f; /* 0..2 to 0..255 */ - - dst_u8[i] = (ma_uint8)x; - } -} - -static MA_INLINE void ma_pcm_f32_to_u8__optimized(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) -{ - ma_pcm_f32_to_u8__reference(dst, src, count, ditherMode); -} - -#if defined(MA_SUPPORT_SSE2) -static MA_INLINE void ma_pcm_f32_to_u8__sse2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) -{ - ma_pcm_f32_to_u8__optimized(dst, src, count, ditherMode); -} -#endif -#if defined(MA_SUPPORT_NEON) -static MA_INLINE void ma_pcm_f32_to_u8__neon(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) -{ - ma_pcm_f32_to_u8__optimized(dst, src, count, ditherMode); -} -#endif - -MA_API void ma_pcm_f32_to_u8(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) -{ -#ifdef MA_USE_REFERENCE_CONVERSION_APIS - ma_pcm_f32_to_u8__reference(dst, src, count, ditherMode); -#else - # if defined(MA_SUPPORT_SSE2) - if (ma_has_sse2()) { - ma_pcm_f32_to_u8__sse2(dst, src, count, ditherMode); - } else - #elif defined(MA_SUPPORT_NEON) - if (ma_has_neon()) { - ma_pcm_f32_to_u8__neon(dst, src, count, ditherMode); - } else - #endif - { - ma_pcm_f32_to_u8__optimized(dst, src, count, ditherMode); - } -#endif -} - -#ifdef MA_USE_REFERENCE_CONVERSION_APIS -static MA_INLINE void ma_pcm_f32_to_s16__reference(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) -{ - ma_uint64 i; - - ma_int16* dst_s16 = (ma_int16*)dst; - const float* src_f32 = (const float*)src; - - float ditherMin = 0; - float ditherMax = 0; - if (ditherMode != ma_dither_mode_none) { - ditherMin = 1.0f / -32768; - ditherMax = 1.0f / 32767; - } - - for (i = 0; i < count; i += 1) { - float x = src_f32[i]; - x = x + ma_dither_f32(ditherMode, ditherMin, ditherMax); - x = ((x < -1) ? -1 : ((x > 1) ? 1 : x)); /* clip */ - -#if 0 - /* The accurate way. */ - x = x + 1; /* -1..1 to 0..2 */ - x = x * 32767.5f; /* 0..2 to 0..65535 */ - x = x - 32768.0f; /* 0...65535 to -32768..32767 */ -#else - /* The fast way. */ - x = x * 32767.0f; /* -1..1 to -32767..32767 */ -#endif - - dst_s16[i] = (ma_int16)x; - } -} -#else -static MA_INLINE void ma_pcm_f32_to_s16__optimized(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) -{ - ma_uint64 i; - ma_uint64 i4; - ma_uint64 count4; - - ma_int16* dst_s16 = (ma_int16*)dst; - const float* src_f32 = (const float*)src; - - float ditherMin = 0; - float ditherMax = 0; - if (ditherMode != ma_dither_mode_none) { - ditherMin = 1.0f / -32768; - ditherMax = 1.0f / 32767; - } - - /* Unrolled. */ - i = 0; - count4 = count >> 2; - for (i4 = 0; i4 < count4; i4 += 1) { - float d0 = ma_dither_f32(ditherMode, ditherMin, ditherMax); - float d1 = ma_dither_f32(ditherMode, ditherMin, ditherMax); - float d2 = ma_dither_f32(ditherMode, ditherMin, ditherMax); - float d3 = ma_dither_f32(ditherMode, ditherMin, ditherMax); - - float x0 = src_f32[i+0]; - float x1 = src_f32[i+1]; - float x2 = src_f32[i+2]; - float x3 = src_f32[i+3]; - - x0 = x0 + d0; - x1 = x1 + d1; - x2 = x2 + d2; - x3 = x3 + d3; - - x0 = ((x0 < -1) ? -1 : ((x0 > 1) ? 1 : x0)); - x1 = ((x1 < -1) ? -1 : ((x1 > 1) ? 1 : x1)); - x2 = ((x2 < -1) ? -1 : ((x2 > 1) ? 1 : x2)); - x3 = ((x3 < -1) ? -1 : ((x3 > 1) ? 1 : x3)); - - x0 = x0 * 32767.0f; - x1 = x1 * 32767.0f; - x2 = x2 * 32767.0f; - x3 = x3 * 32767.0f; - - dst_s16[i+0] = (ma_int16)x0; - dst_s16[i+1] = (ma_int16)x1; - dst_s16[i+2] = (ma_int16)x2; - dst_s16[i+3] = (ma_int16)x3; - - i += 4; - } - - /* Leftover. */ - for (; i < count; i += 1) { - float x = src_f32[i]; - x = x + ma_dither_f32(ditherMode, ditherMin, ditherMax); - x = ((x < -1) ? -1 : ((x > 1) ? 1 : x)); /* clip */ - x = x * 32767.0f; /* -1..1 to -32767..32767 */ - - dst_s16[i] = (ma_int16)x; - } -} - -#if defined(MA_SUPPORT_SSE2) -static MA_INLINE void ma_pcm_f32_to_s16__sse2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) -{ - ma_uint64 i; - ma_uint64 i8; - ma_uint64 count8; - ma_int16* dst_s16; - const float* src_f32; - float ditherMin; - float ditherMax; - - /* Both the input and output buffers need to be aligned to 16 bytes. */ - if ((((ma_uintptr)dst & 15) != 0) || (((ma_uintptr)src & 15) != 0)) { - ma_pcm_f32_to_s16__optimized(dst, src, count, ditherMode); - return; - } - - dst_s16 = (ma_int16*)dst; - src_f32 = (const float*)src; - - ditherMin = 0; - ditherMax = 0; - if (ditherMode != ma_dither_mode_none) { - ditherMin = 1.0f / -32768; - ditherMax = 1.0f / 32767; - } - - i = 0; - - /* SSE2. SSE allows us to output 8 s16's at a time which means our loop is unrolled 8 times. */ - count8 = count >> 3; - for (i8 = 0; i8 < count8; i8 += 1) { - __m128 d0; - __m128 d1; - __m128 x0; - __m128 x1; - - if (ditherMode == ma_dither_mode_none) { - d0 = _mm_set1_ps(0); - d1 = _mm_set1_ps(0); - } else if (ditherMode == ma_dither_mode_rectangle) { - d0 = _mm_set_ps( - ma_dither_f32_rectangle(ditherMin, ditherMax), - ma_dither_f32_rectangle(ditherMin, ditherMax), - ma_dither_f32_rectangle(ditherMin, ditherMax), - ma_dither_f32_rectangle(ditherMin, ditherMax) - ); - d1 = _mm_set_ps( - ma_dither_f32_rectangle(ditherMin, ditherMax), - ma_dither_f32_rectangle(ditherMin, ditherMax), - ma_dither_f32_rectangle(ditherMin, ditherMax), - ma_dither_f32_rectangle(ditherMin, ditherMax) - ); - } else { - d0 = _mm_set_ps( - ma_dither_f32_triangle(ditherMin, ditherMax), - ma_dither_f32_triangle(ditherMin, ditherMax), - ma_dither_f32_triangle(ditherMin, ditherMax), - ma_dither_f32_triangle(ditherMin, ditherMax) - ); - d1 = _mm_set_ps( - ma_dither_f32_triangle(ditherMin, ditherMax), - ma_dither_f32_triangle(ditherMin, ditherMax), - ma_dither_f32_triangle(ditherMin, ditherMax), - ma_dither_f32_triangle(ditherMin, ditherMax) - ); - } - - x0 = *((__m128*)(src_f32 + i) + 0); - x1 = *((__m128*)(src_f32 + i) + 1); - - x0 = _mm_add_ps(x0, d0); - x1 = _mm_add_ps(x1, d1); - - x0 = _mm_mul_ps(x0, _mm_set1_ps(32767.0f)); - x1 = _mm_mul_ps(x1, _mm_set1_ps(32767.0f)); - - _mm_stream_si128(((__m128i*)(dst_s16 + i)), _mm_packs_epi32(_mm_cvttps_epi32(x0), _mm_cvttps_epi32(x1))); - - i += 8; - } - - - /* Leftover. */ - for (; i < count; i += 1) { - float x = src_f32[i]; - x = x + ma_dither_f32(ditherMode, ditherMin, ditherMax); - x = ((x < -1) ? -1 : ((x > 1) ? 1 : x)); /* clip */ - x = x * 32767.0f; /* -1..1 to -32767..32767 */ - - dst_s16[i] = (ma_int16)x; - } -} -#endif /* SSE2 */ - -#if defined(MA_SUPPORT_NEON) -static MA_INLINE void ma_pcm_f32_to_s16__neon(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) -{ - ma_uint64 i; - ma_uint64 i8; - ma_uint64 count8; - ma_int16* dst_s16; - const float* src_f32; - float ditherMin; - float ditherMax; - - if (!ma_has_neon()) { - ma_pcm_f32_to_s16__optimized(dst, src, count, ditherMode); - return; - } - - /* Both the input and output buffers need to be aligned to 16 bytes. */ - if ((((ma_uintptr)dst & 15) != 0) || (((ma_uintptr)src & 15) != 0)) { - ma_pcm_f32_to_s16__optimized(dst, src, count, ditherMode); - return; - } - - dst_s16 = (ma_int16*)dst; - src_f32 = (const float*)src; - - ditherMin = 0; - ditherMax = 0; - if (ditherMode != ma_dither_mode_none) { - ditherMin = 1.0f / -32768; - ditherMax = 1.0f / 32767; - } - - i = 0; - - /* NEON. NEON allows us to output 8 s16's at a time which means our loop is unrolled 8 times. */ - count8 = count >> 3; - for (i8 = 0; i8 < count8; i8 += 1) { - float32x4_t d0; - float32x4_t d1; - float32x4_t x0; - float32x4_t x1; - int32x4_t i0; - int32x4_t i1; - - if (ditherMode == ma_dither_mode_none) { - d0 = vmovq_n_f32(0); - d1 = vmovq_n_f32(0); - } else if (ditherMode == ma_dither_mode_rectangle) { - float d0v[4]; - float d1v[4]; - - d0v[0] = ma_dither_f32_rectangle(ditherMin, ditherMax); - d0v[1] = ma_dither_f32_rectangle(ditherMin, ditherMax); - d0v[2] = ma_dither_f32_rectangle(ditherMin, ditherMax); - d0v[3] = ma_dither_f32_rectangle(ditherMin, ditherMax); - d0 = vld1q_f32(d0v); - - d1v[0] = ma_dither_f32_rectangle(ditherMin, ditherMax); - d1v[1] = ma_dither_f32_rectangle(ditherMin, ditherMax); - d1v[2] = ma_dither_f32_rectangle(ditherMin, ditherMax); - d1v[3] = ma_dither_f32_rectangle(ditherMin, ditherMax); - d1 = vld1q_f32(d1v); - } else { - float d0v[4]; - float d1v[4]; - - d0v[0] = ma_dither_f32_triangle(ditherMin, ditherMax); - d0v[1] = ma_dither_f32_triangle(ditherMin, ditherMax); - d0v[2] = ma_dither_f32_triangle(ditherMin, ditherMax); - d0v[3] = ma_dither_f32_triangle(ditherMin, ditherMax); - d0 = vld1q_f32(d0v); - - d1v[0] = ma_dither_f32_triangle(ditherMin, ditherMax); - d1v[1] = ma_dither_f32_triangle(ditherMin, ditherMax); - d1v[2] = ma_dither_f32_triangle(ditherMin, ditherMax); - d1v[3] = ma_dither_f32_triangle(ditherMin, ditherMax); - d1 = vld1q_f32(d1v); - } - - x0 = *((float32x4_t*)(src_f32 + i) + 0); - x1 = *((float32x4_t*)(src_f32 + i) + 1); - - x0 = vaddq_f32(x0, d0); - x1 = vaddq_f32(x1, d1); - - x0 = vmulq_n_f32(x0, 32767.0f); - x1 = vmulq_n_f32(x1, 32767.0f); - - i0 = vcvtq_s32_f32(x0); - i1 = vcvtq_s32_f32(x1); - *((int16x8_t*)(dst_s16 + i)) = vcombine_s16(vqmovn_s32(i0), vqmovn_s32(i1)); - - i += 8; - } - - - /* Leftover. */ - for (; i < count; i += 1) { - float x = src_f32[i]; - x = x + ma_dither_f32(ditherMode, ditherMin, ditherMax); - x = ((x < -1) ? -1 : ((x > 1) ? 1 : x)); /* clip */ - x = x * 32767.0f; /* -1..1 to -32767..32767 */ - - dst_s16[i] = (ma_int16)x; - } -} -#endif /* Neon */ -#endif /* MA_USE_REFERENCE_CONVERSION_APIS */ - -MA_API void ma_pcm_f32_to_s16(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) -{ -#ifdef MA_USE_REFERENCE_CONVERSION_APIS - ma_pcm_f32_to_s16__reference(dst, src, count, ditherMode); -#else - # if defined(MA_SUPPORT_SSE2) - if (ma_has_sse2()) { - ma_pcm_f32_to_s16__sse2(dst, src, count, ditherMode); - } else - #elif defined(MA_SUPPORT_NEON) - if (ma_has_neon()) { - ma_pcm_f32_to_s16__neon(dst, src, count, ditherMode); - } else - #endif - { - ma_pcm_f32_to_s16__optimized(dst, src, count, ditherMode); - } -#endif -} - - -static MA_INLINE void ma_pcm_f32_to_s24__reference(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) -{ - ma_uint8* dst_s24 = (ma_uint8*)dst; - const float* src_f32 = (const float*)src; - - ma_uint64 i; - for (i = 0; i < count; i += 1) { - ma_int32 r; - float x = src_f32[i]; - x = ((x < -1) ? -1 : ((x > 1) ? 1 : x)); /* clip */ - -#if 0 - /* The accurate way. */ - x = x + 1; /* -1..1 to 0..2 */ - x = x * 8388607.5f; /* 0..2 to 0..16777215 */ - x = x - 8388608.0f; /* 0..16777215 to -8388608..8388607 */ -#else - /* The fast way. */ - x = x * 8388607.0f; /* -1..1 to -8388607..8388607 */ -#endif - - r = (ma_int32)x; - dst_s24[(i*3)+0] = (ma_uint8)((r & 0x0000FF) >> 0); - dst_s24[(i*3)+1] = (ma_uint8)((r & 0x00FF00) >> 8); - dst_s24[(i*3)+2] = (ma_uint8)((r & 0xFF0000) >> 16); - } - - (void)ditherMode; /* No dithering for f32 -> s24. */ -} - -static MA_INLINE void ma_pcm_f32_to_s24__optimized(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) -{ - ma_pcm_f32_to_s24__reference(dst, src, count, ditherMode); -} - -#if defined(MA_SUPPORT_SSE2) -static MA_INLINE void ma_pcm_f32_to_s24__sse2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) -{ - ma_pcm_f32_to_s24__optimized(dst, src, count, ditherMode); -} -#endif -#if defined(MA_SUPPORT_NEON) -static MA_INLINE void ma_pcm_f32_to_s24__neon(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) -{ - ma_pcm_f32_to_s24__optimized(dst, src, count, ditherMode); -} -#endif - -MA_API void ma_pcm_f32_to_s24(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) -{ -#ifdef MA_USE_REFERENCE_CONVERSION_APIS - ma_pcm_f32_to_s24__reference(dst, src, count, ditherMode); -#else - # if defined(MA_SUPPORT_SSE2) - if (ma_has_sse2()) { - ma_pcm_f32_to_s24__sse2(dst, src, count, ditherMode); - } else - #elif defined(MA_SUPPORT_NEON) - if (ma_has_neon()) { - ma_pcm_f32_to_s24__neon(dst, src, count, ditherMode); - } else - #endif - { - ma_pcm_f32_to_s24__optimized(dst, src, count, ditherMode); - } -#endif -} - - -static MA_INLINE void ma_pcm_f32_to_s32__reference(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) -{ - ma_int32* dst_s32 = (ma_int32*)dst; - const float* src_f32 = (const float*)src; - - ma_uint32 i; - for (i = 0; i < count; i += 1) { - double x = src_f32[i]; - x = ((x < -1) ? -1 : ((x > 1) ? 1 : x)); /* clip */ - -#if 0 - /* The accurate way. */ - x = x + 1; /* -1..1 to 0..2 */ - x = x * 2147483647.5; /* 0..2 to 0..4294967295 */ - x = x - 2147483648.0; /* 0...4294967295 to -2147483648..2147483647 */ -#else - /* The fast way. */ - x = x * 2147483647.0; /* -1..1 to -2147483647..2147483647 */ -#endif - - dst_s32[i] = (ma_int32)x; - } - - (void)ditherMode; /* No dithering for f32 -> s32. */ -} - -static MA_INLINE void ma_pcm_f32_to_s32__optimized(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) -{ - ma_pcm_f32_to_s32__reference(dst, src, count, ditherMode); -} - -#if defined(MA_SUPPORT_SSE2) -static MA_INLINE void ma_pcm_f32_to_s32__sse2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) -{ - ma_pcm_f32_to_s32__optimized(dst, src, count, ditherMode); -} -#endif -#if defined(MA_SUPPORT_NEON) -static MA_INLINE void ma_pcm_f32_to_s32__neon(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) -{ - ma_pcm_f32_to_s32__optimized(dst, src, count, ditherMode); -} -#endif - -MA_API void ma_pcm_f32_to_s32(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) -{ -#ifdef MA_USE_REFERENCE_CONVERSION_APIS - ma_pcm_f32_to_s32__reference(dst, src, count, ditherMode); -#else - # if defined(MA_SUPPORT_SSE2) - if (ma_has_sse2()) { - ma_pcm_f32_to_s32__sse2(dst, src, count, ditherMode); - } else - #elif defined(MA_SUPPORT_NEON) - if (ma_has_neon()) { - ma_pcm_f32_to_s32__neon(dst, src, count, ditherMode); - } else - #endif - { - ma_pcm_f32_to_s32__optimized(dst, src, count, ditherMode); - } -#endif -} - - -MA_API void ma_pcm_f32_to_f32(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) -{ - (void)ditherMode; - - ma_copy_memory_64(dst, src, count * sizeof(float)); -} - - -static void ma_pcm_interleave_f32__reference(void* dst, const void** src, ma_uint64 frameCount, ma_uint32 channels) -{ - float* dst_f32 = (float*)dst; - const float** src_f32 = (const float**)src; - - ma_uint64 iFrame; - for (iFrame = 0; iFrame < frameCount; iFrame += 1) { - ma_uint32 iChannel; - for (iChannel = 0; iChannel < channels; iChannel += 1) { - dst_f32[iFrame*channels + iChannel] = src_f32[iChannel][iFrame]; - } - } -} - -static void ma_pcm_interleave_f32__optimized(void* dst, const void** src, ma_uint64 frameCount, ma_uint32 channels) -{ - ma_pcm_interleave_f32__reference(dst, src, frameCount, channels); -} - -MA_API void ma_pcm_interleave_f32(void* dst, const void** src, ma_uint64 frameCount, ma_uint32 channels) -{ -#ifdef MA_USE_REFERENCE_CONVERSION_APIS - ma_pcm_interleave_f32__reference(dst, src, frameCount, channels); -#else - ma_pcm_interleave_f32__optimized(dst, src, frameCount, channels); -#endif -} - - -static void ma_pcm_deinterleave_f32__reference(void** dst, const void* src, ma_uint64 frameCount, ma_uint32 channels) -{ - float** dst_f32 = (float**)dst; - const float* src_f32 = (const float*)src; - - ma_uint64 iFrame; - for (iFrame = 0; iFrame < frameCount; iFrame += 1) { - ma_uint32 iChannel; - for (iChannel = 0; iChannel < channels; iChannel += 1) { - dst_f32[iChannel][iFrame] = src_f32[iFrame*channels + iChannel]; - } - } -} - -static void ma_pcm_deinterleave_f32__optimized(void** dst, const void* src, ma_uint64 frameCount, ma_uint32 channels) -{ - ma_pcm_deinterleave_f32__reference(dst, src, frameCount, channels); -} - -MA_API void ma_pcm_deinterleave_f32(void** dst, const void* src, ma_uint64 frameCount, ma_uint32 channels) -{ -#ifdef MA_USE_REFERENCE_CONVERSION_APIS - ma_pcm_deinterleave_f32__reference(dst, src, frameCount, channels); -#else - ma_pcm_deinterleave_f32__optimized(dst, src, frameCount, channels); -#endif -} - - -MA_API void ma_pcm_convert(void* pOut, ma_format formatOut, const void* pIn, ma_format formatIn, ma_uint64 sampleCount, ma_dither_mode ditherMode) -{ - if (formatOut == formatIn) { - ma_copy_memory_64(pOut, pIn, sampleCount * ma_get_bytes_per_sample(formatOut)); - return; - } - - switch (formatIn) - { - case ma_format_u8: - { - switch (formatOut) - { - case ma_format_s16: ma_pcm_u8_to_s16(pOut, pIn, sampleCount, ditherMode); return; - case ma_format_s24: ma_pcm_u8_to_s24(pOut, pIn, sampleCount, ditherMode); return; - case ma_format_s32: ma_pcm_u8_to_s32(pOut, pIn, sampleCount, ditherMode); return; - case ma_format_f32: ma_pcm_u8_to_f32(pOut, pIn, sampleCount, ditherMode); return; - default: break; - } - } break; - - case ma_format_s16: - { - switch (formatOut) - { - case ma_format_u8: ma_pcm_s16_to_u8( pOut, pIn, sampleCount, ditherMode); return; - case ma_format_s24: ma_pcm_s16_to_s24(pOut, pIn, sampleCount, ditherMode); return; - case ma_format_s32: ma_pcm_s16_to_s32(pOut, pIn, sampleCount, ditherMode); return; - case ma_format_f32: ma_pcm_s16_to_f32(pOut, pIn, sampleCount, ditherMode); return; - default: break; - } - } break; - - case ma_format_s24: - { - switch (formatOut) - { - case ma_format_u8: ma_pcm_s24_to_u8( pOut, pIn, sampleCount, ditherMode); return; - case ma_format_s16: ma_pcm_s24_to_s16(pOut, pIn, sampleCount, ditherMode); return; - case ma_format_s32: ma_pcm_s24_to_s32(pOut, pIn, sampleCount, ditherMode); return; - case ma_format_f32: ma_pcm_s24_to_f32(pOut, pIn, sampleCount, ditherMode); return; - default: break; - } - } break; - - case ma_format_s32: - { - switch (formatOut) - { - case ma_format_u8: ma_pcm_s32_to_u8( pOut, pIn, sampleCount, ditherMode); return; - case ma_format_s16: ma_pcm_s32_to_s16(pOut, pIn, sampleCount, ditherMode); return; - case ma_format_s24: ma_pcm_s32_to_s24(pOut, pIn, sampleCount, ditherMode); return; - case ma_format_f32: ma_pcm_s32_to_f32(pOut, pIn, sampleCount, ditherMode); return; - default: break; - } - } break; - - case ma_format_f32: - { - switch (formatOut) - { - case ma_format_u8: ma_pcm_f32_to_u8( pOut, pIn, sampleCount, ditherMode); return; - case ma_format_s16: ma_pcm_f32_to_s16(pOut, pIn, sampleCount, ditherMode); return; - case ma_format_s24: ma_pcm_f32_to_s24(pOut, pIn, sampleCount, ditherMode); return; - case ma_format_s32: ma_pcm_f32_to_s32(pOut, pIn, sampleCount, ditherMode); return; - default: break; - } - } break; - - default: break; - } -} - -MA_API void ma_convert_pcm_frames_format(void* pOut, ma_format formatOut, const void* pIn, ma_format formatIn, ma_uint64 frameCount, ma_uint32 channels, ma_dither_mode ditherMode) -{ - ma_pcm_convert(pOut, formatOut, pIn, formatIn, frameCount * channels, ditherMode); -} - -MA_API void ma_deinterleave_pcm_frames(ma_format format, ma_uint32 channels, ma_uint64 frameCount, const void* pInterleavedPCMFrames, void** ppDeinterleavedPCMFrames) -{ - if (pInterleavedPCMFrames == NULL || ppDeinterleavedPCMFrames == NULL) { - return; /* Invalid args. */ - } - - /* For efficiency we do this per format. */ - switch (format) { - case ma_format_s16: - { - const ma_int16* pSrcS16 = (const ma_int16*)pInterleavedPCMFrames; - ma_uint64 iPCMFrame; - for (iPCMFrame = 0; iPCMFrame < frameCount; ++iPCMFrame) { - ma_uint32 iChannel; - for (iChannel = 0; iChannel < channels; ++iChannel) { - ma_int16* pDstS16 = (ma_int16*)ppDeinterleavedPCMFrames[iChannel]; - pDstS16[iPCMFrame] = pSrcS16[iPCMFrame*channels+iChannel]; - } - } - } break; - - case ma_format_f32: - { - const float* pSrcF32 = (const float*)pInterleavedPCMFrames; - ma_uint64 iPCMFrame; - for (iPCMFrame = 0; iPCMFrame < frameCount; ++iPCMFrame) { - ma_uint32 iChannel; - for (iChannel = 0; iChannel < channels; ++iChannel) { - float* pDstF32 = (float*)ppDeinterleavedPCMFrames[iChannel]; - pDstF32[iPCMFrame] = pSrcF32[iPCMFrame*channels+iChannel]; - } - } - } break; - - default: - { - ma_uint32 sampleSizeInBytes = ma_get_bytes_per_sample(format); - ma_uint64 iPCMFrame; - for (iPCMFrame = 0; iPCMFrame < frameCount; ++iPCMFrame) { - ma_uint32 iChannel; - for (iChannel = 0; iChannel < channels; ++iChannel) { - void* pDst = ma_offset_ptr(ppDeinterleavedPCMFrames[iChannel], iPCMFrame*sampleSizeInBytes); - const void* pSrc = ma_offset_ptr(pInterleavedPCMFrames, (iPCMFrame*channels+iChannel)*sampleSizeInBytes); - memcpy(pDst, pSrc, sampleSizeInBytes); - } - } - } break; - } -} - -MA_API void ma_interleave_pcm_frames(ma_format format, ma_uint32 channels, ma_uint64 frameCount, const void** ppDeinterleavedPCMFrames, void* pInterleavedPCMFrames) -{ - switch (format) - { - case ma_format_s16: - { - ma_int16* pDstS16 = (ma_int16*)pInterleavedPCMFrames; - ma_uint64 iPCMFrame; - for (iPCMFrame = 0; iPCMFrame < frameCount; ++iPCMFrame) { - ma_uint32 iChannel; - for (iChannel = 0; iChannel < channels; ++iChannel) { - const ma_int16* pSrcS16 = (const ma_int16*)ppDeinterleavedPCMFrames[iChannel]; - pDstS16[iPCMFrame*channels+iChannel] = pSrcS16[iPCMFrame]; - } - } - } break; - - case ma_format_f32: - { - float* pDstF32 = (float*)pInterleavedPCMFrames; - ma_uint64 iPCMFrame; - for (iPCMFrame = 0; iPCMFrame < frameCount; ++iPCMFrame) { - ma_uint32 iChannel; - for (iChannel = 0; iChannel < channels; ++iChannel) { - const float* pSrcF32 = (const float*)ppDeinterleavedPCMFrames[iChannel]; - pDstF32[iPCMFrame*channels+iChannel] = pSrcF32[iPCMFrame]; - } - } - } break; - - default: - { - ma_uint32 sampleSizeInBytes = ma_get_bytes_per_sample(format); - ma_uint64 iPCMFrame; - for (iPCMFrame = 0; iPCMFrame < frameCount; ++iPCMFrame) { - ma_uint32 iChannel; - for (iChannel = 0; iChannel < channels; ++iChannel) { - void* pDst = ma_offset_ptr(pInterleavedPCMFrames, (iPCMFrame*channels+iChannel)*sampleSizeInBytes); - const void* pSrc = ma_offset_ptr(ppDeinterleavedPCMFrames[iChannel], iPCMFrame*sampleSizeInBytes); - memcpy(pDst, pSrc, sampleSizeInBytes); - } - } - } break; - } -} - - -/************************************************************************************************************************************************************** - -Biquad Filter - -**************************************************************************************************************************************************************/ -#ifndef MA_BIQUAD_FIXED_POINT_SHIFT -#define MA_BIQUAD_FIXED_POINT_SHIFT 14 -#endif - -static ma_int32 ma_biquad_float_to_fp(double x) -{ - return (ma_int32)(x * (1 << MA_BIQUAD_FIXED_POINT_SHIFT)); -} - -MA_API ma_biquad_config ma_biquad_config_init(ma_format format, ma_uint32 channels, double b0, double b1, double b2, double a0, double a1, double a2) -{ - ma_biquad_config config; - - MA_ZERO_OBJECT(&config); - config.format = format; - config.channels = channels; - config.b0 = b0; - config.b1 = b1; - config.b2 = b2; - config.a0 = a0; - config.a1 = a1; - config.a2 = a2; - - return config; -} - - -typedef struct -{ - size_t sizeInBytes; - size_t r1Offset; - size_t r2Offset; -} ma_biquad_heap_layout; - -static ma_result ma_biquad_get_heap_layout(const ma_biquad_config* pConfig, ma_biquad_heap_layout* pHeapLayout) -{ - MA_ASSERT(pHeapLayout != NULL); - - MA_ZERO_OBJECT(pHeapLayout); - - if (pConfig == NULL) { - return MA_INVALID_ARGS; - } - - if (pConfig->channels == 0) { - return MA_INVALID_ARGS; - } - - pHeapLayout->sizeInBytes = 0; - - /* R0 */ - pHeapLayout->r1Offset = pHeapLayout->sizeInBytes; - pHeapLayout->sizeInBytes += sizeof(ma_biquad_coefficient) * pConfig->channels; - - /* R1 */ - pHeapLayout->r2Offset = pHeapLayout->sizeInBytes; - pHeapLayout->sizeInBytes += sizeof(ma_biquad_coefficient) * pConfig->channels; - - /* Make sure allocation size is aligned. */ - pHeapLayout->sizeInBytes = ma_align_64(pHeapLayout->sizeInBytes); - - return MA_SUCCESS; -} - -MA_API ma_result ma_biquad_get_heap_size(const ma_biquad_config* pConfig, size_t* pHeapSizeInBytes) -{ - ma_result result; - ma_biquad_heap_layout heapLayout; - - if (pHeapSizeInBytes == NULL) { - return MA_INVALID_ARGS; - } - - *pHeapSizeInBytes = 0; - - result = ma_biquad_get_heap_layout(pConfig, &heapLayout); - if (result != MA_SUCCESS) { - return result; - } - - *pHeapSizeInBytes = heapLayout.sizeInBytes; - - return MA_SUCCESS; -} - -MA_API ma_result ma_biquad_init_preallocated(const ma_biquad_config* pConfig, void* pHeap, ma_biquad* pBQ) -{ - ma_result result; - ma_biquad_heap_layout heapLayout; - - if (pBQ == NULL) { - return MA_INVALID_ARGS; - } - - MA_ZERO_OBJECT(pBQ); - - result = ma_biquad_get_heap_layout(pConfig, &heapLayout); - if (result != MA_SUCCESS) { - return result; - } - - pBQ->_pHeap = pHeap; - MA_ZERO_MEMORY(pHeap, heapLayout.sizeInBytes); - - pBQ->pR1 = (ma_biquad_coefficient*)ma_offset_ptr(pHeap, heapLayout.r1Offset); - pBQ->pR2 = (ma_biquad_coefficient*)ma_offset_ptr(pHeap, heapLayout.r2Offset); - - return ma_biquad_reinit(pConfig, pBQ); -} - -MA_API ma_result ma_biquad_init(const ma_biquad_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_biquad* pBQ) -{ - ma_result result; - size_t heapSizeInBytes; - void* pHeap; - - result = ma_biquad_get_heap_size(pConfig, &heapSizeInBytes); - if (result != MA_SUCCESS) { - return result; - } - - if (heapSizeInBytes > 0) { - pHeap = ma_malloc(heapSizeInBytes, pAllocationCallbacks); - if (pHeap == NULL) { - return MA_OUT_OF_MEMORY; - } - } else { - pHeap = NULL; - } - - result = ma_biquad_init_preallocated(pConfig, pHeap, pBQ); - if (result != MA_SUCCESS) { - ma_free(pHeap, pAllocationCallbacks); - return result; - } - - pBQ->_ownsHeap = MA_TRUE; - return MA_SUCCESS; -} - -MA_API void ma_biquad_uninit(ma_biquad* pBQ, const ma_allocation_callbacks* pAllocationCallbacks) -{ - if (pBQ == NULL) { - return; - } - - if (pBQ->_ownsHeap) { - ma_free(pBQ->_pHeap, pAllocationCallbacks); - } -} - -MA_API ma_result ma_biquad_reinit(const ma_biquad_config* pConfig, ma_biquad* pBQ) -{ - if (pBQ == NULL || pConfig == NULL) { - return MA_INVALID_ARGS; - } - - if (pConfig->a0 == 0) { - return MA_INVALID_ARGS; /* Division by zero. */ - } - - /* Only supporting f32 and s16. */ - if (pConfig->format != ma_format_f32 && pConfig->format != ma_format_s16) { - return MA_INVALID_ARGS; - } - - /* The format cannot be changed after initialization. */ - if (pBQ->format != ma_format_unknown && pBQ->format != pConfig->format) { - return MA_INVALID_OPERATION; - } - - /* The channel count cannot be changed after initialization. */ - if (pBQ->channels != 0 && pBQ->channels != pConfig->channels) { - return MA_INVALID_OPERATION; - } - - - pBQ->format = pConfig->format; - pBQ->channels = pConfig->channels; - - /* Normalize. */ - if (pConfig->format == ma_format_f32) { - pBQ->b0.f32 = (float)(pConfig->b0 / pConfig->a0); - pBQ->b1.f32 = (float)(pConfig->b1 / pConfig->a0); - pBQ->b2.f32 = (float)(pConfig->b2 / pConfig->a0); - pBQ->a1.f32 = (float)(pConfig->a1 / pConfig->a0); - pBQ->a2.f32 = (float)(pConfig->a2 / pConfig->a0); - } else { - pBQ->b0.s32 = ma_biquad_float_to_fp(pConfig->b0 / pConfig->a0); - pBQ->b1.s32 = ma_biquad_float_to_fp(pConfig->b1 / pConfig->a0); - pBQ->b2.s32 = ma_biquad_float_to_fp(pConfig->b2 / pConfig->a0); - pBQ->a1.s32 = ma_biquad_float_to_fp(pConfig->a1 / pConfig->a0); - pBQ->a2.s32 = ma_biquad_float_to_fp(pConfig->a2 / pConfig->a0); - } - - return MA_SUCCESS; -} - -MA_API ma_result ma_biquad_clear_cache(ma_biquad* pBQ) -{ - if (pBQ == NULL) { - return MA_INVALID_ARGS; - } - - if (pBQ->format == ma_format_f32) { - pBQ->pR1->f32 = 0; - pBQ->pR2->f32 = 0; - } else { - pBQ->pR1->s32 = 0; - pBQ->pR2->s32 = 0; - } - - return MA_SUCCESS; -} - -static MA_INLINE void ma_biquad_process_pcm_frame_f32__direct_form_2_transposed(ma_biquad* pBQ, float* pY, const float* pX) -{ - ma_uint32 c; - const ma_uint32 channels = pBQ->channels; - const float b0 = pBQ->b0.f32; - const float b1 = pBQ->b1.f32; - const float b2 = pBQ->b2.f32; - const float a1 = pBQ->a1.f32; - const float a2 = pBQ->a2.f32; - - MA_ASSUME(channels > 0); - for (c = 0; c < channels; c += 1) { - float r1 = pBQ->pR1[c].f32; - float r2 = pBQ->pR2[c].f32; - float x = pX[c]; - float y; - - y = b0*x + r1; - r1 = b1*x - a1*y + r2; - r2 = b2*x - a2*y; - - pY[c] = y; - pBQ->pR1[c].f32 = r1; - pBQ->pR2[c].f32 = r2; - } -} - -static MA_INLINE void ma_biquad_process_pcm_frame_f32(ma_biquad* pBQ, float* pY, const float* pX) -{ - ma_biquad_process_pcm_frame_f32__direct_form_2_transposed(pBQ, pY, pX); -} - -static MA_INLINE void ma_biquad_process_pcm_frame_s16__direct_form_2_transposed(ma_biquad* pBQ, ma_int16* pY, const ma_int16* pX) -{ - ma_uint32 c; - const ma_uint32 channels = pBQ->channels; - const ma_int32 b0 = pBQ->b0.s32; - const ma_int32 b1 = pBQ->b1.s32; - const ma_int32 b2 = pBQ->b2.s32; - const ma_int32 a1 = pBQ->a1.s32; - const ma_int32 a2 = pBQ->a2.s32; - - MA_ASSUME(channels > 0); - for (c = 0; c < channels; c += 1) { - ma_int32 r1 = pBQ->pR1[c].s32; - ma_int32 r2 = pBQ->pR2[c].s32; - ma_int32 x = pX[c]; - ma_int32 y; - - y = (b0*x + r1) >> MA_BIQUAD_FIXED_POINT_SHIFT; - r1 = (b1*x - a1*y + r2); - r2 = (b2*x - a2*y); - - pY[c] = (ma_int16)ma_clamp(y, -32768, 32767); - pBQ->pR1[c].s32 = r1; - pBQ->pR2[c].s32 = r2; - } -} - -static MA_INLINE void ma_biquad_process_pcm_frame_s16(ma_biquad* pBQ, ma_int16* pY, const ma_int16* pX) -{ - ma_biquad_process_pcm_frame_s16__direct_form_2_transposed(pBQ, pY, pX); -} - -MA_API ma_result ma_biquad_process_pcm_frames(ma_biquad* pBQ, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount) -{ - ma_uint32 n; - - if (pBQ == NULL || pFramesOut == NULL || pFramesIn == NULL) { - return MA_INVALID_ARGS; - } - - /* Note that the logic below needs to support in-place filtering. That is, it must support the case where pFramesOut and pFramesIn are the same. */ - - if (pBQ->format == ma_format_f32) { - /* */ float* pY = ( float*)pFramesOut; - const float* pX = (const float*)pFramesIn; - - for (n = 0; n < frameCount; n += 1) { - ma_biquad_process_pcm_frame_f32__direct_form_2_transposed(pBQ, pY, pX); - pY += pBQ->channels; - pX += pBQ->channels; - } - } else if (pBQ->format == ma_format_s16) { - /* */ ma_int16* pY = ( ma_int16*)pFramesOut; - const ma_int16* pX = (const ma_int16*)pFramesIn; - - for (n = 0; n < frameCount; n += 1) { - ma_biquad_process_pcm_frame_s16__direct_form_2_transposed(pBQ, pY, pX); - pY += pBQ->channels; - pX += pBQ->channels; - } - } else { - MA_ASSERT(MA_FALSE); - return MA_INVALID_ARGS; /* Format not supported. Should never hit this because it's checked in ma_biquad_init() and ma_biquad_reinit(). */ - } - - return MA_SUCCESS; -} - -MA_API ma_uint32 ma_biquad_get_latency(const ma_biquad* pBQ) -{ - if (pBQ == NULL) { - return 0; - } - - return 2; -} - - -/************************************************************************************************************************************************************** - -Low-Pass Filter - -**************************************************************************************************************************************************************/ -MA_API ma_lpf1_config ma_lpf1_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRate, double cutoffFrequency) -{ - ma_lpf1_config config; - - MA_ZERO_OBJECT(&config); - config.format = format; - config.channels = channels; - config.sampleRate = sampleRate; - config.cutoffFrequency = cutoffFrequency; - config.q = 0.5; - - return config; -} - -MA_API ma_lpf2_config ma_lpf2_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRate, double cutoffFrequency, double q) -{ - ma_lpf2_config config; - - MA_ZERO_OBJECT(&config); - config.format = format; - config.channels = channels; - config.sampleRate = sampleRate; - config.cutoffFrequency = cutoffFrequency; - config.q = q; - - /* Q cannot be 0 or else it'll result in a division by 0. In this case just default to 0.707107. */ - if (config.q == 0) { - config.q = 0.707107; - } - - return config; -} - - -typedef struct -{ - size_t sizeInBytes; - size_t r1Offset; -} ma_lpf1_heap_layout; - -static ma_result ma_lpf1_get_heap_layout(const ma_lpf1_config* pConfig, ma_lpf1_heap_layout* pHeapLayout) -{ - MA_ASSERT(pHeapLayout != NULL); - - MA_ZERO_OBJECT(pHeapLayout); - - if (pConfig == NULL) { - return MA_INVALID_ARGS; - } - - if (pConfig->channels == 0) { - return MA_INVALID_ARGS; - } - - pHeapLayout->sizeInBytes = 0; - - /* R1 */ - pHeapLayout->r1Offset = pHeapLayout->sizeInBytes; - pHeapLayout->sizeInBytes += sizeof(ma_biquad_coefficient) * pConfig->channels; - - /* Make sure allocation size is aligned. */ - pHeapLayout->sizeInBytes = ma_align_64(pHeapLayout->sizeInBytes); - - return MA_SUCCESS; -} - -MA_API ma_result ma_lpf1_get_heap_size(const ma_lpf1_config* pConfig, size_t* pHeapSizeInBytes) -{ - ma_result result; - ma_lpf1_heap_layout heapLayout; - - if (pHeapSizeInBytes == NULL) { - return MA_INVALID_ARGS; - } - - result = ma_lpf1_get_heap_layout(pConfig, &heapLayout); - if (result != MA_SUCCESS) { - return result; - } - - *pHeapSizeInBytes = heapLayout.sizeInBytes; - - return MA_SUCCESS; -} - -MA_API ma_result ma_lpf1_init_preallocated(const ma_lpf1_config* pConfig, void* pHeap, ma_lpf1* pLPF) -{ - ma_result result; - ma_lpf1_heap_layout heapLayout; - - if (pLPF == NULL) { - return MA_INVALID_ARGS; - } - - MA_ZERO_OBJECT(pLPF); - - result = ma_lpf1_get_heap_layout(pConfig, &heapLayout); - if (result != MA_SUCCESS) { - return result; - } - - pLPF->_pHeap = pHeap; - MA_ZERO_MEMORY(pHeap, heapLayout.sizeInBytes); - - pLPF->pR1 = (ma_biquad_coefficient*)ma_offset_ptr(pHeap, heapLayout.r1Offset); - - return ma_lpf1_reinit(pConfig, pLPF); -} - -MA_API ma_result ma_lpf1_init(const ma_lpf1_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_lpf1* pLPF) -{ - ma_result result; - size_t heapSizeInBytes; - void* pHeap; - - result = ma_lpf1_get_heap_size(pConfig, &heapSizeInBytes); - if (result != MA_SUCCESS) { - return result; - } - - if (heapSizeInBytes > 0) { - pHeap = ma_malloc(heapSizeInBytes, pAllocationCallbacks); - if (pHeap == NULL) { - return MA_OUT_OF_MEMORY; - } - } else { - pHeap = NULL; - } - - result = ma_lpf1_init_preallocated(pConfig, pHeap, pLPF); - if (result != MA_SUCCESS) { - ma_free(pHeap, pAllocationCallbacks); - return result; - } - - pLPF->_ownsHeap = MA_TRUE; - return MA_SUCCESS; -} - -MA_API void ma_lpf1_uninit(ma_lpf1* pLPF, const ma_allocation_callbacks* pAllocationCallbacks) -{ - if (pLPF == NULL) { - return; - } - - if (pLPF->_ownsHeap) { - ma_free(pLPF->_pHeap, pAllocationCallbacks); - } -} - -MA_API ma_result ma_lpf1_reinit(const ma_lpf1_config* pConfig, ma_lpf1* pLPF) -{ - double a; - - if (pLPF == NULL || pConfig == NULL) { - return MA_INVALID_ARGS; - } - - /* Only supporting f32 and s16. */ - if (pConfig->format != ma_format_f32 && pConfig->format != ma_format_s16) { - return MA_INVALID_ARGS; - } - - /* The format cannot be changed after initialization. */ - if (pLPF->format != ma_format_unknown && pLPF->format != pConfig->format) { - return MA_INVALID_OPERATION; - } - - /* The channel count cannot be changed after initialization. */ - if (pLPF->channels != 0 && pLPF->channels != pConfig->channels) { - return MA_INVALID_OPERATION; - } - - pLPF->format = pConfig->format; - pLPF->channels = pConfig->channels; - - a = ma_expd(-2 * MA_PI_D * pConfig->cutoffFrequency / pConfig->sampleRate); - if (pConfig->format == ma_format_f32) { - pLPF->a.f32 = (float)a; - } else { - pLPF->a.s32 = ma_biquad_float_to_fp(a); - } - - return MA_SUCCESS; -} - -MA_API ma_result ma_lpf1_clear_cache(ma_lpf1* pLPF) -{ - if (pLPF == NULL) { - return MA_INVALID_ARGS; - } - - if (pLPF->format == ma_format_f32) { - pLPF->a.f32 = 0; - } else { - pLPF->a.s32 = 0; - } - - return MA_SUCCESS; -} - -static MA_INLINE void ma_lpf1_process_pcm_frame_f32(ma_lpf1* pLPF, float* pY, const float* pX) -{ - ma_uint32 c; - const ma_uint32 channels = pLPF->channels; - const float a = pLPF->a.f32; - const float b = 1 - a; - - MA_ASSUME(channels > 0); - for (c = 0; c < channels; c += 1) { - float r1 = pLPF->pR1[c].f32; - float x = pX[c]; - float y; - - y = b*x + a*r1; - - pY[c] = y; - pLPF->pR1[c].f32 = y; - } -} - -static MA_INLINE void ma_lpf1_process_pcm_frame_s16(ma_lpf1* pLPF, ma_int16* pY, const ma_int16* pX) -{ - ma_uint32 c; - const ma_uint32 channels = pLPF->channels; - const ma_int32 a = pLPF->a.s32; - const ma_int32 b = ((1 << MA_BIQUAD_FIXED_POINT_SHIFT) - a); - - MA_ASSUME(channels > 0); - for (c = 0; c < channels; c += 1) { - ma_int32 r1 = pLPF->pR1[c].s32; - ma_int32 x = pX[c]; - ma_int32 y; - - y = (b*x + a*r1) >> MA_BIQUAD_FIXED_POINT_SHIFT; - - pY[c] = (ma_int16)y; - pLPF->pR1[c].s32 = (ma_int32)y; - } -} - -MA_API ma_result ma_lpf1_process_pcm_frames(ma_lpf1* pLPF, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount) -{ - ma_uint32 n; - - if (pLPF == NULL || pFramesOut == NULL || pFramesIn == NULL) { - return MA_INVALID_ARGS; - } - - /* Note that the logic below needs to support in-place filtering. That is, it must support the case where pFramesOut and pFramesIn are the same. */ - - if (pLPF->format == ma_format_f32) { - /* */ float* pY = ( float*)pFramesOut; - const float* pX = (const float*)pFramesIn; - - for (n = 0; n < frameCount; n += 1) { - ma_lpf1_process_pcm_frame_f32(pLPF, pY, pX); - pY += pLPF->channels; - pX += pLPF->channels; - } - } else if (pLPF->format == ma_format_s16) { - /* */ ma_int16* pY = ( ma_int16*)pFramesOut; - const ma_int16* pX = (const ma_int16*)pFramesIn; - - for (n = 0; n < frameCount; n += 1) { - ma_lpf1_process_pcm_frame_s16(pLPF, pY, pX); - pY += pLPF->channels; - pX += pLPF->channels; - } - } else { - MA_ASSERT(MA_FALSE); - return MA_INVALID_ARGS; /* Format not supported. Should never hit this because it's checked in ma_biquad_init() and ma_biquad_reinit(). */ - } - - return MA_SUCCESS; -} - -MA_API ma_uint32 ma_lpf1_get_latency(const ma_lpf1* pLPF) -{ - if (pLPF == NULL) { - return 0; - } - - return 1; -} - - -static MA_INLINE ma_biquad_config ma_lpf2__get_biquad_config(const ma_lpf2_config* pConfig) -{ - ma_biquad_config bqConfig; - double q; - double w; - double s; - double c; - double a; - - MA_ASSERT(pConfig != NULL); - - q = pConfig->q; - w = 2 * MA_PI_D * pConfig->cutoffFrequency / pConfig->sampleRate; - s = ma_sind(w); - c = ma_cosd(w); - a = s / (2*q); - - bqConfig.b0 = (1 - c) / 2; - bqConfig.b1 = 1 - c; - bqConfig.b2 = (1 - c) / 2; - bqConfig.a0 = 1 + a; - bqConfig.a1 = -2 * c; - bqConfig.a2 = 1 - a; - - bqConfig.format = pConfig->format; - bqConfig.channels = pConfig->channels; - - return bqConfig; -} - -MA_API ma_result ma_lpf2_get_heap_size(const ma_lpf2_config* pConfig, size_t* pHeapSizeInBytes) -{ - ma_biquad_config bqConfig; - bqConfig = ma_lpf2__get_biquad_config(pConfig); - - return ma_biquad_get_heap_size(&bqConfig, pHeapSizeInBytes); -} - -MA_API ma_result ma_lpf2_init_preallocated(const ma_lpf2_config* pConfig, void* pHeap, ma_lpf2* pLPF) -{ - ma_result result; - ma_biquad_config bqConfig; - - if (pLPF == NULL) { - return MA_INVALID_ARGS; - } - - MA_ZERO_OBJECT(pLPF); - - if (pConfig == NULL) { - return MA_INVALID_ARGS; - } - - bqConfig = ma_lpf2__get_biquad_config(pConfig); - result = ma_biquad_init_preallocated(&bqConfig, pHeap, &pLPF->bq); - if (result != MA_SUCCESS) { - return result; - } - - return MA_SUCCESS; -} - -MA_API ma_result ma_lpf2_init(const ma_lpf2_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_lpf2* pLPF) -{ - ma_result result; - size_t heapSizeInBytes; - void* pHeap; - - result = ma_lpf2_get_heap_size(pConfig, &heapSizeInBytes); - if (result != MA_SUCCESS) { - return result; - } - - if (heapSizeInBytes > 0) { - pHeap = ma_malloc(heapSizeInBytes, pAllocationCallbacks); - if (pHeap == NULL) { - return MA_OUT_OF_MEMORY; - } - } else { - pHeap = NULL; - } - - result = ma_lpf2_init_preallocated(pConfig, pHeap, pLPF); - if (result != MA_SUCCESS) { - ma_free(pHeap, pAllocationCallbacks); - return result; - } - - pLPF->bq._ownsHeap = MA_TRUE; /* <-- This will cause the biquad to take ownership of the heap and free it when it's uninitialized. */ - return MA_SUCCESS; -} - -MA_API void ma_lpf2_uninit(ma_lpf2* pLPF, const ma_allocation_callbacks* pAllocationCallbacks) -{ - if (pLPF == NULL) { - return; - } - - ma_biquad_uninit(&pLPF->bq, pAllocationCallbacks); /* <-- This will free the heap allocation. */ -} - -MA_API ma_result ma_lpf2_reinit(const ma_lpf2_config* pConfig, ma_lpf2* pLPF) -{ - ma_result result; - ma_biquad_config bqConfig; - - if (pLPF == NULL || pConfig == NULL) { - return MA_INVALID_ARGS; - } - - bqConfig = ma_lpf2__get_biquad_config(pConfig); - result = ma_biquad_reinit(&bqConfig, &pLPF->bq); - if (result != MA_SUCCESS) { - return result; - } - - return MA_SUCCESS; -} - -MA_API ma_result ma_lpf2_clear_cache(ma_lpf2* pLPF) -{ - if (pLPF == NULL) { - return MA_INVALID_ARGS; - } - - ma_biquad_clear_cache(&pLPF->bq); - - return MA_SUCCESS; -} - -static MA_INLINE void ma_lpf2_process_pcm_frame_s16(ma_lpf2* pLPF, ma_int16* pFrameOut, const ma_int16* pFrameIn) -{ - ma_biquad_process_pcm_frame_s16(&pLPF->bq, pFrameOut, pFrameIn); -} - -static MA_INLINE void ma_lpf2_process_pcm_frame_f32(ma_lpf2* pLPF, float* pFrameOut, const float* pFrameIn) -{ - ma_biquad_process_pcm_frame_f32(&pLPF->bq, pFrameOut, pFrameIn); -} - -MA_API ma_result ma_lpf2_process_pcm_frames(ma_lpf2* pLPF, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount) -{ - if (pLPF == NULL) { - return MA_INVALID_ARGS; - } - - return ma_biquad_process_pcm_frames(&pLPF->bq, pFramesOut, pFramesIn, frameCount); -} - -MA_API ma_uint32 ma_lpf2_get_latency(const ma_lpf2* pLPF) -{ - if (pLPF == NULL) { - return 0; - } - - return ma_biquad_get_latency(&pLPF->bq); -} - - -MA_API ma_lpf_config ma_lpf_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRate, double cutoffFrequency, ma_uint32 order) -{ - ma_lpf_config config; - - MA_ZERO_OBJECT(&config); - config.format = format; - config.channels = channels; - config.sampleRate = sampleRate; - config.cutoffFrequency = cutoffFrequency; - config.order = ma_min(order, MA_MAX_FILTER_ORDER); - - return config; -} - - -typedef struct -{ - size_t sizeInBytes; - size_t lpf1Offset; - size_t lpf2Offset; /* Offset of the first second order filter. Subsequent filters will come straight after, and will each have the same heap size. */ -} ma_lpf_heap_layout; - -static void ma_lpf_calculate_sub_lpf_counts(ma_uint32 order, ma_uint32* pLPF1Count, ma_uint32* pLPF2Count) -{ - MA_ASSERT(pLPF1Count != NULL); - MA_ASSERT(pLPF2Count != NULL); - - *pLPF1Count = order % 2; - *pLPF2Count = order / 2; -} - -static ma_result ma_lpf_get_heap_layout(const ma_lpf_config* pConfig, ma_lpf_heap_layout* pHeapLayout) -{ - ma_result result; - ma_uint32 lpf1Count; - ma_uint32 lpf2Count; - ma_uint32 ilpf1; - ma_uint32 ilpf2; - - MA_ASSERT(pHeapLayout != NULL); - - MA_ZERO_OBJECT(pHeapLayout); - - if (pConfig == NULL) { - return MA_INVALID_ARGS; - } - - if (pConfig->channels == 0) { - return MA_INVALID_ARGS; - } - - if (pConfig->order > MA_MAX_FILTER_ORDER) { - return MA_INVALID_ARGS; - } - - ma_lpf_calculate_sub_lpf_counts(pConfig->order, &lpf1Count, &lpf2Count); - - pHeapLayout->sizeInBytes = 0; - - /* LPF 1 */ - pHeapLayout->lpf1Offset = pHeapLayout->sizeInBytes; - for (ilpf1 = 0; ilpf1 < lpf1Count; ilpf1 += 1) { - size_t lpf1HeapSizeInBytes; - ma_lpf1_config lpf1Config = ma_lpf1_config_init(pConfig->format, pConfig->channels, pConfig->sampleRate, pConfig->cutoffFrequency); - - result = ma_lpf1_get_heap_size(&lpf1Config, &lpf1HeapSizeInBytes); - if (result != MA_SUCCESS) { - return result; - } - - pHeapLayout->sizeInBytes += sizeof(ma_lpf1) + lpf1HeapSizeInBytes; - } - - /* LPF 2*/ - pHeapLayout->lpf2Offset = pHeapLayout->sizeInBytes; - for (ilpf2 = 0; ilpf2 < lpf2Count; ilpf2 += 1) { - size_t lpf2HeapSizeInBytes; - ma_lpf2_config lpf2Config = ma_lpf2_config_init(pConfig->format, pConfig->channels, pConfig->sampleRate, pConfig->cutoffFrequency, 0.707107); /* <-- The "q" parameter does not matter for the purpose of calculating the heap size. */ - - result = ma_lpf2_get_heap_size(&lpf2Config, &lpf2HeapSizeInBytes); - if (result != MA_SUCCESS) { - return result; - } - - pHeapLayout->sizeInBytes += sizeof(ma_lpf2) + lpf2HeapSizeInBytes; - } - - /* Make sure allocation size is aligned. */ - pHeapLayout->sizeInBytes = ma_align_64(pHeapLayout->sizeInBytes); - - return MA_SUCCESS; -} - -static ma_result ma_lpf_reinit__internal(const ma_lpf_config* pConfig, void* pHeap, ma_lpf* pLPF, ma_bool32 isNew) -{ - ma_result result; - ma_uint32 lpf1Count; - ma_uint32 lpf2Count; - ma_uint32 ilpf1; - ma_uint32 ilpf2; - ma_lpf_heap_layout heapLayout; /* Only used if isNew is true. */ - - if (pLPF == NULL || pConfig == NULL) { - return MA_INVALID_ARGS; - } - - /* Only supporting f32 and s16. */ - if (pConfig->format != ma_format_f32 && pConfig->format != ma_format_s16) { - return MA_INVALID_ARGS; - } - - /* The format cannot be changed after initialization. */ - if (pLPF->format != ma_format_unknown && pLPF->format != pConfig->format) { - return MA_INVALID_OPERATION; - } - - /* The channel count cannot be changed after initialization. */ - if (pLPF->channels != 0 && pLPF->channels != pConfig->channels) { - return MA_INVALID_OPERATION; - } - - if (pConfig->order > MA_MAX_FILTER_ORDER) { - return MA_INVALID_ARGS; - } - - ma_lpf_calculate_sub_lpf_counts(pConfig->order, &lpf1Count, &lpf2Count); - - /* The filter order can't change between reinits. */ - if (!isNew) { - if (pLPF->lpf1Count != lpf1Count || pLPF->lpf2Count != lpf2Count) { - return MA_INVALID_OPERATION; - } - } - - if (isNew) { - result = ma_lpf_get_heap_layout(pConfig, &heapLayout); - if (result != MA_SUCCESS) { - return result; - } - - pLPF->_pHeap = pHeap; - MA_ZERO_MEMORY(pHeap, heapLayout.sizeInBytes); - - pLPF->pLPF1 = (ma_lpf1*)ma_offset_ptr(pHeap, heapLayout.lpf1Offset); - pLPF->pLPF2 = (ma_lpf2*)ma_offset_ptr(pHeap, heapLayout.lpf2Offset); - } else { - MA_ZERO_OBJECT(&heapLayout); /* To silence a compiler warning. */ - } - - for (ilpf1 = 0; ilpf1 < lpf1Count; ilpf1 += 1) { - ma_lpf1_config lpf1Config = ma_lpf1_config_init(pConfig->format, pConfig->channels, pConfig->sampleRate, pConfig->cutoffFrequency); - - if (isNew) { - size_t lpf1HeapSizeInBytes; - - result = ma_lpf1_get_heap_size(&lpf1Config, &lpf1HeapSizeInBytes); - if (result == MA_SUCCESS) { - result = ma_lpf1_init_preallocated(&lpf1Config, ma_offset_ptr(pHeap, heapLayout.lpf1Offset + (sizeof(ma_lpf1) * lpf1Count) + (ilpf1 * lpf1HeapSizeInBytes)), &pLPF->pLPF1[ilpf1]); - } - } else { - result = ma_lpf1_reinit(&lpf1Config, &pLPF->pLPF1[ilpf1]); - } - - if (result != MA_SUCCESS) { - ma_uint32 jlpf1; - - for (jlpf1 = 0; jlpf1 < ilpf1; jlpf1 += 1) { - ma_lpf1_uninit(&pLPF->pLPF1[jlpf1], NULL); /* No need for allocation callbacks here since we used a preallocated heap allocation. */ - } - - return result; - } - } - - for (ilpf2 = 0; ilpf2 < lpf2Count; ilpf2 += 1) { - ma_lpf2_config lpf2Config; - double q; - double a; - - /* Tempting to use 0.707107, but won't result in a Butterworth filter if the order is > 2. */ - if (lpf1Count == 1) { - a = (1 + ilpf2*1) * (MA_PI_D/(pConfig->order*1)); /* Odd order. */ - } else { - a = (1 + ilpf2*2) * (MA_PI_D/(pConfig->order*2)); /* Even order. */ - } - q = 1 / (2*ma_cosd(a)); - - lpf2Config = ma_lpf2_config_init(pConfig->format, pConfig->channels, pConfig->sampleRate, pConfig->cutoffFrequency, q); - - if (isNew) { - size_t lpf2HeapSizeInBytes; - - result = ma_lpf2_get_heap_size(&lpf2Config, &lpf2HeapSizeInBytes); - if (result == MA_SUCCESS) { - result = ma_lpf2_init_preallocated(&lpf2Config, ma_offset_ptr(pHeap, heapLayout.lpf2Offset + (sizeof(ma_lpf2) * lpf2Count) + (ilpf2 * lpf2HeapSizeInBytes)), &pLPF->pLPF2[ilpf2]); - } - } else { - result = ma_lpf2_reinit(&lpf2Config, &pLPF->pLPF2[ilpf2]); - } - - if (result != MA_SUCCESS) { - ma_uint32 jlpf1; - ma_uint32 jlpf2; - - for (jlpf1 = 0; jlpf1 < lpf1Count; jlpf1 += 1) { - ma_lpf1_uninit(&pLPF->pLPF1[jlpf1], NULL); /* No need for allocation callbacks here since we used a preallocated heap allocation. */ - } - - for (jlpf2 = 0; jlpf2 < ilpf2; jlpf2 += 1) { - ma_lpf2_uninit(&pLPF->pLPF2[jlpf2], NULL); /* No need for allocation callbacks here since we used a preallocated heap allocation. */ - } - - return result; - } - } - - pLPF->lpf1Count = lpf1Count; - pLPF->lpf2Count = lpf2Count; - pLPF->format = pConfig->format; - pLPF->channels = pConfig->channels; - pLPF->sampleRate = pConfig->sampleRate; - - return MA_SUCCESS; -} - -MA_API ma_result ma_lpf_get_heap_size(const ma_lpf_config* pConfig, size_t* pHeapSizeInBytes) -{ - ma_result result; - ma_lpf_heap_layout heapLayout; - - if (pHeapSizeInBytes == NULL) { - return MA_INVALID_ARGS; - } - - *pHeapSizeInBytes = 0; - - result = ma_lpf_get_heap_layout(pConfig, &heapLayout); - if (result != MA_SUCCESS) { - return result; - } - - *pHeapSizeInBytes = heapLayout.sizeInBytes; - - return result; -} - -MA_API ma_result ma_lpf_init_preallocated(const ma_lpf_config* pConfig, void* pHeap, ma_lpf* pLPF) -{ - if (pLPF == NULL) { - return MA_INVALID_ARGS; - } - - MA_ZERO_OBJECT(pLPF); - - return ma_lpf_reinit__internal(pConfig, pHeap, pLPF, /*isNew*/MA_TRUE); -} - -MA_API ma_result ma_lpf_init(const ma_lpf_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_lpf* pLPF) -{ - ma_result result; - size_t heapSizeInBytes; - void* pHeap; - - result = ma_lpf_get_heap_size(pConfig, &heapSizeInBytes); - if (result != MA_SUCCESS) { - return result; - } - - if (heapSizeInBytes > 0) { - pHeap = ma_malloc(heapSizeInBytes, pAllocationCallbacks); - if (pHeap == NULL) { - return MA_OUT_OF_MEMORY; - } - } else { - pHeap = NULL; - } - - result = ma_lpf_init_preallocated(pConfig, pHeap, pLPF); - if (result != MA_SUCCESS) { - ma_free(pHeap, pAllocationCallbacks); - return result; - } - - pLPF->_ownsHeap = MA_TRUE; - return MA_SUCCESS; -} - -MA_API void ma_lpf_uninit(ma_lpf* pLPF, const ma_allocation_callbacks* pAllocationCallbacks) -{ - ma_uint32 ilpf1; - ma_uint32 ilpf2; - - if (pLPF == NULL) { - return; - } - - for (ilpf1 = 0; ilpf1 < pLPF->lpf1Count; ilpf1 += 1) { - ma_lpf1_uninit(&pLPF->pLPF1[ilpf1], pAllocationCallbacks); - } - - for (ilpf2 = 0; ilpf2 < pLPF->lpf2Count; ilpf2 += 1) { - ma_lpf2_uninit(&pLPF->pLPF2[ilpf2], pAllocationCallbacks); - } - - if (pLPF->_ownsHeap) { - ma_free(pLPF->_pHeap, pAllocationCallbacks); - } -} - -MA_API ma_result ma_lpf_reinit(const ma_lpf_config* pConfig, ma_lpf* pLPF) -{ - return ma_lpf_reinit__internal(pConfig, NULL, pLPF, /*isNew*/MA_FALSE); -} - -MA_API ma_result ma_lpf_clear_cache(ma_lpf* pLPF) -{ - ma_uint32 ilpf1; - ma_uint32 ilpf2; - - if (pLPF == NULL) { - return MA_INVALID_ARGS; - } - - for (ilpf1 = 0; ilpf1 < pLPF->lpf1Count; ilpf1 += 1) { - ma_lpf1_clear_cache(&pLPF->pLPF1[ilpf1]); - } - - for (ilpf2 = 0; ilpf2 < pLPF->lpf2Count; ilpf2 += 1) { - ma_lpf2_clear_cache(&pLPF->pLPF2[ilpf2]); - } - - return MA_SUCCESS; -} - -static MA_INLINE void ma_lpf_process_pcm_frame_f32(ma_lpf* pLPF, float* pY, const void* pX) -{ - ma_uint32 ilpf1; - ma_uint32 ilpf2; - - MA_ASSERT(pLPF->format == ma_format_f32); - - MA_MOVE_MEMORY(pY, pX, ma_get_bytes_per_frame(pLPF->format, pLPF->channels)); - - for (ilpf1 = 0; ilpf1 < pLPF->lpf1Count; ilpf1 += 1) { - ma_lpf1_process_pcm_frame_f32(&pLPF->pLPF1[ilpf1], pY, pY); - } - - for (ilpf2 = 0; ilpf2 < pLPF->lpf2Count; ilpf2 += 1) { - ma_lpf2_process_pcm_frame_f32(&pLPF->pLPF2[ilpf2], pY, pY); - } -} - -static MA_INLINE void ma_lpf_process_pcm_frame_s16(ma_lpf* pLPF, ma_int16* pY, const ma_int16* pX) -{ - ma_uint32 ilpf1; - ma_uint32 ilpf2; - - MA_ASSERT(pLPF->format == ma_format_s16); - - MA_MOVE_MEMORY(pY, pX, ma_get_bytes_per_frame(pLPF->format, pLPF->channels)); - - for (ilpf1 = 0; ilpf1 < pLPF->lpf1Count; ilpf1 += 1) { - ma_lpf1_process_pcm_frame_s16(&pLPF->pLPF1[ilpf1], pY, pY); - } - - for (ilpf2 = 0; ilpf2 < pLPF->lpf2Count; ilpf2 += 1) { - ma_lpf2_process_pcm_frame_s16(&pLPF->pLPF2[ilpf2], pY, pY); - } -} - -MA_API ma_result ma_lpf_process_pcm_frames(ma_lpf* pLPF, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount) -{ - ma_result result; - ma_uint32 ilpf1; - ma_uint32 ilpf2; - - if (pLPF == NULL) { - return MA_INVALID_ARGS; - } - - /* Faster path for in-place. */ - if (pFramesOut == pFramesIn) { - for (ilpf1 = 0; ilpf1 < pLPF->lpf1Count; ilpf1 += 1) { - result = ma_lpf1_process_pcm_frames(&pLPF->pLPF1[ilpf1], pFramesOut, pFramesOut, frameCount); - if (result != MA_SUCCESS) { - return result; - } - } - - for (ilpf2 = 0; ilpf2 < pLPF->lpf2Count; ilpf2 += 1) { - result = ma_lpf2_process_pcm_frames(&pLPF->pLPF2[ilpf2], pFramesOut, pFramesOut, frameCount); - if (result != MA_SUCCESS) { - return result; - } - } - } - - /* Slightly slower path for copying. */ - if (pFramesOut != pFramesIn) { - ma_uint32 iFrame; - - /* */ if (pLPF->format == ma_format_f32) { - /* */ float* pFramesOutF32 = ( float*)pFramesOut; - const float* pFramesInF32 = (const float*)pFramesIn; - - for (iFrame = 0; iFrame < frameCount; iFrame += 1) { - ma_lpf_process_pcm_frame_f32(pLPF, pFramesOutF32, pFramesInF32); - pFramesOutF32 += pLPF->channels; - pFramesInF32 += pLPF->channels; - } - } else if (pLPF->format == ma_format_s16) { - /* */ ma_int16* pFramesOutS16 = ( ma_int16*)pFramesOut; - const ma_int16* pFramesInS16 = (const ma_int16*)pFramesIn; - - for (iFrame = 0; iFrame < frameCount; iFrame += 1) { - ma_lpf_process_pcm_frame_s16(pLPF, pFramesOutS16, pFramesInS16); - pFramesOutS16 += pLPF->channels; - pFramesInS16 += pLPF->channels; - } - } else { - MA_ASSERT(MA_FALSE); - return MA_INVALID_OPERATION; /* Should never hit this. */ - } - } - - return MA_SUCCESS; -} - -MA_API ma_uint32 ma_lpf_get_latency(const ma_lpf* pLPF) -{ - if (pLPF == NULL) { - return 0; - } - - return pLPF->lpf2Count*2 + pLPF->lpf1Count; -} - - -/************************************************************************************************************************************************************** - -High-Pass Filtering - -**************************************************************************************************************************************************************/ -MA_API ma_hpf1_config ma_hpf1_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRate, double cutoffFrequency) -{ - ma_hpf1_config config; - - MA_ZERO_OBJECT(&config); - config.format = format; - config.channels = channels; - config.sampleRate = sampleRate; - config.cutoffFrequency = cutoffFrequency; - - return config; -} - -MA_API ma_hpf2_config ma_hpf2_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRate, double cutoffFrequency, double q) -{ - ma_hpf2_config config; - - MA_ZERO_OBJECT(&config); - config.format = format; - config.channels = channels; - config.sampleRate = sampleRate; - config.cutoffFrequency = cutoffFrequency; - config.q = q; - - /* Q cannot be 0 or else it'll result in a division by 0. In this case just default to 0.707107. */ - if (config.q == 0) { - config.q = 0.707107; - } - - return config; -} - - -typedef struct -{ - size_t sizeInBytes; - size_t r1Offset; -} ma_hpf1_heap_layout; - -static ma_result ma_hpf1_get_heap_layout(const ma_hpf1_config* pConfig, ma_hpf1_heap_layout* pHeapLayout) -{ - MA_ASSERT(pHeapLayout != NULL); - - MA_ZERO_OBJECT(pHeapLayout); - - if (pConfig == NULL) { - return MA_INVALID_ARGS; - } - - if (pConfig->channels == 0) { - return MA_INVALID_ARGS; - } - - pHeapLayout->sizeInBytes = 0; - - /* R1 */ - pHeapLayout->r1Offset = pHeapLayout->sizeInBytes; - pHeapLayout->sizeInBytes += sizeof(ma_biquad_coefficient) * pConfig->channels; - - /* Make sure allocation size is aligned. */ - pHeapLayout->sizeInBytes = ma_align_64(pHeapLayout->sizeInBytes); - - return MA_SUCCESS; -} - -MA_API ma_result ma_hpf1_get_heap_size(const ma_hpf1_config* pConfig, size_t* pHeapSizeInBytes) -{ - ma_result result; - ma_hpf1_heap_layout heapLayout; - - if (pHeapSizeInBytes == NULL) { - return MA_INVALID_ARGS; - } - - result = ma_hpf1_get_heap_layout(pConfig, &heapLayout); - if (result != MA_SUCCESS) { - return result; - } - - *pHeapSizeInBytes = heapLayout.sizeInBytes; - - return MA_SUCCESS; -} - -MA_API ma_result ma_hpf1_init_preallocated(const ma_hpf1_config* pConfig, void* pHeap, ma_hpf1* pLPF) -{ - ma_result result; - ma_hpf1_heap_layout heapLayout; - - if (pLPF == NULL) { - return MA_INVALID_ARGS; - } - - MA_ZERO_OBJECT(pLPF); - - result = ma_hpf1_get_heap_layout(pConfig, &heapLayout); - if (result != MA_SUCCESS) { - return result; - } - - pLPF->_pHeap = pHeap; - MA_ZERO_MEMORY(pHeap, heapLayout.sizeInBytes); - - pLPF->pR1 = (ma_biquad_coefficient*)ma_offset_ptr(pHeap, heapLayout.r1Offset); - - return ma_hpf1_reinit(pConfig, pLPF); -} - -MA_API ma_result ma_hpf1_init(const ma_hpf1_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_hpf1* pLPF) -{ - ma_result result; - size_t heapSizeInBytes; - void* pHeap; - - result = ma_hpf1_get_heap_size(pConfig, &heapSizeInBytes); - if (result != MA_SUCCESS) { - return result; - } - - if (heapSizeInBytes > 0) { - pHeap = ma_malloc(heapSizeInBytes, pAllocationCallbacks); - if (pHeap == NULL) { - return MA_OUT_OF_MEMORY; - } - } else { - pHeap = NULL; - } - - result = ma_hpf1_init_preallocated(pConfig, pHeap, pLPF); - if (result != MA_SUCCESS) { - ma_free(pHeap, pAllocationCallbacks); - return result; - } - - pLPF->_ownsHeap = MA_TRUE; - return MA_SUCCESS; -} - -MA_API void ma_hpf1_uninit(ma_hpf1* pHPF, const ma_allocation_callbacks* pAllocationCallbacks) -{ - if (pHPF == NULL) { - return; - } - - if (pHPF->_ownsHeap) { - ma_free(pHPF->_pHeap, pAllocationCallbacks); - } -} - -MA_API ma_result ma_hpf1_reinit(const ma_hpf1_config* pConfig, ma_hpf1* pHPF) -{ - double a; - - if (pHPF == NULL || pConfig == NULL) { - return MA_INVALID_ARGS; - } - - /* Only supporting f32 and s16. */ - if (pConfig->format != ma_format_f32 && pConfig->format != ma_format_s16) { - return MA_INVALID_ARGS; - } - - /* The format cannot be changed after initialization. */ - if (pHPF->format != ma_format_unknown && pHPF->format != pConfig->format) { - return MA_INVALID_OPERATION; - } - - /* The channel count cannot be changed after initialization. */ - if (pHPF->channels != 0 && pHPF->channels != pConfig->channels) { - return MA_INVALID_OPERATION; - } - - pHPF->format = pConfig->format; - pHPF->channels = pConfig->channels; - - a = ma_expd(-2 * MA_PI_D * pConfig->cutoffFrequency / pConfig->sampleRate); - if (pConfig->format == ma_format_f32) { - pHPF->a.f32 = (float)a; - } else { - pHPF->a.s32 = ma_biquad_float_to_fp(a); - } - - return MA_SUCCESS; -} - -static MA_INLINE void ma_hpf1_process_pcm_frame_f32(ma_hpf1* pHPF, float* pY, const float* pX) -{ - ma_uint32 c; - const ma_uint32 channels = pHPF->channels; - const float a = 1 - pHPF->a.f32; - const float b = 1 - a; - - MA_ASSUME(channels > 0); - for (c = 0; c < channels; c += 1) { - float r1 = pHPF->pR1[c].f32; - float x = pX[c]; - float y; - - y = b*x - a*r1; - - pY[c] = y; - pHPF->pR1[c].f32 = y; - } -} - -static MA_INLINE void ma_hpf1_process_pcm_frame_s16(ma_hpf1* pHPF, ma_int16* pY, const ma_int16* pX) -{ - ma_uint32 c; - const ma_uint32 channels = pHPF->channels; - const ma_int32 a = ((1 << MA_BIQUAD_FIXED_POINT_SHIFT) - pHPF->a.s32); - const ma_int32 b = ((1 << MA_BIQUAD_FIXED_POINT_SHIFT) - a); - - MA_ASSUME(channels > 0); - for (c = 0; c < channels; c += 1) { - ma_int32 r1 = pHPF->pR1[c].s32; - ma_int32 x = pX[c]; - ma_int32 y; - - y = (b*x - a*r1) >> MA_BIQUAD_FIXED_POINT_SHIFT; - - pY[c] = (ma_int16)y; - pHPF->pR1[c].s32 = (ma_int32)y; - } -} - -MA_API ma_result ma_hpf1_process_pcm_frames(ma_hpf1* pHPF, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount) -{ - ma_uint32 n; - - if (pHPF == NULL || pFramesOut == NULL || pFramesIn == NULL) { - return MA_INVALID_ARGS; - } - - /* Note that the logic below needs to support in-place filtering. That is, it must support the case where pFramesOut and pFramesIn are the same. */ - - if (pHPF->format == ma_format_f32) { - /* */ float* pY = ( float*)pFramesOut; - const float* pX = (const float*)pFramesIn; - - for (n = 0; n < frameCount; n += 1) { - ma_hpf1_process_pcm_frame_f32(pHPF, pY, pX); - pY += pHPF->channels; - pX += pHPF->channels; - } - } else if (pHPF->format == ma_format_s16) { - /* */ ma_int16* pY = ( ma_int16*)pFramesOut; - const ma_int16* pX = (const ma_int16*)pFramesIn; - - for (n = 0; n < frameCount; n += 1) { - ma_hpf1_process_pcm_frame_s16(pHPF, pY, pX); - pY += pHPF->channels; - pX += pHPF->channels; - } - } else { - MA_ASSERT(MA_FALSE); - return MA_INVALID_ARGS; /* Format not supported. Should never hit this because it's checked in ma_biquad_init() and ma_biquad_reinit(). */ - } - - return MA_SUCCESS; -} - -MA_API ma_uint32 ma_hpf1_get_latency(const ma_hpf1* pHPF) -{ - if (pHPF == NULL) { - return 0; - } - - return 1; -} - - -static MA_INLINE ma_biquad_config ma_hpf2__get_biquad_config(const ma_hpf2_config* pConfig) -{ - ma_biquad_config bqConfig; - double q; - double w; - double s; - double c; - double a; - - MA_ASSERT(pConfig != NULL); - - q = pConfig->q; - w = 2 * MA_PI_D * pConfig->cutoffFrequency / pConfig->sampleRate; - s = ma_sind(w); - c = ma_cosd(w); - a = s / (2*q); - - bqConfig.b0 = (1 + c) / 2; - bqConfig.b1 = -(1 + c); - bqConfig.b2 = (1 + c) / 2; - bqConfig.a0 = 1 + a; - bqConfig.a1 = -2 * c; - bqConfig.a2 = 1 - a; - - bqConfig.format = pConfig->format; - bqConfig.channels = pConfig->channels; - - return bqConfig; -} - -MA_API ma_result ma_hpf2_get_heap_size(const ma_hpf2_config* pConfig, size_t* pHeapSizeInBytes) -{ - ma_biquad_config bqConfig; - bqConfig = ma_hpf2__get_biquad_config(pConfig); - - return ma_biquad_get_heap_size(&bqConfig, pHeapSizeInBytes); -} - -MA_API ma_result ma_hpf2_init_preallocated(const ma_hpf2_config* pConfig, void* pHeap, ma_hpf2* pHPF) -{ - ma_result result; - ma_biquad_config bqConfig; - - if (pHPF == NULL) { - return MA_INVALID_ARGS; - } - - MA_ZERO_OBJECT(pHPF); - - if (pConfig == NULL) { - return MA_INVALID_ARGS; - } - - bqConfig = ma_hpf2__get_biquad_config(pConfig); - result = ma_biquad_init_preallocated(&bqConfig, pHeap, &pHPF->bq); - if (result != MA_SUCCESS) { - return result; - } - - return MA_SUCCESS; -} - -MA_API ma_result ma_hpf2_init(const ma_hpf2_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_hpf2* pHPF) -{ - ma_result result; - size_t heapSizeInBytes; - void* pHeap; - - result = ma_hpf2_get_heap_size(pConfig, &heapSizeInBytes); - if (result != MA_SUCCESS) { - return result; - } - - if (heapSizeInBytes > 0) { - pHeap = ma_malloc(heapSizeInBytes, pAllocationCallbacks); - if (pHeap == NULL) { - return MA_OUT_OF_MEMORY; - } - } else { - pHeap = NULL; - } - - result = ma_hpf2_init_preallocated(pConfig, pHeap, pHPF); - if (result != MA_SUCCESS) { - ma_free(pHeap, pAllocationCallbacks); - return result; - } - - pHPF->bq._ownsHeap = MA_TRUE; /* <-- This will cause the biquad to take ownership of the heap and free it when it's uninitialized. */ - return MA_SUCCESS; -} - -MA_API void ma_hpf2_uninit(ma_hpf2* pHPF, const ma_allocation_callbacks* pAllocationCallbacks) -{ - if (pHPF == NULL) { - return; - } - - ma_biquad_uninit(&pHPF->bq, pAllocationCallbacks); /* <-- This will free the heap allocation. */ -} - -MA_API ma_result ma_hpf2_reinit(const ma_hpf2_config* pConfig, ma_hpf2* pHPF) -{ - ma_result result; - ma_biquad_config bqConfig; - - if (pHPF == NULL || pConfig == NULL) { - return MA_INVALID_ARGS; - } - - bqConfig = ma_hpf2__get_biquad_config(pConfig); - result = ma_biquad_reinit(&bqConfig, &pHPF->bq); - if (result != MA_SUCCESS) { - return result; - } - - return MA_SUCCESS; -} - -static MA_INLINE void ma_hpf2_process_pcm_frame_s16(ma_hpf2* pHPF, ma_int16* pFrameOut, const ma_int16* pFrameIn) -{ - ma_biquad_process_pcm_frame_s16(&pHPF->bq, pFrameOut, pFrameIn); -} - -static MA_INLINE void ma_hpf2_process_pcm_frame_f32(ma_hpf2* pHPF, float* pFrameOut, const float* pFrameIn) -{ - ma_biquad_process_pcm_frame_f32(&pHPF->bq, pFrameOut, pFrameIn); -} - -MA_API ma_result ma_hpf2_process_pcm_frames(ma_hpf2* pHPF, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount) -{ - if (pHPF == NULL) { - return MA_INVALID_ARGS; - } - - return ma_biquad_process_pcm_frames(&pHPF->bq, pFramesOut, pFramesIn, frameCount); -} - -MA_API ma_uint32 ma_hpf2_get_latency(const ma_hpf2* pHPF) -{ - if (pHPF == NULL) { - return 0; - } - - return ma_biquad_get_latency(&pHPF->bq); -} - - -MA_API ma_hpf_config ma_hpf_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRate, double cutoffFrequency, ma_uint32 order) -{ - ma_hpf_config config; - - MA_ZERO_OBJECT(&config); - config.format = format; - config.channels = channels; - config.sampleRate = sampleRate; - config.cutoffFrequency = cutoffFrequency; - config.order = ma_min(order, MA_MAX_FILTER_ORDER); - - return config; -} - - -typedef struct -{ - size_t sizeInBytes; - size_t hpf1Offset; - size_t hpf2Offset; /* Offset of the first second order filter. Subsequent filters will come straight after, and will each have the same heap size. */ -} ma_hpf_heap_layout; - -static void ma_hpf_calculate_sub_hpf_counts(ma_uint32 order, ma_uint32* pHPF1Count, ma_uint32* pHPF2Count) -{ - MA_ASSERT(pHPF1Count != NULL); - MA_ASSERT(pHPF2Count != NULL); - - *pHPF1Count = order % 2; - *pHPF2Count = order / 2; -} - -static ma_result ma_hpf_get_heap_layout(const ma_hpf_config* pConfig, ma_hpf_heap_layout* pHeapLayout) -{ - ma_result result; - ma_uint32 hpf1Count; - ma_uint32 hpf2Count; - ma_uint32 ihpf1; - ma_uint32 ihpf2; - - MA_ASSERT(pHeapLayout != NULL); - - MA_ZERO_OBJECT(pHeapLayout); - - if (pConfig == NULL) { - return MA_INVALID_ARGS; - } - - if (pConfig->channels == 0) { - return MA_INVALID_ARGS; - } - - if (pConfig->order > MA_MAX_FILTER_ORDER) { - return MA_INVALID_ARGS; - } - - ma_hpf_calculate_sub_hpf_counts(pConfig->order, &hpf1Count, &hpf2Count); - - pHeapLayout->sizeInBytes = 0; - - /* HPF 1 */ - pHeapLayout->hpf1Offset = pHeapLayout->sizeInBytes; - for (ihpf1 = 0; ihpf1 < hpf1Count; ihpf1 += 1) { - size_t hpf1HeapSizeInBytes; - ma_hpf1_config hpf1Config = ma_hpf1_config_init(pConfig->format, pConfig->channels, pConfig->sampleRate, pConfig->cutoffFrequency); - - result = ma_hpf1_get_heap_size(&hpf1Config, &hpf1HeapSizeInBytes); - if (result != MA_SUCCESS) { - return result; - } - - pHeapLayout->sizeInBytes += sizeof(ma_hpf1) + hpf1HeapSizeInBytes; - } - - /* HPF 2*/ - pHeapLayout->hpf2Offset = pHeapLayout->sizeInBytes; - for (ihpf2 = 0; ihpf2 < hpf2Count; ihpf2 += 1) { - size_t hpf2HeapSizeInBytes; - ma_hpf2_config hpf2Config = ma_hpf2_config_init(pConfig->format, pConfig->channels, pConfig->sampleRate, pConfig->cutoffFrequency, 0.707107); /* <-- The "q" parameter does not matter for the purpose of calculating the heap size. */ - - result = ma_hpf2_get_heap_size(&hpf2Config, &hpf2HeapSizeInBytes); - if (result != MA_SUCCESS) { - return result; - } - - pHeapLayout->sizeInBytes += sizeof(ma_hpf2) + hpf2HeapSizeInBytes; - } - - /* Make sure allocation size is aligned. */ - pHeapLayout->sizeInBytes = ma_align_64(pHeapLayout->sizeInBytes); - - return MA_SUCCESS; -} - -static ma_result ma_hpf_reinit__internal(const ma_hpf_config* pConfig, void* pHeap, ma_hpf* pHPF, ma_bool32 isNew) -{ - ma_result result; - ma_uint32 hpf1Count; - ma_uint32 hpf2Count; - ma_uint32 ihpf1; - ma_uint32 ihpf2; - ma_hpf_heap_layout heapLayout; /* Only used if isNew is true. */ - - if (pHPF == NULL || pConfig == NULL) { - return MA_INVALID_ARGS; - } - - /* Only supporting f32 and s16. */ - if (pConfig->format != ma_format_f32 && pConfig->format != ma_format_s16) { - return MA_INVALID_ARGS; - } - - /* The format cannot be changed after initialization. */ - if (pHPF->format != ma_format_unknown && pHPF->format != pConfig->format) { - return MA_INVALID_OPERATION; - } - - /* The channel count cannot be changed after initialization. */ - if (pHPF->channels != 0 && pHPF->channels != pConfig->channels) { - return MA_INVALID_OPERATION; - } - - if (pConfig->order > MA_MAX_FILTER_ORDER) { - return MA_INVALID_ARGS; - } - - ma_hpf_calculate_sub_hpf_counts(pConfig->order, &hpf1Count, &hpf2Count); - - /* The filter order can't change between reinits. */ - if (!isNew) { - if (pHPF->hpf1Count != hpf1Count || pHPF->hpf2Count != hpf2Count) { - return MA_INVALID_OPERATION; - } - } - - if (isNew) { - result = ma_hpf_get_heap_layout(pConfig, &heapLayout); - if (result != MA_SUCCESS) { - return result; - } - - pHPF->_pHeap = pHeap; - MA_ZERO_MEMORY(pHeap, heapLayout.sizeInBytes); - - pHPF->pHPF1 = (ma_hpf1*)ma_offset_ptr(pHeap, heapLayout.hpf1Offset); - pHPF->pHPF2 = (ma_hpf2*)ma_offset_ptr(pHeap, heapLayout.hpf2Offset); - } else { - MA_ZERO_OBJECT(&heapLayout); /* To silence a compiler warning. */ - } - - for (ihpf1 = 0; ihpf1 < hpf1Count; ihpf1 += 1) { - ma_hpf1_config hpf1Config = ma_hpf1_config_init(pConfig->format, pConfig->channels, pConfig->sampleRate, pConfig->cutoffFrequency); - - if (isNew) { - size_t hpf1HeapSizeInBytes; - - result = ma_hpf1_get_heap_size(&hpf1Config, &hpf1HeapSizeInBytes); - if (result == MA_SUCCESS) { - result = ma_hpf1_init_preallocated(&hpf1Config, ma_offset_ptr(pHeap, heapLayout.hpf1Offset + (sizeof(ma_hpf1) * hpf1Count) + (ihpf1 * hpf1HeapSizeInBytes)), &pHPF->pHPF1[ihpf1]); - } - } else { - result = ma_hpf1_reinit(&hpf1Config, &pHPF->pHPF1[ihpf1]); - } - - if (result != MA_SUCCESS) { - ma_uint32 jhpf1; - - for (jhpf1 = 0; jhpf1 < ihpf1; jhpf1 += 1) { - ma_hpf1_uninit(&pHPF->pHPF1[jhpf1], NULL); /* No need for allocation callbacks here since we used a preallocated heap allocation. */ - } - - return result; - } - } - - for (ihpf2 = 0; ihpf2 < hpf2Count; ihpf2 += 1) { - ma_hpf2_config hpf2Config; - double q; - double a; - - /* Tempting to use 0.707107, but won't result in a Butterworth filter if the order is > 2. */ - if (hpf1Count == 1) { - a = (1 + ihpf2*1) * (MA_PI_D/(pConfig->order*1)); /* Odd order. */ - } else { - a = (1 + ihpf2*2) * (MA_PI_D/(pConfig->order*2)); /* Even order. */ - } - q = 1 / (2*ma_cosd(a)); - - hpf2Config = ma_hpf2_config_init(pConfig->format, pConfig->channels, pConfig->sampleRate, pConfig->cutoffFrequency, q); - - if (isNew) { - size_t hpf2HeapSizeInBytes; - - result = ma_hpf2_get_heap_size(&hpf2Config, &hpf2HeapSizeInBytes); - if (result == MA_SUCCESS) { - result = ma_hpf2_init_preallocated(&hpf2Config, ma_offset_ptr(pHeap, heapLayout.hpf2Offset + (sizeof(ma_hpf2) * hpf2Count) + (ihpf2 * hpf2HeapSizeInBytes)), &pHPF->pHPF2[ihpf2]); - } - } else { - result = ma_hpf2_reinit(&hpf2Config, &pHPF->pHPF2[ihpf2]); - } - - if (result != MA_SUCCESS) { - ma_uint32 jhpf1; - ma_uint32 jhpf2; - - for (jhpf1 = 0; jhpf1 < hpf1Count; jhpf1 += 1) { - ma_hpf1_uninit(&pHPF->pHPF1[jhpf1], NULL); /* No need for allocation callbacks here since we used a preallocated heap allocation. */ - } - - for (jhpf2 = 0; jhpf2 < ihpf2; jhpf2 += 1) { - ma_hpf2_uninit(&pHPF->pHPF2[jhpf2], NULL); /* No need for allocation callbacks here since we used a preallocated heap allocation. */ - } - - return result; - } - } - - pHPF->hpf1Count = hpf1Count; - pHPF->hpf2Count = hpf2Count; - pHPF->format = pConfig->format; - pHPF->channels = pConfig->channels; - pHPF->sampleRate = pConfig->sampleRate; - - return MA_SUCCESS; -} - -MA_API ma_result ma_hpf_get_heap_size(const ma_hpf_config* pConfig, size_t* pHeapSizeInBytes) -{ - ma_result result; - ma_hpf_heap_layout heapLayout; - - if (pHeapSizeInBytes == NULL) { - return MA_INVALID_ARGS; - } - - *pHeapSizeInBytes = 0; - - result = ma_hpf_get_heap_layout(pConfig, &heapLayout); - if (result != MA_SUCCESS) { - return result; - } - - *pHeapSizeInBytes = heapLayout.sizeInBytes; - - return result; -} - -MA_API ma_result ma_hpf_init_preallocated(const ma_hpf_config* pConfig, void* pHeap, ma_hpf* pLPF) -{ - if (pLPF == NULL) { - return MA_INVALID_ARGS; - } - - MA_ZERO_OBJECT(pLPF); - - return ma_hpf_reinit__internal(pConfig, pHeap, pLPF, /*isNew*/MA_TRUE); -} - -MA_API ma_result ma_hpf_init(const ma_hpf_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_hpf* pHPF) -{ - ma_result result; - size_t heapSizeInBytes; - void* pHeap; - - result = ma_hpf_get_heap_size(pConfig, &heapSizeInBytes); - if (result != MA_SUCCESS) { - return result; - } - - if (heapSizeInBytes > 0) { - pHeap = ma_malloc(heapSizeInBytes, pAllocationCallbacks); - if (pHeap == NULL) { - return MA_OUT_OF_MEMORY; - } - } else { - pHeap = NULL; - } - - result = ma_hpf_init_preallocated(pConfig, pHeap, pHPF); - if (result != MA_SUCCESS) { - ma_free(pHeap, pAllocationCallbacks); - return result; - } - - pHPF->_ownsHeap = MA_TRUE; - return MA_SUCCESS; -} - -MA_API void ma_hpf_uninit(ma_hpf* pHPF, const ma_allocation_callbacks* pAllocationCallbacks) -{ - ma_uint32 ihpf1; - ma_uint32 ihpf2; - - if (pHPF == NULL) { - return; - } - - for (ihpf1 = 0; ihpf1 < pHPF->hpf1Count; ihpf1 += 1) { - ma_hpf1_uninit(&pHPF->pHPF1[ihpf1], pAllocationCallbacks); - } - - for (ihpf2 = 0; ihpf2 < pHPF->hpf2Count; ihpf2 += 1) { - ma_hpf2_uninit(&pHPF->pHPF2[ihpf2], pAllocationCallbacks); - } - - if (pHPF->_ownsHeap) { - ma_free(pHPF->_pHeap, pAllocationCallbacks); - } -} - -MA_API ma_result ma_hpf_reinit(const ma_hpf_config* pConfig, ma_hpf* pHPF) -{ - return ma_hpf_reinit__internal(pConfig, NULL, pHPF, /*isNew*/MA_FALSE); -} - -MA_API ma_result ma_hpf_process_pcm_frames(ma_hpf* pHPF, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount) -{ - ma_result result; - ma_uint32 ihpf1; - ma_uint32 ihpf2; - - if (pHPF == NULL) { - return MA_INVALID_ARGS; - } - - /* Faster path for in-place. */ - if (pFramesOut == pFramesIn) { - for (ihpf1 = 0; ihpf1 < pHPF->hpf1Count; ihpf1 += 1) { - result = ma_hpf1_process_pcm_frames(&pHPF->pHPF1[ihpf1], pFramesOut, pFramesOut, frameCount); - if (result != MA_SUCCESS) { - return result; - } - } - - for (ihpf2 = 0; ihpf2 < pHPF->hpf2Count; ihpf2 += 1) { - result = ma_hpf2_process_pcm_frames(&pHPF->pHPF2[ihpf2], pFramesOut, pFramesOut, frameCount); - if (result != MA_SUCCESS) { - return result; - } - } - } - - /* Slightly slower path for copying. */ - if (pFramesOut != pFramesIn) { - ma_uint32 iFrame; - - /* */ if (pHPF->format == ma_format_f32) { - /* */ float* pFramesOutF32 = ( float*)pFramesOut; - const float* pFramesInF32 = (const float*)pFramesIn; - - for (iFrame = 0; iFrame < frameCount; iFrame += 1) { - MA_COPY_MEMORY(pFramesOutF32, pFramesInF32, ma_get_bytes_per_frame(pHPF->format, pHPF->channels)); - - for (ihpf1 = 0; ihpf1 < pHPF->hpf1Count; ihpf1 += 1) { - ma_hpf1_process_pcm_frame_f32(&pHPF->pHPF1[ihpf1], pFramesOutF32, pFramesOutF32); - } - - for (ihpf2 = 0; ihpf2 < pHPF->hpf2Count; ihpf2 += 1) { - ma_hpf2_process_pcm_frame_f32(&pHPF->pHPF2[ihpf2], pFramesOutF32, pFramesOutF32); - } - - pFramesOutF32 += pHPF->channels; - pFramesInF32 += pHPF->channels; - } - } else if (pHPF->format == ma_format_s16) { - /* */ ma_int16* pFramesOutS16 = ( ma_int16*)pFramesOut; - const ma_int16* pFramesInS16 = (const ma_int16*)pFramesIn; - - for (iFrame = 0; iFrame < frameCount; iFrame += 1) { - MA_COPY_MEMORY(pFramesOutS16, pFramesInS16, ma_get_bytes_per_frame(pHPF->format, pHPF->channels)); - - for (ihpf1 = 0; ihpf1 < pHPF->hpf1Count; ihpf1 += 1) { - ma_hpf1_process_pcm_frame_s16(&pHPF->pHPF1[ihpf1], pFramesOutS16, pFramesOutS16); - } - - for (ihpf2 = 0; ihpf2 < pHPF->hpf2Count; ihpf2 += 1) { - ma_hpf2_process_pcm_frame_s16(&pHPF->pHPF2[ihpf2], pFramesOutS16, pFramesOutS16); - } - - pFramesOutS16 += pHPF->channels; - pFramesInS16 += pHPF->channels; - } - } else { - MA_ASSERT(MA_FALSE); - return MA_INVALID_OPERATION; /* Should never hit this. */ - } - } - - return MA_SUCCESS; -} - -MA_API ma_uint32 ma_hpf_get_latency(const ma_hpf* pHPF) -{ - if (pHPF == NULL) { - return 0; - } - - return pHPF->hpf2Count*2 + pHPF->hpf1Count; -} - - -/************************************************************************************************************************************************************** - -Band-Pass Filtering - -**************************************************************************************************************************************************************/ -MA_API ma_bpf2_config ma_bpf2_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRate, double cutoffFrequency, double q) -{ - ma_bpf2_config config; - - MA_ZERO_OBJECT(&config); - config.format = format; - config.channels = channels; - config.sampleRate = sampleRate; - config.cutoffFrequency = cutoffFrequency; - config.q = q; - - /* Q cannot be 0 or else it'll result in a division by 0. In this case just default to 0.707107. */ - if (config.q == 0) { - config.q = 0.707107; - } - - return config; -} - - -static MA_INLINE ma_biquad_config ma_bpf2__get_biquad_config(const ma_bpf2_config* pConfig) -{ - ma_biquad_config bqConfig; - double q; - double w; - double s; - double c; - double a; - - MA_ASSERT(pConfig != NULL); - - q = pConfig->q; - w = 2 * MA_PI_D * pConfig->cutoffFrequency / pConfig->sampleRate; - s = ma_sind(w); - c = ma_cosd(w); - a = s / (2*q); - - bqConfig.b0 = q * a; - bqConfig.b1 = 0; - bqConfig.b2 = -q * a; - bqConfig.a0 = 1 + a; - bqConfig.a1 = -2 * c; - bqConfig.a2 = 1 - a; - - bqConfig.format = pConfig->format; - bqConfig.channels = pConfig->channels; - - return bqConfig; -} - -MA_API ma_result ma_bpf2_get_heap_size(const ma_bpf2_config* pConfig, size_t* pHeapSizeInBytes) -{ - ma_biquad_config bqConfig; - bqConfig = ma_bpf2__get_biquad_config(pConfig); - - return ma_biquad_get_heap_size(&bqConfig, pHeapSizeInBytes); -} - -MA_API ma_result ma_bpf2_init_preallocated(const ma_bpf2_config* pConfig, void* pHeap, ma_bpf2* pBPF) -{ - ma_result result; - ma_biquad_config bqConfig; - - if (pBPF == NULL) { - return MA_INVALID_ARGS; - } - - MA_ZERO_OBJECT(pBPF); - - if (pConfig == NULL) { - return MA_INVALID_ARGS; - } - - bqConfig = ma_bpf2__get_biquad_config(pConfig); - result = ma_biquad_init_preallocated(&bqConfig, pHeap, &pBPF->bq); - if (result != MA_SUCCESS) { - return result; - } - - return MA_SUCCESS; -} - -MA_API ma_result ma_bpf2_init(const ma_bpf2_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_bpf2* pBPF) -{ - ma_result result; - size_t heapSizeInBytes; - void* pHeap; - - result = ma_bpf2_get_heap_size(pConfig, &heapSizeInBytes); - if (result != MA_SUCCESS) { - return result; - } - - if (heapSizeInBytes > 0) { - pHeap = ma_malloc(heapSizeInBytes, pAllocationCallbacks); - if (pHeap == NULL) { - return MA_OUT_OF_MEMORY; - } - } else { - pHeap = NULL; - } - - result = ma_bpf2_init_preallocated(pConfig, pHeap, pBPF); - if (result != MA_SUCCESS) { - ma_free(pHeap, pAllocationCallbacks); - return result; - } - - pBPF->bq._ownsHeap = MA_TRUE; /* <-- This will cause the biquad to take ownership of the heap and free it when it's uninitialized. */ - return MA_SUCCESS; -} - -MA_API void ma_bpf2_uninit(ma_bpf2* pBPF, const ma_allocation_callbacks* pAllocationCallbacks) -{ - if (pBPF == NULL) { - return; - } - - ma_biquad_uninit(&pBPF->bq, pAllocationCallbacks); /* <-- This will free the heap allocation. */ -} - -MA_API ma_result ma_bpf2_reinit(const ma_bpf2_config* pConfig, ma_bpf2* pBPF) -{ - ma_result result; - ma_biquad_config bqConfig; - - if (pBPF == NULL || pConfig == NULL) { - return MA_INVALID_ARGS; - } - - bqConfig = ma_bpf2__get_biquad_config(pConfig); - result = ma_biquad_reinit(&bqConfig, &pBPF->bq); - if (result != MA_SUCCESS) { - return result; - } - - return MA_SUCCESS; -} - -static MA_INLINE void ma_bpf2_process_pcm_frame_s16(ma_bpf2* pBPF, ma_int16* pFrameOut, const ma_int16* pFrameIn) -{ - ma_biquad_process_pcm_frame_s16(&pBPF->bq, pFrameOut, pFrameIn); -} - -static MA_INLINE void ma_bpf2_process_pcm_frame_f32(ma_bpf2* pBPF, float* pFrameOut, const float* pFrameIn) -{ - ma_biquad_process_pcm_frame_f32(&pBPF->bq, pFrameOut, pFrameIn); -} - -MA_API ma_result ma_bpf2_process_pcm_frames(ma_bpf2* pBPF, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount) -{ - if (pBPF == NULL) { - return MA_INVALID_ARGS; - } - - return ma_biquad_process_pcm_frames(&pBPF->bq, pFramesOut, pFramesIn, frameCount); -} - -MA_API ma_uint32 ma_bpf2_get_latency(const ma_bpf2* pBPF) -{ - if (pBPF == NULL) { - return 0; - } - - return ma_biquad_get_latency(&pBPF->bq); -} - - -MA_API ma_bpf_config ma_bpf_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRate, double cutoffFrequency, ma_uint32 order) -{ - ma_bpf_config config; - - MA_ZERO_OBJECT(&config); - config.format = format; - config.channels = channels; - config.sampleRate = sampleRate; - config.cutoffFrequency = cutoffFrequency; - config.order = ma_min(order, MA_MAX_FILTER_ORDER); - - return config; -} - - -typedef struct -{ - size_t sizeInBytes; - size_t bpf2Offset; -} ma_bpf_heap_layout; - -static ma_result ma_bpf_get_heap_layout(const ma_bpf_config* pConfig, ma_bpf_heap_layout* pHeapLayout) -{ - ma_result result; - ma_uint32 bpf2Count; - ma_uint32 ibpf2; - - MA_ASSERT(pHeapLayout != NULL); - - MA_ZERO_OBJECT(pHeapLayout); - - if (pConfig == NULL) { - return MA_INVALID_ARGS; - } - - if (pConfig->order > MA_MAX_FILTER_ORDER) { - return MA_INVALID_ARGS; - } - - /* We must have an even number of order. */ - if ((pConfig->order & 0x1) != 0) { - return MA_INVALID_ARGS; - } - - bpf2Count = pConfig->order / 2; - - pHeapLayout->sizeInBytes = 0; - - /* BPF 2 */ - pHeapLayout->bpf2Offset = pHeapLayout->sizeInBytes; - for (ibpf2 = 0; ibpf2 < bpf2Count; ibpf2 += 1) { - size_t bpf2HeapSizeInBytes; - ma_bpf2_config bpf2Config = ma_bpf2_config_init(pConfig->format, pConfig->channels, pConfig->sampleRate, pConfig->cutoffFrequency, 0.707107); /* <-- The "q" parameter does not matter for the purpose of calculating the heap size. */ - - result = ma_bpf2_get_heap_size(&bpf2Config, &bpf2HeapSizeInBytes); - if (result != MA_SUCCESS) { - return result; - } - - pHeapLayout->sizeInBytes += sizeof(ma_bpf2) + bpf2HeapSizeInBytes; - } - - /* Make sure allocation size is aligned. */ - pHeapLayout->sizeInBytes = ma_align_64(pHeapLayout->sizeInBytes); - - return MA_SUCCESS; -} - -static ma_result ma_bpf_reinit__internal(const ma_bpf_config* pConfig, void* pHeap, ma_bpf* pBPF, ma_bool32 isNew) -{ - ma_result result; - ma_uint32 bpf2Count; - ma_uint32 ibpf2; - ma_bpf_heap_layout heapLayout; /* Only used if isNew is true. */ - - if (pBPF == NULL || pConfig == NULL) { - return MA_INVALID_ARGS; - } - - /* Only supporting f32 and s16. */ - if (pConfig->format != ma_format_f32 && pConfig->format != ma_format_s16) { - return MA_INVALID_ARGS; - } - - /* The format cannot be changed after initialization. */ - if (pBPF->format != ma_format_unknown && pBPF->format != pConfig->format) { - return MA_INVALID_OPERATION; - } - - /* The channel count cannot be changed after initialization. */ - if (pBPF->channels != 0 && pBPF->channels != pConfig->channels) { - return MA_INVALID_OPERATION; - } - - if (pConfig->order > MA_MAX_FILTER_ORDER) { - return MA_INVALID_ARGS; - } - - /* We must have an even number of order. */ - if ((pConfig->order & 0x1) != 0) { - return MA_INVALID_ARGS; - } - - bpf2Count = pConfig->order / 2; - - /* The filter order can't change between reinits. */ - if (!isNew) { - if (pBPF->bpf2Count != bpf2Count) { - return MA_INVALID_OPERATION; - } - } - - if (isNew) { - result = ma_bpf_get_heap_layout(pConfig, &heapLayout); - if (result != MA_SUCCESS) { - return result; - } - - pBPF->_pHeap = pHeap; - MA_ZERO_MEMORY(pHeap, heapLayout.sizeInBytes); - - pBPF->pBPF2 = (ma_bpf2*)ma_offset_ptr(pHeap, heapLayout.bpf2Offset); - } else { - MA_ZERO_OBJECT(&heapLayout); - } - - for (ibpf2 = 0; ibpf2 < bpf2Count; ibpf2 += 1) { - ma_bpf2_config bpf2Config; - double q; - - /* TODO: Calculate Q to make this a proper Butterworth filter. */ - q = 0.707107; - - bpf2Config = ma_bpf2_config_init(pConfig->format, pConfig->channels, pConfig->sampleRate, pConfig->cutoffFrequency, q); - - if (isNew) { - size_t bpf2HeapSizeInBytes; - - result = ma_bpf2_get_heap_size(&bpf2Config, &bpf2HeapSizeInBytes); - if (result == MA_SUCCESS) { - result = ma_bpf2_init_preallocated(&bpf2Config, ma_offset_ptr(pHeap, heapLayout.bpf2Offset + (sizeof(ma_bpf2) * bpf2Count) + (ibpf2 * bpf2HeapSizeInBytes)), &pBPF->pBPF2[ibpf2]); - } - } else { - result = ma_bpf2_reinit(&bpf2Config, &pBPF->pBPF2[ibpf2]); - } - - if (result != MA_SUCCESS) { - return result; - } - } - - pBPF->bpf2Count = bpf2Count; - pBPF->format = pConfig->format; - pBPF->channels = pConfig->channels; - - return MA_SUCCESS; -} - - -MA_API ma_result ma_bpf_get_heap_size(const ma_bpf_config* pConfig, size_t* pHeapSizeInBytes) -{ - ma_result result; - ma_bpf_heap_layout heapLayout; - - if (pHeapSizeInBytes == NULL) { - return MA_INVALID_ARGS; - } - - *pHeapSizeInBytes = 0; - - result = ma_bpf_get_heap_layout(pConfig, &heapLayout); - if (result != MA_SUCCESS) { - return result; - } - - *pHeapSizeInBytes = heapLayout.sizeInBytes; - - return MA_SUCCESS; -} - -MA_API ma_result ma_bpf_init_preallocated(const ma_bpf_config* pConfig, void* pHeap, ma_bpf* pBPF) -{ - if (pBPF == NULL) { - return MA_INVALID_ARGS; - } - - MA_ZERO_OBJECT(pBPF); - - return ma_bpf_reinit__internal(pConfig, pHeap, pBPF, /*isNew*/MA_TRUE); -} - -MA_API ma_result ma_bpf_init(const ma_bpf_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_bpf* pBPF) -{ - ma_result result; - size_t heapSizeInBytes; - void* pHeap; - - result = ma_bpf_get_heap_size(pConfig, &heapSizeInBytes); - if (result != MA_SUCCESS) { - return result; - } - - if (heapSizeInBytes > 0) { - pHeap = ma_malloc(heapSizeInBytes, pAllocationCallbacks); - if (pHeap == NULL) { - return MA_OUT_OF_MEMORY; - } - } else { - pHeap = NULL; - } - - result = ma_bpf_init_preallocated(pConfig, pHeap, pBPF); - if (result != MA_SUCCESS) { - ma_free(pHeap, pAllocationCallbacks); - return result; - } - - pBPF->_ownsHeap = MA_TRUE; - return MA_SUCCESS; -} - -MA_API void ma_bpf_uninit(ma_bpf* pBPF, const ma_allocation_callbacks* pAllocationCallbacks) -{ - ma_uint32 ibpf2; - - if (pBPF == NULL) { - return; - } - - for (ibpf2 = 0; ibpf2 < pBPF->bpf2Count; ibpf2 += 1) { - ma_bpf2_uninit(&pBPF->pBPF2[ibpf2], pAllocationCallbacks); - } - - if (pBPF->_ownsHeap) { - ma_free(pBPF->_pHeap, pAllocationCallbacks); - } -} - -MA_API ma_result ma_bpf_reinit(const ma_bpf_config* pConfig, ma_bpf* pBPF) -{ - return ma_bpf_reinit__internal(pConfig, NULL, pBPF, /*isNew*/MA_FALSE); -} - -MA_API ma_result ma_bpf_process_pcm_frames(ma_bpf* pBPF, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount) -{ - ma_result result; - ma_uint32 ibpf2; - - if (pBPF == NULL) { - return MA_INVALID_ARGS; - } - - /* Faster path for in-place. */ - if (pFramesOut == pFramesIn) { - for (ibpf2 = 0; ibpf2 < pBPF->bpf2Count; ibpf2 += 1) { - result = ma_bpf2_process_pcm_frames(&pBPF->pBPF2[ibpf2], pFramesOut, pFramesOut, frameCount); - if (result != MA_SUCCESS) { - return result; - } - } - } - - /* Slightly slower path for copying. */ - if (pFramesOut != pFramesIn) { - ma_uint32 iFrame; - - /* */ if (pBPF->format == ma_format_f32) { - /* */ float* pFramesOutF32 = ( float*)pFramesOut; - const float* pFramesInF32 = (const float*)pFramesIn; - - for (iFrame = 0; iFrame < frameCount; iFrame += 1) { - MA_COPY_MEMORY(pFramesOutF32, pFramesInF32, ma_get_bytes_per_frame(pBPF->format, pBPF->channels)); - - for (ibpf2 = 0; ibpf2 < pBPF->bpf2Count; ibpf2 += 1) { - ma_bpf2_process_pcm_frame_f32(&pBPF->pBPF2[ibpf2], pFramesOutF32, pFramesOutF32); - } - - pFramesOutF32 += pBPF->channels; - pFramesInF32 += pBPF->channels; - } - } else if (pBPF->format == ma_format_s16) { - /* */ ma_int16* pFramesOutS16 = ( ma_int16*)pFramesOut; - const ma_int16* pFramesInS16 = (const ma_int16*)pFramesIn; - - for (iFrame = 0; iFrame < frameCount; iFrame += 1) { - MA_COPY_MEMORY(pFramesOutS16, pFramesInS16, ma_get_bytes_per_frame(pBPF->format, pBPF->channels)); - - for (ibpf2 = 0; ibpf2 < pBPF->bpf2Count; ibpf2 += 1) { - ma_bpf2_process_pcm_frame_s16(&pBPF->pBPF2[ibpf2], pFramesOutS16, pFramesOutS16); - } - - pFramesOutS16 += pBPF->channels; - pFramesInS16 += pBPF->channels; - } - } else { - MA_ASSERT(MA_FALSE); - return MA_INVALID_OPERATION; /* Should never hit this. */ - } - } - - return MA_SUCCESS; -} - -MA_API ma_uint32 ma_bpf_get_latency(const ma_bpf* pBPF) -{ - if (pBPF == NULL) { - return 0; - } - - return pBPF->bpf2Count*2; -} - - -/************************************************************************************************************************************************************** - -Notching Filter - -**************************************************************************************************************************************************************/ -MA_API ma_notch2_config ma_notch2_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRate, double q, double frequency) -{ - ma_notch2_config config; - - MA_ZERO_OBJECT(&config); - config.format = format; - config.channels = channels; - config.sampleRate = sampleRate; - config.q = q; - config.frequency = frequency; - - if (config.q == 0) { - config.q = 0.707107; - } - - return config; -} - - -static MA_INLINE ma_biquad_config ma_notch2__get_biquad_config(const ma_notch2_config* pConfig) -{ - ma_biquad_config bqConfig; - double q; - double w; - double s; - double c; - double a; - - MA_ASSERT(pConfig != NULL); - - q = pConfig->q; - w = 2 * MA_PI_D * pConfig->frequency / pConfig->sampleRate; - s = ma_sind(w); - c = ma_cosd(w); - a = s / (2*q); - - bqConfig.b0 = 1; - bqConfig.b1 = -2 * c; - bqConfig.b2 = 1; - bqConfig.a0 = 1 + a; - bqConfig.a1 = -2 * c; - bqConfig.a2 = 1 - a; - - bqConfig.format = pConfig->format; - bqConfig.channels = pConfig->channels; - - return bqConfig; -} - -MA_API ma_result ma_notch2_get_heap_size(const ma_notch2_config* pConfig, size_t* pHeapSizeInBytes) -{ - ma_biquad_config bqConfig; - bqConfig = ma_notch2__get_biquad_config(pConfig); - - return ma_biquad_get_heap_size(&bqConfig, pHeapSizeInBytes); -} - -MA_API ma_result ma_notch2_init_preallocated(const ma_notch2_config* pConfig, void* pHeap, ma_notch2* pFilter) -{ - ma_result result; - ma_biquad_config bqConfig; - - if (pFilter == NULL) { - return MA_INVALID_ARGS; - } - - MA_ZERO_OBJECT(pFilter); - - if (pConfig == NULL) { - return MA_INVALID_ARGS; - } - - bqConfig = ma_notch2__get_biquad_config(pConfig); - result = ma_biquad_init_preallocated(&bqConfig, pHeap, &pFilter->bq); - if (result != MA_SUCCESS) { - return result; - } - - return MA_SUCCESS; -} - -MA_API ma_result ma_notch2_init(const ma_notch2_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_notch2* pFilter) -{ - ma_result result; - size_t heapSizeInBytes; - void* pHeap; - - result = ma_notch2_get_heap_size(pConfig, &heapSizeInBytes); - if (result != MA_SUCCESS) { - return result; - } - - if (heapSizeInBytes > 0) { - pHeap = ma_malloc(heapSizeInBytes, pAllocationCallbacks); - if (pHeap == NULL) { - return MA_OUT_OF_MEMORY; - } - } else { - pHeap = NULL; - } - - result = ma_notch2_init_preallocated(pConfig, pHeap, pFilter); - if (result != MA_SUCCESS) { - ma_free(pHeap, pAllocationCallbacks); - return result; - } - - pFilter->bq._ownsHeap = MA_TRUE; /* <-- This will cause the biquad to take ownership of the heap and free it when it's uninitialized. */ - return MA_SUCCESS; -} - -MA_API void ma_notch2_uninit(ma_notch2* pFilter, const ma_allocation_callbacks* pAllocationCallbacks) -{ - if (pFilter == NULL) { - return; - } - - ma_biquad_uninit(&pFilter->bq, pAllocationCallbacks); /* <-- This will free the heap allocation. */ -} - -MA_API ma_result ma_notch2_reinit(const ma_notch2_config* pConfig, ma_notch2* pFilter) -{ - ma_result result; - ma_biquad_config bqConfig; - - if (pFilter == NULL || pConfig == NULL) { - return MA_INVALID_ARGS; - } - - bqConfig = ma_notch2__get_biquad_config(pConfig); - result = ma_biquad_reinit(&bqConfig, &pFilter->bq); - if (result != MA_SUCCESS) { - return result; - } - - return MA_SUCCESS; -} - -static MA_INLINE void ma_notch2_process_pcm_frame_s16(ma_notch2* pFilter, ma_int16* pFrameOut, const ma_int16* pFrameIn) -{ - ma_biquad_process_pcm_frame_s16(&pFilter->bq, pFrameOut, pFrameIn); -} - -static MA_INLINE void ma_notch2_process_pcm_frame_f32(ma_notch2* pFilter, float* pFrameOut, const float* pFrameIn) -{ - ma_biquad_process_pcm_frame_f32(&pFilter->bq, pFrameOut, pFrameIn); -} - -MA_API ma_result ma_notch2_process_pcm_frames(ma_notch2* pFilter, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount) -{ - if (pFilter == NULL) { - return MA_INVALID_ARGS; - } - - return ma_biquad_process_pcm_frames(&pFilter->bq, pFramesOut, pFramesIn, frameCount); -} - -MA_API ma_uint32 ma_notch2_get_latency(const ma_notch2* pFilter) -{ - if (pFilter == NULL) { - return 0; - } - - return ma_biquad_get_latency(&pFilter->bq); -} - - - -/************************************************************************************************************************************************************** - -Peaking EQ Filter - -**************************************************************************************************************************************************************/ -MA_API ma_peak2_config ma_peak2_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRate, double gainDB, double q, double frequency) -{ - ma_peak2_config config; - - MA_ZERO_OBJECT(&config); - config.format = format; - config.channels = channels; - config.sampleRate = sampleRate; - config.gainDB = gainDB; - config.q = q; - config.frequency = frequency; - - if (config.q == 0) { - config.q = 0.707107; - } - - return config; -} - - -static MA_INLINE ma_biquad_config ma_peak2__get_biquad_config(const ma_peak2_config* pConfig) -{ - ma_biquad_config bqConfig; - double q; - double w; - double s; - double c; - double a; - double A; - - MA_ASSERT(pConfig != NULL); - - q = pConfig->q; - w = 2 * MA_PI_D * pConfig->frequency / pConfig->sampleRate; - s = ma_sind(w); - c = ma_cosd(w); - a = s / (2*q); - A = ma_powd(10, (pConfig->gainDB / 40)); - - bqConfig.b0 = 1 + (a * A); - bqConfig.b1 = -2 * c; - bqConfig.b2 = 1 - (a * A); - bqConfig.a0 = 1 + (a / A); - bqConfig.a1 = -2 * c; - bqConfig.a2 = 1 - (a / A); - - bqConfig.format = pConfig->format; - bqConfig.channels = pConfig->channels; - - return bqConfig; -} - -MA_API ma_result ma_peak2_get_heap_size(const ma_peak2_config* pConfig, size_t* pHeapSizeInBytes) -{ - ma_biquad_config bqConfig; - bqConfig = ma_peak2__get_biquad_config(pConfig); - - return ma_biquad_get_heap_size(&bqConfig, pHeapSizeInBytes); -} - -MA_API ma_result ma_peak2_init_preallocated(const ma_peak2_config* pConfig, void* pHeap, ma_peak2* pFilter) -{ - ma_result result; - ma_biquad_config bqConfig; - - if (pFilter == NULL) { - return MA_INVALID_ARGS; - } - - MA_ZERO_OBJECT(pFilter); - - if (pConfig == NULL) { - return MA_INVALID_ARGS; - } - - bqConfig = ma_peak2__get_biquad_config(pConfig); - result = ma_biquad_init_preallocated(&bqConfig, pHeap, &pFilter->bq); - if (result != MA_SUCCESS) { - return result; - } - - return MA_SUCCESS; -} - -MA_API ma_result ma_peak2_init(const ma_peak2_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_peak2* pFilter) -{ - ma_result result; - size_t heapSizeInBytes; - void* pHeap; - - result = ma_peak2_get_heap_size(pConfig, &heapSizeInBytes); - if (result != MA_SUCCESS) { - return result; - } - - if (heapSizeInBytes > 0) { - pHeap = ma_malloc(heapSizeInBytes, pAllocationCallbacks); - if (pHeap == NULL) { - return MA_OUT_OF_MEMORY; - } - } else { - pHeap = NULL; - } - - result = ma_peak2_init_preallocated(pConfig, pHeap, pFilter); - if (result != MA_SUCCESS) { - ma_free(pHeap, pAllocationCallbacks); - return result; - } - - pFilter->bq._ownsHeap = MA_TRUE; /* <-- This will cause the biquad to take ownership of the heap and free it when it's uninitialized. */ - return MA_SUCCESS; -} - -MA_API void ma_peak2_uninit(ma_peak2* pFilter, const ma_allocation_callbacks* pAllocationCallbacks) -{ - if (pFilter == NULL) { - return; - } - - ma_biquad_uninit(&pFilter->bq, pAllocationCallbacks); /* <-- This will free the heap allocation. */ -} - -MA_API ma_result ma_peak2_reinit(const ma_peak2_config* pConfig, ma_peak2* pFilter) -{ - ma_result result; - ma_biquad_config bqConfig; - - if (pFilter == NULL || pConfig == NULL) { - return MA_INVALID_ARGS; - } - - bqConfig = ma_peak2__get_biquad_config(pConfig); - result = ma_biquad_reinit(&bqConfig, &pFilter->bq); - if (result != MA_SUCCESS) { - return result; - } - - return MA_SUCCESS; -} - -static MA_INLINE void ma_peak2_process_pcm_frame_s16(ma_peak2* pFilter, ma_int16* pFrameOut, const ma_int16* pFrameIn) -{ - ma_biquad_process_pcm_frame_s16(&pFilter->bq, pFrameOut, pFrameIn); -} - -static MA_INLINE void ma_peak2_process_pcm_frame_f32(ma_peak2* pFilter, float* pFrameOut, const float* pFrameIn) -{ - ma_biquad_process_pcm_frame_f32(&pFilter->bq, pFrameOut, pFrameIn); -} - -MA_API ma_result ma_peak2_process_pcm_frames(ma_peak2* pFilter, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount) -{ - if (pFilter == NULL) { - return MA_INVALID_ARGS; - } - - return ma_biquad_process_pcm_frames(&pFilter->bq, pFramesOut, pFramesIn, frameCount); -} - -MA_API ma_uint32 ma_peak2_get_latency(const ma_peak2* pFilter) -{ - if (pFilter == NULL) { - return 0; - } - - return ma_biquad_get_latency(&pFilter->bq); -} - - -/************************************************************************************************************************************************************** - -Low Shelf Filter - -**************************************************************************************************************************************************************/ -MA_API ma_loshelf2_config ma_loshelf2_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRate, double gainDB, double shelfSlope, double frequency) -{ - ma_loshelf2_config config; - - MA_ZERO_OBJECT(&config); - config.format = format; - config.channels = channels; - config.sampleRate = sampleRate; - config.gainDB = gainDB; - config.shelfSlope = shelfSlope; - config.frequency = frequency; - - return config; -} - - -static MA_INLINE ma_biquad_config ma_loshelf2__get_biquad_config(const ma_loshelf2_config* pConfig) -{ - ma_biquad_config bqConfig; - double w; - double s; - double c; - double A; - double S; - double a; - double sqrtA; - - MA_ASSERT(pConfig != NULL); - - w = 2 * MA_PI_D * pConfig->frequency / pConfig->sampleRate; - s = ma_sind(w); - c = ma_cosd(w); - A = ma_powd(10, (pConfig->gainDB / 40)); - S = pConfig->shelfSlope; - a = s/2 * ma_sqrtd((A + 1/A) * (1/S - 1) + 2); - sqrtA = 2*ma_sqrtd(A)*a; - - bqConfig.b0 = A * ((A + 1) - (A - 1)*c + sqrtA); - bqConfig.b1 = 2 * A * ((A - 1) - (A + 1)*c); - bqConfig.b2 = A * ((A + 1) - (A - 1)*c - sqrtA); - bqConfig.a0 = (A + 1) + (A - 1)*c + sqrtA; - bqConfig.a1 = -2 * ((A - 1) + (A + 1)*c); - bqConfig.a2 = (A + 1) + (A - 1)*c - sqrtA; - - bqConfig.format = pConfig->format; - bqConfig.channels = pConfig->channels; - - return bqConfig; -} - -MA_API ma_result ma_loshelf2_get_heap_size(const ma_loshelf2_config* pConfig, size_t* pHeapSizeInBytes) -{ - ma_biquad_config bqConfig; - bqConfig = ma_loshelf2__get_biquad_config(pConfig); - - return ma_biquad_get_heap_size(&bqConfig, pHeapSizeInBytes); -} - -MA_API ma_result ma_loshelf2_init_preallocated(const ma_loshelf2_config* pConfig, void* pHeap, ma_loshelf2* pFilter) -{ - ma_result result; - ma_biquad_config bqConfig; - - if (pFilter == NULL) { - return MA_INVALID_ARGS; - } - - MA_ZERO_OBJECT(pFilter); - - if (pConfig == NULL) { - return MA_INVALID_ARGS; - } - - bqConfig = ma_loshelf2__get_biquad_config(pConfig); - result = ma_biquad_init_preallocated(&bqConfig, pHeap, &pFilter->bq); - if (result != MA_SUCCESS) { - return result; - } - - return MA_SUCCESS; -} - -MA_API ma_result ma_loshelf2_init(const ma_loshelf2_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_loshelf2* pFilter) -{ - ma_result result; - size_t heapSizeInBytes; - void* pHeap; - - result = ma_loshelf2_get_heap_size(pConfig, &heapSizeInBytes); - if (result != MA_SUCCESS) { - return result; - } - - if (heapSizeInBytes > 0) { - pHeap = ma_malloc(heapSizeInBytes, pAllocationCallbacks); - if (pHeap == NULL) { - return MA_OUT_OF_MEMORY; - } - } else { - pHeap = NULL; - } - - result = ma_loshelf2_init_preallocated(pConfig, pHeap, pFilter); - if (result != MA_SUCCESS) { - ma_free(pHeap, pAllocationCallbacks); - return result; - } - - pFilter->bq._ownsHeap = MA_TRUE; /* <-- This will cause the biquad to take ownership of the heap and free it when it's uninitialized. */ - return MA_SUCCESS; -} - -MA_API void ma_loshelf2_uninit(ma_loshelf2* pFilter, const ma_allocation_callbacks* pAllocationCallbacks) -{ - if (pFilter == NULL) { - return; - } - - ma_biquad_uninit(&pFilter->bq, pAllocationCallbacks); /* <-- This will free the heap allocation. */ -} - -MA_API ma_result ma_loshelf2_reinit(const ma_loshelf2_config* pConfig, ma_loshelf2* pFilter) -{ - ma_result result; - ma_biquad_config bqConfig; - - if (pFilter == NULL || pConfig == NULL) { - return MA_INVALID_ARGS; - } - - bqConfig = ma_loshelf2__get_biquad_config(pConfig); - result = ma_biquad_reinit(&bqConfig, &pFilter->bq); - if (result != MA_SUCCESS) { - return result; - } - - return MA_SUCCESS; -} - -static MA_INLINE void ma_loshelf2_process_pcm_frame_s16(ma_loshelf2* pFilter, ma_int16* pFrameOut, const ma_int16* pFrameIn) -{ - ma_biquad_process_pcm_frame_s16(&pFilter->bq, pFrameOut, pFrameIn); -} - -static MA_INLINE void ma_loshelf2_process_pcm_frame_f32(ma_loshelf2* pFilter, float* pFrameOut, const float* pFrameIn) -{ - ma_biquad_process_pcm_frame_f32(&pFilter->bq, pFrameOut, pFrameIn); -} - -MA_API ma_result ma_loshelf2_process_pcm_frames(ma_loshelf2* pFilter, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount) -{ - if (pFilter == NULL) { - return MA_INVALID_ARGS; - } - - return ma_biquad_process_pcm_frames(&pFilter->bq, pFramesOut, pFramesIn, frameCount); -} - -MA_API ma_uint32 ma_loshelf2_get_latency(const ma_loshelf2* pFilter) -{ - if (pFilter == NULL) { - return 0; - } - - return ma_biquad_get_latency(&pFilter->bq); -} - - -/************************************************************************************************************************************************************** - -High Shelf Filter - -**************************************************************************************************************************************************************/ -MA_API ma_hishelf2_config ma_hishelf2_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRate, double gainDB, double shelfSlope, double frequency) -{ - ma_hishelf2_config config; - - MA_ZERO_OBJECT(&config); - config.format = format; - config.channels = channels; - config.sampleRate = sampleRate; - config.gainDB = gainDB; - config.shelfSlope = shelfSlope; - config.frequency = frequency; - - return config; -} - - -static MA_INLINE ma_biquad_config ma_hishelf2__get_biquad_config(const ma_hishelf2_config* pConfig) -{ - ma_biquad_config bqConfig; - double w; - double s; - double c; - double A; - double S; - double a; - double sqrtA; - - MA_ASSERT(pConfig != NULL); - - w = 2 * MA_PI_D * pConfig->frequency / pConfig->sampleRate; - s = ma_sind(w); - c = ma_cosd(w); - A = ma_powd(10, (pConfig->gainDB / 40)); - S = pConfig->shelfSlope; - a = s/2 * ma_sqrtd((A + 1/A) * (1/S - 1) + 2); - sqrtA = 2*ma_sqrtd(A)*a; - - bqConfig.b0 = A * ((A + 1) + (A - 1)*c + sqrtA); - bqConfig.b1 = -2 * A * ((A - 1) + (A + 1)*c); - bqConfig.b2 = A * ((A + 1) + (A - 1)*c - sqrtA); - bqConfig.a0 = (A + 1) - (A - 1)*c + sqrtA; - bqConfig.a1 = 2 * ((A - 1) - (A + 1)*c); - bqConfig.a2 = (A + 1) - (A - 1)*c - sqrtA; - - bqConfig.format = pConfig->format; - bqConfig.channels = pConfig->channels; - - return bqConfig; -} - -MA_API ma_result ma_hishelf2_get_heap_size(const ma_hishelf2_config* pConfig, size_t* pHeapSizeInBytes) -{ - ma_biquad_config bqConfig; - bqConfig = ma_hishelf2__get_biquad_config(pConfig); - - return ma_biquad_get_heap_size(&bqConfig, pHeapSizeInBytes); -} - -MA_API ma_result ma_hishelf2_init_preallocated(const ma_hishelf2_config* pConfig, void* pHeap, ma_hishelf2* pFilter) -{ - ma_result result; - ma_biquad_config bqConfig; - - if (pFilter == NULL) { - return MA_INVALID_ARGS; - } - - MA_ZERO_OBJECT(pFilter); - - if (pConfig == NULL) { - return MA_INVALID_ARGS; - } - - bqConfig = ma_hishelf2__get_biquad_config(pConfig); - result = ma_biquad_init_preallocated(&bqConfig, pHeap, &pFilter->bq); - if (result != MA_SUCCESS) { - return result; - } - - return MA_SUCCESS; -} - -MA_API ma_result ma_hishelf2_init(const ma_hishelf2_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_hishelf2* pFilter) -{ - ma_result result; - size_t heapSizeInBytes; - void* pHeap; - - result = ma_hishelf2_get_heap_size(pConfig, &heapSizeInBytes); - if (result != MA_SUCCESS) { - return result; - } - - if (heapSizeInBytes > 0) { - pHeap = ma_malloc(heapSizeInBytes, pAllocationCallbacks); - if (pHeap == NULL) { - return MA_OUT_OF_MEMORY; - } - } else { - pHeap = NULL; - } - - result = ma_hishelf2_init_preallocated(pConfig, pHeap, pFilter); - if (result != MA_SUCCESS) { - ma_free(pHeap, pAllocationCallbacks); - return result; - } - - pFilter->bq._ownsHeap = MA_TRUE; /* <-- This will cause the biquad to take ownership of the heap and free it when it's uninitialized. */ - return MA_SUCCESS; -} - -MA_API void ma_hishelf2_uninit(ma_hishelf2* pFilter, const ma_allocation_callbacks* pAllocationCallbacks) -{ - if (pFilter == NULL) { - return; - } - - ma_biquad_uninit(&pFilter->bq, pAllocationCallbacks); /* <-- This will free the heap allocation. */ -} - -MA_API ma_result ma_hishelf2_reinit(const ma_hishelf2_config* pConfig, ma_hishelf2* pFilter) -{ - ma_result result; - ma_biquad_config bqConfig; - - if (pFilter == NULL || pConfig == NULL) { - return MA_INVALID_ARGS; - } - - bqConfig = ma_hishelf2__get_biquad_config(pConfig); - result = ma_biquad_reinit(&bqConfig, &pFilter->bq); - if (result != MA_SUCCESS) { - return result; - } - - return MA_SUCCESS; -} - -static MA_INLINE void ma_hishelf2_process_pcm_frame_s16(ma_hishelf2* pFilter, ma_int16* pFrameOut, const ma_int16* pFrameIn) -{ - ma_biquad_process_pcm_frame_s16(&pFilter->bq, pFrameOut, pFrameIn); -} - -static MA_INLINE void ma_hishelf2_process_pcm_frame_f32(ma_hishelf2* pFilter, float* pFrameOut, const float* pFrameIn) -{ - ma_biquad_process_pcm_frame_f32(&pFilter->bq, pFrameOut, pFrameIn); -} - -MA_API ma_result ma_hishelf2_process_pcm_frames(ma_hishelf2* pFilter, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount) -{ - if (pFilter == NULL) { - return MA_INVALID_ARGS; - } - - return ma_biquad_process_pcm_frames(&pFilter->bq, pFramesOut, pFramesIn, frameCount); -} - -MA_API ma_uint32 ma_hishelf2_get_latency(const ma_hishelf2* pFilter) -{ - if (pFilter == NULL) { - return 0; - } - - return ma_biquad_get_latency(&pFilter->bq); -} - - - -/* -Delay -*/ -MA_API ma_delay_config ma_delay_config_init(ma_uint32 channels, ma_uint32 sampleRate, ma_uint32 delayInFrames, float decay) -{ - ma_delay_config config; - - MA_ZERO_OBJECT(&config); - config.channels = channels; - config.sampleRate = sampleRate; - config.delayInFrames = delayInFrames; - config.delayStart = (decay == 0) ? MA_TRUE : MA_FALSE; /* Delay the start if it looks like we're not configuring an echo. */ - config.wet = 1; - config.dry = 1; - config.decay = decay; - - return config; -} - - -MA_API ma_result ma_delay_init(const ma_delay_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_delay* pDelay) -{ - if (pDelay == NULL) { - return MA_INVALID_ARGS; - } - - MA_ZERO_OBJECT(pDelay); - - if (pConfig == NULL) { - return MA_INVALID_ARGS; - } - - if (pConfig->decay < 0 || pConfig->decay > 1) { - return MA_INVALID_ARGS; - } - - pDelay->config = *pConfig; - pDelay->bufferSizeInFrames = pConfig->delayInFrames; - pDelay->cursor = 0; - - pDelay->pBuffer = (float*)ma_malloc((size_t)(pDelay->bufferSizeInFrames * ma_get_bytes_per_frame(ma_format_f32, pConfig->channels)), pAllocationCallbacks); - if (pDelay->pBuffer == NULL) { - return MA_OUT_OF_MEMORY; - } - - ma_silence_pcm_frames(pDelay->pBuffer, pDelay->bufferSizeInFrames, ma_format_f32, pConfig->channels); - - return MA_SUCCESS; -} - -MA_API void ma_delay_uninit(ma_delay* pDelay, const ma_allocation_callbacks* pAllocationCallbacks) -{ - if (pDelay == NULL) { - return; - } - - ma_free(pDelay->pBuffer, pAllocationCallbacks); -} - -MA_API ma_result ma_delay_process_pcm_frames(ma_delay* pDelay, void* pFramesOut, const void* pFramesIn, ma_uint32 frameCount) -{ - ma_uint32 iFrame; - ma_uint32 iChannel; - float* pFramesOutF32 = (float*)pFramesOut; - const float* pFramesInF32 = (const float*)pFramesIn; - - if (pDelay == NULL || pFramesOut == NULL || pFramesIn == NULL) { - return MA_INVALID_ARGS; - } - - for (iFrame = 0; iFrame < frameCount; iFrame += 1) { - for (iChannel = 0; iChannel < pDelay->config.channels; iChannel += 1) { - ma_uint32 iBuffer = (pDelay->cursor * pDelay->config.channels) + iChannel; - - if (pDelay->config.delayStart) { - /* Delayed start. */ - - /* Read */ - pFramesOutF32[iChannel] = pDelay->pBuffer[iBuffer] * pDelay->config.wet; - - /* Feedback */ - pDelay->pBuffer[iBuffer] = (pDelay->pBuffer[iBuffer] * pDelay->config.decay) + (pFramesInF32[iChannel] * pDelay->config.dry); - } else { - /* Immediate start */ - - /* Feedback */ - pDelay->pBuffer[iBuffer] = (pDelay->pBuffer[iBuffer] * pDelay->config.decay) + (pFramesInF32[iChannel] * pDelay->config.dry); - - /* Read */ - pFramesOutF32[iChannel] = pDelay->pBuffer[iBuffer] * pDelay->config.wet; - } - } - - pDelay->cursor = (pDelay->cursor + 1) % pDelay->bufferSizeInFrames; - - pFramesOutF32 += pDelay->config.channels; - pFramesInF32 += pDelay->config.channels; - } - - return MA_SUCCESS; -} - -MA_API void ma_delay_set_wet(ma_delay* pDelay, float value) -{ - if (pDelay == NULL) { - return; - } - - pDelay->config.wet = value; -} - -MA_API float ma_delay_get_wet(const ma_delay* pDelay) -{ - if (pDelay == NULL) { - return 0; - } - - return pDelay->config.wet; -} - -MA_API void ma_delay_set_dry(ma_delay* pDelay, float value) -{ - if (pDelay == NULL) { - return; - } - - pDelay->config.dry = value; -} - -MA_API float ma_delay_get_dry(const ma_delay* pDelay) -{ - if (pDelay == NULL) { - return 0; - } - - return pDelay->config.dry; -} - -MA_API void ma_delay_set_decay(ma_delay* pDelay, float value) -{ - if (pDelay == NULL) { - return; - } - - pDelay->config.decay = value; -} - -MA_API float ma_delay_get_decay(const ma_delay* pDelay) -{ - if (pDelay == NULL) { - return 0; - } - - return pDelay->config.decay; -} - - -MA_API ma_gainer_config ma_gainer_config_init(ma_uint32 channels, ma_uint32 smoothTimeInFrames) -{ - ma_gainer_config config; - - MA_ZERO_OBJECT(&config); - config.channels = channels; - config.smoothTimeInFrames = smoothTimeInFrames; - - return config; -} - - -typedef struct -{ - size_t sizeInBytes; - size_t oldGainsOffset; - size_t newGainsOffset; -} ma_gainer_heap_layout; - -static ma_result ma_gainer_get_heap_layout(const ma_gainer_config* pConfig, ma_gainer_heap_layout* pHeapLayout) -{ - MA_ASSERT(pHeapLayout != NULL); - - MA_ZERO_OBJECT(pHeapLayout); - - if (pConfig == NULL) { - return MA_INVALID_ARGS; - } - - if (pConfig->channels == 0) { - return MA_INVALID_ARGS; - } - - pHeapLayout->sizeInBytes = 0; - - /* Old gains. */ - pHeapLayout->oldGainsOffset = pHeapLayout->sizeInBytes; - pHeapLayout->sizeInBytes += sizeof(float) * pConfig->channels; - - /* New gains. */ - pHeapLayout->newGainsOffset = pHeapLayout->sizeInBytes; - pHeapLayout->sizeInBytes += sizeof(float) * pConfig->channels; - - /* Alignment. */ - pHeapLayout->sizeInBytes = ma_align_64(pHeapLayout->sizeInBytes); - - return MA_SUCCESS; -} - - -MA_API ma_result ma_gainer_get_heap_size(const ma_gainer_config* pConfig, size_t* pHeapSizeInBytes) -{ - ma_result result; - ma_gainer_heap_layout heapLayout; - - if (pHeapSizeInBytes == NULL) { - return MA_INVALID_ARGS; - } - - *pHeapSizeInBytes = 0; - - result = ma_gainer_get_heap_layout(pConfig, &heapLayout); - if (result != MA_SUCCESS) { - return MA_INVALID_ARGS; - } - - *pHeapSizeInBytes = heapLayout.sizeInBytes; - - return MA_SUCCESS; -} - - -MA_API ma_result ma_gainer_init_preallocated(const ma_gainer_config* pConfig, void* pHeap, ma_gainer* pGainer) -{ - ma_result result; - ma_gainer_heap_layout heapLayout; - ma_uint32 iChannel; - - if (pGainer == NULL) { - return MA_INVALID_ARGS; - } - - MA_ZERO_OBJECT(pGainer); - - if (pConfig == NULL || pHeap == NULL) { - return MA_INVALID_ARGS; - } - - result = ma_gainer_get_heap_layout(pConfig, &heapLayout); - if (result != MA_SUCCESS) { - return result; - } - - pGainer->_pHeap = pHeap; - MA_ZERO_MEMORY(pHeap, heapLayout.sizeInBytes); - - pGainer->pOldGains = (float*)ma_offset_ptr(pHeap, heapLayout.oldGainsOffset); - pGainer->pNewGains = (float*)ma_offset_ptr(pHeap, heapLayout.newGainsOffset); - pGainer->masterVolume = 1; - - pGainer->config = *pConfig; - pGainer->t = (ma_uint32)-1; /* No interpolation by default. */ - - for (iChannel = 0; iChannel < pConfig->channels; iChannel += 1) { - pGainer->pOldGains[iChannel] = 1; - pGainer->pNewGains[iChannel] = 1; - } - - return MA_SUCCESS; -} - -MA_API ma_result ma_gainer_init(const ma_gainer_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_gainer* pGainer) -{ - ma_result result; - size_t heapSizeInBytes; - void* pHeap; - - result = ma_gainer_get_heap_size(pConfig, &heapSizeInBytes); - if (result != MA_SUCCESS) { - return result; /* Failed to retrieve the size of the heap allocation. */ - } - - if (heapSizeInBytes > 0) { - pHeap = ma_malloc(heapSizeInBytes, pAllocationCallbacks); - if (pHeap == NULL) { - return MA_OUT_OF_MEMORY; - } - } else { - pHeap = NULL; - } - - result = ma_gainer_init_preallocated(pConfig, pHeap, pGainer); - if (result != MA_SUCCESS) { - ma_free(pHeap, pAllocationCallbacks); - return result; - } - - pGainer->_ownsHeap = MA_TRUE; - return MA_SUCCESS; -} - -MA_API void ma_gainer_uninit(ma_gainer* pGainer, const ma_allocation_callbacks* pAllocationCallbacks) -{ - if (pGainer == NULL) { - return; - } - - if (pGainer->_ownsHeap) { - ma_free(pGainer->_pHeap, pAllocationCallbacks); - } -} - -static float ma_gainer_calculate_current_gain(const ma_gainer* pGainer, ma_uint32 channel) -{ - float a = (float)pGainer->t / pGainer->config.smoothTimeInFrames; - return ma_mix_f32_fast(pGainer->pOldGains[channel], pGainer->pNewGains[channel], a); -} - -static /*__attribute__((noinline))*/ ma_result ma_gainer_process_pcm_frames_internal(ma_gainer * pGainer, void* MA_RESTRICT pFramesOut, const void* MA_RESTRICT pFramesIn, ma_uint64 frameCount) -{ - ma_uint64 iFrame; - ma_uint32 iChannel; - ma_uint64 interpolatedFrameCount; - - MA_ASSERT(pGainer != NULL); - - /* - We don't necessarily need to apply a linear interpolation for the entire frameCount frames. When - linear interpolation is not needed we can do a simple volume adjustment which will be more - efficient than a lerp with an alpha value of 1. - - To do this, all we need to do is determine how many frames need to have a lerp applied. Then we - just process that number of frames with linear interpolation. After that we run on an optimized - path which just applies the new gains without a lerp. - */ - if (pGainer->t >= pGainer->config.smoothTimeInFrames) { - interpolatedFrameCount = 0; - } else { - interpolatedFrameCount = pGainer->t - pGainer->config.smoothTimeInFrames; - if (interpolatedFrameCount > frameCount) { - interpolatedFrameCount = frameCount; - } - } - - /* - Start off with our interpolated frames. When we do this, we'll adjust frameCount and our pointers - so that the fast path can work naturally without consideration of the interpolated path. - */ - if (interpolatedFrameCount > 0) { - /* We can allow the input and output buffers to be null in which case we'll just update the internal timer. */ - if (pFramesOut != NULL && pFramesIn != NULL) { - /* - All we're really doing here is moving the old gains towards the new gains. We don't want to - be modifying the gains inside the ma_gainer object because that will break things. Instead - we can make a copy here on the stack. For extreme channel counts we can fall back to a slower - implementation which just uses a standard lerp. - */ - float* pFramesOutF32 = (float*)pFramesOut; - const float* pFramesInF32 = (const float*)pFramesIn; - float a = (float)pGainer->t / pGainer->config.smoothTimeInFrames; - float d = 1.0f / pGainer->config.smoothTimeInFrames; - - if (pGainer->config.channels <= 32) { - float pRunningGain[32]; - float pRunningGainDelta[32]; /* Could this be heap-allocated as part of the ma_gainer object? */ - - /* Initialize the running gain. */ - for (iChannel = 0; iChannel < pGainer->config.channels; iChannel += 1) { - float t = (pGainer->pNewGains[iChannel] - pGainer->pOldGains[iChannel]) * pGainer->masterVolume; - pRunningGainDelta[iChannel] = t * d; - pRunningGain[iChannel] = (pGainer->pOldGains[iChannel] * pGainer->masterVolume) + (t * a); - } - - iFrame = 0; - - /* Optimized paths for common channel counts. This is mostly just experimenting with some SIMD ideas. It's not necessarily final. */ - if (pGainer->config.channels == 2) { - #if defined(MA_SUPPORT_SSE2) - if (ma_has_sse2()) { - ma_uint64 unrolledLoopCount = interpolatedFrameCount >> 1; - - /* Expand some arrays so we can have a clean SIMD loop below. */ - __m128 runningGainDelta0 = _mm_set_ps(pRunningGainDelta[1], pRunningGainDelta[0], pRunningGainDelta[1], pRunningGainDelta[0]); - __m128 runningGain0 = _mm_set_ps(pRunningGain[1] + pRunningGainDelta[1], pRunningGain[0] + pRunningGainDelta[0], pRunningGain[1], pRunningGain[0]); - - for (; iFrame < unrolledLoopCount; iFrame += 1) { - _mm_storeu_ps(&pFramesOutF32[iFrame*4 + 0], _mm_mul_ps(_mm_loadu_ps(&pFramesInF32[iFrame*4 + 0]), runningGain0)); - runningGain0 = _mm_add_ps(runningGain0, runningGainDelta0); - } - - iFrame = unrolledLoopCount << 1; - } else - #endif - { - /* - Two different scalar implementations here. Clang (and I assume GCC) will vectorize - both of these, but the bottom version results in a nicer vectorization with less - instructions emitted. The problem, however, is that the bottom version runs slower - when compiled with MSVC. The top version will be partially vectorized by MSVC. - */ - #if defined(_MSC_VER) && !defined(__clang__) - ma_uint64 unrolledLoopCount = interpolatedFrameCount >> 1; - - /* Expand some arrays so we can have a clean 4x SIMD operation in the loop. */ - pRunningGainDelta[2] = pRunningGainDelta[0]; - pRunningGainDelta[3] = pRunningGainDelta[1]; - pRunningGain[2] = pRunningGain[0] + pRunningGainDelta[0]; - pRunningGain[3] = pRunningGain[1] + pRunningGainDelta[1]; - - for (; iFrame < unrolledLoopCount; iFrame += 1) { - pFramesOutF32[iFrame*4 + 0] = pFramesInF32[iFrame*4 + 0] * pRunningGain[0]; - pFramesOutF32[iFrame*4 + 1] = pFramesInF32[iFrame*4 + 1] * pRunningGain[1]; - pFramesOutF32[iFrame*4 + 2] = pFramesInF32[iFrame*4 + 2] * pRunningGain[2]; - pFramesOutF32[iFrame*4 + 3] = pFramesInF32[iFrame*4 + 3] * pRunningGain[3]; - - /* Move the running gain forward towards the new gain. */ - pRunningGain[0] += pRunningGainDelta[0]; - pRunningGain[1] += pRunningGainDelta[1]; - pRunningGain[2] += pRunningGainDelta[2]; - pRunningGain[3] += pRunningGainDelta[3]; - } - - iFrame = unrolledLoopCount << 1; - #else - for (; iFrame < interpolatedFrameCount; iFrame += 1) { - for (iChannel = 0; iChannel < 2; iChannel += 1) { - pFramesOutF32[iFrame*2 + iChannel] = pFramesInF32[iFrame*2 + iChannel] * pRunningGain[iChannel]; - } - - for (iChannel = 0; iChannel < 2; iChannel += 1) { - pRunningGain[iChannel] += pRunningGainDelta[iChannel]; - } - } - #endif - } - } else if (pGainer->config.channels == 6) { - #if defined(MA_SUPPORT_SSE2) - if (ma_has_sse2()) { - /* - For 6 channels things are a bit more complicated because 6 isn't cleanly divisible by 4. We need to do 2 frames - at a time, meaning we'll be doing 12 samples in a group. Like the stereo case we'll need to expand some arrays - so we can do clean 4x SIMD operations. - */ - ma_uint64 unrolledLoopCount = interpolatedFrameCount >> 1; - - /* Expand some arrays so we can have a clean SIMD loop below. */ - __m128 runningGainDelta0 = _mm_set_ps(pRunningGainDelta[3], pRunningGainDelta[2], pRunningGainDelta[1], pRunningGainDelta[0]); - __m128 runningGainDelta1 = _mm_set_ps(pRunningGainDelta[1], pRunningGainDelta[0], pRunningGainDelta[5], pRunningGainDelta[4]); - __m128 runningGainDelta2 = _mm_set_ps(pRunningGainDelta[5], pRunningGainDelta[4], pRunningGainDelta[3], pRunningGainDelta[2]); - - __m128 runningGain0 = _mm_set_ps(pRunningGain[3], pRunningGain[2], pRunningGain[1], pRunningGain[0]); - __m128 runningGain1 = _mm_set_ps(pRunningGain[1] + pRunningGainDelta[1], pRunningGain[0] + pRunningGainDelta[0], pRunningGain[5], pRunningGain[4]); - __m128 runningGain2 = _mm_set_ps(pRunningGain[5] + pRunningGainDelta[5], pRunningGain[4] + pRunningGainDelta[4], pRunningGain[3] + pRunningGainDelta[3], pRunningGain[2] + pRunningGainDelta[2]); - - for (; iFrame < unrolledLoopCount; iFrame += 1) { - _mm_storeu_ps(&pFramesOutF32[iFrame*12 + 0], _mm_mul_ps(_mm_loadu_ps(&pFramesInF32[iFrame*12 + 0]), runningGain0)); - _mm_storeu_ps(&pFramesOutF32[iFrame*12 + 4], _mm_mul_ps(_mm_loadu_ps(&pFramesInF32[iFrame*12 + 4]), runningGain1)); - _mm_storeu_ps(&pFramesOutF32[iFrame*12 + 8], _mm_mul_ps(_mm_loadu_ps(&pFramesInF32[iFrame*12 + 8]), runningGain2)); - - runningGain0 = _mm_add_ps(runningGain0, runningGainDelta0); - runningGain1 = _mm_add_ps(runningGain1, runningGainDelta1); - runningGain2 = _mm_add_ps(runningGain2, runningGainDelta2); - } - - iFrame = unrolledLoopCount << 1; - } else - #endif - { - for (; iFrame < interpolatedFrameCount; iFrame += 1) { - for (iChannel = 0; iChannel < 6; iChannel += 1) { - pFramesOutF32[iFrame*6 + iChannel] = pFramesInF32[iFrame*6 + iChannel] * pRunningGain[iChannel]; - } - - /* Move the running gain forward towards the new gain. */ - for (iChannel = 0; iChannel < 6; iChannel += 1) { - pRunningGain[iChannel] += pRunningGainDelta[iChannel]; - } - } - } - } else if (pGainer->config.channels == 8) { - /* For 8 channels we can just go over frame by frame and do all eight channels as 2 separate 4x SIMD operations. */ - #if defined(MA_SUPPORT_SSE2) - if (ma_has_sse2()) { - __m128 runningGainDelta0 = _mm_loadu_ps(&pRunningGainDelta[0]); - __m128 runningGainDelta1 = _mm_loadu_ps(&pRunningGainDelta[4]); - __m128 runningGain0 = _mm_loadu_ps(&pRunningGain[0]); - __m128 runningGain1 = _mm_loadu_ps(&pRunningGain[4]); - - for (; iFrame < interpolatedFrameCount; iFrame += 1) { - _mm_storeu_ps(&pFramesOutF32[iFrame*8 + 0], _mm_mul_ps(_mm_loadu_ps(&pFramesInF32[iFrame*8 + 0]), runningGain0)); - _mm_storeu_ps(&pFramesOutF32[iFrame*8 + 4], _mm_mul_ps(_mm_loadu_ps(&pFramesInF32[iFrame*8 + 4]), runningGain1)); - - runningGain0 = _mm_add_ps(runningGain0, runningGainDelta0); - runningGain1 = _mm_add_ps(runningGain1, runningGainDelta1); - } - } else - #endif - { - /* This is crafted so that it auto-vectorizes when compiled with Clang. */ - for (; iFrame < interpolatedFrameCount; iFrame += 1) { - for (iChannel = 0; iChannel < 8; iChannel += 1) { - pFramesOutF32[iFrame*8 + iChannel] = pFramesInF32[iFrame*8 + iChannel] * pRunningGain[iChannel]; - } - - /* Move the running gain forward towards the new gain. */ - for (iChannel = 0; iChannel < 8; iChannel += 1) { - pRunningGain[iChannel] += pRunningGainDelta[iChannel]; - } - } - } - } - - for (; iFrame < interpolatedFrameCount; iFrame += 1) { - for (iChannel = 0; iChannel < pGainer->config.channels; iChannel += 1) { - pFramesOutF32[iFrame*pGainer->config.channels + iChannel] = pFramesInF32[iFrame*pGainer->config.channels + iChannel] * pRunningGain[iChannel]; - pRunningGain[iChannel] += pRunningGainDelta[iChannel]; - } - } - } else { - /* Slower path for extreme channel counts where we can't fit enough on the stack. We could also move this to the heap as part of the ma_gainer object which might even be better since it'll only be updated when the gains actually change. */ - for (iFrame = 0; iFrame < interpolatedFrameCount; iFrame += 1) { - for (iChannel = 0; iChannel < pGainer->config.channels; iChannel += 1) { - pFramesOutF32[iFrame*pGainer->config.channels + iChannel] = pFramesInF32[iFrame*pGainer->config.channels + iChannel] * ma_mix_f32_fast(pGainer->pOldGains[iChannel], pGainer->pNewGains[iChannel], a) * pGainer->masterVolume; - } - - a += d; - } - } - } - - /* Make sure the timer is updated. */ - pGainer->t = (ma_uint32)ma_min(pGainer->t + interpolatedFrameCount, pGainer->config.smoothTimeInFrames); - - /* Adjust our arguments so the next part can work normally. */ - frameCount -= interpolatedFrameCount; - pFramesOut = ma_offset_ptr(pFramesOut, interpolatedFrameCount * sizeof(float)); - pFramesIn = ma_offset_ptr(pFramesIn, interpolatedFrameCount * sizeof(float)); - } - - /* All we need to do here is apply the new gains using an optimized path. */ - if (pFramesOut != NULL && pFramesIn != NULL) { - if (pGainer->config.channels <= 32) { - float gains[32]; - for (iChannel = 0; iChannel < pGainer->config.channels; iChannel += 1) { - gains[iChannel] = pGainer->pNewGains[iChannel] * pGainer->masterVolume; - } - - ma_copy_and_apply_volume_factor_per_channel_f32((float*)pFramesOut, (const float*)pFramesIn, frameCount, pGainer->config.channels, gains); - } else { - /* Slow path. Too many channels to fit on the stack. Need to apply a master volume as a separate path. */ - for (iFrame = 0; iFrame < frameCount; iFrame += 1) { - for (iChannel = 0; iChannel < pGainer->config.channels; iChannel += 1) { - ((float*)pFramesOut)[iFrame*pGainer->config.channels + iChannel] = ((const float*)pFramesIn)[iFrame*pGainer->config.channels + iChannel] * pGainer->pNewGains[iChannel] * pGainer->masterVolume; - } - } - } - } - - /* Now that some frames have been processed we need to make sure future changes to the gain are interpolated. */ - if (pGainer->t == (ma_uint32)-1) { - pGainer->t = (ma_uint32)ma_min(pGainer->config.smoothTimeInFrames, frameCount); - } - -#if 0 - if (pGainer->t >= pGainer->config.smoothTimeInFrames) { - /* Fast path. No gain calculation required. */ - ma_copy_and_apply_volume_factor_per_channel_f32(pFramesOutF32, pFramesInF32, frameCount, pGainer->config.channels, pGainer->pNewGains); - ma_apply_volume_factor_f32(pFramesOutF32, frameCount * pGainer->config.channels, pGainer->masterVolume); - - /* Now that some frames have been processed we need to make sure future changes to the gain are interpolated. */ - if (pGainer->t == (ma_uint32)-1) { - pGainer->t = pGainer->config.smoothTimeInFrames; - } - } else { - /* Slow path. Need to interpolate the gain for each channel individually. */ - - /* We can allow the input and output buffers to be null in which case we'll just update the internal timer. */ - if (pFramesOut != NULL && pFramesIn != NULL) { - float a = (float)pGainer->t / pGainer->config.smoothTimeInFrames; - float d = 1.0f / pGainer->config.smoothTimeInFrames; - ma_uint32 channelCount = pGainer->config.channels; - - for (iFrame = 0; iFrame < frameCount; iFrame += 1) { - for (iChannel = 0; iChannel < channelCount; iChannel += 1) { - pFramesOutF32[iChannel] = pFramesInF32[iChannel] * ma_mix_f32_fast(pGainer->pOldGains[iChannel], pGainer->pNewGains[iChannel], a) * pGainer->masterVolume; - } - - pFramesOutF32 += channelCount; - pFramesInF32 += channelCount; - - a += d; - if (a > 1) { - a = 1; - } - } - } - - pGainer->t = (ma_uint32)ma_min(pGainer->t + frameCount, pGainer->config.smoothTimeInFrames); - - #if 0 /* Reference implementation. */ - for (iFrame = 0; iFrame < frameCount; iFrame += 1) { - /* We can allow the input and output buffers to be null in which case we'll just update the internal timer. */ - if (pFramesOut != NULL && pFramesIn != NULL) { - for (iChannel = 0; iChannel < pGainer->config.channels; iChannel += 1) { - pFramesOutF32[iFrame * pGainer->config.channels + iChannel] = pFramesInF32[iFrame * pGainer->config.channels + iChannel] * ma_gainer_calculate_current_gain(pGainer, iChannel) * pGainer->masterVolume; - } - } - - /* Move interpolation time forward, but don't go beyond our smoothing time. */ - pGainer->t = ma_min(pGainer->t + 1, pGainer->config.smoothTimeInFrames); - } - #endif - } -#endif - - return MA_SUCCESS; -} - -MA_API ma_result ma_gainer_process_pcm_frames(ma_gainer* pGainer, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount) -{ - if (pGainer == NULL) { - return MA_INVALID_ARGS; - } - - /* - ma_gainer_process_pcm_frames_internal() marks pFramesOut and pFramesIn with MA_RESTRICT which - helps with auto-vectorization. - */ - return ma_gainer_process_pcm_frames_internal(pGainer, pFramesOut, pFramesIn, frameCount); -} - -static void ma_gainer_set_gain_by_index(ma_gainer* pGainer, float newGain, ma_uint32 iChannel) -{ - pGainer->pOldGains[iChannel] = ma_gainer_calculate_current_gain(pGainer, iChannel); - pGainer->pNewGains[iChannel] = newGain; -} - -static void ma_gainer_reset_smoothing_time(ma_gainer* pGainer) -{ - if (pGainer->t == (ma_uint32)-1) { - pGainer->t = pGainer->config.smoothTimeInFrames; /* No smoothing required for initial gains setting. */ - } else { - pGainer->t = 0; - } -} - -MA_API ma_result ma_gainer_set_gain(ma_gainer* pGainer, float newGain) -{ - ma_uint32 iChannel; - - if (pGainer == NULL) { - return MA_INVALID_ARGS; - } - - for (iChannel = 0; iChannel < pGainer->config.channels; iChannel += 1) { - ma_gainer_set_gain_by_index(pGainer, newGain, iChannel); - } - - /* The smoothing time needs to be reset to ensure we always interpolate by the configured smoothing time, but only if it's not the first setting. */ - ma_gainer_reset_smoothing_time(pGainer); - - return MA_SUCCESS; -} - -MA_API ma_result ma_gainer_set_gains(ma_gainer* pGainer, float* pNewGains) -{ - ma_uint32 iChannel; - - if (pGainer == NULL || pNewGains == NULL) { - return MA_INVALID_ARGS; - } - - for (iChannel = 0; iChannel < pGainer->config.channels; iChannel += 1) { - ma_gainer_set_gain_by_index(pGainer, pNewGains[iChannel], iChannel); - } - - /* The smoothing time needs to be reset to ensure we always interpolate by the configured smoothing time, but only if it's not the first setting. */ - ma_gainer_reset_smoothing_time(pGainer); - - return MA_SUCCESS; -} - -MA_API ma_result ma_gainer_set_master_volume(ma_gainer* pGainer, float volume) -{ - if (pGainer == NULL) { - return MA_INVALID_ARGS; - } - - pGainer->masterVolume = volume; - - return MA_SUCCESS; -} - -MA_API ma_result ma_gainer_get_master_volume(const ma_gainer* pGainer, float* pVolume) -{ - if (pGainer == NULL || pVolume == NULL) { - return MA_INVALID_ARGS; - } - - *pVolume = pGainer->masterVolume; - - return MA_SUCCESS; -} - - -MA_API ma_panner_config ma_panner_config_init(ma_format format, ma_uint32 channels) -{ - ma_panner_config config; - - MA_ZERO_OBJECT(&config); - config.format = format; - config.channels = channels; - config.mode = ma_pan_mode_balance; /* Set to balancing mode by default because it's consistent with other audio engines and most likely what the caller is expecting. */ - config.pan = 0; - - return config; -} - - -MA_API ma_result ma_panner_init(const ma_panner_config* pConfig, ma_panner* pPanner) -{ - if (pPanner == NULL) { - return MA_INVALID_ARGS; - } - - MA_ZERO_OBJECT(pPanner); - - if (pConfig == NULL) { - return MA_INVALID_ARGS; - } - - pPanner->format = pConfig->format; - pPanner->channels = pConfig->channels; - pPanner->mode = pConfig->mode; - pPanner->pan = pConfig->pan; - - return MA_SUCCESS; -} - -static void ma_stereo_balance_pcm_frames_f32(float* pFramesOut, const float* pFramesIn, ma_uint64 frameCount, float pan) -{ - ma_uint64 iFrame; - - if (pan > 0) { - float factor = 1.0f - pan; - if (pFramesOut == pFramesIn) { - for (iFrame = 0; iFrame < frameCount; iFrame += 1) { - pFramesOut[iFrame*2 + 0] = pFramesIn[iFrame*2 + 0] * factor; - } - } else { - for (iFrame = 0; iFrame < frameCount; iFrame += 1) { - pFramesOut[iFrame*2 + 0] = pFramesIn[iFrame*2 + 0] * factor; - pFramesOut[iFrame*2 + 1] = pFramesIn[iFrame*2 + 1]; - } - } - } else { - float factor = 1.0f + pan; - if (pFramesOut == pFramesIn) { - for (iFrame = 0; iFrame < frameCount; iFrame += 1) { - pFramesOut[iFrame*2 + 1] = pFramesIn[iFrame*2 + 1] * factor; - } - } else { - for (iFrame = 0; iFrame < frameCount; iFrame += 1) { - pFramesOut[iFrame*2 + 0] = pFramesIn[iFrame*2 + 0]; - pFramesOut[iFrame*2 + 1] = pFramesIn[iFrame*2 + 1] * factor; - } - } - } -} - -static void ma_stereo_balance_pcm_frames(void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount, ma_format format, float pan) -{ - if (pan == 0) { - /* Fast path. No panning required. */ - if (pFramesOut == pFramesIn) { - /* No-op */ - } else { - ma_copy_pcm_frames(pFramesOut, pFramesIn, frameCount, format, 2); - } - - return; - } - - switch (format) { - case ma_format_f32: ma_stereo_balance_pcm_frames_f32((float*)pFramesOut, (float*)pFramesIn, frameCount, pan); break; - - /* Unknown format. Just copy. */ - default: - { - ma_copy_pcm_frames(pFramesOut, pFramesIn, frameCount, format, 2); - } break; - } -} - - -static void ma_stereo_pan_pcm_frames_f32(float* pFramesOut, const float* pFramesIn, ma_uint64 frameCount, float pan) -{ - ma_uint64 iFrame; - - if (pan > 0) { - float factorL0 = 1.0f - pan; - float factorL1 = 0.0f + pan; - - for (iFrame = 0; iFrame < frameCount; iFrame += 1) { - float sample0 = (pFramesIn[iFrame*2 + 0] * factorL0); - float sample1 = (pFramesIn[iFrame*2 + 0] * factorL1) + pFramesIn[iFrame*2 + 1]; - - pFramesOut[iFrame*2 + 0] = sample0; - pFramesOut[iFrame*2 + 1] = sample1; - } - } else { - float factorR0 = 0.0f - pan; - float factorR1 = 1.0f + pan; - - for (iFrame = 0; iFrame < frameCount; iFrame += 1) { - float sample0 = pFramesIn[iFrame*2 + 0] + (pFramesIn[iFrame*2 + 1] * factorR0); - float sample1 = (pFramesIn[iFrame*2 + 1] * factorR1); - - pFramesOut[iFrame*2 + 0] = sample0; - pFramesOut[iFrame*2 + 1] = sample1; - } - } -} - -static void ma_stereo_pan_pcm_frames(void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount, ma_format format, float pan) -{ - if (pan == 0) { - /* Fast path. No panning required. */ - if (pFramesOut == pFramesIn) { - /* No-op */ - } else { - ma_copy_pcm_frames(pFramesOut, pFramesIn, frameCount, format, 2); - } - - return; - } - - switch (format) { - case ma_format_f32: ma_stereo_pan_pcm_frames_f32((float*)pFramesOut, (float*)pFramesIn, frameCount, pan); break; - - /* Unknown format. Just copy. */ - default: - { - ma_copy_pcm_frames(pFramesOut, pFramesIn, frameCount, format, 2); - } break; - } -} - -MA_API ma_result ma_panner_process_pcm_frames(ma_panner* pPanner, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount) -{ - if (pPanner == NULL || pFramesOut == NULL || pFramesIn == NULL) { - return MA_INVALID_ARGS; - } - - if (pPanner->channels == 2) { - /* Stereo case. For now assume channel 0 is left and channel right is 1, but should probably add support for a channel map. */ - if (pPanner->mode == ma_pan_mode_balance) { - ma_stereo_balance_pcm_frames(pFramesOut, pFramesIn, frameCount, pPanner->format, pPanner->pan); - } else { - ma_stereo_pan_pcm_frames(pFramesOut, pFramesIn, frameCount, pPanner->format, pPanner->pan); - } - } else { - if (pPanner->channels == 1) { - /* Panning has no effect on mono streams. */ - ma_copy_pcm_frames(pFramesOut, pFramesIn, frameCount, pPanner->format, pPanner->channels); - } else { - /* For now we're not going to support non-stereo set ups. Not sure how I want to handle this case just yet. */ - ma_copy_pcm_frames(pFramesOut, pFramesIn, frameCount, pPanner->format, pPanner->channels); - } - } - - return MA_SUCCESS; -} - -MA_API void ma_panner_set_mode(ma_panner* pPanner, ma_pan_mode mode) -{ - if (pPanner == NULL) { - return; - } - - pPanner->mode = mode; -} - -MA_API ma_pan_mode ma_panner_get_mode(const ma_panner* pPanner) -{ - if (pPanner == NULL) { - return ma_pan_mode_balance; - } - - return pPanner->mode; -} - -MA_API void ma_panner_set_pan(ma_panner* pPanner, float pan) -{ - if (pPanner == NULL) { - return; - } - - pPanner->pan = ma_clamp(pan, -1.0f, 1.0f); -} - -MA_API float ma_panner_get_pan(const ma_panner* pPanner) -{ - if (pPanner == NULL) { - return 0; - } - - return pPanner->pan; -} - - - - -MA_API ma_fader_config ma_fader_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRate) -{ - ma_fader_config config; - - MA_ZERO_OBJECT(&config); - config.format = format; - config.channels = channels; - config.sampleRate = sampleRate; - - return config; -} - - -MA_API ma_result ma_fader_init(const ma_fader_config* pConfig, ma_fader* pFader) -{ - if (pFader == NULL) { - return MA_INVALID_ARGS; - } - - MA_ZERO_OBJECT(pFader); - - if (pConfig == NULL) { - return MA_INVALID_ARGS; - } - - /* Only f32 is supported for now. */ - if (pConfig->format != ma_format_f32) { - return MA_INVALID_ARGS; - } - - pFader->config = *pConfig; - pFader->volumeBeg = 1; - pFader->volumeEnd = 1; - pFader->lengthInFrames = 0; - pFader->cursorInFrames = 0; - - return MA_SUCCESS; -} - -MA_API ma_result ma_fader_process_pcm_frames(ma_fader* pFader, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount) -{ - if (pFader == NULL) { - return MA_INVALID_ARGS; - } - - /* If the cursor is still negative we need to just copy the absolute number of those frames, but no more than frameCount. */ - if (pFader->cursorInFrames < 0) { - ma_uint64 absCursorInFrames = (ma_uint64)0 - pFader->cursorInFrames; - if (absCursorInFrames > frameCount) { - absCursorInFrames = frameCount; - } - - ma_copy_pcm_frames(pFramesOut, pFramesIn, absCursorInFrames, pFader->config.format, pFader->config.channels); - - pFader->cursorInFrames += absCursorInFrames; - frameCount -= absCursorInFrames; - pFramesOut = ma_offset_ptr(pFramesOut, ma_get_bytes_per_frame(pFader->config.format, pFader->config.channels)*absCursorInFrames); - pFramesIn = ma_offset_ptr(pFramesIn, ma_get_bytes_per_frame(pFader->config.format, pFader->config.channels)*absCursorInFrames); - } - - if (pFader->cursorInFrames >= 0) { - /* - For now we need to clamp frameCount so that the cursor never overflows 32-bits. This is required for - the conversion to a float which we use for the linear interpolation. This might be changed later. - */ - if (frameCount + pFader->cursorInFrames > UINT_MAX) { - frameCount = UINT_MAX - pFader->cursorInFrames; - } - - /* Optimized path if volumeBeg and volumeEnd are equal. */ - if (pFader->volumeBeg == pFader->volumeEnd) { - if (pFader->volumeBeg == 1) { - /* Straight copy. */ - ma_copy_pcm_frames(pFramesOut, pFramesIn, frameCount, pFader->config.format, pFader->config.channels); - } else { - /* Copy with volume. */ - ma_copy_and_apply_volume_and_clip_pcm_frames(pFramesOut, pFramesIn, frameCount, pFader->config.format, pFader->config.channels, pFader->volumeBeg); - } - } else { - /* Slower path. Volumes are different, so may need to do an interpolation. */ - if ((ma_uint64)pFader->cursorInFrames >= pFader->lengthInFrames) { - /* Fast path. We've gone past the end of the fade period so just apply the end volume to all samples. */ - ma_copy_and_apply_volume_and_clip_pcm_frames(pFramesOut, pFramesIn, frameCount, pFader->config.format, pFader->config.channels, pFader->volumeEnd); - } else { - /* Slow path. This is where we do the actual fading. */ - ma_uint64 iFrame; - ma_uint32 iChannel; - - /* For now we only support f32. Support for other formats might be added later. */ - if (pFader->config.format == ma_format_f32) { - const float* pFramesInF32 = (const float*)pFramesIn; - /* */ float* pFramesOutF32 = ( float*)pFramesOut; - - for (iFrame = 0; iFrame < frameCount; iFrame += 1) { - float a = (ma_uint32)ma_min(pFader->cursorInFrames + iFrame, pFader->lengthInFrames) / (float)((ma_uint32)pFader->lengthInFrames); /* Safe cast due to the frameCount clamp at the top of this function. */ - float volume = ma_mix_f32_fast(pFader->volumeBeg, pFader->volumeEnd, a); - - for (iChannel = 0; iChannel < pFader->config.channels; iChannel += 1) { - pFramesOutF32[iFrame*pFader->config.channels + iChannel] = pFramesInF32[iFrame*pFader->config.channels + iChannel] * volume; - } - } - } else { - return MA_NOT_IMPLEMENTED; - } - } - } - } - - pFader->cursorInFrames += frameCount; - - return MA_SUCCESS; -} - -MA_API void ma_fader_get_data_format(const ma_fader* pFader, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate) -{ - if (pFader == NULL) { - return; - } - - if (pFormat != NULL) { - *pFormat = pFader->config.format; - } - - if (pChannels != NULL) { - *pChannels = pFader->config.channels; - } - - if (pSampleRate != NULL) { - *pSampleRate = pFader->config.sampleRate; - } -} - -MA_API void ma_fader_set_fade(ma_fader* pFader, float volumeBeg, float volumeEnd, ma_uint64 lengthInFrames) -{ - ma_fader_set_fade_ex(pFader, volumeBeg, volumeEnd, lengthInFrames, 0); -} - -MA_API void ma_fader_set_fade_ex(ma_fader* pFader, float volumeBeg, float volumeEnd, ma_uint64 lengthInFrames, ma_int64 startOffsetInFrames) -{ - if (pFader == NULL) { - return; - } - - /* If the volume is negative, use current volume. */ - if (volumeBeg < 0) { - volumeBeg = ma_fader_get_current_volume(pFader); - } - - /* - The length needs to be clamped to 32-bits due to how we convert it to a float for linear - interpolation reasons. I might change this requirement later, but for now it's not important. - */ - if (lengthInFrames > UINT_MAX) { - lengthInFrames = UINT_MAX; - } - - /* The start offset needs to be clamped to ensure it doesn't overflow a signed number. */ - if (startOffsetInFrames > INT_MAX) { - startOffsetInFrames = INT_MAX; - } - - pFader->volumeBeg = volumeBeg; - pFader->volumeEnd = volumeEnd; - pFader->lengthInFrames = lengthInFrames; - pFader->cursorInFrames = -startOffsetInFrames; -} - -MA_API float ma_fader_get_current_volume(const ma_fader* pFader) -{ - if (pFader == NULL) { - return 0.0f; - } - - /* Any frames prior to the start of the fade period will be at unfaded volume. */ - if (pFader->cursorInFrames < 0) { - return 1.0f; - } - - /* The current volume depends on the position of the cursor. */ - if (pFader->cursorInFrames == 0) { - return pFader->volumeBeg; - } else if ((ma_uint64)pFader->cursorInFrames >= pFader->lengthInFrames) { /* Safe case because the < 0 case was checked above. */ - return pFader->volumeEnd; - } else { - /* The cursor is somewhere inside the fading period. We can figure this out with a simple linear interpolation between volumeBeg and volumeEnd based on our cursor position. */ - return ma_mix_f32_fast(pFader->volumeBeg, pFader->volumeEnd, (ma_uint32)pFader->cursorInFrames / (float)((ma_uint32)pFader->lengthInFrames)); /* Safe cast to uint32 because we clamp it in ma_fader_process_pcm_frames(). */ - } -} - - - - - -MA_API ma_vec3f ma_vec3f_init_3f(float x, float y, float z) -{ - ma_vec3f v; - - v.x = x; - v.y = y; - v.z = z; - - return v; -} - -MA_API ma_vec3f ma_vec3f_sub(ma_vec3f a, ma_vec3f b) -{ - return ma_vec3f_init_3f( - a.x - b.x, - a.y - b.y, - a.z - b.z - ); -} - -MA_API ma_vec3f ma_vec3f_neg(ma_vec3f a) -{ - return ma_vec3f_init_3f( - -a.x, - -a.y, - -a.z - ); -} - -MA_API float ma_vec3f_dot(ma_vec3f a, ma_vec3f b) -{ - return a.x*b.x + a.y*b.y + a.z*b.z; -} - -MA_API float ma_vec3f_len2(ma_vec3f v) -{ - return ma_vec3f_dot(v, v); -} - -MA_API float ma_vec3f_len(ma_vec3f v) -{ - return (float)ma_sqrtd(ma_vec3f_len2(v)); -} - - - -MA_API float ma_vec3f_dist(ma_vec3f a, ma_vec3f b) -{ - return ma_vec3f_len(ma_vec3f_sub(a, b)); -} - -MA_API ma_vec3f ma_vec3f_normalize(ma_vec3f v) -{ - float invLen; - float len2 = ma_vec3f_len2(v); - if (len2 == 0) { - return ma_vec3f_init_3f(0, 0, 0); - } - - invLen = ma_rsqrtf(len2); - v.x *= invLen; - v.y *= invLen; - v.z *= invLen; - - return v; -} - -MA_API ma_vec3f ma_vec3f_cross(ma_vec3f a, ma_vec3f b) -{ - return ma_vec3f_init_3f( - a.y*b.z - a.z*b.y, - a.z*b.x - a.x*b.z, - a.x*b.y - a.y*b.x - ); -} - - -MA_API void ma_atomic_vec3f_init(ma_atomic_vec3f* v, ma_vec3f value) -{ - v->v = value; - v->lock = 0; /* Important this is initialized to 0. */ -} - -MA_API void ma_atomic_vec3f_set(ma_atomic_vec3f* v, ma_vec3f value) -{ - ma_spinlock_lock(&v->lock); - { - v->v = value; - } - ma_spinlock_unlock(&v->lock); -} - -MA_API ma_vec3f ma_atomic_vec3f_get(ma_atomic_vec3f* v) -{ - ma_vec3f r; - - ma_spinlock_lock(&v->lock); - { - r = v->v; - } - ma_spinlock_unlock(&v->lock); - - return r; -} - - - -static void ma_channel_map_apply_f32(float* pFramesOut, const ma_channel* pChannelMapOut, ma_uint32 channelsOut, const float* pFramesIn, const ma_channel* pChannelMapIn, ma_uint32 channelsIn, ma_uint64 frameCount, ma_channel_mix_mode mode, ma_mono_expansion_mode monoExpansionMode); -static ma_bool32 ma_is_spatial_channel_position(ma_channel channelPosition); - - -#ifndef MA_DEFAULT_SPEED_OF_SOUND -#define MA_DEFAULT_SPEED_OF_SOUND 343.3f -#endif - -/* -These vectors represent the direction that speakers are facing from the center point. They're used -for panning in the spatializer. Must be normalized. -*/ -static ma_vec3f g_maChannelDirections[MA_CHANNEL_POSITION_COUNT] = { - { 0.0f, 0.0f, -1.0f }, /* MA_CHANNEL_NONE */ - { 0.0f, 0.0f, -1.0f }, /* MA_CHANNEL_MONO */ - {-0.7071f, 0.0f, -0.7071f }, /* MA_CHANNEL_FRONT_LEFT */ - {+0.7071f, 0.0f, -0.7071f }, /* MA_CHANNEL_FRONT_RIGHT */ - { 0.0f, 0.0f, -1.0f }, /* MA_CHANNEL_FRONT_CENTER */ - { 0.0f, 0.0f, -1.0f }, /* MA_CHANNEL_LFE */ - {-0.7071f, 0.0f, +0.7071f }, /* MA_CHANNEL_BACK_LEFT */ - {+0.7071f, 0.0f, +0.7071f }, /* MA_CHANNEL_BACK_RIGHT */ - {-0.3162f, 0.0f, -0.9487f }, /* MA_CHANNEL_FRONT_LEFT_CENTER */ - {+0.3162f, 0.0f, -0.9487f }, /* MA_CHANNEL_FRONT_RIGHT_CENTER */ - { 0.0f, 0.0f, +1.0f }, /* MA_CHANNEL_BACK_CENTER */ - {-1.0f, 0.0f, 0.0f }, /* MA_CHANNEL_SIDE_LEFT */ - {+1.0f, 0.0f, 0.0f }, /* MA_CHANNEL_SIDE_RIGHT */ - { 0.0f, +1.0f, 0.0f }, /* MA_CHANNEL_TOP_CENTER */ - {-0.5774f, +0.5774f, -0.5774f }, /* MA_CHANNEL_TOP_FRONT_LEFT */ - { 0.0f, +0.7071f, -0.7071f }, /* MA_CHANNEL_TOP_FRONT_CENTER */ - {+0.5774f, +0.5774f, -0.5774f }, /* MA_CHANNEL_TOP_FRONT_RIGHT */ - {-0.5774f, +0.5774f, +0.5774f }, /* MA_CHANNEL_TOP_BACK_LEFT */ - { 0.0f, +0.7071f, +0.7071f }, /* MA_CHANNEL_TOP_BACK_CENTER */ - {+0.5774f, +0.5774f, +0.5774f }, /* MA_CHANNEL_TOP_BACK_RIGHT */ - { 0.0f, 0.0f, -1.0f }, /* MA_CHANNEL_AUX_0 */ - { 0.0f, 0.0f, -1.0f }, /* MA_CHANNEL_AUX_1 */ - { 0.0f, 0.0f, -1.0f }, /* MA_CHANNEL_AUX_2 */ - { 0.0f, 0.0f, -1.0f }, /* MA_CHANNEL_AUX_3 */ - { 0.0f, 0.0f, -1.0f }, /* MA_CHANNEL_AUX_4 */ - { 0.0f, 0.0f, -1.0f }, /* MA_CHANNEL_AUX_5 */ - { 0.0f, 0.0f, -1.0f }, /* MA_CHANNEL_AUX_6 */ - { 0.0f, 0.0f, -1.0f }, /* MA_CHANNEL_AUX_7 */ - { 0.0f, 0.0f, -1.0f }, /* MA_CHANNEL_AUX_8 */ - { 0.0f, 0.0f, -1.0f }, /* MA_CHANNEL_AUX_9 */ - { 0.0f, 0.0f, -1.0f }, /* MA_CHANNEL_AUX_10 */ - { 0.0f, 0.0f, -1.0f }, /* MA_CHANNEL_AUX_11 */ - { 0.0f, 0.0f, -1.0f }, /* MA_CHANNEL_AUX_12 */ - { 0.0f, 0.0f, -1.0f }, /* MA_CHANNEL_AUX_13 */ - { 0.0f, 0.0f, -1.0f }, /* MA_CHANNEL_AUX_14 */ - { 0.0f, 0.0f, -1.0f }, /* MA_CHANNEL_AUX_15 */ - { 0.0f, 0.0f, -1.0f }, /* MA_CHANNEL_AUX_16 */ - { 0.0f, 0.0f, -1.0f }, /* MA_CHANNEL_AUX_17 */ - { 0.0f, 0.0f, -1.0f }, /* MA_CHANNEL_AUX_18 */ - { 0.0f, 0.0f, -1.0f }, /* MA_CHANNEL_AUX_19 */ - { 0.0f, 0.0f, -1.0f }, /* MA_CHANNEL_AUX_20 */ - { 0.0f, 0.0f, -1.0f }, /* MA_CHANNEL_AUX_21 */ - { 0.0f, 0.0f, -1.0f }, /* MA_CHANNEL_AUX_22 */ - { 0.0f, 0.0f, -1.0f }, /* MA_CHANNEL_AUX_23 */ - { 0.0f, 0.0f, -1.0f }, /* MA_CHANNEL_AUX_24 */ - { 0.0f, 0.0f, -1.0f }, /* MA_CHANNEL_AUX_25 */ - { 0.0f, 0.0f, -1.0f }, /* MA_CHANNEL_AUX_26 */ - { 0.0f, 0.0f, -1.0f }, /* MA_CHANNEL_AUX_27 */ - { 0.0f, 0.0f, -1.0f }, /* MA_CHANNEL_AUX_28 */ - { 0.0f, 0.0f, -1.0f }, /* MA_CHANNEL_AUX_29 */ - { 0.0f, 0.0f, -1.0f }, /* MA_CHANNEL_AUX_30 */ - { 0.0f, 0.0f, -1.0f } /* MA_CHANNEL_AUX_31 */ -}; - -static ma_vec3f ma_get_channel_direction(ma_channel channel) -{ - if (channel >= MA_CHANNEL_POSITION_COUNT) { - return ma_vec3f_init_3f(0, 0, -1); - } else { - return g_maChannelDirections[channel]; - } -} - - - -static float ma_attenuation_inverse(float distance, float minDistance, float maxDistance, float rolloff) -{ - if (minDistance >= maxDistance) { - return 1; /* To avoid division by zero. Do not attenuate. */ - } - - return minDistance / (minDistance + rolloff * (ma_clamp(distance, minDistance, maxDistance) - minDistance)); -} - -static float ma_attenuation_linear(float distance, float minDistance, float maxDistance, float rolloff) -{ - if (minDistance >= maxDistance) { - return 1; /* To avoid division by zero. Do not attenuate. */ - } - - return 1 - rolloff * (ma_clamp(distance, minDistance, maxDistance) - minDistance) / (maxDistance - minDistance); -} - -static float ma_attenuation_exponential(float distance, float minDistance, float maxDistance, float rolloff) -{ - if (minDistance >= maxDistance) { - return 1; /* To avoid division by zero. Do not attenuate. */ - } - - return (float)ma_powd(ma_clamp(distance, minDistance, maxDistance) / minDistance, -rolloff); -} - - -/* -Doppler Effect calculation taken from the OpenAL spec, with two main differences: - - 1) The source to listener vector will have already been calculated at an earlier step so we can - just use that directly. We need only the position of the source relative to the origin. - - 2) We don't scale by a frequency because we actually just want the ratio which we'll plug straight - into the resampler directly. -*/ -static float ma_doppler_pitch(ma_vec3f relativePosition, ma_vec3f sourceVelocity, ma_vec3f listenVelocity, float speedOfSound, float dopplerFactor) -{ - float len; - float vls; - float vss; - - len = ma_vec3f_len(relativePosition); - - /* - There's a case where the position of the source will be right on top of the listener in which - case the length will be 0 and we'll end up with a division by zero. We can just return a ratio - of 1.0 in this case. This is not considered in the OpenAL spec, but is necessary. - */ - if (len == 0) { - return 1.0; - } - - vls = ma_vec3f_dot(relativePosition, listenVelocity) / len; - vss = ma_vec3f_dot(relativePosition, sourceVelocity) / len; - - vls = ma_min(vls, speedOfSound / dopplerFactor); - vss = ma_min(vss, speedOfSound / dopplerFactor); - - return (speedOfSound - dopplerFactor*vls) / (speedOfSound - dopplerFactor*vss); -} - - -static void ma_get_default_channel_map_for_spatializer(ma_channel* pChannelMap, size_t channelMapCap, ma_uint32 channelCount) -{ - /* - Special case for stereo. Want to default the left and right speakers to side left and side - right so that they're facing directly down the X axis rather than slightly forward. Not - doing this will result in sounds being quieter when behind the listener. This might - actually be good for some scenarios, but I don't think it's an appropriate default because - it can be a bit unexpected. - */ - if (channelCount == 2) { - pChannelMap[0] = MA_CHANNEL_SIDE_LEFT; - pChannelMap[1] = MA_CHANNEL_SIDE_RIGHT; - } else { - ma_channel_map_init_standard(ma_standard_channel_map_default, pChannelMap, channelMapCap, channelCount); - } -} - - -MA_API ma_spatializer_listener_config ma_spatializer_listener_config_init(ma_uint32 channelsOut) -{ - ma_spatializer_listener_config config; - - MA_ZERO_OBJECT(&config); - config.channelsOut = channelsOut; - config.pChannelMapOut = NULL; - config.handedness = ma_handedness_right; - config.worldUp = ma_vec3f_init_3f(0, 1, 0); - config.coneInnerAngleInRadians = 6.283185f; /* 360 degrees. */ - config.coneOuterAngleInRadians = 6.283185f; /* 360 degrees. */ - config.coneOuterGain = 0; - config.speedOfSound = 343.3f; /* Same as OpenAL. Used for doppler effect. */ - - return config; -} - - -typedef struct -{ - size_t sizeInBytes; - size_t channelMapOutOffset; -} ma_spatializer_listener_heap_layout; - -static ma_result ma_spatializer_listener_get_heap_layout(const ma_spatializer_listener_config* pConfig, ma_spatializer_listener_heap_layout* pHeapLayout) -{ - MA_ASSERT(pHeapLayout != NULL); - - MA_ZERO_OBJECT(pHeapLayout); - - if (pConfig == NULL) { - return MA_INVALID_ARGS; - } - - if (pConfig->channelsOut == 0) { - return MA_INVALID_ARGS; - } - - pHeapLayout->sizeInBytes = 0; - - /* Channel map. We always need this, even for passthroughs. */ - pHeapLayout->channelMapOutOffset = pHeapLayout->sizeInBytes; - pHeapLayout->sizeInBytes += ma_align_64(sizeof(*pConfig->pChannelMapOut) * pConfig->channelsOut); - - return MA_SUCCESS; -} - - -MA_API ma_result ma_spatializer_listener_get_heap_size(const ma_spatializer_listener_config* pConfig, size_t* pHeapSizeInBytes) -{ - ma_result result; - ma_spatializer_listener_heap_layout heapLayout; - - if (pHeapSizeInBytes == NULL) { - return MA_INVALID_ARGS; - } - - *pHeapSizeInBytes = 0; - - result = ma_spatializer_listener_get_heap_layout(pConfig, &heapLayout); - if (result != MA_SUCCESS) { - return result; - } - - *pHeapSizeInBytes = heapLayout.sizeInBytes; - - return MA_SUCCESS; -} - -MA_API ma_result ma_spatializer_listener_init_preallocated(const ma_spatializer_listener_config* pConfig, void* pHeap, ma_spatializer_listener* pListener) -{ - ma_result result; - ma_spatializer_listener_heap_layout heapLayout; - - if (pListener == NULL) { - return MA_INVALID_ARGS; - } - - MA_ZERO_OBJECT(pListener); - - result = ma_spatializer_listener_get_heap_layout(pConfig, &heapLayout); - if (result != MA_SUCCESS) { - return result; - } - - pListener->_pHeap = pHeap; - MA_ZERO_MEMORY(pHeap, heapLayout.sizeInBytes); - - pListener->config = *pConfig; - ma_atomic_vec3f_init(&pListener->position, ma_vec3f_init_3f(0, 0, 0)); - ma_atomic_vec3f_init(&pListener->direction, ma_vec3f_init_3f(0, 0, -1)); - ma_atomic_vec3f_init(&pListener->velocity, ma_vec3f_init_3f(0, 0, 0)); - pListener->isEnabled = MA_TRUE; - - /* Swap the forward direction if we're left handed (it was initialized based on right handed). */ - if (pListener->config.handedness == ma_handedness_left) { - ma_vec3f negDir = ma_vec3f_neg(ma_spatializer_listener_get_direction(pListener)); - ma_spatializer_listener_set_direction(pListener, negDir.x, negDir.y, negDir.z); - } - - - /* We must always have a valid channel map. */ - pListener->config.pChannelMapOut = (ma_channel*)ma_offset_ptr(pHeap, heapLayout.channelMapOutOffset); - - /* Use a slightly different default channel map for stereo. */ - if (pConfig->pChannelMapOut == NULL) { - ma_get_default_channel_map_for_spatializer(pListener->config.pChannelMapOut, pConfig->channelsOut, pConfig->channelsOut); - } else { - ma_channel_map_copy_or_default(pListener->config.pChannelMapOut, pConfig->channelsOut, pConfig->pChannelMapOut, pConfig->channelsOut); - } - - return MA_SUCCESS; -} - -MA_API ma_result ma_spatializer_listener_init(const ma_spatializer_listener_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_spatializer_listener* pListener) -{ - ma_result result; - size_t heapSizeInBytes; - void* pHeap; - - result = ma_spatializer_listener_get_heap_size(pConfig, &heapSizeInBytes); - if (result != MA_SUCCESS) { - return result; - } - - if (heapSizeInBytes > 0) { - pHeap = ma_malloc(heapSizeInBytes, pAllocationCallbacks); - if (pHeap == NULL) { - return MA_OUT_OF_MEMORY; - } - } else { - pHeap = NULL; - } - - result = ma_spatializer_listener_init_preallocated(pConfig, pHeap, pListener); - if (result != MA_SUCCESS) { - ma_free(pHeap, pAllocationCallbacks); - return result; - } - - pListener->_ownsHeap = MA_TRUE; - return MA_SUCCESS; -} - -MA_API void ma_spatializer_listener_uninit(ma_spatializer_listener* pListener, const ma_allocation_callbacks* pAllocationCallbacks) -{ - if (pListener == NULL) { - return; - } - - if (pListener->_ownsHeap) { - ma_free(pListener->_pHeap, pAllocationCallbacks); - } -} - -MA_API ma_channel* ma_spatializer_listener_get_channel_map(ma_spatializer_listener* pListener) -{ - if (pListener == NULL) { - return NULL; - } - - return pListener->config.pChannelMapOut; -} - -MA_API void ma_spatializer_listener_set_cone(ma_spatializer_listener* pListener, float innerAngleInRadians, float outerAngleInRadians, float outerGain) -{ - if (pListener == NULL) { - return; - } - - pListener->config.coneInnerAngleInRadians = innerAngleInRadians; - pListener->config.coneOuterAngleInRadians = outerAngleInRadians; - pListener->config.coneOuterGain = outerGain; -} - -MA_API void ma_spatializer_listener_get_cone(const ma_spatializer_listener* pListener, float* pInnerAngleInRadians, float* pOuterAngleInRadians, float* pOuterGain) -{ - if (pListener == NULL) { - return; - } - - if (pInnerAngleInRadians != NULL) { - *pInnerAngleInRadians = pListener->config.coneInnerAngleInRadians; - } - - if (pOuterAngleInRadians != NULL) { - *pOuterAngleInRadians = pListener->config.coneOuterAngleInRadians; - } - - if (pOuterGain != NULL) { - *pOuterGain = pListener->config.coneOuterGain; - } -} - -MA_API void ma_spatializer_listener_set_position(ma_spatializer_listener* pListener, float x, float y, float z) -{ - if (pListener == NULL) { - return; - } - - ma_atomic_vec3f_set(&pListener->position, ma_vec3f_init_3f(x, y, z)); -} - -MA_API ma_vec3f ma_spatializer_listener_get_position(const ma_spatializer_listener* pListener) -{ - if (pListener == NULL) { - return ma_vec3f_init_3f(0, 0, 0); - } - - return ma_atomic_vec3f_get((ma_atomic_vec3f*)&pListener->position); /* Naughty const-cast. It's just for atomically loading the vec3 which should be safe. */ -} - -MA_API void ma_spatializer_listener_set_direction(ma_spatializer_listener* pListener, float x, float y, float z) -{ - if (pListener == NULL) { - return; - } - - ma_atomic_vec3f_set(&pListener->direction, ma_vec3f_init_3f(x, y, z)); -} - -MA_API ma_vec3f ma_spatializer_listener_get_direction(const ma_spatializer_listener* pListener) -{ - if (pListener == NULL) { - return ma_vec3f_init_3f(0, 0, -1); - } - - return ma_atomic_vec3f_get((ma_atomic_vec3f*)&pListener->direction); /* Naughty const-cast. It's just for atomically loading the vec3 which should be safe. */ -} - -MA_API void ma_spatializer_listener_set_velocity(ma_spatializer_listener* pListener, float x, float y, float z) -{ - if (pListener == NULL) { - return; - } - - ma_atomic_vec3f_set(&pListener->velocity, ma_vec3f_init_3f(x, y, z)); -} - -MA_API ma_vec3f ma_spatializer_listener_get_velocity(const ma_spatializer_listener* pListener) -{ - if (pListener == NULL) { - return ma_vec3f_init_3f(0, 0, 0); - } - - return ma_atomic_vec3f_get((ma_atomic_vec3f*)&pListener->velocity); /* Naughty const-cast. It's just for atomically loading the vec3 which should be safe. */ -} - -MA_API void ma_spatializer_listener_set_speed_of_sound(ma_spatializer_listener* pListener, float speedOfSound) -{ - if (pListener == NULL) { - return; - } - - pListener->config.speedOfSound = speedOfSound; -} - -MA_API float ma_spatializer_listener_get_speed_of_sound(const ma_spatializer_listener* pListener) -{ - if (pListener == NULL) { - return 0; - } - - return pListener->config.speedOfSound; -} - -MA_API void ma_spatializer_listener_set_world_up(ma_spatializer_listener* pListener, float x, float y, float z) -{ - if (pListener == NULL) { - return; - } - - pListener->config.worldUp = ma_vec3f_init_3f(x, y, z); -} - -MA_API ma_vec3f ma_spatializer_listener_get_world_up(const ma_spatializer_listener* pListener) -{ - if (pListener == NULL) { - return ma_vec3f_init_3f(0, 1, 0); - } - - return pListener->config.worldUp; -} - -MA_API void ma_spatializer_listener_set_enabled(ma_spatializer_listener* pListener, ma_bool32 isEnabled) -{ - if (pListener == NULL) { - return; - } - - pListener->isEnabled = isEnabled; -} - -MA_API ma_bool32 ma_spatializer_listener_is_enabled(const ma_spatializer_listener* pListener) -{ - if (pListener == NULL) { - return MA_FALSE; - } - - return pListener->isEnabled; -} - - - - -MA_API ma_spatializer_config ma_spatializer_config_init(ma_uint32 channelsIn, ma_uint32 channelsOut) -{ - ma_spatializer_config config; - - MA_ZERO_OBJECT(&config); - config.channelsIn = channelsIn; - config.channelsOut = channelsOut; - config.pChannelMapIn = NULL; - config.attenuationModel = ma_attenuation_model_inverse; - config.positioning = ma_positioning_absolute; - config.handedness = ma_handedness_right; - config.minGain = 0; - config.maxGain = 1; - config.minDistance = 1; - config.maxDistance = MA_FLT_MAX; - config.rolloff = 1; - config.coneInnerAngleInRadians = 6.283185f; /* 360 degrees. */ - config.coneOuterAngleInRadians = 6.283185f; /* 360 degrees. */ - config.coneOuterGain = 0.0f; - config.dopplerFactor = 1; - config.directionalAttenuationFactor = 1; - config.minSpatializationChannelGain = 0.2f; - config.gainSmoothTimeInFrames = 360; /* 7.5ms @ 48K. */ - - return config; -} - - -static ma_gainer_config ma_spatializer_gainer_config_init(const ma_spatializer_config* pConfig) -{ - MA_ASSERT(pConfig != NULL); - return ma_gainer_config_init(pConfig->channelsOut, pConfig->gainSmoothTimeInFrames); -} - -static ma_result ma_spatializer_validate_config(const ma_spatializer_config* pConfig) -{ - MA_ASSERT(pConfig != NULL); - - if (pConfig->channelsIn == 0 || pConfig->channelsOut == 0) { - return MA_INVALID_ARGS; - } - - return MA_SUCCESS; -} - -typedef struct -{ - size_t sizeInBytes; - size_t channelMapInOffset; - size_t newChannelGainsOffset; - size_t gainerOffset; -} ma_spatializer_heap_layout; - -static ma_result ma_spatializer_get_heap_layout(const ma_spatializer_config* pConfig, ma_spatializer_heap_layout* pHeapLayout) -{ - ma_result result; - - MA_ASSERT(pHeapLayout != NULL); - - MA_ZERO_OBJECT(pHeapLayout); - - if (pConfig == NULL) { - return MA_INVALID_ARGS; - } - - result = ma_spatializer_validate_config(pConfig); - if (result != MA_SUCCESS) { - return result; - } - - pHeapLayout->sizeInBytes = 0; - - /* Channel map. */ - pHeapLayout->channelMapInOffset = MA_SIZE_MAX; /* <-- MA_SIZE_MAX indicates no allocation necessary. */ - if (pConfig->pChannelMapIn != NULL) { - pHeapLayout->channelMapInOffset = pHeapLayout->sizeInBytes; - pHeapLayout->sizeInBytes += ma_align_64(sizeof(*pConfig->pChannelMapIn) * pConfig->channelsIn); - } - - /* New channel gains for output. */ - pHeapLayout->newChannelGainsOffset = pHeapLayout->sizeInBytes; - pHeapLayout->sizeInBytes += ma_align_64(sizeof(float) * pConfig->channelsOut); - - /* Gainer. */ - { - size_t gainerHeapSizeInBytes; - ma_gainer_config gainerConfig; - - gainerConfig = ma_spatializer_gainer_config_init(pConfig); - - result = ma_gainer_get_heap_size(&gainerConfig, &gainerHeapSizeInBytes); - if (result != MA_SUCCESS) { - return result; - } - - pHeapLayout->gainerOffset = pHeapLayout->sizeInBytes; - pHeapLayout->sizeInBytes += ma_align_64(gainerHeapSizeInBytes); - } - - return MA_SUCCESS; -} - -MA_API ma_result ma_spatializer_get_heap_size(const ma_spatializer_config* pConfig, size_t* pHeapSizeInBytes) -{ - ma_result result; - ma_spatializer_heap_layout heapLayout; - - if (pHeapSizeInBytes == NULL) { - return MA_INVALID_ARGS; - } - - *pHeapSizeInBytes = 0; /* Safety. */ - - result = ma_spatializer_get_heap_layout(pConfig, &heapLayout); - if (result != MA_SUCCESS) { - return result; - } - - *pHeapSizeInBytes = heapLayout.sizeInBytes; - - return MA_SUCCESS; -} - - -MA_API ma_result ma_spatializer_init_preallocated(const ma_spatializer_config* pConfig, void* pHeap, ma_spatializer* pSpatializer) -{ - ma_result result; - ma_spatializer_heap_layout heapLayout; - ma_gainer_config gainerConfig; - - if (pSpatializer == NULL) { - return MA_INVALID_ARGS; - } - - MA_ZERO_OBJECT(pSpatializer); - - if (pConfig == NULL || pHeap == NULL) { - return MA_INVALID_ARGS; - } - - result = ma_spatializer_get_heap_layout(pConfig, &heapLayout); - if (result != MA_SUCCESS) { - return result; - } - - pSpatializer->_pHeap = pHeap; - MA_ZERO_MEMORY(pHeap, heapLayout.sizeInBytes); - - pSpatializer->channelsIn = pConfig->channelsIn; - pSpatializer->channelsOut = pConfig->channelsOut; - pSpatializer->attenuationModel = pConfig->attenuationModel; - pSpatializer->positioning = pConfig->positioning; - pSpatializer->handedness = pConfig->handedness; - pSpatializer->minGain = pConfig->minGain; - pSpatializer->maxGain = pConfig->maxGain; - pSpatializer->minDistance = pConfig->minDistance; - pSpatializer->maxDistance = pConfig->maxDistance; - pSpatializer->rolloff = pConfig->rolloff; - pSpatializer->coneInnerAngleInRadians = pConfig->coneInnerAngleInRadians; - pSpatializer->coneOuterAngleInRadians = pConfig->coneOuterAngleInRadians; - pSpatializer->coneOuterGain = pConfig->coneOuterGain; - pSpatializer->dopplerFactor = pConfig->dopplerFactor; - pSpatializer->minSpatializationChannelGain = pConfig->minSpatializationChannelGain; - pSpatializer->directionalAttenuationFactor = pConfig->directionalAttenuationFactor; - pSpatializer->gainSmoothTimeInFrames = pConfig->gainSmoothTimeInFrames; - ma_atomic_vec3f_init(&pSpatializer->position, ma_vec3f_init_3f(0, 0, 0)); - ma_atomic_vec3f_init(&pSpatializer->direction, ma_vec3f_init_3f(0, 0, -1)); - ma_atomic_vec3f_init(&pSpatializer->velocity, ma_vec3f_init_3f(0, 0, 0)); - pSpatializer->dopplerPitch = 1; - - /* Swap the forward direction if we're left handed (it was initialized based on right handed). */ - if (pSpatializer->handedness == ma_handedness_left) { - ma_vec3f negDir = ma_vec3f_neg(ma_spatializer_get_direction(pSpatializer)); - ma_spatializer_set_direction(pSpatializer, negDir.x, negDir.y, negDir.z); - } - - /* Channel map. This will be on the heap. */ - if (pConfig->pChannelMapIn != NULL) { - pSpatializer->pChannelMapIn = (ma_channel*)ma_offset_ptr(pHeap, heapLayout.channelMapInOffset); - ma_channel_map_copy_or_default(pSpatializer->pChannelMapIn, pSpatializer->channelsIn, pConfig->pChannelMapIn, pSpatializer->channelsIn); - } - - /* New channel gains for output channels. */ - pSpatializer->pNewChannelGainsOut = (float*)ma_offset_ptr(pHeap, heapLayout.newChannelGainsOffset); - - /* Gainer. */ - gainerConfig = ma_spatializer_gainer_config_init(pConfig); - - result = ma_gainer_init_preallocated(&gainerConfig, ma_offset_ptr(pHeap, heapLayout.gainerOffset), &pSpatializer->gainer); - if (result != MA_SUCCESS) { - return result; /* Failed to initialize the gainer. */ - } - - return MA_SUCCESS; -} - -MA_API ma_result ma_spatializer_init(const ma_spatializer_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_spatializer* pSpatializer) -{ - ma_result result; - size_t heapSizeInBytes; - void* pHeap; - - /* We'll need a heap allocation to retrieve the size. */ - result = ma_spatializer_get_heap_size(pConfig, &heapSizeInBytes); - if (result != MA_SUCCESS) { - return result; - } - - if (heapSizeInBytes > 0) { - pHeap = ma_malloc(heapSizeInBytes, pAllocationCallbacks); - if (pHeap == NULL) { - return MA_OUT_OF_MEMORY; - } - } else { - pHeap = NULL; - } - - result = ma_spatializer_init_preallocated(pConfig, pHeap, pSpatializer); - if (result != MA_SUCCESS) { - ma_free(pHeap, pAllocationCallbacks); - return result; - } - - pSpatializer->_ownsHeap = MA_TRUE; - return MA_SUCCESS; -} - -MA_API void ma_spatializer_uninit(ma_spatializer* pSpatializer, const ma_allocation_callbacks* pAllocationCallbacks) -{ - if (pSpatializer == NULL) { - return; - } - - ma_gainer_uninit(&pSpatializer->gainer, pAllocationCallbacks); - - if (pSpatializer->_ownsHeap) { - ma_free(pSpatializer->_pHeap, pAllocationCallbacks); - } -} - -static float ma_calculate_angular_gain(ma_vec3f dirA, ma_vec3f dirB, float coneInnerAngleInRadians, float coneOuterAngleInRadians, float coneOuterGain) -{ - /* - Angular attenuation. - - Unlike distance gain, the math for this is not specified by the OpenAL spec so we'll just go ahead and figure - this out for ourselves at the expense of possibly being inconsistent with other implementations. - - To do cone attenuation, I'm just using the same math that we'd use to implement a basic spotlight in OpenGL. We - just need to get the direction from the source to the listener and then do a dot product against that and the - direction of the spotlight. Then we just compare that dot product against the cosine of the inner and outer - angles. If the dot product is greater than the outer angle, we just use coneOuterGain. If it's less than - the inner angle, we just use a gain of 1. Otherwise we linearly interpolate between 1 and coneOuterGain. - */ - if (coneInnerAngleInRadians < 6.283185f) { - float angularGain = 1; - float cutoffInner = (float)ma_cosd(coneInnerAngleInRadians*0.5f); - float cutoffOuter = (float)ma_cosd(coneOuterAngleInRadians*0.5f); - float d; - - d = ma_vec3f_dot(dirA, dirB); - - if (d > cutoffInner) { - /* It's inside the inner angle. */ - angularGain = 1; - } else { - /* It's outside the inner angle. */ - if (d > cutoffOuter) { - /* It's between the inner and outer angle. We need to linearly interpolate between 1 and coneOuterGain. */ - angularGain = ma_mix_f32(coneOuterGain, 1, (d - cutoffOuter) / (cutoffInner - cutoffOuter)); - } else { - /* It's outside the outer angle. */ - angularGain = coneOuterGain; - } - } - - /*printf("d = %f; cutoffInner = %f; cutoffOuter = %f; angularGain = %f\n", d, cutoffInner, cutoffOuter, angularGain);*/ - return angularGain; - } else { - /* Inner angle is 360 degrees so no need to do any attenuation. */ - return 1; - } -} - -MA_API ma_result ma_spatializer_process_pcm_frames(ma_spatializer* pSpatializer, ma_spatializer_listener* pListener, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount) -{ - ma_channel* pChannelMapIn = pSpatializer->pChannelMapIn; - ma_channel* pChannelMapOut = pListener->config.pChannelMapOut; - - if (pSpatializer == NULL) { - return MA_INVALID_ARGS; - } - - /* If we're not spatializing we need to run an optimized path. */ - if (ma_atomic_load_i32(&pSpatializer->attenuationModel) == ma_attenuation_model_none) { - if (ma_spatializer_listener_is_enabled(pListener)) { - /* No attenuation is required, but we'll need to do some channel conversion. */ - if (pSpatializer->channelsIn == pSpatializer->channelsOut) { - ma_copy_pcm_frames(pFramesOut, pFramesIn, frameCount, ma_format_f32, pSpatializer->channelsIn); - } else { - ma_channel_map_apply_f32((float*)pFramesOut, pChannelMapOut, pSpatializer->channelsOut, (const float*)pFramesIn, pChannelMapIn, pSpatializer->channelsIn, frameCount, ma_channel_mix_mode_rectangular, ma_mono_expansion_mode_default); /* Safe casts to float* because f32 is the only supported format. */ - } - } else { - /* The listener is disabled. Output silence. */ - ma_silence_pcm_frames(pFramesOut, frameCount, ma_format_f32, pSpatializer->channelsOut); - } - - /* - We're not doing attenuation so don't bother with doppler for now. I'm not sure if this is - the correct thinking so might need to review this later. - */ - pSpatializer->dopplerPitch = 1; - } else { - /* - Let's first determine which listener the sound is closest to. Need to keep in mind that we - might not have a world or any listeners, in which case we just spatializer based on the - listener being positioned at the origin (0, 0, 0). - */ - ma_vec3f relativePosNormalized; - ma_vec3f relativePos; /* The position relative to the listener. */ - ma_vec3f relativeDir; /* The direction of the sound, relative to the listener. */ - ma_vec3f listenerVel; /* The velocity of the listener. For doppler pitch calculation. */ - float speedOfSound; - float distance = 0; - float gain = 1; - ma_uint32 iChannel; - const ma_uint32 channelsOut = pSpatializer->channelsOut; - const ma_uint32 channelsIn = pSpatializer->channelsIn; - float minDistance = ma_spatializer_get_min_distance(pSpatializer); - float maxDistance = ma_spatializer_get_max_distance(pSpatializer); - float rolloff = ma_spatializer_get_rolloff(pSpatializer); - float dopplerFactor = ma_spatializer_get_doppler_factor(pSpatializer); - - /* - We'll need the listener velocity for doppler pitch calculations. The speed of sound is - defined by the listener, so we'll grab that here too. - */ - if (pListener != NULL) { - listenerVel = ma_spatializer_listener_get_velocity(pListener); - speedOfSound = pListener->config.speedOfSound; - } else { - listenerVel = ma_vec3f_init_3f(0, 0, 0); - speedOfSound = MA_DEFAULT_SPEED_OF_SOUND; - } - - if (pListener == NULL || ma_spatializer_get_positioning(pSpatializer) == ma_positioning_relative) { - /* There's no listener or we're using relative positioning. */ - relativePos = ma_spatializer_get_position(pSpatializer); - relativeDir = ma_spatializer_get_direction(pSpatializer); - } else { - /* - We've found a listener and we're using absolute positioning. We need to transform the - sound's position and direction so that it's relative to listener. Later on we'll use - this for determining the factors to apply to each channel to apply the panning effect. - */ - ma_spatializer_get_relative_position_and_direction(pSpatializer, pListener, &relativePos, &relativeDir); - } - - distance = ma_vec3f_len(relativePos); - - /* We've gathered the data, so now we can apply some spatialization. */ - switch (ma_spatializer_get_attenuation_model(pSpatializer)) { - case ma_attenuation_model_inverse: - { - gain = ma_attenuation_inverse(distance, minDistance, maxDistance, rolloff); - } break; - case ma_attenuation_model_linear: - { - gain = ma_attenuation_linear(distance, minDistance, maxDistance, rolloff); - } break; - case ma_attenuation_model_exponential: - { - gain = ma_attenuation_exponential(distance, minDistance, maxDistance, rolloff); - } break; - case ma_attenuation_model_none: - default: - { - gain = 1; - } break; - } - - /* Normalize the position. */ - if (distance > 0.001f) { - float distanceInv = 1/distance; - relativePosNormalized = relativePos; - relativePosNormalized.x *= distanceInv; - relativePosNormalized.y *= distanceInv; - relativePosNormalized.z *= distanceInv; - } else { - distance = 0; - relativePosNormalized = ma_vec3f_init_3f(0, 0, 0); - } - - /* - Angular attenuation. - - Unlike distance gain, the math for this is not specified by the OpenAL spec so we'll just go ahead and figure - this out for ourselves at the expense of possibly being inconsistent with other implementations. - - To do cone attenuation, I'm just using the same math that we'd use to implement a basic spotlight in OpenGL. We - just need to get the direction from the source to the listener and then do a dot product against that and the - direction of the spotlight. Then we just compare that dot product against the cosine of the inner and outer - angles. If the dot product is greater than the outer angle, we just use coneOuterGain. If it's less than - the inner angle, we just use a gain of 1. Otherwise we linearly interpolate between 1 and coneOuterGain. - */ - if (distance > 0) { - /* Source angular gain. */ - float spatializerConeInnerAngle; - float spatializerConeOuterAngle; - float spatializerConeOuterGain; - ma_spatializer_get_cone(pSpatializer, &spatializerConeInnerAngle, &spatializerConeOuterAngle, &spatializerConeOuterGain); - - gain *= ma_calculate_angular_gain(relativeDir, ma_vec3f_neg(relativePosNormalized), spatializerConeInnerAngle, spatializerConeOuterAngle, spatializerConeOuterGain); - - /* - We're supporting angular gain on the listener as well for those who want to reduce the volume of sounds that - are positioned behind the listener. On default settings, this will have no effect. - */ - if (pListener != NULL && pListener->config.coneInnerAngleInRadians < 6.283185f) { - ma_vec3f listenerDirection; - float listenerInnerAngle; - float listenerOuterAngle; - float listenerOuterGain; - - if (pListener->config.handedness == ma_handedness_right) { - listenerDirection = ma_vec3f_init_3f(0, 0, -1); - } else { - listenerDirection = ma_vec3f_init_3f(0, 0, +1); - } - - listenerInnerAngle = pListener->config.coneInnerAngleInRadians; - listenerOuterAngle = pListener->config.coneOuterAngleInRadians; - listenerOuterGain = pListener->config.coneOuterGain; - - gain *= ma_calculate_angular_gain(listenerDirection, relativePosNormalized, listenerInnerAngle, listenerOuterAngle, listenerOuterGain); - } - } else { - /* The sound is right on top of the listener. Don't do any angular attenuation. */ - } - - - /* Clamp the gain. */ - gain = ma_clamp(gain, ma_spatializer_get_min_gain(pSpatializer), ma_spatializer_get_max_gain(pSpatializer)); - - /* - The gain needs to be applied per-channel here. The spatialization code below will be changing the per-channel - gains which will then eventually be passed into the gainer which will deal with smoothing the gain transitions - to avoid harsh changes in gain. - */ - for (iChannel = 0; iChannel < channelsOut; iChannel += 1) { - pSpatializer->pNewChannelGainsOut[iChannel] = gain; - } - - /* - Convert to our output channel count. If the listener is disabled we just output silence here. We cannot ignore - the whole section of code here because we need to update some internal spatialization state. - */ - if (ma_spatializer_listener_is_enabled(pListener)) { - ma_channel_map_apply_f32((float*)pFramesOut, pChannelMapOut, channelsOut, (const float*)pFramesIn, pChannelMapIn, channelsIn, frameCount, ma_channel_mix_mode_rectangular, ma_mono_expansion_mode_default); - } else { - ma_silence_pcm_frames(pFramesOut, frameCount, ma_format_f32, pSpatializer->channelsOut); - } - - - /* - Panning. This is where we'll apply the gain and convert to the output channel count. We have an optimized path for - when we're converting to a mono stream. In that case we don't really need to do any panning - we just apply the - gain to the final output. - */ - /*printf("distance=%f; gain=%f\n", distance, gain);*/ - - /* We must have a valid channel map here to ensure we spatialize properly. */ - MA_ASSERT(pChannelMapOut != NULL); - - /* - We're not converting to mono so we'll want to apply some panning. This is where the feeling of something being - to the left, right, infront or behind the listener is calculated. I'm just using a basic model here. Note that - the code below is not based on any specific algorithm. I'm just implementing this off the top of my head and - seeing how it goes. There might be better ways to do this. - - To determine the direction of the sound relative to a speaker I'm using dot products. Each speaker is given a - direction. For example, the left channel in a stereo system will be -1 on the X axis and the right channel will - be +1 on the X axis. A dot product is performed against the direction vector of the channel and the normalized - position of the sound. - */ - - /* - Calculate our per-channel gains. We do this based on the normalized relative position of the sound and it's - relation to the direction of the channel. - */ - if (distance > 0) { - ma_vec3f unitPos = relativePos; - float distanceInv = 1/distance; - unitPos.x *= distanceInv; - unitPos.y *= distanceInv; - unitPos.z *= distanceInv; - - for (iChannel = 0; iChannel < channelsOut; iChannel += 1) { - ma_channel channelOut; - float d; - float dMin; - - channelOut = ma_channel_map_get_channel(pChannelMapOut, channelsOut, iChannel); - if (ma_is_spatial_channel_position(channelOut)) { - d = ma_mix_f32_fast(1, ma_vec3f_dot(unitPos, ma_get_channel_direction(channelOut)), ma_spatializer_get_directional_attenuation_factor(pSpatializer)); - } else { - d = 1; /* It's not a spatial channel so there's no real notion of direction. */ - } - - /* - In my testing, if the panning effect is too aggressive it makes spatialization feel uncomfortable. - The "dMin" variable below is used to control the aggressiveness of the panning effect. When set to - 0, panning will be most extreme and any sounds that are positioned on the opposite side of the - speaker will be completely silent from that speaker. Not only does this feel uncomfortable, it - doesn't even remotely represent the real world at all because sounds that come from your right side - are still clearly audible from your left side. Setting "dMin" to 1 will result in no panning at - all, which is also not ideal. By setting it to something greater than 0, the spatialization effect - becomes much less dramatic and a lot more bearable. - - Summary: 0 = more extreme panning; 1 = no panning. - */ - dMin = pSpatializer->minSpatializationChannelGain; - - /* - At this point, "d" will be positive if the sound is on the same side as the channel and negative if - it's on the opposite side. It will be in the range of -1..1. There's two ways I can think of to - calculate a panning value. The first is to simply convert it to 0..1, however this has a problem - which I'm not entirely happy with. Considering a stereo system, when a sound is positioned right - in front of the listener it'll result in each speaker getting a gain of 0.5. I don't know if I like - the idea of having a scaling factor of 0.5 being applied to a sound when it's sitting right in front - of the listener. I would intuitively expect that to be played at full volume, or close to it. - - The second idea I think of is to only apply a reduction in gain when the sound is on the opposite - side of the speaker. That is, reduce the gain only when the dot product is negative. The problem - with this is that there will not be any attenuation as the sound sweeps around the 180 degrees - where the dot product is positive. The idea with this option is that you leave the gain at 1 when - the sound is being played on the same side as the speaker and then you just reduce the volume when - the sound is on the other side. - - The summarize, I think the first option should give a better sense of spatialization, but the second - option is better for preserving the sound's power. - - UPDATE: In my testing, I find the first option to sound better. You can feel the sense of space a - bit better, but you can also hear the reduction in volume when it's right in front. - */ - #if 1 - { - /* - Scale the dot product from -1..1 to 0..1. Will result in a sound directly in front losing power - by being played at 0.5 gain. - */ - d = (d + 1) * 0.5f; /* -1..1 to 0..1 */ - d = ma_max(d, dMin); - pSpatializer->pNewChannelGainsOut[iChannel] *= d; - } - #else - { - /* - Only reduce the volume of the sound if it's on the opposite side. This path keeps the volume more - consistent, but comes at the expense of a worse sense of space and positioning. - */ - if (d < 0) { - d += 1; /* Move into the positive range. */ - d = ma_max(d, dMin); - channelGainsOut[iChannel] *= d; - } - } - #endif - } - } else { - /* Assume the sound is right on top of us. Don't do any panning. */ - } - - /* Now we need to apply the volume to each channel. This needs to run through the gainer to ensure we get a smooth volume transition. */ - ma_gainer_set_gains(&pSpatializer->gainer, pSpatializer->pNewChannelGainsOut); - ma_gainer_process_pcm_frames(&pSpatializer->gainer, pFramesOut, pFramesOut, frameCount); - - /* - Before leaving we'll want to update our doppler pitch so that the caller can apply some - pitch shifting if they desire. Note that we need to negate the relative position here - because the doppler calculation needs to be source-to-listener, but ours is listener-to- - source. - */ - if (dopplerFactor > 0) { - pSpatializer->dopplerPitch = ma_doppler_pitch(ma_vec3f_sub(ma_spatializer_listener_get_position(pListener), ma_spatializer_get_position(pSpatializer)), ma_spatializer_get_velocity(pSpatializer), listenerVel, speedOfSound, dopplerFactor); - } else { - pSpatializer->dopplerPitch = 1; - } - } - - return MA_SUCCESS; -} - -MA_API ma_result ma_spatializer_set_master_volume(ma_spatializer* pSpatializer, float volume) -{ - if (pSpatializer == NULL) { - return MA_INVALID_ARGS; - } - - return ma_gainer_set_master_volume(&pSpatializer->gainer, volume); -} - -MA_API ma_result ma_spatializer_get_master_volume(const ma_spatializer* pSpatializer, float* pVolume) -{ - if (pSpatializer == NULL) { - return MA_INVALID_ARGS; - } - - return ma_gainer_get_master_volume(&pSpatializer->gainer, pVolume); -} - -MA_API ma_uint32 ma_spatializer_get_input_channels(const ma_spatializer* pSpatializer) -{ - if (pSpatializer == NULL) { - return 0; - } - - return pSpatializer->channelsIn; -} - -MA_API ma_uint32 ma_spatializer_get_output_channels(const ma_spatializer* pSpatializer) -{ - if (pSpatializer == NULL) { - return 0; - } - - return pSpatializer->channelsOut; -} - -MA_API void ma_spatializer_set_attenuation_model(ma_spatializer* pSpatializer, ma_attenuation_model attenuationModel) -{ - if (pSpatializer == NULL) { - return; - } - - ma_atomic_exchange_i32(&pSpatializer->attenuationModel, attenuationModel); -} - -MA_API ma_attenuation_model ma_spatializer_get_attenuation_model(const ma_spatializer* pSpatializer) -{ - if (pSpatializer == NULL) { - return ma_attenuation_model_none; - } - - return (ma_attenuation_model)ma_atomic_load_i32(&pSpatializer->attenuationModel); -} - -MA_API void ma_spatializer_set_positioning(ma_spatializer* pSpatializer, ma_positioning positioning) -{ - if (pSpatializer == NULL) { - return; - } - - ma_atomic_exchange_i32(&pSpatializer->positioning, positioning); -} - -MA_API ma_positioning ma_spatializer_get_positioning(const ma_spatializer* pSpatializer) -{ - if (pSpatializer == NULL) { - return ma_positioning_absolute; - } - - return (ma_positioning)ma_atomic_load_i32(&pSpatializer->positioning); -} - -MA_API void ma_spatializer_set_rolloff(ma_spatializer* pSpatializer, float rolloff) -{ - if (pSpatializer == NULL) { - return; - } - - ma_atomic_exchange_f32(&pSpatializer->rolloff, rolloff); -} - -MA_API float ma_spatializer_get_rolloff(const ma_spatializer* pSpatializer) -{ - if (pSpatializer == NULL) { - return 0; - } - - return ma_atomic_load_f32(&pSpatializer->rolloff); -} - -MA_API void ma_spatializer_set_min_gain(ma_spatializer* pSpatializer, float minGain) -{ - if (pSpatializer == NULL) { - return; - } - - ma_atomic_exchange_f32(&pSpatializer->minGain, minGain); -} - -MA_API float ma_spatializer_get_min_gain(const ma_spatializer* pSpatializer) -{ - if (pSpatializer == NULL) { - return 0; - } - - return ma_atomic_load_f32(&pSpatializer->minGain); -} - -MA_API void ma_spatializer_set_max_gain(ma_spatializer* pSpatializer, float maxGain) -{ - if (pSpatializer == NULL) { - return; - } - - ma_atomic_exchange_f32(&pSpatializer->maxGain, maxGain); -} - -MA_API float ma_spatializer_get_max_gain(const ma_spatializer* pSpatializer) -{ - if (pSpatializer == NULL) { - return 0; - } - - return ma_atomic_load_f32(&pSpatializer->maxGain); -} - -MA_API void ma_spatializer_set_min_distance(ma_spatializer* pSpatializer, float minDistance) -{ - if (pSpatializer == NULL) { - return; - } - - ma_atomic_exchange_f32(&pSpatializer->minDistance, minDistance); -} - -MA_API float ma_spatializer_get_min_distance(const ma_spatializer* pSpatializer) -{ - if (pSpatializer == NULL) { - return 0; - } - - return ma_atomic_load_f32(&pSpatializer->minDistance); -} - -MA_API void ma_spatializer_set_max_distance(ma_spatializer* pSpatializer, float maxDistance) -{ - if (pSpatializer == NULL) { - return; - } - - ma_atomic_exchange_f32(&pSpatializer->maxDistance, maxDistance); -} - -MA_API float ma_spatializer_get_max_distance(const ma_spatializer* pSpatializer) -{ - if (pSpatializer == NULL) { - return 0; - } - - return ma_atomic_load_f32(&pSpatializer->maxDistance); -} - -MA_API void ma_spatializer_set_cone(ma_spatializer* pSpatializer, float innerAngleInRadians, float outerAngleInRadians, float outerGain) -{ - if (pSpatializer == NULL) { - return; - } - - ma_atomic_exchange_f32(&pSpatializer->coneInnerAngleInRadians, innerAngleInRadians); - ma_atomic_exchange_f32(&pSpatializer->coneOuterAngleInRadians, outerAngleInRadians); - ma_atomic_exchange_f32(&pSpatializer->coneOuterGain, outerGain); -} - -MA_API void ma_spatializer_get_cone(const ma_spatializer* pSpatializer, float* pInnerAngleInRadians, float* pOuterAngleInRadians, float* pOuterGain) -{ - if (pSpatializer == NULL) { - return; - } - - if (pInnerAngleInRadians != NULL) { - *pInnerAngleInRadians = ma_atomic_load_f32(&pSpatializer->coneInnerAngleInRadians); - } - - if (pOuterAngleInRadians != NULL) { - *pOuterAngleInRadians = ma_atomic_load_f32(&pSpatializer->coneOuterAngleInRadians); - } - - if (pOuterGain != NULL) { - *pOuterGain = ma_atomic_load_f32(&pSpatializer->coneOuterGain); - } -} - -MA_API void ma_spatializer_set_doppler_factor(ma_spatializer* pSpatializer, float dopplerFactor) -{ - if (pSpatializer == NULL) { - return; - } - - ma_atomic_exchange_f32(&pSpatializer->dopplerFactor, dopplerFactor); -} - -MA_API float ma_spatializer_get_doppler_factor(const ma_spatializer* pSpatializer) -{ - if (pSpatializer == NULL) { - return 1; - } - - return ma_atomic_load_f32(&pSpatializer->dopplerFactor); -} - -MA_API void ma_spatializer_set_directional_attenuation_factor(ma_spatializer* pSpatializer, float directionalAttenuationFactor) -{ - if (pSpatializer == NULL) { - return; - } - - ma_atomic_exchange_f32(&pSpatializer->directionalAttenuationFactor, directionalAttenuationFactor); -} - -MA_API float ma_spatializer_get_directional_attenuation_factor(const ma_spatializer* pSpatializer) -{ - if (pSpatializer == NULL) { - return 1; - } - - return ma_atomic_load_f32(&pSpatializer->directionalAttenuationFactor); -} - -MA_API void ma_spatializer_set_position(ma_spatializer* pSpatializer, float x, float y, float z) -{ - if (pSpatializer == NULL) { - return; - } - - ma_atomic_vec3f_set(&pSpatializer->position, ma_vec3f_init_3f(x, y, z)); -} - -MA_API ma_vec3f ma_spatializer_get_position(const ma_spatializer* pSpatializer) -{ - if (pSpatializer == NULL) { - return ma_vec3f_init_3f(0, 0, 0); - } - - return ma_atomic_vec3f_get((ma_atomic_vec3f*)&pSpatializer->position); /* Naughty const-cast. It's just for atomically loading the vec3 which should be safe. */ -} - -MA_API void ma_spatializer_set_direction(ma_spatializer* pSpatializer, float x, float y, float z) -{ - if (pSpatializer == NULL) { - return; - } - - ma_atomic_vec3f_set(&pSpatializer->direction, ma_vec3f_init_3f(x, y, z)); -} - -MA_API ma_vec3f ma_spatializer_get_direction(const ma_spatializer* pSpatializer) -{ - if (pSpatializer == NULL) { - return ma_vec3f_init_3f(0, 0, -1); - } - - return ma_atomic_vec3f_get((ma_atomic_vec3f*)&pSpatializer->direction); /* Naughty const-cast. It's just for atomically loading the vec3 which should be safe. */ -} - -MA_API void ma_spatializer_set_velocity(ma_spatializer* pSpatializer, float x, float y, float z) -{ - if (pSpatializer == NULL) { - return; - } - - ma_atomic_vec3f_set(&pSpatializer->velocity, ma_vec3f_init_3f(x, y, z)); -} - -MA_API ma_vec3f ma_spatializer_get_velocity(const ma_spatializer* pSpatializer) -{ - if (pSpatializer == NULL) { - return ma_vec3f_init_3f(0, 0, 0); - } - - return ma_atomic_vec3f_get((ma_atomic_vec3f*)&pSpatializer->velocity); /* Naughty const-cast. It's just for atomically loading the vec3 which should be safe. */ -} - -MA_API void ma_spatializer_get_relative_position_and_direction(const ma_spatializer* pSpatializer, const ma_spatializer_listener* pListener, ma_vec3f* pRelativePos, ma_vec3f* pRelativeDir) -{ - if (pRelativePos != NULL) { - pRelativePos->x = 0; - pRelativePos->y = 0; - pRelativePos->z = 0; - } - - if (pRelativeDir != NULL) { - pRelativeDir->x = 0; - pRelativeDir->y = 0; - pRelativeDir->z = -1; - } - - if (pSpatializer == NULL) { - return; - } - - if (pListener == NULL || ma_spatializer_get_positioning(pSpatializer) == ma_positioning_relative) { - /* There's no listener or we're using relative positioning. */ - if (pRelativePos != NULL) { - *pRelativePos = ma_spatializer_get_position(pSpatializer); - } - if (pRelativeDir != NULL) { - *pRelativeDir = ma_spatializer_get_direction(pSpatializer); - } - } else { - ma_vec3f spatializerPosition; - ma_vec3f spatializerDirection; - ma_vec3f listenerPosition; - ma_vec3f listenerDirection; - ma_vec3f v; - ma_vec3f axisX; - ma_vec3f axisY; - ma_vec3f axisZ; - float m[4][4]; - - spatializerPosition = ma_spatializer_get_position(pSpatializer); - spatializerDirection = ma_spatializer_get_direction(pSpatializer); - listenerPosition = ma_spatializer_listener_get_position(pListener); - listenerDirection = ma_spatializer_listener_get_direction(pListener); - - /* - We need to calculate the right vector from our forward and up vectors. This is done with - a cross product. - */ - axisZ = ma_vec3f_normalize(listenerDirection); /* Normalization required here because we can't trust the caller. */ - axisX = ma_vec3f_normalize(ma_vec3f_cross(axisZ, pListener->config.worldUp)); /* Normalization required here because the world up vector may not be perpendicular with the forward vector. */ - - /* - The calculation of axisX above can result in a zero-length vector if the listener is - looking straight up on the Y axis. We'll need to fall back to a +X in this case so that - the calculations below don't fall apart. This is where a quaternion based listener and - sound orientation would come in handy. - */ - if (ma_vec3f_len2(axisX) == 0) { - axisX = ma_vec3f_init_3f(1, 0, 0); - } - - axisY = ma_vec3f_cross(axisX, axisZ); /* No normalization is required here because axisX and axisZ are unit length and perpendicular. */ - - /* - We need to swap the X axis if we're left handed because otherwise the cross product above - will have resulted in it pointing in the wrong direction (right handed was assumed in the - cross products above). - */ - if (pListener->config.handedness == ma_handedness_left) { - axisX = ma_vec3f_neg(axisX); - } - - /* Lookat. */ - m[0][0] = axisX.x; m[1][0] = axisX.y; m[2][0] = axisX.z; m[3][0] = -ma_vec3f_dot(axisX, listenerPosition); - m[0][1] = axisY.x; m[1][1] = axisY.y; m[2][1] = axisY.z; m[3][1] = -ma_vec3f_dot(axisY, listenerPosition); - m[0][2] = -axisZ.x; m[1][2] = -axisZ.y; m[2][2] = -axisZ.z; m[3][2] = -ma_vec3f_dot(ma_vec3f_neg(axisZ), listenerPosition); - m[0][3] = 0; m[1][3] = 0; m[2][3] = 0; m[3][3] = 1; - - /* - Multiply the lookat matrix by the spatializer position to transform it to listener - space. This allows calculations to work based on the sound being relative to the - origin which makes things simpler. - */ - if (pRelativePos != NULL) { - v = spatializerPosition; - pRelativePos->x = m[0][0] * v.x + m[1][0] * v.y + m[2][0] * v.z + m[3][0] * 1; - pRelativePos->y = m[0][1] * v.x + m[1][1] * v.y + m[2][1] * v.z + m[3][1] * 1; - pRelativePos->z = m[0][2] * v.x + m[1][2] * v.y + m[2][2] * v.z + m[3][2] * 1; - } - - /* - The direction of the sound needs to also be transformed so that it's relative to the - rotation of the listener. - */ - if (pRelativeDir != NULL) { - v = spatializerDirection; - pRelativeDir->x = m[0][0] * v.x + m[1][0] * v.y + m[2][0] * v.z; - pRelativeDir->y = m[0][1] * v.x + m[1][1] * v.y + m[2][1] * v.z; - pRelativeDir->z = m[0][2] * v.x + m[1][2] * v.y + m[2][2] * v.z; - } - } -} - - - - -/************************************************************************************************************************************************************** - -Resampling - -**************************************************************************************************************************************************************/ -MA_API ma_linear_resampler_config ma_linear_resampler_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRateIn, ma_uint32 sampleRateOut) -{ - ma_linear_resampler_config config; - MA_ZERO_OBJECT(&config); - config.format = format; - config.channels = channels; - config.sampleRateIn = sampleRateIn; - config.sampleRateOut = sampleRateOut; - config.lpfOrder = ma_min(MA_DEFAULT_RESAMPLER_LPF_ORDER, MA_MAX_FILTER_ORDER); - config.lpfNyquistFactor = 1; - - return config; -} - - -typedef struct -{ - size_t sizeInBytes; - size_t x0Offset; - size_t x1Offset; - size_t lpfOffset; -} ma_linear_resampler_heap_layout; - - -static void ma_linear_resampler_adjust_timer_for_new_rate(ma_linear_resampler* pResampler, ma_uint32 oldSampleRateOut, ma_uint32 newSampleRateOut) -{ - /* - So what's happening here? Basically we need to adjust the fractional component of the time advance based on the new rate. The old time advance will - be based on the old sample rate, but we are needing to adjust it to that it's based on the new sample rate. - */ - ma_uint32 oldRateTimeWhole = pResampler->inTimeFrac / oldSampleRateOut; /* <-- This should almost never be anything other than 0, but leaving it here to make this more general and robust just in case. */ - ma_uint32 oldRateTimeFract = pResampler->inTimeFrac % oldSampleRateOut; - - pResampler->inTimeFrac = - (oldRateTimeWhole * newSampleRateOut) + - ((oldRateTimeFract * newSampleRateOut) / oldSampleRateOut); - - /* Make sure the fractional part is less than the output sample rate. */ - pResampler->inTimeInt += pResampler->inTimeFrac / pResampler->config.sampleRateOut; - pResampler->inTimeFrac = pResampler->inTimeFrac % pResampler->config.sampleRateOut; -} - -static ma_result ma_linear_resampler_set_rate_internal(ma_linear_resampler* pResampler, void* pHeap, ma_linear_resampler_heap_layout* pHeapLayout, ma_uint32 sampleRateIn, ma_uint32 sampleRateOut, ma_bool32 isResamplerAlreadyInitialized) -{ - ma_result result; - ma_uint32 gcf; - ma_uint32 lpfSampleRate; - double lpfCutoffFrequency; - ma_lpf_config lpfConfig; - ma_uint32 oldSampleRateOut; /* Required for adjusting time advance down the bottom. */ - - if (pResampler == NULL) { - return MA_INVALID_ARGS; - } - - if (sampleRateIn == 0 || sampleRateOut == 0) { - return MA_INVALID_ARGS; - } - - oldSampleRateOut = pResampler->config.sampleRateOut; - - pResampler->config.sampleRateIn = sampleRateIn; - pResampler->config.sampleRateOut = sampleRateOut; - - /* Simplify the sample rate. */ - gcf = ma_gcf_u32(pResampler->config.sampleRateIn, pResampler->config.sampleRateOut); - pResampler->config.sampleRateIn /= gcf; - pResampler->config.sampleRateOut /= gcf; - - /* Always initialize the low-pass filter, even when the order is 0. */ - if (pResampler->config.lpfOrder > MA_MAX_FILTER_ORDER) { - return MA_INVALID_ARGS; - } - - lpfSampleRate = (ma_uint32)(ma_max(pResampler->config.sampleRateIn, pResampler->config.sampleRateOut)); - lpfCutoffFrequency = ( double)(ma_min(pResampler->config.sampleRateIn, pResampler->config.sampleRateOut) * 0.5 * pResampler->config.lpfNyquistFactor); - - lpfConfig = ma_lpf_config_init(pResampler->config.format, pResampler->config.channels, lpfSampleRate, lpfCutoffFrequency, pResampler->config.lpfOrder); - - /* - If the resampler is already initialized we don't want to do a fresh initialization of the low-pass filter because it will result in the cached frames - getting cleared. Instead we re-initialize the filter which will maintain any cached frames. - */ - if (isResamplerAlreadyInitialized) { - result = ma_lpf_reinit(&lpfConfig, &pResampler->lpf); - } else { - result = ma_lpf_init_preallocated(&lpfConfig, ma_offset_ptr(pHeap, pHeapLayout->lpfOffset), &pResampler->lpf); - } - - if (result != MA_SUCCESS) { - return result; - } - - - pResampler->inAdvanceInt = pResampler->config.sampleRateIn / pResampler->config.sampleRateOut; - pResampler->inAdvanceFrac = pResampler->config.sampleRateIn % pResampler->config.sampleRateOut; - - /* Our timer was based on the old rate. We need to adjust it so that it's based on the new rate. */ - ma_linear_resampler_adjust_timer_for_new_rate(pResampler, oldSampleRateOut, pResampler->config.sampleRateOut); - - return MA_SUCCESS; -} - -static ma_result ma_linear_resampler_get_heap_layout(const ma_linear_resampler_config* pConfig, ma_linear_resampler_heap_layout* pHeapLayout) -{ - MA_ASSERT(pHeapLayout != NULL); - - MA_ZERO_OBJECT(pHeapLayout); - - if (pConfig == NULL) { - return MA_INVALID_ARGS; - } - - if (pConfig->format != ma_format_f32 && pConfig->format != ma_format_s16) { - return MA_INVALID_ARGS; - } - - if (pConfig->channels == 0) { - return MA_INVALID_ARGS; - } - - pHeapLayout->sizeInBytes = 0; - - /* x0 */ - pHeapLayout->x0Offset = pHeapLayout->sizeInBytes; - if (pConfig->format == ma_format_f32) { - pHeapLayout->sizeInBytes += sizeof(float) * pConfig->channels; - } else { - pHeapLayout->sizeInBytes += sizeof(ma_int16) * pConfig->channels; - } - - /* x1 */ - pHeapLayout->x1Offset = pHeapLayout->sizeInBytes; - if (pConfig->format == ma_format_f32) { - pHeapLayout->sizeInBytes += sizeof(float) * pConfig->channels; - } else { - pHeapLayout->sizeInBytes += sizeof(ma_int16) * pConfig->channels; - } - - /* LPF */ - pHeapLayout->lpfOffset = ma_align_64(pHeapLayout->sizeInBytes); - { - ma_result result; - size_t lpfHeapSizeInBytes; - ma_lpf_config lpfConfig = ma_lpf_config_init(pConfig->format, pConfig->channels, 1, 1, pConfig->lpfOrder); /* Sample rate and cutoff frequency do not matter. */ - - result = ma_lpf_get_heap_size(&lpfConfig, &lpfHeapSizeInBytes); - if (result != MA_SUCCESS) { - return result; - } - - pHeapLayout->sizeInBytes += lpfHeapSizeInBytes; - } - - /* Make sure allocation size is aligned. */ - pHeapLayout->sizeInBytes = ma_align_64(pHeapLayout->sizeInBytes); - - return MA_SUCCESS; -} - -MA_API ma_result ma_linear_resampler_get_heap_size(const ma_linear_resampler_config* pConfig, size_t* pHeapSizeInBytes) -{ - ma_result result; - ma_linear_resampler_heap_layout heapLayout; - - if (pHeapSizeInBytes == NULL) { - return MA_INVALID_ARGS; - } - - *pHeapSizeInBytes = 0; - - result = ma_linear_resampler_get_heap_layout(pConfig, &heapLayout); - if (result != MA_SUCCESS) { - return result; - } - - *pHeapSizeInBytes = heapLayout.sizeInBytes; - - return MA_SUCCESS; -} - -MA_API ma_result ma_linear_resampler_init_preallocated(const ma_linear_resampler_config* pConfig, void* pHeap, ma_linear_resampler* pResampler) -{ - ma_result result; - ma_linear_resampler_heap_layout heapLayout; - - if (pResampler == NULL) { - return MA_INVALID_ARGS; - } - - MA_ZERO_OBJECT(pResampler); - - result = ma_linear_resampler_get_heap_layout(pConfig, &heapLayout); - if (result != MA_SUCCESS) { - return result; - } - - pResampler->config = *pConfig; - - pResampler->_pHeap = pHeap; - MA_ZERO_MEMORY(pHeap, heapLayout.sizeInBytes); - - if (pConfig->format == ma_format_f32) { - pResampler->x0.f32 = (float*)ma_offset_ptr(pHeap, heapLayout.x0Offset); - pResampler->x1.f32 = (float*)ma_offset_ptr(pHeap, heapLayout.x1Offset); - } else { - pResampler->x0.s16 = (ma_int16*)ma_offset_ptr(pHeap, heapLayout.x0Offset); - pResampler->x1.s16 = (ma_int16*)ma_offset_ptr(pHeap, heapLayout.x1Offset); - } - - /* Setting the rate will set up the filter and time advances for us. */ - result = ma_linear_resampler_set_rate_internal(pResampler, pHeap, &heapLayout, pConfig->sampleRateIn, pConfig->sampleRateOut, /* isResamplerAlreadyInitialized = */ MA_FALSE); - if (result != MA_SUCCESS) { - return result; - } - - pResampler->inTimeInt = 1; /* Set this to one to force an input sample to always be loaded for the first output frame. */ - pResampler->inTimeFrac = 0; - - return MA_SUCCESS; -} - -MA_API ma_result ma_linear_resampler_init(const ma_linear_resampler_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_linear_resampler* pResampler) -{ - ma_result result; - size_t heapSizeInBytes; - void* pHeap; - - result = ma_linear_resampler_get_heap_size(pConfig, &heapSizeInBytes); - if (result != MA_SUCCESS) { - return result; - } - - if (heapSizeInBytes > 0) { - pHeap = ma_malloc(heapSizeInBytes, pAllocationCallbacks); - if (pHeap == NULL) { - return MA_OUT_OF_MEMORY; - } - } else { - pHeap = NULL; - } - - result = ma_linear_resampler_init_preallocated(pConfig, pHeap, pResampler); - if (result != MA_SUCCESS) { - ma_free(pHeap, pAllocationCallbacks); - return result; - } - - pResampler->_ownsHeap = MA_TRUE; - return MA_SUCCESS; -} - -MA_API void ma_linear_resampler_uninit(ma_linear_resampler* pResampler, const ma_allocation_callbacks* pAllocationCallbacks) -{ - if (pResampler == NULL) { - return; - } - - ma_lpf_uninit(&pResampler->lpf, pAllocationCallbacks); - - if (pResampler->_ownsHeap) { - ma_free(pResampler->_pHeap, pAllocationCallbacks); - } -} - -static MA_INLINE ma_int16 ma_linear_resampler_mix_s16(ma_int16 x, ma_int16 y, ma_int32 a, const ma_int32 shift) -{ - ma_int32 b; - ma_int32 c; - ma_int32 r; - - MA_ASSERT(a <= (1<> shift); -} - -static void ma_linear_resampler_interpolate_frame_s16(ma_linear_resampler* pResampler, ma_int16* MA_RESTRICT pFrameOut) -{ - ma_uint32 c; - ma_uint32 a; - const ma_uint32 channels = pResampler->config.channels; - const ma_uint32 shift = 12; - - MA_ASSERT(pResampler != NULL); - MA_ASSERT(pFrameOut != NULL); - - a = (pResampler->inTimeFrac << shift) / pResampler->config.sampleRateOut; - - MA_ASSUME(channels > 0); - for (c = 0; c < channels; c += 1) { - ma_int16 s = ma_linear_resampler_mix_s16(pResampler->x0.s16[c], pResampler->x1.s16[c], a, shift); - pFrameOut[c] = s; - } -} - - -static void ma_linear_resampler_interpolate_frame_f32(ma_linear_resampler* pResampler, float* MA_RESTRICT pFrameOut) -{ - ma_uint32 c; - float a; - const ma_uint32 channels = pResampler->config.channels; - - MA_ASSERT(pResampler != NULL); - MA_ASSERT(pFrameOut != NULL); - - a = (float)pResampler->inTimeFrac / pResampler->config.sampleRateOut; - - MA_ASSUME(channels > 0); - for (c = 0; c < channels; c += 1) { - float s = ma_mix_f32_fast(pResampler->x0.f32[c], pResampler->x1.f32[c], a); - pFrameOut[c] = s; - } -} - -static ma_result ma_linear_resampler_process_pcm_frames_s16_downsample(ma_linear_resampler* pResampler, const void* pFramesIn, ma_uint64* pFrameCountIn, void* pFramesOut, ma_uint64* pFrameCountOut) -{ - const ma_int16* pFramesInS16; - /* */ ma_int16* pFramesOutS16; - ma_uint64 frameCountIn; - ma_uint64 frameCountOut; - ma_uint64 framesProcessedIn; - ma_uint64 framesProcessedOut; - - MA_ASSERT(pResampler != NULL); - MA_ASSERT(pFrameCountIn != NULL); - MA_ASSERT(pFrameCountOut != NULL); - - pFramesInS16 = (const ma_int16*)pFramesIn; - pFramesOutS16 = ( ma_int16*)pFramesOut; - frameCountIn = *pFrameCountIn; - frameCountOut = *pFrameCountOut; - framesProcessedIn = 0; - framesProcessedOut = 0; - - while (framesProcessedOut < frameCountOut) { - /* Before interpolating we need to load the buffers. When doing this we need to ensure we run every input sample through the filter. */ - while (pResampler->inTimeInt > 0 && frameCountIn > framesProcessedIn) { - ma_uint32 iChannel; - - if (pFramesInS16 != NULL) { - for (iChannel = 0; iChannel < pResampler->config.channels; iChannel += 1) { - pResampler->x0.s16[iChannel] = pResampler->x1.s16[iChannel]; - pResampler->x1.s16[iChannel] = pFramesInS16[iChannel]; - } - pFramesInS16 += pResampler->config.channels; - } else { - for (iChannel = 0; iChannel < pResampler->config.channels; iChannel += 1) { - pResampler->x0.s16[iChannel] = pResampler->x1.s16[iChannel]; - pResampler->x1.s16[iChannel] = 0; - } - } - - /* Filter. Do not apply filtering if sample rates are the same or else you'll get dangerous glitching. */ - if (pResampler->config.sampleRateIn != pResampler->config.sampleRateOut) { - ma_lpf_process_pcm_frame_s16(&pResampler->lpf, pResampler->x1.s16, pResampler->x1.s16); - } - - framesProcessedIn += 1; - pResampler->inTimeInt -= 1; - } - - if (pResampler->inTimeInt > 0) { - break; /* Ran out of input data. */ - } - - /* Getting here means the frames have been loaded and filtered and we can generate the next output frame. */ - if (pFramesOutS16 != NULL) { - MA_ASSERT(pResampler->inTimeInt == 0); - ma_linear_resampler_interpolate_frame_s16(pResampler, pFramesOutS16); - - pFramesOutS16 += pResampler->config.channels; - } - - framesProcessedOut += 1; - - /* Advance time forward. */ - pResampler->inTimeInt += pResampler->inAdvanceInt; - pResampler->inTimeFrac += pResampler->inAdvanceFrac; - if (pResampler->inTimeFrac >= pResampler->config.sampleRateOut) { - pResampler->inTimeFrac -= pResampler->config.sampleRateOut; - pResampler->inTimeInt += 1; - } - } - - *pFrameCountIn = framesProcessedIn; - *pFrameCountOut = framesProcessedOut; - - return MA_SUCCESS; -} - -static ma_result ma_linear_resampler_process_pcm_frames_s16_upsample(ma_linear_resampler* pResampler, const void* pFramesIn, ma_uint64* pFrameCountIn, void* pFramesOut, ma_uint64* pFrameCountOut) -{ - const ma_int16* pFramesInS16; - /* */ ma_int16* pFramesOutS16; - ma_uint64 frameCountIn; - ma_uint64 frameCountOut; - ma_uint64 framesProcessedIn; - ma_uint64 framesProcessedOut; - - MA_ASSERT(pResampler != NULL); - MA_ASSERT(pFrameCountIn != NULL); - MA_ASSERT(pFrameCountOut != NULL); - - pFramesInS16 = (const ma_int16*)pFramesIn; - pFramesOutS16 = ( ma_int16*)pFramesOut; - frameCountIn = *pFrameCountIn; - frameCountOut = *pFrameCountOut; - framesProcessedIn = 0; - framesProcessedOut = 0; - - while (framesProcessedOut < frameCountOut) { - /* Before interpolating we need to load the buffers. */ - while (pResampler->inTimeInt > 0 && frameCountIn > framesProcessedIn) { - ma_uint32 iChannel; - - if (pFramesInS16 != NULL) { - for (iChannel = 0; iChannel < pResampler->config.channels; iChannel += 1) { - pResampler->x0.s16[iChannel] = pResampler->x1.s16[iChannel]; - pResampler->x1.s16[iChannel] = pFramesInS16[iChannel]; - } - pFramesInS16 += pResampler->config.channels; - } else { - for (iChannel = 0; iChannel < pResampler->config.channels; iChannel += 1) { - pResampler->x0.s16[iChannel] = pResampler->x1.s16[iChannel]; - pResampler->x1.s16[iChannel] = 0; - } - } - - framesProcessedIn += 1; - pResampler->inTimeInt -= 1; - } - - if (pResampler->inTimeInt > 0) { - break; /* Ran out of input data. */ - } - - /* Getting here means the frames have been loaded and we can generate the next output frame. */ - if (pFramesOutS16 != NULL) { - MA_ASSERT(pResampler->inTimeInt == 0); - ma_linear_resampler_interpolate_frame_s16(pResampler, pFramesOutS16); - - /* Filter. Do not apply filtering if sample rates are the same or else you'll get dangerous glitching. */ - if (pResampler->config.sampleRateIn != pResampler->config.sampleRateOut) { - ma_lpf_process_pcm_frame_s16(&pResampler->lpf, pFramesOutS16, pFramesOutS16); - } - - pFramesOutS16 += pResampler->config.channels; - } - - framesProcessedOut += 1; - - /* Advance time forward. */ - pResampler->inTimeInt += pResampler->inAdvanceInt; - pResampler->inTimeFrac += pResampler->inAdvanceFrac; - if (pResampler->inTimeFrac >= pResampler->config.sampleRateOut) { - pResampler->inTimeFrac -= pResampler->config.sampleRateOut; - pResampler->inTimeInt += 1; - } - } - - *pFrameCountIn = framesProcessedIn; - *pFrameCountOut = framesProcessedOut; - - return MA_SUCCESS; -} - -static ma_result ma_linear_resampler_process_pcm_frames_s16(ma_linear_resampler* pResampler, const void* pFramesIn, ma_uint64* pFrameCountIn, void* pFramesOut, ma_uint64* pFrameCountOut) -{ - MA_ASSERT(pResampler != NULL); - - if (pResampler->config.sampleRateIn > pResampler->config.sampleRateOut) { - return ma_linear_resampler_process_pcm_frames_s16_downsample(pResampler, pFramesIn, pFrameCountIn, pFramesOut, pFrameCountOut); - } else { - return ma_linear_resampler_process_pcm_frames_s16_upsample(pResampler, pFramesIn, pFrameCountIn, pFramesOut, pFrameCountOut); - } -} - - -static ma_result ma_linear_resampler_process_pcm_frames_f32_downsample(ma_linear_resampler* pResampler, const void* pFramesIn, ma_uint64* pFrameCountIn, void* pFramesOut, ma_uint64* pFrameCountOut) -{ - const float* pFramesInF32; - /* */ float* pFramesOutF32; - ma_uint64 frameCountIn; - ma_uint64 frameCountOut; - ma_uint64 framesProcessedIn; - ma_uint64 framesProcessedOut; - - MA_ASSERT(pResampler != NULL); - MA_ASSERT(pFrameCountIn != NULL); - MA_ASSERT(pFrameCountOut != NULL); - - pFramesInF32 = (const float*)pFramesIn; - pFramesOutF32 = ( float*)pFramesOut; - frameCountIn = *pFrameCountIn; - frameCountOut = *pFrameCountOut; - framesProcessedIn = 0; - framesProcessedOut = 0; - - while (framesProcessedOut < frameCountOut) { - /* Before interpolating we need to load the buffers. When doing this we need to ensure we run every input sample through the filter. */ - while (pResampler->inTimeInt > 0 && frameCountIn > framesProcessedIn) { - ma_uint32 iChannel; - - if (pFramesInF32 != NULL) { - for (iChannel = 0; iChannel < pResampler->config.channels; iChannel += 1) { - pResampler->x0.f32[iChannel] = pResampler->x1.f32[iChannel]; - pResampler->x1.f32[iChannel] = pFramesInF32[iChannel]; - } - pFramesInF32 += pResampler->config.channels; - } else { - for (iChannel = 0; iChannel < pResampler->config.channels; iChannel += 1) { - pResampler->x0.f32[iChannel] = pResampler->x1.f32[iChannel]; - pResampler->x1.f32[iChannel] = 0; - } - } - - /* Filter. Do not apply filtering if sample rates are the same or else you'll get dangerous glitching. */ - if (pResampler->config.sampleRateIn != pResampler->config.sampleRateOut) { - ma_lpf_process_pcm_frame_f32(&pResampler->lpf, pResampler->x1.f32, pResampler->x1.f32); - } - - framesProcessedIn += 1; - pResampler->inTimeInt -= 1; - } - - if (pResampler->inTimeInt > 0) { - break; /* Ran out of input data. */ - } - - /* Getting here means the frames have been loaded and filtered and we can generate the next output frame. */ - if (pFramesOutF32 != NULL) { - MA_ASSERT(pResampler->inTimeInt == 0); - ma_linear_resampler_interpolate_frame_f32(pResampler, pFramesOutF32); - - pFramesOutF32 += pResampler->config.channels; - } - - framesProcessedOut += 1; - - /* Advance time forward. */ - pResampler->inTimeInt += pResampler->inAdvanceInt; - pResampler->inTimeFrac += pResampler->inAdvanceFrac; - if (pResampler->inTimeFrac >= pResampler->config.sampleRateOut) { - pResampler->inTimeFrac -= pResampler->config.sampleRateOut; - pResampler->inTimeInt += 1; - } - } - - *pFrameCountIn = framesProcessedIn; - *pFrameCountOut = framesProcessedOut; - - return MA_SUCCESS; -} - -static ma_result ma_linear_resampler_process_pcm_frames_f32_upsample(ma_linear_resampler* pResampler, const void* pFramesIn, ma_uint64* pFrameCountIn, void* pFramesOut, ma_uint64* pFrameCountOut) -{ - const float* pFramesInF32; - /* */ float* pFramesOutF32; - ma_uint64 frameCountIn; - ma_uint64 frameCountOut; - ma_uint64 framesProcessedIn; - ma_uint64 framesProcessedOut; - - MA_ASSERT(pResampler != NULL); - MA_ASSERT(pFrameCountIn != NULL); - MA_ASSERT(pFrameCountOut != NULL); - - pFramesInF32 = (const float*)pFramesIn; - pFramesOutF32 = ( float*)pFramesOut; - frameCountIn = *pFrameCountIn; - frameCountOut = *pFrameCountOut; - framesProcessedIn = 0; - framesProcessedOut = 0; - - while (framesProcessedOut < frameCountOut) { - /* Before interpolating we need to load the buffers. */ - while (pResampler->inTimeInt > 0 && frameCountIn > framesProcessedIn) { - ma_uint32 iChannel; - - if (pFramesInF32 != NULL) { - for (iChannel = 0; iChannel < pResampler->config.channels; iChannel += 1) { - pResampler->x0.f32[iChannel] = pResampler->x1.f32[iChannel]; - pResampler->x1.f32[iChannel] = pFramesInF32[iChannel]; - } - pFramesInF32 += pResampler->config.channels; - } else { - for (iChannel = 0; iChannel < pResampler->config.channels; iChannel += 1) { - pResampler->x0.f32[iChannel] = pResampler->x1.f32[iChannel]; - pResampler->x1.f32[iChannel] = 0; - } - } - - framesProcessedIn += 1; - pResampler->inTimeInt -= 1; - } - - if (pResampler->inTimeInt > 0) { - break; /* Ran out of input data. */ - } - - /* Getting here means the frames have been loaded and we can generate the next output frame. */ - if (pFramesOutF32 != NULL) { - MA_ASSERT(pResampler->inTimeInt == 0); - ma_linear_resampler_interpolate_frame_f32(pResampler, pFramesOutF32); - - /* Filter. Do not apply filtering if sample rates are the same or else you'll get dangerous glitching. */ - if (pResampler->config.sampleRateIn != pResampler->config.sampleRateOut) { - ma_lpf_process_pcm_frame_f32(&pResampler->lpf, pFramesOutF32, pFramesOutF32); - } - - pFramesOutF32 += pResampler->config.channels; - } - - framesProcessedOut += 1; - - /* Advance time forward. */ - pResampler->inTimeInt += pResampler->inAdvanceInt; - pResampler->inTimeFrac += pResampler->inAdvanceFrac; - if (pResampler->inTimeFrac >= pResampler->config.sampleRateOut) { - pResampler->inTimeFrac -= pResampler->config.sampleRateOut; - pResampler->inTimeInt += 1; - } - } - - *pFrameCountIn = framesProcessedIn; - *pFrameCountOut = framesProcessedOut; - - return MA_SUCCESS; -} - -static ma_result ma_linear_resampler_process_pcm_frames_f32(ma_linear_resampler* pResampler, const void* pFramesIn, ma_uint64* pFrameCountIn, void* pFramesOut, ma_uint64* pFrameCountOut) -{ - MA_ASSERT(pResampler != NULL); - - if (pResampler->config.sampleRateIn > pResampler->config.sampleRateOut) { - return ma_linear_resampler_process_pcm_frames_f32_downsample(pResampler, pFramesIn, pFrameCountIn, pFramesOut, pFrameCountOut); - } else { - return ma_linear_resampler_process_pcm_frames_f32_upsample(pResampler, pFramesIn, pFrameCountIn, pFramesOut, pFrameCountOut); - } -} - - -MA_API ma_result ma_linear_resampler_process_pcm_frames(ma_linear_resampler* pResampler, const void* pFramesIn, ma_uint64* pFrameCountIn, void* pFramesOut, ma_uint64* pFrameCountOut) -{ - if (pResampler == NULL) { - return MA_INVALID_ARGS; - } - - /* */ if (pResampler->config.format == ma_format_s16) { - return ma_linear_resampler_process_pcm_frames_s16(pResampler, pFramesIn, pFrameCountIn, pFramesOut, pFrameCountOut); - } else if (pResampler->config.format == ma_format_f32) { - return ma_linear_resampler_process_pcm_frames_f32(pResampler, pFramesIn, pFrameCountIn, pFramesOut, pFrameCountOut); - } else { - /* Should never get here. Getting here means the format is not supported and you didn't check the return value of ma_linear_resampler_init(). */ - MA_ASSERT(MA_FALSE); - return MA_INVALID_ARGS; - } -} - - -MA_API ma_result ma_linear_resampler_set_rate(ma_linear_resampler* pResampler, ma_uint32 sampleRateIn, ma_uint32 sampleRateOut) -{ - return ma_linear_resampler_set_rate_internal(pResampler, NULL, NULL, sampleRateIn, sampleRateOut, /* isResamplerAlreadyInitialized = */ MA_TRUE); -} - -MA_API ma_result ma_linear_resampler_set_rate_ratio(ma_linear_resampler* pResampler, float ratioInOut) -{ - ma_uint32 n; - ma_uint32 d; - - if (pResampler == NULL) { - return MA_INVALID_ARGS; - } - - if (ratioInOut <= 0) { - return MA_INVALID_ARGS; - } - - d = 1000000; - n = (ma_uint32)(ratioInOut * d); - - if (n == 0) { - return MA_INVALID_ARGS; /* Ratio too small. */ - } - - MA_ASSERT(n != 0); - - return ma_linear_resampler_set_rate(pResampler, n, d); -} - -MA_API ma_uint64 ma_linear_resampler_get_input_latency(const ma_linear_resampler* pResampler) -{ - if (pResampler == NULL) { - return 0; - } - - return 1 + ma_lpf_get_latency(&pResampler->lpf); -} - -MA_API ma_uint64 ma_linear_resampler_get_output_latency(const ma_linear_resampler* pResampler) -{ - if (pResampler == NULL) { - return 0; - } - - return ma_linear_resampler_get_input_latency(pResampler) * pResampler->config.sampleRateOut / pResampler->config.sampleRateIn; -} - -MA_API ma_result ma_linear_resampler_get_required_input_frame_count(const ma_linear_resampler* pResampler, ma_uint64 outputFrameCount, ma_uint64* pInputFrameCount) -{ - ma_uint64 inputFrameCount; - - if (pInputFrameCount == NULL) { - return MA_INVALID_ARGS; - } - - *pInputFrameCount = 0; - - if (pResampler == NULL) { - return MA_INVALID_ARGS; - } - - if (outputFrameCount == 0) { - return MA_SUCCESS; - } - - /* Any whole input frames are consumed before the first output frame is generated. */ - inputFrameCount = pResampler->inTimeInt; - outputFrameCount -= 1; - - /* The rest of the output frames can be calculated in constant time. */ - inputFrameCount += outputFrameCount * pResampler->inAdvanceInt; - inputFrameCount += (pResampler->inTimeFrac + (outputFrameCount * pResampler->inAdvanceFrac)) / pResampler->config.sampleRateOut; - - *pInputFrameCount = inputFrameCount; - - return MA_SUCCESS; -} - -MA_API ma_result ma_linear_resampler_get_expected_output_frame_count(const ma_linear_resampler* pResampler, ma_uint64 inputFrameCount, ma_uint64* pOutputFrameCount) -{ - ma_uint64 outputFrameCount; - ma_uint64 preliminaryInputFrameCountFromFrac; - ma_uint64 preliminaryInputFrameCount; - - if (pOutputFrameCount == NULL) { - return MA_INVALID_ARGS; - } - - *pOutputFrameCount = 0; - - if (pResampler == NULL) { - return MA_INVALID_ARGS; - } - - /* - The first step is to get a preliminary output frame count. This will either be exactly equal to what we need, or less by 1. We need to - determine how many input frames will be consumed by this value. If it's greater than our original input frame count it means we won't - be able to generate an extra frame because we will have run out of input data. Otherwise we will have enough input for the generation - of an extra output frame. This add-by-one logic is necessary due to how the data loading logic works when processing frames. - */ - outputFrameCount = (inputFrameCount * pResampler->config.sampleRateOut) / pResampler->config.sampleRateIn; - - /* - We need to determine how many *whole* input frames will have been processed to generate our preliminary output frame count. This is - used in the logic below to determine whether or not we need to add an extra output frame. - */ - preliminaryInputFrameCountFromFrac = (pResampler->inTimeFrac + outputFrameCount*pResampler->inAdvanceFrac) / pResampler->config.sampleRateOut; - preliminaryInputFrameCount = (pResampler->inTimeInt + outputFrameCount*pResampler->inAdvanceInt ) + preliminaryInputFrameCountFromFrac; - - /* - If the total number of *whole* input frames that would be required to generate our preliminary output frame count is greater than - the amount of whole input frames we have available as input we need to *not* add an extra output frame as there won't be enough data - to actually process. Otherwise we need to add the extra output frame. - */ - if (preliminaryInputFrameCount <= inputFrameCount) { - outputFrameCount += 1; - } - - *pOutputFrameCount = outputFrameCount; - - return MA_SUCCESS; -} - -MA_API ma_result ma_linear_resampler_reset(ma_linear_resampler* pResampler) -{ - ma_uint32 iChannel; - - if (pResampler == NULL) { - return MA_INVALID_ARGS; - } - - /* Timers need to be cleared back to zero. */ - pResampler->inTimeInt = 1; /* Set this to one to force an input sample to always be loaded for the first output frame. */ - pResampler->inTimeFrac = 0; - - /* Cached samples need to be cleared. */ - if (pResampler->config.format == ma_format_f32) { - for (iChannel = 0; iChannel < pResampler->config.channels; iChannel += 1) { - pResampler->x0.f32[iChannel] = 0; - pResampler->x1.f32[iChannel] = 0; - } - } else { - for (iChannel = 0; iChannel < pResampler->config.channels; iChannel += 1) { - pResampler->x0.s16[iChannel] = 0; - pResampler->x1.s16[iChannel] = 0; - } - } - - /* The low pass filter needs to have its cache reset. */ - ma_lpf_clear_cache(&pResampler->lpf); - - return MA_SUCCESS; -} - - - -/* Linear resampler backend vtable. */ -static ma_linear_resampler_config ma_resampling_backend_get_config__linear(const ma_resampler_config* pConfig) -{ - ma_linear_resampler_config linearConfig; - - linearConfig = ma_linear_resampler_config_init(pConfig->format, pConfig->channels, pConfig->sampleRateIn, pConfig->sampleRateOut); - linearConfig.lpfOrder = pConfig->linear.lpfOrder; - - return linearConfig; -} - -static ma_result ma_resampling_backend_get_heap_size__linear(void* pUserData, const ma_resampler_config* pConfig, size_t* pHeapSizeInBytes) -{ - ma_linear_resampler_config linearConfig; - - (void)pUserData; - - linearConfig = ma_resampling_backend_get_config__linear(pConfig); - - return ma_linear_resampler_get_heap_size(&linearConfig, pHeapSizeInBytes); -} - -static ma_result ma_resampling_backend_init__linear(void* pUserData, const ma_resampler_config* pConfig, void* pHeap, ma_resampling_backend** ppBackend) -{ - ma_resampler* pResampler = (ma_resampler*)pUserData; - ma_result result; - ma_linear_resampler_config linearConfig; - - (void)pUserData; - - linearConfig = ma_resampling_backend_get_config__linear(pConfig); - - result = ma_linear_resampler_init_preallocated(&linearConfig, pHeap, &pResampler->state.linear); - if (result != MA_SUCCESS) { - return result; - } - - *ppBackend = &pResampler->state.linear; - - return MA_SUCCESS; -} - -static void ma_resampling_backend_uninit__linear(void* pUserData, ma_resampling_backend* pBackend, const ma_allocation_callbacks* pAllocationCallbacks) -{ - (void)pUserData; - - ma_linear_resampler_uninit((ma_linear_resampler*)pBackend, pAllocationCallbacks); -} - -static ma_result ma_resampling_backend_process__linear(void* pUserData, ma_resampling_backend* pBackend, const void* pFramesIn, ma_uint64* pFrameCountIn, void* pFramesOut, ma_uint64* pFrameCountOut) -{ - (void)pUserData; - - return ma_linear_resampler_process_pcm_frames((ma_linear_resampler*)pBackend, pFramesIn, pFrameCountIn, pFramesOut, pFrameCountOut); -} - -static ma_result ma_resampling_backend_set_rate__linear(void* pUserData, ma_resampling_backend* pBackend, ma_uint32 sampleRateIn, ma_uint32 sampleRateOut) -{ - (void)pUserData; - - return ma_linear_resampler_set_rate((ma_linear_resampler*)pBackend, sampleRateIn, sampleRateOut); -} - -static ma_uint64 ma_resampling_backend_get_input_latency__linear(void* pUserData, const ma_resampling_backend* pBackend) -{ - (void)pUserData; - - return ma_linear_resampler_get_input_latency((const ma_linear_resampler*)pBackend); -} - -static ma_uint64 ma_resampling_backend_get_output_latency__linear(void* pUserData, const ma_resampling_backend* pBackend) -{ - (void)pUserData; - - return ma_linear_resampler_get_output_latency((const ma_linear_resampler*)pBackend); -} - -static ma_result ma_resampling_backend_get_required_input_frame_count__linear(void* pUserData, const ma_resampling_backend* pBackend, ma_uint64 outputFrameCount, ma_uint64* pInputFrameCount) -{ - (void)pUserData; - - return ma_linear_resampler_get_required_input_frame_count((const ma_linear_resampler*)pBackend, outputFrameCount, pInputFrameCount); -} - -static ma_result ma_resampling_backend_get_expected_output_frame_count__linear(void* pUserData, const ma_resampling_backend* pBackend, ma_uint64 inputFrameCount, ma_uint64* pOutputFrameCount) -{ - (void)pUserData; - - return ma_linear_resampler_get_expected_output_frame_count((const ma_linear_resampler*)pBackend, inputFrameCount, pOutputFrameCount); -} - -static ma_result ma_resampling_backend_reset__linear(void* pUserData, ma_resampling_backend* pBackend) -{ - (void)pUserData; - - return ma_linear_resampler_reset((ma_linear_resampler*)pBackend); -} - -static ma_resampling_backend_vtable g_ma_linear_resampler_vtable = -{ - ma_resampling_backend_get_heap_size__linear, - ma_resampling_backend_init__linear, - ma_resampling_backend_uninit__linear, - ma_resampling_backend_process__linear, - ma_resampling_backend_set_rate__linear, - ma_resampling_backend_get_input_latency__linear, - ma_resampling_backend_get_output_latency__linear, - ma_resampling_backend_get_required_input_frame_count__linear, - ma_resampling_backend_get_expected_output_frame_count__linear, - ma_resampling_backend_reset__linear -}; - - - -MA_API ma_resampler_config ma_resampler_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRateIn, ma_uint32 sampleRateOut, ma_resample_algorithm algorithm) -{ - ma_resampler_config config; - - MA_ZERO_OBJECT(&config); - config.format = format; - config.channels = channels; - config.sampleRateIn = sampleRateIn; - config.sampleRateOut = sampleRateOut; - config.algorithm = algorithm; - - /* Linear. */ - config.linear.lpfOrder = ma_min(MA_DEFAULT_RESAMPLER_LPF_ORDER, MA_MAX_FILTER_ORDER); - - return config; -} - -static ma_result ma_resampler_get_vtable(const ma_resampler_config* pConfig, ma_resampler* pResampler, ma_resampling_backend_vtable** ppVTable, void** ppUserData) -{ - MA_ASSERT(pConfig != NULL); - MA_ASSERT(ppVTable != NULL); - MA_ASSERT(ppUserData != NULL); - - /* Safety. */ - *ppVTable = NULL; - *ppUserData = NULL; - - switch (pConfig->algorithm) - { - case ma_resample_algorithm_linear: - { - *ppVTable = &g_ma_linear_resampler_vtable; - *ppUserData = pResampler; - } break; - - case ma_resample_algorithm_custom: - { - *ppVTable = pConfig->pBackendVTable; - *ppUserData = pConfig->pBackendUserData; - } break; - - default: return MA_INVALID_ARGS; - } - - return MA_SUCCESS; -} - -MA_API ma_result ma_resampler_get_heap_size(const ma_resampler_config* pConfig, size_t* pHeapSizeInBytes) -{ - ma_result result; - ma_resampling_backend_vtable* pVTable; - void* pVTableUserData; - - if (pHeapSizeInBytes == NULL) { - return MA_INVALID_ARGS; - } - - *pHeapSizeInBytes = 0; - - if (pConfig == NULL) { - return MA_INVALID_ARGS; - } - - result = ma_resampler_get_vtable(pConfig, NULL, &pVTable, &pVTableUserData); - if (result != MA_SUCCESS) { - return result; - } - - if (pVTable == NULL || pVTable->onGetHeapSize == NULL) { - return MA_NOT_IMPLEMENTED; - } - - result = pVTable->onGetHeapSize(pVTableUserData, pConfig, pHeapSizeInBytes); - if (result != MA_SUCCESS) { - return result; - } - - return MA_SUCCESS; -} - -MA_API ma_result ma_resampler_init_preallocated(const ma_resampler_config* pConfig, void* pHeap, ma_resampler* pResampler) -{ - ma_result result; - - if (pResampler == NULL) { - return MA_INVALID_ARGS; - } - - MA_ZERO_OBJECT(pResampler); - - if (pConfig == NULL) { - return MA_INVALID_ARGS; - } - - pResampler->_pHeap = pHeap; - pResampler->format = pConfig->format; - pResampler->channels = pConfig->channels; - pResampler->sampleRateIn = pConfig->sampleRateIn; - pResampler->sampleRateOut = pConfig->sampleRateOut; - - result = ma_resampler_get_vtable(pConfig, pResampler, &pResampler->pBackendVTable, &pResampler->pBackendUserData); - if (result != MA_SUCCESS) { - return result; - } - - if (pResampler->pBackendVTable == NULL || pResampler->pBackendVTable->onInit == NULL) { - return MA_NOT_IMPLEMENTED; /* onInit not implemented. */ - } - - result = pResampler->pBackendVTable->onInit(pResampler->pBackendUserData, pConfig, pHeap, &pResampler->pBackend); - if (result != MA_SUCCESS) { - return result; - } - - return MA_SUCCESS; -} - -MA_API ma_result ma_resampler_init(const ma_resampler_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_resampler* pResampler) -{ - ma_result result; - size_t heapSizeInBytes; - void* pHeap; - - result = ma_resampler_get_heap_size(pConfig, &heapSizeInBytes); - if (result != MA_SUCCESS) { - return result; - } - - if (heapSizeInBytes > 0) { - pHeap = ma_malloc(heapSizeInBytes, pAllocationCallbacks); - if (pHeap == NULL) { - return MA_OUT_OF_MEMORY; - } - } else { - pHeap = NULL; - } - - result = ma_resampler_init_preallocated(pConfig, pHeap, pResampler); - if (result != MA_SUCCESS) { - ma_free(pHeap, pAllocationCallbacks); - return result; - } - - pResampler->_ownsHeap = MA_TRUE; - return MA_SUCCESS; -} - -MA_API void ma_resampler_uninit(ma_resampler* pResampler, const ma_allocation_callbacks* pAllocationCallbacks) -{ - if (pResampler == NULL) { - return; - } - - if (pResampler->pBackendVTable == NULL || pResampler->pBackendVTable->onUninit == NULL) { - return; - } - - pResampler->pBackendVTable->onUninit(pResampler->pBackendUserData, pResampler->pBackend, pAllocationCallbacks); - - if (pResampler->_ownsHeap) { - ma_free(pResampler->_pHeap, pAllocationCallbacks); - } -} - -MA_API ma_result ma_resampler_process_pcm_frames(ma_resampler* pResampler, const void* pFramesIn, ma_uint64* pFrameCountIn, void* pFramesOut, ma_uint64* pFrameCountOut) -{ - if (pResampler == NULL) { - return MA_INVALID_ARGS; - } - - if (pFrameCountOut == NULL && pFrameCountIn == NULL) { - return MA_INVALID_ARGS; - } - - if (pResampler->pBackendVTable == NULL || pResampler->pBackendVTable->onProcess == NULL) { - return MA_NOT_IMPLEMENTED; - } - - return pResampler->pBackendVTable->onProcess(pResampler->pBackendUserData, pResampler->pBackend, pFramesIn, pFrameCountIn, pFramesOut, pFrameCountOut); -} - -MA_API ma_result ma_resampler_set_rate(ma_resampler* pResampler, ma_uint32 sampleRateIn, ma_uint32 sampleRateOut) -{ - ma_result result; - - if (pResampler == NULL) { - return MA_INVALID_ARGS; - } - - if (sampleRateIn == 0 || sampleRateOut == 0) { - return MA_INVALID_ARGS; - } - - if (pResampler->pBackendVTable == NULL || pResampler->pBackendVTable->onSetRate == NULL) { - return MA_NOT_IMPLEMENTED; - } - - result = pResampler->pBackendVTable->onSetRate(pResampler->pBackendUserData, pResampler->pBackend, sampleRateIn, sampleRateOut); - if (result != MA_SUCCESS) { - return result; - } - - pResampler->sampleRateIn = sampleRateIn; - pResampler->sampleRateOut = sampleRateOut; - - return MA_SUCCESS; -} - -MA_API ma_result ma_resampler_set_rate_ratio(ma_resampler* pResampler, float ratio) -{ - ma_uint32 n; - ma_uint32 d; - - if (pResampler == NULL) { - return MA_INVALID_ARGS; - } - - if (ratio <= 0) { - return MA_INVALID_ARGS; - } - - d = 1000; - n = (ma_uint32)(ratio * d); - - if (n == 0) { - return MA_INVALID_ARGS; /* Ratio too small. */ - } - - MA_ASSERT(n != 0); - - return ma_resampler_set_rate(pResampler, n, d); -} - -MA_API ma_uint64 ma_resampler_get_input_latency(const ma_resampler* pResampler) -{ - if (pResampler == NULL) { - return 0; - } - - if (pResampler->pBackendVTable == NULL || pResampler->pBackendVTable->onGetInputLatency == NULL) { - return 0; - } - - return pResampler->pBackendVTable->onGetInputLatency(pResampler->pBackendUserData, pResampler->pBackend); -} - -MA_API ma_uint64 ma_resampler_get_output_latency(const ma_resampler* pResampler) -{ - if (pResampler == NULL) { - return 0; - } - - if (pResampler->pBackendVTable == NULL || pResampler->pBackendVTable->onGetOutputLatency == NULL) { - return 0; - } - - return pResampler->pBackendVTable->onGetOutputLatency(pResampler->pBackendUserData, pResampler->pBackend); -} - -MA_API ma_result ma_resampler_get_required_input_frame_count(const ma_resampler* pResampler, ma_uint64 outputFrameCount, ma_uint64* pInputFrameCount) -{ - if (pInputFrameCount == NULL) { - return MA_INVALID_ARGS; - } - - *pInputFrameCount = 0; - - if (pResampler == NULL) { - return MA_INVALID_ARGS; - } - - if (pResampler->pBackendVTable == NULL || pResampler->pBackendVTable->onGetRequiredInputFrameCount == NULL) { - return MA_NOT_IMPLEMENTED; - } - - return pResampler->pBackendVTable->onGetRequiredInputFrameCount(pResampler->pBackendUserData, pResampler->pBackend, outputFrameCount, pInputFrameCount); -} - -MA_API ma_result ma_resampler_get_expected_output_frame_count(const ma_resampler* pResampler, ma_uint64 inputFrameCount, ma_uint64* pOutputFrameCount) -{ - if (pOutputFrameCount == NULL) { - return MA_INVALID_ARGS; - } - - *pOutputFrameCount = 0; - - if (pResampler == NULL) { - return MA_INVALID_ARGS; - } - - if (pResampler->pBackendVTable == NULL || pResampler->pBackendVTable->onGetExpectedOutputFrameCount == NULL) { - return MA_NOT_IMPLEMENTED; - } - - return pResampler->pBackendVTable->onGetExpectedOutputFrameCount(pResampler->pBackendUserData, pResampler->pBackend, inputFrameCount, pOutputFrameCount); -} - -MA_API ma_result ma_resampler_reset(ma_resampler* pResampler) -{ - if (pResampler == NULL) { - return MA_INVALID_ARGS; - } - - if (pResampler->pBackendVTable == NULL || pResampler->pBackendVTable->onReset == NULL) { - return MA_NOT_IMPLEMENTED; - } - - return pResampler->pBackendVTable->onReset(pResampler->pBackendUserData, pResampler->pBackend); -} - -/************************************************************************************************************************************************************** - -Channel Conversion - -**************************************************************************************************************************************************************/ -#ifndef MA_CHANNEL_CONVERTER_FIXED_POINT_SHIFT -#define MA_CHANNEL_CONVERTER_FIXED_POINT_SHIFT 12 -#endif - -#define MA_PLANE_LEFT 0 -#define MA_PLANE_RIGHT 1 -#define MA_PLANE_FRONT 2 -#define MA_PLANE_BACK 3 -#define MA_PLANE_BOTTOM 4 -#define MA_PLANE_TOP 5 - -static float g_maChannelPlaneRatios[MA_CHANNEL_POSITION_COUNT][6] = { - { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_NONE */ - { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_MONO */ - { 0.5f, 0.0f, 0.5f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_FRONT_LEFT */ - { 0.0f, 0.5f, 0.5f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_FRONT_RIGHT */ - { 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_FRONT_CENTER */ - { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_LFE */ - { 0.5f, 0.0f, 0.0f, 0.5f, 0.0f, 0.0f}, /* MA_CHANNEL_BACK_LEFT */ - { 0.0f, 0.5f, 0.0f, 0.5f, 0.0f, 0.0f}, /* MA_CHANNEL_BACK_RIGHT */ - { 0.25f, 0.0f, 0.75f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_FRONT_LEFT_CENTER */ - { 0.0f, 0.25f, 0.75f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_FRONT_RIGHT_CENTER */ - { 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f}, /* MA_CHANNEL_BACK_CENTER */ - { 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_SIDE_LEFT */ - { 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_SIDE_RIGHT */ - { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f}, /* MA_CHANNEL_TOP_CENTER */ - { 0.33f, 0.0f, 0.33f, 0.0f, 0.0f, 0.34f}, /* MA_CHANNEL_TOP_FRONT_LEFT */ - { 0.0f, 0.0f, 0.5f, 0.0f, 0.0f, 0.5f}, /* MA_CHANNEL_TOP_FRONT_CENTER */ - { 0.0f, 0.33f, 0.33f, 0.0f, 0.0f, 0.34f}, /* MA_CHANNEL_TOP_FRONT_RIGHT */ - { 0.33f, 0.0f, 0.0f, 0.33f, 0.0f, 0.34f}, /* MA_CHANNEL_TOP_BACK_LEFT */ - { 0.0f, 0.0f, 0.0f, 0.5f, 0.0f, 0.5f}, /* MA_CHANNEL_TOP_BACK_CENTER */ - { 0.0f, 0.33f, 0.0f, 0.33f, 0.0f, 0.34f}, /* MA_CHANNEL_TOP_BACK_RIGHT */ - { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_0 */ - { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_1 */ - { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_2 */ - { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_3 */ - { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_4 */ - { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_5 */ - { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_6 */ - { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_7 */ - { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_8 */ - { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_9 */ - { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_10 */ - { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_11 */ - { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_12 */ - { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_13 */ - { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_14 */ - { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_15 */ - { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_16 */ - { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_17 */ - { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_18 */ - { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_19 */ - { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_20 */ - { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_21 */ - { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_22 */ - { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_23 */ - { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_24 */ - { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_25 */ - { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_26 */ - { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_27 */ - { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_28 */ - { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_29 */ - { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_30 */ - { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_31 */ -}; - -static float ma_calculate_channel_position_rectangular_weight(ma_channel channelPositionA, ma_channel channelPositionB) -{ - /* - Imagine the following simplified example: You have a single input speaker which is the front/left speaker which you want to convert to - the following output configuration: - - - front/left - - side/left - - back/left - - The front/left output is easy - it the same speaker position so it receives the full contribution of the front/left input. The amount - of contribution to apply to the side/left and back/left speakers, however, is a bit more complicated. - - Imagine the front/left speaker as emitting audio from two planes - the front plane and the left plane. You can think of the front/left - speaker emitting half of its total volume from the front, and the other half from the left. Since part of its volume is being emitted - from the left side, and the side/left and back/left channels also emit audio from the left plane, one would expect that they would - receive some amount of contribution from front/left speaker. The amount of contribution depends on how many planes are shared between - the two speakers. Note that in the examples below I've added a top/front/left speaker as an example just to show how the math works - across 3 spatial dimensions. - - The first thing to do is figure out how each speaker's volume is spread over each of plane: - - front/left: 2 planes (front and left) = 1/2 = half its total volume on each plane - - side/left: 1 plane (left only) = 1/1 = entire volume from left plane - - back/left: 2 planes (back and left) = 1/2 = half its total volume on each plane - - top/front/left: 3 planes (top, front and left) = 1/3 = one third its total volume on each plane - - The amount of volume each channel contributes to each of its planes is what controls how much it is willing to given and take to other - channels on the same plane. The volume that is willing to the given by one channel is multiplied by the volume that is willing to be - taken by the other to produce the final contribution. - */ - - /* Contribution = Sum(Volume to Give * Volume to Take) */ - float contribution = - g_maChannelPlaneRatios[channelPositionA][0] * g_maChannelPlaneRatios[channelPositionB][0] + - g_maChannelPlaneRatios[channelPositionA][1] * g_maChannelPlaneRatios[channelPositionB][1] + - g_maChannelPlaneRatios[channelPositionA][2] * g_maChannelPlaneRatios[channelPositionB][2] + - g_maChannelPlaneRatios[channelPositionA][3] * g_maChannelPlaneRatios[channelPositionB][3] + - g_maChannelPlaneRatios[channelPositionA][4] * g_maChannelPlaneRatios[channelPositionB][4] + - g_maChannelPlaneRatios[channelPositionA][5] * g_maChannelPlaneRatios[channelPositionB][5]; - - return contribution; -} - -MA_API ma_channel_converter_config ma_channel_converter_config_init(ma_format format, ma_uint32 channelsIn, const ma_channel* pChannelMapIn, ma_uint32 channelsOut, const ma_channel* pChannelMapOut, ma_channel_mix_mode mixingMode) -{ - ma_channel_converter_config config; - - MA_ZERO_OBJECT(&config); - config.format = format; - config.channelsIn = channelsIn; - config.channelsOut = channelsOut; - config.pChannelMapIn = pChannelMapIn; - config.pChannelMapOut = pChannelMapOut; - config.mixingMode = mixingMode; - - return config; -} - -static ma_int32 ma_channel_converter_float_to_fixed(float x) -{ - return (ma_int32)(x * (1< 0); - - for (iChannel = 0; iChannel < channels; ++iChannel) { - if (ma_is_spatial_channel_position(ma_channel_map_get_channel(pChannelMap, channels, iChannel))) { - spatialChannelCount++; - } - } - - return spatialChannelCount; -} - -static ma_bool32 ma_is_spatial_channel_position(ma_channel channelPosition) -{ - int i; - - if (channelPosition == MA_CHANNEL_NONE || channelPosition == MA_CHANNEL_MONO || channelPosition == MA_CHANNEL_LFE) { - return MA_FALSE; - } - - if (channelPosition >= MA_CHANNEL_AUX_0 && channelPosition <= MA_CHANNEL_AUX_31) { - return MA_FALSE; - } - - for (i = 0; i < 6; ++i) { /* Each side of a cube. */ - if (g_maChannelPlaneRatios[channelPosition][i] != 0) { - return MA_TRUE; - } - } - - return MA_FALSE; -} - - -static ma_bool32 ma_channel_map_is_passthrough(const ma_channel* pChannelMapIn, ma_uint32 channelsIn, const ma_channel* pChannelMapOut, ma_uint32 channelsOut) -{ - if (channelsOut == channelsIn) { - return ma_channel_map_is_equal(pChannelMapOut, pChannelMapIn, channelsOut); - } else { - return MA_FALSE; /* Channel counts differ, so cannot be a passthrough. */ - } -} - -static ma_channel_conversion_path ma_channel_map_get_conversion_path(const ma_channel* pChannelMapIn, ma_uint32 channelsIn, const ma_channel* pChannelMapOut, ma_uint32 channelsOut, ma_channel_mix_mode mode) -{ - if (ma_channel_map_is_passthrough(pChannelMapIn, channelsIn, pChannelMapOut, channelsOut)) { - return ma_channel_conversion_path_passthrough; - } - - if (channelsOut == 1 && (pChannelMapOut == NULL || pChannelMapOut[0] == MA_CHANNEL_MONO)) { - return ma_channel_conversion_path_mono_out; - } - - if (channelsIn == 1 && (pChannelMapIn == NULL || pChannelMapIn[0] == MA_CHANNEL_MONO)) { - return ma_channel_conversion_path_mono_in; - } - - if (mode == ma_channel_mix_mode_custom_weights) { - return ma_channel_conversion_path_weights; - } - - /* - We can use a simple shuffle if both channel maps have the same channel count and all channel - positions are present in both. - */ - if (channelsIn == channelsOut) { - ma_uint32 iChannelIn; - ma_bool32 areAllChannelPositionsPresent = MA_TRUE; - for (iChannelIn = 0; iChannelIn < channelsIn; ++iChannelIn) { - ma_bool32 isInputChannelPositionInOutput = ma_channel_map_contains_channel_position(channelsOut, pChannelMapOut, ma_channel_map_get_channel(pChannelMapIn, channelsIn, iChannelIn)); - if (!isInputChannelPositionInOutput) { - areAllChannelPositionsPresent = MA_FALSE; - break; - } - } - - if (areAllChannelPositionsPresent) { - return ma_channel_conversion_path_shuffle; - } - } - - /* Getting here means we'll need to use weights. */ - return ma_channel_conversion_path_weights; -} - - -static ma_result ma_channel_map_build_shuffle_table(const ma_channel* pChannelMapIn, ma_uint32 channelCountIn, const ma_channel* pChannelMapOut, ma_uint32 channelCountOut, ma_uint8* pShuffleTable) -{ - ma_uint32 iChannelIn; - ma_uint32 iChannelOut; - - if (pShuffleTable == NULL || channelCountIn == 0 || channelCountOut == 0) { - return MA_INVALID_ARGS; - } - - /* - When building the shuffle table we just do a 1:1 mapping based on the first occurrence of a channel. If the - input channel has more than one occurrence of a channel position, the second one will be ignored. - */ - for (iChannelOut = 0; iChannelOut < channelCountOut; iChannelOut += 1) { - ma_channel channelOut; - - /* Default to MA_CHANNEL_INDEX_NULL so that if a mapping is not found it'll be set appropriately. */ - pShuffleTable[iChannelOut] = MA_CHANNEL_INDEX_NULL; - - channelOut = ma_channel_map_get_channel(pChannelMapOut, channelCountOut, iChannelOut); - for (iChannelIn = 0; iChannelIn < channelCountIn; iChannelIn += 1) { - ma_channel channelIn; - - channelIn = ma_channel_map_get_channel(pChannelMapIn, channelCountIn, iChannelIn); - if (channelOut == channelIn) { - pShuffleTable[iChannelOut] = (ma_uint8)iChannelIn; - break; - } - - /* - Getting here means the channels don't exactly match, but we are going to support some - relaxed matching for practicality. If, for example, there are two stereo channel maps, - but one uses front left/right and the other uses side left/right, it makes logical - sense to just map these. The way we'll do it is we'll check if there is a logical - corresponding mapping, and if so, apply it, but we will *not* break from the loop, - thereby giving the loop a chance to find an exact match later which will take priority. - */ - switch (channelOut) - { - /* Left channels. */ - case MA_CHANNEL_FRONT_LEFT: - case MA_CHANNEL_SIDE_LEFT: - { - switch (channelIn) { - case MA_CHANNEL_FRONT_LEFT: - case MA_CHANNEL_SIDE_LEFT: - { - pShuffleTable[iChannelOut] = (ma_uint8)iChannelIn; - } break; - } - } break; - - /* Right channels. */ - case MA_CHANNEL_FRONT_RIGHT: - case MA_CHANNEL_SIDE_RIGHT: - { - switch (channelIn) { - case MA_CHANNEL_FRONT_RIGHT: - case MA_CHANNEL_SIDE_RIGHT: - { - pShuffleTable[iChannelOut] = (ma_uint8)iChannelIn; - } break; - } - } break; - - default: break; - } - } - } - - return MA_SUCCESS; -} - - -static void ma_channel_map_apply_shuffle_table_u8(ma_uint8* pFramesOut, ma_uint32 channelsOut, const ma_uint8* pFramesIn, ma_uint32 channelsIn, ma_uint64 frameCount, const ma_uint8* pShuffleTable) -{ - ma_uint64 iFrame; - ma_uint32 iChannelOut; - - for (iFrame = 0; iFrame < frameCount; iFrame += 1) { - for (iChannelOut = 0; iChannelOut < channelsOut; iChannelOut += 1) { - ma_uint8 iChannelIn = pShuffleTable[iChannelOut]; - if (iChannelIn < channelsIn) { /* For safety, and to deal with MA_CHANNEL_INDEX_NULL. */ - pFramesOut[iChannelOut] = pFramesIn[iChannelIn]; - } else { - pFramesOut[iChannelOut] = 0; - } - } - - pFramesOut += channelsOut; - pFramesIn += channelsIn; - } -} - -static void ma_channel_map_apply_shuffle_table_s16(ma_int16* pFramesOut, ma_uint32 channelsOut, const ma_int16* pFramesIn, ma_uint32 channelsIn, ma_uint64 frameCount, const ma_uint8* pShuffleTable) -{ - ma_uint64 iFrame; - ma_uint32 iChannelOut; - - for (iFrame = 0; iFrame < frameCount; iFrame += 1) { - for (iChannelOut = 0; iChannelOut < channelsOut; iChannelOut += 1) { - ma_uint8 iChannelIn = pShuffleTable[iChannelOut]; - if (iChannelIn < channelsIn) { /* For safety, and to deal with MA_CHANNEL_INDEX_NULL. */ - pFramesOut[iChannelOut] = pFramesIn[iChannelIn]; - } else { - pFramesOut[iChannelOut] = 0; - } - } - - pFramesOut += channelsOut; - pFramesIn += channelsIn; - } -} - -static void ma_channel_map_apply_shuffle_table_s24(ma_uint8* pFramesOut, ma_uint32 channelsOut, const ma_uint8* pFramesIn, ma_uint32 channelsIn, ma_uint64 frameCount, const ma_uint8* pShuffleTable) -{ - ma_uint64 iFrame; - ma_uint32 iChannelOut; - - for (iFrame = 0; iFrame < frameCount; iFrame += 1) { - for (iChannelOut = 0; iChannelOut < channelsOut; iChannelOut += 1) { - ma_uint8 iChannelIn = pShuffleTable[iChannelOut]; - if (iChannelIn < channelsIn) { /* For safety, and to deal with MA_CHANNEL_INDEX_NULL. */ - pFramesOut[iChannelOut*3 + 0] = pFramesIn[iChannelIn*3 + 0]; - pFramesOut[iChannelOut*3 + 1] = pFramesIn[iChannelIn*3 + 1]; - pFramesOut[iChannelOut*3 + 2] = pFramesIn[iChannelIn*3 + 2]; - } else { - pFramesOut[iChannelOut*3 + 0] = 0; - } pFramesOut[iChannelOut*3 + 1] = 0; - } pFramesOut[iChannelOut*3 + 2] = 0; - - pFramesOut += channelsOut*3; - pFramesIn += channelsIn*3; - } -} - -static void ma_channel_map_apply_shuffle_table_s32(ma_int32* pFramesOut, ma_uint32 channelsOut, const ma_int32* pFramesIn, ma_uint32 channelsIn, ma_uint64 frameCount, const ma_uint8* pShuffleTable) -{ - ma_uint64 iFrame; - ma_uint32 iChannelOut; - - for (iFrame = 0; iFrame < frameCount; iFrame += 1) { - for (iChannelOut = 0; iChannelOut < channelsOut; iChannelOut += 1) { - ma_uint8 iChannelIn = pShuffleTable[iChannelOut]; - if (iChannelIn < channelsIn) { /* For safety, and to deal with MA_CHANNEL_INDEX_NULL. */ - pFramesOut[iChannelOut] = pFramesIn[iChannelIn]; - } else { - pFramesOut[iChannelOut] = 0; - } - } - - pFramesOut += channelsOut; - pFramesIn += channelsIn; - } -} - -static void ma_channel_map_apply_shuffle_table_f32(float* pFramesOut, ma_uint32 channelsOut, const float* pFramesIn, ma_uint32 channelsIn, ma_uint64 frameCount, const ma_uint8* pShuffleTable) -{ - ma_uint64 iFrame; - ma_uint32 iChannelOut; - - for (iFrame = 0; iFrame < frameCount; iFrame += 1) { - for (iChannelOut = 0; iChannelOut < channelsOut; iChannelOut += 1) { - ma_uint8 iChannelIn = pShuffleTable[iChannelOut]; - if (iChannelIn < channelsIn) { /* For safety, and to deal with MA_CHANNEL_INDEX_NULL. */ - pFramesOut[iChannelOut] = pFramesIn[iChannelIn]; - } else { - pFramesOut[iChannelOut] = 0; - } - } - - pFramesOut += channelsOut; - pFramesIn += channelsIn; - } -} - -static ma_result ma_channel_map_apply_shuffle_table(void* pFramesOut, ma_uint32 channelsOut, const void* pFramesIn, ma_uint32 channelsIn, ma_uint64 frameCount, const ma_uint8* pShuffleTable, ma_format format) -{ - if (pFramesOut == NULL || pFramesIn == NULL || channelsOut == 0 || pShuffleTable == NULL) { - return MA_INVALID_ARGS; - } - - switch (format) - { - case ma_format_u8: - { - ma_channel_map_apply_shuffle_table_u8((ma_uint8*)pFramesOut, channelsOut, (const ma_uint8*)pFramesIn, channelsIn, frameCount, pShuffleTable); - } break; - - case ma_format_s16: - { - ma_channel_map_apply_shuffle_table_s16((ma_int16*)pFramesOut, channelsOut, (const ma_int16*)pFramesIn, channelsIn, frameCount, pShuffleTable); - } break; - - case ma_format_s24: - { - ma_channel_map_apply_shuffle_table_s24((ma_uint8*)pFramesOut, channelsOut, (const ma_uint8*)pFramesIn, channelsIn, frameCount, pShuffleTable); - } break; - - case ma_format_s32: - { - ma_channel_map_apply_shuffle_table_s32((ma_int32*)pFramesOut, channelsOut, (const ma_int32*)pFramesIn, channelsIn, frameCount, pShuffleTable); - } break; - - case ma_format_f32: - { - ma_channel_map_apply_shuffle_table_f32((float*)pFramesOut, channelsOut, (const float*)pFramesIn, channelsIn, frameCount, pShuffleTable); - } break; - - default: return MA_INVALID_ARGS; /* Unknown format. */ - } - - return MA_SUCCESS; -} - -static ma_result ma_channel_map_apply_mono_out_f32(float* pFramesOut, const float* pFramesIn, const ma_channel* pChannelMapIn, ma_uint32 channelsIn, ma_uint64 frameCount) -{ - ma_uint64 iFrame; - ma_uint32 iChannelIn; - ma_uint32 accumulationCount; - - if (pFramesOut == NULL || pFramesIn == NULL || channelsIn == 0) { - return MA_INVALID_ARGS; - } - - /* In this case the output stream needs to be the average of all channels, ignoring NONE. */ - - /* A quick pre-processing step to get the accumulation counter since we're ignoring NONE channels. */ - accumulationCount = 0; - for (iChannelIn = 0; iChannelIn < channelsIn; iChannelIn += 1) { - if (ma_channel_map_get_channel(pChannelMapIn, channelsIn, iChannelIn) != MA_CHANNEL_NONE) { - accumulationCount += 1; - } - } - - if (accumulationCount > 0) { /* <-- Prevent a division by zero. */ - for (iFrame = 0; iFrame < frameCount; iFrame += 1) { - float accumulation = 0; - - for (iChannelIn = 0; iChannelIn < channelsIn; iChannelIn += 1) { - ma_channel channelIn = ma_channel_map_get_channel(pChannelMapIn, channelsIn, iChannelIn); - if (channelIn != MA_CHANNEL_NONE) { - accumulation += pFramesIn[iChannelIn]; - } - } - - pFramesOut[0] = accumulation / accumulationCount; - pFramesOut += 1; - pFramesIn += channelsIn; - } - } else { - ma_silence_pcm_frames(pFramesOut, frameCount, ma_format_f32, 1); - } - - return MA_SUCCESS; -} - -static ma_result ma_channel_map_apply_mono_in_f32(float* MA_RESTRICT pFramesOut, const ma_channel* pChannelMapOut, ma_uint32 channelsOut, const float* MA_RESTRICT pFramesIn, ma_uint64 frameCount, ma_mono_expansion_mode monoExpansionMode) -{ - ma_uint64 iFrame; - ma_uint32 iChannelOut; - - if (pFramesOut == NULL || channelsOut == 0 || pFramesIn == NULL) { - return MA_INVALID_ARGS; - } - - /* Note that the MA_CHANNEL_NONE channel must be ignored in all cases. */ - switch (monoExpansionMode) - { - case ma_mono_expansion_mode_average: - { - float weight; - ma_uint32 validChannelCount = 0; - - for (iChannelOut = 0; iChannelOut < channelsOut; iChannelOut += 1) { - ma_channel channelOut = ma_channel_map_get_channel(pChannelMapOut, channelsOut, iChannelOut); - if (channelOut != MA_CHANNEL_NONE) { - validChannelCount += 1; - } - } - - weight = 1.0f / validChannelCount; - - for (iFrame = 0; iFrame < frameCount; iFrame += 1) { - for (iChannelOut = 0; iChannelOut < channelsOut; iChannelOut += 1) { - ma_channel channelOut = ma_channel_map_get_channel(pChannelMapOut, channelsOut, iChannelOut); - if (channelOut != MA_CHANNEL_NONE) { - pFramesOut[iChannelOut] = pFramesIn[0] * weight; - } - } - - pFramesOut += channelsOut; - pFramesIn += 1; - } - } break; - - case ma_mono_expansion_mode_stereo_only: - { - if (channelsOut >= 2) { - ma_uint32 iChannelLeft = (ma_uint32)-1; - ma_uint32 iChannelRight = (ma_uint32)-1; - - /* - We first need to find our stereo channels. We prefer front-left and front-right, but - if they're not available, we'll also try side-left and side-right. If neither are - available we'll fall through to the default case below. - */ - for (iChannelOut = 0; iChannelOut < channelsOut; iChannelOut += 1) { - ma_channel channelOut = ma_channel_map_get_channel(pChannelMapOut, channelsOut, iChannelOut); - if (channelOut == MA_CHANNEL_SIDE_LEFT) { - iChannelLeft = iChannelOut; - } - if (channelOut == MA_CHANNEL_SIDE_RIGHT) { - iChannelRight = iChannelOut; - } - } - - for (iChannelOut = 0; iChannelOut < channelsOut; iChannelOut += 1) { - ma_channel channelOut = ma_channel_map_get_channel(pChannelMapOut, channelsOut, iChannelOut); - if (channelOut == MA_CHANNEL_FRONT_LEFT) { - iChannelLeft = iChannelOut; - } - if (channelOut == MA_CHANNEL_FRONT_RIGHT) { - iChannelRight = iChannelOut; - } - } - - - if (iChannelLeft != (ma_uint32)-1 && iChannelRight != (ma_uint32)-1) { - /* We found our stereo channels so we can duplicate the signal across those channels. */ - for (iFrame = 0; iFrame < frameCount; iFrame += 1) { - for (iChannelOut = 0; iChannelOut < channelsOut; iChannelOut += 1) { - ma_channel channelOut = ma_channel_map_get_channel(pChannelMapOut, channelsOut, iChannelOut); - if (channelOut != MA_CHANNEL_NONE) { - if (iChannelOut == iChannelLeft || iChannelOut == iChannelRight) { - pFramesOut[iChannelOut] = pFramesIn[0]; - } else { - pFramesOut[iChannelOut] = 0.0f; - } - } - } - - pFramesOut += channelsOut; - pFramesIn += 1; - } - - break; /* Get out of the switch. */ - } else { - /* Fallthrough. Does not have left and right channels. */ - goto default_handler; - } - } else { - /* Fallthrough. Does not have stereo channels. */ - goto default_handler; - } - }; /* Fallthrough. See comments above. */ - - case ma_mono_expansion_mode_duplicate: - default: - { - default_handler: - { - if (channelsOut <= MA_MAX_CHANNELS) { - ma_bool32 hasEmptyChannel = MA_FALSE; - ma_channel channelPositions[MA_MAX_CHANNELS]; - for (iChannelOut = 0; iChannelOut < channelsOut; iChannelOut += 1) { - channelPositions[iChannelOut] = ma_channel_map_get_channel(pChannelMapOut, channelsOut, iChannelOut); - if (channelPositions[iChannelOut] == MA_CHANNEL_NONE) { - hasEmptyChannel = MA_TRUE; - } - } - - if (hasEmptyChannel == MA_FALSE) { - /* - Faster path when there's no MA_CHANNEL_NONE channel positions. This should hopefully - help the compiler with auto-vectorization.m - */ - if (channelsOut == 2) { - #if defined(MA_SUPPORT_SSE2) - if (ma_has_sse2()) { - /* We want to do two frames in each iteration. */ - ma_uint64 unrolledFrameCount = frameCount >> 1; - - for (iFrame = 0; iFrame < unrolledFrameCount; iFrame += 1) { - __m128 in0 = _mm_set1_ps(pFramesIn[iFrame*2 + 0]); - __m128 in1 = _mm_set1_ps(pFramesIn[iFrame*2 + 1]); - _mm_storeu_ps(&pFramesOut[iFrame*4 + 0], _mm_shuffle_ps(in0, in1, _MM_SHUFFLE(0, 0, 0, 0))); - } - - /* Tail. */ - iFrame = unrolledFrameCount << 1; - goto generic_on_fastpath; - } else - #endif - { - for (iFrame = 0; iFrame < frameCount; iFrame += 1) { - for (iChannelOut = 0; iChannelOut < 2; iChannelOut += 1) { - pFramesOut[iFrame*2 + iChannelOut] = pFramesIn[iFrame]; - } - } - } - } else if (channelsOut == 6) { - #if defined(MA_SUPPORT_SSE2) - if (ma_has_sse2()) { - /* We want to do two frames in each iteration so we can have a multiple of 4 samples. */ - ma_uint64 unrolledFrameCount = frameCount >> 1; - - for (iFrame = 0; iFrame < unrolledFrameCount; iFrame += 1) { - __m128 in0 = _mm_set1_ps(pFramesIn[iFrame*2 + 0]); - __m128 in1 = _mm_set1_ps(pFramesIn[iFrame*2 + 1]); - - _mm_storeu_ps(&pFramesOut[iFrame*12 + 0], in0); - _mm_storeu_ps(&pFramesOut[iFrame*12 + 4], _mm_shuffle_ps(in0, in1, _MM_SHUFFLE(0, 0, 0, 0))); - _mm_storeu_ps(&pFramesOut[iFrame*12 + 8], in1); - } - - /* Tail. */ - iFrame = unrolledFrameCount << 1; - goto generic_on_fastpath; - } else - #endif - { - for (iFrame = 0; iFrame < frameCount; iFrame += 1) { - for (iChannelOut = 0; iChannelOut < 6; iChannelOut += 1) { - pFramesOut[iFrame*6 + iChannelOut] = pFramesIn[iFrame]; - } - } - } - } else if (channelsOut == 8) { - #if defined(MA_SUPPORT_SSE2) - if (ma_has_sse2()) { - for (iFrame = 0; iFrame < frameCount; iFrame += 1) { - __m128 in = _mm_set1_ps(pFramesIn[iFrame]); - _mm_storeu_ps(&pFramesOut[iFrame*8 + 0], in); - _mm_storeu_ps(&pFramesOut[iFrame*8 + 4], in); - } - } else - #endif - { - for (iFrame = 0; iFrame < frameCount; iFrame += 1) { - for (iChannelOut = 0; iChannelOut < 8; iChannelOut += 1) { - pFramesOut[iFrame*8 + iChannelOut] = pFramesIn[iFrame]; - } - } - } - } else { - iFrame = 0; - - #if defined(MA_SUPPORT_SSE2) /* For silencing a warning with non-x86 builds. */ - generic_on_fastpath: - #endif - { - for (; iFrame < frameCount; iFrame += 1) { - for (iChannelOut = 0; iChannelOut < channelsOut; iChannelOut += 1) { - pFramesOut[iFrame*channelsOut + iChannelOut] = pFramesIn[iFrame]; - } - } - } - } - } else { - /* Slow path. Need to handle MA_CHANNEL_NONE. */ - for (iFrame = 0; iFrame < frameCount; iFrame += 1) { - for (iChannelOut = 0; iChannelOut < channelsOut; iChannelOut += 1) { - if (channelPositions[iChannelOut] != MA_CHANNEL_NONE) { - pFramesOut[iFrame*channelsOut + iChannelOut] = pFramesIn[iFrame]; - } - } - } - } - } else { - /* Slow path. Too many channels to store on the stack. */ - for (iFrame = 0; iFrame < frameCount; iFrame += 1) { - for (iChannelOut = 0; iChannelOut < channelsOut; iChannelOut += 1) { - ma_channel channelOut = ma_channel_map_get_channel(pChannelMapOut, channelsOut, iChannelOut); - if (channelOut != MA_CHANNEL_NONE) { - pFramesOut[iFrame*channelsOut + iChannelOut] = pFramesIn[iFrame]; - } - } - } - } - } - } break; - } - - return MA_SUCCESS; -} - -static void ma_channel_map_apply_f32(float* pFramesOut, const ma_channel* pChannelMapOut, ma_uint32 channelsOut, const float* pFramesIn, const ma_channel* pChannelMapIn, ma_uint32 channelsIn, ma_uint64 frameCount, ma_channel_mix_mode mode, ma_mono_expansion_mode monoExpansionMode) -{ - ma_channel_conversion_path conversionPath = ma_channel_map_get_conversion_path(pChannelMapIn, channelsIn, pChannelMapOut, channelsOut, mode); - - /* Optimized Path: Passthrough */ - if (conversionPath == ma_channel_conversion_path_passthrough) { - ma_copy_pcm_frames(pFramesOut, pFramesIn, frameCount, ma_format_f32, channelsOut); - return; - } - - /* Special Path: Mono Output. */ - if (conversionPath == ma_channel_conversion_path_mono_out) { - ma_channel_map_apply_mono_out_f32(pFramesOut, pFramesIn, pChannelMapIn, channelsIn, frameCount); - return; - } - - /* Special Path: Mono Input. */ - if (conversionPath == ma_channel_conversion_path_mono_in) { - ma_channel_map_apply_mono_in_f32(pFramesOut, pChannelMapOut, channelsOut, pFramesIn, frameCount, monoExpansionMode); - return; - } - - /* Getting here means we aren't running on an optimized conversion path. */ - if (channelsOut <= MA_MAX_CHANNELS) { - ma_result result; - - if (mode == ma_channel_mix_mode_simple) { - ma_channel shuffleTable[MA_MAX_CHANNELS]; - - result = ma_channel_map_build_shuffle_table(pChannelMapIn, channelsIn, pChannelMapOut, channelsOut, shuffleTable); - if (result != MA_SUCCESS) { - return; - } - - result = ma_channel_map_apply_shuffle_table(pFramesOut, channelsOut, pFramesIn, channelsIn, frameCount, shuffleTable, ma_format_f32); - if (result != MA_SUCCESS) { - return; - } - } else { - ma_uint32 iFrame; - ma_uint32 iChannelOut; - ma_uint32 iChannelIn; - float weights[32][32]; /* Do not use MA_MAX_CHANNELS here! */ - - /* - If we have a small enough number of channels, pre-compute the weights. Otherwise we'll just need to - fall back to a slower path because otherwise we'll run out of stack space. - */ - if (channelsIn <= ma_countof(weights) && channelsOut <= ma_countof(weights)) { - /* Pre-compute weights. */ - for (iChannelOut = 0; iChannelOut < channelsOut; iChannelOut += 1) { - ma_channel channelOut = ma_channel_map_get_channel(pChannelMapOut, channelsOut, iChannelOut); - for (iChannelIn = 0; iChannelIn < channelsIn; iChannelIn += 1) { - ma_channel channelIn = ma_channel_map_get_channel(pChannelMapIn, channelsIn, iChannelIn); - weights[iChannelOut][iChannelIn] = ma_calculate_channel_position_rectangular_weight(channelOut, channelIn); - } - } - - iFrame = 0; - - /* Experiment: Try an optimized unroll for some specific cases to see how it improves performance. RESULT: Good gains. */ - if (channelsOut == 8) { - /* Experiment 2: Expand the inner loop to see what kind of different it makes. RESULT: Small, but worthwhile gain. */ - if (channelsIn == 2) { - for (; iFrame < frameCount; iFrame += 1) { - float accumulation[8] = { 0, 0, 0, 0, 0, 0, 0, 0 }; - - accumulation[0] += pFramesIn[iFrame*2 + 0] * weights[0][0]; - accumulation[1] += pFramesIn[iFrame*2 + 0] * weights[1][0]; - accumulation[2] += pFramesIn[iFrame*2 + 0] * weights[2][0]; - accumulation[3] += pFramesIn[iFrame*2 + 0] * weights[3][0]; - accumulation[4] += pFramesIn[iFrame*2 + 0] * weights[4][0]; - accumulation[5] += pFramesIn[iFrame*2 + 0] * weights[5][0]; - accumulation[6] += pFramesIn[iFrame*2 + 0] * weights[6][0]; - accumulation[7] += pFramesIn[iFrame*2 + 0] * weights[7][0]; - - accumulation[0] += pFramesIn[iFrame*2 + 1] * weights[0][1]; - accumulation[1] += pFramesIn[iFrame*2 + 1] * weights[1][1]; - accumulation[2] += pFramesIn[iFrame*2 + 1] * weights[2][1]; - accumulation[3] += pFramesIn[iFrame*2 + 1] * weights[3][1]; - accumulation[4] += pFramesIn[iFrame*2 + 1] * weights[4][1]; - accumulation[5] += pFramesIn[iFrame*2 + 1] * weights[5][1]; - accumulation[6] += pFramesIn[iFrame*2 + 1] * weights[6][1]; - accumulation[7] += pFramesIn[iFrame*2 + 1] * weights[7][1]; - - pFramesOut[iFrame*8 + 0] = accumulation[0]; - pFramesOut[iFrame*8 + 1] = accumulation[1]; - pFramesOut[iFrame*8 + 2] = accumulation[2]; - pFramesOut[iFrame*8 + 3] = accumulation[3]; - pFramesOut[iFrame*8 + 4] = accumulation[4]; - pFramesOut[iFrame*8 + 5] = accumulation[5]; - pFramesOut[iFrame*8 + 6] = accumulation[6]; - pFramesOut[iFrame*8 + 7] = accumulation[7]; - } - } else { - /* When outputting to 8 channels, we can do everything in groups of two 4x SIMD operations. */ - for (; iFrame < frameCount; iFrame += 1) { - float accumulation[8] = { 0, 0, 0, 0, 0, 0, 0, 0 }; - - for (iChannelIn = 0; iChannelIn < channelsIn; iChannelIn += 1) { - accumulation[0] += pFramesIn[iFrame*channelsIn + iChannelIn] * weights[0][iChannelIn]; - accumulation[1] += pFramesIn[iFrame*channelsIn + iChannelIn] * weights[1][iChannelIn]; - accumulation[2] += pFramesIn[iFrame*channelsIn + iChannelIn] * weights[2][iChannelIn]; - accumulation[3] += pFramesIn[iFrame*channelsIn + iChannelIn] * weights[3][iChannelIn]; - accumulation[4] += pFramesIn[iFrame*channelsIn + iChannelIn] * weights[4][iChannelIn]; - accumulation[5] += pFramesIn[iFrame*channelsIn + iChannelIn] * weights[5][iChannelIn]; - accumulation[6] += pFramesIn[iFrame*channelsIn + iChannelIn] * weights[6][iChannelIn]; - accumulation[7] += pFramesIn[iFrame*channelsIn + iChannelIn] * weights[7][iChannelIn]; - } - - pFramesOut[iFrame*8 + 0] = accumulation[0]; - pFramesOut[iFrame*8 + 1] = accumulation[1]; - pFramesOut[iFrame*8 + 2] = accumulation[2]; - pFramesOut[iFrame*8 + 3] = accumulation[3]; - pFramesOut[iFrame*8 + 4] = accumulation[4]; - pFramesOut[iFrame*8 + 5] = accumulation[5]; - pFramesOut[iFrame*8 + 6] = accumulation[6]; - pFramesOut[iFrame*8 + 7] = accumulation[7]; - } - } - } else if (channelsOut == 6) { - /* - When outputting to 6 channels we unfortunately don't have a nice multiple of 4 to do 4x SIMD operations. Instead we'll - expand our weights and do two frames at a time. - */ - for (; iFrame < frameCount; iFrame += 1) { - float accumulation[12] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; - - for (iChannelIn = 0; iChannelIn < channelsIn; iChannelIn += 1) { - accumulation[0] += pFramesIn[iFrame*channelsIn + iChannelIn] * weights[0][iChannelIn]; - accumulation[1] += pFramesIn[iFrame*channelsIn + iChannelIn] * weights[1][iChannelIn]; - accumulation[2] += pFramesIn[iFrame*channelsIn + iChannelIn] * weights[2][iChannelIn]; - accumulation[3] += pFramesIn[iFrame*channelsIn + iChannelIn] * weights[3][iChannelIn]; - accumulation[4] += pFramesIn[iFrame*channelsIn + iChannelIn] * weights[4][iChannelIn]; - accumulation[5] += pFramesIn[iFrame*channelsIn + iChannelIn] * weights[5][iChannelIn]; - } - - pFramesOut[iFrame*6 + 0] = accumulation[0]; - pFramesOut[iFrame*6 + 1] = accumulation[1]; - pFramesOut[iFrame*6 + 2] = accumulation[2]; - pFramesOut[iFrame*6 + 3] = accumulation[3]; - pFramesOut[iFrame*6 + 4] = accumulation[4]; - pFramesOut[iFrame*6 + 5] = accumulation[5]; - } - } - - /* Leftover frames. */ - for (; iFrame < frameCount; iFrame += 1) { - for (iChannelOut = 0; iChannelOut < channelsOut; iChannelOut += 1) { - float accumulation = 0; - - for (iChannelIn = 0; iChannelIn < channelsIn; iChannelIn += 1) { - accumulation += pFramesIn[iFrame*channelsIn + iChannelIn] * weights[iChannelOut][iChannelIn]; - } - - pFramesOut[iFrame*channelsOut + iChannelOut] = accumulation; - } - } - } else { - /* Cannot pre-compute weights because not enough room in stack-allocated buffer. */ - for (iFrame = 0; iFrame < frameCount; iFrame += 1) { - for (iChannelOut = 0; iChannelOut < channelsOut; iChannelOut += 1) { - float accumulation = 0; - ma_channel channelOut = ma_channel_map_get_channel(pChannelMapOut, channelsOut, iChannelOut); - - for (iChannelIn = 0; iChannelIn < channelsIn; iChannelIn += 1) { - ma_channel channelIn = ma_channel_map_get_channel(pChannelMapIn, channelsIn, iChannelIn); - accumulation += pFramesIn[iFrame*channelsIn + iChannelIn] * ma_calculate_channel_position_rectangular_weight(channelOut, channelIn); - } - - pFramesOut[iFrame*channelsOut + iChannelOut] = accumulation; - } - } - } - } - } else { - /* Fall back to silence. If you hit this, what are you doing with so many channels?! */ - ma_silence_pcm_frames(pFramesOut, frameCount, ma_format_f32, channelsOut); - } -} - - -typedef struct -{ - size_t sizeInBytes; - size_t channelMapInOffset; - size_t channelMapOutOffset; - size_t shuffleTableOffset; - size_t weightsOffset; -} ma_channel_converter_heap_layout; - -static ma_channel_conversion_path ma_channel_converter_config_get_conversion_path(const ma_channel_converter_config* pConfig) -{ - return ma_channel_map_get_conversion_path(pConfig->pChannelMapIn, pConfig->channelsIn, pConfig->pChannelMapOut, pConfig->channelsOut, pConfig->mixingMode); -} - -static ma_result ma_channel_converter_get_heap_layout(const ma_channel_converter_config* pConfig, ma_channel_converter_heap_layout* pHeapLayout) -{ - ma_channel_conversion_path conversionPath; - - MA_ASSERT(pHeapLayout != NULL); - - if (pConfig == NULL) { - return MA_INVALID_ARGS; - } - - if (pConfig->channelsIn == 0 || pConfig->channelsOut == 0) { - return MA_INVALID_ARGS; - } - - if (!ma_channel_map_is_valid(pConfig->pChannelMapIn, pConfig->channelsIn)) { - return MA_INVALID_ARGS; - } - - if (!ma_channel_map_is_valid(pConfig->pChannelMapOut, pConfig->channelsOut)) { - return MA_INVALID_ARGS; - } - - pHeapLayout->sizeInBytes = 0; - - /* Input channel map. Only need to allocate this if we have an input channel map (otherwise default channel map is assumed). */ - pHeapLayout->channelMapInOffset = pHeapLayout->sizeInBytes; - if (pConfig->pChannelMapIn != NULL) { - pHeapLayout->sizeInBytes += sizeof(ma_channel) * pConfig->channelsIn; - } - - /* Output channel map. Only need to allocate this if we have an output channel map (otherwise default channel map is assumed). */ - pHeapLayout->channelMapOutOffset = pHeapLayout->sizeInBytes; - if (pConfig->pChannelMapOut != NULL) { - pHeapLayout->sizeInBytes += sizeof(ma_channel) * pConfig->channelsOut; - } - - /* Alignment for the next section. */ - pHeapLayout->sizeInBytes = ma_align_64(pHeapLayout->sizeInBytes); - - /* Whether or not we use weights of a shuffle table depends on the channel map themselves and the algorithm we've chosen. */ - conversionPath = ma_channel_converter_config_get_conversion_path(pConfig); - - /* Shuffle table */ - pHeapLayout->shuffleTableOffset = pHeapLayout->sizeInBytes; - if (conversionPath == ma_channel_conversion_path_shuffle) { - pHeapLayout->sizeInBytes += sizeof(ma_uint8) * pConfig->channelsOut; - } - - /* Weights */ - pHeapLayout->weightsOffset = pHeapLayout->sizeInBytes; - if (conversionPath == ma_channel_conversion_path_weights) { - pHeapLayout->sizeInBytes += sizeof(float*) * pConfig->channelsIn; - pHeapLayout->sizeInBytes += sizeof(float ) * pConfig->channelsIn * pConfig->channelsOut; - } - - /* Make sure allocation size is aligned. */ - pHeapLayout->sizeInBytes = ma_align_64(pHeapLayout->sizeInBytes); - - return MA_SUCCESS; -} - -MA_API ma_result ma_channel_converter_get_heap_size(const ma_channel_converter_config* pConfig, size_t* pHeapSizeInBytes) -{ - ma_result result; - ma_channel_converter_heap_layout heapLayout; - - if (pHeapSizeInBytes == NULL) { - return MA_INVALID_ARGS; - } - - *pHeapSizeInBytes = 0; - - result = ma_channel_converter_get_heap_layout(pConfig, &heapLayout); - if (result != MA_SUCCESS) { - return result; - } - - *pHeapSizeInBytes = heapLayout.sizeInBytes; - - return MA_SUCCESS; -} - -MA_API ma_result ma_channel_converter_init_preallocated(const ma_channel_converter_config* pConfig, void* pHeap, ma_channel_converter* pConverter) -{ - ma_result result; - ma_channel_converter_heap_layout heapLayout; - - if (pConverter == NULL) { - return MA_INVALID_ARGS; - } - - MA_ZERO_OBJECT(pConverter); - - result = ma_channel_converter_get_heap_layout(pConfig, &heapLayout); - if (result != MA_SUCCESS) { - return result; - } - - pConverter->_pHeap = pHeap; - MA_ZERO_MEMORY(pConverter->_pHeap, heapLayout.sizeInBytes); - - pConverter->format = pConfig->format; - pConverter->channelsIn = pConfig->channelsIn; - pConverter->channelsOut = pConfig->channelsOut; - pConverter->mixingMode = pConfig->mixingMode; - - if (pConfig->pChannelMapIn != NULL) { - pConverter->pChannelMapIn = (ma_channel*)ma_offset_ptr(pHeap, heapLayout.channelMapInOffset); - ma_channel_map_copy_or_default(pConverter->pChannelMapIn, pConfig->channelsIn, pConfig->pChannelMapIn, pConfig->channelsIn); - } else { - pConverter->pChannelMapIn = NULL; /* Use default channel map. */ - } - - if (pConfig->pChannelMapOut != NULL) { - pConverter->pChannelMapOut = (ma_channel*)ma_offset_ptr(pHeap, heapLayout.channelMapOutOffset); - ma_channel_map_copy_or_default(pConverter->pChannelMapOut, pConfig->channelsOut, pConfig->pChannelMapOut, pConfig->channelsOut); - } else { - pConverter->pChannelMapOut = NULL; /* Use default channel map. */ - } - - pConverter->conversionPath = ma_channel_converter_config_get_conversion_path(pConfig); - - if (pConverter->conversionPath == ma_channel_conversion_path_shuffle) { - pConverter->pShuffleTable = (ma_uint8*)ma_offset_ptr(pHeap, heapLayout.shuffleTableOffset); - ma_channel_map_build_shuffle_table(pConverter->pChannelMapIn, pConverter->channelsIn, pConverter->pChannelMapOut, pConverter->channelsOut, pConverter->pShuffleTable); - } - - if (pConverter->conversionPath == ma_channel_conversion_path_weights) { - ma_uint32 iChannelIn; - ma_uint32 iChannelOut; - - if (pConverter->format == ma_format_f32) { - pConverter->weights.f32 = (float** )ma_offset_ptr(pHeap, heapLayout.weightsOffset); - for (iChannelIn = 0; iChannelIn < pConverter->channelsIn; iChannelIn += 1) { - pConverter->weights.f32[iChannelIn] = (float*)ma_offset_ptr(pHeap, heapLayout.weightsOffset + ((sizeof(float*) * pConverter->channelsIn) + (sizeof(float) * pConverter->channelsOut * iChannelIn))); - } - } else { - pConverter->weights.s16 = (ma_int32**)ma_offset_ptr(pHeap, heapLayout.weightsOffset); - for (iChannelIn = 0; iChannelIn < pConverter->channelsIn; iChannelIn += 1) { - pConverter->weights.s16[iChannelIn] = (ma_int32*)ma_offset_ptr(pHeap, heapLayout.weightsOffset + ((sizeof(ma_int32*) * pConverter->channelsIn) + (sizeof(ma_int32) * pConverter->channelsOut * iChannelIn))); - } - } - - /* Silence our weights by default. */ - for (iChannelIn = 0; iChannelIn < pConverter->channelsIn; iChannelIn += 1) { - for (iChannelOut = 0; iChannelOut < pConverter->channelsOut; iChannelOut += 1) { - if (pConverter->format == ma_format_f32) { - pConverter->weights.f32[iChannelIn][iChannelOut] = 0.0f; - } else { - pConverter->weights.s16[iChannelIn][iChannelOut] = 0; - } - } - } - - /* - We now need to fill out our weights table. This is determined by the mixing mode. - */ - - /* In all cases we need to make sure all channels that are present in both channel maps have a 1:1 mapping. */ - for (iChannelIn = 0; iChannelIn < pConverter->channelsIn; ++iChannelIn) { - ma_channel channelPosIn = ma_channel_map_get_channel(pConverter->pChannelMapIn, pConverter->channelsIn, iChannelIn); - - for (iChannelOut = 0; iChannelOut < pConverter->channelsOut; ++iChannelOut) { - ma_channel channelPosOut = ma_channel_map_get_channel(pConverter->pChannelMapOut, pConverter->channelsOut, iChannelOut); - - if (channelPosIn == channelPosOut) { - float weight = 1; - - if (pConverter->format == ma_format_f32) { - pConverter->weights.f32[iChannelIn][iChannelOut] = weight; - } else { - pConverter->weights.s16[iChannelIn][iChannelOut] = ma_channel_converter_float_to_fixed(weight); - } - } - } - } - - switch (pConverter->mixingMode) - { - case ma_channel_mix_mode_custom_weights: - { - if (pConfig->ppWeights == NULL) { - return MA_INVALID_ARGS; /* Config specified a custom weights mixing mode, but no custom weights have been specified. */ - } - - for (iChannelIn = 0; iChannelIn < pConverter->channelsIn; iChannelIn += 1) { - for (iChannelOut = 0; iChannelOut < pConverter->channelsOut; iChannelOut += 1) { - float weight = pConfig->ppWeights[iChannelIn][iChannelOut]; - - if (pConverter->format == ma_format_f32) { - pConverter->weights.f32[iChannelIn][iChannelOut] = weight; - } else { - pConverter->weights.s16[iChannelIn][iChannelOut] = ma_channel_converter_float_to_fixed(weight); - } - } - } - } break; - - case ma_channel_mix_mode_simple: - { - /* - In simple mode, only set weights for channels that have exactly matching types, leave the rest at - zero. The 1:1 mappings have already been covered before this switch statement. - */ - } break; - - case ma_channel_mix_mode_rectangular: - default: - { - /* Unmapped input channels. */ - for (iChannelIn = 0; iChannelIn < pConverter->channelsIn; ++iChannelIn) { - ma_channel channelPosIn = ma_channel_map_get_channel(pConverter->pChannelMapIn, pConverter->channelsIn, iChannelIn); - - if (ma_is_spatial_channel_position(channelPosIn)) { - if (!ma_channel_map_contains_channel_position(pConverter->channelsOut, pConverter->pChannelMapOut, channelPosIn)) { - for (iChannelOut = 0; iChannelOut < pConverter->channelsOut; ++iChannelOut) { - ma_channel channelPosOut = ma_channel_map_get_channel(pConverter->pChannelMapOut, pConverter->channelsOut, iChannelOut); - - if (ma_is_spatial_channel_position(channelPosOut)) { - float weight = 0; - if (pConverter->mixingMode == ma_channel_mix_mode_rectangular) { - weight = ma_calculate_channel_position_rectangular_weight(channelPosIn, channelPosOut); - } - - /* Only apply the weight if we haven't already got some contribution from the respective channels. */ - if (pConverter->format == ma_format_f32) { - if (pConverter->weights.f32[iChannelIn][iChannelOut] == 0) { - pConverter->weights.f32[iChannelIn][iChannelOut] = weight; - } - } else { - if (pConverter->weights.s16[iChannelIn][iChannelOut] == 0) { - pConverter->weights.s16[iChannelIn][iChannelOut] = ma_channel_converter_float_to_fixed(weight); - } - } - } - } - } - } - } - - /* Unmapped output channels. */ - for (iChannelOut = 0; iChannelOut < pConverter->channelsOut; ++iChannelOut) { - ma_channel channelPosOut = ma_channel_map_get_channel(pConverter->pChannelMapOut, pConverter->channelsOut, iChannelOut); - - if (ma_is_spatial_channel_position(channelPosOut)) { - if (!ma_channel_map_contains_channel_position(pConverter->channelsIn, pConverter->pChannelMapIn, channelPosOut)) { - for (iChannelIn = 0; iChannelIn < pConverter->channelsIn; ++iChannelIn) { - ma_channel channelPosIn = ma_channel_map_get_channel(pConverter->pChannelMapIn, pConverter->channelsIn, iChannelIn); - - if (ma_is_spatial_channel_position(channelPosIn)) { - float weight = 0; - if (pConverter->mixingMode == ma_channel_mix_mode_rectangular) { - weight = ma_calculate_channel_position_rectangular_weight(channelPosIn, channelPosOut); - } - - /* Only apply the weight if we haven't already got some contribution from the respective channels. */ - if (pConverter->format == ma_format_f32) { - if (pConverter->weights.f32[iChannelIn][iChannelOut] == 0) { - pConverter->weights.f32[iChannelIn][iChannelOut] = weight; - } - } else { - if (pConverter->weights.s16[iChannelIn][iChannelOut] == 0) { - pConverter->weights.s16[iChannelIn][iChannelOut] = ma_channel_converter_float_to_fixed(weight); - } - } - } - } - } - } - } - - /* If LFE is in the output channel map but was not present in the input channel map, configure its weight now */ - if (pConfig->calculateLFEFromSpatialChannels) { - if (!ma_channel_map_contains_channel_position(pConverter->channelsIn, pConverter->pChannelMapIn, MA_CHANNEL_LFE)) { - ma_uint32 spatialChannelCount = ma_channel_map_get_spatial_channel_count(pConverter->pChannelMapIn, pConverter->channelsIn); - ma_uint32 iChannelOutLFE; - - if (spatialChannelCount > 0 && ma_channel_map_find_channel_position(pConverter->channelsOut, pConverter->pChannelMapOut, MA_CHANNEL_LFE, &iChannelOutLFE)) { - const float weightForLFE = 1.0f / spatialChannelCount; - for (iChannelIn = 0; iChannelIn < pConverter->channelsIn; ++iChannelIn) { - const ma_channel channelPosIn = ma_channel_map_get_channel(pConverter->pChannelMapIn, pConverter->channelsIn, iChannelIn); - if (ma_is_spatial_channel_position(channelPosIn)) { - if (pConverter->format == ma_format_f32) { - if (pConverter->weights.f32[iChannelIn][iChannelOutLFE] == 0) { - pConverter->weights.f32[iChannelIn][iChannelOutLFE] = weightForLFE; - } - } else { - if (pConverter->weights.s16[iChannelIn][iChannelOutLFE] == 0) { - pConverter->weights.s16[iChannelIn][iChannelOutLFE] = ma_channel_converter_float_to_fixed(weightForLFE); - } - } - } - } - } - } - } - } break; - } - } - - return MA_SUCCESS; -} - -MA_API ma_result ma_channel_converter_init(const ma_channel_converter_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_channel_converter* pConverter) -{ - ma_result result; - size_t heapSizeInBytes; - void* pHeap; - - result = ma_channel_converter_get_heap_size(pConfig, &heapSizeInBytes); - if (result != MA_SUCCESS) { - return result; - } - - if (heapSizeInBytes > 0) { - pHeap = ma_malloc(heapSizeInBytes, pAllocationCallbacks); - if (pHeap == NULL) { - return MA_OUT_OF_MEMORY; - } - } else { - pHeap = NULL; - } - - result = ma_channel_converter_init_preallocated(pConfig, pHeap, pConverter); - if (result != MA_SUCCESS) { - ma_free(pHeap, pAllocationCallbacks); - return result; - } - - pConverter->_ownsHeap = MA_TRUE; - return MA_SUCCESS; -} - -MA_API void ma_channel_converter_uninit(ma_channel_converter* pConverter, const ma_allocation_callbacks* pAllocationCallbacks) -{ - if (pConverter == NULL) { - return; - } - - if (pConverter->_ownsHeap) { - ma_free(pConverter->_pHeap, pAllocationCallbacks); - } -} - -static ma_result ma_channel_converter_process_pcm_frames__passthrough(ma_channel_converter* pConverter, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount) -{ - MA_ASSERT(pConverter != NULL); - MA_ASSERT(pFramesOut != NULL); - MA_ASSERT(pFramesIn != NULL); - - ma_copy_memory_64(pFramesOut, pFramesIn, frameCount * ma_get_bytes_per_frame(pConverter->format, pConverter->channelsOut)); - return MA_SUCCESS; -} - -static ma_result ma_channel_converter_process_pcm_frames__shuffle(ma_channel_converter* pConverter, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount) -{ - MA_ASSERT(pConverter != NULL); - MA_ASSERT(pFramesOut != NULL); - MA_ASSERT(pFramesIn != NULL); - MA_ASSERT(pConverter->channelsIn == pConverter->channelsOut); - - return ma_channel_map_apply_shuffle_table(pFramesOut, pConverter->channelsOut, pFramesIn, pConverter->channelsIn, frameCount, pConverter->pShuffleTable, pConverter->format); -} - -static ma_result ma_channel_converter_process_pcm_frames__mono_in(ma_channel_converter* pConverter, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount) -{ - ma_uint64 iFrame; - - MA_ASSERT(pConverter != NULL); - MA_ASSERT(pFramesOut != NULL); - MA_ASSERT(pFramesIn != NULL); - MA_ASSERT(pConverter->channelsIn == 1); - - switch (pConverter->format) - { - case ma_format_u8: - { - /* */ ma_uint8* pFramesOutU8 = ( ma_uint8*)pFramesOut; - const ma_uint8* pFramesInU8 = (const ma_uint8*)pFramesIn; - - for (iFrame = 0; iFrame < frameCount; ++iFrame) { - ma_uint32 iChannel; - for (iChannel = 0; iChannel < pConverter->channelsOut; iChannel += 1) { - pFramesOutU8[iFrame*pConverter->channelsOut + iChannel] = pFramesInU8[iFrame]; - } - } - } break; - - case ma_format_s16: - { - /* */ ma_int16* pFramesOutS16 = ( ma_int16*)pFramesOut; - const ma_int16* pFramesInS16 = (const ma_int16*)pFramesIn; - - if (pConverter->channelsOut == 2) { - for (iFrame = 0; iFrame < frameCount; ++iFrame) { - pFramesOutS16[iFrame*2 + 0] = pFramesInS16[iFrame]; - pFramesOutS16[iFrame*2 + 1] = pFramesInS16[iFrame]; - } - } else { - for (iFrame = 0; iFrame < frameCount; ++iFrame) { - ma_uint32 iChannel; - for (iChannel = 0; iChannel < pConverter->channelsOut; iChannel += 1) { - pFramesOutS16[iFrame*pConverter->channelsOut + iChannel] = pFramesInS16[iFrame]; - } - } - } - } break; - - case ma_format_s24: - { - /* */ ma_uint8* pFramesOutS24 = ( ma_uint8*)pFramesOut; - const ma_uint8* pFramesInS24 = (const ma_uint8*)pFramesIn; - - for (iFrame = 0; iFrame < frameCount; ++iFrame) { - ma_uint32 iChannel; - for (iChannel = 0; iChannel < pConverter->channelsOut; iChannel += 1) { - ma_uint64 iSampleOut = iFrame*pConverter->channelsOut + iChannel; - ma_uint64 iSampleIn = iFrame; - pFramesOutS24[iSampleOut*3 + 0] = pFramesInS24[iSampleIn*3 + 0]; - pFramesOutS24[iSampleOut*3 + 1] = pFramesInS24[iSampleIn*3 + 1]; - pFramesOutS24[iSampleOut*3 + 2] = pFramesInS24[iSampleIn*3 + 2]; - } - } - } break; - - case ma_format_s32: - { - /* */ ma_int32* pFramesOutS32 = ( ma_int32*)pFramesOut; - const ma_int32* pFramesInS32 = (const ma_int32*)pFramesIn; - - for (iFrame = 0; iFrame < frameCount; ++iFrame) { - ma_uint32 iChannel; - for (iChannel = 0; iChannel < pConverter->channelsOut; iChannel += 1) { - pFramesOutS32[iFrame*pConverter->channelsOut + iChannel] = pFramesInS32[iFrame]; - } - } - } break; - - case ma_format_f32: - { - /* */ float* pFramesOutF32 = ( float*)pFramesOut; - const float* pFramesInF32 = (const float*)pFramesIn; - - if (pConverter->channelsOut == 2) { - for (iFrame = 0; iFrame < frameCount; ++iFrame) { - pFramesOutF32[iFrame*2 + 0] = pFramesInF32[iFrame]; - pFramesOutF32[iFrame*2 + 1] = pFramesInF32[iFrame]; - } - } else { - for (iFrame = 0; iFrame < frameCount; ++iFrame) { - ma_uint32 iChannel; - for (iChannel = 0; iChannel < pConverter->channelsOut; iChannel += 1) { - pFramesOutF32[iFrame*pConverter->channelsOut + iChannel] = pFramesInF32[iFrame]; - } - } - } - } break; - - default: return MA_INVALID_OPERATION; /* Unknown format. */ - } - - return MA_SUCCESS; -} - -static ma_result ma_channel_converter_process_pcm_frames__mono_out(ma_channel_converter* pConverter, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount) -{ - ma_uint64 iFrame; - ma_uint32 iChannel; - - MA_ASSERT(pConverter != NULL); - MA_ASSERT(pFramesOut != NULL); - MA_ASSERT(pFramesIn != NULL); - MA_ASSERT(pConverter->channelsOut == 1); - - switch (pConverter->format) - { - case ma_format_u8: - { - /* */ ma_uint8* pFramesOutU8 = ( ma_uint8*)pFramesOut; - const ma_uint8* pFramesInU8 = (const ma_uint8*)pFramesIn; - - for (iFrame = 0; iFrame < frameCount; ++iFrame) { - ma_int32 t = 0; - for (iChannel = 0; iChannel < pConverter->channelsIn; iChannel += 1) { - t += ma_pcm_sample_u8_to_s16_no_scale(pFramesInU8[iFrame*pConverter->channelsIn + iChannel]); - } - - pFramesOutU8[iFrame] = ma_clip_u8(t / pConverter->channelsOut); - } - } break; - - case ma_format_s16: - { - /* */ ma_int16* pFramesOutS16 = ( ma_int16*)pFramesOut; - const ma_int16* pFramesInS16 = (const ma_int16*)pFramesIn; - - for (iFrame = 0; iFrame < frameCount; ++iFrame) { - ma_int32 t = 0; - for (iChannel = 0; iChannel < pConverter->channelsIn; iChannel += 1) { - t += pFramesInS16[iFrame*pConverter->channelsIn + iChannel]; - } - - pFramesOutS16[iFrame] = (ma_int16)(t / pConverter->channelsIn); - } - } break; - - case ma_format_s24: - { - /* */ ma_uint8* pFramesOutS24 = ( ma_uint8*)pFramesOut; - const ma_uint8* pFramesInS24 = (const ma_uint8*)pFramesIn; - - for (iFrame = 0; iFrame < frameCount; ++iFrame) { - ma_int64 t = 0; - for (iChannel = 0; iChannel < pConverter->channelsIn; iChannel += 1) { - t += ma_pcm_sample_s24_to_s32_no_scale(&pFramesInS24[(iFrame*pConverter->channelsIn + iChannel)*3]); - } - - ma_pcm_sample_s32_to_s24_no_scale(t / pConverter->channelsIn, &pFramesOutS24[iFrame*3]); - } - } break; - - case ma_format_s32: - { - /* */ ma_int32* pFramesOutS32 = ( ma_int32*)pFramesOut; - const ma_int32* pFramesInS32 = (const ma_int32*)pFramesIn; - - for (iFrame = 0; iFrame < frameCount; ++iFrame) { - ma_int64 t = 0; - for (iChannel = 0; iChannel < pConverter->channelsIn; iChannel += 1) { - t += pFramesInS32[iFrame*pConverter->channelsIn + iChannel]; - } - - pFramesOutS32[iFrame] = (ma_int32)(t / pConverter->channelsIn); - } - } break; - - case ma_format_f32: - { - /* */ float* pFramesOutF32 = ( float*)pFramesOut; - const float* pFramesInF32 = (const float*)pFramesIn; - - for (iFrame = 0; iFrame < frameCount; ++iFrame) { - float t = 0; - for (iChannel = 0; iChannel < pConverter->channelsIn; iChannel += 1) { - t += pFramesInF32[iFrame*pConverter->channelsIn + iChannel]; - } - - pFramesOutF32[iFrame] = t / pConverter->channelsIn; - } - } break; - - default: return MA_INVALID_OPERATION; /* Unknown format. */ - } - - return MA_SUCCESS; -} - -static ma_result ma_channel_converter_process_pcm_frames__weights(ma_channel_converter* pConverter, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount) -{ - ma_uint32 iFrame; - ma_uint32 iChannelIn; - ma_uint32 iChannelOut; - - MA_ASSERT(pConverter != NULL); - MA_ASSERT(pFramesOut != NULL); - MA_ASSERT(pFramesIn != NULL); - - /* This is the more complicated case. Each of the output channels is accumulated with 0 or more input channels. */ - - /* Clear. */ - ma_zero_memory_64(pFramesOut, frameCount * ma_get_bytes_per_frame(pConverter->format, pConverter->channelsOut)); - - /* Accumulate. */ - switch (pConverter->format) - { - case ma_format_u8: - { - /* */ ma_uint8* pFramesOutU8 = ( ma_uint8*)pFramesOut; - const ma_uint8* pFramesInU8 = (const ma_uint8*)pFramesIn; - - for (iFrame = 0; iFrame < frameCount; iFrame += 1) { - for (iChannelIn = 0; iChannelIn < pConverter->channelsIn; ++iChannelIn) { - for (iChannelOut = 0; iChannelOut < pConverter->channelsOut; ++iChannelOut) { - ma_int16 u8_O = ma_pcm_sample_u8_to_s16_no_scale(pFramesOutU8[iFrame*pConverter->channelsOut + iChannelOut]); - ma_int16 u8_I = ma_pcm_sample_u8_to_s16_no_scale(pFramesInU8 [iFrame*pConverter->channelsIn + iChannelIn ]); - ma_int32 s = (ma_int32)ma_clamp(u8_O + ((u8_I * pConverter->weights.s16[iChannelIn][iChannelOut]) >> MA_CHANNEL_CONVERTER_FIXED_POINT_SHIFT), -128, 127); - pFramesOutU8[iFrame*pConverter->channelsOut + iChannelOut] = ma_clip_u8((ma_int16)s); - } - } - } - } break; - - case ma_format_s16: - { - /* */ ma_int16* pFramesOutS16 = ( ma_int16*)pFramesOut; - const ma_int16* pFramesInS16 = (const ma_int16*)pFramesIn; - - for (iFrame = 0; iFrame < frameCount; iFrame += 1) { - for (iChannelIn = 0; iChannelIn < pConverter->channelsIn; ++iChannelIn) { - for (iChannelOut = 0; iChannelOut < pConverter->channelsOut; ++iChannelOut) { - ma_int32 s = pFramesOutS16[iFrame*pConverter->channelsOut + iChannelOut]; - s += (pFramesInS16[iFrame*pConverter->channelsIn + iChannelIn] * pConverter->weights.s16[iChannelIn][iChannelOut]) >> MA_CHANNEL_CONVERTER_FIXED_POINT_SHIFT; - - pFramesOutS16[iFrame*pConverter->channelsOut + iChannelOut] = (ma_int16)ma_clamp(s, -32768, 32767); - } - } - } - } break; - - case ma_format_s24: - { - /* */ ma_uint8* pFramesOutS24 = ( ma_uint8*)pFramesOut; - const ma_uint8* pFramesInS24 = (const ma_uint8*)pFramesIn; - - for (iFrame = 0; iFrame < frameCount; iFrame += 1) { - for (iChannelIn = 0; iChannelIn < pConverter->channelsIn; ++iChannelIn) { - for (iChannelOut = 0; iChannelOut < pConverter->channelsOut; ++iChannelOut) { - ma_int64 s24_O = ma_pcm_sample_s24_to_s32_no_scale(&pFramesOutS24[(iFrame*pConverter->channelsOut + iChannelOut)*3]); - ma_int64 s24_I = ma_pcm_sample_s24_to_s32_no_scale(&pFramesInS24 [(iFrame*pConverter->channelsIn + iChannelIn )*3]); - ma_int64 s24 = (ma_int32)ma_clamp(s24_O + ((s24_I * pConverter->weights.s16[iChannelIn][iChannelOut]) >> MA_CHANNEL_CONVERTER_FIXED_POINT_SHIFT), -8388608, 8388607); - ma_pcm_sample_s32_to_s24_no_scale(s24, &pFramesOutS24[(iFrame*pConverter->channelsOut + iChannelOut)*3]); - } - } - } - } break; - - case ma_format_s32: - { - /* */ ma_int32* pFramesOutS32 = ( ma_int32*)pFramesOut; - const ma_int32* pFramesInS32 = (const ma_int32*)pFramesIn; - - for (iFrame = 0; iFrame < frameCount; iFrame += 1) { - for (iChannelIn = 0; iChannelIn < pConverter->channelsIn; ++iChannelIn) { - for (iChannelOut = 0; iChannelOut < pConverter->channelsOut; ++iChannelOut) { - ma_int64 s = pFramesOutS32[iFrame*pConverter->channelsOut + iChannelOut]; - s += ((ma_int64)pFramesInS32[iFrame*pConverter->channelsIn + iChannelIn] * pConverter->weights.s16[iChannelIn][iChannelOut]) >> MA_CHANNEL_CONVERTER_FIXED_POINT_SHIFT; - - pFramesOutS32[iFrame*pConverter->channelsOut + iChannelOut] = ma_clip_s32(s); - } - } - } - } break; - - case ma_format_f32: - { - /* */ float* pFramesOutF32 = ( float*)pFramesOut; - const float* pFramesInF32 = (const float*)pFramesIn; - - for (iFrame = 0; iFrame < frameCount; iFrame += 1) { - for (iChannelIn = 0; iChannelIn < pConverter->channelsIn; ++iChannelIn) { - for (iChannelOut = 0; iChannelOut < pConverter->channelsOut; ++iChannelOut) { - pFramesOutF32[iFrame*pConverter->channelsOut + iChannelOut] += pFramesInF32[iFrame*pConverter->channelsIn + iChannelIn] * pConverter->weights.f32[iChannelIn][iChannelOut]; - } - } - } - } break; - - default: return MA_INVALID_OPERATION; /* Unknown format. */ - } - - return MA_SUCCESS; -} - -MA_API ma_result ma_channel_converter_process_pcm_frames(ma_channel_converter* pConverter, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount) -{ - if (pConverter == NULL) { - return MA_INVALID_ARGS; - } - - if (pFramesOut == NULL) { - return MA_INVALID_ARGS; - } - - if (pFramesIn == NULL) { - ma_zero_memory_64(pFramesOut, frameCount * ma_get_bytes_per_frame(pConverter->format, pConverter->channelsOut)); - return MA_SUCCESS; - } - - switch (pConverter->conversionPath) - { - case ma_channel_conversion_path_passthrough: return ma_channel_converter_process_pcm_frames__passthrough(pConverter, pFramesOut, pFramesIn, frameCount); - case ma_channel_conversion_path_mono_out: return ma_channel_converter_process_pcm_frames__mono_out(pConverter, pFramesOut, pFramesIn, frameCount); - case ma_channel_conversion_path_mono_in: return ma_channel_converter_process_pcm_frames__mono_in(pConverter, pFramesOut, pFramesIn, frameCount); - case ma_channel_conversion_path_shuffle: return ma_channel_converter_process_pcm_frames__shuffle(pConverter, pFramesOut, pFramesIn, frameCount); - case ma_channel_conversion_path_weights: - default: - { - return ma_channel_converter_process_pcm_frames__weights(pConverter, pFramesOut, pFramesIn, frameCount); - } - } -} - -MA_API ma_result ma_channel_converter_get_input_channel_map(const ma_channel_converter* pConverter, ma_channel* pChannelMap, size_t channelMapCap) -{ - if (pConverter == NULL || pChannelMap == NULL) { - return MA_INVALID_ARGS; - } - - ma_channel_map_copy_or_default(pChannelMap, channelMapCap, pConverter->pChannelMapIn, pConverter->channelsIn); - - return MA_SUCCESS; -} - -MA_API ma_result ma_channel_converter_get_output_channel_map(const ma_channel_converter* pConverter, ma_channel* pChannelMap, size_t channelMapCap) -{ - if (pConverter == NULL || pChannelMap == NULL) { - return MA_INVALID_ARGS; - } - - ma_channel_map_copy_or_default(pChannelMap, channelMapCap, pConverter->pChannelMapOut, pConverter->channelsOut); - - return MA_SUCCESS; -} - - -/************************************************************************************************************************************************************** - -Data Conversion - -**************************************************************************************************************************************************************/ -MA_API ma_data_converter_config ma_data_converter_config_init_default(void) -{ - ma_data_converter_config config; - MA_ZERO_OBJECT(&config); - - config.ditherMode = ma_dither_mode_none; - config.resampling.algorithm = ma_resample_algorithm_linear; - config.allowDynamicSampleRate = MA_FALSE; /* Disable dynamic sample rates by default because dynamic rate adjustments should be quite rare and it allows an optimization for cases when the in and out sample rates are the same. */ - - /* Linear resampling defaults. */ - config.resampling.linear.lpfOrder = 1; - - return config; -} - -MA_API ma_data_converter_config ma_data_converter_config_init(ma_format formatIn, ma_format formatOut, ma_uint32 channelsIn, ma_uint32 channelsOut, ma_uint32 sampleRateIn, ma_uint32 sampleRateOut) -{ - ma_data_converter_config config = ma_data_converter_config_init_default(); - config.formatIn = formatIn; - config.formatOut = formatOut; - config.channelsIn = channelsIn; - config.channelsOut = channelsOut; - config.sampleRateIn = sampleRateIn; - config.sampleRateOut = sampleRateOut; - - return config; -} - - -typedef struct -{ - size_t sizeInBytes; - size_t channelConverterOffset; - size_t resamplerOffset; -} ma_data_converter_heap_layout; - -static ma_bool32 ma_data_converter_config_is_resampler_required(const ma_data_converter_config* pConfig) -{ - MA_ASSERT(pConfig != NULL); - - return pConfig->allowDynamicSampleRate || pConfig->sampleRateIn != pConfig->sampleRateOut; -} - -static ma_format ma_data_converter_config_get_mid_format(const ma_data_converter_config* pConfig) -{ - MA_ASSERT(pConfig != NULL); - - /* - We want to avoid as much data conversion as possible. The channel converter and linear - resampler both support s16 and f32 natively. We need to decide on the format to use for this - stage. We call this the mid format because it's used in the middle stage of the conversion - pipeline. If the output format is either s16 or f32 we use that one. If that is not the case it - will do the same thing for the input format. If it's neither we just use f32. If we are using a - custom resampling backend, we can only guarantee that f32 will be supported so we'll be forced - to use that if resampling is required. - */ - if (ma_data_converter_config_is_resampler_required(pConfig) && pConfig->resampling.algorithm != ma_resample_algorithm_linear) { - return ma_format_f32; /* <-- Force f32 since that is the only one we can guarantee will be supported by the resampler. */ - } else { - /* */ if (pConfig->formatOut == ma_format_s16 || pConfig->formatOut == ma_format_f32) { - return pConfig->formatOut; - } else if (pConfig->formatIn == ma_format_s16 || pConfig->formatIn == ma_format_f32) { - return pConfig->formatIn; - } else { - return ma_format_f32; - } - } -} - -static ma_channel_converter_config ma_channel_converter_config_init_from_data_converter_config(const ma_data_converter_config* pConfig) -{ - ma_channel_converter_config channelConverterConfig; - - MA_ASSERT(pConfig != NULL); - - channelConverterConfig = ma_channel_converter_config_init(ma_data_converter_config_get_mid_format(pConfig), pConfig->channelsIn, pConfig->pChannelMapIn, pConfig->channelsOut, pConfig->pChannelMapOut, pConfig->channelMixMode); - channelConverterConfig.ppWeights = pConfig->ppChannelWeights; - channelConverterConfig.calculateLFEFromSpatialChannels = pConfig->calculateLFEFromSpatialChannels; - - return channelConverterConfig; -} - -static ma_resampler_config ma_resampler_config_init_from_data_converter_config(const ma_data_converter_config* pConfig) -{ - ma_resampler_config resamplerConfig; - ma_uint32 resamplerChannels; - - MA_ASSERT(pConfig != NULL); - - /* The resampler is the most expensive part of the conversion process, so we need to do it at the stage where the channel count is at it's lowest. */ - if (pConfig->channelsIn < pConfig->channelsOut) { - resamplerChannels = pConfig->channelsIn; - } else { - resamplerChannels = pConfig->channelsOut; - } - - resamplerConfig = ma_resampler_config_init(ma_data_converter_config_get_mid_format(pConfig), resamplerChannels, pConfig->sampleRateIn, pConfig->sampleRateOut, pConfig->resampling.algorithm); - resamplerConfig.linear = pConfig->resampling.linear; - resamplerConfig.pBackendVTable = pConfig->resampling.pBackendVTable; - resamplerConfig.pBackendUserData = pConfig->resampling.pBackendUserData; - - return resamplerConfig; -} - -static ma_result ma_data_converter_get_heap_layout(const ma_data_converter_config* pConfig, ma_data_converter_heap_layout* pHeapLayout) -{ - ma_result result; - - MA_ASSERT(pHeapLayout != NULL); - - MA_ZERO_OBJECT(pHeapLayout); - - if (pConfig == NULL) { - return MA_INVALID_ARGS; - } - - if (pConfig->channelsIn == 0 || pConfig->channelsOut == 0) { - return MA_INVALID_ARGS; - } - - pHeapLayout->sizeInBytes = 0; - - /* Channel converter. */ - pHeapLayout->channelConverterOffset = pHeapLayout->sizeInBytes; - { - size_t heapSizeInBytes; - ma_channel_converter_config channelConverterConfig = ma_channel_converter_config_init_from_data_converter_config(pConfig); - - result = ma_channel_converter_get_heap_size(&channelConverterConfig, &heapSizeInBytes); - if (result != MA_SUCCESS) { - return result; - } - - pHeapLayout->sizeInBytes += heapSizeInBytes; - } - - /* Resampler. */ - pHeapLayout->resamplerOffset = pHeapLayout->sizeInBytes; - if (ma_data_converter_config_is_resampler_required(pConfig)) { - size_t heapSizeInBytes; - ma_resampler_config resamplerConfig = ma_resampler_config_init_from_data_converter_config(pConfig); - - result = ma_resampler_get_heap_size(&resamplerConfig, &heapSizeInBytes); - if (result != MA_SUCCESS) { - return result; - } - - pHeapLayout->sizeInBytes += heapSizeInBytes; - } - - /* Make sure allocation size is aligned. */ - pHeapLayout->sizeInBytes = ma_align_64(pHeapLayout->sizeInBytes); - - return MA_SUCCESS; -} - -MA_API ma_result ma_data_converter_get_heap_size(const ma_data_converter_config* pConfig, size_t* pHeapSizeInBytes) -{ - ma_result result; - ma_data_converter_heap_layout heapLayout; - - if (pHeapSizeInBytes == NULL) { - return MA_INVALID_ARGS; - } - - *pHeapSizeInBytes = 0; - - result = ma_data_converter_get_heap_layout(pConfig, &heapLayout); - if (result != MA_SUCCESS) { - return result; - } - - *pHeapSizeInBytes = heapLayout.sizeInBytes; - - return MA_SUCCESS; -} - -MA_API ma_result ma_data_converter_init_preallocated(const ma_data_converter_config* pConfig, void* pHeap, ma_data_converter* pConverter) -{ - ma_result result; - ma_data_converter_heap_layout heapLayout; - ma_format midFormat; - ma_bool32 isResamplingRequired; - - if (pConverter == NULL) { - return MA_INVALID_ARGS; - } - - MA_ZERO_OBJECT(pConverter); - - result = ma_data_converter_get_heap_layout(pConfig, &heapLayout); - if (result != MA_SUCCESS) { - return result; - } - - pConverter->_pHeap = pHeap; - MA_ZERO_MEMORY(pHeap, heapLayout.sizeInBytes); - - pConverter->formatIn = pConfig->formatIn; - pConverter->formatOut = pConfig->formatOut; - pConverter->channelsIn = pConfig->channelsIn; - pConverter->channelsOut = pConfig->channelsOut; - pConverter->sampleRateIn = pConfig->sampleRateIn; - pConverter->sampleRateOut = pConfig->sampleRateOut; - pConverter->ditherMode = pConfig->ditherMode; - - /* - Determine if resampling is required. We need to do this so we can determine an appropriate - mid format to use. If resampling is required, the mid format must be ma_format_f32 since - that is the only one that is guaranteed to supported by custom resampling backends. - */ - isResamplingRequired = ma_data_converter_config_is_resampler_required(pConfig); - midFormat = ma_data_converter_config_get_mid_format(pConfig); - - - /* Channel converter. We always initialize this, but we check if it configures itself as a passthrough to determine whether or not it's needed. */ - { - ma_channel_converter_config channelConverterConfig = ma_channel_converter_config_init_from_data_converter_config(pConfig); - - result = ma_channel_converter_init_preallocated(&channelConverterConfig, ma_offset_ptr(pHeap, heapLayout.channelConverterOffset), &pConverter->channelConverter); - if (result != MA_SUCCESS) { - return result; - } - - /* If the channel converter is not a passthrough we need to enable it. Otherwise we can skip it. */ - if (pConverter->channelConverter.conversionPath != ma_channel_conversion_path_passthrough) { - pConverter->hasChannelConverter = MA_TRUE; - } - } - - - /* Resampler. */ - if (isResamplingRequired) { - ma_resampler_config resamplerConfig = ma_resampler_config_init_from_data_converter_config(pConfig); - - result = ma_resampler_init_preallocated(&resamplerConfig, ma_offset_ptr(pHeap, heapLayout.resamplerOffset), &pConverter->resampler); - if (result != MA_SUCCESS) { - return result; - } - - pConverter->hasResampler = MA_TRUE; - } - - - /* We can simplify pre- and post-format conversion if we have neither channel conversion nor resampling. */ - if (pConverter->hasChannelConverter == MA_FALSE && pConverter->hasResampler == MA_FALSE) { - /* We have neither channel conversion nor resampling so we'll only need one of pre- or post-format conversion, or none if the input and output formats are the same. */ - if (pConverter->formatIn == pConverter->formatOut) { - /* The formats are the same so we can just pass through. */ - pConverter->hasPreFormatConversion = MA_FALSE; - pConverter->hasPostFormatConversion = MA_FALSE; - } else { - /* The formats are different so we need to do either pre- or post-format conversion. It doesn't matter which. */ - pConverter->hasPreFormatConversion = MA_FALSE; - pConverter->hasPostFormatConversion = MA_TRUE; - } - } else { - /* We have a channel converter and/or resampler so we'll need channel conversion based on the mid format. */ - if (pConverter->formatIn != midFormat) { - pConverter->hasPreFormatConversion = MA_TRUE; - } - if (pConverter->formatOut != midFormat) { - pConverter->hasPostFormatConversion = MA_TRUE; - } - } - - /* We can enable passthrough optimizations if applicable. Note that we'll only be able to do this if the sample rate is static. */ - if (pConverter->hasPreFormatConversion == MA_FALSE && - pConverter->hasPostFormatConversion == MA_FALSE && - pConverter->hasChannelConverter == MA_FALSE && - pConverter->hasResampler == MA_FALSE) { - pConverter->isPassthrough = MA_TRUE; - } - - - /* We now need to determine our execution path. */ - if (pConverter->isPassthrough) { - pConverter->executionPath = ma_data_converter_execution_path_passthrough; - } else { - if (pConverter->channelsIn < pConverter->channelsOut) { - /* Do resampling first, if necessary. */ - MA_ASSERT(pConverter->hasChannelConverter == MA_TRUE); - - if (pConverter->hasResampler) { - pConverter->executionPath = ma_data_converter_execution_path_resample_first; - } else { - pConverter->executionPath = ma_data_converter_execution_path_channels_only; - } - } else { - /* Do channel conversion first, if necessary. */ - if (pConverter->hasChannelConverter) { - if (pConverter->hasResampler) { - pConverter->executionPath = ma_data_converter_execution_path_channels_first; - } else { - pConverter->executionPath = ma_data_converter_execution_path_channels_only; - } - } else { - /* Channel routing not required. */ - if (pConverter->hasResampler) { - pConverter->executionPath = ma_data_converter_execution_path_resample_only; - } else { - pConverter->executionPath = ma_data_converter_execution_path_format_only; - } - } - } - } - - return MA_SUCCESS; -} - -MA_API ma_result ma_data_converter_init(const ma_data_converter_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_data_converter* pConverter) -{ - ma_result result; - size_t heapSizeInBytes; - void* pHeap; - - result = ma_data_converter_get_heap_size(pConfig, &heapSizeInBytes); - if (result != MA_SUCCESS) { - return result; - } - - if (heapSizeInBytes > 0) { - pHeap = ma_malloc(heapSizeInBytes, pAllocationCallbacks); - if (pHeap == NULL) { - return MA_OUT_OF_MEMORY; - } - } else { - pHeap = NULL; - } - - result = ma_data_converter_init_preallocated(pConfig, pHeap, pConverter); - if (result != MA_SUCCESS) { - ma_free(pHeap, pAllocationCallbacks); - return result; - } - - pConverter->_ownsHeap = MA_TRUE; - return MA_SUCCESS; -} - -MA_API void ma_data_converter_uninit(ma_data_converter* pConverter, const ma_allocation_callbacks* pAllocationCallbacks) -{ - if (pConverter == NULL) { - return; - } - - if (pConverter->hasResampler) { - ma_resampler_uninit(&pConverter->resampler, pAllocationCallbacks); - } - - ma_channel_converter_uninit(&pConverter->channelConverter, pAllocationCallbacks); - - if (pConverter->_ownsHeap) { - ma_free(pConverter->_pHeap, pAllocationCallbacks); - } -} - -static ma_result ma_data_converter_process_pcm_frames__passthrough(ma_data_converter* pConverter, const void* pFramesIn, ma_uint64* pFrameCountIn, void* pFramesOut, ma_uint64* pFrameCountOut) -{ - ma_uint64 frameCountIn; - ma_uint64 frameCountOut; - ma_uint64 frameCount; - - MA_ASSERT(pConverter != NULL); - - frameCountIn = 0; - if (pFrameCountIn != NULL) { - frameCountIn = *pFrameCountIn; - } - - frameCountOut = 0; - if (pFrameCountOut != NULL) { - frameCountOut = *pFrameCountOut; - } - - frameCount = ma_min(frameCountIn, frameCountOut); - - if (pFramesOut != NULL) { - if (pFramesIn != NULL) { - ma_copy_memory_64(pFramesOut, pFramesIn, frameCount * ma_get_bytes_per_frame(pConverter->formatOut, pConverter->channelsOut)); - } else { - ma_zero_memory_64(pFramesOut, frameCount * ma_get_bytes_per_frame(pConverter->formatOut, pConverter->channelsOut)); - } - } - - if (pFrameCountIn != NULL) { - *pFrameCountIn = frameCount; - } - if (pFrameCountOut != NULL) { - *pFrameCountOut = frameCount; - } - - return MA_SUCCESS; -} - -static ma_result ma_data_converter_process_pcm_frames__format_only(ma_data_converter* pConverter, const void* pFramesIn, ma_uint64* pFrameCountIn, void* pFramesOut, ma_uint64* pFrameCountOut) -{ - ma_uint64 frameCountIn; - ma_uint64 frameCountOut; - ma_uint64 frameCount; - - MA_ASSERT(pConverter != NULL); - - frameCountIn = 0; - if (pFrameCountIn != NULL) { - frameCountIn = *pFrameCountIn; - } - - frameCountOut = 0; - if (pFrameCountOut != NULL) { - frameCountOut = *pFrameCountOut; - } - - frameCount = ma_min(frameCountIn, frameCountOut); - - if (pFramesOut != NULL) { - if (pFramesIn != NULL) { - ma_convert_pcm_frames_format(pFramesOut, pConverter->formatOut, pFramesIn, pConverter->formatIn, frameCount, pConverter->channelsIn, pConverter->ditherMode); - } else { - ma_zero_memory_64(pFramesOut, frameCount * ma_get_bytes_per_frame(pConverter->formatOut, pConverter->channelsOut)); - } - } - - if (pFrameCountIn != NULL) { - *pFrameCountIn = frameCount; - } - if (pFrameCountOut != NULL) { - *pFrameCountOut = frameCount; - } - - return MA_SUCCESS; -} - - -static ma_result ma_data_converter_process_pcm_frames__resample_with_format_conversion(ma_data_converter* pConverter, const void* pFramesIn, ma_uint64* pFrameCountIn, void* pFramesOut, ma_uint64* pFrameCountOut) -{ - ma_result result = MA_SUCCESS; - ma_uint64 frameCountIn; - ma_uint64 frameCountOut; - ma_uint64 framesProcessedIn; - ma_uint64 framesProcessedOut; - - MA_ASSERT(pConverter != NULL); - - frameCountIn = 0; - if (pFrameCountIn != NULL) { - frameCountIn = *pFrameCountIn; - } - - frameCountOut = 0; - if (pFrameCountOut != NULL) { - frameCountOut = *pFrameCountOut; - } - - framesProcessedIn = 0; - framesProcessedOut = 0; - - while (framesProcessedOut < frameCountOut) { - ma_uint8 pTempBufferOut[MA_DATA_CONVERTER_STACK_BUFFER_SIZE]; - const ma_uint32 tempBufferOutCap = sizeof(pTempBufferOut) / ma_get_bytes_per_frame(pConverter->resampler.format, pConverter->resampler.channels); - const void* pFramesInThisIteration; - /* */ void* pFramesOutThisIteration; - ma_uint64 frameCountInThisIteration; - ma_uint64 frameCountOutThisIteration; - - if (pFramesIn != NULL) { - pFramesInThisIteration = ma_offset_ptr(pFramesIn, framesProcessedIn * ma_get_bytes_per_frame(pConverter->formatIn, pConverter->channelsIn)); - } else { - pFramesInThisIteration = NULL; - } - - if (pFramesOut != NULL) { - pFramesOutThisIteration = ma_offset_ptr(pFramesOut, framesProcessedOut * ma_get_bytes_per_frame(pConverter->formatOut, pConverter->channelsOut)); - } else { - pFramesOutThisIteration = NULL; - } - - /* Do a pre format conversion if necessary. */ - if (pConverter->hasPreFormatConversion) { - ma_uint8 pTempBufferIn[MA_DATA_CONVERTER_STACK_BUFFER_SIZE]; - const ma_uint32 tempBufferInCap = sizeof(pTempBufferIn) / ma_get_bytes_per_frame(pConverter->resampler.format, pConverter->resampler.channels); - - frameCountInThisIteration = (frameCountIn - framesProcessedIn); - if (frameCountInThisIteration > tempBufferInCap) { - frameCountInThisIteration = tempBufferInCap; - } - - if (pConverter->hasPostFormatConversion) { - if (frameCountInThisIteration > tempBufferOutCap) { - frameCountInThisIteration = tempBufferOutCap; - } - } - - if (pFramesInThisIteration != NULL) { - ma_convert_pcm_frames_format(pTempBufferIn, pConverter->resampler.format, pFramesInThisIteration, pConverter->formatIn, frameCountInThisIteration, pConverter->channelsIn, pConverter->ditherMode); - } else { - MA_ZERO_MEMORY(pTempBufferIn, sizeof(pTempBufferIn)); - } - - frameCountOutThisIteration = (frameCountOut - framesProcessedOut); - - if (pConverter->hasPostFormatConversion) { - /* Both input and output conversion required. Output to the temp buffer. */ - if (frameCountOutThisIteration > tempBufferOutCap) { - frameCountOutThisIteration = tempBufferOutCap; - } - - result = ma_resampler_process_pcm_frames(&pConverter->resampler, pTempBufferIn, &frameCountInThisIteration, pTempBufferOut, &frameCountOutThisIteration); - } else { - /* Only pre-format required. Output straight to the output buffer. */ - result = ma_resampler_process_pcm_frames(&pConverter->resampler, pTempBufferIn, &frameCountInThisIteration, pFramesOutThisIteration, &frameCountOutThisIteration); - } - - if (result != MA_SUCCESS) { - break; - } - } else { - /* No pre-format required. Just read straight from the input buffer. */ - MA_ASSERT(pConverter->hasPostFormatConversion == MA_TRUE); - - frameCountInThisIteration = (frameCountIn - framesProcessedIn); - frameCountOutThisIteration = (frameCountOut - framesProcessedOut); - if (frameCountOutThisIteration > tempBufferOutCap) { - frameCountOutThisIteration = tempBufferOutCap; - } - - result = ma_resampler_process_pcm_frames(&pConverter->resampler, pFramesInThisIteration, &frameCountInThisIteration, pTempBufferOut, &frameCountOutThisIteration); - if (result != MA_SUCCESS) { - break; - } - } - - /* If we are doing a post format conversion we need to do that now. */ - if (pConverter->hasPostFormatConversion) { - if (pFramesOutThisIteration != NULL) { - ma_convert_pcm_frames_format(pFramesOutThisIteration, pConverter->formatOut, pTempBufferOut, pConverter->resampler.format, frameCountOutThisIteration, pConverter->resampler.channels, pConverter->ditherMode); - } - } - - framesProcessedIn += frameCountInThisIteration; - framesProcessedOut += frameCountOutThisIteration; - - MA_ASSERT(framesProcessedIn <= frameCountIn); - MA_ASSERT(framesProcessedOut <= frameCountOut); - - if (frameCountOutThisIteration == 0) { - break; /* Consumed all of our input data. */ - } - } - - if (pFrameCountIn != NULL) { - *pFrameCountIn = framesProcessedIn; - } - if (pFrameCountOut != NULL) { - *pFrameCountOut = framesProcessedOut; - } - - return result; -} - -static ma_result ma_data_converter_process_pcm_frames__resample_only(ma_data_converter* pConverter, const void* pFramesIn, ma_uint64* pFrameCountIn, void* pFramesOut, ma_uint64* pFrameCountOut) -{ - MA_ASSERT(pConverter != NULL); - - if (pConverter->hasPreFormatConversion == MA_FALSE && pConverter->hasPostFormatConversion == MA_FALSE) { - /* Neither pre- nor post-format required. This is simple case where only resampling is required. */ - return ma_resampler_process_pcm_frames(&pConverter->resampler, pFramesIn, pFrameCountIn, pFramesOut, pFrameCountOut); - } else { - /* Format conversion required. */ - return ma_data_converter_process_pcm_frames__resample_with_format_conversion(pConverter, pFramesIn, pFrameCountIn, pFramesOut, pFrameCountOut); - } -} - -static ma_result ma_data_converter_process_pcm_frames__channels_only(ma_data_converter* pConverter, const void* pFramesIn, ma_uint64* pFrameCountIn, void* pFramesOut, ma_uint64* pFrameCountOut) -{ - ma_result result; - ma_uint64 frameCountIn; - ma_uint64 frameCountOut; - ma_uint64 frameCount; - - MA_ASSERT(pConverter != NULL); - - frameCountIn = 0; - if (pFrameCountIn != NULL) { - frameCountIn = *pFrameCountIn; - } - - frameCountOut = 0; - if (pFrameCountOut != NULL) { - frameCountOut = *pFrameCountOut; - } - - frameCount = ma_min(frameCountIn, frameCountOut); - - if (pConverter->hasPreFormatConversion == MA_FALSE && pConverter->hasPostFormatConversion == MA_FALSE) { - /* No format conversion required. */ - result = ma_channel_converter_process_pcm_frames(&pConverter->channelConverter, pFramesOut, pFramesIn, frameCount); - if (result != MA_SUCCESS) { - return result; - } - } else { - /* Format conversion required. */ - ma_uint64 framesProcessed = 0; - - while (framesProcessed < frameCount) { - ma_uint8 pTempBufferOut[MA_DATA_CONVERTER_STACK_BUFFER_SIZE]; - const ma_uint32 tempBufferOutCap = sizeof(pTempBufferOut) / ma_get_bytes_per_frame(pConverter->channelConverter.format, pConverter->channelConverter.channelsOut); - const void* pFramesInThisIteration; - /* */ void* pFramesOutThisIteration; - ma_uint64 frameCountThisIteration; - - if (pFramesIn != NULL) { - pFramesInThisIteration = ma_offset_ptr(pFramesIn, framesProcessed * ma_get_bytes_per_frame(pConverter->formatIn, pConverter->channelsIn)); - } else { - pFramesInThisIteration = NULL; - } - - if (pFramesOut != NULL) { - pFramesOutThisIteration = ma_offset_ptr(pFramesOut, framesProcessed * ma_get_bytes_per_frame(pConverter->formatOut, pConverter->channelsOut)); - } else { - pFramesOutThisIteration = NULL; - } - - /* Do a pre format conversion if necessary. */ - if (pConverter->hasPreFormatConversion) { - ma_uint8 pTempBufferIn[MA_DATA_CONVERTER_STACK_BUFFER_SIZE]; - const ma_uint32 tempBufferInCap = sizeof(pTempBufferIn) / ma_get_bytes_per_frame(pConverter->channelConverter.format, pConverter->channelConverter.channelsIn); - - frameCountThisIteration = (frameCount - framesProcessed); - if (frameCountThisIteration > tempBufferInCap) { - frameCountThisIteration = tempBufferInCap; - } - - if (pConverter->hasPostFormatConversion) { - if (frameCountThisIteration > tempBufferOutCap) { - frameCountThisIteration = tempBufferOutCap; - } - } - - if (pFramesInThisIteration != NULL) { - ma_convert_pcm_frames_format(pTempBufferIn, pConverter->channelConverter.format, pFramesInThisIteration, pConverter->formatIn, frameCountThisIteration, pConverter->channelsIn, pConverter->ditherMode); - } else { - MA_ZERO_MEMORY(pTempBufferIn, sizeof(pTempBufferIn)); - } - - if (pConverter->hasPostFormatConversion) { - /* Both input and output conversion required. Output to the temp buffer. */ - result = ma_channel_converter_process_pcm_frames(&pConverter->channelConverter, pTempBufferOut, pTempBufferIn, frameCountThisIteration); - } else { - /* Only pre-format required. Output straight to the output buffer. */ - result = ma_channel_converter_process_pcm_frames(&pConverter->channelConverter, pFramesOutThisIteration, pTempBufferIn, frameCountThisIteration); - } - - if (result != MA_SUCCESS) { - break; - } - } else { - /* No pre-format required. Just read straight from the input buffer. */ - MA_ASSERT(pConverter->hasPostFormatConversion == MA_TRUE); - - frameCountThisIteration = (frameCount - framesProcessed); - if (frameCountThisIteration > tempBufferOutCap) { - frameCountThisIteration = tempBufferOutCap; - } - - result = ma_channel_converter_process_pcm_frames(&pConverter->channelConverter, pTempBufferOut, pFramesInThisIteration, frameCountThisIteration); - if (result != MA_SUCCESS) { - break; - } - } - - /* If we are doing a post format conversion we need to do that now. */ - if (pConverter->hasPostFormatConversion) { - if (pFramesOutThisIteration != NULL) { - ma_convert_pcm_frames_format(pFramesOutThisIteration, pConverter->formatOut, pTempBufferOut, pConverter->channelConverter.format, frameCountThisIteration, pConverter->channelConverter.channelsOut, pConverter->ditherMode); - } - } - - framesProcessed += frameCountThisIteration; - } - } - - if (pFrameCountIn != NULL) { - *pFrameCountIn = frameCount; - } - if (pFrameCountOut != NULL) { - *pFrameCountOut = frameCount; - } - - return MA_SUCCESS; -} - -static ma_result ma_data_converter_process_pcm_frames__resample_first(ma_data_converter* pConverter, const void* pFramesIn, ma_uint64* pFrameCountIn, void* pFramesOut, ma_uint64* pFrameCountOut) -{ - ma_result result; - ma_uint64 frameCountIn; - ma_uint64 frameCountOut; - ma_uint64 framesProcessedIn; - ma_uint64 framesProcessedOut; - ma_uint8 pTempBufferIn[MA_DATA_CONVERTER_STACK_BUFFER_SIZE]; /* In resampler format. */ - ma_uint64 tempBufferInCap; - ma_uint8 pTempBufferMid[MA_DATA_CONVERTER_STACK_BUFFER_SIZE]; /* In resampler format, channel converter input format. */ - ma_uint64 tempBufferMidCap; - ma_uint8 pTempBufferOut[MA_DATA_CONVERTER_STACK_BUFFER_SIZE]; /* In channel converter output format. */ - ma_uint64 tempBufferOutCap; - - MA_ASSERT(pConverter != NULL); - MA_ASSERT(pConverter->resampler.format == pConverter->channelConverter.format); - MA_ASSERT(pConverter->resampler.channels == pConverter->channelConverter.channelsIn); - MA_ASSERT(pConverter->resampler.channels < pConverter->channelConverter.channelsOut); - - frameCountIn = 0; - if (pFrameCountIn != NULL) { - frameCountIn = *pFrameCountIn; - } - - frameCountOut = 0; - if (pFrameCountOut != NULL) { - frameCountOut = *pFrameCountOut; - } - - framesProcessedIn = 0; - framesProcessedOut = 0; - - tempBufferInCap = sizeof(pTempBufferIn) / ma_get_bytes_per_frame(pConverter->resampler.format, pConverter->resampler.channels); - tempBufferMidCap = sizeof(pTempBufferIn) / ma_get_bytes_per_frame(pConverter->resampler.format, pConverter->resampler.channels); - tempBufferOutCap = sizeof(pTempBufferOut) / ma_get_bytes_per_frame(pConverter->channelConverter.format, pConverter->channelConverter.channelsOut); - - while (framesProcessedOut < frameCountOut) { - ma_uint64 frameCountInThisIteration; - ma_uint64 frameCountOutThisIteration; - const void* pRunningFramesIn = NULL; - void* pRunningFramesOut = NULL; - const void* pResampleBufferIn; - void* pChannelsBufferOut; - - if (pFramesIn != NULL) { - pRunningFramesIn = ma_offset_ptr(pFramesIn, framesProcessedIn * ma_get_bytes_per_frame(pConverter->formatIn, pConverter->channelsIn)); - } - if (pFramesOut != NULL) { - pRunningFramesOut = ma_offset_ptr(pFramesOut, framesProcessedOut * ma_get_bytes_per_frame(pConverter->formatOut, pConverter->channelsOut)); - } - - /* Run input data through the resampler and output it to the temporary buffer. */ - frameCountInThisIteration = (frameCountIn - framesProcessedIn); - - if (pConverter->hasPreFormatConversion) { - if (frameCountInThisIteration > tempBufferInCap) { - frameCountInThisIteration = tempBufferInCap; - } - } - - frameCountOutThisIteration = (frameCountOut - framesProcessedOut); - if (frameCountOutThisIteration > tempBufferMidCap) { - frameCountOutThisIteration = tempBufferMidCap; - } - - /* We can't read more frames than can fit in the output buffer. */ - if (pConverter->hasPostFormatConversion) { - if (frameCountOutThisIteration > tempBufferOutCap) { - frameCountOutThisIteration = tempBufferOutCap; - } - } - - /* We need to ensure we don't try to process too many input frames that we run out of room in the output buffer. If this happens we'll end up glitching. */ - - /* - We need to try to predict how many input frames will be required for the resampler. If the - resampler can tell us, we'll use that. Otherwise we'll need to make a best guess. The further - off we are from this, the more wasted format conversions we'll end up doing. - */ - #if 1 - { - ma_uint64 requiredInputFrameCount; - - result = ma_resampler_get_required_input_frame_count(&pConverter->resampler, frameCountOutThisIteration, &requiredInputFrameCount); - if (result != MA_SUCCESS) { - /* Fall back to a best guess. */ - requiredInputFrameCount = (frameCountOutThisIteration * pConverter->resampler.sampleRateIn) / pConverter->resampler.sampleRateOut; - } - - if (frameCountInThisIteration > requiredInputFrameCount) { - frameCountInThisIteration = requiredInputFrameCount; - } - } - #endif - - if (pConverter->hasPreFormatConversion) { - if (pFramesIn != NULL) { - ma_convert_pcm_frames_format(pTempBufferIn, pConverter->resampler.format, pRunningFramesIn, pConverter->formatIn, frameCountInThisIteration, pConverter->channelsIn, pConverter->ditherMode); - pResampleBufferIn = pTempBufferIn; - } else { - pResampleBufferIn = NULL; - } - } else { - pResampleBufferIn = pRunningFramesIn; - } - - result = ma_resampler_process_pcm_frames(&pConverter->resampler, pResampleBufferIn, &frameCountInThisIteration, pTempBufferMid, &frameCountOutThisIteration); - if (result != MA_SUCCESS) { - return result; - } - - - /* - The input data has been resampled so now we need to run it through the channel converter. The input data is always contained in pTempBufferMid. We only need to do - this part if we have an output buffer. - */ - if (pFramesOut != NULL) { - if (pConverter->hasPostFormatConversion) { - pChannelsBufferOut = pTempBufferOut; - } else { - pChannelsBufferOut = pRunningFramesOut; - } - - result = ma_channel_converter_process_pcm_frames(&pConverter->channelConverter, pChannelsBufferOut, pTempBufferMid, frameCountOutThisIteration); - if (result != MA_SUCCESS) { - return result; - } - - /* Finally we do post format conversion. */ - if (pConverter->hasPostFormatConversion) { - ma_convert_pcm_frames_format(pRunningFramesOut, pConverter->formatOut, pChannelsBufferOut, pConverter->channelConverter.format, frameCountOutThisIteration, pConverter->channelConverter.channelsOut, pConverter->ditherMode); - } - } - - - framesProcessedIn += frameCountInThisIteration; - framesProcessedOut += frameCountOutThisIteration; - - MA_ASSERT(framesProcessedIn <= frameCountIn); - MA_ASSERT(framesProcessedOut <= frameCountOut); - - if (frameCountOutThisIteration == 0) { - break; /* Consumed all of our input data. */ - } - } - - if (pFrameCountIn != NULL) { - *pFrameCountIn = framesProcessedIn; - } - if (pFrameCountOut != NULL) { - *pFrameCountOut = framesProcessedOut; - } - - return MA_SUCCESS; -} - -static ma_result ma_data_converter_process_pcm_frames__channels_first(ma_data_converter* pConverter, const void* pFramesIn, ma_uint64* pFrameCountIn, void* pFramesOut, ma_uint64* pFrameCountOut) -{ - ma_result result; - ma_uint64 frameCountIn; - ma_uint64 frameCountOut; - ma_uint64 framesProcessedIn; - ma_uint64 framesProcessedOut; - ma_uint8 pTempBufferIn[MA_DATA_CONVERTER_STACK_BUFFER_SIZE]; /* In resampler format. */ - ma_uint64 tempBufferInCap; - ma_uint8 pTempBufferMid[MA_DATA_CONVERTER_STACK_BUFFER_SIZE]; /* In resampler format, channel converter input format. */ - ma_uint64 tempBufferMidCap; - ma_uint8 pTempBufferOut[MA_DATA_CONVERTER_STACK_BUFFER_SIZE]; /* In channel converter output format. */ - ma_uint64 tempBufferOutCap; - - MA_ASSERT(pConverter != NULL); - MA_ASSERT(pConverter->resampler.format == pConverter->channelConverter.format); - MA_ASSERT(pConverter->resampler.channels == pConverter->channelConverter.channelsOut); - MA_ASSERT(pConverter->resampler.channels <= pConverter->channelConverter.channelsIn); - - frameCountIn = 0; - if (pFrameCountIn != NULL) { - frameCountIn = *pFrameCountIn; - } - - frameCountOut = 0; - if (pFrameCountOut != NULL) { - frameCountOut = *pFrameCountOut; - } - - framesProcessedIn = 0; - framesProcessedOut = 0; - - tempBufferInCap = sizeof(pTempBufferIn) / ma_get_bytes_per_frame(pConverter->channelConverter.format, pConverter->channelConverter.channelsIn); - tempBufferMidCap = sizeof(pTempBufferIn) / ma_get_bytes_per_frame(pConverter->channelConverter.format, pConverter->channelConverter.channelsOut); - tempBufferOutCap = sizeof(pTempBufferOut) / ma_get_bytes_per_frame(pConverter->resampler.format, pConverter->resampler.channels); - - while (framesProcessedOut < frameCountOut) { - ma_uint64 frameCountInThisIteration; - ma_uint64 frameCountOutThisIteration; - const void* pRunningFramesIn = NULL; - void* pRunningFramesOut = NULL; - const void* pChannelsBufferIn; - void* pResampleBufferOut; - - if (pFramesIn != NULL) { - pRunningFramesIn = ma_offset_ptr(pFramesIn, framesProcessedIn * ma_get_bytes_per_frame(pConverter->formatIn, pConverter->channelsIn)); - } - if (pFramesOut != NULL) { - pRunningFramesOut = ma_offset_ptr(pFramesOut, framesProcessedOut * ma_get_bytes_per_frame(pConverter->formatOut, pConverter->channelsOut)); - } - - /* - Before doing any processing we need to determine how many frames we should try processing - this iteration, for both input and output. The resampler requires us to perform format and - channel conversion before passing any data into it. If we get our input count wrong, we'll - end up performing redundant pre-processing. This isn't the end of the world, but it does - result in some inefficiencies proportionate to how far our estimates are off. - - If the resampler has a means to calculate exactly how much we'll need, we'll use that. - Otherwise we'll make a best guess. In order to do this, we'll need to calculate the output - frame count first. - */ - frameCountOutThisIteration = (frameCountOut - framesProcessedOut); - if (frameCountOutThisIteration > tempBufferMidCap) { - frameCountOutThisIteration = tempBufferMidCap; - } - - if (pConverter->hasPostFormatConversion) { - if (frameCountOutThisIteration > tempBufferOutCap) { - frameCountOutThisIteration = tempBufferOutCap; - } - } - - /* Now that we have the output frame count we can determine the input frame count. */ - frameCountInThisIteration = (frameCountIn - framesProcessedIn); - if (pConverter->hasPreFormatConversion) { - if (frameCountInThisIteration > tempBufferInCap) { - frameCountInThisIteration = tempBufferInCap; - } - } - - if (frameCountInThisIteration > tempBufferMidCap) { - frameCountInThisIteration = tempBufferMidCap; - } - - #if 1 - { - ma_uint64 requiredInputFrameCount; - - result = ma_resampler_get_required_input_frame_count(&pConverter->resampler, frameCountOutThisIteration, &requiredInputFrameCount); - if (result != MA_SUCCESS) { - /* Fall back to a best guess. */ - requiredInputFrameCount = (frameCountOutThisIteration * pConverter->resampler.sampleRateIn) / pConverter->resampler.sampleRateOut; - } - - if (frameCountInThisIteration > requiredInputFrameCount) { - frameCountInThisIteration = requiredInputFrameCount; - } - } - #endif - - - /* Pre format conversion. */ - if (pConverter->hasPreFormatConversion) { - if (pRunningFramesIn != NULL) { - ma_convert_pcm_frames_format(pTempBufferIn, pConverter->channelConverter.format, pRunningFramesIn, pConverter->formatIn, frameCountInThisIteration, pConverter->channelsIn, pConverter->ditherMode); - pChannelsBufferIn = pTempBufferIn; - } else { - pChannelsBufferIn = NULL; - } - } else { - pChannelsBufferIn = pRunningFramesIn; - } - - - /* Channel conversion. */ - result = ma_channel_converter_process_pcm_frames(&pConverter->channelConverter, pTempBufferMid, pChannelsBufferIn, frameCountInThisIteration); - if (result != MA_SUCCESS) { - return result; - } - - - /* Resampling. */ - if (pConverter->hasPostFormatConversion) { - pResampleBufferOut = pTempBufferOut; - } else { - pResampleBufferOut = pRunningFramesOut; - } - - result = ma_resampler_process_pcm_frames(&pConverter->resampler, pTempBufferMid, &frameCountInThisIteration, pResampleBufferOut, &frameCountOutThisIteration); - if (result != MA_SUCCESS) { - return result; - } - - - /* Post format conversion. */ - if (pConverter->hasPostFormatConversion) { - if (pRunningFramesOut != NULL) { - ma_convert_pcm_frames_format(pRunningFramesOut, pConverter->formatOut, pResampleBufferOut, pConverter->resampler.format, frameCountOutThisIteration, pConverter->channelsOut, pConverter->ditherMode); - } - } - - - framesProcessedIn += frameCountInThisIteration; - framesProcessedOut += frameCountOutThisIteration; - - MA_ASSERT(framesProcessedIn <= frameCountIn); - MA_ASSERT(framesProcessedOut <= frameCountOut); - - if (frameCountOutThisIteration == 0) { - break; /* Consumed all of our input data. */ - } - } - - if (pFrameCountIn != NULL) { - *pFrameCountIn = framesProcessedIn; - } - if (pFrameCountOut != NULL) { - *pFrameCountOut = framesProcessedOut; - } - - return MA_SUCCESS; -} - -MA_API ma_result ma_data_converter_process_pcm_frames(ma_data_converter* pConverter, const void* pFramesIn, ma_uint64* pFrameCountIn, void* pFramesOut, ma_uint64* pFrameCountOut) -{ - if (pConverter == NULL) { - return MA_INVALID_ARGS; - } - - switch (pConverter->executionPath) - { - case ma_data_converter_execution_path_passthrough: return ma_data_converter_process_pcm_frames__passthrough(pConverter, pFramesIn, pFrameCountIn, pFramesOut, pFrameCountOut); - case ma_data_converter_execution_path_format_only: return ma_data_converter_process_pcm_frames__format_only(pConverter, pFramesIn, pFrameCountIn, pFramesOut, pFrameCountOut); - case ma_data_converter_execution_path_channels_only: return ma_data_converter_process_pcm_frames__channels_only(pConverter, pFramesIn, pFrameCountIn, pFramesOut, pFrameCountOut); - case ma_data_converter_execution_path_resample_only: return ma_data_converter_process_pcm_frames__resample_only(pConverter, pFramesIn, pFrameCountIn, pFramesOut, pFrameCountOut); - case ma_data_converter_execution_path_resample_first: return ma_data_converter_process_pcm_frames__resample_first(pConverter, pFramesIn, pFrameCountIn, pFramesOut, pFrameCountOut); - case ma_data_converter_execution_path_channels_first: return ma_data_converter_process_pcm_frames__channels_first(pConverter, pFramesIn, pFrameCountIn, pFramesOut, pFrameCountOut); - default: return MA_INVALID_OPERATION; /* Should never hit this. */ - } -} - -MA_API ma_result ma_data_converter_set_rate(ma_data_converter* pConverter, ma_uint32 sampleRateIn, ma_uint32 sampleRateOut) -{ - if (pConverter == NULL) { - return MA_INVALID_ARGS; - } - - if (pConverter->hasResampler == MA_FALSE) { - return MA_INVALID_OPERATION; /* Dynamic resampling not enabled. */ - } - - return ma_resampler_set_rate(&pConverter->resampler, sampleRateIn, sampleRateOut); -} - -MA_API ma_result ma_data_converter_set_rate_ratio(ma_data_converter* pConverter, float ratioInOut) -{ - if (pConverter == NULL) { - return MA_INVALID_ARGS; - } - - if (pConverter->hasResampler == MA_FALSE) { - return MA_INVALID_OPERATION; /* Dynamic resampling not enabled. */ - } - - return ma_resampler_set_rate_ratio(&pConverter->resampler, ratioInOut); -} - -MA_API ma_uint64 ma_data_converter_get_input_latency(const ma_data_converter* pConverter) -{ - if (pConverter == NULL) { - return 0; - } - - if (pConverter->hasResampler) { - return ma_resampler_get_input_latency(&pConverter->resampler); - } - - return 0; /* No latency without a resampler. */ -} - -MA_API ma_uint64 ma_data_converter_get_output_latency(const ma_data_converter* pConverter) -{ - if (pConverter == NULL) { - return 0; - } - - if (pConverter->hasResampler) { - return ma_resampler_get_output_latency(&pConverter->resampler); - } - - return 0; /* No latency without a resampler. */ -} - -MA_API ma_result ma_data_converter_get_required_input_frame_count(const ma_data_converter* pConverter, ma_uint64 outputFrameCount, ma_uint64* pInputFrameCount) -{ - if (pInputFrameCount == NULL) { - return MA_INVALID_ARGS; - } - - *pInputFrameCount = 0; - - if (pConverter == NULL) { - return MA_INVALID_ARGS; - } - - if (pConverter->hasResampler) { - return ma_resampler_get_required_input_frame_count(&pConverter->resampler, outputFrameCount, pInputFrameCount); - } else { - *pInputFrameCount = outputFrameCount; /* 1:1 */ - return MA_SUCCESS; - } -} - -MA_API ma_result ma_data_converter_get_expected_output_frame_count(const ma_data_converter* pConverter, ma_uint64 inputFrameCount, ma_uint64* pOutputFrameCount) -{ - if (pOutputFrameCount == NULL) { - return MA_INVALID_ARGS; - } - - *pOutputFrameCount = 0; - - if (pConverter == NULL) { - return MA_INVALID_ARGS; - } - - if (pConverter->hasResampler) { - return ma_resampler_get_expected_output_frame_count(&pConverter->resampler, inputFrameCount, pOutputFrameCount); - } else { - *pOutputFrameCount = inputFrameCount; /* 1:1 */ - return MA_SUCCESS; - } -} - -MA_API ma_result ma_data_converter_get_input_channel_map(const ma_data_converter* pConverter, ma_channel* pChannelMap, size_t channelMapCap) -{ - if (pConverter == NULL || pChannelMap == NULL) { - return MA_INVALID_ARGS; - } - - if (pConverter->hasChannelConverter) { - ma_channel_converter_get_output_channel_map(&pConverter->channelConverter, pChannelMap, channelMapCap); - } else { - ma_channel_map_init_standard(ma_standard_channel_map_default, pChannelMap, channelMapCap, pConverter->channelsOut); - } - - return MA_SUCCESS; -} - -MA_API ma_result ma_data_converter_get_output_channel_map(const ma_data_converter* pConverter, ma_channel* pChannelMap, size_t channelMapCap) -{ - if (pConverter == NULL || pChannelMap == NULL) { - return MA_INVALID_ARGS; - } - - if (pConverter->hasChannelConverter) { - ma_channel_converter_get_input_channel_map(&pConverter->channelConverter, pChannelMap, channelMapCap); - } else { - ma_channel_map_init_standard(ma_standard_channel_map_default, pChannelMap, channelMapCap, pConverter->channelsIn); - } - - return MA_SUCCESS; -} - -MA_API ma_result ma_data_converter_reset(ma_data_converter* pConverter) -{ - if (pConverter == NULL) { - return MA_INVALID_ARGS; - } - - /* There's nothing to do if we're not resampling. */ - if (pConverter->hasResampler == MA_FALSE) { - return MA_SUCCESS; - } - - return ma_resampler_reset(&pConverter->resampler); -} - - - -/************************************************************************************************************************************************************** - -Channel Maps - -**************************************************************************************************************************************************************/ -static ma_channel ma_channel_map_init_standard_channel(ma_standard_channel_map standardChannelMap, ma_uint32 channelCount, ma_uint32 channelIndex); - -MA_API ma_channel ma_channel_map_get_channel(const ma_channel* pChannelMap, ma_uint32 channelCount, ma_uint32 channelIndex) -{ - if (pChannelMap == NULL) { - return ma_channel_map_init_standard_channel(ma_standard_channel_map_default, channelCount, channelIndex); - } else { - if (channelIndex >= channelCount) { - return MA_CHANNEL_NONE; - } - - return pChannelMap[channelIndex]; - } -} - -MA_API void ma_channel_map_init_blank(ma_channel* pChannelMap, ma_uint32 channels) -{ - if (pChannelMap == NULL) { - return; - } - - MA_ZERO_MEMORY(pChannelMap, sizeof(*pChannelMap) * channels); -} - - -static ma_channel ma_channel_map_init_standard_channel_microsoft(ma_uint32 channelCount, ma_uint32 channelIndex) -{ - if (channelCount == 0 || channelIndex >= channelCount) { - return MA_CHANNEL_NONE; - } - - /* This is the Microsoft channel map. Based off the speaker configurations mentioned here: https://docs.microsoft.com/en-us/windows-hardware/drivers/ddi/content/ksmedia/ns-ksmedia-ksaudio_channel_config */ - switch (channelCount) - { - case 0: return MA_CHANNEL_NONE; - - case 1: - { - return MA_CHANNEL_MONO; - } break; - - case 2: - { - switch (channelIndex) { - case 0: return MA_CHANNEL_FRONT_LEFT; - case 1: return MA_CHANNEL_FRONT_RIGHT; - } - } break; - - case 3: /* No defined, but best guess. */ - { - switch (channelIndex) { - case 0: return MA_CHANNEL_FRONT_LEFT; - case 1: return MA_CHANNEL_FRONT_RIGHT; - case 2: return MA_CHANNEL_FRONT_CENTER; - } - } break; - - case 4: - { - switch (channelIndex) { - #ifndef MA_USE_QUAD_MICROSOFT_CHANNEL_MAP - /* Surround. Using the Surround profile has the advantage of the 3rd channel (MA_CHANNEL_FRONT_CENTER) mapping nicely with higher channel counts. */ - case 0: return MA_CHANNEL_FRONT_LEFT; - case 1: return MA_CHANNEL_FRONT_RIGHT; - case 2: return MA_CHANNEL_FRONT_CENTER; - case 3: return MA_CHANNEL_BACK_CENTER; - #else - /* Quad. */ - case 0: return MA_CHANNEL_FRONT_LEFT; - case 1: return MA_CHANNEL_FRONT_RIGHT; - case 2: return MA_CHANNEL_BACK_LEFT; - case 3: return MA_CHANNEL_BACK_RIGHT; - #endif - } - } break; - - case 5: /* Not defined, but best guess. */ - { - switch (channelIndex) { - case 0: return MA_CHANNEL_FRONT_LEFT; - case 1: return MA_CHANNEL_FRONT_RIGHT; - case 2: return MA_CHANNEL_FRONT_CENTER; - case 3: return MA_CHANNEL_BACK_LEFT; - case 4: return MA_CHANNEL_BACK_RIGHT; - } - } break; - - case 6: - { - switch (channelIndex) { - case 0: return MA_CHANNEL_FRONT_LEFT; - case 1: return MA_CHANNEL_FRONT_RIGHT; - case 2: return MA_CHANNEL_FRONT_CENTER; - case 3: return MA_CHANNEL_LFE; - case 4: return MA_CHANNEL_SIDE_LEFT; - case 5: return MA_CHANNEL_SIDE_RIGHT; - } - } break; - - case 7: /* Not defined, but best guess. */ - { - switch (channelIndex) { - case 0: return MA_CHANNEL_FRONT_LEFT; - case 1: return MA_CHANNEL_FRONT_RIGHT; - case 2: return MA_CHANNEL_FRONT_CENTER; - case 3: return MA_CHANNEL_LFE; - case 4: return MA_CHANNEL_BACK_CENTER; - case 5: return MA_CHANNEL_SIDE_LEFT; - case 6: return MA_CHANNEL_SIDE_RIGHT; - } - } break; - - case 8: - default: - { - switch (channelIndex) { - case 0: return MA_CHANNEL_FRONT_LEFT; - case 1: return MA_CHANNEL_FRONT_RIGHT; - case 2: return MA_CHANNEL_FRONT_CENTER; - case 3: return MA_CHANNEL_LFE; - case 4: return MA_CHANNEL_BACK_LEFT; - case 5: return MA_CHANNEL_BACK_RIGHT; - case 6: return MA_CHANNEL_SIDE_LEFT; - case 7: return MA_CHANNEL_SIDE_RIGHT; - } - } break; - } - - if (channelCount > 8) { - if (channelIndex < 32) { /* We have 32 AUX channels. */ - return (ma_channel)(MA_CHANNEL_AUX_0 + (channelIndex - 8)); - } - } - - /* Getting here means we don't know how to map the channel position so just return MA_CHANNEL_NONE. */ - return MA_CHANNEL_NONE; -} - -static ma_channel ma_channel_map_init_standard_channel_alsa(ma_uint32 channelCount, ma_uint32 channelIndex) -{ - switch (channelCount) - { - case 0: return MA_CHANNEL_NONE; - - case 1: - { - return MA_CHANNEL_MONO; - } break; - - case 2: - { - switch (channelIndex) { - case 0: return MA_CHANNEL_FRONT_LEFT; - case 1: return MA_CHANNEL_FRONT_RIGHT; - } - } break; - - case 3: - { - switch (channelIndex) { - case 0: return MA_CHANNEL_FRONT_LEFT; - case 1: return MA_CHANNEL_FRONT_RIGHT; - case 2: return MA_CHANNEL_FRONT_CENTER; - } - } break; - - case 4: - { - switch (channelIndex) { - case 0: return MA_CHANNEL_FRONT_LEFT; - case 1: return MA_CHANNEL_FRONT_RIGHT; - case 2: return MA_CHANNEL_BACK_LEFT; - case 3: return MA_CHANNEL_BACK_RIGHT; - } - } break; - - case 5: - { - switch (channelIndex) { - case 0: return MA_CHANNEL_FRONT_LEFT; - case 1: return MA_CHANNEL_FRONT_RIGHT; - case 2: return MA_CHANNEL_BACK_LEFT; - case 3: return MA_CHANNEL_BACK_RIGHT; - case 4: return MA_CHANNEL_FRONT_CENTER; - } - } break; - - case 6: - { - switch (channelIndex) { - case 0: return MA_CHANNEL_FRONT_LEFT; - case 1: return MA_CHANNEL_FRONT_RIGHT; - case 2: return MA_CHANNEL_BACK_LEFT; - case 3: return MA_CHANNEL_BACK_RIGHT; - case 4: return MA_CHANNEL_FRONT_CENTER; - case 5: return MA_CHANNEL_LFE; - } - } break; - - case 7: - { - switch (channelIndex) { - case 0: return MA_CHANNEL_FRONT_LEFT; - case 1: return MA_CHANNEL_FRONT_RIGHT; - case 2: return MA_CHANNEL_BACK_LEFT; - case 3: return MA_CHANNEL_BACK_RIGHT; - case 4: return MA_CHANNEL_FRONT_CENTER; - case 5: return MA_CHANNEL_LFE; - case 6: return MA_CHANNEL_BACK_CENTER; - } - } break; - - case 8: - default: - { - switch (channelIndex) { - case 0: return MA_CHANNEL_FRONT_LEFT; - case 1: return MA_CHANNEL_FRONT_RIGHT; - case 2: return MA_CHANNEL_BACK_LEFT; - case 3: return MA_CHANNEL_BACK_RIGHT; - case 4: return MA_CHANNEL_FRONT_CENTER; - case 5: return MA_CHANNEL_LFE; - case 6: return MA_CHANNEL_SIDE_LEFT; - case 7: return MA_CHANNEL_SIDE_RIGHT; - } - } break; - } - - if (channelCount > 8) { - if (channelIndex < 32) { /* We have 32 AUX channels. */ - return (ma_channel)(MA_CHANNEL_AUX_0 + (channelIndex - 8)); - } - } - - /* Getting here means we don't know how to map the channel position so just return MA_CHANNEL_NONE. */ - return MA_CHANNEL_NONE; -} - -static ma_channel ma_channel_map_init_standard_channel_rfc3551(ma_uint32 channelCount, ma_uint32 channelIndex) -{ - switch (channelCount) - { - case 0: return MA_CHANNEL_NONE; - - case 1: - { - return MA_CHANNEL_MONO; - } break; - - case 2: - { - switch (channelIndex) { - case 0: return MA_CHANNEL_FRONT_LEFT; - case 1: return MA_CHANNEL_FRONT_RIGHT; - } - } break; - - case 3: - { - switch (channelIndex) { - case 0: return MA_CHANNEL_FRONT_LEFT; - case 1: return MA_CHANNEL_FRONT_RIGHT; - case 2: return MA_CHANNEL_FRONT_CENTER; - } - } break; - - case 4: - { - switch (channelIndex) { - case 0: return MA_CHANNEL_FRONT_LEFT; - case 2: return MA_CHANNEL_FRONT_CENTER; - case 1: return MA_CHANNEL_FRONT_RIGHT; - case 3: return MA_CHANNEL_BACK_CENTER; - } - } break; - - case 5: - { - switch (channelIndex) { - case 0: return MA_CHANNEL_FRONT_LEFT; - case 1: return MA_CHANNEL_FRONT_RIGHT; - case 2: return MA_CHANNEL_FRONT_CENTER; - case 3: return MA_CHANNEL_BACK_LEFT; - case 4: return MA_CHANNEL_BACK_RIGHT; - } - } break; - - case 6: - default: - { - switch (channelIndex) { - case 0: return MA_CHANNEL_FRONT_LEFT; - case 1: return MA_CHANNEL_SIDE_LEFT; - case 2: return MA_CHANNEL_FRONT_CENTER; - case 3: return MA_CHANNEL_FRONT_RIGHT; - case 4: return MA_CHANNEL_SIDE_RIGHT; - case 5: return MA_CHANNEL_BACK_CENTER; - } - } break; - } - - if (channelCount > 6) { - if (channelIndex < 32) { /* We have 32 AUX channels. */ - return (ma_channel)(MA_CHANNEL_AUX_0 + (channelIndex - 6)); - } - } - - /* Getting here means we don't know how to map the channel position so just return MA_CHANNEL_NONE. */ - return MA_CHANNEL_NONE; -} - -static ma_channel ma_channel_map_init_standard_channel_flac(ma_uint32 channelCount, ma_uint32 channelIndex) -{ - switch (channelCount) - { - case 0: return MA_CHANNEL_NONE; - - case 1: - { - return MA_CHANNEL_MONO; - } break; - - case 2: - { - switch (channelIndex) { - case 0: return MA_CHANNEL_FRONT_LEFT; - case 1: return MA_CHANNEL_FRONT_RIGHT; - } - } break; - - case 3: - { - switch (channelIndex) { - case 0: return MA_CHANNEL_FRONT_LEFT; - case 1: return MA_CHANNEL_FRONT_RIGHT; - case 2: return MA_CHANNEL_FRONT_CENTER; - } - } break; - - case 4: - { - switch (channelIndex) { - case 0: return MA_CHANNEL_FRONT_LEFT; - case 1: return MA_CHANNEL_FRONT_RIGHT; - case 2: return MA_CHANNEL_BACK_LEFT; - case 3: return MA_CHANNEL_BACK_RIGHT; - } - } break; - - case 5: - { - switch (channelIndex) { - case 0: return MA_CHANNEL_FRONT_LEFT; - case 1: return MA_CHANNEL_FRONT_RIGHT; - case 2: return MA_CHANNEL_FRONT_CENTER; - case 3: return MA_CHANNEL_BACK_LEFT; - case 4: return MA_CHANNEL_BACK_RIGHT; - } - } break; - - case 6: - { - switch (channelIndex) { - case 0: return MA_CHANNEL_FRONT_LEFT; - case 1: return MA_CHANNEL_FRONT_RIGHT; - case 2: return MA_CHANNEL_FRONT_CENTER; - case 3: return MA_CHANNEL_LFE; - case 4: return MA_CHANNEL_BACK_LEFT; - case 5: return MA_CHANNEL_BACK_RIGHT; - } - } break; - - case 7: - { - switch (channelIndex) { - case 0: return MA_CHANNEL_FRONT_LEFT; - case 1: return MA_CHANNEL_FRONT_RIGHT; - case 2: return MA_CHANNEL_FRONT_CENTER; - case 3: return MA_CHANNEL_LFE; - case 4: return MA_CHANNEL_BACK_CENTER; - case 5: return MA_CHANNEL_SIDE_LEFT; - case 6: return MA_CHANNEL_SIDE_RIGHT; - } - } break; - - case 8: - default: - { - switch (channelIndex) { - case 0: return MA_CHANNEL_FRONT_LEFT; - case 1: return MA_CHANNEL_FRONT_RIGHT; - case 2: return MA_CHANNEL_FRONT_CENTER; - case 3: return MA_CHANNEL_LFE; - case 4: return MA_CHANNEL_BACK_LEFT; - case 5: return MA_CHANNEL_BACK_RIGHT; - case 6: return MA_CHANNEL_SIDE_LEFT; - case 7: return MA_CHANNEL_SIDE_RIGHT; - } - } break; - } - - if (channelCount > 8) { - if (channelIndex < 32) { /* We have 32 AUX channels. */ - return (ma_channel)(MA_CHANNEL_AUX_0 + (channelIndex - 8)); - } - } - - /* Getting here means we don't know how to map the channel position so just return MA_CHANNEL_NONE. */ - return MA_CHANNEL_NONE; -} - -static ma_channel ma_channel_map_init_standard_channel_vorbis(ma_uint32 channelCount, ma_uint32 channelIndex) -{ - switch (channelCount) - { - case 0: return MA_CHANNEL_NONE; - - case 1: - { - return MA_CHANNEL_MONO; - } break; - - case 2: - { - switch (channelIndex) { - case 0: return MA_CHANNEL_FRONT_LEFT; - case 1: return MA_CHANNEL_FRONT_RIGHT; - } - } break; - - case 3: - { - switch (channelIndex) { - case 0: return MA_CHANNEL_FRONT_LEFT; - case 1: return MA_CHANNEL_FRONT_CENTER; - case 2: return MA_CHANNEL_FRONT_RIGHT; - } - } break; - - case 4: - { - switch (channelIndex) { - case 0: return MA_CHANNEL_FRONT_LEFT; - case 1: return MA_CHANNEL_FRONT_RIGHT; - case 2: return MA_CHANNEL_BACK_LEFT; - case 3: return MA_CHANNEL_BACK_RIGHT; - } - } break; - - case 5: - { - switch (channelIndex) { - case 0: return MA_CHANNEL_FRONT_LEFT; - case 1: return MA_CHANNEL_FRONT_CENTER; - case 2: return MA_CHANNEL_FRONT_RIGHT; - case 3: return MA_CHANNEL_BACK_LEFT; - case 4: return MA_CHANNEL_BACK_RIGHT; - } - } break; - - case 6: - { - switch (channelIndex) { - case 0: return MA_CHANNEL_FRONT_LEFT; - case 1: return MA_CHANNEL_FRONT_CENTER; - case 2: return MA_CHANNEL_FRONT_RIGHT; - case 3: return MA_CHANNEL_BACK_LEFT; - case 4: return MA_CHANNEL_BACK_RIGHT; - case 5: return MA_CHANNEL_LFE; - } - } break; - - case 7: - { - switch (channelIndex) { - case 0: return MA_CHANNEL_FRONT_LEFT; - case 1: return MA_CHANNEL_FRONT_CENTER; - case 2: return MA_CHANNEL_FRONT_RIGHT; - case 3: return MA_CHANNEL_SIDE_LEFT; - case 4: return MA_CHANNEL_SIDE_RIGHT; - case 5: return MA_CHANNEL_BACK_CENTER; - case 6: return MA_CHANNEL_LFE; - } - } break; - - case 8: - default: - { - switch (channelIndex) { - case 0: return MA_CHANNEL_FRONT_LEFT; - case 1: return MA_CHANNEL_FRONT_CENTER; - case 2: return MA_CHANNEL_FRONT_RIGHT; - case 3: return MA_CHANNEL_SIDE_LEFT; - case 4: return MA_CHANNEL_SIDE_RIGHT; - case 5: return MA_CHANNEL_BACK_LEFT; - case 6: return MA_CHANNEL_BACK_RIGHT; - case 7: return MA_CHANNEL_LFE; - } - } break; - } - - if (channelCount > 8) { - if (channelIndex < 32) { /* We have 32 AUX channels. */ - return (ma_channel)(MA_CHANNEL_AUX_0 + (channelIndex - 8)); - } - } - - /* Getting here means we don't know how to map the channel position so just return MA_CHANNEL_NONE. */ - return MA_CHANNEL_NONE; -} - -static ma_channel ma_channel_map_init_standard_channel_sound4(ma_uint32 channelCount, ma_uint32 channelIndex) -{ - switch (channelCount) - { - case 0: return MA_CHANNEL_NONE; - - case 1: - { - return MA_CHANNEL_MONO; - } break; - - case 2: - { - switch (channelIndex) { - case 0: return MA_CHANNEL_FRONT_LEFT; - case 1: return MA_CHANNEL_FRONT_RIGHT; - } - } break; - - case 3: - { - switch (channelIndex) { - case 0: return MA_CHANNEL_FRONT_LEFT; - case 1: return MA_CHANNEL_FRONT_RIGHT; - case 2: return MA_CHANNEL_FRONT_CENTER; - } - } break; - - case 4: - { - switch (channelIndex) { - case 0: return MA_CHANNEL_FRONT_LEFT; - case 1: return MA_CHANNEL_FRONT_RIGHT; - case 2: return MA_CHANNEL_BACK_LEFT; - case 3: return MA_CHANNEL_BACK_RIGHT; - } - } break; - - case 5: - { - switch (channelIndex) { - case 0: return MA_CHANNEL_FRONT_LEFT; - case 1: return MA_CHANNEL_FRONT_RIGHT; - case 2: return MA_CHANNEL_FRONT_CENTER; - case 3: return MA_CHANNEL_BACK_LEFT; - case 4: return MA_CHANNEL_BACK_RIGHT; - } - } break; - - case 6: - { - switch (channelIndex) { - case 0: return MA_CHANNEL_FRONT_LEFT; - case 1: return MA_CHANNEL_FRONT_CENTER; - case 2: return MA_CHANNEL_FRONT_RIGHT; - case 3: return MA_CHANNEL_BACK_LEFT; - case 4: return MA_CHANNEL_BACK_RIGHT; - case 5: return MA_CHANNEL_LFE; - } - } break; - - case 7: - { - switch (channelIndex) { - case 0: return MA_CHANNEL_FRONT_LEFT; - case 1: return MA_CHANNEL_FRONT_CENTER; - case 2: return MA_CHANNEL_FRONT_RIGHT; - case 3: return MA_CHANNEL_SIDE_LEFT; - case 4: return MA_CHANNEL_SIDE_RIGHT; - case 5: return MA_CHANNEL_BACK_CENTER; - case 6: return MA_CHANNEL_LFE; - } - } break; - - case 8: - default: - { - switch (channelIndex) { - case 0: return MA_CHANNEL_FRONT_LEFT; - case 1: return MA_CHANNEL_FRONT_CENTER; - case 2: return MA_CHANNEL_FRONT_RIGHT; - case 3: return MA_CHANNEL_SIDE_LEFT; - case 4: return MA_CHANNEL_SIDE_RIGHT; - case 5: return MA_CHANNEL_BACK_LEFT; - case 6: return MA_CHANNEL_BACK_RIGHT; - case 7: return MA_CHANNEL_LFE; - } - } break; - } - - if (channelCount > 8) { - if (channelIndex < 32) { /* We have 32 AUX channels. */ - return (ma_channel)(MA_CHANNEL_AUX_0 + (channelIndex - 8)); - } - } - - /* Getting here means we don't know how to map the channel position so just return MA_CHANNEL_NONE. */ - return MA_CHANNEL_NONE; -} - -static ma_channel ma_channel_map_init_standard_channel_sndio(ma_uint32 channelCount, ma_uint32 channelIndex) -{ - switch (channelCount) - { - case 0: return MA_CHANNEL_NONE; - - case 1: - { - return MA_CHANNEL_MONO; - } break; - - case 2: - { - switch (channelIndex) { - case 0: return MA_CHANNEL_FRONT_LEFT; - case 1: return MA_CHANNEL_FRONT_RIGHT; - } - } break; - - case 3: /* No defined, but best guess. */ - { - switch (channelIndex) { - case 0: return MA_CHANNEL_FRONT_LEFT; - case 1: return MA_CHANNEL_FRONT_RIGHT; - case 2: return MA_CHANNEL_FRONT_CENTER; - } - } break; - - case 4: - { - switch (channelIndex) { - case 0: return MA_CHANNEL_FRONT_LEFT; - case 1: return MA_CHANNEL_FRONT_RIGHT; - case 2: return MA_CHANNEL_BACK_LEFT; - case 3: return MA_CHANNEL_BACK_RIGHT; - } - } break; - - case 5: /* Not defined, but best guess. */ - { - switch (channelIndex) { - case 0: return MA_CHANNEL_FRONT_LEFT; - case 1: return MA_CHANNEL_FRONT_RIGHT; - case 2: return MA_CHANNEL_BACK_LEFT; - case 3: return MA_CHANNEL_BACK_RIGHT; - case 4: return MA_CHANNEL_FRONT_CENTER; - } - } break; - - case 6: - default: - { - switch (channelIndex) { - case 0: return MA_CHANNEL_FRONT_LEFT; - case 1: return MA_CHANNEL_FRONT_RIGHT; - case 2: return MA_CHANNEL_BACK_LEFT; - case 3: return MA_CHANNEL_BACK_RIGHT; - case 4: return MA_CHANNEL_FRONT_CENTER; - case 5: return MA_CHANNEL_LFE; - } - } break; - } - - if (channelCount > 6) { - if (channelIndex < 32) { /* We have 32 AUX channels. */ - return (ma_channel)(MA_CHANNEL_AUX_0 + (channelIndex - 6)); - } - } - - /* Getting here means we don't know how to map the channel position so just return MA_CHANNEL_NONE. */ - return MA_CHANNEL_NONE; -} - - -static ma_channel ma_channel_map_init_standard_channel(ma_standard_channel_map standardChannelMap, ma_uint32 channelCount, ma_uint32 channelIndex) -{ - if (channelCount == 0 || channelIndex >= channelCount) { - return MA_CHANNEL_NONE; - } - - switch (standardChannelMap) - { - case ma_standard_channel_map_alsa: - { - return ma_channel_map_init_standard_channel_alsa(channelCount, channelIndex); - } break; - - case ma_standard_channel_map_rfc3551: - { - return ma_channel_map_init_standard_channel_rfc3551(channelCount, channelIndex); - } break; - - case ma_standard_channel_map_flac: - { - return ma_channel_map_init_standard_channel_flac(channelCount, channelIndex); - } break; - - case ma_standard_channel_map_vorbis: - { - return ma_channel_map_init_standard_channel_vorbis(channelCount, channelIndex); - } break; - - case ma_standard_channel_map_sound4: - { - return ma_channel_map_init_standard_channel_sound4(channelCount, channelIndex); - } break; - - case ma_standard_channel_map_sndio: - { - return ma_channel_map_init_standard_channel_sndio(channelCount, channelIndex); - } break; - - case ma_standard_channel_map_microsoft: /* Also default. */ - /*case ma_standard_channel_map_default;*/ - default: - { - return ma_channel_map_init_standard_channel_microsoft(channelCount, channelIndex); - } break; - } -} - -MA_API void ma_channel_map_init_standard(ma_standard_channel_map standardChannelMap, ma_channel* pChannelMap, size_t channelMapCap, ma_uint32 channels) -{ - ma_uint32 iChannel; - - if (pChannelMap == NULL || channelMapCap == 0 || channels == 0) { - return; - } - - for (iChannel = 0; iChannel < channels; iChannel += 1) { - if (channelMapCap == 0) { - break; /* Ran out of room. */ - } - - pChannelMap[0] = ma_channel_map_init_standard_channel(standardChannelMap, channels, iChannel); - pChannelMap += 1; - channelMapCap -= 1; - } -} - -MA_API void ma_channel_map_copy(ma_channel* pOut, const ma_channel* pIn, ma_uint32 channels) -{ - if (pOut != NULL && pIn != NULL && channels > 0) { - MA_COPY_MEMORY(pOut, pIn, sizeof(*pOut) * channels); - } -} - -MA_API void ma_channel_map_copy_or_default(ma_channel* pOut, size_t channelMapCapOut, const ma_channel* pIn, ma_uint32 channels) -{ - if (pOut == NULL || channels == 0) { - return; - } - - if (pIn != NULL) { - ma_channel_map_copy(pOut, pIn, channels); - } else { - ma_channel_map_init_standard(ma_standard_channel_map_default, pOut, channelMapCapOut, channels); - } -} - -MA_API ma_bool32 ma_channel_map_is_valid(const ma_channel* pChannelMap, ma_uint32 channels) -{ - /* A channel count of 0 is invalid. */ - if (channels == 0) { - return MA_FALSE; - } - - /* It does not make sense to have a mono channel when there is more than 1 channel. */ - if (channels > 1) { - ma_uint32 iChannel; - for (iChannel = 0; iChannel < channels; ++iChannel) { - if (ma_channel_map_get_channel(pChannelMap, channels, iChannel) == MA_CHANNEL_MONO) { - return MA_FALSE; - } - } - } - - return MA_TRUE; -} - -MA_API ma_bool32 ma_channel_map_is_equal(const ma_channel* pChannelMapA, const ma_channel* pChannelMapB, ma_uint32 channels) -{ - ma_uint32 iChannel; - - if (pChannelMapA == pChannelMapB) { - return MA_TRUE; - } - - for (iChannel = 0; iChannel < channels; ++iChannel) { - if (ma_channel_map_get_channel(pChannelMapA, channels, iChannel) != ma_channel_map_get_channel(pChannelMapB, channels, iChannel)) { - return MA_FALSE; - } - } - - return MA_TRUE; -} - -MA_API ma_bool32 ma_channel_map_is_blank(const ma_channel* pChannelMap, ma_uint32 channels) -{ - ma_uint32 iChannel; - - /* A null channel map is equivalent to the default channel map. */ - if (pChannelMap == NULL) { - return MA_FALSE; - } - - for (iChannel = 0; iChannel < channels; ++iChannel) { - if (pChannelMap[iChannel] != MA_CHANNEL_NONE) { - return MA_FALSE; - } - } - - return MA_TRUE; -} - -MA_API ma_bool32 ma_channel_map_contains_channel_position(ma_uint32 channels, const ma_channel* pChannelMap, ma_channel channelPosition) -{ - return ma_channel_map_find_channel_position(channels, pChannelMap, channelPosition, NULL); -} - -MA_API ma_bool32 ma_channel_map_find_channel_position(ma_uint32 channels, const ma_channel* pChannelMap, ma_channel channelPosition, ma_uint32* pChannelIndex) -{ - ma_uint32 iChannel; - - if (pChannelIndex != NULL) { - *pChannelIndex = (ma_uint32)-1; - } - - for (iChannel = 0; iChannel < channels; ++iChannel) { - if (ma_channel_map_get_channel(pChannelMap, channels, iChannel) == channelPosition) { - if (pChannelIndex != NULL) { - *pChannelIndex = iChannel; - } - - return MA_TRUE; - } - } - - /* Getting here means the channel position was not found. */ - return MA_FALSE; -} - -MA_API size_t ma_channel_map_to_string(const ma_channel* pChannelMap, ma_uint32 channels, char* pBufferOut, size_t bufferCap) -{ - size_t len; - ma_uint32 iChannel; - - len = 0; - - for (iChannel = 0; iChannel < channels; iChannel += 1) { - const char* pChannelStr = ma_channel_position_to_string(ma_channel_map_get_channel(pChannelMap, channels, iChannel)); - size_t channelStrLen = strlen(pChannelStr); - - /* Append the string if necessary. */ - if (pBufferOut != NULL && bufferCap > len + channelStrLen) { - MA_COPY_MEMORY(pBufferOut + len, pChannelStr, channelStrLen); - } - len += channelStrLen; - - /* Append a space if it's not the last item. */ - if (iChannel+1 < channels) { - if (pBufferOut != NULL && bufferCap > len + 1) { - pBufferOut[len] = ' '; - } - len += 1; - } - } - - /* Null terminate. Don't increment the length here. */ - if (pBufferOut != NULL && bufferCap > len + 1) { - pBufferOut[len] = '\0'; - } - - return len; -} - -MA_API const char* ma_channel_position_to_string(ma_channel channel) -{ - switch (channel) - { - case MA_CHANNEL_NONE : return "CHANNEL_NONE"; - case MA_CHANNEL_MONO : return "CHANNEL_MONO"; - case MA_CHANNEL_FRONT_LEFT : return "CHANNEL_FRONT_LEFT"; - case MA_CHANNEL_FRONT_RIGHT : return "CHANNEL_FRONT_RIGHT"; - case MA_CHANNEL_FRONT_CENTER : return "CHANNEL_FRONT_CENTER"; - case MA_CHANNEL_LFE : return "CHANNEL_LFE"; - case MA_CHANNEL_BACK_LEFT : return "CHANNEL_BACK_LEFT"; - case MA_CHANNEL_BACK_RIGHT : return "CHANNEL_BACK_RIGHT"; - case MA_CHANNEL_FRONT_LEFT_CENTER : return "CHANNEL_FRONT_LEFT_CENTER"; - case MA_CHANNEL_FRONT_RIGHT_CENTER: return "CHANNEL_FRONT_RIGHT_CENTER"; - case MA_CHANNEL_BACK_CENTER : return "CHANNEL_BACK_CENTER"; - case MA_CHANNEL_SIDE_LEFT : return "CHANNEL_SIDE_LEFT"; - case MA_CHANNEL_SIDE_RIGHT : return "CHANNEL_SIDE_RIGHT"; - case MA_CHANNEL_TOP_CENTER : return "CHANNEL_TOP_CENTER"; - case MA_CHANNEL_TOP_FRONT_LEFT : return "CHANNEL_TOP_FRONT_LEFT"; - case MA_CHANNEL_TOP_FRONT_CENTER : return "CHANNEL_TOP_FRONT_CENTER"; - case MA_CHANNEL_TOP_FRONT_RIGHT : return "CHANNEL_TOP_FRONT_RIGHT"; - case MA_CHANNEL_TOP_BACK_LEFT : return "CHANNEL_TOP_BACK_LEFT"; - case MA_CHANNEL_TOP_BACK_CENTER : return "CHANNEL_TOP_BACK_CENTER"; - case MA_CHANNEL_TOP_BACK_RIGHT : return "CHANNEL_TOP_BACK_RIGHT"; - case MA_CHANNEL_AUX_0 : return "CHANNEL_AUX_0"; - case MA_CHANNEL_AUX_1 : return "CHANNEL_AUX_1"; - case MA_CHANNEL_AUX_2 : return "CHANNEL_AUX_2"; - case MA_CHANNEL_AUX_3 : return "CHANNEL_AUX_3"; - case MA_CHANNEL_AUX_4 : return "CHANNEL_AUX_4"; - case MA_CHANNEL_AUX_5 : return "CHANNEL_AUX_5"; - case MA_CHANNEL_AUX_6 : return "CHANNEL_AUX_6"; - case MA_CHANNEL_AUX_7 : return "CHANNEL_AUX_7"; - case MA_CHANNEL_AUX_8 : return "CHANNEL_AUX_8"; - case MA_CHANNEL_AUX_9 : return "CHANNEL_AUX_9"; - case MA_CHANNEL_AUX_10 : return "CHANNEL_AUX_10"; - case MA_CHANNEL_AUX_11 : return "CHANNEL_AUX_11"; - case MA_CHANNEL_AUX_12 : return "CHANNEL_AUX_12"; - case MA_CHANNEL_AUX_13 : return "CHANNEL_AUX_13"; - case MA_CHANNEL_AUX_14 : return "CHANNEL_AUX_14"; - case MA_CHANNEL_AUX_15 : return "CHANNEL_AUX_15"; - case MA_CHANNEL_AUX_16 : return "CHANNEL_AUX_16"; - case MA_CHANNEL_AUX_17 : return "CHANNEL_AUX_17"; - case MA_CHANNEL_AUX_18 : return "CHANNEL_AUX_18"; - case MA_CHANNEL_AUX_19 : return "CHANNEL_AUX_19"; - case MA_CHANNEL_AUX_20 : return "CHANNEL_AUX_20"; - case MA_CHANNEL_AUX_21 : return "CHANNEL_AUX_21"; - case MA_CHANNEL_AUX_22 : return "CHANNEL_AUX_22"; - case MA_CHANNEL_AUX_23 : return "CHANNEL_AUX_23"; - case MA_CHANNEL_AUX_24 : return "CHANNEL_AUX_24"; - case MA_CHANNEL_AUX_25 : return "CHANNEL_AUX_25"; - case MA_CHANNEL_AUX_26 : return "CHANNEL_AUX_26"; - case MA_CHANNEL_AUX_27 : return "CHANNEL_AUX_27"; - case MA_CHANNEL_AUX_28 : return "CHANNEL_AUX_28"; - case MA_CHANNEL_AUX_29 : return "CHANNEL_AUX_29"; - case MA_CHANNEL_AUX_30 : return "CHANNEL_AUX_30"; - case MA_CHANNEL_AUX_31 : return "CHANNEL_AUX_31"; - default: break; - } - - return "UNKNOWN"; -} - - - -/************************************************************************************************************************************************************** - -Conversion Helpers - -**************************************************************************************************************************************************************/ -MA_API ma_uint64 ma_convert_frames(void* pOut, ma_uint64 frameCountOut, ma_format formatOut, ma_uint32 channelsOut, ma_uint32 sampleRateOut, const void* pIn, ma_uint64 frameCountIn, ma_format formatIn, ma_uint32 channelsIn, ma_uint32 sampleRateIn) -{ - ma_data_converter_config config; - - config = ma_data_converter_config_init(formatIn, formatOut, channelsIn, channelsOut, sampleRateIn, sampleRateOut); - config.resampling.linear.lpfOrder = ma_min(MA_DEFAULT_RESAMPLER_LPF_ORDER, MA_MAX_FILTER_ORDER); - - return ma_convert_frames_ex(pOut, frameCountOut, pIn, frameCountIn, &config); -} - -MA_API ma_uint64 ma_convert_frames_ex(void* pOut, ma_uint64 frameCountOut, const void* pIn, ma_uint64 frameCountIn, const ma_data_converter_config* pConfig) -{ - ma_result result; - ma_data_converter converter; - - if (frameCountIn == 0 || pConfig == NULL) { - return 0; - } - - result = ma_data_converter_init(pConfig, NULL, &converter); - if (result != MA_SUCCESS) { - return 0; /* Failed to initialize the data converter. */ - } - - if (pOut == NULL) { - result = ma_data_converter_get_expected_output_frame_count(&converter, frameCountIn, &frameCountOut); - if (result != MA_SUCCESS) { - if (result == MA_NOT_IMPLEMENTED) { - /* No way to calculate the number of frames, so we'll need to brute force it and loop. */ - frameCountOut = 0; - - while (frameCountIn > 0) { - ma_uint64 framesProcessedIn = frameCountIn; - ma_uint64 framesProcessedOut = 0xFFFFFFFF; - - result = ma_data_converter_process_pcm_frames(&converter, pIn, &framesProcessedIn, NULL, &framesProcessedOut); - if (result != MA_SUCCESS) { - break; - } - - frameCountIn -= framesProcessedIn; - } - } - } - } else { - result = ma_data_converter_process_pcm_frames(&converter, pIn, &frameCountIn, pOut, &frameCountOut); - if (result != MA_SUCCESS) { - frameCountOut = 0; - } - } - - ma_data_converter_uninit(&converter, NULL); - return frameCountOut; -} - - -/************************************************************************************************************************************************************** - -Ring Buffer - -**************************************************************************************************************************************************************/ -static MA_INLINE ma_uint32 ma_rb__extract_offset_in_bytes(ma_uint32 encodedOffset) -{ - return encodedOffset & 0x7FFFFFFF; -} - -static MA_INLINE ma_uint32 ma_rb__extract_offset_loop_flag(ma_uint32 encodedOffset) -{ - return encodedOffset & 0x80000000; -} - -static MA_INLINE void* ma_rb__get_read_ptr(ma_rb* pRB) -{ - MA_ASSERT(pRB != NULL); - return ma_offset_ptr(pRB->pBuffer, ma_rb__extract_offset_in_bytes(ma_atomic_load_32(&pRB->encodedReadOffset))); -} - -static MA_INLINE void* ma_rb__get_write_ptr(ma_rb* pRB) -{ - MA_ASSERT(pRB != NULL); - return ma_offset_ptr(pRB->pBuffer, ma_rb__extract_offset_in_bytes(ma_atomic_load_32(&pRB->encodedWriteOffset))); -} - -static MA_INLINE ma_uint32 ma_rb__construct_offset(ma_uint32 offsetInBytes, ma_uint32 offsetLoopFlag) -{ - return offsetLoopFlag | offsetInBytes; -} - -static MA_INLINE void ma_rb__deconstruct_offset(ma_uint32 encodedOffset, ma_uint32* pOffsetInBytes, ma_uint32* pOffsetLoopFlag) -{ - MA_ASSERT(pOffsetInBytes != NULL); - MA_ASSERT(pOffsetLoopFlag != NULL); - - *pOffsetInBytes = ma_rb__extract_offset_in_bytes(encodedOffset); - *pOffsetLoopFlag = ma_rb__extract_offset_loop_flag(encodedOffset); -} - - -MA_API ma_result ma_rb_init_ex(size_t subbufferSizeInBytes, size_t subbufferCount, size_t subbufferStrideInBytes, void* pOptionalPreallocatedBuffer, const ma_allocation_callbacks* pAllocationCallbacks, ma_rb* pRB) -{ - ma_result result; - const ma_uint32 maxSubBufferSize = 0x7FFFFFFF - (MA_SIMD_ALIGNMENT-1); - - if (pRB == NULL) { - return MA_INVALID_ARGS; - } - - if (subbufferSizeInBytes == 0 || subbufferCount == 0) { - return MA_INVALID_ARGS; - } - - if (subbufferSizeInBytes > maxSubBufferSize) { - return MA_INVALID_ARGS; /* Maximum buffer size is ~2GB. The most significant bit is a flag for use internally. */ - } - - - MA_ZERO_OBJECT(pRB); - - result = ma_allocation_callbacks_init_copy(&pRB->allocationCallbacks, pAllocationCallbacks); - if (result != MA_SUCCESS) { - return result; - } - - pRB->subbufferSizeInBytes = (ma_uint32)subbufferSizeInBytes; - pRB->subbufferCount = (ma_uint32)subbufferCount; - - if (pOptionalPreallocatedBuffer != NULL) { - pRB->subbufferStrideInBytes = (ma_uint32)subbufferStrideInBytes; - pRB->pBuffer = pOptionalPreallocatedBuffer; - } else { - size_t bufferSizeInBytes; - - /* - Here is where we allocate our own buffer. We always want to align this to MA_SIMD_ALIGNMENT for future SIMD optimization opportunity. To do this - we need to make sure the stride is a multiple of MA_SIMD_ALIGNMENT. - */ - pRB->subbufferStrideInBytes = (pRB->subbufferSizeInBytes + (MA_SIMD_ALIGNMENT-1)) & ~MA_SIMD_ALIGNMENT; - - bufferSizeInBytes = (size_t)pRB->subbufferCount*pRB->subbufferStrideInBytes; - pRB->pBuffer = ma_aligned_malloc(bufferSizeInBytes, MA_SIMD_ALIGNMENT, &pRB->allocationCallbacks); - if (pRB->pBuffer == NULL) { - return MA_OUT_OF_MEMORY; - } - - MA_ZERO_MEMORY(pRB->pBuffer, bufferSizeInBytes); - pRB->ownsBuffer = MA_TRUE; - } - - return MA_SUCCESS; -} - -MA_API ma_result ma_rb_init(size_t bufferSizeInBytes, void* pOptionalPreallocatedBuffer, const ma_allocation_callbacks* pAllocationCallbacks, ma_rb* pRB) -{ - return ma_rb_init_ex(bufferSizeInBytes, 1, 0, pOptionalPreallocatedBuffer, pAllocationCallbacks, pRB); -} - -MA_API void ma_rb_uninit(ma_rb* pRB) -{ - if (pRB == NULL) { - return; - } - - if (pRB->ownsBuffer) { - ma_aligned_free(pRB->pBuffer, &pRB->allocationCallbacks); - } -} - -MA_API void ma_rb_reset(ma_rb* pRB) -{ - if (pRB == NULL) { - return; - } - - ma_atomic_exchange_32(&pRB->encodedReadOffset, 0); - ma_atomic_exchange_32(&pRB->encodedWriteOffset, 0); -} - -MA_API ma_result ma_rb_acquire_read(ma_rb* pRB, size_t* pSizeInBytes, void** ppBufferOut) -{ - ma_uint32 writeOffset; - ma_uint32 writeOffsetInBytes; - ma_uint32 writeOffsetLoopFlag; - ma_uint32 readOffset; - ma_uint32 readOffsetInBytes; - ma_uint32 readOffsetLoopFlag; - size_t bytesAvailable; - size_t bytesRequested; - - if (pRB == NULL || pSizeInBytes == NULL || ppBufferOut == NULL) { - return MA_INVALID_ARGS; - } - - /* The returned buffer should never move ahead of the write pointer. */ - writeOffset = ma_atomic_load_32(&pRB->encodedWriteOffset); - ma_rb__deconstruct_offset(writeOffset, &writeOffsetInBytes, &writeOffsetLoopFlag); - - readOffset = ma_atomic_load_32(&pRB->encodedReadOffset); - ma_rb__deconstruct_offset(readOffset, &readOffsetInBytes, &readOffsetLoopFlag); - - /* - The number of bytes available depends on whether or not the read and write pointers are on the same loop iteration. If so, we - can only read up to the write pointer. If not, we can only read up to the end of the buffer. - */ - if (readOffsetLoopFlag == writeOffsetLoopFlag) { - bytesAvailable = writeOffsetInBytes - readOffsetInBytes; - } else { - bytesAvailable = pRB->subbufferSizeInBytes - readOffsetInBytes; - } - - bytesRequested = *pSizeInBytes; - if (bytesRequested > bytesAvailable) { - bytesRequested = bytesAvailable; - } - - *pSizeInBytes = bytesRequested; - (*ppBufferOut) = ma_rb__get_read_ptr(pRB); - - return MA_SUCCESS; -} - -MA_API ma_result ma_rb_commit_read(ma_rb* pRB, size_t sizeInBytes) -{ - ma_uint32 readOffset; - ma_uint32 readOffsetInBytes; - ma_uint32 readOffsetLoopFlag; - ma_uint32 newReadOffsetInBytes; - ma_uint32 newReadOffsetLoopFlag; - - if (pRB == NULL) { - return MA_INVALID_ARGS; - } - - readOffset = ma_atomic_load_32(&pRB->encodedReadOffset); - ma_rb__deconstruct_offset(readOffset, &readOffsetInBytes, &readOffsetLoopFlag); - - /* Check that sizeInBytes is correct. It should never go beyond the end of the buffer. */ - newReadOffsetInBytes = (ma_uint32)(readOffsetInBytes + sizeInBytes); - if (newReadOffsetInBytes > pRB->subbufferSizeInBytes) { - return MA_INVALID_ARGS; /* <-- sizeInBytes will cause the read offset to overflow. */ - } - - /* Move the read pointer back to the start if necessary. */ - newReadOffsetLoopFlag = readOffsetLoopFlag; - if (newReadOffsetInBytes == pRB->subbufferSizeInBytes) { - newReadOffsetInBytes = 0; - newReadOffsetLoopFlag ^= 0x80000000; - } - - ma_atomic_exchange_32(&pRB->encodedReadOffset, ma_rb__construct_offset(newReadOffsetInBytes, newReadOffsetLoopFlag)); - - return MA_SUCCESS; -} - -MA_API ma_result ma_rb_acquire_write(ma_rb* pRB, size_t* pSizeInBytes, void** ppBufferOut) -{ - ma_uint32 readOffset; - ma_uint32 readOffsetInBytes; - ma_uint32 readOffsetLoopFlag; - ma_uint32 writeOffset; - ma_uint32 writeOffsetInBytes; - ma_uint32 writeOffsetLoopFlag; - size_t bytesAvailable; - size_t bytesRequested; - - if (pRB == NULL || pSizeInBytes == NULL || ppBufferOut == NULL) { - return MA_INVALID_ARGS; - } - - /* The returned buffer should never overtake the read buffer. */ - readOffset = ma_atomic_load_32(&pRB->encodedReadOffset); - ma_rb__deconstruct_offset(readOffset, &readOffsetInBytes, &readOffsetLoopFlag); - - writeOffset = ma_atomic_load_32(&pRB->encodedWriteOffset); - ma_rb__deconstruct_offset(writeOffset, &writeOffsetInBytes, &writeOffsetLoopFlag); - - /* - In the case of writing, if the write pointer and the read pointer are on the same loop iteration we can only - write up to the end of the buffer. Otherwise we can only write up to the read pointer. The write pointer should - never overtake the read pointer. - */ - if (writeOffsetLoopFlag == readOffsetLoopFlag) { - bytesAvailable = pRB->subbufferSizeInBytes - writeOffsetInBytes; - } else { - bytesAvailable = readOffsetInBytes - writeOffsetInBytes; - } - - bytesRequested = *pSizeInBytes; - if (bytesRequested > bytesAvailable) { - bytesRequested = bytesAvailable; - } - - *pSizeInBytes = bytesRequested; - *ppBufferOut = ma_rb__get_write_ptr(pRB); - - /* Clear the buffer if desired. */ - if (pRB->clearOnWriteAcquire) { - MA_ZERO_MEMORY(*ppBufferOut, *pSizeInBytes); - } - - return MA_SUCCESS; -} - -MA_API ma_result ma_rb_commit_write(ma_rb* pRB, size_t sizeInBytes) -{ - ma_uint32 writeOffset; - ma_uint32 writeOffsetInBytes; - ma_uint32 writeOffsetLoopFlag; - ma_uint32 newWriteOffsetInBytes; - ma_uint32 newWriteOffsetLoopFlag; - - if (pRB == NULL) { - return MA_INVALID_ARGS; - } - - writeOffset = ma_atomic_load_32(&pRB->encodedWriteOffset); - ma_rb__deconstruct_offset(writeOffset, &writeOffsetInBytes, &writeOffsetLoopFlag); - - /* Check that sizeInBytes is correct. It should never go beyond the end of the buffer. */ - newWriteOffsetInBytes = (ma_uint32)(writeOffsetInBytes + sizeInBytes); - if (newWriteOffsetInBytes > pRB->subbufferSizeInBytes) { - return MA_INVALID_ARGS; /* <-- sizeInBytes will cause the read offset to overflow. */ - } - - /* Move the read pointer back to the start if necessary. */ - newWriteOffsetLoopFlag = writeOffsetLoopFlag; - if (newWriteOffsetInBytes == pRB->subbufferSizeInBytes) { - newWriteOffsetInBytes = 0; - newWriteOffsetLoopFlag ^= 0x80000000; - } - - ma_atomic_exchange_32(&pRB->encodedWriteOffset, ma_rb__construct_offset(newWriteOffsetInBytes, newWriteOffsetLoopFlag)); - - return MA_SUCCESS; -} - -MA_API ma_result ma_rb_seek_read(ma_rb* pRB, size_t offsetInBytes) -{ - ma_uint32 readOffset; - ma_uint32 readOffsetInBytes; - ma_uint32 readOffsetLoopFlag; - ma_uint32 writeOffset; - ma_uint32 writeOffsetInBytes; - ma_uint32 writeOffsetLoopFlag; - ma_uint32 newReadOffsetInBytes; - ma_uint32 newReadOffsetLoopFlag; - - if (pRB == NULL || offsetInBytes > pRB->subbufferSizeInBytes) { - return MA_INVALID_ARGS; - } - - readOffset = ma_atomic_load_32(&pRB->encodedReadOffset); - ma_rb__deconstruct_offset(readOffset, &readOffsetInBytes, &readOffsetLoopFlag); - - writeOffset = ma_atomic_load_32(&pRB->encodedWriteOffset); - ma_rb__deconstruct_offset(writeOffset, &writeOffsetInBytes, &writeOffsetLoopFlag); - - newReadOffsetLoopFlag = readOffsetLoopFlag; - - /* We cannot go past the write buffer. */ - if (readOffsetLoopFlag == writeOffsetLoopFlag) { - if ((readOffsetInBytes + offsetInBytes) > writeOffsetInBytes) { - newReadOffsetInBytes = writeOffsetInBytes; - } else { - newReadOffsetInBytes = (ma_uint32)(readOffsetInBytes + offsetInBytes); - } - } else { - /* May end up looping. */ - if ((readOffsetInBytes + offsetInBytes) >= pRB->subbufferSizeInBytes) { - newReadOffsetInBytes = (ma_uint32)(readOffsetInBytes + offsetInBytes) - pRB->subbufferSizeInBytes; - newReadOffsetLoopFlag ^= 0x80000000; /* <-- Looped. */ - } else { - newReadOffsetInBytes = (ma_uint32)(readOffsetInBytes + offsetInBytes); - } - } - - ma_atomic_exchange_32(&pRB->encodedReadOffset, ma_rb__construct_offset(newReadOffsetInBytes, newReadOffsetLoopFlag)); - return MA_SUCCESS; -} - -MA_API ma_result ma_rb_seek_write(ma_rb* pRB, size_t offsetInBytes) -{ - ma_uint32 readOffset; - ma_uint32 readOffsetInBytes; - ma_uint32 readOffsetLoopFlag; - ma_uint32 writeOffset; - ma_uint32 writeOffsetInBytes; - ma_uint32 writeOffsetLoopFlag; - ma_uint32 newWriteOffsetInBytes; - ma_uint32 newWriteOffsetLoopFlag; - - if (pRB == NULL) { - return MA_INVALID_ARGS; - } - - readOffset = ma_atomic_load_32(&pRB->encodedReadOffset); - ma_rb__deconstruct_offset(readOffset, &readOffsetInBytes, &readOffsetLoopFlag); - - writeOffset = ma_atomic_load_32(&pRB->encodedWriteOffset); - ma_rb__deconstruct_offset(writeOffset, &writeOffsetInBytes, &writeOffsetLoopFlag); - - newWriteOffsetLoopFlag = writeOffsetLoopFlag; - - /* We cannot go past the write buffer. */ - if (readOffsetLoopFlag == writeOffsetLoopFlag) { - /* May end up looping. */ - if ((writeOffsetInBytes + offsetInBytes) >= pRB->subbufferSizeInBytes) { - newWriteOffsetInBytes = (ma_uint32)(writeOffsetInBytes + offsetInBytes) - pRB->subbufferSizeInBytes; - newWriteOffsetLoopFlag ^= 0x80000000; /* <-- Looped. */ - } else { - newWriteOffsetInBytes = (ma_uint32)(writeOffsetInBytes + offsetInBytes); - } - } else { - if ((writeOffsetInBytes + offsetInBytes) > readOffsetInBytes) { - newWriteOffsetInBytes = readOffsetInBytes; - } else { - newWriteOffsetInBytes = (ma_uint32)(writeOffsetInBytes + offsetInBytes); - } - } - - ma_atomic_exchange_32(&pRB->encodedWriteOffset, ma_rb__construct_offset(newWriteOffsetInBytes, newWriteOffsetLoopFlag)); - return MA_SUCCESS; -} - -MA_API ma_int32 ma_rb_pointer_distance(ma_rb* pRB) -{ - ma_uint32 readOffset; - ma_uint32 readOffsetInBytes; - ma_uint32 readOffsetLoopFlag; - ma_uint32 writeOffset; - ma_uint32 writeOffsetInBytes; - ma_uint32 writeOffsetLoopFlag; - - if (pRB == NULL) { - return 0; - } - - readOffset = ma_atomic_load_32(&pRB->encodedReadOffset); - ma_rb__deconstruct_offset(readOffset, &readOffsetInBytes, &readOffsetLoopFlag); - - writeOffset = ma_atomic_load_32(&pRB->encodedWriteOffset); - ma_rb__deconstruct_offset(writeOffset, &writeOffsetInBytes, &writeOffsetLoopFlag); - - if (readOffsetLoopFlag == writeOffsetLoopFlag) { - return writeOffsetInBytes - readOffsetInBytes; - } else { - return writeOffsetInBytes + (pRB->subbufferSizeInBytes - readOffsetInBytes); - } -} - -MA_API ma_uint32 ma_rb_available_read(ma_rb* pRB) -{ - ma_int32 dist; - - if (pRB == NULL) { - return 0; - } - - dist = ma_rb_pointer_distance(pRB); - if (dist < 0) { - return 0; - } - - return dist; -} - -MA_API ma_uint32 ma_rb_available_write(ma_rb* pRB) -{ - if (pRB == NULL) { - return 0; - } - - return (ma_uint32)(ma_rb_get_subbuffer_size(pRB) - ma_rb_pointer_distance(pRB)); -} - -MA_API size_t ma_rb_get_subbuffer_size(ma_rb* pRB) -{ - if (pRB == NULL) { - return 0; - } - - return pRB->subbufferSizeInBytes; -} - -MA_API size_t ma_rb_get_subbuffer_stride(ma_rb* pRB) -{ - if (pRB == NULL) { - return 0; - } - - if (pRB->subbufferStrideInBytes == 0) { - return (size_t)pRB->subbufferSizeInBytes; - } - - return (size_t)pRB->subbufferStrideInBytes; -} - -MA_API size_t ma_rb_get_subbuffer_offset(ma_rb* pRB, size_t subbufferIndex) -{ - if (pRB == NULL) { - return 0; - } - - return subbufferIndex * ma_rb_get_subbuffer_stride(pRB); -} - -MA_API void* ma_rb_get_subbuffer_ptr(ma_rb* pRB, size_t subbufferIndex, void* pBuffer) -{ - if (pRB == NULL) { - return NULL; - } - - return ma_offset_ptr(pBuffer, ma_rb_get_subbuffer_offset(pRB, subbufferIndex)); -} - - - -static ma_result ma_pcm_rb_data_source__on_read(ma_data_source* pDataSource, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead) -{ - /* Since there's no notion of an end, we don't ever want to return MA_AT_END here. But it is possible to return 0. */ - ma_pcm_rb* pRB = (ma_pcm_rb*)pDataSource; - ma_result result; - ma_uint64 totalFramesRead; - - MA_ASSERT(pRB != NULL); - - /* We need to run this in a loop since the ring buffer itself may loop. */ - totalFramesRead = 0; - while (totalFramesRead < frameCount) { - void* pMappedBuffer; - ma_uint32 mappedFrameCount; - ma_uint64 framesToRead = frameCount - totalFramesRead; - if (framesToRead > 0xFFFFFFFF) { - framesToRead = 0xFFFFFFFF; - } - - mappedFrameCount = (ma_uint32)framesToRead; - result = ma_pcm_rb_acquire_read(pRB, &mappedFrameCount, &pMappedBuffer); - if (result != MA_SUCCESS) { - break; - } - - if (mappedFrameCount == 0) { - break; /* <-- End of ring buffer. */ - } - - ma_copy_pcm_frames(ma_offset_pcm_frames_ptr(pFramesOut, totalFramesRead, pRB->format, pRB->channels), pMappedBuffer, mappedFrameCount, pRB->format, pRB->channels); - - result = ma_pcm_rb_commit_read(pRB, mappedFrameCount); - if (result != MA_SUCCESS) { - break; - } - - totalFramesRead += mappedFrameCount; - } - - /* - There is no notion of an "end" in a ring buffer. If we didn't have enough data to fill the requested frame - count we'll need to pad with silence. If we don't do this, totalFramesRead might equal 0 which will result - in the data source layer at a higher level translating this to MA_AT_END which is incorrect for a ring buffer. - */ - if (totalFramesRead < frameCount) { - ma_silence_pcm_frames(ma_offset_pcm_frames_ptr(pFramesOut, totalFramesRead, pRB->format, pRB->channels), (frameCount - totalFramesRead), pRB->format, pRB->channels); - totalFramesRead = frameCount; - } - - *pFramesRead = totalFramesRead; - return MA_SUCCESS; -} - -static ma_result ma_pcm_rb_data_source__on_get_data_format(ma_data_source* pDataSource, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate, ma_channel* pChannelMap, size_t channelMapCap) -{ - ma_pcm_rb* pRB = (ma_pcm_rb*)pDataSource; - MA_ASSERT(pRB != NULL); - - if (pFormat != NULL) { - *pFormat = pRB->format; - } - - if (pChannels != NULL) { - *pChannels = pRB->channels; - } - - if (pSampleRate != NULL) { - *pSampleRate = pRB->sampleRate; - } - - /* Just assume the default channel map. */ - if (pChannelMap != NULL) { - ma_channel_map_init_standard(ma_standard_channel_map_default, pChannelMap, channelMapCap, pRB->channels); - } - - return MA_SUCCESS; -} - -static ma_data_source_vtable ma_gRBDataSourceVTable = -{ - ma_pcm_rb_data_source__on_read, - NULL, /* onSeek */ - ma_pcm_rb_data_source__on_get_data_format, - NULL, /* onGetCursor */ - NULL, /* onGetLength */ - NULL, /* onSetLooping */ - 0 -}; - -static MA_INLINE ma_uint32 ma_pcm_rb_get_bpf(ma_pcm_rb* pRB) -{ - MA_ASSERT(pRB != NULL); - - return ma_get_bytes_per_frame(pRB->format, pRB->channels); -} - -MA_API ma_result ma_pcm_rb_init_ex(ma_format format, ma_uint32 channels, ma_uint32 subbufferSizeInFrames, ma_uint32 subbufferCount, ma_uint32 subbufferStrideInFrames, void* pOptionalPreallocatedBuffer, const ma_allocation_callbacks* pAllocationCallbacks, ma_pcm_rb* pRB) -{ - ma_uint32 bpf; - ma_result result; - - if (pRB == NULL) { - return MA_INVALID_ARGS; - } - - MA_ZERO_OBJECT(pRB); - - bpf = ma_get_bytes_per_frame(format, channels); - if (bpf == 0) { - return MA_INVALID_ARGS; - } - - result = ma_rb_init_ex(subbufferSizeInFrames*bpf, subbufferCount, subbufferStrideInFrames*bpf, pOptionalPreallocatedBuffer, pAllocationCallbacks, &pRB->rb); - if (result != MA_SUCCESS) { - return result; - } - - pRB->format = format; - pRB->channels = channels; - pRB->sampleRate = 0; /* The sample rate is not passed in as a parameter. */ - - /* The PCM ring buffer is a data source. We need to get that set up as well. */ - { - ma_data_source_config dataSourceConfig = ma_data_source_config_init(); - dataSourceConfig.vtable = &ma_gRBDataSourceVTable; - - result = ma_data_source_init(&dataSourceConfig, &pRB->ds); - if (result != MA_SUCCESS) { - ma_rb_uninit(&pRB->rb); - return result; - } - } - - return MA_SUCCESS; -} - -MA_API ma_result ma_pcm_rb_init(ma_format format, ma_uint32 channels, ma_uint32 bufferSizeInFrames, void* pOptionalPreallocatedBuffer, const ma_allocation_callbacks* pAllocationCallbacks, ma_pcm_rb* pRB) -{ - return ma_pcm_rb_init_ex(format, channels, bufferSizeInFrames, 1, 0, pOptionalPreallocatedBuffer, pAllocationCallbacks, pRB); -} - -MA_API void ma_pcm_rb_uninit(ma_pcm_rb* pRB) -{ - if (pRB == NULL) { - return; - } - - ma_data_source_uninit(&pRB->ds); - ma_rb_uninit(&pRB->rb); -} - -MA_API void ma_pcm_rb_reset(ma_pcm_rb* pRB) -{ - if (pRB == NULL) { - return; - } - - ma_rb_reset(&pRB->rb); -} - -MA_API ma_result ma_pcm_rb_acquire_read(ma_pcm_rb* pRB, ma_uint32* pSizeInFrames, void** ppBufferOut) -{ - size_t sizeInBytes; - ma_result result; - - if (pRB == NULL || pSizeInFrames == NULL) { - return MA_INVALID_ARGS; - } - - sizeInBytes = *pSizeInFrames * ma_pcm_rb_get_bpf(pRB); - - result = ma_rb_acquire_read(&pRB->rb, &sizeInBytes, ppBufferOut); - if (result != MA_SUCCESS) { - return result; - } - - *pSizeInFrames = (ma_uint32)(sizeInBytes / (size_t)ma_pcm_rb_get_bpf(pRB)); - return MA_SUCCESS; -} - -MA_API ma_result ma_pcm_rb_commit_read(ma_pcm_rb* pRB, ma_uint32 sizeInFrames) -{ - if (pRB == NULL) { - return MA_INVALID_ARGS; - } - - return ma_rb_commit_read(&pRB->rb, sizeInFrames * ma_pcm_rb_get_bpf(pRB)); -} - -MA_API ma_result ma_pcm_rb_acquire_write(ma_pcm_rb* pRB, ma_uint32* pSizeInFrames, void** ppBufferOut) -{ - size_t sizeInBytes; - ma_result result; - - if (pRB == NULL) { - return MA_INVALID_ARGS; - } - - sizeInBytes = *pSizeInFrames * ma_pcm_rb_get_bpf(pRB); - - result = ma_rb_acquire_write(&pRB->rb, &sizeInBytes, ppBufferOut); - if (result != MA_SUCCESS) { - return result; - } - - *pSizeInFrames = (ma_uint32)(sizeInBytes / ma_pcm_rb_get_bpf(pRB)); - return MA_SUCCESS; -} - -MA_API ma_result ma_pcm_rb_commit_write(ma_pcm_rb* pRB, ma_uint32 sizeInFrames) -{ - if (pRB == NULL) { - return MA_INVALID_ARGS; - } - - return ma_rb_commit_write(&pRB->rb, sizeInFrames * ma_pcm_rb_get_bpf(pRB)); -} - -MA_API ma_result ma_pcm_rb_seek_read(ma_pcm_rb* pRB, ma_uint32 offsetInFrames) -{ - if (pRB == NULL) { - return MA_INVALID_ARGS; - } - - return ma_rb_seek_read(&pRB->rb, offsetInFrames * ma_pcm_rb_get_bpf(pRB)); -} - -MA_API ma_result ma_pcm_rb_seek_write(ma_pcm_rb* pRB, ma_uint32 offsetInFrames) -{ - if (pRB == NULL) { - return MA_INVALID_ARGS; - } - - return ma_rb_seek_write(&pRB->rb, offsetInFrames * ma_pcm_rb_get_bpf(pRB)); -} - -MA_API ma_int32 ma_pcm_rb_pointer_distance(ma_pcm_rb* pRB) -{ - if (pRB == NULL) { - return 0; - } - - return ma_rb_pointer_distance(&pRB->rb) / ma_pcm_rb_get_bpf(pRB); -} - -MA_API ma_uint32 ma_pcm_rb_available_read(ma_pcm_rb* pRB) -{ - if (pRB == NULL) { - return 0; - } - - return ma_rb_available_read(&pRB->rb) / ma_pcm_rb_get_bpf(pRB); -} - -MA_API ma_uint32 ma_pcm_rb_available_write(ma_pcm_rb* pRB) -{ - if (pRB == NULL) { - return 0; - } - - return ma_rb_available_write(&pRB->rb) / ma_pcm_rb_get_bpf(pRB); -} - -MA_API ma_uint32 ma_pcm_rb_get_subbuffer_size(ma_pcm_rb* pRB) -{ - if (pRB == NULL) { - return 0; - } - - return (ma_uint32)(ma_rb_get_subbuffer_size(&pRB->rb) / ma_pcm_rb_get_bpf(pRB)); -} - -MA_API ma_uint32 ma_pcm_rb_get_subbuffer_stride(ma_pcm_rb* pRB) -{ - if (pRB == NULL) { - return 0; - } - - return (ma_uint32)(ma_rb_get_subbuffer_stride(&pRB->rb) / ma_pcm_rb_get_bpf(pRB)); -} - -MA_API ma_uint32 ma_pcm_rb_get_subbuffer_offset(ma_pcm_rb* pRB, ma_uint32 subbufferIndex) -{ - if (pRB == NULL) { - return 0; - } - - return (ma_uint32)(ma_rb_get_subbuffer_offset(&pRB->rb, subbufferIndex) / ma_pcm_rb_get_bpf(pRB)); -} - -MA_API void* ma_pcm_rb_get_subbuffer_ptr(ma_pcm_rb* pRB, ma_uint32 subbufferIndex, void* pBuffer) -{ - if (pRB == NULL) { - return NULL; - } - - return ma_rb_get_subbuffer_ptr(&pRB->rb, subbufferIndex, pBuffer); -} - -MA_API ma_format ma_pcm_rb_get_format(const ma_pcm_rb* pRB) -{ - if (pRB == NULL) { - return ma_format_unknown; - } - - return pRB->format; -} - -MA_API ma_uint32 ma_pcm_rb_get_channels(const ma_pcm_rb* pRB) -{ - if (pRB == NULL) { - return 0; - } - - return pRB->channels; -} - -MA_API ma_uint32 ma_pcm_rb_get_sample_rate(const ma_pcm_rb* pRB) -{ - if (pRB == NULL) { - return 0; - } - - return pRB->sampleRate; -} - -MA_API void ma_pcm_rb_set_sample_rate(ma_pcm_rb* pRB, ma_uint32 sampleRate) -{ - if (pRB == NULL) { - return; - } - - pRB->sampleRate = sampleRate; -} - - - -MA_API ma_result ma_duplex_rb_init(ma_format captureFormat, ma_uint32 captureChannels, ma_uint32 sampleRate, ma_uint32 captureInternalSampleRate, ma_uint32 captureInternalPeriodSizeInFrames, const ma_allocation_callbacks* pAllocationCallbacks, ma_duplex_rb* pRB) -{ - ma_result result; - ma_uint32 sizeInFrames; - - sizeInFrames = (ma_uint32)ma_calculate_frame_count_after_resampling(sampleRate, captureInternalSampleRate, captureInternalPeriodSizeInFrames * 5); - if (sizeInFrames == 0) { - return MA_INVALID_ARGS; - } - - result = ma_pcm_rb_init(captureFormat, captureChannels, sizeInFrames, NULL, pAllocationCallbacks, &pRB->rb); - if (result != MA_SUCCESS) { - return result; - } - - /* Seek forward a bit so we have a bit of a buffer in case of desyncs. */ - ma_pcm_rb_seek_write((ma_pcm_rb*)pRB, captureInternalPeriodSizeInFrames * 2); - - return MA_SUCCESS; -} - -MA_API ma_result ma_duplex_rb_uninit(ma_duplex_rb* pRB) -{ - ma_pcm_rb_uninit((ma_pcm_rb*)pRB); - return MA_SUCCESS; -} - - - -/************************************************************************************************************************************************************** - -Miscellaneous Helpers - -**************************************************************************************************************************************************************/ -MA_API const char* ma_result_description(ma_result result) -{ - switch (result) - { - case MA_SUCCESS: return "No error"; - case MA_ERROR: return "Unknown error"; - case MA_INVALID_ARGS: return "Invalid argument"; - case MA_INVALID_OPERATION: return "Invalid operation"; - case MA_OUT_OF_MEMORY: return "Out of memory"; - case MA_OUT_OF_RANGE: return "Out of range"; - case MA_ACCESS_DENIED: return "Permission denied"; - case MA_DOES_NOT_EXIST: return "Resource does not exist"; - case MA_ALREADY_EXISTS: return "Resource already exists"; - case MA_TOO_MANY_OPEN_FILES: return "Too many open files"; - case MA_INVALID_FILE: return "Invalid file"; - case MA_TOO_BIG: return "Too large"; - case MA_PATH_TOO_LONG: return "Path too long"; - case MA_NAME_TOO_LONG: return "Name too long"; - case MA_NOT_DIRECTORY: return "Not a directory"; - case MA_IS_DIRECTORY: return "Is a directory"; - case MA_DIRECTORY_NOT_EMPTY: return "Directory not empty"; - case MA_AT_END: return "At end"; - case MA_NO_SPACE: return "No space available"; - case MA_BUSY: return "Device or resource busy"; - case MA_IO_ERROR: return "Input/output error"; - case MA_INTERRUPT: return "Interrupted"; - case MA_UNAVAILABLE: return "Resource unavailable"; - case MA_ALREADY_IN_USE: return "Resource already in use"; - case MA_BAD_ADDRESS: return "Bad address"; - case MA_BAD_SEEK: return "Illegal seek"; - case MA_BAD_PIPE: return "Broken pipe"; - case MA_DEADLOCK: return "Deadlock"; - case MA_TOO_MANY_LINKS: return "Too many links"; - case MA_NOT_IMPLEMENTED: return "Not implemented"; - case MA_NO_MESSAGE: return "No message of desired type"; - case MA_BAD_MESSAGE: return "Invalid message"; - case MA_NO_DATA_AVAILABLE: return "No data available"; - case MA_INVALID_DATA: return "Invalid data"; - case MA_TIMEOUT: return "Timeout"; - case MA_NO_NETWORK: return "Network unavailable"; - case MA_NOT_UNIQUE: return "Not unique"; - case MA_NOT_SOCKET: return "Socket operation on non-socket"; - case MA_NO_ADDRESS: return "Destination address required"; - case MA_BAD_PROTOCOL: return "Protocol wrong type for socket"; - case MA_PROTOCOL_UNAVAILABLE: return "Protocol not available"; - case MA_PROTOCOL_NOT_SUPPORTED: return "Protocol not supported"; - case MA_PROTOCOL_FAMILY_NOT_SUPPORTED: return "Protocol family not supported"; - case MA_ADDRESS_FAMILY_NOT_SUPPORTED: return "Address family not supported"; - case MA_SOCKET_NOT_SUPPORTED: return "Socket type not supported"; - case MA_CONNECTION_RESET: return "Connection reset"; - case MA_ALREADY_CONNECTED: return "Already connected"; - case MA_NOT_CONNECTED: return "Not connected"; - case MA_CONNECTION_REFUSED: return "Connection refused"; - case MA_NO_HOST: return "No host"; - case MA_IN_PROGRESS: return "Operation in progress"; - case MA_CANCELLED: return "Operation cancelled"; - case MA_MEMORY_ALREADY_MAPPED: return "Memory already mapped"; - - case MA_FORMAT_NOT_SUPPORTED: return "Format not supported"; - case MA_DEVICE_TYPE_NOT_SUPPORTED: return "Device type not supported"; - case MA_SHARE_MODE_NOT_SUPPORTED: return "Share mode not supported"; - case MA_NO_BACKEND: return "No backend"; - case MA_NO_DEVICE: return "No device"; - case MA_API_NOT_FOUND: return "API not found"; - case MA_INVALID_DEVICE_CONFIG: return "Invalid device config"; - - case MA_DEVICE_NOT_INITIALIZED: return "Device not initialized"; - case MA_DEVICE_NOT_STARTED: return "Device not started"; - - case MA_FAILED_TO_INIT_BACKEND: return "Failed to initialize backend"; - case MA_FAILED_TO_OPEN_BACKEND_DEVICE: return "Failed to open backend device"; - case MA_FAILED_TO_START_BACKEND_DEVICE: return "Failed to start backend device"; - case MA_FAILED_TO_STOP_BACKEND_DEVICE: return "Failed to stop backend device"; - - default: return "Unknown error"; - } -} - -MA_API void* ma_malloc(size_t sz, const ma_allocation_callbacks* pAllocationCallbacks) -{ - if (pAllocationCallbacks != NULL) { - if (pAllocationCallbacks->onMalloc != NULL) { - return pAllocationCallbacks->onMalloc(sz, pAllocationCallbacks->pUserData); - } else { - return NULL; /* Do not fall back to the default implementation. */ - } - } else { - return ma__malloc_default(sz, NULL); - } -} - -MA_API void* ma_calloc(size_t sz, const ma_allocation_callbacks* pAllocationCallbacks) -{ - void* p = ma_malloc(sz, pAllocationCallbacks); - if (p != NULL) { - MA_ZERO_MEMORY(p, sz); - } - - return p; -} - -MA_API void* ma_realloc(void* p, size_t sz, const ma_allocation_callbacks* pAllocationCallbacks) -{ - if (pAllocationCallbacks != NULL) { - if (pAllocationCallbacks->onRealloc != NULL) { - return pAllocationCallbacks->onRealloc(p, sz, pAllocationCallbacks->pUserData); - } else { - return NULL; /* Do not fall back to the default implementation. */ - } - } else { - return ma__realloc_default(p, sz, NULL); - } -} - -MA_API void ma_free(void* p, const ma_allocation_callbacks* pAllocationCallbacks) -{ - if (p == NULL) { - return; - } - - if (pAllocationCallbacks != NULL) { - if (pAllocationCallbacks->onFree != NULL) { - pAllocationCallbacks->onFree(p, pAllocationCallbacks->pUserData); - } else { - return; /* Do no fall back to the default implementation. */ - } - } else { - ma__free_default(p, NULL); - } -} - -MA_API void* ma_aligned_malloc(size_t sz, size_t alignment, const ma_allocation_callbacks* pAllocationCallbacks) -{ - size_t extraBytes; - void* pUnaligned; - void* pAligned; - - if (alignment == 0) { - return 0; - } - - extraBytes = alignment-1 + sizeof(void*); - - pUnaligned = ma_malloc(sz + extraBytes, pAllocationCallbacks); - if (pUnaligned == NULL) { - return NULL; - } - - pAligned = (void*)(((ma_uintptr)pUnaligned + extraBytes) & ~((ma_uintptr)(alignment-1))); - ((void**)pAligned)[-1] = pUnaligned; - - return pAligned; -} - -MA_API void ma_aligned_free(void* p, const ma_allocation_callbacks* pAllocationCallbacks) -{ - ma_free(((void**)p)[-1], pAllocationCallbacks); -} - -MA_API const char* ma_get_format_name(ma_format format) -{ - switch (format) - { - case ma_format_unknown: return "Unknown"; - case ma_format_u8: return "8-bit Unsigned Integer"; - case ma_format_s16: return "16-bit Signed Integer"; - case ma_format_s24: return "24-bit Signed Integer (Tightly Packed)"; - case ma_format_s32: return "32-bit Signed Integer"; - case ma_format_f32: return "32-bit IEEE Floating Point"; - default: return "Invalid"; - } -} - -MA_API void ma_blend_f32(float* pOut, float* pInA, float* pInB, float factor, ma_uint32 channels) -{ - ma_uint32 i; - for (i = 0; i < channels; ++i) { - pOut[i] = ma_mix_f32(pInA[i], pInB[i], factor); - } -} - - -MA_API ma_uint32 ma_get_bytes_per_sample(ma_format format) -{ - ma_uint32 sizes[] = { - 0, /* unknown */ - 1, /* u8 */ - 2, /* s16 */ - 3, /* s24 */ - 4, /* s32 */ - 4, /* f32 */ - }; - return sizes[format]; -} - - - -#define MA_DATA_SOURCE_DEFAULT_RANGE_BEG 0 -#define MA_DATA_SOURCE_DEFAULT_RANGE_END ~((ma_uint64)0) -#define MA_DATA_SOURCE_DEFAULT_LOOP_POINT_BEG 0 -#define MA_DATA_SOURCE_DEFAULT_LOOP_POINT_END ~((ma_uint64)0) - -MA_API ma_data_source_config ma_data_source_config_init(void) -{ - ma_data_source_config config; - - MA_ZERO_OBJECT(&config); - - return config; -} - - -MA_API ma_result ma_data_source_init(const ma_data_source_config* pConfig, ma_data_source* pDataSource) -{ - ma_data_source_base* pDataSourceBase = (ma_data_source_base*)pDataSource; - - if (pDataSource == NULL) { - return MA_INVALID_ARGS; - } - - MA_ZERO_OBJECT(pDataSourceBase); - - if (pConfig == NULL) { - return MA_INVALID_ARGS; - } - - if (pConfig->vtable == NULL) { - return MA_INVALID_ARGS; - } - - pDataSourceBase->vtable = pConfig->vtable; - pDataSourceBase->rangeBegInFrames = MA_DATA_SOURCE_DEFAULT_RANGE_BEG; - pDataSourceBase->rangeEndInFrames = MA_DATA_SOURCE_DEFAULT_RANGE_END; - pDataSourceBase->loopBegInFrames = MA_DATA_SOURCE_DEFAULT_LOOP_POINT_BEG; - pDataSourceBase->loopEndInFrames = MA_DATA_SOURCE_DEFAULT_LOOP_POINT_END; - pDataSourceBase->pCurrent = pDataSource; /* Always read from ourself by default. */ - pDataSourceBase->pNext = NULL; - pDataSourceBase->onGetNext = NULL; - - return MA_SUCCESS; -} - -MA_API void ma_data_source_uninit(ma_data_source* pDataSource) -{ - if (pDataSource == NULL) { - return; - } - - /* - This is placeholder in case we need this later. Data sources need to call this in their - uninitialization routine to ensure things work later on if something is added here. - */ -} - -static ma_result ma_data_source_resolve_current(ma_data_source* pDataSource, ma_data_source** ppCurrentDataSource) -{ - ma_data_source_base* pCurrentDataSource = (ma_data_source_base*)pDataSource; - - MA_ASSERT(pDataSource != NULL); - MA_ASSERT(ppCurrentDataSource != NULL); - - if (pCurrentDataSource->pCurrent == NULL) { - /* - The current data source is NULL. If we're using this in the context of a chain we need to return NULL - here so that we don't end up looping. Otherwise we just return the data source itself. - */ - if (pCurrentDataSource->pNext != NULL || pCurrentDataSource->onGetNext != NULL) { - pCurrentDataSource = NULL; - } else { - pCurrentDataSource = (ma_data_source_base*)pDataSource; /* Not being used in a chain. Make sure we just always read from the data source itself at all times. */ - } - } else { - pCurrentDataSource = (ma_data_source_base*)pCurrentDataSource->pCurrent; - } - - *ppCurrentDataSource = pCurrentDataSource; - - return MA_SUCCESS; -} - -static ma_result ma_data_source_read_pcm_frames_from_backend(ma_data_source* pDataSource, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead) -{ - ma_data_source_base* pDataSourceBase = (ma_data_source_base*)pDataSource; - - MA_ASSERT(pDataSourceBase != NULL); - MA_ASSERT(pDataSourceBase->vtable != NULL); - MA_ASSERT(pDataSourceBase->vtable->onRead != NULL); - MA_ASSERT(pFramesRead != NULL); - - if (pFramesOut != NULL) { - return pDataSourceBase->vtable->onRead(pDataSourceBase, pFramesOut, frameCount, pFramesRead); - } else { - /* - No output buffer. Probably seeking forward. Read and discard. Can probably optimize this in terms of - onSeek and onGetCursor, but need to keep in mind that the data source may not implement these functions. - */ - ma_result result; - ma_uint64 framesRead; - ma_format format; - ma_uint32 channels; - ma_uint64 discardBufferCapInFrames; - ma_uint8 pDiscardBuffer[4096]; - - result = ma_data_source_get_data_format(pDataSource, &format, &channels, NULL, NULL, 0); - if (result != MA_SUCCESS) { - return result; - } - - discardBufferCapInFrames = sizeof(pDiscardBuffer) / ma_get_bytes_per_frame(format, channels); - - framesRead = 0; - while (framesRead < frameCount) { - ma_uint64 framesReadThisIteration = 0; - ma_uint64 framesToRead = frameCount - framesRead; - if (framesToRead > discardBufferCapInFrames) { - framesToRead = discardBufferCapInFrames; - } - - result = pDataSourceBase->vtable->onRead(pDataSourceBase, pDiscardBuffer, framesToRead, &framesReadThisIteration); - if (result != MA_SUCCESS) { - return result; - } - - framesRead += framesReadThisIteration; - } - - *pFramesRead = framesRead; - - return MA_SUCCESS; - } -} - -static ma_result ma_data_source_read_pcm_frames_within_range(ma_data_source* pDataSource, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead) -{ - ma_data_source_base* pDataSourceBase = (ma_data_source_base*)pDataSource; - ma_result result; - ma_uint64 framesRead = 0; - ma_bool32 loop = ma_data_source_is_looping(pDataSource); - - if (pDataSourceBase == NULL) { - return MA_AT_END; - } - - if (frameCount == 0) { - return MA_INVALID_ARGS; - } - - MA_ASSERT(pDataSourceBase->vtable != NULL); - - if ((pDataSourceBase->vtable->flags & MA_DATA_SOURCE_SELF_MANAGED_RANGE_AND_LOOP_POINT) != 0 || (pDataSourceBase->rangeEndInFrames == ~((ma_uint64)0) && (pDataSourceBase->loopEndInFrames == ~((ma_uint64)0) || loop == MA_FALSE))) { - /* Either the data source is self-managing the range, or no range is set - just read like normal. The data source itself will tell us when the end is reached. */ - result = ma_data_source_read_pcm_frames_from_backend(pDataSource, pFramesOut, frameCount, &framesRead); - } else { - /* Need to clamp to within the range. */ - ma_uint64 relativeCursor; - ma_uint64 absoluteCursor; - - result = ma_data_source_get_cursor_in_pcm_frames(pDataSourceBase, &relativeCursor); - if (result != MA_SUCCESS) { - /* Failed to retrieve the cursor. Cannot read within a range or loop points. Just read like normal - this may happen for things like noise data sources where it doesn't really matter. */ - result = ma_data_source_read_pcm_frames_from_backend(pDataSource, pFramesOut, frameCount, &framesRead); - } else { - ma_uint64 rangeBeg; - ma_uint64 rangeEnd; - - /* We have the cursor. We need to make sure we don't read beyond our range. */ - rangeBeg = pDataSourceBase->rangeBegInFrames; - rangeEnd = pDataSourceBase->rangeEndInFrames; - - absoluteCursor = rangeBeg + relativeCursor; - - /* If looping, make sure we're within range. */ - if (loop) { - if (pDataSourceBase->loopEndInFrames != ~((ma_uint64)0)) { - rangeEnd = ma_min(rangeEnd, pDataSourceBase->rangeBegInFrames + pDataSourceBase->loopEndInFrames); - } - } - - if (frameCount > (rangeEnd - absoluteCursor) && rangeEnd != ~((ma_uint64)0)) { - frameCount = (rangeEnd - absoluteCursor); - } - - /* - If the cursor is sitting on the end of the range the frame count will be set to 0 which can - result in MA_INVALID_ARGS. In this case, we don't want to try reading, but instead return - MA_AT_END so the higher level function can know about it. - */ - if (frameCount > 0) { - result = ma_data_source_read_pcm_frames_from_backend(pDataSource, pFramesOut, frameCount, &framesRead); - } else { - result = MA_AT_END; /* The cursor is sitting on the end of the range which means we're at the end. */ - } - } - } - - if (pFramesRead != NULL) { - *pFramesRead = framesRead; - } - - /* We need to make sure MA_AT_END is returned if we hit the end of the range. */ - if (result == MA_SUCCESS && framesRead == 0) { - result = MA_AT_END; - } - - return result; -} - -MA_API ma_result ma_data_source_read_pcm_frames(ma_data_source* pDataSource, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead) -{ - ma_result result = MA_SUCCESS; - ma_data_source_base* pDataSourceBase = (ma_data_source_base*)pDataSource; - ma_data_source_base* pCurrentDataSource; - void* pRunningFramesOut = pFramesOut; - ma_uint64 totalFramesProcessed = 0; - ma_format format; - ma_uint32 channels; - ma_uint32 emptyLoopCounter = 0; /* Keeps track of how many times 0 frames have been read. For infinite loop detection of sounds with no audio data. */ - ma_bool32 loop; - - if (pFramesRead != NULL) { - *pFramesRead = 0; - } - - if (frameCount == 0) { - return MA_INVALID_ARGS; - } - - if (pDataSourceBase == NULL) { - return MA_INVALID_ARGS; - } - - loop = ma_data_source_is_looping(pDataSource); - - /* - We need to know the data format so we can advance the output buffer as we read frames. If this - fails, chaining will not work and we'll just read as much as we can from the current source. - */ - if (ma_data_source_get_data_format(pDataSource, &format, &channels, NULL, NULL, 0) != MA_SUCCESS) { - result = ma_data_source_resolve_current(pDataSource, (ma_data_source**)&pCurrentDataSource); - if (result != MA_SUCCESS) { - return result; - } - - return ma_data_source_read_pcm_frames_within_range(pCurrentDataSource, pFramesOut, frameCount, pFramesRead); - } - - /* - Looping is a bit of a special case. When the `loop` argument is true, chaining will not work and - only the current data source will be read from. - */ - - /* Keep reading until we've read as many frames as possible. */ - while (totalFramesProcessed < frameCount) { - ma_uint64 framesProcessed; - ma_uint64 framesRemaining = frameCount - totalFramesProcessed; - - /* We need to resolve the data source that we'll actually be reading from. */ - result = ma_data_source_resolve_current(pDataSource, (ma_data_source**)&pCurrentDataSource); - if (result != MA_SUCCESS) { - break; - } - - if (pCurrentDataSource == NULL) { - break; - } - - result = ma_data_source_read_pcm_frames_within_range(pCurrentDataSource, pRunningFramesOut, framesRemaining, &framesProcessed); - totalFramesProcessed += framesProcessed; - - /* - If we encountered an error from the read callback, make sure it's propagated to the caller. The caller may need to know whether or not MA_BUSY is returned which is - not necessarily considered an error. - */ - if (result != MA_SUCCESS && result != MA_AT_END) { - break; - } - - /* - We can determine if we've reached the end by checking if ma_data_source_read_pcm_frames_within_range() returned - MA_AT_END. To loop back to the start, all we need to do is seek back to the first frame. - */ - if (result == MA_AT_END) { - /* - The result needs to be reset back to MA_SUCCESS (from MA_AT_END) so that we don't - accidentally return MA_AT_END when data has been read in prior loop iterations. at the - end of this function, the result will be checked for MA_SUCCESS, and if the total - number of frames processed is 0, will be explicitly set to MA_AT_END. - */ - result = MA_SUCCESS; - - /* - We reached the end. If we're looping, we just loop back to the start of the current - data source. If we're not looping we need to check if we have another in the chain, and - if so, switch to it. - */ - if (loop) { - if (framesProcessed == 0) { - emptyLoopCounter += 1; - if (emptyLoopCounter > 1) { - break; /* Infinite loop detected. Get out. */ - } - } else { - emptyLoopCounter = 0; - } - - result = ma_data_source_seek_to_pcm_frame(pCurrentDataSource, pCurrentDataSource->loopBegInFrames); - if (result != MA_SUCCESS) { - break; /* Failed to loop. Abort. */ - } - - /* Don't return MA_AT_END for looping sounds. */ - result = MA_SUCCESS; - } else { - if (pCurrentDataSource->pNext != NULL) { - pDataSourceBase->pCurrent = pCurrentDataSource->pNext; - } else if (pCurrentDataSource->onGetNext != NULL) { - pDataSourceBase->pCurrent = pCurrentDataSource->onGetNext(pCurrentDataSource); - if (pDataSourceBase->pCurrent == NULL) { - break; /* Our callback did not return a next data source. We're done. */ - } - } else { - /* Reached the end of the chain. We're done. */ - break; - } - - /* The next data source needs to be rewound to ensure data is read in looping scenarios. */ - result = ma_data_source_seek_to_pcm_frame(pDataSourceBase->pCurrent, 0); - if (result != MA_SUCCESS) { - break; - } - } - } - - if (pRunningFramesOut != NULL) { - pRunningFramesOut = ma_offset_ptr(pRunningFramesOut, framesProcessed * ma_get_bytes_per_frame(format, channels)); - } - } - - if (pFramesRead != NULL) { - *pFramesRead = totalFramesProcessed; - } - - MA_ASSERT(!(result == MA_AT_END && totalFramesProcessed > 0)); /* We should never be returning MA_AT_END if we read some data. */ - - if (result == MA_SUCCESS && totalFramesProcessed == 0) { - result = MA_AT_END; - } - - return result; -} - -MA_API ma_result ma_data_source_seek_pcm_frames(ma_data_source* pDataSource, ma_uint64 frameCount, ma_uint64* pFramesSeeked) -{ - return ma_data_source_read_pcm_frames(pDataSource, NULL, frameCount, pFramesSeeked); -} - -MA_API ma_result ma_data_source_seek_to_pcm_frame(ma_data_source* pDataSource, ma_uint64 frameIndex) -{ - ma_data_source_base* pDataSourceBase = (ma_data_source_base*)pDataSource; - - if (pDataSourceBase == NULL) { - return MA_INVALID_ARGS; - } - - if (pDataSourceBase->vtable->onSeek == NULL) { - return MA_NOT_IMPLEMENTED; - } - - if (frameIndex > pDataSourceBase->rangeEndInFrames) { - return MA_INVALID_OPERATION; /* Trying to seek too far forward. */ - } - - MA_ASSERT(pDataSourceBase->vtable != NULL); - - return pDataSourceBase->vtable->onSeek(pDataSource, pDataSourceBase->rangeBegInFrames + frameIndex); -} - -MA_API ma_result ma_data_source_seek_seconds(ma_data_source* pDataSource, float secondCount, float* pSecondsSeeked) -{ - ma_uint64 frameCount; - ma_uint64 framesSeeked = 0; - ma_uint32 sampleRate; - ma_result result; - - if (pDataSource == NULL) { - return MA_INVALID_ARGS; - } - - result = ma_data_source_get_data_format(pDataSource, NULL, NULL, &sampleRate, NULL, 0); - if (result != MA_SUCCESS) { - return result; - } - - /* We need PCM frames instead of seconds */ - frameCount = (ma_uint64)(secondCount * sampleRate); - - result = ma_data_source_seek_pcm_frames(pDataSource, frameCount, &framesSeeked); - - /* VC6 doesn't support division between unsigned 64-bit integer and floating point number. Signed integer needed. This shouldn't affect anything in practice */ - *pSecondsSeeked = (ma_int64)framesSeeked / (float)sampleRate; - return result; -} - -MA_API ma_result ma_data_source_seek_to_second(ma_data_source* pDataSource, float seekPointInSeconds) -{ - ma_uint64 frameIndex; - ma_uint32 sampleRate; - ma_result result; - - if (pDataSource == NULL) { - return MA_INVALID_ARGS; - } - - result = ma_data_source_get_data_format(pDataSource, NULL, NULL, &sampleRate, NULL, 0); - if (result != MA_SUCCESS) { - return result; - } - - /* We need PCM frames instead of seconds */ - frameIndex = (ma_uint64)(seekPointInSeconds * sampleRate); - - return ma_data_source_seek_to_pcm_frame(pDataSource, frameIndex); -} - -MA_API ma_result ma_data_source_get_data_format(ma_data_source* pDataSource, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate, ma_channel* pChannelMap, size_t channelMapCap) -{ - ma_data_source_base* pDataSourceBase = (ma_data_source_base*)pDataSource; - ma_result result; - ma_format format; - ma_uint32 channels; - ma_uint32 sampleRate; - - /* Initialize to defaults for safety just in case the data source does not implement this callback. */ - if (pFormat != NULL) { - *pFormat = ma_format_unknown; - } - if (pChannels != NULL) { - *pChannels = 0; - } - if (pSampleRate != NULL) { - *pSampleRate = 0; - } - if (pChannelMap != NULL) { - MA_ZERO_MEMORY(pChannelMap, sizeof(*pChannelMap) * channelMapCap); - } - - if (pDataSourceBase == NULL) { - return MA_INVALID_ARGS; - } - - MA_ASSERT(pDataSourceBase->vtable != NULL); - - if (pDataSourceBase->vtable->onGetDataFormat == NULL) { - return MA_NOT_IMPLEMENTED; - } - - result = pDataSourceBase->vtable->onGetDataFormat(pDataSource, &format, &channels, &sampleRate, pChannelMap, channelMapCap); - if (result != MA_SUCCESS) { - return result; - } - - if (pFormat != NULL) { - *pFormat = format; - } - if (pChannels != NULL) { - *pChannels = channels; - } - if (pSampleRate != NULL) { - *pSampleRate = sampleRate; - } - - /* Channel map was passed in directly to the callback. This is safe due to the channelMapCap parameter. */ - - return MA_SUCCESS; -} - -MA_API ma_result ma_data_source_get_cursor_in_pcm_frames(ma_data_source* pDataSource, ma_uint64* pCursor) -{ - ma_data_source_base* pDataSourceBase = (ma_data_source_base*)pDataSource; - ma_result result; - ma_uint64 cursor; - - if (pCursor == NULL) { - return MA_INVALID_ARGS; - } - - *pCursor = 0; - - if (pDataSourceBase == NULL) { - return MA_SUCCESS; - } - - MA_ASSERT(pDataSourceBase->vtable != NULL); - - if (pDataSourceBase->vtable->onGetCursor == NULL) { - return MA_NOT_IMPLEMENTED; - } - - result = pDataSourceBase->vtable->onGetCursor(pDataSourceBase, &cursor); - if (result != MA_SUCCESS) { - return result; - } - - /* The cursor needs to be made relative to the start of the range. */ - if (cursor < pDataSourceBase->rangeBegInFrames) { /* Safety check so we don't return some huge number. */ - *pCursor = 0; - } else { - *pCursor = cursor - pDataSourceBase->rangeBegInFrames; - } - - return MA_SUCCESS; -} - -MA_API ma_result ma_data_source_get_length_in_pcm_frames(ma_data_source* pDataSource, ma_uint64* pLength) -{ - ma_data_source_base* pDataSourceBase = (ma_data_source_base*)pDataSource; - - if (pLength == NULL) { - return MA_INVALID_ARGS; - } - - *pLength = 0; - - if (pDataSourceBase == NULL) { - return MA_INVALID_ARGS; - } - - MA_ASSERT(pDataSourceBase->vtable != NULL); - - /* - If we have a range defined we'll use that to determine the length. This is one of rare times - where we'll actually trust the caller. If they've set the range, I think it's mostly safe to - assume they've set it based on some higher level knowledge of the structure of the sound bank. - */ - if (pDataSourceBase->rangeEndInFrames != ~((ma_uint64)0)) { - *pLength = pDataSourceBase->rangeEndInFrames - pDataSourceBase->rangeBegInFrames; - return MA_SUCCESS; - } - - /* - Getting here means a range is not defined so we'll need to get the data source itself to tell - us the length. - */ - if (pDataSourceBase->vtable->onGetLength == NULL) { - return MA_NOT_IMPLEMENTED; - } - - return pDataSourceBase->vtable->onGetLength(pDataSource, pLength); -} - -MA_API ma_result ma_data_source_get_cursor_in_seconds(ma_data_source* pDataSource, float* pCursor) -{ - ma_result result; - ma_uint64 cursorInPCMFrames; - ma_uint32 sampleRate; - - if (pCursor == NULL) { - return MA_INVALID_ARGS; - } - - *pCursor = 0; - - result = ma_data_source_get_cursor_in_pcm_frames(pDataSource, &cursorInPCMFrames); - if (result != MA_SUCCESS) { - return result; - } - - result = ma_data_source_get_data_format(pDataSource, NULL, NULL, &sampleRate, NULL, 0); - if (result != MA_SUCCESS) { - return result; - } - - /* VC6 does not support division of unsigned 64-bit integers with floating point numbers. Need to use a signed number. This shouldn't effect anything in practice. */ - *pCursor = (ma_int64)cursorInPCMFrames / (float)sampleRate; - - return MA_SUCCESS; -} - -MA_API ma_result ma_data_source_get_length_in_seconds(ma_data_source* pDataSource, float* pLength) -{ - ma_result result; - ma_uint64 lengthInPCMFrames; - ma_uint32 sampleRate; - - if (pLength == NULL) { - return MA_INVALID_ARGS; - } - - *pLength = 0; - - result = ma_data_source_get_length_in_pcm_frames(pDataSource, &lengthInPCMFrames); - if (result != MA_SUCCESS) { - return result; - } - - result = ma_data_source_get_data_format(pDataSource, NULL, NULL, &sampleRate, NULL, 0); - if (result != MA_SUCCESS) { - return result; - } - - /* VC6 does not support division of unsigned 64-bit integers with floating point numbers. Need to use a signed number. This shouldn't effect anything in practice. */ - *pLength = (ma_int64)lengthInPCMFrames / (float)sampleRate; - - return MA_SUCCESS; -} - -MA_API ma_result ma_data_source_set_looping(ma_data_source* pDataSource, ma_bool32 isLooping) -{ - ma_data_source_base* pDataSourceBase = (ma_data_source_base*)pDataSource; - - if (pDataSource == NULL) { - return MA_INVALID_ARGS; - } - - ma_atomic_exchange_32(&pDataSourceBase->isLooping, isLooping); - - MA_ASSERT(pDataSourceBase->vtable != NULL); - - /* If there's no callback for this just treat it as a successful no-op. */ - if (pDataSourceBase->vtable->onSetLooping == NULL) { - return MA_SUCCESS; - } - - return pDataSourceBase->vtable->onSetLooping(pDataSource, isLooping); -} - -MA_API ma_bool32 ma_data_source_is_looping(const ma_data_source* pDataSource) -{ - const ma_data_source_base* pDataSourceBase = (const ma_data_source_base*)pDataSource; - - if (pDataSource == NULL) { - return MA_FALSE; - } - - return ma_atomic_load_32(&pDataSourceBase->isLooping); -} - -MA_API ma_result ma_data_source_set_range_in_pcm_frames(ma_data_source* pDataSource, ma_uint64 rangeBegInFrames, ma_uint64 rangeEndInFrames) -{ - ma_data_source_base* pDataSourceBase = (ma_data_source_base*)pDataSource; - ma_result result; - ma_uint64 relativeCursor; - ma_uint64 absoluteCursor; - ma_bool32 doSeekAdjustment = MA_FALSE; - - if (pDataSource == NULL) { - return MA_INVALID_ARGS; - } - - if (rangeEndInFrames < rangeBegInFrames) { - return MA_INVALID_ARGS; /* The end of the range must come after the beginning. */ - } - - /* - We may need to adjust the position of the cursor to ensure it's clamped to the range. Grab it now - so we can calculate its absolute position before we change the range. - */ - result = ma_data_source_get_cursor_in_pcm_frames(pDataSource, &relativeCursor); - if (result == MA_SUCCESS) { - doSeekAdjustment = MA_TRUE; - absoluteCursor = relativeCursor + pDataSourceBase->rangeBegInFrames; - } else { - /* - We couldn't get the position of the cursor. It probably means the data source has no notion - of a cursor. We'll just leave it at position 0. Don't treat this as an error. - */ - doSeekAdjustment = MA_FALSE; - relativeCursor = 0; - absoluteCursor = 0; - } - - pDataSourceBase->rangeBegInFrames = rangeBegInFrames; - pDataSourceBase->rangeEndInFrames = rangeEndInFrames; - - /* - The commented out logic below was intended to maintain loop points in response to a change in the - range. However, this is not useful because it results in the sound breaking when you move the range - outside of the old loop points. I'm simplifying this by simply resetting the loop points. The - caller is expected to update their loop points if they change the range. - - In practice this should be mostly a non-issue because the majority of the time the range will be - set once right after initialization. - */ - pDataSourceBase->loopBegInFrames = 0; - pDataSourceBase->loopEndInFrames = ~((ma_uint64)0); - - - /* - Seek to within range. Note that our seek positions here are relative to the new range. We don't want - to do this if we failed to retrieve the cursor earlier on because it probably means the data source - has no notion of a cursor. In practice the seek would probably fail (which we silently ignore), but - I'm just not even going to attempt it. - */ - if (doSeekAdjustment) { - if (absoluteCursor < rangeBegInFrames) { - ma_data_source_seek_to_pcm_frame(pDataSource, 0); - } else if (absoluteCursor > rangeEndInFrames) { - ma_data_source_seek_to_pcm_frame(pDataSource, rangeEndInFrames - rangeBegInFrames); - } - } - - return MA_SUCCESS; -} - -MA_API void ma_data_source_get_range_in_pcm_frames(const ma_data_source* pDataSource, ma_uint64* pRangeBegInFrames, ma_uint64* pRangeEndInFrames) -{ - const ma_data_source_base* pDataSourceBase = (const ma_data_source_base*)pDataSource; - - if (pRangeBegInFrames != NULL) { - *pRangeBegInFrames = 0; - } - if (pRangeEndInFrames != NULL) { - *pRangeEndInFrames = 0; - } - - if (pDataSource == NULL) { - return; - } - - if (pRangeBegInFrames != NULL) { - *pRangeBegInFrames = pDataSourceBase->rangeBegInFrames; - } - - if (pRangeEndInFrames != NULL) { - *pRangeEndInFrames = pDataSourceBase->rangeEndInFrames; - } -} - -MA_API ma_result ma_data_source_set_loop_point_in_pcm_frames(ma_data_source* pDataSource, ma_uint64 loopBegInFrames, ma_uint64 loopEndInFrames) -{ - ma_data_source_base* pDataSourceBase = (ma_data_source_base*)pDataSource; - - if (pDataSource == NULL) { - return MA_INVALID_ARGS; - } - - if (loopEndInFrames < loopBegInFrames) { - return MA_INVALID_ARGS; /* The end of the loop point must come after the beginning. */ - } - - if (loopEndInFrames > pDataSourceBase->rangeEndInFrames && loopEndInFrames != ~((ma_uint64)0)) { - return MA_INVALID_ARGS; /* The end of the loop point must not go beyond the range. */ - } - - pDataSourceBase->loopBegInFrames = loopBegInFrames; - pDataSourceBase->loopEndInFrames = loopEndInFrames; - - /* The end cannot exceed the range. */ - if (pDataSourceBase->loopEndInFrames > (pDataSourceBase->rangeEndInFrames - pDataSourceBase->rangeBegInFrames) && pDataSourceBase->loopEndInFrames != ~((ma_uint64)0)) { - pDataSourceBase->loopEndInFrames = (pDataSourceBase->rangeEndInFrames - pDataSourceBase->rangeBegInFrames); - } - - return MA_SUCCESS; -} - -MA_API void ma_data_source_get_loop_point_in_pcm_frames(const ma_data_source* pDataSource, ma_uint64* pLoopBegInFrames, ma_uint64* pLoopEndInFrames) -{ - const ma_data_source_base* pDataSourceBase = (const ma_data_source_base*)pDataSource; - - if (pLoopBegInFrames != NULL) { - *pLoopBegInFrames = 0; - } - if (pLoopEndInFrames != NULL) { - *pLoopEndInFrames = 0; - } - - if (pDataSource == NULL) { - return; - } - - if (pLoopBegInFrames != NULL) { - *pLoopBegInFrames = pDataSourceBase->loopBegInFrames; - } - - if (pLoopEndInFrames != NULL) { - *pLoopEndInFrames = pDataSourceBase->loopEndInFrames; - } -} - -MA_API ma_result ma_data_source_set_current(ma_data_source* pDataSource, ma_data_source* pCurrentDataSource) -{ - ma_data_source_base* pDataSourceBase = (ma_data_source_base*)pDataSource; - - if (pDataSource == NULL) { - return MA_INVALID_ARGS; - } - - pDataSourceBase->pCurrent = pCurrentDataSource; - - return MA_SUCCESS; -} - -MA_API ma_data_source* ma_data_source_get_current(const ma_data_source* pDataSource) -{ - const ma_data_source_base* pDataSourceBase = (const ma_data_source_base*)pDataSource; - - if (pDataSource == NULL) { - return NULL; - } - - return pDataSourceBase->pCurrent; -} - -MA_API ma_result ma_data_source_set_next(ma_data_source* pDataSource, ma_data_source* pNextDataSource) -{ - ma_data_source_base* pDataSourceBase = (ma_data_source_base*)pDataSource; - - if (pDataSource == NULL) { - return MA_INVALID_ARGS; - } - - pDataSourceBase->pNext = pNextDataSource; - - return MA_SUCCESS; -} - -MA_API ma_data_source* ma_data_source_get_next(const ma_data_source* pDataSource) -{ - const ma_data_source_base* pDataSourceBase = (const ma_data_source_base*)pDataSource; - - if (pDataSource == NULL) { - return NULL; - } - - return pDataSourceBase->pNext; -} - -MA_API ma_result ma_data_source_set_next_callback(ma_data_source* pDataSource, ma_data_source_get_next_proc onGetNext) -{ - ma_data_source_base* pDataSourceBase = (ma_data_source_base*)pDataSource; - - if (pDataSource == NULL) { - return MA_INVALID_ARGS; - } - - pDataSourceBase->onGetNext = onGetNext; - - return MA_SUCCESS; -} - -MA_API ma_data_source_get_next_proc ma_data_source_get_next_callback(const ma_data_source* pDataSource) -{ - const ma_data_source_base* pDataSourceBase = (const ma_data_source_base*)pDataSource; - - if (pDataSource == NULL) { - return NULL; - } - - return pDataSourceBase->onGetNext; -} - - -static ma_result ma_audio_buffer_ref__data_source_on_read(ma_data_source* pDataSource, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead) -{ - ma_audio_buffer_ref* pAudioBufferRef = (ma_audio_buffer_ref*)pDataSource; - ma_uint64 framesRead = ma_audio_buffer_ref_read_pcm_frames(pAudioBufferRef, pFramesOut, frameCount, MA_FALSE); - - if (pFramesRead != NULL) { - *pFramesRead = framesRead; - } - - if (framesRead < frameCount || framesRead == 0) { - return MA_AT_END; - } - - return MA_SUCCESS; -} - -static ma_result ma_audio_buffer_ref__data_source_on_seek(ma_data_source* pDataSource, ma_uint64 frameIndex) -{ - return ma_audio_buffer_ref_seek_to_pcm_frame((ma_audio_buffer_ref*)pDataSource, frameIndex); -} - -static ma_result ma_audio_buffer_ref__data_source_on_get_data_format(ma_data_source* pDataSource, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate, ma_channel* pChannelMap, size_t channelMapCap) -{ - ma_audio_buffer_ref* pAudioBufferRef = (ma_audio_buffer_ref*)pDataSource; - - *pFormat = pAudioBufferRef->format; - *pChannels = pAudioBufferRef->channels; - *pSampleRate = pAudioBufferRef->sampleRate; - ma_channel_map_init_standard(ma_standard_channel_map_default, pChannelMap, channelMapCap, pAudioBufferRef->channels); - - return MA_SUCCESS; -} - -static ma_result ma_audio_buffer_ref__data_source_on_get_cursor(ma_data_source* pDataSource, ma_uint64* pCursor) -{ - ma_audio_buffer_ref* pAudioBufferRef = (ma_audio_buffer_ref*)pDataSource; - - *pCursor = pAudioBufferRef->cursor; - - return MA_SUCCESS; -} - -static ma_result ma_audio_buffer_ref__data_source_on_get_length(ma_data_source* pDataSource, ma_uint64* pLength) -{ - ma_audio_buffer_ref* pAudioBufferRef = (ma_audio_buffer_ref*)pDataSource; - - *pLength = pAudioBufferRef->sizeInFrames; - - return MA_SUCCESS; -} - -static ma_data_source_vtable g_ma_audio_buffer_ref_data_source_vtable = -{ - ma_audio_buffer_ref__data_source_on_read, - ma_audio_buffer_ref__data_source_on_seek, - ma_audio_buffer_ref__data_source_on_get_data_format, - ma_audio_buffer_ref__data_source_on_get_cursor, - ma_audio_buffer_ref__data_source_on_get_length, - NULL, /* onSetLooping */ - 0 -}; - -MA_API ma_result ma_audio_buffer_ref_init(ma_format format, ma_uint32 channels, const void* pData, ma_uint64 sizeInFrames, ma_audio_buffer_ref* pAudioBufferRef) -{ - ma_result result; - ma_data_source_config dataSourceConfig; - - if (pAudioBufferRef == NULL) { - return MA_INVALID_ARGS; - } - - MA_ZERO_OBJECT(pAudioBufferRef); - - dataSourceConfig = ma_data_source_config_init(); - dataSourceConfig.vtable = &g_ma_audio_buffer_ref_data_source_vtable; - - result = ma_data_source_init(&dataSourceConfig, &pAudioBufferRef->ds); - if (result != MA_SUCCESS) { - return result; - } - - pAudioBufferRef->format = format; - pAudioBufferRef->channels = channels; - pAudioBufferRef->sampleRate = 0; /* TODO: Version 0.12. Set this to sampleRate. */ - pAudioBufferRef->cursor = 0; - pAudioBufferRef->sizeInFrames = sizeInFrames; - pAudioBufferRef->pData = pData; - - return MA_SUCCESS; -} - -MA_API void ma_audio_buffer_ref_uninit(ma_audio_buffer_ref* pAudioBufferRef) -{ - if (pAudioBufferRef == NULL) { - return; - } - - ma_data_source_uninit(&pAudioBufferRef->ds); -} - -MA_API ma_result ma_audio_buffer_ref_set_data(ma_audio_buffer_ref* pAudioBufferRef, const void* pData, ma_uint64 sizeInFrames) -{ - if (pAudioBufferRef == NULL) { - return MA_INVALID_ARGS; - } - - pAudioBufferRef->cursor = 0; - pAudioBufferRef->sizeInFrames = sizeInFrames; - pAudioBufferRef->pData = pData; - - return MA_SUCCESS; -} - -MA_API ma_uint64 ma_audio_buffer_ref_read_pcm_frames(ma_audio_buffer_ref* pAudioBufferRef, void* pFramesOut, ma_uint64 frameCount, ma_bool32 loop) -{ - ma_uint64 totalFramesRead = 0; - - if (pAudioBufferRef == NULL) { - return 0; - } - - if (frameCount == 0) { - return 0; - } - - while (totalFramesRead < frameCount) { - ma_uint64 framesAvailable = pAudioBufferRef->sizeInFrames - pAudioBufferRef->cursor; - ma_uint64 framesRemaining = frameCount - totalFramesRead; - ma_uint64 framesToRead; - - framesToRead = framesRemaining; - if (framesToRead > framesAvailable) { - framesToRead = framesAvailable; - } - - if (pFramesOut != NULL) { - ma_copy_pcm_frames(ma_offset_ptr(pFramesOut, totalFramesRead * ma_get_bytes_per_frame(pAudioBufferRef->format, pAudioBufferRef->channels)), ma_offset_ptr(pAudioBufferRef->pData, pAudioBufferRef->cursor * ma_get_bytes_per_frame(pAudioBufferRef->format, pAudioBufferRef->channels)), framesToRead, pAudioBufferRef->format, pAudioBufferRef->channels); - } - - totalFramesRead += framesToRead; - - pAudioBufferRef->cursor += framesToRead; - if (pAudioBufferRef->cursor == pAudioBufferRef->sizeInFrames) { - if (loop) { - pAudioBufferRef->cursor = 0; - } else { - break; /* We've reached the end and we're not looping. Done. */ - } - } - - MA_ASSERT(pAudioBufferRef->cursor < pAudioBufferRef->sizeInFrames); - } - - return totalFramesRead; -} - -MA_API ma_result ma_audio_buffer_ref_seek_to_pcm_frame(ma_audio_buffer_ref* pAudioBufferRef, ma_uint64 frameIndex) -{ - if (pAudioBufferRef == NULL) { - return MA_INVALID_ARGS; - } - - if (frameIndex > pAudioBufferRef->sizeInFrames) { - return MA_INVALID_ARGS; - } - - pAudioBufferRef->cursor = (size_t)frameIndex; - - return MA_SUCCESS; -} - -MA_API ma_result ma_audio_buffer_ref_map(ma_audio_buffer_ref* pAudioBufferRef, void** ppFramesOut, ma_uint64* pFrameCount) -{ - ma_uint64 framesAvailable; - ma_uint64 frameCount = 0; - - if (ppFramesOut != NULL) { - *ppFramesOut = NULL; /* Safety. */ - } - - if (pFrameCount != NULL) { - frameCount = *pFrameCount; - *pFrameCount = 0; /* Safety. */ - } - - if (pAudioBufferRef == NULL || ppFramesOut == NULL || pFrameCount == NULL) { - return MA_INVALID_ARGS; - } - - framesAvailable = pAudioBufferRef->sizeInFrames - pAudioBufferRef->cursor; - if (frameCount > framesAvailable) { - frameCount = framesAvailable; - } - - *ppFramesOut = ma_offset_ptr(pAudioBufferRef->pData, pAudioBufferRef->cursor * ma_get_bytes_per_frame(pAudioBufferRef->format, pAudioBufferRef->channels)); - *pFrameCount = frameCount; - - return MA_SUCCESS; -} - -MA_API ma_result ma_audio_buffer_ref_unmap(ma_audio_buffer_ref* pAudioBufferRef, ma_uint64 frameCount) -{ - ma_uint64 framesAvailable; - - if (pAudioBufferRef == NULL) { - return MA_INVALID_ARGS; - } - - framesAvailable = pAudioBufferRef->sizeInFrames - pAudioBufferRef->cursor; - if (frameCount > framesAvailable) { - return MA_INVALID_ARGS; /* The frame count was too big. This should never happen in an unmapping. Need to make sure the caller is aware of this. */ - } - - pAudioBufferRef->cursor += frameCount; - - if (pAudioBufferRef->cursor == pAudioBufferRef->sizeInFrames) { - return MA_AT_END; /* Successful. Need to tell the caller that the end has been reached so that it can loop if desired. */ - } else { - return MA_SUCCESS; - } -} - -MA_API ma_bool32 ma_audio_buffer_ref_at_end(const ma_audio_buffer_ref* pAudioBufferRef) -{ - if (pAudioBufferRef == NULL) { - return MA_FALSE; - } - - return pAudioBufferRef->cursor == pAudioBufferRef->sizeInFrames; -} - -MA_API ma_result ma_audio_buffer_ref_get_cursor_in_pcm_frames(const ma_audio_buffer_ref* pAudioBufferRef, ma_uint64* pCursor) -{ - if (pCursor == NULL) { - return MA_INVALID_ARGS; - } - - *pCursor = 0; - - if (pAudioBufferRef == NULL) { - return MA_INVALID_ARGS; - } - - *pCursor = pAudioBufferRef->cursor; - - return MA_SUCCESS; -} - -MA_API ma_result ma_audio_buffer_ref_get_length_in_pcm_frames(const ma_audio_buffer_ref* pAudioBufferRef, ma_uint64* pLength) -{ - if (pLength == NULL) { - return MA_INVALID_ARGS; - } - - *pLength = 0; - - if (pAudioBufferRef == NULL) { - return MA_INVALID_ARGS; - } - - *pLength = pAudioBufferRef->sizeInFrames; - - return MA_SUCCESS; -} - -MA_API ma_result ma_audio_buffer_ref_get_available_frames(const ma_audio_buffer_ref* pAudioBufferRef, ma_uint64* pAvailableFrames) -{ - if (pAvailableFrames == NULL) { - return MA_INVALID_ARGS; - } - - *pAvailableFrames = 0; - - if (pAudioBufferRef == NULL) { - return MA_INVALID_ARGS; - } - - if (pAudioBufferRef->sizeInFrames <= pAudioBufferRef->cursor) { - *pAvailableFrames = 0; - } else { - *pAvailableFrames = pAudioBufferRef->sizeInFrames - pAudioBufferRef->cursor; - } - - return MA_SUCCESS; -} - - - - -MA_API ma_audio_buffer_config ma_audio_buffer_config_init(ma_format format, ma_uint32 channels, ma_uint64 sizeInFrames, const void* pData, const ma_allocation_callbacks* pAllocationCallbacks) -{ - ma_audio_buffer_config config; - - MA_ZERO_OBJECT(&config); - config.format = format; - config.channels = channels; - config.sampleRate = 0; /* TODO: Version 0.12. Set this to sampleRate. */ - config.sizeInFrames = sizeInFrames; - config.pData = pData; - ma_allocation_callbacks_init_copy(&config.allocationCallbacks, pAllocationCallbacks); - - return config; -} - -static ma_result ma_audio_buffer_init_ex(const ma_audio_buffer_config* pConfig, ma_bool32 doCopy, ma_audio_buffer* pAudioBuffer) -{ - ma_result result; - - if (pAudioBuffer == NULL) { - return MA_INVALID_ARGS; - } - - MA_ZERO_MEMORY(pAudioBuffer, sizeof(*pAudioBuffer) - sizeof(pAudioBuffer->_pExtraData)); /* Safety. Don't overwrite the extra data. */ - - if (pConfig == NULL) { - return MA_INVALID_ARGS; - } - - if (pConfig->sizeInFrames == 0) { - return MA_INVALID_ARGS; /* Not allowing buffer sizes of 0 frames. */ - } - - result = ma_audio_buffer_ref_init(pConfig->format, pConfig->channels, NULL, 0, &pAudioBuffer->ref); - if (result != MA_SUCCESS) { - return result; - } - - /* TODO: Version 0.12. Set this in ma_audio_buffer_ref_init() instead of here. */ - pAudioBuffer->ref.sampleRate = pConfig->sampleRate; - - ma_allocation_callbacks_init_copy(&pAudioBuffer->allocationCallbacks, &pConfig->allocationCallbacks); - - if (doCopy) { - ma_uint64 allocationSizeInBytes; - void* pData; - - allocationSizeInBytes = pConfig->sizeInFrames * ma_get_bytes_per_frame(pConfig->format, pConfig->channels); - if (allocationSizeInBytes > MA_SIZE_MAX) { - return MA_OUT_OF_MEMORY; /* Too big. */ - } - - pData = ma_malloc((size_t)allocationSizeInBytes, &pAudioBuffer->allocationCallbacks); /* Safe cast to size_t. */ - if (pData == NULL) { - return MA_OUT_OF_MEMORY; - } - - if (pConfig->pData != NULL) { - ma_copy_pcm_frames(pData, pConfig->pData, pConfig->sizeInFrames, pConfig->format, pConfig->channels); - } else { - ma_silence_pcm_frames(pData, pConfig->sizeInFrames, pConfig->format, pConfig->channels); - } - - ma_audio_buffer_ref_set_data(&pAudioBuffer->ref, pData, pConfig->sizeInFrames); - pAudioBuffer->ownsData = MA_TRUE; - } else { - ma_audio_buffer_ref_set_data(&pAudioBuffer->ref, pConfig->pData, pConfig->sizeInFrames); - pAudioBuffer->ownsData = MA_FALSE; - } - - return MA_SUCCESS; -} - -static void ma_audio_buffer_uninit_ex(ma_audio_buffer* pAudioBuffer, ma_bool32 doFree) -{ - if (pAudioBuffer == NULL) { - return; - } - - if (pAudioBuffer->ownsData && pAudioBuffer->ref.pData != &pAudioBuffer->_pExtraData[0]) { - ma_free((void*)pAudioBuffer->ref.pData, &pAudioBuffer->allocationCallbacks); /* Naugty const cast, but OK in this case since we've guarded it with the ownsData check. */ - } - - if (doFree) { - ma_free(pAudioBuffer, &pAudioBuffer->allocationCallbacks); - } - - ma_audio_buffer_ref_uninit(&pAudioBuffer->ref); -} - -MA_API ma_result ma_audio_buffer_init(const ma_audio_buffer_config* pConfig, ma_audio_buffer* pAudioBuffer) -{ - return ma_audio_buffer_init_ex(pConfig, MA_FALSE, pAudioBuffer); -} - -MA_API ma_result ma_audio_buffer_init_copy(const ma_audio_buffer_config* pConfig, ma_audio_buffer* pAudioBuffer) -{ - return ma_audio_buffer_init_ex(pConfig, MA_TRUE, pAudioBuffer); -} - -MA_API ma_result ma_audio_buffer_alloc_and_init(const ma_audio_buffer_config* pConfig, ma_audio_buffer** ppAudioBuffer) -{ - ma_result result; - ma_audio_buffer* pAudioBuffer; - ma_audio_buffer_config innerConfig; /* We'll be making some changes to the config, so need to make a copy. */ - ma_uint64 allocationSizeInBytes; - - if (ppAudioBuffer == NULL) { - return MA_INVALID_ARGS; - } - - *ppAudioBuffer = NULL; /* Safety. */ - - if (pConfig == NULL) { - return MA_INVALID_ARGS; - } - - innerConfig = *pConfig; - ma_allocation_callbacks_init_copy(&innerConfig.allocationCallbacks, &pConfig->allocationCallbacks); - - allocationSizeInBytes = sizeof(*pAudioBuffer) - sizeof(pAudioBuffer->_pExtraData) + (pConfig->sizeInFrames * ma_get_bytes_per_frame(pConfig->format, pConfig->channels)); - if (allocationSizeInBytes > MA_SIZE_MAX) { - return MA_OUT_OF_MEMORY; /* Too big. */ - } - - pAudioBuffer = (ma_audio_buffer*)ma_malloc((size_t)allocationSizeInBytes, &innerConfig.allocationCallbacks); /* Safe cast to size_t. */ - if (pAudioBuffer == NULL) { - return MA_OUT_OF_MEMORY; - } - - if (pConfig->pData != NULL) { - ma_copy_pcm_frames(&pAudioBuffer->_pExtraData[0], pConfig->pData, pConfig->sizeInFrames, pConfig->format, pConfig->channels); - } else { - ma_silence_pcm_frames(&pAudioBuffer->_pExtraData[0], pConfig->sizeInFrames, pConfig->format, pConfig->channels); - } - - innerConfig.pData = &pAudioBuffer->_pExtraData[0]; - - result = ma_audio_buffer_init_ex(&innerConfig, MA_FALSE, pAudioBuffer); - if (result != MA_SUCCESS) { - ma_free(pAudioBuffer, &innerConfig.allocationCallbacks); - return result; - } - - *ppAudioBuffer = pAudioBuffer; - - return MA_SUCCESS; -} - -MA_API void ma_audio_buffer_uninit(ma_audio_buffer* pAudioBuffer) -{ - ma_audio_buffer_uninit_ex(pAudioBuffer, MA_FALSE); -} - -MA_API void ma_audio_buffer_uninit_and_free(ma_audio_buffer* pAudioBuffer) -{ - ma_audio_buffer_uninit_ex(pAudioBuffer, MA_TRUE); -} - -MA_API ma_uint64 ma_audio_buffer_read_pcm_frames(ma_audio_buffer* pAudioBuffer, void* pFramesOut, ma_uint64 frameCount, ma_bool32 loop) -{ - if (pAudioBuffer == NULL) { - return 0; - } - - return ma_audio_buffer_ref_read_pcm_frames(&pAudioBuffer->ref, pFramesOut, frameCount, loop); -} - -MA_API ma_result ma_audio_buffer_seek_to_pcm_frame(ma_audio_buffer* pAudioBuffer, ma_uint64 frameIndex) -{ - if (pAudioBuffer == NULL) { - return MA_INVALID_ARGS; - } - - return ma_audio_buffer_ref_seek_to_pcm_frame(&pAudioBuffer->ref, frameIndex); -} - -MA_API ma_result ma_audio_buffer_map(ma_audio_buffer* pAudioBuffer, void** ppFramesOut, ma_uint64* pFrameCount) -{ - if (ppFramesOut != NULL) { - *ppFramesOut = NULL; /* Safety. */ - } - - if (pAudioBuffer == NULL) { - if (pFrameCount != NULL) { - *pFrameCount = 0; - } - - return MA_INVALID_ARGS; - } - - return ma_audio_buffer_ref_map(&pAudioBuffer->ref, ppFramesOut, pFrameCount); -} - -MA_API ma_result ma_audio_buffer_unmap(ma_audio_buffer* pAudioBuffer, ma_uint64 frameCount) -{ - if (pAudioBuffer == NULL) { - return MA_INVALID_ARGS; - } - - return ma_audio_buffer_ref_unmap(&pAudioBuffer->ref, frameCount); -} - -MA_API ma_bool32 ma_audio_buffer_at_end(const ma_audio_buffer* pAudioBuffer) -{ - if (pAudioBuffer == NULL) { - return MA_FALSE; - } - - return ma_audio_buffer_ref_at_end(&pAudioBuffer->ref); -} - -MA_API ma_result ma_audio_buffer_get_cursor_in_pcm_frames(const ma_audio_buffer* pAudioBuffer, ma_uint64* pCursor) -{ - if (pAudioBuffer == NULL) { - return MA_INVALID_ARGS; - } - - return ma_audio_buffer_ref_get_cursor_in_pcm_frames(&pAudioBuffer->ref, pCursor); -} - -MA_API ma_result ma_audio_buffer_get_length_in_pcm_frames(const ma_audio_buffer* pAudioBuffer, ma_uint64* pLength) -{ - if (pAudioBuffer == NULL) { - return MA_INVALID_ARGS; - } - - return ma_audio_buffer_ref_get_length_in_pcm_frames(&pAudioBuffer->ref, pLength); -} - -MA_API ma_result ma_audio_buffer_get_available_frames(const ma_audio_buffer* pAudioBuffer, ma_uint64* pAvailableFrames) -{ - if (pAvailableFrames == NULL) { - return MA_INVALID_ARGS; - } - - *pAvailableFrames = 0; - - if (pAudioBuffer == NULL) { - return MA_INVALID_ARGS; - } - - return ma_audio_buffer_ref_get_available_frames(&pAudioBuffer->ref, pAvailableFrames); -} - - - - - -MA_API ma_result ma_paged_audio_buffer_data_init(ma_format format, ma_uint32 channels, ma_paged_audio_buffer_data* pData) -{ - if (pData == NULL) { - return MA_INVALID_ARGS; - } - - MA_ZERO_OBJECT(pData); - - pData->format = format; - pData->channels = channels; - pData->pTail = &pData->head; - - return MA_SUCCESS; -} - -MA_API void ma_paged_audio_buffer_data_uninit(ma_paged_audio_buffer_data* pData, const ma_allocation_callbacks* pAllocationCallbacks) -{ - ma_paged_audio_buffer_page* pPage; - - if (pData == NULL) { - return; - } - - /* All pages need to be freed. */ - pPage = (ma_paged_audio_buffer_page*)ma_atomic_load_ptr(&pData->head.pNext); - while (pPage != NULL) { - ma_paged_audio_buffer_page* pNext = (ma_paged_audio_buffer_page*)ma_atomic_load_ptr(&pPage->pNext); - - ma_free(pPage, pAllocationCallbacks); - pPage = pNext; - } -} - -MA_API ma_paged_audio_buffer_page* ma_paged_audio_buffer_data_get_head(ma_paged_audio_buffer_data* pData) -{ - if (pData == NULL) { - return NULL; - } - - return &pData->head; -} - -MA_API ma_paged_audio_buffer_page* ma_paged_audio_buffer_data_get_tail(ma_paged_audio_buffer_data* pData) -{ - if (pData == NULL) { - return NULL; - } - - return pData->pTail; -} - -MA_API ma_result ma_paged_audio_buffer_data_get_length_in_pcm_frames(ma_paged_audio_buffer_data* pData, ma_uint64* pLength) -{ - ma_paged_audio_buffer_page* pPage; - - if (pLength == NULL) { - return MA_INVALID_ARGS; - } - - *pLength = 0; - - if (pData == NULL) { - return MA_INVALID_ARGS; - } - - /* Calculate the length from the linked list. */ - for (pPage = (ma_paged_audio_buffer_page*)ma_atomic_load_ptr(&pData->head.pNext); pPage != NULL; pPage = (ma_paged_audio_buffer_page*)ma_atomic_load_ptr(&pPage->pNext)) { - *pLength += pPage->sizeInFrames; - } - - return MA_SUCCESS; -} - -MA_API ma_result ma_paged_audio_buffer_data_allocate_page(ma_paged_audio_buffer_data* pData, ma_uint64 pageSizeInFrames, const void* pInitialData, const ma_allocation_callbacks* pAllocationCallbacks, ma_paged_audio_buffer_page** ppPage) -{ - ma_paged_audio_buffer_page* pPage; - ma_uint64 allocationSize; - - if (ppPage == NULL) { - return MA_INVALID_ARGS; - } - - *ppPage = NULL; - - if (pData == NULL) { - return MA_INVALID_ARGS; - } - - allocationSize = sizeof(*pPage) + (pageSizeInFrames * ma_get_bytes_per_frame(pData->format, pData->channels)); - if (allocationSize > MA_SIZE_MAX) { - return MA_OUT_OF_MEMORY; /* Too big. */ - } - - pPage = (ma_paged_audio_buffer_page*)ma_malloc((size_t)allocationSize, pAllocationCallbacks); /* Safe cast to size_t. */ - if (pPage == NULL) { - return MA_OUT_OF_MEMORY; - } - - pPage->pNext = NULL; - pPage->sizeInFrames = pageSizeInFrames; - - if (pInitialData != NULL) { - ma_copy_pcm_frames(pPage->pAudioData, pInitialData, pageSizeInFrames, pData->format, pData->channels); - } - - *ppPage = pPage; - - return MA_SUCCESS; -} - -MA_API ma_result ma_paged_audio_buffer_data_free_page(ma_paged_audio_buffer_data* pData, ma_paged_audio_buffer_page* pPage, const ma_allocation_callbacks* pAllocationCallbacks) -{ - if (pData == NULL || pPage == NULL) { - return MA_INVALID_ARGS; - } - - /* It's assumed the page is not attached to the list. */ - ma_free(pPage, pAllocationCallbacks); - - return MA_SUCCESS; -} - -MA_API ma_result ma_paged_audio_buffer_data_append_page(ma_paged_audio_buffer_data* pData, ma_paged_audio_buffer_page* pPage) -{ - if (pData == NULL || pPage == NULL) { - return MA_INVALID_ARGS; - } - - /* This function assumes the page has been filled with audio data by this point. As soon as we append, the page will be available for reading. */ - - /* First thing to do is update the tail. */ - for (;;) { - ma_paged_audio_buffer_page* pOldTail = (ma_paged_audio_buffer_page*)ma_atomic_load_ptr(&pData->pTail); - ma_paged_audio_buffer_page* pNewTail = pPage; - - if (ma_atomic_compare_exchange_weak_ptr((volatile void**)&pData->pTail, (void**)&pOldTail, pNewTail)) { - /* Here is where we append the page to the list. After this, the page is attached to the list and ready to be read from. */ - ma_atomic_exchange_ptr(&pOldTail->pNext, pPage); - break; /* Done. */ - } - } - - return MA_SUCCESS; -} - -MA_API ma_result ma_paged_audio_buffer_data_allocate_and_append_page(ma_paged_audio_buffer_data* pData, ma_uint32 pageSizeInFrames, const void* pInitialData, const ma_allocation_callbacks* pAllocationCallbacks) -{ - ma_result result; - ma_paged_audio_buffer_page* pPage; - - result = ma_paged_audio_buffer_data_allocate_page(pData, pageSizeInFrames, pInitialData, pAllocationCallbacks, &pPage); - if (result != MA_SUCCESS) { - return result; - } - - return ma_paged_audio_buffer_data_append_page(pData, pPage); /* <-- Should never fail. */ -} - - -MA_API ma_paged_audio_buffer_config ma_paged_audio_buffer_config_init(ma_paged_audio_buffer_data* pData) -{ - ma_paged_audio_buffer_config config; - - MA_ZERO_OBJECT(&config); - config.pData = pData; - - return config; -} - - -static ma_result ma_paged_audio_buffer__data_source_on_read(ma_data_source* pDataSource, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead) -{ - return ma_paged_audio_buffer_read_pcm_frames((ma_paged_audio_buffer*)pDataSource, pFramesOut, frameCount, pFramesRead); -} - -static ma_result ma_paged_audio_buffer__data_source_on_seek(ma_data_source* pDataSource, ma_uint64 frameIndex) -{ - return ma_paged_audio_buffer_seek_to_pcm_frame((ma_paged_audio_buffer*)pDataSource, frameIndex); -} - -static ma_result ma_paged_audio_buffer__data_source_on_get_data_format(ma_data_source* pDataSource, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate, ma_channel* pChannelMap, size_t channelMapCap) -{ - ma_paged_audio_buffer* pPagedAudioBuffer = (ma_paged_audio_buffer*)pDataSource; - - *pFormat = pPagedAudioBuffer->pData->format; - *pChannels = pPagedAudioBuffer->pData->channels; - *pSampleRate = 0; /* There is no notion of a sample rate with audio buffers. */ - ma_channel_map_init_standard(ma_standard_channel_map_default, pChannelMap, channelMapCap, pPagedAudioBuffer->pData->channels); - - return MA_SUCCESS; -} - -static ma_result ma_paged_audio_buffer__data_source_on_get_cursor(ma_data_source* pDataSource, ma_uint64* pCursor) -{ - return ma_paged_audio_buffer_get_cursor_in_pcm_frames((ma_paged_audio_buffer*)pDataSource, pCursor); -} - -static ma_result ma_paged_audio_buffer__data_source_on_get_length(ma_data_source* pDataSource, ma_uint64* pLength) -{ - return ma_paged_audio_buffer_get_length_in_pcm_frames((ma_paged_audio_buffer*)pDataSource, pLength); -} - -static ma_data_source_vtable g_ma_paged_audio_buffer_data_source_vtable = -{ - ma_paged_audio_buffer__data_source_on_read, - ma_paged_audio_buffer__data_source_on_seek, - ma_paged_audio_buffer__data_source_on_get_data_format, - ma_paged_audio_buffer__data_source_on_get_cursor, - ma_paged_audio_buffer__data_source_on_get_length, - NULL, /* onSetLooping */ - 0 -}; - -MA_API ma_result ma_paged_audio_buffer_init(const ma_paged_audio_buffer_config* pConfig, ma_paged_audio_buffer* pPagedAudioBuffer) -{ - ma_result result; - ma_data_source_config dataSourceConfig; - - if (pPagedAudioBuffer == NULL) { - return MA_INVALID_ARGS; - } - - MA_ZERO_OBJECT(pPagedAudioBuffer); - - /* A config is required for the format and channel count. */ - if (pConfig == NULL) { - return MA_INVALID_ARGS; - } - - if (pConfig->pData == NULL) { - return MA_INVALID_ARGS; /* No underlying data specified. */ - } - - dataSourceConfig = ma_data_source_config_init(); - dataSourceConfig.vtable = &g_ma_paged_audio_buffer_data_source_vtable; - - result = ma_data_source_init(&dataSourceConfig, &pPagedAudioBuffer->ds); - if (result != MA_SUCCESS) { - return result; - } - - pPagedAudioBuffer->pData = pConfig->pData; - pPagedAudioBuffer->pCurrent = ma_paged_audio_buffer_data_get_head(pConfig->pData); - pPagedAudioBuffer->relativeCursor = 0; - pPagedAudioBuffer->absoluteCursor = 0; - - return MA_SUCCESS; -} - -MA_API void ma_paged_audio_buffer_uninit(ma_paged_audio_buffer* pPagedAudioBuffer) -{ - if (pPagedAudioBuffer == NULL) { - return; - } - - /* Nothing to do. The data needs to be deleted separately. */ -} - -MA_API ma_result ma_paged_audio_buffer_read_pcm_frames(ma_paged_audio_buffer* pPagedAudioBuffer, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead) -{ - ma_result result = MA_SUCCESS; - ma_uint64 totalFramesRead = 0; - ma_format format; - ma_uint32 channels; - - if (pPagedAudioBuffer == NULL) { - return MA_INVALID_ARGS; - } - - format = pPagedAudioBuffer->pData->format; - channels = pPagedAudioBuffer->pData->channels; - - while (totalFramesRead < frameCount) { - /* Read from the current page. The buffer should never be in a state where this is NULL. */ - ma_uint64 framesRemainingInCurrentPage; - ma_uint64 framesRemainingToRead = frameCount - totalFramesRead; - ma_uint64 framesToReadThisIteration; - - MA_ASSERT(pPagedAudioBuffer->pCurrent != NULL); - - framesRemainingInCurrentPage = pPagedAudioBuffer->pCurrent->sizeInFrames - pPagedAudioBuffer->relativeCursor; - - framesToReadThisIteration = ma_min(framesRemainingInCurrentPage, framesRemainingToRead); - ma_copy_pcm_frames(ma_offset_pcm_frames_ptr(pFramesOut, totalFramesRead, format, channels), ma_offset_pcm_frames_ptr(pPagedAudioBuffer->pCurrent->pAudioData, pPagedAudioBuffer->relativeCursor, format, channels), framesToReadThisIteration, format, channels); - totalFramesRead += framesToReadThisIteration; - - pPagedAudioBuffer->absoluteCursor += framesToReadThisIteration; - pPagedAudioBuffer->relativeCursor += framesToReadThisIteration; - - /* Move to the next page if necessary. If there's no more pages, we need to return MA_AT_END. */ - MA_ASSERT(pPagedAudioBuffer->relativeCursor <= pPagedAudioBuffer->pCurrent->sizeInFrames); - - if (pPagedAudioBuffer->relativeCursor == pPagedAudioBuffer->pCurrent->sizeInFrames) { - /* We reached the end of the page. Need to move to the next. If there's no more pages, we're done. */ - ma_paged_audio_buffer_page* pNext = (ma_paged_audio_buffer_page*)ma_atomic_load_ptr(&pPagedAudioBuffer->pCurrent->pNext); - if (pNext == NULL) { - result = MA_AT_END; - break; /* We've reached the end. */ - } else { - pPagedAudioBuffer->pCurrent = pNext; - pPagedAudioBuffer->relativeCursor = 0; - } - } - } - - if (pFramesRead != NULL) { - *pFramesRead = totalFramesRead; - } - - return result; -} - -MA_API ma_result ma_paged_audio_buffer_seek_to_pcm_frame(ma_paged_audio_buffer* pPagedAudioBuffer, ma_uint64 frameIndex) -{ - if (pPagedAudioBuffer == NULL) { - return MA_INVALID_ARGS; - } - - if (frameIndex == pPagedAudioBuffer->absoluteCursor) { - return MA_SUCCESS; /* Nothing to do. */ - } - - if (frameIndex < pPagedAudioBuffer->absoluteCursor) { - /* Moving backwards. Need to move the cursor back to the start, and then move forward. */ - pPagedAudioBuffer->pCurrent = ma_paged_audio_buffer_data_get_head(pPagedAudioBuffer->pData); - pPagedAudioBuffer->absoluteCursor = 0; - pPagedAudioBuffer->relativeCursor = 0; - - /* Fall through to the forward seeking section below. */ - } - - if (frameIndex > pPagedAudioBuffer->absoluteCursor) { - /* Moving forward. */ - ma_paged_audio_buffer_page* pPage; - ma_uint64 runningCursor = 0; - - for (pPage = (ma_paged_audio_buffer_page*)ma_atomic_load_ptr(&ma_paged_audio_buffer_data_get_head(pPagedAudioBuffer->pData)->pNext); pPage != NULL; pPage = (ma_paged_audio_buffer_page*)ma_atomic_load_ptr(&pPage->pNext)) { - ma_uint64 pageRangeBeg = runningCursor; - ma_uint64 pageRangeEnd = pageRangeBeg + pPage->sizeInFrames; - - if (frameIndex >= pageRangeBeg) { - if (frameIndex < pageRangeEnd || (frameIndex == pageRangeEnd && pPage == (ma_paged_audio_buffer_page*)ma_atomic_load_ptr(ma_paged_audio_buffer_data_get_tail(pPagedAudioBuffer->pData)))) { /* A small edge case - allow seeking to the very end of the buffer. */ - /* We found the page. */ - pPagedAudioBuffer->pCurrent = pPage; - pPagedAudioBuffer->absoluteCursor = frameIndex; - pPagedAudioBuffer->relativeCursor = frameIndex - pageRangeBeg; - return MA_SUCCESS; - } - } - - runningCursor = pageRangeEnd; - } - - /* Getting here means we tried seeking too far forward. Don't change any state. */ - return MA_BAD_SEEK; - } - - return MA_SUCCESS; -} - -MA_API ma_result ma_paged_audio_buffer_get_cursor_in_pcm_frames(ma_paged_audio_buffer* pPagedAudioBuffer, ma_uint64* pCursor) -{ - if (pCursor == NULL) { - return MA_INVALID_ARGS; - } - - *pCursor = 0; /* Safety. */ - - if (pPagedAudioBuffer == NULL) { - return MA_INVALID_ARGS; - } - - *pCursor = pPagedAudioBuffer->absoluteCursor; - - return MA_SUCCESS; -} - -MA_API ma_result ma_paged_audio_buffer_get_length_in_pcm_frames(ma_paged_audio_buffer* pPagedAudioBuffer, ma_uint64* pLength) -{ - return ma_paged_audio_buffer_data_get_length_in_pcm_frames(pPagedAudioBuffer->pData, pLength); -} - - - -/************************************************************************************************************************************************************** - -VFS - -**************************************************************************************************************************************************************/ -MA_API ma_result ma_vfs_open(ma_vfs* pVFS, const char* pFilePath, ma_uint32 openMode, ma_vfs_file* pFile) -{ - ma_vfs_callbacks* pCallbacks = (ma_vfs_callbacks*)pVFS; - - if (pFile == NULL) { - return MA_INVALID_ARGS; - } - - *pFile = NULL; - - if (pVFS == NULL || pFilePath == NULL || openMode == 0) { - return MA_INVALID_ARGS; - } - - if (pCallbacks->onOpen == NULL) { - return MA_NOT_IMPLEMENTED; - } - - return pCallbacks->onOpen(pVFS, pFilePath, openMode, pFile); -} - -MA_API ma_result ma_vfs_open_w(ma_vfs* pVFS, const wchar_t* pFilePath, ma_uint32 openMode, ma_vfs_file* pFile) -{ - ma_vfs_callbacks* pCallbacks = (ma_vfs_callbacks*)pVFS; - - if (pFile == NULL) { - return MA_INVALID_ARGS; - } - - *pFile = NULL; - - if (pVFS == NULL || pFilePath == NULL || openMode == 0) { - return MA_INVALID_ARGS; - } - - if (pCallbacks->onOpenW == NULL) { - return MA_NOT_IMPLEMENTED; - } - - return pCallbacks->onOpenW(pVFS, pFilePath, openMode, pFile); -} - -MA_API ma_result ma_vfs_close(ma_vfs* pVFS, ma_vfs_file file) -{ - ma_vfs_callbacks* pCallbacks = (ma_vfs_callbacks*)pVFS; - - if (pVFS == NULL || file == NULL) { - return MA_INVALID_ARGS; - } - - if (pCallbacks->onClose == NULL) { - return MA_NOT_IMPLEMENTED; - } - - return pCallbacks->onClose(pVFS, file); -} - -MA_API ma_result ma_vfs_read(ma_vfs* pVFS, ma_vfs_file file, void* pDst, size_t sizeInBytes, size_t* pBytesRead) -{ - ma_vfs_callbacks* pCallbacks = (ma_vfs_callbacks*)pVFS; - ma_result result; - size_t bytesRead = 0; - - if (pBytesRead != NULL) { - *pBytesRead = 0; - } - - if (pVFS == NULL || file == NULL || pDst == NULL) { - return MA_INVALID_ARGS; - } - - if (pCallbacks->onRead == NULL) { - return MA_NOT_IMPLEMENTED; - } - - result = pCallbacks->onRead(pVFS, file, pDst, sizeInBytes, &bytesRead); - - if (pBytesRead != NULL) { - *pBytesRead = bytesRead; - } - - if (result == MA_SUCCESS && bytesRead == 0 && sizeInBytes > 0) { - result = MA_AT_END; - } - - return result; -} - -MA_API ma_result ma_vfs_write(ma_vfs* pVFS, ma_vfs_file file, const void* pSrc, size_t sizeInBytes, size_t* pBytesWritten) -{ - ma_vfs_callbacks* pCallbacks = (ma_vfs_callbacks*)pVFS; - - if (pBytesWritten != NULL) { - *pBytesWritten = 0; - } - - if (pVFS == NULL || file == NULL || pSrc == NULL) { - return MA_INVALID_ARGS; - } - - if (pCallbacks->onWrite == NULL) { - return MA_NOT_IMPLEMENTED; - } - - return pCallbacks->onWrite(pVFS, file, pSrc, sizeInBytes, pBytesWritten); -} - -MA_API ma_result ma_vfs_seek(ma_vfs* pVFS, ma_vfs_file file, ma_int64 offset, ma_seek_origin origin) -{ - ma_vfs_callbacks* pCallbacks = (ma_vfs_callbacks*)pVFS; - - if (pVFS == NULL || file == NULL) { - return MA_INVALID_ARGS; - } - - if (pCallbacks->onSeek == NULL) { - return MA_NOT_IMPLEMENTED; - } - - return pCallbacks->onSeek(pVFS, file, offset, origin); -} - -MA_API ma_result ma_vfs_tell(ma_vfs* pVFS, ma_vfs_file file, ma_int64* pCursor) -{ - ma_vfs_callbacks* pCallbacks = (ma_vfs_callbacks*)pVFS; - - if (pCursor == NULL) { - return MA_INVALID_ARGS; - } - - *pCursor = 0; - - if (pVFS == NULL || file == NULL) { - return MA_INVALID_ARGS; - } - - if (pCallbacks->onTell == NULL) { - return MA_NOT_IMPLEMENTED; - } - - return pCallbacks->onTell(pVFS, file, pCursor); -} - -MA_API ma_result ma_vfs_info(ma_vfs* pVFS, ma_vfs_file file, ma_file_info* pInfo) -{ - ma_vfs_callbacks* pCallbacks = (ma_vfs_callbacks*)pVFS; - - if (pInfo == NULL) { - return MA_INVALID_ARGS; - } - - MA_ZERO_OBJECT(pInfo); - - if (pVFS == NULL || file == NULL) { - return MA_INVALID_ARGS; - } - - if (pCallbacks->onInfo == NULL) { - return MA_NOT_IMPLEMENTED; - } - - return pCallbacks->onInfo(pVFS, file, pInfo); -} - - -#if !defined(MA_USE_WIN32_FILEIO) && (defined(MA_WIN32) && defined(MA_WIN32_DESKTOP) && !defined(MA_NO_WIN32_FILEIO) && !defined(MA_POSIX)) - #define MA_USE_WIN32_FILEIO -#endif - -#if defined(MA_USE_WIN32_FILEIO) -/* -We need to dynamically load SetFilePointer or SetFilePointerEx because older versions of Windows do -not have the Ex version. We therefore need to do some dynamic branching depending on what's available. - -We load these when we load our first file from the default VFS. It's left open for the life of the -program and is left to the OS to uninitialize when the program terminates. -*/ -typedef DWORD (__stdcall * ma_SetFilePointer_proc)(HANDLE hFile, LONG lDistanceToMove, LONG* lpDistanceToMoveHigh, DWORD dwMoveMethod); -typedef BOOL (__stdcall * ma_SetFilePointerEx_proc)(HANDLE hFile, LARGE_INTEGER liDistanceToMove, LARGE_INTEGER* lpNewFilePointer, DWORD dwMoveMethod); - -static ma_handle hKernel32DLL = NULL; -static ma_SetFilePointer_proc ma_SetFilePointer = NULL; -static ma_SetFilePointerEx_proc ma_SetFilePointerEx = NULL; - -static void ma_win32_fileio_init(void) -{ - if (hKernel32DLL == NULL) { - hKernel32DLL = ma_dlopen(NULL, "kernel32.dll"); - if (hKernel32DLL != NULL) { - ma_SetFilePointer = (ma_SetFilePointer_proc) ma_dlsym(NULL, hKernel32DLL, "SetFilePointer"); - ma_SetFilePointerEx = (ma_SetFilePointerEx_proc)ma_dlsym(NULL, hKernel32DLL, "SetFilePointerEx"); - } - } -} - -static void ma_default_vfs__get_open_settings_win32(ma_uint32 openMode, DWORD* pDesiredAccess, DWORD* pShareMode, DWORD* pCreationDisposition) -{ - *pDesiredAccess = 0; - if ((openMode & MA_OPEN_MODE_READ) != 0) { - *pDesiredAccess |= GENERIC_READ; - } - if ((openMode & MA_OPEN_MODE_WRITE) != 0) { - *pDesiredAccess |= GENERIC_WRITE; - } - - *pShareMode = 0; - if ((openMode & MA_OPEN_MODE_READ) != 0) { - *pShareMode |= FILE_SHARE_READ; - } - - if ((openMode & MA_OPEN_MODE_WRITE) != 0) { - *pCreationDisposition = CREATE_ALWAYS; /* Opening in write mode. Truncate. */ - } else { - *pCreationDisposition = OPEN_EXISTING; /* Opening in read mode. File must exist. */ - } -} - -static ma_result ma_default_vfs_open__win32(ma_vfs* pVFS, const char* pFilePath, ma_uint32 openMode, ma_vfs_file* pFile) -{ - HANDLE hFile; - DWORD dwDesiredAccess; - DWORD dwShareMode; - DWORD dwCreationDisposition; - - (void)pVFS; - - /* Load some Win32 symbols dynamically so we can dynamically check for the existence of SetFilePointerEx. */ - ma_win32_fileio_init(); - - ma_default_vfs__get_open_settings_win32(openMode, &dwDesiredAccess, &dwShareMode, &dwCreationDisposition); - - hFile = CreateFileA(pFilePath, dwDesiredAccess, dwShareMode, NULL, dwCreationDisposition, FILE_ATTRIBUTE_NORMAL, NULL); - if (hFile == INVALID_HANDLE_VALUE) { - return ma_result_from_GetLastError(GetLastError()); - } - - *pFile = hFile; - return MA_SUCCESS; -} - -static ma_result ma_default_vfs_open_w__win32(ma_vfs* pVFS, const wchar_t* pFilePath, ma_uint32 openMode, ma_vfs_file* pFile) -{ - HANDLE hFile; - DWORD dwDesiredAccess; - DWORD dwShareMode; - DWORD dwCreationDisposition; - - (void)pVFS; - - /* Load some Win32 symbols dynamically so we can dynamically check for the existence of SetFilePointerEx. */ - ma_win32_fileio_init(); - - ma_default_vfs__get_open_settings_win32(openMode, &dwDesiredAccess, &dwShareMode, &dwCreationDisposition); - - hFile = CreateFileW(pFilePath, dwDesiredAccess, dwShareMode, NULL, dwCreationDisposition, FILE_ATTRIBUTE_NORMAL, NULL); - if (hFile == INVALID_HANDLE_VALUE) { - return ma_result_from_GetLastError(GetLastError()); - } - - *pFile = hFile; - return MA_SUCCESS; -} - -static ma_result ma_default_vfs_close__win32(ma_vfs* pVFS, ma_vfs_file file) -{ - (void)pVFS; - - if (CloseHandle((HANDLE)file) == 0) { - return ma_result_from_GetLastError(GetLastError()); - } - - return MA_SUCCESS; -} - - -static ma_result ma_default_vfs_read__win32(ma_vfs* pVFS, ma_vfs_file file, void* pDst, size_t sizeInBytes, size_t* pBytesRead) -{ - ma_result result = MA_SUCCESS; - size_t totalBytesRead; - - (void)pVFS; - - totalBytesRead = 0; - while (totalBytesRead < sizeInBytes) { - size_t bytesRemaining; - DWORD bytesToRead; - DWORD bytesRead; - BOOL readResult; - - bytesRemaining = sizeInBytes - totalBytesRead; - if (bytesRemaining >= 0xFFFFFFFF) { - bytesToRead = 0xFFFFFFFF; - } else { - bytesToRead = (DWORD)bytesRemaining; - } - - readResult = ReadFile((HANDLE)file, ma_offset_ptr(pDst, totalBytesRead), bytesToRead, &bytesRead, NULL); - if (readResult == 1 && bytesRead == 0) { - result = MA_AT_END; - break; /* EOF */ - } - - totalBytesRead += bytesRead; - - if (bytesRead < bytesToRead) { - break; /* EOF */ - } - - if (readResult == 0) { - result = ma_result_from_GetLastError(GetLastError()); - break; - } - } - - if (pBytesRead != NULL) { - *pBytesRead = totalBytesRead; - } - - return result; -} - -static ma_result ma_default_vfs_write__win32(ma_vfs* pVFS, ma_vfs_file file, const void* pSrc, size_t sizeInBytes, size_t* pBytesWritten) -{ - ma_result result = MA_SUCCESS; - size_t totalBytesWritten; - - (void)pVFS; - - totalBytesWritten = 0; - while (totalBytesWritten < sizeInBytes) { - size_t bytesRemaining; - DWORD bytesToWrite; - DWORD bytesWritten; - BOOL writeResult; - - bytesRemaining = sizeInBytes - totalBytesWritten; - if (bytesRemaining >= 0xFFFFFFFF) { - bytesToWrite = 0xFFFFFFFF; - } else { - bytesToWrite = (DWORD)bytesRemaining; - } - - writeResult = WriteFile((HANDLE)file, ma_offset_ptr(pSrc, totalBytesWritten), bytesToWrite, &bytesWritten, NULL); - totalBytesWritten += bytesWritten; - - if (writeResult == 0) { - result = ma_result_from_GetLastError(GetLastError()); - break; - } - } - - if (pBytesWritten != NULL) { - *pBytesWritten = totalBytesWritten; - } - - return result; -} - - -static ma_result ma_default_vfs_seek__win32(ma_vfs* pVFS, ma_vfs_file file, ma_int64 offset, ma_seek_origin origin) -{ - LARGE_INTEGER liDistanceToMove; - DWORD dwMoveMethod; - BOOL result; - - (void)pVFS; - - liDistanceToMove.QuadPart = offset; - - /* */ if (origin == ma_seek_origin_current) { - dwMoveMethod = FILE_CURRENT; - } else if (origin == ma_seek_origin_end) { - dwMoveMethod = FILE_END; - } else { - dwMoveMethod = FILE_BEGIN; - } - - if (ma_SetFilePointerEx != NULL) { - result = ma_SetFilePointerEx((HANDLE)file, liDistanceToMove, NULL, dwMoveMethod); - } else if (ma_SetFilePointer != NULL) { - /* No SetFilePointerEx() so restrict to 31 bits. */ - if (offset > 0x7FFFFFFF) { - return MA_OUT_OF_RANGE; - } - - result = ma_SetFilePointer((HANDLE)file, (LONG)liDistanceToMove.QuadPart, NULL, dwMoveMethod); - } else { - return MA_NOT_IMPLEMENTED; - } - - if (result == 0) { - return ma_result_from_GetLastError(GetLastError()); - } - - return MA_SUCCESS; -} - -static ma_result ma_default_vfs_tell__win32(ma_vfs* pVFS, ma_vfs_file file, ma_int64* pCursor) -{ - LARGE_INTEGER liZero; - LARGE_INTEGER liTell; - BOOL result; - - (void)pVFS; - - liZero.QuadPart = 0; - - if (ma_SetFilePointerEx != NULL) { - result = ma_SetFilePointerEx((HANDLE)file, liZero, &liTell, FILE_CURRENT); - } else if (ma_SetFilePointer != NULL) { - LONG tell; - - result = ma_SetFilePointer((HANDLE)file, (LONG)liZero.QuadPart, &tell, FILE_CURRENT); - liTell.QuadPart = tell; - } else { - return MA_NOT_IMPLEMENTED; - } - - if (result == 0) { - return ma_result_from_GetLastError(GetLastError()); - } - - if (pCursor != NULL) { - *pCursor = liTell.QuadPart; - } - - return MA_SUCCESS; -} - -static ma_result ma_default_vfs_info__win32(ma_vfs* pVFS, ma_vfs_file file, ma_file_info* pInfo) -{ - BY_HANDLE_FILE_INFORMATION fi; - BOOL result; - - (void)pVFS; - - result = GetFileInformationByHandle((HANDLE)file, &fi); - if (result == 0) { - return ma_result_from_GetLastError(GetLastError()); - } - - pInfo->sizeInBytes = ((ma_uint64)fi.nFileSizeHigh << 32) | ((ma_uint64)fi.nFileSizeLow); - - return MA_SUCCESS; -} -#else -static ma_result ma_default_vfs_open__stdio(ma_vfs* pVFS, const char* pFilePath, ma_uint32 openMode, ma_vfs_file* pFile) -{ - ma_result result; - FILE* pFileStd; - const char* pOpenModeStr; - - MA_ASSERT(pFilePath != NULL); - MA_ASSERT(openMode != 0); - MA_ASSERT(pFile != NULL); - - (void)pVFS; - - if ((openMode & MA_OPEN_MODE_READ) != 0) { - if ((openMode & MA_OPEN_MODE_WRITE) != 0) { - pOpenModeStr = "r+"; - } else { - pOpenModeStr = "rb"; - } - } else { - pOpenModeStr = "wb"; - } - - result = ma_fopen(&pFileStd, pFilePath, pOpenModeStr); - if (result != MA_SUCCESS) { - return result; - } - - *pFile = pFileStd; - - return MA_SUCCESS; -} - -static ma_result ma_default_vfs_open_w__stdio(ma_vfs* pVFS, const wchar_t* pFilePath, ma_uint32 openMode, ma_vfs_file* pFile) -{ - ma_result result; - FILE* pFileStd; - const wchar_t* pOpenModeStr; - - MA_ASSERT(pFilePath != NULL); - MA_ASSERT(openMode != 0); - MA_ASSERT(pFile != NULL); - - (void)pVFS; - - if ((openMode & MA_OPEN_MODE_READ) != 0) { - if ((openMode & MA_OPEN_MODE_WRITE) != 0) { - pOpenModeStr = L"r+"; - } else { - pOpenModeStr = L"rb"; - } - } else { - pOpenModeStr = L"wb"; - } - - result = ma_wfopen(&pFileStd, pFilePath, pOpenModeStr, (pVFS != NULL) ? &((ma_default_vfs*)pVFS)->allocationCallbacks : NULL); - if (result != MA_SUCCESS) { - return result; - } - - *pFile = pFileStd; - - return MA_SUCCESS; -} - -static ma_result ma_default_vfs_close__stdio(ma_vfs* pVFS, ma_vfs_file file) -{ - MA_ASSERT(file != NULL); - - (void)pVFS; - - fclose((FILE*)file); - - return MA_SUCCESS; -} - -static ma_result ma_default_vfs_read__stdio(ma_vfs* pVFS, ma_vfs_file file, void* pDst, size_t sizeInBytes, size_t* pBytesRead) -{ - size_t result; - - MA_ASSERT(file != NULL); - MA_ASSERT(pDst != NULL); - - (void)pVFS; - - result = fread(pDst, 1, sizeInBytes, (FILE*)file); - - if (pBytesRead != NULL) { - *pBytesRead = result; - } - - if (result != sizeInBytes) { - if (result == 0 && feof((FILE*)file)) { - return MA_AT_END; - } else { - return ma_result_from_errno(ferror((FILE*)file)); - } - } - - return MA_SUCCESS; -} - -static ma_result ma_default_vfs_write__stdio(ma_vfs* pVFS, ma_vfs_file file, const void* pSrc, size_t sizeInBytes, size_t* pBytesWritten) -{ - size_t result; - - MA_ASSERT(file != NULL); - MA_ASSERT(pSrc != NULL); - - (void)pVFS; - - result = fwrite(pSrc, 1, sizeInBytes, (FILE*)file); - - if (pBytesWritten != NULL) { - *pBytesWritten = result; - } - - if (result != sizeInBytes) { - return ma_result_from_errno(ferror((FILE*)file)); - } - - return MA_SUCCESS; -} - -static ma_result ma_default_vfs_seek__stdio(ma_vfs* pVFS, ma_vfs_file file, ma_int64 offset, ma_seek_origin origin) -{ - int result; - int whence; - - MA_ASSERT(file != NULL); - - (void)pVFS; - - if (origin == ma_seek_origin_start) { - whence = SEEK_SET; - } else if (origin == ma_seek_origin_end) { - whence = SEEK_END; - } else { - whence = SEEK_CUR; - } - -#if defined(_WIN32) - #if defined(_MSC_VER) && _MSC_VER > 1200 - result = _fseeki64((FILE*)file, offset, whence); - #else - /* No _fseeki64() so restrict to 31 bits. */ - if (offset > 0x7FFFFFFF) { - return MA_OUT_OF_RANGE; - } - - result = fseek((FILE*)file, (int)offset, whence); - #endif -#else - result = fseek((FILE*)file, (long int)offset, whence); -#endif - if (result != 0) { - return MA_ERROR; - } - - return MA_SUCCESS; -} - -static ma_result ma_default_vfs_tell__stdio(ma_vfs* pVFS, ma_vfs_file file, ma_int64* pCursor) -{ - ma_int64 result; - - MA_ASSERT(file != NULL); - MA_ASSERT(pCursor != NULL); - - (void)pVFS; - -#if defined(_WIN32) - #if defined(_MSC_VER) && _MSC_VER > 1200 - result = _ftelli64((FILE*)file); - #else - result = ftell((FILE*)file); - #endif -#else - result = ftell((FILE*)file); -#endif - - *pCursor = result; - - return MA_SUCCESS; -} - -#if !defined(_MSC_VER) && !((defined(_POSIX_C_SOURCE) && _POSIX_C_SOURCE >= 1) || defined(_XOPEN_SOURCE) || defined(_POSIX_SOURCE)) && !defined(MA_BSD) -int fileno(FILE *stream); -#endif - -static ma_result ma_default_vfs_info__stdio(ma_vfs* pVFS, ma_vfs_file file, ma_file_info* pInfo) -{ - int fd; - struct stat info; - - MA_ASSERT(file != NULL); - MA_ASSERT(pInfo != NULL); - - (void)pVFS; - -#if defined(_MSC_VER) - fd = _fileno((FILE*)file); -#else - fd = fileno((FILE*)file); -#endif - - if (fstat(fd, &info) != 0) { - return ma_result_from_errno(errno); - } - - pInfo->sizeInBytes = info.st_size; - - return MA_SUCCESS; -} -#endif - - -static ma_result ma_default_vfs_open(ma_vfs* pVFS, const char* pFilePath, ma_uint32 openMode, ma_vfs_file* pFile) -{ - if (pFile == NULL) { - return MA_INVALID_ARGS; - } - - *pFile = NULL; - - if (pFilePath == NULL || openMode == 0) { - return MA_INVALID_ARGS; - } - -#if defined(MA_USE_WIN32_FILEIO) - return ma_default_vfs_open__win32(pVFS, pFilePath, openMode, pFile); -#else - return ma_default_vfs_open__stdio(pVFS, pFilePath, openMode, pFile); -#endif -} - -static ma_result ma_default_vfs_open_w(ma_vfs* pVFS, const wchar_t* pFilePath, ma_uint32 openMode, ma_vfs_file* pFile) -{ - if (pFile == NULL) { - return MA_INVALID_ARGS; - } - - *pFile = NULL; - - if (pFilePath == NULL || openMode == 0) { - return MA_INVALID_ARGS; - } - -#if defined(MA_USE_WIN32_FILEIO) - return ma_default_vfs_open_w__win32(pVFS, pFilePath, openMode, pFile); -#else - return ma_default_vfs_open_w__stdio(pVFS, pFilePath, openMode, pFile); -#endif -} - -static ma_result ma_default_vfs_close(ma_vfs* pVFS, ma_vfs_file file) -{ - if (file == NULL) { - return MA_INVALID_ARGS; - } - -#if defined(MA_USE_WIN32_FILEIO) - return ma_default_vfs_close__win32(pVFS, file); -#else - return ma_default_vfs_close__stdio(pVFS, file); -#endif -} - -static ma_result ma_default_vfs_read(ma_vfs* pVFS, ma_vfs_file file, void* pDst, size_t sizeInBytes, size_t* pBytesRead) -{ - if (pBytesRead != NULL) { - *pBytesRead = 0; - } - - if (file == NULL || pDst == NULL) { - return MA_INVALID_ARGS; - } - -#if defined(MA_USE_WIN32_FILEIO) - return ma_default_vfs_read__win32(pVFS, file, pDst, sizeInBytes, pBytesRead); -#else - return ma_default_vfs_read__stdio(pVFS, file, pDst, sizeInBytes, pBytesRead); -#endif -} - -static ma_result ma_default_vfs_write(ma_vfs* pVFS, ma_vfs_file file, const void* pSrc, size_t sizeInBytes, size_t* pBytesWritten) -{ - if (pBytesWritten != NULL) { - *pBytesWritten = 0; - } - - if (file == NULL || pSrc == NULL) { - return MA_INVALID_ARGS; - } - -#if defined(MA_USE_WIN32_FILEIO) - return ma_default_vfs_write__win32(pVFS, file, pSrc, sizeInBytes, pBytesWritten); -#else - return ma_default_vfs_write__stdio(pVFS, file, pSrc, sizeInBytes, pBytesWritten); -#endif -} - -static ma_result ma_default_vfs_seek(ma_vfs* pVFS, ma_vfs_file file, ma_int64 offset, ma_seek_origin origin) -{ - if (file == NULL) { - return MA_INVALID_ARGS; - } - -#if defined(MA_USE_WIN32_FILEIO) - return ma_default_vfs_seek__win32(pVFS, file, offset, origin); -#else - return ma_default_vfs_seek__stdio(pVFS, file, offset, origin); -#endif -} - -static ma_result ma_default_vfs_tell(ma_vfs* pVFS, ma_vfs_file file, ma_int64* pCursor) -{ - if (pCursor == NULL) { - return MA_INVALID_ARGS; - } - - *pCursor = 0; - - if (file == NULL) { - return MA_INVALID_ARGS; - } - -#if defined(MA_USE_WIN32_FILEIO) - return ma_default_vfs_tell__win32(pVFS, file, pCursor); -#else - return ma_default_vfs_tell__stdio(pVFS, file, pCursor); -#endif -} - -static ma_result ma_default_vfs_info(ma_vfs* pVFS, ma_vfs_file file, ma_file_info* pInfo) -{ - if (pInfo == NULL) { - return MA_INVALID_ARGS; - } - - MA_ZERO_OBJECT(pInfo); - - if (file == NULL) { - return MA_INVALID_ARGS; - } - -#if defined(MA_USE_WIN32_FILEIO) - return ma_default_vfs_info__win32(pVFS, file, pInfo); -#else - return ma_default_vfs_info__stdio(pVFS, file, pInfo); -#endif -} - - -MA_API ma_result ma_default_vfs_init(ma_default_vfs* pVFS, const ma_allocation_callbacks* pAllocationCallbacks) -{ - if (pVFS == NULL) { - return MA_INVALID_ARGS; - } - - pVFS->cb.onOpen = ma_default_vfs_open; - pVFS->cb.onOpenW = ma_default_vfs_open_w; - pVFS->cb.onClose = ma_default_vfs_close; - pVFS->cb.onRead = ma_default_vfs_read; - pVFS->cb.onWrite = ma_default_vfs_write; - pVFS->cb.onSeek = ma_default_vfs_seek; - pVFS->cb.onTell = ma_default_vfs_tell; - pVFS->cb.onInfo = ma_default_vfs_info; - ma_allocation_callbacks_init_copy(&pVFS->allocationCallbacks, pAllocationCallbacks); - - return MA_SUCCESS; -} - - -MA_API ma_result ma_vfs_or_default_open(ma_vfs* pVFS, const char* pFilePath, ma_uint32 openMode, ma_vfs_file* pFile) -{ - if (pVFS != NULL) { - return ma_vfs_open(pVFS, pFilePath, openMode, pFile); - } else { - return ma_default_vfs_open(pVFS, pFilePath, openMode, pFile); - } -} - -MA_API ma_result ma_vfs_or_default_open_w(ma_vfs* pVFS, const wchar_t* pFilePath, ma_uint32 openMode, ma_vfs_file* pFile) -{ - if (pVFS != NULL) { - return ma_vfs_open_w(pVFS, pFilePath, openMode, pFile); - } else { - return ma_default_vfs_open_w(pVFS, pFilePath, openMode, pFile); - } -} - -MA_API ma_result ma_vfs_or_default_close(ma_vfs* pVFS, ma_vfs_file file) -{ - if (pVFS != NULL) { - return ma_vfs_close(pVFS, file); - } else { - return ma_default_vfs_close(pVFS, file); - } -} - -MA_API ma_result ma_vfs_or_default_read(ma_vfs* pVFS, ma_vfs_file file, void* pDst, size_t sizeInBytes, size_t* pBytesRead) -{ - if (pVFS != NULL) { - return ma_vfs_read(pVFS, file, pDst, sizeInBytes, pBytesRead); - } else { - return ma_default_vfs_read(pVFS, file, pDst, sizeInBytes, pBytesRead); - } -} - -MA_API ma_result ma_vfs_or_default_write(ma_vfs* pVFS, ma_vfs_file file, const void* pSrc, size_t sizeInBytes, size_t* pBytesWritten) -{ - if (pVFS != NULL) { - return ma_vfs_write(pVFS, file, pSrc, sizeInBytes, pBytesWritten); - } else { - return ma_default_vfs_write(pVFS, file, pSrc, sizeInBytes, pBytesWritten); - } -} - -MA_API ma_result ma_vfs_or_default_seek(ma_vfs* pVFS, ma_vfs_file file, ma_int64 offset, ma_seek_origin origin) -{ - if (pVFS != NULL) { - return ma_vfs_seek(pVFS, file, offset, origin); - } else { - return ma_default_vfs_seek(pVFS, file, offset, origin); - } -} - -MA_API ma_result ma_vfs_or_default_tell(ma_vfs* pVFS, ma_vfs_file file, ma_int64* pCursor) -{ - if (pVFS != NULL) { - return ma_vfs_tell(pVFS, file, pCursor); - } else { - return ma_default_vfs_tell(pVFS, file, pCursor); - } -} - -MA_API ma_result ma_vfs_or_default_info(ma_vfs* pVFS, ma_vfs_file file, ma_file_info* pInfo) -{ - if (pVFS != NULL) { - return ma_vfs_info(pVFS, file, pInfo); - } else { - return ma_default_vfs_info(pVFS, file, pInfo); - } -} - - - -static ma_result ma_vfs_open_and_read_file_ex(ma_vfs* pVFS, const char* pFilePath, const wchar_t* pFilePathW, void** ppData, size_t* pSize, const ma_allocation_callbacks* pAllocationCallbacks) -{ - ma_result result; - ma_vfs_file file; - ma_file_info info; - void* pData; - size_t bytesRead; - - if (ppData != NULL) { - *ppData = NULL; - } - if (pSize != NULL) { - *pSize = 0; - } - - if (ppData == NULL) { - return MA_INVALID_ARGS; - } - - if (pFilePath != NULL) { - result = ma_vfs_or_default_open(pVFS, pFilePath, MA_OPEN_MODE_READ, &file); - } else { - result = ma_vfs_or_default_open_w(pVFS, pFilePathW, MA_OPEN_MODE_READ, &file); - } - if (result != MA_SUCCESS) { - return result; - } - - result = ma_vfs_or_default_info(pVFS, file, &info); - if (result != MA_SUCCESS) { - ma_vfs_or_default_close(pVFS, file); - return result; - } - - if (info.sizeInBytes > MA_SIZE_MAX) { - ma_vfs_or_default_close(pVFS, file); - return MA_TOO_BIG; - } - - pData = ma_malloc((size_t)info.sizeInBytes, pAllocationCallbacks); /* Safe cast. */ - if (pData == NULL) { - ma_vfs_or_default_close(pVFS, file); - return result; - } - - result = ma_vfs_or_default_read(pVFS, file, pData, (size_t)info.sizeInBytes, &bytesRead); /* Safe cast. */ - ma_vfs_or_default_close(pVFS, file); - - if (result != MA_SUCCESS) { - ma_free(pData, pAllocationCallbacks); - return result; - } - - if (pSize != NULL) { - *pSize = bytesRead; - } - - MA_ASSERT(ppData != NULL); - *ppData = pData; - - return MA_SUCCESS; -} - -MA_API ma_result ma_vfs_open_and_read_file(ma_vfs* pVFS, const char* pFilePath, void** ppData, size_t* pSize, const ma_allocation_callbacks* pAllocationCallbacks) -{ - return ma_vfs_open_and_read_file_ex(pVFS, pFilePath, NULL, ppData, pSize, pAllocationCallbacks); -} - -MA_API ma_result ma_vfs_open_and_read_file_w(ma_vfs* pVFS, const wchar_t* pFilePath, void** ppData, size_t* pSize, const ma_allocation_callbacks* pAllocationCallbacks) -{ - return ma_vfs_open_and_read_file_ex(pVFS, NULL, pFilePath, ppData, pSize, pAllocationCallbacks); -} - - - -/************************************************************************************************************************************************************** - -Decoding and Encoding Headers. These are auto-generated from a tool. - -**************************************************************************************************************************************************************/ -#if !defined(MA_NO_WAV) && (!defined(MA_NO_DECODING) || !defined(MA_NO_ENCODING)) -/* dr_wav_h begin */ -#ifndef ma_dr_wav_h -#define ma_dr_wav_h -#ifdef __cplusplus -extern "C" { -#endif -#define MA_DR_WAV_STRINGIFY(x) #x -#define MA_DR_WAV_XSTRINGIFY(x) MA_DR_WAV_STRINGIFY(x) -#define MA_DR_WAV_VERSION_MAJOR 0 -#define MA_DR_WAV_VERSION_MINOR 13 -#define MA_DR_WAV_VERSION_REVISION 18 -#define MA_DR_WAV_VERSION_STRING MA_DR_WAV_XSTRINGIFY(MA_DR_WAV_VERSION_MAJOR) "." MA_DR_WAV_XSTRINGIFY(MA_DR_WAV_VERSION_MINOR) "." MA_DR_WAV_XSTRINGIFY(MA_DR_WAV_VERSION_REVISION) -#include -#define MA_DR_WAVE_FORMAT_PCM 0x1 -#define MA_DR_WAVE_FORMAT_ADPCM 0x2 -#define MA_DR_WAVE_FORMAT_IEEE_FLOAT 0x3 -#define MA_DR_WAVE_FORMAT_ALAW 0x6 -#define MA_DR_WAVE_FORMAT_MULAW 0x7 -#define MA_DR_WAVE_FORMAT_DVI_ADPCM 0x11 -#define MA_DR_WAVE_FORMAT_EXTENSIBLE 0xFFFE -#define MA_DR_WAV_SEQUENTIAL 0x00000001 -#define MA_DR_WAV_WITH_METADATA 0x00000002 -MA_API void ma_dr_wav_version(ma_uint32* pMajor, ma_uint32* pMinor, ma_uint32* pRevision); -MA_API const char* ma_dr_wav_version_string(void); -typedef enum -{ - ma_dr_wav_seek_origin_start, - ma_dr_wav_seek_origin_current -} ma_dr_wav_seek_origin; -typedef enum -{ - ma_dr_wav_container_riff, - ma_dr_wav_container_rifx, - ma_dr_wav_container_w64, - ma_dr_wav_container_rf64, - ma_dr_wav_container_aiff -} ma_dr_wav_container; -typedef struct -{ - union - { - ma_uint8 fourcc[4]; - ma_uint8 guid[16]; - } id; - ma_uint64 sizeInBytes; - unsigned int paddingSize; -} ma_dr_wav_chunk_header; -typedef struct -{ - ma_uint16 formatTag; - ma_uint16 channels; - ma_uint32 sampleRate; - ma_uint32 avgBytesPerSec; - ma_uint16 blockAlign; - ma_uint16 bitsPerSample; - ma_uint16 extendedSize; - ma_uint16 validBitsPerSample; - ma_uint32 channelMask; - ma_uint8 subFormat[16]; -} ma_dr_wav_fmt; -MA_API ma_uint16 ma_dr_wav_fmt_get_format(const ma_dr_wav_fmt* pFMT); -typedef size_t (* ma_dr_wav_read_proc)(void* pUserData, void* pBufferOut, size_t bytesToRead); -typedef size_t (* ma_dr_wav_write_proc)(void* pUserData, const void* pData, size_t bytesToWrite); -typedef ma_bool32 (* ma_dr_wav_seek_proc)(void* pUserData, int offset, ma_dr_wav_seek_origin origin); -typedef ma_uint64 (* ma_dr_wav_chunk_proc)(void* pChunkUserData, ma_dr_wav_read_proc onRead, ma_dr_wav_seek_proc onSeek, void* pReadSeekUserData, const ma_dr_wav_chunk_header* pChunkHeader, ma_dr_wav_container container, const ma_dr_wav_fmt* pFMT); -typedef struct -{ - const ma_uint8* data; - size_t dataSize; - size_t currentReadPos; -} ma_dr_wav__memory_stream; -typedef struct -{ - void** ppData; - size_t* pDataSize; - size_t dataSize; - size_t dataCapacity; - size_t currentWritePos; -} ma_dr_wav__memory_stream_write; -typedef struct -{ - ma_dr_wav_container container; - ma_uint32 format; - ma_uint32 channels; - ma_uint32 sampleRate; - ma_uint32 bitsPerSample; -} ma_dr_wav_data_format; -typedef enum -{ - ma_dr_wav_metadata_type_none = 0, - ma_dr_wav_metadata_type_unknown = 1 << 0, - ma_dr_wav_metadata_type_smpl = 1 << 1, - ma_dr_wav_metadata_type_inst = 1 << 2, - ma_dr_wav_metadata_type_cue = 1 << 3, - ma_dr_wav_metadata_type_acid = 1 << 4, - ma_dr_wav_metadata_type_bext = 1 << 5, - ma_dr_wav_metadata_type_list_label = 1 << 6, - ma_dr_wav_metadata_type_list_note = 1 << 7, - ma_dr_wav_metadata_type_list_labelled_cue_region = 1 << 8, - ma_dr_wav_metadata_type_list_info_software = 1 << 9, - ma_dr_wav_metadata_type_list_info_copyright = 1 << 10, - ma_dr_wav_metadata_type_list_info_title = 1 << 11, - ma_dr_wav_metadata_type_list_info_artist = 1 << 12, - ma_dr_wav_metadata_type_list_info_comment = 1 << 13, - ma_dr_wav_metadata_type_list_info_date = 1 << 14, - ma_dr_wav_metadata_type_list_info_genre = 1 << 15, - ma_dr_wav_metadata_type_list_info_album = 1 << 16, - ma_dr_wav_metadata_type_list_info_tracknumber = 1 << 17, - ma_dr_wav_metadata_type_list_all_info_strings = ma_dr_wav_metadata_type_list_info_software - | ma_dr_wav_metadata_type_list_info_copyright - | ma_dr_wav_metadata_type_list_info_title - | ma_dr_wav_metadata_type_list_info_artist - | ma_dr_wav_metadata_type_list_info_comment - | ma_dr_wav_metadata_type_list_info_date - | ma_dr_wav_metadata_type_list_info_genre - | ma_dr_wav_metadata_type_list_info_album - | ma_dr_wav_metadata_type_list_info_tracknumber, - ma_dr_wav_metadata_type_list_all_adtl = ma_dr_wav_metadata_type_list_label - | ma_dr_wav_metadata_type_list_note - | ma_dr_wav_metadata_type_list_labelled_cue_region, - ma_dr_wav_metadata_type_all = -2, - ma_dr_wav_metadata_type_all_including_unknown = -1 -} ma_dr_wav_metadata_type; -typedef enum -{ - ma_dr_wav_smpl_loop_type_forward = 0, - ma_dr_wav_smpl_loop_type_pingpong = 1, - ma_dr_wav_smpl_loop_type_backward = 2 -} ma_dr_wav_smpl_loop_type; -typedef struct -{ - ma_uint32 cuePointId; - ma_uint32 type; - ma_uint32 firstSampleByteOffset; - ma_uint32 lastSampleByteOffset; - ma_uint32 sampleFraction; - ma_uint32 playCount; -} ma_dr_wav_smpl_loop; -typedef struct -{ - ma_uint32 manufacturerId; - ma_uint32 productId; - ma_uint32 samplePeriodNanoseconds; - ma_uint32 midiUnityNote; - ma_uint32 midiPitchFraction; - ma_uint32 smpteFormat; - ma_uint32 smpteOffset; - ma_uint32 sampleLoopCount; - ma_uint32 samplerSpecificDataSizeInBytes; - ma_dr_wav_smpl_loop* pLoops; - ma_uint8* pSamplerSpecificData; -} ma_dr_wav_smpl; -typedef struct -{ - ma_int8 midiUnityNote; - ma_int8 fineTuneCents; - ma_int8 gainDecibels; - ma_int8 lowNote; - ma_int8 highNote; - ma_int8 lowVelocity; - ma_int8 highVelocity; -} ma_dr_wav_inst; -typedef struct -{ - ma_uint32 id; - ma_uint32 playOrderPosition; - ma_uint8 dataChunkId[4]; - ma_uint32 chunkStart; - ma_uint32 blockStart; - ma_uint32 sampleByteOffset; -} ma_dr_wav_cue_point; -typedef struct -{ - ma_uint32 cuePointCount; - ma_dr_wav_cue_point *pCuePoints; -} ma_dr_wav_cue; -typedef enum -{ - ma_dr_wav_acid_flag_one_shot = 1, - ma_dr_wav_acid_flag_root_note_set = 2, - ma_dr_wav_acid_flag_stretch = 4, - ma_dr_wav_acid_flag_disk_based = 8, - ma_dr_wav_acid_flag_acidizer = 16 -} ma_dr_wav_acid_flag; -typedef struct -{ - ma_uint32 flags; - ma_uint16 midiUnityNote; - ma_uint16 reserved1; - float reserved2; - ma_uint32 numBeats; - ma_uint16 meterDenominator; - ma_uint16 meterNumerator; - float tempo; -} ma_dr_wav_acid; -typedef struct -{ - ma_uint32 cuePointId; - ma_uint32 stringLength; - char* pString; -} ma_dr_wav_list_label_or_note; -typedef struct -{ - char* pDescription; - char* pOriginatorName; - char* pOriginatorReference; - char pOriginationDate[10]; - char pOriginationTime[8]; - ma_uint64 timeReference; - ma_uint16 version; - char* pCodingHistory; - ma_uint32 codingHistorySize; - ma_uint8* pUMID; - ma_uint16 loudnessValue; - ma_uint16 loudnessRange; - ma_uint16 maxTruePeakLevel; - ma_uint16 maxMomentaryLoudness; - ma_uint16 maxShortTermLoudness; -} ma_dr_wav_bext; -typedef struct -{ - ma_uint32 stringLength; - char* pString; -} ma_dr_wav_list_info_text; -typedef struct -{ - ma_uint32 cuePointId; - ma_uint32 sampleLength; - ma_uint8 purposeId[4]; - ma_uint16 country; - ma_uint16 language; - ma_uint16 dialect; - ma_uint16 codePage; - ma_uint32 stringLength; - char* pString; -} ma_dr_wav_list_labelled_cue_region; -typedef enum -{ - ma_dr_wav_metadata_location_invalid, - ma_dr_wav_metadata_location_top_level, - ma_dr_wav_metadata_location_inside_info_list, - ma_dr_wav_metadata_location_inside_adtl_list -} ma_dr_wav_metadata_location; -typedef struct -{ - ma_uint8 id[4]; - ma_dr_wav_metadata_location chunkLocation; - ma_uint32 dataSizeInBytes; - ma_uint8* pData; -} ma_dr_wav_unknown_metadata; -typedef struct -{ - ma_dr_wav_metadata_type type; - union - { - ma_dr_wav_cue cue; - ma_dr_wav_smpl smpl; - ma_dr_wav_acid acid; - ma_dr_wav_inst inst; - ma_dr_wav_bext bext; - ma_dr_wav_list_label_or_note labelOrNote; - ma_dr_wav_list_labelled_cue_region labelledCueRegion; - ma_dr_wav_list_info_text infoText; - ma_dr_wav_unknown_metadata unknown; - } data; -} ma_dr_wav_metadata; -typedef struct -{ - ma_dr_wav_read_proc onRead; - ma_dr_wav_write_proc onWrite; - ma_dr_wav_seek_proc onSeek; - void* pUserData; - ma_allocation_callbacks allocationCallbacks; - ma_dr_wav_container container; - ma_dr_wav_fmt fmt; - ma_uint32 sampleRate; - ma_uint16 channels; - ma_uint16 bitsPerSample; - ma_uint16 translatedFormatTag; - ma_uint64 totalPCMFrameCount; - ma_uint64 dataChunkDataSize; - ma_uint64 dataChunkDataPos; - ma_uint64 bytesRemaining; - ma_uint64 readCursorInPCMFrames; - ma_uint64 dataChunkDataSizeTargetWrite; - ma_bool32 isSequentialWrite; - ma_dr_wav_metadata* pMetadata; - ma_uint32 metadataCount; - ma_dr_wav__memory_stream memoryStream; - ma_dr_wav__memory_stream_write memoryStreamWrite; - struct - { - ma_uint32 bytesRemainingInBlock; - ma_uint16 predictor[2]; - ma_int32 delta[2]; - ma_int32 cachedFrames[4]; - ma_uint32 cachedFrameCount; - ma_int32 prevFrames[2][2]; - } msadpcm; - struct - { - ma_uint32 bytesRemainingInBlock; - ma_int32 predictor[2]; - ma_int32 stepIndex[2]; - ma_int32 cachedFrames[16]; - ma_uint32 cachedFrameCount; - } ima; - struct - { - ma_bool8 isLE; - ma_bool8 isUnsigned; - } aiff; -} ma_dr_wav; -MA_API ma_bool32 ma_dr_wav_init(ma_dr_wav* pWav, ma_dr_wav_read_proc onRead, ma_dr_wav_seek_proc onSeek, void* pUserData, const ma_allocation_callbacks* pAllocationCallbacks); -MA_API ma_bool32 ma_dr_wav_init_ex(ma_dr_wav* pWav, ma_dr_wav_read_proc onRead, ma_dr_wav_seek_proc onSeek, ma_dr_wav_chunk_proc onChunk, void* pReadSeekUserData, void* pChunkUserData, ma_uint32 flags, const ma_allocation_callbacks* pAllocationCallbacks); -MA_API ma_bool32 ma_dr_wav_init_with_metadata(ma_dr_wav* pWav, ma_dr_wav_read_proc onRead, ma_dr_wav_seek_proc onSeek, void* pUserData, ma_uint32 flags, const ma_allocation_callbacks* pAllocationCallbacks); -MA_API ma_bool32 ma_dr_wav_init_write(ma_dr_wav* pWav, const ma_dr_wav_data_format* pFormat, ma_dr_wav_write_proc onWrite, ma_dr_wav_seek_proc onSeek, void* pUserData, const ma_allocation_callbacks* pAllocationCallbacks); -MA_API ma_bool32 ma_dr_wav_init_write_sequential(ma_dr_wav* pWav, const ma_dr_wav_data_format* pFormat, ma_uint64 totalSampleCount, ma_dr_wav_write_proc onWrite, void* pUserData, const ma_allocation_callbacks* pAllocationCallbacks); -MA_API ma_bool32 ma_dr_wav_init_write_sequential_pcm_frames(ma_dr_wav* pWav, const ma_dr_wav_data_format* pFormat, ma_uint64 totalPCMFrameCount, ma_dr_wav_write_proc onWrite, void* pUserData, const ma_allocation_callbacks* pAllocationCallbacks); -MA_API ma_bool32 ma_dr_wav_init_write_with_metadata(ma_dr_wav* pWav, const ma_dr_wav_data_format* pFormat, ma_dr_wav_write_proc onWrite, ma_dr_wav_seek_proc onSeek, void* pUserData, const ma_allocation_callbacks* pAllocationCallbacks, ma_dr_wav_metadata* pMetadata, ma_uint32 metadataCount); -MA_API ma_uint64 ma_dr_wav_target_write_size_bytes(const ma_dr_wav_data_format* pFormat, ma_uint64 totalFrameCount, ma_dr_wav_metadata* pMetadata, ma_uint32 metadataCount); -MA_API ma_dr_wav_metadata* ma_dr_wav_take_ownership_of_metadata(ma_dr_wav* pWav); -MA_API ma_result ma_dr_wav_uninit(ma_dr_wav* pWav); -MA_API size_t ma_dr_wav_read_raw(ma_dr_wav* pWav, size_t bytesToRead, void* pBufferOut); -MA_API ma_uint64 ma_dr_wav_read_pcm_frames(ma_dr_wav* pWav, ma_uint64 framesToRead, void* pBufferOut); -MA_API ma_uint64 ma_dr_wav_read_pcm_frames_le(ma_dr_wav* pWav, ma_uint64 framesToRead, void* pBufferOut); -MA_API ma_uint64 ma_dr_wav_read_pcm_frames_be(ma_dr_wav* pWav, ma_uint64 framesToRead, void* pBufferOut); -MA_API ma_bool32 ma_dr_wav_seek_to_pcm_frame(ma_dr_wav* pWav, ma_uint64 targetFrameIndex); -MA_API ma_result ma_dr_wav_get_cursor_in_pcm_frames(ma_dr_wav* pWav, ma_uint64* pCursor); -MA_API ma_result ma_dr_wav_get_length_in_pcm_frames(ma_dr_wav* pWav, ma_uint64* pLength); -MA_API size_t ma_dr_wav_write_raw(ma_dr_wav* pWav, size_t bytesToWrite, const void* pData); -MA_API ma_uint64 ma_dr_wav_write_pcm_frames(ma_dr_wav* pWav, ma_uint64 framesToWrite, const void* pData); -MA_API ma_uint64 ma_dr_wav_write_pcm_frames_le(ma_dr_wav* pWav, ma_uint64 framesToWrite, const void* pData); -MA_API ma_uint64 ma_dr_wav_write_pcm_frames_be(ma_dr_wav* pWav, ma_uint64 framesToWrite, const void* pData); -#ifndef MA_DR_WAV_NO_CONVERSION_API -MA_API ma_uint64 ma_dr_wav_read_pcm_frames_s16(ma_dr_wav* pWav, ma_uint64 framesToRead, ma_int16* pBufferOut); -MA_API ma_uint64 ma_dr_wav_read_pcm_frames_s16le(ma_dr_wav* pWav, ma_uint64 framesToRead, ma_int16* pBufferOut); -MA_API ma_uint64 ma_dr_wav_read_pcm_frames_s16be(ma_dr_wav* pWav, ma_uint64 framesToRead, ma_int16* pBufferOut); -MA_API void ma_dr_wav_u8_to_s16(ma_int16* pOut, const ma_uint8* pIn, size_t sampleCount); -MA_API void ma_dr_wav_s24_to_s16(ma_int16* pOut, const ma_uint8* pIn, size_t sampleCount); -MA_API void ma_dr_wav_s32_to_s16(ma_int16* pOut, const ma_int32* pIn, size_t sampleCount); -MA_API void ma_dr_wav_f32_to_s16(ma_int16* pOut, const float* pIn, size_t sampleCount); -MA_API void ma_dr_wav_f64_to_s16(ma_int16* pOut, const double* pIn, size_t sampleCount); -MA_API void ma_dr_wav_alaw_to_s16(ma_int16* pOut, const ma_uint8* pIn, size_t sampleCount); -MA_API void ma_dr_wav_mulaw_to_s16(ma_int16* pOut, const ma_uint8* pIn, size_t sampleCount); -MA_API ma_uint64 ma_dr_wav_read_pcm_frames_f32(ma_dr_wav* pWav, ma_uint64 framesToRead, float* pBufferOut); -MA_API ma_uint64 ma_dr_wav_read_pcm_frames_f32le(ma_dr_wav* pWav, ma_uint64 framesToRead, float* pBufferOut); -MA_API ma_uint64 ma_dr_wav_read_pcm_frames_f32be(ma_dr_wav* pWav, ma_uint64 framesToRead, float* pBufferOut); -MA_API void ma_dr_wav_u8_to_f32(float* pOut, const ma_uint8* pIn, size_t sampleCount); -MA_API void ma_dr_wav_s16_to_f32(float* pOut, const ma_int16* pIn, size_t sampleCount); -MA_API void ma_dr_wav_s24_to_f32(float* pOut, const ma_uint8* pIn, size_t sampleCount); -MA_API void ma_dr_wav_s32_to_f32(float* pOut, const ma_int32* pIn, size_t sampleCount); -MA_API void ma_dr_wav_f64_to_f32(float* pOut, const double* pIn, size_t sampleCount); -MA_API void ma_dr_wav_alaw_to_f32(float* pOut, const ma_uint8* pIn, size_t sampleCount); -MA_API void ma_dr_wav_mulaw_to_f32(float* pOut, const ma_uint8* pIn, size_t sampleCount); -MA_API ma_uint64 ma_dr_wav_read_pcm_frames_s32(ma_dr_wav* pWav, ma_uint64 framesToRead, ma_int32* pBufferOut); -MA_API ma_uint64 ma_dr_wav_read_pcm_frames_s32le(ma_dr_wav* pWav, ma_uint64 framesToRead, ma_int32* pBufferOut); -MA_API ma_uint64 ma_dr_wav_read_pcm_frames_s32be(ma_dr_wav* pWav, ma_uint64 framesToRead, ma_int32* pBufferOut); -MA_API void ma_dr_wav_u8_to_s32(ma_int32* pOut, const ma_uint8* pIn, size_t sampleCount); -MA_API void ma_dr_wav_s16_to_s32(ma_int32* pOut, const ma_int16* pIn, size_t sampleCount); -MA_API void ma_dr_wav_s24_to_s32(ma_int32* pOut, const ma_uint8* pIn, size_t sampleCount); -MA_API void ma_dr_wav_f32_to_s32(ma_int32* pOut, const float* pIn, size_t sampleCount); -MA_API void ma_dr_wav_f64_to_s32(ma_int32* pOut, const double* pIn, size_t sampleCount); -MA_API void ma_dr_wav_alaw_to_s32(ma_int32* pOut, const ma_uint8* pIn, size_t sampleCount); -MA_API void ma_dr_wav_mulaw_to_s32(ma_int32* pOut, const ma_uint8* pIn, size_t sampleCount); -#endif -#ifndef MA_DR_WAV_NO_STDIO -MA_API ma_bool32 ma_dr_wav_init_file(ma_dr_wav* pWav, const char* filename, const ma_allocation_callbacks* pAllocationCallbacks); -MA_API ma_bool32 ma_dr_wav_init_file_ex(ma_dr_wav* pWav, const char* filename, ma_dr_wav_chunk_proc onChunk, void* pChunkUserData, ma_uint32 flags, const ma_allocation_callbacks* pAllocationCallbacks); -MA_API ma_bool32 ma_dr_wav_init_file_w(ma_dr_wav* pWav, const wchar_t* filename, const ma_allocation_callbacks* pAllocationCallbacks); -MA_API ma_bool32 ma_dr_wav_init_file_ex_w(ma_dr_wav* pWav, const wchar_t* filename, ma_dr_wav_chunk_proc onChunk, void* pChunkUserData, ma_uint32 flags, const ma_allocation_callbacks* pAllocationCallbacks); -MA_API ma_bool32 ma_dr_wav_init_file_with_metadata(ma_dr_wav* pWav, const char* filename, ma_uint32 flags, const ma_allocation_callbacks* pAllocationCallbacks); -MA_API ma_bool32 ma_dr_wav_init_file_with_metadata_w(ma_dr_wav* pWav, const wchar_t* filename, ma_uint32 flags, const ma_allocation_callbacks* pAllocationCallbacks); -MA_API ma_bool32 ma_dr_wav_init_file_write(ma_dr_wav* pWav, const char* filename, const ma_dr_wav_data_format* pFormat, const ma_allocation_callbacks* pAllocationCallbacks); -MA_API ma_bool32 ma_dr_wav_init_file_write_sequential(ma_dr_wav* pWav, const char* filename, const ma_dr_wav_data_format* pFormat, ma_uint64 totalSampleCount, const ma_allocation_callbacks* pAllocationCallbacks); -MA_API ma_bool32 ma_dr_wav_init_file_write_sequential_pcm_frames(ma_dr_wav* pWav, const char* filename, const ma_dr_wav_data_format* pFormat, ma_uint64 totalPCMFrameCount, const ma_allocation_callbacks* pAllocationCallbacks); -MA_API ma_bool32 ma_dr_wav_init_file_write_w(ma_dr_wav* pWav, const wchar_t* filename, const ma_dr_wav_data_format* pFormat, const ma_allocation_callbacks* pAllocationCallbacks); -MA_API ma_bool32 ma_dr_wav_init_file_write_sequential_w(ma_dr_wav* pWav, const wchar_t* filename, const ma_dr_wav_data_format* pFormat, ma_uint64 totalSampleCount, const ma_allocation_callbacks* pAllocationCallbacks); -MA_API ma_bool32 ma_dr_wav_init_file_write_sequential_pcm_frames_w(ma_dr_wav* pWav, const wchar_t* filename, const ma_dr_wav_data_format* pFormat, ma_uint64 totalPCMFrameCount, const ma_allocation_callbacks* pAllocationCallbacks); -#endif -MA_API ma_bool32 ma_dr_wav_init_memory(ma_dr_wav* pWav, const void* data, size_t dataSize, const ma_allocation_callbacks* pAllocationCallbacks); -MA_API ma_bool32 ma_dr_wav_init_memory_ex(ma_dr_wav* pWav, const void* data, size_t dataSize, ma_dr_wav_chunk_proc onChunk, void* pChunkUserData, ma_uint32 flags, const ma_allocation_callbacks* pAllocationCallbacks); -MA_API ma_bool32 ma_dr_wav_init_memory_with_metadata(ma_dr_wav* pWav, const void* data, size_t dataSize, ma_uint32 flags, const ma_allocation_callbacks* pAllocationCallbacks); -MA_API ma_bool32 ma_dr_wav_init_memory_write(ma_dr_wav* pWav, void** ppData, size_t* pDataSize, const ma_dr_wav_data_format* pFormat, const ma_allocation_callbacks* pAllocationCallbacks); -MA_API ma_bool32 ma_dr_wav_init_memory_write_sequential(ma_dr_wav* pWav, void** ppData, size_t* pDataSize, const ma_dr_wav_data_format* pFormat, ma_uint64 totalSampleCount, const ma_allocation_callbacks* pAllocationCallbacks); -MA_API ma_bool32 ma_dr_wav_init_memory_write_sequential_pcm_frames(ma_dr_wav* pWav, void** ppData, size_t* pDataSize, const ma_dr_wav_data_format* pFormat, ma_uint64 totalPCMFrameCount, const ma_allocation_callbacks* pAllocationCallbacks); -#ifndef MA_DR_WAV_NO_CONVERSION_API -MA_API ma_int16* ma_dr_wav_open_and_read_pcm_frames_s16(ma_dr_wav_read_proc onRead, ma_dr_wav_seek_proc onSeek, void* pUserData, unsigned int* channelsOut, unsigned int* sampleRateOut, ma_uint64* totalFrameCountOut, const ma_allocation_callbacks* pAllocationCallbacks); -MA_API float* ma_dr_wav_open_and_read_pcm_frames_f32(ma_dr_wav_read_proc onRead, ma_dr_wav_seek_proc onSeek, void* pUserData, unsigned int* channelsOut, unsigned int* sampleRateOut, ma_uint64* totalFrameCountOut, const ma_allocation_callbacks* pAllocationCallbacks); -MA_API ma_int32* ma_dr_wav_open_and_read_pcm_frames_s32(ma_dr_wav_read_proc onRead, ma_dr_wav_seek_proc onSeek, void* pUserData, unsigned int* channelsOut, unsigned int* sampleRateOut, ma_uint64* totalFrameCountOut, const ma_allocation_callbacks* pAllocationCallbacks); -#ifndef MA_DR_WAV_NO_STDIO -MA_API ma_int16* ma_dr_wav_open_file_and_read_pcm_frames_s16(const char* filename, unsigned int* channelsOut, unsigned int* sampleRateOut, ma_uint64* totalFrameCountOut, const ma_allocation_callbacks* pAllocationCallbacks); -MA_API float* ma_dr_wav_open_file_and_read_pcm_frames_f32(const char* filename, unsigned int* channelsOut, unsigned int* sampleRateOut, ma_uint64* totalFrameCountOut, const ma_allocation_callbacks* pAllocationCallbacks); -MA_API ma_int32* ma_dr_wav_open_file_and_read_pcm_frames_s32(const char* filename, unsigned int* channelsOut, unsigned int* sampleRateOut, ma_uint64* totalFrameCountOut, const ma_allocation_callbacks* pAllocationCallbacks); -MA_API ma_int16* ma_dr_wav_open_file_and_read_pcm_frames_s16_w(const wchar_t* filename, unsigned int* channelsOut, unsigned int* sampleRateOut, ma_uint64* totalFrameCountOut, const ma_allocation_callbacks* pAllocationCallbacks); -MA_API float* ma_dr_wav_open_file_and_read_pcm_frames_f32_w(const wchar_t* filename, unsigned int* channelsOut, unsigned int* sampleRateOut, ma_uint64* totalFrameCountOut, const ma_allocation_callbacks* pAllocationCallbacks); -MA_API ma_int32* ma_dr_wav_open_file_and_read_pcm_frames_s32_w(const wchar_t* filename, unsigned int* channelsOut, unsigned int* sampleRateOut, ma_uint64* totalFrameCountOut, const ma_allocation_callbacks* pAllocationCallbacks); -#endif -MA_API ma_int16* ma_dr_wav_open_memory_and_read_pcm_frames_s16(const void* data, size_t dataSize, unsigned int* channelsOut, unsigned int* sampleRateOut, ma_uint64* totalFrameCountOut, const ma_allocation_callbacks* pAllocationCallbacks); -MA_API float* ma_dr_wav_open_memory_and_read_pcm_frames_f32(const void* data, size_t dataSize, unsigned int* channelsOut, unsigned int* sampleRateOut, ma_uint64* totalFrameCountOut, const ma_allocation_callbacks* pAllocationCallbacks); -MA_API ma_int32* ma_dr_wav_open_memory_and_read_pcm_frames_s32(const void* data, size_t dataSize, unsigned int* channelsOut, unsigned int* sampleRateOut, ma_uint64* totalFrameCountOut, const ma_allocation_callbacks* pAllocationCallbacks); -#endif -MA_API void ma_dr_wav_free(void* p, const ma_allocation_callbacks* pAllocationCallbacks); -MA_API ma_uint16 ma_dr_wav_bytes_to_u16(const ma_uint8* data); -MA_API ma_int16 ma_dr_wav_bytes_to_s16(const ma_uint8* data); -MA_API ma_uint32 ma_dr_wav_bytes_to_u32(const ma_uint8* data); -MA_API ma_int32 ma_dr_wav_bytes_to_s32(const ma_uint8* data); -MA_API ma_uint64 ma_dr_wav_bytes_to_u64(const ma_uint8* data); -MA_API ma_int64 ma_dr_wav_bytes_to_s64(const ma_uint8* data); -MA_API float ma_dr_wav_bytes_to_f32(const ma_uint8* data); -MA_API ma_bool32 ma_dr_wav_guid_equal(const ma_uint8 a[16], const ma_uint8 b[16]); -MA_API ma_bool32 ma_dr_wav_fourcc_equal(const ma_uint8* a, const char* b); -#ifdef __cplusplus -} -#endif -#endif -/* dr_wav_h end */ -#endif /* MA_NO_WAV */ - -#if !defined(MA_NO_FLAC) && !defined(MA_NO_DECODING) -/* dr_flac_h begin */ -#ifndef ma_dr_flac_h -#define ma_dr_flac_h -#ifdef __cplusplus -extern "C" { -#endif -#define MA_DR_FLAC_STRINGIFY(x) #x -#define MA_DR_FLAC_XSTRINGIFY(x) MA_DR_FLAC_STRINGIFY(x) -#define MA_DR_FLAC_VERSION_MAJOR 0 -#define MA_DR_FLAC_VERSION_MINOR 12 -#define MA_DR_FLAC_VERSION_REVISION 43 -#define MA_DR_FLAC_VERSION_STRING MA_DR_FLAC_XSTRINGIFY(MA_DR_FLAC_VERSION_MAJOR) "." MA_DR_FLAC_XSTRINGIFY(MA_DR_FLAC_VERSION_MINOR) "." MA_DR_FLAC_XSTRINGIFY(MA_DR_FLAC_VERSION_REVISION) -#include -#if defined(_MSC_VER) && _MSC_VER >= 1700 - #define MA_DR_FLAC_DEPRECATED __declspec(deprecated) -#elif (defined(__GNUC__) && __GNUC__ >= 4) - #define MA_DR_FLAC_DEPRECATED __attribute__((deprecated)) -#elif defined(__has_feature) - #if __has_feature(attribute_deprecated) - #define MA_DR_FLAC_DEPRECATED __attribute__((deprecated)) - #else - #define MA_DR_FLAC_DEPRECATED - #endif -#else - #define MA_DR_FLAC_DEPRECATED -#endif -MA_API void ma_dr_flac_version(ma_uint32* pMajor, ma_uint32* pMinor, ma_uint32* pRevision); -MA_API const char* ma_dr_flac_version_string(void); -#ifndef MA_DR_FLAC_BUFFER_SIZE -#define MA_DR_FLAC_BUFFER_SIZE 4096 -#endif -#ifdef MA_64BIT -typedef ma_uint64 ma_dr_flac_cache_t; -#else -typedef ma_uint32 ma_dr_flac_cache_t; -#endif -#define MA_DR_FLAC_METADATA_BLOCK_TYPE_STREAMINFO 0 -#define MA_DR_FLAC_METADATA_BLOCK_TYPE_PADDING 1 -#define MA_DR_FLAC_METADATA_BLOCK_TYPE_APPLICATION 2 -#define MA_DR_FLAC_METADATA_BLOCK_TYPE_SEEKTABLE 3 -#define MA_DR_FLAC_METADATA_BLOCK_TYPE_VORBIS_COMMENT 4 -#define MA_DR_FLAC_METADATA_BLOCK_TYPE_CUESHEET 5 -#define MA_DR_FLAC_METADATA_BLOCK_TYPE_PICTURE 6 -#define MA_DR_FLAC_METADATA_BLOCK_TYPE_INVALID 127 -#define MA_DR_FLAC_PICTURE_TYPE_OTHER 0 -#define MA_DR_FLAC_PICTURE_TYPE_FILE_ICON 1 -#define MA_DR_FLAC_PICTURE_TYPE_OTHER_FILE_ICON 2 -#define MA_DR_FLAC_PICTURE_TYPE_COVER_FRONT 3 -#define MA_DR_FLAC_PICTURE_TYPE_COVER_BACK 4 -#define MA_DR_FLAC_PICTURE_TYPE_LEAFLET_PAGE 5 -#define MA_DR_FLAC_PICTURE_TYPE_MEDIA 6 -#define MA_DR_FLAC_PICTURE_TYPE_LEAD_ARTIST 7 -#define MA_DR_FLAC_PICTURE_TYPE_ARTIST 8 -#define MA_DR_FLAC_PICTURE_TYPE_CONDUCTOR 9 -#define MA_DR_FLAC_PICTURE_TYPE_BAND 10 -#define MA_DR_FLAC_PICTURE_TYPE_COMPOSER 11 -#define MA_DR_FLAC_PICTURE_TYPE_LYRICIST 12 -#define MA_DR_FLAC_PICTURE_TYPE_RECORDING_LOCATION 13 -#define MA_DR_FLAC_PICTURE_TYPE_DURING_RECORDING 14 -#define MA_DR_FLAC_PICTURE_TYPE_DURING_PERFORMANCE 15 -#define MA_DR_FLAC_PICTURE_TYPE_SCREEN_CAPTURE 16 -#define MA_DR_FLAC_PICTURE_TYPE_BRIGHT_COLORED_FISH 17 -#define MA_DR_FLAC_PICTURE_TYPE_ILLUSTRATION 18 -#define MA_DR_FLAC_PICTURE_TYPE_BAND_LOGOTYPE 19 -#define MA_DR_FLAC_PICTURE_TYPE_PUBLISHER_LOGOTYPE 20 -typedef enum -{ - ma_dr_flac_container_native, - ma_dr_flac_container_ogg, - ma_dr_flac_container_unknown -} ma_dr_flac_container; -typedef enum -{ - ma_dr_flac_seek_origin_start, - ma_dr_flac_seek_origin_current -} ma_dr_flac_seek_origin; -typedef struct -{ - ma_uint64 firstPCMFrame; - ma_uint64 flacFrameOffset; - ma_uint16 pcmFrameCount; -} ma_dr_flac_seekpoint; -typedef struct -{ - ma_uint16 minBlockSizeInPCMFrames; - ma_uint16 maxBlockSizeInPCMFrames; - ma_uint32 minFrameSizeInPCMFrames; - ma_uint32 maxFrameSizeInPCMFrames; - ma_uint32 sampleRate; - ma_uint8 channels; - ma_uint8 bitsPerSample; - ma_uint64 totalPCMFrameCount; - ma_uint8 md5[16]; -} ma_dr_flac_streaminfo; -typedef struct -{ - ma_uint32 type; - const void* pRawData; - ma_uint32 rawDataSize; - union - { - ma_dr_flac_streaminfo streaminfo; - struct - { - int unused; - } padding; - struct - { - ma_uint32 id; - const void* pData; - ma_uint32 dataSize; - } application; - struct - { - ma_uint32 seekpointCount; - const ma_dr_flac_seekpoint* pSeekpoints; - } seektable; - struct - { - ma_uint32 vendorLength; - const char* vendor; - ma_uint32 commentCount; - const void* pComments; - } vorbis_comment; - struct - { - char catalog[128]; - ma_uint64 leadInSampleCount; - ma_bool32 isCD; - ma_uint8 trackCount; - const void* pTrackData; - } cuesheet; - struct - { - ma_uint32 type; - ma_uint32 mimeLength; - const char* mime; - ma_uint32 descriptionLength; - const char* description; - ma_uint32 width; - ma_uint32 height; - ma_uint32 colorDepth; - ma_uint32 indexColorCount; - ma_uint32 pictureDataSize; - const ma_uint8* pPictureData; - } picture; - } data; -} ma_dr_flac_metadata; -typedef size_t (* ma_dr_flac_read_proc)(void* pUserData, void* pBufferOut, size_t bytesToRead); -typedef ma_bool32 (* ma_dr_flac_seek_proc)(void* pUserData, int offset, ma_dr_flac_seek_origin origin); -typedef void (* ma_dr_flac_meta_proc)(void* pUserData, ma_dr_flac_metadata* pMetadata); -typedef struct -{ - const ma_uint8* data; - size_t dataSize; - size_t currentReadPos; -} ma_dr_flac__memory_stream; -typedef struct -{ - ma_dr_flac_read_proc onRead; - ma_dr_flac_seek_proc onSeek; - void* pUserData; - size_t unalignedByteCount; - ma_dr_flac_cache_t unalignedCache; - ma_uint32 nextL2Line; - ma_uint32 consumedBits; - ma_dr_flac_cache_t cacheL2[MA_DR_FLAC_BUFFER_SIZE/sizeof(ma_dr_flac_cache_t)]; - ma_dr_flac_cache_t cache; - ma_uint16 crc16; - ma_dr_flac_cache_t crc16Cache; - ma_uint32 crc16CacheIgnoredBytes; -} ma_dr_flac_bs; -typedef struct -{ - ma_uint8 subframeType; - ma_uint8 wastedBitsPerSample; - ma_uint8 lpcOrder; - ma_int32* pSamplesS32; -} ma_dr_flac_subframe; -typedef struct -{ - ma_uint64 pcmFrameNumber; - ma_uint32 flacFrameNumber; - ma_uint32 sampleRate; - ma_uint16 blockSizeInPCMFrames; - ma_uint8 channelAssignment; - ma_uint8 bitsPerSample; - ma_uint8 crc8; -} ma_dr_flac_frame_header; -typedef struct -{ - ma_dr_flac_frame_header header; - ma_uint32 pcmFramesRemaining; - ma_dr_flac_subframe subframes[8]; -} ma_dr_flac_frame; -typedef struct -{ - ma_dr_flac_meta_proc onMeta; - void* pUserDataMD; - ma_allocation_callbacks allocationCallbacks; - ma_uint32 sampleRate; - ma_uint8 channels; - ma_uint8 bitsPerSample; - ma_uint16 maxBlockSizeInPCMFrames; - ma_uint64 totalPCMFrameCount; - ma_dr_flac_container container; - ma_uint32 seekpointCount; - ma_dr_flac_frame currentFLACFrame; - ma_uint64 currentPCMFrame; - ma_uint64 firstFLACFramePosInBytes; - ma_dr_flac__memory_stream memoryStream; - ma_int32* pDecodedSamples; - ma_dr_flac_seekpoint* pSeekpoints; - void* _oggbs; - ma_bool32 _noSeekTableSeek : 1; - ma_bool32 _noBinarySearchSeek : 1; - ma_bool32 _noBruteForceSeek : 1; - ma_dr_flac_bs bs; - ma_uint8 pExtraData[1]; -} ma_dr_flac; -MA_API ma_dr_flac* ma_dr_flac_open(ma_dr_flac_read_proc onRead, ma_dr_flac_seek_proc onSeek, void* pUserData, const ma_allocation_callbacks* pAllocationCallbacks); -MA_API ma_dr_flac* ma_dr_flac_open_relaxed(ma_dr_flac_read_proc onRead, ma_dr_flac_seek_proc onSeek, ma_dr_flac_container container, void* pUserData, const ma_allocation_callbacks* pAllocationCallbacks); -MA_API ma_dr_flac* ma_dr_flac_open_with_metadata(ma_dr_flac_read_proc onRead, ma_dr_flac_seek_proc onSeek, ma_dr_flac_meta_proc onMeta, void* pUserData, const ma_allocation_callbacks* pAllocationCallbacks); -MA_API ma_dr_flac* ma_dr_flac_open_with_metadata_relaxed(ma_dr_flac_read_proc onRead, ma_dr_flac_seek_proc onSeek, ma_dr_flac_meta_proc onMeta, ma_dr_flac_container container, void* pUserData, const ma_allocation_callbacks* pAllocationCallbacks); -MA_API void ma_dr_flac_close(ma_dr_flac* pFlac); -MA_API ma_uint64 ma_dr_flac_read_pcm_frames_s32(ma_dr_flac* pFlac, ma_uint64 framesToRead, ma_int32* pBufferOut); -MA_API ma_uint64 ma_dr_flac_read_pcm_frames_s16(ma_dr_flac* pFlac, ma_uint64 framesToRead, ma_int16* pBufferOut); -MA_API ma_uint64 ma_dr_flac_read_pcm_frames_f32(ma_dr_flac* pFlac, ma_uint64 framesToRead, float* pBufferOut); -MA_API ma_bool32 ma_dr_flac_seek_to_pcm_frame(ma_dr_flac* pFlac, ma_uint64 pcmFrameIndex); -#ifndef MA_DR_FLAC_NO_STDIO -MA_API ma_dr_flac* ma_dr_flac_open_file(const char* pFileName, const ma_allocation_callbacks* pAllocationCallbacks); -MA_API ma_dr_flac* ma_dr_flac_open_file_w(const wchar_t* pFileName, const ma_allocation_callbacks* pAllocationCallbacks); -MA_API ma_dr_flac* ma_dr_flac_open_file_with_metadata(const char* pFileName, ma_dr_flac_meta_proc onMeta, void* pUserData, const ma_allocation_callbacks* pAllocationCallbacks); -MA_API ma_dr_flac* ma_dr_flac_open_file_with_metadata_w(const wchar_t* pFileName, ma_dr_flac_meta_proc onMeta, void* pUserData, const ma_allocation_callbacks* pAllocationCallbacks); -#endif -MA_API ma_dr_flac* ma_dr_flac_open_memory(const void* pData, size_t dataSize, const ma_allocation_callbacks* pAllocationCallbacks); -MA_API ma_dr_flac* ma_dr_flac_open_memory_with_metadata(const void* pData, size_t dataSize, ma_dr_flac_meta_proc onMeta, void* pUserData, const ma_allocation_callbacks* pAllocationCallbacks); -MA_API ma_int32* ma_dr_flac_open_and_read_pcm_frames_s32(ma_dr_flac_read_proc onRead, ma_dr_flac_seek_proc onSeek, void* pUserData, unsigned int* channels, unsigned int* sampleRate, ma_uint64* totalPCMFrameCount, const ma_allocation_callbacks* pAllocationCallbacks); -MA_API ma_int16* ma_dr_flac_open_and_read_pcm_frames_s16(ma_dr_flac_read_proc onRead, ma_dr_flac_seek_proc onSeek, void* pUserData, unsigned int* channels, unsigned int* sampleRate, ma_uint64* totalPCMFrameCount, const ma_allocation_callbacks* pAllocationCallbacks); -MA_API float* ma_dr_flac_open_and_read_pcm_frames_f32(ma_dr_flac_read_proc onRead, ma_dr_flac_seek_proc onSeek, void* pUserData, unsigned int* channels, unsigned int* sampleRate, ma_uint64* totalPCMFrameCount, const ma_allocation_callbacks* pAllocationCallbacks); -#ifndef MA_DR_FLAC_NO_STDIO -MA_API ma_int32* ma_dr_flac_open_file_and_read_pcm_frames_s32(const char* filename, unsigned int* channels, unsigned int* sampleRate, ma_uint64* totalPCMFrameCount, const ma_allocation_callbacks* pAllocationCallbacks); -MA_API ma_int16* ma_dr_flac_open_file_and_read_pcm_frames_s16(const char* filename, unsigned int* channels, unsigned int* sampleRate, ma_uint64* totalPCMFrameCount, const ma_allocation_callbacks* pAllocationCallbacks); -MA_API float* ma_dr_flac_open_file_and_read_pcm_frames_f32(const char* filename, unsigned int* channels, unsigned int* sampleRate, ma_uint64* totalPCMFrameCount, const ma_allocation_callbacks* pAllocationCallbacks); -#endif -MA_API ma_int32* ma_dr_flac_open_memory_and_read_pcm_frames_s32(const void* data, size_t dataSize, unsigned int* channels, unsigned int* sampleRate, ma_uint64* totalPCMFrameCount, const ma_allocation_callbacks* pAllocationCallbacks); -MA_API ma_int16* ma_dr_flac_open_memory_and_read_pcm_frames_s16(const void* data, size_t dataSize, unsigned int* channels, unsigned int* sampleRate, ma_uint64* totalPCMFrameCount, const ma_allocation_callbacks* pAllocationCallbacks); -MA_API float* ma_dr_flac_open_memory_and_read_pcm_frames_f32(const void* data, size_t dataSize, unsigned int* channels, unsigned int* sampleRate, ma_uint64* totalPCMFrameCount, const ma_allocation_callbacks* pAllocationCallbacks); -MA_API void ma_dr_flac_free(void* p, const ma_allocation_callbacks* pAllocationCallbacks); -typedef struct -{ - ma_uint32 countRemaining; - const char* pRunningData; -} ma_dr_flac_vorbis_comment_iterator; -MA_API void ma_dr_flac_init_vorbis_comment_iterator(ma_dr_flac_vorbis_comment_iterator* pIter, ma_uint32 commentCount, const void* pComments); -MA_API const char* ma_dr_flac_next_vorbis_comment(ma_dr_flac_vorbis_comment_iterator* pIter, ma_uint32* pCommentLengthOut); -typedef struct -{ - ma_uint32 countRemaining; - const char* pRunningData; -} ma_dr_flac_cuesheet_track_iterator; -typedef struct -{ - ma_uint64 offset; - ma_uint8 index; - ma_uint8 reserved[3]; -} ma_dr_flac_cuesheet_track_index; -typedef struct -{ - ma_uint64 offset; - ma_uint8 trackNumber; - char ISRC[12]; - ma_bool8 isAudio; - ma_bool8 preEmphasis; - ma_uint8 indexCount; - const ma_dr_flac_cuesheet_track_index* pIndexPoints; -} ma_dr_flac_cuesheet_track; -MA_API void ma_dr_flac_init_cuesheet_track_iterator(ma_dr_flac_cuesheet_track_iterator* pIter, ma_uint32 trackCount, const void* pTrackData); -MA_API ma_bool32 ma_dr_flac_next_cuesheet_track(ma_dr_flac_cuesheet_track_iterator* pIter, ma_dr_flac_cuesheet_track* pCuesheetTrack); -#ifdef __cplusplus -} -#endif -#endif -/* dr_flac_h end */ -#endif /* MA_NO_FLAC */ - -#if !defined(MA_NO_MP3) && !defined(MA_NO_DECODING) -/* dr_mp3_h begin */ -#ifndef ma_dr_mp3_h -#define ma_dr_mp3_h -#ifdef __cplusplus -extern "C" { -#endif -#define MA_DR_MP3_STRINGIFY(x) #x -#define MA_DR_MP3_XSTRINGIFY(x) MA_DR_MP3_STRINGIFY(x) -#define MA_DR_MP3_VERSION_MAJOR 0 -#define MA_DR_MP3_VERSION_MINOR 6 -#define MA_DR_MP3_VERSION_REVISION 40 -#define MA_DR_MP3_VERSION_STRING MA_DR_MP3_XSTRINGIFY(MA_DR_MP3_VERSION_MAJOR) "." MA_DR_MP3_XSTRINGIFY(MA_DR_MP3_VERSION_MINOR) "." MA_DR_MP3_XSTRINGIFY(MA_DR_MP3_VERSION_REVISION) -#include -#define MA_DR_MP3_MAX_PCM_FRAMES_PER_MP3_FRAME 1152 -#define MA_DR_MP3_MAX_SAMPLES_PER_FRAME (MA_DR_MP3_MAX_PCM_FRAMES_PER_MP3_FRAME*2) -MA_API void ma_dr_mp3_version(ma_uint32* pMajor, ma_uint32* pMinor, ma_uint32* pRevision); -MA_API const char* ma_dr_mp3_version_string(void); -typedef struct -{ - int frame_bytes, channels, hz, layer, bitrate_kbps; -} ma_dr_mp3dec_frame_info; -typedef struct -{ - float mdct_overlap[2][9*32], qmf_state[15*2*32]; - int reserv, free_format_bytes; - ma_uint8 header[4], reserv_buf[511]; -} ma_dr_mp3dec; -MA_API void ma_dr_mp3dec_init(ma_dr_mp3dec *dec); -MA_API int ma_dr_mp3dec_decode_frame(ma_dr_mp3dec *dec, const ma_uint8 *mp3, int mp3_bytes, void *pcm, ma_dr_mp3dec_frame_info *info); -MA_API void ma_dr_mp3dec_f32_to_s16(const float *in, ma_int16 *out, size_t num_samples); -typedef enum -{ - ma_dr_mp3_seek_origin_start, - ma_dr_mp3_seek_origin_current -} ma_dr_mp3_seek_origin; -typedef struct -{ - ma_uint64 seekPosInBytes; - ma_uint64 pcmFrameIndex; - ma_uint16 mp3FramesToDiscard; - ma_uint16 pcmFramesToDiscard; -} ma_dr_mp3_seek_point; -typedef size_t (* ma_dr_mp3_read_proc)(void* pUserData, void* pBufferOut, size_t bytesToRead); -typedef ma_bool32 (* ma_dr_mp3_seek_proc)(void* pUserData, int offset, ma_dr_mp3_seek_origin origin); -typedef struct -{ - ma_uint32 channels; - ma_uint32 sampleRate; -} ma_dr_mp3_config; -typedef struct -{ - ma_dr_mp3dec decoder; - ma_uint32 channels; - ma_uint32 sampleRate; - ma_dr_mp3_read_proc onRead; - ma_dr_mp3_seek_proc onSeek; - void* pUserData; - ma_allocation_callbacks allocationCallbacks; - ma_uint32 mp3FrameChannels; - ma_uint32 mp3FrameSampleRate; - ma_uint32 pcmFramesConsumedInMP3Frame; - ma_uint32 pcmFramesRemainingInMP3Frame; - ma_uint8 pcmFrames[sizeof(float)*MA_DR_MP3_MAX_SAMPLES_PER_FRAME]; - ma_uint64 currentPCMFrame; - ma_uint64 streamCursor; - ma_dr_mp3_seek_point* pSeekPoints; - ma_uint32 seekPointCount; - size_t dataSize; - size_t dataCapacity; - size_t dataConsumed; - ma_uint8* pData; - ma_bool32 atEnd : 1; - struct - { - const ma_uint8* pData; - size_t dataSize; - size_t currentReadPos; - } memory; -} ma_dr_mp3; -MA_API ma_bool32 ma_dr_mp3_init(ma_dr_mp3* pMP3, ma_dr_mp3_read_proc onRead, ma_dr_mp3_seek_proc onSeek, void* pUserData, const ma_allocation_callbacks* pAllocationCallbacks); -MA_API ma_bool32 ma_dr_mp3_init_memory(ma_dr_mp3* pMP3, const void* pData, size_t dataSize, const ma_allocation_callbacks* pAllocationCallbacks); -#ifndef MA_DR_MP3_NO_STDIO -MA_API ma_bool32 ma_dr_mp3_init_file(ma_dr_mp3* pMP3, const char* pFilePath, const ma_allocation_callbacks* pAllocationCallbacks); -MA_API ma_bool32 ma_dr_mp3_init_file_w(ma_dr_mp3* pMP3, const wchar_t* pFilePath, const ma_allocation_callbacks* pAllocationCallbacks); -#endif -MA_API void ma_dr_mp3_uninit(ma_dr_mp3* pMP3); -MA_API ma_uint64 ma_dr_mp3_read_pcm_frames_f32(ma_dr_mp3* pMP3, ma_uint64 framesToRead, float* pBufferOut); -MA_API ma_uint64 ma_dr_mp3_read_pcm_frames_s16(ma_dr_mp3* pMP3, ma_uint64 framesToRead, ma_int16* pBufferOut); -MA_API ma_bool32 ma_dr_mp3_seek_to_pcm_frame(ma_dr_mp3* pMP3, ma_uint64 frameIndex); -MA_API ma_uint64 ma_dr_mp3_get_pcm_frame_count(ma_dr_mp3* pMP3); -MA_API ma_uint64 ma_dr_mp3_get_mp3_frame_count(ma_dr_mp3* pMP3); -MA_API ma_bool32 ma_dr_mp3_get_mp3_and_pcm_frame_count(ma_dr_mp3* pMP3, ma_uint64* pMP3FrameCount, ma_uint64* pPCMFrameCount); -MA_API ma_bool32 ma_dr_mp3_calculate_seek_points(ma_dr_mp3* pMP3, ma_uint32* pSeekPointCount, ma_dr_mp3_seek_point* pSeekPoints); -MA_API ma_bool32 ma_dr_mp3_bind_seek_table(ma_dr_mp3* pMP3, ma_uint32 seekPointCount, ma_dr_mp3_seek_point* pSeekPoints); -MA_API float* ma_dr_mp3_open_and_read_pcm_frames_f32(ma_dr_mp3_read_proc onRead, ma_dr_mp3_seek_proc onSeek, void* pUserData, ma_dr_mp3_config* pConfig, ma_uint64* pTotalFrameCount, const ma_allocation_callbacks* pAllocationCallbacks); -MA_API ma_int16* ma_dr_mp3_open_and_read_pcm_frames_s16(ma_dr_mp3_read_proc onRead, ma_dr_mp3_seek_proc onSeek, void* pUserData, ma_dr_mp3_config* pConfig, ma_uint64* pTotalFrameCount, const ma_allocation_callbacks* pAllocationCallbacks); -MA_API float* ma_dr_mp3_open_memory_and_read_pcm_frames_f32(const void* pData, size_t dataSize, ma_dr_mp3_config* pConfig, ma_uint64* pTotalFrameCount, const ma_allocation_callbacks* pAllocationCallbacks); -MA_API ma_int16* ma_dr_mp3_open_memory_and_read_pcm_frames_s16(const void* pData, size_t dataSize, ma_dr_mp3_config* pConfig, ma_uint64* pTotalFrameCount, const ma_allocation_callbacks* pAllocationCallbacks); -#ifndef MA_DR_MP3_NO_STDIO -MA_API float* ma_dr_mp3_open_file_and_read_pcm_frames_f32(const char* filePath, ma_dr_mp3_config* pConfig, ma_uint64* pTotalFrameCount, const ma_allocation_callbacks* pAllocationCallbacks); -MA_API ma_int16* ma_dr_mp3_open_file_and_read_pcm_frames_s16(const char* filePath, ma_dr_mp3_config* pConfig, ma_uint64* pTotalFrameCount, const ma_allocation_callbacks* pAllocationCallbacks); -#endif -MA_API void* ma_dr_mp3_malloc(size_t sz, const ma_allocation_callbacks* pAllocationCallbacks); -MA_API void ma_dr_mp3_free(void* p, const ma_allocation_callbacks* pAllocationCallbacks); -#ifdef __cplusplus -} -#endif -#endif -/* dr_mp3_h end */ -#endif /* MA_NO_MP3 */ - - -/************************************************************************************************************************************************************** - -Decoding - -**************************************************************************************************************************************************************/ -#ifndef MA_NO_DECODING - -static ma_result ma_decoder_read_bytes(ma_decoder* pDecoder, void* pBufferOut, size_t bytesToRead, size_t* pBytesRead) -{ - MA_ASSERT(pDecoder != NULL); - - return pDecoder->onRead(pDecoder, pBufferOut, bytesToRead, pBytesRead); -} - -static ma_result ma_decoder_seek_bytes(ma_decoder* pDecoder, ma_int64 byteOffset, ma_seek_origin origin) -{ - MA_ASSERT(pDecoder != NULL); - - return pDecoder->onSeek(pDecoder, byteOffset, origin); -} - -static ma_result ma_decoder_tell_bytes(ma_decoder* pDecoder, ma_int64* pCursor) -{ - MA_ASSERT(pDecoder != NULL); - - if (pDecoder->onTell == NULL) { - return MA_NOT_IMPLEMENTED; - } - - return pDecoder->onTell(pDecoder, pCursor); -} - - -MA_API ma_decoding_backend_config ma_decoding_backend_config_init(ma_format preferredFormat, ma_uint32 seekPointCount) -{ - ma_decoding_backend_config config; - - MA_ZERO_OBJECT(&config); - config.preferredFormat = preferredFormat; - config.seekPointCount = seekPointCount; - - return config; -} - - -MA_API ma_decoder_config ma_decoder_config_init(ma_format outputFormat, ma_uint32 outputChannels, ma_uint32 outputSampleRate) -{ - ma_decoder_config config; - MA_ZERO_OBJECT(&config); - config.format = outputFormat; - config.channels = outputChannels; - config.sampleRate = outputSampleRate; - config.resampling = ma_resampler_config_init(ma_format_unknown, 0, 0, 0, ma_resample_algorithm_linear); /* Format/channels/rate doesn't matter here. */ - config.encodingFormat = ma_encoding_format_unknown; - - /* Note that we are intentionally leaving the channel map empty here which will cause the default channel map to be used. */ - - return config; -} - -MA_API ma_decoder_config ma_decoder_config_init_default(void) -{ - return ma_decoder_config_init(ma_format_unknown, 0, 0); -} - -MA_API ma_decoder_config ma_decoder_config_init_copy(const ma_decoder_config* pConfig) -{ - ma_decoder_config config; - if (pConfig != NULL) { - config = *pConfig; - } else { - MA_ZERO_OBJECT(&config); - } - - return config; -} - -static ma_result ma_decoder__init_data_converter(ma_decoder* pDecoder, const ma_decoder_config* pConfig) -{ - ma_result result; - ma_data_converter_config converterConfig; - ma_format internalFormat; - ma_uint32 internalChannels; - ma_uint32 internalSampleRate; - ma_channel internalChannelMap[MA_MAX_CHANNELS]; - - MA_ASSERT(pDecoder != NULL); - MA_ASSERT(pConfig != NULL); - - result = ma_data_source_get_data_format(pDecoder->pBackend, &internalFormat, &internalChannels, &internalSampleRate, internalChannelMap, ma_countof(internalChannelMap)); - if (result != MA_SUCCESS) { - return result; /* Failed to retrieve the internal data format. */ - } - - - /* Make sure we're not asking for too many channels. */ - if (pConfig->channels > MA_MAX_CHANNELS) { - return MA_INVALID_ARGS; - } - - /* The internal channels should have already been validated at a higher level, but we'll do it again explicitly here for safety. */ - if (internalChannels > MA_MAX_CHANNELS) { - return MA_INVALID_ARGS; - } - - - /* Output format. */ - if (pConfig->format == ma_format_unknown) { - pDecoder->outputFormat = internalFormat; - } else { - pDecoder->outputFormat = pConfig->format; - } - - if (pConfig->channels == 0) { - pDecoder->outputChannels = internalChannels; - } else { - pDecoder->outputChannels = pConfig->channels; - } - - if (pConfig->sampleRate == 0) { - pDecoder->outputSampleRate = internalSampleRate; - } else { - pDecoder->outputSampleRate = pConfig->sampleRate; - } - - converterConfig = ma_data_converter_config_init( - internalFormat, pDecoder->outputFormat, - internalChannels, pDecoder->outputChannels, - internalSampleRate, pDecoder->outputSampleRate - ); - converterConfig.pChannelMapIn = internalChannelMap; - converterConfig.pChannelMapOut = pConfig->pChannelMap; - converterConfig.channelMixMode = pConfig->channelMixMode; - converterConfig.ditherMode = pConfig->ditherMode; - converterConfig.allowDynamicSampleRate = MA_FALSE; /* Never allow dynamic sample rate conversion. Setting this to true will disable passthrough optimizations. */ - converterConfig.resampling = pConfig->resampling; - - result = ma_data_converter_init(&converterConfig, &pDecoder->allocationCallbacks, &pDecoder->converter); - if (result != MA_SUCCESS) { - return result; - } - - /* - Now that we have the decoder we need to determine whether or not we need a heap-allocated cache. We'll - need this if the data converter does not support calculation of the required input frame count. To - determine support for this we'll just run a test. - */ - { - ma_uint64 unused; - - result = ma_data_converter_get_required_input_frame_count(&pDecoder->converter, 1, &unused); - if (result != MA_SUCCESS) { - /* - We were unable to calculate the required input frame count which means we'll need to use - a heap-allocated cache. - */ - ma_uint64 inputCacheCapSizeInBytes; - - pDecoder->inputCacheCap = MA_DATA_CONVERTER_STACK_BUFFER_SIZE / ma_get_bytes_per_frame(internalFormat, internalChannels); - - /* Not strictly necessary, but keeping here for safety in case we change the default value of pDecoder->inputCacheCap. */ - inputCacheCapSizeInBytes = pDecoder->inputCacheCap * ma_get_bytes_per_frame(internalFormat, internalChannels); - if (inputCacheCapSizeInBytes > MA_SIZE_MAX) { - ma_data_converter_uninit(&pDecoder->converter, &pDecoder->allocationCallbacks); - return MA_OUT_OF_MEMORY; - } - - pDecoder->pInputCache = ma_malloc((size_t)inputCacheCapSizeInBytes, &pDecoder->allocationCallbacks); /* Safe cast to size_t. */ - if (pDecoder->pInputCache == NULL) { - ma_data_converter_uninit(&pDecoder->converter, &pDecoder->allocationCallbacks); - return MA_OUT_OF_MEMORY; - } - } - } - - return MA_SUCCESS; -} - - - -static ma_result ma_decoder_internal_on_read__custom(void* pUserData, void* pBufferOut, size_t bytesToRead, size_t* pBytesRead) -{ - ma_decoder* pDecoder = (ma_decoder*)pUserData; - MA_ASSERT(pDecoder != NULL); - - return ma_decoder_read_bytes(pDecoder, pBufferOut, bytesToRead, pBytesRead); -} - -static ma_result ma_decoder_internal_on_seek__custom(void* pUserData, ma_int64 offset, ma_seek_origin origin) -{ - ma_decoder* pDecoder = (ma_decoder*)pUserData; - MA_ASSERT(pDecoder != NULL); - - return ma_decoder_seek_bytes(pDecoder, offset, origin); -} - -static ma_result ma_decoder_internal_on_tell__custom(void* pUserData, ma_int64* pCursor) -{ - ma_decoder* pDecoder = (ma_decoder*)pUserData; - MA_ASSERT(pDecoder != NULL); - - return ma_decoder_tell_bytes(pDecoder, pCursor); -} - - -static ma_result ma_decoder_init_from_vtable__internal(const ma_decoding_backend_vtable* pVTable, void* pVTableUserData, const ma_decoder_config* pConfig, ma_decoder* pDecoder) -{ - ma_result result; - ma_decoding_backend_config backendConfig; - ma_data_source* pBackend; - - MA_ASSERT(pVTable != NULL); - MA_ASSERT(pConfig != NULL); - MA_ASSERT(pDecoder != NULL); - - if (pVTable->onInit == NULL) { - return MA_NOT_IMPLEMENTED; - } - - backendConfig = ma_decoding_backend_config_init(pConfig->format, pConfig->seekPointCount); - - result = pVTable->onInit(pVTableUserData, ma_decoder_internal_on_read__custom, ma_decoder_internal_on_seek__custom, ma_decoder_internal_on_tell__custom, pDecoder, &backendConfig, &pDecoder->allocationCallbacks, &pBackend); - if (result != MA_SUCCESS) { - return result; /* Failed to initialize the backend from this vtable. */ - } - - /* Getting here means we were able to initialize the backend so we can now initialize the decoder. */ - pDecoder->pBackend = pBackend; - pDecoder->pBackendVTable = pVTable; - pDecoder->pBackendUserData = pConfig->pCustomBackendUserData; - - return MA_SUCCESS; -} - -static ma_result ma_decoder_init_from_file__internal(const ma_decoding_backend_vtable* pVTable, void* pVTableUserData, const char* pFilePath, const ma_decoder_config* pConfig, ma_decoder* pDecoder) -{ - ma_result result; - ma_decoding_backend_config backendConfig; - ma_data_source* pBackend; - - MA_ASSERT(pVTable != NULL); - MA_ASSERT(pConfig != NULL); - MA_ASSERT(pDecoder != NULL); - - if (pVTable->onInitFile == NULL) { - return MA_NOT_IMPLEMENTED; - } - - backendConfig = ma_decoding_backend_config_init(pConfig->format, pConfig->seekPointCount); - - result = pVTable->onInitFile(pVTableUserData, pFilePath, &backendConfig, &pDecoder->allocationCallbacks, &pBackend); - if (result != MA_SUCCESS) { - return result; /* Failed to initialize the backend from this vtable. */ - } - - /* Getting here means we were able to initialize the backend so we can now initialize the decoder. */ - pDecoder->pBackend = pBackend; - pDecoder->pBackendVTable = pVTable; - pDecoder->pBackendUserData = pConfig->pCustomBackendUserData; - - return MA_SUCCESS; -} - -static ma_result ma_decoder_init_from_file_w__internal(const ma_decoding_backend_vtable* pVTable, void* pVTableUserData, const wchar_t* pFilePath, const ma_decoder_config* pConfig, ma_decoder* pDecoder) -{ - ma_result result; - ma_decoding_backend_config backendConfig; - ma_data_source* pBackend; - - MA_ASSERT(pVTable != NULL); - MA_ASSERT(pConfig != NULL); - MA_ASSERT(pDecoder != NULL); - - if (pVTable->onInitFileW == NULL) { - return MA_NOT_IMPLEMENTED; - } - - backendConfig = ma_decoding_backend_config_init(pConfig->format, pConfig->seekPointCount); - - result = pVTable->onInitFileW(pVTableUserData, pFilePath, &backendConfig, &pDecoder->allocationCallbacks, &pBackend); - if (result != MA_SUCCESS) { - return result; /* Failed to initialize the backend from this vtable. */ - } - - /* Getting here means we were able to initialize the backend so we can now initialize the decoder. */ - pDecoder->pBackend = pBackend; - pDecoder->pBackendVTable = pVTable; - pDecoder->pBackendUserData = pConfig->pCustomBackendUserData; - - return MA_SUCCESS; -} - -static ma_result ma_decoder_init_from_memory__internal(const ma_decoding_backend_vtable* pVTable, void* pVTableUserData, const void* pData, size_t dataSize, const ma_decoder_config* pConfig, ma_decoder* pDecoder) -{ - ma_result result; - ma_decoding_backend_config backendConfig; - ma_data_source* pBackend; - - MA_ASSERT(pVTable != NULL); - MA_ASSERT(pConfig != NULL); - MA_ASSERT(pDecoder != NULL); - - if (pVTable->onInitMemory == NULL) { - return MA_NOT_IMPLEMENTED; - } - - backendConfig = ma_decoding_backend_config_init(pConfig->format, pConfig->seekPointCount); - - result = pVTable->onInitMemory(pVTableUserData, pData, dataSize, &backendConfig, &pDecoder->allocationCallbacks, &pBackend); - if (result != MA_SUCCESS) { - return result; /* Failed to initialize the backend from this vtable. */ - } - - /* Getting here means we were able to initialize the backend so we can now initialize the decoder. */ - pDecoder->pBackend = pBackend; - pDecoder->pBackendVTable = pVTable; - pDecoder->pBackendUserData = pConfig->pCustomBackendUserData; - - return MA_SUCCESS; -} - - - -static ma_result ma_decoder_init_custom__internal(const ma_decoder_config* pConfig, ma_decoder* pDecoder) -{ - ma_result result = MA_NO_BACKEND; - size_t ivtable; - - MA_ASSERT(pConfig != NULL); - MA_ASSERT(pDecoder != NULL); - - if (pConfig->ppCustomBackendVTables == NULL) { - return MA_NO_BACKEND; - } - - /* The order each backend is listed is what defines the priority. */ - for (ivtable = 0; ivtable < pConfig->customBackendCount; ivtable += 1) { - const ma_decoding_backend_vtable* pVTable = pConfig->ppCustomBackendVTables[ivtable]; - if (pVTable != NULL) { - result = ma_decoder_init_from_vtable__internal(pVTable, pConfig->pCustomBackendUserData, pConfig, pDecoder); - if (result == MA_SUCCESS) { - return MA_SUCCESS; - } else { - /* Initialization failed. Move on to the next one, but seek back to the start first so the next vtable starts from the first byte of the file. */ - result = ma_decoder_seek_bytes(pDecoder, 0, ma_seek_origin_start); - if (result != MA_SUCCESS) { - return result; /* Failed to seek back to the start. */ - } - } - } else { - /* No vtable. */ - } - } - - /* Getting here means we couldn't find a backend. */ - return MA_NO_BACKEND; -} - -static ma_result ma_decoder_init_custom_from_file__internal(const char* pFilePath, const ma_decoder_config* pConfig, ma_decoder* pDecoder) -{ - ma_result result = MA_NO_BACKEND; - size_t ivtable; - - MA_ASSERT(pConfig != NULL); - MA_ASSERT(pDecoder != NULL); - - if (pConfig->ppCustomBackendVTables == NULL) { - return MA_NO_BACKEND; - } - - /* The order each backend is listed is what defines the priority. */ - for (ivtable = 0; ivtable < pConfig->customBackendCount; ivtable += 1) { - const ma_decoding_backend_vtable* pVTable = pConfig->ppCustomBackendVTables[ivtable]; - if (pVTable != NULL) { - result = ma_decoder_init_from_file__internal(pVTable, pConfig->pCustomBackendUserData, pFilePath, pConfig, pDecoder); - if (result == MA_SUCCESS) { - return MA_SUCCESS; - } - } else { - /* No vtable. */ - } - } - - /* Getting here means we couldn't find a backend. */ - return MA_NO_BACKEND; -} - -static ma_result ma_decoder_init_custom_from_file_w__internal(const wchar_t* pFilePath, const ma_decoder_config* pConfig, ma_decoder* pDecoder) -{ - ma_result result = MA_NO_BACKEND; - size_t ivtable; - - MA_ASSERT(pConfig != NULL); - MA_ASSERT(pDecoder != NULL); - - if (pConfig->ppCustomBackendVTables == NULL) { - return MA_NO_BACKEND; - } - - /* The order each backend is listed is what defines the priority. */ - for (ivtable = 0; ivtable < pConfig->customBackendCount; ivtable += 1) { - const ma_decoding_backend_vtable* pVTable = pConfig->ppCustomBackendVTables[ivtable]; - if (pVTable != NULL) { - result = ma_decoder_init_from_file_w__internal(pVTable, pConfig->pCustomBackendUserData, pFilePath, pConfig, pDecoder); - if (result == MA_SUCCESS) { - return MA_SUCCESS; - } - } else { - /* No vtable. */ - } - } - - /* Getting here means we couldn't find a backend. */ - return MA_NO_BACKEND; -} - -static ma_result ma_decoder_init_custom_from_memory__internal(const void* pData, size_t dataSize, const ma_decoder_config* pConfig, ma_decoder* pDecoder) -{ - ma_result result = MA_NO_BACKEND; - size_t ivtable; - - MA_ASSERT(pConfig != NULL); - MA_ASSERT(pDecoder != NULL); - - if (pConfig->ppCustomBackendVTables == NULL) { - return MA_NO_BACKEND; - } - - /* The order each backend is listed is what defines the priority. */ - for (ivtable = 0; ivtable < pConfig->customBackendCount; ivtable += 1) { - const ma_decoding_backend_vtable* pVTable = pConfig->ppCustomBackendVTables[ivtable]; - if (pVTable != NULL) { - result = ma_decoder_init_from_memory__internal(pVTable, pConfig->pCustomBackendUserData, pData, dataSize, pConfig, pDecoder); - if (result == MA_SUCCESS) { - return MA_SUCCESS; - } - } else { - /* No vtable. */ - } - } - - /* Getting here means we couldn't find a backend. */ - return MA_NO_BACKEND; -} - - -/* WAV */ -#ifdef ma_dr_wav_h -#define MA_HAS_WAV - -typedef struct -{ - ma_data_source_base ds; - ma_read_proc onRead; - ma_seek_proc onSeek; - ma_tell_proc onTell; - void* pReadSeekTellUserData; - ma_format format; /* Can be f32, s16 or s32. */ -#if !defined(MA_NO_WAV) - ma_dr_wav dr; -#endif -} ma_wav; - -MA_API ma_result ma_wav_init(ma_read_proc onRead, ma_seek_proc onSeek, ma_tell_proc onTell, void* pReadSeekTellUserData, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_wav* pWav); -MA_API ma_result ma_wav_init_file(const char* pFilePath, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_wav* pWav); -MA_API ma_result ma_wav_init_file_w(const wchar_t* pFilePath, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_wav* pWav); -MA_API ma_result ma_wav_init_memory(const void* pData, size_t dataSize, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_wav* pWav); -MA_API void ma_wav_uninit(ma_wav* pWav, const ma_allocation_callbacks* pAllocationCallbacks); -MA_API ma_result ma_wav_read_pcm_frames(ma_wav* pWav, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead); -MA_API ma_result ma_wav_seek_to_pcm_frame(ma_wav* pWav, ma_uint64 frameIndex); -MA_API ma_result ma_wav_get_data_format(ma_wav* pWav, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate, ma_channel* pChannelMap, size_t channelMapCap); -MA_API ma_result ma_wav_get_cursor_in_pcm_frames(ma_wav* pWav, ma_uint64* pCursor); -MA_API ma_result ma_wav_get_length_in_pcm_frames(ma_wav* pWav, ma_uint64* pLength); - - -static ma_result ma_wav_ds_read(ma_data_source* pDataSource, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead) -{ - return ma_wav_read_pcm_frames((ma_wav*)pDataSource, pFramesOut, frameCount, pFramesRead); -} - -static ma_result ma_wav_ds_seek(ma_data_source* pDataSource, ma_uint64 frameIndex) -{ - return ma_wav_seek_to_pcm_frame((ma_wav*)pDataSource, frameIndex); -} - -static ma_result ma_wav_ds_get_data_format(ma_data_source* pDataSource, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate, ma_channel* pChannelMap, size_t channelMapCap) -{ - return ma_wav_get_data_format((ma_wav*)pDataSource, pFormat, pChannels, pSampleRate, pChannelMap, channelMapCap); -} - -static ma_result ma_wav_ds_get_cursor(ma_data_source* pDataSource, ma_uint64* pCursor) -{ - return ma_wav_get_cursor_in_pcm_frames((ma_wav*)pDataSource, pCursor); -} - -static ma_result ma_wav_ds_get_length(ma_data_source* pDataSource, ma_uint64* pLength) -{ - return ma_wav_get_length_in_pcm_frames((ma_wav*)pDataSource, pLength); -} - -static ma_data_source_vtable g_ma_wav_ds_vtable = -{ - ma_wav_ds_read, - ma_wav_ds_seek, - ma_wav_ds_get_data_format, - ma_wav_ds_get_cursor, - ma_wav_ds_get_length, - NULL, /* onSetLooping */ - 0 -}; - - -#if !defined(MA_NO_WAV) -static size_t ma_wav_dr_callback__read(void* pUserData, void* pBufferOut, size_t bytesToRead) -{ - ma_wav* pWav = (ma_wav*)pUserData; - ma_result result; - size_t bytesRead; - - MA_ASSERT(pWav != NULL); - - result = pWav->onRead(pWav->pReadSeekTellUserData, pBufferOut, bytesToRead, &bytesRead); - (void)result; - - return bytesRead; -} - -static ma_bool32 ma_wav_dr_callback__seek(void* pUserData, int offset, ma_dr_wav_seek_origin origin) -{ - ma_wav* pWav = (ma_wav*)pUserData; - ma_result result; - ma_seek_origin maSeekOrigin; - - MA_ASSERT(pWav != NULL); - - maSeekOrigin = ma_seek_origin_start; - if (origin == ma_dr_wav_seek_origin_current) { - maSeekOrigin = ma_seek_origin_current; - } - - result = pWav->onSeek(pWav->pReadSeekTellUserData, offset, maSeekOrigin); - if (result != MA_SUCCESS) { - return MA_FALSE; - } - - return MA_TRUE; -} -#endif - -static ma_result ma_wav_init_internal(const ma_decoding_backend_config* pConfig, ma_wav* pWav) -{ - ma_result result; - ma_data_source_config dataSourceConfig; - - if (pWav == NULL) { - return MA_INVALID_ARGS; - } - - MA_ZERO_OBJECT(pWav); - pWav->format = ma_format_unknown; /* Use closest match to source file by default. */ - - if (pConfig != NULL && (pConfig->preferredFormat == ma_format_f32 || pConfig->preferredFormat == ma_format_s16 || pConfig->preferredFormat == ma_format_s32)) { - pWav->format = pConfig->preferredFormat; - } else { - /* Getting here means something other than f32 and s16 was specified. Just leave this unset to use the default format. */ - } - - dataSourceConfig = ma_data_source_config_init(); - dataSourceConfig.vtable = &g_ma_wav_ds_vtable; - - result = ma_data_source_init(&dataSourceConfig, &pWav->ds); - if (result != MA_SUCCESS) { - return result; /* Failed to initialize the base data source. */ - } - - return MA_SUCCESS; -} - -static ma_result ma_wav_post_init(ma_wav* pWav) -{ - /* - If an explicit format was not specified, try picking the closest match based on the internal - format. The format needs to be supported by miniaudio. - */ - if (pWav->format == ma_format_unknown) { - switch (pWav->dr.translatedFormatTag) - { - case MA_DR_WAVE_FORMAT_PCM: - { - if (pWav->dr.bitsPerSample == 8) { - pWav->format = ma_format_u8; - } else if (pWav->dr.bitsPerSample == 16) { - pWav->format = ma_format_s16; - } else if (pWav->dr.bitsPerSample == 24) { - pWav->format = ma_format_s24; - } else if (pWav->dr.bitsPerSample == 32) { - pWav->format = ma_format_s32; - } - } break; - - case MA_DR_WAVE_FORMAT_IEEE_FLOAT: - { - if (pWav->dr.bitsPerSample == 32) { - pWav->format = ma_format_f32; - } - } break; - - default: break; - } - - /* Fall back to f32 if we couldn't find anything. */ - if (pWav->format == ma_format_unknown) { - pWav->format = ma_format_f32; - } - } - - return MA_SUCCESS; -} - -MA_API ma_result ma_wav_init(ma_read_proc onRead, ma_seek_proc onSeek, ma_tell_proc onTell, void* pReadSeekTellUserData, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_wav* pWav) -{ - ma_result result; - - result = ma_wav_init_internal(pConfig, pWav); - if (result != MA_SUCCESS) { - return result; - } - - if (onRead == NULL || onSeek == NULL) { - return MA_INVALID_ARGS; /* onRead and onSeek are mandatory. */ - } - - pWav->onRead = onRead; - pWav->onSeek = onSeek; - pWav->onTell = onTell; - pWav->pReadSeekTellUserData = pReadSeekTellUserData; - - #if !defined(MA_NO_WAV) - { - ma_bool32 wavResult; - - wavResult = ma_dr_wav_init(&pWav->dr, ma_wav_dr_callback__read, ma_wav_dr_callback__seek, pWav, pAllocationCallbacks); - if (wavResult != MA_TRUE) { - return MA_INVALID_FILE; - } - - ma_wav_post_init(pWav); - - return MA_SUCCESS; - } - #else - { - /* wav is disabled. */ - (void)pAllocationCallbacks; - return MA_NOT_IMPLEMENTED; - } - #endif -} - -MA_API ma_result ma_wav_init_file(const char* pFilePath, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_wav* pWav) -{ - ma_result result; - - result = ma_wav_init_internal(pConfig, pWav); - if (result != MA_SUCCESS) { - return result; - } - - #if !defined(MA_NO_WAV) - { - ma_bool32 wavResult; - - wavResult = ma_dr_wav_init_file(&pWav->dr, pFilePath, pAllocationCallbacks); - if (wavResult != MA_TRUE) { - return MA_INVALID_FILE; - } - - ma_wav_post_init(pWav); - - return MA_SUCCESS; - } - #else - { - /* wav is disabled. */ - (void)pFilePath; - (void)pAllocationCallbacks; - return MA_NOT_IMPLEMENTED; - } - #endif -} - -MA_API ma_result ma_wav_init_file_w(const wchar_t* pFilePath, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_wav* pWav) -{ - ma_result result; - - result = ma_wav_init_internal(pConfig, pWav); - if (result != MA_SUCCESS) { - return result; - } - - #if !defined(MA_NO_WAV) - { - ma_bool32 wavResult; - - wavResult = ma_dr_wav_init_file_w(&pWav->dr, pFilePath, pAllocationCallbacks); - if (wavResult != MA_TRUE) { - return MA_INVALID_FILE; - } - - ma_wav_post_init(pWav); - - return MA_SUCCESS; - } - #else - { - /* wav is disabled. */ - (void)pFilePath; - (void)pAllocationCallbacks; - return MA_NOT_IMPLEMENTED; - } - #endif -} - -MA_API ma_result ma_wav_init_memory(const void* pData, size_t dataSize, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_wav* pWav) -{ - ma_result result; - - result = ma_wav_init_internal(pConfig, pWav); - if (result != MA_SUCCESS) { - return result; - } - - #if !defined(MA_NO_WAV) - { - ma_bool32 wavResult; - - wavResult = ma_dr_wav_init_memory(&pWav->dr, pData, dataSize, pAllocationCallbacks); - if (wavResult != MA_TRUE) { - return MA_INVALID_FILE; - } - - ma_wav_post_init(pWav); - - return MA_SUCCESS; - } - #else - { - /* wav is disabled. */ - (void)pData; - (void)dataSize; - (void)pAllocationCallbacks; - return MA_NOT_IMPLEMENTED; - } - #endif -} - -MA_API void ma_wav_uninit(ma_wav* pWav, const ma_allocation_callbacks* pAllocationCallbacks) -{ - if (pWav == NULL) { - return; - } - - (void)pAllocationCallbacks; - - #if !defined(MA_NO_WAV) - { - ma_dr_wav_uninit(&pWav->dr); - } - #else - { - /* wav is disabled. Should never hit this since initialization would have failed. */ - MA_ASSERT(MA_FALSE); - } - #endif - - ma_data_source_uninit(&pWav->ds); -} - -MA_API ma_result ma_wav_read_pcm_frames(ma_wav* pWav, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead) -{ - if (pFramesRead != NULL) { - *pFramesRead = 0; - } - - if (frameCount == 0) { - return MA_INVALID_ARGS; - } - - if (pWav == NULL) { - return MA_INVALID_ARGS; - } - - #if !defined(MA_NO_WAV) - { - /* We always use floating point format. */ - ma_result result = MA_SUCCESS; /* Must be initialized to MA_SUCCESS. */ - ma_uint64 totalFramesRead = 0; - ma_format format; - - ma_wav_get_data_format(pWav, &format, NULL, NULL, NULL, 0); - - switch (format) - { - case ma_format_f32: - { - totalFramesRead = ma_dr_wav_read_pcm_frames_f32(&pWav->dr, frameCount, (float*)pFramesOut); - } break; - - case ma_format_s16: - { - totalFramesRead = ma_dr_wav_read_pcm_frames_s16(&pWav->dr, frameCount, (ma_int16*)pFramesOut); - } break; - - case ma_format_s32: - { - totalFramesRead = ma_dr_wav_read_pcm_frames_s32(&pWav->dr, frameCount, (ma_int32*)pFramesOut); - } break; - - /* Fallback to a raw read. */ - case ma_format_unknown: return MA_INVALID_OPERATION; /* <-- this should never be hit because initialization would just fall back to a supported format. */ - default: - { - totalFramesRead = ma_dr_wav_read_pcm_frames(&pWav->dr, frameCount, pFramesOut); - } break; - } - - /* In the future we'll update ma_dr_wav to return MA_AT_END for us. */ - if (totalFramesRead == 0) { - result = MA_AT_END; - } - - if (pFramesRead != NULL) { - *pFramesRead = totalFramesRead; - } - - if (result == MA_SUCCESS && totalFramesRead == 0) { - result = MA_AT_END; - } - - return result; - } - #else - { - /* wav is disabled. Should never hit this since initialization would have failed. */ - MA_ASSERT(MA_FALSE); - - (void)pFramesOut; - (void)frameCount; - (void)pFramesRead; - - return MA_NOT_IMPLEMENTED; - } - #endif -} - -MA_API ma_result ma_wav_seek_to_pcm_frame(ma_wav* pWav, ma_uint64 frameIndex) -{ - if (pWav == NULL) { - return MA_INVALID_ARGS; - } - - #if !defined(MA_NO_WAV) - { - ma_bool32 wavResult; - - wavResult = ma_dr_wav_seek_to_pcm_frame(&pWav->dr, frameIndex); - if (wavResult != MA_TRUE) { - return MA_ERROR; - } - - return MA_SUCCESS; - } - #else - { - /* wav is disabled. Should never hit this since initialization would have failed. */ - MA_ASSERT(MA_FALSE); - - (void)frameIndex; - - return MA_NOT_IMPLEMENTED; - } - #endif -} - -MA_API ma_result ma_wav_get_data_format(ma_wav* pWav, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate, ma_channel* pChannelMap, size_t channelMapCap) -{ - /* Defaults for safety. */ - if (pFormat != NULL) { - *pFormat = ma_format_unknown; - } - if (pChannels != NULL) { - *pChannels = 0; - } - if (pSampleRate != NULL) { - *pSampleRate = 0; - } - if (pChannelMap != NULL) { - MA_ZERO_MEMORY(pChannelMap, sizeof(*pChannelMap) * channelMapCap); - } - - if (pWav == NULL) { - return MA_INVALID_OPERATION; - } - - if (pFormat != NULL) { - *pFormat = pWav->format; - } - - #if !defined(MA_NO_WAV) - { - if (pChannels != NULL) { - *pChannels = pWav->dr.channels; - } - - if (pSampleRate != NULL) { - *pSampleRate = pWav->dr.sampleRate; - } - - if (pChannelMap != NULL) { - ma_channel_map_init_standard(ma_standard_channel_map_microsoft, pChannelMap, channelMapCap, pWav->dr.channels); - } - - return MA_SUCCESS; - } - #else - { - /* wav is disabled. Should never hit this since initialization would have failed. */ - MA_ASSERT(MA_FALSE); - return MA_NOT_IMPLEMENTED; - } - #endif -} - -MA_API ma_result ma_wav_get_cursor_in_pcm_frames(ma_wav* pWav, ma_uint64* pCursor) -{ - if (pCursor == NULL) { - return MA_INVALID_ARGS; - } - - *pCursor = 0; /* Safety. */ - - if (pWav == NULL) { - return MA_INVALID_ARGS; - } - - #if !defined(MA_NO_WAV) - { - ma_result wavResult = ma_dr_wav_get_cursor_in_pcm_frames(&pWav->dr, pCursor); - if (wavResult != MA_SUCCESS) { - return (ma_result)wavResult; /* ma_dr_wav result codes map to miniaudio's. */ - } - - return MA_SUCCESS; - } - #else - { - /* wav is disabled. Should never hit this since initialization would have failed. */ - MA_ASSERT(MA_FALSE); - return MA_NOT_IMPLEMENTED; - } - #endif -} - -MA_API ma_result ma_wav_get_length_in_pcm_frames(ma_wav* pWav, ma_uint64* pLength) -{ - if (pLength == NULL) { - return MA_INVALID_ARGS; - } - - *pLength = 0; /* Safety. */ - - if (pWav == NULL) { - return MA_INVALID_ARGS; - } - - #if !defined(MA_NO_WAV) - { - ma_result wavResult = ma_dr_wav_get_length_in_pcm_frames(&pWav->dr, pLength); - if (wavResult != MA_SUCCESS) { - return (ma_result)wavResult; /* ma_dr_wav result codes map to miniaudio's. */ - } - - return MA_SUCCESS; - } - #else - { - /* wav is disabled. Should never hit this since initialization would have failed. */ - MA_ASSERT(MA_FALSE); - return MA_NOT_IMPLEMENTED; - } - #endif -} - - -static ma_result ma_decoding_backend_init__wav(void* pUserData, ma_read_proc onRead, ma_seek_proc onSeek, ma_tell_proc onTell, void* pReadSeekTellUserData, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_data_source** ppBackend) -{ - ma_result result; - ma_wav* pWav; - - (void)pUserData; /* For now not using pUserData, but once we start storing the vorbis decoder state within the ma_decoder structure this will be set to the decoder so we can avoid a malloc. */ - - /* For now we're just allocating the decoder backend on the heap. */ - pWav = (ma_wav*)ma_malloc(sizeof(*pWav), pAllocationCallbacks); - if (pWav == NULL) { - return MA_OUT_OF_MEMORY; - } - - result = ma_wav_init(onRead, onSeek, onTell, pReadSeekTellUserData, pConfig, pAllocationCallbacks, pWav); - if (result != MA_SUCCESS) { - ma_free(pWav, pAllocationCallbacks); - return result; - } - - *ppBackend = pWav; - - return MA_SUCCESS; -} - -static ma_result ma_decoding_backend_init_file__wav(void* pUserData, const char* pFilePath, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_data_source** ppBackend) -{ - ma_result result; - ma_wav* pWav; - - (void)pUserData; /* For now not using pUserData, but once we start storing the vorbis decoder state within the ma_decoder structure this will be set to the decoder so we can avoid a malloc. */ - - /* For now we're just allocating the decoder backend on the heap. */ - pWav = (ma_wav*)ma_malloc(sizeof(*pWav), pAllocationCallbacks); - if (pWav == NULL) { - return MA_OUT_OF_MEMORY; - } - - result = ma_wav_init_file(pFilePath, pConfig, pAllocationCallbacks, pWav); - if (result != MA_SUCCESS) { - ma_free(pWav, pAllocationCallbacks); - return result; - } - - *ppBackend = pWav; - - return MA_SUCCESS; -} - -static ma_result ma_decoding_backend_init_file_w__wav(void* pUserData, const wchar_t* pFilePath, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_data_source** ppBackend) -{ - ma_result result; - ma_wav* pWav; - - (void)pUserData; /* For now not using pUserData, but once we start storing the vorbis decoder state within the ma_decoder structure this will be set to the decoder so we can avoid a malloc. */ - - /* For now we're just allocating the decoder backend on the heap. */ - pWav = (ma_wav*)ma_malloc(sizeof(*pWav), pAllocationCallbacks); - if (pWav == NULL) { - return MA_OUT_OF_MEMORY; - } - - result = ma_wav_init_file_w(pFilePath, pConfig, pAllocationCallbacks, pWav); - if (result != MA_SUCCESS) { - ma_free(pWav, pAllocationCallbacks); - return result; - } - - *ppBackend = pWav; - - return MA_SUCCESS; -} - -static ma_result ma_decoding_backend_init_memory__wav(void* pUserData, const void* pData, size_t dataSize, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_data_source** ppBackend) -{ - ma_result result; - ma_wav* pWav; - - (void)pUserData; /* For now not using pUserData, but once we start storing the vorbis decoder state within the ma_decoder structure this will be set to the decoder so we can avoid a malloc. */ - - /* For now we're just allocating the decoder backend on the heap. */ - pWav = (ma_wav*)ma_malloc(sizeof(*pWav), pAllocationCallbacks); - if (pWav == NULL) { - return MA_OUT_OF_MEMORY; - } - - result = ma_wav_init_memory(pData, dataSize, pConfig, pAllocationCallbacks, pWav); - if (result != MA_SUCCESS) { - ma_free(pWav, pAllocationCallbacks); - return result; - } - - *ppBackend = pWav; - - return MA_SUCCESS; -} - -static void ma_decoding_backend_uninit__wav(void* pUserData, ma_data_source* pBackend, const ma_allocation_callbacks* pAllocationCallbacks) -{ - ma_wav* pWav = (ma_wav*)pBackend; - - (void)pUserData; - - ma_wav_uninit(pWav, pAllocationCallbacks); - ma_free(pWav, pAllocationCallbacks); -} - -static ma_decoding_backend_vtable g_ma_decoding_backend_vtable_wav = -{ - ma_decoding_backend_init__wav, - ma_decoding_backend_init_file__wav, - ma_decoding_backend_init_file_w__wav, - ma_decoding_backend_init_memory__wav, - ma_decoding_backend_uninit__wav -}; - -static ma_result ma_decoder_init_wav__internal(const ma_decoder_config* pConfig, ma_decoder* pDecoder) -{ - return ma_decoder_init_from_vtable__internal(&g_ma_decoding_backend_vtable_wav, NULL, pConfig, pDecoder); -} - -static ma_result ma_decoder_init_wav_from_file__internal(const char* pFilePath, const ma_decoder_config* pConfig, ma_decoder* pDecoder) -{ - return ma_decoder_init_from_file__internal(&g_ma_decoding_backend_vtable_wav, NULL, pFilePath, pConfig, pDecoder); -} - -static ma_result ma_decoder_init_wav_from_file_w__internal(const wchar_t* pFilePath, const ma_decoder_config* pConfig, ma_decoder* pDecoder) -{ - return ma_decoder_init_from_file_w__internal(&g_ma_decoding_backend_vtable_wav, NULL, pFilePath, pConfig, pDecoder); -} - -static ma_result ma_decoder_init_wav_from_memory__internal(const void* pData, size_t dataSize, const ma_decoder_config* pConfig, ma_decoder* pDecoder) -{ - return ma_decoder_init_from_memory__internal(&g_ma_decoding_backend_vtable_wav, NULL, pData, dataSize, pConfig, pDecoder); -} -#endif /* ma_dr_wav_h */ - -/* FLAC */ -#ifdef ma_dr_flac_h -#define MA_HAS_FLAC - -typedef struct -{ - ma_data_source_base ds; - ma_read_proc onRead; - ma_seek_proc onSeek; - ma_tell_proc onTell; - void* pReadSeekTellUserData; - ma_format format; /* Can be f32, s16 or s32. */ -#if !defined(MA_NO_FLAC) - ma_dr_flac* dr; -#endif -} ma_flac; - -MA_API ma_result ma_flac_init(ma_read_proc onRead, ma_seek_proc onSeek, ma_tell_proc onTell, void* pReadSeekTellUserData, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_flac* pFlac); -MA_API ma_result ma_flac_init_file(const char* pFilePath, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_flac* pFlac); -MA_API ma_result ma_flac_init_file_w(const wchar_t* pFilePath, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_flac* pFlac); -MA_API ma_result ma_flac_init_memory(const void* pData, size_t dataSize, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_flac* pFlac); -MA_API void ma_flac_uninit(ma_flac* pFlac, const ma_allocation_callbacks* pAllocationCallbacks); -MA_API ma_result ma_flac_read_pcm_frames(ma_flac* pFlac, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead); -MA_API ma_result ma_flac_seek_to_pcm_frame(ma_flac* pFlac, ma_uint64 frameIndex); -MA_API ma_result ma_flac_get_data_format(ma_flac* pFlac, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate, ma_channel* pChannelMap, size_t channelMapCap); -MA_API ma_result ma_flac_get_cursor_in_pcm_frames(ma_flac* pFlac, ma_uint64* pCursor); -MA_API ma_result ma_flac_get_length_in_pcm_frames(ma_flac* pFlac, ma_uint64* pLength); - - -static ma_result ma_flac_ds_read(ma_data_source* pDataSource, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead) -{ - return ma_flac_read_pcm_frames((ma_flac*)pDataSource, pFramesOut, frameCount, pFramesRead); -} - -static ma_result ma_flac_ds_seek(ma_data_source* pDataSource, ma_uint64 frameIndex) -{ - return ma_flac_seek_to_pcm_frame((ma_flac*)pDataSource, frameIndex); -} - -static ma_result ma_flac_ds_get_data_format(ma_data_source* pDataSource, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate, ma_channel* pChannelMap, size_t channelMapCap) -{ - return ma_flac_get_data_format((ma_flac*)pDataSource, pFormat, pChannels, pSampleRate, pChannelMap, channelMapCap); -} - -static ma_result ma_flac_ds_get_cursor(ma_data_source* pDataSource, ma_uint64* pCursor) -{ - return ma_flac_get_cursor_in_pcm_frames((ma_flac*)pDataSource, pCursor); -} - -static ma_result ma_flac_ds_get_length(ma_data_source* pDataSource, ma_uint64* pLength) -{ - return ma_flac_get_length_in_pcm_frames((ma_flac*)pDataSource, pLength); -} - -static ma_data_source_vtable g_ma_flac_ds_vtable = -{ - ma_flac_ds_read, - ma_flac_ds_seek, - ma_flac_ds_get_data_format, - ma_flac_ds_get_cursor, - ma_flac_ds_get_length, - NULL, /* onSetLooping */ - 0 -}; - - -#if !defined(MA_NO_FLAC) -static size_t ma_flac_dr_callback__read(void* pUserData, void* pBufferOut, size_t bytesToRead) -{ - ma_flac* pFlac = (ma_flac*)pUserData; - ma_result result; - size_t bytesRead; - - MA_ASSERT(pFlac != NULL); - - result = pFlac->onRead(pFlac->pReadSeekTellUserData, pBufferOut, bytesToRead, &bytesRead); - (void)result; - - return bytesRead; -} - -static ma_bool32 ma_flac_dr_callback__seek(void* pUserData, int offset, ma_dr_flac_seek_origin origin) -{ - ma_flac* pFlac = (ma_flac*)pUserData; - ma_result result; - ma_seek_origin maSeekOrigin; - - MA_ASSERT(pFlac != NULL); - - maSeekOrigin = ma_seek_origin_start; - if (origin == ma_dr_flac_seek_origin_current) { - maSeekOrigin = ma_seek_origin_current; - } - - result = pFlac->onSeek(pFlac->pReadSeekTellUserData, offset, maSeekOrigin); - if (result != MA_SUCCESS) { - return MA_FALSE; - } - - return MA_TRUE; -} -#endif - -static ma_result ma_flac_init_internal(const ma_decoding_backend_config* pConfig, ma_flac* pFlac) -{ - ma_result result; - ma_data_source_config dataSourceConfig; - - if (pFlac == NULL) { - return MA_INVALID_ARGS; - } - - MA_ZERO_OBJECT(pFlac); - pFlac->format = ma_format_f32; /* f32 by default. */ - - if (pConfig != NULL && (pConfig->preferredFormat == ma_format_f32 || pConfig->preferredFormat == ma_format_s16 || pConfig->preferredFormat == ma_format_s32)) { - pFlac->format = pConfig->preferredFormat; - } else { - /* Getting here means something other than f32 and s16 was specified. Just leave this unset to use the default format. */ - } - - dataSourceConfig = ma_data_source_config_init(); - dataSourceConfig.vtable = &g_ma_flac_ds_vtable; - - result = ma_data_source_init(&dataSourceConfig, &pFlac->ds); - if (result != MA_SUCCESS) { - return result; /* Failed to initialize the base data source. */ - } - - return MA_SUCCESS; -} - -MA_API ma_result ma_flac_init(ma_read_proc onRead, ma_seek_proc onSeek, ma_tell_proc onTell, void* pReadSeekTellUserData, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_flac* pFlac) -{ - ma_result result; - - result = ma_flac_init_internal(pConfig, pFlac); - if (result != MA_SUCCESS) { - return result; - } - - if (onRead == NULL || onSeek == NULL) { - return MA_INVALID_ARGS; /* onRead and onSeek are mandatory. */ - } - - pFlac->onRead = onRead; - pFlac->onSeek = onSeek; - pFlac->onTell = onTell; - pFlac->pReadSeekTellUserData = pReadSeekTellUserData; - - #if !defined(MA_NO_FLAC) - { - pFlac->dr = ma_dr_flac_open(ma_flac_dr_callback__read, ma_flac_dr_callback__seek, pFlac, pAllocationCallbacks); - if (pFlac->dr == NULL) { - return MA_INVALID_FILE; - } - - return MA_SUCCESS; - } - #else - { - /* flac is disabled. */ - (void)pAllocationCallbacks; - return MA_NOT_IMPLEMENTED; - } - #endif -} - -MA_API ma_result ma_flac_init_file(const char* pFilePath, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_flac* pFlac) -{ - ma_result result; - - result = ma_flac_init_internal(pConfig, pFlac); - if (result != MA_SUCCESS) { - return result; - } - - #if !defined(MA_NO_FLAC) - { - pFlac->dr = ma_dr_flac_open_file(pFilePath, pAllocationCallbacks); - if (pFlac->dr == NULL) { - return MA_INVALID_FILE; - } - - return MA_SUCCESS; - } - #else - { - /* flac is disabled. */ - (void)pFilePath; - (void)pAllocationCallbacks; - return MA_NOT_IMPLEMENTED; - } - #endif -} - -MA_API ma_result ma_flac_init_file_w(const wchar_t* pFilePath, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_flac* pFlac) -{ - ma_result result; - - result = ma_flac_init_internal(pConfig, pFlac); - if (result != MA_SUCCESS) { - return result; - } - - #if !defined(MA_NO_FLAC) - { - pFlac->dr = ma_dr_flac_open_file_w(pFilePath, pAllocationCallbacks); - if (pFlac->dr == NULL) { - return MA_INVALID_FILE; - } - - return MA_SUCCESS; - } - #else - { - /* flac is disabled. */ - (void)pFilePath; - (void)pAllocationCallbacks; - return MA_NOT_IMPLEMENTED; - } - #endif -} - -MA_API ma_result ma_flac_init_memory(const void* pData, size_t dataSize, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_flac* pFlac) -{ - ma_result result; - - result = ma_flac_init_internal(pConfig, pFlac); - if (result != MA_SUCCESS) { - return result; - } - - #if !defined(MA_NO_FLAC) - { - pFlac->dr = ma_dr_flac_open_memory(pData, dataSize, pAllocationCallbacks); - if (pFlac->dr == NULL) { - return MA_INVALID_FILE; - } - - return MA_SUCCESS; - } - #else - { - /* flac is disabled. */ - (void)pData; - (void)dataSize; - (void)pAllocationCallbacks; - return MA_NOT_IMPLEMENTED; - } - #endif -} - -MA_API void ma_flac_uninit(ma_flac* pFlac, const ma_allocation_callbacks* pAllocationCallbacks) -{ - if (pFlac == NULL) { - return; - } - - (void)pAllocationCallbacks; - - #if !defined(MA_NO_FLAC) - { - ma_dr_flac_close(pFlac->dr); - } - #else - { - /* flac is disabled. Should never hit this since initialization would have failed. */ - MA_ASSERT(MA_FALSE); - } - #endif - - ma_data_source_uninit(&pFlac->ds); -} - -MA_API ma_result ma_flac_read_pcm_frames(ma_flac* pFlac, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead) -{ - if (pFramesRead != NULL) { - *pFramesRead = 0; - } - - if (frameCount == 0) { - return MA_INVALID_ARGS; - } - - if (pFlac == NULL) { - return MA_INVALID_ARGS; - } - - #if !defined(MA_NO_FLAC) - { - /* We always use floating point format. */ - ma_result result = MA_SUCCESS; /* Must be initialized to MA_SUCCESS. */ - ma_uint64 totalFramesRead = 0; - ma_format format; - - ma_flac_get_data_format(pFlac, &format, NULL, NULL, NULL, 0); - - switch (format) - { - case ma_format_f32: - { - totalFramesRead = ma_dr_flac_read_pcm_frames_f32(pFlac->dr, frameCount, (float*)pFramesOut); - } break; - - case ma_format_s16: - { - totalFramesRead = ma_dr_flac_read_pcm_frames_s16(pFlac->dr, frameCount, (ma_int16*)pFramesOut); - } break; - - case ma_format_s32: - { - totalFramesRead = ma_dr_flac_read_pcm_frames_s32(pFlac->dr, frameCount, (ma_int32*)pFramesOut); - } break; - - case ma_format_u8: - case ma_format_s24: - case ma_format_unknown: - default: - { - return MA_INVALID_OPERATION; - }; - } - - /* In the future we'll update ma_dr_flac to return MA_AT_END for us. */ - if (totalFramesRead == 0) { - result = MA_AT_END; - } - - if (pFramesRead != NULL) { - *pFramesRead = totalFramesRead; - } - - if (result == MA_SUCCESS && totalFramesRead == 0) { - result = MA_AT_END; - } - - return result; - } - #else - { - /* flac is disabled. Should never hit this since initialization would have failed. */ - MA_ASSERT(MA_FALSE); - - (void)pFramesOut; - (void)frameCount; - (void)pFramesRead; - - return MA_NOT_IMPLEMENTED; - } - #endif -} - -MA_API ma_result ma_flac_seek_to_pcm_frame(ma_flac* pFlac, ma_uint64 frameIndex) -{ - if (pFlac == NULL) { - return MA_INVALID_ARGS; - } - - #if !defined(MA_NO_FLAC) - { - ma_bool32 flacResult; - - flacResult = ma_dr_flac_seek_to_pcm_frame(pFlac->dr, frameIndex); - if (flacResult != MA_TRUE) { - return MA_ERROR; - } - - return MA_SUCCESS; - } - #else - { - /* flac is disabled. Should never hit this since initialization would have failed. */ - MA_ASSERT(MA_FALSE); - - (void)frameIndex; - - return MA_NOT_IMPLEMENTED; - } - #endif -} - -MA_API ma_result ma_flac_get_data_format(ma_flac* pFlac, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate, ma_channel* pChannelMap, size_t channelMapCap) -{ - /* Defaults for safety. */ - if (pFormat != NULL) { - *pFormat = ma_format_unknown; - } - if (pChannels != NULL) { - *pChannels = 0; - } - if (pSampleRate != NULL) { - *pSampleRate = 0; - } - if (pChannelMap != NULL) { - MA_ZERO_MEMORY(pChannelMap, sizeof(*pChannelMap) * channelMapCap); - } - - if (pFlac == NULL) { - return MA_INVALID_OPERATION; - } - - if (pFormat != NULL) { - *pFormat = pFlac->format; - } - - #if !defined(MA_NO_FLAC) - { - if (pChannels != NULL) { - *pChannels = pFlac->dr->channels; - } - - if (pSampleRate != NULL) { - *pSampleRate = pFlac->dr->sampleRate; - } - - if (pChannelMap != NULL) { - ma_channel_map_init_standard(ma_standard_channel_map_microsoft, pChannelMap, channelMapCap, pFlac->dr->channels); - } - - return MA_SUCCESS; - } - #else - { - /* flac is disabled. Should never hit this since initialization would have failed. */ - MA_ASSERT(MA_FALSE); - return MA_NOT_IMPLEMENTED; - } - #endif -} - -MA_API ma_result ma_flac_get_cursor_in_pcm_frames(ma_flac* pFlac, ma_uint64* pCursor) -{ - if (pCursor == NULL) { - return MA_INVALID_ARGS; - } - - *pCursor = 0; /* Safety. */ - - if (pFlac == NULL) { - return MA_INVALID_ARGS; - } - - #if !defined(MA_NO_FLAC) - { - *pCursor = pFlac->dr->currentPCMFrame; - - return MA_SUCCESS; - } - #else - { - /* flac is disabled. Should never hit this since initialization would have failed. */ - MA_ASSERT(MA_FALSE); - return MA_NOT_IMPLEMENTED; - } - #endif -} - -MA_API ma_result ma_flac_get_length_in_pcm_frames(ma_flac* pFlac, ma_uint64* pLength) -{ - if (pLength == NULL) { - return MA_INVALID_ARGS; - } - - *pLength = 0; /* Safety. */ - - if (pFlac == NULL) { - return MA_INVALID_ARGS; - } - - #if !defined(MA_NO_FLAC) - { - *pLength = pFlac->dr->totalPCMFrameCount; - - return MA_SUCCESS; - } - #else - { - /* flac is disabled. Should never hit this since initialization would have failed. */ - MA_ASSERT(MA_FALSE); - return MA_NOT_IMPLEMENTED; - } - #endif -} - - -static ma_result ma_decoding_backend_init__flac(void* pUserData, ma_read_proc onRead, ma_seek_proc onSeek, ma_tell_proc onTell, void* pReadSeekTellUserData, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_data_source** ppBackend) -{ - ma_result result; - ma_flac* pFlac; - - (void)pUserData; /* For now not using pUserData, but once we start storing the vorbis decoder state within the ma_decoder structure this will be set to the decoder so we can avoid a malloc. */ - - /* For now we're just allocating the decoder backend on the heap. */ - pFlac = (ma_flac*)ma_malloc(sizeof(*pFlac), pAllocationCallbacks); - if (pFlac == NULL) { - return MA_OUT_OF_MEMORY; - } - - result = ma_flac_init(onRead, onSeek, onTell, pReadSeekTellUserData, pConfig, pAllocationCallbacks, pFlac); - if (result != MA_SUCCESS) { - ma_free(pFlac, pAllocationCallbacks); - return result; - } - - *ppBackend = pFlac; - - return MA_SUCCESS; -} - -static ma_result ma_decoding_backend_init_file__flac(void* pUserData, const char* pFilePath, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_data_source** ppBackend) -{ - ma_result result; - ma_flac* pFlac; - - (void)pUserData; /* For now not using pUserData, but once we start storing the vorbis decoder state within the ma_decoder structure this will be set to the decoder so we can avoid a malloc. */ - - /* For now we're just allocating the decoder backend on the heap. */ - pFlac = (ma_flac*)ma_malloc(sizeof(*pFlac), pAllocationCallbacks); - if (pFlac == NULL) { - return MA_OUT_OF_MEMORY; - } - - result = ma_flac_init_file(pFilePath, pConfig, pAllocationCallbacks, pFlac); - if (result != MA_SUCCESS) { - ma_free(pFlac, pAllocationCallbacks); - return result; - } - - *ppBackend = pFlac; - - return MA_SUCCESS; -} - -static ma_result ma_decoding_backend_init_file_w__flac(void* pUserData, const wchar_t* pFilePath, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_data_source** ppBackend) -{ - ma_result result; - ma_flac* pFlac; - - (void)pUserData; /* For now not using pUserData, but once we start storing the vorbis decoder state within the ma_decoder structure this will be set to the decoder so we can avoid a malloc. */ - - /* For now we're just allocating the decoder backend on the heap. */ - pFlac = (ma_flac*)ma_malloc(sizeof(*pFlac), pAllocationCallbacks); - if (pFlac == NULL) { - return MA_OUT_OF_MEMORY; - } - - result = ma_flac_init_file_w(pFilePath, pConfig, pAllocationCallbacks, pFlac); - if (result != MA_SUCCESS) { - ma_free(pFlac, pAllocationCallbacks); - return result; - } - - *ppBackend = pFlac; - - return MA_SUCCESS; -} - -static ma_result ma_decoding_backend_init_memory__flac(void* pUserData, const void* pData, size_t dataSize, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_data_source** ppBackend) -{ - ma_result result; - ma_flac* pFlac; - - (void)pUserData; /* For now not using pUserData, but once we start storing the vorbis decoder state within the ma_decoder structure this will be set to the decoder so we can avoid a malloc. */ - - /* For now we're just allocating the decoder backend on the heap. */ - pFlac = (ma_flac*)ma_malloc(sizeof(*pFlac), pAllocationCallbacks); - if (pFlac == NULL) { - return MA_OUT_OF_MEMORY; - } - - result = ma_flac_init_memory(pData, dataSize, pConfig, pAllocationCallbacks, pFlac); - if (result != MA_SUCCESS) { - ma_free(pFlac, pAllocationCallbacks); - return result; - } - - *ppBackend = pFlac; - - return MA_SUCCESS; -} - -static void ma_decoding_backend_uninit__flac(void* pUserData, ma_data_source* pBackend, const ma_allocation_callbacks* pAllocationCallbacks) -{ - ma_flac* pFlac = (ma_flac*)pBackend; - - (void)pUserData; - - ma_flac_uninit(pFlac, pAllocationCallbacks); - ma_free(pFlac, pAllocationCallbacks); -} - -static ma_decoding_backend_vtable g_ma_decoding_backend_vtable_flac = -{ - ma_decoding_backend_init__flac, - ma_decoding_backend_init_file__flac, - ma_decoding_backend_init_file_w__flac, - ma_decoding_backend_init_memory__flac, - ma_decoding_backend_uninit__flac -}; - -static ma_result ma_decoder_init_flac__internal(const ma_decoder_config* pConfig, ma_decoder* pDecoder) -{ - return ma_decoder_init_from_vtable__internal(&g_ma_decoding_backend_vtable_flac, NULL, pConfig, pDecoder); -} - -static ma_result ma_decoder_init_flac_from_file__internal(const char* pFilePath, const ma_decoder_config* pConfig, ma_decoder* pDecoder) -{ - return ma_decoder_init_from_file__internal(&g_ma_decoding_backend_vtable_flac, NULL, pFilePath, pConfig, pDecoder); -} - -static ma_result ma_decoder_init_flac_from_file_w__internal(const wchar_t* pFilePath, const ma_decoder_config* pConfig, ma_decoder* pDecoder) -{ - return ma_decoder_init_from_file_w__internal(&g_ma_decoding_backend_vtable_flac, NULL, pFilePath, pConfig, pDecoder); -} - -static ma_result ma_decoder_init_flac_from_memory__internal(const void* pData, size_t dataSize, const ma_decoder_config* pConfig, ma_decoder* pDecoder) -{ - return ma_decoder_init_from_memory__internal(&g_ma_decoding_backend_vtable_flac, NULL, pData, dataSize, pConfig, pDecoder); -} -#endif /* ma_dr_flac_h */ - -/* MP3 */ -#ifdef ma_dr_mp3_h -#define MA_HAS_MP3 - -typedef struct -{ - ma_data_source_base ds; - ma_read_proc onRead; - ma_seek_proc onSeek; - ma_tell_proc onTell; - void* pReadSeekTellUserData; - ma_format format; /* Can be f32 or s16. */ -#if !defined(MA_NO_MP3) - ma_dr_mp3 dr; - ma_uint32 seekPointCount; - ma_dr_mp3_seek_point* pSeekPoints; /* Only used if seek table generation is used. */ -#endif -} ma_mp3; - -MA_API ma_result ma_mp3_init(ma_read_proc onRead, ma_seek_proc onSeek, ma_tell_proc onTell, void* pReadSeekTellUserData, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_mp3* pMP3); -MA_API ma_result ma_mp3_init_file(const char* pFilePath, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_mp3* pMP3); -MA_API ma_result ma_mp3_init_file_w(const wchar_t* pFilePath, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_mp3* pMP3); -MA_API ma_result ma_mp3_init_memory(const void* pData, size_t dataSize, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_mp3* pMP3); -MA_API void ma_mp3_uninit(ma_mp3* pMP3, const ma_allocation_callbacks* pAllocationCallbacks); -MA_API ma_result ma_mp3_read_pcm_frames(ma_mp3* pMP3, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead); -MA_API ma_result ma_mp3_seek_to_pcm_frame(ma_mp3* pMP3, ma_uint64 frameIndex); -MA_API ma_result ma_mp3_get_data_format(ma_mp3* pMP3, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate, ma_channel* pChannelMap, size_t channelMapCap); -MA_API ma_result ma_mp3_get_cursor_in_pcm_frames(ma_mp3* pMP3, ma_uint64* pCursor); -MA_API ma_result ma_mp3_get_length_in_pcm_frames(ma_mp3* pMP3, ma_uint64* pLength); - - -static ma_result ma_mp3_ds_read(ma_data_source* pDataSource, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead) -{ - return ma_mp3_read_pcm_frames((ma_mp3*)pDataSource, pFramesOut, frameCount, pFramesRead); -} - -static ma_result ma_mp3_ds_seek(ma_data_source* pDataSource, ma_uint64 frameIndex) -{ - return ma_mp3_seek_to_pcm_frame((ma_mp3*)pDataSource, frameIndex); -} - -static ma_result ma_mp3_ds_get_data_format(ma_data_source* pDataSource, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate, ma_channel* pChannelMap, size_t channelMapCap) -{ - return ma_mp3_get_data_format((ma_mp3*)pDataSource, pFormat, pChannels, pSampleRate, pChannelMap, channelMapCap); -} - -static ma_result ma_mp3_ds_get_cursor(ma_data_source* pDataSource, ma_uint64* pCursor) -{ - return ma_mp3_get_cursor_in_pcm_frames((ma_mp3*)pDataSource, pCursor); -} - -static ma_result ma_mp3_ds_get_length(ma_data_source* pDataSource, ma_uint64* pLength) -{ - return ma_mp3_get_length_in_pcm_frames((ma_mp3*)pDataSource, pLength); -} - -static ma_data_source_vtable g_ma_mp3_ds_vtable = -{ - ma_mp3_ds_read, - ma_mp3_ds_seek, - ma_mp3_ds_get_data_format, - ma_mp3_ds_get_cursor, - ma_mp3_ds_get_length, - NULL, /* onSetLooping */ - 0 -}; - - -#if !defined(MA_NO_MP3) -static size_t ma_mp3_dr_callback__read(void* pUserData, void* pBufferOut, size_t bytesToRead) -{ - ma_mp3* pMP3 = (ma_mp3*)pUserData; - ma_result result; - size_t bytesRead; - - MA_ASSERT(pMP3 != NULL); - - result = pMP3->onRead(pMP3->pReadSeekTellUserData, pBufferOut, bytesToRead, &bytesRead); - (void)result; - - return bytesRead; -} - -static ma_bool32 ma_mp3_dr_callback__seek(void* pUserData, int offset, ma_dr_mp3_seek_origin origin) -{ - ma_mp3* pMP3 = (ma_mp3*)pUserData; - ma_result result; - ma_seek_origin maSeekOrigin; - - MA_ASSERT(pMP3 != NULL); - - maSeekOrigin = ma_seek_origin_start; - if (origin == ma_dr_mp3_seek_origin_current) { - maSeekOrigin = ma_seek_origin_current; - } - - result = pMP3->onSeek(pMP3->pReadSeekTellUserData, offset, maSeekOrigin); - if (result != MA_SUCCESS) { - return MA_FALSE; - } - - return MA_TRUE; -} -#endif - -static ma_result ma_mp3_init_internal(const ma_decoding_backend_config* pConfig, ma_mp3* pMP3) -{ - ma_result result; - ma_data_source_config dataSourceConfig; - - if (pMP3 == NULL) { - return MA_INVALID_ARGS; - } - - MA_ZERO_OBJECT(pMP3); - pMP3->format = ma_format_f32; /* f32 by default. */ - - if (pConfig != NULL && (pConfig->preferredFormat == ma_format_f32 || pConfig->preferredFormat == ma_format_s16)) { - pMP3->format = pConfig->preferredFormat; - } else { - /* Getting here means something other than f32 and s16 was specified. Just leave this unset to use the default format. */ - } - - dataSourceConfig = ma_data_source_config_init(); - dataSourceConfig.vtable = &g_ma_mp3_ds_vtable; - - result = ma_data_source_init(&dataSourceConfig, &pMP3->ds); - if (result != MA_SUCCESS) { - return result; /* Failed to initialize the base data source. */ - } - - return MA_SUCCESS; -} - -static ma_result ma_mp3_generate_seek_table(ma_mp3* pMP3, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks) -{ - ma_bool32 mp3Result; - ma_uint32 seekPointCount = 0; - ma_dr_mp3_seek_point* pSeekPoints = NULL; - - MA_ASSERT(pMP3 != NULL); - MA_ASSERT(pConfig != NULL); - - seekPointCount = pConfig->seekPointCount; - if (seekPointCount > 0) { - pSeekPoints = (ma_dr_mp3_seek_point*)ma_malloc(sizeof(*pMP3->pSeekPoints) * seekPointCount, pAllocationCallbacks); - if (pSeekPoints == NULL) { - return MA_OUT_OF_MEMORY; - } - } - - mp3Result = ma_dr_mp3_calculate_seek_points(&pMP3->dr, &seekPointCount, pSeekPoints); - if (mp3Result != MA_TRUE) { - ma_free(pSeekPoints, pAllocationCallbacks); - return MA_ERROR; - } - - mp3Result = ma_dr_mp3_bind_seek_table(&pMP3->dr, seekPointCount, pSeekPoints); - if (mp3Result != MA_TRUE) { - ma_free(pSeekPoints, pAllocationCallbacks); - return MA_ERROR; - } - - pMP3->seekPointCount = seekPointCount; - pMP3->pSeekPoints = pSeekPoints; - - return MA_SUCCESS; -} - -static ma_result ma_mp3_post_init(ma_mp3* pMP3, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks) -{ - ma_result result; - - result = ma_mp3_generate_seek_table(pMP3, pConfig, pAllocationCallbacks); - if (result != MA_SUCCESS) { - return result; - } - - return MA_SUCCESS; -} - -MA_API ma_result ma_mp3_init(ma_read_proc onRead, ma_seek_proc onSeek, ma_tell_proc onTell, void* pReadSeekTellUserData, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_mp3* pMP3) -{ - ma_result result; - - result = ma_mp3_init_internal(pConfig, pMP3); - if (result != MA_SUCCESS) { - return result; - } - - if (onRead == NULL || onSeek == NULL) { - return MA_INVALID_ARGS; /* onRead and onSeek are mandatory. */ - } - - pMP3->onRead = onRead; - pMP3->onSeek = onSeek; - pMP3->onTell = onTell; - pMP3->pReadSeekTellUserData = pReadSeekTellUserData; - - #if !defined(MA_NO_MP3) - { - ma_bool32 mp3Result; - - mp3Result = ma_dr_mp3_init(&pMP3->dr, ma_mp3_dr_callback__read, ma_mp3_dr_callback__seek, pMP3, pAllocationCallbacks); - if (mp3Result != MA_TRUE) { - return MA_INVALID_FILE; - } - - ma_mp3_post_init(pMP3, pConfig, pAllocationCallbacks); - - return MA_SUCCESS; - } - #else - { - /* mp3 is disabled. */ - (void)pAllocationCallbacks; - return MA_NOT_IMPLEMENTED; - } - #endif -} - -MA_API ma_result ma_mp3_init_file(const char* pFilePath, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_mp3* pMP3) -{ - ma_result result; - - result = ma_mp3_init_internal(pConfig, pMP3); - if (result != MA_SUCCESS) { - return result; - } - - #if !defined(MA_NO_MP3) - { - ma_bool32 mp3Result; - - mp3Result = ma_dr_mp3_init_file(&pMP3->dr, pFilePath, pAllocationCallbacks); - if (mp3Result != MA_TRUE) { - return MA_INVALID_FILE; - } - - ma_mp3_post_init(pMP3, pConfig, pAllocationCallbacks); - - return MA_SUCCESS; - } - #else - { - /* mp3 is disabled. */ - (void)pFilePath; - (void)pAllocationCallbacks; - return MA_NOT_IMPLEMENTED; - } - #endif -} - -MA_API ma_result ma_mp3_init_file_w(const wchar_t* pFilePath, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_mp3* pMP3) -{ - ma_result result; - - result = ma_mp3_init_internal(pConfig, pMP3); - if (result != MA_SUCCESS) { - return result; - } - - #if !defined(MA_NO_MP3) - { - ma_bool32 mp3Result; - - mp3Result = ma_dr_mp3_init_file_w(&pMP3->dr, pFilePath, pAllocationCallbacks); - if (mp3Result != MA_TRUE) { - return MA_INVALID_FILE; - } - - ma_mp3_post_init(pMP3, pConfig, pAllocationCallbacks); - - return MA_SUCCESS; - } - #else - { - /* mp3 is disabled. */ - (void)pFilePath; - (void)pAllocationCallbacks; - return MA_NOT_IMPLEMENTED; - } - #endif -} - -MA_API ma_result ma_mp3_init_memory(const void* pData, size_t dataSize, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_mp3* pMP3) -{ - ma_result result; - - result = ma_mp3_init_internal(pConfig, pMP3); - if (result != MA_SUCCESS) { - return result; - } - - #if !defined(MA_NO_MP3) - { - ma_bool32 mp3Result; - - mp3Result = ma_dr_mp3_init_memory(&pMP3->dr, pData, dataSize, pAllocationCallbacks); - if (mp3Result != MA_TRUE) { - return MA_INVALID_FILE; - } - - ma_mp3_post_init(pMP3, pConfig, pAllocationCallbacks); - - return MA_SUCCESS; - } - #else - { - /* mp3 is disabled. */ - (void)pData; - (void)dataSize; - (void)pAllocationCallbacks; - return MA_NOT_IMPLEMENTED; - } - #endif -} - -MA_API void ma_mp3_uninit(ma_mp3* pMP3, const ma_allocation_callbacks* pAllocationCallbacks) -{ - if (pMP3 == NULL) { - return; - } - - #if !defined(MA_NO_MP3) - { - ma_dr_mp3_uninit(&pMP3->dr); - } - #else - { - /* mp3 is disabled. Should never hit this since initialization would have failed. */ - MA_ASSERT(MA_FALSE); - } - #endif - - /* Seek points need to be freed after the MP3 decoder has been uninitialized to ensure they're no longer being referenced. */ - ma_free(pMP3->pSeekPoints, pAllocationCallbacks); - - ma_data_source_uninit(&pMP3->ds); -} - -MA_API ma_result ma_mp3_read_pcm_frames(ma_mp3* pMP3, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead) -{ - if (pFramesRead != NULL) { - *pFramesRead = 0; - } - - if (frameCount == 0) { - return MA_INVALID_ARGS; - } - - if (pMP3 == NULL) { - return MA_INVALID_ARGS; - } - - #if !defined(MA_NO_MP3) - { - /* We always use floating point format. */ - ma_result result = MA_SUCCESS; /* Must be initialized to MA_SUCCESS. */ - ma_uint64 totalFramesRead = 0; - ma_format format; - - ma_mp3_get_data_format(pMP3, &format, NULL, NULL, NULL, 0); - - switch (format) - { - case ma_format_f32: - { - totalFramesRead = ma_dr_mp3_read_pcm_frames_f32(&pMP3->dr, frameCount, (float*)pFramesOut); - } break; - - case ma_format_s16: - { - totalFramesRead = ma_dr_mp3_read_pcm_frames_s16(&pMP3->dr, frameCount, (ma_int16*)pFramesOut); - } break; - - case ma_format_u8: - case ma_format_s24: - case ma_format_s32: - case ma_format_unknown: - default: - { - return MA_INVALID_OPERATION; - }; - } - - /* In the future we'll update ma_dr_mp3 to return MA_AT_END for us. */ - if (totalFramesRead == 0) { - result = MA_AT_END; - } - - if (pFramesRead != NULL) { - *pFramesRead = totalFramesRead; - } - - return result; - } - #else - { - /* mp3 is disabled. Should never hit this since initialization would have failed. */ - MA_ASSERT(MA_FALSE); - - (void)pFramesOut; - (void)frameCount; - (void)pFramesRead; - - return MA_NOT_IMPLEMENTED; - } - #endif -} - -MA_API ma_result ma_mp3_seek_to_pcm_frame(ma_mp3* pMP3, ma_uint64 frameIndex) -{ - if (pMP3 == NULL) { - return MA_INVALID_ARGS; - } - - #if !defined(MA_NO_MP3) - { - ma_bool32 mp3Result; - - mp3Result = ma_dr_mp3_seek_to_pcm_frame(&pMP3->dr, frameIndex); - if (mp3Result != MA_TRUE) { - return MA_ERROR; - } - - return MA_SUCCESS; - } - #else - { - /* mp3 is disabled. Should never hit this since initialization would have failed. */ - MA_ASSERT(MA_FALSE); - - (void)frameIndex; - - return MA_NOT_IMPLEMENTED; - } - #endif -} - -MA_API ma_result ma_mp3_get_data_format(ma_mp3* pMP3, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate, ma_channel* pChannelMap, size_t channelMapCap) -{ - /* Defaults for safety. */ - if (pFormat != NULL) { - *pFormat = ma_format_unknown; - } - if (pChannels != NULL) { - *pChannels = 0; - } - if (pSampleRate != NULL) { - *pSampleRate = 0; - } - if (pChannelMap != NULL) { - MA_ZERO_MEMORY(pChannelMap, sizeof(*pChannelMap) * channelMapCap); - } - - if (pMP3 == NULL) { - return MA_INVALID_OPERATION; - } - - if (pFormat != NULL) { - *pFormat = pMP3->format; - } - - #if !defined(MA_NO_MP3) - { - if (pChannels != NULL) { - *pChannels = pMP3->dr.channels; - } - - if (pSampleRate != NULL) { - *pSampleRate = pMP3->dr.sampleRate; - } - - if (pChannelMap != NULL) { - ma_channel_map_init_standard(ma_standard_channel_map_default, pChannelMap, channelMapCap, pMP3->dr.channels); - } - - return MA_SUCCESS; - } - #else - { - /* mp3 is disabled. Should never hit this since initialization would have failed. */ - MA_ASSERT(MA_FALSE); - return MA_NOT_IMPLEMENTED; - } - #endif -} - -MA_API ma_result ma_mp3_get_cursor_in_pcm_frames(ma_mp3* pMP3, ma_uint64* pCursor) -{ - if (pCursor == NULL) { - return MA_INVALID_ARGS; - } - - *pCursor = 0; /* Safety. */ - - if (pMP3 == NULL) { - return MA_INVALID_ARGS; - } - - #if !defined(MA_NO_MP3) - { - *pCursor = pMP3->dr.currentPCMFrame; - - return MA_SUCCESS; - } - #else - { - /* mp3 is disabled. Should never hit this since initialization would have failed. */ - MA_ASSERT(MA_FALSE); - return MA_NOT_IMPLEMENTED; - } - #endif -} - -MA_API ma_result ma_mp3_get_length_in_pcm_frames(ma_mp3* pMP3, ma_uint64* pLength) -{ - if (pLength == NULL) { - return MA_INVALID_ARGS; - } - - *pLength = 0; /* Safety. */ - - if (pMP3 == NULL) { - return MA_INVALID_ARGS; - } - - #if !defined(MA_NO_MP3) - { - *pLength = ma_dr_mp3_get_pcm_frame_count(&pMP3->dr); - - return MA_SUCCESS; - } - #else - { - /* mp3 is disabled. Should never hit this since initialization would have failed. */ - MA_ASSERT(MA_FALSE); - return MA_NOT_IMPLEMENTED; - } - #endif -} - - -static ma_result ma_decoding_backend_init__mp3(void* pUserData, ma_read_proc onRead, ma_seek_proc onSeek, ma_tell_proc onTell, void* pReadSeekTellUserData, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_data_source** ppBackend) -{ - ma_result result; - ma_mp3* pMP3; - - (void)pUserData; /* For now not using pUserData, but once we start storing the vorbis decoder state within the ma_decoder structure this will be set to the decoder so we can avoid a malloc. */ - - /* For now we're just allocating the decoder backend on the heap. */ - pMP3 = (ma_mp3*)ma_malloc(sizeof(*pMP3), pAllocationCallbacks); - if (pMP3 == NULL) { - return MA_OUT_OF_MEMORY; - } - - result = ma_mp3_init(onRead, onSeek, onTell, pReadSeekTellUserData, pConfig, pAllocationCallbacks, pMP3); - if (result != MA_SUCCESS) { - ma_free(pMP3, pAllocationCallbacks); - return result; - } - - *ppBackend = pMP3; - - return MA_SUCCESS; -} - -static ma_result ma_decoding_backend_init_file__mp3(void* pUserData, const char* pFilePath, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_data_source** ppBackend) -{ - ma_result result; - ma_mp3* pMP3; - - (void)pUserData; /* For now not using pUserData, but once we start storing the vorbis decoder state within the ma_decoder structure this will be set to the decoder so we can avoid a malloc. */ - - /* For now we're just allocating the decoder backend on the heap. */ - pMP3 = (ma_mp3*)ma_malloc(sizeof(*pMP3), pAllocationCallbacks); - if (pMP3 == NULL) { - return MA_OUT_OF_MEMORY; - } - - result = ma_mp3_init_file(pFilePath, pConfig, pAllocationCallbacks, pMP3); - if (result != MA_SUCCESS) { - ma_free(pMP3, pAllocationCallbacks); - return result; - } - - *ppBackend = pMP3; - - return MA_SUCCESS; -} - -static ma_result ma_decoding_backend_init_file_w__mp3(void* pUserData, const wchar_t* pFilePath, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_data_source** ppBackend) -{ - ma_result result; - ma_mp3* pMP3; - - (void)pUserData; /* For now not using pUserData, but once we start storing the vorbis decoder state within the ma_decoder structure this will be set to the decoder so we can avoid a malloc. */ - - /* For now we're just allocating the decoder backend on the heap. */ - pMP3 = (ma_mp3*)ma_malloc(sizeof(*pMP3), pAllocationCallbacks); - if (pMP3 == NULL) { - return MA_OUT_OF_MEMORY; - } - - result = ma_mp3_init_file_w(pFilePath, pConfig, pAllocationCallbacks, pMP3); - if (result != MA_SUCCESS) { - ma_free(pMP3, pAllocationCallbacks); - return result; - } - - *ppBackend = pMP3; - - return MA_SUCCESS; -} - -static ma_result ma_decoding_backend_init_memory__mp3(void* pUserData, const void* pData, size_t dataSize, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_data_source** ppBackend) -{ - ma_result result; - ma_mp3* pMP3; - - (void)pUserData; /* For now not using pUserData, but once we start storing the vorbis decoder state within the ma_decoder structure this will be set to the decoder so we can avoid a malloc. */ - - /* For now we're just allocating the decoder backend on the heap. */ - pMP3 = (ma_mp3*)ma_malloc(sizeof(*pMP3), pAllocationCallbacks); - if (pMP3 == NULL) { - return MA_OUT_OF_MEMORY; - } - - result = ma_mp3_init_memory(pData, dataSize, pConfig, pAllocationCallbacks, pMP3); - if (result != MA_SUCCESS) { - ma_free(pMP3, pAllocationCallbacks); - return result; - } - - *ppBackend = pMP3; - - return MA_SUCCESS; -} - -static void ma_decoding_backend_uninit__mp3(void* pUserData, ma_data_source* pBackend, const ma_allocation_callbacks* pAllocationCallbacks) -{ - ma_mp3* pMP3 = (ma_mp3*)pBackend; - - (void)pUserData; - - ma_mp3_uninit(pMP3, pAllocationCallbacks); - ma_free(pMP3, pAllocationCallbacks); -} - -static ma_decoding_backend_vtable g_ma_decoding_backend_vtable_mp3 = -{ - ma_decoding_backend_init__mp3, - ma_decoding_backend_init_file__mp3, - ma_decoding_backend_init_file_w__mp3, - ma_decoding_backend_init_memory__mp3, - ma_decoding_backend_uninit__mp3 -}; - -static ma_result ma_decoder_init_mp3__internal(const ma_decoder_config* pConfig, ma_decoder* pDecoder) -{ - return ma_decoder_init_from_vtable__internal(&g_ma_decoding_backend_vtable_mp3, NULL, pConfig, pDecoder); -} - -static ma_result ma_decoder_init_mp3_from_file__internal(const char* pFilePath, const ma_decoder_config* pConfig, ma_decoder* pDecoder) -{ - return ma_decoder_init_from_file__internal(&g_ma_decoding_backend_vtable_mp3, NULL, pFilePath, pConfig, pDecoder); -} - -static ma_result ma_decoder_init_mp3_from_file_w__internal(const wchar_t* pFilePath, const ma_decoder_config* pConfig, ma_decoder* pDecoder) -{ - return ma_decoder_init_from_file_w__internal(&g_ma_decoding_backend_vtable_mp3, NULL, pFilePath, pConfig, pDecoder); -} - -static ma_result ma_decoder_init_mp3_from_memory__internal(const void* pData, size_t dataSize, const ma_decoder_config* pConfig, ma_decoder* pDecoder) -{ - return ma_decoder_init_from_memory__internal(&g_ma_decoding_backend_vtable_mp3, NULL, pData, dataSize, pConfig, pDecoder); -} -#endif /* ma_dr_mp3_h */ - -/* Vorbis */ -#ifdef STB_VORBIS_INCLUDE_STB_VORBIS_H -#define MA_HAS_VORBIS - -/* The size in bytes of each chunk of data to read from the Vorbis stream. */ -#define MA_VORBIS_DATA_CHUNK_SIZE 4096 - -typedef struct -{ - ma_data_source_base ds; - ma_read_proc onRead; - ma_seek_proc onSeek; - ma_tell_proc onTell; - void* pReadSeekTellUserData; - ma_allocation_callbacks allocationCallbacks; /* Store the allocation callbacks within the structure because we may need to dynamically expand a buffer in ma_stbvorbis_read_pcm_frames() when using push mode. */ - ma_format format; /* Only f32 is allowed with stb_vorbis. */ - ma_uint32 channels; - ma_uint32 sampleRate; - ma_uint64 cursor; -#if !defined(MA_NO_VORBIS) - stb_vorbis* stb; - ma_bool32 usingPushMode; - struct - { - ma_uint8* pData; - size_t dataSize; - size_t dataCapacity; - size_t audioStartOffsetInBytes; - ma_uint32 framesConsumed; /* The number of frames consumed in ppPacketData. */ - ma_uint32 framesRemaining; /* The number of frames remaining in ppPacketData. */ - float** ppPacketData; - } push; -#endif -} ma_stbvorbis; - -MA_API ma_result ma_stbvorbis_init(ma_read_proc onRead, ma_seek_proc onSeek, ma_tell_proc onTell, void* pReadSeekTellUserData, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_stbvorbis* pVorbis); -MA_API ma_result ma_stbvorbis_init_file(const char* pFilePath, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_stbvorbis* pVorbis); -MA_API ma_result ma_stbvorbis_init_memory(const void* pData, size_t dataSize, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_stbvorbis* pVorbis); -MA_API void ma_stbvorbis_uninit(ma_stbvorbis* pVorbis, const ma_allocation_callbacks* pAllocationCallbacks); -MA_API ma_result ma_stbvorbis_read_pcm_frames(ma_stbvorbis* pVorbis, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead); -MA_API ma_result ma_stbvorbis_seek_to_pcm_frame(ma_stbvorbis* pVorbis, ma_uint64 frameIndex); -MA_API ma_result ma_stbvorbis_get_data_format(ma_stbvorbis* pVorbis, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate, ma_channel* pChannelMap, size_t channelMapCap); -MA_API ma_result ma_stbvorbis_get_cursor_in_pcm_frames(ma_stbvorbis* pVorbis, ma_uint64* pCursor); -MA_API ma_result ma_stbvorbis_get_length_in_pcm_frames(ma_stbvorbis* pVorbis, ma_uint64* pLength); - - -static ma_result ma_stbvorbis_ds_read(ma_data_source* pDataSource, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead) -{ - return ma_stbvorbis_read_pcm_frames((ma_stbvorbis*)pDataSource, pFramesOut, frameCount, pFramesRead); -} - -static ma_result ma_stbvorbis_ds_seek(ma_data_source* pDataSource, ma_uint64 frameIndex) -{ - return ma_stbvorbis_seek_to_pcm_frame((ma_stbvorbis*)pDataSource, frameIndex); -} - -static ma_result ma_stbvorbis_ds_get_data_format(ma_data_source* pDataSource, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate, ma_channel* pChannelMap, size_t channelMapCap) -{ - return ma_stbvorbis_get_data_format((ma_stbvorbis*)pDataSource, pFormat, pChannels, pSampleRate, pChannelMap, channelMapCap); -} - -static ma_result ma_stbvorbis_ds_get_cursor(ma_data_source* pDataSource, ma_uint64* pCursor) -{ - return ma_stbvorbis_get_cursor_in_pcm_frames((ma_stbvorbis*)pDataSource, pCursor); -} - -static ma_result ma_stbvorbis_ds_get_length(ma_data_source* pDataSource, ma_uint64* pLength) -{ - return ma_stbvorbis_get_length_in_pcm_frames((ma_stbvorbis*)pDataSource, pLength); -} - -static ma_data_source_vtable g_ma_stbvorbis_ds_vtable = -{ - ma_stbvorbis_ds_read, - ma_stbvorbis_ds_seek, - ma_stbvorbis_ds_get_data_format, - ma_stbvorbis_ds_get_cursor, - ma_stbvorbis_ds_get_length, - NULL, /* onSetLooping */ - 0 -}; - - -static ma_result ma_stbvorbis_init_internal(const ma_decoding_backend_config* pConfig, ma_stbvorbis* pVorbis) -{ - ma_result result; - ma_data_source_config dataSourceConfig; - - (void)pConfig; - - if (pVorbis == NULL) { - return MA_INVALID_ARGS; - } - - MA_ZERO_OBJECT(pVorbis); - pVorbis->format = ma_format_f32; /* Only supporting f32. */ - - dataSourceConfig = ma_data_source_config_init(); - dataSourceConfig.vtable = &g_ma_stbvorbis_ds_vtable; - - result = ma_data_source_init(&dataSourceConfig, &pVorbis->ds); - if (result != MA_SUCCESS) { - return result; /* Failed to initialize the base data source. */ - } - - return MA_SUCCESS; -} - -#if !defined(MA_NO_VORBIS) -static ma_result ma_stbvorbis_post_init(ma_stbvorbis* pVorbis) -{ - stb_vorbis_info info; - - MA_ASSERT(pVorbis != NULL); - - info = stb_vorbis_get_info(pVorbis->stb); - - pVorbis->channels = info.channels; - pVorbis->sampleRate = info.sample_rate; - - return MA_SUCCESS; -} - -static ma_result ma_stbvorbis_init_internal_decoder_push(ma_stbvorbis* pVorbis) -{ - ma_result result; - stb_vorbis* stb; - size_t dataSize = 0; - size_t dataCapacity = 0; - ma_uint8* pData = NULL; /* <-- Must be initialized to NULL. */ - - for (;;) { - int vorbisError; - int consumedDataSize; /* <-- Fill by stb_vorbis_open_pushdata(). */ - size_t bytesRead; - ma_uint8* pNewData; - - /* Allocate memory for the new chunk. */ - dataCapacity += MA_VORBIS_DATA_CHUNK_SIZE; - pNewData = (ma_uint8*)ma_realloc(pData, dataCapacity, &pVorbis->allocationCallbacks); - if (pNewData == NULL) { - ma_free(pData, &pVorbis->allocationCallbacks); - return MA_OUT_OF_MEMORY; - } - - pData = pNewData; - - /* Read in the next chunk. */ - result = pVorbis->onRead(pVorbis->pReadSeekTellUserData, ma_offset_ptr(pData, dataSize), (dataCapacity - dataSize), &bytesRead); - dataSize += bytesRead; - - if (result != MA_SUCCESS) { - ma_free(pData, &pVorbis->allocationCallbacks); - return result; - } - - /* We have a maximum of 31 bits with stb_vorbis. */ - if (dataSize > INT_MAX) { - ma_free(pData, &pVorbis->allocationCallbacks); - return MA_TOO_BIG; - } - - stb = stb_vorbis_open_pushdata(pData, (int)dataSize, &consumedDataSize, &vorbisError, NULL); - if (stb != NULL) { - /* - Successfully opened the Vorbis decoder. We might have some leftover unprocessed - data so we'll need to move that down to the front. - */ - dataSize -= (size_t)consumedDataSize; /* Consume the data. */ - MA_MOVE_MEMORY(pData, ma_offset_ptr(pData, consumedDataSize), dataSize); - - /* - We need to track the start point so we can seek back to the start of the audio - data when seeking. - */ - pVorbis->push.audioStartOffsetInBytes = consumedDataSize; - - break; - } else { - /* Failed to open the decoder. */ - if (vorbisError == VORBIS_need_more_data) { - continue; - } else { - ma_free(pData, &pVorbis->allocationCallbacks); - return MA_ERROR; /* Failed to open the stb_vorbis decoder. */ - } - } - } - - MA_ASSERT(stb != NULL); - pVorbis->stb = stb; - pVorbis->push.pData = pData; - pVorbis->push.dataSize = dataSize; - pVorbis->push.dataCapacity = dataCapacity; - - return MA_SUCCESS; -} -#endif - -MA_API ma_result ma_stbvorbis_init(ma_read_proc onRead, ma_seek_proc onSeek, ma_tell_proc onTell, void* pReadSeekTellUserData, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_stbvorbis* pVorbis) -{ - ma_result result; - - result = ma_stbvorbis_init_internal(pConfig, pVorbis); - if (result != MA_SUCCESS) { - return result; - } - - if (onRead == NULL || onSeek == NULL) { - return MA_INVALID_ARGS; /* onRead and onSeek are mandatory. */ - } - - pVorbis->onRead = onRead; - pVorbis->onSeek = onSeek; - pVorbis->onTell = onTell; - pVorbis->pReadSeekTellUserData = pReadSeekTellUserData; - ma_allocation_callbacks_init_copy(&pVorbis->allocationCallbacks, pAllocationCallbacks); - - #if !defined(MA_NO_VORBIS) - { - /* - stb_vorbis lacks a callback based API for its pulling API which means we're stuck with the - pushing API. In order for us to be able to successfully initialize the decoder we need to - supply it with enough data. We need to keep loading data until we have enough. - */ - result = ma_stbvorbis_init_internal_decoder_push(pVorbis); - if (result != MA_SUCCESS) { - return result; - } - - pVorbis->usingPushMode = MA_TRUE; - - result = ma_stbvorbis_post_init(pVorbis); - if (result != MA_SUCCESS) { - stb_vorbis_close(pVorbis->stb); - ma_free(pVorbis->push.pData, pAllocationCallbacks); - return result; - } - - return MA_SUCCESS; - } - #else - { - /* vorbis is disabled. */ - (void)pAllocationCallbacks; - return MA_NOT_IMPLEMENTED; - } - #endif -} - -MA_API ma_result ma_stbvorbis_init_file(const char* pFilePath, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_stbvorbis* pVorbis) -{ - ma_result result; - - result = ma_stbvorbis_init_internal(pConfig, pVorbis); - if (result != MA_SUCCESS) { - return result; - } - - #if !defined(MA_NO_VORBIS) - { - (void)pAllocationCallbacks; /* Don't know how to make use of this with stb_vorbis. */ - - /* We can use stb_vorbis' pull mode for file based streams. */ - pVorbis->stb = stb_vorbis_open_filename(pFilePath, NULL, NULL); - if (pVorbis->stb == NULL) { - return MA_INVALID_FILE; - } - - pVorbis->usingPushMode = MA_FALSE; - - result = ma_stbvorbis_post_init(pVorbis); - if (result != MA_SUCCESS) { - stb_vorbis_close(pVorbis->stb); - return result; - } - - return MA_SUCCESS; - } - #else - { - /* vorbis is disabled. */ - (void)pFilePath; - (void)pAllocationCallbacks; - return MA_NOT_IMPLEMENTED; - } - #endif -} - -MA_API ma_result ma_stbvorbis_init_memory(const void* pData, size_t dataSize, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_stbvorbis* pVorbis) -{ - ma_result result; - - result = ma_stbvorbis_init_internal(pConfig, pVorbis); - if (result != MA_SUCCESS) { - return result; - } - - #if !defined(MA_NO_VORBIS) - { - (void)pAllocationCallbacks; - - /* stb_vorbis uses an int as its size specifier, restricting it to 32-bit even on 64-bit systems. *sigh*. */ - if (dataSize > INT_MAX) { - return MA_TOO_BIG; - } - - pVorbis->stb = stb_vorbis_open_memory((const unsigned char*)pData, (int)dataSize, NULL, NULL); - if (pVorbis->stb == NULL) { - return MA_INVALID_FILE; - } - - pVorbis->usingPushMode = MA_FALSE; - - result = ma_stbvorbis_post_init(pVorbis); - if (result != MA_SUCCESS) { - stb_vorbis_close(pVorbis->stb); - return result; - } - - return MA_SUCCESS; - } - #else - { - /* vorbis is disabled. */ - (void)pData; - (void)dataSize; - (void)pAllocationCallbacks; - return MA_NOT_IMPLEMENTED; - } - #endif -} - -MA_API void ma_stbvorbis_uninit(ma_stbvorbis* pVorbis, const ma_allocation_callbacks* pAllocationCallbacks) -{ - if (pVorbis == NULL) { - return; - } - - #if !defined(MA_NO_VORBIS) - { - stb_vorbis_close(pVorbis->stb); - - /* We'll have to clear some memory if we're using push mode. */ - if (pVorbis->usingPushMode) { - ma_free(pVorbis->push.pData, pAllocationCallbacks); - } - } - #else - { - /* vorbis is disabled. Should never hit this since initialization would have failed. */ - MA_ASSERT(MA_FALSE); - } - #endif - - ma_data_source_uninit(&pVorbis->ds); -} - -MA_API ma_result ma_stbvorbis_read_pcm_frames(ma_stbvorbis* pVorbis, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead) -{ - if (pFramesRead != NULL) { - *pFramesRead = 0; - } - - if (frameCount == 0) { - return MA_INVALID_ARGS; - } - - if (pVorbis == NULL) { - return MA_INVALID_ARGS; - } - - #if !defined(MA_NO_VORBIS) - { - /* We always use floating point format. */ - ma_result result = MA_SUCCESS; /* Must be initialized to MA_SUCCESS. */ - ma_uint64 totalFramesRead = 0; - ma_format format; - ma_uint32 channels; - - ma_stbvorbis_get_data_format(pVorbis, &format, &channels, NULL, NULL, 0); - - if (format == ma_format_f32) { - /* We read differently depending on whether or not we're using push mode. */ - if (pVorbis->usingPushMode) { - /* Push mode. This is the complex case. */ - float* pFramesOutF32 = (float*)pFramesOut; - - while (totalFramesRead < frameCount) { - /* The first thing to do is read from any already-cached frames. */ - ma_uint32 framesToReadFromCache = (ma_uint32)ma_min(pVorbis->push.framesRemaining, (frameCount - totalFramesRead)); /* Safe cast because pVorbis->framesRemaining is 32-bit. */ - - /* The output pointer can be null in which case we just treat it as a seek. */ - if (pFramesOut != NULL) { - ma_uint64 iFrame; - for (iFrame = 0; iFrame < framesToReadFromCache; iFrame += 1) { - ma_uint32 iChannel; - for (iChannel = 0; iChannel < pVorbis->channels; iChannel += 1) { - pFramesOutF32[iChannel] = pVorbis->push.ppPacketData[iChannel][pVorbis->push.framesConsumed + iFrame]; - } - - pFramesOutF32 += pVorbis->channels; - } - } - - /* Update pointers and counters. */ - pVorbis->push.framesConsumed += framesToReadFromCache; - pVorbis->push.framesRemaining -= framesToReadFromCache; - totalFramesRead += framesToReadFromCache; - - /* Don't bother reading any more frames right now if we've just finished loading. */ - if (totalFramesRead == frameCount) { - break; - } - - MA_ASSERT(pVorbis->push.framesRemaining == 0); - - /* Getting here means we've run out of cached frames. We'll need to load some more. */ - for (;;) { - int samplesRead = 0; - int consumedDataSize; - - /* We need to case dataSize to an int, so make sure we can do it safely. */ - if (pVorbis->push.dataSize > INT_MAX) { - break; /* Too big. */ - } - - consumedDataSize = stb_vorbis_decode_frame_pushdata(pVorbis->stb, pVorbis->push.pData, (int)pVorbis->push.dataSize, NULL, &pVorbis->push.ppPacketData, &samplesRead); - if (consumedDataSize != 0) { - /* Successfully decoded a Vorbis frame. Consume the data. */ - pVorbis->push.dataSize -= (size_t)consumedDataSize; - MA_MOVE_MEMORY(pVorbis->push.pData, ma_offset_ptr(pVorbis->push.pData, consumedDataSize), pVorbis->push.dataSize); - - pVorbis->push.framesConsumed = 0; - pVorbis->push.framesRemaining = samplesRead; - - break; - } else { - /* Not enough data. Read more. */ - size_t bytesRead; - - /* Expand the data buffer if necessary. */ - if (pVorbis->push.dataCapacity == pVorbis->push.dataSize) { - size_t newCap = pVorbis->push.dataCapacity + MA_VORBIS_DATA_CHUNK_SIZE; - ma_uint8* pNewData; - - pNewData = (ma_uint8*)ma_realloc(pVorbis->push.pData, newCap, &pVorbis->allocationCallbacks); - if (pNewData == NULL) { - result = MA_OUT_OF_MEMORY; - break; - } - - pVorbis->push.pData = pNewData; - pVorbis->push.dataCapacity = newCap; - } - - /* We should have enough room to load some data. */ - result = pVorbis->onRead(pVorbis->pReadSeekTellUserData, ma_offset_ptr(pVorbis->push.pData, pVorbis->push.dataSize), (pVorbis->push.dataCapacity - pVorbis->push.dataSize), &bytesRead); - pVorbis->push.dataSize += bytesRead; - - if (result != MA_SUCCESS) { - break; /* Failed to read any data. Get out. */ - } - } - } - - /* If we don't have a success code at this point it means we've encountered an error or the end of the file has been reached (probably the latter). */ - if (result != MA_SUCCESS) { - break; - } - } - } else { - /* Pull mode. This is the simple case, but we still need to run in a loop because stb_vorbis loves using 32-bit instead of 64-bit. */ - while (totalFramesRead < frameCount) { - ma_uint64 framesRemaining = (frameCount - totalFramesRead); - int framesRead; - - if (framesRemaining > INT_MAX) { - framesRemaining = INT_MAX; - } - - framesRead = stb_vorbis_get_samples_float_interleaved(pVorbis->stb, channels, (float*)ma_offset_pcm_frames_ptr(pFramesOut, totalFramesRead, format, channels), (int)framesRemaining * channels); /* Safe cast. */ - totalFramesRead += framesRead; - - if (framesRead < (int)framesRemaining) { - break; /* Nothing left to read. Get out. */ - } - } - } - } else { - result = MA_INVALID_ARGS; - } - - pVorbis->cursor += totalFramesRead; - - if (totalFramesRead == 0) { - result = MA_AT_END; - } - - if (pFramesRead != NULL) { - *pFramesRead = totalFramesRead; - } - - if (result == MA_SUCCESS && totalFramesRead == 0) { - result = MA_AT_END; - } - - return result; - } - #else - { - /* vorbis is disabled. Should never hit this since initialization would have failed. */ - MA_ASSERT(MA_FALSE); - - (void)pFramesOut; - (void)frameCount; - (void)pFramesRead; - - return MA_NOT_IMPLEMENTED; - } - #endif -} - -MA_API ma_result ma_stbvorbis_seek_to_pcm_frame(ma_stbvorbis* pVorbis, ma_uint64 frameIndex) -{ - if (pVorbis == NULL) { - return MA_INVALID_ARGS; - } - - #if !defined(MA_NO_VORBIS) - { - /* Different seeking methods depending on whether or not we're using push mode. */ - if (pVorbis->usingPushMode) { - /* Push mode. This is the complex case. */ - ma_result result; - float buffer[4096]; - - /* If we're seeking backwards, we need to seek back to the start and then brute-force forward. */ - if (frameIndex < pVorbis->cursor) { - if (frameIndex > 0x7FFFFFFF) { - return MA_INVALID_ARGS; /* Trying to seek beyond the 32-bit maximum of stb_vorbis. */ - } - - /* - This is wildly inefficient due to me having trouble getting sample exact seeking working - robustly with stb_vorbis_flush_pushdata(). The only way I can think to make this work - perfectly is to reinitialize the decoder. Note that we only enter this path when seeking - backwards. This will hopefully be removed once we get our own Vorbis decoder implemented. - */ - stb_vorbis_close(pVorbis->stb); - ma_free(pVorbis->push.pData, &pVorbis->allocationCallbacks); - - MA_ZERO_OBJECT(&pVorbis->push); - - /* Seek to the start of the file. */ - result = pVorbis->onSeek(pVorbis->pReadSeekTellUserData, 0, ma_seek_origin_start); - if (result != MA_SUCCESS) { - return result; - } - - result = ma_stbvorbis_init_internal_decoder_push(pVorbis); - if (result != MA_SUCCESS) { - return result; - } - - /* At this point we should be sitting on the first frame. */ - pVorbis->cursor = 0; - } - - /* We're just brute-forcing this for now. */ - while (pVorbis->cursor < frameIndex) { - ma_uint64 framesRead; - ma_uint64 framesToRead = ma_countof(buffer)/pVorbis->channels; - if (framesToRead > (frameIndex - pVorbis->cursor)) { - framesToRead = (frameIndex - pVorbis->cursor); - } - - result = ma_stbvorbis_read_pcm_frames(pVorbis, buffer, framesToRead, &framesRead); - if (result != MA_SUCCESS) { - return result; - } - } - } else { - /* Pull mode. This is the simple case. */ - int vorbisResult; - - if (frameIndex > UINT_MAX) { - return MA_INVALID_ARGS; /* Trying to seek beyond the 32-bit maximum of stb_vorbis. */ - } - - vorbisResult = stb_vorbis_seek(pVorbis->stb, (unsigned int)frameIndex); /* Safe cast. */ - if (vorbisResult == 0) { - return MA_ERROR; /* See failed. */ - } - - pVorbis->cursor = frameIndex; - } - - return MA_SUCCESS; - } - #else - { - /* vorbis is disabled. Should never hit this since initialization would have failed. */ - MA_ASSERT(MA_FALSE); - - (void)frameIndex; - - return MA_NOT_IMPLEMENTED; - } - #endif -} - -MA_API ma_result ma_stbvorbis_get_data_format(ma_stbvorbis* pVorbis, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate, ma_channel* pChannelMap, size_t channelMapCap) -{ - /* Defaults for safety. */ - if (pFormat != NULL) { - *pFormat = ma_format_unknown; - } - if (pChannels != NULL) { - *pChannels = 0; - } - if (pSampleRate != NULL) { - *pSampleRate = 0; - } - if (pChannelMap != NULL) { - MA_ZERO_MEMORY(pChannelMap, sizeof(*pChannelMap) * channelMapCap); - } - - if (pVorbis == NULL) { - return MA_INVALID_OPERATION; - } - - if (pFormat != NULL) { - *pFormat = pVorbis->format; - } - - #if !defined(MA_NO_VORBIS) - { - if (pChannels != NULL) { - *pChannels = pVorbis->channels; - } - - if (pSampleRate != NULL) { - *pSampleRate = pVorbis->sampleRate; - } - - if (pChannelMap != NULL) { - ma_channel_map_init_standard(ma_standard_channel_map_vorbis, pChannelMap, channelMapCap, pVorbis->channels); - } - - return MA_SUCCESS; - } - #else - { - /* vorbis is disabled. Should never hit this since initialization would have failed. */ - MA_ASSERT(MA_FALSE); - return MA_NOT_IMPLEMENTED; - } - #endif -} - -MA_API ma_result ma_stbvorbis_get_cursor_in_pcm_frames(ma_stbvorbis* pVorbis, ma_uint64* pCursor) -{ - if (pCursor == NULL) { - return MA_INVALID_ARGS; - } - - *pCursor = 0; /* Safety. */ - - if (pVorbis == NULL) { - return MA_INVALID_ARGS; - } - - #if !defined(MA_NO_VORBIS) - { - *pCursor = pVorbis->cursor; - - return MA_SUCCESS; - } - #else - { - /* vorbis is disabled. Should never hit this since initialization would have failed. */ - MA_ASSERT(MA_FALSE); - return MA_NOT_IMPLEMENTED; - } - #endif -} - -MA_API ma_result ma_stbvorbis_get_length_in_pcm_frames(ma_stbvorbis* pVorbis, ma_uint64* pLength) -{ - if (pLength == NULL) { - return MA_INVALID_ARGS; - } - - *pLength = 0; /* Safety. */ - - if (pVorbis == NULL) { - return MA_INVALID_ARGS; - } - - #if !defined(MA_NO_VORBIS) - { - if (pVorbis->usingPushMode) { - *pLength = 0; /* I don't know of a good way to determine this reliably with stb_vorbis and push mode. */ - } else { - *pLength = stb_vorbis_stream_length_in_samples(pVorbis->stb); - } - - return MA_SUCCESS; - } - #else - { - /* vorbis is disabled. Should never hit this since initialization would have failed. */ - MA_ASSERT(MA_FALSE); - return MA_NOT_IMPLEMENTED; - } - #endif -} - - -static ma_result ma_decoding_backend_init__stbvorbis(void* pUserData, ma_read_proc onRead, ma_seek_proc onSeek, ma_tell_proc onTell, void* pReadSeekTellUserData, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_data_source** ppBackend) -{ - ma_result result; - ma_stbvorbis* pVorbis; - - (void)pUserData; /* For now not using pUserData, but once we start storing the vorbis decoder state within the ma_decoder structure this will be set to the decoder so we can avoid a malloc. */ - - /* For now we're just allocating the decoder backend on the heap. */ - pVorbis = (ma_stbvorbis*)ma_malloc(sizeof(*pVorbis), pAllocationCallbacks); - if (pVorbis == NULL) { - return MA_OUT_OF_MEMORY; - } - - result = ma_stbvorbis_init(onRead, onSeek, onTell, pReadSeekTellUserData, pConfig, pAllocationCallbacks, pVorbis); - if (result != MA_SUCCESS) { - ma_free(pVorbis, pAllocationCallbacks); - return result; - } - - *ppBackend = pVorbis; - - return MA_SUCCESS; -} - -static ma_result ma_decoding_backend_init_file__stbvorbis(void* pUserData, const char* pFilePath, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_data_source** ppBackend) -{ - ma_result result; - ma_stbvorbis* pVorbis; - - (void)pUserData; /* For now not using pUserData, but once we start storing the vorbis decoder state within the ma_decoder structure this will be set to the decoder so we can avoid a malloc. */ - - /* For now we're just allocating the decoder backend on the heap. */ - pVorbis = (ma_stbvorbis*)ma_malloc(sizeof(*pVorbis), pAllocationCallbacks); - if (pVorbis == NULL) { - return MA_OUT_OF_MEMORY; - } - - result = ma_stbvorbis_init_file(pFilePath, pConfig, pAllocationCallbacks, pVorbis); - if (result != MA_SUCCESS) { - ma_free(pVorbis, pAllocationCallbacks); - return result; - } - - *ppBackend = pVorbis; - - return MA_SUCCESS; -} - -static ma_result ma_decoding_backend_init_memory__stbvorbis(void* pUserData, const void* pData, size_t dataSize, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_data_source** ppBackend) -{ - ma_result result; - ma_stbvorbis* pVorbis; - - (void)pUserData; /* For now not using pUserData, but once we start storing the vorbis decoder state within the ma_decoder structure this will be set to the decoder so we can avoid a malloc. */ - - /* For now we're just allocating the decoder backend on the heap. */ - pVorbis = (ma_stbvorbis*)ma_malloc(sizeof(*pVorbis), pAllocationCallbacks); - if (pVorbis == NULL) { - return MA_OUT_OF_MEMORY; - } - - result = ma_stbvorbis_init_memory(pData, dataSize, pConfig, pAllocationCallbacks, pVorbis); - if (result != MA_SUCCESS) { - ma_free(pVorbis, pAllocationCallbacks); - return result; - } - - *ppBackend = pVorbis; - - return MA_SUCCESS; -} - -static void ma_decoding_backend_uninit__stbvorbis(void* pUserData, ma_data_source* pBackend, const ma_allocation_callbacks* pAllocationCallbacks) -{ - ma_stbvorbis* pVorbis = (ma_stbvorbis*)pBackend; - - (void)pUserData; - - ma_stbvorbis_uninit(pVorbis, pAllocationCallbacks); - ma_free(pVorbis, pAllocationCallbacks); -} - -static ma_decoding_backend_vtable g_ma_decoding_backend_vtable_stbvorbis = -{ - ma_decoding_backend_init__stbvorbis, - ma_decoding_backend_init_file__stbvorbis, - NULL, /* onInitFileW() */ - ma_decoding_backend_init_memory__stbvorbis, - ma_decoding_backend_uninit__stbvorbis -}; - -static ma_result ma_decoder_init_vorbis__internal(const ma_decoder_config* pConfig, ma_decoder* pDecoder) -{ - return ma_decoder_init_from_vtable__internal(&g_ma_decoding_backend_vtable_stbvorbis, NULL, pConfig, pDecoder); -} - -static ma_result ma_decoder_init_vorbis_from_file__internal(const char* pFilePath, const ma_decoder_config* pConfig, ma_decoder* pDecoder) -{ - return ma_decoder_init_from_file__internal(&g_ma_decoding_backend_vtable_stbvorbis, NULL, pFilePath, pConfig, pDecoder); -} - -static ma_result ma_decoder_init_vorbis_from_file_w__internal(const wchar_t* pFilePath, const ma_decoder_config* pConfig, ma_decoder* pDecoder) -{ - return ma_decoder_init_from_file_w__internal(&g_ma_decoding_backend_vtable_stbvorbis, NULL, pFilePath, pConfig, pDecoder); -} - -static ma_result ma_decoder_init_vorbis_from_memory__internal(const void* pData, size_t dataSize, const ma_decoder_config* pConfig, ma_decoder* pDecoder) -{ - return ma_decoder_init_from_memory__internal(&g_ma_decoding_backend_vtable_stbvorbis, NULL, pData, dataSize, pConfig, pDecoder); -} -#endif /* STB_VORBIS_INCLUDE_STB_VORBIS_H */ - - - -static ma_result ma_decoder__init_allocation_callbacks(const ma_decoder_config* pConfig, ma_decoder* pDecoder) -{ - MA_ASSERT(pDecoder != NULL); - - if (pConfig != NULL) { - return ma_allocation_callbacks_init_copy(&pDecoder->allocationCallbacks, &pConfig->allocationCallbacks); - } else { - pDecoder->allocationCallbacks = ma_allocation_callbacks_init_default(); - return MA_SUCCESS; - } -} - -static ma_result ma_decoder__data_source_on_read(ma_data_source* pDataSource, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead) -{ - return ma_decoder_read_pcm_frames((ma_decoder*)pDataSource, pFramesOut, frameCount, pFramesRead); -} - -static ma_result ma_decoder__data_source_on_seek(ma_data_source* pDataSource, ma_uint64 frameIndex) -{ - return ma_decoder_seek_to_pcm_frame((ma_decoder*)pDataSource, frameIndex); -} - -static ma_result ma_decoder__data_source_on_get_data_format(ma_data_source* pDataSource, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate, ma_channel* pChannelMap, size_t channelMapCap) -{ - return ma_decoder_get_data_format((ma_decoder*)pDataSource, pFormat, pChannels, pSampleRate, pChannelMap, channelMapCap); -} - -static ma_result ma_decoder__data_source_on_get_cursor(ma_data_source* pDataSource, ma_uint64* pCursor) -{ - return ma_decoder_get_cursor_in_pcm_frames((ma_decoder*)pDataSource, pCursor); -} - -static ma_result ma_decoder__data_source_on_get_length(ma_data_source* pDataSource, ma_uint64* pLength) -{ - return ma_decoder_get_length_in_pcm_frames((ma_decoder*)pDataSource, pLength); -} - -static ma_data_source_vtable g_ma_decoder_data_source_vtable = -{ - ma_decoder__data_source_on_read, - ma_decoder__data_source_on_seek, - ma_decoder__data_source_on_get_data_format, - ma_decoder__data_source_on_get_cursor, - ma_decoder__data_source_on_get_length, - NULL, /* onSetLooping */ - 0 -}; - -static ma_result ma_decoder__preinit(ma_decoder_read_proc onRead, ma_decoder_seek_proc onSeek, ma_decoder_tell_proc onTell, void* pUserData, const ma_decoder_config* pConfig, ma_decoder* pDecoder) -{ - ma_result result; - ma_data_source_config dataSourceConfig; - - MA_ASSERT(pConfig != NULL); - - if (pDecoder == NULL) { - return MA_INVALID_ARGS; - } - - MA_ZERO_OBJECT(pDecoder); - - dataSourceConfig = ma_data_source_config_init(); - dataSourceConfig.vtable = &g_ma_decoder_data_source_vtable; - - result = ma_data_source_init(&dataSourceConfig, &pDecoder->ds); - if (result != MA_SUCCESS) { - return result; - } - - pDecoder->onRead = onRead; - pDecoder->onSeek = onSeek; - pDecoder->onTell = onTell; - pDecoder->pUserData = pUserData; - - result = ma_decoder__init_allocation_callbacks(pConfig, pDecoder); - if (result != MA_SUCCESS) { - ma_data_source_uninit(&pDecoder->ds); - return result; - } - - return MA_SUCCESS; -} - -static ma_result ma_decoder__postinit(const ma_decoder_config* pConfig, ma_decoder* pDecoder) -{ - ma_result result; - - result = ma_decoder__init_data_converter(pDecoder, pConfig); - - /* If we failed post initialization we need to uninitialize the decoder before returning to prevent a memory leak. */ - if (result != MA_SUCCESS) { - ma_decoder_uninit(pDecoder); - return result; - } - - return result; -} - - -static ma_result ma_decoder_init__internal(ma_decoder_read_proc onRead, ma_decoder_seek_proc onSeek, void* pUserData, const ma_decoder_config* pConfig, ma_decoder* pDecoder) -{ - ma_result result = MA_NO_BACKEND; - - MA_ASSERT(pConfig != NULL); - MA_ASSERT(pDecoder != NULL); - - /* Silence some warnings in the case that we don't have any decoder backends enabled. */ - (void)onRead; - (void)onSeek; - (void)pUserData; - - - /* If we've specified a specific encoding type, try that first. */ - if (pConfig->encodingFormat != ma_encoding_format_unknown) { - #ifdef MA_HAS_WAV - if (pConfig->encodingFormat == ma_encoding_format_wav) { - result = ma_decoder_init_wav__internal(pConfig, pDecoder); - } - #endif - #ifdef MA_HAS_FLAC - if (pConfig->encodingFormat == ma_encoding_format_flac) { - result = ma_decoder_init_flac__internal(pConfig, pDecoder); - } - #endif - #ifdef MA_HAS_MP3 - if (pConfig->encodingFormat == ma_encoding_format_mp3) { - result = ma_decoder_init_mp3__internal(pConfig, pDecoder); - } - #endif - #ifdef MA_HAS_VORBIS - if (pConfig->encodingFormat == ma_encoding_format_vorbis) { - result = ma_decoder_init_vorbis__internal(pConfig, pDecoder); - } - #endif - - /* If we weren't able to initialize the decoder, seek back to the start to give the next attempts a clean start. */ - if (result != MA_SUCCESS) { - onSeek(pDecoder, 0, ma_seek_origin_start); - } - } - - if (result != MA_SUCCESS) { - /* Getting here means we couldn't load a specific decoding backend based on the encoding format. */ - - /* - We use trial and error to open a decoder. We prioritize custom decoders so that if they - implement the same encoding format they take priority over the built-in decoders. - */ - if (result != MA_SUCCESS) { - result = ma_decoder_init_custom__internal(pConfig, pDecoder); - if (result != MA_SUCCESS) { - onSeek(pDecoder, 0, ma_seek_origin_start); - } - } - - /* - If we get to this point and we still haven't found a decoder, and the caller has requested a - specific encoding format, there's no hope for it. Abort. - */ - if (pConfig->encodingFormat != ma_encoding_format_unknown) { - return MA_NO_BACKEND; - } - - #ifdef MA_HAS_WAV - if (result != MA_SUCCESS) { - result = ma_decoder_init_wav__internal(pConfig, pDecoder); - if (result != MA_SUCCESS) { - onSeek(pDecoder, 0, ma_seek_origin_start); - } - } - #endif - #ifdef MA_HAS_FLAC - if (result != MA_SUCCESS) { - result = ma_decoder_init_flac__internal(pConfig, pDecoder); - if (result != MA_SUCCESS) { - onSeek(pDecoder, 0, ma_seek_origin_start); - } - } - #endif - #ifdef MA_HAS_MP3 - if (result != MA_SUCCESS) { - result = ma_decoder_init_mp3__internal(pConfig, pDecoder); - if (result != MA_SUCCESS) { - onSeek(pDecoder, 0, ma_seek_origin_start); - } - } - #endif - #ifdef MA_HAS_VORBIS - if (result != MA_SUCCESS) { - result = ma_decoder_init_vorbis__internal(pConfig, pDecoder); - if (result != MA_SUCCESS) { - onSeek(pDecoder, 0, ma_seek_origin_start); - } - } - #endif - } - - if (result != MA_SUCCESS) { - return result; - } - - return ma_decoder__postinit(pConfig, pDecoder); -} - -MA_API ma_result ma_decoder_init(ma_decoder_read_proc onRead, ma_decoder_seek_proc onSeek, void* pUserData, const ma_decoder_config* pConfig, ma_decoder* pDecoder) -{ - ma_decoder_config config; - ma_result result; - - config = ma_decoder_config_init_copy(pConfig); - - result = ma_decoder__preinit(onRead, onSeek, NULL, pUserData, &config, pDecoder); - if (result != MA_SUCCESS) { - return result; - } - - return ma_decoder_init__internal(onRead, onSeek, pUserData, &config, pDecoder); -} - - -static ma_result ma_decoder__on_read_memory(ma_decoder* pDecoder, void* pBufferOut, size_t bytesToRead, size_t* pBytesRead) -{ - size_t bytesRemaining; - - MA_ASSERT(pDecoder->data.memory.dataSize >= pDecoder->data.memory.currentReadPos); - - if (pBytesRead != NULL) { - *pBytesRead = 0; - } - - bytesRemaining = pDecoder->data.memory.dataSize - pDecoder->data.memory.currentReadPos; - if (bytesToRead > bytesRemaining) { - bytesToRead = bytesRemaining; - } - - if (bytesRemaining == 0) { - return MA_AT_END; - } - - if (bytesToRead > 0) { - MA_COPY_MEMORY(pBufferOut, pDecoder->data.memory.pData + pDecoder->data.memory.currentReadPos, bytesToRead); - pDecoder->data.memory.currentReadPos += bytesToRead; - } - - if (pBytesRead != NULL) { - *pBytesRead = bytesToRead; - } - - return MA_SUCCESS; -} - -static ma_result ma_decoder__on_seek_memory(ma_decoder* pDecoder, ma_int64 byteOffset, ma_seek_origin origin) -{ - if (byteOffset > 0 && (ma_uint64)byteOffset > MA_SIZE_MAX) { - return MA_BAD_SEEK; - } - - if (origin == ma_seek_origin_current) { - if (byteOffset > 0) { - if (pDecoder->data.memory.currentReadPos + byteOffset > pDecoder->data.memory.dataSize) { - byteOffset = (ma_int64)(pDecoder->data.memory.dataSize - pDecoder->data.memory.currentReadPos); /* Trying to seek too far forward. */ - } - - pDecoder->data.memory.currentReadPos += (size_t)byteOffset; - } else { - if (pDecoder->data.memory.currentReadPos < (size_t)-byteOffset) { - byteOffset = -(ma_int64)pDecoder->data.memory.currentReadPos; /* Trying to seek too far backwards. */ - } - - pDecoder->data.memory.currentReadPos -= (size_t)-byteOffset; - } - } else { - if (origin == ma_seek_origin_end) { - if (byteOffset < 0) { - byteOffset = -byteOffset; - } - - if (byteOffset > (ma_int64)pDecoder->data.memory.dataSize) { - pDecoder->data.memory.currentReadPos = 0; /* Trying to seek too far back. */ - } else { - pDecoder->data.memory.currentReadPos = pDecoder->data.memory.dataSize - (size_t)byteOffset; - } - } else { - if ((size_t)byteOffset <= pDecoder->data.memory.dataSize) { - pDecoder->data.memory.currentReadPos = (size_t)byteOffset; - } else { - pDecoder->data.memory.currentReadPos = pDecoder->data.memory.dataSize; /* Trying to seek too far forward. */ - } - } - } - - return MA_SUCCESS; -} - -static ma_result ma_decoder__on_tell_memory(ma_decoder* pDecoder, ma_int64* pCursor) -{ - MA_ASSERT(pDecoder != NULL); - MA_ASSERT(pCursor != NULL); - - *pCursor = (ma_int64)pDecoder->data.memory.currentReadPos; - - return MA_SUCCESS; -} - -static ma_result ma_decoder__preinit_memory_wrapper(const void* pData, size_t dataSize, const ma_decoder_config* pConfig, ma_decoder* pDecoder) -{ - ma_result result = ma_decoder__preinit(ma_decoder__on_read_memory, ma_decoder__on_seek_memory, ma_decoder__on_tell_memory, NULL, pConfig, pDecoder); - if (result != MA_SUCCESS) { - return result; - } - - if (pData == NULL || dataSize == 0) { - return MA_INVALID_ARGS; - } - - pDecoder->data.memory.pData = (const ma_uint8*)pData; - pDecoder->data.memory.dataSize = dataSize; - pDecoder->data.memory.currentReadPos = 0; - - (void)pConfig; - return MA_SUCCESS; -} - -MA_API ma_result ma_decoder_init_memory(const void* pData, size_t dataSize, const ma_decoder_config* pConfig, ma_decoder* pDecoder) -{ - ma_result result; - ma_decoder_config config; - - config = ma_decoder_config_init_copy(pConfig); - - result = ma_decoder__preinit(NULL, NULL, NULL, NULL, &config, pDecoder); - if (result != MA_SUCCESS) { - return result; - } - - if (pData == NULL || dataSize == 0) { - return MA_INVALID_ARGS; - } - - /* If the backend has support for loading from a file path we'll want to use that. If that all fails we'll fall back to the VFS path. */ - result = MA_NO_BACKEND; - - if (config.encodingFormat != ma_encoding_format_unknown) { - #ifdef MA_HAS_WAV - if (config.encodingFormat == ma_encoding_format_wav) { - result = ma_decoder_init_wav_from_memory__internal(pData, dataSize, &config, pDecoder); - } - #endif - #ifdef MA_HAS_FLAC - if (config.encodingFormat == ma_encoding_format_flac) { - result = ma_decoder_init_flac_from_memory__internal(pData, dataSize, &config, pDecoder); - } - #endif - #ifdef MA_HAS_MP3 - if (config.encodingFormat == ma_encoding_format_mp3) { - result = ma_decoder_init_mp3_from_memory__internal(pData, dataSize, &config, pDecoder); - } - #endif - #ifdef MA_HAS_VORBIS - if (config.encodingFormat == ma_encoding_format_vorbis) { - result = ma_decoder_init_vorbis_from_memory__internal(pData, dataSize, &config, pDecoder); - } - #endif - } - - if (result != MA_SUCCESS) { - /* Getting here means we weren't able to initialize a decoder of a specific encoding format. */ - - /* - We use trial and error to open a decoder. We prioritize custom decoders so that if they - implement the same encoding format they take priority over the built-in decoders. - */ - result = ma_decoder_init_custom_from_memory__internal(pData, dataSize, &config, pDecoder); - - /* - If we get to this point and we still haven't found a decoder, and the caller has requested a - specific encoding format, there's no hope for it. Abort. - */ - if (result != MA_SUCCESS && config.encodingFormat != ma_encoding_format_unknown) { - return MA_NO_BACKEND; - } - - /* Use trial and error for stock decoders. */ - if (result != MA_SUCCESS) { - #ifdef MA_HAS_WAV - if (result != MA_SUCCESS) { - result = ma_decoder_init_wav_from_memory__internal(pData, dataSize, &config, pDecoder); - } - #endif - #ifdef MA_HAS_FLAC - if (result != MA_SUCCESS) { - result = ma_decoder_init_flac_from_memory__internal(pData, dataSize, &config, pDecoder); - } - #endif - #ifdef MA_HAS_MP3 - if (result != MA_SUCCESS) { - result = ma_decoder_init_mp3_from_memory__internal(pData, dataSize, &config, pDecoder); - } - #endif - #ifdef MA_HAS_VORBIS - if (result != MA_SUCCESS) { - result = ma_decoder_init_vorbis_from_memory__internal(pData, dataSize, &config, pDecoder); - } - #endif - } - } - - /* - If at this point we still haven't successfully initialized the decoder it most likely means - the backend doesn't have an implementation for loading from a file path. We'll try using - miniaudio's built-in file IO for loading file. - */ - if (result == MA_SUCCESS) { - /* Initialization was successful. Finish up. */ - result = ma_decoder__postinit(&config, pDecoder); - if (result != MA_SUCCESS) { - /* - The backend was initialized successfully, but for some reason post-initialization failed. This is most likely - due to an out of memory error. We're going to abort with an error here and not try to recover. - */ - if (pDecoder->pBackendVTable != NULL && pDecoder->pBackendVTable->onUninit != NULL) { - pDecoder->pBackendVTable->onUninit(pDecoder->pBackendUserData, &pDecoder->pBackend, &pDecoder->allocationCallbacks); - } - - return result; - } - } else { - /* Probably no implementation for loading from a block of memory. Use miniaudio's abstraction instead. */ - result = ma_decoder__preinit_memory_wrapper(pData, dataSize, &config, pDecoder); - if (result != MA_SUCCESS) { - return result; - } - - result = ma_decoder_init__internal(ma_decoder__on_read_memory, ma_decoder__on_seek_memory, NULL, &config, pDecoder); - if (result != MA_SUCCESS) { - return result; - } - } - - return MA_SUCCESS; -} - - -#if defined(MA_HAS_WAV) || \ - defined(MA_HAS_MP3) || \ - defined(MA_HAS_FLAC) || \ - defined(MA_HAS_VORBIS) -#define MA_HAS_PATH_API -#endif - -#if defined(MA_HAS_PATH_API) -static const char* ma_path_file_name(const char* path) -{ - const char* fileName; - - if (path == NULL) { - return NULL; - } - - fileName = path; - - /* We just loop through the path until we find the last slash. */ - while (path[0] != '\0') { - if (path[0] == '/' || path[0] == '\\') { - fileName = path; - } - - path += 1; - } - - /* At this point the file name is sitting on a slash, so just move forward. */ - while (fileName[0] != '\0' && (fileName[0] == '/' || fileName[0] == '\\')) { - fileName += 1; - } - - return fileName; -} - -static const wchar_t* ma_path_file_name_w(const wchar_t* path) -{ - const wchar_t* fileName; - - if (path == NULL) { - return NULL; - } - - fileName = path; - - /* We just loop through the path until we find the last slash. */ - while (path[0] != '\0') { - if (path[0] == '/' || path[0] == '\\') { - fileName = path; - } - - path += 1; - } - - /* At this point the file name is sitting on a slash, so just move forward. */ - while (fileName[0] != '\0' && (fileName[0] == '/' || fileName[0] == '\\')) { - fileName += 1; - } - - return fileName; -} - - -static const char* ma_path_extension(const char* path) -{ - const char* extension; - const char* lastOccurance; - - if (path == NULL) { - path = ""; - } - - extension = ma_path_file_name(path); - lastOccurance = NULL; - - /* Just find the last '.' and return. */ - while (extension[0] != '\0') { - if (extension[0] == '.') { - extension += 1; - lastOccurance = extension; - } - - extension += 1; - } - - return (lastOccurance != NULL) ? lastOccurance : extension; -} - -static const wchar_t* ma_path_extension_w(const wchar_t* path) -{ - const wchar_t* extension; - const wchar_t* lastOccurance; - - if (path == NULL) { - path = L""; - } - - extension = ma_path_file_name_w(path); - lastOccurance = NULL; - - /* Just find the last '.' and return. */ - while (extension[0] != '\0') { - if (extension[0] == '.') { - extension += 1; - lastOccurance = extension; - } - - extension += 1; - } - - return (lastOccurance != NULL) ? lastOccurance : extension; -} - - -static ma_bool32 ma_path_extension_equal(const char* path, const char* extension) -{ - const char* ext1; - const char* ext2; - - if (path == NULL || extension == NULL) { - return MA_FALSE; - } - - ext1 = extension; - ext2 = ma_path_extension(path); - -#if defined(_MSC_VER) || defined(__DMC__) - return _stricmp(ext1, ext2) == 0; -#else - return strcasecmp(ext1, ext2) == 0; -#endif -} - -static ma_bool32 ma_path_extension_equal_w(const wchar_t* path, const wchar_t* extension) -{ - const wchar_t* ext1; - const wchar_t* ext2; - - if (path == NULL || extension == NULL) { - return MA_FALSE; - } - - ext1 = extension; - ext2 = ma_path_extension_w(path); - -#if defined(_MSC_VER) || defined(__WATCOMC__) || defined(__DMC__) - return _wcsicmp(ext1, ext2) == 0; -#else - /* - I'm not aware of a wide character version of strcasecmp(). I'm therefore converting the extensions to multibyte strings and comparing those. This - isn't the most efficient way to do it, but it should work OK. - */ - { - char ext1MB[4096]; - char ext2MB[4096]; - const wchar_t* pext1 = ext1; - const wchar_t* pext2 = ext2; - mbstate_t mbs1; - mbstate_t mbs2; - - MA_ZERO_OBJECT(&mbs1); - MA_ZERO_OBJECT(&mbs2); - - if (wcsrtombs(ext1MB, &pext1, sizeof(ext1MB), &mbs1) == (size_t)-1) { - return MA_FALSE; - } - if (wcsrtombs(ext2MB, &pext2, sizeof(ext2MB), &mbs2) == (size_t)-1) { - return MA_FALSE; - } - - return strcasecmp(ext1MB, ext2MB) == 0; - } -#endif -} -#endif /* MA_HAS_PATH_API */ - - - -static ma_result ma_decoder__on_read_vfs(ma_decoder* pDecoder, void* pBufferOut, size_t bytesToRead, size_t* pBytesRead) -{ - MA_ASSERT(pDecoder != NULL); - MA_ASSERT(pBufferOut != NULL); - - return ma_vfs_or_default_read(pDecoder->data.vfs.pVFS, pDecoder->data.vfs.file, pBufferOut, bytesToRead, pBytesRead); -} - -static ma_result ma_decoder__on_seek_vfs(ma_decoder* pDecoder, ma_int64 offset, ma_seek_origin origin) -{ - MA_ASSERT(pDecoder != NULL); - - return ma_vfs_or_default_seek(pDecoder->data.vfs.pVFS, pDecoder->data.vfs.file, offset, origin); -} - -static ma_result ma_decoder__on_tell_vfs(ma_decoder* pDecoder, ma_int64* pCursor) -{ - MA_ASSERT(pDecoder != NULL); - - return ma_vfs_or_default_tell(pDecoder->data.vfs.pVFS, pDecoder->data.vfs.file, pCursor); -} - -static ma_result ma_decoder__preinit_vfs(ma_vfs* pVFS, const char* pFilePath, const ma_decoder_config* pConfig, ma_decoder* pDecoder) -{ - ma_result result; - ma_vfs_file file; - - result = ma_decoder__preinit(ma_decoder__on_read_vfs, ma_decoder__on_seek_vfs, ma_decoder__on_tell_vfs, NULL, pConfig, pDecoder); - if (result != MA_SUCCESS) { - return result; - } - - if (pFilePath == NULL || pFilePath[0] == '\0') { - return MA_INVALID_ARGS; - } - - result = ma_vfs_or_default_open(pVFS, pFilePath, MA_OPEN_MODE_READ, &file); - if (result != MA_SUCCESS) { - return result; - } - - pDecoder->data.vfs.pVFS = pVFS; - pDecoder->data.vfs.file = file; - - return MA_SUCCESS; -} - -MA_API ma_result ma_decoder_init_vfs(ma_vfs* pVFS, const char* pFilePath, const ma_decoder_config* pConfig, ma_decoder* pDecoder) -{ - ma_result result; - ma_decoder_config config; - - config = ma_decoder_config_init_copy(pConfig); - result = ma_decoder__preinit_vfs(pVFS, pFilePath, &config, pDecoder); - if (result != MA_SUCCESS) { - return result; - } - - result = MA_NO_BACKEND; - - if (config.encodingFormat != ma_encoding_format_unknown) { - #ifdef MA_HAS_WAV - if (config.encodingFormat == ma_encoding_format_wav) { - result = ma_decoder_init_wav__internal(&config, pDecoder); - } - #endif - #ifdef MA_HAS_FLAC - if (config.encodingFormat == ma_encoding_format_flac) { - result = ma_decoder_init_flac__internal(&config, pDecoder); - } - #endif - #ifdef MA_HAS_MP3 - if (config.encodingFormat == ma_encoding_format_mp3) { - result = ma_decoder_init_mp3__internal(&config, pDecoder); - } - #endif - #ifdef MA_HAS_VORBIS - if (config.encodingFormat == ma_encoding_format_vorbis) { - result = ma_decoder_init_vorbis__internal(&config, pDecoder); - } - #endif - - /* Make sure we seek back to the start if we didn't initialize a decoder successfully so the next attempts have a fresh start. */ - if (result != MA_SUCCESS) { - ma_decoder__on_seek_vfs(pDecoder, 0, ma_seek_origin_start); - } - } - - if (result != MA_SUCCESS) { - /* Getting here means we weren't able to initialize a decoder of a specific encoding format. */ - - /* - We use trial and error to open a decoder. We prioritize custom decoders so that if they - implement the same encoding format they take priority over the built-in decoders. - */ - if (result != MA_SUCCESS) { - result = ma_decoder_init_custom__internal(&config, pDecoder); - if (result != MA_SUCCESS) { - ma_decoder__on_seek_vfs(pDecoder, 0, ma_seek_origin_start); - } - } - - /* - If we get to this point and we still haven't found a decoder, and the caller has requested a - specific encoding format, there's no hope for it. Abort. - */ - if (config.encodingFormat != ma_encoding_format_unknown) { - return MA_NO_BACKEND; - } - - #ifdef MA_HAS_WAV - if (result != MA_SUCCESS && ma_path_extension_equal(pFilePath, "wav")) { - result = ma_decoder_init_wav__internal(&config, pDecoder); - if (result != MA_SUCCESS) { - ma_decoder__on_seek_vfs(pDecoder, 0, ma_seek_origin_start); - } - } - #endif - #ifdef MA_HAS_FLAC - if (result != MA_SUCCESS && ma_path_extension_equal(pFilePath, "flac")) { - result = ma_decoder_init_flac__internal(&config, pDecoder); - if (result != MA_SUCCESS) { - ma_decoder__on_seek_vfs(pDecoder, 0, ma_seek_origin_start); - } - } - #endif - #ifdef MA_HAS_MP3 - if (result != MA_SUCCESS && ma_path_extension_equal(pFilePath, "mp3")) { - result = ma_decoder_init_mp3__internal(&config, pDecoder); - if (result != MA_SUCCESS) { - ma_decoder__on_seek_vfs(pDecoder, 0, ma_seek_origin_start); - } - } - #endif - } - - /* If we still haven't got a result just use trial and error. Otherwise we can finish up. */ - if (result != MA_SUCCESS) { - result = ma_decoder_init__internal(ma_decoder__on_read_vfs, ma_decoder__on_seek_vfs, NULL, &config, pDecoder); - } else { - result = ma_decoder__postinit(&config, pDecoder); - } - - if (result != MA_SUCCESS) { - if (pDecoder->data.vfs.file != NULL) { /* <-- Will be reset to NULL if ma_decoder_uninit() is called in one of the steps above which allows us to avoid a double close of the file. */ - ma_vfs_or_default_close(pVFS, pDecoder->data.vfs.file); - } - - return result; - } - - return MA_SUCCESS; -} - - -static ma_result ma_decoder__preinit_vfs_w(ma_vfs* pVFS, const wchar_t* pFilePath, const ma_decoder_config* pConfig, ma_decoder* pDecoder) -{ - ma_result result; - ma_vfs_file file; - - result = ma_decoder__preinit(ma_decoder__on_read_vfs, ma_decoder__on_seek_vfs, ma_decoder__on_tell_vfs, NULL, pConfig, pDecoder); - if (result != MA_SUCCESS) { - return result; - } - - if (pFilePath == NULL || pFilePath[0] == '\0') { - return MA_INVALID_ARGS; - } - - result = ma_vfs_or_default_open_w(pVFS, pFilePath, MA_OPEN_MODE_READ, &file); - if (result != MA_SUCCESS) { - return result; - } - - pDecoder->data.vfs.pVFS = pVFS; - pDecoder->data.vfs.file = file; - - return MA_SUCCESS; -} - -MA_API ma_result ma_decoder_init_vfs_w(ma_vfs* pVFS, const wchar_t* pFilePath, const ma_decoder_config* pConfig, ma_decoder* pDecoder) -{ - ma_result result; - ma_decoder_config config; - - config = ma_decoder_config_init_copy(pConfig); - result = ma_decoder__preinit_vfs_w(pVFS, pFilePath, &config, pDecoder); - if (result != MA_SUCCESS) { - return result; - } - - result = MA_NO_BACKEND; - - if (config.encodingFormat != ma_encoding_format_unknown) { - #ifdef MA_HAS_WAV - if (config.encodingFormat == ma_encoding_format_wav) { - result = ma_decoder_init_wav__internal(&config, pDecoder); - } - #endif - #ifdef MA_HAS_FLAC - if (config.encodingFormat == ma_encoding_format_flac) { - result = ma_decoder_init_flac__internal(&config, pDecoder); - } - #endif - #ifdef MA_HAS_MP3 - if (config.encodingFormat == ma_encoding_format_mp3) { - result = ma_decoder_init_mp3__internal(&config, pDecoder); - } - #endif - #ifdef MA_HAS_VORBIS - if (config.encodingFormat == ma_encoding_format_vorbis) { - result = ma_decoder_init_vorbis__internal(&config, pDecoder); - } - #endif - - /* Make sure we seek back to the start if we didn't initialize a decoder successfully so the next attempts have a fresh start. */ - if (result != MA_SUCCESS) { - ma_decoder__on_seek_vfs(pDecoder, 0, ma_seek_origin_start); - } - } - - if (result != MA_SUCCESS) { - /* Getting here means we weren't able to initialize a decoder of a specific encoding format. */ - - /* - We use trial and error to open a decoder. We prioritize custom decoders so that if they - implement the same encoding format they take priority over the built-in decoders. - */ - if (result != MA_SUCCESS) { - result = ma_decoder_init_custom__internal(&config, pDecoder); - if (result != MA_SUCCESS) { - ma_decoder__on_seek_vfs(pDecoder, 0, ma_seek_origin_start); - } - } - - /* - If we get to this point and we still haven't found a decoder, and the caller has requested a - specific encoding format, there's no hope for it. Abort. - */ - if (config.encodingFormat != ma_encoding_format_unknown) { - return MA_NO_BACKEND; - } - - #ifdef MA_HAS_WAV - if (result != MA_SUCCESS && ma_path_extension_equal_w(pFilePath, L"wav")) { - result = ma_decoder_init_wav__internal(&config, pDecoder); - if (result != MA_SUCCESS) { - ma_decoder__on_seek_vfs(pDecoder, 0, ma_seek_origin_start); - } - } - #endif - #ifdef MA_HAS_FLAC - if (result != MA_SUCCESS && ma_path_extension_equal_w(pFilePath, L"flac")) { - result = ma_decoder_init_flac__internal(&config, pDecoder); - if (result != MA_SUCCESS) { - ma_decoder__on_seek_vfs(pDecoder, 0, ma_seek_origin_start); - } - } - #endif - #ifdef MA_HAS_MP3 - if (result != MA_SUCCESS && ma_path_extension_equal_w(pFilePath, L"mp3")) { - result = ma_decoder_init_mp3__internal(&config, pDecoder); - if (result != MA_SUCCESS) { - ma_decoder__on_seek_vfs(pDecoder, 0, ma_seek_origin_start); - } - } - #endif - } - - /* If we still haven't got a result just use trial and error. Otherwise we can finish up. */ - if (result != MA_SUCCESS) { - result = ma_decoder_init__internal(ma_decoder__on_read_vfs, ma_decoder__on_seek_vfs, NULL, &config, pDecoder); - } else { - result = ma_decoder__postinit(&config, pDecoder); - } - - if (result != MA_SUCCESS) { - ma_vfs_or_default_close(pVFS, pDecoder->data.vfs.file); - return result; - } - - return MA_SUCCESS; -} - - -static ma_result ma_decoder__preinit_file(const char* pFilePath, const ma_decoder_config* pConfig, ma_decoder* pDecoder) -{ - ma_result result; - - result = ma_decoder__preinit(NULL, NULL, NULL, NULL, pConfig, pDecoder); - if (result != MA_SUCCESS) { - return result; - } - - if (pFilePath == NULL || pFilePath[0] == '\0') { - return MA_INVALID_ARGS; - } - - return MA_SUCCESS; -} - -MA_API ma_result ma_decoder_init_file(const char* pFilePath, const ma_decoder_config* pConfig, ma_decoder* pDecoder) -{ - ma_result result; - ma_decoder_config config; - - config = ma_decoder_config_init_copy(pConfig); - result = ma_decoder__preinit_file(pFilePath, &config, pDecoder); - if (result != MA_SUCCESS) { - return result; - } - - /* If the backend has support for loading from a file path we'll want to use that. If that all fails we'll fall back to the VFS path. */ - result = MA_NO_BACKEND; - - if (config.encodingFormat != ma_encoding_format_unknown) { - #ifdef MA_HAS_WAV - if (config.encodingFormat == ma_encoding_format_wav) { - result = ma_decoder_init_wav_from_file__internal(pFilePath, &config, pDecoder); - } - #endif - #ifdef MA_HAS_FLAC - if (config.encodingFormat == ma_encoding_format_flac) { - result = ma_decoder_init_flac_from_file__internal(pFilePath, &config, pDecoder); - } - #endif - #ifdef MA_HAS_MP3 - if (config.encodingFormat == ma_encoding_format_mp3) { - result = ma_decoder_init_mp3_from_file__internal(pFilePath, &config, pDecoder); - } - #endif - #ifdef MA_HAS_VORBIS - if (config.encodingFormat == ma_encoding_format_vorbis) { - result = ma_decoder_init_vorbis_from_file__internal(pFilePath, &config, pDecoder); - } - #endif - } - - if (result != MA_SUCCESS) { - /* Getting here means we weren't able to initialize a decoder of a specific encoding format. */ - - /* - We use trial and error to open a decoder. We prioritize custom decoders so that if they - implement the same encoding format they take priority over the built-in decoders. - */ - result = ma_decoder_init_custom_from_file__internal(pFilePath, &config, pDecoder); - - /* - If we get to this point and we still haven't found a decoder, and the caller has requested a - specific encoding format, there's no hope for it. Abort. - */ - if (result != MA_SUCCESS && config.encodingFormat != ma_encoding_format_unknown) { - return MA_NO_BACKEND; - } - - /* First try loading based on the file extension so we don't waste time opening and closing files. */ - #ifdef MA_HAS_WAV - if (result != MA_SUCCESS && ma_path_extension_equal(pFilePath, "wav")) { - result = ma_decoder_init_wav_from_file__internal(pFilePath, &config, pDecoder); - } - #endif - #ifdef MA_HAS_FLAC - if (result != MA_SUCCESS && ma_path_extension_equal(pFilePath, "flac")) { - result = ma_decoder_init_flac_from_file__internal(pFilePath, &config, pDecoder); - } - #endif - #ifdef MA_HAS_MP3 - if (result != MA_SUCCESS && ma_path_extension_equal(pFilePath, "mp3")) { - result = ma_decoder_init_mp3_from_file__internal(pFilePath, &config, pDecoder); - } - #endif - #ifdef MA_HAS_VORBIS - if (result != MA_SUCCESS && ma_path_extension_equal(pFilePath, "ogg")) { - result = ma_decoder_init_vorbis_from_file__internal(pFilePath, &config, pDecoder); - } - #endif - - /* - If we still haven't got a result just use trial and error. Custom decoders have already been attempted, so here we - need only iterate over our stock decoders. - */ - if (result != MA_SUCCESS) { - #ifdef MA_HAS_WAV - if (result != MA_SUCCESS) { - result = ma_decoder_init_wav_from_file__internal(pFilePath, &config, pDecoder); - } - #endif - #ifdef MA_HAS_FLAC - if (result != MA_SUCCESS) { - result = ma_decoder_init_flac_from_file__internal(pFilePath, &config, pDecoder); - } - #endif - #ifdef MA_HAS_MP3 - if (result != MA_SUCCESS) { - result = ma_decoder_init_mp3_from_file__internal(pFilePath, &config, pDecoder); - } - #endif - #ifdef MA_HAS_VORBIS - if (result != MA_SUCCESS) { - result = ma_decoder_init_vorbis_from_file__internal(pFilePath, &config, pDecoder); - } - #endif - } - } - - /* - If at this point we still haven't successfully initialized the decoder it most likely means - the backend doesn't have an implementation for loading from a file path. We'll try using - miniaudio's built-in file IO for loading file. - */ - if (result == MA_SUCCESS) { - /* Initialization was successful. Finish up. */ - result = ma_decoder__postinit(&config, pDecoder); - if (result != MA_SUCCESS) { - /* - The backend was initialized successfully, but for some reason post-initialization failed. This is most likely - due to an out of memory error. We're going to abort with an error here and not try to recover. - */ - if (pDecoder->pBackendVTable != NULL && pDecoder->pBackendVTable->onUninit != NULL) { - pDecoder->pBackendVTable->onUninit(pDecoder->pBackendUserData, &pDecoder->pBackend, &pDecoder->allocationCallbacks); - } - - return result; - } - } else { - /* Probably no implementation for loading from a file path. Use miniaudio's file IO instead. */ - result = ma_decoder_init_vfs(NULL, pFilePath, pConfig, pDecoder); - if (result != MA_SUCCESS) { - return result; - } - } - - return MA_SUCCESS; -} - -static ma_result ma_decoder__preinit_file_w(const wchar_t* pFilePath, const ma_decoder_config* pConfig, ma_decoder* pDecoder) -{ - ma_result result; - - result = ma_decoder__preinit(NULL, NULL, NULL, NULL, pConfig, pDecoder); - if (result != MA_SUCCESS) { - return result; - } - - if (pFilePath == NULL || pFilePath[0] == '\0') { - return MA_INVALID_ARGS; - } - - return MA_SUCCESS; -} - -MA_API ma_result ma_decoder_init_file_w(const wchar_t* pFilePath, const ma_decoder_config* pConfig, ma_decoder* pDecoder) -{ - ma_result result; - ma_decoder_config config; - - config = ma_decoder_config_init_copy(pConfig); - result = ma_decoder__preinit_file_w(pFilePath, &config, pDecoder); - if (result != MA_SUCCESS) { - return result; - } - - /* If the backend has support for loading from a file path we'll want to use that. If that all fails we'll fall back to the VFS path. */ - result = MA_NO_BACKEND; - - if (config.encodingFormat != ma_encoding_format_unknown) { - #ifdef MA_HAS_WAV - if (config.encodingFormat == ma_encoding_format_wav) { - result = ma_decoder_init_wav_from_file_w__internal(pFilePath, &config, pDecoder); - } - #endif - #ifdef MA_HAS_FLAC - if (config.encodingFormat == ma_encoding_format_flac) { - result = ma_decoder_init_flac_from_file_w__internal(pFilePath, &config, pDecoder); - } - #endif - #ifdef MA_HAS_MP3 - if (config.encodingFormat == ma_encoding_format_mp3) { - result = ma_decoder_init_mp3_from_file_w__internal(pFilePath, &config, pDecoder); - } - #endif - #ifdef MA_HAS_VORBIS - if (config.encodingFormat == ma_encoding_format_vorbis) { - result = ma_decoder_init_vorbis_from_file_w__internal(pFilePath, &config, pDecoder); - } - #endif - } - - if (result != MA_SUCCESS) { - /* Getting here means we weren't able to initialize a decoder of a specific encoding format. */ - - /* - We use trial and error to open a decoder. We prioritize custom decoders so that if they - implement the same encoding format they take priority over the built-in decoders. - */ - result = ma_decoder_init_custom_from_file_w__internal(pFilePath, &config, pDecoder); - - /* - If we get to this point and we still haven't found a decoder, and the caller has requested a - specific encoding format, there's no hope for it. Abort. - */ - if (result != MA_SUCCESS && config.encodingFormat != ma_encoding_format_unknown) { - return MA_NO_BACKEND; - } - - /* First try loading based on the file extension so we don't waste time opening and closing files. */ - #ifdef MA_HAS_WAV - if (result != MA_SUCCESS && ma_path_extension_equal_w(pFilePath, L"wav")) { - result = ma_decoder_init_wav_from_file_w__internal(pFilePath, &config, pDecoder); - } - #endif - #ifdef MA_HAS_FLAC - if (result != MA_SUCCESS && ma_path_extension_equal_w(pFilePath, L"flac")) { - result = ma_decoder_init_flac_from_file_w__internal(pFilePath, &config, pDecoder); - } - #endif - #ifdef MA_HAS_MP3 - if (result != MA_SUCCESS && ma_path_extension_equal_w(pFilePath, L"mp3")) { - result = ma_decoder_init_mp3_from_file_w__internal(pFilePath, &config, pDecoder); - } - #endif - #ifdef MA_HAS_VORBIS - if (result != MA_SUCCESS && ma_path_extension_equal_w(pFilePath, L"ogg")) { - result = ma_decoder_init_vorbis_from_file_w__internal(pFilePath, &config, pDecoder); - } - #endif - - /* - If we still haven't got a result just use trial and error. Custom decoders have already been attempted, so here we - need only iterate over our stock decoders. - */ - if (result != MA_SUCCESS) { - #ifdef MA_HAS_WAV - if (result != MA_SUCCESS) { - result = ma_decoder_init_wav_from_file_w__internal(pFilePath, &config, pDecoder); - } - #endif - #ifdef MA_HAS_FLAC - if (result != MA_SUCCESS) { - result = ma_decoder_init_flac_from_file_w__internal(pFilePath, &config, pDecoder); - } - #endif - #ifdef MA_HAS_MP3 - if (result != MA_SUCCESS) { - result = ma_decoder_init_mp3_from_file_w__internal(pFilePath, &config, pDecoder); - } - #endif - #ifdef MA_HAS_VORBIS - if (result != MA_SUCCESS) { - result = ma_decoder_init_vorbis_from_file_w__internal(pFilePath, &config, pDecoder); - } - #endif - } - } - - /* - If at this point we still haven't successfully initialized the decoder it most likely means - the backend doesn't have an implementation for loading from a file path. We'll try using - miniaudio's built-in file IO for loading file. - */ - if (result == MA_SUCCESS) { - /* Initialization was successful. Finish up. */ - result = ma_decoder__postinit(&config, pDecoder); - if (result != MA_SUCCESS) { - /* - The backend was initialized successfully, but for some reason post-initialization failed. This is most likely - due to an out of memory error. We're going to abort with an error here and not try to recover. - */ - if (pDecoder->pBackendVTable != NULL && pDecoder->pBackendVTable->onUninit != NULL) { - pDecoder->pBackendVTable->onUninit(pDecoder->pBackendUserData, &pDecoder->pBackend, &pDecoder->allocationCallbacks); - } - - return result; - } - } else { - /* Probably no implementation for loading from a file path. Use miniaudio's file IO instead. */ - result = ma_decoder_init_vfs_w(NULL, pFilePath, pConfig, pDecoder); - if (result != MA_SUCCESS) { - return result; - } - } - - return MA_SUCCESS; -} - -MA_API ma_result ma_decoder_uninit(ma_decoder* pDecoder) -{ - if (pDecoder == NULL) { - return MA_INVALID_ARGS; - } - - if (pDecoder->pBackend != NULL) { - if (pDecoder->pBackendVTable != NULL && pDecoder->pBackendVTable->onUninit != NULL) { - pDecoder->pBackendVTable->onUninit(pDecoder->pBackendUserData, pDecoder->pBackend, &pDecoder->allocationCallbacks); - } - } - - if (pDecoder->onRead == ma_decoder__on_read_vfs) { - ma_vfs_or_default_close(pDecoder->data.vfs.pVFS, pDecoder->data.vfs.file); - pDecoder->data.vfs.file = NULL; - } - - ma_data_converter_uninit(&pDecoder->converter, &pDecoder->allocationCallbacks); - ma_data_source_uninit(&pDecoder->ds); - - if (pDecoder->pInputCache != NULL) { - ma_free(pDecoder->pInputCache, &pDecoder->allocationCallbacks); - } - - return MA_SUCCESS; -} - -MA_API ma_result ma_decoder_read_pcm_frames(ma_decoder* pDecoder, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead) -{ - ma_result result = MA_SUCCESS; - ma_uint64 totalFramesReadOut; - void* pRunningFramesOut; - - if (pFramesRead != NULL) { - *pFramesRead = 0; /* Safety. */ - } - - if (frameCount == 0) { - return MA_INVALID_ARGS; - } - - if (pDecoder == NULL) { - return MA_INVALID_ARGS; - } - - if (pDecoder->pBackend == NULL) { - return MA_INVALID_OPERATION; - } - - /* Fast path. */ - if (pDecoder->converter.isPassthrough) { - result = ma_data_source_read_pcm_frames(pDecoder->pBackend, pFramesOut, frameCount, &totalFramesReadOut); - } else { - /* - Getting here means we need to do data conversion. If we're seeking forward and are _not_ doing resampling we can run this in a fast path. If we're doing resampling we - need to run through each sample because we need to ensure its internal cache is updated. - */ - if (pFramesOut == NULL && pDecoder->converter.hasResampler == MA_FALSE) { - result = ma_data_source_read_pcm_frames(pDecoder->pBackend, NULL, frameCount, &totalFramesReadOut); - } else { - /* Slow path. Need to run everything through the data converter. */ - ma_format internalFormat; - ma_uint32 internalChannels; - - totalFramesReadOut = 0; - pRunningFramesOut = pFramesOut; - - result = ma_data_source_get_data_format(pDecoder->pBackend, &internalFormat, &internalChannels, NULL, NULL, 0); - if (result != MA_SUCCESS) { - return result; /* Failed to retrieve the internal format and channel count. */ - } - - /* - We run a different path depending on whether or not we are using a heap-allocated - intermediary buffer or not. If the data converter does not support the calculation of - the required number of input frames, we'll use the heap-allocated path. Otherwise we'll - use the stack-allocated path. - */ - if (pDecoder->pInputCache != NULL) { - /* We don't have a way of determining the required number of input frames, so need to persistently store input data in a cache. */ - while (totalFramesReadOut < frameCount) { - ma_uint64 framesToReadThisIterationIn; - ma_uint64 framesToReadThisIterationOut; - - /* If there's any data available in the cache, that needs to get processed first. */ - if (pDecoder->inputCacheRemaining > 0) { - framesToReadThisIterationOut = (frameCount - totalFramesReadOut); - framesToReadThisIterationIn = framesToReadThisIterationOut; - if (framesToReadThisIterationIn > pDecoder->inputCacheRemaining) { - framesToReadThisIterationIn = pDecoder->inputCacheRemaining; - } - - result = ma_data_converter_process_pcm_frames(&pDecoder->converter, ma_offset_pcm_frames_ptr(pDecoder->pInputCache, pDecoder->inputCacheConsumed, internalFormat, internalChannels), &framesToReadThisIterationIn, pRunningFramesOut, &framesToReadThisIterationOut); - if (result != MA_SUCCESS) { - break; - } - - pDecoder->inputCacheConsumed += framesToReadThisIterationIn; - pDecoder->inputCacheRemaining -= framesToReadThisIterationIn; - - totalFramesReadOut += framesToReadThisIterationOut; - - if (pRunningFramesOut != NULL) { - pRunningFramesOut = ma_offset_ptr(pRunningFramesOut, framesToReadThisIterationOut * ma_get_bytes_per_frame(pDecoder->outputFormat, pDecoder->outputChannels)); - } - - if (framesToReadThisIterationIn == 0 && framesToReadThisIterationOut == 0) { - break; /* We're done. */ - } - } - - /* Getting here means there's no data in the cache and we need to fill it up from the data source. */ - if (pDecoder->inputCacheRemaining == 0) { - pDecoder->inputCacheConsumed = 0; - - result = ma_data_source_read_pcm_frames(pDecoder->pBackend, pDecoder->pInputCache, pDecoder->inputCacheCap, &pDecoder->inputCacheRemaining); - if (result != MA_SUCCESS) { - break; - } - } - } - } else { - /* We have a way of determining the required number of input frames so just use the stack. */ - while (totalFramesReadOut < frameCount) { - ma_uint8 pIntermediaryBuffer[MA_DATA_CONVERTER_STACK_BUFFER_SIZE]; /* In internal format. */ - ma_uint64 intermediaryBufferCap = sizeof(pIntermediaryBuffer) / ma_get_bytes_per_frame(internalFormat, internalChannels); - ma_uint64 framesToReadThisIterationIn; - ma_uint64 framesReadThisIterationIn; - ma_uint64 framesToReadThisIterationOut; - ma_uint64 framesReadThisIterationOut; - ma_uint64 requiredInputFrameCount; - - framesToReadThisIterationOut = (frameCount - totalFramesReadOut); - framesToReadThisIterationIn = framesToReadThisIterationOut; - if (framesToReadThisIterationIn > intermediaryBufferCap) { - framesToReadThisIterationIn = intermediaryBufferCap; - } - - ma_data_converter_get_required_input_frame_count(&pDecoder->converter, framesToReadThisIterationOut, &requiredInputFrameCount); - if (framesToReadThisIterationIn > requiredInputFrameCount) { - framesToReadThisIterationIn = requiredInputFrameCount; - } - - if (requiredInputFrameCount > 0) { - result = ma_data_source_read_pcm_frames(pDecoder->pBackend, pIntermediaryBuffer, framesToReadThisIterationIn, &framesReadThisIterationIn); - - /* - Note here that even if we've reached the end, we don't want to abort because there might be more output frames needing to be - generated from cached input data, which might happen if resampling is being performed. - */ - if (result != MA_SUCCESS && result != MA_AT_END) { - break; - } - } else { - framesReadThisIterationIn = 0; - pIntermediaryBuffer[0] = 0; /* <-- This is just to silence a static analysis warning. */ - } - - /* - At this point we have our decoded data in input format and now we need to convert to output format. Note that even if we didn't read any - input frames, we still want to try processing frames because there may some output frames generated from cached input data. - */ - framesReadThisIterationOut = framesToReadThisIterationOut; - result = ma_data_converter_process_pcm_frames(&pDecoder->converter, pIntermediaryBuffer, &framesReadThisIterationIn, pRunningFramesOut, &framesReadThisIterationOut); - if (result != MA_SUCCESS) { - break; - } - - totalFramesReadOut += framesReadThisIterationOut; - - if (pRunningFramesOut != NULL) { - pRunningFramesOut = ma_offset_ptr(pRunningFramesOut, framesReadThisIterationOut * ma_get_bytes_per_frame(pDecoder->outputFormat, pDecoder->outputChannels)); - } - - if (framesReadThisIterationIn == 0 && framesReadThisIterationOut == 0) { - break; /* We're done. */ - } - } - } - } - } - - pDecoder->readPointerInPCMFrames += totalFramesReadOut; - - if (pFramesRead != NULL) { - *pFramesRead = totalFramesReadOut; - } - - if (result == MA_SUCCESS && totalFramesReadOut == 0) { - result = MA_AT_END; - } - - return result; -} - -MA_API ma_result ma_decoder_seek_to_pcm_frame(ma_decoder* pDecoder, ma_uint64 frameIndex) -{ - if (pDecoder == NULL) { - return MA_INVALID_ARGS; - } - - if (pDecoder->pBackend != NULL) { - ma_result result; - ma_uint64 internalFrameIndex; - ma_uint32 internalSampleRate; - ma_uint64 currentFrameIndex; - - result = ma_data_source_get_data_format(pDecoder->pBackend, NULL, NULL, &internalSampleRate, NULL, 0); - if (result != MA_SUCCESS) { - return result; /* Failed to retrieve the internal sample rate. */ - } - - if (internalSampleRate == pDecoder->outputSampleRate) { - internalFrameIndex = frameIndex; - } else { - internalFrameIndex = ma_calculate_frame_count_after_resampling(internalSampleRate, pDecoder->outputSampleRate, frameIndex); - } - - /* Only seek if we're requesting a different frame to what we're currently sitting on. */ - ma_data_source_get_cursor_in_pcm_frames(pDecoder->pBackend, ¤tFrameIndex); - if (currentFrameIndex != internalFrameIndex) { - result = ma_data_source_seek_to_pcm_frame(pDecoder->pBackend, internalFrameIndex); - if (result == MA_SUCCESS) { - pDecoder->readPointerInPCMFrames = frameIndex; - } - - /* Reset the data converter so that any cached data in the resampler is cleared. */ - ma_data_converter_reset(&pDecoder->converter); - } - - return result; - } - - /* Should never get here, but if we do it means onSeekToPCMFrame was not set by the backend. */ - return MA_INVALID_ARGS; -} - -MA_API ma_result ma_decoder_get_data_format(ma_decoder* pDecoder, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate, ma_channel* pChannelMap, size_t channelMapCap) -{ - if (pDecoder == NULL) { - return MA_INVALID_ARGS; - } - - if (pFormat != NULL) { - *pFormat = pDecoder->outputFormat; - } - - if (pChannels != NULL) { - *pChannels = pDecoder->outputChannels; - } - - if (pSampleRate != NULL) { - *pSampleRate = pDecoder->outputSampleRate; - } - - if (pChannelMap != NULL) { - ma_data_converter_get_output_channel_map(&pDecoder->converter, pChannelMap, channelMapCap); - } - - return MA_SUCCESS; -} - -MA_API ma_result ma_decoder_get_cursor_in_pcm_frames(ma_decoder* pDecoder, ma_uint64* pCursor) -{ - if (pCursor == NULL) { - return MA_INVALID_ARGS; - } - - *pCursor = 0; - - if (pDecoder == NULL) { - return MA_INVALID_ARGS; - } - - *pCursor = pDecoder->readPointerInPCMFrames; - - return MA_SUCCESS; -} - -MA_API ma_result ma_decoder_get_length_in_pcm_frames(ma_decoder* pDecoder, ma_uint64* pLength) -{ - if (pLength == NULL) { - return MA_INVALID_ARGS; - } - - *pLength = 0; - - if (pDecoder == NULL) { - return MA_INVALID_ARGS; - } - - if (pDecoder->pBackend != NULL) { - ma_result result; - ma_uint64 internalLengthInPCMFrames; - ma_uint32 internalSampleRate; - - result = ma_data_source_get_length_in_pcm_frames(pDecoder->pBackend, &internalLengthInPCMFrames); - if (result != MA_SUCCESS) { - return result; /* Failed to retrieve the internal length. */ - } - - result = ma_data_source_get_data_format(pDecoder->pBackend, NULL, NULL, &internalSampleRate, NULL, 0); - if (result != MA_SUCCESS) { - return result; /* Failed to retrieve the internal sample rate. */ - } - - if (internalSampleRate == pDecoder->outputSampleRate) { - *pLength = internalLengthInPCMFrames; - } else { - *pLength = ma_calculate_frame_count_after_resampling(pDecoder->outputSampleRate, internalSampleRate, internalLengthInPCMFrames); - } - - return MA_SUCCESS; - } else { - return MA_NO_BACKEND; - } -} - -MA_API ma_result ma_decoder_get_available_frames(ma_decoder* pDecoder, ma_uint64* pAvailableFrames) -{ - ma_result result; - ma_uint64 totalFrameCount; - - if (pAvailableFrames == NULL) { - return MA_INVALID_ARGS; - } - - *pAvailableFrames = 0; - - if (pDecoder == NULL) { - return MA_INVALID_ARGS; - } - - result = ma_decoder_get_length_in_pcm_frames(pDecoder, &totalFrameCount); - if (result != MA_SUCCESS) { - return result; - } - - if (totalFrameCount <= pDecoder->readPointerInPCMFrames) { - *pAvailableFrames = 0; - } else { - *pAvailableFrames = totalFrameCount - pDecoder->readPointerInPCMFrames; - } - - return MA_SUCCESS; -} - - -static ma_result ma_decoder__full_decode_and_uninit(ma_decoder* pDecoder, ma_decoder_config* pConfigOut, ma_uint64* pFrameCountOut, void** ppPCMFramesOut) -{ - ma_result result; - ma_uint64 totalFrameCount; - ma_uint64 bpf; - ma_uint64 dataCapInFrames; - void* pPCMFramesOut; - - MA_ASSERT(pDecoder != NULL); - - totalFrameCount = 0; - bpf = ma_get_bytes_per_frame(pDecoder->outputFormat, pDecoder->outputChannels); - - /* The frame count is unknown until we try reading. Thus, we just run in a loop. */ - dataCapInFrames = 0; - pPCMFramesOut = NULL; - for (;;) { - ma_uint64 frameCountToTryReading; - ma_uint64 framesJustRead; - - /* Make room if there's not enough. */ - if (totalFrameCount == dataCapInFrames) { - void* pNewPCMFramesOut; - ma_uint64 newDataCapInFrames = dataCapInFrames*2; - if (newDataCapInFrames == 0) { - newDataCapInFrames = 4096; - } - - if ((newDataCapInFrames * bpf) > MA_SIZE_MAX) { - ma_free(pPCMFramesOut, &pDecoder->allocationCallbacks); - return MA_TOO_BIG; - } - - pNewPCMFramesOut = (void*)ma_realloc(pPCMFramesOut, (size_t)(newDataCapInFrames * bpf), &pDecoder->allocationCallbacks); - if (pNewPCMFramesOut == NULL) { - ma_free(pPCMFramesOut, &pDecoder->allocationCallbacks); - return MA_OUT_OF_MEMORY; - } - - dataCapInFrames = newDataCapInFrames; - pPCMFramesOut = pNewPCMFramesOut; - } - - frameCountToTryReading = dataCapInFrames - totalFrameCount; - MA_ASSERT(frameCountToTryReading > 0); - - result = ma_decoder_read_pcm_frames(pDecoder, (ma_uint8*)pPCMFramesOut + (totalFrameCount * bpf), frameCountToTryReading, &framesJustRead); - totalFrameCount += framesJustRead; - - if (result != MA_SUCCESS) { - break; - } - - if (framesJustRead < frameCountToTryReading) { - break; - } - } - - - if (pConfigOut != NULL) { - pConfigOut->format = pDecoder->outputFormat; - pConfigOut->channels = pDecoder->outputChannels; - pConfigOut->sampleRate = pDecoder->outputSampleRate; - } - - if (ppPCMFramesOut != NULL) { - *ppPCMFramesOut = pPCMFramesOut; - } else { - ma_free(pPCMFramesOut, &pDecoder->allocationCallbacks); - } - - if (pFrameCountOut != NULL) { - *pFrameCountOut = totalFrameCount; - } - - ma_decoder_uninit(pDecoder); - return MA_SUCCESS; -} - -MA_API ma_result ma_decode_from_vfs(ma_vfs* pVFS, const char* pFilePath, ma_decoder_config* pConfig, ma_uint64* pFrameCountOut, void** ppPCMFramesOut) -{ - ma_result result; - ma_decoder_config config; - ma_decoder decoder; - - if (pFrameCountOut != NULL) { - *pFrameCountOut = 0; - } - if (ppPCMFramesOut != NULL) { - *ppPCMFramesOut = NULL; - } - - config = ma_decoder_config_init_copy(pConfig); - - result = ma_decoder_init_vfs(pVFS, pFilePath, &config, &decoder); - if (result != MA_SUCCESS) { - return result; - } - - result = ma_decoder__full_decode_and_uninit(&decoder, pConfig, pFrameCountOut, ppPCMFramesOut); - - return result; -} - -MA_API ma_result ma_decode_file(const char* pFilePath, ma_decoder_config* pConfig, ma_uint64* pFrameCountOut, void** ppPCMFramesOut) -{ - return ma_decode_from_vfs(NULL, pFilePath, pConfig, pFrameCountOut, ppPCMFramesOut); -} - -MA_API ma_result ma_decode_memory(const void* pData, size_t dataSize, ma_decoder_config* pConfig, ma_uint64* pFrameCountOut, void** ppPCMFramesOut) -{ - ma_decoder_config config; - ma_decoder decoder; - ma_result result; - - if (pFrameCountOut != NULL) { - *pFrameCountOut = 0; - } - if (ppPCMFramesOut != NULL) { - *ppPCMFramesOut = NULL; - } - - if (pData == NULL || dataSize == 0) { - return MA_INVALID_ARGS; - } - - config = ma_decoder_config_init_copy(pConfig); - - result = ma_decoder_init_memory(pData, dataSize, &config, &decoder); - if (result != MA_SUCCESS) { - return result; - } - - return ma_decoder__full_decode_and_uninit(&decoder, pConfig, pFrameCountOut, ppPCMFramesOut); -} -#endif /* MA_NO_DECODING */ - - -#ifndef MA_NO_ENCODING - -#if defined(MA_HAS_WAV) -static size_t ma_encoder__internal_on_write_wav(void* pUserData, const void* pData, size_t bytesToWrite) -{ - ma_encoder* pEncoder = (ma_encoder*)pUserData; - size_t bytesWritten = 0; - - MA_ASSERT(pEncoder != NULL); - - pEncoder->onWrite(pEncoder, pData, bytesToWrite, &bytesWritten); - return bytesWritten; -} - -static ma_bool32 ma_encoder__internal_on_seek_wav(void* pUserData, int offset, ma_dr_wav_seek_origin origin) -{ - ma_encoder* pEncoder = (ma_encoder*)pUserData; - ma_result result; - - MA_ASSERT(pEncoder != NULL); - - result = pEncoder->onSeek(pEncoder, offset, (origin == ma_dr_wav_seek_origin_start) ? ma_seek_origin_start : ma_seek_origin_current); - if (result != MA_SUCCESS) { - return MA_FALSE; - } else { - return MA_TRUE; - } -} - -static ma_result ma_encoder__on_init_wav(ma_encoder* pEncoder) -{ - ma_dr_wav_data_format wavFormat; - ma_allocation_callbacks allocationCallbacks; - ma_dr_wav* pWav; - - MA_ASSERT(pEncoder != NULL); - - pWav = (ma_dr_wav*)ma_malloc(sizeof(*pWav), &pEncoder->config.allocationCallbacks); - if (pWav == NULL) { - return MA_OUT_OF_MEMORY; - } - - wavFormat.container = ma_dr_wav_container_riff; - wavFormat.channels = pEncoder->config.channels; - wavFormat.sampleRate = pEncoder->config.sampleRate; - wavFormat.bitsPerSample = ma_get_bytes_per_sample(pEncoder->config.format) * 8; - if (pEncoder->config.format == ma_format_f32) { - wavFormat.format = MA_DR_WAVE_FORMAT_IEEE_FLOAT; - } else { - wavFormat.format = MA_DR_WAVE_FORMAT_PCM; - } - - allocationCallbacks.pUserData = pEncoder->config.allocationCallbacks.pUserData; - allocationCallbacks.onMalloc = pEncoder->config.allocationCallbacks.onMalloc; - allocationCallbacks.onRealloc = pEncoder->config.allocationCallbacks.onRealloc; - allocationCallbacks.onFree = pEncoder->config.allocationCallbacks.onFree; - - if (!ma_dr_wav_init_write(pWav, &wavFormat, ma_encoder__internal_on_write_wav, ma_encoder__internal_on_seek_wav, pEncoder, &allocationCallbacks)) { - return MA_ERROR; - } - - pEncoder->pInternalEncoder = pWav; - - return MA_SUCCESS; -} - -static void ma_encoder__on_uninit_wav(ma_encoder* pEncoder) -{ - ma_dr_wav* pWav; - - MA_ASSERT(pEncoder != NULL); - - pWav = (ma_dr_wav*)pEncoder->pInternalEncoder; - MA_ASSERT(pWav != NULL); - - ma_dr_wav_uninit(pWav); - ma_free(pWav, &pEncoder->config.allocationCallbacks); -} - -static ma_result ma_encoder__on_write_pcm_frames_wav(ma_encoder* pEncoder, const void* pFramesIn, ma_uint64 frameCount, ma_uint64* pFramesWritten) -{ - ma_dr_wav* pWav; - ma_uint64 framesWritten; - - MA_ASSERT(pEncoder != NULL); - - pWav = (ma_dr_wav*)pEncoder->pInternalEncoder; - MA_ASSERT(pWav != NULL); - - framesWritten = ma_dr_wav_write_pcm_frames(pWav, frameCount, pFramesIn); - - if (pFramesWritten != NULL) { - *pFramesWritten = framesWritten; - } - - return MA_SUCCESS; -} -#endif - -MA_API ma_encoder_config ma_encoder_config_init(ma_encoding_format encodingFormat, ma_format format, ma_uint32 channels, ma_uint32 sampleRate) -{ - ma_encoder_config config; - - MA_ZERO_OBJECT(&config); - config.encodingFormat = encodingFormat; - config.format = format; - config.channels = channels; - config.sampleRate = sampleRate; - - return config; -} - -MA_API ma_result ma_encoder_preinit(const ma_encoder_config* pConfig, ma_encoder* pEncoder) -{ - ma_result result; - - if (pEncoder == NULL) { - return MA_INVALID_ARGS; - } - - MA_ZERO_OBJECT(pEncoder); - - if (pConfig == NULL) { - return MA_INVALID_ARGS; - } - - if (pConfig->format == ma_format_unknown || pConfig->channels == 0 || pConfig->sampleRate == 0) { - return MA_INVALID_ARGS; - } - - pEncoder->config = *pConfig; - - result = ma_allocation_callbacks_init_copy(&pEncoder->config.allocationCallbacks, &pConfig->allocationCallbacks); - if (result != MA_SUCCESS) { - return result; - } - - return MA_SUCCESS; -} - -MA_API ma_result ma_encoder_init__internal(ma_encoder_write_proc onWrite, ma_encoder_seek_proc onSeek, void* pUserData, ma_encoder* pEncoder) -{ - ma_result result = MA_SUCCESS; - - /* This assumes ma_encoder_preinit() has been called prior. */ - MA_ASSERT(pEncoder != NULL); - - if (onWrite == NULL || onSeek == NULL) { - return MA_INVALID_ARGS; - } - - pEncoder->onWrite = onWrite; - pEncoder->onSeek = onSeek; - pEncoder->pUserData = pUserData; - - switch (pEncoder->config.encodingFormat) - { - case ma_encoding_format_wav: - { - #if defined(MA_HAS_WAV) - pEncoder->onInit = ma_encoder__on_init_wav; - pEncoder->onUninit = ma_encoder__on_uninit_wav; - pEncoder->onWritePCMFrames = ma_encoder__on_write_pcm_frames_wav; - #else - result = MA_NO_BACKEND; - #endif - } break; - - default: - { - result = MA_INVALID_ARGS; - } break; - } - - /* Getting here means we should have our backend callbacks set up. */ - if (result == MA_SUCCESS) { - result = pEncoder->onInit(pEncoder); - } - - return result; -} - -static ma_result ma_encoder__on_write_vfs(ma_encoder* pEncoder, const void* pBufferIn, size_t bytesToWrite, size_t* pBytesWritten) -{ - return ma_vfs_or_default_write(pEncoder->data.vfs.pVFS, pEncoder->data.vfs.file, pBufferIn, bytesToWrite, pBytesWritten); -} - -static ma_result ma_encoder__on_seek_vfs(ma_encoder* pEncoder, ma_int64 offset, ma_seek_origin origin) -{ - return ma_vfs_or_default_seek(pEncoder->data.vfs.pVFS, pEncoder->data.vfs.file, offset, origin); -} - -MA_API ma_result ma_encoder_init_vfs(ma_vfs* pVFS, const char* pFilePath, const ma_encoder_config* pConfig, ma_encoder* pEncoder) -{ - ma_result result; - ma_vfs_file file; - - result = ma_encoder_preinit(pConfig, pEncoder); - if (result != MA_SUCCESS) { - return result; - } - - /* Now open the file. If this fails we don't need to uninitialize the encoder. */ - result = ma_vfs_or_default_open(pVFS, pFilePath, MA_OPEN_MODE_WRITE, &file); - if (result != MA_SUCCESS) { - return result; - } - - pEncoder->data.vfs.pVFS = pVFS; - pEncoder->data.vfs.file = file; - - result = ma_encoder_init__internal(ma_encoder__on_write_vfs, ma_encoder__on_seek_vfs, NULL, pEncoder); - if (result != MA_SUCCESS) { - ma_vfs_or_default_close(pVFS, file); - return result; - } - - return MA_SUCCESS; -} - -MA_API ma_result ma_encoder_init_vfs_w(ma_vfs* pVFS, const wchar_t* pFilePath, const ma_encoder_config* pConfig, ma_encoder* pEncoder) -{ - ma_result result; - ma_vfs_file file; - - result = ma_encoder_preinit(pConfig, pEncoder); - if (result != MA_SUCCESS) { - return result; - } - - /* Now open the file. If this fails we don't need to uninitialize the encoder. */ - result = ma_vfs_or_default_open_w(pVFS, pFilePath, MA_OPEN_MODE_WRITE, &file); - if (result != MA_SUCCESS) { - return result; - } - - pEncoder->data.vfs.pVFS = pVFS; - pEncoder->data.vfs.file = file; - - result = ma_encoder_init__internal(ma_encoder__on_write_vfs, ma_encoder__on_seek_vfs, NULL, pEncoder); - if (result != MA_SUCCESS) { - ma_vfs_or_default_close(pVFS, file); - return result; - } - - return MA_SUCCESS; -} - -MA_API ma_result ma_encoder_init_file(const char* pFilePath, const ma_encoder_config* pConfig, ma_encoder* pEncoder) -{ - return ma_encoder_init_vfs(NULL, pFilePath, pConfig, pEncoder); -} - -MA_API ma_result ma_encoder_init_file_w(const wchar_t* pFilePath, const ma_encoder_config* pConfig, ma_encoder* pEncoder) -{ - return ma_encoder_init_vfs_w(NULL, pFilePath, pConfig, pEncoder); -} - -MA_API ma_result ma_encoder_init(ma_encoder_write_proc onWrite, ma_encoder_seek_proc onSeek, void* pUserData, const ma_encoder_config* pConfig, ma_encoder* pEncoder) -{ - ma_result result; - - result = ma_encoder_preinit(pConfig, pEncoder); - if (result != MA_SUCCESS) { - return result; - } - - return ma_encoder_init__internal(onWrite, onSeek, pUserData, pEncoder); -} - - -MA_API void ma_encoder_uninit(ma_encoder* pEncoder) -{ - if (pEncoder == NULL) { - return; - } - - if (pEncoder->onUninit) { - pEncoder->onUninit(pEncoder); - } - - /* If we have a file handle, close it. */ - if (pEncoder->onWrite == ma_encoder__on_write_vfs) { - ma_vfs_or_default_close(pEncoder->data.vfs.pVFS, pEncoder->data.vfs.file); - pEncoder->data.vfs.file = NULL; - } -} - - -MA_API ma_result ma_encoder_write_pcm_frames(ma_encoder* pEncoder, const void* pFramesIn, ma_uint64 frameCount, ma_uint64* pFramesWritten) -{ - if (pFramesWritten != NULL) { - *pFramesWritten = 0; - } - - if (pEncoder == NULL || pFramesIn == NULL) { - return MA_INVALID_ARGS; - } - - return pEncoder->onWritePCMFrames(pEncoder, pFramesIn, frameCount, pFramesWritten); -} -#endif /* MA_NO_ENCODING */ - - - -/************************************************************************************************************************************************************** - -Generation - -**************************************************************************************************************************************************************/ -#ifndef MA_NO_GENERATION -MA_API ma_waveform_config ma_waveform_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRate, ma_waveform_type type, double amplitude, double frequency) -{ - ma_waveform_config config; - - MA_ZERO_OBJECT(&config); - config.format = format; - config.channels = channels; - config.sampleRate = sampleRate; - config.type = type; - config.amplitude = amplitude; - config.frequency = frequency; - - return config; -} - -static ma_result ma_waveform__data_source_on_read(ma_data_source* pDataSource, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead) -{ - return ma_waveform_read_pcm_frames((ma_waveform*)pDataSource, pFramesOut, frameCount, pFramesRead); -} - -static ma_result ma_waveform__data_source_on_seek(ma_data_source* pDataSource, ma_uint64 frameIndex) -{ - return ma_waveform_seek_to_pcm_frame((ma_waveform*)pDataSource, frameIndex); -} - -static ma_result ma_waveform__data_source_on_get_data_format(ma_data_source* pDataSource, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate, ma_channel* pChannelMap, size_t channelMapCap) -{ - ma_waveform* pWaveform = (ma_waveform*)pDataSource; - - *pFormat = pWaveform->config.format; - *pChannels = pWaveform->config.channels; - *pSampleRate = pWaveform->config.sampleRate; - ma_channel_map_init_standard(ma_standard_channel_map_default, pChannelMap, channelMapCap, pWaveform->config.channels); - - return MA_SUCCESS; -} - -static ma_result ma_waveform__data_source_on_get_cursor(ma_data_source* pDataSource, ma_uint64* pCursor) -{ - ma_waveform* pWaveform = (ma_waveform*)pDataSource; - - *pCursor = (ma_uint64)(pWaveform->time / pWaveform->advance); - - return MA_SUCCESS; -} - -static double ma_waveform__calculate_advance(ma_uint32 sampleRate, double frequency) -{ - return (1.0 / (sampleRate / frequency)); -} - -static void ma_waveform__update_advance(ma_waveform* pWaveform) -{ - pWaveform->advance = ma_waveform__calculate_advance(pWaveform->config.sampleRate, pWaveform->config.frequency); -} - -static ma_data_source_vtable g_ma_waveform_data_source_vtable = -{ - ma_waveform__data_source_on_read, - ma_waveform__data_source_on_seek, - ma_waveform__data_source_on_get_data_format, - ma_waveform__data_source_on_get_cursor, - NULL, /* onGetLength. There's no notion of a length in waveforms. */ - NULL, /* onSetLooping */ - 0 -}; - -MA_API ma_result ma_waveform_init(const ma_waveform_config* pConfig, ma_waveform* pWaveform) -{ - ma_result result; - ma_data_source_config dataSourceConfig; - - if (pWaveform == NULL) { - return MA_INVALID_ARGS; - } - - MA_ZERO_OBJECT(pWaveform); - - dataSourceConfig = ma_data_source_config_init(); - dataSourceConfig.vtable = &g_ma_waveform_data_source_vtable; - - result = ma_data_source_init(&dataSourceConfig, &pWaveform->ds); - if (result != MA_SUCCESS) { - return result; - } - - pWaveform->config = *pConfig; - pWaveform->advance = ma_waveform__calculate_advance(pWaveform->config.sampleRate, pWaveform->config.frequency); - pWaveform->time = 0; - - return MA_SUCCESS; -} - -MA_API void ma_waveform_uninit(ma_waveform* pWaveform) -{ - if (pWaveform == NULL) { - return; - } - - ma_data_source_uninit(&pWaveform->ds); -} - -MA_API ma_result ma_waveform_set_amplitude(ma_waveform* pWaveform, double amplitude) -{ - if (pWaveform == NULL) { - return MA_INVALID_ARGS; - } - - pWaveform->config.amplitude = amplitude; - return MA_SUCCESS; -} - -MA_API ma_result ma_waveform_set_frequency(ma_waveform* pWaveform, double frequency) -{ - if (pWaveform == NULL) { - return MA_INVALID_ARGS; - } - - pWaveform->config.frequency = frequency; - ma_waveform__update_advance(pWaveform); - - return MA_SUCCESS; -} - -MA_API ma_result ma_waveform_set_type(ma_waveform* pWaveform, ma_waveform_type type) -{ - if (pWaveform == NULL) { - return MA_INVALID_ARGS; - } - - pWaveform->config.type = type; - return MA_SUCCESS; -} - -MA_API ma_result ma_waveform_set_sample_rate(ma_waveform* pWaveform, ma_uint32 sampleRate) -{ - if (pWaveform == NULL) { - return MA_INVALID_ARGS; - } - - pWaveform->config.sampleRate = sampleRate; - ma_waveform__update_advance(pWaveform); - - return MA_SUCCESS; -} - -static float ma_waveform_sine_f32(double time, double amplitude) -{ - return (float)(ma_sind(MA_TAU_D * time) * amplitude); -} - -static ma_int16 ma_waveform_sine_s16(double time, double amplitude) -{ - return ma_pcm_sample_f32_to_s16(ma_waveform_sine_f32(time, amplitude)); -} - -static float ma_waveform_square_f32(double time, double dutyCycle, double amplitude) -{ - double f = time - (ma_int64)time; - double r; - - if (f < dutyCycle) { - r = amplitude; - } else { - r = -amplitude; - } - - return (float)r; -} - -static ma_int16 ma_waveform_square_s16(double time, double dutyCycle, double amplitude) -{ - return ma_pcm_sample_f32_to_s16(ma_waveform_square_f32(time, dutyCycle, amplitude)); -} - -static float ma_waveform_triangle_f32(double time, double amplitude) -{ - double f = time - (ma_int64)time; - double r; - - r = 2 * ma_abs(2 * (f - 0.5)) - 1; - - return (float)(r * amplitude); -} - -static ma_int16 ma_waveform_triangle_s16(double time, double amplitude) -{ - return ma_pcm_sample_f32_to_s16(ma_waveform_triangle_f32(time, amplitude)); -} - -static float ma_waveform_sawtooth_f32(double time, double amplitude) -{ - double f = time - (ma_int64)time; - double r; - - r = 2 * (f - 0.5); - - return (float)(r * amplitude); -} - -static ma_int16 ma_waveform_sawtooth_s16(double time, double amplitude) -{ - return ma_pcm_sample_f32_to_s16(ma_waveform_sawtooth_f32(time, amplitude)); -} - -static void ma_waveform_read_pcm_frames__sine(ma_waveform* pWaveform, void* pFramesOut, ma_uint64 frameCount) -{ - ma_uint64 iFrame; - ma_uint64 iChannel; - ma_uint32 bps = ma_get_bytes_per_sample(pWaveform->config.format); - ma_uint32 bpf = bps * pWaveform->config.channels; - - MA_ASSERT(pWaveform != NULL); - MA_ASSERT(pFramesOut != NULL); - - if (pWaveform->config.format == ma_format_f32) { - float* pFramesOutF32 = (float*)pFramesOut; - for (iFrame = 0; iFrame < frameCount; iFrame += 1) { - float s = ma_waveform_sine_f32(pWaveform->time, pWaveform->config.amplitude); - pWaveform->time += pWaveform->advance; - - for (iChannel = 0; iChannel < pWaveform->config.channels; iChannel += 1) { - pFramesOutF32[iFrame*pWaveform->config.channels + iChannel] = s; - } - } - } else if (pWaveform->config.format == ma_format_s16) { - ma_int16* pFramesOutS16 = (ma_int16*)pFramesOut; - for (iFrame = 0; iFrame < frameCount; iFrame += 1) { - ma_int16 s = ma_waveform_sine_s16(pWaveform->time, pWaveform->config.amplitude); - pWaveform->time += pWaveform->advance; - - for (iChannel = 0; iChannel < pWaveform->config.channels; iChannel += 1) { - pFramesOutS16[iFrame*pWaveform->config.channels + iChannel] = s; - } - } - } else { - for (iFrame = 0; iFrame < frameCount; iFrame += 1) { - float s = ma_waveform_sine_f32(pWaveform->time, pWaveform->config.amplitude); - pWaveform->time += pWaveform->advance; - - for (iChannel = 0; iChannel < pWaveform->config.channels; iChannel += 1) { - ma_pcm_convert(ma_offset_ptr(pFramesOut, iFrame*bpf + iChannel*bps), pWaveform->config.format, &s, ma_format_f32, 1, ma_dither_mode_none); - } - } - } -} - -static void ma_waveform_read_pcm_frames__square(ma_waveform* pWaveform, double dutyCycle, void* pFramesOut, ma_uint64 frameCount) -{ - ma_uint64 iFrame; - ma_uint64 iChannel; - ma_uint32 bps = ma_get_bytes_per_sample(pWaveform->config.format); - ma_uint32 bpf = bps * pWaveform->config.channels; - - MA_ASSERT(pWaveform != NULL); - MA_ASSERT(pFramesOut != NULL); - - if (pWaveform->config.format == ma_format_f32) { - float* pFramesOutF32 = (float*)pFramesOut; - for (iFrame = 0; iFrame < frameCount; iFrame += 1) { - float s = ma_waveform_square_f32(pWaveform->time, dutyCycle, pWaveform->config.amplitude); - pWaveform->time += pWaveform->advance; - - for (iChannel = 0; iChannel < pWaveform->config.channels; iChannel += 1) { - pFramesOutF32[iFrame*pWaveform->config.channels + iChannel] = s; - } - } - } else if (pWaveform->config.format == ma_format_s16) { - ma_int16* pFramesOutS16 = (ma_int16*)pFramesOut; - for (iFrame = 0; iFrame < frameCount; iFrame += 1) { - ma_int16 s = ma_waveform_square_s16(pWaveform->time, dutyCycle, pWaveform->config.amplitude); - pWaveform->time += pWaveform->advance; - - for (iChannel = 0; iChannel < pWaveform->config.channels; iChannel += 1) { - pFramesOutS16[iFrame*pWaveform->config.channels + iChannel] = s; - } - } - } else { - for (iFrame = 0; iFrame < frameCount; iFrame += 1) { - float s = ma_waveform_square_f32(pWaveform->time, dutyCycle, pWaveform->config.amplitude); - pWaveform->time += pWaveform->advance; - - for (iChannel = 0; iChannel < pWaveform->config.channels; iChannel += 1) { - ma_pcm_convert(ma_offset_ptr(pFramesOut, iFrame*bpf + iChannel*bps), pWaveform->config.format, &s, ma_format_f32, 1, ma_dither_mode_none); - } - } - } -} - -static void ma_waveform_read_pcm_frames__triangle(ma_waveform* pWaveform, void* pFramesOut, ma_uint64 frameCount) -{ - ma_uint64 iFrame; - ma_uint64 iChannel; - ma_uint32 bps = ma_get_bytes_per_sample(pWaveform->config.format); - ma_uint32 bpf = bps * pWaveform->config.channels; - - MA_ASSERT(pWaveform != NULL); - MA_ASSERT(pFramesOut != NULL); - - if (pWaveform->config.format == ma_format_f32) { - float* pFramesOutF32 = (float*)pFramesOut; - for (iFrame = 0; iFrame < frameCount; iFrame += 1) { - float s = ma_waveform_triangle_f32(pWaveform->time, pWaveform->config.amplitude); - pWaveform->time += pWaveform->advance; - - for (iChannel = 0; iChannel < pWaveform->config.channels; iChannel += 1) { - pFramesOutF32[iFrame*pWaveform->config.channels + iChannel] = s; - } - } - } else if (pWaveform->config.format == ma_format_s16) { - ma_int16* pFramesOutS16 = (ma_int16*)pFramesOut; - for (iFrame = 0; iFrame < frameCount; iFrame += 1) { - ma_int16 s = ma_waveform_triangle_s16(pWaveform->time, pWaveform->config.amplitude); - pWaveform->time += pWaveform->advance; - - for (iChannel = 0; iChannel < pWaveform->config.channels; iChannel += 1) { - pFramesOutS16[iFrame*pWaveform->config.channels + iChannel] = s; - } - } - } else { - for (iFrame = 0; iFrame < frameCount; iFrame += 1) { - float s = ma_waveform_triangle_f32(pWaveform->time, pWaveform->config.amplitude); - pWaveform->time += pWaveform->advance; - - for (iChannel = 0; iChannel < pWaveform->config.channels; iChannel += 1) { - ma_pcm_convert(ma_offset_ptr(pFramesOut, iFrame*bpf + iChannel*bps), pWaveform->config.format, &s, ma_format_f32, 1, ma_dither_mode_none); - } - } - } -} - -static void ma_waveform_read_pcm_frames__sawtooth(ma_waveform* pWaveform, void* pFramesOut, ma_uint64 frameCount) -{ - ma_uint64 iFrame; - ma_uint64 iChannel; - ma_uint32 bps = ma_get_bytes_per_sample(pWaveform->config.format); - ma_uint32 bpf = bps * pWaveform->config.channels; - - MA_ASSERT(pWaveform != NULL); - MA_ASSERT(pFramesOut != NULL); - - if (pWaveform->config.format == ma_format_f32) { - float* pFramesOutF32 = (float*)pFramesOut; - for (iFrame = 0; iFrame < frameCount; iFrame += 1) { - float s = ma_waveform_sawtooth_f32(pWaveform->time, pWaveform->config.amplitude); - pWaveform->time += pWaveform->advance; - - for (iChannel = 0; iChannel < pWaveform->config.channels; iChannel += 1) { - pFramesOutF32[iFrame*pWaveform->config.channels + iChannel] = s; - } - } - } else if (pWaveform->config.format == ma_format_s16) { - ma_int16* pFramesOutS16 = (ma_int16*)pFramesOut; - for (iFrame = 0; iFrame < frameCount; iFrame += 1) { - ma_int16 s = ma_waveform_sawtooth_s16(pWaveform->time, pWaveform->config.amplitude); - pWaveform->time += pWaveform->advance; - - for (iChannel = 0; iChannel < pWaveform->config.channels; iChannel += 1) { - pFramesOutS16[iFrame*pWaveform->config.channels + iChannel] = s; - } - } - } else { - for (iFrame = 0; iFrame < frameCount; iFrame += 1) { - float s = ma_waveform_sawtooth_f32(pWaveform->time, pWaveform->config.amplitude); - pWaveform->time += pWaveform->advance; - - for (iChannel = 0; iChannel < pWaveform->config.channels; iChannel += 1) { - ma_pcm_convert(ma_offset_ptr(pFramesOut, iFrame*bpf + iChannel*bps), pWaveform->config.format, &s, ma_format_f32, 1, ma_dither_mode_none); - } - } - } -} - -MA_API ma_result ma_waveform_read_pcm_frames(ma_waveform* pWaveform, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead) -{ - if (pFramesRead != NULL) { - *pFramesRead = 0; - } - - if (frameCount == 0) { - return MA_INVALID_ARGS; - } - - if (pWaveform == NULL) { - return MA_INVALID_ARGS; - } - - if (pFramesOut != NULL) { - switch (pWaveform->config.type) - { - case ma_waveform_type_sine: - { - ma_waveform_read_pcm_frames__sine(pWaveform, pFramesOut, frameCount); - } break; - - case ma_waveform_type_square: - { - ma_waveform_read_pcm_frames__square(pWaveform, 0.5, pFramesOut, frameCount); - } break; - - case ma_waveform_type_triangle: - { - ma_waveform_read_pcm_frames__triangle(pWaveform, pFramesOut, frameCount); - } break; - - case ma_waveform_type_sawtooth: - { - ma_waveform_read_pcm_frames__sawtooth(pWaveform, pFramesOut, frameCount); - } break; - - default: return MA_INVALID_OPERATION; /* Unknown waveform type. */ - } - } else { - pWaveform->time += pWaveform->advance * (ma_int64)frameCount; /* Cast to int64 required for VC6. Won't affect anything in practice. */ - } - - if (pFramesRead != NULL) { - *pFramesRead = frameCount; - } - - return MA_SUCCESS; -} - -MA_API ma_result ma_waveform_seek_to_pcm_frame(ma_waveform* pWaveform, ma_uint64 frameIndex) -{ - if (pWaveform == NULL) { - return MA_INVALID_ARGS; - } - - pWaveform->time = pWaveform->advance * (ma_int64)frameIndex; /* Casting for VC6. Won't be an issue in practice. */ - - return MA_SUCCESS; -} - -MA_API ma_pulsewave_config ma_pulsewave_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRate, double dutyCycle, double amplitude, double frequency) -{ - ma_pulsewave_config config; - - MA_ZERO_OBJECT(&config); - config.format = format; - config.channels = channels; - config.sampleRate = sampleRate; - config.dutyCycle = dutyCycle; - config.amplitude = amplitude; - config.frequency = frequency; - - return config; -} - -MA_API ma_result ma_pulsewave_init(const ma_pulsewave_config* pConfig, ma_pulsewave* pWaveform) -{ - ma_result result; - ma_waveform_config config; - - if (pWaveform == NULL) { - return MA_INVALID_ARGS; - } - - MA_ZERO_OBJECT(pWaveform); - - config = ma_waveform_config_init( - pConfig->format, - pConfig->channels, - pConfig->sampleRate, - ma_waveform_type_square, - pConfig->amplitude, - pConfig->frequency - ); - - result = ma_waveform_init(&config, &pWaveform->waveform); - ma_pulsewave_set_duty_cycle(pWaveform, pConfig->dutyCycle); - - return result; -} - -MA_API void ma_pulsewave_uninit(ma_pulsewave* pWaveform) -{ - if (pWaveform == NULL) { - return; - } - - ma_waveform_uninit(&pWaveform->waveform); -} - -MA_API ma_result ma_pulsewave_read_pcm_frames(ma_pulsewave* pWaveform, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead) -{ - if (pFramesRead != NULL) { - *pFramesRead = 0; - } - - if (frameCount == 0) { - return MA_INVALID_ARGS; - } - - if (pWaveform == NULL) { - return MA_INVALID_ARGS; - } - - if (pFramesOut != NULL) { - ma_waveform_read_pcm_frames__square(&pWaveform->waveform, pWaveform->config.dutyCycle, pFramesOut, frameCount); - } else { - pWaveform->waveform.time += pWaveform->waveform.advance * (ma_int64)frameCount; /* Cast to int64 required for VC6. Won't affect anything in practice. */ - } - - if (pFramesRead != NULL) { - *pFramesRead = frameCount; - } - - return MA_SUCCESS; -} - -MA_API ma_result ma_pulsewave_seek_to_pcm_frame(ma_pulsewave* pWaveform, ma_uint64 frameIndex) -{ - if (pWaveform == NULL) { - return MA_INVALID_ARGS; - } - - ma_waveform_seek_to_pcm_frame(&pWaveform->waveform, frameIndex); - - return MA_SUCCESS; -} - -MA_API ma_result ma_pulsewave_set_amplitude(ma_pulsewave* pWaveform, double amplitude) -{ - if (pWaveform == NULL) { - return MA_INVALID_ARGS; - } - - pWaveform->config.amplitude = amplitude; - ma_waveform_set_amplitude(&pWaveform->waveform, amplitude); - - return MA_SUCCESS; -} - -MA_API ma_result ma_pulsewave_set_frequency(ma_pulsewave* pWaveform, double frequency) -{ - if (pWaveform == NULL) { - return MA_INVALID_ARGS; - } - - pWaveform->config.frequency = frequency; - ma_waveform_set_frequency(&pWaveform->waveform, frequency); - - return MA_SUCCESS; -} - -MA_API ma_result ma_pulsewave_set_sample_rate(ma_pulsewave* pWaveform, ma_uint32 sampleRate) -{ - if (pWaveform == NULL) { - return MA_INVALID_ARGS; - } - - pWaveform->config.sampleRate = sampleRate; - ma_waveform_set_sample_rate(&pWaveform->waveform, sampleRate); - - return MA_SUCCESS; -} - -MA_API ma_result ma_pulsewave_set_duty_cycle(ma_pulsewave* pWaveform, double dutyCycle) -{ - if (pWaveform == NULL) { - return MA_INVALID_ARGS; - } - - pWaveform->config.dutyCycle = dutyCycle; - - return MA_SUCCESS; -} - - - -MA_API ma_noise_config ma_noise_config_init(ma_format format, ma_uint32 channels, ma_noise_type type, ma_int32 seed, double amplitude) -{ - ma_noise_config config; - MA_ZERO_OBJECT(&config); - - config.format = format; - config.channels = channels; - config.type = type; - config.seed = seed; - config.amplitude = amplitude; - - if (config.seed == 0) { - config.seed = MA_DEFAULT_LCG_SEED; - } - - return config; -} - - -static ma_result ma_noise__data_source_on_read(ma_data_source* pDataSource, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead) -{ - return ma_noise_read_pcm_frames((ma_noise*)pDataSource, pFramesOut, frameCount, pFramesRead); -} - -static ma_result ma_noise__data_source_on_seek(ma_data_source* pDataSource, ma_uint64 frameIndex) -{ - /* No-op. Just pretend to be successful. */ - (void)pDataSource; - (void)frameIndex; - return MA_SUCCESS; -} - -static ma_result ma_noise__data_source_on_get_data_format(ma_data_source* pDataSource, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate, ma_channel* pChannelMap, size_t channelMapCap) -{ - ma_noise* pNoise = (ma_noise*)pDataSource; - - *pFormat = pNoise->config.format; - *pChannels = pNoise->config.channels; - *pSampleRate = 0; /* There is no notion of sample rate with noise generation. */ - ma_channel_map_init_standard(ma_standard_channel_map_default, pChannelMap, channelMapCap, pNoise->config.channels); - - return MA_SUCCESS; -} - -static ma_data_source_vtable g_ma_noise_data_source_vtable = -{ - ma_noise__data_source_on_read, - ma_noise__data_source_on_seek, /* No-op for noise. */ - ma_noise__data_source_on_get_data_format, - NULL, /* onGetCursor. No notion of a cursor for noise. */ - NULL, /* onGetLength. No notion of a length for noise. */ - NULL, /* onSetLooping */ - 0 -}; - - -#ifndef MA_PINK_NOISE_BIN_SIZE -#define MA_PINK_NOISE_BIN_SIZE 16 -#endif - -typedef struct -{ - size_t sizeInBytes; - struct - { - size_t binOffset; - size_t accumulationOffset; - size_t counterOffset; - } pink; - struct - { - size_t accumulationOffset; - } brownian; -} ma_noise_heap_layout; - -static ma_result ma_noise_get_heap_layout(const ma_noise_config* pConfig, ma_noise_heap_layout* pHeapLayout) -{ - MA_ASSERT(pHeapLayout != NULL); - - MA_ZERO_OBJECT(pHeapLayout); - - if (pConfig == NULL) { - return MA_INVALID_ARGS; - } - - if (pConfig->channels == 0) { - return MA_INVALID_ARGS; - } - - pHeapLayout->sizeInBytes = 0; - - /* Pink. */ - if (pConfig->type == ma_noise_type_pink) { - /* bin */ - pHeapLayout->pink.binOffset = pHeapLayout->sizeInBytes; - pHeapLayout->sizeInBytes += sizeof(double*) * pConfig->channels; - pHeapLayout->sizeInBytes += sizeof(double ) * pConfig->channels * MA_PINK_NOISE_BIN_SIZE; - - /* accumulation */ - pHeapLayout->pink.accumulationOffset = pHeapLayout->sizeInBytes; - pHeapLayout->sizeInBytes += sizeof(double) * pConfig->channels; - - /* counter */ - pHeapLayout->pink.counterOffset = pHeapLayout->sizeInBytes; - pHeapLayout->sizeInBytes += sizeof(ma_uint32) * pConfig->channels; - } - - /* Brownian. */ - if (pConfig->type == ma_noise_type_brownian) { - /* accumulation */ - pHeapLayout->brownian.accumulationOffset = pHeapLayout->sizeInBytes; - pHeapLayout->sizeInBytes += sizeof(double) * pConfig->channels; - } - - /* Make sure allocation size is aligned. */ - pHeapLayout->sizeInBytes = ma_align_64(pHeapLayout->sizeInBytes); - - return MA_SUCCESS; -} - -MA_API ma_result ma_noise_get_heap_size(const ma_noise_config* pConfig, size_t* pHeapSizeInBytes) -{ - ma_result result; - ma_noise_heap_layout heapLayout; - - if (pHeapSizeInBytes == NULL) { - return MA_INVALID_ARGS; - } - - *pHeapSizeInBytes = 0; - - result = ma_noise_get_heap_layout(pConfig, &heapLayout); - if (result != MA_SUCCESS) { - return result; - } - - *pHeapSizeInBytes = heapLayout.sizeInBytes; - - return MA_SUCCESS; -} - -MA_API ma_result ma_noise_init_preallocated(const ma_noise_config* pConfig, void* pHeap, ma_noise* pNoise) -{ - ma_result result; - ma_noise_heap_layout heapLayout; - ma_data_source_config dataSourceConfig; - ma_uint32 iChannel; - - if (pNoise == NULL) { - return MA_INVALID_ARGS; - } - - MA_ZERO_OBJECT(pNoise); - - result = ma_noise_get_heap_layout(pConfig, &heapLayout); - if (result != MA_SUCCESS) { - return result; - } - - pNoise->_pHeap = pHeap; - MA_ZERO_MEMORY(pNoise->_pHeap, heapLayout.sizeInBytes); - - dataSourceConfig = ma_data_source_config_init(); - dataSourceConfig.vtable = &g_ma_noise_data_source_vtable; - - result = ma_data_source_init(&dataSourceConfig, &pNoise->ds); - if (result != MA_SUCCESS) { - return result; - } - - pNoise->config = *pConfig; - ma_lcg_seed(&pNoise->lcg, pConfig->seed); - - if (pNoise->config.type == ma_noise_type_pink) { - pNoise->state.pink.bin = (double** )ma_offset_ptr(pHeap, heapLayout.pink.binOffset); - pNoise->state.pink.accumulation = (double* )ma_offset_ptr(pHeap, heapLayout.pink.accumulationOffset); - pNoise->state.pink.counter = (ma_uint32*)ma_offset_ptr(pHeap, heapLayout.pink.counterOffset); - - for (iChannel = 0; iChannel < pConfig->channels; iChannel += 1) { - pNoise->state.pink.bin[iChannel] = (double*)ma_offset_ptr(pHeap, heapLayout.pink.binOffset + (sizeof(double*) * pConfig->channels) + (sizeof(double) * MA_PINK_NOISE_BIN_SIZE * iChannel)); - pNoise->state.pink.accumulation[iChannel] = 0; - pNoise->state.pink.counter[iChannel] = 1; - } - } - - if (pNoise->config.type == ma_noise_type_brownian) { - pNoise->state.brownian.accumulation = (double*)ma_offset_ptr(pHeap, heapLayout.brownian.accumulationOffset); - - for (iChannel = 0; iChannel < pConfig->channels; iChannel += 1) { - pNoise->state.brownian.accumulation[iChannel] = 0; - } - } - - return MA_SUCCESS; -} - -MA_API ma_result ma_noise_init(const ma_noise_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_noise* pNoise) -{ - ma_result result; - size_t heapSizeInBytes; - void* pHeap; - - result = ma_noise_get_heap_size(pConfig, &heapSizeInBytes); - if (result != MA_SUCCESS) { - return result; - } - - if (heapSizeInBytes > 0) { - pHeap = ma_malloc(heapSizeInBytes, pAllocationCallbacks); - if (pHeap == NULL) { - return MA_OUT_OF_MEMORY; - } - } else { - pHeap = NULL; - } - - result = ma_noise_init_preallocated(pConfig, pHeap, pNoise); - if (result != MA_SUCCESS) { - ma_free(pHeap, pAllocationCallbacks); - return result; - } - - pNoise->_ownsHeap = MA_TRUE; - return MA_SUCCESS; -} - -MA_API void ma_noise_uninit(ma_noise* pNoise, const ma_allocation_callbacks* pAllocationCallbacks) -{ - if (pNoise == NULL) { - return; - } - - ma_data_source_uninit(&pNoise->ds); - - if (pNoise->_ownsHeap) { - ma_free(pNoise->_pHeap, pAllocationCallbacks); - } -} - -MA_API ma_result ma_noise_set_amplitude(ma_noise* pNoise, double amplitude) -{ - if (pNoise == NULL) { - return MA_INVALID_ARGS; - } - - pNoise->config.amplitude = amplitude; - return MA_SUCCESS; -} - -MA_API ma_result ma_noise_set_seed(ma_noise* pNoise, ma_int32 seed) -{ - if (pNoise == NULL) { - return MA_INVALID_ARGS; - } - - pNoise->lcg.state = seed; - return MA_SUCCESS; -} - - -MA_API ma_result ma_noise_set_type(ma_noise* pNoise, ma_noise_type type) -{ - if (pNoise == NULL) { - return MA_INVALID_ARGS; - } - - /* - This function should never have been implemented in the first place. Changing the type dynamically is not - supported. Instead you need to uninitialize and reinitialize a fresh `ma_noise` object. This function - will be removed in version 0.12. - */ - MA_ASSERT(MA_FALSE); - (void)type; - - return MA_INVALID_OPERATION; -} - -static MA_INLINE float ma_noise_f32_white(ma_noise* pNoise) -{ - return (float)(ma_lcg_rand_f64(&pNoise->lcg) * pNoise->config.amplitude); -} - -static MA_INLINE ma_int16 ma_noise_s16_white(ma_noise* pNoise) -{ - return ma_pcm_sample_f32_to_s16(ma_noise_f32_white(pNoise)); -} - -static MA_INLINE ma_uint64 ma_noise_read_pcm_frames__white(ma_noise* pNoise, void* pFramesOut, ma_uint64 frameCount) -{ - ma_uint64 iFrame; - ma_uint32 iChannel; - const ma_uint32 channels = pNoise->config.channels; - MA_ASSUME(channels > 0); - - if (pNoise->config.format == ma_format_f32) { - float* pFramesOutF32 = (float*)pFramesOut; - if (pNoise->config.duplicateChannels) { - for (iFrame = 0; iFrame < frameCount; iFrame += 1) { - float s = ma_noise_f32_white(pNoise); - for (iChannel = 0; iChannel < channels; iChannel += 1) { - pFramesOutF32[iFrame*channels + iChannel] = s; - } - } - } else { - for (iFrame = 0; iFrame < frameCount; iFrame += 1) { - for (iChannel = 0; iChannel < channels; iChannel += 1) { - pFramesOutF32[iFrame*channels + iChannel] = ma_noise_f32_white(pNoise); - } - } - } - } else if (pNoise->config.format == ma_format_s16) { - ma_int16* pFramesOutS16 = (ma_int16*)pFramesOut; - if (pNoise->config.duplicateChannels) { - for (iFrame = 0; iFrame < frameCount; iFrame += 1) { - ma_int16 s = ma_noise_s16_white(pNoise); - for (iChannel = 0; iChannel < channels; iChannel += 1) { - pFramesOutS16[iFrame*channels + iChannel] = s; - } - } - } else { - for (iFrame = 0; iFrame < frameCount; iFrame += 1) { - for (iChannel = 0; iChannel < channels; iChannel += 1) { - pFramesOutS16[iFrame*channels + iChannel] = ma_noise_s16_white(pNoise); - } - } - } - } else { - const ma_uint32 bps = ma_get_bytes_per_sample(pNoise->config.format); - const ma_uint32 bpf = bps * channels; - - if (pNoise->config.duplicateChannels) { - for (iFrame = 0; iFrame < frameCount; iFrame += 1) { - float s = ma_noise_f32_white(pNoise); - for (iChannel = 0; iChannel < channels; iChannel += 1) { - ma_pcm_convert(ma_offset_ptr(pFramesOut, iFrame*bpf + iChannel*bps), pNoise->config.format, &s, ma_format_f32, 1, ma_dither_mode_none); - } - } - } else { - for (iFrame = 0; iFrame < frameCount; iFrame += 1) { - for (iChannel = 0; iChannel < channels; iChannel += 1) { - float s = ma_noise_f32_white(pNoise); - ma_pcm_convert(ma_offset_ptr(pFramesOut, iFrame*bpf + iChannel*bps), pNoise->config.format, &s, ma_format_f32, 1, ma_dither_mode_none); - } - } - } - } - - return frameCount; -} - - -static MA_INLINE unsigned int ma_tzcnt32(unsigned int x) -{ - unsigned int n; - - /* Special case for odd numbers since they should happen about half the time. */ - if (x & 0x1) { - return 0; - } - - if (x == 0) { - return sizeof(x) << 3; - } - - n = 1; - if ((x & 0x0000FFFF) == 0) { x >>= 16; n += 16; } - if ((x & 0x000000FF) == 0) { x >>= 8; n += 8; } - if ((x & 0x0000000F) == 0) { x >>= 4; n += 4; } - if ((x & 0x00000003) == 0) { x >>= 2; n += 2; } - n -= x & 0x00000001; - - return n; -} - -/* -Pink noise generation based on Tonic (public domain) with modifications. https://github.com/TonicAudio/Tonic/blob/master/src/Tonic/Noise.h - -This is basically _the_ reference for pink noise from what I've found: http://www.firstpr.com.au/dsp/pink-noise/ -*/ -static MA_INLINE float ma_noise_f32_pink(ma_noise* pNoise, ma_uint32 iChannel) -{ - double result; - double binPrev; - double binNext; - unsigned int ibin; - - ibin = ma_tzcnt32(pNoise->state.pink.counter[iChannel]) & (MA_PINK_NOISE_BIN_SIZE - 1); - - binPrev = pNoise->state.pink.bin[iChannel][ibin]; - binNext = ma_lcg_rand_f64(&pNoise->lcg); - pNoise->state.pink.bin[iChannel][ibin] = binNext; - - pNoise->state.pink.accumulation[iChannel] += (binNext - binPrev); - pNoise->state.pink.counter[iChannel] += 1; - - result = (ma_lcg_rand_f64(&pNoise->lcg) + pNoise->state.pink.accumulation[iChannel]); - result /= 10; - - return (float)(result * pNoise->config.amplitude); -} - -static MA_INLINE ma_int16 ma_noise_s16_pink(ma_noise* pNoise, ma_uint32 iChannel) -{ - return ma_pcm_sample_f32_to_s16(ma_noise_f32_pink(pNoise, iChannel)); -} - -static MA_INLINE ma_uint64 ma_noise_read_pcm_frames__pink(ma_noise* pNoise, void* pFramesOut, ma_uint64 frameCount) -{ - ma_uint64 iFrame; - ma_uint32 iChannel; - const ma_uint32 channels = pNoise->config.channels; - MA_ASSUME(channels > 0); - - if (pNoise->config.format == ma_format_f32) { - float* pFramesOutF32 = (float*)pFramesOut; - if (pNoise->config.duplicateChannels) { - for (iFrame = 0; iFrame < frameCount; iFrame += 1) { - float s = ma_noise_f32_pink(pNoise, 0); - for (iChannel = 0; iChannel < channels; iChannel += 1) { - pFramesOutF32[iFrame*channels + iChannel] = s; - } - } - } else { - for (iFrame = 0; iFrame < frameCount; iFrame += 1) { - for (iChannel = 0; iChannel < channels; iChannel += 1) { - pFramesOutF32[iFrame*channels + iChannel] = ma_noise_f32_pink(pNoise, iChannel); - } - } - } - } else if (pNoise->config.format == ma_format_s16) { - ma_int16* pFramesOutS16 = (ma_int16*)pFramesOut; - if (pNoise->config.duplicateChannels) { - for (iFrame = 0; iFrame < frameCount; iFrame += 1) { - ma_int16 s = ma_noise_s16_pink(pNoise, 0); - for (iChannel = 0; iChannel < channels; iChannel += 1) { - pFramesOutS16[iFrame*channels + iChannel] = s; - } - } - } else { - for (iFrame = 0; iFrame < frameCount; iFrame += 1) { - for (iChannel = 0; iChannel < channels; iChannel += 1) { - pFramesOutS16[iFrame*channels + iChannel] = ma_noise_s16_pink(pNoise, iChannel); - } - } - } - } else { - const ma_uint32 bps = ma_get_bytes_per_sample(pNoise->config.format); - const ma_uint32 bpf = bps * channels; - - if (pNoise->config.duplicateChannels) { - for (iFrame = 0; iFrame < frameCount; iFrame += 1) { - float s = ma_noise_f32_pink(pNoise, 0); - for (iChannel = 0; iChannel < channels; iChannel += 1) { - ma_pcm_convert(ma_offset_ptr(pFramesOut, iFrame*bpf + iChannel*bps), pNoise->config.format, &s, ma_format_f32, 1, ma_dither_mode_none); - } - } - } else { - for (iFrame = 0; iFrame < frameCount; iFrame += 1) { - for (iChannel = 0; iChannel < channels; iChannel += 1) { - float s = ma_noise_f32_pink(pNoise, iChannel); - ma_pcm_convert(ma_offset_ptr(pFramesOut, iFrame*bpf + iChannel*bps), pNoise->config.format, &s, ma_format_f32, 1, ma_dither_mode_none); - } - } - } - } - - return frameCount; -} - - -static MA_INLINE float ma_noise_f32_brownian(ma_noise* pNoise, ma_uint32 iChannel) -{ - double result; - - result = (ma_lcg_rand_f64(&pNoise->lcg) + pNoise->state.brownian.accumulation[iChannel]); - result /= 1.005; /* Don't escape the -1..1 range on average. */ - - pNoise->state.brownian.accumulation[iChannel] = result; - result /= 20; - - return (float)(result * pNoise->config.amplitude); -} - -static MA_INLINE ma_int16 ma_noise_s16_brownian(ma_noise* pNoise, ma_uint32 iChannel) -{ - return ma_pcm_sample_f32_to_s16(ma_noise_f32_brownian(pNoise, iChannel)); -} - -static MA_INLINE ma_uint64 ma_noise_read_pcm_frames__brownian(ma_noise* pNoise, void* pFramesOut, ma_uint64 frameCount) -{ - ma_uint64 iFrame; - ma_uint32 iChannel; - const ma_uint32 channels = pNoise->config.channels; - MA_ASSUME(channels > 0); - - if (pNoise->config.format == ma_format_f32) { - float* pFramesOutF32 = (float*)pFramesOut; - if (pNoise->config.duplicateChannels) { - for (iFrame = 0; iFrame < frameCount; iFrame += 1) { - float s = ma_noise_f32_brownian(pNoise, 0); - for (iChannel = 0; iChannel < channels; iChannel += 1) { - pFramesOutF32[iFrame*channels + iChannel] = s; - } - } - } else { - for (iFrame = 0; iFrame < frameCount; iFrame += 1) { - for (iChannel = 0; iChannel < channels; iChannel += 1) { - pFramesOutF32[iFrame*channels + iChannel] = ma_noise_f32_brownian(pNoise, iChannel); - } - } - } - } else if (pNoise->config.format == ma_format_s16) { - ma_int16* pFramesOutS16 = (ma_int16*)pFramesOut; - if (pNoise->config.duplicateChannels) { - for (iFrame = 0; iFrame < frameCount; iFrame += 1) { - ma_int16 s = ma_noise_s16_brownian(pNoise, 0); - for (iChannel = 0; iChannel < channels; iChannel += 1) { - pFramesOutS16[iFrame*channels + iChannel] = s; - } - } - } else { - for (iFrame = 0; iFrame < frameCount; iFrame += 1) { - for (iChannel = 0; iChannel < channels; iChannel += 1) { - pFramesOutS16[iFrame*channels + iChannel] = ma_noise_s16_brownian(pNoise, iChannel); - } - } - } - } else { - const ma_uint32 bps = ma_get_bytes_per_sample(pNoise->config.format); - const ma_uint32 bpf = bps * channels; - - if (pNoise->config.duplicateChannels) { - for (iFrame = 0; iFrame < frameCount; iFrame += 1) { - float s = ma_noise_f32_brownian(pNoise, 0); - for (iChannel = 0; iChannel < channels; iChannel += 1) { - ma_pcm_convert(ma_offset_ptr(pFramesOut, iFrame*bpf + iChannel*bps), pNoise->config.format, &s, ma_format_f32, 1, ma_dither_mode_none); - } - } - } else { - for (iFrame = 0; iFrame < frameCount; iFrame += 1) { - for (iChannel = 0; iChannel < channels; iChannel += 1) { - float s = ma_noise_f32_brownian(pNoise, iChannel); - ma_pcm_convert(ma_offset_ptr(pFramesOut, iFrame*bpf + iChannel*bps), pNoise->config.format, &s, ma_format_f32, 1, ma_dither_mode_none); - } - } - } - } - - return frameCount; -} - -MA_API ma_result ma_noise_read_pcm_frames(ma_noise* pNoise, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead) -{ - ma_uint64 framesRead = 0; - - if (pFramesRead != NULL) { - *pFramesRead = 0; - } - - if (frameCount == 0) { - return MA_INVALID_ARGS; - } - - if (pNoise == NULL) { - return MA_INVALID_ARGS; - } - - /* The output buffer is allowed to be NULL. Since we aren't tracking cursors or anything we can just do nothing and pretend to be successful. */ - if (pFramesOut == NULL) { - framesRead = frameCount; - } else { - switch (pNoise->config.type) { - case ma_noise_type_white: framesRead = ma_noise_read_pcm_frames__white (pNoise, pFramesOut, frameCount); break; - case ma_noise_type_pink: framesRead = ma_noise_read_pcm_frames__pink (pNoise, pFramesOut, frameCount); break; - case ma_noise_type_brownian: framesRead = ma_noise_read_pcm_frames__brownian(pNoise, pFramesOut, frameCount); break; - default: return MA_INVALID_OPERATION; /* Unknown noise type. */ - } - } - - if (pFramesRead != NULL) { - *pFramesRead = framesRead; - } - - return MA_SUCCESS; -} -#endif /* MA_NO_GENERATION */ - - - -#ifndef MA_NO_RESOURCE_MANAGER -#ifndef MA_RESOURCE_MANAGER_PAGE_SIZE_IN_MILLISECONDS -#define MA_RESOURCE_MANAGER_PAGE_SIZE_IN_MILLISECONDS 1000 -#endif - -#ifndef MA_JOB_TYPE_RESOURCE_MANAGER_QUEUE_CAPACITY -#define MA_JOB_TYPE_RESOURCE_MANAGER_QUEUE_CAPACITY 1024 -#endif - -MA_API ma_resource_manager_pipeline_notifications ma_resource_manager_pipeline_notifications_init(void) -{ - ma_resource_manager_pipeline_notifications notifications; - - MA_ZERO_OBJECT(¬ifications); - - return notifications; -} - -static void ma_resource_manager_pipeline_notifications_signal_all_notifications(const ma_resource_manager_pipeline_notifications* pPipelineNotifications) -{ - if (pPipelineNotifications == NULL) { - return; - } - - if (pPipelineNotifications->init.pNotification) { ma_async_notification_signal(pPipelineNotifications->init.pNotification); } - if (pPipelineNotifications->done.pNotification) { ma_async_notification_signal(pPipelineNotifications->done.pNotification); } -} - -static void ma_resource_manager_pipeline_notifications_acquire_all_fences(const ma_resource_manager_pipeline_notifications* pPipelineNotifications) -{ - if (pPipelineNotifications == NULL) { - return; - } - - if (pPipelineNotifications->init.pFence != NULL) { ma_fence_acquire(pPipelineNotifications->init.pFence); } - if (pPipelineNotifications->done.pFence != NULL) { ma_fence_acquire(pPipelineNotifications->done.pFence); } -} - -static void ma_resource_manager_pipeline_notifications_release_all_fences(const ma_resource_manager_pipeline_notifications* pPipelineNotifications) -{ - if (pPipelineNotifications == NULL) { - return; - } - - if (pPipelineNotifications->init.pFence != NULL) { ma_fence_release(pPipelineNotifications->init.pFence); } - if (pPipelineNotifications->done.pFence != NULL) { ma_fence_release(pPipelineNotifications->done.pFence); } -} - - - -#ifndef MA_DEFAULT_HASH_SEED -#define MA_DEFAULT_HASH_SEED 42 -#endif - -/* MurmurHash3. Based on code from https://github.com/PeterScott/murmur3/blob/master/murmur3.c (public domain). */ -#if defined(__clang__) || (defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6))) - #pragma GCC diagnostic push - #if __GNUC__ >= 7 - #pragma GCC diagnostic ignored "-Wimplicit-fallthrough" - #endif -#endif - -static MA_INLINE ma_uint32 ma_rotl32(ma_uint32 x, ma_int8 r) -{ - return (x << r) | (x >> (32 - r)); -} - -static MA_INLINE ma_uint32 ma_hash_getblock(const ma_uint32* blocks, int i) -{ - ma_uint32 block; - - /* Try silencing a sanitization warning about unaligned access by doing a memcpy() instead of assignment. */ - MA_COPY_MEMORY(&block, ma_offset_ptr(blocks, i * sizeof(block)), sizeof(block)); - - if (ma_is_little_endian()) { - return block; - } else { - return ma_swap_endian_uint32(block); - } -} - -static MA_INLINE ma_uint32 ma_hash_fmix32(ma_uint32 h) -{ - h ^= h >> 16; - h *= 0x85ebca6b; - h ^= h >> 13; - h *= 0xc2b2ae35; - h ^= h >> 16; - - return h; -} - -static ma_uint32 ma_hash_32(const void* key, int len, ma_uint32 seed) -{ - const ma_uint8* data = (const ma_uint8*)key; - const ma_uint32* blocks; - const ma_uint8* tail; - const int nblocks = len / 4; - ma_uint32 h1 = seed; - ma_uint32 c1 = 0xcc9e2d51; - ma_uint32 c2 = 0x1b873593; - ma_uint32 k1; - int i; - - blocks = (const ma_uint32 *)(data + nblocks*4); - - for(i = -nblocks; i; i++) { - k1 = ma_hash_getblock(blocks,i); - - k1 *= c1; - k1 = ma_rotl32(k1, 15); - k1 *= c2; - - h1 ^= k1; - h1 = ma_rotl32(h1, 13); - h1 = h1*5 + 0xe6546b64; - } - - - tail = (const ma_uint8*)(data + nblocks*4); - - k1 = 0; - switch(len & 3) { - case 3: k1 ^= tail[2] << 16; - case 2: k1 ^= tail[1] << 8; - case 1: k1 ^= tail[0]; - k1 *= c1; k1 = ma_rotl32(k1, 15); k1 *= c2; h1 ^= k1; - }; - - - h1 ^= len; - h1 = ma_hash_fmix32(h1); - - return h1; -} - -#if defined(__clang__) || (defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6))) - #pragma GCC diagnostic push -#endif -/* End MurmurHash3 */ - -static ma_uint32 ma_hash_string_32(const char* str) -{ - return ma_hash_32(str, (int)strlen(str), MA_DEFAULT_HASH_SEED); -} - -static ma_uint32 ma_hash_string_w_32(const wchar_t* str) -{ - return ma_hash_32(str, (int)wcslen(str) * sizeof(*str), MA_DEFAULT_HASH_SEED); -} - - - - -/* -Basic BST Functions -*/ -static ma_result ma_resource_manager_data_buffer_node_search(ma_resource_manager* pResourceManager, ma_uint32 hashedName32, ma_resource_manager_data_buffer_node** ppDataBufferNode) -{ - ma_resource_manager_data_buffer_node* pCurrentNode; - - MA_ASSERT(pResourceManager != NULL); - MA_ASSERT(ppDataBufferNode != NULL); - - pCurrentNode = pResourceManager->pRootDataBufferNode; - while (pCurrentNode != NULL) { - if (hashedName32 == pCurrentNode->hashedName32) { - break; /* Found. */ - } else if (hashedName32 < pCurrentNode->hashedName32) { - pCurrentNode = pCurrentNode->pChildLo; - } else { - pCurrentNode = pCurrentNode->pChildHi; - } - } - - *ppDataBufferNode = pCurrentNode; - - if (pCurrentNode == NULL) { - return MA_DOES_NOT_EXIST; - } else { - return MA_SUCCESS; - } -} - -static ma_result ma_resource_manager_data_buffer_node_insert_point(ma_resource_manager* pResourceManager, ma_uint32 hashedName32, ma_resource_manager_data_buffer_node** ppInsertPoint) -{ - ma_result result = MA_SUCCESS; - ma_resource_manager_data_buffer_node* pCurrentNode; - - MA_ASSERT(pResourceManager != NULL); - MA_ASSERT(ppInsertPoint != NULL); - - *ppInsertPoint = NULL; - - if (pResourceManager->pRootDataBufferNode == NULL) { - return MA_SUCCESS; /* No items. */ - } - - /* We need to find the node that will become the parent of the new node. If a node is found that already has the same hashed name we need to return MA_ALREADY_EXISTS. */ - pCurrentNode = pResourceManager->pRootDataBufferNode; - while (pCurrentNode != NULL) { - if (hashedName32 == pCurrentNode->hashedName32) { - result = MA_ALREADY_EXISTS; - break; - } else { - if (hashedName32 < pCurrentNode->hashedName32) { - if (pCurrentNode->pChildLo == NULL) { - result = MA_SUCCESS; - break; - } else { - pCurrentNode = pCurrentNode->pChildLo; - } - } else { - if (pCurrentNode->pChildHi == NULL) { - result = MA_SUCCESS; - break; - } else { - pCurrentNode = pCurrentNode->pChildHi; - } - } - } - } - - *ppInsertPoint = pCurrentNode; - return result; -} - -static ma_result ma_resource_manager_data_buffer_node_insert_at(ma_resource_manager* pResourceManager, ma_resource_manager_data_buffer_node* pDataBufferNode, ma_resource_manager_data_buffer_node* pInsertPoint) -{ - MA_ASSERT(pResourceManager != NULL); - MA_ASSERT(pDataBufferNode != NULL); - - /* The key must have been set before calling this function. */ - MA_ASSERT(pDataBufferNode->hashedName32 != 0); - - if (pInsertPoint == NULL) { - /* It's the first node. */ - pResourceManager->pRootDataBufferNode = pDataBufferNode; - } else { - /* It's not the first node. It needs to be inserted. */ - if (pDataBufferNode->hashedName32 < pInsertPoint->hashedName32) { - MA_ASSERT(pInsertPoint->pChildLo == NULL); - pInsertPoint->pChildLo = pDataBufferNode; - } else { - MA_ASSERT(pInsertPoint->pChildHi == NULL); - pInsertPoint->pChildHi = pDataBufferNode; - } - } - - pDataBufferNode->pParent = pInsertPoint; - - return MA_SUCCESS; -} - -#if 0 /* Unused for now. */ -static ma_result ma_resource_manager_data_buffer_node_insert(ma_resource_manager* pResourceManager, ma_resource_manager_data_buffer_node* pDataBufferNode) -{ - ma_result result; - ma_resource_manager_data_buffer_node* pInsertPoint; - - MA_ASSERT(pResourceManager != NULL); - MA_ASSERT(pDataBufferNode != NULL); - - result = ma_resource_manager_data_buffer_node_insert_point(pResourceManager, pDataBufferNode->hashedName32, &pInsertPoint); - if (result != MA_SUCCESS) { - return MA_INVALID_ARGS; - } - - return ma_resource_manager_data_buffer_node_insert_at(pResourceManager, pDataBufferNode, pInsertPoint); -} -#endif - -static MA_INLINE ma_resource_manager_data_buffer_node* ma_resource_manager_data_buffer_node_find_min(ma_resource_manager_data_buffer_node* pDataBufferNode) -{ - ma_resource_manager_data_buffer_node* pCurrentNode; - - MA_ASSERT(pDataBufferNode != NULL); - - pCurrentNode = pDataBufferNode; - while (pCurrentNode->pChildLo != NULL) { - pCurrentNode = pCurrentNode->pChildLo; - } - - return pCurrentNode; -} - -static MA_INLINE ma_resource_manager_data_buffer_node* ma_resource_manager_data_buffer_node_find_max(ma_resource_manager_data_buffer_node* pDataBufferNode) -{ - ma_resource_manager_data_buffer_node* pCurrentNode; - - MA_ASSERT(pDataBufferNode != NULL); - - pCurrentNode = pDataBufferNode; - while (pCurrentNode->pChildHi != NULL) { - pCurrentNode = pCurrentNode->pChildHi; - } - - return pCurrentNode; -} - -static MA_INLINE ma_resource_manager_data_buffer_node* ma_resource_manager_data_buffer_node_find_inorder_successor(ma_resource_manager_data_buffer_node* pDataBufferNode) -{ - MA_ASSERT(pDataBufferNode != NULL); - MA_ASSERT(pDataBufferNode->pChildHi != NULL); - - return ma_resource_manager_data_buffer_node_find_min(pDataBufferNode->pChildHi); -} - -static MA_INLINE ma_resource_manager_data_buffer_node* ma_resource_manager_data_buffer_node_find_inorder_predecessor(ma_resource_manager_data_buffer_node* pDataBufferNode) -{ - MA_ASSERT(pDataBufferNode != NULL); - MA_ASSERT(pDataBufferNode->pChildLo != NULL); - - return ma_resource_manager_data_buffer_node_find_max(pDataBufferNode->pChildLo); -} - -static ma_result ma_resource_manager_data_buffer_node_remove(ma_resource_manager* pResourceManager, ma_resource_manager_data_buffer_node* pDataBufferNode) -{ - MA_ASSERT(pResourceManager != NULL); - MA_ASSERT(pDataBufferNode != NULL); - - if (pDataBufferNode->pChildLo == NULL) { - if (pDataBufferNode->pChildHi == NULL) { - /* Simple case - deleting a buffer with no children. */ - if (pDataBufferNode->pParent == NULL) { - MA_ASSERT(pResourceManager->pRootDataBufferNode == pDataBufferNode); /* There is only a single buffer in the tree which should be equal to the root node. */ - pResourceManager->pRootDataBufferNode = NULL; - } else { - if (pDataBufferNode->pParent->pChildLo == pDataBufferNode) { - pDataBufferNode->pParent->pChildLo = NULL; - } else { - pDataBufferNode->pParent->pChildHi = NULL; - } - } - } else { - /* Node has one child - pChildHi != NULL. */ - pDataBufferNode->pChildHi->pParent = pDataBufferNode->pParent; - - if (pDataBufferNode->pParent == NULL) { - MA_ASSERT(pResourceManager->pRootDataBufferNode == pDataBufferNode); - pResourceManager->pRootDataBufferNode = pDataBufferNode->pChildHi; - } else { - if (pDataBufferNode->pParent->pChildLo == pDataBufferNode) { - pDataBufferNode->pParent->pChildLo = pDataBufferNode->pChildHi; - } else { - pDataBufferNode->pParent->pChildHi = pDataBufferNode->pChildHi; - } - } - } - } else { - if (pDataBufferNode->pChildHi == NULL) { - /* Node has one child - pChildLo != NULL. */ - pDataBufferNode->pChildLo->pParent = pDataBufferNode->pParent; - - if (pDataBufferNode->pParent == NULL) { - MA_ASSERT(pResourceManager->pRootDataBufferNode == pDataBufferNode); - pResourceManager->pRootDataBufferNode = pDataBufferNode->pChildLo; - } else { - if (pDataBufferNode->pParent->pChildLo == pDataBufferNode) { - pDataBufferNode->pParent->pChildLo = pDataBufferNode->pChildLo; - } else { - pDataBufferNode->pParent->pChildHi = pDataBufferNode->pChildLo; - } - } - } else { - /* Complex case - deleting a node with two children. */ - ma_resource_manager_data_buffer_node* pReplacementDataBufferNode; - - /* For now we are just going to use the in-order successor as the replacement, but we may want to try to keep this balanced by switching between the two. */ - pReplacementDataBufferNode = ma_resource_manager_data_buffer_node_find_inorder_successor(pDataBufferNode); - MA_ASSERT(pReplacementDataBufferNode != NULL); - - /* - Now that we have our replacement node we can make the change. The simple way to do this would be to just exchange the values, and then remove the replacement - node, however we track specific nodes via pointers which means we can't just swap out the values. We need to instead just change the pointers around. The - replacement node should have at most 1 child. Therefore, we can detach it in terms of our simpler cases above. What we're essentially doing is detaching the - replacement node and reinserting it into the same position as the deleted node. - */ - MA_ASSERT(pReplacementDataBufferNode->pParent != NULL); /* The replacement node should never be the root which means it should always have a parent. */ - MA_ASSERT(pReplacementDataBufferNode->pChildLo == NULL); /* Because we used in-order successor. This would be pChildHi == NULL if we used in-order predecessor. */ - - if (pReplacementDataBufferNode->pChildHi == NULL) { - if (pReplacementDataBufferNode->pParent->pChildLo == pReplacementDataBufferNode) { - pReplacementDataBufferNode->pParent->pChildLo = NULL; - } else { - pReplacementDataBufferNode->pParent->pChildHi = NULL; - } - } else { - pReplacementDataBufferNode->pChildHi->pParent = pReplacementDataBufferNode->pParent; - if (pReplacementDataBufferNode->pParent->pChildLo == pReplacementDataBufferNode) { - pReplacementDataBufferNode->pParent->pChildLo = pReplacementDataBufferNode->pChildHi; - } else { - pReplacementDataBufferNode->pParent->pChildHi = pReplacementDataBufferNode->pChildHi; - } - } - - - /* The replacement node has essentially been detached from the binary tree, so now we need to replace the old data buffer with it. The first thing to update is the parent */ - if (pDataBufferNode->pParent != NULL) { - if (pDataBufferNode->pParent->pChildLo == pDataBufferNode) { - pDataBufferNode->pParent->pChildLo = pReplacementDataBufferNode; - } else { - pDataBufferNode->pParent->pChildHi = pReplacementDataBufferNode; - } - } - - /* Now need to update the replacement node's pointers. */ - pReplacementDataBufferNode->pParent = pDataBufferNode->pParent; - pReplacementDataBufferNode->pChildLo = pDataBufferNode->pChildLo; - pReplacementDataBufferNode->pChildHi = pDataBufferNode->pChildHi; - - /* Now the children of the replacement node need to have their parent pointers updated. */ - if (pReplacementDataBufferNode->pChildLo != NULL) { - pReplacementDataBufferNode->pChildLo->pParent = pReplacementDataBufferNode; - } - if (pReplacementDataBufferNode->pChildHi != NULL) { - pReplacementDataBufferNode->pChildHi->pParent = pReplacementDataBufferNode; - } - - /* Now the root node needs to be updated. */ - if (pResourceManager->pRootDataBufferNode == pDataBufferNode) { - pResourceManager->pRootDataBufferNode = pReplacementDataBufferNode; - } - } - } - - return MA_SUCCESS; -} - -#if 0 /* Unused for now. */ -static ma_result ma_resource_manager_data_buffer_node_remove_by_key(ma_resource_manager* pResourceManager, ma_uint32 hashedName32) -{ - ma_result result; - ma_resource_manager_data_buffer_node* pDataBufferNode; - - result = ma_resource_manager_data_buffer_search(pResourceManager, hashedName32, &pDataBufferNode); - if (result != MA_SUCCESS) { - return result; /* Could not find the data buffer. */ - } - - return ma_resource_manager_data_buffer_remove(pResourceManager, pDataBufferNode); -} -#endif - -static ma_resource_manager_data_supply_type ma_resource_manager_data_buffer_node_get_data_supply_type(ma_resource_manager_data_buffer_node* pDataBufferNode) -{ - return (ma_resource_manager_data_supply_type)ma_atomic_load_i32(&pDataBufferNode->data.type); -} - -static void ma_resource_manager_data_buffer_node_set_data_supply_type(ma_resource_manager_data_buffer_node* pDataBufferNode, ma_resource_manager_data_supply_type supplyType) -{ - ma_atomic_exchange_i32(&pDataBufferNode->data.type, supplyType); -} - -static ma_result ma_resource_manager_data_buffer_node_increment_ref(ma_resource_manager* pResourceManager, ma_resource_manager_data_buffer_node* pDataBufferNode, ma_uint32* pNewRefCount) -{ - ma_uint32 refCount; - - MA_ASSERT(pResourceManager != NULL); - MA_ASSERT(pDataBufferNode != NULL); - - (void)pResourceManager; - - refCount = ma_atomic_fetch_add_32(&pDataBufferNode->refCount, 1) + 1; - - if (pNewRefCount != NULL) { - *pNewRefCount = refCount; - } - - return MA_SUCCESS; -} - -static ma_result ma_resource_manager_data_buffer_node_decrement_ref(ma_resource_manager* pResourceManager, ma_resource_manager_data_buffer_node* pDataBufferNode, ma_uint32* pNewRefCount) -{ - ma_uint32 refCount; - - MA_ASSERT(pResourceManager != NULL); - MA_ASSERT(pDataBufferNode != NULL); - - (void)pResourceManager; - - refCount = ma_atomic_fetch_sub_32(&pDataBufferNode->refCount, 1) - 1; - - if (pNewRefCount != NULL) { - *pNewRefCount = refCount; - } - - return MA_SUCCESS; -} - -static void ma_resource_manager_data_buffer_node_free(ma_resource_manager* pResourceManager, ma_resource_manager_data_buffer_node* pDataBufferNode) -{ - MA_ASSERT(pResourceManager != NULL); - MA_ASSERT(pDataBufferNode != NULL); - - if (pDataBufferNode->isDataOwnedByResourceManager) { - if (ma_resource_manager_data_buffer_node_get_data_supply_type(pDataBufferNode) == ma_resource_manager_data_supply_type_encoded) { - ma_free((void*)pDataBufferNode->data.backend.encoded.pData, &pResourceManager->config.allocationCallbacks); - pDataBufferNode->data.backend.encoded.pData = NULL; - pDataBufferNode->data.backend.encoded.sizeInBytes = 0; - } else if (ma_resource_manager_data_buffer_node_get_data_supply_type(pDataBufferNode) == ma_resource_manager_data_supply_type_decoded) { - ma_free((void*)pDataBufferNode->data.backend.decoded.pData, &pResourceManager->config.allocationCallbacks); - pDataBufferNode->data.backend.decoded.pData = NULL; - pDataBufferNode->data.backend.decoded.totalFrameCount = 0; - } else if (ma_resource_manager_data_buffer_node_get_data_supply_type(pDataBufferNode) == ma_resource_manager_data_supply_type_decoded_paged) { - ma_paged_audio_buffer_data_uninit(&pDataBufferNode->data.backend.decodedPaged.data, &pResourceManager->config.allocationCallbacks); - } else { - /* Should never hit this if the node was successfully initialized. */ - MA_ASSERT(pDataBufferNode->result != MA_SUCCESS); - } - } - - /* The data buffer itself needs to be freed. */ - ma_free(pDataBufferNode, &pResourceManager->config.allocationCallbacks); -} - -static ma_result ma_resource_manager_data_buffer_node_result(const ma_resource_manager_data_buffer_node* pDataBufferNode) -{ - MA_ASSERT(pDataBufferNode != NULL); - - return (ma_result)ma_atomic_load_i32((ma_result*)&pDataBufferNode->result); /* Need a naughty const-cast here. */ -} - - -static ma_bool32 ma_resource_manager_is_threading_enabled(const ma_resource_manager* pResourceManager) -{ - MA_ASSERT(pResourceManager != NULL); - - return (pResourceManager->config.flags & MA_RESOURCE_MANAGER_FLAG_NO_THREADING) == 0; -} - - -typedef struct -{ - union - { - ma_async_notification_event e; - ma_async_notification_poll p; - } backend; /* Must be the first member. */ - ma_resource_manager* pResourceManager; -} ma_resource_manager_inline_notification; - -static ma_result ma_resource_manager_inline_notification_init(ma_resource_manager* pResourceManager, ma_resource_manager_inline_notification* pNotification) -{ - MA_ASSERT(pResourceManager != NULL); - MA_ASSERT(pNotification != NULL); - - pNotification->pResourceManager = pResourceManager; - - if (ma_resource_manager_is_threading_enabled(pResourceManager)) { - return ma_async_notification_event_init(&pNotification->backend.e); - } else { - return ma_async_notification_poll_init(&pNotification->backend.p); - } -} - -static void ma_resource_manager_inline_notification_uninit(ma_resource_manager_inline_notification* pNotification) -{ - MA_ASSERT(pNotification != NULL); - - if (ma_resource_manager_is_threading_enabled(pNotification->pResourceManager)) { - ma_async_notification_event_uninit(&pNotification->backend.e); - } else { - /* No need to uninitialize a polling notification. */ - } -} - -static void ma_resource_manager_inline_notification_wait(ma_resource_manager_inline_notification* pNotification) -{ - MA_ASSERT(pNotification != NULL); - - if (ma_resource_manager_is_threading_enabled(pNotification->pResourceManager)) { - ma_async_notification_event_wait(&pNotification->backend.e); - } else { - while (ma_async_notification_poll_is_signalled(&pNotification->backend.p) == MA_FALSE) { - ma_result result = ma_resource_manager_process_next_job(pNotification->pResourceManager); - if (result == MA_NO_DATA_AVAILABLE || result == MA_CANCELLED) { - break; - } - } - } -} - -static void ma_resource_manager_inline_notification_wait_and_uninit(ma_resource_manager_inline_notification* pNotification) -{ - ma_resource_manager_inline_notification_wait(pNotification); - ma_resource_manager_inline_notification_uninit(pNotification); -} - - -static void ma_resource_manager_data_buffer_bst_lock(ma_resource_manager* pResourceManager) -{ - MA_ASSERT(pResourceManager != NULL); - - if (ma_resource_manager_is_threading_enabled(pResourceManager)) { - #ifndef MA_NO_THREADING - { - ma_mutex_lock(&pResourceManager->dataBufferBSTLock); - } - #else - { - MA_ASSERT(MA_FALSE); /* Should never hit this. */ - } - #endif - } else { - /* Threading not enabled. Do nothing. */ - } -} - -static void ma_resource_manager_data_buffer_bst_unlock(ma_resource_manager* pResourceManager) -{ - MA_ASSERT(pResourceManager != NULL); - - if (ma_resource_manager_is_threading_enabled(pResourceManager)) { - #ifndef MA_NO_THREADING - { - ma_mutex_unlock(&pResourceManager->dataBufferBSTLock); - } - #else - { - MA_ASSERT(MA_FALSE); /* Should never hit this. */ - } - #endif - } else { - /* Threading not enabled. Do nothing. */ - } -} - -#ifndef MA_NO_THREADING -static ma_thread_result MA_THREADCALL ma_resource_manager_job_thread(void* pUserData) -{ - ma_resource_manager* pResourceManager = (ma_resource_manager*)pUserData; - MA_ASSERT(pResourceManager != NULL); - - for (;;) { - ma_result result; - ma_job job; - - result = ma_resource_manager_next_job(pResourceManager, &job); - if (result != MA_SUCCESS) { - break; - } - - /* Terminate if we got a quit message. */ - if (job.toc.breakup.code == MA_JOB_TYPE_QUIT) { - break; - } - - ma_job_process(&job); - } - - return (ma_thread_result)0; -} -#endif - -MA_API ma_resource_manager_config ma_resource_manager_config_init(void) -{ - ma_resource_manager_config config; - - MA_ZERO_OBJECT(&config); - config.decodedFormat = ma_format_unknown; - config.decodedChannels = 0; - config.decodedSampleRate = 0; - config.jobThreadCount = 1; /* A single miniaudio-managed job thread by default. */ - config.jobQueueCapacity = MA_JOB_TYPE_RESOURCE_MANAGER_QUEUE_CAPACITY; - - /* Flags. */ - config.flags = 0; - #ifdef MA_NO_THREADING - { - /* Threading is disabled at compile time so disable threading at runtime as well by default. */ - config.flags |= MA_RESOURCE_MANAGER_FLAG_NO_THREADING; - config.jobThreadCount = 0; - } - #endif - - return config; -} - - -MA_API ma_result ma_resource_manager_init(const ma_resource_manager_config* pConfig, ma_resource_manager* pResourceManager) -{ - ma_result result; - ma_job_queue_config jobQueueConfig; - - if (pResourceManager == NULL) { - return MA_INVALID_ARGS; - } - - MA_ZERO_OBJECT(pResourceManager); - - if (pConfig == NULL) { - return MA_INVALID_ARGS; - } - - #ifndef MA_NO_THREADING - { - if (pConfig->jobThreadCount > ma_countof(pResourceManager->jobThreads)) { - return MA_INVALID_ARGS; /* Requesting too many job threads. */ - } - } - #endif - - pResourceManager->config = *pConfig; - ma_allocation_callbacks_init_copy(&pResourceManager->config.allocationCallbacks, &pConfig->allocationCallbacks); - - /* Get the log set up early so we can start using it as soon as possible. */ - if (pResourceManager->config.pLog == NULL) { - result = ma_log_init(&pResourceManager->config.allocationCallbacks, &pResourceManager->log); - if (result == MA_SUCCESS) { - pResourceManager->config.pLog = &pResourceManager->log; - } else { - pResourceManager->config.pLog = NULL; /* Logging is unavailable. */ - } - } - - if (pResourceManager->config.pVFS == NULL) { - result = ma_default_vfs_init(&pResourceManager->defaultVFS, &pResourceManager->config.allocationCallbacks); - if (result != MA_SUCCESS) { - return result; /* Failed to initialize the default file system. */ - } - - pResourceManager->config.pVFS = &pResourceManager->defaultVFS; - } - - /* If threading has been disabled at compile time, enforce it at run time as well. */ - #ifdef MA_NO_THREADING - { - pResourceManager->config.flags |= MA_RESOURCE_MANAGER_FLAG_NO_THREADING; - } - #endif - - /* We need to force MA_RESOURCE_MANAGER_FLAG_NON_BLOCKING if MA_RESOURCE_MANAGER_FLAG_NO_THREADING is set. */ - if ((pResourceManager->config.flags & MA_RESOURCE_MANAGER_FLAG_NO_THREADING) != 0) { - pResourceManager->config.flags |= MA_RESOURCE_MANAGER_FLAG_NON_BLOCKING; - - /* We cannot allow job threads when MA_RESOURCE_MANAGER_FLAG_NO_THREADING has been set. This is an invalid use case. */ - if (pResourceManager->config.jobThreadCount > 0) { - return MA_INVALID_ARGS; - } - } - - /* Job queue. */ - jobQueueConfig.capacity = pResourceManager->config.jobQueueCapacity; - jobQueueConfig.flags = 0; - if ((pResourceManager->config.flags & MA_RESOURCE_MANAGER_FLAG_NON_BLOCKING) != 0) { - if (pResourceManager->config.jobThreadCount > 0) { - return MA_INVALID_ARGS; /* Non-blocking mode is only valid for self-managed job threads. */ - } - - jobQueueConfig.flags |= MA_JOB_QUEUE_FLAG_NON_BLOCKING; - } - - result = ma_job_queue_init(&jobQueueConfig, &pResourceManager->config.allocationCallbacks, &pResourceManager->jobQueue); - if (result != MA_SUCCESS) { - return result; - } - - - /* Custom decoding backends. */ - if (pConfig->ppCustomDecodingBackendVTables != NULL && pConfig->customDecodingBackendCount > 0) { - size_t sizeInBytes = sizeof(*pResourceManager->config.ppCustomDecodingBackendVTables) * pConfig->customDecodingBackendCount; - ma_decoding_backend_vtable** ppCustomDecodingBackendVTables; - - ppCustomDecodingBackendVTables = (ma_decoding_backend_vtable**)ma_malloc(sizeInBytes, &pResourceManager->config.allocationCallbacks); - if (pResourceManager->config.ppCustomDecodingBackendVTables == NULL) { - ma_job_queue_uninit(&pResourceManager->jobQueue, &pResourceManager->config.allocationCallbacks); - return MA_OUT_OF_MEMORY; - } - - MA_COPY_MEMORY(ppCustomDecodingBackendVTables, pConfig->ppCustomDecodingBackendVTables, sizeInBytes); - - pResourceManager->config.ppCustomDecodingBackendVTables = ppCustomDecodingBackendVTables; - pResourceManager->config.customDecodingBackendCount = pConfig->customDecodingBackendCount; - pResourceManager->config.pCustomDecodingBackendUserData = pConfig->pCustomDecodingBackendUserData; - } - - - - /* Here is where we initialize our threading stuff. We don't do this if we don't support threading. */ - if (ma_resource_manager_is_threading_enabled(pResourceManager)) { - #ifndef MA_NO_THREADING - { - ma_uint32 iJobThread; - - /* Data buffer lock. */ - result = ma_mutex_init(&pResourceManager->dataBufferBSTLock); - if (result != MA_SUCCESS) { - ma_job_queue_uninit(&pResourceManager->jobQueue, &pResourceManager->config.allocationCallbacks); - return result; - } - - /* Create the job threads last to ensure the threads has access to valid data. */ - for (iJobThread = 0; iJobThread < pResourceManager->config.jobThreadCount; iJobThread += 1) { - result = ma_thread_create(&pResourceManager->jobThreads[iJobThread], ma_thread_priority_normal, pResourceManager->config.jobThreadStackSize, ma_resource_manager_job_thread, pResourceManager, &pResourceManager->config.allocationCallbacks); - if (result != MA_SUCCESS) { - ma_mutex_uninit(&pResourceManager->dataBufferBSTLock); - ma_job_queue_uninit(&pResourceManager->jobQueue, &pResourceManager->config.allocationCallbacks); - return result; - } - } - } - #else - { - /* Threading is disabled at compile time. We should never get here because validation checks should have already been performed. */ - MA_ASSERT(MA_FALSE); - } - #endif - } - - return MA_SUCCESS; -} - - -static void ma_resource_manager_delete_all_data_buffer_nodes(ma_resource_manager* pResourceManager) -{ - MA_ASSERT(pResourceManager); - - /* If everything was done properly, there shouldn't be any active data buffers. */ - while (pResourceManager->pRootDataBufferNode != NULL) { - ma_resource_manager_data_buffer_node* pDataBufferNode = pResourceManager->pRootDataBufferNode; - ma_resource_manager_data_buffer_node_remove(pResourceManager, pDataBufferNode); - - /* The data buffer has been removed from the BST, so now we need to free its data. */ - ma_resource_manager_data_buffer_node_free(pResourceManager, pDataBufferNode); - } -} - -MA_API void ma_resource_manager_uninit(ma_resource_manager* pResourceManager) -{ - if (pResourceManager == NULL) { - return; - } - - /* - Job threads need to be killed first. To do this we need to post a quit message to the message queue and then wait for the thread. The quit message will never be removed from the - queue which means it will never not be returned after being encountered for the first time which means all threads will eventually receive it. - */ - ma_resource_manager_post_job_quit(pResourceManager); - - /* Wait for every job to finish before continuing to ensure nothing is sill trying to access any of our objects below. */ - if (ma_resource_manager_is_threading_enabled(pResourceManager)) { - #ifndef MA_NO_THREADING - { - ma_uint32 iJobThread; - - for (iJobThread = 0; iJobThread < pResourceManager->config.jobThreadCount; iJobThread += 1) { - ma_thread_wait(&pResourceManager->jobThreads[iJobThread]); - } - } - #else - { - MA_ASSERT(MA_FALSE); /* Should never hit this. */ - } - #endif - } - - /* At this point the thread should have returned and no other thread should be accessing our data. We can now delete all data buffers. */ - ma_resource_manager_delete_all_data_buffer_nodes(pResourceManager); - - /* The job queue is no longer needed. */ - ma_job_queue_uninit(&pResourceManager->jobQueue, &pResourceManager->config.allocationCallbacks); - - /* We're no longer doing anything with data buffers so the lock can now be uninitialized. */ - if (ma_resource_manager_is_threading_enabled(pResourceManager)) { - #ifndef MA_NO_THREADING - { - ma_mutex_uninit(&pResourceManager->dataBufferBSTLock); - } - #else - { - MA_ASSERT(MA_FALSE); /* Should never hit this. */ - } - #endif - } - - ma_free((ma_decoding_backend_vtable**)pResourceManager->config.ppCustomDecodingBackendVTables, &pResourceManager->config.allocationCallbacks); /* <-- Naughty const-cast, but this is safe. */ - - if (pResourceManager->config.pLog == &pResourceManager->log) { - ma_log_uninit(&pResourceManager->log); - } -} - -MA_API ma_log* ma_resource_manager_get_log(ma_resource_manager* pResourceManager) -{ - if (pResourceManager == NULL) { - return NULL; - } - - return pResourceManager->config.pLog; -} - - - -MA_API ma_resource_manager_data_source_config ma_resource_manager_data_source_config_init(void) -{ - ma_resource_manager_data_source_config config; - - MA_ZERO_OBJECT(&config); - config.rangeBegInPCMFrames = MA_DATA_SOURCE_DEFAULT_RANGE_BEG; - config.rangeEndInPCMFrames = MA_DATA_SOURCE_DEFAULT_RANGE_END; - config.loopPointBegInPCMFrames = MA_DATA_SOURCE_DEFAULT_LOOP_POINT_BEG; - config.loopPointEndInPCMFrames = MA_DATA_SOURCE_DEFAULT_LOOP_POINT_END; - config.isLooping = MA_FALSE; - - return config; -} - - -static ma_decoder_config ma_resource_manager__init_decoder_config(ma_resource_manager* pResourceManager) -{ - ma_decoder_config config; - - config = ma_decoder_config_init(pResourceManager->config.decodedFormat, pResourceManager->config.decodedChannels, pResourceManager->config.decodedSampleRate); - config.allocationCallbacks = pResourceManager->config.allocationCallbacks; - config.ppCustomBackendVTables = pResourceManager->config.ppCustomDecodingBackendVTables; - config.customBackendCount = pResourceManager->config.customDecodingBackendCount; - config.pCustomBackendUserData = pResourceManager->config.pCustomDecodingBackendUserData; - - return config; -} - -static ma_result ma_resource_manager__init_decoder(ma_resource_manager* pResourceManager, const char* pFilePath, const wchar_t* pFilePathW, ma_decoder* pDecoder) -{ - ma_result result; - ma_decoder_config config; - - MA_ASSERT(pResourceManager != NULL); - MA_ASSERT(pFilePath != NULL || pFilePathW != NULL); - MA_ASSERT(pDecoder != NULL); - - config = ma_resource_manager__init_decoder_config(pResourceManager); - - if (pFilePath != NULL) { - result = ma_decoder_init_vfs(pResourceManager->config.pVFS, pFilePath, &config, pDecoder); - if (result != MA_SUCCESS) { - ma_log_postf(ma_resource_manager_get_log(pResourceManager), MA_LOG_LEVEL_WARNING, "Failed to load file \"%s\". %s.\n", pFilePath, ma_result_description(result)); - return result; - } - } else { - result = ma_decoder_init_vfs_w(pResourceManager->config.pVFS, pFilePathW, &config, pDecoder); - if (result != MA_SUCCESS) { - #if (defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L) || defined(_MSC_VER) - ma_log_postf(ma_resource_manager_get_log(pResourceManager), MA_LOG_LEVEL_WARNING, "Failed to load file \"%ls\". %s.\n", pFilePathW, ma_result_description(result)); - #endif - return result; - } - } - - return MA_SUCCESS; -} - -static ma_bool32 ma_resource_manager_data_buffer_has_connector(ma_resource_manager_data_buffer* pDataBuffer) -{ - return ma_atomic_bool32_get(&pDataBuffer->isConnectorInitialized); -} - -static ma_data_source* ma_resource_manager_data_buffer_get_connector(ma_resource_manager_data_buffer* pDataBuffer) -{ - if (ma_resource_manager_data_buffer_has_connector(pDataBuffer) == MA_FALSE) { - return NULL; /* Connector not yet initialized. */ - } - - switch (pDataBuffer->pNode->data.type) - { - case ma_resource_manager_data_supply_type_encoded: return &pDataBuffer->connector.decoder; - case ma_resource_manager_data_supply_type_decoded: return &pDataBuffer->connector.buffer; - case ma_resource_manager_data_supply_type_decoded_paged: return &pDataBuffer->connector.pagedBuffer; - - case ma_resource_manager_data_supply_type_unknown: - default: - { - ma_log_postf(ma_resource_manager_get_log(pDataBuffer->pResourceManager), MA_LOG_LEVEL_ERROR, "Failed to retrieve data buffer connector. Unknown data supply type.\n"); - return NULL; - }; - }; -} - -static ma_result ma_resource_manager_data_buffer_init_connector(ma_resource_manager_data_buffer* pDataBuffer, const ma_resource_manager_data_source_config* pConfig, ma_async_notification* pInitNotification, ma_fence* pInitFence) -{ - ma_result result; - - MA_ASSERT(pDataBuffer != NULL); - MA_ASSERT(pConfig != NULL); - MA_ASSERT(ma_resource_manager_data_buffer_has_connector(pDataBuffer) == MA_FALSE); - - /* The underlying data buffer must be initialized before we'll be able to know how to initialize the backend. */ - result = ma_resource_manager_data_buffer_node_result(pDataBuffer->pNode); - if (result != MA_SUCCESS && result != MA_BUSY) { - return result; /* The data buffer is in an erroneous state. */ - } - - /* - We need to initialize either a ma_decoder or an ma_audio_buffer depending on whether or not the backing data is encoded or decoded. These act as the - "instance" to the data and are used to form the connection between underlying data buffer and the data source. If the data buffer is decoded, we can use - an ma_audio_buffer. This enables us to use memory mapping when mixing which saves us a bit of data movement overhead. - */ - switch (ma_resource_manager_data_buffer_node_get_data_supply_type(pDataBuffer->pNode)) - { - case ma_resource_manager_data_supply_type_encoded: /* Connector is a decoder. */ - { - ma_decoder_config config; - config = ma_resource_manager__init_decoder_config(pDataBuffer->pResourceManager); - result = ma_decoder_init_memory(pDataBuffer->pNode->data.backend.encoded.pData, pDataBuffer->pNode->data.backend.encoded.sizeInBytes, &config, &pDataBuffer->connector.decoder); - } break; - - case ma_resource_manager_data_supply_type_decoded: /* Connector is an audio buffer. */ - { - ma_audio_buffer_config config; - config = ma_audio_buffer_config_init(pDataBuffer->pNode->data.backend.decoded.format, pDataBuffer->pNode->data.backend.decoded.channels, pDataBuffer->pNode->data.backend.decoded.totalFrameCount, pDataBuffer->pNode->data.backend.decoded.pData, NULL); - result = ma_audio_buffer_init(&config, &pDataBuffer->connector.buffer); - } break; - - case ma_resource_manager_data_supply_type_decoded_paged: /* Connector is a paged audio buffer. */ - { - ma_paged_audio_buffer_config config; - config = ma_paged_audio_buffer_config_init(&pDataBuffer->pNode->data.backend.decodedPaged.data); - result = ma_paged_audio_buffer_init(&config, &pDataBuffer->connector.pagedBuffer); - } break; - - case ma_resource_manager_data_supply_type_unknown: - default: - { - /* Unknown data supply type. Should never happen. Need to post an error here. */ - return MA_INVALID_ARGS; - }; - } - - /* - Initialization of the connector is when we can fire the init notification. This will give the application access to - the format/channels/rate of the data source. - */ - if (result == MA_SUCCESS) { - /* - The resource manager supports the ability to set the range and loop settings via a config at - initialization time. This results in an case where the ranges could be set explicitly via - ma_data_source_set_*() before we get to this point here. If this happens, we'll end up - hitting a case where we just override those settings which results in what feels like a bug. - - To address this we only change the relevant properties if they're not equal to defaults. If - they're equal to defaults there's no need to change them anyway. If they're *not* set to the - default values, we can assume the user has set the range and loop settings via the config. If - they're doing their own calls to ma_data_source_set_*() in addition to setting them via the - config, that's entirely on the caller and any synchronization issue becomes their problem. - */ - if (pConfig->rangeBegInPCMFrames != MA_DATA_SOURCE_DEFAULT_RANGE_BEG || pConfig->rangeEndInPCMFrames != MA_DATA_SOURCE_DEFAULT_RANGE_END) { - ma_data_source_set_range_in_pcm_frames(pDataBuffer, pConfig->rangeBegInPCMFrames, pConfig->rangeEndInPCMFrames); - } - - if (pConfig->loopPointBegInPCMFrames != MA_DATA_SOURCE_DEFAULT_LOOP_POINT_BEG || pConfig->loopPointEndInPCMFrames != MA_DATA_SOURCE_DEFAULT_LOOP_POINT_END) { - ma_data_source_set_loop_point_in_pcm_frames(pDataBuffer, pConfig->loopPointBegInPCMFrames, pConfig->loopPointEndInPCMFrames); - } - - if (pConfig->isLooping != MA_FALSE) { - ma_data_source_set_looping(pDataBuffer, pConfig->isLooping); - } - - ma_atomic_bool32_set(&pDataBuffer->isConnectorInitialized, MA_TRUE); - - if (pInitNotification != NULL) { - ma_async_notification_signal(pInitNotification); - } - - if (pInitFence != NULL) { - ma_fence_release(pInitFence); - } - } - - /* At this point the backend should be initialized. We do *not* want to set pDataSource->result here - that needs to be done at a higher level to ensure it's done as the last step. */ - return result; -} - -static ma_result ma_resource_manager_data_buffer_uninit_connector(ma_resource_manager* pResourceManager, ma_resource_manager_data_buffer* pDataBuffer) -{ - MA_ASSERT(pResourceManager != NULL); - MA_ASSERT(pDataBuffer != NULL); - - (void)pResourceManager; - - switch (ma_resource_manager_data_buffer_node_get_data_supply_type(pDataBuffer->pNode)) - { - case ma_resource_manager_data_supply_type_encoded: /* Connector is a decoder. */ - { - ma_decoder_uninit(&pDataBuffer->connector.decoder); - } break; - - case ma_resource_manager_data_supply_type_decoded: /* Connector is an audio buffer. */ - { - ma_audio_buffer_uninit(&pDataBuffer->connector.buffer); - } break; - - case ma_resource_manager_data_supply_type_decoded_paged: /* Connector is a paged audio buffer. */ - { - ma_paged_audio_buffer_uninit(&pDataBuffer->connector.pagedBuffer); - } break; - - case ma_resource_manager_data_supply_type_unknown: - default: - { - /* Unknown data supply type. Should never happen. Need to post an error here. */ - return MA_INVALID_ARGS; - }; - } - - return MA_SUCCESS; -} - -static ma_uint32 ma_resource_manager_data_buffer_node_next_execution_order(ma_resource_manager_data_buffer_node* pDataBufferNode) -{ - MA_ASSERT(pDataBufferNode != NULL); - return ma_atomic_fetch_add_32(&pDataBufferNode->executionCounter, 1); -} - -static ma_result ma_resource_manager_data_buffer_node_init_supply_encoded(ma_resource_manager* pResourceManager, ma_resource_manager_data_buffer_node* pDataBufferNode, const char* pFilePath, const wchar_t* pFilePathW) -{ - ma_result result; - size_t dataSizeInBytes; - void* pData; - - MA_ASSERT(pResourceManager != NULL); - MA_ASSERT(pDataBufferNode != NULL); - MA_ASSERT(pFilePath != NULL || pFilePathW != NULL); - - result = ma_vfs_open_and_read_file_ex(pResourceManager->config.pVFS, pFilePath, pFilePathW, &pData, &dataSizeInBytes, &pResourceManager->config.allocationCallbacks); - if (result != MA_SUCCESS) { - if (pFilePath != NULL) { - ma_log_postf(ma_resource_manager_get_log(pResourceManager), MA_LOG_LEVEL_WARNING, "Failed to load file \"%s\". %s.\n", pFilePath, ma_result_description(result)); - } else { - #if (defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L) || defined(_MSC_VER) - ma_log_postf(ma_resource_manager_get_log(pResourceManager), MA_LOG_LEVEL_WARNING, "Failed to load file \"%ls\". %s.\n", pFilePathW, ma_result_description(result)); - #endif - } - - return result; - } - - pDataBufferNode->data.backend.encoded.pData = pData; - pDataBufferNode->data.backend.encoded.sizeInBytes = dataSizeInBytes; - ma_resource_manager_data_buffer_node_set_data_supply_type(pDataBufferNode, ma_resource_manager_data_supply_type_encoded); /* <-- Must be set last. */ - - return MA_SUCCESS; -} - -static ma_result ma_resource_manager_data_buffer_node_init_supply_decoded(ma_resource_manager* pResourceManager, ma_resource_manager_data_buffer_node* pDataBufferNode, const char* pFilePath, const wchar_t* pFilePathW, ma_uint32 flags, ma_decoder** ppDecoder) -{ - ma_result result = MA_SUCCESS; - ma_decoder* pDecoder; - ma_uint64 totalFrameCount; - - MA_ASSERT(pResourceManager != NULL); - MA_ASSERT(pDataBufferNode != NULL); - MA_ASSERT(ppDecoder != NULL); - MA_ASSERT(pFilePath != NULL || pFilePathW != NULL); - - *ppDecoder = NULL; /* For safety. */ - - pDecoder = (ma_decoder*)ma_malloc(sizeof(*pDecoder), &pResourceManager->config.allocationCallbacks); - if (pDecoder == NULL) { - return MA_OUT_OF_MEMORY; - } - - result = ma_resource_manager__init_decoder(pResourceManager, pFilePath, pFilePathW, pDecoder); - if (result != MA_SUCCESS) { - ma_free(pDecoder, &pResourceManager->config.allocationCallbacks); - return result; - } - - /* - At this point we have the decoder and we now need to initialize the data supply. This will - be either a decoded buffer, or a decoded paged buffer. A regular buffer is just one big heap - allocated buffer, whereas a paged buffer is a linked list of paged-sized buffers. The latter - is used when the length of a sound is unknown until a full decode has been performed. - */ - if ((flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_UNKNOWN_LENGTH) == 0) { - result = ma_decoder_get_length_in_pcm_frames(pDecoder, &totalFrameCount); - if (result != MA_SUCCESS) { - return result; - } - } else { - totalFrameCount = 0; - } - - if (totalFrameCount > 0) { - /* It's a known length. The data supply is a regular decoded buffer. */ - ma_uint64 dataSizeInBytes; - void* pData; - - dataSizeInBytes = totalFrameCount * ma_get_bytes_per_frame(pDecoder->outputFormat, pDecoder->outputChannels); - if (dataSizeInBytes > MA_SIZE_MAX) { - ma_decoder_uninit(pDecoder); - ma_free(pDecoder, &pResourceManager->config.allocationCallbacks); - return MA_TOO_BIG; - } - - pData = ma_malloc((size_t)dataSizeInBytes, &pResourceManager->config.allocationCallbacks); - if (pData == NULL) { - ma_decoder_uninit(pDecoder); - ma_free(pDecoder, &pResourceManager->config.allocationCallbacks); - return MA_OUT_OF_MEMORY; - } - - /* The buffer needs to be initialized to silence in case the caller reads from it. */ - ma_silence_pcm_frames(pData, totalFrameCount, pDecoder->outputFormat, pDecoder->outputChannels); - - /* Data has been allocated and the data supply can now be initialized. */ - pDataBufferNode->data.backend.decoded.pData = pData; - pDataBufferNode->data.backend.decoded.totalFrameCount = totalFrameCount; - pDataBufferNode->data.backend.decoded.format = pDecoder->outputFormat; - pDataBufferNode->data.backend.decoded.channels = pDecoder->outputChannels; - pDataBufferNode->data.backend.decoded.sampleRate = pDecoder->outputSampleRate; - pDataBufferNode->data.backend.decoded.decodedFrameCount = 0; - ma_resource_manager_data_buffer_node_set_data_supply_type(pDataBufferNode, ma_resource_manager_data_supply_type_decoded); /* <-- Must be set last. */ - } else { - /* - It's an unknown length. The data supply is a paged decoded buffer. Setting this up is - actually easier than the non-paged decoded buffer because we just need to initialize - a ma_paged_audio_buffer object. - */ - result = ma_paged_audio_buffer_data_init(pDecoder->outputFormat, pDecoder->outputChannels, &pDataBufferNode->data.backend.decodedPaged.data); - if (result != MA_SUCCESS) { - ma_decoder_uninit(pDecoder); - ma_free(pDecoder, &pResourceManager->config.allocationCallbacks); - return result; - } - - pDataBufferNode->data.backend.decodedPaged.sampleRate = pDecoder->outputSampleRate; - pDataBufferNode->data.backend.decodedPaged.decodedFrameCount = 0; - ma_resource_manager_data_buffer_node_set_data_supply_type(pDataBufferNode, ma_resource_manager_data_supply_type_decoded_paged); /* <-- Must be set last. */ - } - - *ppDecoder = pDecoder; - - return MA_SUCCESS; -} - -static ma_result ma_resource_manager_data_buffer_node_decode_next_page(ma_resource_manager* pResourceManager, ma_resource_manager_data_buffer_node* pDataBufferNode, ma_decoder* pDecoder) -{ - ma_result result = MA_SUCCESS; - ma_uint64 pageSizeInFrames; - ma_uint64 framesToTryReading; - ma_uint64 framesRead; - - MA_ASSERT(pResourceManager != NULL); - MA_ASSERT(pDataBufferNode != NULL); - MA_ASSERT(pDecoder != NULL); - - /* We need to know the size of a page in frames to know how many frames to decode. */ - pageSizeInFrames = MA_RESOURCE_MANAGER_PAGE_SIZE_IN_MILLISECONDS * (pDecoder->outputSampleRate/1000); - framesToTryReading = pageSizeInFrames; - - /* - Here is where we do the decoding of the next page. We'll run a slightly different path depending - on whether or not we're using a flat or paged buffer because the allocation of the page differs - between the two. For a flat buffer it's an offset to an already-allocated buffer. For a paged - buffer, we need to allocate a new page and attach it to the linked list. - */ - switch (ma_resource_manager_data_buffer_node_get_data_supply_type(pDataBufferNode)) - { - case ma_resource_manager_data_supply_type_decoded: - { - /* The destination buffer is an offset to the existing buffer. Don't read more than we originally retrieved when we first initialized the decoder. */ - void* pDst; - ma_uint64 framesRemaining = pDataBufferNode->data.backend.decoded.totalFrameCount - pDataBufferNode->data.backend.decoded.decodedFrameCount; - if (framesToTryReading > framesRemaining) { - framesToTryReading = framesRemaining; - } - - if (framesToTryReading > 0) { - pDst = ma_offset_ptr( - pDataBufferNode->data.backend.decoded.pData, - pDataBufferNode->data.backend.decoded.decodedFrameCount * ma_get_bytes_per_frame(pDataBufferNode->data.backend.decoded.format, pDataBufferNode->data.backend.decoded.channels) - ); - MA_ASSERT(pDst != NULL); - - result = ma_decoder_read_pcm_frames(pDecoder, pDst, framesToTryReading, &framesRead); - if (framesRead > 0) { - pDataBufferNode->data.backend.decoded.decodedFrameCount += framesRead; - } - } else { - framesRead = 0; - } - } break; - - case ma_resource_manager_data_supply_type_decoded_paged: - { - /* The destination buffer is a freshly allocated page. */ - ma_paged_audio_buffer_page* pPage; - - result = ma_paged_audio_buffer_data_allocate_page(&pDataBufferNode->data.backend.decodedPaged.data, framesToTryReading, NULL, &pResourceManager->config.allocationCallbacks, &pPage); - if (result != MA_SUCCESS) { - return result; - } - - result = ma_decoder_read_pcm_frames(pDecoder, pPage->pAudioData, framesToTryReading, &framesRead); - if (result == MA_SUCCESS && framesRead > 0) { - pPage->sizeInFrames = framesRead; - - result = ma_paged_audio_buffer_data_append_page(&pDataBufferNode->data.backend.decodedPaged.data, pPage); - if (result == MA_SUCCESS) { - pDataBufferNode->data.backend.decodedPaged.decodedFrameCount += framesRead; - } else { - /* Failed to append the page. Just abort and set the status to MA_AT_END. */ - ma_paged_audio_buffer_data_free_page(&pDataBufferNode->data.backend.decodedPaged.data, pPage, &pResourceManager->config.allocationCallbacks); - result = MA_AT_END; - } - } else { - /* No frames were read. Free the page and just set the status to MA_AT_END. */ - ma_paged_audio_buffer_data_free_page(&pDataBufferNode->data.backend.decodedPaged.data, pPage, &pResourceManager->config.allocationCallbacks); - result = MA_AT_END; - } - } break; - - case ma_resource_manager_data_supply_type_encoded: - case ma_resource_manager_data_supply_type_unknown: - default: - { - /* Unexpected data supply type. */ - ma_log_postf(ma_resource_manager_get_log(pResourceManager), MA_LOG_LEVEL_ERROR, "Unexpected data supply type (%d) when decoding page.", ma_resource_manager_data_buffer_node_get_data_supply_type(pDataBufferNode)); - return MA_ERROR; - }; - } - - if (result == MA_SUCCESS && framesRead == 0) { - result = MA_AT_END; - } - - return result; -} - -static ma_result ma_resource_manager_data_buffer_node_acquire_critical_section(ma_resource_manager* pResourceManager, const char* pFilePath, const wchar_t* pFilePathW, ma_uint32 hashedName32, ma_uint32 flags, const ma_resource_manager_data_supply* pExistingData, ma_fence* pInitFence, ma_fence* pDoneFence, ma_resource_manager_inline_notification* pInitNotification, ma_resource_manager_data_buffer_node** ppDataBufferNode) -{ - ma_result result = MA_SUCCESS; - ma_resource_manager_data_buffer_node* pDataBufferNode = NULL; - ma_resource_manager_data_buffer_node* pInsertPoint; - - if (ppDataBufferNode != NULL) { - *ppDataBufferNode = NULL; - } - - result = ma_resource_manager_data_buffer_node_insert_point(pResourceManager, hashedName32, &pInsertPoint); - if (result == MA_ALREADY_EXISTS) { - /* The node already exists. We just need to increment the reference count. */ - pDataBufferNode = pInsertPoint; - - result = ma_resource_manager_data_buffer_node_increment_ref(pResourceManager, pDataBufferNode, NULL); - if (result != MA_SUCCESS) { - return result; /* Should never happen. Failed to increment the reference count. */ - } - - result = MA_ALREADY_EXISTS; - goto done; - } else { - /* - The node does not already exist. We need to post a LOAD_DATA_BUFFER_NODE job here. This - needs to be done inside the critical section to ensure an uninitialization of the node - does not occur before initialization on another thread. - */ - pDataBufferNode = (ma_resource_manager_data_buffer_node*)ma_malloc(sizeof(*pDataBufferNode), &pResourceManager->config.allocationCallbacks); - if (pDataBufferNode == NULL) { - return MA_OUT_OF_MEMORY; - } - - MA_ZERO_OBJECT(pDataBufferNode); - pDataBufferNode->hashedName32 = hashedName32; - pDataBufferNode->refCount = 1; /* Always set to 1 by default (this is our first reference). */ - - if (pExistingData == NULL) { - pDataBufferNode->data.type = ma_resource_manager_data_supply_type_unknown; /* <-- We won't know this until we start decoding. */ - pDataBufferNode->result = MA_BUSY; /* Must be set to MA_BUSY before we leave the critical section, so might as well do it now. */ - pDataBufferNode->isDataOwnedByResourceManager = MA_TRUE; - } else { - pDataBufferNode->data = *pExistingData; - pDataBufferNode->result = MA_SUCCESS; /* Not loading asynchronously, so just set the status */ - pDataBufferNode->isDataOwnedByResourceManager = MA_FALSE; - } - - result = ma_resource_manager_data_buffer_node_insert_at(pResourceManager, pDataBufferNode, pInsertPoint); - if (result != MA_SUCCESS) { - ma_free(pDataBufferNode, &pResourceManager->config.allocationCallbacks); - return result; /* Should never happen. Failed to insert the data buffer into the BST. */ - } - - /* - Here is where we'll post the job, but only if we're loading asynchronously. If we're - loading synchronously we'll defer loading to a later stage, outside of the critical - section. - */ - if (pDataBufferNode->isDataOwnedByResourceManager && (flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_ASYNC) != 0) { - /* Loading asynchronously. Post the job. */ - ma_job job; - char* pFilePathCopy = NULL; - wchar_t* pFilePathWCopy = NULL; - - /* We need a copy of the file path. We should probably make this more efficient, but for now we'll do a transient memory allocation. */ - if (pFilePath != NULL) { - pFilePathCopy = ma_copy_string(pFilePath, &pResourceManager->config.allocationCallbacks); - } else { - pFilePathWCopy = ma_copy_string_w(pFilePathW, &pResourceManager->config.allocationCallbacks); - } - - if (pFilePathCopy == NULL && pFilePathWCopy == NULL) { - ma_resource_manager_data_buffer_node_remove(pResourceManager, pDataBufferNode); - ma_free(pDataBufferNode, &pResourceManager->config.allocationCallbacks); - return MA_OUT_OF_MEMORY; - } - - if ((flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_WAIT_INIT) != 0) { - ma_resource_manager_inline_notification_init(pResourceManager, pInitNotification); - } - - /* Acquire init and done fences before posting the job. These will be unacquired by the job thread. */ - if (pInitFence != NULL) { ma_fence_acquire(pInitFence); } - if (pDoneFence != NULL) { ma_fence_acquire(pDoneFence); } - - /* We now have everything we need to post the job to the job thread. */ - job = ma_job_init(MA_JOB_TYPE_RESOURCE_MANAGER_LOAD_DATA_BUFFER_NODE); - job.order = ma_resource_manager_data_buffer_node_next_execution_order(pDataBufferNode); - job.data.resourceManager.loadDataBufferNode.pResourceManager = pResourceManager; - job.data.resourceManager.loadDataBufferNode.pDataBufferNode = pDataBufferNode; - job.data.resourceManager.loadDataBufferNode.pFilePath = pFilePathCopy; - job.data.resourceManager.loadDataBufferNode.pFilePathW = pFilePathWCopy; - job.data.resourceManager.loadDataBufferNode.flags = flags; - job.data.resourceManager.loadDataBufferNode.pInitNotification = ((flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_WAIT_INIT) != 0) ? pInitNotification : NULL; - job.data.resourceManager.loadDataBufferNode.pDoneNotification = NULL; - job.data.resourceManager.loadDataBufferNode.pInitFence = pInitFence; - job.data.resourceManager.loadDataBufferNode.pDoneFence = pDoneFence; - - if ((flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_WAIT_INIT) != 0) { - result = ma_job_process(&job); - } else { - result = ma_resource_manager_post_job(pResourceManager, &job); - } - - if (result != MA_SUCCESS) { - /* Failed to post job. Probably ran out of memory. */ - ma_log_postf(ma_resource_manager_get_log(pResourceManager), MA_LOG_LEVEL_ERROR, "Failed to post MA_JOB_TYPE_RESOURCE_MANAGER_LOAD_DATA_BUFFER_NODE job. %s.\n", ma_result_description(result)); - - /* - Fences were acquired before posting the job, but since the job was not able to - be posted, we need to make sure we release them so nothing gets stuck waiting. - */ - if (pInitFence != NULL) { ma_fence_release(pInitFence); } - if (pDoneFence != NULL) { ma_fence_release(pDoneFence); } - - if ((flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_WAIT_INIT) != 0) { - ma_resource_manager_inline_notification_uninit(pInitNotification); - } else { - /* These will have been freed by the job thread, but with WAIT_INIT they will already have happened since the job has already been handled. */ - ma_free(pFilePathCopy, &pResourceManager->config.allocationCallbacks); - ma_free(pFilePathWCopy, &pResourceManager->config.allocationCallbacks); - } - - ma_resource_manager_data_buffer_node_remove(pResourceManager, pDataBufferNode); - ma_free(pDataBufferNode, &pResourceManager->config.allocationCallbacks); - - return result; - } - } - } - -done: - if (ppDataBufferNode != NULL) { - *ppDataBufferNode = pDataBufferNode; - } - - return result; -} - -static ma_result ma_resource_manager_data_buffer_node_acquire(ma_resource_manager* pResourceManager, const char* pFilePath, const wchar_t* pFilePathW, ma_uint32 hashedName32, ma_uint32 flags, const ma_resource_manager_data_supply* pExistingData, ma_fence* pInitFence, ma_fence* pDoneFence, ma_resource_manager_data_buffer_node** ppDataBufferNode) -{ - ma_result result = MA_SUCCESS; - ma_bool32 nodeAlreadyExists = MA_FALSE; - ma_resource_manager_data_buffer_node* pDataBufferNode = NULL; - ma_resource_manager_inline_notification initNotification; /* Used when the WAIT_INIT flag is set. */ - - if (ppDataBufferNode != NULL) { - *ppDataBufferNode = NULL; /* Safety. */ - } - - if (pResourceManager == NULL || (pFilePath == NULL && pFilePathW == NULL && hashedName32 == 0)) { - return MA_INVALID_ARGS; - } - - /* If we're specifying existing data, it must be valid. */ - if (pExistingData != NULL && pExistingData->type == ma_resource_manager_data_supply_type_unknown) { - return MA_INVALID_ARGS; - } - - /* If we don't support threading, remove the ASYNC flag to make the rest of this a bit simpler. */ - if (ma_resource_manager_is_threading_enabled(pResourceManager) == MA_FALSE) { - flags &= ~MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_ASYNC; - } - - if (hashedName32 == 0) { - if (pFilePath != NULL) { - hashedName32 = ma_hash_string_32(pFilePath); - } else { - hashedName32 = ma_hash_string_w_32(pFilePathW); - } - } - - /* - Here is where we either increment the node's reference count or allocate a new one and add it - to the BST. When allocating a new node, we need to make sure the LOAD_DATA_BUFFER_NODE job is - posted inside the critical section just in case the caller immediately uninitializes the node - as this will ensure the FREE_DATA_BUFFER_NODE job is given an execution order such that the - node is not uninitialized before initialization. - */ - ma_resource_manager_data_buffer_bst_lock(pResourceManager); - { - result = ma_resource_manager_data_buffer_node_acquire_critical_section(pResourceManager, pFilePath, pFilePathW, hashedName32, flags, pExistingData, pInitFence, pDoneFence, &initNotification, &pDataBufferNode); - } - ma_resource_manager_data_buffer_bst_unlock(pResourceManager); - - if (result == MA_ALREADY_EXISTS) { - nodeAlreadyExists = MA_TRUE; - result = MA_SUCCESS; - } else { - if (result != MA_SUCCESS) { - return result; - } - } - - /* - If we're loading synchronously, we'll need to load everything now. When loading asynchronously, - a job will have been posted inside the BST critical section so that an uninitialization can be - allocated an appropriate execution order thereby preventing it from being uninitialized before - the node is initialized by the decoding thread(s). - */ - if (nodeAlreadyExists == MA_FALSE) { /* Don't need to try loading anything if the node already exists. */ - if (pFilePath == NULL && pFilePathW == NULL) { - /* - If this path is hit, it means a buffer is being copied (i.e. initialized from only the - hashed name), but that node has been freed in the meantime, probably from some other - thread. This is an invalid operation. - */ - ma_log_postf(ma_resource_manager_get_log(pResourceManager), MA_LOG_LEVEL_WARNING, "Cloning data buffer node failed because the source node was released. The source node must remain valid until the cloning has completed.\n"); - result = MA_INVALID_OPERATION; - goto done; - } - - if (pDataBufferNode->isDataOwnedByResourceManager) { - if ((flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_ASYNC) == 0) { - /* Loading synchronously. Load the sound in it's entirety here. */ - if ((flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_DECODE) == 0) { - /* No decoding. This is the simple case - just store the file contents in memory. */ - result = ma_resource_manager_data_buffer_node_init_supply_encoded(pResourceManager, pDataBufferNode, pFilePath, pFilePathW); - if (result != MA_SUCCESS) { - goto done; - } - } else { - /* Decoding. We do this the same way as we do when loading asynchronously. */ - ma_decoder* pDecoder; - result = ma_resource_manager_data_buffer_node_init_supply_decoded(pResourceManager, pDataBufferNode, pFilePath, pFilePathW, flags, &pDecoder); - if (result != MA_SUCCESS) { - goto done; - } - - /* We have the decoder, now decode page by page just like we do when loading asynchronously. */ - for (;;) { - /* Decode next page. */ - result = ma_resource_manager_data_buffer_node_decode_next_page(pResourceManager, pDataBufferNode, pDecoder); - if (result != MA_SUCCESS) { - break; /* Will return MA_AT_END when the last page has been decoded. */ - } - } - - /* Reaching the end needs to be considered successful. */ - if (result == MA_AT_END) { - result = MA_SUCCESS; - } - - /* - At this point the data buffer is either fully decoded or some error occurred. Either - way, the decoder is no longer necessary. - */ - ma_decoder_uninit(pDecoder); - ma_free(pDecoder, &pResourceManager->config.allocationCallbacks); - } - - /* Getting here means we were successful. Make sure the status of the node is updated accordingly. */ - ma_atomic_exchange_i32(&pDataBufferNode->result, result); - } else { - /* Loading asynchronously. We may need to wait for initialization. */ - if ((flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_WAIT_INIT) != 0) { - ma_resource_manager_inline_notification_wait(&initNotification); - } - } - } else { - /* The data is not managed by the resource manager so there's nothing else to do. */ - MA_ASSERT(pExistingData != NULL); - } - } - -done: - /* If we failed to initialize the data buffer we need to free it. */ - if (result != MA_SUCCESS) { - if (nodeAlreadyExists == MA_FALSE) { - ma_resource_manager_data_buffer_node_remove(pResourceManager, pDataBufferNode); - ma_free(pDataBufferNode, &pResourceManager->config.allocationCallbacks); - } - } - - /* - The init notification needs to be uninitialized. This will be used if the node does not already - exist, and we've specified ASYNC | WAIT_INIT. - */ - if (nodeAlreadyExists == MA_FALSE && pDataBufferNode->isDataOwnedByResourceManager && (flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_ASYNC) != 0) { - if ((flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_WAIT_INIT) != 0) { - ma_resource_manager_inline_notification_uninit(&initNotification); - } - } - - if (ppDataBufferNode != NULL) { - *ppDataBufferNode = pDataBufferNode; - } - - return result; -} - -static ma_result ma_resource_manager_data_buffer_node_unacquire(ma_resource_manager* pResourceManager, ma_resource_manager_data_buffer_node* pDataBufferNode, const char* pName, const wchar_t* pNameW) -{ - ma_result result = MA_SUCCESS; - ma_uint32 refCount = 0xFFFFFFFF; /* The new reference count of the node after decrementing. Initialize to non-0 to be safe we don't fall into the freeing path. */ - ma_uint32 hashedName32 = 0; - - if (pResourceManager == NULL) { - return MA_INVALID_ARGS; - } - - if (pDataBufferNode == NULL) { - if (pName == NULL && pNameW == NULL) { - return MA_INVALID_ARGS; - } - - if (pName != NULL) { - hashedName32 = ma_hash_string_32(pName); - } else { - hashedName32 = ma_hash_string_w_32(pNameW); - } - } - - /* - The first thing to do is decrement the reference counter of the node. Then, if the reference - count is zero, we need to free the node. If the node is still in the process of loading, we'll - need to post a job to the job queue to free the node. Otherwise we'll just do it here. - */ - ma_resource_manager_data_buffer_bst_lock(pResourceManager); - { - /* Might need to find the node. Must be done inside the critical section. */ - if (pDataBufferNode == NULL) { - result = ma_resource_manager_data_buffer_node_search(pResourceManager, hashedName32, &pDataBufferNode); - if (result != MA_SUCCESS) { - goto stage2; /* Couldn't find the node. */ - } - } - - result = ma_resource_manager_data_buffer_node_decrement_ref(pResourceManager, pDataBufferNode, &refCount); - if (result != MA_SUCCESS) { - goto stage2; /* Should never happen. */ - } - - if (refCount == 0) { - result = ma_resource_manager_data_buffer_node_remove(pResourceManager, pDataBufferNode); - if (result != MA_SUCCESS) { - goto stage2; /* An error occurred when trying to remove the data buffer. This should never happen. */ - } - } - } - ma_resource_manager_data_buffer_bst_unlock(pResourceManager); - -stage2: - if (result != MA_SUCCESS) { - return result; - } - - /* - Here is where we need to free the node. We don't want to do this inside the critical section - above because we want to keep that as small as possible for multi-threaded efficiency. - */ - if (refCount == 0) { - if (ma_resource_manager_data_buffer_node_result(pDataBufferNode) == MA_BUSY) { - /* The sound is still loading. We need to delay the freeing of the node to a safe time. */ - ma_job job; - - /* We need to mark the node as unavailable for the sake of the resource manager worker threads. */ - ma_atomic_exchange_i32(&pDataBufferNode->result, MA_UNAVAILABLE); - - job = ma_job_init(MA_JOB_TYPE_RESOURCE_MANAGER_FREE_DATA_BUFFER_NODE); - job.order = ma_resource_manager_data_buffer_node_next_execution_order(pDataBufferNode); - job.data.resourceManager.freeDataBufferNode.pResourceManager = pResourceManager; - job.data.resourceManager.freeDataBufferNode.pDataBufferNode = pDataBufferNode; - - result = ma_resource_manager_post_job(pResourceManager, &job); - if (result != MA_SUCCESS) { - ma_log_postf(ma_resource_manager_get_log(pResourceManager), MA_LOG_LEVEL_ERROR, "Failed to post MA_JOB_TYPE_RESOURCE_MANAGER_FREE_DATA_BUFFER_NODE job. %s.\n", ma_result_description(result)); - return result; - } - - /* If we don't support threading, process the job queue here. */ - if (ma_resource_manager_is_threading_enabled(pResourceManager) == MA_FALSE) { - while (ma_resource_manager_data_buffer_node_result(pDataBufferNode) == MA_BUSY) { - result = ma_resource_manager_process_next_job(pResourceManager); - if (result == MA_NO_DATA_AVAILABLE || result == MA_CANCELLED) { - result = MA_SUCCESS; - break; - } - } - } else { - /* Threading is enabled. The job queue will deal with the rest of the cleanup from here. */ - } - } else { - /* The sound isn't loading so we can just free the node here. */ - ma_resource_manager_data_buffer_node_free(pResourceManager, pDataBufferNode); - } - } - - return result; -} - - - -static ma_uint32 ma_resource_manager_data_buffer_next_execution_order(ma_resource_manager_data_buffer* pDataBuffer) -{ - MA_ASSERT(pDataBuffer != NULL); - return ma_atomic_fetch_add_32(&pDataBuffer->executionCounter, 1); -} - -static ma_result ma_resource_manager_data_buffer_cb__read_pcm_frames(ma_data_source* pDataSource, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead) -{ - return ma_resource_manager_data_buffer_read_pcm_frames((ma_resource_manager_data_buffer*)pDataSource, pFramesOut, frameCount, pFramesRead); -} - -static ma_result ma_resource_manager_data_buffer_cb__seek_to_pcm_frame(ma_data_source* pDataSource, ma_uint64 frameIndex) -{ - return ma_resource_manager_data_buffer_seek_to_pcm_frame((ma_resource_manager_data_buffer*)pDataSource, frameIndex); -} - -static ma_result ma_resource_manager_data_buffer_cb__get_data_format(ma_data_source* pDataSource, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate, ma_channel* pChannelMap, size_t channelMapCap) -{ - return ma_resource_manager_data_buffer_get_data_format((ma_resource_manager_data_buffer*)pDataSource, pFormat, pChannels, pSampleRate, pChannelMap, channelMapCap); -} - -static ma_result ma_resource_manager_data_buffer_cb__get_cursor_in_pcm_frames(ma_data_source* pDataSource, ma_uint64* pCursor) -{ - return ma_resource_manager_data_buffer_get_cursor_in_pcm_frames((ma_resource_manager_data_buffer*)pDataSource, pCursor); -} - -static ma_result ma_resource_manager_data_buffer_cb__get_length_in_pcm_frames(ma_data_source* pDataSource, ma_uint64* pLength) -{ - return ma_resource_manager_data_buffer_get_length_in_pcm_frames((ma_resource_manager_data_buffer*)pDataSource, pLength); -} - -static ma_result ma_resource_manager_data_buffer_cb__set_looping(ma_data_source* pDataSource, ma_bool32 isLooping) -{ - ma_resource_manager_data_buffer* pDataBuffer = (ma_resource_manager_data_buffer*)pDataSource; - MA_ASSERT(pDataBuffer != NULL); - - ma_atomic_exchange_32(&pDataBuffer->isLooping, isLooping); - - /* The looping state needs to be set on the connector as well or else looping won't work when we read audio data. */ - ma_data_source_set_looping(ma_resource_manager_data_buffer_get_connector(pDataBuffer), isLooping); - - return MA_SUCCESS; -} - -static ma_data_source_vtable g_ma_resource_manager_data_buffer_vtable = -{ - ma_resource_manager_data_buffer_cb__read_pcm_frames, - ma_resource_manager_data_buffer_cb__seek_to_pcm_frame, - ma_resource_manager_data_buffer_cb__get_data_format, - ma_resource_manager_data_buffer_cb__get_cursor_in_pcm_frames, - ma_resource_manager_data_buffer_cb__get_length_in_pcm_frames, - ma_resource_manager_data_buffer_cb__set_looping, - 0 -}; - -static ma_result ma_resource_manager_data_buffer_init_ex_internal(ma_resource_manager* pResourceManager, const ma_resource_manager_data_source_config* pConfig, ma_uint32 hashedName32, ma_resource_manager_data_buffer* pDataBuffer) -{ - ma_result result = MA_SUCCESS; - ma_resource_manager_data_buffer_node* pDataBufferNode; - ma_data_source_config dataSourceConfig; - ma_bool32 async; - ma_uint32 flags; - ma_resource_manager_pipeline_notifications notifications; - - if (pDataBuffer == NULL) { - if (pConfig != NULL && pConfig->pNotifications != NULL) { - ma_resource_manager_pipeline_notifications_signal_all_notifications(pConfig->pNotifications); - } - - return MA_INVALID_ARGS; - } - - MA_ZERO_OBJECT(pDataBuffer); - - if (pConfig == NULL) { - return MA_INVALID_ARGS; - } - - if (pConfig->pNotifications != NULL) { - notifications = *pConfig->pNotifications; /* From here on out we should be referencing `notifications` instead of `pNotifications`. Set this to NULL to catch errors at testing time. */ - } else { - MA_ZERO_OBJECT(¬ifications); - } - - /* For safety, always remove the ASYNC flag if threading is disabled on the resource manager. */ - flags = pConfig->flags; - if (ma_resource_manager_is_threading_enabled(pResourceManager) == MA_FALSE) { - flags &= ~MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_ASYNC; - } - - if (pConfig->isLooping) { - flags |= MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_LOOPING; - } - - async = (flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_ASYNC) != 0; - - /* - Fences need to be acquired before doing anything. These must be acquired and released outside of - the node to ensure there's no holes where ma_fence_wait() could prematurely return before the - data buffer has completed initialization. - - When loading asynchronously, the node acquisition routine below will acquire the fences on this - thread and then release them on the async thread when the operation is complete. - - These fences are always released at the "done" tag at the end of this function. They'll be - acquired a second if loading asynchronously. This double acquisition system is just done to - simplify code maintenance. - */ - ma_resource_manager_pipeline_notifications_acquire_all_fences(¬ifications); - { - /* We first need to acquire a node. If ASYNC is not set, this will not return until the entire sound has been loaded. */ - result = ma_resource_manager_data_buffer_node_acquire(pResourceManager, pConfig->pFilePath, pConfig->pFilePathW, hashedName32, flags, NULL, notifications.init.pFence, notifications.done.pFence, &pDataBufferNode); - if (result != MA_SUCCESS) { - ma_resource_manager_pipeline_notifications_signal_all_notifications(¬ifications); - goto done; - } - - dataSourceConfig = ma_data_source_config_init(); - dataSourceConfig.vtable = &g_ma_resource_manager_data_buffer_vtable; - - result = ma_data_source_init(&dataSourceConfig, &pDataBuffer->ds); - if (result != MA_SUCCESS) { - ma_resource_manager_data_buffer_node_unacquire(pResourceManager, pDataBufferNode, NULL, NULL); - ma_resource_manager_pipeline_notifications_signal_all_notifications(¬ifications); - goto done; - } - - pDataBuffer->pResourceManager = pResourceManager; - pDataBuffer->pNode = pDataBufferNode; - pDataBuffer->flags = flags; - pDataBuffer->result = MA_BUSY; /* Always default to MA_BUSY for safety. It'll be overwritten when loading completes or an error occurs. */ - - /* If we're loading asynchronously we need to post a job to the job queue to initialize the connector. */ - if (async == MA_FALSE || ma_resource_manager_data_buffer_node_result(pDataBufferNode) == MA_SUCCESS) { - /* Loading synchronously or the data has already been fully loaded. We can just initialize the connector from here without a job. */ - result = ma_resource_manager_data_buffer_init_connector(pDataBuffer, pConfig, NULL, NULL); - ma_atomic_exchange_i32(&pDataBuffer->result, result); - - ma_resource_manager_pipeline_notifications_signal_all_notifications(¬ifications); - goto done; - } else { - /* The node's data supply isn't initialized yet. The caller has requested that we load asynchronously so we need to post a job to do this. */ - ma_job job; - ma_resource_manager_inline_notification initNotification; /* Used when the WAIT_INIT flag is set. */ - - if ((flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_WAIT_INIT) != 0) { - ma_resource_manager_inline_notification_init(pResourceManager, &initNotification); - } - - /* - The status of the data buffer needs to be set to MA_BUSY before posting the job so that the - worker thread is aware of its busy state. If the LOAD_DATA_BUFFER job sees a status other - than MA_BUSY, it'll assume an error and fall through to an early exit. - */ - ma_atomic_exchange_i32(&pDataBuffer->result, MA_BUSY); - - /* Acquire fences a second time. These will be released by the async thread. */ - ma_resource_manager_pipeline_notifications_acquire_all_fences(¬ifications); - - job = ma_job_init(MA_JOB_TYPE_RESOURCE_MANAGER_LOAD_DATA_BUFFER); - job.order = ma_resource_manager_data_buffer_next_execution_order(pDataBuffer); - job.data.resourceManager.loadDataBuffer.pDataBuffer = pDataBuffer; - job.data.resourceManager.loadDataBuffer.pInitNotification = ((flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_WAIT_INIT) != 0) ? &initNotification : notifications.init.pNotification; - job.data.resourceManager.loadDataBuffer.pDoneNotification = notifications.done.pNotification; - job.data.resourceManager.loadDataBuffer.pInitFence = notifications.init.pFence; - job.data.resourceManager.loadDataBuffer.pDoneFence = notifications.done.pFence; - job.data.resourceManager.loadDataBuffer.rangeBegInPCMFrames = pConfig->rangeBegInPCMFrames; - job.data.resourceManager.loadDataBuffer.rangeEndInPCMFrames = pConfig->rangeEndInPCMFrames; - job.data.resourceManager.loadDataBuffer.loopPointBegInPCMFrames = pConfig->loopPointBegInPCMFrames; - job.data.resourceManager.loadDataBuffer.loopPointEndInPCMFrames = pConfig->loopPointEndInPCMFrames; - job.data.resourceManager.loadDataBuffer.isLooping = (flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_LOOPING) != 0; - - /* If we need to wait for initialization to complete we can just process the job in place. */ - if ((flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_WAIT_INIT) != 0) { - result = ma_job_process(&job); - } else { - result = ma_resource_manager_post_job(pResourceManager, &job); - } - - if (result != MA_SUCCESS) { - /* We failed to post the job. Most likely there isn't enough room in the queue's buffer. */ - ma_log_postf(ma_resource_manager_get_log(pResourceManager), MA_LOG_LEVEL_ERROR, "Failed to post MA_JOB_TYPE_RESOURCE_MANAGER_LOAD_DATA_BUFFER job. %s.\n", ma_result_description(result)); - ma_atomic_exchange_i32(&pDataBuffer->result, result); - - /* Release the fences after the result has been set on the data buffer. */ - ma_resource_manager_pipeline_notifications_release_all_fences(¬ifications); - } else { - if ((flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_WAIT_INIT) != 0) { - ma_resource_manager_inline_notification_wait(&initNotification); - - if (notifications.init.pNotification != NULL) { - ma_async_notification_signal(notifications.init.pNotification); - } - - /* NOTE: Do not release the init fence here. It will have been done by the job. */ - - /* Make sure we return an error if initialization failed on the async thread. */ - result = ma_resource_manager_data_buffer_result(pDataBuffer); - if (result == MA_BUSY) { - result = MA_SUCCESS; - } - } - } - - if ((flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_WAIT_INIT) != 0) { - ma_resource_manager_inline_notification_uninit(&initNotification); - } - } - - if (result != MA_SUCCESS) { - ma_resource_manager_data_buffer_node_unacquire(pResourceManager, pDataBufferNode, NULL, NULL); - goto done; - } - } -done: - if (result == MA_SUCCESS) { - if (pConfig->initialSeekPointInPCMFrames > 0) { - ma_resource_manager_data_buffer_seek_to_pcm_frame(pDataBuffer, pConfig->initialSeekPointInPCMFrames); - } - } - - ma_resource_manager_pipeline_notifications_release_all_fences(¬ifications); - - return result; -} - -MA_API ma_result ma_resource_manager_data_buffer_init_ex(ma_resource_manager* pResourceManager, const ma_resource_manager_data_source_config* pConfig, ma_resource_manager_data_buffer* pDataBuffer) -{ - return ma_resource_manager_data_buffer_init_ex_internal(pResourceManager, pConfig, 0, pDataBuffer); -} - -MA_API ma_result ma_resource_manager_data_buffer_init(ma_resource_manager* pResourceManager, const char* pFilePath, ma_uint32 flags, const ma_resource_manager_pipeline_notifications* pNotifications, ma_resource_manager_data_buffer* pDataBuffer) -{ - ma_resource_manager_data_source_config config; - - config = ma_resource_manager_data_source_config_init(); - config.pFilePath = pFilePath; - config.flags = flags; - config.pNotifications = pNotifications; - - return ma_resource_manager_data_buffer_init_ex(pResourceManager, &config, pDataBuffer); -} - -MA_API ma_result ma_resource_manager_data_buffer_init_w(ma_resource_manager* pResourceManager, const wchar_t* pFilePath, ma_uint32 flags, const ma_resource_manager_pipeline_notifications* pNotifications, ma_resource_manager_data_buffer* pDataBuffer) -{ - ma_resource_manager_data_source_config config; - - config = ma_resource_manager_data_source_config_init(); - config.pFilePathW = pFilePath; - config.flags = flags; - config.pNotifications = pNotifications; - - return ma_resource_manager_data_buffer_init_ex(pResourceManager, &config, pDataBuffer); -} - -MA_API ma_result ma_resource_manager_data_buffer_init_copy(ma_resource_manager* pResourceManager, const ma_resource_manager_data_buffer* pExistingDataBuffer, ma_resource_manager_data_buffer* pDataBuffer) -{ - ma_resource_manager_data_source_config config; - - if (pExistingDataBuffer == NULL) { - return MA_INVALID_ARGS; - } - - MA_ASSERT(pExistingDataBuffer->pNode != NULL); /* <-- If you've triggered this, you've passed in an invalid existing data buffer. */ - - config = ma_resource_manager_data_source_config_init(); - config.flags = pExistingDataBuffer->flags; - - return ma_resource_manager_data_buffer_init_ex_internal(pResourceManager, &config, pExistingDataBuffer->pNode->hashedName32, pDataBuffer); -} - -static ma_result ma_resource_manager_data_buffer_uninit_internal(ma_resource_manager_data_buffer* pDataBuffer) -{ - MA_ASSERT(pDataBuffer != NULL); - - /* The connector should be uninitialized first. */ - ma_resource_manager_data_buffer_uninit_connector(pDataBuffer->pResourceManager, pDataBuffer); - - /* With the connector uninitialized we can unacquire the node. */ - ma_resource_manager_data_buffer_node_unacquire(pDataBuffer->pResourceManager, pDataBuffer->pNode, NULL, NULL); - - /* The base data source needs to be uninitialized as well. */ - ma_data_source_uninit(&pDataBuffer->ds); - - return MA_SUCCESS; -} - -MA_API ma_result ma_resource_manager_data_buffer_uninit(ma_resource_manager_data_buffer* pDataBuffer) -{ - ma_result result; - - if (pDataBuffer == NULL) { - return MA_INVALID_ARGS; - } - - if (ma_resource_manager_data_buffer_result(pDataBuffer) == MA_SUCCESS) { - /* The data buffer can be deleted synchronously. */ - return ma_resource_manager_data_buffer_uninit_internal(pDataBuffer); - } else { - /* - The data buffer needs to be deleted asynchronously because it's still loading. With the status set to MA_UNAVAILABLE, no more pages will - be loaded and the uninitialization should happen fairly quickly. Since the caller owns the data buffer, we need to wait for this event - to get processed before returning. - */ - ma_resource_manager_inline_notification notification; - ma_job job; - - /* - We need to mark the node as unavailable so we don't try reading from it anymore, but also to - let the loading thread know that it needs to abort it's loading procedure. - */ - ma_atomic_exchange_i32(&pDataBuffer->result, MA_UNAVAILABLE); - - result = ma_resource_manager_inline_notification_init(pDataBuffer->pResourceManager, ¬ification); - if (result != MA_SUCCESS) { - return result; /* Failed to create the notification. This should rarely, if ever, happen. */ - } - - job = ma_job_init(MA_JOB_TYPE_RESOURCE_MANAGER_FREE_DATA_BUFFER); - job.order = ma_resource_manager_data_buffer_next_execution_order(pDataBuffer); - job.data.resourceManager.freeDataBuffer.pDataBuffer = pDataBuffer; - job.data.resourceManager.freeDataBuffer.pDoneNotification = ¬ification; - job.data.resourceManager.freeDataBuffer.pDoneFence = NULL; - - result = ma_resource_manager_post_job(pDataBuffer->pResourceManager, &job); - if (result != MA_SUCCESS) { - ma_resource_manager_inline_notification_uninit(¬ification); - return result; - } - - ma_resource_manager_inline_notification_wait_and_uninit(¬ification); - } - - return result; -} - -MA_API ma_result ma_resource_manager_data_buffer_read_pcm_frames(ma_resource_manager_data_buffer* pDataBuffer, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead) -{ - ma_result result = MA_SUCCESS; - ma_uint64 framesRead = 0; - ma_bool32 isDecodedBufferBusy = MA_FALSE; - - /* Safety. */ - if (pFramesRead != NULL) { - *pFramesRead = 0; - } - - if (frameCount == 0) { - return MA_INVALID_ARGS; - } - - /* - We cannot be using the data buffer after it's been uninitialized. If you trigger this assert it means you're trying to read from the data buffer after - it's been uninitialized or is in the process of uninitializing. - */ - MA_ASSERT(ma_resource_manager_data_buffer_node_result(pDataBuffer->pNode) != MA_UNAVAILABLE); - - /* If the node is not initialized we need to abort with a busy code. */ - if (ma_resource_manager_data_buffer_has_connector(pDataBuffer) == MA_FALSE) { - return MA_BUSY; /* Still loading. */ - } - - /* - If we've got a seek scheduled we'll want to do that before reading. However, for paged buffers, there's - a chance that the sound hasn't yet been decoded up to the seek point will result in the seek failing. If - this happens, we need to keep the seek scheduled and return MA_BUSY. - */ - if (pDataBuffer->seekToCursorOnNextRead) { - pDataBuffer->seekToCursorOnNextRead = MA_FALSE; - - result = ma_data_source_seek_to_pcm_frame(ma_resource_manager_data_buffer_get_connector(pDataBuffer), pDataBuffer->seekTargetInPCMFrames); - if (result != MA_SUCCESS) { - if (result == MA_BAD_SEEK && ma_resource_manager_data_buffer_node_get_data_supply_type(pDataBuffer->pNode) == ma_resource_manager_data_supply_type_decoded_paged) { - pDataBuffer->seekToCursorOnNextRead = MA_TRUE; /* Keep the seek scheduled. We just haven't loaded enough data yet to do the seek properly. */ - return MA_BUSY; - } - - return result; - } - } - - /* - For decoded buffers (not paged) we need to check beforehand how many frames we have available. We cannot - exceed this amount. We'll read as much as we can, and then return MA_BUSY. - */ - if (ma_resource_manager_data_buffer_node_get_data_supply_type(pDataBuffer->pNode) == ma_resource_manager_data_supply_type_decoded) { - ma_uint64 availableFrames; - - isDecodedBufferBusy = (ma_resource_manager_data_buffer_node_result(pDataBuffer->pNode) == MA_BUSY); - - if (ma_resource_manager_data_buffer_get_available_frames(pDataBuffer, &availableFrames) == MA_SUCCESS) { - /* Don't try reading more than the available frame count if the data buffer node is still loading. */ - if (isDecodedBufferBusy) { - if (frameCount > availableFrames) { - frameCount = availableFrames; - - /* - If there's no frames available we want to set the status to MA_AT_END. The logic below - will check if the node is busy, and if so, change it to MA_BUSY. The reason we do this - is because we don't want to call `ma_data_source_read_pcm_frames()` if the frame count - is 0 because that'll result in a situation where it's possible MA_AT_END won't get - returned. - */ - if (frameCount == 0) { - result = MA_AT_END; - } - } else { - isDecodedBufferBusy = MA_FALSE; /* We have enough frames available in the buffer to avoid a MA_BUSY status. */ - } - } else { - /* - Getting here means the buffer has been fully loaded. We can just pass the frame count straight - into ma_data_source_read_pcm_frames() below and let ma_data_source handle it. - */ - } - } - } - - /* Don't attempt to read anything if we've got no frames available. */ - if (frameCount > 0) { - result = ma_data_source_read_pcm_frames(ma_resource_manager_data_buffer_get_connector(pDataBuffer), pFramesOut, frameCount, &framesRead); - } - - /* - If we returned MA_AT_END, but the node is still loading, we don't want to return that code or else the caller will interpret the sound - as at the end and terminate decoding. - */ - if (result == MA_AT_END) { - if (ma_resource_manager_data_buffer_node_result(pDataBuffer->pNode) == MA_BUSY) { - result = MA_BUSY; - } - } - - if (isDecodedBufferBusy) { - result = MA_BUSY; - } - - if (pFramesRead != NULL) { - *pFramesRead = framesRead; - } - - if (result == MA_SUCCESS && framesRead == 0) { - result = MA_AT_END; - } - - return result; -} - -MA_API ma_result ma_resource_manager_data_buffer_seek_to_pcm_frame(ma_resource_manager_data_buffer* pDataBuffer, ma_uint64 frameIndex) -{ - ma_result result; - - /* We cannot be using the data source after it's been uninitialized. */ - MA_ASSERT(ma_resource_manager_data_buffer_node_result(pDataBuffer->pNode) != MA_UNAVAILABLE); - - /* If we haven't yet got a connector we need to abort. */ - if (ma_resource_manager_data_buffer_has_connector(pDataBuffer) == MA_FALSE) { - pDataBuffer->seekTargetInPCMFrames = frameIndex; - pDataBuffer->seekToCursorOnNextRead = MA_TRUE; - return MA_BUSY; /* Still loading. */ - } - - result = ma_data_source_seek_to_pcm_frame(ma_resource_manager_data_buffer_get_connector(pDataBuffer), frameIndex); - if (result != MA_SUCCESS) { - return result; - } - - pDataBuffer->seekTargetInPCMFrames = ~(ma_uint64)0; /* <-- For identification purposes. */ - pDataBuffer->seekToCursorOnNextRead = MA_FALSE; - - return MA_SUCCESS; -} - -MA_API ma_result ma_resource_manager_data_buffer_get_data_format(ma_resource_manager_data_buffer* pDataBuffer, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate, ma_channel* pChannelMap, size_t channelMapCap) -{ - /* We cannot be using the data source after it's been uninitialized. */ - MA_ASSERT(ma_resource_manager_data_buffer_node_result(pDataBuffer->pNode) != MA_UNAVAILABLE); - - switch (ma_resource_manager_data_buffer_node_get_data_supply_type(pDataBuffer->pNode)) - { - case ma_resource_manager_data_supply_type_encoded: - { - return ma_data_source_get_data_format(&pDataBuffer->connector.decoder, pFormat, pChannels, pSampleRate, pChannelMap, channelMapCap); - }; - - case ma_resource_manager_data_supply_type_decoded: - { - *pFormat = pDataBuffer->pNode->data.backend.decoded.format; - *pChannels = pDataBuffer->pNode->data.backend.decoded.channels; - *pSampleRate = pDataBuffer->pNode->data.backend.decoded.sampleRate; - ma_channel_map_init_standard(ma_standard_channel_map_default, pChannelMap, channelMapCap, pDataBuffer->pNode->data.backend.decoded.channels); - return MA_SUCCESS; - }; - - case ma_resource_manager_data_supply_type_decoded_paged: - { - *pFormat = pDataBuffer->pNode->data.backend.decodedPaged.data.format; - *pChannels = pDataBuffer->pNode->data.backend.decodedPaged.data.channels; - *pSampleRate = pDataBuffer->pNode->data.backend.decodedPaged.sampleRate; - ma_channel_map_init_standard(ma_standard_channel_map_default, pChannelMap, channelMapCap, pDataBuffer->pNode->data.backend.decoded.channels); - return MA_SUCCESS; - }; - - case ma_resource_manager_data_supply_type_unknown: - { - return MA_BUSY; /* Still loading. */ - }; - - default: - { - /* Unknown supply type. Should never hit this. */ - return MA_INVALID_ARGS; - } - } -} - -MA_API ma_result ma_resource_manager_data_buffer_get_cursor_in_pcm_frames(ma_resource_manager_data_buffer* pDataBuffer, ma_uint64* pCursor) -{ - /* We cannot be using the data source after it's been uninitialized. */ - MA_ASSERT(ma_resource_manager_data_buffer_node_result(pDataBuffer->pNode) != MA_UNAVAILABLE); - - if (pDataBuffer == NULL || pCursor == NULL) { - return MA_INVALID_ARGS; - } - - *pCursor = 0; - - switch (ma_resource_manager_data_buffer_node_get_data_supply_type(pDataBuffer->pNode)) - { - case ma_resource_manager_data_supply_type_encoded: - { - return ma_decoder_get_cursor_in_pcm_frames(&pDataBuffer->connector.decoder, pCursor); - }; - - case ma_resource_manager_data_supply_type_decoded: - { - return ma_audio_buffer_get_cursor_in_pcm_frames(&pDataBuffer->connector.buffer, pCursor); - }; - - case ma_resource_manager_data_supply_type_decoded_paged: - { - return ma_paged_audio_buffer_get_cursor_in_pcm_frames(&pDataBuffer->connector.pagedBuffer, pCursor); - }; - - case ma_resource_manager_data_supply_type_unknown: - { - return MA_BUSY; - }; - - default: - { - return MA_INVALID_ARGS; - } - } -} - -MA_API ma_result ma_resource_manager_data_buffer_get_length_in_pcm_frames(ma_resource_manager_data_buffer* pDataBuffer, ma_uint64* pLength) -{ - /* We cannot be using the data source after it's been uninitialized. */ - MA_ASSERT(ma_resource_manager_data_buffer_node_result(pDataBuffer->pNode) != MA_UNAVAILABLE); - - if (pDataBuffer == NULL || pLength == NULL) { - return MA_INVALID_ARGS; - } - - if (ma_resource_manager_data_buffer_node_get_data_supply_type(pDataBuffer->pNode) == ma_resource_manager_data_supply_type_unknown) { - return MA_BUSY; /* Still loading. */ - } - - return ma_data_source_get_length_in_pcm_frames(ma_resource_manager_data_buffer_get_connector(pDataBuffer), pLength); -} - -MA_API ma_result ma_resource_manager_data_buffer_result(const ma_resource_manager_data_buffer* pDataBuffer) -{ - if (pDataBuffer == NULL) { - return MA_INVALID_ARGS; - } - - return (ma_result)ma_atomic_load_i32((ma_result*)&pDataBuffer->result); /* Need a naughty const-cast here. */ -} - -MA_API ma_result ma_resource_manager_data_buffer_set_looping(ma_resource_manager_data_buffer* pDataBuffer, ma_bool32 isLooping) -{ - return ma_data_source_set_looping(pDataBuffer, isLooping); -} - -MA_API ma_bool32 ma_resource_manager_data_buffer_is_looping(const ma_resource_manager_data_buffer* pDataBuffer) -{ - return ma_data_source_is_looping(pDataBuffer); -} - -MA_API ma_result ma_resource_manager_data_buffer_get_available_frames(ma_resource_manager_data_buffer* pDataBuffer, ma_uint64* pAvailableFrames) -{ - if (pAvailableFrames == NULL) { - return MA_INVALID_ARGS; - } - - *pAvailableFrames = 0; - - if (pDataBuffer == NULL) { - return MA_INVALID_ARGS; - } - - if (ma_resource_manager_data_buffer_node_get_data_supply_type(pDataBuffer->pNode) == ma_resource_manager_data_supply_type_unknown) { - if (ma_resource_manager_data_buffer_node_result(pDataBuffer->pNode) == MA_BUSY) { - return MA_BUSY; - } else { - return MA_INVALID_OPERATION; /* No connector. */ - } - } - - switch (ma_resource_manager_data_buffer_node_get_data_supply_type(pDataBuffer->pNode)) - { - case ma_resource_manager_data_supply_type_encoded: - { - return ma_decoder_get_available_frames(&pDataBuffer->connector.decoder, pAvailableFrames); - }; - - case ma_resource_manager_data_supply_type_decoded: - { - return ma_audio_buffer_get_available_frames(&pDataBuffer->connector.buffer, pAvailableFrames); - }; - - case ma_resource_manager_data_supply_type_decoded_paged: - { - ma_uint64 cursor; - ma_paged_audio_buffer_get_cursor_in_pcm_frames(&pDataBuffer->connector.pagedBuffer, &cursor); - - if (pDataBuffer->pNode->data.backend.decodedPaged.decodedFrameCount > cursor) { - *pAvailableFrames = pDataBuffer->pNode->data.backend.decodedPaged.decodedFrameCount - cursor; - } else { - *pAvailableFrames = 0; - } - - return MA_SUCCESS; - }; - - case ma_resource_manager_data_supply_type_unknown: - default: - { - /* Unknown supply type. Should never hit this. */ - return MA_INVALID_ARGS; - } - } -} - -MA_API ma_result ma_resource_manager_register_file(ma_resource_manager* pResourceManager, const char* pFilePath, ma_uint32 flags) -{ - return ma_resource_manager_data_buffer_node_acquire(pResourceManager, pFilePath, NULL, 0, flags, NULL, NULL, NULL, NULL); -} - -MA_API ma_result ma_resource_manager_register_file_w(ma_resource_manager* pResourceManager, const wchar_t* pFilePath, ma_uint32 flags) -{ - return ma_resource_manager_data_buffer_node_acquire(pResourceManager, NULL, pFilePath, 0, flags, NULL, NULL, NULL, NULL); -} - - -static ma_result ma_resource_manager_register_data(ma_resource_manager* pResourceManager, const char* pName, const wchar_t* pNameW, ma_resource_manager_data_supply* pExistingData) -{ - return ma_resource_manager_data_buffer_node_acquire(pResourceManager, pName, pNameW, 0, 0, pExistingData, NULL, NULL, NULL); -} - -static ma_result ma_resource_manager_register_decoded_data_internal(ma_resource_manager* pResourceManager, const char* pName, const wchar_t* pNameW, const void* pData, ma_uint64 frameCount, ma_format format, ma_uint32 channels, ma_uint32 sampleRate) -{ - ma_resource_manager_data_supply data; - data.type = ma_resource_manager_data_supply_type_decoded; - data.backend.decoded.pData = pData; - data.backend.decoded.totalFrameCount = frameCount; - data.backend.decoded.format = format; - data.backend.decoded.channels = channels; - data.backend.decoded.sampleRate = sampleRate; - - return ma_resource_manager_register_data(pResourceManager, pName, pNameW, &data); -} - -MA_API ma_result ma_resource_manager_register_decoded_data(ma_resource_manager* pResourceManager, const char* pName, const void* pData, ma_uint64 frameCount, ma_format format, ma_uint32 channels, ma_uint32 sampleRate) -{ - return ma_resource_manager_register_decoded_data_internal(pResourceManager, pName, NULL, pData, frameCount, format, channels, sampleRate); -} - -MA_API ma_result ma_resource_manager_register_decoded_data_w(ma_resource_manager* pResourceManager, const wchar_t* pName, const void* pData, ma_uint64 frameCount, ma_format format, ma_uint32 channels, ma_uint32 sampleRate) -{ - return ma_resource_manager_register_decoded_data_internal(pResourceManager, NULL, pName, pData, frameCount, format, channels, sampleRate); -} - - -static ma_result ma_resource_manager_register_encoded_data_internal(ma_resource_manager* pResourceManager, const char* pName, const wchar_t* pNameW, const void* pData, size_t sizeInBytes) -{ - ma_resource_manager_data_supply data; - data.type = ma_resource_manager_data_supply_type_encoded; - data.backend.encoded.pData = pData; - data.backend.encoded.sizeInBytes = sizeInBytes; - - return ma_resource_manager_register_data(pResourceManager, pName, pNameW, &data); -} - -MA_API ma_result ma_resource_manager_register_encoded_data(ma_resource_manager* pResourceManager, const char* pName, const void* pData, size_t sizeInBytes) -{ - return ma_resource_manager_register_encoded_data_internal(pResourceManager, pName, NULL, pData, sizeInBytes); -} - -MA_API ma_result ma_resource_manager_register_encoded_data_w(ma_resource_manager* pResourceManager, const wchar_t* pName, const void* pData, size_t sizeInBytes) -{ - return ma_resource_manager_register_encoded_data_internal(pResourceManager, NULL, pName, pData, sizeInBytes); -} - - -MA_API ma_result ma_resource_manager_unregister_file(ma_resource_manager* pResourceManager, const char* pFilePath) -{ - return ma_resource_manager_unregister_data(pResourceManager, pFilePath); -} - -MA_API ma_result ma_resource_manager_unregister_file_w(ma_resource_manager* pResourceManager, const wchar_t* pFilePath) -{ - return ma_resource_manager_unregister_data_w(pResourceManager, pFilePath); -} - -MA_API ma_result ma_resource_manager_unregister_data(ma_resource_manager* pResourceManager, const char* pName) -{ - return ma_resource_manager_data_buffer_node_unacquire(pResourceManager, NULL, pName, NULL); -} - -MA_API ma_result ma_resource_manager_unregister_data_w(ma_resource_manager* pResourceManager, const wchar_t* pName) -{ - return ma_resource_manager_data_buffer_node_unacquire(pResourceManager, NULL, NULL, pName); -} - - -static ma_uint32 ma_resource_manager_data_stream_next_execution_order(ma_resource_manager_data_stream* pDataStream) -{ - MA_ASSERT(pDataStream != NULL); - return ma_atomic_fetch_add_32(&pDataStream->executionCounter, 1); -} - -static ma_bool32 ma_resource_manager_data_stream_is_decoder_at_end(const ma_resource_manager_data_stream* pDataStream) -{ - MA_ASSERT(pDataStream != NULL); - return ma_atomic_load_32((ma_bool32*)&pDataStream->isDecoderAtEnd); -} - -static ma_uint32 ma_resource_manager_data_stream_seek_counter(const ma_resource_manager_data_stream* pDataStream) -{ - MA_ASSERT(pDataStream != NULL); - return ma_atomic_load_32((ma_uint32*)&pDataStream->seekCounter); -} - - -static ma_result ma_resource_manager_data_stream_cb__read_pcm_frames(ma_data_source* pDataSource, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead) -{ - return ma_resource_manager_data_stream_read_pcm_frames((ma_resource_manager_data_stream*)pDataSource, pFramesOut, frameCount, pFramesRead); -} - -static ma_result ma_resource_manager_data_stream_cb__seek_to_pcm_frame(ma_data_source* pDataSource, ma_uint64 frameIndex) -{ - return ma_resource_manager_data_stream_seek_to_pcm_frame((ma_resource_manager_data_stream*)pDataSource, frameIndex); -} - -static ma_result ma_resource_manager_data_stream_cb__get_data_format(ma_data_source* pDataSource, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate, ma_channel* pChannelMap, size_t channelMapCap) -{ - return ma_resource_manager_data_stream_get_data_format((ma_resource_manager_data_stream*)pDataSource, pFormat, pChannels, pSampleRate, pChannelMap, channelMapCap); -} - -static ma_result ma_resource_manager_data_stream_cb__get_cursor_in_pcm_frames(ma_data_source* pDataSource, ma_uint64* pCursor) -{ - return ma_resource_manager_data_stream_get_cursor_in_pcm_frames((ma_resource_manager_data_stream*)pDataSource, pCursor); -} - -static ma_result ma_resource_manager_data_stream_cb__get_length_in_pcm_frames(ma_data_source* pDataSource, ma_uint64* pLength) -{ - return ma_resource_manager_data_stream_get_length_in_pcm_frames((ma_resource_manager_data_stream*)pDataSource, pLength); -} - -static ma_result ma_resource_manager_data_stream_cb__set_looping(ma_data_source* pDataSource, ma_bool32 isLooping) -{ - ma_resource_manager_data_stream* pDataStream = (ma_resource_manager_data_stream*)pDataSource; - MA_ASSERT(pDataStream != NULL); - - ma_atomic_exchange_32(&pDataStream->isLooping, isLooping); - - return MA_SUCCESS; -} - -static ma_data_source_vtable g_ma_resource_manager_data_stream_vtable = -{ - ma_resource_manager_data_stream_cb__read_pcm_frames, - ma_resource_manager_data_stream_cb__seek_to_pcm_frame, - ma_resource_manager_data_stream_cb__get_data_format, - ma_resource_manager_data_stream_cb__get_cursor_in_pcm_frames, - ma_resource_manager_data_stream_cb__get_length_in_pcm_frames, - ma_resource_manager_data_stream_cb__set_looping, - 0 /*MA_DATA_SOURCE_SELF_MANAGED_RANGE_AND_LOOP_POINT*/ -}; - -static void ma_resource_manager_data_stream_set_absolute_cursor(ma_resource_manager_data_stream* pDataStream, ma_uint64 absoluteCursor) -{ - /* Loop if possible. */ - if (absoluteCursor > pDataStream->totalLengthInPCMFrames && pDataStream->totalLengthInPCMFrames > 0) { - absoluteCursor = absoluteCursor % pDataStream->totalLengthInPCMFrames; - } - - ma_atomic_exchange_64(&pDataStream->absoluteCursor, absoluteCursor); -} - -MA_API ma_result ma_resource_manager_data_stream_init_ex(ma_resource_manager* pResourceManager, const ma_resource_manager_data_source_config* pConfig, ma_resource_manager_data_stream* pDataStream) -{ - ma_result result; - ma_data_source_config dataSourceConfig; - char* pFilePathCopy = NULL; - wchar_t* pFilePathWCopy = NULL; - ma_job job; - ma_bool32 waitBeforeReturning = MA_FALSE; - ma_resource_manager_inline_notification waitNotification; - ma_resource_manager_pipeline_notifications notifications; - ma_uint32 flags; - - if (pDataStream == NULL) { - if (pConfig != NULL && pConfig->pNotifications != NULL) { - ma_resource_manager_pipeline_notifications_signal_all_notifications(pConfig->pNotifications); - } - - return MA_INVALID_ARGS; - } - - MA_ZERO_OBJECT(pDataStream); - - if (pConfig == NULL) { - return MA_INVALID_ARGS; - } - - if (pConfig->pNotifications != NULL) { - notifications = *pConfig->pNotifications; /* From here on out, `notifications` should be used instead of `pNotifications`. Setting this to NULL to catch any errors at testing time. */ - } else { - MA_ZERO_OBJECT(¬ifications); - } - - dataSourceConfig = ma_data_source_config_init(); - dataSourceConfig.vtable = &g_ma_resource_manager_data_stream_vtable; - - result = ma_data_source_init(&dataSourceConfig, &pDataStream->ds); - if (result != MA_SUCCESS) { - ma_resource_manager_pipeline_notifications_signal_all_notifications(¬ifications); - return result; - } - - flags = pConfig->flags; - if (pConfig->isLooping) { - flags |= MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_LOOPING; - } - - pDataStream->pResourceManager = pResourceManager; - pDataStream->flags = pConfig->flags; - pDataStream->result = MA_BUSY; - - ma_data_source_set_range_in_pcm_frames(pDataStream, pConfig->rangeBegInPCMFrames, pConfig->rangeEndInPCMFrames); - ma_data_source_set_loop_point_in_pcm_frames(pDataStream, pConfig->loopPointBegInPCMFrames, pConfig->loopPointEndInPCMFrames); - ma_data_source_set_looping(pDataStream, (flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_LOOPING) != 0); - - if (pResourceManager == NULL || (pConfig->pFilePath == NULL && pConfig->pFilePathW == NULL)) { - ma_resource_manager_pipeline_notifications_signal_all_notifications(¬ifications); - return MA_INVALID_ARGS; - } - - /* We want all access to the VFS and the internal decoder to happen on the job thread just to keep things easier to manage for the VFS. */ - - /* We need a copy of the file path. We should probably make this more efficient, but for now we'll do a transient memory allocation. */ - if (pConfig->pFilePath != NULL) { - pFilePathCopy = ma_copy_string(pConfig->pFilePath, &pResourceManager->config.allocationCallbacks); - } else { - pFilePathWCopy = ma_copy_string_w(pConfig->pFilePathW, &pResourceManager->config.allocationCallbacks); - } - - if (pFilePathCopy == NULL && pFilePathWCopy == NULL) { - ma_resource_manager_pipeline_notifications_signal_all_notifications(¬ifications); - return MA_OUT_OF_MEMORY; - } - - /* - We need to check for the presence of MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_ASYNC. If it's not set, we need to wait before returning. Otherwise we - can return immediately. Likewise, we'll also check for MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_WAIT_INIT and do the same. - */ - if ((pConfig->flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_ASYNC) == 0 || (pConfig->flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_WAIT_INIT) != 0) { - waitBeforeReturning = MA_TRUE; - ma_resource_manager_inline_notification_init(pResourceManager, &waitNotification); - } - - ma_resource_manager_pipeline_notifications_acquire_all_fences(¬ifications); - - /* Set the absolute cursor to our initial seek position so retrieval of the cursor returns a good value. */ - ma_resource_manager_data_stream_set_absolute_cursor(pDataStream, pConfig->initialSeekPointInPCMFrames); - - /* We now have everything we need to post the job. This is the last thing we need to do from here. The rest will be done by the job thread. */ - job = ma_job_init(MA_JOB_TYPE_RESOURCE_MANAGER_LOAD_DATA_STREAM); - job.order = ma_resource_manager_data_stream_next_execution_order(pDataStream); - job.data.resourceManager.loadDataStream.pDataStream = pDataStream; - job.data.resourceManager.loadDataStream.pFilePath = pFilePathCopy; - job.data.resourceManager.loadDataStream.pFilePathW = pFilePathWCopy; - job.data.resourceManager.loadDataStream.initialSeekPoint = pConfig->initialSeekPointInPCMFrames; - job.data.resourceManager.loadDataStream.pInitNotification = (waitBeforeReturning == MA_TRUE) ? &waitNotification : notifications.init.pNotification; - job.data.resourceManager.loadDataStream.pInitFence = notifications.init.pFence; - result = ma_resource_manager_post_job(pResourceManager, &job); - if (result != MA_SUCCESS) { - ma_resource_manager_pipeline_notifications_signal_all_notifications(¬ifications); - ma_resource_manager_pipeline_notifications_release_all_fences(¬ifications); - - if (waitBeforeReturning) { - ma_resource_manager_inline_notification_uninit(&waitNotification); - } - - ma_free(pFilePathCopy, &pResourceManager->config.allocationCallbacks); - ma_free(pFilePathWCopy, &pResourceManager->config.allocationCallbacks); - return result; - } - - /* Wait if needed. */ - if (waitBeforeReturning) { - ma_resource_manager_inline_notification_wait_and_uninit(&waitNotification); - - if (notifications.init.pNotification != NULL) { - ma_async_notification_signal(notifications.init.pNotification); - } - - /* - If there was an error during initialization make sure we return that result here. We don't want to do this - if we're not waiting because it will most likely be in a busy state. - */ - if (pDataStream->result != MA_SUCCESS) { - return pDataStream->result; - } - - /* NOTE: Do not release pInitFence here. That will be done by the job. */ - } - - return MA_SUCCESS; -} - -MA_API ma_result ma_resource_manager_data_stream_init(ma_resource_manager* pResourceManager, const char* pFilePath, ma_uint32 flags, const ma_resource_manager_pipeline_notifications* pNotifications, ma_resource_manager_data_stream* pDataStream) -{ - ma_resource_manager_data_source_config config; - - config = ma_resource_manager_data_source_config_init(); - config.pFilePath = pFilePath; - config.flags = flags; - config.pNotifications = pNotifications; - - return ma_resource_manager_data_stream_init_ex(pResourceManager, &config, pDataStream); -} - -MA_API ma_result ma_resource_manager_data_stream_init_w(ma_resource_manager* pResourceManager, const wchar_t* pFilePath, ma_uint32 flags, const ma_resource_manager_pipeline_notifications* pNotifications, ma_resource_manager_data_stream* pDataStream) -{ - ma_resource_manager_data_source_config config; - - config = ma_resource_manager_data_source_config_init(); - config.pFilePathW = pFilePath; - config.flags = flags; - config.pNotifications = pNotifications; - - return ma_resource_manager_data_stream_init_ex(pResourceManager, &config, pDataStream); -} - -MA_API ma_result ma_resource_manager_data_stream_uninit(ma_resource_manager_data_stream* pDataStream) -{ - ma_resource_manager_inline_notification freeEvent; - ma_job job; - - if (pDataStream == NULL) { - return MA_INVALID_ARGS; - } - - /* The first thing to do is set the result to unavailable. This will prevent future page decoding. */ - ma_atomic_exchange_i32(&pDataStream->result, MA_UNAVAILABLE); - - /* - We need to post a job to ensure we're not in the middle or decoding or anything. Because the object is owned by the caller, we'll need - to wait for it to complete before returning which means we need an event. - */ - ma_resource_manager_inline_notification_init(pDataStream->pResourceManager, &freeEvent); - - job = ma_job_init(MA_JOB_TYPE_RESOURCE_MANAGER_FREE_DATA_STREAM); - job.order = ma_resource_manager_data_stream_next_execution_order(pDataStream); - job.data.resourceManager.freeDataStream.pDataStream = pDataStream; - job.data.resourceManager.freeDataStream.pDoneNotification = &freeEvent; - job.data.resourceManager.freeDataStream.pDoneFence = NULL; - ma_resource_manager_post_job(pDataStream->pResourceManager, &job); - - /* We need to wait for the job to finish processing before we return. */ - ma_resource_manager_inline_notification_wait_and_uninit(&freeEvent); - - return MA_SUCCESS; -} - - -static ma_uint32 ma_resource_manager_data_stream_get_page_size_in_frames(ma_resource_manager_data_stream* pDataStream) -{ - MA_ASSERT(pDataStream != NULL); - MA_ASSERT(pDataStream->isDecoderInitialized == MA_TRUE); - - return MA_RESOURCE_MANAGER_PAGE_SIZE_IN_MILLISECONDS * (pDataStream->decoder.outputSampleRate/1000); -} - -static void* ma_resource_manager_data_stream_get_page_data_pointer(ma_resource_manager_data_stream* pDataStream, ma_uint32 pageIndex, ma_uint32 relativeCursor) -{ - MA_ASSERT(pDataStream != NULL); - MA_ASSERT(pDataStream->isDecoderInitialized == MA_TRUE); - MA_ASSERT(pageIndex == 0 || pageIndex == 1); - - return ma_offset_ptr(pDataStream->pPageData, ((ma_resource_manager_data_stream_get_page_size_in_frames(pDataStream) * pageIndex) + relativeCursor) * ma_get_bytes_per_frame(pDataStream->decoder.outputFormat, pDataStream->decoder.outputChannels)); -} - -static void ma_resource_manager_data_stream_fill_page(ma_resource_manager_data_stream* pDataStream, ma_uint32 pageIndex) -{ - ma_result result = MA_SUCCESS; - ma_uint64 pageSizeInFrames; - ma_uint64 totalFramesReadForThisPage = 0; - void* pPageData = ma_resource_manager_data_stream_get_page_data_pointer(pDataStream, pageIndex, 0); - - pageSizeInFrames = ma_resource_manager_data_stream_get_page_size_in_frames(pDataStream); - - /* The decoder needs to inherit the stream's looping and range state. */ - { - ma_uint64 rangeBeg; - ma_uint64 rangeEnd; - ma_uint64 loopPointBeg; - ma_uint64 loopPointEnd; - - ma_data_source_set_looping(&pDataStream->decoder, ma_resource_manager_data_stream_is_looping(pDataStream)); - - ma_data_source_get_range_in_pcm_frames(pDataStream, &rangeBeg, &rangeEnd); - ma_data_source_set_range_in_pcm_frames(&pDataStream->decoder, rangeBeg, rangeEnd); - - ma_data_source_get_loop_point_in_pcm_frames(pDataStream, &loopPointBeg, &loopPointEnd); - ma_data_source_set_loop_point_in_pcm_frames(&pDataStream->decoder, loopPointBeg, loopPointEnd); - } - - /* Just read straight from the decoder. It will deal with ranges and looping for us. */ - result = ma_data_source_read_pcm_frames(&pDataStream->decoder, pPageData, pageSizeInFrames, &totalFramesReadForThisPage); - if (result == MA_AT_END || totalFramesReadForThisPage < pageSizeInFrames) { - ma_atomic_exchange_32(&pDataStream->isDecoderAtEnd, MA_TRUE); - } - - ma_atomic_exchange_32(&pDataStream->pageFrameCount[pageIndex], (ma_uint32)totalFramesReadForThisPage); - ma_atomic_exchange_32(&pDataStream->isPageValid[pageIndex], MA_TRUE); -} - -static void ma_resource_manager_data_stream_fill_pages(ma_resource_manager_data_stream* pDataStream) -{ - ma_uint32 iPage; - - MA_ASSERT(pDataStream != NULL); - - for (iPage = 0; iPage < 2; iPage += 1) { - ma_resource_manager_data_stream_fill_page(pDataStream, iPage); - } -} - - -static ma_result ma_resource_manager_data_stream_map(ma_resource_manager_data_stream* pDataStream, void** ppFramesOut, ma_uint64* pFrameCount) -{ - ma_uint64 framesAvailable; - ma_uint64 frameCount = 0; - - /* We cannot be using the data source after it's been uninitialized. */ - MA_ASSERT(ma_resource_manager_data_stream_result(pDataStream) != MA_UNAVAILABLE); - - if (pFrameCount != NULL) { - frameCount = *pFrameCount; - *pFrameCount = 0; - } - if (ppFramesOut != NULL) { - *ppFramesOut = NULL; - } - - if (pDataStream == NULL || ppFramesOut == NULL || pFrameCount == NULL) { - return MA_INVALID_ARGS; - } - - if (ma_resource_manager_data_stream_result(pDataStream) != MA_SUCCESS) { - return MA_INVALID_OPERATION; - } - - /* Don't attempt to read while we're in the middle of seeking. Tell the caller that we're busy. */ - if (ma_resource_manager_data_stream_seek_counter(pDataStream) > 0) { - return MA_BUSY; - } - - /* If the page we're on is invalid it means we've caught up to the job thread. */ - if (ma_atomic_load_32(&pDataStream->isPageValid[pDataStream->currentPageIndex]) == MA_FALSE) { - framesAvailable = 0; - } else { - /* - The page we're on is valid so we must have some frames available. We need to make sure that we don't overflow into the next page, even if it's valid. The reason is - that the unmap process will only post an update for one page at a time. Keeping mapping tied to page boundaries makes this simpler. - */ - ma_uint32 currentPageFrameCount = ma_atomic_load_32(&pDataStream->pageFrameCount[pDataStream->currentPageIndex]); - MA_ASSERT(currentPageFrameCount >= pDataStream->relativeCursor); - - framesAvailable = currentPageFrameCount - pDataStream->relativeCursor; - } - - /* If there's no frames available and the result is set to MA_AT_END we need to return MA_AT_END. */ - if (framesAvailable == 0) { - if (ma_resource_manager_data_stream_is_decoder_at_end(pDataStream)) { - return MA_AT_END; - } else { - return MA_BUSY; /* There are no frames available, but we're not marked as EOF so we might have caught up to the job thread. Need to return MA_BUSY and wait for more data. */ - } - } - - MA_ASSERT(framesAvailable > 0); - - if (frameCount > framesAvailable) { - frameCount = framesAvailable; - } - - *ppFramesOut = ma_resource_manager_data_stream_get_page_data_pointer(pDataStream, pDataStream->currentPageIndex, pDataStream->relativeCursor); - *pFrameCount = frameCount; - - return MA_SUCCESS; -} - -static ma_result ma_resource_manager_data_stream_unmap(ma_resource_manager_data_stream* pDataStream, ma_uint64 frameCount) -{ - ma_uint32 newRelativeCursor; - ma_uint32 pageSizeInFrames; - ma_job job; - - /* We cannot be using the data source after it's been uninitialized. */ - MA_ASSERT(ma_resource_manager_data_stream_result(pDataStream) != MA_UNAVAILABLE); - - if (pDataStream == NULL) { - return MA_INVALID_ARGS; - } - - if (ma_resource_manager_data_stream_result(pDataStream) != MA_SUCCESS) { - return MA_INVALID_OPERATION; - } - - /* The frame count should always fit inside a 32-bit integer. */ - if (frameCount > 0xFFFFFFFF) { - return MA_INVALID_ARGS; - } - - pageSizeInFrames = ma_resource_manager_data_stream_get_page_size_in_frames(pDataStream); - - /* The absolute cursor needs to be updated for ma_resource_manager_data_stream_get_cursor_in_pcm_frames(). */ - ma_resource_manager_data_stream_set_absolute_cursor(pDataStream, ma_atomic_load_64(&pDataStream->absoluteCursor) + frameCount); - - /* Here is where we need to check if we need to load a new page, and if so, post a job to load it. */ - newRelativeCursor = pDataStream->relativeCursor + (ma_uint32)frameCount; - - /* If the new cursor has flowed over to the next page we need to mark the old one as invalid and post an event for it. */ - if (newRelativeCursor >= pageSizeInFrames) { - newRelativeCursor -= pageSizeInFrames; - - /* Here is where we post the job start decoding. */ - job = ma_job_init(MA_JOB_TYPE_RESOURCE_MANAGER_PAGE_DATA_STREAM); - job.order = ma_resource_manager_data_stream_next_execution_order(pDataStream); - job.data.resourceManager.pageDataStream.pDataStream = pDataStream; - job.data.resourceManager.pageDataStream.pageIndex = pDataStream->currentPageIndex; - - /* The page needs to be marked as invalid so that the public API doesn't try reading from it. */ - ma_atomic_exchange_32(&pDataStream->isPageValid[pDataStream->currentPageIndex], MA_FALSE); - - /* Before posting the job we need to make sure we set some state. */ - pDataStream->relativeCursor = newRelativeCursor; - pDataStream->currentPageIndex = (pDataStream->currentPageIndex + 1) & 0x01; - return ma_resource_manager_post_job(pDataStream->pResourceManager, &job); - } else { - /* We haven't moved into a new page so we can just move the cursor forward. */ - pDataStream->relativeCursor = newRelativeCursor; - return MA_SUCCESS; - } -} - - -MA_API ma_result ma_resource_manager_data_stream_read_pcm_frames(ma_resource_manager_data_stream* pDataStream, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead) -{ - ma_result result = MA_SUCCESS; - ma_uint64 totalFramesProcessed; - ma_format format; - ma_uint32 channels; - - /* Safety. */ - if (pFramesRead != NULL) { - *pFramesRead = 0; - } - - if (frameCount == 0) { - return MA_INVALID_ARGS; - } - - /* We cannot be using the data source after it's been uninitialized. */ - MA_ASSERT(ma_resource_manager_data_stream_result(pDataStream) != MA_UNAVAILABLE); - - if (pDataStream == NULL) { - return MA_INVALID_ARGS; - } - - if (ma_resource_manager_data_stream_result(pDataStream) != MA_SUCCESS) { - return MA_INVALID_OPERATION; - } - - /* Don't attempt to read while we're in the middle of seeking. Tell the caller that we're busy. */ - if (ma_resource_manager_data_stream_seek_counter(pDataStream) > 0) { - return MA_BUSY; - } - - ma_resource_manager_data_stream_get_data_format(pDataStream, &format, &channels, NULL, NULL, 0); - - /* Reading is implemented in terms of map/unmap. We need to run this in a loop because mapping is clamped against page boundaries. */ - totalFramesProcessed = 0; - while (totalFramesProcessed < frameCount) { - void* pMappedFrames; - ma_uint64 mappedFrameCount; - - mappedFrameCount = frameCount - totalFramesProcessed; - result = ma_resource_manager_data_stream_map(pDataStream, &pMappedFrames, &mappedFrameCount); - if (result != MA_SUCCESS) { - break; - } - - /* Copy the mapped data to the output buffer if we have one. It's allowed for pFramesOut to be NULL in which case a relative forward seek is performed. */ - if (pFramesOut != NULL) { - ma_copy_pcm_frames(ma_offset_pcm_frames_ptr(pFramesOut, totalFramesProcessed, format, channels), pMappedFrames, mappedFrameCount, format, channels); - } - - totalFramesProcessed += mappedFrameCount; - - result = ma_resource_manager_data_stream_unmap(pDataStream, mappedFrameCount); - if (result != MA_SUCCESS) { - break; /* This is really bad - will only get an error here if we failed to post a job to the queue for loading the next page. */ - } - } - - if (pFramesRead != NULL) { - *pFramesRead = totalFramesProcessed; - } - - if (result == MA_SUCCESS && totalFramesProcessed == 0) { - result = MA_AT_END; - } - - return result; -} - -MA_API ma_result ma_resource_manager_data_stream_seek_to_pcm_frame(ma_resource_manager_data_stream* pDataStream, ma_uint64 frameIndex) -{ - ma_job job; - ma_result streamResult; - - streamResult = ma_resource_manager_data_stream_result(pDataStream); - - /* We cannot be using the data source after it's been uninitialized. */ - MA_ASSERT(streamResult != MA_UNAVAILABLE); - - if (pDataStream == NULL) { - return MA_INVALID_ARGS; - } - - if (streamResult != MA_SUCCESS && streamResult != MA_BUSY) { - return MA_INVALID_OPERATION; - } - - /* If we're not already seeking and we're sitting on the same frame, just make this a no-op. */ - if (ma_atomic_load_32(&pDataStream->seekCounter) == 0) { - if (ma_atomic_load_64(&pDataStream->absoluteCursor) == frameIndex) { - return MA_SUCCESS; - } - } - - - /* Increment the seek counter first to indicate to read_paged_pcm_frames() and map_paged_pcm_frames() that we are in the middle of a seek and MA_BUSY should be returned. */ - ma_atomic_fetch_add_32(&pDataStream->seekCounter, 1); - - /* Update the absolute cursor so that ma_resource_manager_data_stream_get_cursor_in_pcm_frames() returns the new position. */ - ma_resource_manager_data_stream_set_absolute_cursor(pDataStream, frameIndex); - - /* - We need to clear our currently loaded pages so that the stream starts playback from the new seek point as soon as possible. These are for the purpose of the public - API and will be ignored by the seek job. The seek job will operate on the assumption that both pages have been marked as invalid and the cursor is at the start of - the first page. - */ - pDataStream->relativeCursor = 0; - pDataStream->currentPageIndex = 0; - ma_atomic_exchange_32(&pDataStream->isPageValid[0], MA_FALSE); - ma_atomic_exchange_32(&pDataStream->isPageValid[1], MA_FALSE); - - /* Make sure the data stream is not marked as at the end or else if we seek in response to hitting the end, we won't be able to read any more data. */ - ma_atomic_exchange_32(&pDataStream->isDecoderAtEnd, MA_FALSE); - - /* - The public API is not allowed to touch the internal decoder so we need to use a job to perform the seek. When seeking, the job thread will assume both pages - are invalid and any content contained within them will be discarded and replaced with newly decoded data. - */ - job = ma_job_init(MA_JOB_TYPE_RESOURCE_MANAGER_SEEK_DATA_STREAM); - job.order = ma_resource_manager_data_stream_next_execution_order(pDataStream); - job.data.resourceManager.seekDataStream.pDataStream = pDataStream; - job.data.resourceManager.seekDataStream.frameIndex = frameIndex; - return ma_resource_manager_post_job(pDataStream->pResourceManager, &job); -} - -MA_API ma_result ma_resource_manager_data_stream_get_data_format(ma_resource_manager_data_stream* pDataStream, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate, ma_channel* pChannelMap, size_t channelMapCap) -{ - /* We cannot be using the data source after it's been uninitialized. */ - MA_ASSERT(ma_resource_manager_data_stream_result(pDataStream) != MA_UNAVAILABLE); - - if (pFormat != NULL) { - *pFormat = ma_format_unknown; - } - - if (pChannels != NULL) { - *pChannels = 0; - } - - if (pSampleRate != NULL) { - *pSampleRate = 0; - } - - if (pChannelMap != NULL) { - MA_ZERO_MEMORY(pChannelMap, sizeof(*pChannelMap) * channelMapCap); - } - - if (pDataStream == NULL) { - return MA_INVALID_ARGS; - } - - if (ma_resource_manager_data_stream_result(pDataStream) != MA_SUCCESS) { - return MA_INVALID_OPERATION; - } - - /* - We're being a little bit naughty here and accessing the internal decoder from the public API. The output data format is constant, and we've defined this function - such that the application is responsible for ensuring it's not called while uninitializing so it should be safe. - */ - return ma_data_source_get_data_format(&pDataStream->decoder, pFormat, pChannels, pSampleRate, pChannelMap, channelMapCap); -} - -MA_API ma_result ma_resource_manager_data_stream_get_cursor_in_pcm_frames(ma_resource_manager_data_stream* pDataStream, ma_uint64* pCursor) -{ - ma_result result; - - if (pCursor == NULL) { - return MA_INVALID_ARGS; - } - - *pCursor = 0; - - /* We cannot be using the data source after it's been uninitialized. */ - MA_ASSERT(ma_resource_manager_data_stream_result(pDataStream) != MA_UNAVAILABLE); - - if (pDataStream == NULL) { - return MA_INVALID_ARGS; - } - - /* - If the stream is in an erroneous state we need to return an invalid operation. We can allow - this to be called when the data stream is in a busy state because the caller may have asked - for an initial seek position and it's convenient to return that as the cursor position. - */ - result = ma_resource_manager_data_stream_result(pDataStream); - if (result != MA_SUCCESS && result != MA_BUSY) { - return MA_INVALID_OPERATION; - } - - *pCursor = ma_atomic_load_64(&pDataStream->absoluteCursor); - - return MA_SUCCESS; -} - -MA_API ma_result ma_resource_manager_data_stream_get_length_in_pcm_frames(ma_resource_manager_data_stream* pDataStream, ma_uint64* pLength) -{ - ma_result streamResult; - - if (pLength == NULL) { - return MA_INVALID_ARGS; - } - - *pLength = 0; - - streamResult = ma_resource_manager_data_stream_result(pDataStream); - - /* We cannot be using the data source after it's been uninitialized. */ - MA_ASSERT(streamResult != MA_UNAVAILABLE); - - if (pDataStream == NULL) { - return MA_INVALID_ARGS; - } - - if (streamResult != MA_SUCCESS) { - return streamResult; - } - - /* - We most definitely do not want to be calling ma_decoder_get_length_in_pcm_frames() directly. Instead we want to use a cached value that we - calculated when we initialized it on the job thread. - */ - *pLength = pDataStream->totalLengthInPCMFrames; - if (*pLength == 0) { - return MA_NOT_IMPLEMENTED; /* Some decoders may not have a known length. */ - } - - return MA_SUCCESS; -} - -MA_API ma_result ma_resource_manager_data_stream_result(const ma_resource_manager_data_stream* pDataStream) -{ - if (pDataStream == NULL) { - return MA_INVALID_ARGS; - } - - return (ma_result)ma_atomic_load_i32(&pDataStream->result); -} - -MA_API ma_result ma_resource_manager_data_stream_set_looping(ma_resource_manager_data_stream* pDataStream, ma_bool32 isLooping) -{ - return ma_data_source_set_looping(pDataStream, isLooping); -} - -MA_API ma_bool32 ma_resource_manager_data_stream_is_looping(const ma_resource_manager_data_stream* pDataStream) -{ - if (pDataStream == NULL) { - return MA_FALSE; - } - - return ma_atomic_load_32((ma_bool32*)&pDataStream->isLooping); /* Naughty const-cast. Value won't change from here in practice (maybe from another thread). */ -} - -MA_API ma_result ma_resource_manager_data_stream_get_available_frames(ma_resource_manager_data_stream* pDataStream, ma_uint64* pAvailableFrames) -{ - ma_uint32 pageIndex0; - ma_uint32 pageIndex1; - ma_uint32 relativeCursor; - ma_uint64 availableFrames; - - if (pAvailableFrames == NULL) { - return MA_INVALID_ARGS; - } - - *pAvailableFrames = 0; - - if (pDataStream == NULL) { - return MA_INVALID_ARGS; - } - - pageIndex0 = pDataStream->currentPageIndex; - pageIndex1 = (pDataStream->currentPageIndex + 1) & 0x01; - relativeCursor = pDataStream->relativeCursor; - - availableFrames = 0; - if (ma_atomic_load_32(&pDataStream->isPageValid[pageIndex0])) { - availableFrames += ma_atomic_load_32(&pDataStream->pageFrameCount[pageIndex0]) - relativeCursor; - if (ma_atomic_load_32(&pDataStream->isPageValid[pageIndex1])) { - availableFrames += ma_atomic_load_32(&pDataStream->pageFrameCount[pageIndex1]); - } - } - - *pAvailableFrames = availableFrames; - return MA_SUCCESS; -} - - -static ma_result ma_resource_manager_data_source_preinit(ma_resource_manager* pResourceManager, const ma_resource_manager_data_source_config* pConfig, ma_resource_manager_data_source* pDataSource) -{ - if (pDataSource == NULL) { - return MA_INVALID_ARGS; - } - - MA_ZERO_OBJECT(pDataSource); - - if (pConfig == NULL) { - return MA_INVALID_ARGS; - } - - if (pResourceManager == NULL) { - return MA_INVALID_ARGS; - } - - pDataSource->flags = pConfig->flags; - if (pConfig->isLooping) { - pDataSource->flags |= MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_LOOPING; - } - - return MA_SUCCESS; -} - -MA_API ma_result ma_resource_manager_data_source_init_ex(ma_resource_manager* pResourceManager, const ma_resource_manager_data_source_config* pConfig, ma_resource_manager_data_source* pDataSource) -{ - ma_result result; - - result = ma_resource_manager_data_source_preinit(pResourceManager, pConfig, pDataSource); - if (result != MA_SUCCESS) { - return result; - } - - /* The data source itself is just a data stream or a data buffer. */ - if ((pConfig->flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_STREAM) != 0) { - return ma_resource_manager_data_stream_init_ex(pResourceManager, pConfig, &pDataSource->backend.stream); - } else { - return ma_resource_manager_data_buffer_init_ex(pResourceManager, pConfig, &pDataSource->backend.buffer); - } -} - -MA_API ma_result ma_resource_manager_data_source_init(ma_resource_manager* pResourceManager, const char* pName, ma_uint32 flags, const ma_resource_manager_pipeline_notifications* pNotifications, ma_resource_manager_data_source* pDataSource) -{ - ma_resource_manager_data_source_config config; - - config = ma_resource_manager_data_source_config_init(); - config.pFilePath = pName; - config.flags = flags; - config.pNotifications = pNotifications; - - return ma_resource_manager_data_source_init_ex(pResourceManager, &config, pDataSource); -} - -MA_API ma_result ma_resource_manager_data_source_init_w(ma_resource_manager* pResourceManager, const wchar_t* pName, ma_uint32 flags, const ma_resource_manager_pipeline_notifications* pNotifications, ma_resource_manager_data_source* pDataSource) -{ - ma_resource_manager_data_source_config config; - - config = ma_resource_manager_data_source_config_init(); - config.pFilePathW = pName; - config.flags = flags; - config.pNotifications = pNotifications; - - return ma_resource_manager_data_source_init_ex(pResourceManager, &config, pDataSource); -} - -MA_API ma_result ma_resource_manager_data_source_init_copy(ma_resource_manager* pResourceManager, const ma_resource_manager_data_source* pExistingDataSource, ma_resource_manager_data_source* pDataSource) -{ - ma_result result; - ma_resource_manager_data_source_config config; - - if (pExistingDataSource == NULL) { - return MA_INVALID_ARGS; - } - - config = ma_resource_manager_data_source_config_init(); - config.flags = pExistingDataSource->flags; - - result = ma_resource_manager_data_source_preinit(pResourceManager, &config, pDataSource); - if (result != MA_SUCCESS) { - return result; - } - - /* Copying can only be done from data buffers. Streams cannot be copied. */ - if ((pExistingDataSource->flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_STREAM) != 0) { - return MA_INVALID_OPERATION; - } - - return ma_resource_manager_data_buffer_init_copy(pResourceManager, &pExistingDataSource->backend.buffer, &pDataSource->backend.buffer); -} - -MA_API ma_result ma_resource_manager_data_source_uninit(ma_resource_manager_data_source* pDataSource) -{ - if (pDataSource == NULL) { - return MA_INVALID_ARGS; - } - - /* All we need to is uninitialize the underlying data buffer or data stream. */ - if ((pDataSource->flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_STREAM) != 0) { - return ma_resource_manager_data_stream_uninit(&pDataSource->backend.stream); - } else { - return ma_resource_manager_data_buffer_uninit(&pDataSource->backend.buffer); - } -} - -MA_API ma_result ma_resource_manager_data_source_read_pcm_frames(ma_resource_manager_data_source* pDataSource, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead) -{ - /* Safety. */ - if (pFramesRead != NULL) { - *pFramesRead = 0; - } - - if (pDataSource == NULL) { - return MA_INVALID_ARGS; - } - - if ((pDataSource->flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_STREAM) != 0) { - return ma_resource_manager_data_stream_read_pcm_frames(&pDataSource->backend.stream, pFramesOut, frameCount, pFramesRead); - } else { - return ma_resource_manager_data_buffer_read_pcm_frames(&pDataSource->backend.buffer, pFramesOut, frameCount, pFramesRead); - } -} - -MA_API ma_result ma_resource_manager_data_source_seek_to_pcm_frame(ma_resource_manager_data_source* pDataSource, ma_uint64 frameIndex) -{ - if (pDataSource == NULL) { - return MA_INVALID_ARGS; - } - - if ((pDataSource->flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_STREAM) != 0) { - return ma_resource_manager_data_stream_seek_to_pcm_frame(&pDataSource->backend.stream, frameIndex); - } else { - return ma_resource_manager_data_buffer_seek_to_pcm_frame(&pDataSource->backend.buffer, frameIndex); - } -} - -MA_API ma_result ma_resource_manager_data_source_map(ma_resource_manager_data_source* pDataSource, void** ppFramesOut, ma_uint64* pFrameCount) -{ - if (pDataSource == NULL) { - return MA_INVALID_ARGS; - } - - if ((pDataSource->flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_STREAM) != 0) { - return ma_resource_manager_data_stream_map(&pDataSource->backend.stream, ppFramesOut, pFrameCount); - } else { - return MA_NOT_IMPLEMENTED; /* Mapping not supported with data buffers. */ - } -} - -MA_API ma_result ma_resource_manager_data_source_unmap(ma_resource_manager_data_source* pDataSource, ma_uint64 frameCount) -{ - if (pDataSource == NULL) { - return MA_INVALID_ARGS; - } - - if ((pDataSource->flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_STREAM) != 0) { - return ma_resource_manager_data_stream_unmap(&pDataSource->backend.stream, frameCount); - } else { - return MA_NOT_IMPLEMENTED; /* Mapping not supported with data buffers. */ - } -} - -MA_API ma_result ma_resource_manager_data_source_get_data_format(ma_resource_manager_data_source* pDataSource, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate, ma_channel* pChannelMap, size_t channelMapCap) -{ - if (pDataSource == NULL) { - return MA_INVALID_ARGS; - } - - if ((pDataSource->flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_STREAM) != 0) { - return ma_resource_manager_data_stream_get_data_format(&pDataSource->backend.stream, pFormat, pChannels, pSampleRate, pChannelMap, channelMapCap); - } else { - return ma_resource_manager_data_buffer_get_data_format(&pDataSource->backend.buffer, pFormat, pChannels, pSampleRate, pChannelMap, channelMapCap); - } -} - -MA_API ma_result ma_resource_manager_data_source_get_cursor_in_pcm_frames(ma_resource_manager_data_source* pDataSource, ma_uint64* pCursor) -{ - if (pDataSource == NULL) { - return MA_INVALID_ARGS; - } - - if ((pDataSource->flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_STREAM) != 0) { - return ma_resource_manager_data_stream_get_cursor_in_pcm_frames(&pDataSource->backend.stream, pCursor); - } else { - return ma_resource_manager_data_buffer_get_cursor_in_pcm_frames(&pDataSource->backend.buffer, pCursor); - } -} - -MA_API ma_result ma_resource_manager_data_source_get_length_in_pcm_frames(ma_resource_manager_data_source* pDataSource, ma_uint64* pLength) -{ - if (pDataSource == NULL) { - return MA_INVALID_ARGS; - } - - if ((pDataSource->flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_STREAM) != 0) { - return ma_resource_manager_data_stream_get_length_in_pcm_frames(&pDataSource->backend.stream, pLength); - } else { - return ma_resource_manager_data_buffer_get_length_in_pcm_frames(&pDataSource->backend.buffer, pLength); - } -} - -MA_API ma_result ma_resource_manager_data_source_result(const ma_resource_manager_data_source* pDataSource) -{ - if (pDataSource == NULL) { - return MA_INVALID_ARGS; - } - - if ((pDataSource->flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_STREAM) != 0) { - return ma_resource_manager_data_stream_result(&pDataSource->backend.stream); - } else { - return ma_resource_manager_data_buffer_result(&pDataSource->backend.buffer); - } -} - -MA_API ma_result ma_resource_manager_data_source_set_looping(ma_resource_manager_data_source* pDataSource, ma_bool32 isLooping) -{ - if (pDataSource == NULL) { - return MA_INVALID_ARGS; - } - - if ((pDataSource->flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_STREAM) != 0) { - return ma_resource_manager_data_stream_set_looping(&pDataSource->backend.stream, isLooping); - } else { - return ma_resource_manager_data_buffer_set_looping(&pDataSource->backend.buffer, isLooping); - } -} - -MA_API ma_bool32 ma_resource_manager_data_source_is_looping(const ma_resource_manager_data_source* pDataSource) -{ - if (pDataSource == NULL) { - return MA_FALSE; - } - - if ((pDataSource->flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_STREAM) != 0) { - return ma_resource_manager_data_stream_is_looping(&pDataSource->backend.stream); - } else { - return ma_resource_manager_data_buffer_is_looping(&pDataSource->backend.buffer); - } -} - -MA_API ma_result ma_resource_manager_data_source_get_available_frames(ma_resource_manager_data_source* pDataSource, ma_uint64* pAvailableFrames) -{ - if (pAvailableFrames == NULL) { - return MA_INVALID_ARGS; - } - - *pAvailableFrames = 0; - - if (pDataSource == NULL) { - return MA_INVALID_ARGS; - } - - if ((pDataSource->flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_STREAM) != 0) { - return ma_resource_manager_data_stream_get_available_frames(&pDataSource->backend.stream, pAvailableFrames); - } else { - return ma_resource_manager_data_buffer_get_available_frames(&pDataSource->backend.buffer, pAvailableFrames); - } -} - - -MA_API ma_result ma_resource_manager_post_job(ma_resource_manager* pResourceManager, const ma_job* pJob) -{ - if (pResourceManager == NULL) { - return MA_INVALID_ARGS; - } - - return ma_job_queue_post(&pResourceManager->jobQueue, pJob); -} - -MA_API ma_result ma_resource_manager_post_job_quit(ma_resource_manager* pResourceManager) -{ - ma_job job = ma_job_init(MA_JOB_TYPE_QUIT); - return ma_resource_manager_post_job(pResourceManager, &job); -} - -MA_API ma_result ma_resource_manager_next_job(ma_resource_manager* pResourceManager, ma_job* pJob) -{ - if (pResourceManager == NULL) { - return MA_INVALID_ARGS; - } - - return ma_job_queue_next(&pResourceManager->jobQueue, pJob); -} - - -static ma_result ma_job_process__resource_manager__load_data_buffer_node(ma_job* pJob) -{ - ma_result result = MA_SUCCESS; - ma_resource_manager* pResourceManager; - ma_resource_manager_data_buffer_node* pDataBufferNode; - - MA_ASSERT(pJob != NULL); - - pResourceManager = (ma_resource_manager*)pJob->data.resourceManager.loadDataBufferNode.pResourceManager; - MA_ASSERT(pResourceManager != NULL); - - pDataBufferNode = (ma_resource_manager_data_buffer_node*)pJob->data.resourceManager.loadDataBufferNode.pDataBufferNode; - MA_ASSERT(pDataBufferNode != NULL); - MA_ASSERT(pDataBufferNode->isDataOwnedByResourceManager == MA_TRUE); /* The data should always be owned by the resource manager. */ - - /* The data buffer is not getting deleted, but we may be getting executed out of order. If so, we need to push the job back onto the queue and return. */ - if (pJob->order != ma_atomic_load_32(&pDataBufferNode->executionPointer)) { - return ma_resource_manager_post_job(pResourceManager, pJob); /* Attempting to execute out of order. Probably interleaved with a MA_JOB_TYPE_RESOURCE_MANAGER_FREE_DATA_BUFFER job. */ - } - - /* First thing we need to do is check whether or not the data buffer is getting deleted. If so we just abort. */ - if (ma_resource_manager_data_buffer_node_result(pDataBufferNode) != MA_BUSY) { - result = ma_resource_manager_data_buffer_node_result(pDataBufferNode); /* The data buffer may be getting deleted before it's even been loaded. */ - goto done; - } - - /* - We're ready to start loading. Essentially what we're doing here is initializing the data supply - of the node. Once this is complete, data buffers can have their connectors initialized which - will allow then to have audio data read from them. - - Note that when the data supply type has been moved away from "unknown", that is when other threads - will determine that the node is available for data delivery and the data buffer connectors can be - initialized. Therefore, it's important that it is set after the data supply has been initialized. - */ - if ((pJob->data.resourceManager.loadDataBufferNode.flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_DECODE) != 0) { - /* - Decoding. This is the complex case because we're not going to be doing the entire decoding - process here. Instead it's going to be split of multiple jobs and loaded in pages. The - reason for this is to evenly distribute decoding time across multiple sounds, rather than - having one huge sound hog all the available processing resources. - - The first thing we do is initialize a decoder. This is allocated on the heap and is passed - around to the paging jobs. When the last paging job has completed it's processing, it'll - free the decoder for us. - - This job does not do any actual decoding. It instead just posts a PAGE_DATA_BUFFER_NODE job - which is where the actual decoding work will be done. However, once this job is complete, - the node will be in a state where data buffer connectors can be initialized. - */ - ma_decoder* pDecoder; /* <-- Free'd on the last page decode. */ - ma_job pageDataBufferNodeJob; - - /* Allocate the decoder by initializing a decoded data supply. */ - result = ma_resource_manager_data_buffer_node_init_supply_decoded(pResourceManager, pDataBufferNode, pJob->data.resourceManager.loadDataBufferNode.pFilePath, pJob->data.resourceManager.loadDataBufferNode.pFilePathW, pJob->data.resourceManager.loadDataBufferNode.flags, &pDecoder); - - /* - Don't ever propagate an MA_BUSY result code or else the resource manager will think the - node is just busy decoding rather than in an error state. This should never happen, but - including this logic for safety just in case. - */ - if (result == MA_BUSY) { - result = MA_ERROR; - } - - if (result != MA_SUCCESS) { - if (pJob->data.resourceManager.loadDataBufferNode.pFilePath != NULL) { - ma_log_postf(ma_resource_manager_get_log(pResourceManager), MA_LOG_LEVEL_WARNING, "Failed to initialize data supply for \"%s\". %s.\n", pJob->data.resourceManager.loadDataBufferNode.pFilePath, ma_result_description(result)); - } else { - #if (defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L) || defined(_MSC_VER) - ma_log_postf(ma_resource_manager_get_log(pResourceManager), MA_LOG_LEVEL_WARNING, "Failed to initialize data supply for \"%ls\", %s.\n", pJob->data.resourceManager.loadDataBufferNode.pFilePathW, ma_result_description(result)); - #endif - } - - goto done; - } - - /* - At this point the node's data supply is initialized and other threads can start initializing - their data buffer connectors. However, no data will actually be available until we start to - actually decode it. To do this, we need to post a paging job which is where the decoding - work is done. - - Note that if an error occurred at an earlier point, this section will have been skipped. - */ - pageDataBufferNodeJob = ma_job_init(MA_JOB_TYPE_RESOURCE_MANAGER_PAGE_DATA_BUFFER_NODE); - pageDataBufferNodeJob.order = ma_resource_manager_data_buffer_node_next_execution_order(pDataBufferNode); - pageDataBufferNodeJob.data.resourceManager.pageDataBufferNode.pResourceManager = pResourceManager; - pageDataBufferNodeJob.data.resourceManager.pageDataBufferNode.pDataBufferNode = pDataBufferNode; - pageDataBufferNodeJob.data.resourceManager.pageDataBufferNode.pDecoder = pDecoder; - pageDataBufferNodeJob.data.resourceManager.pageDataBufferNode.pDoneNotification = pJob->data.resourceManager.loadDataBufferNode.pDoneNotification; - pageDataBufferNodeJob.data.resourceManager.pageDataBufferNode.pDoneFence = pJob->data.resourceManager.loadDataBufferNode.pDoneFence; - - /* The job has been set up so it can now be posted. */ - result = ma_resource_manager_post_job(pResourceManager, &pageDataBufferNodeJob); - - /* - When we get here, we want to make sure the result code is set to MA_BUSY. The reason for - this is that the result will be copied over to the node's internal result variable. In - this case, since the decoding is still in-progress, we need to make sure the result code - is set to MA_BUSY. - */ - if (result != MA_SUCCESS) { - ma_log_postf(ma_resource_manager_get_log(pResourceManager), MA_LOG_LEVEL_ERROR, "Failed to post MA_JOB_TYPE_RESOURCE_MANAGER_PAGE_DATA_BUFFER_NODE job. %s\n", ma_result_description(result)); - ma_decoder_uninit(pDecoder); - ma_free(pDecoder, &pResourceManager->config.allocationCallbacks); - } else { - result = MA_BUSY; - } - } else { - /* No decoding. This is the simple case. We need only read the file content into memory and we're done. */ - result = ma_resource_manager_data_buffer_node_init_supply_encoded(pResourceManager, pDataBufferNode, pJob->data.resourceManager.loadDataBufferNode.pFilePath, pJob->data.resourceManager.loadDataBufferNode.pFilePathW); - } - - -done: - /* File paths are no longer needed. */ - ma_free(pJob->data.resourceManager.loadDataBufferNode.pFilePath, &pResourceManager->config.allocationCallbacks); - ma_free(pJob->data.resourceManager.loadDataBufferNode.pFilePathW, &pResourceManager->config.allocationCallbacks); - - /* - We need to set the result to at the very end to ensure no other threads try reading the data before we've fully initialized the object. Other threads - are going to be inspecting this variable to determine whether or not they're ready to read data. We can only change the result if it's set to MA_BUSY - because otherwise we may be changing away from an error code which would be bad. An example is if the application creates a data buffer, but then - immediately deletes it before we've got to this point. In this case, pDataBuffer->result will be MA_UNAVAILABLE, and setting it to MA_SUCCESS or any - other error code would cause the buffer to look like it's in a state that it's not. - */ - ma_atomic_compare_and_swap_i32(&pDataBufferNode->result, MA_BUSY, result); - - /* At this point initialization is complete and we can signal the notification if any. */ - if (pJob->data.resourceManager.loadDataBufferNode.pInitNotification != NULL) { - ma_async_notification_signal(pJob->data.resourceManager.loadDataBufferNode.pInitNotification); - } - if (pJob->data.resourceManager.loadDataBufferNode.pInitFence != NULL) { - ma_fence_release(pJob->data.resourceManager.loadDataBufferNode.pInitFence); - } - - /* If we have a success result it means we've fully loaded the buffer. This will happen in the non-decoding case. */ - if (result != MA_BUSY) { - if (pJob->data.resourceManager.loadDataBufferNode.pDoneNotification != NULL) { - ma_async_notification_signal(pJob->data.resourceManager.loadDataBufferNode.pDoneNotification); - } - if (pJob->data.resourceManager.loadDataBufferNode.pDoneFence != NULL) { - ma_fence_release(pJob->data.resourceManager.loadDataBufferNode.pDoneFence); - } - } - - /* Increment the node's execution pointer so that the next jobs can be processed. This is how we keep decoding of pages in-order. */ - ma_atomic_fetch_add_32(&pDataBufferNode->executionPointer, 1); - - /* A busy result should be considered successful from the point of view of the job system. */ - if (result == MA_BUSY) { - result = MA_SUCCESS; - } - - return result; -} - -static ma_result ma_job_process__resource_manager__free_data_buffer_node(ma_job* pJob) -{ - ma_resource_manager* pResourceManager; - ma_resource_manager_data_buffer_node* pDataBufferNode; - - MA_ASSERT(pJob != NULL); - - pResourceManager = (ma_resource_manager*)pJob->data.resourceManager.freeDataBufferNode.pResourceManager; - MA_ASSERT(pResourceManager != NULL); - - pDataBufferNode = (ma_resource_manager_data_buffer_node*)pJob->data.resourceManager.freeDataBufferNode.pDataBufferNode; - MA_ASSERT(pDataBufferNode != NULL); - - if (pJob->order != ma_atomic_load_32(&pDataBufferNode->executionPointer)) { - return ma_resource_manager_post_job(pResourceManager, pJob); /* Out of order. */ - } - - ma_resource_manager_data_buffer_node_free(pResourceManager, pDataBufferNode); - - /* The event needs to be signalled last. */ - if (pJob->data.resourceManager.freeDataBufferNode.pDoneNotification != NULL) { - ma_async_notification_signal(pJob->data.resourceManager.freeDataBufferNode.pDoneNotification); - } - - if (pJob->data.resourceManager.freeDataBufferNode.pDoneFence != NULL) { - ma_fence_release(pJob->data.resourceManager.freeDataBufferNode.pDoneFence); - } - - ma_atomic_fetch_add_32(&pDataBufferNode->executionPointer, 1); - return MA_SUCCESS; -} - -static ma_result ma_job_process__resource_manager__page_data_buffer_node(ma_job* pJob) -{ - ma_result result = MA_SUCCESS; - ma_resource_manager* pResourceManager; - ma_resource_manager_data_buffer_node* pDataBufferNode; - - MA_ASSERT(pJob != NULL); - - pResourceManager = (ma_resource_manager*)pJob->data.resourceManager.pageDataBufferNode.pResourceManager; - MA_ASSERT(pResourceManager != NULL); - - pDataBufferNode = (ma_resource_manager_data_buffer_node*)pJob->data.resourceManager.pageDataBufferNode.pDataBufferNode; - MA_ASSERT(pDataBufferNode != NULL); - - if (pJob->order != ma_atomic_load_32(&pDataBufferNode->executionPointer)) { - return ma_resource_manager_post_job(pResourceManager, pJob); /* Out of order. */ - } - - /* Don't do any more decoding if the data buffer has started the uninitialization process. */ - result = ma_resource_manager_data_buffer_node_result(pDataBufferNode); - if (result != MA_BUSY) { - goto done; - } - - /* We're ready to decode the next page. */ - result = ma_resource_manager_data_buffer_node_decode_next_page(pResourceManager, pDataBufferNode, (ma_decoder*)pJob->data.resourceManager.pageDataBufferNode.pDecoder); - - /* - If we have a success code by this point, we want to post another job. We're going to set the - result back to MA_BUSY to make it clear that there's still more to load. - */ - if (result == MA_SUCCESS) { - ma_job newJob; - newJob = *pJob; /* Everything is the same as the input job, except the execution order. */ - newJob.order = ma_resource_manager_data_buffer_node_next_execution_order(pDataBufferNode); /* We need a fresh execution order. */ - - result = ma_resource_manager_post_job(pResourceManager, &newJob); - - /* Since the sound isn't yet fully decoded we want the status to be set to busy. */ - if (result == MA_SUCCESS) { - result = MA_BUSY; - } - } - -done: - /* If there's still more to decode the result will be set to MA_BUSY. Otherwise we can free the decoder. */ - if (result != MA_BUSY) { - ma_decoder_uninit((ma_decoder*)pJob->data.resourceManager.pageDataBufferNode.pDecoder); - ma_free(pJob->data.resourceManager.pageDataBufferNode.pDecoder, &pResourceManager->config.allocationCallbacks); - } - - /* If we reached the end we need to treat it as successful. */ - if (result == MA_AT_END) { - result = MA_SUCCESS; - } - - /* Make sure we set the result of node in case some error occurred. */ - ma_atomic_compare_and_swap_i32(&pDataBufferNode->result, MA_BUSY, result); - - /* Signal the notification after setting the result in case the notification callback wants to inspect the result code. */ - if (result != MA_BUSY) { - if (pJob->data.resourceManager.pageDataBufferNode.pDoneNotification != NULL) { - ma_async_notification_signal(pJob->data.resourceManager.pageDataBufferNode.pDoneNotification); - } - - if (pJob->data.resourceManager.pageDataBufferNode.pDoneFence != NULL) { - ma_fence_release(pJob->data.resourceManager.pageDataBufferNode.pDoneFence); - } - } - - ma_atomic_fetch_add_32(&pDataBufferNode->executionPointer, 1); - return result; -} - - -static ma_result ma_job_process__resource_manager__load_data_buffer(ma_job* pJob) -{ - ma_result result = MA_SUCCESS; - ma_resource_manager* pResourceManager; - ma_resource_manager_data_buffer* pDataBuffer; - ma_resource_manager_data_supply_type dataSupplyType = ma_resource_manager_data_supply_type_unknown; - ma_bool32 isConnectorInitialized = MA_FALSE; - - /* - All we're doing here is checking if the node has finished loading. If not, we just re-post the job - and keep waiting. Otherwise we increment the execution counter and set the buffer's result code. - */ - MA_ASSERT(pJob != NULL); - - pDataBuffer = (ma_resource_manager_data_buffer*)pJob->data.resourceManager.loadDataBuffer.pDataBuffer; - MA_ASSERT(pDataBuffer != NULL); - - pResourceManager = pDataBuffer->pResourceManager; - - if (pJob->order != ma_atomic_load_32(&pDataBuffer->executionPointer)) { - return ma_resource_manager_post_job(pResourceManager, pJob); /* Attempting to execute out of order. Probably interleaved with a MA_JOB_TYPE_RESOURCE_MANAGER_FREE_DATA_BUFFER job. */ - } - - /* - First thing we need to do is check whether or not the data buffer is getting deleted. If so we - just abort, but making sure we increment the execution pointer. - */ - result = ma_resource_manager_data_buffer_result(pDataBuffer); - if (result != MA_BUSY) { - goto done; /* <-- This will ensure the execution pointer is incremented. */ - } else { - result = MA_SUCCESS; /* <-- Make sure this is reset. */ - (void)result; /* <-- This is to suppress a static analysis diagnostic about "result" not being used. But for safety when I do future maintenance I don't want to delete that assignment. */ - } - - /* Try initializing the connector if we haven't already. */ - isConnectorInitialized = ma_resource_manager_data_buffer_has_connector(pDataBuffer); - if (isConnectorInitialized == MA_FALSE) { - dataSupplyType = ma_resource_manager_data_buffer_node_get_data_supply_type(pDataBuffer->pNode); - - if (dataSupplyType != ma_resource_manager_data_supply_type_unknown) { - /* We can now initialize the connector. If this fails, we need to abort. It's very rare for this to fail. */ - ma_resource_manager_data_source_config dataSourceConfig; /* For setting initial looping state and range. */ - dataSourceConfig = ma_resource_manager_data_source_config_init(); - dataSourceConfig.rangeBegInPCMFrames = pJob->data.resourceManager.loadDataBuffer.rangeBegInPCMFrames; - dataSourceConfig.rangeEndInPCMFrames = pJob->data.resourceManager.loadDataBuffer.rangeEndInPCMFrames; - dataSourceConfig.loopPointBegInPCMFrames = pJob->data.resourceManager.loadDataBuffer.loopPointBegInPCMFrames; - dataSourceConfig.loopPointEndInPCMFrames = pJob->data.resourceManager.loadDataBuffer.loopPointEndInPCMFrames; - dataSourceConfig.isLooping = pJob->data.resourceManager.loadDataBuffer.isLooping; - - result = ma_resource_manager_data_buffer_init_connector(pDataBuffer, &dataSourceConfig, pJob->data.resourceManager.loadDataBuffer.pInitNotification, pJob->data.resourceManager.loadDataBuffer.pInitFence); - if (result != MA_SUCCESS) { - ma_log_postf(ma_resource_manager_get_log(pResourceManager), MA_LOG_LEVEL_ERROR, "Failed to initialize connector for data buffer. %s.\n", ma_result_description(result)); - goto done; - } - } else { - /* Don't have a known data supply type. Most likely the data buffer node is still loading, but it could be that an error occurred. */ - } - } else { - /* The connector is already initialized. Nothing to do here. */ - } - - /* - If the data node is still loading, we need to repost the job and *not* increment the execution - pointer (i.e. we need to not fall through to the "done" label). - - There is a hole between here and the where the data connector is initialized where the data - buffer node may have finished initializing. We need to check for this by checking the result of - the data buffer node and whether or not we had an unknown data supply type at the time of - trying to initialize the data connector. - */ - result = ma_resource_manager_data_buffer_node_result(pDataBuffer->pNode); - if (result == MA_BUSY || (result == MA_SUCCESS && isConnectorInitialized == MA_FALSE && dataSupplyType == ma_resource_manager_data_supply_type_unknown)) { - return ma_resource_manager_post_job(pResourceManager, pJob); - } - -done: - /* Only move away from a busy code so that we don't trash any existing error codes. */ - ma_atomic_compare_and_swap_i32(&pDataBuffer->result, MA_BUSY, result); - - /* Only signal the other threads after the result has been set just for cleanliness sake. */ - if (pJob->data.resourceManager.loadDataBuffer.pDoneNotification != NULL) { - ma_async_notification_signal(pJob->data.resourceManager.loadDataBuffer.pDoneNotification); - } - if (pJob->data.resourceManager.loadDataBuffer.pDoneFence != NULL) { - ma_fence_release(pJob->data.resourceManager.loadDataBuffer.pDoneFence); - } - - /* - If at this point the data buffer has not had it's connector initialized, it means the - notification event was never signalled which means we need to signal it here. - */ - if (ma_resource_manager_data_buffer_has_connector(pDataBuffer) == MA_FALSE && result != MA_SUCCESS) { - if (pJob->data.resourceManager.loadDataBuffer.pInitNotification != NULL) { - ma_async_notification_signal(pJob->data.resourceManager.loadDataBuffer.pInitNotification); - } - if (pJob->data.resourceManager.loadDataBuffer.pInitFence != NULL) { - ma_fence_release(pJob->data.resourceManager.loadDataBuffer.pInitFence); - } - } - - ma_atomic_fetch_add_32(&pDataBuffer->executionPointer, 1); - return result; -} - -static ma_result ma_job_process__resource_manager__free_data_buffer(ma_job* pJob) -{ - ma_resource_manager* pResourceManager; - ma_resource_manager_data_buffer* pDataBuffer; - - MA_ASSERT(pJob != NULL); - - pDataBuffer = (ma_resource_manager_data_buffer*)pJob->data.resourceManager.freeDataBuffer.pDataBuffer; - MA_ASSERT(pDataBuffer != NULL); - - pResourceManager = pDataBuffer->pResourceManager; - - if (pJob->order != ma_atomic_load_32(&pDataBuffer->executionPointer)) { - return ma_resource_manager_post_job(pResourceManager, pJob); /* Out of order. */ - } - - ma_resource_manager_data_buffer_uninit_internal(pDataBuffer); - - /* The event needs to be signalled last. */ - if (pJob->data.resourceManager.freeDataBuffer.pDoneNotification != NULL) { - ma_async_notification_signal(pJob->data.resourceManager.freeDataBuffer.pDoneNotification); - } - - if (pJob->data.resourceManager.freeDataBuffer.pDoneFence != NULL) { - ma_fence_release(pJob->data.resourceManager.freeDataBuffer.pDoneFence); - } - - ma_atomic_fetch_add_32(&pDataBuffer->executionPointer, 1); - return MA_SUCCESS; -} - -static ma_result ma_job_process__resource_manager__load_data_stream(ma_job* pJob) -{ - ma_result result = MA_SUCCESS; - ma_decoder_config decoderConfig; - ma_uint32 pageBufferSizeInBytes; - ma_resource_manager* pResourceManager; - ma_resource_manager_data_stream* pDataStream; - - MA_ASSERT(pJob != NULL); - - pDataStream = (ma_resource_manager_data_stream*)pJob->data.resourceManager.loadDataStream.pDataStream; - MA_ASSERT(pDataStream != NULL); - - pResourceManager = pDataStream->pResourceManager; - - if (pJob->order != ma_atomic_load_32(&pDataStream->executionPointer)) { - return ma_resource_manager_post_job(pResourceManager, pJob); /* Out of order. */ - } - - if (ma_resource_manager_data_stream_result(pDataStream) != MA_BUSY) { - result = MA_INVALID_OPERATION; /* Most likely the data stream is being uninitialized. */ - goto done; - } - - /* We need to initialize the decoder first so we can determine the size of the pages. */ - decoderConfig = ma_resource_manager__init_decoder_config(pResourceManager); - - if (pJob->data.resourceManager.loadDataStream.pFilePath != NULL) { - result = ma_decoder_init_vfs(pResourceManager->config.pVFS, pJob->data.resourceManager.loadDataStream.pFilePath, &decoderConfig, &pDataStream->decoder); - } else { - result = ma_decoder_init_vfs_w(pResourceManager->config.pVFS, pJob->data.resourceManager.loadDataStream.pFilePathW, &decoderConfig, &pDataStream->decoder); - } - if (result != MA_SUCCESS) { - goto done; - } - - /* Retrieve the total length of the file before marking the decoder as loaded. */ - if ((pDataStream->flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_UNKNOWN_LENGTH) == 0) { - result = ma_decoder_get_length_in_pcm_frames(&pDataStream->decoder, &pDataStream->totalLengthInPCMFrames); - if (result != MA_SUCCESS) { - goto done; /* Failed to retrieve the length. */ - } - } else { - pDataStream->totalLengthInPCMFrames = 0; - } - - /* - Only mark the decoder as initialized when the length of the decoder has been retrieved because that can possibly require a scan over the whole file - and we don't want to have another thread trying to access the decoder while it's scanning. - */ - pDataStream->isDecoderInitialized = MA_TRUE; - - /* We have the decoder so we can now initialize our page buffer. */ - pageBufferSizeInBytes = ma_resource_manager_data_stream_get_page_size_in_frames(pDataStream) * 2 * ma_get_bytes_per_frame(pDataStream->decoder.outputFormat, pDataStream->decoder.outputChannels); - - pDataStream->pPageData = ma_malloc(pageBufferSizeInBytes, &pResourceManager->config.allocationCallbacks); - if (pDataStream->pPageData == NULL) { - ma_decoder_uninit(&pDataStream->decoder); - result = MA_OUT_OF_MEMORY; - goto done; - } - - /* Seek to our initial seek point before filling the initial pages. */ - ma_decoder_seek_to_pcm_frame(&pDataStream->decoder, pJob->data.resourceManager.loadDataStream.initialSeekPoint); - - /* We have our decoder and our page buffer, so now we need to fill our pages. */ - ma_resource_manager_data_stream_fill_pages(pDataStream); - - /* And now we're done. We want to make sure the result is MA_SUCCESS. */ - result = MA_SUCCESS; - -done: - ma_free(pJob->data.resourceManager.loadDataStream.pFilePath, &pResourceManager->config.allocationCallbacks); - ma_free(pJob->data.resourceManager.loadDataStream.pFilePathW, &pResourceManager->config.allocationCallbacks); - - /* We can only change the status away from MA_BUSY. If it's set to anything else it means an error has occurred somewhere or the uninitialization process has started (most likely). */ - ma_atomic_compare_and_swap_i32(&pDataStream->result, MA_BUSY, result); - - /* Only signal the other threads after the result has been set just for cleanliness sake. */ - if (pJob->data.resourceManager.loadDataStream.pInitNotification != NULL) { - ma_async_notification_signal(pJob->data.resourceManager.loadDataStream.pInitNotification); - } - if (pJob->data.resourceManager.loadDataStream.pInitFence != NULL) { - ma_fence_release(pJob->data.resourceManager.loadDataStream.pInitFence); - } - - ma_atomic_fetch_add_32(&pDataStream->executionPointer, 1); - return result; -} - -static ma_result ma_job_process__resource_manager__free_data_stream(ma_job* pJob) -{ - ma_resource_manager* pResourceManager; - ma_resource_manager_data_stream* pDataStream; - - MA_ASSERT(pJob != NULL); - - pDataStream = (ma_resource_manager_data_stream*)pJob->data.resourceManager.freeDataStream.pDataStream; - MA_ASSERT(pDataStream != NULL); - - pResourceManager = pDataStream->pResourceManager; - - if (pJob->order != ma_atomic_load_32(&pDataStream->executionPointer)) { - return ma_resource_manager_post_job(pResourceManager, pJob); /* Out of order. */ - } - - /* If our status is not MA_UNAVAILABLE we have a bug somewhere. */ - MA_ASSERT(ma_resource_manager_data_stream_result(pDataStream) == MA_UNAVAILABLE); - - if (pDataStream->isDecoderInitialized) { - ma_decoder_uninit(&pDataStream->decoder); - } - - if (pDataStream->pPageData != NULL) { - ma_free(pDataStream->pPageData, &pResourceManager->config.allocationCallbacks); - pDataStream->pPageData = NULL; /* Just in case... */ - } - - ma_data_source_uninit(&pDataStream->ds); - - /* The event needs to be signalled last. */ - if (pJob->data.resourceManager.freeDataStream.pDoneNotification != NULL) { - ma_async_notification_signal(pJob->data.resourceManager.freeDataStream.pDoneNotification); - } - if (pJob->data.resourceManager.freeDataStream.pDoneFence != NULL) { - ma_fence_release(pJob->data.resourceManager.freeDataStream.pDoneFence); - } - - /*ma_atomic_fetch_add_32(&pDataStream->executionPointer, 1);*/ - return MA_SUCCESS; -} - -static ma_result ma_job_process__resource_manager__page_data_stream(ma_job* pJob) -{ - ma_result result = MA_SUCCESS; - ma_resource_manager* pResourceManager; - ma_resource_manager_data_stream* pDataStream; - - MA_ASSERT(pJob != NULL); - - pDataStream = (ma_resource_manager_data_stream*)pJob->data.resourceManager.pageDataStream.pDataStream; - MA_ASSERT(pDataStream != NULL); - - pResourceManager = pDataStream->pResourceManager; - - if (pJob->order != ma_atomic_load_32(&pDataStream->executionPointer)) { - return ma_resource_manager_post_job(pResourceManager, pJob); /* Out of order. */ - } - - /* For streams, the status should be MA_SUCCESS. */ - if (ma_resource_manager_data_stream_result(pDataStream) != MA_SUCCESS) { - result = MA_INVALID_OPERATION; - goto done; - } - - ma_resource_manager_data_stream_fill_page(pDataStream, pJob->data.resourceManager.pageDataStream.pageIndex); - -done: - ma_atomic_fetch_add_32(&pDataStream->executionPointer, 1); - return result; -} - -static ma_result ma_job_process__resource_manager__seek_data_stream(ma_job* pJob) -{ - ma_result result = MA_SUCCESS; - ma_resource_manager* pResourceManager; - ma_resource_manager_data_stream* pDataStream; - - MA_ASSERT(pJob != NULL); - - pDataStream = (ma_resource_manager_data_stream*)pJob->data.resourceManager.seekDataStream.pDataStream; - MA_ASSERT(pDataStream != NULL); - - pResourceManager = pDataStream->pResourceManager; - - if (pJob->order != ma_atomic_load_32(&pDataStream->executionPointer)) { - return ma_resource_manager_post_job(pResourceManager, pJob); /* Out of order. */ - } - - /* For streams the status should be MA_SUCCESS for this to do anything. */ - if (ma_resource_manager_data_stream_result(pDataStream) != MA_SUCCESS || pDataStream->isDecoderInitialized == MA_FALSE) { - result = MA_INVALID_OPERATION; - goto done; - } - - /* - With seeking we just assume both pages are invalid and the relative frame cursor at position 0. This is basically exactly the same as loading, except - instead of initializing the decoder, we seek to a frame. - */ - ma_decoder_seek_to_pcm_frame(&pDataStream->decoder, pJob->data.resourceManager.seekDataStream.frameIndex); - - /* After seeking we'll need to reload the pages. */ - ma_resource_manager_data_stream_fill_pages(pDataStream); - - /* We need to let the public API know that we're done seeking. */ - ma_atomic_fetch_sub_32(&pDataStream->seekCounter, 1); - -done: - ma_atomic_fetch_add_32(&pDataStream->executionPointer, 1); - return result; -} - -MA_API ma_result ma_resource_manager_process_job(ma_resource_manager* pResourceManager, ma_job* pJob) -{ - if (pResourceManager == NULL || pJob == NULL) { - return MA_INVALID_ARGS; - } - - return ma_job_process(pJob); -} - -MA_API ma_result ma_resource_manager_process_next_job(ma_resource_manager* pResourceManager) -{ - ma_result result; - ma_job job; - - if (pResourceManager == NULL) { - return MA_INVALID_ARGS; - } - - /* This will return MA_CANCELLED if the next job is a quit job. */ - result = ma_resource_manager_next_job(pResourceManager, &job); - if (result != MA_SUCCESS) { - return result; - } - - return ma_job_process(&job); -} -#else -/* We'll get here if the resource manager is being excluded from the build. We need to define the job processing callbacks as no-ops. */ -static ma_result ma_job_process__resource_manager__load_data_buffer_node(ma_job* pJob) { return ma_job_process__noop(pJob); } -static ma_result ma_job_process__resource_manager__free_data_buffer_node(ma_job* pJob) { return ma_job_process__noop(pJob); } -static ma_result ma_job_process__resource_manager__page_data_buffer_node(ma_job* pJob) { return ma_job_process__noop(pJob); } -static ma_result ma_job_process__resource_manager__load_data_buffer(ma_job* pJob) { return ma_job_process__noop(pJob); } -static ma_result ma_job_process__resource_manager__free_data_buffer(ma_job* pJob) { return ma_job_process__noop(pJob); } -static ma_result ma_job_process__resource_manager__load_data_stream(ma_job* pJob) { return ma_job_process__noop(pJob); } -static ma_result ma_job_process__resource_manager__free_data_stream(ma_job* pJob) { return ma_job_process__noop(pJob); } -static ma_result ma_job_process__resource_manager__page_data_stream(ma_job* pJob) { return ma_job_process__noop(pJob); } -static ma_result ma_job_process__resource_manager__seek_data_stream(ma_job* pJob) { return ma_job_process__noop(pJob); } -#endif /* MA_NO_RESOURCE_MANAGER */ - - -#ifndef MA_NO_NODE_GRAPH - -static ma_stack* ma_stack_init(size_t sizeInBytes, const ma_allocation_callbacks* pAllocationCallbacks) -{ - ma_stack* pStack; - - if (sizeInBytes == 0) { - return NULL; - } - - pStack = (ma_stack*)ma_malloc(sizeof(*pStack) - sizeof(pStack->_data) + sizeInBytes, pAllocationCallbacks); - if (pStack == NULL) { - return NULL; - } - - pStack->offset = 0; - pStack->sizeInBytes = sizeInBytes; - - return pStack; -} - -static void ma_stack_uninit(ma_stack* pStack, const ma_allocation_callbacks* pAllocationCallbacks) -{ - if (pStack == NULL) { - return; - } - - ma_free(pStack, pAllocationCallbacks); -} - -static void* ma_stack_alloc(ma_stack* pStack, size_t sz) -{ - /* The size of the allocation is stored in the memory directly before the pointer. This needs to include padding to keep it aligned to ma_uintptr */ - void* p = (void*)((char*)pStack->_data + pStack->offset); - size_t* pSize = (size_t*)p; - - sz = (sz + (sizeof(ma_uintptr) - 1)) & ~(sizeof(ma_uintptr) - 1); /* Padding. */ - if (pStack->offset + sz + sizeof(size_t) > pStack->sizeInBytes) { - return NULL; /* Out of memory. */ - } - - pStack->offset += sz + sizeof(size_t); - - *pSize = sz; - return (void*)((char*)p + sizeof(size_t)); -} - -static void ma_stack_free(ma_stack* pStack, void* p) -{ - size_t* pSize; - - if (p == NULL) { - return; - } - - pSize = (size_t*)p - 1; - pStack->offset -= *pSize + sizeof(size_t); -} - - - -/* 10ms @ 48K = 480. Must never exceed 65535. */ -#ifndef MA_DEFAULT_NODE_CACHE_CAP_IN_FRAMES_PER_BUS -#define MA_DEFAULT_NODE_CACHE_CAP_IN_FRAMES_PER_BUS 480 -#endif - -#ifndef MA_DEFAULT_PREMIX_STACK_SIZE_PER_CHANNEL -#define MA_DEFAULT_PREMIX_STACK_SIZE_PER_CHANNEL 524288 -#endif - -static ma_result ma_node_read_pcm_frames(ma_node* pNode, ma_uint32 outputBusIndex, float* pFramesOut, ma_uint32 frameCount, ma_uint32* pFramesRead, ma_uint64 globalTime); - -MA_API void ma_debug_fill_pcm_frames_with_sine_wave(float* pFramesOut, ma_uint32 frameCount, ma_format format, ma_uint32 channels, ma_uint32 sampleRate) -{ - #ifndef MA_NO_GENERATION - { - ma_waveform_config waveformConfig; - ma_waveform waveform; - - waveformConfig = ma_waveform_config_init(format, channels, sampleRate, ma_waveform_type_sine, 1.0, 400); - ma_waveform_init(&waveformConfig, &waveform); - ma_waveform_read_pcm_frames(&waveform, pFramesOut, frameCount, NULL); - } - #else - { - (void)pFramesOut; - (void)frameCount; - (void)format; - (void)channels; - (void)sampleRate; - #if defined(MA_DEBUG_OUTPUT) - { - #if _MSC_VER - #pragma message ("ma_debug_fill_pcm_frames_with_sine_wave() will do nothing because MA_NO_GENERATION is enabled.") - #endif - } - #endif - } - #endif -} - - - -MA_API ma_node_graph_config ma_node_graph_config_init(ma_uint32 channels) -{ - ma_node_graph_config config; - - MA_ZERO_OBJECT(&config); - config.channels = channels; - config.processingSizeInFrames = 0; - - return config; -} - - -static void ma_node_graph_set_is_reading(ma_node_graph* pNodeGraph, ma_bool32 isReading) -{ - MA_ASSERT(pNodeGraph != NULL); - ma_atomic_exchange_32(&pNodeGraph->isReading, isReading); -} - -#if 0 -static ma_bool32 ma_node_graph_is_reading(ma_node_graph* pNodeGraph) -{ - MA_ASSERT(pNodeGraph != NULL); - return ma_atomic_load_32(&pNodeGraph->isReading); -} -#endif - - -static void ma_node_graph_node_process_pcm_frames(ma_node* pNode, const float** ppFramesIn, ma_uint32* pFrameCountIn, float** ppFramesOut, ma_uint32* pFrameCountOut) -{ - ma_node_graph* pNodeGraph = (ma_node_graph*)pNode; - ma_uint64 framesRead; - - ma_node_graph_read_pcm_frames(pNodeGraph, ppFramesOut[0], *pFrameCountOut, &framesRead); - - *pFrameCountOut = (ma_uint32)framesRead; /* Safe cast. */ - - (void)ppFramesIn; - (void)pFrameCountIn; -} - -static ma_node_vtable g_node_graph_node_vtable = -{ - ma_node_graph_node_process_pcm_frames, - NULL, /* onGetRequiredInputFrameCount */ - 0, /* 0 input buses. */ - 1, /* 1 output bus. */ - 0 /* Flags. */ -}; - -static void ma_node_graph_endpoint_process_pcm_frames(ma_node* pNode, const float** ppFramesIn, ma_uint32* pFrameCountIn, float** ppFramesOut, ma_uint32* pFrameCountOut) -{ - MA_ASSERT(pNode != NULL); - MA_ASSERT(ma_node_get_input_bus_count(pNode) == 1); - MA_ASSERT(ma_node_get_output_bus_count(pNode) == 1); - - /* Input channel count needs to be the same as the output channel count. */ - MA_ASSERT(ma_node_get_input_channels(pNode, 0) == ma_node_get_output_channels(pNode, 0)); - - /* We don't need to do anything here because it's a passthrough. */ - (void)pNode; - (void)ppFramesIn; - (void)pFrameCountIn; - (void)ppFramesOut; - (void)pFrameCountOut; - -#if 0 - /* The data has already been mixed. We just need to move it to the output buffer. */ - if (ppFramesIn != NULL) { - ma_copy_pcm_frames(ppFramesOut[0], ppFramesIn[0], *pFrameCountOut, ma_format_f32, ma_node_get_output_channels(pNode, 0)); - } -#endif -} - -static ma_node_vtable g_node_graph_endpoint_vtable = -{ - ma_node_graph_endpoint_process_pcm_frames, - NULL, /* onGetRequiredInputFrameCount */ - 1, /* 1 input bus. */ - 1, /* 1 output bus. */ - MA_NODE_FLAG_PASSTHROUGH /* Flags. The endpoint is a passthrough. */ -}; - -MA_API ma_result ma_node_graph_init(const ma_node_graph_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_node_graph* pNodeGraph) -{ - ma_result result; - ma_node_config baseConfig; - ma_node_config endpointConfig; - - if (pNodeGraph == NULL) { - return MA_INVALID_ARGS; - } - - MA_ZERO_OBJECT(pNodeGraph); - pNodeGraph->processingSizeInFrames = pConfig->processingSizeInFrames; - - /* Base node so we can use the node graph as a node into another graph. */ - baseConfig = ma_node_config_init(); - baseConfig.vtable = &g_node_graph_node_vtable; - baseConfig.pOutputChannels = &pConfig->channels; - - result = ma_node_init(pNodeGraph, &baseConfig, pAllocationCallbacks, &pNodeGraph->base); - if (result != MA_SUCCESS) { - return result; - } - - - /* Endpoint. */ - endpointConfig = ma_node_config_init(); - endpointConfig.vtable = &g_node_graph_endpoint_vtable; - endpointConfig.pInputChannels = &pConfig->channels; - endpointConfig.pOutputChannels = &pConfig->channels; - - result = ma_node_init(pNodeGraph, &endpointConfig, pAllocationCallbacks, &pNodeGraph->endpoint); - if (result != MA_SUCCESS) { - ma_node_uninit(&pNodeGraph->base, pAllocationCallbacks); - return result; - } - - - /* Processing cache. */ - if (pConfig->processingSizeInFrames > 0) { - pNodeGraph->pProcessingCache = (float*)ma_malloc(pConfig->processingSizeInFrames * pConfig->channels * sizeof(float), pAllocationCallbacks); - if (pNodeGraph->pProcessingCache == NULL) { - ma_node_uninit(&pNodeGraph->endpoint, pAllocationCallbacks); - ma_node_uninit(&pNodeGraph->base, pAllocationCallbacks); - return MA_OUT_OF_MEMORY; - } - } - - - /* - We need a pre-mix stack. The size of this stack is configurable via the config. The default value depends on the channel count. - */ - { - size_t preMixStackSizeInBytes = pConfig->preMixStackSizeInBytes; - if (preMixStackSizeInBytes == 0) { - preMixStackSizeInBytes = pConfig->channels * MA_DEFAULT_PREMIX_STACK_SIZE_PER_CHANNEL; - } - - pNodeGraph->pPreMixStack = ma_stack_init(preMixStackSizeInBytes, pAllocationCallbacks); - if (pNodeGraph->pPreMixStack == NULL) { - ma_node_uninit(&pNodeGraph->endpoint, pAllocationCallbacks); - ma_node_uninit(&pNodeGraph->base, pAllocationCallbacks); - if (pNodeGraph->pProcessingCache != NULL) { - ma_free(pNodeGraph->pProcessingCache, pAllocationCallbacks); - } - - return MA_OUT_OF_MEMORY; - } - } - - - return MA_SUCCESS; -} - -MA_API void ma_node_graph_uninit(ma_node_graph* pNodeGraph, const ma_allocation_callbacks* pAllocationCallbacks) -{ - if (pNodeGraph == NULL) { - return; - } - - ma_node_uninit(&pNodeGraph->endpoint, pAllocationCallbacks); - ma_node_uninit(&pNodeGraph->base, pAllocationCallbacks); - - if (pNodeGraph->pProcessingCache != NULL) { - ma_free(pNodeGraph->pProcessingCache, pAllocationCallbacks); - pNodeGraph->pProcessingCache = NULL; - } - - if (pNodeGraph->pPreMixStack != NULL) { - ma_stack_uninit(pNodeGraph->pPreMixStack, pAllocationCallbacks); - pNodeGraph->pPreMixStack = NULL; - } -} - -MA_API ma_node* ma_node_graph_get_endpoint(ma_node_graph* pNodeGraph) -{ - if (pNodeGraph == NULL) { - return NULL; - } - - return &pNodeGraph->endpoint; -} - -MA_API ma_result ma_node_graph_read_pcm_frames(ma_node_graph* pNodeGraph, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead) -{ - ma_result result = MA_SUCCESS; - ma_uint64 totalFramesRead; - ma_uint32 channels; - - if (pFramesRead != NULL) { - *pFramesRead = 0; /* Safety. */ - } - - if (pNodeGraph == NULL) { - return MA_INVALID_ARGS; - } - - channels = ma_node_get_output_channels(&pNodeGraph->endpoint, 0); - - - /* We'll be nice and try to do a full read of all frameCount frames. */ - totalFramesRead = 0; - while (totalFramesRead < frameCount) { - ma_uint32 framesJustRead; - ma_uint64 framesToRead; - float* pRunningFramesOut; - - framesToRead = frameCount - totalFramesRead; - if (framesToRead > 0xFFFFFFFF) { - framesToRead = 0xFFFFFFFF; - } - - pRunningFramesOut = (float*)ma_offset_pcm_frames_ptr(pFramesOut, totalFramesRead, ma_format_f32, channels); - - /* If there's anything in the cache, consume that first. */ - if (pNodeGraph->processingCacheFramesRemaining > 0) { - ma_uint32 framesToReadFromCache; - - framesToReadFromCache = (ma_uint32)framesToRead; - if (framesToReadFromCache > pNodeGraph->processingCacheFramesRemaining) { - framesToReadFromCache = pNodeGraph->processingCacheFramesRemaining; - } - - MA_COPY_MEMORY(pRunningFramesOut, pNodeGraph->pProcessingCache, framesToReadFromCache * channels * sizeof(float)); - MA_MOVE_MEMORY(pNodeGraph->pProcessingCache, pNodeGraph->pProcessingCache + (framesToReadFromCache * channels), (pNodeGraph->processingCacheFramesRemaining - framesToReadFromCache) * channels * sizeof(float)); - pNodeGraph->processingCacheFramesRemaining -= framesToReadFromCache; - - totalFramesRead += framesToReadFromCache; - continue; - } else { - /* - If processingSizeInFrames is non-zero, we need to make sure we always read in chunks of that size. If the frame count is less than - that, we need to read into the cache and then continue on. - */ - float* pReadDst = pRunningFramesOut; - - if (pNodeGraph->processingSizeInFrames > 0) { - if (framesToRead < pNodeGraph->processingSizeInFrames) { - pReadDst = pNodeGraph->pProcessingCache; /* We need to read into the cache because otherwise we'll overflow the output buffer. */ - } - - framesToRead = pNodeGraph->processingSizeInFrames; - } - - ma_node_graph_set_is_reading(pNodeGraph, MA_TRUE); - { - result = ma_node_read_pcm_frames(&pNodeGraph->endpoint, 0, pReadDst, (ma_uint32)framesToRead, &framesJustRead, ma_node_get_time(&pNodeGraph->endpoint)); - } - ma_node_graph_set_is_reading(pNodeGraph, MA_FALSE); - - /* - Do not increment the total frames read counter if we read into the cache. We use this to determine how many frames have - been written to the final output buffer. - */ - if (pReadDst == pNodeGraph->pProcessingCache) { - /* We read into the cache. */ - pNodeGraph->processingCacheFramesRemaining = framesJustRead; - } else { - /* We read straight into the output buffer. */ - totalFramesRead += framesJustRead; - } - - if (result != MA_SUCCESS) { - break; - } - - /* Abort if we weren't able to read any frames or else we risk getting stuck in a loop. */ - if (framesJustRead == 0) { - break; - } - } - } - - /* Let's go ahead and silence any leftover frames just for some added safety to ensure the caller doesn't try emitting garbage out of the speakers. */ - if (totalFramesRead < frameCount) { - ma_silence_pcm_frames(ma_offset_pcm_frames_ptr(pFramesOut, totalFramesRead, ma_format_f32, channels), (frameCount - totalFramesRead), ma_format_f32, channels); - } - - if (pFramesRead != NULL) { - *pFramesRead = totalFramesRead; - } - - return result; -} - -MA_API ma_uint32 ma_node_graph_get_channels(const ma_node_graph* pNodeGraph) -{ - if (pNodeGraph == NULL) { - return 0; - } - - return ma_node_get_output_channels(&pNodeGraph->endpoint, 0); -} - -MA_API ma_uint64 ma_node_graph_get_time(const ma_node_graph* pNodeGraph) -{ - if (pNodeGraph == NULL) { - return 0; - } - - return ma_node_get_time(&pNodeGraph->endpoint); /* Global time is just the local time of the endpoint. */ -} - -MA_API ma_result ma_node_graph_set_time(ma_node_graph* pNodeGraph, ma_uint64 globalTime) -{ - if (pNodeGraph == NULL) { - return MA_INVALID_ARGS; - } - - return ma_node_set_time(&pNodeGraph->endpoint, globalTime); /* Global time is just the local time of the endpoint. */ -} - - -#define MA_NODE_OUTPUT_BUS_FLAG_HAS_READ 0x01 /* Whether or not this bus ready to read more data. Only used on nodes with multiple output buses. */ - -static ma_result ma_node_output_bus_init(ma_node* pNode, ma_uint32 outputBusIndex, ma_uint32 channels, ma_node_output_bus* pOutputBus) -{ - MA_ASSERT(pOutputBus != NULL); - MA_ASSERT(outputBusIndex < MA_MAX_NODE_BUS_COUNT); - MA_ASSERT(outputBusIndex < ma_node_get_output_bus_count(pNode)); - MA_ASSERT(channels < 256); - - MA_ZERO_OBJECT(pOutputBus); - - if (channels == 0) { - return MA_INVALID_ARGS; - } - - pOutputBus->pNode = pNode; - pOutputBus->outputBusIndex = (ma_uint8)outputBusIndex; - pOutputBus->channels = (ma_uint8)channels; - pOutputBus->flags = MA_NODE_OUTPUT_BUS_FLAG_HAS_READ; /* <-- Important that this flag is set by default. */ - pOutputBus->volume = 1; - - return MA_SUCCESS; -} - -static void ma_node_output_bus_lock(ma_node_output_bus* pOutputBus) -{ - ma_spinlock_lock(&pOutputBus->lock); -} - -static void ma_node_output_bus_unlock(ma_node_output_bus* pOutputBus) -{ - ma_spinlock_unlock(&pOutputBus->lock); -} - - -static ma_uint32 ma_node_output_bus_get_channels(const ma_node_output_bus* pOutputBus) -{ - return pOutputBus->channels; -} - - -static void ma_node_output_bus_set_has_read(ma_node_output_bus* pOutputBus, ma_bool32 hasRead) -{ - if (hasRead) { - ma_atomic_fetch_or_32(&pOutputBus->flags, MA_NODE_OUTPUT_BUS_FLAG_HAS_READ); - } else { - ma_atomic_fetch_and_32(&pOutputBus->flags, (ma_uint32)~MA_NODE_OUTPUT_BUS_FLAG_HAS_READ); - } -} - -static ma_bool32 ma_node_output_bus_has_read(ma_node_output_bus* pOutputBus) -{ - return (ma_atomic_load_32(&pOutputBus->flags) & MA_NODE_OUTPUT_BUS_FLAG_HAS_READ) != 0; -} - - -static void ma_node_output_bus_set_is_attached(ma_node_output_bus* pOutputBus, ma_bool32 isAttached) -{ - ma_atomic_exchange_32(&pOutputBus->isAttached, isAttached); -} - -static ma_bool32 ma_node_output_bus_is_attached(ma_node_output_bus* pOutputBus) -{ - return ma_atomic_load_32(&pOutputBus->isAttached); -} - - -static ma_result ma_node_output_bus_set_volume(ma_node_output_bus* pOutputBus, float volume) -{ - MA_ASSERT(pOutputBus != NULL); - - if (volume < 0.0f) { - volume = 0.0f; - } - - ma_atomic_exchange_f32(&pOutputBus->volume, volume); - - return MA_SUCCESS; -} - -static float ma_node_output_bus_get_volume(const ma_node_output_bus* pOutputBus) -{ - return ma_atomic_load_f32((float*)&pOutputBus->volume); -} - - -static ma_result ma_node_input_bus_init(ma_uint32 channels, ma_node_input_bus* pInputBus) -{ - MA_ASSERT(pInputBus != NULL); - MA_ASSERT(channels < 256); - - MA_ZERO_OBJECT(pInputBus); - - if (channels == 0) { - return MA_INVALID_ARGS; - } - - pInputBus->channels = (ma_uint8)channels; - - return MA_SUCCESS; -} - -static void ma_node_input_bus_lock(ma_node_input_bus* pInputBus) -{ - MA_ASSERT(pInputBus != NULL); - - ma_spinlock_lock(&pInputBus->lock); -} - -static void ma_node_input_bus_unlock(ma_node_input_bus* pInputBus) -{ - MA_ASSERT(pInputBus != NULL); - - ma_spinlock_unlock(&pInputBus->lock); -} - - -static void ma_node_input_bus_next_begin(ma_node_input_bus* pInputBus) -{ - ma_atomic_fetch_add_32(&pInputBus->nextCounter, 1); -} - -static void ma_node_input_bus_next_end(ma_node_input_bus* pInputBus) -{ - ma_atomic_fetch_sub_32(&pInputBus->nextCounter, 1); -} - -static ma_uint32 ma_node_input_bus_get_next_counter(ma_node_input_bus* pInputBus) -{ - return ma_atomic_load_32(&pInputBus->nextCounter); -} - - -static ma_uint32 ma_node_input_bus_get_channels(const ma_node_input_bus* pInputBus) -{ - return pInputBus->channels; -} - - -static void ma_node_input_bus_detach__no_output_bus_lock(ma_node_input_bus* pInputBus, ma_node_output_bus* pOutputBus) -{ - MA_ASSERT(pInputBus != NULL); - MA_ASSERT(pOutputBus != NULL); - - /* - Mark the output bus as detached first. This will prevent future iterations on the audio thread - from iterating this output bus. - */ - ma_node_output_bus_set_is_attached(pOutputBus, MA_FALSE); - - /* - We cannot use the output bus lock here since it'll be getting used at a higher level, but we do - still need to use the input bus lock since we'll be updating pointers on two different output - buses. The same rules apply here as the attaching case. Although we're using a lock here, we're - *not* using a lock when iterating over the list in the audio thread. We therefore need to craft - this in a way such that the iteration on the audio thread doesn't break. - - The first thing to do is swap out the "next" pointer of the previous output bus with the - new "next" output bus. This is the operation that matters for iteration on the audio thread. - After that, the previous pointer on the new "next" pointer needs to be updated, after which - point the linked list will be in a good state. - */ - ma_node_input_bus_lock(pInputBus); - { - ma_node_output_bus* pOldPrev = (ma_node_output_bus*)ma_atomic_load_ptr(&pOutputBus->pPrev); - ma_node_output_bus* pOldNext = (ma_node_output_bus*)ma_atomic_load_ptr(&pOutputBus->pNext); - - if (pOldPrev != NULL) { - ma_atomic_exchange_ptr(&pOldPrev->pNext, pOldNext); /* <-- This is where the output bus is detached from the list. */ - } - if (pOldNext != NULL) { - ma_atomic_exchange_ptr(&pOldNext->pPrev, pOldPrev); /* <-- This is required for detachment. */ - } - } - ma_node_input_bus_unlock(pInputBus); - - /* At this point the output bus is detached and the linked list is completely unaware of it. Reset some data for safety. */ - ma_atomic_exchange_ptr(&pOutputBus->pNext, NULL); /* Using atomic exchanges here, mainly for the benefit of analysis tools which don't always recognize spinlocks. */ - ma_atomic_exchange_ptr(&pOutputBus->pPrev, NULL); /* As above. */ - pOutputBus->pInputNode = NULL; - pOutputBus->inputNodeInputBusIndex = 0; - - - /* - For thread-safety reasons, we don't want to be returning from this straight away. We need to - wait for the audio thread to finish with the output bus. There's two things we need to wait - for. The first is the part that selects the next output bus in the list, and the other is the - part that reads from the output bus. Basically all we're doing is waiting for the input bus - to stop referencing the output bus. - - We're doing this part last because we want the section above to run while the audio thread - is finishing up with the output bus, just for efficiency reasons. We marked the output bus as - detached right at the top of this function which is going to prevent the audio thread from - iterating the output bus again. - */ - - /* Part 1: Wait for the current iteration to complete. */ - while (ma_node_input_bus_get_next_counter(pInputBus) > 0) { - ma_yield(); - } - - /* Part 2: Wait for any reads to complete. */ - while (ma_atomic_load_32(&pOutputBus->refCount) > 0) { - ma_yield(); - } - - /* - At this point we're done detaching and we can be guaranteed that the audio thread is not going - to attempt to reference this output bus again (until attached again). - */ -} - -#if 0 /* Not used at the moment, but leaving here in case I need it later. */ -static void ma_node_input_bus_detach(ma_node_input_bus* pInputBus, ma_node_output_bus* pOutputBus) -{ - MA_ASSERT(pInputBus != NULL); - MA_ASSERT(pOutputBus != NULL); - - ma_node_output_bus_lock(pOutputBus); - { - ma_node_input_bus_detach__no_output_bus_lock(pInputBus, pOutputBus); - } - ma_node_output_bus_unlock(pOutputBus); -} -#endif - -static void ma_node_input_bus_attach(ma_node_input_bus* pInputBus, ma_node_output_bus* pOutputBus, ma_node* pNewInputNode, ma_uint32 inputNodeInputBusIndex) -{ - MA_ASSERT(pInputBus != NULL); - MA_ASSERT(pOutputBus != NULL); - - ma_node_output_bus_lock(pOutputBus); - { - ma_node_output_bus* pOldInputNode = (ma_node_output_bus*)ma_atomic_load_ptr(&pOutputBus->pInputNode); - - /* Detach from any existing attachment first if necessary. */ - if (pOldInputNode != NULL) { - ma_node_input_bus_detach__no_output_bus_lock(pInputBus, pOutputBus); - } - - /* - At this point we can be sure the output bus is not attached to anything. The linked list in the - old input bus has been updated so that pOutputBus will not get iterated again. - */ - pOutputBus->pInputNode = pNewInputNode; /* No need for an atomic assignment here because modification of this variable always happens within a lock. */ - pOutputBus->inputNodeInputBusIndex = (ma_uint8)inputNodeInputBusIndex; - - /* - Now we need to attach the output bus to the linked list. This involves updating two pointers on - two different output buses so I'm going to go ahead and keep this simple and just use a lock. - There are ways to do this without a lock, but it's just too hard to maintain for its value. - - Although we're locking here, it's important to remember that we're *not* locking when iterating - and reading audio data since that'll be running on the audio thread. As a result we need to be - careful how we craft this so that we don't break iteration. What we're going to do is always - attach the new item so that it becomes the first item in the list. That way, as we're iterating - we won't break any links in the list and iteration will continue safely. The detaching case will - also be crafted in a way as to not break list iteration. It's important to remember to use - atomic exchanges here since no locking is happening on the audio thread during iteration. - */ - ma_node_input_bus_lock(pInputBus); - { - ma_node_output_bus* pNewPrev = &pInputBus->head; - ma_node_output_bus* pNewNext = (ma_node_output_bus*)ma_atomic_load_ptr(&pInputBus->head.pNext); - - /* Update the local output bus. */ - ma_atomic_exchange_ptr(&pOutputBus->pPrev, pNewPrev); - ma_atomic_exchange_ptr(&pOutputBus->pNext, pNewNext); - - /* Update the other output buses to point back to the local output bus. */ - ma_atomic_exchange_ptr(&pInputBus->head.pNext, pOutputBus); /* <-- This is where the output bus is actually attached to the input bus. */ - - /* Do the previous pointer last. This is only used for detachment. */ - if (pNewNext != NULL) { - ma_atomic_exchange_ptr(&pNewNext->pPrev, pOutputBus); - } - } - ma_node_input_bus_unlock(pInputBus); - - /* - Mark the node as attached last. This is used to controlling whether or the output bus will be - iterated on the audio thread. Mainly required for detachment purposes. - */ - ma_node_output_bus_set_is_attached(pOutputBus, MA_TRUE); - } - ma_node_output_bus_unlock(pOutputBus); -} - -static ma_node_output_bus* ma_node_input_bus_next(ma_node_input_bus* pInputBus, ma_node_output_bus* pOutputBus) -{ - ma_node_output_bus* pNext; - - MA_ASSERT(pInputBus != NULL); - - if (pOutputBus == NULL) { - return NULL; - } - - ma_node_input_bus_next_begin(pInputBus); - { - pNext = pOutputBus; - for (;;) { - pNext = (ma_node_output_bus*)ma_atomic_load_ptr(&pNext->pNext); - if (pNext == NULL) { - break; /* Reached the end. */ - } - - if (ma_node_output_bus_is_attached(pNext) == MA_FALSE) { - continue; /* The node is not attached. Keep checking. */ - } - - /* The next node has been selected. */ - break; - } - - /* We need to increment the reference count of the selected node. */ - if (pNext != NULL) { - ma_atomic_fetch_add_32(&pNext->refCount, 1); - } - - /* The previous node is no longer being referenced. */ - ma_atomic_fetch_sub_32(&pOutputBus->refCount, 1); - } - ma_node_input_bus_next_end(pInputBus); - - return pNext; -} - -static ma_node_output_bus* ma_node_input_bus_first(ma_node_input_bus* pInputBus) -{ - return ma_node_input_bus_next(pInputBus, &pInputBus->head); -} - - - -static ma_result ma_node_input_bus_read_pcm_frames(ma_node* pInputNode, ma_node_input_bus* pInputBus, float* pFramesOut, ma_uint32 frameCount, ma_uint32* pFramesRead, ma_uint64 globalTime) -{ - ma_result result = MA_SUCCESS; - ma_node_output_bus* pOutputBus; - ma_node_output_bus* pFirst; - ma_uint32 inputChannels; - ma_bool32 doesOutputBufferHaveContent = MA_FALSE; - - /* - This will be called from the audio thread which means we can't be doing any locking. Basically, - this function will not perform any locking, whereas attaching and detaching will, but crafted in - such a way that we don't need to perform any locking here. The important thing to remember is - to always iterate in a forward direction. - - In order to process any data we need to first read from all input buses. That's where this - function comes in. This iterates over each of the attachments and accumulates/mixes them. We - also convert the channels to the nodes output channel count before mixing. We want to do this - channel conversion so that the caller of this function can invoke the processing callback - without having to do it themselves. - - When we iterate over each of the attachments on the input bus, we need to read as much data as - we can from each of them so that we don't end up with holes between each of the attachments. To - do this, we need to read from each attachment in a loop and read as many frames as we can, up - to `frameCount`. - */ - MA_ASSERT(pInputNode != NULL); - MA_ASSERT(pFramesRead != NULL); /* pFramesRead is critical and must always be specified. On input it's undefined and on output it'll be set to the number of frames actually read. */ - - *pFramesRead = 0; /* Safety. */ - - inputChannels = ma_node_input_bus_get_channels(pInputBus); - - /* - We need to be careful with how we call ma_node_input_bus_first() and ma_node_input_bus_next(). They - are both critical to our lock-free thread-safety system. We can only call ma_node_input_bus_first() - once per iteration, however we have an optimization to checks whether or not it's the first item in - the list. We therefore need to store a pointer to the first item rather than repeatedly calling - ma_node_input_bus_first(). It's safe to keep hold of this pointer, so long as we don't dereference it - after calling ma_node_input_bus_next(), which we won't be. - */ - pFirst = ma_node_input_bus_first(pInputBus); - if (pFirst == NULL) { - return MA_SUCCESS; /* No attachments. Read nothing. */ - } - - for (pOutputBus = pFirst; pOutputBus != NULL; pOutputBus = ma_node_input_bus_next(pInputBus, pOutputBus)) { - ma_uint32 framesProcessed = 0; - ma_bool32 isSilentOutput = MA_FALSE; - - MA_ASSERT(pOutputBus->pNode != NULL); - MA_ASSERT(((ma_node_base*)pOutputBus->pNode)->vtable != NULL); - - isSilentOutput = (((ma_node_base*)pOutputBus->pNode)->vtable->flags & MA_NODE_FLAG_SILENT_OUTPUT) != 0; - - if (pFramesOut != NULL) { - /* Read. */ - while (framesProcessed < frameCount) { - float* pRunningFramesOut; - ma_uint32 framesToRead; - ma_uint32 framesJustRead = 0; - - framesToRead = frameCount - framesProcessed; - pRunningFramesOut = ma_offset_pcm_frames_ptr_f32(pFramesOut, framesProcessed, inputChannels); - - if (doesOutputBufferHaveContent == MA_FALSE) { - /* Fast path. First attachment. We just read straight into the output buffer (no mixing required). */ - result = ma_node_read_pcm_frames(pOutputBus->pNode, pOutputBus->outputBusIndex, pRunningFramesOut, framesToRead, &framesJustRead, globalTime + framesProcessed); - } else { - /* Slow path. Not the first attachment. Mixing required. */ - ma_uint32 preMixBufferCapInFrames = ((ma_node_base*)pInputNode)->cachedDataCapInFramesPerBus; - float* pPreMixBuffer = (float*)ma_stack_alloc(((ma_node_base*)pInputNode)->pNodeGraph->pPreMixStack, preMixBufferCapInFrames * inputChannels * sizeof(float)); - - if (pPreMixBuffer == NULL) { - /* - If you're hitting this assert it means you've got an unusually deep chain of nodes, you've got an excessively large processing - size, or you have a combination of both, and as a result have run out of stack space. You can increase this using the - preMixStackSizeInBytes variable in ma_node_graph_config. If you're using ma_engine, you can do it via the preMixStackSizeInBytes - variable in ma_engine_config. It defaults to 512KB per output channel. - */ - MA_ASSERT(MA_FALSE); - } else { - if (framesToRead > preMixBufferCapInFrames) { - framesToRead = preMixBufferCapInFrames; - } - - result = ma_node_read_pcm_frames(pOutputBus->pNode, pOutputBus->outputBusIndex, pPreMixBuffer, framesToRead, &framesJustRead, globalTime + framesProcessed); - if (result == MA_SUCCESS || result == MA_AT_END) { - if (isSilentOutput == MA_FALSE) { /* Don't mix if the node outputs silence. */ - ma_mix_pcm_frames_f32(pRunningFramesOut, pPreMixBuffer, framesJustRead, inputChannels, /*volume*/1); - } - } - - /* The pre-mix buffer is no longer required. */ - ma_stack_free(((ma_node_base*)pInputNode)->pNodeGraph->pPreMixStack, pPreMixBuffer); - pPreMixBuffer = NULL; - } - } - - framesProcessed += framesJustRead; - - /* If we reached the end or otherwise failed to read any data we need to finish up with this output node. */ - if (result != MA_SUCCESS) { - break; - } - - /* If we didn't read anything, abort so we don't get stuck in a loop. */ - if (framesJustRead == 0) { - break; - } - } - - /* If it's the first attachment we didn't do any mixing. Any leftover samples need to be silenced. */ - if (pOutputBus == pFirst && framesProcessed < frameCount) { - ma_silence_pcm_frames(ma_offset_pcm_frames_ptr(pFramesOut, framesProcessed, ma_format_f32, inputChannels), (frameCount - framesProcessed), ma_format_f32, inputChannels); - } - - if (isSilentOutput == MA_FALSE) { - doesOutputBufferHaveContent = MA_TRUE; - } - } else { - /* Seek. */ - ma_node_read_pcm_frames(pOutputBus->pNode, pOutputBus->outputBusIndex, NULL, frameCount, &framesProcessed, globalTime); - } - } - - /* If we didn't output anything, output silence. */ - if (doesOutputBufferHaveContent == MA_FALSE && pFramesOut != NULL) { - ma_silence_pcm_frames(pFramesOut, frameCount, ma_format_f32, inputChannels); - } - - /* In this path we always "process" the entire amount. */ - *pFramesRead = frameCount; - - return result; -} - - -MA_API ma_node_config ma_node_config_init(void) -{ - ma_node_config config; - - MA_ZERO_OBJECT(&config); - config.initialState = ma_node_state_started; /* Nodes are started by default. */ - config.inputBusCount = MA_NODE_BUS_COUNT_UNKNOWN; - config.outputBusCount = MA_NODE_BUS_COUNT_UNKNOWN; - - return config; -} - -static ma_uint16 ma_node_config_get_cache_size_in_frames(const ma_node_config* pConfig, const ma_node_graph* pNodeGraph) -{ - ma_uint32 cacheSizeInFrames; - - (void)pConfig; - - if (pNodeGraph->processingSizeInFrames > 0) { - cacheSizeInFrames = pNodeGraph->processingSizeInFrames; - } else { - cacheSizeInFrames = MA_DEFAULT_NODE_CACHE_CAP_IN_FRAMES_PER_BUS; - } - - if (cacheSizeInFrames > 0xFFFF) { - cacheSizeInFrames = 0xFFFF; - } - - return (ma_uint16)cacheSizeInFrames; -} - - - -static ma_result ma_node_detach_full(ma_node* pNode); - -static float* ma_node_get_cached_input_ptr(ma_node* pNode, ma_uint32 inputBusIndex) -{ - ma_node_base* pNodeBase = (ma_node_base*)pNode; - ma_uint32 iInputBus; - float* pBasePtr; - - MA_ASSERT(pNodeBase != NULL); - - /* Input data is stored at the front of the buffer. */ - pBasePtr = pNodeBase->pCachedData; - for (iInputBus = 0; iInputBus < inputBusIndex; iInputBus += 1) { - pBasePtr += pNodeBase->cachedDataCapInFramesPerBus * ma_node_input_bus_get_channels(&pNodeBase->pInputBuses[iInputBus]); - } - - return pBasePtr; -} - -static float* ma_node_get_cached_output_ptr(ma_node* pNode, ma_uint32 outputBusIndex) -{ - ma_node_base* pNodeBase = (ma_node_base*)pNode; - ma_uint32 iInputBus; - ma_uint32 iOutputBus; - float* pBasePtr; - - MA_ASSERT(pNodeBase != NULL); - - /* Cached output data starts after the input data. */ - pBasePtr = pNodeBase->pCachedData; - for (iInputBus = 0; iInputBus < ma_node_get_input_bus_count(pNodeBase); iInputBus += 1) { - pBasePtr += pNodeBase->cachedDataCapInFramesPerBus * ma_node_input_bus_get_channels(&pNodeBase->pInputBuses[iInputBus]); - } - - for (iOutputBus = 0; iOutputBus < outputBusIndex; iOutputBus += 1) { - pBasePtr += pNodeBase->cachedDataCapInFramesPerBus * ma_node_output_bus_get_channels(&pNodeBase->pOutputBuses[iOutputBus]); - } - - return pBasePtr; -} - - -typedef struct -{ - size_t sizeInBytes; - size_t inputBusOffset; - size_t outputBusOffset; - size_t cachedDataOffset; - ma_uint32 inputBusCount; /* So it doesn't have to be calculated twice. */ - ma_uint32 outputBusCount; /* So it doesn't have to be calculated twice. */ -} ma_node_heap_layout; - -static ma_result ma_node_translate_bus_counts(const ma_node_config* pConfig, ma_uint32* pInputBusCount, ma_uint32* pOutputBusCount) -{ - ma_uint32 inputBusCount; - ma_uint32 outputBusCount; - - MA_ASSERT(pConfig != NULL); - MA_ASSERT(pInputBusCount != NULL); - MA_ASSERT(pOutputBusCount != NULL); - - /* Bus counts are determined by the vtable, unless they're set to `MA_NODE_BUS_COUNT_UNKNWON`, in which case they're taken from the config. */ - if (pConfig->vtable->inputBusCount == MA_NODE_BUS_COUNT_UNKNOWN) { - inputBusCount = pConfig->inputBusCount; - } else { - inputBusCount = pConfig->vtable->inputBusCount; - - if (pConfig->inputBusCount != MA_NODE_BUS_COUNT_UNKNOWN && pConfig->inputBusCount != pConfig->vtable->inputBusCount) { - return MA_INVALID_ARGS; /* Invalid configuration. You must not specify a conflicting bus count between the node's config and the vtable. */ - } - } - - if (pConfig->vtable->outputBusCount == MA_NODE_BUS_COUNT_UNKNOWN) { - outputBusCount = pConfig->outputBusCount; - } else { - outputBusCount = pConfig->vtable->outputBusCount; - - if (pConfig->outputBusCount != MA_NODE_BUS_COUNT_UNKNOWN && pConfig->outputBusCount != pConfig->vtable->outputBusCount) { - return MA_INVALID_ARGS; /* Invalid configuration. You must not specify a conflicting bus count between the node's config and the vtable. */ - } - } - - /* Bus counts must be within limits. */ - if (inputBusCount > MA_MAX_NODE_BUS_COUNT || outputBusCount > MA_MAX_NODE_BUS_COUNT) { - return MA_INVALID_ARGS; - } - - - /* We must have channel counts for each bus. */ - if ((inputBusCount > 0 && pConfig->pInputChannels == NULL) || (outputBusCount > 0 && pConfig->pOutputChannels == NULL)) { - return MA_INVALID_ARGS; /* You must specify channel counts for each input and output bus. */ - } - - - /* Some special rules for passthrough nodes. */ - if ((pConfig->vtable->flags & MA_NODE_FLAG_PASSTHROUGH) != 0) { - if ((pConfig->vtable->inputBusCount != 0 && pConfig->vtable->inputBusCount != 1) || pConfig->vtable->outputBusCount != 1) { - return MA_INVALID_ARGS; /* Passthrough nodes must have exactly 1 output bus and either 0 or 1 input bus. */ - } - - if (pConfig->pInputChannels[0] != pConfig->pOutputChannels[0]) { - return MA_INVALID_ARGS; /* Passthrough nodes must have the same number of channels between input and output nodes. */ - } - } - - - *pInputBusCount = inputBusCount; - *pOutputBusCount = outputBusCount; - - return MA_SUCCESS; -} - -static ma_result ma_node_get_heap_layout(ma_node_graph* pNodeGraph, const ma_node_config* pConfig, ma_node_heap_layout* pHeapLayout) -{ - ma_result result; - ma_uint32 inputBusCount; - ma_uint32 outputBusCount; - - MA_ASSERT(pHeapLayout != NULL); - - MA_ZERO_OBJECT(pHeapLayout); - - if (pConfig == NULL || pConfig->vtable == NULL || pConfig->vtable->onProcess == NULL) { - return MA_INVALID_ARGS; - } - - result = ma_node_translate_bus_counts(pConfig, &inputBusCount, &outputBusCount); - if (result != MA_SUCCESS) { - return result; - } - - pHeapLayout->sizeInBytes = 0; - - /* Input buses. */ - if (inputBusCount > MA_MAX_NODE_LOCAL_BUS_COUNT) { - pHeapLayout->inputBusOffset = pHeapLayout->sizeInBytes; - pHeapLayout->sizeInBytes += ma_align_64(sizeof(ma_node_input_bus) * inputBusCount); - } else { - pHeapLayout->inputBusOffset = MA_SIZE_MAX; /* MA_SIZE_MAX indicates that no heap allocation is required for the input bus. */ - } - - /* Output buses. */ - if (outputBusCount > MA_MAX_NODE_LOCAL_BUS_COUNT) { - pHeapLayout->outputBusOffset = pHeapLayout->sizeInBytes; - pHeapLayout->sizeInBytes += ma_align_64(sizeof(ma_node_output_bus) * outputBusCount); - } else { - pHeapLayout->outputBusOffset = MA_SIZE_MAX; - } - - /* - Cached audio data. - - We need to allocate memory for caching both input and output data. We have an optimization - where no caching is necessary for specific conditions: - - - The node has 0 inputs and 1 output. - - When a node meets the above conditions, no cache is allocated. - - The size choice for this buffer is a little bit finicky. We don't want to be too wasteful by - allocating too much, but at the same time we want it be large enough so that enough frames can - be processed for each call to ma_node_read_pcm_frames() so that it keeps things efficient. For - now I'm going with 10ms @ 48K which is 480 frames per bus. This is configurable at compile - time. It might also be worth investigating whether or not this can be configured at run time. - */ - if (inputBusCount == 0 && outputBusCount == 1) { - /* Fast path. No cache needed. */ - pHeapLayout->cachedDataOffset = MA_SIZE_MAX; - } else { - /* Slow path. Cache needed. */ - size_t cachedDataSizeInBytes = 0; - ma_uint32 cacheCapInFrames; - ma_uint32 iBus; - - /* The capacity of the cache is based on our callback processing size. */ - cacheCapInFrames = ma_node_config_get_cache_size_in_frames(pConfig, pNodeGraph); - - for (iBus = 0; iBus < inputBusCount; iBus += 1) { - cachedDataSizeInBytes += cacheCapInFrames * ma_get_bytes_per_frame(ma_format_f32, pConfig->pInputChannels[iBus]); - } - - for (iBus = 0; iBus < outputBusCount; iBus += 1) { - cachedDataSizeInBytes += cacheCapInFrames * ma_get_bytes_per_frame(ma_format_f32, pConfig->pOutputChannels[iBus]); - } - - pHeapLayout->cachedDataOffset = pHeapLayout->sizeInBytes; - pHeapLayout->sizeInBytes += ma_align_64(cachedDataSizeInBytes); - } - - - /* - Not technically part of the heap, but we can output the input and output bus counts so we can - avoid a redundant call to ma_node_translate_bus_counts(). - */ - pHeapLayout->inputBusCount = inputBusCount; - pHeapLayout->outputBusCount = outputBusCount; - - /* Make sure allocation size is aligned. */ - pHeapLayout->sizeInBytes = ma_align_64(pHeapLayout->sizeInBytes); - - return MA_SUCCESS; -} - -MA_API ma_result ma_node_get_heap_size(ma_node_graph* pNodeGraph, const ma_node_config* pConfig, size_t* pHeapSizeInBytes) -{ - ma_result result; - ma_node_heap_layout heapLayout; - - if (pHeapSizeInBytes == NULL) { - return MA_INVALID_ARGS; - } - - *pHeapSizeInBytes = 0; - - result = ma_node_get_heap_layout(pNodeGraph, pConfig, &heapLayout); - if (result != MA_SUCCESS) { - return result; - } - - *pHeapSizeInBytes = heapLayout.sizeInBytes; - - return MA_SUCCESS; -} - -MA_API ma_result ma_node_init_preallocated(ma_node_graph* pNodeGraph, const ma_node_config* pConfig, void* pHeap, ma_node* pNode) -{ - ma_node_base* pNodeBase = (ma_node_base*)pNode; - ma_result result; - ma_node_heap_layout heapLayout; - ma_uint32 iInputBus; - ma_uint32 iOutputBus; - - if (pNodeBase == NULL) { - return MA_INVALID_ARGS; - } - - MA_ZERO_OBJECT(pNodeBase); - - result = ma_node_get_heap_layout(pNodeGraph, pConfig, &heapLayout); - if (result != MA_SUCCESS) { - return result; - } - - pNodeBase->_pHeap = pHeap; - MA_ZERO_MEMORY(pHeap, heapLayout.sizeInBytes); - - pNodeBase->pNodeGraph = pNodeGraph; - pNodeBase->vtable = pConfig->vtable; - pNodeBase->state = pConfig->initialState; - pNodeBase->stateTimes[ma_node_state_started] = 0; - pNodeBase->stateTimes[ma_node_state_stopped] = (ma_uint64)(ma_int64)-1; /* Weird casting for VC6 compatibility. */ - pNodeBase->inputBusCount = heapLayout.inputBusCount; - pNodeBase->outputBusCount = heapLayout.outputBusCount; - - if (heapLayout.inputBusOffset != MA_SIZE_MAX) { - pNodeBase->pInputBuses = (ma_node_input_bus*)ma_offset_ptr(pHeap, heapLayout.inputBusOffset); - } else { - pNodeBase->pInputBuses = pNodeBase->_inputBuses; - } - - if (heapLayout.outputBusOffset != MA_SIZE_MAX) { - pNodeBase->pOutputBuses = (ma_node_output_bus*)ma_offset_ptr(pHeap, heapLayout.outputBusOffset); - } else { - pNodeBase->pOutputBuses = pNodeBase->_outputBuses; - } - - if (heapLayout.cachedDataOffset != MA_SIZE_MAX) { - pNodeBase->pCachedData = (float*)ma_offset_ptr(pHeap, heapLayout.cachedDataOffset); - pNodeBase->cachedDataCapInFramesPerBus = ma_node_config_get_cache_size_in_frames(pConfig, pNodeGraph); - } else { - pNodeBase->pCachedData = NULL; - } - - - /* We need to run an initialization step for each input and output bus. */ - for (iInputBus = 0; iInputBus < ma_node_get_input_bus_count(pNodeBase); iInputBus += 1) { - result = ma_node_input_bus_init(pConfig->pInputChannels[iInputBus], &pNodeBase->pInputBuses[iInputBus]); - if (result != MA_SUCCESS) { - return result; - } - } - - for (iOutputBus = 0; iOutputBus < ma_node_get_output_bus_count(pNodeBase); iOutputBus += 1) { - result = ma_node_output_bus_init(pNodeBase, iOutputBus, pConfig->pOutputChannels[iOutputBus], &pNodeBase->pOutputBuses[iOutputBus]); - if (result != MA_SUCCESS) { - return result; - } - } - - - /* The cached data needs to be initialized to silence (or a sine wave tone if we're debugging). */ - if (pNodeBase->pCachedData != NULL) { - ma_uint32 iBus; - - #if 1 /* Toggle this between 0 and 1 to turn debugging on or off. 1 = fill with a sine wave for debugging; 0 = fill with silence. */ - /* For safety we'll go ahead and default the buffer to silence. */ - for (iBus = 0; iBus < ma_node_get_input_bus_count(pNodeBase); iBus += 1) { - ma_silence_pcm_frames(ma_node_get_cached_input_ptr(pNode, iBus), pNodeBase->cachedDataCapInFramesPerBus, ma_format_f32, ma_node_input_bus_get_channels(&pNodeBase->pInputBuses[iBus])); - } - for (iBus = 0; iBus < ma_node_get_output_bus_count(pNodeBase); iBus += 1) { - ma_silence_pcm_frames(ma_node_get_cached_output_ptr(pNode, iBus), pNodeBase->cachedDataCapInFramesPerBus, ma_format_f32, ma_node_output_bus_get_channels(&pNodeBase->pOutputBuses[iBus])); - } - #else - /* For debugging. Default to a sine wave. */ - for (iBus = 0; iBus < ma_node_get_input_bus_count(pNodeBase); iBus += 1) { - ma_debug_fill_pcm_frames_with_sine_wave(ma_node_get_cached_input_ptr(pNode, iBus), pNodeBase->cachedDataCapInFramesPerBus, ma_format_f32, ma_node_input_bus_get_channels(&pNodeBase->pInputBuses[iBus]), 48000); - } - for (iBus = 0; iBus < ma_node_get_output_bus_count(pNodeBase); iBus += 1) { - ma_debug_fill_pcm_frames_with_sine_wave(ma_node_get_cached_output_ptr(pNode, iBus), pNodeBase->cachedDataCapInFramesPerBus, ma_format_f32, ma_node_output_bus_get_channels(&pNodeBase->pOutputBuses[iBus]), 48000); - } - #endif - } - - return MA_SUCCESS; -} - -MA_API ma_result ma_node_init(ma_node_graph* pNodeGraph, const ma_node_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_node* pNode) -{ - ma_result result; - size_t heapSizeInBytes; - void* pHeap; - - result = ma_node_get_heap_size(pNodeGraph, pConfig, &heapSizeInBytes); - if (result != MA_SUCCESS) { - return result; - } - - if (heapSizeInBytes > 0) { - pHeap = ma_malloc(heapSizeInBytes, pAllocationCallbacks); - if (pHeap == NULL) { - return MA_OUT_OF_MEMORY; - } - } else { - pHeap = NULL; - } - - result = ma_node_init_preallocated(pNodeGraph, pConfig, pHeap, pNode); - if (result != MA_SUCCESS) { - ma_free(pHeap, pAllocationCallbacks); - return result; - } - - ((ma_node_base*)pNode)->_ownsHeap = MA_TRUE; - return MA_SUCCESS; -} - -MA_API void ma_node_uninit(ma_node* pNode, const ma_allocation_callbacks* pAllocationCallbacks) -{ - ma_node_base* pNodeBase = (ma_node_base*)pNode; - - if (pNodeBase == NULL) { - return; - } - - /* - The first thing we need to do is fully detach the node. This will detach all inputs and - outputs. We need to do this first because it will sever the connection with the node graph and - allow us to complete uninitialization without needing to worry about thread-safety with the - audio thread. The detachment process will wait for any local processing of the node to finish. - */ - ma_node_detach_full(pNode); - - /* - At this point the node should be completely unreferenced by the node graph and we can finish up - the uninitialization process without needing to worry about thread-safety. - */ - if (pNodeBase->_ownsHeap) { - ma_free(pNodeBase->_pHeap, pAllocationCallbacks); - } -} - -MA_API ma_node_graph* ma_node_get_node_graph(const ma_node* pNode) -{ - if (pNode == NULL) { - return NULL; - } - - return ((const ma_node_base*)pNode)->pNodeGraph; -} - -MA_API ma_uint32 ma_node_get_input_bus_count(const ma_node* pNode) -{ - if (pNode == NULL) { - return 0; - } - - return ((ma_node_base*)pNode)->inputBusCount; -} - -MA_API ma_uint32 ma_node_get_output_bus_count(const ma_node* pNode) -{ - if (pNode == NULL) { - return 0; - } - - return ((ma_node_base*)pNode)->outputBusCount; -} - - -MA_API ma_uint32 ma_node_get_input_channels(const ma_node* pNode, ma_uint32 inputBusIndex) -{ - const ma_node_base* pNodeBase = (const ma_node_base*)pNode; - - if (pNode == NULL) { - return 0; - } - - if (inputBusIndex >= ma_node_get_input_bus_count(pNode)) { - return 0; /* Invalid bus index. */ - } - - return ma_node_input_bus_get_channels(&pNodeBase->pInputBuses[inputBusIndex]); -} - -MA_API ma_uint32 ma_node_get_output_channels(const ma_node* pNode, ma_uint32 outputBusIndex) -{ - const ma_node_base* pNodeBase = (const ma_node_base*)pNode; - - if (pNode == NULL) { - return 0; - } - - if (outputBusIndex >= ma_node_get_output_bus_count(pNode)) { - return 0; /* Invalid bus index. */ - } - - return ma_node_output_bus_get_channels(&pNodeBase->pOutputBuses[outputBusIndex]); -} - - -static ma_result ma_node_detach_full(ma_node* pNode) -{ - ma_node_base* pNodeBase = (ma_node_base*)pNode; - ma_uint32 iInputBus; - - if (pNodeBase == NULL) { - return MA_INVALID_ARGS; - } - - /* - Make sure the node is completely detached first. This will not return until the output bus is - guaranteed to no longer be referenced by the audio thread. - */ - ma_node_detach_all_output_buses(pNode); - - /* - At this point all output buses will have been detached from the graph and we can be guaranteed - that none of its input nodes will be getting processed by the graph. We can detach these - without needing to worry about the audio thread touching them. - */ - for (iInputBus = 0; iInputBus < ma_node_get_input_bus_count(pNode); iInputBus += 1) { - ma_node_input_bus* pInputBus; - ma_node_output_bus* pOutputBus; - - pInputBus = &pNodeBase->pInputBuses[iInputBus]; - - /* - This is important. We cannot be using ma_node_input_bus_first() or ma_node_input_bus_next(). Those - functions are specifically for the audio thread. We'll instead just manually iterate using standard - linked list logic. We don't need to worry about the audio thread referencing these because the step - above severed the connection to the graph. - */ - for (pOutputBus = (ma_node_output_bus*)ma_atomic_load_ptr(&pInputBus->head.pNext); pOutputBus != NULL; pOutputBus = (ma_node_output_bus*)ma_atomic_load_ptr(&pInputBus->head.pNext)) { - ma_node_detach_output_bus(pOutputBus->pNode, pOutputBus->outputBusIndex); /* This won't do any waiting in practice and should be efficient. */ - } - } - - return MA_SUCCESS; -} - -MA_API ma_result ma_node_detach_output_bus(ma_node* pNode, ma_uint32 outputBusIndex) -{ - ma_result result = MA_SUCCESS; - ma_node_base* pNodeBase = (ma_node_base*)pNode; - ma_node_base* pInputNodeBase; - - if (pNode == NULL) { - return MA_INVALID_ARGS; - } - - if (outputBusIndex >= ma_node_get_output_bus_count(pNode)) { - return MA_INVALID_ARGS; /* Invalid output bus index. */ - } - - /* We need to lock the output bus because we need to inspect the input node and grab its input bus. */ - ma_node_output_bus_lock(&pNodeBase->pOutputBuses[outputBusIndex]); - { - pInputNodeBase = (ma_node_base*)pNodeBase->pOutputBuses[outputBusIndex].pInputNode; - if (pInputNodeBase != NULL) { - ma_node_input_bus_detach__no_output_bus_lock(&pInputNodeBase->pInputBuses[pNodeBase->pOutputBuses[outputBusIndex].inputNodeInputBusIndex], &pNodeBase->pOutputBuses[outputBusIndex]); - } - } - ma_node_output_bus_unlock(&pNodeBase->pOutputBuses[outputBusIndex]); - - return result; -} - -MA_API ma_result ma_node_detach_all_output_buses(ma_node* pNode) -{ - ma_uint32 iOutputBus; - - if (pNode == NULL) { - return MA_INVALID_ARGS; - } - - for (iOutputBus = 0; iOutputBus < ma_node_get_output_bus_count(pNode); iOutputBus += 1) { - ma_node_detach_output_bus(pNode, iOutputBus); - } - - return MA_SUCCESS; -} - -MA_API ma_result ma_node_attach_output_bus(ma_node* pNode, ma_uint32 outputBusIndex, ma_node* pOtherNode, ma_uint32 otherNodeInputBusIndex) -{ - ma_node_base* pNodeBase = (ma_node_base*)pNode; - ma_node_base* pOtherNodeBase = (ma_node_base*)pOtherNode; - - if (pNodeBase == NULL || pOtherNodeBase == NULL) { - return MA_INVALID_ARGS; - } - - if (pNodeBase == pOtherNodeBase) { - return MA_INVALID_OPERATION; /* Cannot attach a node to itself. */ - } - - if (outputBusIndex >= ma_node_get_output_bus_count(pNode) || otherNodeInputBusIndex >= ma_node_get_input_bus_count(pOtherNode)) { - return MA_INVALID_OPERATION; /* Invalid bus index. */ - } - - /* The output channel count of the output node must be the same as the input channel count of the input node. */ - if (ma_node_get_output_channels(pNode, outputBusIndex) != ma_node_get_input_channels(pOtherNode, otherNodeInputBusIndex)) { - return MA_INVALID_OPERATION; /* Channel count is incompatible. */ - } - - /* This will deal with detaching if the output bus is already attached to something. */ - ma_node_input_bus_attach(&pOtherNodeBase->pInputBuses[otherNodeInputBusIndex], &pNodeBase->pOutputBuses[outputBusIndex], pOtherNode, otherNodeInputBusIndex); - - return MA_SUCCESS; -} - -MA_API ma_result ma_node_set_output_bus_volume(ma_node* pNode, ma_uint32 outputBusIndex, float volume) -{ - ma_node_base* pNodeBase = (ma_node_base*)pNode; - - if (pNodeBase == NULL) { - return MA_INVALID_ARGS; - } - - if (outputBusIndex >= ma_node_get_output_bus_count(pNode)) { - return MA_INVALID_ARGS; /* Invalid bus index. */ - } - - return ma_node_output_bus_set_volume(&pNodeBase->pOutputBuses[outputBusIndex], volume); -} - -MA_API float ma_node_get_output_bus_volume(const ma_node* pNode, ma_uint32 outputBusIndex) -{ - const ma_node_base* pNodeBase = (const ma_node_base*)pNode; - - if (pNodeBase == NULL) { - return 0; - } - - if (outputBusIndex >= ma_node_get_output_bus_count(pNode)) { - return 0; /* Invalid bus index. */ - } - - return ma_node_output_bus_get_volume(&pNodeBase->pOutputBuses[outputBusIndex]); -} - -MA_API ma_result ma_node_set_state(ma_node* pNode, ma_node_state state) -{ - ma_node_base* pNodeBase = (ma_node_base*)pNode; - - if (pNodeBase == NULL) { - return MA_INVALID_ARGS; - } - - ma_atomic_exchange_i32(&pNodeBase->state, state); - - return MA_SUCCESS; -} - -MA_API ma_node_state ma_node_get_state(const ma_node* pNode) -{ - const ma_node_base* pNodeBase = (const ma_node_base*)pNode; - - if (pNodeBase == NULL) { - return ma_node_state_stopped; - } - - return (ma_node_state)ma_atomic_load_i32(&pNodeBase->state); -} - -MA_API ma_result ma_node_set_state_time(ma_node* pNode, ma_node_state state, ma_uint64 globalTime) -{ - if (pNode == NULL) { - return MA_INVALID_ARGS; - } - - /* Validation check for safety since we'll be using this as an index into stateTimes[]. */ - if (state != ma_node_state_started && state != ma_node_state_stopped) { - return MA_INVALID_ARGS; - } - - ma_atomic_exchange_64(&((ma_node_base*)pNode)->stateTimes[state], globalTime); - - return MA_SUCCESS; -} - -MA_API ma_uint64 ma_node_get_state_time(const ma_node* pNode, ma_node_state state) -{ - if (pNode == NULL) { - return 0; - } - - /* Validation check for safety since we'll be using this as an index into stateTimes[]. */ - if (state != ma_node_state_started && state != ma_node_state_stopped) { - return 0; - } - - return ma_atomic_load_64(&((ma_node_base*)pNode)->stateTimes[state]); -} - -MA_API ma_node_state ma_node_get_state_by_time(const ma_node* pNode, ma_uint64 globalTime) -{ - if (pNode == NULL) { - return ma_node_state_stopped; - } - - return ma_node_get_state_by_time_range(pNode, globalTime, globalTime); -} - -MA_API ma_node_state ma_node_get_state_by_time_range(const ma_node* pNode, ma_uint64 globalTimeBeg, ma_uint64 globalTimeEnd) -{ - ma_node_state state; - - if (pNode == NULL) { - return ma_node_state_stopped; - } - - state = ma_node_get_state(pNode); - - /* An explicitly stopped node is always stopped. */ - if (state == ma_node_state_stopped) { - return ma_node_state_stopped; - } - - /* - Getting here means the node is marked as started, but it may still not be truly started due to - its start time not having been reached yet. Also, the stop time may have also been reached in - which case it'll be considered stopped. - */ - if (ma_node_get_state_time(pNode, ma_node_state_started) > globalTimeBeg) { - return ma_node_state_stopped; /* Start time has not yet been reached. */ - } - - if (ma_node_get_state_time(pNode, ma_node_state_stopped) <= globalTimeEnd) { - return ma_node_state_stopped; /* Stop time has been reached. */ - } - - /* Getting here means the node is marked as started and is within its start/stop times. */ - return ma_node_state_started; -} - -MA_API ma_uint64 ma_node_get_time(const ma_node* pNode) -{ - if (pNode == NULL) { - return 0; - } - - return ma_atomic_load_64(&((ma_node_base*)pNode)->localTime); -} - -MA_API ma_result ma_node_set_time(ma_node* pNode, ma_uint64 localTime) -{ - if (pNode == NULL) { - return MA_INVALID_ARGS; - } - - ma_atomic_exchange_64(&((ma_node_base*)pNode)->localTime, localTime); - - return MA_SUCCESS; -} - - - -static void ma_node_process_pcm_frames_internal(ma_node* pNode, const float** ppFramesIn, ma_uint32* pFrameCountIn, float** ppFramesOut, ma_uint32* pFrameCountOut) -{ - ma_node_base* pNodeBase = (ma_node_base*)pNode; - - MA_ASSERT(pNode != NULL); - - if (pNodeBase->vtable->onProcess) { - pNodeBase->vtable->onProcess(pNode, ppFramesIn, pFrameCountIn, ppFramesOut, pFrameCountOut); - } -} - -static ma_result ma_node_read_pcm_frames(ma_node* pNode, ma_uint32 outputBusIndex, float* pFramesOut, ma_uint32 frameCount, ma_uint32* pFramesRead, ma_uint64 globalTime) -{ - ma_node_base* pNodeBase = (ma_node_base*)pNode; - ma_result result = MA_SUCCESS; - ma_uint32 iInputBus; - ma_uint32 iOutputBus; - ma_uint32 inputBusCount; - ma_uint32 outputBusCount; - ma_uint32 totalFramesRead = 0; - float* ppFramesIn[MA_MAX_NODE_BUS_COUNT]; - float* ppFramesOut[MA_MAX_NODE_BUS_COUNT]; - ma_uint64 globalTimeBeg; - ma_uint64 globalTimeEnd; - ma_uint64 startTime; - ma_uint64 stopTime; - ma_uint32 timeOffsetBeg; - ma_uint32 timeOffsetEnd; - ma_uint32 frameCountIn; - ma_uint32 frameCountOut; - - /* - pFramesRead is mandatory. It must be used to determine how many frames were read. It's normal and - expected that the number of frames read may be different to that requested. Therefore, the caller - must look at this value to correctly determine how many frames were read. - */ - MA_ASSERT(pFramesRead != NULL); /* <-- If you've triggered this assert, you're using this function wrong. You *must* use this variable and inspect it after the call returns. */ - if (pFramesRead == NULL) { - return MA_INVALID_ARGS; - } - - *pFramesRead = 0; /* Safety. */ - - if (pNodeBase == NULL) { - return MA_INVALID_ARGS; - } - - if (outputBusIndex >= ma_node_get_output_bus_count(pNodeBase)) { - return MA_INVALID_ARGS; /* Invalid output bus index. */ - } - - /* Don't do anything if we're in a stopped state. */ - if (ma_node_get_state_by_time_range(pNode, globalTime, globalTime + frameCount) != ma_node_state_started) { - return MA_SUCCESS; /* We're in a stopped state. This is not an error - we just need to not read anything. */ - } - - - globalTimeBeg = globalTime; - globalTimeEnd = globalTime + frameCount; - startTime = ma_node_get_state_time(pNode, ma_node_state_started); - stopTime = ma_node_get_state_time(pNode, ma_node_state_stopped); - - /* - At this point we know that we are inside our start/stop times. However, we may need to adjust - our frame count and output pointer to accommodate since we could be straddling the time period - that this function is getting called for. - - It's possible (and likely) that the start time does not line up with the output buffer. We - therefore need to offset it by a number of frames to accommodate. The same thing applies for - the stop time. - */ - timeOffsetBeg = (globalTimeBeg < startTime) ? (ma_uint32)(globalTimeEnd - startTime) : 0; - timeOffsetEnd = (globalTimeEnd > stopTime) ? (ma_uint32)(globalTimeEnd - stopTime) : 0; - - /* Trim based on the start offset. We need to silence the start of the buffer. */ - if (timeOffsetBeg > 0) { - ma_silence_pcm_frames(pFramesOut, timeOffsetBeg, ma_format_f32, ma_node_get_output_channels(pNode, outputBusIndex)); - pFramesOut += timeOffsetBeg * ma_node_get_output_channels(pNode, outputBusIndex); - frameCount -= timeOffsetBeg; - } - - /* Trim based on the end offset. We don't need to silence the tail section because we'll just have a reduced value written to pFramesRead. */ - if (timeOffsetEnd > 0) { - frameCount -= timeOffsetEnd; - } - - - /* We run on different paths depending on the bus counts. */ - inputBusCount = ma_node_get_input_bus_count(pNode); - outputBusCount = ma_node_get_output_bus_count(pNode); - - /* - Run a simplified path when there are no inputs and one output. In this case there's nothing to - actually read and we can go straight to output. This is a very common scenario because the vast - majority of data source nodes will use this setup so this optimization I think is worthwhile. - */ - if (inputBusCount == 0 && outputBusCount == 1) { - /* Fast path. No need to read from input and no need for any caching. */ - frameCountIn = 0; - frameCountOut = frameCount; /* Just read as much as we can. The callback will return what was actually read. */ - - ppFramesOut[0] = pFramesOut; - - /* - If it's a passthrough we won't be expecting the callback to output anything, so we'll - need to pre-silence the output buffer. - */ - if ((pNodeBase->vtable->flags & MA_NODE_FLAG_PASSTHROUGH) != 0) { - ma_silence_pcm_frames(pFramesOut, frameCount, ma_format_f32, ma_node_get_output_channels(pNode, outputBusIndex)); - } - - ma_node_process_pcm_frames_internal(pNode, NULL, &frameCountIn, ppFramesOut, &frameCountOut); - totalFramesRead = frameCountOut; - } else { - /* Slow path. Need to read input data. */ - if ((pNodeBase->vtable->flags & MA_NODE_FLAG_PASSTHROUGH) != 0) { - /* - Fast path. We're running a passthrough. We need to read directly into the output buffer, but - still fire the callback so that event handling and trigger nodes can do their thing. Since - it's a passthrough there's no need for any kind of caching logic. - */ - MA_ASSERT(outputBusCount == inputBusCount); - MA_ASSERT(outputBusCount == 1); - MA_ASSERT(outputBusIndex == 0); - - /* We just read directly from input bus to output buffer, and then afterwards fire the callback. */ - ppFramesOut[0] = pFramesOut; - ppFramesIn[0] = ppFramesOut[0]; - - result = ma_node_input_bus_read_pcm_frames(pNodeBase, &pNodeBase->pInputBuses[0], ppFramesIn[0], frameCount, &totalFramesRead, globalTime); - if (result == MA_SUCCESS) { - /* Even though it's a passthrough, we still need to fire the callback. */ - frameCountIn = totalFramesRead; - frameCountOut = totalFramesRead; - - if (totalFramesRead > 0) { - ma_node_process_pcm_frames_internal(pNode, (const float**)ppFramesIn, &frameCountIn, ppFramesOut, &frameCountOut); /* From GCC: expected 'const float **' but argument is of type 'float **'. Shouldn't this be implicit? Explicit cast to silence the warning. */ - } - - /* - A passthrough should never have modified the input and output frame counts. If you're - triggering these asserts you need to fix your processing callback. - */ - MA_ASSERT(frameCountIn == totalFramesRead); - MA_ASSERT(frameCountOut == totalFramesRead); - } - } else { - /* Slow path. Need to do caching. */ - ma_uint32 framesToProcessIn; - ma_uint32 framesToProcessOut; - ma_bool32 consumeNullInput = MA_FALSE; - - /* - We use frameCount as a basis for the number of frames to read since that's what's being - requested, however we still need to clamp it to whatever can fit in the cache. - - This will also be used as the basis for determining how many input frames to read. This is - not ideal because it can result in too many input frames being read which introduces latency. - To solve this, nodes can implement an optional callback called onGetRequiredInputFrameCount - which is used as hint to miniaudio as to how many input frames it needs to read at a time. This - callback is completely optional, and if it's not set, miniaudio will assume `frameCount`. - - This function will be called multiple times for each period of time, once for each output node. - We cannot read from each input node each time this function is called. Instead we need to check - whether or not this is first output bus to be read from for this time period, and if so, read - from our input data. - - To determine whether or not we're ready to read data, we check a flag. There will be one flag - for each output. When the flag is set, it means data has been read previously and that we're - ready to advance time forward for our input nodes by reading fresh data. - */ - framesToProcessOut = frameCount; - if (framesToProcessOut > pNodeBase->cachedDataCapInFramesPerBus) { - framesToProcessOut = pNodeBase->cachedDataCapInFramesPerBus; - } - - framesToProcessIn = frameCount; - if (pNodeBase->vtable->onGetRequiredInputFrameCount) { - pNodeBase->vtable->onGetRequiredInputFrameCount(pNode, framesToProcessOut, &framesToProcessIn); /* <-- It does not matter if this fails. */ - } - if (framesToProcessIn > pNodeBase->cachedDataCapInFramesPerBus) { - framesToProcessIn = pNodeBase->cachedDataCapInFramesPerBus; - } - - - MA_ASSERT(framesToProcessIn <= 0xFFFF); - MA_ASSERT(framesToProcessOut <= 0xFFFF); - - if (ma_node_output_bus_has_read(&pNodeBase->pOutputBuses[outputBusIndex])) { - /* Getting here means we need to do another round of processing. */ - pNodeBase->cachedFrameCountOut = 0; - - for (;;) { - frameCountOut = 0; - - /* - We need to prepare our output frame pointers for processing. In the same iteration we need - to mark every output bus as unread so that future calls to this function for different buses - for the current time period don't pull in data when they should instead be reading from cache. - */ - for (iOutputBus = 0; iOutputBus < outputBusCount; iOutputBus += 1) { - ma_node_output_bus_set_has_read(&pNodeBase->pOutputBuses[iOutputBus], MA_FALSE); /* <-- This is what tells the next calls to this function for other output buses for this time period to read from cache instead of pulling in more data. */ - ppFramesOut[iOutputBus] = ma_node_get_cached_output_ptr(pNode, iOutputBus); - } - - /* We only need to read from input buses if there isn't already some data in the cache. */ - if (pNodeBase->cachedFrameCountIn == 0) { - ma_uint32 maxFramesReadIn = 0; - - /* Here is where we pull in data from the input buses. This is what will trigger an advance in time. */ - for (iInputBus = 0; iInputBus < inputBusCount; iInputBus += 1) { - ma_uint32 framesRead; - - /* The first thing to do is get the offset within our bulk allocation to store this input data. */ - ppFramesIn[iInputBus] = ma_node_get_cached_input_ptr(pNode, iInputBus); - - /* Once we've determined our destination pointer we can read. Note that we must inspect the number of frames read and fill any leftovers with silence for safety. */ - result = ma_node_input_bus_read_pcm_frames(pNodeBase, &pNodeBase->pInputBuses[iInputBus], ppFramesIn[iInputBus], framesToProcessIn, &framesRead, globalTime); - if (result != MA_SUCCESS) { - /* It doesn't really matter if we fail because we'll just fill with silence. */ - framesRead = 0; /* Just for safety, but I don't think it's really needed. */ - } - - /* TODO: Minor optimization opportunity here. If no frames were read and the buffer is already filled with silence, no need to re-silence it. */ - /* Any leftover frames need to silenced for safety. */ - if (framesRead < framesToProcessIn) { - ma_silence_pcm_frames(ppFramesIn[iInputBus] + (framesRead * ma_node_get_input_channels(pNodeBase, iInputBus)), (framesToProcessIn - framesRead), ma_format_f32, ma_node_get_input_channels(pNodeBase, iInputBus)); - } - - maxFramesReadIn = ma_max(maxFramesReadIn, framesRead); - } - - /* This was a fresh load of input data so reset our consumption counter. */ - pNodeBase->consumedFrameCountIn = 0; - - /* - We don't want to keep processing if there's nothing to process, so set the number of cached - input frames to the maximum number we read from each attachment (the lesser will be padded - with silence). If we didn't read anything, this will be set to 0 and the entire buffer will - have been assigned to silence. This being equal to 0 is an important property for us because - it allows us to detect when NULL can be passed into the processing callback for the input - buffer for the purpose of continuous processing. - */ - pNodeBase->cachedFrameCountIn = (ma_uint16)maxFramesReadIn; - } else { - /* We don't need to read anything, but we do need to prepare our input frame pointers. */ - for (iInputBus = 0; iInputBus < inputBusCount; iInputBus += 1) { - ppFramesIn[iInputBus] = ma_node_get_cached_input_ptr(pNode, iInputBus) + (pNodeBase->consumedFrameCountIn * ma_node_get_input_channels(pNodeBase, iInputBus)); - } - } - - /* - At this point we have our input data so now we need to do some processing. Sneaky little - optimization here - we can set the pointer to the output buffer for this output bus so - that the final copy into the output buffer is done directly by onProcess(). - */ - if (pFramesOut != NULL) { - ppFramesOut[outputBusIndex] = ma_offset_pcm_frames_ptr_f32(pFramesOut, pNodeBase->cachedFrameCountOut, ma_node_get_output_channels(pNode, outputBusIndex)); - } - - - /* Give the processing function the entire capacity of the output buffer. */ - frameCountOut = (framesToProcessOut - pNodeBase->cachedFrameCountOut); - - /* - We need to treat nodes with continuous processing a little differently. For these ones, - we always want to fire the callback with the requested number of frames, regardless of - pNodeBase->cachedFrameCountIn, which could be 0. Also, we want to check if we can pass - in NULL for the input buffer to the callback. - */ - if ((pNodeBase->vtable->flags & MA_NODE_FLAG_CONTINUOUS_PROCESSING) != 0) { - /* We're using continuous processing. Make sure we specify the whole frame count at all times. */ - frameCountIn = framesToProcessIn; /* Give the processing function as much input data as we've got in the buffer, including any silenced padding from short reads. */ - - if ((pNodeBase->vtable->flags & MA_NODE_FLAG_ALLOW_NULL_INPUT) != 0 && pNodeBase->consumedFrameCountIn == 0 && pNodeBase->cachedFrameCountIn == 0) { - consumeNullInput = MA_TRUE; - } else { - consumeNullInput = MA_FALSE; - } - - /* - Since we're using continuous processing we're always passing in a full frame count - regardless of how much input data was read. If this is greater than what we read as - input, we'll end up with an underflow. We instead need to make sure our cached frame - count is set to the number of frames we'll be passing to the data callback. Not - doing this will result in an underflow when we "consume" the cached data later on. - - Note that this check needs to be done after the "consumeNullInput" check above because - we use the property of cachedFrameCountIn being 0 to determine whether or not we - should be passing in a null pointer to the processing callback for when the node is - configured with MA_NODE_FLAG_ALLOW_NULL_INPUT. - */ - if (pNodeBase->cachedFrameCountIn < (ma_uint16)frameCountIn) { - pNodeBase->cachedFrameCountIn = (ma_uint16)frameCountIn; - } - } else { - frameCountIn = pNodeBase->cachedFrameCountIn; /* Give the processing function as much valid input data as we've got. */ - consumeNullInput = MA_FALSE; - } - - /* - Process data slightly differently depending on whether or not we're consuming NULL - input (checked just above). - */ - if (consumeNullInput) { - ma_node_process_pcm_frames_internal(pNode, NULL, &frameCountIn, ppFramesOut, &frameCountOut); - } else { - /* - We want to skip processing if there's no input data, but we can only do that safely if - we know that there is no chance of any output frames being produced. If continuous - processing is being used, this won't be a problem because the input frame count will - always be non-0. However, if continuous processing is *not* enabled and input and output - data is processed at different rates, we still need to process that last input frame - because there could be a few excess output frames needing to be produced from cached - data. The `MA_NODE_FLAG_DIFFERENT_PROCESSING_RATES` flag is used as the indicator for - determining whether or not we need to process the node even when there are no input - frames available right now. - */ - if (frameCountIn > 0 || (pNodeBase->vtable->flags & MA_NODE_FLAG_DIFFERENT_PROCESSING_RATES) != 0) { - ma_node_process_pcm_frames_internal(pNode, (const float**)ppFramesIn, &frameCountIn, ppFramesOut, &frameCountOut); /* From GCC: expected 'const float **' but argument is of type 'float **'. Shouldn't this be implicit? Explicit cast to silence the warning. */ - } else { - frameCountOut = 0; /* No data was processed. */ - } - } - - /* - Thanks to our sneaky optimization above we don't need to do any data copying directly into - the output buffer - the onProcess() callback just did that for us. We do, however, need to - apply the number of input and output frames that were processed. Note that due to continuous - processing above, we need to do explicit checks here. If we just consumed a NULL input - buffer it means that no actual input data was processed from the internal buffers and we - don't want to be modifying any counters. - */ - if (consumeNullInput == MA_FALSE) { - pNodeBase->consumedFrameCountIn += (ma_uint16)frameCountIn; - pNodeBase->cachedFrameCountIn -= (ma_uint16)frameCountIn; - } - - /* The cached output frame count is always equal to what we just read. */ - pNodeBase->cachedFrameCountOut += (ma_uint16)frameCountOut; - - /* If we couldn't process any data, we're done. The loop needs to be terminated here or else we'll get stuck in a loop. */ - if (pNodeBase->cachedFrameCountOut == framesToProcessOut || (frameCountOut == 0 && frameCountIn == 0)) { - break; - } - } - } else { - /* - We're not needing to read anything from the input buffer so just read directly from our - already-processed data. - */ - if (pFramesOut != NULL) { - ma_copy_pcm_frames(pFramesOut, ma_node_get_cached_output_ptr(pNodeBase, outputBusIndex), pNodeBase->cachedFrameCountOut, ma_format_f32, ma_node_get_output_channels(pNodeBase, outputBusIndex)); - } - } - - /* The number of frames read is always equal to the number of cached output frames. */ - totalFramesRead = pNodeBase->cachedFrameCountOut; - - /* Now that we've read the data, make sure our read flag is set. */ - ma_node_output_bus_set_has_read(&pNodeBase->pOutputBuses[outputBusIndex], MA_TRUE); - } - } - - /* Apply volume, if necessary. */ - ma_apply_volume_factor_f32(pFramesOut, totalFramesRead * ma_node_get_output_channels(pNodeBase, outputBusIndex), ma_node_output_bus_get_volume(&pNodeBase->pOutputBuses[outputBusIndex])); - - /* Advance our local time forward. */ - ma_atomic_fetch_add_64(&pNodeBase->localTime, (ma_uint64)totalFramesRead); - - *pFramesRead = totalFramesRead + timeOffsetBeg; /* Must include the silenced section at the start of the buffer. */ - return result; -} - - - - -/* Data source node. */ -MA_API ma_data_source_node_config ma_data_source_node_config_init(ma_data_source* pDataSource) -{ - ma_data_source_node_config config; - - MA_ZERO_OBJECT(&config); - config.nodeConfig = ma_node_config_init(); - config.pDataSource = pDataSource; - - return config; -} - - -static void ma_data_source_node_process_pcm_frames(ma_node* pNode, const float** ppFramesIn, ma_uint32* pFrameCountIn, float** ppFramesOut, ma_uint32* pFrameCountOut) -{ - ma_data_source_node* pDataSourceNode = (ma_data_source_node*)pNode; - ma_format format; - ma_uint32 channels; - ma_uint32 frameCount; - ma_uint64 framesRead = 0; - - MA_ASSERT(pDataSourceNode != NULL); - MA_ASSERT(pDataSourceNode->pDataSource != NULL); - MA_ASSERT(ma_node_get_input_bus_count(pDataSourceNode) == 0); - MA_ASSERT(ma_node_get_output_bus_count(pDataSourceNode) == 1); - - /* We don't want to read from ppFramesIn at all. Instead we read from the data source. */ - (void)ppFramesIn; - (void)pFrameCountIn; - - frameCount = *pFrameCountOut; - - /* miniaudio should never be calling this with a frame count of zero. */ - MA_ASSERT(frameCount > 0); - - if (ma_data_source_get_data_format(pDataSourceNode->pDataSource, &format, &channels, NULL, NULL, 0) == MA_SUCCESS) { /* <-- Don't care about sample rate here. */ - /* The node graph system requires samples be in floating point format. This is checked in ma_data_source_node_init(). */ - MA_ASSERT(format == ma_format_f32); - (void)format; /* Just to silence some static analysis tools. */ - - ma_data_source_read_pcm_frames(pDataSourceNode->pDataSource, ppFramesOut[0], frameCount, &framesRead); - } - - *pFrameCountOut = (ma_uint32)framesRead; -} - -static ma_node_vtable g_ma_data_source_node_vtable = -{ - ma_data_source_node_process_pcm_frames, - NULL, /* onGetRequiredInputFrameCount */ - 0, /* 0 input buses. */ - 1, /* 1 output bus. */ - 0 -}; - -MA_API ma_result ma_data_source_node_init(ma_node_graph* pNodeGraph, const ma_data_source_node_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_data_source_node* pDataSourceNode) -{ - ma_result result; - ma_format format; /* For validating the format, which must be ma_format_f32. */ - ma_uint32 channels; /* For specifying the channel count of the output bus. */ - ma_node_config baseConfig; - - if (pDataSourceNode == NULL) { - return MA_INVALID_ARGS; - } - - MA_ZERO_OBJECT(pDataSourceNode); - - if (pConfig == NULL) { - return MA_INVALID_ARGS; - } - - result = ma_data_source_get_data_format(pConfig->pDataSource, &format, &channels, NULL, NULL, 0); /* Don't care about sample rate. This will check pDataSource for NULL. */ - if (result != MA_SUCCESS) { - return result; - } - - MA_ASSERT(format == ma_format_f32); /* <-- If you've triggered this it means your data source is not outputting floating-point samples. You must configure your data source to use ma_format_f32. */ - if (format != ma_format_f32) { - return MA_INVALID_ARGS; /* Invalid format. */ - } - - /* The channel count is defined by the data source. If the caller has manually changed the channels we just ignore it. */ - baseConfig = pConfig->nodeConfig; - baseConfig.vtable = &g_ma_data_source_node_vtable; /* Explicitly set the vtable here to prevent callers from setting it incorrectly. */ - - /* - The channel count is defined by the data source. It is invalid for the caller to manually set - the channel counts in the config. `ma_data_source_node_config_init()` will have defaulted the - channel count pointer to NULL which is how it must remain. If you trigger any of these asserts - it means you're explicitly setting the channel count. Instead, configure the output channel - count of your data source to be the necessary channel count. - */ - if (baseConfig.pOutputChannels != NULL) { - return MA_INVALID_ARGS; - } - - baseConfig.pOutputChannels = &channels; - - result = ma_node_init(pNodeGraph, &baseConfig, pAllocationCallbacks, &pDataSourceNode->base); - if (result != MA_SUCCESS) { - return result; - } - - pDataSourceNode->pDataSource = pConfig->pDataSource; - - return MA_SUCCESS; -} - -MA_API void ma_data_source_node_uninit(ma_data_source_node* pDataSourceNode, const ma_allocation_callbacks* pAllocationCallbacks) -{ - ma_node_uninit(&pDataSourceNode->base, pAllocationCallbacks); -} - -MA_API ma_result ma_data_source_node_set_looping(ma_data_source_node* pDataSourceNode, ma_bool32 isLooping) -{ - if (pDataSourceNode == NULL) { - return MA_INVALID_ARGS; - } - - return ma_data_source_set_looping(pDataSourceNode->pDataSource, isLooping); -} - -MA_API ma_bool32 ma_data_source_node_is_looping(ma_data_source_node* pDataSourceNode) -{ - if (pDataSourceNode == NULL) { - return MA_FALSE; - } - - return ma_data_source_is_looping(pDataSourceNode->pDataSource); -} - - - -/* Splitter Node. */ -MA_API ma_splitter_node_config ma_splitter_node_config_init(ma_uint32 channels) -{ - ma_splitter_node_config config; - - MA_ZERO_OBJECT(&config); - config.nodeConfig = ma_node_config_init(); - config.channels = channels; - config.outputBusCount = 2; - - return config; -} - - -static void ma_splitter_node_process_pcm_frames(ma_node* pNode, const float** ppFramesIn, ma_uint32* pFrameCountIn, float** ppFramesOut, ma_uint32* pFrameCountOut) -{ - ma_node_base* pNodeBase = (ma_node_base*)pNode; - ma_uint32 iOutputBus; - ma_uint32 channels; - - MA_ASSERT(pNodeBase != NULL); - MA_ASSERT(ma_node_get_input_bus_count(pNodeBase) == 1); - - /* We don't need to consider the input frame count - it'll be the same as the output frame count and we process everything. */ - (void)pFrameCountIn; - - /* NOTE: This assumes the same number of channels for all inputs and outputs. This was checked in ma_splitter_node_init(). */ - channels = ma_node_get_input_channels(pNodeBase, 0); - - /* Splitting is just copying the first input bus and copying it over to each output bus. */ - for (iOutputBus = 0; iOutputBus < ma_node_get_output_bus_count(pNodeBase); iOutputBus += 1) { - ma_copy_pcm_frames(ppFramesOut[iOutputBus], ppFramesIn[0], *pFrameCountOut, ma_format_f32, channels); - } -} - -static ma_node_vtable g_ma_splitter_node_vtable = -{ - ma_splitter_node_process_pcm_frames, - NULL, /* onGetRequiredInputFrameCount */ - 1, /* 1 input bus. */ - MA_NODE_BUS_COUNT_UNKNOWN, /* The output bus count is specified on a per-node basis. */ - 0 -}; - -MA_API ma_result ma_splitter_node_init(ma_node_graph* pNodeGraph, const ma_splitter_node_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_splitter_node* pSplitterNode) -{ - ma_result result; - ma_node_config baseConfig; - ma_uint32 pInputChannels[1]; - ma_uint32 pOutputChannels[MA_MAX_NODE_BUS_COUNT]; - ma_uint32 iOutputBus; - - if (pSplitterNode == NULL) { - return MA_INVALID_ARGS; - } - - MA_ZERO_OBJECT(pSplitterNode); - - if (pConfig == NULL) { - return MA_INVALID_ARGS; - } - - if (pConfig->outputBusCount > MA_MAX_NODE_BUS_COUNT) { - return MA_INVALID_ARGS; /* Too many output buses. */ - } - - /* Splitters require the same number of channels between inputs and outputs. */ - pInputChannels[0] = pConfig->channels; - for (iOutputBus = 0; iOutputBus < pConfig->outputBusCount; iOutputBus += 1) { - pOutputChannels[iOutputBus] = pConfig->channels; - } - - baseConfig = pConfig->nodeConfig; - baseConfig.vtable = &g_ma_splitter_node_vtable; - baseConfig.pInputChannels = pInputChannels; - baseConfig.pOutputChannels = pOutputChannels; - baseConfig.outputBusCount = pConfig->outputBusCount; - - result = ma_node_init(pNodeGraph, &baseConfig, pAllocationCallbacks, &pSplitterNode->base); - if (result != MA_SUCCESS) { - return result; /* Failed to initialize the base node. */ - } - - return MA_SUCCESS; -} - -MA_API void ma_splitter_node_uninit(ma_splitter_node* pSplitterNode, const ma_allocation_callbacks* pAllocationCallbacks) -{ - ma_node_uninit(pSplitterNode, pAllocationCallbacks); -} - - -/* -Biquad Node -*/ -MA_API ma_biquad_node_config ma_biquad_node_config_init(ma_uint32 channels, float b0, float b1, float b2, float a0, float a1, float a2) -{ - ma_biquad_node_config config; - - config.nodeConfig = ma_node_config_init(); - config.biquad = ma_biquad_config_init(ma_format_f32, channels, b0, b1, b2, a0, a1, a2); - - return config; -} - -static void ma_biquad_node_process_pcm_frames(ma_node* pNode, const float** ppFramesIn, ma_uint32* pFrameCountIn, float** ppFramesOut, ma_uint32* pFrameCountOut) -{ - ma_biquad_node* pLPFNode = (ma_biquad_node*)pNode; - - MA_ASSERT(pNode != NULL); - (void)pFrameCountIn; - - ma_biquad_process_pcm_frames(&pLPFNode->biquad, ppFramesOut[0], ppFramesIn[0], *pFrameCountOut); -} - -static ma_node_vtable g_ma_biquad_node_vtable = -{ - ma_biquad_node_process_pcm_frames, - NULL, /* onGetRequiredInputFrameCount */ - 1, /* One input. */ - 1, /* One output. */ - 0 /* Default flags. */ -}; - -MA_API ma_result ma_biquad_node_init(ma_node_graph* pNodeGraph, const ma_biquad_node_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_biquad_node* pNode) -{ - ma_result result; - ma_node_config baseNodeConfig; - - if (pNode == NULL) { - return MA_INVALID_ARGS; - } - - MA_ZERO_OBJECT(pNode); - - if (pConfig == NULL) { - return MA_INVALID_ARGS; - } - - if (pConfig->biquad.format != ma_format_f32) { - return MA_INVALID_ARGS; /* The format must be f32. */ - } - - result = ma_biquad_init(&pConfig->biquad, pAllocationCallbacks, &pNode->biquad); - if (result != MA_SUCCESS) { - return result; - } - - baseNodeConfig = ma_node_config_init(); - baseNodeConfig.vtable = &g_ma_biquad_node_vtable; - baseNodeConfig.pInputChannels = &pConfig->biquad.channels; - baseNodeConfig.pOutputChannels = &pConfig->biquad.channels; - - result = ma_node_init(pNodeGraph, &baseNodeConfig, pAllocationCallbacks, pNode); - if (result != MA_SUCCESS) { - return result; - } - - return result; -} - -MA_API ma_result ma_biquad_node_reinit(const ma_biquad_config* pConfig, ma_biquad_node* pNode) -{ - ma_biquad_node* pLPFNode = (ma_biquad_node*)pNode; - - MA_ASSERT(pNode != NULL); - - return ma_biquad_reinit(pConfig, &pLPFNode->biquad); -} - -MA_API void ma_biquad_node_uninit(ma_biquad_node* pNode, const ma_allocation_callbacks* pAllocationCallbacks) -{ - ma_biquad_node* pLPFNode = (ma_biquad_node*)pNode; - - if (pNode == NULL) { - return; - } - - ma_node_uninit(pNode, pAllocationCallbacks); - ma_biquad_uninit(&pLPFNode->biquad, pAllocationCallbacks); -} - - - -/* -Low Pass Filter Node -*/ -MA_API ma_lpf_node_config ma_lpf_node_config_init(ma_uint32 channels, ma_uint32 sampleRate, double cutoffFrequency, ma_uint32 order) -{ - ma_lpf_node_config config; - - config.nodeConfig = ma_node_config_init(); - config.lpf = ma_lpf_config_init(ma_format_f32, channels, sampleRate, cutoffFrequency, order); - - return config; -} - -static void ma_lpf_node_process_pcm_frames(ma_node* pNode, const float** ppFramesIn, ma_uint32* pFrameCountIn, float** ppFramesOut, ma_uint32* pFrameCountOut) -{ - ma_lpf_node* pLPFNode = (ma_lpf_node*)pNode; - - MA_ASSERT(pNode != NULL); - (void)pFrameCountIn; - - ma_lpf_process_pcm_frames(&pLPFNode->lpf, ppFramesOut[0], ppFramesIn[0], *pFrameCountOut); -} - -static ma_node_vtable g_ma_lpf_node_vtable = -{ - ma_lpf_node_process_pcm_frames, - NULL, /* onGetRequiredInputFrameCount */ - 1, /* One input. */ - 1, /* One output. */ - 0 /* Default flags. */ -}; - -MA_API ma_result ma_lpf_node_init(ma_node_graph* pNodeGraph, const ma_lpf_node_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_lpf_node* pNode) -{ - ma_result result; - ma_node_config baseNodeConfig; - - if (pNode == NULL) { - return MA_INVALID_ARGS; - } - - MA_ZERO_OBJECT(pNode); - - if (pConfig == NULL) { - return MA_INVALID_ARGS; - } - - if (pConfig->lpf.format != ma_format_f32) { - return MA_INVALID_ARGS; /* The format must be f32. */ - } - - result = ma_lpf_init(&pConfig->lpf, pAllocationCallbacks, &pNode->lpf); - if (result != MA_SUCCESS) { - return result; - } - - baseNodeConfig = ma_node_config_init(); - baseNodeConfig.vtable = &g_ma_lpf_node_vtable; - baseNodeConfig.pInputChannels = &pConfig->lpf.channels; - baseNodeConfig.pOutputChannels = &pConfig->lpf.channels; - - result = ma_node_init(pNodeGraph, &baseNodeConfig, pAllocationCallbacks, pNode); - if (result != MA_SUCCESS) { - return result; - } - - return result; -} - -MA_API ma_result ma_lpf_node_reinit(const ma_lpf_config* pConfig, ma_lpf_node* pNode) -{ - ma_lpf_node* pLPFNode = (ma_lpf_node*)pNode; - - if (pNode == NULL) { - return MA_INVALID_ARGS; - } - - return ma_lpf_reinit(pConfig, &pLPFNode->lpf); -} - -MA_API void ma_lpf_node_uninit(ma_lpf_node* pNode, const ma_allocation_callbacks* pAllocationCallbacks) -{ - ma_lpf_node* pLPFNode = (ma_lpf_node*)pNode; - - if (pNode == NULL) { - return; - } - - ma_node_uninit(pNode, pAllocationCallbacks); - ma_lpf_uninit(&pLPFNode->lpf, pAllocationCallbacks); -} - - - -/* -High Pass Filter Node -*/ -MA_API ma_hpf_node_config ma_hpf_node_config_init(ma_uint32 channels, ma_uint32 sampleRate, double cutoffFrequency, ma_uint32 order) -{ - ma_hpf_node_config config; - - config.nodeConfig = ma_node_config_init(); - config.hpf = ma_hpf_config_init(ma_format_f32, channels, sampleRate, cutoffFrequency, order); - - return config; -} - -static void ma_hpf_node_process_pcm_frames(ma_node* pNode, const float** ppFramesIn, ma_uint32* pFrameCountIn, float** ppFramesOut, ma_uint32* pFrameCountOut) -{ - ma_hpf_node* pHPFNode = (ma_hpf_node*)pNode; - - MA_ASSERT(pNode != NULL); - (void)pFrameCountIn; - - ma_hpf_process_pcm_frames(&pHPFNode->hpf, ppFramesOut[0], ppFramesIn[0], *pFrameCountOut); -} - -static ma_node_vtable g_ma_hpf_node_vtable = -{ - ma_hpf_node_process_pcm_frames, - NULL, /* onGetRequiredInputFrameCount */ - 1, /* One input. */ - 1, /* One output. */ - 0 /* Default flags. */ -}; - -MA_API ma_result ma_hpf_node_init(ma_node_graph* pNodeGraph, const ma_hpf_node_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_hpf_node* pNode) -{ - ma_result result; - ma_node_config baseNodeConfig; - - if (pNode == NULL) { - return MA_INVALID_ARGS; - } - - MA_ZERO_OBJECT(pNode); - - if (pConfig == NULL) { - return MA_INVALID_ARGS; - } - - if (pConfig->hpf.format != ma_format_f32) { - return MA_INVALID_ARGS; /* The format must be f32. */ - } - - result = ma_hpf_init(&pConfig->hpf, pAllocationCallbacks, &pNode->hpf); - if (result != MA_SUCCESS) { - return result; - } - - baseNodeConfig = ma_node_config_init(); - baseNodeConfig.vtable = &g_ma_hpf_node_vtable; - baseNodeConfig.pInputChannels = &pConfig->hpf.channels; - baseNodeConfig.pOutputChannels = &pConfig->hpf.channels; - - result = ma_node_init(pNodeGraph, &baseNodeConfig, pAllocationCallbacks, pNode); - if (result != MA_SUCCESS) { - return result; - } - - return result; -} - -MA_API ma_result ma_hpf_node_reinit(const ma_hpf_config* pConfig, ma_hpf_node* pNode) -{ - ma_hpf_node* pHPFNode = (ma_hpf_node*)pNode; - - if (pNode == NULL) { - return MA_INVALID_ARGS; - } - - return ma_hpf_reinit(pConfig, &pHPFNode->hpf); -} - -MA_API void ma_hpf_node_uninit(ma_hpf_node* pNode, const ma_allocation_callbacks* pAllocationCallbacks) -{ - ma_hpf_node* pHPFNode = (ma_hpf_node*)pNode; - - if (pNode == NULL) { - return; - } - - ma_node_uninit(pNode, pAllocationCallbacks); - ma_hpf_uninit(&pHPFNode->hpf, pAllocationCallbacks); -} - - - - -/* -Band Pass Filter Node -*/ -MA_API ma_bpf_node_config ma_bpf_node_config_init(ma_uint32 channels, ma_uint32 sampleRate, double cutoffFrequency, ma_uint32 order) -{ - ma_bpf_node_config config; - - config.nodeConfig = ma_node_config_init(); - config.bpf = ma_bpf_config_init(ma_format_f32, channels, sampleRate, cutoffFrequency, order); - - return config; -} - -static void ma_bpf_node_process_pcm_frames(ma_node* pNode, const float** ppFramesIn, ma_uint32* pFrameCountIn, float** ppFramesOut, ma_uint32* pFrameCountOut) -{ - ma_bpf_node* pBPFNode = (ma_bpf_node*)pNode; - - MA_ASSERT(pNode != NULL); - (void)pFrameCountIn; - - ma_bpf_process_pcm_frames(&pBPFNode->bpf, ppFramesOut[0], ppFramesIn[0], *pFrameCountOut); -} - -static ma_node_vtable g_ma_bpf_node_vtable = -{ - ma_bpf_node_process_pcm_frames, - NULL, /* onGetRequiredInputFrameCount */ - 1, /* One input. */ - 1, /* One output. */ - 0 /* Default flags. */ -}; - -MA_API ma_result ma_bpf_node_init(ma_node_graph* pNodeGraph, const ma_bpf_node_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_bpf_node* pNode) -{ - ma_result result; - ma_node_config baseNodeConfig; - - if (pNode == NULL) { - return MA_INVALID_ARGS; - } - - MA_ZERO_OBJECT(pNode); - - if (pConfig == NULL) { - return MA_INVALID_ARGS; - } - - if (pConfig->bpf.format != ma_format_f32) { - return MA_INVALID_ARGS; /* The format must be f32. */ - } - - result = ma_bpf_init(&pConfig->bpf, pAllocationCallbacks, &pNode->bpf); - if (result != MA_SUCCESS) { - return result; - } - - baseNodeConfig = ma_node_config_init(); - baseNodeConfig.vtable = &g_ma_bpf_node_vtable; - baseNodeConfig.pInputChannels = &pConfig->bpf.channels; - baseNodeConfig.pOutputChannels = &pConfig->bpf.channels; - - result = ma_node_init(pNodeGraph, &baseNodeConfig, pAllocationCallbacks, pNode); - if (result != MA_SUCCESS) { - return result; - } - - return result; -} - -MA_API ma_result ma_bpf_node_reinit(const ma_bpf_config* pConfig, ma_bpf_node* pNode) -{ - ma_bpf_node* pBPFNode = (ma_bpf_node*)pNode; - - if (pNode == NULL) { - return MA_INVALID_ARGS; - } - - return ma_bpf_reinit(pConfig, &pBPFNode->bpf); -} - -MA_API void ma_bpf_node_uninit(ma_bpf_node* pNode, const ma_allocation_callbacks* pAllocationCallbacks) -{ - ma_bpf_node* pBPFNode = (ma_bpf_node*)pNode; - - if (pNode == NULL) { - return; - } - - ma_node_uninit(pNode, pAllocationCallbacks); - ma_bpf_uninit(&pBPFNode->bpf, pAllocationCallbacks); -} - - - -/* -Notching Filter Node -*/ -MA_API ma_notch_node_config ma_notch_node_config_init(ma_uint32 channels, ma_uint32 sampleRate, double q, double frequency) -{ - ma_notch_node_config config; - - config.nodeConfig = ma_node_config_init(); - config.notch = ma_notch2_config_init(ma_format_f32, channels, sampleRate, q, frequency); - - return config; -} - -static void ma_notch_node_process_pcm_frames(ma_node* pNode, const float** ppFramesIn, ma_uint32* pFrameCountIn, float** ppFramesOut, ma_uint32* pFrameCountOut) -{ - ma_notch_node* pBPFNode = (ma_notch_node*)pNode; - - MA_ASSERT(pNode != NULL); - (void)pFrameCountIn; - - ma_notch2_process_pcm_frames(&pBPFNode->notch, ppFramesOut[0], ppFramesIn[0], *pFrameCountOut); -} - -static ma_node_vtable g_ma_notch_node_vtable = -{ - ma_notch_node_process_pcm_frames, - NULL, /* onGetRequiredInputFrameCount */ - 1, /* One input. */ - 1, /* One output. */ - 0 /* Default flags. */ -}; - -MA_API ma_result ma_notch_node_init(ma_node_graph* pNodeGraph, const ma_notch_node_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_notch_node* pNode) -{ - ma_result result; - ma_node_config baseNodeConfig; - - if (pNode == NULL) { - return MA_INVALID_ARGS; - } - - MA_ZERO_OBJECT(pNode); - - if (pConfig == NULL) { - return MA_INVALID_ARGS; - } - - if (pConfig->notch.format != ma_format_f32) { - return MA_INVALID_ARGS; /* The format must be f32. */ - } - - result = ma_notch2_init(&pConfig->notch, pAllocationCallbacks, &pNode->notch); - if (result != MA_SUCCESS) { - return result; - } - - baseNodeConfig = ma_node_config_init(); - baseNodeConfig.vtable = &g_ma_notch_node_vtable; - baseNodeConfig.pInputChannels = &pConfig->notch.channels; - baseNodeConfig.pOutputChannels = &pConfig->notch.channels; - - result = ma_node_init(pNodeGraph, &baseNodeConfig, pAllocationCallbacks, pNode); - if (result != MA_SUCCESS) { - return result; - } - - return result; -} - -MA_API ma_result ma_notch_node_reinit(const ma_notch_config* pConfig, ma_notch_node* pNode) -{ - ma_notch_node* pNotchNode = (ma_notch_node*)pNode; - - if (pNode == NULL) { - return MA_INVALID_ARGS; - } - - return ma_notch2_reinit(pConfig, &pNotchNode->notch); -} - -MA_API void ma_notch_node_uninit(ma_notch_node* pNode, const ma_allocation_callbacks* pAllocationCallbacks) -{ - ma_notch_node* pNotchNode = (ma_notch_node*)pNode; - - if (pNode == NULL) { - return; - } - - ma_node_uninit(pNode, pAllocationCallbacks); - ma_notch2_uninit(&pNotchNode->notch, pAllocationCallbacks); -} - - - -/* -Peaking Filter Node -*/ -MA_API ma_peak_node_config ma_peak_node_config_init(ma_uint32 channels, ma_uint32 sampleRate, double gainDB, double q, double frequency) -{ - ma_peak_node_config config; - - config.nodeConfig = ma_node_config_init(); - config.peak = ma_peak2_config_init(ma_format_f32, channels, sampleRate, gainDB, q, frequency); - - return config; -} - -static void ma_peak_node_process_pcm_frames(ma_node* pNode, const float** ppFramesIn, ma_uint32* pFrameCountIn, float** ppFramesOut, ma_uint32* pFrameCountOut) -{ - ma_peak_node* pBPFNode = (ma_peak_node*)pNode; - - MA_ASSERT(pNode != NULL); - (void)pFrameCountIn; - - ma_peak2_process_pcm_frames(&pBPFNode->peak, ppFramesOut[0], ppFramesIn[0], *pFrameCountOut); -} - -static ma_node_vtable g_ma_peak_node_vtable = -{ - ma_peak_node_process_pcm_frames, - NULL, /* onGetRequiredInputFrameCount */ - 1, /* One input. */ - 1, /* One output. */ - 0 /* Default flags. */ -}; - -MA_API ma_result ma_peak_node_init(ma_node_graph* pNodeGraph, const ma_peak_node_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_peak_node* pNode) -{ - ma_result result; - ma_node_config baseNodeConfig; - - if (pNode == NULL) { - return MA_INVALID_ARGS; - } - - MA_ZERO_OBJECT(pNode); - - if (pConfig == NULL) { - return MA_INVALID_ARGS; - } - - if (pConfig->peak.format != ma_format_f32) { - return MA_INVALID_ARGS; /* The format must be f32. */ - } - - result = ma_peak2_init(&pConfig->peak, pAllocationCallbacks, &pNode->peak); - if (result != MA_SUCCESS) { - ma_node_uninit(pNode, pAllocationCallbacks); - return result; - } - - baseNodeConfig = ma_node_config_init(); - baseNodeConfig.vtable = &g_ma_peak_node_vtable; - baseNodeConfig.pInputChannels = &pConfig->peak.channels; - baseNodeConfig.pOutputChannels = &pConfig->peak.channels; - - result = ma_node_init(pNodeGraph, &baseNodeConfig, pAllocationCallbacks, pNode); - if (result != MA_SUCCESS) { - return result; - } - - return result; -} - -MA_API ma_result ma_peak_node_reinit(const ma_peak_config* pConfig, ma_peak_node* pNode) -{ - ma_peak_node* pPeakNode = (ma_peak_node*)pNode; - - if (pNode == NULL) { - return MA_INVALID_ARGS; - } - - return ma_peak2_reinit(pConfig, &pPeakNode->peak); -} - -MA_API void ma_peak_node_uninit(ma_peak_node* pNode, const ma_allocation_callbacks* pAllocationCallbacks) -{ - ma_peak_node* pPeakNode = (ma_peak_node*)pNode; - - if (pNode == NULL) { - return; - } - - ma_node_uninit(pNode, pAllocationCallbacks); - ma_peak2_uninit(&pPeakNode->peak, pAllocationCallbacks); -} - - - -/* -Low Shelf Filter Node -*/ -MA_API ma_loshelf_node_config ma_loshelf_node_config_init(ma_uint32 channels, ma_uint32 sampleRate, double gainDB, double q, double frequency) -{ - ma_loshelf_node_config config; - - config.nodeConfig = ma_node_config_init(); - config.loshelf = ma_loshelf2_config_init(ma_format_f32, channels, sampleRate, gainDB, q, frequency); - - return config; -} - -static void ma_loshelf_node_process_pcm_frames(ma_node* pNode, const float** ppFramesIn, ma_uint32* pFrameCountIn, float** ppFramesOut, ma_uint32* pFrameCountOut) -{ - ma_loshelf_node* pBPFNode = (ma_loshelf_node*)pNode; - - MA_ASSERT(pNode != NULL); - (void)pFrameCountIn; - - ma_loshelf2_process_pcm_frames(&pBPFNode->loshelf, ppFramesOut[0], ppFramesIn[0], *pFrameCountOut); -} - -static ma_node_vtable g_ma_loshelf_node_vtable = -{ - ma_loshelf_node_process_pcm_frames, - NULL, /* onGetRequiredInputFrameCount */ - 1, /* One input. */ - 1, /* One output. */ - 0 /* Default flags. */ -}; - -MA_API ma_result ma_loshelf_node_init(ma_node_graph* pNodeGraph, const ma_loshelf_node_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_loshelf_node* pNode) -{ - ma_result result; - ma_node_config baseNodeConfig; - - if (pNode == NULL) { - return MA_INVALID_ARGS; - } - - MA_ZERO_OBJECT(pNode); - - if (pConfig == NULL) { - return MA_INVALID_ARGS; - } - - if (pConfig->loshelf.format != ma_format_f32) { - return MA_INVALID_ARGS; /* The format must be f32. */ - } - - result = ma_loshelf2_init(&pConfig->loshelf, pAllocationCallbacks, &pNode->loshelf); - if (result != MA_SUCCESS) { - return result; - } - - baseNodeConfig = ma_node_config_init(); - baseNodeConfig.vtable = &g_ma_loshelf_node_vtable; - baseNodeConfig.pInputChannels = &pConfig->loshelf.channels; - baseNodeConfig.pOutputChannels = &pConfig->loshelf.channels; - - result = ma_node_init(pNodeGraph, &baseNodeConfig, pAllocationCallbacks, pNode); - if (result != MA_SUCCESS) { - return result; - } - - return result; -} - -MA_API ma_result ma_loshelf_node_reinit(const ma_loshelf_config* pConfig, ma_loshelf_node* pNode) -{ - ma_loshelf_node* pLoshelfNode = (ma_loshelf_node*)pNode; - - if (pNode == NULL) { - return MA_INVALID_ARGS; - } - - return ma_loshelf2_reinit(pConfig, &pLoshelfNode->loshelf); -} - -MA_API void ma_loshelf_node_uninit(ma_loshelf_node* pNode, const ma_allocation_callbacks* pAllocationCallbacks) -{ - ma_loshelf_node* pLoshelfNode = (ma_loshelf_node*)pNode; - - if (pNode == NULL) { - return; - } - - ma_node_uninit(pNode, pAllocationCallbacks); - ma_loshelf2_uninit(&pLoshelfNode->loshelf, pAllocationCallbacks); -} - - - -/* -High Shelf Filter Node -*/ -MA_API ma_hishelf_node_config ma_hishelf_node_config_init(ma_uint32 channels, ma_uint32 sampleRate, double gainDB, double q, double frequency) -{ - ma_hishelf_node_config config; - - config.nodeConfig = ma_node_config_init(); - config.hishelf = ma_hishelf2_config_init(ma_format_f32, channels, sampleRate, gainDB, q, frequency); - - return config; -} - -static void ma_hishelf_node_process_pcm_frames(ma_node* pNode, const float** ppFramesIn, ma_uint32* pFrameCountIn, float** ppFramesOut, ma_uint32* pFrameCountOut) -{ - ma_hishelf_node* pBPFNode = (ma_hishelf_node*)pNode; - - MA_ASSERT(pNode != NULL); - (void)pFrameCountIn; - - ma_hishelf2_process_pcm_frames(&pBPFNode->hishelf, ppFramesOut[0], ppFramesIn[0], *pFrameCountOut); -} - -static ma_node_vtable g_ma_hishelf_node_vtable = -{ - ma_hishelf_node_process_pcm_frames, - NULL, /* onGetRequiredInputFrameCount */ - 1, /* One input. */ - 1, /* One output. */ - 0 /* Default flags. */ -}; - -MA_API ma_result ma_hishelf_node_init(ma_node_graph* pNodeGraph, const ma_hishelf_node_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_hishelf_node* pNode) -{ - ma_result result; - ma_node_config baseNodeConfig; - - if (pNode == NULL) { - return MA_INVALID_ARGS; - } - - MA_ZERO_OBJECT(pNode); - - if (pConfig == NULL) { - return MA_INVALID_ARGS; - } - - if (pConfig->hishelf.format != ma_format_f32) { - return MA_INVALID_ARGS; /* The format must be f32. */ - } - - result = ma_hishelf2_init(&pConfig->hishelf, pAllocationCallbacks, &pNode->hishelf); - if (result != MA_SUCCESS) { - return result; - } - - baseNodeConfig = ma_node_config_init(); - baseNodeConfig.vtable = &g_ma_hishelf_node_vtable; - baseNodeConfig.pInputChannels = &pConfig->hishelf.channels; - baseNodeConfig.pOutputChannels = &pConfig->hishelf.channels; - - result = ma_node_init(pNodeGraph, &baseNodeConfig, pAllocationCallbacks, pNode); - if (result != MA_SUCCESS) { - return result; - } - - return result; -} - -MA_API ma_result ma_hishelf_node_reinit(const ma_hishelf_config* pConfig, ma_hishelf_node* pNode) -{ - ma_hishelf_node* pHishelfNode = (ma_hishelf_node*)pNode; - - if (pNode == NULL) { - return MA_INVALID_ARGS; - } - - return ma_hishelf2_reinit(pConfig, &pHishelfNode->hishelf); -} - -MA_API void ma_hishelf_node_uninit(ma_hishelf_node* pNode, const ma_allocation_callbacks* pAllocationCallbacks) -{ - ma_hishelf_node* pHishelfNode = (ma_hishelf_node*)pNode; - - if (pNode == NULL) { - return; - } - - ma_node_uninit(pNode, pAllocationCallbacks); - ma_hishelf2_uninit(&pHishelfNode->hishelf, pAllocationCallbacks); -} - - - - -MA_API ma_delay_node_config ma_delay_node_config_init(ma_uint32 channels, ma_uint32 sampleRate, ma_uint32 delayInFrames, float decay) -{ - ma_delay_node_config config; - - config.nodeConfig = ma_node_config_init(); - config.delay = ma_delay_config_init(channels, sampleRate, delayInFrames, decay); - - return config; -} - - -static void ma_delay_node_process_pcm_frames(ma_node* pNode, const float** ppFramesIn, ma_uint32* pFrameCountIn, float** ppFramesOut, ma_uint32* pFrameCountOut) -{ - ma_delay_node* pDelayNode = (ma_delay_node*)pNode; - - (void)pFrameCountIn; - - ma_delay_process_pcm_frames(&pDelayNode->delay, ppFramesOut[0], ppFramesIn[0], *pFrameCountOut); -} - -static ma_node_vtable g_ma_delay_node_vtable = -{ - ma_delay_node_process_pcm_frames, - NULL, - 1, /* 1 input channels. */ - 1, /* 1 output channel. */ - MA_NODE_FLAG_CONTINUOUS_PROCESSING /* Delay requires continuous processing to ensure the tail get's processed. */ -}; - -MA_API ma_result ma_delay_node_init(ma_node_graph* pNodeGraph, const ma_delay_node_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_delay_node* pDelayNode) -{ - ma_result result; - ma_node_config baseConfig; - - if (pDelayNode == NULL) { - return MA_INVALID_ARGS; - } - - MA_ZERO_OBJECT(pDelayNode); - - result = ma_delay_init(&pConfig->delay, pAllocationCallbacks, &pDelayNode->delay); - if (result != MA_SUCCESS) { - return result; - } - - baseConfig = pConfig->nodeConfig; - baseConfig.vtable = &g_ma_delay_node_vtable; - baseConfig.pInputChannels = &pConfig->delay.channels; - baseConfig.pOutputChannels = &pConfig->delay.channels; - - result = ma_node_init(pNodeGraph, &baseConfig, pAllocationCallbacks, &pDelayNode->baseNode); - if (result != MA_SUCCESS) { - ma_delay_uninit(&pDelayNode->delay, pAllocationCallbacks); - return result; - } - - return result; -} - -MA_API void ma_delay_node_uninit(ma_delay_node* pDelayNode, const ma_allocation_callbacks* pAllocationCallbacks) -{ - if (pDelayNode == NULL) { - return; - } - - /* The base node is always uninitialized first. */ - ma_node_uninit(pDelayNode, pAllocationCallbacks); - ma_delay_uninit(&pDelayNode->delay, pAllocationCallbacks); -} - -MA_API void ma_delay_node_set_wet(ma_delay_node* pDelayNode, float value) -{ - if (pDelayNode == NULL) { - return; - } - - ma_delay_set_wet(&pDelayNode->delay, value); -} - -MA_API float ma_delay_node_get_wet(const ma_delay_node* pDelayNode) -{ - if (pDelayNode == NULL) { - return 0; - } - - return ma_delay_get_wet(&pDelayNode->delay); -} - -MA_API void ma_delay_node_set_dry(ma_delay_node* pDelayNode, float value) -{ - if (pDelayNode == NULL) { - return; - } - - ma_delay_set_dry(&pDelayNode->delay, value); -} - -MA_API float ma_delay_node_get_dry(const ma_delay_node* pDelayNode) -{ - if (pDelayNode == NULL) { - return 0; - } - - return ma_delay_get_dry(&pDelayNode->delay); -} - -MA_API void ma_delay_node_set_decay(ma_delay_node* pDelayNode, float value) -{ - if (pDelayNode == NULL) { - return; - } - - ma_delay_set_decay(&pDelayNode->delay, value); -} - -MA_API float ma_delay_node_get_decay(const ma_delay_node* pDelayNode) -{ - if (pDelayNode == NULL) { - return 0; - } - - return ma_delay_get_decay(&pDelayNode->delay); -} -#endif /* MA_NO_NODE_GRAPH */ - - -/* SECTION: miniaudio_engine.c */ -#if !defined(MA_NO_ENGINE) && !defined(MA_NO_NODE_GRAPH) -/************************************************************************************************************************************************************** - -Engine - -**************************************************************************************************************************************************************/ -#define MA_SEEK_TARGET_NONE (~(ma_uint64)0) - - -static void ma_sound_set_at_end(ma_sound* pSound, ma_bool32 atEnd) -{ - MA_ASSERT(pSound != NULL); - ma_atomic_exchange_32(&pSound->atEnd, atEnd); - - /* Fire any callbacks or events. */ - if (atEnd) { - if (pSound->endCallback != NULL) { - pSound->endCallback(pSound->pEndCallbackUserData, pSound); - } - } -} - -static ma_bool32 ma_sound_get_at_end(const ma_sound* pSound) -{ - MA_ASSERT(pSound != NULL); - return ma_atomic_load_32(&pSound->atEnd); -} - - -MA_API ma_engine_node_config ma_engine_node_config_init(ma_engine* pEngine, ma_engine_node_type type, ma_uint32 flags) -{ - ma_engine_node_config config; - - MA_ZERO_OBJECT(&config); - config.pEngine = pEngine; - config.type = type; - config.isPitchDisabled = (flags & MA_SOUND_FLAG_NO_PITCH) != 0; - config.isSpatializationDisabled = (flags & MA_SOUND_FLAG_NO_SPATIALIZATION) != 0; - config.monoExpansionMode = pEngine->monoExpansionMode; - - return config; -} - - -static void ma_engine_node_update_pitch_if_required(ma_engine_node* pEngineNode) -{ - ma_bool32 isUpdateRequired = MA_FALSE; - float newPitch; - - MA_ASSERT(pEngineNode != NULL); - - newPitch = ma_atomic_load_explicit_f32(&pEngineNode->pitch, ma_atomic_memory_order_acquire); - - if (pEngineNode->oldPitch != newPitch) { - pEngineNode->oldPitch = newPitch; - isUpdateRequired = MA_TRUE; - } - - if (pEngineNode->oldDopplerPitch != pEngineNode->spatializer.dopplerPitch) { - pEngineNode->oldDopplerPitch = pEngineNode->spatializer.dopplerPitch; - isUpdateRequired = MA_TRUE; - } - - if (isUpdateRequired) { - float basePitch = (float)pEngineNode->sampleRate / ma_engine_get_sample_rate(pEngineNode->pEngine); - ma_linear_resampler_set_rate_ratio(&pEngineNode->resampler, basePitch * pEngineNode->oldPitch * pEngineNode->oldDopplerPitch); - } -} - -static ma_bool32 ma_engine_node_is_pitching_enabled(const ma_engine_node* pEngineNode) -{ - MA_ASSERT(pEngineNode != NULL); - - /* Don't try to be clever by skipping resampling in the pitch=1 case or else you'll glitch when moving away from 1. */ - return !ma_atomic_load_explicit_32(&pEngineNode->isPitchDisabled, ma_atomic_memory_order_acquire); -} - -static ma_bool32 ma_engine_node_is_spatialization_enabled(const ma_engine_node* pEngineNode) -{ - MA_ASSERT(pEngineNode != NULL); - - return !ma_atomic_load_explicit_32(&pEngineNode->isSpatializationDisabled, ma_atomic_memory_order_acquire); -} - -static ma_uint64 ma_engine_node_get_required_input_frame_count(const ma_engine_node* pEngineNode, ma_uint64 outputFrameCount) -{ - ma_uint64 inputFrameCount = 0; - - if (ma_engine_node_is_pitching_enabled(pEngineNode)) { - ma_result result = ma_linear_resampler_get_required_input_frame_count(&pEngineNode->resampler, outputFrameCount, &inputFrameCount); - if (result != MA_SUCCESS) { - inputFrameCount = 0; - } - } else { - inputFrameCount = outputFrameCount; /* No resampling, so 1:1. */ - } - - return inputFrameCount; -} - -static ma_result ma_engine_node_set_volume(ma_engine_node* pEngineNode, float volume) -{ - if (pEngineNode == NULL) { - return MA_INVALID_ARGS; - } - - ma_atomic_float_set(&pEngineNode->volume, volume); - - /* If we're not smoothing we should bypass the volume gainer entirely. */ - if (pEngineNode->volumeSmoothTimeInPCMFrames == 0) { - /* We should always have an active spatializer because it can be enabled and disabled dynamically. We can just use that for holding our volume. */ - ma_spatializer_set_master_volume(&pEngineNode->spatializer, volume); - } else { - /* We're using volume smoothing, so apply the master volume to the gainer. */ - ma_gainer_set_gain(&pEngineNode->volumeGainer, volume); - } - - return MA_SUCCESS; -} - -static ma_result ma_engine_node_get_volume(const ma_engine_node* pEngineNode, float* pVolume) -{ - if (pVolume == NULL) { - return MA_INVALID_ARGS; - } - - *pVolume = 0.0f; - - if (pEngineNode == NULL) { - return MA_INVALID_ARGS; - } - - *pVolume = ma_atomic_float_get((ma_atomic_float*)&pEngineNode->volume); - - return MA_SUCCESS; -} - - -static void ma_engine_node_process_pcm_frames__general(ma_engine_node* pEngineNode, const float** ppFramesIn, ma_uint32* pFrameCountIn, float** ppFramesOut, ma_uint32* pFrameCountOut) -{ - ma_uint32 frameCountIn; - ma_uint32 frameCountOut; - ma_uint32 totalFramesProcessedIn; - ma_uint32 totalFramesProcessedOut; - ma_uint32 channelsIn; - ma_uint32 channelsOut; - ma_bool32 isPitchingEnabled; - ma_bool32 isFadingEnabled; - ma_bool32 isSpatializationEnabled; - ma_bool32 isPanningEnabled; - ma_bool32 isVolumeSmoothingEnabled; - - frameCountIn = *pFrameCountIn; - frameCountOut = *pFrameCountOut; - - channelsIn = ma_spatializer_get_input_channels(&pEngineNode->spatializer); - channelsOut = ma_spatializer_get_output_channels(&pEngineNode->spatializer); - - totalFramesProcessedIn = 0; - totalFramesProcessedOut = 0; - - /* Update the fader if applicable. */ - { - ma_uint64 fadeLengthInFrames = ma_atomic_uint64_get(&pEngineNode->fadeSettings.fadeLengthInFrames); - if (fadeLengthInFrames != ~(ma_uint64)0) { - float fadeVolumeBeg = ma_atomic_float_get(&pEngineNode->fadeSettings.volumeBeg); - float fadeVolumeEnd = ma_atomic_float_get(&pEngineNode->fadeSettings.volumeEnd); - ma_int64 fadeStartOffsetInFrames = (ma_int64)ma_atomic_uint64_get(&pEngineNode->fadeSettings.absoluteGlobalTimeInFrames); - if (fadeStartOffsetInFrames == (ma_int64)(~(ma_uint64)0)) { - fadeStartOffsetInFrames = 0; - } else { - fadeStartOffsetInFrames -= ma_engine_get_time_in_pcm_frames(pEngineNode->pEngine); - } - - ma_fader_set_fade_ex(&pEngineNode->fader, fadeVolumeBeg, fadeVolumeEnd, fadeLengthInFrames, fadeStartOffsetInFrames); - - /* Reset the fade length so we don't erroneously apply it again. */ - ma_atomic_uint64_set(&pEngineNode->fadeSettings.fadeLengthInFrames, ~(ma_uint64)0); - } - } - - isPitchingEnabled = ma_engine_node_is_pitching_enabled(pEngineNode); - isFadingEnabled = pEngineNode->fader.volumeBeg != 1 || pEngineNode->fader.volumeEnd != 1; - isSpatializationEnabled = ma_engine_node_is_spatialization_enabled(pEngineNode); - isPanningEnabled = pEngineNode->panner.pan != 0 && channelsOut != 1; - isVolumeSmoothingEnabled = pEngineNode->volumeSmoothTimeInPCMFrames > 0; - - /* Keep going while we've still got data available for processing. */ - while (totalFramesProcessedOut < frameCountOut) { - /* - We need to process in a specific order. We always do resampling first because it's likely - we're going to be increasing the channel count after spatialization. Also, I want to do - fading based on the output sample rate. - - We'll first read into a buffer from the resampler. Then we'll do all processing that - operates on the on the input channel count. We'll then get the spatializer to output to - the output buffer and then do all effects from that point directly in the output buffer - in-place. - - Note that we're always running the resampler if pitching is enabled, even when the pitch - is 1. If we try to be clever and skip resampling when the pitch is 1, we'll get a glitch - when we move away from 1, back to 1, and then away from 1 again. We'll want to implement - any pitch=1 optimizations in the resampler itself. - - There's a small optimization here that we'll utilize since it might be a fairly common - case. When the input and output channel counts are the same, we'll read straight into the - output buffer from the resampler and do everything in-place. - */ - const float* pRunningFramesIn; - float* pRunningFramesOut; - float* pWorkingBuffer; /* This is the buffer that we'll be processing frames in. This is in input channels. */ - float temp[MA_DATA_CONVERTER_STACK_BUFFER_SIZE / sizeof(float)]; - ma_uint32 tempCapInFrames = ma_countof(temp) / channelsIn; - ma_uint32 framesAvailableIn; - ma_uint32 framesAvailableOut; - ma_uint32 framesJustProcessedIn; - ma_uint32 framesJustProcessedOut; - ma_bool32 isWorkingBufferValid = MA_FALSE; - - framesAvailableIn = frameCountIn - totalFramesProcessedIn; - framesAvailableOut = frameCountOut - totalFramesProcessedOut; - - pRunningFramesIn = ma_offset_pcm_frames_const_ptr_f32(ppFramesIn[0], totalFramesProcessedIn, channelsIn); - pRunningFramesOut = ma_offset_pcm_frames_ptr_f32(ppFramesOut[0], totalFramesProcessedOut, channelsOut); - - if (channelsIn == channelsOut) { - /* Fast path. Channel counts are the same. No need for an intermediary input buffer. */ - pWorkingBuffer = pRunningFramesOut; - } else { - /* Slow path. Channel counts are different. Need to use an intermediary input buffer. */ - pWorkingBuffer = temp; - if (framesAvailableOut > tempCapInFrames) { - framesAvailableOut = tempCapInFrames; - } - } - - /* First is resampler. */ - if (isPitchingEnabled) { - ma_uint64 resampleFrameCountIn = framesAvailableIn; - ma_uint64 resampleFrameCountOut = framesAvailableOut; - - ma_linear_resampler_process_pcm_frames(&pEngineNode->resampler, pRunningFramesIn, &resampleFrameCountIn, pWorkingBuffer, &resampleFrameCountOut); - isWorkingBufferValid = MA_TRUE; - - framesJustProcessedIn = (ma_uint32)resampleFrameCountIn; - framesJustProcessedOut = (ma_uint32)resampleFrameCountOut; - } else { - framesJustProcessedIn = ma_min(framesAvailableIn, framesAvailableOut); - framesJustProcessedOut = framesJustProcessedIn; /* When no resampling is being performed, the number of output frames is the same as input frames. */ - } - - /* Fading. */ - if (isFadingEnabled) { - if (isWorkingBufferValid) { - ma_fader_process_pcm_frames(&pEngineNode->fader, pWorkingBuffer, pWorkingBuffer, framesJustProcessedOut); /* In-place processing. */ - } else { - ma_fader_process_pcm_frames(&pEngineNode->fader, pWorkingBuffer, pRunningFramesIn, framesJustProcessedOut); - isWorkingBufferValid = MA_TRUE; - } - } - - /* - If we're using smoothing, we won't be applying volume via the spatializer, but instead from a ma_gainer. In this case - we'll want to apply our volume now. - */ - if (isVolumeSmoothingEnabled) { - if (isWorkingBufferValid) { - ma_gainer_process_pcm_frames(&pEngineNode->volumeGainer, pWorkingBuffer, pWorkingBuffer, framesJustProcessedOut); - } else { - ma_gainer_process_pcm_frames(&pEngineNode->volumeGainer, pWorkingBuffer, pRunningFramesIn, framesJustProcessedOut); - isWorkingBufferValid = MA_TRUE; - } - } - - /* - If at this point we still haven't actually done anything with the working buffer we need - to just read straight from the input buffer. - */ - if (isWorkingBufferValid == MA_FALSE) { - pWorkingBuffer = (float*)pRunningFramesIn; /* Naughty const cast, but it's safe at this point because we won't ever be writing to it from this point out. */ - } - - /* Spatialization. */ - if (isSpatializationEnabled) { - ma_uint32 iListener; - - /* - When determining the listener to use, we first check to see if the sound is pinned to a - specific listener. If so, we use that. Otherwise we just use the closest listener. - */ - if (pEngineNode->pinnedListenerIndex != MA_LISTENER_INDEX_CLOSEST && pEngineNode->pinnedListenerIndex < ma_engine_get_listener_count(pEngineNode->pEngine)) { - iListener = pEngineNode->pinnedListenerIndex; - } else { - ma_vec3f spatializerPosition = ma_spatializer_get_position(&pEngineNode->spatializer); - iListener = ma_engine_find_closest_listener(pEngineNode->pEngine, spatializerPosition.x, spatializerPosition.y, spatializerPosition.z); - } - - ma_spatializer_process_pcm_frames(&pEngineNode->spatializer, &pEngineNode->pEngine->listeners[iListener], pRunningFramesOut, pWorkingBuffer, framesJustProcessedOut); - } else { - /* No spatialization, but we still need to do channel conversion and master volume. */ - float volume; - ma_engine_node_get_volume(pEngineNode, &volume); /* Should never fail. */ - - if (channelsIn == channelsOut) { - /* No channel conversion required. Just copy straight to the output buffer. */ - if (isVolumeSmoothingEnabled) { - /* Volume has already been applied. Just copy straight to the output buffer. */ - ma_copy_pcm_frames(pRunningFramesOut, pWorkingBuffer, framesJustProcessedOut * channelsOut, ma_format_f32, channelsOut); - } else { - /* Volume has not been applied yet. Copy and apply volume in the same pass. */ - ma_copy_and_apply_volume_factor_f32(pRunningFramesOut, pWorkingBuffer, framesJustProcessedOut * channelsOut, volume); - } - } else { - /* Channel conversion required. TODO: Add support for channel maps here. */ - ma_channel_map_apply_f32(pRunningFramesOut, NULL, channelsOut, pWorkingBuffer, NULL, channelsIn, framesJustProcessedOut, ma_channel_mix_mode_simple, pEngineNode->monoExpansionMode); - - /* If we're using smoothing, the volume will have already been applied. */ - if (!isVolumeSmoothingEnabled) { - ma_apply_volume_factor_f32(pRunningFramesOut, framesJustProcessedOut * channelsOut, volume); - } - } - } - - /* At this point we can guarantee that the output buffer contains valid data. We can process everything in place now. */ - - /* Panning. */ - if (isPanningEnabled) { - ma_panner_process_pcm_frames(&pEngineNode->panner, pRunningFramesOut, pRunningFramesOut, framesJustProcessedOut); /* In-place processing. */ - } - - /* We're done for this chunk. */ - totalFramesProcessedIn += framesJustProcessedIn; - totalFramesProcessedOut += framesJustProcessedOut; - - /* If we didn't process any output frames this iteration it means we've either run out of input data, or run out of room in the output buffer. */ - if (framesJustProcessedOut == 0) { - break; - } - } - - /* At this point we're done processing. */ - *pFrameCountIn = totalFramesProcessedIn; - *pFrameCountOut = totalFramesProcessedOut; -} - -static void ma_engine_node_process_pcm_frames__sound(ma_node* pNode, const float** ppFramesIn, ma_uint32* pFrameCountIn, float** ppFramesOut, ma_uint32* pFrameCountOut) -{ - /* For sounds, we need to first read from the data source. Then we need to apply the engine effects (pan, pitch, fades, etc.). */ - ma_result result = MA_SUCCESS; - ma_sound* pSound = (ma_sound*)pNode; - ma_uint32 frameCount = *pFrameCountOut; - ma_uint32 totalFramesRead = 0; - ma_format dataSourceFormat; - ma_uint32 dataSourceChannels; - ma_uint8 temp[MA_DATA_CONVERTER_STACK_BUFFER_SIZE]; - ma_uint32 tempCapInFrames; - ma_uint64 seekTarget; - - /* This is a data source node which means no input buses. */ - (void)ppFramesIn; - (void)pFrameCountIn; - - /* If we're marked at the end we need to stop the sound and do nothing. */ - if (ma_sound_at_end(pSound)) { - ma_sound_stop(pSound); - *pFrameCountOut = 0; - return; - } - - /* If we're seeking, do so now before reading. */ - seekTarget = ma_atomic_load_64(&pSound->seekTarget); - if (seekTarget != MA_SEEK_TARGET_NONE) { - ma_data_source_seek_to_pcm_frame(pSound->pDataSource, seekTarget); - - /* Any time-dependant effects need to have their times updated. */ - ma_node_set_time(pSound, seekTarget); - - ma_atomic_exchange_64(&pSound->seekTarget, MA_SEEK_TARGET_NONE); - } - - /* - We want to update the pitch once. For sounds, this can be either at the start or at the end. If - we don't force this to only ever be updating once, we could end up in a situation where - retrieving the required input frame count ends up being different to what we actually retrieve. - What could happen is that the required input frame count is calculated, the pitch is update, - and then this processing function is called resulting in a different number of input frames - being processed. Do not call this in ma_engine_node_process_pcm_frames__general() or else - you'll hit the aforementioned bug. - */ - ma_engine_node_update_pitch_if_required(&pSound->engineNode); - - /* - For the convenience of the caller, we're doing to allow data sources to use non-floating-point formats and channel counts that differ - from the main engine. - */ - result = ma_data_source_get_data_format(pSound->pDataSource, &dataSourceFormat, &dataSourceChannels, NULL, NULL, 0); - if (result == MA_SUCCESS) { - tempCapInFrames = sizeof(temp) / ma_get_bytes_per_frame(dataSourceFormat, dataSourceChannels); - - /* Keep reading until we've read as much as was requested or we reach the end of the data source. */ - while (totalFramesRead < frameCount) { - ma_uint32 framesRemaining = frameCount - totalFramesRead; - ma_uint32 framesToRead; - ma_uint64 framesJustRead; - ma_uint32 frameCountIn; - ma_uint32 frameCountOut; - const float* pRunningFramesIn; - float* pRunningFramesOut; - - /* - The first thing we need to do is read into the temporary buffer. We can calculate exactly - how many input frames we'll need after resampling. - */ - framesToRead = (ma_uint32)ma_engine_node_get_required_input_frame_count(&pSound->engineNode, framesRemaining); - if (framesToRead > tempCapInFrames) { - framesToRead = tempCapInFrames; - } - - result = ma_data_source_read_pcm_frames(pSound->pDataSource, temp, framesToRead, &framesJustRead); - - /* If we reached the end of the sound we'll want to mark it as at the end and stop it. This should never be returned for looping sounds. */ - if (result == MA_AT_END) { - ma_sound_set_at_end(pSound, MA_TRUE); /* This will be set to false in ma_sound_start(). */ - } - - pRunningFramesOut = ma_offset_pcm_frames_ptr_f32(ppFramesOut[0], totalFramesRead, ma_node_get_output_channels(pNode, 0)); - - frameCountIn = (ma_uint32)framesJustRead; - frameCountOut = framesRemaining; - - /* Convert if necessary. */ - if (dataSourceFormat == ma_format_f32) { - /* Fast path. No data conversion necessary. */ - pRunningFramesIn = (float*)temp; - ma_engine_node_process_pcm_frames__general(&pSound->engineNode, &pRunningFramesIn, &frameCountIn, &pRunningFramesOut, &frameCountOut); - } else { - /* Slow path. Need to do sample format conversion to f32. If we give the f32 buffer the same count as the first temp buffer, we're guaranteed it'll be large enough. */ - float tempf32[MA_DATA_CONVERTER_STACK_BUFFER_SIZE]; /* Do not do `MA_DATA_CONVERTER_STACK_BUFFER_SIZE/sizeof(float)` here like we've done in other places. */ - ma_convert_pcm_frames_format(tempf32, ma_format_f32, temp, dataSourceFormat, framesJustRead, dataSourceChannels, ma_dither_mode_none); - - /* Now that we have our samples in f32 format we can process like normal. */ - pRunningFramesIn = tempf32; - ma_engine_node_process_pcm_frames__general(&pSound->engineNode, &pRunningFramesIn, &frameCountIn, &pRunningFramesOut, &frameCountOut); - } - - /* We should have processed all of our input frames since we calculated the required number of input frames at the top. */ - MA_ASSERT(frameCountIn == framesJustRead); - totalFramesRead += (ma_uint32)frameCountOut; /* Safe cast. */ - - if (result != MA_SUCCESS || ma_sound_at_end(pSound)) { - break; /* Might have reached the end. */ - } - } - } - - *pFrameCountOut = totalFramesRead; -} - -static void ma_engine_node_process_pcm_frames__group(ma_node* pNode, const float** ppFramesIn, ma_uint32* pFrameCountIn, float** ppFramesOut, ma_uint32* pFrameCountOut) -{ - /* - Make sure the pitch is updated before trying to read anything. It's important that this is done - only once and not in ma_engine_node_process_pcm_frames__general(). The reason for this is that - ma_engine_node_process_pcm_frames__general() will call ma_engine_node_get_required_input_frame_count(), - and if another thread modifies the pitch just after that call it can result in a glitch due to - the input rate changing. - */ - ma_engine_node_update_pitch_if_required((ma_engine_node*)pNode); - - /* For groups, the input data has already been read and we just need to apply the effect. */ - ma_engine_node_process_pcm_frames__general((ma_engine_node*)pNode, ppFramesIn, pFrameCountIn, ppFramesOut, pFrameCountOut); -} - -static ma_result ma_engine_node_get_required_input_frame_count__group(ma_node* pNode, ma_uint32 outputFrameCount, ma_uint32* pInputFrameCount) -{ - ma_uint64 inputFrameCount; - - MA_ASSERT(pInputFrameCount != NULL); - - /* Our pitch will affect this calculation. We need to update it. */ - ma_engine_node_update_pitch_if_required((ma_engine_node*)pNode); - - inputFrameCount = ma_engine_node_get_required_input_frame_count((ma_engine_node*)pNode, outputFrameCount); - if (inputFrameCount > 0xFFFFFFFF) { - inputFrameCount = 0xFFFFFFFF; /* Will never happen because miniaudio will only ever process in relatively small chunks. */ - } - - *pInputFrameCount = (ma_uint32)inputFrameCount; - - return MA_SUCCESS; -} - - -static ma_node_vtable g_ma_engine_node_vtable__sound = -{ - ma_engine_node_process_pcm_frames__sound, - NULL, /* onGetRequiredInputFrameCount */ - 0, /* Sounds are data source nodes which means they have zero inputs (their input is drawn from the data source itself). */ - 1, /* Sounds have one output bus. */ - 0 /* Default flags. */ -}; - -static ma_node_vtable g_ma_engine_node_vtable__group = -{ - ma_engine_node_process_pcm_frames__group, - ma_engine_node_get_required_input_frame_count__group, - 1, /* Groups have one input bus. */ - 1, /* Groups have one output bus. */ - MA_NODE_FLAG_DIFFERENT_PROCESSING_RATES /* The engine node does resampling so should let miniaudio know about it. */ -}; - - - -static ma_node_config ma_engine_node_base_node_config_init(const ma_engine_node_config* pConfig) -{ - ma_node_config baseNodeConfig; - - if (pConfig->type == ma_engine_node_type_sound) { - /* Sound. */ - baseNodeConfig = ma_node_config_init(); - baseNodeConfig.vtable = &g_ma_engine_node_vtable__sound; - baseNodeConfig.initialState = ma_node_state_stopped; /* Sounds are stopped by default. */ - } else { - /* Group. */ - baseNodeConfig = ma_node_config_init(); - baseNodeConfig.vtable = &g_ma_engine_node_vtable__group; - baseNodeConfig.initialState = ma_node_state_started; /* Groups are started by default. */ - } - - return baseNodeConfig; -} - -static ma_spatializer_config ma_engine_node_spatializer_config_init(const ma_node_config* pBaseNodeConfig) -{ - return ma_spatializer_config_init(pBaseNodeConfig->pInputChannels[0], pBaseNodeConfig->pOutputChannels[0]); -} - -typedef struct -{ - size_t sizeInBytes; - size_t baseNodeOffset; - size_t resamplerOffset; - size_t spatializerOffset; - size_t gainerOffset; -} ma_engine_node_heap_layout; - -static ma_result ma_engine_node_get_heap_layout(const ma_engine_node_config* pConfig, ma_engine_node_heap_layout* pHeapLayout) -{ - ma_result result; - size_t tempHeapSize; - ma_node_config baseNodeConfig; - ma_linear_resampler_config resamplerConfig; - ma_spatializer_config spatializerConfig; - ma_gainer_config gainerConfig; - ma_uint32 channelsIn; - ma_uint32 channelsOut; - ma_channel defaultStereoChannelMap[2] = {MA_CHANNEL_SIDE_LEFT, MA_CHANNEL_SIDE_RIGHT}; /* <-- Consistent with the default channel map of a stereo listener. Means channel conversion can run on a fast path. */ - - MA_ASSERT(pHeapLayout); - - MA_ZERO_OBJECT(pHeapLayout); - - if (pConfig == NULL) { - return MA_INVALID_ARGS; - } - - if (pConfig->pEngine == NULL) { - return MA_INVALID_ARGS; /* An engine must be specified. */ - } - - pHeapLayout->sizeInBytes = 0; - - channelsIn = (pConfig->channelsIn != 0) ? pConfig->channelsIn : ma_engine_get_channels(pConfig->pEngine); - channelsOut = (pConfig->channelsOut != 0) ? pConfig->channelsOut : ma_engine_get_channels(pConfig->pEngine); - - - /* Base node. */ - baseNodeConfig = ma_engine_node_base_node_config_init(pConfig); - baseNodeConfig.pInputChannels = &channelsIn; - baseNodeConfig.pOutputChannels = &channelsOut; - - result = ma_node_get_heap_size(ma_engine_get_node_graph(pConfig->pEngine), &baseNodeConfig, &tempHeapSize); - if (result != MA_SUCCESS) { - return result; /* Failed to retrieve the size of the heap for the base node. */ - } - - pHeapLayout->baseNodeOffset = pHeapLayout->sizeInBytes; - pHeapLayout->sizeInBytes += ma_align_64(tempHeapSize); - - - /* Resmapler. */ - resamplerConfig = ma_linear_resampler_config_init(ma_format_f32, channelsIn, 1, 1); /* Input and output sample rates don't affect the calculation of the heap size. */ - resamplerConfig.lpfOrder = 0; - - result = ma_linear_resampler_get_heap_size(&resamplerConfig, &tempHeapSize); - if (result != MA_SUCCESS) { - return result; /* Failed to retrieve the size of the heap for the resampler. */ - } - - pHeapLayout->resamplerOffset = pHeapLayout->sizeInBytes; - pHeapLayout->sizeInBytes += ma_align_64(tempHeapSize); - - - /* Spatializer. */ - spatializerConfig = ma_engine_node_spatializer_config_init(&baseNodeConfig); - - if (spatializerConfig.channelsIn == 2) { - spatializerConfig.pChannelMapIn = defaultStereoChannelMap; - } - - result = ma_spatializer_get_heap_size(&spatializerConfig, &tempHeapSize); - if (result != MA_SUCCESS) { - return result; /* Failed to retrieve the size of the heap for the spatializer. */ - } - - pHeapLayout->spatializerOffset = pHeapLayout->sizeInBytes; - pHeapLayout->sizeInBytes += ma_align_64(tempHeapSize); - - - /* Gainer. Will not be used if we are not using smoothing. */ - if (pConfig->volumeSmoothTimeInPCMFrames > 0) { - gainerConfig = ma_gainer_config_init(channelsIn, pConfig->volumeSmoothTimeInPCMFrames); - - result = ma_gainer_get_heap_size(&gainerConfig, &tempHeapSize); - if (result != MA_SUCCESS) { - return result; - } - - pHeapLayout->gainerOffset = pHeapLayout->sizeInBytes; - pHeapLayout->sizeInBytes += ma_align_64(tempHeapSize); - } - - - return MA_SUCCESS; -} - -MA_API ma_result ma_engine_node_get_heap_size(const ma_engine_node_config* pConfig, size_t* pHeapSizeInBytes) -{ - ma_result result; - ma_engine_node_heap_layout heapLayout; - - if (pHeapSizeInBytes == NULL) { - return MA_INVALID_ARGS; - } - - *pHeapSizeInBytes = 0; - - result = ma_engine_node_get_heap_layout(pConfig, &heapLayout); - if (result != MA_SUCCESS) { - return result; - } - - *pHeapSizeInBytes = heapLayout.sizeInBytes; - - return MA_SUCCESS; -} - -MA_API ma_result ma_engine_node_init_preallocated(const ma_engine_node_config* pConfig, void* pHeap, ma_engine_node* pEngineNode) -{ - ma_result result; - ma_engine_node_heap_layout heapLayout; - ma_node_config baseNodeConfig; - ma_linear_resampler_config resamplerConfig; - ma_fader_config faderConfig; - ma_spatializer_config spatializerConfig; - ma_panner_config pannerConfig; - ma_gainer_config gainerConfig; - ma_uint32 channelsIn; - ma_uint32 channelsOut; - ma_channel defaultStereoChannelMap[2] = {MA_CHANNEL_SIDE_LEFT, MA_CHANNEL_SIDE_RIGHT}; /* <-- Consistent with the default channel map of a stereo listener. Means channel conversion can run on a fast path. */ - - if (pEngineNode == NULL) { - return MA_INVALID_ARGS; - } - - MA_ZERO_OBJECT(pEngineNode); - - result = ma_engine_node_get_heap_layout(pConfig, &heapLayout); - if (result != MA_SUCCESS) { - return result; - } - - if (pConfig->pinnedListenerIndex != MA_LISTENER_INDEX_CLOSEST && pConfig->pinnedListenerIndex >= ma_engine_get_listener_count(pConfig->pEngine)) { - return MA_INVALID_ARGS; /* Invalid listener. */ - } - - pEngineNode->_pHeap = pHeap; - MA_ZERO_MEMORY(pHeap, heapLayout.sizeInBytes); - - pEngineNode->pEngine = pConfig->pEngine; - pEngineNode->sampleRate = (pConfig->sampleRate > 0) ? pConfig->sampleRate : ma_engine_get_sample_rate(pEngineNode->pEngine); - pEngineNode->volumeSmoothTimeInPCMFrames = pConfig->volumeSmoothTimeInPCMFrames; - pEngineNode->monoExpansionMode = pConfig->monoExpansionMode; - ma_atomic_float_set(&pEngineNode->volume, 1); - pEngineNode->pitch = 1; - pEngineNode->oldPitch = 1; - pEngineNode->oldDopplerPitch = 1; - pEngineNode->isPitchDisabled = pConfig->isPitchDisabled; - pEngineNode->isSpatializationDisabled = pConfig->isSpatializationDisabled; - pEngineNode->pinnedListenerIndex = pConfig->pinnedListenerIndex; - ma_atomic_float_set(&pEngineNode->fadeSettings.volumeBeg, 1); - ma_atomic_float_set(&pEngineNode->fadeSettings.volumeEnd, 1); - ma_atomic_uint64_set(&pEngineNode->fadeSettings.fadeLengthInFrames, (~(ma_uint64)0)); - ma_atomic_uint64_set(&pEngineNode->fadeSettings.absoluteGlobalTimeInFrames, (~(ma_uint64)0)); /* <-- Indicates that the fade should start immediately. */ - - channelsIn = (pConfig->channelsIn != 0) ? pConfig->channelsIn : ma_engine_get_channels(pConfig->pEngine); - channelsOut = (pConfig->channelsOut != 0) ? pConfig->channelsOut : ma_engine_get_channels(pConfig->pEngine); - - /* - If the sample rate of the sound is different to the engine, make sure pitching is enabled so that the resampler - is activated. Not doing this will result in the sound not being resampled if MA_SOUND_FLAG_NO_PITCH is used. - */ - if (pEngineNode->sampleRate != ma_engine_get_sample_rate(pEngineNode->pEngine)) { - pEngineNode->isPitchDisabled = MA_FALSE; - } - - - /* Base node. */ - baseNodeConfig = ma_engine_node_base_node_config_init(pConfig); - baseNodeConfig.pInputChannels = &channelsIn; - baseNodeConfig.pOutputChannels = &channelsOut; - - result = ma_node_init_preallocated(&pConfig->pEngine->nodeGraph, &baseNodeConfig, ma_offset_ptr(pHeap, heapLayout.baseNodeOffset), &pEngineNode->baseNode); - if (result != MA_SUCCESS) { - goto error0; - } - - - /* - We can now initialize the effects we need in order to implement the engine node. There's a - defined order of operations here, mainly centered around when we convert our channels from the - data source's native channel count to the engine's channel count. As a rule, we want to do as - much computation as possible before spatialization because there's a chance that will increase - the channel count, thereby increasing the amount of work needing to be done to process. - */ - - /* We'll always do resampling first. */ - resamplerConfig = ma_linear_resampler_config_init(ma_format_f32, baseNodeConfig.pInputChannels[0], pEngineNode->sampleRate, ma_engine_get_sample_rate(pEngineNode->pEngine)); - resamplerConfig.lpfOrder = 0; /* <-- Need to disable low-pass filtering for pitch shifting for now because there's cases where the biquads are becoming unstable. Need to figure out a better fix for this. */ - - result = ma_linear_resampler_init_preallocated(&resamplerConfig, ma_offset_ptr(pHeap, heapLayout.resamplerOffset), &pEngineNode->resampler); - if (result != MA_SUCCESS) { - goto error1; - } - - - /* After resampling will come the fader. */ - faderConfig = ma_fader_config_init(ma_format_f32, baseNodeConfig.pInputChannels[0], ma_engine_get_sample_rate(pEngineNode->pEngine)); - - result = ma_fader_init(&faderConfig, &pEngineNode->fader); - if (result != MA_SUCCESS) { - goto error2; - } - - - /* - Spatialization comes next. We spatialize based on the node's output channel count. It's up the caller to - ensure channels counts link up correctly in the node graph. - */ - spatializerConfig = ma_engine_node_spatializer_config_init(&baseNodeConfig); - spatializerConfig.gainSmoothTimeInFrames = pEngineNode->pEngine->gainSmoothTimeInFrames; - - if (spatializerConfig.channelsIn == 2) { - spatializerConfig.pChannelMapIn = defaultStereoChannelMap; - } - - result = ma_spatializer_init_preallocated(&spatializerConfig, ma_offset_ptr(pHeap, heapLayout.spatializerOffset), &pEngineNode->spatializer); - if (result != MA_SUCCESS) { - goto error2; - } - - - /* - After spatialization comes panning. We need to do this after spatialization because otherwise we wouldn't - be able to pan mono sounds. - */ - pannerConfig = ma_panner_config_init(ma_format_f32, baseNodeConfig.pOutputChannels[0]); - - result = ma_panner_init(&pannerConfig, &pEngineNode->panner); - if (result != MA_SUCCESS) { - goto error3; - } - - - /* We'll need a gainer for smoothing out volume changes if we have a non-zero smooth time. We apply this before converting to the output channel count. */ - if (pConfig->volumeSmoothTimeInPCMFrames > 0) { - gainerConfig = ma_gainer_config_init(channelsIn, pConfig->volumeSmoothTimeInPCMFrames); - - result = ma_gainer_init_preallocated(&gainerConfig, ma_offset_ptr(pHeap, heapLayout.gainerOffset), &pEngineNode->volumeGainer); - if (result != MA_SUCCESS) { - goto error3; - } - } - - - return MA_SUCCESS; - - /* No need for allocation callbacks here because we use a preallocated heap. */ -error3: ma_spatializer_uninit(&pEngineNode->spatializer, NULL); -error2: ma_linear_resampler_uninit(&pEngineNode->resampler, NULL); -error1: ma_node_uninit(&pEngineNode->baseNode, NULL); -error0: return result; -} - -MA_API ma_result ma_engine_node_init(const ma_engine_node_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_engine_node* pEngineNode) -{ - ma_result result; - size_t heapSizeInBytes; - void* pHeap; - - result = ma_engine_node_get_heap_size(pConfig, &heapSizeInBytes); - if (result != MA_SUCCESS) { - return result; - } - - if (heapSizeInBytes > 0) { - pHeap = ma_malloc(heapSizeInBytes, pAllocationCallbacks); - if (pHeap == NULL) { - return MA_OUT_OF_MEMORY; - } - } else { - pHeap = NULL; - } - - result = ma_engine_node_init_preallocated(pConfig, pHeap, pEngineNode); - if (result != MA_SUCCESS) { - ma_free(pHeap, pAllocationCallbacks); - return result; - } - - pEngineNode->_ownsHeap = MA_TRUE; - return MA_SUCCESS; -} - -MA_API void ma_engine_node_uninit(ma_engine_node* pEngineNode, const ma_allocation_callbacks* pAllocationCallbacks) -{ - /* - The base node always needs to be uninitialized first to ensure it's detached from the graph completely before we - destroy anything that might be in the middle of being used by the processing function. - */ - ma_node_uninit(&pEngineNode->baseNode, pAllocationCallbacks); - - /* Now that the node has been uninitialized we can safely uninitialize the rest. */ - if (pEngineNode->volumeSmoothTimeInPCMFrames > 0) { - ma_gainer_uninit(&pEngineNode->volumeGainer, pAllocationCallbacks); - } - - ma_spatializer_uninit(&pEngineNode->spatializer, pAllocationCallbacks); - ma_linear_resampler_uninit(&pEngineNode->resampler, pAllocationCallbacks); - - /* Free the heap last. */ - if (pEngineNode->_ownsHeap) { - ma_free(pEngineNode->_pHeap, pAllocationCallbacks); - } -} - - -MA_API ma_sound_config ma_sound_config_init(void) -{ - return ma_sound_config_init_2(NULL); -} - -MA_API ma_sound_config ma_sound_config_init_2(ma_engine* pEngine) -{ - ma_sound_config config; - - MA_ZERO_OBJECT(&config); - - if (pEngine != NULL) { - config.monoExpansionMode = pEngine->monoExpansionMode; - } else { - config.monoExpansionMode = ma_mono_expansion_mode_default; - } - - config.rangeEndInPCMFrames = ~((ma_uint64)0); - config.loopPointEndInPCMFrames = ~((ma_uint64)0); - - return config; -} - -MA_API ma_sound_group_config ma_sound_group_config_init(void) -{ - return ma_sound_group_config_init_2(NULL); -} - -MA_API ma_sound_group_config ma_sound_group_config_init_2(ma_engine* pEngine) -{ - ma_sound_group_config config; - - MA_ZERO_OBJECT(&config); - - if (pEngine != NULL) { - config.monoExpansionMode = pEngine->monoExpansionMode; - } else { - config.monoExpansionMode = ma_mono_expansion_mode_default; - } - - return config; -} - - -MA_API ma_engine_config ma_engine_config_init(void) -{ - ma_engine_config config; - - MA_ZERO_OBJECT(&config); - config.listenerCount = 1; /* Always want at least one listener. */ - config.monoExpansionMode = ma_mono_expansion_mode_default; - - return config; -} - - -#if !defined(MA_NO_DEVICE_IO) -static void ma_engine_data_callback_internal(ma_device* pDevice, void* pFramesOut, const void* pFramesIn, ma_uint32 frameCount) -{ - ma_engine* pEngine = (ma_engine*)pDevice->pUserData; - - (void)pFramesIn; - - /* - Experiment: Try processing a resource manager job if we're on the Emscripten build. - - This serves two purposes: - - 1) It ensures jobs are actually processed at some point since we cannot guarantee that the - caller is doing the right thing and calling ma_resource_manager_process_next_job(); and - - 2) It's an attempt at working around an issue where processing jobs on the Emscripten main - loop doesn't work as well as it should. When trying to load sounds without the `DECODE` - flag or with the `ASYNC` flag, the sound data is just not able to be loaded in time - before the callback is processed. I think it's got something to do with the single- - threaded nature of Web, but I'm not entirely sure. - */ - #if !defined(MA_NO_RESOURCE_MANAGER) && defined(MA_EMSCRIPTEN) - { - if (pEngine->pResourceManager != NULL) { - if ((pEngine->pResourceManager->config.flags & MA_RESOURCE_MANAGER_FLAG_NO_THREADING) != 0) { - ma_resource_manager_process_next_job(pEngine->pResourceManager); - } - } - } - #endif - - ma_engine_read_pcm_frames(pEngine, pFramesOut, frameCount, NULL); -} - -static ma_uint32 ma_device__get_processing_size_in_frames(ma_device* pDevice) -{ - /* - The processing size is the period size. The device can have a fixed sized processing size, or - it can be decided by the backend in which case it can be variable. - */ - if (pDevice->playback.intermediaryBufferCap > 0) { - /* Using a fixed sized processing callback. */ - return pDevice->playback.intermediaryBufferCap; - } else { - /* Not using a fixed sized processing callback. Need to estimate the processing size based on the backend. */ - return pDevice->playback.internalPeriodSizeInFrames; - } -} -#endif - -MA_API ma_result ma_engine_init(const ma_engine_config* pConfig, ma_engine* pEngine) -{ - ma_result result; - ma_node_graph_config nodeGraphConfig; - ma_engine_config engineConfig; - ma_spatializer_listener_config listenerConfig; - ma_uint32 iListener; - - if (pEngine == NULL) { - return MA_INVALID_ARGS; - } - - MA_ZERO_OBJECT(pEngine); - - /* The config is allowed to be NULL in which case we use defaults for everything. */ - if (pConfig != NULL) { - engineConfig = *pConfig; - } else { - engineConfig = ma_engine_config_init(); - } - - pEngine->monoExpansionMode = engineConfig.monoExpansionMode; - pEngine->defaultVolumeSmoothTimeInPCMFrames = engineConfig.defaultVolumeSmoothTimeInPCMFrames; - pEngine->onProcess = engineConfig.onProcess; - pEngine->pProcessUserData = engineConfig.pProcessUserData; - ma_allocation_callbacks_init_copy(&pEngine->allocationCallbacks, &engineConfig.allocationCallbacks); - - #if !defined(MA_NO_RESOURCE_MANAGER) - { - pEngine->pResourceManager = engineConfig.pResourceManager; - } - #endif - - #if !defined(MA_NO_DEVICE_IO) - { - pEngine->pDevice = engineConfig.pDevice; - - /* If we don't have a device, we need one. */ - if (pEngine->pDevice == NULL && engineConfig.noDevice == MA_FALSE) { - ma_device_config deviceConfig; - - pEngine->pDevice = (ma_device*)ma_malloc(sizeof(*pEngine->pDevice), &pEngine->allocationCallbacks); - if (pEngine->pDevice == NULL) { - return MA_OUT_OF_MEMORY; - } - - deviceConfig = ma_device_config_init(ma_device_type_playback); - deviceConfig.playback.pDeviceID = engineConfig.pPlaybackDeviceID; - deviceConfig.playback.format = ma_format_f32; - deviceConfig.playback.channels = engineConfig.channels; - deviceConfig.sampleRate = engineConfig.sampleRate; - deviceConfig.dataCallback = (engineConfig.dataCallback != NULL) ? engineConfig.dataCallback : ma_engine_data_callback_internal; - deviceConfig.pUserData = pEngine; - deviceConfig.notificationCallback = engineConfig.notificationCallback; - deviceConfig.periodSizeInFrames = engineConfig.periodSizeInFrames; - deviceConfig.periodSizeInMilliseconds = engineConfig.periodSizeInMilliseconds; - deviceConfig.noPreSilencedOutputBuffer = MA_TRUE; /* We'll always be outputting to every frame in the callback so there's no need for a pre-silenced buffer. */ - deviceConfig.noClip = MA_TRUE; /* The engine will do clipping itself. */ - - if (engineConfig.pContext == NULL) { - ma_context_config contextConfig = ma_context_config_init(); - contextConfig.allocationCallbacks = pEngine->allocationCallbacks; - contextConfig.pLog = engineConfig.pLog; - - /* If the engine config does not specify a log, use the resource manager's if we have one. */ - #ifndef MA_NO_RESOURCE_MANAGER - { - if (contextConfig.pLog == NULL && engineConfig.pResourceManager != NULL) { - contextConfig.pLog = ma_resource_manager_get_log(engineConfig.pResourceManager); - } - } - #endif - - result = ma_device_init_ex(NULL, 0, &contextConfig, &deviceConfig, pEngine->pDevice); - } else { - result = ma_device_init(engineConfig.pContext, &deviceConfig, pEngine->pDevice); - } - - if (result != MA_SUCCESS) { - ma_free(pEngine->pDevice, &pEngine->allocationCallbacks); - pEngine->pDevice = NULL; - return result; - } - - pEngine->ownsDevice = MA_TRUE; - } - - /* Update the channel count and sample rate of the engine config so we can reference it below. */ - if (pEngine->pDevice != NULL) { - engineConfig.channels = pEngine->pDevice->playback.channels; - engineConfig.sampleRate = pEngine->pDevice->sampleRate; - - /* - The processing size used by the engine is determined by engineConfig.periodSizeInFrames. We want - to make this equal to what the device is using for it's period size. If we don't do that, it's - possible that the node graph will split it's processing into multiple passes which can introduce - glitching. - */ - engineConfig.periodSizeInFrames = ma_device__get_processing_size_in_frames(pEngine->pDevice); - } - } - #endif - - if (engineConfig.channels == 0 || engineConfig.sampleRate == 0) { - return MA_INVALID_ARGS; - } - - pEngine->sampleRate = engineConfig.sampleRate; - - /* The engine always uses either the log that was passed into the config, or the context's log is available. */ - if (engineConfig.pLog != NULL) { - pEngine->pLog = engineConfig.pLog; - } else { - #if !defined(MA_NO_DEVICE_IO) - { - pEngine->pLog = ma_device_get_log(pEngine->pDevice); - } - #else - { - pEngine->pLog = NULL; - } - #endif - } - - - /* The engine is a node graph. This needs to be initialized after we have the device so we can determine the channel count. */ - nodeGraphConfig = ma_node_graph_config_init(engineConfig.channels); - nodeGraphConfig.processingSizeInFrames = engineConfig.periodSizeInFrames; - nodeGraphConfig.preMixStackSizeInBytes = engineConfig.preMixStackSizeInBytes; - - result = ma_node_graph_init(&nodeGraphConfig, &pEngine->allocationCallbacks, &pEngine->nodeGraph); - if (result != MA_SUCCESS) { - goto on_error_1; - } - - - /* We need at least one listener. */ - if (engineConfig.listenerCount == 0) { - engineConfig.listenerCount = 1; - } - - if (engineConfig.listenerCount > MA_ENGINE_MAX_LISTENERS) { - result = MA_INVALID_ARGS; /* Too many listeners. */ - goto on_error_1; - } - - for (iListener = 0; iListener < engineConfig.listenerCount; iListener += 1) { - listenerConfig = ma_spatializer_listener_config_init(ma_node_graph_get_channels(&pEngine->nodeGraph)); - - /* - If we're using a device, use the device's channel map for the listener. Otherwise just use - miniaudio's default channel map. - */ - #if !defined(MA_NO_DEVICE_IO) - { - if (pEngine->pDevice != NULL) { - /* - Temporarily disabled. There is a subtle bug here where front-left and front-right - will be used by the device's channel map, but this is not what we want to use for - spatialization. Instead we want to use side-left and side-right. I need to figure - out a better solution for this. For now, disabling the use of device channel maps. - */ - /*listenerConfig.pChannelMapOut = pEngine->pDevice->playback.channelMap;*/ - } - } - #endif - - result = ma_spatializer_listener_init(&listenerConfig, &pEngine->allocationCallbacks, &pEngine->listeners[iListener]); /* TODO: Change this to a pre-allocated heap. */ - if (result != MA_SUCCESS) { - goto on_error_2; - } - - pEngine->listenerCount += 1; - } - - - /* Gain smoothing for spatialized sounds. */ - pEngine->gainSmoothTimeInFrames = engineConfig.gainSmoothTimeInFrames; - if (pEngine->gainSmoothTimeInFrames == 0) { - ma_uint32 gainSmoothTimeInMilliseconds = engineConfig.gainSmoothTimeInMilliseconds; - if (gainSmoothTimeInMilliseconds == 0) { - gainSmoothTimeInMilliseconds = 8; - } - - pEngine->gainSmoothTimeInFrames = (gainSmoothTimeInMilliseconds * ma_engine_get_sample_rate(pEngine)) / 1000; /* 8ms by default. */ - } - - - /* We need a resource manager. */ - #ifndef MA_NO_RESOURCE_MANAGER - { - if (pEngine->pResourceManager == NULL) { - ma_resource_manager_config resourceManagerConfig; - - pEngine->pResourceManager = (ma_resource_manager*)ma_malloc(sizeof(*pEngine->pResourceManager), &pEngine->allocationCallbacks); - if (pEngine->pResourceManager == NULL) { - result = MA_OUT_OF_MEMORY; - goto on_error_2; - } - - resourceManagerConfig = ma_resource_manager_config_init(); - resourceManagerConfig.pLog = pEngine->pLog; /* Always use the engine's log for internally-managed resource managers. */ - resourceManagerConfig.decodedFormat = ma_format_f32; - resourceManagerConfig.decodedChannels = 0; /* Leave the decoded channel count as 0 so we can get good spatialization. */ - resourceManagerConfig.decodedSampleRate = ma_engine_get_sample_rate(pEngine); - ma_allocation_callbacks_init_copy(&resourceManagerConfig.allocationCallbacks, &pEngine->allocationCallbacks); - resourceManagerConfig.pVFS = engineConfig.pResourceManagerVFS; - - /* The Emscripten build cannot use threads unless it's targeting pthreads. */ - #if defined(MA_EMSCRIPTEN) && !defined(__EMSCRIPTEN_PTHREADS__) - { - resourceManagerConfig.jobThreadCount = 0; - resourceManagerConfig.flags |= MA_RESOURCE_MANAGER_FLAG_NO_THREADING; - } - #endif - - result = ma_resource_manager_init(&resourceManagerConfig, pEngine->pResourceManager); - if (result != MA_SUCCESS) { - goto on_error_3; - } - - pEngine->ownsResourceManager = MA_TRUE; - } - } - #endif - - /* Setup some stuff for inlined sounds. That is sounds played with ma_engine_play_sound(). */ - pEngine->inlinedSoundLock = 0; - pEngine->pInlinedSoundHead = NULL; - - /* Start the engine if required. This should always be the last step. */ - #if !defined(MA_NO_DEVICE_IO) - { - if (engineConfig.noAutoStart == MA_FALSE && pEngine->pDevice != NULL) { - result = ma_engine_start(pEngine); - if (result != MA_SUCCESS) { - goto on_error_4; /* Failed to start the engine. */ - } - } - } - #endif - - return MA_SUCCESS; - -#if !defined(MA_NO_DEVICE_IO) -on_error_4: -#endif -#if !defined(MA_NO_RESOURCE_MANAGER) -on_error_3: - if (pEngine->ownsResourceManager) { - ma_free(pEngine->pResourceManager, &pEngine->allocationCallbacks); - } -#endif /* MA_NO_RESOURCE_MANAGER */ -on_error_2: - for (iListener = 0; iListener < pEngine->listenerCount; iListener += 1) { - ma_spatializer_listener_uninit(&pEngine->listeners[iListener], &pEngine->allocationCallbacks); - } - - ma_node_graph_uninit(&pEngine->nodeGraph, &pEngine->allocationCallbacks); -on_error_1: - #if !defined(MA_NO_DEVICE_IO) - { - if (pEngine->ownsDevice) { - ma_device_uninit(pEngine->pDevice); - ma_free(pEngine->pDevice, &pEngine->allocationCallbacks); - } - } - #endif - - return result; -} - -MA_API void ma_engine_uninit(ma_engine* pEngine) -{ - ma_uint32 iListener; - - if (pEngine == NULL) { - return; - } - - /* The device must be uninitialized before the node graph to ensure the audio thread doesn't try accessing it. */ - #if !defined(MA_NO_DEVICE_IO) - { - if (pEngine->ownsDevice) { - ma_device_uninit(pEngine->pDevice); - ma_free(pEngine->pDevice, &pEngine->allocationCallbacks); - } else { - if (pEngine->pDevice != NULL) { - ma_device_stop(pEngine->pDevice); - } - } - } - #endif - - /* - All inlined sounds need to be deleted. I'm going to use a lock here just to future proof in case - I want to do some kind of garbage collection later on. - */ - ma_spinlock_lock(&pEngine->inlinedSoundLock); - { - for (;;) { - ma_sound_inlined* pSoundToDelete = pEngine->pInlinedSoundHead; - if (pSoundToDelete == NULL) { - break; /* Done. */ - } - - pEngine->pInlinedSoundHead = pSoundToDelete->pNext; - - ma_sound_uninit(&pSoundToDelete->sound); - ma_free(pSoundToDelete, &pEngine->allocationCallbacks); - } - } - ma_spinlock_unlock(&pEngine->inlinedSoundLock); - - for (iListener = 0; iListener < pEngine->listenerCount; iListener += 1) { - ma_spatializer_listener_uninit(&pEngine->listeners[iListener], &pEngine->allocationCallbacks); - } - - /* Make sure the node graph is uninitialized after the audio thread has been shutdown to prevent accessing of the node graph after being uninitialized. */ - ma_node_graph_uninit(&pEngine->nodeGraph, &pEngine->allocationCallbacks); - - /* Uninitialize the resource manager last to ensure we don't have a thread still trying to access it. */ -#ifndef MA_NO_RESOURCE_MANAGER - if (pEngine->ownsResourceManager) { - ma_resource_manager_uninit(pEngine->pResourceManager); - ma_free(pEngine->pResourceManager, &pEngine->allocationCallbacks); - } -#endif -} - -MA_API ma_result ma_engine_read_pcm_frames(ma_engine* pEngine, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead) -{ - ma_result result; - ma_uint64 framesRead = 0; - - if (pFramesRead != NULL) { - *pFramesRead = 0; - } - - result = ma_node_graph_read_pcm_frames(&pEngine->nodeGraph, pFramesOut, frameCount, &framesRead); - if (result != MA_SUCCESS) { - return result; - } - - if (pFramesRead != NULL) { - *pFramesRead = framesRead; - } - - if (pEngine->onProcess) { - pEngine->onProcess(pEngine->pProcessUserData, (float*)pFramesOut, framesRead); /* Safe cast to float* because the engine always works on floating point samples. */ - } - - return MA_SUCCESS; -} - -MA_API ma_node_graph* ma_engine_get_node_graph(ma_engine* pEngine) -{ - if (pEngine == NULL) { - return NULL; - } - - return &pEngine->nodeGraph; -} - -#if !defined(MA_NO_RESOURCE_MANAGER) -MA_API ma_resource_manager* ma_engine_get_resource_manager(ma_engine* pEngine) -{ - if (pEngine == NULL) { - return NULL; - } - - #if !defined(MA_NO_RESOURCE_MANAGER) - { - return pEngine->pResourceManager; - } - #else - { - return NULL; - } - #endif -} -#endif - -MA_API ma_device* ma_engine_get_device(ma_engine* pEngine) -{ - if (pEngine == NULL) { - return NULL; - } - - #if !defined(MA_NO_DEVICE_IO) - { - return pEngine->pDevice; - } - #else - { - return NULL; - } - #endif -} - -MA_API ma_log* ma_engine_get_log(ma_engine* pEngine) -{ - if (pEngine == NULL) { - return NULL; - } - - if (pEngine->pLog != NULL) { - return pEngine->pLog; - } else { - #if !defined(MA_NO_DEVICE_IO) - { - return ma_device_get_log(ma_engine_get_device(pEngine)); - } - #else - { - return NULL; - } - #endif - } -} - -MA_API ma_node* ma_engine_get_endpoint(ma_engine* pEngine) -{ - return ma_node_graph_get_endpoint(&pEngine->nodeGraph); -} - -MA_API ma_uint64 ma_engine_get_time_in_pcm_frames(const ma_engine* pEngine) -{ - return ma_node_graph_get_time(&pEngine->nodeGraph); -} - -MA_API ma_uint64 ma_engine_get_time_in_milliseconds(const ma_engine* pEngine) -{ - return ma_engine_get_time_in_pcm_frames(pEngine) * 1000 / ma_engine_get_sample_rate(pEngine); -} - -MA_API ma_result ma_engine_set_time_in_pcm_frames(ma_engine* pEngine, ma_uint64 globalTime) -{ - return ma_node_graph_set_time(&pEngine->nodeGraph, globalTime); -} - -MA_API ma_result ma_engine_set_time_in_milliseconds(ma_engine* pEngine, ma_uint64 globalTime) -{ - return ma_engine_set_time_in_pcm_frames(pEngine, globalTime * ma_engine_get_sample_rate(pEngine) / 1000); -} - -MA_API ma_uint64 ma_engine_get_time(const ma_engine* pEngine) -{ - return ma_engine_get_time_in_pcm_frames(pEngine); -} - -MA_API ma_result ma_engine_set_time(ma_engine* pEngine, ma_uint64 globalTime) -{ - return ma_engine_set_time_in_pcm_frames(pEngine, globalTime); -} - -MA_API ma_uint32 ma_engine_get_channels(const ma_engine* pEngine) -{ - return ma_node_graph_get_channels(&pEngine->nodeGraph); -} - -MA_API ma_uint32 ma_engine_get_sample_rate(const ma_engine* pEngine) -{ - if (pEngine == NULL) { - return 0; - } - - return pEngine->sampleRate; -} - - -MA_API ma_result ma_engine_start(ma_engine* pEngine) -{ - ma_result result; - - if (pEngine == NULL) { - return MA_INVALID_ARGS; - } - - #if !defined(MA_NO_DEVICE_IO) - { - if (pEngine->pDevice != NULL) { - result = ma_device_start(pEngine->pDevice); - } else { - result = MA_INVALID_OPERATION; /* The engine is running without a device which means there's no real notion of "starting" the engine. */ - } - } - #else - { - result = MA_INVALID_OPERATION; /* Device IO is disabled, so there's no real notion of "starting" the engine. */ - } - #endif - - if (result != MA_SUCCESS) { - return result; - } - - return MA_SUCCESS; -} - -MA_API ma_result ma_engine_stop(ma_engine* pEngine) -{ - ma_result result; - - if (pEngine == NULL) { - return MA_INVALID_ARGS; - } - - #if !defined(MA_NO_DEVICE_IO) - { - if (pEngine->pDevice != NULL) { - result = ma_device_stop(pEngine->pDevice); - } else { - result = MA_INVALID_OPERATION; /* The engine is running without a device which means there's no real notion of "stopping" the engine. */ - } - } - #else - { - result = MA_INVALID_OPERATION; /* Device IO is disabled, so there's no real notion of "stopping" the engine. */ - } - #endif - - if (result != MA_SUCCESS) { - return result; - } - - return MA_SUCCESS; -} - -MA_API ma_result ma_engine_set_volume(ma_engine* pEngine, float volume) -{ - if (pEngine == NULL) { - return MA_INVALID_ARGS; - } - - return ma_node_set_output_bus_volume(ma_node_graph_get_endpoint(&pEngine->nodeGraph), 0, volume); -} - -MA_API float ma_engine_get_volume(ma_engine* pEngine) -{ - if (pEngine == NULL) { - return 0; - } - - return ma_node_get_output_bus_volume(ma_node_graph_get_endpoint(&pEngine->nodeGraph), 0); -} - -MA_API ma_result ma_engine_set_gain_db(ma_engine* pEngine, float gainDB) -{ - return ma_engine_set_volume(pEngine, ma_volume_db_to_linear(gainDB)); -} - -MA_API float ma_engine_get_gain_db(ma_engine* pEngine) -{ - return ma_volume_linear_to_db(ma_engine_get_volume(pEngine)); -} - - -MA_API ma_uint32 ma_engine_get_listener_count(const ma_engine* pEngine) -{ - if (pEngine == NULL) { - return 0; - } - - return pEngine->listenerCount; -} - -MA_API ma_uint32 ma_engine_find_closest_listener(const ma_engine* pEngine, float absolutePosX, float absolutePosY, float absolutePosZ) -{ - ma_uint32 iListener; - ma_uint32 iListenerClosest; - float closestLen2 = MA_FLT_MAX; - - if (pEngine == NULL || pEngine->listenerCount == 1) { - return 0; - } - - iListenerClosest = 0; - for (iListener = 0; iListener < pEngine->listenerCount; iListener += 1) { - if (ma_engine_listener_is_enabled(pEngine, iListener)) { - float len2 = ma_vec3f_len2(ma_vec3f_sub(ma_spatializer_listener_get_position(&pEngine->listeners[iListener]), ma_vec3f_init_3f(absolutePosX, absolutePosY, absolutePosZ))); - if (closestLen2 > len2) { - closestLen2 = len2; - iListenerClosest = iListener; - } - } - } - - MA_ASSERT(iListenerClosest < 255); - return iListenerClosest; -} - -MA_API void ma_engine_listener_set_position(ma_engine* pEngine, ma_uint32 listenerIndex, float x, float y, float z) -{ - if (pEngine == NULL || listenerIndex >= pEngine->listenerCount) { - return; - } - - ma_spatializer_listener_set_position(&pEngine->listeners[listenerIndex], x, y, z); -} - -MA_API ma_vec3f ma_engine_listener_get_position(const ma_engine* pEngine, ma_uint32 listenerIndex) -{ - if (pEngine == NULL || listenerIndex >= pEngine->listenerCount) { - return ma_vec3f_init_3f(0, 0, 0); - } - - return ma_spatializer_listener_get_position(&pEngine->listeners[listenerIndex]); -} - -MA_API void ma_engine_listener_set_direction(ma_engine* pEngine, ma_uint32 listenerIndex, float x, float y, float z) -{ - if (pEngine == NULL || listenerIndex >= pEngine->listenerCount) { - return; - } - - ma_spatializer_listener_set_direction(&pEngine->listeners[listenerIndex], x, y, z); -} - -MA_API ma_vec3f ma_engine_listener_get_direction(const ma_engine* pEngine, ma_uint32 listenerIndex) -{ - if (pEngine == NULL || listenerIndex >= pEngine->listenerCount) { - return ma_vec3f_init_3f(0, 0, -1); - } - - return ma_spatializer_listener_get_direction(&pEngine->listeners[listenerIndex]); -} - -MA_API void ma_engine_listener_set_velocity(ma_engine* pEngine, ma_uint32 listenerIndex, float x, float y, float z) -{ - if (pEngine == NULL || listenerIndex >= pEngine->listenerCount) { - return; - } - - ma_spatializer_listener_set_velocity(&pEngine->listeners[listenerIndex], x, y, z); -} - -MA_API ma_vec3f ma_engine_listener_get_velocity(const ma_engine* pEngine, ma_uint32 listenerIndex) -{ - if (pEngine == NULL || listenerIndex >= pEngine->listenerCount) { - return ma_vec3f_init_3f(0, 0, 0); - } - - return ma_spatializer_listener_get_velocity(&pEngine->listeners[listenerIndex]); -} - -MA_API void ma_engine_listener_set_cone(ma_engine* pEngine, ma_uint32 listenerIndex, float innerAngleInRadians, float outerAngleInRadians, float outerGain) -{ - if (pEngine == NULL || listenerIndex >= pEngine->listenerCount) { - return; - } - - ma_spatializer_listener_set_cone(&pEngine->listeners[listenerIndex], innerAngleInRadians, outerAngleInRadians, outerGain); -} - -MA_API void ma_engine_listener_get_cone(const ma_engine* pEngine, ma_uint32 listenerIndex, float* pInnerAngleInRadians, float* pOuterAngleInRadians, float* pOuterGain) -{ - if (pInnerAngleInRadians != NULL) { - *pInnerAngleInRadians = 0; - } - - if (pOuterAngleInRadians != NULL) { - *pOuterAngleInRadians = 0; - } - - if (pOuterGain != NULL) { - *pOuterGain = 0; - } - - if (pEngine == NULL || listenerIndex >= pEngine->listenerCount) { - return; - } - - ma_spatializer_listener_get_cone(&pEngine->listeners[listenerIndex], pInnerAngleInRadians, pOuterAngleInRadians, pOuterGain); -} - -MA_API void ma_engine_listener_set_world_up(ma_engine* pEngine, ma_uint32 listenerIndex, float x, float y, float z) -{ - if (pEngine == NULL || listenerIndex >= pEngine->listenerCount) { - return; - } - - ma_spatializer_listener_set_world_up(&pEngine->listeners[listenerIndex], x, y, z); -} - -MA_API ma_vec3f ma_engine_listener_get_world_up(const ma_engine* pEngine, ma_uint32 listenerIndex) -{ - if (pEngine == NULL || listenerIndex >= pEngine->listenerCount) { - return ma_vec3f_init_3f(0, 1, 0); - } - - return ma_spatializer_listener_get_world_up(&pEngine->listeners[listenerIndex]); -} - -MA_API void ma_engine_listener_set_enabled(ma_engine* pEngine, ma_uint32 listenerIndex, ma_bool32 isEnabled) -{ - if (pEngine == NULL || listenerIndex >= pEngine->listenerCount) { - return; - } - - ma_spatializer_listener_set_enabled(&pEngine->listeners[listenerIndex], isEnabled); -} - -MA_API ma_bool32 ma_engine_listener_is_enabled(const ma_engine* pEngine, ma_uint32 listenerIndex) -{ - if (pEngine == NULL || listenerIndex >= pEngine->listenerCount) { - return MA_FALSE; - } - - return ma_spatializer_listener_is_enabled(&pEngine->listeners[listenerIndex]); -} - - -#ifndef MA_NO_RESOURCE_MANAGER -MA_API ma_result ma_engine_play_sound_ex(ma_engine* pEngine, const char* pFilePath, ma_node* pNode, ma_uint32 nodeInputBusIndex) -{ - ma_result result = MA_SUCCESS; - ma_sound_inlined* pSound = NULL; - ma_sound_inlined* pNextSound = NULL; - - if (pEngine == NULL || pFilePath == NULL) { - return MA_INVALID_ARGS; - } - - /* Attach to the endpoint node if nothing is specified. */ - if (pNode == NULL) { - pNode = ma_node_graph_get_endpoint(&pEngine->nodeGraph); - nodeInputBusIndex = 0; - } - - /* - We want to check if we can recycle an already-allocated inlined sound. Since this is just a - helper I'm not *too* concerned about performance here and I'm happy to use a lock to keep - the implementation simple. Maybe this can be optimized later if there's enough demand, but - if this function is being used it probably means the caller doesn't really care too much. - - What we do is check the atEnd flag. When this is true, we can recycle the sound. Otherwise - we just keep iterating. If we reach the end without finding a sound to recycle we just - allocate a new one. This doesn't scale well for a massive number of sounds being played - simultaneously as we don't ever actually free the sound objects. Some kind of garbage - collection routine might be valuable for this which I'll think about. - */ - ma_spinlock_lock(&pEngine->inlinedSoundLock); - { - ma_uint32 soundFlags = 0; - - for (pNextSound = pEngine->pInlinedSoundHead; pNextSound != NULL; pNextSound = pNextSound->pNext) { - if (ma_sound_at_end(&pNextSound->sound)) { - /* - The sound is at the end which means it's available for recycling. All we need to do - is uninitialize it and reinitialize it. All we're doing is recycling memory. - */ - pSound = pNextSound; - ma_atomic_fetch_sub_32(&pEngine->inlinedSoundCount, 1); - break; - } - } - - if (pSound != NULL) { - /* - We actually want to detach the sound from the list here. The reason is because we want the sound - to be in a consistent state at the non-recycled case to simplify the logic below. - */ - if (pEngine->pInlinedSoundHead == pSound) { - pEngine->pInlinedSoundHead = pSound->pNext; - } - - if (pSound->pPrev != NULL) { - pSound->pPrev->pNext = pSound->pNext; - } - if (pSound->pNext != NULL) { - pSound->pNext->pPrev = pSound->pPrev; - } - - /* Now the previous sound needs to be uninitialized. */ - ma_sound_uninit(&pNextSound->sound); - } else { - /* No sound available for recycling. Allocate one now. */ - pSound = (ma_sound_inlined*)ma_malloc(sizeof(*pSound), &pEngine->allocationCallbacks); - } - - if (pSound != NULL) { /* Safety check for the allocation above. */ - /* - At this point we should have memory allocated for the inlined sound. We just need - to initialize it like a normal sound now. - */ - soundFlags |= MA_SOUND_FLAG_ASYNC; /* For inlined sounds we don't want to be sitting around waiting for stuff to load so force an async load. */ - soundFlags |= MA_SOUND_FLAG_NO_DEFAULT_ATTACHMENT; /* We want specific control over where the sound is attached in the graph. We'll attach it manually just before playing the sound. */ - soundFlags |= MA_SOUND_FLAG_NO_PITCH; /* Pitching isn't usable with inlined sounds, so disable it to save on speed. */ - soundFlags |= MA_SOUND_FLAG_NO_SPATIALIZATION; /* Not currently doing spatialization with inlined sounds, but this might actually change later. For now disable spatialization. Will be removed if we ever add support for spatialization here. */ - - result = ma_sound_init_from_file(pEngine, pFilePath, soundFlags, NULL, NULL, &pSound->sound); - if (result == MA_SUCCESS) { - /* Now attach the sound to the graph. */ - result = ma_node_attach_output_bus(pSound, 0, pNode, nodeInputBusIndex); - if (result == MA_SUCCESS) { - /* At this point the sound should be loaded and we can go ahead and add it to the list. The new item becomes the new head. */ - pSound->pNext = pEngine->pInlinedSoundHead; - pSound->pPrev = NULL; - - pEngine->pInlinedSoundHead = pSound; /* <-- This is what attaches the sound to the list. */ - if (pSound->pNext != NULL) { - pSound->pNext->pPrev = pSound; - } - } else { - ma_free(pSound, &pEngine->allocationCallbacks); - } - } else { - ma_free(pSound, &pEngine->allocationCallbacks); - } - } else { - result = MA_OUT_OF_MEMORY; - } - } - ma_spinlock_unlock(&pEngine->inlinedSoundLock); - - if (result != MA_SUCCESS) { - return result; - } - - /* Finally we can start playing the sound. */ - result = ma_sound_start(&pSound->sound); - if (result != MA_SUCCESS) { - /* Failed to start the sound. We need to mark it for recycling and return an error. */ - ma_atomic_exchange_32(&pSound->sound.atEnd, MA_TRUE); - return result; - } - - ma_atomic_fetch_add_32(&pEngine->inlinedSoundCount, 1); - return result; -} - -MA_API ma_result ma_engine_play_sound(ma_engine* pEngine, const char* pFilePath, ma_sound_group* pGroup) -{ - return ma_engine_play_sound_ex(pEngine, pFilePath, pGroup, 0); -} -#endif - - -static ma_result ma_sound_preinit(ma_engine* pEngine, ma_sound* pSound) -{ - if (pSound == NULL) { - return MA_INVALID_ARGS; - } - - MA_ZERO_OBJECT(pSound); - pSound->seekTarget = MA_SEEK_TARGET_NONE; - - if (pEngine == NULL) { - return MA_INVALID_ARGS; - } - - return MA_SUCCESS; -} - -static ma_result ma_sound_init_from_data_source_internal(ma_engine* pEngine, const ma_sound_config* pConfig, ma_sound* pSound) -{ - ma_result result; - ma_engine_node_config engineNodeConfig; - ma_engine_node_type type; /* Will be set to ma_engine_node_type_group if no data source is specified. */ - - /* Do not clear pSound to zero here - that's done at a higher level with ma_sound_preinit(). */ - MA_ASSERT(pEngine != NULL); - MA_ASSERT(pSound != NULL); - - if (pConfig == NULL) { - return MA_INVALID_ARGS; - } - - pSound->pDataSource = pConfig->pDataSource; - - if (pConfig->pDataSource != NULL) { - type = ma_engine_node_type_sound; - } else { - type = ma_engine_node_type_group; - } - - /* - Sounds are engine nodes. Before we can initialize this we need to determine the channel count. - If we can't do this we need to abort. It's up to the caller to ensure they're using a data - source that provides this information upfront. - */ - engineNodeConfig = ma_engine_node_config_init(pEngine, type, pConfig->flags); - engineNodeConfig.channelsIn = pConfig->channelsIn; - engineNodeConfig.channelsOut = pConfig->channelsOut; - engineNodeConfig.volumeSmoothTimeInPCMFrames = pConfig->volumeSmoothTimeInPCMFrames; - engineNodeConfig.monoExpansionMode = pConfig->monoExpansionMode; - - if (engineNodeConfig.volumeSmoothTimeInPCMFrames == 0) { - engineNodeConfig.volumeSmoothTimeInPCMFrames = pEngine->defaultVolumeSmoothTimeInPCMFrames; - } - - /* If we're loading from a data source the input channel count needs to be the data source's native channel count. */ - if (pConfig->pDataSource != NULL) { - result = ma_data_source_get_data_format(pConfig->pDataSource, NULL, &engineNodeConfig.channelsIn, &engineNodeConfig.sampleRate, NULL, 0); - if (result != MA_SUCCESS) { - return result; /* Failed to retrieve the channel count. */ - } - - if (engineNodeConfig.channelsIn == 0) { - return MA_INVALID_OPERATION; /* Invalid channel count. */ - } - - if (engineNodeConfig.channelsOut == MA_SOUND_SOURCE_CHANNEL_COUNT) { - engineNodeConfig.channelsOut = engineNodeConfig.channelsIn; - } - } - - - /* Getting here means we should have a valid channel count and we can initialize the engine node. */ - result = ma_engine_node_init(&engineNodeConfig, &pEngine->allocationCallbacks, &pSound->engineNode); - if (result != MA_SUCCESS) { - return result; - } - - /* If no attachment is specified, attach the sound straight to the endpoint. */ - if (pConfig->pInitialAttachment == NULL) { - /* No group. Attach straight to the endpoint by default, unless the caller has requested that it not. */ - if ((pConfig->flags & MA_SOUND_FLAG_NO_DEFAULT_ATTACHMENT) == 0) { - result = ma_node_attach_output_bus(pSound, 0, ma_node_graph_get_endpoint(&pEngine->nodeGraph), 0); - } - } else { - /* An attachment is specified. Attach to it by default. The sound has only a single output bus, and the config will specify which input bus to attach to. */ - result = ma_node_attach_output_bus(pSound, 0, pConfig->pInitialAttachment, pConfig->initialAttachmentInputBusIndex); - } - - if (result != MA_SUCCESS) { - ma_engine_node_uninit(&pSound->engineNode, &pEngine->allocationCallbacks); - return result; - } - - - /* Apply initial range and looping state to the data source if applicable. */ - if (pConfig->rangeBegInPCMFrames != 0 || pConfig->rangeEndInPCMFrames != ~((ma_uint64)0)) { - ma_data_source_set_range_in_pcm_frames(ma_sound_get_data_source(pSound), pConfig->rangeBegInPCMFrames, pConfig->rangeEndInPCMFrames); - } - - if (pConfig->loopPointBegInPCMFrames != 0 || pConfig->loopPointEndInPCMFrames != ~((ma_uint64)0)) { - ma_data_source_set_range_in_pcm_frames(ma_sound_get_data_source(pSound), pConfig->loopPointBegInPCMFrames, pConfig->loopPointEndInPCMFrames); - } - - ma_sound_set_looping(pSound, pConfig->isLooping || ((pConfig->flags & MA_SOUND_FLAG_LOOPING) != 0)); - - return MA_SUCCESS; -} - -#ifndef MA_NO_RESOURCE_MANAGER -MA_API ma_result ma_sound_init_from_file_internal(ma_engine* pEngine, const ma_sound_config* pConfig, ma_sound* pSound) -{ - ma_result result = MA_SUCCESS; - ma_uint32 flags; - ma_sound_config config; - ma_resource_manager_pipeline_notifications notifications; - - /* - The engine requires knowledge of the channel count of the underlying data source before it can - initialize the sound. Therefore, we need to make the resource manager wait until initialization - of the underlying data source to be initialized so we can get access to the channel count. To - do this, the MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_WAIT_INIT is forced. - - Because we're initializing the data source before the sound, there's a chance the notification - will get triggered before this function returns. This is OK, so long as the caller is aware of - it and can avoid accessing the sound from within the notification. - */ - flags = pConfig->flags | MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_WAIT_INIT; - if (pConfig->isLooping) { - flags |= MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_LOOPING; - } - - pSound->pResourceManagerDataSource = (ma_resource_manager_data_source*)ma_malloc(sizeof(*pSound->pResourceManagerDataSource), &pEngine->allocationCallbacks); - if (pSound->pResourceManagerDataSource == NULL) { - return MA_OUT_OF_MEMORY; - } - - /* Removed in 0.12. Set pDoneFence on the notifications. */ - notifications = pConfig->initNotifications; - if (pConfig->pDoneFence != NULL && notifications.done.pFence == NULL) { - notifications.done.pFence = pConfig->pDoneFence; - } - - /* - We must wrap everything around the fence if one was specified. This ensures ma_fence_wait() does - not return prematurely before the sound has finished initializing. - */ - if (notifications.done.pFence) { ma_fence_acquire(notifications.done.pFence); } - { - ma_resource_manager_data_source_config resourceManagerDataSourceConfig = ma_resource_manager_data_source_config_init(); - resourceManagerDataSourceConfig.pFilePath = pConfig->pFilePath; - resourceManagerDataSourceConfig.pFilePathW = pConfig->pFilePathW; - resourceManagerDataSourceConfig.flags = flags; - resourceManagerDataSourceConfig.pNotifications = ¬ifications; - resourceManagerDataSourceConfig.initialSeekPointInPCMFrames = pConfig->initialSeekPointInPCMFrames; - resourceManagerDataSourceConfig.rangeBegInPCMFrames = pConfig->rangeBegInPCMFrames; - resourceManagerDataSourceConfig.rangeEndInPCMFrames = pConfig->rangeEndInPCMFrames; - resourceManagerDataSourceConfig.loopPointBegInPCMFrames = pConfig->loopPointBegInPCMFrames; - resourceManagerDataSourceConfig.loopPointEndInPCMFrames = pConfig->loopPointEndInPCMFrames; - resourceManagerDataSourceConfig.isLooping = (flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_LOOPING) != 0; - - result = ma_resource_manager_data_source_init_ex(pEngine->pResourceManager, &resourceManagerDataSourceConfig, pSound->pResourceManagerDataSource); - if (result != MA_SUCCESS) { - goto done; - } - - pSound->ownsDataSource = MA_TRUE; /* <-- Important. Not setting this will result in the resource manager data source never getting uninitialized. */ - - /* We need to use a slightly customized version of the config so we'll need to make a copy. */ - config = *pConfig; - config.pFilePath = NULL; - config.pFilePathW = NULL; - config.pDataSource = pSound->pResourceManagerDataSource; - - result = ma_sound_init_from_data_source_internal(pEngine, &config, pSound); - if (result != MA_SUCCESS) { - ma_resource_manager_data_source_uninit(pSound->pResourceManagerDataSource); - ma_free(pSound->pResourceManagerDataSource, &pEngine->allocationCallbacks); - MA_ZERO_OBJECT(pSound); - goto done; - } - } -done: - if (notifications.done.pFence) { ma_fence_release(notifications.done.pFence); } - return result; -} - -MA_API ma_result ma_sound_init_from_file(ma_engine* pEngine, const char* pFilePath, ma_uint32 flags, ma_sound_group* pGroup, ma_fence* pDoneFence, ma_sound* pSound) -{ - ma_sound_config config; - - if (pFilePath == NULL) { - return MA_INVALID_ARGS; - } - - config = ma_sound_config_init_2(pEngine); - config.pFilePath = pFilePath; - config.flags = flags; - config.pInitialAttachment = pGroup; - config.pDoneFence = pDoneFence; - - return ma_sound_init_ex(pEngine, &config, pSound); -} - -MA_API ma_result ma_sound_init_from_file_w(ma_engine* pEngine, const wchar_t* pFilePath, ma_uint32 flags, ma_sound_group* pGroup, ma_fence* pDoneFence, ma_sound* pSound) -{ - ma_sound_config config; - - if (pFilePath == NULL) { - return MA_INVALID_ARGS; - } - - config = ma_sound_config_init_2(pEngine); - config.pFilePathW = pFilePath; - config.flags = flags; - config.pInitialAttachment = pGroup; - config.pDoneFence = pDoneFence; - - return ma_sound_init_ex(pEngine, &config, pSound); -} - -MA_API ma_result ma_sound_init_copy(ma_engine* pEngine, const ma_sound* pExistingSound, ma_uint32 flags, ma_sound_group* pGroup, ma_sound* pSound) -{ - ma_result result; - ma_sound_config config; - - result = ma_sound_preinit(pEngine, pSound); - if (result != MA_SUCCESS) { - return result; - } - - if (pExistingSound == NULL) { - return MA_INVALID_ARGS; - } - - /* Cloning only works for data buffers (not streams) that are loaded from the resource manager. */ - if (pExistingSound->pResourceManagerDataSource == NULL) { - return MA_INVALID_OPERATION; - } - - /* - We need to make a clone of the data source. If the data source is not a data buffer (i.e. a stream) - this will fail. - */ - pSound->pResourceManagerDataSource = (ma_resource_manager_data_source*)ma_malloc(sizeof(*pSound->pResourceManagerDataSource), &pEngine->allocationCallbacks); - if (pSound->pResourceManagerDataSource == NULL) { - return MA_OUT_OF_MEMORY; - } - - result = ma_resource_manager_data_source_init_copy(pEngine->pResourceManager, pExistingSound->pResourceManagerDataSource, pSound->pResourceManagerDataSource); - if (result != MA_SUCCESS) { - ma_free(pSound->pResourceManagerDataSource, &pEngine->allocationCallbacks); - return result; - } - - config = ma_sound_config_init_2(pEngine); - config.pDataSource = pSound->pResourceManagerDataSource; - config.flags = flags; - config.pInitialAttachment = pGroup; - config.monoExpansionMode = pExistingSound->engineNode.monoExpansionMode; - config.volumeSmoothTimeInPCMFrames = pExistingSound->engineNode.volumeSmoothTimeInPCMFrames; - - result = ma_sound_init_from_data_source_internal(pEngine, &config, pSound); - if (result != MA_SUCCESS) { - ma_resource_manager_data_source_uninit(pSound->pResourceManagerDataSource); - ma_free(pSound->pResourceManagerDataSource, &pEngine->allocationCallbacks); - MA_ZERO_OBJECT(pSound); - return result; - } - - /* Make sure the sound is marked as the owner of the data source or else it will never get uninitialized. */ - pSound->ownsDataSource = MA_TRUE; - - return MA_SUCCESS; -} -#endif - -MA_API ma_result ma_sound_init_from_data_source(ma_engine* pEngine, ma_data_source* pDataSource, ma_uint32 flags, ma_sound_group* pGroup, ma_sound* pSound) -{ - ma_sound_config config = ma_sound_config_init_2(pEngine); - config.pDataSource = pDataSource; - config.flags = flags; - config.pInitialAttachment = pGroup; - return ma_sound_init_ex(pEngine, &config, pSound); -} - -MA_API ma_result ma_sound_init_ex(ma_engine* pEngine, const ma_sound_config* pConfig, ma_sound* pSound) -{ - ma_result result; - - result = ma_sound_preinit(pEngine, pSound); - if (result != MA_SUCCESS) { - return result; - } - - if (pConfig == NULL) { - return MA_INVALID_ARGS; - } - - pSound->endCallback = pConfig->endCallback; - pSound->pEndCallbackUserData = pConfig->pEndCallbackUserData; - - /* We need to load the sound differently depending on whether or not we're loading from a file. */ -#ifndef MA_NO_RESOURCE_MANAGER - if (pConfig->pFilePath != NULL || pConfig->pFilePathW != NULL) { - return ma_sound_init_from_file_internal(pEngine, pConfig, pSound); - } else -#endif - { - /* - Getting here means we're not loading from a file. We may be loading from an already-initialized - data source, or none at all. If we aren't specifying any data source, we'll be initializing - the equivalent to a group. ma_data_source_init_from_data_source_internal() will deal with this - for us, so no special treatment required here. - */ - return ma_sound_init_from_data_source_internal(pEngine, pConfig, pSound); - } -} - -MA_API void ma_sound_uninit(ma_sound* pSound) -{ - if (pSound == NULL) { - return; - } - - /* - Always uninitialize the node first. This ensures it's detached from the graph and does not return until it has done - so which makes thread safety beyond this point trivial. - */ - ma_engine_node_uninit(&pSound->engineNode, &pSound->engineNode.pEngine->allocationCallbacks); - - /* Once the sound is detached from the group we can guarantee that it won't be referenced by the mixer thread which means it's safe for us to destroy the data source. */ -#ifndef MA_NO_RESOURCE_MANAGER - if (pSound->ownsDataSource) { - ma_resource_manager_data_source_uninit(pSound->pResourceManagerDataSource); - ma_free(pSound->pResourceManagerDataSource, &pSound->engineNode.pEngine->allocationCallbacks); - pSound->pDataSource = NULL; - } -#else - MA_ASSERT(pSound->ownsDataSource == MA_FALSE); -#endif -} - -MA_API ma_engine* ma_sound_get_engine(const ma_sound* pSound) -{ - if (pSound == NULL) { - return NULL; - } - - return pSound->engineNode.pEngine; -} - -MA_API ma_data_source* ma_sound_get_data_source(const ma_sound* pSound) -{ - if (pSound == NULL) { - return NULL; - } - - return pSound->pDataSource; -} - -MA_API ma_result ma_sound_start(ma_sound* pSound) -{ - if (pSound == NULL) { - return MA_INVALID_ARGS; - } - - /* If the sound is already playing, do nothing. */ - if (ma_sound_is_playing(pSound)) { - return MA_SUCCESS; - } - - /* If the sound is at the end it means we want to start from the start again. */ - if (ma_sound_at_end(pSound)) { - ma_result result = ma_data_source_seek_to_pcm_frame(pSound->pDataSource, 0); - if (result != MA_SUCCESS && result != MA_NOT_IMPLEMENTED) { - return result; /* Failed to seek back to the start. */ - } - - /* Make sure we clear the end indicator. */ - ma_atomic_exchange_32(&pSound->atEnd, MA_FALSE); - } - - /* Make sure the sound is started. If there's a start delay, the sound won't actually start until the start time is reached. */ - ma_node_set_state(pSound, ma_node_state_started); - - return MA_SUCCESS; -} - -MA_API ma_result ma_sound_stop(ma_sound* pSound) -{ - if (pSound == NULL) { - return MA_INVALID_ARGS; - } - - /* This will stop the sound immediately. Use ma_sound_set_stop_time() to stop the sound at a specific time. */ - ma_node_set_state(pSound, ma_node_state_stopped); - - return MA_SUCCESS; -} - -MA_API ma_result ma_sound_stop_with_fade_in_pcm_frames(ma_sound* pSound, ma_uint64 fadeLengthInFrames) -{ - if (pSound == NULL) { - return MA_INVALID_ARGS; - } - - /* Stopping with a fade out requires us to schedule the stop into the future by the fade length. */ - ma_sound_set_stop_time_with_fade_in_pcm_frames(pSound, ma_engine_get_time_in_pcm_frames(ma_sound_get_engine(pSound)) + fadeLengthInFrames, fadeLengthInFrames); - - return MA_SUCCESS; -} - -MA_API ma_result ma_sound_stop_with_fade_in_milliseconds(ma_sound* pSound, ma_uint64 fadeLengthInMilliseconds) -{ - ma_uint64 sampleRate; - - if (pSound == NULL) { - return MA_INVALID_ARGS; - } - - sampleRate = ma_engine_get_sample_rate(ma_sound_get_engine(pSound)); - - return ma_sound_stop_with_fade_in_pcm_frames(pSound, (fadeLengthInMilliseconds * sampleRate) / 1000); -} - -MA_API void ma_sound_set_volume(ma_sound* pSound, float volume) -{ - if (pSound == NULL) { - return; - } - - ma_engine_node_set_volume(&pSound->engineNode, volume); -} - -MA_API float ma_sound_get_volume(const ma_sound* pSound) -{ - float volume = 0; - - if (pSound == NULL) { - return 0; - } - - ma_engine_node_get_volume(&pSound->engineNode, &volume); - - return volume; -} - -MA_API void ma_sound_set_pan(ma_sound* pSound, float pan) -{ - if (pSound == NULL) { - return; - } - - ma_panner_set_pan(&pSound->engineNode.panner, pan); -} - -MA_API float ma_sound_get_pan(const ma_sound* pSound) -{ - if (pSound == NULL) { - return 0; - } - - return ma_panner_get_pan(&pSound->engineNode.panner); -} - -MA_API void ma_sound_set_pan_mode(ma_sound* pSound, ma_pan_mode panMode) -{ - if (pSound == NULL) { - return; - } - - ma_panner_set_mode(&pSound->engineNode.panner, panMode); -} - -MA_API ma_pan_mode ma_sound_get_pan_mode(const ma_sound* pSound) -{ - if (pSound == NULL) { - return ma_pan_mode_balance; - } - - return ma_panner_get_mode(&pSound->engineNode.panner); -} - -MA_API void ma_sound_set_pitch(ma_sound* pSound, float pitch) -{ - if (pSound == NULL) { - return; - } - - if (pitch <= 0) { - return; - } - - ma_atomic_exchange_explicit_f32(&pSound->engineNode.pitch, pitch, ma_atomic_memory_order_release); -} - -MA_API float ma_sound_get_pitch(const ma_sound* pSound) -{ - if (pSound == NULL) { - return 0; - } - - return ma_atomic_load_f32(&pSound->engineNode.pitch); /* Naughty const-cast for this. */ -} - -MA_API void ma_sound_set_spatialization_enabled(ma_sound* pSound, ma_bool32 enabled) -{ - if (pSound == NULL) { - return; - } - - ma_atomic_exchange_explicit_32(&pSound->engineNode.isSpatializationDisabled, !enabled, ma_atomic_memory_order_release); -} - -MA_API ma_bool32 ma_sound_is_spatialization_enabled(const ma_sound* pSound) -{ - if (pSound == NULL) { - return MA_FALSE; - } - - return ma_engine_node_is_spatialization_enabled(&pSound->engineNode); -} - -MA_API void ma_sound_set_pinned_listener_index(ma_sound* pSound, ma_uint32 listenerIndex) -{ - if (pSound == NULL || listenerIndex >= ma_engine_get_listener_count(ma_sound_get_engine(pSound))) { - return; - } - - ma_atomic_exchange_explicit_32(&pSound->engineNode.pinnedListenerIndex, listenerIndex, ma_atomic_memory_order_release); -} - -MA_API ma_uint32 ma_sound_get_pinned_listener_index(const ma_sound* pSound) -{ - if (pSound == NULL) { - return MA_LISTENER_INDEX_CLOSEST; - } - - return ma_atomic_load_explicit_32(&pSound->engineNode.pinnedListenerIndex, ma_atomic_memory_order_acquire); -} - -MA_API ma_uint32 ma_sound_get_listener_index(const ma_sound* pSound) -{ - ma_uint32 listenerIndex; - - if (pSound == NULL) { - return 0; - } - - listenerIndex = ma_sound_get_pinned_listener_index(pSound); - if (listenerIndex == MA_LISTENER_INDEX_CLOSEST) { - ma_vec3f position = ma_sound_get_position(pSound); - return ma_engine_find_closest_listener(ma_sound_get_engine(pSound), position.x, position.y, position.z); - } - - return listenerIndex; -} - -MA_API ma_vec3f ma_sound_get_direction_to_listener(const ma_sound* pSound) -{ - ma_vec3f relativePos; - ma_engine* pEngine; - - if (pSound == NULL) { - return ma_vec3f_init_3f(0, 0, -1); - } - - pEngine = ma_sound_get_engine(pSound); - if (pEngine == NULL) { - return ma_vec3f_init_3f(0, 0, -1); - } - - ma_spatializer_get_relative_position_and_direction(&pSound->engineNode.spatializer, &pEngine->listeners[ma_sound_get_listener_index(pSound)], &relativePos, NULL); - - return ma_vec3f_normalize(ma_vec3f_neg(relativePos)); -} - -MA_API void ma_sound_set_position(ma_sound* pSound, float x, float y, float z) -{ - if (pSound == NULL) { - return; - } - - ma_spatializer_set_position(&pSound->engineNode.spatializer, x, y, z); -} - -MA_API ma_vec3f ma_sound_get_position(const ma_sound* pSound) -{ - if (pSound == NULL) { - return ma_vec3f_init_3f(0, 0, 0); - } - - return ma_spatializer_get_position(&pSound->engineNode.spatializer); -} - -MA_API void ma_sound_set_direction(ma_sound* pSound, float x, float y, float z) -{ - if (pSound == NULL) { - return; - } - - ma_spatializer_set_direction(&pSound->engineNode.spatializer, x, y, z); -} - -MA_API ma_vec3f ma_sound_get_direction(const ma_sound* pSound) -{ - if (pSound == NULL) { - return ma_vec3f_init_3f(0, 0, 0); - } - - return ma_spatializer_get_direction(&pSound->engineNode.spatializer); -} - -MA_API void ma_sound_set_velocity(ma_sound* pSound, float x, float y, float z) -{ - if (pSound == NULL) { - return; - } - - ma_spatializer_set_velocity(&pSound->engineNode.spatializer, x, y, z); -} - -MA_API ma_vec3f ma_sound_get_velocity(const ma_sound* pSound) -{ - if (pSound == NULL) { - return ma_vec3f_init_3f(0, 0, 0); - } - - return ma_spatializer_get_velocity(&pSound->engineNode.spatializer); -} - -MA_API void ma_sound_set_attenuation_model(ma_sound* pSound, ma_attenuation_model attenuationModel) -{ - if (pSound == NULL) { - return; - } - - ma_spatializer_set_attenuation_model(&pSound->engineNode.spatializer, attenuationModel); -} - -MA_API ma_attenuation_model ma_sound_get_attenuation_model(const ma_sound* pSound) -{ - if (pSound == NULL) { - return ma_attenuation_model_none; - } - - return ma_spatializer_get_attenuation_model(&pSound->engineNode.spatializer); -} - -MA_API void ma_sound_set_positioning(ma_sound* pSound, ma_positioning positioning) -{ - if (pSound == NULL) { - return; - } - - ma_spatializer_set_positioning(&pSound->engineNode.spatializer, positioning); -} - -MA_API ma_positioning ma_sound_get_positioning(const ma_sound* pSound) -{ - if (pSound == NULL) { - return ma_positioning_absolute; - } - - return ma_spatializer_get_positioning(&pSound->engineNode.spatializer); -} - -MA_API void ma_sound_set_rolloff(ma_sound* pSound, float rolloff) -{ - if (pSound == NULL) { - return; - } - - ma_spatializer_set_rolloff(&pSound->engineNode.spatializer, rolloff); -} - -MA_API float ma_sound_get_rolloff(const ma_sound* pSound) -{ - if (pSound == NULL) { - return 0; - } - - return ma_spatializer_get_rolloff(&pSound->engineNode.spatializer); -} - -MA_API void ma_sound_set_min_gain(ma_sound* pSound, float minGain) -{ - if (pSound == NULL) { - return; - } - - ma_spatializer_set_min_gain(&pSound->engineNode.spatializer, minGain); -} - -MA_API float ma_sound_get_min_gain(const ma_sound* pSound) -{ - if (pSound == NULL) { - return 0; - } - - return ma_spatializer_get_min_gain(&pSound->engineNode.spatializer); -} - -MA_API void ma_sound_set_max_gain(ma_sound* pSound, float maxGain) -{ - if (pSound == NULL) { - return; - } - - ma_spatializer_set_max_gain(&pSound->engineNode.spatializer, maxGain); -} - -MA_API float ma_sound_get_max_gain(const ma_sound* pSound) -{ - if (pSound == NULL) { - return 0; - } - - return ma_spatializer_get_max_gain(&pSound->engineNode.spatializer); -} - -MA_API void ma_sound_set_min_distance(ma_sound* pSound, float minDistance) -{ - if (pSound == NULL) { - return; - } - - ma_spatializer_set_min_distance(&pSound->engineNode.spatializer, minDistance); -} - -MA_API float ma_sound_get_min_distance(const ma_sound* pSound) -{ - if (pSound == NULL) { - return 0; - } - - return ma_spatializer_get_min_distance(&pSound->engineNode.spatializer); -} - -MA_API void ma_sound_set_max_distance(ma_sound* pSound, float maxDistance) -{ - if (pSound == NULL) { - return; - } - - ma_spatializer_set_max_distance(&pSound->engineNode.spatializer, maxDistance); -} - -MA_API float ma_sound_get_max_distance(const ma_sound* pSound) -{ - if (pSound == NULL) { - return 0; - } - - return ma_spatializer_get_max_distance(&pSound->engineNode.spatializer); -} - -MA_API void ma_sound_set_cone(ma_sound* pSound, float innerAngleInRadians, float outerAngleInRadians, float outerGain) -{ - if (pSound == NULL) { - return; - } - - ma_spatializer_set_cone(&pSound->engineNode.spatializer, innerAngleInRadians, outerAngleInRadians, outerGain); -} - -MA_API void ma_sound_get_cone(const ma_sound* pSound, float* pInnerAngleInRadians, float* pOuterAngleInRadians, float* pOuterGain) -{ - if (pInnerAngleInRadians != NULL) { - *pInnerAngleInRadians = 0; - } - - if (pOuterAngleInRadians != NULL) { - *pOuterAngleInRadians = 0; - } - - if (pOuterGain != NULL) { - *pOuterGain = 0; - } - - if (pSound == NULL) { - return; - } - - ma_spatializer_get_cone(&pSound->engineNode.spatializer, pInnerAngleInRadians, pOuterAngleInRadians, pOuterGain); -} - -MA_API void ma_sound_set_doppler_factor(ma_sound* pSound, float dopplerFactor) -{ - if (pSound == NULL) { - return; - } - - ma_spatializer_set_doppler_factor(&pSound->engineNode.spatializer, dopplerFactor); -} - -MA_API float ma_sound_get_doppler_factor(const ma_sound* pSound) -{ - if (pSound == NULL) { - return 0; - } - - return ma_spatializer_get_doppler_factor(&pSound->engineNode.spatializer); -} - -MA_API void ma_sound_set_directional_attenuation_factor(ma_sound* pSound, float directionalAttenuationFactor) -{ - if (pSound == NULL) { - return; - } - - ma_spatializer_set_directional_attenuation_factor(&pSound->engineNode.spatializer, directionalAttenuationFactor); -} - -MA_API float ma_sound_get_directional_attenuation_factor(const ma_sound* pSound) -{ - if (pSound == NULL) { - return 1; - } - - return ma_spatializer_get_directional_attenuation_factor(&pSound->engineNode.spatializer); -} - - -MA_API void ma_sound_set_fade_in_pcm_frames(ma_sound* pSound, float volumeBeg, float volumeEnd, ma_uint64 fadeLengthInFrames) -{ - if (pSound == NULL) { - return; - } - - ma_sound_set_fade_start_in_pcm_frames(pSound, volumeBeg, volumeEnd, fadeLengthInFrames, (~(ma_uint64)0)); -} - -MA_API void ma_sound_set_fade_in_milliseconds(ma_sound* pSound, float volumeBeg, float volumeEnd, ma_uint64 fadeLengthInMilliseconds) -{ - if (pSound == NULL) { - return; - } - - ma_sound_set_fade_in_pcm_frames(pSound, volumeBeg, volumeEnd, (fadeLengthInMilliseconds * pSound->engineNode.fader.config.sampleRate) / 1000); -} - -MA_API void ma_sound_set_fade_start_in_pcm_frames(ma_sound* pSound, float volumeBeg, float volumeEnd, ma_uint64 fadeLengthInFrames, ma_uint64 absoluteGlobalTimeInFrames) -{ - if (pSound == NULL) { - return; - } - - /* - We don't want to update the fader at this point because we need to use the engine's current time - to derive the fader's start offset. The timer is being updated on the audio thread so in order to - do this as accurately as possible we'll need to defer this to the audio thread. - */ - ma_atomic_float_set(&pSound->engineNode.fadeSettings.volumeBeg, volumeBeg); - ma_atomic_float_set(&pSound->engineNode.fadeSettings.volumeEnd, volumeEnd); - ma_atomic_uint64_set(&pSound->engineNode.fadeSettings.fadeLengthInFrames, fadeLengthInFrames); - ma_atomic_uint64_set(&pSound->engineNode.fadeSettings.absoluteGlobalTimeInFrames, absoluteGlobalTimeInFrames); -} - -MA_API void ma_sound_set_fade_start_in_milliseconds(ma_sound* pSound, float volumeBeg, float volumeEnd, ma_uint64 fadeLengthInMilliseconds, ma_uint64 absoluteGlobalTimeInMilliseconds) -{ - ma_uint32 sampleRate; - - if (pSound == NULL) { - return; - } - - sampleRate = ma_engine_get_sample_rate(ma_sound_get_engine(pSound)); - - ma_sound_set_fade_start_in_pcm_frames(pSound, volumeBeg, volumeEnd, (fadeLengthInMilliseconds * sampleRate) / 1000, (absoluteGlobalTimeInMilliseconds * sampleRate) / 1000); -} - -MA_API float ma_sound_get_current_fade_volume(const ma_sound* pSound) -{ - if (pSound == NULL) { - return MA_INVALID_ARGS; - } - - return ma_fader_get_current_volume(&pSound->engineNode.fader); -} - -MA_API void ma_sound_set_start_time_in_pcm_frames(ma_sound* pSound, ma_uint64 absoluteGlobalTimeInFrames) -{ - if (pSound == NULL) { - return; - } - - ma_node_set_state_time(pSound, ma_node_state_started, absoluteGlobalTimeInFrames); -} - -MA_API void ma_sound_set_start_time_in_milliseconds(ma_sound* pSound, ma_uint64 absoluteGlobalTimeInMilliseconds) -{ - if (pSound == NULL) { - return; - } - - ma_sound_set_start_time_in_pcm_frames(pSound, absoluteGlobalTimeInMilliseconds * ma_engine_get_sample_rate(ma_sound_get_engine(pSound)) / 1000); -} - -MA_API void ma_sound_set_stop_time_in_pcm_frames(ma_sound* pSound, ma_uint64 absoluteGlobalTimeInFrames) -{ - if (pSound == NULL) { - return; - } - - ma_sound_set_stop_time_with_fade_in_pcm_frames(pSound, absoluteGlobalTimeInFrames, 0); -} - -MA_API void ma_sound_set_stop_time_in_milliseconds(ma_sound* pSound, ma_uint64 absoluteGlobalTimeInMilliseconds) -{ - if (pSound == NULL) { - return; - } - - ma_sound_set_stop_time_in_pcm_frames(pSound, absoluteGlobalTimeInMilliseconds * ma_engine_get_sample_rate(ma_sound_get_engine(pSound)) / 1000); -} - -MA_API void ma_sound_set_stop_time_with_fade_in_pcm_frames(ma_sound* pSound, ma_uint64 stopAbsoluteGlobalTimeInFrames, ma_uint64 fadeLengthInFrames) -{ - if (pSound == NULL) { - return; - } - - if (fadeLengthInFrames > 0) { - if (fadeLengthInFrames > stopAbsoluteGlobalTimeInFrames) { - fadeLengthInFrames = stopAbsoluteGlobalTimeInFrames; - } - - ma_sound_set_fade_start_in_pcm_frames(pSound, -1, 0, fadeLengthInFrames, stopAbsoluteGlobalTimeInFrames - fadeLengthInFrames); - } - - ma_node_set_state_time(pSound, ma_node_state_stopped, stopAbsoluteGlobalTimeInFrames); -} - -MA_API void ma_sound_set_stop_time_with_fade_in_milliseconds(ma_sound* pSound, ma_uint64 stopAbsoluteGlobalTimeInMilliseconds, ma_uint64 fadeLengthInMilliseconds) -{ - ma_uint32 sampleRate; - - if (pSound == NULL) { - return; - } - - sampleRate = ma_engine_get_sample_rate(ma_sound_get_engine(pSound)); - - ma_sound_set_stop_time_with_fade_in_pcm_frames(pSound, (stopAbsoluteGlobalTimeInMilliseconds * sampleRate) / 1000, (fadeLengthInMilliseconds * sampleRate) / 1000); -} - -MA_API ma_bool32 ma_sound_is_playing(const ma_sound* pSound) -{ - if (pSound == NULL) { - return MA_FALSE; - } - - return ma_node_get_state_by_time(pSound, ma_engine_get_time_in_pcm_frames(ma_sound_get_engine(pSound))) == ma_node_state_started; -} - -MA_API ma_uint64 ma_sound_get_time_in_pcm_frames(const ma_sound* pSound) -{ - if (pSound == NULL) { - return 0; - } - - return ma_node_get_time(pSound); -} - -MA_API ma_uint64 ma_sound_get_time_in_milliseconds(const ma_sound* pSound) -{ - return ma_sound_get_time_in_pcm_frames(pSound) * 1000 / ma_engine_get_sample_rate(ma_sound_get_engine(pSound)); -} - -MA_API void ma_sound_set_looping(ma_sound* pSound, ma_bool32 isLooping) -{ - if (pSound == NULL) { - return; - } - - /* Looping is only a valid concept if the sound is backed by a data source. */ - if (pSound->pDataSource == NULL) { - return; - } - - /* The looping state needs to be applied to the data source in order for any looping to actually happen. */ - ma_data_source_set_looping(pSound->pDataSource, isLooping); -} - -MA_API ma_bool32 ma_sound_is_looping(const ma_sound* pSound) -{ - if (pSound == NULL) { - return MA_FALSE; - } - - /* There is no notion of looping for sounds that are not backed by a data source. */ - if (pSound->pDataSource == NULL) { - return MA_FALSE; - } - - return ma_data_source_is_looping(pSound->pDataSource); -} - -MA_API ma_bool32 ma_sound_at_end(const ma_sound* pSound) -{ - if (pSound == NULL) { - return MA_FALSE; - } - - /* There is no notion of an end of a sound if it's not backed by a data source. */ - if (pSound->pDataSource == NULL) { - return MA_FALSE; - } - - return ma_sound_get_at_end(pSound); -} - -MA_API ma_result ma_sound_seek_to_pcm_frame(ma_sound* pSound, ma_uint64 frameIndex) -{ - if (pSound == NULL) { - return MA_INVALID_ARGS; - } - - /* Seeking is only valid for sounds that are backed by a data source. */ - if (pSound->pDataSource == NULL) { - return MA_INVALID_OPERATION; - } - - /* We can't be seeking while reading at the same time. We just set the seek target and get the mixing thread to do the actual seek. */ - ma_atomic_exchange_64(&pSound->seekTarget, frameIndex); - - return MA_SUCCESS; -} - -MA_API ma_result ma_sound_seek_to_second(ma_sound* pSound, float seekPointInSeconds) -{ - ma_uint64 frameIndex; - ma_uint32 sampleRate; - ma_result result; - - if (pSound == NULL) { - return MA_INVALID_ARGS; - } - - result = ma_sound_get_data_format(pSound, NULL, NULL, &sampleRate, NULL, 0); - if (result != MA_SUCCESS) { - return result; - } - - /* We need PCM frames. We need to convert first */ - frameIndex = (ma_uint64)(seekPointInSeconds * sampleRate); - - return ma_sound_seek_to_pcm_frame(pSound, frameIndex); -} - -MA_API ma_result ma_sound_get_data_format(ma_sound* pSound, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate, ma_channel* pChannelMap, size_t channelMapCap) -{ - if (pSound == NULL) { - return MA_INVALID_ARGS; - } - - /* The data format is retrieved directly from the data source if the sound is backed by one. Otherwise we pull it from the node. */ - if (pSound->pDataSource == NULL) { - ma_uint32 channels; - - if (pFormat != NULL) { - *pFormat = ma_format_f32; - } - - channels = ma_node_get_input_channels(&pSound->engineNode, 0); - if (pChannels != NULL) { - *pChannels = channels; - } - - if (pSampleRate != NULL) { - *pSampleRate = pSound->engineNode.resampler.config.sampleRateIn; - } - - if (pChannelMap != NULL) { - ma_channel_map_init_standard(ma_standard_channel_map_default, pChannelMap, channelMapCap, channels); - } - - return MA_SUCCESS; - } else { - return ma_data_source_get_data_format(pSound->pDataSource, pFormat, pChannels, pSampleRate, pChannelMap, channelMapCap); - } -} - -MA_API ma_result ma_sound_get_cursor_in_pcm_frames(ma_sound* pSound, ma_uint64* pCursor) -{ - ma_uint64 seekTarget; - - if (pSound == NULL) { - return MA_INVALID_ARGS; - } - - /* The notion of a cursor is only valid for sounds that are backed by a data source. */ - if (pSound->pDataSource == NULL) { - return MA_INVALID_OPERATION; - } - - seekTarget = ma_atomic_load_64(&pSound->seekTarget); - if (seekTarget != MA_SEEK_TARGET_NONE) { - *pCursor = seekTarget; - return MA_SUCCESS; - } else { - return ma_data_source_get_cursor_in_pcm_frames(pSound->pDataSource, pCursor); - } -} - -MA_API ma_result ma_sound_get_length_in_pcm_frames(ma_sound* pSound, ma_uint64* pLength) -{ - if (pSound == NULL) { - return MA_INVALID_ARGS; - } - - /* The notion of a sound length is only valid for sounds that are backed by a data source. */ - if (pSound->pDataSource == NULL) { - return MA_INVALID_OPERATION; - } - - return ma_data_source_get_length_in_pcm_frames(pSound->pDataSource, pLength); -} - -MA_API ma_result ma_sound_get_cursor_in_seconds(ma_sound* pSound, float* pCursor) -{ - ma_result result; - ma_uint64 cursorInPCMFrames; - ma_uint32 sampleRate; - - if (pCursor != NULL) { - *pCursor = 0; - } - - result = ma_sound_get_cursor_in_pcm_frames(pSound, &cursorInPCMFrames); - if (result != MA_SUCCESS) { - return result; - } - - result = ma_sound_get_data_format(pSound, NULL, NULL, &sampleRate, NULL, 0); - if (result != MA_SUCCESS) { - return result; - } - - /* VC6 does not support division of unsigned 64-bit integers with floating point numbers. Need to use a signed number. This shouldn't effect anything in practice. */ - *pCursor = (ma_int64)cursorInPCMFrames / (float)sampleRate; - - return MA_SUCCESS; -} - -MA_API ma_result ma_sound_get_length_in_seconds(ma_sound* pSound, float* pLength) -{ - if (pSound == NULL) { - return MA_INVALID_ARGS; - } - - /* The notion of a sound length is only valid for sounds that are backed by a data source. */ - if (pSound->pDataSource == NULL) { - return MA_INVALID_OPERATION; - } - - return ma_data_source_get_length_in_seconds(pSound->pDataSource, pLength); -} - -MA_API ma_result ma_sound_set_end_callback(ma_sound* pSound, ma_sound_end_proc callback, void* pUserData) -{ - if (pSound == NULL) { - return MA_INVALID_ARGS; - } - - /* The notion of an end is only valid for sounds that are backed by a data source. */ - if (pSound->pDataSource == NULL) { - return MA_INVALID_OPERATION; - } - - pSound->endCallback = callback; - pSound->pEndCallbackUserData = pUserData; - - return MA_SUCCESS; -} - - -MA_API ma_result ma_sound_group_init(ma_engine* pEngine, ma_uint32 flags, ma_sound_group* pParentGroup, ma_sound_group* pGroup) -{ - ma_sound_group_config config = ma_sound_group_config_init_2(pEngine); - config.flags = flags; - config.pInitialAttachment = pParentGroup; - return ma_sound_group_init_ex(pEngine, &config, pGroup); -} - -MA_API ma_result ma_sound_group_init_ex(ma_engine* pEngine, const ma_sound_group_config* pConfig, ma_sound_group* pGroup) -{ - ma_sound_config soundConfig; - - if (pGroup == NULL) { - return MA_INVALID_ARGS; - } - - MA_ZERO_OBJECT(pGroup); - - if (pConfig == NULL) { - return MA_INVALID_ARGS; - } - - /* A sound group is just a sound without a data source. */ - soundConfig = *pConfig; - soundConfig.pFilePath = NULL; - soundConfig.pFilePathW = NULL; - soundConfig.pDataSource = NULL; - - /* - Groups need to have spatialization disabled by default because I think it'll be pretty rare - that programs will want to spatialize groups (but not unheard of). Certainly it feels like - disabling this by default feels like the right option. Spatialization can be enabled with a - call to ma_sound_group_set_spatialization_enabled(). - */ - soundConfig.flags |= MA_SOUND_FLAG_NO_SPATIALIZATION; - - return ma_sound_init_ex(pEngine, &soundConfig, pGroup); -} - -MA_API void ma_sound_group_uninit(ma_sound_group* pGroup) -{ - ma_sound_uninit(pGroup); -} - -MA_API ma_engine* ma_sound_group_get_engine(const ma_sound_group* pGroup) -{ - return ma_sound_get_engine(pGroup); -} - -MA_API ma_result ma_sound_group_start(ma_sound_group* pGroup) -{ - return ma_sound_start(pGroup); -} - -MA_API ma_result ma_sound_group_stop(ma_sound_group* pGroup) -{ - return ma_sound_stop(pGroup); -} - -MA_API void ma_sound_group_set_volume(ma_sound_group* pGroup, float volume) -{ - ma_sound_set_volume(pGroup, volume); -} - -MA_API float ma_sound_group_get_volume(const ma_sound_group* pGroup) -{ - return ma_sound_get_volume(pGroup); -} - -MA_API void ma_sound_group_set_pan(ma_sound_group* pGroup, float pan) -{ - ma_sound_set_pan(pGroup, pan); -} - -MA_API float ma_sound_group_get_pan(const ma_sound_group* pGroup) -{ - return ma_sound_get_pan(pGroup); -} - -MA_API void ma_sound_group_set_pan_mode(ma_sound_group* pGroup, ma_pan_mode panMode) -{ - ma_sound_set_pan_mode(pGroup, panMode); -} - -MA_API ma_pan_mode ma_sound_group_get_pan_mode(const ma_sound_group* pGroup) -{ - return ma_sound_get_pan_mode(pGroup); -} - -MA_API void ma_sound_group_set_pitch(ma_sound_group* pGroup, float pitch) -{ - ma_sound_set_pitch(pGroup, pitch); -} - -MA_API float ma_sound_group_get_pitch(const ma_sound_group* pGroup) -{ - return ma_sound_get_pitch(pGroup); -} - -MA_API void ma_sound_group_set_spatialization_enabled(ma_sound_group* pGroup, ma_bool32 enabled) -{ - ma_sound_set_spatialization_enabled(pGroup, enabled); -} - -MA_API ma_bool32 ma_sound_group_is_spatialization_enabled(const ma_sound_group* pGroup) -{ - return ma_sound_is_spatialization_enabled(pGroup); -} - -MA_API void ma_sound_group_set_pinned_listener_index(ma_sound_group* pGroup, ma_uint32 listenerIndex) -{ - ma_sound_set_pinned_listener_index(pGroup, listenerIndex); -} - -MA_API ma_uint32 ma_sound_group_get_pinned_listener_index(const ma_sound_group* pGroup) -{ - return ma_sound_get_pinned_listener_index(pGroup); -} - -MA_API ma_uint32 ma_sound_group_get_listener_index(const ma_sound_group* pGroup) -{ - return ma_sound_get_listener_index(pGroup); -} - -MA_API ma_vec3f ma_sound_group_get_direction_to_listener(const ma_sound_group* pGroup) -{ - return ma_sound_get_direction_to_listener(pGroup); -} - -MA_API void ma_sound_group_set_position(ma_sound_group* pGroup, float x, float y, float z) -{ - ma_sound_set_position(pGroup, x, y, z); -} - -MA_API ma_vec3f ma_sound_group_get_position(const ma_sound_group* pGroup) -{ - return ma_sound_get_position(pGroup); -} - -MA_API void ma_sound_group_set_direction(ma_sound_group* pGroup, float x, float y, float z) -{ - ma_sound_set_direction(pGroup, x, y, z); -} - -MA_API ma_vec3f ma_sound_group_get_direction(const ma_sound_group* pGroup) -{ - return ma_sound_get_direction(pGroup); -} - -MA_API void ma_sound_group_set_velocity(ma_sound_group* pGroup, float x, float y, float z) -{ - ma_sound_set_velocity(pGroup, x, y, z); -} - -MA_API ma_vec3f ma_sound_group_get_velocity(const ma_sound_group* pGroup) -{ - return ma_sound_get_velocity(pGroup); -} - -MA_API void ma_sound_group_set_attenuation_model(ma_sound_group* pGroup, ma_attenuation_model attenuationModel) -{ - ma_sound_set_attenuation_model(pGroup, attenuationModel); -} - -MA_API ma_attenuation_model ma_sound_group_get_attenuation_model(const ma_sound_group* pGroup) -{ - return ma_sound_get_attenuation_model(pGroup); -} - -MA_API void ma_sound_group_set_positioning(ma_sound_group* pGroup, ma_positioning positioning) -{ - ma_sound_set_positioning(pGroup, positioning); -} - -MA_API ma_positioning ma_sound_group_get_positioning(const ma_sound_group* pGroup) -{ - return ma_sound_get_positioning(pGroup); -} - -MA_API void ma_sound_group_set_rolloff(ma_sound_group* pGroup, float rolloff) -{ - ma_sound_set_rolloff(pGroup, rolloff); -} - -MA_API float ma_sound_group_get_rolloff(const ma_sound_group* pGroup) -{ - return ma_sound_get_rolloff(pGroup); -} - -MA_API void ma_sound_group_set_min_gain(ma_sound_group* pGroup, float minGain) -{ - ma_sound_set_min_gain(pGroup, minGain); -} - -MA_API float ma_sound_group_get_min_gain(const ma_sound_group* pGroup) -{ - return ma_sound_get_min_gain(pGroup); -} - -MA_API void ma_sound_group_set_max_gain(ma_sound_group* pGroup, float maxGain) -{ - ma_sound_set_max_gain(pGroup, maxGain); -} - -MA_API float ma_sound_group_get_max_gain(const ma_sound_group* pGroup) -{ - return ma_sound_get_max_gain(pGroup); -} - -MA_API void ma_sound_group_set_min_distance(ma_sound_group* pGroup, float minDistance) -{ - ma_sound_set_min_distance(pGroup, minDistance); -} - -MA_API float ma_sound_group_get_min_distance(const ma_sound_group* pGroup) -{ - return ma_sound_get_min_distance(pGroup); -} - -MA_API void ma_sound_group_set_max_distance(ma_sound_group* pGroup, float maxDistance) -{ - ma_sound_set_max_distance(pGroup, maxDistance); -} - -MA_API float ma_sound_group_get_max_distance(const ma_sound_group* pGroup) -{ - return ma_sound_get_max_distance(pGroup); -} - -MA_API void ma_sound_group_set_cone(ma_sound_group* pGroup, float innerAngleInRadians, float outerAngleInRadians, float outerGain) -{ - ma_sound_set_cone(pGroup, innerAngleInRadians, outerAngleInRadians, outerGain); -} - -MA_API void ma_sound_group_get_cone(const ma_sound_group* pGroup, float* pInnerAngleInRadians, float* pOuterAngleInRadians, float* pOuterGain) -{ - ma_sound_get_cone(pGroup, pInnerAngleInRadians, pOuterAngleInRadians, pOuterGain); -} - -MA_API void ma_sound_group_set_doppler_factor(ma_sound_group* pGroup, float dopplerFactor) -{ - ma_sound_set_doppler_factor(pGroup, dopplerFactor); -} - -MA_API float ma_sound_group_get_doppler_factor(const ma_sound_group* pGroup) -{ - return ma_sound_get_doppler_factor(pGroup); -} - -MA_API void ma_sound_group_set_directional_attenuation_factor(ma_sound_group* pGroup, float directionalAttenuationFactor) -{ - ma_sound_set_directional_attenuation_factor(pGroup, directionalAttenuationFactor); -} - -MA_API float ma_sound_group_get_directional_attenuation_factor(const ma_sound_group* pGroup) -{ - return ma_sound_get_directional_attenuation_factor(pGroup); -} - -MA_API void ma_sound_group_set_fade_in_pcm_frames(ma_sound_group* pGroup, float volumeBeg, float volumeEnd, ma_uint64 fadeLengthInFrames) -{ - ma_sound_set_fade_in_pcm_frames(pGroup, volumeBeg, volumeEnd, fadeLengthInFrames); -} - -MA_API void ma_sound_group_set_fade_in_milliseconds(ma_sound_group* pGroup, float volumeBeg, float volumeEnd, ma_uint64 fadeLengthInMilliseconds) -{ - ma_sound_set_fade_in_milliseconds(pGroup, volumeBeg, volumeEnd, fadeLengthInMilliseconds); -} - -MA_API float ma_sound_group_get_current_fade_volume(ma_sound_group* pGroup) -{ - return ma_sound_get_current_fade_volume(pGroup); -} - -MA_API void ma_sound_group_set_start_time_in_pcm_frames(ma_sound_group* pGroup, ma_uint64 absoluteGlobalTimeInFrames) -{ - ma_sound_set_start_time_in_pcm_frames(pGroup, absoluteGlobalTimeInFrames); -} - -MA_API void ma_sound_group_set_start_time_in_milliseconds(ma_sound_group* pGroup, ma_uint64 absoluteGlobalTimeInMilliseconds) -{ - ma_sound_set_start_time_in_milliseconds(pGroup, absoluteGlobalTimeInMilliseconds); -} - -MA_API void ma_sound_group_set_stop_time_in_pcm_frames(ma_sound_group* pGroup, ma_uint64 absoluteGlobalTimeInFrames) -{ - ma_sound_set_stop_time_in_pcm_frames(pGroup, absoluteGlobalTimeInFrames); -} - -MA_API void ma_sound_group_set_stop_time_in_milliseconds(ma_sound_group* pGroup, ma_uint64 absoluteGlobalTimeInMilliseconds) -{ - ma_sound_set_stop_time_in_milliseconds(pGroup, absoluteGlobalTimeInMilliseconds); -} - -MA_API ma_bool32 ma_sound_group_is_playing(const ma_sound_group* pGroup) -{ - return ma_sound_is_playing(pGroup); -} - -MA_API ma_uint64 ma_sound_group_get_time_in_pcm_frames(const ma_sound_group* pGroup) -{ - return ma_sound_get_time_in_pcm_frames(pGroup); -} -#endif /* MA_NO_ENGINE */ -/* END SECTION: miniaudio_engine.c */ - - - -/************************************************************************************************************************************************************** -*************************************************************************************************************************************************************** - -Auto Generated -============== -All code below is auto-generated from a tool. This mostly consists of decoding backend implementations such as ma_dr_wav, ma_dr_flac, etc. If you find a bug in the -code below please report the bug to the respective repository for the relevant project (probably dr_libs). - -*************************************************************************************************************************************************************** -**************************************************************************************************************************************************************/ -#if !defined(MA_NO_WAV) && (!defined(MA_NO_DECODING) || !defined(MA_NO_ENCODING)) -#if !defined(MA_DR_WAV_IMPLEMENTATION) -/* dr_wav_c begin */ -#ifndef ma_dr_wav_c -#define ma_dr_wav_c -#ifdef __MRC__ -#pragma options opt off -#endif -#include -#include -#include -#ifndef MA_DR_WAV_NO_STDIO -#include -#ifndef MA_DR_WAV_NO_WCHAR -#include -#endif -#endif -#ifndef MA_DR_WAV_ASSERT -#include -#define MA_DR_WAV_ASSERT(expression) assert(expression) -#endif -#ifndef MA_DR_WAV_MALLOC -#define MA_DR_WAV_MALLOC(sz) malloc((sz)) -#endif -#ifndef MA_DR_WAV_REALLOC -#define MA_DR_WAV_REALLOC(p, sz) realloc((p), (sz)) -#endif -#ifndef MA_DR_WAV_FREE -#define MA_DR_WAV_FREE(p) free((p)) -#endif -#ifndef MA_DR_WAV_COPY_MEMORY -#define MA_DR_WAV_COPY_MEMORY(dst, src, sz) memcpy((dst), (src), (sz)) -#endif -#ifndef MA_DR_WAV_ZERO_MEMORY -#define MA_DR_WAV_ZERO_MEMORY(p, sz) memset((p), 0, (sz)) -#endif -#ifndef MA_DR_WAV_ZERO_OBJECT -#define MA_DR_WAV_ZERO_OBJECT(p) MA_DR_WAV_ZERO_MEMORY((p), sizeof(*p)) -#endif -#define ma_dr_wav_countof(x) (sizeof(x) / sizeof(x[0])) -#define ma_dr_wav_align(x, a) ((((x) + (a) - 1) / (a)) * (a)) -#define ma_dr_wav_min(a, b) (((a) < (b)) ? (a) : (b)) -#define ma_dr_wav_max(a, b) (((a) > (b)) ? (a) : (b)) -#define ma_dr_wav_clamp(x, lo, hi) (ma_dr_wav_max((lo), ma_dr_wav_min((hi), (x)))) -#define ma_dr_wav_offset_ptr(p, offset) (((ma_uint8*)(p)) + (offset)) -#define MA_DR_WAV_MAX_SIMD_VECTOR_SIZE 32 -#define MA_DR_WAV_INT64_MIN ((ma_int64) ((ma_uint64)0x80000000 << 32)) -#define MA_DR_WAV_INT64_MAX ((ma_int64)(((ma_uint64)0x7FFFFFFF << 32) | 0xFFFFFFFF)) -#if defined(_MSC_VER) && _MSC_VER >= 1400 - #define MA_DR_WAV_HAS_BYTESWAP16_INTRINSIC - #define MA_DR_WAV_HAS_BYTESWAP32_INTRINSIC - #define MA_DR_WAV_HAS_BYTESWAP64_INTRINSIC -#elif defined(__clang__) - #if defined(__has_builtin) - #if __has_builtin(__builtin_bswap16) - #define MA_DR_WAV_HAS_BYTESWAP16_INTRINSIC - #endif - #if __has_builtin(__builtin_bswap32) - #define MA_DR_WAV_HAS_BYTESWAP32_INTRINSIC - #endif - #if __has_builtin(__builtin_bswap64) - #define MA_DR_WAV_HAS_BYTESWAP64_INTRINSIC - #endif - #endif -#elif defined(__GNUC__) - #if ((__GNUC__ > 4) || (__GNUC__ == 4 && __GNUC_MINOR__ >= 3)) - #define MA_DR_WAV_HAS_BYTESWAP32_INTRINSIC - #define MA_DR_WAV_HAS_BYTESWAP64_INTRINSIC - #endif - #if ((__GNUC__ > 4) || (__GNUC__ == 4 && __GNUC_MINOR__ >= 8)) - #define MA_DR_WAV_HAS_BYTESWAP16_INTRINSIC - #endif -#endif -MA_API void ma_dr_wav_version(ma_uint32* pMajor, ma_uint32* pMinor, ma_uint32* pRevision) -{ - if (pMajor) { - *pMajor = MA_DR_WAV_VERSION_MAJOR; - } - if (pMinor) { - *pMinor = MA_DR_WAV_VERSION_MINOR; - } - if (pRevision) { - *pRevision = MA_DR_WAV_VERSION_REVISION; - } -} -MA_API const char* ma_dr_wav_version_string(void) -{ - return MA_DR_WAV_VERSION_STRING; -} -#ifndef MA_DR_WAV_MAX_SAMPLE_RATE -#define MA_DR_WAV_MAX_SAMPLE_RATE 384000 -#endif -#ifndef MA_DR_WAV_MAX_CHANNELS -#define MA_DR_WAV_MAX_CHANNELS 256 -#endif -#ifndef MA_DR_WAV_MAX_BITS_PER_SAMPLE -#define MA_DR_WAV_MAX_BITS_PER_SAMPLE 64 -#endif -static const ma_uint8 ma_dr_wavGUID_W64_RIFF[16] = {0x72,0x69,0x66,0x66, 0x2E,0x91, 0xCF,0x11, 0xA5,0xD6, 0x28,0xDB,0x04,0xC1,0x00,0x00}; -static const ma_uint8 ma_dr_wavGUID_W64_WAVE[16] = {0x77,0x61,0x76,0x65, 0xF3,0xAC, 0xD3,0x11, 0x8C,0xD1, 0x00,0xC0,0x4F,0x8E,0xDB,0x8A}; -static const ma_uint8 ma_dr_wavGUID_W64_FMT [16] = {0x66,0x6D,0x74,0x20, 0xF3,0xAC, 0xD3,0x11, 0x8C,0xD1, 0x00,0xC0,0x4F,0x8E,0xDB,0x8A}; -static const ma_uint8 ma_dr_wavGUID_W64_FACT[16] = {0x66,0x61,0x63,0x74, 0xF3,0xAC, 0xD3,0x11, 0x8C,0xD1, 0x00,0xC0,0x4F,0x8E,0xDB,0x8A}; -static const ma_uint8 ma_dr_wavGUID_W64_DATA[16] = {0x64,0x61,0x74,0x61, 0xF3,0xAC, 0xD3,0x11, 0x8C,0xD1, 0x00,0xC0,0x4F,0x8E,0xDB,0x8A}; -static MA_INLINE int ma_dr_wav__is_little_endian(void) -{ -#if defined(MA_X86) || defined(MA_X64) - return MA_TRUE; -#elif defined(__BYTE_ORDER) && defined(__LITTLE_ENDIAN) && __BYTE_ORDER == __LITTLE_ENDIAN - return MA_TRUE; -#else - int n = 1; - return (*(char*)&n) == 1; -#endif -} -static MA_INLINE void ma_dr_wav_bytes_to_guid(const ma_uint8* data, ma_uint8* guid) -{ - int i; - for (i = 0; i < 16; ++i) { - guid[i] = data[i]; - } -} -static MA_INLINE ma_uint16 ma_dr_wav__bswap16(ma_uint16 n) -{ -#ifdef MA_DR_WAV_HAS_BYTESWAP16_INTRINSIC - #if defined(_MSC_VER) - return _byteswap_ushort(n); - #elif defined(__GNUC__) || defined(__clang__) - return __builtin_bswap16(n); - #else - #error "This compiler does not support the byte swap intrinsic." - #endif -#else - return ((n & 0xFF00) >> 8) | - ((n & 0x00FF) << 8); -#endif -} -static MA_INLINE ma_uint32 ma_dr_wav__bswap32(ma_uint32 n) -{ -#ifdef MA_DR_WAV_HAS_BYTESWAP32_INTRINSIC - #if defined(_MSC_VER) - return _byteswap_ulong(n); - #elif defined(__GNUC__) || defined(__clang__) - #if defined(MA_ARM) && (defined(__ARM_ARCH) && __ARM_ARCH >= 6) && !defined(MA_64BIT) - ma_uint32 r; - __asm__ __volatile__ ( - #if defined(MA_64BIT) - "rev %w[out], %w[in]" : [out]"=r"(r) : [in]"r"(n) - #else - "rev %[out], %[in]" : [out]"=r"(r) : [in]"r"(n) - #endif - ); - return r; - #else - return __builtin_bswap32(n); - #endif - #else - #error "This compiler does not support the byte swap intrinsic." - #endif -#else - return ((n & 0xFF000000) >> 24) | - ((n & 0x00FF0000) >> 8) | - ((n & 0x0000FF00) << 8) | - ((n & 0x000000FF) << 24); -#endif -} -static MA_INLINE ma_uint64 ma_dr_wav__bswap64(ma_uint64 n) -{ -#ifdef MA_DR_WAV_HAS_BYTESWAP64_INTRINSIC - #if defined(_MSC_VER) - return _byteswap_uint64(n); - #elif defined(__GNUC__) || defined(__clang__) - return __builtin_bswap64(n); - #else - #error "This compiler does not support the byte swap intrinsic." - #endif -#else - return ((n & ((ma_uint64)0xFF000000 << 32)) >> 56) | - ((n & ((ma_uint64)0x00FF0000 << 32)) >> 40) | - ((n & ((ma_uint64)0x0000FF00 << 32)) >> 24) | - ((n & ((ma_uint64)0x000000FF << 32)) >> 8) | - ((n & ((ma_uint64)0xFF000000 )) << 8) | - ((n & ((ma_uint64)0x00FF0000 )) << 24) | - ((n & ((ma_uint64)0x0000FF00 )) << 40) | - ((n & ((ma_uint64)0x000000FF )) << 56); -#endif -} -static MA_INLINE ma_int16 ma_dr_wav__bswap_s16(ma_int16 n) -{ - return (ma_int16)ma_dr_wav__bswap16((ma_uint16)n); -} -static MA_INLINE void ma_dr_wav__bswap_samples_s16(ma_int16* pSamples, ma_uint64 sampleCount) -{ - ma_uint64 iSample; - for (iSample = 0; iSample < sampleCount; iSample += 1) { - pSamples[iSample] = ma_dr_wav__bswap_s16(pSamples[iSample]); - } -} -static MA_INLINE void ma_dr_wav__bswap_s24(ma_uint8* p) -{ - ma_uint8 t; - t = p[0]; - p[0] = p[2]; - p[2] = t; -} -static MA_INLINE void ma_dr_wav__bswap_samples_s24(ma_uint8* pSamples, ma_uint64 sampleCount) -{ - ma_uint64 iSample; - for (iSample = 0; iSample < sampleCount; iSample += 1) { - ma_uint8* pSample = pSamples + (iSample*3); - ma_dr_wav__bswap_s24(pSample); - } -} -static MA_INLINE ma_int32 ma_dr_wav__bswap_s32(ma_int32 n) -{ - return (ma_int32)ma_dr_wav__bswap32((ma_uint32)n); -} -static MA_INLINE void ma_dr_wav__bswap_samples_s32(ma_int32* pSamples, ma_uint64 sampleCount) -{ - ma_uint64 iSample; - for (iSample = 0; iSample < sampleCount; iSample += 1) { - pSamples[iSample] = ma_dr_wav__bswap_s32(pSamples[iSample]); - } -} -static MA_INLINE ma_int64 ma_dr_wav__bswap_s64(ma_int64 n) -{ - return (ma_int64)ma_dr_wav__bswap64((ma_uint64)n); -} -static MA_INLINE void ma_dr_wav__bswap_samples_s64(ma_int64* pSamples, ma_uint64 sampleCount) -{ - ma_uint64 iSample; - for (iSample = 0; iSample < sampleCount; iSample += 1) { - pSamples[iSample] = ma_dr_wav__bswap_s64(pSamples[iSample]); - } -} -static MA_INLINE float ma_dr_wav__bswap_f32(float n) -{ - union { - ma_uint32 i; - float f; - } x; - x.f = n; - x.i = ma_dr_wav__bswap32(x.i); - return x.f; -} -static MA_INLINE void ma_dr_wav__bswap_samples_f32(float* pSamples, ma_uint64 sampleCount) -{ - ma_uint64 iSample; - for (iSample = 0; iSample < sampleCount; iSample += 1) { - pSamples[iSample] = ma_dr_wav__bswap_f32(pSamples[iSample]); - } -} -static MA_INLINE void ma_dr_wav__bswap_samples(void* pSamples, ma_uint64 sampleCount, ma_uint32 bytesPerSample) -{ - switch (bytesPerSample) - { - case 1: - { - } break; - case 2: - { - ma_dr_wav__bswap_samples_s16((ma_int16*)pSamples, sampleCount); - } break; - case 3: - { - ma_dr_wav__bswap_samples_s24((ma_uint8*)pSamples, sampleCount); - } break; - case 4: - { - ma_dr_wav__bswap_samples_s32((ma_int32*)pSamples, sampleCount); - } break; - case 8: - { - ma_dr_wav__bswap_samples_s64((ma_int64*)pSamples, sampleCount); - } break; - default: - { - MA_DR_WAV_ASSERT(MA_FALSE); - } break; - } -} -MA_PRIVATE MA_INLINE ma_bool32 ma_dr_wav_is_container_be(ma_dr_wav_container container) -{ - if (container == ma_dr_wav_container_rifx || container == ma_dr_wav_container_aiff) { - return MA_TRUE; - } else { - return MA_FALSE; - } -} -MA_PRIVATE MA_INLINE ma_uint16 ma_dr_wav_bytes_to_u16_le(const ma_uint8* data) -{ - return ((ma_uint16)data[0] << 0) | ((ma_uint16)data[1] << 8); -} -MA_PRIVATE MA_INLINE ma_uint16 ma_dr_wav_bytes_to_u16_be(const ma_uint8* data) -{ - return ((ma_uint16)data[1] << 0) | ((ma_uint16)data[0] << 8); -} -MA_PRIVATE MA_INLINE ma_uint16 ma_dr_wav_bytes_to_u16_ex(const ma_uint8* data, ma_dr_wav_container container) -{ - if (ma_dr_wav_is_container_be(container)) { - return ma_dr_wav_bytes_to_u16_be(data); - } else { - return ma_dr_wav_bytes_to_u16_le(data); - } -} -MA_PRIVATE MA_INLINE ma_uint32 ma_dr_wav_bytes_to_u32_le(const ma_uint8* data) -{ - return ((ma_uint32)data[0] << 0) | ((ma_uint32)data[1] << 8) | ((ma_uint32)data[2] << 16) | ((ma_uint32)data[3] << 24); -} -MA_PRIVATE MA_INLINE ma_uint32 ma_dr_wav_bytes_to_u32_be(const ma_uint8* data) -{ - return ((ma_uint32)data[3] << 0) | ((ma_uint32)data[2] << 8) | ((ma_uint32)data[1] << 16) | ((ma_uint32)data[0] << 24); -} -MA_PRIVATE MA_INLINE ma_uint32 ma_dr_wav_bytes_to_u32_ex(const ma_uint8* data, ma_dr_wav_container container) -{ - if (ma_dr_wav_is_container_be(container)) { - return ma_dr_wav_bytes_to_u32_be(data); - } else { - return ma_dr_wav_bytes_to_u32_le(data); - } -} -MA_PRIVATE ma_int64 ma_dr_wav_aiff_extented_to_s64(const ma_uint8* data) -{ - ma_uint32 exponent = ((ma_uint32)data[0] << 8) | data[1]; - ma_uint64 hi = ((ma_uint64)data[2] << 24) | ((ma_uint64)data[3] << 16) | ((ma_uint64)data[4] << 8) | ((ma_uint64)data[5] << 0); - ma_uint64 lo = ((ma_uint64)data[6] << 24) | ((ma_uint64)data[7] << 16) | ((ma_uint64)data[8] << 8) | ((ma_uint64)data[9] << 0); - ma_uint64 significand = (hi << 32) | lo; - int sign = exponent >> 15; - exponent &= 0x7FFF; - if (exponent == 0 && significand == 0) { - return 0; - } else if (exponent == 0x7FFF) { - return sign ? MA_DR_WAV_INT64_MIN : MA_DR_WAV_INT64_MAX; - } - exponent -= 16383; - if (exponent > 63) { - return sign ? MA_DR_WAV_INT64_MIN : MA_DR_WAV_INT64_MAX; - } else if (exponent < 1) { - return 0; - } - significand >>= (63 - exponent); - if (sign) { - return -(ma_int64)significand; - } else { - return (ma_int64)significand; - } -} -MA_PRIVATE void* ma_dr_wav__malloc_default(size_t sz, void* pUserData) -{ - (void)pUserData; - return MA_DR_WAV_MALLOC(sz); -} -MA_PRIVATE void* ma_dr_wav__realloc_default(void* p, size_t sz, void* pUserData) -{ - (void)pUserData; - return MA_DR_WAV_REALLOC(p, sz); -} -MA_PRIVATE void ma_dr_wav__free_default(void* p, void* pUserData) -{ - (void)pUserData; - MA_DR_WAV_FREE(p); -} -MA_PRIVATE void* ma_dr_wav__malloc_from_callbacks(size_t sz, const ma_allocation_callbacks* pAllocationCallbacks) -{ - if (pAllocationCallbacks == NULL) { - return NULL; - } - if (pAllocationCallbacks->onMalloc != NULL) { - return pAllocationCallbacks->onMalloc(sz, pAllocationCallbacks->pUserData); - } - if (pAllocationCallbacks->onRealloc != NULL) { - return pAllocationCallbacks->onRealloc(NULL, sz, pAllocationCallbacks->pUserData); - } - return NULL; -} -MA_PRIVATE void* ma_dr_wav__realloc_from_callbacks(void* p, size_t szNew, size_t szOld, const ma_allocation_callbacks* pAllocationCallbacks) -{ - if (pAllocationCallbacks == NULL) { - return NULL; - } - if (pAllocationCallbacks->onRealloc != NULL) { - return pAllocationCallbacks->onRealloc(p, szNew, pAllocationCallbacks->pUserData); - } - if (pAllocationCallbacks->onMalloc != NULL && pAllocationCallbacks->onFree != NULL) { - void* p2; - p2 = pAllocationCallbacks->onMalloc(szNew, pAllocationCallbacks->pUserData); - if (p2 == NULL) { - return NULL; - } - if (p != NULL) { - MA_DR_WAV_COPY_MEMORY(p2, p, szOld); - pAllocationCallbacks->onFree(p, pAllocationCallbacks->pUserData); - } - return p2; - } - return NULL; -} -MA_PRIVATE void ma_dr_wav__free_from_callbacks(void* p, const ma_allocation_callbacks* pAllocationCallbacks) -{ - if (p == NULL || pAllocationCallbacks == NULL) { - return; - } - if (pAllocationCallbacks->onFree != NULL) { - pAllocationCallbacks->onFree(p, pAllocationCallbacks->pUserData); - } -} -MA_PRIVATE ma_allocation_callbacks ma_dr_wav_copy_allocation_callbacks_or_defaults(const ma_allocation_callbacks* pAllocationCallbacks) -{ - if (pAllocationCallbacks != NULL) { - return *pAllocationCallbacks; - } else { - ma_allocation_callbacks allocationCallbacks; - allocationCallbacks.pUserData = NULL; - allocationCallbacks.onMalloc = ma_dr_wav__malloc_default; - allocationCallbacks.onRealloc = ma_dr_wav__realloc_default; - allocationCallbacks.onFree = ma_dr_wav__free_default; - return allocationCallbacks; - } -} -static MA_INLINE ma_bool32 ma_dr_wav__is_compressed_format_tag(ma_uint16 formatTag) -{ - return - formatTag == MA_DR_WAVE_FORMAT_ADPCM || - formatTag == MA_DR_WAVE_FORMAT_DVI_ADPCM; -} -MA_PRIVATE unsigned int ma_dr_wav__chunk_padding_size_riff(ma_uint64 chunkSize) -{ - return (unsigned int)(chunkSize % 2); -} -MA_PRIVATE unsigned int ma_dr_wav__chunk_padding_size_w64(ma_uint64 chunkSize) -{ - return (unsigned int)(chunkSize % 8); -} -MA_PRIVATE ma_uint64 ma_dr_wav_read_pcm_frames_s16__msadpcm(ma_dr_wav* pWav, ma_uint64 samplesToRead, ma_int16* pBufferOut); -MA_PRIVATE ma_uint64 ma_dr_wav_read_pcm_frames_s16__ima(ma_dr_wav* pWav, ma_uint64 samplesToRead, ma_int16* pBufferOut); -MA_PRIVATE ma_bool32 ma_dr_wav_init_write__internal(ma_dr_wav* pWav, const ma_dr_wav_data_format* pFormat, ma_uint64 totalSampleCount); -MA_PRIVATE ma_result ma_dr_wav__read_chunk_header(ma_dr_wav_read_proc onRead, void* pUserData, ma_dr_wav_container container, ma_uint64* pRunningBytesReadOut, ma_dr_wav_chunk_header* pHeaderOut) -{ - if (container == ma_dr_wav_container_riff || container == ma_dr_wav_container_rifx || container == ma_dr_wav_container_rf64 || container == ma_dr_wav_container_aiff) { - ma_uint8 sizeInBytes[4]; - if (onRead(pUserData, pHeaderOut->id.fourcc, 4) != 4) { - return MA_AT_END; - } - if (onRead(pUserData, sizeInBytes, 4) != 4) { - return MA_INVALID_FILE; - } - pHeaderOut->sizeInBytes = ma_dr_wav_bytes_to_u32_ex(sizeInBytes, container); - pHeaderOut->paddingSize = ma_dr_wav__chunk_padding_size_riff(pHeaderOut->sizeInBytes); - *pRunningBytesReadOut += 8; - } else if (container == ma_dr_wav_container_w64) { - ma_uint8 sizeInBytes[8]; - if (onRead(pUserData, pHeaderOut->id.guid, 16) != 16) { - return MA_AT_END; - } - if (onRead(pUserData, sizeInBytes, 8) != 8) { - return MA_INVALID_FILE; - } - pHeaderOut->sizeInBytes = ma_dr_wav_bytes_to_u64(sizeInBytes) - 24; - pHeaderOut->paddingSize = ma_dr_wav__chunk_padding_size_w64(pHeaderOut->sizeInBytes); - *pRunningBytesReadOut += 24; - } else { - return MA_INVALID_FILE; - } - return MA_SUCCESS; -} -MA_PRIVATE ma_bool32 ma_dr_wav__seek_forward(ma_dr_wav_seek_proc onSeek, ma_uint64 offset, void* pUserData) -{ - ma_uint64 bytesRemainingToSeek = offset; - while (bytesRemainingToSeek > 0) { - if (bytesRemainingToSeek > 0x7FFFFFFF) { - if (!onSeek(pUserData, 0x7FFFFFFF, ma_dr_wav_seek_origin_current)) { - return MA_FALSE; - } - bytesRemainingToSeek -= 0x7FFFFFFF; - } else { - if (!onSeek(pUserData, (int)bytesRemainingToSeek, ma_dr_wav_seek_origin_current)) { - return MA_FALSE; - } - bytesRemainingToSeek = 0; - } - } - return MA_TRUE; -} -MA_PRIVATE ma_bool32 ma_dr_wav__seek_from_start(ma_dr_wav_seek_proc onSeek, ma_uint64 offset, void* pUserData) -{ - if (offset <= 0x7FFFFFFF) { - return onSeek(pUserData, (int)offset, ma_dr_wav_seek_origin_start); - } - if (!onSeek(pUserData, 0x7FFFFFFF, ma_dr_wav_seek_origin_start)) { - return MA_FALSE; - } - offset -= 0x7FFFFFFF; - for (;;) { - if (offset <= 0x7FFFFFFF) { - return onSeek(pUserData, (int)offset, ma_dr_wav_seek_origin_current); - } - if (!onSeek(pUserData, 0x7FFFFFFF, ma_dr_wav_seek_origin_current)) { - return MA_FALSE; - } - offset -= 0x7FFFFFFF; - } -} -MA_PRIVATE size_t ma_dr_wav__on_read(ma_dr_wav_read_proc onRead, void* pUserData, void* pBufferOut, size_t bytesToRead, ma_uint64* pCursor) -{ - size_t bytesRead; - MA_DR_WAV_ASSERT(onRead != NULL); - MA_DR_WAV_ASSERT(pCursor != NULL); - bytesRead = onRead(pUserData, pBufferOut, bytesToRead); - *pCursor += bytesRead; - return bytesRead; -} -#if 0 -MA_PRIVATE ma_bool32 ma_dr_wav__on_seek(ma_dr_wav_seek_proc onSeek, void* pUserData, int offset, ma_dr_wav_seek_origin origin, ma_uint64* pCursor) -{ - MA_DR_WAV_ASSERT(onSeek != NULL); - MA_DR_WAV_ASSERT(pCursor != NULL); - if (!onSeek(pUserData, offset, origin)) { - return MA_FALSE; - } - if (origin == ma_dr_wav_seek_origin_start) { - *pCursor = offset; - } else { - *pCursor += offset; - } - return MA_TRUE; -} -#endif -#define MA_DR_WAV_SMPL_BYTES 36 -#define MA_DR_WAV_SMPL_LOOP_BYTES 24 -#define MA_DR_WAV_INST_BYTES 7 -#define MA_DR_WAV_ACID_BYTES 24 -#define MA_DR_WAV_CUE_BYTES 4 -#define MA_DR_WAV_BEXT_BYTES 602 -#define MA_DR_WAV_BEXT_DESCRIPTION_BYTES 256 -#define MA_DR_WAV_BEXT_ORIGINATOR_NAME_BYTES 32 -#define MA_DR_WAV_BEXT_ORIGINATOR_REF_BYTES 32 -#define MA_DR_WAV_BEXT_RESERVED_BYTES 180 -#define MA_DR_WAV_BEXT_UMID_BYTES 64 -#define MA_DR_WAV_CUE_POINT_BYTES 24 -#define MA_DR_WAV_LIST_LABEL_OR_NOTE_BYTES 4 -#define MA_DR_WAV_LIST_LABELLED_TEXT_BYTES 20 -#define MA_DR_WAV_METADATA_ALIGNMENT 8 -typedef enum -{ - ma_dr_wav__metadata_parser_stage_count, - ma_dr_wav__metadata_parser_stage_read -} ma_dr_wav__metadata_parser_stage; -typedef struct -{ - ma_dr_wav_read_proc onRead; - ma_dr_wav_seek_proc onSeek; - void *pReadSeekUserData; - ma_dr_wav__metadata_parser_stage stage; - ma_dr_wav_metadata *pMetadata; - ma_uint32 metadataCount; - ma_uint8 *pData; - ma_uint8 *pDataCursor; - ma_uint64 metadataCursor; - ma_uint64 extraCapacity; -} ma_dr_wav__metadata_parser; -MA_PRIVATE size_t ma_dr_wav__metadata_memory_capacity(ma_dr_wav__metadata_parser* pParser) -{ - ma_uint64 cap = sizeof(ma_dr_wav_metadata) * (ma_uint64)pParser->metadataCount + pParser->extraCapacity; - if (cap > MA_SIZE_MAX) { - return 0; - } - return (size_t)cap; -} -MA_PRIVATE ma_uint8* ma_dr_wav__metadata_get_memory(ma_dr_wav__metadata_parser* pParser, size_t size, size_t align) -{ - ma_uint8* pResult; - if (align) { - ma_uintptr modulo = (ma_uintptr)pParser->pDataCursor % align; - if (modulo != 0) { - pParser->pDataCursor += align - modulo; - } - } - pResult = pParser->pDataCursor; - MA_DR_WAV_ASSERT((pResult + size) <= (pParser->pData + ma_dr_wav__metadata_memory_capacity(pParser))); - pParser->pDataCursor += size; - return pResult; -} -MA_PRIVATE void ma_dr_wav__metadata_request_extra_memory_for_stage_2(ma_dr_wav__metadata_parser* pParser, size_t bytes, size_t align) -{ - size_t extra = bytes + (align ? (align - 1) : 0); - pParser->extraCapacity += extra; -} -MA_PRIVATE ma_result ma_dr_wav__metadata_alloc(ma_dr_wav__metadata_parser* pParser, ma_allocation_callbacks* pAllocationCallbacks) -{ - if (pParser->extraCapacity != 0 || pParser->metadataCount != 0) { - pAllocationCallbacks->onFree(pParser->pData, pAllocationCallbacks->pUserData); - pParser->pData = (ma_uint8*)pAllocationCallbacks->onMalloc(ma_dr_wav__metadata_memory_capacity(pParser), pAllocationCallbacks->pUserData); - pParser->pDataCursor = pParser->pData; - if (pParser->pData == NULL) { - return MA_OUT_OF_MEMORY; - } - pParser->pMetadata = (ma_dr_wav_metadata*)ma_dr_wav__metadata_get_memory(pParser, sizeof(ma_dr_wav_metadata) * pParser->metadataCount, 1); - pParser->metadataCursor = 0; - } - return MA_SUCCESS; -} -MA_PRIVATE size_t ma_dr_wav__metadata_parser_read(ma_dr_wav__metadata_parser* pParser, void* pBufferOut, size_t bytesToRead, ma_uint64* pCursor) -{ - if (pCursor != NULL) { - return ma_dr_wav__on_read(pParser->onRead, pParser->pReadSeekUserData, pBufferOut, bytesToRead, pCursor); - } else { - return pParser->onRead(pParser->pReadSeekUserData, pBufferOut, bytesToRead); - } -} -MA_PRIVATE ma_uint64 ma_dr_wav__read_smpl_to_metadata_obj(ma_dr_wav__metadata_parser* pParser, const ma_dr_wav_chunk_header* pChunkHeader, ma_dr_wav_metadata* pMetadata) -{ - ma_uint8 smplHeaderData[MA_DR_WAV_SMPL_BYTES]; - ma_uint64 totalBytesRead = 0; - size_t bytesJustRead; - if (pMetadata == NULL) { - return 0; - } - bytesJustRead = ma_dr_wav__metadata_parser_read(pParser, smplHeaderData, sizeof(smplHeaderData), &totalBytesRead); - MA_DR_WAV_ASSERT(pParser->stage == ma_dr_wav__metadata_parser_stage_read); - MA_DR_WAV_ASSERT(pChunkHeader != NULL); - if (pMetadata != NULL && bytesJustRead == sizeof(smplHeaderData)) { - ma_uint32 iSampleLoop; - pMetadata->type = ma_dr_wav_metadata_type_smpl; - pMetadata->data.smpl.manufacturerId = ma_dr_wav_bytes_to_u32(smplHeaderData + 0); - pMetadata->data.smpl.productId = ma_dr_wav_bytes_to_u32(smplHeaderData + 4); - pMetadata->data.smpl.samplePeriodNanoseconds = ma_dr_wav_bytes_to_u32(smplHeaderData + 8); - pMetadata->data.smpl.midiUnityNote = ma_dr_wav_bytes_to_u32(smplHeaderData + 12); - pMetadata->data.smpl.midiPitchFraction = ma_dr_wav_bytes_to_u32(smplHeaderData + 16); - pMetadata->data.smpl.smpteFormat = ma_dr_wav_bytes_to_u32(smplHeaderData + 20); - pMetadata->data.smpl.smpteOffset = ma_dr_wav_bytes_to_u32(smplHeaderData + 24); - pMetadata->data.smpl.sampleLoopCount = ma_dr_wav_bytes_to_u32(smplHeaderData + 28); - pMetadata->data.smpl.samplerSpecificDataSizeInBytes = ma_dr_wav_bytes_to_u32(smplHeaderData + 32); - if (pMetadata->data.smpl.sampleLoopCount == (pChunkHeader->sizeInBytes - MA_DR_WAV_SMPL_BYTES) / MA_DR_WAV_SMPL_LOOP_BYTES) { - pMetadata->data.smpl.pLoops = (ma_dr_wav_smpl_loop*)ma_dr_wav__metadata_get_memory(pParser, sizeof(ma_dr_wav_smpl_loop) * pMetadata->data.smpl.sampleLoopCount, MA_DR_WAV_METADATA_ALIGNMENT); - for (iSampleLoop = 0; iSampleLoop < pMetadata->data.smpl.sampleLoopCount; ++iSampleLoop) { - ma_uint8 smplLoopData[MA_DR_WAV_SMPL_LOOP_BYTES]; - bytesJustRead = ma_dr_wav__metadata_parser_read(pParser, smplLoopData, sizeof(smplLoopData), &totalBytesRead); - if (bytesJustRead == sizeof(smplLoopData)) { - pMetadata->data.smpl.pLoops[iSampleLoop].cuePointId = ma_dr_wav_bytes_to_u32(smplLoopData + 0); - pMetadata->data.smpl.pLoops[iSampleLoop].type = ma_dr_wav_bytes_to_u32(smplLoopData + 4); - pMetadata->data.smpl.pLoops[iSampleLoop].firstSampleByteOffset = ma_dr_wav_bytes_to_u32(smplLoopData + 8); - pMetadata->data.smpl.pLoops[iSampleLoop].lastSampleByteOffset = ma_dr_wav_bytes_to_u32(smplLoopData + 12); - pMetadata->data.smpl.pLoops[iSampleLoop].sampleFraction = ma_dr_wav_bytes_to_u32(smplLoopData + 16); - pMetadata->data.smpl.pLoops[iSampleLoop].playCount = ma_dr_wav_bytes_to_u32(smplLoopData + 20); - } else { - break; - } - } - if (pMetadata->data.smpl.samplerSpecificDataSizeInBytes > 0) { - pMetadata->data.smpl.pSamplerSpecificData = ma_dr_wav__metadata_get_memory(pParser, pMetadata->data.smpl.samplerSpecificDataSizeInBytes, 1); - MA_DR_WAV_ASSERT(pMetadata->data.smpl.pSamplerSpecificData != NULL); - ma_dr_wav__metadata_parser_read(pParser, pMetadata->data.smpl.pSamplerSpecificData, pMetadata->data.smpl.samplerSpecificDataSizeInBytes, &totalBytesRead); - } - } - } - return totalBytesRead; -} -MA_PRIVATE ma_uint64 ma_dr_wav__read_cue_to_metadata_obj(ma_dr_wav__metadata_parser* pParser, const ma_dr_wav_chunk_header* pChunkHeader, ma_dr_wav_metadata* pMetadata) -{ - ma_uint8 cueHeaderSectionData[MA_DR_WAV_CUE_BYTES]; - ma_uint64 totalBytesRead = 0; - size_t bytesJustRead; - if (pMetadata == NULL) { - return 0; - } - bytesJustRead = ma_dr_wav__metadata_parser_read(pParser, cueHeaderSectionData, sizeof(cueHeaderSectionData), &totalBytesRead); - MA_DR_WAV_ASSERT(pParser->stage == ma_dr_wav__metadata_parser_stage_read); - if (bytesJustRead == sizeof(cueHeaderSectionData)) { - pMetadata->type = ma_dr_wav_metadata_type_cue; - pMetadata->data.cue.cuePointCount = ma_dr_wav_bytes_to_u32(cueHeaderSectionData); - if (pMetadata->data.cue.cuePointCount == (pChunkHeader->sizeInBytes - MA_DR_WAV_CUE_BYTES) / MA_DR_WAV_CUE_POINT_BYTES) { - pMetadata->data.cue.pCuePoints = (ma_dr_wav_cue_point*)ma_dr_wav__metadata_get_memory(pParser, sizeof(ma_dr_wav_cue_point) * pMetadata->data.cue.cuePointCount, MA_DR_WAV_METADATA_ALIGNMENT); - MA_DR_WAV_ASSERT(pMetadata->data.cue.pCuePoints != NULL); - if (pMetadata->data.cue.cuePointCount > 0) { - ma_uint32 iCuePoint; - for (iCuePoint = 0; iCuePoint < pMetadata->data.cue.cuePointCount; ++iCuePoint) { - ma_uint8 cuePointData[MA_DR_WAV_CUE_POINT_BYTES]; - bytesJustRead = ma_dr_wav__metadata_parser_read(pParser, cuePointData, sizeof(cuePointData), &totalBytesRead); - if (bytesJustRead == sizeof(cuePointData)) { - pMetadata->data.cue.pCuePoints[iCuePoint].id = ma_dr_wav_bytes_to_u32(cuePointData + 0); - pMetadata->data.cue.pCuePoints[iCuePoint].playOrderPosition = ma_dr_wav_bytes_to_u32(cuePointData + 4); - pMetadata->data.cue.pCuePoints[iCuePoint].dataChunkId[0] = cuePointData[8]; - pMetadata->data.cue.pCuePoints[iCuePoint].dataChunkId[1] = cuePointData[9]; - pMetadata->data.cue.pCuePoints[iCuePoint].dataChunkId[2] = cuePointData[10]; - pMetadata->data.cue.pCuePoints[iCuePoint].dataChunkId[3] = cuePointData[11]; - pMetadata->data.cue.pCuePoints[iCuePoint].chunkStart = ma_dr_wav_bytes_to_u32(cuePointData + 12); - pMetadata->data.cue.pCuePoints[iCuePoint].blockStart = ma_dr_wav_bytes_to_u32(cuePointData + 16); - pMetadata->data.cue.pCuePoints[iCuePoint].sampleByteOffset = ma_dr_wav_bytes_to_u32(cuePointData + 20); - } else { - break; - } - } - } - } - } - return totalBytesRead; -} -MA_PRIVATE ma_uint64 ma_dr_wav__read_inst_to_metadata_obj(ma_dr_wav__metadata_parser* pParser, ma_dr_wav_metadata* pMetadata) -{ - ma_uint8 instData[MA_DR_WAV_INST_BYTES]; - ma_uint64 bytesRead; - if (pMetadata == NULL) { - return 0; - } - bytesRead = ma_dr_wav__metadata_parser_read(pParser, instData, sizeof(instData), NULL); - MA_DR_WAV_ASSERT(pParser->stage == ma_dr_wav__metadata_parser_stage_read); - if (bytesRead == sizeof(instData)) { - pMetadata->type = ma_dr_wav_metadata_type_inst; - pMetadata->data.inst.midiUnityNote = (ma_int8)instData[0]; - pMetadata->data.inst.fineTuneCents = (ma_int8)instData[1]; - pMetadata->data.inst.gainDecibels = (ma_int8)instData[2]; - pMetadata->data.inst.lowNote = (ma_int8)instData[3]; - pMetadata->data.inst.highNote = (ma_int8)instData[4]; - pMetadata->data.inst.lowVelocity = (ma_int8)instData[5]; - pMetadata->data.inst.highVelocity = (ma_int8)instData[6]; - } - return bytesRead; -} -MA_PRIVATE ma_uint64 ma_dr_wav__read_acid_to_metadata_obj(ma_dr_wav__metadata_parser* pParser, ma_dr_wav_metadata* pMetadata) -{ - ma_uint8 acidData[MA_DR_WAV_ACID_BYTES]; - ma_uint64 bytesRead; - if (pMetadata == NULL) { - return 0; - } - bytesRead = ma_dr_wav__metadata_parser_read(pParser, acidData, sizeof(acidData), NULL); - MA_DR_WAV_ASSERT(pParser->stage == ma_dr_wav__metadata_parser_stage_read); - if (bytesRead == sizeof(acidData)) { - pMetadata->type = ma_dr_wav_metadata_type_acid; - pMetadata->data.acid.flags = ma_dr_wav_bytes_to_u32(acidData + 0); - pMetadata->data.acid.midiUnityNote = ma_dr_wav_bytes_to_u16(acidData + 4); - pMetadata->data.acid.reserved1 = ma_dr_wav_bytes_to_u16(acidData + 6); - pMetadata->data.acid.reserved2 = ma_dr_wav_bytes_to_f32(acidData + 8); - pMetadata->data.acid.numBeats = ma_dr_wav_bytes_to_u32(acidData + 12); - pMetadata->data.acid.meterDenominator = ma_dr_wav_bytes_to_u16(acidData + 16); - pMetadata->data.acid.meterNumerator = ma_dr_wav_bytes_to_u16(acidData + 18); - pMetadata->data.acid.tempo = ma_dr_wav_bytes_to_f32(acidData + 20); - } - return bytesRead; -} -MA_PRIVATE size_t ma_dr_wav__strlen(const char* str) -{ - size_t result = 0; - while (*str++) { - result += 1; - } - return result; -} -MA_PRIVATE size_t ma_dr_wav__strlen_clamped(const char* str, size_t maxToRead) -{ - size_t result = 0; - while (*str++ && result < maxToRead) { - result += 1; - } - return result; -} -MA_PRIVATE char* ma_dr_wav__metadata_copy_string(ma_dr_wav__metadata_parser* pParser, const char* str, size_t maxToRead) -{ - size_t len = ma_dr_wav__strlen_clamped(str, maxToRead); - if (len) { - char* result = (char*)ma_dr_wav__metadata_get_memory(pParser, len + 1, 1); - MA_DR_WAV_ASSERT(result != NULL); - MA_DR_WAV_COPY_MEMORY(result, str, len); - result[len] = '\0'; - return result; - } else { - return NULL; - } -} -typedef struct -{ - const void* pBuffer; - size_t sizeInBytes; - size_t cursor; -} ma_dr_wav_buffer_reader; -MA_PRIVATE ma_result ma_dr_wav_buffer_reader_init(const void* pBuffer, size_t sizeInBytes, ma_dr_wav_buffer_reader* pReader) -{ - MA_DR_WAV_ASSERT(pBuffer != NULL); - MA_DR_WAV_ASSERT(pReader != NULL); - MA_DR_WAV_ZERO_OBJECT(pReader); - pReader->pBuffer = pBuffer; - pReader->sizeInBytes = sizeInBytes; - pReader->cursor = 0; - return MA_SUCCESS; -} -MA_PRIVATE const void* ma_dr_wav_buffer_reader_ptr(const ma_dr_wav_buffer_reader* pReader) -{ - MA_DR_WAV_ASSERT(pReader != NULL); - return ma_dr_wav_offset_ptr(pReader->pBuffer, pReader->cursor); -} -MA_PRIVATE ma_result ma_dr_wav_buffer_reader_seek(ma_dr_wav_buffer_reader* pReader, size_t bytesToSeek) -{ - MA_DR_WAV_ASSERT(pReader != NULL); - if (pReader->cursor + bytesToSeek > pReader->sizeInBytes) { - return MA_BAD_SEEK; - } - pReader->cursor += bytesToSeek; - return MA_SUCCESS; -} -MA_PRIVATE ma_result ma_dr_wav_buffer_reader_read(ma_dr_wav_buffer_reader* pReader, void* pDst, size_t bytesToRead, size_t* pBytesRead) -{ - ma_result result = MA_SUCCESS; - size_t bytesRemaining; - MA_DR_WAV_ASSERT(pReader != NULL); - if (pBytesRead != NULL) { - *pBytesRead = 0; - } - bytesRemaining = (pReader->sizeInBytes - pReader->cursor); - if (bytesToRead > bytesRemaining) { - bytesToRead = bytesRemaining; - } - if (pDst == NULL) { - result = ma_dr_wav_buffer_reader_seek(pReader, bytesToRead); - } else { - MA_DR_WAV_COPY_MEMORY(pDst, ma_dr_wav_buffer_reader_ptr(pReader), bytesToRead); - pReader->cursor += bytesToRead; - } - MA_DR_WAV_ASSERT(pReader->cursor <= pReader->sizeInBytes); - if (result == MA_SUCCESS) { - if (pBytesRead != NULL) { - *pBytesRead = bytesToRead; - } - } - return MA_SUCCESS; -} -MA_PRIVATE ma_result ma_dr_wav_buffer_reader_read_u16(ma_dr_wav_buffer_reader* pReader, ma_uint16* pDst) -{ - ma_result result; - size_t bytesRead; - ma_uint8 data[2]; - MA_DR_WAV_ASSERT(pReader != NULL); - MA_DR_WAV_ASSERT(pDst != NULL); - *pDst = 0; - result = ma_dr_wav_buffer_reader_read(pReader, data, sizeof(*pDst), &bytesRead); - if (result != MA_SUCCESS || bytesRead != sizeof(*pDst)) { - return result; - } - *pDst = ma_dr_wav_bytes_to_u16(data); - return MA_SUCCESS; -} -MA_PRIVATE ma_result ma_dr_wav_buffer_reader_read_u32(ma_dr_wav_buffer_reader* pReader, ma_uint32* pDst) -{ - ma_result result; - size_t bytesRead; - ma_uint8 data[4]; - MA_DR_WAV_ASSERT(pReader != NULL); - MA_DR_WAV_ASSERT(pDst != NULL); - *pDst = 0; - result = ma_dr_wav_buffer_reader_read(pReader, data, sizeof(*pDst), &bytesRead); - if (result != MA_SUCCESS || bytesRead != sizeof(*pDst)) { - return result; - } - *pDst = ma_dr_wav_bytes_to_u32(data); - return MA_SUCCESS; -} -MA_PRIVATE ma_uint64 ma_dr_wav__read_bext_to_metadata_obj(ma_dr_wav__metadata_parser* pParser, ma_dr_wav_metadata* pMetadata, ma_uint64 chunkSize) -{ - ma_uint8 bextData[MA_DR_WAV_BEXT_BYTES]; - size_t bytesRead = ma_dr_wav__metadata_parser_read(pParser, bextData, sizeof(bextData), NULL); - MA_DR_WAV_ASSERT(pParser->stage == ma_dr_wav__metadata_parser_stage_read); - if (bytesRead == sizeof(bextData)) { - ma_dr_wav_buffer_reader reader; - ma_uint32 timeReferenceLow; - ma_uint32 timeReferenceHigh; - size_t extraBytes; - pMetadata->type = ma_dr_wav_metadata_type_bext; - if (ma_dr_wav_buffer_reader_init(bextData, bytesRead, &reader) == MA_SUCCESS) { - pMetadata->data.bext.pDescription = ma_dr_wav__metadata_copy_string(pParser, (const char*)ma_dr_wav_buffer_reader_ptr(&reader), MA_DR_WAV_BEXT_DESCRIPTION_BYTES); - ma_dr_wav_buffer_reader_seek(&reader, MA_DR_WAV_BEXT_DESCRIPTION_BYTES); - pMetadata->data.bext.pOriginatorName = ma_dr_wav__metadata_copy_string(pParser, (const char*)ma_dr_wav_buffer_reader_ptr(&reader), MA_DR_WAV_BEXT_ORIGINATOR_NAME_BYTES); - ma_dr_wav_buffer_reader_seek(&reader, MA_DR_WAV_BEXT_ORIGINATOR_NAME_BYTES); - pMetadata->data.bext.pOriginatorReference = ma_dr_wav__metadata_copy_string(pParser, (const char*)ma_dr_wav_buffer_reader_ptr(&reader), MA_DR_WAV_BEXT_ORIGINATOR_REF_BYTES); - ma_dr_wav_buffer_reader_seek(&reader, MA_DR_WAV_BEXT_ORIGINATOR_REF_BYTES); - ma_dr_wav_buffer_reader_read(&reader, pMetadata->data.bext.pOriginationDate, sizeof(pMetadata->data.bext.pOriginationDate), NULL); - ma_dr_wav_buffer_reader_read(&reader, pMetadata->data.bext.pOriginationTime, sizeof(pMetadata->data.bext.pOriginationTime), NULL); - ma_dr_wav_buffer_reader_read_u32(&reader, &timeReferenceLow); - ma_dr_wav_buffer_reader_read_u32(&reader, &timeReferenceHigh); - pMetadata->data.bext.timeReference = ((ma_uint64)timeReferenceHigh << 32) + timeReferenceLow; - ma_dr_wav_buffer_reader_read_u16(&reader, &pMetadata->data.bext.version); - pMetadata->data.bext.pUMID = ma_dr_wav__metadata_get_memory(pParser, MA_DR_WAV_BEXT_UMID_BYTES, 1); - ma_dr_wav_buffer_reader_read(&reader, pMetadata->data.bext.pUMID, MA_DR_WAV_BEXT_UMID_BYTES, NULL); - ma_dr_wav_buffer_reader_read_u16(&reader, &pMetadata->data.bext.loudnessValue); - ma_dr_wav_buffer_reader_read_u16(&reader, &pMetadata->data.bext.loudnessRange); - ma_dr_wav_buffer_reader_read_u16(&reader, &pMetadata->data.bext.maxTruePeakLevel); - ma_dr_wav_buffer_reader_read_u16(&reader, &pMetadata->data.bext.maxMomentaryLoudness); - ma_dr_wav_buffer_reader_read_u16(&reader, &pMetadata->data.bext.maxShortTermLoudness); - MA_DR_WAV_ASSERT((ma_dr_wav_offset_ptr(ma_dr_wav_buffer_reader_ptr(&reader), MA_DR_WAV_BEXT_RESERVED_BYTES)) == (bextData + MA_DR_WAV_BEXT_BYTES)); - extraBytes = (size_t)(chunkSize - MA_DR_WAV_BEXT_BYTES); - if (extraBytes > 0) { - pMetadata->data.bext.pCodingHistory = (char*)ma_dr_wav__metadata_get_memory(pParser, extraBytes + 1, 1); - MA_DR_WAV_ASSERT(pMetadata->data.bext.pCodingHistory != NULL); - bytesRead += ma_dr_wav__metadata_parser_read(pParser, pMetadata->data.bext.pCodingHistory, extraBytes, NULL); - pMetadata->data.bext.codingHistorySize = (ma_uint32)ma_dr_wav__strlen(pMetadata->data.bext.pCodingHistory); - } else { - pMetadata->data.bext.pCodingHistory = NULL; - pMetadata->data.bext.codingHistorySize = 0; - } - } - } - return bytesRead; -} -MA_PRIVATE ma_uint64 ma_dr_wav__read_list_label_or_note_to_metadata_obj(ma_dr_wav__metadata_parser* pParser, ma_dr_wav_metadata* pMetadata, ma_uint64 chunkSize, ma_dr_wav_metadata_type type) -{ - ma_uint8 cueIDBuffer[MA_DR_WAV_LIST_LABEL_OR_NOTE_BYTES]; - ma_uint64 totalBytesRead = 0; - size_t bytesJustRead = ma_dr_wav__metadata_parser_read(pParser, cueIDBuffer, sizeof(cueIDBuffer), &totalBytesRead); - MA_DR_WAV_ASSERT(pParser->stage == ma_dr_wav__metadata_parser_stage_read); - if (bytesJustRead == sizeof(cueIDBuffer)) { - ma_uint32 sizeIncludingNullTerminator; - pMetadata->type = type; - pMetadata->data.labelOrNote.cuePointId = ma_dr_wav_bytes_to_u32(cueIDBuffer); - sizeIncludingNullTerminator = (ma_uint32)chunkSize - MA_DR_WAV_LIST_LABEL_OR_NOTE_BYTES; - if (sizeIncludingNullTerminator > 0) { - pMetadata->data.labelOrNote.stringLength = sizeIncludingNullTerminator - 1; - pMetadata->data.labelOrNote.pString = (char*)ma_dr_wav__metadata_get_memory(pParser, sizeIncludingNullTerminator, 1); - MA_DR_WAV_ASSERT(pMetadata->data.labelOrNote.pString != NULL); - ma_dr_wav__metadata_parser_read(pParser, pMetadata->data.labelOrNote.pString, sizeIncludingNullTerminator, &totalBytesRead); - } else { - pMetadata->data.labelOrNote.stringLength = 0; - pMetadata->data.labelOrNote.pString = NULL; - } - } - return totalBytesRead; -} -MA_PRIVATE ma_uint64 ma_dr_wav__read_list_labelled_cue_region_to_metadata_obj(ma_dr_wav__metadata_parser* pParser, ma_dr_wav_metadata* pMetadata, ma_uint64 chunkSize) -{ - ma_uint8 buffer[MA_DR_WAV_LIST_LABELLED_TEXT_BYTES]; - ma_uint64 totalBytesRead = 0; - size_t bytesJustRead = ma_dr_wav__metadata_parser_read(pParser, buffer, sizeof(buffer), &totalBytesRead); - MA_DR_WAV_ASSERT(pParser->stage == ma_dr_wav__metadata_parser_stage_read); - if (bytesJustRead == sizeof(buffer)) { - ma_uint32 sizeIncludingNullTerminator; - pMetadata->type = ma_dr_wav_metadata_type_list_labelled_cue_region; - pMetadata->data.labelledCueRegion.cuePointId = ma_dr_wav_bytes_to_u32(buffer + 0); - pMetadata->data.labelledCueRegion.sampleLength = ma_dr_wav_bytes_to_u32(buffer + 4); - pMetadata->data.labelledCueRegion.purposeId[0] = buffer[8]; - pMetadata->data.labelledCueRegion.purposeId[1] = buffer[9]; - pMetadata->data.labelledCueRegion.purposeId[2] = buffer[10]; - pMetadata->data.labelledCueRegion.purposeId[3] = buffer[11]; - pMetadata->data.labelledCueRegion.country = ma_dr_wav_bytes_to_u16(buffer + 12); - pMetadata->data.labelledCueRegion.language = ma_dr_wav_bytes_to_u16(buffer + 14); - pMetadata->data.labelledCueRegion.dialect = ma_dr_wav_bytes_to_u16(buffer + 16); - pMetadata->data.labelledCueRegion.codePage = ma_dr_wav_bytes_to_u16(buffer + 18); - sizeIncludingNullTerminator = (ma_uint32)chunkSize - MA_DR_WAV_LIST_LABELLED_TEXT_BYTES; - if (sizeIncludingNullTerminator > 0) { - pMetadata->data.labelledCueRegion.stringLength = sizeIncludingNullTerminator - 1; - pMetadata->data.labelledCueRegion.pString = (char*)ma_dr_wav__metadata_get_memory(pParser, sizeIncludingNullTerminator, 1); - MA_DR_WAV_ASSERT(pMetadata->data.labelledCueRegion.pString != NULL); - ma_dr_wav__metadata_parser_read(pParser, pMetadata->data.labelledCueRegion.pString, sizeIncludingNullTerminator, &totalBytesRead); - } else { - pMetadata->data.labelledCueRegion.stringLength = 0; - pMetadata->data.labelledCueRegion.pString = NULL; - } - } - return totalBytesRead; -} -MA_PRIVATE ma_uint64 ma_dr_wav__metadata_process_info_text_chunk(ma_dr_wav__metadata_parser* pParser, ma_uint64 chunkSize, ma_dr_wav_metadata_type type) -{ - ma_uint64 bytesRead = 0; - ma_uint32 stringSizeWithNullTerminator = (ma_uint32)chunkSize; - if (pParser->stage == ma_dr_wav__metadata_parser_stage_count) { - pParser->metadataCount += 1; - ma_dr_wav__metadata_request_extra_memory_for_stage_2(pParser, stringSizeWithNullTerminator, 1); - } else { - ma_dr_wav_metadata* pMetadata = &pParser->pMetadata[pParser->metadataCursor]; - pMetadata->type = type; - if (stringSizeWithNullTerminator > 0) { - pMetadata->data.infoText.stringLength = stringSizeWithNullTerminator - 1; - pMetadata->data.infoText.pString = (char*)ma_dr_wav__metadata_get_memory(pParser, stringSizeWithNullTerminator, 1); - MA_DR_WAV_ASSERT(pMetadata->data.infoText.pString != NULL); - bytesRead = ma_dr_wav__metadata_parser_read(pParser, pMetadata->data.infoText.pString, (size_t)stringSizeWithNullTerminator, NULL); - if (bytesRead == chunkSize) { - pParser->metadataCursor += 1; - } else { - } - } else { - pMetadata->data.infoText.stringLength = 0; - pMetadata->data.infoText.pString = NULL; - pParser->metadataCursor += 1; - } - } - return bytesRead; -} -MA_PRIVATE ma_uint64 ma_dr_wav__metadata_process_unknown_chunk(ma_dr_wav__metadata_parser* pParser, const ma_uint8* pChunkId, ma_uint64 chunkSize, ma_dr_wav_metadata_location location) -{ - ma_uint64 bytesRead = 0; - if (location == ma_dr_wav_metadata_location_invalid) { - return 0; - } - if (ma_dr_wav_fourcc_equal(pChunkId, "data") || ma_dr_wav_fourcc_equal(pChunkId, "fmt ") || ma_dr_wav_fourcc_equal(pChunkId, "fact")) { - return 0; - } - if (pParser->stage == ma_dr_wav__metadata_parser_stage_count) { - pParser->metadataCount += 1; - ma_dr_wav__metadata_request_extra_memory_for_stage_2(pParser, (size_t)chunkSize, 1); - } else { - ma_dr_wav_metadata* pMetadata = &pParser->pMetadata[pParser->metadataCursor]; - pMetadata->type = ma_dr_wav_metadata_type_unknown; - pMetadata->data.unknown.chunkLocation = location; - pMetadata->data.unknown.id[0] = pChunkId[0]; - pMetadata->data.unknown.id[1] = pChunkId[1]; - pMetadata->data.unknown.id[2] = pChunkId[2]; - pMetadata->data.unknown.id[3] = pChunkId[3]; - pMetadata->data.unknown.dataSizeInBytes = (ma_uint32)chunkSize; - pMetadata->data.unknown.pData = (ma_uint8 *)ma_dr_wav__metadata_get_memory(pParser, (size_t)chunkSize, 1); - MA_DR_WAV_ASSERT(pMetadata->data.unknown.pData != NULL); - bytesRead = ma_dr_wav__metadata_parser_read(pParser, pMetadata->data.unknown.pData, pMetadata->data.unknown.dataSizeInBytes, NULL); - if (bytesRead == pMetadata->data.unknown.dataSizeInBytes) { - pParser->metadataCursor += 1; - } else { - } - } - return bytesRead; -} -MA_PRIVATE ma_bool32 ma_dr_wav__chunk_matches(ma_dr_wav_metadata_type allowedMetadataTypes, const ma_uint8* pChunkID, ma_dr_wav_metadata_type type, const char* pID) -{ - return (allowedMetadataTypes & type) && ma_dr_wav_fourcc_equal(pChunkID, pID); -} -MA_PRIVATE ma_uint64 ma_dr_wav__metadata_process_chunk(ma_dr_wav__metadata_parser* pParser, const ma_dr_wav_chunk_header* pChunkHeader, ma_dr_wav_metadata_type allowedMetadataTypes) -{ - const ma_uint8 *pChunkID = pChunkHeader->id.fourcc; - ma_uint64 bytesRead = 0; - if (ma_dr_wav__chunk_matches(allowedMetadataTypes, pChunkID, ma_dr_wav_metadata_type_smpl, "smpl")) { - if (pChunkHeader->sizeInBytes >= MA_DR_WAV_SMPL_BYTES) { - if (pParser->stage == ma_dr_wav__metadata_parser_stage_count) { - ma_uint8 buffer[4]; - size_t bytesJustRead; - if (!pParser->onSeek(pParser->pReadSeekUserData, 28, ma_dr_wav_seek_origin_current)) { - return bytesRead; - } - bytesRead += 28; - bytesJustRead = ma_dr_wav__metadata_parser_read(pParser, buffer, sizeof(buffer), &bytesRead); - if (bytesJustRead == sizeof(buffer)) { - ma_uint32 loopCount = ma_dr_wav_bytes_to_u32(buffer); - ma_uint64 calculatedLoopCount; - calculatedLoopCount = (pChunkHeader->sizeInBytes - MA_DR_WAV_SMPL_BYTES) / MA_DR_WAV_SMPL_LOOP_BYTES; - if (calculatedLoopCount == loopCount) { - bytesJustRead = ma_dr_wav__metadata_parser_read(pParser, buffer, sizeof(buffer), &bytesRead); - if (bytesJustRead == sizeof(buffer)) { - ma_uint32 samplerSpecificDataSizeInBytes = ma_dr_wav_bytes_to_u32(buffer); - pParser->metadataCount += 1; - ma_dr_wav__metadata_request_extra_memory_for_stage_2(pParser, sizeof(ma_dr_wav_smpl_loop) * loopCount, MA_DR_WAV_METADATA_ALIGNMENT); - ma_dr_wav__metadata_request_extra_memory_for_stage_2(pParser, samplerSpecificDataSizeInBytes, 1); - } - } else { - } - } - } else { - bytesRead = ma_dr_wav__read_smpl_to_metadata_obj(pParser, pChunkHeader, &pParser->pMetadata[pParser->metadataCursor]); - if (bytesRead == pChunkHeader->sizeInBytes) { - pParser->metadataCursor += 1; - } else { - } - } - } else { - } - } else if (ma_dr_wav__chunk_matches(allowedMetadataTypes, pChunkID, ma_dr_wav_metadata_type_inst, "inst")) { - if (pChunkHeader->sizeInBytes == MA_DR_WAV_INST_BYTES) { - if (pParser->stage == ma_dr_wav__metadata_parser_stage_count) { - pParser->metadataCount += 1; - } else { - bytesRead = ma_dr_wav__read_inst_to_metadata_obj(pParser, &pParser->pMetadata[pParser->metadataCursor]); - if (bytesRead == pChunkHeader->sizeInBytes) { - pParser->metadataCursor += 1; - } else { - } - } - } else { - } - } else if (ma_dr_wav__chunk_matches(allowedMetadataTypes, pChunkID, ma_dr_wav_metadata_type_acid, "acid")) { - if (pChunkHeader->sizeInBytes == MA_DR_WAV_ACID_BYTES) { - if (pParser->stage == ma_dr_wav__metadata_parser_stage_count) { - pParser->metadataCount += 1; - } else { - bytesRead = ma_dr_wav__read_acid_to_metadata_obj(pParser, &pParser->pMetadata[pParser->metadataCursor]); - if (bytesRead == pChunkHeader->sizeInBytes) { - pParser->metadataCursor += 1; - } else { - } - } - } else { - } - } else if (ma_dr_wav__chunk_matches(allowedMetadataTypes, pChunkID, ma_dr_wav_metadata_type_cue, "cue ")) { - if (pChunkHeader->sizeInBytes >= MA_DR_WAV_CUE_BYTES) { - if (pParser->stage == ma_dr_wav__metadata_parser_stage_count) { - size_t cueCount; - pParser->metadataCount += 1; - cueCount = (size_t)(pChunkHeader->sizeInBytes - MA_DR_WAV_CUE_BYTES) / MA_DR_WAV_CUE_POINT_BYTES; - ma_dr_wav__metadata_request_extra_memory_for_stage_2(pParser, sizeof(ma_dr_wav_cue_point) * cueCount, MA_DR_WAV_METADATA_ALIGNMENT); - } else { - bytesRead = ma_dr_wav__read_cue_to_metadata_obj(pParser, pChunkHeader, &pParser->pMetadata[pParser->metadataCursor]); - if (bytesRead == pChunkHeader->sizeInBytes) { - pParser->metadataCursor += 1; - } else { - } - } - } else { - } - } else if (ma_dr_wav__chunk_matches(allowedMetadataTypes, pChunkID, ma_dr_wav_metadata_type_bext, "bext")) { - if (pChunkHeader->sizeInBytes >= MA_DR_WAV_BEXT_BYTES) { - if (pParser->stage == ma_dr_wav__metadata_parser_stage_count) { - char buffer[MA_DR_WAV_BEXT_DESCRIPTION_BYTES + 1]; - size_t allocSizeNeeded = MA_DR_WAV_BEXT_UMID_BYTES; - size_t bytesJustRead; - buffer[MA_DR_WAV_BEXT_DESCRIPTION_BYTES] = '\0'; - bytesJustRead = ma_dr_wav__metadata_parser_read(pParser, buffer, MA_DR_WAV_BEXT_DESCRIPTION_BYTES, &bytesRead); - if (bytesJustRead != MA_DR_WAV_BEXT_DESCRIPTION_BYTES) { - return bytesRead; - } - allocSizeNeeded += ma_dr_wav__strlen(buffer) + 1; - buffer[MA_DR_WAV_BEXT_ORIGINATOR_NAME_BYTES] = '\0'; - bytesJustRead = ma_dr_wav__metadata_parser_read(pParser, buffer, MA_DR_WAV_BEXT_ORIGINATOR_NAME_BYTES, &bytesRead); - if (bytesJustRead != MA_DR_WAV_BEXT_ORIGINATOR_NAME_BYTES) { - return bytesRead; - } - allocSizeNeeded += ma_dr_wav__strlen(buffer) + 1; - buffer[MA_DR_WAV_BEXT_ORIGINATOR_REF_BYTES] = '\0'; - bytesJustRead = ma_dr_wav__metadata_parser_read(pParser, buffer, MA_DR_WAV_BEXT_ORIGINATOR_REF_BYTES, &bytesRead); - if (bytesJustRead != MA_DR_WAV_BEXT_ORIGINATOR_REF_BYTES) { - return bytesRead; - } - allocSizeNeeded += ma_dr_wav__strlen(buffer) + 1; - allocSizeNeeded += (size_t)pChunkHeader->sizeInBytes - MA_DR_WAV_BEXT_BYTES; - ma_dr_wav__metadata_request_extra_memory_for_stage_2(pParser, allocSizeNeeded, 1); - pParser->metadataCount += 1; - } else { - bytesRead = ma_dr_wav__read_bext_to_metadata_obj(pParser, &pParser->pMetadata[pParser->metadataCursor], pChunkHeader->sizeInBytes); - if (bytesRead == pChunkHeader->sizeInBytes) { - pParser->metadataCursor += 1; - } else { - } - } - } else { - } - } else if (ma_dr_wav_fourcc_equal(pChunkID, "LIST") || ma_dr_wav_fourcc_equal(pChunkID, "list")) { - ma_dr_wav_metadata_location listType = ma_dr_wav_metadata_location_invalid; - while (bytesRead < pChunkHeader->sizeInBytes) { - ma_uint8 subchunkId[4]; - ma_uint8 subchunkSizeBuffer[4]; - ma_uint64 subchunkDataSize; - ma_uint64 subchunkBytesRead = 0; - ma_uint64 bytesJustRead = ma_dr_wav__metadata_parser_read(pParser, subchunkId, sizeof(subchunkId), &bytesRead); - if (bytesJustRead != sizeof(subchunkId)) { - break; - } - if (ma_dr_wav_fourcc_equal(subchunkId, "adtl")) { - listType = ma_dr_wav_metadata_location_inside_adtl_list; - continue; - } else if (ma_dr_wav_fourcc_equal(subchunkId, "INFO")) { - listType = ma_dr_wav_metadata_location_inside_info_list; - continue; - } - bytesJustRead = ma_dr_wav__metadata_parser_read(pParser, subchunkSizeBuffer, sizeof(subchunkSizeBuffer), &bytesRead); - if (bytesJustRead != sizeof(subchunkSizeBuffer)) { - break; - } - subchunkDataSize = ma_dr_wav_bytes_to_u32(subchunkSizeBuffer); - if (ma_dr_wav__chunk_matches(allowedMetadataTypes, subchunkId, ma_dr_wav_metadata_type_list_label, "labl") || ma_dr_wav__chunk_matches(allowedMetadataTypes, subchunkId, ma_dr_wav_metadata_type_list_note, "note")) { - if (subchunkDataSize >= MA_DR_WAV_LIST_LABEL_OR_NOTE_BYTES) { - ma_uint64 stringSizeWithNullTerm = subchunkDataSize - MA_DR_WAV_LIST_LABEL_OR_NOTE_BYTES; - if (pParser->stage == ma_dr_wav__metadata_parser_stage_count) { - pParser->metadataCount += 1; - ma_dr_wav__metadata_request_extra_memory_for_stage_2(pParser, (size_t)stringSizeWithNullTerm, 1); - } else { - subchunkBytesRead = ma_dr_wav__read_list_label_or_note_to_metadata_obj(pParser, &pParser->pMetadata[pParser->metadataCursor], subchunkDataSize, ma_dr_wav_fourcc_equal(subchunkId, "labl") ? ma_dr_wav_metadata_type_list_label : ma_dr_wav_metadata_type_list_note); - if (subchunkBytesRead == subchunkDataSize) { - pParser->metadataCursor += 1; - } else { - } - } - } else { - } - } else if (ma_dr_wav__chunk_matches(allowedMetadataTypes, subchunkId, ma_dr_wav_metadata_type_list_labelled_cue_region, "ltxt")) { - if (subchunkDataSize >= MA_DR_WAV_LIST_LABELLED_TEXT_BYTES) { - ma_uint64 stringSizeWithNullTerminator = subchunkDataSize - MA_DR_WAV_LIST_LABELLED_TEXT_BYTES; - if (pParser->stage == ma_dr_wav__metadata_parser_stage_count) { - pParser->metadataCount += 1; - ma_dr_wav__metadata_request_extra_memory_for_stage_2(pParser, (size_t)stringSizeWithNullTerminator, 1); - } else { - subchunkBytesRead = ma_dr_wav__read_list_labelled_cue_region_to_metadata_obj(pParser, &pParser->pMetadata[pParser->metadataCursor], subchunkDataSize); - if (subchunkBytesRead == subchunkDataSize) { - pParser->metadataCursor += 1; - } else { - } - } - } else { - } - } else if (ma_dr_wav__chunk_matches(allowedMetadataTypes, subchunkId, ma_dr_wav_metadata_type_list_info_software, "ISFT")) { - subchunkBytesRead = ma_dr_wav__metadata_process_info_text_chunk(pParser, subchunkDataSize, ma_dr_wav_metadata_type_list_info_software); - } else if (ma_dr_wav__chunk_matches(allowedMetadataTypes, subchunkId, ma_dr_wav_metadata_type_list_info_copyright, "ICOP")) { - subchunkBytesRead = ma_dr_wav__metadata_process_info_text_chunk(pParser, subchunkDataSize, ma_dr_wav_metadata_type_list_info_copyright); - } else if (ma_dr_wav__chunk_matches(allowedMetadataTypes, subchunkId, ma_dr_wav_metadata_type_list_info_title, "INAM")) { - subchunkBytesRead = ma_dr_wav__metadata_process_info_text_chunk(pParser, subchunkDataSize, ma_dr_wav_metadata_type_list_info_title); - } else if (ma_dr_wav__chunk_matches(allowedMetadataTypes, subchunkId, ma_dr_wav_metadata_type_list_info_artist, "IART")) { - subchunkBytesRead = ma_dr_wav__metadata_process_info_text_chunk(pParser, subchunkDataSize, ma_dr_wav_metadata_type_list_info_artist); - } else if (ma_dr_wav__chunk_matches(allowedMetadataTypes, subchunkId, ma_dr_wav_metadata_type_list_info_comment, "ICMT")) { - subchunkBytesRead = ma_dr_wav__metadata_process_info_text_chunk(pParser, subchunkDataSize, ma_dr_wav_metadata_type_list_info_comment); - } else if (ma_dr_wav__chunk_matches(allowedMetadataTypes, subchunkId, ma_dr_wav_metadata_type_list_info_date, "ICRD")) { - subchunkBytesRead = ma_dr_wav__metadata_process_info_text_chunk(pParser, subchunkDataSize, ma_dr_wav_metadata_type_list_info_date); - } else if (ma_dr_wav__chunk_matches(allowedMetadataTypes, subchunkId, ma_dr_wav_metadata_type_list_info_genre, "IGNR")) { - subchunkBytesRead = ma_dr_wav__metadata_process_info_text_chunk(pParser, subchunkDataSize, ma_dr_wav_metadata_type_list_info_genre); - } else if (ma_dr_wav__chunk_matches(allowedMetadataTypes, subchunkId, ma_dr_wav_metadata_type_list_info_album, "IPRD")) { - subchunkBytesRead = ma_dr_wav__metadata_process_info_text_chunk(pParser, subchunkDataSize, ma_dr_wav_metadata_type_list_info_album); - } else if (ma_dr_wav__chunk_matches(allowedMetadataTypes, subchunkId, ma_dr_wav_metadata_type_list_info_tracknumber, "ITRK")) { - subchunkBytesRead = ma_dr_wav__metadata_process_info_text_chunk(pParser, subchunkDataSize, ma_dr_wav_metadata_type_list_info_tracknumber); - } else if ((allowedMetadataTypes & ma_dr_wav_metadata_type_unknown) != 0) { - subchunkBytesRead = ma_dr_wav__metadata_process_unknown_chunk(pParser, subchunkId, subchunkDataSize, listType); - } - bytesRead += subchunkBytesRead; - MA_DR_WAV_ASSERT(subchunkBytesRead <= subchunkDataSize); - if (subchunkBytesRead < subchunkDataSize) { - ma_uint64 bytesToSeek = subchunkDataSize - subchunkBytesRead; - if (!pParser->onSeek(pParser->pReadSeekUserData, (int)bytesToSeek, ma_dr_wav_seek_origin_current)) { - break; - } - bytesRead += bytesToSeek; - } - if ((subchunkDataSize % 2) == 1) { - if (!pParser->onSeek(pParser->pReadSeekUserData, 1, ma_dr_wav_seek_origin_current)) { - break; - } - bytesRead += 1; - } - } - } else if ((allowedMetadataTypes & ma_dr_wav_metadata_type_unknown) != 0) { - bytesRead = ma_dr_wav__metadata_process_unknown_chunk(pParser, pChunkID, pChunkHeader->sizeInBytes, ma_dr_wav_metadata_location_top_level); - } - return bytesRead; -} -MA_PRIVATE ma_uint32 ma_dr_wav_get_bytes_per_pcm_frame(ma_dr_wav* pWav) -{ - ma_uint32 bytesPerFrame; - if ((pWav->bitsPerSample & 0x7) == 0) { - bytesPerFrame = (pWav->bitsPerSample * pWav->fmt.channels) >> 3; - } else { - bytesPerFrame = pWav->fmt.blockAlign; - } - if (pWav->translatedFormatTag == MA_DR_WAVE_FORMAT_ALAW || pWav->translatedFormatTag == MA_DR_WAVE_FORMAT_MULAW) { - if (bytesPerFrame != pWav->fmt.channels) { - return 0; - } - } - return bytesPerFrame; -} -MA_API ma_uint16 ma_dr_wav_fmt_get_format(const ma_dr_wav_fmt* pFMT) -{ - if (pFMT == NULL) { - return 0; - } - if (pFMT->formatTag != MA_DR_WAVE_FORMAT_EXTENSIBLE) { - return pFMT->formatTag; - } else { - return ma_dr_wav_bytes_to_u16(pFMT->subFormat); - } -} -MA_PRIVATE ma_bool32 ma_dr_wav_preinit(ma_dr_wav* pWav, ma_dr_wav_read_proc onRead, ma_dr_wav_seek_proc onSeek, void* pReadSeekUserData, const ma_allocation_callbacks* pAllocationCallbacks) -{ - if (pWav == NULL || onRead == NULL || onSeek == NULL) { - return MA_FALSE; - } - MA_DR_WAV_ZERO_MEMORY(pWav, sizeof(*pWav)); - pWav->onRead = onRead; - pWav->onSeek = onSeek; - pWav->pUserData = pReadSeekUserData; - pWav->allocationCallbacks = ma_dr_wav_copy_allocation_callbacks_or_defaults(pAllocationCallbacks); - if (pWav->allocationCallbacks.onFree == NULL || (pWav->allocationCallbacks.onMalloc == NULL && pWav->allocationCallbacks.onRealloc == NULL)) { - return MA_FALSE; - } - return MA_TRUE; -} -MA_PRIVATE ma_bool32 ma_dr_wav_init__internal(ma_dr_wav* pWav, ma_dr_wav_chunk_proc onChunk, void* pChunkUserData, ma_uint32 flags) -{ - ma_result result; - ma_uint64 cursor; - ma_bool32 sequential; - ma_uint8 riff[4]; - ma_dr_wav_fmt fmt; - unsigned short translatedFormatTag; - ma_uint64 dataChunkSize = 0; - ma_uint64 sampleCountFromFactChunk = 0; - ma_uint64 metadataStartPos; - ma_dr_wav__metadata_parser metadataParser; - ma_bool8 isProcessingMetadata = MA_FALSE; - ma_bool8 foundChunk_fmt = MA_FALSE; - ma_bool8 foundChunk_data = MA_FALSE; - ma_bool8 isAIFCFormType = MA_FALSE; - ma_uint64 aiffFrameCount = 0; - cursor = 0; - sequential = (flags & MA_DR_WAV_SEQUENTIAL) != 0; - MA_DR_WAV_ZERO_OBJECT(&fmt); - if (ma_dr_wav__on_read(pWav->onRead, pWav->pUserData, riff, sizeof(riff), &cursor) != sizeof(riff)) { - return MA_FALSE; - } - if (ma_dr_wav_fourcc_equal(riff, "RIFF")) { - pWav->container = ma_dr_wav_container_riff; - } else if (ma_dr_wav_fourcc_equal(riff, "RIFX")) { - pWav->container = ma_dr_wav_container_rifx; - } else if (ma_dr_wav_fourcc_equal(riff, "riff")) { - int i; - ma_uint8 riff2[12]; - pWav->container = ma_dr_wav_container_w64; - if (ma_dr_wav__on_read(pWav->onRead, pWav->pUserData, riff2, sizeof(riff2), &cursor) != sizeof(riff2)) { - return MA_FALSE; - } - for (i = 0; i < 12; ++i) { - if (riff2[i] != ma_dr_wavGUID_W64_RIFF[i+4]) { - return MA_FALSE; - } - } - } else if (ma_dr_wav_fourcc_equal(riff, "RF64")) { - pWav->container = ma_dr_wav_container_rf64; - } else if (ma_dr_wav_fourcc_equal(riff, "FORM")) { - pWav->container = ma_dr_wav_container_aiff; - } else { - return MA_FALSE; - } - if (pWav->container == ma_dr_wav_container_riff || pWav->container == ma_dr_wav_container_rifx || pWav->container == ma_dr_wav_container_rf64) { - ma_uint8 chunkSizeBytes[4]; - ma_uint8 wave[4]; - if (ma_dr_wav__on_read(pWav->onRead, pWav->pUserData, chunkSizeBytes, sizeof(chunkSizeBytes), &cursor) != sizeof(chunkSizeBytes)) { - return MA_FALSE; - } - if (pWav->container == ma_dr_wav_container_riff || pWav->container == ma_dr_wav_container_rifx) { - if (ma_dr_wav_bytes_to_u32_ex(chunkSizeBytes, pWav->container) < 36) { - } - } else if (pWav->container == ma_dr_wav_container_rf64) { - if (ma_dr_wav_bytes_to_u32_le(chunkSizeBytes) != 0xFFFFFFFF) { - return MA_FALSE; - } - } else { - return MA_FALSE; - } - if (ma_dr_wav__on_read(pWav->onRead, pWav->pUserData, wave, sizeof(wave), &cursor) != sizeof(wave)) { - return MA_FALSE; - } - if (!ma_dr_wav_fourcc_equal(wave, "WAVE")) { - return MA_FALSE; - } - } else if (pWav->container == ma_dr_wav_container_w64) { - ma_uint8 chunkSizeBytes[8]; - ma_uint8 wave[16]; - if (ma_dr_wav__on_read(pWav->onRead, pWav->pUserData, chunkSizeBytes, sizeof(chunkSizeBytes), &cursor) != sizeof(chunkSizeBytes)) { - return MA_FALSE; - } - if (ma_dr_wav_bytes_to_u64(chunkSizeBytes) < 80) { - return MA_FALSE; - } - if (ma_dr_wav__on_read(pWav->onRead, pWav->pUserData, wave, sizeof(wave), &cursor) != sizeof(wave)) { - return MA_FALSE; - } - if (!ma_dr_wav_guid_equal(wave, ma_dr_wavGUID_W64_WAVE)) { - return MA_FALSE; - } - } else if (pWav->container == ma_dr_wav_container_aiff) { - ma_uint8 chunkSizeBytes[4]; - ma_uint8 aiff[4]; - if (ma_dr_wav__on_read(pWav->onRead, pWav->pUserData, chunkSizeBytes, sizeof(chunkSizeBytes), &cursor) != sizeof(chunkSizeBytes)) { - return MA_FALSE; - } - if (ma_dr_wav_bytes_to_u32_be(chunkSizeBytes) < 18) { - return MA_FALSE; - } - if (ma_dr_wav__on_read(pWav->onRead, pWav->pUserData, aiff, sizeof(aiff), &cursor) != sizeof(aiff)) { - return MA_FALSE; - } - if (ma_dr_wav_fourcc_equal(aiff, "AIFF")) { - isAIFCFormType = MA_FALSE; - } else if (ma_dr_wav_fourcc_equal(aiff, "AIFC")) { - isAIFCFormType = MA_TRUE; - } else { - return MA_FALSE; - } - } else { - return MA_FALSE; - } - if (pWav->container == ma_dr_wav_container_rf64) { - ma_uint8 sizeBytes[8]; - ma_uint64 bytesRemainingInChunk; - ma_dr_wav_chunk_header header; - result = ma_dr_wav__read_chunk_header(pWav->onRead, pWav->pUserData, pWav->container, &cursor, &header); - if (result != MA_SUCCESS) { - return MA_FALSE; - } - if (!ma_dr_wav_fourcc_equal(header.id.fourcc, "ds64")) { - return MA_FALSE; - } - bytesRemainingInChunk = header.sizeInBytes + header.paddingSize; - if (!ma_dr_wav__seek_forward(pWav->onSeek, 8, pWav->pUserData)) { - return MA_FALSE; - } - bytesRemainingInChunk -= 8; - cursor += 8; - if (ma_dr_wav__on_read(pWav->onRead, pWav->pUserData, sizeBytes, sizeof(sizeBytes), &cursor) != sizeof(sizeBytes)) { - return MA_FALSE; - } - bytesRemainingInChunk -= 8; - dataChunkSize = ma_dr_wav_bytes_to_u64(sizeBytes); - if (ma_dr_wav__on_read(pWav->onRead, pWav->pUserData, sizeBytes, sizeof(sizeBytes), &cursor) != sizeof(sizeBytes)) { - return MA_FALSE; - } - bytesRemainingInChunk -= 8; - sampleCountFromFactChunk = ma_dr_wav_bytes_to_u64(sizeBytes); - if (!ma_dr_wav__seek_forward(pWav->onSeek, bytesRemainingInChunk, pWav->pUserData)) { - return MA_FALSE; - } - cursor += bytesRemainingInChunk; - } - metadataStartPos = cursor; - isProcessingMetadata = !sequential && ((flags & MA_DR_WAV_WITH_METADATA) != 0); - if (pWav->container != ma_dr_wav_container_riff && pWav->container != ma_dr_wav_container_rf64) { - isProcessingMetadata = MA_FALSE; - } - MA_DR_WAV_ZERO_MEMORY(&metadataParser, sizeof(metadataParser)); - if (isProcessingMetadata) { - metadataParser.onRead = pWav->onRead; - metadataParser.onSeek = pWav->onSeek; - metadataParser.pReadSeekUserData = pWav->pUserData; - metadataParser.stage = ma_dr_wav__metadata_parser_stage_count; - } - for (;;) { - ma_dr_wav_chunk_header header; - ma_uint64 chunkSize; - result = ma_dr_wav__read_chunk_header(pWav->onRead, pWav->pUserData, pWav->container, &cursor, &header); - if (result != MA_SUCCESS) { - break; - } - chunkSize = header.sizeInBytes; - if (!sequential && onChunk != NULL) { - ma_uint64 callbackBytesRead = onChunk(pChunkUserData, pWav->onRead, pWav->onSeek, pWav->pUserData, &header, pWav->container, &fmt); - if (callbackBytesRead > 0) { - if (ma_dr_wav__seek_from_start(pWav->onSeek, cursor, pWav->pUserData) == MA_FALSE) { - return MA_FALSE; - } - } - } - if (((pWav->container == ma_dr_wav_container_riff || pWav->container == ma_dr_wav_container_rifx || pWav->container == ma_dr_wav_container_rf64) && ma_dr_wav_fourcc_equal(header.id.fourcc, "fmt ")) || - ((pWav->container == ma_dr_wav_container_w64) && ma_dr_wav_guid_equal(header.id.guid, ma_dr_wavGUID_W64_FMT))) { - ma_uint8 fmtData[16]; - foundChunk_fmt = MA_TRUE; - if (pWav->onRead(pWav->pUserData, fmtData, sizeof(fmtData)) != sizeof(fmtData)) { - return MA_FALSE; - } - cursor += sizeof(fmtData); - fmt.formatTag = ma_dr_wav_bytes_to_u16_ex(fmtData + 0, pWav->container); - fmt.channels = ma_dr_wav_bytes_to_u16_ex(fmtData + 2, pWav->container); - fmt.sampleRate = ma_dr_wav_bytes_to_u32_ex(fmtData + 4, pWav->container); - fmt.avgBytesPerSec = ma_dr_wav_bytes_to_u32_ex(fmtData + 8, pWav->container); - fmt.blockAlign = ma_dr_wav_bytes_to_u16_ex(fmtData + 12, pWav->container); - fmt.bitsPerSample = ma_dr_wav_bytes_to_u16_ex(fmtData + 14, pWav->container); - fmt.extendedSize = 0; - fmt.validBitsPerSample = 0; - fmt.channelMask = 0; - MA_DR_WAV_ZERO_MEMORY(fmt.subFormat, sizeof(fmt.subFormat)); - if (header.sizeInBytes > 16) { - ma_uint8 fmt_cbSize[2]; - int bytesReadSoFar = 0; - if (pWav->onRead(pWav->pUserData, fmt_cbSize, sizeof(fmt_cbSize)) != sizeof(fmt_cbSize)) { - return MA_FALSE; - } - cursor += sizeof(fmt_cbSize); - bytesReadSoFar = 18; - fmt.extendedSize = ma_dr_wav_bytes_to_u16_ex(fmt_cbSize, pWav->container); - if (fmt.extendedSize > 0) { - if (fmt.formatTag == MA_DR_WAVE_FORMAT_EXTENSIBLE) { - if (fmt.extendedSize != 22) { - return MA_FALSE; - } - } - if (fmt.formatTag == MA_DR_WAVE_FORMAT_EXTENSIBLE) { - ma_uint8 fmtext[22]; - if (pWav->onRead(pWav->pUserData, fmtext, fmt.extendedSize) != fmt.extendedSize) { - return MA_FALSE; - } - fmt.validBitsPerSample = ma_dr_wav_bytes_to_u16_ex(fmtext + 0, pWav->container); - fmt.channelMask = ma_dr_wav_bytes_to_u32_ex(fmtext + 2, pWav->container); - ma_dr_wav_bytes_to_guid(fmtext + 6, fmt.subFormat); - } else { - if (pWav->onSeek(pWav->pUserData, fmt.extendedSize, ma_dr_wav_seek_origin_current) == MA_FALSE) { - return MA_FALSE; - } - } - cursor += fmt.extendedSize; - bytesReadSoFar += fmt.extendedSize; - } - if (pWav->onSeek(pWav->pUserData, (int)(header.sizeInBytes - bytesReadSoFar), ma_dr_wav_seek_origin_current) == MA_FALSE) { - return MA_FALSE; - } - cursor += (header.sizeInBytes - bytesReadSoFar); - } - if (header.paddingSize > 0) { - if (ma_dr_wav__seek_forward(pWav->onSeek, header.paddingSize, pWav->pUserData) == MA_FALSE) { - break; - } - cursor += header.paddingSize; - } - continue; - } - if (((pWav->container == ma_dr_wav_container_riff || pWav->container == ma_dr_wav_container_rifx || pWav->container == ma_dr_wav_container_rf64) && ma_dr_wav_fourcc_equal(header.id.fourcc, "data")) || - ((pWav->container == ma_dr_wav_container_w64) && ma_dr_wav_guid_equal(header.id.guid, ma_dr_wavGUID_W64_DATA))) { - foundChunk_data = MA_TRUE; - pWav->dataChunkDataPos = cursor; - if (pWav->container != ma_dr_wav_container_rf64) { - dataChunkSize = chunkSize; - } - if (sequential || !isProcessingMetadata) { - break; - } else { - chunkSize += header.paddingSize; - if (ma_dr_wav__seek_forward(pWav->onSeek, chunkSize, pWav->pUserData) == MA_FALSE) { - break; - } - cursor += chunkSize; - continue; - } - } - if (((pWav->container == ma_dr_wav_container_riff || pWav->container == ma_dr_wav_container_rifx || pWav->container == ma_dr_wav_container_rf64) && ma_dr_wav_fourcc_equal(header.id.fourcc, "fact")) || - ((pWav->container == ma_dr_wav_container_w64) && ma_dr_wav_guid_equal(header.id.guid, ma_dr_wavGUID_W64_FACT))) { - if (pWav->container == ma_dr_wav_container_riff || pWav->container == ma_dr_wav_container_rifx) { - ma_uint8 sampleCount[4]; - if (ma_dr_wav__on_read(pWav->onRead, pWav->pUserData, &sampleCount, 4, &cursor) != 4) { - return MA_FALSE; - } - chunkSize -= 4; - if (pWav->translatedFormatTag == MA_DR_WAVE_FORMAT_ADPCM) { - sampleCountFromFactChunk = ma_dr_wav_bytes_to_u32_ex(sampleCount, pWav->container); - } else { - sampleCountFromFactChunk = 0; - } - } else if (pWav->container == ma_dr_wav_container_w64) { - if (ma_dr_wav__on_read(pWav->onRead, pWav->pUserData, &sampleCountFromFactChunk, 8, &cursor) != 8) { - return MA_FALSE; - } - chunkSize -= 8; - } else if (pWav->container == ma_dr_wav_container_rf64) { - } - chunkSize += header.paddingSize; - if (ma_dr_wav__seek_forward(pWav->onSeek, chunkSize, pWav->pUserData) == MA_FALSE) { - break; - } - cursor += chunkSize; - continue; - } - if (pWav->container == ma_dr_wav_container_aiff && ma_dr_wav_fourcc_equal(header.id.fourcc, "COMM")) { - ma_uint8 commData[24]; - ma_uint32 commDataBytesToRead; - ma_uint16 channels; - ma_uint32 frameCount; - ma_uint16 sampleSizeInBits; - ma_int64 sampleRate; - ma_uint16 compressionFormat; - foundChunk_fmt = MA_TRUE; - if (isAIFCFormType) { - commDataBytesToRead = 24; - if (header.sizeInBytes < commDataBytesToRead) { - return MA_FALSE; - } - } else { - commDataBytesToRead = 18; - if (header.sizeInBytes != commDataBytesToRead) { - return MA_FALSE; - } - } - if (ma_dr_wav__on_read(pWav->onRead, pWav->pUserData, commData, commDataBytesToRead, &cursor) != commDataBytesToRead) { - return MA_FALSE; - } - channels = ma_dr_wav_bytes_to_u16_ex (commData + 0, pWav->container); - frameCount = ma_dr_wav_bytes_to_u32_ex (commData + 2, pWav->container); - sampleSizeInBits = ma_dr_wav_bytes_to_u16_ex (commData + 6, pWav->container); - sampleRate = ma_dr_wav_aiff_extented_to_s64(commData + 8); - if (sampleRate < 0 || sampleRate > 0xFFFFFFFF) { - return MA_FALSE; - } - if (isAIFCFormType) { - const ma_uint8* type = commData + 18; - if (ma_dr_wav_fourcc_equal(type, "NONE")) { - compressionFormat = MA_DR_WAVE_FORMAT_PCM; - } else if (ma_dr_wav_fourcc_equal(type, "raw ")) { - compressionFormat = MA_DR_WAVE_FORMAT_PCM; - if (sampleSizeInBits == 8) { - pWav->aiff.isUnsigned = MA_TRUE; - } - } else if (ma_dr_wav_fourcc_equal(type, "sowt")) { - compressionFormat = MA_DR_WAVE_FORMAT_PCM; - pWav->aiff.isLE = MA_TRUE; - } else if (ma_dr_wav_fourcc_equal(type, "fl32") || ma_dr_wav_fourcc_equal(type, "fl64") || ma_dr_wav_fourcc_equal(type, "FL32") || ma_dr_wav_fourcc_equal(type, "FL64")) { - compressionFormat = MA_DR_WAVE_FORMAT_IEEE_FLOAT; - } else if (ma_dr_wav_fourcc_equal(type, "alaw") || ma_dr_wav_fourcc_equal(type, "ALAW")) { - compressionFormat = MA_DR_WAVE_FORMAT_ALAW; - } else if (ma_dr_wav_fourcc_equal(type, "ulaw") || ma_dr_wav_fourcc_equal(type, "ULAW")) { - compressionFormat = MA_DR_WAVE_FORMAT_MULAW; - } else if (ma_dr_wav_fourcc_equal(type, "ima4")) { - compressionFormat = MA_DR_WAVE_FORMAT_DVI_ADPCM; - sampleSizeInBits = 4; - (void)compressionFormat; - (void)sampleSizeInBits; - return MA_FALSE; - } else { - return MA_FALSE; - } - } else { - compressionFormat = MA_DR_WAVE_FORMAT_PCM; - } - aiffFrameCount = frameCount; - fmt.formatTag = compressionFormat; - fmt.channels = channels; - fmt.sampleRate = (ma_uint32)sampleRate; - fmt.bitsPerSample = sampleSizeInBits; - fmt.blockAlign = (ma_uint16)(fmt.channels * fmt.bitsPerSample / 8); - fmt.avgBytesPerSec = fmt.blockAlign * fmt.sampleRate; - if (fmt.blockAlign == 0 && compressionFormat == MA_DR_WAVE_FORMAT_DVI_ADPCM) { - fmt.blockAlign = 34 * fmt.channels; - } - if (compressionFormat == MA_DR_WAVE_FORMAT_ALAW || compressionFormat == MA_DR_WAVE_FORMAT_MULAW) { - if (fmt.bitsPerSample > 8) { - fmt.bitsPerSample = 8; - fmt.blockAlign = fmt.channels; - } - } - fmt.bitsPerSample += (fmt.bitsPerSample & 7); - if (isAIFCFormType) { - if (ma_dr_wav__seek_forward(pWav->onSeek, (chunkSize - commDataBytesToRead), pWav->pUserData) == MA_FALSE) { - return MA_FALSE; - } - cursor += (chunkSize - commDataBytesToRead); - } - continue; - } - if (pWav->container == ma_dr_wav_container_aiff && ma_dr_wav_fourcc_equal(header.id.fourcc, "SSND")) { - ma_uint8 offsetAndBlockSizeData[8]; - ma_uint32 offset; - foundChunk_data = MA_TRUE; - if (ma_dr_wav__on_read(pWav->onRead, pWav->pUserData, offsetAndBlockSizeData, sizeof(offsetAndBlockSizeData), &cursor) != sizeof(offsetAndBlockSizeData)) { - return MA_FALSE; - } - offset = ma_dr_wav_bytes_to_u32_ex(offsetAndBlockSizeData + 0, pWav->container); - if (ma_dr_wav__seek_forward(pWav->onSeek, offset, pWav->pUserData) == MA_FALSE) { - return MA_FALSE; - } - cursor += offset; - pWav->dataChunkDataPos = cursor; - dataChunkSize = chunkSize; - if (sequential || !isProcessingMetadata) { - break; - } else { - if (ma_dr_wav__seek_forward(pWav->onSeek, chunkSize, pWav->pUserData) == MA_FALSE) { - break; - } - cursor += chunkSize; - continue; - } - } - if (isProcessingMetadata) { - ma_dr_wav__metadata_process_chunk(&metadataParser, &header, ma_dr_wav_metadata_type_all_including_unknown); - if (ma_dr_wav__seek_from_start(pWav->onSeek, cursor, pWav->pUserData) == MA_FALSE) { - break; - } - } - chunkSize += header.paddingSize; - if (ma_dr_wav__seek_forward(pWav->onSeek, chunkSize, pWav->pUserData) == MA_FALSE) { - break; - } - cursor += chunkSize; - } - if (!foundChunk_fmt || !foundChunk_data) { - return MA_FALSE; - } - if ((fmt.sampleRate == 0 || fmt.sampleRate > MA_DR_WAV_MAX_SAMPLE_RATE ) || - (fmt.channels == 0 || fmt.channels > MA_DR_WAV_MAX_CHANNELS ) || - (fmt.bitsPerSample == 0 || fmt.bitsPerSample > MA_DR_WAV_MAX_BITS_PER_SAMPLE) || - fmt.blockAlign == 0) { - return MA_FALSE; - } - translatedFormatTag = fmt.formatTag; - if (translatedFormatTag == MA_DR_WAVE_FORMAT_EXTENSIBLE) { - translatedFormatTag = ma_dr_wav_bytes_to_u16_ex(fmt.subFormat + 0, pWav->container); - } - if (!sequential) { - if (!ma_dr_wav__seek_from_start(pWav->onSeek, pWav->dataChunkDataPos, pWav->pUserData)) { - return MA_FALSE; - } - cursor = pWav->dataChunkDataPos; - } - if (isProcessingMetadata && metadataParser.metadataCount > 0) { - if (ma_dr_wav__seek_from_start(pWav->onSeek, metadataStartPos, pWav->pUserData) == MA_FALSE) { - return MA_FALSE; - } - result = ma_dr_wav__metadata_alloc(&metadataParser, &pWav->allocationCallbacks); - if (result != MA_SUCCESS) { - return MA_FALSE; - } - metadataParser.stage = ma_dr_wav__metadata_parser_stage_read; - for (;;) { - ma_dr_wav_chunk_header header; - ma_uint64 metadataBytesRead; - result = ma_dr_wav__read_chunk_header(pWav->onRead, pWav->pUserData, pWav->container, &cursor, &header); - if (result != MA_SUCCESS) { - break; - } - metadataBytesRead = ma_dr_wav__metadata_process_chunk(&metadataParser, &header, ma_dr_wav_metadata_type_all_including_unknown); - if (ma_dr_wav__seek_forward(pWav->onSeek, (header.sizeInBytes + header.paddingSize) - metadataBytesRead, pWav->pUserData) == MA_FALSE) { - ma_dr_wav_free(metadataParser.pMetadata, &pWav->allocationCallbacks); - return MA_FALSE; - } - } - pWav->pMetadata = metadataParser.pMetadata; - pWav->metadataCount = metadataParser.metadataCount; - } - if (dataChunkSize == 0xFFFFFFFF && (pWav->container == ma_dr_wav_container_riff || pWav->container == ma_dr_wav_container_rifx) && pWav->isSequentialWrite == MA_FALSE) { - dataChunkSize = 0; - for (;;) { - ma_uint8 temp[4096]; - size_t bytesRead = pWav->onRead(pWav->pUserData, temp, sizeof(temp)); - dataChunkSize += bytesRead; - if (bytesRead < sizeof(temp)) { - break; - } - } - } - if (ma_dr_wav__seek_from_start(pWav->onSeek, pWav->dataChunkDataPos, pWav->pUserData) == MA_FALSE) { - ma_dr_wav_free(pWav->pMetadata, &pWav->allocationCallbacks); - return MA_FALSE; - } - pWav->fmt = fmt; - pWav->sampleRate = fmt.sampleRate; - pWav->channels = fmt.channels; - pWav->bitsPerSample = fmt.bitsPerSample; - pWav->bytesRemaining = dataChunkSize; - pWav->translatedFormatTag = translatedFormatTag; - pWav->dataChunkDataSize = dataChunkSize; - if (sampleCountFromFactChunk != 0) { - pWav->totalPCMFrameCount = sampleCountFromFactChunk; - } else if (aiffFrameCount != 0) { - pWav->totalPCMFrameCount = aiffFrameCount; - } else { - ma_uint32 bytesPerFrame = ma_dr_wav_get_bytes_per_pcm_frame(pWav); - if (bytesPerFrame == 0) { - ma_dr_wav_free(pWav->pMetadata, &pWav->allocationCallbacks); - return MA_FALSE; - } - pWav->totalPCMFrameCount = dataChunkSize / bytesPerFrame; - if (pWav->translatedFormatTag == MA_DR_WAVE_FORMAT_ADPCM) { - ma_uint64 totalBlockHeaderSizeInBytes; - ma_uint64 blockCount = dataChunkSize / fmt.blockAlign; - if ((blockCount * fmt.blockAlign) < dataChunkSize) { - blockCount += 1; - } - totalBlockHeaderSizeInBytes = blockCount * (6*fmt.channels); - pWav->totalPCMFrameCount = ((dataChunkSize - totalBlockHeaderSizeInBytes) * 2) / fmt.channels; - } - if (pWav->translatedFormatTag == MA_DR_WAVE_FORMAT_DVI_ADPCM) { - ma_uint64 totalBlockHeaderSizeInBytes; - ma_uint64 blockCount = dataChunkSize / fmt.blockAlign; - if ((blockCount * fmt.blockAlign) < dataChunkSize) { - blockCount += 1; - } - totalBlockHeaderSizeInBytes = blockCount * (4*fmt.channels); - pWav->totalPCMFrameCount = ((dataChunkSize - totalBlockHeaderSizeInBytes) * 2) / fmt.channels; - pWav->totalPCMFrameCount += blockCount; - } - } - if (pWav->translatedFormatTag == MA_DR_WAVE_FORMAT_ADPCM || pWav->translatedFormatTag == MA_DR_WAVE_FORMAT_DVI_ADPCM) { - if (pWav->channels > 2) { - ma_dr_wav_free(pWav->pMetadata, &pWav->allocationCallbacks); - return MA_FALSE; - } - } - if (ma_dr_wav_get_bytes_per_pcm_frame(pWav) == 0) { - ma_dr_wav_free(pWav->pMetadata, &pWav->allocationCallbacks); - return MA_FALSE; - } -#ifdef MA_DR_WAV_LIBSNDFILE_COMPAT - if (pWav->translatedFormatTag == MA_DR_WAVE_FORMAT_ADPCM) { - ma_uint64 blockCount = dataChunkSize / fmt.blockAlign; - pWav->totalPCMFrameCount = (((blockCount * (fmt.blockAlign - (6*pWav->channels))) * 2)) / fmt.channels; - } - if (pWav->translatedFormatTag == MA_DR_WAVE_FORMAT_DVI_ADPCM) { - ma_uint64 blockCount = dataChunkSize / fmt.blockAlign; - pWav->totalPCMFrameCount = (((blockCount * (fmt.blockAlign - (4*pWav->channels))) * 2) + (blockCount * pWav->channels)) / fmt.channels; - } -#endif - return MA_TRUE; -} -MA_API ma_bool32 ma_dr_wav_init(ma_dr_wav* pWav, ma_dr_wav_read_proc onRead, ma_dr_wav_seek_proc onSeek, void* pUserData, const ma_allocation_callbacks* pAllocationCallbacks) -{ - return ma_dr_wav_init_ex(pWav, onRead, onSeek, NULL, pUserData, NULL, 0, pAllocationCallbacks); -} -MA_API ma_bool32 ma_dr_wav_init_ex(ma_dr_wav* pWav, ma_dr_wav_read_proc onRead, ma_dr_wav_seek_proc onSeek, ma_dr_wav_chunk_proc onChunk, void* pReadSeekUserData, void* pChunkUserData, ma_uint32 flags, const ma_allocation_callbacks* pAllocationCallbacks) -{ - if (!ma_dr_wav_preinit(pWav, onRead, onSeek, pReadSeekUserData, pAllocationCallbacks)) { - return MA_FALSE; - } - return ma_dr_wav_init__internal(pWav, onChunk, pChunkUserData, flags); -} -MA_API ma_bool32 ma_dr_wav_init_with_metadata(ma_dr_wav* pWav, ma_dr_wav_read_proc onRead, ma_dr_wav_seek_proc onSeek, void* pUserData, ma_uint32 flags, const ma_allocation_callbacks* pAllocationCallbacks) -{ - if (!ma_dr_wav_preinit(pWav, onRead, onSeek, pUserData, pAllocationCallbacks)) { - return MA_FALSE; - } - return ma_dr_wav_init__internal(pWav, NULL, NULL, flags | MA_DR_WAV_WITH_METADATA); -} -MA_API ma_dr_wav_metadata* ma_dr_wav_take_ownership_of_metadata(ma_dr_wav* pWav) -{ - ma_dr_wav_metadata *result = pWav->pMetadata; - pWav->pMetadata = NULL; - pWav->metadataCount = 0; - return result; -} -MA_PRIVATE size_t ma_dr_wav__write(ma_dr_wav* pWav, const void* pData, size_t dataSize) -{ - MA_DR_WAV_ASSERT(pWav != NULL); - MA_DR_WAV_ASSERT(pWav->onWrite != NULL); - return pWav->onWrite(pWav->pUserData, pData, dataSize); -} -MA_PRIVATE size_t ma_dr_wav__write_byte(ma_dr_wav* pWav, ma_uint8 byte) -{ - MA_DR_WAV_ASSERT(pWav != NULL); - MA_DR_WAV_ASSERT(pWav->onWrite != NULL); - return pWav->onWrite(pWav->pUserData, &byte, 1); -} -MA_PRIVATE size_t ma_dr_wav__write_u16ne_to_le(ma_dr_wav* pWav, ma_uint16 value) -{ - MA_DR_WAV_ASSERT(pWav != NULL); - MA_DR_WAV_ASSERT(pWav->onWrite != NULL); - if (!ma_dr_wav__is_little_endian()) { - value = ma_dr_wav__bswap16(value); - } - return ma_dr_wav__write(pWav, &value, 2); -} -MA_PRIVATE size_t ma_dr_wav__write_u32ne_to_le(ma_dr_wav* pWav, ma_uint32 value) -{ - MA_DR_WAV_ASSERT(pWav != NULL); - MA_DR_WAV_ASSERT(pWav->onWrite != NULL); - if (!ma_dr_wav__is_little_endian()) { - value = ma_dr_wav__bswap32(value); - } - return ma_dr_wav__write(pWav, &value, 4); -} -MA_PRIVATE size_t ma_dr_wav__write_u64ne_to_le(ma_dr_wav* pWav, ma_uint64 value) -{ - MA_DR_WAV_ASSERT(pWav != NULL); - MA_DR_WAV_ASSERT(pWav->onWrite != NULL); - if (!ma_dr_wav__is_little_endian()) { - value = ma_dr_wav__bswap64(value); - } - return ma_dr_wav__write(pWav, &value, 8); -} -MA_PRIVATE size_t ma_dr_wav__write_f32ne_to_le(ma_dr_wav* pWav, float value) -{ - union { - ma_uint32 u32; - float f32; - } u; - MA_DR_WAV_ASSERT(pWav != NULL); - MA_DR_WAV_ASSERT(pWav->onWrite != NULL); - u.f32 = value; - if (!ma_dr_wav__is_little_endian()) { - u.u32 = ma_dr_wav__bswap32(u.u32); - } - return ma_dr_wav__write(pWav, &u.u32, 4); -} -MA_PRIVATE size_t ma_dr_wav__write_or_count(ma_dr_wav* pWav, const void* pData, size_t dataSize) -{ - if (pWav == NULL) { - return dataSize; - } - return ma_dr_wav__write(pWav, pData, dataSize); -} -MA_PRIVATE size_t ma_dr_wav__write_or_count_byte(ma_dr_wav* pWav, ma_uint8 byte) -{ - if (pWav == NULL) { - return 1; - } - return ma_dr_wav__write_byte(pWav, byte); -} -MA_PRIVATE size_t ma_dr_wav__write_or_count_u16ne_to_le(ma_dr_wav* pWav, ma_uint16 value) -{ - if (pWav == NULL) { - return 2; - } - return ma_dr_wav__write_u16ne_to_le(pWav, value); -} -MA_PRIVATE size_t ma_dr_wav__write_or_count_u32ne_to_le(ma_dr_wav* pWav, ma_uint32 value) -{ - if (pWav == NULL) { - return 4; - } - return ma_dr_wav__write_u32ne_to_le(pWav, value); -} -#if 0 -MA_PRIVATE size_t ma_dr_wav__write_or_count_u64ne_to_le(ma_dr_wav* pWav, ma_uint64 value) -{ - if (pWav == NULL) { - return 8; - } - return ma_dr_wav__write_u64ne_to_le(pWav, value); -} -#endif -MA_PRIVATE size_t ma_dr_wav__write_or_count_f32ne_to_le(ma_dr_wav* pWav, float value) -{ - if (pWav == NULL) { - return 4; - } - return ma_dr_wav__write_f32ne_to_le(pWav, value); -} -MA_PRIVATE size_t ma_dr_wav__write_or_count_string_to_fixed_size_buf(ma_dr_wav* pWav, char* str, size_t bufFixedSize) -{ - size_t len; - if (pWav == NULL) { - return bufFixedSize; - } - len = ma_dr_wav__strlen_clamped(str, bufFixedSize); - ma_dr_wav__write_or_count(pWav, str, len); - if (len < bufFixedSize) { - size_t i; - for (i = 0; i < bufFixedSize - len; ++i) { - ma_dr_wav__write_byte(pWav, 0); - } - } - return bufFixedSize; -} -MA_PRIVATE size_t ma_dr_wav__write_or_count_metadata(ma_dr_wav* pWav, ma_dr_wav_metadata* pMetadatas, ma_uint32 metadataCount) -{ - size_t bytesWritten = 0; - ma_bool32 hasListAdtl = MA_FALSE; - ma_bool32 hasListInfo = MA_FALSE; - ma_uint32 iMetadata; - if (pMetadatas == NULL || metadataCount == 0) { - return 0; - } - for (iMetadata = 0; iMetadata < metadataCount; ++iMetadata) { - ma_dr_wav_metadata* pMetadata = &pMetadatas[iMetadata]; - ma_uint32 chunkSize = 0; - if ((pMetadata->type & ma_dr_wav_metadata_type_list_all_info_strings) || (pMetadata->type == ma_dr_wav_metadata_type_unknown && pMetadata->data.unknown.chunkLocation == ma_dr_wav_metadata_location_inside_info_list)) { - hasListInfo = MA_TRUE; - } - if ((pMetadata->type & ma_dr_wav_metadata_type_list_all_adtl) || (pMetadata->type == ma_dr_wav_metadata_type_unknown && pMetadata->data.unknown.chunkLocation == ma_dr_wav_metadata_location_inside_adtl_list)) { - hasListAdtl = MA_TRUE; - } - switch (pMetadata->type) { - case ma_dr_wav_metadata_type_smpl: - { - ma_uint32 iLoop; - chunkSize = MA_DR_WAV_SMPL_BYTES + MA_DR_WAV_SMPL_LOOP_BYTES * pMetadata->data.smpl.sampleLoopCount + pMetadata->data.smpl.samplerSpecificDataSizeInBytes; - bytesWritten += ma_dr_wav__write_or_count(pWav, "smpl", 4); - bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, chunkSize); - bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, pMetadata->data.smpl.manufacturerId); - bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, pMetadata->data.smpl.productId); - bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, pMetadata->data.smpl.samplePeriodNanoseconds); - bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, pMetadata->data.smpl.midiUnityNote); - bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, pMetadata->data.smpl.midiPitchFraction); - bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, pMetadata->data.smpl.smpteFormat); - bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, pMetadata->data.smpl.smpteOffset); - bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, pMetadata->data.smpl.sampleLoopCount); - bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, pMetadata->data.smpl.samplerSpecificDataSizeInBytes); - for (iLoop = 0; iLoop < pMetadata->data.smpl.sampleLoopCount; ++iLoop) { - bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, pMetadata->data.smpl.pLoops[iLoop].cuePointId); - bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, pMetadata->data.smpl.pLoops[iLoop].type); - bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, pMetadata->data.smpl.pLoops[iLoop].firstSampleByteOffset); - bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, pMetadata->data.smpl.pLoops[iLoop].lastSampleByteOffset); - bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, pMetadata->data.smpl.pLoops[iLoop].sampleFraction); - bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, pMetadata->data.smpl.pLoops[iLoop].playCount); - } - if (pMetadata->data.smpl.samplerSpecificDataSizeInBytes > 0) { - bytesWritten += ma_dr_wav__write_or_count(pWav, pMetadata->data.smpl.pSamplerSpecificData, pMetadata->data.smpl.samplerSpecificDataSizeInBytes); - } - } break; - case ma_dr_wav_metadata_type_inst: - { - chunkSize = MA_DR_WAV_INST_BYTES; - bytesWritten += ma_dr_wav__write_or_count(pWav, "inst", 4); - bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, chunkSize); - bytesWritten += ma_dr_wav__write_or_count(pWav, &pMetadata->data.inst.midiUnityNote, 1); - bytesWritten += ma_dr_wav__write_or_count(pWav, &pMetadata->data.inst.fineTuneCents, 1); - bytesWritten += ma_dr_wav__write_or_count(pWav, &pMetadata->data.inst.gainDecibels, 1); - bytesWritten += ma_dr_wav__write_or_count(pWav, &pMetadata->data.inst.lowNote, 1); - bytesWritten += ma_dr_wav__write_or_count(pWav, &pMetadata->data.inst.highNote, 1); - bytesWritten += ma_dr_wav__write_or_count(pWav, &pMetadata->data.inst.lowVelocity, 1); - bytesWritten += ma_dr_wav__write_or_count(pWav, &pMetadata->data.inst.highVelocity, 1); - } break; - case ma_dr_wav_metadata_type_cue: - { - ma_uint32 iCuePoint; - chunkSize = MA_DR_WAV_CUE_BYTES + MA_DR_WAV_CUE_POINT_BYTES * pMetadata->data.cue.cuePointCount; - bytesWritten += ma_dr_wav__write_or_count(pWav, "cue ", 4); - bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, chunkSize); - bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, pMetadata->data.cue.cuePointCount); - for (iCuePoint = 0; iCuePoint < pMetadata->data.cue.cuePointCount; ++iCuePoint) { - bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, pMetadata->data.cue.pCuePoints[iCuePoint].id); - bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, pMetadata->data.cue.pCuePoints[iCuePoint].playOrderPosition); - bytesWritten += ma_dr_wav__write_or_count(pWav, pMetadata->data.cue.pCuePoints[iCuePoint].dataChunkId, 4); - bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, pMetadata->data.cue.pCuePoints[iCuePoint].chunkStart); - bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, pMetadata->data.cue.pCuePoints[iCuePoint].blockStart); - bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, pMetadata->data.cue.pCuePoints[iCuePoint].sampleByteOffset); - } - } break; - case ma_dr_wav_metadata_type_acid: - { - chunkSize = MA_DR_WAV_ACID_BYTES; - bytesWritten += ma_dr_wav__write_or_count(pWav, "acid", 4); - bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, chunkSize); - bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, pMetadata->data.acid.flags); - bytesWritten += ma_dr_wav__write_or_count_u16ne_to_le(pWav, pMetadata->data.acid.midiUnityNote); - bytesWritten += ma_dr_wav__write_or_count_u16ne_to_le(pWav, pMetadata->data.acid.reserved1); - bytesWritten += ma_dr_wav__write_or_count_f32ne_to_le(pWav, pMetadata->data.acid.reserved2); - bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, pMetadata->data.acid.numBeats); - bytesWritten += ma_dr_wav__write_or_count_u16ne_to_le(pWav, pMetadata->data.acid.meterDenominator); - bytesWritten += ma_dr_wav__write_or_count_u16ne_to_le(pWav, pMetadata->data.acid.meterNumerator); - bytesWritten += ma_dr_wav__write_or_count_f32ne_to_le(pWav, pMetadata->data.acid.tempo); - } break; - case ma_dr_wav_metadata_type_bext: - { - char reservedBuf[MA_DR_WAV_BEXT_RESERVED_BYTES]; - ma_uint32 timeReferenceLow; - ma_uint32 timeReferenceHigh; - chunkSize = MA_DR_WAV_BEXT_BYTES + pMetadata->data.bext.codingHistorySize; - bytesWritten += ma_dr_wav__write_or_count(pWav, "bext", 4); - bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, chunkSize); - bytesWritten += ma_dr_wav__write_or_count_string_to_fixed_size_buf(pWav, pMetadata->data.bext.pDescription, MA_DR_WAV_BEXT_DESCRIPTION_BYTES); - bytesWritten += ma_dr_wav__write_or_count_string_to_fixed_size_buf(pWav, pMetadata->data.bext.pOriginatorName, MA_DR_WAV_BEXT_ORIGINATOR_NAME_BYTES); - bytesWritten += ma_dr_wav__write_or_count_string_to_fixed_size_buf(pWav, pMetadata->data.bext.pOriginatorReference, MA_DR_WAV_BEXT_ORIGINATOR_REF_BYTES); - bytesWritten += ma_dr_wav__write_or_count(pWav, pMetadata->data.bext.pOriginationDate, sizeof(pMetadata->data.bext.pOriginationDate)); - bytesWritten += ma_dr_wav__write_or_count(pWav, pMetadata->data.bext.pOriginationTime, sizeof(pMetadata->data.bext.pOriginationTime)); - timeReferenceLow = (ma_uint32)(pMetadata->data.bext.timeReference & 0xFFFFFFFF); - timeReferenceHigh = (ma_uint32)(pMetadata->data.bext.timeReference >> 32); - bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, timeReferenceLow); - bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, timeReferenceHigh); - bytesWritten += ma_dr_wav__write_or_count_u16ne_to_le(pWav, pMetadata->data.bext.version); - bytesWritten += ma_dr_wav__write_or_count(pWav, pMetadata->data.bext.pUMID, MA_DR_WAV_BEXT_UMID_BYTES); - bytesWritten += ma_dr_wav__write_or_count_u16ne_to_le(pWav, pMetadata->data.bext.loudnessValue); - bytesWritten += ma_dr_wav__write_or_count_u16ne_to_le(pWav, pMetadata->data.bext.loudnessRange); - bytesWritten += ma_dr_wav__write_or_count_u16ne_to_le(pWav, pMetadata->data.bext.maxTruePeakLevel); - bytesWritten += ma_dr_wav__write_or_count_u16ne_to_le(pWav, pMetadata->data.bext.maxMomentaryLoudness); - bytesWritten += ma_dr_wav__write_or_count_u16ne_to_le(pWav, pMetadata->data.bext.maxShortTermLoudness); - MA_DR_WAV_ZERO_MEMORY(reservedBuf, sizeof(reservedBuf)); - bytesWritten += ma_dr_wav__write_or_count(pWav, reservedBuf, sizeof(reservedBuf)); - if (pMetadata->data.bext.codingHistorySize > 0) { - bytesWritten += ma_dr_wav__write_or_count(pWav, pMetadata->data.bext.pCodingHistory, pMetadata->data.bext.codingHistorySize); - } - } break; - case ma_dr_wav_metadata_type_unknown: - { - if (pMetadata->data.unknown.chunkLocation == ma_dr_wav_metadata_location_top_level) { - chunkSize = pMetadata->data.unknown.dataSizeInBytes; - bytesWritten += ma_dr_wav__write_or_count(pWav, pMetadata->data.unknown.id, 4); - bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, chunkSize); - bytesWritten += ma_dr_wav__write_or_count(pWav, pMetadata->data.unknown.pData, pMetadata->data.unknown.dataSizeInBytes); - } - } break; - default: break; - } - if ((chunkSize % 2) != 0) { - bytesWritten += ma_dr_wav__write_or_count_byte(pWav, 0); - } - } - if (hasListInfo) { - ma_uint32 chunkSize = 4; - for (iMetadata = 0; iMetadata < metadataCount; ++iMetadata) { - ma_dr_wav_metadata* pMetadata = &pMetadatas[iMetadata]; - if ((pMetadata->type & ma_dr_wav_metadata_type_list_all_info_strings)) { - chunkSize += 8; - chunkSize += pMetadata->data.infoText.stringLength + 1; - } else if (pMetadata->type == ma_dr_wav_metadata_type_unknown && pMetadata->data.unknown.chunkLocation == ma_dr_wav_metadata_location_inside_info_list) { - chunkSize += 8; - chunkSize += pMetadata->data.unknown.dataSizeInBytes; - } - if ((chunkSize % 2) != 0) { - chunkSize += 1; - } - } - bytesWritten += ma_dr_wav__write_or_count(pWav, "LIST", 4); - bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, chunkSize); - bytesWritten += ma_dr_wav__write_or_count(pWav, "INFO", 4); - for (iMetadata = 0; iMetadata < metadataCount; ++iMetadata) { - ma_dr_wav_metadata* pMetadata = &pMetadatas[iMetadata]; - ma_uint32 subchunkSize = 0; - if (pMetadata->type & ma_dr_wav_metadata_type_list_all_info_strings) { - const char* pID = NULL; - switch (pMetadata->type) { - case ma_dr_wav_metadata_type_list_info_software: pID = "ISFT"; break; - case ma_dr_wav_metadata_type_list_info_copyright: pID = "ICOP"; break; - case ma_dr_wav_metadata_type_list_info_title: pID = "INAM"; break; - case ma_dr_wav_metadata_type_list_info_artist: pID = "IART"; break; - case ma_dr_wav_metadata_type_list_info_comment: pID = "ICMT"; break; - case ma_dr_wav_metadata_type_list_info_date: pID = "ICRD"; break; - case ma_dr_wav_metadata_type_list_info_genre: pID = "IGNR"; break; - case ma_dr_wav_metadata_type_list_info_album: pID = "IPRD"; break; - case ma_dr_wav_metadata_type_list_info_tracknumber: pID = "ITRK"; break; - default: break; - } - MA_DR_WAV_ASSERT(pID != NULL); - if (pMetadata->data.infoText.stringLength) { - subchunkSize = pMetadata->data.infoText.stringLength + 1; - bytesWritten += ma_dr_wav__write_or_count(pWav, pID, 4); - bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, subchunkSize); - bytesWritten += ma_dr_wav__write_or_count(pWav, pMetadata->data.infoText.pString, pMetadata->data.infoText.stringLength); - bytesWritten += ma_dr_wav__write_or_count_byte(pWav, '\0'); - } - } else if (pMetadata->type == ma_dr_wav_metadata_type_unknown && pMetadata->data.unknown.chunkLocation == ma_dr_wav_metadata_location_inside_info_list) { - if (pMetadata->data.unknown.dataSizeInBytes) { - subchunkSize = pMetadata->data.unknown.dataSizeInBytes; - bytesWritten += ma_dr_wav__write_or_count(pWav, pMetadata->data.unknown.id, 4); - bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, pMetadata->data.unknown.dataSizeInBytes); - bytesWritten += ma_dr_wav__write_or_count(pWav, pMetadata->data.unknown.pData, subchunkSize); - } - } - if ((subchunkSize % 2) != 0) { - bytesWritten += ma_dr_wav__write_or_count_byte(pWav, 0); - } - } - } - if (hasListAdtl) { - ma_uint32 chunkSize = 4; - for (iMetadata = 0; iMetadata < metadataCount; ++iMetadata) { - ma_dr_wav_metadata* pMetadata = &pMetadatas[iMetadata]; - switch (pMetadata->type) - { - case ma_dr_wav_metadata_type_list_label: - case ma_dr_wav_metadata_type_list_note: - { - chunkSize += 8; - chunkSize += MA_DR_WAV_LIST_LABEL_OR_NOTE_BYTES; - if (pMetadata->data.labelOrNote.stringLength > 0) { - chunkSize += pMetadata->data.labelOrNote.stringLength + 1; - } - } break; - case ma_dr_wav_metadata_type_list_labelled_cue_region: - { - chunkSize += 8; - chunkSize += MA_DR_WAV_LIST_LABELLED_TEXT_BYTES; - if (pMetadata->data.labelledCueRegion.stringLength > 0) { - chunkSize += pMetadata->data.labelledCueRegion.stringLength + 1; - } - } break; - case ma_dr_wav_metadata_type_unknown: - { - if (pMetadata->data.unknown.chunkLocation == ma_dr_wav_metadata_location_inside_adtl_list) { - chunkSize += 8; - chunkSize += pMetadata->data.unknown.dataSizeInBytes; - } - } break; - default: break; - } - if ((chunkSize % 2) != 0) { - chunkSize += 1; - } - } - bytesWritten += ma_dr_wav__write_or_count(pWav, "LIST", 4); - bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, chunkSize); - bytesWritten += ma_dr_wav__write_or_count(pWav, "adtl", 4); - for (iMetadata = 0; iMetadata < metadataCount; ++iMetadata) { - ma_dr_wav_metadata* pMetadata = &pMetadatas[iMetadata]; - ma_uint32 subchunkSize = 0; - switch (pMetadata->type) - { - case ma_dr_wav_metadata_type_list_label: - case ma_dr_wav_metadata_type_list_note: - { - if (pMetadata->data.labelOrNote.stringLength > 0) { - const char *pID = NULL; - if (pMetadata->type == ma_dr_wav_metadata_type_list_label) { - pID = "labl"; - } - else if (pMetadata->type == ma_dr_wav_metadata_type_list_note) { - pID = "note"; - } - MA_DR_WAV_ASSERT(pID != NULL); - MA_DR_WAV_ASSERT(pMetadata->data.labelOrNote.pString != NULL); - subchunkSize = MA_DR_WAV_LIST_LABEL_OR_NOTE_BYTES; - bytesWritten += ma_dr_wav__write_or_count(pWav, pID, 4); - subchunkSize += pMetadata->data.labelOrNote.stringLength + 1; - bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, subchunkSize); - bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, pMetadata->data.labelOrNote.cuePointId); - bytesWritten += ma_dr_wav__write_or_count(pWav, pMetadata->data.labelOrNote.pString, pMetadata->data.labelOrNote.stringLength); - bytesWritten += ma_dr_wav__write_or_count_byte(pWav, '\0'); - } - } break; - case ma_dr_wav_metadata_type_list_labelled_cue_region: - { - subchunkSize = MA_DR_WAV_LIST_LABELLED_TEXT_BYTES; - bytesWritten += ma_dr_wav__write_or_count(pWav, "ltxt", 4); - if (pMetadata->data.labelledCueRegion.stringLength > 0) { - subchunkSize += pMetadata->data.labelledCueRegion.stringLength + 1; - } - bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, subchunkSize); - bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, pMetadata->data.labelledCueRegion.cuePointId); - bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, pMetadata->data.labelledCueRegion.sampleLength); - bytesWritten += ma_dr_wav__write_or_count(pWav, pMetadata->data.labelledCueRegion.purposeId, 4); - bytesWritten += ma_dr_wav__write_or_count_u16ne_to_le(pWav, pMetadata->data.labelledCueRegion.country); - bytesWritten += ma_dr_wav__write_or_count_u16ne_to_le(pWav, pMetadata->data.labelledCueRegion.language); - bytesWritten += ma_dr_wav__write_or_count_u16ne_to_le(pWav, pMetadata->data.labelledCueRegion.dialect); - bytesWritten += ma_dr_wav__write_or_count_u16ne_to_le(pWav, pMetadata->data.labelledCueRegion.codePage); - if (pMetadata->data.labelledCueRegion.stringLength > 0) { - MA_DR_WAV_ASSERT(pMetadata->data.labelledCueRegion.pString != NULL); - bytesWritten += ma_dr_wav__write_or_count(pWav, pMetadata->data.labelledCueRegion.pString, pMetadata->data.labelledCueRegion.stringLength); - bytesWritten += ma_dr_wav__write_or_count_byte(pWav, '\0'); - } - } break; - case ma_dr_wav_metadata_type_unknown: - { - if (pMetadata->data.unknown.chunkLocation == ma_dr_wav_metadata_location_inside_adtl_list) { - subchunkSize = pMetadata->data.unknown.dataSizeInBytes; - MA_DR_WAV_ASSERT(pMetadata->data.unknown.pData != NULL); - bytesWritten += ma_dr_wav__write_or_count(pWav, pMetadata->data.unknown.id, 4); - bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, subchunkSize); - bytesWritten += ma_dr_wav__write_or_count(pWav, pMetadata->data.unknown.pData, subchunkSize); - } - } break; - default: break; - } - if ((subchunkSize % 2) != 0) { - bytesWritten += ma_dr_wav__write_or_count_byte(pWav, 0); - } - } - } - MA_DR_WAV_ASSERT((bytesWritten % 2) == 0); - return bytesWritten; -} -MA_PRIVATE ma_uint32 ma_dr_wav__riff_chunk_size_riff(ma_uint64 dataChunkSize, ma_dr_wav_metadata* pMetadata, ma_uint32 metadataCount) -{ - ma_uint64 chunkSize = 4 + 24 + (ma_uint64)ma_dr_wav__write_or_count_metadata(NULL, pMetadata, metadataCount) + 8 + dataChunkSize + ma_dr_wav__chunk_padding_size_riff(dataChunkSize); - if (chunkSize > 0xFFFFFFFFUL) { - chunkSize = 0xFFFFFFFFUL; - } - return (ma_uint32)chunkSize; -} -MA_PRIVATE ma_uint32 ma_dr_wav__data_chunk_size_riff(ma_uint64 dataChunkSize) -{ - if (dataChunkSize <= 0xFFFFFFFFUL) { - return (ma_uint32)dataChunkSize; - } else { - return 0xFFFFFFFFUL; - } -} -MA_PRIVATE ma_uint64 ma_dr_wav__riff_chunk_size_w64(ma_uint64 dataChunkSize) -{ - ma_uint64 dataSubchunkPaddingSize = ma_dr_wav__chunk_padding_size_w64(dataChunkSize); - return 80 + 24 + dataChunkSize + dataSubchunkPaddingSize; -} -MA_PRIVATE ma_uint64 ma_dr_wav__data_chunk_size_w64(ma_uint64 dataChunkSize) -{ - return 24 + dataChunkSize; -} -MA_PRIVATE ma_uint64 ma_dr_wav__riff_chunk_size_rf64(ma_uint64 dataChunkSize, ma_dr_wav_metadata *metadata, ma_uint32 numMetadata) -{ - ma_uint64 chunkSize = 4 + 36 + 24 + (ma_uint64)ma_dr_wav__write_or_count_metadata(NULL, metadata, numMetadata) + 8 + dataChunkSize + ma_dr_wav__chunk_padding_size_riff(dataChunkSize); - if (chunkSize > 0xFFFFFFFFUL) { - chunkSize = 0xFFFFFFFFUL; - } - return chunkSize; -} -MA_PRIVATE ma_uint64 ma_dr_wav__data_chunk_size_rf64(ma_uint64 dataChunkSize) -{ - return dataChunkSize; -} -MA_PRIVATE ma_bool32 ma_dr_wav_preinit_write(ma_dr_wav* pWav, const ma_dr_wav_data_format* pFormat, ma_bool32 isSequential, ma_dr_wav_write_proc onWrite, ma_dr_wav_seek_proc onSeek, void* pUserData, const ma_allocation_callbacks* pAllocationCallbacks) -{ - if (pWav == NULL || onWrite == NULL) { - return MA_FALSE; - } - if (!isSequential && onSeek == NULL) { - return MA_FALSE; - } - if (pFormat->format == MA_DR_WAVE_FORMAT_EXTENSIBLE) { - return MA_FALSE; - } - if (pFormat->format == MA_DR_WAVE_FORMAT_ADPCM || pFormat->format == MA_DR_WAVE_FORMAT_DVI_ADPCM) { - return MA_FALSE; - } - MA_DR_WAV_ZERO_MEMORY(pWav, sizeof(*pWav)); - pWav->onWrite = onWrite; - pWav->onSeek = onSeek; - pWav->pUserData = pUserData; - pWav->allocationCallbacks = ma_dr_wav_copy_allocation_callbacks_or_defaults(pAllocationCallbacks); - if (pWav->allocationCallbacks.onFree == NULL || (pWav->allocationCallbacks.onMalloc == NULL && pWav->allocationCallbacks.onRealloc == NULL)) { - return MA_FALSE; - } - pWav->fmt.formatTag = (ma_uint16)pFormat->format; - pWav->fmt.channels = (ma_uint16)pFormat->channels; - pWav->fmt.sampleRate = pFormat->sampleRate; - pWav->fmt.avgBytesPerSec = (ma_uint32)((pFormat->bitsPerSample * pFormat->sampleRate * pFormat->channels) / 8); - pWav->fmt.blockAlign = (ma_uint16)((pFormat->channels * pFormat->bitsPerSample) / 8); - pWav->fmt.bitsPerSample = (ma_uint16)pFormat->bitsPerSample; - pWav->fmt.extendedSize = 0; - pWav->isSequentialWrite = isSequential; - return MA_TRUE; -} -MA_PRIVATE ma_bool32 ma_dr_wav_init_write__internal(ma_dr_wav* pWav, const ma_dr_wav_data_format* pFormat, ma_uint64 totalSampleCount) -{ - size_t runningPos = 0; - ma_uint64 initialDataChunkSize = 0; - ma_uint64 chunkSizeFMT; - if (pWav->isSequentialWrite) { - initialDataChunkSize = (totalSampleCount * pWav->fmt.bitsPerSample) / 8; - if (pFormat->container == ma_dr_wav_container_riff) { - if (initialDataChunkSize > (0xFFFFFFFFUL - 36)) { - return MA_FALSE; - } - } - } - pWav->dataChunkDataSizeTargetWrite = initialDataChunkSize; - if (pFormat->container == ma_dr_wav_container_riff) { - ma_uint32 chunkSizeRIFF = 28 + (ma_uint32)initialDataChunkSize; - runningPos += ma_dr_wav__write(pWav, "RIFF", 4); - runningPos += ma_dr_wav__write_u32ne_to_le(pWav, chunkSizeRIFF); - runningPos += ma_dr_wav__write(pWav, "WAVE", 4); - } else if (pFormat->container == ma_dr_wav_container_w64) { - ma_uint64 chunkSizeRIFF = 80 + 24 + initialDataChunkSize; - runningPos += ma_dr_wav__write(pWav, ma_dr_wavGUID_W64_RIFF, 16); - runningPos += ma_dr_wav__write_u64ne_to_le(pWav, chunkSizeRIFF); - runningPos += ma_dr_wav__write(pWav, ma_dr_wavGUID_W64_WAVE, 16); - } else if (pFormat->container == ma_dr_wav_container_rf64) { - runningPos += ma_dr_wav__write(pWav, "RF64", 4); - runningPos += ma_dr_wav__write_u32ne_to_le(pWav, 0xFFFFFFFF); - runningPos += ma_dr_wav__write(pWav, "WAVE", 4); - } else { - return MA_FALSE; - } - if (pFormat->container == ma_dr_wav_container_rf64) { - ma_uint32 initialds64ChunkSize = 28; - ma_uint64 initialRiffChunkSize = 8 + initialds64ChunkSize + initialDataChunkSize; - runningPos += ma_dr_wav__write(pWav, "ds64", 4); - runningPos += ma_dr_wav__write_u32ne_to_le(pWav, initialds64ChunkSize); - runningPos += ma_dr_wav__write_u64ne_to_le(pWav, initialRiffChunkSize); - runningPos += ma_dr_wav__write_u64ne_to_le(pWav, initialDataChunkSize); - runningPos += ma_dr_wav__write_u64ne_to_le(pWav, totalSampleCount); - runningPos += ma_dr_wav__write_u32ne_to_le(pWav, 0); - } - if (pFormat->container == ma_dr_wav_container_riff || pFormat->container == ma_dr_wav_container_rf64) { - chunkSizeFMT = 16; - runningPos += ma_dr_wav__write(pWav, "fmt ", 4); - runningPos += ma_dr_wav__write_u32ne_to_le(pWav, (ma_uint32)chunkSizeFMT); - } else if (pFormat->container == ma_dr_wav_container_w64) { - chunkSizeFMT = 40; - runningPos += ma_dr_wav__write(pWav, ma_dr_wavGUID_W64_FMT, 16); - runningPos += ma_dr_wav__write_u64ne_to_le(pWav, chunkSizeFMT); - } - runningPos += ma_dr_wav__write_u16ne_to_le(pWav, pWav->fmt.formatTag); - runningPos += ma_dr_wav__write_u16ne_to_le(pWav, pWav->fmt.channels); - runningPos += ma_dr_wav__write_u32ne_to_le(pWav, pWav->fmt.sampleRate); - runningPos += ma_dr_wav__write_u32ne_to_le(pWav, pWav->fmt.avgBytesPerSec); - runningPos += ma_dr_wav__write_u16ne_to_le(pWav, pWav->fmt.blockAlign); - runningPos += ma_dr_wav__write_u16ne_to_le(pWav, pWav->fmt.bitsPerSample); - if (!pWav->isSequentialWrite && pWav->pMetadata != NULL && pWav->metadataCount > 0 && (pFormat->container == ma_dr_wav_container_riff || pFormat->container == ma_dr_wav_container_rf64)) { - runningPos += ma_dr_wav__write_or_count_metadata(pWav, pWav->pMetadata, pWav->metadataCount); - } - pWav->dataChunkDataPos = runningPos; - if (pFormat->container == ma_dr_wav_container_riff) { - ma_uint32 chunkSizeDATA = (ma_uint32)initialDataChunkSize; - runningPos += ma_dr_wav__write(pWav, "data", 4); - runningPos += ma_dr_wav__write_u32ne_to_le(pWav, chunkSizeDATA); - } else if (pFormat->container == ma_dr_wav_container_w64) { - ma_uint64 chunkSizeDATA = 24 + initialDataChunkSize; - runningPos += ma_dr_wav__write(pWav, ma_dr_wavGUID_W64_DATA, 16); - runningPos += ma_dr_wav__write_u64ne_to_le(pWav, chunkSizeDATA); - } else if (pFormat->container == ma_dr_wav_container_rf64) { - runningPos += ma_dr_wav__write(pWav, "data", 4); - runningPos += ma_dr_wav__write_u32ne_to_le(pWav, 0xFFFFFFFF); - } - pWav->container = pFormat->container; - pWav->channels = (ma_uint16)pFormat->channels; - pWav->sampleRate = pFormat->sampleRate; - pWav->bitsPerSample = (ma_uint16)pFormat->bitsPerSample; - pWav->translatedFormatTag = (ma_uint16)pFormat->format; - pWav->dataChunkDataPos = runningPos; - return MA_TRUE; -} -MA_API ma_bool32 ma_dr_wav_init_write(ma_dr_wav* pWav, const ma_dr_wav_data_format* pFormat, ma_dr_wav_write_proc onWrite, ma_dr_wav_seek_proc onSeek, void* pUserData, const ma_allocation_callbacks* pAllocationCallbacks) -{ - if (!ma_dr_wav_preinit_write(pWav, pFormat, MA_FALSE, onWrite, onSeek, pUserData, pAllocationCallbacks)) { - return MA_FALSE; - } - return ma_dr_wav_init_write__internal(pWav, pFormat, 0); -} -MA_API ma_bool32 ma_dr_wav_init_write_sequential(ma_dr_wav* pWav, const ma_dr_wav_data_format* pFormat, ma_uint64 totalSampleCount, ma_dr_wav_write_proc onWrite, void* pUserData, const ma_allocation_callbacks* pAllocationCallbacks) -{ - if (!ma_dr_wav_preinit_write(pWav, pFormat, MA_TRUE, onWrite, NULL, pUserData, pAllocationCallbacks)) { - return MA_FALSE; - } - return ma_dr_wav_init_write__internal(pWav, pFormat, totalSampleCount); -} -MA_API ma_bool32 ma_dr_wav_init_write_sequential_pcm_frames(ma_dr_wav* pWav, const ma_dr_wav_data_format* pFormat, ma_uint64 totalPCMFrameCount, ma_dr_wav_write_proc onWrite, void* pUserData, const ma_allocation_callbacks* pAllocationCallbacks) -{ - if (pFormat == NULL) { - return MA_FALSE; - } - return ma_dr_wav_init_write_sequential(pWav, pFormat, totalPCMFrameCount*pFormat->channels, onWrite, pUserData, pAllocationCallbacks); -} -MA_API ma_bool32 ma_dr_wav_init_write_with_metadata(ma_dr_wav* pWav, const ma_dr_wav_data_format* pFormat, ma_dr_wav_write_proc onWrite, ma_dr_wav_seek_proc onSeek, void* pUserData, const ma_allocation_callbacks* pAllocationCallbacks, ma_dr_wav_metadata* pMetadata, ma_uint32 metadataCount) -{ - if (!ma_dr_wav_preinit_write(pWav, pFormat, MA_FALSE, onWrite, onSeek, pUserData, pAllocationCallbacks)) { - return MA_FALSE; - } - pWav->pMetadata = pMetadata; - pWav->metadataCount = metadataCount; - return ma_dr_wav_init_write__internal(pWav, pFormat, 0); -} -MA_API ma_uint64 ma_dr_wav_target_write_size_bytes(const ma_dr_wav_data_format* pFormat, ma_uint64 totalFrameCount, ma_dr_wav_metadata* pMetadata, ma_uint32 metadataCount) -{ - ma_uint64 targetDataSizeBytes = (ma_uint64)((ma_int64)totalFrameCount * pFormat->channels * pFormat->bitsPerSample/8.0); - ma_uint64 riffChunkSizeBytes; - ma_uint64 fileSizeBytes = 0; - if (pFormat->container == ma_dr_wav_container_riff) { - riffChunkSizeBytes = ma_dr_wav__riff_chunk_size_riff(targetDataSizeBytes, pMetadata, metadataCount); - fileSizeBytes = (8 + riffChunkSizeBytes); - } else if (pFormat->container == ma_dr_wav_container_w64) { - riffChunkSizeBytes = ma_dr_wav__riff_chunk_size_w64(targetDataSizeBytes); - fileSizeBytes = riffChunkSizeBytes; - } else if (pFormat->container == ma_dr_wav_container_rf64) { - riffChunkSizeBytes = ma_dr_wav__riff_chunk_size_rf64(targetDataSizeBytes, pMetadata, metadataCount); - fileSizeBytes = (8 + riffChunkSizeBytes); - } - return fileSizeBytes; -} -#ifndef MA_DR_WAV_NO_STDIO -MA_PRIVATE size_t ma_dr_wav__on_read_stdio(void* pUserData, void* pBufferOut, size_t bytesToRead) -{ - return fread(pBufferOut, 1, bytesToRead, (FILE*)pUserData); -} -MA_PRIVATE size_t ma_dr_wav__on_write_stdio(void* pUserData, const void* pData, size_t bytesToWrite) -{ - return fwrite(pData, 1, bytesToWrite, (FILE*)pUserData); -} -MA_PRIVATE ma_bool32 ma_dr_wav__on_seek_stdio(void* pUserData, int offset, ma_dr_wav_seek_origin origin) -{ - return fseek((FILE*)pUserData, offset, (origin == ma_dr_wav_seek_origin_current) ? SEEK_CUR : SEEK_SET) == 0; -} -MA_API ma_bool32 ma_dr_wav_init_file(ma_dr_wav* pWav, const char* filename, const ma_allocation_callbacks* pAllocationCallbacks) -{ - return ma_dr_wav_init_file_ex(pWav, filename, NULL, NULL, 0, pAllocationCallbacks); -} -MA_PRIVATE ma_bool32 ma_dr_wav_init_file__internal_FILE(ma_dr_wav* pWav, FILE* pFile, ma_dr_wav_chunk_proc onChunk, void* pChunkUserData, ma_uint32 flags, const ma_allocation_callbacks* pAllocationCallbacks) -{ - ma_bool32 result; - result = ma_dr_wav_preinit(pWav, ma_dr_wav__on_read_stdio, ma_dr_wav__on_seek_stdio, (void*)pFile, pAllocationCallbacks); - if (result != MA_TRUE) { - fclose(pFile); - return result; - } - result = ma_dr_wav_init__internal(pWav, onChunk, pChunkUserData, flags); - if (result != MA_TRUE) { - fclose(pFile); - return result; - } - return MA_TRUE; -} -MA_API ma_bool32 ma_dr_wav_init_file_ex(ma_dr_wav* pWav, const char* filename, ma_dr_wav_chunk_proc onChunk, void* pChunkUserData, ma_uint32 flags, const ma_allocation_callbacks* pAllocationCallbacks) -{ - FILE* pFile; - if (ma_fopen(&pFile, filename, "rb") != MA_SUCCESS) { - return MA_FALSE; - } - return ma_dr_wav_init_file__internal_FILE(pWav, pFile, onChunk, pChunkUserData, flags, pAllocationCallbacks); -} -#ifndef MA_DR_WAV_NO_WCHAR -MA_API ma_bool32 ma_dr_wav_init_file_w(ma_dr_wav* pWav, const wchar_t* filename, const ma_allocation_callbacks* pAllocationCallbacks) -{ - return ma_dr_wav_init_file_ex_w(pWav, filename, NULL, NULL, 0, pAllocationCallbacks); -} -MA_API ma_bool32 ma_dr_wav_init_file_ex_w(ma_dr_wav* pWav, const wchar_t* filename, ma_dr_wav_chunk_proc onChunk, void* pChunkUserData, ma_uint32 flags, const ma_allocation_callbacks* pAllocationCallbacks) -{ - FILE* pFile; - if (ma_wfopen(&pFile, filename, L"rb", pAllocationCallbacks) != MA_SUCCESS) { - return MA_FALSE; - } - return ma_dr_wav_init_file__internal_FILE(pWav, pFile, onChunk, pChunkUserData, flags, pAllocationCallbacks); -} -#endif -MA_API ma_bool32 ma_dr_wav_init_file_with_metadata(ma_dr_wav* pWav, const char* filename, ma_uint32 flags, const ma_allocation_callbacks* pAllocationCallbacks) -{ - FILE* pFile; - if (ma_fopen(&pFile, filename, "rb") != MA_SUCCESS) { - return MA_FALSE; - } - return ma_dr_wav_init_file__internal_FILE(pWav, pFile, NULL, NULL, flags | MA_DR_WAV_WITH_METADATA, pAllocationCallbacks); -} -#ifndef MA_DR_WAV_NO_WCHAR -MA_API ma_bool32 ma_dr_wav_init_file_with_metadata_w(ma_dr_wav* pWav, const wchar_t* filename, ma_uint32 flags, const ma_allocation_callbacks* pAllocationCallbacks) -{ - FILE* pFile; - if (ma_wfopen(&pFile, filename, L"rb", pAllocationCallbacks) != MA_SUCCESS) { - return MA_FALSE; - } - return ma_dr_wav_init_file__internal_FILE(pWav, pFile, NULL, NULL, flags | MA_DR_WAV_WITH_METADATA, pAllocationCallbacks); -} -#endif -MA_PRIVATE ma_bool32 ma_dr_wav_init_file_write__internal_FILE(ma_dr_wav* pWav, FILE* pFile, const ma_dr_wav_data_format* pFormat, ma_uint64 totalSampleCount, ma_bool32 isSequential, const ma_allocation_callbacks* pAllocationCallbacks) -{ - ma_bool32 result; - result = ma_dr_wav_preinit_write(pWav, pFormat, isSequential, ma_dr_wav__on_write_stdio, ma_dr_wav__on_seek_stdio, (void*)pFile, pAllocationCallbacks); - if (result != MA_TRUE) { - fclose(pFile); - return result; - } - result = ma_dr_wav_init_write__internal(pWav, pFormat, totalSampleCount); - if (result != MA_TRUE) { - fclose(pFile); - return result; - } - return MA_TRUE; -} -MA_PRIVATE ma_bool32 ma_dr_wav_init_file_write__internal(ma_dr_wav* pWav, const char* filename, const ma_dr_wav_data_format* pFormat, ma_uint64 totalSampleCount, ma_bool32 isSequential, const ma_allocation_callbacks* pAllocationCallbacks) -{ - FILE* pFile; - if (ma_fopen(&pFile, filename, "wb") != MA_SUCCESS) { - return MA_FALSE; - } - return ma_dr_wav_init_file_write__internal_FILE(pWav, pFile, pFormat, totalSampleCount, isSequential, pAllocationCallbacks); -} -#ifndef MA_DR_WAV_NO_WCHAR -MA_PRIVATE ma_bool32 ma_dr_wav_init_file_write_w__internal(ma_dr_wav* pWav, const wchar_t* filename, const ma_dr_wav_data_format* pFormat, ma_uint64 totalSampleCount, ma_bool32 isSequential, const ma_allocation_callbacks* pAllocationCallbacks) -{ - FILE* pFile; - if (ma_wfopen(&pFile, filename, L"wb", pAllocationCallbacks) != MA_SUCCESS) { - return MA_FALSE; - } - return ma_dr_wav_init_file_write__internal_FILE(pWav, pFile, pFormat, totalSampleCount, isSequential, pAllocationCallbacks); -} -#endif -MA_API ma_bool32 ma_dr_wav_init_file_write(ma_dr_wav* pWav, const char* filename, const ma_dr_wav_data_format* pFormat, const ma_allocation_callbacks* pAllocationCallbacks) -{ - return ma_dr_wav_init_file_write__internal(pWav, filename, pFormat, 0, MA_FALSE, pAllocationCallbacks); -} -MA_API ma_bool32 ma_dr_wav_init_file_write_sequential(ma_dr_wav* pWav, const char* filename, const ma_dr_wav_data_format* pFormat, ma_uint64 totalSampleCount, const ma_allocation_callbacks* pAllocationCallbacks) -{ - return ma_dr_wav_init_file_write__internal(pWav, filename, pFormat, totalSampleCount, MA_TRUE, pAllocationCallbacks); -} -MA_API ma_bool32 ma_dr_wav_init_file_write_sequential_pcm_frames(ma_dr_wav* pWav, const char* filename, const ma_dr_wav_data_format* pFormat, ma_uint64 totalPCMFrameCount, const ma_allocation_callbacks* pAllocationCallbacks) -{ - if (pFormat == NULL) { - return MA_FALSE; - } - return ma_dr_wav_init_file_write_sequential(pWav, filename, pFormat, totalPCMFrameCount*pFormat->channels, pAllocationCallbacks); -} -#ifndef MA_DR_WAV_NO_WCHAR -MA_API ma_bool32 ma_dr_wav_init_file_write_w(ma_dr_wav* pWav, const wchar_t* filename, const ma_dr_wav_data_format* pFormat, const ma_allocation_callbacks* pAllocationCallbacks) -{ - return ma_dr_wav_init_file_write_w__internal(pWav, filename, pFormat, 0, MA_FALSE, pAllocationCallbacks); -} -MA_API ma_bool32 ma_dr_wav_init_file_write_sequential_w(ma_dr_wav* pWav, const wchar_t* filename, const ma_dr_wav_data_format* pFormat, ma_uint64 totalSampleCount, const ma_allocation_callbacks* pAllocationCallbacks) -{ - return ma_dr_wav_init_file_write_w__internal(pWav, filename, pFormat, totalSampleCount, MA_TRUE, pAllocationCallbacks); -} -MA_API ma_bool32 ma_dr_wav_init_file_write_sequential_pcm_frames_w(ma_dr_wav* pWav, const wchar_t* filename, const ma_dr_wav_data_format* pFormat, ma_uint64 totalPCMFrameCount, const ma_allocation_callbacks* pAllocationCallbacks) -{ - if (pFormat == NULL) { - return MA_FALSE; - } - return ma_dr_wav_init_file_write_sequential_w(pWav, filename, pFormat, totalPCMFrameCount*pFormat->channels, pAllocationCallbacks); -} -#endif -#endif -MA_PRIVATE size_t ma_dr_wav__on_read_memory(void* pUserData, void* pBufferOut, size_t bytesToRead) -{ - ma_dr_wav* pWav = (ma_dr_wav*)pUserData; - size_t bytesRemaining; - MA_DR_WAV_ASSERT(pWav != NULL); - MA_DR_WAV_ASSERT(pWav->memoryStream.dataSize >= pWav->memoryStream.currentReadPos); - bytesRemaining = pWav->memoryStream.dataSize - pWav->memoryStream.currentReadPos; - if (bytesToRead > bytesRemaining) { - bytesToRead = bytesRemaining; - } - if (bytesToRead > 0) { - MA_DR_WAV_COPY_MEMORY(pBufferOut, pWav->memoryStream.data + pWav->memoryStream.currentReadPos, bytesToRead); - pWav->memoryStream.currentReadPos += bytesToRead; - } - return bytesToRead; -} -MA_PRIVATE ma_bool32 ma_dr_wav__on_seek_memory(void* pUserData, int offset, ma_dr_wav_seek_origin origin) -{ - ma_dr_wav* pWav = (ma_dr_wav*)pUserData; - MA_DR_WAV_ASSERT(pWav != NULL); - if (origin == ma_dr_wav_seek_origin_current) { - if (offset > 0) { - if (pWav->memoryStream.currentReadPos + offset > pWav->memoryStream.dataSize) { - return MA_FALSE; - } - } else { - if (pWav->memoryStream.currentReadPos < (size_t)-offset) { - return MA_FALSE; - } - } - pWav->memoryStream.currentReadPos += offset; - } else { - if ((ma_uint32)offset <= pWav->memoryStream.dataSize) { - pWav->memoryStream.currentReadPos = offset; - } else { - return MA_FALSE; - } - } - return MA_TRUE; -} -MA_PRIVATE size_t ma_dr_wav__on_write_memory(void* pUserData, const void* pDataIn, size_t bytesToWrite) -{ - ma_dr_wav* pWav = (ma_dr_wav*)pUserData; - size_t bytesRemaining; - MA_DR_WAV_ASSERT(pWav != NULL); - MA_DR_WAV_ASSERT(pWav->memoryStreamWrite.dataCapacity >= pWav->memoryStreamWrite.currentWritePos); - bytesRemaining = pWav->memoryStreamWrite.dataCapacity - pWav->memoryStreamWrite.currentWritePos; - if (bytesRemaining < bytesToWrite) { - void* pNewData; - size_t newDataCapacity = (pWav->memoryStreamWrite.dataCapacity == 0) ? 256 : pWav->memoryStreamWrite.dataCapacity * 2; - if ((newDataCapacity - pWav->memoryStreamWrite.currentWritePos) < bytesToWrite) { - newDataCapacity = pWav->memoryStreamWrite.currentWritePos + bytesToWrite; - } - pNewData = ma_dr_wav__realloc_from_callbacks(*pWav->memoryStreamWrite.ppData, newDataCapacity, pWav->memoryStreamWrite.dataCapacity, &pWav->allocationCallbacks); - if (pNewData == NULL) { - return 0; - } - *pWav->memoryStreamWrite.ppData = pNewData; - pWav->memoryStreamWrite.dataCapacity = newDataCapacity; - } - MA_DR_WAV_COPY_MEMORY(((ma_uint8*)(*pWav->memoryStreamWrite.ppData)) + pWav->memoryStreamWrite.currentWritePos, pDataIn, bytesToWrite); - pWav->memoryStreamWrite.currentWritePos += bytesToWrite; - if (pWav->memoryStreamWrite.dataSize < pWav->memoryStreamWrite.currentWritePos) { - pWav->memoryStreamWrite.dataSize = pWav->memoryStreamWrite.currentWritePos; - } - *pWav->memoryStreamWrite.pDataSize = pWav->memoryStreamWrite.dataSize; - return bytesToWrite; -} -MA_PRIVATE ma_bool32 ma_dr_wav__on_seek_memory_write(void* pUserData, int offset, ma_dr_wav_seek_origin origin) -{ - ma_dr_wav* pWav = (ma_dr_wav*)pUserData; - MA_DR_WAV_ASSERT(pWav != NULL); - if (origin == ma_dr_wav_seek_origin_current) { - if (offset > 0) { - if (pWav->memoryStreamWrite.currentWritePos + offset > pWav->memoryStreamWrite.dataSize) { - offset = (int)(pWav->memoryStreamWrite.dataSize - pWav->memoryStreamWrite.currentWritePos); - } - } else { - if (pWav->memoryStreamWrite.currentWritePos < (size_t)-offset) { - offset = -(int)pWav->memoryStreamWrite.currentWritePos; - } - } - pWav->memoryStreamWrite.currentWritePos += offset; - } else { - if ((ma_uint32)offset <= pWav->memoryStreamWrite.dataSize) { - pWav->memoryStreamWrite.currentWritePos = offset; - } else { - pWav->memoryStreamWrite.currentWritePos = pWav->memoryStreamWrite.dataSize; - } - } - return MA_TRUE; -} -MA_API ma_bool32 ma_dr_wav_init_memory(ma_dr_wav* pWav, const void* data, size_t dataSize, const ma_allocation_callbacks* pAllocationCallbacks) -{ - return ma_dr_wav_init_memory_ex(pWav, data, dataSize, NULL, NULL, 0, pAllocationCallbacks); -} -MA_API ma_bool32 ma_dr_wav_init_memory_ex(ma_dr_wav* pWav, const void* data, size_t dataSize, ma_dr_wav_chunk_proc onChunk, void* pChunkUserData, ma_uint32 flags, const ma_allocation_callbacks* pAllocationCallbacks) -{ - if (data == NULL || dataSize == 0) { - return MA_FALSE; - } - if (!ma_dr_wav_preinit(pWav, ma_dr_wav__on_read_memory, ma_dr_wav__on_seek_memory, pWav, pAllocationCallbacks)) { - return MA_FALSE; - } - pWav->memoryStream.data = (const ma_uint8*)data; - pWav->memoryStream.dataSize = dataSize; - pWav->memoryStream.currentReadPos = 0; - return ma_dr_wav_init__internal(pWav, onChunk, pChunkUserData, flags); -} -MA_API ma_bool32 ma_dr_wav_init_memory_with_metadata(ma_dr_wav* pWav, const void* data, size_t dataSize, ma_uint32 flags, const ma_allocation_callbacks* pAllocationCallbacks) -{ - if (data == NULL || dataSize == 0) { - return MA_FALSE; - } - if (!ma_dr_wav_preinit(pWav, ma_dr_wav__on_read_memory, ma_dr_wav__on_seek_memory, pWav, pAllocationCallbacks)) { - return MA_FALSE; - } - pWav->memoryStream.data = (const ma_uint8*)data; - pWav->memoryStream.dataSize = dataSize; - pWav->memoryStream.currentReadPos = 0; - return ma_dr_wav_init__internal(pWav, NULL, NULL, flags | MA_DR_WAV_WITH_METADATA); -} -MA_PRIVATE ma_bool32 ma_dr_wav_init_memory_write__internal(ma_dr_wav* pWav, void** ppData, size_t* pDataSize, const ma_dr_wav_data_format* pFormat, ma_uint64 totalSampleCount, ma_bool32 isSequential, const ma_allocation_callbacks* pAllocationCallbacks) -{ - if (ppData == NULL || pDataSize == NULL) { - return MA_FALSE; - } - *ppData = NULL; - *pDataSize = 0; - if (!ma_dr_wav_preinit_write(pWav, pFormat, isSequential, ma_dr_wav__on_write_memory, ma_dr_wav__on_seek_memory_write, pWav, pAllocationCallbacks)) { - return MA_FALSE; - } - pWav->memoryStreamWrite.ppData = ppData; - pWav->memoryStreamWrite.pDataSize = pDataSize; - pWav->memoryStreamWrite.dataSize = 0; - pWav->memoryStreamWrite.dataCapacity = 0; - pWav->memoryStreamWrite.currentWritePos = 0; - return ma_dr_wav_init_write__internal(pWav, pFormat, totalSampleCount); -} -MA_API ma_bool32 ma_dr_wav_init_memory_write(ma_dr_wav* pWav, void** ppData, size_t* pDataSize, const ma_dr_wav_data_format* pFormat, const ma_allocation_callbacks* pAllocationCallbacks) -{ - return ma_dr_wav_init_memory_write__internal(pWav, ppData, pDataSize, pFormat, 0, MA_FALSE, pAllocationCallbacks); -} -MA_API ma_bool32 ma_dr_wav_init_memory_write_sequential(ma_dr_wav* pWav, void** ppData, size_t* pDataSize, const ma_dr_wav_data_format* pFormat, ma_uint64 totalSampleCount, const ma_allocation_callbacks* pAllocationCallbacks) -{ - return ma_dr_wav_init_memory_write__internal(pWav, ppData, pDataSize, pFormat, totalSampleCount, MA_TRUE, pAllocationCallbacks); -} -MA_API ma_bool32 ma_dr_wav_init_memory_write_sequential_pcm_frames(ma_dr_wav* pWav, void** ppData, size_t* pDataSize, const ma_dr_wav_data_format* pFormat, ma_uint64 totalPCMFrameCount, const ma_allocation_callbacks* pAllocationCallbacks) -{ - if (pFormat == NULL) { - return MA_FALSE; - } - return ma_dr_wav_init_memory_write_sequential(pWav, ppData, pDataSize, pFormat, totalPCMFrameCount*pFormat->channels, pAllocationCallbacks); -} -MA_API ma_result ma_dr_wav_uninit(ma_dr_wav* pWav) -{ - ma_result result = MA_SUCCESS; - if (pWav == NULL) { - return MA_INVALID_ARGS; - } - if (pWav->onWrite != NULL) { - ma_uint32 paddingSize = 0; - if (pWav->container == ma_dr_wav_container_riff || pWav->container == ma_dr_wav_container_rf64) { - paddingSize = ma_dr_wav__chunk_padding_size_riff(pWav->dataChunkDataSize); - } else { - paddingSize = ma_dr_wav__chunk_padding_size_w64(pWav->dataChunkDataSize); - } - if (paddingSize > 0) { - ma_uint64 paddingData = 0; - ma_dr_wav__write(pWav, &paddingData, paddingSize); - } - if (pWav->onSeek && !pWav->isSequentialWrite) { - if (pWav->container == ma_dr_wav_container_riff) { - if (pWav->onSeek(pWav->pUserData, 4, ma_dr_wav_seek_origin_start)) { - ma_uint32 riffChunkSize = ma_dr_wav__riff_chunk_size_riff(pWav->dataChunkDataSize, pWav->pMetadata, pWav->metadataCount); - ma_dr_wav__write_u32ne_to_le(pWav, riffChunkSize); - } - if (pWav->onSeek(pWav->pUserData, (int)pWav->dataChunkDataPos - 4, ma_dr_wav_seek_origin_start)) { - ma_uint32 dataChunkSize = ma_dr_wav__data_chunk_size_riff(pWav->dataChunkDataSize); - ma_dr_wav__write_u32ne_to_le(pWav, dataChunkSize); - } - } else if (pWav->container == ma_dr_wav_container_w64) { - if (pWav->onSeek(pWav->pUserData, 16, ma_dr_wav_seek_origin_start)) { - ma_uint64 riffChunkSize = ma_dr_wav__riff_chunk_size_w64(pWav->dataChunkDataSize); - ma_dr_wav__write_u64ne_to_le(pWav, riffChunkSize); - } - if (pWav->onSeek(pWav->pUserData, (int)pWav->dataChunkDataPos - 8, ma_dr_wav_seek_origin_start)) { - ma_uint64 dataChunkSize = ma_dr_wav__data_chunk_size_w64(pWav->dataChunkDataSize); - ma_dr_wav__write_u64ne_to_le(pWav, dataChunkSize); - } - } else if (pWav->container == ma_dr_wav_container_rf64) { - int ds64BodyPos = 12 + 8; - if (pWav->onSeek(pWav->pUserData, ds64BodyPos + 0, ma_dr_wav_seek_origin_start)) { - ma_uint64 riffChunkSize = ma_dr_wav__riff_chunk_size_rf64(pWav->dataChunkDataSize, pWav->pMetadata, pWav->metadataCount); - ma_dr_wav__write_u64ne_to_le(pWav, riffChunkSize); - } - if (pWav->onSeek(pWav->pUserData, ds64BodyPos + 8, ma_dr_wav_seek_origin_start)) { - ma_uint64 dataChunkSize = ma_dr_wav__data_chunk_size_rf64(pWav->dataChunkDataSize); - ma_dr_wav__write_u64ne_to_le(pWav, dataChunkSize); - } - } - } - if (pWav->isSequentialWrite) { - if (pWav->dataChunkDataSize != pWav->dataChunkDataSizeTargetWrite) { - result = MA_INVALID_FILE; - } - } - } else { - ma_dr_wav_free(pWav->pMetadata, &pWav->allocationCallbacks); - } -#ifndef MA_DR_WAV_NO_STDIO - if (pWav->onRead == ma_dr_wav__on_read_stdio || pWav->onWrite == ma_dr_wav__on_write_stdio) { - fclose((FILE*)pWav->pUserData); - } -#endif - return result; -} -MA_API size_t ma_dr_wav_read_raw(ma_dr_wav* pWav, size_t bytesToRead, void* pBufferOut) -{ - size_t bytesRead; - ma_uint32 bytesPerFrame; - if (pWav == NULL || bytesToRead == 0) { - return 0; - } - if (bytesToRead > pWav->bytesRemaining) { - bytesToRead = (size_t)pWav->bytesRemaining; - } - if (bytesToRead == 0) { - return 0; - } - bytesPerFrame = ma_dr_wav_get_bytes_per_pcm_frame(pWav); - if (bytesPerFrame == 0) { - return 0; - } - if (pBufferOut != NULL) { - bytesRead = pWav->onRead(pWav->pUserData, pBufferOut, bytesToRead); - } else { - bytesRead = 0; - while (bytesRead < bytesToRead) { - size_t bytesToSeek = (bytesToRead - bytesRead); - if (bytesToSeek > 0x7FFFFFFF) { - bytesToSeek = 0x7FFFFFFF; - } - if (pWav->onSeek(pWav->pUserData, (int)bytesToSeek, ma_dr_wav_seek_origin_current) == MA_FALSE) { - break; - } - bytesRead += bytesToSeek; - } - while (bytesRead < bytesToRead) { - ma_uint8 buffer[4096]; - size_t bytesSeeked; - size_t bytesToSeek = (bytesToRead - bytesRead); - if (bytesToSeek > sizeof(buffer)) { - bytesToSeek = sizeof(buffer); - } - bytesSeeked = pWav->onRead(pWav->pUserData, buffer, bytesToSeek); - bytesRead += bytesSeeked; - if (bytesSeeked < bytesToSeek) { - break; - } - } - } - pWav->readCursorInPCMFrames += bytesRead / bytesPerFrame; - pWav->bytesRemaining -= bytesRead; - return bytesRead; -} -MA_API ma_uint64 ma_dr_wav_read_pcm_frames_le(ma_dr_wav* pWav, ma_uint64 framesToRead, void* pBufferOut) -{ - ma_uint32 bytesPerFrame; - ma_uint64 bytesToRead; - ma_uint64 framesRemainingInFile; - if (pWav == NULL || framesToRead == 0) { - return 0; - } - if (ma_dr_wav__is_compressed_format_tag(pWav->translatedFormatTag)) { - return 0; - } - framesRemainingInFile = pWav->totalPCMFrameCount - pWav->readCursorInPCMFrames; - if (framesToRead > framesRemainingInFile) { - framesToRead = framesRemainingInFile; - } - bytesPerFrame = ma_dr_wav_get_bytes_per_pcm_frame(pWav); - if (bytesPerFrame == 0) { - return 0; - } - bytesToRead = framesToRead * bytesPerFrame; - if (bytesToRead > MA_SIZE_MAX) { - bytesToRead = (MA_SIZE_MAX / bytesPerFrame) * bytesPerFrame; - } - if (bytesToRead == 0) { - return 0; - } - return ma_dr_wav_read_raw(pWav, (size_t)bytesToRead, pBufferOut) / bytesPerFrame; -} -MA_API ma_uint64 ma_dr_wav_read_pcm_frames_be(ma_dr_wav* pWav, ma_uint64 framesToRead, void* pBufferOut) -{ - ma_uint64 framesRead = ma_dr_wav_read_pcm_frames_le(pWav, framesToRead, pBufferOut); - if (pBufferOut != NULL) { - ma_uint32 bytesPerFrame = ma_dr_wav_get_bytes_per_pcm_frame(pWav); - if (bytesPerFrame == 0) { - return 0; - } - ma_dr_wav__bswap_samples(pBufferOut, framesRead*pWav->channels, bytesPerFrame/pWav->channels); - } - return framesRead; -} -MA_API ma_uint64 ma_dr_wav_read_pcm_frames(ma_dr_wav* pWav, ma_uint64 framesToRead, void* pBufferOut) -{ - ma_uint64 framesRead = 0; - if (ma_dr_wav_is_container_be(pWav->container)) { - if (pWav->container != ma_dr_wav_container_aiff || pWav->aiff.isLE == MA_FALSE) { - if (ma_dr_wav__is_little_endian()) { - framesRead = ma_dr_wav_read_pcm_frames_be(pWav, framesToRead, pBufferOut); - } else { - framesRead = ma_dr_wav_read_pcm_frames_le(pWav, framesToRead, pBufferOut); - } - goto post_process; - } - } - if (ma_dr_wav__is_little_endian()) { - framesRead = ma_dr_wav_read_pcm_frames_le(pWav, framesToRead, pBufferOut); - } else { - framesRead = ma_dr_wav_read_pcm_frames_be(pWav, framesToRead, pBufferOut); - } - post_process: - { - if (pWav->container == ma_dr_wav_container_aiff && pWav->bitsPerSample == 8 && pWav->aiff.isUnsigned == MA_FALSE) { - if (pBufferOut != NULL) { - ma_uint64 iSample; - for (iSample = 0; iSample < framesRead * pWav->channels; iSample += 1) { - ((ma_uint8*)pBufferOut)[iSample] += 128; - } - } - } - } - return framesRead; -} -MA_PRIVATE ma_bool32 ma_dr_wav_seek_to_first_pcm_frame(ma_dr_wav* pWav) -{ - if (pWav->onWrite != NULL) { - return MA_FALSE; - } - if (!pWav->onSeek(pWav->pUserData, (int)pWav->dataChunkDataPos, ma_dr_wav_seek_origin_start)) { - return MA_FALSE; - } - if (ma_dr_wav__is_compressed_format_tag(pWav->translatedFormatTag)) { - if (pWav->translatedFormatTag == MA_DR_WAVE_FORMAT_ADPCM) { - MA_DR_WAV_ZERO_OBJECT(&pWav->msadpcm); - } else if (pWav->translatedFormatTag == MA_DR_WAVE_FORMAT_DVI_ADPCM) { - MA_DR_WAV_ZERO_OBJECT(&pWav->ima); - } else { - MA_DR_WAV_ASSERT(MA_FALSE); - } - } - pWav->readCursorInPCMFrames = 0; - pWav->bytesRemaining = pWav->dataChunkDataSize; - return MA_TRUE; -} -MA_API ma_bool32 ma_dr_wav_seek_to_pcm_frame(ma_dr_wav* pWav, ma_uint64 targetFrameIndex) -{ - if (pWav == NULL || pWav->onSeek == NULL) { - return MA_FALSE; - } - if (pWav->onWrite != NULL) { - return MA_FALSE; - } - if (pWav->totalPCMFrameCount == 0) { - return MA_TRUE; - } - if (targetFrameIndex > pWav->totalPCMFrameCount) { - targetFrameIndex = pWav->totalPCMFrameCount; - } - if (ma_dr_wav__is_compressed_format_tag(pWav->translatedFormatTag)) { - if (targetFrameIndex < pWav->readCursorInPCMFrames) { - if (!ma_dr_wav_seek_to_first_pcm_frame(pWav)) { - return MA_FALSE; - } - } - if (targetFrameIndex > pWav->readCursorInPCMFrames) { - ma_uint64 offsetInFrames = targetFrameIndex - pWav->readCursorInPCMFrames; - ma_int16 devnull[2048]; - while (offsetInFrames > 0) { - ma_uint64 framesRead = 0; - ma_uint64 framesToRead = offsetInFrames; - if (framesToRead > ma_dr_wav_countof(devnull)/pWav->channels) { - framesToRead = ma_dr_wav_countof(devnull)/pWav->channels; - } - if (pWav->translatedFormatTag == MA_DR_WAVE_FORMAT_ADPCM) { - framesRead = ma_dr_wav_read_pcm_frames_s16__msadpcm(pWav, framesToRead, devnull); - } else if (pWav->translatedFormatTag == MA_DR_WAVE_FORMAT_DVI_ADPCM) { - framesRead = ma_dr_wav_read_pcm_frames_s16__ima(pWav, framesToRead, devnull); - } else { - MA_DR_WAV_ASSERT(MA_FALSE); - } - if (framesRead != framesToRead) { - return MA_FALSE; - } - offsetInFrames -= framesRead; - } - } - } else { - ma_uint64 totalSizeInBytes; - ma_uint64 currentBytePos; - ma_uint64 targetBytePos; - ma_uint64 offset; - ma_uint32 bytesPerFrame; - bytesPerFrame = ma_dr_wav_get_bytes_per_pcm_frame(pWav); - if (bytesPerFrame == 0) { - return MA_FALSE; - } - totalSizeInBytes = pWav->totalPCMFrameCount * bytesPerFrame; - currentBytePos = totalSizeInBytes - pWav->bytesRemaining; - targetBytePos = targetFrameIndex * bytesPerFrame; - if (currentBytePos < targetBytePos) { - offset = (targetBytePos - currentBytePos); - } else { - if (!ma_dr_wav_seek_to_first_pcm_frame(pWav)) { - return MA_FALSE; - } - offset = targetBytePos; - } - while (offset > 0) { - int offset32 = ((offset > INT_MAX) ? INT_MAX : (int)offset); - if (!pWav->onSeek(pWav->pUserData, offset32, ma_dr_wav_seek_origin_current)) { - return MA_FALSE; - } - pWav->readCursorInPCMFrames += offset32 / bytesPerFrame; - pWav->bytesRemaining -= offset32; - offset -= offset32; - } - } - return MA_TRUE; -} -MA_API ma_result ma_dr_wav_get_cursor_in_pcm_frames(ma_dr_wav* pWav, ma_uint64* pCursor) -{ - if (pCursor == NULL) { - return MA_INVALID_ARGS; - } - *pCursor = 0; - if (pWav == NULL) { - return MA_INVALID_ARGS; - } - *pCursor = pWav->readCursorInPCMFrames; - return MA_SUCCESS; -} -MA_API ma_result ma_dr_wav_get_length_in_pcm_frames(ma_dr_wav* pWav, ma_uint64* pLength) -{ - if (pLength == NULL) { - return MA_INVALID_ARGS; - } - *pLength = 0; - if (pWav == NULL) { - return MA_INVALID_ARGS; - } - *pLength = pWav->totalPCMFrameCount; - return MA_SUCCESS; -} -MA_API size_t ma_dr_wav_write_raw(ma_dr_wav* pWav, size_t bytesToWrite, const void* pData) -{ - size_t bytesWritten; - if (pWav == NULL || bytesToWrite == 0 || pData == NULL) { - return 0; - } - bytesWritten = pWav->onWrite(pWav->pUserData, pData, bytesToWrite); - pWav->dataChunkDataSize += bytesWritten; - return bytesWritten; -} -MA_API ma_uint64 ma_dr_wav_write_pcm_frames_le(ma_dr_wav* pWav, ma_uint64 framesToWrite, const void* pData) -{ - ma_uint64 bytesToWrite; - ma_uint64 bytesWritten; - const ma_uint8* pRunningData; - if (pWav == NULL || framesToWrite == 0 || pData == NULL) { - return 0; - } - bytesToWrite = ((framesToWrite * pWav->channels * pWav->bitsPerSample) / 8); - if (bytesToWrite > MA_SIZE_MAX) { - return 0; - } - bytesWritten = 0; - pRunningData = (const ma_uint8*)pData; - while (bytesToWrite > 0) { - size_t bytesJustWritten; - ma_uint64 bytesToWriteThisIteration; - bytesToWriteThisIteration = bytesToWrite; - MA_DR_WAV_ASSERT(bytesToWriteThisIteration <= MA_SIZE_MAX); - bytesJustWritten = ma_dr_wav_write_raw(pWav, (size_t)bytesToWriteThisIteration, pRunningData); - if (bytesJustWritten == 0) { - break; - } - bytesToWrite -= bytesJustWritten; - bytesWritten += bytesJustWritten; - pRunningData += bytesJustWritten; - } - return (bytesWritten * 8) / pWav->bitsPerSample / pWav->channels; -} -MA_API ma_uint64 ma_dr_wav_write_pcm_frames_be(ma_dr_wav* pWav, ma_uint64 framesToWrite, const void* pData) -{ - ma_uint64 bytesToWrite; - ma_uint64 bytesWritten; - ma_uint32 bytesPerSample; - const ma_uint8* pRunningData; - if (pWav == NULL || framesToWrite == 0 || pData == NULL) { - return 0; - } - bytesToWrite = ((framesToWrite * pWav->channels * pWav->bitsPerSample) / 8); - if (bytesToWrite > MA_SIZE_MAX) { - return 0; - } - bytesWritten = 0; - pRunningData = (const ma_uint8*)pData; - bytesPerSample = ma_dr_wav_get_bytes_per_pcm_frame(pWav) / pWav->channels; - if (bytesPerSample == 0) { - return 0; - } - while (bytesToWrite > 0) { - ma_uint8 temp[4096]; - ma_uint32 sampleCount; - size_t bytesJustWritten; - ma_uint64 bytesToWriteThisIteration; - bytesToWriteThisIteration = bytesToWrite; - MA_DR_WAV_ASSERT(bytesToWriteThisIteration <= MA_SIZE_MAX); - sampleCount = sizeof(temp)/bytesPerSample; - if (bytesToWriteThisIteration > ((ma_uint64)sampleCount)*bytesPerSample) { - bytesToWriteThisIteration = ((ma_uint64)sampleCount)*bytesPerSample; - } - MA_DR_WAV_COPY_MEMORY(temp, pRunningData, (size_t)bytesToWriteThisIteration); - ma_dr_wav__bswap_samples(temp, sampleCount, bytesPerSample); - bytesJustWritten = ma_dr_wav_write_raw(pWav, (size_t)bytesToWriteThisIteration, temp); - if (bytesJustWritten == 0) { - break; - } - bytesToWrite -= bytesJustWritten; - bytesWritten += bytesJustWritten; - pRunningData += bytesJustWritten; - } - return (bytesWritten * 8) / pWav->bitsPerSample / pWav->channels; -} -MA_API ma_uint64 ma_dr_wav_write_pcm_frames(ma_dr_wav* pWav, ma_uint64 framesToWrite, const void* pData) -{ - if (ma_dr_wav__is_little_endian()) { - return ma_dr_wav_write_pcm_frames_le(pWav, framesToWrite, pData); - } else { - return ma_dr_wav_write_pcm_frames_be(pWav, framesToWrite, pData); - } -} -MA_PRIVATE ma_uint64 ma_dr_wav_read_pcm_frames_s16__msadpcm(ma_dr_wav* pWav, ma_uint64 framesToRead, ma_int16* pBufferOut) -{ - ma_uint64 totalFramesRead = 0; - static ma_int32 adaptationTable[] = { - 230, 230, 230, 230, 307, 409, 512, 614, - 768, 614, 512, 409, 307, 230, 230, 230 - }; - static ma_int32 coeff1Table[] = { 256, 512, 0, 192, 240, 460, 392 }; - static ma_int32 coeff2Table[] = { 0, -256, 0, 64, 0, -208, -232 }; - MA_DR_WAV_ASSERT(pWav != NULL); - MA_DR_WAV_ASSERT(framesToRead > 0); - while (pWav->readCursorInPCMFrames < pWav->totalPCMFrameCount) { - MA_DR_WAV_ASSERT(framesToRead > 0); - if (pWav->msadpcm.cachedFrameCount == 0 && pWav->msadpcm.bytesRemainingInBlock == 0) { - if (pWav->channels == 1) { - ma_uint8 header[7]; - if (pWav->onRead(pWav->pUserData, header, sizeof(header)) != sizeof(header)) { - return totalFramesRead; - } - pWav->msadpcm.bytesRemainingInBlock = pWav->fmt.blockAlign - sizeof(header); - pWav->msadpcm.predictor[0] = header[0]; - pWav->msadpcm.delta[0] = ma_dr_wav_bytes_to_s16(header + 1); - pWav->msadpcm.prevFrames[0][1] = (ma_int32)ma_dr_wav_bytes_to_s16(header + 3); - pWav->msadpcm.prevFrames[0][0] = (ma_int32)ma_dr_wav_bytes_to_s16(header + 5); - pWav->msadpcm.cachedFrames[2] = pWav->msadpcm.prevFrames[0][0]; - pWav->msadpcm.cachedFrames[3] = pWav->msadpcm.prevFrames[0][1]; - pWav->msadpcm.cachedFrameCount = 2; - if (pWav->msadpcm.predictor[0] >= ma_dr_wav_countof(coeff1Table)) { - return totalFramesRead; - } - } else { - ma_uint8 header[14]; - if (pWav->onRead(pWav->pUserData, header, sizeof(header)) != sizeof(header)) { - return totalFramesRead; - } - pWav->msadpcm.bytesRemainingInBlock = pWav->fmt.blockAlign - sizeof(header); - pWav->msadpcm.predictor[0] = header[0]; - pWav->msadpcm.predictor[1] = header[1]; - pWav->msadpcm.delta[0] = ma_dr_wav_bytes_to_s16(header + 2); - pWav->msadpcm.delta[1] = ma_dr_wav_bytes_to_s16(header + 4); - pWav->msadpcm.prevFrames[0][1] = (ma_int32)ma_dr_wav_bytes_to_s16(header + 6); - pWav->msadpcm.prevFrames[1][1] = (ma_int32)ma_dr_wav_bytes_to_s16(header + 8); - pWav->msadpcm.prevFrames[0][0] = (ma_int32)ma_dr_wav_bytes_to_s16(header + 10); - pWav->msadpcm.prevFrames[1][0] = (ma_int32)ma_dr_wav_bytes_to_s16(header + 12); - pWav->msadpcm.cachedFrames[0] = pWav->msadpcm.prevFrames[0][0]; - pWav->msadpcm.cachedFrames[1] = pWav->msadpcm.prevFrames[1][0]; - pWav->msadpcm.cachedFrames[2] = pWav->msadpcm.prevFrames[0][1]; - pWav->msadpcm.cachedFrames[3] = pWav->msadpcm.prevFrames[1][1]; - pWav->msadpcm.cachedFrameCount = 2; - if (pWav->msadpcm.predictor[0] >= ma_dr_wav_countof(coeff1Table) || pWav->msadpcm.predictor[1] >= ma_dr_wav_countof(coeff2Table)) { - return totalFramesRead; - } - } - } - while (framesToRead > 0 && pWav->msadpcm.cachedFrameCount > 0 && pWav->readCursorInPCMFrames < pWav->totalPCMFrameCount) { - if (pBufferOut != NULL) { - ma_uint32 iSample = 0; - for (iSample = 0; iSample < pWav->channels; iSample += 1) { - pBufferOut[iSample] = (ma_int16)pWav->msadpcm.cachedFrames[(ma_dr_wav_countof(pWav->msadpcm.cachedFrames) - (pWav->msadpcm.cachedFrameCount*pWav->channels)) + iSample]; - } - pBufferOut += pWav->channels; - } - framesToRead -= 1; - totalFramesRead += 1; - pWav->readCursorInPCMFrames += 1; - pWav->msadpcm.cachedFrameCount -= 1; - } - if (framesToRead == 0) { - break; - } - if (pWav->msadpcm.cachedFrameCount == 0) { - if (pWav->msadpcm.bytesRemainingInBlock == 0) { - continue; - } else { - ma_uint8 nibbles; - ma_int32 nibble0; - ma_int32 nibble1; - if (pWav->onRead(pWav->pUserData, &nibbles, 1) != 1) { - return totalFramesRead; - } - pWav->msadpcm.bytesRemainingInBlock -= 1; - nibble0 = ((nibbles & 0xF0) >> 4); if ((nibbles & 0x80)) { nibble0 |= 0xFFFFFFF0UL; } - nibble1 = ((nibbles & 0x0F) >> 0); if ((nibbles & 0x08)) { nibble1 |= 0xFFFFFFF0UL; } - if (pWav->channels == 1) { - ma_int32 newSample0; - ma_int32 newSample1; - newSample0 = ((pWav->msadpcm.prevFrames[0][1] * coeff1Table[pWav->msadpcm.predictor[0]]) + (pWav->msadpcm.prevFrames[0][0] * coeff2Table[pWav->msadpcm.predictor[0]])) >> 8; - newSample0 += nibble0 * pWav->msadpcm.delta[0]; - newSample0 = ma_dr_wav_clamp(newSample0, -32768, 32767); - pWav->msadpcm.delta[0] = (adaptationTable[((nibbles & 0xF0) >> 4)] * pWav->msadpcm.delta[0]) >> 8; - if (pWav->msadpcm.delta[0] < 16) { - pWav->msadpcm.delta[0] = 16; - } - pWav->msadpcm.prevFrames[0][0] = pWav->msadpcm.prevFrames[0][1]; - pWav->msadpcm.prevFrames[0][1] = newSample0; - newSample1 = ((pWav->msadpcm.prevFrames[0][1] * coeff1Table[pWav->msadpcm.predictor[0]]) + (pWav->msadpcm.prevFrames[0][0] * coeff2Table[pWav->msadpcm.predictor[0]])) >> 8; - newSample1 += nibble1 * pWav->msadpcm.delta[0]; - newSample1 = ma_dr_wav_clamp(newSample1, -32768, 32767); - pWav->msadpcm.delta[0] = (adaptationTable[((nibbles & 0x0F) >> 0)] * pWav->msadpcm.delta[0]) >> 8; - if (pWav->msadpcm.delta[0] < 16) { - pWav->msadpcm.delta[0] = 16; - } - pWav->msadpcm.prevFrames[0][0] = pWav->msadpcm.prevFrames[0][1]; - pWav->msadpcm.prevFrames[0][1] = newSample1; - pWav->msadpcm.cachedFrames[2] = newSample0; - pWav->msadpcm.cachedFrames[3] = newSample1; - pWav->msadpcm.cachedFrameCount = 2; - } else { - ma_int32 newSample0; - ma_int32 newSample1; - newSample0 = ((pWav->msadpcm.prevFrames[0][1] * coeff1Table[pWav->msadpcm.predictor[0]]) + (pWav->msadpcm.prevFrames[0][0] * coeff2Table[pWav->msadpcm.predictor[0]])) >> 8; - newSample0 += nibble0 * pWav->msadpcm.delta[0]; - newSample0 = ma_dr_wav_clamp(newSample0, -32768, 32767); - pWav->msadpcm.delta[0] = (adaptationTable[((nibbles & 0xF0) >> 4)] * pWav->msadpcm.delta[0]) >> 8; - if (pWav->msadpcm.delta[0] < 16) { - pWav->msadpcm.delta[0] = 16; - } - pWav->msadpcm.prevFrames[0][0] = pWav->msadpcm.prevFrames[0][1]; - pWav->msadpcm.prevFrames[0][1] = newSample0; - newSample1 = ((pWav->msadpcm.prevFrames[1][1] * coeff1Table[pWav->msadpcm.predictor[1]]) + (pWav->msadpcm.prevFrames[1][0] * coeff2Table[pWav->msadpcm.predictor[1]])) >> 8; - newSample1 += nibble1 * pWav->msadpcm.delta[1]; - newSample1 = ma_dr_wav_clamp(newSample1, -32768, 32767); - pWav->msadpcm.delta[1] = (adaptationTable[((nibbles & 0x0F) >> 0)] * pWav->msadpcm.delta[1]) >> 8; - if (pWav->msadpcm.delta[1] < 16) { - pWav->msadpcm.delta[1] = 16; - } - pWav->msadpcm.prevFrames[1][0] = pWav->msadpcm.prevFrames[1][1]; - pWav->msadpcm.prevFrames[1][1] = newSample1; - pWav->msadpcm.cachedFrames[2] = newSample0; - pWav->msadpcm.cachedFrames[3] = newSample1; - pWav->msadpcm.cachedFrameCount = 1; - } - } - } - } - return totalFramesRead; -} -MA_PRIVATE ma_uint64 ma_dr_wav_read_pcm_frames_s16__ima(ma_dr_wav* pWav, ma_uint64 framesToRead, ma_int16* pBufferOut) -{ - ma_uint64 totalFramesRead = 0; - ma_uint32 iChannel; - static ma_int32 indexTable[16] = { - -1, -1, -1, -1, 2, 4, 6, 8, - -1, -1, -1, -1, 2, 4, 6, 8 - }; - static ma_int32 stepTable[89] = { - 7, 8, 9, 10, 11, 12, 13, 14, 16, 17, - 19, 21, 23, 25, 28, 31, 34, 37, 41, 45, - 50, 55, 60, 66, 73, 80, 88, 97, 107, 118, - 130, 143, 157, 173, 190, 209, 230, 253, 279, 307, - 337, 371, 408, 449, 494, 544, 598, 658, 724, 796, - 876, 963, 1060, 1166, 1282, 1411, 1552, 1707, 1878, 2066, - 2272, 2499, 2749, 3024, 3327, 3660, 4026, 4428, 4871, 5358, - 5894, 6484, 7132, 7845, 8630, 9493, 10442, 11487, 12635, 13899, - 15289, 16818, 18500, 20350, 22385, 24623, 27086, 29794, 32767 - }; - MA_DR_WAV_ASSERT(pWav != NULL); - MA_DR_WAV_ASSERT(framesToRead > 0); - while (pWav->readCursorInPCMFrames < pWav->totalPCMFrameCount) { - MA_DR_WAV_ASSERT(framesToRead > 0); - if (pWav->ima.cachedFrameCount == 0 && pWav->ima.bytesRemainingInBlock == 0) { - if (pWav->channels == 1) { - ma_uint8 header[4]; - if (pWav->onRead(pWav->pUserData, header, sizeof(header)) != sizeof(header)) { - return totalFramesRead; - } - pWav->ima.bytesRemainingInBlock = pWav->fmt.blockAlign - sizeof(header); - if (header[2] >= ma_dr_wav_countof(stepTable)) { - pWav->onSeek(pWav->pUserData, pWav->ima.bytesRemainingInBlock, ma_dr_wav_seek_origin_current); - pWav->ima.bytesRemainingInBlock = 0; - return totalFramesRead; - } - pWav->ima.predictor[0] = (ma_int16)ma_dr_wav_bytes_to_u16(header + 0); - pWav->ima.stepIndex[0] = ma_dr_wav_clamp(header[2], 0, (ma_int32)ma_dr_wav_countof(stepTable)-1); - pWav->ima.cachedFrames[ma_dr_wav_countof(pWav->ima.cachedFrames) - 1] = pWav->ima.predictor[0]; - pWav->ima.cachedFrameCount = 1; - } else { - ma_uint8 header[8]; - if (pWav->onRead(pWav->pUserData, header, sizeof(header)) != sizeof(header)) { - return totalFramesRead; - } - pWav->ima.bytesRemainingInBlock = pWav->fmt.blockAlign - sizeof(header); - if (header[2] >= ma_dr_wav_countof(stepTable) || header[6] >= ma_dr_wav_countof(stepTable)) { - pWav->onSeek(pWav->pUserData, pWav->ima.bytesRemainingInBlock, ma_dr_wav_seek_origin_current); - pWav->ima.bytesRemainingInBlock = 0; - return totalFramesRead; - } - pWav->ima.predictor[0] = ma_dr_wav_bytes_to_s16(header + 0); - pWav->ima.stepIndex[0] = ma_dr_wav_clamp(header[2], 0, (ma_int32)ma_dr_wav_countof(stepTable)-1); - pWav->ima.predictor[1] = ma_dr_wav_bytes_to_s16(header + 4); - pWav->ima.stepIndex[1] = ma_dr_wav_clamp(header[6], 0, (ma_int32)ma_dr_wav_countof(stepTable)-1); - pWav->ima.cachedFrames[ma_dr_wav_countof(pWav->ima.cachedFrames) - 2] = pWav->ima.predictor[0]; - pWav->ima.cachedFrames[ma_dr_wav_countof(pWav->ima.cachedFrames) - 1] = pWav->ima.predictor[1]; - pWav->ima.cachedFrameCount = 1; - } - } - while (framesToRead > 0 && pWav->ima.cachedFrameCount > 0 && pWav->readCursorInPCMFrames < pWav->totalPCMFrameCount) { - if (pBufferOut != NULL) { - ma_uint32 iSample; - for (iSample = 0; iSample < pWav->channels; iSample += 1) { - pBufferOut[iSample] = (ma_int16)pWav->ima.cachedFrames[(ma_dr_wav_countof(pWav->ima.cachedFrames) - (pWav->ima.cachedFrameCount*pWav->channels)) + iSample]; - } - pBufferOut += pWav->channels; - } - framesToRead -= 1; - totalFramesRead += 1; - pWav->readCursorInPCMFrames += 1; - pWav->ima.cachedFrameCount -= 1; - } - if (framesToRead == 0) { - break; - } - if (pWav->ima.cachedFrameCount == 0) { - if (pWav->ima.bytesRemainingInBlock == 0) { - continue; - } else { - pWav->ima.cachedFrameCount = 8; - for (iChannel = 0; iChannel < pWav->channels; ++iChannel) { - ma_uint32 iByte; - ma_uint8 nibbles[4]; - if (pWav->onRead(pWav->pUserData, &nibbles, 4) != 4) { - pWav->ima.cachedFrameCount = 0; - return totalFramesRead; - } - pWav->ima.bytesRemainingInBlock -= 4; - for (iByte = 0; iByte < 4; ++iByte) { - ma_uint8 nibble0 = ((nibbles[iByte] & 0x0F) >> 0); - ma_uint8 nibble1 = ((nibbles[iByte] & 0xF0) >> 4); - ma_int32 step = stepTable[pWav->ima.stepIndex[iChannel]]; - ma_int32 predictor = pWav->ima.predictor[iChannel]; - ma_int32 diff = step >> 3; - if (nibble0 & 1) diff += step >> 2; - if (nibble0 & 2) diff += step >> 1; - if (nibble0 & 4) diff += step; - if (nibble0 & 8) diff = -diff; - predictor = ma_dr_wav_clamp(predictor + diff, -32768, 32767); - pWav->ima.predictor[iChannel] = predictor; - pWav->ima.stepIndex[iChannel] = ma_dr_wav_clamp(pWav->ima.stepIndex[iChannel] + indexTable[nibble0], 0, (ma_int32)ma_dr_wav_countof(stepTable)-1); - pWav->ima.cachedFrames[(ma_dr_wav_countof(pWav->ima.cachedFrames) - (pWav->ima.cachedFrameCount*pWav->channels)) + (iByte*2+0)*pWav->channels + iChannel] = predictor; - step = stepTable[pWav->ima.stepIndex[iChannel]]; - predictor = pWav->ima.predictor[iChannel]; - diff = step >> 3; - if (nibble1 & 1) diff += step >> 2; - if (nibble1 & 2) diff += step >> 1; - if (nibble1 & 4) diff += step; - if (nibble1 & 8) diff = -diff; - predictor = ma_dr_wav_clamp(predictor + diff, -32768, 32767); - pWav->ima.predictor[iChannel] = predictor; - pWav->ima.stepIndex[iChannel] = ma_dr_wav_clamp(pWav->ima.stepIndex[iChannel] + indexTable[nibble1], 0, (ma_int32)ma_dr_wav_countof(stepTable)-1); - pWav->ima.cachedFrames[(ma_dr_wav_countof(pWav->ima.cachedFrames) - (pWav->ima.cachedFrameCount*pWav->channels)) + (iByte*2+1)*pWav->channels + iChannel] = predictor; - } - } - } - } - } - return totalFramesRead; -} -#ifndef MA_DR_WAV_NO_CONVERSION_API -static unsigned short g_ma_dr_wavAlawTable[256] = { - 0xEA80, 0xEB80, 0xE880, 0xE980, 0xEE80, 0xEF80, 0xEC80, 0xED80, 0xE280, 0xE380, 0xE080, 0xE180, 0xE680, 0xE780, 0xE480, 0xE580, - 0xF540, 0xF5C0, 0xF440, 0xF4C0, 0xF740, 0xF7C0, 0xF640, 0xF6C0, 0xF140, 0xF1C0, 0xF040, 0xF0C0, 0xF340, 0xF3C0, 0xF240, 0xF2C0, - 0xAA00, 0xAE00, 0xA200, 0xA600, 0xBA00, 0xBE00, 0xB200, 0xB600, 0x8A00, 0x8E00, 0x8200, 0x8600, 0x9A00, 0x9E00, 0x9200, 0x9600, - 0xD500, 0xD700, 0xD100, 0xD300, 0xDD00, 0xDF00, 0xD900, 0xDB00, 0xC500, 0xC700, 0xC100, 0xC300, 0xCD00, 0xCF00, 0xC900, 0xCB00, - 0xFEA8, 0xFEB8, 0xFE88, 0xFE98, 0xFEE8, 0xFEF8, 0xFEC8, 0xFED8, 0xFE28, 0xFE38, 0xFE08, 0xFE18, 0xFE68, 0xFE78, 0xFE48, 0xFE58, - 0xFFA8, 0xFFB8, 0xFF88, 0xFF98, 0xFFE8, 0xFFF8, 0xFFC8, 0xFFD8, 0xFF28, 0xFF38, 0xFF08, 0xFF18, 0xFF68, 0xFF78, 0xFF48, 0xFF58, - 0xFAA0, 0xFAE0, 0xFA20, 0xFA60, 0xFBA0, 0xFBE0, 0xFB20, 0xFB60, 0xF8A0, 0xF8E0, 0xF820, 0xF860, 0xF9A0, 0xF9E0, 0xF920, 0xF960, - 0xFD50, 0xFD70, 0xFD10, 0xFD30, 0xFDD0, 0xFDF0, 0xFD90, 0xFDB0, 0xFC50, 0xFC70, 0xFC10, 0xFC30, 0xFCD0, 0xFCF0, 0xFC90, 0xFCB0, - 0x1580, 0x1480, 0x1780, 0x1680, 0x1180, 0x1080, 0x1380, 0x1280, 0x1D80, 0x1C80, 0x1F80, 0x1E80, 0x1980, 0x1880, 0x1B80, 0x1A80, - 0x0AC0, 0x0A40, 0x0BC0, 0x0B40, 0x08C0, 0x0840, 0x09C0, 0x0940, 0x0EC0, 0x0E40, 0x0FC0, 0x0F40, 0x0CC0, 0x0C40, 0x0DC0, 0x0D40, - 0x5600, 0x5200, 0x5E00, 0x5A00, 0x4600, 0x4200, 0x4E00, 0x4A00, 0x7600, 0x7200, 0x7E00, 0x7A00, 0x6600, 0x6200, 0x6E00, 0x6A00, - 0x2B00, 0x2900, 0x2F00, 0x2D00, 0x2300, 0x2100, 0x2700, 0x2500, 0x3B00, 0x3900, 0x3F00, 0x3D00, 0x3300, 0x3100, 0x3700, 0x3500, - 0x0158, 0x0148, 0x0178, 0x0168, 0x0118, 0x0108, 0x0138, 0x0128, 0x01D8, 0x01C8, 0x01F8, 0x01E8, 0x0198, 0x0188, 0x01B8, 0x01A8, - 0x0058, 0x0048, 0x0078, 0x0068, 0x0018, 0x0008, 0x0038, 0x0028, 0x00D8, 0x00C8, 0x00F8, 0x00E8, 0x0098, 0x0088, 0x00B8, 0x00A8, - 0x0560, 0x0520, 0x05E0, 0x05A0, 0x0460, 0x0420, 0x04E0, 0x04A0, 0x0760, 0x0720, 0x07E0, 0x07A0, 0x0660, 0x0620, 0x06E0, 0x06A0, - 0x02B0, 0x0290, 0x02F0, 0x02D0, 0x0230, 0x0210, 0x0270, 0x0250, 0x03B0, 0x0390, 0x03F0, 0x03D0, 0x0330, 0x0310, 0x0370, 0x0350 -}; -static unsigned short g_ma_dr_wavMulawTable[256] = { - 0x8284, 0x8684, 0x8A84, 0x8E84, 0x9284, 0x9684, 0x9A84, 0x9E84, 0xA284, 0xA684, 0xAA84, 0xAE84, 0xB284, 0xB684, 0xBA84, 0xBE84, - 0xC184, 0xC384, 0xC584, 0xC784, 0xC984, 0xCB84, 0xCD84, 0xCF84, 0xD184, 0xD384, 0xD584, 0xD784, 0xD984, 0xDB84, 0xDD84, 0xDF84, - 0xE104, 0xE204, 0xE304, 0xE404, 0xE504, 0xE604, 0xE704, 0xE804, 0xE904, 0xEA04, 0xEB04, 0xEC04, 0xED04, 0xEE04, 0xEF04, 0xF004, - 0xF0C4, 0xF144, 0xF1C4, 0xF244, 0xF2C4, 0xF344, 0xF3C4, 0xF444, 0xF4C4, 0xF544, 0xF5C4, 0xF644, 0xF6C4, 0xF744, 0xF7C4, 0xF844, - 0xF8A4, 0xF8E4, 0xF924, 0xF964, 0xF9A4, 0xF9E4, 0xFA24, 0xFA64, 0xFAA4, 0xFAE4, 0xFB24, 0xFB64, 0xFBA4, 0xFBE4, 0xFC24, 0xFC64, - 0xFC94, 0xFCB4, 0xFCD4, 0xFCF4, 0xFD14, 0xFD34, 0xFD54, 0xFD74, 0xFD94, 0xFDB4, 0xFDD4, 0xFDF4, 0xFE14, 0xFE34, 0xFE54, 0xFE74, - 0xFE8C, 0xFE9C, 0xFEAC, 0xFEBC, 0xFECC, 0xFEDC, 0xFEEC, 0xFEFC, 0xFF0C, 0xFF1C, 0xFF2C, 0xFF3C, 0xFF4C, 0xFF5C, 0xFF6C, 0xFF7C, - 0xFF88, 0xFF90, 0xFF98, 0xFFA0, 0xFFA8, 0xFFB0, 0xFFB8, 0xFFC0, 0xFFC8, 0xFFD0, 0xFFD8, 0xFFE0, 0xFFE8, 0xFFF0, 0xFFF8, 0x0000, - 0x7D7C, 0x797C, 0x757C, 0x717C, 0x6D7C, 0x697C, 0x657C, 0x617C, 0x5D7C, 0x597C, 0x557C, 0x517C, 0x4D7C, 0x497C, 0x457C, 0x417C, - 0x3E7C, 0x3C7C, 0x3A7C, 0x387C, 0x367C, 0x347C, 0x327C, 0x307C, 0x2E7C, 0x2C7C, 0x2A7C, 0x287C, 0x267C, 0x247C, 0x227C, 0x207C, - 0x1EFC, 0x1DFC, 0x1CFC, 0x1BFC, 0x1AFC, 0x19FC, 0x18FC, 0x17FC, 0x16FC, 0x15FC, 0x14FC, 0x13FC, 0x12FC, 0x11FC, 0x10FC, 0x0FFC, - 0x0F3C, 0x0EBC, 0x0E3C, 0x0DBC, 0x0D3C, 0x0CBC, 0x0C3C, 0x0BBC, 0x0B3C, 0x0ABC, 0x0A3C, 0x09BC, 0x093C, 0x08BC, 0x083C, 0x07BC, - 0x075C, 0x071C, 0x06DC, 0x069C, 0x065C, 0x061C, 0x05DC, 0x059C, 0x055C, 0x051C, 0x04DC, 0x049C, 0x045C, 0x041C, 0x03DC, 0x039C, - 0x036C, 0x034C, 0x032C, 0x030C, 0x02EC, 0x02CC, 0x02AC, 0x028C, 0x026C, 0x024C, 0x022C, 0x020C, 0x01EC, 0x01CC, 0x01AC, 0x018C, - 0x0174, 0x0164, 0x0154, 0x0144, 0x0134, 0x0124, 0x0114, 0x0104, 0x00F4, 0x00E4, 0x00D4, 0x00C4, 0x00B4, 0x00A4, 0x0094, 0x0084, - 0x0078, 0x0070, 0x0068, 0x0060, 0x0058, 0x0050, 0x0048, 0x0040, 0x0038, 0x0030, 0x0028, 0x0020, 0x0018, 0x0010, 0x0008, 0x0000 -}; -static MA_INLINE ma_int16 ma_dr_wav__alaw_to_s16(ma_uint8 sampleIn) -{ - return (short)g_ma_dr_wavAlawTable[sampleIn]; -} -static MA_INLINE ma_int16 ma_dr_wav__mulaw_to_s16(ma_uint8 sampleIn) -{ - return (short)g_ma_dr_wavMulawTable[sampleIn]; -} -MA_PRIVATE void ma_dr_wav__pcm_to_s16(ma_int16* pOut, const ma_uint8* pIn, size_t totalSampleCount, unsigned int bytesPerSample) -{ - size_t i; - if (bytesPerSample == 1) { - ma_dr_wav_u8_to_s16(pOut, pIn, totalSampleCount); - return; - } - if (bytesPerSample == 2) { - for (i = 0; i < totalSampleCount; ++i) { - *pOut++ = ((const ma_int16*)pIn)[i]; - } - return; - } - if (bytesPerSample == 3) { - ma_dr_wav_s24_to_s16(pOut, pIn, totalSampleCount); - return; - } - if (bytesPerSample == 4) { - ma_dr_wav_s32_to_s16(pOut, (const ma_int32*)pIn, totalSampleCount); - return; - } - if (bytesPerSample > 8) { - MA_DR_WAV_ZERO_MEMORY(pOut, totalSampleCount * sizeof(*pOut)); - return; - } - for (i = 0; i < totalSampleCount; ++i) { - ma_uint64 sample = 0; - unsigned int shift = (8 - bytesPerSample) * 8; - unsigned int j; - for (j = 0; j < bytesPerSample; j += 1) { - MA_DR_WAV_ASSERT(j < 8); - sample |= (ma_uint64)(pIn[j]) << shift; - shift += 8; - } - pIn += j; - *pOut++ = (ma_int16)((ma_int64)sample >> 48); - } -} -MA_PRIVATE void ma_dr_wav__ieee_to_s16(ma_int16* pOut, const ma_uint8* pIn, size_t totalSampleCount, unsigned int bytesPerSample) -{ - if (bytesPerSample == 4) { - ma_dr_wav_f32_to_s16(pOut, (const float*)pIn, totalSampleCount); - return; - } else if (bytesPerSample == 8) { - ma_dr_wav_f64_to_s16(pOut, (const double*)pIn, totalSampleCount); - return; - } else { - MA_DR_WAV_ZERO_MEMORY(pOut, totalSampleCount * sizeof(*pOut)); - return; - } -} -MA_PRIVATE ma_uint64 ma_dr_wav_read_pcm_frames_s16__pcm(ma_dr_wav* pWav, ma_uint64 framesToRead, ma_int16* pBufferOut) -{ - ma_uint64 totalFramesRead; - ma_uint8 sampleData[4096] = {0}; - ma_uint32 bytesPerFrame; - ma_uint32 bytesPerSample; - ma_uint64 samplesRead; - if ((pWav->translatedFormatTag == MA_DR_WAVE_FORMAT_PCM && pWav->bitsPerSample == 16) || pBufferOut == NULL) { - return ma_dr_wav_read_pcm_frames(pWav, framesToRead, pBufferOut); - } - bytesPerFrame = ma_dr_wav_get_bytes_per_pcm_frame(pWav); - if (bytesPerFrame == 0) { - return 0; - } - bytesPerSample = bytesPerFrame / pWav->channels; - if (bytesPerSample == 0 || (bytesPerFrame % pWav->channels) != 0) { - return 0; - } - totalFramesRead = 0; - while (framesToRead > 0) { - ma_uint64 framesToReadThisIteration = ma_dr_wav_min(framesToRead, sizeof(sampleData)/bytesPerFrame); - ma_uint64 framesRead = ma_dr_wav_read_pcm_frames(pWav, framesToReadThisIteration, sampleData); - if (framesRead == 0) { - break; - } - MA_DR_WAV_ASSERT(framesRead <= framesToReadThisIteration); - samplesRead = framesRead * pWav->channels; - if ((samplesRead * bytesPerSample) > sizeof(sampleData)) { - MA_DR_WAV_ASSERT(MA_FALSE); - break; - } - ma_dr_wav__pcm_to_s16(pBufferOut, sampleData, (size_t)samplesRead, bytesPerSample); - pBufferOut += samplesRead; - framesToRead -= framesRead; - totalFramesRead += framesRead; - } - return totalFramesRead; -} -MA_PRIVATE ma_uint64 ma_dr_wav_read_pcm_frames_s16__ieee(ma_dr_wav* pWav, ma_uint64 framesToRead, ma_int16* pBufferOut) -{ - ma_uint64 totalFramesRead; - ma_uint8 sampleData[4096] = {0}; - ma_uint32 bytesPerFrame; - ma_uint32 bytesPerSample; - ma_uint64 samplesRead; - if (pBufferOut == NULL) { - return ma_dr_wav_read_pcm_frames(pWav, framesToRead, NULL); - } - bytesPerFrame = ma_dr_wav_get_bytes_per_pcm_frame(pWav); - if (bytesPerFrame == 0) { - return 0; - } - bytesPerSample = bytesPerFrame / pWav->channels; - if (bytesPerSample == 0 || (bytesPerFrame % pWav->channels) != 0) { - return 0; - } - totalFramesRead = 0; - while (framesToRead > 0) { - ma_uint64 framesToReadThisIteration = ma_dr_wav_min(framesToRead, sizeof(sampleData)/bytesPerFrame); - ma_uint64 framesRead = ma_dr_wav_read_pcm_frames(pWav, framesToReadThisIteration, sampleData); - if (framesRead == 0) { - break; - } - MA_DR_WAV_ASSERT(framesRead <= framesToReadThisIteration); - samplesRead = framesRead * pWav->channels; - if ((samplesRead * bytesPerSample) > sizeof(sampleData)) { - MA_DR_WAV_ASSERT(MA_FALSE); - break; - } - ma_dr_wav__ieee_to_s16(pBufferOut, sampleData, (size_t)samplesRead, bytesPerSample); - pBufferOut += samplesRead; - framesToRead -= framesRead; - totalFramesRead += framesRead; - } - return totalFramesRead; -} -MA_PRIVATE ma_uint64 ma_dr_wav_read_pcm_frames_s16__alaw(ma_dr_wav* pWav, ma_uint64 framesToRead, ma_int16* pBufferOut) -{ - ma_uint64 totalFramesRead; - ma_uint8 sampleData[4096] = {0}; - ma_uint32 bytesPerFrame; - ma_uint32 bytesPerSample; - ma_uint64 samplesRead; - if (pBufferOut == NULL) { - return ma_dr_wav_read_pcm_frames(pWav, framesToRead, NULL); - } - bytesPerFrame = ma_dr_wav_get_bytes_per_pcm_frame(pWav); - if (bytesPerFrame == 0) { - return 0; - } - bytesPerSample = bytesPerFrame / pWav->channels; - if (bytesPerSample == 0 || (bytesPerFrame % pWav->channels) != 0) { - return 0; - } - totalFramesRead = 0; - while (framesToRead > 0) { - ma_uint64 framesToReadThisIteration = ma_dr_wav_min(framesToRead, sizeof(sampleData)/bytesPerFrame); - ma_uint64 framesRead = ma_dr_wav_read_pcm_frames(pWav, framesToReadThisIteration, sampleData); - if (framesRead == 0) { - break; - } - MA_DR_WAV_ASSERT(framesRead <= framesToReadThisIteration); - samplesRead = framesRead * pWav->channels; - if ((samplesRead * bytesPerSample) > sizeof(sampleData)) { - MA_DR_WAV_ASSERT(MA_FALSE); - break; - } - ma_dr_wav_alaw_to_s16(pBufferOut, sampleData, (size_t)samplesRead); - #ifdef MA_DR_WAV_LIBSNDFILE_COMPAT - { - if (pWav->container == ma_dr_wav_container_aiff) { - ma_uint64 iSample; - for (iSample = 0; iSample < samplesRead; iSample += 1) { - pBufferOut[iSample] = -pBufferOut[iSample]; - } - } - } - #endif - pBufferOut += samplesRead; - framesToRead -= framesRead; - totalFramesRead += framesRead; - } - return totalFramesRead; -} -MA_PRIVATE ma_uint64 ma_dr_wav_read_pcm_frames_s16__mulaw(ma_dr_wav* pWav, ma_uint64 framesToRead, ma_int16* pBufferOut) -{ - ma_uint64 totalFramesRead; - ma_uint8 sampleData[4096] = {0}; - ma_uint32 bytesPerFrame; - ma_uint32 bytesPerSample; - ma_uint64 samplesRead; - if (pBufferOut == NULL) { - return ma_dr_wav_read_pcm_frames(pWav, framesToRead, NULL); - } - bytesPerFrame = ma_dr_wav_get_bytes_per_pcm_frame(pWav); - if (bytesPerFrame == 0) { - return 0; - } - bytesPerSample = bytesPerFrame / pWav->channels; - if (bytesPerSample == 0 || (bytesPerFrame % pWav->channels) != 0) { - return 0; - } - totalFramesRead = 0; - while (framesToRead > 0) { - ma_uint64 framesToReadThisIteration = ma_dr_wav_min(framesToRead, sizeof(sampleData)/bytesPerFrame); - ma_uint64 framesRead = ma_dr_wav_read_pcm_frames(pWav, framesToReadThisIteration, sampleData); - if (framesRead == 0) { - break; - } - MA_DR_WAV_ASSERT(framesRead <= framesToReadThisIteration); - samplesRead = framesRead * pWav->channels; - if ((samplesRead * bytesPerSample) > sizeof(sampleData)) { - MA_DR_WAV_ASSERT(MA_FALSE); - break; - } - ma_dr_wav_mulaw_to_s16(pBufferOut, sampleData, (size_t)samplesRead); - #ifdef MA_DR_WAV_LIBSNDFILE_COMPAT - { - if (pWav->container == ma_dr_wav_container_aiff) { - ma_uint64 iSample; - for (iSample = 0; iSample < samplesRead; iSample += 1) { - pBufferOut[iSample] = -pBufferOut[iSample]; - } - } - } - #endif - pBufferOut += samplesRead; - framesToRead -= framesRead; - totalFramesRead += framesRead; - } - return totalFramesRead; -} -MA_API ma_uint64 ma_dr_wav_read_pcm_frames_s16(ma_dr_wav* pWav, ma_uint64 framesToRead, ma_int16* pBufferOut) -{ - if (pWav == NULL || framesToRead == 0) { - return 0; - } - if (pBufferOut == NULL) { - return ma_dr_wav_read_pcm_frames(pWav, framesToRead, NULL); - } - if (framesToRead * pWav->channels * sizeof(ma_int16) > MA_SIZE_MAX) { - framesToRead = MA_SIZE_MAX / sizeof(ma_int16) / pWav->channels; - } - if (pWav->translatedFormatTag == MA_DR_WAVE_FORMAT_PCM) { - return ma_dr_wav_read_pcm_frames_s16__pcm(pWav, framesToRead, pBufferOut); - } - if (pWav->translatedFormatTag == MA_DR_WAVE_FORMAT_IEEE_FLOAT) { - return ma_dr_wav_read_pcm_frames_s16__ieee(pWav, framesToRead, pBufferOut); - } - if (pWav->translatedFormatTag == MA_DR_WAVE_FORMAT_ALAW) { - return ma_dr_wav_read_pcm_frames_s16__alaw(pWav, framesToRead, pBufferOut); - } - if (pWav->translatedFormatTag == MA_DR_WAVE_FORMAT_MULAW) { - return ma_dr_wav_read_pcm_frames_s16__mulaw(pWav, framesToRead, pBufferOut); - } - if (pWav->translatedFormatTag == MA_DR_WAVE_FORMAT_ADPCM) { - return ma_dr_wav_read_pcm_frames_s16__msadpcm(pWav, framesToRead, pBufferOut); - } - if (pWav->translatedFormatTag == MA_DR_WAVE_FORMAT_DVI_ADPCM) { - return ma_dr_wav_read_pcm_frames_s16__ima(pWav, framesToRead, pBufferOut); - } - return 0; -} -MA_API ma_uint64 ma_dr_wav_read_pcm_frames_s16le(ma_dr_wav* pWav, ma_uint64 framesToRead, ma_int16* pBufferOut) -{ - ma_uint64 framesRead = ma_dr_wav_read_pcm_frames_s16(pWav, framesToRead, pBufferOut); - if (pBufferOut != NULL && ma_dr_wav__is_little_endian() == MA_FALSE) { - ma_dr_wav__bswap_samples_s16(pBufferOut, framesRead*pWav->channels); - } - return framesRead; -} -MA_API ma_uint64 ma_dr_wav_read_pcm_frames_s16be(ma_dr_wav* pWav, ma_uint64 framesToRead, ma_int16* pBufferOut) -{ - ma_uint64 framesRead = ma_dr_wav_read_pcm_frames_s16(pWav, framesToRead, pBufferOut); - if (pBufferOut != NULL && ma_dr_wav__is_little_endian() == MA_TRUE) { - ma_dr_wav__bswap_samples_s16(pBufferOut, framesRead*pWav->channels); - } - return framesRead; -} -MA_API void ma_dr_wav_u8_to_s16(ma_int16* pOut, const ma_uint8* pIn, size_t sampleCount) -{ - int r; - size_t i; - for (i = 0; i < sampleCount; ++i) { - int x = pIn[i]; - r = x << 8; - r = r - 32768; - pOut[i] = (short)r; - } -} -MA_API void ma_dr_wav_s24_to_s16(ma_int16* pOut, const ma_uint8* pIn, size_t sampleCount) -{ - int r; - size_t i; - for (i = 0; i < sampleCount; ++i) { - int x = ((int)(((unsigned int)(((const ma_uint8*)pIn)[i*3+0]) << 8) | ((unsigned int)(((const ma_uint8*)pIn)[i*3+1]) << 16) | ((unsigned int)(((const ma_uint8*)pIn)[i*3+2])) << 24)) >> 8; - r = x >> 8; - pOut[i] = (short)r; - } -} -MA_API void ma_dr_wav_s32_to_s16(ma_int16* pOut, const ma_int32* pIn, size_t sampleCount) -{ - int r; - size_t i; - for (i = 0; i < sampleCount; ++i) { - int x = pIn[i]; - r = x >> 16; - pOut[i] = (short)r; - } -} -MA_API void ma_dr_wav_f32_to_s16(ma_int16* pOut, const float* pIn, size_t sampleCount) -{ - int r; - size_t i; - for (i = 0; i < sampleCount; ++i) { - float x = pIn[i]; - float c; - c = ((x < -1) ? -1 : ((x > 1) ? 1 : x)); - c = c + 1; - r = (int)(c * 32767.5f); - r = r - 32768; - pOut[i] = (short)r; - } -} -MA_API void ma_dr_wav_f64_to_s16(ma_int16* pOut, const double* pIn, size_t sampleCount) -{ - int r; - size_t i; - for (i = 0; i < sampleCount; ++i) { - double x = pIn[i]; - double c; - c = ((x < -1) ? -1 : ((x > 1) ? 1 : x)); - c = c + 1; - r = (int)(c * 32767.5); - r = r - 32768; - pOut[i] = (short)r; - } -} -MA_API void ma_dr_wav_alaw_to_s16(ma_int16* pOut, const ma_uint8* pIn, size_t sampleCount) -{ - size_t i; - for (i = 0; i < sampleCount; ++i) { - pOut[i] = ma_dr_wav__alaw_to_s16(pIn[i]); - } -} -MA_API void ma_dr_wav_mulaw_to_s16(ma_int16* pOut, const ma_uint8* pIn, size_t sampleCount) -{ - size_t i; - for (i = 0; i < sampleCount; ++i) { - pOut[i] = ma_dr_wav__mulaw_to_s16(pIn[i]); - } -} -MA_PRIVATE void ma_dr_wav__pcm_to_f32(float* pOut, const ma_uint8* pIn, size_t sampleCount, unsigned int bytesPerSample) -{ - unsigned int i; - if (bytesPerSample == 1) { - ma_dr_wav_u8_to_f32(pOut, pIn, sampleCount); - return; - } - if (bytesPerSample == 2) { - ma_dr_wav_s16_to_f32(pOut, (const ma_int16*)pIn, sampleCount); - return; - } - if (bytesPerSample == 3) { - ma_dr_wav_s24_to_f32(pOut, pIn, sampleCount); - return; - } - if (bytesPerSample == 4) { - ma_dr_wav_s32_to_f32(pOut, (const ma_int32*)pIn, sampleCount); - return; - } - if (bytesPerSample > 8) { - MA_DR_WAV_ZERO_MEMORY(pOut, sampleCount * sizeof(*pOut)); - return; - } - for (i = 0; i < sampleCount; ++i) { - ma_uint64 sample = 0; - unsigned int shift = (8 - bytesPerSample) * 8; - unsigned int j; - for (j = 0; j < bytesPerSample; j += 1) { - MA_DR_WAV_ASSERT(j < 8); - sample |= (ma_uint64)(pIn[j]) << shift; - shift += 8; - } - pIn += j; - *pOut++ = (float)((ma_int64)sample / 9223372036854775807.0); - } -} -MA_PRIVATE void ma_dr_wav__ieee_to_f32(float* pOut, const ma_uint8* pIn, size_t sampleCount, unsigned int bytesPerSample) -{ - if (bytesPerSample == 4) { - unsigned int i; - for (i = 0; i < sampleCount; ++i) { - *pOut++ = ((const float*)pIn)[i]; - } - return; - } else if (bytesPerSample == 8) { - ma_dr_wav_f64_to_f32(pOut, (const double*)pIn, sampleCount); - return; - } else { - MA_DR_WAV_ZERO_MEMORY(pOut, sampleCount * sizeof(*pOut)); - return; - } -} -MA_PRIVATE ma_uint64 ma_dr_wav_read_pcm_frames_f32__pcm(ma_dr_wav* pWav, ma_uint64 framesToRead, float* pBufferOut) -{ - ma_uint64 totalFramesRead; - ma_uint8 sampleData[4096] = {0}; - ma_uint32 bytesPerFrame; - ma_uint32 bytesPerSample; - ma_uint64 samplesRead; - bytesPerFrame = ma_dr_wav_get_bytes_per_pcm_frame(pWav); - if (bytesPerFrame == 0) { - return 0; - } - bytesPerSample = bytesPerFrame / pWav->channels; - if (bytesPerSample == 0 || (bytesPerFrame % pWav->channels) != 0) { - return 0; - } - totalFramesRead = 0; - while (framesToRead > 0) { - ma_uint64 framesToReadThisIteration = ma_dr_wav_min(framesToRead, sizeof(sampleData)/bytesPerFrame); - ma_uint64 framesRead = ma_dr_wav_read_pcm_frames(pWav, framesToReadThisIteration, sampleData); - if (framesRead == 0) { - break; - } - MA_DR_WAV_ASSERT(framesRead <= framesToReadThisIteration); - samplesRead = framesRead * pWav->channels; - if ((samplesRead * bytesPerSample) > sizeof(sampleData)) { - MA_DR_WAV_ASSERT(MA_FALSE); - break; - } - ma_dr_wav__pcm_to_f32(pBufferOut, sampleData, (size_t)samplesRead, bytesPerSample); - pBufferOut += samplesRead; - framesToRead -= framesRead; - totalFramesRead += framesRead; - } - return totalFramesRead; -} -MA_PRIVATE ma_uint64 ma_dr_wav_read_pcm_frames_f32__msadpcm_ima(ma_dr_wav* pWav, ma_uint64 framesToRead, float* pBufferOut) -{ - ma_uint64 totalFramesRead; - ma_int16 samples16[2048]; - totalFramesRead = 0; - while (framesToRead > 0) { - ma_uint64 framesToReadThisIteration = ma_dr_wav_min(framesToRead, ma_dr_wav_countof(samples16)/pWav->channels); - ma_uint64 framesRead = ma_dr_wav_read_pcm_frames_s16(pWav, framesToReadThisIteration, samples16); - if (framesRead == 0) { - break; - } - MA_DR_WAV_ASSERT(framesRead <= framesToReadThisIteration); - ma_dr_wav_s16_to_f32(pBufferOut, samples16, (size_t)(framesRead*pWav->channels)); - pBufferOut += framesRead*pWav->channels; - framesToRead -= framesRead; - totalFramesRead += framesRead; - } - return totalFramesRead; -} -MA_PRIVATE ma_uint64 ma_dr_wav_read_pcm_frames_f32__ieee(ma_dr_wav* pWav, ma_uint64 framesToRead, float* pBufferOut) -{ - ma_uint64 totalFramesRead; - ma_uint8 sampleData[4096] = {0}; - ma_uint32 bytesPerFrame; - ma_uint32 bytesPerSample; - ma_uint64 samplesRead; - if (pWav->translatedFormatTag == MA_DR_WAVE_FORMAT_IEEE_FLOAT && pWav->bitsPerSample == 32) { - return ma_dr_wav_read_pcm_frames(pWav, framesToRead, pBufferOut); - } - bytesPerFrame = ma_dr_wav_get_bytes_per_pcm_frame(pWav); - if (bytesPerFrame == 0) { - return 0; - } - bytesPerSample = bytesPerFrame / pWav->channels; - if (bytesPerSample == 0 || (bytesPerFrame % pWav->channels) != 0) { - return 0; - } - totalFramesRead = 0; - while (framesToRead > 0) { - ma_uint64 framesToReadThisIteration = ma_dr_wav_min(framesToRead, sizeof(sampleData)/bytesPerFrame); - ma_uint64 framesRead = ma_dr_wav_read_pcm_frames(pWav, framesToReadThisIteration, sampleData); - if (framesRead == 0) { - break; - } - MA_DR_WAV_ASSERT(framesRead <= framesToReadThisIteration); - samplesRead = framesRead * pWav->channels; - if ((samplesRead * bytesPerSample) > sizeof(sampleData)) { - MA_DR_WAV_ASSERT(MA_FALSE); - break; - } - ma_dr_wav__ieee_to_f32(pBufferOut, sampleData, (size_t)samplesRead, bytesPerSample); - pBufferOut += samplesRead; - framesToRead -= framesRead; - totalFramesRead += framesRead; - } - return totalFramesRead; -} -MA_PRIVATE ma_uint64 ma_dr_wav_read_pcm_frames_f32__alaw(ma_dr_wav* pWav, ma_uint64 framesToRead, float* pBufferOut) -{ - ma_uint64 totalFramesRead; - ma_uint8 sampleData[4096] = {0}; - ma_uint32 bytesPerFrame; - ma_uint32 bytesPerSample; - ma_uint64 samplesRead; - bytesPerFrame = ma_dr_wav_get_bytes_per_pcm_frame(pWav); - if (bytesPerFrame == 0) { - return 0; - } - bytesPerSample = bytesPerFrame / pWav->channels; - if (bytesPerSample == 0 || (bytesPerFrame % pWav->channels) != 0) { - return 0; - } - totalFramesRead = 0; - while (framesToRead > 0) { - ma_uint64 framesToReadThisIteration = ma_dr_wav_min(framesToRead, sizeof(sampleData)/bytesPerFrame); - ma_uint64 framesRead = ma_dr_wav_read_pcm_frames(pWav, framesToReadThisIteration, sampleData); - if (framesRead == 0) { - break; - } - MA_DR_WAV_ASSERT(framesRead <= framesToReadThisIteration); - samplesRead = framesRead * pWav->channels; - if ((samplesRead * bytesPerSample) > sizeof(sampleData)) { - MA_DR_WAV_ASSERT(MA_FALSE); - break; - } - ma_dr_wav_alaw_to_f32(pBufferOut, sampleData, (size_t)samplesRead); - #ifdef MA_DR_WAV_LIBSNDFILE_COMPAT - { - if (pWav->container == ma_dr_wav_container_aiff) { - ma_uint64 iSample; - for (iSample = 0; iSample < samplesRead; iSample += 1) { - pBufferOut[iSample] = -pBufferOut[iSample]; - } - } - } - #endif - pBufferOut += samplesRead; - framesToRead -= framesRead; - totalFramesRead += framesRead; - } - return totalFramesRead; -} -MA_PRIVATE ma_uint64 ma_dr_wav_read_pcm_frames_f32__mulaw(ma_dr_wav* pWav, ma_uint64 framesToRead, float* pBufferOut) -{ - ma_uint64 totalFramesRead; - ma_uint8 sampleData[4096] = {0}; - ma_uint32 bytesPerFrame; - ma_uint32 bytesPerSample; - ma_uint64 samplesRead; - bytesPerFrame = ma_dr_wav_get_bytes_per_pcm_frame(pWav); - if (bytesPerFrame == 0) { - return 0; - } - bytesPerSample = bytesPerFrame / pWav->channels; - if (bytesPerSample == 0 || (bytesPerFrame % pWav->channels) != 0) { - return 0; - } - totalFramesRead = 0; - while (framesToRead > 0) { - ma_uint64 framesToReadThisIteration = ma_dr_wav_min(framesToRead, sizeof(sampleData)/bytesPerFrame); - ma_uint64 framesRead = ma_dr_wav_read_pcm_frames(pWav, framesToReadThisIteration, sampleData); - if (framesRead == 0) { - break; - } - MA_DR_WAV_ASSERT(framesRead <= framesToReadThisIteration); - samplesRead = framesRead * pWav->channels; - if ((samplesRead * bytesPerSample) > sizeof(sampleData)) { - MA_DR_WAV_ASSERT(MA_FALSE); - break; - } - ma_dr_wav_mulaw_to_f32(pBufferOut, sampleData, (size_t)samplesRead); - #ifdef MA_DR_WAV_LIBSNDFILE_COMPAT - { - if (pWav->container == ma_dr_wav_container_aiff) { - ma_uint64 iSample; - for (iSample = 0; iSample < samplesRead; iSample += 1) { - pBufferOut[iSample] = -pBufferOut[iSample]; - } - } - } - #endif - pBufferOut += samplesRead; - framesToRead -= framesRead; - totalFramesRead += framesRead; - } - return totalFramesRead; -} -MA_API ma_uint64 ma_dr_wav_read_pcm_frames_f32(ma_dr_wav* pWav, ma_uint64 framesToRead, float* pBufferOut) -{ - if (pWav == NULL || framesToRead == 0) { - return 0; - } - if (pBufferOut == NULL) { - return ma_dr_wav_read_pcm_frames(pWav, framesToRead, NULL); - } - if (framesToRead * pWav->channels * sizeof(float) > MA_SIZE_MAX) { - framesToRead = MA_SIZE_MAX / sizeof(float) / pWav->channels; - } - if (pWav->translatedFormatTag == MA_DR_WAVE_FORMAT_PCM) { - return ma_dr_wav_read_pcm_frames_f32__pcm(pWav, framesToRead, pBufferOut); - } - if (pWav->translatedFormatTag == MA_DR_WAVE_FORMAT_ADPCM || pWav->translatedFormatTag == MA_DR_WAVE_FORMAT_DVI_ADPCM) { - return ma_dr_wav_read_pcm_frames_f32__msadpcm_ima(pWav, framesToRead, pBufferOut); - } - if (pWav->translatedFormatTag == MA_DR_WAVE_FORMAT_IEEE_FLOAT) { - return ma_dr_wav_read_pcm_frames_f32__ieee(pWav, framesToRead, pBufferOut); - } - if (pWav->translatedFormatTag == MA_DR_WAVE_FORMAT_ALAW) { - return ma_dr_wav_read_pcm_frames_f32__alaw(pWav, framesToRead, pBufferOut); - } - if (pWav->translatedFormatTag == MA_DR_WAVE_FORMAT_MULAW) { - return ma_dr_wav_read_pcm_frames_f32__mulaw(pWav, framesToRead, pBufferOut); - } - return 0; -} -MA_API ma_uint64 ma_dr_wav_read_pcm_frames_f32le(ma_dr_wav* pWav, ma_uint64 framesToRead, float* pBufferOut) -{ - ma_uint64 framesRead = ma_dr_wav_read_pcm_frames_f32(pWav, framesToRead, pBufferOut); - if (pBufferOut != NULL && ma_dr_wav__is_little_endian() == MA_FALSE) { - ma_dr_wav__bswap_samples_f32(pBufferOut, framesRead*pWav->channels); - } - return framesRead; -} -MA_API ma_uint64 ma_dr_wav_read_pcm_frames_f32be(ma_dr_wav* pWav, ma_uint64 framesToRead, float* pBufferOut) -{ - ma_uint64 framesRead = ma_dr_wav_read_pcm_frames_f32(pWav, framesToRead, pBufferOut); - if (pBufferOut != NULL && ma_dr_wav__is_little_endian() == MA_TRUE) { - ma_dr_wav__bswap_samples_f32(pBufferOut, framesRead*pWav->channels); - } - return framesRead; -} -MA_API void ma_dr_wav_u8_to_f32(float* pOut, const ma_uint8* pIn, size_t sampleCount) -{ - size_t i; - if (pOut == NULL || pIn == NULL) { - return; - } -#ifdef MA_DR_WAV_LIBSNDFILE_COMPAT - for (i = 0; i < sampleCount; ++i) { - *pOut++ = (pIn[i] / 256.0f) * 2 - 1; - } -#else - for (i = 0; i < sampleCount; ++i) { - float x = pIn[i]; - x = x * 0.00784313725490196078f; - x = x - 1; - *pOut++ = x; - } -#endif -} -MA_API void ma_dr_wav_s16_to_f32(float* pOut, const ma_int16* pIn, size_t sampleCount) -{ - size_t i; - if (pOut == NULL || pIn == NULL) { - return; - } - for (i = 0; i < sampleCount; ++i) { - *pOut++ = pIn[i] * 0.000030517578125f; - } -} -MA_API void ma_dr_wav_s24_to_f32(float* pOut, const ma_uint8* pIn, size_t sampleCount) -{ - size_t i; - if (pOut == NULL || pIn == NULL) { - return; - } - for (i = 0; i < sampleCount; ++i) { - double x; - ma_uint32 a = ((ma_uint32)(pIn[i*3+0]) << 8); - ma_uint32 b = ((ma_uint32)(pIn[i*3+1]) << 16); - ma_uint32 c = ((ma_uint32)(pIn[i*3+2]) << 24); - x = (double)((ma_int32)(a | b | c) >> 8); - *pOut++ = (float)(x * 0.00000011920928955078125); - } -} -MA_API void ma_dr_wav_s32_to_f32(float* pOut, const ma_int32* pIn, size_t sampleCount) -{ - size_t i; - if (pOut == NULL || pIn == NULL) { - return; - } - for (i = 0; i < sampleCount; ++i) { - *pOut++ = (float)(pIn[i] / 2147483648.0); - } -} -MA_API void ma_dr_wav_f64_to_f32(float* pOut, const double* pIn, size_t sampleCount) -{ - size_t i; - if (pOut == NULL || pIn == NULL) { - return; - } - for (i = 0; i < sampleCount; ++i) { - *pOut++ = (float)pIn[i]; - } -} -MA_API void ma_dr_wav_alaw_to_f32(float* pOut, const ma_uint8* pIn, size_t sampleCount) -{ - size_t i; - if (pOut == NULL || pIn == NULL) { - return; - } - for (i = 0; i < sampleCount; ++i) { - *pOut++ = ma_dr_wav__alaw_to_s16(pIn[i]) / 32768.0f; - } -} -MA_API void ma_dr_wav_mulaw_to_f32(float* pOut, const ma_uint8* pIn, size_t sampleCount) -{ - size_t i; - if (pOut == NULL || pIn == NULL) { - return; - } - for (i = 0; i < sampleCount; ++i) { - *pOut++ = ma_dr_wav__mulaw_to_s16(pIn[i]) / 32768.0f; - } -} -MA_PRIVATE void ma_dr_wav__pcm_to_s32(ma_int32* pOut, const ma_uint8* pIn, size_t totalSampleCount, unsigned int bytesPerSample) -{ - unsigned int i; - if (bytesPerSample == 1) { - ma_dr_wav_u8_to_s32(pOut, pIn, totalSampleCount); - return; - } - if (bytesPerSample == 2) { - ma_dr_wav_s16_to_s32(pOut, (const ma_int16*)pIn, totalSampleCount); - return; - } - if (bytesPerSample == 3) { - ma_dr_wav_s24_to_s32(pOut, pIn, totalSampleCount); - return; - } - if (bytesPerSample == 4) { - for (i = 0; i < totalSampleCount; ++i) { - *pOut++ = ((const ma_int32*)pIn)[i]; - } - return; - } - if (bytesPerSample > 8) { - MA_DR_WAV_ZERO_MEMORY(pOut, totalSampleCount * sizeof(*pOut)); - return; - } - for (i = 0; i < totalSampleCount; ++i) { - ma_uint64 sample = 0; - unsigned int shift = (8 - bytesPerSample) * 8; - unsigned int j; - for (j = 0; j < bytesPerSample; j += 1) { - MA_DR_WAV_ASSERT(j < 8); - sample |= (ma_uint64)(pIn[j]) << shift; - shift += 8; - } - pIn += j; - *pOut++ = (ma_int32)((ma_int64)sample >> 32); - } -} -MA_PRIVATE void ma_dr_wav__ieee_to_s32(ma_int32* pOut, const ma_uint8* pIn, size_t totalSampleCount, unsigned int bytesPerSample) -{ - if (bytesPerSample == 4) { - ma_dr_wav_f32_to_s32(pOut, (const float*)pIn, totalSampleCount); - return; - } else if (bytesPerSample == 8) { - ma_dr_wav_f64_to_s32(pOut, (const double*)pIn, totalSampleCount); - return; - } else { - MA_DR_WAV_ZERO_MEMORY(pOut, totalSampleCount * sizeof(*pOut)); - return; - } -} -MA_PRIVATE ma_uint64 ma_dr_wav_read_pcm_frames_s32__pcm(ma_dr_wav* pWav, ma_uint64 framesToRead, ma_int32* pBufferOut) -{ - ma_uint64 totalFramesRead; - ma_uint8 sampleData[4096] = {0}; - ma_uint32 bytesPerFrame; - ma_uint32 bytesPerSample; - ma_uint64 samplesRead; - if (pWav->translatedFormatTag == MA_DR_WAVE_FORMAT_PCM && pWav->bitsPerSample == 32) { - return ma_dr_wav_read_pcm_frames(pWav, framesToRead, pBufferOut); - } - bytesPerFrame = ma_dr_wav_get_bytes_per_pcm_frame(pWav); - if (bytesPerFrame == 0) { - return 0; - } - bytesPerSample = bytesPerFrame / pWav->channels; - if (bytesPerSample == 0 || (bytesPerFrame % pWav->channels) != 0) { - return 0; - } - totalFramesRead = 0; - while (framesToRead > 0) { - ma_uint64 framesToReadThisIteration = ma_dr_wav_min(framesToRead, sizeof(sampleData)/bytesPerFrame); - ma_uint64 framesRead = ma_dr_wav_read_pcm_frames(pWav, framesToReadThisIteration, sampleData); - if (framesRead == 0) { - break; - } - MA_DR_WAV_ASSERT(framesRead <= framesToReadThisIteration); - samplesRead = framesRead * pWav->channels; - if ((samplesRead * bytesPerSample) > sizeof(sampleData)) { - MA_DR_WAV_ASSERT(MA_FALSE); - break; - } - ma_dr_wav__pcm_to_s32(pBufferOut, sampleData, (size_t)samplesRead, bytesPerSample); - pBufferOut += samplesRead; - framesToRead -= framesRead; - totalFramesRead += framesRead; - } - return totalFramesRead; -} -MA_PRIVATE ma_uint64 ma_dr_wav_read_pcm_frames_s32__msadpcm_ima(ma_dr_wav* pWav, ma_uint64 framesToRead, ma_int32* pBufferOut) -{ - ma_uint64 totalFramesRead = 0; - ma_int16 samples16[2048]; - while (framesToRead > 0) { - ma_uint64 framesToReadThisIteration = ma_dr_wav_min(framesToRead, ma_dr_wav_countof(samples16)/pWav->channels); - ma_uint64 framesRead = ma_dr_wav_read_pcm_frames_s16(pWav, framesToReadThisIteration, samples16); - if (framesRead == 0) { - break; - } - MA_DR_WAV_ASSERT(framesRead <= framesToReadThisIteration); - ma_dr_wav_s16_to_s32(pBufferOut, samples16, (size_t)(framesRead*pWav->channels)); - pBufferOut += framesRead*pWav->channels; - framesToRead -= framesRead; - totalFramesRead += framesRead; - } - return totalFramesRead; -} -MA_PRIVATE ma_uint64 ma_dr_wav_read_pcm_frames_s32__ieee(ma_dr_wav* pWav, ma_uint64 framesToRead, ma_int32* pBufferOut) -{ - ma_uint64 totalFramesRead; - ma_uint8 sampleData[4096] = {0}; - ma_uint32 bytesPerFrame; - ma_uint32 bytesPerSample; - ma_uint64 samplesRead; - bytesPerFrame = ma_dr_wav_get_bytes_per_pcm_frame(pWav); - if (bytesPerFrame == 0) { - return 0; - } - bytesPerSample = bytesPerFrame / pWav->channels; - if (bytesPerSample == 0 || (bytesPerFrame % pWav->channels) != 0) { - return 0; - } - totalFramesRead = 0; - while (framesToRead > 0) { - ma_uint64 framesToReadThisIteration = ma_dr_wav_min(framesToRead, sizeof(sampleData)/bytesPerFrame); - ma_uint64 framesRead = ma_dr_wav_read_pcm_frames(pWav, framesToReadThisIteration, sampleData); - if (framesRead == 0) { - break; - } - MA_DR_WAV_ASSERT(framesRead <= framesToReadThisIteration); - samplesRead = framesRead * pWav->channels; - if ((samplesRead * bytesPerSample) > sizeof(sampleData)) { - MA_DR_WAV_ASSERT(MA_FALSE); - break; - } - ma_dr_wav__ieee_to_s32(pBufferOut, sampleData, (size_t)samplesRead, bytesPerSample); - pBufferOut += samplesRead; - framesToRead -= framesRead; - totalFramesRead += framesRead; - } - return totalFramesRead; -} -MA_PRIVATE ma_uint64 ma_dr_wav_read_pcm_frames_s32__alaw(ma_dr_wav* pWav, ma_uint64 framesToRead, ma_int32* pBufferOut) -{ - ma_uint64 totalFramesRead; - ma_uint8 sampleData[4096] = {0}; - ma_uint32 bytesPerFrame; - ma_uint32 bytesPerSample; - ma_uint64 samplesRead; - bytesPerFrame = ma_dr_wav_get_bytes_per_pcm_frame(pWav); - if (bytesPerFrame == 0) { - return 0; - } - bytesPerSample = bytesPerFrame / pWav->channels; - if (bytesPerSample == 0 || (bytesPerFrame % pWav->channels) != 0) { - return 0; - } - totalFramesRead = 0; - while (framesToRead > 0) { - ma_uint64 framesToReadThisIteration = ma_dr_wav_min(framesToRead, sizeof(sampleData)/bytesPerFrame); - ma_uint64 framesRead = ma_dr_wav_read_pcm_frames(pWav, framesToReadThisIteration, sampleData); - if (framesRead == 0) { - break; - } - MA_DR_WAV_ASSERT(framesRead <= framesToReadThisIteration); - samplesRead = framesRead * pWav->channels; - if ((samplesRead * bytesPerSample) > sizeof(sampleData)) { - MA_DR_WAV_ASSERT(MA_FALSE); - break; - } - ma_dr_wav_alaw_to_s32(pBufferOut, sampleData, (size_t)samplesRead); - #ifdef MA_DR_WAV_LIBSNDFILE_COMPAT - { - if (pWav->container == ma_dr_wav_container_aiff) { - ma_uint64 iSample; - for (iSample = 0; iSample < samplesRead; iSample += 1) { - pBufferOut[iSample] = -pBufferOut[iSample]; - } - } - } - #endif - pBufferOut += samplesRead; - framesToRead -= framesRead; - totalFramesRead += framesRead; - } - return totalFramesRead; -} -MA_PRIVATE ma_uint64 ma_dr_wav_read_pcm_frames_s32__mulaw(ma_dr_wav* pWav, ma_uint64 framesToRead, ma_int32* pBufferOut) -{ - ma_uint64 totalFramesRead; - ma_uint8 sampleData[4096] = {0}; - ma_uint32 bytesPerFrame; - ma_uint32 bytesPerSample; - ma_uint64 samplesRead; - bytesPerFrame = ma_dr_wav_get_bytes_per_pcm_frame(pWav); - if (bytesPerFrame == 0) { - return 0; - } - bytesPerSample = bytesPerFrame / pWav->channels; - if (bytesPerSample == 0 || (bytesPerFrame % pWav->channels) != 0) { - return 0; - } - totalFramesRead = 0; - while (framesToRead > 0) { - ma_uint64 framesToReadThisIteration = ma_dr_wav_min(framesToRead, sizeof(sampleData)/bytesPerFrame); - ma_uint64 framesRead = ma_dr_wav_read_pcm_frames(pWav, framesToReadThisIteration, sampleData); - if (framesRead == 0) { - break; - } - MA_DR_WAV_ASSERT(framesRead <= framesToReadThisIteration); - samplesRead = framesRead * pWav->channels; - if ((samplesRead * bytesPerSample) > sizeof(sampleData)) { - MA_DR_WAV_ASSERT(MA_FALSE); - break; - } - ma_dr_wav_mulaw_to_s32(pBufferOut, sampleData, (size_t)samplesRead); - #ifdef MA_DR_WAV_LIBSNDFILE_COMPAT - { - if (pWav->container == ma_dr_wav_container_aiff) { - ma_uint64 iSample; - for (iSample = 0; iSample < samplesRead; iSample += 1) { - pBufferOut[iSample] = -pBufferOut[iSample]; - } - } - } - #endif - pBufferOut += samplesRead; - framesToRead -= framesRead; - totalFramesRead += framesRead; - } - return totalFramesRead; -} -MA_API ma_uint64 ma_dr_wav_read_pcm_frames_s32(ma_dr_wav* pWav, ma_uint64 framesToRead, ma_int32* pBufferOut) -{ - if (pWav == NULL || framesToRead == 0) { - return 0; - } - if (pBufferOut == NULL) { - return ma_dr_wav_read_pcm_frames(pWav, framesToRead, NULL); - } - if (framesToRead * pWav->channels * sizeof(ma_int32) > MA_SIZE_MAX) { - framesToRead = MA_SIZE_MAX / sizeof(ma_int32) / pWav->channels; - } - if (pWav->translatedFormatTag == MA_DR_WAVE_FORMAT_PCM) { - return ma_dr_wav_read_pcm_frames_s32__pcm(pWav, framesToRead, pBufferOut); - } - if (pWav->translatedFormatTag == MA_DR_WAVE_FORMAT_ADPCM || pWav->translatedFormatTag == MA_DR_WAVE_FORMAT_DVI_ADPCM) { - return ma_dr_wav_read_pcm_frames_s32__msadpcm_ima(pWav, framesToRead, pBufferOut); - } - if (pWav->translatedFormatTag == MA_DR_WAVE_FORMAT_IEEE_FLOAT) { - return ma_dr_wav_read_pcm_frames_s32__ieee(pWav, framesToRead, pBufferOut); - } - if (pWav->translatedFormatTag == MA_DR_WAVE_FORMAT_ALAW) { - return ma_dr_wav_read_pcm_frames_s32__alaw(pWav, framesToRead, pBufferOut); - } - if (pWav->translatedFormatTag == MA_DR_WAVE_FORMAT_MULAW) { - return ma_dr_wav_read_pcm_frames_s32__mulaw(pWav, framesToRead, pBufferOut); - } - return 0; -} -MA_API ma_uint64 ma_dr_wav_read_pcm_frames_s32le(ma_dr_wav* pWav, ma_uint64 framesToRead, ma_int32* pBufferOut) -{ - ma_uint64 framesRead = ma_dr_wav_read_pcm_frames_s32(pWav, framesToRead, pBufferOut); - if (pBufferOut != NULL && ma_dr_wav__is_little_endian() == MA_FALSE) { - ma_dr_wav__bswap_samples_s32(pBufferOut, framesRead*pWav->channels); - } - return framesRead; -} -MA_API ma_uint64 ma_dr_wav_read_pcm_frames_s32be(ma_dr_wav* pWav, ma_uint64 framesToRead, ma_int32* pBufferOut) -{ - ma_uint64 framesRead = ma_dr_wav_read_pcm_frames_s32(pWav, framesToRead, pBufferOut); - if (pBufferOut != NULL && ma_dr_wav__is_little_endian() == MA_TRUE) { - ma_dr_wav__bswap_samples_s32(pBufferOut, framesRead*pWav->channels); - } - return framesRead; -} -MA_API void ma_dr_wav_u8_to_s32(ma_int32* pOut, const ma_uint8* pIn, size_t sampleCount) -{ - size_t i; - if (pOut == NULL || pIn == NULL) { - return; - } - for (i = 0; i < sampleCount; ++i) { - *pOut++ = ((int)pIn[i] - 128) << 24; - } -} -MA_API void ma_dr_wav_s16_to_s32(ma_int32* pOut, const ma_int16* pIn, size_t sampleCount) -{ - size_t i; - if (pOut == NULL || pIn == NULL) { - return; - } - for (i = 0; i < sampleCount; ++i) { - *pOut++ = pIn[i] << 16; - } -} -MA_API void ma_dr_wav_s24_to_s32(ma_int32* pOut, const ma_uint8* pIn, size_t sampleCount) -{ - size_t i; - if (pOut == NULL || pIn == NULL) { - return; - } - for (i = 0; i < sampleCount; ++i) { - unsigned int s0 = pIn[i*3 + 0]; - unsigned int s1 = pIn[i*3 + 1]; - unsigned int s2 = pIn[i*3 + 2]; - ma_int32 sample32 = (ma_int32)((s0 << 8) | (s1 << 16) | (s2 << 24)); - *pOut++ = sample32; - } -} -MA_API void ma_dr_wav_f32_to_s32(ma_int32* pOut, const float* pIn, size_t sampleCount) -{ - size_t i; - if (pOut == NULL || pIn == NULL) { - return; - } - for (i = 0; i < sampleCount; ++i) { - *pOut++ = (ma_int32)(2147483648.0f * pIn[i]); - } -} -MA_API void ma_dr_wav_f64_to_s32(ma_int32* pOut, const double* pIn, size_t sampleCount) -{ - size_t i; - if (pOut == NULL || pIn == NULL) { - return; - } - for (i = 0; i < sampleCount; ++i) { - *pOut++ = (ma_int32)(2147483648.0 * pIn[i]); - } -} -MA_API void ma_dr_wav_alaw_to_s32(ma_int32* pOut, const ma_uint8* pIn, size_t sampleCount) -{ - size_t i; - if (pOut == NULL || pIn == NULL) { - return; - } - for (i = 0; i < sampleCount; ++i) { - *pOut++ = ((ma_int32)ma_dr_wav__alaw_to_s16(pIn[i])) << 16; - } -} -MA_API void ma_dr_wav_mulaw_to_s32(ma_int32* pOut, const ma_uint8* pIn, size_t sampleCount) -{ - size_t i; - if (pOut == NULL || pIn == NULL) { - return; - } - for (i= 0; i < sampleCount; ++i) { - *pOut++ = ((ma_int32)ma_dr_wav__mulaw_to_s16(pIn[i])) << 16; - } -} -MA_PRIVATE ma_int16* ma_dr_wav__read_pcm_frames_and_close_s16(ma_dr_wav* pWav, unsigned int* channels, unsigned int* sampleRate, ma_uint64* totalFrameCount) -{ - ma_uint64 sampleDataSize; - ma_int16* pSampleData; - ma_uint64 framesRead; - MA_DR_WAV_ASSERT(pWav != NULL); - sampleDataSize = pWav->totalPCMFrameCount * pWav->channels * sizeof(ma_int16); - if (sampleDataSize > MA_SIZE_MAX) { - ma_dr_wav_uninit(pWav); - return NULL; - } - pSampleData = (ma_int16*)ma_dr_wav__malloc_from_callbacks((size_t)sampleDataSize, &pWav->allocationCallbacks); - if (pSampleData == NULL) { - ma_dr_wav_uninit(pWav); - return NULL; - } - framesRead = ma_dr_wav_read_pcm_frames_s16(pWav, (size_t)pWav->totalPCMFrameCount, pSampleData); - if (framesRead != pWav->totalPCMFrameCount) { - ma_dr_wav__free_from_callbacks(pSampleData, &pWav->allocationCallbacks); - ma_dr_wav_uninit(pWav); - return NULL; - } - ma_dr_wav_uninit(pWav); - if (sampleRate) { - *sampleRate = pWav->sampleRate; - } - if (channels) { - *channels = pWav->channels; - } - if (totalFrameCount) { - *totalFrameCount = pWav->totalPCMFrameCount; - } - return pSampleData; -} -MA_PRIVATE float* ma_dr_wav__read_pcm_frames_and_close_f32(ma_dr_wav* pWav, unsigned int* channels, unsigned int* sampleRate, ma_uint64* totalFrameCount) -{ - ma_uint64 sampleDataSize; - float* pSampleData; - ma_uint64 framesRead; - MA_DR_WAV_ASSERT(pWav != NULL); - sampleDataSize = pWav->totalPCMFrameCount * pWav->channels * sizeof(float); - if (sampleDataSize > MA_SIZE_MAX) { - ma_dr_wav_uninit(pWav); - return NULL; - } - pSampleData = (float*)ma_dr_wav__malloc_from_callbacks((size_t)sampleDataSize, &pWav->allocationCallbacks); - if (pSampleData == NULL) { - ma_dr_wav_uninit(pWav); - return NULL; - } - framesRead = ma_dr_wav_read_pcm_frames_f32(pWav, (size_t)pWav->totalPCMFrameCount, pSampleData); - if (framesRead != pWav->totalPCMFrameCount) { - ma_dr_wav__free_from_callbacks(pSampleData, &pWav->allocationCallbacks); - ma_dr_wav_uninit(pWav); - return NULL; - } - ma_dr_wav_uninit(pWav); - if (sampleRate) { - *sampleRate = pWav->sampleRate; - } - if (channels) { - *channels = pWav->channels; - } - if (totalFrameCount) { - *totalFrameCount = pWav->totalPCMFrameCount; - } - return pSampleData; -} -MA_PRIVATE ma_int32* ma_dr_wav__read_pcm_frames_and_close_s32(ma_dr_wav* pWav, unsigned int* channels, unsigned int* sampleRate, ma_uint64* totalFrameCount) -{ - ma_uint64 sampleDataSize; - ma_int32* pSampleData; - ma_uint64 framesRead; - MA_DR_WAV_ASSERT(pWav != NULL); - sampleDataSize = pWav->totalPCMFrameCount * pWav->channels * sizeof(ma_int32); - if (sampleDataSize > MA_SIZE_MAX) { - ma_dr_wav_uninit(pWav); - return NULL; - } - pSampleData = (ma_int32*)ma_dr_wav__malloc_from_callbacks((size_t)sampleDataSize, &pWav->allocationCallbacks); - if (pSampleData == NULL) { - ma_dr_wav_uninit(pWav); - return NULL; - } - framesRead = ma_dr_wav_read_pcm_frames_s32(pWav, (size_t)pWav->totalPCMFrameCount, pSampleData); - if (framesRead != pWav->totalPCMFrameCount) { - ma_dr_wav__free_from_callbacks(pSampleData, &pWav->allocationCallbacks); - ma_dr_wav_uninit(pWav); - return NULL; - } - ma_dr_wav_uninit(pWav); - if (sampleRate) { - *sampleRate = pWav->sampleRate; - } - if (channels) { - *channels = pWav->channels; - } - if (totalFrameCount) { - *totalFrameCount = pWav->totalPCMFrameCount; - } - return pSampleData; -} -MA_API ma_int16* ma_dr_wav_open_and_read_pcm_frames_s16(ma_dr_wav_read_proc onRead, ma_dr_wav_seek_proc onSeek, void* pUserData, unsigned int* channelsOut, unsigned int* sampleRateOut, ma_uint64* totalFrameCountOut, const ma_allocation_callbacks* pAllocationCallbacks) -{ - ma_dr_wav wav; - if (channelsOut) { - *channelsOut = 0; - } - if (sampleRateOut) { - *sampleRateOut = 0; - } - if (totalFrameCountOut) { - *totalFrameCountOut = 0; - } - if (!ma_dr_wav_init(&wav, onRead, onSeek, pUserData, pAllocationCallbacks)) { - return NULL; - } - return ma_dr_wav__read_pcm_frames_and_close_s16(&wav, channelsOut, sampleRateOut, totalFrameCountOut); -} -MA_API float* ma_dr_wav_open_and_read_pcm_frames_f32(ma_dr_wav_read_proc onRead, ma_dr_wav_seek_proc onSeek, void* pUserData, unsigned int* channelsOut, unsigned int* sampleRateOut, ma_uint64* totalFrameCountOut, const ma_allocation_callbacks* pAllocationCallbacks) -{ - ma_dr_wav wav; - if (channelsOut) { - *channelsOut = 0; - } - if (sampleRateOut) { - *sampleRateOut = 0; - } - if (totalFrameCountOut) { - *totalFrameCountOut = 0; - } - if (!ma_dr_wav_init(&wav, onRead, onSeek, pUserData, pAllocationCallbacks)) { - return NULL; - } - return ma_dr_wav__read_pcm_frames_and_close_f32(&wav, channelsOut, sampleRateOut, totalFrameCountOut); -} -MA_API ma_int32* ma_dr_wav_open_and_read_pcm_frames_s32(ma_dr_wav_read_proc onRead, ma_dr_wav_seek_proc onSeek, void* pUserData, unsigned int* channelsOut, unsigned int* sampleRateOut, ma_uint64* totalFrameCountOut, const ma_allocation_callbacks* pAllocationCallbacks) -{ - ma_dr_wav wav; - if (channelsOut) { - *channelsOut = 0; - } - if (sampleRateOut) { - *sampleRateOut = 0; - } - if (totalFrameCountOut) { - *totalFrameCountOut = 0; - } - if (!ma_dr_wav_init(&wav, onRead, onSeek, pUserData, pAllocationCallbacks)) { - return NULL; - } - return ma_dr_wav__read_pcm_frames_and_close_s32(&wav, channelsOut, sampleRateOut, totalFrameCountOut); -} -#ifndef MA_DR_WAV_NO_STDIO -MA_API ma_int16* ma_dr_wav_open_file_and_read_pcm_frames_s16(const char* filename, unsigned int* channelsOut, unsigned int* sampleRateOut, ma_uint64* totalFrameCountOut, const ma_allocation_callbacks* pAllocationCallbacks) -{ - ma_dr_wav wav; - if (channelsOut) { - *channelsOut = 0; - } - if (sampleRateOut) { - *sampleRateOut = 0; - } - if (totalFrameCountOut) { - *totalFrameCountOut = 0; - } - if (!ma_dr_wav_init_file(&wav, filename, pAllocationCallbacks)) { - return NULL; - } - return ma_dr_wav__read_pcm_frames_and_close_s16(&wav, channelsOut, sampleRateOut, totalFrameCountOut); -} -MA_API float* ma_dr_wav_open_file_and_read_pcm_frames_f32(const char* filename, unsigned int* channelsOut, unsigned int* sampleRateOut, ma_uint64* totalFrameCountOut, const ma_allocation_callbacks* pAllocationCallbacks) -{ - ma_dr_wav wav; - if (channelsOut) { - *channelsOut = 0; - } - if (sampleRateOut) { - *sampleRateOut = 0; - } - if (totalFrameCountOut) { - *totalFrameCountOut = 0; - } - if (!ma_dr_wav_init_file(&wav, filename, pAllocationCallbacks)) { - return NULL; - } - return ma_dr_wav__read_pcm_frames_and_close_f32(&wav, channelsOut, sampleRateOut, totalFrameCountOut); -} -MA_API ma_int32* ma_dr_wav_open_file_and_read_pcm_frames_s32(const char* filename, unsigned int* channelsOut, unsigned int* sampleRateOut, ma_uint64* totalFrameCountOut, const ma_allocation_callbacks* pAllocationCallbacks) -{ - ma_dr_wav wav; - if (channelsOut) { - *channelsOut = 0; - } - if (sampleRateOut) { - *sampleRateOut = 0; - } - if (totalFrameCountOut) { - *totalFrameCountOut = 0; - } - if (!ma_dr_wav_init_file(&wav, filename, pAllocationCallbacks)) { - return NULL; - } - return ma_dr_wav__read_pcm_frames_and_close_s32(&wav, channelsOut, sampleRateOut, totalFrameCountOut); -} -#ifndef MA_DR_WAV_NO_WCHAR -MA_API ma_int16* ma_dr_wav_open_file_and_read_pcm_frames_s16_w(const wchar_t* filename, unsigned int* channelsOut, unsigned int* sampleRateOut, ma_uint64* totalFrameCountOut, const ma_allocation_callbacks* pAllocationCallbacks) -{ - ma_dr_wav wav; - if (sampleRateOut) { - *sampleRateOut = 0; - } - if (channelsOut) { - *channelsOut = 0; - } - if (totalFrameCountOut) { - *totalFrameCountOut = 0; - } - if (!ma_dr_wav_init_file_w(&wav, filename, pAllocationCallbacks)) { - return NULL; - } - return ma_dr_wav__read_pcm_frames_and_close_s16(&wav, channelsOut, sampleRateOut, totalFrameCountOut); -} -MA_API float* ma_dr_wav_open_file_and_read_pcm_frames_f32_w(const wchar_t* filename, unsigned int* channelsOut, unsigned int* sampleRateOut, ma_uint64* totalFrameCountOut, const ma_allocation_callbacks* pAllocationCallbacks) -{ - ma_dr_wav wav; - if (sampleRateOut) { - *sampleRateOut = 0; - } - if (channelsOut) { - *channelsOut = 0; - } - if (totalFrameCountOut) { - *totalFrameCountOut = 0; - } - if (!ma_dr_wav_init_file_w(&wav, filename, pAllocationCallbacks)) { - return NULL; - } - return ma_dr_wav__read_pcm_frames_and_close_f32(&wav, channelsOut, sampleRateOut, totalFrameCountOut); -} -MA_API ma_int32* ma_dr_wav_open_file_and_read_pcm_frames_s32_w(const wchar_t* filename, unsigned int* channelsOut, unsigned int* sampleRateOut, ma_uint64* totalFrameCountOut, const ma_allocation_callbacks* pAllocationCallbacks) -{ - ma_dr_wav wav; - if (sampleRateOut) { - *sampleRateOut = 0; - } - if (channelsOut) { - *channelsOut = 0; - } - if (totalFrameCountOut) { - *totalFrameCountOut = 0; - } - if (!ma_dr_wav_init_file_w(&wav, filename, pAllocationCallbacks)) { - return NULL; - } - return ma_dr_wav__read_pcm_frames_and_close_s32(&wav, channelsOut, sampleRateOut, totalFrameCountOut); -} -#endif -#endif -MA_API ma_int16* ma_dr_wav_open_memory_and_read_pcm_frames_s16(const void* data, size_t dataSize, unsigned int* channelsOut, unsigned int* sampleRateOut, ma_uint64* totalFrameCountOut, const ma_allocation_callbacks* pAllocationCallbacks) -{ - ma_dr_wav wav; - if (channelsOut) { - *channelsOut = 0; - } - if (sampleRateOut) { - *sampleRateOut = 0; - } - if (totalFrameCountOut) { - *totalFrameCountOut = 0; - } - if (!ma_dr_wav_init_memory(&wav, data, dataSize, pAllocationCallbacks)) { - return NULL; - } - return ma_dr_wav__read_pcm_frames_and_close_s16(&wav, channelsOut, sampleRateOut, totalFrameCountOut); -} -MA_API float* ma_dr_wav_open_memory_and_read_pcm_frames_f32(const void* data, size_t dataSize, unsigned int* channelsOut, unsigned int* sampleRateOut, ma_uint64* totalFrameCountOut, const ma_allocation_callbacks* pAllocationCallbacks) -{ - ma_dr_wav wav; - if (channelsOut) { - *channelsOut = 0; - } - if (sampleRateOut) { - *sampleRateOut = 0; - } - if (totalFrameCountOut) { - *totalFrameCountOut = 0; - } - if (!ma_dr_wav_init_memory(&wav, data, dataSize, pAllocationCallbacks)) { - return NULL; - } - return ma_dr_wav__read_pcm_frames_and_close_f32(&wav, channelsOut, sampleRateOut, totalFrameCountOut); -} -MA_API ma_int32* ma_dr_wav_open_memory_and_read_pcm_frames_s32(const void* data, size_t dataSize, unsigned int* channelsOut, unsigned int* sampleRateOut, ma_uint64* totalFrameCountOut, const ma_allocation_callbacks* pAllocationCallbacks) -{ - ma_dr_wav wav; - if (channelsOut) { - *channelsOut = 0; - } - if (sampleRateOut) { - *sampleRateOut = 0; - } - if (totalFrameCountOut) { - *totalFrameCountOut = 0; - } - if (!ma_dr_wav_init_memory(&wav, data, dataSize, pAllocationCallbacks)) { - return NULL; - } - return ma_dr_wav__read_pcm_frames_and_close_s32(&wav, channelsOut, sampleRateOut, totalFrameCountOut); -} -#endif -MA_API void ma_dr_wav_free(void* p, const ma_allocation_callbacks* pAllocationCallbacks) -{ - if (pAllocationCallbacks != NULL) { - ma_dr_wav__free_from_callbacks(p, pAllocationCallbacks); - } else { - ma_dr_wav__free_default(p, NULL); - } -} -MA_API ma_uint16 ma_dr_wav_bytes_to_u16(const ma_uint8* data) -{ - return ((ma_uint16)data[0] << 0) | ((ma_uint16)data[1] << 8); -} -MA_API ma_int16 ma_dr_wav_bytes_to_s16(const ma_uint8* data) -{ - return (ma_int16)ma_dr_wav_bytes_to_u16(data); -} -MA_API ma_uint32 ma_dr_wav_bytes_to_u32(const ma_uint8* data) -{ - return ma_dr_wav_bytes_to_u32_le(data); -} -MA_API float ma_dr_wav_bytes_to_f32(const ma_uint8* data) -{ - union { - ma_uint32 u32; - float f32; - } value; - value.u32 = ma_dr_wav_bytes_to_u32(data); - return value.f32; -} -MA_API ma_int32 ma_dr_wav_bytes_to_s32(const ma_uint8* data) -{ - return (ma_int32)ma_dr_wav_bytes_to_u32(data); -} -MA_API ma_uint64 ma_dr_wav_bytes_to_u64(const ma_uint8* data) -{ - return - ((ma_uint64)data[0] << 0) | ((ma_uint64)data[1] << 8) | ((ma_uint64)data[2] << 16) | ((ma_uint64)data[3] << 24) | - ((ma_uint64)data[4] << 32) | ((ma_uint64)data[5] << 40) | ((ma_uint64)data[6] << 48) | ((ma_uint64)data[7] << 56); -} -MA_API ma_int64 ma_dr_wav_bytes_to_s64(const ma_uint8* data) -{ - return (ma_int64)ma_dr_wav_bytes_to_u64(data); -} -MA_API ma_bool32 ma_dr_wav_guid_equal(const ma_uint8 a[16], const ma_uint8 b[16]) -{ - int i; - for (i = 0; i < 16; i += 1) { - if (a[i] != b[i]) { - return MA_FALSE; - } - } - return MA_TRUE; -} -MA_API ma_bool32 ma_dr_wav_fourcc_equal(const ma_uint8* a, const char* b) -{ - return - a[0] == b[0] && - a[1] == b[1] && - a[2] == b[2] && - a[3] == b[3]; -} -#ifdef __MRC__ -#pragma options opt reset -#endif -#endif -/* dr_wav_c end */ -#endif /* MA_DR_WAV_IMPLEMENTATION */ -#endif /* MA_NO_WAV */ - -#if !defined(MA_NO_FLAC) && !defined(MA_NO_DECODING) -#if !defined(MA_DR_FLAC_IMPLEMENTATION) -/* dr_flac_c begin */ -#ifndef ma_dr_flac_c -#define ma_dr_flac_c -#if defined(__clang__) || (defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6))) - #pragma GCC diagnostic push - #if __GNUC__ >= 7 - #pragma GCC diagnostic ignored "-Wimplicit-fallthrough" - #endif -#endif -#ifdef __linux__ - #ifndef _BSD_SOURCE - #define _BSD_SOURCE - #endif - #ifndef _DEFAULT_SOURCE - #define _DEFAULT_SOURCE - #endif - #ifndef __USE_BSD - #define __USE_BSD - #endif - #include -#endif -#include -#include -#if !defined(MA_DR_FLAC_NO_SIMD) - #if defined(MA_X64) || defined(MA_X86) - #if defined(_MSC_VER) && !defined(__clang__) - #if _MSC_VER >= 1400 && !defined(MA_DR_FLAC_NO_SSE2) - #define MA_DR_FLAC_SUPPORT_SSE2 - #endif - #if _MSC_VER >= 1600 && !defined(MA_DR_FLAC_NO_SSE41) - #define MA_DR_FLAC_SUPPORT_SSE41 - #endif - #elif defined(__clang__) || (defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 3))) - #if defined(__SSE2__) && !defined(MA_DR_FLAC_NO_SSE2) - #define MA_DR_FLAC_SUPPORT_SSE2 - #endif - #if defined(__SSE4_1__) && !defined(MA_DR_FLAC_NO_SSE41) - #define MA_DR_FLAC_SUPPORT_SSE41 - #endif - #endif - #if !defined(__GNUC__) && !defined(__clang__) && defined(__has_include) - #if !defined(MA_DR_FLAC_SUPPORT_SSE2) && !defined(MA_DR_FLAC_NO_SSE2) && __has_include() - #define MA_DR_FLAC_SUPPORT_SSE2 - #endif - #if !defined(MA_DR_FLAC_SUPPORT_SSE41) && !defined(MA_DR_FLAC_NO_SSE41) && __has_include() - #define MA_DR_FLAC_SUPPORT_SSE41 - #endif - #endif - #if defined(MA_DR_FLAC_SUPPORT_SSE41) - #include - #elif defined(MA_DR_FLAC_SUPPORT_SSE2) - #include - #endif - #endif - #if defined(MA_ARM) - #if !defined(MA_DR_FLAC_NO_NEON) && (defined(__ARM_NEON) || defined(__aarch64__) || defined(_M_ARM64)) - #define MA_DR_FLAC_SUPPORT_NEON - #include - #endif - #endif -#endif -#if !defined(MA_DR_FLAC_NO_SIMD) && (defined(MA_X86) || defined(MA_X64)) - #if defined(_MSC_VER) && !defined(__clang__) - #if _MSC_VER >= 1400 - #include - static void ma_dr_flac__cpuid(int info[4], int fid) - { - __cpuid(info, fid); - } - #else - #define MA_DR_FLAC_NO_CPUID - #endif - #else - #if defined(__GNUC__) || defined(__clang__) - static void ma_dr_flac__cpuid(int info[4], int fid) - { - #if defined(MA_X86) && defined(__PIC__) - __asm__ __volatile__ ( - "xchg{l} {%%}ebx, %k1;" - "cpuid;" - "xchg{l} {%%}ebx, %k1;" - : "=a"(info[0]), "=&r"(info[1]), "=c"(info[2]), "=d"(info[3]) : "a"(fid), "c"(0) - ); - #else - __asm__ __volatile__ ( - "cpuid" : "=a"(info[0]), "=b"(info[1]), "=c"(info[2]), "=d"(info[3]) : "a"(fid), "c"(0) - ); - #endif - } - #else - #define MA_DR_FLAC_NO_CPUID - #endif - #endif -#else - #define MA_DR_FLAC_NO_CPUID -#endif -static MA_INLINE ma_bool32 ma_dr_flac_has_sse2(void) -{ -#if defined(MA_DR_FLAC_SUPPORT_SSE2) - #if (defined(MA_X64) || defined(MA_X86)) && !defined(MA_DR_FLAC_NO_SSE2) - #if defined(MA_X64) - return MA_TRUE; - #elif (defined(_M_IX86_FP) && _M_IX86_FP == 2) || defined(__SSE2__) - return MA_TRUE; - #else - #if defined(MA_DR_FLAC_NO_CPUID) - return MA_FALSE; - #else - int info[4]; - ma_dr_flac__cpuid(info, 1); - return (info[3] & (1 << 26)) != 0; - #endif - #endif - #else - return MA_FALSE; - #endif -#else - return MA_FALSE; -#endif -} -static MA_INLINE ma_bool32 ma_dr_flac_has_sse41(void) -{ -#if defined(MA_DR_FLAC_SUPPORT_SSE41) - #if (defined(MA_X64) || defined(MA_X86)) && !defined(MA_DR_FLAC_NO_SSE41) - #if defined(__SSE4_1__) || defined(__AVX__) - return MA_TRUE; - #else - #if defined(MA_DR_FLAC_NO_CPUID) - return MA_FALSE; - #else - int info[4]; - ma_dr_flac__cpuid(info, 1); - return (info[2] & (1 << 19)) != 0; - #endif - #endif - #else - return MA_FALSE; - #endif -#else - return MA_FALSE; -#endif -} -#if defined(_MSC_VER) && _MSC_VER >= 1500 && (defined(MA_X86) || defined(MA_X64)) && !defined(__clang__) - #define MA_DR_FLAC_HAS_LZCNT_INTRINSIC -#elif (defined(__GNUC__) && ((__GNUC__ > 4) || (__GNUC__ == 4 && __GNUC_MINOR__ >= 7))) - #define MA_DR_FLAC_HAS_LZCNT_INTRINSIC -#elif defined(__clang__) - #if defined(__has_builtin) - #if __has_builtin(__builtin_clzll) || __has_builtin(__builtin_clzl) - #define MA_DR_FLAC_HAS_LZCNT_INTRINSIC - #endif - #endif -#endif -#if defined(_MSC_VER) && _MSC_VER >= 1400 && !defined(__clang__) - #define MA_DR_FLAC_HAS_BYTESWAP16_INTRINSIC - #define MA_DR_FLAC_HAS_BYTESWAP32_INTRINSIC - #define MA_DR_FLAC_HAS_BYTESWAP64_INTRINSIC -#elif defined(__clang__) - #if defined(__has_builtin) - #if __has_builtin(__builtin_bswap16) - #define MA_DR_FLAC_HAS_BYTESWAP16_INTRINSIC - #endif - #if __has_builtin(__builtin_bswap32) - #define MA_DR_FLAC_HAS_BYTESWAP32_INTRINSIC - #endif - #if __has_builtin(__builtin_bswap64) - #define MA_DR_FLAC_HAS_BYTESWAP64_INTRINSIC - #endif - #endif -#elif defined(__GNUC__) - #if ((__GNUC__ > 4) || (__GNUC__ == 4 && __GNUC_MINOR__ >= 3)) - #define MA_DR_FLAC_HAS_BYTESWAP32_INTRINSIC - #define MA_DR_FLAC_HAS_BYTESWAP64_INTRINSIC - #endif - #if ((__GNUC__ > 4) || (__GNUC__ == 4 && __GNUC_MINOR__ >= 8)) - #define MA_DR_FLAC_HAS_BYTESWAP16_INTRINSIC - #endif -#elif defined(__WATCOMC__) && defined(__386__) - #define MA_DR_FLAC_HAS_BYTESWAP16_INTRINSIC - #define MA_DR_FLAC_HAS_BYTESWAP32_INTRINSIC - #define MA_DR_FLAC_HAS_BYTESWAP64_INTRINSIC - extern __inline ma_uint16 _watcom_bswap16(ma_uint16); - extern __inline ma_uint32 _watcom_bswap32(ma_uint32); - extern __inline ma_uint64 _watcom_bswap64(ma_uint64); -#pragma aux _watcom_bswap16 = \ - "xchg al, ah" \ - parm [ax] \ - value [ax] \ - modify nomemory; -#pragma aux _watcom_bswap32 = \ - "bswap eax" \ - parm [eax] \ - value [eax] \ - modify nomemory; -#pragma aux _watcom_bswap64 = \ - "bswap eax" \ - "bswap edx" \ - "xchg eax,edx" \ - parm [eax edx] \ - value [eax edx] \ - modify nomemory; -#endif -#ifndef MA_DR_FLAC_ASSERT -#include -#define MA_DR_FLAC_ASSERT(expression) assert(expression) -#endif -#ifndef MA_DR_FLAC_MALLOC -#define MA_DR_FLAC_MALLOC(sz) malloc((sz)) -#endif -#ifndef MA_DR_FLAC_REALLOC -#define MA_DR_FLAC_REALLOC(p, sz) realloc((p), (sz)) -#endif -#ifndef MA_DR_FLAC_FREE -#define MA_DR_FLAC_FREE(p) free((p)) -#endif -#ifndef MA_DR_FLAC_COPY_MEMORY -#define MA_DR_FLAC_COPY_MEMORY(dst, src, sz) memcpy((dst), (src), (sz)) -#endif -#ifndef MA_DR_FLAC_ZERO_MEMORY -#define MA_DR_FLAC_ZERO_MEMORY(p, sz) memset((p), 0, (sz)) -#endif -#ifndef MA_DR_FLAC_ZERO_OBJECT -#define MA_DR_FLAC_ZERO_OBJECT(p) MA_DR_FLAC_ZERO_MEMORY((p), sizeof(*(p))) -#endif -#define MA_DR_FLAC_MAX_SIMD_VECTOR_SIZE 64 -#define MA_DR_FLAC_SUBFRAME_CONSTANT 0 -#define MA_DR_FLAC_SUBFRAME_VERBATIM 1 -#define MA_DR_FLAC_SUBFRAME_FIXED 8 -#define MA_DR_FLAC_SUBFRAME_LPC 32 -#define MA_DR_FLAC_SUBFRAME_RESERVED 255 -#define MA_DR_FLAC_RESIDUAL_CODING_METHOD_PARTITIONED_RICE 0 -#define MA_DR_FLAC_RESIDUAL_CODING_METHOD_PARTITIONED_RICE2 1 -#define MA_DR_FLAC_CHANNEL_ASSIGNMENT_INDEPENDENT 0 -#define MA_DR_FLAC_CHANNEL_ASSIGNMENT_LEFT_SIDE 8 -#define MA_DR_FLAC_CHANNEL_ASSIGNMENT_RIGHT_SIDE 9 -#define MA_DR_FLAC_CHANNEL_ASSIGNMENT_MID_SIDE 10 -#define MA_DR_FLAC_SEEKPOINT_SIZE_IN_BYTES 18 -#define MA_DR_FLAC_CUESHEET_TRACK_SIZE_IN_BYTES 36 -#define MA_DR_FLAC_CUESHEET_TRACK_INDEX_SIZE_IN_BYTES 12 -#define ma_dr_flac_align(x, a) ((((x) + (a) - 1) / (a)) * (a)) -MA_API void ma_dr_flac_version(ma_uint32* pMajor, ma_uint32* pMinor, ma_uint32* pRevision) -{ - if (pMajor) { - *pMajor = MA_DR_FLAC_VERSION_MAJOR; - } - if (pMinor) { - *pMinor = MA_DR_FLAC_VERSION_MINOR; - } - if (pRevision) { - *pRevision = MA_DR_FLAC_VERSION_REVISION; - } -} -MA_API const char* ma_dr_flac_version_string(void) -{ - return MA_DR_FLAC_VERSION_STRING; -} -#if defined(__has_feature) - #if __has_feature(thread_sanitizer) - #define MA_DR_FLAC_NO_THREAD_SANITIZE __attribute__((no_sanitize("thread"))) - #else - #define MA_DR_FLAC_NO_THREAD_SANITIZE - #endif -#else - #define MA_DR_FLAC_NO_THREAD_SANITIZE -#endif -#if defined(MA_DR_FLAC_HAS_LZCNT_INTRINSIC) -static ma_bool32 ma_dr_flac__gIsLZCNTSupported = MA_FALSE; -#endif -#ifndef MA_DR_FLAC_NO_CPUID -static ma_bool32 ma_dr_flac__gIsSSE2Supported = MA_FALSE; -static ma_bool32 ma_dr_flac__gIsSSE41Supported = MA_FALSE; -MA_DR_FLAC_NO_THREAD_SANITIZE static void ma_dr_flac__init_cpu_caps(void) -{ - static ma_bool32 isCPUCapsInitialized = MA_FALSE; - if (!isCPUCapsInitialized) { -#if defined(MA_DR_FLAC_HAS_LZCNT_INTRINSIC) - int info[4] = {0}; - ma_dr_flac__cpuid(info, 0x80000001); - ma_dr_flac__gIsLZCNTSupported = (info[2] & (1 << 5)) != 0; -#endif - ma_dr_flac__gIsSSE2Supported = ma_dr_flac_has_sse2(); - ma_dr_flac__gIsSSE41Supported = ma_dr_flac_has_sse41(); - isCPUCapsInitialized = MA_TRUE; - } -} -#else -static ma_bool32 ma_dr_flac__gIsNEONSupported = MA_FALSE; -static MA_INLINE ma_bool32 ma_dr_flac__has_neon(void) -{ -#if defined(MA_DR_FLAC_SUPPORT_NEON) - #if defined(MA_ARM) && !defined(MA_DR_FLAC_NO_NEON) - #if (defined(__ARM_NEON) || defined(__aarch64__) || defined(_M_ARM64)) - return MA_TRUE; - #else - return MA_FALSE; - #endif - #else - return MA_FALSE; - #endif -#else - return MA_FALSE; -#endif -} -MA_DR_FLAC_NO_THREAD_SANITIZE static void ma_dr_flac__init_cpu_caps(void) -{ - ma_dr_flac__gIsNEONSupported = ma_dr_flac__has_neon(); -#if defined(MA_DR_FLAC_HAS_LZCNT_INTRINSIC) && defined(MA_ARM) && (defined(__ARM_ARCH) && __ARM_ARCH >= 5) - ma_dr_flac__gIsLZCNTSupported = MA_TRUE; -#endif -} -#endif -static MA_INLINE ma_bool32 ma_dr_flac__is_little_endian(void) -{ -#if defined(MA_X86) || defined(MA_X64) - return MA_TRUE; -#elif defined(__BYTE_ORDER) && defined(__LITTLE_ENDIAN) && __BYTE_ORDER == __LITTLE_ENDIAN - return MA_TRUE; -#else - int n = 1; - return (*(char*)&n) == 1; -#endif -} -static MA_INLINE ma_uint16 ma_dr_flac__swap_endian_uint16(ma_uint16 n) -{ -#ifdef MA_DR_FLAC_HAS_BYTESWAP16_INTRINSIC - #if defined(_MSC_VER) && !defined(__clang__) - return _byteswap_ushort(n); - #elif defined(__GNUC__) || defined(__clang__) - return __builtin_bswap16(n); - #elif defined(__WATCOMC__) && defined(__386__) - return _watcom_bswap16(n); - #else - #error "This compiler does not support the byte swap intrinsic." - #endif -#else - return ((n & 0xFF00) >> 8) | - ((n & 0x00FF) << 8); -#endif -} -static MA_INLINE ma_uint32 ma_dr_flac__swap_endian_uint32(ma_uint32 n) -{ -#ifdef MA_DR_FLAC_HAS_BYTESWAP32_INTRINSIC - #if defined(_MSC_VER) && !defined(__clang__) - return _byteswap_ulong(n); - #elif defined(__GNUC__) || defined(__clang__) - #if defined(MA_ARM) && (defined(__ARM_ARCH) && __ARM_ARCH >= 6) && !defined(__ARM_ARCH_6M__) && !defined(MA_64BIT) - ma_uint32 r; - __asm__ __volatile__ ( - #if defined(MA_64BIT) - "rev %w[out], %w[in]" : [out]"=r"(r) : [in]"r"(n) - #else - "rev %[out], %[in]" : [out]"=r"(r) : [in]"r"(n) - #endif - ); - return r; - #else - return __builtin_bswap32(n); - #endif - #elif defined(__WATCOMC__) && defined(__386__) - return _watcom_bswap32(n); - #else - #error "This compiler does not support the byte swap intrinsic." - #endif -#else - return ((n & 0xFF000000) >> 24) | - ((n & 0x00FF0000) >> 8) | - ((n & 0x0000FF00) << 8) | - ((n & 0x000000FF) << 24); -#endif -} -static MA_INLINE ma_uint64 ma_dr_flac__swap_endian_uint64(ma_uint64 n) -{ -#ifdef MA_DR_FLAC_HAS_BYTESWAP64_INTRINSIC - #if defined(_MSC_VER) && !defined(__clang__) - return _byteswap_uint64(n); - #elif defined(__GNUC__) || defined(__clang__) - return __builtin_bswap64(n); - #elif defined(__WATCOMC__) && defined(__386__) - return _watcom_bswap64(n); - #else - #error "This compiler does not support the byte swap intrinsic." - #endif -#else - return ((n & ((ma_uint64)0xFF000000 << 32)) >> 56) | - ((n & ((ma_uint64)0x00FF0000 << 32)) >> 40) | - ((n & ((ma_uint64)0x0000FF00 << 32)) >> 24) | - ((n & ((ma_uint64)0x000000FF << 32)) >> 8) | - ((n & ((ma_uint64)0xFF000000 )) << 8) | - ((n & ((ma_uint64)0x00FF0000 )) << 24) | - ((n & ((ma_uint64)0x0000FF00 )) << 40) | - ((n & ((ma_uint64)0x000000FF )) << 56); -#endif -} -static MA_INLINE ma_uint16 ma_dr_flac__be2host_16(ma_uint16 n) -{ - if (ma_dr_flac__is_little_endian()) { - return ma_dr_flac__swap_endian_uint16(n); - } - return n; -} -static MA_INLINE ma_uint32 ma_dr_flac__be2host_32(ma_uint32 n) -{ - if (ma_dr_flac__is_little_endian()) { - return ma_dr_flac__swap_endian_uint32(n); - } - return n; -} -static MA_INLINE ma_uint32 ma_dr_flac__be2host_32_ptr_unaligned(const void* pData) -{ - const ma_uint8* pNum = (ma_uint8*)pData; - return *(pNum) << 24 | *(pNum+1) << 16 | *(pNum+2) << 8 | *(pNum+3); -} -static MA_INLINE ma_uint64 ma_dr_flac__be2host_64(ma_uint64 n) -{ - if (ma_dr_flac__is_little_endian()) { - return ma_dr_flac__swap_endian_uint64(n); - } - return n; -} -static MA_INLINE ma_uint32 ma_dr_flac__le2host_32(ma_uint32 n) -{ - if (!ma_dr_flac__is_little_endian()) { - return ma_dr_flac__swap_endian_uint32(n); - } - return n; -} -static MA_INLINE ma_uint32 ma_dr_flac__le2host_32_ptr_unaligned(const void* pData) -{ - const ma_uint8* pNum = (ma_uint8*)pData; - return *pNum | *(pNum+1) << 8 | *(pNum+2) << 16 | *(pNum+3) << 24; -} -static MA_INLINE ma_uint32 ma_dr_flac__unsynchsafe_32(ma_uint32 n) -{ - ma_uint32 result = 0; - result |= (n & 0x7F000000) >> 3; - result |= (n & 0x007F0000) >> 2; - result |= (n & 0x00007F00) >> 1; - result |= (n & 0x0000007F) >> 0; - return result; -} -static ma_uint8 ma_dr_flac__crc8_table[] = { - 0x00, 0x07, 0x0E, 0x09, 0x1C, 0x1B, 0x12, 0x15, 0x38, 0x3F, 0x36, 0x31, 0x24, 0x23, 0x2A, 0x2D, - 0x70, 0x77, 0x7E, 0x79, 0x6C, 0x6B, 0x62, 0x65, 0x48, 0x4F, 0x46, 0x41, 0x54, 0x53, 0x5A, 0x5D, - 0xE0, 0xE7, 0xEE, 0xE9, 0xFC, 0xFB, 0xF2, 0xF5, 0xD8, 0xDF, 0xD6, 0xD1, 0xC4, 0xC3, 0xCA, 0xCD, - 0x90, 0x97, 0x9E, 0x99, 0x8C, 0x8B, 0x82, 0x85, 0xA8, 0xAF, 0xA6, 0xA1, 0xB4, 0xB3, 0xBA, 0xBD, - 0xC7, 0xC0, 0xC9, 0xCE, 0xDB, 0xDC, 0xD5, 0xD2, 0xFF, 0xF8, 0xF1, 0xF6, 0xE3, 0xE4, 0xED, 0xEA, - 0xB7, 0xB0, 0xB9, 0xBE, 0xAB, 0xAC, 0xA5, 0xA2, 0x8F, 0x88, 0x81, 0x86, 0x93, 0x94, 0x9D, 0x9A, - 0x27, 0x20, 0x29, 0x2E, 0x3B, 0x3C, 0x35, 0x32, 0x1F, 0x18, 0x11, 0x16, 0x03, 0x04, 0x0D, 0x0A, - 0x57, 0x50, 0x59, 0x5E, 0x4B, 0x4C, 0x45, 0x42, 0x6F, 0x68, 0x61, 0x66, 0x73, 0x74, 0x7D, 0x7A, - 0x89, 0x8E, 0x87, 0x80, 0x95, 0x92, 0x9B, 0x9C, 0xB1, 0xB6, 0xBF, 0xB8, 0xAD, 0xAA, 0xA3, 0xA4, - 0xF9, 0xFE, 0xF7, 0xF0, 0xE5, 0xE2, 0xEB, 0xEC, 0xC1, 0xC6, 0xCF, 0xC8, 0xDD, 0xDA, 0xD3, 0xD4, - 0x69, 0x6E, 0x67, 0x60, 0x75, 0x72, 0x7B, 0x7C, 0x51, 0x56, 0x5F, 0x58, 0x4D, 0x4A, 0x43, 0x44, - 0x19, 0x1E, 0x17, 0x10, 0x05, 0x02, 0x0B, 0x0C, 0x21, 0x26, 0x2F, 0x28, 0x3D, 0x3A, 0x33, 0x34, - 0x4E, 0x49, 0x40, 0x47, 0x52, 0x55, 0x5C, 0x5B, 0x76, 0x71, 0x78, 0x7F, 0x6A, 0x6D, 0x64, 0x63, - 0x3E, 0x39, 0x30, 0x37, 0x22, 0x25, 0x2C, 0x2B, 0x06, 0x01, 0x08, 0x0F, 0x1A, 0x1D, 0x14, 0x13, - 0xAE, 0xA9, 0xA0, 0xA7, 0xB2, 0xB5, 0xBC, 0xBB, 0x96, 0x91, 0x98, 0x9F, 0x8A, 0x8D, 0x84, 0x83, - 0xDE, 0xD9, 0xD0, 0xD7, 0xC2, 0xC5, 0xCC, 0xCB, 0xE6, 0xE1, 0xE8, 0xEF, 0xFA, 0xFD, 0xF4, 0xF3 -}; -static ma_uint16 ma_dr_flac__crc16_table[] = { - 0x0000, 0x8005, 0x800F, 0x000A, 0x801B, 0x001E, 0x0014, 0x8011, - 0x8033, 0x0036, 0x003C, 0x8039, 0x0028, 0x802D, 0x8027, 0x0022, - 0x8063, 0x0066, 0x006C, 0x8069, 0x0078, 0x807D, 0x8077, 0x0072, - 0x0050, 0x8055, 0x805F, 0x005A, 0x804B, 0x004E, 0x0044, 0x8041, - 0x80C3, 0x00C6, 0x00CC, 0x80C9, 0x00D8, 0x80DD, 0x80D7, 0x00D2, - 0x00F0, 0x80F5, 0x80FF, 0x00FA, 0x80EB, 0x00EE, 0x00E4, 0x80E1, - 0x00A0, 0x80A5, 0x80AF, 0x00AA, 0x80BB, 0x00BE, 0x00B4, 0x80B1, - 0x8093, 0x0096, 0x009C, 0x8099, 0x0088, 0x808D, 0x8087, 0x0082, - 0x8183, 0x0186, 0x018C, 0x8189, 0x0198, 0x819D, 0x8197, 0x0192, - 0x01B0, 0x81B5, 0x81BF, 0x01BA, 0x81AB, 0x01AE, 0x01A4, 0x81A1, - 0x01E0, 0x81E5, 0x81EF, 0x01EA, 0x81FB, 0x01FE, 0x01F4, 0x81F1, - 0x81D3, 0x01D6, 0x01DC, 0x81D9, 0x01C8, 0x81CD, 0x81C7, 0x01C2, - 0x0140, 0x8145, 0x814F, 0x014A, 0x815B, 0x015E, 0x0154, 0x8151, - 0x8173, 0x0176, 0x017C, 0x8179, 0x0168, 0x816D, 0x8167, 0x0162, - 0x8123, 0x0126, 0x012C, 0x8129, 0x0138, 0x813D, 0x8137, 0x0132, - 0x0110, 0x8115, 0x811F, 0x011A, 0x810B, 0x010E, 0x0104, 0x8101, - 0x8303, 0x0306, 0x030C, 0x8309, 0x0318, 0x831D, 0x8317, 0x0312, - 0x0330, 0x8335, 0x833F, 0x033A, 0x832B, 0x032E, 0x0324, 0x8321, - 0x0360, 0x8365, 0x836F, 0x036A, 0x837B, 0x037E, 0x0374, 0x8371, - 0x8353, 0x0356, 0x035C, 0x8359, 0x0348, 0x834D, 0x8347, 0x0342, - 0x03C0, 0x83C5, 0x83CF, 0x03CA, 0x83DB, 0x03DE, 0x03D4, 0x83D1, - 0x83F3, 0x03F6, 0x03FC, 0x83F9, 0x03E8, 0x83ED, 0x83E7, 0x03E2, - 0x83A3, 0x03A6, 0x03AC, 0x83A9, 0x03B8, 0x83BD, 0x83B7, 0x03B2, - 0x0390, 0x8395, 0x839F, 0x039A, 0x838B, 0x038E, 0x0384, 0x8381, - 0x0280, 0x8285, 0x828F, 0x028A, 0x829B, 0x029E, 0x0294, 0x8291, - 0x82B3, 0x02B6, 0x02BC, 0x82B9, 0x02A8, 0x82AD, 0x82A7, 0x02A2, - 0x82E3, 0x02E6, 0x02EC, 0x82E9, 0x02F8, 0x82FD, 0x82F7, 0x02F2, - 0x02D0, 0x82D5, 0x82DF, 0x02DA, 0x82CB, 0x02CE, 0x02C4, 0x82C1, - 0x8243, 0x0246, 0x024C, 0x8249, 0x0258, 0x825D, 0x8257, 0x0252, - 0x0270, 0x8275, 0x827F, 0x027A, 0x826B, 0x026E, 0x0264, 0x8261, - 0x0220, 0x8225, 0x822F, 0x022A, 0x823B, 0x023E, 0x0234, 0x8231, - 0x8213, 0x0216, 0x021C, 0x8219, 0x0208, 0x820D, 0x8207, 0x0202 -}; -static MA_INLINE ma_uint8 ma_dr_flac_crc8_byte(ma_uint8 crc, ma_uint8 data) -{ - return ma_dr_flac__crc8_table[crc ^ data]; -} -static MA_INLINE ma_uint8 ma_dr_flac_crc8(ma_uint8 crc, ma_uint32 data, ma_uint32 count) -{ -#ifdef MA_DR_FLAC_NO_CRC - (void)crc; - (void)data; - (void)count; - return 0; -#else -#if 0 - ma_uint8 p = 0x07; - for (int i = count-1; i >= 0; --i) { - ma_uint8 bit = (data & (1 << i)) >> i; - if (crc & 0x80) { - crc = ((crc << 1) | bit) ^ p; - } else { - crc = ((crc << 1) | bit); - } - } - return crc; -#else - ma_uint32 wholeBytes; - ma_uint32 leftoverBits; - ma_uint64 leftoverDataMask; - static ma_uint64 leftoverDataMaskTable[8] = { - 0x00, 0x01, 0x03, 0x07, 0x0F, 0x1F, 0x3F, 0x7F - }; - MA_DR_FLAC_ASSERT(count <= 32); - wholeBytes = count >> 3; - leftoverBits = count - (wholeBytes*8); - leftoverDataMask = leftoverDataMaskTable[leftoverBits]; - switch (wholeBytes) { - case 4: crc = ma_dr_flac_crc8_byte(crc, (ma_uint8)((data & (0xFF000000UL << leftoverBits)) >> (24 + leftoverBits))); - case 3: crc = ma_dr_flac_crc8_byte(crc, (ma_uint8)((data & (0x00FF0000UL << leftoverBits)) >> (16 + leftoverBits))); - case 2: crc = ma_dr_flac_crc8_byte(crc, (ma_uint8)((data & (0x0000FF00UL << leftoverBits)) >> ( 8 + leftoverBits))); - case 1: crc = ma_dr_flac_crc8_byte(crc, (ma_uint8)((data & (0x000000FFUL << leftoverBits)) >> ( 0 + leftoverBits))); - case 0: if (leftoverBits > 0) crc = (ma_uint8)((crc << leftoverBits) ^ ma_dr_flac__crc8_table[(crc >> (8 - leftoverBits)) ^ (data & leftoverDataMask)]); - } - return crc; -#endif -#endif -} -static MA_INLINE ma_uint16 ma_dr_flac_crc16_byte(ma_uint16 crc, ma_uint8 data) -{ - return (crc << 8) ^ ma_dr_flac__crc16_table[(ma_uint8)(crc >> 8) ^ data]; -} -static MA_INLINE ma_uint16 ma_dr_flac_crc16_cache(ma_uint16 crc, ma_dr_flac_cache_t data) -{ -#ifdef MA_64BIT - crc = ma_dr_flac_crc16_byte(crc, (ma_uint8)((data >> 56) & 0xFF)); - crc = ma_dr_flac_crc16_byte(crc, (ma_uint8)((data >> 48) & 0xFF)); - crc = ma_dr_flac_crc16_byte(crc, (ma_uint8)((data >> 40) & 0xFF)); - crc = ma_dr_flac_crc16_byte(crc, (ma_uint8)((data >> 32) & 0xFF)); -#endif - crc = ma_dr_flac_crc16_byte(crc, (ma_uint8)((data >> 24) & 0xFF)); - crc = ma_dr_flac_crc16_byte(crc, (ma_uint8)((data >> 16) & 0xFF)); - crc = ma_dr_flac_crc16_byte(crc, (ma_uint8)((data >> 8) & 0xFF)); - crc = ma_dr_flac_crc16_byte(crc, (ma_uint8)((data >> 0) & 0xFF)); - return crc; -} -static MA_INLINE ma_uint16 ma_dr_flac_crc16_bytes(ma_uint16 crc, ma_dr_flac_cache_t data, ma_uint32 byteCount) -{ - switch (byteCount) - { -#ifdef MA_64BIT - case 8: crc = ma_dr_flac_crc16_byte(crc, (ma_uint8)((data >> 56) & 0xFF)); - case 7: crc = ma_dr_flac_crc16_byte(crc, (ma_uint8)((data >> 48) & 0xFF)); - case 6: crc = ma_dr_flac_crc16_byte(crc, (ma_uint8)((data >> 40) & 0xFF)); - case 5: crc = ma_dr_flac_crc16_byte(crc, (ma_uint8)((data >> 32) & 0xFF)); -#endif - case 4: crc = ma_dr_flac_crc16_byte(crc, (ma_uint8)((data >> 24) & 0xFF)); - case 3: crc = ma_dr_flac_crc16_byte(crc, (ma_uint8)((data >> 16) & 0xFF)); - case 2: crc = ma_dr_flac_crc16_byte(crc, (ma_uint8)((data >> 8) & 0xFF)); - case 1: crc = ma_dr_flac_crc16_byte(crc, (ma_uint8)((data >> 0) & 0xFF)); - } - return crc; -} -#if 0 -static MA_INLINE ma_uint16 ma_dr_flac_crc16__32bit(ma_uint16 crc, ma_uint32 data, ma_uint32 count) -{ -#ifdef MA_DR_FLAC_NO_CRC - (void)crc; - (void)data; - (void)count; - return 0; -#else -#if 0 - ma_uint16 p = 0x8005; - for (int i = count-1; i >= 0; --i) { - ma_uint16 bit = (data & (1ULL << i)) >> i; - if (r & 0x8000) { - r = ((r << 1) | bit) ^ p; - } else { - r = ((r << 1) | bit); - } - } - return crc; -#else - ma_uint32 wholeBytes; - ma_uint32 leftoverBits; - ma_uint64 leftoverDataMask; - static ma_uint64 leftoverDataMaskTable[8] = { - 0x00, 0x01, 0x03, 0x07, 0x0F, 0x1F, 0x3F, 0x7F - }; - MA_DR_FLAC_ASSERT(count <= 64); - wholeBytes = count >> 3; - leftoverBits = count & 7; - leftoverDataMask = leftoverDataMaskTable[leftoverBits]; - switch (wholeBytes) { - default: - case 4: crc = ma_dr_flac_crc16_byte(crc, (ma_uint8)((data & (0xFF000000UL << leftoverBits)) >> (24 + leftoverBits))); - case 3: crc = ma_dr_flac_crc16_byte(crc, (ma_uint8)((data & (0x00FF0000UL << leftoverBits)) >> (16 + leftoverBits))); - case 2: crc = ma_dr_flac_crc16_byte(crc, (ma_uint8)((data & (0x0000FF00UL << leftoverBits)) >> ( 8 + leftoverBits))); - case 1: crc = ma_dr_flac_crc16_byte(crc, (ma_uint8)((data & (0x000000FFUL << leftoverBits)) >> ( 0 + leftoverBits))); - case 0: if (leftoverBits > 0) crc = (crc << leftoverBits) ^ ma_dr_flac__crc16_table[(crc >> (16 - leftoverBits)) ^ (data & leftoverDataMask)]; - } - return crc; -#endif -#endif -} -static MA_INLINE ma_uint16 ma_dr_flac_crc16__64bit(ma_uint16 crc, ma_uint64 data, ma_uint32 count) -{ -#ifdef MA_DR_FLAC_NO_CRC - (void)crc; - (void)data; - (void)count; - return 0; -#else - ma_uint32 wholeBytes; - ma_uint32 leftoverBits; - ma_uint64 leftoverDataMask; - static ma_uint64 leftoverDataMaskTable[8] = { - 0x00, 0x01, 0x03, 0x07, 0x0F, 0x1F, 0x3F, 0x7F - }; - MA_DR_FLAC_ASSERT(count <= 64); - wholeBytes = count >> 3; - leftoverBits = count & 7; - leftoverDataMask = leftoverDataMaskTable[leftoverBits]; - switch (wholeBytes) { - default: - case 8: crc = ma_dr_flac_crc16_byte(crc, (ma_uint8)((data & (((ma_uint64)0xFF000000 << 32) << leftoverBits)) >> (56 + leftoverBits))); - case 7: crc = ma_dr_flac_crc16_byte(crc, (ma_uint8)((data & (((ma_uint64)0x00FF0000 << 32) << leftoverBits)) >> (48 + leftoverBits))); - case 6: crc = ma_dr_flac_crc16_byte(crc, (ma_uint8)((data & (((ma_uint64)0x0000FF00 << 32) << leftoverBits)) >> (40 + leftoverBits))); - case 5: crc = ma_dr_flac_crc16_byte(crc, (ma_uint8)((data & (((ma_uint64)0x000000FF << 32) << leftoverBits)) >> (32 + leftoverBits))); - case 4: crc = ma_dr_flac_crc16_byte(crc, (ma_uint8)((data & (((ma_uint64)0xFF000000 ) << leftoverBits)) >> (24 + leftoverBits))); - case 3: crc = ma_dr_flac_crc16_byte(crc, (ma_uint8)((data & (((ma_uint64)0x00FF0000 ) << leftoverBits)) >> (16 + leftoverBits))); - case 2: crc = ma_dr_flac_crc16_byte(crc, (ma_uint8)((data & (((ma_uint64)0x0000FF00 ) << leftoverBits)) >> ( 8 + leftoverBits))); - case 1: crc = ma_dr_flac_crc16_byte(crc, (ma_uint8)((data & (((ma_uint64)0x000000FF ) << leftoverBits)) >> ( 0 + leftoverBits))); - case 0: if (leftoverBits > 0) crc = (crc << leftoverBits) ^ ma_dr_flac__crc16_table[(crc >> (16 - leftoverBits)) ^ (data & leftoverDataMask)]; - } - return crc; -#endif -} -static MA_INLINE ma_uint16 ma_dr_flac_crc16(ma_uint16 crc, ma_dr_flac_cache_t data, ma_uint32 count) -{ -#ifdef MA_64BIT - return ma_dr_flac_crc16__64bit(crc, data, count); -#else - return ma_dr_flac_crc16__32bit(crc, data, count); -#endif -} -#endif -#ifdef MA_64BIT -#define ma_dr_flac__be2host__cache_line ma_dr_flac__be2host_64 -#else -#define ma_dr_flac__be2host__cache_line ma_dr_flac__be2host_32 -#endif -#define MA_DR_FLAC_CACHE_L1_SIZE_BYTES(bs) (sizeof((bs)->cache)) -#define MA_DR_FLAC_CACHE_L1_SIZE_BITS(bs) (sizeof((bs)->cache)*8) -#define MA_DR_FLAC_CACHE_L1_BITS_REMAINING(bs) (MA_DR_FLAC_CACHE_L1_SIZE_BITS(bs) - (bs)->consumedBits) -#define MA_DR_FLAC_CACHE_L1_SELECTION_MASK(_bitCount) (~((~(ma_dr_flac_cache_t)0) >> (_bitCount))) -#define MA_DR_FLAC_CACHE_L1_SELECTION_SHIFT(bs, _bitCount) (MA_DR_FLAC_CACHE_L1_SIZE_BITS(bs) - (_bitCount)) -#define MA_DR_FLAC_CACHE_L1_SELECT(bs, _bitCount) (((bs)->cache) & MA_DR_FLAC_CACHE_L1_SELECTION_MASK(_bitCount)) -#define MA_DR_FLAC_CACHE_L1_SELECT_AND_SHIFT(bs, _bitCount) (MA_DR_FLAC_CACHE_L1_SELECT((bs), (_bitCount)) >> MA_DR_FLAC_CACHE_L1_SELECTION_SHIFT((bs), (_bitCount))) -#define MA_DR_FLAC_CACHE_L1_SELECT_AND_SHIFT_SAFE(bs, _bitCount)(MA_DR_FLAC_CACHE_L1_SELECT((bs), (_bitCount)) >> (MA_DR_FLAC_CACHE_L1_SELECTION_SHIFT((bs), (_bitCount)) & (MA_DR_FLAC_CACHE_L1_SIZE_BITS(bs)-1))) -#define MA_DR_FLAC_CACHE_L2_SIZE_BYTES(bs) (sizeof((bs)->cacheL2)) -#define MA_DR_FLAC_CACHE_L2_LINE_COUNT(bs) (MA_DR_FLAC_CACHE_L2_SIZE_BYTES(bs) / sizeof((bs)->cacheL2[0])) -#define MA_DR_FLAC_CACHE_L2_LINES_REMAINING(bs) (MA_DR_FLAC_CACHE_L2_LINE_COUNT(bs) - (bs)->nextL2Line) -#ifndef MA_DR_FLAC_NO_CRC -static MA_INLINE void ma_dr_flac__reset_crc16(ma_dr_flac_bs* bs) -{ - bs->crc16 = 0; - bs->crc16CacheIgnoredBytes = bs->consumedBits >> 3; -} -static MA_INLINE void ma_dr_flac__update_crc16(ma_dr_flac_bs* bs) -{ - if (bs->crc16CacheIgnoredBytes == 0) { - bs->crc16 = ma_dr_flac_crc16_cache(bs->crc16, bs->crc16Cache); - } else { - bs->crc16 = ma_dr_flac_crc16_bytes(bs->crc16, bs->crc16Cache, MA_DR_FLAC_CACHE_L1_SIZE_BYTES(bs) - bs->crc16CacheIgnoredBytes); - bs->crc16CacheIgnoredBytes = 0; - } -} -static MA_INLINE ma_uint16 ma_dr_flac__flush_crc16(ma_dr_flac_bs* bs) -{ - MA_DR_FLAC_ASSERT((MA_DR_FLAC_CACHE_L1_BITS_REMAINING(bs) & 7) == 0); - if (MA_DR_FLAC_CACHE_L1_BITS_REMAINING(bs) == 0) { - ma_dr_flac__update_crc16(bs); - } else { - bs->crc16 = ma_dr_flac_crc16_bytes(bs->crc16, bs->crc16Cache >> MA_DR_FLAC_CACHE_L1_BITS_REMAINING(bs), (bs->consumedBits >> 3) - bs->crc16CacheIgnoredBytes); - bs->crc16CacheIgnoredBytes = bs->consumedBits >> 3; - } - return bs->crc16; -} -#endif -static MA_INLINE ma_bool32 ma_dr_flac__reload_l1_cache_from_l2(ma_dr_flac_bs* bs) -{ - size_t bytesRead; - size_t alignedL1LineCount; - if (bs->nextL2Line < MA_DR_FLAC_CACHE_L2_LINE_COUNT(bs)) { - bs->cache = bs->cacheL2[bs->nextL2Line++]; - return MA_TRUE; - } - if (bs->unalignedByteCount > 0) { - return MA_FALSE; - } - bytesRead = bs->onRead(bs->pUserData, bs->cacheL2, MA_DR_FLAC_CACHE_L2_SIZE_BYTES(bs)); - bs->nextL2Line = 0; - if (bytesRead == MA_DR_FLAC_CACHE_L2_SIZE_BYTES(bs)) { - bs->cache = bs->cacheL2[bs->nextL2Line++]; - return MA_TRUE; - } - alignedL1LineCount = bytesRead / MA_DR_FLAC_CACHE_L1_SIZE_BYTES(bs); - bs->unalignedByteCount = bytesRead - (alignedL1LineCount * MA_DR_FLAC_CACHE_L1_SIZE_BYTES(bs)); - if (bs->unalignedByteCount > 0) { - bs->unalignedCache = bs->cacheL2[alignedL1LineCount]; - } - if (alignedL1LineCount > 0) { - size_t offset = MA_DR_FLAC_CACHE_L2_LINE_COUNT(bs) - alignedL1LineCount; - size_t i; - for (i = alignedL1LineCount; i > 0; --i) { - bs->cacheL2[i-1 + offset] = bs->cacheL2[i-1]; - } - bs->nextL2Line = (ma_uint32)offset; - bs->cache = bs->cacheL2[bs->nextL2Line++]; - return MA_TRUE; - } else { - bs->nextL2Line = MA_DR_FLAC_CACHE_L2_LINE_COUNT(bs); - return MA_FALSE; - } -} -static ma_bool32 ma_dr_flac__reload_cache(ma_dr_flac_bs* bs) -{ - size_t bytesRead; -#ifndef MA_DR_FLAC_NO_CRC - ma_dr_flac__update_crc16(bs); -#endif - if (ma_dr_flac__reload_l1_cache_from_l2(bs)) { - bs->cache = ma_dr_flac__be2host__cache_line(bs->cache); - bs->consumedBits = 0; -#ifndef MA_DR_FLAC_NO_CRC - bs->crc16Cache = bs->cache; -#endif - return MA_TRUE; - } - bytesRead = bs->unalignedByteCount; - if (bytesRead == 0) { - bs->consumedBits = MA_DR_FLAC_CACHE_L1_SIZE_BITS(bs); - return MA_FALSE; - } - MA_DR_FLAC_ASSERT(bytesRead < MA_DR_FLAC_CACHE_L1_SIZE_BYTES(bs)); - bs->consumedBits = (ma_uint32)(MA_DR_FLAC_CACHE_L1_SIZE_BYTES(bs) - bytesRead) * 8; - bs->cache = ma_dr_flac__be2host__cache_line(bs->unalignedCache); - bs->cache &= MA_DR_FLAC_CACHE_L1_SELECTION_MASK(MA_DR_FLAC_CACHE_L1_BITS_REMAINING(bs)); - bs->unalignedByteCount = 0; -#ifndef MA_DR_FLAC_NO_CRC - bs->crc16Cache = bs->cache >> bs->consumedBits; - bs->crc16CacheIgnoredBytes = bs->consumedBits >> 3; -#endif - return MA_TRUE; -} -static void ma_dr_flac__reset_cache(ma_dr_flac_bs* bs) -{ - bs->nextL2Line = MA_DR_FLAC_CACHE_L2_LINE_COUNT(bs); - bs->consumedBits = MA_DR_FLAC_CACHE_L1_SIZE_BITS(bs); - bs->cache = 0; - bs->unalignedByteCount = 0; - bs->unalignedCache = 0; -#ifndef MA_DR_FLAC_NO_CRC - bs->crc16Cache = 0; - bs->crc16CacheIgnoredBytes = 0; -#endif -} -static MA_INLINE ma_bool32 ma_dr_flac__read_uint32(ma_dr_flac_bs* bs, unsigned int bitCount, ma_uint32* pResultOut) -{ - MA_DR_FLAC_ASSERT(bs != NULL); - MA_DR_FLAC_ASSERT(pResultOut != NULL); - MA_DR_FLAC_ASSERT(bitCount > 0); - MA_DR_FLAC_ASSERT(bitCount <= 32); - if (bs->consumedBits == MA_DR_FLAC_CACHE_L1_SIZE_BITS(bs)) { - if (!ma_dr_flac__reload_cache(bs)) { - return MA_FALSE; - } - } - if (bitCount <= MA_DR_FLAC_CACHE_L1_BITS_REMAINING(bs)) { -#ifdef MA_64BIT - *pResultOut = (ma_uint32)MA_DR_FLAC_CACHE_L1_SELECT_AND_SHIFT(bs, bitCount); - bs->consumedBits += bitCount; - bs->cache <<= bitCount; -#else - if (bitCount < MA_DR_FLAC_CACHE_L1_SIZE_BITS(bs)) { - *pResultOut = (ma_uint32)MA_DR_FLAC_CACHE_L1_SELECT_AND_SHIFT(bs, bitCount); - bs->consumedBits += bitCount; - bs->cache <<= bitCount; - } else { - *pResultOut = (ma_uint32)bs->cache; - bs->consumedBits = MA_DR_FLAC_CACHE_L1_SIZE_BITS(bs); - bs->cache = 0; - } -#endif - return MA_TRUE; - } else { - ma_uint32 bitCountHi = MA_DR_FLAC_CACHE_L1_BITS_REMAINING(bs); - ma_uint32 bitCountLo = bitCount - bitCountHi; - ma_uint32 resultHi; - MA_DR_FLAC_ASSERT(bitCountHi > 0); - MA_DR_FLAC_ASSERT(bitCountHi < 32); - resultHi = (ma_uint32)MA_DR_FLAC_CACHE_L1_SELECT_AND_SHIFT(bs, bitCountHi); - if (!ma_dr_flac__reload_cache(bs)) { - return MA_FALSE; - } - if (bitCountLo > MA_DR_FLAC_CACHE_L1_BITS_REMAINING(bs)) { - return MA_FALSE; - } - *pResultOut = (resultHi << bitCountLo) | (ma_uint32)MA_DR_FLAC_CACHE_L1_SELECT_AND_SHIFT(bs, bitCountLo); - bs->consumedBits += bitCountLo; - bs->cache <<= bitCountLo; - return MA_TRUE; - } -} -static ma_bool32 ma_dr_flac__read_int32(ma_dr_flac_bs* bs, unsigned int bitCount, ma_int32* pResult) -{ - ma_uint32 result; - MA_DR_FLAC_ASSERT(bs != NULL); - MA_DR_FLAC_ASSERT(pResult != NULL); - MA_DR_FLAC_ASSERT(bitCount > 0); - MA_DR_FLAC_ASSERT(bitCount <= 32); - if (!ma_dr_flac__read_uint32(bs, bitCount, &result)) { - return MA_FALSE; - } - if (bitCount < 32) { - ma_uint32 signbit; - signbit = ((result >> (bitCount-1)) & 0x01); - result |= (~signbit + 1) << bitCount; - } - *pResult = (ma_int32)result; - return MA_TRUE; -} -#ifdef MA_64BIT -static ma_bool32 ma_dr_flac__read_uint64(ma_dr_flac_bs* bs, unsigned int bitCount, ma_uint64* pResultOut) -{ - ma_uint32 resultHi; - ma_uint32 resultLo; - MA_DR_FLAC_ASSERT(bitCount <= 64); - MA_DR_FLAC_ASSERT(bitCount > 32); - if (!ma_dr_flac__read_uint32(bs, bitCount - 32, &resultHi)) { - return MA_FALSE; - } - if (!ma_dr_flac__read_uint32(bs, 32, &resultLo)) { - return MA_FALSE; - } - *pResultOut = (((ma_uint64)resultHi) << 32) | ((ma_uint64)resultLo); - return MA_TRUE; -} -#endif -#if 0 -static ma_bool32 ma_dr_flac__read_int64(ma_dr_flac_bs* bs, unsigned int bitCount, ma_int64* pResultOut) -{ - ma_uint64 result; - ma_uint64 signbit; - MA_DR_FLAC_ASSERT(bitCount <= 64); - if (!ma_dr_flac__read_uint64(bs, bitCount, &result)) { - return MA_FALSE; - } - signbit = ((result >> (bitCount-1)) & 0x01); - result |= (~signbit + 1) << bitCount; - *pResultOut = (ma_int64)result; - return MA_TRUE; -} -#endif -static ma_bool32 ma_dr_flac__read_uint16(ma_dr_flac_bs* bs, unsigned int bitCount, ma_uint16* pResult) -{ - ma_uint32 result; - MA_DR_FLAC_ASSERT(bs != NULL); - MA_DR_FLAC_ASSERT(pResult != NULL); - MA_DR_FLAC_ASSERT(bitCount > 0); - MA_DR_FLAC_ASSERT(bitCount <= 16); - if (!ma_dr_flac__read_uint32(bs, bitCount, &result)) { - return MA_FALSE; - } - *pResult = (ma_uint16)result; - return MA_TRUE; -} -#if 0 -static ma_bool32 ma_dr_flac__read_int16(ma_dr_flac_bs* bs, unsigned int bitCount, ma_int16* pResult) -{ - ma_int32 result; - MA_DR_FLAC_ASSERT(bs != NULL); - MA_DR_FLAC_ASSERT(pResult != NULL); - MA_DR_FLAC_ASSERT(bitCount > 0); - MA_DR_FLAC_ASSERT(bitCount <= 16); - if (!ma_dr_flac__read_int32(bs, bitCount, &result)) { - return MA_FALSE; - } - *pResult = (ma_int16)result; - return MA_TRUE; -} -#endif -static ma_bool32 ma_dr_flac__read_uint8(ma_dr_flac_bs* bs, unsigned int bitCount, ma_uint8* pResult) -{ - ma_uint32 result; - MA_DR_FLAC_ASSERT(bs != NULL); - MA_DR_FLAC_ASSERT(pResult != NULL); - MA_DR_FLAC_ASSERT(bitCount > 0); - MA_DR_FLAC_ASSERT(bitCount <= 8); - if (!ma_dr_flac__read_uint32(bs, bitCount, &result)) { - return MA_FALSE; - } - *pResult = (ma_uint8)result; - return MA_TRUE; -} -static ma_bool32 ma_dr_flac__read_int8(ma_dr_flac_bs* bs, unsigned int bitCount, ma_int8* pResult) -{ - ma_int32 result; - MA_DR_FLAC_ASSERT(bs != NULL); - MA_DR_FLAC_ASSERT(pResult != NULL); - MA_DR_FLAC_ASSERT(bitCount > 0); - MA_DR_FLAC_ASSERT(bitCount <= 8); - if (!ma_dr_flac__read_int32(bs, bitCount, &result)) { - return MA_FALSE; - } - *pResult = (ma_int8)result; - return MA_TRUE; -} -static ma_bool32 ma_dr_flac__seek_bits(ma_dr_flac_bs* bs, size_t bitsToSeek) -{ - if (bitsToSeek <= MA_DR_FLAC_CACHE_L1_BITS_REMAINING(bs)) { - bs->consumedBits += (ma_uint32)bitsToSeek; - bs->cache <<= bitsToSeek; - return MA_TRUE; - } else { - bitsToSeek -= MA_DR_FLAC_CACHE_L1_BITS_REMAINING(bs); - bs->consumedBits += MA_DR_FLAC_CACHE_L1_BITS_REMAINING(bs); - bs->cache = 0; -#ifdef MA_64BIT - while (bitsToSeek >= MA_DR_FLAC_CACHE_L1_SIZE_BITS(bs)) { - ma_uint64 bin; - if (!ma_dr_flac__read_uint64(bs, MA_DR_FLAC_CACHE_L1_SIZE_BITS(bs), &bin)) { - return MA_FALSE; - } - bitsToSeek -= MA_DR_FLAC_CACHE_L1_SIZE_BITS(bs); - } -#else - while (bitsToSeek >= MA_DR_FLAC_CACHE_L1_SIZE_BITS(bs)) { - ma_uint32 bin; - if (!ma_dr_flac__read_uint32(bs, MA_DR_FLAC_CACHE_L1_SIZE_BITS(bs), &bin)) { - return MA_FALSE; - } - bitsToSeek -= MA_DR_FLAC_CACHE_L1_SIZE_BITS(bs); - } -#endif - while (bitsToSeek >= 8) { - ma_uint8 bin; - if (!ma_dr_flac__read_uint8(bs, 8, &bin)) { - return MA_FALSE; - } - bitsToSeek -= 8; - } - if (bitsToSeek > 0) { - ma_uint8 bin; - if (!ma_dr_flac__read_uint8(bs, (ma_uint32)bitsToSeek, &bin)) { - return MA_FALSE; - } - bitsToSeek = 0; - } - MA_DR_FLAC_ASSERT(bitsToSeek == 0); - return MA_TRUE; - } -} -static ma_bool32 ma_dr_flac__find_and_seek_to_next_sync_code(ma_dr_flac_bs* bs) -{ - MA_DR_FLAC_ASSERT(bs != NULL); - if (!ma_dr_flac__seek_bits(bs, MA_DR_FLAC_CACHE_L1_BITS_REMAINING(bs) & 7)) { - return MA_FALSE; - } - for (;;) { - ma_uint8 hi; -#ifndef MA_DR_FLAC_NO_CRC - ma_dr_flac__reset_crc16(bs); -#endif - if (!ma_dr_flac__read_uint8(bs, 8, &hi)) { - return MA_FALSE; - } - if (hi == 0xFF) { - ma_uint8 lo; - if (!ma_dr_flac__read_uint8(bs, 6, &lo)) { - return MA_FALSE; - } - if (lo == 0x3E) { - return MA_TRUE; - } else { - if (!ma_dr_flac__seek_bits(bs, MA_DR_FLAC_CACHE_L1_BITS_REMAINING(bs) & 7)) { - return MA_FALSE; - } - } - } - } -} -#if defined(MA_DR_FLAC_HAS_LZCNT_INTRINSIC) -#define MA_DR_FLAC_IMPLEMENT_CLZ_LZCNT -#endif -#if defined(_MSC_VER) && _MSC_VER >= 1400 && (defined(MA_X64) || defined(MA_X86)) && !defined(__clang__) -#define MA_DR_FLAC_IMPLEMENT_CLZ_MSVC -#endif -#if defined(__WATCOMC__) && defined(__386__) -#define MA_DR_FLAC_IMPLEMENT_CLZ_WATCOM -#endif -#ifdef __MRC__ -#include -#define MA_DR_FLAC_IMPLEMENT_CLZ_MRC -#endif -static MA_INLINE ma_uint32 ma_dr_flac__clz_software(ma_dr_flac_cache_t x) -{ - ma_uint32 n; - static ma_uint32 clz_table_4[] = { - 0, - 4, - 3, 3, - 2, 2, 2, 2, - 1, 1, 1, 1, 1, 1, 1, 1 - }; - if (x == 0) { - return sizeof(x)*8; - } - n = clz_table_4[x >> (sizeof(x)*8 - 4)]; - if (n == 0) { -#ifdef MA_64BIT - if ((x & ((ma_uint64)0xFFFFFFFF << 32)) == 0) { n = 32; x <<= 32; } - if ((x & ((ma_uint64)0xFFFF0000 << 32)) == 0) { n += 16; x <<= 16; } - if ((x & ((ma_uint64)0xFF000000 << 32)) == 0) { n += 8; x <<= 8; } - if ((x & ((ma_uint64)0xF0000000 << 32)) == 0) { n += 4; x <<= 4; } -#else - if ((x & 0xFFFF0000) == 0) { n = 16; x <<= 16; } - if ((x & 0xFF000000) == 0) { n += 8; x <<= 8; } - if ((x & 0xF0000000) == 0) { n += 4; x <<= 4; } -#endif - n += clz_table_4[x >> (sizeof(x)*8 - 4)]; - } - return n - 1; -} -#ifdef MA_DR_FLAC_IMPLEMENT_CLZ_LZCNT -static MA_INLINE ma_bool32 ma_dr_flac__is_lzcnt_supported(void) -{ -#if defined(MA_DR_FLAC_HAS_LZCNT_INTRINSIC) && defined(MA_ARM) && (defined(__ARM_ARCH) && __ARM_ARCH >= 5) - return MA_TRUE; -#elif defined(__MRC__) - return MA_TRUE; -#else - #ifdef MA_DR_FLAC_HAS_LZCNT_INTRINSIC - return ma_dr_flac__gIsLZCNTSupported; - #else - return MA_FALSE; - #endif -#endif -} -static MA_INLINE ma_uint32 ma_dr_flac__clz_lzcnt(ma_dr_flac_cache_t x) -{ -#if defined(_MSC_VER) - #ifdef MA_64BIT - return (ma_uint32)__lzcnt64(x); - #else - return (ma_uint32)__lzcnt(x); - #endif -#else - #if defined(__GNUC__) || defined(__clang__) - #if defined(MA_X64) - { - ma_uint64 r; - __asm__ __volatile__ ( - "lzcnt{ %1, %0| %0, %1}" : "=r"(r) : "r"(x) : "cc" - ); - return (ma_uint32)r; - } - #elif defined(MA_X86) - { - ma_uint32 r; - __asm__ __volatile__ ( - "lzcnt{l %1, %0| %0, %1}" : "=r"(r) : "r"(x) : "cc" - ); - return r; - } - #elif defined(MA_ARM) && (defined(__ARM_ARCH) && __ARM_ARCH >= 5) && !defined(__ARM_ARCH_6M__) && !defined(MA_64BIT) - { - unsigned int r; - __asm__ __volatile__ ( - #if defined(MA_64BIT) - "clz %w[out], %w[in]" : [out]"=r"(r) : [in]"r"(x) - #else - "clz %[out], %[in]" : [out]"=r"(r) : [in]"r"(x) - #endif - ); - return r; - } - #else - if (x == 0) { - return sizeof(x)*8; - } - #ifdef MA_64BIT - return (ma_uint32)__builtin_clzll((ma_uint64)x); - #else - return (ma_uint32)__builtin_clzl((ma_uint32)x); - #endif - #endif - #else - #error "This compiler does not support the lzcnt intrinsic." - #endif -#endif -} -#endif -#ifdef MA_DR_FLAC_IMPLEMENT_CLZ_MSVC -#include -static MA_INLINE ma_uint32 ma_dr_flac__clz_msvc(ma_dr_flac_cache_t x) -{ - ma_uint32 n; - if (x == 0) { - return sizeof(x)*8; - } -#ifdef MA_64BIT - _BitScanReverse64((unsigned long*)&n, x); -#else - _BitScanReverse((unsigned long*)&n, x); -#endif - return sizeof(x)*8 - n - 1; -} -#endif -#ifdef MA_DR_FLAC_IMPLEMENT_CLZ_WATCOM -static __inline ma_uint32 ma_dr_flac__clz_watcom (ma_uint32); -#ifdef MA_DR_FLAC_IMPLEMENT_CLZ_WATCOM_LZCNT -#pragma aux ma_dr_flac__clz_watcom_lzcnt = \ - "db 0F3h, 0Fh, 0BDh, 0C0h" \ - parm [eax] \ - value [eax] \ - modify nomemory; -#else -#pragma aux ma_dr_flac__clz_watcom = \ - "bsr eax, eax" \ - "xor eax, 31" \ - parm [eax] nomemory \ - value [eax] \ - modify exact [eax] nomemory; -#endif -#endif -static MA_INLINE ma_uint32 ma_dr_flac__clz(ma_dr_flac_cache_t x) -{ -#ifdef MA_DR_FLAC_IMPLEMENT_CLZ_LZCNT - if (ma_dr_flac__is_lzcnt_supported()) { - return ma_dr_flac__clz_lzcnt(x); - } else -#endif - { -#ifdef MA_DR_FLAC_IMPLEMENT_CLZ_MSVC - return ma_dr_flac__clz_msvc(x); -#elif defined(MA_DR_FLAC_IMPLEMENT_CLZ_WATCOM_LZCNT) - return ma_dr_flac__clz_watcom_lzcnt(x); -#elif defined(MA_DR_FLAC_IMPLEMENT_CLZ_WATCOM) - return (x == 0) ? sizeof(x)*8 : ma_dr_flac__clz_watcom(x); -#elif defined(__MRC__) - return __cntlzw(x); -#else - return ma_dr_flac__clz_software(x); -#endif - } -} -static MA_INLINE ma_bool32 ma_dr_flac__seek_past_next_set_bit(ma_dr_flac_bs* bs, unsigned int* pOffsetOut) -{ - ma_uint32 zeroCounter = 0; - ma_uint32 setBitOffsetPlus1; - while (bs->cache == 0) { - zeroCounter += (ma_uint32)MA_DR_FLAC_CACHE_L1_BITS_REMAINING(bs); - if (!ma_dr_flac__reload_cache(bs)) { - return MA_FALSE; - } - } - if (bs->cache == 1) { - *pOffsetOut = zeroCounter + (ma_uint32)MA_DR_FLAC_CACHE_L1_BITS_REMAINING(bs) - 1; - if (!ma_dr_flac__reload_cache(bs)) { - return MA_FALSE; - } - return MA_TRUE; - } - setBitOffsetPlus1 = ma_dr_flac__clz(bs->cache); - setBitOffsetPlus1 += 1; - if (setBitOffsetPlus1 > MA_DR_FLAC_CACHE_L1_BITS_REMAINING(bs)) { - return MA_FALSE; - } - bs->consumedBits += setBitOffsetPlus1; - bs->cache <<= setBitOffsetPlus1; - *pOffsetOut = zeroCounter + setBitOffsetPlus1 - 1; - return MA_TRUE; -} -static ma_bool32 ma_dr_flac__seek_to_byte(ma_dr_flac_bs* bs, ma_uint64 offsetFromStart) -{ - MA_DR_FLAC_ASSERT(bs != NULL); - MA_DR_FLAC_ASSERT(offsetFromStart > 0); - if (offsetFromStart > 0x7FFFFFFF) { - ma_uint64 bytesRemaining = offsetFromStart; - if (!bs->onSeek(bs->pUserData, 0x7FFFFFFF, ma_dr_flac_seek_origin_start)) { - return MA_FALSE; - } - bytesRemaining -= 0x7FFFFFFF; - while (bytesRemaining > 0x7FFFFFFF) { - if (!bs->onSeek(bs->pUserData, 0x7FFFFFFF, ma_dr_flac_seek_origin_current)) { - return MA_FALSE; - } - bytesRemaining -= 0x7FFFFFFF; - } - if (bytesRemaining > 0) { - if (!bs->onSeek(bs->pUserData, (int)bytesRemaining, ma_dr_flac_seek_origin_current)) { - return MA_FALSE; - } - } - } else { - if (!bs->onSeek(bs->pUserData, (int)offsetFromStart, ma_dr_flac_seek_origin_start)) { - return MA_FALSE; - } - } - ma_dr_flac__reset_cache(bs); - return MA_TRUE; -} -static ma_result ma_dr_flac__read_utf8_coded_number(ma_dr_flac_bs* bs, ma_uint64* pNumberOut, ma_uint8* pCRCOut) -{ - ma_uint8 crc; - ma_uint64 result; - ma_uint8 utf8[7] = {0}; - int byteCount; - int i; - MA_DR_FLAC_ASSERT(bs != NULL); - MA_DR_FLAC_ASSERT(pNumberOut != NULL); - MA_DR_FLAC_ASSERT(pCRCOut != NULL); - crc = *pCRCOut; - if (!ma_dr_flac__read_uint8(bs, 8, utf8)) { - *pNumberOut = 0; - return MA_AT_END; - } - crc = ma_dr_flac_crc8(crc, utf8[0], 8); - if ((utf8[0] & 0x80) == 0) { - *pNumberOut = utf8[0]; - *pCRCOut = crc; - return MA_SUCCESS; - } - if ((utf8[0] & 0xE0) == 0xC0) { - byteCount = 2; - } else if ((utf8[0] & 0xF0) == 0xE0) { - byteCount = 3; - } else if ((utf8[0] & 0xF8) == 0xF0) { - byteCount = 4; - } else if ((utf8[0] & 0xFC) == 0xF8) { - byteCount = 5; - } else if ((utf8[0] & 0xFE) == 0xFC) { - byteCount = 6; - } else if ((utf8[0] & 0xFF) == 0xFE) { - byteCount = 7; - } else { - *pNumberOut = 0; - return MA_CRC_MISMATCH; - } - MA_DR_FLAC_ASSERT(byteCount > 1); - result = (ma_uint64)(utf8[0] & (0xFF >> (byteCount + 1))); - for (i = 1; i < byteCount; ++i) { - if (!ma_dr_flac__read_uint8(bs, 8, utf8 + i)) { - *pNumberOut = 0; - return MA_AT_END; - } - crc = ma_dr_flac_crc8(crc, utf8[i], 8); - result = (result << 6) | (utf8[i] & 0x3F); - } - *pNumberOut = result; - *pCRCOut = crc; - return MA_SUCCESS; -} -static MA_INLINE ma_uint32 ma_dr_flac__ilog2_u32(ma_uint32 x) -{ -#if 1 - ma_uint32 result = 0; - while (x > 0) { - result += 1; - x >>= 1; - } - return result; -#endif -} -static MA_INLINE ma_bool32 ma_dr_flac__use_64_bit_prediction(ma_uint32 bitsPerSample, ma_uint32 order, ma_uint32 precision) -{ - return bitsPerSample + precision + ma_dr_flac__ilog2_u32(order) > 32; -} -#if defined(__clang__) -__attribute__((no_sanitize("signed-integer-overflow"))) -#endif -static MA_INLINE ma_int32 ma_dr_flac__calculate_prediction_32(ma_uint32 order, ma_int32 shift, const ma_int32* coefficients, ma_int32* pDecodedSamples) -{ - ma_int32 prediction = 0; - MA_DR_FLAC_ASSERT(order <= 32); - switch (order) - { - case 32: prediction += coefficients[31] * pDecodedSamples[-32]; - case 31: prediction += coefficients[30] * pDecodedSamples[-31]; - case 30: prediction += coefficients[29] * pDecodedSamples[-30]; - case 29: prediction += coefficients[28] * pDecodedSamples[-29]; - case 28: prediction += coefficients[27] * pDecodedSamples[-28]; - case 27: prediction += coefficients[26] * pDecodedSamples[-27]; - case 26: prediction += coefficients[25] * pDecodedSamples[-26]; - case 25: prediction += coefficients[24] * pDecodedSamples[-25]; - case 24: prediction += coefficients[23] * pDecodedSamples[-24]; - case 23: prediction += coefficients[22] * pDecodedSamples[-23]; - case 22: prediction += coefficients[21] * pDecodedSamples[-22]; - case 21: prediction += coefficients[20] * pDecodedSamples[-21]; - case 20: prediction += coefficients[19] * pDecodedSamples[-20]; - case 19: prediction += coefficients[18] * pDecodedSamples[-19]; - case 18: prediction += coefficients[17] * pDecodedSamples[-18]; - case 17: prediction += coefficients[16] * pDecodedSamples[-17]; - case 16: prediction += coefficients[15] * pDecodedSamples[-16]; - case 15: prediction += coefficients[14] * pDecodedSamples[-15]; - case 14: prediction += coefficients[13] * pDecodedSamples[-14]; - case 13: prediction += coefficients[12] * pDecodedSamples[-13]; - case 12: prediction += coefficients[11] * pDecodedSamples[-12]; - case 11: prediction += coefficients[10] * pDecodedSamples[-11]; - case 10: prediction += coefficients[ 9] * pDecodedSamples[-10]; - case 9: prediction += coefficients[ 8] * pDecodedSamples[- 9]; - case 8: prediction += coefficients[ 7] * pDecodedSamples[- 8]; - case 7: prediction += coefficients[ 6] * pDecodedSamples[- 7]; - case 6: prediction += coefficients[ 5] * pDecodedSamples[- 6]; - case 5: prediction += coefficients[ 4] * pDecodedSamples[- 5]; - case 4: prediction += coefficients[ 3] * pDecodedSamples[- 4]; - case 3: prediction += coefficients[ 2] * pDecodedSamples[- 3]; - case 2: prediction += coefficients[ 1] * pDecodedSamples[- 2]; - case 1: prediction += coefficients[ 0] * pDecodedSamples[- 1]; - } - return (ma_int32)(prediction >> shift); -} -static MA_INLINE ma_int32 ma_dr_flac__calculate_prediction_64(ma_uint32 order, ma_int32 shift, const ma_int32* coefficients, ma_int32* pDecodedSamples) -{ - ma_int64 prediction; - MA_DR_FLAC_ASSERT(order <= 32); -#ifndef MA_64BIT - if (order == 8) - { - prediction = coefficients[0] * (ma_int64)pDecodedSamples[-1]; - prediction += coefficients[1] * (ma_int64)pDecodedSamples[-2]; - prediction += coefficients[2] * (ma_int64)pDecodedSamples[-3]; - prediction += coefficients[3] * (ma_int64)pDecodedSamples[-4]; - prediction += coefficients[4] * (ma_int64)pDecodedSamples[-5]; - prediction += coefficients[5] * (ma_int64)pDecodedSamples[-6]; - prediction += coefficients[6] * (ma_int64)pDecodedSamples[-7]; - prediction += coefficients[7] * (ma_int64)pDecodedSamples[-8]; - } - else if (order == 7) - { - prediction = coefficients[0] * (ma_int64)pDecodedSamples[-1]; - prediction += coefficients[1] * (ma_int64)pDecodedSamples[-2]; - prediction += coefficients[2] * (ma_int64)pDecodedSamples[-3]; - prediction += coefficients[3] * (ma_int64)pDecodedSamples[-4]; - prediction += coefficients[4] * (ma_int64)pDecodedSamples[-5]; - prediction += coefficients[5] * (ma_int64)pDecodedSamples[-6]; - prediction += coefficients[6] * (ma_int64)pDecodedSamples[-7]; - } - else if (order == 3) - { - prediction = coefficients[0] * (ma_int64)pDecodedSamples[-1]; - prediction += coefficients[1] * (ma_int64)pDecodedSamples[-2]; - prediction += coefficients[2] * (ma_int64)pDecodedSamples[-3]; - } - else if (order == 6) - { - prediction = coefficients[0] * (ma_int64)pDecodedSamples[-1]; - prediction += coefficients[1] * (ma_int64)pDecodedSamples[-2]; - prediction += coefficients[2] * (ma_int64)pDecodedSamples[-3]; - prediction += coefficients[3] * (ma_int64)pDecodedSamples[-4]; - prediction += coefficients[4] * (ma_int64)pDecodedSamples[-5]; - prediction += coefficients[5] * (ma_int64)pDecodedSamples[-6]; - } - else if (order == 5) - { - prediction = coefficients[0] * (ma_int64)pDecodedSamples[-1]; - prediction += coefficients[1] * (ma_int64)pDecodedSamples[-2]; - prediction += coefficients[2] * (ma_int64)pDecodedSamples[-3]; - prediction += coefficients[3] * (ma_int64)pDecodedSamples[-4]; - prediction += coefficients[4] * (ma_int64)pDecodedSamples[-5]; - } - else if (order == 4) - { - prediction = coefficients[0] * (ma_int64)pDecodedSamples[-1]; - prediction += coefficients[1] * (ma_int64)pDecodedSamples[-2]; - prediction += coefficients[2] * (ma_int64)pDecodedSamples[-3]; - prediction += coefficients[3] * (ma_int64)pDecodedSamples[-4]; - } - else if (order == 12) - { - prediction = coefficients[0] * (ma_int64)pDecodedSamples[-1]; - prediction += coefficients[1] * (ma_int64)pDecodedSamples[-2]; - prediction += coefficients[2] * (ma_int64)pDecodedSamples[-3]; - prediction += coefficients[3] * (ma_int64)pDecodedSamples[-4]; - prediction += coefficients[4] * (ma_int64)pDecodedSamples[-5]; - prediction += coefficients[5] * (ma_int64)pDecodedSamples[-6]; - prediction += coefficients[6] * (ma_int64)pDecodedSamples[-7]; - prediction += coefficients[7] * (ma_int64)pDecodedSamples[-8]; - prediction += coefficients[8] * (ma_int64)pDecodedSamples[-9]; - prediction += coefficients[9] * (ma_int64)pDecodedSamples[-10]; - prediction += coefficients[10] * (ma_int64)pDecodedSamples[-11]; - prediction += coefficients[11] * (ma_int64)pDecodedSamples[-12]; - } - else if (order == 2) - { - prediction = coefficients[0] * (ma_int64)pDecodedSamples[-1]; - prediction += coefficients[1] * (ma_int64)pDecodedSamples[-2]; - } - else if (order == 1) - { - prediction = coefficients[0] * (ma_int64)pDecodedSamples[-1]; - } - else if (order == 10) - { - prediction = coefficients[0] * (ma_int64)pDecodedSamples[-1]; - prediction += coefficients[1] * (ma_int64)pDecodedSamples[-2]; - prediction += coefficients[2] * (ma_int64)pDecodedSamples[-3]; - prediction += coefficients[3] * (ma_int64)pDecodedSamples[-4]; - prediction += coefficients[4] * (ma_int64)pDecodedSamples[-5]; - prediction += coefficients[5] * (ma_int64)pDecodedSamples[-6]; - prediction += coefficients[6] * (ma_int64)pDecodedSamples[-7]; - prediction += coefficients[7] * (ma_int64)pDecodedSamples[-8]; - prediction += coefficients[8] * (ma_int64)pDecodedSamples[-9]; - prediction += coefficients[9] * (ma_int64)pDecodedSamples[-10]; - } - else if (order == 9) - { - prediction = coefficients[0] * (ma_int64)pDecodedSamples[-1]; - prediction += coefficients[1] * (ma_int64)pDecodedSamples[-2]; - prediction += coefficients[2] * (ma_int64)pDecodedSamples[-3]; - prediction += coefficients[3] * (ma_int64)pDecodedSamples[-4]; - prediction += coefficients[4] * (ma_int64)pDecodedSamples[-5]; - prediction += coefficients[5] * (ma_int64)pDecodedSamples[-6]; - prediction += coefficients[6] * (ma_int64)pDecodedSamples[-7]; - prediction += coefficients[7] * (ma_int64)pDecodedSamples[-8]; - prediction += coefficients[8] * (ma_int64)pDecodedSamples[-9]; - } - else if (order == 11) - { - prediction = coefficients[0] * (ma_int64)pDecodedSamples[-1]; - prediction += coefficients[1] * (ma_int64)pDecodedSamples[-2]; - prediction += coefficients[2] * (ma_int64)pDecodedSamples[-3]; - prediction += coefficients[3] * (ma_int64)pDecodedSamples[-4]; - prediction += coefficients[4] * (ma_int64)pDecodedSamples[-5]; - prediction += coefficients[5] * (ma_int64)pDecodedSamples[-6]; - prediction += coefficients[6] * (ma_int64)pDecodedSamples[-7]; - prediction += coefficients[7] * (ma_int64)pDecodedSamples[-8]; - prediction += coefficients[8] * (ma_int64)pDecodedSamples[-9]; - prediction += coefficients[9] * (ma_int64)pDecodedSamples[-10]; - prediction += coefficients[10] * (ma_int64)pDecodedSamples[-11]; - } - else - { - int j; - prediction = 0; - for (j = 0; j < (int)order; ++j) { - prediction += coefficients[j] * (ma_int64)pDecodedSamples[-j-1]; - } - } -#endif -#ifdef MA_64BIT - prediction = 0; - switch (order) - { - case 32: prediction += coefficients[31] * (ma_int64)pDecodedSamples[-32]; - case 31: prediction += coefficients[30] * (ma_int64)pDecodedSamples[-31]; - case 30: prediction += coefficients[29] * (ma_int64)pDecodedSamples[-30]; - case 29: prediction += coefficients[28] * (ma_int64)pDecodedSamples[-29]; - case 28: prediction += coefficients[27] * (ma_int64)pDecodedSamples[-28]; - case 27: prediction += coefficients[26] * (ma_int64)pDecodedSamples[-27]; - case 26: prediction += coefficients[25] * (ma_int64)pDecodedSamples[-26]; - case 25: prediction += coefficients[24] * (ma_int64)pDecodedSamples[-25]; - case 24: prediction += coefficients[23] * (ma_int64)pDecodedSamples[-24]; - case 23: prediction += coefficients[22] * (ma_int64)pDecodedSamples[-23]; - case 22: prediction += coefficients[21] * (ma_int64)pDecodedSamples[-22]; - case 21: prediction += coefficients[20] * (ma_int64)pDecodedSamples[-21]; - case 20: prediction += coefficients[19] * (ma_int64)pDecodedSamples[-20]; - case 19: prediction += coefficients[18] * (ma_int64)pDecodedSamples[-19]; - case 18: prediction += coefficients[17] * (ma_int64)pDecodedSamples[-18]; - case 17: prediction += coefficients[16] * (ma_int64)pDecodedSamples[-17]; - case 16: prediction += coefficients[15] * (ma_int64)pDecodedSamples[-16]; - case 15: prediction += coefficients[14] * (ma_int64)pDecodedSamples[-15]; - case 14: prediction += coefficients[13] * (ma_int64)pDecodedSamples[-14]; - case 13: prediction += coefficients[12] * (ma_int64)pDecodedSamples[-13]; - case 12: prediction += coefficients[11] * (ma_int64)pDecodedSamples[-12]; - case 11: prediction += coefficients[10] * (ma_int64)pDecodedSamples[-11]; - case 10: prediction += coefficients[ 9] * (ma_int64)pDecodedSamples[-10]; - case 9: prediction += coefficients[ 8] * (ma_int64)pDecodedSamples[- 9]; - case 8: prediction += coefficients[ 7] * (ma_int64)pDecodedSamples[- 8]; - case 7: prediction += coefficients[ 6] * (ma_int64)pDecodedSamples[- 7]; - case 6: prediction += coefficients[ 5] * (ma_int64)pDecodedSamples[- 6]; - case 5: prediction += coefficients[ 4] * (ma_int64)pDecodedSamples[- 5]; - case 4: prediction += coefficients[ 3] * (ma_int64)pDecodedSamples[- 4]; - case 3: prediction += coefficients[ 2] * (ma_int64)pDecodedSamples[- 3]; - case 2: prediction += coefficients[ 1] * (ma_int64)pDecodedSamples[- 2]; - case 1: prediction += coefficients[ 0] * (ma_int64)pDecodedSamples[- 1]; - } -#endif - return (ma_int32)(prediction >> shift); -} -#if 0 -static ma_bool32 ma_dr_flac__decode_samples_with_residual__rice__reference(ma_dr_flac_bs* bs, ma_uint32 bitsPerSample, ma_uint32 count, ma_uint8 riceParam, ma_uint32 lpcOrder, ma_int32 lpcShift, ma_uint32 lpcPrecision, const ma_int32* coefficients, ma_int32* pSamplesOut) -{ - ma_uint32 i; - MA_DR_FLAC_ASSERT(bs != NULL); - MA_DR_FLAC_ASSERT(pSamplesOut != NULL); - for (i = 0; i < count; ++i) { - ma_uint32 zeroCounter = 0; - for (;;) { - ma_uint8 bit; - if (!ma_dr_flac__read_uint8(bs, 1, &bit)) { - return MA_FALSE; - } - if (bit == 0) { - zeroCounter += 1; - } else { - break; - } - } - ma_uint32 decodedRice; - if (riceParam > 0) { - if (!ma_dr_flac__read_uint32(bs, riceParam, &decodedRice)) { - return MA_FALSE; - } - } else { - decodedRice = 0; - } - decodedRice |= (zeroCounter << riceParam); - if ((decodedRice & 0x01)) { - decodedRice = ~(decodedRice >> 1); - } else { - decodedRice = (decodedRice >> 1); - } - if (ma_dr_flac__use_64_bit_prediction(bitsPerSample, lpcOrder, lpcPrecision)) { - pSamplesOut[i] = decodedRice + ma_dr_flac__calculate_prediction_64(lpcOrder, lpcShift, coefficients, pSamplesOut + i); - } else { - pSamplesOut[i] = decodedRice + ma_dr_flac__calculate_prediction_32(lpcOrder, lpcShift, coefficients, pSamplesOut + i); - } - } - return MA_TRUE; -} -#endif -#if 0 -static ma_bool32 ma_dr_flac__read_rice_parts__reference(ma_dr_flac_bs* bs, ma_uint8 riceParam, ma_uint32* pZeroCounterOut, ma_uint32* pRiceParamPartOut) -{ - ma_uint32 zeroCounter = 0; - ma_uint32 decodedRice; - for (;;) { - ma_uint8 bit; - if (!ma_dr_flac__read_uint8(bs, 1, &bit)) { - return MA_FALSE; - } - if (bit == 0) { - zeroCounter += 1; - } else { - break; - } - } - if (riceParam > 0) { - if (!ma_dr_flac__read_uint32(bs, riceParam, &decodedRice)) { - return MA_FALSE; - } - } else { - decodedRice = 0; - } - *pZeroCounterOut = zeroCounter; - *pRiceParamPartOut = decodedRice; - return MA_TRUE; -} -#endif -#if 0 -static MA_INLINE ma_bool32 ma_dr_flac__read_rice_parts(ma_dr_flac_bs* bs, ma_uint8 riceParam, ma_uint32* pZeroCounterOut, ma_uint32* pRiceParamPartOut) -{ - ma_dr_flac_cache_t riceParamMask; - ma_uint32 zeroCounter; - ma_uint32 setBitOffsetPlus1; - ma_uint32 riceParamPart; - ma_uint32 riceLength; - MA_DR_FLAC_ASSERT(riceParam > 0); - riceParamMask = MA_DR_FLAC_CACHE_L1_SELECTION_MASK(riceParam); - zeroCounter = 0; - while (bs->cache == 0) { - zeroCounter += (ma_uint32)MA_DR_FLAC_CACHE_L1_BITS_REMAINING(bs); - if (!ma_dr_flac__reload_cache(bs)) { - return MA_FALSE; - } - } - setBitOffsetPlus1 = ma_dr_flac__clz(bs->cache); - zeroCounter += setBitOffsetPlus1; - setBitOffsetPlus1 += 1; - riceLength = setBitOffsetPlus1 + riceParam; - if (riceLength < MA_DR_FLAC_CACHE_L1_BITS_REMAINING(bs)) { - riceParamPart = (ma_uint32)((bs->cache & (riceParamMask >> setBitOffsetPlus1)) >> MA_DR_FLAC_CACHE_L1_SELECTION_SHIFT(bs, riceLength)); - bs->consumedBits += riceLength; - bs->cache <<= riceLength; - } else { - ma_uint32 bitCountLo; - ma_dr_flac_cache_t resultHi; - bs->consumedBits += riceLength; - bs->cache <<= setBitOffsetPlus1 & (MA_DR_FLAC_CACHE_L1_SIZE_BITS(bs)-1); - bitCountLo = bs->consumedBits - MA_DR_FLAC_CACHE_L1_SIZE_BITS(bs); - resultHi = MA_DR_FLAC_CACHE_L1_SELECT_AND_SHIFT(bs, riceParam); - if (bs->nextL2Line < MA_DR_FLAC_CACHE_L2_LINE_COUNT(bs)) { -#ifndef MA_DR_FLAC_NO_CRC - ma_dr_flac__update_crc16(bs); -#endif - bs->cache = ma_dr_flac__be2host__cache_line(bs->cacheL2[bs->nextL2Line++]); - bs->consumedBits = 0; -#ifndef MA_DR_FLAC_NO_CRC - bs->crc16Cache = bs->cache; -#endif - } else { - if (!ma_dr_flac__reload_cache(bs)) { - return MA_FALSE; - } - if (bitCountLo > MA_DR_FLAC_CACHE_L1_BITS_REMAINING(bs)) { - return MA_FALSE; - } - } - riceParamPart = (ma_uint32)(resultHi | MA_DR_FLAC_CACHE_L1_SELECT_AND_SHIFT_SAFE(bs, bitCountLo)); - bs->consumedBits += bitCountLo; - bs->cache <<= bitCountLo; - } - pZeroCounterOut[0] = zeroCounter; - pRiceParamPartOut[0] = riceParamPart; - return MA_TRUE; -} -#endif -static MA_INLINE ma_bool32 ma_dr_flac__read_rice_parts_x1(ma_dr_flac_bs* bs, ma_uint8 riceParam, ma_uint32* pZeroCounterOut, ma_uint32* pRiceParamPartOut) -{ - ma_uint32 riceParamPlus1 = riceParam + 1; - ma_uint32 riceParamPlus1Shift = MA_DR_FLAC_CACHE_L1_SELECTION_SHIFT(bs, riceParamPlus1); - ma_uint32 riceParamPlus1MaxConsumedBits = MA_DR_FLAC_CACHE_L1_SIZE_BITS(bs) - riceParamPlus1; - ma_dr_flac_cache_t bs_cache = bs->cache; - ma_uint32 bs_consumedBits = bs->consumedBits; - ma_uint32 lzcount = ma_dr_flac__clz(bs_cache); - if (lzcount < sizeof(bs_cache)*8) { - pZeroCounterOut[0] = lzcount; - extract_rice_param_part: - bs_cache <<= lzcount; - bs_consumedBits += lzcount; - if (bs_consumedBits <= riceParamPlus1MaxConsumedBits) { - pRiceParamPartOut[0] = (ma_uint32)(bs_cache >> riceParamPlus1Shift); - bs_cache <<= riceParamPlus1; - bs_consumedBits += riceParamPlus1; - } else { - ma_uint32 riceParamPartHi; - ma_uint32 riceParamPartLo; - ma_uint32 riceParamPartLoBitCount; - riceParamPartHi = (ma_uint32)(bs_cache >> riceParamPlus1Shift); - riceParamPartLoBitCount = bs_consumedBits - riceParamPlus1MaxConsumedBits; - MA_DR_FLAC_ASSERT(riceParamPartLoBitCount > 0 && riceParamPartLoBitCount < 32); - if (bs->nextL2Line < MA_DR_FLAC_CACHE_L2_LINE_COUNT(bs)) { - #ifndef MA_DR_FLAC_NO_CRC - ma_dr_flac__update_crc16(bs); - #endif - bs_cache = ma_dr_flac__be2host__cache_line(bs->cacheL2[bs->nextL2Line++]); - bs_consumedBits = riceParamPartLoBitCount; - #ifndef MA_DR_FLAC_NO_CRC - bs->crc16Cache = bs_cache; - #endif - } else { - if (!ma_dr_flac__reload_cache(bs)) { - return MA_FALSE; - } - if (riceParamPartLoBitCount > MA_DR_FLAC_CACHE_L1_BITS_REMAINING(bs)) { - return MA_FALSE; - } - bs_cache = bs->cache; - bs_consumedBits = bs->consumedBits + riceParamPartLoBitCount; - } - riceParamPartLo = (ma_uint32)(bs_cache >> (MA_DR_FLAC_CACHE_L1_SELECTION_SHIFT(bs, riceParamPartLoBitCount))); - pRiceParamPartOut[0] = riceParamPartHi | riceParamPartLo; - bs_cache <<= riceParamPartLoBitCount; - } - } else { - ma_uint32 zeroCounter = (ma_uint32)(MA_DR_FLAC_CACHE_L1_SIZE_BITS(bs) - bs_consumedBits); - for (;;) { - if (bs->nextL2Line < MA_DR_FLAC_CACHE_L2_LINE_COUNT(bs)) { - #ifndef MA_DR_FLAC_NO_CRC - ma_dr_flac__update_crc16(bs); - #endif - bs_cache = ma_dr_flac__be2host__cache_line(bs->cacheL2[bs->nextL2Line++]); - bs_consumedBits = 0; - #ifndef MA_DR_FLAC_NO_CRC - bs->crc16Cache = bs_cache; - #endif - } else { - if (!ma_dr_flac__reload_cache(bs)) { - return MA_FALSE; - } - bs_cache = bs->cache; - bs_consumedBits = bs->consumedBits; - } - lzcount = ma_dr_flac__clz(bs_cache); - zeroCounter += lzcount; - if (lzcount < sizeof(bs_cache)*8) { - break; - } - } - pZeroCounterOut[0] = zeroCounter; - goto extract_rice_param_part; - } - bs->cache = bs_cache; - bs->consumedBits = bs_consumedBits; - return MA_TRUE; -} -static MA_INLINE ma_bool32 ma_dr_flac__seek_rice_parts(ma_dr_flac_bs* bs, ma_uint8 riceParam) -{ - ma_uint32 riceParamPlus1 = riceParam + 1; - ma_uint32 riceParamPlus1MaxConsumedBits = MA_DR_FLAC_CACHE_L1_SIZE_BITS(bs) - riceParamPlus1; - ma_dr_flac_cache_t bs_cache = bs->cache; - ma_uint32 bs_consumedBits = bs->consumedBits; - ma_uint32 lzcount = ma_dr_flac__clz(bs_cache); - if (lzcount < sizeof(bs_cache)*8) { - extract_rice_param_part: - bs_cache <<= lzcount; - bs_consumedBits += lzcount; - if (bs_consumedBits <= riceParamPlus1MaxConsumedBits) { - bs_cache <<= riceParamPlus1; - bs_consumedBits += riceParamPlus1; - } else { - ma_uint32 riceParamPartLoBitCount = bs_consumedBits - riceParamPlus1MaxConsumedBits; - MA_DR_FLAC_ASSERT(riceParamPartLoBitCount > 0 && riceParamPartLoBitCount < 32); - if (bs->nextL2Line < MA_DR_FLAC_CACHE_L2_LINE_COUNT(bs)) { - #ifndef MA_DR_FLAC_NO_CRC - ma_dr_flac__update_crc16(bs); - #endif - bs_cache = ma_dr_flac__be2host__cache_line(bs->cacheL2[bs->nextL2Line++]); - bs_consumedBits = riceParamPartLoBitCount; - #ifndef MA_DR_FLAC_NO_CRC - bs->crc16Cache = bs_cache; - #endif - } else { - if (!ma_dr_flac__reload_cache(bs)) { - return MA_FALSE; - } - if (riceParamPartLoBitCount > MA_DR_FLAC_CACHE_L1_BITS_REMAINING(bs)) { - return MA_FALSE; - } - bs_cache = bs->cache; - bs_consumedBits = bs->consumedBits + riceParamPartLoBitCount; - } - bs_cache <<= riceParamPartLoBitCount; - } - } else { - for (;;) { - if (bs->nextL2Line < MA_DR_FLAC_CACHE_L2_LINE_COUNT(bs)) { - #ifndef MA_DR_FLAC_NO_CRC - ma_dr_flac__update_crc16(bs); - #endif - bs_cache = ma_dr_flac__be2host__cache_line(bs->cacheL2[bs->nextL2Line++]); - bs_consumedBits = 0; - #ifndef MA_DR_FLAC_NO_CRC - bs->crc16Cache = bs_cache; - #endif - } else { - if (!ma_dr_flac__reload_cache(bs)) { - return MA_FALSE; - } - bs_cache = bs->cache; - bs_consumedBits = bs->consumedBits; - } - lzcount = ma_dr_flac__clz(bs_cache); - if (lzcount < sizeof(bs_cache)*8) { - break; - } - } - goto extract_rice_param_part; - } - bs->cache = bs_cache; - bs->consumedBits = bs_consumedBits; - return MA_TRUE; -} -static ma_bool32 ma_dr_flac__decode_samples_with_residual__rice__scalar_zeroorder(ma_dr_flac_bs* bs, ma_uint32 bitsPerSample, ma_uint32 count, ma_uint8 riceParam, ma_uint32 order, ma_int32 shift, const ma_int32* coefficients, ma_int32* pSamplesOut) -{ - ma_uint32 t[2] = {0x00000000, 0xFFFFFFFF}; - ma_uint32 zeroCountPart0; - ma_uint32 riceParamPart0; - ma_uint32 riceParamMask; - ma_uint32 i; - MA_DR_FLAC_ASSERT(bs != NULL); - MA_DR_FLAC_ASSERT(pSamplesOut != NULL); - (void)bitsPerSample; - (void)order; - (void)shift; - (void)coefficients; - riceParamMask = (ma_uint32)~((~0UL) << riceParam); - i = 0; - while (i < count) { - if (!ma_dr_flac__read_rice_parts_x1(bs, riceParam, &zeroCountPart0, &riceParamPart0)) { - return MA_FALSE; - } - riceParamPart0 &= riceParamMask; - riceParamPart0 |= (zeroCountPart0 << riceParam); - riceParamPart0 = (riceParamPart0 >> 1) ^ t[riceParamPart0 & 0x01]; - pSamplesOut[i] = riceParamPart0; - i += 1; - } - return MA_TRUE; -} -static ma_bool32 ma_dr_flac__decode_samples_with_residual__rice__scalar(ma_dr_flac_bs* bs, ma_uint32 bitsPerSample, ma_uint32 count, ma_uint8 riceParam, ma_uint32 lpcOrder, ma_int32 lpcShift, ma_uint32 lpcPrecision, const ma_int32* coefficients, ma_int32* pSamplesOut) -{ - ma_uint32 t[2] = {0x00000000, 0xFFFFFFFF}; - ma_uint32 zeroCountPart0 = 0; - ma_uint32 zeroCountPart1 = 0; - ma_uint32 zeroCountPart2 = 0; - ma_uint32 zeroCountPart3 = 0; - ma_uint32 riceParamPart0 = 0; - ma_uint32 riceParamPart1 = 0; - ma_uint32 riceParamPart2 = 0; - ma_uint32 riceParamPart3 = 0; - ma_uint32 riceParamMask; - const ma_int32* pSamplesOutEnd; - ma_uint32 i; - MA_DR_FLAC_ASSERT(bs != NULL); - MA_DR_FLAC_ASSERT(pSamplesOut != NULL); - if (lpcOrder == 0) { - return ma_dr_flac__decode_samples_with_residual__rice__scalar_zeroorder(bs, bitsPerSample, count, riceParam, lpcOrder, lpcShift, coefficients, pSamplesOut); - } - riceParamMask = (ma_uint32)~((~0UL) << riceParam); - pSamplesOutEnd = pSamplesOut + (count & ~3); - if (ma_dr_flac__use_64_bit_prediction(bitsPerSample, lpcOrder, lpcPrecision)) { - while (pSamplesOut < pSamplesOutEnd) { - if (!ma_dr_flac__read_rice_parts_x1(bs, riceParam, &zeroCountPart0, &riceParamPart0) || - !ma_dr_flac__read_rice_parts_x1(bs, riceParam, &zeroCountPart1, &riceParamPart1) || - !ma_dr_flac__read_rice_parts_x1(bs, riceParam, &zeroCountPart2, &riceParamPart2) || - !ma_dr_flac__read_rice_parts_x1(bs, riceParam, &zeroCountPart3, &riceParamPart3)) { - return MA_FALSE; - } - riceParamPart0 &= riceParamMask; - riceParamPart1 &= riceParamMask; - riceParamPart2 &= riceParamMask; - riceParamPart3 &= riceParamMask; - riceParamPart0 |= (zeroCountPart0 << riceParam); - riceParamPart1 |= (zeroCountPart1 << riceParam); - riceParamPart2 |= (zeroCountPart2 << riceParam); - riceParamPart3 |= (zeroCountPart3 << riceParam); - riceParamPart0 = (riceParamPart0 >> 1) ^ t[riceParamPart0 & 0x01]; - riceParamPart1 = (riceParamPart1 >> 1) ^ t[riceParamPart1 & 0x01]; - riceParamPart2 = (riceParamPart2 >> 1) ^ t[riceParamPart2 & 0x01]; - riceParamPart3 = (riceParamPart3 >> 1) ^ t[riceParamPart3 & 0x01]; - pSamplesOut[0] = riceParamPart0 + ma_dr_flac__calculate_prediction_64(lpcOrder, lpcShift, coefficients, pSamplesOut + 0); - pSamplesOut[1] = riceParamPart1 + ma_dr_flac__calculate_prediction_64(lpcOrder, lpcShift, coefficients, pSamplesOut + 1); - pSamplesOut[2] = riceParamPart2 + ma_dr_flac__calculate_prediction_64(lpcOrder, lpcShift, coefficients, pSamplesOut + 2); - pSamplesOut[3] = riceParamPart3 + ma_dr_flac__calculate_prediction_64(lpcOrder, lpcShift, coefficients, pSamplesOut + 3); - pSamplesOut += 4; - } - } else { - while (pSamplesOut < pSamplesOutEnd) { - if (!ma_dr_flac__read_rice_parts_x1(bs, riceParam, &zeroCountPart0, &riceParamPart0) || - !ma_dr_flac__read_rice_parts_x1(bs, riceParam, &zeroCountPart1, &riceParamPart1) || - !ma_dr_flac__read_rice_parts_x1(bs, riceParam, &zeroCountPart2, &riceParamPart2) || - !ma_dr_flac__read_rice_parts_x1(bs, riceParam, &zeroCountPart3, &riceParamPart3)) { - return MA_FALSE; - } - riceParamPart0 &= riceParamMask; - riceParamPart1 &= riceParamMask; - riceParamPart2 &= riceParamMask; - riceParamPart3 &= riceParamMask; - riceParamPart0 |= (zeroCountPart0 << riceParam); - riceParamPart1 |= (zeroCountPart1 << riceParam); - riceParamPart2 |= (zeroCountPart2 << riceParam); - riceParamPart3 |= (zeroCountPart3 << riceParam); - riceParamPart0 = (riceParamPart0 >> 1) ^ t[riceParamPart0 & 0x01]; - riceParamPart1 = (riceParamPart1 >> 1) ^ t[riceParamPart1 & 0x01]; - riceParamPart2 = (riceParamPart2 >> 1) ^ t[riceParamPart2 & 0x01]; - riceParamPart3 = (riceParamPart3 >> 1) ^ t[riceParamPart3 & 0x01]; - pSamplesOut[0] = riceParamPart0 + ma_dr_flac__calculate_prediction_32(lpcOrder, lpcShift, coefficients, pSamplesOut + 0); - pSamplesOut[1] = riceParamPart1 + ma_dr_flac__calculate_prediction_32(lpcOrder, lpcShift, coefficients, pSamplesOut + 1); - pSamplesOut[2] = riceParamPart2 + ma_dr_flac__calculate_prediction_32(lpcOrder, lpcShift, coefficients, pSamplesOut + 2); - pSamplesOut[3] = riceParamPart3 + ma_dr_flac__calculate_prediction_32(lpcOrder, lpcShift, coefficients, pSamplesOut + 3); - pSamplesOut += 4; - } - } - i = (count & ~3); - while (i < count) { - if (!ma_dr_flac__read_rice_parts_x1(bs, riceParam, &zeroCountPart0, &riceParamPart0)) { - return MA_FALSE; - } - riceParamPart0 &= riceParamMask; - riceParamPart0 |= (zeroCountPart0 << riceParam); - riceParamPart0 = (riceParamPart0 >> 1) ^ t[riceParamPart0 & 0x01]; - if (ma_dr_flac__use_64_bit_prediction(bitsPerSample, lpcOrder, lpcPrecision)) { - pSamplesOut[0] = riceParamPart0 + ma_dr_flac__calculate_prediction_64(lpcOrder, lpcShift, coefficients, pSamplesOut + 0); - } else { - pSamplesOut[0] = riceParamPart0 + ma_dr_flac__calculate_prediction_32(lpcOrder, lpcShift, coefficients, pSamplesOut + 0); - } - i += 1; - pSamplesOut += 1; - } - return MA_TRUE; -} -#if defined(MA_DR_FLAC_SUPPORT_SSE2) -static MA_INLINE __m128i ma_dr_flac__mm_packs_interleaved_epi32(__m128i a, __m128i b) -{ - __m128i r; - r = _mm_packs_epi32(a, b); - r = _mm_shuffle_epi32(r, _MM_SHUFFLE(3, 1, 2, 0)); - r = _mm_shufflehi_epi16(r, _MM_SHUFFLE(3, 1, 2, 0)); - r = _mm_shufflelo_epi16(r, _MM_SHUFFLE(3, 1, 2, 0)); - return r; -} -#endif -#if defined(MA_DR_FLAC_SUPPORT_SSE41) -static MA_INLINE __m128i ma_dr_flac__mm_not_si128(__m128i a) -{ - return _mm_xor_si128(a, _mm_cmpeq_epi32(_mm_setzero_si128(), _mm_setzero_si128())); -} -static MA_INLINE __m128i ma_dr_flac__mm_hadd_epi32(__m128i x) -{ - __m128i x64 = _mm_add_epi32(x, _mm_shuffle_epi32(x, _MM_SHUFFLE(1, 0, 3, 2))); - __m128i x32 = _mm_shufflelo_epi16(x64, _MM_SHUFFLE(1, 0, 3, 2)); - return _mm_add_epi32(x64, x32); -} -static MA_INLINE __m128i ma_dr_flac__mm_hadd_epi64(__m128i x) -{ - return _mm_add_epi64(x, _mm_shuffle_epi32(x, _MM_SHUFFLE(1, 0, 3, 2))); -} -static MA_INLINE __m128i ma_dr_flac__mm_srai_epi64(__m128i x, int count) -{ - __m128i lo = _mm_srli_epi64(x, count); - __m128i hi = _mm_srai_epi32(x, count); - hi = _mm_and_si128(hi, _mm_set_epi32(0xFFFFFFFF, 0, 0xFFFFFFFF, 0)); - return _mm_or_si128(lo, hi); -} -static ma_bool32 ma_dr_flac__decode_samples_with_residual__rice__sse41_32(ma_dr_flac_bs* bs, ma_uint32 count, ma_uint8 riceParam, ma_uint32 order, ma_int32 shift, const ma_int32* coefficients, ma_int32* pSamplesOut) -{ - int i; - ma_uint32 riceParamMask; - ma_int32* pDecodedSamples = pSamplesOut; - ma_int32* pDecodedSamplesEnd = pSamplesOut + (count & ~3); - ma_uint32 zeroCountParts0 = 0; - ma_uint32 zeroCountParts1 = 0; - ma_uint32 zeroCountParts2 = 0; - ma_uint32 zeroCountParts3 = 0; - ma_uint32 riceParamParts0 = 0; - ma_uint32 riceParamParts1 = 0; - ma_uint32 riceParamParts2 = 0; - ma_uint32 riceParamParts3 = 0; - __m128i coefficients128_0; - __m128i coefficients128_4; - __m128i coefficients128_8; - __m128i samples128_0; - __m128i samples128_4; - __m128i samples128_8; - __m128i riceParamMask128; - const ma_uint32 t[2] = {0x00000000, 0xFFFFFFFF}; - riceParamMask = (ma_uint32)~((~0UL) << riceParam); - riceParamMask128 = _mm_set1_epi32(riceParamMask); - coefficients128_0 = _mm_setzero_si128(); - coefficients128_4 = _mm_setzero_si128(); - coefficients128_8 = _mm_setzero_si128(); - samples128_0 = _mm_setzero_si128(); - samples128_4 = _mm_setzero_si128(); - samples128_8 = _mm_setzero_si128(); -#if 1 - { - int runningOrder = order; - if (runningOrder >= 4) { - coefficients128_0 = _mm_loadu_si128((const __m128i*)(coefficients + 0)); - samples128_0 = _mm_loadu_si128((const __m128i*)(pSamplesOut - 4)); - runningOrder -= 4; - } else { - switch (runningOrder) { - case 3: coefficients128_0 = _mm_set_epi32(0, coefficients[2], coefficients[1], coefficients[0]); samples128_0 = _mm_set_epi32(pSamplesOut[-1], pSamplesOut[-2], pSamplesOut[-3], 0); break; - case 2: coefficients128_0 = _mm_set_epi32(0, 0, coefficients[1], coefficients[0]); samples128_0 = _mm_set_epi32(pSamplesOut[-1], pSamplesOut[-2], 0, 0); break; - case 1: coefficients128_0 = _mm_set_epi32(0, 0, 0, coefficients[0]); samples128_0 = _mm_set_epi32(pSamplesOut[-1], 0, 0, 0); break; - } - runningOrder = 0; - } - if (runningOrder >= 4) { - coefficients128_4 = _mm_loadu_si128((const __m128i*)(coefficients + 4)); - samples128_4 = _mm_loadu_si128((const __m128i*)(pSamplesOut - 8)); - runningOrder -= 4; - } else { - switch (runningOrder) { - case 3: coefficients128_4 = _mm_set_epi32(0, coefficients[6], coefficients[5], coefficients[4]); samples128_4 = _mm_set_epi32(pSamplesOut[-5], pSamplesOut[-6], pSamplesOut[-7], 0); break; - case 2: coefficients128_4 = _mm_set_epi32(0, 0, coefficients[5], coefficients[4]); samples128_4 = _mm_set_epi32(pSamplesOut[-5], pSamplesOut[-6], 0, 0); break; - case 1: coefficients128_4 = _mm_set_epi32(0, 0, 0, coefficients[4]); samples128_4 = _mm_set_epi32(pSamplesOut[-5], 0, 0, 0); break; - } - runningOrder = 0; - } - if (runningOrder == 4) { - coefficients128_8 = _mm_loadu_si128((const __m128i*)(coefficients + 8)); - samples128_8 = _mm_loadu_si128((const __m128i*)(pSamplesOut - 12)); - runningOrder -= 4; - } else { - switch (runningOrder) { - case 3: coefficients128_8 = _mm_set_epi32(0, coefficients[10], coefficients[9], coefficients[8]); samples128_8 = _mm_set_epi32(pSamplesOut[-9], pSamplesOut[-10], pSamplesOut[-11], 0); break; - case 2: coefficients128_8 = _mm_set_epi32(0, 0, coefficients[9], coefficients[8]); samples128_8 = _mm_set_epi32(pSamplesOut[-9], pSamplesOut[-10], 0, 0); break; - case 1: coefficients128_8 = _mm_set_epi32(0, 0, 0, coefficients[8]); samples128_8 = _mm_set_epi32(pSamplesOut[-9], 0, 0, 0); break; - } - runningOrder = 0; - } - coefficients128_0 = _mm_shuffle_epi32(coefficients128_0, _MM_SHUFFLE(0, 1, 2, 3)); - coefficients128_4 = _mm_shuffle_epi32(coefficients128_4, _MM_SHUFFLE(0, 1, 2, 3)); - coefficients128_8 = _mm_shuffle_epi32(coefficients128_8, _MM_SHUFFLE(0, 1, 2, 3)); - } -#else - switch (order) - { - case 12: ((ma_int32*)&coefficients128_8)[0] = coefficients[11]; ((ma_int32*)&samples128_8)[0] = pDecodedSamples[-12]; - case 11: ((ma_int32*)&coefficients128_8)[1] = coefficients[10]; ((ma_int32*)&samples128_8)[1] = pDecodedSamples[-11]; - case 10: ((ma_int32*)&coefficients128_8)[2] = coefficients[ 9]; ((ma_int32*)&samples128_8)[2] = pDecodedSamples[-10]; - case 9: ((ma_int32*)&coefficients128_8)[3] = coefficients[ 8]; ((ma_int32*)&samples128_8)[3] = pDecodedSamples[- 9]; - case 8: ((ma_int32*)&coefficients128_4)[0] = coefficients[ 7]; ((ma_int32*)&samples128_4)[0] = pDecodedSamples[- 8]; - case 7: ((ma_int32*)&coefficients128_4)[1] = coefficients[ 6]; ((ma_int32*)&samples128_4)[1] = pDecodedSamples[- 7]; - case 6: ((ma_int32*)&coefficients128_4)[2] = coefficients[ 5]; ((ma_int32*)&samples128_4)[2] = pDecodedSamples[- 6]; - case 5: ((ma_int32*)&coefficients128_4)[3] = coefficients[ 4]; ((ma_int32*)&samples128_4)[3] = pDecodedSamples[- 5]; - case 4: ((ma_int32*)&coefficients128_0)[0] = coefficients[ 3]; ((ma_int32*)&samples128_0)[0] = pDecodedSamples[- 4]; - case 3: ((ma_int32*)&coefficients128_0)[1] = coefficients[ 2]; ((ma_int32*)&samples128_0)[1] = pDecodedSamples[- 3]; - case 2: ((ma_int32*)&coefficients128_0)[2] = coefficients[ 1]; ((ma_int32*)&samples128_0)[2] = pDecodedSamples[- 2]; - case 1: ((ma_int32*)&coefficients128_0)[3] = coefficients[ 0]; ((ma_int32*)&samples128_0)[3] = pDecodedSamples[- 1]; - } -#endif - while (pDecodedSamples < pDecodedSamplesEnd) { - __m128i prediction128; - __m128i zeroCountPart128; - __m128i riceParamPart128; - if (!ma_dr_flac__read_rice_parts_x1(bs, riceParam, &zeroCountParts0, &riceParamParts0) || - !ma_dr_flac__read_rice_parts_x1(bs, riceParam, &zeroCountParts1, &riceParamParts1) || - !ma_dr_flac__read_rice_parts_x1(bs, riceParam, &zeroCountParts2, &riceParamParts2) || - !ma_dr_flac__read_rice_parts_x1(bs, riceParam, &zeroCountParts3, &riceParamParts3)) { - return MA_FALSE; - } - zeroCountPart128 = _mm_set_epi32(zeroCountParts3, zeroCountParts2, zeroCountParts1, zeroCountParts0); - riceParamPart128 = _mm_set_epi32(riceParamParts3, riceParamParts2, riceParamParts1, riceParamParts0); - riceParamPart128 = _mm_and_si128(riceParamPart128, riceParamMask128); - riceParamPart128 = _mm_or_si128(riceParamPart128, _mm_slli_epi32(zeroCountPart128, riceParam)); - riceParamPart128 = _mm_xor_si128(_mm_srli_epi32(riceParamPart128, 1), _mm_add_epi32(ma_dr_flac__mm_not_si128(_mm_and_si128(riceParamPart128, _mm_set1_epi32(0x01))), _mm_set1_epi32(0x01))); - if (order <= 4) { - for (i = 0; i < 4; i += 1) { - prediction128 = _mm_mullo_epi32(coefficients128_0, samples128_0); - prediction128 = ma_dr_flac__mm_hadd_epi32(prediction128); - prediction128 = _mm_srai_epi32(prediction128, shift); - prediction128 = _mm_add_epi32(riceParamPart128, prediction128); - samples128_0 = _mm_alignr_epi8(prediction128, samples128_0, 4); - riceParamPart128 = _mm_alignr_epi8(_mm_setzero_si128(), riceParamPart128, 4); - } - } else if (order <= 8) { - for (i = 0; i < 4; i += 1) { - prediction128 = _mm_mullo_epi32(coefficients128_4, samples128_4); - prediction128 = _mm_add_epi32(prediction128, _mm_mullo_epi32(coefficients128_0, samples128_0)); - prediction128 = ma_dr_flac__mm_hadd_epi32(prediction128); - prediction128 = _mm_srai_epi32(prediction128, shift); - prediction128 = _mm_add_epi32(riceParamPart128, prediction128); - samples128_4 = _mm_alignr_epi8(samples128_0, samples128_4, 4); - samples128_0 = _mm_alignr_epi8(prediction128, samples128_0, 4); - riceParamPart128 = _mm_alignr_epi8(_mm_setzero_si128(), riceParamPart128, 4); - } - } else { - for (i = 0; i < 4; i += 1) { - prediction128 = _mm_mullo_epi32(coefficients128_8, samples128_8); - prediction128 = _mm_add_epi32(prediction128, _mm_mullo_epi32(coefficients128_4, samples128_4)); - prediction128 = _mm_add_epi32(prediction128, _mm_mullo_epi32(coefficients128_0, samples128_0)); - prediction128 = ma_dr_flac__mm_hadd_epi32(prediction128); - prediction128 = _mm_srai_epi32(prediction128, shift); - prediction128 = _mm_add_epi32(riceParamPart128, prediction128); - samples128_8 = _mm_alignr_epi8(samples128_4, samples128_8, 4); - samples128_4 = _mm_alignr_epi8(samples128_0, samples128_4, 4); - samples128_0 = _mm_alignr_epi8(prediction128, samples128_0, 4); - riceParamPart128 = _mm_alignr_epi8(_mm_setzero_si128(), riceParamPart128, 4); - } - } - _mm_storeu_si128((__m128i*)pDecodedSamples, samples128_0); - pDecodedSamples += 4; - } - i = (count & ~3); - while (i < (int)count) { - if (!ma_dr_flac__read_rice_parts_x1(bs, riceParam, &zeroCountParts0, &riceParamParts0)) { - return MA_FALSE; - } - riceParamParts0 &= riceParamMask; - riceParamParts0 |= (zeroCountParts0 << riceParam); - riceParamParts0 = (riceParamParts0 >> 1) ^ t[riceParamParts0 & 0x01]; - pDecodedSamples[0] = riceParamParts0 + ma_dr_flac__calculate_prediction_32(order, shift, coefficients, pDecodedSamples); - i += 1; - pDecodedSamples += 1; - } - return MA_TRUE; -} -static ma_bool32 ma_dr_flac__decode_samples_with_residual__rice__sse41_64(ma_dr_flac_bs* bs, ma_uint32 count, ma_uint8 riceParam, ma_uint32 order, ma_int32 shift, const ma_int32* coefficients, ma_int32* pSamplesOut) -{ - int i; - ma_uint32 riceParamMask; - ma_int32* pDecodedSamples = pSamplesOut; - ma_int32* pDecodedSamplesEnd = pSamplesOut + (count & ~3); - ma_uint32 zeroCountParts0 = 0; - ma_uint32 zeroCountParts1 = 0; - ma_uint32 zeroCountParts2 = 0; - ma_uint32 zeroCountParts3 = 0; - ma_uint32 riceParamParts0 = 0; - ma_uint32 riceParamParts1 = 0; - ma_uint32 riceParamParts2 = 0; - ma_uint32 riceParamParts3 = 0; - __m128i coefficients128_0; - __m128i coefficients128_4; - __m128i coefficients128_8; - __m128i samples128_0; - __m128i samples128_4; - __m128i samples128_8; - __m128i prediction128; - __m128i riceParamMask128; - const ma_uint32 t[2] = {0x00000000, 0xFFFFFFFF}; - MA_DR_FLAC_ASSERT(order <= 12); - riceParamMask = (ma_uint32)~((~0UL) << riceParam); - riceParamMask128 = _mm_set1_epi32(riceParamMask); - prediction128 = _mm_setzero_si128(); - coefficients128_0 = _mm_setzero_si128(); - coefficients128_4 = _mm_setzero_si128(); - coefficients128_8 = _mm_setzero_si128(); - samples128_0 = _mm_setzero_si128(); - samples128_4 = _mm_setzero_si128(); - samples128_8 = _mm_setzero_si128(); -#if 1 - { - int runningOrder = order; - if (runningOrder >= 4) { - coefficients128_0 = _mm_loadu_si128((const __m128i*)(coefficients + 0)); - samples128_0 = _mm_loadu_si128((const __m128i*)(pSamplesOut - 4)); - runningOrder -= 4; - } else { - switch (runningOrder) { - case 3: coefficients128_0 = _mm_set_epi32(0, coefficients[2], coefficients[1], coefficients[0]); samples128_0 = _mm_set_epi32(pSamplesOut[-1], pSamplesOut[-2], pSamplesOut[-3], 0); break; - case 2: coefficients128_0 = _mm_set_epi32(0, 0, coefficients[1], coefficients[0]); samples128_0 = _mm_set_epi32(pSamplesOut[-1], pSamplesOut[-2], 0, 0); break; - case 1: coefficients128_0 = _mm_set_epi32(0, 0, 0, coefficients[0]); samples128_0 = _mm_set_epi32(pSamplesOut[-1], 0, 0, 0); break; - } - runningOrder = 0; - } - if (runningOrder >= 4) { - coefficients128_4 = _mm_loadu_si128((const __m128i*)(coefficients + 4)); - samples128_4 = _mm_loadu_si128((const __m128i*)(pSamplesOut - 8)); - runningOrder -= 4; - } else { - switch (runningOrder) { - case 3: coefficients128_4 = _mm_set_epi32(0, coefficients[6], coefficients[5], coefficients[4]); samples128_4 = _mm_set_epi32(pSamplesOut[-5], pSamplesOut[-6], pSamplesOut[-7], 0); break; - case 2: coefficients128_4 = _mm_set_epi32(0, 0, coefficients[5], coefficients[4]); samples128_4 = _mm_set_epi32(pSamplesOut[-5], pSamplesOut[-6], 0, 0); break; - case 1: coefficients128_4 = _mm_set_epi32(0, 0, 0, coefficients[4]); samples128_4 = _mm_set_epi32(pSamplesOut[-5], 0, 0, 0); break; - } - runningOrder = 0; - } - if (runningOrder == 4) { - coefficients128_8 = _mm_loadu_si128((const __m128i*)(coefficients + 8)); - samples128_8 = _mm_loadu_si128((const __m128i*)(pSamplesOut - 12)); - runningOrder -= 4; - } else { - switch (runningOrder) { - case 3: coefficients128_8 = _mm_set_epi32(0, coefficients[10], coefficients[9], coefficients[8]); samples128_8 = _mm_set_epi32(pSamplesOut[-9], pSamplesOut[-10], pSamplesOut[-11], 0); break; - case 2: coefficients128_8 = _mm_set_epi32(0, 0, coefficients[9], coefficients[8]); samples128_8 = _mm_set_epi32(pSamplesOut[-9], pSamplesOut[-10], 0, 0); break; - case 1: coefficients128_8 = _mm_set_epi32(0, 0, 0, coefficients[8]); samples128_8 = _mm_set_epi32(pSamplesOut[-9], 0, 0, 0); break; - } - runningOrder = 0; - } - coefficients128_0 = _mm_shuffle_epi32(coefficients128_0, _MM_SHUFFLE(0, 1, 2, 3)); - coefficients128_4 = _mm_shuffle_epi32(coefficients128_4, _MM_SHUFFLE(0, 1, 2, 3)); - coefficients128_8 = _mm_shuffle_epi32(coefficients128_8, _MM_SHUFFLE(0, 1, 2, 3)); - } -#else - switch (order) - { - case 12: ((ma_int32*)&coefficients128_8)[0] = coefficients[11]; ((ma_int32*)&samples128_8)[0] = pDecodedSamples[-12]; - case 11: ((ma_int32*)&coefficients128_8)[1] = coefficients[10]; ((ma_int32*)&samples128_8)[1] = pDecodedSamples[-11]; - case 10: ((ma_int32*)&coefficients128_8)[2] = coefficients[ 9]; ((ma_int32*)&samples128_8)[2] = pDecodedSamples[-10]; - case 9: ((ma_int32*)&coefficients128_8)[3] = coefficients[ 8]; ((ma_int32*)&samples128_8)[3] = pDecodedSamples[- 9]; - case 8: ((ma_int32*)&coefficients128_4)[0] = coefficients[ 7]; ((ma_int32*)&samples128_4)[0] = pDecodedSamples[- 8]; - case 7: ((ma_int32*)&coefficients128_4)[1] = coefficients[ 6]; ((ma_int32*)&samples128_4)[1] = pDecodedSamples[- 7]; - case 6: ((ma_int32*)&coefficients128_4)[2] = coefficients[ 5]; ((ma_int32*)&samples128_4)[2] = pDecodedSamples[- 6]; - case 5: ((ma_int32*)&coefficients128_4)[3] = coefficients[ 4]; ((ma_int32*)&samples128_4)[3] = pDecodedSamples[- 5]; - case 4: ((ma_int32*)&coefficients128_0)[0] = coefficients[ 3]; ((ma_int32*)&samples128_0)[0] = pDecodedSamples[- 4]; - case 3: ((ma_int32*)&coefficients128_0)[1] = coefficients[ 2]; ((ma_int32*)&samples128_0)[1] = pDecodedSamples[- 3]; - case 2: ((ma_int32*)&coefficients128_0)[2] = coefficients[ 1]; ((ma_int32*)&samples128_0)[2] = pDecodedSamples[- 2]; - case 1: ((ma_int32*)&coefficients128_0)[3] = coefficients[ 0]; ((ma_int32*)&samples128_0)[3] = pDecodedSamples[- 1]; - } -#endif - while (pDecodedSamples < pDecodedSamplesEnd) { - __m128i zeroCountPart128; - __m128i riceParamPart128; - if (!ma_dr_flac__read_rice_parts_x1(bs, riceParam, &zeroCountParts0, &riceParamParts0) || - !ma_dr_flac__read_rice_parts_x1(bs, riceParam, &zeroCountParts1, &riceParamParts1) || - !ma_dr_flac__read_rice_parts_x1(bs, riceParam, &zeroCountParts2, &riceParamParts2) || - !ma_dr_flac__read_rice_parts_x1(bs, riceParam, &zeroCountParts3, &riceParamParts3)) { - return MA_FALSE; - } - zeroCountPart128 = _mm_set_epi32(zeroCountParts3, zeroCountParts2, zeroCountParts1, zeroCountParts0); - riceParamPart128 = _mm_set_epi32(riceParamParts3, riceParamParts2, riceParamParts1, riceParamParts0); - riceParamPart128 = _mm_and_si128(riceParamPart128, riceParamMask128); - riceParamPart128 = _mm_or_si128(riceParamPart128, _mm_slli_epi32(zeroCountPart128, riceParam)); - riceParamPart128 = _mm_xor_si128(_mm_srli_epi32(riceParamPart128, 1), _mm_add_epi32(ma_dr_flac__mm_not_si128(_mm_and_si128(riceParamPart128, _mm_set1_epi32(1))), _mm_set1_epi32(1))); - for (i = 0; i < 4; i += 1) { - prediction128 = _mm_xor_si128(prediction128, prediction128); - switch (order) - { - case 12: - case 11: prediction128 = _mm_add_epi64(prediction128, _mm_mul_epi32(_mm_shuffle_epi32(coefficients128_8, _MM_SHUFFLE(1, 1, 0, 0)), _mm_shuffle_epi32(samples128_8, _MM_SHUFFLE(1, 1, 0, 0)))); - case 10: - case 9: prediction128 = _mm_add_epi64(prediction128, _mm_mul_epi32(_mm_shuffle_epi32(coefficients128_8, _MM_SHUFFLE(3, 3, 2, 2)), _mm_shuffle_epi32(samples128_8, _MM_SHUFFLE(3, 3, 2, 2)))); - case 8: - case 7: prediction128 = _mm_add_epi64(prediction128, _mm_mul_epi32(_mm_shuffle_epi32(coefficients128_4, _MM_SHUFFLE(1, 1, 0, 0)), _mm_shuffle_epi32(samples128_4, _MM_SHUFFLE(1, 1, 0, 0)))); - case 6: - case 5: prediction128 = _mm_add_epi64(prediction128, _mm_mul_epi32(_mm_shuffle_epi32(coefficients128_4, _MM_SHUFFLE(3, 3, 2, 2)), _mm_shuffle_epi32(samples128_4, _MM_SHUFFLE(3, 3, 2, 2)))); - case 4: - case 3: prediction128 = _mm_add_epi64(prediction128, _mm_mul_epi32(_mm_shuffle_epi32(coefficients128_0, _MM_SHUFFLE(1, 1, 0, 0)), _mm_shuffle_epi32(samples128_0, _MM_SHUFFLE(1, 1, 0, 0)))); - case 2: - case 1: prediction128 = _mm_add_epi64(prediction128, _mm_mul_epi32(_mm_shuffle_epi32(coefficients128_0, _MM_SHUFFLE(3, 3, 2, 2)), _mm_shuffle_epi32(samples128_0, _MM_SHUFFLE(3, 3, 2, 2)))); - } - prediction128 = ma_dr_flac__mm_hadd_epi64(prediction128); - prediction128 = ma_dr_flac__mm_srai_epi64(prediction128, shift); - prediction128 = _mm_add_epi32(riceParamPart128, prediction128); - samples128_8 = _mm_alignr_epi8(samples128_4, samples128_8, 4); - samples128_4 = _mm_alignr_epi8(samples128_0, samples128_4, 4); - samples128_0 = _mm_alignr_epi8(prediction128, samples128_0, 4); - riceParamPart128 = _mm_alignr_epi8(_mm_setzero_si128(), riceParamPart128, 4); - } - _mm_storeu_si128((__m128i*)pDecodedSamples, samples128_0); - pDecodedSamples += 4; - } - i = (count & ~3); - while (i < (int)count) { - if (!ma_dr_flac__read_rice_parts_x1(bs, riceParam, &zeroCountParts0, &riceParamParts0)) { - return MA_FALSE; - } - riceParamParts0 &= riceParamMask; - riceParamParts0 |= (zeroCountParts0 << riceParam); - riceParamParts0 = (riceParamParts0 >> 1) ^ t[riceParamParts0 & 0x01]; - pDecodedSamples[0] = riceParamParts0 + ma_dr_flac__calculate_prediction_64(order, shift, coefficients, pDecodedSamples); - i += 1; - pDecodedSamples += 1; - } - return MA_TRUE; -} -static ma_bool32 ma_dr_flac__decode_samples_with_residual__rice__sse41(ma_dr_flac_bs* bs, ma_uint32 bitsPerSample, ma_uint32 count, ma_uint8 riceParam, ma_uint32 lpcOrder, ma_int32 lpcShift, ma_uint32 lpcPrecision, const ma_int32* coefficients, ma_int32* pSamplesOut) -{ - MA_DR_FLAC_ASSERT(bs != NULL); - MA_DR_FLAC_ASSERT(pSamplesOut != NULL); - if (lpcOrder > 0 && lpcOrder <= 12) { - if (ma_dr_flac__use_64_bit_prediction(bitsPerSample, lpcOrder, lpcPrecision)) { - return ma_dr_flac__decode_samples_with_residual__rice__sse41_64(bs, count, riceParam, lpcOrder, lpcShift, coefficients, pSamplesOut); - } else { - return ma_dr_flac__decode_samples_with_residual__rice__sse41_32(bs, count, riceParam, lpcOrder, lpcShift, coefficients, pSamplesOut); - } - } else { - return ma_dr_flac__decode_samples_with_residual__rice__scalar(bs, bitsPerSample, count, riceParam, lpcOrder, lpcShift, lpcPrecision, coefficients, pSamplesOut); - } -} -#endif -#if defined(MA_DR_FLAC_SUPPORT_NEON) -static MA_INLINE void ma_dr_flac__vst2q_s32(ma_int32* p, int32x4x2_t x) -{ - vst1q_s32(p+0, x.val[0]); - vst1q_s32(p+4, x.val[1]); -} -static MA_INLINE void ma_dr_flac__vst2q_u32(ma_uint32* p, uint32x4x2_t x) -{ - vst1q_u32(p+0, x.val[0]); - vst1q_u32(p+4, x.val[1]); -} -static MA_INLINE void ma_dr_flac__vst2q_f32(float* p, float32x4x2_t x) -{ - vst1q_f32(p+0, x.val[0]); - vst1q_f32(p+4, x.val[1]); -} -static MA_INLINE void ma_dr_flac__vst2q_s16(ma_int16* p, int16x4x2_t x) -{ - vst1q_s16(p, vcombine_s16(x.val[0], x.val[1])); -} -static MA_INLINE void ma_dr_flac__vst2q_u16(ma_uint16* p, uint16x4x2_t x) -{ - vst1q_u16(p, vcombine_u16(x.val[0], x.val[1])); -} -static MA_INLINE int32x4_t ma_dr_flac__vdupq_n_s32x4(ma_int32 x3, ma_int32 x2, ma_int32 x1, ma_int32 x0) -{ - ma_int32 x[4]; - x[3] = x3; - x[2] = x2; - x[1] = x1; - x[0] = x0; - return vld1q_s32(x); -} -static MA_INLINE int32x4_t ma_dr_flac__valignrq_s32_1(int32x4_t a, int32x4_t b) -{ - return vextq_s32(b, a, 1); -} -static MA_INLINE uint32x4_t ma_dr_flac__valignrq_u32_1(uint32x4_t a, uint32x4_t b) -{ - return vextq_u32(b, a, 1); -} -static MA_INLINE int32x2_t ma_dr_flac__vhaddq_s32(int32x4_t x) -{ - int32x2_t r = vadd_s32(vget_high_s32(x), vget_low_s32(x)); - return vpadd_s32(r, r); -} -static MA_INLINE int64x1_t ma_dr_flac__vhaddq_s64(int64x2_t x) -{ - return vadd_s64(vget_high_s64(x), vget_low_s64(x)); -} -static MA_INLINE int32x4_t ma_dr_flac__vrevq_s32(int32x4_t x) -{ - return vrev64q_s32(vcombine_s32(vget_high_s32(x), vget_low_s32(x))); -} -static MA_INLINE int32x4_t ma_dr_flac__vnotq_s32(int32x4_t x) -{ - return veorq_s32(x, vdupq_n_s32(0xFFFFFFFF)); -} -static MA_INLINE uint32x4_t ma_dr_flac__vnotq_u32(uint32x4_t x) -{ - return veorq_u32(x, vdupq_n_u32(0xFFFFFFFF)); -} -static ma_bool32 ma_dr_flac__decode_samples_with_residual__rice__neon_32(ma_dr_flac_bs* bs, ma_uint32 count, ma_uint8 riceParam, ma_uint32 order, ma_int32 shift, const ma_int32* coefficients, ma_int32* pSamplesOut) -{ - int i; - ma_uint32 riceParamMask; - ma_int32* pDecodedSamples = pSamplesOut; - ma_int32* pDecodedSamplesEnd = pSamplesOut + (count & ~3); - ma_uint32 zeroCountParts[4]; - ma_uint32 riceParamParts[4]; - int32x4_t coefficients128_0; - int32x4_t coefficients128_4; - int32x4_t coefficients128_8; - int32x4_t samples128_0; - int32x4_t samples128_4; - int32x4_t samples128_8; - uint32x4_t riceParamMask128; - int32x4_t riceParam128; - int32x2_t shift64; - uint32x4_t one128; - const ma_uint32 t[2] = {0x00000000, 0xFFFFFFFF}; - riceParamMask = (ma_uint32)~((~0UL) << riceParam); - riceParamMask128 = vdupq_n_u32(riceParamMask); - riceParam128 = vdupq_n_s32(riceParam); - shift64 = vdup_n_s32(-shift); - one128 = vdupq_n_u32(1); - { - int runningOrder = order; - ma_int32 tempC[4] = {0, 0, 0, 0}; - ma_int32 tempS[4] = {0, 0, 0, 0}; - if (runningOrder >= 4) { - coefficients128_0 = vld1q_s32(coefficients + 0); - samples128_0 = vld1q_s32(pSamplesOut - 4); - runningOrder -= 4; - } else { - switch (runningOrder) { - case 3: tempC[2] = coefficients[2]; tempS[1] = pSamplesOut[-3]; - case 2: tempC[1] = coefficients[1]; tempS[2] = pSamplesOut[-2]; - case 1: tempC[0] = coefficients[0]; tempS[3] = pSamplesOut[-1]; - } - coefficients128_0 = vld1q_s32(tempC); - samples128_0 = vld1q_s32(tempS); - runningOrder = 0; - } - if (runningOrder >= 4) { - coefficients128_4 = vld1q_s32(coefficients + 4); - samples128_4 = vld1q_s32(pSamplesOut - 8); - runningOrder -= 4; - } else { - switch (runningOrder) { - case 3: tempC[2] = coefficients[6]; tempS[1] = pSamplesOut[-7]; - case 2: tempC[1] = coefficients[5]; tempS[2] = pSamplesOut[-6]; - case 1: tempC[0] = coefficients[4]; tempS[3] = pSamplesOut[-5]; - } - coefficients128_4 = vld1q_s32(tempC); - samples128_4 = vld1q_s32(tempS); - runningOrder = 0; - } - if (runningOrder == 4) { - coefficients128_8 = vld1q_s32(coefficients + 8); - samples128_8 = vld1q_s32(pSamplesOut - 12); - runningOrder -= 4; - } else { - switch (runningOrder) { - case 3: tempC[2] = coefficients[10]; tempS[1] = pSamplesOut[-11]; - case 2: tempC[1] = coefficients[ 9]; tempS[2] = pSamplesOut[-10]; - case 1: tempC[0] = coefficients[ 8]; tempS[3] = pSamplesOut[- 9]; - } - coefficients128_8 = vld1q_s32(tempC); - samples128_8 = vld1q_s32(tempS); - runningOrder = 0; - } - coefficients128_0 = ma_dr_flac__vrevq_s32(coefficients128_0); - coefficients128_4 = ma_dr_flac__vrevq_s32(coefficients128_4); - coefficients128_8 = ma_dr_flac__vrevq_s32(coefficients128_8); - } - while (pDecodedSamples < pDecodedSamplesEnd) { - int32x4_t prediction128; - int32x2_t prediction64; - uint32x4_t zeroCountPart128; - uint32x4_t riceParamPart128; - if (!ma_dr_flac__read_rice_parts_x1(bs, riceParam, &zeroCountParts[0], &riceParamParts[0]) || - !ma_dr_flac__read_rice_parts_x1(bs, riceParam, &zeroCountParts[1], &riceParamParts[1]) || - !ma_dr_flac__read_rice_parts_x1(bs, riceParam, &zeroCountParts[2], &riceParamParts[2]) || - !ma_dr_flac__read_rice_parts_x1(bs, riceParam, &zeroCountParts[3], &riceParamParts[3])) { - return MA_FALSE; - } - zeroCountPart128 = vld1q_u32(zeroCountParts); - riceParamPart128 = vld1q_u32(riceParamParts); - riceParamPart128 = vandq_u32(riceParamPart128, riceParamMask128); - riceParamPart128 = vorrq_u32(riceParamPart128, vshlq_u32(zeroCountPart128, riceParam128)); - riceParamPart128 = veorq_u32(vshrq_n_u32(riceParamPart128, 1), vaddq_u32(ma_dr_flac__vnotq_u32(vandq_u32(riceParamPart128, one128)), one128)); - if (order <= 4) { - for (i = 0; i < 4; i += 1) { - prediction128 = vmulq_s32(coefficients128_0, samples128_0); - prediction64 = ma_dr_flac__vhaddq_s32(prediction128); - prediction64 = vshl_s32(prediction64, shift64); - prediction64 = vadd_s32(prediction64, vget_low_s32(vreinterpretq_s32_u32(riceParamPart128))); - samples128_0 = ma_dr_flac__valignrq_s32_1(vcombine_s32(prediction64, vdup_n_s32(0)), samples128_0); - riceParamPart128 = ma_dr_flac__valignrq_u32_1(vdupq_n_u32(0), riceParamPart128); - } - } else if (order <= 8) { - for (i = 0; i < 4; i += 1) { - prediction128 = vmulq_s32(coefficients128_4, samples128_4); - prediction128 = vmlaq_s32(prediction128, coefficients128_0, samples128_0); - prediction64 = ma_dr_flac__vhaddq_s32(prediction128); - prediction64 = vshl_s32(prediction64, shift64); - prediction64 = vadd_s32(prediction64, vget_low_s32(vreinterpretq_s32_u32(riceParamPart128))); - samples128_4 = ma_dr_flac__valignrq_s32_1(samples128_0, samples128_4); - samples128_0 = ma_dr_flac__valignrq_s32_1(vcombine_s32(prediction64, vdup_n_s32(0)), samples128_0); - riceParamPart128 = ma_dr_flac__valignrq_u32_1(vdupq_n_u32(0), riceParamPart128); - } - } else { - for (i = 0; i < 4; i += 1) { - prediction128 = vmulq_s32(coefficients128_8, samples128_8); - prediction128 = vmlaq_s32(prediction128, coefficients128_4, samples128_4); - prediction128 = vmlaq_s32(prediction128, coefficients128_0, samples128_0); - prediction64 = ma_dr_flac__vhaddq_s32(prediction128); - prediction64 = vshl_s32(prediction64, shift64); - prediction64 = vadd_s32(prediction64, vget_low_s32(vreinterpretq_s32_u32(riceParamPart128))); - samples128_8 = ma_dr_flac__valignrq_s32_1(samples128_4, samples128_8); - samples128_4 = ma_dr_flac__valignrq_s32_1(samples128_0, samples128_4); - samples128_0 = ma_dr_flac__valignrq_s32_1(vcombine_s32(prediction64, vdup_n_s32(0)), samples128_0); - riceParamPart128 = ma_dr_flac__valignrq_u32_1(vdupq_n_u32(0), riceParamPart128); - } - } - vst1q_s32(pDecodedSamples, samples128_0); - pDecodedSamples += 4; - } - i = (count & ~3); - while (i < (int)count) { - if (!ma_dr_flac__read_rice_parts_x1(bs, riceParam, &zeroCountParts[0], &riceParamParts[0])) { - return MA_FALSE; - } - riceParamParts[0] &= riceParamMask; - riceParamParts[0] |= (zeroCountParts[0] << riceParam); - riceParamParts[0] = (riceParamParts[0] >> 1) ^ t[riceParamParts[0] & 0x01]; - pDecodedSamples[0] = riceParamParts[0] + ma_dr_flac__calculate_prediction_32(order, shift, coefficients, pDecodedSamples); - i += 1; - pDecodedSamples += 1; - } - return MA_TRUE; -} -static ma_bool32 ma_dr_flac__decode_samples_with_residual__rice__neon_64(ma_dr_flac_bs* bs, ma_uint32 count, ma_uint8 riceParam, ma_uint32 order, ma_int32 shift, const ma_int32* coefficients, ma_int32* pSamplesOut) -{ - int i; - ma_uint32 riceParamMask; - ma_int32* pDecodedSamples = pSamplesOut; - ma_int32* pDecodedSamplesEnd = pSamplesOut + (count & ~3); - ma_uint32 zeroCountParts[4]; - ma_uint32 riceParamParts[4]; - int32x4_t coefficients128_0; - int32x4_t coefficients128_4; - int32x4_t coefficients128_8; - int32x4_t samples128_0; - int32x4_t samples128_4; - int32x4_t samples128_8; - uint32x4_t riceParamMask128; - int32x4_t riceParam128; - int64x1_t shift64; - uint32x4_t one128; - int64x2_t prediction128 = { 0 }; - uint32x4_t zeroCountPart128; - uint32x4_t riceParamPart128; - const ma_uint32 t[2] = {0x00000000, 0xFFFFFFFF}; - riceParamMask = (ma_uint32)~((~0UL) << riceParam); - riceParamMask128 = vdupq_n_u32(riceParamMask); - riceParam128 = vdupq_n_s32(riceParam); - shift64 = vdup_n_s64(-shift); - one128 = vdupq_n_u32(1); - { - int runningOrder = order; - ma_int32 tempC[4] = {0, 0, 0, 0}; - ma_int32 tempS[4] = {0, 0, 0, 0}; - if (runningOrder >= 4) { - coefficients128_0 = vld1q_s32(coefficients + 0); - samples128_0 = vld1q_s32(pSamplesOut - 4); - runningOrder -= 4; - } else { - switch (runningOrder) { - case 3: tempC[2] = coefficients[2]; tempS[1] = pSamplesOut[-3]; - case 2: tempC[1] = coefficients[1]; tempS[2] = pSamplesOut[-2]; - case 1: tempC[0] = coefficients[0]; tempS[3] = pSamplesOut[-1]; - } - coefficients128_0 = vld1q_s32(tempC); - samples128_0 = vld1q_s32(tempS); - runningOrder = 0; - } - if (runningOrder >= 4) { - coefficients128_4 = vld1q_s32(coefficients + 4); - samples128_4 = vld1q_s32(pSamplesOut - 8); - runningOrder -= 4; - } else { - switch (runningOrder) { - case 3: tempC[2] = coefficients[6]; tempS[1] = pSamplesOut[-7]; - case 2: tempC[1] = coefficients[5]; tempS[2] = pSamplesOut[-6]; - case 1: tempC[0] = coefficients[4]; tempS[3] = pSamplesOut[-5]; - } - coefficients128_4 = vld1q_s32(tempC); - samples128_4 = vld1q_s32(tempS); - runningOrder = 0; - } - if (runningOrder == 4) { - coefficients128_8 = vld1q_s32(coefficients + 8); - samples128_8 = vld1q_s32(pSamplesOut - 12); - runningOrder -= 4; - } else { - switch (runningOrder) { - case 3: tempC[2] = coefficients[10]; tempS[1] = pSamplesOut[-11]; - case 2: tempC[1] = coefficients[ 9]; tempS[2] = pSamplesOut[-10]; - case 1: tempC[0] = coefficients[ 8]; tempS[3] = pSamplesOut[- 9]; - } - coefficients128_8 = vld1q_s32(tempC); - samples128_8 = vld1q_s32(tempS); - runningOrder = 0; - } - coefficients128_0 = ma_dr_flac__vrevq_s32(coefficients128_0); - coefficients128_4 = ma_dr_flac__vrevq_s32(coefficients128_4); - coefficients128_8 = ma_dr_flac__vrevq_s32(coefficients128_8); - } - while (pDecodedSamples < pDecodedSamplesEnd) { - if (!ma_dr_flac__read_rice_parts_x1(bs, riceParam, &zeroCountParts[0], &riceParamParts[0]) || - !ma_dr_flac__read_rice_parts_x1(bs, riceParam, &zeroCountParts[1], &riceParamParts[1]) || - !ma_dr_flac__read_rice_parts_x1(bs, riceParam, &zeroCountParts[2], &riceParamParts[2]) || - !ma_dr_flac__read_rice_parts_x1(bs, riceParam, &zeroCountParts[3], &riceParamParts[3])) { - return MA_FALSE; - } - zeroCountPart128 = vld1q_u32(zeroCountParts); - riceParamPart128 = vld1q_u32(riceParamParts); - riceParamPart128 = vandq_u32(riceParamPart128, riceParamMask128); - riceParamPart128 = vorrq_u32(riceParamPart128, vshlq_u32(zeroCountPart128, riceParam128)); - riceParamPart128 = veorq_u32(vshrq_n_u32(riceParamPart128, 1), vaddq_u32(ma_dr_flac__vnotq_u32(vandq_u32(riceParamPart128, one128)), one128)); - for (i = 0; i < 4; i += 1) { - int64x1_t prediction64; - prediction128 = veorq_s64(prediction128, prediction128); - switch (order) - { - case 12: - case 11: prediction128 = vaddq_s64(prediction128, vmull_s32(vget_low_s32(coefficients128_8), vget_low_s32(samples128_8))); - case 10: - case 9: prediction128 = vaddq_s64(prediction128, vmull_s32(vget_high_s32(coefficients128_8), vget_high_s32(samples128_8))); - case 8: - case 7: prediction128 = vaddq_s64(prediction128, vmull_s32(vget_low_s32(coefficients128_4), vget_low_s32(samples128_4))); - case 6: - case 5: prediction128 = vaddq_s64(prediction128, vmull_s32(vget_high_s32(coefficients128_4), vget_high_s32(samples128_4))); - case 4: - case 3: prediction128 = vaddq_s64(prediction128, vmull_s32(vget_low_s32(coefficients128_0), vget_low_s32(samples128_0))); - case 2: - case 1: prediction128 = vaddq_s64(prediction128, vmull_s32(vget_high_s32(coefficients128_0), vget_high_s32(samples128_0))); - } - prediction64 = ma_dr_flac__vhaddq_s64(prediction128); - prediction64 = vshl_s64(prediction64, shift64); - prediction64 = vadd_s64(prediction64, vdup_n_s64(vgetq_lane_u32(riceParamPart128, 0))); - samples128_8 = ma_dr_flac__valignrq_s32_1(samples128_4, samples128_8); - samples128_4 = ma_dr_flac__valignrq_s32_1(samples128_0, samples128_4); - samples128_0 = ma_dr_flac__valignrq_s32_1(vcombine_s32(vreinterpret_s32_s64(prediction64), vdup_n_s32(0)), samples128_0); - riceParamPart128 = ma_dr_flac__valignrq_u32_1(vdupq_n_u32(0), riceParamPart128); - } - vst1q_s32(pDecodedSamples, samples128_0); - pDecodedSamples += 4; - } - i = (count & ~3); - while (i < (int)count) { - if (!ma_dr_flac__read_rice_parts_x1(bs, riceParam, &zeroCountParts[0], &riceParamParts[0])) { - return MA_FALSE; - } - riceParamParts[0] &= riceParamMask; - riceParamParts[0] |= (zeroCountParts[0] << riceParam); - riceParamParts[0] = (riceParamParts[0] >> 1) ^ t[riceParamParts[0] & 0x01]; - pDecodedSamples[0] = riceParamParts[0] + ma_dr_flac__calculate_prediction_64(order, shift, coefficients, pDecodedSamples); - i += 1; - pDecodedSamples += 1; - } - return MA_TRUE; -} -static ma_bool32 ma_dr_flac__decode_samples_with_residual__rice__neon(ma_dr_flac_bs* bs, ma_uint32 bitsPerSample, ma_uint32 count, ma_uint8 riceParam, ma_uint32 lpcOrder, ma_int32 lpcShift, ma_uint32 lpcPrecision, const ma_int32* coefficients, ma_int32* pSamplesOut) -{ - MA_DR_FLAC_ASSERT(bs != NULL); - MA_DR_FLAC_ASSERT(pSamplesOut != NULL); - if (lpcOrder > 0 && lpcOrder <= 12) { - if (ma_dr_flac__use_64_bit_prediction(bitsPerSample, lpcOrder, lpcPrecision)) { - return ma_dr_flac__decode_samples_with_residual__rice__neon_64(bs, count, riceParam, lpcOrder, lpcShift, coefficients, pSamplesOut); - } else { - return ma_dr_flac__decode_samples_with_residual__rice__neon_32(bs, count, riceParam, lpcOrder, lpcShift, coefficients, pSamplesOut); - } - } else { - return ma_dr_flac__decode_samples_with_residual__rice__scalar(bs, bitsPerSample, count, riceParam, lpcOrder, lpcShift, lpcPrecision, coefficients, pSamplesOut); - } -} -#endif -static ma_bool32 ma_dr_flac__decode_samples_with_residual__rice(ma_dr_flac_bs* bs, ma_uint32 bitsPerSample, ma_uint32 count, ma_uint8 riceParam, ma_uint32 lpcOrder, ma_int32 lpcShift, ma_uint32 lpcPrecision, const ma_int32* coefficients, ma_int32* pSamplesOut) -{ -#if defined(MA_DR_FLAC_SUPPORT_SSE41) - if (ma_dr_flac__gIsSSE41Supported) { - return ma_dr_flac__decode_samples_with_residual__rice__sse41(bs, bitsPerSample, count, riceParam, lpcOrder, lpcShift, lpcPrecision, coefficients, pSamplesOut); - } else -#elif defined(MA_DR_FLAC_SUPPORT_NEON) - if (ma_dr_flac__gIsNEONSupported) { - return ma_dr_flac__decode_samples_with_residual__rice__neon(bs, bitsPerSample, count, riceParam, lpcOrder, lpcShift, lpcPrecision, coefficients, pSamplesOut); - } else -#endif - { - #if 0 - return ma_dr_flac__decode_samples_with_residual__rice__reference(bs, bitsPerSample, count, riceParam, lpcOrder, lpcShift, lpcPrecision, coefficients, pSamplesOut); - #else - return ma_dr_flac__decode_samples_with_residual__rice__scalar(bs, bitsPerSample, count, riceParam, lpcOrder, lpcShift, lpcPrecision, coefficients, pSamplesOut); - #endif - } -} -static ma_bool32 ma_dr_flac__read_and_seek_residual__rice(ma_dr_flac_bs* bs, ma_uint32 count, ma_uint8 riceParam) -{ - ma_uint32 i; - MA_DR_FLAC_ASSERT(bs != NULL); - for (i = 0; i < count; ++i) { - if (!ma_dr_flac__seek_rice_parts(bs, riceParam)) { - return MA_FALSE; - } - } - return MA_TRUE; -} -#if defined(__clang__) -__attribute__((no_sanitize("signed-integer-overflow"))) -#endif -static ma_bool32 ma_dr_flac__decode_samples_with_residual__unencoded(ma_dr_flac_bs* bs, ma_uint32 bitsPerSample, ma_uint32 count, ma_uint8 unencodedBitsPerSample, ma_uint32 lpcOrder, ma_int32 lpcShift, ma_uint32 lpcPrecision, const ma_int32* coefficients, ma_int32* pSamplesOut) -{ - ma_uint32 i; - MA_DR_FLAC_ASSERT(bs != NULL); - MA_DR_FLAC_ASSERT(unencodedBitsPerSample <= 31); - MA_DR_FLAC_ASSERT(pSamplesOut != NULL); - for (i = 0; i < count; ++i) { - if (unencodedBitsPerSample > 0) { - if (!ma_dr_flac__read_int32(bs, unencodedBitsPerSample, pSamplesOut + i)) { - return MA_FALSE; - } - } else { - pSamplesOut[i] = 0; - } - if (ma_dr_flac__use_64_bit_prediction(bitsPerSample, lpcOrder, lpcPrecision)) { - pSamplesOut[i] += ma_dr_flac__calculate_prediction_64(lpcOrder, lpcShift, coefficients, pSamplesOut + i); - } else { - pSamplesOut[i] += ma_dr_flac__calculate_prediction_32(lpcOrder, lpcShift, coefficients, pSamplesOut + i); - } - } - return MA_TRUE; -} -static ma_bool32 ma_dr_flac__decode_samples_with_residual(ma_dr_flac_bs* bs, ma_uint32 bitsPerSample, ma_uint32 blockSize, ma_uint32 lpcOrder, ma_int32 lpcShift, ma_uint32 lpcPrecision, const ma_int32* coefficients, ma_int32* pDecodedSamples) -{ - ma_uint8 residualMethod; - ma_uint8 partitionOrder; - ma_uint32 samplesInPartition; - ma_uint32 partitionsRemaining; - MA_DR_FLAC_ASSERT(bs != NULL); - MA_DR_FLAC_ASSERT(blockSize != 0); - MA_DR_FLAC_ASSERT(pDecodedSamples != NULL); - if (!ma_dr_flac__read_uint8(bs, 2, &residualMethod)) { - return MA_FALSE; - } - if (residualMethod != MA_DR_FLAC_RESIDUAL_CODING_METHOD_PARTITIONED_RICE && residualMethod != MA_DR_FLAC_RESIDUAL_CODING_METHOD_PARTITIONED_RICE2) { - return MA_FALSE; - } - pDecodedSamples += lpcOrder; - if (!ma_dr_flac__read_uint8(bs, 4, &partitionOrder)) { - return MA_FALSE; - } - if (partitionOrder > 8) { - return MA_FALSE; - } - if ((blockSize / (1 << partitionOrder)) < lpcOrder) { - return MA_FALSE; - } - samplesInPartition = (blockSize / (1 << partitionOrder)) - lpcOrder; - partitionsRemaining = (1 << partitionOrder); - for (;;) { - ma_uint8 riceParam = 0; - if (residualMethod == MA_DR_FLAC_RESIDUAL_CODING_METHOD_PARTITIONED_RICE) { - if (!ma_dr_flac__read_uint8(bs, 4, &riceParam)) { - return MA_FALSE; - } - if (riceParam == 15) { - riceParam = 0xFF; - } - } else if (residualMethod == MA_DR_FLAC_RESIDUAL_CODING_METHOD_PARTITIONED_RICE2) { - if (!ma_dr_flac__read_uint8(bs, 5, &riceParam)) { - return MA_FALSE; - } - if (riceParam == 31) { - riceParam = 0xFF; - } - } - if (riceParam != 0xFF) { - if (!ma_dr_flac__decode_samples_with_residual__rice(bs, bitsPerSample, samplesInPartition, riceParam, lpcOrder, lpcShift, lpcPrecision, coefficients, pDecodedSamples)) { - return MA_FALSE; - } - } else { - ma_uint8 unencodedBitsPerSample = 0; - if (!ma_dr_flac__read_uint8(bs, 5, &unencodedBitsPerSample)) { - return MA_FALSE; - } - if (!ma_dr_flac__decode_samples_with_residual__unencoded(bs, bitsPerSample, samplesInPartition, unencodedBitsPerSample, lpcOrder, lpcShift, lpcPrecision, coefficients, pDecodedSamples)) { - return MA_FALSE; - } - } - pDecodedSamples += samplesInPartition; - if (partitionsRemaining == 1) { - break; - } - partitionsRemaining -= 1; - if (partitionOrder != 0) { - samplesInPartition = blockSize / (1 << partitionOrder); - } - } - return MA_TRUE; -} -static ma_bool32 ma_dr_flac__read_and_seek_residual(ma_dr_flac_bs* bs, ma_uint32 blockSize, ma_uint32 order) -{ - ma_uint8 residualMethod; - ma_uint8 partitionOrder; - ma_uint32 samplesInPartition; - ma_uint32 partitionsRemaining; - MA_DR_FLAC_ASSERT(bs != NULL); - MA_DR_FLAC_ASSERT(blockSize != 0); - if (!ma_dr_flac__read_uint8(bs, 2, &residualMethod)) { - return MA_FALSE; - } - if (residualMethod != MA_DR_FLAC_RESIDUAL_CODING_METHOD_PARTITIONED_RICE && residualMethod != MA_DR_FLAC_RESIDUAL_CODING_METHOD_PARTITIONED_RICE2) { - return MA_FALSE; - } - if (!ma_dr_flac__read_uint8(bs, 4, &partitionOrder)) { - return MA_FALSE; - } - if (partitionOrder > 8) { - return MA_FALSE; - } - if ((blockSize / (1 << partitionOrder)) <= order) { - return MA_FALSE; - } - samplesInPartition = (blockSize / (1 << partitionOrder)) - order; - partitionsRemaining = (1 << partitionOrder); - for (;;) - { - ma_uint8 riceParam = 0; - if (residualMethod == MA_DR_FLAC_RESIDUAL_CODING_METHOD_PARTITIONED_RICE) { - if (!ma_dr_flac__read_uint8(bs, 4, &riceParam)) { - return MA_FALSE; - } - if (riceParam == 15) { - riceParam = 0xFF; - } - } else if (residualMethod == MA_DR_FLAC_RESIDUAL_CODING_METHOD_PARTITIONED_RICE2) { - if (!ma_dr_flac__read_uint8(bs, 5, &riceParam)) { - return MA_FALSE; - } - if (riceParam == 31) { - riceParam = 0xFF; - } - } - if (riceParam != 0xFF) { - if (!ma_dr_flac__read_and_seek_residual__rice(bs, samplesInPartition, riceParam)) { - return MA_FALSE; - } - } else { - ma_uint8 unencodedBitsPerSample = 0; - if (!ma_dr_flac__read_uint8(bs, 5, &unencodedBitsPerSample)) { - return MA_FALSE; - } - if (!ma_dr_flac__seek_bits(bs, unencodedBitsPerSample * samplesInPartition)) { - return MA_FALSE; - } - } - if (partitionsRemaining == 1) { - break; - } - partitionsRemaining -= 1; - samplesInPartition = blockSize / (1 << partitionOrder); - } - return MA_TRUE; -} -static ma_bool32 ma_dr_flac__decode_samples__constant(ma_dr_flac_bs* bs, ma_uint32 blockSize, ma_uint32 subframeBitsPerSample, ma_int32* pDecodedSamples) -{ - ma_uint32 i; - ma_int32 sample; - if (!ma_dr_flac__read_int32(bs, subframeBitsPerSample, &sample)) { - return MA_FALSE; - } - for (i = 0; i < blockSize; ++i) { - pDecodedSamples[i] = sample; - } - return MA_TRUE; -} -static ma_bool32 ma_dr_flac__decode_samples__verbatim(ma_dr_flac_bs* bs, ma_uint32 blockSize, ma_uint32 subframeBitsPerSample, ma_int32* pDecodedSamples) -{ - ma_uint32 i; - for (i = 0; i < blockSize; ++i) { - ma_int32 sample; - if (!ma_dr_flac__read_int32(bs, subframeBitsPerSample, &sample)) { - return MA_FALSE; - } - pDecodedSamples[i] = sample; - } - return MA_TRUE; -} -static ma_bool32 ma_dr_flac__decode_samples__fixed(ma_dr_flac_bs* bs, ma_uint32 blockSize, ma_uint32 subframeBitsPerSample, ma_uint8 lpcOrder, ma_int32* pDecodedSamples) -{ - ma_uint32 i; - static ma_int32 lpcCoefficientsTable[5][4] = { - {0, 0, 0, 0}, - {1, 0, 0, 0}, - {2, -1, 0, 0}, - {3, -3, 1, 0}, - {4, -6, 4, -1} - }; - for (i = 0; i < lpcOrder; ++i) { - ma_int32 sample; - if (!ma_dr_flac__read_int32(bs, subframeBitsPerSample, &sample)) { - return MA_FALSE; - } - pDecodedSamples[i] = sample; - } - if (!ma_dr_flac__decode_samples_with_residual(bs, subframeBitsPerSample, blockSize, lpcOrder, 0, 4, lpcCoefficientsTable[lpcOrder], pDecodedSamples)) { - return MA_FALSE; - } - return MA_TRUE; -} -static ma_bool32 ma_dr_flac__decode_samples__lpc(ma_dr_flac_bs* bs, ma_uint32 blockSize, ma_uint32 bitsPerSample, ma_uint8 lpcOrder, ma_int32* pDecodedSamples) -{ - ma_uint8 i; - ma_uint8 lpcPrecision; - ma_int8 lpcShift; - ma_int32 coefficients[32]; - for (i = 0; i < lpcOrder; ++i) { - ma_int32 sample; - if (!ma_dr_flac__read_int32(bs, bitsPerSample, &sample)) { - return MA_FALSE; - } - pDecodedSamples[i] = sample; - } - if (!ma_dr_flac__read_uint8(bs, 4, &lpcPrecision)) { - return MA_FALSE; - } - if (lpcPrecision == 15) { - return MA_FALSE; - } - lpcPrecision += 1; - if (!ma_dr_flac__read_int8(bs, 5, &lpcShift)) { - return MA_FALSE; - } - if (lpcShift < 0) { - return MA_FALSE; - } - MA_DR_FLAC_ZERO_MEMORY(coefficients, sizeof(coefficients)); - for (i = 0; i < lpcOrder; ++i) { - if (!ma_dr_flac__read_int32(bs, lpcPrecision, coefficients + i)) { - return MA_FALSE; - } - } - if (!ma_dr_flac__decode_samples_with_residual(bs, bitsPerSample, blockSize, lpcOrder, lpcShift, lpcPrecision, coefficients, pDecodedSamples)) { - return MA_FALSE; - } - return MA_TRUE; -} -static ma_bool32 ma_dr_flac__read_next_flac_frame_header(ma_dr_flac_bs* bs, ma_uint8 streaminfoBitsPerSample, ma_dr_flac_frame_header* header) -{ - const ma_uint32 sampleRateTable[12] = {0, 88200, 176400, 192000, 8000, 16000, 22050, 24000, 32000, 44100, 48000, 96000}; - const ma_uint8 bitsPerSampleTable[8] = {0, 8, 12, (ma_uint8)-1, 16, 20, 24, (ma_uint8)-1}; - MA_DR_FLAC_ASSERT(bs != NULL); - MA_DR_FLAC_ASSERT(header != NULL); - for (;;) { - ma_uint8 crc8 = 0xCE; - ma_uint8 reserved = 0; - ma_uint8 blockingStrategy = 0; - ma_uint8 blockSize = 0; - ma_uint8 sampleRate = 0; - ma_uint8 channelAssignment = 0; - ma_uint8 bitsPerSample = 0; - ma_bool32 isVariableBlockSize; - if (!ma_dr_flac__find_and_seek_to_next_sync_code(bs)) { - return MA_FALSE; - } - if (!ma_dr_flac__read_uint8(bs, 1, &reserved)) { - return MA_FALSE; - } - if (reserved == 1) { - continue; - } - crc8 = ma_dr_flac_crc8(crc8, reserved, 1); - if (!ma_dr_flac__read_uint8(bs, 1, &blockingStrategy)) { - return MA_FALSE; - } - crc8 = ma_dr_flac_crc8(crc8, blockingStrategy, 1); - if (!ma_dr_flac__read_uint8(bs, 4, &blockSize)) { - return MA_FALSE; - } - if (blockSize == 0) { - continue; - } - crc8 = ma_dr_flac_crc8(crc8, blockSize, 4); - if (!ma_dr_flac__read_uint8(bs, 4, &sampleRate)) { - return MA_FALSE; - } - crc8 = ma_dr_flac_crc8(crc8, sampleRate, 4); - if (!ma_dr_flac__read_uint8(bs, 4, &channelAssignment)) { - return MA_FALSE; - } - if (channelAssignment > 10) { - continue; - } - crc8 = ma_dr_flac_crc8(crc8, channelAssignment, 4); - if (!ma_dr_flac__read_uint8(bs, 3, &bitsPerSample)) { - return MA_FALSE; - } - if (bitsPerSample == 3 || bitsPerSample == 7) { - continue; - } - crc8 = ma_dr_flac_crc8(crc8, bitsPerSample, 3); - if (!ma_dr_flac__read_uint8(bs, 1, &reserved)) { - return MA_FALSE; - } - if (reserved == 1) { - continue; - } - crc8 = ma_dr_flac_crc8(crc8, reserved, 1); - isVariableBlockSize = blockingStrategy == 1; - if (isVariableBlockSize) { - ma_uint64 pcmFrameNumber; - ma_result result = ma_dr_flac__read_utf8_coded_number(bs, &pcmFrameNumber, &crc8); - if (result != MA_SUCCESS) { - if (result == MA_AT_END) { - return MA_FALSE; - } else { - continue; - } - } - header->flacFrameNumber = 0; - header->pcmFrameNumber = pcmFrameNumber; - } else { - ma_uint64 flacFrameNumber = 0; - ma_result result = ma_dr_flac__read_utf8_coded_number(bs, &flacFrameNumber, &crc8); - if (result != MA_SUCCESS) { - if (result == MA_AT_END) { - return MA_FALSE; - } else { - continue; - } - } - header->flacFrameNumber = (ma_uint32)flacFrameNumber; - header->pcmFrameNumber = 0; - } - MA_DR_FLAC_ASSERT(blockSize > 0); - if (blockSize == 1) { - header->blockSizeInPCMFrames = 192; - } else if (blockSize <= 5) { - MA_DR_FLAC_ASSERT(blockSize >= 2); - header->blockSizeInPCMFrames = 576 * (1 << (blockSize - 2)); - } else if (blockSize == 6) { - if (!ma_dr_flac__read_uint16(bs, 8, &header->blockSizeInPCMFrames)) { - return MA_FALSE; - } - crc8 = ma_dr_flac_crc8(crc8, header->blockSizeInPCMFrames, 8); - header->blockSizeInPCMFrames += 1; - } else if (blockSize == 7) { - if (!ma_dr_flac__read_uint16(bs, 16, &header->blockSizeInPCMFrames)) { - return MA_FALSE; - } - crc8 = ma_dr_flac_crc8(crc8, header->blockSizeInPCMFrames, 16); - if (header->blockSizeInPCMFrames == 0xFFFF) { - return MA_FALSE; - } - header->blockSizeInPCMFrames += 1; - } else { - MA_DR_FLAC_ASSERT(blockSize >= 8); - header->blockSizeInPCMFrames = 256 * (1 << (blockSize - 8)); - } - if (sampleRate <= 11) { - header->sampleRate = sampleRateTable[sampleRate]; - } else if (sampleRate == 12) { - if (!ma_dr_flac__read_uint32(bs, 8, &header->sampleRate)) { - return MA_FALSE; - } - crc8 = ma_dr_flac_crc8(crc8, header->sampleRate, 8); - header->sampleRate *= 1000; - } else if (sampleRate == 13) { - if (!ma_dr_flac__read_uint32(bs, 16, &header->sampleRate)) { - return MA_FALSE; - } - crc8 = ma_dr_flac_crc8(crc8, header->sampleRate, 16); - } else if (sampleRate == 14) { - if (!ma_dr_flac__read_uint32(bs, 16, &header->sampleRate)) { - return MA_FALSE; - } - crc8 = ma_dr_flac_crc8(crc8, header->sampleRate, 16); - header->sampleRate *= 10; - } else { - continue; - } - header->channelAssignment = channelAssignment; - header->bitsPerSample = bitsPerSampleTable[bitsPerSample]; - if (header->bitsPerSample == 0) { - header->bitsPerSample = streaminfoBitsPerSample; - } - if (header->bitsPerSample != streaminfoBitsPerSample) { - return MA_FALSE; - } - if (!ma_dr_flac__read_uint8(bs, 8, &header->crc8)) { - return MA_FALSE; - } -#ifndef MA_DR_FLAC_NO_CRC - if (header->crc8 != crc8) { - continue; - } -#endif - return MA_TRUE; - } -} -static ma_bool32 ma_dr_flac__read_subframe_header(ma_dr_flac_bs* bs, ma_dr_flac_subframe* pSubframe) -{ - ma_uint8 header; - int type; - if (!ma_dr_flac__read_uint8(bs, 8, &header)) { - return MA_FALSE; - } - if ((header & 0x80) != 0) { - return MA_FALSE; - } - pSubframe->lpcOrder = 0; - type = (header & 0x7E) >> 1; - if (type == 0) { - pSubframe->subframeType = MA_DR_FLAC_SUBFRAME_CONSTANT; - } else if (type == 1) { - pSubframe->subframeType = MA_DR_FLAC_SUBFRAME_VERBATIM; - } else { - if ((type & 0x20) != 0) { - pSubframe->subframeType = MA_DR_FLAC_SUBFRAME_LPC; - pSubframe->lpcOrder = (ma_uint8)(type & 0x1F) + 1; - } else if ((type & 0x08) != 0) { - pSubframe->subframeType = MA_DR_FLAC_SUBFRAME_FIXED; - pSubframe->lpcOrder = (ma_uint8)(type & 0x07); - if (pSubframe->lpcOrder > 4) { - pSubframe->subframeType = MA_DR_FLAC_SUBFRAME_RESERVED; - pSubframe->lpcOrder = 0; - } - } else { - pSubframe->subframeType = MA_DR_FLAC_SUBFRAME_RESERVED; - } - } - if (pSubframe->subframeType == MA_DR_FLAC_SUBFRAME_RESERVED) { - return MA_FALSE; - } - pSubframe->wastedBitsPerSample = 0; - if ((header & 0x01) == 1) { - unsigned int wastedBitsPerSample; - if (!ma_dr_flac__seek_past_next_set_bit(bs, &wastedBitsPerSample)) { - return MA_FALSE; - } - pSubframe->wastedBitsPerSample = (ma_uint8)wastedBitsPerSample + 1; - } - return MA_TRUE; -} -static ma_bool32 ma_dr_flac__decode_subframe(ma_dr_flac_bs* bs, ma_dr_flac_frame* frame, int subframeIndex, ma_int32* pDecodedSamplesOut) -{ - ma_dr_flac_subframe* pSubframe; - ma_uint32 subframeBitsPerSample; - MA_DR_FLAC_ASSERT(bs != NULL); - MA_DR_FLAC_ASSERT(frame != NULL); - pSubframe = frame->subframes + subframeIndex; - if (!ma_dr_flac__read_subframe_header(bs, pSubframe)) { - return MA_FALSE; - } - subframeBitsPerSample = frame->header.bitsPerSample; - if ((frame->header.channelAssignment == MA_DR_FLAC_CHANNEL_ASSIGNMENT_LEFT_SIDE || frame->header.channelAssignment == MA_DR_FLAC_CHANNEL_ASSIGNMENT_MID_SIDE) && subframeIndex == 1) { - subframeBitsPerSample += 1; - } else if (frame->header.channelAssignment == MA_DR_FLAC_CHANNEL_ASSIGNMENT_RIGHT_SIDE && subframeIndex == 0) { - subframeBitsPerSample += 1; - } - if (subframeBitsPerSample > 32) { - return MA_FALSE; - } - if (pSubframe->wastedBitsPerSample >= subframeBitsPerSample) { - return MA_FALSE; - } - subframeBitsPerSample -= pSubframe->wastedBitsPerSample; - pSubframe->pSamplesS32 = pDecodedSamplesOut; - if (frame->header.blockSizeInPCMFrames < pSubframe->lpcOrder) { - return MA_FALSE; - } - switch (pSubframe->subframeType) - { - case MA_DR_FLAC_SUBFRAME_CONSTANT: - { - ma_dr_flac__decode_samples__constant(bs, frame->header.blockSizeInPCMFrames, subframeBitsPerSample, pSubframe->pSamplesS32); - } break; - case MA_DR_FLAC_SUBFRAME_VERBATIM: - { - ma_dr_flac__decode_samples__verbatim(bs, frame->header.blockSizeInPCMFrames, subframeBitsPerSample, pSubframe->pSamplesS32); - } break; - case MA_DR_FLAC_SUBFRAME_FIXED: - { - ma_dr_flac__decode_samples__fixed(bs, frame->header.blockSizeInPCMFrames, subframeBitsPerSample, pSubframe->lpcOrder, pSubframe->pSamplesS32); - } break; - case MA_DR_FLAC_SUBFRAME_LPC: - { - ma_dr_flac__decode_samples__lpc(bs, frame->header.blockSizeInPCMFrames, subframeBitsPerSample, pSubframe->lpcOrder, pSubframe->pSamplesS32); - } break; - default: return MA_FALSE; - } - return MA_TRUE; -} -static ma_bool32 ma_dr_flac__seek_subframe(ma_dr_flac_bs* bs, ma_dr_flac_frame* frame, int subframeIndex) -{ - ma_dr_flac_subframe* pSubframe; - ma_uint32 subframeBitsPerSample; - MA_DR_FLAC_ASSERT(bs != NULL); - MA_DR_FLAC_ASSERT(frame != NULL); - pSubframe = frame->subframes + subframeIndex; - if (!ma_dr_flac__read_subframe_header(bs, pSubframe)) { - return MA_FALSE; - } - subframeBitsPerSample = frame->header.bitsPerSample; - if ((frame->header.channelAssignment == MA_DR_FLAC_CHANNEL_ASSIGNMENT_LEFT_SIDE || frame->header.channelAssignment == MA_DR_FLAC_CHANNEL_ASSIGNMENT_MID_SIDE) && subframeIndex == 1) { - subframeBitsPerSample += 1; - } else if (frame->header.channelAssignment == MA_DR_FLAC_CHANNEL_ASSIGNMENT_RIGHT_SIDE && subframeIndex == 0) { - subframeBitsPerSample += 1; - } - if (pSubframe->wastedBitsPerSample >= subframeBitsPerSample) { - return MA_FALSE; - } - subframeBitsPerSample -= pSubframe->wastedBitsPerSample; - pSubframe->pSamplesS32 = NULL; - switch (pSubframe->subframeType) - { - case MA_DR_FLAC_SUBFRAME_CONSTANT: - { - if (!ma_dr_flac__seek_bits(bs, subframeBitsPerSample)) { - return MA_FALSE; - } - } break; - case MA_DR_FLAC_SUBFRAME_VERBATIM: - { - unsigned int bitsToSeek = frame->header.blockSizeInPCMFrames * subframeBitsPerSample; - if (!ma_dr_flac__seek_bits(bs, bitsToSeek)) { - return MA_FALSE; - } - } break; - case MA_DR_FLAC_SUBFRAME_FIXED: - { - unsigned int bitsToSeek = pSubframe->lpcOrder * subframeBitsPerSample; - if (!ma_dr_flac__seek_bits(bs, bitsToSeek)) { - return MA_FALSE; - } - if (!ma_dr_flac__read_and_seek_residual(bs, frame->header.blockSizeInPCMFrames, pSubframe->lpcOrder)) { - return MA_FALSE; - } - } break; - case MA_DR_FLAC_SUBFRAME_LPC: - { - ma_uint8 lpcPrecision; - unsigned int bitsToSeek = pSubframe->lpcOrder * subframeBitsPerSample; - if (!ma_dr_flac__seek_bits(bs, bitsToSeek)) { - return MA_FALSE; - } - if (!ma_dr_flac__read_uint8(bs, 4, &lpcPrecision)) { - return MA_FALSE; - } - if (lpcPrecision == 15) { - return MA_FALSE; - } - lpcPrecision += 1; - bitsToSeek = (pSubframe->lpcOrder * lpcPrecision) + 5; - if (!ma_dr_flac__seek_bits(bs, bitsToSeek)) { - return MA_FALSE; - } - if (!ma_dr_flac__read_and_seek_residual(bs, frame->header.blockSizeInPCMFrames, pSubframe->lpcOrder)) { - return MA_FALSE; - } - } break; - default: return MA_FALSE; - } - return MA_TRUE; -} -static MA_INLINE ma_uint8 ma_dr_flac__get_channel_count_from_channel_assignment(ma_int8 channelAssignment) -{ - ma_uint8 lookup[] = {1, 2, 3, 4, 5, 6, 7, 8, 2, 2, 2}; - MA_DR_FLAC_ASSERT(channelAssignment <= 10); - return lookup[channelAssignment]; -} -static ma_result ma_dr_flac__decode_flac_frame(ma_dr_flac* pFlac) -{ - int channelCount; - int i; - ma_uint8 paddingSizeInBits; - ma_uint16 desiredCRC16; -#ifndef MA_DR_FLAC_NO_CRC - ma_uint16 actualCRC16; -#endif - MA_DR_FLAC_ZERO_MEMORY(pFlac->currentFLACFrame.subframes, sizeof(pFlac->currentFLACFrame.subframes)); - if (pFlac->currentFLACFrame.header.blockSizeInPCMFrames > pFlac->maxBlockSizeInPCMFrames) { - return MA_ERROR; - } - channelCount = ma_dr_flac__get_channel_count_from_channel_assignment(pFlac->currentFLACFrame.header.channelAssignment); - if (channelCount != (int)pFlac->channels) { - return MA_ERROR; - } - for (i = 0; i < channelCount; ++i) { - if (!ma_dr_flac__decode_subframe(&pFlac->bs, &pFlac->currentFLACFrame, i, pFlac->pDecodedSamples + (pFlac->currentFLACFrame.header.blockSizeInPCMFrames * i))) { - return MA_ERROR; - } - } - paddingSizeInBits = (ma_uint8)(MA_DR_FLAC_CACHE_L1_BITS_REMAINING(&pFlac->bs) & 7); - if (paddingSizeInBits > 0) { - ma_uint8 padding = 0; - if (!ma_dr_flac__read_uint8(&pFlac->bs, paddingSizeInBits, &padding)) { - return MA_AT_END; - } - } -#ifndef MA_DR_FLAC_NO_CRC - actualCRC16 = ma_dr_flac__flush_crc16(&pFlac->bs); -#endif - if (!ma_dr_flac__read_uint16(&pFlac->bs, 16, &desiredCRC16)) { - return MA_AT_END; - } -#ifndef MA_DR_FLAC_NO_CRC - if (actualCRC16 != desiredCRC16) { - return MA_CRC_MISMATCH; - } -#endif - pFlac->currentFLACFrame.pcmFramesRemaining = pFlac->currentFLACFrame.header.blockSizeInPCMFrames; - return MA_SUCCESS; -} -static ma_result ma_dr_flac__seek_flac_frame(ma_dr_flac* pFlac) -{ - int channelCount; - int i; - ma_uint16 desiredCRC16; -#ifndef MA_DR_FLAC_NO_CRC - ma_uint16 actualCRC16; -#endif - channelCount = ma_dr_flac__get_channel_count_from_channel_assignment(pFlac->currentFLACFrame.header.channelAssignment); - for (i = 0; i < channelCount; ++i) { - if (!ma_dr_flac__seek_subframe(&pFlac->bs, &pFlac->currentFLACFrame, i)) { - return MA_ERROR; - } - } - if (!ma_dr_flac__seek_bits(&pFlac->bs, MA_DR_FLAC_CACHE_L1_BITS_REMAINING(&pFlac->bs) & 7)) { - return MA_ERROR; - } -#ifndef MA_DR_FLAC_NO_CRC - actualCRC16 = ma_dr_flac__flush_crc16(&pFlac->bs); -#endif - if (!ma_dr_flac__read_uint16(&pFlac->bs, 16, &desiredCRC16)) { - return MA_AT_END; - } -#ifndef MA_DR_FLAC_NO_CRC - if (actualCRC16 != desiredCRC16) { - return MA_CRC_MISMATCH; - } -#endif - return MA_SUCCESS; -} -static ma_bool32 ma_dr_flac__read_and_decode_next_flac_frame(ma_dr_flac* pFlac) -{ - MA_DR_FLAC_ASSERT(pFlac != NULL); - for (;;) { - ma_result result; - if (!ma_dr_flac__read_next_flac_frame_header(&pFlac->bs, pFlac->bitsPerSample, &pFlac->currentFLACFrame.header)) { - return MA_FALSE; - } - result = ma_dr_flac__decode_flac_frame(pFlac); - if (result != MA_SUCCESS) { - if (result == MA_CRC_MISMATCH) { - continue; - } else { - return MA_FALSE; - } - } - return MA_TRUE; - } -} -static void ma_dr_flac__get_pcm_frame_range_of_current_flac_frame(ma_dr_flac* pFlac, ma_uint64* pFirstPCMFrame, ma_uint64* pLastPCMFrame) -{ - ma_uint64 firstPCMFrame; - ma_uint64 lastPCMFrame; - MA_DR_FLAC_ASSERT(pFlac != NULL); - firstPCMFrame = pFlac->currentFLACFrame.header.pcmFrameNumber; - if (firstPCMFrame == 0) { - firstPCMFrame = ((ma_uint64)pFlac->currentFLACFrame.header.flacFrameNumber) * pFlac->maxBlockSizeInPCMFrames; - } - lastPCMFrame = firstPCMFrame + pFlac->currentFLACFrame.header.blockSizeInPCMFrames; - if (lastPCMFrame > 0) { - lastPCMFrame -= 1; - } - if (pFirstPCMFrame) { - *pFirstPCMFrame = firstPCMFrame; - } - if (pLastPCMFrame) { - *pLastPCMFrame = lastPCMFrame; - } -} -static ma_bool32 ma_dr_flac__seek_to_first_frame(ma_dr_flac* pFlac) -{ - ma_bool32 result; - MA_DR_FLAC_ASSERT(pFlac != NULL); - result = ma_dr_flac__seek_to_byte(&pFlac->bs, pFlac->firstFLACFramePosInBytes); - MA_DR_FLAC_ZERO_MEMORY(&pFlac->currentFLACFrame, sizeof(pFlac->currentFLACFrame)); - pFlac->currentPCMFrame = 0; - return result; -} -static MA_INLINE ma_result ma_dr_flac__seek_to_next_flac_frame(ma_dr_flac* pFlac) -{ - MA_DR_FLAC_ASSERT(pFlac != NULL); - return ma_dr_flac__seek_flac_frame(pFlac); -} -static ma_uint64 ma_dr_flac__seek_forward_by_pcm_frames(ma_dr_flac* pFlac, ma_uint64 pcmFramesToSeek) -{ - ma_uint64 pcmFramesRead = 0; - while (pcmFramesToSeek > 0) { - if (pFlac->currentFLACFrame.pcmFramesRemaining == 0) { - if (!ma_dr_flac__read_and_decode_next_flac_frame(pFlac)) { - break; - } - } else { - if (pFlac->currentFLACFrame.pcmFramesRemaining > pcmFramesToSeek) { - pcmFramesRead += pcmFramesToSeek; - pFlac->currentFLACFrame.pcmFramesRemaining -= (ma_uint32)pcmFramesToSeek; - pcmFramesToSeek = 0; - } else { - pcmFramesRead += pFlac->currentFLACFrame.pcmFramesRemaining; - pcmFramesToSeek -= pFlac->currentFLACFrame.pcmFramesRemaining; - pFlac->currentFLACFrame.pcmFramesRemaining = 0; - } - } - } - pFlac->currentPCMFrame += pcmFramesRead; - return pcmFramesRead; -} -static ma_bool32 ma_dr_flac__seek_to_pcm_frame__brute_force(ma_dr_flac* pFlac, ma_uint64 pcmFrameIndex) -{ - ma_bool32 isMidFrame = MA_FALSE; - ma_uint64 runningPCMFrameCount; - MA_DR_FLAC_ASSERT(pFlac != NULL); - if (pcmFrameIndex >= pFlac->currentPCMFrame) { - runningPCMFrameCount = pFlac->currentPCMFrame; - if (pFlac->currentPCMFrame == 0 && pFlac->currentFLACFrame.pcmFramesRemaining == 0) { - if (!ma_dr_flac__read_next_flac_frame_header(&pFlac->bs, pFlac->bitsPerSample, &pFlac->currentFLACFrame.header)) { - return MA_FALSE; - } - } else { - isMidFrame = MA_TRUE; - } - } else { - runningPCMFrameCount = 0; - if (!ma_dr_flac__seek_to_first_frame(pFlac)) { - return MA_FALSE; - } - if (!ma_dr_flac__read_next_flac_frame_header(&pFlac->bs, pFlac->bitsPerSample, &pFlac->currentFLACFrame.header)) { - return MA_FALSE; - } - } - for (;;) { - ma_uint64 pcmFrameCountInThisFLACFrame; - ma_uint64 firstPCMFrameInFLACFrame = 0; - ma_uint64 lastPCMFrameInFLACFrame = 0; - ma_dr_flac__get_pcm_frame_range_of_current_flac_frame(pFlac, &firstPCMFrameInFLACFrame, &lastPCMFrameInFLACFrame); - pcmFrameCountInThisFLACFrame = (lastPCMFrameInFLACFrame - firstPCMFrameInFLACFrame) + 1; - if (pcmFrameIndex < (runningPCMFrameCount + pcmFrameCountInThisFLACFrame)) { - ma_uint64 pcmFramesToDecode = pcmFrameIndex - runningPCMFrameCount; - if (!isMidFrame) { - ma_result result = ma_dr_flac__decode_flac_frame(pFlac); - if (result == MA_SUCCESS) { - return ma_dr_flac__seek_forward_by_pcm_frames(pFlac, pcmFramesToDecode) == pcmFramesToDecode; - } else { - if (result == MA_CRC_MISMATCH) { - goto next_iteration; - } else { - return MA_FALSE; - } - } - } else { - return ma_dr_flac__seek_forward_by_pcm_frames(pFlac, pcmFramesToDecode) == pcmFramesToDecode; - } - } else { - if (!isMidFrame) { - ma_result result = ma_dr_flac__seek_to_next_flac_frame(pFlac); - if (result == MA_SUCCESS) { - runningPCMFrameCount += pcmFrameCountInThisFLACFrame; - } else { - if (result == MA_CRC_MISMATCH) { - goto next_iteration; - } else { - return MA_FALSE; - } - } - } else { - runningPCMFrameCount += pFlac->currentFLACFrame.pcmFramesRemaining; - pFlac->currentFLACFrame.pcmFramesRemaining = 0; - isMidFrame = MA_FALSE; - } - if (pcmFrameIndex == pFlac->totalPCMFrameCount && runningPCMFrameCount == pFlac->totalPCMFrameCount) { - return MA_TRUE; - } - } - next_iteration: - if (!ma_dr_flac__read_next_flac_frame_header(&pFlac->bs, pFlac->bitsPerSample, &pFlac->currentFLACFrame.header)) { - return MA_FALSE; - } - } -} -#if !defined(MA_DR_FLAC_NO_CRC) -#define MA_DR_FLAC_BINARY_SEARCH_APPROX_COMPRESSION_RATIO 0.6f -static ma_bool32 ma_dr_flac__seek_to_approximate_flac_frame_to_byte(ma_dr_flac* pFlac, ma_uint64 targetByte, ma_uint64 rangeLo, ma_uint64 rangeHi, ma_uint64* pLastSuccessfulSeekOffset) -{ - MA_DR_FLAC_ASSERT(pFlac != NULL); - MA_DR_FLAC_ASSERT(pLastSuccessfulSeekOffset != NULL); - MA_DR_FLAC_ASSERT(targetByte >= rangeLo); - MA_DR_FLAC_ASSERT(targetByte <= rangeHi); - *pLastSuccessfulSeekOffset = pFlac->firstFLACFramePosInBytes; - for (;;) { - ma_uint64 lastTargetByte = targetByte; - if (!ma_dr_flac__seek_to_byte(&pFlac->bs, targetByte)) { - if (targetByte == 0) { - ma_dr_flac__seek_to_first_frame(pFlac); - return MA_FALSE; - } - targetByte = rangeLo + ((rangeHi - rangeLo)/2); - rangeHi = targetByte; - } else { - MA_DR_FLAC_ZERO_MEMORY(&pFlac->currentFLACFrame, sizeof(pFlac->currentFLACFrame)); -#if 1 - if (!ma_dr_flac__read_and_decode_next_flac_frame(pFlac)) { - targetByte = rangeLo + ((rangeHi - rangeLo)/2); - rangeHi = targetByte; - } else { - break; - } -#else - if (!ma_dr_flac__read_next_flac_frame_header(&pFlac->bs, pFlac->bitsPerSample, &pFlac->currentFLACFrame.header)) { - targetByte = rangeLo + ((rangeHi - rangeLo)/2); - rangeHi = targetByte; - } else { - break; - } -#endif - } - if(targetByte == lastTargetByte) { - return MA_FALSE; - } - } - ma_dr_flac__get_pcm_frame_range_of_current_flac_frame(pFlac, &pFlac->currentPCMFrame, NULL); - MA_DR_FLAC_ASSERT(targetByte <= rangeHi); - *pLastSuccessfulSeekOffset = targetByte; - return MA_TRUE; -} -static ma_bool32 ma_dr_flac__decode_flac_frame_and_seek_forward_by_pcm_frames(ma_dr_flac* pFlac, ma_uint64 offset) -{ -#if 0 - if (ma_dr_flac__decode_flac_frame(pFlac) != MA_SUCCESS) { - if (ma_dr_flac__read_and_decode_next_flac_frame(pFlac) == MA_FALSE) { - return MA_FALSE; - } - } -#endif - return ma_dr_flac__seek_forward_by_pcm_frames(pFlac, offset) == offset; -} -static ma_bool32 ma_dr_flac__seek_to_pcm_frame__binary_search_internal(ma_dr_flac* pFlac, ma_uint64 pcmFrameIndex, ma_uint64 byteRangeLo, ma_uint64 byteRangeHi) -{ - ma_uint64 targetByte; - ma_uint64 pcmRangeLo = pFlac->totalPCMFrameCount; - ma_uint64 pcmRangeHi = 0; - ma_uint64 lastSuccessfulSeekOffset = (ma_uint64)-1; - ma_uint64 closestSeekOffsetBeforeTargetPCMFrame = byteRangeLo; - ma_uint32 seekForwardThreshold = (pFlac->maxBlockSizeInPCMFrames != 0) ? pFlac->maxBlockSizeInPCMFrames*2 : 4096; - targetByte = byteRangeLo + (ma_uint64)(((ma_int64)((pcmFrameIndex - pFlac->currentPCMFrame) * pFlac->channels * pFlac->bitsPerSample)/8.0f) * MA_DR_FLAC_BINARY_SEARCH_APPROX_COMPRESSION_RATIO); - if (targetByte > byteRangeHi) { - targetByte = byteRangeHi; - } - for (;;) { - if (ma_dr_flac__seek_to_approximate_flac_frame_to_byte(pFlac, targetByte, byteRangeLo, byteRangeHi, &lastSuccessfulSeekOffset)) { - ma_uint64 newPCMRangeLo; - ma_uint64 newPCMRangeHi; - ma_dr_flac__get_pcm_frame_range_of_current_flac_frame(pFlac, &newPCMRangeLo, &newPCMRangeHi); - if (pcmRangeLo == newPCMRangeLo) { - if (!ma_dr_flac__seek_to_approximate_flac_frame_to_byte(pFlac, closestSeekOffsetBeforeTargetPCMFrame, closestSeekOffsetBeforeTargetPCMFrame, byteRangeHi, &lastSuccessfulSeekOffset)) { - break; - } - if (ma_dr_flac__decode_flac_frame_and_seek_forward_by_pcm_frames(pFlac, pcmFrameIndex - pFlac->currentPCMFrame)) { - return MA_TRUE; - } else { - break; - } - } - pcmRangeLo = newPCMRangeLo; - pcmRangeHi = newPCMRangeHi; - if (pcmRangeLo <= pcmFrameIndex && pcmRangeHi >= pcmFrameIndex) { - if (ma_dr_flac__decode_flac_frame_and_seek_forward_by_pcm_frames(pFlac, pcmFrameIndex - pFlac->currentPCMFrame) ) { - return MA_TRUE; - } else { - break; - } - } else { - const float approxCompressionRatio = (ma_int64)(lastSuccessfulSeekOffset - pFlac->firstFLACFramePosInBytes) / ((ma_int64)(pcmRangeLo * pFlac->channels * pFlac->bitsPerSample)/8.0f); - if (pcmRangeLo > pcmFrameIndex) { - byteRangeHi = lastSuccessfulSeekOffset; - if (byteRangeLo > byteRangeHi) { - byteRangeLo = byteRangeHi; - } - targetByte = byteRangeLo + ((byteRangeHi - byteRangeLo) / 2); - if (targetByte < byteRangeLo) { - targetByte = byteRangeLo; - } - } else { - if ((pcmFrameIndex - pcmRangeLo) < seekForwardThreshold) { - if (ma_dr_flac__decode_flac_frame_and_seek_forward_by_pcm_frames(pFlac, pcmFrameIndex - pFlac->currentPCMFrame)) { - return MA_TRUE; - } else { - break; - } - } else { - byteRangeLo = lastSuccessfulSeekOffset; - if (byteRangeHi < byteRangeLo) { - byteRangeHi = byteRangeLo; - } - targetByte = lastSuccessfulSeekOffset + (ma_uint64)(((ma_int64)((pcmFrameIndex-pcmRangeLo) * pFlac->channels * pFlac->bitsPerSample)/8.0f) * approxCompressionRatio); - if (targetByte > byteRangeHi) { - targetByte = byteRangeHi; - } - if (closestSeekOffsetBeforeTargetPCMFrame < lastSuccessfulSeekOffset) { - closestSeekOffsetBeforeTargetPCMFrame = lastSuccessfulSeekOffset; - } - } - } - } - } else { - break; - } - } - ma_dr_flac__seek_to_first_frame(pFlac); - return MA_FALSE; -} -static ma_bool32 ma_dr_flac__seek_to_pcm_frame__binary_search(ma_dr_flac* pFlac, ma_uint64 pcmFrameIndex) -{ - ma_uint64 byteRangeLo; - ma_uint64 byteRangeHi; - ma_uint32 seekForwardThreshold = (pFlac->maxBlockSizeInPCMFrames != 0) ? pFlac->maxBlockSizeInPCMFrames*2 : 4096; - if (ma_dr_flac__seek_to_first_frame(pFlac) == MA_FALSE) { - return MA_FALSE; - } - if (pcmFrameIndex < seekForwardThreshold) { - return ma_dr_flac__seek_forward_by_pcm_frames(pFlac, pcmFrameIndex) == pcmFrameIndex; - } - byteRangeLo = pFlac->firstFLACFramePosInBytes; - byteRangeHi = pFlac->firstFLACFramePosInBytes + (ma_uint64)((ma_int64)(pFlac->totalPCMFrameCount * pFlac->channels * pFlac->bitsPerSample)/8.0f); - return ma_dr_flac__seek_to_pcm_frame__binary_search_internal(pFlac, pcmFrameIndex, byteRangeLo, byteRangeHi); -} -#endif -static ma_bool32 ma_dr_flac__seek_to_pcm_frame__seek_table(ma_dr_flac* pFlac, ma_uint64 pcmFrameIndex) -{ - ma_uint32 iClosestSeekpoint = 0; - ma_bool32 isMidFrame = MA_FALSE; - ma_uint64 runningPCMFrameCount; - ma_uint32 iSeekpoint; - MA_DR_FLAC_ASSERT(pFlac != NULL); - if (pFlac->pSeekpoints == NULL || pFlac->seekpointCount == 0) { - return MA_FALSE; - } - if (pFlac->pSeekpoints[0].firstPCMFrame > pcmFrameIndex) { - return MA_FALSE; - } - for (iSeekpoint = 0; iSeekpoint < pFlac->seekpointCount; ++iSeekpoint) { - if (pFlac->pSeekpoints[iSeekpoint].firstPCMFrame >= pcmFrameIndex) { - break; - } - iClosestSeekpoint = iSeekpoint; - } - if (pFlac->pSeekpoints[iClosestSeekpoint].pcmFrameCount == 0 || pFlac->pSeekpoints[iClosestSeekpoint].pcmFrameCount > pFlac->maxBlockSizeInPCMFrames) { - return MA_FALSE; - } - if (pFlac->pSeekpoints[iClosestSeekpoint].firstPCMFrame > pFlac->totalPCMFrameCount && pFlac->totalPCMFrameCount > 0) { - return MA_FALSE; - } -#if !defined(MA_DR_FLAC_NO_CRC) - if (pFlac->totalPCMFrameCount > 0) { - ma_uint64 byteRangeLo; - ma_uint64 byteRangeHi; - byteRangeHi = pFlac->firstFLACFramePosInBytes + (ma_uint64)((ma_int64)(pFlac->totalPCMFrameCount * pFlac->channels * pFlac->bitsPerSample)/8.0f); - byteRangeLo = pFlac->firstFLACFramePosInBytes + pFlac->pSeekpoints[iClosestSeekpoint].flacFrameOffset; - if (iClosestSeekpoint < pFlac->seekpointCount-1) { - ma_uint32 iNextSeekpoint = iClosestSeekpoint + 1; - if (pFlac->pSeekpoints[iClosestSeekpoint].flacFrameOffset >= pFlac->pSeekpoints[iNextSeekpoint].flacFrameOffset || pFlac->pSeekpoints[iNextSeekpoint].pcmFrameCount == 0) { - return MA_FALSE; - } - if (pFlac->pSeekpoints[iNextSeekpoint].firstPCMFrame != (((ma_uint64)0xFFFFFFFF << 32) | 0xFFFFFFFF)) { - byteRangeHi = pFlac->firstFLACFramePosInBytes + pFlac->pSeekpoints[iNextSeekpoint].flacFrameOffset - 1; - } - } - if (ma_dr_flac__seek_to_byte(&pFlac->bs, pFlac->firstFLACFramePosInBytes + pFlac->pSeekpoints[iClosestSeekpoint].flacFrameOffset)) { - if (ma_dr_flac__read_next_flac_frame_header(&pFlac->bs, pFlac->bitsPerSample, &pFlac->currentFLACFrame.header)) { - ma_dr_flac__get_pcm_frame_range_of_current_flac_frame(pFlac, &pFlac->currentPCMFrame, NULL); - if (ma_dr_flac__seek_to_pcm_frame__binary_search_internal(pFlac, pcmFrameIndex, byteRangeLo, byteRangeHi)) { - return MA_TRUE; - } - } - } - } -#endif - if (pcmFrameIndex >= pFlac->currentPCMFrame && pFlac->pSeekpoints[iClosestSeekpoint].firstPCMFrame <= pFlac->currentPCMFrame) { - runningPCMFrameCount = pFlac->currentPCMFrame; - if (pFlac->currentPCMFrame == 0 && pFlac->currentFLACFrame.pcmFramesRemaining == 0) { - if (!ma_dr_flac__read_next_flac_frame_header(&pFlac->bs, pFlac->bitsPerSample, &pFlac->currentFLACFrame.header)) { - return MA_FALSE; - } - } else { - isMidFrame = MA_TRUE; - } - } else { - runningPCMFrameCount = pFlac->pSeekpoints[iClosestSeekpoint].firstPCMFrame; - if (!ma_dr_flac__seek_to_byte(&pFlac->bs, pFlac->firstFLACFramePosInBytes + pFlac->pSeekpoints[iClosestSeekpoint].flacFrameOffset)) { - return MA_FALSE; - } - if (!ma_dr_flac__read_next_flac_frame_header(&pFlac->bs, pFlac->bitsPerSample, &pFlac->currentFLACFrame.header)) { - return MA_FALSE; - } - } - for (;;) { - ma_uint64 pcmFrameCountInThisFLACFrame; - ma_uint64 firstPCMFrameInFLACFrame = 0; - ma_uint64 lastPCMFrameInFLACFrame = 0; - ma_dr_flac__get_pcm_frame_range_of_current_flac_frame(pFlac, &firstPCMFrameInFLACFrame, &lastPCMFrameInFLACFrame); - pcmFrameCountInThisFLACFrame = (lastPCMFrameInFLACFrame - firstPCMFrameInFLACFrame) + 1; - if (pcmFrameIndex < (runningPCMFrameCount + pcmFrameCountInThisFLACFrame)) { - ma_uint64 pcmFramesToDecode = pcmFrameIndex - runningPCMFrameCount; - if (!isMidFrame) { - ma_result result = ma_dr_flac__decode_flac_frame(pFlac); - if (result == MA_SUCCESS) { - return ma_dr_flac__seek_forward_by_pcm_frames(pFlac, pcmFramesToDecode) == pcmFramesToDecode; - } else { - if (result == MA_CRC_MISMATCH) { - goto next_iteration; - } else { - return MA_FALSE; - } - } - } else { - return ma_dr_flac__seek_forward_by_pcm_frames(pFlac, pcmFramesToDecode) == pcmFramesToDecode; - } - } else { - if (!isMidFrame) { - ma_result result = ma_dr_flac__seek_to_next_flac_frame(pFlac); - if (result == MA_SUCCESS) { - runningPCMFrameCount += pcmFrameCountInThisFLACFrame; - } else { - if (result == MA_CRC_MISMATCH) { - goto next_iteration; - } else { - return MA_FALSE; - } - } - } else { - runningPCMFrameCount += pFlac->currentFLACFrame.pcmFramesRemaining; - pFlac->currentFLACFrame.pcmFramesRemaining = 0; - isMidFrame = MA_FALSE; - } - if (pcmFrameIndex == pFlac->totalPCMFrameCount && runningPCMFrameCount == pFlac->totalPCMFrameCount) { - return MA_TRUE; - } - } - next_iteration: - if (!ma_dr_flac__read_next_flac_frame_header(&pFlac->bs, pFlac->bitsPerSample, &pFlac->currentFLACFrame.header)) { - return MA_FALSE; - } - } -} -#ifndef MA_DR_FLAC_NO_OGG -typedef struct -{ - ma_uint8 capturePattern[4]; - ma_uint8 structureVersion; - ma_uint8 headerType; - ma_uint64 granulePosition; - ma_uint32 serialNumber; - ma_uint32 sequenceNumber; - ma_uint32 checksum; - ma_uint8 segmentCount; - ma_uint8 segmentTable[255]; -} ma_dr_flac_ogg_page_header; -#endif -typedef struct -{ - ma_dr_flac_read_proc onRead; - ma_dr_flac_seek_proc onSeek; - ma_dr_flac_meta_proc onMeta; - ma_dr_flac_container container; - void* pUserData; - void* pUserDataMD; - ma_uint32 sampleRate; - ma_uint8 channels; - ma_uint8 bitsPerSample; - ma_uint64 totalPCMFrameCount; - ma_uint16 maxBlockSizeInPCMFrames; - ma_uint64 runningFilePos; - ma_bool32 hasStreamInfoBlock; - ma_bool32 hasMetadataBlocks; - ma_dr_flac_bs bs; - ma_dr_flac_frame_header firstFrameHeader; -#ifndef MA_DR_FLAC_NO_OGG - ma_uint32 oggSerial; - ma_uint64 oggFirstBytePos; - ma_dr_flac_ogg_page_header oggBosHeader; -#endif -} ma_dr_flac_init_info; -static MA_INLINE void ma_dr_flac__decode_block_header(ma_uint32 blockHeader, ma_uint8* isLastBlock, ma_uint8* blockType, ma_uint32* blockSize) -{ - blockHeader = ma_dr_flac__be2host_32(blockHeader); - *isLastBlock = (ma_uint8)((blockHeader & 0x80000000UL) >> 31); - *blockType = (ma_uint8)((blockHeader & 0x7F000000UL) >> 24); - *blockSize = (blockHeader & 0x00FFFFFFUL); -} -static MA_INLINE ma_bool32 ma_dr_flac__read_and_decode_block_header(ma_dr_flac_read_proc onRead, void* pUserData, ma_uint8* isLastBlock, ma_uint8* blockType, ma_uint32* blockSize) -{ - ma_uint32 blockHeader; - *blockSize = 0; - if (onRead(pUserData, &blockHeader, 4) != 4) { - return MA_FALSE; - } - ma_dr_flac__decode_block_header(blockHeader, isLastBlock, blockType, blockSize); - return MA_TRUE; -} -static ma_bool32 ma_dr_flac__read_streaminfo(ma_dr_flac_read_proc onRead, void* pUserData, ma_dr_flac_streaminfo* pStreamInfo) -{ - ma_uint32 blockSizes; - ma_uint64 frameSizes = 0; - ma_uint64 importantProps; - ma_uint8 md5[16]; - if (onRead(pUserData, &blockSizes, 4) != 4) { - return MA_FALSE; - } - if (onRead(pUserData, &frameSizes, 6) != 6) { - return MA_FALSE; - } - if (onRead(pUserData, &importantProps, 8) != 8) { - return MA_FALSE; - } - if (onRead(pUserData, md5, sizeof(md5)) != sizeof(md5)) { - return MA_FALSE; - } - blockSizes = ma_dr_flac__be2host_32(blockSizes); - frameSizes = ma_dr_flac__be2host_64(frameSizes); - importantProps = ma_dr_flac__be2host_64(importantProps); - pStreamInfo->minBlockSizeInPCMFrames = (ma_uint16)((blockSizes & 0xFFFF0000) >> 16); - pStreamInfo->maxBlockSizeInPCMFrames = (ma_uint16) (blockSizes & 0x0000FFFF); - pStreamInfo->minFrameSizeInPCMFrames = (ma_uint32)((frameSizes & (((ma_uint64)0x00FFFFFF << 16) << 24)) >> 40); - pStreamInfo->maxFrameSizeInPCMFrames = (ma_uint32)((frameSizes & (((ma_uint64)0x00FFFFFF << 16) << 0)) >> 16); - pStreamInfo->sampleRate = (ma_uint32)((importantProps & (((ma_uint64)0x000FFFFF << 16) << 28)) >> 44); - pStreamInfo->channels = (ma_uint8 )((importantProps & (((ma_uint64)0x0000000E << 16) << 24)) >> 41) + 1; - pStreamInfo->bitsPerSample = (ma_uint8 )((importantProps & (((ma_uint64)0x0000001F << 16) << 20)) >> 36) + 1; - pStreamInfo->totalPCMFrameCount = ((importantProps & ((((ma_uint64)0x0000000F << 16) << 16) | 0xFFFFFFFF))); - MA_DR_FLAC_COPY_MEMORY(pStreamInfo->md5, md5, sizeof(md5)); - return MA_TRUE; -} -static void* ma_dr_flac__malloc_default(size_t sz, void* pUserData) -{ - (void)pUserData; - return MA_DR_FLAC_MALLOC(sz); -} -static void* ma_dr_flac__realloc_default(void* p, size_t sz, void* pUserData) -{ - (void)pUserData; - return MA_DR_FLAC_REALLOC(p, sz); -} -static void ma_dr_flac__free_default(void* p, void* pUserData) -{ - (void)pUserData; - MA_DR_FLAC_FREE(p); -} -static void* ma_dr_flac__malloc_from_callbacks(size_t sz, const ma_allocation_callbacks* pAllocationCallbacks) -{ - if (pAllocationCallbacks == NULL) { - return NULL; - } - if (pAllocationCallbacks->onMalloc != NULL) { - return pAllocationCallbacks->onMalloc(sz, pAllocationCallbacks->pUserData); - } - if (pAllocationCallbacks->onRealloc != NULL) { - return pAllocationCallbacks->onRealloc(NULL, sz, pAllocationCallbacks->pUserData); - } - return NULL; -} -static void* ma_dr_flac__realloc_from_callbacks(void* p, size_t szNew, size_t szOld, const ma_allocation_callbacks* pAllocationCallbacks) -{ - if (pAllocationCallbacks == NULL) { - return NULL; - } - if (pAllocationCallbacks->onRealloc != NULL) { - return pAllocationCallbacks->onRealloc(p, szNew, pAllocationCallbacks->pUserData); - } - if (pAllocationCallbacks->onMalloc != NULL && pAllocationCallbacks->onFree != NULL) { - void* p2; - p2 = pAllocationCallbacks->onMalloc(szNew, pAllocationCallbacks->pUserData); - if (p2 == NULL) { - return NULL; - } - if (p != NULL) { - MA_DR_FLAC_COPY_MEMORY(p2, p, szOld); - pAllocationCallbacks->onFree(p, pAllocationCallbacks->pUserData); - } - return p2; - } - return NULL; -} -static void ma_dr_flac__free_from_callbacks(void* p, const ma_allocation_callbacks* pAllocationCallbacks) -{ - if (p == NULL || pAllocationCallbacks == NULL) { - return; - } - if (pAllocationCallbacks->onFree != NULL) { - pAllocationCallbacks->onFree(p, pAllocationCallbacks->pUserData); - } -} -static ma_bool32 ma_dr_flac__read_and_decode_metadata(ma_dr_flac_read_proc onRead, ma_dr_flac_seek_proc onSeek, ma_dr_flac_meta_proc onMeta, void* pUserData, void* pUserDataMD, ma_uint64* pFirstFramePos, ma_uint64* pSeektablePos, ma_uint32* pSeekpointCount, ma_allocation_callbacks* pAllocationCallbacks) -{ - ma_uint64 runningFilePos = 42; - ma_uint64 seektablePos = 0; - ma_uint32 seektableSize = 0; - for (;;) { - ma_dr_flac_metadata metadata; - ma_uint8 isLastBlock = 0; - ma_uint8 blockType = 0; - ma_uint32 blockSize; - if (ma_dr_flac__read_and_decode_block_header(onRead, pUserData, &isLastBlock, &blockType, &blockSize) == MA_FALSE) { - return MA_FALSE; - } - runningFilePos += 4; - metadata.type = blockType; - metadata.pRawData = NULL; - metadata.rawDataSize = 0; - switch (blockType) - { - case MA_DR_FLAC_METADATA_BLOCK_TYPE_APPLICATION: - { - if (blockSize < 4) { - return MA_FALSE; - } - if (onMeta) { - void* pRawData = ma_dr_flac__malloc_from_callbacks(blockSize, pAllocationCallbacks); - if (pRawData == NULL) { - return MA_FALSE; - } - if (onRead(pUserData, pRawData, blockSize) != blockSize) { - ma_dr_flac__free_from_callbacks(pRawData, pAllocationCallbacks); - return MA_FALSE; - } - metadata.pRawData = pRawData; - metadata.rawDataSize = blockSize; - metadata.data.application.id = ma_dr_flac__be2host_32(*(ma_uint32*)pRawData); - metadata.data.application.pData = (const void*)((ma_uint8*)pRawData + sizeof(ma_uint32)); - metadata.data.application.dataSize = blockSize - sizeof(ma_uint32); - onMeta(pUserDataMD, &metadata); - ma_dr_flac__free_from_callbacks(pRawData, pAllocationCallbacks); - } - } break; - case MA_DR_FLAC_METADATA_BLOCK_TYPE_SEEKTABLE: - { - seektablePos = runningFilePos; - seektableSize = blockSize; - if (onMeta) { - ma_uint32 seekpointCount; - ma_uint32 iSeekpoint; - void* pRawData; - seekpointCount = blockSize/MA_DR_FLAC_SEEKPOINT_SIZE_IN_BYTES; - pRawData = ma_dr_flac__malloc_from_callbacks(seekpointCount * sizeof(ma_dr_flac_seekpoint), pAllocationCallbacks); - if (pRawData == NULL) { - return MA_FALSE; - } - for (iSeekpoint = 0; iSeekpoint < seekpointCount; ++iSeekpoint) { - ma_dr_flac_seekpoint* pSeekpoint = (ma_dr_flac_seekpoint*)pRawData + iSeekpoint; - if (onRead(pUserData, pSeekpoint, MA_DR_FLAC_SEEKPOINT_SIZE_IN_BYTES) != MA_DR_FLAC_SEEKPOINT_SIZE_IN_BYTES) { - ma_dr_flac__free_from_callbacks(pRawData, pAllocationCallbacks); - return MA_FALSE; - } - pSeekpoint->firstPCMFrame = ma_dr_flac__be2host_64(pSeekpoint->firstPCMFrame); - pSeekpoint->flacFrameOffset = ma_dr_flac__be2host_64(pSeekpoint->flacFrameOffset); - pSeekpoint->pcmFrameCount = ma_dr_flac__be2host_16(pSeekpoint->pcmFrameCount); - } - metadata.pRawData = pRawData; - metadata.rawDataSize = blockSize; - metadata.data.seektable.seekpointCount = seekpointCount; - metadata.data.seektable.pSeekpoints = (const ma_dr_flac_seekpoint*)pRawData; - onMeta(pUserDataMD, &metadata); - ma_dr_flac__free_from_callbacks(pRawData, pAllocationCallbacks); - } - } break; - case MA_DR_FLAC_METADATA_BLOCK_TYPE_VORBIS_COMMENT: - { - if (blockSize < 8) { - return MA_FALSE; - } - if (onMeta) { - void* pRawData; - const char* pRunningData; - const char* pRunningDataEnd; - ma_uint32 i; - pRawData = ma_dr_flac__malloc_from_callbacks(blockSize, pAllocationCallbacks); - if (pRawData == NULL) { - return MA_FALSE; - } - if (onRead(pUserData, pRawData, blockSize) != blockSize) { - ma_dr_flac__free_from_callbacks(pRawData, pAllocationCallbacks); - return MA_FALSE; - } - metadata.pRawData = pRawData; - metadata.rawDataSize = blockSize; - pRunningData = (const char*)pRawData; - pRunningDataEnd = (const char*)pRawData + blockSize; - metadata.data.vorbis_comment.vendorLength = ma_dr_flac__le2host_32_ptr_unaligned(pRunningData); pRunningData += 4; - if ((pRunningDataEnd - pRunningData) - 4 < (ma_int64)metadata.data.vorbis_comment.vendorLength) { - ma_dr_flac__free_from_callbacks(pRawData, pAllocationCallbacks); - return MA_FALSE; - } - metadata.data.vorbis_comment.vendor = pRunningData; pRunningData += metadata.data.vorbis_comment.vendorLength; - metadata.data.vorbis_comment.commentCount = ma_dr_flac__le2host_32_ptr_unaligned(pRunningData); pRunningData += 4; - if ((pRunningDataEnd - pRunningData) / sizeof(ma_uint32) < metadata.data.vorbis_comment.commentCount) { - ma_dr_flac__free_from_callbacks(pRawData, pAllocationCallbacks); - return MA_FALSE; - } - metadata.data.vorbis_comment.pComments = pRunningData; - for (i = 0; i < metadata.data.vorbis_comment.commentCount; ++i) { - ma_uint32 commentLength; - if (pRunningDataEnd - pRunningData < 4) { - ma_dr_flac__free_from_callbacks(pRawData, pAllocationCallbacks); - return MA_FALSE; - } - commentLength = ma_dr_flac__le2host_32_ptr_unaligned(pRunningData); pRunningData += 4; - if (pRunningDataEnd - pRunningData < (ma_int64)commentLength) { - ma_dr_flac__free_from_callbacks(pRawData, pAllocationCallbacks); - return MA_FALSE; - } - pRunningData += commentLength; - } - onMeta(pUserDataMD, &metadata); - ma_dr_flac__free_from_callbacks(pRawData, pAllocationCallbacks); - } - } break; - case MA_DR_FLAC_METADATA_BLOCK_TYPE_CUESHEET: - { - if (blockSize < 396) { - return MA_FALSE; - } - if (onMeta) { - void* pRawData; - const char* pRunningData; - const char* pRunningDataEnd; - size_t bufferSize; - ma_uint8 iTrack; - ma_uint8 iIndex; - void* pTrackData; - pRawData = ma_dr_flac__malloc_from_callbacks(blockSize, pAllocationCallbacks); - if (pRawData == NULL) { - return MA_FALSE; - } - if (onRead(pUserData, pRawData, blockSize) != blockSize) { - ma_dr_flac__free_from_callbacks(pRawData, pAllocationCallbacks); - return MA_FALSE; - } - metadata.pRawData = pRawData; - metadata.rawDataSize = blockSize; - pRunningData = (const char*)pRawData; - pRunningDataEnd = (const char*)pRawData + blockSize; - MA_DR_FLAC_COPY_MEMORY(metadata.data.cuesheet.catalog, pRunningData, 128); pRunningData += 128; - metadata.data.cuesheet.leadInSampleCount = ma_dr_flac__be2host_64(*(const ma_uint64*)pRunningData); pRunningData += 8; - metadata.data.cuesheet.isCD = (pRunningData[0] & 0x80) != 0; pRunningData += 259; - metadata.data.cuesheet.trackCount = pRunningData[0]; pRunningData += 1; - metadata.data.cuesheet.pTrackData = NULL; - { - const char* pRunningDataSaved = pRunningData; - bufferSize = metadata.data.cuesheet.trackCount * MA_DR_FLAC_CUESHEET_TRACK_SIZE_IN_BYTES; - for (iTrack = 0; iTrack < metadata.data.cuesheet.trackCount; ++iTrack) { - ma_uint8 indexCount; - ma_uint32 indexPointSize; - if (pRunningDataEnd - pRunningData < MA_DR_FLAC_CUESHEET_TRACK_SIZE_IN_BYTES) { - ma_dr_flac__free_from_callbacks(pRawData, pAllocationCallbacks); - return MA_FALSE; - } - pRunningData += 35; - indexCount = pRunningData[0]; - pRunningData += 1; - bufferSize += indexCount * sizeof(ma_dr_flac_cuesheet_track_index); - indexPointSize = indexCount * MA_DR_FLAC_CUESHEET_TRACK_INDEX_SIZE_IN_BYTES; - if (pRunningDataEnd - pRunningData < (ma_int64)indexPointSize) { - ma_dr_flac__free_from_callbacks(pRawData, pAllocationCallbacks); - return MA_FALSE; - } - pRunningData += indexPointSize; - } - pRunningData = pRunningDataSaved; - } - { - char* pRunningTrackData; - pTrackData = ma_dr_flac__malloc_from_callbacks(bufferSize, pAllocationCallbacks); - if (pTrackData == NULL) { - ma_dr_flac__free_from_callbacks(pRawData, pAllocationCallbacks); - return MA_FALSE; - } - pRunningTrackData = (char*)pTrackData; - for (iTrack = 0; iTrack < metadata.data.cuesheet.trackCount; ++iTrack) { - ma_uint8 indexCount; - MA_DR_FLAC_COPY_MEMORY(pRunningTrackData, pRunningData, MA_DR_FLAC_CUESHEET_TRACK_SIZE_IN_BYTES); - pRunningData += MA_DR_FLAC_CUESHEET_TRACK_SIZE_IN_BYTES-1; - pRunningTrackData += MA_DR_FLAC_CUESHEET_TRACK_SIZE_IN_BYTES-1; - indexCount = pRunningData[0]; - pRunningData += 1; - pRunningTrackData += 1; - for (iIndex = 0; iIndex < indexCount; ++iIndex) { - ma_dr_flac_cuesheet_track_index* pTrackIndex = (ma_dr_flac_cuesheet_track_index*)pRunningTrackData; - MA_DR_FLAC_COPY_MEMORY(pRunningTrackData, pRunningData, MA_DR_FLAC_CUESHEET_TRACK_INDEX_SIZE_IN_BYTES); - pRunningData += MA_DR_FLAC_CUESHEET_TRACK_INDEX_SIZE_IN_BYTES; - pRunningTrackData += sizeof(ma_dr_flac_cuesheet_track_index); - pTrackIndex->offset = ma_dr_flac__be2host_64(pTrackIndex->offset); - } - } - metadata.data.cuesheet.pTrackData = pTrackData; - } - ma_dr_flac__free_from_callbacks(pRawData, pAllocationCallbacks); - pRawData = NULL; - onMeta(pUserDataMD, &metadata); - ma_dr_flac__free_from_callbacks(pTrackData, pAllocationCallbacks); - pTrackData = NULL; - } - } break; - case MA_DR_FLAC_METADATA_BLOCK_TYPE_PICTURE: - { - if (blockSize < 32) { - return MA_FALSE; - } - if (onMeta) { - void* pRawData; - const char* pRunningData; - const char* pRunningDataEnd; - pRawData = ma_dr_flac__malloc_from_callbacks(blockSize, pAllocationCallbacks); - if (pRawData == NULL) { - return MA_FALSE; - } - if (onRead(pUserData, pRawData, blockSize) != blockSize) { - ma_dr_flac__free_from_callbacks(pRawData, pAllocationCallbacks); - return MA_FALSE; - } - metadata.pRawData = pRawData; - metadata.rawDataSize = blockSize; - pRunningData = (const char*)pRawData; - pRunningDataEnd = (const char*)pRawData + blockSize; - metadata.data.picture.type = ma_dr_flac__be2host_32_ptr_unaligned(pRunningData); pRunningData += 4; - metadata.data.picture.mimeLength = ma_dr_flac__be2host_32_ptr_unaligned(pRunningData); pRunningData += 4; - if ((pRunningDataEnd - pRunningData) - 24 < (ma_int64)metadata.data.picture.mimeLength) { - ma_dr_flac__free_from_callbacks(pRawData, pAllocationCallbacks); - return MA_FALSE; - } - metadata.data.picture.mime = pRunningData; pRunningData += metadata.data.picture.mimeLength; - metadata.data.picture.descriptionLength = ma_dr_flac__be2host_32_ptr_unaligned(pRunningData); pRunningData += 4; - if ((pRunningDataEnd - pRunningData) - 20 < (ma_int64)metadata.data.picture.descriptionLength) { - ma_dr_flac__free_from_callbacks(pRawData, pAllocationCallbacks); - return MA_FALSE; - } - metadata.data.picture.description = pRunningData; pRunningData += metadata.data.picture.descriptionLength; - metadata.data.picture.width = ma_dr_flac__be2host_32_ptr_unaligned(pRunningData); pRunningData += 4; - metadata.data.picture.height = ma_dr_flac__be2host_32_ptr_unaligned(pRunningData); pRunningData += 4; - metadata.data.picture.colorDepth = ma_dr_flac__be2host_32_ptr_unaligned(pRunningData); pRunningData += 4; - metadata.data.picture.indexColorCount = ma_dr_flac__be2host_32_ptr_unaligned(pRunningData); pRunningData += 4; - metadata.data.picture.pictureDataSize = ma_dr_flac__be2host_32_ptr_unaligned(pRunningData); pRunningData += 4; - metadata.data.picture.pPictureData = (const ma_uint8*)pRunningData; - if (pRunningDataEnd - pRunningData < (ma_int64)metadata.data.picture.pictureDataSize) { - ma_dr_flac__free_from_callbacks(pRawData, pAllocationCallbacks); - return MA_FALSE; - } - onMeta(pUserDataMD, &metadata); - ma_dr_flac__free_from_callbacks(pRawData, pAllocationCallbacks); - } - } break; - case MA_DR_FLAC_METADATA_BLOCK_TYPE_PADDING: - { - if (onMeta) { - metadata.data.padding.unused = 0; - if (!onSeek(pUserData, blockSize, ma_dr_flac_seek_origin_current)) { - isLastBlock = MA_TRUE; - } else { - onMeta(pUserDataMD, &metadata); - } - } - } break; - case MA_DR_FLAC_METADATA_BLOCK_TYPE_INVALID: - { - if (onMeta) { - if (!onSeek(pUserData, blockSize, ma_dr_flac_seek_origin_current)) { - isLastBlock = MA_TRUE; - } - } - } break; - default: - { - if (onMeta) { - void* pRawData = ma_dr_flac__malloc_from_callbacks(blockSize, pAllocationCallbacks); - if (pRawData == NULL) { - return MA_FALSE; - } - if (onRead(pUserData, pRawData, blockSize) != blockSize) { - ma_dr_flac__free_from_callbacks(pRawData, pAllocationCallbacks); - return MA_FALSE; - } - metadata.pRawData = pRawData; - metadata.rawDataSize = blockSize; - onMeta(pUserDataMD, &metadata); - ma_dr_flac__free_from_callbacks(pRawData, pAllocationCallbacks); - } - } break; - } - if (onMeta == NULL && blockSize > 0) { - if (!onSeek(pUserData, blockSize, ma_dr_flac_seek_origin_current)) { - isLastBlock = MA_TRUE; - } - } - runningFilePos += blockSize; - if (isLastBlock) { - break; - } - } - *pSeektablePos = seektablePos; - *pSeekpointCount = seektableSize / MA_DR_FLAC_SEEKPOINT_SIZE_IN_BYTES; - *pFirstFramePos = runningFilePos; - return MA_TRUE; -} -static ma_bool32 ma_dr_flac__init_private__native(ma_dr_flac_init_info* pInit, ma_dr_flac_read_proc onRead, ma_dr_flac_seek_proc onSeek, ma_dr_flac_meta_proc onMeta, void* pUserData, void* pUserDataMD, ma_bool32 relaxed) -{ - ma_uint8 isLastBlock; - ma_uint8 blockType; - ma_uint32 blockSize; - (void)onSeek; - pInit->container = ma_dr_flac_container_native; - if (!ma_dr_flac__read_and_decode_block_header(onRead, pUserData, &isLastBlock, &blockType, &blockSize)) { - return MA_FALSE; - } - if (blockType != MA_DR_FLAC_METADATA_BLOCK_TYPE_STREAMINFO || blockSize != 34) { - if (!relaxed) { - return MA_FALSE; - } else { - pInit->hasStreamInfoBlock = MA_FALSE; - pInit->hasMetadataBlocks = MA_FALSE; - if (!ma_dr_flac__read_next_flac_frame_header(&pInit->bs, 0, &pInit->firstFrameHeader)) { - return MA_FALSE; - } - if (pInit->firstFrameHeader.bitsPerSample == 0) { - return MA_FALSE; - } - pInit->sampleRate = pInit->firstFrameHeader.sampleRate; - pInit->channels = ma_dr_flac__get_channel_count_from_channel_assignment(pInit->firstFrameHeader.channelAssignment); - pInit->bitsPerSample = pInit->firstFrameHeader.bitsPerSample; - pInit->maxBlockSizeInPCMFrames = 65535; - return MA_TRUE; - } - } else { - ma_dr_flac_streaminfo streaminfo; - if (!ma_dr_flac__read_streaminfo(onRead, pUserData, &streaminfo)) { - return MA_FALSE; - } - pInit->hasStreamInfoBlock = MA_TRUE; - pInit->sampleRate = streaminfo.sampleRate; - pInit->channels = streaminfo.channels; - pInit->bitsPerSample = streaminfo.bitsPerSample; - pInit->totalPCMFrameCount = streaminfo.totalPCMFrameCount; - pInit->maxBlockSizeInPCMFrames = streaminfo.maxBlockSizeInPCMFrames; - pInit->hasMetadataBlocks = !isLastBlock; - if (onMeta) { - ma_dr_flac_metadata metadata; - metadata.type = MA_DR_FLAC_METADATA_BLOCK_TYPE_STREAMINFO; - metadata.pRawData = NULL; - metadata.rawDataSize = 0; - metadata.data.streaminfo = streaminfo; - onMeta(pUserDataMD, &metadata); - } - return MA_TRUE; - } -} -#ifndef MA_DR_FLAC_NO_OGG -#define MA_DR_FLAC_OGG_MAX_PAGE_SIZE 65307 -#define MA_DR_FLAC_OGG_CAPTURE_PATTERN_CRC32 1605413199 -typedef enum -{ - ma_dr_flac_ogg_recover_on_crc_mismatch, - ma_dr_flac_ogg_fail_on_crc_mismatch -} ma_dr_flac_ogg_crc_mismatch_recovery; -#ifndef MA_DR_FLAC_NO_CRC -static ma_uint32 ma_dr_flac__crc32_table[] = { - 0x00000000L, 0x04C11DB7L, 0x09823B6EL, 0x0D4326D9L, - 0x130476DCL, 0x17C56B6BL, 0x1A864DB2L, 0x1E475005L, - 0x2608EDB8L, 0x22C9F00FL, 0x2F8AD6D6L, 0x2B4BCB61L, - 0x350C9B64L, 0x31CD86D3L, 0x3C8EA00AL, 0x384FBDBDL, - 0x4C11DB70L, 0x48D0C6C7L, 0x4593E01EL, 0x4152FDA9L, - 0x5F15ADACL, 0x5BD4B01BL, 0x569796C2L, 0x52568B75L, - 0x6A1936C8L, 0x6ED82B7FL, 0x639B0DA6L, 0x675A1011L, - 0x791D4014L, 0x7DDC5DA3L, 0x709F7B7AL, 0x745E66CDL, - 0x9823B6E0L, 0x9CE2AB57L, 0x91A18D8EL, 0x95609039L, - 0x8B27C03CL, 0x8FE6DD8BL, 0x82A5FB52L, 0x8664E6E5L, - 0xBE2B5B58L, 0xBAEA46EFL, 0xB7A96036L, 0xB3687D81L, - 0xAD2F2D84L, 0xA9EE3033L, 0xA4AD16EAL, 0xA06C0B5DL, - 0xD4326D90L, 0xD0F37027L, 0xDDB056FEL, 0xD9714B49L, - 0xC7361B4CL, 0xC3F706FBL, 0xCEB42022L, 0xCA753D95L, - 0xF23A8028L, 0xF6FB9D9FL, 0xFBB8BB46L, 0xFF79A6F1L, - 0xE13EF6F4L, 0xE5FFEB43L, 0xE8BCCD9AL, 0xEC7DD02DL, - 0x34867077L, 0x30476DC0L, 0x3D044B19L, 0x39C556AEL, - 0x278206ABL, 0x23431B1CL, 0x2E003DC5L, 0x2AC12072L, - 0x128E9DCFL, 0x164F8078L, 0x1B0CA6A1L, 0x1FCDBB16L, - 0x018AEB13L, 0x054BF6A4L, 0x0808D07DL, 0x0CC9CDCAL, - 0x7897AB07L, 0x7C56B6B0L, 0x71159069L, 0x75D48DDEL, - 0x6B93DDDBL, 0x6F52C06CL, 0x6211E6B5L, 0x66D0FB02L, - 0x5E9F46BFL, 0x5A5E5B08L, 0x571D7DD1L, 0x53DC6066L, - 0x4D9B3063L, 0x495A2DD4L, 0x44190B0DL, 0x40D816BAL, - 0xACA5C697L, 0xA864DB20L, 0xA527FDF9L, 0xA1E6E04EL, - 0xBFA1B04BL, 0xBB60ADFCL, 0xB6238B25L, 0xB2E29692L, - 0x8AAD2B2FL, 0x8E6C3698L, 0x832F1041L, 0x87EE0DF6L, - 0x99A95DF3L, 0x9D684044L, 0x902B669DL, 0x94EA7B2AL, - 0xE0B41DE7L, 0xE4750050L, 0xE9362689L, 0xEDF73B3EL, - 0xF3B06B3BL, 0xF771768CL, 0xFA325055L, 0xFEF34DE2L, - 0xC6BCF05FL, 0xC27DEDE8L, 0xCF3ECB31L, 0xCBFFD686L, - 0xD5B88683L, 0xD1799B34L, 0xDC3ABDEDL, 0xD8FBA05AL, - 0x690CE0EEL, 0x6DCDFD59L, 0x608EDB80L, 0x644FC637L, - 0x7A089632L, 0x7EC98B85L, 0x738AAD5CL, 0x774BB0EBL, - 0x4F040D56L, 0x4BC510E1L, 0x46863638L, 0x42472B8FL, - 0x5C007B8AL, 0x58C1663DL, 0x558240E4L, 0x51435D53L, - 0x251D3B9EL, 0x21DC2629L, 0x2C9F00F0L, 0x285E1D47L, - 0x36194D42L, 0x32D850F5L, 0x3F9B762CL, 0x3B5A6B9BL, - 0x0315D626L, 0x07D4CB91L, 0x0A97ED48L, 0x0E56F0FFL, - 0x1011A0FAL, 0x14D0BD4DL, 0x19939B94L, 0x1D528623L, - 0xF12F560EL, 0xF5EE4BB9L, 0xF8AD6D60L, 0xFC6C70D7L, - 0xE22B20D2L, 0xE6EA3D65L, 0xEBA91BBCL, 0xEF68060BL, - 0xD727BBB6L, 0xD3E6A601L, 0xDEA580D8L, 0xDA649D6FL, - 0xC423CD6AL, 0xC0E2D0DDL, 0xCDA1F604L, 0xC960EBB3L, - 0xBD3E8D7EL, 0xB9FF90C9L, 0xB4BCB610L, 0xB07DABA7L, - 0xAE3AFBA2L, 0xAAFBE615L, 0xA7B8C0CCL, 0xA379DD7BL, - 0x9B3660C6L, 0x9FF77D71L, 0x92B45BA8L, 0x9675461FL, - 0x8832161AL, 0x8CF30BADL, 0x81B02D74L, 0x857130C3L, - 0x5D8A9099L, 0x594B8D2EL, 0x5408ABF7L, 0x50C9B640L, - 0x4E8EE645L, 0x4A4FFBF2L, 0x470CDD2BL, 0x43CDC09CL, - 0x7B827D21L, 0x7F436096L, 0x7200464FL, 0x76C15BF8L, - 0x68860BFDL, 0x6C47164AL, 0x61043093L, 0x65C52D24L, - 0x119B4BE9L, 0x155A565EL, 0x18197087L, 0x1CD86D30L, - 0x029F3D35L, 0x065E2082L, 0x0B1D065BL, 0x0FDC1BECL, - 0x3793A651L, 0x3352BBE6L, 0x3E119D3FL, 0x3AD08088L, - 0x2497D08DL, 0x2056CD3AL, 0x2D15EBE3L, 0x29D4F654L, - 0xC5A92679L, 0xC1683BCEL, 0xCC2B1D17L, 0xC8EA00A0L, - 0xD6AD50A5L, 0xD26C4D12L, 0xDF2F6BCBL, 0xDBEE767CL, - 0xE3A1CBC1L, 0xE760D676L, 0xEA23F0AFL, 0xEEE2ED18L, - 0xF0A5BD1DL, 0xF464A0AAL, 0xF9278673L, 0xFDE69BC4L, - 0x89B8FD09L, 0x8D79E0BEL, 0x803AC667L, 0x84FBDBD0L, - 0x9ABC8BD5L, 0x9E7D9662L, 0x933EB0BBL, 0x97FFAD0CL, - 0xAFB010B1L, 0xAB710D06L, 0xA6322BDFL, 0xA2F33668L, - 0xBCB4666DL, 0xB8757BDAL, 0xB5365D03L, 0xB1F740B4L -}; -#endif -static MA_INLINE ma_uint32 ma_dr_flac_crc32_byte(ma_uint32 crc32, ma_uint8 data) -{ -#ifndef MA_DR_FLAC_NO_CRC - return (crc32 << 8) ^ ma_dr_flac__crc32_table[(ma_uint8)((crc32 >> 24) & 0xFF) ^ data]; -#else - (void)data; - return crc32; -#endif -} -#if 0 -static MA_INLINE ma_uint32 ma_dr_flac_crc32_uint32(ma_uint32 crc32, ma_uint32 data) -{ - crc32 = ma_dr_flac_crc32_byte(crc32, (ma_uint8)((data >> 24) & 0xFF)); - crc32 = ma_dr_flac_crc32_byte(crc32, (ma_uint8)((data >> 16) & 0xFF)); - crc32 = ma_dr_flac_crc32_byte(crc32, (ma_uint8)((data >> 8) & 0xFF)); - crc32 = ma_dr_flac_crc32_byte(crc32, (ma_uint8)((data >> 0) & 0xFF)); - return crc32; -} -static MA_INLINE ma_uint32 ma_dr_flac_crc32_uint64(ma_uint32 crc32, ma_uint64 data) -{ - crc32 = ma_dr_flac_crc32_uint32(crc32, (ma_uint32)((data >> 32) & 0xFFFFFFFF)); - crc32 = ma_dr_flac_crc32_uint32(crc32, (ma_uint32)((data >> 0) & 0xFFFFFFFF)); - return crc32; -} -#endif -static MA_INLINE ma_uint32 ma_dr_flac_crc32_buffer(ma_uint32 crc32, ma_uint8* pData, ma_uint32 dataSize) -{ - ma_uint32 i; - for (i = 0; i < dataSize; ++i) { - crc32 = ma_dr_flac_crc32_byte(crc32, pData[i]); - } - return crc32; -} -static MA_INLINE ma_bool32 ma_dr_flac_ogg__is_capture_pattern(ma_uint8 pattern[4]) -{ - return pattern[0] == 'O' && pattern[1] == 'g' && pattern[2] == 'g' && pattern[3] == 'S'; -} -static MA_INLINE ma_uint32 ma_dr_flac_ogg__get_page_header_size(ma_dr_flac_ogg_page_header* pHeader) -{ - return 27 + pHeader->segmentCount; -} -static MA_INLINE ma_uint32 ma_dr_flac_ogg__get_page_body_size(ma_dr_flac_ogg_page_header* pHeader) -{ - ma_uint32 pageBodySize = 0; - int i; - for (i = 0; i < pHeader->segmentCount; ++i) { - pageBodySize += pHeader->segmentTable[i]; - } - return pageBodySize; -} -static ma_result ma_dr_flac_ogg__read_page_header_after_capture_pattern(ma_dr_flac_read_proc onRead, void* pUserData, ma_dr_flac_ogg_page_header* pHeader, ma_uint32* pBytesRead, ma_uint32* pCRC32) -{ - ma_uint8 data[23]; - ma_uint32 i; - MA_DR_FLAC_ASSERT(*pCRC32 == MA_DR_FLAC_OGG_CAPTURE_PATTERN_CRC32); - if (onRead(pUserData, data, 23) != 23) { - return MA_AT_END; - } - *pBytesRead += 23; - pHeader->capturePattern[0] = 'O'; - pHeader->capturePattern[1] = 'g'; - pHeader->capturePattern[2] = 'g'; - pHeader->capturePattern[3] = 'S'; - pHeader->structureVersion = data[0]; - pHeader->headerType = data[1]; - MA_DR_FLAC_COPY_MEMORY(&pHeader->granulePosition, &data[ 2], 8); - MA_DR_FLAC_COPY_MEMORY(&pHeader->serialNumber, &data[10], 4); - MA_DR_FLAC_COPY_MEMORY(&pHeader->sequenceNumber, &data[14], 4); - MA_DR_FLAC_COPY_MEMORY(&pHeader->checksum, &data[18], 4); - pHeader->segmentCount = data[22]; - data[18] = 0; - data[19] = 0; - data[20] = 0; - data[21] = 0; - for (i = 0; i < 23; ++i) { - *pCRC32 = ma_dr_flac_crc32_byte(*pCRC32, data[i]); - } - if (onRead(pUserData, pHeader->segmentTable, pHeader->segmentCount) != pHeader->segmentCount) { - return MA_AT_END; - } - *pBytesRead += pHeader->segmentCount; - for (i = 0; i < pHeader->segmentCount; ++i) { - *pCRC32 = ma_dr_flac_crc32_byte(*pCRC32, pHeader->segmentTable[i]); - } - return MA_SUCCESS; -} -static ma_result ma_dr_flac_ogg__read_page_header(ma_dr_flac_read_proc onRead, void* pUserData, ma_dr_flac_ogg_page_header* pHeader, ma_uint32* pBytesRead, ma_uint32* pCRC32) -{ - ma_uint8 id[4]; - *pBytesRead = 0; - if (onRead(pUserData, id, 4) != 4) { - return MA_AT_END; - } - *pBytesRead += 4; - for (;;) { - if (ma_dr_flac_ogg__is_capture_pattern(id)) { - ma_result result; - *pCRC32 = MA_DR_FLAC_OGG_CAPTURE_PATTERN_CRC32; - result = ma_dr_flac_ogg__read_page_header_after_capture_pattern(onRead, pUserData, pHeader, pBytesRead, pCRC32); - if (result == MA_SUCCESS) { - return MA_SUCCESS; - } else { - if (result == MA_CRC_MISMATCH) { - continue; - } else { - return result; - } - } - } else { - id[0] = id[1]; - id[1] = id[2]; - id[2] = id[3]; - if (onRead(pUserData, &id[3], 1) != 1) { - return MA_AT_END; - } - *pBytesRead += 1; - } - } -} -typedef struct -{ - ma_dr_flac_read_proc onRead; - ma_dr_flac_seek_proc onSeek; - void* pUserData; - ma_uint64 currentBytePos; - ma_uint64 firstBytePos; - ma_uint32 serialNumber; - ma_dr_flac_ogg_page_header bosPageHeader; - ma_dr_flac_ogg_page_header currentPageHeader; - ma_uint32 bytesRemainingInPage; - ma_uint32 pageDataSize; - ma_uint8 pageData[MA_DR_FLAC_OGG_MAX_PAGE_SIZE]; -} ma_dr_flac_oggbs; -static size_t ma_dr_flac_oggbs__read_physical(ma_dr_flac_oggbs* oggbs, void* bufferOut, size_t bytesToRead) -{ - size_t bytesActuallyRead = oggbs->onRead(oggbs->pUserData, bufferOut, bytesToRead); - oggbs->currentBytePos += bytesActuallyRead; - return bytesActuallyRead; -} -static ma_bool32 ma_dr_flac_oggbs__seek_physical(ma_dr_flac_oggbs* oggbs, ma_uint64 offset, ma_dr_flac_seek_origin origin) -{ - if (origin == ma_dr_flac_seek_origin_start) { - if (offset <= 0x7FFFFFFF) { - if (!oggbs->onSeek(oggbs->pUserData, (int)offset, ma_dr_flac_seek_origin_start)) { - return MA_FALSE; - } - oggbs->currentBytePos = offset; - return MA_TRUE; - } else { - if (!oggbs->onSeek(oggbs->pUserData, 0x7FFFFFFF, ma_dr_flac_seek_origin_start)) { - return MA_FALSE; - } - oggbs->currentBytePos = offset; - return ma_dr_flac_oggbs__seek_physical(oggbs, offset - 0x7FFFFFFF, ma_dr_flac_seek_origin_current); - } - } else { - while (offset > 0x7FFFFFFF) { - if (!oggbs->onSeek(oggbs->pUserData, 0x7FFFFFFF, ma_dr_flac_seek_origin_current)) { - return MA_FALSE; - } - oggbs->currentBytePos += 0x7FFFFFFF; - offset -= 0x7FFFFFFF; - } - if (!oggbs->onSeek(oggbs->pUserData, (int)offset, ma_dr_flac_seek_origin_current)) { - return MA_FALSE; - } - oggbs->currentBytePos += offset; - return MA_TRUE; - } -} -static ma_bool32 ma_dr_flac_oggbs__goto_next_page(ma_dr_flac_oggbs* oggbs, ma_dr_flac_ogg_crc_mismatch_recovery recoveryMethod) -{ - ma_dr_flac_ogg_page_header header; - for (;;) { - ma_uint32 crc32 = 0; - ma_uint32 bytesRead; - ma_uint32 pageBodySize; -#ifndef MA_DR_FLAC_NO_CRC - ma_uint32 actualCRC32; -#endif - if (ma_dr_flac_ogg__read_page_header(oggbs->onRead, oggbs->pUserData, &header, &bytesRead, &crc32) != MA_SUCCESS) { - return MA_FALSE; - } - oggbs->currentBytePos += bytesRead; - pageBodySize = ma_dr_flac_ogg__get_page_body_size(&header); - if (pageBodySize > MA_DR_FLAC_OGG_MAX_PAGE_SIZE) { - continue; - } - if (header.serialNumber != oggbs->serialNumber) { - if (pageBodySize > 0 && !ma_dr_flac_oggbs__seek_physical(oggbs, pageBodySize, ma_dr_flac_seek_origin_current)) { - return MA_FALSE; - } - continue; - } - if (ma_dr_flac_oggbs__read_physical(oggbs, oggbs->pageData, pageBodySize) != pageBodySize) { - return MA_FALSE; - } - oggbs->pageDataSize = pageBodySize; -#ifndef MA_DR_FLAC_NO_CRC - actualCRC32 = ma_dr_flac_crc32_buffer(crc32, oggbs->pageData, oggbs->pageDataSize); - if (actualCRC32 != header.checksum) { - if (recoveryMethod == ma_dr_flac_ogg_recover_on_crc_mismatch) { - continue; - } else { - ma_dr_flac_oggbs__goto_next_page(oggbs, ma_dr_flac_ogg_recover_on_crc_mismatch); - return MA_FALSE; - } - } -#else - (void)recoveryMethod; -#endif - oggbs->currentPageHeader = header; - oggbs->bytesRemainingInPage = pageBodySize; - return MA_TRUE; - } -} -#if 0 -static ma_uint8 ma_dr_flac_oggbs__get_current_segment_index(ma_dr_flac_oggbs* oggbs, ma_uint8* pBytesRemainingInSeg) -{ - ma_uint32 bytesConsumedInPage = ma_dr_flac_ogg__get_page_body_size(&oggbs->currentPageHeader) - oggbs->bytesRemainingInPage; - ma_uint8 iSeg = 0; - ma_uint32 iByte = 0; - while (iByte < bytesConsumedInPage) { - ma_uint8 segmentSize = oggbs->currentPageHeader.segmentTable[iSeg]; - if (iByte + segmentSize > bytesConsumedInPage) { - break; - } else { - iSeg += 1; - iByte += segmentSize; - } - } - *pBytesRemainingInSeg = oggbs->currentPageHeader.segmentTable[iSeg] - (ma_uint8)(bytesConsumedInPage - iByte); - return iSeg; -} -static ma_bool32 ma_dr_flac_oggbs__seek_to_next_packet(ma_dr_flac_oggbs* oggbs) -{ - for (;;) { - ma_bool32 atEndOfPage = MA_FALSE; - ma_uint8 bytesRemainingInSeg; - ma_uint8 iFirstSeg = ma_dr_flac_oggbs__get_current_segment_index(oggbs, &bytesRemainingInSeg); - ma_uint32 bytesToEndOfPacketOrPage = bytesRemainingInSeg; - for (ma_uint8 iSeg = iFirstSeg; iSeg < oggbs->currentPageHeader.segmentCount; ++iSeg) { - ma_uint8 segmentSize = oggbs->currentPageHeader.segmentTable[iSeg]; - if (segmentSize < 255) { - if (iSeg == oggbs->currentPageHeader.segmentCount-1) { - atEndOfPage = MA_TRUE; - } - break; - } - bytesToEndOfPacketOrPage += segmentSize; - } - ma_dr_flac_oggbs__seek_physical(oggbs, bytesToEndOfPacketOrPage, ma_dr_flac_seek_origin_current); - oggbs->bytesRemainingInPage -= bytesToEndOfPacketOrPage; - if (atEndOfPage) { - if (!ma_dr_flac_oggbs__goto_next_page(oggbs)) { - return MA_FALSE; - } - if ((oggbs->currentPageHeader.headerType & 0x01) == 0) { - return MA_TRUE; - } - } else { - return MA_TRUE; - } - } -} -static ma_bool32 ma_dr_flac_oggbs__seek_to_next_frame(ma_dr_flac_oggbs* oggbs) -{ - return ma_dr_flac_oggbs__seek_to_next_packet(oggbs); -} -#endif -static size_t ma_dr_flac__on_read_ogg(void* pUserData, void* bufferOut, size_t bytesToRead) -{ - ma_dr_flac_oggbs* oggbs = (ma_dr_flac_oggbs*)pUserData; - ma_uint8* pRunningBufferOut = (ma_uint8*)bufferOut; - size_t bytesRead = 0; - MA_DR_FLAC_ASSERT(oggbs != NULL); - MA_DR_FLAC_ASSERT(pRunningBufferOut != NULL); - while (bytesRead < bytesToRead) { - size_t bytesRemainingToRead = bytesToRead - bytesRead; - if (oggbs->bytesRemainingInPage >= bytesRemainingToRead) { - MA_DR_FLAC_COPY_MEMORY(pRunningBufferOut, oggbs->pageData + (oggbs->pageDataSize - oggbs->bytesRemainingInPage), bytesRemainingToRead); - bytesRead += bytesRemainingToRead; - oggbs->bytesRemainingInPage -= (ma_uint32)bytesRemainingToRead; - break; - } - if (oggbs->bytesRemainingInPage > 0) { - MA_DR_FLAC_COPY_MEMORY(pRunningBufferOut, oggbs->pageData + (oggbs->pageDataSize - oggbs->bytesRemainingInPage), oggbs->bytesRemainingInPage); - bytesRead += oggbs->bytesRemainingInPage; - pRunningBufferOut += oggbs->bytesRemainingInPage; - oggbs->bytesRemainingInPage = 0; - } - MA_DR_FLAC_ASSERT(bytesRemainingToRead > 0); - if (!ma_dr_flac_oggbs__goto_next_page(oggbs, ma_dr_flac_ogg_recover_on_crc_mismatch)) { - break; - } - } - return bytesRead; -} -static ma_bool32 ma_dr_flac__on_seek_ogg(void* pUserData, int offset, ma_dr_flac_seek_origin origin) -{ - ma_dr_flac_oggbs* oggbs = (ma_dr_flac_oggbs*)pUserData; - int bytesSeeked = 0; - MA_DR_FLAC_ASSERT(oggbs != NULL); - MA_DR_FLAC_ASSERT(offset >= 0); - if (origin == ma_dr_flac_seek_origin_start) { - if (!ma_dr_flac_oggbs__seek_physical(oggbs, (int)oggbs->firstBytePos, ma_dr_flac_seek_origin_start)) { - return MA_FALSE; - } - if (!ma_dr_flac_oggbs__goto_next_page(oggbs, ma_dr_flac_ogg_fail_on_crc_mismatch)) { - return MA_FALSE; - } - return ma_dr_flac__on_seek_ogg(pUserData, offset, ma_dr_flac_seek_origin_current); - } - MA_DR_FLAC_ASSERT(origin == ma_dr_flac_seek_origin_current); - while (bytesSeeked < offset) { - int bytesRemainingToSeek = offset - bytesSeeked; - MA_DR_FLAC_ASSERT(bytesRemainingToSeek >= 0); - if (oggbs->bytesRemainingInPage >= (size_t)bytesRemainingToSeek) { - bytesSeeked += bytesRemainingToSeek; - (void)bytesSeeked; - oggbs->bytesRemainingInPage -= bytesRemainingToSeek; - break; - } - if (oggbs->bytesRemainingInPage > 0) { - bytesSeeked += (int)oggbs->bytesRemainingInPage; - oggbs->bytesRemainingInPage = 0; - } - MA_DR_FLAC_ASSERT(bytesRemainingToSeek > 0); - if (!ma_dr_flac_oggbs__goto_next_page(oggbs, ma_dr_flac_ogg_fail_on_crc_mismatch)) { - return MA_FALSE; - } - } - return MA_TRUE; -} -static ma_bool32 ma_dr_flac_ogg__seek_to_pcm_frame(ma_dr_flac* pFlac, ma_uint64 pcmFrameIndex) -{ - ma_dr_flac_oggbs* oggbs = (ma_dr_flac_oggbs*)pFlac->_oggbs; - ma_uint64 originalBytePos; - ma_uint64 runningGranulePosition; - ma_uint64 runningFrameBytePos; - ma_uint64 runningPCMFrameCount; - MA_DR_FLAC_ASSERT(oggbs != NULL); - originalBytePos = oggbs->currentBytePos; - if (!ma_dr_flac__seek_to_byte(&pFlac->bs, pFlac->firstFLACFramePosInBytes)) { - return MA_FALSE; - } - oggbs->bytesRemainingInPage = 0; - runningGranulePosition = 0; - for (;;) { - if (!ma_dr_flac_oggbs__goto_next_page(oggbs, ma_dr_flac_ogg_recover_on_crc_mismatch)) { - ma_dr_flac_oggbs__seek_physical(oggbs, originalBytePos, ma_dr_flac_seek_origin_start); - return MA_FALSE; - } - runningFrameBytePos = oggbs->currentBytePos - ma_dr_flac_ogg__get_page_header_size(&oggbs->currentPageHeader) - oggbs->pageDataSize; - if (oggbs->currentPageHeader.granulePosition >= pcmFrameIndex) { - break; - } - if ((oggbs->currentPageHeader.headerType & 0x01) == 0) { - if (oggbs->currentPageHeader.segmentTable[0] >= 2) { - ma_uint8 firstBytesInPage[2]; - firstBytesInPage[0] = oggbs->pageData[0]; - firstBytesInPage[1] = oggbs->pageData[1]; - if ((firstBytesInPage[0] == 0xFF) && (firstBytesInPage[1] & 0xFC) == 0xF8) { - runningGranulePosition = oggbs->currentPageHeader.granulePosition; - } - continue; - } - } - } - if (!ma_dr_flac_oggbs__seek_physical(oggbs, runningFrameBytePos, ma_dr_flac_seek_origin_start)) { - return MA_FALSE; - } - if (!ma_dr_flac_oggbs__goto_next_page(oggbs, ma_dr_flac_ogg_recover_on_crc_mismatch)) { - return MA_FALSE; - } - runningPCMFrameCount = runningGranulePosition; - for (;;) { - ma_uint64 firstPCMFrameInFLACFrame = 0; - ma_uint64 lastPCMFrameInFLACFrame = 0; - ma_uint64 pcmFrameCountInThisFrame; - if (!ma_dr_flac__read_next_flac_frame_header(&pFlac->bs, pFlac->bitsPerSample, &pFlac->currentFLACFrame.header)) { - return MA_FALSE; - } - ma_dr_flac__get_pcm_frame_range_of_current_flac_frame(pFlac, &firstPCMFrameInFLACFrame, &lastPCMFrameInFLACFrame); - pcmFrameCountInThisFrame = (lastPCMFrameInFLACFrame - firstPCMFrameInFLACFrame) + 1; - if (pcmFrameIndex == pFlac->totalPCMFrameCount && (runningPCMFrameCount + pcmFrameCountInThisFrame) == pFlac->totalPCMFrameCount) { - ma_result result = ma_dr_flac__decode_flac_frame(pFlac); - if (result == MA_SUCCESS) { - pFlac->currentPCMFrame = pcmFrameIndex; - pFlac->currentFLACFrame.pcmFramesRemaining = 0; - return MA_TRUE; - } else { - return MA_FALSE; - } - } - if (pcmFrameIndex < (runningPCMFrameCount + pcmFrameCountInThisFrame)) { - ma_result result = ma_dr_flac__decode_flac_frame(pFlac); - if (result == MA_SUCCESS) { - ma_uint64 pcmFramesToDecode = (size_t)(pcmFrameIndex - runningPCMFrameCount); - if (pcmFramesToDecode == 0) { - return MA_TRUE; - } - pFlac->currentPCMFrame = runningPCMFrameCount; - return ma_dr_flac__seek_forward_by_pcm_frames(pFlac, pcmFramesToDecode) == pcmFramesToDecode; - } else { - if (result == MA_CRC_MISMATCH) { - continue; - } else { - return MA_FALSE; - } - } - } else { - ma_result result = ma_dr_flac__seek_to_next_flac_frame(pFlac); - if (result == MA_SUCCESS) { - runningPCMFrameCount += pcmFrameCountInThisFrame; - } else { - if (result == MA_CRC_MISMATCH) { - continue; - } else { - return MA_FALSE; - } - } - } - } -} -static ma_bool32 ma_dr_flac__init_private__ogg(ma_dr_flac_init_info* pInit, ma_dr_flac_read_proc onRead, ma_dr_flac_seek_proc onSeek, ma_dr_flac_meta_proc onMeta, void* pUserData, void* pUserDataMD, ma_bool32 relaxed) -{ - ma_dr_flac_ogg_page_header header; - ma_uint32 crc32 = MA_DR_FLAC_OGG_CAPTURE_PATTERN_CRC32; - ma_uint32 bytesRead = 0; - (void)relaxed; - pInit->container = ma_dr_flac_container_ogg; - pInit->oggFirstBytePos = 0; - if (ma_dr_flac_ogg__read_page_header_after_capture_pattern(onRead, pUserData, &header, &bytesRead, &crc32) != MA_SUCCESS) { - return MA_FALSE; - } - pInit->runningFilePos += bytesRead; - for (;;) { - int pageBodySize; - if ((header.headerType & 0x02) == 0) { - return MA_FALSE; - } - pageBodySize = ma_dr_flac_ogg__get_page_body_size(&header); - if (pageBodySize == 51) { - ma_uint32 bytesRemainingInPage = pageBodySize; - ma_uint8 packetType; - if (onRead(pUserData, &packetType, 1) != 1) { - return MA_FALSE; - } - bytesRemainingInPage -= 1; - if (packetType == 0x7F) { - ma_uint8 sig[4]; - if (onRead(pUserData, sig, 4) != 4) { - return MA_FALSE; - } - bytesRemainingInPage -= 4; - if (sig[0] == 'F' && sig[1] == 'L' && sig[2] == 'A' && sig[3] == 'C') { - ma_uint8 mappingVersion[2]; - if (onRead(pUserData, mappingVersion, 2) != 2) { - return MA_FALSE; - } - if (mappingVersion[0] != 1) { - return MA_FALSE; - } - if (!onSeek(pUserData, 2, ma_dr_flac_seek_origin_current)) { - return MA_FALSE; - } - if (onRead(pUserData, sig, 4) != 4) { - return MA_FALSE; - } - if (sig[0] == 'f' && sig[1] == 'L' && sig[2] == 'a' && sig[3] == 'C') { - ma_dr_flac_streaminfo streaminfo; - ma_uint8 isLastBlock; - ma_uint8 blockType; - ma_uint32 blockSize; - if (!ma_dr_flac__read_and_decode_block_header(onRead, pUserData, &isLastBlock, &blockType, &blockSize)) { - return MA_FALSE; - } - if (blockType != MA_DR_FLAC_METADATA_BLOCK_TYPE_STREAMINFO || blockSize != 34) { - return MA_FALSE; - } - if (ma_dr_flac__read_streaminfo(onRead, pUserData, &streaminfo)) { - pInit->hasStreamInfoBlock = MA_TRUE; - pInit->sampleRate = streaminfo.sampleRate; - pInit->channels = streaminfo.channels; - pInit->bitsPerSample = streaminfo.bitsPerSample; - pInit->totalPCMFrameCount = streaminfo.totalPCMFrameCount; - pInit->maxBlockSizeInPCMFrames = streaminfo.maxBlockSizeInPCMFrames; - pInit->hasMetadataBlocks = !isLastBlock; - if (onMeta) { - ma_dr_flac_metadata metadata; - metadata.type = MA_DR_FLAC_METADATA_BLOCK_TYPE_STREAMINFO; - metadata.pRawData = NULL; - metadata.rawDataSize = 0; - metadata.data.streaminfo = streaminfo; - onMeta(pUserDataMD, &metadata); - } - pInit->runningFilePos += pageBodySize; - pInit->oggFirstBytePos = pInit->runningFilePos - 79; - pInit->oggSerial = header.serialNumber; - pInit->oggBosHeader = header; - break; - } else { - return MA_FALSE; - } - } else { - return MA_FALSE; - } - } else { - if (!onSeek(pUserData, bytesRemainingInPage, ma_dr_flac_seek_origin_current)) { - return MA_FALSE; - } - } - } else { - if (!onSeek(pUserData, bytesRemainingInPage, ma_dr_flac_seek_origin_current)) { - return MA_FALSE; - } - } - } else { - if (!onSeek(pUserData, pageBodySize, ma_dr_flac_seek_origin_current)) { - return MA_FALSE; - } - } - pInit->runningFilePos += pageBodySize; - if (ma_dr_flac_ogg__read_page_header(onRead, pUserData, &header, &bytesRead, &crc32) != MA_SUCCESS) { - return MA_FALSE; - } - pInit->runningFilePos += bytesRead; - } - pInit->hasMetadataBlocks = MA_TRUE; - return MA_TRUE; -} -#endif -static ma_bool32 ma_dr_flac__init_private(ma_dr_flac_init_info* pInit, ma_dr_flac_read_proc onRead, ma_dr_flac_seek_proc onSeek, ma_dr_flac_meta_proc onMeta, ma_dr_flac_container container, void* pUserData, void* pUserDataMD) -{ - ma_bool32 relaxed; - ma_uint8 id[4]; - if (pInit == NULL || onRead == NULL || onSeek == NULL) { - return MA_FALSE; - } - MA_DR_FLAC_ZERO_MEMORY(pInit, sizeof(*pInit)); - pInit->onRead = onRead; - pInit->onSeek = onSeek; - pInit->onMeta = onMeta; - pInit->container = container; - pInit->pUserData = pUserData; - pInit->pUserDataMD = pUserDataMD; - pInit->bs.onRead = onRead; - pInit->bs.onSeek = onSeek; - pInit->bs.pUserData = pUserData; - ma_dr_flac__reset_cache(&pInit->bs); - relaxed = container != ma_dr_flac_container_unknown; - for (;;) { - if (onRead(pUserData, id, 4) != 4) { - return MA_FALSE; - } - pInit->runningFilePos += 4; - if (id[0] == 'I' && id[1] == 'D' && id[2] == '3') { - ma_uint8 header[6]; - ma_uint8 flags; - ma_uint32 headerSize; - if (onRead(pUserData, header, 6) != 6) { - return MA_FALSE; - } - pInit->runningFilePos += 6; - flags = header[1]; - MA_DR_FLAC_COPY_MEMORY(&headerSize, header+2, 4); - headerSize = ma_dr_flac__unsynchsafe_32(ma_dr_flac__be2host_32(headerSize)); - if (flags & 0x10) { - headerSize += 10; - } - if (!onSeek(pUserData, headerSize, ma_dr_flac_seek_origin_current)) { - return MA_FALSE; - } - pInit->runningFilePos += headerSize; - } else { - break; - } - } - if (id[0] == 'f' && id[1] == 'L' && id[2] == 'a' && id[3] == 'C') { - return ma_dr_flac__init_private__native(pInit, onRead, onSeek, onMeta, pUserData, pUserDataMD, relaxed); - } -#ifndef MA_DR_FLAC_NO_OGG - if (id[0] == 'O' && id[1] == 'g' && id[2] == 'g' && id[3] == 'S') { - return ma_dr_flac__init_private__ogg(pInit, onRead, onSeek, onMeta, pUserData, pUserDataMD, relaxed); - } -#endif - if (relaxed) { - if (container == ma_dr_flac_container_native) { - return ma_dr_flac__init_private__native(pInit, onRead, onSeek, onMeta, pUserData, pUserDataMD, relaxed); - } -#ifndef MA_DR_FLAC_NO_OGG - if (container == ma_dr_flac_container_ogg) { - return ma_dr_flac__init_private__ogg(pInit, onRead, onSeek, onMeta, pUserData, pUserDataMD, relaxed); - } -#endif - } - return MA_FALSE; -} -static void ma_dr_flac__init_from_info(ma_dr_flac* pFlac, const ma_dr_flac_init_info* pInit) -{ - MA_DR_FLAC_ASSERT(pFlac != NULL); - MA_DR_FLAC_ASSERT(pInit != NULL); - MA_DR_FLAC_ZERO_MEMORY(pFlac, sizeof(*pFlac)); - pFlac->bs = pInit->bs; - pFlac->onMeta = pInit->onMeta; - pFlac->pUserDataMD = pInit->pUserDataMD; - pFlac->maxBlockSizeInPCMFrames = pInit->maxBlockSizeInPCMFrames; - pFlac->sampleRate = pInit->sampleRate; - pFlac->channels = (ma_uint8)pInit->channels; - pFlac->bitsPerSample = (ma_uint8)pInit->bitsPerSample; - pFlac->totalPCMFrameCount = pInit->totalPCMFrameCount; - pFlac->container = pInit->container; -} -static ma_dr_flac* ma_dr_flac_open_with_metadata_private(ma_dr_flac_read_proc onRead, ma_dr_flac_seek_proc onSeek, ma_dr_flac_meta_proc onMeta, ma_dr_flac_container container, void* pUserData, void* pUserDataMD, const ma_allocation_callbacks* pAllocationCallbacks) -{ - ma_dr_flac_init_info init; - ma_uint32 allocationSize; - ma_uint32 wholeSIMDVectorCountPerChannel; - ma_uint32 decodedSamplesAllocationSize; -#ifndef MA_DR_FLAC_NO_OGG - ma_dr_flac_oggbs* pOggbs = NULL; -#endif - ma_uint64 firstFramePos; - ma_uint64 seektablePos; - ma_uint32 seekpointCount; - ma_allocation_callbacks allocationCallbacks; - ma_dr_flac* pFlac; - ma_dr_flac__init_cpu_caps(); - if (!ma_dr_flac__init_private(&init, onRead, onSeek, onMeta, container, pUserData, pUserDataMD)) { - return NULL; - } - if (pAllocationCallbacks != NULL) { - allocationCallbacks = *pAllocationCallbacks; - if (allocationCallbacks.onFree == NULL || (allocationCallbacks.onMalloc == NULL && allocationCallbacks.onRealloc == NULL)) { - return NULL; - } - } else { - allocationCallbacks.pUserData = NULL; - allocationCallbacks.onMalloc = ma_dr_flac__malloc_default; - allocationCallbacks.onRealloc = ma_dr_flac__realloc_default; - allocationCallbacks.onFree = ma_dr_flac__free_default; - } - allocationSize = sizeof(ma_dr_flac); - if ((init.maxBlockSizeInPCMFrames % (MA_DR_FLAC_MAX_SIMD_VECTOR_SIZE / sizeof(ma_int32))) == 0) { - wholeSIMDVectorCountPerChannel = (init.maxBlockSizeInPCMFrames / (MA_DR_FLAC_MAX_SIMD_VECTOR_SIZE / sizeof(ma_int32))); - } else { - wholeSIMDVectorCountPerChannel = (init.maxBlockSizeInPCMFrames / (MA_DR_FLAC_MAX_SIMD_VECTOR_SIZE / sizeof(ma_int32))) + 1; - } - decodedSamplesAllocationSize = wholeSIMDVectorCountPerChannel * MA_DR_FLAC_MAX_SIMD_VECTOR_SIZE * init.channels; - allocationSize += decodedSamplesAllocationSize; - allocationSize += MA_DR_FLAC_MAX_SIMD_VECTOR_SIZE; -#ifndef MA_DR_FLAC_NO_OGG - if (init.container == ma_dr_flac_container_ogg) { - allocationSize += sizeof(ma_dr_flac_oggbs); - pOggbs = (ma_dr_flac_oggbs*)ma_dr_flac__malloc_from_callbacks(sizeof(*pOggbs), &allocationCallbacks); - if (pOggbs == NULL) { - return NULL; - } - MA_DR_FLAC_ZERO_MEMORY(pOggbs, sizeof(*pOggbs)); - pOggbs->onRead = onRead; - pOggbs->onSeek = onSeek; - pOggbs->pUserData = pUserData; - pOggbs->currentBytePos = init.oggFirstBytePos; - pOggbs->firstBytePos = init.oggFirstBytePos; - pOggbs->serialNumber = init.oggSerial; - pOggbs->bosPageHeader = init.oggBosHeader; - pOggbs->bytesRemainingInPage = 0; - } -#endif - firstFramePos = 42; - seektablePos = 0; - seekpointCount = 0; - if (init.hasMetadataBlocks) { - ma_dr_flac_read_proc onReadOverride = onRead; - ma_dr_flac_seek_proc onSeekOverride = onSeek; - void* pUserDataOverride = pUserData; -#ifndef MA_DR_FLAC_NO_OGG - if (init.container == ma_dr_flac_container_ogg) { - onReadOverride = ma_dr_flac__on_read_ogg; - onSeekOverride = ma_dr_flac__on_seek_ogg; - pUserDataOverride = (void*)pOggbs; - } -#endif - if (!ma_dr_flac__read_and_decode_metadata(onReadOverride, onSeekOverride, onMeta, pUserDataOverride, pUserDataMD, &firstFramePos, &seektablePos, &seekpointCount, &allocationCallbacks)) { - #ifndef MA_DR_FLAC_NO_OGG - ma_dr_flac__free_from_callbacks(pOggbs, &allocationCallbacks); - #endif - return NULL; - } - allocationSize += seekpointCount * sizeof(ma_dr_flac_seekpoint); - } - pFlac = (ma_dr_flac*)ma_dr_flac__malloc_from_callbacks(allocationSize, &allocationCallbacks); - if (pFlac == NULL) { - #ifndef MA_DR_FLAC_NO_OGG - ma_dr_flac__free_from_callbacks(pOggbs, &allocationCallbacks); - #endif - return NULL; - } - ma_dr_flac__init_from_info(pFlac, &init); - pFlac->allocationCallbacks = allocationCallbacks; - pFlac->pDecodedSamples = (ma_int32*)ma_dr_flac_align((size_t)pFlac->pExtraData, MA_DR_FLAC_MAX_SIMD_VECTOR_SIZE); -#ifndef MA_DR_FLAC_NO_OGG - if (init.container == ma_dr_flac_container_ogg) { - ma_dr_flac_oggbs* pInternalOggbs = (ma_dr_flac_oggbs*)((ma_uint8*)pFlac->pDecodedSamples + decodedSamplesAllocationSize + (seekpointCount * sizeof(ma_dr_flac_seekpoint))); - MA_DR_FLAC_COPY_MEMORY(pInternalOggbs, pOggbs, sizeof(*pOggbs)); - ma_dr_flac__free_from_callbacks(pOggbs, &allocationCallbacks); - pOggbs = NULL; - pFlac->bs.onRead = ma_dr_flac__on_read_ogg; - pFlac->bs.onSeek = ma_dr_flac__on_seek_ogg; - pFlac->bs.pUserData = (void*)pInternalOggbs; - pFlac->_oggbs = (void*)pInternalOggbs; - } -#endif - pFlac->firstFLACFramePosInBytes = firstFramePos; -#ifndef MA_DR_FLAC_NO_OGG - if (init.container == ma_dr_flac_container_ogg) - { - pFlac->pSeekpoints = NULL; - pFlac->seekpointCount = 0; - } - else -#endif - { - if (seektablePos != 0) { - pFlac->seekpointCount = seekpointCount; - pFlac->pSeekpoints = (ma_dr_flac_seekpoint*)((ma_uint8*)pFlac->pDecodedSamples + decodedSamplesAllocationSize); - MA_DR_FLAC_ASSERT(pFlac->bs.onSeek != NULL); - MA_DR_FLAC_ASSERT(pFlac->bs.onRead != NULL); - if (pFlac->bs.onSeek(pFlac->bs.pUserData, (int)seektablePos, ma_dr_flac_seek_origin_start)) { - ma_uint32 iSeekpoint; - for (iSeekpoint = 0; iSeekpoint < seekpointCount; iSeekpoint += 1) { - if (pFlac->bs.onRead(pFlac->bs.pUserData, pFlac->pSeekpoints + iSeekpoint, MA_DR_FLAC_SEEKPOINT_SIZE_IN_BYTES) == MA_DR_FLAC_SEEKPOINT_SIZE_IN_BYTES) { - pFlac->pSeekpoints[iSeekpoint].firstPCMFrame = ma_dr_flac__be2host_64(pFlac->pSeekpoints[iSeekpoint].firstPCMFrame); - pFlac->pSeekpoints[iSeekpoint].flacFrameOffset = ma_dr_flac__be2host_64(pFlac->pSeekpoints[iSeekpoint].flacFrameOffset); - pFlac->pSeekpoints[iSeekpoint].pcmFrameCount = ma_dr_flac__be2host_16(pFlac->pSeekpoints[iSeekpoint].pcmFrameCount); - } else { - pFlac->pSeekpoints = NULL; - pFlac->seekpointCount = 0; - break; - } - } - if (!pFlac->bs.onSeek(pFlac->bs.pUserData, (int)pFlac->firstFLACFramePosInBytes, ma_dr_flac_seek_origin_start)) { - ma_dr_flac__free_from_callbacks(pFlac, &allocationCallbacks); - return NULL; - } - } else { - pFlac->pSeekpoints = NULL; - pFlac->seekpointCount = 0; - } - } - } - if (!init.hasStreamInfoBlock) { - pFlac->currentFLACFrame.header = init.firstFrameHeader; - for (;;) { - ma_result result = ma_dr_flac__decode_flac_frame(pFlac); - if (result == MA_SUCCESS) { - break; - } else { - if (result == MA_CRC_MISMATCH) { - if (!ma_dr_flac__read_next_flac_frame_header(&pFlac->bs, pFlac->bitsPerSample, &pFlac->currentFLACFrame.header)) { - ma_dr_flac__free_from_callbacks(pFlac, &allocationCallbacks); - return NULL; - } - continue; - } else { - ma_dr_flac__free_from_callbacks(pFlac, &allocationCallbacks); - return NULL; - } - } - } - } - return pFlac; -} -#ifndef MA_DR_FLAC_NO_STDIO -#include -#ifndef MA_DR_FLAC_NO_WCHAR -#include -#endif -static size_t ma_dr_flac__on_read_stdio(void* pUserData, void* bufferOut, size_t bytesToRead) -{ - return fread(bufferOut, 1, bytesToRead, (FILE*)pUserData); -} -static ma_bool32 ma_dr_flac__on_seek_stdio(void* pUserData, int offset, ma_dr_flac_seek_origin origin) -{ - MA_DR_FLAC_ASSERT(offset >= 0); - return fseek((FILE*)pUserData, offset, (origin == ma_dr_flac_seek_origin_current) ? SEEK_CUR : SEEK_SET) == 0; -} -MA_API ma_dr_flac* ma_dr_flac_open_file(const char* pFileName, const ma_allocation_callbacks* pAllocationCallbacks) -{ - ma_dr_flac* pFlac; - FILE* pFile; - if (ma_fopen(&pFile, pFileName, "rb") != MA_SUCCESS) { - return NULL; - } - pFlac = ma_dr_flac_open(ma_dr_flac__on_read_stdio, ma_dr_flac__on_seek_stdio, (void*)pFile, pAllocationCallbacks); - if (pFlac == NULL) { - fclose(pFile); - return NULL; - } - return pFlac; -} -#ifndef MA_DR_FLAC_NO_WCHAR -MA_API ma_dr_flac* ma_dr_flac_open_file_w(const wchar_t* pFileName, const ma_allocation_callbacks* pAllocationCallbacks) -{ - ma_dr_flac* pFlac; - FILE* pFile; - if (ma_wfopen(&pFile, pFileName, L"rb", pAllocationCallbacks) != MA_SUCCESS) { - return NULL; - } - pFlac = ma_dr_flac_open(ma_dr_flac__on_read_stdio, ma_dr_flac__on_seek_stdio, (void*)pFile, pAllocationCallbacks); - if (pFlac == NULL) { - fclose(pFile); - return NULL; - } - return pFlac; -} -#endif -MA_API ma_dr_flac* ma_dr_flac_open_file_with_metadata(const char* pFileName, ma_dr_flac_meta_proc onMeta, void* pUserData, const ma_allocation_callbacks* pAllocationCallbacks) -{ - ma_dr_flac* pFlac; - FILE* pFile; - if (ma_fopen(&pFile, pFileName, "rb") != MA_SUCCESS) { - return NULL; - } - pFlac = ma_dr_flac_open_with_metadata_private(ma_dr_flac__on_read_stdio, ma_dr_flac__on_seek_stdio, onMeta, ma_dr_flac_container_unknown, (void*)pFile, pUserData, pAllocationCallbacks); - if (pFlac == NULL) { - fclose(pFile); - return pFlac; - } - return pFlac; -} -#ifndef MA_DR_FLAC_NO_WCHAR -MA_API ma_dr_flac* ma_dr_flac_open_file_with_metadata_w(const wchar_t* pFileName, ma_dr_flac_meta_proc onMeta, void* pUserData, const ma_allocation_callbacks* pAllocationCallbacks) -{ - ma_dr_flac* pFlac; - FILE* pFile; - if (ma_wfopen(&pFile, pFileName, L"rb", pAllocationCallbacks) != MA_SUCCESS) { - return NULL; - } - pFlac = ma_dr_flac_open_with_metadata_private(ma_dr_flac__on_read_stdio, ma_dr_flac__on_seek_stdio, onMeta, ma_dr_flac_container_unknown, (void*)pFile, pUserData, pAllocationCallbacks); - if (pFlac == NULL) { - fclose(pFile); - return pFlac; - } - return pFlac; -} -#endif -#endif -static size_t ma_dr_flac__on_read_memory(void* pUserData, void* bufferOut, size_t bytesToRead) -{ - ma_dr_flac__memory_stream* memoryStream = (ma_dr_flac__memory_stream*)pUserData; - size_t bytesRemaining; - MA_DR_FLAC_ASSERT(memoryStream != NULL); - MA_DR_FLAC_ASSERT(memoryStream->dataSize >= memoryStream->currentReadPos); - bytesRemaining = memoryStream->dataSize - memoryStream->currentReadPos; - if (bytesToRead > bytesRemaining) { - bytesToRead = bytesRemaining; - } - if (bytesToRead > 0) { - MA_DR_FLAC_COPY_MEMORY(bufferOut, memoryStream->data + memoryStream->currentReadPos, bytesToRead); - memoryStream->currentReadPos += bytesToRead; - } - return bytesToRead; -} -static ma_bool32 ma_dr_flac__on_seek_memory(void* pUserData, int offset, ma_dr_flac_seek_origin origin) -{ - ma_dr_flac__memory_stream* memoryStream = (ma_dr_flac__memory_stream*)pUserData; - MA_DR_FLAC_ASSERT(memoryStream != NULL); - MA_DR_FLAC_ASSERT(offset >= 0); - if (offset > (ma_int64)memoryStream->dataSize) { - return MA_FALSE; - } - if (origin == ma_dr_flac_seek_origin_current) { - if (memoryStream->currentReadPos + offset <= memoryStream->dataSize) { - memoryStream->currentReadPos += offset; - } else { - return MA_FALSE; - } - } else { - if ((ma_uint32)offset <= memoryStream->dataSize) { - memoryStream->currentReadPos = offset; - } else { - return MA_FALSE; - } - } - return MA_TRUE; -} -MA_API ma_dr_flac* ma_dr_flac_open_memory(const void* pData, size_t dataSize, const ma_allocation_callbacks* pAllocationCallbacks) -{ - ma_dr_flac__memory_stream memoryStream; - ma_dr_flac* pFlac; - memoryStream.data = (const ma_uint8*)pData; - memoryStream.dataSize = dataSize; - memoryStream.currentReadPos = 0; - pFlac = ma_dr_flac_open(ma_dr_flac__on_read_memory, ma_dr_flac__on_seek_memory, &memoryStream, pAllocationCallbacks); - if (pFlac == NULL) { - return NULL; - } - pFlac->memoryStream = memoryStream; -#ifndef MA_DR_FLAC_NO_OGG - if (pFlac->container == ma_dr_flac_container_ogg) - { - ma_dr_flac_oggbs* oggbs = (ma_dr_flac_oggbs*)pFlac->_oggbs; - oggbs->pUserData = &pFlac->memoryStream; - } - else -#endif - { - pFlac->bs.pUserData = &pFlac->memoryStream; - } - return pFlac; -} -MA_API ma_dr_flac* ma_dr_flac_open_memory_with_metadata(const void* pData, size_t dataSize, ma_dr_flac_meta_proc onMeta, void* pUserData, const ma_allocation_callbacks* pAllocationCallbacks) -{ - ma_dr_flac__memory_stream memoryStream; - ma_dr_flac* pFlac; - memoryStream.data = (const ma_uint8*)pData; - memoryStream.dataSize = dataSize; - memoryStream.currentReadPos = 0; - pFlac = ma_dr_flac_open_with_metadata_private(ma_dr_flac__on_read_memory, ma_dr_flac__on_seek_memory, onMeta, ma_dr_flac_container_unknown, &memoryStream, pUserData, pAllocationCallbacks); - if (pFlac == NULL) { - return NULL; - } - pFlac->memoryStream = memoryStream; -#ifndef MA_DR_FLAC_NO_OGG - if (pFlac->container == ma_dr_flac_container_ogg) - { - ma_dr_flac_oggbs* oggbs = (ma_dr_flac_oggbs*)pFlac->_oggbs; - oggbs->pUserData = &pFlac->memoryStream; - } - else -#endif - { - pFlac->bs.pUserData = &pFlac->memoryStream; - } - return pFlac; -} -MA_API ma_dr_flac* ma_dr_flac_open(ma_dr_flac_read_proc onRead, ma_dr_flac_seek_proc onSeek, void* pUserData, const ma_allocation_callbacks* pAllocationCallbacks) -{ - return ma_dr_flac_open_with_metadata_private(onRead, onSeek, NULL, ma_dr_flac_container_unknown, pUserData, pUserData, pAllocationCallbacks); -} -MA_API ma_dr_flac* ma_dr_flac_open_relaxed(ma_dr_flac_read_proc onRead, ma_dr_flac_seek_proc onSeek, ma_dr_flac_container container, void* pUserData, const ma_allocation_callbacks* pAllocationCallbacks) -{ - return ma_dr_flac_open_with_metadata_private(onRead, onSeek, NULL, container, pUserData, pUserData, pAllocationCallbacks); -} -MA_API ma_dr_flac* ma_dr_flac_open_with_metadata(ma_dr_flac_read_proc onRead, ma_dr_flac_seek_proc onSeek, ma_dr_flac_meta_proc onMeta, void* pUserData, const ma_allocation_callbacks* pAllocationCallbacks) -{ - return ma_dr_flac_open_with_metadata_private(onRead, onSeek, onMeta, ma_dr_flac_container_unknown, pUserData, pUserData, pAllocationCallbacks); -} -MA_API ma_dr_flac* ma_dr_flac_open_with_metadata_relaxed(ma_dr_flac_read_proc onRead, ma_dr_flac_seek_proc onSeek, ma_dr_flac_meta_proc onMeta, ma_dr_flac_container container, void* pUserData, const ma_allocation_callbacks* pAllocationCallbacks) -{ - return ma_dr_flac_open_with_metadata_private(onRead, onSeek, onMeta, container, pUserData, pUserData, pAllocationCallbacks); -} -MA_API void ma_dr_flac_close(ma_dr_flac* pFlac) -{ - if (pFlac == NULL) { - return; - } -#ifndef MA_DR_FLAC_NO_STDIO - if (pFlac->bs.onRead == ma_dr_flac__on_read_stdio) { - fclose((FILE*)pFlac->bs.pUserData); - } -#ifndef MA_DR_FLAC_NO_OGG - if (pFlac->container == ma_dr_flac_container_ogg) { - ma_dr_flac_oggbs* oggbs = (ma_dr_flac_oggbs*)pFlac->_oggbs; - MA_DR_FLAC_ASSERT(pFlac->bs.onRead == ma_dr_flac__on_read_ogg); - if (oggbs->onRead == ma_dr_flac__on_read_stdio) { - fclose((FILE*)oggbs->pUserData); - } - } -#endif -#endif - ma_dr_flac__free_from_callbacks(pFlac, &pFlac->allocationCallbacks); -} -#if 0 -static MA_INLINE void ma_dr_flac_read_pcm_frames_s32__decode_left_side__reference(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, ma_int32* pOutputSamples) -{ - ma_uint64 i; - for (i = 0; i < frameCount; ++i) { - ma_uint32 left = (ma_uint32)pInputSamples0[i] << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample); - ma_uint32 side = (ma_uint32)pInputSamples1[i] << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample); - ma_uint32 right = left - side; - pOutputSamples[i*2+0] = (ma_int32)left; - pOutputSamples[i*2+1] = (ma_int32)right; - } -} -#endif -static MA_INLINE void ma_dr_flac_read_pcm_frames_s32__decode_left_side__scalar(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, ma_int32* pOutputSamples) -{ - ma_uint64 i; - ma_uint64 frameCount4 = frameCount >> 2; - const ma_uint32* pInputSamples0U32 = (const ma_uint32*)pInputSamples0; - const ma_uint32* pInputSamples1U32 = (const ma_uint32*)pInputSamples1; - ma_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; - ma_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; - for (i = 0; i < frameCount4; ++i) { - ma_uint32 left0 = pInputSamples0U32[i*4+0] << shift0; - ma_uint32 left1 = pInputSamples0U32[i*4+1] << shift0; - ma_uint32 left2 = pInputSamples0U32[i*4+2] << shift0; - ma_uint32 left3 = pInputSamples0U32[i*4+3] << shift0; - ma_uint32 side0 = pInputSamples1U32[i*4+0] << shift1; - ma_uint32 side1 = pInputSamples1U32[i*4+1] << shift1; - ma_uint32 side2 = pInputSamples1U32[i*4+2] << shift1; - ma_uint32 side3 = pInputSamples1U32[i*4+3] << shift1; - ma_uint32 right0 = left0 - side0; - ma_uint32 right1 = left1 - side1; - ma_uint32 right2 = left2 - side2; - ma_uint32 right3 = left3 - side3; - pOutputSamples[i*8+0] = (ma_int32)left0; - pOutputSamples[i*8+1] = (ma_int32)right0; - pOutputSamples[i*8+2] = (ma_int32)left1; - pOutputSamples[i*8+3] = (ma_int32)right1; - pOutputSamples[i*8+4] = (ma_int32)left2; - pOutputSamples[i*8+5] = (ma_int32)right2; - pOutputSamples[i*8+6] = (ma_int32)left3; - pOutputSamples[i*8+7] = (ma_int32)right3; - } - for (i = (frameCount4 << 2); i < frameCount; ++i) { - ma_uint32 left = pInputSamples0U32[i] << shift0; - ma_uint32 side = pInputSamples1U32[i] << shift1; - ma_uint32 right = left - side; - pOutputSamples[i*2+0] = (ma_int32)left; - pOutputSamples[i*2+1] = (ma_int32)right; - } -} -#if defined(MA_DR_FLAC_SUPPORT_SSE2) -static MA_INLINE void ma_dr_flac_read_pcm_frames_s32__decode_left_side__sse2(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, ma_int32* pOutputSamples) -{ - ma_uint64 i; - ma_uint64 frameCount4 = frameCount >> 2; - const ma_uint32* pInputSamples0U32 = (const ma_uint32*)pInputSamples0; - const ma_uint32* pInputSamples1U32 = (const ma_uint32*)pInputSamples1; - ma_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; - ma_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; - MA_DR_FLAC_ASSERT(pFlac->bitsPerSample <= 24); - for (i = 0; i < frameCount4; ++i) { - __m128i left = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples0 + i), shift0); - __m128i side = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples1 + i), shift1); - __m128i right = _mm_sub_epi32(left, side); - _mm_storeu_si128((__m128i*)(pOutputSamples + i*8 + 0), _mm_unpacklo_epi32(left, right)); - _mm_storeu_si128((__m128i*)(pOutputSamples + i*8 + 4), _mm_unpackhi_epi32(left, right)); - } - for (i = (frameCount4 << 2); i < frameCount; ++i) { - ma_uint32 left = pInputSamples0U32[i] << shift0; - ma_uint32 side = pInputSamples1U32[i] << shift1; - ma_uint32 right = left - side; - pOutputSamples[i*2+0] = (ma_int32)left; - pOutputSamples[i*2+1] = (ma_int32)right; - } -} -#endif -#if defined(MA_DR_FLAC_SUPPORT_NEON) -static MA_INLINE void ma_dr_flac_read_pcm_frames_s32__decode_left_side__neon(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, ma_int32* pOutputSamples) -{ - ma_uint64 i; - ma_uint64 frameCount4 = frameCount >> 2; - const ma_uint32* pInputSamples0U32 = (const ma_uint32*)pInputSamples0; - const ma_uint32* pInputSamples1U32 = (const ma_uint32*)pInputSamples1; - ma_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; - ma_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; - int32x4_t shift0_4; - int32x4_t shift1_4; - MA_DR_FLAC_ASSERT(pFlac->bitsPerSample <= 24); - shift0_4 = vdupq_n_s32(shift0); - shift1_4 = vdupq_n_s32(shift1); - for (i = 0; i < frameCount4; ++i) { - uint32x4_t left; - uint32x4_t side; - uint32x4_t right; - left = vshlq_u32(vld1q_u32(pInputSamples0U32 + i*4), shift0_4); - side = vshlq_u32(vld1q_u32(pInputSamples1U32 + i*4), shift1_4); - right = vsubq_u32(left, side); - ma_dr_flac__vst2q_u32((ma_uint32*)pOutputSamples + i*8, vzipq_u32(left, right)); - } - for (i = (frameCount4 << 2); i < frameCount; ++i) { - ma_uint32 left = pInputSamples0U32[i] << shift0; - ma_uint32 side = pInputSamples1U32[i] << shift1; - ma_uint32 right = left - side; - pOutputSamples[i*2+0] = (ma_int32)left; - pOutputSamples[i*2+1] = (ma_int32)right; - } -} -#endif -static MA_INLINE void ma_dr_flac_read_pcm_frames_s32__decode_left_side(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, ma_int32* pOutputSamples) -{ -#if defined(MA_DR_FLAC_SUPPORT_SSE2) - if (ma_dr_flac__gIsSSE2Supported && pFlac->bitsPerSample <= 24) { - ma_dr_flac_read_pcm_frames_s32__decode_left_side__sse2(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); - } else -#elif defined(MA_DR_FLAC_SUPPORT_NEON) - if (ma_dr_flac__gIsNEONSupported && pFlac->bitsPerSample <= 24) { - ma_dr_flac_read_pcm_frames_s32__decode_left_side__neon(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); - } else -#endif - { -#if 0 - ma_dr_flac_read_pcm_frames_s32__decode_left_side__reference(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); -#else - ma_dr_flac_read_pcm_frames_s32__decode_left_side__scalar(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); -#endif - } -} -#if 0 -static MA_INLINE void ma_dr_flac_read_pcm_frames_s32__decode_right_side__reference(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, ma_int32* pOutputSamples) -{ - ma_uint64 i; - for (i = 0; i < frameCount; ++i) { - ma_uint32 side = (ma_uint32)pInputSamples0[i] << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample); - ma_uint32 right = (ma_uint32)pInputSamples1[i] << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample); - ma_uint32 left = right + side; - pOutputSamples[i*2+0] = (ma_int32)left; - pOutputSamples[i*2+1] = (ma_int32)right; - } -} -#endif -static MA_INLINE void ma_dr_flac_read_pcm_frames_s32__decode_right_side__scalar(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, ma_int32* pOutputSamples) -{ - ma_uint64 i; - ma_uint64 frameCount4 = frameCount >> 2; - const ma_uint32* pInputSamples0U32 = (const ma_uint32*)pInputSamples0; - const ma_uint32* pInputSamples1U32 = (const ma_uint32*)pInputSamples1; - ma_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; - ma_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; - for (i = 0; i < frameCount4; ++i) { - ma_uint32 side0 = pInputSamples0U32[i*4+0] << shift0; - ma_uint32 side1 = pInputSamples0U32[i*4+1] << shift0; - ma_uint32 side2 = pInputSamples0U32[i*4+2] << shift0; - ma_uint32 side3 = pInputSamples0U32[i*4+3] << shift0; - ma_uint32 right0 = pInputSamples1U32[i*4+0] << shift1; - ma_uint32 right1 = pInputSamples1U32[i*4+1] << shift1; - ma_uint32 right2 = pInputSamples1U32[i*4+2] << shift1; - ma_uint32 right3 = pInputSamples1U32[i*4+3] << shift1; - ma_uint32 left0 = right0 + side0; - ma_uint32 left1 = right1 + side1; - ma_uint32 left2 = right2 + side2; - ma_uint32 left3 = right3 + side3; - pOutputSamples[i*8+0] = (ma_int32)left0; - pOutputSamples[i*8+1] = (ma_int32)right0; - pOutputSamples[i*8+2] = (ma_int32)left1; - pOutputSamples[i*8+3] = (ma_int32)right1; - pOutputSamples[i*8+4] = (ma_int32)left2; - pOutputSamples[i*8+5] = (ma_int32)right2; - pOutputSamples[i*8+6] = (ma_int32)left3; - pOutputSamples[i*8+7] = (ma_int32)right3; - } - for (i = (frameCount4 << 2); i < frameCount; ++i) { - ma_uint32 side = pInputSamples0U32[i] << shift0; - ma_uint32 right = pInputSamples1U32[i] << shift1; - ma_uint32 left = right + side; - pOutputSamples[i*2+0] = (ma_int32)left; - pOutputSamples[i*2+1] = (ma_int32)right; - } -} -#if defined(MA_DR_FLAC_SUPPORT_SSE2) -static MA_INLINE void ma_dr_flac_read_pcm_frames_s32__decode_right_side__sse2(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, ma_int32* pOutputSamples) -{ - ma_uint64 i; - ma_uint64 frameCount4 = frameCount >> 2; - const ma_uint32* pInputSamples0U32 = (const ma_uint32*)pInputSamples0; - const ma_uint32* pInputSamples1U32 = (const ma_uint32*)pInputSamples1; - ma_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; - ma_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; - MA_DR_FLAC_ASSERT(pFlac->bitsPerSample <= 24); - for (i = 0; i < frameCount4; ++i) { - __m128i side = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples0 + i), shift0); - __m128i right = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples1 + i), shift1); - __m128i left = _mm_add_epi32(right, side); - _mm_storeu_si128((__m128i*)(pOutputSamples + i*8 + 0), _mm_unpacklo_epi32(left, right)); - _mm_storeu_si128((__m128i*)(pOutputSamples + i*8 + 4), _mm_unpackhi_epi32(left, right)); - } - for (i = (frameCount4 << 2); i < frameCount; ++i) { - ma_uint32 side = pInputSamples0U32[i] << shift0; - ma_uint32 right = pInputSamples1U32[i] << shift1; - ma_uint32 left = right + side; - pOutputSamples[i*2+0] = (ma_int32)left; - pOutputSamples[i*2+1] = (ma_int32)right; - } -} -#endif -#if defined(MA_DR_FLAC_SUPPORT_NEON) -static MA_INLINE void ma_dr_flac_read_pcm_frames_s32__decode_right_side__neon(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, ma_int32* pOutputSamples) -{ - ma_uint64 i; - ma_uint64 frameCount4 = frameCount >> 2; - const ma_uint32* pInputSamples0U32 = (const ma_uint32*)pInputSamples0; - const ma_uint32* pInputSamples1U32 = (const ma_uint32*)pInputSamples1; - ma_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; - ma_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; - int32x4_t shift0_4; - int32x4_t shift1_4; - MA_DR_FLAC_ASSERT(pFlac->bitsPerSample <= 24); - shift0_4 = vdupq_n_s32(shift0); - shift1_4 = vdupq_n_s32(shift1); - for (i = 0; i < frameCount4; ++i) { - uint32x4_t side; - uint32x4_t right; - uint32x4_t left; - side = vshlq_u32(vld1q_u32(pInputSamples0U32 + i*4), shift0_4); - right = vshlq_u32(vld1q_u32(pInputSamples1U32 + i*4), shift1_4); - left = vaddq_u32(right, side); - ma_dr_flac__vst2q_u32((ma_uint32*)pOutputSamples + i*8, vzipq_u32(left, right)); - } - for (i = (frameCount4 << 2); i < frameCount; ++i) { - ma_uint32 side = pInputSamples0U32[i] << shift0; - ma_uint32 right = pInputSamples1U32[i] << shift1; - ma_uint32 left = right + side; - pOutputSamples[i*2+0] = (ma_int32)left; - pOutputSamples[i*2+1] = (ma_int32)right; - } -} -#endif -static MA_INLINE void ma_dr_flac_read_pcm_frames_s32__decode_right_side(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, ma_int32* pOutputSamples) -{ -#if defined(MA_DR_FLAC_SUPPORT_SSE2) - if (ma_dr_flac__gIsSSE2Supported && pFlac->bitsPerSample <= 24) { - ma_dr_flac_read_pcm_frames_s32__decode_right_side__sse2(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); - } else -#elif defined(MA_DR_FLAC_SUPPORT_NEON) - if (ma_dr_flac__gIsNEONSupported && pFlac->bitsPerSample <= 24) { - ma_dr_flac_read_pcm_frames_s32__decode_right_side__neon(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); - } else -#endif - { -#if 0 - ma_dr_flac_read_pcm_frames_s32__decode_right_side__reference(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); -#else - ma_dr_flac_read_pcm_frames_s32__decode_right_side__scalar(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); -#endif - } -} -#if 0 -static MA_INLINE void ma_dr_flac_read_pcm_frames_s32__decode_mid_side__reference(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, ma_int32* pOutputSamples) -{ - for (ma_uint64 i = 0; i < frameCount; ++i) { - ma_uint32 mid = pInputSamples0U32[i] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; - ma_uint32 side = pInputSamples1U32[i] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; - mid = (mid << 1) | (side & 0x01); - pOutputSamples[i*2+0] = (ma_int32)((ma_uint32)((ma_int32)(mid + side) >> 1) << unusedBitsPerSample); - pOutputSamples[i*2+1] = (ma_int32)((ma_uint32)((ma_int32)(mid - side) >> 1) << unusedBitsPerSample); - } -} -#endif -static MA_INLINE void ma_dr_flac_read_pcm_frames_s32__decode_mid_side__scalar(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, ma_int32* pOutputSamples) -{ - ma_uint64 i; - ma_uint64 frameCount4 = frameCount >> 2; - const ma_uint32* pInputSamples0U32 = (const ma_uint32*)pInputSamples0; - const ma_uint32* pInputSamples1U32 = (const ma_uint32*)pInputSamples1; - ma_int32 shift = unusedBitsPerSample; - if (shift > 0) { - shift -= 1; - for (i = 0; i < frameCount4; ++i) { - ma_uint32 temp0L; - ma_uint32 temp1L; - ma_uint32 temp2L; - ma_uint32 temp3L; - ma_uint32 temp0R; - ma_uint32 temp1R; - ma_uint32 temp2R; - ma_uint32 temp3R; - ma_uint32 mid0 = pInputSamples0U32[i*4+0] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; - ma_uint32 mid1 = pInputSamples0U32[i*4+1] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; - ma_uint32 mid2 = pInputSamples0U32[i*4+2] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; - ma_uint32 mid3 = pInputSamples0U32[i*4+3] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; - ma_uint32 side0 = pInputSamples1U32[i*4+0] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; - ma_uint32 side1 = pInputSamples1U32[i*4+1] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; - ma_uint32 side2 = pInputSamples1U32[i*4+2] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; - ma_uint32 side3 = pInputSamples1U32[i*4+3] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; - mid0 = (mid0 << 1) | (side0 & 0x01); - mid1 = (mid1 << 1) | (side1 & 0x01); - mid2 = (mid2 << 1) | (side2 & 0x01); - mid3 = (mid3 << 1) | (side3 & 0x01); - temp0L = (mid0 + side0) << shift; - temp1L = (mid1 + side1) << shift; - temp2L = (mid2 + side2) << shift; - temp3L = (mid3 + side3) << shift; - temp0R = (mid0 - side0) << shift; - temp1R = (mid1 - side1) << shift; - temp2R = (mid2 - side2) << shift; - temp3R = (mid3 - side3) << shift; - pOutputSamples[i*8+0] = (ma_int32)temp0L; - pOutputSamples[i*8+1] = (ma_int32)temp0R; - pOutputSamples[i*8+2] = (ma_int32)temp1L; - pOutputSamples[i*8+3] = (ma_int32)temp1R; - pOutputSamples[i*8+4] = (ma_int32)temp2L; - pOutputSamples[i*8+5] = (ma_int32)temp2R; - pOutputSamples[i*8+6] = (ma_int32)temp3L; - pOutputSamples[i*8+7] = (ma_int32)temp3R; - } - } else { - for (i = 0; i < frameCount4; ++i) { - ma_uint32 temp0L; - ma_uint32 temp1L; - ma_uint32 temp2L; - ma_uint32 temp3L; - ma_uint32 temp0R; - ma_uint32 temp1R; - ma_uint32 temp2R; - ma_uint32 temp3R; - ma_uint32 mid0 = pInputSamples0U32[i*4+0] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; - ma_uint32 mid1 = pInputSamples0U32[i*4+1] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; - ma_uint32 mid2 = pInputSamples0U32[i*4+2] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; - ma_uint32 mid3 = pInputSamples0U32[i*4+3] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; - ma_uint32 side0 = pInputSamples1U32[i*4+0] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; - ma_uint32 side1 = pInputSamples1U32[i*4+1] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; - ma_uint32 side2 = pInputSamples1U32[i*4+2] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; - ma_uint32 side3 = pInputSamples1U32[i*4+3] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; - mid0 = (mid0 << 1) | (side0 & 0x01); - mid1 = (mid1 << 1) | (side1 & 0x01); - mid2 = (mid2 << 1) | (side2 & 0x01); - mid3 = (mid3 << 1) | (side3 & 0x01); - temp0L = (ma_uint32)((ma_int32)(mid0 + side0) >> 1); - temp1L = (ma_uint32)((ma_int32)(mid1 + side1) >> 1); - temp2L = (ma_uint32)((ma_int32)(mid2 + side2) >> 1); - temp3L = (ma_uint32)((ma_int32)(mid3 + side3) >> 1); - temp0R = (ma_uint32)((ma_int32)(mid0 - side0) >> 1); - temp1R = (ma_uint32)((ma_int32)(mid1 - side1) >> 1); - temp2R = (ma_uint32)((ma_int32)(mid2 - side2) >> 1); - temp3R = (ma_uint32)((ma_int32)(mid3 - side3) >> 1); - pOutputSamples[i*8+0] = (ma_int32)temp0L; - pOutputSamples[i*8+1] = (ma_int32)temp0R; - pOutputSamples[i*8+2] = (ma_int32)temp1L; - pOutputSamples[i*8+3] = (ma_int32)temp1R; - pOutputSamples[i*8+4] = (ma_int32)temp2L; - pOutputSamples[i*8+5] = (ma_int32)temp2R; - pOutputSamples[i*8+6] = (ma_int32)temp3L; - pOutputSamples[i*8+7] = (ma_int32)temp3R; - } - } - for (i = (frameCount4 << 2); i < frameCount; ++i) { - ma_uint32 mid = pInputSamples0U32[i] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; - ma_uint32 side = pInputSamples1U32[i] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; - mid = (mid << 1) | (side & 0x01); - pOutputSamples[i*2+0] = (ma_int32)((ma_uint32)((ma_int32)(mid + side) >> 1) << unusedBitsPerSample); - pOutputSamples[i*2+1] = (ma_int32)((ma_uint32)((ma_int32)(mid - side) >> 1) << unusedBitsPerSample); - } -} -#if defined(MA_DR_FLAC_SUPPORT_SSE2) -static MA_INLINE void ma_dr_flac_read_pcm_frames_s32__decode_mid_side__sse2(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, ma_int32* pOutputSamples) -{ - ma_uint64 i; - ma_uint64 frameCount4 = frameCount >> 2; - const ma_uint32* pInputSamples0U32 = (const ma_uint32*)pInputSamples0; - const ma_uint32* pInputSamples1U32 = (const ma_uint32*)pInputSamples1; - ma_int32 shift = unusedBitsPerSample; - MA_DR_FLAC_ASSERT(pFlac->bitsPerSample <= 24); - if (shift == 0) { - for (i = 0; i < frameCount4; ++i) { - __m128i mid; - __m128i side; - __m128i left; - __m128i right; - mid = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples0 + i), pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample); - side = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples1 + i), pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample); - mid = _mm_or_si128(_mm_slli_epi32(mid, 1), _mm_and_si128(side, _mm_set1_epi32(0x01))); - left = _mm_srai_epi32(_mm_add_epi32(mid, side), 1); - right = _mm_srai_epi32(_mm_sub_epi32(mid, side), 1); - _mm_storeu_si128((__m128i*)(pOutputSamples + i*8 + 0), _mm_unpacklo_epi32(left, right)); - _mm_storeu_si128((__m128i*)(pOutputSamples + i*8 + 4), _mm_unpackhi_epi32(left, right)); - } - for (i = (frameCount4 << 2); i < frameCount; ++i) { - ma_uint32 mid = pInputSamples0U32[i] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; - ma_uint32 side = pInputSamples1U32[i] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; - mid = (mid << 1) | (side & 0x01); - pOutputSamples[i*2+0] = (ma_int32)(mid + side) >> 1; - pOutputSamples[i*2+1] = (ma_int32)(mid - side) >> 1; - } - } else { - shift -= 1; - for (i = 0; i < frameCount4; ++i) { - __m128i mid; - __m128i side; - __m128i left; - __m128i right; - mid = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples0 + i), pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample); - side = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples1 + i), pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample); - mid = _mm_or_si128(_mm_slli_epi32(mid, 1), _mm_and_si128(side, _mm_set1_epi32(0x01))); - left = _mm_slli_epi32(_mm_add_epi32(mid, side), shift); - right = _mm_slli_epi32(_mm_sub_epi32(mid, side), shift); - _mm_storeu_si128((__m128i*)(pOutputSamples + i*8 + 0), _mm_unpacklo_epi32(left, right)); - _mm_storeu_si128((__m128i*)(pOutputSamples + i*8 + 4), _mm_unpackhi_epi32(left, right)); - } - for (i = (frameCount4 << 2); i < frameCount; ++i) { - ma_uint32 mid = pInputSamples0U32[i] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; - ma_uint32 side = pInputSamples1U32[i] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; - mid = (mid << 1) | (side & 0x01); - pOutputSamples[i*2+0] = (ma_int32)((mid + side) << shift); - pOutputSamples[i*2+1] = (ma_int32)((mid - side) << shift); - } - } -} -#endif -#if defined(MA_DR_FLAC_SUPPORT_NEON) -static MA_INLINE void ma_dr_flac_read_pcm_frames_s32__decode_mid_side__neon(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, ma_int32* pOutputSamples) -{ - ma_uint64 i; - ma_uint64 frameCount4 = frameCount >> 2; - const ma_uint32* pInputSamples0U32 = (const ma_uint32*)pInputSamples0; - const ma_uint32* pInputSamples1U32 = (const ma_uint32*)pInputSamples1; - ma_int32 shift = unusedBitsPerSample; - int32x4_t wbpsShift0_4; - int32x4_t wbpsShift1_4; - uint32x4_t one4; - MA_DR_FLAC_ASSERT(pFlac->bitsPerSample <= 24); - wbpsShift0_4 = vdupq_n_s32(pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample); - wbpsShift1_4 = vdupq_n_s32(pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample); - one4 = vdupq_n_u32(1); - if (shift == 0) { - for (i = 0; i < frameCount4; ++i) { - uint32x4_t mid; - uint32x4_t side; - int32x4_t left; - int32x4_t right; - mid = vshlq_u32(vld1q_u32(pInputSamples0U32 + i*4), wbpsShift0_4); - side = vshlq_u32(vld1q_u32(pInputSamples1U32 + i*4), wbpsShift1_4); - mid = vorrq_u32(vshlq_n_u32(mid, 1), vandq_u32(side, one4)); - left = vshrq_n_s32(vreinterpretq_s32_u32(vaddq_u32(mid, side)), 1); - right = vshrq_n_s32(vreinterpretq_s32_u32(vsubq_u32(mid, side)), 1); - ma_dr_flac__vst2q_s32(pOutputSamples + i*8, vzipq_s32(left, right)); - } - for (i = (frameCount4 << 2); i < frameCount; ++i) { - ma_uint32 mid = pInputSamples0U32[i] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; - ma_uint32 side = pInputSamples1U32[i] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; - mid = (mid << 1) | (side & 0x01); - pOutputSamples[i*2+0] = (ma_int32)(mid + side) >> 1; - pOutputSamples[i*2+1] = (ma_int32)(mid - side) >> 1; - } - } else { - int32x4_t shift4; - shift -= 1; - shift4 = vdupq_n_s32(shift); - for (i = 0; i < frameCount4; ++i) { - uint32x4_t mid; - uint32x4_t side; - int32x4_t left; - int32x4_t right; - mid = vshlq_u32(vld1q_u32(pInputSamples0U32 + i*4), wbpsShift0_4); - side = vshlq_u32(vld1q_u32(pInputSamples1U32 + i*4), wbpsShift1_4); - mid = vorrq_u32(vshlq_n_u32(mid, 1), vandq_u32(side, one4)); - left = vreinterpretq_s32_u32(vshlq_u32(vaddq_u32(mid, side), shift4)); - right = vreinterpretq_s32_u32(vshlq_u32(vsubq_u32(mid, side), shift4)); - ma_dr_flac__vst2q_s32(pOutputSamples + i*8, vzipq_s32(left, right)); - } - for (i = (frameCount4 << 2); i < frameCount; ++i) { - ma_uint32 mid = pInputSamples0U32[i] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; - ma_uint32 side = pInputSamples1U32[i] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; - mid = (mid << 1) | (side & 0x01); - pOutputSamples[i*2+0] = (ma_int32)((mid + side) << shift); - pOutputSamples[i*2+1] = (ma_int32)((mid - side) << shift); - } - } -} -#endif -static MA_INLINE void ma_dr_flac_read_pcm_frames_s32__decode_mid_side(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, ma_int32* pOutputSamples) -{ -#if defined(MA_DR_FLAC_SUPPORT_SSE2) - if (ma_dr_flac__gIsSSE2Supported && pFlac->bitsPerSample <= 24) { - ma_dr_flac_read_pcm_frames_s32__decode_mid_side__sse2(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); - } else -#elif defined(MA_DR_FLAC_SUPPORT_NEON) - if (ma_dr_flac__gIsNEONSupported && pFlac->bitsPerSample <= 24) { - ma_dr_flac_read_pcm_frames_s32__decode_mid_side__neon(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); - } else -#endif - { -#if 0 - ma_dr_flac_read_pcm_frames_s32__decode_mid_side__reference(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); -#else - ma_dr_flac_read_pcm_frames_s32__decode_mid_side__scalar(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); -#endif - } -} -#if 0 -static MA_INLINE void ma_dr_flac_read_pcm_frames_s32__decode_independent_stereo__reference(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, ma_int32* pOutputSamples) -{ - for (ma_uint64 i = 0; i < frameCount; ++i) { - pOutputSamples[i*2+0] = (ma_int32)((ma_uint32)pInputSamples0[i] << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample)); - pOutputSamples[i*2+1] = (ma_int32)((ma_uint32)pInputSamples1[i] << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample)); - } -} -#endif -static MA_INLINE void ma_dr_flac_read_pcm_frames_s32__decode_independent_stereo__scalar(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, ma_int32* pOutputSamples) -{ - ma_uint64 i; - ma_uint64 frameCount4 = frameCount >> 2; - const ma_uint32* pInputSamples0U32 = (const ma_uint32*)pInputSamples0; - const ma_uint32* pInputSamples1U32 = (const ma_uint32*)pInputSamples1; - ma_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; - ma_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; - for (i = 0; i < frameCount4; ++i) { - ma_uint32 tempL0 = pInputSamples0U32[i*4+0] << shift0; - ma_uint32 tempL1 = pInputSamples0U32[i*4+1] << shift0; - ma_uint32 tempL2 = pInputSamples0U32[i*4+2] << shift0; - ma_uint32 tempL3 = pInputSamples0U32[i*4+3] << shift0; - ma_uint32 tempR0 = pInputSamples1U32[i*4+0] << shift1; - ma_uint32 tempR1 = pInputSamples1U32[i*4+1] << shift1; - ma_uint32 tempR2 = pInputSamples1U32[i*4+2] << shift1; - ma_uint32 tempR3 = pInputSamples1U32[i*4+3] << shift1; - pOutputSamples[i*8+0] = (ma_int32)tempL0; - pOutputSamples[i*8+1] = (ma_int32)tempR0; - pOutputSamples[i*8+2] = (ma_int32)tempL1; - pOutputSamples[i*8+3] = (ma_int32)tempR1; - pOutputSamples[i*8+4] = (ma_int32)tempL2; - pOutputSamples[i*8+5] = (ma_int32)tempR2; - pOutputSamples[i*8+6] = (ma_int32)tempL3; - pOutputSamples[i*8+7] = (ma_int32)tempR3; - } - for (i = (frameCount4 << 2); i < frameCount; ++i) { - pOutputSamples[i*2+0] = (ma_int32)(pInputSamples0U32[i] << shift0); - pOutputSamples[i*2+1] = (ma_int32)(pInputSamples1U32[i] << shift1); - } -} -#if defined(MA_DR_FLAC_SUPPORT_SSE2) -static MA_INLINE void ma_dr_flac_read_pcm_frames_s32__decode_independent_stereo__sse2(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, ma_int32* pOutputSamples) -{ - ma_uint64 i; - ma_uint64 frameCount4 = frameCount >> 2; - const ma_uint32* pInputSamples0U32 = (const ma_uint32*)pInputSamples0; - const ma_uint32* pInputSamples1U32 = (const ma_uint32*)pInputSamples1; - ma_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; - ma_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; - for (i = 0; i < frameCount4; ++i) { - __m128i left = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples0 + i), shift0); - __m128i right = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples1 + i), shift1); - _mm_storeu_si128((__m128i*)(pOutputSamples + i*8 + 0), _mm_unpacklo_epi32(left, right)); - _mm_storeu_si128((__m128i*)(pOutputSamples + i*8 + 4), _mm_unpackhi_epi32(left, right)); - } - for (i = (frameCount4 << 2); i < frameCount; ++i) { - pOutputSamples[i*2+0] = (ma_int32)(pInputSamples0U32[i] << shift0); - pOutputSamples[i*2+1] = (ma_int32)(pInputSamples1U32[i] << shift1); - } -} -#endif -#if defined(MA_DR_FLAC_SUPPORT_NEON) -static MA_INLINE void ma_dr_flac_read_pcm_frames_s32__decode_independent_stereo__neon(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, ma_int32* pOutputSamples) -{ - ma_uint64 i; - ma_uint64 frameCount4 = frameCount >> 2; - const ma_uint32* pInputSamples0U32 = (const ma_uint32*)pInputSamples0; - const ma_uint32* pInputSamples1U32 = (const ma_uint32*)pInputSamples1; - ma_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; - ma_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; - int32x4_t shift4_0 = vdupq_n_s32(shift0); - int32x4_t shift4_1 = vdupq_n_s32(shift1); - for (i = 0; i < frameCount4; ++i) { - int32x4_t left; - int32x4_t right; - left = vreinterpretq_s32_u32(vshlq_u32(vld1q_u32(pInputSamples0U32 + i*4), shift4_0)); - right = vreinterpretq_s32_u32(vshlq_u32(vld1q_u32(pInputSamples1U32 + i*4), shift4_1)); - ma_dr_flac__vst2q_s32(pOutputSamples + i*8, vzipq_s32(left, right)); - } - for (i = (frameCount4 << 2); i < frameCount; ++i) { - pOutputSamples[i*2+0] = (ma_int32)(pInputSamples0U32[i] << shift0); - pOutputSamples[i*2+1] = (ma_int32)(pInputSamples1U32[i] << shift1); - } -} -#endif -static MA_INLINE void ma_dr_flac_read_pcm_frames_s32__decode_independent_stereo(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, ma_int32* pOutputSamples) -{ -#if defined(MA_DR_FLAC_SUPPORT_SSE2) - if (ma_dr_flac__gIsSSE2Supported && pFlac->bitsPerSample <= 24) { - ma_dr_flac_read_pcm_frames_s32__decode_independent_stereo__sse2(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); - } else -#elif defined(MA_DR_FLAC_SUPPORT_NEON) - if (ma_dr_flac__gIsNEONSupported && pFlac->bitsPerSample <= 24) { - ma_dr_flac_read_pcm_frames_s32__decode_independent_stereo__neon(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); - } else -#endif - { -#if 0 - ma_dr_flac_read_pcm_frames_s32__decode_independent_stereo__reference(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); -#else - ma_dr_flac_read_pcm_frames_s32__decode_independent_stereo__scalar(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); -#endif - } -} -MA_API ma_uint64 ma_dr_flac_read_pcm_frames_s32(ma_dr_flac* pFlac, ma_uint64 framesToRead, ma_int32* pBufferOut) -{ - ma_uint64 framesRead; - ma_uint32 unusedBitsPerSample; - if (pFlac == NULL || framesToRead == 0) { - return 0; - } - if (pBufferOut == NULL) { - return ma_dr_flac__seek_forward_by_pcm_frames(pFlac, framesToRead); - } - MA_DR_FLAC_ASSERT(pFlac->bitsPerSample <= 32); - unusedBitsPerSample = 32 - pFlac->bitsPerSample; - framesRead = 0; - while (framesToRead > 0) { - if (pFlac->currentFLACFrame.pcmFramesRemaining == 0) { - if (!ma_dr_flac__read_and_decode_next_flac_frame(pFlac)) { - break; - } - } else { - unsigned int channelCount = ma_dr_flac__get_channel_count_from_channel_assignment(pFlac->currentFLACFrame.header.channelAssignment); - ma_uint64 iFirstPCMFrame = pFlac->currentFLACFrame.header.blockSizeInPCMFrames - pFlac->currentFLACFrame.pcmFramesRemaining; - ma_uint64 frameCountThisIteration = framesToRead; - if (frameCountThisIteration > pFlac->currentFLACFrame.pcmFramesRemaining) { - frameCountThisIteration = pFlac->currentFLACFrame.pcmFramesRemaining; - } - if (channelCount == 2) { - const ma_int32* pDecodedSamples0 = pFlac->currentFLACFrame.subframes[0].pSamplesS32 + iFirstPCMFrame; - const ma_int32* pDecodedSamples1 = pFlac->currentFLACFrame.subframes[1].pSamplesS32 + iFirstPCMFrame; - switch (pFlac->currentFLACFrame.header.channelAssignment) - { - case MA_DR_FLAC_CHANNEL_ASSIGNMENT_LEFT_SIDE: - { - ma_dr_flac_read_pcm_frames_s32__decode_left_side(pFlac, frameCountThisIteration, unusedBitsPerSample, pDecodedSamples0, pDecodedSamples1, pBufferOut); - } break; - case MA_DR_FLAC_CHANNEL_ASSIGNMENT_RIGHT_SIDE: - { - ma_dr_flac_read_pcm_frames_s32__decode_right_side(pFlac, frameCountThisIteration, unusedBitsPerSample, pDecodedSamples0, pDecodedSamples1, pBufferOut); - } break; - case MA_DR_FLAC_CHANNEL_ASSIGNMENT_MID_SIDE: - { - ma_dr_flac_read_pcm_frames_s32__decode_mid_side(pFlac, frameCountThisIteration, unusedBitsPerSample, pDecodedSamples0, pDecodedSamples1, pBufferOut); - } break; - case MA_DR_FLAC_CHANNEL_ASSIGNMENT_INDEPENDENT: - default: - { - ma_dr_flac_read_pcm_frames_s32__decode_independent_stereo(pFlac, frameCountThisIteration, unusedBitsPerSample, pDecodedSamples0, pDecodedSamples1, pBufferOut); - } break; - } - } else { - ma_uint64 i; - for (i = 0; i < frameCountThisIteration; ++i) { - unsigned int j; - for (j = 0; j < channelCount; ++j) { - pBufferOut[(i*channelCount)+j] = (ma_int32)((ma_uint32)(pFlac->currentFLACFrame.subframes[j].pSamplesS32[iFirstPCMFrame + i]) << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[j].wastedBitsPerSample)); - } - } - } - framesRead += frameCountThisIteration; - pBufferOut += frameCountThisIteration * channelCount; - framesToRead -= frameCountThisIteration; - pFlac->currentPCMFrame += frameCountThisIteration; - pFlac->currentFLACFrame.pcmFramesRemaining -= (ma_uint32)frameCountThisIteration; - } - } - return framesRead; -} -#if 0 -static MA_INLINE void ma_dr_flac_read_pcm_frames_s16__decode_left_side__reference(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, ma_int16* pOutputSamples) -{ - ma_uint64 i; - for (i = 0; i < frameCount; ++i) { - ma_uint32 left = (ma_uint32)pInputSamples0[i] << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample); - ma_uint32 side = (ma_uint32)pInputSamples1[i] << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample); - ma_uint32 right = left - side; - left >>= 16; - right >>= 16; - pOutputSamples[i*2+0] = (ma_int16)left; - pOutputSamples[i*2+1] = (ma_int16)right; - } -} -#endif -static MA_INLINE void ma_dr_flac_read_pcm_frames_s16__decode_left_side__scalar(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, ma_int16* pOutputSamples) -{ - ma_uint64 i; - ma_uint64 frameCount4 = frameCount >> 2; - const ma_uint32* pInputSamples0U32 = (const ma_uint32*)pInputSamples0; - const ma_uint32* pInputSamples1U32 = (const ma_uint32*)pInputSamples1; - ma_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; - ma_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; - for (i = 0; i < frameCount4; ++i) { - ma_uint32 left0 = pInputSamples0U32[i*4+0] << shift0; - ma_uint32 left1 = pInputSamples0U32[i*4+1] << shift0; - ma_uint32 left2 = pInputSamples0U32[i*4+2] << shift0; - ma_uint32 left3 = pInputSamples0U32[i*4+3] << shift0; - ma_uint32 side0 = pInputSamples1U32[i*4+0] << shift1; - ma_uint32 side1 = pInputSamples1U32[i*4+1] << shift1; - ma_uint32 side2 = pInputSamples1U32[i*4+2] << shift1; - ma_uint32 side3 = pInputSamples1U32[i*4+3] << shift1; - ma_uint32 right0 = left0 - side0; - ma_uint32 right1 = left1 - side1; - ma_uint32 right2 = left2 - side2; - ma_uint32 right3 = left3 - side3; - left0 >>= 16; - left1 >>= 16; - left2 >>= 16; - left3 >>= 16; - right0 >>= 16; - right1 >>= 16; - right2 >>= 16; - right3 >>= 16; - pOutputSamples[i*8+0] = (ma_int16)left0; - pOutputSamples[i*8+1] = (ma_int16)right0; - pOutputSamples[i*8+2] = (ma_int16)left1; - pOutputSamples[i*8+3] = (ma_int16)right1; - pOutputSamples[i*8+4] = (ma_int16)left2; - pOutputSamples[i*8+5] = (ma_int16)right2; - pOutputSamples[i*8+6] = (ma_int16)left3; - pOutputSamples[i*8+7] = (ma_int16)right3; - } - for (i = (frameCount4 << 2); i < frameCount; ++i) { - ma_uint32 left = pInputSamples0U32[i] << shift0; - ma_uint32 side = pInputSamples1U32[i] << shift1; - ma_uint32 right = left - side; - left >>= 16; - right >>= 16; - pOutputSamples[i*2+0] = (ma_int16)left; - pOutputSamples[i*2+1] = (ma_int16)right; - } -} -#if defined(MA_DR_FLAC_SUPPORT_SSE2) -static MA_INLINE void ma_dr_flac_read_pcm_frames_s16__decode_left_side__sse2(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, ma_int16* pOutputSamples) -{ - ma_uint64 i; - ma_uint64 frameCount4 = frameCount >> 2; - const ma_uint32* pInputSamples0U32 = (const ma_uint32*)pInputSamples0; - const ma_uint32* pInputSamples1U32 = (const ma_uint32*)pInputSamples1; - ma_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; - ma_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; - MA_DR_FLAC_ASSERT(pFlac->bitsPerSample <= 24); - for (i = 0; i < frameCount4; ++i) { - __m128i left = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples0 + i), shift0); - __m128i side = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples1 + i), shift1); - __m128i right = _mm_sub_epi32(left, side); - left = _mm_srai_epi32(left, 16); - right = _mm_srai_epi32(right, 16); - _mm_storeu_si128((__m128i*)(pOutputSamples + i*8), ma_dr_flac__mm_packs_interleaved_epi32(left, right)); - } - for (i = (frameCount4 << 2); i < frameCount; ++i) { - ma_uint32 left = pInputSamples0U32[i] << shift0; - ma_uint32 side = pInputSamples1U32[i] << shift1; - ma_uint32 right = left - side; - left >>= 16; - right >>= 16; - pOutputSamples[i*2+0] = (ma_int16)left; - pOutputSamples[i*2+1] = (ma_int16)right; - } -} -#endif -#if defined(MA_DR_FLAC_SUPPORT_NEON) -static MA_INLINE void ma_dr_flac_read_pcm_frames_s16__decode_left_side__neon(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, ma_int16* pOutputSamples) -{ - ma_uint64 i; - ma_uint64 frameCount4 = frameCount >> 2; - const ma_uint32* pInputSamples0U32 = (const ma_uint32*)pInputSamples0; - const ma_uint32* pInputSamples1U32 = (const ma_uint32*)pInputSamples1; - ma_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; - ma_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; - int32x4_t shift0_4; - int32x4_t shift1_4; - MA_DR_FLAC_ASSERT(pFlac->bitsPerSample <= 24); - shift0_4 = vdupq_n_s32(shift0); - shift1_4 = vdupq_n_s32(shift1); - for (i = 0; i < frameCount4; ++i) { - uint32x4_t left; - uint32x4_t side; - uint32x4_t right; - left = vshlq_u32(vld1q_u32(pInputSamples0U32 + i*4), shift0_4); - side = vshlq_u32(vld1q_u32(pInputSamples1U32 + i*4), shift1_4); - right = vsubq_u32(left, side); - left = vshrq_n_u32(left, 16); - right = vshrq_n_u32(right, 16); - ma_dr_flac__vst2q_u16((ma_uint16*)pOutputSamples + i*8, vzip_u16(vmovn_u32(left), vmovn_u32(right))); - } - for (i = (frameCount4 << 2); i < frameCount; ++i) { - ma_uint32 left = pInputSamples0U32[i] << shift0; - ma_uint32 side = pInputSamples1U32[i] << shift1; - ma_uint32 right = left - side; - left >>= 16; - right >>= 16; - pOutputSamples[i*2+0] = (ma_int16)left; - pOutputSamples[i*2+1] = (ma_int16)right; - } -} -#endif -static MA_INLINE void ma_dr_flac_read_pcm_frames_s16__decode_left_side(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, ma_int16* pOutputSamples) -{ -#if defined(MA_DR_FLAC_SUPPORT_SSE2) - if (ma_dr_flac__gIsSSE2Supported && pFlac->bitsPerSample <= 24) { - ma_dr_flac_read_pcm_frames_s16__decode_left_side__sse2(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); - } else -#elif defined(MA_DR_FLAC_SUPPORT_NEON) - if (ma_dr_flac__gIsNEONSupported && pFlac->bitsPerSample <= 24) { - ma_dr_flac_read_pcm_frames_s16__decode_left_side__neon(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); - } else -#endif - { -#if 0 - ma_dr_flac_read_pcm_frames_s16__decode_left_side__reference(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); -#else - ma_dr_flac_read_pcm_frames_s16__decode_left_side__scalar(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); -#endif - } -} -#if 0 -static MA_INLINE void ma_dr_flac_read_pcm_frames_s16__decode_right_side__reference(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, ma_int16* pOutputSamples) -{ - ma_uint64 i; - for (i = 0; i < frameCount; ++i) { - ma_uint32 side = (ma_uint32)pInputSamples0[i] << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample); - ma_uint32 right = (ma_uint32)pInputSamples1[i] << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample); - ma_uint32 left = right + side; - left >>= 16; - right >>= 16; - pOutputSamples[i*2+0] = (ma_int16)left; - pOutputSamples[i*2+1] = (ma_int16)right; - } -} -#endif -static MA_INLINE void ma_dr_flac_read_pcm_frames_s16__decode_right_side__scalar(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, ma_int16* pOutputSamples) -{ - ma_uint64 i; - ma_uint64 frameCount4 = frameCount >> 2; - const ma_uint32* pInputSamples0U32 = (const ma_uint32*)pInputSamples0; - const ma_uint32* pInputSamples1U32 = (const ma_uint32*)pInputSamples1; - ma_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; - ma_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; - for (i = 0; i < frameCount4; ++i) { - ma_uint32 side0 = pInputSamples0U32[i*4+0] << shift0; - ma_uint32 side1 = pInputSamples0U32[i*4+1] << shift0; - ma_uint32 side2 = pInputSamples0U32[i*4+2] << shift0; - ma_uint32 side3 = pInputSamples0U32[i*4+3] << shift0; - ma_uint32 right0 = pInputSamples1U32[i*4+0] << shift1; - ma_uint32 right1 = pInputSamples1U32[i*4+1] << shift1; - ma_uint32 right2 = pInputSamples1U32[i*4+2] << shift1; - ma_uint32 right3 = pInputSamples1U32[i*4+3] << shift1; - ma_uint32 left0 = right0 + side0; - ma_uint32 left1 = right1 + side1; - ma_uint32 left2 = right2 + side2; - ma_uint32 left3 = right3 + side3; - left0 >>= 16; - left1 >>= 16; - left2 >>= 16; - left3 >>= 16; - right0 >>= 16; - right1 >>= 16; - right2 >>= 16; - right3 >>= 16; - pOutputSamples[i*8+0] = (ma_int16)left0; - pOutputSamples[i*8+1] = (ma_int16)right0; - pOutputSamples[i*8+2] = (ma_int16)left1; - pOutputSamples[i*8+3] = (ma_int16)right1; - pOutputSamples[i*8+4] = (ma_int16)left2; - pOutputSamples[i*8+5] = (ma_int16)right2; - pOutputSamples[i*8+6] = (ma_int16)left3; - pOutputSamples[i*8+7] = (ma_int16)right3; - } - for (i = (frameCount4 << 2); i < frameCount; ++i) { - ma_uint32 side = pInputSamples0U32[i] << shift0; - ma_uint32 right = pInputSamples1U32[i] << shift1; - ma_uint32 left = right + side; - left >>= 16; - right >>= 16; - pOutputSamples[i*2+0] = (ma_int16)left; - pOutputSamples[i*2+1] = (ma_int16)right; - } -} -#if defined(MA_DR_FLAC_SUPPORT_SSE2) -static MA_INLINE void ma_dr_flac_read_pcm_frames_s16__decode_right_side__sse2(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, ma_int16* pOutputSamples) -{ - ma_uint64 i; - ma_uint64 frameCount4 = frameCount >> 2; - const ma_uint32* pInputSamples0U32 = (const ma_uint32*)pInputSamples0; - const ma_uint32* pInputSamples1U32 = (const ma_uint32*)pInputSamples1; - ma_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; - ma_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; - MA_DR_FLAC_ASSERT(pFlac->bitsPerSample <= 24); - for (i = 0; i < frameCount4; ++i) { - __m128i side = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples0 + i), shift0); - __m128i right = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples1 + i), shift1); - __m128i left = _mm_add_epi32(right, side); - left = _mm_srai_epi32(left, 16); - right = _mm_srai_epi32(right, 16); - _mm_storeu_si128((__m128i*)(pOutputSamples + i*8), ma_dr_flac__mm_packs_interleaved_epi32(left, right)); - } - for (i = (frameCount4 << 2); i < frameCount; ++i) { - ma_uint32 side = pInputSamples0U32[i] << shift0; - ma_uint32 right = pInputSamples1U32[i] << shift1; - ma_uint32 left = right + side; - left >>= 16; - right >>= 16; - pOutputSamples[i*2+0] = (ma_int16)left; - pOutputSamples[i*2+1] = (ma_int16)right; - } -} -#endif -#if defined(MA_DR_FLAC_SUPPORT_NEON) -static MA_INLINE void ma_dr_flac_read_pcm_frames_s16__decode_right_side__neon(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, ma_int16* pOutputSamples) -{ - ma_uint64 i; - ma_uint64 frameCount4 = frameCount >> 2; - const ma_uint32* pInputSamples0U32 = (const ma_uint32*)pInputSamples0; - const ma_uint32* pInputSamples1U32 = (const ma_uint32*)pInputSamples1; - ma_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; - ma_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; - int32x4_t shift0_4; - int32x4_t shift1_4; - MA_DR_FLAC_ASSERT(pFlac->bitsPerSample <= 24); - shift0_4 = vdupq_n_s32(shift0); - shift1_4 = vdupq_n_s32(shift1); - for (i = 0; i < frameCount4; ++i) { - uint32x4_t side; - uint32x4_t right; - uint32x4_t left; - side = vshlq_u32(vld1q_u32(pInputSamples0U32 + i*4), shift0_4); - right = vshlq_u32(vld1q_u32(pInputSamples1U32 + i*4), shift1_4); - left = vaddq_u32(right, side); - left = vshrq_n_u32(left, 16); - right = vshrq_n_u32(right, 16); - ma_dr_flac__vst2q_u16((ma_uint16*)pOutputSamples + i*8, vzip_u16(vmovn_u32(left), vmovn_u32(right))); - } - for (i = (frameCount4 << 2); i < frameCount; ++i) { - ma_uint32 side = pInputSamples0U32[i] << shift0; - ma_uint32 right = pInputSamples1U32[i] << shift1; - ma_uint32 left = right + side; - left >>= 16; - right >>= 16; - pOutputSamples[i*2+0] = (ma_int16)left; - pOutputSamples[i*2+1] = (ma_int16)right; - } -} -#endif -static MA_INLINE void ma_dr_flac_read_pcm_frames_s16__decode_right_side(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, ma_int16* pOutputSamples) -{ -#if defined(MA_DR_FLAC_SUPPORT_SSE2) - if (ma_dr_flac__gIsSSE2Supported && pFlac->bitsPerSample <= 24) { - ma_dr_flac_read_pcm_frames_s16__decode_right_side__sse2(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); - } else -#elif defined(MA_DR_FLAC_SUPPORT_NEON) - if (ma_dr_flac__gIsNEONSupported && pFlac->bitsPerSample <= 24) { - ma_dr_flac_read_pcm_frames_s16__decode_right_side__neon(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); - } else -#endif - { -#if 0 - ma_dr_flac_read_pcm_frames_s16__decode_right_side__reference(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); -#else - ma_dr_flac_read_pcm_frames_s16__decode_right_side__scalar(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); -#endif - } -} -#if 0 -static MA_INLINE void ma_dr_flac_read_pcm_frames_s16__decode_mid_side__reference(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, ma_int16* pOutputSamples) -{ - for (ma_uint64 i = 0; i < frameCount; ++i) { - ma_uint32 mid = (ma_uint32)pInputSamples0[i] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; - ma_uint32 side = (ma_uint32)pInputSamples1[i] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; - mid = (mid << 1) | (side & 0x01); - pOutputSamples[i*2+0] = (ma_int16)(((ma_uint32)((ma_int32)(mid + side) >> 1) << unusedBitsPerSample) >> 16); - pOutputSamples[i*2+1] = (ma_int16)(((ma_uint32)((ma_int32)(mid - side) >> 1) << unusedBitsPerSample) >> 16); - } -} -#endif -static MA_INLINE void ma_dr_flac_read_pcm_frames_s16__decode_mid_side__scalar(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, ma_int16* pOutputSamples) -{ - ma_uint64 i; - ma_uint64 frameCount4 = frameCount >> 2; - const ma_uint32* pInputSamples0U32 = (const ma_uint32*)pInputSamples0; - const ma_uint32* pInputSamples1U32 = (const ma_uint32*)pInputSamples1; - ma_uint32 shift = unusedBitsPerSample; - if (shift > 0) { - shift -= 1; - for (i = 0; i < frameCount4; ++i) { - ma_uint32 temp0L; - ma_uint32 temp1L; - ma_uint32 temp2L; - ma_uint32 temp3L; - ma_uint32 temp0R; - ma_uint32 temp1R; - ma_uint32 temp2R; - ma_uint32 temp3R; - ma_uint32 mid0 = pInputSamples0U32[i*4+0] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; - ma_uint32 mid1 = pInputSamples0U32[i*4+1] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; - ma_uint32 mid2 = pInputSamples0U32[i*4+2] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; - ma_uint32 mid3 = pInputSamples0U32[i*4+3] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; - ma_uint32 side0 = pInputSamples1U32[i*4+0] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; - ma_uint32 side1 = pInputSamples1U32[i*4+1] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; - ma_uint32 side2 = pInputSamples1U32[i*4+2] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; - ma_uint32 side3 = pInputSamples1U32[i*4+3] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; - mid0 = (mid0 << 1) | (side0 & 0x01); - mid1 = (mid1 << 1) | (side1 & 0x01); - mid2 = (mid2 << 1) | (side2 & 0x01); - mid3 = (mid3 << 1) | (side3 & 0x01); - temp0L = (mid0 + side0) << shift; - temp1L = (mid1 + side1) << shift; - temp2L = (mid2 + side2) << shift; - temp3L = (mid3 + side3) << shift; - temp0R = (mid0 - side0) << shift; - temp1R = (mid1 - side1) << shift; - temp2R = (mid2 - side2) << shift; - temp3R = (mid3 - side3) << shift; - temp0L >>= 16; - temp1L >>= 16; - temp2L >>= 16; - temp3L >>= 16; - temp0R >>= 16; - temp1R >>= 16; - temp2R >>= 16; - temp3R >>= 16; - pOutputSamples[i*8+0] = (ma_int16)temp0L; - pOutputSamples[i*8+1] = (ma_int16)temp0R; - pOutputSamples[i*8+2] = (ma_int16)temp1L; - pOutputSamples[i*8+3] = (ma_int16)temp1R; - pOutputSamples[i*8+4] = (ma_int16)temp2L; - pOutputSamples[i*8+5] = (ma_int16)temp2R; - pOutputSamples[i*8+6] = (ma_int16)temp3L; - pOutputSamples[i*8+7] = (ma_int16)temp3R; - } - } else { - for (i = 0; i < frameCount4; ++i) { - ma_uint32 temp0L; - ma_uint32 temp1L; - ma_uint32 temp2L; - ma_uint32 temp3L; - ma_uint32 temp0R; - ma_uint32 temp1R; - ma_uint32 temp2R; - ma_uint32 temp3R; - ma_uint32 mid0 = pInputSamples0U32[i*4+0] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; - ma_uint32 mid1 = pInputSamples0U32[i*4+1] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; - ma_uint32 mid2 = pInputSamples0U32[i*4+2] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; - ma_uint32 mid3 = pInputSamples0U32[i*4+3] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; - ma_uint32 side0 = pInputSamples1U32[i*4+0] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; - ma_uint32 side1 = pInputSamples1U32[i*4+1] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; - ma_uint32 side2 = pInputSamples1U32[i*4+2] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; - ma_uint32 side3 = pInputSamples1U32[i*4+3] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; - mid0 = (mid0 << 1) | (side0 & 0x01); - mid1 = (mid1 << 1) | (side1 & 0x01); - mid2 = (mid2 << 1) | (side2 & 0x01); - mid3 = (mid3 << 1) | (side3 & 0x01); - temp0L = ((ma_int32)(mid0 + side0) >> 1); - temp1L = ((ma_int32)(mid1 + side1) >> 1); - temp2L = ((ma_int32)(mid2 + side2) >> 1); - temp3L = ((ma_int32)(mid3 + side3) >> 1); - temp0R = ((ma_int32)(mid0 - side0) >> 1); - temp1R = ((ma_int32)(mid1 - side1) >> 1); - temp2R = ((ma_int32)(mid2 - side2) >> 1); - temp3R = ((ma_int32)(mid3 - side3) >> 1); - temp0L >>= 16; - temp1L >>= 16; - temp2L >>= 16; - temp3L >>= 16; - temp0R >>= 16; - temp1R >>= 16; - temp2R >>= 16; - temp3R >>= 16; - pOutputSamples[i*8+0] = (ma_int16)temp0L; - pOutputSamples[i*8+1] = (ma_int16)temp0R; - pOutputSamples[i*8+2] = (ma_int16)temp1L; - pOutputSamples[i*8+3] = (ma_int16)temp1R; - pOutputSamples[i*8+4] = (ma_int16)temp2L; - pOutputSamples[i*8+5] = (ma_int16)temp2R; - pOutputSamples[i*8+6] = (ma_int16)temp3L; - pOutputSamples[i*8+7] = (ma_int16)temp3R; - } - } - for (i = (frameCount4 << 2); i < frameCount; ++i) { - ma_uint32 mid = pInputSamples0U32[i] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; - ma_uint32 side = pInputSamples1U32[i] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; - mid = (mid << 1) | (side & 0x01); - pOutputSamples[i*2+0] = (ma_int16)(((ma_uint32)((ma_int32)(mid + side) >> 1) << unusedBitsPerSample) >> 16); - pOutputSamples[i*2+1] = (ma_int16)(((ma_uint32)((ma_int32)(mid - side) >> 1) << unusedBitsPerSample) >> 16); - } -} -#if defined(MA_DR_FLAC_SUPPORT_SSE2) -static MA_INLINE void ma_dr_flac_read_pcm_frames_s16__decode_mid_side__sse2(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, ma_int16* pOutputSamples) -{ - ma_uint64 i; - ma_uint64 frameCount4 = frameCount >> 2; - const ma_uint32* pInputSamples0U32 = (const ma_uint32*)pInputSamples0; - const ma_uint32* pInputSamples1U32 = (const ma_uint32*)pInputSamples1; - ma_uint32 shift = unusedBitsPerSample; - MA_DR_FLAC_ASSERT(pFlac->bitsPerSample <= 24); - if (shift == 0) { - for (i = 0; i < frameCount4; ++i) { - __m128i mid; - __m128i side; - __m128i left; - __m128i right; - mid = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples0 + i), pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample); - side = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples1 + i), pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample); - mid = _mm_or_si128(_mm_slli_epi32(mid, 1), _mm_and_si128(side, _mm_set1_epi32(0x01))); - left = _mm_srai_epi32(_mm_add_epi32(mid, side), 1); - right = _mm_srai_epi32(_mm_sub_epi32(mid, side), 1); - left = _mm_srai_epi32(left, 16); - right = _mm_srai_epi32(right, 16); - _mm_storeu_si128((__m128i*)(pOutputSamples + i*8), ma_dr_flac__mm_packs_interleaved_epi32(left, right)); - } - for (i = (frameCount4 << 2); i < frameCount; ++i) { - ma_uint32 mid = pInputSamples0U32[i] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; - ma_uint32 side = pInputSamples1U32[i] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; - mid = (mid << 1) | (side & 0x01); - pOutputSamples[i*2+0] = (ma_int16)(((ma_int32)(mid + side) >> 1) >> 16); - pOutputSamples[i*2+1] = (ma_int16)(((ma_int32)(mid - side) >> 1) >> 16); - } - } else { - shift -= 1; - for (i = 0; i < frameCount4; ++i) { - __m128i mid; - __m128i side; - __m128i left; - __m128i right; - mid = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples0 + i), pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample); - side = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples1 + i), pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample); - mid = _mm_or_si128(_mm_slli_epi32(mid, 1), _mm_and_si128(side, _mm_set1_epi32(0x01))); - left = _mm_slli_epi32(_mm_add_epi32(mid, side), shift); - right = _mm_slli_epi32(_mm_sub_epi32(mid, side), shift); - left = _mm_srai_epi32(left, 16); - right = _mm_srai_epi32(right, 16); - _mm_storeu_si128((__m128i*)(pOutputSamples + i*8), ma_dr_flac__mm_packs_interleaved_epi32(left, right)); - } - for (i = (frameCount4 << 2); i < frameCount; ++i) { - ma_uint32 mid = pInputSamples0U32[i] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; - ma_uint32 side = pInputSamples1U32[i] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; - mid = (mid << 1) | (side & 0x01); - pOutputSamples[i*2+0] = (ma_int16)(((mid + side) << shift) >> 16); - pOutputSamples[i*2+1] = (ma_int16)(((mid - side) << shift) >> 16); - } - } -} -#endif -#if defined(MA_DR_FLAC_SUPPORT_NEON) -static MA_INLINE void ma_dr_flac_read_pcm_frames_s16__decode_mid_side__neon(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, ma_int16* pOutputSamples) -{ - ma_uint64 i; - ma_uint64 frameCount4 = frameCount >> 2; - const ma_uint32* pInputSamples0U32 = (const ma_uint32*)pInputSamples0; - const ma_uint32* pInputSamples1U32 = (const ma_uint32*)pInputSamples1; - ma_uint32 shift = unusedBitsPerSample; - int32x4_t wbpsShift0_4; - int32x4_t wbpsShift1_4; - MA_DR_FLAC_ASSERT(pFlac->bitsPerSample <= 24); - wbpsShift0_4 = vdupq_n_s32(pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample); - wbpsShift1_4 = vdupq_n_s32(pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample); - if (shift == 0) { - for (i = 0; i < frameCount4; ++i) { - uint32x4_t mid; - uint32x4_t side; - int32x4_t left; - int32x4_t right; - mid = vshlq_u32(vld1q_u32(pInputSamples0U32 + i*4), wbpsShift0_4); - side = vshlq_u32(vld1q_u32(pInputSamples1U32 + i*4), wbpsShift1_4); - mid = vorrq_u32(vshlq_n_u32(mid, 1), vandq_u32(side, vdupq_n_u32(1))); - left = vshrq_n_s32(vreinterpretq_s32_u32(vaddq_u32(mid, side)), 1); - right = vshrq_n_s32(vreinterpretq_s32_u32(vsubq_u32(mid, side)), 1); - left = vshrq_n_s32(left, 16); - right = vshrq_n_s32(right, 16); - ma_dr_flac__vst2q_s16(pOutputSamples + i*8, vzip_s16(vmovn_s32(left), vmovn_s32(right))); - } - for (i = (frameCount4 << 2); i < frameCount; ++i) { - ma_uint32 mid = pInputSamples0U32[i] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; - ma_uint32 side = pInputSamples1U32[i] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; - mid = (mid << 1) | (side & 0x01); - pOutputSamples[i*2+0] = (ma_int16)(((ma_int32)(mid + side) >> 1) >> 16); - pOutputSamples[i*2+1] = (ma_int16)(((ma_int32)(mid - side) >> 1) >> 16); - } - } else { - int32x4_t shift4; - shift -= 1; - shift4 = vdupq_n_s32(shift); - for (i = 0; i < frameCount4; ++i) { - uint32x4_t mid; - uint32x4_t side; - int32x4_t left; - int32x4_t right; - mid = vshlq_u32(vld1q_u32(pInputSamples0U32 + i*4), wbpsShift0_4); - side = vshlq_u32(vld1q_u32(pInputSamples1U32 + i*4), wbpsShift1_4); - mid = vorrq_u32(vshlq_n_u32(mid, 1), vandq_u32(side, vdupq_n_u32(1))); - left = vreinterpretq_s32_u32(vshlq_u32(vaddq_u32(mid, side), shift4)); - right = vreinterpretq_s32_u32(vshlq_u32(vsubq_u32(mid, side), shift4)); - left = vshrq_n_s32(left, 16); - right = vshrq_n_s32(right, 16); - ma_dr_flac__vst2q_s16(pOutputSamples + i*8, vzip_s16(vmovn_s32(left), vmovn_s32(right))); - } - for (i = (frameCount4 << 2); i < frameCount; ++i) { - ma_uint32 mid = pInputSamples0U32[i] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; - ma_uint32 side = pInputSamples1U32[i] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; - mid = (mid << 1) | (side & 0x01); - pOutputSamples[i*2+0] = (ma_int16)(((mid + side) << shift) >> 16); - pOutputSamples[i*2+1] = (ma_int16)(((mid - side) << shift) >> 16); - } - } -} -#endif -static MA_INLINE void ma_dr_flac_read_pcm_frames_s16__decode_mid_side(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, ma_int16* pOutputSamples) -{ -#if defined(MA_DR_FLAC_SUPPORT_SSE2) - if (ma_dr_flac__gIsSSE2Supported && pFlac->bitsPerSample <= 24) { - ma_dr_flac_read_pcm_frames_s16__decode_mid_side__sse2(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); - } else -#elif defined(MA_DR_FLAC_SUPPORT_NEON) - if (ma_dr_flac__gIsNEONSupported && pFlac->bitsPerSample <= 24) { - ma_dr_flac_read_pcm_frames_s16__decode_mid_side__neon(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); - } else -#endif - { -#if 0 - ma_dr_flac_read_pcm_frames_s16__decode_mid_side__reference(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); -#else - ma_dr_flac_read_pcm_frames_s16__decode_mid_side__scalar(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); -#endif - } -} -#if 0 -static MA_INLINE void ma_dr_flac_read_pcm_frames_s16__decode_independent_stereo__reference(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, ma_int16* pOutputSamples) -{ - for (ma_uint64 i = 0; i < frameCount; ++i) { - pOutputSamples[i*2+0] = (ma_int16)((ma_int32)((ma_uint32)pInputSamples0[i] << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample)) >> 16); - pOutputSamples[i*2+1] = (ma_int16)((ma_int32)((ma_uint32)pInputSamples1[i] << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample)) >> 16); - } -} -#endif -static MA_INLINE void ma_dr_flac_read_pcm_frames_s16__decode_independent_stereo__scalar(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, ma_int16* pOutputSamples) -{ - ma_uint64 i; - ma_uint64 frameCount4 = frameCount >> 2; - const ma_uint32* pInputSamples0U32 = (const ma_uint32*)pInputSamples0; - const ma_uint32* pInputSamples1U32 = (const ma_uint32*)pInputSamples1; - ma_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; - ma_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; - for (i = 0; i < frameCount4; ++i) { - ma_uint32 tempL0 = pInputSamples0U32[i*4+0] << shift0; - ma_uint32 tempL1 = pInputSamples0U32[i*4+1] << shift0; - ma_uint32 tempL2 = pInputSamples0U32[i*4+2] << shift0; - ma_uint32 tempL3 = pInputSamples0U32[i*4+3] << shift0; - ma_uint32 tempR0 = pInputSamples1U32[i*4+0] << shift1; - ma_uint32 tempR1 = pInputSamples1U32[i*4+1] << shift1; - ma_uint32 tempR2 = pInputSamples1U32[i*4+2] << shift1; - ma_uint32 tempR3 = pInputSamples1U32[i*4+3] << shift1; - tempL0 >>= 16; - tempL1 >>= 16; - tempL2 >>= 16; - tempL3 >>= 16; - tempR0 >>= 16; - tempR1 >>= 16; - tempR2 >>= 16; - tempR3 >>= 16; - pOutputSamples[i*8+0] = (ma_int16)tempL0; - pOutputSamples[i*8+1] = (ma_int16)tempR0; - pOutputSamples[i*8+2] = (ma_int16)tempL1; - pOutputSamples[i*8+3] = (ma_int16)tempR1; - pOutputSamples[i*8+4] = (ma_int16)tempL2; - pOutputSamples[i*8+5] = (ma_int16)tempR2; - pOutputSamples[i*8+6] = (ma_int16)tempL3; - pOutputSamples[i*8+7] = (ma_int16)tempR3; - } - for (i = (frameCount4 << 2); i < frameCount; ++i) { - pOutputSamples[i*2+0] = (ma_int16)((pInputSamples0U32[i] << shift0) >> 16); - pOutputSamples[i*2+1] = (ma_int16)((pInputSamples1U32[i] << shift1) >> 16); - } -} -#if defined(MA_DR_FLAC_SUPPORT_SSE2) -static MA_INLINE void ma_dr_flac_read_pcm_frames_s16__decode_independent_stereo__sse2(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, ma_int16* pOutputSamples) -{ - ma_uint64 i; - ma_uint64 frameCount4 = frameCount >> 2; - const ma_uint32* pInputSamples0U32 = (const ma_uint32*)pInputSamples0; - const ma_uint32* pInputSamples1U32 = (const ma_uint32*)pInputSamples1; - ma_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; - ma_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; - for (i = 0; i < frameCount4; ++i) { - __m128i left = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples0 + i), shift0); - __m128i right = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples1 + i), shift1); - left = _mm_srai_epi32(left, 16); - right = _mm_srai_epi32(right, 16); - _mm_storeu_si128((__m128i*)(pOutputSamples + i*8), ma_dr_flac__mm_packs_interleaved_epi32(left, right)); - } - for (i = (frameCount4 << 2); i < frameCount; ++i) { - pOutputSamples[i*2+0] = (ma_int16)((pInputSamples0U32[i] << shift0) >> 16); - pOutputSamples[i*2+1] = (ma_int16)((pInputSamples1U32[i] << shift1) >> 16); - } -} -#endif -#if defined(MA_DR_FLAC_SUPPORT_NEON) -static MA_INLINE void ma_dr_flac_read_pcm_frames_s16__decode_independent_stereo__neon(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, ma_int16* pOutputSamples) -{ - ma_uint64 i; - ma_uint64 frameCount4 = frameCount >> 2; - const ma_uint32* pInputSamples0U32 = (const ma_uint32*)pInputSamples0; - const ma_uint32* pInputSamples1U32 = (const ma_uint32*)pInputSamples1; - ma_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; - ma_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; - int32x4_t shift0_4 = vdupq_n_s32(shift0); - int32x4_t shift1_4 = vdupq_n_s32(shift1); - for (i = 0; i < frameCount4; ++i) { - int32x4_t left; - int32x4_t right; - left = vreinterpretq_s32_u32(vshlq_u32(vld1q_u32(pInputSamples0U32 + i*4), shift0_4)); - right = vreinterpretq_s32_u32(vshlq_u32(vld1q_u32(pInputSamples1U32 + i*4), shift1_4)); - left = vshrq_n_s32(left, 16); - right = vshrq_n_s32(right, 16); - ma_dr_flac__vst2q_s16(pOutputSamples + i*8, vzip_s16(vmovn_s32(left), vmovn_s32(right))); - } - for (i = (frameCount4 << 2); i < frameCount; ++i) { - pOutputSamples[i*2+0] = (ma_int16)((pInputSamples0U32[i] << shift0) >> 16); - pOutputSamples[i*2+1] = (ma_int16)((pInputSamples1U32[i] << shift1) >> 16); - } -} -#endif -static MA_INLINE void ma_dr_flac_read_pcm_frames_s16__decode_independent_stereo(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, ma_int16* pOutputSamples) -{ -#if defined(MA_DR_FLAC_SUPPORT_SSE2) - if (ma_dr_flac__gIsSSE2Supported && pFlac->bitsPerSample <= 24) { - ma_dr_flac_read_pcm_frames_s16__decode_independent_stereo__sse2(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); - } else -#elif defined(MA_DR_FLAC_SUPPORT_NEON) - if (ma_dr_flac__gIsNEONSupported && pFlac->bitsPerSample <= 24) { - ma_dr_flac_read_pcm_frames_s16__decode_independent_stereo__neon(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); - } else -#endif - { -#if 0 - ma_dr_flac_read_pcm_frames_s16__decode_independent_stereo__reference(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); -#else - ma_dr_flac_read_pcm_frames_s16__decode_independent_stereo__scalar(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); -#endif - } -} -MA_API ma_uint64 ma_dr_flac_read_pcm_frames_s16(ma_dr_flac* pFlac, ma_uint64 framesToRead, ma_int16* pBufferOut) -{ - ma_uint64 framesRead; - ma_uint32 unusedBitsPerSample; - if (pFlac == NULL || framesToRead == 0) { - return 0; - } - if (pBufferOut == NULL) { - return ma_dr_flac__seek_forward_by_pcm_frames(pFlac, framesToRead); - } - MA_DR_FLAC_ASSERT(pFlac->bitsPerSample <= 32); - unusedBitsPerSample = 32 - pFlac->bitsPerSample; - framesRead = 0; - while (framesToRead > 0) { - if (pFlac->currentFLACFrame.pcmFramesRemaining == 0) { - if (!ma_dr_flac__read_and_decode_next_flac_frame(pFlac)) { - break; - } - } else { - unsigned int channelCount = ma_dr_flac__get_channel_count_from_channel_assignment(pFlac->currentFLACFrame.header.channelAssignment); - ma_uint64 iFirstPCMFrame = pFlac->currentFLACFrame.header.blockSizeInPCMFrames - pFlac->currentFLACFrame.pcmFramesRemaining; - ma_uint64 frameCountThisIteration = framesToRead; - if (frameCountThisIteration > pFlac->currentFLACFrame.pcmFramesRemaining) { - frameCountThisIteration = pFlac->currentFLACFrame.pcmFramesRemaining; - } - if (channelCount == 2) { - const ma_int32* pDecodedSamples0 = pFlac->currentFLACFrame.subframes[0].pSamplesS32 + iFirstPCMFrame; - const ma_int32* pDecodedSamples1 = pFlac->currentFLACFrame.subframes[1].pSamplesS32 + iFirstPCMFrame; - switch (pFlac->currentFLACFrame.header.channelAssignment) - { - case MA_DR_FLAC_CHANNEL_ASSIGNMENT_LEFT_SIDE: - { - ma_dr_flac_read_pcm_frames_s16__decode_left_side(pFlac, frameCountThisIteration, unusedBitsPerSample, pDecodedSamples0, pDecodedSamples1, pBufferOut); - } break; - case MA_DR_FLAC_CHANNEL_ASSIGNMENT_RIGHT_SIDE: - { - ma_dr_flac_read_pcm_frames_s16__decode_right_side(pFlac, frameCountThisIteration, unusedBitsPerSample, pDecodedSamples0, pDecodedSamples1, pBufferOut); - } break; - case MA_DR_FLAC_CHANNEL_ASSIGNMENT_MID_SIDE: - { - ma_dr_flac_read_pcm_frames_s16__decode_mid_side(pFlac, frameCountThisIteration, unusedBitsPerSample, pDecodedSamples0, pDecodedSamples1, pBufferOut); - } break; - case MA_DR_FLAC_CHANNEL_ASSIGNMENT_INDEPENDENT: - default: - { - ma_dr_flac_read_pcm_frames_s16__decode_independent_stereo(pFlac, frameCountThisIteration, unusedBitsPerSample, pDecodedSamples0, pDecodedSamples1, pBufferOut); - } break; - } - } else { - ma_uint64 i; - for (i = 0; i < frameCountThisIteration; ++i) { - unsigned int j; - for (j = 0; j < channelCount; ++j) { - ma_int32 sampleS32 = (ma_int32)((ma_uint32)(pFlac->currentFLACFrame.subframes[j].pSamplesS32[iFirstPCMFrame + i]) << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[j].wastedBitsPerSample)); - pBufferOut[(i*channelCount)+j] = (ma_int16)(sampleS32 >> 16); - } - } - } - framesRead += frameCountThisIteration; - pBufferOut += frameCountThisIteration * channelCount; - framesToRead -= frameCountThisIteration; - pFlac->currentPCMFrame += frameCountThisIteration; - pFlac->currentFLACFrame.pcmFramesRemaining -= (ma_uint32)frameCountThisIteration; - } - } - return framesRead; -} -#if 0 -static MA_INLINE void ma_dr_flac_read_pcm_frames_f32__decode_left_side__reference(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, float* pOutputSamples) -{ - ma_uint64 i; - for (i = 0; i < frameCount; ++i) { - ma_uint32 left = (ma_uint32)pInputSamples0[i] << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample); - ma_uint32 side = (ma_uint32)pInputSamples1[i] << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample); - ma_uint32 right = left - side; - pOutputSamples[i*2+0] = (float)((ma_int32)left / 2147483648.0); - pOutputSamples[i*2+1] = (float)((ma_int32)right / 2147483648.0); - } -} -#endif -static MA_INLINE void ma_dr_flac_read_pcm_frames_f32__decode_left_side__scalar(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, float* pOutputSamples) -{ - ma_uint64 i; - ma_uint64 frameCount4 = frameCount >> 2; - const ma_uint32* pInputSamples0U32 = (const ma_uint32*)pInputSamples0; - const ma_uint32* pInputSamples1U32 = (const ma_uint32*)pInputSamples1; - ma_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; - ma_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; - float factor = 1 / 2147483648.0; - for (i = 0; i < frameCount4; ++i) { - ma_uint32 left0 = pInputSamples0U32[i*4+0] << shift0; - ma_uint32 left1 = pInputSamples0U32[i*4+1] << shift0; - ma_uint32 left2 = pInputSamples0U32[i*4+2] << shift0; - ma_uint32 left3 = pInputSamples0U32[i*4+3] << shift0; - ma_uint32 side0 = pInputSamples1U32[i*4+0] << shift1; - ma_uint32 side1 = pInputSamples1U32[i*4+1] << shift1; - ma_uint32 side2 = pInputSamples1U32[i*4+2] << shift1; - ma_uint32 side3 = pInputSamples1U32[i*4+3] << shift1; - ma_uint32 right0 = left0 - side0; - ma_uint32 right1 = left1 - side1; - ma_uint32 right2 = left2 - side2; - ma_uint32 right3 = left3 - side3; - pOutputSamples[i*8+0] = (ma_int32)left0 * factor; - pOutputSamples[i*8+1] = (ma_int32)right0 * factor; - pOutputSamples[i*8+2] = (ma_int32)left1 * factor; - pOutputSamples[i*8+3] = (ma_int32)right1 * factor; - pOutputSamples[i*8+4] = (ma_int32)left2 * factor; - pOutputSamples[i*8+5] = (ma_int32)right2 * factor; - pOutputSamples[i*8+6] = (ma_int32)left3 * factor; - pOutputSamples[i*8+7] = (ma_int32)right3 * factor; - } - for (i = (frameCount4 << 2); i < frameCount; ++i) { - ma_uint32 left = pInputSamples0U32[i] << shift0; - ma_uint32 side = pInputSamples1U32[i] << shift1; - ma_uint32 right = left - side; - pOutputSamples[i*2+0] = (ma_int32)left * factor; - pOutputSamples[i*2+1] = (ma_int32)right * factor; - } -} -#if defined(MA_DR_FLAC_SUPPORT_SSE2) -static MA_INLINE void ma_dr_flac_read_pcm_frames_f32__decode_left_side__sse2(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, float* pOutputSamples) -{ - ma_uint64 i; - ma_uint64 frameCount4 = frameCount >> 2; - const ma_uint32* pInputSamples0U32 = (const ma_uint32*)pInputSamples0; - const ma_uint32* pInputSamples1U32 = (const ma_uint32*)pInputSamples1; - ma_uint32 shift0 = (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample) - 8; - ma_uint32 shift1 = (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample) - 8; - __m128 factor; - MA_DR_FLAC_ASSERT(pFlac->bitsPerSample <= 24); - factor = _mm_set1_ps(1.0f / 8388608.0f); - for (i = 0; i < frameCount4; ++i) { - __m128i left = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples0 + i), shift0); - __m128i side = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples1 + i), shift1); - __m128i right = _mm_sub_epi32(left, side); - __m128 leftf = _mm_mul_ps(_mm_cvtepi32_ps(left), factor); - __m128 rightf = _mm_mul_ps(_mm_cvtepi32_ps(right), factor); - _mm_storeu_ps(pOutputSamples + i*8 + 0, _mm_unpacklo_ps(leftf, rightf)); - _mm_storeu_ps(pOutputSamples + i*8 + 4, _mm_unpackhi_ps(leftf, rightf)); - } - for (i = (frameCount4 << 2); i < frameCount; ++i) { - ma_uint32 left = pInputSamples0U32[i] << shift0; - ma_uint32 side = pInputSamples1U32[i] << shift1; - ma_uint32 right = left - side; - pOutputSamples[i*2+0] = (ma_int32)left / 8388608.0f; - pOutputSamples[i*2+1] = (ma_int32)right / 8388608.0f; - } -} -#endif -#if defined(MA_DR_FLAC_SUPPORT_NEON) -static MA_INLINE void ma_dr_flac_read_pcm_frames_f32__decode_left_side__neon(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, float* pOutputSamples) -{ - ma_uint64 i; - ma_uint64 frameCount4 = frameCount >> 2; - const ma_uint32* pInputSamples0U32 = (const ma_uint32*)pInputSamples0; - const ma_uint32* pInputSamples1U32 = (const ma_uint32*)pInputSamples1; - ma_uint32 shift0 = (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample) - 8; - ma_uint32 shift1 = (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample) - 8; - float32x4_t factor4; - int32x4_t shift0_4; - int32x4_t shift1_4; - MA_DR_FLAC_ASSERT(pFlac->bitsPerSample <= 24); - factor4 = vdupq_n_f32(1.0f / 8388608.0f); - shift0_4 = vdupq_n_s32(shift0); - shift1_4 = vdupq_n_s32(shift1); - for (i = 0; i < frameCount4; ++i) { - uint32x4_t left; - uint32x4_t side; - uint32x4_t right; - float32x4_t leftf; - float32x4_t rightf; - left = vshlq_u32(vld1q_u32(pInputSamples0U32 + i*4), shift0_4); - side = vshlq_u32(vld1q_u32(pInputSamples1U32 + i*4), shift1_4); - right = vsubq_u32(left, side); - leftf = vmulq_f32(vcvtq_f32_s32(vreinterpretq_s32_u32(left)), factor4); - rightf = vmulq_f32(vcvtq_f32_s32(vreinterpretq_s32_u32(right)), factor4); - ma_dr_flac__vst2q_f32(pOutputSamples + i*8, vzipq_f32(leftf, rightf)); - } - for (i = (frameCount4 << 2); i < frameCount; ++i) { - ma_uint32 left = pInputSamples0U32[i] << shift0; - ma_uint32 side = pInputSamples1U32[i] << shift1; - ma_uint32 right = left - side; - pOutputSamples[i*2+0] = (ma_int32)left / 8388608.0f; - pOutputSamples[i*2+1] = (ma_int32)right / 8388608.0f; - } -} -#endif -static MA_INLINE void ma_dr_flac_read_pcm_frames_f32__decode_left_side(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, float* pOutputSamples) -{ -#if defined(MA_DR_FLAC_SUPPORT_SSE2) - if (ma_dr_flac__gIsSSE2Supported && pFlac->bitsPerSample <= 24) { - ma_dr_flac_read_pcm_frames_f32__decode_left_side__sse2(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); - } else -#elif defined(MA_DR_FLAC_SUPPORT_NEON) - if (ma_dr_flac__gIsNEONSupported && pFlac->bitsPerSample <= 24) { - ma_dr_flac_read_pcm_frames_f32__decode_left_side__neon(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); - } else -#endif - { -#if 0 - ma_dr_flac_read_pcm_frames_f32__decode_left_side__reference(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); -#else - ma_dr_flac_read_pcm_frames_f32__decode_left_side__scalar(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); -#endif - } -} -#if 0 -static MA_INLINE void ma_dr_flac_read_pcm_frames_f32__decode_right_side__reference(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, float* pOutputSamples) -{ - ma_uint64 i; - for (i = 0; i < frameCount; ++i) { - ma_uint32 side = (ma_uint32)pInputSamples0[i] << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample); - ma_uint32 right = (ma_uint32)pInputSamples1[i] << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample); - ma_uint32 left = right + side; - pOutputSamples[i*2+0] = (float)((ma_int32)left / 2147483648.0); - pOutputSamples[i*2+1] = (float)((ma_int32)right / 2147483648.0); - } -} -#endif -static MA_INLINE void ma_dr_flac_read_pcm_frames_f32__decode_right_side__scalar(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, float* pOutputSamples) -{ - ma_uint64 i; - ma_uint64 frameCount4 = frameCount >> 2; - const ma_uint32* pInputSamples0U32 = (const ma_uint32*)pInputSamples0; - const ma_uint32* pInputSamples1U32 = (const ma_uint32*)pInputSamples1; - ma_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; - ma_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; - float factor = 1 / 2147483648.0; - for (i = 0; i < frameCount4; ++i) { - ma_uint32 side0 = pInputSamples0U32[i*4+0] << shift0; - ma_uint32 side1 = pInputSamples0U32[i*4+1] << shift0; - ma_uint32 side2 = pInputSamples0U32[i*4+2] << shift0; - ma_uint32 side3 = pInputSamples0U32[i*4+3] << shift0; - ma_uint32 right0 = pInputSamples1U32[i*4+0] << shift1; - ma_uint32 right1 = pInputSamples1U32[i*4+1] << shift1; - ma_uint32 right2 = pInputSamples1U32[i*4+2] << shift1; - ma_uint32 right3 = pInputSamples1U32[i*4+3] << shift1; - ma_uint32 left0 = right0 + side0; - ma_uint32 left1 = right1 + side1; - ma_uint32 left2 = right2 + side2; - ma_uint32 left3 = right3 + side3; - pOutputSamples[i*8+0] = (ma_int32)left0 * factor; - pOutputSamples[i*8+1] = (ma_int32)right0 * factor; - pOutputSamples[i*8+2] = (ma_int32)left1 * factor; - pOutputSamples[i*8+3] = (ma_int32)right1 * factor; - pOutputSamples[i*8+4] = (ma_int32)left2 * factor; - pOutputSamples[i*8+5] = (ma_int32)right2 * factor; - pOutputSamples[i*8+6] = (ma_int32)left3 * factor; - pOutputSamples[i*8+7] = (ma_int32)right3 * factor; - } - for (i = (frameCount4 << 2); i < frameCount; ++i) { - ma_uint32 side = pInputSamples0U32[i] << shift0; - ma_uint32 right = pInputSamples1U32[i] << shift1; - ma_uint32 left = right + side; - pOutputSamples[i*2+0] = (ma_int32)left * factor; - pOutputSamples[i*2+1] = (ma_int32)right * factor; - } -} -#if defined(MA_DR_FLAC_SUPPORT_SSE2) -static MA_INLINE void ma_dr_flac_read_pcm_frames_f32__decode_right_side__sse2(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, float* pOutputSamples) -{ - ma_uint64 i; - ma_uint64 frameCount4 = frameCount >> 2; - const ma_uint32* pInputSamples0U32 = (const ma_uint32*)pInputSamples0; - const ma_uint32* pInputSamples1U32 = (const ma_uint32*)pInputSamples1; - ma_uint32 shift0 = (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample) - 8; - ma_uint32 shift1 = (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample) - 8; - __m128 factor; - MA_DR_FLAC_ASSERT(pFlac->bitsPerSample <= 24); - factor = _mm_set1_ps(1.0f / 8388608.0f); - for (i = 0; i < frameCount4; ++i) { - __m128i side = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples0 + i), shift0); - __m128i right = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples1 + i), shift1); - __m128i left = _mm_add_epi32(right, side); - __m128 leftf = _mm_mul_ps(_mm_cvtepi32_ps(left), factor); - __m128 rightf = _mm_mul_ps(_mm_cvtepi32_ps(right), factor); - _mm_storeu_ps(pOutputSamples + i*8 + 0, _mm_unpacklo_ps(leftf, rightf)); - _mm_storeu_ps(pOutputSamples + i*8 + 4, _mm_unpackhi_ps(leftf, rightf)); - } - for (i = (frameCount4 << 2); i < frameCount; ++i) { - ma_uint32 side = pInputSamples0U32[i] << shift0; - ma_uint32 right = pInputSamples1U32[i] << shift1; - ma_uint32 left = right + side; - pOutputSamples[i*2+0] = (ma_int32)left / 8388608.0f; - pOutputSamples[i*2+1] = (ma_int32)right / 8388608.0f; - } -} -#endif -#if defined(MA_DR_FLAC_SUPPORT_NEON) -static MA_INLINE void ma_dr_flac_read_pcm_frames_f32__decode_right_side__neon(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, float* pOutputSamples) -{ - ma_uint64 i; - ma_uint64 frameCount4 = frameCount >> 2; - const ma_uint32* pInputSamples0U32 = (const ma_uint32*)pInputSamples0; - const ma_uint32* pInputSamples1U32 = (const ma_uint32*)pInputSamples1; - ma_uint32 shift0 = (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample) - 8; - ma_uint32 shift1 = (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample) - 8; - float32x4_t factor4; - int32x4_t shift0_4; - int32x4_t shift1_4; - MA_DR_FLAC_ASSERT(pFlac->bitsPerSample <= 24); - factor4 = vdupq_n_f32(1.0f / 8388608.0f); - shift0_4 = vdupq_n_s32(shift0); - shift1_4 = vdupq_n_s32(shift1); - for (i = 0; i < frameCount4; ++i) { - uint32x4_t side; - uint32x4_t right; - uint32x4_t left; - float32x4_t leftf; - float32x4_t rightf; - side = vshlq_u32(vld1q_u32(pInputSamples0U32 + i*4), shift0_4); - right = vshlq_u32(vld1q_u32(pInputSamples1U32 + i*4), shift1_4); - left = vaddq_u32(right, side); - leftf = vmulq_f32(vcvtq_f32_s32(vreinterpretq_s32_u32(left)), factor4); - rightf = vmulq_f32(vcvtq_f32_s32(vreinterpretq_s32_u32(right)), factor4); - ma_dr_flac__vst2q_f32(pOutputSamples + i*8, vzipq_f32(leftf, rightf)); - } - for (i = (frameCount4 << 2); i < frameCount; ++i) { - ma_uint32 side = pInputSamples0U32[i] << shift0; - ma_uint32 right = pInputSamples1U32[i] << shift1; - ma_uint32 left = right + side; - pOutputSamples[i*2+0] = (ma_int32)left / 8388608.0f; - pOutputSamples[i*2+1] = (ma_int32)right / 8388608.0f; - } -} -#endif -static MA_INLINE void ma_dr_flac_read_pcm_frames_f32__decode_right_side(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, float* pOutputSamples) -{ -#if defined(MA_DR_FLAC_SUPPORT_SSE2) - if (ma_dr_flac__gIsSSE2Supported && pFlac->bitsPerSample <= 24) { - ma_dr_flac_read_pcm_frames_f32__decode_right_side__sse2(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); - } else -#elif defined(MA_DR_FLAC_SUPPORT_NEON) - if (ma_dr_flac__gIsNEONSupported && pFlac->bitsPerSample <= 24) { - ma_dr_flac_read_pcm_frames_f32__decode_right_side__neon(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); - } else -#endif - { -#if 0 - ma_dr_flac_read_pcm_frames_f32__decode_right_side__reference(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); -#else - ma_dr_flac_read_pcm_frames_f32__decode_right_side__scalar(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); -#endif - } -} -#if 0 -static MA_INLINE void ma_dr_flac_read_pcm_frames_f32__decode_mid_side__reference(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, float* pOutputSamples) -{ - for (ma_uint64 i = 0; i < frameCount; ++i) { - ma_uint32 mid = (ma_uint32)pInputSamples0[i] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; - ma_uint32 side = (ma_uint32)pInputSamples1[i] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; - mid = (mid << 1) | (side & 0x01); - pOutputSamples[i*2+0] = (float)((((ma_int32)(mid + side) >> 1) << (unusedBitsPerSample)) / 2147483648.0); - pOutputSamples[i*2+1] = (float)((((ma_int32)(mid - side) >> 1) << (unusedBitsPerSample)) / 2147483648.0); - } -} -#endif -static MA_INLINE void ma_dr_flac_read_pcm_frames_f32__decode_mid_side__scalar(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, float* pOutputSamples) -{ - ma_uint64 i; - ma_uint64 frameCount4 = frameCount >> 2; - const ma_uint32* pInputSamples0U32 = (const ma_uint32*)pInputSamples0; - const ma_uint32* pInputSamples1U32 = (const ma_uint32*)pInputSamples1; - ma_uint32 shift = unusedBitsPerSample; - float factor = 1 / 2147483648.0; - if (shift > 0) { - shift -= 1; - for (i = 0; i < frameCount4; ++i) { - ma_uint32 temp0L; - ma_uint32 temp1L; - ma_uint32 temp2L; - ma_uint32 temp3L; - ma_uint32 temp0R; - ma_uint32 temp1R; - ma_uint32 temp2R; - ma_uint32 temp3R; - ma_uint32 mid0 = pInputSamples0U32[i*4+0] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; - ma_uint32 mid1 = pInputSamples0U32[i*4+1] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; - ma_uint32 mid2 = pInputSamples0U32[i*4+2] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; - ma_uint32 mid3 = pInputSamples0U32[i*4+3] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; - ma_uint32 side0 = pInputSamples1U32[i*4+0] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; - ma_uint32 side1 = pInputSamples1U32[i*4+1] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; - ma_uint32 side2 = pInputSamples1U32[i*4+2] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; - ma_uint32 side3 = pInputSamples1U32[i*4+3] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; - mid0 = (mid0 << 1) | (side0 & 0x01); - mid1 = (mid1 << 1) | (side1 & 0x01); - mid2 = (mid2 << 1) | (side2 & 0x01); - mid3 = (mid3 << 1) | (side3 & 0x01); - temp0L = (mid0 + side0) << shift; - temp1L = (mid1 + side1) << shift; - temp2L = (mid2 + side2) << shift; - temp3L = (mid3 + side3) << shift; - temp0R = (mid0 - side0) << shift; - temp1R = (mid1 - side1) << shift; - temp2R = (mid2 - side2) << shift; - temp3R = (mid3 - side3) << shift; - pOutputSamples[i*8+0] = (ma_int32)temp0L * factor; - pOutputSamples[i*8+1] = (ma_int32)temp0R * factor; - pOutputSamples[i*8+2] = (ma_int32)temp1L * factor; - pOutputSamples[i*8+3] = (ma_int32)temp1R * factor; - pOutputSamples[i*8+4] = (ma_int32)temp2L * factor; - pOutputSamples[i*8+5] = (ma_int32)temp2R * factor; - pOutputSamples[i*8+6] = (ma_int32)temp3L * factor; - pOutputSamples[i*8+7] = (ma_int32)temp3R * factor; - } - } else { - for (i = 0; i < frameCount4; ++i) { - ma_uint32 temp0L; - ma_uint32 temp1L; - ma_uint32 temp2L; - ma_uint32 temp3L; - ma_uint32 temp0R; - ma_uint32 temp1R; - ma_uint32 temp2R; - ma_uint32 temp3R; - ma_uint32 mid0 = pInputSamples0U32[i*4+0] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; - ma_uint32 mid1 = pInputSamples0U32[i*4+1] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; - ma_uint32 mid2 = pInputSamples0U32[i*4+2] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; - ma_uint32 mid3 = pInputSamples0U32[i*4+3] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; - ma_uint32 side0 = pInputSamples1U32[i*4+0] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; - ma_uint32 side1 = pInputSamples1U32[i*4+1] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; - ma_uint32 side2 = pInputSamples1U32[i*4+2] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; - ma_uint32 side3 = pInputSamples1U32[i*4+3] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; - mid0 = (mid0 << 1) | (side0 & 0x01); - mid1 = (mid1 << 1) | (side1 & 0x01); - mid2 = (mid2 << 1) | (side2 & 0x01); - mid3 = (mid3 << 1) | (side3 & 0x01); - temp0L = (ma_uint32)((ma_int32)(mid0 + side0) >> 1); - temp1L = (ma_uint32)((ma_int32)(mid1 + side1) >> 1); - temp2L = (ma_uint32)((ma_int32)(mid2 + side2) >> 1); - temp3L = (ma_uint32)((ma_int32)(mid3 + side3) >> 1); - temp0R = (ma_uint32)((ma_int32)(mid0 - side0) >> 1); - temp1R = (ma_uint32)((ma_int32)(mid1 - side1) >> 1); - temp2R = (ma_uint32)((ma_int32)(mid2 - side2) >> 1); - temp3R = (ma_uint32)((ma_int32)(mid3 - side3) >> 1); - pOutputSamples[i*8+0] = (ma_int32)temp0L * factor; - pOutputSamples[i*8+1] = (ma_int32)temp0R * factor; - pOutputSamples[i*8+2] = (ma_int32)temp1L * factor; - pOutputSamples[i*8+3] = (ma_int32)temp1R * factor; - pOutputSamples[i*8+4] = (ma_int32)temp2L * factor; - pOutputSamples[i*8+5] = (ma_int32)temp2R * factor; - pOutputSamples[i*8+6] = (ma_int32)temp3L * factor; - pOutputSamples[i*8+7] = (ma_int32)temp3R * factor; - } - } - for (i = (frameCount4 << 2); i < frameCount; ++i) { - ma_uint32 mid = pInputSamples0U32[i] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; - ma_uint32 side = pInputSamples1U32[i] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; - mid = (mid << 1) | (side & 0x01); - pOutputSamples[i*2+0] = (ma_int32)((ma_uint32)((ma_int32)(mid + side) >> 1) << unusedBitsPerSample) * factor; - pOutputSamples[i*2+1] = (ma_int32)((ma_uint32)((ma_int32)(mid - side) >> 1) << unusedBitsPerSample) * factor; - } -} -#if defined(MA_DR_FLAC_SUPPORT_SSE2) -static MA_INLINE void ma_dr_flac_read_pcm_frames_f32__decode_mid_side__sse2(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, float* pOutputSamples) -{ - ma_uint64 i; - ma_uint64 frameCount4 = frameCount >> 2; - const ma_uint32* pInputSamples0U32 = (const ma_uint32*)pInputSamples0; - const ma_uint32* pInputSamples1U32 = (const ma_uint32*)pInputSamples1; - ma_uint32 shift = unusedBitsPerSample - 8; - float factor; - __m128 factor128; - MA_DR_FLAC_ASSERT(pFlac->bitsPerSample <= 24); - factor = 1.0f / 8388608.0f; - factor128 = _mm_set1_ps(factor); - if (shift == 0) { - for (i = 0; i < frameCount4; ++i) { - __m128i mid; - __m128i side; - __m128i tempL; - __m128i tempR; - __m128 leftf; - __m128 rightf; - mid = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples0 + i), pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample); - side = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples1 + i), pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample); - mid = _mm_or_si128(_mm_slli_epi32(mid, 1), _mm_and_si128(side, _mm_set1_epi32(0x01))); - tempL = _mm_srai_epi32(_mm_add_epi32(mid, side), 1); - tempR = _mm_srai_epi32(_mm_sub_epi32(mid, side), 1); - leftf = _mm_mul_ps(_mm_cvtepi32_ps(tempL), factor128); - rightf = _mm_mul_ps(_mm_cvtepi32_ps(tempR), factor128); - _mm_storeu_ps(pOutputSamples + i*8 + 0, _mm_unpacklo_ps(leftf, rightf)); - _mm_storeu_ps(pOutputSamples + i*8 + 4, _mm_unpackhi_ps(leftf, rightf)); - } - for (i = (frameCount4 << 2); i < frameCount; ++i) { - ma_uint32 mid = pInputSamples0U32[i] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; - ma_uint32 side = pInputSamples1U32[i] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; - mid = (mid << 1) | (side & 0x01); - pOutputSamples[i*2+0] = ((ma_int32)(mid + side) >> 1) * factor; - pOutputSamples[i*2+1] = ((ma_int32)(mid - side) >> 1) * factor; - } - } else { - shift -= 1; - for (i = 0; i < frameCount4; ++i) { - __m128i mid; - __m128i side; - __m128i tempL; - __m128i tempR; - __m128 leftf; - __m128 rightf; - mid = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples0 + i), pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample); - side = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples1 + i), pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample); - mid = _mm_or_si128(_mm_slli_epi32(mid, 1), _mm_and_si128(side, _mm_set1_epi32(0x01))); - tempL = _mm_slli_epi32(_mm_add_epi32(mid, side), shift); - tempR = _mm_slli_epi32(_mm_sub_epi32(mid, side), shift); - leftf = _mm_mul_ps(_mm_cvtepi32_ps(tempL), factor128); - rightf = _mm_mul_ps(_mm_cvtepi32_ps(tempR), factor128); - _mm_storeu_ps(pOutputSamples + i*8 + 0, _mm_unpacklo_ps(leftf, rightf)); - _mm_storeu_ps(pOutputSamples + i*8 + 4, _mm_unpackhi_ps(leftf, rightf)); - } - for (i = (frameCount4 << 2); i < frameCount; ++i) { - ma_uint32 mid = pInputSamples0U32[i] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; - ma_uint32 side = pInputSamples1U32[i] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; - mid = (mid << 1) | (side & 0x01); - pOutputSamples[i*2+0] = (ma_int32)((mid + side) << shift) * factor; - pOutputSamples[i*2+1] = (ma_int32)((mid - side) << shift) * factor; - } - } -} -#endif -#if defined(MA_DR_FLAC_SUPPORT_NEON) -static MA_INLINE void ma_dr_flac_read_pcm_frames_f32__decode_mid_side__neon(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, float* pOutputSamples) -{ - ma_uint64 i; - ma_uint64 frameCount4 = frameCount >> 2; - const ma_uint32* pInputSamples0U32 = (const ma_uint32*)pInputSamples0; - const ma_uint32* pInputSamples1U32 = (const ma_uint32*)pInputSamples1; - ma_uint32 shift = unusedBitsPerSample - 8; - float factor; - float32x4_t factor4; - int32x4_t shift4; - int32x4_t wbps0_4; - int32x4_t wbps1_4; - MA_DR_FLAC_ASSERT(pFlac->bitsPerSample <= 24); - factor = 1.0f / 8388608.0f; - factor4 = vdupq_n_f32(factor); - wbps0_4 = vdupq_n_s32(pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample); - wbps1_4 = vdupq_n_s32(pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample); - if (shift == 0) { - for (i = 0; i < frameCount4; ++i) { - int32x4_t lefti; - int32x4_t righti; - float32x4_t leftf; - float32x4_t rightf; - uint32x4_t mid = vshlq_u32(vld1q_u32(pInputSamples0U32 + i*4), wbps0_4); - uint32x4_t side = vshlq_u32(vld1q_u32(pInputSamples1U32 + i*4), wbps1_4); - mid = vorrq_u32(vshlq_n_u32(mid, 1), vandq_u32(side, vdupq_n_u32(1))); - lefti = vshrq_n_s32(vreinterpretq_s32_u32(vaddq_u32(mid, side)), 1); - righti = vshrq_n_s32(vreinterpretq_s32_u32(vsubq_u32(mid, side)), 1); - leftf = vmulq_f32(vcvtq_f32_s32(lefti), factor4); - rightf = vmulq_f32(vcvtq_f32_s32(righti), factor4); - ma_dr_flac__vst2q_f32(pOutputSamples + i*8, vzipq_f32(leftf, rightf)); - } - for (i = (frameCount4 << 2); i < frameCount; ++i) { - ma_uint32 mid = pInputSamples0U32[i] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; - ma_uint32 side = pInputSamples1U32[i] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; - mid = (mid << 1) | (side & 0x01); - pOutputSamples[i*2+0] = ((ma_int32)(mid + side) >> 1) * factor; - pOutputSamples[i*2+1] = ((ma_int32)(mid - side) >> 1) * factor; - } - } else { - shift -= 1; - shift4 = vdupq_n_s32(shift); - for (i = 0; i < frameCount4; ++i) { - uint32x4_t mid; - uint32x4_t side; - int32x4_t lefti; - int32x4_t righti; - float32x4_t leftf; - float32x4_t rightf; - mid = vshlq_u32(vld1q_u32(pInputSamples0U32 + i*4), wbps0_4); - side = vshlq_u32(vld1q_u32(pInputSamples1U32 + i*4), wbps1_4); - mid = vorrq_u32(vshlq_n_u32(mid, 1), vandq_u32(side, vdupq_n_u32(1))); - lefti = vreinterpretq_s32_u32(vshlq_u32(vaddq_u32(mid, side), shift4)); - righti = vreinterpretq_s32_u32(vshlq_u32(vsubq_u32(mid, side), shift4)); - leftf = vmulq_f32(vcvtq_f32_s32(lefti), factor4); - rightf = vmulq_f32(vcvtq_f32_s32(righti), factor4); - ma_dr_flac__vst2q_f32(pOutputSamples + i*8, vzipq_f32(leftf, rightf)); - } - for (i = (frameCount4 << 2); i < frameCount; ++i) { - ma_uint32 mid = pInputSamples0U32[i] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; - ma_uint32 side = pInputSamples1U32[i] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; - mid = (mid << 1) | (side & 0x01); - pOutputSamples[i*2+0] = (ma_int32)((mid + side) << shift) * factor; - pOutputSamples[i*2+1] = (ma_int32)((mid - side) << shift) * factor; - } - } -} -#endif -static MA_INLINE void ma_dr_flac_read_pcm_frames_f32__decode_mid_side(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, float* pOutputSamples) -{ -#if defined(MA_DR_FLAC_SUPPORT_SSE2) - if (ma_dr_flac__gIsSSE2Supported && pFlac->bitsPerSample <= 24) { - ma_dr_flac_read_pcm_frames_f32__decode_mid_side__sse2(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); - } else -#elif defined(MA_DR_FLAC_SUPPORT_NEON) - if (ma_dr_flac__gIsNEONSupported && pFlac->bitsPerSample <= 24) { - ma_dr_flac_read_pcm_frames_f32__decode_mid_side__neon(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); - } else -#endif - { -#if 0 - ma_dr_flac_read_pcm_frames_f32__decode_mid_side__reference(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); -#else - ma_dr_flac_read_pcm_frames_f32__decode_mid_side__scalar(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); -#endif - } -} -#if 0 -static MA_INLINE void ma_dr_flac_read_pcm_frames_f32__decode_independent_stereo__reference(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, float* pOutputSamples) -{ - for (ma_uint64 i = 0; i < frameCount; ++i) { - pOutputSamples[i*2+0] = (float)((ma_int32)((ma_uint32)pInputSamples0[i] << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample)) / 2147483648.0); - pOutputSamples[i*2+1] = (float)((ma_int32)((ma_uint32)pInputSamples1[i] << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample)) / 2147483648.0); - } -} -#endif -static MA_INLINE void ma_dr_flac_read_pcm_frames_f32__decode_independent_stereo__scalar(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, float* pOutputSamples) -{ - ma_uint64 i; - ma_uint64 frameCount4 = frameCount >> 2; - const ma_uint32* pInputSamples0U32 = (const ma_uint32*)pInputSamples0; - const ma_uint32* pInputSamples1U32 = (const ma_uint32*)pInputSamples1; - ma_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; - ma_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; - float factor = 1 / 2147483648.0; - for (i = 0; i < frameCount4; ++i) { - ma_uint32 tempL0 = pInputSamples0U32[i*4+0] << shift0; - ma_uint32 tempL1 = pInputSamples0U32[i*4+1] << shift0; - ma_uint32 tempL2 = pInputSamples0U32[i*4+2] << shift0; - ma_uint32 tempL3 = pInputSamples0U32[i*4+3] << shift0; - ma_uint32 tempR0 = pInputSamples1U32[i*4+0] << shift1; - ma_uint32 tempR1 = pInputSamples1U32[i*4+1] << shift1; - ma_uint32 tempR2 = pInputSamples1U32[i*4+2] << shift1; - ma_uint32 tempR3 = pInputSamples1U32[i*4+3] << shift1; - pOutputSamples[i*8+0] = (ma_int32)tempL0 * factor; - pOutputSamples[i*8+1] = (ma_int32)tempR0 * factor; - pOutputSamples[i*8+2] = (ma_int32)tempL1 * factor; - pOutputSamples[i*8+3] = (ma_int32)tempR1 * factor; - pOutputSamples[i*8+4] = (ma_int32)tempL2 * factor; - pOutputSamples[i*8+5] = (ma_int32)tempR2 * factor; - pOutputSamples[i*8+6] = (ma_int32)tempL3 * factor; - pOutputSamples[i*8+7] = (ma_int32)tempR3 * factor; - } - for (i = (frameCount4 << 2); i < frameCount; ++i) { - pOutputSamples[i*2+0] = (ma_int32)(pInputSamples0U32[i] << shift0) * factor; - pOutputSamples[i*2+1] = (ma_int32)(pInputSamples1U32[i] << shift1) * factor; - } -} -#if defined(MA_DR_FLAC_SUPPORT_SSE2) -static MA_INLINE void ma_dr_flac_read_pcm_frames_f32__decode_independent_stereo__sse2(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, float* pOutputSamples) -{ - ma_uint64 i; - ma_uint64 frameCount4 = frameCount >> 2; - const ma_uint32* pInputSamples0U32 = (const ma_uint32*)pInputSamples0; - const ma_uint32* pInputSamples1U32 = (const ma_uint32*)pInputSamples1; - ma_uint32 shift0 = (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample) - 8; - ma_uint32 shift1 = (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample) - 8; - float factor = 1.0f / 8388608.0f; - __m128 factor128 = _mm_set1_ps(factor); - for (i = 0; i < frameCount4; ++i) { - __m128i lefti; - __m128i righti; - __m128 leftf; - __m128 rightf; - lefti = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples0 + i), shift0); - righti = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples1 + i), shift1); - leftf = _mm_mul_ps(_mm_cvtepi32_ps(lefti), factor128); - rightf = _mm_mul_ps(_mm_cvtepi32_ps(righti), factor128); - _mm_storeu_ps(pOutputSamples + i*8 + 0, _mm_unpacklo_ps(leftf, rightf)); - _mm_storeu_ps(pOutputSamples + i*8 + 4, _mm_unpackhi_ps(leftf, rightf)); - } - for (i = (frameCount4 << 2); i < frameCount; ++i) { - pOutputSamples[i*2+0] = (ma_int32)(pInputSamples0U32[i] << shift0) * factor; - pOutputSamples[i*2+1] = (ma_int32)(pInputSamples1U32[i] << shift1) * factor; - } -} -#endif -#if defined(MA_DR_FLAC_SUPPORT_NEON) -static MA_INLINE void ma_dr_flac_read_pcm_frames_f32__decode_independent_stereo__neon(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, float* pOutputSamples) -{ - ma_uint64 i; - ma_uint64 frameCount4 = frameCount >> 2; - const ma_uint32* pInputSamples0U32 = (const ma_uint32*)pInputSamples0; - const ma_uint32* pInputSamples1U32 = (const ma_uint32*)pInputSamples1; - ma_uint32 shift0 = (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample) - 8; - ma_uint32 shift1 = (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample) - 8; - float factor = 1.0f / 8388608.0f; - float32x4_t factor4 = vdupq_n_f32(factor); - int32x4_t shift0_4 = vdupq_n_s32(shift0); - int32x4_t shift1_4 = vdupq_n_s32(shift1); - for (i = 0; i < frameCount4; ++i) { - int32x4_t lefti; - int32x4_t righti; - float32x4_t leftf; - float32x4_t rightf; - lefti = vreinterpretq_s32_u32(vshlq_u32(vld1q_u32(pInputSamples0U32 + i*4), shift0_4)); - righti = vreinterpretq_s32_u32(vshlq_u32(vld1q_u32(pInputSamples1U32 + i*4), shift1_4)); - leftf = vmulq_f32(vcvtq_f32_s32(lefti), factor4); - rightf = vmulq_f32(vcvtq_f32_s32(righti), factor4); - ma_dr_flac__vst2q_f32(pOutputSamples + i*8, vzipq_f32(leftf, rightf)); - } - for (i = (frameCount4 << 2); i < frameCount; ++i) { - pOutputSamples[i*2+0] = (ma_int32)(pInputSamples0U32[i] << shift0) * factor; - pOutputSamples[i*2+1] = (ma_int32)(pInputSamples1U32[i] << shift1) * factor; - } -} -#endif -static MA_INLINE void ma_dr_flac_read_pcm_frames_f32__decode_independent_stereo(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, float* pOutputSamples) -{ -#if defined(MA_DR_FLAC_SUPPORT_SSE2) - if (ma_dr_flac__gIsSSE2Supported && pFlac->bitsPerSample <= 24) { - ma_dr_flac_read_pcm_frames_f32__decode_independent_stereo__sse2(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); - } else -#elif defined(MA_DR_FLAC_SUPPORT_NEON) - if (ma_dr_flac__gIsNEONSupported && pFlac->bitsPerSample <= 24) { - ma_dr_flac_read_pcm_frames_f32__decode_independent_stereo__neon(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); - } else -#endif - { -#if 0 - ma_dr_flac_read_pcm_frames_f32__decode_independent_stereo__reference(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); -#else - ma_dr_flac_read_pcm_frames_f32__decode_independent_stereo__scalar(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); -#endif - } -} -MA_API ma_uint64 ma_dr_flac_read_pcm_frames_f32(ma_dr_flac* pFlac, ma_uint64 framesToRead, float* pBufferOut) -{ - ma_uint64 framesRead; - ma_uint32 unusedBitsPerSample; - if (pFlac == NULL || framesToRead == 0) { - return 0; - } - if (pBufferOut == NULL) { - return ma_dr_flac__seek_forward_by_pcm_frames(pFlac, framesToRead); - } - MA_DR_FLAC_ASSERT(pFlac->bitsPerSample <= 32); - unusedBitsPerSample = 32 - pFlac->bitsPerSample; - framesRead = 0; - while (framesToRead > 0) { - if (pFlac->currentFLACFrame.pcmFramesRemaining == 0) { - if (!ma_dr_flac__read_and_decode_next_flac_frame(pFlac)) { - break; - } - } else { - unsigned int channelCount = ma_dr_flac__get_channel_count_from_channel_assignment(pFlac->currentFLACFrame.header.channelAssignment); - ma_uint64 iFirstPCMFrame = pFlac->currentFLACFrame.header.blockSizeInPCMFrames - pFlac->currentFLACFrame.pcmFramesRemaining; - ma_uint64 frameCountThisIteration = framesToRead; - if (frameCountThisIteration > pFlac->currentFLACFrame.pcmFramesRemaining) { - frameCountThisIteration = pFlac->currentFLACFrame.pcmFramesRemaining; - } - if (channelCount == 2) { - const ma_int32* pDecodedSamples0 = pFlac->currentFLACFrame.subframes[0].pSamplesS32 + iFirstPCMFrame; - const ma_int32* pDecodedSamples1 = pFlac->currentFLACFrame.subframes[1].pSamplesS32 + iFirstPCMFrame; - switch (pFlac->currentFLACFrame.header.channelAssignment) - { - case MA_DR_FLAC_CHANNEL_ASSIGNMENT_LEFT_SIDE: - { - ma_dr_flac_read_pcm_frames_f32__decode_left_side(pFlac, frameCountThisIteration, unusedBitsPerSample, pDecodedSamples0, pDecodedSamples1, pBufferOut); - } break; - case MA_DR_FLAC_CHANNEL_ASSIGNMENT_RIGHT_SIDE: - { - ma_dr_flac_read_pcm_frames_f32__decode_right_side(pFlac, frameCountThisIteration, unusedBitsPerSample, pDecodedSamples0, pDecodedSamples1, pBufferOut); - } break; - case MA_DR_FLAC_CHANNEL_ASSIGNMENT_MID_SIDE: - { - ma_dr_flac_read_pcm_frames_f32__decode_mid_side(pFlac, frameCountThisIteration, unusedBitsPerSample, pDecodedSamples0, pDecodedSamples1, pBufferOut); - } break; - case MA_DR_FLAC_CHANNEL_ASSIGNMENT_INDEPENDENT: - default: - { - ma_dr_flac_read_pcm_frames_f32__decode_independent_stereo(pFlac, frameCountThisIteration, unusedBitsPerSample, pDecodedSamples0, pDecodedSamples1, pBufferOut); - } break; - } - } else { - ma_uint64 i; - for (i = 0; i < frameCountThisIteration; ++i) { - unsigned int j; - for (j = 0; j < channelCount; ++j) { - ma_int32 sampleS32 = (ma_int32)((ma_uint32)(pFlac->currentFLACFrame.subframes[j].pSamplesS32[iFirstPCMFrame + i]) << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[j].wastedBitsPerSample)); - pBufferOut[(i*channelCount)+j] = (float)(sampleS32 / 2147483648.0); - } - } - } - framesRead += frameCountThisIteration; - pBufferOut += frameCountThisIteration * channelCount; - framesToRead -= frameCountThisIteration; - pFlac->currentPCMFrame += frameCountThisIteration; - pFlac->currentFLACFrame.pcmFramesRemaining -= (unsigned int)frameCountThisIteration; - } - } - return framesRead; -} -MA_API ma_bool32 ma_dr_flac_seek_to_pcm_frame(ma_dr_flac* pFlac, ma_uint64 pcmFrameIndex) -{ - if (pFlac == NULL) { - return MA_FALSE; - } - if (pFlac->currentPCMFrame == pcmFrameIndex) { - return MA_TRUE; - } - if (pFlac->firstFLACFramePosInBytes == 0) { - return MA_FALSE; - } - if (pcmFrameIndex == 0) { - pFlac->currentPCMFrame = 0; - return ma_dr_flac__seek_to_first_frame(pFlac); - } else { - ma_bool32 wasSuccessful = MA_FALSE; - ma_uint64 originalPCMFrame = pFlac->currentPCMFrame; - if (pcmFrameIndex > pFlac->totalPCMFrameCount) { - pcmFrameIndex = pFlac->totalPCMFrameCount; - } - if (pcmFrameIndex > pFlac->currentPCMFrame) { - ma_uint32 offset = (ma_uint32)(pcmFrameIndex - pFlac->currentPCMFrame); - if (pFlac->currentFLACFrame.pcmFramesRemaining > offset) { - pFlac->currentFLACFrame.pcmFramesRemaining -= offset; - pFlac->currentPCMFrame = pcmFrameIndex; - return MA_TRUE; - } - } else { - ma_uint32 offsetAbs = (ma_uint32)(pFlac->currentPCMFrame - pcmFrameIndex); - ma_uint32 currentFLACFramePCMFrameCount = pFlac->currentFLACFrame.header.blockSizeInPCMFrames; - ma_uint32 currentFLACFramePCMFramesConsumed = currentFLACFramePCMFrameCount - pFlac->currentFLACFrame.pcmFramesRemaining; - if (currentFLACFramePCMFramesConsumed > offsetAbs) { - pFlac->currentFLACFrame.pcmFramesRemaining += offsetAbs; - pFlac->currentPCMFrame = pcmFrameIndex; - return MA_TRUE; - } - } -#ifndef MA_DR_FLAC_NO_OGG - if (pFlac->container == ma_dr_flac_container_ogg) - { - wasSuccessful = ma_dr_flac_ogg__seek_to_pcm_frame(pFlac, pcmFrameIndex); - } - else -#endif - { - if (!pFlac->_noSeekTableSeek) { - wasSuccessful = ma_dr_flac__seek_to_pcm_frame__seek_table(pFlac, pcmFrameIndex); - } -#if !defined(MA_DR_FLAC_NO_CRC) - if (!wasSuccessful && !pFlac->_noBinarySearchSeek && pFlac->totalPCMFrameCount > 0) { - wasSuccessful = ma_dr_flac__seek_to_pcm_frame__binary_search(pFlac, pcmFrameIndex); - } -#endif - if (!wasSuccessful && !pFlac->_noBruteForceSeek) { - wasSuccessful = ma_dr_flac__seek_to_pcm_frame__brute_force(pFlac, pcmFrameIndex); - } - } - if (wasSuccessful) { - pFlac->currentPCMFrame = pcmFrameIndex; - } else { - if (ma_dr_flac_seek_to_pcm_frame(pFlac, originalPCMFrame) == MA_FALSE) { - ma_dr_flac_seek_to_pcm_frame(pFlac, 0); - } - } - return wasSuccessful; - } -} -#define MA_DR_FLAC_DEFINE_FULL_READ_AND_CLOSE(extension, type) \ -static type* ma_dr_flac__full_read_and_close_ ## extension (ma_dr_flac* pFlac, unsigned int* channelsOut, unsigned int* sampleRateOut, ma_uint64* totalPCMFrameCountOut)\ -{ \ - type* pSampleData = NULL; \ - ma_uint64 totalPCMFrameCount; \ - \ - MA_DR_FLAC_ASSERT(pFlac != NULL); \ - \ - totalPCMFrameCount = pFlac->totalPCMFrameCount; \ - \ - if (totalPCMFrameCount == 0) { \ - type buffer[4096]; \ - ma_uint64 pcmFramesRead; \ - size_t sampleDataBufferSize = sizeof(buffer); \ - \ - pSampleData = (type*)ma_dr_flac__malloc_from_callbacks(sampleDataBufferSize, &pFlac->allocationCallbacks); \ - if (pSampleData == NULL) { \ - goto on_error; \ - } \ - \ - while ((pcmFramesRead = (ma_uint64)ma_dr_flac_read_pcm_frames_##extension(pFlac, sizeof(buffer)/sizeof(buffer[0])/pFlac->channels, buffer)) > 0) { \ - if (((totalPCMFrameCount + pcmFramesRead) * pFlac->channels * sizeof(type)) > sampleDataBufferSize) { \ - type* pNewSampleData; \ - size_t newSampleDataBufferSize; \ - \ - newSampleDataBufferSize = sampleDataBufferSize * 2; \ - pNewSampleData = (type*)ma_dr_flac__realloc_from_callbacks(pSampleData, newSampleDataBufferSize, sampleDataBufferSize, &pFlac->allocationCallbacks); \ - if (pNewSampleData == NULL) { \ - ma_dr_flac__free_from_callbacks(pSampleData, &pFlac->allocationCallbacks); \ - goto on_error; \ - } \ - \ - sampleDataBufferSize = newSampleDataBufferSize; \ - pSampleData = pNewSampleData; \ - } \ - \ - MA_DR_FLAC_COPY_MEMORY(pSampleData + (totalPCMFrameCount*pFlac->channels), buffer, (size_t)(pcmFramesRead*pFlac->channels*sizeof(type))); \ - totalPCMFrameCount += pcmFramesRead; \ - } \ - \ - \ - MA_DR_FLAC_ZERO_MEMORY(pSampleData + (totalPCMFrameCount*pFlac->channels), (size_t)(sampleDataBufferSize - totalPCMFrameCount*pFlac->channels*sizeof(type))); \ - } else { \ - ma_uint64 dataSize = totalPCMFrameCount*pFlac->channels*sizeof(type); \ - if (dataSize > (ma_uint64)MA_SIZE_MAX) { \ - goto on_error; \ - } \ - \ - pSampleData = (type*)ma_dr_flac__malloc_from_callbacks((size_t)dataSize, &pFlac->allocationCallbacks); \ - if (pSampleData == NULL) { \ - goto on_error; \ - } \ - \ - totalPCMFrameCount = ma_dr_flac_read_pcm_frames_##extension(pFlac, pFlac->totalPCMFrameCount, pSampleData); \ - } \ - \ - if (sampleRateOut) *sampleRateOut = pFlac->sampleRate; \ - if (channelsOut) *channelsOut = pFlac->channels; \ - if (totalPCMFrameCountOut) *totalPCMFrameCountOut = totalPCMFrameCount; \ - \ - ma_dr_flac_close(pFlac); \ - return pSampleData; \ - \ -on_error: \ - ma_dr_flac_close(pFlac); \ - return NULL; \ -} -MA_DR_FLAC_DEFINE_FULL_READ_AND_CLOSE(s32, ma_int32) -MA_DR_FLAC_DEFINE_FULL_READ_AND_CLOSE(s16, ma_int16) -MA_DR_FLAC_DEFINE_FULL_READ_AND_CLOSE(f32, float) -MA_API ma_int32* ma_dr_flac_open_and_read_pcm_frames_s32(ma_dr_flac_read_proc onRead, ma_dr_flac_seek_proc onSeek, void* pUserData, unsigned int* channelsOut, unsigned int* sampleRateOut, ma_uint64* totalPCMFrameCountOut, const ma_allocation_callbacks* pAllocationCallbacks) -{ - ma_dr_flac* pFlac; - if (channelsOut) { - *channelsOut = 0; - } - if (sampleRateOut) { - *sampleRateOut = 0; - } - if (totalPCMFrameCountOut) { - *totalPCMFrameCountOut = 0; - } - pFlac = ma_dr_flac_open(onRead, onSeek, pUserData, pAllocationCallbacks); - if (pFlac == NULL) { - return NULL; - } - return ma_dr_flac__full_read_and_close_s32(pFlac, channelsOut, sampleRateOut, totalPCMFrameCountOut); -} -MA_API ma_int16* ma_dr_flac_open_and_read_pcm_frames_s16(ma_dr_flac_read_proc onRead, ma_dr_flac_seek_proc onSeek, void* pUserData, unsigned int* channelsOut, unsigned int* sampleRateOut, ma_uint64* totalPCMFrameCountOut, const ma_allocation_callbacks* pAllocationCallbacks) -{ - ma_dr_flac* pFlac; - if (channelsOut) { - *channelsOut = 0; - } - if (sampleRateOut) { - *sampleRateOut = 0; - } - if (totalPCMFrameCountOut) { - *totalPCMFrameCountOut = 0; - } - pFlac = ma_dr_flac_open(onRead, onSeek, pUserData, pAllocationCallbacks); - if (pFlac == NULL) { - return NULL; - } - return ma_dr_flac__full_read_and_close_s16(pFlac, channelsOut, sampleRateOut, totalPCMFrameCountOut); -} -MA_API float* ma_dr_flac_open_and_read_pcm_frames_f32(ma_dr_flac_read_proc onRead, ma_dr_flac_seek_proc onSeek, void* pUserData, unsigned int* channelsOut, unsigned int* sampleRateOut, ma_uint64* totalPCMFrameCountOut, const ma_allocation_callbacks* pAllocationCallbacks) -{ - ma_dr_flac* pFlac; - if (channelsOut) { - *channelsOut = 0; - } - if (sampleRateOut) { - *sampleRateOut = 0; - } - if (totalPCMFrameCountOut) { - *totalPCMFrameCountOut = 0; - } - pFlac = ma_dr_flac_open(onRead, onSeek, pUserData, pAllocationCallbacks); - if (pFlac == NULL) { - return NULL; - } - return ma_dr_flac__full_read_and_close_f32(pFlac, channelsOut, sampleRateOut, totalPCMFrameCountOut); -} -#ifndef MA_DR_FLAC_NO_STDIO -MA_API ma_int32* ma_dr_flac_open_file_and_read_pcm_frames_s32(const char* filename, unsigned int* channels, unsigned int* sampleRate, ma_uint64* totalPCMFrameCount, const ma_allocation_callbacks* pAllocationCallbacks) -{ - ma_dr_flac* pFlac; - if (sampleRate) { - *sampleRate = 0; - } - if (channels) { - *channels = 0; - } - if (totalPCMFrameCount) { - *totalPCMFrameCount = 0; - } - pFlac = ma_dr_flac_open_file(filename, pAllocationCallbacks); - if (pFlac == NULL) { - return NULL; - } - return ma_dr_flac__full_read_and_close_s32(pFlac, channels, sampleRate, totalPCMFrameCount); -} -MA_API ma_int16* ma_dr_flac_open_file_and_read_pcm_frames_s16(const char* filename, unsigned int* channels, unsigned int* sampleRate, ma_uint64* totalPCMFrameCount, const ma_allocation_callbacks* pAllocationCallbacks) -{ - ma_dr_flac* pFlac; - if (sampleRate) { - *sampleRate = 0; - } - if (channels) { - *channels = 0; - } - if (totalPCMFrameCount) { - *totalPCMFrameCount = 0; - } - pFlac = ma_dr_flac_open_file(filename, pAllocationCallbacks); - if (pFlac == NULL) { - return NULL; - } - return ma_dr_flac__full_read_and_close_s16(pFlac, channels, sampleRate, totalPCMFrameCount); -} -MA_API float* ma_dr_flac_open_file_and_read_pcm_frames_f32(const char* filename, unsigned int* channels, unsigned int* sampleRate, ma_uint64* totalPCMFrameCount, const ma_allocation_callbacks* pAllocationCallbacks) -{ - ma_dr_flac* pFlac; - if (sampleRate) { - *sampleRate = 0; - } - if (channels) { - *channels = 0; - } - if (totalPCMFrameCount) { - *totalPCMFrameCount = 0; - } - pFlac = ma_dr_flac_open_file(filename, pAllocationCallbacks); - if (pFlac == NULL) { - return NULL; - } - return ma_dr_flac__full_read_and_close_f32(pFlac, channels, sampleRate, totalPCMFrameCount); -} -#endif -MA_API ma_int32* ma_dr_flac_open_memory_and_read_pcm_frames_s32(const void* data, size_t dataSize, unsigned int* channels, unsigned int* sampleRate, ma_uint64* totalPCMFrameCount, const ma_allocation_callbacks* pAllocationCallbacks) -{ - ma_dr_flac* pFlac; - if (sampleRate) { - *sampleRate = 0; - } - if (channels) { - *channels = 0; - } - if (totalPCMFrameCount) { - *totalPCMFrameCount = 0; - } - pFlac = ma_dr_flac_open_memory(data, dataSize, pAllocationCallbacks); - if (pFlac == NULL) { - return NULL; - } - return ma_dr_flac__full_read_and_close_s32(pFlac, channels, sampleRate, totalPCMFrameCount); -} -MA_API ma_int16* ma_dr_flac_open_memory_and_read_pcm_frames_s16(const void* data, size_t dataSize, unsigned int* channels, unsigned int* sampleRate, ma_uint64* totalPCMFrameCount, const ma_allocation_callbacks* pAllocationCallbacks) -{ - ma_dr_flac* pFlac; - if (sampleRate) { - *sampleRate = 0; - } - if (channels) { - *channels = 0; - } - if (totalPCMFrameCount) { - *totalPCMFrameCount = 0; - } - pFlac = ma_dr_flac_open_memory(data, dataSize, pAllocationCallbacks); - if (pFlac == NULL) { - return NULL; - } - return ma_dr_flac__full_read_and_close_s16(pFlac, channels, sampleRate, totalPCMFrameCount); -} -MA_API float* ma_dr_flac_open_memory_and_read_pcm_frames_f32(const void* data, size_t dataSize, unsigned int* channels, unsigned int* sampleRate, ma_uint64* totalPCMFrameCount, const ma_allocation_callbacks* pAllocationCallbacks) -{ - ma_dr_flac* pFlac; - if (sampleRate) { - *sampleRate = 0; - } - if (channels) { - *channels = 0; - } - if (totalPCMFrameCount) { - *totalPCMFrameCount = 0; - } - pFlac = ma_dr_flac_open_memory(data, dataSize, pAllocationCallbacks); - if (pFlac == NULL) { - return NULL; - } - return ma_dr_flac__full_read_and_close_f32(pFlac, channels, sampleRate, totalPCMFrameCount); -} -MA_API void ma_dr_flac_free(void* p, const ma_allocation_callbacks* pAllocationCallbacks) -{ - if (pAllocationCallbacks != NULL) { - ma_dr_flac__free_from_callbacks(p, pAllocationCallbacks); - } else { - ma_dr_flac__free_default(p, NULL); - } -} -MA_API void ma_dr_flac_init_vorbis_comment_iterator(ma_dr_flac_vorbis_comment_iterator* pIter, ma_uint32 commentCount, const void* pComments) -{ - if (pIter == NULL) { - return; - } - pIter->countRemaining = commentCount; - pIter->pRunningData = (const char*)pComments; -} -MA_API const char* ma_dr_flac_next_vorbis_comment(ma_dr_flac_vorbis_comment_iterator* pIter, ma_uint32* pCommentLengthOut) -{ - ma_int32 length; - const char* pComment; - if (pCommentLengthOut) { - *pCommentLengthOut = 0; - } - if (pIter == NULL || pIter->countRemaining == 0 || pIter->pRunningData == NULL) { - return NULL; - } - length = ma_dr_flac__le2host_32_ptr_unaligned(pIter->pRunningData); - pIter->pRunningData += 4; - pComment = pIter->pRunningData; - pIter->pRunningData += length; - pIter->countRemaining -= 1; - if (pCommentLengthOut) { - *pCommentLengthOut = length; - } - return pComment; -} -MA_API void ma_dr_flac_init_cuesheet_track_iterator(ma_dr_flac_cuesheet_track_iterator* pIter, ma_uint32 trackCount, const void* pTrackData) -{ - if (pIter == NULL) { - return; - } - pIter->countRemaining = trackCount; - pIter->pRunningData = (const char*)pTrackData; -} -MA_API ma_bool32 ma_dr_flac_next_cuesheet_track(ma_dr_flac_cuesheet_track_iterator* pIter, ma_dr_flac_cuesheet_track* pCuesheetTrack) -{ - ma_dr_flac_cuesheet_track cuesheetTrack; - const char* pRunningData; - ma_uint64 offsetHi; - ma_uint64 offsetLo; - if (pIter == NULL || pIter->countRemaining == 0 || pIter->pRunningData == NULL) { - return MA_FALSE; - } - pRunningData = pIter->pRunningData; - offsetHi = ma_dr_flac__be2host_32(*(const ma_uint32*)pRunningData); pRunningData += 4; - offsetLo = ma_dr_flac__be2host_32(*(const ma_uint32*)pRunningData); pRunningData += 4; - cuesheetTrack.offset = offsetLo | (offsetHi << 32); - cuesheetTrack.trackNumber = pRunningData[0]; pRunningData += 1; - MA_DR_FLAC_COPY_MEMORY(cuesheetTrack.ISRC, pRunningData, sizeof(cuesheetTrack.ISRC)); pRunningData += 12; - cuesheetTrack.isAudio = (pRunningData[0] & 0x80) != 0; - cuesheetTrack.preEmphasis = (pRunningData[0] & 0x40) != 0; pRunningData += 14; - cuesheetTrack.indexCount = pRunningData[0]; pRunningData += 1; - cuesheetTrack.pIndexPoints = (const ma_dr_flac_cuesheet_track_index*)pRunningData; pRunningData += cuesheetTrack.indexCount * sizeof(ma_dr_flac_cuesheet_track_index); - pIter->pRunningData = pRunningData; - pIter->countRemaining -= 1; - if (pCuesheetTrack) { - *pCuesheetTrack = cuesheetTrack; - } - return MA_TRUE; -} -#if defined(__clang__) || (defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6))) - #pragma GCC diagnostic pop -#endif -#endif -/* dr_flac_c end */ -#endif /* MA_DR_FLAC_IMPLEMENTATION */ -#endif /* MA_NO_FLAC */ - -#if !defined(MA_NO_MP3) && !defined(MA_NO_DECODING) -#if !defined(MA_DR_MP3_IMPLEMENTATION) -/* dr_mp3_c begin */ -#ifndef ma_dr_mp3_c -#define ma_dr_mp3_c -#include -#include -#include -MA_API void ma_dr_mp3_version(ma_uint32* pMajor, ma_uint32* pMinor, ma_uint32* pRevision) -{ - if (pMajor) { - *pMajor = MA_DR_MP3_VERSION_MAJOR; - } - if (pMinor) { - *pMinor = MA_DR_MP3_VERSION_MINOR; - } - if (pRevision) { - *pRevision = MA_DR_MP3_VERSION_REVISION; - } -} -MA_API const char* ma_dr_mp3_version_string(void) -{ - return MA_DR_MP3_VERSION_STRING; -} -#if defined(__TINYC__) -#define MA_DR_MP3_NO_SIMD -#endif -#define MA_DR_MP3_OFFSET_PTR(p, offset) ((void*)((ma_uint8*)(p) + (offset))) -#define MA_DR_MP3_MAX_FREE_FORMAT_FRAME_SIZE 2304 -#ifndef MA_DR_MP3_MAX_FRAME_SYNC_MATCHES -#define MA_DR_MP3_MAX_FRAME_SYNC_MATCHES 10 -#endif -#define MA_DR_MP3_MAX_L3_FRAME_PAYLOAD_BYTES MA_DR_MP3_MAX_FREE_FORMAT_FRAME_SIZE -#define MA_DR_MP3_MAX_BITRESERVOIR_BYTES 511 -#define MA_DR_MP3_SHORT_BLOCK_TYPE 2 -#define MA_DR_MP3_STOP_BLOCK_TYPE 3 -#define MA_DR_MP3_MODE_MONO 3 -#define MA_DR_MP3_MODE_JOINT_STEREO 1 -#define MA_DR_MP3_HDR_SIZE 4 -#define MA_DR_MP3_HDR_IS_MONO(h) (((h[3]) & 0xC0) == 0xC0) -#define MA_DR_MP3_HDR_IS_MS_STEREO(h) (((h[3]) & 0xE0) == 0x60) -#define MA_DR_MP3_HDR_IS_FREE_FORMAT(h) (((h[2]) & 0xF0) == 0) -#define MA_DR_MP3_HDR_IS_CRC(h) (!((h[1]) & 1)) -#define MA_DR_MP3_HDR_TEST_PADDING(h) ((h[2]) & 0x2) -#define MA_DR_MP3_HDR_TEST_MPEG1(h) ((h[1]) & 0x8) -#define MA_DR_MP3_HDR_TEST_NOT_MPEG25(h) ((h[1]) & 0x10) -#define MA_DR_MP3_HDR_TEST_I_STEREO(h) ((h[3]) & 0x10) -#define MA_DR_MP3_HDR_TEST_MS_STEREO(h) ((h[3]) & 0x20) -#define MA_DR_MP3_HDR_GET_STEREO_MODE(h) (((h[3]) >> 6) & 3) -#define MA_DR_MP3_HDR_GET_STEREO_MODE_EXT(h) (((h[3]) >> 4) & 3) -#define MA_DR_MP3_HDR_GET_LAYER(h) (((h[1]) >> 1) & 3) -#define MA_DR_MP3_HDR_GET_BITRATE(h) ((h[2]) >> 4) -#define MA_DR_MP3_HDR_GET_SAMPLE_RATE(h) (((h[2]) >> 2) & 3) -#define MA_DR_MP3_HDR_GET_MY_SAMPLE_RATE(h) (MA_DR_MP3_HDR_GET_SAMPLE_RATE(h) + (((h[1] >> 3) & 1) + ((h[1] >> 4) & 1))*3) -#define MA_DR_MP3_HDR_IS_FRAME_576(h) ((h[1] & 14) == 2) -#define MA_DR_MP3_HDR_IS_LAYER_1(h) ((h[1] & 6) == 6) -#define MA_DR_MP3_BITS_DEQUANTIZER_OUT -1 -#define MA_DR_MP3_MAX_SCF (255 + MA_DR_MP3_BITS_DEQUANTIZER_OUT*4 - 210) -#define MA_DR_MP3_MAX_SCFI ((MA_DR_MP3_MAX_SCF + 3) & ~3) -#define MA_DR_MP3_MIN(a, b) ((a) > (b) ? (b) : (a)) -#define MA_DR_MP3_MAX(a, b) ((a) < (b) ? (b) : (a)) -#if !defined(MA_DR_MP3_NO_SIMD) -#if !defined(MA_DR_MP3_ONLY_SIMD) && (defined(_M_X64) || defined(__x86_64__) || defined(__aarch64__) || defined(_M_ARM64) || defined(_M_ARM64EC)) -#define MA_DR_MP3_ONLY_SIMD -#endif -#if ((defined(_MSC_VER) && _MSC_VER >= 1400) && defined(_M_X64)) || ((defined(__i386) || defined(_M_IX86) || defined(__i386__) || defined(__x86_64__)) && ((defined(_M_IX86_FP) && _M_IX86_FP == 2) || defined(__SSE2__))) -#if defined(_MSC_VER) -#include -#endif -#include -#define MA_DR_MP3_HAVE_SSE 1 -#define MA_DR_MP3_HAVE_SIMD 1 -#define MA_DR_MP3_VSTORE _mm_storeu_ps -#define MA_DR_MP3_VLD _mm_loadu_ps -#define MA_DR_MP3_VSET _mm_set1_ps -#define MA_DR_MP3_VADD _mm_add_ps -#define MA_DR_MP3_VSUB _mm_sub_ps -#define MA_DR_MP3_VMUL _mm_mul_ps -#define MA_DR_MP3_VMAC(a, x, y) _mm_add_ps(a, _mm_mul_ps(x, y)) -#define MA_DR_MP3_VMSB(a, x, y) _mm_sub_ps(a, _mm_mul_ps(x, y)) -#define MA_DR_MP3_VMUL_S(x, s) _mm_mul_ps(x, _mm_set1_ps(s)) -#define MA_DR_MP3_VREV(x) _mm_shuffle_ps(x, x, _MM_SHUFFLE(0, 1, 2, 3)) -typedef __m128 ma_dr_mp3_f4; -#if defined(_MSC_VER) || defined(MA_DR_MP3_ONLY_SIMD) -#define ma_dr_mp3_cpuid __cpuid -#else -static __inline__ __attribute__((always_inline)) void ma_dr_mp3_cpuid(int CPUInfo[], const int InfoType) -{ -#if defined(__PIC__) - __asm__ __volatile__( -#if defined(__x86_64__) - "push %%rbx\n" - "cpuid\n" - "xchgl %%ebx, %1\n" - "pop %%rbx\n" -#else - "xchgl %%ebx, %1\n" - "cpuid\n" - "xchgl %%ebx, %1\n" -#endif - : "=a" (CPUInfo[0]), "=r" (CPUInfo[1]), "=c" (CPUInfo[2]), "=d" (CPUInfo[3]) - : "a" (InfoType)); -#else - __asm__ __volatile__( - "cpuid" - : "=a" (CPUInfo[0]), "=b" (CPUInfo[1]), "=c" (CPUInfo[2]), "=d" (CPUInfo[3]) - : "a" (InfoType)); -#endif -} -#endif -static int ma_dr_mp3_have_simd(void) -{ -#ifdef MA_DR_MP3_ONLY_SIMD - return 1; -#else - static int g_have_simd; - int CPUInfo[4]; -#ifdef MINIMP3_TEST - static int g_counter; - if (g_counter++ > 100) - return 0; -#endif - if (g_have_simd) - goto end; - ma_dr_mp3_cpuid(CPUInfo, 0); - if (CPUInfo[0] > 0) - { - ma_dr_mp3_cpuid(CPUInfo, 1); - g_have_simd = (CPUInfo[3] & (1 << 26)) + 1; - return g_have_simd - 1; - } -end: - return g_have_simd - 1; -#endif -} -#elif defined(__ARM_NEON) || defined(__aarch64__) || defined(_M_ARM64) || defined(_M_ARM64EC) -#include -#define MA_DR_MP3_HAVE_SSE 0 -#define MA_DR_MP3_HAVE_SIMD 1 -#define MA_DR_MP3_VSTORE vst1q_f32 -#define MA_DR_MP3_VLD vld1q_f32 -#define MA_DR_MP3_VSET vmovq_n_f32 -#define MA_DR_MP3_VADD vaddq_f32 -#define MA_DR_MP3_VSUB vsubq_f32 -#define MA_DR_MP3_VMUL vmulq_f32 -#define MA_DR_MP3_VMAC(a, x, y) vmlaq_f32(a, x, y) -#define MA_DR_MP3_VMSB(a, x, y) vmlsq_f32(a, x, y) -#define MA_DR_MP3_VMUL_S(x, s) vmulq_f32(x, vmovq_n_f32(s)) -#define MA_DR_MP3_VREV(x) vcombine_f32(vget_high_f32(vrev64q_f32(x)), vget_low_f32(vrev64q_f32(x))) -typedef float32x4_t ma_dr_mp3_f4; -static int ma_dr_mp3_have_simd(void) -{ - return 1; -} -#else -#define MA_DR_MP3_HAVE_SSE 0 -#define MA_DR_MP3_HAVE_SIMD 0 -#ifdef MA_DR_MP3_ONLY_SIMD -#error MA_DR_MP3_ONLY_SIMD used, but SSE/NEON not enabled -#endif -#endif -#else -#define MA_DR_MP3_HAVE_SIMD 0 -#endif -#if defined(__ARM_ARCH) && (__ARM_ARCH >= 6) && !defined(__aarch64__) && !defined(_M_ARM64) && !defined(_M_ARM64EC) && !defined(__ARM_ARCH_6M__) -#define MA_DR_MP3_HAVE_ARMV6 1 -static __inline__ __attribute__((always_inline)) ma_int32 ma_dr_mp3_clip_int16_arm(ma_int32 a) -{ - ma_int32 x = 0; - __asm__ ("ssat %0, #16, %1" : "=r"(x) : "r"(a)); - return x; -} -#else -#define MA_DR_MP3_HAVE_ARMV6 0 -#endif -#ifndef MA_DR_MP3_ASSERT -#include -#define MA_DR_MP3_ASSERT(expression) assert(expression) -#endif -#ifndef MA_DR_MP3_COPY_MEMORY -#define MA_DR_MP3_COPY_MEMORY(dst, src, sz) memcpy((dst), (src), (sz)) -#endif -#ifndef MA_DR_MP3_MOVE_MEMORY -#define MA_DR_MP3_MOVE_MEMORY(dst, src, sz) memmove((dst), (src), (sz)) -#endif -#ifndef MA_DR_MP3_ZERO_MEMORY -#define MA_DR_MP3_ZERO_MEMORY(p, sz) memset((p), 0, (sz)) -#endif -#define MA_DR_MP3_ZERO_OBJECT(p) MA_DR_MP3_ZERO_MEMORY((p), sizeof(*(p))) -#ifndef MA_DR_MP3_MALLOC -#define MA_DR_MP3_MALLOC(sz) malloc((sz)) -#endif -#ifndef MA_DR_MP3_REALLOC -#define MA_DR_MP3_REALLOC(p, sz) realloc((p), (sz)) -#endif -#ifndef MA_DR_MP3_FREE -#define MA_DR_MP3_FREE(p) free((p)) -#endif -typedef struct -{ - const ma_uint8 *buf; - int pos, limit; -} ma_dr_mp3_bs; -typedef struct -{ - float scf[3*64]; - ma_uint8 total_bands, stereo_bands, bitalloc[64], scfcod[64]; -} ma_dr_mp3_L12_scale_info; -typedef struct -{ - ma_uint8 tab_offset, code_tab_width, band_count; -} ma_dr_mp3_L12_subband_alloc; -typedef struct -{ - const ma_uint8 *sfbtab; - ma_uint16 part_23_length, big_values, scalefac_compress; - ma_uint8 global_gain, block_type, mixed_block_flag, n_long_sfb, n_short_sfb; - ma_uint8 table_select[3], region_count[3], subblock_gain[3]; - ma_uint8 preflag, scalefac_scale, count1_table, scfsi; -} ma_dr_mp3_L3_gr_info; -typedef struct -{ - ma_dr_mp3_bs bs; - ma_uint8 maindata[MA_DR_MP3_MAX_BITRESERVOIR_BYTES + MA_DR_MP3_MAX_L3_FRAME_PAYLOAD_BYTES]; - ma_dr_mp3_L3_gr_info gr_info[4]; - float grbuf[2][576], scf[40], syn[18 + 15][2*32]; - ma_uint8 ist_pos[2][39]; -} ma_dr_mp3dec_scratch; -static void ma_dr_mp3_bs_init(ma_dr_mp3_bs *bs, const ma_uint8 *data, int bytes) -{ - bs->buf = data; - bs->pos = 0; - bs->limit = bytes*8; -} -static ma_uint32 ma_dr_mp3_bs_get_bits(ma_dr_mp3_bs *bs, int n) -{ - ma_uint32 next, cache = 0, s = bs->pos & 7; - int shl = n + s; - const ma_uint8 *p = bs->buf + (bs->pos >> 3); - if ((bs->pos += n) > bs->limit) - return 0; - next = *p++ & (255 >> s); - while ((shl -= 8) > 0) - { - cache |= next << shl; - next = *p++; - } - return cache | (next >> -shl); -} -static int ma_dr_mp3_hdr_valid(const ma_uint8 *h) -{ - return h[0] == 0xff && - ((h[1] & 0xF0) == 0xf0 || (h[1] & 0xFE) == 0xe2) && - (MA_DR_MP3_HDR_GET_LAYER(h) != 0) && - (MA_DR_MP3_HDR_GET_BITRATE(h) != 15) && - (MA_DR_MP3_HDR_GET_SAMPLE_RATE(h) != 3); -} -static int ma_dr_mp3_hdr_compare(const ma_uint8 *h1, const ma_uint8 *h2) -{ - return ma_dr_mp3_hdr_valid(h2) && - ((h1[1] ^ h2[1]) & 0xFE) == 0 && - ((h1[2] ^ h2[2]) & 0x0C) == 0 && - !(MA_DR_MP3_HDR_IS_FREE_FORMAT(h1) ^ MA_DR_MP3_HDR_IS_FREE_FORMAT(h2)); -} -static unsigned ma_dr_mp3_hdr_bitrate_kbps(const ma_uint8 *h) -{ - static const ma_uint8 halfrate[2][3][15] = { - { { 0,4,8,12,16,20,24,28,32,40,48,56,64,72,80 }, { 0,4,8,12,16,20,24,28,32,40,48,56,64,72,80 }, { 0,16,24,28,32,40,48,56,64,72,80,88,96,112,128 } }, - { { 0,16,20,24,28,32,40,48,56,64,80,96,112,128,160 }, { 0,16,24,28,32,40,48,56,64,80,96,112,128,160,192 }, { 0,16,32,48,64,80,96,112,128,144,160,176,192,208,224 } }, - }; - return 2*halfrate[!!MA_DR_MP3_HDR_TEST_MPEG1(h)][MA_DR_MP3_HDR_GET_LAYER(h) - 1][MA_DR_MP3_HDR_GET_BITRATE(h)]; -} -static unsigned ma_dr_mp3_hdr_sample_rate_hz(const ma_uint8 *h) -{ - static const unsigned g_hz[3] = { 44100, 48000, 32000 }; - return g_hz[MA_DR_MP3_HDR_GET_SAMPLE_RATE(h)] >> (int)!MA_DR_MP3_HDR_TEST_MPEG1(h) >> (int)!MA_DR_MP3_HDR_TEST_NOT_MPEG25(h); -} -static unsigned ma_dr_mp3_hdr_frame_samples(const ma_uint8 *h) -{ - return MA_DR_MP3_HDR_IS_LAYER_1(h) ? 384 : (1152 >> (int)MA_DR_MP3_HDR_IS_FRAME_576(h)); -} -static int ma_dr_mp3_hdr_frame_bytes(const ma_uint8 *h, int free_format_size) -{ - int frame_bytes = ma_dr_mp3_hdr_frame_samples(h)*ma_dr_mp3_hdr_bitrate_kbps(h)*125/ma_dr_mp3_hdr_sample_rate_hz(h); - if (MA_DR_MP3_HDR_IS_LAYER_1(h)) - { - frame_bytes &= ~3; - } - return frame_bytes ? frame_bytes : free_format_size; -} -static int ma_dr_mp3_hdr_padding(const ma_uint8 *h) -{ - return MA_DR_MP3_HDR_TEST_PADDING(h) ? (MA_DR_MP3_HDR_IS_LAYER_1(h) ? 4 : 1) : 0; -} -#ifndef MA_DR_MP3_ONLY_MP3 -static const ma_dr_mp3_L12_subband_alloc *ma_dr_mp3_L12_subband_alloc_table(const ma_uint8 *hdr, ma_dr_mp3_L12_scale_info *sci) -{ - const ma_dr_mp3_L12_subband_alloc *alloc; - int mode = MA_DR_MP3_HDR_GET_STEREO_MODE(hdr); - int nbands, stereo_bands = (mode == MA_DR_MP3_MODE_MONO) ? 0 : (mode == MA_DR_MP3_MODE_JOINT_STEREO) ? (MA_DR_MP3_HDR_GET_STEREO_MODE_EXT(hdr) << 2) + 4 : 32; - if (MA_DR_MP3_HDR_IS_LAYER_1(hdr)) - { - static const ma_dr_mp3_L12_subband_alloc g_alloc_L1[] = { { 76, 4, 32 } }; - alloc = g_alloc_L1; - nbands = 32; - } else if (!MA_DR_MP3_HDR_TEST_MPEG1(hdr)) - { - static const ma_dr_mp3_L12_subband_alloc g_alloc_L2M2[] = { { 60, 4, 4 }, { 44, 3, 7 }, { 44, 2, 19 } }; - alloc = g_alloc_L2M2; - nbands = 30; - } else - { - static const ma_dr_mp3_L12_subband_alloc g_alloc_L2M1[] = { { 0, 4, 3 }, { 16, 4, 8 }, { 32, 3, 12 }, { 40, 2, 7 } }; - int sample_rate_idx = MA_DR_MP3_HDR_GET_SAMPLE_RATE(hdr); - unsigned kbps = ma_dr_mp3_hdr_bitrate_kbps(hdr) >> (int)(mode != MA_DR_MP3_MODE_MONO); - if (!kbps) - { - kbps = 192; - } - alloc = g_alloc_L2M1; - nbands = 27; - if (kbps < 56) - { - static const ma_dr_mp3_L12_subband_alloc g_alloc_L2M1_lowrate[] = { { 44, 4, 2 }, { 44, 3, 10 } }; - alloc = g_alloc_L2M1_lowrate; - nbands = sample_rate_idx == 2 ? 12 : 8; - } else if (kbps >= 96 && sample_rate_idx != 1) - { - nbands = 30; - } - } - sci->total_bands = (ma_uint8)nbands; - sci->stereo_bands = (ma_uint8)MA_DR_MP3_MIN(stereo_bands, nbands); - return alloc; -} -static void ma_dr_mp3_L12_read_scalefactors(ma_dr_mp3_bs *bs, ma_uint8 *pba, ma_uint8 *scfcod, int bands, float *scf) -{ - static const float g_deq_L12[18*3] = { -#define MA_DR_MP3_DQ(x) 9.53674316e-07f/x, 7.56931807e-07f/x, 6.00777173e-07f/x - MA_DR_MP3_DQ(3),MA_DR_MP3_DQ(7),MA_DR_MP3_DQ(15),MA_DR_MP3_DQ(31),MA_DR_MP3_DQ(63),MA_DR_MP3_DQ(127),MA_DR_MP3_DQ(255),MA_DR_MP3_DQ(511),MA_DR_MP3_DQ(1023),MA_DR_MP3_DQ(2047),MA_DR_MP3_DQ(4095),MA_DR_MP3_DQ(8191),MA_DR_MP3_DQ(16383),MA_DR_MP3_DQ(32767),MA_DR_MP3_DQ(65535),MA_DR_MP3_DQ(3),MA_DR_MP3_DQ(5),MA_DR_MP3_DQ(9) - }; - int i, m; - for (i = 0; i < bands; i++) - { - float s = 0; - int ba = *pba++; - int mask = ba ? 4 + ((19 >> scfcod[i]) & 3) : 0; - for (m = 4; m; m >>= 1) - { - if (mask & m) - { - int b = ma_dr_mp3_bs_get_bits(bs, 6); - s = g_deq_L12[ba*3 - 6 + b % 3]*(int)(1 << 21 >> b/3); - } - *scf++ = s; - } - } -} -static void ma_dr_mp3_L12_read_scale_info(const ma_uint8 *hdr, ma_dr_mp3_bs *bs, ma_dr_mp3_L12_scale_info *sci) -{ - static const ma_uint8 g_bitalloc_code_tab[] = { - 0,17, 3, 4, 5,6,7, 8,9,10,11,12,13,14,15,16, - 0,17,18, 3,19,4,5, 6,7, 8, 9,10,11,12,13,16, - 0,17,18, 3,19,4,5,16, - 0,17,18,16, - 0,17,18,19, 4,5,6, 7,8, 9,10,11,12,13,14,15, - 0,17,18, 3,19,4,5, 6,7, 8, 9,10,11,12,13,14, - 0, 2, 3, 4, 5,6,7, 8,9,10,11,12,13,14,15,16 - }; - const ma_dr_mp3_L12_subband_alloc *subband_alloc = ma_dr_mp3_L12_subband_alloc_table(hdr, sci); - int i, k = 0, ba_bits = 0; - const ma_uint8 *ba_code_tab = g_bitalloc_code_tab; - for (i = 0; i < sci->total_bands; i++) - { - ma_uint8 ba; - if (i == k) - { - k += subband_alloc->band_count; - ba_bits = subband_alloc->code_tab_width; - ba_code_tab = g_bitalloc_code_tab + subband_alloc->tab_offset; - subband_alloc++; - } - ba = ba_code_tab[ma_dr_mp3_bs_get_bits(bs, ba_bits)]; - sci->bitalloc[2*i] = ba; - if (i < sci->stereo_bands) - { - ba = ba_code_tab[ma_dr_mp3_bs_get_bits(bs, ba_bits)]; - } - sci->bitalloc[2*i + 1] = sci->stereo_bands ? ba : 0; - } - for (i = 0; i < 2*sci->total_bands; i++) - { - sci->scfcod[i] = (ma_uint8)(sci->bitalloc[i] ? MA_DR_MP3_HDR_IS_LAYER_1(hdr) ? 2 : ma_dr_mp3_bs_get_bits(bs, 2) : 6); - } - ma_dr_mp3_L12_read_scalefactors(bs, sci->bitalloc, sci->scfcod, sci->total_bands*2, sci->scf); - for (i = sci->stereo_bands; i < sci->total_bands; i++) - { - sci->bitalloc[2*i + 1] = 0; - } -} -static int ma_dr_mp3_L12_dequantize_granule(float *grbuf, ma_dr_mp3_bs *bs, ma_dr_mp3_L12_scale_info *sci, int group_size) -{ - int i, j, k, choff = 576; - for (j = 0; j < 4; j++) - { - float *dst = grbuf + group_size*j; - for (i = 0; i < 2*sci->total_bands; i++) - { - int ba = sci->bitalloc[i]; - if (ba != 0) - { - if (ba < 17) - { - int half = (1 << (ba - 1)) - 1; - for (k = 0; k < group_size; k++) - { - dst[k] = (float)((int)ma_dr_mp3_bs_get_bits(bs, ba) - half); - } - } else - { - unsigned mod = (2 << (ba - 17)) + 1; - unsigned code = ma_dr_mp3_bs_get_bits(bs, mod + 2 - (mod >> 3)); - for (k = 0; k < group_size; k++, code /= mod) - { - dst[k] = (float)((int)(code % mod - mod/2)); - } - } - } - dst += choff; - choff = 18 - choff; - } - } - return group_size*4; -} -static void ma_dr_mp3_L12_apply_scf_384(ma_dr_mp3_L12_scale_info *sci, const float *scf, float *dst) -{ - int i, k; - MA_DR_MP3_COPY_MEMORY(dst + 576 + sci->stereo_bands*18, dst + sci->stereo_bands*18, (sci->total_bands - sci->stereo_bands)*18*sizeof(float)); - for (i = 0; i < sci->total_bands; i++, dst += 18, scf += 6) - { - for (k = 0; k < 12; k++) - { - dst[k + 0] *= scf[0]; - dst[k + 576] *= scf[3]; - } - } -} -#endif -static int ma_dr_mp3_L3_read_side_info(ma_dr_mp3_bs *bs, ma_dr_mp3_L3_gr_info *gr, const ma_uint8 *hdr) -{ - static const ma_uint8 g_scf_long[8][23] = { - { 6,6,6,6,6,6,8,10,12,14,16,20,24,28,32,38,46,52,60,68,58,54,0 }, - { 12,12,12,12,12,12,16,20,24,28,32,40,48,56,64,76,90,2,2,2,2,2,0 }, - { 6,6,6,6,6,6,8,10,12,14,16,20,24,28,32,38,46,52,60,68,58,54,0 }, - { 6,6,6,6,6,6,8,10,12,14,16,18,22,26,32,38,46,54,62,70,76,36,0 }, - { 6,6,6,6,6,6,8,10,12,14,16,20,24,28,32,38,46,52,60,68,58,54,0 }, - { 4,4,4,4,4,4,6,6,8,8,10,12,16,20,24,28,34,42,50,54,76,158,0 }, - { 4,4,4,4,4,4,6,6,6,8,10,12,16,18,22,28,34,40,46,54,54,192,0 }, - { 4,4,4,4,4,4,6,6,8,10,12,16,20,24,30,38,46,56,68,84,102,26,0 } - }; - static const ma_uint8 g_scf_short[8][40] = { - { 4,4,4,4,4,4,4,4,4,6,6,6,8,8,8,10,10,10,12,12,12,14,14,14,18,18,18,24,24,24,30,30,30,40,40,40,18,18,18,0 }, - { 8,8,8,8,8,8,8,8,8,12,12,12,16,16,16,20,20,20,24,24,24,28,28,28,36,36,36,2,2,2,2,2,2,2,2,2,26,26,26,0 }, - { 4,4,4,4,4,4,4,4,4,6,6,6,6,6,6,8,8,8,10,10,10,14,14,14,18,18,18,26,26,26,32,32,32,42,42,42,18,18,18,0 }, - { 4,4,4,4,4,4,4,4,4,6,6,6,8,8,8,10,10,10,12,12,12,14,14,14,18,18,18,24,24,24,32,32,32,44,44,44,12,12,12,0 }, - { 4,4,4,4,4,4,4,4,4,6,6,6,8,8,8,10,10,10,12,12,12,14,14,14,18,18,18,24,24,24,30,30,30,40,40,40,18,18,18,0 }, - { 4,4,4,4,4,4,4,4,4,4,4,4,6,6,6,8,8,8,10,10,10,12,12,12,14,14,14,18,18,18,22,22,22,30,30,30,56,56,56,0 }, - { 4,4,4,4,4,4,4,4,4,4,4,4,6,6,6,6,6,6,10,10,10,12,12,12,14,14,14,16,16,16,20,20,20,26,26,26,66,66,66,0 }, - { 4,4,4,4,4,4,4,4,4,4,4,4,6,6,6,8,8,8,12,12,12,16,16,16,20,20,20,26,26,26,34,34,34,42,42,42,12,12,12,0 } - }; - static const ma_uint8 g_scf_mixed[8][40] = { - { 6,6,6,6,6,6,6,6,6,8,8,8,10,10,10,12,12,12,14,14,14,18,18,18,24,24,24,30,30,30,40,40,40,18,18,18,0 }, - { 12,12,12,4,4,4,8,8,8,12,12,12,16,16,16,20,20,20,24,24,24,28,28,28,36,36,36,2,2,2,2,2,2,2,2,2,26,26,26,0 }, - { 6,6,6,6,6,6,6,6,6,6,6,6,8,8,8,10,10,10,14,14,14,18,18,18,26,26,26,32,32,32,42,42,42,18,18,18,0 }, - { 6,6,6,6,6,6,6,6,6,8,8,8,10,10,10,12,12,12,14,14,14,18,18,18,24,24,24,32,32,32,44,44,44,12,12,12,0 }, - { 6,6,6,6,6,6,6,6,6,8,8,8,10,10,10,12,12,12,14,14,14,18,18,18,24,24,24,30,30,30,40,40,40,18,18,18,0 }, - { 4,4,4,4,4,4,6,6,4,4,4,6,6,6,8,8,8,10,10,10,12,12,12,14,14,14,18,18,18,22,22,22,30,30,30,56,56,56,0 }, - { 4,4,4,4,4,4,6,6,4,4,4,6,6,6,6,6,6,10,10,10,12,12,12,14,14,14,16,16,16,20,20,20,26,26,26,66,66,66,0 }, - { 4,4,4,4,4,4,6,6,4,4,4,6,6,6,8,8,8,12,12,12,16,16,16,20,20,20,26,26,26,34,34,34,42,42,42,12,12,12,0 } - }; - unsigned tables, scfsi = 0; - int main_data_begin, part_23_sum = 0; - int gr_count = MA_DR_MP3_HDR_IS_MONO(hdr) ? 1 : 2; - int sr_idx = MA_DR_MP3_HDR_GET_MY_SAMPLE_RATE(hdr); sr_idx -= (sr_idx != 0); - if (MA_DR_MP3_HDR_TEST_MPEG1(hdr)) - { - gr_count *= 2; - main_data_begin = ma_dr_mp3_bs_get_bits(bs, 9); - scfsi = ma_dr_mp3_bs_get_bits(bs, 7 + gr_count); - } else - { - main_data_begin = ma_dr_mp3_bs_get_bits(bs, 8 + gr_count) >> gr_count; - } - do - { - if (MA_DR_MP3_HDR_IS_MONO(hdr)) - { - scfsi <<= 4; - } - gr->part_23_length = (ma_uint16)ma_dr_mp3_bs_get_bits(bs, 12); - part_23_sum += gr->part_23_length; - gr->big_values = (ma_uint16)ma_dr_mp3_bs_get_bits(bs, 9); - if (gr->big_values > 288) - { - return -1; - } - gr->global_gain = (ma_uint8)ma_dr_mp3_bs_get_bits(bs, 8); - gr->scalefac_compress = (ma_uint16)ma_dr_mp3_bs_get_bits(bs, MA_DR_MP3_HDR_TEST_MPEG1(hdr) ? 4 : 9); - gr->sfbtab = g_scf_long[sr_idx]; - gr->n_long_sfb = 22; - gr->n_short_sfb = 0; - if (ma_dr_mp3_bs_get_bits(bs, 1)) - { - gr->block_type = (ma_uint8)ma_dr_mp3_bs_get_bits(bs, 2); - if (!gr->block_type) - { - return -1; - } - gr->mixed_block_flag = (ma_uint8)ma_dr_mp3_bs_get_bits(bs, 1); - gr->region_count[0] = 7; - gr->region_count[1] = 255; - if (gr->block_type == MA_DR_MP3_SHORT_BLOCK_TYPE) - { - scfsi &= 0x0F0F; - if (!gr->mixed_block_flag) - { - gr->region_count[0] = 8; - gr->sfbtab = g_scf_short[sr_idx]; - gr->n_long_sfb = 0; - gr->n_short_sfb = 39; - } else - { - gr->sfbtab = g_scf_mixed[sr_idx]; - gr->n_long_sfb = MA_DR_MP3_HDR_TEST_MPEG1(hdr) ? 8 : 6; - gr->n_short_sfb = 30; - } - } - tables = ma_dr_mp3_bs_get_bits(bs, 10); - tables <<= 5; - gr->subblock_gain[0] = (ma_uint8)ma_dr_mp3_bs_get_bits(bs, 3); - gr->subblock_gain[1] = (ma_uint8)ma_dr_mp3_bs_get_bits(bs, 3); - gr->subblock_gain[2] = (ma_uint8)ma_dr_mp3_bs_get_bits(bs, 3); - } else - { - gr->block_type = 0; - gr->mixed_block_flag = 0; - tables = ma_dr_mp3_bs_get_bits(bs, 15); - gr->region_count[0] = (ma_uint8)ma_dr_mp3_bs_get_bits(bs, 4); - gr->region_count[1] = (ma_uint8)ma_dr_mp3_bs_get_bits(bs, 3); - gr->region_count[2] = 255; - } - gr->table_select[0] = (ma_uint8)(tables >> 10); - gr->table_select[1] = (ma_uint8)((tables >> 5) & 31); - gr->table_select[2] = (ma_uint8)((tables) & 31); - gr->preflag = (ma_uint8)(MA_DR_MP3_HDR_TEST_MPEG1(hdr) ? ma_dr_mp3_bs_get_bits(bs, 1) : (gr->scalefac_compress >= 500)); - gr->scalefac_scale = (ma_uint8)ma_dr_mp3_bs_get_bits(bs, 1); - gr->count1_table = (ma_uint8)ma_dr_mp3_bs_get_bits(bs, 1); - gr->scfsi = (ma_uint8)((scfsi >> 12) & 15); - scfsi <<= 4; - gr++; - } while(--gr_count); - if (part_23_sum + bs->pos > bs->limit + main_data_begin*8) - { - return -1; - } - return main_data_begin; -} -static void ma_dr_mp3_L3_read_scalefactors(ma_uint8 *scf, ma_uint8 *ist_pos, const ma_uint8 *scf_size, const ma_uint8 *scf_count, ma_dr_mp3_bs *bitbuf, int scfsi) -{ - int i, k; - for (i = 0; i < 4 && scf_count[i]; i++, scfsi *= 2) - { - int cnt = scf_count[i]; - if (scfsi & 8) - { - MA_DR_MP3_COPY_MEMORY(scf, ist_pos, cnt); - } else - { - int bits = scf_size[i]; - if (!bits) - { - MA_DR_MP3_ZERO_MEMORY(scf, cnt); - MA_DR_MP3_ZERO_MEMORY(ist_pos, cnt); - } else - { - int max_scf = (scfsi < 0) ? (1 << bits) - 1 : -1; - for (k = 0; k < cnt; k++) - { - int s = ma_dr_mp3_bs_get_bits(bitbuf, bits); - ist_pos[k] = (ma_uint8)(s == max_scf ? -1 : s); - scf[k] = (ma_uint8)s; - } - } - } - ist_pos += cnt; - scf += cnt; - } - scf[0] = scf[1] = scf[2] = 0; -} -static float ma_dr_mp3_L3_ldexp_q2(float y, int exp_q2) -{ - static const float g_expfrac[4] = { 9.31322575e-10f,7.83145814e-10f,6.58544508e-10f,5.53767716e-10f }; - int e; - do - { - e = MA_DR_MP3_MIN(30*4, exp_q2); - y *= g_expfrac[e & 3]*(1 << 30 >> (e >> 2)); - } while ((exp_q2 -= e) > 0); - return y; -} -static void ma_dr_mp3_L3_decode_scalefactors(const ma_uint8 *hdr, ma_uint8 *ist_pos, ma_dr_mp3_bs *bs, const ma_dr_mp3_L3_gr_info *gr, float *scf, int ch) -{ - static const ma_uint8 g_scf_partitions[3][28] = { - { 6,5,5, 5,6,5,5,5,6,5, 7,3,11,10,0,0, 7, 7, 7,0, 6, 6,6,3, 8, 8,5,0 }, - { 8,9,6,12,6,9,9,9,6,9,12,6,15,18,0,0, 6,15,12,0, 6,12,9,6, 6,18,9,0 }, - { 9,9,6,12,9,9,9,9,9,9,12,6,18,18,0,0,12,12,12,0,12, 9,9,6,15,12,9,0 } - }; - const ma_uint8 *scf_partition = g_scf_partitions[!!gr->n_short_sfb + !gr->n_long_sfb]; - ma_uint8 scf_size[4], iscf[40]; - int i, scf_shift = gr->scalefac_scale + 1, gain_exp, scfsi = gr->scfsi; - float gain; - if (MA_DR_MP3_HDR_TEST_MPEG1(hdr)) - { - static const ma_uint8 g_scfc_decode[16] = { 0,1,2,3, 12,5,6,7, 9,10,11,13, 14,15,18,19 }; - int part = g_scfc_decode[gr->scalefac_compress]; - scf_size[1] = scf_size[0] = (ma_uint8)(part >> 2); - scf_size[3] = scf_size[2] = (ma_uint8)(part & 3); - } else - { - static const ma_uint8 g_mod[6*4] = { 5,5,4,4,5,5,4,1,4,3,1,1,5,6,6,1,4,4,4,1,4,3,1,1 }; - int k, modprod, sfc, ist = MA_DR_MP3_HDR_TEST_I_STEREO(hdr) && ch; - sfc = gr->scalefac_compress >> ist; - for (k = ist*3*4; sfc >= 0; sfc -= modprod, k += 4) - { - for (modprod = 1, i = 3; i >= 0; i--) - { - scf_size[i] = (ma_uint8)(sfc / modprod % g_mod[k + i]); - modprod *= g_mod[k + i]; - } - } - scf_partition += k; - scfsi = -16; - } - ma_dr_mp3_L3_read_scalefactors(iscf, ist_pos, scf_size, scf_partition, bs, scfsi); - if (gr->n_short_sfb) - { - int sh = 3 - scf_shift; - for (i = 0; i < gr->n_short_sfb; i += 3) - { - iscf[gr->n_long_sfb + i + 0] = (ma_uint8)(iscf[gr->n_long_sfb + i + 0] + (gr->subblock_gain[0] << sh)); - iscf[gr->n_long_sfb + i + 1] = (ma_uint8)(iscf[gr->n_long_sfb + i + 1] + (gr->subblock_gain[1] << sh)); - iscf[gr->n_long_sfb + i + 2] = (ma_uint8)(iscf[gr->n_long_sfb + i + 2] + (gr->subblock_gain[2] << sh)); - } - } else if (gr->preflag) - { - static const ma_uint8 g_preamp[10] = { 1,1,1,1,2,2,3,3,3,2 }; - for (i = 0; i < 10; i++) - { - iscf[11 + i] = (ma_uint8)(iscf[11 + i] + g_preamp[i]); - } - } - gain_exp = gr->global_gain + MA_DR_MP3_BITS_DEQUANTIZER_OUT*4 - 210 - (MA_DR_MP3_HDR_IS_MS_STEREO(hdr) ? 2 : 0); - gain = ma_dr_mp3_L3_ldexp_q2(1 << (MA_DR_MP3_MAX_SCFI/4), MA_DR_MP3_MAX_SCFI - gain_exp); - for (i = 0; i < (int)(gr->n_long_sfb + gr->n_short_sfb); i++) - { - scf[i] = ma_dr_mp3_L3_ldexp_q2(gain, iscf[i] << scf_shift); - } -} -static const float g_ma_dr_mp3_pow43[129 + 16] = { - 0,-1,-2.519842f,-4.326749f,-6.349604f,-8.549880f,-10.902724f,-13.390518f,-16.000000f,-18.720754f,-21.544347f,-24.463781f,-27.473142f,-30.567351f,-33.741992f,-36.993181f, - 0,1,2.519842f,4.326749f,6.349604f,8.549880f,10.902724f,13.390518f,16.000000f,18.720754f,21.544347f,24.463781f,27.473142f,30.567351f,33.741992f,36.993181f,40.317474f,43.711787f,47.173345f,50.699631f,54.288352f,57.937408f,61.644865f,65.408941f,69.227979f,73.100443f,77.024898f,81.000000f,85.024491f,89.097188f,93.216975f,97.382800f,101.593667f,105.848633f,110.146801f,114.487321f,118.869381f,123.292209f,127.755065f,132.257246f,136.798076f,141.376907f,145.993119f,150.646117f,155.335327f,160.060199f,164.820202f,169.614826f,174.443577f,179.305980f,184.201575f,189.129918f,194.090580f,199.083145f,204.107210f,209.162385f,214.248292f,219.364564f,224.510845f,229.686789f,234.892058f,240.126328f,245.389280f,250.680604f,256.000000f,261.347174f,266.721841f,272.123723f,277.552547f,283.008049f,288.489971f,293.998060f,299.532071f,305.091761f,310.676898f,316.287249f,321.922592f,327.582707f,333.267377f,338.976394f,344.709550f,350.466646f,356.247482f,362.051866f,367.879608f,373.730522f,379.604427f,385.501143f,391.420496f,397.362314f,403.326427f,409.312672f,415.320884f,421.350905f,427.402579f,433.475750f,439.570269f,445.685987f,451.822757f,457.980436f,464.158883f,470.357960f,476.577530f,482.817459f,489.077615f,495.357868f,501.658090f,507.978156f,514.317941f,520.677324f,527.056184f,533.454404f,539.871867f,546.308458f,552.764065f,559.238575f,565.731879f,572.243870f,578.774440f,585.323483f,591.890898f,598.476581f,605.080431f,611.702349f,618.342238f,625.000000f,631.675540f,638.368763f,645.079578f -}; -static float ma_dr_mp3_L3_pow_43(int x) -{ - float frac; - int sign, mult = 256; - if (x < 129) - { - return g_ma_dr_mp3_pow43[16 + x]; - } - if (x < 1024) - { - mult = 16; - x <<= 3; - } - sign = 2*x & 64; - frac = (float)((x & 63) - sign) / ((x & ~63) + sign); - return g_ma_dr_mp3_pow43[16 + ((x + sign) >> 6)]*(1.f + frac*((4.f/3) + frac*(2.f/9)))*mult; -} -static void ma_dr_mp3_L3_huffman(float *dst, ma_dr_mp3_bs *bs, const ma_dr_mp3_L3_gr_info *gr_info, const float *scf, int layer3gr_limit) -{ - static const ma_int16 tabs[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 785,785,785,785,784,784,784,784,513,513,513,513,513,513,513,513,256,256,256,256,256,256,256,256,256,256,256,256,256,256,256,256, - -255,1313,1298,1282,785,785,785,785,784,784,784,784,769,769,769,769,256,256,256,256,256,256,256,256,256,256,256,256,256,256,256,256,290,288, - -255,1313,1298,1282,769,769,769,769,529,529,529,529,529,529,529,529,528,528,528,528,528,528,528,528,512,512,512,512,512,512,512,512,290,288, - -253,-318,-351,-367,785,785,785,785,784,784,784,784,769,769,769,769,256,256,256,256,256,256,256,256,256,256,256,256,256,256,256,256,819,818,547,547,275,275,275,275,561,560,515,546,289,274,288,258, - -254,-287,1329,1299,1314,1312,1057,1057,1042,1042,1026,1026,784,784,784,784,529,529,529,529,529,529,529,529,769,769,769,769,768,768,768,768,563,560,306,306,291,259, - -252,-413,-477,-542,1298,-575,1041,1041,784,784,784,784,769,769,769,769,256,256,256,256,256,256,256,256,256,256,256,256,256,256,256,256,-383,-399,1107,1092,1106,1061,849,849,789,789,1104,1091,773,773,1076,1075,341,340,325,309,834,804,577,577,532,532,516,516,832,818,803,816,561,561,531,531,515,546,289,289,288,258, - -252,-429,-493,-559,1057,1057,1042,1042,529,529,529,529,529,529,529,529,784,784,784,784,769,769,769,769,512,512,512,512,512,512,512,512,-382,1077,-415,1106,1061,1104,849,849,789,789,1091,1076,1029,1075,834,834,597,581,340,340,339,324,804,833,532,532,832,772,818,803,817,787,816,771,290,290,290,290,288,258, - -253,-349,-414,-447,-463,1329,1299,-479,1314,1312,1057,1057,1042,1042,1026,1026,785,785,785,785,784,784,784,784,769,769,769,769,768,768,768,768,-319,851,821,-335,836,850,805,849,341,340,325,336,533,533,579,579,564,564,773,832,578,548,563,516,321,276,306,291,304,259, - -251,-572,-733,-830,-863,-879,1041,1041,784,784,784,784,769,769,769,769,256,256,256,256,256,256,256,256,256,256,256,256,256,256,256,256,-511,-527,-543,1396,1351,1381,1366,1395,1335,1380,-559,1334,1138,1138,1063,1063,1350,1392,1031,1031,1062,1062,1364,1363,1120,1120,1333,1348,881,881,881,881,375,374,359,373,343,358,341,325,791,791,1123,1122,-703,1105,1045,-719,865,865,790,790,774,774,1104,1029,338,293,323,308,-799,-815,833,788,772,818,803,816,322,292,307,320,561,531,515,546,289,274,288,258, - -251,-525,-605,-685,-765,-831,-846,1298,1057,1057,1312,1282,785,785,785,785,784,784,784,784,769,769,769,769,512,512,512,512,512,512,512,512,1399,1398,1383,1367,1382,1396,1351,-511,1381,1366,1139,1139,1079,1079,1124,1124,1364,1349,1363,1333,882,882,882,882,807,807,807,807,1094,1094,1136,1136,373,341,535,535,881,775,867,822,774,-591,324,338,-671,849,550,550,866,864,609,609,293,336,534,534,789,835,773,-751,834,804,308,307,833,788,832,772,562,562,547,547,305,275,560,515,290,290, - -252,-397,-477,-557,-622,-653,-719,-735,-750,1329,1299,1314,1057,1057,1042,1042,1312,1282,1024,1024,785,785,785,785,784,784,784,784,769,769,769,769,-383,1127,1141,1111,1126,1140,1095,1110,869,869,883,883,1079,1109,882,882,375,374,807,868,838,881,791,-463,867,822,368,263,852,837,836,-543,610,610,550,550,352,336,534,534,865,774,851,821,850,805,593,533,579,564,773,832,578,578,548,548,577,577,307,276,306,291,516,560,259,259, - -250,-2107,-2507,-2764,-2909,-2974,-3007,-3023,1041,1041,1040,1040,769,769,769,769,256,256,256,256,256,256,256,256,256,256,256,256,256,256,256,256,-767,-1052,-1213,-1277,-1358,-1405,-1469,-1535,-1550,-1582,-1614,-1647,-1662,-1694,-1726,-1759,-1774,-1807,-1822,-1854,-1886,1565,-1919,-1935,-1951,-1967,1731,1730,1580,1717,-1983,1729,1564,-1999,1548,-2015,-2031,1715,1595,-2047,1714,-2063,1610,-2079,1609,-2095,1323,1323,1457,1457,1307,1307,1712,1547,1641,1700,1699,1594,1685,1625,1442,1442,1322,1322,-780,-973,-910,1279,1278,1277,1262,1276,1261,1275,1215,1260,1229,-959,974,974,989,989,-943,735,478,478,495,463,506,414,-1039,1003,958,1017,927,942,987,957,431,476,1272,1167,1228,-1183,1256,-1199,895,895,941,941,1242,1227,1212,1135,1014,1014,490,489,503,487,910,1013,985,925,863,894,970,955,1012,847,-1343,831,755,755,984,909,428,366,754,559,-1391,752,486,457,924,997,698,698,983,893,740,740,908,877,739,739,667,667,953,938,497,287,271,271,683,606,590,712,726,574,302,302,738,736,481,286,526,725,605,711,636,724,696,651,589,681,666,710,364,467,573,695,466,466,301,465,379,379,709,604,665,679,316,316,634,633,436,436,464,269,424,394,452,332,438,363,347,408,393,448,331,422,362,407,392,421,346,406,391,376,375,359,1441,1306,-2367,1290,-2383,1337,-2399,-2415,1426,1321,-2431,1411,1336,-2447,-2463,-2479,1169,1169,1049,1049,1424,1289,1412,1352,1319,-2495,1154,1154,1064,1064,1153,1153,416,390,360,404,403,389,344,374,373,343,358,372,327,357,342,311,356,326,1395,1394,1137,1137,1047,1047,1365,1392,1287,1379,1334,1364,1349,1378,1318,1363,792,792,792,792,1152,1152,1032,1032,1121,1121,1046,1046,1120,1120,1030,1030,-2895,1106,1061,1104,849,849,789,789,1091,1076,1029,1090,1060,1075,833,833,309,324,532,532,832,772,818,803,561,561,531,560,515,546,289,274,288,258, - -250,-1179,-1579,-1836,-1996,-2124,-2253,-2333,-2413,-2477,-2542,-2574,-2607,-2622,-2655,1314,1313,1298,1312,1282,785,785,785,785,1040,1040,1025,1025,768,768,768,768,-766,-798,-830,-862,-895,-911,-927,-943,-959,-975,-991,-1007,-1023,-1039,-1055,-1070,1724,1647,-1103,-1119,1631,1767,1662,1738,1708,1723,-1135,1780,1615,1779,1599,1677,1646,1778,1583,-1151,1777,1567,1737,1692,1765,1722,1707,1630,1751,1661,1764,1614,1736,1676,1763,1750,1645,1598,1721,1691,1762,1706,1582,1761,1566,-1167,1749,1629,767,766,751,765,494,494,735,764,719,749,734,763,447,447,748,718,477,506,431,491,446,476,461,505,415,430,475,445,504,399,460,489,414,503,383,474,429,459,502,502,746,752,488,398,501,473,413,472,486,271,480,270,-1439,-1455,1357,-1471,-1487,-1503,1341,1325,-1519,1489,1463,1403,1309,-1535,1372,1448,1418,1476,1356,1462,1387,-1551,1475,1340,1447,1402,1386,-1567,1068,1068,1474,1461,455,380,468,440,395,425,410,454,364,467,466,464,453,269,409,448,268,432,1371,1473,1432,1417,1308,1460,1355,1446,1459,1431,1083,1083,1401,1416,1458,1445,1067,1067,1370,1457,1051,1051,1291,1430,1385,1444,1354,1415,1400,1443,1082,1082,1173,1113,1186,1066,1185,1050,-1967,1158,1128,1172,1097,1171,1081,-1983,1157,1112,416,266,375,400,1170,1142,1127,1065,793,793,1169,1033,1156,1096,1141,1111,1155,1080,1126,1140,898,898,808,808,897,897,792,792,1095,1152,1032,1125,1110,1139,1079,1124,882,807,838,881,853,791,-2319,867,368,263,822,852,837,866,806,865,-2399,851,352,262,534,534,821,836,594,594,549,549,593,593,533,533,848,773,579,579,564,578,548,563,276,276,577,576,306,291,516,560,305,305,275,259, - -251,-892,-2058,-2620,-2828,-2957,-3023,-3039,1041,1041,1040,1040,769,769,769,769,256,256,256,256,256,256,256,256,256,256,256,256,256,256,256,256,-511,-527,-543,-559,1530,-575,-591,1528,1527,1407,1526,1391,1023,1023,1023,1023,1525,1375,1268,1268,1103,1103,1087,1087,1039,1039,1523,-604,815,815,815,815,510,495,509,479,508,463,507,447,431,505,415,399,-734,-782,1262,-815,1259,1244,-831,1258,1228,-847,-863,1196,-879,1253,987,987,748,-767,493,493,462,477,414,414,686,669,478,446,461,445,474,429,487,458,412,471,1266,1264,1009,1009,799,799,-1019,-1276,-1452,-1581,-1677,-1757,-1821,-1886,-1933,-1997,1257,1257,1483,1468,1512,1422,1497,1406,1467,1496,1421,1510,1134,1134,1225,1225,1466,1451,1374,1405,1252,1252,1358,1480,1164,1164,1251,1251,1238,1238,1389,1465,-1407,1054,1101,-1423,1207,-1439,830,830,1248,1038,1237,1117,1223,1148,1236,1208,411,426,395,410,379,269,1193,1222,1132,1235,1221,1116,976,976,1192,1162,1177,1220,1131,1191,963,963,-1647,961,780,-1663,558,558,994,993,437,408,393,407,829,978,813,797,947,-1743,721,721,377,392,844,950,828,890,706,706,812,859,796,960,948,843,934,874,571,571,-1919,690,555,689,421,346,539,539,944,779,918,873,932,842,903,888,570,570,931,917,674,674,-2575,1562,-2591,1609,-2607,1654,1322,1322,1441,1441,1696,1546,1683,1593,1669,1624,1426,1426,1321,1321,1639,1680,1425,1425,1305,1305,1545,1668,1608,1623,1667,1592,1638,1666,1320,1320,1652,1607,1409,1409,1304,1304,1288,1288,1664,1637,1395,1395,1335,1335,1622,1636,1394,1394,1319,1319,1606,1621,1392,1392,1137,1137,1137,1137,345,390,360,375,404,373,1047,-2751,-2767,-2783,1062,1121,1046,-2799,1077,-2815,1106,1061,789,789,1105,1104,263,355,310,340,325,354,352,262,339,324,1091,1076,1029,1090,1060,1075,833,833,788,788,1088,1028,818,818,803,803,561,561,531,531,816,771,546,546,289,274,288,258, - -253,-317,-381,-446,-478,-509,1279,1279,-811,-1179,-1451,-1756,-1900,-2028,-2189,-2253,-2333,-2414,-2445,-2511,-2526,1313,1298,-2559,1041,1041,1040,1040,1025,1025,1024,1024,1022,1007,1021,991,1020,975,1019,959,687,687,1018,1017,671,671,655,655,1016,1015,639,639,758,758,623,623,757,607,756,591,755,575,754,559,543,543,1009,783,-575,-621,-685,-749,496,-590,750,749,734,748,974,989,1003,958,988,973,1002,942,987,957,972,1001,926,986,941,971,956,1000,910,985,925,999,894,970,-1071,-1087,-1102,1390,-1135,1436,1509,1451,1374,-1151,1405,1358,1480,1420,-1167,1507,1494,1389,1342,1465,1435,1450,1326,1505,1310,1493,1373,1479,1404,1492,1464,1419,428,443,472,397,736,526,464,464,486,457,442,471,484,482,1357,1449,1434,1478,1388,1491,1341,1490,1325,1489,1463,1403,1309,1477,1372,1448,1418,1433,1476,1356,1462,1387,-1439,1475,1340,1447,1402,1474,1324,1461,1371,1473,269,448,1432,1417,1308,1460,-1711,1459,-1727,1441,1099,1099,1446,1386,1431,1401,-1743,1289,1083,1083,1160,1160,1458,1445,1067,1067,1370,1457,1307,1430,1129,1129,1098,1098,268,432,267,416,266,400,-1887,1144,1187,1082,1173,1113,1186,1066,1050,1158,1128,1143,1172,1097,1171,1081,420,391,1157,1112,1170,1142,1127,1065,1169,1049,1156,1096,1141,1111,1155,1080,1126,1154,1064,1153,1140,1095,1048,-2159,1125,1110,1137,-2175,823,823,1139,1138,807,807,384,264,368,263,868,838,853,791,867,822,852,837,866,806,865,790,-2319,851,821,836,352,262,850,805,849,-2399,533,533,835,820,336,261,578,548,563,577,532,532,832,772,562,562,547,547,305,275,560,515,290,290,288,258 }; - static const ma_uint8 tab32[] = { 130,162,193,209,44,28,76,140,9,9,9,9,9,9,9,9,190,254,222,238,126,94,157,157,109,61,173,205}; - static const ma_uint8 tab33[] = { 252,236,220,204,188,172,156,140,124,108,92,76,60,44,28,12 }; - static const ma_int16 tabindex[2*16] = { 0,32,64,98,0,132,180,218,292,364,426,538,648,746,0,1126,1460,1460,1460,1460,1460,1460,1460,1460,1842,1842,1842,1842,1842,1842,1842,1842 }; - static const ma_uint8 g_linbits[] = { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,2,3,4,6,8,10,13,4,5,6,7,8,9,11,13 }; -#define MA_DR_MP3_PEEK_BITS(n) (bs_cache >> (32 - (n))) -#define MA_DR_MP3_FLUSH_BITS(n) { bs_cache <<= (n); bs_sh += (n); } -#define MA_DR_MP3_CHECK_BITS while (bs_sh >= 0) { bs_cache |= (ma_uint32)*bs_next_ptr++ << bs_sh; bs_sh -= 8; } -#define MA_DR_MP3_BSPOS ((bs_next_ptr - bs->buf)*8 - 24 + bs_sh) - float one = 0.0f; - int ireg = 0, big_val_cnt = gr_info->big_values; - const ma_uint8 *sfb = gr_info->sfbtab; - const ma_uint8 *bs_next_ptr = bs->buf + bs->pos/8; - ma_uint32 bs_cache = (((bs_next_ptr[0]*256u + bs_next_ptr[1])*256u + bs_next_ptr[2])*256u + bs_next_ptr[3]) << (bs->pos & 7); - int pairs_to_decode, np, bs_sh = (bs->pos & 7) - 8; - bs_next_ptr += 4; - while (big_val_cnt > 0) - { - int tab_num = gr_info->table_select[ireg]; - int sfb_cnt = gr_info->region_count[ireg++]; - const ma_int16 *codebook = tabs + tabindex[tab_num]; - int linbits = g_linbits[tab_num]; - if (linbits) - { - do - { - np = *sfb++ / 2; - pairs_to_decode = MA_DR_MP3_MIN(big_val_cnt, np); - one = *scf++; - do - { - int j, w = 5; - int leaf = codebook[MA_DR_MP3_PEEK_BITS(w)]; - while (leaf < 0) - { - MA_DR_MP3_FLUSH_BITS(w); - w = leaf & 7; - leaf = codebook[MA_DR_MP3_PEEK_BITS(w) - (leaf >> 3)]; - } - MA_DR_MP3_FLUSH_BITS(leaf >> 8); - for (j = 0; j < 2; j++, dst++, leaf >>= 4) - { - int lsb = leaf & 0x0F; - if (lsb == 15) - { - lsb += MA_DR_MP3_PEEK_BITS(linbits); - MA_DR_MP3_FLUSH_BITS(linbits); - MA_DR_MP3_CHECK_BITS; - *dst = one*ma_dr_mp3_L3_pow_43(lsb)*((ma_int32)bs_cache < 0 ? -1: 1); - } else - { - *dst = g_ma_dr_mp3_pow43[16 + lsb - 16*(bs_cache >> 31)]*one; - } - MA_DR_MP3_FLUSH_BITS(lsb ? 1 : 0); - } - MA_DR_MP3_CHECK_BITS; - } while (--pairs_to_decode); - } while ((big_val_cnt -= np) > 0 && --sfb_cnt >= 0); - } else - { - do - { - np = *sfb++ / 2; - pairs_to_decode = MA_DR_MP3_MIN(big_val_cnt, np); - one = *scf++; - do - { - int j, w = 5; - int leaf = codebook[MA_DR_MP3_PEEK_BITS(w)]; - while (leaf < 0) - { - MA_DR_MP3_FLUSH_BITS(w); - w = leaf & 7; - leaf = codebook[MA_DR_MP3_PEEK_BITS(w) - (leaf >> 3)]; - } - MA_DR_MP3_FLUSH_BITS(leaf >> 8); - for (j = 0; j < 2; j++, dst++, leaf >>= 4) - { - int lsb = leaf & 0x0F; - *dst = g_ma_dr_mp3_pow43[16 + lsb - 16*(bs_cache >> 31)]*one; - MA_DR_MP3_FLUSH_BITS(lsb ? 1 : 0); - } - MA_DR_MP3_CHECK_BITS; - } while (--pairs_to_decode); - } while ((big_val_cnt -= np) > 0 && --sfb_cnt >= 0); - } - } - for (np = 1 - big_val_cnt;; dst += 4) - { - const ma_uint8 *codebook_count1 = (gr_info->count1_table) ? tab33 : tab32; - int leaf = codebook_count1[MA_DR_MP3_PEEK_BITS(4)]; - if (!(leaf & 8)) - { - leaf = codebook_count1[(leaf >> 3) + (bs_cache << 4 >> (32 - (leaf & 3)))]; - } - MA_DR_MP3_FLUSH_BITS(leaf & 7); - if (MA_DR_MP3_BSPOS > layer3gr_limit) - { - break; - } -#define MA_DR_MP3_RELOAD_SCALEFACTOR if (!--np) { np = *sfb++/2; if (!np) break; one = *scf++; } -#define MA_DR_MP3_DEQ_COUNT1(s) if (leaf & (128 >> s)) { dst[s] = ((ma_int32)bs_cache < 0) ? -one : one; MA_DR_MP3_FLUSH_BITS(1) } - MA_DR_MP3_RELOAD_SCALEFACTOR; - MA_DR_MP3_DEQ_COUNT1(0); - MA_DR_MP3_DEQ_COUNT1(1); - MA_DR_MP3_RELOAD_SCALEFACTOR; - MA_DR_MP3_DEQ_COUNT1(2); - MA_DR_MP3_DEQ_COUNT1(3); - MA_DR_MP3_CHECK_BITS; - } - bs->pos = layer3gr_limit; -} -static void ma_dr_mp3_L3_midside_stereo(float *left, int n) -{ - int i = 0; - float *right = left + 576; -#if MA_DR_MP3_HAVE_SIMD - if (ma_dr_mp3_have_simd()) - { - for (; i < n - 3; i += 4) - { - ma_dr_mp3_f4 vl = MA_DR_MP3_VLD(left + i); - ma_dr_mp3_f4 vr = MA_DR_MP3_VLD(right + i); - MA_DR_MP3_VSTORE(left + i, MA_DR_MP3_VADD(vl, vr)); - MA_DR_MP3_VSTORE(right + i, MA_DR_MP3_VSUB(vl, vr)); - } -#ifdef __GNUC__ - if (__builtin_constant_p(n % 4 == 0) && n % 4 == 0) - return; -#endif - } -#endif - for (; i < n; i++) - { - float a = left[i]; - float b = right[i]; - left[i] = a + b; - right[i] = a - b; - } -} -static void ma_dr_mp3_L3_intensity_stereo_band(float *left, int n, float kl, float kr) -{ - int i; - for (i = 0; i < n; i++) - { - left[i + 576] = left[i]*kr; - left[i] = left[i]*kl; - } -} -static void ma_dr_mp3_L3_stereo_top_band(const float *right, const ma_uint8 *sfb, int nbands, int max_band[3]) -{ - int i, k; - max_band[0] = max_band[1] = max_band[2] = -1; - for (i = 0; i < nbands; i++) - { - for (k = 0; k < sfb[i]; k += 2) - { - if (right[k] != 0 || right[k + 1] != 0) - { - max_band[i % 3] = i; - break; - } - } - right += sfb[i]; - } -} -static void ma_dr_mp3_L3_stereo_process(float *left, const ma_uint8 *ist_pos, const ma_uint8 *sfb, const ma_uint8 *hdr, int max_band[3], int mpeg2_sh) -{ - static const float g_pan[7*2] = { 0,1,0.21132487f,0.78867513f,0.36602540f,0.63397460f,0.5f,0.5f,0.63397460f,0.36602540f,0.78867513f,0.21132487f,1,0 }; - unsigned i, max_pos = MA_DR_MP3_HDR_TEST_MPEG1(hdr) ? 7 : 64; - for (i = 0; sfb[i]; i++) - { - unsigned ipos = ist_pos[i]; - if ((int)i > max_band[i % 3] && ipos < max_pos) - { - float kl, kr, s = MA_DR_MP3_HDR_TEST_MS_STEREO(hdr) ? 1.41421356f : 1; - if (MA_DR_MP3_HDR_TEST_MPEG1(hdr)) - { - kl = g_pan[2*ipos]; - kr = g_pan[2*ipos + 1]; - } else - { - kl = 1; - kr = ma_dr_mp3_L3_ldexp_q2(1, (ipos + 1) >> 1 << mpeg2_sh); - if (ipos & 1) - { - kl = kr; - kr = 1; - } - } - ma_dr_mp3_L3_intensity_stereo_band(left, sfb[i], kl*s, kr*s); - } else if (MA_DR_MP3_HDR_TEST_MS_STEREO(hdr)) - { - ma_dr_mp3_L3_midside_stereo(left, sfb[i]); - } - left += sfb[i]; - } -} -static void ma_dr_mp3_L3_intensity_stereo(float *left, ma_uint8 *ist_pos, const ma_dr_mp3_L3_gr_info *gr, const ma_uint8 *hdr) -{ - int max_band[3], n_sfb = gr->n_long_sfb + gr->n_short_sfb; - int i, max_blocks = gr->n_short_sfb ? 3 : 1; - ma_dr_mp3_L3_stereo_top_band(left + 576, gr->sfbtab, n_sfb, max_band); - if (gr->n_long_sfb) - { - max_band[0] = max_band[1] = max_band[2] = MA_DR_MP3_MAX(MA_DR_MP3_MAX(max_band[0], max_band[1]), max_band[2]); - } - for (i = 0; i < max_blocks; i++) - { - int default_pos = MA_DR_MP3_HDR_TEST_MPEG1(hdr) ? 3 : 0; - int itop = n_sfb - max_blocks + i; - int prev = itop - max_blocks; - ist_pos[itop] = (ma_uint8)(max_band[i] >= prev ? default_pos : ist_pos[prev]); - } - ma_dr_mp3_L3_stereo_process(left, ist_pos, gr->sfbtab, hdr, max_band, gr[1].scalefac_compress & 1); -} -static void ma_dr_mp3_L3_reorder(float *grbuf, float *scratch, const ma_uint8 *sfb) -{ - int i, len; - float *src = grbuf, *dst = scratch; - for (;0 != (len = *sfb); sfb += 3, src += 2*len) - { - for (i = 0; i < len; i++, src++) - { - *dst++ = src[0*len]; - *dst++ = src[1*len]; - *dst++ = src[2*len]; - } - } - MA_DR_MP3_COPY_MEMORY(grbuf, scratch, (dst - scratch)*sizeof(float)); -} -static void ma_dr_mp3_L3_antialias(float *grbuf, int nbands) -{ - static const float g_aa[2][8] = { - {0.85749293f,0.88174200f,0.94962865f,0.98331459f,0.99551782f,0.99916056f,0.99989920f,0.99999316f}, - {0.51449576f,0.47173197f,0.31337745f,0.18191320f,0.09457419f,0.04096558f,0.01419856f,0.00369997f} - }; - for (; nbands > 0; nbands--, grbuf += 18) - { - int i = 0; -#if MA_DR_MP3_HAVE_SIMD - if (ma_dr_mp3_have_simd()) for (; i < 8; i += 4) - { - ma_dr_mp3_f4 vu = MA_DR_MP3_VLD(grbuf + 18 + i); - ma_dr_mp3_f4 vd = MA_DR_MP3_VLD(grbuf + 14 - i); - ma_dr_mp3_f4 vc0 = MA_DR_MP3_VLD(g_aa[0] + i); - ma_dr_mp3_f4 vc1 = MA_DR_MP3_VLD(g_aa[1] + i); - vd = MA_DR_MP3_VREV(vd); - MA_DR_MP3_VSTORE(grbuf + 18 + i, MA_DR_MP3_VSUB(MA_DR_MP3_VMUL(vu, vc0), MA_DR_MP3_VMUL(vd, vc1))); - vd = MA_DR_MP3_VADD(MA_DR_MP3_VMUL(vu, vc1), MA_DR_MP3_VMUL(vd, vc0)); - MA_DR_MP3_VSTORE(grbuf + 14 - i, MA_DR_MP3_VREV(vd)); - } -#endif -#ifndef MA_DR_MP3_ONLY_SIMD - for(; i < 8; i++) - { - float u = grbuf[18 + i]; - float d = grbuf[17 - i]; - grbuf[18 + i] = u*g_aa[0][i] - d*g_aa[1][i]; - grbuf[17 - i] = u*g_aa[1][i] + d*g_aa[0][i]; - } -#endif - } -} -static void ma_dr_mp3_L3_dct3_9(float *y) -{ - float s0, s1, s2, s3, s4, s5, s6, s7, s8, t0, t2, t4; - s0 = y[0]; s2 = y[2]; s4 = y[4]; s6 = y[6]; s8 = y[8]; - t0 = s0 + s6*0.5f; - s0 -= s6; - t4 = (s4 + s2)*0.93969262f; - t2 = (s8 + s2)*0.76604444f; - s6 = (s4 - s8)*0.17364818f; - s4 += s8 - s2; - s2 = s0 - s4*0.5f; - y[4] = s4 + s0; - s8 = t0 - t2 + s6; - s0 = t0 - t4 + t2; - s4 = t0 + t4 - s6; - s1 = y[1]; s3 = y[3]; s5 = y[5]; s7 = y[7]; - s3 *= 0.86602540f; - t0 = (s5 + s1)*0.98480775f; - t4 = (s5 - s7)*0.34202014f; - t2 = (s1 + s7)*0.64278761f; - s1 = (s1 - s5 - s7)*0.86602540f; - s5 = t0 - s3 - t2; - s7 = t4 - s3 - t0; - s3 = t4 + s3 - t2; - y[0] = s4 - s7; - y[1] = s2 + s1; - y[2] = s0 - s3; - y[3] = s8 + s5; - y[5] = s8 - s5; - y[6] = s0 + s3; - y[7] = s2 - s1; - y[8] = s4 + s7; -} -static void ma_dr_mp3_L3_imdct36(float *grbuf, float *overlap, const float *window, int nbands) -{ - int i, j; - static const float g_twid9[18] = { - 0.73727734f,0.79335334f,0.84339145f,0.88701083f,0.92387953f,0.95371695f,0.97629601f,0.99144486f,0.99904822f,0.67559021f,0.60876143f,0.53729961f,0.46174861f,0.38268343f,0.30070580f,0.21643961f,0.13052619f,0.04361938f - }; - for (j = 0; j < nbands; j++, grbuf += 18, overlap += 9) - { - float co[9], si[9]; - co[0] = -grbuf[0]; - si[0] = grbuf[17]; - for (i = 0; i < 4; i++) - { - si[8 - 2*i] = grbuf[4*i + 1] - grbuf[4*i + 2]; - co[1 + 2*i] = grbuf[4*i + 1] + grbuf[4*i + 2]; - si[7 - 2*i] = grbuf[4*i + 4] - grbuf[4*i + 3]; - co[2 + 2*i] = -(grbuf[4*i + 3] + grbuf[4*i + 4]); - } - ma_dr_mp3_L3_dct3_9(co); - ma_dr_mp3_L3_dct3_9(si); - si[1] = -si[1]; - si[3] = -si[3]; - si[5] = -si[5]; - si[7] = -si[7]; - i = 0; -#if MA_DR_MP3_HAVE_SIMD - if (ma_dr_mp3_have_simd()) for (; i < 8; i += 4) - { - ma_dr_mp3_f4 vovl = MA_DR_MP3_VLD(overlap + i); - ma_dr_mp3_f4 vc = MA_DR_MP3_VLD(co + i); - ma_dr_mp3_f4 vs = MA_DR_MP3_VLD(si + i); - ma_dr_mp3_f4 vr0 = MA_DR_MP3_VLD(g_twid9 + i); - ma_dr_mp3_f4 vr1 = MA_DR_MP3_VLD(g_twid9 + 9 + i); - ma_dr_mp3_f4 vw0 = MA_DR_MP3_VLD(window + i); - ma_dr_mp3_f4 vw1 = MA_DR_MP3_VLD(window + 9 + i); - ma_dr_mp3_f4 vsum = MA_DR_MP3_VADD(MA_DR_MP3_VMUL(vc, vr1), MA_DR_MP3_VMUL(vs, vr0)); - MA_DR_MP3_VSTORE(overlap + i, MA_DR_MP3_VSUB(MA_DR_MP3_VMUL(vc, vr0), MA_DR_MP3_VMUL(vs, vr1))); - MA_DR_MP3_VSTORE(grbuf + i, MA_DR_MP3_VSUB(MA_DR_MP3_VMUL(vovl, vw0), MA_DR_MP3_VMUL(vsum, vw1))); - vsum = MA_DR_MP3_VADD(MA_DR_MP3_VMUL(vovl, vw1), MA_DR_MP3_VMUL(vsum, vw0)); - MA_DR_MP3_VSTORE(grbuf + 14 - i, MA_DR_MP3_VREV(vsum)); - } -#endif - for (; i < 9; i++) - { - float ovl = overlap[i]; - float sum = co[i]*g_twid9[9 + i] + si[i]*g_twid9[0 + i]; - overlap[i] = co[i]*g_twid9[0 + i] - si[i]*g_twid9[9 + i]; - grbuf[i] = ovl*window[0 + i] - sum*window[9 + i]; - grbuf[17 - i] = ovl*window[9 + i] + sum*window[0 + i]; - } - } -} -static void ma_dr_mp3_L3_idct3(float x0, float x1, float x2, float *dst) -{ - float m1 = x1*0.86602540f; - float a1 = x0 - x2*0.5f; - dst[1] = x0 + x2; - dst[0] = a1 + m1; - dst[2] = a1 - m1; -} -static void ma_dr_mp3_L3_imdct12(float *x, float *dst, float *overlap) -{ - static const float g_twid3[6] = { 0.79335334f,0.92387953f,0.99144486f, 0.60876143f,0.38268343f,0.13052619f }; - float co[3], si[3]; - int i; - ma_dr_mp3_L3_idct3(-x[0], x[6] + x[3], x[12] + x[9], co); - ma_dr_mp3_L3_idct3(x[15], x[12] - x[9], x[6] - x[3], si); - si[1] = -si[1]; - for (i = 0; i < 3; i++) - { - float ovl = overlap[i]; - float sum = co[i]*g_twid3[3 + i] + si[i]*g_twid3[0 + i]; - overlap[i] = co[i]*g_twid3[0 + i] - si[i]*g_twid3[3 + i]; - dst[i] = ovl*g_twid3[2 - i] - sum*g_twid3[5 - i]; - dst[5 - i] = ovl*g_twid3[5 - i] + sum*g_twid3[2 - i]; - } -} -static void ma_dr_mp3_L3_imdct_short(float *grbuf, float *overlap, int nbands) -{ - for (;nbands > 0; nbands--, overlap += 9, grbuf += 18) - { - float tmp[18]; - MA_DR_MP3_COPY_MEMORY(tmp, grbuf, sizeof(tmp)); - MA_DR_MP3_COPY_MEMORY(grbuf, overlap, 6*sizeof(float)); - ma_dr_mp3_L3_imdct12(tmp, grbuf + 6, overlap + 6); - ma_dr_mp3_L3_imdct12(tmp + 1, grbuf + 12, overlap + 6); - ma_dr_mp3_L3_imdct12(tmp + 2, overlap, overlap + 6); - } -} -static void ma_dr_mp3_L3_change_sign(float *grbuf) -{ - int b, i; - for (b = 0, grbuf += 18; b < 32; b += 2, grbuf += 36) - for (i = 1; i < 18; i += 2) - grbuf[i] = -grbuf[i]; -} -static void ma_dr_mp3_L3_imdct_gr(float *grbuf, float *overlap, unsigned block_type, unsigned n_long_bands) -{ - static const float g_mdct_window[2][18] = { - { 0.99904822f,0.99144486f,0.97629601f,0.95371695f,0.92387953f,0.88701083f,0.84339145f,0.79335334f,0.73727734f,0.04361938f,0.13052619f,0.21643961f,0.30070580f,0.38268343f,0.46174861f,0.53729961f,0.60876143f,0.67559021f }, - { 1,1,1,1,1,1,0.99144486f,0.92387953f,0.79335334f,0,0,0,0,0,0,0.13052619f,0.38268343f,0.60876143f } - }; - if (n_long_bands) - { - ma_dr_mp3_L3_imdct36(grbuf, overlap, g_mdct_window[0], n_long_bands); - grbuf += 18*n_long_bands; - overlap += 9*n_long_bands; - } - if (block_type == MA_DR_MP3_SHORT_BLOCK_TYPE) - ma_dr_mp3_L3_imdct_short(grbuf, overlap, 32 - n_long_bands); - else - ma_dr_mp3_L3_imdct36(grbuf, overlap, g_mdct_window[block_type == MA_DR_MP3_STOP_BLOCK_TYPE], 32 - n_long_bands); -} -static void ma_dr_mp3_L3_save_reservoir(ma_dr_mp3dec *h, ma_dr_mp3dec_scratch *s) -{ - int pos = (s->bs.pos + 7)/8u; - int remains = s->bs.limit/8u - pos; - if (remains > MA_DR_MP3_MAX_BITRESERVOIR_BYTES) - { - pos += remains - MA_DR_MP3_MAX_BITRESERVOIR_BYTES; - remains = MA_DR_MP3_MAX_BITRESERVOIR_BYTES; - } - if (remains > 0) - { - MA_DR_MP3_MOVE_MEMORY(h->reserv_buf, s->maindata + pos, remains); - } - h->reserv = remains; -} -static int ma_dr_mp3_L3_restore_reservoir(ma_dr_mp3dec *h, ma_dr_mp3_bs *bs, ma_dr_mp3dec_scratch *s, int main_data_begin) -{ - int frame_bytes = (bs->limit - bs->pos)/8; - int bytes_have = MA_DR_MP3_MIN(h->reserv, main_data_begin); - MA_DR_MP3_COPY_MEMORY(s->maindata, h->reserv_buf + MA_DR_MP3_MAX(0, h->reserv - main_data_begin), MA_DR_MP3_MIN(h->reserv, main_data_begin)); - MA_DR_MP3_COPY_MEMORY(s->maindata + bytes_have, bs->buf + bs->pos/8, frame_bytes); - ma_dr_mp3_bs_init(&s->bs, s->maindata, bytes_have + frame_bytes); - return h->reserv >= main_data_begin; -} -static void ma_dr_mp3_L3_decode(ma_dr_mp3dec *h, ma_dr_mp3dec_scratch *s, ma_dr_mp3_L3_gr_info *gr_info, int nch) -{ - int ch; - for (ch = 0; ch < nch; ch++) - { - int layer3gr_limit = s->bs.pos + gr_info[ch].part_23_length; - ma_dr_mp3_L3_decode_scalefactors(h->header, s->ist_pos[ch], &s->bs, gr_info + ch, s->scf, ch); - ma_dr_mp3_L3_huffman(s->grbuf[ch], &s->bs, gr_info + ch, s->scf, layer3gr_limit); - } - if (MA_DR_MP3_HDR_TEST_I_STEREO(h->header)) - { - ma_dr_mp3_L3_intensity_stereo(s->grbuf[0], s->ist_pos[1], gr_info, h->header); - } else if (MA_DR_MP3_HDR_IS_MS_STEREO(h->header)) - { - ma_dr_mp3_L3_midside_stereo(s->grbuf[0], 576); - } - for (ch = 0; ch < nch; ch++, gr_info++) - { - int aa_bands = 31; - int n_long_bands = (gr_info->mixed_block_flag ? 2 : 0) << (int)(MA_DR_MP3_HDR_GET_MY_SAMPLE_RATE(h->header) == 2); - if (gr_info->n_short_sfb) - { - aa_bands = n_long_bands - 1; - ma_dr_mp3_L3_reorder(s->grbuf[ch] + n_long_bands*18, s->syn[0], gr_info->sfbtab + gr_info->n_long_sfb); - } - ma_dr_mp3_L3_antialias(s->grbuf[ch], aa_bands); - ma_dr_mp3_L3_imdct_gr(s->grbuf[ch], h->mdct_overlap[ch], gr_info->block_type, n_long_bands); - ma_dr_mp3_L3_change_sign(s->grbuf[ch]); - } -} -static void ma_dr_mp3d_DCT_II(float *grbuf, int n) -{ - static const float g_sec[24] = { - 10.19000816f,0.50060302f,0.50241929f,3.40760851f,0.50547093f,0.52249861f,2.05778098f,0.51544732f,0.56694406f,1.48416460f,0.53104258f,0.64682180f,1.16943991f,0.55310392f,0.78815460f,0.97256821f,0.58293498f,1.06067765f,0.83934963f,0.62250412f,1.72244716f,0.74453628f,0.67480832f,5.10114861f - }; - int i, k = 0; -#if MA_DR_MP3_HAVE_SIMD - if (ma_dr_mp3_have_simd()) for (; k < n; k += 4) - { - ma_dr_mp3_f4 t[4][8], *x; - float *y = grbuf + k; - for (x = t[0], i = 0; i < 8; i++, x++) - { - ma_dr_mp3_f4 x0 = MA_DR_MP3_VLD(&y[i*18]); - ma_dr_mp3_f4 x1 = MA_DR_MP3_VLD(&y[(15 - i)*18]); - ma_dr_mp3_f4 x2 = MA_DR_MP3_VLD(&y[(16 + i)*18]); - ma_dr_mp3_f4 x3 = MA_DR_MP3_VLD(&y[(31 - i)*18]); - ma_dr_mp3_f4 t0 = MA_DR_MP3_VADD(x0, x3); - ma_dr_mp3_f4 t1 = MA_DR_MP3_VADD(x1, x2); - ma_dr_mp3_f4 t2 = MA_DR_MP3_VMUL_S(MA_DR_MP3_VSUB(x1, x2), g_sec[3*i + 0]); - ma_dr_mp3_f4 t3 = MA_DR_MP3_VMUL_S(MA_DR_MP3_VSUB(x0, x3), g_sec[3*i + 1]); - x[0] = MA_DR_MP3_VADD(t0, t1); - x[8] = MA_DR_MP3_VMUL_S(MA_DR_MP3_VSUB(t0, t1), g_sec[3*i + 2]); - x[16] = MA_DR_MP3_VADD(t3, t2); - x[24] = MA_DR_MP3_VMUL_S(MA_DR_MP3_VSUB(t3, t2), g_sec[3*i + 2]); - } - for (x = t[0], i = 0; i < 4; i++, x += 8) - { - ma_dr_mp3_f4 x0 = x[0], x1 = x[1], x2 = x[2], x3 = x[3], x4 = x[4], x5 = x[5], x6 = x[6], x7 = x[7], xt; - xt = MA_DR_MP3_VSUB(x0, x7); x0 = MA_DR_MP3_VADD(x0, x7); - x7 = MA_DR_MP3_VSUB(x1, x6); x1 = MA_DR_MP3_VADD(x1, x6); - x6 = MA_DR_MP3_VSUB(x2, x5); x2 = MA_DR_MP3_VADD(x2, x5); - x5 = MA_DR_MP3_VSUB(x3, x4); x3 = MA_DR_MP3_VADD(x3, x4); - x4 = MA_DR_MP3_VSUB(x0, x3); x0 = MA_DR_MP3_VADD(x0, x3); - x3 = MA_DR_MP3_VSUB(x1, x2); x1 = MA_DR_MP3_VADD(x1, x2); - x[0] = MA_DR_MP3_VADD(x0, x1); - x[4] = MA_DR_MP3_VMUL_S(MA_DR_MP3_VSUB(x0, x1), 0.70710677f); - x5 = MA_DR_MP3_VADD(x5, x6); - x6 = MA_DR_MP3_VMUL_S(MA_DR_MP3_VADD(x6, x7), 0.70710677f); - x7 = MA_DR_MP3_VADD(x7, xt); - x3 = MA_DR_MP3_VMUL_S(MA_DR_MP3_VADD(x3, x4), 0.70710677f); - x5 = MA_DR_MP3_VSUB(x5, MA_DR_MP3_VMUL_S(x7, 0.198912367f)); - x7 = MA_DR_MP3_VADD(x7, MA_DR_MP3_VMUL_S(x5, 0.382683432f)); - x5 = MA_DR_MP3_VSUB(x5, MA_DR_MP3_VMUL_S(x7, 0.198912367f)); - x0 = MA_DR_MP3_VSUB(xt, x6); xt = MA_DR_MP3_VADD(xt, x6); - x[1] = MA_DR_MP3_VMUL_S(MA_DR_MP3_VADD(xt, x7), 0.50979561f); - x[2] = MA_DR_MP3_VMUL_S(MA_DR_MP3_VADD(x4, x3), 0.54119611f); - x[3] = MA_DR_MP3_VMUL_S(MA_DR_MP3_VSUB(x0, x5), 0.60134488f); - x[5] = MA_DR_MP3_VMUL_S(MA_DR_MP3_VADD(x0, x5), 0.89997619f); - x[6] = MA_DR_MP3_VMUL_S(MA_DR_MP3_VSUB(x4, x3), 1.30656302f); - x[7] = MA_DR_MP3_VMUL_S(MA_DR_MP3_VSUB(xt, x7), 2.56291556f); - } - if (k > n - 3) - { -#if MA_DR_MP3_HAVE_SSE -#define MA_DR_MP3_VSAVE2(i, v) _mm_storel_pi((__m64 *)(void*)&y[i*18], v) -#else -#define MA_DR_MP3_VSAVE2(i, v) vst1_f32((float32_t *)&y[(i)*18], vget_low_f32(v)) -#endif - for (i = 0; i < 7; i++, y += 4*18) - { - ma_dr_mp3_f4 s = MA_DR_MP3_VADD(t[3][i], t[3][i + 1]); - MA_DR_MP3_VSAVE2(0, t[0][i]); - MA_DR_MP3_VSAVE2(1, MA_DR_MP3_VADD(t[2][i], s)); - MA_DR_MP3_VSAVE2(2, MA_DR_MP3_VADD(t[1][i], t[1][i + 1])); - MA_DR_MP3_VSAVE2(3, MA_DR_MP3_VADD(t[2][1 + i], s)); - } - MA_DR_MP3_VSAVE2(0, t[0][7]); - MA_DR_MP3_VSAVE2(1, MA_DR_MP3_VADD(t[2][7], t[3][7])); - MA_DR_MP3_VSAVE2(2, t[1][7]); - MA_DR_MP3_VSAVE2(3, t[3][7]); - } else - { -#define MA_DR_MP3_VSAVE4(i, v) MA_DR_MP3_VSTORE(&y[(i)*18], v) - for (i = 0; i < 7; i++, y += 4*18) - { - ma_dr_mp3_f4 s = MA_DR_MP3_VADD(t[3][i], t[3][i + 1]); - MA_DR_MP3_VSAVE4(0, t[0][i]); - MA_DR_MP3_VSAVE4(1, MA_DR_MP3_VADD(t[2][i], s)); - MA_DR_MP3_VSAVE4(2, MA_DR_MP3_VADD(t[1][i], t[1][i + 1])); - MA_DR_MP3_VSAVE4(3, MA_DR_MP3_VADD(t[2][1 + i], s)); - } - MA_DR_MP3_VSAVE4(0, t[0][7]); - MA_DR_MP3_VSAVE4(1, MA_DR_MP3_VADD(t[2][7], t[3][7])); - MA_DR_MP3_VSAVE4(2, t[1][7]); - MA_DR_MP3_VSAVE4(3, t[3][7]); - } - } else -#endif -#ifdef MA_DR_MP3_ONLY_SIMD - {} -#else - for (; k < n; k++) - { - float t[4][8], *x, *y = grbuf + k; - for (x = t[0], i = 0; i < 8; i++, x++) - { - float x0 = y[i*18]; - float x1 = y[(15 - i)*18]; - float x2 = y[(16 + i)*18]; - float x3 = y[(31 - i)*18]; - float t0 = x0 + x3; - float t1 = x1 + x2; - float t2 = (x1 - x2)*g_sec[3*i + 0]; - float t3 = (x0 - x3)*g_sec[3*i + 1]; - x[0] = t0 + t1; - x[8] = (t0 - t1)*g_sec[3*i + 2]; - x[16] = t3 + t2; - x[24] = (t3 - t2)*g_sec[3*i + 2]; - } - for (x = t[0], i = 0; i < 4; i++, x += 8) - { - float x0 = x[0], x1 = x[1], x2 = x[2], x3 = x[3], x4 = x[4], x5 = x[5], x6 = x[6], x7 = x[7], xt; - xt = x0 - x7; x0 += x7; - x7 = x1 - x6; x1 += x6; - x6 = x2 - x5; x2 += x5; - x5 = x3 - x4; x3 += x4; - x4 = x0 - x3; x0 += x3; - x3 = x1 - x2; x1 += x2; - x[0] = x0 + x1; - x[4] = (x0 - x1)*0.70710677f; - x5 = x5 + x6; - x6 = (x6 + x7)*0.70710677f; - x7 = x7 + xt; - x3 = (x3 + x4)*0.70710677f; - x5 -= x7*0.198912367f; - x7 += x5*0.382683432f; - x5 -= x7*0.198912367f; - x0 = xt - x6; xt += x6; - x[1] = (xt + x7)*0.50979561f; - x[2] = (x4 + x3)*0.54119611f; - x[3] = (x0 - x5)*0.60134488f; - x[5] = (x0 + x5)*0.89997619f; - x[6] = (x4 - x3)*1.30656302f; - x[7] = (xt - x7)*2.56291556f; - } - for (i = 0; i < 7; i++, y += 4*18) - { - y[0*18] = t[0][i]; - y[1*18] = t[2][i] + t[3][i] + t[3][i + 1]; - y[2*18] = t[1][i] + t[1][i + 1]; - y[3*18] = t[2][i + 1] + t[3][i] + t[3][i + 1]; - } - y[0*18] = t[0][7]; - y[1*18] = t[2][7] + t[3][7]; - y[2*18] = t[1][7]; - y[3*18] = t[3][7]; - } -#endif -} -#ifndef MA_DR_MP3_FLOAT_OUTPUT -typedef ma_int16 ma_dr_mp3d_sample_t; -static ma_int16 ma_dr_mp3d_scale_pcm(float sample) -{ - ma_int16 s; -#if MA_DR_MP3_HAVE_ARMV6 - ma_int32 s32 = (ma_int32)(sample + .5f); - s32 -= (s32 < 0); - s = (ma_int16)ma_dr_mp3_clip_int16_arm(s32); -#else - if (sample >= 32766.5f) return (ma_int16) 32767; - if (sample <= -32767.5f) return (ma_int16)-32768; - s = (ma_int16)(sample + .5f); - s -= (s < 0); -#endif - return s; -} -#else -typedef float ma_dr_mp3d_sample_t; -static float ma_dr_mp3d_scale_pcm(float sample) -{ - return sample*(1.f/32768.f); -} -#endif -static void ma_dr_mp3d_synth_pair(ma_dr_mp3d_sample_t *pcm, int nch, const float *z) -{ - float a; - a = (z[14*64] - z[ 0]) * 29; - a += (z[ 1*64] + z[13*64]) * 213; - a += (z[12*64] - z[ 2*64]) * 459; - a += (z[ 3*64] + z[11*64]) * 2037; - a += (z[10*64] - z[ 4*64]) * 5153; - a += (z[ 5*64] + z[ 9*64]) * 6574; - a += (z[ 8*64] - z[ 6*64]) * 37489; - a += z[ 7*64] * 75038; - pcm[0] = ma_dr_mp3d_scale_pcm(a); - z += 2; - a = z[14*64] * 104; - a += z[12*64] * 1567; - a += z[10*64] * 9727; - a += z[ 8*64] * 64019; - a += z[ 6*64] * -9975; - a += z[ 4*64] * -45; - a += z[ 2*64] * 146; - a += z[ 0*64] * -5; - pcm[16*nch] = ma_dr_mp3d_scale_pcm(a); -} -static void ma_dr_mp3d_synth(float *xl, ma_dr_mp3d_sample_t *dstl, int nch, float *lins) -{ - int i; - float *xr = xl + 576*(nch - 1); - ma_dr_mp3d_sample_t *dstr = dstl + (nch - 1); - static const float g_win[] = { - -1,26,-31,208,218,401,-519,2063,2000,4788,-5517,7134,5959,35640,-39336,74992, - -1,24,-35,202,222,347,-581,2080,1952,4425,-5879,7640,5288,33791,-41176,74856, - -1,21,-38,196,225,294,-645,2087,1893,4063,-6237,8092,4561,31947,-43006,74630, - -1,19,-41,190,227,244,-711,2085,1822,3705,-6589,8492,3776,30112,-44821,74313, - -1,17,-45,183,228,197,-779,2075,1739,3351,-6935,8840,2935,28289,-46617,73908, - -1,16,-49,176,228,153,-848,2057,1644,3004,-7271,9139,2037,26482,-48390,73415, - -2,14,-53,169,227,111,-919,2032,1535,2663,-7597,9389,1082,24694,-50137,72835, - -2,13,-58,161,224,72,-991,2001,1414,2330,-7910,9592,70,22929,-51853,72169, - -2,11,-63,154,221,36,-1064,1962,1280,2006,-8209,9750,-998,21189,-53534,71420, - -2,10,-68,147,215,2,-1137,1919,1131,1692,-8491,9863,-2122,19478,-55178,70590, - -3,9,-73,139,208,-29,-1210,1870,970,1388,-8755,9935,-3300,17799,-56778,69679, - -3,8,-79,132,200,-57,-1283,1817,794,1095,-8998,9966,-4533,16155,-58333,68692, - -4,7,-85,125,189,-83,-1356,1759,605,814,-9219,9959,-5818,14548,-59838,67629, - -4,7,-91,117,177,-106,-1428,1698,402,545,-9416,9916,-7154,12980,-61289,66494, - -5,6,-97,111,163,-127,-1498,1634,185,288,-9585,9838,-8540,11455,-62684,65290 - }; - float *zlin = lins + 15*64; - const float *w = g_win; - zlin[4*15] = xl[18*16]; - zlin[4*15 + 1] = xr[18*16]; - zlin[4*15 + 2] = xl[0]; - zlin[4*15 + 3] = xr[0]; - zlin[4*31] = xl[1 + 18*16]; - zlin[4*31 + 1] = xr[1 + 18*16]; - zlin[4*31 + 2] = xl[1]; - zlin[4*31 + 3] = xr[1]; - ma_dr_mp3d_synth_pair(dstr, nch, lins + 4*15 + 1); - ma_dr_mp3d_synth_pair(dstr + 32*nch, nch, lins + 4*15 + 64 + 1); - ma_dr_mp3d_synth_pair(dstl, nch, lins + 4*15); - ma_dr_mp3d_synth_pair(dstl + 32*nch, nch, lins + 4*15 + 64); -#if MA_DR_MP3_HAVE_SIMD - if (ma_dr_mp3_have_simd()) for (i = 14; i >= 0; i--) - { -#define MA_DR_MP3_VLOAD(k) ma_dr_mp3_f4 w0 = MA_DR_MP3_VSET(*w++); ma_dr_mp3_f4 w1 = MA_DR_MP3_VSET(*w++); ma_dr_mp3_f4 vz = MA_DR_MP3_VLD(&zlin[4*i - 64*k]); ma_dr_mp3_f4 vy = MA_DR_MP3_VLD(&zlin[4*i - 64*(15 - k)]); -#define MA_DR_MP3_V0(k) { MA_DR_MP3_VLOAD(k) b = MA_DR_MP3_VADD(MA_DR_MP3_VMUL(vz, w1), MA_DR_MP3_VMUL(vy, w0)) ; a = MA_DR_MP3_VSUB(MA_DR_MP3_VMUL(vz, w0), MA_DR_MP3_VMUL(vy, w1)); } -#define MA_DR_MP3_V1(k) { MA_DR_MP3_VLOAD(k) b = MA_DR_MP3_VADD(b, MA_DR_MP3_VADD(MA_DR_MP3_VMUL(vz, w1), MA_DR_MP3_VMUL(vy, w0))); a = MA_DR_MP3_VADD(a, MA_DR_MP3_VSUB(MA_DR_MP3_VMUL(vz, w0), MA_DR_MP3_VMUL(vy, w1))); } -#define MA_DR_MP3_V2(k) { MA_DR_MP3_VLOAD(k) b = MA_DR_MP3_VADD(b, MA_DR_MP3_VADD(MA_DR_MP3_VMUL(vz, w1), MA_DR_MP3_VMUL(vy, w0))); a = MA_DR_MP3_VADD(a, MA_DR_MP3_VSUB(MA_DR_MP3_VMUL(vy, w1), MA_DR_MP3_VMUL(vz, w0))); } - ma_dr_mp3_f4 a, b; - zlin[4*i] = xl[18*(31 - i)]; - zlin[4*i + 1] = xr[18*(31 - i)]; - zlin[4*i + 2] = xl[1 + 18*(31 - i)]; - zlin[4*i + 3] = xr[1 + 18*(31 - i)]; - zlin[4*i + 64] = xl[1 + 18*(1 + i)]; - zlin[4*i + 64 + 1] = xr[1 + 18*(1 + i)]; - zlin[4*i - 64 + 2] = xl[18*(1 + i)]; - zlin[4*i - 64 + 3] = xr[18*(1 + i)]; - MA_DR_MP3_V0(0) MA_DR_MP3_V2(1) MA_DR_MP3_V1(2) MA_DR_MP3_V2(3) MA_DR_MP3_V1(4) MA_DR_MP3_V2(5) MA_DR_MP3_V1(6) MA_DR_MP3_V2(7) - { -#ifndef MA_DR_MP3_FLOAT_OUTPUT -#if MA_DR_MP3_HAVE_SSE - static const ma_dr_mp3_f4 g_max = { 32767.0f, 32767.0f, 32767.0f, 32767.0f }; - static const ma_dr_mp3_f4 g_min = { -32768.0f, -32768.0f, -32768.0f, -32768.0f }; - __m128i pcm8 = _mm_packs_epi32(_mm_cvtps_epi32(_mm_max_ps(_mm_min_ps(a, g_max), g_min)), - _mm_cvtps_epi32(_mm_max_ps(_mm_min_ps(b, g_max), g_min))); - dstr[(15 - i)*nch] = (ma_int16)_mm_extract_epi16(pcm8, 1); - dstr[(17 + i)*nch] = (ma_int16)_mm_extract_epi16(pcm8, 5); - dstl[(15 - i)*nch] = (ma_int16)_mm_extract_epi16(pcm8, 0); - dstl[(17 + i)*nch] = (ma_int16)_mm_extract_epi16(pcm8, 4); - dstr[(47 - i)*nch] = (ma_int16)_mm_extract_epi16(pcm8, 3); - dstr[(49 + i)*nch] = (ma_int16)_mm_extract_epi16(pcm8, 7); - dstl[(47 - i)*nch] = (ma_int16)_mm_extract_epi16(pcm8, 2); - dstl[(49 + i)*nch] = (ma_int16)_mm_extract_epi16(pcm8, 6); -#else - int16x4_t pcma, pcmb; - a = MA_DR_MP3_VADD(a, MA_DR_MP3_VSET(0.5f)); - b = MA_DR_MP3_VADD(b, MA_DR_MP3_VSET(0.5f)); - pcma = vqmovn_s32(vqaddq_s32(vcvtq_s32_f32(a), vreinterpretq_s32_u32(vcltq_f32(a, MA_DR_MP3_VSET(0))))); - pcmb = vqmovn_s32(vqaddq_s32(vcvtq_s32_f32(b), vreinterpretq_s32_u32(vcltq_f32(b, MA_DR_MP3_VSET(0))))); - vst1_lane_s16(dstr + (15 - i)*nch, pcma, 1); - vst1_lane_s16(dstr + (17 + i)*nch, pcmb, 1); - vst1_lane_s16(dstl + (15 - i)*nch, pcma, 0); - vst1_lane_s16(dstl + (17 + i)*nch, pcmb, 0); - vst1_lane_s16(dstr + (47 - i)*nch, pcma, 3); - vst1_lane_s16(dstr + (49 + i)*nch, pcmb, 3); - vst1_lane_s16(dstl + (47 - i)*nch, pcma, 2); - vst1_lane_s16(dstl + (49 + i)*nch, pcmb, 2); -#endif -#else - #if MA_DR_MP3_HAVE_SSE - static const ma_dr_mp3_f4 g_scale = { 1.0f/32768.0f, 1.0f/32768.0f, 1.0f/32768.0f, 1.0f/32768.0f }; - #else - const ma_dr_mp3_f4 g_scale = vdupq_n_f32(1.0f/32768.0f); - #endif - a = MA_DR_MP3_VMUL(a, g_scale); - b = MA_DR_MP3_VMUL(b, g_scale); -#if MA_DR_MP3_HAVE_SSE - _mm_store_ss(dstr + (15 - i)*nch, _mm_shuffle_ps(a, a, _MM_SHUFFLE(1, 1, 1, 1))); - _mm_store_ss(dstr + (17 + i)*nch, _mm_shuffle_ps(b, b, _MM_SHUFFLE(1, 1, 1, 1))); - _mm_store_ss(dstl + (15 - i)*nch, _mm_shuffle_ps(a, a, _MM_SHUFFLE(0, 0, 0, 0))); - _mm_store_ss(dstl + (17 + i)*nch, _mm_shuffle_ps(b, b, _MM_SHUFFLE(0, 0, 0, 0))); - _mm_store_ss(dstr + (47 - i)*nch, _mm_shuffle_ps(a, a, _MM_SHUFFLE(3, 3, 3, 3))); - _mm_store_ss(dstr + (49 + i)*nch, _mm_shuffle_ps(b, b, _MM_SHUFFLE(3, 3, 3, 3))); - _mm_store_ss(dstl + (47 - i)*nch, _mm_shuffle_ps(a, a, _MM_SHUFFLE(2, 2, 2, 2))); - _mm_store_ss(dstl + (49 + i)*nch, _mm_shuffle_ps(b, b, _MM_SHUFFLE(2, 2, 2, 2))); -#else - vst1q_lane_f32(dstr + (15 - i)*nch, a, 1); - vst1q_lane_f32(dstr + (17 + i)*nch, b, 1); - vst1q_lane_f32(dstl + (15 - i)*nch, a, 0); - vst1q_lane_f32(dstl + (17 + i)*nch, b, 0); - vst1q_lane_f32(dstr + (47 - i)*nch, a, 3); - vst1q_lane_f32(dstr + (49 + i)*nch, b, 3); - vst1q_lane_f32(dstl + (47 - i)*nch, a, 2); - vst1q_lane_f32(dstl + (49 + i)*nch, b, 2); -#endif -#endif - } - } else -#endif -#ifdef MA_DR_MP3_ONLY_SIMD - {} -#else - for (i = 14; i >= 0; i--) - { -#define MA_DR_MP3_LOAD(k) float w0 = *w++; float w1 = *w++; float *vz = &zlin[4*i - k*64]; float *vy = &zlin[4*i - (15 - k)*64]; -#define MA_DR_MP3_S0(k) { int j; MA_DR_MP3_LOAD(k); for (j = 0; j < 4; j++) b[j] = vz[j]*w1 + vy[j]*w0, a[j] = vz[j]*w0 - vy[j]*w1; } -#define MA_DR_MP3_S1(k) { int j; MA_DR_MP3_LOAD(k); for (j = 0; j < 4; j++) b[j] += vz[j]*w1 + vy[j]*w0, a[j] += vz[j]*w0 - vy[j]*w1; } -#define MA_DR_MP3_S2(k) { int j; MA_DR_MP3_LOAD(k); for (j = 0; j < 4; j++) b[j] += vz[j]*w1 + vy[j]*w0, a[j] += vy[j]*w1 - vz[j]*w0; } - float a[4], b[4]; - zlin[4*i] = xl[18*(31 - i)]; - zlin[4*i + 1] = xr[18*(31 - i)]; - zlin[4*i + 2] = xl[1 + 18*(31 - i)]; - zlin[4*i + 3] = xr[1 + 18*(31 - i)]; - zlin[4*(i + 16)] = xl[1 + 18*(1 + i)]; - zlin[4*(i + 16) + 1] = xr[1 + 18*(1 + i)]; - zlin[4*(i - 16) + 2] = xl[18*(1 + i)]; - zlin[4*(i - 16) + 3] = xr[18*(1 + i)]; - MA_DR_MP3_S0(0) MA_DR_MP3_S2(1) MA_DR_MP3_S1(2) MA_DR_MP3_S2(3) MA_DR_MP3_S1(4) MA_DR_MP3_S2(5) MA_DR_MP3_S1(6) MA_DR_MP3_S2(7) - dstr[(15 - i)*nch] = ma_dr_mp3d_scale_pcm(a[1]); - dstr[(17 + i)*nch] = ma_dr_mp3d_scale_pcm(b[1]); - dstl[(15 - i)*nch] = ma_dr_mp3d_scale_pcm(a[0]); - dstl[(17 + i)*nch] = ma_dr_mp3d_scale_pcm(b[0]); - dstr[(47 - i)*nch] = ma_dr_mp3d_scale_pcm(a[3]); - dstr[(49 + i)*nch] = ma_dr_mp3d_scale_pcm(b[3]); - dstl[(47 - i)*nch] = ma_dr_mp3d_scale_pcm(a[2]); - dstl[(49 + i)*nch] = ma_dr_mp3d_scale_pcm(b[2]); - } -#endif -} -static void ma_dr_mp3d_synth_granule(float *qmf_state, float *grbuf, int nbands, int nch, ma_dr_mp3d_sample_t *pcm, float *lins) -{ - int i; - for (i = 0; i < nch; i++) - { - ma_dr_mp3d_DCT_II(grbuf + 576*i, nbands); - } - MA_DR_MP3_COPY_MEMORY(lins, qmf_state, sizeof(float)*15*64); - for (i = 0; i < nbands; i += 2) - { - ma_dr_mp3d_synth(grbuf + i, pcm + 32*nch*i, nch, lins + i*64); - } -#ifndef MA_DR_MP3_NONSTANDARD_BUT_LOGICAL - if (nch == 1) - { - for (i = 0; i < 15*64; i += 2) - { - qmf_state[i] = lins[nbands*64 + i]; - } - } else -#endif - { - MA_DR_MP3_COPY_MEMORY(qmf_state, lins + nbands*64, sizeof(float)*15*64); - } -} -static int ma_dr_mp3d_match_frame(const ma_uint8 *hdr, int mp3_bytes, int frame_bytes) -{ - int i, nmatch; - for (i = 0, nmatch = 0; nmatch < MA_DR_MP3_MAX_FRAME_SYNC_MATCHES; nmatch++) - { - i += ma_dr_mp3_hdr_frame_bytes(hdr + i, frame_bytes) + ma_dr_mp3_hdr_padding(hdr + i); - if (i + MA_DR_MP3_HDR_SIZE > mp3_bytes) - return nmatch > 0; - if (!ma_dr_mp3_hdr_compare(hdr, hdr + i)) - return 0; - } - return 1; -} -static int ma_dr_mp3d_find_frame(const ma_uint8 *mp3, int mp3_bytes, int *free_format_bytes, int *ptr_frame_bytes) -{ - int i, k; - for (i = 0; i < mp3_bytes - MA_DR_MP3_HDR_SIZE; i++, mp3++) - { - if (ma_dr_mp3_hdr_valid(mp3)) - { - int frame_bytes = ma_dr_mp3_hdr_frame_bytes(mp3, *free_format_bytes); - int frame_and_padding = frame_bytes + ma_dr_mp3_hdr_padding(mp3); - for (k = MA_DR_MP3_HDR_SIZE; !frame_bytes && k < MA_DR_MP3_MAX_FREE_FORMAT_FRAME_SIZE && i + 2*k < mp3_bytes - MA_DR_MP3_HDR_SIZE; k++) - { - if (ma_dr_mp3_hdr_compare(mp3, mp3 + k)) - { - int fb = k - ma_dr_mp3_hdr_padding(mp3); - int nextfb = fb + ma_dr_mp3_hdr_padding(mp3 + k); - if (i + k + nextfb + MA_DR_MP3_HDR_SIZE > mp3_bytes || !ma_dr_mp3_hdr_compare(mp3, mp3 + k + nextfb)) - continue; - frame_and_padding = k; - frame_bytes = fb; - *free_format_bytes = fb; - } - } - if ((frame_bytes && i + frame_and_padding <= mp3_bytes && - ma_dr_mp3d_match_frame(mp3, mp3_bytes - i, frame_bytes)) || - (!i && frame_and_padding == mp3_bytes)) - { - *ptr_frame_bytes = frame_and_padding; - return i; - } - *free_format_bytes = 0; - } - } - *ptr_frame_bytes = 0; - return mp3_bytes; -} -MA_API void ma_dr_mp3dec_init(ma_dr_mp3dec *dec) -{ - dec->header[0] = 0; -} -MA_API int ma_dr_mp3dec_decode_frame(ma_dr_mp3dec *dec, const ma_uint8 *mp3, int mp3_bytes, void *pcm, ma_dr_mp3dec_frame_info *info) -{ - int i = 0, igr, frame_size = 0, success = 1; - const ma_uint8 *hdr; - ma_dr_mp3_bs bs_frame[1]; - ma_dr_mp3dec_scratch scratch; - if (mp3_bytes > 4 && dec->header[0] == 0xff && ma_dr_mp3_hdr_compare(dec->header, mp3)) - { - frame_size = ma_dr_mp3_hdr_frame_bytes(mp3, dec->free_format_bytes) + ma_dr_mp3_hdr_padding(mp3); - if (frame_size != mp3_bytes && (frame_size + MA_DR_MP3_HDR_SIZE > mp3_bytes || !ma_dr_mp3_hdr_compare(mp3, mp3 + frame_size))) - { - frame_size = 0; - } - } - if (!frame_size) - { - MA_DR_MP3_ZERO_MEMORY(dec, sizeof(ma_dr_mp3dec)); - i = ma_dr_mp3d_find_frame(mp3, mp3_bytes, &dec->free_format_bytes, &frame_size); - if (!frame_size || i + frame_size > mp3_bytes) - { - info->frame_bytes = i; - return 0; - } - } - hdr = mp3 + i; - MA_DR_MP3_COPY_MEMORY(dec->header, hdr, MA_DR_MP3_HDR_SIZE); - info->frame_bytes = i + frame_size; - info->channels = MA_DR_MP3_HDR_IS_MONO(hdr) ? 1 : 2; - info->hz = ma_dr_mp3_hdr_sample_rate_hz(hdr); - info->layer = 4 - MA_DR_MP3_HDR_GET_LAYER(hdr); - info->bitrate_kbps = ma_dr_mp3_hdr_bitrate_kbps(hdr); - ma_dr_mp3_bs_init(bs_frame, hdr + MA_DR_MP3_HDR_SIZE, frame_size - MA_DR_MP3_HDR_SIZE); - if (MA_DR_MP3_HDR_IS_CRC(hdr)) - { - ma_dr_mp3_bs_get_bits(bs_frame, 16); - } - if (info->layer == 3) - { - int main_data_begin = ma_dr_mp3_L3_read_side_info(bs_frame, scratch.gr_info, hdr); - if (main_data_begin < 0 || bs_frame->pos > bs_frame->limit) - { - ma_dr_mp3dec_init(dec); - return 0; - } - success = ma_dr_mp3_L3_restore_reservoir(dec, bs_frame, &scratch, main_data_begin); - if (success && pcm != NULL) - { - for (igr = 0; igr < (MA_DR_MP3_HDR_TEST_MPEG1(hdr) ? 2 : 1); igr++, pcm = MA_DR_MP3_OFFSET_PTR(pcm, sizeof(ma_dr_mp3d_sample_t)*576*info->channels)) - { - MA_DR_MP3_ZERO_MEMORY(scratch.grbuf[0], 576*2*sizeof(float)); - ma_dr_mp3_L3_decode(dec, &scratch, scratch.gr_info + igr*info->channels, info->channels); - ma_dr_mp3d_synth_granule(dec->qmf_state, scratch.grbuf[0], 18, info->channels, (ma_dr_mp3d_sample_t*)pcm, scratch.syn[0]); - } - } - ma_dr_mp3_L3_save_reservoir(dec, &scratch); - } else - { -#ifdef MA_DR_MP3_ONLY_MP3 - return 0; -#else - ma_dr_mp3_L12_scale_info sci[1]; - if (pcm == NULL) { - return ma_dr_mp3_hdr_frame_samples(hdr); - } - ma_dr_mp3_L12_read_scale_info(hdr, bs_frame, sci); - MA_DR_MP3_ZERO_MEMORY(scratch.grbuf[0], 576*2*sizeof(float)); - for (i = 0, igr = 0; igr < 3; igr++) - { - if (12 == (i += ma_dr_mp3_L12_dequantize_granule(scratch.grbuf[0] + i, bs_frame, sci, info->layer | 1))) - { - i = 0; - ma_dr_mp3_L12_apply_scf_384(sci, sci->scf + igr, scratch.grbuf[0]); - ma_dr_mp3d_synth_granule(dec->qmf_state, scratch.grbuf[0], 12, info->channels, (ma_dr_mp3d_sample_t*)pcm, scratch.syn[0]); - MA_DR_MP3_ZERO_MEMORY(scratch.grbuf[0], 576*2*sizeof(float)); - pcm = MA_DR_MP3_OFFSET_PTR(pcm, sizeof(ma_dr_mp3d_sample_t)*384*info->channels); - } - if (bs_frame->pos > bs_frame->limit) - { - ma_dr_mp3dec_init(dec); - return 0; - } - } -#endif - } - return success*ma_dr_mp3_hdr_frame_samples(dec->header); -} -MA_API void ma_dr_mp3dec_f32_to_s16(const float *in, ma_int16 *out, size_t num_samples) -{ - size_t i = 0; -#if MA_DR_MP3_HAVE_SIMD - size_t aligned_count = num_samples & ~7; - for(; i < aligned_count; i+=8) - { - ma_dr_mp3_f4 scale = MA_DR_MP3_VSET(32768.0f); - ma_dr_mp3_f4 a = MA_DR_MP3_VMUL(MA_DR_MP3_VLD(&in[i ]), scale); - ma_dr_mp3_f4 b = MA_DR_MP3_VMUL(MA_DR_MP3_VLD(&in[i+4]), scale); -#if MA_DR_MP3_HAVE_SSE - ma_dr_mp3_f4 s16max = MA_DR_MP3_VSET( 32767.0f); - ma_dr_mp3_f4 s16min = MA_DR_MP3_VSET(-32768.0f); - __m128i pcm8 = _mm_packs_epi32(_mm_cvtps_epi32(_mm_max_ps(_mm_min_ps(a, s16max), s16min)), - _mm_cvtps_epi32(_mm_max_ps(_mm_min_ps(b, s16max), s16min))); - out[i ] = (ma_int16)_mm_extract_epi16(pcm8, 0); - out[i+1] = (ma_int16)_mm_extract_epi16(pcm8, 1); - out[i+2] = (ma_int16)_mm_extract_epi16(pcm8, 2); - out[i+3] = (ma_int16)_mm_extract_epi16(pcm8, 3); - out[i+4] = (ma_int16)_mm_extract_epi16(pcm8, 4); - out[i+5] = (ma_int16)_mm_extract_epi16(pcm8, 5); - out[i+6] = (ma_int16)_mm_extract_epi16(pcm8, 6); - out[i+7] = (ma_int16)_mm_extract_epi16(pcm8, 7); -#else - int16x4_t pcma, pcmb; - a = MA_DR_MP3_VADD(a, MA_DR_MP3_VSET(0.5f)); - b = MA_DR_MP3_VADD(b, MA_DR_MP3_VSET(0.5f)); - pcma = vqmovn_s32(vqaddq_s32(vcvtq_s32_f32(a), vreinterpretq_s32_u32(vcltq_f32(a, MA_DR_MP3_VSET(0))))); - pcmb = vqmovn_s32(vqaddq_s32(vcvtq_s32_f32(b), vreinterpretq_s32_u32(vcltq_f32(b, MA_DR_MP3_VSET(0))))); - vst1_lane_s16(out+i , pcma, 0); - vst1_lane_s16(out+i+1, pcma, 1); - vst1_lane_s16(out+i+2, pcma, 2); - vst1_lane_s16(out+i+3, pcma, 3); - vst1_lane_s16(out+i+4, pcmb, 0); - vst1_lane_s16(out+i+5, pcmb, 1); - vst1_lane_s16(out+i+6, pcmb, 2); - vst1_lane_s16(out+i+7, pcmb, 3); -#endif - } -#endif - for(; i < num_samples; i++) - { - float sample = in[i] * 32768.0f; - if (sample >= 32766.5f) - out[i] = (ma_int16) 32767; - else if (sample <= -32767.5f) - out[i] = (ma_int16)-32768; - else - { - short s = (ma_int16)(sample + .5f); - s -= (s < 0); - out[i] = s; - } - } -} -#ifndef MA_DR_MP3_SEEK_LEADING_MP3_FRAMES -#define MA_DR_MP3_SEEK_LEADING_MP3_FRAMES 2 -#endif -#define MA_DR_MP3_MIN_DATA_CHUNK_SIZE 16384 -#ifndef MA_DR_MP3_DATA_CHUNK_SIZE -#define MA_DR_MP3_DATA_CHUNK_SIZE (MA_DR_MP3_MIN_DATA_CHUNK_SIZE*4) -#endif -#define MA_DR_MP3_COUNTOF(x) (sizeof(x) / sizeof(x[0])) -#define MA_DR_MP3_CLAMP(x, lo, hi) (MA_DR_MP3_MAX(lo, MA_DR_MP3_MIN(x, hi))) -#ifndef MA_DR_MP3_PI_D -#define MA_DR_MP3_PI_D 3.14159265358979323846264 -#endif -#define MA_DR_MP3_DEFAULT_RESAMPLER_LPF_ORDER 2 -static MA_INLINE float ma_dr_mp3_mix_f32(float x, float y, float a) -{ - return x*(1-a) + y*a; -} -static MA_INLINE float ma_dr_mp3_mix_f32_fast(float x, float y, float a) -{ - float r0 = (y - x); - float r1 = r0*a; - return x + r1; -} -static MA_INLINE ma_uint32 ma_dr_mp3_gcf_u32(ma_uint32 a, ma_uint32 b) -{ - for (;;) { - if (b == 0) { - break; - } else { - ma_uint32 t = a; - a = b; - b = t % a; - } - } - return a; -} -static void* ma_dr_mp3__malloc_default(size_t sz, void* pUserData) -{ - (void)pUserData; - return MA_DR_MP3_MALLOC(sz); -} -static void* ma_dr_mp3__realloc_default(void* p, size_t sz, void* pUserData) -{ - (void)pUserData; - return MA_DR_MP3_REALLOC(p, sz); -} -static void ma_dr_mp3__free_default(void* p, void* pUserData) -{ - (void)pUserData; - MA_DR_MP3_FREE(p); -} -static void* ma_dr_mp3__malloc_from_callbacks(size_t sz, const ma_allocation_callbacks* pAllocationCallbacks) -{ - if (pAllocationCallbacks == NULL) { - return NULL; - } - if (pAllocationCallbacks->onMalloc != NULL) { - return pAllocationCallbacks->onMalloc(sz, pAllocationCallbacks->pUserData); - } - if (pAllocationCallbacks->onRealloc != NULL) { - return pAllocationCallbacks->onRealloc(NULL, sz, pAllocationCallbacks->pUserData); - } - return NULL; -} -static void* ma_dr_mp3__realloc_from_callbacks(void* p, size_t szNew, size_t szOld, const ma_allocation_callbacks* pAllocationCallbacks) -{ - if (pAllocationCallbacks == NULL) { - return NULL; - } - if (pAllocationCallbacks->onRealloc != NULL) { - return pAllocationCallbacks->onRealloc(p, szNew, pAllocationCallbacks->pUserData); - } - if (pAllocationCallbacks->onMalloc != NULL && pAllocationCallbacks->onFree != NULL) { - void* p2; - p2 = pAllocationCallbacks->onMalloc(szNew, pAllocationCallbacks->pUserData); - if (p2 == NULL) { - return NULL; - } - if (p != NULL) { - MA_DR_MP3_COPY_MEMORY(p2, p, szOld); - pAllocationCallbacks->onFree(p, pAllocationCallbacks->pUserData); - } - return p2; - } - return NULL; -} -static void ma_dr_mp3__free_from_callbacks(void* p, const ma_allocation_callbacks* pAllocationCallbacks) -{ - if (p == NULL || pAllocationCallbacks == NULL) { - return; - } - if (pAllocationCallbacks->onFree != NULL) { - pAllocationCallbacks->onFree(p, pAllocationCallbacks->pUserData); - } -} -static ma_allocation_callbacks ma_dr_mp3_copy_allocation_callbacks_or_defaults(const ma_allocation_callbacks* pAllocationCallbacks) -{ - if (pAllocationCallbacks != NULL) { - return *pAllocationCallbacks; - } else { - ma_allocation_callbacks allocationCallbacks; - allocationCallbacks.pUserData = NULL; - allocationCallbacks.onMalloc = ma_dr_mp3__malloc_default; - allocationCallbacks.onRealloc = ma_dr_mp3__realloc_default; - allocationCallbacks.onFree = ma_dr_mp3__free_default; - return allocationCallbacks; - } -} -static size_t ma_dr_mp3__on_read(ma_dr_mp3* pMP3, void* pBufferOut, size_t bytesToRead) -{ - size_t bytesRead = pMP3->onRead(pMP3->pUserData, pBufferOut, bytesToRead); - pMP3->streamCursor += bytesRead; - return bytesRead; -} -static ma_bool32 ma_dr_mp3__on_seek(ma_dr_mp3* pMP3, int offset, ma_dr_mp3_seek_origin origin) -{ - MA_DR_MP3_ASSERT(offset >= 0); - if (!pMP3->onSeek(pMP3->pUserData, offset, origin)) { - return MA_FALSE; - } - if (origin == ma_dr_mp3_seek_origin_start) { - pMP3->streamCursor = (ma_uint64)offset; - } else { - pMP3->streamCursor += offset; - } - return MA_TRUE; -} -static ma_bool32 ma_dr_mp3__on_seek_64(ma_dr_mp3* pMP3, ma_uint64 offset, ma_dr_mp3_seek_origin origin) -{ - if (offset <= 0x7FFFFFFF) { - return ma_dr_mp3__on_seek(pMP3, (int)offset, origin); - } - if (!ma_dr_mp3__on_seek(pMP3, 0x7FFFFFFF, ma_dr_mp3_seek_origin_start)) { - return MA_FALSE; - } - offset -= 0x7FFFFFFF; - while (offset > 0) { - if (offset <= 0x7FFFFFFF) { - if (!ma_dr_mp3__on_seek(pMP3, (int)offset, ma_dr_mp3_seek_origin_current)) { - return MA_FALSE; - } - offset = 0; - } else { - if (!ma_dr_mp3__on_seek(pMP3, 0x7FFFFFFF, ma_dr_mp3_seek_origin_current)) { - return MA_FALSE; - } - offset -= 0x7FFFFFFF; - } - } - return MA_TRUE; -} -static ma_uint32 ma_dr_mp3_decode_next_frame_ex__callbacks(ma_dr_mp3* pMP3, ma_dr_mp3d_sample_t* pPCMFrames) -{ - ma_uint32 pcmFramesRead = 0; - MA_DR_MP3_ASSERT(pMP3 != NULL); - MA_DR_MP3_ASSERT(pMP3->onRead != NULL); - if (pMP3->atEnd) { - return 0; - } - for (;;) { - ma_dr_mp3dec_frame_info info; - if (pMP3->dataSize < MA_DR_MP3_MIN_DATA_CHUNK_SIZE) { - size_t bytesRead; - if (pMP3->pData != NULL) { - MA_DR_MP3_MOVE_MEMORY(pMP3->pData, pMP3->pData + pMP3->dataConsumed, pMP3->dataSize); - } - pMP3->dataConsumed = 0; - if (pMP3->dataCapacity < MA_DR_MP3_DATA_CHUNK_SIZE) { - ma_uint8* pNewData; - size_t newDataCap; - newDataCap = MA_DR_MP3_DATA_CHUNK_SIZE; - pNewData = (ma_uint8*)ma_dr_mp3__realloc_from_callbacks(pMP3->pData, newDataCap, pMP3->dataCapacity, &pMP3->allocationCallbacks); - if (pNewData == NULL) { - return 0; - } - pMP3->pData = pNewData; - pMP3->dataCapacity = newDataCap; - } - bytesRead = ma_dr_mp3__on_read(pMP3, pMP3->pData + pMP3->dataSize, (pMP3->dataCapacity - pMP3->dataSize)); - if (bytesRead == 0) { - if (pMP3->dataSize == 0) { - pMP3->atEnd = MA_TRUE; - return 0; - } - } - pMP3->dataSize += bytesRead; - } - if (pMP3->dataSize > INT_MAX) { - pMP3->atEnd = MA_TRUE; - return 0; - } - MA_DR_MP3_ASSERT(pMP3->pData != NULL); - MA_DR_MP3_ASSERT(pMP3->dataCapacity > 0); - if (pMP3->pData == NULL) { - return 0; - } - pcmFramesRead = ma_dr_mp3dec_decode_frame(&pMP3->decoder, pMP3->pData + pMP3->dataConsumed, (int)pMP3->dataSize, pPCMFrames, &info); - if (info.frame_bytes > 0) { - pMP3->dataConsumed += (size_t)info.frame_bytes; - pMP3->dataSize -= (size_t)info.frame_bytes; - } - if (pcmFramesRead > 0) { - pcmFramesRead = ma_dr_mp3_hdr_frame_samples(pMP3->decoder.header); - pMP3->pcmFramesConsumedInMP3Frame = 0; - pMP3->pcmFramesRemainingInMP3Frame = pcmFramesRead; - pMP3->mp3FrameChannels = info.channels; - pMP3->mp3FrameSampleRate = info.hz; - break; - } else if (info.frame_bytes == 0) { - size_t bytesRead; - MA_DR_MP3_MOVE_MEMORY(pMP3->pData, pMP3->pData + pMP3->dataConsumed, pMP3->dataSize); - pMP3->dataConsumed = 0; - if (pMP3->dataCapacity == pMP3->dataSize) { - ma_uint8* pNewData; - size_t newDataCap; - newDataCap = pMP3->dataCapacity + MA_DR_MP3_DATA_CHUNK_SIZE; - pNewData = (ma_uint8*)ma_dr_mp3__realloc_from_callbacks(pMP3->pData, newDataCap, pMP3->dataCapacity, &pMP3->allocationCallbacks); - if (pNewData == NULL) { - return 0; - } - pMP3->pData = pNewData; - pMP3->dataCapacity = newDataCap; - } - bytesRead = ma_dr_mp3__on_read(pMP3, pMP3->pData + pMP3->dataSize, (pMP3->dataCapacity - pMP3->dataSize)); - if (bytesRead == 0) { - pMP3->atEnd = MA_TRUE; - return 0; - } - pMP3->dataSize += bytesRead; - } - }; - return pcmFramesRead; -} -static ma_uint32 ma_dr_mp3_decode_next_frame_ex__memory(ma_dr_mp3* pMP3, ma_dr_mp3d_sample_t* pPCMFrames) -{ - ma_uint32 pcmFramesRead = 0; - ma_dr_mp3dec_frame_info info; - MA_DR_MP3_ASSERT(pMP3 != NULL); - MA_DR_MP3_ASSERT(pMP3->memory.pData != NULL); - if (pMP3->atEnd) { - return 0; - } - for (;;) { - pcmFramesRead = ma_dr_mp3dec_decode_frame(&pMP3->decoder, pMP3->memory.pData + pMP3->memory.currentReadPos, (int)(pMP3->memory.dataSize - pMP3->memory.currentReadPos), pPCMFrames, &info); - if (pcmFramesRead > 0) { - pcmFramesRead = ma_dr_mp3_hdr_frame_samples(pMP3->decoder.header); - pMP3->pcmFramesConsumedInMP3Frame = 0; - pMP3->pcmFramesRemainingInMP3Frame = pcmFramesRead; - pMP3->mp3FrameChannels = info.channels; - pMP3->mp3FrameSampleRate = info.hz; - break; - } else if (info.frame_bytes > 0) { - pMP3->memory.currentReadPos += (size_t)info.frame_bytes; - } else { - break; - } - } - pMP3->memory.currentReadPos += (size_t)info.frame_bytes; - return pcmFramesRead; -} -static ma_uint32 ma_dr_mp3_decode_next_frame_ex(ma_dr_mp3* pMP3, ma_dr_mp3d_sample_t* pPCMFrames) -{ - if (pMP3->memory.pData != NULL && pMP3->memory.dataSize > 0) { - return ma_dr_mp3_decode_next_frame_ex__memory(pMP3, pPCMFrames); - } else { - return ma_dr_mp3_decode_next_frame_ex__callbacks(pMP3, pPCMFrames); - } -} -static ma_uint32 ma_dr_mp3_decode_next_frame(ma_dr_mp3* pMP3) -{ - MA_DR_MP3_ASSERT(pMP3 != NULL); - return ma_dr_mp3_decode_next_frame_ex(pMP3, (ma_dr_mp3d_sample_t*)pMP3->pcmFrames); -} -#if 0 -static ma_uint32 ma_dr_mp3_seek_next_frame(ma_dr_mp3* pMP3) -{ - ma_uint32 pcmFrameCount; - MA_DR_MP3_ASSERT(pMP3 != NULL); - pcmFrameCount = ma_dr_mp3_decode_next_frame_ex(pMP3, NULL); - if (pcmFrameCount == 0) { - return 0; - } - pMP3->currentPCMFrame += pcmFrameCount; - pMP3->pcmFramesConsumedInMP3Frame = pcmFrameCount; - pMP3->pcmFramesRemainingInMP3Frame = 0; - return pcmFrameCount; -} -#endif -static ma_bool32 ma_dr_mp3_init_internal(ma_dr_mp3* pMP3, ma_dr_mp3_read_proc onRead, ma_dr_mp3_seek_proc onSeek, void* pUserData, const ma_allocation_callbacks* pAllocationCallbacks) -{ - MA_DR_MP3_ASSERT(pMP3 != NULL); - MA_DR_MP3_ASSERT(onRead != NULL); - ma_dr_mp3dec_init(&pMP3->decoder); - pMP3->onRead = onRead; - pMP3->onSeek = onSeek; - pMP3->pUserData = pUserData; - pMP3->allocationCallbacks = ma_dr_mp3_copy_allocation_callbacks_or_defaults(pAllocationCallbacks); - if (pMP3->allocationCallbacks.onFree == NULL || (pMP3->allocationCallbacks.onMalloc == NULL && pMP3->allocationCallbacks.onRealloc == NULL)) { - return MA_FALSE; - } - if (ma_dr_mp3_decode_next_frame(pMP3) == 0) { - ma_dr_mp3__free_from_callbacks(pMP3->pData, &pMP3->allocationCallbacks); - return MA_FALSE; - } - pMP3->channels = pMP3->mp3FrameChannels; - pMP3->sampleRate = pMP3->mp3FrameSampleRate; - return MA_TRUE; -} -MA_API ma_bool32 ma_dr_mp3_init(ma_dr_mp3* pMP3, ma_dr_mp3_read_proc onRead, ma_dr_mp3_seek_proc onSeek, void* pUserData, const ma_allocation_callbacks* pAllocationCallbacks) -{ - if (pMP3 == NULL || onRead == NULL) { - return MA_FALSE; - } - MA_DR_MP3_ZERO_OBJECT(pMP3); - return ma_dr_mp3_init_internal(pMP3, onRead, onSeek, pUserData, pAllocationCallbacks); -} -static size_t ma_dr_mp3__on_read_memory(void* pUserData, void* pBufferOut, size_t bytesToRead) -{ - ma_dr_mp3* pMP3 = (ma_dr_mp3*)pUserData; - size_t bytesRemaining; - MA_DR_MP3_ASSERT(pMP3 != NULL); - MA_DR_MP3_ASSERT(pMP3->memory.dataSize >= pMP3->memory.currentReadPos); - bytesRemaining = pMP3->memory.dataSize - pMP3->memory.currentReadPos; - if (bytesToRead > bytesRemaining) { - bytesToRead = bytesRemaining; - } - if (bytesToRead > 0) { - MA_DR_MP3_COPY_MEMORY(pBufferOut, pMP3->memory.pData + pMP3->memory.currentReadPos, bytesToRead); - pMP3->memory.currentReadPos += bytesToRead; - } - return bytesToRead; -} -static ma_bool32 ma_dr_mp3__on_seek_memory(void* pUserData, int byteOffset, ma_dr_mp3_seek_origin origin) -{ - ma_dr_mp3* pMP3 = (ma_dr_mp3*)pUserData; - MA_DR_MP3_ASSERT(pMP3 != NULL); - if (origin == ma_dr_mp3_seek_origin_current) { - if (byteOffset > 0) { - if (pMP3->memory.currentReadPos + byteOffset > pMP3->memory.dataSize) { - byteOffset = (int)(pMP3->memory.dataSize - pMP3->memory.currentReadPos); - } - } else { - if (pMP3->memory.currentReadPos < (size_t)-byteOffset) { - byteOffset = -(int)pMP3->memory.currentReadPos; - } - } - pMP3->memory.currentReadPos += byteOffset; - } else { - if ((ma_uint32)byteOffset <= pMP3->memory.dataSize) { - pMP3->memory.currentReadPos = byteOffset; - } else { - pMP3->memory.currentReadPos = pMP3->memory.dataSize; - } - } - return MA_TRUE; -} -MA_API ma_bool32 ma_dr_mp3_init_memory(ma_dr_mp3* pMP3, const void* pData, size_t dataSize, const ma_allocation_callbacks* pAllocationCallbacks) -{ - if (pMP3 == NULL) { - return MA_FALSE; - } - MA_DR_MP3_ZERO_OBJECT(pMP3); - if (pData == NULL || dataSize == 0) { - return MA_FALSE; - } - pMP3->memory.pData = (const ma_uint8*)pData; - pMP3->memory.dataSize = dataSize; - pMP3->memory.currentReadPos = 0; - return ma_dr_mp3_init_internal(pMP3, ma_dr_mp3__on_read_memory, ma_dr_mp3__on_seek_memory, pMP3, pAllocationCallbacks); -} -#ifndef MA_DR_MP3_NO_STDIO -#include -#include -static size_t ma_dr_mp3__on_read_stdio(void* pUserData, void* pBufferOut, size_t bytesToRead) -{ - return fread(pBufferOut, 1, bytesToRead, (FILE*)pUserData); -} -static ma_bool32 ma_dr_mp3__on_seek_stdio(void* pUserData, int offset, ma_dr_mp3_seek_origin origin) -{ - return fseek((FILE*)pUserData, offset, (origin == ma_dr_mp3_seek_origin_current) ? SEEK_CUR : SEEK_SET) == 0; -} -MA_API ma_bool32 ma_dr_mp3_init_file(ma_dr_mp3* pMP3, const char* pFilePath, const ma_allocation_callbacks* pAllocationCallbacks) -{ - ma_bool32 result; - FILE* pFile; - if (ma_fopen(&pFile, pFilePath, "rb") != MA_SUCCESS) { - return MA_FALSE; - } - result = ma_dr_mp3_init(pMP3, ma_dr_mp3__on_read_stdio, ma_dr_mp3__on_seek_stdio, (void*)pFile, pAllocationCallbacks); - if (result != MA_TRUE) { - fclose(pFile); - return result; - } - return MA_TRUE; -} -MA_API ma_bool32 ma_dr_mp3_init_file_w(ma_dr_mp3* pMP3, const wchar_t* pFilePath, const ma_allocation_callbacks* pAllocationCallbacks) -{ - ma_bool32 result; - FILE* pFile; - if (ma_wfopen(&pFile, pFilePath, L"rb", pAllocationCallbacks) != MA_SUCCESS) { - return MA_FALSE; - } - result = ma_dr_mp3_init(pMP3, ma_dr_mp3__on_read_stdio, ma_dr_mp3__on_seek_stdio, (void*)pFile, pAllocationCallbacks); - if (result != MA_TRUE) { - fclose(pFile); - return result; - } - return MA_TRUE; -} -#endif -MA_API void ma_dr_mp3_uninit(ma_dr_mp3* pMP3) -{ - if (pMP3 == NULL) { - return; - } -#ifndef MA_DR_MP3_NO_STDIO - if (pMP3->onRead == ma_dr_mp3__on_read_stdio) { - FILE* pFile = (FILE*)pMP3->pUserData; - if (pFile != NULL) { - fclose(pFile); - pMP3->pUserData = NULL; - } - } -#endif - ma_dr_mp3__free_from_callbacks(pMP3->pData, &pMP3->allocationCallbacks); -} -#if defined(MA_DR_MP3_FLOAT_OUTPUT) -static void ma_dr_mp3_f32_to_s16(ma_int16* dst, const float* src, ma_uint64 sampleCount) -{ - ma_uint64 i; - ma_uint64 i4; - ma_uint64 sampleCount4; - i = 0; - sampleCount4 = sampleCount >> 2; - for (i4 = 0; i4 < sampleCount4; i4 += 1) { - float x0 = src[i+0]; - float x1 = src[i+1]; - float x2 = src[i+2]; - float x3 = src[i+3]; - x0 = ((x0 < -1) ? -1 : ((x0 > 1) ? 1 : x0)); - x1 = ((x1 < -1) ? -1 : ((x1 > 1) ? 1 : x1)); - x2 = ((x2 < -1) ? -1 : ((x2 > 1) ? 1 : x2)); - x3 = ((x3 < -1) ? -1 : ((x3 > 1) ? 1 : x3)); - x0 = x0 * 32767.0f; - x1 = x1 * 32767.0f; - x2 = x2 * 32767.0f; - x3 = x3 * 32767.0f; - dst[i+0] = (ma_int16)x0; - dst[i+1] = (ma_int16)x1; - dst[i+2] = (ma_int16)x2; - dst[i+3] = (ma_int16)x3; - i += 4; - } - for (; i < sampleCount; i += 1) { - float x = src[i]; - x = ((x < -1) ? -1 : ((x > 1) ? 1 : x)); - x = x * 32767.0f; - dst[i] = (ma_int16)x; - } -} -#endif -#if !defined(MA_DR_MP3_FLOAT_OUTPUT) -static void ma_dr_mp3_s16_to_f32(float* dst, const ma_int16* src, ma_uint64 sampleCount) -{ - ma_uint64 i; - for (i = 0; i < sampleCount; i += 1) { - float x = (float)src[i]; - x = x * 0.000030517578125f; - dst[i] = x; - } -} -#endif -static ma_uint64 ma_dr_mp3_read_pcm_frames_raw(ma_dr_mp3* pMP3, ma_uint64 framesToRead, void* pBufferOut) -{ - ma_uint64 totalFramesRead = 0; - MA_DR_MP3_ASSERT(pMP3 != NULL); - MA_DR_MP3_ASSERT(pMP3->onRead != NULL); - while (framesToRead > 0) { - ma_uint32 framesToConsume = (ma_uint32)MA_DR_MP3_MIN(pMP3->pcmFramesRemainingInMP3Frame, framesToRead); - if (pBufferOut != NULL) { - #if defined(MA_DR_MP3_FLOAT_OUTPUT) - float* pFramesOutF32 = (float*)MA_DR_MP3_OFFSET_PTR(pBufferOut, sizeof(float) * totalFramesRead * pMP3->channels); - float* pFramesInF32 = (float*)MA_DR_MP3_OFFSET_PTR(&pMP3->pcmFrames[0], sizeof(float) * pMP3->pcmFramesConsumedInMP3Frame * pMP3->mp3FrameChannels); - MA_DR_MP3_COPY_MEMORY(pFramesOutF32, pFramesInF32, sizeof(float) * framesToConsume * pMP3->channels); - #else - ma_int16* pFramesOutS16 = (ma_int16*)MA_DR_MP3_OFFSET_PTR(pBufferOut, sizeof(ma_int16) * totalFramesRead * pMP3->channels); - ma_int16* pFramesInS16 = (ma_int16*)MA_DR_MP3_OFFSET_PTR(&pMP3->pcmFrames[0], sizeof(ma_int16) * pMP3->pcmFramesConsumedInMP3Frame * pMP3->mp3FrameChannels); - MA_DR_MP3_COPY_MEMORY(pFramesOutS16, pFramesInS16, sizeof(ma_int16) * framesToConsume * pMP3->channels); - #endif - } - pMP3->currentPCMFrame += framesToConsume; - pMP3->pcmFramesConsumedInMP3Frame += framesToConsume; - pMP3->pcmFramesRemainingInMP3Frame -= framesToConsume; - totalFramesRead += framesToConsume; - framesToRead -= framesToConsume; - if (framesToRead == 0) { - break; - } - MA_DR_MP3_ASSERT(pMP3->pcmFramesRemainingInMP3Frame == 0); - if (ma_dr_mp3_decode_next_frame(pMP3) == 0) { - break; - } - } - return totalFramesRead; -} -MA_API ma_uint64 ma_dr_mp3_read_pcm_frames_f32(ma_dr_mp3* pMP3, ma_uint64 framesToRead, float* pBufferOut) -{ - if (pMP3 == NULL || pMP3->onRead == NULL) { - return 0; - } -#if defined(MA_DR_MP3_FLOAT_OUTPUT) - return ma_dr_mp3_read_pcm_frames_raw(pMP3, framesToRead, pBufferOut); -#else - { - ma_int16 pTempS16[8192]; - ma_uint64 totalPCMFramesRead = 0; - while (totalPCMFramesRead < framesToRead) { - ma_uint64 framesJustRead; - ma_uint64 framesRemaining = framesToRead - totalPCMFramesRead; - ma_uint64 framesToReadNow = MA_DR_MP3_COUNTOF(pTempS16) / pMP3->channels; - if (framesToReadNow > framesRemaining) { - framesToReadNow = framesRemaining; - } - framesJustRead = ma_dr_mp3_read_pcm_frames_raw(pMP3, framesToReadNow, pTempS16); - if (framesJustRead == 0) { - break; - } - ma_dr_mp3_s16_to_f32((float*)MA_DR_MP3_OFFSET_PTR(pBufferOut, sizeof(float) * totalPCMFramesRead * pMP3->channels), pTempS16, framesJustRead * pMP3->channels); - totalPCMFramesRead += framesJustRead; - } - return totalPCMFramesRead; - } -#endif -} -MA_API ma_uint64 ma_dr_mp3_read_pcm_frames_s16(ma_dr_mp3* pMP3, ma_uint64 framesToRead, ma_int16* pBufferOut) -{ - if (pMP3 == NULL || pMP3->onRead == NULL) { - return 0; - } -#if !defined(MA_DR_MP3_FLOAT_OUTPUT) - return ma_dr_mp3_read_pcm_frames_raw(pMP3, framesToRead, pBufferOut); -#else - { - float pTempF32[4096]; - ma_uint64 totalPCMFramesRead = 0; - while (totalPCMFramesRead < framesToRead) { - ma_uint64 framesJustRead; - ma_uint64 framesRemaining = framesToRead - totalPCMFramesRead; - ma_uint64 framesToReadNow = MA_DR_MP3_COUNTOF(pTempF32) / pMP3->channels; - if (framesToReadNow > framesRemaining) { - framesToReadNow = framesRemaining; - } - framesJustRead = ma_dr_mp3_read_pcm_frames_raw(pMP3, framesToReadNow, pTempF32); - if (framesJustRead == 0) { - break; - } - ma_dr_mp3_f32_to_s16((ma_int16*)MA_DR_MP3_OFFSET_PTR(pBufferOut, sizeof(ma_int16) * totalPCMFramesRead * pMP3->channels), pTempF32, framesJustRead * pMP3->channels); - totalPCMFramesRead += framesJustRead; - } - return totalPCMFramesRead; - } -#endif -} -static void ma_dr_mp3_reset(ma_dr_mp3* pMP3) -{ - MA_DR_MP3_ASSERT(pMP3 != NULL); - pMP3->pcmFramesConsumedInMP3Frame = 0; - pMP3->pcmFramesRemainingInMP3Frame = 0; - pMP3->currentPCMFrame = 0; - pMP3->dataSize = 0; - pMP3->atEnd = MA_FALSE; - ma_dr_mp3dec_init(&pMP3->decoder); -} -static ma_bool32 ma_dr_mp3_seek_to_start_of_stream(ma_dr_mp3* pMP3) -{ - MA_DR_MP3_ASSERT(pMP3 != NULL); - MA_DR_MP3_ASSERT(pMP3->onSeek != NULL); - if (!ma_dr_mp3__on_seek(pMP3, 0, ma_dr_mp3_seek_origin_start)) { - return MA_FALSE; - } - ma_dr_mp3_reset(pMP3); - return MA_TRUE; -} -static ma_bool32 ma_dr_mp3_seek_forward_by_pcm_frames__brute_force(ma_dr_mp3* pMP3, ma_uint64 frameOffset) -{ - ma_uint64 framesRead; -#if defined(MA_DR_MP3_FLOAT_OUTPUT) - framesRead = ma_dr_mp3_read_pcm_frames_f32(pMP3, frameOffset, NULL); -#else - framesRead = ma_dr_mp3_read_pcm_frames_s16(pMP3, frameOffset, NULL); -#endif - if (framesRead != frameOffset) { - return MA_FALSE; - } - return MA_TRUE; -} -static ma_bool32 ma_dr_mp3_seek_to_pcm_frame__brute_force(ma_dr_mp3* pMP3, ma_uint64 frameIndex) -{ - MA_DR_MP3_ASSERT(pMP3 != NULL); - if (frameIndex == pMP3->currentPCMFrame) { - return MA_TRUE; - } - if (frameIndex < pMP3->currentPCMFrame) { - if (!ma_dr_mp3_seek_to_start_of_stream(pMP3)) { - return MA_FALSE; - } - } - MA_DR_MP3_ASSERT(frameIndex >= pMP3->currentPCMFrame); - return ma_dr_mp3_seek_forward_by_pcm_frames__brute_force(pMP3, (frameIndex - pMP3->currentPCMFrame)); -} -static ma_bool32 ma_dr_mp3_find_closest_seek_point(ma_dr_mp3* pMP3, ma_uint64 frameIndex, ma_uint32* pSeekPointIndex) -{ - ma_uint32 iSeekPoint; - MA_DR_MP3_ASSERT(pSeekPointIndex != NULL); - *pSeekPointIndex = 0; - if (frameIndex < pMP3->pSeekPoints[0].pcmFrameIndex) { - return MA_FALSE; - } - for (iSeekPoint = 0; iSeekPoint < pMP3->seekPointCount; ++iSeekPoint) { - if (pMP3->pSeekPoints[iSeekPoint].pcmFrameIndex > frameIndex) { - break; - } - *pSeekPointIndex = iSeekPoint; - } - return MA_TRUE; -} -static ma_bool32 ma_dr_mp3_seek_to_pcm_frame__seek_table(ma_dr_mp3* pMP3, ma_uint64 frameIndex) -{ - ma_dr_mp3_seek_point seekPoint; - ma_uint32 priorSeekPointIndex; - ma_uint16 iMP3Frame; - ma_uint64 leftoverFrames; - MA_DR_MP3_ASSERT(pMP3 != NULL); - MA_DR_MP3_ASSERT(pMP3->pSeekPoints != NULL); - MA_DR_MP3_ASSERT(pMP3->seekPointCount > 0); - if (ma_dr_mp3_find_closest_seek_point(pMP3, frameIndex, &priorSeekPointIndex)) { - seekPoint = pMP3->pSeekPoints[priorSeekPointIndex]; - } else { - seekPoint.seekPosInBytes = 0; - seekPoint.pcmFrameIndex = 0; - seekPoint.mp3FramesToDiscard = 0; - seekPoint.pcmFramesToDiscard = 0; - } - if (!ma_dr_mp3__on_seek_64(pMP3, seekPoint.seekPosInBytes, ma_dr_mp3_seek_origin_start)) { - return MA_FALSE; - } - ma_dr_mp3_reset(pMP3); - for (iMP3Frame = 0; iMP3Frame < seekPoint.mp3FramesToDiscard; ++iMP3Frame) { - ma_uint32 pcmFramesRead; - ma_dr_mp3d_sample_t* pPCMFrames; - pPCMFrames = NULL; - if (iMP3Frame == seekPoint.mp3FramesToDiscard-1) { - pPCMFrames = (ma_dr_mp3d_sample_t*)pMP3->pcmFrames; - } - pcmFramesRead = ma_dr_mp3_decode_next_frame_ex(pMP3, pPCMFrames); - if (pcmFramesRead == 0) { - return MA_FALSE; - } - } - pMP3->currentPCMFrame = seekPoint.pcmFrameIndex - seekPoint.pcmFramesToDiscard; - leftoverFrames = frameIndex - pMP3->currentPCMFrame; - return ma_dr_mp3_seek_forward_by_pcm_frames__brute_force(pMP3, leftoverFrames); -} -MA_API ma_bool32 ma_dr_mp3_seek_to_pcm_frame(ma_dr_mp3* pMP3, ma_uint64 frameIndex) -{ - if (pMP3 == NULL || pMP3->onSeek == NULL) { - return MA_FALSE; - } - if (frameIndex == 0) { - return ma_dr_mp3_seek_to_start_of_stream(pMP3); - } - if (pMP3->pSeekPoints != NULL && pMP3->seekPointCount > 0) { - return ma_dr_mp3_seek_to_pcm_frame__seek_table(pMP3, frameIndex); - } else { - return ma_dr_mp3_seek_to_pcm_frame__brute_force(pMP3, frameIndex); - } -} -MA_API ma_bool32 ma_dr_mp3_get_mp3_and_pcm_frame_count(ma_dr_mp3* pMP3, ma_uint64* pMP3FrameCount, ma_uint64* pPCMFrameCount) -{ - ma_uint64 currentPCMFrame; - ma_uint64 totalPCMFrameCount; - ma_uint64 totalMP3FrameCount; - if (pMP3 == NULL) { - return MA_FALSE; - } - if (pMP3->onSeek == NULL) { - return MA_FALSE; - } - currentPCMFrame = pMP3->currentPCMFrame; - if (!ma_dr_mp3_seek_to_start_of_stream(pMP3)) { - return MA_FALSE; - } - totalPCMFrameCount = 0; - totalMP3FrameCount = 0; - for (;;) { - ma_uint32 pcmFramesInCurrentMP3Frame; - pcmFramesInCurrentMP3Frame = ma_dr_mp3_decode_next_frame_ex(pMP3, NULL); - if (pcmFramesInCurrentMP3Frame == 0) { - break; - } - totalPCMFrameCount += pcmFramesInCurrentMP3Frame; - totalMP3FrameCount += 1; - } - if (!ma_dr_mp3_seek_to_start_of_stream(pMP3)) { - return MA_FALSE; - } - if (!ma_dr_mp3_seek_to_pcm_frame(pMP3, currentPCMFrame)) { - return MA_FALSE; - } - if (pMP3FrameCount != NULL) { - *pMP3FrameCount = totalMP3FrameCount; - } - if (pPCMFrameCount != NULL) { - *pPCMFrameCount = totalPCMFrameCount; - } - return MA_TRUE; -} -MA_API ma_uint64 ma_dr_mp3_get_pcm_frame_count(ma_dr_mp3* pMP3) -{ - ma_uint64 totalPCMFrameCount; - if (!ma_dr_mp3_get_mp3_and_pcm_frame_count(pMP3, NULL, &totalPCMFrameCount)) { - return 0; - } - return totalPCMFrameCount; -} -MA_API ma_uint64 ma_dr_mp3_get_mp3_frame_count(ma_dr_mp3* pMP3) -{ - ma_uint64 totalMP3FrameCount; - if (!ma_dr_mp3_get_mp3_and_pcm_frame_count(pMP3, &totalMP3FrameCount, NULL)) { - return 0; - } - return totalMP3FrameCount; -} -static void ma_dr_mp3__accumulate_running_pcm_frame_count(ma_dr_mp3* pMP3, ma_uint32 pcmFrameCountIn, ma_uint64* pRunningPCMFrameCount, float* pRunningPCMFrameCountFractionalPart) -{ - float srcRatio; - float pcmFrameCountOutF; - ma_uint32 pcmFrameCountOut; - srcRatio = (float)pMP3->mp3FrameSampleRate / (float)pMP3->sampleRate; - MA_DR_MP3_ASSERT(srcRatio > 0); - pcmFrameCountOutF = *pRunningPCMFrameCountFractionalPart + (pcmFrameCountIn / srcRatio); - pcmFrameCountOut = (ma_uint32)pcmFrameCountOutF; - *pRunningPCMFrameCountFractionalPart = pcmFrameCountOutF - pcmFrameCountOut; - *pRunningPCMFrameCount += pcmFrameCountOut; -} -typedef struct -{ - ma_uint64 bytePos; - ma_uint64 pcmFrameIndex; -} ma_dr_mp3__seeking_mp3_frame_info; -MA_API ma_bool32 ma_dr_mp3_calculate_seek_points(ma_dr_mp3* pMP3, ma_uint32* pSeekPointCount, ma_dr_mp3_seek_point* pSeekPoints) -{ - ma_uint32 seekPointCount; - ma_uint64 currentPCMFrame; - ma_uint64 totalMP3FrameCount; - ma_uint64 totalPCMFrameCount; - if (pMP3 == NULL || pSeekPointCount == NULL || pSeekPoints == NULL) { - return MA_FALSE; - } - seekPointCount = *pSeekPointCount; - if (seekPointCount == 0) { - return MA_FALSE; - } - currentPCMFrame = pMP3->currentPCMFrame; - if (!ma_dr_mp3_get_mp3_and_pcm_frame_count(pMP3, &totalMP3FrameCount, &totalPCMFrameCount)) { - return MA_FALSE; - } - if (totalMP3FrameCount < MA_DR_MP3_SEEK_LEADING_MP3_FRAMES+1) { - seekPointCount = 1; - pSeekPoints[0].seekPosInBytes = 0; - pSeekPoints[0].pcmFrameIndex = 0; - pSeekPoints[0].mp3FramesToDiscard = 0; - pSeekPoints[0].pcmFramesToDiscard = 0; - } else { - ma_uint64 pcmFramesBetweenSeekPoints; - ma_dr_mp3__seeking_mp3_frame_info mp3FrameInfo[MA_DR_MP3_SEEK_LEADING_MP3_FRAMES+1]; - ma_uint64 runningPCMFrameCount = 0; - float runningPCMFrameCountFractionalPart = 0; - ma_uint64 nextTargetPCMFrame; - ma_uint32 iMP3Frame; - ma_uint32 iSeekPoint; - if (seekPointCount > totalMP3FrameCount-1) { - seekPointCount = (ma_uint32)totalMP3FrameCount-1; - } - pcmFramesBetweenSeekPoints = totalPCMFrameCount / (seekPointCount+1); - if (!ma_dr_mp3_seek_to_start_of_stream(pMP3)) { - return MA_FALSE; - } - for (iMP3Frame = 0; iMP3Frame < MA_DR_MP3_SEEK_LEADING_MP3_FRAMES+1; ++iMP3Frame) { - ma_uint32 pcmFramesInCurrentMP3FrameIn; - MA_DR_MP3_ASSERT(pMP3->streamCursor >= pMP3->dataSize); - mp3FrameInfo[iMP3Frame].bytePos = pMP3->streamCursor - pMP3->dataSize; - mp3FrameInfo[iMP3Frame].pcmFrameIndex = runningPCMFrameCount; - pcmFramesInCurrentMP3FrameIn = ma_dr_mp3_decode_next_frame_ex(pMP3, NULL); - if (pcmFramesInCurrentMP3FrameIn == 0) { - return MA_FALSE; - } - ma_dr_mp3__accumulate_running_pcm_frame_count(pMP3, pcmFramesInCurrentMP3FrameIn, &runningPCMFrameCount, &runningPCMFrameCountFractionalPart); - } - nextTargetPCMFrame = 0; - for (iSeekPoint = 0; iSeekPoint < seekPointCount; ++iSeekPoint) { - nextTargetPCMFrame += pcmFramesBetweenSeekPoints; - for (;;) { - if (nextTargetPCMFrame < runningPCMFrameCount) { - pSeekPoints[iSeekPoint].seekPosInBytes = mp3FrameInfo[0].bytePos; - pSeekPoints[iSeekPoint].pcmFrameIndex = nextTargetPCMFrame; - pSeekPoints[iSeekPoint].mp3FramesToDiscard = MA_DR_MP3_SEEK_LEADING_MP3_FRAMES; - pSeekPoints[iSeekPoint].pcmFramesToDiscard = (ma_uint16)(nextTargetPCMFrame - mp3FrameInfo[MA_DR_MP3_SEEK_LEADING_MP3_FRAMES-1].pcmFrameIndex); - break; - } else { - size_t i; - ma_uint32 pcmFramesInCurrentMP3FrameIn; - for (i = 0; i < MA_DR_MP3_COUNTOF(mp3FrameInfo)-1; ++i) { - mp3FrameInfo[i] = mp3FrameInfo[i+1]; - } - mp3FrameInfo[MA_DR_MP3_COUNTOF(mp3FrameInfo)-1].bytePos = pMP3->streamCursor - pMP3->dataSize; - mp3FrameInfo[MA_DR_MP3_COUNTOF(mp3FrameInfo)-1].pcmFrameIndex = runningPCMFrameCount; - pcmFramesInCurrentMP3FrameIn = ma_dr_mp3_decode_next_frame_ex(pMP3, NULL); - if (pcmFramesInCurrentMP3FrameIn == 0) { - pSeekPoints[iSeekPoint].seekPosInBytes = mp3FrameInfo[0].bytePos; - pSeekPoints[iSeekPoint].pcmFrameIndex = nextTargetPCMFrame; - pSeekPoints[iSeekPoint].mp3FramesToDiscard = MA_DR_MP3_SEEK_LEADING_MP3_FRAMES; - pSeekPoints[iSeekPoint].pcmFramesToDiscard = (ma_uint16)(nextTargetPCMFrame - mp3FrameInfo[MA_DR_MP3_SEEK_LEADING_MP3_FRAMES-1].pcmFrameIndex); - break; - } - ma_dr_mp3__accumulate_running_pcm_frame_count(pMP3, pcmFramesInCurrentMP3FrameIn, &runningPCMFrameCount, &runningPCMFrameCountFractionalPart); - } - } - } - if (!ma_dr_mp3_seek_to_start_of_stream(pMP3)) { - return MA_FALSE; - } - if (!ma_dr_mp3_seek_to_pcm_frame(pMP3, currentPCMFrame)) { - return MA_FALSE; - } - } - *pSeekPointCount = seekPointCount; - return MA_TRUE; -} -MA_API ma_bool32 ma_dr_mp3_bind_seek_table(ma_dr_mp3* pMP3, ma_uint32 seekPointCount, ma_dr_mp3_seek_point* pSeekPoints) -{ - if (pMP3 == NULL) { - return MA_FALSE; - } - if (seekPointCount == 0 || pSeekPoints == NULL) { - pMP3->seekPointCount = 0; - pMP3->pSeekPoints = NULL; - } else { - pMP3->seekPointCount = seekPointCount; - pMP3->pSeekPoints = pSeekPoints; - } - return MA_TRUE; -} -static float* ma_dr_mp3__full_read_and_close_f32(ma_dr_mp3* pMP3, ma_dr_mp3_config* pConfig, ma_uint64* pTotalFrameCount) -{ - ma_uint64 totalFramesRead = 0; - ma_uint64 framesCapacity = 0; - float* pFrames = NULL; - float temp[4096]; - MA_DR_MP3_ASSERT(pMP3 != NULL); - for (;;) { - ma_uint64 framesToReadRightNow = MA_DR_MP3_COUNTOF(temp) / pMP3->channels; - ma_uint64 framesJustRead = ma_dr_mp3_read_pcm_frames_f32(pMP3, framesToReadRightNow, temp); - if (framesJustRead == 0) { - break; - } - if (framesCapacity < totalFramesRead + framesJustRead) { - ma_uint64 oldFramesBufferSize; - ma_uint64 newFramesBufferSize; - ma_uint64 newFramesCap; - float* pNewFrames; - newFramesCap = framesCapacity * 2; - if (newFramesCap < totalFramesRead + framesJustRead) { - newFramesCap = totalFramesRead + framesJustRead; - } - oldFramesBufferSize = framesCapacity * pMP3->channels * sizeof(float); - newFramesBufferSize = newFramesCap * pMP3->channels * sizeof(float); - if (newFramesBufferSize > (ma_uint64)MA_SIZE_MAX) { - break; - } - pNewFrames = (float*)ma_dr_mp3__realloc_from_callbacks(pFrames, (size_t)newFramesBufferSize, (size_t)oldFramesBufferSize, &pMP3->allocationCallbacks); - if (pNewFrames == NULL) { - ma_dr_mp3__free_from_callbacks(pFrames, &pMP3->allocationCallbacks); - break; - } - pFrames = pNewFrames; - framesCapacity = newFramesCap; - } - MA_DR_MP3_COPY_MEMORY(pFrames + totalFramesRead*pMP3->channels, temp, (size_t)(framesJustRead*pMP3->channels*sizeof(float))); - totalFramesRead += framesJustRead; - if (framesJustRead != framesToReadRightNow) { - break; - } - } - if (pConfig != NULL) { - pConfig->channels = pMP3->channels; - pConfig->sampleRate = pMP3->sampleRate; - } - ma_dr_mp3_uninit(pMP3); - if (pTotalFrameCount) { - *pTotalFrameCount = totalFramesRead; - } - return pFrames; -} -static ma_int16* ma_dr_mp3__full_read_and_close_s16(ma_dr_mp3* pMP3, ma_dr_mp3_config* pConfig, ma_uint64* pTotalFrameCount) -{ - ma_uint64 totalFramesRead = 0; - ma_uint64 framesCapacity = 0; - ma_int16* pFrames = NULL; - ma_int16 temp[4096]; - MA_DR_MP3_ASSERT(pMP3 != NULL); - for (;;) { - ma_uint64 framesToReadRightNow = MA_DR_MP3_COUNTOF(temp) / pMP3->channels; - ma_uint64 framesJustRead = ma_dr_mp3_read_pcm_frames_s16(pMP3, framesToReadRightNow, temp); - if (framesJustRead == 0) { - break; - } - if (framesCapacity < totalFramesRead + framesJustRead) { - ma_uint64 newFramesBufferSize; - ma_uint64 oldFramesBufferSize; - ma_uint64 newFramesCap; - ma_int16* pNewFrames; - newFramesCap = framesCapacity * 2; - if (newFramesCap < totalFramesRead + framesJustRead) { - newFramesCap = totalFramesRead + framesJustRead; - } - oldFramesBufferSize = framesCapacity * pMP3->channels * sizeof(ma_int16); - newFramesBufferSize = newFramesCap * pMP3->channels * sizeof(ma_int16); - if (newFramesBufferSize > (ma_uint64)MA_SIZE_MAX) { - break; - } - pNewFrames = (ma_int16*)ma_dr_mp3__realloc_from_callbacks(pFrames, (size_t)newFramesBufferSize, (size_t)oldFramesBufferSize, &pMP3->allocationCallbacks); - if (pNewFrames == NULL) { - ma_dr_mp3__free_from_callbacks(pFrames, &pMP3->allocationCallbacks); - break; - } - pFrames = pNewFrames; - framesCapacity = newFramesCap; - } - MA_DR_MP3_COPY_MEMORY(pFrames + totalFramesRead*pMP3->channels, temp, (size_t)(framesJustRead*pMP3->channels*sizeof(ma_int16))); - totalFramesRead += framesJustRead; - if (framesJustRead != framesToReadRightNow) { - break; - } - } - if (pConfig != NULL) { - pConfig->channels = pMP3->channels; - pConfig->sampleRate = pMP3->sampleRate; - } - ma_dr_mp3_uninit(pMP3); - if (pTotalFrameCount) { - *pTotalFrameCount = totalFramesRead; - } - return pFrames; -} -MA_API float* ma_dr_mp3_open_and_read_pcm_frames_f32(ma_dr_mp3_read_proc onRead, ma_dr_mp3_seek_proc onSeek, void* pUserData, ma_dr_mp3_config* pConfig, ma_uint64* pTotalFrameCount, const ma_allocation_callbacks* pAllocationCallbacks) -{ - ma_dr_mp3 mp3; - if (!ma_dr_mp3_init(&mp3, onRead, onSeek, pUserData, pAllocationCallbacks)) { - return NULL; - } - return ma_dr_mp3__full_read_and_close_f32(&mp3, pConfig, pTotalFrameCount); -} -MA_API ma_int16* ma_dr_mp3_open_and_read_pcm_frames_s16(ma_dr_mp3_read_proc onRead, ma_dr_mp3_seek_proc onSeek, void* pUserData, ma_dr_mp3_config* pConfig, ma_uint64* pTotalFrameCount, const ma_allocation_callbacks* pAllocationCallbacks) -{ - ma_dr_mp3 mp3; - if (!ma_dr_mp3_init(&mp3, onRead, onSeek, pUserData, pAllocationCallbacks)) { - return NULL; - } - return ma_dr_mp3__full_read_and_close_s16(&mp3, pConfig, pTotalFrameCount); -} -MA_API float* ma_dr_mp3_open_memory_and_read_pcm_frames_f32(const void* pData, size_t dataSize, ma_dr_mp3_config* pConfig, ma_uint64* pTotalFrameCount, const ma_allocation_callbacks* pAllocationCallbacks) -{ - ma_dr_mp3 mp3; - if (!ma_dr_mp3_init_memory(&mp3, pData, dataSize, pAllocationCallbacks)) { - return NULL; - } - return ma_dr_mp3__full_read_and_close_f32(&mp3, pConfig, pTotalFrameCount); -} -MA_API ma_int16* ma_dr_mp3_open_memory_and_read_pcm_frames_s16(const void* pData, size_t dataSize, ma_dr_mp3_config* pConfig, ma_uint64* pTotalFrameCount, const ma_allocation_callbacks* pAllocationCallbacks) -{ - ma_dr_mp3 mp3; - if (!ma_dr_mp3_init_memory(&mp3, pData, dataSize, pAllocationCallbacks)) { - return NULL; - } - return ma_dr_mp3__full_read_and_close_s16(&mp3, pConfig, pTotalFrameCount); -} -#ifndef MA_DR_MP3_NO_STDIO -MA_API float* ma_dr_mp3_open_file_and_read_pcm_frames_f32(const char* filePath, ma_dr_mp3_config* pConfig, ma_uint64* pTotalFrameCount, const ma_allocation_callbacks* pAllocationCallbacks) -{ - ma_dr_mp3 mp3; - if (!ma_dr_mp3_init_file(&mp3, filePath, pAllocationCallbacks)) { - return NULL; - } - return ma_dr_mp3__full_read_and_close_f32(&mp3, pConfig, pTotalFrameCount); -} -MA_API ma_int16* ma_dr_mp3_open_file_and_read_pcm_frames_s16(const char* filePath, ma_dr_mp3_config* pConfig, ma_uint64* pTotalFrameCount, const ma_allocation_callbacks* pAllocationCallbacks) -{ - ma_dr_mp3 mp3; - if (!ma_dr_mp3_init_file(&mp3, filePath, pAllocationCallbacks)) { - return NULL; - } - return ma_dr_mp3__full_read_and_close_s16(&mp3, pConfig, pTotalFrameCount); -} -#endif -MA_API void* ma_dr_mp3_malloc(size_t sz, const ma_allocation_callbacks* pAllocationCallbacks) -{ - if (pAllocationCallbacks != NULL) { - return ma_dr_mp3__malloc_from_callbacks(sz, pAllocationCallbacks); - } else { - return ma_dr_mp3__malloc_default(sz, NULL); - } -} -MA_API void ma_dr_mp3_free(void* p, const ma_allocation_callbacks* pAllocationCallbacks) -{ - if (pAllocationCallbacks != NULL) { - ma_dr_mp3__free_from_callbacks(p, pAllocationCallbacks); - } else { - ma_dr_mp3__free_default(p, NULL); - } -} -#endif -/* dr_mp3_c end */ -#endif /* MA_DR_MP3_IMPLEMENTATION */ -#endif /* MA_NO_MP3 */ - - -/* End globally disabled warnings. */ -#if defined(_MSC_VER) - #pragma warning(pop) -#endif - -#endif /* miniaudio_c */ -#endif /* MINIAUDIO_IMPLEMENTATION */ - - -/* -This software is available as a choice of the following licenses. Choose -whichever you prefer. - -=============================================================================== -ALTERNATIVE 1 - Public Domain (www.unlicense.org) -=============================================================================== -This is free and unencumbered software released into the public domain. - -Anyone is free to copy, modify, publish, use, compile, sell, or distribute this -software, either in source code form or as a compiled binary, for any purpose, -commercial or non-commercial, and by any means. - -In jurisdictions that recognize copyright laws, the author or authors of this -software dedicate any and all copyright interest in the software to the public -domain. We make this dedication for the benefit of the public at large and to -the detriment of our heirs and successors. We intend this dedication to be an -overt act of relinquishment in perpetuity of all present and future rights to -this software under copyright law. - -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 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. - -For more information, please refer to - -=============================================================================== -ALTERNATIVE 2 - MIT No Attribution -=============================================================================== -Copyright 2025 David Reid - -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. - -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. -*/ diff --git a/tests-cmake/pipeline/pipeline.cpp b/tests-cmake/pipeline/pipeline.cpp deleted file mode 100644 index acffa532c2..0000000000 --- a/tests-cmake/pipeline/pipeline.cpp +++ /dev/null @@ -1,45 +0,0 @@ -// Simple wrapper for Arduino sketch to compilable with cpp in cmake -#include "AudioTools.h" -#include "AudioTools/AudioLibs/MiniAudioStream.h" -#include "AudioTools/AudioLibs/StdioStream.h" - -int16_t AR1[] = { 0, 4560, 9031, 13327, 17363, 21062, 24350, 27165, 29450, 31163, 32269, 32747, 32587, 31793, 30381, 28377, 25820, 22761, 19259, 15383, 11206, 6812, 2285, -2285, -6812, -11206, -15383, -19259, -22761, -25820, -28377, -30381, -31793, -32587, -32747, -32269, -31163, -29450, -27165, -24350, -21062, -17363, -13327, -9031, -4560 }; - -AudioInfo info(44100, 2, 16); -GeneratorFromArray wave(AR1, 0, false); -GeneratedSoundStream snd(wave); -Pipeline pip; -ResampleStream resample; -VolumeStream volume; -ChannelFormatConverterStream channels; -NumberFormatConverterStream bits; -MiniAudioStream out; // Output to MiniAudioStream -//I2SStream i2s; -StreamCopy copier(out, pip); - -// Arduino Setup -void setup(void) { - //Serial.begin(115200); - AudioToolsLogger.begin(Serial, AudioToolsLogLevel::Info); - - out.begin(); - resample.setStepSize(0.4f); - volume.setVolume(0.5); - channels.setToChannels(1); - bits.setToBits(32); - copier.setSynchAudioInfo(true); - - pip.setInput(snd); - pip.add(resample); - pip.add(volume); - pip.add(channels); - pip.add(bits); - //pip.addNotifyAudioChange(out); - Serial.println("*** begin ***"); - pip.begin(info); - -} - -void loop() { - copier.copy(); -} \ No newline at end of file diff --git a/tests-cmake/pitch-shift/pitch-shift/pitch-shift.ino b/tests-cmake/pitch-shift/pitch-shift/pitch-shift.ino new file mode 100644 index 0000000000..13d336d354 --- /dev/null +++ b/tests-cmake/pitch-shift/pitch-shift/pitch-shift.ino @@ -0,0 +1,37 @@ + +#include "AudioTools.h" + +float pitch_shift = 1.5; +int buffer_size = 1000; +AudioInfo info(44100,1,16); +SineWaveGenerator sineWave(32000); // subclass of SoundGenerator with max amplitude of 32000 +GeneratedSoundStream sound(sineWave); // Stream generated from sine wave +CsvOutput out(Serial, 1); +//use one of VariableSpeedRingBufferSimple, VariableSpeedRingBuffer, VariableSpeedRingBuffer180 +PitchShiftOutput> pitchShift(out); +StreamCopy copier(pitchShift, sound); // copies sound to out + +// Arduino Setup +void setup(void) { + // Open Serial + Serial.begin(115200); + AudioLogger::instance().begin(Serial, AudioLogger::Warning); + + // Define CSV Output + out.begin(info); + + auto pcfg = pitchShift.defaultConfig(); + pcfg.copyFrom(info); + pcfg.pitch_shift = pitch_shift; + pcfg.buffer_size = buffer_size; + pitchShift.begin(pcfg); + + // Setup sine wave + sineWave.begin(info, N_B4); + Serial.println("started..."); +} + +// Arduino loop - copy sound to out +void loop() { + copier.copy(); +} diff --git a/tests-cmake/player-wav/CMakeLists.txt b/tests-cmake/player-wav/CMakeLists.txt deleted file mode 100644 index 136856b38e..0000000000 --- a/tests-cmake/player-wav/CMakeLists.txt +++ /dev/null @@ -1,34 +0,0 @@ -cmake_minimum_required(VERSION 3.20) - -# set the project name -project(player-wav) -set (CMAKE_CXX_STANDARD 11) -set (DCMAKE_CXX_FLAGS "-Werror") - -# add_compile_options(-Wstack-usage=1024) - -# Add Portaduio for desktop build -add_compile_options(-DIS_DESKTOP) -FetchContent_Declare(portaudio GIT_REPOSITORY "https://github.com/PortAudio/portaudio.git" GIT_TAG v19.7.0 ) -FetchContent_GetProperties(portaudio) -if(NOT portaudio_POPULATED) - FetchContent_Populate(portaudio) - add_subdirectory(${portaudio_SOURCE_DIR} ${CMAKE_CURRENT_BINARY_DIR}/portaudio) -endif() - - -# Build with arduino-audio-tools -if(CMAKE_SOURCE_DIR STREQUAL CMAKE_CURRENT_SOURCE_DIR) - add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/../.. ${CMAKE_CURRENT_BINARY_DIR}/arduino-audio-tools ) -endif() - -# build sketch as executable -add_executable (player-wav player-wav.cpp) -target_include_directories(player-wav PRIVATE "${arduino_emulator_SOURCE_DIR}/ArduinoCore-Linux/libraries/SdFat" ) - -# set preprocessor defines -target_compile_definitions(player-wav PUBLIC -DIS_DESKTOP) - -# specify libraries -target_link_libraries(player-wav portaudio arduino_emulator arduino-audio-tools) - diff --git a/tests-cmake/player-wav/player-wav.cpp b/tests-cmake/player-wav/player-wav.cpp deleted file mode 100644 index 735bd8e21c..0000000000 --- a/tests-cmake/player-wav/player-wav.cpp +++ /dev/null @@ -1,29 +0,0 @@ -#include "AudioTools.h" -#include "AudioTools/AudioLibs/AudioSourceSTD.h" -#include "AudioTools/AudioLibs/StdioStream.h" -#include "AudioTools/AudioLibs/PortAudioStream.h" - -//StdioStream out; // Output to Desktop -PortAudioStream out; // Output to Desktop -const char *startFilePath="/home/pschatzmann/Downloads"; -const char* ext="wav"; -AudioSourceSTD source(startFilePath, ext); -WAVDecoder decoder; -AudioPlayer player(source, out, decoder); - - -void setup() { - AudioToolsLogger.begin(Serial, AudioToolsLogLevel::Warning); - - // setup output - auto cfg = out.defaultConfig(); - out.begin(cfg); - - // setup player - //source.setFileFilter("*Bob Dylan*"); - player.begin(); -} - -void loop() { - player.copy(); -} \ No newline at end of file diff --git a/tests-cmake/resample/CMakeLists.txt b/tests-cmake/resample/CMakeLists.txt index fcf35f8e62..b3d822ac52 100644 --- a/tests-cmake/resample/CMakeLists.txt +++ b/tests-cmake/resample/CMakeLists.txt @@ -4,7 +4,10 @@ cmake_minimum_required(VERSION 3.20) project(resample) set (CMAKE_CXX_STANDARD 11) set (DCMAKE_CXX_FLAGS "-Werror") -# add_compile_options(-Wstack-usage=1024) +if (CMAKE_CXX_COMPILER_ID STREQUAL "Clang") + set (CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -fno-omit-frame-pointer -fsanitize=address") + set (CMAKE_LINKER_FLAGS_DEBUG "${CMAKE_LINKER_FLAGS_DEBUG} -fno-omit-frame-pointer -fsanitize=address") +endif() # Build with arduino-audio-tools if(CMAKE_SOURCE_DIR STREQUAL CMAKE_CURRENT_SOURCE_DIR) @@ -12,10 +15,10 @@ if(CMAKE_SOURCE_DIR STREQUAL CMAKE_CURRENT_SOURCE_DIR) endif() # build sketch as executable -add_executable (resample resample.cpp) +add_executable (resample resample.cpp ../main.cpp) # set preprocessor defines -target_compile_definitions(resample PUBLIC -DIS_DESKTOP) +target_compile_definitions(resample PUBLIC -DEXIT_ON_STOP -DIS_DESKTOP) # specify libraries target_link_libraries(resample arduino_emulator arduino-audio-tools) diff --git a/tests-cmake/resample/resample.cpp b/tests-cmake/resample/resample.cpp index 057f6608eb..8399b6d1c5 100644 --- a/tests-cmake/resample/resample.cpp +++ b/tests-cmake/resample/resample.cpp @@ -1,5 +1,5 @@ // Simple wrapper for Arduino sketch to compilable with cpp in cmake -//#include "Arduino.h" +#include "Arduino.h" #include "AudioTools.h" AudioInfo info(44100, 1, 16); @@ -9,14 +9,14 @@ GeneratorFromArray sineWave(arsineC256,0,false); GeneratedSoundStream sound(sineWave); // Stream generated from sine wave ResampleStream out(sound); // We double the output sample rate CsvOutput csv(Serial); // Output to Serial -InputMerge imerge; -StreamCopy copier(csv, imerge, 1024); // copies sound to out +InputMerge merge; +StreamCopy copier(csv, merge, 1024); // copies sound to out // Arduino Setup void setup(void) { // Open Serial //Serial.begin(115200); - //AudioToolsLogger.begin(Serial, AudioToolsLogLevel::Info); + AudioLogger::instance().begin(Serial, AudioLogger::Info); auto config = out.defaultConfig(); config.copyFrom(info); @@ -27,13 +27,13 @@ void setup(void) { sound.begin(info); // sineWave.begin(config, N_B4); sineWave.begin(info); - imerge.begin(info); - imerge.add(out, 1); - //Serial.println("started (mixer)..."); + merge.begin(info); + merge.add(out); + Serial.println("started (mixer)..."); } // Arduino loop - copy sound to out void loop() { copier.copy(); - //Serial.println("----"); + Serial.println("----"); } diff --git a/tests-cmake/url-test/CMakeLists.txt b/tests-cmake/url-test/CMakeLists.txt index 7fdfa72028..92f8d25781 100644 --- a/tests-cmake/url-test/CMakeLists.txt +++ b/tests-cmake/url-test/CMakeLists.txt @@ -4,7 +4,10 @@ cmake_minimum_required(VERSION 3.20) project(url-test) set (CMAKE_CXX_STANDARD 11) set (DCMAKE_CXX_FLAGS "-Werror") -# add_compile_options(-Wstack-usage=1024) +if (CMAKE_CXX_COMPILER_ID STREQUAL "Clang") + set (CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -fno-omit-frame-pointer -fsanitize=address") + set (CMAKE_LINKER_FLAGS_DEBUG "${CMAKE_LINKER_FLAGS_DEBUG} -fno-omit-frame-pointer -fsanitize=address") +endif() include(FetchContent) @@ -14,9 +17,9 @@ if(CMAKE_SOURCE_DIR STREQUAL CMAKE_CURRENT_SOURCE_DIR) endif() # build sketch as executable -add_executable (url-test url-test.cpp) +add_executable (url-test url-test.cpp ../main.cpp) # set preprocessor defines -target_compile_definitions(url-test PUBLIC -DARDUINO -DIS_DESKTOP) +target_compile_definitions(url-test PUBLIC -DARDUINO -DEXIT_ON_STOP -DIS_DESKTOP) # OS/X might need this setting for core audio #target_compile_definitions(portaudio PUBLIC -DPA_USE_COREAUDIO=1) diff --git a/tests-cmake/url-test/url-test.cpp b/tests-cmake/url-test/url-test.cpp index 4f82a1b610..efa54d15b8 100644 --- a/tests-cmake/url-test/url-test.cpp +++ b/tests-cmake/url-test/url-test.cpp @@ -9,7 +9,7 @@ StreamCopy copier(null_out, url); // copy url to decoder void setup(){ Serial.begin(115200); - AudioToolsLogger.begin(Serial, AudioToolsLogLevel::Info); + AudioLogger::instance().begin(Serial, AudioLogger::Debug); // mp3 radio url.begin("http://stream.srg-ssr.ch/m/rsj/mp3_128","audio/mp3"); }